@agentconnect.md/daemon 1.0.0-rc.245 → 1.0.0-rc.246

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1950,6 +1950,14 @@ const MAX_INDEX_INJECT_BYTES = 25e3;
1950
1950
  /** Hard cap on a single memory file. Injected/read every session, so an unbounded
1951
1951
  * file would swell the prompt and the wire; writes over this are rejected. */
1952
1952
  const MAX_MEMORY_FILE_BYTES = 256e3;
1953
+ /** The append-only change log for an agent's memory, a sidecar beside the memory
1954
+ * files (`<agent-root>/memory/.history`). One JSON line per write, for provenance
1955
+ * (who/what/when changed a file). Dotfile so it never surfaces as a topic. */
1956
+ const MEMORY_HISTORY_FILENAME = ".history";
1957
+ /** Cap on a `before`/`after` snapshot stored in a history line — keeps a single log
1958
+ * entry bounded even for a large file. Over this, the snapshot is truncated (with a
1959
+ * `…` marker) rather than omitted, so the line stays small but still human-readable. */
1960
+ const MAX_HISTORY_VALUE_BYTES = 4e3;
1953
1961
  /** Raised when a memory path escapes the memory dir. Surfaces as `BAD_PAYLOAD`. */
1954
1962
  var MemoryPathError = class extends Error {
1955
1963
  constructor(message) {
@@ -2013,17 +2021,47 @@ async function readMemoryFile(agentDir, relPath) {
2013
2021
  throw err;
2014
2022
  }
2015
2023
  }
2024
+ /** Truncate a snapshot to the history value cap, on a UTF-8 boundary, flagging it. */
2025
+ function clampHistoryValue(text) {
2026
+ if (Buffer.byteLength(text) <= 4e3) return {
2027
+ value: text,
2028
+ truncated: false
2029
+ };
2030
+ return {
2031
+ value: Buffer.from(text, "utf8").subarray(0, MAX_HISTORY_VALUE_BYTES).toString("utf8") + "…",
2032
+ truncated: true
2033
+ };
2034
+ }
2035
+ /** Append one line to the memory change log (`<agent-root>/memory/.history`). A single
2036
+ * `O_APPEND` write, so concurrent appends never interleave a partial line. Best-effort:
2037
+ * a logging failure must never fail the write it describes, so errors are swallowed. */
2038
+ async function appendHistory(agentDir, record) {
2039
+ try {
2040
+ await promises.mkdir(memoryDir(agentDir), { recursive: true });
2041
+ await promises.appendFile(join(memoryDir(agentDir), MEMORY_HISTORY_FILENAME), JSON.stringify(record) + "\n", "utf8");
2042
+ } catch {}
2043
+ }
2016
2044
  /** Overwrite a memory file with `content` (creating the dir if needed). Atomic
2017
- * (tmp + rename) so a concurrent read never sees a partial file.
2045
+ * (tmp + rename) so a concurrent read never sees a partial file. Appends a line to
2046
+ * the change log (`.history`) recording the add/update, its `before`/`after`
2047
+ * snapshot (bounded), and the `source`.
2018
2048
  *
2019
2049
  * - Rejects content over {@link MAX_MEMORY_FILE_BYTES} ({@link MemoryTooLargeError}).
2020
2050
  * - `ifMatchMtime` (optimistic concurrency): when given, the current file's mtime
2021
2051
  * must equal it, else {@link MemoryConflictError} — so a console edit can't
2022
2052
  * clobber a newer write. A brand-new file (no mtime) matches `ifMatchMtime`
2023
2053
  * only when the caller passes none (or the empty string). */
2024
- async function writeMemoryFile(agentDir, relPath, content, ifMatchMtime) {
2054
+ async function writeMemoryFile(agentDir, relPath, content, ifMatchMtime, source = "tool") {
2025
2055
  if (Buffer.byteLength(content) > 256e3) throw new MemoryTooLargeError(`memory file exceeds the ${MAX_MEMORY_FILE_BYTES}-byte limit`);
2026
2056
  const abs = resolveInMemoryDir(agentDir, relPath);
2057
+ let before = "";
2058
+ let existed = false;
2059
+ try {
2060
+ before = await promises.readFile(abs, "utf8");
2061
+ existed = true;
2062
+ } catch (err) {
2063
+ if (err.code !== "ENOENT") throw err;
2064
+ }
2027
2065
  if (ifMatchMtime) {
2028
2066
  let current = null;
2029
2067
  try {
@@ -2038,6 +2076,18 @@ async function writeMemoryFile(agentDir, relPath, content, ifMatchMtime) {
2038
2076
  await promises.writeFile(tmp, content, "utf8");
2039
2077
  await promises.rename(tmp, abs);
2040
2078
  const st = await promises.stat(abs);
2079
+ const beforeClamped = existed ? clampHistoryValue(before) : void 0;
2080
+ const afterClamped = clampHistoryValue(content);
2081
+ await appendHistory(agentDir, {
2082
+ path: relPath,
2083
+ event: existed ? "update" : "add",
2084
+ ...beforeClamped ? { before: beforeClamped.value } : {},
2085
+ after: afterClamped.value,
2086
+ at: st.mtime.toISOString(),
2087
+ scope: "agent",
2088
+ source,
2089
+ ...afterClamped.truncated || beforeClamped?.truncated ? { truncated: true } : {}
2090
+ });
2041
2091
  return {
2042
2092
  size: st.size,
2043
2093
  mtime: st.mtime.toISOString()
@@ -2056,7 +2106,7 @@ async function listMemory(agentDir) {
2056
2106
  }
2057
2107
  const files = [];
2058
2108
  for (const d of dirents) {
2059
- if (!d.isFile() || d.name.endsWith(".tmp")) continue;
2109
+ if (!d.isFile() || d.name.endsWith(".tmp") || d.name.startsWith(".")) continue;
2060
2110
  try {
2061
2111
  const st = await promises.stat(join(dir, d.name));
2062
2112
  files.push({
@@ -2263,8 +2313,8 @@ var ManagedMemoryProvider = class {
2263
2313
  content: await readMemoryFile(this.dirFor(scope.agentId), path)
2264
2314
  };
2265
2315
  }
2266
- async write(scope, path, content, ifMatch) {
2267
- const { size, mtime } = await writeMemoryFile(this.dirFor(scope.agentId), path, content, ifMatch);
2316
+ async write(scope, path, content, ifMatch, source) {
2317
+ const { size, mtime } = await writeMemoryFile(this.dirFor(scope.agentId), path, content, ifMatch, source);
2268
2318
  return {
2269
2319
  ok: true,
2270
2320
  path,
@@ -2315,7 +2365,7 @@ var NativeMemoryProvider = class {
2315
2365
  async read(scope, path) {
2316
2366
  return nativeMemoryRead(this.dirFor(scope.agentId), this.runtimeFor(scope.agentId), path);
2317
2367
  }
2318
- async write(scope, path, content, ifMatch) {
2368
+ async write(scope, path, content, ifMatch, _source) {
2319
2369
  return nativeMemoryWrite(this.dirFor(scope.agentId), this.runtimeFor(scope.agentId), path, content, ifMatch);
2320
2370
  }
2321
2371
  };
@@ -2370,8 +2420,8 @@ var DispatchingMemoryProvider = class {
2370
2420
  read(scope, path) {
2371
2421
  return this.forAgent(scope.agentId).read(scope, path);
2372
2422
  }
2373
- write(scope, path, content, ifMatch) {
2374
- return this.forAgent(scope.agentId).write(scope, path, content, ifMatch);
2423
+ write(scope, path, content, ifMatch, source) {
2424
+ return this.forAgent(scope.agentId).write(scope, path, content, ifMatch, source);
2375
2425
  }
2376
2426
  /** The memory tools to inject for a specific agent (the daemon calls this in
2377
2427
  * `mcpServersFor` — native contributes none). Scope-carrying, unlike the
@@ -11506,4 +11556,4 @@ var AcpHost = class {
11506
11556
  //#endregion
11507
11557
  export { toolsForIntegrations as A, decodeRelayDaemonFrame as C, MAX_FRAME_BYTES as D, encode as E, cachedRuntimeVersions as M, resolveRuntimes as N, RESERVED_MCP_SERVER_NAME as O, buildRelayDaemonFrame as S, decodeEnvelope as T, writeMemoryFile as _, initGitInjection as a, RELAY_DAEMON_SUBPROTOCOL as b, simpleGit as c, memoryProviderFor as d, MemoryConflictError as f, resolveInMemoryDir as g, listMemory as h, prepareWorkspace as i, cachedRuntimeNames as j, MEMORY_TOOL_NAMES as k, createMemoryProvider as l, MemoryTooLargeError as m, installedRuntimes as n, probeGitVersion as o, MemoryPathError as p, prefetchWorkspace as r, sessionGitEnv as s, AcpHost as t, memoryKindOf as u, isFrame as v, buildEnvelope as w, RELAY_DAEMON_WS_PATH as x, normalizeGitUrl as y };
11508
11558
 
11509
- //# sourceMappingURL=acp-host-iKdfasdN.js.map
11559
+ //# sourceMappingURL=acp-host-Bhc0M4Pn.js.map