@spinabot/brigade 1.1.0 → 1.2.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.
- package/convex/channels.d.ts +5 -5
- package/convex/schema.d.ts +2 -2
- package/dist/agents/agent-loop.d.ts.map +1 -1
- package/dist/agents/agent-loop.js +27 -4
- package/dist/agents/agent-loop.js.map +1 -1
- package/dist/agents/channels/approval-callback-codec.d.ts +107 -0
- package/dist/agents/channels/approval-callback-codec.d.ts.map +1 -0
- package/dist/agents/channels/approval-callback-codec.js +173 -0
- package/dist/agents/channels/approval-callback-codec.js.map +1 -0
- package/dist/agents/channels/approval-router.d.ts +77 -20
- package/dist/agents/channels/approval-router.d.ts.map +1 -1
- package/dist/agents/channels/approval-router.js +163 -37
- package/dist/agents/channels/approval-router.js.map +1 -1
- package/dist/agents/channels/backoff.d.ts +55 -0
- package/dist/agents/channels/backoff.d.ts.map +1 -0
- package/dist/agents/channels/backoff.js +47 -0
- package/dist/agents/channels/backoff.js.map +1 -0
- package/dist/agents/channels/channel-secrets.d.ts +45 -0
- package/dist/agents/channels/channel-secrets.d.ts.map +1 -0
- package/dist/agents/channels/channel-secrets.js +69 -0
- package/dist/agents/channels/channel-secrets.js.map +1 -0
- package/dist/agents/channels/inbound-pipeline.d.ts.map +1 -1
- package/dist/agents/channels/inbound-pipeline.js +67 -3
- package/dist/agents/channels/inbound-pipeline.js.map +1 -1
- package/dist/agents/channels/last-sent-message.d.ts +46 -0
- package/dist/agents/channels/last-sent-message.d.ts.map +1 -0
- package/dist/agents/channels/last-sent-message.js +55 -0
- package/dist/agents/channels/last-sent-message.js.map +1 -0
- package/dist/agents/channels/manager.d.ts +52 -0
- package/dist/agents/channels/manager.d.ts.map +1 -1
- package/dist/agents/channels/manager.js +141 -31
- package/dist/agents/channels/manager.js.map +1 -1
- package/dist/agents/channels/plugin-channel-manager-facade.d.ts +13 -2
- package/dist/agents/channels/plugin-channel-manager-facade.d.ts.map +1 -1
- package/dist/agents/channels/plugin-channel-manager-facade.js +21 -0
- package/dist/agents/channels/plugin-channel-manager-facade.js.map +1 -1
- package/dist/agents/channels/sdk.d.ts +426 -0
- package/dist/agents/channels/sdk.d.ts.map +1 -0
- package/dist/agents/channels/sdk.js +274 -0
- package/dist/agents/channels/sdk.js.map +1 -0
- package/dist/agents/channels/telegram/account-config.d.ts +92 -0
- package/dist/agents/channels/telegram/account-config.d.ts.map +1 -0
- package/dist/agents/channels/telegram/account-config.js +192 -0
- package/dist/agents/channels/telegram/account-config.js.map +1 -0
- package/dist/agents/channels/telegram/adapter.d.ts +79 -0
- package/dist/agents/channels/telegram/adapter.d.ts.map +1 -0
- package/dist/agents/channels/telegram/adapter.js +475 -0
- package/dist/agents/channels/telegram/adapter.js.map +1 -0
- package/dist/agents/channels/telegram/allowed-updates.d.ts +44 -0
- package/dist/agents/channels/telegram/allowed-updates.d.ts.map +1 -0
- package/dist/agents/channels/telegram/allowed-updates.js +52 -0
- package/dist/agents/channels/telegram/allowed-updates.js.map +1 -0
- package/dist/agents/channels/telegram/approval-authorize.d.ts +41 -0
- package/dist/agents/channels/telegram/approval-authorize.d.ts.map +1 -0
- package/dist/agents/channels/telegram/approval-authorize.js +69 -0
- package/dist/agents/channels/telegram/approval-authorize.js.map +1 -0
- package/dist/agents/channels/telegram/approval-native.d.ts +68 -0
- package/dist/agents/channels/telegram/approval-native.d.ts.map +1 -0
- package/dist/agents/channels/telegram/approval-native.js +94 -0
- package/dist/agents/channels/telegram/approval-native.js.map +1 -0
- package/dist/agents/channels/telegram/command-menu.d.ts +35 -0
- package/dist/agents/channels/telegram/command-menu.d.ts.map +1 -0
- package/dist/agents/channels/telegram/command-menu.js +59 -0
- package/dist/agents/channels/telegram/command-menu.js.map +1 -0
- package/dist/agents/channels/telegram/connection.d.ts +359 -0
- package/dist/agents/channels/telegram/connection.d.ts.map +1 -0
- package/dist/agents/channels/telegram/connection.js +865 -0
- package/dist/agents/channels/telegram/connection.js.map +1 -0
- package/dist/agents/channels/telegram/format.d.ts +48 -0
- package/dist/agents/channels/telegram/format.d.ts.map +1 -0
- package/dist/agents/channels/telegram/format.js +256 -0
- package/dist/agents/channels/telegram/format.js.map +1 -0
- package/dist/agents/channels/telegram/inbound-extras.d.ts +73 -0
- package/dist/agents/channels/telegram/inbound-extras.d.ts.map +1 -0
- package/dist/agents/channels/telegram/inbound-extras.js +231 -0
- package/dist/agents/channels/telegram/inbound-extras.js.map +1 -0
- package/dist/agents/channels/telegram/index.d.ts +14 -0
- package/dist/agents/channels/telegram/index.d.ts.map +1 -0
- package/dist/agents/channels/telegram/index.js +14 -0
- package/dist/agents/channels/telegram/index.js.map +1 -0
- package/dist/agents/channels/telegram/media.d.ts +68 -0
- package/dist/agents/channels/telegram/media.d.ts.map +1 -0
- package/dist/agents/channels/telegram/media.js +143 -0
- package/dist/agents/channels/telegram/media.js.map +1 -0
- package/dist/agents/channels/telegram/module.d.ts +15 -0
- package/dist/agents/channels/telegram/module.d.ts.map +1 -0
- package/dist/agents/channels/telegram/module.js +36 -0
- package/dist/agents/channels/telegram/module.js.map +1 -0
- package/dist/agents/channels/telegram/plugin.d.ts +76 -0
- package/dist/agents/channels/telegram/plugin.d.ts.map +1 -0
- package/dist/agents/channels/telegram/plugin.js +314 -0
- package/dist/agents/channels/telegram/plugin.js.map +1 -0
- package/dist/agents/channels/telegram/probe.d.ts +54 -0
- package/dist/agents/channels/telegram/probe.d.ts.map +1 -0
- package/dist/agents/channels/telegram/probe.js +95 -0
- package/dist/agents/channels/telegram/probe.js.map +1 -0
- package/dist/agents/channels/telegram/webhook.d.ts +55 -0
- package/dist/agents/channels/telegram/webhook.d.ts.map +1 -0
- package/dist/agents/channels/telegram/webhook.js +141 -0
- package/dist/agents/channels/telegram/webhook.js.map +1 -0
- package/dist/agents/extensions/modules/index.d.ts.map +1 -1
- package/dist/agents/extensions/modules/index.js +4 -0
- package/dist/agents/extensions/modules/index.js.map +1 -1
- package/dist/agents/extensions/types.d.ts +72 -2
- package/dist/agents/extensions/types.d.ts.map +1 -1
- package/dist/agents/extensions/types.js.map +1 -1
- package/dist/agents/tools/connect-channel-tool.d.ts +86 -0
- package/dist/agents/tools/connect-channel-tool.d.ts.map +1 -0
- package/dist/agents/tools/connect-channel-tool.js +398 -0
- package/dist/agents/tools/connect-channel-tool.js.map +1 -0
- package/dist/agents/tools/message-action-tool.d.ts +67 -0
- package/dist/agents/tools/message-action-tool.d.ts.map +1 -0
- package/dist/agents/tools/message-action-tool.js +216 -0
- package/dist/agents/tools/message-action-tool.js.map +1 -0
- package/dist/agents/tools/registry.d.ts.map +1 -1
- package/dist/agents/tools/registry.js +19 -0
- package/dist/agents/tools/registry.js.map +1 -1
- package/dist/buildstamp.json +1 -1
- package/dist/cli/commands/channels.d.ts.map +1 -1
- package/dist/cli/commands/channels.js +27 -2
- package/dist/cli/commands/channels.js.map +1 -1
- package/dist/core/server.d.ts.map +1 -1
- package/dist/core/server.js +77 -27
- package/dist/core/server.js.map +1 -1
- package/dist/cron/service/state.d.ts +10 -0
- package/dist/cron/service/state.d.ts.map +1 -1
- package/dist/cron/service/state.js.map +1 -1
- package/dist/cron/service/timer.d.ts.map +1 -1
- package/dist/cron/service/timer.js +43 -14
- package/dist/cron/service/timer.js.map +1 -1
- package/dist/cron/session-reaper.d.ts +27 -0
- package/dist/cron/session-reaper.d.ts.map +1 -1
- package/dist/cron/session-reaper.js +81 -0
- package/dist/cron/session-reaper.js.map +1 -1
- package/dist/system-prompt/assembler.d.ts +14 -0
- package/dist/system-prompt/assembler.d.ts.map +1 -1
- package/dist/system-prompt/assembler.js +36 -14
- package/dist/system-prompt/assembler.js.map +1 -1
- package/package.json +16 -3
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-agent last-sent-message registry.
|
|
3
|
+
*
|
|
4
|
+
* The additive `{ messageId }` return on `ChannelAdapter.sendText` / `sendMedia`
|
|
5
|
+
* lets the pipeline learn the native id of the message the agent just sent. This
|
|
6
|
+
* registry remembers that id per (agent, channel, conversation) so the agent can
|
|
7
|
+
* say "edit my last message" / "delete that" without having to have memorised an
|
|
8
|
+
* id — the `message_action` tool falls back to this record when no explicit
|
|
9
|
+
* `messageId` is supplied.
|
|
10
|
+
*
|
|
11
|
+
* Mirrors `last-channel.ts` exactly in scope + persistence: an in-memory map
|
|
12
|
+
* pinned via global-singleton (one record set across hot-reload / dual-build),
|
|
13
|
+
* updated whenever a reply send returns an id, read by `message_action`. NOT
|
|
14
|
+
* persisted across a gateway restart — the agent can always re-send and act on
|
|
15
|
+
* the fresh id; there is no correctness risk in losing the pin on reboot.
|
|
16
|
+
*/
|
|
17
|
+
import { resolveGlobalSingleton } from "../../shared/global-singleton.js";
|
|
18
|
+
/** Pinned via global-singleton so hot-reload / dual-build share one record map. */
|
|
19
|
+
const LAST_SENT_MESSAGE_KEY = Symbol.for("brigade.lastSentMessage.byKey");
|
|
20
|
+
const lastSentByKey = resolveGlobalSingleton(LAST_SENT_MESSAGE_KEY, () => new Map());
|
|
21
|
+
/** Key by (agent, channel, conversation) so each chat tracks its own last id. */
|
|
22
|
+
function recordKey(agentId, channelId, conversationId) {
|
|
23
|
+
return `${agentId}::${channelId}::${conversationId}`;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Record the id of the message the agent just sent on this conversation. A
|
|
27
|
+
* no-op when any of the keys (or the id itself) is empty — channels that don't
|
|
28
|
+
* return an id simply never populate the registry.
|
|
29
|
+
*/
|
|
30
|
+
export function recordLastSentMessage(args) {
|
|
31
|
+
if (!args.agentId || !args.channelId || !args.conversationId)
|
|
32
|
+
return;
|
|
33
|
+
const id = (args.messageId ?? "").trim();
|
|
34
|
+
if (!id)
|
|
35
|
+
return;
|
|
36
|
+
lastSentByKey.set(recordKey(args.agentId, args.channelId, args.conversationId), {
|
|
37
|
+
messageId: id,
|
|
38
|
+
...(args.threadId !== undefined ? { threadId: args.threadId } : {}),
|
|
39
|
+
...(args.accountId !== undefined ? { accountId: args.accountId } : {}),
|
|
40
|
+
updatedAtMs: args.nowMs ?? Date.now(),
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Look up the agent's most-recently-sent message id on a conversation, or
|
|
45
|
+
* `undefined` if none has been recorded (fresh boot, a channel that doesn't
|
|
46
|
+
* return ids, or no sends yet).
|
|
47
|
+
*/
|
|
48
|
+
export function getLastSentMessage(agentId, channelId, conversationId) {
|
|
49
|
+
return lastSentByKey.get(recordKey(agentId, channelId, conversationId));
|
|
50
|
+
}
|
|
51
|
+
/** Test-only — clear every recorded last-sent message. */
|
|
52
|
+
export function resetLastSentMessageRegistryForTests() {
|
|
53
|
+
lastSentByKey.clear();
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=last-sent-message.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"last-sent-message.js","sourceRoot":"","sources":["../../../src/agents/channels/last-sent-message.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;AAU1E,mFAAmF;AACnF,MAAM,qBAAqB,GAAG,MAAM,CAAC,GAAG,CAAC,+BAA+B,CAAC,CAAC;AAC1E,MAAM,aAAa,GAAG,sBAAsB,CAC3C,qBAAqB,EACrB,GAAG,EAAE,CAAC,IAAI,GAAG,EAAiC,CAC9C,CAAC;AAEF,iFAAiF;AACjF,SAAS,SAAS,CAAC,OAAe,EAAE,SAAiB,EAAE,cAAsB;IAC5E,OAAO,GAAG,OAAO,KAAK,SAAS,KAAK,cAAc,EAAE,CAAC;AACtD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAQrC;IACA,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,IAAI,CAAC,cAAc;QAAE,OAAO;IACrE,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACzC,IAAI,CAAC,EAAE;QAAE,OAAO;IAChB,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,cAAc,CAAC,EAAE;QAC/E,SAAS,EAAE,EAAE;QACb,GAAG,CAAC,IAAI,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACnE,GAAG,CAAC,IAAI,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACtE,WAAW,EAAE,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,GAAG,EAAE;KACrC,CAAC,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CACjC,OAAe,EACf,SAAiB,EACjB,cAAsB;IAEtB,OAAO,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,EAAE,SAAS,EAAE,cAAc,CAAC,CAAC,CAAC;AACzE,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,oCAAoC;IACnD,aAAa,CAAC,KAAK,EAAE,CAAC;AACvB,CAAC"}
|
|
@@ -13,6 +13,14 @@
|
|
|
13
13
|
* Failure isolation: a channel that fails to start is logged and skipped (the
|
|
14
14
|
* others still come up); an inbound message that throws is logged and dropped
|
|
15
15
|
* (the channel stays connected). Nothing here can crash the gateway.
|
|
16
|
+
*
|
|
17
|
+
* Runtime single-channel lifecycle (added for `connect_channel`): the manager
|
|
18
|
+
* captures its boot args + the FULL adapter catalog so a channel can be started
|
|
19
|
+
* or stopped LIVE — after boot, without a gateway restart — via `startChannel`
|
|
20
|
+
* / `stopChannel`. Each started adapter gets its OWN abort controller chained
|
|
21
|
+
* to the manager's master controller, so stopping ONE channel never signals the
|
|
22
|
+
* others (master `stop()` still cascades to all). WhatsApp's boot path does not
|
|
23
|
+
* call the new methods, so its behaviour is byte-identical to before.
|
|
16
24
|
*/
|
|
17
25
|
import type { BrigadeConfig } from "../../config/io.js";
|
|
18
26
|
import type { ChannelAdapter, ChannelCommand } from "../extensions/types.js";
|
|
@@ -50,6 +58,24 @@ export interface StartChannelsArgs {
|
|
|
50
58
|
value: string;
|
|
51
59
|
}) => void;
|
|
52
60
|
}
|
|
61
|
+
/** Outcome of a runtime single-channel start attempt. */
|
|
62
|
+
export interface StartChannelResult {
|
|
63
|
+
/** True when the adapter is now started (either freshly or already running). */
|
|
64
|
+
ok: boolean;
|
|
65
|
+
/** True when this call actually started it; false when it was already running. */
|
|
66
|
+
started: boolean;
|
|
67
|
+
/** Machine-readable reason when `ok` is false. */
|
|
68
|
+
reason?: "unknown-channel" | "env-missing" | "not-configured" | "start-failed";
|
|
69
|
+
/** Human-facing detail (safe to surface). */
|
|
70
|
+
message?: string;
|
|
71
|
+
}
|
|
72
|
+
/** Outcome of a runtime single-channel stop attempt. */
|
|
73
|
+
export interface StopChannelResult {
|
|
74
|
+
ok: boolean;
|
|
75
|
+
/** True when this call actually stopped a running adapter; false when it wasn't running. */
|
|
76
|
+
stopped: boolean;
|
|
77
|
+
message?: string;
|
|
78
|
+
}
|
|
53
79
|
export interface ChannelManager {
|
|
54
80
|
/** Ids of channels that started successfully. */
|
|
55
81
|
readonly started: string[];
|
|
@@ -66,11 +92,37 @@ export interface ChannelManager {
|
|
|
66
92
|
* accounts onto one adapter so the arg is a no-op there.
|
|
67
93
|
*/
|
|
68
94
|
adapter(id: string, accountId?: string): ChannelAdapter | undefined;
|
|
95
|
+
/**
|
|
96
|
+
* Start ONE channel adapter LIVE, after boot, without a gateway restart.
|
|
97
|
+
* The adapter is resolved from the FULL catalog the manager was built with
|
|
98
|
+
* (`startChannels({ adapters })`), gated the same way boot gates it
|
|
99
|
+
* (`requiresEnv` present + `adapter.isConfigured(config, env)`), then wired
|
|
100
|
+
* through the identical inbound pipeline. Idempotent: starting an
|
|
101
|
+
* already-running channel returns `{ ok: true, started: false }`.
|
|
102
|
+
*
|
|
103
|
+
* `config` (optional) overrides the snapshot the manager captured at boot —
|
|
104
|
+
* pass the freshly-written config when the caller has just enabled the
|
|
105
|
+
* channel + set its token via `mutateConfigAtomic`, so `isConfigured` and
|
|
106
|
+
* the adapter's `start()` see the new values without a manager rebuild.
|
|
107
|
+
*/
|
|
108
|
+
startChannel(id: string, config?: BrigadeConfig): Promise<StartChannelResult>;
|
|
109
|
+
/**
|
|
110
|
+
* Stop ONE channel adapter LIVE without touching the others. Aborts only
|
|
111
|
+
* that channel's listener (its own controller, chained to the master), drops
|
|
112
|
+
* its approval dispatcher, clears its pending debounce slots, and calls
|
|
113
|
+
* `adapter.stop()`. Idempotent: stopping a channel that isn't running
|
|
114
|
+
* returns `{ ok: true, stopped: false }`.
|
|
115
|
+
*/
|
|
116
|
+
stopChannel(id: string): Promise<StopChannelResult>;
|
|
69
117
|
}
|
|
70
118
|
/**
|
|
71
119
|
* Start every configured channel adapter. Returns a handle whose `stop()` tears
|
|
72
120
|
* them all down. Channels that aren't configured (missing keys/settings) are
|
|
73
121
|
* skipped silently — only configured channels spin up a listener.
|
|
122
|
+
*
|
|
123
|
+
* The returned manager also supports LIVE single-channel start/stop
|
|
124
|
+
* (`startChannel` / `stopChannel`) so a tool can connect a new channel after
|
|
125
|
+
* boot without a gateway restart.
|
|
74
126
|
*/
|
|
75
127
|
export declare function startChannels(args: StartChannelsArgs): Promise<ChannelManager>;
|
|
76
128
|
//# sourceMappingURL=manager.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../../src/agents/channels/manager.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../../src/agents/channels/manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAuC,MAAM,wBAAwB,CAAC;AAClH,OAAO,EACN,KAAK,oBAAoB,EAGzB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAIN,KAAK,iBAAiB,IAAI,yBAAyB,EAGnD,MAAM,uBAAuB,CAAC;AAI/B,mFAAmF;AACnF,MAAM,MAAM,iBAAiB,GAAG,yBAAyB,CAAC;AAE1D,MAAM,WAAW,iBAAiB;IACjC,8DAA8D;IAC9D,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,gFAAgF;IAChF,MAAM,EAAE,aAAa,CAAC;IACtB,4EAA4E;IAC5E,OAAO,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,OAAO,EAAE,CAAC,IAAI,EAAE;QACf,IAAI,EAAE,MAAM,CAAC;QACb,UAAU,EAAE,MAAM,CAAC;QACnB,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,WAAW,CAAC;QACrB,aAAa,CAAC,EAAE,OAAO,CAAC;QACxB,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;KAC5C,KAAK,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACjC,yDAAyD;IACzD,QAAQ,CAAC,EAAE,cAAc,EAAE,CAAC;IAC5B,gEAAgE;IAChE,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,8EAA8E;IAC9E,SAAS,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE;QAAE,IAAI,EAAE,IAAI,GAAG,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CACtF;AAED,yDAAyD;AACzD,MAAM,WAAW,kBAAkB;IAClC,gFAAgF;IAChF,EAAE,EAAE,OAAO,CAAC;IACZ,kFAAkF;IAClF,OAAO,EAAE,OAAO,CAAC;IACjB,kDAAkD;IAClD,MAAM,CAAC,EAAE,iBAAiB,GAAG,aAAa,GAAG,gBAAgB,GAAG,cAAc,CAAC;IAC/E,6CAA6C;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wDAAwD;AACxD,MAAM,WAAW,iBAAiB;IACjC,EAAE,EAAE,OAAO,CAAC;IACZ,4FAA4F;IAC5F,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC9B,iDAAiD;IACjD,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAC3B,sEAAsE;IACtE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAAC;IACpE;;;;;;;;;;;;OAYG;IACH,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAC9E;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;CACpD;AAWD;;;;;;;;GAQG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,cAAc,CAAC,CA4MpF"}
|
|
@@ -13,6 +13,14 @@
|
|
|
13
13
|
* Failure isolation: a channel that fails to start is logged and skipped (the
|
|
14
14
|
* others still come up); an inbound message that throws is logged and dropped
|
|
15
15
|
* (the channel stays connected). Nothing here can crash the gateway.
|
|
16
|
+
*
|
|
17
|
+
* Runtime single-channel lifecycle (added for `connect_channel`): the manager
|
|
18
|
+
* captures its boot args + the FULL adapter catalog so a channel can be started
|
|
19
|
+
* or stopped LIVE — after boot, without a gateway restart — via `startChannel`
|
|
20
|
+
* / `stopChannel`. Each started adapter gets its OWN abort controller chained
|
|
21
|
+
* to the manager's master controller, so stopping ONE channel never signals the
|
|
22
|
+
* others (master `stop()` still cascades to all). WhatsApp's boot path does not
|
|
23
|
+
* call the new methods, so its behaviour is byte-identical to before.
|
|
16
24
|
*/
|
|
17
25
|
import { createSubsystemLogger } from "../../logging/subsystem-logger.js";
|
|
18
26
|
import { registerChannelApprovalDispatcher, removeChannelApprovalDispatcher, } from "./approval-router.js";
|
|
@@ -22,33 +30,70 @@ const log = createSubsystemLogger("channels/manager");
|
|
|
22
30
|
* Start every configured channel adapter. Returns a handle whose `stop()` tears
|
|
23
31
|
* them all down. Channels that aren't configured (missing keys/settings) are
|
|
24
32
|
* skipped silently — only configured channels spin up a listener.
|
|
33
|
+
*
|
|
34
|
+
* The returned manager also supports LIVE single-channel start/stop
|
|
35
|
+
* (`startChannel` / `stopChannel`) so a tool can connect a new channel after
|
|
36
|
+
* boot without a gateway restart.
|
|
25
37
|
*/
|
|
26
38
|
export async function startChannels(args) {
|
|
27
39
|
const env = args.env ?? process.env;
|
|
28
|
-
|
|
40
|
+
// Master controller — aborting it cascades to EVERY per-channel controller
|
|
41
|
+
// (they're chained below). `stop()` (all channels) aborts the master.
|
|
42
|
+
const master = new AbortController();
|
|
29
43
|
const started = [];
|
|
30
44
|
const userCommands = args.commands ?? [];
|
|
31
|
-
|
|
45
|
+
// Mutable config snapshot — boot value, refreshable by a live `startChannel`
|
|
46
|
+
// that passes the just-written config so a late-started adapter sees it.
|
|
47
|
+
let activeConfig = args.config;
|
|
48
|
+
/**
|
|
49
|
+
* Start ONE adapter and register it in `started`. Shared by the boot loop
|
|
50
|
+
* and the runtime `startChannel` path so the gate + pipeline wiring + abort
|
|
51
|
+
* chaining + dispatcher registration stay byte-identical on both paths.
|
|
52
|
+
*
|
|
53
|
+
* Returns a structured result. Throwing is reserved for genuinely
|
|
54
|
+
* unexpected failures; a configured-but-failed start is captured as
|
|
55
|
+
* `{ ok:false, reason:"start-failed" }` (logged + skipped, never crashes).
|
|
56
|
+
*/
|
|
57
|
+
async function startOneAdapter(adapter) {
|
|
58
|
+
// Already running → no-op (idempotent).
|
|
59
|
+
if (started.some((s) => s.id === adapter.id)) {
|
|
60
|
+
return { ok: true, started: false, message: `channel "${adapter.id}" already started` };
|
|
61
|
+
}
|
|
32
62
|
// Gate: required env present AND the adapter says it's configured.
|
|
33
63
|
const envMissing = adapter.requiresEnv?.some((v) => !env[v] || env[v]?.trim() === "");
|
|
34
64
|
if (envMissing) {
|
|
35
65
|
log.info("channel skipped — required env missing", { channel: adapter.id, requiresEnv: adapter.requiresEnv });
|
|
36
|
-
|
|
66
|
+
return {
|
|
67
|
+
ok: false,
|
|
68
|
+
started: false,
|
|
69
|
+
reason: "env-missing",
|
|
70
|
+
message: `channel "${adapter.id}" is missing required env (${(adapter.requiresEnv ?? []).join(", ")})`,
|
|
71
|
+
};
|
|
37
72
|
}
|
|
38
73
|
let configured = false;
|
|
39
74
|
try {
|
|
40
|
-
configured = adapter.isConfigured(
|
|
75
|
+
configured = adapter.isConfigured(activeConfig, env);
|
|
41
76
|
}
|
|
42
77
|
catch (err) {
|
|
43
78
|
log.warn("channel isConfigured threw — skipping", {
|
|
44
79
|
channel: adapter.id,
|
|
45
80
|
error: err instanceof Error ? err.message : String(err),
|
|
46
81
|
});
|
|
47
|
-
|
|
82
|
+
return {
|
|
83
|
+
ok: false,
|
|
84
|
+
started: false,
|
|
85
|
+
reason: "not-configured",
|
|
86
|
+
message: `channel "${adapter.id}" configuration check failed`,
|
|
87
|
+
};
|
|
48
88
|
}
|
|
49
89
|
if (!configured) {
|
|
50
90
|
log.info("channel skipped — not configured", { channel: adapter.id });
|
|
51
|
-
|
|
91
|
+
return {
|
|
92
|
+
ok: false,
|
|
93
|
+
started: false,
|
|
94
|
+
reason: "not-configured",
|
|
95
|
+
message: `channel "${adapter.id}" is not configured (enable it + provide credentials)`,
|
|
96
|
+
};
|
|
52
97
|
}
|
|
53
98
|
// Per-adapter command map: user-registered + bundled `/help` `/status` `/allowlist`.
|
|
54
99
|
const commandMap = new Map();
|
|
@@ -56,19 +101,28 @@ export async function startChannels(args) {
|
|
|
56
101
|
commandMap.set(c.name.toLowerCase(), c);
|
|
57
102
|
for (const c of buildBundledCommands(adapter))
|
|
58
103
|
commandMap.set(c.name.toLowerCase(), c);
|
|
104
|
+
// Per-channel abort controller, chained to the master so an all-channels
|
|
105
|
+
// `stop()` still cancels this channel, but a single-channel `stopChannel`
|
|
106
|
+
// can cancel ONLY this one without signalling the others.
|
|
107
|
+
const channelAbort = new AbortController();
|
|
108
|
+
const onMasterAbort = () => channelAbort.abort();
|
|
109
|
+
if (master.signal.aborted)
|
|
110
|
+
channelAbort.abort();
|
|
111
|
+
else
|
|
112
|
+
master.signal.addEventListener("abort", onMasterAbort, { once: true });
|
|
59
113
|
// Adapt the boot-args `runTurn` into the pipeline's `RunChannelTurnFn`
|
|
60
114
|
// shape — same payload, additive optional fields the pipeline reads.
|
|
61
115
|
const pipelineRunTurn = (turn) => args.runTurn(turn);
|
|
62
116
|
const pipeline = createInboundPipelineContext({
|
|
63
117
|
adapter,
|
|
64
|
-
config:
|
|
118
|
+
config: activeConfig,
|
|
65
119
|
agentId: args.agentId,
|
|
66
120
|
runTurn: pipelineRunTurn,
|
|
67
121
|
commandMap,
|
|
68
|
-
parentAbort:
|
|
122
|
+
parentAbort: channelAbort.signal,
|
|
69
123
|
});
|
|
70
124
|
const ctx = {
|
|
71
|
-
signal:
|
|
125
|
+
signal: channelAbort.signal,
|
|
72
126
|
log: (msg, meta) => log.info(`[${adapter.id}] ${msg}`, meta),
|
|
73
127
|
onPairing: args.onPairing ? (info) => args.onPairing?.(adapter.id, info) : undefined,
|
|
74
128
|
onInbound: async (msg) => {
|
|
@@ -77,7 +131,7 @@ export async function startChannels(args) {
|
|
|
77
131
|
};
|
|
78
132
|
try {
|
|
79
133
|
await adapter.start(ctx);
|
|
80
|
-
started.push({ id: adapter.id, adapter, pipeline });
|
|
134
|
+
started.push({ id: adapter.id, adapter, pipeline, abort: channelAbort });
|
|
81
135
|
// Register the adapter's outbound surface so a gated tool call inside
|
|
82
136
|
// a channel-routed turn surfaces the prompt INTO this conversation.
|
|
83
137
|
// Single-account adapters land on the default-account dispatcher slot.
|
|
@@ -86,45 +140,101 @@ export async function startChannels(args) {
|
|
|
86
140
|
prettyName: adapter.label,
|
|
87
141
|
});
|
|
88
142
|
log.info("channel started", { channel: adapter.id, label: adapter.label });
|
|
143
|
+
return { ok: true, started: true, message: `channel "${adapter.id}" started` };
|
|
89
144
|
}
|
|
90
145
|
catch (err) {
|
|
146
|
+
// Clean up the abort chaining we set up before the failed start.
|
|
147
|
+
master.signal.removeEventListener("abort", onMasterAbort);
|
|
91
148
|
log.warn("channel failed to start — skipping", {
|
|
92
149
|
channel: adapter.id,
|
|
93
150
|
error: err instanceof Error ? err.message : String(err),
|
|
94
151
|
});
|
|
152
|
+
return {
|
|
153
|
+
ok: false,
|
|
154
|
+
started: false,
|
|
155
|
+
reason: "start-failed",
|
|
156
|
+
message: `channel "${adapter.id}" failed to start: ${err instanceof Error ? err.message : String(err)}`,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
/** Tear down ONE started entry: dispatcher, debounce slots, abort, stop(). */
|
|
161
|
+
async function teardownEntry(entry) {
|
|
162
|
+
// Drop the approval router's dispatcher BEFORE adapter.stop() so an
|
|
163
|
+
// in-flight bridge can't ask a torn-down channel to send.
|
|
164
|
+
removeChannelApprovalDispatcher(entry.id);
|
|
165
|
+
// Cancel pending debounce slots so a flush can't fire post-stop.
|
|
166
|
+
for (const slot of entry.pipeline.pendingDispatches.values())
|
|
167
|
+
clearTimeout(slot.timer);
|
|
168
|
+
entry.pipeline.pendingDispatches.clear();
|
|
169
|
+
// Abort only THIS channel's listener.
|
|
170
|
+
entry.abort.abort();
|
|
171
|
+
try {
|
|
172
|
+
await entry.adapter.stop();
|
|
173
|
+
}
|
|
174
|
+
catch (err) {
|
|
175
|
+
log.warn("channel stop failed", {
|
|
176
|
+
channel: entry.id,
|
|
177
|
+
error: err instanceof Error ? err.message : String(err),
|
|
178
|
+
});
|
|
95
179
|
}
|
|
96
180
|
}
|
|
181
|
+
// Boot loop — start every configured adapter (same gate + skip-on-failure
|
|
182
|
+
// semantics as before; the per-adapter body now lives in startOneAdapter).
|
|
183
|
+
for (const adapter of args.adapters) {
|
|
184
|
+
await startOneAdapter(adapter);
|
|
185
|
+
}
|
|
97
186
|
let stopped = false;
|
|
98
187
|
return {
|
|
99
|
-
|
|
188
|
+
// Live getter — reflects runtime single-channel start/stop, not a
|
|
189
|
+
// boot-time snapshot (startChannel / stopChannel mutate `started`).
|
|
190
|
+
get started() {
|
|
191
|
+
return started.map((s) => s.id);
|
|
192
|
+
},
|
|
100
193
|
adapter(id) {
|
|
101
194
|
const entry = started.find((s) => s.id === id);
|
|
102
195
|
return entry?.adapter;
|
|
103
196
|
},
|
|
197
|
+
async startChannel(id, config) {
|
|
198
|
+
if (stopped) {
|
|
199
|
+
return { ok: false, started: false, reason: "start-failed", message: "channel manager is stopped" };
|
|
200
|
+
}
|
|
201
|
+
// Refresh the config snapshot when the caller just wrote new values
|
|
202
|
+
// (enabled the channel + set its token) so the gate + start see them.
|
|
203
|
+
if (config)
|
|
204
|
+
activeConfig = config;
|
|
205
|
+
const adapter = args.adapters.find((a) => a.id === id);
|
|
206
|
+
if (!adapter) {
|
|
207
|
+
return {
|
|
208
|
+
ok: false,
|
|
209
|
+
started: false,
|
|
210
|
+
reason: "unknown-channel",
|
|
211
|
+
message: `no channel adapter registered with id "${id}"`,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
return startOneAdapter(adapter);
|
|
215
|
+
},
|
|
216
|
+
async stopChannel(id) {
|
|
217
|
+
const idx = started.findIndex((s) => s.id === id);
|
|
218
|
+
if (idx === -1) {
|
|
219
|
+
return { ok: true, stopped: false, message: `channel "${id}" is not running` };
|
|
220
|
+
}
|
|
221
|
+
const entry = started[idx];
|
|
222
|
+
// Remove from `started` FIRST so a concurrent read of `.started`
|
|
223
|
+
// doesn't see a channel that's mid-teardown.
|
|
224
|
+
started.splice(idx, 1);
|
|
225
|
+
await teardownEntry(entry);
|
|
226
|
+
return { ok: true, stopped: true, message: `channel "${id}" stopped` };
|
|
227
|
+
},
|
|
104
228
|
async stop() {
|
|
105
229
|
if (stopped)
|
|
106
230
|
return;
|
|
107
231
|
stopped = true;
|
|
108
|
-
//
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
abort.abort();
|
|
115
|
-
for (const { id, adapter } of started) {
|
|
116
|
-
// Drop the approval router's dispatcher BEFORE adapter.stop()
|
|
117
|
-
// so an in-flight bridge can't ask a torn-down channel to send.
|
|
118
|
-
removeChannelApprovalDispatcher(id);
|
|
119
|
-
try {
|
|
120
|
-
await adapter.stop();
|
|
121
|
-
}
|
|
122
|
-
catch (err) {
|
|
123
|
-
log.warn("channel stop failed", {
|
|
124
|
-
channel: id,
|
|
125
|
-
error: err instanceof Error ? err.message : String(err),
|
|
126
|
-
});
|
|
127
|
-
}
|
|
232
|
+
// Abort the master FIRST — cascades to every per-channel controller.
|
|
233
|
+
master.abort();
|
|
234
|
+
// Snapshot + clear so teardownEntry's own splice-free path is safe.
|
|
235
|
+
const entries = started.splice(0, started.length);
|
|
236
|
+
for (const entry of entries) {
|
|
237
|
+
await teardownEntry(entry);
|
|
128
238
|
}
|
|
129
239
|
},
|
|
130
240
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manager.js","sourceRoot":"","sources":["../../../src/agents/channels/manager.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"manager.js","sourceRoot":"","sources":["../../../src/agents/channels/manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,10 +9,21 @@
|
|
|
9
9
|
* (back-compat for callers that don't yet pass accountId). Single-account
|
|
10
10
|
* installs return their one account either way.
|
|
11
11
|
*/
|
|
12
|
+
import type { ChannelAdapter } from "../extensions/types.js";
|
|
12
13
|
import type { ChannelManager } from "./manager.js";
|
|
13
|
-
|
|
14
|
+
/**
|
|
15
|
+
* The minimal per-account introspection surface the facade needs from a plugin
|
|
16
|
+
* handle — `id` + which accounts are running + a per-account adapter lookup.
|
|
17
|
+
* Both `WhatsAppPluginHandle` and `TelegramPluginHandle` satisfy this, so the
|
|
18
|
+
* facade is channel-agnostic and one facade can front a mixed plugin list.
|
|
19
|
+
*/
|
|
20
|
+
export interface PluginChannelManagerHandle {
|
|
21
|
+
id: string;
|
|
22
|
+
startedAccountIds(): string[];
|
|
23
|
+
getAdapter(accountId: string): ChannelAdapter | undefined;
|
|
24
|
+
}
|
|
14
25
|
/** Build a manager facade over a list of plugin handles. */
|
|
15
26
|
export declare function createPluginChannelManagerFacade(args: {
|
|
16
|
-
plugins:
|
|
27
|
+
plugins: PluginChannelManagerHandle[];
|
|
17
28
|
}): ChannelManager;
|
|
18
29
|
//# sourceMappingURL=plugin-channel-manager-facade.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin-channel-manager-facade.d.ts","sourceRoot":"","sources":["../../../src/agents/channels/plugin-channel-manager-facade.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;
|
|
1
|
+
{"version":3,"file":"plugin-channel-manager-facade.d.ts","sourceRoot":"","sources":["../../../src/agents/channels/plugin-channel-manager-facade.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAE,cAAc,EAAyC,MAAM,cAAc,CAAC;AAE1F;;;;;GAKG;AACH,MAAM,WAAW,0BAA0B;IAC1C,EAAE,EAAE,MAAM,CAAC;IACX,iBAAiB,IAAI,MAAM,EAAE,CAAC;IAC9B,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAAC;CAC1D;AAED,4DAA4D;AAC5D,wBAAgB,gCAAgC,CAAC,IAAI,EAAE;IACtD,OAAO,EAAE,0BAA0B,EAAE,CAAC;CACtC,GAAG,cAAc,CAsDjB"}
|
|
@@ -47,6 +47,27 @@ export function createPluginChannelManagerFacade(args) {
|
|
|
47
47
|
// Stop is owned by the `ChannelPluginManager` directly; the facade
|
|
48
48
|
// is a read-only surface for the `send_message` tool.
|
|
49
49
|
},
|
|
50
|
+
async startChannel(id) {
|
|
51
|
+
// The multi-account plugin path owns its own lifecycle through the
|
|
52
|
+
// `ChannelPluginManager` (per-account loops + restart-backoff), so a
|
|
53
|
+
// live single-channel start does NOT route through this read-only
|
|
54
|
+
// facade. Report it honestly so `connect_channel` falls back to
|
|
55
|
+
// "config written — restart the gateway to connect".
|
|
56
|
+
return {
|
|
57
|
+
ok: false,
|
|
58
|
+
started: false,
|
|
59
|
+
reason: "start-failed",
|
|
60
|
+
message: `live start for "${id}" is managed by the multi-account channel plugin manager — restart the gateway to apply config changes`,
|
|
61
|
+
};
|
|
62
|
+
},
|
|
63
|
+
async stopChannel(id) {
|
|
64
|
+
// Same ownership note as startChannel — the plugin manager owns stop.
|
|
65
|
+
return {
|
|
66
|
+
ok: true,
|
|
67
|
+
stopped: false,
|
|
68
|
+
message: `live stop for "${id}" is managed by the multi-account channel plugin manager`,
|
|
69
|
+
};
|
|
70
|
+
},
|
|
50
71
|
};
|
|
51
72
|
}
|
|
52
73
|
//# sourceMappingURL=plugin-channel-manager-facade.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin-channel-manager-facade.js","sourceRoot":"","sources":["../../../src/agents/channels/plugin-channel-manager-facade.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;
|
|
1
|
+
{"version":3,"file":"plugin-channel-manager-facade.js","sourceRoot":"","sources":["../../../src/agents/channels/plugin-channel-manager-facade.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAiBH,4DAA4D;AAC5D,MAAM,UAAU,gCAAgC,CAAC,IAEhD;IACA,MAAM,OAAO,GAAG,GAAa,EAAE;QAC9B,MAAM,GAAG,GAAa,EAAE,CAAC;QACzB,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAC9B,IAAI,CAAC,CAAC,iBAAiB,EAAE,CAAC,MAAM,GAAG,CAAC;gBAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACtD,CAAC;QACD,OAAO,GAAG,CAAC;IACZ,CAAC,CAAC;IACF,OAAO;QACN,IAAI,OAAO;YACV,OAAO,OAAO,EAAE,CAAC;QAClB,CAAC;QACD,OAAO,CAAC,EAAU,EAAE,SAAkB;YACrC,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;gBAC9B,IAAI,CAAC,CAAC,EAAE,KAAK,EAAE;oBAAE,SAAS;gBAC1B,MAAM,QAAQ,GAAG,CAAC,CAAC,iBAAiB,EAAE,CAAC;gBACvC,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;oBAC7B,MAAM,UAAU,GAAG,SAAS,CAAC,IAAI,EAAE,CAAC;oBACpC,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;wBAAE,OAAO,SAAS,CAAC;oBAC9C,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,UAAU,CAAC;wBAAE,OAAO,SAAS,CAAC;oBACrD,OAAO,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;gBACjC,CAAC;gBACD,MAAM,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;gBAC1B,IAAI,CAAC,KAAK;oBAAE,OAAO,SAAS,CAAC;gBAC7B,OAAO,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;YAC5B,CAAC;YACD,OAAO,SAAS,CAAC;QAClB,CAAC;QACD,KAAK,CAAC,IAAI;YACT,mEAAmE;YACnE,sDAAsD;QACvD,CAAC;QACD,KAAK,CAAC,YAAY,CAAC,EAAU;YAC5B,mEAAmE;YACnE,qEAAqE;YACrE,kEAAkE;YAClE,gEAAgE;YAChE,qDAAqD;YACrD,OAAO;gBACN,EAAE,EAAE,KAAK;gBACT,OAAO,EAAE,KAAK;gBACd,MAAM,EAAE,cAAc;gBACtB,OAAO,EAAE,mBAAmB,EAAE,wGAAwG;aACtI,CAAC;QACH,CAAC;QACD,KAAK,CAAC,WAAW,CAAC,EAAU;YAC3B,sEAAsE;YACtE,OAAO;gBACN,EAAE,EAAE,IAAI;gBACR,OAAO,EAAE,KAAK;gBACd,OAAO,EAAE,kBAAkB,EAAE,0DAA0D;aACvF,CAAC;QACH,CAAC;KACD,CAAC;AACH,CAAC"}
|