Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .aiwg/reports/g-icm-01-interface-inventory.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
"control_id": "G-ICM-01",
"project_id": "github.com/chronodeai/agentmemory",
"source_identity": {
"commit_sha": "78e11a0b67c66f9e848001a889fe02300360ec5c",
"commit_tree_sha": "dd211619484da34de4ba4958f0940ea8c2601cbb",
"inventory_input_sha256": "948e6690e0c490edefca5c834f6afb812e38b18ca38fdd56ff28ecd959e16979"
"commit_sha": "5df4a9441d0c2d583498b953c6d3f1f0b78803bb",
"commit_tree_sha": "38f5054811be14f96cf7950b09649a2a2d710002",
"inventory_input_sha256": "fe2dea65299cdc4eeba6e309dccce74b9a001fcd71f9edcee0656897c65e1c4c"
},
"public_route_allowlist": [
"GET /agentmemory/livez"
Expand Down
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,26 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),

## [Unreleased]

## [0.9.30-chronode.2] — 2026-08-24

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.
- **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 `<data-dir>/.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.
- **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 `<data-dir>/projects/<hash>.yaml`, falling back to the legacy `~/.agentmemory/projects/<hash>.yaml` copy so existing overrides keep working.

### Changed

- `<data-dir>/.env` is parsed once per daemon process; edits require a daemon restart to take effect.

## [0.9.30-chronode.1] — 2026-08-24

Upstream v0.9.29 sync wave, staged across five reviewed trains (#5–#9). Fork architecture of record unchanged: remote-derived canonical project identity, exclusive project scope, fail-closed governance, self-contained bundled hooks.
Expand Down
6 changes: 3 additions & 3 deletions ci/r13-test-manifest.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"count": 171,
"sha256": "dfb0a43ae58136529042da617518eddb884a379233b038ca6970aa46d993ac8f",
"content_sha256": "4a9a6cfa26b4796eadf14ec294559de28dfdfba7f7083c65fc7bbd479c6326b8"
"count": 176,
"sha256": "4ef314289367720f8cbe199ca8f2823eea416ec3fa0c861fefcbaf0550d530f1",
"content_sha256": "542b9f58957d1ac9fa9421abb47bbd4a985dae5a3a1fe6ee4b2f1916d527693e"
}
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@agentmemory/agentmemory",
"version": "0.9.30-chronode.1",
"version": "0.9.30-chronode.2",
"description": "Persistent memory for AI coding agents, powered by iii-engine's three primitives",
"type": "module",
"main": "dist/index.mjs",
Expand Down
4 changes: 2 additions & 2 deletions packages/mcp/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@agentmemory/mcp",
"version": "0.9.30-chronode.1",
"version": "0.9.30-chronode.2",
"description": "Standalone MCP server for agentmemory — thin shim that re-exposes @agentmemory/agentmemory's MCP entrypoint",
"type": "module",
"bin": {
Expand Down Expand Up @@ -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.1"
"@agentmemory/agentmemory": "0.9.30-chronode.2"
},
"publishConfig": {
"access": "public",
Expand Down
2 changes: 1 addition & 1 deletion plugin/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "agentmemory",
"version": "0.9.30-chronode.1",
"version": "0.9.30-chronode.2",
"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",
Expand Down
2 changes: 1 addition & 1 deletion plugin/.codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "agentmemory",
"version": "0.9.30-chronode.1",
"version": "0.9.30-chronode.2",
"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",
Expand Down
2 changes: 1 addition & 1 deletion plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "agentmemory",
"version": "0.9.30-chronode.1",
"version": "0.9.30-chronode.2",
"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",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { i as isStrictCapabilityMode, n as PROJECT_CAPABILITY_PROJECT_HEADER, r as createProjectCapabilityToken } from "./auth-Evr8deOq.mjs";
import { i as isStrictCapabilityMode, n as PROJECT_CAPABILITY_PROJECT_HEADER, o as hydrateProcessEnvFromFile, r as createProjectCapabilityToken, s as resolveDataDir } from "./auth-DkiaFluQ.mjs";
import { createRequire } from "node:module";
import { existsSync, readFileSync, realpathSync } from "node:fs";
import { isAbsolute, join, relative, resolve } from "node:path";
Expand Down Expand Up @@ -6574,7 +6574,7 @@ var require_public_api = /* @__PURE__ */ __commonJSMin(((exports) => {
exports.stringify = stringify;
}));
//#endregion
//#region src/data-dir.ts
//#region src/project-config.ts
var import_dist = (/* @__PURE__ */ __commonJSMin(((exports) => {
var composer = require_composer();
var Document = require_Document();
Expand Down Expand Up @@ -6621,89 +6621,6 @@ var import_dist = (/* @__PURE__ */ __commonJSMin(((exports) => {
exports.visit = visit.visit;
exports.visitAsync = visit.visitAsync;
})))();
const DATA_DIR_FLAG = "--data-dir";
const DATA_DIR_ENV = "AGENTMEMORY_DATA_DIR";
function readDataDirFlag(argv) {
const equalsPrefix = `${DATA_DIR_FLAG}=`;
for (const arg of argv) if (arg.startsWith(equalsPrefix)) return arg.slice(equalsPrefix.length);
const idx = argv.indexOf(DATA_DIR_FLAG);
if (idx !== -1) return argv[idx + 1];
}
function expandHomePath(pathValue, home) {
if (pathValue === "~") return home;
if (pathValue.startsWith("~/") || pathValue.startsWith("~\\")) return join(home, pathValue.slice(2));
return pathValue;
}
function defaultDataDir(home = homedir()) {
return join(home, ".agentmemory");
}
function toAbsoluteDataDir(raw, cwd, home) {
const expanded = expandHomePath(raw.trim(), home);
return isAbsolute(expanded) ? expanded : resolve(cwd, expanded);
}
function resolveDataDirDetailed(options = {}) {
const argv = options.argv ?? process.argv.slice(2);
const env = options.env ?? process.env;
const cwd = options.cwd ?? process.cwd();
const home = options.home ?? homedir();
const flagValue = readDataDirFlag(argv);
if (flagValue !== void 0 && flagValue.trim().length > 0) return {
dir: toAbsoluteDataDir(flagValue, cwd, home),
source: "flag"
};
const envValue = env[DATA_DIR_ENV];
if (envValue !== void 0 && envValue.trim().length > 0) return {
dir: toAbsoluteDataDir(envValue, cwd, home),
source: "env"
};
return {
dir: defaultDataDir(home),
source: "default"
};
}
function resolveDataDir(options = {}) {
return resolveDataDirDetailed(options).dir;
}
//#endregion
//#region src/config.ts
let envFileCache;
function envFilePath() {
return join(resolveDataDir(), ".env");
}
function loadEnvFile() {
if (envFileCache) return envFileCache;
const envFile = envFilePath();
if (!existsSync(envFile)) {
envFileCache = {};
return envFileCache;
}
const content = readFileSync(envFile, "utf-8");
const vars = {};
for (const line of content.split("\n")) {
const trimmed = line.trim();
if (!trimmed || trimmed.startsWith("#")) continue;
const eqIdx = trimmed.indexOf("=");
if (eqIdx === -1) continue;
const key = trimmed.slice(0, eqIdx).trim();
let val = trimmed.slice(eqIdx + 1).trim();
const quoteChar = val[0] === "\"" || val[0] === "'" ? val[0] : "";
if (quoteChar) {
const closeIdx = val.indexOf(quoteChar, 1);
if (closeIdx !== -1) val = val.slice(1, closeIdx);
} else {
const hashIdx = val.indexOf(" #");
if (hashIdx !== -1) val = val.slice(0, hashIdx).trim();
}
vars[key] = val;
}
envFileCache = vars;
return envFileCache;
}
function hydrateProcessEnvFromFile() {
for (const [k, v] of Object.entries(loadEnvFile())) if (process.env[k] === void 0) process.env[k] = v;
}
//#endregion
//#region src/project-config.ts
const PRIVACY_ORDER = {
standard: 0,
private: 1,
Expand Down Expand Up @@ -6826,6 +6743,19 @@ function normalizeGitRemote(remote) {
return;
}
}
/**
* Mask credentials before a raw git remote reaches stderr. Everything from
* the last "@" onward is kept (host/path), everything between the scheme
* and that "@" is replaced; remotes without "@" pass through unchanged.
*
* @param value - Raw `git remote get-url` output, in any git-supported form.
* @returns A log-safe rendering of the remote.
*/
function redactRemoteForLog(value) {
const at = value.lastIndexOf("@");
if (at === -1) return value;
return `${value.match(/^[a-z][a-z0-9+.-]*:\/\//i)?.[0] ?? ""}***@${value.slice(at + 1)}`;
}
let warnedUnnormalizableRemote = false;
function inferProjectId(root) {
const remote = git(root, [
Expand All @@ -6842,7 +6772,7 @@ function inferProjectId(root) {
if (remote && !normalizedRemote) {
if (!warnedUnnormalizableRemote) {
warnedUnnormalizableRemote = true;
process.stderr.write(`[agentmemory] Git remote "${remote}" cannot be normalized to a canonical project id; using local/${projectPathHash(root)} for this checkout\n`);
process.stderr.write(`[agentmemory] Git remote "${redactRemoteForLog(remote)}" cannot be normalized to a canonical project id; using local/${projectPathHash(root)} for this checkout\n`);
}
}
return normalizedRemote ?? `local/${projectPathHash(root)}`;
Expand All @@ -6868,7 +6798,10 @@ function readConfigFile(path) {
}
}
function getUserProjectConfigPath(root) {
return join(userHome(), ".agentmemory", "projects", `${projectPathHash(root)}.yaml`);
const hashed = `${projectPathHash(root)}.yaml`;
const dataDirOverride = join(resolveDataDir(), "projects", hashed);
if (existsSync(dataDirOverride)) return dataDirOverride;
return join(userHome(), ".agentmemory", "projects", hashed);
}
function loadAgentmemoryEnvironment() {
hydrateProcessEnvFromFile();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { a as normalizedProjectPath, r as isProjectPathExcluded } from "./_auth-8LRLcG2I.mjs";
import { a as normalizedProjectPath, r as isProjectPathExcluded } from "./_auth-1Z57rc-e.mjs";
import { resolve } from "node:path";
import { createHash } from "node:crypto";
import { execFileSync } from "node:child_process";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { n as projectAuthHeaders } from "./_auth-8LRLcG2I.mjs";
import { n as projectAuthHeaders } from "./_auth-1Z57rc-e.mjs";
//#region src/hooks/_delivery.ts
var HookDeliveryError = class extends Error {
retryable;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { n as projectAuthHeaders } from "./_auth-8LRLcG2I.mjs";
import { n as projectAuthHeaders } from "./_auth-1Z57rc-e.mjs";
//#region src/hooks/_observe-delivery.ts
const MAX_ATTEMPTS = 2;
const REQUEST_TIMEOUT_MS = 250;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { i as loadAgentmemoryEnvironment, o as resolveProjectConfig } from "./_auth-8LRLcG2I.mjs";
import { i as loadAgentmemoryEnvironment, o as resolveProjectConfig } from "./_auth-1Z57rc-e.mjs";
//#region src/hooks/_project.ts
loadAgentmemoryEnvironment();
/**
Expand Down
Loading
Loading