@rubytech/create-maxy-code 0.1.300 → 0.1.302
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/plugins/admin/PLUGIN.md +10 -10
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +93 -84
- package/payload/platform/plugins/admin/skills/public-agent-manager/SKILL.md +4 -4
- package/payload/platform/plugins/aeo/PLUGIN.md +1 -1
- package/payload/platform/plugins/business-assistant/skills/e-sign/SKILL.md +4 -4
- package/payload/platform/plugins/cloudflare/PLUGIN.md +1 -1
- package/payload/platform/plugins/cloudflare/references/manual-setup.md +14 -8
- package/payload/platform/plugins/cloudflare/references/reset-guide.md +1 -1
- package/payload/platform/plugins/cloudflare/skills/cloudflare/SKILL.md +5 -2
- package/payload/platform/plugins/contacts/PLUGIN.md +1 -1
- package/payload/platform/plugins/docs/references/admin-identity-gate.md +6 -6
- package/payload/platform/plugins/docs/references/admin-session.md +15 -15
- package/payload/platform/plugins/docs/references/admin-ui.md +27 -18
- package/payload/platform/plugins/docs/references/aeo.md +1 -1
- package/payload/platform/plugins/docs/references/deployment.md +3 -3
- package/payload/platform/plugins/docs/references/internals.md +28 -28
- package/payload/platform/plugins/docs/references/platform.md +21 -21
- package/payload/platform/plugins/docs/references/plugins-guide.md +3 -3
- package/payload/platform/plugins/docs/references/troubleshooting.md +6 -6
- package/payload/platform/plugins/docs/references/visitor-graph.md +3 -3
- package/payload/platform/plugins/email/PLUGIN.md +2 -2
- package/payload/platform/plugins/email/references/email-reference.md +2 -2
- package/payload/platform/plugins/linkedin-import/skills/linkedin-import/SKILL.md +2 -2
- package/payload/platform/plugins/memory/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/memory/PLUGIN.md +9 -9
- package/payload/platform/plugins/memory/references/graph-primitives.md +2 -2
- package/payload/platform/plugins/memory/references/schema-base.md +14 -14
- package/payload/platform/plugins/memory/references/transcript-formats/circleback.md +1 -1
- package/payload/platform/plugins/memory/references/transcript-formats/otter.md +1 -1
- package/payload/platform/plugins/memory/skills/conversation-archive/SKILL.md +6 -6
- package/payload/platform/plugins/memory/skills/conversation-archive-enrich/SKILL.md +1 -1
- package/payload/platform/plugins/memory/skills/conversation-archive-mcp/SKILL.md +8 -8
- package/payload/platform/plugins/memory/skills/document-ingest/SKILL.md +5 -5
- package/payload/platform/plugins/obsidian-import/skills/obsidian-import/references/daily-notes.md +1 -1
- package/payload/platform/plugins/prompt-optimiser/skills/prompt-optimiser/SKILL.md +1 -1
- package/payload/platform/plugins/slides/PROVENANCE.md +1 -1
- package/payload/platform/plugins/substack-import/skills/substack-import/SKILL.md +1 -1
- package/payload/platform/plugins/telegram/PLUGIN.md +1 -1
- package/payload/platform/plugins/url-get/PLUGIN.md +2 -2
- package/payload/platform/plugins/whatsapp/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/whatsapp/PLUGIN.md +5 -5
- package/payload/platform/plugins/whatsapp/references/channels-whatsapp.md +5 -5
- package/payload/platform/plugins/whatsapp/skills/manage-whatsapp-config/SKILL.md +1 -1
- package/payload/platform/plugins/workflows/PLUGIN.md +1 -1
- package/payload/platform/plugins/x-import/PLUGIN.md +2 -2
- package/payload/platform/plugins/x-import/skills/x-import/SKILL.md +1 -1
- package/payload/platform/scripts/__tests__/check-no-task-id-leaks.test.sh +125 -0
- package/payload/platform/scripts/__tests__/resume-tunnel.test.sh +253 -0
- package/payload/platform/scripts/check-no-task-id-leaks.mjs +62 -6
- package/payload/platform/scripts/resume-tunnel.sh +89 -5
- package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts +1 -0
- package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/system-prompt.js +23 -0
- package/payload/platform/services/claude-session-manager/dist/system-prompt.js.map +1 -1
- package/payload/platform/services/webchat-channel/dist/notification.d.ts +12 -0
- package/payload/platform/services/webchat-channel/dist/notification.d.ts.map +1 -1
- package/payload/platform/services/webchat-channel/dist/notification.js +29 -0
- package/payload/platform/services/webchat-channel/dist/notification.js.map +1 -1
- package/payload/platform/services/webchat-channel/dist/server.d.ts +1 -0
- package/payload/platform/services/webchat-channel/dist/server.d.ts.map +1 -1
- package/payload/platform/services/webchat-channel/dist/server.js +21 -6
- package/payload/platform/services/webchat-channel/dist/server.js.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/notification.d.ts +26 -0
- package/payload/platform/services/whatsapp-channel/dist/notification.d.ts.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/notification.js +40 -0
- package/payload/platform/services/whatsapp-channel/dist/notification.js.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/server.d.ts +4 -0
- package/payload/platform/services/whatsapp-channel/dist/server.d.ts.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/server.js +93 -9
- package/payload/platform/services/whatsapp-channel/dist/server.js.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/turn-follow.d.ts +22 -0
- package/payload/platform/services/whatsapp-channel/dist/turn-follow.d.ts.map +1 -0
- package/payload/platform/services/whatsapp-channel/dist/turn-follow.js +157 -0
- package/payload/platform/services/whatsapp-channel/dist/turn-follow.js.map +1 -0
- package/payload/platform/templates/specialists/agents/database-operator.md +1 -1
- package/payload/platform/templates/specialists/agents/librarian.md +2 -2
- package/payload/server/public/assets/AdminShell-CKQFuyG1.js +1 -0
- package/payload/server/public/assets/{Checkbox-BZ1XbA1t.js → Checkbox-CTB1YICm.js} +1 -1
- package/payload/server/public/assets/{Transcript-CaVix6W0.js → Transcript-ClvmtDzk.js} +1 -1
- package/payload/server/public/assets/admin-STC3C8ed.js +1 -0
- package/payload/server/public/assets/{arc-DxSm8tli.js → arc-BrTRjBIM.js} +1 -1
- package/payload/server/public/assets/architecture-YZFGNWBL-DqQB3T6j.js +1 -0
- package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-7JSAh0vL.js → architectureDiagram-Q4EWVU46-DJ9on7sZ.js} +1 -1
- package/payload/server/public/assets/{audio-attachment-mime-CFyc3gpD.js → audio-attachment-mime-B6GLbrSj.js} +3 -3
- package/payload/server/public/assets/{blockDiagram-DXYQGD6D-DLoVmHKD.js → blockDiagram-DXYQGD6D-CIcyQn0q.js} +1 -1
- package/payload/server/public/assets/{browser-ClnGUKp-.js → browser-Df57jxpr.js} +1 -1
- package/payload/server/public/assets/{c4Diagram-AHTNJAMY-Nv1G0o9P.js → c4Diagram-AHTNJAMY-Dt5dh_GR.js} +1 -1
- package/payload/server/public/assets/channel-NZeJdLVK.js +1 -0
- package/payload/server/public/assets/chat-B9ZI_e8i.js +1 -0
- package/payload/server/public/assets/{chunk-2KRD3SAO-BpqPMhoA.js → chunk-2KRD3SAO-BUq3MqUy.js} +1 -1
- package/payload/server/public/assets/{chunk-336JU56O-DgXHlVU_.js → chunk-336JU56O-CUzJH5Qt.js} +2 -2
- package/payload/server/public/assets/chunk-426QAEUC-DFKX5l0L.js +1 -0
- package/payload/server/public/assets/{chunk-4BX2VUAB-nwyZSZH8.js → chunk-4BX2VUAB-B4JUc3UA.js} +1 -1
- package/payload/server/public/assets/{chunk-4TB4RGXK-Tfxz9LHX.js → chunk-4TB4RGXK-HRO9fIIs.js} +1 -1
- package/payload/server/public/assets/{chunk-55IACEB6-6FMJQGXG.js → chunk-55IACEB6-MZdrLBuw.js} +1 -1
- package/payload/server/public/assets/{chunk-5FUZZQ4R-g2FgZF3D.js → chunk-5FUZZQ4R-XcAN5zlM.js} +1 -1
- package/payload/server/public/assets/{chunk-5PVQY5BW-DtLwXsPH.js → chunk-5PVQY5BW-PQ1GEsxo.js} +1 -1
- package/payload/server/public/assets/{chunk-67CJDMHE-K91CP5ZU.js → chunk-67CJDMHE-BVvCaqGd.js} +1 -1
- package/payload/server/public/assets/{chunk-7N4EOEYR-BGpCQhqK.js → chunk-7N4EOEYR-BlBbZdpM.js} +1 -1
- package/payload/server/public/assets/{chunk-AA7GKIK3-jkDbInck.js → chunk-AA7GKIK3-Ct-PgkGa.js} +1 -1
- package/payload/server/public/assets/{chunk-BSJP7CBP-mDosdzGs.js → chunk-BSJP7CBP-DZRRfCSz.js} +1 -1
- package/payload/server/public/assets/{chunk-CIAEETIT-DhBxewpQ.js → chunk-CIAEETIT-BoGEJiXe.js} +1 -1
- package/payload/server/public/assets/{chunk-EDXVE4YY-B7c-9Emg.js → chunk-EDXVE4YY-DiSmN31f.js} +1 -1
- package/payload/server/public/assets/{chunk-ENJZ2VHE-Cqhhncox.js → chunk-ENJZ2VHE-uhhokIvs.js} +1 -1
- package/payload/server/public/assets/{chunk-FMBD7UC4-BpDq6wj1.js → chunk-FMBD7UC4-CfCx5WI7.js} +1 -1
- package/payload/server/public/assets/{chunk-FOC6F5B3-Ds02-ktu.js → chunk-FOC6F5B3-Dj9E9gTu.js} +1 -1
- package/payload/server/public/assets/{chunk-ICPOFSXX-BTX5POFr.js → chunk-ICPOFSXX-DcBs_FNy.js} +2 -2
- package/payload/server/public/assets/{chunk-K5T4RW27-Du1PXXBU.js → chunk-K5T4RW27-7-VefyJD.js} +1 -1
- package/payload/server/public/assets/{chunk-KGLVRYIC-siasOAf8.js → chunk-KGLVRYIC-CqgTEVxm.js} +1 -1
- package/payload/server/public/assets/{chunk-LIHQZDEY-xf1rbZtf.js → chunk-LIHQZDEY-C0gzDXha.js} +1 -1
- package/payload/server/public/assets/{chunk-ORNJ4GCN-DeTLQ_LD.js → chunk-ORNJ4GCN-JXz23R1H.js} +1 -1
- package/payload/server/public/assets/{chunk-OYMX7WX6-DQ7_oVJn.js → chunk-OYMX7WX6-C2snCNq9.js} +1 -1
- package/payload/server/public/assets/chunk-QZHKN3VN-BWCCN0AM.js +1 -0
- package/payload/server/public/assets/{chunk-U2HBQHQK-CXmFUKgn.js → chunk-U2HBQHQK-CXCU7M-M.js} +1 -1
- package/payload/server/public/assets/{chunk-X2U36JSP-Bj8-8Wkz.js → chunk-X2U36JSP-CEdcSb28.js} +1 -1
- package/payload/server/public/assets/{chunk-XPW4576I-Con3TXqt.js → chunk-XPW4576I-Cckc75YS.js} +1 -1
- package/payload/server/public/assets/{chunk-YZCP3GAM-Dx8BHGWf.js → chunk-YZCP3GAM-4nN8AO7H.js} +1 -1
- package/payload/server/public/assets/{chunk-ZZ45TVLE-Dp4Q5Y-f.js → chunk-ZZ45TVLE-DcoBc8-7.js} +1 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-DPlCQCX2.js +1 -0
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-CqkQq74h.js +1 -0
- package/payload/server/public/assets/clone-CSykdD1A.js +1 -0
- package/payload/server/public/assets/{cose-bilkent-S5V4N54A-pZiCv69E.js → cose-bilkent-S5V4N54A-BFFro_t9.js} +1 -1
- package/payload/server/public/assets/{dagre-CLqhEgbL.js → dagre-CN5to_JM.js} +1 -1
- package/payload/server/public/assets/{dagre-KV5264BT-BCxE8iyk.js → dagre-KV5264BT-WYrPwDg0.js} +1 -1
- package/payload/server/public/assets/{data-DDjeQQ7e.js → data-BYiwvhSl.js} +1 -1
- package/payload/server/public/assets/{diagram-5BDNPKRD-slUEdZzz.js → diagram-5BDNPKRD-Dz2CiboN.js} +1 -1
- package/payload/server/public/assets/{diagram-G4DWMVQ6-DPcraMfu.js → diagram-G4DWMVQ6-r8h3Ip9O.js} +1 -1
- package/payload/server/public/assets/{diagram-MMDJMWI5-DR6luhGK.js → diagram-MMDJMWI5-CECcpGtr.js} +1 -1
- package/payload/server/public/assets/{diagram-TYMM5635-Jap1B4wA.js → diagram-TYMM5635-CHOcAyIP.js} +1 -1
- package/payload/server/public/assets/{erDiagram-SMLLAGMA-DJKUs2hO.js → erDiagram-SMLLAGMA-YIqKdPbS.js} +1 -1
- package/payload/server/public/assets/{file-download-DBMW0BNz.js → file-download-CU0xHXGF.js} +1 -1
- package/payload/server/public/assets/{flatten-Cl1Bc3dR.js → flatten-DhYYI_lo.js} +1 -1
- package/payload/server/public/assets/{flowDiagram-DWJPFMVM-C8W-ZZ9W.js → flowDiagram-DWJPFMVM-DaEvQWpj.js} +1 -1
- package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-DYCX4Xu2.js → ganttDiagram-T4ZO3ILL-x1hClEOB.js} +1 -1
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-V7Tx7_eY.js +1 -0
- package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-CZu_fpgS.js → gitGraphDiagram-UUTBAWPF-C-sZsh6Z.js} +1 -1
- package/payload/server/public/assets/{graph-CR74qec3.js → graph-BqdF9YLX.js} +1 -1
- package/payload/server/public/assets/{graph-labels-1Uz0ZBRd.js → graph-labels-Bv6MHqfG.js} +1 -1
- package/payload/server/public/assets/{graphlib-Bdp7TA4y.js → graphlib-CNkAqm2w.js} +1 -1
- package/payload/server/public/assets/info-OMHHGYJF-D3DqVPGn.js +1 -0
- package/payload/server/public/assets/infoDiagram-42DDH7IO-B_O-R0vN.js +2 -0
- package/payload/server/public/assets/{isEmpty-D1pGOD1J.js → isEmpty-DGhq_DBP.js} +1 -1
- package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-B7KfIX6z.js → ishikawaDiagram-UXIWVN3A-wHE-RwzZ.js} +1 -1
- package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-CSDwBfhF.js → journeyDiagram-VCZTEJTY-DQB6iThz.js} +1 -1
- package/payload/server/public/assets/jsx-runtime-DuiIXAPx.css +1 -0
- package/payload/server/public/assets/{kanban-definition-6JOO6SKY-CWzcEKTr.js → kanban-definition-6JOO6SKY-Ck08vXSA.js} +1 -1
- package/payload/server/public/assets/{line-BNQSlbYn.js → line-Cdc2FBEU.js} +1 -1
- package/payload/server/public/assets/{linear-BN6kGN93.js → linear-DhtUm5H7.js} +1 -1
- package/payload/server/public/assets/{mermaid-parser.core-CcFhkElq.js → mermaid-parser.core-CSYulPIL.js} +2 -2
- package/payload/server/public/assets/{mermaid.core-BsIeJ7Cc.js → mermaid.core-CP-hOmMh.js} +3 -3
- package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-CQVzj6D_.js → mindmap-definition-QFDTVHPH-GxlkvV1Z.js} +1 -1
- package/payload/server/public/assets/operator-lvmlNtX0.js +1 -0
- package/payload/server/public/assets/{ordinal-CBZeH4Yp.js → ordinal-CibZU3PM.js} +1 -1
- package/payload/server/public/assets/packet-4T2RLAQJ-BY7mgCTQ.js +1 -0
- package/payload/server/public/assets/page-lv61ROf4.js +1 -0
- package/payload/server/public/assets/pie-ZZUOXDRM-B9f6jN54.js +1 -0
- package/payload/server/public/assets/{pieDiagram-DEJITSTG-DnrQ9fyn.js → pieDiagram-DEJITSTG-Di6kWh9c.js} +1 -1
- package/payload/server/public/assets/{public-W-HholkU.js → public-B3qyifbI.js} +1 -1
- package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-BQWlCyWi.js → quadrantDiagram-34T5L4WZ-iNHC5bkO.js} +1 -1
- package/payload/server/public/assets/radar-PYXPWWZC-BKS8JOUe.js +1 -0
- package/payload/server/public/assets/{reduce-PuPlFPRX.js → reduce-Da0RBva0.js} +1 -1
- package/payload/server/public/assets/{requirementDiagram-MS252O5E-Du_vZhU1.js → requirementDiagram-MS252O5E-BBlLREXo.js} +1 -1
- package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-Cf6Z8GPQ.js → sankeyDiagram-XADWPNL6-qyDkP-Ca.js} +1 -1
- package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-CS8GVol8.js → sequenceDiagram-FGHM5R23-C0MJuS0m.js} +1 -1
- package/payload/server/public/assets/{stateDiagram-FHFEXIEX-Bv1NW2F1.js → stateDiagram-FHFEXIEX-aHTrlosa.js} +1 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-V2vo4BjY.js +1 -0
- package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-ClGYAsr0.js → timeline-definition-GMOUNBTQ-BlRZIxEp.js} +1 -1
- package/payload/server/public/assets/treeView-SZITEDCU-bIKzyIdw.js +1 -0
- package/payload/server/public/assets/treemap-W4RFUUIX-CQycQ4H9.js +1 -0
- package/payload/server/public/assets/{useSelectionMode-geYq13zs.js → useSelectionMode-FzQBlPMx.js} +1 -1
- package/payload/server/public/assets/{vennDiagram-DHZGUBPP-C2wTcxQT.js → vennDiagram-DHZGUBPP-s76adysx.js} +1 -1
- package/payload/server/public/assets/wardley-RL74JXVD-COr9o-nG.js +1 -0
- package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-D86-ivEx.js → wardleyDiagram-NUSXRM2D-xC8t2QGW.js} +1 -1
- package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-Ba5WAN8P.js → xychartDiagram-5P7HB3ND-DRcHg-Wj.js} +1 -1
- package/payload/server/public/browser.html +6 -6
- package/payload/server/public/chat.html +10 -9
- package/payload/server/public/data.html +5 -5
- package/payload/server/public/graph.html +8 -8
- package/payload/server/public/index.html +9 -10
- package/payload/server/public/operator.html +11 -10
- package/payload/server/public/public.html +7 -7
- package/payload/server/server.js +51 -6
- package/payload/server/public/assets/AdminShell-BFdBxg4P.js +0 -1
- package/payload/server/public/assets/admin-BK7xIr3o.js +0 -1
- package/payload/server/public/assets/architecture-YZFGNWBL-6g9jRVgc.js +0 -1
- package/payload/server/public/assets/channel-BgQLJanG.js +0 -1
- package/payload/server/public/assets/chat-ZTAhSoJV.js +0 -1
- package/payload/server/public/assets/chunk-426QAEUC-Dr_XVuUq.js +0 -1
- package/payload/server/public/assets/chunk-QZHKN3VN-DF4o9HRJ.js +0 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-DAbPKU6u.js +0 -1
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-BKuFQLcv.js +0 -1
- package/payload/server/public/assets/clone-DnNHGhNe.js +0 -1
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-g7mvu6IY.js +0 -1
- package/payload/server/public/assets/info-OMHHGYJF-DZqJuIkB.js +0 -1
- package/payload/server/public/assets/infoDiagram-42DDH7IO-cGfGccHz.js +0 -2
- package/payload/server/public/assets/jsx-runtime-BqGVUT1h.css +0 -1
- package/payload/server/public/assets/operator-QP_z97s8.js +0 -1
- package/payload/server/public/assets/packet-4T2RLAQJ-Hn8rlHpy.js +0 -1
- package/payload/server/public/assets/page-CLijftr1.js +0 -1
- package/payload/server/public/assets/pie-ZZUOXDRM-BPGWVqBm.js +0 -1
- package/payload/server/public/assets/radar-PYXPWWZC-DMdLrj3E.js +0 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-BGUCxPmp.js +0 -1
- package/payload/server/public/assets/treeView-SZITEDCU-CWxofUbI.js +0 -1
- package/payload/server/public/assets/treemap-W4RFUUIX-Cfgb_ZG0.js +0 -1
- package/payload/server/public/assets/wardley-RL74JXVD-B45olYjj.js +0 -1
- /package/payload/server/public/assets/{page-CWMpccrR.css → 500-CWMpccrR.css} +0 -0
- /package/payload/server/public/assets/{_baseFor-brRjpUOX.js → _baseFor-BFYpZGkN.js} +0 -0
- /package/payload/server/public/assets/{array-BGFCBI0e.js → array-CYkMkqnU.js} +0 -0
- /package/payload/server/public/assets/{cytoscape.esm-TvRJ2tGX.js → cytoscape.esm-DqlHsaog.js} +0 -0
- /package/payload/server/public/assets/{defaultLocale-CRZydyG6.js → defaultLocale-Beh6XjaL.js} +0 -0
- /package/payload/server/public/assets/{dist-Dbh7HrZX.js → dist-t9p-NJH2.js} +0 -0
- /package/payload/server/public/assets/{init-B8gtcn7T.js → init-Rr1s_RiX.js} +0 -0
- /package/payload/server/public/assets/{jsx-runtime-D3JkFIbi.js → jsx-runtime-Brvwqc85.js} +0 -0
- /package/payload/server/public/assets/{katex-RxgSu_dy.js → katex-pVJiI_rc.js} +0 -0
- /package/payload/server/public/assets/{path-DZF-JdEe.js → path-BAQ3hXlG.js} +0 -0
- /package/payload/server/public/assets/{preload-helper-CQ36nxok.js → preload-helper-B_l5jfgx.js} +0 -0
- /package/payload/server/public/assets/{rough.esm-6CnTHTkH.js → rough.esm-nHaDi0Kw.js} +0 -0
- /package/payload/server/public/assets/{src-BoaldmnP.js → src-DuPQVw-H.js} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# Visitor graph
|
|
1
|
+
# Visitor graph
|
|
2
2
|
|
|
3
|
-
Behavioural analytics that connect anonymous page visits to known `:Person` contacts. Replaces the anonymous click-through metric
|
|
3
|
+
Behavioural analytics that connect anonymous page visits to known `:Person` contacts. Replaces the anonymous click-through metric with a fully attributed graph.
|
|
4
4
|
|
|
5
5
|
## What this gives the operator
|
|
6
6
|
|
|
@@ -54,7 +54,7 @@ All under the `real-agent-buyers` plugin, admin-side only:
|
|
|
54
54
|
| `visitor-session-detail` | Full event timeline for one `:Session`. |
|
|
55
55
|
| `visitor-event-ingest` | Admin companion to `POST /v/event` for test harness work. |
|
|
56
56
|
| `visitor-backfill-from-logs` | One-shot importer: parses `[property-recommended]` and `[property-card-click]` log lines into the graph; used to recover late-arriving sessions and for ad-hoc forensics. |
|
|
57
|
-
| `mint-visitor-token` | Mints a signed token bound to a `:Person` for outbound URLs in `morning-round`, `lead-nurturing`, `vendor-updates`. Returns `{ token, expiryMs }`; the agent appends `&v=<token>` to `/listings/<slug>/click?session=<sk>`. Same secret file as the UI server; both processes share it via the wx-create pattern.
|
|
57
|
+
| `mint-visitor-token` | Mints a signed token bound to a `:Person` for outbound URLs in `morning-round`, `lead-nurturing`, `vendor-updates`. Returns `{ token, expiryMs }`; the agent appends `&v=<token>` to `/listings/<slug>/click?session=<sk>`. Same secret file as the UI server; both processes share it via the wx-create pattern. |
|
|
58
58
|
|
|
59
59
|
## Privacy
|
|
60
60
|
|
|
@@ -80,8 +80,8 @@ Manages the agent's own dedicated email account — IMAP for reading, SMTP for s
|
|
|
80
80
|
- **Compose to Drafts:** `email-draft` — builds the same MIME message as `email-send` (same `cc`/`bcc`/`attachments` rules; unlike a dispatched send, the stored draft keeps its `Bcc:` header, because the draft bytes are the only recipient carrier) but APPENDs it to the mailbox's `\Drafts` special-use folder with the `\Draft` flag instead of dispatching it. Nothing is sent. Pass `messageId` to compose a threaded draft-reply: the parent is resolved via IMAP and the draft carries `In-Reply-To`/`References` plus a `Re:` subject from the parent, exactly like `email-reply` (the `subject` argument is then optional and ignored). The success message names the Drafts folder and the APPEND UID so a saved draft is never indistinguishable from a silent no-op. Fails naming the mailbox when the server advertises no `\Drafts` special-use folder — no fallback folder is picked. Use when the operator wants to review and send by hand.
|
|
81
81
|
- **Revise a draft:** `email-draft-edit` — replace a stored draft addressed by its Drafts UID. Composes the full replacement from the parameters (same shape as `email-draft`, plus `targetUid` and an optional `messageId` for a threaded revision), APPENDs it with `\Draft`, then deletes the old UID — the replacement is always appended before the old copy is expunged, so a mid-cycle failure never leaves zero copies. An absent `targetUid` fails before any append. Nothing is sent. The confirmation names the folder and both the old and new UIDs.
|
|
82
82
|
- **Send a draft:** `email-draft-send` — dispatch a stored draft addressed by its Drafts UID via SMTP, then remove it from Drafts. Recipients come from the draft's own To/Cc/Bcc headers (the `\Drafts` copy keeps its `Bcc:`), deduped, sent from the agent alias with an explicit SMTP envelope. The draft is deleted only after SMTP accepts; an SMTP failure leaves it in place and surfaces verbatim. A draft with no recipients is refused before sending. Operator-approved sends only.
|
|
83
|
-
- **Ingestion — fetch:** `email-fetch` — list new IMAP messages since the stored high-water mark. Metadata + body preview only — including attachment names, mimetypes, and sizes so the operator sees what's attached before approving
|
|
84
|
-
- **Ingestion — apply:** `email-ingest` — apply the operator's per-message decisions to the staged batch. Each Message-ID must carry a disposition (`ingest` or `discard`). Approved messages land on `:ConversationArchive {source:'email'}` via the source-agnostic conversation-archive pipeline (one archive per thread, sessionised into `:Section` chunks). Attachment bytes are fetched, archived to `{accountDir}/archive/email/<uidValidity>-<uid>/<sha256>-<filename>` (0o600, 25 MB cap), and each attachment lands as a content-addressed `:DigitalDocument` keyed on `attachmentId = sha256(bytes)`; the `:HAS_ENCLOSURE` edge from the parent `:ConversationArchive` is wired by `memory-ingest` on its next pass via the `pendingArchiveEdges` queue
|
|
83
|
+
- **Ingestion — fetch:** `email-fetch` — list new IMAP messages since the stored high-water mark. Metadata + body preview only — including attachment names, mimetypes, and sizes so the operator sees what's attached before approving. No bytes downloaded, no graph writes. Stages the batch in-memory for `email-ingest`.
|
|
84
|
+
- **Ingestion — apply:** `email-ingest` — apply the operator's per-message decisions to the staged batch. Each Message-ID must carry a disposition (`ingest` or `discard`). Approved messages land on `:ConversationArchive {source:'email'}` via the source-agnostic conversation-archive pipeline (one archive per thread, sessionised into `:Section` chunks). Attachment bytes are fetched, archived to `{accountDir}/archive/email/<uidValidity>-<uid>/<sha256>-<filename>` (0o600, 25 MB cap), and each attachment lands as a content-addressed `:DigitalDocument` keyed on `attachmentId = sha256(bytes)`; the `:HAS_ENCLOSURE` edge from the parent `:ConversationArchive` is wired by `memory-ingest` on its next pass via the `pendingArchiveEdges` queue. Advances the inbox high-water mark only after a successful write.
|
|
85
85
|
- **Recall/history:** `email-graph-query` — search stored email archives in Neo4j by natural language, participant address, or date range. Each thread is one `:ConversationArchive {source:'email'}` with `:Section` chunks; operator participants attach via `:PARTICIPANT_IN` edges from `:Person`/`:AdminUser`. List mode surfaces a per-archive summary built from concatenated `:Section.summary` values; query mode runs vector search over the `section_embedding` index and dedups to the parent archive.
|
|
86
86
|
- **Status:** `email-status` — connection health and configuration state.
|
|
87
87
|
- **OTP:** `email-otp-extract` — poll for verification codes during service authentication.
|
|
@@ -35,7 +35,7 @@ The agent never displays stored passwords.
|
|
|
35
35
|
|
|
36
36
|
For any Google address, **require an app password — never the regular account password.** Google retired regular-password (basic auth) sign-in in May 2025, so the login password cannot work for IMAP/SMTP. Walk the operator through generating one: Google Account → Security → turn on 2-Step Verification (required first) → App passwords → Mail → generate. Paste that 16-character value as `password` to `email-setup`. Do not ask for the normal password at any point.
|
|
37
37
|
|
|
38
|
-
For a Workspace custom domain there is one extra dependency: the Workspace admin must allow app passwords (Admin console → Security). If the app password is rejected at `email-setup` — an authentication failure with an app password supplied — the tenant has app passwords disabled, and the account needs OAuth instead (
|
|
38
|
+
For a Workspace custom domain there is one extra dependency: the Workspace admin must allow app passwords (Admin console → Security). If the app password is rejected at `email-setup` — an authentication failure with an app password supplied — the tenant has app passwords disabled, and the account needs OAuth instead (not yet shipped). Relay that to the operator rather than retrying the password.
|
|
39
39
|
|
|
40
40
|
### Microsoft / Microsoft 365
|
|
41
41
|
|
|
@@ -146,7 +146,7 @@ When the user asks about stored emails, email history, or email recall — use `
|
|
|
146
146
|
|
|
147
147
|
There is no automated screener. The operator screens by eye during the `email-ingest` skill flow: `email-fetch` lists the new messages with sender, subject, and a body preview; the operator picks which ones to ingest and which to discard. A `screening` property of `clean` is stamped on every operator-approved row as a provenance marker (operator-approved), and discarded messages never enter the graph.
|
|
148
148
|
|
|
149
|
-
The previous Haiku classifier was removed
|
|
149
|
+
The previous Haiku classifier was removed; the auto-respond binary that depended on it was removed alongside the screener. Both are intentionally absent — the doctrine is that every LLM-bearing pipeline is operator-initiated.
|
|
150
150
|
|
|
151
151
|
## Security
|
|
152
152
|
|
|
@@ -55,7 +55,7 @@ When the owner is an external Person (non-operator archive), the anchor is the c
|
|
|
55
55
|
- LinkedIn skills → `:DefinedTerm` with `category: 'linkedin-skill'`.
|
|
56
56
|
- LinkedIn articles → `:CreativeWork` with `category: 'linkedin-article'`.
|
|
57
57
|
- LinkedIn recommendations → `:Review`.
|
|
58
|
-
- LinkedIn DMs → routed to the `conversation-archive` skill
|
|
58
|
+
- LinkedIn DMs → routed to the `conversation-archive` skill as a `--source linkedin-messages` invocation. Each DM thread becomes one `:ConversationArchive` parent with `:Section` chunks; no bespoke LinkedIn sublabels.
|
|
59
59
|
- Certifications → `:Credential` (genuinely new — no existing {{productName}} label fits `schema:EducationalOccupationalCredential`).
|
|
60
60
|
5. **Schema-base property names.** `givenName` / `familyName` / `telephone` / `dateSent` — not `firstName` / `phone` / `sentAt`. See [`platform/plugins/memory/references/schema-base.md`](../../../../plugins/memory/references/schema-base.md).
|
|
61
61
|
6. **Schema-base edge names.** `(:Person)-[:WORKS_FOR]->(:Organization)` for positions. Custom LinkedIn edges (`CONNECTED_ON_LINKEDIN`, `ENDORSED`) only where schema-base has no canonical name.
|
|
@@ -100,7 +100,7 @@ What's shipped today:
|
|
|
100
100
|
|------|-----------|----------------|
|
|
101
101
|
| `Profile.csv` | [profile.md](references/profile.md) | enrichment on `:UserProfile` (no new nodes) |
|
|
102
102
|
| `Connections.csv` | [connections.md](references/connections.md) | `(:AdminUser)-[:CONNECTED_ON_LINKEDIN {connectedOn}]->(:Person)` + `(:Person)-[:WORKS_FOR {title}]->(:Organization)` |
|
|
103
|
-
| `messages.csv` (and the three other DM CSVs) | conversation-archive
|
|
103
|
+
| `messages.csv` (and the three other DM CSVs) | conversation-archive | dispatched to `conversation-archive` skill with `--source linkedin-messages`; one `:ConversationArchive` per DM thread + `:Section` chunks + `(:Person)-[:PARTICIPANT_IN]->(archive)` |
|
|
104
104
|
|
|
105
105
|
Additional CSVs in the export (Positions, Education, Skills, Endorsements, Recommendations, Company Follows, Articles, Jobs, etc.) land as new references when the operator asks for them — each is its own follow-up.
|
|
106
106
|
|
|
@@ -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-write, memory-update, memory-edge (create/delete typed directed edges between pre-existing nodes), memory-update-by-name (repair surface that resolves an elementId from a (label, name, accountId) tuple when search cannot return one), memory-lookup-by-name (deterministic read surface that returns nodes by case-insensitive exact `name` match, optionally constrained by labels, bypassing the memory-search ranking stack so a present entity is never missed because a diluted query ranked it below the cut), 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 goes through memory-ingest; the dispatched specialist produces typed-section JSON in-turn from the loaded ontology. Operator-solicited reclassify of an already-stored thread-shaped KD (today: email-thread KDs) is exposed as `kd-classify <attachmentId>` — runs a body-growth gate (proceed iff body grew ≥25% since the last classify, or first-ever classify), stages the body to a temp file under `$ACCOUNT_DIR/tmp/`, and returns a dispatch envelope for the `librarian`; on success `memory-ingest` replaces the prior :Section children and stamps `lastClassifiedAt` + `lastClassifiedBodyLength` on the parent KD. Two modes: `document` (default) for unstructured PDF/web content → :KnowledgeDocument (keyed on `attachmentId`) + :Section, and `chat` for conversation transcripts → :ConversationArchive (keyed on `conversationIdentity`) + :Section chunks; two parent labels, two writer paths chosen by which identity property is set. Conversation-archive Phase 2 splits across two read-only tools
|
|
3
|
+
"description": "Graph memory plugin. Provides memory-search (with optional `fields` projection for known-shape lookups), memory-write, memory-update, memory-edge (create/delete typed directed edges between pre-existing nodes), memory-update-by-name (repair surface that resolves an elementId from a (label, name, accountId) tuple when search cannot return one), memory-lookup-by-name (deterministic read surface that returns nodes by case-insensitive exact `name` match, optionally constrained by labels, bypassing the memory-search ranking stack so a present entity is never missed because a diluted query ranked it below the cut), 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 goes through memory-ingest; the dispatched specialist produces typed-section JSON in-turn from the loaded ontology. Operator-solicited reclassify of an already-stored thread-shaped KD (today: email-thread KDs) is exposed as `kd-classify <attachmentId>` — runs a body-growth gate (proceed iff body grew ≥25% since the last classify, or first-ever classify), stages the body to a temp file under `$ACCOUNT_DIR/tmp/`, and returns a dispatch envelope for the `librarian`; on success `memory-ingest` replaces the prior :Section children and stamps `lastClassifiedAt` + `lastClassifiedBodyLength` on the parent KD. Two modes: `document` (default) for unstructured PDF/web content → :KnowledgeDocument (keyed on `attachmentId`) + :Section, and `chat` for conversation transcripts → :ConversationArchive (keyed on `conversationIdentity`) + :Section chunks; two parent labels, two writer paths chosen by which identity property is set. Conversation-archive Phase 2 splits across two read-only tools: `conversation-archive-list-chunks` pages chunk bodies for the dispatched specialist to read in-turn; `conversation-archive-derive-insights` converts the specialist's per-chunk claims into operator-facing proposals with per-kind cypher. `conversation-archive-enrich-rejection` records (or undoes) durable per-row rejections so already-triaged claims do not re-surface on re-runs. Ships five skills: `conversational-memory`, `document-ingest`, `conversation-archive` (source-agnostic transcript ingest for WhatsApp, Telegram, Signal, LinkedIn DMs, Zoom, meeting minutes, iMessage, Slack), `conversation-archive-mcp` (operator-initiated MCP-driven ingest for Granola, Otter, Circleback meetings — sibling of `conversation-archive` sharing the same writer surface and identity formula), and `conversation-archive-enrich` (per-row operator-gated insight derivation over a named archive's chunks). Ships three thinking-tool skills: `challenge` (adversarial retrieval — strongest counter-evidence from the graph for a given claim), `connect` (bridge-finding between two topics via shared graph neighbors), and `emerge` (clusters uncategorised KnowledgeDocument and Section nodes into operator-approved Concept proposals).",
|
|
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-write, memory-update, memory-edge (create/delete typed directed edges between pre-existing nodes), memory-update-by-name (repair surface that resolves an elementId from a (label, name, accountId) tuple when search cannot return one), memory-lookup-by-name (deterministic read surface that returns nodes by case-insensitive exact `name` match, optionally constrained by labels, bypassing the memory-search ranking stack so a present entity is never missed because a diluted query ranked it below the cut), 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 goes through memory-ingest; the dispatched specialist produces typed-section JSON in-turn from the loaded ontology. Operator-solicited reclassify of an already-stored thread-shaped KD (today: email-thread KDs) is exposed as `kd-classify <attachmentId>` — runs a body-growth gate (proceed iff body grew ≥25% since the last classify, or first-ever classify), stages the body to a temp file under `$ACCOUNT_DIR/tmp/`, and returns a dispatch envelope for the `librarian`; on success `memory-ingest` replaces the prior :Section children and stamps `lastClassifiedAt` + `lastClassifiedBodyLength` on the parent KD. Two modes: `document` (default) for unstructured PDF/web content → :KnowledgeDocument (keyed on `attachmentId`) + :Section, and `chat` for conversation transcripts → :ConversationArchive (keyed on `conversationIdentity`) + :Section chunks; two parent labels, two writer paths chosen by which identity property is set. Conversation-archive Phase 2 splits across two read-only tools
|
|
3
|
+
description: "Graph memory plugin. Provides memory-search (with optional `fields` projection for known-shape lookups), memory-write, memory-update, memory-edge (create/delete typed directed edges between pre-existing nodes), memory-update-by-name (repair surface that resolves an elementId from a (label, name, accountId) tuple when search cannot return one), memory-lookup-by-name (deterministic read surface that returns nodes by case-insensitive exact `name` match, optionally constrained by labels, bypassing the memory-search ranking stack so a present entity is never missed because a diluted query ranked it below the cut), 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 goes through memory-ingest; the dispatched specialist produces typed-section JSON in-turn from the loaded ontology. Operator-solicited reclassify of an already-stored thread-shaped KD (today: email-thread KDs) is exposed as `kd-classify <attachmentId>` — runs a body-growth gate (proceed iff body grew ≥25% since the last classify, or first-ever classify), stages the body to a temp file under `$ACCOUNT_DIR/tmp/`, and returns a dispatch envelope for the `librarian`; on success `memory-ingest` replaces the prior :Section children and stamps `lastClassifiedAt` + `lastClassifiedBodyLength` on the parent KD. Two modes: `document` (default) for unstructured PDF/web content → :KnowledgeDocument (keyed on `attachmentId`) + :Section, and `chat` for conversation transcripts → :ConversationArchive (keyed on `conversationIdentity`) + :Section chunks; two parent labels, two writer paths chosen by which identity property is set. Conversation-archive Phase 2 splits across two read-only tools: `conversation-archive-list-chunks` pages chunk bodies for the dispatched specialist to read in-turn; `conversation-archive-derive-insights` converts the specialist's per-chunk claims into operator-facing proposals with per-kind cypher. `conversation-archive-enrich-rejection` records (or undoes) durable per-row rejections so already-triaged claims do not re-surface on re-runs. Ships five skills: `conversational-memory`, `document-ingest`, `conversation-archive` (source-agnostic transcript ingest for WhatsApp, Telegram, Signal, LinkedIn DMs, Zoom, meeting minutes, iMessage, Slack), `conversation-archive-mcp` (operator-initiated MCP-driven ingest for Granola, Otter, Circleback meetings — sibling of `conversation-archive` sharing the same writer surface and identity formula), and `conversation-archive-enrich` (per-row operator-gated insight derivation over a named archive's chunks). Ships three thinking-tool skills: `challenge` (adversarial retrieval — strongest counter-evidence from the graph for a given claim), `connect` (bridge-finding between two topics via shared graph neighbors), and `emerge` (clusters uncategorised KnowledgeDocument and Section nodes into operator-approved Concept proposals)."
|
|
4
4
|
tools:
|
|
5
5
|
- name: memory-search
|
|
6
6
|
publicAllowlist: false
|
|
@@ -160,13 +160,13 @@ mcp-manifest: auto
|
|
|
160
160
|
|
|
161
161
|
# Memory
|
|
162
162
|
|
|
163
|
-
Provides read and write access to the Neo4j knowledge graph — search, structured writes, document ingestion, attachment management, conversation history, and user profiles. Tool routing is each agent's IDENTITY.md responsibility; this plugin describes what tools are available, not when to use them. The admin agent and specialist subagents call these tools directly via MCP. Public agents are toolless by construction
|
|
163
|
+
Provides read and write access to the Neo4j knowledge graph — search, structured writes, document ingestion, attachment management, conversation history, and user profiles. Tool routing is each agent's IDENTITY.md responsibility; this plugin describes what tools are available, not when to use them. The admin agent and specialist subagents call these tools directly via MCP. Public agents are toolless by construction: they receive no MCP tools from this plugin and have no graph access mid-conversation — their knowledge comes only from the baked KNOWLEDGE.md and SOUL assembled into the system prompt at spawn.
|
|
164
164
|
|
|
165
165
|
Tools are available via the `memory` MCP server.
|
|
166
166
|
|
|
167
167
|
## Ranking
|
|
168
168
|
|
|
169
|
-
Ranking is in-turn agent work — call `memory-search` for a candidate pool of 15–30 rows (use `limit` to size the pool), then order them yourself against the operator's criterion. The agent has the same context the server-side ranker used to have plus the conversation history the server never saw, so the right place to rank is the calling agent's turn.
|
|
169
|
+
Ranking is in-turn agent work — call `memory-search` for a candidate pool of 15–30 rows (use `limit` to size the pool), then order them yourself against the operator's criterion. The agent has the same context the server-side ranker used to have plus the conversation history the server never saw, so the right place to rank is the calling agent's turn. The `memory-rank` tool (server-side Haiku rerank against a `criteria` string) was removed — the criterion now lives in your reasoning, not in a separate OAuth round-trip.
|
|
170
170
|
|
|
171
171
|
## Search Output Size
|
|
172
172
|
|
|
@@ -176,15 +176,15 @@ Use `expandHops: 0` for listing and inventory queries (returns node properties o
|
|
|
176
176
|
|
|
177
177
|
## Hierarchy
|
|
178
178
|
|
|
179
|
-
Every node in the graph sits under a parent in the canonical chain `LocalBusiness → Project → Task | Person | Organisation | KnowledgeDocument | ConversationArchive` (
|
|
179
|
+
Every node in the graph sits under a parent in the canonical chain `LocalBusiness → Project → Task | Person | Organisation | KnowledgeDocument | ConversationArchive` (`:ConversationArchive` was promoted to a peer parent). `memory-write` and `memory-ingest` require the caller to name the parent (an `elementId` for the resolved parent node) and refuse the write if the parent does not exist or is at the wrong level of the chain. Specialists author the parent resolution — typically via `memory-search` against the brief — before invoking the write. Cross-hierarchy edges (a `:Person` linked to several Projects, a `:KnowledgeDocument` or `:ConversationArchive` about several Organisations) are normal and are added as separate `memory-write` calls; the hierarchy rule governs the containment edge that anchors the node, not the additional cross-edges that describe what the node relates to.
|
|
180
180
|
|
|
181
181
|
## Anchoring writes to provenance — `producedByTaskId` and the conversation env-stamp
|
|
182
182
|
|
|
183
|
-
Writes targeting `:Person`, `:UserProfile`, `:AdminUser`, `:Organization`, `:LocalBusiness`, `:CloudflareTunnel`, or `:CloudflareHostname` expect an inbound `:PRODUCED` edge whose source is one of `:Task`, `:Conversation`, or `:Message` (deterministic bootstrap paths run as `createdBy.agent === 'system'` and are exempt). The expectation is enforced as a soft warning, not a reject: when no qualifying edge resolves the storage primitive emits a single `[graph-write] warn reason=missing-provenance labels=<csv> agent=<agentLabel>` line on stderr and the write proceeds.
|
|
183
|
+
Writes targeting `:Person`, `:UserProfile`, `:AdminUser`, `:Organization`, `:LocalBusiness`, `:CloudflareTunnel`, or `:CloudflareHostname` expect an inbound `:PRODUCED` edge whose source is one of `:Task`, `:Conversation`, or `:Message` (deterministic bootstrap paths run as `createdBy.agent === 'system'` and are exempt). The expectation is enforced as a soft warning, not a reject: when no qualifying edge resolves the storage primitive emits a single `[graph-write] warn reason=missing-provenance labels=<csv> agent=<agentLabel>` line on stderr and the write proceeds. This was relaxed from a hard throw — the composer-spawned admin path inherits a bare per-account env that never receives the `SESSION_NODE_ID` stamp, so the throw was failing every direct admin contact-create / memory-write for a gated label. The two stamping surfaces below remain the right thing to use; the warn line is the operator-visible signal for how often the gap fires. Two surfaces feed the stamp:
|
|
184
184
|
|
|
185
185
|
- **Workflow path — `producedByTaskId`.** `memory-write` accepts an optional `producedByTaskId` parameter. When set, the platform composes an inbound `:PRODUCED` edge from that `:Task` node into the write's `relationships` array before the write runs. The Task and the new node must share the same `accountId`; mismatch is rejected loud. The typical pattern: call `work-create` at the start of an autonomous flow (onboarding skill, cloudflare tunnel-login) with `kind` and `raisedDuringConversationKey`; capture the returned `taskId`; pass it as `producedByTaskId` on every subsequent `memory-write` for one of the gated labels.
|
|
186
186
|
|
|
187
|
-
- **Direct-ask path — `SESSION_NODE_ID` env-stamp.** When `producedByTaskId` is not provided (typical of direct admin asks like "add Anneke as person"), `memory-write` falls back to the `SESSION_NODE_ID` env var. The admin server stamps it at PTY spawn time on both admin sessions and on the specialist subagent spawns the admin dispatches (
|
|
187
|
+
- **Direct-ask path — `SESSION_NODE_ID` env-stamp.** When `producedByTaskId` is not provided (typical of direct admin asks like "add Anneke as person"), `memory-write` falls back to the `SESSION_NODE_ID` env var. The admin server stamps it at PTY spawn time on both admin sessions and on the specialist subagent spawns the admin dispatches (listing-curator, content-producer, database-operator and friends inherit the same anchor). The value is the `sessionId` UUID of the active `:AdminConversation`; the wrapper's `injectConversationProvenance` MATCHes `(c:Conversation {sessionId, accountId})` so account isolation is enforced inside the natural key — a cross-account stamp returns zero rows and never injects. Autonomous spawns with no parent conversation (cron, scheduled-task) legitimately have no env-stamp; those must thread `producedByTaskId` via `work-create` instead. The env-stamp is invisible to the agent (no schema field changes); the LLM never decides whether to use it.
|
|
188
188
|
|
|
189
189
|
## Graph Hygiene
|
|
190
190
|
|
|
@@ -198,13 +198,13 @@ Graph hygiene is **agent-directed, case by case** — no autonomous rule engine,
|
|
|
198
198
|
**Conversation-level expunge.**
|
|
199
199
|
- `conversation-memory-expunge` — agent-invoked, scoped to one conversation. Scrubs a specified substring from every linked `:Memory` node's string properties with audit. Transcript Message nodes are immutable history and are never modified — only the derived `:Memory` payload that subsequent `memory-search` would re-surface.
|
|
200
200
|
|
|
201
|
-
**Single-node removal.** Use `memory-delete` by `elementId`. The node is marked `:Trashed` (relationships preserved, invisible to `memory-search` and any read filtered via `notTrashed`). For `KnowledgeDocument` and `ConversationArchive
|
|
201
|
+
**Single-node removal.** Use `memory-delete` by `elementId`. The node is marked `:Trashed` (relationships preserved, invisible to `memory-search` and any read filtered via `notTrashed`). For `KnowledgeDocument` and `ConversationArchive`, the trash cascades to linked `Section` and `Chunk` children. Restore via `memory-restore` (fails loudly when an active node already holds a unique slot the trashed one needs back). Hard removal happens later via `memory-empty-trash` after the grace period (default 30 days). GDPR cascades go through `contact-erase` (per Art. 17) — that bypasses the trash primitive deliberately. There is no bulk delete-by-shape.
|
|
202
202
|
|
|
203
203
|
**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.
|
|
204
204
|
|
|
205
205
|
## Dream-cycle maintenance — `memory-dream-run` / `memory-review-queue`
|
|
206
206
|
|
|
207
|
-
`memory-dream-run` is the operator-initiated graph-hygiene sweep — seven 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`; prune compiled-truth revisions to newest 20 per entity; materialise persistent `:BACKLINKS` edges derived from `:MENTIONS`/`:REFERENCES` (making reverse-link queries O(1)); normalise `tags[]` array properties on `:KnowledgeDocument` nodes and merge `:DefinedTerm {category:'obsidian-tag'}` variants. Each run writes a `:Report` (via `memory-report-write`) 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. Pass `phases: [4]` to run only the citation audit (e.g. after a bulk ingest); `maxEventsPerRun` defaults to 200 for phase 4, with a resume cursor stored as `:Report.lastEventCursor`. `memory-review-queue` is the read-only surface for `:OrphanCandidate` and `:CitationProposal` queues — counts plus a small sample per queue.
|
|
207
|
+
`memory-dream-run` is the operator-initiated graph-hygiene sweep — seven 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`; prune compiled-truth revisions to newest 20 per entity; materialise persistent `:BACKLINKS` edges derived from `:MENTIONS`/`:REFERENCES` (making reverse-link queries O(1)); normalise `tags[]` array properties on `:KnowledgeDocument` nodes and merge `:DefinedTerm {category:'obsidian-tag'}` variants. Each run writes a `:Report` (via `memory-report-write`) 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. Pass `phases: [4]` to run only the citation audit (e.g. after a bulk ingest); `maxEventsPerRun` defaults to 200 for phase 4, with a resume cursor stored as `:Report.lastEventCursor`. `memory-review-queue` is the read-only surface for `:OrphanCandidate` and `:CitationProposal` queues — counts plus a small sample per queue.
|
|
208
208
|
|
|
209
209
|
## Brain-capture diagnostic — `memory-brain-capture-recent`
|
|
210
210
|
|
|
@@ -262,7 +262,7 @@ Document ingestion of any kind — PDFs, text, transcripts, web pages, single fi
|
|
|
262
262
|
The skill drives a two-tool pipeline with the dispatched specialist's in-turn work in between:
|
|
263
263
|
|
|
264
264
|
1. **`memory-ingest-extract`** — pulls text from PDF/markdown/plain-text and caches it under the `attachmentId`. No chunking — the chunker has moved upstream into in-turn section classification.
|
|
265
|
-
2. **In-turn classification (specialist's work)** — the dispatched specialist reads the cached text and the loaded ontology, then produces typed-section JSON (`{kind, title, body, properties, anchorEdge, related}`) directly in its turn. Every `kind` must come from the ontology label set the writer enforces — the writer rejects out-of-set kinds, so the specialist verifies before emitting.
|
|
265
|
+
2. **In-turn classification (specialist's work)** — the dispatched specialist reads the cached text and the loaded ontology, then produces typed-section JSON (`{kind, title, body, properties, anchorEdge, related}`) directly in its turn. Every `kind` must come from the ontology label set the writer enforces — the writer rejects out-of-set kinds, so the specialist verifies before emitting. The `memory-classify` server tool that previously ran this step as a separate OAuth round-trip was deleted.
|
|
266
266
|
3. **`memory-ingest`** — writes typed graph nodes (Position, Service, Credential, etc.) anchored to `UserProfile` / `LocalBusiness` / `Person` / `Organization` via natural ontology edges, plus `(KnowledgeDocument)-[:REFERENCES]->(typed)` links. `Other` sections become generic `:Section:Other` nodes hanging off the document via `HAS_SECTION` (ontology-growth fallback) so free-form prose retrieval still works.
|
|
267
267
|
|
|
268
268
|
### Scope
|
|
@@ -40,7 +40,7 @@ token-limit discipline the upstream server enforces.
|
|
|
40
40
|
|
|
41
41
|
## When the memory tools are absent
|
|
42
42
|
|
|
43
|
-
The maxy MCP tool names are bound to the live registry
|
|
43
|
+
The maxy MCP tool names are bound to the live registry: the
|
|
44
44
|
canonical long name `mcp__plugin_memory_memory__<tool>` is generated from the
|
|
45
45
|
memory plugin's manifest, a build gate fails when any instruction names a tool
|
|
46
46
|
the registry does not serve, and a PostToolUse hook turns a missing-tool call
|
|
@@ -318,7 +318,7 @@ RETURN labels(node)[0] AS type,
|
|
|
318
318
|
LIMIT 20
|
|
319
319
|
```
|
|
320
320
|
|
|
321
|
-
|
|
321
|
+
Earlier the index was named `knowledge_fulltext` and covered only
|
|
322
322
|
`KnowledgeDocument | Section | Chunk`. Existing Pis pick up the rename on
|
|
323
323
|
the next install via `seed-neo4j.sh`.
|
|
324
324
|
|
|
@@ -39,13 +39,13 @@ When loading this reference, confirm which schema files were consulted by noting
|
|
|
39
39
|
| Position | `Position` | platform-native (analogue of `schema:OrganizationRole`) | — | `accountId`, `title`, `startDate` |
|
|
40
40
|
| WhatsApp Conversation (legacy) | `WhatsAppConversation` | extends `schema:Conversation` | — | `accountId`, `sessionId`, `archiveSourceFile`, `firstMessageAt`, `lastMessageAt`, `participantCount`, `messageCount`, `scope`, `createdByAgent`, `createdBySession`, `createdAt` |
|
|
41
41
|
| WhatsApp Message (legacy) | `WhatsAppMessage` | extends `schema:Message` | — | `accountId`, `sessionId`, `messageId`, `dateSent`, `body`, `senderName`, `sequenceIndex`, `scope`, `createdByAgent`, `createdBySession`, `createdAt` |
|
|
42
|
-
| Email thread | `ConversationArchive {source:'email'}` | One archive per thread (
|
|
42
|
+
| Email thread | `ConversationArchive {source:'email'}` | One archive per thread (the `:Email` label was retired; threads moved off `:KnowledgeDocument` onto the source-agnostic conversation-archive pipeline) | RFC 5322 over IMAP | `accountId`, `conversationIdentity` (sha256 of accountId + sortedParticipantElementIds + source), `source = 'email'`, `title` (subject of root message), `firstMessageAt`, `lastMessageAt`, `lastIngestedMessageHash`, `authorshipMode`. `:Section` chunks carry the message bodies via `HAS_SECTION` and the `NEXT` chain. |
|
|
43
43
|
| SocialPost | `SocialPost` | `schema:SocialMediaPosting` | — | `accountId`, `platform` (linkedin/twitter/instagram/…), `body`, `datePublished`, `authorshipMode` |
|
|
44
|
-
| VoiceProfile | `VoiceProfile` | platform-native (
|
|
45
|
-
| VoiceEdit | `VoiceEdit` | platform-native (
|
|
44
|
+
| VoiceProfile | `VoiceProfile` | platform-native (authorial-voice style card) | — | `accountId`, `adminUserId`, `styleCard`, `generatedAt`, `corpusSize`, `feedbackEntries` |
|
|
45
|
+
| VoiceEdit | `VoiceEdit` | platform-native (operator edit on an agent draft) | — | `accountId`, `adminUserId`, `originalText`, `editedText`, `intent`, `occurredAt` |
|
|
46
46
|
| AEO Audit | `AEOAudit` | platform-native (AEO audit history) | — | `accountId`, `auditId`, `target`, `score`, `heuristicsJson`, `runAt`, `createdByAgent`, `createdBySession`, `createdAt` |
|
|
47
47
|
| Idea | `Idea` | platform-native (original-thinking candidate extracted by the pre-turn graph pass) | — | `accountId`, `body`, `sessionId`, `scope`, `createdByAgent`, `createdBySession`, `createdAt` |
|
|
48
|
-
| TimelineEvent | `TimelineEvent` | platform-native (
|
|
48
|
+
| TimelineEvent | `TimelineEvent` | platform-native (compiled-truth + timeline doctrine) | — | `accountId`, `timelineEventId`, `occurredAt`, `kind`, `summary`, `source` |
|
|
49
49
|
|
|
50
50
|
**Branding properties on LocalBusiness:** `primaryColor`, `accentColor`, `backgroundColor`, `tagline` — optional, used to brand the public chat endpoint. Hex color values must match `#[0-9a-fA-F]{3,8}`. Logo and icon are linked via `HAS_BRAND_ASSET → ImageObject` with `purpose: "logo"` or `"icon"`.
|
|
51
51
|
|
|
@@ -53,7 +53,7 @@ When loading this reference, confirm which schema files were consulted by noting
|
|
|
53
53
|
|
|
54
54
|
**Personal-profile role on Person:** the optional `role` property on a `Person` node carries the value `"admin-personal"` when the node is the operator's personal-profile bootstrap (onboarding step 9 personal mode). The platform accepts `Person {role: "admin-personal"}` in lieu of a `LocalBusiness` so personal-mode users can complete onboarding without registering a business. Do not set `role: "admin-personal"` on customer or staff Person nodes — it's the personal-profile discriminator and only the bootstrap node should carry it.
|
|
55
55
|
|
|
56
|
-
**Organization is open by default.** The label covers both supplier-style use (a third-party company you do business with, where vCard-shaped contact data is meaningful) and reference-style use (an organization named in a CV, bio, or document where contact info is unknown or irrelevant). The validator enforces only `accountId` and `name`. For supplier-style use, gather `telephone` or `email` and a postal address when the source provides them — agent judgment, not validator-enforced.
|
|
56
|
+
**Organization is open by default.** The label covers both supplier-style use (a third-party company you do business with, where vCard-shaped contact data is meaningful) and reference-style use (an organization named in a CV, bio, or document where contact info is unknown or irrelevant). The validator enforces only `accountId` and `name`. For supplier-style use, gather `telephone` or `email` and a postal address when the source provides them — agent judgment, not validator-enforced. The row previously enforced `telephone or email` and the agent-write path obeyed it, but the document-ingest writer bypassed the validator and minted 23 contact-less Organizations on the first install that combined a CV PDF with a website ingest; those rows were schema-invalid against the prose contract while being valid against the actual write surface. Relaxing the rule resolves the straddle: the agent decides per case whether contacts belong, and search no longer hides ingest-created Orgs.
|
|
57
57
|
|
|
58
58
|
**Person is open by default.** Any Person property the agent judges useful is permitted; the set is comprehensive, not enumerated. The only hard requirement is `accountId` (the universal account-scoping property — see [feedback_accountid_must_be_uuid.md](../../../../.claude/projects/-Users-neo-getmaxy/memory/feedback_accountid_must_be_uuid.md)). Identifying fields (`givenName`, `familyName`, `email`, `telephone`, …) are agent-judged: a single field is enough to create a Person; the agent and operator manage dedup and conflicts downstream. Forbidden Properties (e.g. `Person.name` rejects in favour of `givenName` + `familyName`) and Schema.org synonyms (e.g. `phone` rejects in favour of `telephone`) still apply. The `profile-update` tool's `personFields` parameter is the personal-profile conversational surface, validating in `mode: "update"` so SET-only writes skip the (now-empty) required-property check (givenName/familyName were established at PIN setup).
|
|
59
59
|
|
|
@@ -63,9 +63,9 @@ When loading this reference, confirm which schema files were consulted by noting
|
|
|
63
63
|
|
|
64
64
|
**Position** carries a single role held by a `UserProfile` at an `Organization` — its title, dates, and per-role properties (e.g. achievement bullets in `description`, `endDate` for a finished role). Each role is its own node so it can carry its own structure; multiple Positions per UserProfile (career history); one Organization may host many Positions across many UserProfiles. Written by the `document-ingest` skill when a CV section classifies as `Position`. The natural pattern is `(:UserProfile)-[:HAS_POSITION]->(:Position)-[:AT]->(:Organization)` — never set the employer name as a property on Position; always create the AT edge to the (MERGEd) Organization.
|
|
65
65
|
|
|
66
|
-
**`authorshipMode` on content labels
|
|
66
|
+
**`authorshipMode` on content labels.** Every `:KnowledgeDocument`, `:ConversationArchive` (every chat transcript parent, including email threads under `source:'email'`), `:Message`, `:SocialPost`, and `:Section` write carries an `authorshipMode` enum drawn from `human-only | human-led-agent-assisted | agent-led-human-reviewed | agent-only | unknown`. The value records who actually wrote the content body, not who triggered the write. `human-only` means every word of the body came from the operator; `human-led-agent-assisted` is operator-authored with light agent polish (the operator chose every claim and structure); `agent-led-human-reviewed` is agent-drafted then operator-edited before send; `agent-only` is pure agent output the operator did not touch. The voice-mirror distillation walks the corpus where `authorshipMode IN ['human-only', 'human-led-agent-assisted']` — both modes carry voice signal. `:ConversationArchive` `:Section` chunks (WhatsApp / Telegram / Signal / Granola / Otter / Circleback exports ingested under `(:ConversationArchive)-[:HAS_SECTION]->(:Section)`) are first-class voice corpus and the highest-leverage signal source because chat captures the unguarded voice in a way email rarely does. Legacy nodes default to `unknown` until reclassified via the voice-mirror backfill flow. The per-label authorshipMode index on `:Section` covers transcript chunks without a secondary label. Writers that cannot determine the mode (e.g. bulk archive imports) write `unknown` rather than guessing.
|
|
67
67
|
|
|
68
|
-
**Voice profile pattern
|
|
68
|
+
**Voice profile pattern.** `(:AdminUser)-[:HAS_VOICE_PROFILE]->(:VoiceProfile)`. The styleCard is YAML serialized to a string property containing sentence-length distribution, register, favoured/avoided constructions and words, taboos, opening/closing patterns, and exemplar sentences. `(:VoiceProfile)-[:LEARNED_FROM]->(:KnowledgeDocument|:Message|:SocialPost|:Section)` links the profile to every node in its source corpus so the operator can audit what drove the distillation. Each operator edit on an agent draft becomes `(:VoiceEdit)-[:FEEDBACK_FOR]->(:VoiceProfile)` with the original/edited bodies plus a Haiku-summarised `intent` so the next distillation knows what to adjust.
|
|
69
69
|
|
|
70
70
|
**Idea** is a forward-only write surface for original-thinking candidates extracted from admin-side conversations by `database-operator` per-write briefs (dispatched by admin inline mid-turn, and during the on-demand `/insight` graph-completeness sweep). `body` is the verbatim operator statement; `sessionId` denormalises the source conversation; `scope` is always `"admin"`. Never written by any other path. No dedup or clustering at write time — let the brain accumulate first.
|
|
71
71
|
|
|
@@ -73,7 +73,7 @@ When loading this reference, confirm which schema files were consulted by noting
|
|
|
73
73
|
|
|
74
74
|
---
|
|
75
75
|
|
|
76
|
-
## Compiled truth + timeline
|
|
76
|
+
## Compiled truth + timeline
|
|
77
77
|
|
|
78
78
|
Every `:Person`, `:Organization`, and `:Concept` carries a two-part knowledge shape: a **compiled truth** above the rule (the agent's current best understanding of the subject, rewritten when evidence changes) and an **append-only timeline** below (dated events linked via `:HAS_TIMELINE_EVENT`, never edited).
|
|
79
79
|
|
|
@@ -83,11 +83,11 @@ Every `:Person`, `:Organization`, and `:Concept` carries a two-part knowledge sh
|
|
|
83
83
|
| `compiledTruthUpdatedAt` | datetime | Last rewriter or operator write. Epoch on first write. |
|
|
84
84
|
| `compiledTruthSource` | `'rewriter' \| 'operator'` | Last writer. `'operator'` marks passes shaped by an operator hint from chat; the rewriter respects a 7-day cooldown against further auto-rewriter passes unless `forceRewrite=true` is set. |
|
|
85
85
|
|
|
86
|
-
The compiled truth is system-managed: direct writes to any of the three properties via `memory-update` or `memory-write` are **rejected loudly** for normal callers.
|
|
86
|
+
The compiled truth is system-managed: direct writes to any of the three properties via `memory-update` or `memory-write` are **rejected loudly** for normal callers. The rewriter runs in the dispatched `compiled-truth-rewriter` specialist, which is the only sanctioned writer. The admin agent surfaces an operator request (e.g. "refresh Acme's compiled truth", or "fix the bio for Joel — say he's an angel investor now") by dispatching the specialist via the Task tool with the `nodeId`, an optional `operatorHint` (admin-twin correction), and an optional `publicOperatorHint` (public-twin correction). The specialist reads the node + 90-day timeline, computes the new summary, and writes it back via `memory-update`; the gate allows the write because `MAXY_SPECIALIST=compiled-truth-rewriter` is stamped on its env at spawn time.
|
|
87
87
|
|
|
88
|
-
### Revision history
|
|
88
|
+
### Revision history
|
|
89
89
|
|
|
90
|
-
Existing `:CompiledTruthRevision` rows remain readable via `memory-compiled-truth-history(nodeId)`; the dream-cycle phase 5 prune (keep newest 20 per entity) still runs.
|
|
90
|
+
Existing `:CompiledTruthRevision` rows remain readable via `memory-compiled-truth-history(nodeId)`; the dream-cycle phase 5 prune (keep newest 20 per entity) still runs. The inline single-TX revision stamp that used to fire on every rewriter SET was deleted. Reinstatement under the new specialist path is tracked as a follow-up; until then, new specialist writes do not extend the revision chain.
|
|
91
91
|
|
|
92
92
|
### Timeline events
|
|
93
93
|
|
|
@@ -101,7 +101,7 @@ Existing `:CompiledTruthRevision` rows remain readable via `memory-compiled-trut
|
|
|
101
101
|
mentioned | met | quoted | attended | transacted
|
|
102
102
|
```
|
|
103
103
|
|
|
104
|
-
The extractor refuses on ambiguity rather than collapsing to "other". Default on write is `mentioned`; the typed-edge post-write hook
|
|
104
|
+
The extractor refuses on ambiguity rather than collapsing to "other". Default on write is `mentioned`; the typed-edge post-write hook upgrades the kind asynchronously when it can.
|
|
105
105
|
|
|
106
106
|
### Write doctrine
|
|
107
107
|
|
|
@@ -237,7 +237,7 @@ The closed enumeration:
|
|
|
237
237
|
|
|
238
238
|
`Preface`, `Abstract` (alias for Summary / ExecutiveSummary), `Introduction`, `TableOfContents`, `Chapter`, `Conclusion`, `Appendix`, `Bibliography` (alias for References), `Glossary`, `Acknowledgments` — each becomes `:Section:<Kind>` linked to the document via `(:KnowledgeDocument)-[:HAS_SECTION]->` and chained to siblings via `(:Section)-[:NEXT]->(:Section)` in reading order.
|
|
239
239
|
|
|
240
|
-
`Conversation` — chat-mode chunks emitted by `memory-classify` with `mode='chat'`. Becomes `:Section` linked via `(:ConversationArchive)-[:HAS_SECTION]->`
|
|
240
|
+
`Conversation` — chat-mode chunks emitted by `memory-classify` with `mode='chat'`. Becomes `:Section` linked via `(:ConversationArchive)-[:HAS_SECTION]->` — same Section shape as document-mode, only the parent label differs. The parent `:ConversationArchive` carries `conversationIdentity` (and `source`); readers that need conversation-vs-document filtering match on the parent label rather than a property heuristic. Properties beyond the base section shape: `summary`, `keywords`, `firstMessageAt`, `lastMessageAt`, `participantNames`, `messageCount`, `archiveSha256`. The `:Section` validator rules (embedding, body, position) apply uniformly.
|
|
241
241
|
|
|
242
242
|
**Contract-clause kinds (HAS_SECTION + NEXT, plus special-case extras for two kinds):**
|
|
243
243
|
|
|
@@ -289,7 +289,7 @@ Patterns with high occurrence count are candidates for new section-kind labels.
|
|
|
289
289
|
|
|
290
290
|
---
|
|
291
291
|
|
|
292
|
-
## Visitor Graph
|
|
292
|
+
## Visitor Graph
|
|
293
293
|
|
|
294
294
|
Behavioural analytics — pixel events from operator-owned listing pages land here.
|
|
295
295
|
Cross-session attribution is via a signed-token cookie that the property-recommender
|
|
@@ -35,7 +35,7 @@ A Circleback meeting object carries the following fields relevant to ingest:
|
|
|
35
35
|
| `attendees[]` — each with `name`, `email` | Per-attendee resolution against `:AdminUser` / `:Person` (Step 4 of the skill) |
|
|
36
36
|
| `transcript[]` — each with `speaker`, `timestamp`, `text` | Rendered into turn-attributed `[<ISO>] <speaker>: <text>` lines for the typed-section JSON |
|
|
37
37
|
| `calendarEventId` / `event_id` | `calendarEventId` cross-reference property on `:ConversationArchive` when present |
|
|
38
|
-
| `actionItems[]` / `summary` | NOT used for `:Section` rows — provider-authored layers; downstream Phase 2 derivation
|
|
38
|
+
| `actionItems[]` / `summary` | NOT used for `:Section` rows — provider-authored layers; downstream Phase 2 derivation produces uniform typed claims from the transcript |
|
|
39
39
|
|
|
40
40
|
## Edge cases
|
|
41
41
|
|
|
@@ -42,7 +42,7 @@ An Otter conversation object carries the following fields relevant to ingest:
|
|
|
42
42
|
- **Speaker labels are pseudonymous** ("Speaker 1", "Speaker 2") when Otter could not identify a participant. Surface them to the operator at attendee-resolution time — the operator either matches "Speaker 1" to a known `:Person` (because they recognise the meeting) or blocks the attendee.
|
|
43
43
|
- **Live in-progress conversations** — Otter may expose a conversation that is still being transcribed. Skip these: the skill only ingests finished recordings. Detect by checking the provider's `status` / `is_live` field when present and FAIL with `phase=fetch reason="otter conversation <id> still in progress"`.
|
|
44
44
|
- **Attendee with no email** (free-text participant added in Otter): falls through to the per-attendee prompt loop.
|
|
45
|
-
- **Highlights / action items** in Otter's UI are provider-authored layers; do not ingest them as separate `:Section` rows. The transcript text is the source of truth; downstream Phase 2 derivation produces typed claims uniformly across sources
|
|
45
|
+
- **Highlights / action items** in Otter's UI are provider-authored layers; do not ingest them as separate `:Section` rows. The transcript text is the source of truth; downstream Phase 2 derivation produces typed claims uniformly across sources.
|
|
46
46
|
|
|
47
47
|
## What this reference is not
|
|
48
48
|
|
|
@@ -7,17 +7,17 @@ description: Source-agnostic ingest for conversation transcripts. One skill for
|
|
|
7
7
|
|
|
8
8
|
**Invoked from `specialists:librarian`** — the admin agent dispatches the librarian when the operator drops a conversation transcript; the librarian loads this skill.
|
|
9
9
|
|
|
10
|
-
**Default parent.** Every `:ConversationArchive` (
|
|
10
|
+
**Default parent.** Every `:ConversationArchive` (previously a `:KnowledgeDocument` carrying `conversationIdentity`) is anchored to the operator-confirmed owner (`:AdminUser` for the account owner, `:Person` for a third-party archive). The owner's elementId is derived inside the bash entry from `(ACCOUNT_ID, USER_ID)` env (see Identity below) and returned in the prepare JSON; it becomes the `anchorNodeId` argument to `memory-ingest` for every session of the run. Per-session `:Section` chunks attach to the archive via `:HAS_SECTION`, chained in reading order by `:NEXT`. Participant cross-edges from the archive to each distinct sender (`:Person` / `:AdminUser`) attach via `:PARTICIPANT_IN` — they describe the conversation, they do not substitute for the parent.
|
|
11
11
|
|
|
12
12
|
One skill, one bash entry, one writer. The pipeline is identical for every conversation source. The only source-specific code is the per-source normaliser (a pluggable function under [`platform/plugins/memory/mcp/src/lib/conversation-normalisers/`](../../mcp/src/lib/conversation-normalisers/)). New sources = a new normaliser file + an enum entry, never a new skill.
|
|
13
13
|
|
|
14
|
-
## Where the work lives
|
|
14
|
+
## Where the work lives
|
|
15
15
|
|
|
16
16
|
The deterministic work — parsing, sessionising, archiveSha256/conversationIdentity, prior-cursor lookup, delta slicing — lives in the bash entry and runs once per ingest. The LLM-judgment work — producing topic-bounded `:Section` chunks with summary + keywords for each session — lives in the dispatched specialist's per-turn work, one session at a time, against the turn-attributed text the prepare script returns. The specialist calls the `memory-ingest` MCP tool once per session with that session's classified chunks + the session's per-session cursor; the writer commits chunks + cursor advance in one Cypher transaction, so a kill mid-archive resumes from the next session on re-issue without re-classifying anything already written.
|
|
17
17
|
|
|
18
18
|
## Confirmed sources (Phase 0)
|
|
19
19
|
|
|
20
|
-
`whatsapp` ships today (relocated grammar from the retired `whatsapp-import` plugin). `x-dm` ships alongside the `x-import` plugin
|
|
20
|
+
`whatsapp` ships today (relocated grammar from the retired `whatsapp-import` plugin). `x-dm` ships alongside the `x-import` plugin — driven by the x-import skill, one invocation per dmConversation slice. Other sources land as needed:
|
|
21
21
|
- `telegram` — Telegram JSON export
|
|
22
22
|
- `signal` — Signal text export
|
|
23
23
|
- `linkedin-messages` — LinkedIn DM thread export (LinkedIn *connections* stay flat-dataset; only DMs route here)
|
|
@@ -26,7 +26,7 @@ The deterministic work — parsing, sessionising, archiveSha256/conversationIden
|
|
|
26
26
|
- `imessage` — iMessage backup export
|
|
27
27
|
- `slack` — Slack channel export
|
|
28
28
|
- `x-dm` — X (Twitter) Direct Messages, dispatched by the `x-import` skill from `data/direct-messages.js` / `data/direct-messages-group.js` — one bash invocation per dmConversation slice; senders carry numeric X senderIds resolved to `:Person` / `:AdminUser` upstream in the x-import skill
|
|
29
|
-
- `email` — RFC 5322 email thread, dispatched by the `email` plugin's writer
|
|
29
|
+
- `email` — RFC 5322 email thread, dispatched by the `email` plugin's writer. Participants resolve to existing `:Person` / `:AdminUser` by the `email` property on the canonical node — closed-set, no auto-creation, same discipline as LinkedIn DMs.
|
|
30
30
|
|
|
31
31
|
Until a normaliser ships, the bash entry loud-fails with the implemented set listed in the error.
|
|
32
32
|
|
|
@@ -118,7 +118,7 @@ For each `session` in the prepare JSON, in order:
|
|
|
118
118
|
|
|
119
119
|
1. **Read** the session's `turnText` (turn-attributed `[ISO timestamp] Sender: body\n…`).
|
|
120
120
|
|
|
121
|
-
2. **Produce a typed-section JSON chunking in-turn.** Split the turn text into topic-bounded `:Section` chunks following the standard ontology shape: each section carries `kind` (`Conversation` for a generic chunk, or a typed kind like `Topic` when the conversation explicitly establishes a typed entity), `title`, `summary` (1-3 sentences, ≤500 chars), `sourceStart` / `sourceEnd` (char offsets into this session's `turnText`), `properties` (carry `firstMessageAt`, `lastMessageAt`, and `messageCount` from the session — or finer per-chunk slices when the chunking subdivides), and `anchorEdge: null` (chunks attach via the parent's `:HAS_SECTION` chain). **Do NOT emit `body`.** The writer slices `:Section.body` server-side from the bin's per-session tempfile using the `(sourceStart, sourceEnd)` offsets —
|
|
121
|
+
2. **Produce a typed-section JSON chunking in-turn.** Split the turn text into topic-bounded `:Section` chunks following the standard ontology shape: each section carries `kind` (`Conversation` for a generic chunk, or a typed kind like `Topic` when the conversation explicitly establishes a typed entity), `title`, `summary` (1-3 sentences, ≤500 chars), `sourceStart` / `sourceEnd` (char offsets into this session's `turnText`), `properties` (carry `firstMessageAt`, `lastMessageAt`, and `messageCount` from the session — or finer per-chunk slices when the chunking subdivides), and `anchorEdge: null` (chunks attach via the parent's `:HAS_SECTION` chain). **Do NOT emit `body`.** The writer slices `:Section.body` server-side from the bin's per-session tempfile using the `(sourceStart, sourceEnd)` offsets — this closes the same LLM-paraphrase vector closed on the document path. Any `body` you emit is logged and ignored. Also produce a `documentKeywords` array of 3-8 topic keywords for the session, deduplicated across the archive.
|
|
122
122
|
|
|
123
123
|
3. **Call the `memory-ingest` MCP tool** with this session's payload (conversation-archive path):
|
|
124
124
|
|
|
@@ -181,7 +181,7 @@ If the first session's response carries `cleanedPriorChunks > 0` and `prepare.re
|
|
|
181
181
|
- `MATCH (a:ConversationArchive { conversationIdentity: $cid }) RETURN elementId(a), a.source, a.lastIngestedMessageAt, a.lastIngestedMessageHash` — `source` matches `--source`; counters agree with the aggregated MCP responses.
|
|
182
182
|
- `MATCH (a:ConversationArchive { conversationIdentity: $cid })-[:HAS_SECTION]->(c:Section) RETURN count(c)` — equals the aggregated `sectionCount`.
|
|
183
183
|
- `MATCH (p)-[:PARTICIPANT_IN]->(:ConversationArchive { conversationIdentity: $cid }) RETURN count(p)` — equals `participantsLinked` after a first-ingest.
|
|
184
|
-
- Phase 2 (`:Observation` / `:Task` / `:Preference` derivation) is its own follow-up
|
|
184
|
+
- Phase 2 (`:Observation` / `:Task` / `:Preference` derivation) is its own follow-up.
|
|
185
185
|
|
|
186
186
|
## What this is not
|
|
187
187
|
|
|
@@ -9,7 +9,7 @@ description: "Walk an existing ConversationArchive's chunks and derive high-conf
|
|
|
9
9
|
|
|
10
10
|
One skill. Walks one `:ConversationArchive`'s chunks in pages. Per chunk, the specialist reads the chunk body and emits zero or more high-confidence claims under the four-kind contract below; the claims pass through `mcp__plugin_memory_memory__conversation-archive-derive-insights` for per-kind cypher emission; per proposal, the operator decides `wire / skip / reject`. Both tools are read-only — the only writes happen through this skill's per-row dispatch.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
The LLM step runs in the specialist's turn. There is no server-side Haiku round-trip.
|
|
13
13
|
|
|
14
14
|
## When to invoke
|
|
15
15
|
|
|
@@ -11,12 +11,12 @@ description: Operator-initiated, MCP-driven transcript ingest for Granola / Otte
|
|
|
11
11
|
|
|
12
12
|
**Shared writer surface.** Every meeting ingested through this skill lands as a `:ConversationArchive` indistinguishable in shape from a file-drop ingest of the same meeting:
|
|
13
13
|
|
|
14
|
-
- Parent label: `:ConversationArchive
|
|
14
|
+
- Parent label: `:ConversationArchive`.
|
|
15
15
|
- Identity: `conversationIdentity = sha256(accountId + ":" + sortedParticipantElementIds.join(",") + ":" + source)`, with `source ∈ {"granola", "otter", "circleback"}`.
|
|
16
16
|
- Section chunks attach via `:HAS_SECTION` chained by `:NEXT`, same as the file-drop path.
|
|
17
17
|
- Participant cross-edges via `:PARTICIPANT_IN` from the archive to each resolved `:Person` / `:AdminUser`.
|
|
18
18
|
- Per-provider stable meeting ID rides as a cross-reference property on the parent: `granolaNoteId`, `otterConversationId`, or `circlebackMeetingId`. Multiple may co-exist if the same conversationIdentity ingests through two providers in sequence (e.g. operator imports a meeting from Otter, then re-imports the same participant set from Granola — the second ingest MERGEs into the same parent and stamps the additional provider ID).
|
|
19
|
-
- `calendarEventId` stamped on the parent when the provider supplies it. The MERGE-into-`:Meeting` link is downstream of this skill —
|
|
19
|
+
- `calendarEventId` stamped on the parent when the provider supplies it. The MERGE-into-`:Meeting` link is downstream of this skill — a planned writer-side change uses `calendarEventId` as the primary MERGE key when present.
|
|
20
20
|
|
|
21
21
|
**No bash entry. No per-source normaliser.** The provider's MCP server is the parser. The skill calls `list` and `get` tools the provider exposes, renders the returned shape into turn-attributed text in-turn, and calls `memory-ingest` directly. This is the structural reason the skill is sibling rather than nested — there is no `conversation-archive-ingest.sh` involvement on this path.
|
|
22
22
|
|
|
@@ -97,7 +97,7 @@ For each selected meeting, in chronological order:
|
|
|
97
97
|
```
|
|
98
98
|
conversationIdentity = sha256(accountId + ":" + sortedParticipantElementIds.join(",") + ":" + source)
|
|
99
99
|
```
|
|
100
|
-
Source is one of `granola | otter | circleback`. The same formula as the file-drop path; a meeting that ingests once via this skill and is later re-exported via the file-drop path will MERGE into the same parent only if the file-drop source enum matches — which it will not (file-drop has its own source enum). Cross-provider MERGE within this skill (Granola + Otter recordings of the same Zoom call landing as one parent) requires the
|
|
100
|
+
Source is one of `granola | otter | circleback`. The same formula as the file-drop path; a meeting that ingests once via this skill and is later re-exported via the file-drop path will MERGE into the same parent only if the file-drop source enum matches — which it will not (file-drop has its own source enum). Cross-provider MERGE within this skill (Granola + Otter recordings of the same Zoom call landing as one parent) requires the planned writer-side change to use `calendarEventId` as the primary key when present. Until that lands, same-meeting two-provider ingest writes TWO `:ConversationArchive` rows — name this explicitly in the operator summary.
|
|
101
101
|
|
|
102
102
|
4. **Produce typed-section JSON chunking in-turn.** Split the rendered turn text into topic-bounded `:Section` chunks following the standard ontology shape: each section carries `kind` (`Conversation` for a generic chunk, or a typed kind like `Topic` when the conversation explicitly establishes a typed entity), `title`, `body`, `summary` (1-3 sentences, ≤500 chars), `sourceStart` / `sourceEnd` (char offsets into the rendered turn text), `properties` (`firstMessageAt`, `lastMessageAt`, `messageCount` from the section slice), and `anchorEdge: null`. Produce a `documentKeywords` array of 3-8 topic keywords for the meeting.
|
|
103
103
|
|
|
@@ -118,7 +118,7 @@ For each selected meeting, in chronological order:
|
|
|
118
118
|
- `scope` ← `"admin"`.
|
|
119
119
|
- **Additional properties on the parent (stamped via the standard property bag):**
|
|
120
120
|
- `granolaNoteId` | `otterConversationId` | `circlebackMeetingId` — set the one that matches the source.
|
|
121
|
-
- `calendarEventId` — set when the provider supplied one. Stamped now; the
|
|
121
|
+
- `calendarEventId` — set when the provider supplied one. Stamped now; the planned writer-side change will MATCH on this for cross-provider MERGE.
|
|
122
122
|
- `cleanupPriorChunks` ← `false` always on this path. The MCP path has no `--rebuild` semantics; a destructive re-ingest is the operator's call via the file-drop skill if ever needed.
|
|
123
123
|
|
|
124
124
|
6. **If `memory-ingest` returns an error**, stop the loop and surface the error. Meetings already ingested are durable on the parent's `:ConversationArchive` MERGE; re-running with the same filter resumes from the next un-ingested meeting (the empty-delta short-circuit on re-issue skips already-written meetings).
|
|
@@ -129,7 +129,7 @@ After the last meeting lands, emit three operator-facing lines aggregated across
|
|
|
129
129
|
|
|
130
130
|
1. **Fetch summary.** `Fetched <count> <provider> meeting(s) (<dateRange.first> → <dateRange.last>). Attendees: <distinct attendee names>.`
|
|
131
131
|
2. **Ingest summary.** `Ingested <count> :ConversationArchive row(s), <totalChunks> chunks total. Topics: <unioned documentKeywords>.`
|
|
132
|
-
3. **Cross-reference summary.** `Stamped provider IDs: <provider>=<count>. calendarEventId stamped on <n>/<total>. Cross-provider MERGE on calendarEventId pending
|
|
132
|
+
3. **Cross-reference summary.** `Stamped provider IDs: <provider>=<count>. calendarEventId stamped on <n>/<total>. Cross-provider MERGE on calendarEventId pending.`
|
|
133
133
|
|
|
134
134
|
For an empty-delta re-import (every meeting already ingested under the same `conversationIdentity` + same content hash): emit only line 1 + a noop line `noop reason="all <count> meetings already ingested (empty-delta on every conversationIdentity)"`.
|
|
135
135
|
|
|
@@ -152,7 +152,7 @@ For an empty-delta re-import (every meeting already ingested under the same `con
|
|
|
152
152
|
- **Never auto-create a `:Person`.** Email-autoresolve binds to existing nodes only. The operator owns every create.
|
|
153
153
|
- **One skill, not three.** Per-provider variance lives in the reference docs, not in three near-duplicate skills.
|
|
154
154
|
- **Provider OAuth lives in the host.** Maxy never holds a Granola / Otter / Circleback token. The operator runs `claude mcp add` and authorises each provider in the provider's own UI.
|
|
155
|
-
- **`calendarEventId` is the future primary MERGE key.** This skill stamps it; the writer-side MERGE-on-calendar-event change is
|
|
155
|
+
- **`calendarEventId` is the future primary MERGE key.** This skill stamps it; the writer-side MERGE-on-calendar-event change is planned. Until then, cross-provider dedup of the same meeting requires the operator to use a single provider per meeting.
|
|
156
156
|
|
|
157
157
|
## Verification (post-write)
|
|
158
158
|
|
|
@@ -166,5 +166,5 @@ For an empty-delta re-import (every meeting already ingested under the same `con
|
|
|
166
166
|
- **Not** a file-drop fallback for Granola / Otter / Circleband transcript exports. If a meeting falls outside the provider's MCP retention window, the operator's recourse today is to export the transcript file and feed it to the file-drop `conversation-archive` skill — that path requires a per-provider normaliser to be implemented under `conversation-archive`; not in scope here.
|
|
167
167
|
- **Not** an OAuth manager. Tokens live in Claude Code's MCP host, not in maxy.
|
|
168
168
|
- **Not** a polling sync. Every ingest is operator-initiated.
|
|
169
|
-
- **Not** the writer-side MERGE-on-`calendarEventId` change. This skill *stamps* `calendarEventId`; the writer-side change is
|
|
170
|
-
- **Not** an insight-extraction pass. Phase 2 derivation (`:Observation` / `:Task` / `:Preference` / `:MENTIONS`)
|
|
169
|
+
- **Not** the writer-side MERGE-on-`calendarEventId` change. This skill *stamps* `calendarEventId`; the writer-side change is planned.
|
|
170
|
+
- **Not** an insight-extraction pass. Phase 2 derivation (`:Observation` / `:Task` / `:Preference` / `:MENTIONS`) applies uniformly to every source.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: document-ingest
|
|
3
|
-
description: Universal document ingestion — maps any unstructured document (PDF, text, transcript, web page) OR chat archive (WhatsApp `_chat.txt`) to ontologically-grounded graph nodes. The dispatched specialist reads the cached extract and produces typed-section JSON in-turn from the loaded ontology, then calls memory-ingest (
|
|
3
|
+
description: Universal document ingestion — maps any unstructured document (PDF, text, transcript, web page) OR chat archive (WhatsApp `_chat.txt`) to ontologically-grounded graph nodes. The dispatched specialist reads the cached extract and produces typed-section JSON in-turn from the loaded ontology, then calls memory-ingest (the separate server-side memory-classify step was removed). Triggers when the operator uploads or fetches a document or chat export for ingestion. One skill for every input shape — no per-doctype, no per-channel branching.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Document Ingest
|
|
@@ -100,7 +100,7 @@ After extract returns, emit one line in chat: "Ingesting `<filename>` (`<N>` cha
|
|
|
100
100
|
|
|
101
101
|
### 2. Classify in-turn (your work)
|
|
102
102
|
|
|
103
|
-
You produce typed-section JSON yourself from the cached text and the loaded ontology. There is no classifier tool —
|
|
103
|
+
You produce typed-section JSON yourself from the cached text and the loaded ontology. There is no classifier tool — it was deleted. Read the relevant ontology references first (`platform/plugins/memory/references/schema-base.md` and, when `brand.json#vertical` is set, the matching `schema-<vertical>.md`) so every `kind` you emit is a label the writer will accept.
|
|
104
104
|
|
|
105
105
|
**Document mode (default).** Build the JSON with this top-level shape:
|
|
106
106
|
|
|
@@ -145,7 +145,7 @@ Other (label fallback) | anchorEdge: null — section-kind closure mis
|
|
|
145
145
|
|
|
146
146
|
`documentEdges` covers party-like links the document itself names: `PARTY` for contract parties, `PARTICIPANT` for meeting/call attendees, `FROM`/`TO`/`CC` for email-header parties, `SPEAKER` for transcript voices. The `memory-ingest` tool definition spells out the exact field shape.
|
|
147
147
|
|
|
148
|
-
**Typed-entity edges target real entities, never `:Section
|
|
148
|
+
**Typed-entity edges target real entities, never `:Section`.** Edge types governed by the typed-edge allowlist — `FOUNDED`, `WORKS_AT`, `INVESTED_IN`, `ADVISES`, `MENTIONS`, `AUTHORED`, `ATTACHED_TO`, `REFERENCES`, `ATTENDED` — connect real entities (`Person`, `Organization`, etc.) per the (source, target) shapes declared in `platform/plugins/memory/mcp/src/lib/typed-edge-schema.ts`. They MUST NOT land on a `:Section` node as either endpoint. The writer rejects loud with the offending `(:Source)-[:EDGE]->(:Target)` triple if a classifier emission violates this.
|
|
149
149
|
|
|
150
150
|
When a section names entities related to the **document anchor** (e.g. a Team section on a company-page document whose anchor is the `:Organization`), set `edge.targetMode = "anchor"` on the related entry. The writer connects the related entity to the anchor instead of the section node:
|
|
151
151
|
|
|
@@ -296,7 +296,7 @@ Extract:
|
|
|
296
296
|
|
|
297
297
|
Process each text-based attachment (PDF, plain text, markdown). Skip images — they have no text to ingest.
|
|
298
298
|
|
|
299
|
-
The `storagePath` you pass to `memory-ingest-extract` is threaded through to the writer automatically (via the extract cache) and persisted on the `:KnowledgeDocument` as `sourcePath`, so the sidebar download resolver can serve the real file later
|
|
299
|
+
The `storagePath` you pass to `memory-ingest-extract` is threaded through to the writer automatically (via the extract cache) and persisted on the `:KnowledgeDocument` as `sourcePath`, so the sidebar download resolver can serve the real file later. You do not pass it to `memory-ingest` — the cache carries it. Web ingests are excepted: `memory-ingest-web`'s temp file is unlinked after extraction, so no `sourcePath` is persisted and the doc stays non-downloadable by design.
|
|
300
300
|
|
|
301
301
|
## Visibility scope
|
|
302
302
|
|
|
@@ -318,7 +318,7 @@ CV / personal-profile content defaults to `admin`. Pricing guides, services, FAQ
|
|
|
318
318
|
6. **Idempotent re-ingest.** `memory-ingest` is safe to re-run with the same `attachmentId`. Prior `:Section` children (any secondary label) and `:Project` standalones are deleted; prior KD-level edges are deleted by **provenance match** — every edge stamped `sourceDocumentId=<this attachmentId>` is dropped, regardless of edge type. The legacy `PARTY` edge type is also matched by name as a belt-and-braces for pre-stamping data. Any KD-level edge that this pipeline writes (classifier-emitted PARTY/PARTICIPANT/FROM/TO/CC/SPEAKER and agent-emitted brief-wired MENTIONS/REFERENCES/etc.) MUST carry the stamp — without it, the edge persists across re-ingests as a duplicate. MERGEd shared entities (Organizations, Persons referenced by other docs) are spared.
|
|
319
319
|
7. **Loud failure, never silent landfill.** Classifier failure aborts the ingest; nothing is written. Edge orphans are surfaced via `orphanCandidates`; the writer never synthesises edges to "fix" them.
|
|
320
320
|
8. **Brief is a deliverable, not a hint.** Step 4 is mandatory whenever the dispatch brief named entities. Returning *"document ingested as a KnowledgeDocument anchored to <X>"* without listing wired/unresolved brief entities is a silent failure — the document becomes an island anchored to one node while the named Persons/Organizations/Tasks stay disconnected, defeating adjacency-ranked `memory-search`. The `[document-ingest] wire-brief-entities` log line is the verification surface; `edges=0` while the brief listed entities is a regression.
|
|
321
|
-
9. **Typed-entity edges never target a `:Section
|
|
321
|
+
9. **Typed-entity edges never target a `:Section`.** Edge types declared in `TYPED_EDGE_ALLOWLIST` (`FOUNDED`, `WORKS_AT`, `INVESTED_IN`, `ADVISES`, `MENTIONS`, `AUTHORED`, `ATTACHED_TO`, `REFERENCES`, `ATTENDED`) must honour the (source, target) label shapes declared there. When the natural target is the document anchor (e.g. founders in a company-page Team section), set `edge.targetMode = "anchor"` on the related entry — the writer routes the edge to the anchor instead of the section. The writer rejects loud with the offending `(:Source)-[:EDGE]->(:Target)` triple on any classifier emission that violates the allowlist.
|
|
322
322
|
|
|
323
323
|
## Output contract
|
|
324
324
|
|
package/payload/platform/plugins/obsidian-import/skills/obsidian-import/references/daily-notes.md
CHANGED
|
@@ -16,7 +16,7 @@ Signals are evaluated in that order; the first match wins. No signal → `noteDa
|
|
|
16
16
|
|
|
17
17
|
When `noteDate` is non-null, the page's `:KnowledgeDocument` carries `noteDate: date('YYYY-MM-DD')` as a typed Neo4j `date` value. This is queryable: `MATCH (k:KnowledgeDocument) WHERE k.noteDate >= date('2026-01-01') RETURN k` returns every daily note from this year, without needing a separate `:Day` label.
|
|
18
18
|
|
|
19
|
-
No `:Day` node is created. The brief originally proposed `(:Page)-[:HAS_NOTE]->(:Day {date})`; the Maxy graph encodes the same information as a property on the page itself, mirroring `:Person.birthDate` and `:Event.eventDate`. Date-bearing event semantics live on `:TimelineEvent
|
|
19
|
+
No `:Day` node is created. The brief originally proposed `(:Page)-[:HAS_NOTE]->(:Day {date})`; the Maxy graph encodes the same information as a property on the page itself, mirroring `:Person.birthDate` and `:Event.eventDate`. Date-bearing event semantics live on `:TimelineEvent`, not here — a daily note is a document-at-a-date, not an event-on-a-date.
|
|
20
20
|
|
|
21
21
|
## Folder semantics
|
|
22
22
|
|
|
@@ -23,7 +23,7 @@ This skill is invoked two ways, and they want opposite outputs. Decide which one
|
|
|
23
23
|
- Do not invent requirements or add scope the user did not ask for. If the prompt was already clear, say so in one line and restate it tightly.
|
|
24
24
|
- The restated brief is bound by the three doctrines (see `/doctrine`): precise/concise/plain, no speculation, observability-first. If the user's wording carries a speculative claim or skips an observability step the doctrines require, the restatement corrects it rather than echoing the slip.
|
|
25
25
|
- Reminder — source of knowledge: every factual claim in the restated brief, and in the work you then do from it, traces to one of IDENTITY's four permissible sources (a graph read this session, a tool output returned this session, content the operator pasted into this conversation, or the body of a loaded skill such as platform-architecture). Pretraining recall is not a source. If a fact the brief needs does not trace to one of the four, do not bake it in from memory — read the graph, run the tool, load the skill, or ask the operator first.
|
|
26
|
-
- End the restated brief with the three-tier routing ladder, applied to the restated task in order. **(1)** Delegate to the specialist that owns the deliverable's type via the Agent tool: ownership covers origination, not just the final render, and the brief states the outcome plus the binding constraints, never the lines, anchors, or literal text to insert. **(2)** Only if no installed specialist owns it, load a skill from the admin-usable list with skill-load and do the work from this seat. **(3)** Only if neither fits, author it directly — freestyle inline authoring is the last resort, not a peer of the two routes above. The live specialist roster and admin-usable list are injected each turn by the standing prompt-optimiser directive; the ranked ladder is the classifier.
|
|
26
|
+
- End the restated brief with the three-tier routing ladder, applied to the restated task in order. **(1)** Delegate to the specialist that owns the deliverable's type via the Agent tool: ownership covers origination, not just the final render, and the brief states the outcome plus the binding constraints, never the lines, anchors, or literal text to insert. **(2)** Only if no installed specialist owns it, load a skill from the admin-usable list with skill-load and do the work from this seat. **(3)** Only if neither fits, author it directly — freestyle inline authoring is the last resort, not a peer of the two routes above. The live specialist roster and admin-usable list are injected each turn by the standing prompt-optimiser directive; the ranked ladder is the classifier. The closing line is not the trigger; the classifier is.
|
|
27
27
|
|
|
28
28
|
Then do the work from the restated task. Everything below this section is for the other mode and does not apply — stop reading here.
|
|
29
29
|
|
|
@@ -9,7 +9,7 @@ This plugin is a **frozen vendored copy** of the open-source Slides™ deck fram
|
|
|
9
9
|
| Pinned commit | `578af900d537e8a2bd8bfca11a4d72dc4e8fb588` |
|
|
10
10
|
| Upstream plugin version | `1.0.0` |
|
|
11
11
|
| License | MIT — see [`LICENSE`](LICENSE) (Copyright © 2026 Nils Smed) |
|
|
12
|
-
| Vendored | 2026-06-05
|
|
12
|
+
| Vendored | 2026-06-05 |
|
|
13
13
|
|
|
14
14
|
## What was copied
|
|
15
15
|
|