mixdog 1.2.6 → 1.3.0

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.
Files changed (246) hide show
  1. package/LICENSES/simple-icons-CC0-1.0.txt +30 -0
  2. package/NOTICE.md +19 -0
  3. package/package.json +4 -1
  4. package/scripts/check-model-recommendations.mjs +89 -0
  5. package/scripts/gate-local.mjs +7 -0
  6. package/scripts/lib/isolated-test-env.mjs +16 -0
  7. package/src/agents/reviewer/AGENT.md +4 -2
  8. package/src/agents/reviewer/agent.json +1 -1
  9. package/src/defaults/shipped-history.json +7 -0
  10. package/src/defaults/skills/browser-use/SKILL.md +5 -0
  11. package/src/defaults/skills/memory-management/SKILL.md +4 -1
  12. package/src/defaults/skills/setup/references/actions.md +11 -4
  13. package/src/defaults/skills/setup-guide/SKILL.md +175 -0
  14. package/src/defaults/skills/setup-guide/model-recommendations.json +333 -0
  15. package/src/lib/rules-builder.cjs +55 -6
  16. package/src/rules/agent/AGENT.md +4 -0
  17. package/src/rules/agent-mode/AGENT_MODE.md +24 -0
  18. package/src/rules/agent-mode/MIXY.md +21 -0
  19. package/src/rules/agent-mode/ROUTINE.md +6 -0
  20. package/src/rules/lead/LEAD.md +7 -3
  21. package/src/rules/shared/70-delivery.md +3 -0
  22. package/src/runtime/agent/orchestrator/agent-runtime/room-supervisor-completion.mjs +86 -0
  23. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +77 -14
  24. package/src/runtime/agent/orchestrator/context/role-instructions.mjs +182 -13
  25. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +4 -2
  26. package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +215 -0
  27. package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +5 -4
  28. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +10 -1
  29. package/src/runtime/agent/orchestrator/mcp/client.mjs +115 -11
  30. package/src/runtime/agent/orchestrator/mcp/oauth-account.mjs +97 -0
  31. package/src/runtime/agent/orchestrator/mcp/oauth-client.mjs +49 -0
  32. package/src/runtime/agent/orchestrator/mcp/oauth-credentials.mjs +59 -9
  33. package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +32 -7
  34. package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +45 -6
  35. package/src/runtime/agent/orchestrator/mcp/oauth.mjs +195 -6
  36. package/src/runtime/agent/orchestrator/mcp/server-connectors.mjs +17 -0
  37. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  38. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +1 -0
  39. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-login.mjs +10 -3
  40. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +4 -0
  41. package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
  42. package/src/runtime/agent/orchestrator/providers/openai-error-contract.mjs +17 -12
  43. package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +10 -0
  44. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +96 -1
  45. package/src/runtime/agent/orchestrator/runtime-core/context-status-shape.mjs +4 -0
  46. package/src/runtime/agent/orchestrator/runtime-core/extension-scopes.mjs +6 -4
  47. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +12 -0
  48. package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +1 -0
  49. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +12 -6
  50. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +32 -5
  51. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +10 -4
  52. package/src/runtime/agent/orchestrator/runtime-core/workflow/agents.mjs +4 -1
  53. package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +50 -20
  54. package/src/runtime/agent/orchestrator/runtime-core/workflow-ids.mjs +2 -0
  55. package/src/runtime/agent/orchestrator/session/agent-home.d.mts +4 -0
  56. package/src/runtime/agent/orchestrator/session/agent-home.mjs +33 -0
  57. package/src/runtime/agent/orchestrator/session/agent-mode.mjs +62 -0
  58. package/src/runtime/agent/orchestrator/session/agent-room-supervisor.d.mts +31 -0
  59. package/src/runtime/agent/orchestrator/session/agent-room-supervisor.mjs +241 -0
  60. package/src/runtime/agent/orchestrator/session/agent-room.d.mts +162 -0
  61. package/src/runtime/agent/orchestrator/session/agent-room.mjs +641 -0
  62. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +7 -1
  63. package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +2 -0
  64. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +6 -2
  65. package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +165 -0
  66. package/src/runtime/agent/orchestrator/session/human-gate/held-message.mjs +95 -0
  67. package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +221 -0
  68. package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +195 -0
  69. package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +167 -0
  70. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +3 -1
  71. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +10 -0
  72. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +16 -1
  73. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +74 -15
  74. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +9 -1
  75. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +5 -4
  76. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +13 -3
  77. package/src/runtime/agent/orchestrator/session/loop/usage.mjs +7 -0
  78. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +1 -0
  79. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +3 -1
  80. package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +6 -0
  81. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +2 -0
  82. package/src/runtime/agent/orchestrator/session/manager/pending-lifecycle-epoch.mjs +0 -8
  83. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +44 -16
  84. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +22 -5
  85. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +39 -4
  86. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +27 -5
  87. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +55 -8
  88. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +16 -0
  89. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +3 -2
  90. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +6 -0
  91. package/src/runtime/agent/orchestrator/session/manager/workflow-switch-notice.mjs +58 -0
  92. package/src/runtime/agent/orchestrator/session/mixy-name.mjs +24 -0
  93. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +32 -5
  94. package/src/runtime/agent/orchestrator/session/routine-result.d.mts +70 -0
  95. package/src/runtime/agent/orchestrator/session/routine-result.mjs +277 -0
  96. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-row.mjs +8 -0
  97. package/src/runtime/agent/orchestrator/session/store-record-strict-scan.mjs +28 -9
  98. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +16 -2
  99. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +44 -12
  100. package/src/runtime/agent/orchestrator/session/store-summary-last-message.mjs +46 -0
  101. package/src/runtime/agent/orchestrator/session/store-summary-visibility.mjs +0 -4
  102. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +44 -1
  103. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +12 -0
  104. package/src/runtime/agent/orchestrator/tools/builtin/native-search-client.mjs +113 -5
  105. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +4 -0
  106. package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +25 -1
  107. package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promote-job.mjs +8 -0
  108. package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +6 -0
  109. package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +3 -0
  110. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +75 -57
  111. package/src/runtime/browser-bridge/client.mjs +24 -0
  112. package/src/runtime/channels/lib/scheduler.mjs +40 -12
  113. package/src/runtime/channels/lib/schedules-db.mjs +71 -12
  114. package/src/runtime/channels/lib/webhook/session-dispatch.mjs +36 -16
  115. package/src/runtime/channels/lib/webhook.mjs +0 -1
  116. package/src/runtime/channels/lib/webhooks-db.mjs +100 -10
  117. package/src/runtime/channels/lib/worker-main.mjs +17 -20
  118. package/src/runtime/computer-bridge/client.mjs +28 -1
  119. package/src/runtime/connectors/gmail-app-password.mjs +690 -0
  120. package/src/runtime/connectors/google-mcp.mjs +446 -0
  121. package/src/runtime/memory/lib/core-memory-access.mjs +69 -0
  122. package/src/runtime/memory/lib/core-memory-file.mjs +16 -8
  123. package/src/runtime/memory/lib/core-memory-index.mjs +15 -9
  124. package/src/runtime/memory/lib/core-memory-management.mjs +10 -4
  125. package/src/runtime/memory/lib/core-memory-store.mjs +53 -30
  126. package/src/runtime/memory/lib/core-memory-uniqueness.mjs +10 -6
  127. package/src/runtime/memory/lib/http-router/session-routes.mjs +3 -2
  128. package/src/runtime/memory/lib/memory-action-handlers/core-actions.mjs +44 -14
  129. package/src/runtime/memory/lib/memory-schema/core-and-meta.mjs +11 -0
  130. package/src/runtime/memory/lib/memory.mjs +7 -1
  131. package/src/runtime/memory/lib/query-core-recall.mjs +9 -1
  132. package/src/runtime/memory/lib/query-hybrid-search.mjs +2 -0
  133. package/src/runtime/memory/lib/tool-call-handler.mjs +2 -0
  134. package/src/runtime/memory/tool-defs.mjs +5 -0
  135. package/src/runtime/shared/agent-avatar-sprites.mjs +445 -0
  136. package/src/runtime/shared/agent-avatar.d.mts +64 -0
  137. package/src/runtime/shared/agent-avatar.mjs +320 -0
  138. package/src/runtime/shared/agent-route-config.d.mts +3 -0
  139. package/src/runtime/shared/agent-route-config.mjs +16 -0
  140. package/src/runtime/shared/atomic-file.mjs +14 -1
  141. package/src/runtime/shared/config.mjs +8 -1
  142. package/src/runtime/shared/connector-accounts.mjs +15 -0
  143. package/src/runtime/shared/connector-catalog.d.mts +52 -0
  144. package/src/runtime/shared/connector-catalog.mjs +230 -0
  145. package/src/runtime/shared/connector-triggers.d.mts +3 -0
  146. package/src/runtime/shared/connector-triggers.mjs +43 -0
  147. package/src/runtime/shared/google-oauth-client.mjs +24 -0
  148. package/src/runtime/shared/held-request-host.d.mts +12 -0
  149. package/src/runtime/shared/held-request-host.mjs +71 -0
  150. package/src/runtime/shared/plain-preview.d.mts +1 -0
  151. package/src/runtime/shared/plain-preview.mjs +28 -0
  152. package/src/runtime/shared/routine-limits.d.mts +11 -0
  153. package/src/runtime/shared/routine-limits.mjs +21 -0
  154. package/src/runtime/shared/routine-trigger.d.mts +10 -0
  155. package/src/runtime/shared/routine-trigger.mjs +44 -0
  156. package/src/runtime/shared/schedule-model-ref.mjs +9 -10
  157. package/src/runtime/shared/slow-span.mjs +4 -12
  158. package/src/runtime/shared/statusline/statusline-segments.mjs +13 -1
  159. package/src/runtime/shared/transcript-metadata.mjs +10 -0
  160. package/src/runtime/shared/turn-changes.mjs +0 -8
  161. package/src/runtime/shared/usage-readout-totals.mjs +56 -0
  162. package/src/runtime/shared/user-data-guard.mjs +114 -36
  163. package/src/session-runtime/agent-home-delivery.mjs +459 -0
  164. package/src/session-runtime/agent-profile-changes.mjs +21 -0
  165. package/src/session-runtime/agent-room-delivery.mjs +92 -0
  166. package/src/session-runtime/agent-task-sessions.mjs +200 -0
  167. package/src/session-runtime/boot/apis.mjs +50 -18
  168. package/src/session-runtime/boot/facade.mjs +5 -8
  169. package/src/session-runtime/boot/infrastructure.mjs +28 -17
  170. package/src/session-runtime/boot/tools.mjs +3 -0
  171. package/src/session-runtime/channel-config-api.mjs +53 -5
  172. package/src/session-runtime/cwd-plugins/core-memory-context.mjs +36 -20
  173. package/src/session-runtime/cwd-plugins/plugin-status.mjs +6 -1
  174. package/src/session-runtime/held-requests.mjs +228 -0
  175. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +121 -0
  176. package/src/session-runtime/internal-tool-executor/feature-tools.mjs +5 -1
  177. package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +31 -2
  178. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +31 -2
  179. package/src/session-runtime/internal-tool-executor.mjs +2 -0
  180. package/src/session-runtime/lifecycle/session-catalog.mjs +9 -0
  181. package/src/session-runtime/lifecycle/session-switch/resume.mjs +4 -0
  182. package/src/session-runtime/lifecycle/shared.mjs +6 -0
  183. package/src/session-runtime/mcp-glue/server-catalog.mjs +8 -0
  184. package/src/session-runtime/mcp-glue/server-input.mjs +3 -0
  185. package/src/session-runtime/mcp-oauth-login.mjs +112 -15
  186. package/src/session-runtime/model-route/set-route.mjs +14 -3
  187. package/src/session-runtime/owned-automation-run.mjs +229 -0
  188. package/src/session-runtime/resource-api.mjs +5 -0
  189. package/src/session-runtime/resource-mcp-api.mjs +107 -13
  190. package/src/session-runtime/resource-skills-api.mjs +12 -0
  191. package/src/session-runtime/room-member-gate.mjs +48 -0
  192. package/src/session-runtime/routine-home-turn.mjs +156 -0
  193. package/src/session-runtime/routine-owner.mjs +48 -0
  194. package/src/session-runtime/schedule-session-run.mjs +30 -64
  195. package/src/session-runtime/services/agent-tool/helpers.mjs +10 -0
  196. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +19 -4
  197. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +2 -0
  198. package/src/session-runtime/services/channel-admin.mjs +93 -24
  199. package/src/session-runtime/services/memory-access.mjs +37 -0
  200. package/src/session-runtime/services/provider-admin.mjs +74 -39
  201. package/src/session-runtime/services/schedule-access.mjs +91 -0
  202. package/src/session-runtime/session-create/session-options.mjs +34 -7
  203. package/src/session-runtime/session-hooks.mjs +16 -8
  204. package/src/session-runtime/setup-tool/executor.mjs +68 -6
  205. package/src/session-runtime/setup-tool/extended-actions.mjs +72 -14
  206. package/src/session-runtime/setup-tool/settings-contract.mjs +8 -6
  207. package/src/session-runtime/setup-tool/tool-defs.mjs +8 -0
  208. package/src/session-runtime/skills-api.mjs +16 -1
  209. package/src/session-runtime/tool-policy-refresh.mjs +238 -29
  210. package/src/session-runtime/turn/session-ops.mjs +8 -1
  211. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +10 -2
  212. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +3 -0
  213. package/src/session-runtime/webhook-session-run.mjs +49 -67
  214. package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +239 -0
  215. package/src/session-runtime/workflow-agents-api/agent-editor.mjs +556 -22
  216. package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +11 -1
  217. package/src/session-runtime/workflow-agents-api.mjs +4 -1
  218. package/src/standalone/daemon-agent-control.mjs +1 -8
  219. package/src/standalone/daemon-session-control.mjs +11 -2
  220. package/src/standalone/daemon.mjs +18 -4
  221. package/src/standalone/session-protocol.mjs +27 -0
  222. package/src/standalone/session-service/session-calls/session-turns.mjs +9 -0
  223. package/src/standalone/session-service.mjs +19 -0
  224. package/src/tui/app/slash-commands.mjs +20 -0
  225. package/src/tui/dist/index.mjs +164 -86
  226. package/src/tui/session/context-state.mjs +6 -1
  227. package/src/tui/session/notice-surface.mjs +35 -4
  228. package/src/tui/session/queue-helpers.mjs +2 -1
  229. package/src/tui/session/session-api/extensions.mjs +4 -0
  230. package/src/tui/session/session-api/intake/submission.mjs +5 -0
  231. package/src/tui/session/session-api/integrations.mjs +8 -0
  232. package/src/tui/session/session-api/lifecycle/session-reset.mjs +13 -3
  233. package/src/tui/session/session-api/lifecycle.mjs +17 -1
  234. package/src/tui/session/session-api/routes.mjs +48 -3
  235. package/src/tui/session/session-flow/drain.mjs +12 -3
  236. package/src/tui/session/session-flow/queue/entry-shape.mjs +2 -1
  237. package/src/tui/session/session-flow/queue/take-entries.mjs +3 -2
  238. package/src/tui/session/session-flow/steering.mjs +58 -24
  239. package/src/tui/session/tool-approval.mjs +26 -5
  240. package/src/tui/session/transcript-intake.mjs +6 -2
  241. package/src/tui/session/transcript-spill.mjs +5 -1
  242. package/src/tui/session/tui-steering-persist.mjs +65 -42
  243. package/src/tui/session/turn-live-call.mjs +56 -0
  244. package/src/tui/session/turn.mjs +22 -6
  245. package/src/session-runtime/automation-agents.mjs +0 -94
  246. package/src/session-runtime/automation-workflow.mjs +0 -59
@@ -2,6 +2,7 @@
2
2
  // through recall, not through a second instruction-management namespace.
3
3
  import { indexedCoreRecord, syncCoreMemoryIndexes } from './core-memory-index.mjs';
4
4
  import { resolveProjectScope } from './project-id-resolver.mjs';
5
+ import { corePoolLabel } from './core-memory-access.mjs';
5
6
 
6
7
  export async function listManagedMemories(db, scope, options = {}) {
7
8
  const includeInactive = options.include_inactive ?? false;
@@ -27,20 +28,24 @@ export async function listManagedMemories(db, scope, options = {}) {
27
28
  state.recordIds.map((id, index) => `WHEN ${Number(id)} THEN ${index + 1}`)
28
29
  );
29
30
  const publicOrder = indexCases.length ? `CASE id ${indexCases.join(' ')} ELSE id END` : 'id';
31
+ // `agent_id` lists that agent's own pool alone; otherwise agent pools are
32
+ // never part of a common/project listing.
33
+ const agentId = typeof options.agent_id === 'string' && options.agent_id ? options.agent_id : null;
30
34
  const result = await db.query(
31
35
  `
32
36
  SELECT * FROM (
33
- SELECT 'curated'::text AS source, c.id, c.project_id, c.element, c.summary,
37
+ SELECT 'curated'::text AS source, c.id, c.project_id, c.agent_id, c.element, c.summary,
34
38
  COALESCE(c.status, 'active')::text AS status,
35
39
  (c.status IS NULL OR c.status = 'active') AS injection_enabled
36
40
  FROM core_entries c
37
41
  ) memories
38
- WHERE ($1::text = '*' OR project_id = $1 OR (project_id IS NULL AND (NOT $5::boolean OR $1::text IS NULL)))
42
+ WHERE (agent_id = $6::text OR ($6::text IS NULL AND agent_id IS NULL
43
+ AND ($1::text = '*' OR project_id = $1 OR (project_id IS NULL AND (NOT $5::boolean OR $1::text IS NULL)))))
39
44
  AND ($4::boolean OR injection_enabled)
40
45
  ORDER BY project_id NULLS FIRST, ${publicOrder} ASC, id ASC
41
46
  LIMIT $2 OFFSET $3
42
47
  `,
43
- [scope, limit + 1, offset, includeInactive, options.scope_only === true]
48
+ [scope, limit + 1, offset, includeInactive, options.scope_only === true, agentId]
44
49
  );
45
50
  const hasMore = result.rows.length > limit;
46
51
  const entries = result.rows.slice(0, limit).map((row) => indexedCoreRecord(row, directory));
@@ -54,7 +59,8 @@ export function formatManagedMemories(page) {
54
59
  const lines = page.entries.map((row) => {
55
60
  const id = row.id ? `id=${row.id}` : `archived_id=${row.record_id}`;
56
61
  const version = row.index_revision ? ` index_revision=${row.index_revision}` : '';
57
- return `${id} source=curated project=${row.project_id ?? 'COMMON'} status=${row.status} injection=${row.injection_enabled ? 'enabled' : 'disabled'}${version} ${row.element ?? ''} — ${row.summary ?? ''}`;
62
+ const pool = corePoolLabel({ projectId: row.project_id, agentId: row.agent_id });
63
+ return `${id} source=curated ${pool} status=${row.status} injection=${row.injection_enabled ? 'enabled' : 'disabled'}${version} ${row.element ?? ''} — ${row.summary ?? ''}`;
58
64
  });
59
65
  if (page.nextOffset !== null)
60
66
  lines.push(`More memories: call list with offset=${page.nextOffset} and the same project scope/include_inactive.`);
@@ -1,6 +1,7 @@
1
-
2
1
  // User-curated core memory store — native PG-backed via core_entries table.
3
- // Per-project entries distinguished by project_id column (NULL = COMMON).
2
+ // Per-project entries distinguished by project_id column (NULL = COMMON); an
3
+ // agent's own pool by agent_id (its project_id stays NULL). Every mutation
4
+ // checks its caller against the pool (core-memory-access.mjs).
4
5
  // Explicit add/edit/delete operations affect only the requested entry.
5
6
  // Generated conversation history never enters this store automatically.
6
7
 
@@ -10,6 +11,11 @@ import { getDatabase } from './memory.mjs';
10
11
  import { VALID_CATEGORY } from './memory-categories.mjs';
11
12
  import { checkedConnect } from './pg/adapter.mjs';
12
13
  import { findCoreKeyRows } from './core-memory-uniqueness.mjs';
14
+ import { assertCoreWrite, corePoolLabel, normalizeMemoryCaller } from './core-memory-access.mjs';
15
+
16
+ // The advisory-lock key of one pool.
17
+ const poolLockKey = (projectId, agentId) =>
18
+ agentId ? `core:agent:${agentId}` : `core:${projectId == null ? 'COMMON' : projectId}`;
13
19
 
14
20
  const CORE_ELEMENT_DERIVE_LENGTH = 40;
15
21
 
@@ -74,7 +80,6 @@ function _getDb(dataDir) {
74
80
  return db;
75
81
  }
76
82
 
77
-
78
83
  // NOTE: boot-time core id compaction was REMOVED. Resequencing core_entries.id
79
84
  // to 1..N rewrote primary keys that are handed out as stable `core:N`
80
85
  // references, so after a restart a retained reference pointed at a different
@@ -83,7 +88,7 @@ function _getDb(dataDir) {
83
88
 
84
89
  export async function listCore(dataDir, projectId = null) {
85
90
  const db = _getDb(dataDir);
86
- const cols = `id, element, summary, category, project_id, created_at, updated_at`;
91
+ const cols = `id, element, summary, category, project_id, agent_id, created_at, updated_at`;
87
92
  // Only live cores enter recall/review — archived (superseded) rows are
88
93
  // retired but retained for audit. Legacy NULL status = active.
89
94
  const live = `(status IS NULL OR status = 'active')`;
@@ -92,7 +97,9 @@ export async function listCore(dataDir, projectId = null) {
92
97
  return r.rows;
93
98
  }
94
99
  if (projectId === null) {
95
- const r = await db.query(`SELECT ${cols} FROM core_entries WHERE project_id IS NULL AND ${live} ORDER BY id ASC`);
100
+ const r = await db.query(
101
+ `SELECT ${cols} FROM core_entries WHERE project_id IS NULL AND agent_id IS NULL AND ${live} ORDER BY id ASC`
102
+ );
96
103
  return r.rows;
97
104
  }
98
105
  const r = await db.query(`SELECT ${cols} FROM core_entries WHERE project_id = $1 AND ${live} ORDER BY id ASC`, [
@@ -110,8 +117,10 @@ function serializeCoreMutation(fn) {
110
117
  return run;
111
118
  }
112
119
 
113
- export function addCore(dataDir, input, projectId) {
114
- return serializeCoreMutation(() => _addCoreImpl(dataDir, input, projectId));
120
+ /** `options`: `agentId` selects that agent's pool (projectId must be null);
121
+ * `caller` is the normalized-or-raw memory caller (absent: the host). */
122
+ export function addCore(dataDir, input, projectId, options = {}) {
123
+ return serializeCoreMutation(() => _addCoreImpl(dataDir, input, projectId, options));
115
124
  }
116
125
  export function editCore(dataDir, id, patch) {
117
126
  return serializeCoreMutation(() => _editCoreImpl(dataDir, id, patch));
@@ -120,9 +129,12 @@ export function deleteCore(dataDir, id, options = {}) {
120
129
  return serializeCoreMutation(() => _deleteCoreImpl(dataDir, id, options));
121
130
  }
122
131
 
123
- async function _addCoreImpl(dataDir, input, projectId) {
132
+ async function _addCoreImpl(dataDir, input, projectId, { agentId = null, caller } = {}) {
124
133
  if (projectId === undefined)
125
134
  throw new Error('addCore: projectId required — pass null for COMMON pool, or slug string for scoped pool');
135
+ if (agentId && projectId !== null) throw new Error('agent memory has no project scope');
136
+ const pool = { projectId, agentId: agentId || null };
137
+ assertCoreWrite(normalizeMemoryCaller(caller), 'add', pool);
126
138
  const {
127
139
  element: el,
128
140
  summary: sm,
@@ -141,30 +153,29 @@ async function _addCoreImpl(dataDir, input, projectId) {
141
153
  try {
142
154
  await client.query('BEGIN');
143
155
  await client.query(`SET LOCAL lock_timeout = '5s'`);
144
- const poolKey = `core:${projectId == null ? 'COMMON' : projectId}`;
145
- await client.query(`SELECT pg_advisory_xact_lock(hashtext($1))`, [poolKey]);
146
- const collisions = await findCoreKeyRows(client, projectId, el);
156
+ await client.query(`SELECT pg_advisory_xact_lock(hashtext($1))`, [poolLockKey(projectId, pool.agentId)]);
157
+ const collisions = await findCoreKeyRows(client, projectId, el, null, pool.agentId);
147
158
  if (collisions.length) {
148
159
  if (collisions.length !== 1 || collisions[0].status !== 'archived') {
149
160
  throw new Error(
150
- `core entry already exists: project=${projectId ?? 'COMMON'} element=${JSON.stringify(el.slice(0, 200))}; use an explicit edit/delete`
161
+ `core entry already exists: ${corePoolLabel(pool)} element=${JSON.stringify(el.slice(0, 200))}; use an explicit edit/delete`
151
162
  );
152
163
  }
153
164
  const revived = await client.query(
154
165
  `UPDATE core_entries SET summary = $1, category = $2,
155
166
  status = 'active', archived_at = NULL, updated_at = $3
156
167
  WHERE id = $4
157
- RETURNING id, element, summary, category, project_id, created_at, updated_at`,
168
+ RETURNING id, element, summary, category, project_id, agent_id, created_at, updated_at`,
158
169
  [sm, cat, now, collisions[0].id]
159
170
  );
160
171
  await client.query('COMMIT');
161
172
  return { ...revived.rows[0], revived_from_archived: true };
162
173
  }
163
174
  const r = await client.query(
164
- `INSERT INTO core_entries(element, summary, category, project_id, created_at, updated_at)
165
- VALUES ($1, $2, $3, $4, $5, $6)
166
- RETURNING id, element, summary, category, project_id, created_at, updated_at`,
167
- [el, sm, cat, projectId, now, now]
175
+ `INSERT INTO core_entries(element, summary, category, project_id, agent_id, created_at, updated_at)
176
+ VALUES ($1, $2, $3, $4, $5, $6, $7)
177
+ RETURNING id, element, summary, category, project_id, agent_id, created_at, updated_at`,
178
+ [el, sm, cat, projectId, pool.agentId, now, now]
168
179
  );
169
180
  await client.query('COMMIT');
170
181
  return r.rows[0];
@@ -187,6 +198,10 @@ async function _editCoreImpl(dataDir, id, patch) {
187
198
  if (Object.hasOwn(patch, 'expectedProjectId') && (patch.expectedProjectId ?? null) !== (cur.project_id ?? null)) {
188
199
  throw new Error(`entry id=${numId} is not in project ${patch.expectedProjectId ?? 'COMMON'}`);
189
200
  }
201
+ if (Object.hasOwn(patch, 'expectedAgentId') && (patch.expectedAgentId ?? null) !== (cur.agent_id ?? null)) {
202
+ throw new Error(`entry id=${numId} is not in ${corePoolLabel({ agentId: patch.expectedAgentId })}`);
203
+ }
204
+ const agentId = cur.agent_id ?? null;
190
205
  const incoming = normalizeCoreInput(patch);
191
206
  const newElement = incoming.element ?? cur.element;
192
207
  const newSummary = incoming.summary ?? cur.summary;
@@ -194,6 +209,10 @@ async function _editCoreImpl(dataDir, id, patch) {
194
209
  const newProjectId = Object.hasOwn(patch, 'targetProjectId')
195
210
  ? (patch.targetProjectId ?? null)
196
211
  : (cur.project_id ?? null);
212
+ if (agentId && newProjectId !== null) throw new Error('agent memory has no project scope');
213
+ const caller = normalizeMemoryCaller(patch.caller);
214
+ assertCoreWrite(caller, 'edit', { projectId: cur.project_id ?? null, agentId });
215
+ if (newProjectId !== (cur.project_id ?? null)) assertCoreWrite(caller, 'edit', { projectId: newProjectId, agentId });
197
216
  const { errors } = normalizeCoreInput(
198
217
  {
199
218
  element: newElement,
@@ -237,10 +256,7 @@ async function _editCoreImpl(dataDir, id, patch) {
237
256
  try {
238
257
  await client.query('BEGIN');
239
258
  await client.query(`SET LOCAL lock_timeout = '5s'`);
240
- const poolKeys = [
241
- `core:${cur.project_id == null ? 'COMMON' : cur.project_id}`,
242
- `core:${newProjectId == null ? 'COMMON' : newProjectId}`,
243
- ].sort();
259
+ const poolKeys = [poolLockKey(cur.project_id, agentId), poolLockKey(newProjectId, agentId)].sort();
244
260
  for (const poolKey of new Set(poolKeys)) {
245
261
  await client.query(`SELECT pg_advisory_xact_lock(hashtext($1))`, [poolKey]);
246
262
  }
@@ -254,14 +270,15 @@ async function _editCoreImpl(dataDir, id, patch) {
254
270
  fresh.summary !== cur.summary ||
255
271
  fresh.category !== cur.category ||
256
272
  (fresh.project_id ?? null) !== (cur.project_id ?? null) ||
273
+ (fresh.agent_id ?? null) !== agentId ||
257
274
  Number(fresh.updated_at ?? 0) !== Number(cur.updated_at ?? 0)
258
275
  ) {
259
276
  throw new Error(`core entry id=${numId} changed concurrently — re-read and retry`);
260
277
  }
261
278
  if (newElement !== cur.element || projectChanged) {
262
- if ((await findCoreKeyRows(client, newProjectId, newElement, numId)).length) {
279
+ if ((await findCoreKeyRows(client, newProjectId, newElement, numId, agentId)).length) {
263
280
  throw new Error(
264
- `core entry already exists: project=${newProjectId ?? 'COMMON'} element=${JSON.stringify(newElement.slice(0, 200))}`
281
+ `core entry already exists: ${corePoolLabel({ projectId: newProjectId, agentId })} element=${JSON.stringify(newElement.slice(0, 200))}`
265
282
  );
266
283
  }
267
284
  }
@@ -294,13 +311,19 @@ async function _deleteCoreImpl(dataDir, id, options = {}) {
294
311
  const numId = Number(id);
295
312
  if (!Number.isInteger(numId) || numId <= 0) throw new Error('integer id > 0 required');
296
313
  const db = _getDb(dataDir);
297
- const scoped = Object.hasOwn(options, 'expectedProjectId');
298
- const r = scoped
299
- ? await db.query(`DELETE FROM core_entries WHERE id = $1 AND project_id IS NOT DISTINCT FROM $2 RETURNING *`, [
300
- numId,
301
- options.expectedProjectId,
302
- ])
303
- : await db.query(`DELETE FROM core_entries WHERE id = $1 RETURNING *`, [numId]);
314
+ const cur = (await db.query(`SELECT project_id, agent_id FROM core_entries WHERE id = $1`, [numId])).rows[0];
315
+ const pool = { projectId: cur?.project_id ?? null, agentId: cur?.agent_id ?? null };
316
+ const expected = (key, value) => !Object.hasOwn(options, key) || (options[key] ?? null) === value;
317
+ if (!cur || !expected('expectedProjectId', pool.projectId) || !expected('expectedAgentId', pool.agentId)) {
318
+ throw new Error(`no entry with id=${numId}`);
319
+ }
320
+ assertCoreWrite(normalizeMemoryCaller(options.caller), 'delete', pool);
321
+ // The pool just authorized: a concurrent move out of it deletes nothing.
322
+ const r = await db.query(
323
+ `DELETE FROM core_entries WHERE id = $1
324
+ AND project_id IS NOT DISTINCT FROM $2 AND agent_id IS NOT DISTINCT FROM $3 RETURNING *`,
325
+ [numId, pool.projectId, pool.agentId]
326
+ );
304
327
  if (r.rows.length === 0) throw new Error(`no entry with id=${numId}`);
305
328
  return r.rows[0];
306
329
  }
@@ -2,10 +2,13 @@ import { __mixdogMemoryLog } from './memory-log.mjs';
2
2
 
3
3
  // Legacy conflicting keys remain visible and editable. Never choose a winner
4
4
  // or delete user-curated content just to install a constraint during startup.
5
+ // The key is the pool (project_id, agent_id) plus element: an agent may keep
6
+ // the same element as common memory. It replaced the (project_id, element)
7
+ // index, which is dropped once the pool key is in place.
5
8
  export async function ensureCoreKeyIndex(db) {
6
9
  const conflicts = await db.query(`
7
- SELECT project_id, element, COUNT(*) AS count
8
- FROM core_entries GROUP BY project_id, element HAVING COUNT(*) > 1
10
+ SELECT project_id, agent_id, element, COUNT(*) AS count
11
+ FROM core_entries GROUP BY project_id, agent_id, element HAVING COUNT(*) > 1
9
12
  LIMIT 1
10
13
  `);
11
14
  if (conflicts.rows.length) {
@@ -16,8 +19,9 @@ export async function ensureCoreKeyIndex(db) {
16
19
  }
17
20
  try {
18
21
  await db.exec(
19
- `CREATE UNIQUE INDEX IF NOT EXISTS core_entries_unique_proj_elem ON core_entries (project_id, element) NULLS NOT DISTINCT`
22
+ `CREATE UNIQUE INDEX IF NOT EXISTS core_entries_unique_pool_elem ON core_entries (project_id, agent_id, element) NULLS NOT DISTINCT`
20
23
  );
24
+ await db.exec(`DROP INDEX IF EXISTS core_entries_unique_proj_elem`);
21
25
  return true;
22
26
  } catch (error) {
23
27
  if (error.code !== '23505') throw error;
@@ -30,15 +34,15 @@ export async function ensureCoreKeyIndex(db) {
30
34
 
31
35
  // Called under the store's per-pool advisory lock. This also protects writes
32
36
  // while a legacy database still has conflicts and cannot install the index.
33
- export async function findCoreKeyRows(db, projectId, element, exceptId = null) {
37
+ export async function findCoreKeyRows(db, projectId, element, exceptId = null, agentId = null) {
34
38
  const result = await db.query(
35
39
  `
36
40
  SELECT id, status FROM core_entries
37
- WHERE project_id IS NOT DISTINCT FROM $1 AND element = $2
41
+ WHERE project_id IS NOT DISTINCT FROM $1 AND agent_id IS NOT DISTINCT FROM $4 AND element = $2
38
42
  AND ($3::bigint IS NULL OR id <> $3)
39
43
  ORDER BY id LIMIT 2 FOR UPDATE
40
44
  `,
41
- [projectId, element, exceptId]
45
+ [projectId, element, exceptId, agentId]
42
46
  );
43
47
  return result.rows;
44
48
  }
@@ -29,8 +29,9 @@ function prewarmRecallEmbedding(log, reason = 'session-start') {
29
29
  }
30
30
  }
31
31
 
32
- const ACTIVE_CORE_ROWS = `SELECT id, summary FROM core_entries WHERE project_id IS NULL AND (status IS NULL OR status = 'active') ORDER BY id ASC`;
33
- const SCOPED_CORE_ROWS = `SELECT id, summary FROM core_entries WHERE project_id = $1 AND (status IS NULL OR status = 'active') ORDER BY id ASC`;
32
+ // Agent pools never ride this payload (core-memory-access.mjs).
33
+ const ACTIVE_CORE_ROWS = `SELECT id, summary FROM core_entries WHERE project_id IS NULL AND agent_id IS NULL AND (status IS NULL OR status = 'active') ORDER BY id ASC`;
34
+ const SCOPED_CORE_ROWS = `SELECT id, summary FROM core_entries WHERE project_id = $1 AND agent_id IS NULL AND (status IS NULL OR status = 'active') ORDER BY id ASC`;
34
35
 
35
36
  export function createSessionRoutes({ getDb, log }) {
36
37
  async function buildSessionCoreMemoryPayload(cwd) {
@@ -1,13 +1,16 @@
1
1
  /**
2
2
  * memory-action-handlers/core-actions.mjs — `core`: user-curated core memory
3
3
  * add / edit / delete / list against the core store, with the
4
- * session-injection snapshot republished after every mutation.
4
+ * session-injection snapshot republished after every mutation. The caller
5
+ * (`memory_caller`) decides which pools it may read and write
6
+ * (core-memory-access.mjs); the store enforces the writes again.
5
7
  */
6
8
  import { addCore, editCore, deleteCore, normalizeCoreInput, normalizeCoreOp } from '../core-memory-store.mjs';
7
9
  import { resolveProjectScope } from '../project-id-resolver.mjs';
8
10
  import { resolvePluginData } from '../../../shared/plugin-paths.mjs';
9
11
  import { listManagedMemories, formatManagedMemories } from '../core-memory-management.mjs';
10
12
  import { publicCoreMemoryIdentity, resolveCoreMemoryIndex } from '../core-memory-index.mjs';
13
+ import { assertCoreRead, corePoolLabel, normalizeMemoryCaller, validAgentId } from '../core-memory-access.mjs';
11
14
 
12
15
  const CORE_OPS = ['add', 'edit', 'delete', 'list'];
13
16
 
@@ -47,6 +50,20 @@ function normalizedWriteArgs(args, op, projectId) {
47
50
  };
48
51
  }
49
52
 
53
+ // `agent_id` selects an agent's own pool instead of a project: "self" is the
54
+ // caller's, another id the pool Mixy or the host manages.
55
+ function coreAgentId(args, caller) {
56
+ const raw = typeof args.agent_id === 'string' ? args.agent_id.trim() : '';
57
+ if (!raw) return null;
58
+ if (raw.toLowerCase() === 'self') {
59
+ if (!caller.agentId) throw new Error('this session has no agent memory');
60
+ return caller.agentId;
61
+ }
62
+ const agentId = validAgentId(raw);
63
+ if (!agentId) throw new Error(`agent_id "${raw}" is not an agent id`);
64
+ return agentId;
65
+ }
66
+
50
67
  function editTargetProjectId(args, projectId) {
51
68
  if (Object.hasOwn(args, 'target_project_id')) {
52
69
  const value = String(args.target_project_id ?? '').trim();
@@ -67,33 +84,44 @@ export function createCoreActions({
67
84
  const describe = async (entry) =>
68
85
  `(${await publicCoreMemoryIdentity(getDb(), entry)}): ${entry.element} — ${entry.summary.slice(0, 200)}`;
69
86
 
87
+ // `pool` is `{ projectId, agentId }`; an agent pool's projectId is null.
70
88
  const ops = {
71
- async list(_coreDataDir, args, projectId) {
72
- const page = await listManagedMemories(getDb(), projectId, args);
89
+ async list(_coreDataDir, args, { projectId, agentId }, caller) {
90
+ assertCoreRead(caller, agentId);
91
+ const page = await listManagedMemories(getDb(), projectId, { ...args, agent_id: agentId });
73
92
  return { text: args.format === 'json' ? JSON.stringify(page) : formatManagedMemories(page), ...page };
74
93
  },
75
- async add(coreDataDir, args, projectId) {
76
- const entry = await addCoreImpl(coreDataDir, args, projectId);
94
+ async add(coreDataDir, args, { projectId, agentId }, caller) {
95
+ const entry = await addCoreImpl(coreDataDir, args, projectId, { agentId, caller });
77
96
  await refreshCoreMemoryFile('core-add');
78
97
  return { text: `core added ${await describe(entry)}` };
79
98
  },
80
- async edit(coreDataDir, args, projectId) {
81
- const targetProjectId = editTargetProjectId(args, projectId);
82
- const recordId = await resolveCoreMemoryIndex(getDb(), projectId, args.id, args.index_revision);
99
+ async edit(coreDataDir, args, { projectId, agentId }, caller) {
100
+ if (agentId && (Object.hasOwn(args, 'target_project_id') || args.target_cwd)) {
101
+ throw new Error('agent memory has no project scope');
102
+ }
103
+ const targetProjectId = agentId ? null : editTargetProjectId(args, projectId);
104
+ const recordId = await resolveCoreMemoryIndex(getDb(), projectId, args.id, args.index_revision, agentId);
83
105
  const entry = await editCoreImpl(coreDataDir, recordId, {
84
106
  ...args,
85
107
  expectedProjectId: projectId,
108
+ expectedAgentId: agentId,
86
109
  targetProjectId,
110
+ caller,
87
111
  });
88
112
  await refreshCoreMemoryFile('core-edit');
89
113
  return { text: `core edited ${await describe(entry)}` };
90
114
  },
91
- async delete(coreDataDir, args, projectId) {
92
- const recordId = await resolveCoreMemoryIndex(getDb(), projectId, args.id, args.index_revision);
93
- const removed = await deleteCoreImpl(coreDataDir, recordId, { expectedProjectId: projectId });
115
+ async delete(coreDataDir, args, { projectId, agentId }, caller) {
116
+ const recordId = await resolveCoreMemoryIndex(getDb(), projectId, args.id, args.index_revision, agentId);
117
+ const removed = await deleteCoreImpl(coreDataDir, recordId, {
118
+ expectedProjectId: projectId,
119
+ expectedAgentId: agentId,
120
+ caller,
121
+ });
94
122
  await refreshCoreMemoryFile('core-delete');
95
123
  return {
96
- text: `core deleted (project=${projectId ?? 'COMMON'} id=${args.id}): ${removed.element}. Remaining indices were compacted; list memories before the next write.`,
124
+ text: `core deleted (${corePoolLabel({ projectId, agentId })} id=${args.id}): ${removed.element}. Remaining indices were compacted; list memories before the next write.`,
97
125
  };
98
126
  },
99
127
  };
@@ -106,8 +134,10 @@ export function createCoreActions({
106
134
  }
107
135
  const coreDataDir = typeof dataDir === 'string' ? dataDir : resolvePluginData();
108
136
  if (!coreDataDir) return { text: 'core: memory data dir is not initialized', isError: true };
109
- const projectId = coreProjectId(args);
137
+ const caller = normalizeMemoryCaller(args.memory_caller);
110
138
  try {
139
+ const agentId = coreAgentId(args, caller);
140
+ const projectId = agentId ? null : coreProjectId(args);
111
141
  if (args.source && args.source !== 'curated') {
112
142
  return { text: 'memory manages user-curated entries only; use recall for generated history.', isError: true };
113
143
  }
@@ -119,7 +149,7 @@ export function createCoreActions({
119
149
  if (projectId === '*' && op !== 'list') {
120
150
  return { text: `core ${op}: project_id "*" only valid for op="list"`, isError: true };
121
151
  }
122
- return await ops[op](coreDataDir, args, projectId);
152
+ return await ops[op](coreDataDir, args, { projectId, agentId }, caller);
123
153
  } catch (e) {
124
154
  return { text: `core ${op} failed: ${e.message}`, isError: true };
125
155
  }
@@ -21,9 +21,20 @@ export async function ensureCoreEntriesSchema(db) {
21
21
  )
22
22
  `);
23
23
  await db.exec(`CREATE INDEX IF NOT EXISTS core_entries_project_idx ON core_entries(project_id)`);
24
+ await ensureCoreAgentColumn(db);
24
25
  await ensureCoreKeyIndex(db);
25
26
  }
26
27
 
28
+ // An agent's own pool (core-memory-access.mjs): agent_id names it and its
29
+ // project_id stays NULL. Existing rows keep NULL, i.e. common/project memory.
30
+ // Idempotent; runs on every boot before the pool key index.
31
+ export async function ensureCoreAgentColumn(db) {
32
+ await db.exec(`ALTER TABLE core_entries ADD COLUMN IF NOT EXISTS agent_id TEXT`);
33
+ await db.exec(
34
+ `CREATE INDEX IF NOT EXISTS core_entries_agent_idx ON core_entries(agent_id) WHERE agent_id IS NOT NULL`
35
+ );
36
+ }
37
+
27
38
  export async function ensureMetaSchema(db) {
28
39
  await db.exec(`
29
40
  CREATE TABLE IF NOT EXISTS meta (
@@ -15,7 +15,12 @@ import { ensureCoreKeyIndex } from './core-memory-uniqueness.mjs';
15
15
  import { ensureScoreSchema } from './memory-schema/score-params.mjs';
16
16
  import { ensureEntriesSchema } from './memory-schema/entries.mjs';
17
17
  import { ensureEntryTriggers } from './memory-schema/entry-triggers.mjs';
18
- import { ensureCoreEntriesSchema, ensureMetaSchema, stampBootstrapMeta } from './memory-schema/core-and-meta.mjs';
18
+ import {
19
+ ensureCoreAgentColumn,
20
+ ensureCoreEntriesSchema,
21
+ ensureMetaSchema,
22
+ stampBootstrapMeta,
23
+ } from './memory-schema/core-and-meta.mjs';
19
24
  import { VALID_CATEGORY } from './memory-categories.mjs';
20
25
 
21
26
  const dbs = new Map();
@@ -366,6 +371,7 @@ export async function ensureCurrentSchemaExtensions(db, dims, embeddingIdentity
366
371
  // Legacy NULL status remains active; obsolete generated metadata is untouched.
367
372
  await db.exec(`ALTER TABLE core_entries ADD COLUMN IF NOT EXISTS status text`);
368
373
  await db.exec(`ALTER TABLE core_entries ADD COLUMN IF NOT EXISTS archived_at bigint`);
374
+ await ensureCoreAgentColumn(db);
369
375
 
370
376
  await ensureCoreKeyIndex(db);
371
377
 
@@ -35,12 +35,20 @@ function appendTimeWindow(where, params, tsFrom, tsTo) {
35
35
  }
36
36
  }
37
37
 
38
- export async function recallCoreRows(db, query, { projectScope, category, limit, tsFrom, tsTo } = {}) {
38
+ /** `agentId`: the caller's own agent pool joins the result; no other agent's
39
+ * pool ever does (core-memory-access.mjs). */
40
+ export async function recallCoreRows(db, query, { projectScope, category, limit, tsFrom, tsTo, agentId = '' } = {}) {
39
41
  const terms = coreRecallTerms(query);
40
42
  if (terms.length === 0) return [];
41
43
 
42
44
  const params = [];
43
45
  const where = [];
46
+ if (agentId) {
47
+ params.push(agentId);
48
+ where.push(`(agent_id IS NULL OR agent_id = $${params.length})`);
49
+ } else {
50
+ where.push('agent_id IS NULL');
51
+ }
44
52
  const scope = normalizeRecallProjectScope(projectScope);
45
53
  let scopeClause = scope;
46
54
  if (scope === null) scopeClause = 'common';
@@ -14,6 +14,7 @@ import { compareRecallByScore, compareRecallNewestFirst } from './recall-order.m
14
14
  import { throwIfAborted } from './memory-cycle-shared.mjs';
15
15
  import { expandRecallEventContext } from './recall-event-context.mjs';
16
16
  import { insertTraceEvents } from './trace-store.mjs';
17
+ import { normalizeMemoryCaller } from './core-memory-access.mjs';
17
18
  import {
18
19
  annotateRecallRootContext,
19
20
  boundRecallRowsToTemporal,
@@ -305,6 +306,7 @@ export async function searchByQuery({
305
306
  : await recallCoreRows(retrievalQuery, {
306
307
  projectScope,
307
308
  category,
309
+ agentId: normalizeMemoryCaller(args.memory_caller).agentId,
308
310
  limit: retrievalLimit,
309
311
  tsFrom: temporal?.startMs,
310
312
  tsTo: temporal?.endMs,
@@ -58,6 +58,8 @@ export function createToolCallHandler({ handleSearch, handleMemoryAction }) {
58
58
  // Hint only — never a filter. Marks the caller's own session as
59
59
  // "(current)" in the multi-session grouped browse output.
60
60
  ...(a.currentSessionId ? { currentSessionId: a.currentSessionId } : {}),
61
+ // The calling session's scope: its own agent pool joins the core leg.
62
+ ...(a.memory_caller ? { memory_caller: a.memory_caller } : {}),
61
63
  };
62
64
  const result = await handleSearch(searchArgs, signal);
63
65
  const cappedText = capRecallText(result.text);
@@ -59,6 +59,11 @@ export const TOOL_DEFS = [
59
59
  description:
60
60
  'Pool: omit for the current Project, "common" for common memory, or a Project slug; "*" is list only.',
61
61
  },
62
+ agent_id: {
63
+ type: 'string',
64
+ description:
65
+ 'Agent memory instead of project_id: "self" is your own pool, read and injected only for you; an unattended run writes only there. The representative agent (lead) may list or delete another agent\'s by its id.',
66
+ },
62
67
  },
63
68
  additionalProperties: false,
64
69
  required: ['op'],