{"version":3,"file":"paths.mjs","sources":["../../../src/memory/paths.ts"],"sourcesContent":["/**\n * Autonomous memory — canonical path whitelist + tier utilities.\n *\n * Single source of truth for the 8 stable memory documents. Every write\n * goes through {@link assertWritablePath}; every reader can ask\n * {@link getTierForPath} what a row belongs to. Adding or removing a\n * path means editing this file — and exactly this file.\n *\n * ## Why a whitelist?\n *\n * Earlier upstream-faithful designs used date-keyed files\n * (`memory/YYYY-MM-DD.md`), which have three problems for a persistent\n * multi-user agent:\n *\n * 1. **Unbounded growth** — one row per day per agent per user, forever.\n * 2. **LLM routing ambiguity** — \"which date file do I read?\" has no\n *    good answer, so the model reads (and ranks against) all of them.\n * 3. **No natural deduplication** — the same fact written across three\n *    days returns three near-identical hits.\n *\n * With 8 stable canonical documents the LLM knows *exactly* where to\n * write (\"is this a preference? → `memory/user/preferences.md`\") and\n * each row accumulates via UPSERT instead of piling up new rows.\n *\n * ## Two tiers\n *\n * - **Agent tier** (`memory/agent/*`) — operational knowledge that\n *   every caller of the agent benefits from. Stored with `user_id = NULL`.\n *   Shared across all users of a collaborative agent; the only tier that\n *   exists for isolated/autonomous agents with no invoker.\n *\n * - **User tier** (`memory/user/*`) — per-invoker personalization.\n *   Stored with `user_id = <caller>`. Read path filters so User A never\n *   sees User B's rows, even for the same agent. Not writable unless the\n *   flush scope carries a non-empty `userId`.\n *\n * The tier of a path is a function of its prefix (`memory/agent/` vs\n * `memory/user/`), so adding a new document is one line here + the\n * constant array entry; no store or route code has to change.\n */\n\nimport { MEMORY_PATH_PREFIX } from './constants';\nimport type { MemoryScope } from './types';\n\n/** Tier discriminator — the two kinds of memory a row can belong to. */\nexport type MemoryTier = 'agent' | 'user';\n\n/** Every canonical document the flush phase is allowed to write. */\nexport interface MemoryPathDescriptor {\n  /** Full path including the `memory/` prefix. */\n  path: string;\n  /** Which tier the path lives under. */\n  tier: MemoryTier;\n  /** Short label shown in UI tier badges and the flush prompt rubric. */\n  label: string;\n  /** One-line description — fed to the LLM to make routing unambiguous. */\n  description: string;\n}\n\n/** Agent-tier documents — shared across all users of the agent. */\nexport const MEMORY_AGENT_PATHS: readonly MemoryPathDescriptor[] =\n  Object.freeze([\n    Object.freeze({\n      path: 'memory/agent/playbook.md',\n      tier: 'agent' as const,\n      label: 'Playbook',\n      description:\n        'Successful task patterns and workflows that have worked for this agent — ' +\n        'reusable recipes, known-good tool sequences, approaches that consistently ' +\n        'produce the right result.',\n    }),\n    Object.freeze({\n      path: 'memory/agent/pitfalls.md',\n      tier: 'agent' as const,\n      label: 'Pitfalls',\n      description:\n        'Tool failures, error recoveries, schema mistakes, and invalid argument ' +\n        'shapes the agent has seen — so the same mistake is not repeated on future ' +\n        'turns. One note per distinct failure mode.',\n    }),\n    Object.freeze({\n      path: 'memory/agent/domain.md',\n      tier: 'agent' as const,\n      label: 'Domain',\n      description:\n        'Durable facts about the systems, APIs, data models, and business rules ' +\n        'this agent operates on — things that are true independent of any one user ' +\n        'and that the agent repeatedly needs to know.',\n    }),\n    Object.freeze({\n      path: 'memory/agent/style.md',\n      tier: 'agent' as const,\n      label: 'Style',\n      description:\n        'Tone, formatting, and response-shape conventions the agent has converged ' +\n        'on across the whole user base. Do NOT write user-specific preferences here ' +\n        '(those belong under the user tier).',\n    }),\n  ]);\n\n/** User-tier documents — per-invoker, never shared across users. */\nexport const MEMORY_USER_PATHS: readonly MemoryPathDescriptor[] = Object.freeze(\n  [\n    Object.freeze({\n      path: 'memory/user/profile.md',\n      tier: 'user' as const,\n      label: 'Profile',\n      description:\n        \"This specific user's identity — name, role, team, sign-off name, \" +\n        'responsibilities, stable facts about who they are. Only facts ' +\n        'volunteered by the user themselves.',\n    }),\n    Object.freeze({\n      path: 'memory/user/preferences.md',\n      tier: 'user' as const,\n      label: 'Preferences',\n      description:\n        'How THIS user wants things done — preferred formats, verbosity, tone, ' +\n        'cadence, output length, language conventions. Explicit corrections they ' +\n        'have given count as preferences.',\n    }),\n    Object.freeze({\n      path: 'memory/user/projects.md',\n      tier: 'user' as const,\n      label: 'Projects',\n      description:\n        \"This user's current initiatives, ongoing work, deadlines, and active \" +\n        'project context. Include dates in the prose when relevant. Retire entries ' +\n        'by overwriting them on future flushes when the user indicates a project is done.',\n    }),\n    Object.freeze({\n      path: 'memory/user/references.md',\n      tier: 'user' as const,\n      label: 'References',\n      description:\n        'External systems, dashboards, accounts, links, channels, and resources ' +\n        \"THIS user has pointed the agent at — the 'where to look' pointers that \" +\n        'make later turns more efficient.',\n    }),\n  ]\n);\n\n/** All 8 canonical documents in display order: agent tier first, user tier second. */\nexport const MEMORY_ALL_PATHS: readonly MemoryPathDescriptor[] = Object.freeze([\n  ...MEMORY_AGENT_PATHS,\n  ...MEMORY_USER_PATHS,\n]);\n\n/** Fast O(1) lookup map: path → descriptor. */\nconst PATH_INDEX: ReadonlyMap<string, MemoryPathDescriptor> = new Map(\n  MEMORY_ALL_PATHS.map((p) => [p.path, p])\n);\n\n/** Set of all whitelisted paths — for `has()` lookups. */\nexport const MEMORY_WRITABLE_PATHS: ReadonlySet<string> = new Set(\n  MEMORY_ALL_PATHS.map((p) => p.path)\n);\n\n/** Returns the descriptor for a canonical path, or `undefined` if not on the whitelist. */\nexport function getPathDescriptor(\n  path: string\n): MemoryPathDescriptor | undefined {\n  return PATH_INDEX.get(path);\n}\n\n/**\n * Returns the tier a given path belongs to.\n *\n * For unknown paths (e.g. legacy date-keyed rows that predate this schema),\n * falls back to inspecting the `memory/agent/` or `memory/user/` prefix.\n * Anything that matches neither is classified as `agent` — it will surface\n * in the admin UI under the agent-tier header and stay read-only there.\n */\nexport function getTierForPath(path: string): MemoryTier {\n  const descriptor = PATH_INDEX.get(path);\n  if (descriptor) return descriptor.tier;\n  if (path.startsWith('memory/user/')) return 'user';\n  return 'agent';\n}\n\n/**\n * Paths that are legal to write **for this caller's scope**.\n *\n * If `scope.userId` is absent (isolated/autonomous agent), the user-tier\n * paths are dropped. This is the list the flush prompt renders into its\n * rubric — the LLM never sees paths it cannot write to.\n */\nexport function getWritablePathsForScope(\n  scope: Pick<MemoryScope, 'userId'>\n): readonly MemoryPathDescriptor[] {\n  if (scope.userId == null || scope.userId === '') {\n    return MEMORY_AGENT_PATHS;\n  }\n  return MEMORY_ALL_PATHS;\n}\n\n/**\n * Validates a write path against the whitelist AND the caller's scope.\n *\n * Throws with an actionable message when:\n * - the path is missing the `memory/` prefix\n * - the path is not on the 8-doc whitelist\n * - the path is user-tier but `scope.userId` is missing\n *\n * The store's append() is the single call site; other callers should\n * never hit this directly. Kept as a standalone function for testability.\n */\nexport function assertWritablePath(\n  path: string,\n  scope: Pick<MemoryScope, 'userId'>\n): MemoryPathDescriptor {\n  if (!path || !path.startsWith(MEMORY_PATH_PREFIX)) {\n    throw new Error(\n      `memory_append path must start with \"${MEMORY_PATH_PREFIX}\" (received \"${path}\")`\n    );\n  }\n  const descriptor = PATH_INDEX.get(path);\n  if (!descriptor) {\n    throw new Error(\n      `memory_append path \"${path}\" is not on the canonical whitelist. ` +\n        `Allowed: ${MEMORY_ALL_PATHS.map((p) => p.path).join(', ')}`\n    );\n  }\n  if (\n    descriptor.tier === 'user' &&\n    (scope.userId == null || scope.userId === '')\n  ) {\n    throw new Error(\n      `memory_append to user-tier path \"${path}\" requires a caller userId in scope, ` +\n        `but scope.userId is missing. This path is private to a specific user and ` +\n        `cannot be written from an isolated/autonomous context.`\n    );\n  }\n  return descriptor;\n}\n\n/**\n * Renders the whitelist as a compact bullet list, used inside the flush\n * prompt so the LLM sees the authoritative rubric at inference time.\n *\n * Output shape:\n *   - memory/agent/playbook.md [Playbook, shared] — <description>\n *   - memory/agent/pitfalls.md [Pitfalls, shared] — <description>\n *   ...\n *\n * The `[Label, shared|private]` tag tells the model whether the doc is\n * visible to all users (agent tier) or only to the caller (user tier).\n * Filtered to only the paths writable for the given scope.\n */\nexport function renderPathsRubric(scope: Pick<MemoryScope, 'userId'>): string {\n  const writable = getWritablePathsForScope(scope);\n  return writable\n    .map((p) => {\n      const visibility = p.tier === 'agent' ? 'shared' : 'private';\n      return `- ${p.path} [${p.label}, ${visibility}] — ${p.description}`;\n    })\n    .join('\\n');\n}\n"],"names":[],"mappings":"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"}