@rubytech/create-maxy-code 0.1.216 → 0.1.221

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.
Files changed (82) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/lib/mcp-eager/dist/index.d.ts +0 -55
  3. package/payload/platform/lib/mcp-eager/dist/index.d.ts.map +1 -1
  4. package/payload/platform/lib/mcp-eager/dist/index.js +0 -76
  5. package/payload/platform/lib/mcp-eager/dist/index.js.map +1 -1
  6. package/payload/platform/lib/mcp-eager/src/index.ts +0 -101
  7. package/payload/platform/plugins/admin/PLUGIN.md +2 -3
  8. package/payload/platform/plugins/admin/hooks/__tests__/hook-emit-stale-lock-ttl.test.sh +102 -0
  9. package/payload/platform/plugins/admin/hooks/archive-ingest-surface-gate.sh +1 -2
  10. package/payload/platform/plugins/admin/hooks/lib/hook-emit.sh +32 -3
  11. package/payload/platform/plugins/admin/hooks/post-tool-use-agent.sh +20 -1
  12. package/payload/platform/plugins/admin/mcp/dist/index.js +2 -2
  13. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  14. package/payload/platform/plugins/admin/skills/admin-user-management/SKILL.md +1 -1
  15. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +18 -18
  16. package/payload/platform/plugins/admin/skills/plugin-management/SKILL.md +2 -2
  17. package/payload/platform/plugins/browser/mcp/dist/index.js +19 -19
  18. package/payload/platform/plugins/browser/mcp/dist/index.js.map +1 -1
  19. package/payload/platform/plugins/docs/references/platform.md +10 -17
  20. package/payload/platform/plugins/docs/references/troubleshooting.md +7 -0
  21. package/payload/platform/plugins/email/mcp/dist/index.js +12 -12
  22. package/payload/platform/plugins/email/mcp/dist/index.js.map +1 -1
  23. package/payload/platform/plugins/memory/mcp/dist/index.js +8 -8
  24. package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
  25. package/payload/platform/plugins/memory/references/graph-primitives.md +2 -2
  26. package/payload/platform/plugins/url-get/mcp/dist/index.js +2 -2
  27. package/payload/platform/plugins/url-get/mcp/dist/index.js.map +1 -1
  28. package/payload/platform/scripts/check-no-esm-require.mjs +3 -0
  29. package/payload/platform/scripts/setup-account.sh +0 -15
  30. package/payload/platform/services/claude-session-manager/dist/auth-snapshot.d.ts +4 -0
  31. package/payload/platform/services/claude-session-manager/dist/auth-snapshot.d.ts.map +1 -0
  32. package/payload/platform/services/claude-session-manager/dist/auth-snapshot.js +50 -0
  33. package/payload/platform/services/claude-session-manager/dist/auth-snapshot.js.map +1 -0
  34. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts +0 -14
  35. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  36. package/payload/platform/services/claude-session-manager/dist/http-server.js +44 -71
  37. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  38. package/payload/platform/services/claude-session-manager/dist/index.js +12 -31
  39. package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
  40. package/payload/platform/services/claude-session-manager/dist/jsonl-tail.d.ts +3 -0
  41. package/payload/platform/services/claude-session-manager/dist/jsonl-tail.d.ts.map +1 -1
  42. package/payload/platform/services/claude-session-manager/dist/jsonl-tail.js +55 -1
  43. package/payload/platform/services/claude-session-manager/dist/jsonl-tail.js.map +1 -1
  44. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +1 -1
  45. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
  46. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +27 -20
  47. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
  48. package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts +23 -17
  49. package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts.map +1 -1
  50. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js +94 -73
  51. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -1
  52. package/payload/platform/services/claude-session-manager/dist/rc-life.d.ts +4 -0
  53. package/payload/platform/services/claude-session-manager/dist/rc-life.d.ts.map +1 -0
  54. package/payload/platform/services/claude-session-manager/dist/rc-life.js +16 -0
  55. package/payload/platform/services/claude-session-manager/dist/rc-life.js.map +1 -0
  56. package/payload/platform/services/claude-session-manager/dist/systemd-scope.d.ts +12 -46
  57. package/payload/platform/services/claude-session-manager/dist/systemd-scope.d.ts.map +1 -1
  58. package/payload/platform/services/claude-session-manager/dist/systemd-scope.js +24 -73
  59. package/payload/platform/services/claude-session-manager/dist/systemd-scope.js.map +1 -1
  60. package/payload/platform/templates/agents/admin/IDENTITY.md +3 -3
  61. package/payload/premium-plugins/writer-craft/mcp/dist/index.d.ts.map +1 -1
  62. package/payload/premium-plugins/writer-craft/mcp/dist/index.js +4 -5
  63. package/payload/premium-plugins/writer-craft/mcp/dist/index.js.map +1 -1
  64. package/payload/premium-plugins/writer-craft/mcp/src/index.ts +4 -5
  65. package/payload/server/public/assets/AdminShell-BcxEZnpu.js +1 -0
  66. package/payload/server/public/assets/{admin-DjGGOx2K.js → admin-DMo5DWKH.js} +1 -1
  67. package/payload/server/public/assets/{data-WBEvtRV-.js → data-D6IQdm0R.js} +1 -1
  68. package/payload/server/public/assets/{graph-DsOLsIPf.js → graph-PkZKVkWD.js} +1 -1
  69. package/payload/server/public/data.html +2 -2
  70. package/payload/server/public/graph.html +2 -2
  71. package/payload/server/public/index.html +2 -2
  72. package/payload/server/server.js +49 -12
  73. package/payload/platform/plugins/admin/hooks/__tests__/pre-tool-use-admin-tool-gate.test.sh +0 -169
  74. package/payload/platform/plugins/admin/hooks/__tests__/pre-tool-use-base64-guard.test.sh +0 -204
  75. package/payload/platform/plugins/admin/hooks/__tests__/pre-tool-use-memory-write-passthrough.test.sh +0 -118
  76. package/payload/platform/plugins/admin/hooks/pre-tool-use.sh +0 -409
  77. package/payload/premium-plugins/writer-craft/lib/mcp-eager/dist/index.d.ts +0 -116
  78. package/payload/premium-plugins/writer-craft/lib/mcp-eager/dist/index.d.ts.map +0 -1
  79. package/payload/premium-plugins/writer-craft/lib/mcp-eager/dist/index.js +0 -125
  80. package/payload/premium-plugins/writer-craft/lib/mcp-eager/dist/index.js.map +0 -1
  81. package/payload/premium-plugins/writer-craft/lib/mcp-eager/package.json +0 -7
  82. package/payload/server/public/assets/AdminShell-CPWbJ_I8.js +0 -1
@@ -1,409 +0,0 @@
1
- #!/usr/bin/env bash
2
- # PreToolUse hook — enforces agent tool boundaries and approval gating.
3
- # Usage: pre-tool-use.sh <agent-type>
4
- # Receives tool call JSON on stdin (from Claude Code hook protocol).
5
- # Exit 0 = allow, exit 2 = block.
6
-
7
- set -uo pipefail
8
-
9
- AGENT_TYPE="${1:-public}"
10
-
11
- # Source the propagation emitter (Task 560). The library is fail-open;
12
- # any error path inside it logs to server.log and returns 0, so sourcing
13
- # never blocks the hook's primary allow/block contract.
14
- HOOKS_LIB_DIR="$(cd "$(dirname "$0")" 2>/dev/null && pwd)/lib"
15
- # shellcheck disable=SC1091
16
- [ -f "$HOOKS_LIB_DIR/hook-emit.sh" ] && source "$HOOKS_LIB_DIR/hook-emit.sh"
17
-
18
- # Read stdin — fail closed if unavailable
19
- if [ -t 0 ]; then
20
- # No INPUT yet; propagate with agentId=unknown so the regression is visible.
21
- if declare -F hook_emit_decision >/dev/null 2>&1; then
22
- hook_emit_decision "unknown" "admin-tool-gate" "?" "block" \
23
- "stdin-missing" "Blocked: Cannot determine tool call (no stdin). Failing closed." 2 0
24
- fi
25
- echo "Blocked: Cannot determine tool call (no stdin). Failing closed." >&2
26
- exit 2
27
- fi
28
- INPUT=$(cat)
29
- TOOL_NAME=$(echo "$INPUT" | grep -o '"tool_name":"[^"]*"' | head -1 | cut -d'"' -f4)
30
-
31
- # Session id for propagation. Empty / parse-failure routes to "unknown" —
32
- # the propagator emits the record under agentId=unknown rather than dropping.
33
- SESSION_ID=$(printf '%s' "$INPUT" | python3 -c '
34
- import sys, json
35
- try:
36
- print(json.load(sys.stdin).get("session_id", "") or "unknown")
37
- except Exception:
38
- print("unknown")
39
- ' 2>/dev/null || echo "unknown")
40
-
41
- # ---------------------------------------------------------------------------
42
- # Admin agent — code directory protection + approval gating
43
- # ---------------------------------------------------------------------------
44
- if [ "$AGENT_TYPE" = "admin" ]; then
45
-
46
- # ── Orchestration-only authoring + research block (Task 529, 559, 566) ───
47
- # The admin seat is orchestration + clarification + delivery only.
48
- # Write/Edit/MultiEdit/Bash/WebFetch/WebSearch are specialist-owned —
49
- # content-producer for authoring, research-assistant for research, the
50
- # shell tools for whichever specialist owns the workflow that needs them.
51
- # In-window Task subagents must keep these tools (Task 222 regression
52
- # class). Maxy MCP `Agent`-spawned specialists are separate top-level
53
- # `claude rc` processes — they have no `parent_tool_use_id` but pty-spawner
54
- # stamps `MAXY_SPECIALIST=<name>` on the child env (pty-spawner.ts:1601).
55
- #
56
- # Discriminator (union of positive markers, never path-shape inference):
57
- # parent_tool_use_id non-empty → in-window Task subagent (allow)
58
- # $MAXY_SPECIALIST non-empty → maxy MCP Agent specialist (allow)
59
- # $MAXY_SESSION_ROLE = admin → admin-direct (block, orchestration-only)
60
- # none of the above → fail closed (missing-marker)
61
- #
62
- # Adding a future spawn shape that lands neither marker surfaces as a loud
63
- # `reason=missing-marker` block, not a silent passthrough or a silent
64
- # admin-direct block on legitimate specialist work.
65
- case "$TOOL_NAME" in
66
- Write|Edit|MultiEdit|Bash|WebFetch|WebSearch)
67
- PARENT_TOOL_USE_ID=$(echo "$INPUT" | python3 -c '
68
- import sys, json
69
- try:
70
- v = json.load(sys.stdin).get("parent_tool_use_id", "")
71
- print(v if isinstance(v, str) else "")
72
- except Exception:
73
- print("")
74
- ' 2>/dev/null)
75
- SPECIALIST="${MAXY_SPECIALIST:-}"
76
- SESSION_ROLE="${MAXY_SESSION_ROLE:-}"
77
- if [ -n "$PARENT_TOOL_USE_ID" ] || [ -n "$SPECIALIST" ]; then
78
- # Subagent — passthrough. Tool name resolves on the subagent's
79
- # own frontmatter `tools:` allowlist downstream.
80
- SOURCE="task-subagent"
81
- if [ -n "$SPECIALIST" ] && [ -n "$PARENT_TOOL_USE_ID" ]; then
82
- SOURCE="task-subagent+mcp-specialist"
83
- elif [ -n "$SPECIALIST" ]; then
84
- SOURCE="mcp-specialist"
85
- fi
86
- echo "[admin-tool-gate] role=subagent parent_tool_use_id=${PARENT_TOOL_USE_ID:--} specialist=${SPECIALIST:--} session_role=${SESSION_ROLE:--} tool=${TOOL_NAME} decision=allow source=${SOURCE}" >&2
87
- elif [ "$SESSION_ROLE" = "admin" ]; then
88
- # Admin direct invocation — refuse with dispatch hint.
89
- case "$TOOL_NAME" in
90
- Write|Edit|MultiEdit)
91
- OWNER="content-producer"
92
- ;;
93
- Bash)
94
- OWNER="the specialist that owns the workflow needing shell access"
95
- ;;
96
- WebFetch|WebSearch)
97
- OWNER="research-assistant"
98
- ;;
99
- esac
100
- echo "[admin-tool-gate] role=admin parent_tool_use_id=- specialist=- session_role=admin tool=${TOOL_NAME} decision=block reason=orchestration-only owner=${OWNER}" >&2
101
- hook_block_with_emit "$SESSION_ID" "admin-tool-gate" "${TOOL_NAME}" \
102
- "orchestration-only" \
103
- "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.
104
- Dispatch the work to ${OWNER} via the Agent tool."
105
- else
106
- # No positive marker — fail closed. A new spawn shape that lands
107
- # neither parent_tool_use_id nor MAXY_SPECIALIST and is not the
108
- # admin seat should surface visibly, not silently pass or silently
109
- # block under the wrong reason.
110
- echo "[admin-tool-gate] role=unknown parent_tool_use_id=- specialist=- session_role=${SESSION_ROLE:--} tool=${TOOL_NAME} decision=block reason=missing-marker" >&2
111
- hook_block_with_emit "$SESSION_ID" "admin-tool-gate" "${TOOL_NAME}" \
112
- "missing-marker" \
113
- "Blocked: \`${TOOL_NAME}\` call carries no positive role marker (no parent_tool_use_id, no MAXY_SPECIALIST, MAXY_SESSION_ROLE is not 'admin'). Failing closed so a new spawn shape cannot silently bypass orchestration gating."
114
- fi
115
- ;;
116
- esac
117
-
118
- # ── Code directory protection ────────────────────────────────────────────
119
- case "$TOOL_NAME" in
120
- Write|Edit)
121
- FILE_PATH=$(echo "$INPUT" | grep -o '"file_path":"[^"]*"' | head -1 | cut -d'"' -f4)
122
- case "$FILE_PATH" in
123
- # Platform UI source (platform/ui/) — source lives here, not on device
124
- */platform/ui/app/*|*/platform/ui/lib/*|*/platform/ui/*.ts|*/platform/ui/*.tsx|*/platform/ui/*.mjs)
125
- hook_block_with_emit "$SESSION_ID" "admin-tool-gate" "${TOOL_NAME}" \
126
- "code-dir-protection-ui" \
127
- "Blocked: Admin agent cannot modify platform UI source at $FILE_PATH"
128
- ;;
129
- # Compiled MCP servers, hooks, and scripts — what actually ships to device
130
- */platform/plugins/*/mcp/dist/*|*/platform/plugins/*/hooks/*|*/platform/scripts/*)
131
- hook_block_with_emit "$SESSION_ID" "admin-tool-gate" "${TOOL_NAME}" \
132
- "code-dir-protection-platform" \
133
- "Blocked: Admin agent cannot modify platform code at $FILE_PATH"
134
- ;;
135
- # Entitlement library — verifier source, compiled output, vendored
136
- # pubkey, hash file, and any signed entitlement.json. Tampering here is the
137
- # agent path to revenue bypass; the legitimate write path is the Rubytech
138
- # issuance endpoint, never the agent's filesystem.
139
- # Patterns intentionally cover both absolute (*/...) and relative
140
- # (no leading slash) paths so an agent can't bypass via cwd-relative writes.
141
- */platform/lib/entitlement/*|platform/lib/entitlement/*|*/entitlement.json|entitlement.json)
142
- echo "[entitlement] tool-deny: tool=${TOOL_NAME} path=${FILE_PATH} field=entitlement-file" >&2
143
- hook_block_with_emit "$SESSION_ID" "admin-tool-gate" "${TOOL_NAME}" \
144
- "entitlement-file" \
145
- "Blocked: Admin agent cannot modify entitlement files at $FILE_PATH. Effective tier and purchasedPlugins derive from a Rubytech-signed payload."
146
- ;;
147
- # account.json — agent must use the account-update
148
- # MCP tool (or plugin-toggle-enabled for enabledPlugins changes), which
149
- # whitelists editable fields server-side. Direct Edit/Write bypasses
150
- # that whitelist; deny unconditionally including cwd-relative paths.
151
- # adds */account.json and *account.json as a backstop so any
152
- # layout the named patterns miss is still caught — the Adam Mackay
153
- # incident showed a tier upgrade via raw Edit on account.json reach
154
- # the disk, exactly the failure mode this hook exists to prevent.
155
- */data/accounts/*/account.json|*/config/accounts/*/account.json|data/accounts/*/account.json|config/accounts/*/account.json|*/account.json|*account.json|account.json)
156
- echo "[entitlement] tool-deny: tool=${TOOL_NAME} path=${FILE_PATH} field=account-json" >&2
157
- hook_block_with_emit "$SESSION_ID" "admin-tool-gate" "${TOOL_NAME}" \
158
- "account-json" \
159
- "Blocked: Admin agent cannot edit account.json directly. Use the account-update or plugin-toggle-enabled MCP tools — they whitelist editable fields server-side and exclude tier and purchasedPlugins by design."
160
- ;;
161
- # Pending action queue — only the hook and MCP tools may write here
162
- */pending-actions/*)
163
- hook_block_with_emit "$SESSION_ID" "admin-tool-gate" "${TOOL_NAME}" \
164
- "pending-actions" \
165
- "Blocked: Admin agent cannot modify pending action files directly"
166
- ;;
167
- esac
168
- ;;
169
- Bash)
170
- # Block shell commands referencing protected code directories or pending actions
171
- case "$INPUT" in
172
- *"platform/ui/app/"*|*"platform/ui/lib/"*|*"platform/plugins/"*"hooks/"*|*"platform/scripts/"*)
173
- hook_block_with_emit "$SESSION_ID" "admin-tool-gate" "Bash" \
174
- "code-dir-protection-bash" \
175
- "Blocked: Admin agent cannot run shell commands against code directories"
176
- ;;
177
- # Entitlement files via shell — same surface, blocked symmetrically
178
- *"platform/lib/entitlement/"*|*"entitlement.json"*)
179
- echo "[entitlement] tool-deny: tool=Bash path=entitlement-file field=entitlement-file" >&2
180
- hook_block_with_emit "$SESSION_ID" "admin-tool-gate" "Bash" \
181
- "entitlement-shell" \
182
- "Blocked: Admin agent cannot reference entitlement files via shell."
183
- ;;
184
- # account.json via shell — same rationale as Edit/Write block
185
- *"data/accounts/"*"account.json"*|*"config/accounts/"*"account.json"*)
186
- echo "[entitlement] tool-deny: tool=Bash path=account-json field=account-json" >&2
187
- hook_block_with_emit "$SESSION_ID" "admin-tool-gate" "Bash" \
188
- "account-json-shell" \
189
- "Blocked: Admin agent cannot edit account.json via shell. Use the account-update MCP tool."
190
- ;;
191
- *"pending-actions/"*)
192
- hook_block_with_emit "$SESSION_ID" "admin-tool-gate" "Bash" \
193
- "pending-actions-shell" \
194
- "Blocked: Admin agent cannot modify pending action files via shell"
195
- ;;
196
- esac
197
- ;;
198
- esac
199
-
200
- # ── Base64 context-overflow guard ─────────────────────────────
201
- # Block inline base64 payloads from reaching the model context. Two paths:
202
- #
203
- # 1. Bash command that ENCODES a binary file to base64/hex (the producer).
204
- # 2. Write/Edit content carrying an inline `data:<mime>;base64,…` blob
205
- # (the consumer — agent quoting bytes from a prior tool_result into a
206
- # HTML/markdown Write).
207
- #
208
- # Either path landed ~33 KB of base64 in the SDK request and the next turn
209
- # tripped `main_stream_stalled` at 180 s. The
210
- # remediation is symmetric: the agent saves bytes to `$ACCOUNT_DIR/tmp/<sha1>.<ext>`
211
- # and references the path (`<img src="./file">` or Read-by-path) instead of
212
- # carrying bytes through the assistant turn.
213
- #
214
- # Parsing uses python3 (already a hook dependency at the action-id site
215
- # below); grep on JSON is unsafe for content with escaped quotes or
216
- # embedded newlines. Parse failure is fail-open (empty extracted string,
217
- # no match, allow) — matches the playwright-file-guard fail-open contract.
218
- # Single python3 invocation parses the JSON, runs the tool-specific
219
- # regex match (avoiding BSD-vs-GNU grep interval-count incompatibilities —
220
- # `grep -E '{4096,}'` errors with "invalid repetition count(s)" on macOS
221
- # BSD grep under some pattern combinations), and prints the rejection
222
- # outcome to stdout as `REJECT:<reason>:<bytes>` or `ALLOW`. The wrapping
223
- # bash logic reads the verdict and emits the rejection log/stderr/exit-2.
224
- # Parse failure prints `ALLOW` (fail-open, matching the playwright-file-
225
- # guard contract for malformed stdin).
226
- GUARD_VERDICT=$(echo "$INPUT" | python3 -c '
227
- import sys, json, re
228
- try:
229
- d = json.load(sys.stdin)
230
- tool = d.get("tool_name", "")
231
- ti = d.get("tool_input", {}) or {}
232
- if tool in ("Write", "Edit"):
233
- # Write.content OR Edit.new_string can carry inline base64.
234
- content = ti.get("content") or ti.get("new_string") or ""
235
- if not isinstance(content, str):
236
- print("ALLOW"); sys.exit(0)
237
- # data:<mime>;base64,<≥4096 base64 chars> — threshold matches the
238
- # doctrine paragraph in .docs/agents.md. The 4096-char body is
239
- # ~3 KB binary, far above any legitimate inline icon.
240
- m = re.search(r"data:[^;]+;base64,[A-Za-z0-9+/]{4096,}={0,2}", content)
241
- if m:
242
- print(f"REJECT:base64-write-content:{len(content)}")
243
- else:
244
- print("ALLOW")
245
- elif tool == "Bash":
246
- command = ti.get("command", "")
247
- if not isinstance(command, str):
248
- print("ALLOW"); sys.exit(0)
249
- # Per-segment scan. A compound command like
250
- # cat in.b64 | base64 -d > out.bin; cat photo.png | base64
251
- # contains both a legitimate decode AND a malicious encoder. A whole-
252
- # command decode-flag check is fooled into allowing the encoder. Split
253
- # on shell separators (;, &&, ||, &) and scan each segment as its own
254
- # command — the encoder rejection fires when ANY segment is a bare
255
- # base64 invocation without a paired decode flag in the SAME segment.
256
- # Pipelines (|) keep the segment together because the encoder direction
257
- # of `cat file | base64` lives across the pipe.
258
- segments = re.split(r";|&&|\|\||(?<![|&])&(?![|&])", command)
259
- rejected = None
260
- for seg in segments:
261
- if re.search(r"(?:^|[\s|;&])xxd[\t ]+-p(?![A-Za-z0-9_-])", seg):
262
- rejected = "xxd-plain-hex"; break
263
- if re.search(r"(?:^|[\s|;&])base64(?![A-Za-z0-9_-])", seg):
264
- if not re.search(r"base64[\t ]+[^|]*(?:-d|-D|--decode)(?![A-Za-z0-9_])", seg):
265
- rejected = "base64-encoder"; break
266
- if rejected:
267
- print(f"REJECT:{rejected}:{len(command)}")
268
- else:
269
- print("ALLOW")
270
- else:
271
- print("ALLOW")
272
- except Exception:
273
- print("ALLOW")
274
- ' 2>/dev/null || echo "ALLOW")
275
- case "$GUARD_VERDICT" in
276
- REJECT:base64-write-content:*)
277
- BYTES="${GUARD_VERDICT##*:}"
278
- echo "[pre-tool-use] guard=base64-write-content bytes=${BYTES} action=reject" >&2
279
- hook_block_with_emit "$SESSION_ID" "base64-guard" "${TOOL_NAME}" \
280
- "base64-write-content" \
281
- "Blocked: ${TOOL_NAME} content carries an inline base64 payload (>4 KB encoded). Inline binary in Write.content overloads the model context — the same path produced a main_stream_stalled at ~33 KB on 2026-05-09.
282
- Save the bytes to \$ACCOUNT_DIR/tmp/<sha1>.<ext> via Bash (e.g. 'base64 -d > out.png'), then reference the file from the document: <img src=\"./<file>\"> or Read-by-path. Do not carry binary bytes through the assistant turn."
283
- ;;
284
- REJECT:base64-encoder:*|REJECT:xxd-plain-hex:*)
285
- REASON="${GUARD_VERDICT#REJECT:}"; REASON="${REASON%:*}"
286
- BYTES="${GUARD_VERDICT##*:}"
287
- echo "[pre-tool-use] guard=base64-tool-result bytes=${BYTES} tool=Bash reason=${REASON} action=reject" >&2
288
- hook_block_with_emit "$SESSION_ID" "base64-guard" "Bash" \
289
- "$REASON" \
290
- "Blocked: Bash command would emit binary as inline base64/hex to stdout, which lands in the assistant turn and overloads the model context (the 2026-05-09 Rubytech-invoice path hit 66% context after a single ~33 KB tool_result).
291
- Instead: save the bytes directly to \$ACCOUNT_DIR/tmp/<sha1>.<ext> and operate on the file via path — Read for inspection, <img src=\"./<file>\"> for HTML embedding, the \`file-presentation\` skill for delivery. Decoding base64 (e.g. 'base64 -d in.b64 > out.bin') is allowed."
292
- ;;
293
- *)
294
- : # ALLOW — fall through to approval gating below
295
- ;;
296
- esac
297
-
298
- # ── Approval gating (EU AI Act Article 14 — human oversight) ─────────────
299
- # Strip the mcp__<plugin>__ prefix to get the short tool name.
300
- # Built-in tools (no prefix) pass through unchanged.
301
- SHORT_NAME=$(echo "$TOOL_NAME" | sed 's/^mcp__[^_]*__//')
302
-
303
- # Derive platform root from this hook's location:
304
- # platform/plugins/admin/hooks/pre-tool-use.sh → platform/
305
- HOOK_DIR="$(cd "$(dirname "$0")" 2>/dev/null && pwd)"
306
- PLATFORM_ROOT="${HOOK_DIR}/../../.."
307
-
308
- # Resolve the single account directory (Phase 0).
309
- ACCOUNTS_DIR="${PLATFORM_ROOT}/../data/accounts"
310
- ACCOUNT_DIR=""
311
- if [ -d "$ACCOUNTS_DIR" ]; then
312
- ACCOUNT_DIR=$(find "$ACCOUNTS_DIR" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | head -1)
313
- fi
314
-
315
- # Default require-review tools — matches DEFAULT_REQUIRE_REVIEW in claude-agent.ts
316
- DEFAULT_REQUIRE_REVIEW="email-send email-reply whatsapp-send whatsapp-send-document message contact-erase"
317
-
318
- # Check if this tool requires approval review.
319
- # 1. Read per-account override from account.json approvalPolicy (if it exists)
320
- # 2. Fall back to the default require-review list
321
- POLICY=""
322
- if [ -n "$ACCOUNT_DIR" ] && [ -f "${ACCOUNT_DIR}/account.json" ]; then
323
- # Check per-account override first (grep for the tool name in the approvalPolicy block)
324
- OVERRIDE=$(grep -o "\"${SHORT_NAME}\"[[:space:]]*:[[:space:]]*\"[^\"]*\"" "${ACCOUNT_DIR}/account.json" 2>/dev/null | head -1 | grep -o '"[^"]*"$' | tr -d '"')
325
- if [ -n "$OVERRIDE" ]; then
326
- POLICY="$OVERRIDE"
327
- fi
328
- fi
329
-
330
- # If no per-account override, check the default list
331
- if [ -z "$POLICY" ]; then
332
- for TOOL in $DEFAULT_REQUIRE_REVIEW; do
333
- if [ "$SHORT_NAME" = "$TOOL" ]; then
334
- POLICY="require-review"
335
- break
336
- fi
337
- done
338
- fi
339
-
340
- # If not in any policy, auto-execute (allow)
341
- if [ "$POLICY" != "require-review" ]; then
342
- exit 0
343
- fi
344
-
345
- # ── Special case: contact-erase preview calls pass through ───────────────
346
- # The contact-erase tool has a two-step flow: first call with confirm:false
347
- # returns a preview (data counts), second call with confirm:true executes.
348
- # Only gate the destructive confirm:true call.
349
- if [ "$SHORT_NAME" = "contact-erase" ]; then
350
- if ! echo "$INPUT" | grep -q '"confirm"[[:space:]]*:[[:space:]]*true'; then
351
- exit 0
352
- fi
353
- fi
354
-
355
- # ── Queue the action for approval ───────────────────────────────────────
356
- PENDING_DIR="${ACCOUNT_DIR}/pending-actions"
357
- mkdir -p "$PENDING_DIR" 2>/dev/null
358
-
359
- ACTION_ID="$(cat /proc/sys/kernel/random/uuid 2>/dev/null || python3 -c 'import uuid; print(uuid.uuid4())' 2>/dev/null || date +%s%N)"
360
- CREATED_AT="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
361
-
362
- # Write atomically: temp file + mv
363
- TEMP=$(mktemp "${PENDING_DIR}/.tmp.XXXXXX" 2>/dev/null)
364
- if [ -z "$TEMP" ]; then
365
- hook_block_with_emit "$SESSION_ID" "approval-gate" "${TOOL_NAME}" \
366
- "approval-queue-mktemp" \
367
- "Blocked: Action requires approval but failed to create queue file (disk error). Failing closed."
368
- fi
369
-
370
- # The pending action file contains the full hook JSON (tool_name + tool_input)
371
- # plus metadata. The MCP approval tools parse it with Node.js JSON.parse.
372
- cat > "$TEMP" <<ENDJSON
373
- {
374
- "actionId": "${ACTION_ID}",
375
- "toolName": "${SHORT_NAME}",
376
- "pluginName": "$(echo "$TOOL_NAME" | sed -n 's/^mcp__\([^_]*\)__.*/\1/p')",
377
- "hookPayload": ${INPUT},
378
- "state": "pending",
379
- "createdAt": "${CREATED_AT}"
380
- }
381
- ENDJSON
382
-
383
- if ! mv "$TEMP" "${PENDING_DIR}/${ACTION_ID}.json" 2>/dev/null; then
384
- rm -f "$TEMP" 2>/dev/null
385
- hook_block_with_emit "$SESSION_ID" "approval-gate" "${TOOL_NAME}" \
386
- "approval-queue-mv" \
387
- "Blocked: Action requires approval but failed to queue (filesystem error). Failing closed."
388
- fi
389
-
390
- # Record the queued-for-approval block in the propagation buffer; the
391
- # readable summary still goes to stdout (CC attaches it as the agent's
392
- # tool_result). hook_emit_decision is the no-exit variant — the
393
- # subsequent echoes + exit 2 below remain the operator-facing surface.
394
- hook_emit_decision "$SESSION_ID" "approval-gate" "${TOOL_NAME}" \
395
- "block" "approval-queued" \
396
- "Action ${ACTION_ID} queued for review" 2 0
397
-
398
- # Extract a readable summary of the action for the agent to present
399
- # tool_input is the second JSON value in the hook payload
400
- echo "Action requires approval before execution."
401
- echo ""
402
- echo "Action ID: ${ACTION_ID}"
403
- echo "Tool: ${SHORT_NAME}"
404
- echo "Status: Queued for review"
405
- echo ""
406
- echo "The admin must approve this action before it can execute."
407
- echo "Use action-approve to send, action-reject to cancel, or action-edit to modify."
408
- exit 2
409
- fi
@@ -1,116 +0,0 @@
1
- /**
2
- * MCP eager-load helper.
3
- *
4
- * The Claude Code SDK marks every MCP tool as deferred-by-default: the model
5
- * cannot invoke an MCP tool until it has first called `ToolSearch` to load
6
- * the schema, which costs one extra turn per unique schema. The SDK's per-
7
- * tool override is `_meta["anthropic/alwaysLoad"]: true` on the tool's
8
- * `tools/list` entry, which `isDeferredTool` reads to flip the default.
9
- *
10
- * The MCP SDK exposes that channel only via `server.registerTool(name,
11
- * config, handler)`; the older `server.tool(name, desc, schema, handler)`
12
- * has no `_meta` parameter. This helper wraps the registerTool call so
13
- * every plugin's index.ts can opt a tool into eager-load with one line
14
- * matching the shape of the existing four-arg `server.tool` overload.
15
- *
16
- * Use for every tool that appears in `ADMIN_CORE_TOOLS` — the permission
17
- * allow-list IS the curation per the CEO review. Tools the
18
- * admin should not see should be removed from the allow-list, not merely
19
- * left deferred.
20
- */
21
- /** Wire-format metadata key the Claude Code SDK reads. */
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;
58
- /**
59
- * Loose structural type for any object exposing a `registerTool` method. The
60
- * MCP SDK ships dual ESM/CJS packages with conditional exports, so importing
61
- * the concrete `McpServer` type here would force every consumer to compile
62
- * under the same resolution mode — they don't. Keep this contract minimal:
63
- * we only need the method to exist; we cast internally to drive the
64
- * upstream generic overloads which differ between SDK versions.
65
- */
66
- type EagerCapableServer = {
67
- registerTool: (...args: any[]) => any;
68
- };
69
- /**
70
- * Local structural type for a Zod raw shape. We deliberately do NOT import
71
- * `ZodRawShape` from zod itself — plugin workspaces ship pinned zod v3 in
72
- * their nested `node_modules/zod`, while `platform/node_modules/zod` is v4,
73
- * and the two `ZodRawShape` types are structurally incompatible (v4
74
- * introduced the `_zod` brand on `ZodType`). A typed-by-value `unknown`
75
- * member is enough: TypeScript treats it as compatible with both v3 and v4
76
- * shapes, while still constraining the generic to "an object whose values
77
- * are zod-like" so the caller's destructured handler args stay typed via
78
- * the SDK's own overload resolution downstream.
79
- */
80
- type LocalZodRawShape = Record<string, unknown>;
81
- /**
82
- * Register an MCP tool that the Claude Code SDK eager-loads into the
83
- * model's prompt at session boot, bypassing the ToolSearch round-trip.
84
- *
85
- * Mirrors the four-arg `server.tool(name, description, inputSchema, handler)`
86
- * call shape — the only overload in use across our plugins — so callers
87
- * replace `server.tool(` with `eagerTool(server, ` mechanically.
88
- *
89
- * The helper deliberately does not constrain handler-arg types itself; the
90
- * downstream `server.registerTool` invocation inside this function passes
91
- * the shape through, and the upstream MCP SDK's overload (resolved against
92
- * the consumer's own zod version) infers the handler args.
93
- */
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;
115
- export {};
116
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
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,125 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ALWAYS_LOAD_META_KEY = void 0;
4
- exports.isAdminSeat = isAdminSeat;
5
- exports.adminBlockedResponse = adminBlockedResponse;
6
- exports.withAdminGate = withAdminGate;
7
- exports.eagerTool = eagerTool;
8
- exports.eagerToolGated = eagerToolGated;
9
- /**
10
- * MCP eager-load helper.
11
- *
12
- * The Claude Code SDK marks every MCP tool as deferred-by-default: the model
13
- * cannot invoke an MCP tool until it has first called `ToolSearch` to load
14
- * the schema, which costs one extra turn per unique schema. The SDK's per-
15
- * tool override is `_meta["anthropic/alwaysLoad"]: true` on the tool's
16
- * `tools/list` entry, which `isDeferredTool` reads to flip the default.
17
- *
18
- * The MCP SDK exposes that channel only via `server.registerTool(name,
19
- * config, handler)`; the older `server.tool(name, desc, schema, handler)`
20
- * has no `_meta` parameter. This helper wraps the registerTool call so
21
- * every plugin's index.ts can opt a tool into eager-load with one line
22
- * matching the shape of the existing four-arg `server.tool` overload.
23
- *
24
- * Use for every tool that appears in `ADMIN_CORE_TOOLS` — the permission
25
- * allow-list IS the curation per the CEO review. Tools the
26
- * admin should not see should be removed from the allow-list, not merely
27
- * left deferred.
28
- */
29
- /** Wire-format metadata key the Claude Code SDK reads. */
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
- }
84
- /**
85
- * Register an MCP tool that the Claude Code SDK eager-loads into the
86
- * model's prompt at session boot, bypassing the ToolSearch round-trip.
87
- *
88
- * Mirrors the four-arg `server.tool(name, description, inputSchema, handler)`
89
- * call shape — the only overload in use across our plugins — so callers
90
- * replace `server.tool(` with `eagerTool(server, ` mechanically.
91
- *
92
- * The helper deliberately does not constrain handler-arg types itself; the
93
- * downstream `server.registerTool` invocation inside this function passes
94
- * the shape through, and the upstream MCP SDK's overload (resolved against
95
- * the consumer's own zod version) infers the handler args.
96
- */
97
- function eagerTool(server, name, description, inputSchema,
98
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
99
- handler) {
100
- server.registerTool(name, {
101
- description,
102
- inputSchema,
103
- _meta: { [exports.ALWAYS_LOAD_META_KEY]: true },
104
- }, handler);
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
- }
125
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
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"}