@spinabot/brigade 1.3.2 → 1.4.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 (225) hide show
  1. package/README.md +54 -10
  2. package/convex/config.d.ts +2 -2
  3. package/convex/extensions.d.ts +2 -2
  4. package/convex/logs.d.ts +2 -2
  5. package/convex/memory.d.ts +7 -7
  6. package/convex/messages.d.ts +4 -4
  7. package/convex/schema.d.ts +17 -17
  8. package/convex/subagents.d.ts +12 -12
  9. package/dist/agents/agent-loop.d.ts +1 -0
  10. package/dist/agents/agent-loop.d.ts.map +1 -1
  11. package/dist/agents/agent-loop.js +1 -1
  12. package/dist/agents/agent-loop.js.map +1 -1
  13. package/dist/agents/channels/access-control/format-allow-from.d.ts +50 -0
  14. package/dist/agents/channels/access-control/format-allow-from.d.ts.map +1 -0
  15. package/dist/agents/channels/access-control/format-allow-from.js +64 -0
  16. package/dist/agents/channels/access-control/format-allow-from.js.map +1 -0
  17. package/dist/agents/channels/access-control/index.d.ts +2 -1
  18. package/dist/agents/channels/access-control/index.d.ts.map +1 -1
  19. package/dist/agents/channels/access-control/index.js +2 -1
  20. package/dist/agents/channels/access-control/index.js.map +1 -1
  21. package/dist/agents/channels/access-control/store.d.ts +15 -0
  22. package/dist/agents/channels/access-control/store.d.ts.map +1 -1
  23. package/dist/agents/channels/access-control/store.js +44 -1
  24. package/dist/agents/channels/access-control/store.js.map +1 -1
  25. package/dist/agents/channels/bundled-channel-metas.d.ts +26 -0
  26. package/dist/agents/channels/bundled-channel-metas.d.ts.map +1 -0
  27. package/dist/agents/channels/bundled-channel-metas.js +44 -0
  28. package/dist/agents/channels/bundled-channel-metas.js.map +1 -0
  29. package/dist/agents/channels/channel-messaging-registry.d.ts +130 -0
  30. package/dist/agents/channels/channel-messaging-registry.d.ts.map +1 -0
  31. package/dist/agents/channels/channel-messaging-registry.js +211 -0
  32. package/dist/agents/channels/channel-messaging-registry.js.map +1 -0
  33. package/dist/agents/channels/channel-meta-registry.d.ts +60 -0
  34. package/dist/agents/channels/channel-meta-registry.d.ts.map +1 -0
  35. package/dist/agents/channels/channel-meta-registry.js +128 -0
  36. package/dist/agents/channels/channel-meta-registry.js.map +1 -0
  37. package/dist/agents/channels/channel-security-registry.d.ts +138 -0
  38. package/dist/agents/channels/channel-security-registry.d.ts.map +1 -0
  39. package/dist/agents/channels/channel-security-registry.js +265 -0
  40. package/dist/agents/channels/channel-security-registry.js.map +1 -0
  41. package/dist/agents/channels/exposure.d.ts +44 -0
  42. package/dist/agents/channels/exposure.d.ts.map +1 -0
  43. package/dist/agents/channels/exposure.js +48 -0
  44. package/dist/agents/channels/exposure.js.map +1 -0
  45. package/dist/agents/channels/general-callback.d.ts +25 -0
  46. package/dist/agents/channels/general-callback.d.ts.map +1 -0
  47. package/dist/agents/channels/general-callback.js +31 -0
  48. package/dist/agents/channels/general-callback.js.map +1 -0
  49. package/dist/agents/channels/inbound-pipeline.d.ts +9 -0
  50. package/dist/agents/channels/inbound-pipeline.d.ts.map +1 -1
  51. package/dist/agents/channels/inbound-pipeline.js +429 -39
  52. package/dist/agents/channels/inbound-pipeline.js.map +1 -1
  53. package/dist/agents/channels/markdown-capability.d.ts +44 -0
  54. package/dist/agents/channels/markdown-capability.d.ts.map +1 -0
  55. package/dist/agents/channels/markdown-capability.js +66 -0
  56. package/dist/agents/channels/markdown-capability.js.map +1 -0
  57. package/dist/agents/channels/sdk.d.ts +170 -10
  58. package/dist/agents/channels/sdk.d.ts.map +1 -1
  59. package/dist/agents/channels/sdk.js +138 -6
  60. package/dist/agents/channels/sdk.js.map +1 -1
  61. package/dist/agents/channels/telegram/account-config.d.ts +41 -0
  62. package/dist/agents/channels/telegram/account-config.d.ts.map +1 -1
  63. package/dist/agents/channels/telegram/account-config.js +79 -0
  64. package/dist/agents/channels/telegram/account-config.js.map +1 -1
  65. package/dist/agents/channels/telegram/adapter.d.ts +6 -0
  66. package/dist/agents/channels/telegram/adapter.d.ts.map +1 -1
  67. package/dist/agents/channels/telegram/adapter.js +178 -6
  68. package/dist/agents/channels/telegram/adapter.js.map +1 -1
  69. package/dist/agents/channels/telegram/allowed-updates.d.ts +14 -5
  70. package/dist/agents/channels/telegram/allowed-updates.d.ts.map +1 -1
  71. package/dist/agents/channels/telegram/allowed-updates.js +8 -4
  72. package/dist/agents/channels/telegram/allowed-updates.js.map +1 -1
  73. package/dist/agents/channels/telegram/connection.d.ts +108 -1
  74. package/dist/agents/channels/telegram/connection.d.ts.map +1 -1
  75. package/dist/agents/channels/telegram/connection.js +219 -3
  76. package/dist/agents/channels/telegram/connection.js.map +1 -1
  77. package/dist/agents/channels/telegram/draft-stream.d.ts +98 -0
  78. package/dist/agents/channels/telegram/draft-stream.d.ts.map +1 -0
  79. package/dist/agents/channels/telegram/draft-stream.js +222 -0
  80. package/dist/agents/channels/telegram/draft-stream.js.map +1 -0
  81. package/dist/agents/channels/telegram/inbound-extras.d.ts +10 -1
  82. package/dist/agents/channels/telegram/inbound-extras.d.ts.map +1 -1
  83. package/dist/agents/channels/telegram/inbound-extras.js +66 -0
  84. package/dist/agents/channels/telegram/inbound-extras.js.map +1 -1
  85. package/dist/agents/channels/telegram/inline-keyboard.d.ts +36 -0
  86. package/dist/agents/channels/telegram/inline-keyboard.d.ts.map +1 -0
  87. package/dist/agents/channels/telegram/inline-keyboard.js +62 -0
  88. package/dist/agents/channels/telegram/inline-keyboard.js.map +1 -0
  89. package/dist/agents/channels/telegram/plugin.d.ts.map +1 -1
  90. package/dist/agents/channels/telegram/plugin.js +7 -11
  91. package/dist/agents/channels/telegram/plugin.js.map +1 -1
  92. package/dist/agents/channels/telegram/reasoning-lane.d.ts +41 -0
  93. package/dist/agents/channels/telegram/reasoning-lane.d.ts.map +1 -0
  94. package/dist/agents/channels/telegram/reasoning-lane.js +67 -0
  95. package/dist/agents/channels/telegram/reasoning-lane.js.map +1 -0
  96. package/dist/agents/channels/telegram/socks-dispatcher.d.ts +32 -0
  97. package/dist/agents/channels/telegram/socks-dispatcher.d.ts.map +1 -0
  98. package/dist/agents/channels/telegram/socks-dispatcher.js +97 -0
  99. package/dist/agents/channels/telegram/socks-dispatcher.js.map +1 -0
  100. package/dist/agents/channels/types.adapters.d.ts +137 -4
  101. package/dist/agents/channels/types.adapters.d.ts.map +1 -1
  102. package/dist/agents/channels/types.adapters.js +2 -2
  103. package/dist/agents/channels/types.core.d.ts +26 -2
  104. package/dist/agents/channels/types.core.d.ts.map +1 -1
  105. package/dist/agents/channels/types.plugin.d.ts +25 -7
  106. package/dist/agents/channels/types.plugin.d.ts.map +1 -1
  107. package/dist/agents/channels/types.plugin.js +6 -5
  108. package/dist/agents/channels/types.plugin.js.map +1 -1
  109. package/dist/agents/channels/whatsapp/adapter.d.ts +8 -0
  110. package/dist/agents/channels/whatsapp/adapter.d.ts.map +1 -1
  111. package/dist/agents/channels/whatsapp/adapter.js +7 -4
  112. package/dist/agents/channels/whatsapp/adapter.js.map +1 -1
  113. package/dist/agents/channels/whatsapp/connection.d.ts +24 -2
  114. package/dist/agents/channels/whatsapp/connection.d.ts.map +1 -1
  115. package/dist/agents/channels/whatsapp/connection.js +26 -5
  116. package/dist/agents/channels/whatsapp/connection.js.map +1 -1
  117. package/dist/agents/channels/whatsapp/plugin.d.ts.map +1 -1
  118. package/dist/agents/channels/whatsapp/plugin.js +6 -10
  119. package/dist/agents/channels/whatsapp/plugin.js.map +1 -1
  120. package/dist/agents/extensions/activation-planner.d.ts +125 -0
  121. package/dist/agents/extensions/activation-planner.d.ts.map +1 -0
  122. package/dist/agents/extensions/activation-planner.js +221 -0
  123. package/dist/agents/extensions/activation-planner.js.map +1 -0
  124. package/dist/agents/extensions/diagnose.d.ts +84 -0
  125. package/dist/agents/extensions/diagnose.d.ts.map +1 -0
  126. package/dist/agents/extensions/diagnose.js +123 -0
  127. package/dist/agents/extensions/diagnose.js.map +1 -0
  128. package/dist/agents/extensions/discovery.d.ts +85 -7
  129. package/dist/agents/extensions/discovery.d.ts.map +1 -1
  130. package/dist/agents/extensions/discovery.js +200 -15
  131. package/dist/agents/extensions/discovery.js.map +1 -1
  132. package/dist/agents/extensions/index.d.ts +3 -2
  133. package/dist/agents/extensions/index.d.ts.map +1 -1
  134. package/dist/agents/extensions/index.js +3 -2
  135. package/dist/agents/extensions/index.js.map +1 -1
  136. package/dist/agents/extensions/install-scan.d.ts +63 -0
  137. package/dist/agents/extensions/install-scan.d.ts.map +1 -0
  138. package/dist/agents/extensions/install-scan.js +201 -0
  139. package/dist/agents/extensions/install-scan.js.map +1 -0
  140. package/dist/agents/extensions/install.d.ts +135 -0
  141. package/dist/agents/extensions/install.d.ts.map +1 -0
  142. package/dist/agents/extensions/install.js +414 -0
  143. package/dist/agents/extensions/install.js.map +1 -0
  144. package/dist/agents/extensions/loader.d.ts +13 -2
  145. package/dist/agents/extensions/loader.d.ts.map +1 -1
  146. package/dist/agents/extensions/loader.js +126 -13
  147. package/dist/agents/extensions/loader.js.map +1 -1
  148. package/dist/agents/extensions/registry.d.ts +109 -0
  149. package/dist/agents/extensions/registry.d.ts.map +1 -1
  150. package/dist/agents/extensions/registry.js +172 -0
  151. package/dist/agents/extensions/registry.js.map +1 -1
  152. package/dist/agents/extensions/sdk-alias.d.ts +45 -0
  153. package/dist/agents/extensions/sdk-alias.d.ts.map +1 -0
  154. package/dist/agents/extensions/sdk-alias.js +94 -0
  155. package/dist/agents/extensions/sdk-alias.js.map +1 -0
  156. package/dist/agents/extensions/types.d.ts +155 -1
  157. package/dist/agents/extensions/types.d.ts.map +1 -1
  158. package/dist/agents/extensions/types.js.map +1 -1
  159. package/dist/agents/tools/composio-tool.d.ts +9 -1
  160. package/dist/agents/tools/composio-tool.d.ts.map +1 -1
  161. package/dist/agents/tools/composio-tool.js +68 -4
  162. package/dist/agents/tools/composio-tool.js.map +1 -1
  163. package/dist/agents/tools/message-action-tool.d.ts +6 -1
  164. package/dist/agents/tools/message-action-tool.d.ts.map +1 -1
  165. package/dist/agents/tools/message-action-tool.js +52 -2
  166. package/dist/agents/tools/message-action-tool.js.map +1 -1
  167. package/dist/agents/tools/send-message-tool.d.ts +1 -0
  168. package/dist/agents/tools/send-message-tool.d.ts.map +1 -1
  169. package/dist/agents/tools/send-message-tool.js +56 -1
  170. package/dist/agents/tools/send-message-tool.js.map +1 -1
  171. package/dist/buildstamp.json +1 -1
  172. package/dist/channel-sdk.d.ts +28 -0
  173. package/dist/channel-sdk.d.ts.map +1 -0
  174. package/dist/channel-sdk.js +28 -0
  175. package/dist/channel-sdk.js.map +1 -0
  176. package/dist/cli/commands/channels.d.ts.map +1 -1
  177. package/dist/cli/commands/channels.js +8 -8
  178. package/dist/cli/commands/channels.js.map +1 -1
  179. package/dist/cli/commands/connect.d.ts +8 -11
  180. package/dist/cli/commands/connect.d.ts.map +1 -1
  181. package/dist/cli/commands/connect.js +157 -17
  182. package/dist/cli/commands/connect.js.map +1 -1
  183. package/dist/cli/commands/doctor.d.ts.map +1 -1
  184. package/dist/cli/commands/doctor.js +64 -0
  185. package/dist/cli/commands/doctor.js.map +1 -1
  186. package/dist/cli/commands/extensions.d.ts +46 -0
  187. package/dist/cli/commands/extensions.d.ts.map +1 -0
  188. package/dist/cli/commands/extensions.js +578 -0
  189. package/dist/cli/commands/extensions.js.map +1 -0
  190. package/dist/cli/commands/pairing.d.ts.map +1 -1
  191. package/dist/cli/commands/pairing.js +16 -2
  192. package/dist/cli/commands/pairing.js.map +1 -1
  193. package/dist/cli/commands/update.d.ts +17 -0
  194. package/dist/cli/commands/update.d.ts.map +1 -0
  195. package/dist/cli/commands/update.js +104 -0
  196. package/dist/cli/commands/update.js.map +1 -0
  197. package/dist/cli/program/build-program.d.ts.map +1 -1
  198. package/dist/cli/program/build-program.js +113 -0
  199. package/dist/cli/program/build-program.js.map +1 -1
  200. package/dist/config/paths.d.ts +1 -0
  201. package/dist/config/paths.d.ts.map +1 -1
  202. package/dist/config/paths.js +9 -0
  203. package/dist/config/paths.js.map +1 -1
  204. package/dist/core/server.d.ts.map +1 -1
  205. package/dist/core/server.js +134 -2
  206. package/dist/core/server.js.map +1 -1
  207. package/dist/protocol.d.ts +25 -0
  208. package/dist/protocol.d.ts.map +1 -1
  209. package/dist/protocol.js.map +1 -1
  210. package/dist/system-prompt/assembler.d.ts.map +1 -1
  211. package/dist/system-prompt/assembler.js +17 -0
  212. package/dist/system-prompt/assembler.js.map +1 -1
  213. package/dist/system-prompt/identity-defaults.d.ts +28 -0
  214. package/dist/system-prompt/identity-defaults.d.ts.map +1 -1
  215. package/dist/system-prompt/identity-defaults.js +47 -0
  216. package/dist/system-prompt/identity-defaults.js.map +1 -1
  217. package/dist/ui/editor.d.ts.map +1 -1
  218. package/dist/ui/editor.js +1 -0
  219. package/dist/ui/editor.js.map +1 -1
  220. package/dist/version.d.ts +4 -3
  221. package/dist/version.d.ts.map +1 -1
  222. package/dist/version.js +27 -5
  223. package/dist/version.js.map +1 -1
  224. package/package.json +21 -4
  225. package/scripts/build-done.mjs +11 -2
@@ -0,0 +1,221 @@
1
+ /**
2
+ * Manifest-driven lazy activation planner (plugin SDK Step 5).
3
+ *
4
+ * Today the loader imports + runs `register()` EAGERLY for every eligible user
5
+ * module. With many installed modules that's O(modules) cold-boot cost paid even
6
+ * for modules whose capability is irrelevant to the active config (a Slack
7
+ * channel module on an install with no Slack channel configured still gets
8
+ * imported + registered). The `BrigadeModuleManifest.activation` block
9
+ * (`onChannels` / `onProviders` / `onCommands` / `onCapabilities`) declares WHEN
10
+ * a module is relevant; this planner consumes it so the loader can SKIP importing
11
+ * a module whose triggers don't fire — O(manifest) boot instead of O(modules).
12
+ *
13
+ * ── Trigger semantics ──────────────────────────────────────────────────────
14
+ * The planner takes a module's `activation` triggers + an `ActivationSnapshot`
15
+ * (a cheap, config-derived view of what's active right now) and returns
16
+ * `{ activate, reason? }`.
17
+ *
18
+ * - NO manifest, or a manifest with NO `activation` block, or an `activation`
19
+ * block with NO non-empty trigger array ⇒ ALWAYS activate (back-compat:
20
+ * nothing that worked before regresses; a module that doesn't opt into lazy
21
+ * activation keeps loading eagerly).
22
+ *
23
+ * - When `activation` declares one or more NON-EMPTY trigger arrays, the
24
+ * module activates iff AT LEAST ONE declared trigger matches the snapshot
25
+ * (OR across trigger kinds AND across entries within a kind — any single
26
+ * hit is enough). The intent of a trigger is "load me when X is present",
27
+ * so the union (not intersection) is the correct semantics: a module that
28
+ * declares both `onChannels: ["slack"]` and `onProviders: ["openai"]` wants
29
+ * to load if EITHER Slack is configured OR OpenAI is the active provider.
30
+ *
31
+ * - A trigger array that is present but EMPTY (`onChannels: []`) is treated as
32
+ * "no constraint from this kind" — it neither forces activation nor blocks
33
+ * it; only non-empty arrays carry a constraint. If ALL declared arrays are
34
+ * empty the module falls back to the back-compat ALWAYS-activate rule.
35
+ *
36
+ * Match rules per trigger kind (all case-insensitive, trimmed):
37
+ * - `onChannels` matches a channel id that is CONFIGURED + ENABLED
38
+ * (`cfg.channels.<id>` present and not `enabled === false`).
39
+ * - `onProviders` matches the active model provider
40
+ * (`cfg.agents.defaults.provider`) OR any provider id that
41
+ * appears in a configured channel/model — the snapshot
42
+ * collects the set; the planner only checks membership.
43
+ * - `onCommands` matches an AVAILABLE command name. The snapshot's
44
+ * `commands` set is the union of (a) the built-in channel
45
+ * commands every configured channel exposes (`/help`,
46
+ * `/status`, `/agent`, …) and (b) any extra command names
47
+ * the loader supplies (e.g. command names the bundled
48
+ * modules declare in their manifest). A module gated only
49
+ * on `onCommands:[...]` activates when one of its named
50
+ * commands is available — otherwise it stays dormant.
51
+ * - `onCapabilities` matches an `extensions.slots.<slot>` value the operator
52
+ * pinned (memory / contextEngine / compaction /
53
+ * agentHarness) — a module that PROVIDES the pinned backend
54
+ * activates; others stay dormant.
55
+ *
56
+ * The planner is a PURE function: no I/O, no config reads of its own. The loader
57
+ * builds the snapshot once (via `buildActivationSnapshot`) and consults the
58
+ * planner per module BEFORE importing it.
59
+ */
60
+ /**
61
+ * The built-in channel command names Brigade wires for EVERY configured channel
62
+ * (`buildBundledCommands` in the inbound pipeline produces these). They are a
63
+ * stable, channel-neutral constant, so the snapshot can fold them into the
64
+ * `commands` set whenever at least one channel is configured WITHOUT importing
65
+ * the pipeline (which would pull a heavy module into this pure planner). A bundled
66
+ * channel like `/help` is therefore matchable by an `onCommands:["help"]` trigger
67
+ * on any install that has a channel. Module-contributed commands are passed in
68
+ * separately via `buildActivationSnapshot(config, availableCommands)`.
69
+ */
70
+ export const BUILTIN_CHANNEL_COMMAND_NAMES = [
71
+ "help",
72
+ "start",
73
+ "pending",
74
+ "approve",
75
+ "deny",
76
+ "status",
77
+ "allowlist",
78
+ "agent",
79
+ "agents",
80
+ "whoami",
81
+ "org",
82
+ ];
83
+ /** Lowercase + trim a list into a set, dropping empties. */
84
+ function toSet(values) {
85
+ const out = new Set();
86
+ for (const v of values ?? []) {
87
+ const norm = typeof v === "string" ? v.trim().toLowerCase() : "";
88
+ if (norm)
89
+ out.add(norm);
90
+ }
91
+ return out;
92
+ }
93
+ /** Does any entry of `wanted` appear in `have`? (case-insensitive; inputs pre-normalized.) */
94
+ function anyMatch(wanted, have) {
95
+ for (const w of wanted) {
96
+ const norm = typeof w === "string" ? w.trim().toLowerCase() : "";
97
+ if (norm && have.has(norm))
98
+ return true;
99
+ }
100
+ return false;
101
+ }
102
+ /**
103
+ * Build the activation snapshot from the active config. Pure read — never
104
+ * mutates config, never touches disk. The loader calls this once before the
105
+ * per-module loop.
106
+ *
107
+ * `availableCommands` (optional) lets the caller fold in EXTRA command names
108
+ * that aren't derivable from config alone — e.g. the command names the bundled
109
+ * modules declare in their manifest. They are merged with the built-in channel
110
+ * command set (the latter added only when at least one channel is configured),
111
+ * so an `onCommands` trigger can fire off either source.
112
+ */
113
+ export function buildActivationSnapshot(config, availableCommands) {
114
+ const channels = new Set();
115
+ const providers = new Set();
116
+ const commands = new Set();
117
+ const capabilities = new Set();
118
+ // ── channels: cfg.channels.<id> present and not explicitly disabled.
119
+ const channelsCfg = config
120
+ .channels;
121
+ if (channelsCfg && typeof channelsCfg === "object") {
122
+ for (const [id, slot] of Object.entries(channelsCfg)) {
123
+ const norm = id.trim().toLowerCase();
124
+ if (!norm)
125
+ continue;
126
+ // A slot that exists counts as "configured"; only an explicit
127
+ // `enabled: false` opts out. (Mirrors the channel manager, which
128
+ // treats an absent `enabled` as on once the slot has credentials.)
129
+ if (slot && typeof slot === "object" && slot.enabled === false)
130
+ continue;
131
+ channels.add(norm);
132
+ // A channel may name an owning provider; fold it into the provider set
133
+ // so an `onProviders` trigger can fire off a channel's transport too.
134
+ if (slot && typeof slot === "object" && typeof slot.provider === "string") {
135
+ const p = slot.provider.trim().toLowerCase();
136
+ if (p)
137
+ providers.add(p);
138
+ }
139
+ }
140
+ }
141
+ // ── providers: the active model provider stamped at onboard time.
142
+ const activeProvider = config.agents?.defaults?.provider;
143
+ if (typeof activeProvider === "string") {
144
+ const p = activeProvider.trim().toLowerCase();
145
+ if (p)
146
+ providers.add(p);
147
+ }
148
+ // ── capabilities: pinned slot backends (memory/contextEngine/compaction/agentHarness).
149
+ const slots = config.extensions?.slots;
150
+ if (slots && typeof slots === "object") {
151
+ for (const value of Object.values(slots)) {
152
+ if (typeof value !== "string")
153
+ continue;
154
+ const norm = value.trim().toLowerCase();
155
+ if (norm)
156
+ capabilities.add(norm);
157
+ }
158
+ }
159
+ // ── commands: the built-in channel commands are wired for EVERY configured
160
+ // channel, so they become "available" the moment any channel is configured.
161
+ // (No channel ⇒ no channel command surface ⇒ leave them out.)
162
+ if (channels.size > 0) {
163
+ for (const name of BUILTIN_CHANNEL_COMMAND_NAMES)
164
+ commands.add(name);
165
+ }
166
+ // Plus any extra command names the caller supplies (e.g. command names the
167
+ // bundled modules declare). Normalized + deduped like every other set.
168
+ for (const name of availableCommands ?? []) {
169
+ const norm = typeof name === "string" ? name.trim().toLowerCase() : "";
170
+ if (norm)
171
+ commands.add(norm);
172
+ }
173
+ return { channels, providers, commands, capabilities };
174
+ }
175
+ /**
176
+ * Decide whether a module should activate, given its manifest (or `undefined`)
177
+ * and the active-config snapshot. PURE — see file header for the full trigger
178
+ * semantics. The headline rules:
179
+ * - no manifest / no activation / no non-empty triggers ⇒ activate (back-compat)
180
+ * - otherwise activate iff at least one declared trigger matches the snapshot.
181
+ */
182
+ export function planActivation(manifest, snapshot) {
183
+ const activation = manifest?.activation;
184
+ if (!activation) {
185
+ // No manifest or no activation block — always-on (eager, as before).
186
+ return { activate: true };
187
+ }
188
+ const onChannels = toSet(activation.onChannels);
189
+ const onProviders = toSet(activation.onProviders);
190
+ const onCommands = toSet(activation.onCommands);
191
+ const onCapabilities = toSet(activation.onCapabilities);
192
+ const declaredKinds = [];
193
+ if (onChannels.size > 0)
194
+ declaredKinds.push({ kind: "onChannels", wanted: onChannels, have: snapshot.channels });
195
+ if (onProviders.size > 0)
196
+ declaredKinds.push({ kind: "onProviders", wanted: onProviders, have: snapshot.providers });
197
+ if (onCommands.size > 0)
198
+ declaredKinds.push({ kind: "onCommands", wanted: onCommands, have: snapshot.commands });
199
+ if (onCapabilities.size > 0)
200
+ declaredKinds.push({ kind: "onCapabilities", wanted: onCapabilities, have: snapshot.capabilities });
201
+ // No non-empty trigger arrays — the activation block carries no constraint,
202
+ // so the module stays always-on (back-compat).
203
+ if (declaredKinds.length === 0) {
204
+ return { activate: true };
205
+ }
206
+ // Activate iff ANY declared trigger matches the snapshot.
207
+ for (const { wanted, have } of declaredKinds) {
208
+ if (anyMatch([...wanted], have)) {
209
+ return { activate: true };
210
+ }
211
+ }
212
+ // Nothing matched — describe what would have been needed.
213
+ const detail = declaredKinds
214
+ .map(({ kind, wanted }) => `${kind}=[${[...wanted].join(",")}]`)
215
+ .join(" ");
216
+ return {
217
+ activate: false,
218
+ reason: `no activation trigger matched active config (${detail})`,
219
+ };
220
+ }
221
+ //# sourceMappingURL=activation-planner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"activation-planner.js","sourceRoot":"","sources":["../../../src/agents/extensions/activation-planner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Extension diagnosis — the shared, registry-free analysis behind
3
+ * `brigade extensions list` and `brigade extensions doctor`.
4
+ *
5
+ * It answers two questions for the operator authoring a plugin:
6
+ * 1. WHAT modules exist — the bundled set that ships with Brigade plus every
7
+ * module dropped into `~/.brigade/extensions/`.
8
+ * 2. For each user module, WOULD IT LOAD — did it pass the safety gate, did it
9
+ * import cleanly, and did it export a usable module? When the answer is no,
10
+ * the exact reason (so the author can fix it).
11
+ *
12
+ * This deliberately does NOT run a module's `register()` or touch a registry —
13
+ * it inspects candidates the same way discovery would, so it's safe to call from
14
+ * a short-lived CLI command. The richer config-gating decisions (allowlist /
15
+ * disabled / requiresEnv / configSchema) live in the loader's structured logs;
16
+ * this surface covers the discovery half ("did Brigade even find + import it").
17
+ */
18
+ import type { BrigadeExtensionRegistry, PluginRecord } from "./registry.js";
19
+ import type { BrigadeModule } from "./types.js";
20
+ /** Where a module came from. */
21
+ export type ExtensionOrigin = "bundled" | "user";
22
+ /** Headline status shown in the `list` table. */
23
+ export type ExtensionStatus = "loaded" | "skipped";
24
+ /**
25
+ * One diagnosed extension entry — a bundled module OR a user candidate. The same
26
+ * shape feeds both the `list` table and the deeper `doctor` view.
27
+ */
28
+ export interface DiagnosedExtension {
29
+ /** The module's declared id, or — for a user candidate that never produced a
30
+ * module — a readable label derived from its file. */
31
+ id: string;
32
+ origin: ExtensionOrigin;
33
+ /** Absolute source path the module was found at. */
34
+ source: string;
35
+ status: ExtensionStatus;
36
+ /** One short, non-jargony line explaining a `skipped` status. */
37
+ reason?: string;
38
+ /** Per-candidate detail (user modules only) for the `doctor` view. */
39
+ checks?: {
40
+ /** Passed the file-safety gate. */
41
+ safe: boolean;
42
+ /** Imported without error. */
43
+ imported: boolean;
44
+ /** Exported a usable module. */
45
+ exportedModule: boolean;
46
+ };
47
+ /**
48
+ * Live REGISTER-phase status (FIX 4), present ONLY when a live registry was
49
+ * passed to `diagnoseExtensions`. Discovery alone never runs `register()`, so
50
+ * without a live registry this is undefined and the discovery-only path is
51
+ * byte-identical to before. When present it reflects the durable
52
+ * `PluginRecord`: did the module activate, fail, or just get discovered, and
53
+ * which capability ids it registered.
54
+ */
55
+ live?: {
56
+ status: PluginRecord["status"];
57
+ failurePhase?: string;
58
+ /** Flat list of `kind:id` strings for the capabilities the module registered. */
59
+ capabilities: string[];
60
+ };
61
+ }
62
+ /** Result of a full diagnosis pass over the bundled + user extension sets. */
63
+ export interface ExtensionDiagnosis {
64
+ /** The extensions directory that was scanned. */
65
+ extensionsDir: string;
66
+ /** Every diagnosed entry, bundled first then user, each set id-sorted. */
67
+ extensions: DiagnosedExtension[];
68
+ }
69
+ /**
70
+ * Diagnose every bundled module + every user candidate under `extensionsDir`.
71
+ *
72
+ * Bundled modules are always reported as `loaded` (they ship in-tree and import
73
+ * at build time). User candidates are taken through the same gate discovery uses
74
+ * — safety check, then import, then valid-module check — and reported with the
75
+ * first failing step as the skip reason.
76
+ *
77
+ * `liveRegistry` (optional, FIX 4) — when a caller has a LIVE registry (one that
78
+ * actually ran `register()`), pass it to overlay each entry's `live` field with
79
+ * the durable `PluginRecord` (register-phase status + attributed capabilities).
80
+ * Omit it (the CLI's default) and the result is byte-identical to the pure
81
+ * discovery pass — no `register()` is ever run here.
82
+ */
83
+ export declare function diagnoseExtensions(bundled: ReadonlyArray<BrigadeModule>, extensionsDir: string, liveRegistry?: BrigadeExtensionRegistry): Promise<ExtensionDiagnosis>;
84
+ //# sourceMappingURL=diagnose.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnose.d.ts","sourceRoot":"","sources":["../../../src/agents/extensions/diagnose.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAOH,OAAO,KAAK,EAAE,wBAAwB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC5E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,gCAAgC;AAChC,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,MAAM,CAAC;AAEjD,iDAAiD;AACjD,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEnD;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IAClC;2DACuD;IACvD,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,eAAe,CAAC;IACxB,oDAAoD;IACpD,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,eAAe,CAAC;IACxB,iEAAiE;IACjE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sEAAsE;IACtE,MAAM,CAAC,EAAE;QACR,mCAAmC;QACnC,IAAI,EAAE,OAAO,CAAC;QACd,8BAA8B;QAC9B,QAAQ,EAAE,OAAO,CAAC;QAClB,gCAAgC;QAChC,cAAc,EAAE,OAAO,CAAC;KACxB,CAAC;IACF;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE;QACN,MAAM,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAC;QAC/B,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,iFAAiF;QACjF,YAAY,EAAE,MAAM,EAAE,CAAC;KACvB,CAAC;CACF;AAED,8EAA8E;AAC9E,MAAM,WAAW,kBAAkB;IAClC,iDAAiD;IACjD,aAAa,EAAE,MAAM,CAAC;IACtB,0EAA0E;IAC1E,UAAU,EAAE,kBAAkB,EAAE,CAAC;CACjC;AAaD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,kBAAkB,CACvC,OAAO,EAAE,aAAa,CAAC,aAAa,CAAC,EACrC,aAAa,EAAE,MAAM,EACrB,YAAY,CAAC,EAAE,wBAAwB,GACrC,OAAO,CAAC,kBAAkB,CAAC,CAqE7B"}
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Extension diagnosis — the shared, registry-free analysis behind
3
+ * `brigade extensions list` and `brigade extensions doctor`.
4
+ *
5
+ * It answers two questions for the operator authoring a plugin:
6
+ * 1. WHAT modules exist — the bundled set that ships with Brigade plus every
7
+ * module dropped into `~/.brigade/extensions/`.
8
+ * 2. For each user module, WOULD IT LOAD — did it pass the safety gate, did it
9
+ * import cleanly, and did it export a usable module? When the answer is no,
10
+ * the exact reason (so the author can fix it).
11
+ *
12
+ * This deliberately does NOT run a module's `register()` or touch a registry —
13
+ * it inspects candidates the same way discovery would, so it's safe to call from
14
+ * a short-lived CLI command. The richer config-gating decisions (allowlist /
15
+ * disabled / requiresEnv / configSchema) live in the loader's structured logs;
16
+ * this surface covers the discovery half ("did Brigade even find + import it").
17
+ */
18
+ import { checkPosixSafety, importCandidateForDiagnosis, listExtensionSources, } from "./discovery.js";
19
+ /** Derive a friendly label for a user candidate that never yielded a module id. */
20
+ function labelFromSource(source) {
21
+ // `<dir>/index.ts` → the folder name; otherwise the file's base name.
22
+ const parts = source.replace(/\\/g, "/").split("/");
23
+ const base = parts[parts.length - 1] ?? source;
24
+ if (/^index\.(js|mjs|ts|mts)$/i.test(base) && parts.length >= 2) {
25
+ return parts[parts.length - 2] ?? base;
26
+ }
27
+ return base.replace(/\.(js|mjs|ts|mts)$/i, "");
28
+ }
29
+ /**
30
+ * Diagnose every bundled module + every user candidate under `extensionsDir`.
31
+ *
32
+ * Bundled modules are always reported as `loaded` (they ship in-tree and import
33
+ * at build time). User candidates are taken through the same gate discovery uses
34
+ * — safety check, then import, then valid-module check — and reported with the
35
+ * first failing step as the skip reason.
36
+ *
37
+ * `liveRegistry` (optional, FIX 4) — when a caller has a LIVE registry (one that
38
+ * actually ran `register()`), pass it to overlay each entry's `live` field with
39
+ * the durable `PluginRecord` (register-phase status + attributed capabilities).
40
+ * Omit it (the CLI's default) and the result is byte-identical to the pure
41
+ * discovery pass — no `register()` is ever run here.
42
+ */
43
+ export async function diagnoseExtensions(bundled, extensionsDir, liveRegistry) {
44
+ const out = [];
45
+ const overlayLive = (entry) => {
46
+ if (!liveRegistry)
47
+ return entry;
48
+ const rec = liveRegistry.pluginRecord(entry.id);
49
+ if (!rec)
50
+ return entry;
51
+ return { ...entry, live: pluginRecordToLive(rec) };
52
+ };
53
+ // Bundled set — sorted by id for a stable table.
54
+ for (const m of [...bundled].sort((a, b) => a.id.localeCompare(b.id))) {
55
+ out.push(overlayLive({ id: m.id, origin: "bundled", source: "(built in)", status: "loaded" }));
56
+ }
57
+ // User candidates — diagnosed one by one.
58
+ const userEntries = [];
59
+ for (const candidate of listExtensionSources(extensionsDir)) {
60
+ const { source } = candidate;
61
+ // Re-run the safety check rather than trust `candidate.safetyReason`
62
+ // alone, so the verdict and the rest of the diagnosis share one source.
63
+ const safetyReason = candidate.safetyReason ?? checkPosixSafety(source, extensionsDir);
64
+ if (safetyReason) {
65
+ userEntries.push({
66
+ id: labelFromSource(source),
67
+ origin: "user",
68
+ source,
69
+ status: "skipped",
70
+ reason: "blocked by a safety check on the file",
71
+ checks: { safe: false, imported: false, exportedModule: false },
72
+ });
73
+ continue;
74
+ }
75
+ const result = await importCandidateForDiagnosis(source);
76
+ if (!result.imported) {
77
+ userEntries.push({
78
+ id: labelFromSource(source),
79
+ origin: "user",
80
+ source,
81
+ status: "skipped",
82
+ reason: "the file could not be loaded — check it for errors",
83
+ checks: { safe: true, imported: false, exportedModule: false },
84
+ });
85
+ continue;
86
+ }
87
+ if (!result.hasValidModule) {
88
+ userEntries.push({
89
+ id: labelFromSource(source),
90
+ origin: "user",
91
+ source,
92
+ status: "skipped",
93
+ reason: "the file does not export a Brigade module (use `export default defineModule({...})`)",
94
+ checks: { safe: true, imported: true, exportedModule: false },
95
+ });
96
+ continue;
97
+ }
98
+ userEntries.push({
99
+ id: result.moduleId ?? labelFromSource(source),
100
+ origin: "user",
101
+ source,
102
+ status: "loaded",
103
+ checks: { safe: true, imported: true, exportedModule: true },
104
+ });
105
+ }
106
+ userEntries.sort((a, b) => a.id.localeCompare(b.id));
107
+ out.push(...userEntries.map(overlayLive));
108
+ return { extensionsDir, extensions: out };
109
+ }
110
+ /** Flatten a `PluginRecord` into the diagnosis `live` view (`kind:id` capability strings). */
111
+ function pluginRecordToLive(rec) {
112
+ const capabilities = [];
113
+ for (const [kind, ids] of Object.entries(rec.capabilities)) {
114
+ for (const id of ids)
115
+ capabilities.push(`${kind}:${id}`);
116
+ }
117
+ return {
118
+ status: rec.status,
119
+ ...(rec.failurePhase !== undefined ? { failurePhase: rec.failurePhase } : {}),
120
+ capabilities,
121
+ };
122
+ }
123
+ //# sourceMappingURL=diagnose.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnose.js","sourceRoot":"","sources":["../../../src/agents/extensions/diagnose.ts"],"names":[],"mappings":"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"}
@@ -2,13 +2,20 @@
2
2
  * User-module discovery.
3
3
  *
4
4
  * Out-of-tree modules dropped into `~/.brigade/extensions/` are discovered and
5
- * dynamic-imported here, alongside the bundled ones. A candidate is either a
6
- * top-level `*.js`/`*.mjs` file or a folder containing `index.js`/`index.mjs`.
7
- * Each must `export default` a `BrigadeModule` (or an array of them); anything
8
- * else is skipped with a warning — a bad user module never aborts boot.
5
+ * imported here, alongside the bundled ones. A candidate is either a top-level
6
+ * `*.js`/`*.mjs`/`*.ts`/`*.mts` file or a folder containing
7
+ * `index.{js,mjs,ts,mts}`. Each must `export default` a `BrigadeModule` (or an
8
+ * array of them); anything else is skipped with a warning — a bad user module
9
+ * never aborts boot. `.d.ts` declaration files are NOT candidates.
9
10
  *
10
- * Authors import the stable `@brigade/extension-sdk` surface (defineModule + the
11
- * capability contracts), so a user module never reaches into Brigade internals.
11
+ * TypeScript-authored modules load directly: imports go through a Jiti instance
12
+ * that transpiles `.ts`/`.mts` on import, so authors don't need a build step.
13
+ *
14
+ * Authors import the stable `brigade/extension-sdk` / `brigade/channel-sdk`
15
+ * surface (defineModule + the capability contracts), so a user module never
16
+ * reaches into Brigade internals. Those specifiers are alias-resolved to
17
+ * Brigade's own built SDK entry points by the Jiti instance (see `sdk-alias.ts`),
18
+ * so the author does NOT install Brigade into the extensions folder.
12
19
  *
13
20
  * POSIX safety gates (non-Windows only): world-writable files are rejected
14
21
  * (mode & 0o002), suspicious ownership (uid != current uid AND != root) is
@@ -56,6 +63,50 @@ export interface DiscoveredModule {
56
63
  * `platformOverride` is a test seam — callers normally omit it.
57
64
  */
58
65
  export declare function checkPosixSafety(candidate: string, extensionsDir: string, platformOverride?: NodeJS.Platform): string | null;
66
+ /**
67
+ * Read the sidecar manifest for a candidate entry, WITHOUT importing the module.
68
+ * `source` is the resolved entry path (a file, or a dir's `index.*`). Returns the
69
+ * parsed `BrigadeModuleManifest` when a readable, well-formed sidecar exists;
70
+ * `undefined` otherwise (no sidecar, unreadable, malformed, or wrong shape).
71
+ */
72
+ export declare function readSidecarManifest(source: string): BrigadeModuleManifest | undefined;
73
+ /**
74
+ * A discovery candidate paired with its sidecar manifest (read WITHOUT importing
75
+ * the module body). The loader's activation planner consults `manifest` to decide
76
+ * whether to import + register the module at all — a non-triggered module is never
77
+ * imported. `manifest` is `undefined` when the candidate ships no sidecar; the
78
+ * loader then falls back to importing the module to read its `manifest` field.
79
+ */
80
+ export interface DiscoveryCandidate {
81
+ /** Absolute entry path the module would be imported from. */
82
+ source: string;
83
+ /** POSIX safety verdict — `null` = passed; non-null = rejection reason. */
84
+ safetyReason: string | null;
85
+ /** Sidecar manifest, when a readable `brigade.extension.json` exists next to the entry. */
86
+ manifest?: BrigadeModuleManifest;
87
+ }
88
+ /**
89
+ * List discovery candidates under `extensionsDir` with each one's POSIX safety
90
+ * verdict + its sidecar manifest (read WITHOUT importing the module body). This
91
+ * is the lazy-activation entry point: the loader plans activation from the
92
+ * sidecar manifest and only imports the candidates that survive the plan. Never
93
+ * imports anything; never throws (an unreadable sidecar is treated as absent).
94
+ */
95
+ export declare function listDiscoveryCandidates(extensionsDir: string): DiscoveryCandidate[];
96
+ /**
97
+ * Import a single candidate the same way `discoverUserModules` does — through the
98
+ * shared SDK-alias + TypeScript Jiti instance, time-boxed — and return the
99
+ * discovered module(s) plus the resolved manifest. Used by the loader's lazy
100
+ * path AFTER the activation planner decides (from a sidecar) that the module
101
+ * should load, OR when the candidate shipped no sidecar so the body must be read
102
+ * to recover its `manifest` field. The POSIX safety gate is the caller's
103
+ * responsibility (mirrors `importCandidateForDiagnosis`). Returns `[]` on
104
+ * import failure (logged), so a bad module never aborts the load.
105
+ *
106
+ * `sidecarManifest` (when provided) takes precedence over a body-carried manifest
107
+ * — the operator's declarative sidecar is authoritative.
108
+ */
109
+ export declare function importDiscoveredModules(source: string, sidecarManifest?: BrigadeModuleManifest): Promise<DiscoveredModule[]>;
59
110
  /**
60
111
  * Public lister — returns the candidate sources under `extensionsDir` with
61
112
  * each one's safety verdict, WITHOUT importing them. Useful for diagnostic
@@ -73,8 +124,35 @@ export declare function listExtensionSources(extensionsDir: string): ReadonlyArr
73
124
  /** Does the extensions dir exist? Plain `fs.existsSync` lift so callers don't
74
125
  * need to re-derive the path. */
75
126
  export declare function extensionsRootExists(extensionsDir: string): boolean;
76
- /** Drop the discovery cache so the next `discoverUserModules` re-scans (reload). */
127
+ /** Drop the discovery cache so the next `discoverUserModules` re-scans (reload).
128
+ * Also drops the shared Jiti instance so a reloaded module is re-transpiled
129
+ * fresh rather than served from Jiti's own module cache. */
77
130
  export declare function clearDiscoveryCache(): void;
131
+ /**
132
+ * Per-candidate import outcome used by the diagnostic surfaces
133
+ * (`brigade extensions list` / `doctor`). Reports — WITHOUT mutating any
134
+ * registry — whether a single candidate file imports cleanly and exports a
135
+ * usable module.
136
+ */
137
+ export interface CandidateImportResult {
138
+ /** The candidate imported without throwing (or timing out). */
139
+ imported: boolean;
140
+ /** A usable BrigadeModule was found on the default/module export. */
141
+ hasValidModule: boolean;
142
+ /** The discovered module's declared id, when one was found. */
143
+ moduleId?: string;
144
+ /** Failure detail when `imported` is false (timeout / transpile / throw). */
145
+ error?: string;
146
+ }
147
+ /**
148
+ * Import a single candidate the same way `discoverUserModules` does — through
149
+ * the shared SDK-alias + TypeScript Jiti instance, time-boxed — and report what
150
+ * came back, without recording anything into a registry. This is the engine
151
+ * behind `brigade extensions doctor`'s "did my plugin import + export a module?"
152
+ * diagnosis. Callers run `checkPosixSafety` FIRST; this only runs for a
153
+ * candidate that passed the safety gate.
154
+ */
155
+ export declare function importCandidateForDiagnosis(source: string): Promise<CandidateImportResult>;
78
156
  /**
79
157
  * Discover + import user modules from `extensionsDir`. Returns the loaded
80
158
  * modules (shape-validated). Errors per candidate are logged and skipped.
@@ -1 +1 @@
1
- {"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../../../src/agents/extensions/discovery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAOH,OAAO,KAAK,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAmBvE,qFAAqF;AACrF,MAAM,WAAW,gBAAgB;IAChC,MAAM,EAAE,aAAa,CAAC;IACtB;;;;;;OAMG;IACH,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,kDAAkD;IAClD,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,QAAQ,CAAC,EAAE,qBAAqB,CAAC;CACjC;AA8BD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,gBAAgB,CAC/B,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,EACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC,QAAQ,GAChC,MAAM,GAAG,IAAI,CAqCf;AAoCD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CACnC,aAAa,EAAE,MAAM,GACnB,aAAa,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAmB5F;AAED;kCACkC;AAClC,wBAAgB,oBAAoB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAEnE;AAED,oFAAoF;AACpF,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAmD5F"}
1
+ {"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../../../src/agents/extensions/discovery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAOH,OAAO,KAAK,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAkCvE,qFAAqF;AACrF,MAAM,WAAW,gBAAgB;IAChC,MAAM,EAAE,aAAa,CAAC;IACtB;;;;;;OAMG;IACH,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,kDAAkD;IAClD,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,QAAQ,CAAC,EAAE,qBAAqB,CAAC;CACjC;AAkDD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,gBAAgB,CAC/B,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,EACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC,QAAQ,GAChC,MAAM,GAAG,IAAI,CAqCf;AA4CD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,qBAAqB,GAAG,SAAS,CAoCrF;AAED;;;;;;GAMG;AACH,MAAM,WAAW,kBAAkB;IAClC,6DAA6D;IAC7D,MAAM,EAAE,MAAM,CAAC;IACf,2EAA2E;IAC3E,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,2FAA2F;IAC3F,QAAQ,CAAC,EAAE,qBAAqB,CAAC;CACjC;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,aAAa,EAAE,MAAM,GAAG,kBAAkB,EAAE,CAQnF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,uBAAuB,CAC5C,MAAM,EAAE,MAAM,EACd,eAAe,CAAC,EAAE,qBAAqB,GACrC,OAAO,CAAC,gBAAgB,EAAE,CAAC,CA6B7B;AAQD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CACnC,aAAa,EAAE,MAAM,GACnB,aAAa,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAmB5F;AAED;kCACkC;AAClC,wBAAgB,oBAAoB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAEnE;AAED;;6DAE6D;AAC7D,wBAAgB,mBAAmB,IAAI,IAAI,CAG1C;AAED;;;;;GAKG;AACH,MAAM,WAAW,qBAAqB;IACrC,+DAA+D;IAC/D,QAAQ,EAAE,OAAO,CAAC;IAClB,qEAAqE;IACrE,cAAc,EAAE,OAAO,CAAC;IACxB,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6EAA6E;IAC7E,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;GAOG;AACH,wBAAsB,2BAA2B,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAiBhG;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAmD5F"}