@rubytech/create-maxy-code 0.1.144 → 0.1.146
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.
- package/package.json +1 -1
- package/payload/platform/neo4j/schema.cypher +154 -4
- package/payload/platform/plugins/admin/PLUGIN.md +1 -1
- package/payload/platform/plugins/admin/hooks/post-turn-graph-pass.sh +251 -0
- package/payload/platform/plugins/admin/skills/datetime/SKILL.md +63 -5
- package/payload/platform/plugins/docs/references/internals.md +16 -0
- package/payload/platform/plugins/docs/references/memory-guide.md +10 -0
- package/payload/platform/plugins/docs/references/platform.md +1 -1
- package/payload/platform/plugins/memory/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/memory/PLUGIN.md +25 -3
- package/payload/platform/plugins/memory/mcp/dist/index.js +309 -11
- package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/compiled-truth-revision.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/compiled-truth-revision.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/compiled-truth-revision.test.js +33 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/compiled-truth-revision.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/graph-write-gate-label.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/graph-write-gate-label.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/graph-write-gate-label.test.js +30 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/graph-write-gate-label.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/phase-prune-revisions.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/phase-prune-revisions.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/phase-prune-revisions.test.js +51 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/phase-prune-revisions.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/relative-date.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/relative-date.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/relative-date.test.js +68 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/relative-date.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/timeline-extractor.test.js +41 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/timeline-extractor.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-revision.d.ts +43 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-revision.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-revision.js +33 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-revision.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-rewriter.d.ts +18 -5
- package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-rewriter.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-rewriter.js +64 -17
- package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-rewriter.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/index.d.ts +44 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/index.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/index.js +14 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/index.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-citation-audit.d.ts +26 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-citation-audit.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-citation-audit.js +219 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-citation-audit.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-dead-edges.d.ts +19 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-dead-edges.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-dead-edges.js +39 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-dead-edges.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-orphans.d.ts +19 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-orphans.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-orphans.js +42 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-orphans.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-prune-revisions.d.ts +21 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-prune-revisions.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-prune-revisions.js +27 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-prune-revisions.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-stale-truth.d.ts +18 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-stale-truth.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-stale-truth.js +60 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-stale-truth.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/graph-write-gate.d.ts +20 -3
- package/payload/platform/plugins/memory/mcp/dist/lib/graph-write-gate.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/graph-write-gate.js +47 -12
- package/payload/platform/plugins/memory/mcp/dist/lib/graph-write-gate.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/relative-date.d.ts +13 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/relative-date.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/relative-date.js +191 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/relative-date.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/timeline-extractor.d.ts +7 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/timeline-extractor.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/timeline-extractor.js +37 -9
- package/payload/platform/plugins/memory/mcp/dist/lib/timeline-extractor.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-compiled-truth-history.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-compiled-truth-history.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-compiled-truth-history.test.js +81 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-compiled-truth-history.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-report-tools.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-report-tools.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-report-tools.test.js +115 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-report-tools.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-emit.test.js +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-emit.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-write-emit.test.js +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-write-emit.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-compiled-truth-history.d.ts +19 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-compiled-truth-history.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-compiled-truth-history.js +50 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-compiled-truth-history.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-dream-run.d.ts +45 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-dream-run.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-dream-run.js +170 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-dream-run.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-report-list.d.ts +28 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-report-list.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-report-list.js +64 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-report-list.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-report-read-latest.d.ts +24 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-report-read-latest.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-report-read-latest.js +49 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-report-read-latest.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-report-write.d.ts +34 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-report-write.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-report-write.js +99 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-report-write.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-review-queue.d.ts +29 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-review-queue.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-review-queue.js +66 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-review-queue.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.js +122 -2
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.d.ts +49 -13
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.js +184 -16
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js +53 -10
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/vitest.config.ts +6 -0
- package/payload/platform/plugins/memory/references/schema-base.md +4 -0
- package/payload/platform/plugins/memory/skills/conversation-archive/SKILL.md +3 -4
- package/payload/platform/plugins/scheduling/skills/briefing/SKILL.md +13 -2
- package/payload/platform/scripts/seed-neo4j.sh +1 -1
- package/payload/platform/templates/agents/admin/IDENTITY.md +9 -1
- package/payload/platform/templates/specialists/agents/database-operator.md +30 -2
- package/payload/server/server.js +195 -91
- package/payload/platform/plugins/admin/hooks/signal-detector-stop.sh +0 -309
- package/payload/platform/templates/specialists/agents/signal-detector.md +0 -129
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "memory",
|
|
3
|
-
"description": "Graph memory plugin. Provides memory-search (with optional `fields` projection for known-shape lookups), memory-rank, memory-write, and memory-
|
|
3
|
+
"description": "Graph memory plugin. Provides memory-search (with optional `fields` projection for known-shape lookups), memory-rank, memory-write, memory-update, and the :Report surface (memory-report-write / memory-report-read-latest / memory-report-list) for reading from, writing to, and updating the Neo4j knowledge graph. Includes conversational memory — organic preference learning, evidence-backed recall, and transparent 'what do you know about me?' responses. Document ingestion (memory-classify + memory-ingest) supports two modes: `document` (default) for unstructured PDF/web content → :KnowledgeDocument (keyed on `attachmentId`) + :Section, and `chat` for conversation transcripts → :KnowledgeDocument (keyed on `conversationIdentity`) + :Section chunks; one parent label, two writer paths chosen by which identity property is set. Conversation-archive Phase 2 (`conversation-archive-derive-insights`) walks chunks of one named archive and emits per-row claim proposals for operator-gated wiring; `conversation-archive-enrich-rejection` records (or undoes) durable per-row rejections so already-triaged claims do not re-surface on re-runs. Ships four skills: `conversational-memory`, `document-ingest`, `conversation-archive` (source-agnostic transcript ingest for WhatsApp, Telegram, Signal, LinkedIn DMs, Zoom, meeting minutes, iMessage, Slack), and `conversation-archive-enrich` (per-row operator-gated insight derivation over a named archive's chunks).",
|
|
4
4
|
"version": "0.1.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Rubytech LLC"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: memory
|
|
3
|
-
description: "Graph memory plugin. Provides memory-search (with optional `fields` projection for known-shape lookups), memory-rank, memory-write, and memory-
|
|
3
|
+
description: "Graph memory plugin. Provides memory-search (with optional `fields` projection for known-shape lookups), memory-rank, memory-write, memory-update, and the :Report surface (memory-report-write / memory-report-read-latest / memory-report-list) for reading from, writing to, and updating the Neo4j knowledge graph. Includes conversational memory — organic preference learning, evidence-backed recall, and transparent 'what do you know about me?' responses. Document ingestion (memory-classify + memory-ingest) supports two modes: `document` (default) for unstructured PDF/web content → :KnowledgeDocument (keyed on `attachmentId`) + :Section, and `chat` for conversation transcripts → :KnowledgeDocument (keyed on `conversationIdentity`) + :Section chunks; one parent label, two writer paths chosen by which identity property is set. Conversation-archive Phase 2 (`conversation-archive-derive-insights`) walks chunks of one named archive and emits per-row claim proposals for operator-gated wiring; `conversation-archive-enrich-rejection` records (or undoes) durable per-row rejections so already-triaged claims do not re-surface on re-runs. Ships four skills: `conversational-memory`, `document-ingest`, `conversation-archive` (source-agnostic transcript ingest for WhatsApp, Telegram, Signal, LinkedIn DMs, Zoom, meeting minutes, iMessage, Slack), and `conversation-archive-enrich` (per-row operator-gated insight derivation over a named archive's chunks)."
|
|
4
4
|
tools:
|
|
5
5
|
- name: memory-search
|
|
6
6
|
publicAllowlist: true
|
|
@@ -65,6 +65,18 @@ tools:
|
|
|
65
65
|
- name: conversation-archive-enrich-rejection
|
|
66
66
|
publicAllowlist: false
|
|
67
67
|
adminAllowlist: false
|
|
68
|
+
- name: memory-report-write
|
|
69
|
+
publicAllowlist: false
|
|
70
|
+
adminAllowlist: true
|
|
71
|
+
- name: memory-report-read-latest
|
|
72
|
+
publicAllowlist: false
|
|
73
|
+
adminAllowlist: true
|
|
74
|
+
- name: memory-report-list
|
|
75
|
+
publicAllowlist: false
|
|
76
|
+
adminAllowlist: true
|
|
77
|
+
- name: memory-compiled-truth-history
|
|
78
|
+
publicAllowlist: false
|
|
79
|
+
adminAllowlist: true
|
|
68
80
|
- name: conversation-list
|
|
69
81
|
publicAllowlist: false
|
|
70
82
|
adminAllowlist: true
|
|
@@ -95,6 +107,12 @@ tools:
|
|
|
95
107
|
- name: conversation-memory-expunge
|
|
96
108
|
publicAllowlist: false
|
|
97
109
|
adminAllowlist: true
|
|
110
|
+
- name: memory-dream-run
|
|
111
|
+
publicAllowlist: false
|
|
112
|
+
adminAllowlist: true
|
|
113
|
+
- name: memory-review-queue
|
|
114
|
+
publicAllowlist: false
|
|
115
|
+
adminAllowlist: true
|
|
98
116
|
hidden:
|
|
99
117
|
- session-compact
|
|
100
118
|
- session-compact-status
|
|
@@ -168,9 +186,13 @@ Graph hygiene is **agent-directed, case by case** — no autonomous rule engine,
|
|
|
168
186
|
|
|
169
187
|
**Why soft-delete.** On 2026-04-20 an autonomous orphan-delete rule wiped 19 nodes in one `graph-prune-run` call; properties were unrecoverable (Neo4j Community has no PITR, no APOC). The cron, the `graph-prune-run` tool, and the rule engine have been removed. An earlier fix added the `:Trashed` label primitive so every soft-delete caller (`memory-delete`, `contact-delete`, the admin `/graph` page's trash button) runs the same shared `trashNode` helper — relationships preserved, unique keys snapshotted into `_trashedKeys`, audit lines `[trash:marked] by=<surface>` for cross-cut provenance.
|
|
170
188
|
|
|
171
|
-
##
|
|
189
|
+
## Dream-cycle maintenance — `memory-dream-run` / `memory-review-queue`
|
|
190
|
+
|
|
191
|
+
`memory-dream-run` is the operator-initiated graph-hygiene sweep — four phases (orphan detection on `:Person`/`:Organization`/`:Concept`/`:Project`; stale compiled-truth scan on `compiledTruthUpdatedAt`; mark edges to `:Trashed` targets with `staleTarget: true`; LLM-bounded citation audit on `:TimelineEvent`). Each run writes a `:Report` (via `memory-report-write` from Task 332) summarising counts; per-phase log lines `[dream-cycle:phase-<n>]` join on the run id. Failure of any phase logs `[dream-cycle:phase-<n>:error]` and continues — subsequent phases still run. Pass `phases: [4]` to run only the citation audit (e.g. after a bulk ingest); `maxEventsPerRun` defaults to 200 to cap LLM cost, with a resume cursor stored as `:Report.lastEventCursor` so subsequent runs continue from the last processed event. `memory-review-queue` is the read-only surface for `:OrphanCandidate` and `:CitationProposal` queues — counts plus a small sample per queue. Phase 5 (backlinks) and phase 6 (tag consistency) are deferred to follow-up tasks. Task 327; absorbs Task 330.
|
|
192
|
+
|
|
193
|
+
## Brain-capture diagnostic — `memory-signals-recent`
|
|
172
194
|
|
|
173
|
-
Read-only window into the
|
|
195
|
+
Read-only window into the extractive graph writes produced under this account. Returns recent `:MENTIONS` edges (`:Message-[:MENTIONS]->Entity`) and `:Idea` nodes interleaved by `observedAt`. Default window is 24 hours; pass `windowHours` (max 720) and `limit` (max 200) to scan a longer arc. Use this when the operator asks "what has the brain captured today?", "show me recent mentions of X", or "what ideas did I leave on the table this week?". Writes are produced by `database-operator`, either inline at admin discretion or via the once-per-turn background pass (`platform/plugins/admin/hooks/post-turn-graph-pass.sh`, Task 412 — supersedes the original `signal-detector` specialist + throttled hook from Task 303).
|
|
174
196
|
|
|
175
197
|
## Conversational Memory
|
|
176
198
|
|
|
@@ -46,6 +46,12 @@ import { conversationArchiveEnrichRejection } from "./tools/conversation-archive
|
|
|
46
46
|
import { fetchImageAsContent } from "./tools/image-fetch.js";
|
|
47
47
|
import { memoryTypedEdgePass } from "./tools/memory-typed-edge-pass.js";
|
|
48
48
|
import { sessionRetrospectiveSkipRate } from "./tools/session-retrospective-skip-rate.js";
|
|
49
|
+
import { memoryReportWrite } from "./tools/memory-report-write.js";
|
|
50
|
+
import { memoryReportReadLatest } from "./tools/memory-report-read-latest.js";
|
|
51
|
+
import { memoryReportList } from "./tools/memory-report-list.js";
|
|
52
|
+
import { memoryDreamRun } from "./tools/memory-dream-run.js";
|
|
53
|
+
import { memoryReviewQueue } from "./tools/memory-review-queue.js";
|
|
54
|
+
import { memoryCompiledTruthHistory } from "./tools/memory-compiled-truth-history.js";
|
|
49
55
|
import { getSession, closeDriver } from "./lib/neo4j.js";
|
|
50
56
|
import { embed } from "./lib/embeddings.js";
|
|
51
57
|
import { notTrashed } from "../../../../lib/graph-trash/dist/index.js";
|
|
@@ -581,6 +587,165 @@ server.tool("conversation-archive-enrich-rejection", "Record or undo a per-row r
|
|
|
581
587
|
}
|
|
582
588
|
});
|
|
583
589
|
process.stderr.write("[memory-mcp] registered tool=conversation-archive-enrich-rejection\n");
|
|
590
|
+
// ---------------------------------------------------------------------------
|
|
591
|
+
// :Report read surface (Task 332). Both tools are read-only and available
|
|
592
|
+
// to admin agents without the !readOnly gate. Routing for "latest X" /
|
|
593
|
+
// "show me X report" intents lives in the admin IDENTITY.md clause and in
|
|
594
|
+
// these tools' descriptions.
|
|
595
|
+
// ---------------------------------------------------------------------------
|
|
596
|
+
eagerTool(server, "memory-report-read-latest", "Return the most recent :Report node tagged with the given keyword. Reports are the durable output of admin workflows (daily briefing, dream cycle, ad-hoc analyses) — keyword-routed so 'latest briefing' or 'last night's dream cycle' resolve deterministically. Default limit=1 returns just the freshest match; raise it to scan a window. Prefer this over memory-search when the operator says 'latest', 'last', 'most recent', or 'show me the X report'.", {
|
|
597
|
+
keyword: z
|
|
598
|
+
.string()
|
|
599
|
+
.min(1)
|
|
600
|
+
.describe("Keyword to match against the report's keywords array (e.g. 'daily-briefing', 'dream-cycle'). Any-match — a report with this keyword in its array qualifies."),
|
|
601
|
+
limit: z
|
|
602
|
+
.number()
|
|
603
|
+
.int()
|
|
604
|
+
.min(1)
|
|
605
|
+
.max(20)
|
|
606
|
+
.optional()
|
|
607
|
+
.describe("Maximum number of reports to return, newest first. Default 1."),
|
|
608
|
+
}, async ({ keyword, limit }) => {
|
|
609
|
+
if (!accountId)
|
|
610
|
+
return refuseNoAccount("memory-report-read-latest");
|
|
611
|
+
try {
|
|
612
|
+
const result = await memoryReportReadLatest({ accountId, keyword, limit });
|
|
613
|
+
if (result.results.length === 0) {
|
|
614
|
+
return {
|
|
615
|
+
content: [
|
|
616
|
+
{
|
|
617
|
+
type: "text",
|
|
618
|
+
text: `No reports found tagged with keyword '${keyword}'.`,
|
|
619
|
+
},
|
|
620
|
+
],
|
|
621
|
+
};
|
|
622
|
+
}
|
|
623
|
+
const body = result.results
|
|
624
|
+
.map((r) => {
|
|
625
|
+
const wf = r.sourceWorkflow ? ` (workflow=${r.sourceWorkflow})` : "";
|
|
626
|
+
return `# ${r.title}${wf}\nreportId: ${r.reportId}\noccurredAt: ${r.occurredAt}\nkeywords: ${r.keywords.join(", ")}\n\n${r.body}`;
|
|
627
|
+
})
|
|
628
|
+
.join("\n\n---\n\n");
|
|
629
|
+
return { content: [{ type: "text", text: body }] };
|
|
630
|
+
}
|
|
631
|
+
catch (err) {
|
|
632
|
+
return {
|
|
633
|
+
content: [
|
|
634
|
+
{
|
|
635
|
+
type: "text",
|
|
636
|
+
text: `memory-report-read-latest failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
637
|
+
},
|
|
638
|
+
],
|
|
639
|
+
isError: true,
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
});
|
|
643
|
+
eagerTool(server, "memory-report-list", "List :Report nodes, newest first, with optional keyword and sourceWorkflow filters. Returns metadata only (title, keywords, sourceWorkflow, occurredAt, body length) — no body — so the agent can scan the catalogue without paying for full bodies. Use memory-report-read-latest to fetch the body of the single most recent match for a keyword.", {
|
|
644
|
+
keyword: z
|
|
645
|
+
.string()
|
|
646
|
+
.optional()
|
|
647
|
+
.describe("Optional keyword filter (any-match against the report's keywords array)."),
|
|
648
|
+
sourceWorkflow: z
|
|
649
|
+
.string()
|
|
650
|
+
.optional()
|
|
651
|
+
.describe("Optional exact-match filter on the originating workflow (e.g. 'daily-briefing', 'dream-cycle')."),
|
|
652
|
+
limit: z
|
|
653
|
+
.number()
|
|
654
|
+
.int()
|
|
655
|
+
.min(1)
|
|
656
|
+
.max(100)
|
|
657
|
+
.optional()
|
|
658
|
+
.describe("Page size, newest first. Default 20, max 100."),
|
|
659
|
+
offset: z
|
|
660
|
+
.number()
|
|
661
|
+
.int()
|
|
662
|
+
.min(0)
|
|
663
|
+
.optional()
|
|
664
|
+
.describe("Zero-based offset for pagination. Default 0."),
|
|
665
|
+
}, async ({ keyword, sourceWorkflow, limit, offset }) => {
|
|
666
|
+
if (!accountId)
|
|
667
|
+
return refuseNoAccount("memory-report-list");
|
|
668
|
+
try {
|
|
669
|
+
const result = await memoryReportList({
|
|
670
|
+
accountId,
|
|
671
|
+
keyword,
|
|
672
|
+
sourceWorkflow,
|
|
673
|
+
limit,
|
|
674
|
+
offset,
|
|
675
|
+
});
|
|
676
|
+
if (result.results.length === 0) {
|
|
677
|
+
return {
|
|
678
|
+
content: [
|
|
679
|
+
{
|
|
680
|
+
type: "text",
|
|
681
|
+
text: `No reports found${keyword ? ` for keyword '${keyword}'` : ""}${sourceWorkflow ? ` (workflow='${sourceWorkflow}')` : ""}.`,
|
|
682
|
+
},
|
|
683
|
+
],
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
const header = `Showing ${result.results.length} of ${result.total} reports.`;
|
|
687
|
+
const rows = result.results
|
|
688
|
+
.map((r) => {
|
|
689
|
+
const wf = r.sourceWorkflow ? ` workflow=${r.sourceWorkflow}` : "";
|
|
690
|
+
return `- ${r.occurredAt} ${r.title} [${r.keywords.join(",")}]${wf} body=${r.bodyChars}c id=${r.reportId.slice(0, 8)} elementId=${r.nodeId}`;
|
|
691
|
+
})
|
|
692
|
+
.join("\n");
|
|
693
|
+
return {
|
|
694
|
+
content: [{ type: "text", text: `${header}\n${rows}` }],
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
catch (err) {
|
|
698
|
+
return {
|
|
699
|
+
content: [
|
|
700
|
+
{
|
|
701
|
+
type: "text",
|
|
702
|
+
text: `memory-report-list failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
703
|
+
},
|
|
704
|
+
],
|
|
705
|
+
isError: true,
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
});
|
|
709
|
+
process.stderr.write("[memory-mcp] registered tool=memory-report-read-latest\n");
|
|
710
|
+
process.stderr.write("[memory-mcp] registered tool=memory-report-list\n");
|
|
711
|
+
eagerTool(server, "memory-compiled-truth-history", "Return the audit history of `compiledTruth` values for one entity (:Person | :Organization | :Concept), newest first, capped at 20 revisions. Each row carries the truth text, when it was written, and whether the rewriter or an operator authored it. Use when the operator asks 'how has my understanding of X changed over time?' or 'when did the agent introduce this claim about X?' — pass the entity's elementId and present the diff between adjacent revisions in chat.", {
|
|
712
|
+
nodeId: z
|
|
713
|
+
.string()
|
|
714
|
+
.describe("elementId of the :Person | :Organization | :Concept whose revision history is being read."),
|
|
715
|
+
}, async ({ nodeId }) => {
|
|
716
|
+
if (!accountId)
|
|
717
|
+
return refuseNoAccount("memory-compiled-truth-history");
|
|
718
|
+
try {
|
|
719
|
+
const result = await memoryCompiledTruthHistory({ accountId, nodeId });
|
|
720
|
+
if (result.revisions.length === 0) {
|
|
721
|
+
return {
|
|
722
|
+
content: [
|
|
723
|
+
{
|
|
724
|
+
type: "text",
|
|
725
|
+
text: `No compiled-truth revisions found for nodeId=${nodeId.slice(0, 12)}.`,
|
|
726
|
+
},
|
|
727
|
+
],
|
|
728
|
+
};
|
|
729
|
+
}
|
|
730
|
+
const header = `Showing ${result.revisions.length} revision(s) (newest first) for nodeId=${nodeId.slice(0, 12)}.`;
|
|
731
|
+
const rows = result.revisions
|
|
732
|
+
.map((r, i) => `## #${i + 1} ${r.writtenAt} source=${r.source} revisionId=${r.revisionId.slice(0, 8)}\n${r.truth}`)
|
|
733
|
+
.join("\n\n");
|
|
734
|
+
return { content: [{ type: "text", text: `${header}\n\n${rows}` }] };
|
|
735
|
+
}
|
|
736
|
+
catch (err) {
|
|
737
|
+
return {
|
|
738
|
+
content: [
|
|
739
|
+
{
|
|
740
|
+
type: "text",
|
|
741
|
+
text: `memory-compiled-truth-history failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
742
|
+
},
|
|
743
|
+
],
|
|
744
|
+
isError: true,
|
|
745
|
+
};
|
|
746
|
+
}
|
|
747
|
+
});
|
|
748
|
+
process.stderr.write("[memory-mcp] registered tool=memory-compiled-truth-history\n");
|
|
584
749
|
if (!readOnly) {
|
|
585
750
|
eagerTool(server, "memory-write", "Create a new node in the knowledge graph with automatic embedding computation. Every node must be created with at least one relationship — call memory-search first to find target elementIds. Writes targeting :Person, :UserProfile, :AdminUser, :Organization, :LocalBusiness, :CloudflareTunnel, or :CloudflareHostname additionally require an inbound :PRODUCED edge from :Task, :Conversation, or :Message. For autonomous workflows that own a :Task lifecycle, pass the Task's elementId as `producedByTaskId`. For direct asks during a conversation, omit `producedByTaskId` — the platform auto-injects the edge from the active conversation.", {
|
|
586
751
|
labels: z
|
|
@@ -643,20 +808,82 @@ if (!readOnly) {
|
|
|
643
808
|
};
|
|
644
809
|
}
|
|
645
810
|
});
|
|
646
|
-
server
|
|
811
|
+
eagerTool(server, "memory-report-write", "Persist a workflow's output as a durable :Report node. Reports are append-only, keyword-routed, and embedded for hybrid search. Use at the end of any operator-initiated workflow whose output the operator will want to recall later — daily briefing, dream cycle, ad-hoc analyses. Idempotent on (title, occurredAt-within-same-minute): a second call with the same title in the same minute returns the existing report instead of duplicating. The platform auto-parents the report to the active conversation (or to the account's :AdminUser when no conversation context exists).", {
|
|
812
|
+
title: z
|
|
813
|
+
.string()
|
|
814
|
+
.min(1)
|
|
815
|
+
.describe("Short report title (e.g. 'Daily briefing 2026-05-25', 'Dream cycle 2026-05-25 14:30'). Used for idempotency together with occurredAt — same title in the same minute deduplicates."),
|
|
816
|
+
body: z
|
|
817
|
+
.string()
|
|
818
|
+
.max(10_000)
|
|
819
|
+
.describe("Report body in markdown. Maximum 10,000 characters; trim or summarise above the cap."),
|
|
820
|
+
keywords: z
|
|
821
|
+
.array(z.string().min(1))
|
|
822
|
+
.min(1)
|
|
823
|
+
.describe("Keyword tags used by memory-report-read-latest routing. Always include the workflow slug (e.g. 'daily-briefing', 'dream-cycle') plus the date in 'YYYY-MM-DD' form. Add topic keywords when the report focuses on one subject."),
|
|
824
|
+
sourceWorkflow: z
|
|
825
|
+
.string()
|
|
826
|
+
.optional()
|
|
827
|
+
.describe("Originating workflow slug for indexed lookup (e.g. 'daily-briefing', 'dream-cycle'). Recommended when the report comes from a named skill or scheduled workflow."),
|
|
828
|
+
occurredAt: z
|
|
829
|
+
.string()
|
|
830
|
+
.optional()
|
|
831
|
+
.describe("ISO 8601 timestamp the report describes. Defaults to now(). Override only when persisting a backdated report."),
|
|
832
|
+
}, async ({ title, body, keywords, sourceWorkflow, occurredAt }) => {
|
|
833
|
+
if (!accountId)
|
|
834
|
+
return refuseNoAccount("memory-report-write");
|
|
835
|
+
try {
|
|
836
|
+
const result = await memoryReportWrite({
|
|
837
|
+
accountId,
|
|
838
|
+
title,
|
|
839
|
+
body,
|
|
840
|
+
keywords,
|
|
841
|
+
sourceWorkflow,
|
|
842
|
+
occurredAt,
|
|
843
|
+
conversationNodeId: process.env.CONVERSATION_NODE_ID,
|
|
844
|
+
});
|
|
845
|
+
const status = result.created
|
|
846
|
+
? `Report created`
|
|
847
|
+
: `Report already existed (idempotent: ${result.idempotencyReason ?? "match"})`;
|
|
848
|
+
return {
|
|
849
|
+
content: [
|
|
850
|
+
{
|
|
851
|
+
type: "text",
|
|
852
|
+
text: `${status} — reportId: ${result.reportId} elementId: ${result.nodeId}`,
|
|
853
|
+
},
|
|
854
|
+
],
|
|
855
|
+
};
|
|
856
|
+
}
|
|
857
|
+
catch (err) {
|
|
858
|
+
return {
|
|
859
|
+
content: [
|
|
860
|
+
{
|
|
861
|
+
type: "text",
|
|
862
|
+
text: `memory-report-write failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
863
|
+
},
|
|
864
|
+
],
|
|
865
|
+
isError: true,
|
|
866
|
+
};
|
|
867
|
+
}
|
|
868
|
+
});
|
|
869
|
+
server.tool("memory-update", "Update properties on an existing node in the knowledge graph. Finds the node by element ID, verifies account ownership, sets the provided properties, and recomputes the embedding. For :Person / :Organization / :Concept / :Project / :LocalBusiness, pass rewriteCompiledTruth=true to recompute the admin compiledTruth summary; when the label is in the public-twin set (:Organization / :Concept / :Project / :LocalBusiness) and the admin pass changes the truth, the public-twin compiledTruthPublic auto-rewrites in the same call. Pass compiledTruthPublic to override the public twin directly via chat. Direct writes to compiledTruth / compiledTruthUpdatedAt / compiledTruthSource / compiledTruthPublic / compiledTruthPublicUpdatedAt / compiledTruthPublicSource via the properties bag are refused — use the dedicated arguments.", {
|
|
647
870
|
nodeId: z.string().describe("Element ID of the node to update (from memory-search results)"),
|
|
648
871
|
properties: z
|
|
649
872
|
.record(z.string(), z.unknown())
|
|
650
|
-
.describe("Properties to set or overwrite. Restricted keys (embedding, accountId, createdAt) are ignored.
|
|
873
|
+
.describe("Properties to set or overwrite. Restricted keys (embedding, accountId, createdAt) are ignored. Compiled-truth fields are system-managed and rejected loudly here — use rewriteCompiledTruth or the compiledTruthPublic argument."),
|
|
651
874
|
rewriteCompiledTruth: z
|
|
652
875
|
.boolean()
|
|
653
876
|
.optional()
|
|
654
|
-
.describe("When true, run the compiled-truth rewriter against the node's most recent :TimelineEvent rows and
|
|
877
|
+
.describe("When true, run the compiled-truth rewriter against the node's most recent :TimelineEvent rows. Writes the admin compiledTruth, and (for :Organization / :Concept / :Project / :LocalBusiness, when the admin rewrite actually changes the text) also re-runs the rewriter in public mode and writes compiledTruthPublic. No-op admin rewrites skip the public pass. Valid on :Person / :Organization / :Concept / :Project / :LocalBusiness."),
|
|
655
878
|
forceRewrite: z
|
|
656
879
|
.boolean()
|
|
657
880
|
.optional()
|
|
658
|
-
.describe("Bypass the 7-day cooldown that protects
|
|
659
|
-
|
|
881
|
+
.describe("Bypass the 7-day cooldown that protects operator-edited compiledTruth and compiledTruthPublic fields from automatic rewriter passes. Set only when the operator explicitly asks for a refresh."),
|
|
882
|
+
compiledTruthPublic: z
|
|
883
|
+
.string()
|
|
884
|
+
.optional()
|
|
885
|
+
.describe("Operator override for the public-facing compiled-truth twin. Writes the supplied string verbatim, stamps source='operator', and resets the public-twin cooldown. Length cap: 2000 characters. Only valid on :Organization / :Concept / :Project / :LocalBusiness (:Person has no public twin)."),
|
|
886
|
+
}, async ({ nodeId, properties, rewriteCompiledTruth, forceRewrite, compiledTruthPublic }) => {
|
|
660
887
|
if (!accountId)
|
|
661
888
|
return refuseNoAccount("memory-update");
|
|
662
889
|
try {
|
|
@@ -671,28 +898,55 @@ if (!readOnly) {
|
|
|
671
898
|
},
|
|
672
899
|
rewriteCompiledTruth,
|
|
673
900
|
forceRewrite,
|
|
901
|
+
compiledTruthPublic,
|
|
674
902
|
});
|
|
675
903
|
const rewriterLine = (() => {
|
|
676
904
|
if (!result.rewriter)
|
|
677
905
|
return "";
|
|
678
906
|
if (result.rewriter.kind === "ok") {
|
|
679
|
-
return `\
|
|
907
|
+
return `\nAdmin compiled-truth rewritten: ${result.rewriter.charsBefore} → ${result.rewriter.charsAfter} chars.`;
|
|
680
908
|
}
|
|
681
909
|
if (result.rewriter.kind === "refused") {
|
|
682
910
|
return result.rewriter.reason === "operator-edit-cooldown"
|
|
683
|
-
? `\
|
|
684
|
-
: `\
|
|
911
|
+
? `\nAdmin rewriter refused: operator-edit cooldown (${result.rewriter.daysRemaining} day(s) remaining; pass forceRewrite=true to override).`
|
|
912
|
+
: `\nAdmin rewriter refused: no :TimelineEvent rows linked to this node.`;
|
|
685
913
|
}
|
|
686
914
|
if (result.rewriter.kind === "too-long") {
|
|
687
|
-
return `\
|
|
915
|
+
return `\nAdmin rewriter output exceeded ${result.rewriter.attemptedLen} chars after one retry; compiledTruth unchanged.`;
|
|
916
|
+
}
|
|
917
|
+
return `\nAdmin rewriter LLM call failed (cause=${result.rewriter.cause}); compiledTruth unchanged.`;
|
|
918
|
+
})();
|
|
919
|
+
const publicRewriterLine = (() => {
|
|
920
|
+
if (!result.publicRewriter)
|
|
921
|
+
return "";
|
|
922
|
+
if (result.publicRewriter.kind === "ok") {
|
|
923
|
+
return `\nPublic compiled-truth rewritten: ${result.publicRewriter.charsBefore} → ${result.publicRewriter.charsAfter} chars.`;
|
|
924
|
+
}
|
|
925
|
+
if (result.publicRewriter.kind === "refused") {
|
|
926
|
+
return result.publicRewriter.reason === "operator-edit-cooldown"
|
|
927
|
+
? `\nPublic rewriter refused: operator-edit cooldown (${result.publicRewriter.daysRemaining} day(s) remaining; pass forceRewrite=true to override).`
|
|
928
|
+
: `\nPublic rewriter refused: no :TimelineEvent rows linked to this node.`;
|
|
929
|
+
}
|
|
930
|
+
if (result.publicRewriter.kind === "too-long") {
|
|
931
|
+
return `\nPublic rewriter output exceeded ${result.publicRewriter.attemptedLen} chars after one retry; compiledTruthPublic unchanged.`;
|
|
932
|
+
}
|
|
933
|
+
return `\nPublic rewriter LLM call failed (cause=${result.publicRewriter.cause}); compiledTruthPublic unchanged.`;
|
|
934
|
+
})();
|
|
935
|
+
const overrideLine = (() => {
|
|
936
|
+
if (!result.publicOverride)
|
|
937
|
+
return "";
|
|
938
|
+
if (result.publicOverride.kind === "applied") {
|
|
939
|
+
return `\ncompiledTruthPublic overridden by operator: ${result.publicOverride.chars} chars.`;
|
|
688
940
|
}
|
|
689
|
-
return
|
|
941
|
+
return result.publicOverride.reason === "wrong-label"
|
|
942
|
+
? `\ncompiledTruthPublic override refused: label is not in the public-twin set (:Organization / :Concept / :Project / :LocalBusiness).`
|
|
943
|
+
: `\ncompiledTruthPublic override refused: exceeds 2000 character cap.`;
|
|
690
944
|
})();
|
|
691
945
|
return {
|
|
692
946
|
content: [
|
|
693
947
|
{
|
|
694
948
|
type: "text",
|
|
695
|
-
text: `Node updated: [${result.labels.join(", ")}] (ID: ${result.nodeId})${rewriterLine}`,
|
|
949
|
+
text: `Node updated: [${result.labels.join(", ")}] (ID: ${result.nodeId})${rewriterLine}${publicRewriterLine}${overrideLine}`,
|
|
696
950
|
},
|
|
697
951
|
],
|
|
698
952
|
};
|
|
@@ -2152,6 +2406,50 @@ server.tool("session-retrospective-skip-rate", "Return the ratio of recent sessi
|
|
|
2152
2406
|
await dbSession.close();
|
|
2153
2407
|
}
|
|
2154
2408
|
});
|
|
2409
|
+
// ===================================================================
|
|
2410
|
+
// Dream-cycle (Task 327, absorbs 330)
|
|
2411
|
+
// ===================================================================
|
|
2412
|
+
// Report MCP tools (Task 332) are registered earlier in this file.
|
|
2413
|
+
// Dream-run calls the same writer in-process for the per-run :Report.
|
|
2414
|
+
server.tool("memory-dream-run", "Operator-initiated dream-cycle: graph-hygiene sweep across five phases " +
|
|
2415
|
+
"(orphan detection, stale compiled-truth, edges to :Trashed targets, citation audit on :TimelineEvent, " +
|
|
2416
|
+
"prune compiled-truth revisions to newest 20 per entity). " +
|
|
2417
|
+
"Writes a :Report node at the end. Failure of any phase logs and continues. " +
|
|
2418
|
+
"Pass phases=[4] to run only the citation audit (e.g. after a bulk ingest), or phases=[5] for revision prune only.", {
|
|
2419
|
+
operatorId: z.string().optional().describe("Identifier of the operator who triggered the run; appears in the report and log line."),
|
|
2420
|
+
phases: z.array(z.number().int().min(1).max(5)).optional().describe("Subset of phases to run. Default [1,2,3,4,5]. Order is fixed."),
|
|
2421
|
+
maxEventsPerRun: z.number().int().positive().optional().describe("Phase 4 cap on :TimelineEvent rows scanned per run. Default 200."),
|
|
2422
|
+
}, async ({ operatorId, phases, maxEventsPerRun }) => {
|
|
2423
|
+
if (!accountId)
|
|
2424
|
+
return refuseNoAccount("memory-dream-run");
|
|
2425
|
+
try {
|
|
2426
|
+
const result = await memoryDreamRun({ accountId, operatorId, phases, maxEventsPerRun });
|
|
2427
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
2428
|
+
}
|
|
2429
|
+
catch (err) {
|
|
2430
|
+
return {
|
|
2431
|
+
content: [{ type: "text", text: `memory-dream-run failed: ${err instanceof Error ? err.message : String(err)}` }],
|
|
2432
|
+
isError: true,
|
|
2433
|
+
};
|
|
2434
|
+
}
|
|
2435
|
+
});
|
|
2436
|
+
server.tool("memory-review-queue", "Read-only summary of the dream-cycle's review queues (:OrphanCandidate, :CitationProposal). " +
|
|
2437
|
+
"Returns per-queue counts and a small sample so the operator can see whether to triage.", {
|
|
2438
|
+
sampleSize: z.number().int().positive().optional().describe("Sample rows per queue. Default 5, max 25."),
|
|
2439
|
+
}, async ({ sampleSize }) => {
|
|
2440
|
+
if (!accountId)
|
|
2441
|
+
return refuseNoAccount("memory-review-queue");
|
|
2442
|
+
try {
|
|
2443
|
+
const result = await memoryReviewQueue({ accountId, sampleSize });
|
|
2444
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
2445
|
+
}
|
|
2446
|
+
catch (err) {
|
|
2447
|
+
return {
|
|
2448
|
+
content: [{ type: "text", text: `memory-review-queue failed: ${err instanceof Error ? err.message : String(err)}` }],
|
|
2449
|
+
isError: true,
|
|
2450
|
+
};
|
|
2451
|
+
}
|
|
2452
|
+
});
|
|
2155
2453
|
// Cleanup on exit
|
|
2156
2454
|
process.on("SIGINT", async () => {
|
|
2157
2455
|
liveSchemaRuntime.cache.stop();
|