From d7fb53ac5e7bfee36925f284c42402b134f13df1 Mon Sep 17 00:00:00 2001 From: ChronodeAi Date: Mon, 24 Aug 2026 15:03:16 -0500 Subject: [PATCH 1/9] feat(connect): zero-touch project capability provisioning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every real connect run (CLI command and onboarding wiring alike) now leaves the machine with a project capability signing credential: when ~/.agentmemory/project-capability-secret is absent or valueless, a 32-byte random hex secret is generated and written with mode 0600; populated files are left untouched. Dry-run stays side-effect free. Only this credential is auto-provisioned — AGENTMEMORY_SECRET and AGENTMEMORY_ADMIN_SECRET keep their own flows. Doctor's capability diagnostic drops its manual-only flag: its fix generates the credential directly and the recheck reports it as provisioned. (cherry picked from commit bfbd84f8292de6b788191c47a7d5d6d3b189a343) --- src/cli.ts | 18 ++++ src/cli/connect/capability-secret.ts | 70 ++++++++++++ src/cli/connect/index.ts | 18 ++++ src/cli/doctor-diagnostics.ts | 10 +- src/cli/onboarding.ts | 14 +++ test/capability-provision.test.ts | 155 +++++++++++++++++++++++++++ test/cli-doctor-fixes.test.ts | 4 + 7 files changed, 285 insertions(+), 4 deletions(-) create mode 100644 src/cli/connect/capability-secret.ts create mode 100644 test/capability-provision.test.ts diff --git a/src/cli.ts b/src/cli.ts index e72470e5e..47fce12c9 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -65,6 +65,7 @@ import { readDataDirFlag, warnOnLegacyDataDir } from "./data-dir.js"; import { VERSION } from "./version.js"; import { getAllTools, ESSENTIAL_TOOLS } from "./mcp/tools-registry.js"; import { knownAgents } from "./cli/connect/index.js"; +import { ensureProjectCapabilitySecret } from "./cli/connect/capability-secret.js"; import { materializeIiiRuntimeConfig } from "./cli/iii-runtime-config.js"; import { getEnvVar } from "./config.js"; import { resolveProjectConfig } from "./project-config.js"; @@ -1751,6 +1752,23 @@ function buildDoctorEffects(): DoctorEffects { return false; } }, + provisionProjectCapability: async () => { + try { + const capability = ensureProjectCapabilitySecret(); + if (capability.reused) { + return { ok: true, message: `Capability credential already present at ${capability.path}` }; + } + return { + ok: true, + message: `Generated project capability credential at ${capability.path} (mode 0600)`, + }; + } catch (err) { + return { + ok: false, + message: err instanceof Error ? err.message : String(err), + }; + } + }, runInit: async () => { try { await runInit(); diff --git a/src/cli/connect/capability-secret.ts b/src/cli/connect/capability-secret.ts new file mode 100644 index 000000000..027c41e01 --- /dev/null +++ b/src/cli/connect/capability-secret.ts @@ -0,0 +1,70 @@ +import { + chmodSync, + existsSync, + mkdirSync, + readFileSync, + writeFileSync, +} from "node:fs"; +import { homedir } from "node:os"; +import { dirname, join } from "node:path"; +import { randomBytes } from "node:crypto"; + +/** + * Zero-touch provisioning for the project capability signing credential. + * + * Strict project authorization needs a signing secret, and historically the + * only way to get one was editing ~/.agentmemory/.env by hand. Every `connect` + * flow now provisions it automatically so a fresh install works without any + * manual step. Only THIS credential is auto-generated — AGENTMEMORY_SECRET and + * AGENTMEMORY_ADMIN_SECRET have their own explicit flows. + */ + +export function projectCapabilitySecretFile(): string { + const configured = process.env["AGENTMEMORY_PROJECT_CAPABILITY_SECRET_FILE"]?.trim(); + const expanded = configured + ? configured.startsWith("~/") + ? join(homedir(), configured.slice(2)) + : configured + : join(homedir(), ".agentmemory", "project-capability-secret"); + return expanded; +} + +export interface CapabilityProvisionResult { + path: string; + /** True when this call wrote a new secret to disk. */ + provisioned: boolean; + /** True when a usable value was already on disk and nothing was touched. */ + reused: boolean; +} + +export function generateCapabilitySecret(): string { + return randomBytes(32).toString("hex"); +} + +/** + * Return the existing credential, or generate one (64 hex chars from 32 + * random bytes) written with mode 0600. A file that exists but holds no + * value is provisioned too — there is no user data to clobber — while a + * populated file is left byte-for-byte untouched. + */ +export function ensureProjectCapabilitySecret(): CapabilityProvisionResult { + const path = projectCapabilitySecretFile(); + if (existsSync(path)) { + try { + if (readFileSync(path, "utf8").trim()) { + return { path, provisioned: false, reused: true }; + } + } catch { + // Unreadable file contents are not ours to overwrite; doctor surfaces it. + return { path, provisioned: false, reused: false }; + } + } + + const secret = generateCapabilitySecret(); + mkdirSync(dirname(path), { recursive: true }); + // The mode flag applies only at file creation; chmod covers the + // pre-existing-but-empty case so the credential is never world-readable. + writeFileSync(path, `${secret}\n`, { encoding: "utf8", mode: 0o600 }); + chmodSync(path, 0o600); + return { path, provisioned: true, reused: false }; +} diff --git a/src/cli/connect/index.ts b/src/cli/connect/index.ts index 268c2115a..43ea1b182 100644 --- a/src/cli/connect/index.ts +++ b/src/cli/connect/index.ts @@ -21,6 +21,7 @@ import { adapter as pi } from "./pi.js"; import { adapter as qwen } from "./qwen.js"; import { adapter as warp } from "./warp.js"; import { adapter as zed } from "./zed.js"; +import { ensureProjectCapabilitySecret } from "./capability-secret.js"; export const ADAPTERS: readonly ConnectAdapter[] = [ claudeCode, @@ -116,6 +117,23 @@ export async function runConnect(args: string[]): Promise { p.intro("agentmemory connect"); + // Zero-touch capability provisioning (#strict-scope): every real connect + // run leaves the machine with a project capability signing credential so + // wired agents authorize on first use. Dry-run stays side-effect free. + if (!dryRun) { + try { + const capability = ensureProjectCapabilitySecret(); + if (capability.provisioned) { + p.log.info( + `Generated project capability credential at ${capability.path} (mode 0600).`, + ); + } + } catch { + // Provisioning must never block wiring; doctor reports a missing + // credential and can generate one later. + } + } + if (positional.length === 0 && !all) { const detected = ADAPTERS.filter((a) => a.detect()); if (detected.length === 0) { diff --git a/src/cli/doctor-diagnostics.ts b/src/cli/doctor-diagnostics.ts index 915d9fdb0..7cc0d6cbe 100644 --- a/src/cli/doctor-diagnostics.ts +++ b/src/cli/doctor-diagnostics.ts @@ -180,6 +180,8 @@ export type DoctorEffects = { runInit: () => Promise; /** Open a file in $EDITOR (or fallback). Resolves when editor exits. */ openEditor: (path: string) => Promise; + /** Generate the project capability credential (mode 0600) when absent. */ + provisionProjectCapability: () => Promise; /** Run the iii installer. */ runIiiInstaller: () => Promise; /** Stop the running engine cleanly. */ @@ -230,11 +232,11 @@ export function buildDiagnostics(effects: DoctorEffects): Diagnostic[] { id: "project-capability-credentials", message: "Strict project authorization has no capability signing credential.", fixPreview: - "Open ~/.agentmemory/.env and configure a project capability secret or secret file.", + "Generate a capability signing credential at ~/.agentmemory/project-capability-secret (mode 0600).", moreInfo: "Project-scoped hooks and MCP calls use short-lived signed capabilities. " + - "Strict mode is the default; without a signing credential, project writes and recalls fail closed.", - manualOnly: true, + "Strict mode is the default; connect flows provision this credential " + + "automatically, and doctor can generate it too — no manual editing needed.", check: async (ctx) => { const env = effects.runtimeEnv(); const strict = !["false", "0", "off"].includes( @@ -255,7 +257,7 @@ export function buildDiagnostics(effects: DoctorEffects): Diagnostic[] { ? { ok: true, detail: `secret file: ${secretFile}` } : { ok: false, detail: "no project capability signing credential" }; }, - fix: (ctx) => effects.openEditor(ctx.envPath), + fix: () => effects.provisionProjectCapability(), }, { id: "context-delivery-credentials", diff --git a/src/cli/onboarding.ts b/src/cli/onboarding.ts index 6d0493554..3d7cc3744 100644 --- a/src/cli/onboarding.ts +++ b/src/cli/onboarding.ts @@ -29,6 +29,7 @@ import { appendFileSync, readFileSync } from "node:fs"; import { readPrefs, writePrefs } from "./preferences.js"; import { ADAPTERS, resolveAdapter, runAdapter } from "./connect/index.js"; import type { ConnectResult } from "./connect/types.js"; +import { ensureProjectCapabilitySecret } from "./connect/capability-secret.js"; const __dirname = dirname(fileURLToPath(import.meta.url)); @@ -321,6 +322,19 @@ async function wireSelectedAgents(agents: string[]): Promise { const manual: { name: string; docs?: string }[] = []; const failed: { name: string; reason: string }[] = []; + // Same zero-touch guarantee as `agentmemory connect`: onboarding-driven + // wiring also provisions the project capability credential up front. + try { + const capability = ensureProjectCapabilitySecret(); + if (capability.provisioned) { + p.log.info( + `Generated project capability credential at ${capability.path} (mode 0600).`, + ); + } + } catch { + // Doctor reports and repairs a missing credential; never block wiring. + } + for (const name of agents) { const adapter = resolveAdapter(name); if (!adapter) { diff --git a/test/capability-provision.test.ts b/test/capability-provision.test.ts new file mode 100644 index 000000000..ba975ff1c --- /dev/null +++ b/test/capability-provision.test.ts @@ -0,0 +1,155 @@ +import { describe, it, expect, beforeEach, afterEach } from "vitest"; +import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs"; +import { join } from "node:path"; +import { tmpdir } from "node:os"; + +import { + ensureProjectCapabilitySecret, + generateCapabilitySecret, + projectCapabilitySecretFile, +} from "../src/cli/connect/capability-secret.js"; +import { buildDiagnostics, type DoctorEffects } from "../src/cli/doctor-diagnostics.js"; + +function stubEffects( + overrides: Partial = {}, +): DoctorEffects { + return { + envFileExists: () => true, + readEnvFile: () => ({}), + runtimeEnv: () => ({ AGENTMEMORY_STRICT_CAPABILITY_MODE: "true" }), + secretFileHasValue: () => false, + pidfileExists: () => false, + pidfilePidIsAlive: () => null, + findIiiBinary: () => "/Users/test/.local/bin/iii", + localBinIiiPath: () => "/Users/test/.local/bin/iii", + iiiBinaryVersion: () => "0.11.2", + viewerReachable: async () => true, + runInit: async () => ({ ok: true }), + openEditor: async () => ({ ok: true }), + provisionProjectCapability: async () => ({ + ok: true, + message: "generated", + }), + runIiiInstaller: async () => ({ ok: true }), + runStop: async () => ({ ok: true }), + runStart: async () => ({ ok: true }), + clearEnginePidAndState: () => {}, + ...overrides, + }; +} + +describe("zero-touch project capability provisioning", () => { + const ORIGINAL_HOME = process.env["HOME"]; + let sandboxHome: string; + + beforeEach(() => { + sandboxHome = mkdtempSync(join(tmpdir(), "am-capability-")); + process.env["HOME"] = sandboxHome; + delete process.env["AGENTMEMORY_PROJECT_CAPABILITY_SECRET_FILE"]; + }); + + afterEach(() => { + if (ORIGINAL_HOME === undefined) delete process.env["HOME"]; + else process.env["HOME"] = ORIGINAL_HOME; + rmSync(sandboxHome, { recursive: true, force: true }); + }); + + it("generates a 64-hex-char secret in a mode-0600 file", () => { + const result = ensureProjectCapabilitySecret(); + expect(result.provisioned).toBe(true); + expect(result.reused).toBe(false); + expect(result.path).toBe( + join(sandboxHome, ".agentmemory", "project-capability-secret"), + ); + const content = readFileSync(result.path, "utf8"); + expect(content.trim()).toMatch(/^[a-f0-9]{64}$/); + // Only the newlines differ from the raw secret. + expect((statSync(result.path).mode & 0o777)).toBe(0o600); + }); + + it("leaves an existing populated file byte-for-byte untouched", () => { + const path = projectCapabilitySecretFile(); + mkdirSync(join(path, ".."), { recursive: true }); + writeFileSync(path, "user-chosen-secret\n", { mode: 0o600 }); + const before = statSync(path); + const result = ensureProjectCapabilitySecret(); + expect(result).toEqual({ path, provisioned: false, reused: true }); + expect(readFileSync(path, "utf8")).toBe("user-chosen-secret\n"); + expect(statSync(path).mtimeMs).toBe(before.mtimeMs); + expect(statSync(path).mode & 0o777).toBe(before.mode & 0o777); + }); + + it("provisions over a pre-existing empty file and enforces 0600", () => { + const path = projectCapabilitySecretFile(); + mkdirSync(join(path, ".."), { recursive: true }); + writeFileSync(path, "", { mode: 0o644 }); + const result = ensureProjectCapabilitySecret(); + expect(result.provisioned).toBe(true); + expect(readFileSync(path, "utf8").trim()).toMatch(/^[a-f0-9]{64}$/); + expect(statSync(path).mode & 0o777).toBe(0o600); + }); + + it("honours AGENTMEMORY_PROJECT_CAPABILITY_SECRET_FILE overrides", () => { + process.env["AGENTMEMORY_PROJECT_CAPABILITY_SECRET_FILE"] = + join(sandboxHome, "custom", "cap-secret"); + const result = ensureProjectCapabilitySecret(); + expect(result.path).toBe(join(sandboxHome, "custom", "cap-secret")); + expect(existsSync(result.path)).toBe(true); + expect(statSync(result.path).mode & 0o777).toBe(0o600); + }); + + it("generateCapabilitySecret returns fresh 32-byte hex values", () => { + const a = generateCapabilitySecret(); + const b = generateCapabilitySecret(); + expect(a).toMatch(/^[a-f0-9]{64}$/); + expect(b).toMatch(/^[a-f0-9]{64}$/); + expect(a).not.toBe(b); + }); +}); + +describe("doctor capability diagnostic after zero-touch provisioning", () => { + function findDiagnostic(effects: DoctorEffects) { + return buildDiagnostics(effects).find( + (d) => d.id === "project-capability-credentials", + )!; + } + + it("is no longer manual-only", () => { + const diagnostic = findDiagnostic(stubEffects()); + expect(diagnostic.manualOnly).toBeFalsy(); + expect(typeof diagnostic.fix).toBe("function"); + }); + + it("reports the provisioned secret file via the auto fix + recheck", async () => { + let provisionedPath: string | null = null; + const diagnostic = findDiagnostic( + stubEffects({ + provisionProjectCapability: async () => { + provisionedPath = "/tmp/test/.agentmemory/project-capability-secret"; + return { ok: true, message: `generated at ${provisionedPath}` }; + }, + secretFileHasValue: (path) => path === provisionedPath, + }), + ); + const ctx = { + baseUrl: "http://localhost:3111", + viewerUrl: "http://localhost:3113", + envPath: "/tmp/test/.agentmemory/.env", + pidfilePath: "/tmp/test/.agentmemory/iii.pid", + enginePath: "/tmp/test/.agentmemory/engine-state.json", + pinnedVersion: "0.11.2", + }; + await expect(diagnostic.check(ctx)).resolves.toMatchObject({ + ok: false, + detail: "no project capability signing credential", + }); + const fixResult = await diagnostic.fix(ctx); + expect(fixResult.ok).toBe(true); + // After the zero-touch fix the same check reports the credential as + // provisioned instead of demanding manual editing. + await expect(diagnostic.check(ctx)).resolves.toMatchObject({ + ok: true, + detail: `secret file: ${provisionedPath}`, + }); + }); +}); diff --git a/test/cli-doctor-fixes.test.ts b/test/cli-doctor-fixes.test.ts index 10244227c..64fae0c4a 100644 --- a/test/cli-doctor-fixes.test.ts +++ b/test/cli-doctor-fixes.test.ts @@ -47,6 +47,10 @@ function stubEffects(overrides: Partial = {}): DoctorEffects { viewerReachable: async () => true, runInit: async () => ({ ok: true, message: "wrote .env" }), openEditor: async () => ({ ok: true, message: "saved" }), + provisionProjectCapability: async () => ({ + ok: true, + message: "generated", + }), runIiiInstaller: async () => ({ ok: true, message: "installed" }), runStop: async () => ({ ok: true, message: "stopped" }), runStart: async () => ({ ok: true, message: "started" }), From db3ba5a35170b16ce3092ee94bce5fb86dab8a0e Mon Sep 17 00:00:00 2001 From: ChronodeAi Date: Mon, 24 Aug 2026 15:13:21 -0500 Subject: [PATCH 2/9] feat(api): project-scope memory export and audit routes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit memory_export and memory_audit bypassed applyProjectScope and answered checkAuth-only REST routes with the full corpus. Both now follow the fork model: requireProjectReadScope makes a project mandatory unless scope is explicitly global, which the api-auth middleware (and the handler contract) gates behind administrative authority — mirroring api::sessions. mem::export filters every attributable section by the requested project and omits sections that carry no project field instead of leaking them; queryAudit matches only entries whose details name the project. MCP schemas for memory_export/memory_audit accept project/scope like the other scoped tools, and the standalone proxy passes the scoping as query params while the local fallback filters identically. (cherry picked from commit 0d38aaa67ad040f82ee5eea293e319999fe0ad83) --- plugin/scripts/standalone.mjs | 57 +++++-- src/functions/audit.ts | 8 + src/functions/export-import.ts | 86 ++++++++--- src/mcp/server.ts | 25 ++- src/mcp/standalone.ts | 49 +++++- src/mcp/tools-registry.ts | 33 +++- src/triggers/api.ts | 44 +++++- test/export-audit-scope.test.ts | 262 ++++++++++++++++++++++++++++++++ 8 files changed, 517 insertions(+), 47 deletions(-) create mode 100644 test/export-audit-scope.test.ts diff --git a/plugin/scripts/standalone.mjs b/plugin/scripts/standalone.mjs index f0472e630..f0760d226 100755 --- a/plugin/scripts/standalone.mjs +++ b/plugin/scripts/standalone.mjs @@ -512,10 +512,20 @@ const CORE_TOOLS = [ }, { name: "memory_export", - description: "Export all memory data as JSON.", + description: "Export memory data as JSON for one project (or all with admin global scope).", inputSchema: { type: "object", - properties: {} + properties: { + project: { + type: "string", + description: "Canonical project ID. Required unless scope is explicitly global." + }, + scope: { + type: "string", + enum: ["global"], + description: "Set to 'global' only for an explicit administrative cross-project export; otherwise provide project." + } + } } }, { @@ -702,7 +712,7 @@ const V040_TOOLS = [ }, { name: "memory_audit", - description: "View the audit trail of memory operations.", + description: "View the audit trail of memory operations for one project (or all with admin global scope).", inputSchema: { type: "object", properties: { @@ -713,6 +723,15 @@ const V040_TOOLS = [ limit: { type: "number", description: "Max entries (default 50)" + }, + project: { + type: "string", + description: "Canonical project ID. Required unless scope is explicitly global." + }, + scope: { + type: "string", + enum: ["global"], + description: "Set to 'global' only for an explicit administrative cross-project query; otherwise provide project." } } } @@ -2181,9 +2200,12 @@ function validate(toolName, args) { applyProjectScope(v, args); return v; } - case "memory_export": return v; + case "memory_export": + applyProjectScope(v, args); + return v; case "memory_audit": v.limit = parseLimit(args["limit"], 50); + applyProjectScope(v, args); return v; default: throw new Error(`Unknown tool: ${toolName}`); } @@ -2241,8 +2263,14 @@ async function handleProxy(v, handle) { ...v.scope === "global" ? { scope: "global" } : { project: v.project } }) })); - case "memory_export": return textResponse(await handle.call("/agentmemory/export", { method: "GET" }), true); - case "memory_audit": return textResponse(await handle.call(`/agentmemory/audit?limit=${v.limit}`, { method: "GET" }), true); + case "memory_export": { + const qs = v.scope === "global" ? "?scope=global" : `?project=${encodeURIComponent(v.project ?? "")}`; + return textResponse(await handle.call(`/agentmemory/export${qs}`, { method: "GET" }), true); + } + case "memory_audit": { + const scopeQs = v.scope === "global" ? "&scope=global" : `&project=${encodeURIComponent(v.project ?? "")}`; + return textResponse(await handle.call(`/agentmemory/audit?limit=${v.limit}${scopeQs}`, { method: "GET" }), true); + } default: throw new Error(`Unknown tool: ${v.tool}`); } } @@ -2323,13 +2351,18 @@ async function handleLocal(v, kvInstance) { reason: v.reason }); } - case "memory_export": return textResponse({ - version: VERSION, - memories: await kvInstance.list("mem:memories"), - sessions: await kvInstance.list("mem:sessions") - }, true); + case "memory_export": { + const allMemories = await kvInstance.list("mem:memories"); + const allSessions = await kvInstance.list("mem:sessions"); + return textResponse({ + version: VERSION, + memories: v.scope === "global" ? allMemories : allMemories.filter((m) => m["project"] === v.project), + sessions: v.scope === "global" ? allSessions : allSessions.filter((s) => s["project"] === v.project) + }, true); + } case "memory_audit": { - const entries = await kvInstance.list("mem:audit"); + const allEntries = await kvInstance.list("mem:audit"); + const entries = v.scope === "global" ? allEntries : allEntries.filter((e) => typeof e["details"] === "object" && e["details"] !== null && e["details"]["project"] === v.project); const limit = v.limit ?? 50; return textResponse({ entries: entries.slice(0, limit) }, true); } diff --git a/src/functions/audit.ts b/src/functions/audit.ts index 774ce50d9..c4f328523 100644 --- a/src/functions/audit.ts +++ b/src/functions/audit.ts @@ -497,6 +497,7 @@ export async function queryAudit( dateFrom?: string; dateTo?: string; limit?: number; + project?: string; }, ): Promise { const all = await kv.list(KV.audit); @@ -521,6 +522,13 @@ export async function queryAudit( } entries = entries.filter((e) => new Date(e.timestamp).getTime() <= to); } + if (filter?.project) { + // Only entries that explicitly name the project match; unattributed + // audit records stay visible in global (administrative) queries only. + entries = entries.filter( + (e) => typeof e.details === "object" && e.details !== null && e.details["project"] === filter.project, + ); + } return entries.slice(0, filter?.limit || 100); } diff --git a/src/functions/export-import.ts b/src/functions/export-import.ts index 92abe3ffd..6f5b23bca 100644 --- a/src/functions/export-import.ts +++ b/src/functions/export-import.ts @@ -38,7 +38,24 @@ export function registerExportImportFunction(sdk: ISdk, kv: StateKV): void { maxSessions?: number; offset?: number; sections?: string[]; + project?: string; + scope?: "global"; }) => { + // Fork scoping model: project is mandatory unless the caller holds + // administrative authorization and passes scope:"global" (the REST and + // MCP layers enforce that). A project-scoped export filters every + // attributable section; sections without a project field cannot be + // attributed safely and are omitted rather than leaked. + const projectScope = + typeof data?.project === "string" && data.project.trim() + ? data.project.trim() + : undefined; + const matchesProject = (record: { project?: string }): boolean => + projectScope === undefined || record.project === projectScope; + const scoped = (rows: T[]): T[] => + projectScope === undefined ? rows : rows.filter(matchesProject); + const globalOnly = (rows: T[]): T[] => + projectScope === undefined ? rows : []; const rawMax = Number(data?.maxSessions); const maxSessions = Number.isFinite(rawMax) && rawMax > 0 ? Math.min(Math.floor(rawMax), 1000) : undefined; const rawOffset = Number(data?.offset); @@ -54,20 +71,21 @@ export function registerExportImportFunction(sdk: ISdk, kv: StateKV): void { const includeSummaries = includes("core") || includes("summaries"); const allSessions = - includeSessions || includes("profiles") + (includeSessions || includes("profiles") ? await kv.list(KV.sessions) - : []; + : [] + ).filter(matchesProject); const paginatedSessions = includeSessions && maxSessions !== undefined ? allSessions.slice(offset, offset + maxSessions) : includeSessions ? allSessions : []; - const memories = includeMemories - ? await kv.list(KV.memories) - : []; - const summaries = includeSummaries - ? await kv.list(KV.summaries) - : []; + const memories = scoped( + includeMemories ? await kv.list(KV.memories) : [], + ); + const summaries = scoped( + includeSummaries ? await kv.list(KV.summaries) : [], + ); const observations: Record = {}; const obsResults = await Promise.all( @@ -135,6 +153,26 @@ export function registerExportImportFunction(sdk: ISdk, kv: StateKV): void { includes("access") ? kv.list(KV.accessLog).catch(() => []) : [], ]); + const fGraphNodes = scoped(graphNodes); + const fGraphEdges = scoped(graphEdges); + const fSemanticMemories = scoped(semanticMemories); + const fProceduralMemories = scoped(proceduralMemories); + const fActions = scoped(actions); + // ActionEdge, Sentinel, Facet, Routine, Signal, Checkpoint and + // AccessLogExport rows carry no project field; a project-scoped export + // cannot attribute them, so it omits them instead of crossing projects. + const fActionEdges = globalOnly(actionEdges); + const fSentinels = globalOnly(sentinels); + const fSketches = scoped(sketches); + const fCrystals = scoped(crystals); + const fFacets = globalOnly(facets); + const fLessons = scoped(lessons); + const fInsights = scoped(insights); + const fRoutines = globalOnly(routines); + const fSignals = globalOnly(signals); + const fCheckpoints = globalOnly(checkpoints); + const fAccessLogs = globalOnly(accessLogs); + const exportData: ExportData = { version: EXPORT_FORMAT_VERSION, exportedAt: new Date().toISOString(), @@ -146,24 +184,24 @@ export function registerExportImportFunction(sdk: ISdk, kv: StateKV): void { memories, summaries, profiles: profiles.length > 0 ? profiles : undefined, - graphNodes: graphNodes.length > 0 ? graphNodes : undefined, - graphEdges: graphEdges.length > 0 ? graphEdges : undefined, + graphNodes: fGraphNodes.length > 0 ? fGraphNodes : undefined, + graphEdges: fGraphEdges.length > 0 ? fGraphEdges : undefined, semanticMemories: - semanticMemories.length > 0 ? semanticMemories : undefined, + fSemanticMemories.length > 0 ? fSemanticMemories : undefined, proceduralMemories: - proceduralMemories.length > 0 ? proceduralMemories : undefined, - actions: actions.length > 0 ? actions : undefined, - actionEdges: actionEdges.length > 0 ? actionEdges : undefined, - sentinels: sentinels.length > 0 ? sentinels : undefined, - sketches: sketches.length > 0 ? sketches : undefined, - crystals: crystals.length > 0 ? crystals : undefined, - facets: facets.length > 0 ? facets : undefined, - lessons: lessons.length > 0 ? lessons : undefined, - insights: insights.length > 0 ? insights : undefined, - routines: routines.length > 0 ? routines : undefined, - signals: signals.length > 0 ? signals : undefined, - checkpoints: checkpoints.length > 0 ? checkpoints : undefined, - accessLogs: accessLogs.length > 0 ? accessLogs : undefined, + fProceduralMemories.length > 0 ? fProceduralMemories : undefined, + actions: fActions.length > 0 ? fActions : undefined, + actionEdges: fActionEdges.length > 0 ? fActionEdges : undefined, + sentinels: fSentinels.length > 0 ? fSentinels : undefined, + sketches: fSketches.length > 0 ? fSketches : undefined, + crystals: fCrystals.length > 0 ? fCrystals : undefined, + facets: fFacets.length > 0 ? fFacets : undefined, + lessons: fLessons.length > 0 ? fLessons : undefined, + insights: fInsights.length > 0 ? fInsights : undefined, + routines: fRoutines.length > 0 ? fRoutines : undefined, + signals: fSignals.length > 0 ? fSignals : undefined, + checkpoints: fCheckpoints.length > 0 ? fCheckpoints : undefined, + accessLogs: fAccessLogs.length > 0 ? fAccessLogs : undefined, }; if (includeSessions && maxSessions !== undefined) { diff --git a/src/mcp/server.ts b/src/mcp/server.ts index 13033b663..49b4c63f3 100644 --- a/src/mcp/server.ts +++ b/src/mcp/server.ts @@ -495,7 +495,20 @@ export function registerMcpEndpoints( } case "memory_export": { - const result = await sdk.trigger({ function_id: "mem::export", payload: {} }); + const exportScope = parseProjectScope(args); + if (!exportScope) { + return { + status_code: 400, + body: { error: "project required unless scope is global" }, + }; + } + const result = await sdk.trigger({ + function_id: "mem::export", + payload: + exportScope.scope === "global" + ? { scope: "global" as const } + : { project: exportScope.project }, + }); return { status_code: 200, body: { @@ -733,10 +746,20 @@ export function registerMcpEndpoints( } case "memory_audit": { + const auditScope = parseProjectScope(args); + if (!auditScope) { + return { + status_code: 400, + body: { error: "project required unless scope is global" }, + }; + } try { const result = await sdk.trigger({ function_id: "mem::audit-query", payload: { operation: args.operation as string, limit: typeof args.limit === "number" ? args.limit : 50, + ...(auditScope.scope === "global" + ? {} + : { project: auditScope.project }), } }); return { status_code: 200, diff --git a/src/mcp/standalone.ts b/src/mcp/standalone.ts index 4b7ccbf1f..8a29fcc06 100644 --- a/src/mcp/standalone.ts +++ b/src/mcp/standalone.ts @@ -263,9 +263,11 @@ function validate(toolName: string, args: Record): Validated { return v; } case "memory_export": + applyProjectScope(v, args); return v; case "memory_audit": { v.limit = parseLimit(args["limit"], 50); + applyProjectScope(v, args); return v; } default: @@ -347,12 +349,22 @@ async function handleProxy( return textResponse(result); } case "memory_export": { - const result = await handle.call("/agentmemory/export", { method: "GET" }); + const qs = + v.scope === "global" + ? "?scope=global" + : `?project=${encodeURIComponent(v.project ?? "")}`; + const result = await handle.call(`/agentmemory/export${qs}`, { + method: "GET", + }); return textResponse(result, true); } case "memory_audit": { + const scopeQs = + v.scope === "global" + ? "&scope=global" + : `&project=${encodeURIComponent(v.project ?? "")}`; const result = await handle.call( - `/agentmemory/audit?limit=${v.limit}`, + `/agentmemory/audit?limit=${v.limit}${scopeQs}`, { method: "GET" }, ); return textResponse(result, true); @@ -477,17 +489,42 @@ async function handleLocal( } case "memory_export": { - const memories = await kvInstance.list("mem:memories"); - const sessions = await kvInstance.list("mem:sessions"); + const allMemories = await kvInstance.list("mem:memories"); + const allSessions = await kvInstance.list("mem:sessions"); + // Local fallback honors the same project scope as the REST route. + const memories = + v.scope === "global" + ? allMemories + : (allMemories as Array>).filter( + (m) => m["project"] === v.project, + ); + const sessions = + v.scope === "global" + ? allSessions + : (allSessions as Array>).filter( + (s) => s["project"] === v.project, + ); return textResponse({ version: VERSION, memories, sessions }, true); } case "memory_audit": { - const entries = await kvInstance.list("mem:audit"); + const allEntries = await kvInstance.list("mem:audit"); + const entries = + v.scope === "global" + ? allEntries + : (allEntries as Array>).filter( + (e) => + typeof e["details"] === "object" && + e["details"] !== null && + (e["details"] as Record)["project"] === + v.project, + ); const limit = v.limit ?? 50; return textResponse( { - entries: (entries as Array>).slice(0, limit), + entries: ( + entries as Array> + ).slice(0, limit), }, true, ); diff --git a/src/mcp/tools-registry.ts b/src/mcp/tools-registry.ts index 1a7c3a96f..660aef4d0 100644 --- a/src/mcp/tools-registry.ts +++ b/src/mcp/tools-registry.ts @@ -262,8 +262,23 @@ export const CORE_TOOLS: McpToolDef[] = [ }, { name: "memory_export", - description: "Export all memory data as JSON.", - inputSchema: { type: "object", properties: {} }, + description: "Export memory data as JSON for one project (or all with admin global scope).", + inputSchema: { + type: "object", + properties: { + project: { + type: "string", + description: + "Canonical project ID. Required unless scope is explicitly global.", + }, + scope: { + type: "string", + enum: ["global"], + description: + "Set to 'global' only for an explicit administrative cross-project export; otherwise provide project.", + }, + }, + }, }, { name: "memory_relations", @@ -440,12 +455,24 @@ export const V040_TOOLS: McpToolDef[] = [ }, { name: "memory_audit", - description: "View the audit trail of memory operations.", + description: + "View the audit trail of memory operations for one project (or all with admin global scope).", inputSchema: { type: "object", properties: { operation: { type: "string", description: "Filter by operation type" }, limit: { type: "number", description: "Max entries (default 50)" }, + project: { + type: "string", + description: + "Canonical project ID. Required unless scope is explicitly global.", + }, + scope: { + type: "string", + enum: ["global"], + description: + "Set to 'global' only for an explicit administrative cross-project query; otherwise provide project.", + }, }, }, }, diff --git a/src/triggers/api.ts b/src/triggers/api.ts index 94600476e..824585054 100644 --- a/src/triggers/api.ts +++ b/src/triggers/api.ts @@ -2302,6 +2302,23 @@ export function registerApiTriggers( async (req: ApiRequest): Promise => { const authErr = checkAuth(req, secret); if (authErr) return authErr; + let exportScope: ReturnType; + try { + exportScope = requireProjectReadScope( + { + project: req.query_params?.["project"], + scope: req.query_params?.["scope"], + }, + "api::export", + ); + } catch (error) { + return { + status_code: 400, + body: { + error: error instanceof Error ? error.message : String(error), + }, + }; + } // mem::export already supports maxSessions/offset internally, // but the HTTP endpoint hardcoded an empty payload — so /export on a // real corpus (40 sessions × 34K observations × 8K memories) hit the @@ -2333,7 +2350,12 @@ export function registerApiTriggers( } const result = await sdk.trigger({ function_id: "mem::export", - payload, + payload: { + ...payload, + ...(exportScope.kind === "global" + ? { scope: "global" as const } + : { project: exportScope.project }), + }, }); return { status_code: 200, body: result }; }, @@ -3026,10 +3048,30 @@ export function registerApiTriggers( async (req: ApiRequest): Promise => { const authErr = checkAuth(req, secret); if (authErr) return authErr; + let auditScope: ReturnType; + try { + auditScope = requireProjectReadScope( + { + project: req.query_params?.["project"], + scope: req.query_params?.["scope"], + }, + "api::audit", + ); + } catch (error) { + return { + status_code: 400, + body: { + error: error instanceof Error ? error.message : String(error), + }, + }; + } const parsedLimit = parseOptionalInt(req.query_params?.["limit"]); const entries = await sdk.trigger({ function_id: "mem::audit-query", payload: { operation: req.query_params?.["operation"], limit: parsedLimit ?? 50, + ...(auditScope.kind === "global" + ? {} + : { project: auditScope.project }), } }); return { status_code: 200, body: { entries, success: true } }; }, diff --git a/test/export-audit-scope.test.ts b/test/export-audit-scope.test.ts new file mode 100644 index 000000000..372f5ef7e --- /dev/null +++ b/test/export-audit-scope.test.ts @@ -0,0 +1,262 @@ +import { describe, expect, it } from "vitest"; +import { + createProjectCapabilityToken, + PROJECT_CAPABILITY_PROJECT_HEADER, +} from "../src/auth.js"; +import { registerApiTriggers } from "../src/triggers/api.js"; +import { registerExportImportFunction } from "../src/functions/export-import.js"; +import { queryAudit, recordAudit } from "../src/functions/audit.js"; +import { KV } from "../src/state/schema.js"; + +type Handler = (request: { + headers?: Record; + query_params?: Record; + body?: Record; +}) => Promise<{ + status_code: number; + body: Record; +}>; + +const ADMIN_SECRET = "export-audit-admin-secret"; +const CAPABILITY_SECRET = "export-audit-capability-secret"; +const PROJECT_A = "github.com/example/project-a"; +const PROJECT_B = "github.com/example/project-b"; + +function projectHeaders(project: string): Record { + const token = createProjectCapabilityToken( + { + version: 1, + audience: "agentmemory", + project, + expiresAt: Math.floor(Date.now() / 1000) + 60, + }, + CAPABILITY_SECRET, + ); + return { + authorization: `Bearer ${token}`, + [PROJECT_CAPABILITY_PROJECT_HEADER]: project, + }; +} + +function adminHeaders(): Record { + return { authorization: `Bearer ${ADMIN_SECRET}` }; +} + +function createApi() { + const functions = new Map(); + const captured: Array<{ function_id: string; payload: unknown }> = []; + const store = new Map>(); + const sdk = { + registerFunction: ( + idOrOptions: string | { id: string }, + handler: Handler, + ) => { + functions.set( + typeof idOrOptions === "string" ? idOrOptions : idOrOptions.id, + handler, + ); + }, + registerTrigger: () => {}, + trigger: async (request: { function_id: string; payload?: unknown }) => { + captured.push(request); + return { success: true }; + }, + }; + const kv = makeKv(store); + registerApiTriggers( + sdk as never, + kv as never, + "legacy-secret", + undefined, + undefined, + ADMIN_SECRET, + CAPABILITY_SECRET, + true, + "agentmemory", + ); + return { functions, captured, store }; +} + +function makeKv(store: Map>) { + return { + get: async (scope: string, key: string): Promise => + (store.get(scope)?.get(key) as T) ?? null, + set: async (scope: string, key: string, value: T): Promise => { + if (!store.has(scope)) store.set(scope, new Map()); + store.get(scope)!.set(key, value); + return value; + }, + update: async () => undefined, + delete: async (scope: string, key: string): Promise => { + store.get(scope)?.delete(key); + }, + list: async (scope: string): Promise => + Array.from(store.get(scope)?.values() ?? []) as T[], + }; +} + +describe("REST export/audit project scoping", () => { + it("rejects unscoped export and audit requests", async () => { + const { functions } = createApi(); + const exportRoute = functions.get("api::export")!; + const auditRoute = functions.get("api::audit")!; + + await expect(exportRoute({ headers: adminHeaders() })).resolves.toMatchObject({ + status_code: 400, + body: { + error: expect.stringContaining("project is required"), + }, + }); + await expect(auditRoute({ headers: adminHeaders() })).resolves.toMatchObject({ + status_code: 400, + body: { + error: expect.stringContaining("project is required"), + }, + }); + }); + + it("passes the requested project through a project-scoped export", async () => { + const { functions, captured } = createApi(); + const response = await functions.get("api::export")!({ + headers: projectHeaders(PROJECT_A), + query_params: { project: PROJECT_A }, + }); + expect(response.status_code).toBe(200); + const exportCall = captured.find((c) => c.function_id === "mem::export"); + expect(exportCall?.payload).toMatchObject({ project: PROJECT_A }); + }); + + it("requires administrative authority for global export/audit scope", async () => { + const { functions } = createApi(); + await expect( + functions.get("api::export")!({ + headers: projectHeaders(PROJECT_A), + query_params: { scope: "global" }, + }), + ).resolves.toMatchObject({ status_code: 401 }); + await expect( + functions.get("api::audit")!({ + headers: projectHeaders(PROJECT_A), + query_params: { scope: "global" }, + }), + ).resolves.toMatchObject({ status_code: 401 }); + + // Admin clears the middleware-level gate; the handler passes global + // through instead of a project filter. + const { functions: f2, captured } = createApi(); + const ok = await f2.get("api::audit")!({ + headers: adminHeaders(), + query_params: { scope: "global" }, + }); + expect(ok.status_code).toBe(200); + const auditCall = captured.find((c) => c.function_id === "mem::audit-query"); + expect(auditCall?.payload).not.toHaveProperty("project"); + }); +}); + +describe("mem::export project filtering", () => { + it("returns filtered data for a project and everything only for global", async () => { + const store = new Map>(); + const kv = makeKv(store); + const functions = new Map< + string, + (data?: Record) => Promise + >(); + const sdk = { + registerFunction: (id: string, handler: never) => { + functions.set(id, handler as never); + }, + registerTrigger: () => {}, + }; + registerExportImportFunction(sdk as never, kv as never); + + await kv.set(KV.sessions, "ses-a", { + id: "ses-a", + project: PROJECT_A, + }); + await kv.set(KV.sessions, "ses-b", { + id: "ses-b", + project: PROJECT_B, + }); + await kv.set(`mem:obs:ses-a`, "obs-1", { id: "obs-1" }); + await kv.set(KV.memories, "mem-a", { + id: "mem-a", + project: PROJECT_A, + }); + await kv.set(KV.memories, "mem-b", { + id: "mem-b", + project: PROJECT_B, + }); + await kv.set(KV.summaries, "sum-a", { + sessionId: "ses-a", + project: PROJECT_A, + }); + await kv.set(KV.sentinels, "sen-x", { id: "sen-x" }); + await kv.set(KV.accessLog, "acc-x", { memoryId: "mem-a" }); + + const memExport = functions.get("mem::export")!; + + const scopedExport = (await memExport({ project: PROJECT_A })) as { + sessions: Array<{ id: string }>; + memories: Array<{ id: string }>; + summaries: Array<{ sessionId: string }>; + observations: Record; + sentinels?: unknown[]; + accessLogs?: unknown[]; + }; + expect(scopedExport.sessions.map((s) => s.id)).toEqual(["ses-a"]); + expect(scopedExport.memories.map((m) => m.id)).toEqual(["mem-a"]); + expect(scopedExport.summaries.map((s) => s.sessionId)).toEqual(["ses-a"]); + expect(Object.keys(scopedExport.observations)).toEqual(["ses-a"]); + // Non-attributable sections are omitted rather than leaked. + expect(scopedExport.sentinels).toBeUndefined(); + expect(scopedExport.accessLogs).toBeUndefined(); + + const globalExport = (await memExport({ scope: "global" })) as { + sessions: Array<{ id: string }>; + memories: Array<{ id: string }>; + sentinels?: unknown[]; + }; + expect(globalExport.sessions.map((s) => s.id).sort()).toEqual([ + "ses-a", + "ses-b", + ]); + expect(globalExport.memories.map((m) => m.id).sort()).toEqual([ + "mem-a", + "mem-b", + ]); + expect(globalExport.sentinels).toBeDefined(); + + const unscoped = (await memExport()) as { + memories: Array<{ id: string }>; + }; + expect(unscoped.memories.map((m) => m.id).sort()).toEqual([ + "mem-a", + "mem-b", + ]); + }); +}); + +describe("queryAudit project filtering", () => { + it("filters to entries attributed to the project; global sees all", async () => { + const store = new Map>(); + const kv = makeKv(store); + + await recordAudit(kv as never, "remember", "mem::remember", ["m1"], { + project: PROJECT_A, + }); + await recordAudit(kv as never, "remember", "mem::remember", ["m2"], { + project: PROJECT_B, + }); + await recordAudit(kv as never, "index_persist", "sys::persist", [], {}); + + const projectEntries = await queryAudit(kv as never, { + project: PROJECT_A, + }); + expect(projectEntries).toHaveLength(1); + expect(projectEntries[0]!.details["project"]).toBe(PROJECT_A); + + const allEntries = await queryAudit(kv as never, {}); + expect(allEntries).toHaveLength(3); + }); +}); From 401a7b2fb0d31ee2539c926063e22db0f7c7fcb9 Mon Sep 17 00:00:00 2001 From: ChronodeAi Date: Mon, 24 Aug 2026 15:24:34 -0500 Subject: [PATCH 3/9] test(api): track api::export forwarding through the scoped payload spread memories-pagination asserted a source pattern that pinned the unscoped payload passthrough; project scoping spreads it into { ...payload, project } so the assertion now matches the scoped forwarding while still pinning that pagination params reach mem::export. (cherry picked from commit 2475fb13f301a6cabcbff8fdc2b8ff4972ef9a0e) --- test/memories-pagination.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/memories-pagination.test.ts b/test/memories-pagination.test.ts index c3275df61..840edd316 100644 --- a/test/memories-pagination.test.ts +++ b/test/memories-pagination.test.ts @@ -30,8 +30,9 @@ describe("memories + export pagination (#544)", () => { expect(api).toMatch(/query_params\?\.\["offset"\]/); // The payload object is named `payload` in our handler; assert it is // forwarded to mem::export rather than the previous empty object. + // Project scoping spreads it into a payload carrying project/scope. expect(api).toMatch( - /sdk\.trigger\(\{\s*function_id:\s*"mem::export",\s*payload,/m, + /function_id:\s*"mem::export",\s*payload:\s*\{\s*\.\.\.payload,/m, ); }); From 3dba5efd6129948d8c923439b131f56bf437112f Mon Sep 17 00:00:00 2001 From: ChronodeAi Date: Mon, 24 Aug 2026 15:15:19 -0500 Subject: [PATCH 4/9] fix(project-config): warn-and-fall-back instead of throwing on unnormalizable remotes inferProjectId threw 'configured Git remote cannot be normalized safely' for bare-path and file:// origin remotes, which killed every hook process running inside such a checkout. Those remotes still identify one machine-local checkout, so inferProjectId now warns once per process to stderr and falls back to the stable local/ id. ssh/https identities are unchanged; no throw remains because the input path is typed string. (cherry picked from commit 7b418a0a21af09af1a56f7b279e03ecc9db5caea) --- src/project-config.ts | 13 ++++++++++++- test/project-config.test.ts | 31 +++++++++++++++++++++++++++---- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/project-config.ts b/src/project-config.ts index 0df6b7f8e..00c8992af 100644 --- a/src/project-config.ts +++ b/src/project-config.ts @@ -210,13 +210,24 @@ export function normalizeGitRemote(remote: string): string | undefined { } } +// A bare local path or file:// remote still identifies one machine-local +// checkout; the hashed canonical path keeps that identity stable instead of +// failing every hook process that runs inside such a clone. The warning +// fires once per process so per-event hooks stay quiet. +let warnedUnnormalizableRemote = false; + export function inferProjectId(root: string): string { const remote = git(root, ["remote", "get-url", "origin"]) ?? git(root, ["remote", "get-url", "--all", "upstream"]); const normalizedRemote = remote ? normalizeGitRemote(remote) : undefined; if (remote && !normalizedRemote) { - throw new Error("configured Git remote cannot be normalized safely"); + if (!warnedUnnormalizableRemote) { + warnedUnnormalizableRemote = true; + process.stderr.write( + `[agentmemory] Git remote "${remote}" cannot be normalized to a canonical project id; using local/${projectPathHash(root)} for this checkout\n`, + ); + } } return normalizedRemote ?? `local/${projectPathHash(root)}`; } diff --git a/test/project-config.test.ts b/test/project-config.test.ts index 4b7f40b44..dbbec1615 100644 --- a/test/project-config.test.ts +++ b/test/project-config.test.ts @@ -60,11 +60,34 @@ describe("canonical project configuration", () => { expect(inferProjectId(local)).toBe(inferProjectId(local)); }); - it("fails closed when a configured remote cannot be normalized", () => { - const root = gitProject("not-a-valid-remote"); - expect(() => inferProjectId(root)).toThrow( - "configured Git remote cannot be normalized safely", + it("falls back to a local id (warned, not thrown) for a file:// remote", () => { + const root = gitProject(); + execFileSync( + "git", + [ + "-C", + root, + "remote", + "add", + "origin", + `file://${root}`, + ], ); + expect(inferProjectId(root)).toMatch(/^local\/[a-f0-9]{24}$/); + expect(inferProjectId(root)).toBe(inferProjectId(root)); + }); + + it("falls back to a local id for a bare-path remote", () => { + const target = gitProject(); + const root = gitProject(target); + expect(inferProjectId(root)).toMatch(/^local\/[a-f0-9]{24}$/); + }); + + it("keeps ssh and https remotes on the canonical identity", () => { + const ssh = gitProject("git@github.com:ChronodeAi/Memetics.git"); + expect(inferProjectId(ssh)).toBe("github.com/chronodeai/memetics"); + const https = gitProject("https://github.com/ChronodeAi/Memetics.git"); + expect(inferProjectId(https)).toBe("github.com/chronodeai/memetics"); }); it("normalizes project paths and applies recursive exclusion globs", () => { From e8208add62a0c23bc44671bb5b6eb751e47fdcf5 Mon Sep 17 00:00:00 2001 From: ChronodeAi Date: Mon, 24 Aug 2026 15:18:53 -0500 Subject: [PATCH 5/9] perf(session): throttle stale-session sweep and cap failed-run tracking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit closeStaleSessions() full-scanned KV.sessions on every event::session::started and every POST /agentmemory/session/start. The hot paths now go through maybeCloseStaleSessions(), which sweeps at most once per 60s per process (AGENTMEMORY_STALE_SESSION_SWEEP_MS tunes the interval, 0 restores per-call sweeps); closeStaleSessions itself stays unthrottled for the session-end paths that read their own records. The health module's failedRuns map grew without bound — recordFailedRun now caps it at 100 entries, evicting oldest-first on both the failure and restart-restore paths. (cherry picked from commit aac67e494ec8abbd364ec971855c1d5dd901b263) --- src/functions/session-lifecycle.ts | 36 ++++++ src/health/background-pipeline.ts | 21 +++- src/triggers/api.ts | 4 +- src/triggers/events.ts | 4 +- test/stale-sweep-perf.test.ts | 175 +++++++++++++++++++++++++++++ 5 files changed, 234 insertions(+), 6 deletions(-) create mode 100644 test/stale-sweep-perf.test.ts diff --git a/src/functions/session-lifecycle.ts b/src/functions/session-lifecycle.ts index 67be54aa2..c71b85197 100644 --- a/src/functions/session-lifecycle.ts +++ b/src/functions/session-lifecycle.ts @@ -4,6 +4,42 @@ import type { StateKV } from "../state/kv.js"; import { withKeyedLock } from "../state/keyed-mutex.js"; const DEFAULT_STALE_MS = 24 * 60 * 60 * 1000; +const DEFAULT_SWEEP_INTERVAL_MS = 60_000; + +// closeStaleSessions() full-scans KV.sessions, so running it on every +// session start turns O(sessions) work into per-event cost. The sweep is +// throttled to at most one run per interval (default 60s) per process; +// AGENTMEMORY_STALE_SESSION_SWEEP_MS tunes the interval, and "0" restores a +// sweep on every call. Session-end paths do not depend on this throttle: +// they read and close their own session record directly. +let lastStaleSweepAt = 0; + +export function staleSessionSweepIntervalMs(): number { + const raw = Number(process.env["AGENTMEMORY_STALE_SESSION_SWEEP_MS"]); + return Number.isFinite(raw) && raw >= 0 + ? Math.floor(raw) + : DEFAULT_SWEEP_INTERVAL_MS; +} + +export function resetStaleSessionSweepForTests(): void { + lastStaleSweepAt = 0; +} + +/** + * Run closeStaleSessions() at most once per sweep interval. Returns true + * when the scan actually ran. + */ +export async function maybeCloseStaleSessions( + kv: StateKV, + now = new Date(), + maxAgeMs = DEFAULT_STALE_MS, +): Promise { + const elapsed = now.getTime() - lastStaleSweepAt; + if (elapsed < staleSessionSweepIntervalMs()) return false; + lastStaleSweepAt = now.getTime(); + await closeStaleSessions(kv, now, maxAgeMs); + return true; +} export interface StartSessionInput { sessionId: string; diff --git a/src/health/background-pipeline.ts b/src/health/background-pipeline.ts index 221b2e7bc..89b82930d 100644 --- a/src/health/background-pipeline.ts +++ b/src/health/background-pipeline.ts @@ -50,8 +50,25 @@ type FailedRun = { const activeRuns = new Map(); const failedRuns = new Map(); +// Unbounded growth guard: every failed pipeline run would otherwise stay +// forever (restarts re-restore them), so keep the most recent 100 and drop +// the oldest entries. +const MAX_FAILED_RUNS = 100; let health: BackgroundPipelineHealth = createInitialHealth(); +function recordFailedRun(runId: string, entry: FailedRun): void { + const grew = !failedRuns.has(runId); + failedRuns.set(runId, entry); + // Re-sets never grow the map; when an insert pushes past the cap, evict + // strictly oldest-first (Map preserves insertion order). The freshly + // recorded run sits at the end, so the first key is always older. + while (grew && failedRuns.size > MAX_FAILED_RUNS) { + const oldest = failedRuns.keys().next(); + if (oldest.done || oldest.value === runId) break; + failedRuns.delete(oldest.value); + } +} + function createInitialHealth(): BackgroundPipelineHealth { return { status: "idle", @@ -206,7 +223,7 @@ export function recordBackgroundPipelineFailed(input: { activeRuns.delete(input.runId); const failedAt = nowIso(); const errorCode = pipelineFailureCode(input.error); - failedRuns.set(input.runId, { + recordFailedRun(input.runId, { sessionId: input.sessionId, project: input.project, stage: input.stage, @@ -236,7 +253,7 @@ export function restoreBackgroundPipelineFailure(input: { failedAt?: string; }): void { const failedAt = input.failedAt ?? nowIso(); - failedRuns.set(input.runId, { + recordFailedRun(input.runId, { sessionId: input.sessionId, project: input.project, stage: input.stage, diff --git a/src/triggers/api.ts b/src/triggers/api.ts index 824585054..2bb8ce9e8 100644 --- a/src/triggers/api.ts +++ b/src/triggers/api.ts @@ -36,7 +36,7 @@ import { getBoundViewerPort, getViewerSkipped } from "../viewer/server.js"; import { MAX_FILES_UPPER_BOUND } from "../functions/replay.js"; import { logger } from "../logger.js"; import { - closeStaleSessions, + maybeCloseStaleSessions, startOrResumeSession, } from "../functions/session-lifecycle.js"; import { requireProjectReadScope } from "../project-scope.js"; @@ -1197,7 +1197,7 @@ export function registerApiTriggers( ? body.agentId.trim().slice(0, 128) : undefined; const agentId = requestAgentId ?? getAgentId(); - await closeStaleSessions(kv); + await maybeCloseStaleSessions(kv); let lifecycle: Awaited>; try { lifecycle = await startOrResumeSession(kv, { diff --git a/src/triggers/events.ts b/src/triggers/events.ts index 475b4fe2f..30b674508 100644 --- a/src/triggers/events.ts +++ b/src/triggers/events.ts @@ -11,7 +11,7 @@ import { import { logger } from "../logger.js"; import { withKeyedLock } from "../state/keyed-mutex.js"; import { - closeStaleSessions, + maybeCloseStaleSessions, startOrResumeSession, } from "../functions/session-lifecycle.js"; import { @@ -450,7 +450,7 @@ export function registerEventTriggers(sdk: ISdk, kv: StateKV): void { ? data.agentId.trim().slice(0, 128) : undefined; const agentId = requestAgentId ?? getAgentId(); - await closeStaleSessions(kv); + await maybeCloseStaleSessions(kv); const { session, resumed } = await startOrResumeSession(kv, { sessionId: data.sessionId, project: data.project, diff --git a/test/stale-sweep-perf.test.ts b/test/stale-sweep-perf.test.ts new file mode 100644 index 000000000..343e77063 --- /dev/null +++ b/test/stale-sweep-perf.test.ts @@ -0,0 +1,175 @@ +import { describe, it, expect, beforeEach, afterEach } from "vitest"; +import { KV } from "../src/state/schema.js"; +import type { Session } from "../src/types.js"; +import { + closeStaleSessions, + maybeCloseStaleSessions, + resetStaleSessionSweepForTests, + staleSessionSweepIntervalMs, +} from "../src/functions/session-lifecycle.js"; +import { + getBackgroundPipelineHealth, + recordBackgroundPipelineFailed, + recordBackgroundPipelineAccepted, + recordBackgroundPipelineStarted, + restoreBackgroundPipelineFailure, + resetBackgroundPipelineHealthForTests, +} from "../src/health/background-pipeline.js"; + +function makeKv() { + const store = new Map>(); + return { + get: async (scope: string, key: string): Promise => + (store.get(scope)?.get(key) as T) ?? null, + set: async (scope: string, key: string, value: T): Promise => { + if (!store.has(scope)) store.set(scope, new Map()); + store.get(scope)!.set(key, value); + return value; + }, + update: async () => undefined, + delete: async (scope: string, key: string): Promise => { + store.get(scope)?.delete(key); + }, + list: async (scope: string): Promise => + Array.from(store.get(scope)?.values() ?? []) as T[], + store, + }; +} + +function staleSession(id: string, ageMs: number): Session { + const now = Date.now(); + return { + id, + project: "github.com/example/project", + cwd: "/tmp", + startedAt: new Date(now - ageMs).toISOString(), + updatedAt: new Date(now - ageMs).toISOString(), + status: "active", + observationCount: 0, + } as Session; +} + +describe("stale-session sweep throttle", () => { + const ORIGINAL_ENV = process.env["AGENTMEMORY_STALE_SESSION_SWEEP_MS"]; + + beforeEach(() => { + resetStaleSessionSweepForTests(); + delete process.env["AGENTMEMORY_STALE_SESSION_SWEEP_MS"]; + }); + + afterEach(() => { + if (ORIGINAL_ENV === undefined) { + delete process.env["AGENTMEMORY_STALE_SESSION_SWEEP_MS"]; + } else { + process.env["AGENTMEMORY_STALE_SESSION_SWEEP_MS"] = ORIGINAL_ENV; + } + resetStaleSessionSweepForTests(); + }); + + it("defaults to a 60s interval", () => { + expect(staleSessionSweepIntervalMs()).toBe(60_000); + }); + + it("honours AGENTMEMORY_STALE_SESSION_SWEEP_MS including 0", () => { + process.env["AGENTMEMORY_STALE_SESSION_SWEEP_MS"] = "5000"; + expect(staleSessionSweepIntervalMs()).toBe(5000); + process.env["AGENTMEMORY_STALE_SESSION_SWEEP_MS"] = "0"; + expect(staleSessionSweepIntervalMs()).toBe(0); + process.env["AGENTMEMORY_STALE_SESSION_SWEEP_MS"] = "bogus"; + expect(staleSessionSweepIntervalMs()).toBe(60_000); + process.env["AGENTMEMORY_STALE_SESSION_SWEEP_MS"] = "-3"; + expect(staleSessionSweepIntervalMs()).toBe(60_000); + }); + + it("runs at most once per interval per process", async () => { + process.env["AGENTMEMORY_STALE_SESSION_SWEEP_MS"] = "60000"; + const kv = makeKv(); + await kv.set(KV.sessions, "ses-stale", staleSession("ses-stale", 48 * 3600 * 1000)); + + const t0 = new Date(); + await expect(maybeCloseStaleSessions(kv, t0)).resolves.toBe(true); + expect( + ((await kv.get(KV.sessions, "ses-stale")) as Session).status, + ).toBe("abandoned"); + + // Inside the window: no scan, no state change even with fresh stale data. + await kv.set(KV.sessions, "ses-stale-2", staleSession("ses-stale-2", 48 * 3600 * 1000)); + await expect( + maybeCloseStaleSessions(kv, new Date(t0.getTime() + 30_000)), + ).resolves.toBe(false); + expect( + ((await kv.get(KV.sessions, "ses-stale-2")) as Session).status, + ).toBe("active"); + + // Past the window: the sweep runs again and closes it. + await expect( + maybeCloseStaleSessions(kv, new Date(t0.getTime() + 61_000)), + ).resolves.toBe(true); + expect( + ((await kv.get(KV.sessions, "ses-stale-2")) as Session).status, + ).toBe("abandoned"); + }); + + it("closeStaleSessions itself stays unthrottled for end-path correctness", async () => { + const kv = makeKv(); + await kv.set(KV.sessions, "ses-a", staleSession("ses-a", 25 * 3600 * 1000)); + await expect(closeStaleSessions(kv)).resolves.toBe(1); + await expect(closeStaleSessions(kv)).resolves.toBe(0); + }); +}); + +describe("background pipeline failedRuns cap", () => { + beforeEach(() => { + resetBackgroundPipelineHealthForTests(); + }); + + afterEach(() => { + resetBackgroundPipelineHealthForTests(); + }); + + function failRun(index: number): void { + const runId = `run-${index}`; + recordBackgroundPipelineAccepted({ + runId, + sessionId: `ses-${index}`, + project: "github.com/example/project", + }); + recordBackgroundPipelineStarted({ runId, sessionId: `ses-${index}`, project: "github.com/example/project" }); + recordBackgroundPipelineFailed({ + runId, + sessionId: `ses-${index}`, + project: "github.com/example/project", + stage: "summary", + error: new Error(`boom-${index}`), + }); + } + + it("caps unresolved failures at 100, dropping the oldest", () => { + for (let i = 0; i < 130; i++) failRun(i); + + const health = getBackgroundPipelineHealth(); + expect(health.unresolvedFailed).toBe(100); + // Oldest entries (run-0..29) were evicted; the most recent survive. + expect(health.failedProjects.length).toBeLessThanOrEqual(100); + }); + + it("keeps the newest failure visible after eviction", () => { + for (let i = 0; i < 101; i++) failRun(i); + const health = getBackgroundPipelineHealth(); + expect(health.lastFailureRunId).toBe("run-100"); + expect(health.unresolvedFailed).toBe(100); + }); + + it("restore path respects the same cap", () => { + for (let i = 0; i < 120; i++) { + restoreBackgroundPipelineFailure({ + runId: `restored-${i}`, + sessionId: `ses-${i}`, + project: "github.com/example/project", + stage: "dispatch", + errorCode: "RESTORED", + }); + } + expect(getBackgroundPipelineHealth().unresolvedFailed).toBe(100); + }); +}); From aae55aa6b460320cc2cdc642ecf63a05090501a2 Mon Sep 17 00:00:00 2001 From: ChronodeAi Date: Mon, 24 Aug 2026 15:20:03 -0500 Subject: [PATCH 6/9] docs(readme): lead with the npx engine install, de-pipe the installer script MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the pipe-to-shell 'curl -fsSL ... | sh' engine-install option with the npx flow (npx -y @agentmemory/agentmemory@latest) and keep the iii-installer as a documented alternative that is downloaded, inspected, and run from a file — never piped straight into a shell. The VERSION=0.11.2 pin warning stays. (cherry picked from commit f0403548b77b9f13aee530312f92e6e721368f1a) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2ec00d562..10818c354 100644 --- a/README.md +++ b/README.md @@ -775,7 +775,7 @@ npx -y @agentmemory/mcp | Port conflict | `netstat -ano \| findstr :3111` to see what's bound, then kill it or use `--port ` | | Docker fallback skipped even though Docker is installed | Make sure Docker Desktop is actually running (system tray icon) | -> Note: the iii **engine** is a prebuilt binary, not a cargo crate — don't try to `cargo install` it. (The iii **SDKs** are published on crates.io, npm, and PyPI, but agentmemory doesn't need them.) Supported engine install methods, all pinned to v0.11.2: the prebuilt v0.11.2 binary above, the upstream sh install script **with the version pin** `curl -fsSL https://install.iii.dev/iii/main/install.sh | VERSION=0.11.2 sh` (macOS/Linux), and the Docker image `iiidev/iii:0.11.2`. A bare `install.sh | sh` installs the **latest** engine, which agentmemory does not support — always pass `VERSION=0.11.2`. Easiest of all: just run `npx @agentmemory/agentmemory`, which fetches the pinned engine into `~/.agentmemory/bin` for you. +> Note: the iii **engine** is a prebuilt binary, not a cargo crate — don't try to `cargo install` it. (The iii **SDKs** are published on crates.io, npm, and PyPI, but agentmemory doesn't need them.) Easiest install: `npx -y @agentmemory/agentmemory@latest`, which fetches the pinned engine into `~/.agentmemory/bin` for you. If you'd rather manage the engine yourself, the supported methods are all pinned to v0.11.2: the prebuilt v0.11.2 binary above, the upstream iii-installer script **with the version pin** — download `https://install.iii.dev/iii/main/install.sh`, inspect it, then run `VERSION=0.11.2 sh install.sh` (macOS/Linux) instead of piping it into a shell — and the Docker image `iiidev/iii:0.11.2`. A bare `install.sh | sh` installs the **latest** engine, which agentmemory does not support — always pass `VERSION=0.11.2`. --- From cb9716a9bb0e9a2af792ef964291354b4389f039 Mon Sep 17 00:00:00 2001 From: ChronodeAi Date: Mon, 24 Aug 2026 15:41:55 -0500 Subject: [PATCH 7/9] docs(hooks): pin AGENTMEMORY_PROJECT_NAME precedence over remote-derived identity resolveProject already applies the env override ahead of the canonical remote-derived identity through resolveProjectConfig's layer order; document that contract on the resolver and pin it with a test that proves a normalizable https remote loses to the override. --- src/hooks/_project.ts | 9 +++++++++ test/hook-project.test.ts | 12 ++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/hooks/_project.ts b/src/hooks/_project.ts index b797b51c1..6ca2d3714 100644 --- a/src/hooks/_project.ts +++ b/src/hooks/_project.ts @@ -7,6 +7,15 @@ import { // reads process.env, while preserving variables explicitly set by the caller. loadAgentmemoryEnvironment(); +/** + * Resolve the canonical project id for a hook payload. + * + * @param cwd Working directory the hook observed, when the host provides one. + * @returns The project id in strict precedence order: AGENTMEMORY_PROJECT_NAME + * (or AGENTMEMORY_PROJECT_ID) environment override first, then configured + * manifest/user layers, then the canonical remote-derived identity for the + * directory — never a path basename. + */ export function resolveProject(cwd?: string): string { const target = typeof cwd === "string" && cwd.trim() ? cwd : process.cwd(); diff --git a/test/hook-project.test.ts b/test/hook-project.test.ts index ca53bdc25..747ecdb15 100644 --- a/test/hook-project.test.ts +++ b/test/hook-project.test.ts @@ -48,6 +48,18 @@ describe("resolveProject — canonical hook project resolver", () => { expect(resolveProject(process.cwd())).toBe("my-override"); }); + it("prefers AGENTMEMORY_PROJECT_NAME over a normalizable remote identity", () => { + const root = createGitFixture(); + process.env.AGENTMEMORY_PROJECT_NAME = "override-project"; + try { + // The fixture carries a normalizable https remote that would resolve + // to github.com/example/project on its own; the env override wins. + expect(resolveProject(root)).toBe("override-project"); + } finally { + rmSync(root, { recursive: true, force: true }); + } + }); + it("does not inspect an unsafe remote when an explicit project is configured", () => { const root = mkdtempSync(join(tmpdir(), "amem-local-remote-")); execFileSync("git", ["init", "-q", root]); From 880e07d2dceb496ce119d4fb67cb4b1c4b812d2e Mon Sep 17 00:00:00 2001 From: ChronodeAi Date: Mon, 24 Aug 2026 15:42:07 -0500 Subject: [PATCH 8/9] build(plugin): regenerate hook bundles for the project-config identity fallback The warn-and-fall-back inferProjectId path is bundled into every packaged hook entry via the shared _project chunk. --- .../{_auth-dmt9vymH.mjs => _auth-8LRLcG2I.mjs} | 8 +++++++- ...pture-KP8LxcSz.mjs => _capture-CdxWrCwc.mjs} | 2 +- ...very-BtSVOGKV.mjs => _delivery-C1jg9u5N.mjs} | 2 +- ...vTi-C.mjs => _observe-delivery-BYrh4Ekr.mjs} | 2 +- plugin/scripts/_project-CXCTta9T.mjs | 8 -------- plugin/scripts/_project-VjQrnNqc.mjs | 17 +++++++++++++++++ plugin/scripts/notification.mjs | 4 ++-- plugin/scripts/post-commit.mjs | 6 +++--- plugin/scripts/post-tool-failure.mjs | 6 +++--- plugin/scripts/post-tool-use.mjs | 6 +++--- plugin/scripts/pre-compact.mjs | 4 ++-- plugin/scripts/pre-tool-use.mjs | 4 ++-- plugin/scripts/prompt-submit.mjs | 4 ++-- plugin/scripts/session-end.mjs | 4 ++-- plugin/scripts/session-start.mjs | 6 +++--- plugin/scripts/stop.mjs | 4 ++-- plugin/scripts/subagent-start.mjs | 4 ++-- plugin/scripts/subagent-stop.mjs | 4 ++-- plugin/scripts/task-completed.mjs | 4 ++-- 19 files changed, 57 insertions(+), 42 deletions(-) rename plugin/scripts/{_auth-dmt9vymH.mjs => _auth-8LRLcG2I.mjs} (99%) rename plugin/scripts/{_capture-KP8LxcSz.mjs => _capture-CdxWrCwc.mjs} (99%) rename plugin/scripts/{_delivery-BtSVOGKV.mjs => _delivery-C1jg9u5N.mjs} (97%) rename plugin/scripts/{_observe-delivery-Bt5vTi-C.mjs => _observe-delivery-BYrh4Ekr.mjs} (97%) delete mode 100644 plugin/scripts/_project-CXCTta9T.mjs create mode 100644 plugin/scripts/_project-VjQrnNqc.mjs diff --git a/plugin/scripts/_auth-dmt9vymH.mjs b/plugin/scripts/_auth-8LRLcG2I.mjs similarity index 99% rename from plugin/scripts/_auth-dmt9vymH.mjs rename to plugin/scripts/_auth-8LRLcG2I.mjs index 1d4238f55..2077e64b9 100644 --- a/plugin/scripts/_auth-dmt9vymH.mjs +++ b/plugin/scripts/_auth-8LRLcG2I.mjs @@ -6826,6 +6826,7 @@ function normalizeGitRemote(remote) { return; } } +let warnedUnnormalizableRemote = false; function inferProjectId(root) { const remote = git(root, [ "remote", @@ -6838,7 +6839,12 @@ function inferProjectId(root) { "upstream" ]); const normalizedRemote = remote ? normalizeGitRemote(remote) : void 0; - if (remote && !normalizedRemote) throw new Error("configured Git remote cannot be normalized safely"); + if (remote && !normalizedRemote) { + if (!warnedUnnormalizableRemote) { + warnedUnnormalizableRemote = true; + process.stderr.write(`[agentmemory] Git remote "${remote}" cannot be normalized to a canonical project id; using local/${projectPathHash(root)} for this checkout\n`); + } + } return normalizedRemote ?? `local/${projectPathHash(root)}`; } function readConfigFile(path) { diff --git a/plugin/scripts/_capture-KP8LxcSz.mjs b/plugin/scripts/_capture-CdxWrCwc.mjs similarity index 99% rename from plugin/scripts/_capture-KP8LxcSz.mjs rename to plugin/scripts/_capture-CdxWrCwc.mjs index 1447e9b62..bd5454584 100644 --- a/plugin/scripts/_capture-KP8LxcSz.mjs +++ b/plugin/scripts/_capture-CdxWrCwc.mjs @@ -1,4 +1,4 @@ -import { a as normalizedProjectPath, r as isProjectPathExcluded } from "./_auth-dmt9vymH.mjs"; +import { a as normalizedProjectPath, r as isProjectPathExcluded } from "./_auth-8LRLcG2I.mjs"; import { resolve } from "node:path"; import { createHash } from "node:crypto"; import { execFileSync } from "node:child_process"; diff --git a/plugin/scripts/_delivery-BtSVOGKV.mjs b/plugin/scripts/_delivery-C1jg9u5N.mjs similarity index 97% rename from plugin/scripts/_delivery-BtSVOGKV.mjs rename to plugin/scripts/_delivery-C1jg9u5N.mjs index 41e07573a..ba5c8dd92 100644 --- a/plugin/scripts/_delivery-BtSVOGKV.mjs +++ b/plugin/scripts/_delivery-C1jg9u5N.mjs @@ -1,4 +1,4 @@ -import { n as projectAuthHeaders } from "./_auth-dmt9vymH.mjs"; +import { n as projectAuthHeaders } from "./_auth-8LRLcG2I.mjs"; //#region src/hooks/_delivery.ts var HookDeliveryError = class extends Error { retryable; diff --git a/plugin/scripts/_observe-delivery-Bt5vTi-C.mjs b/plugin/scripts/_observe-delivery-BYrh4Ekr.mjs similarity index 97% rename from plugin/scripts/_observe-delivery-Bt5vTi-C.mjs rename to plugin/scripts/_observe-delivery-BYrh4Ekr.mjs index c69a5c72c..6f5640946 100644 --- a/plugin/scripts/_observe-delivery-Bt5vTi-C.mjs +++ b/plugin/scripts/_observe-delivery-BYrh4Ekr.mjs @@ -1,4 +1,4 @@ -import { n as projectAuthHeaders } from "./_auth-dmt9vymH.mjs"; +import { n as projectAuthHeaders } from "./_auth-8LRLcG2I.mjs"; //#region src/hooks/_observe-delivery.ts const MAX_ATTEMPTS = 2; const REQUEST_TIMEOUT_MS = 250; diff --git a/plugin/scripts/_project-CXCTta9T.mjs b/plugin/scripts/_project-CXCTta9T.mjs deleted file mode 100644 index f2c5e16a7..000000000 --- a/plugin/scripts/_project-CXCTta9T.mjs +++ /dev/null @@ -1,8 +0,0 @@ -import { i as loadAgentmemoryEnvironment, o as resolveProjectConfig } from "./_auth-dmt9vymH.mjs"; -//#region src/hooks/_project.ts -loadAgentmemoryEnvironment(); -function resolveProject(cwd) { - return resolveProjectConfig(typeof cwd === "string" && cwd.trim() ? cwd : process.cwd()).project_id; -} -//#endregion -export { resolveProject as t }; diff --git a/plugin/scripts/_project-VjQrnNqc.mjs b/plugin/scripts/_project-VjQrnNqc.mjs new file mode 100644 index 000000000..c9ad15dde --- /dev/null +++ b/plugin/scripts/_project-VjQrnNqc.mjs @@ -0,0 +1,17 @@ +import { i as loadAgentmemoryEnvironment, o as resolveProjectConfig } from "./_auth-8LRLcG2I.mjs"; +//#region src/hooks/_project.ts +loadAgentmemoryEnvironment(); +/** +* Resolve the canonical project id for a hook payload. +* +* @param cwd Working directory the hook observed, when the host provides one. +* @returns The project id in strict precedence order: AGENTMEMORY_PROJECT_NAME +* (or AGENTMEMORY_PROJECT_ID) environment override first, then configured +* manifest/user layers, then the canonical remote-derived identity for the +* directory — never a path basename. +*/ +function resolveProject(cwd) { + return resolveProjectConfig(typeof cwd === "string" && cwd.trim() ? cwd : process.cwd()).project_id; +} +//#endregion +export { resolveProject as t }; diff --git a/plugin/scripts/notification.mjs b/plugin/scripts/notification.mjs index 3b86034b9..b9fc3af01 100755 --- a/plugin/scripts/notification.mjs +++ b/plugin/scripts/notification.mjs @@ -1,6 +1,6 @@ #!/usr/bin/env node -import { t as resolveProject } from "./_project-CXCTta9T.mjs"; -import { n as reportObservationDeliveryFailure, t as deliverObservation } from "./_observe-delivery-Bt5vTi-C.mjs"; +import { t as resolveProject } from "./_project-VjQrnNqc.mjs"; +import { n as reportObservationDeliveryFailure, t as deliverObservation } from "./_observe-delivery-BYrh4Ekr.mjs"; //#region src/hooks/notification.ts function isSdkChildContext(payload) { if (process.env["AGENTMEMORY_SDK_CHILD"] === "1") return true; diff --git a/plugin/scripts/post-commit.mjs b/plugin/scripts/post-commit.mjs index 95848464f..f4bd21bae 100755 --- a/plugin/scripts/post-commit.mjs +++ b/plugin/scripts/post-commit.mjs @@ -1,7 +1,7 @@ #!/usr/bin/env node -import { t as resolveProject } from "./_project-CXCTta9T.mjs"; -import { n as reportHookDeliveryFailure, t as deliverProjectRequest } from "./_delivery-BtSVOGKV.mjs"; -import { n as credentialFreeWorktreeId, r as parseCommitTransitions } from "./_capture-KP8LxcSz.mjs"; +import { t as resolveProject } from "./_project-VjQrnNqc.mjs"; +import { n as reportHookDeliveryFailure, t as deliverProjectRequest } from "./_delivery-C1jg9u5N.mjs"; +import { n as credentialFreeWorktreeId, r as parseCommitTransitions } from "./_capture-CdxWrCwc.mjs"; import { resolve } from "node:path"; import { execFile } from "node:child_process"; import { pathToFileURL } from "node:url"; diff --git a/plugin/scripts/post-tool-failure.mjs b/plugin/scripts/post-tool-failure.mjs index 80ef49d35..a697c80e2 100755 --- a/plugin/scripts/post-tool-failure.mjs +++ b/plugin/scripts/post-tool-failure.mjs @@ -1,7 +1,7 @@ #!/usr/bin/env node -import { o as resolveProjectConfig } from "./_auth-dmt9vymH.mjs"; -import { n as reportObservationDeliveryFailure, t as deliverObservation } from "./_observe-delivery-Bt5vTi-C.mjs"; -import { t as captureToolEvent } from "./_capture-KP8LxcSz.mjs"; +import { o as resolveProjectConfig } from "./_auth-8LRLcG2I.mjs"; +import { n as reportObservationDeliveryFailure, t as deliverObservation } from "./_observe-delivery-BYrh4Ekr.mjs"; +import { t as captureToolEvent } from "./_capture-CdxWrCwc.mjs"; //#region src/hooks/post-tool-failure.ts function isSdkChildContext(payload) { if (process.env["AGENTMEMORY_SDK_CHILD"] === "1") return true; diff --git a/plugin/scripts/post-tool-use.mjs b/plugin/scripts/post-tool-use.mjs index 6d0371a9f..6939e2ead 100755 --- a/plugin/scripts/post-tool-use.mjs +++ b/plugin/scripts/post-tool-use.mjs @@ -1,7 +1,7 @@ #!/usr/bin/env node -import { o as resolveProjectConfig } from "./_auth-dmt9vymH.mjs"; -import { n as reportObservationDeliveryFailure, t as deliverObservation } from "./_observe-delivery-Bt5vTi-C.mjs"; -import { t as captureToolEvent } from "./_capture-KP8LxcSz.mjs"; +import { o as resolveProjectConfig } from "./_auth-8LRLcG2I.mjs"; +import { n as reportObservationDeliveryFailure, t as deliverObservation } from "./_observe-delivery-BYrh4Ekr.mjs"; +import { t as captureToolEvent } from "./_capture-CdxWrCwc.mjs"; //#region src/hooks/post-tool-use.ts function isSdkChildContext(payload) { if (process.env["AGENTMEMORY_SDK_CHILD"] === "1") return true; diff --git a/plugin/scripts/pre-compact.mjs b/plugin/scripts/pre-compact.mjs index fd2500d97..d38083c8c 100755 --- a/plugin/scripts/pre-compact.mjs +++ b/plugin/scripts/pre-compact.mjs @@ -1,6 +1,6 @@ #!/usr/bin/env node -import { i as loadAgentmemoryEnvironment, n as projectAuthHeaders, t as contextAcknowledgementSecret } from "./_auth-dmt9vymH.mjs"; -import { t as resolveProject } from "./_project-CXCTta9T.mjs"; +import { i as loadAgentmemoryEnvironment, n as projectAuthHeaders, t as contextAcknowledgementSecret } from "./_auth-8LRLcG2I.mjs"; +import { t as resolveProject } from "./_project-VjQrnNqc.mjs"; import { createHash, createHmac, randomUUID } from "node:crypto"; //#region src/hooks/pre-compact.ts function isSdkChildContext(payload) { diff --git a/plugin/scripts/pre-tool-use.mjs b/plugin/scripts/pre-tool-use.mjs index 384a1df60..eef51c53b 100755 --- a/plugin/scripts/pre-tool-use.mjs +++ b/plugin/scripts/pre-tool-use.mjs @@ -1,6 +1,6 @@ #!/usr/bin/env node -import { i as loadAgentmemoryEnvironment, n as projectAuthHeaders } from "./_auth-dmt9vymH.mjs"; -import { t as resolveProject } from "./_project-CXCTta9T.mjs"; +import { i as loadAgentmemoryEnvironment, n as projectAuthHeaders } from "./_auth-8LRLcG2I.mjs"; +import { t as resolveProject } from "./_project-VjQrnNqc.mjs"; //#region src/hooks/pre-tool-use.ts function isSdkChildContext(payload) { if (process.env["AGENTMEMORY_SDK_CHILD"] === "1") return true; diff --git a/plugin/scripts/prompt-submit.mjs b/plugin/scripts/prompt-submit.mjs index 1dcbced85..97a4649b8 100755 --- a/plugin/scripts/prompt-submit.mjs +++ b/plugin/scripts/prompt-submit.mjs @@ -1,6 +1,6 @@ #!/usr/bin/env node -import { t as resolveProject } from "./_project-CXCTta9T.mjs"; -import { n as reportObservationDeliveryFailure, t as deliverObservation } from "./_observe-delivery-Bt5vTi-C.mjs"; +import { t as resolveProject } from "./_project-VjQrnNqc.mjs"; +import { n as reportObservationDeliveryFailure, t as deliverObservation } from "./_observe-delivery-BYrh4Ekr.mjs"; //#region src/hooks/prompt-submit.ts function isSdkChildContext(payload) { if (process.env["AGENTMEMORY_SDK_CHILD"] === "1") return true; diff --git a/plugin/scripts/session-end.mjs b/plugin/scripts/session-end.mjs index 7eeacb6d5..7452e1929 100755 --- a/plugin/scripts/session-end.mjs +++ b/plugin/scripts/session-end.mjs @@ -1,6 +1,6 @@ #!/usr/bin/env node -import { t as resolveProject } from "./_project-CXCTta9T.mjs"; -import { n as reportHookDeliveryFailure, t as deliverProjectRequest } from "./_delivery-BtSVOGKV.mjs"; +import { t as resolveProject } from "./_project-VjQrnNqc.mjs"; +import { n as reportHookDeliveryFailure, t as deliverProjectRequest } from "./_delivery-C1jg9u5N.mjs"; //#region src/hooks/session-end.ts function isSdkChildContext(payload) { if (process.env["AGENTMEMORY_SDK_CHILD"] === "1") return true; diff --git a/plugin/scripts/session-start.mjs b/plugin/scripts/session-start.mjs index a7be025dd..01cc31ff6 100755 --- a/plugin/scripts/session-start.mjs +++ b/plugin/scripts/session-start.mjs @@ -1,7 +1,7 @@ #!/usr/bin/env node -import { o as resolveProjectConfig } from "./_auth-dmt9vymH.mjs"; -import "./_project-CXCTta9T.mjs"; -import { n as reportHookDeliveryFailure, t as deliverProjectRequest } from "./_delivery-BtSVOGKV.mjs"; +import { o as resolveProjectConfig } from "./_auth-8LRLcG2I.mjs"; +import "./_project-VjQrnNqc.mjs"; +import { n as reportHookDeliveryFailure, t as deliverProjectRequest } from "./_delivery-C1jg9u5N.mjs"; //#region src/hooks/session-start.ts function isSdkChildContext(payload) { if (process.env["AGENTMEMORY_SDK_CHILD"] === "1") return true; diff --git a/plugin/scripts/stop.mjs b/plugin/scripts/stop.mjs index 3ffe5fcbb..b0c2dfb8b 100755 --- a/plugin/scripts/stop.mjs +++ b/plugin/scripts/stop.mjs @@ -1,6 +1,6 @@ #!/usr/bin/env node -import { t as resolveProject } from "./_project-CXCTta9T.mjs"; -import { n as reportHookDeliveryFailure, t as deliverProjectRequest } from "./_delivery-BtSVOGKV.mjs"; +import { t as resolveProject } from "./_project-VjQrnNqc.mjs"; +import { n as reportHookDeliveryFailure, t as deliverProjectRequest } from "./_delivery-C1jg9u5N.mjs"; //#region src/hooks/stop.ts function isSdkChildContext(payload) { if (process.env["AGENTMEMORY_SDK_CHILD"] === "1") return true; diff --git a/plugin/scripts/subagent-start.mjs b/plugin/scripts/subagent-start.mjs index 5295268cc..dbdff3abe 100755 --- a/plugin/scripts/subagent-start.mjs +++ b/plugin/scripts/subagent-start.mjs @@ -1,6 +1,6 @@ #!/usr/bin/env node -import { t as resolveProject } from "./_project-CXCTta9T.mjs"; -import { n as reportObservationDeliveryFailure, t as deliverObservation } from "./_observe-delivery-Bt5vTi-C.mjs"; +import { t as resolveProject } from "./_project-VjQrnNqc.mjs"; +import { n as reportObservationDeliveryFailure, t as deliverObservation } from "./_observe-delivery-BYrh4Ekr.mjs"; //#region src/hooks/subagent-start.ts function isSdkChildContext(payload) { if (process.env["AGENTMEMORY_SDK_CHILD"] === "1") return true; diff --git a/plugin/scripts/subagent-stop.mjs b/plugin/scripts/subagent-stop.mjs index 8f6244809..bdab11c24 100755 --- a/plugin/scripts/subagent-stop.mjs +++ b/plugin/scripts/subagent-stop.mjs @@ -1,6 +1,6 @@ #!/usr/bin/env node -import { t as resolveProject } from "./_project-CXCTta9T.mjs"; -import { n as reportObservationDeliveryFailure, t as deliverObservation } from "./_observe-delivery-Bt5vTi-C.mjs"; +import { t as resolveProject } from "./_project-VjQrnNqc.mjs"; +import { n as reportObservationDeliveryFailure, t as deliverObservation } from "./_observe-delivery-BYrh4Ekr.mjs"; //#region src/hooks/subagent-stop.ts function isSdkChildContext(payload) { if (process.env["AGENTMEMORY_SDK_CHILD"] === "1") return true; diff --git a/plugin/scripts/task-completed.mjs b/plugin/scripts/task-completed.mjs index f2c22a82e..5bf09a9bd 100755 --- a/plugin/scripts/task-completed.mjs +++ b/plugin/scripts/task-completed.mjs @@ -1,6 +1,6 @@ #!/usr/bin/env node -import { t as resolveProject } from "./_project-CXCTta9T.mjs"; -import { n as reportObservationDeliveryFailure, t as deliverObservation } from "./_observe-delivery-Bt5vTi-C.mjs"; +import { t as resolveProject } from "./_project-VjQrnNqc.mjs"; +import { n as reportObservationDeliveryFailure, t as deliverObservation } from "./_observe-delivery-BYrh4Ekr.mjs"; //#region src/hooks/task-completed.ts function isSdkChildContext(payload) { if (process.env["AGENTMEMORY_SDK_CHILD"] === "1") return true; From 9f25099319b80a5c0c3763961081904930bafd7b Mon Sep 17 00:00:00 2001 From: ChronodeAi Date: Mon, 24 Aug 2026 15:43:21 -0500 Subject: [PATCH 9/9] chore(consistency): refresh skills references, interface inventory, and r13 manifest for the trimmed train Skills pick up AGENTMEMORY_STALE_SESSION_SWEEP_MS and the scoped memory_export/memory_audit schemas. The inventory re-issues with unchanged denominators (137 REST, 60 tools, 13 hooks, 19 connectors). The r13 manifest recomputes to 171 tracked tests. --- .../reports/g-icm-01-interface-inventory.json | 434 +++++++++--------- ci/r13-test-manifest.json | 6 +- plugin/skills/agentmemory-config/REFERENCE.md | 3 +- .../skills/agentmemory-mcp-tools/REFERENCE.md | 4 +- 4 files changed, 224 insertions(+), 223 deletions(-) diff --git a/.aiwg/reports/g-icm-01-interface-inventory.json b/.aiwg/reports/g-icm-01-interface-inventory.json index bd40b00a5..0a73aad6d 100644 --- a/.aiwg/reports/g-icm-01-interface-inventory.json +++ b/.aiwg/reports/g-icm-01-interface-inventory.json @@ -3,9 +3,9 @@ "control_id": "G-ICM-01", "project_id": "github.com/chronodeai/agentmemory", "source_identity": { - "commit_sha": "a6f8ccb71100a1868d17557ce30cdc064b1f4c8f", - "commit_tree_sha": "7a4fa44687a3e02021060525baf1075dcc3ff3b2", - "inventory_input_sha256": "ee90fbd67c7226ed862b9d098945ad6bdce3059640ed76f0324b6e8df463f1c4" + "commit_sha": "880e07d2dceb496ce119d4fb67cb4b1c4b812d2e", + "commit_tree_sha": "002224455420499ddce943468311ab7d92629909", + "inventory_input_sha256": "c66469376e6ab299f2c760a6c0ea01c002a7fea1c458e9a86999d857231749e7" }, "public_route_allowlist": [ "GET /agentmemory/livez" @@ -38,7 +38,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3640" + "source": "src/triggers/api.ts:3682" }, { "surface_id": "REST:POST:/agentmemory/actions", @@ -48,7 +48,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3596" + "source": "src/triggers/api.ts:3638" }, { "surface_id": "REST:POST:/agentmemory/actions/edges", @@ -58,7 +58,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3681" + "source": "src/triggers/api.ts:3723" }, { "surface_id": "REST:GET:/agentmemory/actions/get", @@ -68,7 +68,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3658" + "source": "src/triggers/api.ts:3700" }, { "surface_id": "REST:POST:/agentmemory/actions/update", @@ -78,7 +78,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3622" + "source": "src/triggers/api.ts:3664" }, { "surface_id": "REST:GET:/agentmemory/audit", @@ -88,7 +88,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3037" + "source": "src/triggers/api.ts:3079" }, { "surface_id": "REST:POST:/agentmemory/auto-forget", @@ -98,7 +98,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:2427" + "source": "src/triggers/api.ts:2449" }, { "surface_id": "REST:GET:/agentmemory/branch/detect", @@ -108,7 +108,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4197" + "source": "src/triggers/api.ts:4239" }, { "surface_id": "REST:GET:/agentmemory/branch/sessions", @@ -118,7 +118,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4227" + "source": "src/triggers/api.ts:4269" }, { "surface_id": "REST:GET:/agentmemory/branch/worktrees", @@ -128,7 +128,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4212" + "source": "src/triggers/api.ts:4254" }, { "surface_id": "REST:POST:/agentmemory/cascade-update", @@ -138,7 +138,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4522" + "source": "src/triggers/api.ts:4564" }, { "surface_id": "REST:GET:/agentmemory/checkpoints", @@ -148,7 +148,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3964" + "source": "src/triggers/api.ts:4006" }, { "surface_id": "REST:POST:/agentmemory/checkpoints", @@ -158,7 +158,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3923" + "source": "src/triggers/api.ts:3965" }, { "surface_id": "REST:POST:/agentmemory/checkpoints/resolve", @@ -168,7 +168,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3947" + "source": "src/triggers/api.ts:3989" }, { "surface_id": "REST:GET:/agentmemory/claude-bridge/read", @@ -178,7 +178,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:2448" + "source": "src/triggers/api.ts:2470" }, { "surface_id": "REST:POST:/agentmemory/claude-bridge/sync", @@ -188,7 +188,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:2469" + "source": "src/triggers/api.ts:2491" }, { "surface_id": "REST:POST:/agentmemory/commit-link", @@ -254,7 +254,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:2952" + "source": "src/triggers/api.ts:2974" }, { "surface_id": "REST:POST:/agentmemory/context", @@ -300,7 +300,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4422" + "source": "src/triggers/api.ts:4464" }, { "surface_id": "REST:POST:/agentmemory/crystals/auto", @@ -310,7 +310,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4434" + "source": "src/triggers/api.ts:4476" }, { "surface_id": "REST:POST:/agentmemory/crystals/create", @@ -320,7 +320,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4380" + "source": "src/triggers/api.ts:4422" }, { "surface_id": "REST:POST:/agentmemory/diagnostics", @@ -330,7 +330,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4443" + "source": "src/triggers/api.ts:4485" }, { "surface_id": "REST:GET:/agentmemory/diagnostics/followup", @@ -352,7 +352,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4452" + "source": "src/triggers/api.ts:4494" }, { "surface_id": "REST:POST:/agentmemory/enrich", @@ -382,7 +382,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:2411" + "source": "src/triggers/api.ts:2433" }, { "surface_id": "REST:GET:/agentmemory/export", @@ -392,7 +392,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:2341" + "source": "src/triggers/api.ts:2363" }, { "surface_id": "REST:GET:/agentmemory/facets", @@ -402,7 +402,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4491" + "source": "src/triggers/api.ts:4533" }, { "surface_id": "REST:POST:/agentmemory/facets", @@ -412,7 +412,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4462" + "source": "src/triggers/api.ts:4504" }, { "surface_id": "REST:POST:/agentmemory/facets/query", @@ -422,7 +422,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4481" + "source": "src/triggers/api.ts:4523" }, { "surface_id": "REST:POST:/agentmemory/facets/remove", @@ -432,7 +432,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4472" + "source": "src/triggers/api.ts:4514" }, { "surface_id": "REST:GET:/agentmemory/facets/stats", @@ -442,7 +442,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4500" + "source": "src/triggers/api.ts:4542" }, { "surface_id": "REST:POST:/agentmemory/file-context", @@ -462,7 +462,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4182" + "source": "src/triggers/api.ts:4224" }, { "surface_id": "REST:POST:/agentmemory/forget", @@ -482,7 +482,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3700" + "source": "src/triggers/api.ts:3742" }, { "surface_id": "REST:POST:/agentmemory/generate-rules", @@ -502,7 +502,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3107" + "source": "src/triggers/api.ts:3149" }, { "surface_id": "REST:DELETE:/agentmemory/governance/memories", @@ -512,7 +512,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3082" + "source": "src/triggers/api.ts:3124" }, { "surface_id": "REST:POST:/agentmemory/graph/build", @@ -522,7 +522,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:2918" + "source": "src/triggers/api.ts:2940" }, { "surface_id": "REST:POST:/agentmemory/graph/extract", @@ -532,7 +532,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:2724" + "source": "src/triggers/api.ts:2746" }, { "surface_id": "REST:POST:/agentmemory/graph/import-graphify", @@ -542,7 +542,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:2791" + "source": "src/triggers/api.ts:2813" }, { "surface_id": "REST:POST:/agentmemory/graph/query", @@ -552,7 +552,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:2525" + "source": "src/triggers/api.ts:2547" }, { "surface_id": "REST:POST:/agentmemory/graph/reset", @@ -562,7 +562,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:2665" + "source": "src/triggers/api.ts:2687" }, { "surface_id": "REST:POST:/agentmemory/graph/snapshot-rebuild", @@ -572,7 +572,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:2619" + "source": "src/triggers/api.ts:2641" }, { "surface_id": "REST:GET:/agentmemory/graph/stats", @@ -582,7 +582,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:2566" + "source": "src/triggers/api.ts:2588" }, { "surface_id": "REST:GET:/agentmemory/health", @@ -602,7 +602,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:2363" + "source": "src/triggers/api.ts:2385" }, { "surface_id": "REST:POST:/agentmemory/index/rebuild", @@ -624,7 +624,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4722" + "source": "src/triggers/api.ts:4764" }, { "surface_id": "REST:POST:/agentmemory/insights/search", @@ -634,7 +634,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4744" + "source": "src/triggers/api.ts:4786" }, { "surface_id": "REST:POST:/agentmemory/leases/acquire", @@ -644,7 +644,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3736" + "source": "src/triggers/api.ts:3778" }, { "surface_id": "REST:POST:/agentmemory/leases/release", @@ -654,7 +654,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3755" + "source": "src/triggers/api.ts:3797" }, { "surface_id": "REST:POST:/agentmemory/leases/renew", @@ -664,7 +664,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3774" + "source": "src/triggers/api.ts:3816" }, { "surface_id": "REST:GET:/agentmemory/lessons", @@ -674,7 +674,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4586" + "source": "src/triggers/api.ts:4628" }, { "surface_id": "REST:POST:/agentmemory/lessons", @@ -684,7 +684,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4551" + "source": "src/triggers/api.ts:4593" }, { "surface_id": "REST:POST:/agentmemory/lessons/delete", @@ -694,7 +694,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4652" + "source": "src/triggers/api.ts:4694" }, { "surface_id": "REST:POST:/agentmemory/lessons/search", @@ -704,7 +704,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4606" + "source": "src/triggers/api.ts:4648" }, { "surface_id": "REST:POST:/agentmemory/lessons/strengthen", @@ -714,7 +714,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4637" + "source": "src/triggers/api.ts:4679" }, { "surface_id": "REST:GET:/agentmemory/livez", @@ -734,7 +734,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3252" + "source": "src/triggers/api.ts:3294" }, { "surface_id": "REST:GET:/agentmemory/memories/:id", @@ -744,7 +744,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3273" + "source": "src/triggers/api.ts:3315" }, { "surface_id": "REST:GET:/agentmemory/mesh/export", @@ -754,7 +754,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4155" + "source": "src/triggers/api.ts:4197" }, { "surface_id": "REST:GET:/agentmemory/mesh/peers", @@ -764,7 +764,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4009" + "source": "src/triggers/api.ts:4051" }, { "surface_id": "REST:POST:/agentmemory/mesh/peers", @@ -774,7 +774,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3989" + "source": "src/triggers/api.ts:4031" }, { "surface_id": "REST:POST:/agentmemory/mesh/receive", @@ -784,7 +784,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4051" + "source": "src/triggers/api.ts:4093" }, { "surface_id": "REST:POST:/agentmemory/mesh/sync", @@ -794,7 +794,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4031" + "source": "src/triggers/api.ts:4073" }, { "surface_id": "REST:POST:/agentmemory/migrate", @@ -814,7 +814,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3717" + "source": "src/triggers/api.ts:3759" }, { "surface_id": "REST:GET:/agentmemory/observations", @@ -846,7 +846,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4669" + "source": "src/triggers/api.ts:4711" }, { "surface_id": "REST:POST:/agentmemory/patterns", @@ -866,7 +866,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3301" + "source": "src/triggers/api.ts:3343" }, { "surface_id": "REST:GET:/agentmemory/profile", @@ -922,7 +922,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4688" + "source": "src/triggers/api.ts:4730" }, { "surface_id": "REST:GET:/agentmemory/relations", @@ -932,7 +932,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3315" + "source": "src/triggers/api.ts:3357" }, { "surface_id": "REST:POST:/agentmemory/relations", @@ -942,7 +942,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:2385" + "source": "src/triggers/api.ts:2407" }, { "surface_id": "REST:POST:/agentmemory/remember", @@ -992,7 +992,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3812" + "source": "src/triggers/api.ts:3854" }, { "surface_id": "REST:POST:/agentmemory/routines", @@ -1002,7 +1002,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3796" + "source": "src/triggers/api.ts:3838" }, { "surface_id": "REST:POST:/agentmemory/routines/run", @@ -1012,7 +1012,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3831" + "source": "src/triggers/api.ts:3873" }, { "surface_id": "REST:GET:/agentmemory/routines/status", @@ -1022,7 +1022,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3849" + "source": "src/triggers/api.ts:3891" }, { "surface_id": "REST:POST:/agentmemory/search", @@ -1044,7 +1044,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3287" + "source": "src/triggers/api.ts:3329" }, { "surface_id": "REST:GET:/agentmemory/sentinels", @@ -1054,7 +1054,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4308" + "source": "src/triggers/api.ts:4350" }, { "surface_id": "REST:POST:/agentmemory/sentinels", @@ -1064,7 +1064,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4271" + "source": "src/triggers/api.ts:4313" }, { "surface_id": "REST:POST:/agentmemory/sentinels/cancel", @@ -1074,7 +1074,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4299" + "source": "src/triggers/api.ts:4341" }, { "surface_id": "REST:POST:/agentmemory/sentinels/check", @@ -1084,7 +1084,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4289" + "source": "src/triggers/api.ts:4331" }, { "surface_id": "REST:POST:/agentmemory/sentinels/trigger", @@ -1094,7 +1094,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4281" + "source": "src/triggers/api.ts:4323" }, { "surface_id": "REST:GET:/agentmemory/session/by-commit", @@ -1162,7 +1162,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3898" + "source": "src/triggers/api.ts:3940" }, { "surface_id": "REST:POST:/agentmemory/signals/send", @@ -1172,7 +1172,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3874" + "source": "src/triggers/api.ts:3916" }, { "surface_id": "REST:GET:/agentmemory/sketches", @@ -1182,7 +1182,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4357" + "source": "src/triggers/api.ts:4399" }, { "surface_id": "REST:POST:/agentmemory/sketches", @@ -1192,7 +1192,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4318" + "source": "src/triggers/api.ts:4360" }, { "surface_id": "REST:POST:/agentmemory/sketches/add", @@ -1202,7 +1202,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4328" + "source": "src/triggers/api.ts:4370" }, { "surface_id": "REST:POST:/agentmemory/sketches/discard", @@ -1212,7 +1212,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4348" + "source": "src/triggers/api.ts:4390" }, { "surface_id": "REST:POST:/agentmemory/sketches/gc", @@ -1222,7 +1222,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4365" + "source": "src/triggers/api.ts:4407" }, { "surface_id": "REST:POST:/agentmemory/sketches/promote", @@ -1232,7 +1232,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4338" + "source": "src/triggers/api.ts:4380" }, { "surface_id": "REST:DELETE:/agentmemory/slot", @@ -1242,7 +1242,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3546" + "source": "src/triggers/api.ts:3588" }, { "surface_id": "REST:GET:/agentmemory/slot", @@ -1252,7 +1252,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3425" + "source": "src/triggers/api.ts:3467" }, { "surface_id": "REST:POST:/agentmemory/slot", @@ -1262,7 +1262,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3476" + "source": "src/triggers/api.ts:3518" }, { "surface_id": "REST:POST:/agentmemory/slot/append", @@ -1272,7 +1272,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3500" + "source": "src/triggers/api.ts:3542" }, { "surface_id": "REST:POST:/agentmemory/slot/reflect", @@ -1282,7 +1282,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3568" + "source": "src/triggers/api.ts:3610" }, { "surface_id": "REST:POST:/agentmemory/slot/replace", @@ -1292,7 +1292,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3526" + "source": "src/triggers/api.ts:3568" }, { "surface_id": "REST:GET:/agentmemory/slots", @@ -1302,7 +1302,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3405" + "source": "src/triggers/api.ts:3447" }, { "surface_id": "REST:POST:/agentmemory/smart-search", @@ -1322,7 +1322,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3147" + "source": "src/triggers/api.ts:3189" }, { "surface_id": "REST:POST:/agentmemory/snapshot/restore", @@ -1332,7 +1332,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3168" + "source": "src/triggers/api.ts:3210" }, { "surface_id": "REST:GET:/agentmemory/snapshots", @@ -1342,7 +1342,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3128" + "source": "src/triggers/api.ts:3170" }, { "surface_id": "REST:POST:/agentmemory/summarize", @@ -1364,7 +1364,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3001" + "source": "src/triggers/api.ts:3023" }, { "surface_id": "REST:GET:/agentmemory/team/profile", @@ -1374,7 +1374,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3019" + "source": "src/triggers/api.ts:3041" }, { "surface_id": "REST:POST:/agentmemory/team/share", @@ -1384,7 +1384,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:2981" + "source": "src/triggers/api.ts:3003" }, { "surface_id": "REST:POST:/agentmemory/timeline", @@ -1404,7 +1404,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4510" + "source": "src/triggers/api.ts:4552" }, { "surface_id": "REST:GET:/agentmemory/viewer", @@ -1414,7 +1414,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4257" + "source": "src/triggers/api.ts:4299" }, { "surface_id": "REST:POST:/agentmemory/vision-embed", @@ -1424,7 +1424,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3390" + "source": "src/triggers/api.ts:3432" }, { "surface_id": "REST:POST:/agentmemory/vision-search", @@ -1434,7 +1434,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3358" + "source": "src/triggers/api.ts:3400" } ], "mcp_transport": [ @@ -1504,8 +1504,8 @@ "surface_id": "MCP:TOOL:memory_audit", "name": "memory_audit", "required": [], - "project_parameter": false, - "scope_parameter": false + "project_parameter": true, + "scope_parameter": true }, { "surface_id": "MCP:TOOL:memory_checkpoint", @@ -1611,8 +1611,8 @@ "surface_id": "MCP:TOOL:memory_export", "name": "memory_export", "required": [], - "project_parameter": false, - "scope_parameter": false + "project_parameter": true, + "scope_parameter": true }, { "surface_id": "MCP:TOOL:memory_facet_query", @@ -2222,28 +2222,28 @@ ], "provider_attempt_sites": [ { - "surface_id": "PROVIDER:ATTEMPT:src/cli/connect/index.ts:141:summarize", + "surface_id": "PROVIDER:ATTEMPT:src/cli/connect/index.ts:159:summarize", "purpose": "query", "kind": "invocation", "method": "summarize", "receiver": null, - "source": "src/cli/connect/index.ts:141" + "source": "src/cli/connect/index.ts:159" }, { - "surface_id": "PROVIDER:ATTEMPT:src/cli/connect/index.ts:158:summarize", + "surface_id": "PROVIDER:ATTEMPT:src/cli/connect/index.ts:176:summarize", "purpose": "query", "kind": "invocation", "method": "summarize", "receiver": null, - "source": "src/cli/connect/index.ts:158" + "source": "src/cli/connect/index.ts:176" }, { - "surface_id": "PROVIDER:ATTEMPT:src/cli/connect/index.ts:171:summarize", + "surface_id": "PROVIDER:ATTEMPT:src/cli/connect/index.ts:189:summarize", "purpose": "query", "kind": "invocation", "method": "summarize", "receiver": null, - "source": "src/cli/connect/index.ts:171" + "source": "src/cli/connect/index.ts:189" }, { "surface_id": "PROVIDER:ATTEMPT:src/eval/self-correct.ts:14:compress", @@ -2655,7 +2655,7 @@ { "surface_id": "REST:GET:/agentmemory/actions", "type": "rest", - "source": "src/triggers/api.ts:3640", + "source": "src/triggers/api.ts:3682", "auth_control": "required", "control_ids": [ "ICM-04", @@ -2680,7 +2680,7 @@ { "surface_id": "REST:POST:/agentmemory/actions", "type": "rest", - "source": "src/triggers/api.ts:3596", + "source": "src/triggers/api.ts:3638", "auth_control": "required", "control_ids": [ "ICM-04", @@ -2705,7 +2705,7 @@ { "surface_id": "REST:POST:/agentmemory/actions/edges", "type": "rest", - "source": "src/triggers/api.ts:3681", + "source": "src/triggers/api.ts:3723", "auth_control": "required", "control_ids": [ "ICM-04", @@ -2730,7 +2730,7 @@ { "surface_id": "REST:GET:/agentmemory/actions/get", "type": "rest", - "source": "src/triggers/api.ts:3658", + "source": "src/triggers/api.ts:3700", "auth_control": "required", "control_ids": [ "ICM-04", @@ -2755,7 +2755,7 @@ { "surface_id": "REST:POST:/agentmemory/actions/update", "type": "rest", - "source": "src/triggers/api.ts:3622", + "source": "src/triggers/api.ts:3664", "auth_control": "required", "control_ids": [ "ICM-04", @@ -2780,7 +2780,7 @@ { "surface_id": "REST:GET:/agentmemory/audit", "type": "rest", - "source": "src/triggers/api.ts:3037", + "source": "src/triggers/api.ts:3079", "auth_control": "required", "control_ids": [ "ICM-04", @@ -2805,7 +2805,7 @@ { "surface_id": "REST:POST:/agentmemory/auto-forget", "type": "rest", - "source": "src/triggers/api.ts:2427", + "source": "src/triggers/api.ts:2449", "auth_control": "required", "control_ids": [ "ICM-04", @@ -2830,7 +2830,7 @@ { "surface_id": "REST:GET:/agentmemory/branch/detect", "type": "rest", - "source": "src/triggers/api.ts:4197", + "source": "src/triggers/api.ts:4239", "auth_control": "required", "control_ids": [ "ICM-04", @@ -2855,7 +2855,7 @@ { "surface_id": "REST:GET:/agentmemory/branch/sessions", "type": "rest", - "source": "src/triggers/api.ts:4227", + "source": "src/triggers/api.ts:4269", "auth_control": "required", "control_ids": [ "ICM-04", @@ -2880,7 +2880,7 @@ { "surface_id": "REST:GET:/agentmemory/branch/worktrees", "type": "rest", - "source": "src/triggers/api.ts:4212", + "source": "src/triggers/api.ts:4254", "auth_control": "required", "control_ids": [ "ICM-04", @@ -2905,7 +2905,7 @@ { "surface_id": "REST:POST:/agentmemory/cascade-update", "type": "rest", - "source": "src/triggers/api.ts:4522", + "source": "src/triggers/api.ts:4564", "auth_control": "required", "control_ids": [ "ICM-04", @@ -2930,7 +2930,7 @@ { "surface_id": "REST:GET:/agentmemory/checkpoints", "type": "rest", - "source": "src/triggers/api.ts:3964", + "source": "src/triggers/api.ts:4006", "auth_control": "required", "control_ids": [ "ICM-04", @@ -2955,7 +2955,7 @@ { "surface_id": "REST:POST:/agentmemory/checkpoints", "type": "rest", - "source": "src/triggers/api.ts:3923", + "source": "src/triggers/api.ts:3965", "auth_control": "required", "control_ids": [ "ICM-04", @@ -2980,7 +2980,7 @@ { "surface_id": "REST:POST:/agentmemory/checkpoints/resolve", "type": "rest", - "source": "src/triggers/api.ts:3947", + "source": "src/triggers/api.ts:3989", "auth_control": "required", "control_ids": [ "ICM-04", @@ -3005,7 +3005,7 @@ { "surface_id": "REST:GET:/agentmemory/claude-bridge/read", "type": "rest", - "source": "src/triggers/api.ts:2448", + "source": "src/triggers/api.ts:2470", "auth_control": "required", "control_ids": [ "ICM-04", @@ -3030,7 +3030,7 @@ { "surface_id": "REST:POST:/agentmemory/claude-bridge/sync", "type": "rest", - "source": "src/triggers/api.ts:2469", + "source": "src/triggers/api.ts:2491", "auth_control": "required", "control_ids": [ "ICM-04", @@ -3170,7 +3170,7 @@ { "surface_id": "REST:POST:/agentmemory/consolidate-pipeline", "type": "rest", - "source": "src/triggers/api.ts:2952", + "source": "src/triggers/api.ts:2974", "auth_control": "required", "control_ids": [ "ICM-04", @@ -3270,7 +3270,7 @@ { "surface_id": "REST:GET:/agentmemory/crystals", "type": "rest", - "source": "src/triggers/api.ts:4422", + "source": "src/triggers/api.ts:4464", "auth_control": "required", "control_ids": [ "ICM-04", @@ -3295,7 +3295,7 @@ { "surface_id": "REST:POST:/agentmemory/crystals/auto", "type": "rest", - "source": "src/triggers/api.ts:4434", + "source": "src/triggers/api.ts:4476", "auth_control": "required", "control_ids": [ "ICM-04", @@ -3320,7 +3320,7 @@ { "surface_id": "REST:POST:/agentmemory/crystals/create", "type": "rest", - "source": "src/triggers/api.ts:4380", + "source": "src/triggers/api.ts:4422", "auth_control": "required", "control_ids": [ "ICM-04", @@ -3345,7 +3345,7 @@ { "surface_id": "REST:POST:/agentmemory/diagnostics", "type": "rest", - "source": "src/triggers/api.ts:4443", + "source": "src/triggers/api.ts:4485", "auth_control": "required", "control_ids": [ "ICM-04", @@ -3395,7 +3395,7 @@ { "surface_id": "REST:POST:/agentmemory/diagnostics/heal", "type": "rest", - "source": "src/triggers/api.ts:4452", + "source": "src/triggers/api.ts:4494", "auth_control": "required", "control_ids": [ "ICM-04", @@ -3470,7 +3470,7 @@ { "surface_id": "REST:POST:/agentmemory/evolve", "type": "rest", - "source": "src/triggers/api.ts:2411", + "source": "src/triggers/api.ts:2433", "auth_control": "required", "control_ids": [ "ICM-04", @@ -3495,7 +3495,7 @@ { "surface_id": "REST:GET:/agentmemory/export", "type": "rest", - "source": "src/triggers/api.ts:2341", + "source": "src/triggers/api.ts:2363", "auth_control": "required", "control_ids": [ "ICM-04", @@ -3520,7 +3520,7 @@ { "surface_id": "REST:GET:/agentmemory/facets", "type": "rest", - "source": "src/triggers/api.ts:4491", + "source": "src/triggers/api.ts:4533", "auth_control": "required", "control_ids": [ "ICM-04", @@ -3545,7 +3545,7 @@ { "surface_id": "REST:POST:/agentmemory/facets", "type": "rest", - "source": "src/triggers/api.ts:4462", + "source": "src/triggers/api.ts:4504", "auth_control": "required", "control_ids": [ "ICM-04", @@ -3570,7 +3570,7 @@ { "surface_id": "REST:POST:/agentmemory/facets/query", "type": "rest", - "source": "src/triggers/api.ts:4481", + "source": "src/triggers/api.ts:4523", "auth_control": "required", "control_ids": [ "ICM-04", @@ -3595,7 +3595,7 @@ { "surface_id": "REST:POST:/agentmemory/facets/remove", "type": "rest", - "source": "src/triggers/api.ts:4472", + "source": "src/triggers/api.ts:4514", "auth_control": "required", "control_ids": [ "ICM-04", @@ -3620,7 +3620,7 @@ { "surface_id": "REST:GET:/agentmemory/facets/stats", "type": "rest", - "source": "src/triggers/api.ts:4500", + "source": "src/triggers/api.ts:4542", "auth_control": "required", "control_ids": [ "ICM-04", @@ -3670,7 +3670,7 @@ { "surface_id": "REST:POST:/agentmemory/flow/compress", "type": "rest", - "source": "src/triggers/api.ts:4182", + "source": "src/triggers/api.ts:4224", "auth_control": "required", "control_ids": [ "ICM-04", @@ -3720,7 +3720,7 @@ { "surface_id": "REST:GET:/agentmemory/frontier", "type": "rest", - "source": "src/triggers/api.ts:3700", + "source": "src/triggers/api.ts:3742", "auth_control": "required", "control_ids": [ "ICM-04", @@ -3770,7 +3770,7 @@ { "surface_id": "REST:POST:/agentmemory/governance/bulk-delete", "type": "rest", - "source": "src/triggers/api.ts:3107", + "source": "src/triggers/api.ts:3149", "auth_control": "required", "control_ids": [ "ICM-04", @@ -3795,7 +3795,7 @@ { "surface_id": "REST:DELETE:/agentmemory/governance/memories", "type": "rest", - "source": "src/triggers/api.ts:3082", + "source": "src/triggers/api.ts:3124", "auth_control": "required", "control_ids": [ "ICM-04", @@ -3820,7 +3820,7 @@ { "surface_id": "REST:POST:/agentmemory/graph/build", "type": "rest", - "source": "src/triggers/api.ts:2918", + "source": "src/triggers/api.ts:2940", "auth_control": "required", "control_ids": [ "ICM-04", @@ -3845,7 +3845,7 @@ { "surface_id": "REST:POST:/agentmemory/graph/extract", "type": "rest", - "source": "src/triggers/api.ts:2724", + "source": "src/triggers/api.ts:2746", "auth_control": "required", "control_ids": [ "ICM-04", @@ -3870,7 +3870,7 @@ { "surface_id": "REST:POST:/agentmemory/graph/import-graphify", "type": "rest", - "source": "src/triggers/api.ts:2791", + "source": "src/triggers/api.ts:2813", "auth_control": "required", "control_ids": [ "ICM-04", @@ -3895,7 +3895,7 @@ { "surface_id": "REST:POST:/agentmemory/graph/query", "type": "rest", - "source": "src/triggers/api.ts:2525", + "source": "src/triggers/api.ts:2547", "auth_control": "required", "control_ids": [ "ICM-04", @@ -3920,7 +3920,7 @@ { "surface_id": "REST:POST:/agentmemory/graph/reset", "type": "rest", - "source": "src/triggers/api.ts:2665", + "source": "src/triggers/api.ts:2687", "auth_control": "required", "control_ids": [ "ICM-04", @@ -3945,7 +3945,7 @@ { "surface_id": "REST:POST:/agentmemory/graph/snapshot-rebuild", "type": "rest", - "source": "src/triggers/api.ts:2619", + "source": "src/triggers/api.ts:2641", "auth_control": "required", "control_ids": [ "ICM-13" @@ -3966,7 +3966,7 @@ { "surface_id": "REST:GET:/agentmemory/graph/stats", "type": "rest", - "source": "src/triggers/api.ts:2566", + "source": "src/triggers/api.ts:2588", "auth_control": "required", "control_ids": [ "ICM-04", @@ -4015,7 +4015,7 @@ { "surface_id": "REST:POST:/agentmemory/import", "type": "rest", - "source": "src/triggers/api.ts:2363", + "source": "src/triggers/api.ts:2385", "auth_control": "required", "control_ids": [ "ICM-04", @@ -4061,7 +4061,7 @@ { "surface_id": "REST:GET:/agentmemory/insights", "type": "rest", - "source": "src/triggers/api.ts:4722", + "source": "src/triggers/api.ts:4764", "auth_control": "required", "control_ids": [ "ICM-08" @@ -4081,7 +4081,7 @@ { "surface_id": "REST:POST:/agentmemory/insights/search", "type": "rest", - "source": "src/triggers/api.ts:4744", + "source": "src/triggers/api.ts:4786", "auth_control": "required", "control_ids": [ "ICM-08" @@ -4101,7 +4101,7 @@ { "surface_id": "REST:POST:/agentmemory/leases/acquire", "type": "rest", - "source": "src/triggers/api.ts:3736", + "source": "src/triggers/api.ts:3778", "auth_control": "required", "control_ids": [ "ICM-04", @@ -4126,7 +4126,7 @@ { "surface_id": "REST:POST:/agentmemory/leases/release", "type": "rest", - "source": "src/triggers/api.ts:3755", + "source": "src/triggers/api.ts:3797", "auth_control": "required", "control_ids": [ "ICM-04", @@ -4151,7 +4151,7 @@ { "surface_id": "REST:POST:/agentmemory/leases/renew", "type": "rest", - "source": "src/triggers/api.ts:3774", + "source": "src/triggers/api.ts:3816", "auth_control": "required", "control_ids": [ "ICM-04", @@ -4176,7 +4176,7 @@ { "surface_id": "REST:GET:/agentmemory/lessons", "type": "rest", - "source": "src/triggers/api.ts:4586", + "source": "src/triggers/api.ts:4628", "auth_control": "required", "control_ids": [ "ICM-08" @@ -4196,7 +4196,7 @@ { "surface_id": "REST:POST:/agentmemory/lessons", "type": "rest", - "source": "src/triggers/api.ts:4551", + "source": "src/triggers/api.ts:4593", "auth_control": "required", "control_ids": [ "ICM-08" @@ -4216,7 +4216,7 @@ { "surface_id": "REST:POST:/agentmemory/lessons/delete", "type": "rest", - "source": "src/triggers/api.ts:4652", + "source": "src/triggers/api.ts:4694", "auth_control": "required", "control_ids": [ "ICM-08" @@ -4236,7 +4236,7 @@ { "surface_id": "REST:POST:/agentmemory/lessons/search", "type": "rest", - "source": "src/triggers/api.ts:4606", + "source": "src/triggers/api.ts:4648", "auth_control": "required", "control_ids": [ "ICM-08" @@ -4256,7 +4256,7 @@ { "surface_id": "REST:POST:/agentmemory/lessons/strengthen", "type": "rest", - "source": "src/triggers/api.ts:4637", + "source": "src/triggers/api.ts:4679", "auth_control": "required", "control_ids": [ "ICM-08" @@ -4301,7 +4301,7 @@ { "surface_id": "REST:GET:/agentmemory/memories", "type": "rest", - "source": "src/triggers/api.ts:3252", + "source": "src/triggers/api.ts:3294", "auth_control": "required", "control_ids": [ "ICM-04", @@ -4326,7 +4326,7 @@ { "surface_id": "REST:GET:/agentmemory/memories/:id", "type": "rest", - "source": "src/triggers/api.ts:3273", + "source": "src/triggers/api.ts:3315", "auth_control": "required", "control_ids": [ "ICM-04", @@ -4351,7 +4351,7 @@ { "surface_id": "REST:GET:/agentmemory/mesh/export", "type": "rest", - "source": "src/triggers/api.ts:4155", + "source": "src/triggers/api.ts:4197", "auth_control": "required", "control_ids": [ "ICM-04", @@ -4376,7 +4376,7 @@ { "surface_id": "REST:GET:/agentmemory/mesh/peers", "type": "rest", - "source": "src/triggers/api.ts:4009", + "source": "src/triggers/api.ts:4051", "auth_control": "required", "control_ids": [ "ICM-04", @@ -4401,7 +4401,7 @@ { "surface_id": "REST:POST:/agentmemory/mesh/peers", "type": "rest", - "source": "src/triggers/api.ts:3989", + "source": "src/triggers/api.ts:4031", "auth_control": "required", "control_ids": [ "ICM-04", @@ -4426,7 +4426,7 @@ { "surface_id": "REST:POST:/agentmemory/mesh/receive", "type": "rest", - "source": "src/triggers/api.ts:4051", + "source": "src/triggers/api.ts:4093", "auth_control": "required", "control_ids": [ "ICM-04", @@ -4451,7 +4451,7 @@ { "surface_id": "REST:POST:/agentmemory/mesh/sync", "type": "rest", - "source": "src/triggers/api.ts:4031", + "source": "src/triggers/api.ts:4073", "auth_control": "required", "control_ids": [ "ICM-04", @@ -4497,7 +4497,7 @@ { "surface_id": "REST:GET:/agentmemory/next", "type": "rest", - "source": "src/triggers/api.ts:3717", + "source": "src/triggers/api.ts:3759", "auth_control": "required", "control_ids": [ "ICM-04", @@ -4572,7 +4572,7 @@ { "surface_id": "REST:POST:/agentmemory/obsidian/export", "type": "rest", - "source": "src/triggers/api.ts:4669", + "source": "src/triggers/api.ts:4711", "auth_control": "required", "control_ids": [ "ICM-04", @@ -4622,7 +4622,7 @@ { "surface_id": "REST:GET:/agentmemory/procedural", "type": "rest", - "source": "src/triggers/api.ts:3301", + "source": "src/triggers/api.ts:3343", "auth_control": "required", "control_ids": [ "ICM-04", @@ -4736,7 +4736,7 @@ { "surface_id": "REST:POST:/agentmemory/reflect", "type": "rest", - "source": "src/triggers/api.ts:4688", + "source": "src/triggers/api.ts:4730", "auth_control": "required", "control_ids": [ "ICM-04", @@ -4761,7 +4761,7 @@ { "surface_id": "REST:GET:/agentmemory/relations", "type": "rest", - "source": "src/triggers/api.ts:3315", + "source": "src/triggers/api.ts:3357", "auth_control": "required", "control_ids": [ "ICM-04", @@ -4786,7 +4786,7 @@ { "surface_id": "REST:POST:/agentmemory/relations", "type": "rest", - "source": "src/triggers/api.ts:2385", + "source": "src/triggers/api.ts:2407", "auth_control": "required", "control_ids": [ "ICM-04", @@ -4906,7 +4906,7 @@ { "surface_id": "REST:GET:/agentmemory/routines", "type": "rest", - "source": "src/triggers/api.ts:3812", + "source": "src/triggers/api.ts:3854", "auth_control": "required", "control_ids": [ "ICM-04", @@ -4931,7 +4931,7 @@ { "surface_id": "REST:POST:/agentmemory/routines", "type": "rest", - "source": "src/triggers/api.ts:3796", + "source": "src/triggers/api.ts:3838", "auth_control": "required", "control_ids": [ "ICM-04", @@ -4956,7 +4956,7 @@ { "surface_id": "REST:POST:/agentmemory/routines/run", "type": "rest", - "source": "src/triggers/api.ts:3831", + "source": "src/triggers/api.ts:3873", "auth_control": "required", "control_ids": [ "ICM-04", @@ -4981,7 +4981,7 @@ { "surface_id": "REST:GET:/agentmemory/routines/status", "type": "rest", - "source": "src/triggers/api.ts:3849", + "source": "src/triggers/api.ts:3891", "auth_control": "required", "control_ids": [ "ICM-04", @@ -5031,7 +5031,7 @@ { "surface_id": "REST:GET:/agentmemory/semantic", "type": "rest", - "source": "src/triggers/api.ts:3287", + "source": "src/triggers/api.ts:3329", "auth_control": "required", "control_ids": [ "ICM-04", @@ -5056,7 +5056,7 @@ { "surface_id": "REST:GET:/agentmemory/sentinels", "type": "rest", - "source": "src/triggers/api.ts:4308", + "source": "src/triggers/api.ts:4350", "auth_control": "required", "control_ids": [ "ICM-04", @@ -5081,7 +5081,7 @@ { "surface_id": "REST:POST:/agentmemory/sentinels", "type": "rest", - "source": "src/triggers/api.ts:4271", + "source": "src/triggers/api.ts:4313", "auth_control": "required", "control_ids": [ "ICM-04", @@ -5106,7 +5106,7 @@ { "surface_id": "REST:POST:/agentmemory/sentinels/cancel", "type": "rest", - "source": "src/triggers/api.ts:4299", + "source": "src/triggers/api.ts:4341", "auth_control": "required", "control_ids": [ "ICM-04", @@ -5131,7 +5131,7 @@ { "surface_id": "REST:POST:/agentmemory/sentinels/check", "type": "rest", - "source": "src/triggers/api.ts:4289", + "source": "src/triggers/api.ts:4331", "auth_control": "required", "control_ids": [ "ICM-04", @@ -5156,7 +5156,7 @@ { "surface_id": "REST:POST:/agentmemory/sentinels/trigger", "type": "rest", - "source": "src/triggers/api.ts:4281", + "source": "src/triggers/api.ts:4323", "auth_control": "required", "control_ids": [ "ICM-04", @@ -5296,7 +5296,7 @@ { "surface_id": "REST:GET:/agentmemory/signals", "type": "rest", - "source": "src/triggers/api.ts:3898", + "source": "src/triggers/api.ts:3940", "auth_control": "required", "control_ids": [ "ICM-04", @@ -5321,7 +5321,7 @@ { "surface_id": "REST:POST:/agentmemory/signals/send", "type": "rest", - "source": "src/triggers/api.ts:3874", + "source": "src/triggers/api.ts:3916", "auth_control": "required", "control_ids": [ "ICM-04", @@ -5346,7 +5346,7 @@ { "surface_id": "REST:GET:/agentmemory/sketches", "type": "rest", - "source": "src/triggers/api.ts:4357", + "source": "src/triggers/api.ts:4399", "auth_control": "required", "control_ids": [ "ICM-04", @@ -5371,7 +5371,7 @@ { "surface_id": "REST:POST:/agentmemory/sketches", "type": "rest", - "source": "src/triggers/api.ts:4318", + "source": "src/triggers/api.ts:4360", "auth_control": "required", "control_ids": [ "ICM-04", @@ -5396,7 +5396,7 @@ { "surface_id": "REST:POST:/agentmemory/sketches/add", "type": "rest", - "source": "src/triggers/api.ts:4328", + "source": "src/triggers/api.ts:4370", "auth_control": "required", "control_ids": [ "ICM-04", @@ -5421,7 +5421,7 @@ { "surface_id": "REST:POST:/agentmemory/sketches/discard", "type": "rest", - "source": "src/triggers/api.ts:4348", + "source": "src/triggers/api.ts:4390", "auth_control": "required", "control_ids": [ "ICM-04", @@ -5446,7 +5446,7 @@ { "surface_id": "REST:POST:/agentmemory/sketches/gc", "type": "rest", - "source": "src/triggers/api.ts:4365", + "source": "src/triggers/api.ts:4407", "auth_control": "required", "control_ids": [ "ICM-04", @@ -5471,7 +5471,7 @@ { "surface_id": "REST:POST:/agentmemory/sketches/promote", "type": "rest", - "source": "src/triggers/api.ts:4338", + "source": "src/triggers/api.ts:4380", "auth_control": "required", "control_ids": [ "ICM-04", @@ -5496,7 +5496,7 @@ { "surface_id": "REST:DELETE:/agentmemory/slot", "type": "rest", - "source": "src/triggers/api.ts:3546", + "source": "src/triggers/api.ts:3588", "auth_control": "required", "control_ids": [ "ICM-11", @@ -5520,7 +5520,7 @@ { "surface_id": "REST:GET:/agentmemory/slot", "type": "rest", - "source": "src/triggers/api.ts:3425", + "source": "src/triggers/api.ts:3467", "auth_control": "required", "control_ids": [ "ICM-11", @@ -5544,7 +5544,7 @@ { "surface_id": "REST:POST:/agentmemory/slot", "type": "rest", - "source": "src/triggers/api.ts:3476", + "source": "src/triggers/api.ts:3518", "auth_control": "required", "control_ids": [ "ICM-11", @@ -5568,7 +5568,7 @@ { "surface_id": "REST:POST:/agentmemory/slot/append", "type": "rest", - "source": "src/triggers/api.ts:3500", + "source": "src/triggers/api.ts:3542", "auth_control": "required", "control_ids": [ "ICM-11", @@ -5592,7 +5592,7 @@ { "surface_id": "REST:POST:/agentmemory/slot/reflect", "type": "rest", - "source": "src/triggers/api.ts:3568", + "source": "src/triggers/api.ts:3610", "auth_control": "required", "control_ids": [ "ICM-11", @@ -5616,7 +5616,7 @@ { "surface_id": "REST:POST:/agentmemory/slot/replace", "type": "rest", - "source": "src/triggers/api.ts:3526", + "source": "src/triggers/api.ts:3568", "auth_control": "required", "control_ids": [ "ICM-11", @@ -5640,7 +5640,7 @@ { "surface_id": "REST:GET:/agentmemory/slots", "type": "rest", - "source": "src/triggers/api.ts:3405", + "source": "src/triggers/api.ts:3447", "auth_control": "required", "control_ids": [ "ICM-11", @@ -5689,7 +5689,7 @@ { "surface_id": "REST:POST:/agentmemory/snapshot/create", "type": "rest", - "source": "src/triggers/api.ts:3147", + "source": "src/triggers/api.ts:3189", "auth_control": "required", "control_ids": [ "ICM-13" @@ -5710,7 +5710,7 @@ { "surface_id": "REST:POST:/agentmemory/snapshot/restore", "type": "rest", - "source": "src/triggers/api.ts:3168", + "source": "src/triggers/api.ts:3210", "auth_control": "required", "control_ids": [ "ICM-13" @@ -5731,7 +5731,7 @@ { "surface_id": "REST:GET:/agentmemory/snapshots", "type": "rest", - "source": "src/triggers/api.ts:3128", + "source": "src/triggers/api.ts:3170", "auth_control": "required", "control_ids": [ "ICM-13" @@ -5777,7 +5777,7 @@ { "surface_id": "REST:GET:/agentmemory/team/feed", "type": "rest", - "source": "src/triggers/api.ts:3001", + "source": "src/triggers/api.ts:3023", "auth_control": "required", "control_ids": [ "ICM-04", @@ -5802,7 +5802,7 @@ { "surface_id": "REST:GET:/agentmemory/team/profile", "type": "rest", - "source": "src/triggers/api.ts:3019", + "source": "src/triggers/api.ts:3041", "auth_control": "required", "control_ids": [ "ICM-04", @@ -5827,7 +5827,7 @@ { "surface_id": "REST:POST:/agentmemory/team/share", "type": "rest", - "source": "src/triggers/api.ts:2981", + "source": "src/triggers/api.ts:3003", "auth_control": "required", "control_ids": [ "ICM-04", @@ -5877,7 +5877,7 @@ { "surface_id": "REST:POST:/agentmemory/verify", "type": "rest", - "source": "src/triggers/api.ts:4510", + "source": "src/triggers/api.ts:4552", "auth_control": "required", "control_ids": [ "ICM-04", @@ -5902,7 +5902,7 @@ { "surface_id": "REST:GET:/agentmemory/viewer", "type": "rest", - "source": "src/triggers/api.ts:4257", + "source": "src/triggers/api.ts:4299", "auth_control": "required", "control_ids": [ "ICM-11", @@ -5926,7 +5926,7 @@ { "surface_id": "REST:POST:/agentmemory/vision-embed", "type": "rest", - "source": "src/triggers/api.ts:3390", + "source": "src/triggers/api.ts:3432", "auth_control": "required", "control_ids": [ "ICM-04", @@ -5951,7 +5951,7 @@ { "surface_id": "REST:POST:/agentmemory/vision-search", "type": "rest", - "source": "src/triggers/api.ts:3358", + "source": "src/triggers/api.ts:3400", "auth_control": "required", "control_ids": [ "ICM-05", @@ -10663,12 +10663,12 @@ ] }, { - "surface_id": "PROVIDER:ATTEMPT:src/cli/connect/index.ts:141:summarize", + "surface_id": "PROVIDER:ATTEMPT:src/cli/connect/index.ts:159:summarize", "purpose": "query", "kind": "invocation", "method": "summarize", "receiver": null, - "source": "src/cli/connect/index.ts:141", + "source": "src/cli/connect/index.ts:159", "type": "provider-attempt", "auth_control": "processing-policy", "control_ids": [ @@ -10689,12 +10689,12 @@ ] }, { - "surface_id": "PROVIDER:ATTEMPT:src/cli/connect/index.ts:158:summarize", + "surface_id": "PROVIDER:ATTEMPT:src/cli/connect/index.ts:176:summarize", "purpose": "query", "kind": "invocation", "method": "summarize", "receiver": null, - "source": "src/cli/connect/index.ts:158", + "source": "src/cli/connect/index.ts:176", "type": "provider-attempt", "auth_control": "processing-policy", "control_ids": [ @@ -10715,12 +10715,12 @@ ] }, { - "surface_id": "PROVIDER:ATTEMPT:src/cli/connect/index.ts:171:summarize", + "surface_id": "PROVIDER:ATTEMPT:src/cli/connect/index.ts:189:summarize", "purpose": "query", "kind": "invocation", "method": "summarize", "receiver": null, - "source": "src/cli/connect/index.ts:171", + "source": "src/cli/connect/index.ts:189", "type": "provider-attempt", "auth_control": "processing-policy", "control_ids": [ diff --git a/ci/r13-test-manifest.json b/ci/r13-test-manifest.json index cdde1acaf..9436e070d 100644 --- a/ci/r13-test-manifest.json +++ b/ci/r13-test-manifest.json @@ -1,5 +1,5 @@ { - "count": 168, - "sha256": "b35ad81c97bf698167b70254a4b8c19df914a7526043e1744b8009abc36f6ec7", - "content_sha256": "e4b93bad47233b674013dc7d58161e41c8c555ab7487bf746400d86b3d2a9da2" + "count": 171, + "sha256": "dfb0a43ae58136529042da617518eddb884a379233b038ca6970aa46d993ac8f", + "content_sha256": "4a9a6cfa26b4796eadf14ec294559de28dfdfba7f7083c65fc7bbd479c6326b8" } diff --git a/plugin/skills/agentmemory-config/REFERENCE.md b/plugin/skills/agentmemory-config/REFERENCE.md index 1282d44e2..a0daf9dcb 100644 --- a/plugin/skills/agentmemory-config/REFERENCE.md +++ b/plugin/skills/agentmemory-config/REFERENCE.md @@ -3,7 +3,7 @@ Generated by scanning `src/` for `AGENTMEMORY_*` usage. Do not edit the block below by hand; run `npm run skills:gen` after adding or removing a variable. Internal markers ending in two underscores are excluded. -Configuration is read from the environment and from `~/.agentmemory/.env` (no `export` prefix). 66 recognized variables: +Configuration is read from the environment and from `~/.agentmemory/.env` (no `export` prefix). 67 recognized variables: - `AGENTMEMORY_ADMIN_SECRET` - `AGENTMEMORY_ADMIN_SECRET_FILE` @@ -58,6 +58,7 @@ Configuration is read from the environment and from `~/.agentmemory/.env` (no `e - `AGENTMEMORY_SESSION_ID` - `AGENTMEMORY_SLOTS` - `AGENTMEMORY_SOURCE_ROOTS` +- `AGENTMEMORY_STALE_SESSION_SWEEP_MS` - `AGENTMEMORY_STARTUP_AUDIT_GAP_MAX_ENTRIES` - `AGENTMEMORY_STARTUP_GOVERNANCE_MAX_AUDIT_ENTRIES` - `AGENTMEMORY_STARTUP_RECONCILE_MAX_ENTRIES` diff --git a/plugin/skills/agentmemory-mcp-tools/REFERENCE.md b/plugin/skills/agentmemory-mcp-tools/REFERENCE.md index 55a5751a5..3f1f52019 100644 --- a/plugin/skills/agentmemory-mcp-tools/REFERENCE.md +++ b/plugin/skills/agentmemory-mcp-tools/REFERENCE.md @@ -9,7 +9,7 @@ agentmemory exposes 60 MCP tools. 8 are in the lean core set (`--tools core` or | --- | --- | --- | --- | | `memory_action_create` | | `title`*: string, `description`: string, `priority`: number, `project`: string, `tags`: string, `parentId`: string, `requires`: string | Create an actionable work item with typed dependencies. Actions track what agents need to do and how work items relate to each other. | | `memory_action_update` | | `actionId`*: string, `status`: string, `result`: string, `priority`: number | Update an action's status, priority, or details. Set status to 'done' to complete it and unblock dependent actions. | -| `memory_audit` | | `operation`: string, `limit`: number | View the audit trail of memory operations. | +| `memory_audit` | | `operation`: string, `limit`: number, `project`: string, `scope`: string | View the audit trail of memory operations for one project (or all with admin global scope). | | `memory_checkpoint` | | `operation`*: string, `name`: string, `checkpointId`: string, `status`: string, `type`: string, `linkedActionIds`: string | Create or resolve an external checkpoint (CI result, approval, deploy status) that gates action progress. | | `memory_claude_bridge_sync` | | `direction`*: string | Sync memory state to/from Claude Code's native MEMORY.md file. | | `memory_commit_link` | | `sha`*: string, `sessionId`: string, `project`*: string, `baseHeadSha`: string, `worktreeId`: string, `fileTransitions`: array | Link a verified Git commit SHA to its canonical project and optional coding session. | @@ -21,7 +21,7 @@ agentmemory exposes 60 MCP tools. 8 are in the lean core set (`--tools core` or | `memory_context_packet` | | `project`*: string, `sessionId`*: string, `query`: string, `files`: string, `token_budget`: number, `context_class`: string | Build the project-scoped Recall packet for a coding task with fixed budgets for identity, lessons, episodic history, file history, and provenance. | | `memory_crystallize` | | `actionIds`*: string, `project`*: string, `sessionId`: string | Compress completed action chains into compact crystal digests using LLM summarization. Extracts narrative, key outcomes, files affected, and lessons. | | `memory_diagnose` | yes | `categories`: string | Run health checks across all subsystems (actions, leases, sentinels, sketches, signals, sessions, memories, mesh). Identifies stuck, orphaned, and inconsistent state. | -| `memory_export` | | none | Export all memory data as JSON. | +| `memory_export` | | `project`: string, `scope`: string | Export memory data as JSON for one project (or all with admin global scope). | | `memory_facet_query` | | `matchAll`: string, `matchAny`: string, `targetType`: string | Query targets by facet tags with AND/OR logic. Find all actions tagged priority:urgent AND team:backend. | | `memory_facet_tag` | | `targetId`*: string, `targetType`*: string, `dimension`*: string, `value`*: string | Attach a structured tag (dimension:value) to an action, memory, or observation for multi-dimensional categorization. | | `memory_file_history` | | `files`*: string, `sessionId`: string, `project`: string, `scope`: string | Get past observations about specific files. |