From 901117ccb0a600eee1d81589f6007560c974c92f Mon Sep 17 00:00:00 2001 From: ChronodeAi Date: Mon, 24 Aug 2026 20:06:01 -0500 Subject: [PATCH 01/11] fix(security): anchor graphify import to daemon cwd and admin-gate path/cwd on the REST route MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both explicitPath and cwd were client-controlled: {cwd:'/etc',path:'/etc/graph.json'} passed the resolve-vs-resolve containment check, and a symlinked graph.json escaped it. The engine now ignores caller cwd entirely and computes the default from process.cwd(); explicit paths stay constrained (basename + under-daemon-cwd) as defense in depth. The REST route requires administrative authorization whenever a request carries path or cwd — same authorizeAdministrativeRequest pattern as global-scope governance — while capability callers import the default /graphify-out/graph.json and fail honestly when it is absent. --- src/functions/graph-import.ts | 26 ++-- src/triggers/api.ts | 32 ++++- test/graph-import-scope.test.ts | 221 ++++++++++++++++++++++++++++++-- test/graph-import.test.ts | 26 ++-- 4 files changed, 271 insertions(+), 34 deletions(-) diff --git a/src/functions/graph-import.ts b/src/functions/graph-import.ts index 5f6930f95..28ef6ab63 100644 --- a/src/functions/graph-import.ts +++ b/src/functions/graph-import.ts @@ -217,20 +217,26 @@ export function registerGraphImportFunction(sdk: ISdk, kv: StateKV): void { data as { project?: unknown; scope?: unknown }, "mem::graph::import-graphify", ); + // The daemon's own working directory is the only trusted base for the + // computed default path. A caller-supplied cwd is ignored here — both + // cwd and path are client-controlled on the REST surface, so the route + // admin-gates them before they can reach this function, and this check + // anchors to a value the client cannot steer. const explicitPath = typeof data?.path === "string" ? data.path : undefined; - const cwd = typeof data?.cwd === "string" ? data.cwd : process.cwd(); - const path = explicitPath ?? join(cwd, "graphify-out", "graph.json"); + const baseDir = process.cwd(); + const path = explicitPath ?? join(baseDir, "graphify-out", "graph.json"); if (explicitPath !== undefined) { - // The explicit path is client-supplied, so constrain it to the - // graphify artifact inside the project checkout before any stat, - // read, or error echo can touch it. + // Defense in depth for authorized callers too: an explicit path must + // stay a graph.json under the daemon's checkout, so no stat, read, + // or error echo can reach an arbitrary location (including through a + // relocated symlink directory). const resolvedExplicit = resolve(explicitPath); - const resolvedCwd = resolve(cwd); - const insideCwd = - resolvedExplicit === resolvedCwd || - resolvedExplicit.startsWith(resolvedCwd + sep); - if (basename(resolvedExplicit) !== "graph.json" || !insideCwd) { + const resolvedBase = resolve(baseDir); + const insideBase = + resolvedExplicit === resolvedBase || + resolvedExplicit.startsWith(resolvedBase + sep); + if (basename(resolvedExplicit) !== "graph.json" || !insideBase) { return { success: false, error: "path must be a graph.json inside the project cwd", diff --git a/src/triggers/api.ts b/src/triggers/api.ts index 2bb8ce9e8..95f2f17f2 100644 --- a/src/triggers/api.ts +++ b/src/triggers/api.ts @@ -2765,6 +2765,34 @@ export function registerApiTriggers( if (authErr) return authErr; const project = asNonEmptyString(req.body?.project); const requestsGlobal = req.body?.scope === "global"; + const explicitPath = + typeof req.body?.path === "string" ? req.body.path : undefined; + const explicitCwd = + typeof req.body?.cwd === "string" ? req.body.cwd : undefined; + // A caller-supplied path/cwd steers which file the daemon reads, and + // the engine anchors its containment checks to its own process.cwd() + // rather than any client value. Requests that carry either field are + // therefore administrative, gated exactly like global-scope + // governance/export; capability callers import the default + // /graphify-out/graph.json (which fails honestly when it + // is absent). + if ((explicitPath !== undefined || explicitCwd !== undefined) && !requestsGlobal) { + const adminDecision = authorizeAdministrativeRequest( + req.headers, + adminSecret, + ); + if (!adminDecision.authorized) { + return { + status_code: adminDecision.statusCode, + body: { + error: + adminDecision.error === "authentication_unavailable" + ? "global_authentication_unavailable" + : "global_unauthorized", + }, + }; + } + } if (!project && !requestsGlobal) { return { status_code: 400, @@ -2775,8 +2803,8 @@ export function registerApiTriggers( const rawResult = await sdk.trigger({ function_id: "mem::graph::import-graphify", payload: { - ...(typeof req.body?.path === "string" && { path: req.body.path }), - ...(typeof req.body?.cwd === "string" && { cwd: req.body.cwd }), + ...(explicitPath !== undefined && { path: explicitPath }), + ...(explicitCwd !== undefined && { cwd: explicitCwd }), ...(project ? { project } : {}), ...(requestsGlobal ? { scope: req.body?.scope } : {}), }, diff --git a/test/graph-import-scope.test.ts b/test/graph-import-scope.test.ts index 6a199b5b5..484ac536e 100644 --- a/test/graph-import-scope.test.ts +++ b/test/graph-import-scope.test.ts @@ -1,7 +1,7 @@ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; import { mkdtempSync, rmSync, writeFileSync, mkdirSync } from "node:fs"; import { tmpdir } from "node:os"; -import { dirname, join } from "node:path"; +import { join } from "node:path"; vi.mock("../src/logger.js", () => ({ logger: { info: vi.fn(), warn: vi.fn(), error: vi.fn() }, @@ -12,10 +12,14 @@ import { registerGraphImportFunction, } from "../src/functions/graph-import.js"; import type { GraphifyImportResult } from "../src/functions/graph-import.js"; +import { registerApiTriggers } from "../src/triggers/api.js"; +import { createProjectCapabilityToken } from "../src/auth.js"; import { KV } from "../src/state/schema.js"; -// Client-supplied explicit paths must stay inside the project cwd and keep -// the graph.json basename; violations are rejected with a generic message +// The engine anchors every path decision to its own process.cwd(): a +// client-supplied cwd is ignored, explicit paths must stay a graph.json +// under that directory, and on the REST surface any request carrying path +// or cwd is admin-gated. Violations are rejected with a generic message // that does not echo the attempted path. const FIXTURE = JSON.stringify({ nodes: [{ id: "n1", label: "extract", file_type: "code" }], @@ -42,8 +46,34 @@ function mockKV() { }; } +type Handler = (payload?: unknown) => Promise; + +function integratedHarness() { + const functions = new Map(); + const sdk = { + registerFunction: ( + idOrOptions: string | { id: string }, + handler: Handler, + ) => { + const id = + typeof idOrOptions === "string" ? idOrOptions : idOrOptions.id; + functions.set(id, handler); + }, + registerTrigger: () => {}, + // Route handlers dispatch into the real registered functions, so the + // REST gating and the engine containment are exercised end to end. + trigger: async (input: { function_id: string; payload: unknown }) => { + const fn = functions.get(input.function_id); + if (!fn) throw new Error(`No function: ${input.function_id}`); + return fn(input.payload); + }, + }; + return { sdk, functions }; +} + describe("mem::graph::import-graphify path scoping", () => { let tmp: string; + let originalCwd: string; let kv: ReturnType; let trigger: (payload: unknown) => Promise; @@ -62,9 +92,15 @@ describe("mem::graph::import-graphify path scoping", () => { registerTrigger: () => {}, } as never; registerGraphImportFunction(sdk, kv as never); + originalCwd = process.cwd(); + process.chdir(tmp); + // process.cwd() canonicalizes symlinked parents (macOS /var -> /private/var), + // and the engine anchors to it — derive every expectation from this value. + tmp = process.cwd(); }); afterEach(() => { + process.chdir(originalCwd); rmSync(tmp, { recursive: true, force: true }); }); @@ -107,7 +143,35 @@ describe("mem::graph::import-graphify path scoping", () => { expect(await kv.list(KV.graphNodes)).toHaveLength(1); }); - it("keeps the stat-failure pointer generic for explicit paths and specific for the default", async () => { + it("ignores a client-supplied cwd and computes the default from the daemon cwd", async () => { + // The fixture exists at /graphify-out/graph.json; a caller + // pointing cwd elsewhere must not relocate the read. + const decoy = mkdtempSync(join(tmpdir(), "am-graphify-decoy-")); + try { + mkdirSync(join(decoy, "graphify-out"), { recursive: true }); + writeFileSync( + join(decoy, "graphify-out", "graph.json"), + JSON.stringify({ nodes: [{ id: "evil", label: "evil" }] }), + ); + const result = await trigger({ cwd: decoy, project: PROJECT }); + expect(result.success).toBe(true); + expect(result.path).toBe(join(tmp, "graphify-out", "graph.json")); + const imported = await kv.list(KV.graphNodes); + expect(imported).toHaveLength(1); + expect((imported[0] as { name?: string }).name).toBe("extract"); + } finally { + rmSync(decoy, { recursive: true, force: true }); + } + }); + + it("fails honestly when the default artifact is absent at the daemon cwd", async () => { + rmSync(join(tmp, "graphify-out", "graph.json")); + const result = await trigger({ project: PROJECT }); + expect(result.success).toBe(false); + expect(result.error).toContain("Run graphify first"); + }); + + it("keeps the stat-failure pointer generic for explicit paths", async () => { const missingExplicit = join(tmp, "missing-dir", "graph.json"); const explicitResult = await trigger({ path: missingExplicit, @@ -118,18 +182,147 @@ describe("mem::graph::import-graphify path scoping", () => { expect(explicitResult.error).toContain("Run graphify first"); expect(explicitResult.error).not.toContain(missingExplicit); expect(explicitResult.path).toBeUndefined(); + }); - const defaultResult = await trigger({ - cwd: join(tmp, "nowhere"), - project: PROJECT, - }); - expect(defaultResult.success).toBe(false); - expect(defaultResult.error).toContain( - join(tmp, "nowhere", "graphify-out", "graph.json"), + it("reports the exact default path on stat failure at the daemon cwd", async () => { + // The client cannot steer the default lookup (no cwd field is honored), + // so its stat failure may safely name the concrete location. + rmSync(join(tmp, "graphify-out", "graph.json")); + const result = await trigger({ project: PROJECT }); + expect(result.success).toBe(false); + expect(result.error).toContain(join(tmp, "graphify-out", "graph.json")); + expect(result.path).toBe(join(tmp, "graphify-out", "graph.json")); + }); +}); + +describe("api::graph-import-graphify path/cwd admin gating", () => { + let tmp: string; + let originalCwd: string; + + function capabilityHeaders(project: string): Record { + const now = Math.floor(Date.now() / 1000); + const token = createProjectCapabilityToken( + { + version: 1, + audience: "agentmemory", + project, + expiresAt: now + 60, + issuedAt: now, + }, + "capability-secret", ); - expect(defaultResult.path).toBe( - join(tmp, "nowhere", "graphify-out", "graph.json"), + return { authorization: `Bearer ${token}` }; + } + + beforeEach(() => { + tmp = mkdtempSync(join(tmpdir(), "am-graphify-route-")); + mkdirSync(join(tmp, "graphify-out"), { recursive: true }); + writeFileSync(join(tmp, "graphify-out", "graph.json"), FIXTURE); + originalCwd = process.cwd(); + process.chdir(tmp); + // Canonicalize like the engine's process.cwd() anchor (macOS /var -> /private/var). + tmp = process.cwd(); + }); + + afterEach(() => { + process.chdir(originalCwd); + rmSync(tmp, { recursive: true, force: true }); + }); + + function registerRoute() { + const { sdk, functions } = integratedHarness(); + registerGraphImportFunction(sdk as never, mockKV() as never); + registerApiTriggers( + sdk as never, + mockKV() as never, + "legacy-secret", + undefined, + undefined, + "admin-secret", + "capability-secret", + true, + ); + return ( + body: Record, + headers?: Record, + ): Promise<{ status_code: number; body: Record }> => + functions.get("api::graph-import-graphify")!({ + headers, + query_params: {}, + body, + }) as Promise<{ status_code: number; body: Record }>; + } + + it("rejects a capability request carrying an explicit path with 401", async () => { + const route = registerRoute(); + const response = await route( + { project: PROJECT, path: join(tmp, "graphify-out", "graph.json") }, + capabilityHeaders(PROJECT), + ); + expect(response.status_code).toBe(401); + expect(response.body).toEqual({ error: "global_unauthorized" }); + }); + + it("rejects a capability request carrying only cwd with 401", async () => { + const route = registerRoute(); + const response = await route( + { project: PROJECT, cwd: tmp }, + capabilityHeaders(PROJECT), + ); + expect(response.status_code).toBe(401); + expect(response.body).toEqual({ error: "global_unauthorized" }); + }); + + it("proceeds without path/cwd and imports the default daemon-cwd artifact", async () => { + const route = registerRoute(); + const response = await route( + { project: PROJECT }, + capabilityHeaders(PROJECT), + ); + expect(response.status_code).toBe(200); + expect(response.body).toMatchObject({ + success: true, + path: join(tmp, "graphify-out", "graph.json"), + nodesImported: 1, + }); + }); + + it("fails honestly when the default artifact is absent for a capability request", async () => { + const route = registerRoute(); + rmSync(join(tmp, "graphify-out", "graph.json")); + const response = await route({ project: PROJECT }, capabilityHeaders(PROJECT)); + expect(response.body).toMatchObject({ success: false }); + expect(String(response.body.error)).toContain("Run graphify first"); + }); + + it("rejects an admin explicit path outside the daemon cwd generically", async () => { + const route = registerRoute(); + const outsideDir = mkdtempSync(join(tmpdir(), "am-graphify-route-out-")); + try { + const outside = join(outsideDir, "graph.json"); + writeFileSync(outside, FIXTURE); + const response = await route( + { project: PROJECT, path: outside }, + { authorization: "Bearer admin-secret" }, + ); + expect(response.status_code).toBe(400); + expect(response.body).toMatchObject({ + success: false, + error: "path must be a graph.json inside the project cwd", + }); + expect(JSON.stringify(response.body)).not.toContain(outside); + } finally { + rmSync(outsideDir, { recursive: true, force: true }); + } + }); + + it("imports an admin explicit graph.json inside the daemon cwd", async () => { + const route = registerRoute(); + const response = await route( + { project: PROJECT, path: join(tmp, "graphify-out", "graph.json") }, + { authorization: "Bearer admin-secret" }, ); - expect(dirname(missingExplicit)).toBeTruthy(); + expect(response.status_code).toBe(200); + expect(response.body).toMatchObject({ success: true, nodesImported: 1 }); }); }); diff --git a/test/graph-import.test.ts b/test/graph-import.test.ts index 9077399e7..088e963e8 100644 --- a/test/graph-import.test.ts +++ b/test/graph-import.test.ts @@ -140,6 +140,7 @@ describe("parseGraphifyGraph", () => { describe("mem::graph::import-graphify", () => { let tmp: string; + let originalCwd: string; let kv: ReturnType; let sdk: ReturnType; @@ -150,16 +151,24 @@ describe("mem::graph::import-graphify", () => { kv = mockKV(); sdk = mockSdk(); registerGraphImportFunction(sdk as never, kv as never); + // The engine computes its default path from its own process.cwd(); run + // against a sandbox checkout instead of the vitest process directory. + originalCwd = process.cwd(); + process.chdir(tmp); + // Canonicalize like the engine's anchor on symlinked parents + // (macOS /var -> /private/var). + tmp = process.cwd(); }); afterEach(() => { + process.chdir(originalCwd); rmSync(tmp, { recursive: true, force: true }); }); it("imports nodes and edges into the memory graph", async () => { const result = (await (sdk as any).trigger({ function_id: "mem::graph::import-graphify", - payload: { cwd: tmp, project: PROJECT }, + payload: { project: PROJECT }, })) as { success: boolean; newNodes: number; @@ -182,11 +191,11 @@ describe("mem::graph::import-graphify", () => { it("re-import is idempotent: second run merges instead of duplicating", async () => { await (sdk as any).trigger({ function_id: "mem::graph::import-graphify", - payload: { cwd: tmp, project: PROJECT }, + payload: { project: PROJECT }, }); const second = (await (sdk as any).trigger({ function_id: "mem::graph::import-graphify", - payload: { cwd: tmp, project: PROJECT }, + payload: { project: PROJECT }, })) as { success: boolean; newNodes: number; newEdges: number }; expect(second.success).toBe(true); @@ -209,20 +218,21 @@ describe("mem::graph::import-graphify", () => { }); it("fails cleanly with a pointer when graph.json is absent", async () => { + rmSync(join(tmp, "graphify-out", "graph.json")); const result = (await (sdk as any).trigger({ function_id: "mem::graph::import-graphify", - payload: { cwd: join(tmp, "nowhere"), project: PROJECT }, + payload: { project: PROJECT }, })) as { success: boolean; error: string }; expect(result.success).toBe(false); expect(result.error).toContain("Run graphify first"); }); - it("accepts an explicit path inside the project cwd", async () => { + it("accepts an explicit path inside the daemon cwd", async () => { const alt = join(tmp, "graph.json"); writeFileSync(alt, JSON.stringify(FIXTURE)); const result = (await (sdk as any).trigger({ function_id: "mem::graph::import-graphify", - payload: { path: alt, cwd: tmp, project: PROJECT }, + payload: { path: alt, project: PROJECT }, })) as { success: boolean; nodesImported: number }; expect(result.success).toBe(true); expect(result.nodesImported).toBe(4); @@ -232,7 +242,7 @@ describe("mem::graph::import-graphify", () => { writeFileSync(join(tmp, "graphify-out", "graph.json"), "{not json"); const result = (await (sdk as any).trigger({ function_id: "mem::graph::import-graphify", - payload: { cwd: tmp, project: PROJECT }, + payload: { project: PROJECT }, })) as { success: boolean }; expect(result.success).toBe(false); expect(await kv.list(KV.graphNodes)).toHaveLength(0); @@ -242,7 +252,7 @@ describe("mem::graph::import-graphify", () => { await expect( (sdk as any).trigger({ function_id: "mem::graph::import-graphify", - payload: { cwd: tmp }, + payload: {}, }), ).rejects.toThrow("project is required"); }); From 4c49a8b2d434585c1d5f426192f2b55ef6ce96af Mon Sep 17 00:00:00 2001 From: ChronodeAi Date: Mon, 24 Aug 2026 20:09:26 -0500 Subject: [PATCH 02/11] fix(cli): log capability provisioning failures and absorb lstat ENOENT race MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bare catch {} around ensureProjectCapabilitySecret() in connect and onboarding defeated the fail-loud provisioning guarantee — a failed provision now writes one stderr line pointing at 'agentmemory doctor' while keeping the never-block-wiring semantics. An existsSync->lstatSync ENOENT race (file vanished mid-check) returns the reused:false outcome like the surrounding unreadable-file path instead of throwing raw. --- src/cli/connect/capability-secret.ts | 14 +++++++++++- src/cli/connect/index.ts | 8 +++++-- src/cli/onboarding.ts | 7 +++++- test/capability-secret-hardening.test.ts | 28 ++++++++++++++++++++++-- 4 files changed, 51 insertions(+), 6 deletions(-) diff --git a/src/cli/connect/capability-secret.ts b/src/cli/connect/capability-secret.ts index 5c2b2355f..3ef00d6ba 100644 --- a/src/cli/connect/capability-secret.ts +++ b/src/cli/connect/capability-secret.ts @@ -55,7 +55,19 @@ export function ensureProjectCapabilitySecret(): CapabilityProvisionResult { if (existsSync(path)) { // lstat, not stat: a symlink parked at the credential path must be // refused before any read or write can follow it to another target. - if (lstatSync(path).isSymbolicLink()) { + let symlinked: boolean; + try { + symlinked = lstatSync(path).isSymbolicLink(); + } catch (err) { + if ((err as NodeJS.ErrnoException).code === "ENOENT") { + // The file vanished between existsSync and lstat. Match the + // surrounding unreadable-file outcome instead of throwing raw; + // doctor surfaces the missing credential. + return { path, provisioned: false, reused: false }; + } + throw err; + } + if (symlinked) { throw new Error( "project capability secret path is a symlink; refusing", ); diff --git a/src/cli/connect/index.ts b/src/cli/connect/index.ts index 43ea1b182..75a62529e 100644 --- a/src/cli/connect/index.ts +++ b/src/cli/connect/index.ts @@ -128,9 +128,13 @@ export async function runConnect(args: string[]): Promise { `Generated project capability credential at ${capability.path} (mode 0600).`, ); } - } catch { + } catch (err) { // Provisioning must never block wiring; doctor reports a missing - // credential and can generate one later. + // credential and can generate one later. The failure is still logged: + // a silently unwired credential defeats the zero-touch guarantee. + process.stderr.write( + `[agentmemory] capability credential provisioning failed: ${err instanceof Error ? err.message : String(err)} — run 'agentmemory doctor'\n`, + ); } } diff --git a/src/cli/onboarding.ts b/src/cli/onboarding.ts index 3d7cc3744..382d9a576 100644 --- a/src/cli/onboarding.ts +++ b/src/cli/onboarding.ts @@ -331,8 +331,13 @@ async function wireSelectedAgents(agents: string[]): Promise { `Generated project capability credential at ${capability.path} (mode 0600).`, ); } - } catch { + } catch (err) { // Doctor reports and repairs a missing credential; never block wiring. + // The failure is still logged: a silently unwired credential defeats + // the zero-touch guarantee this provisioning exists for. + process.stderr.write( + `[agentmemory] capability credential provisioning failed: ${err instanceof Error ? err.message : String(err)} — run 'agentmemory doctor'\n`, + ); } for (const name of agents) { diff --git a/test/capability-secret-hardening.test.ts b/test/capability-secret-hardening.test.ts index 7ad5c4c43..1793e6487 100644 --- a/test/capability-secret-hardening.test.ts +++ b/test/capability-secret-hardening.test.ts @@ -12,9 +12,10 @@ import { import { join } from "node:path"; import { tmpdir } from "node:os"; -// chmod is the failure surface under test; every other fs call must hit the -// real filesystem. The flag toggles the fault injection per test. +// chmod and lstat are the failure surfaces under test; every other fs call +// must hit the real filesystem. The flags toggle fault injection per test. const chmodFault = vi.hoisted(() => ({ fail: false })); +const lstatFault = vi.hoisted(() => ({ failEnoent: false })); vi.mock("node:fs", async (importOriginal) => { const actual = await importOriginal(); return { @@ -27,6 +28,15 @@ vi.mock("node:fs", async (importOriginal) => { } return actual.chmodSync(path, mode); }) as typeof actual.chmodSync, + lstatSync: ((path: Parameters[0]) => { + if (lstatFault.failEnoent) { + throw Object.assign( + new Error("ENOENT: no such file or directory, lstat"), + { code: "ENOENT" }, + ); + } + return actual.lstatSync(path); + }) as typeof actual.lstatSync, }; }); @@ -47,6 +57,7 @@ describe("project capability secret hardening", () => { process.env["HOME"] = sandboxHome; delete process.env["AGENTMEMORY_PROJECT_CAPABILITY_SECRET_FILE"]; chmodFault.fail = false; + lstatFault.failEnoent = false; stderrWrites.length = 0; vi.spyOn(process.stderr, "write").mockImplementation(((chunk: unknown) => { stderrWrites.push(String(chunk)); @@ -103,4 +114,17 @@ describe("project capability secret hardening", () => { expect(readFileSync(path, "utf8")).toBe("user-chosen-secret\n"); expect(stderrWrites.join("")).toContain("could not tighten permissions"); }); + + it("treats an ENOENT race on lstat as nothing reused instead of crashing", () => { + const path = projectCapabilitySecretFile(); + mkdirSync(join(path, ".."), { recursive: true }); + writeFileSync(path, "user-chosen-secret\n", { mode: 0o600 }); + // existsSync saw the file; the file vanishes before lstat runs. + lstatFault.failEnoent = true; + + const result = ensureProjectCapabilitySecret(); + expect(result).toEqual({ path, provisioned: false, reused: false }); + // No fresh credential was provisioned over the vanished path. + expect(readFileSync(path, "utf8")).toBe("user-chosen-secret\n"); + }); }); From b19a2a9c26b72b1a7a7dadc0a137befd3f24195b Mon Sep 17 00:00:00 2001 From: ChronodeAi Date: Mon, 24 Aug 2026 20:15:14 -0500 Subject: [PATCH 03/11] fix(events): close consolidation cooldown release races with token claims MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two races in the shared release-on-rejection closure: a rejected auto-crystallize cleared the marker while consolidate-pipeline still ran, and an async kv.delete could land after a newer stop wrote a fresh marker, erasing its debounce. Each dispatch now claims the marker with its own generateId token right before firing and deletes on rejection only when the marker still carries that token (compare-and-delete, lock-free). Crystallize fires first so the heavier pipeline holds the newest claim for most of the cycle; latency profile unchanged — no locks around dispatch. --- src/triggers/events.ts | 94 ++++++++++++++++++++-------- test/consolidation-lifecycle.test.ts | 91 +++++++++++++++++++++++++++ 2 files changed, 158 insertions(+), 27 deletions(-) diff --git a/src/triggers/events.ts b/src/triggers/events.ts index 8e075c0fd..3732bf8b3 100644 --- a/src/triggers/events.ts +++ b/src/triggers/events.ts @@ -28,20 +28,33 @@ import { const MAX_BACKGROUND_PIPELINE_ATTEMPTS = 3; // Global marker recording when corpus consolidation last ran, used to debounce -// the per-turn session-stop fan-out. +// the per-turn session-stop fan-out. `token` names the dispatch that owns the +// current claim: each Void dispatch rewrites the marker with its own token +// right before firing, and a rejection handler deletes the marker only when +// it still carries that exact token (compare-and-delete). A stale rejection +// from an older cycle therefore cannot erase a newer cycle's debounce, and a +// rejected dispatch cannot release the cooldown while its sibling dispatch +// still holds a newer claim. const CONSOLIDATION_MARKER_KEY = "consolidation:lastRun"; -async function consolidationDueUnserialized(kv: StateKV): Promise { +type ConsolidationCooldownClaim = { at: number; token: string }; + +async function consolidationDueUnserialized( + kv: StateKV, +): Promise { const cooldownMs = getConsolidationCooldownMs(); - if (cooldownMs <= 0) return true; // debounce disabled + if (cooldownMs <= 0) return { at: Date.now(), token: "" }; // debounce disabled const now = Date.now(); const marker = await kv .get<{ at?: number }>(KV.config, CONSOLIDATION_MARKER_KEY) .catch(() => null); const lastAt = typeof marker?.at === "number" ? marker.at : 0; - if (now - lastAt < cooldownMs) return false; - await kv.set(KV.config, CONSOLIDATION_MARKER_KEY, { at: now }).catch(() => {}); - return true; + if (now - lastAt < cooldownMs) return null; + const token = generateId("ccm"); + await kv + .set(KV.config, CONSOLIDATION_MARKER_KEY, { at: now, token }) + .catch(() => {}); + return { at: now, token }; } // Concurrent session-stop events would otherwise interleave the marker @@ -49,11 +62,13 @@ async function consolidationDueUnserialized(kv: StateKV): Promise { // check through an in-process chain so exactly one concurrent caller wins. let consolidationCheckChain: Promise = Promise.resolve(); -function consolidationDue(kv: StateKV): Promise { +function consolidationDue( + kv: StateKV, +): Promise { const result = consolidationCheckChain.then(() => consolidationDueUnserialized(kv), ); - consolidationCheckChain = result.catch(() => false); + consolidationCheckChain = result.catch(() => null); return result; } @@ -859,24 +874,46 @@ export function registerEventTriggers(sdk: ISdk, kv: StateKV): void { // storm. Bound the global corpus consolidation to once per cooldown // window; AGENTMEMORY_CONSOLIDATION_COOLDOWN_MS=0 disables the debounce. if (isConsolidationEnabled()) { - const due = await consolidationDue(kv); - if (due) { + const claim = await consolidationDue(kv); + if (claim) { // Same dispatch discipline as the slot-reflect / graph-extract // fan-outs above: tolerate synchronous throws and non-promise // returns from sdk.trigger, log async rejections without failing // the stop lifecycle. - const releaseConsolidationCooldown = (): void => { - // The marker was written before the dispatch, so a rejected - // (or never-started) pipeline would otherwise pin the cooldown - // for the whole window with no work behind it. Plain kv delete: - // the marker is debounce bookkeeping, not pipeline state, and - // needs no lock. Best-effort only — the next stop re-checks. - kv.delete(KV.config, CONSOLIDATION_MARKER_KEY).catch(() => {}); - }; - const fireVoid = ( + // + // Each dispatch claims the cooldown marker with its own token right + // before firing; on rejection it re-reads the marker and deletes it + // ONLY when it still carries that token. A rejected dispatch thus + // releases the window only while nothing newer stands behind the + // marker: a crystallize rejection cannot clear the claim under a + // still-running consolidate-pipeline, and a late rejection from an + // older cycle cannot erase a newer cycle's fresh marker. Plain async + // kv ops — no locks around dispatch, marker writes stay lock-free. + const fireVoid = async ( function_id: string, payload: Record, - ) => { + ): Promise => { + const token = generateId("ccm"); + await kv + .set(KV.config, CONSOLIDATION_MARKER_KEY, { + at: claim.at, + token, + }) + .catch(() => {}); + const releaseOnRejection = (): void => { + void (async () => { + const marker = await kv + .get<{ at?: number; token?: string }>( + KV.config, + CONSOLIDATION_MARKER_KEY, + ) + .catch(() => null); + if (!marker || marker.token !== token) return; + await kv + .delete(KV.config, CONSOLIDATION_MARKER_KEY) + .catch(() => {}); + })(); + }; try { const dispatched = sdk.trigger({ function_id, @@ -890,7 +927,7 @@ export function registerEventTriggers(sdk: ISdk, kv: StateKV): void { pipelineRunId, error: err instanceof Error ? err.message : String(err), }); - releaseConsolidationCooldown(); + releaseOnRejection(); }); } catch (err) { logger.warn(function_id + " trigger failed", { @@ -899,16 +936,19 @@ export function registerEventTriggers(sdk: ISdk, kv: StateKV): void { pipelineRunId, error: err instanceof Error ? err.message : String(err), }); - releaseConsolidationCooldown(); + releaseOnRejection(); } }; - fireVoid("mem::consolidate-pipeline", { - tier: "all", - force: true, + // Crystallize fires first so the heavier consolidate-pipeline holds + // the newest claim for most of the cycle: its rejection then finds a + // token mismatch and leaves the debounce standing. + await fireVoid("mem::auto-crystallize", { + olderThanDays: 0, project: data.project, }); - fireVoid("mem::auto-crystallize", { - olderThanDays: 0, + await fireVoid("mem::consolidate-pipeline", { + tier: "all", + force: true, project: data.project, }); } diff --git a/test/consolidation-lifecycle.test.ts b/test/consolidation-lifecycle.test.ts index 3cd8fbb54..0be9f0d75 100644 --- a/test/consolidation-lifecycle.test.ts +++ b/test/consolidation-lifecycle.test.ts @@ -389,4 +389,95 @@ describe("session-stop consolidation lifecycle", () => { ]); expect(await marker()).not.toBeNull(); }); + + it("keeps the marker when crystallize is rejected but the pipeline holds a newer claim", async () => { + await kv.set(KV.sessions, "s-x1", sessionRow("s-x1")); + await kv.set(KV.sessions, "s-x2", sessionRow("s-x2")); + registerRuntime(); + // Crystallize is rejected every time; the pipeline succeeds. The + // pipeline fires after crystallize and therefore owns the newest + // claim on the marker. + let crystallizeDispatches = 0; + (sdk as unknown as { fns: Map Promise> }).fns.set( + "mem::auto-crystallize", + async () => { + crystallizeDispatches += 1; + throw new Error("SIMULATED_CRYSTALLIZE_REJECTION"); + }, + ); + + await stop({ sessionId: "s-x1" }); + await flush(); + + expect(crystallizeDispatches).toBe(1); + expect(consolidationCalls()).toHaveLength(1); + const survivor = await marker(); + expect(typeof survivor?.at).toBe("number"); + expect(typeof survivor?.token).toBe("string"); + + // The running pipeline must stay protected: a stop inside the cooldown + // window is still debounced even though crystallize was rejected. + await stop({ sessionId: "s-x2" }); + await flush(); + expect(crystallizeDispatches).toBe(1); + expect(consolidationCalls()).toHaveLength(1); + }); + + it("a stale rejection cannot erase a newer cycle's marker", async () => { + await kv.set(KV.sessions, "s-t1", sessionRow("s-t1")); + await kv.set(KV.sessions, "s-t2", sessionRow("s-t2")); + await kv.set(KV.sessions, "s-t3", sessionRow("s-t3")); + registerRuntime(); + // First pipeline dispatch hangs until the test releases it, simulating + // a rejection that lands long after newer cycles have claimed the mark. + let pipelineDispatches = 0; + let rejectFirstDispatch!: (err: Error) => void; + const gated = new Promise((_resolve, reject) => { + rejectFirstDispatch = reject; + }); + (sdk as unknown as { fns: Map Promise> }).fns.set( + "mem::consolidate-pipeline", + async (payload) => { + pipelineDispatches += 1; + if (pipelineDispatches === 1) return gated; + calls.push({ + functionId: "mem::consolidate-pipeline", + payload: payload as Record, + }); + return { success: true }; + }, + ); + + // Cycle 1: crystallize succeeds, pipeline dispatch #1 stays pending. + await stop({ sessionId: "s-t1" }); + await flush(); + expect(pipelineDispatches).toBe(1); + expect(await marker()).not.toBeNull(); + + // Cycle 2 becomes eligible only after the window elapses; it writes a + // fresh marker and completes successfully. + const stale = (await marker())!; + await kv.set(KV.config, "consolidation:lastRun", { + at: (stale.at ?? Date.now()) - 300_001, + token: "superseded-cycle-1-claim", + }); + await stop({ sessionId: "s-t2" }); + await flush(); + expect(pipelineDispatches).toBe(2); + const fresh = await marker(); + expect(fresh).not.toBeNull(); + expect(fresh?.token).not.toBe("superseded-cycle-1-claim"); + + // The cycle-1 rejection finally lands: its compare-and-delete finds a + // foreign token and leaves the newer debounce standing. + rejectFirstDispatch(new Error("SIMULATED_LATE_REJECTION")); + await flush(); + expect(await marker()).toEqual(fresh); + + // A further stop inside the window stays debounced by that survivor. + await stop({ sessionId: "s-t3" }); + await flush(); + expect(pipelineDispatches).toBe(2); + expect(crystallizeCalls()).toHaveLength(2); + }); }); From b84a793a8f62d0452d4463636760edff05dd8579 Mon Sep 17 00:00:00 2001 From: ChronodeAi Date: Mon, 24 Aug 2026 20:28:01 -0500 Subject: [PATCH 04/11] =?UTF-8?q?fix(cli):=20restore=20boot=20order=20?= =?UTF-8?q?=E2=80=94=20help=20exits=20early,=20legacy=20warning=20follows?= =?UTF-8?q?=20hydration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --help/-h now prints and exits right after the --data-dir flag fold, before the legacy ./data warning and .env hydration, so printing usage no longer emits a spurious relocation warning. warnOnLegacyDataDir moves after hydrateProcessEnvFromFile() so an AGENTMEMORY_DATA_DIR declared only in /.env is folded into the environment first and suppresses the warning. Worker spawns stay downstream of both (folded env inherited, unchanged); the pinned iii version's default is hoisted to III_PIN_DEFAULT so help text needs no hydrated value. --- src/cli.ts | 159 ++++++++++++++++++++++-------------------- test/data-dir.test.ts | 94 ++++++++++++++++++++++++- 2 files changed, 178 insertions(+), 75 deletions(-) diff --git a/src/cli.ts b/src/cli.ts index e2f9dad30..5969aae63 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -91,6 +91,11 @@ import { const ALL_TOOLS_COUNT = getAllTools().length; const CORE_TOOLS_COUNT = getAllTools().filter((t) => ESSENTIAL_TOOLS.has(t.name)).length; +// Default pinned iii-engine version. Declared before boot sequencing so the +// --help text can name it without reaching the hydrated-environment const +// below (help exits before hydration by design). +const III_PIN_DEFAULT = "0.11.2"; + const __dirname = dirname(fileURLToPath(import.meta.url)); const args = process.argv.slice(2); const IS_WINDOWS = platform() === "win32"; @@ -127,80 +132,9 @@ const dataDirFlagValue = readDataDirFlag(args); if (dataDirFlagValue !== undefined && dataDirFlagValue.trim().length > 0) { process.env["AGENTMEMORY_DATA_DIR"] = dataDirFlagValue.trim(); } -// Upstream adopts a legacy cwd ./data store automatically; this fork does -// not. When state would silently land somewhere different from an existing -// local ./data store, tell the operator how to opt in explicitly. -warnOnLegacyDataDir(); - -// Fold ~/.agentmemory/.env into process.env before anything reads config -// from the environment (the iii version pin, --tools/--port/--instance -// handling, engine boot). Fill-missing-only: a real process.env value — -// including one set by a CLI flag above — always wins over the file. -hydrateProcessEnvFromFile(); - -// Pinned iii-engine version. The unpinned `install.iii.dev/iii/main/install.sh` -// script tracks `latest`, which made every fresh agentmemory install pull -// engine 0.11.6 — and 0.11.6 introduces a new sandbox-everything-via- -// `iii worker add` worker model that agentmemory hasn't been refactored -// for yet (we still use the pre-sandbox worker registration model). The -// architectural mismatch surfaces as EPIPE reconnect loops and empty -// search results after save. Pin to v0.11.2 — the last engine that runs -// agentmemory's current worker model cleanly — until the refactor lands. -// Override env var AGENTMEMORY_III_VERSION lets users on the sandbox -// model already point at a newer engine without us cutting a release. -const IIPINNED_VERSION = - process.env["AGENTMEMORY_III_VERSION"] || "0.11.2"; - -// Map Node platform/arch → the asset name iii-hq/iii ships under -// https://github.com/iii-hq/iii/releases/download/iii/v/ -function iiiReleaseAsset(): string | null { - const p = platform(); - const a = process.arch; - if (p === "darwin" && a === "arm64") - return "iii-aarch64-apple-darwin.tar.gz"; - if (p === "darwin" && a === "x64") - return "iii-x86_64-apple-darwin.tar.gz"; - if (p === "linux" && a === "x64") - return "iii-x86_64-unknown-linux-gnu.tar.gz"; - if (p === "linux" && a === "arm64") - return "iii-aarch64-unknown-linux-gnu.tar.gz"; - if (p === "linux" && a === "arm") - return "iii-armv7-unknown-linux-gnueabihf.tar.gz"; - if (p === "win32" && a === "x64") - return "iii-x86_64-pc-windows-msvc.zip"; - if (p === "win32" && a === "arm64") - return "iii-aarch64-pc-windows-msvc.zip"; - return null; -} - -function iiiReleaseUrl(): string | null { - const asset = iiiReleaseAsset(); - if (!asset) return null; - // Tag name is monorepo-prefixed: `iii/v0.11.2`. Slash is URL-encoded - // by GitHub when serving the download path, hence `iii/v...` not `iii%2Fv...`. - return `https://github.com/iii-hq/iii/releases/download/iii/v${IIPINNED_VERSION}/${asset}`; -} - -function vlog(msg: string): void { - if (IS_VERBOSE) p.log.info(`[verbose] ${msg}`); -} - -function wrapList(items: readonly string[], indent: number, width = 78): string { - const lines: string[] = []; - let line = ""; - for (const item of items) { - const joined = line ? `${line}, ${item}` : item; - if (line && indent + joined.length > width) { - lines.push(`${line},`); - line = item; - } else { - line = joined; - } - } - lines.push(line); - return lines.join(`\n${" ".repeat(indent)}`); -} +// --help / -h early exit. Print + exit before any side effect beyond the +// flag fold above: no legacy-dir warning, no .env hydration, no engine boot. if (args.includes("--help") || args.includes("-h")) { console.log(` agentmemory — persistent memory for AI coding agents @@ -257,7 +191,7 @@ Environment: Honored by status, doctor, and MCP shim commands. AGENTMEMORY_USE_DOCKER=1 Prefer the bundled docker-compose path over the native iii-engine binary on first run. - AGENTMEMORY_III_VERSION Override pinned iii-engine version (default ${IIPINNED_VERSION}). + AGENTMEMORY_III_VERSION Override pinned iii-engine version (default ${III_PIN_DEFAULT}). AGENTMEMORY_FOLLOWUP_WINDOW_SECONDS Window (seconds) for the smart-search follow-up diagnostic (default 30). Long values overcount, short values undercount. @@ -274,6 +208,83 @@ Quick start: process.exit(0); } +// Fold ~/.agentmemory/.env into process.env before anything reads config +// from the environment (the iii version pin, --tools/--port/--instance +// handling, engine boot). Fill-missing-only: a real process.env value — +// including one set by a CLI flag above — always wins over the file. +hydrateProcessEnvFromFile(); + +// Upstream adopts a legacy cwd ./data store automatically; this fork does +// not. When state would silently land somewhere different from an existing +// local ./data store, tell the operator how to opt in explicitly. Runs after +// hydration so an AGENTMEMORY_DATA_DIR declared only in /.env is +// already folded into the environment and suppresses the spurious warning. +warnOnLegacyDataDir(); + +// Pinned iii-engine version. The unpinned `install.iii.dev/iii/main/install.sh` +// script tracks `latest`, which made every fresh agentmemory install pull +// engine 0.11.6 — and 0.11.6 introduces a new sandbox-everything-via- +// `iii worker add` worker model that agentmemory hasn't been refactored +// for yet (we still use the pre-sandbox worker registration model). The +// architectural mismatch surfaces as EPIPE reconnect loops and empty +// search results after save. Pin to v0.11.2 — the last engine that runs +// agentmemory's current worker model cleanly — until the refactor lands. +// Override env var AGENTMEMORY_III_VERSION lets users on the sandbox +// model already point at a newer engine without us cutting a release. +const IIPINNED_VERSION = + process.env["AGENTMEMORY_III_VERSION"] || III_PIN_DEFAULT; + +// Map Node platform/arch → the asset name iii-hq/iii ships under +// https://github.com/iii-hq/iii/releases/download/iii/v/ +function iiiReleaseAsset(): string | null { + const p = platform(); + const a = process.arch; + if (p === "darwin" && a === "arm64") + return "iii-aarch64-apple-darwin.tar.gz"; + if (p === "darwin" && a === "x64") + return "iii-x86_64-apple-darwin.tar.gz"; + if (p === "linux" && a === "x64") + return "iii-x86_64-unknown-linux-gnu.tar.gz"; + if (p === "linux" && a === "arm64") + return "iii-aarch64-unknown-linux-gnu.tar.gz"; + if (p === "linux" && a === "arm") + return "iii-armv7-unknown-linux-gnueabihf.tar.gz"; + if (p === "win32" && a === "x64") + return "iii-x86_64-pc-windows-msvc.zip"; + if (p === "win32" && a === "arm64") + return "iii-aarch64-pc-windows-msvc.zip"; + return null; +} + +function iiiReleaseUrl(): string | null { + const asset = iiiReleaseAsset(); + if (!asset) return null; + // Tag name is monorepo-prefixed: `iii/v0.11.2`. Slash is URL-encoded + // by GitHub when serving the download path, hence `iii/v...` not `iii%2Fv...`. + return `https://github.com/iii-hq/iii/releases/download/iii/v${IIPINNED_VERSION}/${asset}`; +} + +function vlog(msg: string): void { + if (IS_VERBOSE) p.log.info(`[verbose] ${msg}`); +} + +function wrapList(items: readonly string[], indent: number, width = 78): string { + const lines: string[] = []; + let line = ""; + for (const item of items) { + const joined = line ? `${line}, ${item}` : item; + if (line && indent + joined.length > width) { + lines.push(`${line},`); + line = item; + } else { + line = joined; + } + } + lines.push(line); + return lines.join(`\n${" ".repeat(indent)}`); +} + + const toolsIdx = args.indexOf("--tools"); if (toolsIdx !== -1 && args[toolsIdx + 1]) { const toolsMode = args[toolsIdx + 1]!; diff --git a/test/data-dir.test.ts b/test/data-dir.test.ts index b3e9238a0..9c9667c81 100644 --- a/test/data-dir.test.ts +++ b/test/data-dir.test.ts @@ -1,4 +1,7 @@ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; +import { spawnSync } from "node:child_process"; +import { createRequire } from "node:module"; +import { pathToFileURL } from "node:url"; import { mkdirSync, mkdtempSync, @@ -6,7 +9,7 @@ import { writeFileSync, } from "node:fs"; import { homedir, tmpdir } from "node:os"; -import { join } from "node:path"; +import { join, resolve } from "node:path"; import { DATA_DIR_ENV, defaultDataDir, @@ -21,6 +24,8 @@ import { const ORIGINAL_HOME = process.env["HOME"]; const ORIGINAL_USERPROFILE = process.env["USERPROFILE"]; +const repoRoot = resolve(import.meta.dirname, ".."); + let sandboxHome: string; let sandboxCwd: string; @@ -251,6 +256,48 @@ describe("config integration", () => { return await import("../src/config.js"); } + it("a .env-declared AGENTMEMORY_DATA_DIR suppresses the legacy warning after hydration", async () => { + // Boot-order regression guard: warnOnLegacyDataDir must run AFTER + // hydrateProcessEnvFromFile(), so a value declared only in + // ~/.agentmemory/.env is already folded into the environment and the + // resolver sees an explicit data dir instead of the default. + const relocated = join(sandboxHome, "relocated"); + const envDir = join(sandboxHome, ".agentmemory"); + mkdirSync(relocated, { recursive: true }); + mkdirSync(envDir, { recursive: true }); + writeFileSync(join(envDir, ".env"), `${DATA_DIR_ENV}=${relocated}\n`); + const legacyCwd = mkdtempSync(join(tmpdir(), "agentmemory-legacy-cwd-")); + try { + mkdirSync(join(legacyCwd, "data"), { recursive: true }); + writeFileSync(join(legacyCwd, "data", "iii-config.yaml"), ""); + + const before: string[] = []; + expect( + warnOnLegacyDataDir({ + cwd: legacyCwd, + write: (message) => before.push(message), + }), + ).toBe(true); + + const cfg = await freshConfig(); + cfg.hydrateProcessEnvFromFile(); + try { + const after: string[] = []; + expect( + warnOnLegacyDataDir({ + cwd: legacyCwd, + write: (message) => after.push(message), + }), + ).toBe(false); + expect(after).toHaveLength(0); + } finally { + delete process.env[DATA_DIR_ENV]; + } + } finally { + rmSync(legacyCwd, { recursive: true, force: true }); + } + }); + it("loadConfig().dataDir follows AGENTMEMORY_DATA_DIR over the default", async () => { const cfg = await freshConfig(); expect(cfg.loadConfig().dataDir).toBe(join(homedir(), ".agentmemory")); @@ -330,3 +377,48 @@ describe("config integration", () => { delete process.env["AM_FLAGFOLD_PROBE"]; }); }); + +describe("--help boot ordering", () => { + let sandboxHome: string; + let legacyCwd: string; + + beforeEach(() => { + sandboxHome = mkdtempSync(join(tmpdir(), "agentmemory-help-home-")); + legacyCwd = mkdtempSync(join(tmpdir(), "agentmemory-help-cwd-")); + // A legacy store plus a default home: with the pre-fix order (warning + // before the --help exit) this exact tree produced stderr noise on + // every `--help` run. + mkdirSync(join(legacyCwd, "data"), { recursive: true }); + writeFileSync(join(legacyCwd, "data", "state_store.db"), ""); + }); + + afterEach(() => { + rmSync(sandboxHome, { recursive: true, force: true }); + rmSync(legacyCwd, { recursive: true, force: true }); + delete process.env[DATA_DIR_ENV]; + }); + + it("prints usage without a legacy-dir warning side effect", () => { + const childEnv: NodeJS.ProcessEnv = { ...process.env }; + delete childEnv[DATA_DIR_ENV]; + childEnv["HOME"] = sandboxHome; + childEnv["USERPROFILE"] = sandboxHome; + // The child runs from the legacy-store cwd, so both the tsx hook and + // the CLI entry must be passed as absolute specifiers. + const requireFromRepo = createRequire(join(repoRoot, "package.json")); + const result = spawnSync( + process.execPath, + [ + "--import", + pathToFileURL(requireFromRepo.resolve("tsx")).href, + join(repoRoot, "src", "cli.ts"), + "--help", + ], + { cwd: legacyCwd, encoding: "utf8", env: childEnv }, + ); + expect(result.status).toBe(0); + expect(result.stdout).toContain("Usage: agentmemory"); + expect(result.stderr).not.toContain("legacy ./data store"); + expect(result.stderr).not.toContain("--data-dir"); + }); +}); From da5c6e356701c084979ee38e1ed1a342000d98b0 Mon Sep 17 00:00:00 2001 From: ChronodeAi Date: Mon, 24 Aug 2026 20:29:06 -0500 Subject: [PATCH 05/11] fix(config): restore trim-then-fall-back semantics for STANDALONE_PERSIST_PATH A whitespace-only value was honored verbatim as a relative persist path instead of falling back to /standalone.json. Trim first, fall back when empty. --- src/config.ts | 8 ++++---- test/data-dir.test.ts | 9 +++++++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/config.ts b/src/config.ts index ec0c0d09b..8fd8e0a23 100644 --- a/src/config.ts +++ b/src/config.ts @@ -500,10 +500,10 @@ export function isStandaloneMcp(): boolean { export function getStandalonePersistPath(): string { const env = getMergedEnv(); - return ( - env["STANDALONE_PERSIST_PATH"] || - join(resolveDataDir(), "standalone.json") - ); + // Trim before falling back: a whitespace-only value is not a path, and + // honoring it verbatim would persist state to a relative file named " ". + const configured = env["STANDALONE_PERSIST_PATH"]?.trim(); + return configured ? configured : join(resolveDataDir(), "standalone.json"); } const VALID_PROVIDERS = new Set([ diff --git a/test/data-dir.test.ts b/test/data-dir.test.ts index 9c9667c81..ed324b41d 100644 --- a/test/data-dir.test.ts +++ b/test/data-dir.test.ts @@ -346,6 +346,15 @@ describe("config integration", () => { expect(cfg.getStandalonePersistPath()).toBe(join(sandboxHome, "custom.json")); }); + it("a whitespace-only STANDALONE_PERSIST_PATH falls back to the data-dir path", async () => { + process.env[DATA_DIR_ENV] = join(sandboxHome, "relocated"); + process.env["STANDALONE_PERSIST_PATH"] = " "; + const cfg = await freshConfig(); + expect(cfg.getStandalonePersistPath()).toBe( + join(join(sandboxHome, "relocated"), "standalone.json"), + ); + }); + it("hydrates from the flag-folded data dir when AGENTMEMORY_DATA_DIR is set before boot", async () => { // Mirrors the CLI boot order: the --data-dir flag is folded into // process.env first, then hydrateProcessEnvFromFile() reads From 779e2fb5ee78b5c1cbe5e2e70d8065634b6f1bf3 Mon Sep 17 00:00:00 2001 From: ChronodeAi Date: Mon, 24 Aug 2026 20:30:27 -0500 Subject: [PATCH 06/11] docs(changelog): correct graphify scoping entry to admin-only imports; record round-two fixes The 0.9.30-chronode.2 graphify bullet overstated the guarantee ('inside the requested project cwd') when both path and cwd were client-controlled; it now states explicit path/cwd imports are ADMIN-only with the daemon-cwd anchor. Adds bullets for the warnOnLegacyDataDir boot-order fix, the STANDALONE_PERSIST_PATH trim restoration, the cooldown token-CAS hardening, and logged capability-provisioning failures. --- CHANGELOG.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dff405935..6d4c9f15b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,12 +12,15 @@ Adversarial-review fixes over the shipped sync wave. ### Fixed -- **Graphify import path scoping** (security). An explicit path on `mem::graph::import-graphify` (`POST /agentmemory/graph/import-graphify`) could point anywhere the daemon can read. Explicit paths must now resolve inside the requested project cwd and keep the `graph.json` basename; violations return a generic error that never echoes the attempted path, and stat failures for explicit paths stay equally non-specific. +- **Graphify import path scoping** (security). An explicit `path`/`cwd` on `mem::graph::import-graphify` (`POST /agentmemory/graph/import-graphify`) could point anywhere the daemon can read — both fields were client-controlled, and a symlinked `graph.json` escaped the containment check. Explicit path/cwd imports are now ADMIN-only: the REST route requires administrative authorization whenever a request carries either field, while capability callers import the default `/graphify-out/graph.json` (failing honestly when absent). The engine anchors its computed default to its own working directory, ignores caller-supplied cwd values, keeps the `graph.json` basename and under-daemon-cwd checks as defense in depth; violations return a generic error that never echoes the attempted path, and stat failures for explicit paths stay equally non-specific. - **Credential redaction in the identity-fallback warning** (security). The stderr warning for an unnormalizable git remote wrote the raw `git remote get-url` output, leaking embedded passwords; remotes are masked before logging (scheme and host/path preserved, credentials replaced). - **Project capability secret hardening** (security). Zero-touch provisioning creates the credential directory with mode 0700, refuses a symlink parked at the credential path instead of following it, keeps a pre-existing populated secret when only its permission tightening fails, and removes a freshly written secret whose securing chmod failed rather than leaving it readable. - **`--data-dir` honored during `.env` hydration** (correctness). The CLI hydrated `/.env` before folding the flag into the environment, so a flagged run silently read its `.env` from the default `~/.agentmemory`; the fold now happens first. - **Standalone MCP persist path follows the data dir** (correctness). The shim kept a duplicate resolver pinned to `~/.agentmemory`; it now delegates to the shared config resolver after folding `.env`, preserving the `STANDALONE_PERSIST_PATH` override while honoring `AGENTMEMORY_DATA_DIR`. -- **Consolidation cooldown released on rejected dispatch** (correctness). A rejected `mem::consolidate-pipeline` / `mem::auto-crystallize` Void dispatch left the cooldown marker standing for the whole window with no pipeline behind it; rejection handlers clear the marker best-effort so the next eligible stop retries. +- **Consolidation cooldown released on rejected dispatch** (correctness). A rejected `mem::consolidate-pipeline` / `mem::auto-crystallize` Void dispatch left the cooldown marker standing for the whole window with no pipeline behind it; rejection handlers clear the marker best-effort so the next eligible stop retries. Follow-up hardening closed two races in that release: each dispatch now claims the marker with its own token and deletes on rejection only when the marker still carries it, so a rejected crystallize can no longer clear the debounce under a still-running pipeline, and a late rejection from an older cycle cannot erase a newer cycle's fresh marker. +- **CLI boot ordering** (correctness). `--help` printed usage after the legacy-data-dir warning ran, emitting spurious stderr noise on every help invocation; help now exits right after flag parsing. The legacy warning itself moved after `.env` hydration so an `AGENTMEMORY_DATA_DIR` declared only in `/.env` suppresses it instead of producing a false alarm. +- **Standalone persist path trim restored** (correctness). A whitespace-only `STANDALONE_PERSIST_PATH` was honored verbatim as a relative file path; the value is trimmed first and empty results fall back to `/standalone.json`. +- **Capability provisioning failures are logged** (info). `connect` and onboarding swallowed credential-provisioning errors silently; both now write one stderr line pointing at `agentmemory doctor`, keeping the never-block-wiring semantics. - **Origin provenance on peer Memory upserts** (security). Mesh receive/pull wrote peer memories without provenance; records lacking an Origin now gain shared-channel provenance via the shared keep-or-mark factory, and peer-provided origins are preserved. - **Search-index readiness after startup reconciliation** (perf). Reconciliation walks the full memory corpus but never marked the index ready, forcing live saves onto full-scan fallbacks until an explicit rebuild; success now sets the same readiness flag a rebuild does. - **Per-project user overrides follow the data dir** (info). User project config overrides are read from `/projects/.yaml`, falling back to the legacy `~/.agentmemory/projects/.yaml` copy so existing overrides keep working. From 35ca5dd15e68256bd1229b73cf176a121d3f853a Mon Sep 17 00:00:00 2001 From: ChronodeAi Date: Mon, 24 Aug 2026 20:31:16 -0500 Subject: [PATCH 07/11] chore(gate): restamp R-13 test manifest for round-two hotfix test edits Same 176 suites (path hash unchanged); content hash moves for graph-import-scope, graph-import, capability-secret-hardening, consolidation-lifecycle, and data-dir. --- ci/r13-test-manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/r13-test-manifest.json b/ci/r13-test-manifest.json index 67712212b..0d1068d2a 100644 --- a/ci/r13-test-manifest.json +++ b/ci/r13-test-manifest.json @@ -1,5 +1,5 @@ { "count": 176, "sha256": "4ef314289367720f8cbe199ca8f2823eea416ec3fa0c861fefcbaf0550d530f1", - "content_sha256": "542b9f58957d1ac9fa9421abb47bbd4a985dae5a3a1fe6ee4b2f1916d527693e" + "content_sha256": "87a73e351114bac87891494dcb5115636a51608b7c7f8cc0f79ca8809a13145d" } From 4fccce18d5b80b2960b3e4d20a4b27dc8a6dcee0 Mon Sep 17 00:00:00 2001 From: ChronodeAi Date: Mon, 24 Aug 2026 20:36:19 -0500 Subject: [PATCH 08/11] chore(build): regenerate plugin bundles for the round-two hotfix sources tsdown rebuild rehashes the shared sidecar chunks that pull in triggers/api.ts, functions/graph-import.ts, triggers/events.ts, cli/connect/*, and config.ts; import maps updated accordingly. --- plugin/scripts/{_auth-1Z57rc-e.mjs => _auth-CBScPKV6.mjs} | 2 +- .../{_capture-Ba1NCNW7.mjs => _capture-DH0HGaDe.mjs} | 2 +- .../{_delivery-BPnYIm56.mjs => _delivery-Bevmy6M8.mjs} | 2 +- ...delivery-BSYpE5r3.mjs => _observe-delivery-BsKR4_co.mjs} | 2 +- .../{_project-BqDfPlX6.mjs => _project-BQWFXz1a.mjs} | 2 +- plugin/scripts/{auth-DkiaFluQ.mjs => auth-DYDHBWPd.mjs} | 3 ++- 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/standalone.mjs | 2 +- plugin/scripts/stop.mjs | 4 ++-- plugin/scripts/subagent-start.mjs | 4 ++-- plugin/scripts/subagent-stop.mjs | 4 ++-- plugin/scripts/task-completed.mjs | 4 ++-- 20 files changed, 38 insertions(+), 37 deletions(-) rename plugin/scripts/{_auth-1Z57rc-e.mjs => _auth-CBScPKV6.mjs} (99%) rename plugin/scripts/{_capture-Ba1NCNW7.mjs => _capture-DH0HGaDe.mjs} (99%) rename plugin/scripts/{_delivery-BPnYIm56.mjs => _delivery-Bevmy6M8.mjs} (97%) rename plugin/scripts/{_observe-delivery-BSYpE5r3.mjs => _observe-delivery-BsKR4_co.mjs} (97%) rename plugin/scripts/{_project-BqDfPlX6.mjs => _project-BQWFXz1a.mjs} (95%) rename plugin/scripts/{auth-DkiaFluQ.mjs => auth-DYDHBWPd.mjs} (96%) diff --git a/plugin/scripts/_auth-1Z57rc-e.mjs b/plugin/scripts/_auth-CBScPKV6.mjs similarity index 99% rename from plugin/scripts/_auth-1Z57rc-e.mjs rename to plugin/scripts/_auth-CBScPKV6.mjs index 823def7d0..5a8b59295 100644 --- a/plugin/scripts/_auth-1Z57rc-e.mjs +++ b/plugin/scripts/_auth-CBScPKV6.mjs @@ -1,4 +1,4 @@ -import { i as isStrictCapabilityMode, n as PROJECT_CAPABILITY_PROJECT_HEADER, o as hydrateProcessEnvFromFile, r as createProjectCapabilityToken, s as resolveDataDir } from "./auth-DkiaFluQ.mjs"; +import { i as isStrictCapabilityMode, n as PROJECT_CAPABILITY_PROJECT_HEADER, o as hydrateProcessEnvFromFile, r as createProjectCapabilityToken, s as resolveDataDir } from "./auth-DYDHBWPd.mjs"; import { createRequire } from "node:module"; import { existsSync, readFileSync, realpathSync } from "node:fs"; import { isAbsolute, join, relative, resolve } from "node:path"; diff --git a/plugin/scripts/_capture-Ba1NCNW7.mjs b/plugin/scripts/_capture-DH0HGaDe.mjs similarity index 99% rename from plugin/scripts/_capture-Ba1NCNW7.mjs rename to plugin/scripts/_capture-DH0HGaDe.mjs index 07d866062..8a2834e2e 100644 --- a/plugin/scripts/_capture-Ba1NCNW7.mjs +++ b/plugin/scripts/_capture-DH0HGaDe.mjs @@ -1,4 +1,4 @@ -import { a as normalizedProjectPath, r as isProjectPathExcluded } from "./_auth-1Z57rc-e.mjs"; +import { a as normalizedProjectPath, r as isProjectPathExcluded } from "./_auth-CBScPKV6.mjs"; import { resolve } from "node:path"; import { createHash } from "node:crypto"; import { execFileSync } from "node:child_process"; diff --git a/plugin/scripts/_delivery-BPnYIm56.mjs b/plugin/scripts/_delivery-Bevmy6M8.mjs similarity index 97% rename from plugin/scripts/_delivery-BPnYIm56.mjs rename to plugin/scripts/_delivery-Bevmy6M8.mjs index 57e463c17..55cafb397 100644 --- a/plugin/scripts/_delivery-BPnYIm56.mjs +++ b/plugin/scripts/_delivery-Bevmy6M8.mjs @@ -1,4 +1,4 @@ -import { n as projectAuthHeaders } from "./_auth-1Z57rc-e.mjs"; +import { n as projectAuthHeaders } from "./_auth-CBScPKV6.mjs"; //#region src/hooks/_delivery.ts var HookDeliveryError = class extends Error { retryable; diff --git a/plugin/scripts/_observe-delivery-BSYpE5r3.mjs b/plugin/scripts/_observe-delivery-BsKR4_co.mjs similarity index 97% rename from plugin/scripts/_observe-delivery-BSYpE5r3.mjs rename to plugin/scripts/_observe-delivery-BsKR4_co.mjs index 6e821def0..cfda1c291 100644 --- a/plugin/scripts/_observe-delivery-BSYpE5r3.mjs +++ b/plugin/scripts/_observe-delivery-BsKR4_co.mjs @@ -1,4 +1,4 @@ -import { n as projectAuthHeaders } from "./_auth-1Z57rc-e.mjs"; +import { n as projectAuthHeaders } from "./_auth-CBScPKV6.mjs"; //#region src/hooks/_observe-delivery.ts const MAX_ATTEMPTS = 2; const REQUEST_TIMEOUT_MS = 250; diff --git a/plugin/scripts/_project-BqDfPlX6.mjs b/plugin/scripts/_project-BQWFXz1a.mjs similarity index 95% rename from plugin/scripts/_project-BqDfPlX6.mjs rename to plugin/scripts/_project-BQWFXz1a.mjs index 6d2cd31eb..b302d3c03 100644 --- a/plugin/scripts/_project-BqDfPlX6.mjs +++ b/plugin/scripts/_project-BQWFXz1a.mjs @@ -1,4 +1,4 @@ -import { i as loadAgentmemoryEnvironment, o as resolveProjectConfig } from "./_auth-1Z57rc-e.mjs"; +import { i as loadAgentmemoryEnvironment, o as resolveProjectConfig } from "./_auth-CBScPKV6.mjs"; //#region src/hooks/_project.ts loadAgentmemoryEnvironment(); /** diff --git a/plugin/scripts/auth-DkiaFluQ.mjs b/plugin/scripts/auth-DYDHBWPd.mjs similarity index 96% rename from plugin/scripts/auth-DkiaFluQ.mjs rename to plugin/scripts/auth-DYDHBWPd.mjs index 86bdd5292..957b371ed 100644 --- a/plugin/scripts/auth-DkiaFluQ.mjs +++ b/plugin/scripts/auth-DYDHBWPd.mjs @@ -92,7 +92,8 @@ function getMergedEnv(overrides) { }; } function getStandalonePersistPath() { - return getMergedEnv()["STANDALONE_PERSIST_PATH"] || join(resolveDataDir(), "standalone.json"); + const configured = getMergedEnv()["STANDALONE_PERSIST_PATH"]?.trim(); + return configured ? configured : join(resolveDataDir(), "standalone.json"); } randomBytes(32); const PROJECT_CAPABILITY_TOKEN_VERSION = "amcap1"; diff --git a/plugin/scripts/notification.mjs b/plugin/scripts/notification.mjs index 24f6164df..90cc07d72 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-BqDfPlX6.mjs"; -import { n as reportObservationDeliveryFailure, t as deliverObservation } from "./_observe-delivery-BSYpE5r3.mjs"; +import { t as resolveProject } from "./_project-BQWFXz1a.mjs"; +import { n as reportObservationDeliveryFailure, t as deliverObservation } from "./_observe-delivery-BsKR4_co.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 ce823e21b..e17b2b017 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-BqDfPlX6.mjs"; -import { n as reportHookDeliveryFailure, t as deliverProjectRequest } from "./_delivery-BPnYIm56.mjs"; -import { n as credentialFreeWorktreeId, r as parseCommitTransitions } from "./_capture-Ba1NCNW7.mjs"; +import { t as resolveProject } from "./_project-BQWFXz1a.mjs"; +import { n as reportHookDeliveryFailure, t as deliverProjectRequest } from "./_delivery-Bevmy6M8.mjs"; +import { n as credentialFreeWorktreeId, r as parseCommitTransitions } from "./_capture-DH0HGaDe.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 ead2742e3..ac76f0a96 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-1Z57rc-e.mjs"; -import { n as reportObservationDeliveryFailure, t as deliverObservation } from "./_observe-delivery-BSYpE5r3.mjs"; -import { t as captureToolEvent } from "./_capture-Ba1NCNW7.mjs"; +import { o as resolveProjectConfig } from "./_auth-CBScPKV6.mjs"; +import { n as reportObservationDeliveryFailure, t as deliverObservation } from "./_observe-delivery-BsKR4_co.mjs"; +import { t as captureToolEvent } from "./_capture-DH0HGaDe.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 db2d8c3f6..5288f60be 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-1Z57rc-e.mjs"; -import { n as reportObservationDeliveryFailure, t as deliverObservation } from "./_observe-delivery-BSYpE5r3.mjs"; -import { t as captureToolEvent } from "./_capture-Ba1NCNW7.mjs"; +import { o as resolveProjectConfig } from "./_auth-CBScPKV6.mjs"; +import { n as reportObservationDeliveryFailure, t as deliverObservation } from "./_observe-delivery-BsKR4_co.mjs"; +import { t as captureToolEvent } from "./_capture-DH0HGaDe.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 0d245fcb4..ee3f13222 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-1Z57rc-e.mjs"; -import { t as resolveProject } from "./_project-BqDfPlX6.mjs"; +import { i as loadAgentmemoryEnvironment, n as projectAuthHeaders, t as contextAcknowledgementSecret } from "./_auth-CBScPKV6.mjs"; +import { t as resolveProject } from "./_project-BQWFXz1a.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 10c5ba596..154616431 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-1Z57rc-e.mjs"; -import { t as resolveProject } from "./_project-BqDfPlX6.mjs"; +import { i as loadAgentmemoryEnvironment, n as projectAuthHeaders } from "./_auth-CBScPKV6.mjs"; +import { t as resolveProject } from "./_project-BQWFXz1a.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 ecf6d6593..4ff126221 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-BqDfPlX6.mjs"; -import { n as reportObservationDeliveryFailure, t as deliverObservation } from "./_observe-delivery-BSYpE5r3.mjs"; +import { t as resolveProject } from "./_project-BQWFXz1a.mjs"; +import { n as reportObservationDeliveryFailure, t as deliverObservation } from "./_observe-delivery-BsKR4_co.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 8e0c13794..ac812eb8f 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-BqDfPlX6.mjs"; -import { n as reportHookDeliveryFailure, t as deliverProjectRequest } from "./_delivery-BPnYIm56.mjs"; +import { t as resolveProject } from "./_project-BQWFXz1a.mjs"; +import { n as reportHookDeliveryFailure, t as deliverProjectRequest } from "./_delivery-Bevmy6M8.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 66863564d..255c4480c 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-1Z57rc-e.mjs"; -import "./_project-BqDfPlX6.mjs"; -import { n as reportHookDeliveryFailure, t as deliverProjectRequest } from "./_delivery-BPnYIm56.mjs"; +import { o as resolveProjectConfig } from "./_auth-CBScPKV6.mjs"; +import "./_project-BQWFXz1a.mjs"; +import { n as reportHookDeliveryFailure, t as deliverProjectRequest } from "./_delivery-Bevmy6M8.mjs"; //#region src/hooks/session-start.ts function isSdkChildContext(payload) { if (process.env["AGENTMEMORY_SDK_CHILD"] === "1") return true; diff --git a/plugin/scripts/standalone.mjs b/plugin/scripts/standalone.mjs index 24c8c243b..97fe1a700 100755 --- a/plugin/scripts/standalone.mjs +++ b/plugin/scripts/standalone.mjs @@ -1,6 +1,6 @@ #!/usr/bin/env node import { n as generateId, t as KV } from "./schema-Dttua2Zo.mjs"; -import { a as getStandalonePersistPath$1, i as isStrictCapabilityMode, n as PROJECT_CAPABILITY_PROJECT_HEADER, o as hydrateProcessEnvFromFile, r as createProjectCapabilityToken } from "./auth-DkiaFluQ.mjs"; +import { a as getStandalonePersistPath$1, i as isStrictCapabilityMode, n as PROJECT_CAPABILITY_PROJECT_HEADER, o as hydrateProcessEnvFromFile, r as createProjectCapabilityToken } from "./auth-DYDHBWPd.mjs"; import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; import { dirname, join } from "node:path"; import { homedir } from "node:os"; diff --git a/plugin/scripts/stop.mjs b/plugin/scripts/stop.mjs index 2f94ce8ec..206a8371c 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-BqDfPlX6.mjs"; -import { n as reportHookDeliveryFailure, t as deliverProjectRequest } from "./_delivery-BPnYIm56.mjs"; +import { t as resolveProject } from "./_project-BQWFXz1a.mjs"; +import { n as reportHookDeliveryFailure, t as deliverProjectRequest } from "./_delivery-Bevmy6M8.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 d8f0d716d..6ec063c11 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-BqDfPlX6.mjs"; -import { n as reportObservationDeliveryFailure, t as deliverObservation } from "./_observe-delivery-BSYpE5r3.mjs"; +import { t as resolveProject } from "./_project-BQWFXz1a.mjs"; +import { n as reportObservationDeliveryFailure, t as deliverObservation } from "./_observe-delivery-BsKR4_co.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 f87a72123..9aa6523a3 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-BqDfPlX6.mjs"; -import { n as reportObservationDeliveryFailure, t as deliverObservation } from "./_observe-delivery-BSYpE5r3.mjs"; +import { t as resolveProject } from "./_project-BQWFXz1a.mjs"; +import { n as reportObservationDeliveryFailure, t as deliverObservation } from "./_observe-delivery-BsKR4_co.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 aa8e1aba9..f75d9e780 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-BqDfPlX6.mjs"; -import { n as reportObservationDeliveryFailure, t as deliverObservation } from "./_observe-delivery-BSYpE5r3.mjs"; +import { t as resolveProject } from "./_project-BQWFXz1a.mjs"; +import { n as reportObservationDeliveryFailure, t as deliverObservation } from "./_observe-delivery-BsKR4_co.mjs"; //#region src/hooks/task-completed.ts function isSdkChildContext(payload) { if (process.env["AGENTMEMORY_SDK_CHILD"] === "1") return true; From 16e1cfe9220a17fd1e6db54b40000e1d8dab4948 Mon Sep 17 00:00:00 2001 From: ChronodeAi Date: Mon, 24 Aug 2026 20:36:53 -0500 Subject: [PATCH 09/11] chore(evidence): refresh interface inventory for the round-two hotfix Source identity rotated: api::graph-import-graphify gained the path/cwd admin gate in src/triggers/api.ts and connect/onboarding provisioning gained stderr logging; route count and auth coverage unchanged (0 missing-auth routes). --- .../reports/g-icm-01-interface-inventory.json | 378 +++++++++--------- 1 file changed, 189 insertions(+), 189 deletions(-) diff --git a/.aiwg/reports/g-icm-01-interface-inventory.json b/.aiwg/reports/g-icm-01-interface-inventory.json index aa108649e..022667e0d 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": "5df4a9441d0c2d583498b953c6d3f1f0b78803bb", - "commit_tree_sha": "38f5054811be14f96cf7950b09649a2a2d710002", - "inventory_input_sha256": "fe2dea65299cdc4eeba6e309dccce74b9a001fcd71f9edcee0656897c65e1c4c" + "commit_sha": "4fccce18d5b80b2960b3e4d20a4b27dc8a6dcee0", + "commit_tree_sha": "5eaed513be730cae704fa9db1bf7ed2c98e942e2", + "inventory_input_sha256": "0856e463df6a801997ff92f10a4bf9e07978f9b7ccf2a1bc975afa9bc61d394d" }, "public_route_allowlist": [ "GET /agentmemory/livez" @@ -38,7 +38,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3682" + "source": "src/triggers/api.ts:3710" }, { "surface_id": "REST:POST:/agentmemory/actions", @@ -48,7 +48,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3638" + "source": "src/triggers/api.ts:3666" }, { "surface_id": "REST:POST:/agentmemory/actions/edges", @@ -58,7 +58,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3723" + "source": "src/triggers/api.ts:3751" }, { "surface_id": "REST:GET:/agentmemory/actions/get", @@ -68,7 +68,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3700" + "source": "src/triggers/api.ts:3728" }, { "surface_id": "REST:POST:/agentmemory/actions/update", @@ -78,7 +78,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3664" + "source": "src/triggers/api.ts:3692" }, { "surface_id": "REST:GET:/agentmemory/audit", @@ -88,7 +88,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3079" + "source": "src/triggers/api.ts:3107" }, { "surface_id": "REST:POST:/agentmemory/auto-forget", @@ -108,7 +108,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4239" + "source": "src/triggers/api.ts:4267" }, { "surface_id": "REST:GET:/agentmemory/branch/sessions", @@ -118,7 +118,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4269" + "source": "src/triggers/api.ts:4297" }, { "surface_id": "REST:GET:/agentmemory/branch/worktrees", @@ -128,7 +128,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4254" + "source": "src/triggers/api.ts:4282" }, { "surface_id": "REST:POST:/agentmemory/cascade-update", @@ -138,7 +138,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4564" + "source": "src/triggers/api.ts:4592" }, { "surface_id": "REST:GET:/agentmemory/checkpoints", @@ -148,7 +148,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4006" + "source": "src/triggers/api.ts:4034" }, { "surface_id": "REST:POST:/agentmemory/checkpoints", @@ -158,7 +158,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3965" + "source": "src/triggers/api.ts:3993" }, { "surface_id": "REST:POST:/agentmemory/checkpoints/resolve", @@ -168,7 +168,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3989" + "source": "src/triggers/api.ts:4017" }, { "surface_id": "REST:GET:/agentmemory/claude-bridge/read", @@ -254,7 +254,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:2974" + "source": "src/triggers/api.ts:3002" }, { "surface_id": "REST:POST:/agentmemory/context", @@ -300,7 +300,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4464" + "source": "src/triggers/api.ts:4492" }, { "surface_id": "REST:POST:/agentmemory/crystals/auto", @@ -310,7 +310,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4476" + "source": "src/triggers/api.ts:4504" }, { "surface_id": "REST:POST:/agentmemory/crystals/create", @@ -320,7 +320,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4422" + "source": "src/triggers/api.ts:4450" }, { "surface_id": "REST:POST:/agentmemory/diagnostics", @@ -330,7 +330,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4485" + "source": "src/triggers/api.ts:4513" }, { "surface_id": "REST:GET:/agentmemory/diagnostics/followup", @@ -352,7 +352,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4494" + "source": "src/triggers/api.ts:4522" }, { "surface_id": "REST:POST:/agentmemory/enrich", @@ -402,7 +402,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4533" + "source": "src/triggers/api.ts:4561" }, { "surface_id": "REST:POST:/agentmemory/facets", @@ -412,7 +412,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4504" + "source": "src/triggers/api.ts:4532" }, { "surface_id": "REST:POST:/agentmemory/facets/query", @@ -422,7 +422,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4523" + "source": "src/triggers/api.ts:4551" }, { "surface_id": "REST:POST:/agentmemory/facets/remove", @@ -432,7 +432,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4514" + "source": "src/triggers/api.ts:4542" }, { "surface_id": "REST:GET:/agentmemory/facets/stats", @@ -442,7 +442,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4542" + "source": "src/triggers/api.ts:4570" }, { "surface_id": "REST:POST:/agentmemory/file-context", @@ -462,7 +462,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4224" + "source": "src/triggers/api.ts:4252" }, { "surface_id": "REST:POST:/agentmemory/forget", @@ -482,7 +482,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3742" + "source": "src/triggers/api.ts:3770" }, { "surface_id": "REST:POST:/agentmemory/generate-rules", @@ -502,7 +502,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3149" + "source": "src/triggers/api.ts:3177" }, { "surface_id": "REST:DELETE:/agentmemory/governance/memories", @@ -512,7 +512,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3124" + "source": "src/triggers/api.ts:3152" }, { "surface_id": "REST:POST:/agentmemory/graph/build", @@ -522,7 +522,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:2940" + "source": "src/triggers/api.ts:2968" }, { "surface_id": "REST:POST:/agentmemory/graph/extract", @@ -542,7 +542,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:2813" + "source": "src/triggers/api.ts:2841" }, { "surface_id": "REST:POST:/agentmemory/graph/query", @@ -624,7 +624,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4764" + "source": "src/triggers/api.ts:4792" }, { "surface_id": "REST:POST:/agentmemory/insights/search", @@ -634,7 +634,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4786" + "source": "src/triggers/api.ts:4814" }, { "surface_id": "REST:POST:/agentmemory/leases/acquire", @@ -644,7 +644,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3778" + "source": "src/triggers/api.ts:3806" }, { "surface_id": "REST:POST:/agentmemory/leases/release", @@ -654,7 +654,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3797" + "source": "src/triggers/api.ts:3825" }, { "surface_id": "REST:POST:/agentmemory/leases/renew", @@ -664,7 +664,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3816" + "source": "src/triggers/api.ts:3844" }, { "surface_id": "REST:GET:/agentmemory/lessons", @@ -674,7 +674,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4628" + "source": "src/triggers/api.ts:4656" }, { "surface_id": "REST:POST:/agentmemory/lessons", @@ -684,7 +684,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4593" + "source": "src/triggers/api.ts:4621" }, { "surface_id": "REST:POST:/agentmemory/lessons/delete", @@ -694,7 +694,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4694" + "source": "src/triggers/api.ts:4722" }, { "surface_id": "REST:POST:/agentmemory/lessons/search", @@ -704,7 +704,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4648" + "source": "src/triggers/api.ts:4676" }, { "surface_id": "REST:POST:/agentmemory/lessons/strengthen", @@ -714,7 +714,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4679" + "source": "src/triggers/api.ts:4707" }, { "surface_id": "REST:GET:/agentmemory/livez", @@ -734,7 +734,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3294" + "source": "src/triggers/api.ts:3322" }, { "surface_id": "REST:GET:/agentmemory/memories/:id", @@ -744,7 +744,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3315" + "source": "src/triggers/api.ts:3343" }, { "surface_id": "REST:GET:/agentmemory/mesh/export", @@ -754,7 +754,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4197" + "source": "src/triggers/api.ts:4225" }, { "surface_id": "REST:GET:/agentmemory/mesh/peers", @@ -764,7 +764,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4051" + "source": "src/triggers/api.ts:4079" }, { "surface_id": "REST:POST:/agentmemory/mesh/peers", @@ -774,7 +774,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4031" + "source": "src/triggers/api.ts:4059" }, { "surface_id": "REST:POST:/agentmemory/mesh/receive", @@ -784,7 +784,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4093" + "source": "src/triggers/api.ts:4121" }, { "surface_id": "REST:POST:/agentmemory/mesh/sync", @@ -794,7 +794,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4073" + "source": "src/triggers/api.ts:4101" }, { "surface_id": "REST:POST:/agentmemory/migrate", @@ -814,7 +814,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3759" + "source": "src/triggers/api.ts:3787" }, { "surface_id": "REST:GET:/agentmemory/observations", @@ -846,7 +846,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4711" + "source": "src/triggers/api.ts:4739" }, { "surface_id": "REST:POST:/agentmemory/patterns", @@ -866,7 +866,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3343" + "source": "src/triggers/api.ts:3371" }, { "surface_id": "REST:GET:/agentmemory/profile", @@ -922,7 +922,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4730" + "source": "src/triggers/api.ts:4758" }, { "surface_id": "REST:GET:/agentmemory/relations", @@ -932,7 +932,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3357" + "source": "src/triggers/api.ts:3385" }, { "surface_id": "REST:POST:/agentmemory/relations", @@ -992,7 +992,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3854" + "source": "src/triggers/api.ts:3882" }, { "surface_id": "REST:POST:/agentmemory/routines", @@ -1002,7 +1002,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3838" + "source": "src/triggers/api.ts:3866" }, { "surface_id": "REST:POST:/agentmemory/routines/run", @@ -1012,7 +1012,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3873" + "source": "src/triggers/api.ts:3901" }, { "surface_id": "REST:GET:/agentmemory/routines/status", @@ -1022,7 +1022,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3891" + "source": "src/triggers/api.ts:3919" }, { "surface_id": "REST:POST:/agentmemory/search", @@ -1044,7 +1044,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3329" + "source": "src/triggers/api.ts:3357" }, { "surface_id": "REST:GET:/agentmemory/sentinels", @@ -1054,7 +1054,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4350" + "source": "src/triggers/api.ts:4378" }, { "surface_id": "REST:POST:/agentmemory/sentinels", @@ -1064,7 +1064,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4313" + "source": "src/triggers/api.ts:4341" }, { "surface_id": "REST:POST:/agentmemory/sentinels/cancel", @@ -1074,7 +1074,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4341" + "source": "src/triggers/api.ts:4369" }, { "surface_id": "REST:POST:/agentmemory/sentinels/check", @@ -1084,7 +1084,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4331" + "source": "src/triggers/api.ts:4359" }, { "surface_id": "REST:POST:/agentmemory/sentinels/trigger", @@ -1094,7 +1094,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4323" + "source": "src/triggers/api.ts:4351" }, { "surface_id": "REST:GET:/agentmemory/session/by-commit", @@ -1162,7 +1162,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3940" + "source": "src/triggers/api.ts:3968" }, { "surface_id": "REST:POST:/agentmemory/signals/send", @@ -1172,7 +1172,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3916" + "source": "src/triggers/api.ts:3944" }, { "surface_id": "REST:GET:/agentmemory/sketches", @@ -1182,7 +1182,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4399" + "source": "src/triggers/api.ts:4427" }, { "surface_id": "REST:POST:/agentmemory/sketches", @@ -1192,7 +1192,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4360" + "source": "src/triggers/api.ts:4388" }, { "surface_id": "REST:POST:/agentmemory/sketches/add", @@ -1202,7 +1202,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4370" + "source": "src/triggers/api.ts:4398" }, { "surface_id": "REST:POST:/agentmemory/sketches/discard", @@ -1212,7 +1212,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4390" + "source": "src/triggers/api.ts:4418" }, { "surface_id": "REST:POST:/agentmemory/sketches/gc", @@ -1222,7 +1222,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4407" + "source": "src/triggers/api.ts:4435" }, { "surface_id": "REST:POST:/agentmemory/sketches/promote", @@ -1232,7 +1232,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4380" + "source": "src/triggers/api.ts:4408" }, { "surface_id": "REST:DELETE:/agentmemory/slot", @@ -1242,7 +1242,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3588" + "source": "src/triggers/api.ts:3616" }, { "surface_id": "REST:GET:/agentmemory/slot", @@ -1252,7 +1252,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3467" + "source": "src/triggers/api.ts:3495" }, { "surface_id": "REST:POST:/agentmemory/slot", @@ -1262,7 +1262,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3518" + "source": "src/triggers/api.ts:3546" }, { "surface_id": "REST:POST:/agentmemory/slot/append", @@ -1272,7 +1272,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3542" + "source": "src/triggers/api.ts:3570" }, { "surface_id": "REST:POST:/agentmemory/slot/reflect", @@ -1282,7 +1282,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3610" + "source": "src/triggers/api.ts:3638" }, { "surface_id": "REST:POST:/agentmemory/slot/replace", @@ -1292,7 +1292,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3568" + "source": "src/triggers/api.ts:3596" }, { "surface_id": "REST:GET:/agentmemory/slots", @@ -1302,7 +1302,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3447" + "source": "src/triggers/api.ts:3475" }, { "surface_id": "REST:POST:/agentmemory/smart-search", @@ -1322,7 +1322,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3189" + "source": "src/triggers/api.ts:3217" }, { "surface_id": "REST:POST:/agentmemory/snapshot/restore", @@ -1332,7 +1332,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3210" + "source": "src/triggers/api.ts:3238" }, { "surface_id": "REST:GET:/agentmemory/snapshots", @@ -1342,7 +1342,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3170" + "source": "src/triggers/api.ts:3198" }, { "surface_id": "REST:POST:/agentmemory/summarize", @@ -1364,7 +1364,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3023" + "source": "src/triggers/api.ts:3051" }, { "surface_id": "REST:GET:/agentmemory/team/profile", @@ -1374,7 +1374,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3041" + "source": "src/triggers/api.ts:3069" }, { "surface_id": "REST:POST:/agentmemory/team/share", @@ -1384,7 +1384,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3003" + "source": "src/triggers/api.ts:3031" }, { "surface_id": "REST:POST:/agentmemory/timeline", @@ -1404,7 +1404,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4552" + "source": "src/triggers/api.ts:4580" }, { "surface_id": "REST:GET:/agentmemory/viewer", @@ -1414,7 +1414,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:4299" + "source": "src/triggers/api.ts:4327" }, { "surface_id": "REST:POST:/agentmemory/vision-embed", @@ -1424,7 +1424,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3432" + "source": "src/triggers/api.ts:3460" }, { "surface_id": "REST:POST:/agentmemory/vision-search", @@ -1434,7 +1434,7 @@ "middleware": [], "registration": "protected-wrapper", "auth_control": "required", - "source": "src/triggers/api.ts:3400" + "source": "src/triggers/api.ts:3428" } ], "mcp_transport": [ @@ -2222,28 +2222,28 @@ ], "provider_attempt_sites": [ { - "surface_id": "PROVIDER:ATTEMPT:src/cli/connect/index.ts:159:summarize", + "surface_id": "PROVIDER:ATTEMPT:src/cli/connect/index.ts:163:summarize", "purpose": "query", "kind": "invocation", "method": "summarize", "receiver": null, - "source": "src/cli/connect/index.ts:159" + "source": "src/cli/connect/index.ts:163" }, { - "surface_id": "PROVIDER:ATTEMPT:src/cli/connect/index.ts:176:summarize", + "surface_id": "PROVIDER:ATTEMPT:src/cli/connect/index.ts:180:summarize", "purpose": "query", "kind": "invocation", "method": "summarize", "receiver": null, - "source": "src/cli/connect/index.ts:176" + "source": "src/cli/connect/index.ts:180" }, { - "surface_id": "PROVIDER:ATTEMPT:src/cli/connect/index.ts:189:summarize", + "surface_id": "PROVIDER:ATTEMPT:src/cli/connect/index.ts:193:summarize", "purpose": "query", "kind": "invocation", "method": "summarize", "receiver": null, - "source": "src/cli/connect/index.ts:189" + "source": "src/cli/connect/index.ts:193" }, { "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:3682", + "source": "src/triggers/api.ts:3710", "auth_control": "required", "control_ids": [ "ICM-04", @@ -2680,7 +2680,7 @@ { "surface_id": "REST:POST:/agentmemory/actions", "type": "rest", - "source": "src/triggers/api.ts:3638", + "source": "src/triggers/api.ts:3666", "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:3723", + "source": "src/triggers/api.ts:3751", "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:3700", + "source": "src/triggers/api.ts:3728", "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:3664", + "source": "src/triggers/api.ts:3692", "auth_control": "required", "control_ids": [ "ICM-04", @@ -2780,7 +2780,7 @@ { "surface_id": "REST:GET:/agentmemory/audit", "type": "rest", - "source": "src/triggers/api.ts:3079", + "source": "src/triggers/api.ts:3107", "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:4239", + "source": "src/triggers/api.ts:4267", "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:4269", + "source": "src/triggers/api.ts:4297", "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:4254", + "source": "src/triggers/api.ts:4282", "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:4564", + "source": "src/triggers/api.ts:4592", "auth_control": "required", "control_ids": [ "ICM-04", @@ -2930,7 +2930,7 @@ { "surface_id": "REST:GET:/agentmemory/checkpoints", "type": "rest", - "source": "src/triggers/api.ts:4006", + "source": "src/triggers/api.ts:4034", "auth_control": "required", "control_ids": [ "ICM-04", @@ -2955,7 +2955,7 @@ { "surface_id": "REST:POST:/agentmemory/checkpoints", "type": "rest", - "source": "src/triggers/api.ts:3965", + "source": "src/triggers/api.ts:3993", "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:3989", + "source": "src/triggers/api.ts:4017", "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:2974", + "source": "src/triggers/api.ts:3002", "auth_control": "required", "control_ids": [ "ICM-04", @@ -3270,7 +3270,7 @@ { "surface_id": "REST:GET:/agentmemory/crystals", "type": "rest", - "source": "src/triggers/api.ts:4464", + "source": "src/triggers/api.ts:4492", "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:4476", + "source": "src/triggers/api.ts:4504", "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:4422", + "source": "src/triggers/api.ts:4450", "auth_control": "required", "control_ids": [ "ICM-04", @@ -3345,7 +3345,7 @@ { "surface_id": "REST:POST:/agentmemory/diagnostics", "type": "rest", - "source": "src/triggers/api.ts:4485", + "source": "src/triggers/api.ts:4513", "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:4494", + "source": "src/triggers/api.ts:4522", "auth_control": "required", "control_ids": [ "ICM-04", @@ -3520,7 +3520,7 @@ { "surface_id": "REST:GET:/agentmemory/facets", "type": "rest", - "source": "src/triggers/api.ts:4533", + "source": "src/triggers/api.ts:4561", "auth_control": "required", "control_ids": [ "ICM-04", @@ -3545,7 +3545,7 @@ { "surface_id": "REST:POST:/agentmemory/facets", "type": "rest", - "source": "src/triggers/api.ts:4504", + "source": "src/triggers/api.ts:4532", "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:4523", + "source": "src/triggers/api.ts:4551", "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:4514", + "source": "src/triggers/api.ts:4542", "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:4542", + "source": "src/triggers/api.ts:4570", "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:4224", + "source": "src/triggers/api.ts:4252", "auth_control": "required", "control_ids": [ "ICM-04", @@ -3720,7 +3720,7 @@ { "surface_id": "REST:GET:/agentmemory/frontier", "type": "rest", - "source": "src/triggers/api.ts:3742", + "source": "src/triggers/api.ts:3770", "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:3149", + "source": "src/triggers/api.ts:3177", "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:3124", + "source": "src/triggers/api.ts:3152", "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:2940", + "source": "src/triggers/api.ts:2968", "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:2813", + "source": "src/triggers/api.ts:2841", "auth_control": "required", "control_ids": [ "ICM-04", @@ -4061,7 +4061,7 @@ { "surface_id": "REST:GET:/agentmemory/insights", "type": "rest", - "source": "src/triggers/api.ts:4764", + "source": "src/triggers/api.ts:4792", "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:4786", + "source": "src/triggers/api.ts:4814", "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:3778", + "source": "src/triggers/api.ts:3806", "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:3797", + "source": "src/triggers/api.ts:3825", "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:3816", + "source": "src/triggers/api.ts:3844", "auth_control": "required", "control_ids": [ "ICM-04", @@ -4176,7 +4176,7 @@ { "surface_id": "REST:GET:/agentmemory/lessons", "type": "rest", - "source": "src/triggers/api.ts:4628", + "source": "src/triggers/api.ts:4656", "auth_control": "required", "control_ids": [ "ICM-08" @@ -4196,7 +4196,7 @@ { "surface_id": "REST:POST:/agentmemory/lessons", "type": "rest", - "source": "src/triggers/api.ts:4593", + "source": "src/triggers/api.ts:4621", "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:4694", + "source": "src/triggers/api.ts:4722", "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:4648", + "source": "src/triggers/api.ts:4676", "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:4679", + "source": "src/triggers/api.ts:4707", "auth_control": "required", "control_ids": [ "ICM-08" @@ -4301,7 +4301,7 @@ { "surface_id": "REST:GET:/agentmemory/memories", "type": "rest", - "source": "src/triggers/api.ts:3294", + "source": "src/triggers/api.ts:3322", "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:3315", + "source": "src/triggers/api.ts:3343", "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:4197", + "source": "src/triggers/api.ts:4225", "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:4051", + "source": "src/triggers/api.ts:4079", "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:4031", + "source": "src/triggers/api.ts:4059", "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:4093", + "source": "src/triggers/api.ts:4121", "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:4073", + "source": "src/triggers/api.ts:4101", "auth_control": "required", "control_ids": [ "ICM-04", @@ -4497,7 +4497,7 @@ { "surface_id": "REST:GET:/agentmemory/next", "type": "rest", - "source": "src/triggers/api.ts:3759", + "source": "src/triggers/api.ts:3787", "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:4711", + "source": "src/triggers/api.ts:4739", "auth_control": "required", "control_ids": [ "ICM-04", @@ -4622,7 +4622,7 @@ { "surface_id": "REST:GET:/agentmemory/procedural", "type": "rest", - "source": "src/triggers/api.ts:3343", + "source": "src/triggers/api.ts:3371", "auth_control": "required", "control_ids": [ "ICM-04", @@ -4736,7 +4736,7 @@ { "surface_id": "REST:POST:/agentmemory/reflect", "type": "rest", - "source": "src/triggers/api.ts:4730", + "source": "src/triggers/api.ts:4758", "auth_control": "required", "control_ids": [ "ICM-04", @@ -4761,7 +4761,7 @@ { "surface_id": "REST:GET:/agentmemory/relations", "type": "rest", - "source": "src/triggers/api.ts:3357", + "source": "src/triggers/api.ts:3385", "auth_control": "required", "control_ids": [ "ICM-04", @@ -4906,7 +4906,7 @@ { "surface_id": "REST:GET:/agentmemory/routines", "type": "rest", - "source": "src/triggers/api.ts:3854", + "source": "src/triggers/api.ts:3882", "auth_control": "required", "control_ids": [ "ICM-04", @@ -4931,7 +4931,7 @@ { "surface_id": "REST:POST:/agentmemory/routines", "type": "rest", - "source": "src/triggers/api.ts:3838", + "source": "src/triggers/api.ts:3866", "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:3873", + "source": "src/triggers/api.ts:3901", "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:3891", + "source": "src/triggers/api.ts:3919", "auth_control": "required", "control_ids": [ "ICM-04", @@ -5031,7 +5031,7 @@ { "surface_id": "REST:GET:/agentmemory/semantic", "type": "rest", - "source": "src/triggers/api.ts:3329", + "source": "src/triggers/api.ts:3357", "auth_control": "required", "control_ids": [ "ICM-04", @@ -5056,7 +5056,7 @@ { "surface_id": "REST:GET:/agentmemory/sentinels", "type": "rest", - "source": "src/triggers/api.ts:4350", + "source": "src/triggers/api.ts:4378", "auth_control": "required", "control_ids": [ "ICM-04", @@ -5081,7 +5081,7 @@ { "surface_id": "REST:POST:/agentmemory/sentinels", "type": "rest", - "source": "src/triggers/api.ts:4313", + "source": "src/triggers/api.ts:4341", "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:4341", + "source": "src/triggers/api.ts:4369", "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:4331", + "source": "src/triggers/api.ts:4359", "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:4323", + "source": "src/triggers/api.ts:4351", "auth_control": "required", "control_ids": [ "ICM-04", @@ -5296,7 +5296,7 @@ { "surface_id": "REST:GET:/agentmemory/signals", "type": "rest", - "source": "src/triggers/api.ts:3940", + "source": "src/triggers/api.ts:3968", "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:3916", + "source": "src/triggers/api.ts:3944", "auth_control": "required", "control_ids": [ "ICM-04", @@ -5346,7 +5346,7 @@ { "surface_id": "REST:GET:/agentmemory/sketches", "type": "rest", - "source": "src/triggers/api.ts:4399", + "source": "src/triggers/api.ts:4427", "auth_control": "required", "control_ids": [ "ICM-04", @@ -5371,7 +5371,7 @@ { "surface_id": "REST:POST:/agentmemory/sketches", "type": "rest", - "source": "src/triggers/api.ts:4360", + "source": "src/triggers/api.ts:4388", "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:4370", + "source": "src/triggers/api.ts:4398", "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:4390", + "source": "src/triggers/api.ts:4418", "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:4407", + "source": "src/triggers/api.ts:4435", "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:4380", + "source": "src/triggers/api.ts:4408", "auth_control": "required", "control_ids": [ "ICM-04", @@ -5496,7 +5496,7 @@ { "surface_id": "REST:DELETE:/agentmemory/slot", "type": "rest", - "source": "src/triggers/api.ts:3588", + "source": "src/triggers/api.ts:3616", "auth_control": "required", "control_ids": [ "ICM-11", @@ -5520,7 +5520,7 @@ { "surface_id": "REST:GET:/agentmemory/slot", "type": "rest", - "source": "src/triggers/api.ts:3467", + "source": "src/triggers/api.ts:3495", "auth_control": "required", "control_ids": [ "ICM-11", @@ -5544,7 +5544,7 @@ { "surface_id": "REST:POST:/agentmemory/slot", "type": "rest", - "source": "src/triggers/api.ts:3518", + "source": "src/triggers/api.ts:3546", "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:3542", + "source": "src/triggers/api.ts:3570", "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:3610", + "source": "src/triggers/api.ts:3638", "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:3568", + "source": "src/triggers/api.ts:3596", "auth_control": "required", "control_ids": [ "ICM-11", @@ -5640,7 +5640,7 @@ { "surface_id": "REST:GET:/agentmemory/slots", "type": "rest", - "source": "src/triggers/api.ts:3447", + "source": "src/triggers/api.ts:3475", "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:3189", + "source": "src/triggers/api.ts:3217", "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:3210", + "source": "src/triggers/api.ts:3238", "auth_control": "required", "control_ids": [ "ICM-13" @@ -5731,7 +5731,7 @@ { "surface_id": "REST:GET:/agentmemory/snapshots", "type": "rest", - "source": "src/triggers/api.ts:3170", + "source": "src/triggers/api.ts:3198", "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:3023", + "source": "src/triggers/api.ts:3051", "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:3041", + "source": "src/triggers/api.ts:3069", "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:3003", + "source": "src/triggers/api.ts:3031", "auth_control": "required", "control_ids": [ "ICM-04", @@ -5877,7 +5877,7 @@ { "surface_id": "REST:POST:/agentmemory/verify", "type": "rest", - "source": "src/triggers/api.ts:4552", + "source": "src/triggers/api.ts:4580", "auth_control": "required", "control_ids": [ "ICM-04", @@ -5902,7 +5902,7 @@ { "surface_id": "REST:GET:/agentmemory/viewer", "type": "rest", - "source": "src/triggers/api.ts:4299", + "source": "src/triggers/api.ts:4327", "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:3432", + "source": "src/triggers/api.ts:3460", "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:3400", + "source": "src/triggers/api.ts:3428", "auth_control": "required", "control_ids": [ "ICM-05", @@ -10663,12 +10663,12 @@ ] }, { - "surface_id": "PROVIDER:ATTEMPT:src/cli/connect/index.ts:159:summarize", + "surface_id": "PROVIDER:ATTEMPT:src/cli/connect/index.ts:163:summarize", "purpose": "query", "kind": "invocation", "method": "summarize", "receiver": null, - "source": "src/cli/connect/index.ts:159", + "source": "src/cli/connect/index.ts:163", "type": "provider-attempt", "auth_control": "processing-policy", "control_ids": [ @@ -10689,12 +10689,12 @@ ] }, { - "surface_id": "PROVIDER:ATTEMPT:src/cli/connect/index.ts:176:summarize", + "surface_id": "PROVIDER:ATTEMPT:src/cli/connect/index.ts:180:summarize", "purpose": "query", "kind": "invocation", "method": "summarize", "receiver": null, - "source": "src/cli/connect/index.ts:176", + "source": "src/cli/connect/index.ts:180", "type": "provider-attempt", "auth_control": "processing-policy", "control_ids": [ @@ -10715,12 +10715,12 @@ ] }, { - "surface_id": "PROVIDER:ATTEMPT:src/cli/connect/index.ts:189:summarize", + "surface_id": "PROVIDER:ATTEMPT:src/cli/connect/index.ts:193:summarize", "purpose": "query", "kind": "invocation", "method": "summarize", "receiver": null, - "source": "src/cli/connect/index.ts:189", + "source": "src/cli/connect/index.ts:193", "type": "provider-attempt", "auth_control": "processing-policy", "control_ids": [ From 023433b12303e2b98e4e709c02425493b3e6df48 Mon Sep 17 00:00:00 2001 From: ChronodeAi Date: Mon, 24 Aug 2026 20:40:48 -0500 Subject: [PATCH 10/11] chore(release): 0.9.30-chronode.3 --- package-lock.json | 4 ++-- package.json | 2 +- packages/mcp/package.json | 4 ++-- plugin/.claude-plugin/plugin.json | 2 +- plugin/.codex-plugin/plugin.json | 2 +- plugin/plugin.json | 2 +- plugin/scripts/standalone.mjs | 2 +- src/version.ts | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index fa874c898..df4a835e2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@agentmemory/agentmemory", - "version": "0.9.30-chronode.2", + "version": "0.9.30-chronode.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@agentmemory/agentmemory", - "version": "0.9.30-chronode.2", + "version": "0.9.30-chronode.3", "license": "Apache-2.0", "dependencies": { "@anthropic-ai/claude-agent-sdk": "^0.3.142", diff --git a/package.json b/package.json index 022a09199..c102528f3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@agentmemory/agentmemory", - "version": "0.9.30-chronode.2", + "version": "0.9.30-chronode.3", "description": "Persistent memory for AI coding agents, powered by iii-engine's three primitives", "type": "module", "main": "dist/index.mjs", diff --git a/packages/mcp/package.json b/packages/mcp/package.json index 66a08efb9..97dfb4db2 100644 --- a/packages/mcp/package.json +++ b/packages/mcp/package.json @@ -1,6 +1,6 @@ { "name": "@agentmemory/mcp", - "version": "0.9.30-chronode.2", + "version": "0.9.30-chronode.3", "description": "Standalone MCP server for agentmemory — thin shim that re-exposes @agentmemory/agentmemory's MCP entrypoint", "type": "module", "bin": { @@ -28,7 +28,7 @@ "homepage": "https://github.com/rohitg00/agentmemory#readme", "bugs": "https://github.com/rohitg00/agentmemory/issues", "dependencies": { - "@agentmemory/agentmemory": "0.9.30-chronode.2" + "@agentmemory/agentmemory": "0.9.30-chronode.3" }, "publishConfig": { "access": "public", diff --git a/plugin/.claude-plugin/plugin.json b/plugin/.claude-plugin/plugin.json index beb559157..f04e47b97 100644 --- a/plugin/.claude-plugin/plugin.json +++ b/plugin/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "agentmemory", - "version": "0.9.30-chronode.2", + "version": "0.9.30-chronode.3", "description": "Persistent memory for AI coding agents -- captures tool usage, compresses via LLM, injects context into future sessions. 12 hooks, 60 MCP tools, 17 skills, real-time viewer.", "author": { "name": "Rohit Ghumare", diff --git a/plugin/.codex-plugin/plugin.json b/plugin/.codex-plugin/plugin.json index bdbb95f81..63a581e3d 100644 --- a/plugin/.codex-plugin/plugin.json +++ b/plugin/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "agentmemory", - "version": "0.9.30-chronode.2", + "version": "0.9.30-chronode.3", "description": "Persistent memory for AI coding agents -- captures tool usage, compresses via LLM, injects context into future sessions. 11 hooks, 60 MCP tools, 17 skills, real-time viewer.", "author": { "name": "Rohit Ghumare", diff --git a/plugin/plugin.json b/plugin/plugin.json index 3c662ec7f..d9d716781 100644 --- a/plugin/plugin.json +++ b/plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "agentmemory", - "version": "0.9.30-chronode.2", + "version": "0.9.30-chronode.3", "description": "Persistent memory for AI coding agents -- captures tool usage, compresses via LLM, injects context into future sessions. 12 hooks, 60 MCP tools, 17 skills, real-time viewer.", "author": { "name": "Rohit Ghumare", diff --git a/plugin/scripts/standalone.mjs b/plugin/scripts/standalone.mjs index 97fe1a700..877fc0b06 100755 --- a/plugin/scripts/standalone.mjs +++ b/plugin/scripts/standalone.mjs @@ -1761,7 +1761,7 @@ function getAllTools() { } //#endregion //#region src/version.ts -const VERSION = "0.9.30-chronode.2"; +const VERSION = "0.9.30-chronode.3"; process.env["AGENTMEMORY_BUILD_ID"]; process.env["AGENTMEMORY_VIEWER_BUILD_ID"]; //#endregion diff --git a/src/version.ts b/src/version.ts index 60413cef4..6af4c9dbb 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1,4 +1,4 @@ -export const VERSION = "0.9.30-chronode.2"; +export const VERSION = "0.9.30-chronode.3"; export const EXPORT_FORMAT_VERSION = "0.9.28" as const; export const API_CONTRACT_VERSION = 1; export const BACKEND_BUILD_ID = From 209665117300f7dbeda23c4747343290a49bc0d1 Mon Sep 17 00:00:00 2001 From: ChronodeAi Date: Mon, 24 Aug 2026 20:40:48 -0500 Subject: [PATCH 11/11] chore(evidence): refresh inventory for release commit --- .aiwg/reports/g-icm-01-interface-inventory.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.aiwg/reports/g-icm-01-interface-inventory.json b/.aiwg/reports/g-icm-01-interface-inventory.json index 022667e0d..8ae1a672a 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": "4fccce18d5b80b2960b3e4d20a4b27dc8a6dcee0", - "commit_tree_sha": "5eaed513be730cae704fa9db1bf7ed2c98e942e2", - "inventory_input_sha256": "0856e463df6a801997ff92f10a4bf9e07978f9b7ccf2a1bc975afa9bc61d394d" + "commit_sha": "023433b12303e2b98e4e709c02425493b3e6df48", + "commit_tree_sha": "f87647c615d93a42014f3a4b28603412b0bf7e11", + "inventory_input_sha256": "291b5a4f9644ce81144bf8cc6190e786a834315172a759d24a44a22c5f013855" }, "public_route_allowlist": [ "GET /agentmemory/livez"