@rubytech/create-maxy-code 0.1.191 → 0.1.193
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/__tests__/brew-resolve.test.js +1 -1
- package/dist/__tests__/macos-darwin-branch.test.js +1 -1
- package/dist/index.js +2 -2
- package/package.json +1 -1
- package/payload/platform/lib/mcp-eager/dist/index.d.ts +55 -0
- package/payload/platform/lib/mcp-eager/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/mcp-eager/dist/index.js +76 -0
- package/payload/platform/lib/mcp-eager/dist/index.js.map +1 -1
- package/payload/platform/lib/mcp-eager/package.json +7 -0
- package/payload/platform/lib/mcp-eager/src/index.ts +101 -0
- package/payload/platform/plugins/admin/hooks/__tests__/pre-tool-use-admin-tool-gate.test.sh +102 -0
- package/payload/platform/plugins/admin/hooks/__tests__/pre-tool-use-base64-guard.test.sh +4 -4
- package/payload/platform/plugins/admin/hooks/pre-tool-use.sh +56 -0
- package/payload/platform/plugins/browser/mcp/dist/index.js +19 -19
- package/payload/platform/plugins/browser/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/docs/references/projects-guide.md +21 -0
- package/payload/platform/plugins/email/mcp/dist/index.js +12 -12
- package/payload/platform/plugins/email/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/index.js +8 -8
- package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js +35 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js.map +1 -1
- package/payload/platform/plugins/prompt-optimiser/skills/prompt-optimiser/SKILL.md +1 -0
- package/payload/platform/plugins/url-get/mcp/dist/index.js +2 -2
- package/payload/platform/plugins/url-get/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/work/PLUGIN.md +2 -0
- package/payload/platform/plugins/work/skills/execute-task/SKILL.md +101 -0
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.js +46 -2
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -1
- package/payload/premium-plugins/writer-craft/lib/mcp-eager/dist/index.d.ts +116 -0
- package/payload/premium-plugins/writer-craft/lib/mcp-eager/dist/index.d.ts.map +1 -0
- package/payload/premium-plugins/writer-craft/lib/mcp-eager/dist/index.js +125 -0
- package/payload/premium-plugins/writer-craft/lib/mcp-eager/dist/index.js.map +1 -0
- package/payload/premium-plugins/writer-craft/lib/mcp-eager/package.json +7 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/index.d.ts.map +1 -1
- package/payload/premium-plugins/writer-craft/mcp/dist/index.js +5 -4
- package/payload/premium-plugins/writer-craft/mcp/dist/index.js.map +1 -1
- package/payload/premium-plugins/writer-craft/mcp/src/index.ts +5 -4
- package/payload/server/public/assets/AdminShell-BoJbnn6d.js +1 -0
- package/payload/server/public/assets/{Checkbox-g2WZRfCt.js → Checkbox-G-kyM1P1.js} +1 -1
- package/payload/server/public/assets/admin-B-GDNkoN.js +1 -0
- package/payload/server/public/assets/{arc-DMDAZHAN.js → arc-aUiRP9AS.js} +1 -1
- package/payload/server/public/assets/architecture-YZFGNWBL--v-pJPNp.js +1 -0
- package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-BVKxPUIS.js → architectureDiagram-Q4EWVU46-BGgdXdD2.js} +1 -1
- package/payload/server/public/assets/{blockDiagram-DXYQGD6D-BpD-1G1V.js → blockDiagram-DXYQGD6D-DKWoL4Rp.js} +1 -1
- package/payload/server/public/assets/{brand-BGvv6AYI.js → brand-CYcGHhlS.js} +1 -1
- package/payload/server/public/assets/{brand-BQW2DYy_.css → brand-MbpD2Bhr.css} +1 -1
- package/payload/server/public/assets/{c4Diagram-AHTNJAMY-CFl9Dr6U.js → c4Diagram-AHTNJAMY-Ca6rs6F4.js} +1 -1
- package/payload/server/public/assets/channel-BwsV7WTV.js +1 -0
- package/payload/server/public/assets/{chunk-2KRD3SAO-Di4bO8ir.js → chunk-2KRD3SAO-BK3470lx.js} +1 -1
- package/payload/server/public/assets/{chunk-336JU56O-B2Uc6pEC.js → chunk-336JU56O-DcdMZImV.js} +2 -2
- package/payload/server/public/assets/chunk-426QAEUC-9FSP-c1z.js +1 -0
- package/payload/server/public/assets/{chunk-4BX2VUAB-DyEhFk-Z.js → chunk-4BX2VUAB-BOvVdJLf.js} +1 -1
- package/payload/server/public/assets/{chunk-4TB4RGXK-Dvf5uiFR.js → chunk-4TB4RGXK-CC1QpEvA.js} +1 -1
- package/payload/server/public/assets/{chunk-55IACEB6-BRJOZLpU.js → chunk-55IACEB6-BwZyF7vR.js} +1 -1
- package/payload/server/public/assets/{chunk-5FUZZQ4R-DUQR1rF9.js → chunk-5FUZZQ4R-o1b70dEE.js} +1 -1
- package/payload/server/public/assets/{chunk-5PVQY5BW-OTvNug7K.js → chunk-5PVQY5BW-C7VCYRz9.js} +1 -1
- package/payload/server/public/assets/{chunk-67CJDMHE-BG6-9r6c.js → chunk-67CJDMHE-D5bhMrtY.js} +1 -1
- package/payload/server/public/assets/{chunk-7N4EOEYR-BvMbitX7.js → chunk-7N4EOEYR-Si7Lgrwc.js} +1 -1
- package/payload/server/public/assets/{chunk-AA7GKIK3-CE8mGBD5.js → chunk-AA7GKIK3-DMuHtDqO.js} +1 -1
- package/payload/server/public/assets/{chunk-BSJP7CBP-DP7LTBll.js → chunk-BSJP7CBP-L79XKVcb.js} +1 -1
- package/payload/server/public/assets/{chunk-CIAEETIT-VfnIdN-h.js → chunk-CIAEETIT-C0O7Upmg.js} +1 -1
- package/payload/server/public/assets/{chunk-EDXVE4YY-CRAsAWbD.js → chunk-EDXVE4YY-B8d5xj_4.js} +1 -1
- package/payload/server/public/assets/{chunk-ENJZ2VHE-BNllMoi_.js → chunk-ENJZ2VHE-SSbr4n2X.js} +1 -1
- package/payload/server/public/assets/{chunk-FMBD7UC4-w-yBZsN2.js → chunk-FMBD7UC4-C_E43NFJ.js} +1 -1
- package/payload/server/public/assets/{chunk-FOC6F5B3-f9cFywhd.js → chunk-FOC6F5B3-D9lWWHAu.js} +1 -1
- package/payload/server/public/assets/{chunk-ICPOFSXX-vTeVS4qd.js → chunk-ICPOFSXX-Cgj9YSpX.js} +2 -2
- package/payload/server/public/assets/{chunk-K5T4RW27-B_ZUrFUq.js → chunk-K5T4RW27-DuhsNH4c.js} +1 -1
- package/payload/server/public/assets/{chunk-KGLVRYIC-CcWTvRlI.js → chunk-KGLVRYIC-B4-A1Abi.js} +1 -1
- package/payload/server/public/assets/{chunk-LIHQZDEY-D-5-peQw.js → chunk-LIHQZDEY-BxqgHRgT.js} +1 -1
- package/payload/server/public/assets/{chunk-ORNJ4GCN-B4Z5L25I.js → chunk-ORNJ4GCN-DEYQ5WaJ.js} +1 -1
- package/payload/server/public/assets/{chunk-OYMX7WX6-BdSFcvNa.js → chunk-OYMX7WX6-DNEEcCqI.js} +1 -1
- package/payload/server/public/assets/chunk-QZHKN3VN-Bd-GrQM6.js +1 -0
- package/payload/server/public/assets/{chunk-U2HBQHQK-BpFXup4D.js → chunk-U2HBQHQK-DuaoK83q.js} +1 -1
- package/payload/server/public/assets/{chunk-X2U36JSP-DF-6QHx4.js → chunk-X2U36JSP-CzMp0dpq.js} +1 -1
- package/payload/server/public/assets/{chunk-XPW4576I-m1Y_r88I.js → chunk-XPW4576I-CBfZXZDB.js} +1 -1
- package/payload/server/public/assets/{chunk-YZCP3GAM-CKgDoLRT.js → chunk-YZCP3GAM-B2wBDwSa.js} +1 -1
- package/payload/server/public/assets/{chunk-ZZ45TVLE-B0ACvZCZ.js → chunk-ZZ45TVLE-DkyF7XKW.js} +1 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-CEWNfHVo.js +1 -0
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-BBjzRxux.js +1 -0
- package/payload/server/public/assets/clone-CXtSonnn.js +1 -0
- package/payload/server/public/assets/{cose-bilkent-S5V4N54A-BhtgY3T7.js → cose-bilkent-S5V4N54A-Br2gjtEO.js} +1 -1
- package/payload/server/public/assets/{dagre-3yl9rfxR.js → dagre-AGj1P0p4.js} +1 -1
- package/payload/server/public/assets/{dagre-KV5264BT-D-Ux4uwL.js → dagre-KV5264BT-Cnrxcu6A.js} +1 -1
- package/payload/server/public/assets/data-Bg0Frxt3.js +1 -0
- package/payload/server/public/assets/{diagram-5BDNPKRD-D-MOMikl.js → diagram-5BDNPKRD-gyFYSx73.js} +1 -1
- package/payload/server/public/assets/{diagram-G4DWMVQ6-DKdFcnhf.js → diagram-G4DWMVQ6-DFmZEv5C.js} +1 -1
- package/payload/server/public/assets/{diagram-MMDJMWI5-ajamR766.js → diagram-MMDJMWI5-DA4qbuEW.js} +1 -1
- package/payload/server/public/assets/{diagram-TYMM5635-D2Pbk2Kk.js → diagram-TYMM5635-D4n-8gig.js} +1 -1
- package/payload/server/public/assets/{erDiagram-SMLLAGMA-v3o948pk.js → erDiagram-SMLLAGMA-CigRMjF1.js} +1 -1
- package/payload/server/public/assets/{flatten-BsWEYbBB.js → flatten-Bo6YRmWl.js} +1 -1
- package/payload/server/public/assets/{flowDiagram-DWJPFMVM-byC1a6GE.js → flowDiagram-DWJPFMVM-ClS27acA.js} +1 -1
- package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-Dybzs-Sw.js → ganttDiagram-T4ZO3ILL-Dr1IulXe.js} +1 -1
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-CI0s_tqn.js +1 -0
- package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-DvzXYLuW.js → gitGraphDiagram-UUTBAWPF-lwLibdPs.js} +1 -1
- package/payload/server/public/assets/graph-CpozZ74I.js +51 -0
- package/payload/server/public/assets/graph-labels-C2y666R0.js +1 -0
- package/payload/server/public/assets/{graphlib-DvH_spaA.js → graphlib-DBvoJMrc.js} +1 -1
- package/payload/server/public/assets/info-OMHHGYJF-g3gYW7Qm.js +1 -0
- package/payload/server/public/assets/infoDiagram-42DDH7IO-D4kReEIV.js +2 -0
- package/payload/server/public/assets/{isEmpty-BWl67LAZ.js → isEmpty-D6Kr-M1M.js} +1 -1
- package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-CpY5W64P.js → ishikawaDiagram-UXIWVN3A-BnteQRQ0.js} +1 -1
- package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-ymMAQVdm.js → journeyDiagram-VCZTEJTY-CXu6hLyJ.js} +1 -1
- package/payload/server/public/assets/{kanban-definition-6JOO6SKY-_NX9hNK3.js → kanban-definition-6JOO6SKY-CGDW2jle.js} +1 -1
- package/payload/server/public/assets/{line-Dz7zKWX-.js → line-pdg5hxC3.js} +1 -1
- package/payload/server/public/assets/{linear-0O14Y6uf.js → linear-DOh_6k2k.js} +1 -1
- package/payload/server/public/assets/{mermaid-parser.core-BCWd1en6.js → mermaid-parser.core-o3lIIK3Z.js} +2 -2
- package/payload/server/public/assets/{mermaid.core-gSoFkVoN.js → mermaid.core-DLR6Q3rV.js} +3 -3
- package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-QuIyK2bd.js → mindmap-definition-QFDTVHPH-CH4cB0Dn.js} +1 -1
- package/payload/server/public/assets/{ordinal-krseTxxN.js → ordinal-BDi6f4xk.js} +1 -1
- package/payload/server/public/assets/packet-4T2RLAQJ-CT0TB9HI.js +1 -0
- package/payload/server/public/assets/pie-ZZUOXDRM-CXLe7TFF.js +1 -0
- package/payload/server/public/assets/{pieDiagram-DEJITSTG-DHK0yy7U.js → pieDiagram-DEJITSTG-Ba4voL_Y.js} +1 -1
- package/payload/server/public/assets/public-C8mil9h0.js +35 -0
- package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-wK1jwWo5.js → quadrantDiagram-34T5L4WZ-Cqn2xehe.js} +1 -1
- package/payload/server/public/assets/radar-PYXPWWZC-DnPLBl-D.js +1 -0
- package/payload/server/public/assets/{reduce-tk-xY6Fv.js → reduce-CGi9ik8i.js} +1 -1
- package/payload/server/public/assets/{requirementDiagram-MS252O5E-W_mH85_d.js → requirementDiagram-MS252O5E-DLfNx99l.js} +1 -1
- package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-nGAM-YLQ.js → sankeyDiagram-XADWPNL6-uck_V-cb.js} +1 -1
- package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-SMfUCwBH.js → sequenceDiagram-FGHM5R23-DmQalqud.js} +1 -1
- package/payload/server/public/assets/{stateDiagram-FHFEXIEX-CYKXFirH.js → stateDiagram-FHFEXIEX-DUdWeVRa.js} +1 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-DNTpFwdy.js +1 -0
- package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-DethbNFa.js → timeline-definition-GMOUNBTQ-BgL1wM5n.js} +1 -1
- package/payload/server/public/assets/treeView-SZITEDCU-C3cb7Xwe.js +1 -0
- package/payload/server/public/assets/treemap-W4RFUUIX-Dc7G3Bgm.js +1 -0
- package/payload/server/public/assets/useSelectionMode-BLpVlOgm.js +5 -0
- package/payload/server/public/assets/{vennDiagram-DHZGUBPP-BUlJTe9Z.js → vennDiagram-DHZGUBPP-7FHjIqPi.js} +1 -1
- package/payload/server/public/assets/wardley-RL74JXVD-DtgibWAt.js +1 -0
- package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-CL1TvZ7j.js → wardleyDiagram-NUSXRM2D-BHzo4V1J.js} +1 -1
- package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-Cav10l-Z.js → xychartDiagram-5P7HB3ND-DtuWhqwh.js} +1 -1
- package/payload/server/public/data.html +5 -5
- package/payload/server/public/graph.html +6 -6
- package/payload/server/public/index.html +6 -8
- package/payload/server/public/public.html +5 -5
- package/payload/server/server.js +478 -456
- package/payload/server/public/assets/admin-DFxXLXfg.js +0 -216
- package/payload/server/public/assets/architecture-YZFGNWBL-COhEvUpo.js +0 -1
- package/payload/server/public/assets/channel-BTEFjudQ.js +0 -1
- package/payload/server/public/assets/chunk-426QAEUC-CcIGcm29.js +0 -1
- package/payload/server/public/assets/chunk-QZHKN3VN-CWh_0JsP.js +0 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-ClkdniKF.js +0 -1
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-kjJetfh2.js +0 -1
- package/payload/server/public/assets/clone-BllVm2iY.js +0 -1
- package/payload/server/public/assets/data-DtYcqL1G.js +0 -1
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-DeTNsAO0.js +0 -1
- package/payload/server/public/assets/graph-C-XPyGsO.js +0 -1
- package/payload/server/public/assets/graph-labels-CZin3myX.js +0 -1
- package/payload/server/public/assets/info-OMHHGYJF-DJJ9GlS6.js +0 -1
- package/payload/server/public/assets/infoDiagram-42DDH7IO-C2FeU8nE.js +0 -2
- package/payload/server/public/assets/lib-BEvO-mE0.js +0 -33
- package/payload/server/public/assets/packet-4T2RLAQJ-CGbvGkvF.js +0 -1
- package/payload/server/public/assets/page-CKlvbjKn.js +0 -1
- package/payload/server/public/assets/page-DSj21hpG.js +0 -51
- package/payload/server/public/assets/pie-ZZUOXDRM-BZy8rjFn.js +0 -1
- package/payload/server/public/assets/public-BnS9zREj.js +0 -7
- package/payload/server/public/assets/radar-PYXPWWZC-DcfWIVXr.js +0 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-B9ZwM8kx.js +0 -1
- package/payload/server/public/assets/treeView-SZITEDCU-3WugwVdj.js +0 -1
- package/payload/server/public/assets/treemap-W4RFUUIX-Cf5mDLlu.js +0 -1
- package/payload/server/public/assets/wardley-RL74JXVD-Bv4md4b3.js +0 -1
- /package/payload/server/public/assets/{_baseFor-BHtDrjIo.js → _baseFor-Cam2PbSt.js} +0 -0
- /package/payload/server/public/assets/{array-DetWRiSa.js → array-DYRGGQae.js} +0 -0
- /package/payload/server/public/assets/{cytoscape.esm-C9yNhe1u.js → cytoscape.esm-nWsJMTNI.js} +0 -0
- /package/payload/server/public/assets/{defaultLocale-_WRwicXn.js → defaultLocale-Du1XY3Dp.js} +0 -0
- /package/payload/server/public/assets/{dist-Bd4S37oi.js → dist-BzAsli7o.js} +0 -0
- /package/payload/server/public/assets/{init-sTEcj9YX.js → init-B5BXBRcm.js} +0 -0
- /package/payload/server/public/assets/{katex-s61Rgv6l.js → katex-HOUACuRw.js} +0 -0
- /package/payload/server/public/assets/{path-B0Ik7Tu9.js → path-CNO468J-.js} +0 -0
- /package/payload/server/public/assets/{rough.esm-DKRO8IF-.js → rough.esm-DRO6hWPh.js} +0 -0
- /package/payload/server/public/assets/{src-B6XdH6xq.js → src-CWiyyVfn.js} +0 -0
|
@@ -110,7 +110,7 @@ test("DARWIN_ALIASES contains nodejs → node@22 and is the only entry", () => {
|
|
|
110
110
|
// ---------------------------------------------------------------------------
|
|
111
111
|
// Mirrors `DARWIN_BASE_DEPS` in packages/create-maxy-code/src/index.ts. Every
|
|
112
112
|
// entry must be either a real brew formula (pass-through) or an alias.
|
|
113
|
-
const DARWIN_BASE_DEPS_FIXTURE = ["curl", "git", "unzip", "jq", "poppler", "ffmpeg"];
|
|
113
|
+
const DARWIN_BASE_DEPS_FIXTURE = ["curl", "git", "unzip", "jq", "poppler", "ffmpeg", "tesseract", "ocrmypdf"];
|
|
114
114
|
// `neo4j` and `cloudflared` are passed to installAllBrewPackages from
|
|
115
115
|
// installNeo4j (index.ts:1265) and installCloudflared (index.ts:1728). Both are
|
|
116
116
|
// concrete brew formula names — no alias hop expected.
|
|
@@ -34,7 +34,7 @@ test("installSystemDeps darwin branch installs DARWIN_BASE_DEPS via brew", () =>
|
|
|
34
34
|
// The shared dep set the darwin path feeds to installAllBrewPackages. If
|
|
35
35
|
// this list shrinks or a name changes, brew-resolve.test.ts (DARWIN_BASE_DEPS_FIXTURE)
|
|
36
36
|
// must move in lockstep — the comment in brew-resolve.test.ts says so.
|
|
37
|
-
assert.match(SRC, /DARWIN_BASE_DEPS\s*=\s*\[\s*"curl"\s*,\s*"git"\s*,\s*"unzip"\s*,\s*"jq"\s*,\s*"poppler"\s*,\s*"ffmpeg"\s*\]/);
|
|
37
|
+
assert.match(SRC, /DARWIN_BASE_DEPS\s*=\s*\[\s*"curl"\s*,\s*"git"\s*,\s*"unzip"\s*,\s*"jq"\s*,\s*"poppler"\s*,\s*"ffmpeg"\s*,\s*"tesseract"\s*,\s*"ocrmypdf"\s*\]/);
|
|
38
38
|
assert.match(SRC, /installAllBrewPackages\(DARWIN_BASE_DEPS,\s*logFile\)/);
|
|
39
39
|
});
|
|
40
40
|
test("installCloudflared darwin branch logs the documented skip and brew-installs cloudflared", () => {
|
package/dist/index.js
CHANGED
|
@@ -649,11 +649,11 @@ function installSystemDeps() {
|
|
|
649
649
|
// out-of-scope note). mDNS is provided by the OS, so avahi-* drop out.
|
|
650
650
|
// Translate the remaining apt names through decideBrewResolution and
|
|
651
651
|
// let installAllBrewPackages handle the install + verify pattern.
|
|
652
|
-
const DARWIN_BASE_DEPS = ["curl", "git", "unzip", "jq", "poppler", "ffmpeg"];
|
|
652
|
+
const DARWIN_BASE_DEPS = ["curl", "git", "unzip", "jq", "poppler", "ffmpeg", "tesseract", "ocrmypdf"];
|
|
653
653
|
installAllBrewPackages(DARWIN_BASE_DEPS, logFile);
|
|
654
654
|
return;
|
|
655
655
|
}
|
|
656
|
-
const BASE_DEPS = ["curl", "git", "unzip", "jq", "avahi-daemon", "avahi-utils", "poppler-utils", "ffmpeg", "bind9-dnsutils"];
|
|
656
|
+
const BASE_DEPS = ["curl", "git", "unzip", "jq", "avahi-daemon", "avahi-utils", "poppler-utils", "ffmpeg", "bind9-dnsutils", "tesseract-ocr", "ocrmypdf"];
|
|
657
657
|
// xterm is the *preferred* terminal-emulator binary for the VNC-rendered
|
|
658
658
|
// Terminal surface
|
|
659
659
|
// delegates window creation to the session's gnome-terminal-server,
|
package/package.json
CHANGED
|
@@ -20,6 +20,41 @@
|
|
|
20
20
|
*/
|
|
21
21
|
/** Wire-format metadata key the Claude Code SDK reads. */
|
|
22
22
|
export declare const ALWAYS_LOAD_META_KEY: "anthropic/alwaysLoad";
|
|
23
|
+
/**
|
|
24
|
+
* Admin-seat detection. The rc-daemon exports `MAXY_SESSION_ROLE=admin` and
|
|
25
|
+
* `MAXY_SPECIALIST=""` for the orchestrator process and its MCP plugin
|
|
26
|
+
* children; specialist spawns set `MAXY_SPECIALIST=<name>`. A plugin process
|
|
27
|
+
* inheriting the env can therefore decide at registration AND invocation
|
|
28
|
+
* time whether it is loaded inside the admin seat.
|
|
29
|
+
*
|
|
30
|
+
* Mirrors `isAdminSeat` in platform/plugins/admin/mcp/src/skill-resolution.ts
|
|
31
|
+
* — this is a duplicate at the library layer so plugins that cannot import
|
|
32
|
+
* from the admin plugin (a sibling, not a dependency) share the same rule.
|
|
33
|
+
*/
|
|
34
|
+
export declare function isAdminSeat(env?: NodeJS.ProcessEnv): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* MCP refusal envelope returned when the admin seat invokes a specialist-
|
|
37
|
+
* owned tool. The text names the dispatch route so the model's next action
|
|
38
|
+
* is the `Agent` dispatch rather than a dead end.
|
|
39
|
+
*/
|
|
40
|
+
export declare function adminBlockedResponse(toolName: string, owner: string): {
|
|
41
|
+
content: {
|
|
42
|
+
type: "text";
|
|
43
|
+
text: string;
|
|
44
|
+
}[];
|
|
45
|
+
isError: true;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Wrap a tool handler so any admin-seat invocation short-circuits with the
|
|
49
|
+
* refusal envelope above and emits one `[admin-tool-gate]` line to stderr.
|
|
50
|
+
*
|
|
51
|
+
* Specialist seats pass through unchanged: this is the primitive Task 529
|
|
52
|
+
* defines for closing the in-message authoring path that Task 516 could
|
|
53
|
+
* not reach. The handler signature is preserved so existing call sites
|
|
54
|
+
* substitute `eagerTool(... handler)` for `eagerTool(... withAdminGate(name,
|
|
55
|
+
* owner, handler))` mechanically.
|
|
56
|
+
*/
|
|
57
|
+
export declare function withAdminGate<H extends (...args: any[]) => any>(toolName: string, owner: string, handler: H): H;
|
|
23
58
|
/**
|
|
24
59
|
* Loose structural type for any object exposing a `registerTool` method. The
|
|
25
60
|
* MCP SDK ships dual ESM/CJS packages with conditional exports, so importing
|
|
@@ -57,5 +92,25 @@ type LocalZodRawShape = Record<string, unknown>;
|
|
|
57
92
|
* the consumer's own zod version) infers the handler args.
|
|
58
93
|
*/
|
|
59
94
|
export declare function eagerTool<Args extends LocalZodRawShape>(server: EagerCapableServer, name: string, description: string, inputSchema: Args, handler: (...args: any[]) => any): void;
|
|
95
|
+
/**
|
|
96
|
+
* Loose structural type for the legacy four-arg `server.tool(name, desc,
|
|
97
|
+
* schema, handler)` call shape — used when the admin seat must register a
|
|
98
|
+
* specialist-owned tool as deferred (not eager) so it does not appear in
|
|
99
|
+
* the admin's eager tool list.
|
|
100
|
+
*/
|
|
101
|
+
type DeferredCapableServer = {
|
|
102
|
+
tool: (name: string, description: string, inputSchema: any, handler: (...args: any[]) => any) => any;
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
* Register a tool that is eager for specialists but deferred + admin-gated
|
|
106
|
+
* for the admin seat. The owning specialist name is named here so the
|
|
107
|
+
* refusal envelope (and the `[admin-tool-gate]` log line) carries it.
|
|
108
|
+
*
|
|
109
|
+
* Use for every tool whose owner is a specialist (database-operator, writer-
|
|
110
|
+
* craft, research-assistant, personal-assistant, content-producer). The
|
|
111
|
+
* admin seat sees the tool only via ToolSearch — and any invocation from the
|
|
112
|
+
* admin seat refuses with the dispatch hint.
|
|
113
|
+
*/
|
|
114
|
+
export declare function eagerToolGated<Args extends LocalZodRawShape>(server: EagerCapableServer & DeferredCapableServer, name: string, owner: string, description: string, inputSchema: Args, handler: (...args: any[]) => any): void;
|
|
60
115
|
export {};
|
|
61
116
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,0DAA0D;AAC1D,eAAO,MAAM,oBAAoB,EAAG,sBAA+B,CAAC;AAEpE;;;;;;;GAOG;AAEH,KAAK,kBAAkB,GAAG;IAAE,YAAY,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,CAAA;CAAE,CAAC;AAEpE;;;;;;;;;;GAUG;AACH,KAAK,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEhD;;;;;;;;;;;;GAYG;AACH,wBAAgB,SAAS,CAAC,IAAI,SAAS,gBAAgB,EACrD,MAAM,EAAE,kBAAkB,EAC1B,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,IAAI,EAEjB,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,GAC/B,IAAI,CAUN"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,0DAA0D;AAC1D,eAAO,MAAM,oBAAoB,EAAG,sBAA+B,CAAC;AAEpE;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAEzE;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG;IACrE,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC1C,OAAO,EAAE,IAAI,CAAC;CACf,CAYA;AAED;;;;;;;;;GASG;AAEH,wBAAgB,aAAa,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,EAC7D,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,CAAC,GACT,CAAC,CAYH;AAED;;;;;;;GAOG;AAEH,KAAK,kBAAkB,GAAG;IAAE,YAAY,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,CAAA;CAAE,CAAC;AAEpE;;;;;;;;;;GAUG;AACH,KAAK,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEhD;;;;;;;;;;;;GAYG;AACH,wBAAgB,SAAS,CAAC,IAAI,SAAS,gBAAgB,EACrD,MAAM,EAAE,kBAAkB,EAC1B,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,IAAI,EAEjB,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,GAC/B,IAAI,CAUN;AAED;;;;;GAKG;AAEH,KAAK,qBAAqB,GAAG;IAAE,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,KAAK,GAAG,CAAA;CAAE,CAAC;AAEtI;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,IAAI,SAAS,gBAAgB,EAC1D,MAAM,EAAE,kBAAkB,GAAG,qBAAqB,EAClD,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,IAAI,EAEjB,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,GAC/B,IAAI,CAMN"}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ALWAYS_LOAD_META_KEY = void 0;
|
|
4
|
+
exports.isAdminSeat = isAdminSeat;
|
|
5
|
+
exports.adminBlockedResponse = adminBlockedResponse;
|
|
6
|
+
exports.withAdminGate = withAdminGate;
|
|
4
7
|
exports.eagerTool = eagerTool;
|
|
8
|
+
exports.eagerToolGated = eagerToolGated;
|
|
5
9
|
/**
|
|
6
10
|
* MCP eager-load helper.
|
|
7
11
|
*
|
|
@@ -24,6 +28,59 @@ exports.eagerTool = eagerTool;
|
|
|
24
28
|
*/
|
|
25
29
|
/** Wire-format metadata key the Claude Code SDK reads. */
|
|
26
30
|
exports.ALWAYS_LOAD_META_KEY = "anthropic/alwaysLoad";
|
|
31
|
+
/**
|
|
32
|
+
* Admin-seat detection. The rc-daemon exports `MAXY_SESSION_ROLE=admin` and
|
|
33
|
+
* `MAXY_SPECIALIST=""` for the orchestrator process and its MCP plugin
|
|
34
|
+
* children; specialist spawns set `MAXY_SPECIALIST=<name>`. A plugin process
|
|
35
|
+
* inheriting the env can therefore decide at registration AND invocation
|
|
36
|
+
* time whether it is loaded inside the admin seat.
|
|
37
|
+
*
|
|
38
|
+
* Mirrors `isAdminSeat` in platform/plugins/admin/mcp/src/skill-resolution.ts
|
|
39
|
+
* — this is a duplicate at the library layer so plugins that cannot import
|
|
40
|
+
* from the admin plugin (a sibling, not a dependency) share the same rule.
|
|
41
|
+
*/
|
|
42
|
+
function isAdminSeat(env = process.env) {
|
|
43
|
+
return env.MAXY_SESSION_ROLE === "admin" && !(env.MAXY_SPECIALIST && env.MAXY_SPECIALIST.length > 0);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* MCP refusal envelope returned when the admin seat invokes a specialist-
|
|
47
|
+
* owned tool. The text names the dispatch route so the model's next action
|
|
48
|
+
* is the `Agent` dispatch rather than a dead end.
|
|
49
|
+
*/
|
|
50
|
+
function adminBlockedResponse(toolName, owner) {
|
|
51
|
+
return {
|
|
52
|
+
content: [
|
|
53
|
+
{
|
|
54
|
+
type: "text",
|
|
55
|
+
text: `Tool \`${toolName}\` is owned by the \`${owner}\` specialist and is not callable from the admin seat. ` +
|
|
56
|
+
`Dispatch \`${owner}\` with the Agent tool and pass the work as the brief.`,
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
isError: true,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Wrap a tool handler so any admin-seat invocation short-circuits with the
|
|
64
|
+
* refusal envelope above and emits one `[admin-tool-gate]` line to stderr.
|
|
65
|
+
*
|
|
66
|
+
* Specialist seats pass through unchanged: this is the primitive Task 529
|
|
67
|
+
* defines for closing the in-message authoring path that Task 516 could
|
|
68
|
+
* not reach. The handler signature is preserved so existing call sites
|
|
69
|
+
* substitute `eagerTool(... handler)` for `eagerTool(... withAdminGate(name,
|
|
70
|
+
* owner, handler))` mechanically.
|
|
71
|
+
*/
|
|
72
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
73
|
+
function withAdminGate(toolName, owner, handler) {
|
|
74
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
75
|
+
const wrapped = ((...args) => {
|
|
76
|
+
if (isAdminSeat(process.env)) {
|
|
77
|
+
process.stderr.write(`[admin-tool-gate] role=admin tool=${toolName} decision=block reason=specialist-owned owner=${owner}\n`);
|
|
78
|
+
return adminBlockedResponse(toolName, owner);
|
|
79
|
+
}
|
|
80
|
+
return handler(...args);
|
|
81
|
+
});
|
|
82
|
+
return wrapped;
|
|
83
|
+
}
|
|
27
84
|
/**
|
|
28
85
|
* Register an MCP tool that the Claude Code SDK eager-loads into the
|
|
29
86
|
* model's prompt at session boot, bypassing the ToolSearch round-trip.
|
|
@@ -46,4 +103,23 @@ handler) {
|
|
|
46
103
|
_meta: { [exports.ALWAYS_LOAD_META_KEY]: true },
|
|
47
104
|
}, handler);
|
|
48
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* Register a tool that is eager for specialists but deferred + admin-gated
|
|
108
|
+
* for the admin seat. The owning specialist name is named here so the
|
|
109
|
+
* refusal envelope (and the `[admin-tool-gate]` log line) carries it.
|
|
110
|
+
*
|
|
111
|
+
* Use for every tool whose owner is a specialist (database-operator, writer-
|
|
112
|
+
* craft, research-assistant, personal-assistant, content-producer). The
|
|
113
|
+
* admin seat sees the tool only via ToolSearch — and any invocation from the
|
|
114
|
+
* admin seat refuses with the dispatch hint.
|
|
115
|
+
*/
|
|
116
|
+
function eagerToolGated(server, name, owner, description, inputSchema,
|
|
117
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
118
|
+
handler) {
|
|
119
|
+
if (isAdminSeat(process.env)) {
|
|
120
|
+
server.tool(name, description, inputSchema, withAdminGate(name, owner, handler));
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
eagerTool(server, name, description, inputSchema, handler);
|
|
124
|
+
}
|
|
49
125
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAkCA,kCAEC;AAOD,oDAeC;AAaD,sCAgBC;AAuCD,8BAiBC;AAqBD,wCAcC;AAlLD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,0DAA0D;AAC7C,QAAA,oBAAoB,GAAG,sBAA+B,CAAC;AAEpE;;;;;;;;;;GAUG;AACH,SAAgB,WAAW,CAAC,MAAyB,OAAO,CAAC,GAAG;IAC9D,OAAO,GAAG,CAAC,iBAAiB,KAAK,OAAO,IAAI,CAAC,CAAC,GAAG,CAAC,eAAe,IAAI,GAAG,CAAC,eAAe,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AACvG,CAAC;AAED;;;;GAIG;AACH,SAAgB,oBAAoB,CAAC,QAAgB,EAAE,KAAa;IAIlE,OAAO;QACL,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,MAAM;gBACZ,IAAI,EACF,UAAU,QAAQ,wBAAwB,KAAK,yDAAyD;oBACxG,cAAc,KAAK,wDAAwD;aAC9E;SACF;QACD,OAAO,EAAE,IAAI;KACd,CAAC;AACJ,CAAC;AAED;;;;;;;;;GASG;AACH,8DAA8D;AAC9D,SAAgB,aAAa,CAC3B,QAAgB,EAChB,KAAa,EACb,OAAU;IAEV,8DAA8D;IAC9D,MAAM,OAAO,GAAG,CAAC,CAAC,GAAG,IAAW,EAAE,EAAE;QAClC,IAAI,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YAC7B,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,qCAAqC,QAAQ,iDAAiD,KAAK,IAAI,CACxG,CAAC;YACF,OAAO,oBAAoB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QAC/C,CAAC;QACD,OAAO,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;IAC1B,CAAC,CAAiB,CAAC;IACnB,OAAO,OAAO,CAAC;AACjB,CAAC;AA0BD;;;;;;;;;;;;GAYG;AACH,SAAgB,SAAS,CACvB,MAA0B,EAC1B,IAAY,EACZ,WAAmB,EACnB,WAAiB;AACjB,8DAA8D;AAC9D,OAAgC;IAEhC,MAAM,CAAC,YAAY,CACjB,IAAI,EACJ;QACE,WAAW;QACX,WAAW;QACX,KAAK,EAAE,EAAE,CAAC,4BAAoB,CAAC,EAAE,IAAI,EAAE;KACxC,EACD,OAAO,CACR,CAAC;AACJ,CAAC;AAWD;;;;;;;;;GASG;AACH,SAAgB,cAAc,CAC5B,MAAkD,EAClD,IAAY,EACZ,KAAa,EACb,WAAmB,EACnB,WAAiB;AACjB,8DAA8D;AAC9D,OAAgC;IAEhC,IAAI,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QAC7B,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,CAAC,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;QACjF,OAAO;IACT,CAAC;IACD,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;AAC7D,CAAC"}
|
|
@@ -21,6 +21,72 @@
|
|
|
21
21
|
/** Wire-format metadata key the Claude Code SDK reads. */
|
|
22
22
|
export const ALWAYS_LOAD_META_KEY = "anthropic/alwaysLoad" as const;
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Admin-seat detection. The rc-daemon exports `MAXY_SESSION_ROLE=admin` and
|
|
26
|
+
* `MAXY_SPECIALIST=""` for the orchestrator process and its MCP plugin
|
|
27
|
+
* children; specialist spawns set `MAXY_SPECIALIST=<name>`. A plugin process
|
|
28
|
+
* inheriting the env can therefore decide at registration AND invocation
|
|
29
|
+
* time whether it is loaded inside the admin seat.
|
|
30
|
+
*
|
|
31
|
+
* Mirrors `isAdminSeat` in platform/plugins/admin/mcp/src/skill-resolution.ts
|
|
32
|
+
* — this is a duplicate at the library layer so plugins that cannot import
|
|
33
|
+
* from the admin plugin (a sibling, not a dependency) share the same rule.
|
|
34
|
+
*/
|
|
35
|
+
export function isAdminSeat(env: NodeJS.ProcessEnv = process.env): boolean {
|
|
36
|
+
return env.MAXY_SESSION_ROLE === "admin" && !(env.MAXY_SPECIALIST && env.MAXY_SPECIALIST.length > 0);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* MCP refusal envelope returned when the admin seat invokes a specialist-
|
|
41
|
+
* owned tool. The text names the dispatch route so the model's next action
|
|
42
|
+
* is the `Agent` dispatch rather than a dead end.
|
|
43
|
+
*/
|
|
44
|
+
export function adminBlockedResponse(toolName: string, owner: string): {
|
|
45
|
+
content: { type: "text"; text: string }[];
|
|
46
|
+
isError: true;
|
|
47
|
+
} {
|
|
48
|
+
return {
|
|
49
|
+
content: [
|
|
50
|
+
{
|
|
51
|
+
type: "text",
|
|
52
|
+
text:
|
|
53
|
+
`Tool \`${toolName}\` is owned by the \`${owner}\` specialist and is not callable from the admin seat. ` +
|
|
54
|
+
`Dispatch \`${owner}\` with the Agent tool and pass the work as the brief.`,
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
isError: true,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Wrap a tool handler so any admin-seat invocation short-circuits with the
|
|
63
|
+
* refusal envelope above and emits one `[admin-tool-gate]` line to stderr.
|
|
64
|
+
*
|
|
65
|
+
* Specialist seats pass through unchanged: this is the primitive Task 529
|
|
66
|
+
* defines for closing the in-message authoring path that Task 516 could
|
|
67
|
+
* not reach. The handler signature is preserved so existing call sites
|
|
68
|
+
* substitute `eagerTool(... handler)` for `eagerTool(... withAdminGate(name,
|
|
69
|
+
* owner, handler))` mechanically.
|
|
70
|
+
*/
|
|
71
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
72
|
+
export function withAdminGate<H extends (...args: any[]) => any>(
|
|
73
|
+
toolName: string,
|
|
74
|
+
owner: string,
|
|
75
|
+
handler: H,
|
|
76
|
+
): H {
|
|
77
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
78
|
+
const wrapped = ((...args: any[]) => {
|
|
79
|
+
if (isAdminSeat(process.env)) {
|
|
80
|
+
process.stderr.write(
|
|
81
|
+
`[admin-tool-gate] role=admin tool=${toolName} decision=block reason=specialist-owned owner=${owner}\n`,
|
|
82
|
+
);
|
|
83
|
+
return adminBlockedResponse(toolName, owner);
|
|
84
|
+
}
|
|
85
|
+
return handler(...args);
|
|
86
|
+
}) as unknown as H;
|
|
87
|
+
return wrapped;
|
|
88
|
+
}
|
|
89
|
+
|
|
24
90
|
/**
|
|
25
91
|
* Loose structural type for any object exposing a `registerTool` method. The
|
|
26
92
|
* MCP SDK ships dual ESM/CJS packages with conditional exports, so importing
|
|
@@ -76,3 +142,38 @@ export function eagerTool<Args extends LocalZodRawShape>(
|
|
|
76
142
|
handler,
|
|
77
143
|
);
|
|
78
144
|
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Loose structural type for the legacy four-arg `server.tool(name, desc,
|
|
148
|
+
* schema, handler)` call shape — used when the admin seat must register a
|
|
149
|
+
* specialist-owned tool as deferred (not eager) so it does not appear in
|
|
150
|
+
* the admin's eager tool list.
|
|
151
|
+
*/
|
|
152
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
153
|
+
type DeferredCapableServer = { tool: (name: string, description: string, inputSchema: any, handler: (...args: any[]) => any) => any };
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Register a tool that is eager for specialists but deferred + admin-gated
|
|
157
|
+
* for the admin seat. The owning specialist name is named here so the
|
|
158
|
+
* refusal envelope (and the `[admin-tool-gate]` log line) carries it.
|
|
159
|
+
*
|
|
160
|
+
* Use for every tool whose owner is a specialist (database-operator, writer-
|
|
161
|
+
* craft, research-assistant, personal-assistant, content-producer). The
|
|
162
|
+
* admin seat sees the tool only via ToolSearch — and any invocation from the
|
|
163
|
+
* admin seat refuses with the dispatch hint.
|
|
164
|
+
*/
|
|
165
|
+
export function eagerToolGated<Args extends LocalZodRawShape>(
|
|
166
|
+
server: EagerCapableServer & DeferredCapableServer,
|
|
167
|
+
name: string,
|
|
168
|
+
owner: string,
|
|
169
|
+
description: string,
|
|
170
|
+
inputSchema: Args,
|
|
171
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
172
|
+
handler: (...args: any[]) => any,
|
|
173
|
+
): void {
|
|
174
|
+
if (isAdminSeat(process.env)) {
|
|
175
|
+
server.tool(name, description, inputSchema, withAdminGate(name, owner, handler));
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
eagerTool(server, name, description, inputSchema, handler);
|
|
179
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Task 529 — admin-tool-gate enforces orchestration-only at the PreToolUse
|
|
3
|
+
# hook. Admin-direct invocations of Write/Edit/MultiEdit/Bash/WebFetch/
|
|
4
|
+
# WebSearch are refused with the dispatch hint; in-window Task subagents
|
|
5
|
+
# (transcript_path matching `*/subagents/agent-*.jsonl`) pass through
|
|
6
|
+
# (Task 222 propagation regression class — subagents must keep their full
|
|
7
|
+
# tool surface). Missing transcript_path is fail-closed (matches Task 222
|
|
8
|
+
# contract).
|
|
9
|
+
|
|
10
|
+
set -u
|
|
11
|
+
|
|
12
|
+
HOOK="$(cd "$(dirname "$0")/.." && pwd)/pre-tool-use.sh"
|
|
13
|
+
if [[ ! -x "$HOOK" ]]; then
|
|
14
|
+
echo "FAIL: $HOOK not executable" >&2
|
|
15
|
+
exit 1
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
TMPFILES=()
|
|
19
|
+
cleanup() {
|
|
20
|
+
for f in "${TMPFILES[@]:-}"; do
|
|
21
|
+
[[ -n "$f" ]] && rm -f "$f" 2>/dev/null || true
|
|
22
|
+
done
|
|
23
|
+
}
|
|
24
|
+
trap cleanup EXIT
|
|
25
|
+
|
|
26
|
+
PASS=0; FAIL=0
|
|
27
|
+
pass() { PASS=$((PASS+1)); echo "PASS: $1"; }
|
|
28
|
+
fail() { FAIL=$((FAIL+1)); echo "FAIL: $1" >&2; }
|
|
29
|
+
|
|
30
|
+
ADMIN_TRANSCRIPT="/tmp/sess/admin-3104d4b4.jsonl"
|
|
31
|
+
SUBAGENT_TRANSCRIPT="/tmp/sess/subagents/agent-content-producer-1.jsonl"
|
|
32
|
+
|
|
33
|
+
run() {
|
|
34
|
+
local tool="$1" transcript="$2" expected_rc="$3" expected_pattern="$4" label="$5"
|
|
35
|
+
local input_json
|
|
36
|
+
if [[ -n "$transcript" ]]; then
|
|
37
|
+
input_json=$(python3 -c '
|
|
38
|
+
import json, sys
|
|
39
|
+
print(json.dumps({"hook_event_name":"PreToolUse","tool_name":sys.argv[1],"tool_input":{"file_path":"/tmp/x","content":"x"},"transcript_path":sys.argv[2]}, separators=(",", ":")))
|
|
40
|
+
' "$tool" "$transcript")
|
|
41
|
+
else
|
|
42
|
+
input_json=$(python3 -c '
|
|
43
|
+
import json, sys
|
|
44
|
+
print(json.dumps({"hook_event_name":"PreToolUse","tool_name":sys.argv[1],"tool_input":{"file_path":"/tmp/x","content":"x"}}, separators=(",", ":")))
|
|
45
|
+
' "$tool")
|
|
46
|
+
fi
|
|
47
|
+
local stdout_file; stdout_file=$(mktemp); TMPFILES+=("$stdout_file")
|
|
48
|
+
local stderr_file; stderr_file=$(mktemp); TMPFILES+=("$stderr_file")
|
|
49
|
+
printf '%s' "$input_json" | bash "$HOOK" admin >"$stdout_file" 2>"$stderr_file"
|
|
50
|
+
local rc=$?
|
|
51
|
+
if [[ "$rc" -ne "$expected_rc" ]]; then
|
|
52
|
+
fail "$label: expected exit $expected_rc, got $rc. Stderr: $(cat "$stderr_file")"
|
|
53
|
+
return
|
|
54
|
+
fi
|
|
55
|
+
if [[ -n "$expected_pattern" ]] && ! grep -qE "$expected_pattern" "$stderr_file"; then
|
|
56
|
+
fail "$label: stderr missing pattern '$expected_pattern'. Got: $(cat "$stderr_file")"
|
|
57
|
+
return
|
|
58
|
+
fi
|
|
59
|
+
pass "$label"
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
# ── Admin-direct refusals ───────────────────────────────────────────────
|
|
63
|
+
run Write "$ADMIN_TRANSCRIPT" 2 '\[admin-tool-gate\] role=admin tool=Write decision=block.*owner=content-producer' \
|
|
64
|
+
"Test 1: admin-direct Write rejected with content-producer dispatch hint"
|
|
65
|
+
run Edit "$ADMIN_TRANSCRIPT" 2 '\[admin-tool-gate\] role=admin tool=Edit decision=block.*owner=content-producer' \
|
|
66
|
+
"Test 2: admin-direct Edit rejected"
|
|
67
|
+
run MultiEdit "$ADMIN_TRANSCRIPT" 2 '\[admin-tool-gate\] role=admin tool=MultiEdit decision=block.*owner=content-producer' \
|
|
68
|
+
"Test 3: admin-direct MultiEdit rejected"
|
|
69
|
+
run Bash "$ADMIN_TRANSCRIPT" 2 '\[admin-tool-gate\] role=admin tool=Bash decision=block' \
|
|
70
|
+
"Test 4: admin-direct Bash rejected"
|
|
71
|
+
run WebFetch "$ADMIN_TRANSCRIPT" 2 '\[admin-tool-gate\] role=admin tool=WebFetch decision=block.*owner=research-assistant' \
|
|
72
|
+
"Test 5: admin-direct WebFetch rejected"
|
|
73
|
+
run WebSearch "$ADMIN_TRANSCRIPT" 2 '\[admin-tool-gate\] role=admin tool=WebSearch decision=block.*owner=research-assistant' \
|
|
74
|
+
"Test 6: admin-direct WebSearch rejected"
|
|
75
|
+
|
|
76
|
+
# ── In-window subagent passthrough (Task 222 regression class) ──────────
|
|
77
|
+
run Write "$SUBAGENT_TRANSCRIPT" 0 '\[admin-tool-gate\] role=subagent tool=Write decision=allow' \
|
|
78
|
+
"Test 7: subagent Write passes through"
|
|
79
|
+
run Edit "$SUBAGENT_TRANSCRIPT" 0 '\[admin-tool-gate\] role=subagent tool=Edit decision=allow' \
|
|
80
|
+
"Test 8: subagent Edit passes through"
|
|
81
|
+
run Bash "$SUBAGENT_TRANSCRIPT" 0 '\[admin-tool-gate\] role=subagent tool=Bash decision=allow' \
|
|
82
|
+
"Test 9: subagent Bash passes through"
|
|
83
|
+
run WebFetch "$SUBAGENT_TRANSCRIPT" 0 '\[admin-tool-gate\] role=subagent tool=WebFetch decision=allow' \
|
|
84
|
+
"Test 10: subagent WebFetch passes through"
|
|
85
|
+
|
|
86
|
+
# ── Missing transcript_path is fail-closed (Task 222 contract) ──────────
|
|
87
|
+
run Write "" 2 '\[admin-tool-gate\] role=unknown tool=Write decision=block reason=missing-transcript-path' \
|
|
88
|
+
"Test 11: missing transcript_path on Write fails closed"
|
|
89
|
+
run Bash "" 2 '\[admin-tool-gate\] role=unknown tool=Bash decision=block reason=missing-transcript-path' \
|
|
90
|
+
"Test 12: missing transcript_path on Bash fails closed"
|
|
91
|
+
|
|
92
|
+
# ── Unrelated tools untouched ───────────────────────────────────────────
|
|
93
|
+
run Read "$ADMIN_TRANSCRIPT" 0 '' \
|
|
94
|
+
"Test 13: admin-direct Read passes (Read is admin-keep-list)"
|
|
95
|
+
|
|
96
|
+
echo
|
|
97
|
+
echo "──────── pre-tool-use admin-tool-gate test summary ────────"
|
|
98
|
+
echo "PASS: $PASS"
|
|
99
|
+
echo "FAIL: $FAIL"
|
|
100
|
+
|
|
101
|
+
[[ "$FAIL" -gt 0 ]] && exit 1
|
|
102
|
+
exit 0
|
|
@@ -41,7 +41,7 @@ run_bash() {
|
|
|
41
41
|
# Build the input JSON via python3 so command_text with quotes / specials is safe.
|
|
42
42
|
input_json=$(python3 -c '
|
|
43
43
|
import json, sys
|
|
44
|
-
print(json.dumps({"hook_event_name": "PreToolUse", "tool_name": "Bash", "tool_input": {"command": sys.argv[1]}}, separators=(",", ":")))
|
|
44
|
+
print(json.dumps({"hook_event_name": "PreToolUse", "tool_name": "Bash", "tool_input": {"command": sys.argv[1]}, "transcript_path": "/tmp/sess/subagents/agent-x.jsonl"}, separators=(",", ":")))
|
|
45
45
|
' "$command_text")
|
|
46
46
|
local stdout_file; stdout_file=$(mktemp); TMPFILES+=("$stdout_file")
|
|
47
47
|
local stderr_file; stderr_file=$(mktemp); TMPFILES+=("$stderr_file")
|
|
@@ -64,7 +64,7 @@ run_write() {
|
|
|
64
64
|
local input_json
|
|
65
65
|
input_json=$(python3 -c '
|
|
66
66
|
import json, sys
|
|
67
|
-
print(json.dumps({"hook_event_name": "PreToolUse", "tool_name": "Write", "tool_input": {"file_path": "/tmp/test.html", "content": sys.argv[1]}}, separators=(",", ":")))
|
|
67
|
+
print(json.dumps({"hook_event_name": "PreToolUse", "tool_name": "Write", "tool_input": {"file_path": "/tmp/test.html", "content": sys.argv[1]}, "transcript_path": "/tmp/sess/subagents/agent-x.jsonl"}, separators=(",", ":")))
|
|
68
68
|
' "$content")
|
|
69
69
|
local stdout_file; stdout_file=$(mktemp); TMPFILES+=("$stdout_file")
|
|
70
70
|
local stderr_file; stderr_file=$(mktemp); TMPFILES+=("$stderr_file")
|
|
@@ -87,7 +87,7 @@ run_edit() {
|
|
|
87
87
|
local input_json
|
|
88
88
|
input_json=$(python3 -c '
|
|
89
89
|
import json, sys
|
|
90
|
-
print(json.dumps({"hook_event_name": "PreToolUse", "tool_name": "Edit", "tool_input": {"file_path": "/tmp/test.html", "old_string": "OLD", "new_string": sys.argv[1]}}, separators=(",", ":")))
|
|
90
|
+
print(json.dumps({"hook_event_name": "PreToolUse", "tool_name": "Edit", "tool_input": {"file_path": "/tmp/test.html", "old_string": "OLD", "new_string": sys.argv[1]}, "transcript_path": "/tmp/sess/subagents/agent-x.jsonl"}, separators=(",", ":")))
|
|
91
91
|
' "$new_string")
|
|
92
92
|
local stdout_file; stdout_file=$(mktemp); TMPFILES+=("$stdout_file")
|
|
93
93
|
local stderr_file; stderr_file=$(mktemp); TMPFILES+=("$stderr_file")
|
|
@@ -185,7 +185,7 @@ else
|
|
|
185
185
|
fi
|
|
186
186
|
|
|
187
187
|
# Pre-existing guards still active — entitlement file edit still rejected.
|
|
188
|
-
ENT_JSON=$(python3 -c 'import json; print(json.dumps({"hook_event_name":"PreToolUse","tool_name":"Write","tool_input":{"file_path":"/srv/entitlement.json","content":"{\"tier\":\"max\"}"}}, separators=(",", ":")))')
|
|
188
|
+
ENT_JSON=$(python3 -c 'import json; print(json.dumps({"hook_event_name":"PreToolUse","tool_name":"Write","tool_input":{"file_path":"/srv/entitlement.json","content":"{\"tier\":\"max\"}"},"transcript_path":"/tmp/sess/subagents/agent-x.jsonl"}, separators=(",", ":")))')
|
|
189
189
|
STDOUT_FILE=$(mktemp); STDERR_FILE=$(mktemp); TMPFILES+=("$STDOUT_FILE" "$STDERR_FILE")
|
|
190
190
|
printf '%s' "$ENT_JSON" | bash "$HOOK" admin >"$STDOUT_FILE" 2>"$STDERR_FILE"
|
|
191
191
|
RC=$?
|
|
@@ -21,6 +21,62 @@ TOOL_NAME=$(echo "$INPUT" | grep -o '"tool_name":"[^"]*"' | head -1 | cut -d'"'
|
|
|
21
21
|
# ---------------------------------------------------------------------------
|
|
22
22
|
if [ "$AGENT_TYPE" = "admin" ]; then
|
|
23
23
|
|
|
24
|
+
# ── Orchestration-only authoring + research block (Task 529) ─────────────
|
|
25
|
+
# The admin seat is orchestration + clarification + delivery only.
|
|
26
|
+
# Write/Edit/MultiEdit/Bash/WebFetch/WebSearch are specialist-owned —
|
|
27
|
+
# content-producer for authoring, research-assistant for research, the
|
|
28
|
+
# shell tools for whichever specialist owns the workflow that needs them.
|
|
29
|
+
# In-window Task subagents must keep these tools (Task 222 regression
|
|
30
|
+
# class) — the discriminator is the transcript_path: Claude Code stores
|
|
31
|
+
# subagent turns in separate `*/subagents/agent-*.jsonl` files.
|
|
32
|
+
#
|
|
33
|
+
# Admin direct → exit 2 with dispatch hint.
|
|
34
|
+
# Subagent → exit 0 (passthrough).
|
|
35
|
+
# Missing/malformed transcript_path → fail-closed block (matches Task 222).
|
|
36
|
+
case "$TOOL_NAME" in
|
|
37
|
+
Write|Edit|MultiEdit|Bash|WebFetch|WebSearch)
|
|
38
|
+
TRANSCRIPT_PATH=$(echo "$INPUT" | python3 -c '
|
|
39
|
+
import sys, json
|
|
40
|
+
try:
|
|
41
|
+
print(json.load(sys.stdin).get("transcript_path", "") or "")
|
|
42
|
+
except Exception:
|
|
43
|
+
print("")
|
|
44
|
+
' 2>/dev/null)
|
|
45
|
+
case "$TRANSCRIPT_PATH" in
|
|
46
|
+
*/subagents/agent-*.jsonl)
|
|
47
|
+
# In-window subagent — passthrough. Tool name resolves on the
|
|
48
|
+
# subagent's own frontmatter `tools:` allowlist downstream.
|
|
49
|
+
echo "[admin-tool-gate] role=subagent tool=${TOOL_NAME} decision=allow source=hook-passthrough" >&2
|
|
50
|
+
;;
|
|
51
|
+
"")
|
|
52
|
+
# Missing transcript_path on a denied native tool — fail closed
|
|
53
|
+
# rather than guess (Task 222 fail-closed contract).
|
|
54
|
+
echo "[admin-tool-gate] role=unknown tool=${TOOL_NAME} decision=block reason=missing-transcript-path" >&2
|
|
55
|
+
echo "Blocked: ${TOOL_NAME} is unavailable from the admin seat and the caller cannot be classified (no transcript_path). Failing closed." >&2
|
|
56
|
+
exit 2
|
|
57
|
+
;;
|
|
58
|
+
*)
|
|
59
|
+
# Admin direct invocation — refuse with dispatch hint.
|
|
60
|
+
case "$TOOL_NAME" in
|
|
61
|
+
Write|Edit|MultiEdit)
|
|
62
|
+
OWNER="content-producer"
|
|
63
|
+
;;
|
|
64
|
+
Bash)
|
|
65
|
+
OWNER="the specialist that owns the workflow needing shell access"
|
|
66
|
+
;;
|
|
67
|
+
WebFetch|WebSearch)
|
|
68
|
+
OWNER="research-assistant"
|
|
69
|
+
;;
|
|
70
|
+
esac
|
|
71
|
+
echo "[admin-tool-gate] role=admin tool=${TOOL_NAME} decision=block reason=orchestration-only owner=${OWNER}" >&2
|
|
72
|
+
echo "Blocked: \`${TOOL_NAME}\` is not callable from the admin seat. The admin seat is orchestration + clarification + delivery only — authoring, shell work, and web research are owned by specialists." >&2
|
|
73
|
+
echo "Dispatch the work to ${OWNER} via the Agent tool." >&2
|
|
74
|
+
exit 2
|
|
75
|
+
;;
|
|
76
|
+
esac
|
|
77
|
+
;;
|
|
78
|
+
esac
|
|
79
|
+
|
|
24
80
|
# ── Code directory protection ────────────────────────────────────────────
|
|
25
81
|
case "$TOOL_NAME" in
|
|
26
82
|
Write|Edit)
|