@rubytech/create-maxy-code 0.1.82 → 0.1.84
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 +1 -1
- package/payload/platform/plugins/admin/hooks/__tests__/pre-tool-use-memory-write-revoke.test.sh +142 -24
- package/payload/platform/plugins/admin/hooks/__tests__/turn-completed-graph-write.test.sh +131 -218
- package/payload/platform/plugins/admin/hooks/pre-tool-use.sh +85 -17
- package/payload/platform/plugins/cloudflare/PLUGIN.md +2 -11
- package/payload/platform/plugins/cloudflare/scripts/setup-tunnel.sh +9 -7
- package/payload/platform/plugins/cloudflare/skills/setup-tunnel/SKILL.md +20 -10
- package/payload/platform/plugins/docs/references/cloudflare.md +10 -9
- package/payload/platform/scripts/seed-neo4j.sh +20 -14
- package/payload/platform/services/claude-session-manager/dist/jsonl-tail.d.ts +6 -0
- package/payload/platform/services/claude-session-manager/dist/jsonl-tail.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/jsonl-tail.js +21 -0
- package/payload/platform/services/claude-session-manager/dist/jsonl-tail.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +47 -0
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +265 -44
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
- package/payload/platform/templates/agents/admin/IDENTITY.md +1 -1
- package/payload/platform/templates/specialists/agents/database-operator.md +8 -10
- package/payload/premium-plugins/real-agent/plugins/brochures/skills/property-brochure/SKILL.md +3 -0
- package/payload/premium-plugins/real-agent/plugins/brochures/skills/property-brochure/references/template.html +18 -8
- package/payload/server/{chunk-TFEIFU7F.js → chunk-BDFOTLPW.js} +1 -1
- package/payload/server/{chunk-W33UUYYR.js → chunk-BVDLDM6O.js} +1 -1
- package/payload/server/{chunk-VGMVEYX7.js → chunk-JRBCOVA4.js} +64 -5
- package/payload/server/{cloudflare-task-tracker-7P355PSD.js → cloudflare-task-tracker-M5ONAGUT.js} +2 -2
- package/payload/server/maxy-edge.js +2 -2
- package/payload/server/public/assets/Checkbox-B34vplI7.js +1 -0
- package/payload/server/public/assets/{admin-Dl273yTS.js → admin-BkTlPuP0.js} +5 -5
- package/payload/server/public/assets/{arc-aUiRP9AS.js → arc-DMDAZHAN.js} +1 -1
- package/payload/server/public/assets/architecture-YZFGNWBL-COhEvUpo.js +1 -0
- package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-Iyf6H93s.js → architectureDiagram-Q4EWVU46-DQMCKDU2.js} +1 -1
- package/payload/server/public/assets/{blockDiagram-DXYQGD6D-e7l5Mhun.js → blockDiagram-DXYQGD6D-CqJNtiQY.js} +1 -1
- package/payload/server/public/assets/{brand-CTMGYFDd.css → brand-C_iHnsbT.css} +1 -1
- package/payload/server/public/assets/{brand-dPgPNs7n.js → brand-HqTMr1bV.js} +1 -1
- package/payload/server/public/assets/{c4Diagram-AHTNJAMY-bDICnX2_.js → c4Diagram-AHTNJAMY-BVAa1YL3.js} +1 -1
- package/payload/server/public/assets/channel-CZG4mVkt.js +1 -0
- package/payload/server/public/assets/{chunk-2KRD3SAO-BK3470lx.js → chunk-2KRD3SAO-Di4bO8ir.js} +1 -1
- package/payload/server/public/assets/chunk-336JU56O-XIsdqKyD.js +2 -0
- package/payload/server/public/assets/chunk-426QAEUC-Dw4mcMYK.js +1 -0
- package/payload/server/public/assets/{chunk-4BX2VUAB-BOvVdJLf.js → chunk-4BX2VUAB-DyEhFk-Z.js} +1 -1
- package/payload/server/public/assets/{chunk-4TB4RGXK-CzvvHuMJ.js → chunk-4TB4RGXK-BvrlG2FS.js} +1 -1
- package/payload/server/public/assets/{chunk-55IACEB6-BwZyF7vR.js → chunk-55IACEB6-BRJOZLpU.js} +1 -1
- package/payload/server/public/assets/{chunk-5FUZZQ4R-DxqHPDro.js → chunk-5FUZZQ4R-3-UZ9191.js} +1 -1
- package/payload/server/public/assets/{chunk-5PVQY5BW-BA6nMu9F.js → chunk-5PVQY5BW-DOeY60Lp.js} +1 -1
- package/payload/server/public/assets/{chunk-67CJDMHE-D5bhMrtY.js → chunk-67CJDMHE-BG6-9r6c.js} +1 -1
- package/payload/server/public/assets/{chunk-7N4EOEYR-Si7Lgrwc.js → chunk-7N4EOEYR-BvMbitX7.js} +1 -1
- package/payload/server/public/assets/{chunk-AA7GKIK3-DMuHtDqO.js → chunk-AA7GKIK3-CE8mGBD5.js} +1 -1
- package/payload/server/public/assets/{chunk-BSJP7CBP-L79XKVcb.js → chunk-BSJP7CBP-DP7LTBll.js} +1 -1
- package/payload/server/public/assets/{chunk-CIAEETIT-C0O7Upmg.js → chunk-CIAEETIT-VfnIdN-h.js} +1 -1
- package/payload/server/public/assets/{chunk-EDXVE4YY-gUSTktie.js → chunk-EDXVE4YY-BSbGSOxN.js} +1 -1
- package/payload/server/public/assets/{chunk-ENJZ2VHE-C-wW17kd.js → chunk-ENJZ2VHE-CszgAcWZ.js} +1 -1
- package/payload/server/public/assets/{chunk-FMBD7UC4-C_E43NFJ.js → chunk-FMBD7UC4-w-yBZsN2.js} +1 -1
- package/payload/server/public/assets/{chunk-FOC6F5B3-D9lWWHAu.js → chunk-FOC6F5B3-f9cFywhd.js} +1 -1
- package/payload/server/public/assets/{chunk-ICPOFSXX-zLc9NsO5.js → chunk-ICPOFSXX-rgw1I6pt.js} +2 -2
- package/payload/server/public/assets/{chunk-K5T4RW27-DuhsNH4c.js → chunk-K5T4RW27-B_ZUrFUq.js} +1 -1
- package/payload/server/public/assets/{chunk-KGLVRYIC-B4-A1Abi.js → chunk-KGLVRYIC-CcWTvRlI.js} +1 -1
- package/payload/server/public/assets/{chunk-LIHQZDEY-BxqgHRgT.js → chunk-LIHQZDEY-D-5-peQw.js} +1 -1
- package/payload/server/public/assets/{chunk-ORNJ4GCN-DEYQ5WaJ.js → chunk-ORNJ4GCN-B4Z5L25I.js} +1 -1
- package/payload/server/public/assets/{chunk-OYMX7WX6-Bt9RJ_rb.js → chunk-OYMX7WX6-DL4v2Nx4.js} +1 -1
- package/payload/server/public/assets/chunk-QZHKN3VN-CWh_0JsP.js +1 -0
- package/payload/server/public/assets/{chunk-U2HBQHQK-BAM6MO6g.js → chunk-U2HBQHQK-CwMCwyfq.js} +1 -1
- package/payload/server/public/assets/{chunk-X2U36JSP-DbOwgg1R.js → chunk-X2U36JSP-CvKfDVvq.js} +1 -1
- package/payload/server/public/assets/{chunk-XPW4576I-CBfZXZDB.js → chunk-XPW4576I-m1Y_r88I.js} +1 -1
- package/payload/server/public/assets/{chunk-YZCP3GAM-BXUfYLeM.js → chunk-YZCP3GAM-D7vju2_U.js} +1 -1
- package/payload/server/public/assets/{chunk-ZZ45TVLE-BBIHnMjI.js → chunk-ZZ45TVLE-sANOwJKn.js} +1 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-BoqQ3pHg.js +1 -0
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-FoKuBK7n.js +1 -0
- package/payload/server/public/assets/clone-CT8AM-bU.js +1 -0
- package/payload/server/public/assets/{cose-bilkent-S5V4N54A-Br2gjtEO.js → cose-bilkent-S5V4N54A-BhtgY3T7.js} +1 -1
- package/payload/server/public/assets/{dagre-Bv8aHCaJ.js → dagre-CplqWwSs.js} +1 -1
- package/payload/server/public/assets/{dagre-KV5264BT-BZ0q7dDA.js → dagre-KV5264BT-DNbl5g9o.js} +1 -1
- package/payload/server/public/assets/data-BIDM6J5V.js +1 -0
- package/payload/server/public/assets/{device-url-actions-CK6x8-FQ.js → device-url-actions-nxh9PmIH.js} +1 -1
- package/payload/server/public/assets/{diagram-5BDNPKRD-Bbzy2KoW.js → diagram-5BDNPKRD-BCdMaYnY.js} +1 -1
- package/payload/server/public/assets/{diagram-G4DWMVQ6-CadVHWrV.js → diagram-G4DWMVQ6-8XhDBU0o.js} +1 -1
- package/payload/server/public/assets/{diagram-MMDJMWI5-DwJ6fVMv.js → diagram-MMDJMWI5-M-RMQgtx.js} +1 -1
- package/payload/server/public/assets/{diagram-TYMM5635-Bh8d1vg-.js → diagram-TYMM5635-BBHPIBvQ.js} +1 -1
- package/payload/server/public/assets/{erDiagram-SMLLAGMA-B1oTY2ob.js → erDiagram-SMLLAGMA-D6jOL-9s.js} +1 -1
- package/payload/server/public/assets/{flatten-Bo6YRmWl.js → flatten-BsWEYbBB.js} +1 -1
- package/payload/server/public/assets/{flowDiagram-DWJPFMVM-BZU76iLs.js → flowDiagram-DWJPFMVM-CiyTYRlU.js} +1 -1
- package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-PjhJx62i.js → ganttDiagram-T4ZO3ILL-DHSIpjfS.js} +1 -1
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-DeTNsAO0.js +1 -0
- package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-DLrxzJYi.js → gitGraphDiagram-UUTBAWPF-CEneVF3o.js} +1 -1
- package/payload/server/public/assets/graph-Cm-iWFqy.js +1 -0
- package/payload/server/public/assets/graph-labels-CFX6KkCz.js +1 -0
- package/payload/server/public/assets/{graphlib-D2ExKXiT.js → graphlib-CuF7rLfE.js} +1 -1
- package/payload/server/public/assets/info-OMHHGYJF-DJJ9GlS6.js +1 -0
- package/payload/server/public/assets/infoDiagram-42DDH7IO-wGl-JoP7.js +2 -0
- package/payload/server/public/assets/{isEmpty-D6Kr-M1M.js → isEmpty-BWl67LAZ.js} +1 -1
- package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-Ed8ANWEq.js → ishikawaDiagram-UXIWVN3A-B3TxFNi_.js} +1 -1
- package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-DF1gG775.js → journeyDiagram-VCZTEJTY-B5n8No7p.js} +1 -1
- package/payload/server/public/assets/{kanban-definition-6JOO6SKY-D8VcaKAp.js → kanban-definition-6JOO6SKY-ByGF39_h.js} +1 -1
- package/payload/server/public/assets/{line-CgMZIaVM.js → line-Br0FyyFW.js} +1 -1
- package/payload/server/public/assets/{linear-DOh_6k2k.js → linear-0O14Y6uf.js} +1 -1
- package/payload/server/public/assets/{mermaid-parser.core-ez9lChuL.js → mermaid-parser.core-BUcTjodx.js} +2 -2
- package/payload/server/public/assets/{mermaid.core-BiVW_Lka.js → mermaid.core-BKzdtZgG.js} +3 -3
- package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-C-BlLBbo.js → mindmap-definition-QFDTVHPH-CobGMF9l.js} +1 -1
- package/payload/server/public/assets/{ordinal-BDi6f4xk.js → ordinal-krseTxxN.js} +1 -1
- package/payload/server/public/assets/packet-4T2RLAQJ-CGbvGkvF.js +1 -0
- package/payload/server/public/assets/{page-BQCQwUG5.js → page-B5NRnW0t.js} +1 -1
- package/payload/server/public/assets/{page-DStDaJgc.js → page-Ba0BAfpu.js} +2 -2
- package/payload/server/public/assets/pie-ZZUOXDRM-BZy8rjFn.js +1 -0
- package/payload/server/public/assets/{pieDiagram-DEJITSTG-BK0-LxVl.js → pieDiagram-DEJITSTG-DXFmGcNR.js} +1 -1
- package/payload/server/public/assets/{public-DMk9cZKl.js → public-DWfBzf83.js} +5 -5
- package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-vKAQHu-t.js → quadrantDiagram-34T5L4WZ-BnChStlO.js} +1 -1
- package/payload/server/public/assets/radar-PYXPWWZC-DcfWIVXr.js +1 -0
- package/payload/server/public/assets/{reduce-CGi9ik8i.js → reduce-tk-xY6Fv.js} +1 -1
- package/payload/server/public/assets/{requirementDiagram-MS252O5E-Df9sXR35.js → requirementDiagram-MS252O5E-DdiBKsyO.js} +1 -1
- package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-DG6xirA4.js → sankeyDiagram-XADWPNL6-16-np_56.js} +1 -1
- package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-B1a38KHX.js → sequenceDiagram-FGHM5R23-D67SCRpA.js} +1 -1
- package/payload/server/public/assets/{stateDiagram-FHFEXIEX-CRiIDR4O.js → stateDiagram-FHFEXIEX-CvNJX0YC.js} +1 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-CFw2U4iL.js +1 -0
- package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-CkBOE7Y8.js → timeline-definition-GMOUNBTQ-BJE5YoBa.js} +1 -1
- package/payload/server/public/assets/treeView-SZITEDCU-3WugwVdj.js +1 -0
- package/payload/server/public/assets/treemap-W4RFUUIX-Cf5mDLlu.js +1 -0
- package/payload/server/public/assets/{vennDiagram-DHZGUBPP-CwKBydYc.js → vennDiagram-DHZGUBPP-DF17ddXt.js} +1 -1
- package/payload/server/public/assets/wardley-RL74JXVD-Bv4md4b3.js +1 -0
- package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-BWXYfzIr.js → wardleyDiagram-NUSXRM2D-xn_2XUOo.js} +1 -1
- package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-D-4v5Dy_.js → xychartDiagram-5P7HB3ND-DVF8zf3f.js} +1 -1
- package/payload/server/public/data.html +5 -5
- package/payload/server/public/graph.html +6 -5
- package/payload/server/public/index.html +8 -7
- package/payload/server/public/public.html +5 -4
- package/payload/server/server.js +229 -22
- package/payload/server/public/assets/architecture-YZFGNWBL--v-pJPNp.js +0 -1
- package/payload/server/public/assets/channel-CkgyfA91.js +0 -1
- package/payload/server/public/assets/chunk-336JU56O-CbnUj19H.js +0 -2
- package/payload/server/public/assets/chunk-426QAEUC-DxGLqjw5.js +0 -1
- package/payload/server/public/assets/chunk-QZHKN3VN-Bd-GrQM6.js +0 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-PyvJekA9.js +0 -1
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-6hFNhiTb.js +0 -1
- package/payload/server/public/assets/clone-CFIJrRLl.js +0 -1
- package/payload/server/public/assets/data-BgADjmk1.js +0 -1
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-CI0s_tqn.js +0 -1
- package/payload/server/public/assets/graph-B1_erzSo.js +0 -1
- package/payload/server/public/assets/graph-labels-CpFvcba2.js +0 -1
- package/payload/server/public/assets/info-OMHHGYJF-g3gYW7Qm.js +0 -1
- package/payload/server/public/assets/infoDiagram-42DDH7IO-Cn0r9nCK.js +0 -2
- package/payload/server/public/assets/packet-4T2RLAQJ-CT0TB9HI.js +0 -1
- package/payload/server/public/assets/pie-ZZUOXDRM-CXLe7TFF.js +0 -1
- package/payload/server/public/assets/radar-PYXPWWZC-DnPLBl-D.js +0 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-C8cVmi_o.js +0 -1
- package/payload/server/public/assets/treeView-SZITEDCU-C3cb7Xwe.js +0 -1
- package/payload/server/public/assets/treemap-W4RFUUIX-Dc7G3Bgm.js +0 -1
- package/payload/server/public/assets/wardley-RL74JXVD-DtgibWAt.js +0 -1
- /package/payload/server/public/assets/{_baseFor-Cam2PbSt.js → _baseFor-BHtDrjIo.js} +0 -0
- /package/payload/server/public/assets/{array-DYRGGQae.js → array-DetWRiSa.js} +0 -0
- /package/payload/server/public/assets/{cytoscape.esm-nWsJMTNI.js → cytoscape.esm-C9yNhe1u.js} +0 -0
- /package/payload/server/public/assets/{defaultLocale-Du1XY3Dp.js → defaultLocale-_WRwicXn.js} +0 -0
- /package/payload/server/public/assets/{dist-BzAsli7o.js → dist-Bd4S37oi.js} +0 -0
- /package/payload/server/public/assets/{init-B5BXBRcm.js → init-sTEcj9YX.js} +0 -0
- /package/payload/server/public/assets/{katex-HOUACuRw.js → katex-s61Rgv6l.js} +0 -0
- /package/payload/server/public/assets/{path-CNO468J-.js → path-B0Ik7Tu9.js} +0 -0
- /package/payload/server/public/assets/{rough.esm-DRO6hWPh.js → rough.esm-DKRO8IF-.js} +0 -0
- /package/payload/server/public/assets/{src-CWiyyVfn.js → src-B6XdH6xq.js} +0 -0
package/package.json
CHANGED
|
@@ -129,7 +129,7 @@ Tools are available via the `admin` MCP server.
|
|
|
129
129
|
- `hooks/pre-tool-use.sh` — enforces admin agent write boundaries
|
|
130
130
|
- `hooks/playwright-file-guard.sh` — rewrites file:// URLs to a backgrounded loopback http.server before Playwright sees them
|
|
131
131
|
- `hooks/webfetch-preflight.mjs` — short-circuits WebFetch on JS-SPA shells with a structured `WEBFETCH_CANNOT_READ_JS_SPA` error so the agent surfaces a loud failure to the owner instead of paying the 60s extraction timeout. Fail-open on any internal error.
|
|
132
|
-
- `hooks/turn-completed-graph-write.sh` — Stop hook fired once per completed admin-agent turn. Gates on `MAXY_SESSION_ROLE=admin` + `MAXY_SPECIALIST!=database-operator` so it never recurses into the recorder PTY or fires on public sessions. **Task 147** — the recorder is spawned via the same route Sidebar uses. ONE POST to `POST /api/admin/claude-sessions`, body carries `{specialist:'database-operator', model:'haiku', initialMessage:<json-envelope>, adminSessionId:<op>}` — no synthetic `senderId: 'turn-recorder'` marker. The Hono wrapper bypasses cookie auth on this exact method+path when the request originates from `127.0.0.1` (same trust boundary the claude-session-manager itself relies on), looks up the operator's senderId from the manager's `/<adminSessionId>/meta`, and forwards a Sidebar-shape spawn body. The `/recorder-spawn` sibling route is gone. The hook reads its UI port from `MAXY_UI_INTERNAL_PORT` (stamped on the manager systemd unit) — no fallback; absence emits `[turn-recorder] spawn-failed reason=missing-env env=MAXY_UI_INTERNAL_PORT` to stderr instead of silently 19199-ing. **Task 177** — `initialMessage` is a JSON-stringified envelope; the schema lives in [`platform/plugins/docs/references/admin-session.md`](../docs/references/admin-session.md) under "`initialMessage` JSON envelope (Task 177)". Top-level keys exactly: `turns`, `conversationId`, `accountId`, `occurredAt`. `turns` is the full conversation transcript, oldest first, newest last, with each entry `{ role: "user"|"assistant", text, ts, toolCalls? }`. No windowing, no truncation. Multi-record assistant messages collapse on `message.id`. `tool_use` and matching `tool_result` blocks attach as a single `toolCalls` entry on the owning assistant turn; the user record carrying only the `tool_result` does not create a separate user turn. No leading instruction prose. `toolCalls[].input` and `toolCalls[].output` are native JSON values, never re-stringified. (Replaces Task 175's `(operatorMessage, assistantReply)` pair, which asserted a temporal Q→A relationship the walker never enforced.) One observability line `[turn-recorder] envelope sessionId=<op> turnsCount=<n> userTurns=<n> assistantTurns=<n> toolCallTurns=<n>` precedes `spawn-request`. The envelope rides on the `/spawn` body as a trailing positional argv to `claude`, so the database-operator session's JSONL first `role=user` line is the JSON object verbatim. No separate `POST /:id/input` call, no bracketed-paste, no keystroke injection. The recorder-auto-archive subscriber stops the recorder PTY as soon as its JSONL contains an assistant message with `stop_reason === "end_turn"`. **Task 129** — every emit goes through `POST /api/admin/log-ingest` so the lines land in `server.log` keyed by the operator session id. The chain is `trigger` → `spawn-request` → `spawn-success` → `input-delivered` → `tool-call` × N → `tool-result` × N → `write-complete` → `auto-archive`; each gated-off path emits one `trigger-skipped reason=<role-not-admin|is-recorder|empty-stdin|missing-transcript|conversation-empty>` line. Failure modes — `spawn-failed`, `tool-surface-missing`, `input-failed`, `write-empty`, `auto-archive reason=stale-recorder` — each emit one named line; absence is itself a defect.
|
|
132
|
+
- `hooks/turn-completed-graph-write.sh` — **Dormant since Task 214** — the Stop-hook registration is no longer written to account settings.json; admin delegates graph writes to `database-operator` via the Task tool. The script, envelope walker, loopback `/api/admin/claude-sessions` 127.0.0.1 bypass, `[turn-recorder]` emitters, recorder-auto-archive subscriber, and `initialMessage` envelope spec are preserved as reusable infrastructure for any future autonomous post-turn flow. Historical contract (still describes the dormant path): Stop hook fired once per completed admin-agent turn. Gates on `MAXY_SESSION_ROLE=admin` + `MAXY_SPECIALIST!=database-operator` so it never recurses into the recorder PTY or fires on public sessions. **Task 147** — the recorder is spawned via the same route Sidebar uses. ONE POST to `POST /api/admin/claude-sessions`, body carries `{specialist:'database-operator', model:'haiku', initialMessage:<json-envelope>, adminSessionId:<op>}` — no synthetic `senderId: 'turn-recorder'` marker. The Hono wrapper bypasses cookie auth on this exact method+path when the request originates from `127.0.0.1` (same trust boundary the claude-session-manager itself relies on), looks up the operator's senderId from the manager's `/<adminSessionId>/meta`, and forwards a Sidebar-shape spawn body. The `/recorder-spawn` sibling route is gone. The hook reads its UI port from `MAXY_UI_INTERNAL_PORT` (stamped on the manager systemd unit) — no fallback; absence emits `[turn-recorder] spawn-failed reason=missing-env env=MAXY_UI_INTERNAL_PORT` to stderr instead of silently 19199-ing. **Task 177** — `initialMessage` is a JSON-stringified envelope; the schema lives in [`platform/plugins/docs/references/admin-session.md`](../docs/references/admin-session.md) under "`initialMessage` JSON envelope (Task 177)". Top-level keys exactly: `turns`, `conversationId`, `accountId`, `occurredAt`. `turns` is the full conversation transcript, oldest first, newest last, with each entry `{ role: "user"|"assistant", text, ts, toolCalls? }`. No windowing, no truncation. Multi-record assistant messages collapse on `message.id`. `tool_use` and matching `tool_result` blocks attach as a single `toolCalls` entry on the owning assistant turn; the user record carrying only the `tool_result` does not create a separate user turn. No leading instruction prose. `toolCalls[].input` and `toolCalls[].output` are native JSON values, never re-stringified. (Replaces Task 175's `(operatorMessage, assistantReply)` pair, which asserted a temporal Q→A relationship the walker never enforced.) One observability line `[turn-recorder] envelope sessionId=<op> turnsCount=<n> userTurns=<n> assistantTurns=<n> toolCallTurns=<n>` precedes `spawn-request`. The envelope rides on the `/spawn` body as a trailing positional argv to `claude`, so the database-operator session's JSONL first `role=user` line is the JSON object verbatim. No separate `POST /:id/input` call, no bracketed-paste, no keystroke injection. The recorder-auto-archive subscriber stops the recorder PTY as soon as its JSONL contains an assistant message with `stop_reason === "end_turn"`. **Task 129** — every emit goes through `POST /api/admin/log-ingest` so the lines land in `server.log` keyed by the operator session id. The chain is `trigger` → `spawn-request` → `spawn-success` → `input-delivered` → `tool-call` × N → `tool-result` × N → `write-complete` → `auto-archive`; each gated-off path emits one `trigger-skipped reason=<role-not-admin|is-recorder|empty-stdin|missing-transcript|conversation-empty>` line. Failure modes — `spawn-failed`, `tool-surface-missing`, `input-failed`, `write-empty`, `auto-archive reason=stale-recorder` — each emit one named line; absence is itself a defect.
|
|
133
133
|
|
|
134
134
|
## Session identifiers (Task 135)
|
|
135
135
|
|
package/payload/platform/plugins/admin/hooks/__tests__/pre-tool-use-memory-write-revoke.test.sh
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# Task 213 regression test for the memory-write /
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
2
|
+
# Task 213 (rev. Task 222) regression test for the memory-write /
|
|
3
|
+
# memory-update gate in pre-tool-use.sh. The block sits at the top of the
|
|
4
|
+
# admin branch and:
|
|
5
|
+
# * Blocks an admin direct call (parent JSONL transcript_path) with
|
|
6
|
+
# exit 2, stderr explaining the delegation contract, and one structured
|
|
7
|
+
# `admin-tool-revoke` line carrying caller=admin source=hook result=rejected.
|
|
8
|
+
# * Allows a Task subagent call (transcript_path matches subagent path
|
|
9
|
+
# pattern) with exit 0 and a structured `admin-tool-revoke` line
|
|
10
|
+
# carrying caller=subagent:<type> source=hook-passthrough result=allowed.
|
|
11
|
+
# * Fail-closed blocks any payload with missing/malformed
|
|
12
|
+
# transcript_path, emitting caller=unknown source=hook result=rejected.
|
|
7
13
|
|
|
8
14
|
set -u
|
|
9
15
|
|
|
@@ -58,17 +64,21 @@ PY
|
|
|
58
64
|
run_with_tool() {
|
|
59
65
|
local tool="$1"
|
|
60
66
|
local sid="$2"
|
|
67
|
+
local transcript_path="${3:-}"
|
|
61
68
|
local stdout_file; stdout_file=$(mktemp); TMPFILES+=("$stdout_file")
|
|
62
69
|
local stderr_file; stderr_file=$(mktemp); TMPFILES+=("$stderr_file")
|
|
63
70
|
python3 -c '
|
|
64
71
|
import json, sys
|
|
65
|
-
|
|
72
|
+
payload = {
|
|
66
73
|
"hook_event_name": "PreToolUse",
|
|
67
74
|
"session_id": sys.argv[1],
|
|
68
75
|
"tool_name": sys.argv[2],
|
|
69
76
|
"tool_input": {"name":"Smalleys","accountId":"acct-x","scope":"shared"},
|
|
70
|
-
}
|
|
71
|
-
|
|
77
|
+
}
|
|
78
|
+
if sys.argv[3]:
|
|
79
|
+
payload["transcript_path"] = sys.argv[3]
|
|
80
|
+
print(json.dumps(payload, separators=(",", ":")))
|
|
81
|
+
' "$sid" "$tool" "$transcript_path" | \
|
|
72
82
|
MAXY_UI_INTERNAL_PORT="$LISTENER_PORT" \
|
|
73
83
|
bash "$HOOK" admin >"$stdout_file" 2>"$stderr_file"
|
|
74
84
|
HOOK_RC=$?
|
|
@@ -76,33 +86,141 @@ print(json.dumps({
|
|
|
76
86
|
sleep 0.1 # let listener flush
|
|
77
87
|
}
|
|
78
88
|
|
|
89
|
+
# Helper: stage a synthetic parent transcript + subagent transcript pair
|
|
90
|
+
# under a temp dir. The parent transcript carries one Task tool_use whose
|
|
91
|
+
# input.subagent_type = "$1"; the subagent transcript is empty (the hook
|
|
92
|
+
# does not depend on its contents — only on its path).
|
|
93
|
+
stage_subagent_transcripts() {
|
|
94
|
+
local subagent_type="$1"
|
|
95
|
+
local tmp_root; tmp_root=$(mktemp -d); TMPFILES+=("$tmp_root")
|
|
96
|
+
local sess_id; sess_id="abc-session-uuid"
|
|
97
|
+
local agent_id; agent_id="a1234567890abcdef"
|
|
98
|
+
local parent_jsonl="${tmp_root}/${sess_id}.jsonl"
|
|
99
|
+
local subagent_dir="${tmp_root}/${sess_id}/subagents"
|
|
100
|
+
mkdir -p "$subagent_dir"
|
|
101
|
+
local subagent_jsonl="${subagent_dir}/agent-${agent_id}.jsonl"
|
|
102
|
+
# Parent: minimal assistant row with a Task tool_use carrying subagent_type
|
|
103
|
+
python3 -c '
|
|
104
|
+
import json, sys
|
|
105
|
+
row = {
|
|
106
|
+
"type": "assistant",
|
|
107
|
+
"uuid": "u-1",
|
|
108
|
+
"parentUuid": None,
|
|
109
|
+
"sessionId": "abc-session-uuid",
|
|
110
|
+
"message": {
|
|
111
|
+
"role": "assistant",
|
|
112
|
+
"content": [
|
|
113
|
+
{"type": "tool_use", "id": "toolu_T1", "name": "Task",
|
|
114
|
+
"input": {"subagent_type": sys.argv[1], "description": "write Beacons"}}
|
|
115
|
+
]
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
print(json.dumps(row, separators=(",", ":")))
|
|
119
|
+
' "$subagent_type" > "$parent_jsonl"
|
|
120
|
+
# Subagent: empty file is enough — the hook only checks the path pattern
|
|
121
|
+
: > "$subagent_jsonl"
|
|
122
|
+
echo "$subagent_jsonl"
|
|
123
|
+
}
|
|
124
|
+
|
|
79
125
|
start_listener
|
|
80
126
|
|
|
81
|
-
#
|
|
127
|
+
# A parent-transcript path is the default for admin-direct cases. The
|
|
128
|
+
# hook treats any non-subagent path as admin direct, so a synthetic file
|
|
129
|
+
# in a temp dir suffices — we don't need to populate it.
|
|
130
|
+
ADMIN_TRANSCRIPT=$(mktemp); TMPFILES+=("$ADMIN_TRANSCRIPT")
|
|
131
|
+
: > "$ADMIN_TRANSCRIPT"
|
|
132
|
+
|
|
133
|
+
# --- admin-direct memory-write rejected, emits caller=admin -----------
|
|
82
134
|
: > "$REQ_LOG"
|
|
83
|
-
run_with_tool "mcp__plugin_memory_memory__memory-write" "sess-abc"
|
|
84
|
-
[[ "$HOOK_RC" -eq 2 ]] || fail "memory-write expected exit 2, got $HOOK_RC"
|
|
85
|
-
echo "$HOOK_STDERR" | grep -qF "admin agent does not write to the graph" \
|
|
86
|
-
|| fail "memory-write stderr missing rationale, got: $HOOK_STDERR"
|
|
87
|
-
if grep -qE '/api/admin/log-ingest .*"tag":\s*"admin-tool-revoke".*sessionId=sess-abc.*tool=mcp__plugin_memory_memory__memory-write.*source=
|
|
88
|
-
pass "memory-write
|
|
135
|
+
run_with_tool "mcp__plugin_memory_memory__memory-write" "sess-abc" "$ADMIN_TRANSCRIPT"
|
|
136
|
+
[[ "$HOOK_RC" -eq 2 ]] || fail "admin-direct memory-write expected exit 2, got $HOOK_RC"
|
|
137
|
+
echo "$HOOK_STDERR" | grep -qF "admin agent does not write to the graph directly" \
|
|
138
|
+
|| fail "admin-direct memory-write stderr missing rationale, got: $HOOK_STDERR"
|
|
139
|
+
if grep -qE '/api/admin/log-ingest .*"tag":\s*"admin-tool-revoke".*sessionId=sess-abc.*tool=mcp__plugin_memory_memory__memory-write.*caller=admin.*source=hook.*result=rejected' "$REQ_LOG"; then
|
|
140
|
+
pass "admin-direct memory-write blocks with caller=admin source=hook result=rejected"
|
|
89
141
|
else
|
|
90
|
-
fail "memory-write
|
|
142
|
+
fail "admin-direct memory-write did not emit expected log line; log: $(cat "$REQ_LOG")"
|
|
91
143
|
fi
|
|
92
144
|
|
|
93
|
-
# --- memory-update rejected
|
|
145
|
+
# --- admin-direct memory-update rejected, emits caller=admin ----------
|
|
94
146
|
: > "$REQ_LOG"
|
|
95
|
-
run_with_tool "mcp__plugin_memory_memory__memory-update" "sess-xyz"
|
|
96
|
-
[[ "$HOOK_RC" -eq 2 ]] || fail "memory-update expected exit 2, got $HOOK_RC"
|
|
97
|
-
if grep -qE '/api/admin/log-ingest .*"tag":\s*"admin-tool-revoke".*sessionId=sess-xyz.*tool=mcp__plugin_memory_memory__memory-update.*source=
|
|
98
|
-
pass "memory-update
|
|
147
|
+
run_with_tool "mcp__plugin_memory_memory__memory-update" "sess-xyz" "$ADMIN_TRANSCRIPT"
|
|
148
|
+
[[ "$HOOK_RC" -eq 2 ]] || fail "admin-direct memory-update expected exit 2, got $HOOK_RC"
|
|
149
|
+
if grep -qE '/api/admin/log-ingest .*"tag":\s*"admin-tool-revoke".*sessionId=sess-xyz.*tool=mcp__plugin_memory_memory__memory-update.*caller=admin.*source=hook.*result=rejected' "$REQ_LOG"; then
|
|
150
|
+
pass "admin-direct memory-update blocks with caller=admin source=hook result=rejected"
|
|
99
151
|
else
|
|
100
|
-
fail "memory-update
|
|
152
|
+
fail "admin-direct memory-update did not emit expected log line; log: $(cat "$REQ_LOG")"
|
|
101
153
|
fi
|
|
102
154
|
|
|
155
|
+
# --- Task-subagent memory-write ALLOWED, emits caller=subagent:<type> -
|
|
156
|
+
# The path-pattern match (*/subagents/agent-*.jsonl) is the deterministic
|
|
157
|
+
# discriminator; the parent transcript carries the subagent_type for
|
|
158
|
+
# enrichment.
|
|
159
|
+
: > "$REQ_LOG"
|
|
160
|
+
SUB_TRANSCRIPT=$(stage_subagent_transcripts "database-operator")
|
|
161
|
+
run_with_tool "mcp__plugin_memory_memory__memory-write" "sess-sub-1" "$SUB_TRANSCRIPT"
|
|
162
|
+
[[ "$HOOK_RC" -eq 0 ]] || fail "subagent memory-write expected exit 0, got $HOOK_RC stderr: $HOOK_STDERR"
|
|
163
|
+
if grep -qE '/api/admin/log-ingest .*"tag":\s*"admin-tool-revoke".*sessionId=sess-sub-1.*tool=mcp__plugin_memory_memory__memory-write.*caller=subagent:database-operator.*source=hook-passthrough.*result=allowed' "$REQ_LOG"; then
|
|
164
|
+
pass "subagent memory-write allowed with caller=subagent:database-operator source=hook-passthrough result=allowed"
|
|
165
|
+
else
|
|
166
|
+
fail "subagent memory-write did not emit expected allow log line; log: $(cat "$REQ_LOG")"
|
|
167
|
+
fi
|
|
168
|
+
|
|
169
|
+
# --- Task-subagent memory-update ALLOWED ------------------------------
|
|
170
|
+
: > "$REQ_LOG"
|
|
171
|
+
SUB_TRANSCRIPT=$(stage_subagent_transcripts "database-operator")
|
|
172
|
+
run_with_tool "mcp__plugin_memory_memory__memory-update" "sess-sub-2" "$SUB_TRANSCRIPT"
|
|
173
|
+
[[ "$HOOK_RC" -eq 0 ]] || fail "subagent memory-update expected exit 0, got $HOOK_RC stderr: $HOOK_STDERR"
|
|
174
|
+
if grep -qE '/api/admin/log-ingest .*"tag":\s*"admin-tool-revoke".*sessionId=sess-sub-2.*tool=mcp__plugin_memory_memory__memory-update.*caller=subagent:database-operator.*source=hook-passthrough.*result=allowed' "$REQ_LOG"; then
|
|
175
|
+
pass "subagent memory-update allowed with caller=subagent:database-operator source=hook-passthrough result=allowed"
|
|
176
|
+
else
|
|
177
|
+
fail "subagent memory-update did not emit expected allow log line; log: $(cat "$REQ_LOG")"
|
|
178
|
+
fi
|
|
179
|
+
|
|
180
|
+
# --- Subagent path with unreadable parent transcript falls back to bare
|
|
181
|
+
# `caller=subagent` (no name) but still ALLOWS the call. The hook never
|
|
182
|
+
# blocks a legitimate subagent caller because the parent enrichment fails.
|
|
183
|
+
: > "$REQ_LOG"
|
|
184
|
+
ORPHAN_TMP=$(mktemp -d); TMPFILES+=("$ORPHAN_TMP")
|
|
185
|
+
ORPHAN_DIR="${ORPHAN_TMP}/orphan-sess/subagents"
|
|
186
|
+
mkdir -p "$ORPHAN_DIR"
|
|
187
|
+
ORPHAN_SUB="${ORPHAN_DIR}/agent-aorphanid.jsonl"
|
|
188
|
+
: > "$ORPHAN_SUB"
|
|
189
|
+
run_with_tool "mcp__plugin_memory_memory__memory-write" "sess-orphan" "$ORPHAN_SUB"
|
|
190
|
+
[[ "$HOOK_RC" -eq 0 ]] || fail "subagent w/ missing parent expected exit 0, got $HOOK_RC"
|
|
191
|
+
if grep -qE '/api/admin/log-ingest .*caller=subagent[^:].*source=hook-passthrough.*result=allowed' "$REQ_LOG"; then
|
|
192
|
+
pass "subagent caller without resolvable subagent_type falls back to bare 'subagent' label and still allows"
|
|
193
|
+
else
|
|
194
|
+
fail "subagent w/ missing parent did not fall back to bare caller=subagent; log: $(cat "$REQ_LOG")"
|
|
195
|
+
fi
|
|
196
|
+
|
|
197
|
+
# --- Missing transcript_path → fail-closed block, caller=unknown ------
|
|
198
|
+
: > "$REQ_LOG"
|
|
199
|
+
run_with_tool "mcp__plugin_memory_memory__memory-write" "sess-no-tp" ""
|
|
200
|
+
[[ "$HOOK_RC" -eq 2 ]] || fail "missing transcript_path expected exit 2 (fail-closed), got $HOOK_RC"
|
|
201
|
+
if grep -qE '/api/admin/log-ingest .*"tag":\s*"admin-tool-revoke".*sessionId=sess-no-tp.*caller=unknown.*source=hook.*result=rejected' "$REQ_LOG"; then
|
|
202
|
+
pass "missing transcript_path fail-closes with caller=unknown source=hook result=rejected"
|
|
203
|
+
else
|
|
204
|
+
fail "missing transcript_path did not fail-closed with expected log; log: $(cat "$REQ_LOG")"
|
|
205
|
+
fi
|
|
206
|
+
|
|
207
|
+
# --- Malformed stdin payload → fail-closed block ----------------------
|
|
208
|
+
: > "$REQ_LOG"
|
|
209
|
+
stdout_file=$(mktemp); TMPFILES+=("$stdout_file")
|
|
210
|
+
stderr_file=$(mktemp); TMPFILES+=("$stderr_file")
|
|
211
|
+
echo "not-valid-json{{{" | \
|
|
212
|
+
MAXY_UI_INTERNAL_PORT="$LISTENER_PORT" \
|
|
213
|
+
bash "$HOOK" admin >"$stdout_file" 2>"$stderr_file"
|
|
214
|
+
HOOK_RC=$?
|
|
215
|
+
# A non-JSON payload also defeats the upstream TOOL_NAME extraction (grep
|
|
216
|
+
# pattern matches nothing), so this confirms the hook reaches its fall-
|
|
217
|
+
# through path safely without parse-induced crash.
|
|
218
|
+
[[ "$HOOK_RC" -eq 0 ]] || [[ "$HOOK_RC" -eq 2 ]] || fail "malformed payload produced unexpected exit $HOOK_RC"
|
|
219
|
+
pass "malformed payload handled without crash (exit $HOOK_RC)"
|
|
220
|
+
|
|
103
221
|
# --- memory-search (allowed read tool) NOT rejected --------------------
|
|
104
222
|
: > "$REQ_LOG"
|
|
105
|
-
run_with_tool "mcp__plugin_memory_memory__memory-search" "sess-read"
|
|
223
|
+
run_with_tool "mcp__plugin_memory_memory__memory-search" "sess-read" "$ADMIN_TRANSCRIPT"
|
|
106
224
|
[[ "$HOOK_RC" -eq 0 ]] || fail "memory-search expected exit 0, got $HOOK_RC stderr: $HOOK_STDERR"
|
|
107
225
|
if grep -qE '"tag":\s*"admin-tool-revoke"' "$REQ_LOG"; then
|
|
108
226
|
fail "memory-search must NOT emit admin-tool-revoke; log: $(cat "$REQ_LOG")"
|
|
@@ -115,7 +233,7 @@ fi
|
|
|
115
233
|
# old namespace was never the surface admin saw, so matching it would
|
|
116
234
|
# be a regression.
|
|
117
235
|
: > "$REQ_LOG"
|
|
118
|
-
run_with_tool "mcp__memory__memory-write" "sess-old-ns"
|
|
236
|
+
run_with_tool "mcp__memory__memory-write" "sess-old-ns" "$ADMIN_TRANSCRIPT"
|
|
119
237
|
[[ "$HOOK_RC" -eq 0 ]] || fail "pre-209 namespace expected exit 0, got $HOOK_RC"
|
|
120
238
|
if grep -qE '"tag":\s*"admin-tool-revoke"' "$REQ_LOG"; then
|
|
121
239
|
fail "pre-209 namespace must NOT emit admin-tool-revoke"
|