agenthub-multiagent-mcp 1.47.0 → 1.49.0

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 (48) hide show
  1. package/dist/channel.js +65 -41
  2. package/dist/channel.js.map +1 -1
  3. package/dist/client.d.ts +80 -0
  4. package/dist/client.d.ts.map +1 -1
  5. package/dist/client.js +67 -0
  6. package/dist/client.js.map +1 -1
  7. package/dist/commands/setup-shell.js +11 -11
  8. package/dist/setup.js +1 -77
  9. package/dist/setup.js.map +1 -1
  10. package/dist/skillInstaller.d.ts.map +1 -1
  11. package/dist/skillInstaller.gate.test.js +6 -0
  12. package/dist/skillInstaller.gate.test.js.map +1 -1
  13. package/dist/skillInstaller.js +9 -0
  14. package/dist/skillInstaller.js.map +1 -1
  15. package/dist/skillInstaller.test.js +39 -1
  16. package/dist/skillInstaller.test.js.map +1 -1
  17. package/dist/tools/index.d.ts.map +1 -1
  18. package/dist/tools/index.js +229 -0
  19. package/dist/tools/index.js.map +1 -1
  20. package/dist/tools/tools.test.js +71 -10
  21. package/dist/tools/tools.test.js.map +1 -1
  22. package/dist/worker.js +0 -4
  23. package/dist/worker.js.map +1 -1
  24. package/package.json +1 -1
  25. package/skills/catalog.json +8 -0
  26. package/skills/commands/start-session.md +77 -77
  27. package/skills/manifest.json +5 -0
  28. package/skills/skills/deploy-staging/SKILL.md +164 -164
  29. package/skills/skills/deploy-vps-openclaw/SKILL.md +97 -97
  30. package/skills/skills/karpathy-guidelines/SKILL.md +67 -67
  31. package/skills/skills/multiagent-brainstorm/SKILL.md +142 -0
  32. package/dist/.agenthub/activity +0 -1
  33. package/dist/channelLib.d.ts +0 -42
  34. package/dist/channelLib.d.ts.map +0 -1
  35. package/dist/channelLib.js +0 -74
  36. package/dist/channelLib.js.map +0 -1
  37. package/dist/channelLib.test.d.ts +0 -2
  38. package/dist/channelLib.test.d.ts.map +0 -1
  39. package/dist/channelLib.test.js +0 -67
  40. package/dist/channelLib.test.js.map +0 -1
  41. package/dist/channelSetup.d.ts +0 -44
  42. package/dist/channelSetup.d.ts.map +0 -1
  43. package/dist/channelSetup.js +0 -61
  44. package/dist/channelSetup.js.map +0 -1
  45. package/dist/channelSetup.test.d.ts +0 -2
  46. package/dist/channelSetup.test.d.ts.map +0 -1
  47. package/dist/channelSetup.test.js +0 -75
  48. package/dist/channelSetup.test.js.map +0 -1
@@ -0,0 +1,142 @@
1
+ ---
2
+ name: multiagent-brainstorm
3
+ description: Brainstorm a design with another AgentHub agent, mostly autonomously. Use when you want two agents (e.g. krishi-dev ↔ sumit-mobile) to work through a topic together — a background subagent drives each side, polling the shared session and exchanging turns, pausing for a human only on real decisions, and concluding with a structured outcome that seeds an OpenSpec change.
4
+ license: MIT
5
+ ---
6
+
7
+ # Multi-Agent Brainstorm
8
+
9
+ Two AgentHub agents brainstorm a topic through a **server-tracked session**. Each
10
+ side is driven by a **fully autonomous background subagent** that polls the
11
+ session, reasons, and posts turns — surfacing to a human only when a decision
12
+ genuinely needs one. A concluded brainstorm produces a structured outcome that
13
+ seeds an OpenSpec change.
14
+
15
+ Requires the server kill switch `AGENTHUB_BRAINSTORM_ENABLED=true` (tools return
16
+ `brainstorm_disabled` otherwise) and `agent_register` first (the tools bind to
17
+ your project). Runs on the latest Sonnet fine — no special model needed.
18
+
19
+ ## Two roles
20
+
21
+ You are one of:
22
+
23
+ - **initiator** — your human said "brainstorm X with `<peer>`". You call
24
+ `brainstorm_open`, which delivers an invite to the peer.
25
+ - **responder** — you received a `[[BRAINSTORM INVITE]]` message (via your
26
+ auto-inbox at session start, or mid-session). Call `brainstorm_accept` and
27
+ mirror the initiator.
28
+
29
+ If you got a brainstorm invite in your inbox, run the **responder** flow below.
30
+ Otherwise, if the user asked you to brainstorm with someone, run **initiator**.
31
+
32
+ ## Initiator flow
33
+
34
+ 1. `brainstorm_open({ peer, topic, escalation })` — `escalation` is `local`
35
+ (default; you'll ask your local human via a decision prompt) or `slack` (the
36
+ subagent DMs the operator and polls for the reply). Note the returned
37
+ `session_id`.
38
+ 2. **Spawn the autonomous subagent** (see below) with role `initiator`,
39
+ the `session_id`, `after_seq=0`, the topic, and your human's framing.
40
+ 3. Wait. Handle any `NEED_HUMAN` return (see *Human escalation*).
41
+ 4. When the subagent reports `concluded`, run the **Conclusion → OpenSpec** step.
42
+
43
+ ## Responder flow
44
+
45
+ 1. `brainstorm_accept({ session_id })` (idempotent — safe if the invite was
46
+ redelivered).
47
+ 2. Spawn the autonomous subagent with role `responder`, the `session_id`,
48
+ `after_seq=0`, and the topic from the invite.
49
+ 3. Handle `NEED_HUMAN` the same way. When the subagent reports `concluded`, you're
50
+ done — **only the initiator authors the OpenSpec** (avoids double-proposing).
51
+
52
+ ## The autonomous subagent
53
+
54
+ Spawn it with the `Agent` tool (background). Give it this loop, verbatim in
55
+ spirit — it holds the whole transcript in its own context so your main session
56
+ stays clean:
57
+
58
+ ```
59
+ after_seq = 0
60
+ loop:
61
+ { turns, state, turn_count } = brainstorm_poll(session_id, after_seq)
62
+ after_seq = max(seq of turns, after_seq)
63
+
64
+ if state != "active":
65
+ return { done: state } # concluded | aborted | exhausted | abandoned
66
+
67
+ peer_turns = turns authored by the OTHER agent
68
+ if peer's latest turn.kind == "propose_conclude":
69
+ if you agree with their outcome:
70
+ brainstorm_conclude(session_id, outcome) # mutual consensus
71
+ else:
72
+ brainstorm_turn(session_id, "<why not yet, what's open>")
73
+ continue
74
+
75
+ if it's your turn (peer has posted since your last, or you're the initiator on seq 0):
76
+ reason about the dialogue and form your next contribution
77
+ if a decision here really needs a human (scope, tradeoff, commitment):
78
+ return { need_human: { question, digest, after_seq } }
79
+ else:
80
+ brainstorm_turn(session_id, "<your turn>")
81
+ # when you believe you're done, post kind="propose_conclude" with a draft outcome instead
82
+ continue
83
+
84
+ # nothing new and not your move → wait for the peer
85
+ backoff sleep (start ~5s, cap ~30s), then re-poll
86
+ # if last_turn_at is older than the silence deadline, brainstorm_close(state="abandoned")
87
+ ```
88
+
89
+ **Discipline:** one contribution per turn; stay on the topic; don't rubber-stamp
90
+ — push back when the design isn't right yet; propose_conclude only when the
91
+ problem, approach, and key decisions are actually settled.
92
+
93
+ ## Human escalation (return-and-resume)
94
+
95
+ A background subagent can't ask a human directly. When it returns
96
+ `{ need_human: { question, digest, after_seq } }`:
97
+
98
+ - **`escalation=local`** — ask your human with `AskUserQuestion` (include the
99
+ `digest` for context).
100
+ - **`escalation=slack`** — `slack_dm` the operator and poll your inbox for the
101
+ reply.
102
+
103
+ Then **resume the same subagent** (SendMessage to its agent id, context intact)
104
+ with the human's answer and the `after_seq` it gave you. It posts the
105
+ human-informed turn and continues. Do **not** spawn a fresh subagent — you'd lose
106
+ the transcript.
107
+
108
+ ## Peer is a headless daemon?
109
+
110
+ Employee daemons (eng-ic, expert, …) can't run a live loop — one inbound message
111
+ is one shift. If the peer never `brainstorm_accept`s but replies via ordinary
112
+ A2A, the subagent should **fall back to async round-trips**: post a turn, then
113
+ `check_inbox`/`brainstorm_poll` on a longer cadence, one shift per turn. The
114
+ session still tracks turns; you just can't expect a mirror subagent on their end.
115
+
116
+ ## Conclusion → OpenSpec (initiator only)
117
+
118
+ On `concluded`, `brainstorm_get(session_id)` returns the structured `outcome`
119
+ (`problem`, `agreed_approach`, `key_decisions`, `open_questions`,
120
+ `suggested_change_id`, `summary`). Then:
121
+
122
+ 1. **Render the outcome to your human** and ask (via `AskUserQuestion`): author
123
+ the OpenSpec now? — matches the repo's plan-first mandate; a human verifies the
124
+ agreed design before any spec is written.
125
+ 2. On approval, invoke the **`openspec:proposal`** skill seeded from the outcome
126
+ (use `suggested_change_id` as the change id), then `openspec_publish` to sync
127
+ to AgentHub (the mandate: a change on disk only isn't done).
128
+ 3. `brainstorm_link_change(session_id, change_id)` to stamp the change onto the
129
+ session — closes the traceability loop (brainstorm → change).
130
+
131
+ ## Stopping early
132
+
133
+ Either operator can halt: `brainstorm_close({ session_id, reason })` →
134
+ `aborted`. The safety backstops (`exhausted` at `max_turns`, `abandoned` on peer
135
+ silence) fire automatically so a runaway loop or an offline peer can't hang.
136
+
137
+ ## Tool reference
138
+
139
+ `brainstorm_open` · `brainstorm_accept` · `brainstorm_turn` · `brainstorm_poll` ·
140
+ `brainstorm_conclude` · `brainstorm_close` · `brainstorm_get` ·
141
+ `brainstorm_link_change`. Full design: the `multiagent-brainstorm-design` doc;
142
+ guide: `multiagent-brainstorm-guide`.
@@ -1 +0,0 @@
1
- {"active_at":"2026-06-20T12:20:44.058Z","category":"other"}
@@ -1,42 +0,0 @@
1
- /**
2
- * Pure helpers for the AgentHub channel plugin (add-inbox-push-bridge).
3
- *
4
- * Extracted from channel.ts so the WS→notification mapping, kill switch, and
5
- * reply-body construction are unit-testable without opening a socket or running
6
- * the MCP server. channel.ts wires these into the live plugin.
7
- */
8
- export interface ChannelNotification {
9
- content: string;
10
- meta: Record<string, unknown>;
11
- }
12
- /**
13
- * Phase B kill switch (§2.1). The plugin is OFF unless
14
- * AGENTHUB_INBOX_CHANNEL_ENABLED is explicitly truthy. Independent of
15
- * AGENTHUB_AUTO_INBOX (the Phase A SessionStart poll), which stays the floor.
16
- */
17
- export declare function channelEnabled(env?: NodeJS.ProcessEnv): boolean;
18
- /**
19
- * Map a raw AgentHub WS event to a channel notification, or null when nothing
20
- * should be surfaced (self-message, or an event type we don't push). The caller
21
- * still ACKs message events even when this returns null (self-filter case).
22
- */
23
- export declare function buildChannelNotification(msg: unknown, agentId: string): ChannelNotification | null;
24
- /**
25
- * Build the reply POST body (§2.2). Mirrors mcp__agenthub__reply semantics: the
26
- * recipient resolves to the ORIGINAL sender (never empty) and the reply carries
27
- * reply_to, which is what transitions the original message to `responded`
28
- * server-side. The old plugin posted to_agent:"" then PATCHed status to `read`,
29
- * which left the original un-responded — fixed here.
30
- */
31
- export declare function buildReplyBody(original: {
32
- from_agent: string;
33
- subject: string;
34
- }, agentId: string, body: string, messageId: string): {
35
- from_agent: string;
36
- to_agent: string;
37
- type: "response";
38
- subject: string;
39
- body: string;
40
- reply_to: string;
41
- };
42
- //# sourceMappingURL=channelLib.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"channelLib.d.ts","sourceRoot":"","sources":["../src/channelLib.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAE5E;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,OAAO,EACZ,OAAO,EAAE,MAAM,GACd,mBAAmB,GAAG,IAAI,CAuC5B;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EACjD,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,GAChB;IACD,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,UAAU,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB,CASA"}
@@ -1,74 +0,0 @@
1
- /**
2
- * Pure helpers for the AgentHub channel plugin (add-inbox-push-bridge).
3
- *
4
- * Extracted from channel.ts so the WS→notification mapping, kill switch, and
5
- * reply-body construction are unit-testable without opening a socket or running
6
- * the MCP server. channel.ts wires these into the live plugin.
7
- */
8
- /**
9
- * Phase B kill switch (§2.1). The plugin is OFF unless
10
- * AGENTHUB_INBOX_CHANNEL_ENABLED is explicitly truthy. Independent of
11
- * AGENTHUB_AUTO_INBOX (the Phase A SessionStart poll), which stays the floor.
12
- */
13
- export function channelEnabled(env = process.env) {
14
- return /^(1|true|yes|on)$/i.test((env.AGENTHUB_INBOX_CHANNEL_ENABLED || "").trim());
15
- }
16
- /**
17
- * Map a raw AgentHub WS event to a channel notification, or null when nothing
18
- * should be surfaced (self-message, or an event type we don't push). The caller
19
- * still ACKs message events even when this returns null (self-filter case).
20
- */
21
- export function buildChannelNotification(msg, agentId) {
22
- if (!msg || typeof msg !== "object")
23
- return null;
24
- const m = msg;
25
- if (m.type === "message" && m.message) {
26
- const msgObj = m.message;
27
- // Never echo a message the agent itself sent.
28
- if (msgObj.from_agent === agentId)
29
- return null;
30
- const priority = msgObj.priority === "urgent" ? " [URGENT]" : msgObj.priority === "high" ? " [HIGH]" : "";
31
- const content = `📨 Message from ${msgObj.from_agent}${priority}\n` +
32
- `Subject: ${msgObj.subject}\n\n${msgObj.body}\n\n` +
33
- `Message ID: ${msgObj.id}\n` +
34
- `Use agenthub_reply with this message_id to respond.`;
35
- return {
36
- content,
37
- meta: {
38
- source: "agenthub",
39
- from: msgObj.from_agent,
40
- subject: msgObj.subject,
41
- message_id: msgObj.id,
42
- type: msgObj.type,
43
- priority: msgObj.priority,
44
- },
45
- };
46
- }
47
- if (m.type === "task" && m.task) {
48
- const t = m.task;
49
- return {
50
- content: `📋 New task assigned by ${t.assigned_by}\n\n${t.task}\n\n` +
51
- `Task ID: ${t.id}\nPriority: ${t.priority}`,
52
- meta: { source: "agenthub", from: t.assigned_by, task_id: t.id, type: "task", priority: t.priority },
53
- };
54
- }
55
- return null;
56
- }
57
- /**
58
- * Build the reply POST body (§2.2). Mirrors mcp__agenthub__reply semantics: the
59
- * recipient resolves to the ORIGINAL sender (never empty) and the reply carries
60
- * reply_to, which is what transitions the original message to `responded`
61
- * server-side. The old plugin posted to_agent:"" then PATCHed status to `read`,
62
- * which left the original un-responded — fixed here.
63
- */
64
- export function buildReplyBody(original, agentId, body, messageId) {
65
- return {
66
- from_agent: agentId,
67
- to_agent: original.from_agent,
68
- type: "response",
69
- subject: `Re: ${original.subject}`,
70
- body: body.slice(0, 5000),
71
- reply_to: messageId,
72
- };
73
- }
74
- //# sourceMappingURL=channelLib.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"channelLib.js","sourceRoot":"","sources":["../src/channelLib.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,MAAyB,OAAO,CAAC,GAAG;IACjE,OAAO,oBAAoB,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,8BAA8B,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;AACtF,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,wBAAwB,CACtC,GAAY,EACZ,OAAe;IAEf,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACjD,MAAM,CAAC,GAAG,GAAmD,CAAC;IAE9D,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;QACtC,MAAM,MAAM,GAAG,CAAC,CAAC,OAAO,CAAC;QACzB,8CAA8C;QAC9C,IAAI,MAAM,CAAC,UAAU,KAAK,OAAO;YAAE,OAAO,IAAI,CAAC;QAC/C,MAAM,QAAQ,GACZ,MAAM,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3F,MAAM,OAAO,GACX,mBAAmB,MAAM,CAAC,UAAU,GAAG,QAAQ,IAAI;YACnD,YAAY,MAAM,CAAC,OAAO,OAAO,MAAM,CAAC,IAAI,MAAM;YAClD,eAAe,MAAM,CAAC,EAAE,IAAI;YAC5B,qDAAqD,CAAC;QACxD,OAAO;YACL,OAAO;YACP,IAAI,EAAE;gBACJ,MAAM,EAAE,UAAU;gBAClB,IAAI,EAAE,MAAM,CAAC,UAAU;gBACvB,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,UAAU,EAAE,MAAM,CAAC,EAAE;gBACrB,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,QAAQ,EAAE,MAAM,CAAC,QAAQ;aAC1B;SACF,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;QAChC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;QACjB,OAAO;YACL,OAAO,EACL,2BAA2B,CAAC,CAAC,WAAW,OAAO,CAAC,CAAC,IAAI,MAAM;gBAC3D,YAAY,CAAC,CAAC,EAAE,eAAe,CAAC,CAAC,QAAQ,EAAE;YAC7C,IAAI,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE;SACrG,CAAC;IACJ,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAC5B,QAAiD,EACjD,OAAe,EACf,IAAY,EACZ,SAAiB;IASjB,OAAO;QACL,UAAU,EAAE,OAAO;QACnB,QAAQ,EAAE,QAAQ,CAAC,UAAU;QAC7B,IAAI,EAAE,UAAU;QAChB,OAAO,EAAE,OAAO,QAAQ,CAAC,OAAO,EAAE;QAClC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC;QACzB,QAAQ,EAAE,SAAS;KACpB,CAAC;AACJ,CAAC"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=channelLib.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"channelLib.test.d.ts","sourceRoot":"","sources":["../src/channelLib.test.ts"],"names":[],"mappings":""}
@@ -1,67 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { buildChannelNotification, buildReplyBody, channelEnabled } from "./channelLib.js";
3
- // add-inbox-push-bridge §4 — hardening tests for the channel plugin's pure logic.
4
- describe("channelEnabled (kill switch §2.1)", () => {
5
- it("is off when unset", () => {
6
- expect(channelEnabled({})).toBe(false);
7
- });
8
- it("is off for falsey strings", () => {
9
- for (const v of ["", "false", "0", "no", "off", " "]) {
10
- expect(channelEnabled({ AGENTHUB_INBOX_CHANNEL_ENABLED: v })).toBe(false);
11
- }
12
- });
13
- it("is on only for explicit truthy values (case-insensitive, trimmed)", () => {
14
- for (const v of ["true", "TRUE", "1", "yes", "on", " true "]) {
15
- expect(channelEnabled({ AGENTHUB_INBOX_CHANNEL_ENABLED: v })).toBe(true);
16
- }
17
- });
18
- });
19
- describe("buildChannelNotification (§4.1)", () => {
20
- const agentId = "me";
21
- it("maps an inbound message to a channel notification", () => {
22
- const note = buildChannelNotification({
23
- type: "message",
24
- message: { id: "m1", from_agent: "alice", subject: "Hi", body: "ping", priority: "normal", type: "question" },
25
- }, agentId);
26
- expect(note).not.toBeNull();
27
- expect(note.content).toContain("Message from alice");
28
- expect(note.content).toContain("ping");
29
- expect(note.content).toContain("m1");
30
- expect(note.meta).toMatchObject({ source: "agenthub", from: "alice", message_id: "m1" });
31
- });
32
- it("surfaces priority for urgent/high", () => {
33
- const urgent = buildChannelNotification({ type: "message", message: { id: "m2", from_agent: "alice", subject: "x", body: "y", priority: "urgent" } }, agentId);
34
- expect(urgent.content).toContain("[URGENT]");
35
- });
36
- it("returns null for a self-message (no echo)", () => {
37
- const note = buildChannelNotification({ type: "message", message: { id: "m3", from_agent: agentId, subject: "x", body: "y" } }, agentId);
38
- expect(note).toBeNull();
39
- });
40
- it("maps a task event", () => {
41
- const note = buildChannelNotification({ type: "task", task: { id: "t1", assigned_by: "boss", task: "do it", priority: "high" } }, agentId);
42
- expect(note).not.toBeNull();
43
- expect(note.content).toContain("New task assigned by boss");
44
- expect(note.meta).toMatchObject({ task_id: "t1", type: "task" });
45
- });
46
- it("returns null for unknown / malformed events", () => {
47
- expect(buildChannelNotification({ type: "pong" }, agentId)).toBeNull();
48
- expect(buildChannelNotification({ type: "message" }, agentId)).toBeNull(); // no message body
49
- expect(buildChannelNotification(null, agentId)).toBeNull();
50
- expect(buildChannelNotification("nope", agentId)).toBeNull();
51
- });
52
- });
53
- describe("buildReplyBody (reply correctness §2.2)", () => {
54
- it("addresses the reply to the original sender and carries reply_to", () => {
55
- const body = buildReplyBody({ from_agent: "alice", subject: "Hi" }, "me", "thanks", "m1");
56
- expect(body.to_agent).toBe("alice"); // NOT "" — the old bug
57
- expect(body.from_agent).toBe("me");
58
- expect(body.type).toBe("response");
59
- expect(body.subject).toBe("Re: Hi");
60
- expect(body.reply_to).toBe("m1"); // reply_to is what marks the original `responded`
61
- });
62
- it("truncates the body to 5000 chars", () => {
63
- const body = buildReplyBody({ from_agent: "a", subject: "s" }, "me", "x".repeat(6000), "m1");
64
- expect(body.body.length).toBe(5000);
65
- });
66
- });
67
- //# sourceMappingURL=channelLib.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"channelLib.test.js","sourceRoot":"","sources":["../src/channelLib.test.ts"],"names":[],"mappings":"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"}
@@ -1,44 +0,0 @@
1
- /**
2
- * Interactive-session wiring for the AgentHub channel plugin
3
- * (add-inbox-push-bridge §3). Builds the `agenthub-channel` MCP-server entry and
4
- * merges it idempotently into a project's MCP config, so a normal `claude`
5
- * session can opt in via `--channels server:agenthub-channel`.
6
- *
7
- * Pure + filesystem helpers are split so the merge logic is unit-testable.
8
- */
9
- export declare const CHANNEL_SERVER_NAME = "agenthub-channel";
10
- export interface McpServerEntry {
11
- type: "stdio";
12
- command: string;
13
- args: string[];
14
- env: Record<string, string>;
15
- }
16
- export interface ChannelEntryOptions {
17
- url: string;
18
- apiKey: string;
19
- agentId: string;
20
- /** Defaults to true (explicit opt-in). false writes the entry but keeps the
21
- * plugin a no-op via the §2.1 kill switch — safe to install while disabled. */
22
- enabled?: boolean;
23
- }
24
- /**
25
- * The MCP-server entry that makes `server:agenthub-channel` resolvable. Runs the
26
- * published `agenthub-channel` bin via npx (same distribution as the main MCP),
27
- * carrying the agent's existing AgentHub creds + the kill switch.
28
- */
29
- export declare function channelMcpServerEntry(opts: ChannelEntryOptions): McpServerEntry;
30
- /**
31
- * Idempotently set the channel server entry under `mcpServers`, preserving every
32
- * other server. Re-running with the same opts produces the same config.
33
- */
34
- export declare function mergeChannelServer(config: Record<string, unknown>, entry: McpServerEntry, name?: string): Record<string, unknown>;
35
- /**
36
- * Read a JSON MCP-config file (or start from {} if absent/empty), merge the
37
- * channel entry, and write it back with 2-space indent. Returns whether the file
38
- * was newly created and whether the channel entry already matched (no-op).
39
- */
40
- export declare function applyChannelToConfigFile(filePath: string, opts: ChannelEntryOptions, name?: string): {
41
- created: boolean;
42
- unchanged: boolean;
43
- };
44
- //# sourceMappingURL=channelSetup.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"channelSetup.d.ts","sourceRoot":"","sources":["../src/channelSetup.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,eAAO,MAAM,mBAAmB,qBAAqB,CAAC;AAEtD,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC7B;AAED,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB;oFACgF;IAChF,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,mBAAmB,GAAG,cAAc,CAc/E;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,KAAK,EAAE,cAAc,EACrB,IAAI,GAAE,MAA4B,GACjC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAGzB;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,mBAAmB,EACzB,IAAI,GAAE,MAA4B,GACjC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAgB1C"}
@@ -1,61 +0,0 @@
1
- /**
2
- * Interactive-session wiring for the AgentHub channel plugin
3
- * (add-inbox-push-bridge §3). Builds the `agenthub-channel` MCP-server entry and
4
- * merges it idempotently into a project's MCP config, so a normal `claude`
5
- * session can opt in via `--channels server:agenthub-channel`.
6
- *
7
- * Pure + filesystem helpers are split so the merge logic is unit-testable.
8
- */
9
- import * as fs from "fs";
10
- export const CHANNEL_SERVER_NAME = "agenthub-channel";
11
- /**
12
- * The MCP-server entry that makes `server:agenthub-channel` resolvable. Runs the
13
- * published `agenthub-channel` bin via npx (same distribution as the main MCP),
14
- * carrying the agent's existing AgentHub creds + the kill switch.
15
- */
16
- export function channelMcpServerEntry(opts) {
17
- return {
18
- type: "stdio",
19
- command: "npx",
20
- args: ["-y", "-p", "agenthub-multiagent-mcp@latest", CHANNEL_SERVER_NAME],
21
- env: {
22
- AGENTHUB_URL: opts.url,
23
- AGENTHUB_API_KEY: opts.apiKey,
24
- AGENTHUB_AGENT_ID: opts.agentId,
25
- // §3.2 — the opt-in is safe even when disabled: the plugin no-ops unless
26
- // this is truthy (see channelEnabled / §2.1).
27
- AGENTHUB_INBOX_CHANNEL_ENABLED: opts.enabled === false ? "false" : "true",
28
- },
29
- };
30
- }
31
- /**
32
- * Idempotently set the channel server entry under `mcpServers`, preserving every
33
- * other server. Re-running with the same opts produces the same config.
34
- */
35
- export function mergeChannelServer(config, entry, name = CHANNEL_SERVER_NAME) {
36
- const existing = config.mcpServers || {};
37
- return { ...config, mcpServers: { ...existing, [name]: entry } };
38
- }
39
- /**
40
- * Read a JSON MCP-config file (or start from {} if absent/empty), merge the
41
- * channel entry, and write it back with 2-space indent. Returns whether the file
42
- * was newly created and whether the channel entry already matched (no-op).
43
- */
44
- export function applyChannelToConfigFile(filePath, opts, name = CHANNEL_SERVER_NAME) {
45
- let config = {};
46
- let created = true;
47
- if (fs.existsSync(filePath)) {
48
- created = false;
49
- const raw = fs.readFileSync(filePath, "utf-8").trim();
50
- if (raw) {
51
- config = JSON.parse(raw);
52
- }
53
- }
54
- const entry = channelMcpServerEntry(opts);
55
- const before = JSON.stringify(config.mcpServers?.[name]);
56
- const merged = mergeChannelServer(config, entry, name);
57
- const after = JSON.stringify(merged.mcpServers[name]);
58
- fs.writeFileSync(filePath, JSON.stringify(merged, null, 2));
59
- return { created, unchanged: before === after };
60
- }
61
- //# sourceMappingURL=channelSetup.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"channelSetup.js","sourceRoot":"","sources":["../src/channelSetup.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AAEzB,MAAM,CAAC,MAAM,mBAAmB,GAAG,kBAAkB,CAAC;AAkBtD;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAyB;IAC7D,OAAO;QACL,IAAI,EAAE,OAAO;QACb,OAAO,EAAE,KAAK;QACd,IAAI,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,gCAAgC,EAAE,mBAAmB,CAAC;QACzE,GAAG,EAAE;YACH,YAAY,EAAE,IAAI,CAAC,GAAG;YACtB,gBAAgB,EAAE,IAAI,CAAC,MAAM;YAC7B,iBAAiB,EAAE,IAAI,CAAC,OAAO;YAC/B,yEAAyE;YACzE,8CAA8C;YAC9C,8BAA8B,EAAE,IAAI,CAAC,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM;SAC1E;KACF,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAChC,MAA+B,EAC/B,KAAqB,EACrB,OAAe,mBAAmB;IAElC,MAAM,QAAQ,GAAI,MAAM,CAAC,UAAkD,IAAI,EAAE,CAAC;IAClF,OAAO,EAAE,GAAG,MAAM,EAAE,UAAU,EAAE,EAAE,GAAG,QAAQ,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;AACnE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,wBAAwB,CACtC,QAAgB,EAChB,IAAyB,EACzB,OAAe,mBAAmB;IAElC,IAAI,MAAM,GAA4B,EAAE,CAAC;IACzC,IAAI,OAAO,GAAG,IAAI,CAAC;IACnB,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5B,OAAO,GAAG,KAAK,CAAC;QAChB,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;QACtD,IAAI,GAAG,EAAE,CAAC;YACR,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA4B,CAAC;QACtD,CAAC;IACH,CAAC;IACD,MAAM,KAAK,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;IAC1C,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAE,MAAM,CAAC,UAAsC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IACtF,MAAM,MAAM,GAAG,kBAAkB,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;IACvD,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAE,MAAM,CAAC,UAAsC,CAAC,IAAI,CAAC,CAAC,CAAC;IACnF,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAC5D,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,KAAK,KAAK,EAAE,CAAC;AAClD,CAAC"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=channelSetup.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"channelSetup.test.d.ts","sourceRoot":"","sources":["../src/channelSetup.test.ts"],"names":[],"mappings":""}
@@ -1,75 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
- import * as fs from "fs";
3
- import * as os from "os";
4
- import * as path from "path";
5
- import { channelMcpServerEntry, mergeChannelServer, applyChannelToConfigFile, CHANNEL_SERVER_NAME, } from "./channelSetup.js";
6
- // add-inbox-push-bridge §3 — interactive-session wiring helpers.
7
- const OPTS = { url: "https://h.example.com", apiKey: "k123", agentId: "agent-x" };
8
- describe("channelMcpServerEntry", () => {
9
- it("builds a stdio npx entry carrying creds + opt-in", () => {
10
- const e = channelMcpServerEntry(OPTS);
11
- expect(e.type).toBe("stdio");
12
- expect(e.command).toBe("npx");
13
- expect(e.args).toContain(CHANNEL_SERVER_NAME);
14
- expect(e.env.AGENTHUB_URL).toBe("https://h.example.com");
15
- expect(e.env.AGENTHUB_API_KEY).toBe("k123");
16
- expect(e.env.AGENTHUB_AGENT_ID).toBe("agent-x");
17
- expect(e.env.AGENTHUB_INBOX_CHANNEL_ENABLED).toBe("true");
18
- });
19
- it("respects the kill switch when installed disabled (§3.2)", () => {
20
- const e = channelMcpServerEntry({ ...OPTS, enabled: false });
21
- expect(e.env.AGENTHUB_INBOX_CHANNEL_ENABLED).toBe("false");
22
- });
23
- });
24
- describe("mergeChannelServer", () => {
25
- it("adds the channel server while preserving existing servers", () => {
26
- const before = { mcpServers: { agenthub: { command: "npx" } }, other: 1 };
27
- const merged = mergeChannelServer(before, channelMcpServerEntry(OPTS));
28
- expect(merged.mcpServers.agenthub).toEqual({ command: "npx" }); // preserved
29
- expect(merged.mcpServers[CHANNEL_SERVER_NAME]).toBeDefined();
30
- expect(merged.other).toBe(1);
31
- });
32
- it("is idempotent — re-merging the same entry yields the same config", () => {
33
- const entry = channelMcpServerEntry(OPTS);
34
- const once = mergeChannelServer({}, entry);
35
- const twice = mergeChannelServer(once, entry);
36
- expect(JSON.stringify(twice)).toBe(JSON.stringify(once));
37
- });
38
- });
39
- describe("applyChannelToConfigFile", () => {
40
- let dir;
41
- let file;
42
- beforeEach(() => {
43
- dir = fs.mkdtempSync(path.join(os.tmpdir(), "channel-setup-"));
44
- file = path.join(dir, ".mcp.json");
45
- });
46
- afterEach(() => {
47
- fs.rmSync(dir, { recursive: true, force: true });
48
- });
49
- it("creates a config file when none exists", () => {
50
- const r = applyChannelToConfigFile(file, OPTS);
51
- expect(r.created).toBe(true);
52
- const written = JSON.parse(fs.readFileSync(file, "utf-8"));
53
- expect(written.mcpServers[CHANNEL_SERVER_NAME].env.AGENTHUB_AGENT_ID).toBe("agent-x");
54
- });
55
- it("merges into an existing config without clobbering other servers", () => {
56
- fs.writeFileSync(file, JSON.stringify({ mcpServers: { agenthub: { command: "npx", args: [] } } }));
57
- const r = applyChannelToConfigFile(file, OPTS);
58
- expect(r.created).toBe(false);
59
- const written = JSON.parse(fs.readFileSync(file, "utf-8"));
60
- expect(written.mcpServers.agenthub).toBeDefined(); // preserved
61
- expect(written.mcpServers[CHANNEL_SERVER_NAME]).toBeDefined();
62
- });
63
- it("is idempotent — second apply reports unchanged", () => {
64
- applyChannelToConfigFile(file, OPTS);
65
- const second = applyChannelToConfigFile(file, OPTS);
66
- expect(second.unchanged).toBe(true);
67
- });
68
- it("tolerates an empty file", () => {
69
- fs.writeFileSync(file, "");
70
- const r = applyChannelToConfigFile(file, OPTS);
71
- expect(r.created).toBe(false);
72
- expect(JSON.parse(fs.readFileSync(file, "utf-8")).mcpServers[CHANNEL_SERVER_NAME]).toBeDefined();
73
- });
74
- });
75
- //# sourceMappingURL=channelSetup.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"channelSetup.test.js","sourceRoot":"","sources":["../src/channelSetup.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACrE,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EACL,qBAAqB,EACrB,kBAAkB,EAClB,wBAAwB,EACxB,mBAAmB,GACpB,MAAM,mBAAmB,CAAC;AAE3B,iEAAiE;AAEjE,MAAM,IAAI,GAAG,EAAE,GAAG,EAAE,uBAAuB,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;AAElF,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;IACrC,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;QAC1D,MAAM,CAAC,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;QACtC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC7B,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC9B,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;QAC9C,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;QACzD,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC5C,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAChD,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;QACjE,MAAM,CAAC,GAAG,qBAAqB,CAAC,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;QAC7D,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;IAClC,EAAE,CAAC,2DAA2D,EAAE,GAAG,EAAE;QACnE,MAAM,MAAM,GAAG,EAAE,UAAU,EAAE,EAAE,QAAQ,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;QAC1E,MAAM,MAAM,GAAG,kBAAkB,CAAC,MAAM,EAAE,qBAAqB,CAAC,IAAI,CAAC,CAAC,CAAC;QACvE,MAAM,CAAE,MAAM,CAAC,UAAkB,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,YAAY;QACrF,MAAM,CAAE,MAAM,CAAC,UAAkB,CAAC,mBAAmB,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QACtE,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kEAAkE,EAAE,GAAG,EAAE;QAC1E,MAAM,KAAK,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;QAC1C,MAAM,IAAI,GAAG,kBAAkB,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QAC3C,MAAM,KAAK,GAAG,kBAAkB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAC9C,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,0BAA0B,EAAE,GAAG,EAAE;IACxC,IAAI,GAAW,CAAC;IAChB,IAAI,IAAY,CAAC;IAEjB,UAAU,CAAC,GAAG,EAAE;QACd,GAAG,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,gBAAgB,CAAC,CAAC,CAAC;QAC/D,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;IACH,SAAS,CAAC,GAAG,EAAE;QACb,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,CAAC,GAAG,wBAAwB,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC/C,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7B,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;QAC3D,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,mBAAmB,CAAC,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACxF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iEAAiE,EAAE,GAAG,EAAE;QACzE,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,UAAU,EAAE,EAAE,QAAQ,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;QACnG,MAAM,CAAC,GAAG,wBAAwB,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC/C,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC9B,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;QAC3D,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,YAAY;QAC/D,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,mBAAmB,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IAChE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACxD,wBAAwB,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACrC,MAAM,MAAM,GAAG,wBAAwB,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACpD,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yBAAyB,EAAE,GAAG,EAAE;QACjC,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAC3B,MAAM,CAAC,GAAG,wBAAwB,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC/C,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,mBAAmB,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IACnG,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}