@rubytech/create-maxy-code 0.1.290 → 0.1.291
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/dist/index.js +14 -0
- package/package.json +1 -1
- package/payload/platform/lib/graph-style/dist/index.d.ts +2 -1
- package/payload/platform/lib/graph-style/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/graph-style/dist/index.js +14 -1
- package/payload/platform/lib/graph-style/dist/index.js.map +1 -1
- package/payload/platform/lib/graph-style/src/index.ts +15 -1
- package/payload/platform/package.json +3 -2
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +3 -1
- package/payload/platform/plugins/business-assistant/skills/e-sign/SKILL.md +21 -0
- package/payload/platform/plugins/docs/references/troubleshooting.md +2 -0
- package/payload/platform/plugins/whatsapp/PLUGIN.md +4 -0
- package/payload/platform/plugins/whatsapp/references/channels-whatsapp.md +28 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts +6 -0
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js +65 -3
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/index.js +8 -0
- package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/webchat-channel-mcp.d.ts +27 -0
- package/payload/platform/services/claude-session-manager/dist/webchat-channel-mcp.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/webchat-channel-mcp.js +45 -0
- package/payload/platform/services/claude-session-manager/dist/webchat-channel-mcp.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/webchat-channel-store.d.ts +29 -0
- package/payload/platform/services/claude-session-manager/dist/webchat-channel-store.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/webchat-channel-store.js +121 -0
- package/payload/platform/services/claude-session-manager/dist/webchat-channel-store.js.map +1 -0
- package/payload/platform/services/webchat-channel/dist/notification.d.ts +55 -0
- package/payload/platform/services/webchat-channel/dist/notification.d.ts.map +1 -0
- package/payload/platform/services/webchat-channel/dist/notification.js +59 -0
- package/payload/platform/services/webchat-channel/dist/notification.js.map +1 -0
- package/payload/platform/services/webchat-channel/dist/server.d.ts +3 -0
- package/payload/platform/services/webchat-channel/dist/server.d.ts.map +1 -0
- package/payload/platform/services/webchat-channel/dist/server.js +179 -0
- package/payload/platform/services/webchat-channel/dist/server.js.map +1 -0
- package/payload/platform/services/webchat-channel/package.json +20 -0
- package/payload/platform/services/whatsapp-channel/dist/notification.d.ts +20 -15
- package/payload/platform/services/whatsapp-channel/dist/notification.d.ts.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/notification.js +23 -19
- package/payload/platform/services/whatsapp-channel/dist/notification.js.map +1 -1
- package/payload/server/public/assets/AdminShell-BgPlhiCM.js +1 -0
- package/payload/server/public/assets/{Checkbox-Dmc64qme.js → Checkbox-DWx2NcU9.js} +1 -1
- package/payload/server/public/assets/SidebarSplitter-CidskiAr.js +1 -0
- package/payload/server/public/assets/Transcript-iuM5b4JT.js +1 -0
- package/payload/server/public/assets/admin-Cdz561nK.js +1 -0
- package/payload/server/public/assets/{arc-BW1WhwmV.js → arc-yRinR5h4.js} +1 -1
- package/payload/server/public/assets/architecture-YZFGNWBL-B2UAw-4R.js +1 -0
- package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-NkTPStkv.js → architectureDiagram-Q4EWVU46-AtvCAoKj.js} +1 -1
- package/payload/server/public/assets/{blockDiagram-DXYQGD6D-DKsRoptY.js → blockDiagram-DXYQGD6D-CwUSTR3t.js} +1 -1
- package/payload/server/public/assets/browser-Bwd_rYu9.js +1 -0
- package/payload/server/public/assets/{c4Diagram-AHTNJAMY-4SSchEmY.js → c4Diagram-AHTNJAMY-Btkecaf-.js} +1 -1
- package/payload/server/public/assets/channel-1MEkxMOf.js +1 -0
- package/payload/server/public/assets/chat-wbKqew6m.js +1 -0
- package/payload/server/public/assets/{chunk-2KRD3SAO-CKn-bEkI.js → chunk-2KRD3SAO-DAJbbBPY.js} +1 -1
- package/payload/server/public/assets/{chunk-336JU56O-C8szFVD5.js → chunk-336JU56O-fJXOXFRr.js} +2 -2
- package/payload/server/public/assets/chunk-426QAEUC-28Pgly06.js +1 -0
- package/payload/server/public/assets/{chunk-4BX2VUAB-B2mSmC97.js → chunk-4BX2VUAB-BTCTbhtf.js} +1 -1
- package/payload/server/public/assets/{chunk-4TB4RGXK-YsoBMSBN.js → chunk-4TB4RGXK-IFvQOpiF.js} +1 -1
- package/payload/server/public/assets/{chunk-55IACEB6-JLT9m7Hd.js → chunk-55IACEB6-BY026nUv.js} +1 -1
- package/payload/server/public/assets/{chunk-5FUZZQ4R-fvCSUwuQ.js → chunk-5FUZZQ4R-DD-XOAcm.js} +1 -1
- package/payload/server/public/assets/{chunk-5PVQY5BW-BKCeZJLg.js → chunk-5PVQY5BW-Ck-5mL2A.js} +1 -1
- package/payload/server/public/assets/{chunk-67CJDMHE-CCdP3Pdx.js → chunk-67CJDMHE-Dl7bLJGe.js} +1 -1
- package/payload/server/public/assets/{chunk-7N4EOEYR-bXgVzdM9.js → chunk-7N4EOEYR-CFBOyqnD.js} +1 -1
- package/payload/server/public/assets/{chunk-AA7GKIK3-CYeDvY8B.js → chunk-AA7GKIK3-BnnqjSlh.js} +1 -1
- package/payload/server/public/assets/{chunk-BSJP7CBP-U0Fi-Ers.js → chunk-BSJP7CBP-DKCTDAZE.js} +1 -1
- package/payload/server/public/assets/{chunk-CIAEETIT-C8QrV6Gg.js → chunk-CIAEETIT-jjeAEkQy.js} +1 -1
- package/payload/server/public/assets/{chunk-EDXVE4YY-DhpopONL.js → chunk-EDXVE4YY-BPqvO0TO.js} +1 -1
- package/payload/server/public/assets/{chunk-ENJZ2VHE-CwpQJ9IE.js → chunk-ENJZ2VHE-PN6Hbw2D.js} +1 -1
- package/payload/server/public/assets/{chunk-FMBD7UC4-DxAQS3UB.js → chunk-FMBD7UC4-B0zglVlf.js} +1 -1
- package/payload/server/public/assets/{chunk-FOC6F5B3-CyIK3zeY.js → chunk-FOC6F5B3-COVX_UuK.js} +1 -1
- package/payload/server/public/assets/{chunk-ICPOFSXX-D5p7fCDj.js → chunk-ICPOFSXX-DkmwqyfQ.js} +2 -2
- package/payload/server/public/assets/{chunk-K5T4RW27-7GcDzjyJ.js → chunk-K5T4RW27-Bav1KRIS.js} +1 -1
- package/payload/server/public/assets/{chunk-KGLVRYIC-B7pAr9hf.js → chunk-KGLVRYIC-nj7A12cJ.js} +1 -1
- package/payload/server/public/assets/{chunk-LIHQZDEY-DEoU2jrz.js → chunk-LIHQZDEY-BmMkL9jD.js} +1 -1
- package/payload/server/public/assets/{chunk-ORNJ4GCN-BwMFuN24.js → chunk-ORNJ4GCN-BvGwrI3j.js} +1 -1
- package/payload/server/public/assets/{chunk-OYMX7WX6-DCXhGWX0.js → chunk-OYMX7WX6-CWyihoif.js} +1 -1
- package/payload/server/public/assets/chunk-QZHKN3VN-COXR_N4N.js +1 -0
- package/payload/server/public/assets/{chunk-U2HBQHQK-D6lKmEsB.js → chunk-U2HBQHQK-BCWQB0k9.js} +1 -1
- package/payload/server/public/assets/{chunk-X2U36JSP-DiKHy0ar.js → chunk-X2U36JSP-CcaCMsL0.js} +1 -1
- package/payload/server/public/assets/{chunk-XPW4576I-BA0a8Ygs.js → chunk-XPW4576I-DpLw5_cw.js} +1 -1
- package/payload/server/public/assets/{chunk-YZCP3GAM-AqHm3K1O.js → chunk-YZCP3GAM-BUTzrAoG.js} +1 -1
- package/payload/server/public/assets/{chunk-ZZ45TVLE-jDPov-Ao.js → chunk-ZZ45TVLE-BD9UGg96.js} +1 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-BcD2DnVi.js +1 -0
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-ChvbGJh0.js +1 -0
- package/payload/server/public/assets/clone-DPfDeykW.js +1 -0
- package/payload/server/public/assets/{cose-bilkent-S5V4N54A-Df_JmB8E.js → cose-bilkent-S5V4N54A-bCIQHniJ.js} +1 -1
- package/payload/server/public/assets/{dagre-KV5264BT-DDGXoEYU.js → dagre-KV5264BT-DcOMoMzF.js} +1 -1
- package/payload/server/public/assets/{dagre-Dyawd5Z2.js → dagre-dnTAouT6.js} +1 -1
- package/payload/server/public/assets/data-BSt0roMQ.js +1 -0
- package/payload/server/public/assets/{diagram-5BDNPKRD-BQ1s5wpz.js → diagram-5BDNPKRD-BuQUC75n.js} +1 -1
- package/payload/server/public/assets/{diagram-G4DWMVQ6-BnBA0Ain.js → diagram-G4DWMVQ6-DHiu9U7B.js} +1 -1
- package/payload/server/public/assets/{diagram-MMDJMWI5-BrGASMP1.js → diagram-MMDJMWI5-ypu7YJGF.js} +1 -1
- package/payload/server/public/assets/{diagram-TYMM5635-iSPhqW2Q.js → diagram-TYMM5635-DIwuWuqN.js} +1 -1
- package/payload/server/public/assets/{dist-BmvZ8OaX.js → dist-ePW3hz19.js} +1 -1
- package/payload/server/public/assets/{erDiagram-SMLLAGMA-BCRyIkOj.js → erDiagram-SMLLAGMA-CKxGMQZW.js} +1 -1
- package/payload/server/public/assets/{flatten-Bo6YRmWl.js → flatten-CpKIi5d2.js} +1 -1
- package/payload/server/public/assets/{flowDiagram-DWJPFMVM-BgMtd8aD.js → flowDiagram-DWJPFMVM-CqH2RE7a.js} +1 -1
- package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-BKpI7Sqx.js → ganttDiagram-T4ZO3ILL-BfuGXUUh.js} +1 -1
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-CwwaM7ed.js +1 -0
- package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-CEx6x6gJ.js → gitGraphDiagram-UUTBAWPF-BlpUYLLd.js} +1 -1
- package/payload/server/public/assets/{graph-DDJ3p_3N.js → graph-BEu_XQMZ.js} +19 -19
- package/payload/server/public/assets/graph-labels-DvO7i7Ez.js +1 -0
- package/payload/server/public/assets/{graphlib-Do8h5YVR.js → graphlib-O_QsiopN.js} +1 -1
- package/payload/server/public/assets/info-OMHHGYJF-CiiU112m.js +1 -0
- package/payload/server/public/assets/infoDiagram-42DDH7IO-tHUu3AX-.js +2 -0
- package/payload/server/public/assets/{isEmpty-D6Kr-M1M.js → isEmpty-C3Vxk1It.js} +1 -1
- package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-BmCNGBwK.js → ishikawaDiagram-UXIWVN3A-CVMbR1ZD.js} +1 -1
- package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-C_37fL2K.js → journeyDiagram-VCZTEJTY-7pbwsn7f.js} +1 -1
- package/payload/server/public/assets/{kanban-definition-6JOO6SKY-BLFxAQHd.js → kanban-definition-6JOO6SKY-CWFMjHiJ.js} +1 -1
- package/payload/server/public/assets/{line-FtY_Ltj6.js → line-B4V-vQdN.js} +1 -1
- package/payload/server/public/assets/{linear-COm19-p7.js → linear-Cdt0EBMa.js} +1 -1
- package/payload/server/public/assets/{mermaid-parser.core-BIr5bg_O.js → mermaid-parser.core-Ba5Re-_1.js} +2 -2
- package/payload/server/public/assets/{mermaid.core-8XmmGzXJ.js → mermaid.core-C39ZL-9e.js} +3 -3
- package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-DPKDmfiH.js → mindmap-definition-QFDTVHPH-BrREiATP.js} +1 -1
- package/payload/server/public/assets/{ordinal-BDi6f4xk.js → ordinal-BLrOss5K.js} +1 -1
- package/payload/server/public/assets/packet-4T2RLAQJ-Bq_zPNb3.js +1 -0
- package/payload/server/public/assets/pie-ZZUOXDRM-R-cewHYM.js +1 -0
- package/payload/server/public/assets/{pieDiagram-DEJITSTG-DyuI0jFr.js → pieDiagram-DEJITSTG-sJOzKUIf.js} +1 -1
- package/payload/server/public/assets/{public-n0sHV8JI.js → public-927mDSLH.js} +3 -3
- package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-B24Cvsjf.js → quadrantDiagram-34T5L4WZ-bxoGUruT.js} +1 -1
- package/payload/server/public/assets/radar-PYXPWWZC-CZpBUjmo.js +1 -0
- package/payload/server/public/assets/{reduce-CGi9ik8i.js → reduce-C5tBOlxC.js} +1 -1
- package/payload/server/public/assets/{requirementDiagram-MS252O5E-DpieLND2.js → requirementDiagram-MS252O5E-C8_uEDnp.js} +1 -1
- package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-DFSI4LuL.js → sankeyDiagram-XADWPNL6-CKpSBj6p.js} +1 -1
- package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-3babM9wY.js → sequenceDiagram-FGHM5R23-BbVOmtji.js} +1 -1
- package/payload/server/public/assets/{src-DLEIsjER.js → src-DssjGmtU.js} +1 -1
- package/payload/server/public/assets/{stateDiagram-FHFEXIEX-DKS3fdXm.js → stateDiagram-FHFEXIEX-CLiLypPX.js} +1 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-Cn6OBfIT.js +1 -0
- package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-1nRSO-Z9.js → timeline-definition-GMOUNBTQ-Dzzc-pnB.js} +1 -1
- package/payload/server/public/assets/treeView-SZITEDCU-CjsdVrjr.js +1 -0
- package/payload/server/public/assets/treemap-W4RFUUIX-BZ8peN4O.js +1 -0
- package/payload/server/public/assets/{useSelectionMode-DvBm4di-.js → useSelectionMode-BBvWzgtz.js} +1 -1
- package/payload/server/public/assets/useSelectionMode-CzWzToaM.css +1 -0
- package/payload/server/public/assets/{vennDiagram-DHZGUBPP-Ds6i6j3g.js → vennDiagram-DHZGUBPP-CEj_ctTP.js} +1 -1
- package/payload/server/public/assets/wardley-RL74JXVD-BYLmRSDV.js +1 -0
- package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-IZMQXgw6.js → wardleyDiagram-NUSXRM2D-Cnbz79qd.js} +1 -1
- package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-BGZkTooo.js → xychartDiagram-5P7HB3ND-MTjLweae.js} +1 -1
- package/payload/server/public/browser.html +6 -5
- package/payload/server/public/chat.html +19 -0
- package/payload/server/public/data.html +7 -6
- package/payload/server/public/graph.html +8 -7
- package/payload/server/public/index.html +8 -6
- package/payload/server/public/public.html +5 -5
- package/payload/server/server.js +785 -338
- package/payload/server/public/assets/AdminShell-CnslFKoX.js +0 -1
- package/payload/server/public/assets/admin-BJYDe3p8.js +0 -1
- package/payload/server/public/assets/architecture-YZFGNWBL-xHbVjatf.js +0 -1
- package/payload/server/public/assets/browser-CKxPsTlh.js +0 -1
- package/payload/server/public/assets/channel-DPRBU48c.js +0 -1
- package/payload/server/public/assets/chunk-426QAEUC-CWj9Y5LE.js +0 -1
- package/payload/server/public/assets/chunk-QZHKN3VN-CHE_iZO7.js +0 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-BmQz46EF.js +0 -1
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-DyidaYtj.js +0 -1
- package/payload/server/public/assets/clone-D7qj6dUB.js +0 -1
- package/payload/server/public/assets/data-BEDsi4bU.js +0 -1
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-CmHdoS27.js +0 -1
- package/payload/server/public/assets/graph-labels-Cgd3XiNd.js +0 -1
- package/payload/server/public/assets/info-OMHHGYJF-DTA9msO-.js +0 -1
- package/payload/server/public/assets/infoDiagram-42DDH7IO-DGlZXVKh.js +0 -2
- package/payload/server/public/assets/packet-4T2RLAQJ-6GjqIKgu.js +0 -1
- package/payload/server/public/assets/pie-ZZUOXDRM-T72DJ5JR.js +0 -1
- package/payload/server/public/assets/radar-PYXPWWZC-x-6xW1dE.js +0 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-Dh0X-qyv.js +0 -1
- package/payload/server/public/assets/treeView-SZITEDCU-5-6wEryJ.js +0 -1
- package/payload/server/public/assets/treemap-W4RFUUIX-DF48Cc86.js +0 -1
- package/payload/server/public/assets/useSelectionMode-CeQtw8Ba.css +0 -1
- package/payload/server/public/assets/wardley-RL74JXVD-duKIa6Hq.js +0 -1
- /package/payload/server/public/assets/{_baseFor-Cam2PbSt.js → _baseFor-WfS9pKAn.js} +0 -0
- /package/payload/server/public/assets/{array-DYRGGQae.js → array-HeX70jSN.js} +0 -0
- /package/payload/server/public/assets/{chunk-CnGqDkHZ.js → chunk-CWOGyPgy.js} +0 -0
- /package/payload/server/public/assets/{cytoscape.esm-nWsJMTNI.js → cytoscape.esm-CDZo0kst.js} +0 -0
- /package/payload/server/public/assets/{defaultLocale-Du1XY3Dp.js → defaultLocale-GJwWH1Jr.js} +0 -0
- /package/payload/server/public/assets/{init-B5BXBRcm.js → init-BPLPMQ3Y.js} +0 -0
- /package/payload/server/public/assets/{katex-HOUACuRw.js → katex-CKZ-HWMQ.js} +0 -0
- /package/payload/server/public/assets/{path-CNO468J-.js → path-YdFzr2W6.js} +0 -0
- /package/payload/server/public/assets/{rough.esm-DRO6hWPh.js → rough.esm-HAx67Hnb.js} +0 -0
package/dist/index.js
CHANGED
|
@@ -2216,6 +2216,20 @@ function buildPlatform() {
|
|
|
2216
2216
|
console.log(` Installing whatsapp-channel dependencies (${wcDir})...`);
|
|
2217
2217
|
shellRetry("npm", ["install", "--omit=dev", ...NPM_NET_FLAGS], { cwd: wcDir }, 3, 15);
|
|
2218
2218
|
}
|
|
2219
|
+
// (maxy-code, Task 772) — webchat-channel service: identical contract to
|
|
2220
|
+
// whatsapp-channel above (ships dist/ + package.json declaring
|
|
2221
|
+
// @modelcontextprotocol/sdk; the per-conversation channel server resolves
|
|
2222
|
+
// the SDK from this node_modules). Wipe + reinstall on upgrade.
|
|
2223
|
+
const webchatDir = join(INSTALL_DIR, "platform", "services", "webchat-channel");
|
|
2224
|
+
if (existsSync(webchatDir)) {
|
|
2225
|
+
const webchatNodeModules = join(webchatDir, "node_modules");
|
|
2226
|
+
if (existsSync(webchatNodeModules)) {
|
|
2227
|
+
console.log(" Wiping previous webchat-channel/node_modules for a clean reinstall...");
|
|
2228
|
+
rmSync(webchatNodeModules, { recursive: true, force: true });
|
|
2229
|
+
}
|
|
2230
|
+
console.log(` Installing webchat-channel dependencies (${webchatDir})...`);
|
|
2231
|
+
shellRetry("npm", ["install", "--omit=dev", ...NPM_NET_FLAGS], { cwd: webchatDir }, 3, 15);
|
|
2232
|
+
}
|
|
2219
2233
|
// Premium-plugin MCP servers (e.g. real-agent/loop) ship dist/ + package.json
|
|
2220
2234
|
// in the payload but no node_modules — npm pack strips them, same reason as
|
|
2221
2235
|
// server/. Discover every <installDir>/premium-plugins/<bundle>/plugins/<plugin>/mcp
|
package/package.json
CHANGED
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
* unchanged colour-stability tests) keep the surfaces in lockstep.
|
|
13
13
|
*
|
|
14
14
|
* Sources of truth:
|
|
15
|
-
* - Label list:
|
|
15
|
+
* - Label list: platform/neo4j/schema.cypher (constraints + fulltext
|
|
16
|
+
* unions); registry doctrine in .docs/graph-label-registries.md
|
|
16
17
|
* - Brand colour for LocalBusiness: brand.json defaultColors.primary
|
|
17
18
|
* (resolved at runtime; this module supplies the schema-default fallback)
|
|
18
19
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAyFtD,CAAA;AAED,eAAO,MAAM,qBAAqB,YAAY,CAAA;AAE9C,eAAO,MAAM,gBAAgB,EAAE,SAAS,MAAM,EAE7C,CAAA;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,GAAG,IAAI,CAOzE;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,MAAM,EAAE,MAAM,CAAA;IACd,eAAe,EAAE,MAAM,CAAA;CACxB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,kBAAkB,CAU/E;AASD;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACpC;AAKD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAI7D;AAED,iEAAiE;AACjE,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAIjE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,CA0D1D;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,CAmC3D"}
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
* unchanged colour-stability tests) keep the surfaces in lockstep.
|
|
14
14
|
*
|
|
15
15
|
* Sources of truth:
|
|
16
|
-
* - Label list:
|
|
16
|
+
* - Label list: platform/neo4j/schema.cypher (constraints + fulltext
|
|
17
|
+
* unions); registry doctrine in .docs/graph-label-registries.md
|
|
17
18
|
* - Brand colour for LocalBusiness: brand.json defaultColors.primary
|
|
18
19
|
* (resolved at runtime; this module supplies the schema-default fallback)
|
|
19
20
|
*/
|
|
@@ -45,6 +46,18 @@ exports.GRAPH_LABEL_COLOURS = {
|
|
|
45
46
|
Property: '#5C7A99',
|
|
46
47
|
Viewing: '#D4A574',
|
|
47
48
|
Offer: '#B08850',
|
|
49
|
+
// Construction ontology (Task 773; labels registered in schema.cypher by
|
|
50
|
+
// Task 650). Muted brick-red family — the construction register sits
|
|
51
|
+
// apart from people-terracotta, viewing-amber, and task-rose bands so
|
|
52
|
+
// siteoffice legends read as their own group. Top-level (operator-entry)
|
|
53
|
+
// labels only; child labels (LineItem, Milestone, Valuation,
|
|
54
|
+
// VariationNote, TimeLog) intentionally stay fallback-grey.
|
|
55
|
+
Job: '#8C3B2E',
|
|
56
|
+
QuoteDocument: '#A65A4A',
|
|
57
|
+
InboundInvoice: '#6B2A20',
|
|
58
|
+
SubContractor: '#BF7E6E',
|
|
59
|
+
SubInvoice: '#52201A',
|
|
60
|
+
WhatsAppGroup: '#D4A096',
|
|
48
61
|
// External business entity — schema:Organization, written by memory-write
|
|
49
62
|
// as the Supplier label. Warm tobacco — distinct hue from internal-business
|
|
50
63
|
// slates so external entities are scannable at a glance.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
* unchanged colour-stability tests) keep the surfaces in lockstep.
|
|
13
13
|
*
|
|
14
14
|
* Sources of truth:
|
|
15
|
-
* - Label list:
|
|
15
|
+
* - Label list: platform/neo4j/schema.cypher (constraints + fulltext
|
|
16
|
+
* unions); registry doctrine in .docs/graph-label-registries.md
|
|
16
17
|
* - Brand colour for LocalBusiness: brand.json defaultColors.primary
|
|
17
18
|
* (resolved at runtime; this module supplies the schema-default fallback)
|
|
18
19
|
*/
|
|
@@ -38,6 +39,19 @@ export const GRAPH_LABEL_COLOURS: Record<string, string> = {
|
|
|
38
39
|
Viewing: '#D4A574',
|
|
39
40
|
Offer: '#B08850',
|
|
40
41
|
|
|
42
|
+
// Construction ontology (Task 773; labels registered in schema.cypher by
|
|
43
|
+
// Task 650). Muted brick-red family — the construction register sits
|
|
44
|
+
// apart from people-terracotta, viewing-amber, and task-rose bands so
|
|
45
|
+
// siteoffice legends read as their own group. Top-level (operator-entry)
|
|
46
|
+
// labels only; child labels (LineItem, Milestone, Valuation,
|
|
47
|
+
// VariationNote, TimeLog) intentionally stay fallback-grey.
|
|
48
|
+
Job: '#8C3B2E',
|
|
49
|
+
QuoteDocument: '#A65A4A',
|
|
50
|
+
InboundInvoice: '#6B2A20',
|
|
51
|
+
SubContractor: '#BF7E6E',
|
|
52
|
+
SubInvoice: '#52201A',
|
|
53
|
+
WhatsAppGroup: '#D4A096',
|
|
54
|
+
|
|
41
55
|
// External business entity — schema:Organization, written by memory-write
|
|
42
56
|
// as the Supplier label. Warm tobacco — distinct hue from internal-business
|
|
43
57
|
// slates so external entities are scannable at a glance.
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"services/*"
|
|
7
7
|
],
|
|
8
8
|
"scripts": {
|
|
9
|
-
"build": "tsc -p lib/models/tsconfig.json && tsc -p lib/mcp-stderr-tee/tsconfig.json && tsc -p lib/mcp-spawn-tee/tsconfig.json && tsc -p lib/mcp-eager/tsconfig.json && tsc -p lib/account-enumeration/tsconfig.json && tsc -p lib/graph-write/tsconfig.json && tsc -p lib/embed-client/tsconfig.json && tsc -p lib/graph-mcp/tsconfig.json && tsc -p lib/graph-trash/tsconfig.json && tsc -p lib/admin-conversation-purge/tsconfig.json && tsc -p lib/graph-search/tsconfig.json && tsc -p lib/graph-style/tsconfig.json && tsc -p lib/device-url/tsconfig.json && tsc -p lib/brand-templating/tsconfig.json && tsc -p lib/entitlement/tsconfig.json && tsc -p lib/task-secrets/tsconfig.json && tsc -p lib/admins-write/tsconfig.json && tsc -p lib/persistent-components/tsconfig.json && tsc -p lib/require-port-env/tsconfig.json && tsc -p lib/aeo-llms-txt-writer/tsconfig.json && tsc -p lib/obsidian-parser/tsconfig.json && tsc -p services/claude-session-manager/tsconfig.json && tsc -p services/whatsapp-channel/tsconfig.json && NODE_OPTIONS='--max-old-space-size=8192' tsc -b plugins/*/mcp/tsconfig.json",
|
|
9
|
+
"build": "tsc -p lib/models/tsconfig.json && tsc -p lib/mcp-stderr-tee/tsconfig.json && tsc -p lib/mcp-spawn-tee/tsconfig.json && tsc -p lib/mcp-eager/tsconfig.json && tsc -p lib/account-enumeration/tsconfig.json && tsc -p lib/graph-write/tsconfig.json && tsc -p lib/embed-client/tsconfig.json && tsc -p lib/graph-mcp/tsconfig.json && tsc -p lib/graph-trash/tsconfig.json && tsc -p lib/admin-conversation-purge/tsconfig.json && tsc -p lib/graph-search/tsconfig.json && tsc -p lib/graph-style/tsconfig.json && tsc -p lib/device-url/tsconfig.json && tsc -p lib/brand-templating/tsconfig.json && tsc -p lib/entitlement/tsconfig.json && tsc -p lib/task-secrets/tsconfig.json && tsc -p lib/admins-write/tsconfig.json && tsc -p lib/persistent-components/tsconfig.json && tsc -p lib/require-port-env/tsconfig.json && tsc -p lib/aeo-llms-txt-writer/tsconfig.json && tsc -p lib/obsidian-parser/tsconfig.json && tsc -p services/claude-session-manager/tsconfig.json && tsc -p services/whatsapp-channel/tsconfig.json && tsc -p services/webchat-channel/tsconfig.json && NODE_OPTIONS='--max-old-space-size=8192' tsc -b plugins/*/mcp/tsconfig.json",
|
|
10
10
|
"build:lib": "tsc -p lib/models/tsconfig.json && tsc -p lib/mcp-stderr-tee/tsconfig.json && tsc -p lib/mcp-spawn-tee/tsconfig.json && tsc -p lib/mcp-eager/tsconfig.json && tsc -p lib/account-enumeration/tsconfig.json && tsc -p lib/graph-write/tsconfig.json && tsc -p lib/embed-client/tsconfig.json && tsc -p lib/graph-mcp/tsconfig.json && tsc -p lib/graph-trash/tsconfig.json && tsc -p lib/admin-conversation-purge/tsconfig.json && tsc -p lib/graph-search/tsconfig.json && tsc -p lib/graph-style/tsconfig.json && tsc -p lib/device-url/tsconfig.json && tsc -p lib/brand-templating/tsconfig.json && tsc -p lib/entitlement/tsconfig.json && tsc -p lib/task-secrets/tsconfig.json && tsc -p lib/admins-write/tsconfig.json && tsc -p lib/persistent-components/tsconfig.json && tsc -p lib/require-port-env/tsconfig.json && tsc -p lib/aeo-llms-txt-writer/tsconfig.json && tsc -p lib/obsidian-parser/tsconfig.json",
|
|
11
11
|
"gen:canonical-tools": "node scripts/generate-canonical-tool-names.mjs",
|
|
12
12
|
"build:memory": "tsc -p plugins/memory/mcp/tsconfig.json",
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"build:replicate": "tsc -p plugins/replicate/mcp/tsconfig.json",
|
|
23
23
|
"build:graph-viewer": "tsc -p plugins/graph-viewer/mcp/tsconfig.json",
|
|
24
24
|
"build:claude-session-manager": "tsc -p services/claude-session-manager/tsconfig.json",
|
|
25
|
-
"build:whatsapp-channel": "tsc -p services/whatsapp-channel/tsconfig.json"
|
|
25
|
+
"build:whatsapp-channel": "tsc -p services/whatsapp-channel/tsconfig.json",
|
|
26
|
+
"build:webchat-channel": "tsc -p services/webchat-channel/tsconfig.json"
|
|
26
27
|
}
|
|
27
28
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: platform-architecture
|
|
3
3
|
description: Use when grounding any documented-surface claim about what Maxy ships — plugins, skills, specialists, install/deploy flows, internals. This is the install catalogue, not evidence of what is enabled on the current account. For install state on this account, call `capabilities-here`; for documented surface, cite the `Source:` URL inline.
|
|
4
|
-
content-hash: sha256:
|
|
4
|
+
content-hash: sha256:a2ef1747514689873975925dd6b1932c0bf45a1a2479fa06b3b6e2fdb2c2c347
|
|
5
5
|
brand: maxy-code
|
|
6
6
|
product-name: Maxy
|
|
7
7
|
---
|
|
@@ -3814,6 +3814,8 @@ tail -200 ~/.maxy/logs/maxy-ui.log | rg '\[remote-auth\].*resolvedKind='
|
|
|
3814
3814
|
2. `diff <(jq .claudeAiOauth.accessToken ~/.maxy/.claude/.credentials.json) <(jq .claudeAiOauth.accessToken ~/.realagent/.claude/.credentials.json)` — must be non-empty after each brand's operator has run `claude /login` against distinct Anthropic accounts; if it's empty, both brands are still logged in to the same account (operator action, not a code bug).
|
|
3815
3815
|
3. `grep "\[install\] claude-creds pickup" ~/.${brand}/logs/install-*.log` — fires once on the first post-Task-923 install of any brand and moves the legacy `~/.claude/.credentials.json` into that brand's path. Subsequent brands install with no credentials and require a fresh `claude /login` inside that brand's chat (which writes to the brand-scoped path because the systemd unit env is in scope).
|
|
3816
3816
|
|
|
3817
|
+
To deliberately disconnect the wrong account, use the header menu's **Disconnect Claude account** item (the supported path; no SSH or manual file deletion needed). It runs `claude auth logout`, then verifies the brand's `~/${brand.configDir}/.claude/.credentials.json` is actually gone before reporting success — a disconnect that left the credential in place reports failure in the menu rather than false-confirming. The server logs one line per attempt: `grep '\[onboarding\] op=claude-logout' ~/.${brand}/logs/server.log` — `credentialsPresent=true` after the call is a failed disconnect the operator may believe succeeded.
|
|
3818
|
+
|
|
3817
3819
|
**All sessions on the brand stopped responding after a token expiry.** Symptom on the operator side: every spawn dies at `pid-file-timeout` and the dashboard health probe reports auth dead. Diagnose the OAuth refresh path before anything else:
|
|
3818
3820
|
|
|
3819
3821
|
1. `tail -n 300 ~/.${brand}/logs/server.log | grep -E 'auth-refresh|auth-health|invalid_grant'` — `op=lock-acquired` proves the cross-process lock is in play (Task 576). `op=skipped-fresh` means a sibling process (the admin server or a `claude` binary) already rotated the tokens during the lock wait — expected, healthy. `op=renewed expiresAt=…` is the only line that means a network refresh actually ran.
|
|
@@ -106,12 +106,31 @@ CLOUDFLARE_API_TOKEN="${MINTED_ACCESS}" curl -sS \
|
|
|
106
106
|
```
|
|
107
107
|
|
|
108
108
|
2. **Create the Access application** scoped to the deployed host's **document path(s) `and` `/api/*`** — the signing document's path(s) plus the capture endpoints `POST /api/accept` and `GET /api/status` (§ 4). Leave `/` and the static assets **public**. Gating only the document path is a **bypass**: with `/api/*` open, anyone can `POST /api/accept` and forge an acceptance, and `GET /api/status` is readable ungated. Leaving `/` public is deliberate and load-bearing — it is exactly what lets the neutral root portal (below) load **without a PIN**; the document path(s) and `/api/*` are the only gated destinations.
|
|
109
|
+
|
|
110
|
+
**Same-origin invariant — `/api/*` must be Functions of *this* Pages project.** An Access application gates a **host + path**, never a bare path across hosts. So gating `<host>/api/*` covers `/api/accept` and `/api/status` **only when they are Pages Functions of the same Pages project — the same origin — as the gated document**. The § 2 form posts to the **relative** `/api/accept` and `/api/status`, which keeps them same-origin by construction; that default is the invariant, not a convenience. **Splitting the endpoints onto a separate origin** — a second Pages project, any other host — escapes the gate entirely while every per-document check still passes: the document 302s to login, but `POST /api/accept` on the other origin is **forgeable** by anyone and `GET /api/status` is **world-readable**, because the document's Access app never covered that host. The `gate-probe` of "Verify on live" (below) is the only check that catches this; the per-page redirect does not. If the deploy ever resolves `/api/*` to an origin other than the document's own, that origin mismatch is itself the failure — before any probe runs.
|
|
109
111
|
3. **Attach a policy** whose `include` allowlist names the **signer's email** (and the business owner's). Only those addresses can authenticate and view the page.
|
|
110
112
|
|
|
111
113
|
The IdP call shapes are given inline above; the Access **app** and **policy** endpoints live in `api.md`'s endpoint map, and the permission-group ids are resolved by the `api.md` method (`GET …/tokens/permission_groups`).
|
|
112
114
|
|
|
113
115
|
**Verify on live.** Loading the gated page **302-redirects to a Cloudflare login page that offers One-Time PIN** — not an empty dead-end. An allowlisted email receives a code, enters it, and reaches the document; a non-allowlisted email is refused after entering its code. Reproduction-before: with **zero** IdPs the same login page offers no method at all — that is the lockout (§ 8.9).
|
|
114
116
|
|
|
117
|
+
**Verify on live — `gate-probe` `/api/*`, not just the page.** The page redirect above is necessary but **not sufficient**: it never touches `/api/*`, which is exactly how a separate-origin API (the same-origin invariant, above) deploys green while wide open. Before a gated deploy is "done", hit **both** capture endpoints **unauthenticated, with no Access session**, against the live API origin, and read the status code — each must be **refused** (302 to the Access login, or 403) before it reaches the Function:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
# No cookies, no Access session. API MUST be the gated document's OWN origin —
|
|
121
|
+
# the canonical host § 7 writes as <project>.pages.dev, or a custom domain mapped to it.
|
|
122
|
+
# An UNGATED accept endpoint inserts a sentinel row (doc_ref=GATE-PROBE); delete it as
|
|
123
|
+
# part of remediation. A gated endpoint 302s before the Function runs, so nothing is written.
|
|
124
|
+
API="https://<host>"
|
|
125
|
+
printf 'op=gate-probe endpoint=/api/accept method=POST code=%s\n' \
|
|
126
|
+
"$(curl -sS -o /dev/null -w '%{http_code}' -X POST -H 'Content-Type: application/json' \
|
|
127
|
+
-d '{"name":"probe","email":"probe@example.com","token":"GATE-PROBE-aaaaa-aaaaaa","doc_ref":"GATE-PROBE"}' "$API/api/accept")"
|
|
128
|
+
printf 'op=gate-probe endpoint=/api/status method=GET code=%s\n' \
|
|
129
|
+
"$(curl -sS -o /dev/null -w '%{http_code}' "$API/api/status?doc_ref=GATE-PROBE")"
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**Pass:** both `gate-probe` lines report `302` or `403` — the Access gate refused each request **before** it reached the Function. **Fail:** **any other status.** `200` is the canonical signature (`POST /api/accept` forgeable, `GET /api/status` world-readable), but `400`, `409`, or `500` equally fail — any Function-originated response proves the request **reached the Function unauthenticated**, i.e. the API is ungated. (A repeat accept probe returns `409` on the sentinel's `doc_ref` UNIQUE — still ungated, still a fail; only `302`/`403` is a pass.) The cause is almost always `/api/*` resolving to a different origin than the gated document. A non-`302`/`403` here — not the page redirect — is the only signal that catches the split-origin escape. This `gate-probe` is the **unauthenticated negative** test; it is distinct from the § 7 outcome contract, which exercises the *authenticated functional* flow (POST → D1 row → status flip) and therefore runs pre-gate or from an allowlisted session. After both probes refuse, confirm an **allowlisted** session still reaches the form and accepts once.
|
|
133
|
+
|
|
115
134
|
**Dispatch is unaffected (§ 6).** The sweep stamps the **persisted base** (`base.pdf`, § 1), never by fetching the gated page, so a gate never turns a dispatched "PDF" into an Access login screen — § 8.3 makes that structural. Keep it in mind whenever a gated document is swept.
|
|
116
135
|
|
|
117
136
|
**Logout links carry `returnTo` — point them at the public root (the primary loop-fix).** Cloudflare Access **does** honour a logout redirect: `GET /cdn-cgi/access/logout?returnTo=<https origin root>` 302s straight to that root and **strips** the stale `__cf_access_message` param. The parameter is **camelCase `returnTo` only** — `return_to` and `redirect_url` are ignored. So build every signer-facing logout link as `/cdn-cgi/access/logout?returnTo=https://<host>/`, with `returnTo` pointing at the **public root**, never at the gated document path (which would just re-challenge for a PIN). A logout link built this way lands the signer cleanly on the public root with no stale param — this is what actually breaks the "enter PIN → logged-out screen" loop.
|
|
@@ -168,6 +187,8 @@ Confirming A4 *dimensions* is not proof of fit — **read every rendered page**
|
|
|
168
187
|
|
|
169
188
|
**Acceptance token** — `<DOC_REF>-<5-char base36 timestamp>-<6-char djb2 hash>`. The hash is djb2 over `name + email + DOC_REF`: it binds the signer's identity to this document, so a UNIQUE collision on the token means *the same signer re-submitting the same document* — a genuine same-signer duplicate, distinct from a *different* signer hitting an already-accepted document (which the `doc_ref` guard catches; § 4).
|
|
170
189
|
|
|
190
|
+
**The djb2 hash is not an access control.** It is a deterministic function of `name`, `email` and `DOC_REF` — all public-ish fields a forger can supply — so it is **defence-in-depth obfuscation of the token shape, never a gate**. It makes a token mildly unguessable and lets the database discriminate a same-signer re-submit (above); it does **not** authenticate the POST. The only thing that stops a forged `POST /api/accept` is the § 1a Access gate over a **same-origin** `/api/*`. Never treat the token as a substitute for gating the API.
|
|
191
|
+
|
|
171
192
|
All confirmation, closed-state, and fallback panels are built with `textContent` and DOM nodes, never `innerHTML` — the signer's own name and email flow back into the page, and `innerHTML` would make that an injection surface.
|
|
172
193
|
|
|
173
194
|
```html
|
|
@@ -121,6 +121,8 @@ tail -200 ~/.maxy/logs/maxy-ui.log | rg '\[remote-auth\].*resolvedKind='
|
|
|
121
121
|
2. `diff <(jq .claudeAiOauth.accessToken ~/.maxy/.claude/.credentials.json) <(jq .claudeAiOauth.accessToken ~/.realagent/.claude/.credentials.json)` — must be non-empty after each brand's operator has run `claude /login` against distinct Anthropic accounts; if it's empty, both brands are still logged in to the same account (operator action, not a code bug).
|
|
122
122
|
3. `grep "\[install\] claude-creds pickup" ~/.${brand}/logs/install-*.log` — fires once on the first post-Task-923 install of any brand and moves the legacy `~/.claude/.credentials.json` into that brand's path. Subsequent brands install with no credentials and require a fresh `claude /login` inside that brand's chat (which writes to the brand-scoped path because the systemd unit env is in scope).
|
|
123
123
|
|
|
124
|
+
To deliberately disconnect the wrong account, use the header menu's **Disconnect Claude account** item (the supported path; no SSH or manual file deletion needed). It runs `claude auth logout`, then verifies the brand's `~/${brand.configDir}/.claude/.credentials.json` is actually gone before reporting success — a disconnect that left the credential in place reports failure in the menu rather than false-confirming. The server logs one line per attempt: `grep '\[onboarding\] op=claude-logout' ~/.${brand}/logs/server.log` — `credentialsPresent=true` after the call is a failed disconnect the operator may believe succeeded.
|
|
125
|
+
|
|
124
126
|
**All sessions on the brand stopped responding after a token expiry.** Symptom on the operator side: every spawn dies at `pid-file-timeout` and the dashboard health probe reports auth dead. Diagnose the OAuth refresh path before anything else:
|
|
125
127
|
|
|
126
128
|
1. `tail -n 300 ~/.${brand}/logs/server.log | grep -E 'auth-refresh|auth-health|invalid_grant'` — `op=lock-acquired` proves the cross-process lock is in play (Task 576). `op=skipped-fresh` means a sibling process (the admin server or a `claude` binary) already rotated the tokens during the lock wait — expected, healthy. `op=renewed expiresAt=…` is the only line that means a network refresh actually ran.
|
|
@@ -86,6 +86,10 @@ Observation is fully preserved: group messages are stored in the ring buffer, re
|
|
|
86
86
|
|
|
87
87
|
A native admin channel session runs a per-sender `whatsapp-channel` MCP server (`platform/services/whatsapp-channel/`) exposing two reply-only tools: `reply` (text) and `reply-document` (one or more files delivered as WhatsApp documents). `reply-document` POSTs to the gateway's `POST /wa-channel/reply-document`, which funnels through `sendWhatsAppDocument` — the single send core that owns account-dir path validation, the 100 MB ceiling, the canonical `[whatsapp:outbound] sent document …` log, and per-file reconciliation (`op=reply-document-reconciled` on delivery, `file-delivery-unreconciled` on a miss — no silent failure). A bare `SendUserFile` is also caught and delivered through the same core by a read-only JSONL follower on the session, so an attachment reaches the sender even when the agent skips the explicit tool. Both surfaces are reply-only: a file reply to a sender with no live conversation is refused. See [channels-whatsapp.md](references/channels-whatsapp.md) and `.docs/whatsapp-inbound-lifeline.md`.
|
|
88
88
|
|
|
89
|
+
## Self-chat command lane (Task 774)
|
|
90
|
+
|
|
91
|
+
The agent links as a companion device on the operator's own WhatsApp account, so the operator cannot DM the agent from the paired handset — those messages arrive as `fromMe=true` / upsert type `append`. The operator's **self-chat** (message-yourself thread) is the command lane from that handset: the `append` branch in `manager.ts` evaluates a six-clause discriminator (`inbound/self-chat.ts`) — fromMe, self-surface jid (selfJid/selfLid/`selfPhone@s.whatsapp.net`, device-suffix normalised), not an agent-sent echo, timestamp strictly after `lastConnectedAt` (excludes reconnect history backfill; restart-safe backstop for the 2-min echo cache), not a duplicate, non-empty text — and dispatches a match to the **admin** agent with raw text; the reply lands back in the self-chat. Every self-chat `fromMe` message emits one `[whatsapp:self-chat] op=seen` line followed by `op=dispatch` or `op=skip reason=echo|backfill|duplicate|no-text`, so a dropped command is never silent. `fromMe` in any other thread is unchanged (`[Owner reply]` mirror). See [channels-whatsapp.md](references/channels-whatsapp.md) §Self-chat command lane.
|
|
92
|
+
|
|
89
93
|
## Live persistence
|
|
90
94
|
|
|
91
95
|
Every `messages.upsert` event (both `notify` and `append`, both `fromMe` directions) writes a `:Message:WhatsAppMessage` row to Neo4j attached to the sessionKey-keyed `:Conversation`. A single capture site at `platform/ui/app/lib/whatsapp/manager.ts` covers inbound, outbound (Baileys echoes agent-sent messages back through `messages.upsert` with `fromMe=true`), and owner-mirror — without touching `outbound/send.ts`. `messageId` namespace is `whatsapp-live:<waName>:<remoteJid>:<msg.key.id>` where `<waName>` is the Baileys credential dirname (e.g. `default`); distinct from the `:ConversationArchive` `:Section` chunks written by the source-agnostic `conversation-archive` skill (parent `:ConversationArchive` keyed on `conversationIdentity`, plus `:Section` children) — live and archive live in disjoint shapes. Persist failures are loud (`[whatsapp-persist] FAIL …`) and never block dispatch — silent loss is the worse failure mode.
|
|
@@ -150,9 +150,36 @@ The dedupe cache (`agentSentMessages`) has a 2-minute TTL and 500-entry cap, whi
|
|
|
150
150
|
| `src/web/inbound/dedupe.ts` | `trackAgentSentMessage` / `isAgentSentMessage` — echo suppression cache |
|
|
151
151
|
| `src/infra/outbound/cross-agent-echo.ts` | Adapter: agent tool → `mirrorOwnerReplyToSession` |
|
|
152
152
|
|
|
153
|
+
## Self-chat command lane (Task 774)
|
|
154
|
+
|
|
155
|
+
The companion-device link means the operator's paired phone cannot DM the agent — a message typed from the paired number arrives as `fromMe=true` with upsert type `append`, which the manager's append branch historically dropped before dispatch. The operator's **self-chat** (the message-yourself thread, `remoteJid` == the account's own jid/lid) is the exception: the agent never writes there first, so it is an unambiguous admin command surface.
|
|
156
|
+
|
|
157
|
+
The discriminator lives in `platform/ui/app/lib/whatsapp/inbound/self-chat.ts` (`evaluateSelfChatCommand`) and dispatches only when **all** of:
|
|
158
|
+
|
|
159
|
+
1. `msg.key.fromMe === true`;
|
|
160
|
+
2. `remoteJid` is the self surface — `selfJid`, `selfLid`, or `${selfPhone}@s.whatsapp.net`, with device suffixes (`447…:12@s.whatsapp.net`) normalised before comparison;
|
|
161
|
+
3. the message id is not in the agent-sent cache (`isAgentSentMessage`) — suppresses the agent's own self-chat reply echoes within the 2-minute TTL;
|
|
162
|
+
4. the message timestamp (Unix seconds, normalised to ms) is **strictly after** `conn.lastConnectedAt` — excludes history backfill replayed on reconnect, and is the restart-safe backstop when the in-memory echo cache has expired or been cleared;
|
|
163
|
+
5. it is not an in-session duplicate (`isDuplicateInbound`);
|
|
164
|
+
6. it has non-empty extracted text.
|
|
165
|
+
|
|
166
|
+
A match dispatches via `buildSelfChatDispatch` (`manager.ts`): `agentType: "admin"`, raw command text (no `[Owner reply]` prefix), `senderPhone = selfPhone`, the admin cacheKey (`whatsapp:<accountId>`), `reply`/`composing` closures bound to the self jid so the agent's answer returns to the self-chat, and **no `isOwnerMirror` flag** — the inbound route drops owner-mirror payloads before the gateway, and a self-chat message is a command expecting a reply, not a mirror. The reply goes out through `sendTextMessage`, which tracks the id for clause-3 echo suppression.
|
|
167
|
+
|
|
168
|
+
`fromMe` in any **non-self** thread is unchanged: it remains the `[Owner reply]` mirror described above, never a command.
|
|
169
|
+
|
|
170
|
+
**Log grammar** (one decision lineage per self-chat message, greppable by `msgId=`):
|
|
171
|
+
|
|
172
|
+
```
|
|
173
|
+
[whatsapp:self-chat] op=seen account=… msgId=… tsMs=… connAtMs=… fromMe=true
|
|
174
|
+
[whatsapp:self-chat] op=skip account=… msgId=… reason=echo|backfill|duplicate|no-text
|
|
175
|
+
[whatsapp:self-chat] op=dispatch account=… msgId=… bytes=… agentType=admin
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Failure signatures: an `op=dispatch` whose `tsMs ≤ connAtMs` (backfill wrongly dispatched — must never appear; the `op=seen` line carries both fields so this is checkable per message); back-to-back `op=dispatch` with no operator-originated message between (self-reply loop); a self-chat message the operator confirms sending with no `op=seen` at all (silent drop upstream of the discriminator).
|
|
179
|
+
|
|
153
180
|
## Offline Message Recovery
|
|
154
181
|
|
|
155
|
-
Baileys is a WhatsApp Web protocol client. When the gateway is offline, WhatsApp's servers hold incoming messages and push them back as history (`append`-type `messages.upsert` events) when Baileys reconnects — so the system self-recovers automatically on restart. These replayed messages are stored in the UI message cache and marked as read, but **not** dispatched to the agent (no auto-reply on catch-up). The recovery window is determined entirely by WhatsApp's server-side delivery queue (approximately 30 days); `syncFullHistory: false` only prevents requesting full chat history on first pairing and does not restrict this window.
|
|
182
|
+
Baileys is a WhatsApp Web protocol client. When the gateway is offline, WhatsApp's servers hold incoming messages and push them back as history (`append`-type `messages.upsert` events) when Baileys reconnects — so the system self-recovers automatically on restart. These replayed messages are stored in the UI message cache and marked as read, but **not** dispatched to the agent (no auto-reply on catch-up). The self-chat command lane (above) does not weaken this: replayed history fails its strictly-after-`lastConnectedAt` timestamp clause and skips with `reason=backfill`. The recovery window is determined entirely by WhatsApp's server-side delivery queue (approximately 30 days); `syncFullHistory: false` only prevents requesting full chat history on first pairing and does not restrict this window.
|
|
156
183
|
|
|
157
184
|
## Voice Notes
|
|
158
185
|
|
|
@@ -5,6 +5,7 @@ import type { RateLimiter } from './spawn-rate-limiter.js';
|
|
|
5
5
|
import type { AuditRegistry } from './public-tool-audit.js';
|
|
6
6
|
import { type UserTitleStore } from './user-title-store.js';
|
|
7
7
|
import { WaChannelStore } from './wa-channel-store.js';
|
|
8
|
+
import { WebchatChannelStore } from './webchat-channel-store.js';
|
|
8
9
|
export declare const SESSION_ID_PATTERN: RegExp;
|
|
9
10
|
export declare const RC_SPAWN_URL_WAIT_DEFAULT_MS = 60000;
|
|
10
11
|
export interface HttpDeps extends Omit<SpawnDeps, 'onSessionReady' | 'watcher'> {
|
|
@@ -24,6 +25,11 @@ export interface HttpDeps extends Omit<SpawnDeps, 'onSessionReady' | 'watcher'>
|
|
|
24
25
|
* write-throughs it; every resume path reads it back to re-apply the
|
|
25
26
|
* channel argv so `reply` is present on every live PTY for the session. */
|
|
26
27
|
waChannelStore: WaChannelStore;
|
|
28
|
+
/** Task 772 — per-session webchat channel binding, the webchat twin of
|
|
29
|
+
* waChannelStore. Optional so the pre-772 test deps factories (which run
|
|
30
|
+
* the live /rc-spawn handler) keep working unchanged; production index.ts
|
|
31
|
+
* always injects it. */
|
|
32
|
+
webchatChannelStore?: WebchatChannelStore;
|
|
27
33
|
/** Task 648 — override the `/rc-spawn` pid/bind poll bound. Production
|
|
28
34
|
* omits this and gets RC_SPAWN_URL_WAIT_DEFAULT_MS (60s); tests inject a
|
|
29
35
|
* short value to exercise the bind-timeout response without a 60s wait. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-server.d.ts","sourceRoot":"","sources":["../src/http-server.ts"],"names":[],"mappings":"AAsBA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"http-server.d.ts","sourceRoot":"","sources":["../src/http-server.ts"],"names":[],"mappings":"AAsBA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAU3B,OAAO,EAgBL,KAAK,SAAS,EAEf,MAAM,kBAAkB,CAAA;AAoBzB,OAAO,KAAK,EAAE,SAAS,EAAc,MAAM,iBAAiB,CAAA;AAE5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA;AAC3D,OAAO,EAAqB,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAC9E,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AAgFhE,eAAO,MAAM,kBAAkB,QAA2B,CAAA;AAS1D,eAAO,MAAM,4BAA4B,QAAS,CAAA;AAIlD,MAAM,WAAW,QAAS,SAAQ,IAAI,CAAC,SAAS,EAAE,gBAAgB,GAAG,SAAS,CAAC;IAC7E;;qEAEiE;IACjE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,SAAS,CAAA;IAClB,WAAW,EAAE,MAAM,CAAA;IACnB,iBAAiB,EAAE,MAAM,CAAA;IACzB,kBAAkB,EAAE,WAAW,CAAA;IAC/B,eAAe,EAAE,aAAa,CAAA;IAC9B;kFAC8E;IAC9E,cAAc,EAAE,cAAc,CAAA;IAC9B;;gFAE4E;IAC5E,cAAc,EAAE,cAAc,CAAA;IAC9B;;;6BAGyB;IACzB,mBAAmB,CAAC,EAAE,mBAAmB,CAAA;IACzC;;gFAE4E;IAC5E,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC1B;AA+MD;;;kEAGkE;AAClE,MAAM,MAAM,OAAO,GAAG,IAAI,GAAG;IAC3B,2BAA2B,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACpD;;;+CAG2C;IAC3C,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CAChD,CAAA;AAmBD,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAu8DpD"}
|
|
@@ -24,6 +24,7 @@ import { stream } from 'hono/streaming';
|
|
|
24
24
|
import { existsSync, statSync, createReadStream, watchFile, unwatchFile, rmSync, mkdirSync, renameSync, readFileSync, writeFileSync } from 'node:fs';
|
|
25
25
|
import { tmpdir } from 'node:os';
|
|
26
26
|
import { buildWaChannelMcpServers, buildWaChannelArgv, waChannelMcpConfigPath, isDevChannelPrompt } from './wa-channel-mcp.js';
|
|
27
|
+
import { buildWebchatChannelMcpServers, buildWebchatChannelArgv, webchatChannelMcpConfigPath } from './webchat-channel-mcp.js';
|
|
27
28
|
import { randomUUID } from 'node:crypto';
|
|
28
29
|
import { execFileSync } from 'node:child_process';
|
|
29
30
|
import { buildRcChildEnv } from './rc-daemon.js';
|
|
@@ -1305,6 +1306,7 @@ export function buildHttpApp(deps) {
|
|
|
1305
1306
|
// doesn't accrete orphans and a recreated session can't inherit a stale
|
|
1306
1307
|
// binding. Best-effort; the store logs its own failure.
|
|
1307
1308
|
deps.waChannelStore.remove(sessionId);
|
|
1309
|
+
deps.webchatChannelStore?.remove(sessionId);
|
|
1308
1310
|
// Task 702 — the transcript is gone; its reattach hint must go too.
|
|
1309
1311
|
removeScopeRecord(sessionsDir, sessionId, deps.logger);
|
|
1310
1312
|
deps.logger(`purge sessionId=${sessionId.slice(0, 8)} bytes=${bytes}`);
|
|
@@ -1629,6 +1631,18 @@ export function buildHttpApp(deps) {
|
|
|
1629
1631
|
typeof waChannelRaw.serverPath === 'string'
|
|
1630
1632
|
? waChannelRaw
|
|
1631
1633
|
: undefined;
|
|
1634
|
+
// Task 772 — optional webchat native-channel attach, the webchat twin of
|
|
1635
|
+
// waChannel. All three fields must be present strings or the attach is
|
|
1636
|
+
// skipped. A session is whatsapp XOR webchat, so at most one of the two
|
|
1637
|
+
// channel blocks below fires.
|
|
1638
|
+
const webchatChannelRaw = body.webchatChannel;
|
|
1639
|
+
const webchatChannel = webchatChannelRaw !== null &&
|
|
1640
|
+
typeof webchatChannelRaw === 'object' &&
|
|
1641
|
+
typeof webchatChannelRaw.key === 'string' &&
|
|
1642
|
+
typeof webchatChannelRaw.gatewayUrl === 'string' &&
|
|
1643
|
+
typeof webchatChannelRaw.serverPath === 'string'
|
|
1644
|
+
? webchatChannelRaw
|
|
1645
|
+
: undefined;
|
|
1632
1646
|
// Task 724 — the bridge routes EVERY role=admin channel inbound here, not
|
|
1633
1647
|
// just WhatsApp, and supplies the real channel (+ role:'admin', senderId) so
|
|
1634
1648
|
// the classification sidecar below records the actual channel. The WaGateway
|
|
@@ -1740,6 +1754,49 @@ export function buildHttpApp(deps) {
|
|
|
1740
1754
|
deps.logger(`[rc-spawn] op=wa-channel-write-failed err=${JSON.stringify(err instanceof Error ? err.message : String(err))}`);
|
|
1741
1755
|
}
|
|
1742
1756
|
}
|
|
1757
|
+
// Task 772 — webchat native channel, the webchat twin of the waChannel
|
|
1758
|
+
// block above. Same contract: a gateway spawn carries `webchatChannel` and
|
|
1759
|
+
// write-throughs the binding; a bare resume reads it back, so
|
|
1760
|
+
// `mcp__webchat-channel__reply` is present on every live PTY for the
|
|
1761
|
+
// session. A session is whatsapp XOR webchat — enforced here: when a
|
|
1762
|
+
// WhatsApp binding is already effective for this session, the webchat
|
|
1763
|
+
// attach is refused (two channel servers on one PTY is never valid).
|
|
1764
|
+
let effectiveWebchatChannel;
|
|
1765
|
+
if (sessionId && effectiveWaChannel && (webchatChannel || deps.webchatChannelStore?.get(sessionId))) {
|
|
1766
|
+
deps.logger(`[rc-spawn] op=webchat-channel-refused sessionId=${sessionId.slice(0, 8)} reason=wa-channel-already-bound`);
|
|
1767
|
+
}
|
|
1768
|
+
else if (sessionId) {
|
|
1769
|
+
if (webchatChannel) {
|
|
1770
|
+
effectiveWebchatChannel = webchatChannel;
|
|
1771
|
+
deps.webchatChannelStore?.set(sessionId, webchatChannel);
|
|
1772
|
+
}
|
|
1773
|
+
else {
|
|
1774
|
+
const stored = deps.webchatChannelStore?.get(sessionId);
|
|
1775
|
+
if (stored) {
|
|
1776
|
+
effectiveWebchatChannel = { key: stored.key, gatewayUrl: stored.gatewayUrl, serverPath: stored.serverPath };
|
|
1777
|
+
deps.logger(`[rc-spawn] op=webchat-channel-rebind sessionId=${sessionId.slice(0, 8)} key=${stored.key} source=store`);
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
let webchatPath = null;
|
|
1782
|
+
if (effectiveWebchatChannel && sessionId) {
|
|
1783
|
+
const webchatServers = buildWebchatChannelMcpServers({
|
|
1784
|
+
sessionId,
|
|
1785
|
+
key: effectiveWebchatChannel.key,
|
|
1786
|
+
gatewayUrl: effectiveWebchatChannel.gatewayUrl,
|
|
1787
|
+
serverPath: effectiveWebchatChannel.serverPath,
|
|
1788
|
+
});
|
|
1789
|
+
webchatPath = webchatChannelMcpConfigPath(sessionId, tmpdir());
|
|
1790
|
+
try {
|
|
1791
|
+
writeFileSync(webchatPath, JSON.stringify({ mcpServers: webchatServers }, null, 2), 'utf8');
|
|
1792
|
+
argv.push(...buildWebchatChannelArgv(webchatPath));
|
|
1793
|
+
deps.logger(`[rc-spawn] op=webchat-channel key=${effectiveWebchatChannel.key} gateway=${effectiveWebchatChannel.gatewayUrl} path=${webchatPath}`);
|
|
1794
|
+
}
|
|
1795
|
+
catch (err) {
|
|
1796
|
+
webchatPath = null;
|
|
1797
|
+
deps.logger(`[rc-spawn] op=webchat-channel-write-failed err=${JSON.stringify(err instanceof Error ? err.message : String(err))}`);
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1743
1800
|
// Task 626 — the initial message is the trailing positional, processed as
|
|
1744
1801
|
// the session's first user turn at PTY startup. One-shot jobs omit `name`
|
|
1745
1802
|
// so the prompt is the sole positional.
|
|
@@ -1828,6 +1885,10 @@ export function buildHttpApp(deps) {
|
|
|
1828
1885
|
if (sessionId && deps.waChannelStore.get(sessionId) && waPath === null) {
|
|
1829
1886
|
deps.logger(`[rc-spawn] op=channel-binding-lost sessionId=${sessionId} pid=${pty.pid} reason=resume-without-mcp-config`);
|
|
1830
1887
|
}
|
|
1888
|
+
// Task 772 — the same post-condition for the webchat binding.
|
|
1889
|
+
if (sessionId && deps.webchatChannelStore?.get(sessionId) && webchatPath === null) {
|
|
1890
|
+
deps.logger(`[rc-spawn] op=webchat-channel-binding-lost sessionId=${sessionId} pid=${pty.pid} reason=resume-without-mcp-config`);
|
|
1891
|
+
}
|
|
1831
1892
|
// Tracker registration — closes the leak gap the Task 552 path opened.
|
|
1832
1893
|
// For fresh spawns without a caller-supplied sessionId we register the
|
|
1833
1894
|
// tracker under the unitToken so the entry has a stable key; the
|
|
@@ -1863,7 +1924,7 @@ export function buildHttpApp(deps) {
|
|
|
1863
1924
|
// a failed classification sidecar must not tear it down, so it logs and the
|
|
1864
1925
|
// session keeps running (the pre-fix reader-blind behaviour, no worse).
|
|
1865
1926
|
const classifyChannel = adminRole === 'admin' ? adminChannel : undefined;
|
|
1866
|
-
const classifySenderId = adminSenderId ?? effectiveWaChannel?.senderId;
|
|
1927
|
+
const classifySenderId = adminSenderId ?? effectiveWaChannel?.senderId ?? effectiveWebchatChannel?.key;
|
|
1867
1928
|
if (sessionId && classifyChannel && classifySenderId) {
|
|
1868
1929
|
const classifySidecarPath = sidecarPathForSessionId(deps.spawnCwd, sessionId);
|
|
1869
1930
|
if (!existsSync(classifySidecarPath)) {
|
|
@@ -1935,7 +1996,8 @@ export function buildHttpApp(deps) {
|
|
|
1935
1996
|
// no literal spaces, so match on the ANSI/space-stripped letter stream.
|
|
1936
1997
|
// Bounded startup keystroke only — inbound message submission stays a
|
|
1937
1998
|
// channel event (the whole point of the migration). Armed only when a
|
|
1938
|
-
// waChannel
|
|
1999
|
+
// native channel (waChannel or webchatChannel, Task 772) is attached,
|
|
2000
|
+
// fired at most once — the prompt text is channel-agnostic.
|
|
1939
2001
|
let devChannelConfirmed = false;
|
|
1940
2002
|
let devChannelScan = '';
|
|
1941
2003
|
let resolveStdoutSlug = () => { };
|
|
@@ -1943,7 +2005,7 @@ export function buildHttpApp(deps) {
|
|
|
1943
2005
|
const BIND_BUF_TAIL = 8 * 1024;
|
|
1944
2006
|
pty.onData((data) => {
|
|
1945
2007
|
stdoutBytes += data.length;
|
|
1946
|
-
if (effectiveWaChannel && !devChannelConfirmed) {
|
|
2008
|
+
if ((effectiveWaChannel || effectiveWebchatChannel) && !devChannelConfirmed) {
|
|
1947
2009
|
devChannelScan += data;
|
|
1948
2010
|
if (devChannelScan.length > 4096)
|
|
1949
2011
|
devChannelScan = devChannelScan.slice(devChannelScan.length - 4096);
|