@rubytech/create-maxy-code 0.1.61 → 0.1.63

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 (148) hide show
  1. package/dist/__tests__/installer-specialist-registration.test.js +112 -0
  2. package/dist/index.js +22 -0
  3. package/dist/specialist-registration.js +78 -0
  4. package/package.json +1 -1
  5. package/payload/platform/plugins/admin/PLUGIN.md +22 -0
  6. package/payload/platform/plugins/admin/hooks/__tests__/turn-completed-graph-write.test.sh +50 -13
  7. package/payload/platform/plugins/admin/hooks/turn-completed-graph-write.sh +22 -21
  8. package/payload/platform/plugins/contacts/PLUGIN.md +9 -0
  9. package/payload/platform/plugins/docs/references/admin-session.md +55 -7
  10. package/payload/platform/plugins/email/PLUGIN.md +9 -0
  11. package/payload/platform/plugins/epc/PLUGIN.md +5 -0
  12. package/payload/platform/plugins/loop/PLUGIN.md +27 -0
  13. package/payload/platform/plugins/memory/PLUGIN.md +37 -0
  14. package/payload/platform/plugins/memory/mcp/dist/index.js +3 -3
  15. package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
  16. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/live-schema-source.test.js +137 -75
  17. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/live-schema-source.test.js.map +1 -1
  18. package/payload/platform/plugins/memory/mcp/dist/lib/live-schema-source.d.ts +0 -17
  19. package/payload/platform/plugins/memory/mcp/dist/lib/live-schema-source.d.ts.map +1 -1
  20. package/payload/platform/plugins/memory/mcp/dist/lib/live-schema-source.js +32 -4
  21. package/payload/platform/plugins/memory/mcp/dist/lib/live-schema-source.js.map +1 -1
  22. package/payload/platform/plugins/memory/mcp/dist/lib/log-ingest.d.ts +65 -0
  23. package/payload/platform/plugins/memory/mcp/dist/lib/log-ingest.d.ts.map +1 -0
  24. package/payload/platform/plugins/memory/mcp/dist/lib/log-ingest.js +182 -0
  25. package/payload/platform/plugins/memory/mcp/dist/lib/log-ingest.js.map +1 -0
  26. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-enrich-rejection.test.js +8 -8
  27. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-enrich-rejection.test.js.map +1 -1
  28. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/log-ingest.test.d.ts +2 -0
  29. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/log-ingest.test.d.ts.map +1 -0
  30. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/log-ingest.test.js +241 -0
  31. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/log-ingest.test.js.map +1 -0
  32. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.d.ts.map +1 -1
  33. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.js +9 -0
  34. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.js.map +1 -1
  35. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-enrich-rejection.d.ts +1 -1
  36. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-enrich-rejection.d.ts.map +1 -1
  37. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-enrich-rejection.js +9 -1
  38. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-enrich-rejection.js.map +1 -1
  39. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-memory-expunge.d.ts.map +1 -1
  40. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-memory-expunge.js +10 -2
  41. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-memory-expunge.js.map +1 -1
  42. package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-denylist-add.d.ts +1 -1
  43. package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-denylist-add.d.ts.map +1 -1
  44. package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-denylist-add.js +5 -1
  45. package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-denylist-add.js.map +1 -1
  46. package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-denylist-remove.d.ts +1 -1
  47. package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-denylist-remove.d.ts.map +1 -1
  48. package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-denylist-remove.js +5 -1
  49. package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-denylist-remove.js.map +1 -1
  50. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.d.ts.map +1 -1
  51. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.js +4 -0
  52. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.js.map +1 -1
  53. package/payload/platform/plugins/memory/mcp/dist/tools/memory-classify.d.ts.map +1 -1
  54. package/payload/platform/plugins/memory/mcp/dist/tools/memory-classify.js +4 -0
  55. package/payload/platform/plugins/memory/mcp/dist/tools/memory-classify.js.map +1 -1
  56. package/payload/platform/plugins/memory/mcp/dist/tools/memory-delete.d.ts.map +1 -1
  57. package/payload/platform/plugins/memory/mcp/dist/tools/memory-delete.js +4 -0
  58. package/payload/platform/plugins/memory/mcp/dist/tools/memory-delete.js.map +1 -1
  59. package/payload/platform/plugins/memory/mcp/dist/tools/memory-edit-attachment.d.ts.map +1 -1
  60. package/payload/platform/plugins/memory/mcp/dist/tools/memory-edit-attachment.js +4 -0
  61. package/payload/platform/plugins/memory/mcp/dist/tools/memory-edit-attachment.js.map +1 -1
  62. package/payload/platform/plugins/memory/mcp/dist/tools/memory-empty-trash.d.ts.map +1 -1
  63. package/payload/platform/plugins/memory/mcp/dist/tools/memory-empty-trash.js +4 -0
  64. package/payload/platform/plugins/memory/mcp/dist/tools/memory-empty-trash.js.map +1 -1
  65. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-extract.d.ts.map +1 -1
  66. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-extract.js +4 -0
  67. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-extract.js.map +1 -1
  68. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-web.d.ts.map +1 -1
  69. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-web.js +4 -0
  70. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-web.js.map +1 -1
  71. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.d.ts.map +1 -1
  72. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js +5 -0
  73. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js.map +1 -1
  74. package/payload/platform/plugins/memory/mcp/dist/tools/memory-reindex.d.ts.map +1 -1
  75. package/payload/platform/plugins/memory/mcp/dist/tools/memory-reindex.js +4 -0
  76. package/payload/platform/plugins/memory/mcp/dist/tools/memory-reindex.js.map +1 -1
  77. package/payload/platform/plugins/memory/mcp/dist/tools/memory-rename-attachment.d.ts.map +1 -1
  78. package/payload/platform/plugins/memory/mcp/dist/tools/memory-rename-attachment.js +4 -0
  79. package/payload/platform/plugins/memory/mcp/dist/tools/memory-rename-attachment.js.map +1 -1
  80. package/payload/platform/plugins/memory/mcp/dist/tools/memory-restore.d.ts.map +1 -1
  81. package/payload/platform/plugins/memory/mcp/dist/tools/memory-restore.js +4 -0
  82. package/payload/platform/plugins/memory/mcp/dist/tools/memory-restore.js.map +1 -1
  83. package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.d.ts.map +1 -1
  84. package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.js +5 -0
  85. package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.js.map +1 -1
  86. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.d.ts.map +1 -1
  87. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js +6 -0
  88. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js.map +1 -1
  89. package/payload/platform/plugins/memory/mcp/dist/tools/profile-delete.d.ts.map +1 -1
  90. package/payload/platform/plugins/memory/mcp/dist/tools/profile-delete.js +4 -0
  91. package/payload/platform/plugins/memory/mcp/dist/tools/profile-delete.js.map +1 -1
  92. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.d.ts.map +1 -1
  93. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js +4 -0
  94. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js.map +1 -1
  95. package/payload/platform/plugins/memory/mcp/vitest.config.ts +10 -3
  96. package/payload/platform/plugins/outlook/PLUGIN.md +7 -0
  97. package/payload/platform/plugins/preval/.claude-plugin/plugin.json +1 -1
  98. package/payload/platform/plugins/preval/PLUGIN.md +22 -12
  99. package/payload/platform/plugins/preval/hooks/preval-prompt-required.sh +140 -0
  100. package/payload/platform/plugins/preval/skills/property-preval/SKILL.md +77 -19
  101. package/payload/platform/plugins/preval/skills/property-preval/references/__tests__/adherence.test.py +373 -0
  102. package/payload/platform/plugins/preval/skills/property-preval/references/render.py +390 -47
  103. package/payload/platform/plugins/preval/skills/property-preval/references/template-inputs.schema.json +32 -21
  104. package/payload/platform/plugins/preval/skills/property-preval/references/template.html +0 -1
  105. package/payload/platform/plugins/property-data/PLUGIN.md +22 -0
  106. package/payload/platform/plugins/replicate/PLUGIN.md +3 -0
  107. package/payload/platform/plugins/scheduling/PLUGIN.md +8 -0
  108. package/payload/platform/plugins/tasks/PLUGIN.md +14 -0
  109. package/payload/platform/plugins/telegram/PLUGIN.md +3 -0
  110. package/payload/platform/plugins/waitlist/PLUGIN.md +7 -0
  111. package/payload/platform/plugins/whatsapp/PLUGIN.md +12 -0
  112. package/payload/platform/plugins/workflows/PLUGIN.md +8 -0
  113. package/payload/platform/services/claude-session-manager/dist/index.js +39 -1
  114. package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
  115. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +21 -0
  116. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
  117. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +148 -41
  118. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
  119. package/payload/platform/services/claude-session-manager/dist/specialist-drift.d.ts +23 -0
  120. package/payload/platform/services/claude-session-manager/dist/specialist-drift.d.ts.map +1 -0
  121. package/payload/platform/services/claude-session-manager/dist/specialist-drift.js +141 -0
  122. package/payload/platform/services/claude-session-manager/dist/specialist-drift.js.map +1 -0
  123. package/payload/platform/services/claude-session-manager/dist/tool-surface.d.ts +13 -2
  124. package/payload/platform/services/claude-session-manager/dist/tool-surface.d.ts.map +1 -1
  125. package/payload/platform/services/claude-session-manager/dist/tool-surface.js +135 -37
  126. package/payload/platform/services/claude-session-manager/dist/tool-surface.js.map +1 -1
  127. package/payload/premium-plugins/real-agent/agents/compliance.md +1 -1
  128. package/payload/premium-plugins/real-agent/plugins/epc/PLUGIN.md +5 -0
  129. package/payload/premium-plugins/real-agent/plugins/loop/PLUGIN.md +27 -0
  130. package/payload/premium-plugins/real-agent/plugins/preval/.claude-plugin/plugin.json +1 -1
  131. package/payload/premium-plugins/real-agent/plugins/preval/PLUGIN.md +22 -12
  132. package/payload/premium-plugins/real-agent/plugins/preval/hooks/preval-prompt-required.sh +140 -0
  133. package/payload/premium-plugins/real-agent/plugins/preval/skills/property-preval/SKILL.md +77 -19
  134. package/payload/premium-plugins/real-agent/plugins/preval/skills/property-preval/references/__tests__/adherence.test.py +373 -0
  135. package/payload/premium-plugins/real-agent/plugins/preval/skills/property-preval/references/render.py +390 -47
  136. package/payload/premium-plugins/real-agent/plugins/preval/skills/property-preval/references/template-inputs.schema.json +32 -21
  137. package/payload/premium-plugins/real-agent/plugins/preval/skills/property-preval/references/template.html +0 -1
  138. package/payload/premium-plugins/real-agent/plugins/property-data/PLUGIN.md +22 -0
  139. package/payload/server/public/assets/{admin-DhN3G8W7.js → admin-ymReS1mN.js} +1 -1
  140. package/payload/server/public/assets/{data-B2ZVXOcE.js → data-D6U2e85B.js} +1 -1
  141. package/payload/server/public/assets/{graph-BAMGPHrK.js → graph-C274PN27.js} +1 -1
  142. package/payload/server/public/assets/{graph-labels-D9eBbvxo.js → graph-labels-DQFzd1FY.js} +1 -1
  143. package/payload/server/public/assets/{page-DjdVMWCz.js → page-D-OLjort.js} +1 -1
  144. package/payload/server/public/assets/{page-CV27Al6Z.js → page-DFFt9_Q0.js} +1 -1
  145. package/payload/server/public/data.html +3 -3
  146. package/payload/server/public/graph.html +3 -3
  147. package/payload/server/public/index.html +4 -4
  148. package/payload/server/server.js +1 -1
@@ -85,25 +85,41 @@ The Hono route `POST /api/admin/claude-sessions` at [`platform/ui/server/routes/
85
85
 
86
86
  **Callers.** `Sidebar.tsx`'s "+ New session" click handler (sends no `initialMessage` — Task 146 made Claude Code's own label canonical, so no UI-side stimulus is injected); the turn-recorder hook (loopback path — see "Turn-recorder" below); future programmatic spawns. Every caller routes through this wrapper. The on-the-wire signal that the contract held is the `[claude-session-manager:wrapper] spawn-request-in surface=<cookie|loopback>` log line followed by `forward-spawn-done`.
87
87
 
88
- ## Turn-recorder: a Sidebar-equivalent spawn with three overrides
88
+ ## Turn-recorder: a first-class specialist spawn with three body overrides
89
89
 
90
- After every completed operator admin turn, the Stop hook `platform/plugins/admin/hooks/turn-completed-graph-write.sh` dispatches one headless `database-operator` session that writes any new or missing information from the turn into the Neo4j graph. Task 147 — the recorder spawn is byte-for-byte equivalent to a Sidebar "New session" body, with three overrides:
90
+ After every completed operator admin turn, the Stop hook `platform/plugins/admin/hooks/turn-completed-graph-write.sh` dispatches one headless `database-operator` session that writes any new or missing information from the turn into the Neo4j graph. The recorder spawn is structurally a Sidebar "New session" body with three overrides — and the manager treats it as a first-class specialist spawn: the agent file is the prompt, the agent frontmatter is the tool surface, the per-spawn mcp.json is narrowed to the plugins the agent names.
91
91
 
92
+ Body overrides relative to a plain Sidebar click:
92
93
  - `specialist: 'database-operator'`
93
94
  - `model: 'haiku'`
94
- - `initialMessage: "update the graph with any new or missing information or intent\n\n<last-turn-text>"`
95
-
96
- There is no recorder-only carving on the manager side: no preflight, no tool-surface gate, no `originatingSessionId` discriminator, no `MAXY_IS_RECORDER` env stamp, no recorder-only log surfaces. The PTY's own first-use error is the contract for tool absence.
95
+ - `initialMessage: "update the graph with any new or missing information or intent\n\n<conversation-text>"`
97
96
 
98
97
  **Spawn body.** The hook POSTs `{adminSessionId, channel: 'browser', specialist: 'database-operator', model: 'haiku', initialMessage}` to `POST /api/admin/claude-sessions`. `adminSessionId` is the loopback-bypass key — the wrapper resolves the operator's real `senderId` from `GET <managerBase>/<adminSessionId>/meta` and forwards a Sidebar-shape body to the manager.
99
98
 
100
- **`<last-turn-text>`** is the most recent `assistant` record's text content from the operator JSONL, or if no assistant record yields text the most recent `user` record's text. Tool-call blocks, system records, and ephemeral admin-state records are never the candidate. If neither yields text, the hook emits `trigger-skipped reason=last-turn-empty` and no recorder is spawned.
99
+ **`<conversation-text>`** is the operator JSONL walked in transcript order: every `user`/`assistant` record with non-empty text emitted as `"<type>: <text>"`, joined by `\n`. The full exchange is preserved so the recorder can infer operator-asserted facts that span multiple turns (e.g. "hello / what's your business? / Smalleys"). The earlier "last-turn-only" extractor stripped the framing the recorder needed. If no qualifying record exists, the hook emits `trigger-skipped reason=conversation-empty` and no recorder is spawned.
100
+
101
+ **Manager-side specialist branches.** The recorder is the first specialist subagent that exercises the full specialist flag matrix:
102
+ - The bundled `platform/templates/specialists/agents/database-operator.md` template is symlinked into `$CLAUDE_CONFIG_DIR/agents/database-operator.md` by the installer; without that link `claude --agent database-operator` silently falls back to the admin agent.
103
+ - `--append-system-prompt` is omitted entirely — the agent file IS the prompt; admin IDENTITY / SOUL / aboutOwner do NOT layer on top.
104
+ - `--allowed-tools` is sourced from the agent's frontmatter `tools:` list (11 entries for the recorder), not the admin surface.
105
+ - The per-spawn `mcp.json` is narrowed to only `memory`, `contacts`, `tasks` — the plugins whose tools the agent's frontmatter names.
101
106
 
102
107
  **End-of-turn auto-archive.** When any session whose specialist is `database-operator` gains an assistant message with `stop_reason: "end_turn"` in its JSONL, the manager posts `/<sid>/stop` against itself, claude exits, the fs-watcher observes the PID-file removal and drops the row from the index. The auto-archive log line is `auto-archive sessionId=<sid> specialist=database-operator reason=end-turn …`.
103
108
 
104
109
  **Hook recursion gate.** `pty-spawner` stamps `MAXY_SPECIALIST=<specialist>` on every PTY env. The Stop hook short-circuits when `MAXY_SPECIALIST=database-operator` so the recorder's own end-of-turn does not re-fire the hook.
105
110
 
106
- **Observability.** The hook emits exactly two lines per operator turn via `/api/admin/log-ingest`: `[turn-recorder] trigger sessionId=<op> turnIndex=0 transcriptBytes=<n> lastTurnBytes=<n>` and `[turn-recorder] spawn-request sessionId=<op> specialist=database-operator initialMessageBytes=<n>`. The remainder of the recorder lifecycle is covered by the Sidebar's existing lines (`pty-spawned`, JSONL events, `auto-archive`). Failure-mode names: `trigger-skipped reason=…` enumerates `role-not-admin | is-recorder | empty-stdin | missing-transcript | last-turn-empty`. Hook-side spawn errors emit `[turn-recorder] spawn-failed reason=loopback-http http=<code>`.
111
+ **Observability.** The hook emits exactly two lines per operator turn via `/api/admin/log-ingest`: `[turn-recorder] trigger sessionId=<op> turnIndex=0 transcriptBytes=<n> conversationBytes=<n>` and `[turn-recorder] spawn-request sessionId=<op> specialist=database-operator initialMessageBytes=<n>`. The manager adds `pty-spawn-allowlist specialist=database-operator count=11 sourced-from=agent-frontmatter` and `pty-spawn-start … specialist=database-operator append-system-prompt-bytes=0` before the PTY launches. The remainder of the recorder lifecycle is covered by the Sidebar's existing lines (`pty-spawned`, JSONL events, `auto-archive`). Failure-mode names: `trigger-skipped reason=…` enumerates `role-not-admin | is-recorder | empty-stdin | missing-transcript | conversation-empty`. Hook-side spawn errors emit `[turn-recorder] spawn-failed reason=loopback-http http=<code>`.
112
+
113
+ ### Recorder lifecycle diagnostic
114
+
115
+ When the operator reports "the recorder did nothing this turn", run these six greps in order. The first absent line names the phase that failed. `<op>` is the admin operator's session id from the original Stop hook; `<rec>` is the recorder session id returned in phase 2's response and reused across phases 3–6.
116
+
117
+ 1. **Stop hook fired.** `grep '\[turn-recorder\] trigger sessionId=<op>' ~/.<brand>/logs/server.log` — expects one line of shape `[turn-recorder] trigger sessionId=<op> turnIndex=0 transcriptBytes=<n> conversationBytes=<n>`. Absent: the Stop hook didn't run; check that `MAXY_SESSION_ROLE=admin` was set on the operator PTY and that `MAXY_SPECIALIST!=database-operator` (recursion gate).
118
+ 2. **/spawn accepted.** `grep '\[turn-recorder\] spawn-request sessionId=<op>' ~/.<brand>/logs/server.log` — expects `[turn-recorder] spawn-request sessionId=<op> specialist=database-operator initialMessageBytes=<n>`. The response body of this POST carries `<rec>`. Absent: the hook fired but `/api/admin/claude-sessions` rejected; look for `[turn-recorder] spawn-failed reason=loopback-http http=<code>` on the next line.
119
+ 3. **PTY started.** `grep 'pty-spawn-start .* specialist=database-operator' ~/.<brand>/logs/server.log` — expects `pty-spawn-start claudeBin=<…> argv-count=<n> append-system-prompt-bytes=0 … specialist=database-operator prompt-positional=yes prompt-bytes=<n>`. Absent: the wrapper accepted but the manager rejected before exec; check the spawn-failure surfaces (`which-claude-not-found | pty-spawn-failed | pid-file-timeout | host-context-unresolved | identity-unresolved | mcp-config-write-failed`).
120
+ 4. **Agent file resolved.** `grep 'pty-spawn-allowlist specialist=database-operator count=11 sourced-from=agent-frontmatter' ~/.<brand>/logs/server.log` — expects exactly one line per recorder spawn. Absent (or `count` not equal to the frontmatter's tool count): the agent file at `$CLAUDE_CONFIG_DIR/agents/database-operator.md` is missing the `tools:` line, or the symlink the installer creates never landed. Re-run the installer.
121
+ 5. **Graph write outcome.** `grep '\[mcp:memory\] memory-write .* session=<rec>' ~/.<brand>/logs/server.log` — expects one line ending `result=ok elementId=<id>` or `result=error reason=<slug>` (slug enumerated by the memory MCP write-path observability work). Absent: the recorder loaded but produced no tool calls — model-side decision, not infra. Read the recorder JSONL at `<accountDir>/.claude/projects/<slug>/<rec>.jsonl` to see what the LLM did.
122
+ 6. **Auto-archive.** `grep 'auto-archive .* sessionId=<rec> .* specialist=database-operator reason=end-turn' ~/.<brand>/logs/server.log` — expects one line. Absent: the recorder finished but the manager's end-turn watcher didn't fire; the fs-watcher row will get reaped on its TTL but the recorder hung longer than expected. Investigate `pty-spawn-stop` and `pid-file-removed` lines on the same `<rec>`.
107
123
 
108
124
  ## Session identifiers (Task 135)
109
125
 
@@ -114,6 +130,38 @@ The metadata pane surfaces two distinct identifier values. Task 141 collapsed th
114
130
  | `sessionId` | Claude's session. Two phases: bridge suffix (`session_xxx`, the URL segment in `claude.ai/code/<session_xxx>`, set when claude prints the `/remote-control` URL) and JSONL basename UUID (claude's intrinsic id on disk, bound when the first turn flushes the JSONL). Both phases coexist on a live row after URL capture; the manager wire emits the bridge form when set, falling back to the JSONL basename in the pre-URL-capture window. The resolver routes either phase to the same row, so callers never need to choose. | `sessionId` (collapsed from the earlier three-field surface (see Task 141) `sessionId` + `claudeSessionId` + `bridgeSessionId`) | `sessionId=` |
115
131
  | `accountId` | Account / workspace UUID. For the admin channel, the value the manager carries as `senderId` IS the accountId; for non-admin channels the same wire field carries a phone / email / chat-id (the channel-level sender), so its log key on those paths is still `senderId=`. | `senderId` (channel-agnostic) | `accountId=` in admin-flow emit sites; `senderId=` in channel-bridge sites |
116
132
 
133
+ ## Memory MCP write-path outcome lines (Task 166)
134
+
135
+ Every write-path tool in the memory plugin MCP emits one structured line per invocation to `server.log` via the loopback `/api/admin/log-ingest` route. The operator can answer "did this write succeed?" from a single greppable surface — no need to cross-reference the per-process `mcp-memory-stderr-<date>.log`.
136
+
137
+ Line shapes:
138
+
139
+ ```
140
+ [mcp:memory] <tool-name> account=<id|—> session=<convId|—> labels=<csv|—> name=<value|—> result=ok elementId=<id|—> ms=<n>
141
+ [mcp:memory] <tool-name> account=<id|—> session=<convId|—> labels=<csv|—> name=<value|—> result=error reason=<slug> detail=<≤120 chars> ms=<n>
142
+ ```
143
+
144
+ Enumerated `reason` slugs (failure path): `invalid-input | schema-unknown-label | schema-property-naming | account-isolation | producedby-task-not-found | constraint-violation | conflicting-element-id | invalid-target-node-id | gate-blocked | neo4j-driver-error`. Free-form error text remains in the `detail` field. New slugs are added only by amending the helper's union type; nothing silently falls through to a generic `unknown`.
145
+
146
+ Wired tools: `memory-write`, `memory-update`, `memory-delete`, `memory-restore`, `memory-empty-trash`, `memory-archive-write`, `memory-ingest`, `memory-ingest-extract`, `memory-ingest-web`, `memory-classify`, `memory-reindex`, `memory-edit-attachment`, `memory-rename-attachment`, `profile-update`, `profile-delete`, `graph-prune-denylist-add`, `graph-prune-denylist-remove`, `conversation-archive-derive-insights`, `conversation-archive-enrich-rejection`, `conversation-memory-expunge`. Read-path tools (`memory-search`, `memory-rank`, etc.) are intentionally out of scope — reads do not change state and their failures already reach the caller.
147
+
148
+ Diagnostic grep:
149
+
150
+ ```bash
151
+ # Did the most recent memory-write attempt land, and what was it?
152
+ grep '\[mcp:memory\] memory-write .* result=' ~/.${brand}/logs/server.log | tail
153
+
154
+ # All failed writes across every memory MCP tool, grouped by reason:
155
+ grep '\[mcp:memory\] .* result=error' ~/.${brand}/logs/server.log | awk -F'reason=' '{print $2}' | awk '{print $1}' | sort | uniq -c | sort -rn
156
+
157
+ # Did the loopback POST itself fail? (Helper falls back to stderr and re-writes
158
+ # the structured line, so the event is never lost — this just flags that the
159
+ # session-manager's /api/admin/log-ingest was unreachable for some window.)
160
+ grep '\[mcp:memory\] log-ingest-failed' ~/.${brand}/logs/mcp-memory-stderr-*.log
161
+ ```
162
+
163
+ The existing `mcp-memory-stderr-<date>.log` capture is unchanged on error paths — the new structured line is additive, not a relocation. Crash-mode debugging continues to read the stderr log; routine "did it land?" questions read `server.log`.
164
+
117
165
  ## Out of scope
118
166
 
119
167
  - Public/WhatsApp/Telegram sessions — out of scope. Their sessionKeys remain `crypto.randomUUID()` and follow the existing rejection-on-restart contract.
@@ -4,22 +4,31 @@ description: "Email — dedicated email account. Retrieval: email-graph-query fo
4
4
  tools:
5
5
  - name: email-setup
6
6
  publicAllowlist: false
7
+ adminAllowlist: false
7
8
  - name: email-read
8
9
  publicAllowlist: false
10
+ adminAllowlist: false
9
11
  - name: email-send
10
12
  publicAllowlist: false
13
+ adminAllowlist: false
11
14
  - name: email-reply
12
15
  publicAllowlist: false
16
+ adminAllowlist: false
13
17
  - name: email-search
14
18
  publicAllowlist: false
19
+ adminAllowlist: false
15
20
  - name: email-graph-query
16
21
  publicAllowlist: false
22
+ adminAllowlist: false
17
23
  - name: email-otp-extract
18
24
  publicAllowlist: false
25
+ adminAllowlist: false
19
26
  - name: email-status
20
27
  publicAllowlist: false
28
+ adminAllowlist: false
21
29
  - name: email-auto-respond-config
22
30
  publicAllowlist: false
31
+ adminAllowlist: false
23
32
  always: false
24
33
  embed: ["admin"]
25
34
  metadata: {"platform":{}}
@@ -4,14 +4,19 @@ description: "gov.uk EPC register — by-address and by-UPRN lookup of the most
4
4
  tools:
5
5
  - name: epc-key-register
6
6
  publicAllowlist: false
7
+ adminAllowlist: false
7
8
  - name: epc-key-deregister
8
9
  publicAllowlist: false
10
+ adminAllowlist: false
9
11
  - name: epc-key-list
10
12
  publicAllowlist: false
13
+ adminAllowlist: false
11
14
  - name: epc-lookup-by-address
12
15
  publicAllowlist: false
16
+ adminAllowlist: false
13
17
  - name: epc-lookup-by-uprn
14
18
  publicAllowlist: false
19
+ adminAllowlist: false
15
20
  always: false
16
21
  metadata: {"platform":{"optional":true,"embed":["admin"]}}
17
22
  mcp:
@@ -4,58 +4,85 @@ description: "Loop CRM integration — full read/write access to all 8 resource
4
4
  tools:
5
5
  - name: loop-key-register
6
6
  publicAllowlist: false
7
+ adminAllowlist: false
7
8
  - name: loop-key-deregister
8
9
  publicAllowlist: false
10
+ adminAllowlist: false
9
11
  - name: loop-key-list
10
12
  publicAllowlist: false
13
+ adminAllowlist: false
11
14
  - name: loop-people-search
12
15
  publicAllowlist: false
16
+ adminAllowlist: false
13
17
  - name: loop-people-detail
14
18
  publicAllowlist: false
19
+ adminAllowlist: false
15
20
  - name: loop-property-search
16
21
  publicAllowlist: false
22
+ adminAllowlist: false
17
23
  - name: loop-property-detail
18
24
  publicAllowlist: false
25
+ adminAllowlist: false
19
26
  - name: loop-property-listed
20
27
  publicAllowlist: false
28
+ adminAllowlist: false
21
29
  - name: loop-property-sold
22
30
  publicAllowlist: false
31
+ adminAllowlist: false
23
32
  - name: loop-property-viewing
24
33
  publicAllowlist: false
34
+ adminAllowlist: false
25
35
  - name: loop-property-callback
26
36
  publicAllowlist: false
37
+ adminAllowlist: false
27
38
  - name: loop-property-information
28
39
  publicAllowlist: false
40
+ adminAllowlist: false
29
41
  - name: loop-viewing-search
30
42
  publicAllowlist: false
43
+ adminAllowlist: false
31
44
  - name: loop-viewing-detail
32
45
  publicAllowlist: false
46
+ adminAllowlist: false
33
47
  - name: loop-viewing-create
34
48
  publicAllowlist: false
49
+ adminAllowlist: false
35
50
  - name: loop-viewing-update
36
51
  publicAllowlist: false
52
+ adminAllowlist: false
37
53
  - name: loop-feedback-get
38
54
  publicAllowlist: false
55
+ adminAllowlist: false
39
56
  - name: loop-feedback-submit
40
57
  publicAllowlist: false
58
+ adminAllowlist: false
41
59
  - name: loop-team-info
42
60
  publicAllowlist: false
61
+ adminAllowlist: false
43
62
  - name: loop-team-availability
44
63
  publicAllowlist: false
64
+ adminAllowlist: false
45
65
  - name: loop-marketing-match
46
66
  publicAllowlist: false
67
+ adminAllowlist: false
47
68
  - name: loop-marketing-match-batch
48
69
  publicAllowlist: false
70
+ adminAllowlist: false
49
71
  - name: loop-marketing-match-request
50
72
  publicAllowlist: false
73
+ adminAllowlist: false
51
74
  - name: loop-marketing-enquiry
52
75
  publicAllowlist: false
76
+ adminAllowlist: false
53
77
  - name: loop-auto-responder
54
78
  publicAllowlist: false
79
+ adminAllowlist: false
55
80
  - name: loop-customer-preferences
56
81
  publicAllowlist: false
82
+ adminAllowlist: false
57
83
  - name: loop-supplier
58
84
  publicAllowlist: false
85
+ adminAllowlist: false
59
86
  skills:
60
87
  - skills/tone-matched-drafter/SKILL.md
61
88
  - skills/priority-ranker/SKILL.md
@@ -4,62 +4,91 @@ description: "Graph memory plugin. Provides memory-search (with optional `fields
4
4
  tools:
5
5
  - name: memory-search
6
6
  publicAllowlist: false
7
+ adminAllowlist: true
7
8
  - name: memory-rank
8
9
  publicAllowlist: false
10
+ adminAllowlist: false
9
11
  - name: memory-write
10
12
  publicAllowlist: false
13
+ adminAllowlist: false
11
14
  - name: memory-update
12
15
  publicAllowlist: false
16
+ adminAllowlist: false
13
17
  - name: memory-reindex
14
18
  publicAllowlist: false
19
+ adminAllowlist: false
15
20
  - name: memory-ingest
16
21
  publicAllowlist: false
22
+ adminAllowlist: false
17
23
  - name: memory-ingest-extract
18
24
  publicAllowlist: false
25
+ adminAllowlist: false
19
26
  - name: memory-ingest-web
20
27
  publicAllowlist: false
28
+ adminAllowlist: false
21
29
  - name: memory-classify
22
30
  publicAllowlist: false
31
+ adminAllowlist: false
23
32
  - name: memory-find-candidates
24
33
  publicAllowlist: false
34
+ adminAllowlist: true
25
35
  - name: memory-delete
26
36
  publicAllowlist: false
37
+ adminAllowlist: true
27
38
  - name: memory-restore
28
39
  publicAllowlist: false
40
+ adminAllowlist: true
29
41
  - name: memory-empty-trash
30
42
  publicAllowlist: false
43
+ adminAllowlist: true
31
44
  - name: memory-list-attachments
32
45
  publicAllowlist: false
46
+ adminAllowlist: true
33
47
  - name: memory-read-attachment
34
48
  publicAllowlist: false
49
+ adminAllowlist: true
35
50
  - name: memory-edit-attachment
36
51
  publicAllowlist: false
52
+ adminAllowlist: true
37
53
  - name: memory-rename-attachment
38
54
  publicAllowlist: false
55
+ adminAllowlist: true
39
56
  - name: memory-archive-write
40
57
  publicAllowlist: false
58
+ adminAllowlist: false
41
59
  - name: conversation-archive-derive-insights
42
60
  publicAllowlist: false
61
+ adminAllowlist: false
43
62
  - name: conversation-archive-enrich-rejection
44
63
  publicAllowlist: false
64
+ adminAllowlist: false
45
65
  - name: conversation-list
46
66
  publicAllowlist: false
67
+ adminAllowlist: true
47
68
  - name: conversation-search
48
69
  publicAllowlist: false
70
+ adminAllowlist: true
49
71
  - name: profile-read
50
72
  publicAllowlist: false
73
+ adminAllowlist: false
51
74
  - name: profile-update
52
75
  publicAllowlist: false
76
+ adminAllowlist: false
53
77
  - name: profile-delete
54
78
  publicAllowlist: false
79
+ adminAllowlist: true
55
80
  - name: graph-prune-denylist-add
56
81
  publicAllowlist: false
82
+ adminAllowlist: true
57
83
  - name: graph-prune-denylist-list
58
84
  publicAllowlist: false
85
+ adminAllowlist: true
59
86
  - name: graph-prune-denylist-remove
60
87
  publicAllowlist: false
88
+ adminAllowlist: true
61
89
  - name: conversation-memory-expunge
62
90
  publicAllowlist: false
91
+ adminAllowlist: true
63
92
  hidden:
64
93
  - session-compact
65
94
  - session-compact-status
@@ -158,6 +187,14 @@ Before any structured write, load `references/schema-base.md` via `plugin-read`.
158
187
 
159
188
  **Forbidden properties.** A "Forbidden Properties" table in any schema markdown file declares per-label deny-listed property keys. The validator rejects writes that include a forbidden key for the matching primary label and emits `[schema-validator] outcome=rejected label=<X> property=<Y> reason=forbidden writer=<agentName>`. The first row is `Person | name` — use `givenName` + `familyName` (rendered display name composed at read time). The check fires after the synonym pass, so `firstName → givenName` rewrites still happen first.
160
189
 
190
+ **Drift log shape.** On every schema-cache refresh the validator compares `db.labels()` (live) against `schema.cypher` declarations and the schema markdown's documented labels, and emits one summary line per non-empty drift kind:
191
+
192
+ ```
193
+ [schema-validator] drift kind=<kind> count=<n> tokens=<csv up to 10>[+<remainder>]
194
+ ```
195
+
196
+ `<kind>` is one of `declared-not-live`, `live-not-declared`, `markdown-orphan`. Tokens are alphabetically sorted; if more than 10 drift in a kind the line names the first 10 and appends `+<remainder>` (e.g. `count=38 tokens=Applicant,…,Engagement+28`). Empty kinds emit nothing — a clean schema produces no drift lines per refresh. Set `MEMORY_MCP_VERBOSE_DRIFT=1` on the manager unit to keep the historical per-token lines underneath each summary; default off because drift sets of 50+ tokens otherwise bury real signals (write errors, validator rejections) in `mcp-memory-stderr-<date>.log`.
197
+
161
198
  ## Document Ingestion
162
199
 
163
200
  Document ingestion of any kind — PDFs, text, transcripts, web pages, single files — routes to the `specialists:database-operator` specialist, which loads the universal `document-ingest` skill at `skills/document-ingest/SKILL.md`. The admin agent never calls `memory-ingest` directly; it dispatches with the document path, the document subject (the anchor node), and the visibility scope.
@@ -524,7 +524,7 @@ server.tool("conversation-archive-enrich-rejection", "Record or undo a per-row r
524
524
  .describe("record = append rejection; undo = remove rejection (called after `wire`)."),
525
525
  }, async ({ archiveElementId, chunkElementId, kind, contentHash, mode }) => {
526
526
  try {
527
- const result = conversationArchiveEnrichRejection({
527
+ const result = await conversationArchiveEnrichRejection({
528
528
  accountId,
529
529
  archiveElementId,
530
530
  chunkElementId,
@@ -1631,7 +1631,7 @@ if (!readOnly) {
1631
1631
  values: z.array(z.string().min(1)).min(1).describe("Substring values to add. Match is exact substring (case-sensitive) against string-typed properties only."),
1632
1632
  }, async ({ values }) => {
1633
1633
  try {
1634
- const result = graphPruneDenylistAdd(values);
1634
+ const result = await graphPruneDenylistAdd(values);
1635
1635
  const lines = [
1636
1636
  `Added ${result.added.length} entries to deny-list (total ${result.totalAfter}).`,
1637
1637
  ];
@@ -1655,7 +1655,7 @@ if (!readOnly) {
1655
1655
  values: z.array(z.string().min(1)).min(1).describe("Substring values to remove (must match exactly as previously added)."),
1656
1656
  }, async ({ values }) => {
1657
1657
  try {
1658
- const result = graphPruneDenylistRemove(values);
1658
+ const result = await graphPruneDenylistRemove(values);
1659
1659
  const lines = [
1660
1660
  `Removed ${result.removed.length} entries from deny-list (total ${result.totalAfter}).`,
1661
1661
  ];