@spinabot/brigade 1.6.0 → 1.7.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 (194) hide show
  1. package/dist/agents/agent-loop.d.ts.map +1 -1
  2. package/dist/agents/agent-loop.js +51 -1
  3. package/dist/agents/agent-loop.js.map +1 -1
  4. package/dist/agents/channels/bundled-channel-metas.d.ts +2 -0
  5. package/dist/agents/channels/bundled-channel-metas.d.ts.map +1 -1
  6. package/dist/agents/channels/bundled-channel-metas.js +11 -0
  7. package/dist/agents/channels/bundled-channel-metas.js.map +1 -1
  8. package/dist/agents/channels/discord/account-config.d.ts +177 -0
  9. package/dist/agents/channels/discord/account-config.d.ts.map +1 -0
  10. package/dist/agents/channels/discord/account-config.js +349 -0
  11. package/dist/agents/channels/discord/account-config.js.map +1 -0
  12. package/dist/agents/channels/discord/adapter.d.ts +79 -0
  13. package/dist/agents/channels/discord/adapter.d.ts.map +1 -0
  14. package/dist/agents/channels/discord/adapter.js +693 -0
  15. package/dist/agents/channels/discord/adapter.js.map +1 -0
  16. package/dist/agents/channels/discord/approval-authorize.d.ts +43 -0
  17. package/dist/agents/channels/discord/approval-authorize.d.ts.map +1 -0
  18. package/dist/agents/channels/discord/approval-authorize.js +71 -0
  19. package/dist/agents/channels/discord/approval-authorize.js.map +1 -0
  20. package/dist/agents/channels/discord/approval-native.d.ts +68 -0
  21. package/dist/agents/channels/discord/approval-native.d.ts.map +1 -0
  22. package/dist/agents/channels/discord/approval-native.js +81 -0
  23. package/dist/agents/channels/discord/approval-native.js.map +1 -0
  24. package/dist/agents/channels/discord/command-menu.d.ts +49 -0
  25. package/dist/agents/channels/discord/command-menu.d.ts.map +1 -0
  26. package/dist/agents/channels/discord/command-menu.js +73 -0
  27. package/dist/agents/channels/discord/command-menu.js.map +1 -0
  28. package/dist/agents/channels/discord/component-blocks.d.ts +108 -0
  29. package/dist/agents/channels/discord/component-blocks.d.ts.map +1 -0
  30. package/dist/agents/channels/discord/component-blocks.js +113 -0
  31. package/dist/agents/channels/discord/component-blocks.js.map +1 -0
  32. package/dist/agents/channels/discord/components.d.ts +175 -0
  33. package/dist/agents/channels/discord/components.d.ts.map +1 -0
  34. package/dist/agents/channels/discord/components.js +220 -0
  35. package/dist/agents/channels/discord/components.js.map +1 -0
  36. package/dist/agents/channels/discord/connection.d.ts +570 -0
  37. package/dist/agents/channels/discord/connection.d.ts.map +1 -0
  38. package/dist/agents/channels/discord/connection.js +1600 -0
  39. package/dist/agents/channels/discord/connection.js.map +1 -0
  40. package/dist/agents/channels/discord/directory-cache.d.ts +47 -0
  41. package/dist/agents/channels/discord/directory-cache.d.ts.map +1 -0
  42. package/dist/agents/channels/discord/directory-cache.js +131 -0
  43. package/dist/agents/channels/discord/directory-cache.js.map +1 -0
  44. package/dist/agents/channels/discord/directory-live.d.ts +61 -0
  45. package/dist/agents/channels/discord/directory-live.d.ts.map +1 -0
  46. package/dist/agents/channels/discord/directory-live.js +140 -0
  47. package/dist/agents/channels/discord/directory-live.js.map +1 -0
  48. package/dist/agents/channels/discord/draft-stream.d.ts +92 -0
  49. package/dist/agents/channels/discord/draft-stream.d.ts.map +1 -0
  50. package/dist/agents/channels/discord/draft-stream.js +213 -0
  51. package/dist/agents/channels/discord/draft-stream.js.map +1 -0
  52. package/dist/agents/channels/discord/format.d.ts +70 -0
  53. package/dist/agents/channels/discord/format.d.ts.map +1 -0
  54. package/dist/agents/channels/discord/format.js +303 -0
  55. package/dist/agents/channels/discord/format.js.map +1 -0
  56. package/dist/agents/channels/discord/guilds.d.ts +25 -0
  57. package/dist/agents/channels/discord/guilds.d.ts.map +1 -0
  58. package/dist/agents/channels/discord/guilds.js +46 -0
  59. package/dist/agents/channels/discord/guilds.js.map +1 -0
  60. package/dist/agents/channels/discord/inbound-extras.d.ts +377 -0
  61. package/dist/agents/channels/discord/inbound-extras.d.ts.map +1 -0
  62. package/dist/agents/channels/discord/inbound-extras.js +589 -0
  63. package/dist/agents/channels/discord/inbound-extras.js.map +1 -0
  64. package/dist/agents/channels/discord/index.d.ts +21 -0
  65. package/dist/agents/channels/discord/index.d.ts.map +1 -0
  66. package/dist/agents/channels/discord/index.js +21 -0
  67. package/dist/agents/channels/discord/index.js.map +1 -0
  68. package/dist/agents/channels/discord/media.d.ts +85 -0
  69. package/dist/agents/channels/discord/media.d.ts.map +1 -0
  70. package/dist/agents/channels/discord/media.js +242 -0
  71. package/dist/agents/channels/discord/media.js.map +1 -0
  72. package/dist/agents/channels/discord/modal-registry.d.ts +89 -0
  73. package/dist/agents/channels/discord/modal-registry.d.ts.map +1 -0
  74. package/dist/agents/channels/discord/modal-registry.js +104 -0
  75. package/dist/agents/channels/discord/modal-registry.js.map +1 -0
  76. package/dist/agents/channels/discord/modals.d.ts +100 -0
  77. package/dist/agents/channels/discord/modals.d.ts.map +1 -0
  78. package/dist/agents/channels/discord/modals.js +124 -0
  79. package/dist/agents/channels/discord/modals.js.map +1 -0
  80. package/dist/agents/channels/discord/module.d.ts +15 -0
  81. package/dist/agents/channels/discord/module.d.ts.map +1 -0
  82. package/dist/agents/channels/discord/module.js +22 -0
  83. package/dist/agents/channels/discord/module.js.map +1 -0
  84. package/dist/agents/channels/discord/permission-audit.d.ts +43 -0
  85. package/dist/agents/channels/discord/permission-audit.d.ts.map +1 -0
  86. package/dist/agents/channels/discord/permission-audit.js +192 -0
  87. package/dist/agents/channels/discord/permission-audit.js.map +1 -0
  88. package/dist/agents/channels/discord/plugin.d.ts +89 -0
  89. package/dist/agents/channels/discord/plugin.d.ts.map +1 -0
  90. package/dist/agents/channels/discord/plugin.js +372 -0
  91. package/dist/agents/channels/discord/plugin.js.map +1 -0
  92. package/dist/agents/channels/discord/probe.d.ts +115 -0
  93. package/dist/agents/channels/discord/probe.d.ts.map +1 -0
  94. package/dist/agents/channels/discord/probe.js +193 -0
  95. package/dist/agents/channels/discord/probe.js.map +1 -0
  96. package/dist/agents/channels/discord/reasoning-lane.d.ts +42 -0
  97. package/dist/agents/channels/discord/reasoning-lane.d.ts.map +1 -0
  98. package/dist/agents/channels/discord/reasoning-lane.js +68 -0
  99. package/dist/agents/channels/discord/reasoning-lane.js.map +1 -0
  100. package/dist/agents/channels/discord/rest-actions.d.ts +346 -0
  101. package/dist/agents/channels/discord/rest-actions.d.ts.map +1 -0
  102. package/dist/agents/channels/discord/rest-actions.js +559 -0
  103. package/dist/agents/channels/discord/rest-actions.js.map +1 -0
  104. package/dist/agents/channels/discord/rest-components.d.ts +122 -0
  105. package/dist/agents/channels/discord/rest-components.d.ts.map +1 -0
  106. package/dist/agents/channels/discord/rest-components.js +243 -0
  107. package/dist/agents/channels/discord/rest-components.js.map +1 -0
  108. package/dist/agents/channels/discord/security-audit.d.ts +29 -0
  109. package/dist/agents/channels/discord/security-audit.d.ts.map +1 -0
  110. package/dist/agents/channels/discord/security-audit.js +94 -0
  111. package/dist/agents/channels/discord/security-audit.js.map +1 -0
  112. package/dist/agents/channels/discord/security-doctor.d.ts +43 -0
  113. package/dist/agents/channels/discord/security-doctor.d.ts.map +1 -0
  114. package/dist/agents/channels/discord/security-doctor.js +83 -0
  115. package/dist/agents/channels/discord/security-doctor.js.map +1 -0
  116. package/dist/agents/channels/discord/status-issues.d.ts +37 -0
  117. package/dist/agents/channels/discord/status-issues.d.ts.map +1 -0
  118. package/dist/agents/channels/discord/status-issues.js +66 -0
  119. package/dist/agents/channels/discord/status-issues.js.map +1 -0
  120. package/dist/agents/channels/discord/subagent-thread-binding-store.d.ts +57 -0
  121. package/dist/agents/channels/discord/subagent-thread-binding-store.d.ts.map +1 -0
  122. package/dist/agents/channels/discord/subagent-thread-binding-store.js +98 -0
  123. package/dist/agents/channels/discord/subagent-thread-binding-store.js.map +1 -0
  124. package/dist/agents/channels/discord/subagent-thread-binding.d.ts +95 -0
  125. package/dist/agents/channels/discord/subagent-thread-binding.d.ts.map +1 -0
  126. package/dist/agents/channels/discord/subagent-thread-binding.js +208 -0
  127. package/dist/agents/channels/discord/subagent-thread-binding.js.map +1 -0
  128. package/dist/agents/channels/discord/system-events.d.ts +31 -0
  129. package/dist/agents/channels/discord/system-events.d.ts.map +1 -0
  130. package/dist/agents/channels/discord/system-events.js +74 -0
  131. package/dist/agents/channels/discord/system-events.js.map +1 -0
  132. package/dist/agents/channels/general-callback.d.ts +12 -0
  133. package/dist/agents/channels/general-callback.d.ts.map +1 -1
  134. package/dist/agents/channels/general-callback.js +18 -0
  135. package/dist/agents/channels/general-callback.js.map +1 -1
  136. package/dist/agents/channels/inbound-pipeline.d.ts.map +1 -1
  137. package/dist/agents/channels/inbound-pipeline.js +70 -10
  138. package/dist/agents/channels/inbound-pipeline.js.map +1 -1
  139. package/dist/agents/channels/sdk.d.ts +2 -0
  140. package/dist/agents/channels/sdk.d.ts.map +1 -1
  141. package/dist/agents/channels/sdk.js +2 -0
  142. package/dist/agents/channels/sdk.js.map +1 -1
  143. package/dist/agents/extensions/modules/index.d.ts.map +1 -1
  144. package/dist/agents/extensions/modules/index.js +5 -0
  145. package/dist/agents/extensions/modules/index.js.map +1 -1
  146. package/dist/agents/extensions/types.d.ts +7 -0
  147. package/dist/agents/extensions/types.d.ts.map +1 -1
  148. package/dist/agents/extensions/types.js.map +1 -1
  149. package/dist/agents/subagent-announce-delivery.d.ts +10 -0
  150. package/dist/agents/subagent-announce-delivery.d.ts.map +1 -1
  151. package/dist/agents/subagent-announce-delivery.js +1 -0
  152. package/dist/agents/subagent-announce-delivery.js.map +1 -1
  153. package/dist/agents/subagent-completion-bridge.d.ts.map +1 -1
  154. package/dist/agents/subagent-completion-bridge.js +81 -0
  155. package/dist/agents/subagent-completion-bridge.js.map +1 -1
  156. package/dist/agents/subagent-spawn.d.ts.map +1 -1
  157. package/dist/agents/subagent-spawn.js +57 -4
  158. package/dist/agents/subagent-spawn.js.map +1 -1
  159. package/dist/agents/tools/cron-tool.d.ts.map +1 -1
  160. package/dist/agents/tools/cron-tool.js +4 -1
  161. package/dist/agents/tools/cron-tool.js.map +1 -1
  162. package/dist/agents/tools/discord-action-tool.d.ts +224 -0
  163. package/dist/agents/tools/discord-action-tool.d.ts.map +1 -0
  164. package/dist/agents/tools/discord-action-tool.js +848 -0
  165. package/dist/agents/tools/discord-action-tool.js.map +1 -0
  166. package/dist/agents/tools/registry.d.ts.map +1 -1
  167. package/dist/agents/tools/registry.js +21 -0
  168. package/dist/agents/tools/registry.js.map +1 -1
  169. package/dist/agents/tools/sessions/index.d.ts +8 -0
  170. package/dist/agents/tools/sessions/index.d.ts.map +1 -1
  171. package/dist/agents/tools/sessions/index.js +15 -3
  172. package/dist/agents/tools/sessions/index.js.map +1 -1
  173. package/dist/buildstamp.json +1 -1
  174. package/dist/cli/commands/channels.d.ts +2 -0
  175. package/dist/cli/commands/channels.d.ts.map +1 -1
  176. package/dist/cli/commands/channels.js +58 -1
  177. package/dist/cli/commands/channels.js.map +1 -1
  178. package/dist/core/auth-bridge.d.ts +1 -0
  179. package/dist/core/auth-bridge.d.ts.map +1 -1
  180. package/dist/core/auth-bridge.js +46 -1
  181. package/dist/core/auth-bridge.js.map +1 -1
  182. package/dist/core/server.d.ts.map +1 -1
  183. package/dist/core/server.js +18 -2
  184. package/dist/core/server.js.map +1 -1
  185. package/dist/cron/isolated-agent/run-executor.d.ts +11 -0
  186. package/dist/cron/isolated-agent/run-executor.d.ts.map +1 -1
  187. package/dist/cron/isolated-agent/run-executor.js +20 -4
  188. package/dist/cron/isolated-agent/run-executor.js.map +1 -1
  189. package/dist/cron/types.d.ts +8 -0
  190. package/dist/cron/types.d.ts.map +1 -1
  191. package/dist/system-prompt/assembler.d.ts.map +1 -1
  192. package/dist/system-prompt/assembler.js +4 -2
  193. package/dist/system-prompt/assembler.js.map +1 -1
  194. package/package.json +2 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"security-doctor.js","sourceRoot":"","sources":["../../../../src/agents/channels/discord/security-doctor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,qFAAqF;AACrF,MAAM,QAAQ,GAAG,MAAM,CAAC,gBAAgB,CAAC,CAAC,WAAW;AAErD;;;;;;GAMG;AACH,MAAM,UAAU,0BAA0B,CAAC,GAAW;IACrD,MAAM,IAAI,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAChC,IAAI,CAAC,IAAI,IAAI,IAAI,KAAK,GAAG;QAAE,OAAO,KAAK,CAAC;IACxC,6EAA6E;IAC7E,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACnE,IAAI,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC;QAAE,OAAO,KAAK,CAAC;IAC/C,KAAK,MAAM,MAAM,IAAI,CAAC,UAAU,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC;QACnD,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;YAAE,SAAS;QACvC,gFAAgF;QAChF,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,CAAC;IACtD,CAAC;IACD,gDAAgD;IAChD,OAAO,IAAI,CAAC;AACb,CAAC;AAYD,qFAAqF;AACrF,SAAS,iBAAiB,CAAC,IAAa,EAAE,IAAY,EAAE,GAA6B;IACpF,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC9B,0EAA0E;QAC1E,wEAAwE;QACxE,2EAA2E;QAC3E,2EAA2E;QAC3E,iEAAiE;QACjE,2EAA2E;QAC3E,wEAAwE;QACxE,IAAI,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;YAC5C,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,IAAI,QAAQ,EAAE,CAAC,CAAC;QAC/D,CAAC;QACD,OAAO;IACR,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QACzB,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,KAAK,EAAE,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;QAC3E,OAAO;IACR,CAAC;IACD,IAAI,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAA+B,CAAC,EAAE,CAAC;YAC5E,iBAAiB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAC9D,CAAC;IACF,CAAC;AACF,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,2BAA2B,CAAC,aAAsB,EAAE,QAAQ,GAAG,kBAAkB;IAChG,MAAM,GAAG,GAA6B,EAAE,CAAC;IACzC,IAAI,CAAC,aAAa,IAAI,OAAO,aAAa,KAAK,QAAQ;QAAE,OAAO,GAAG,CAAC;IACpE,iBAAiB,CAAC,aAAa,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAC;IAChD,OAAO,GAAG,CAAC;AACZ,CAAC"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Discord structured status-issues — the rollup the central status surface
3
+ * renders (`collectStatusIssues` on the plugin's status adapter).
4
+ *
5
+ * Two issue families:
6
+ * - `intent` — the privileged MESSAGE CONTENT intent is disabled (the bot
7
+ * connects but can't read normal channel messages).
8
+ * - `permissions` — a channel-permission audit found the bot missing
9
+ * `ViewChannel` / `SendMessages` (or couldn't be evaluated),
10
+ * OR some configured channel ids weren't numeric snowflakes
11
+ * (the audit can only check numeric ids).
12
+ *
13
+ * Returns the central `ChannelStatusIssue[]` (`{ accountId, severity, message }`)
14
+ * so the rollup view + `brigade doctor` render Discord health alongside other
15
+ * channels. Pure + total — given a probe result + an audit result, it derives
16
+ * the rows; no I/O of its own.
17
+ */
18
+ import type { ChannelStatusIssue } from "../types.core.js";
19
+ import type { DiscordPermissionAuditResult } from "./permission-audit.js";
20
+ import type { DiscordProbeResult } from "./probe.js";
21
+ /** One account's diagnostics fed to {@link collectDiscordStatusIssues}. */
22
+ export interface DiscordStatusAccount {
23
+ accountId: string;
24
+ /** The `/users/@me` probe result (carries the decoded intents). */
25
+ probe?: DiscordProbeResult;
26
+ /** The channel-permission audit result, when one was run. */
27
+ audit?: DiscordPermissionAuditResult;
28
+ }
29
+ /**
30
+ * Derive the structured status issues for one or more Discord accounts. Emits an
31
+ * `intent` warning when MESSAGE CONTENT is disabled, an `error` per channel that
32
+ * failed the permission audit, and a `warn` when some configured channel ids
33
+ * weren't numeric (so couldn't be audited). Accounts with clean diagnostics
34
+ * contribute nothing.
35
+ */
36
+ export declare function collectDiscordStatusIssues(accounts: DiscordStatusAccount[]): ChannelStatusIssue[];
37
+ //# sourceMappingURL=status-issues.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status-issues.d.ts","sourceRoot":"","sources":["../../../../src/agents/channels/discord/status-issues.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,uBAAuB,CAAC;AAC1E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD,2EAA2E;AAC3E,MAAM,WAAW,oBAAoB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,KAAK,CAAC,EAAE,kBAAkB,CAAC;IAC3B,6DAA6D;IAC7D,KAAK,CAAC,EAAE,4BAA4B,CAAC;CACrC;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,oBAAoB,EAAE,GAAG,kBAAkB,EAAE,CAyCjG"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Discord structured status-issues — the rollup the central status surface
3
+ * renders (`collectStatusIssues` on the plugin's status adapter).
4
+ *
5
+ * Two issue families:
6
+ * - `intent` — the privileged MESSAGE CONTENT intent is disabled (the bot
7
+ * connects but can't read normal channel messages).
8
+ * - `permissions` — a channel-permission audit found the bot missing
9
+ * `ViewChannel` / `SendMessages` (or couldn't be evaluated),
10
+ * OR some configured channel ids weren't numeric snowflakes
11
+ * (the audit can only check numeric ids).
12
+ *
13
+ * Returns the central `ChannelStatusIssue[]` (`{ accountId, severity, message }`)
14
+ * so the rollup view + `brigade doctor` render Discord health alongside other
15
+ * channels. Pure + total — given a probe result + an audit result, it derives
16
+ * the rows; no I/O of its own.
17
+ */
18
+ /**
19
+ * Derive the structured status issues for one or more Discord accounts. Emits an
20
+ * `intent` warning when MESSAGE CONTENT is disabled, an `error` per channel that
21
+ * failed the permission audit, and a `warn` when some configured channel ids
22
+ * weren't numeric (so couldn't be audited). Accounts with clean diagnostics
23
+ * contribute nothing.
24
+ */
25
+ export function collectDiscordStatusIssues(accounts) {
26
+ const issues = [];
27
+ for (const account of accounts ?? []) {
28
+ const accountId = (account?.accountId ?? "").trim();
29
+ if (!accountId)
30
+ continue;
31
+ // ── intent (message content) ──
32
+ const intents = account.probe?.privilegedIntents;
33
+ const messageContent = intents?.messageContent ?? account.probe?.messageContentIntent;
34
+ if (messageContent === "disabled") {
35
+ issues.push({
36
+ accountId,
37
+ severity: "warn",
38
+ message: "Message Content Intent is disabled — the bot can't read normal channel messages. Enable it in the Discord Developer Portal → Bot → Privileged Gateway Intents, or run mention-only.",
39
+ });
40
+ }
41
+ // ── permissions (channel audit) ──
42
+ const audit = account.audit;
43
+ if (audit) {
44
+ if (audit.unresolvedChannels > 0) {
45
+ issues.push({
46
+ accountId,
47
+ severity: "warn",
48
+ message: `Some configured guild channels are not numeric ids (unresolvedChannels=${audit.unresolvedChannels}). The permission audit can only check numeric channel ids — use numeric ids in channels.discord.guilds.*.channels.`,
49
+ });
50
+ }
51
+ for (const channel of audit.channels ?? []) {
52
+ if (channel.ok)
53
+ continue;
54
+ const missing = channel.missingRequired?.length ? ` missing ${channel.missingRequired.join(", ")}` : "";
55
+ const error = channel.error ? `: ${channel.error}` : "";
56
+ issues.push({
57
+ accountId,
58
+ severity: "error",
59
+ message: `Channel ${channel.channelId} permission check failed.${missing}${error} Ensure the bot role can view + send in this channel (and that channel overrides don't deny it).`,
60
+ });
61
+ }
62
+ }
63
+ }
64
+ return issues;
65
+ }
66
+ //# sourceMappingURL=status-issues.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status-issues.js","sourceRoot":"","sources":["../../../../src/agents/channels/discord/status-issues.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAeH;;;;;;GAMG;AACH,MAAM,UAAU,0BAA0B,CAAC,QAAgC;IAC1E,MAAM,MAAM,GAAyB,EAAE,CAAC;IACxC,KAAK,MAAM,OAAO,IAAI,QAAQ,IAAI,EAAE,EAAE,CAAC;QACtC,MAAM,SAAS,GAAG,CAAC,OAAO,EAAE,SAAS,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QACpD,IAAI,CAAC,SAAS;YAAE,SAAS;QAEzB,iCAAiC;QACjC,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,EAAE,iBAAiB,CAAC;QACjD,MAAM,cAAc,GAAG,OAAO,EAAE,cAAc,IAAI,OAAO,CAAC,KAAK,EAAE,oBAAoB,CAAC;QACtF,IAAI,cAAc,KAAK,UAAU,EAAE,CAAC;YACnC,MAAM,CAAC,IAAI,CAAC;gBACX,SAAS;gBACT,QAAQ,EAAE,MAAM;gBAChB,OAAO,EACN,qLAAqL;aACtL,CAAC,CAAC;QACJ,CAAC;QAED,oCAAoC;QACpC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QAC5B,IAAI,KAAK,EAAE,CAAC;YACX,IAAI,KAAK,CAAC,kBAAkB,GAAG,CAAC,EAAE,CAAC;gBAClC,MAAM,CAAC,IAAI,CAAC;oBACX,SAAS;oBACT,QAAQ,EAAE,MAAM;oBAChB,OAAO,EAAE,0EAA0E,KAAK,CAAC,kBAAkB,qHAAqH;iBAChO,CAAC,CAAC;YACJ,CAAC;YACD,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,QAAQ,IAAI,EAAE,EAAE,CAAC;gBAC5C,IAAI,OAAO,CAAC,EAAE;oBAAE,SAAS;gBACzB,MAAM,OAAO,GAAG,OAAO,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC,CAAC,YAAY,OAAO,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACxG,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACxD,MAAM,CAAC,IAAI,CAAC;oBACX,SAAS;oBACT,QAAQ,EAAE,OAAO;oBACjB,OAAO,EAAE,WAAW,OAAO,CAAC,SAAS,4BAA4B,OAAO,GAAG,KAAK,kGAAkG;iBAClL,CAAC,CAAC;YACJ,CAAC;QACF,CAAC;IACF,CAAC;IACD,OAAO,MAAM,CAAC;AACf,CAAC"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Phase 6 — Discord sub-agent thread-binding STORE (dependency-light).
3
+ *
4
+ * The binding registry lives in its own module — separate from the
5
+ * materializer (`subagent-thread-binding.ts`, which transitively pulls in the
6
+ * Discord REST + connection surface) — so hot paths can cheaply check "does
7
+ * this child have a thread binding?" WITHOUT importing discord.js / the REST
8
+ * layer. The completion bridge consults `hasDiscordSubagentThreadBinding`
9
+ * synchronously and only lazy-imports the heavy materializer when a farewell
10
+ * is actually owed.
11
+ *
12
+ * Brigade idiom: the binding IS session metadata — an in-process map keyed by
13
+ * the child session key. No bindings file, no webhook pool, no ACP.
14
+ */
15
+ /**
16
+ * One stored sub-agent → thread binding. The child session key is the map key;
17
+ * this record carries what the completion + farewell paths need to deliver into
18
+ * the thread without re-resolving the spawn origin.
19
+ */
20
+ export interface DiscordSubagentThreadBinding {
21
+ /** The child sub-agent's session key (`…:thread:<id>`). The map key. */
22
+ childSessionKey: string;
23
+ /** The created Discord thread channel id. */
24
+ threadId: string;
25
+ /** The parent (originating) Discord channel id the thread hangs under. */
26
+ parentChannelId: string;
27
+ /** Discord account id (multi-account). */
28
+ accountId: string;
29
+ /** Resolved agent id running in the thread (for the farewell text). */
30
+ agentId: string;
31
+ /** Optional spawn label (surfaced in intro/farewell). */
32
+ label?: string;
33
+ /** Epoch ms the binding was created. */
34
+ boundAt: number;
35
+ }
36
+ export declare function rememberDiscordSubagentThreadBinding(binding: DiscordSubagentThreadBinding): void;
37
+ export declare function getDiscordSubagentThreadBinding(childSessionKey: string | undefined | null): DiscordSubagentThreadBinding | undefined;
38
+ /** Cheap synchronous presence check — no heavy module load needed. */
39
+ export declare function hasDiscordSubagentThreadBinding(childSessionKey: string | undefined | null): boolean;
40
+ export declare function forgetDiscordSubagentThreadBinding(childSessionKey: string | undefined | null): boolean;
41
+ export declare function listDiscordSubagentThreadBindings(): DiscordSubagentThreadBinding[];
42
+ /**
43
+ * Drop any binding pointing at a given thread id (Fix 6). The THREAD_UPDATE
44
+ * archive listener only knows the thread id (not the child session key), so it
45
+ * forgets by thread id to prevent a leaked binding when a thread is archived.
46
+ * Returns the number of bindings dropped (0 when none matched).
47
+ */
48
+ export declare function forgetDiscordSubagentThreadBindingByThreadId(threadId: string | undefined | null): number;
49
+ /**
50
+ * Startup reconcile: drop any binding whose child session is no longer known to
51
+ * the spawn registry (the run completed + was archived across a reload). Cheap —
52
+ * a Map walk. Returns the number dropped. Never deletes the thread itself.
53
+ */
54
+ export declare function reconcileDiscordSubagentThreadBindings(isChildSessionLive: (childSessionKey: string) => boolean): number;
55
+ /** Test-only — wipe the binding registry. */
56
+ export declare function resetDiscordSubagentThreadBindingsForTests(): void;
57
+ //# sourceMappingURL=subagent-thread-binding-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subagent-thread-binding-store.d.ts","sourceRoot":"","sources":["../../../../src/agents/channels/discord/subagent-thread-binding-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH;;;;GAIG;AACH,MAAM,WAAW,4BAA4B;IAC5C,wEAAwE;IACxE,eAAe,EAAE,MAAM,CAAC;IACxB,6CAA6C;IAC7C,QAAQ,EAAE,MAAM,CAAC;IACjB,0EAA0E;IAC1E,eAAe,EAAE,MAAM,CAAC;IACxB,0CAA0C;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,uEAAuE;IACvE,OAAO,EAAE,MAAM,CAAC;IAChB,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wCAAwC;IACxC,OAAO,EAAE,MAAM,CAAC;CAChB;AAeD,wBAAgB,oCAAoC,CAAC,OAAO,EAAE,4BAA4B,GAAG,IAAI,CAIhG;AAED,wBAAgB,+BAA+B,CAC9C,eAAe,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GACxC,4BAA4B,GAAG,SAAS,CAI1C;AAED,sEAAsE;AACtE,wBAAgB,+BAA+B,CAAC,eAAe,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,OAAO,CAInG;AAED,wBAAgB,kCAAkC,CAAC,eAAe,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,OAAO,CAItG;AAED,wBAAgB,iCAAiC,IAAI,4BAA4B,EAAE,CAElF;AAED;;;;;GAKG;AACH,wBAAgB,4CAA4C,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CAYxG;AAED;;;;GAIG;AACH,wBAAgB,sCAAsC,CACrD,kBAAkB,EAAE,CAAC,eAAe,EAAE,MAAM,KAAK,OAAO,GACtD,MAAM,CAiBR;AAED,6CAA6C;AAC7C,wBAAgB,0CAA0C,IAAI,IAAI,CAEjE"}
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Phase 6 — Discord sub-agent thread-binding STORE (dependency-light).
3
+ *
4
+ * The binding registry lives in its own module — separate from the
5
+ * materializer (`subagent-thread-binding.ts`, which transitively pulls in the
6
+ * Discord REST + connection surface) — so hot paths can cheaply check "does
7
+ * this child have a thread binding?" WITHOUT importing discord.js / the REST
8
+ * layer. The completion bridge consults `hasDiscordSubagentThreadBinding`
9
+ * synchronously and only lazy-imports the heavy materializer when a farewell
10
+ * is actually owed.
11
+ *
12
+ * Brigade idiom: the binding IS session metadata — an in-process map keyed by
13
+ * the child session key. No bindings file, no webhook pool, no ACP.
14
+ */
15
+ import { resolveGlobalSingleton } from "../../../shared/global-singleton.js";
16
+ const STATE_KEY = Symbol.for("brigade.discord.subagentThreadBindings");
17
+ function getState() {
18
+ return resolveGlobalSingleton(STATE_KEY, () => ({
19
+ byChildSessionKey: new Map(),
20
+ }));
21
+ }
22
+ export function rememberDiscordSubagentThreadBinding(binding) {
23
+ const key = binding.childSessionKey?.trim();
24
+ if (!key)
25
+ return;
26
+ getState().byChildSessionKey.set(key, binding);
27
+ }
28
+ export function getDiscordSubagentThreadBinding(childSessionKey) {
29
+ const key = childSessionKey?.trim();
30
+ if (!key)
31
+ return undefined;
32
+ return getState().byChildSessionKey.get(key);
33
+ }
34
+ /** Cheap synchronous presence check — no heavy module load needed. */
35
+ export function hasDiscordSubagentThreadBinding(childSessionKey) {
36
+ const key = childSessionKey?.trim();
37
+ if (!key)
38
+ return false;
39
+ return getState().byChildSessionKey.has(key);
40
+ }
41
+ export function forgetDiscordSubagentThreadBinding(childSessionKey) {
42
+ const key = childSessionKey?.trim();
43
+ if (!key)
44
+ return false;
45
+ return getState().byChildSessionKey.delete(key);
46
+ }
47
+ export function listDiscordSubagentThreadBindings() {
48
+ return [...getState().byChildSessionKey.values()];
49
+ }
50
+ /**
51
+ * Drop any binding pointing at a given thread id (Fix 6). The THREAD_UPDATE
52
+ * archive listener only knows the thread id (not the child session key), so it
53
+ * forgets by thread id to prevent a leaked binding when a thread is archived.
54
+ * Returns the number of bindings dropped (0 when none matched).
55
+ */
56
+ export function forgetDiscordSubagentThreadBindingByThreadId(threadId) {
57
+ const id = threadId?.trim();
58
+ if (!id)
59
+ return 0;
60
+ const state = getState();
61
+ let dropped = 0;
62
+ for (const [key, binding] of state.byChildSessionKey) {
63
+ if (binding.threadId === id) {
64
+ state.byChildSessionKey.delete(key);
65
+ dropped += 1;
66
+ }
67
+ }
68
+ return dropped;
69
+ }
70
+ /**
71
+ * Startup reconcile: drop any binding whose child session is no longer known to
72
+ * the spawn registry (the run completed + was archived across a reload). Cheap —
73
+ * a Map walk. Returns the number dropped. Never deletes the thread itself.
74
+ */
75
+ export function reconcileDiscordSubagentThreadBindings(isChildSessionLive) {
76
+ const state = getState();
77
+ let dropped = 0;
78
+ for (const [key] of state.byChildSessionKey) {
79
+ let live = false;
80
+ try {
81
+ live = isChildSessionLive(key);
82
+ }
83
+ catch {
84
+ // On a probe error, keep the binding (fail-open).
85
+ live = true;
86
+ }
87
+ if (!live) {
88
+ state.byChildSessionKey.delete(key);
89
+ dropped += 1;
90
+ }
91
+ }
92
+ return dropped;
93
+ }
94
+ /** Test-only — wipe the binding registry. */
95
+ export function resetDiscordSubagentThreadBindingsForTests() {
96
+ getState().byChildSessionKey.clear();
97
+ }
98
+ //# sourceMappingURL=subagent-thread-binding-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subagent-thread-binding-store.js","sourceRoot":"","sources":["../../../../src/agents/channels/discord/subagent-thread-binding-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Phase 6 — Discord sub-agent thread-binding (Brigade-native materializer).
3
+ *
4
+ * When a sub-agent is spawned with `thread: true` from a Discord conversation,
5
+ * Brigade runs that child in its OWN Discord thread: a fresh thread is created
6
+ * off the originating channel, an intro is posted, the child's session is routed
7
+ * INTO the thread (its key becomes `…:thread:<id>`), the child's completion reply
8
+ * lands in that thread, and a farewell is posted when the child ends.
9
+ *
10
+ * This is the Brigade idiom, NOT the heavier upstream design:
11
+ * - The binding is held as SESSION metadata (an in-process map keyed by the
12
+ * child session key) — there is no separate bindings file, no webhook pool,
13
+ * no ACP runtime, no session-binding adapter registry. Brigade is
14
+ * sub-agent-only; ACP is permanently dropped.
15
+ * - Intro + farewell go out via the BOT REST send (the same self-contained
16
+ * `rest-actions.ts` surface the `discord_action` tool uses) — no webhooks.
17
+ * - Everything here is gated on the spawn origin being Discord AND
18
+ * `thread: true`. A non-thread spawn or a non-Discord-origin spawn never
19
+ * enters this module, so those paths are byte-identical to before.
20
+ *
21
+ * The binding map survives for the process lifetime and is keyed by the child
22
+ * session key so a reload that re-reads the registry can reconcile (drop a
23
+ * binding whose child run no longer exists). It is deliberately lightweight —
24
+ * the central idle-reaper owns thread/session teardown; this module never
25
+ * deletes a thread.
26
+ */
27
+ import type { BrigadeConfig } from "../../../config/io.js";
28
+ import type { DiscordSubagentThreadBinding } from "./subagent-thread-binding-store.js";
29
+ export { getDiscordSubagentThreadBinding, hasDiscordSubagentThreadBinding, rememberDiscordSubagentThreadBinding, forgetDiscordSubagentThreadBinding, listDiscordSubagentThreadBindings, reconcileDiscordSubagentThreadBindings, resetDiscordSubagentThreadBindingsForTests, } from "./subagent-thread-binding-store.js";
30
+ export type { DiscordSubagentThreadBinding } from "./subagent-thread-binding-store.js";
31
+ /** Intro posted into the freshly-created thread (best-effort, bot send). */
32
+ export declare function buildSubagentThreadIntro(params: {
33
+ agentId: string;
34
+ task: string;
35
+ label?: string;
36
+ }): string;
37
+ /** Farewell posted into the thread when the child ends (best-effort, bot send). */
38
+ export declare function buildSubagentThreadFarewell(params: {
39
+ agentId: string;
40
+ label?: string;
41
+ outcome?: "ok" | "error" | "timeout" | "abort";
42
+ }): string;
43
+ export interface MaterializeDiscordSubagentThreadParams {
44
+ /** Originating Discord channel id (the spawn's `to` / `conversationId`). */
45
+ parentChannelId: string;
46
+ /** Discord account id (multi-account). */
47
+ accountId?: string;
48
+ /** Base child session key the spawn engine minted (no `:thread:` suffix yet). */
49
+ baseChildSessionKey: string;
50
+ /** Resolved agent id of the child. */
51
+ agentId: string;
52
+ /** The sub-agent task (drives intro text + thread name). */
53
+ task: string;
54
+ /** Optional spawn label. */
55
+ label?: string;
56
+ /** Config accessor — defaults to `readConfigOrInit()`. Injectable for tests. */
57
+ cfg?: BrigadeConfig;
58
+ /** Injectable fetch (tests stub the REST surface). */
59
+ fetchImpl?: typeof fetch;
60
+ /** Injectable token override (tests). Falls back to `resolveDiscordBotToken(cfg)`. */
61
+ botToken?: string;
62
+ }
63
+ export interface MaterializeDiscordSubagentThreadResult {
64
+ /** The created thread channel id. */
65
+ threadId: string;
66
+ /** The child session key re-rooted into the thread (`…:thread:<id>`). */
67
+ childSessionKey: string;
68
+ /** The binding stored as session metadata. */
69
+ binding: DiscordSubagentThreadBinding;
70
+ }
71
+ /**
72
+ * Create a Discord thread for a `thread: true` sub-agent, post the intro,
73
+ * re-root the child session into the thread, and stash the binding.
74
+ *
75
+ * Returns `null` (and logs best-effort) when the thread can't be created —
76
+ * the caller falls back to a non-threaded spawn (the child still runs, just in
77
+ * the parent channel's session) so a Discord hiccup never fails the spawn.
78
+ */
79
+ export declare function materializeDiscordSubagentThread(params: MaterializeDiscordSubagentThreadParams): Promise<MaterializeDiscordSubagentThreadResult | null>;
80
+ export interface SendDiscordSubagentFarewellParams {
81
+ childSessionKey: string;
82
+ outcome?: "ok" | "error" | "timeout" | "abort";
83
+ cfg?: BrigadeConfig;
84
+ fetchImpl?: typeof fetch;
85
+ botToken?: string;
86
+ /** Drop the binding after sending (default true). */
87
+ forget?: boolean;
88
+ }
89
+ /**
90
+ * Best-effort farewell into the bound thread when the child ends, then drop the
91
+ * binding (the thread itself is left for the central idle-reaper). No-op when no
92
+ * binding is registered for the child session key.
93
+ */
94
+ export declare function sendDiscordSubagentThreadFarewell(params: SendDiscordSubagentFarewellParams): Promise<boolean>;
95
+ //# sourceMappingURL=subagent-thread-binding.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subagent-thread-binding.d.ts","sourceRoot":"","sources":["../../../../src/agents/channels/discord/subagent-thread-binding.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAW3D,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,oCAAoC,CAAC;AAKvF,OAAO,EACN,+BAA+B,EAC/B,+BAA+B,EAC/B,oCAAoC,EACpC,kCAAkC,EAClC,iCAAiC,EACjC,sCAAsC,EACtC,0CAA0C,GAC1C,MAAM,oCAAoC,CAAC;AAC5C,YAAY,EAAE,4BAA4B,EAAE,MAAM,oCAAoC,CAAC;AAkBvF,4EAA4E;AAC5E,wBAAgB,wBAAwB,CAAC,MAAM,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAK1G;AAED,mFAAmF;AACnF,wBAAgB,2BAA2B,CAAC,MAAM,EAAE;IACnD,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,IAAI,GAAG,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC;CAC/C,GAAG,MAAM,CAYT;AAID,MAAM,WAAW,sCAAsC;IACtD,4EAA4E;IAC5E,eAAe,EAAE,MAAM,CAAC;IACxB,0CAA0C;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iFAAiF;IACjF,mBAAmB,EAAE,MAAM,CAAC;IAC5B,sCAAsC;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAC;IACb,4BAA4B;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gFAAgF;IAChF,GAAG,CAAC,EAAE,aAAa,CAAC;IACpB,sDAAsD;IACtD,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,sFAAsF;IACtF,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,sCAAsC;IACtD,qCAAqC;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,yEAAyE;IACzE,eAAe,EAAE,MAAM,CAAC;IACxB,8CAA8C;IAC9C,OAAO,EAAE,4BAA4B,CAAC;CACtC;AAED;;;;;;;GAOG;AACH,wBAAsB,gCAAgC,CACrD,MAAM,EAAE,sCAAsC,GAC5C,OAAO,CAAC,sCAAsC,GAAG,IAAI,CAAC,CAmGxD;AAID,MAAM,WAAW,iCAAiC;IACjD,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,IAAI,GAAG,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC;IAC/C,GAAG,CAAC,EAAE,aAAa,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qDAAqD;IACrD,MAAM,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAsB,iCAAiC,CACtD,MAAM,EAAE,iCAAiC,GACvC,OAAO,CAAC,OAAO,CAAC,CAmClB"}
@@ -0,0 +1,208 @@
1
+ /**
2
+ * Phase 6 — Discord sub-agent thread-binding (Brigade-native materializer).
3
+ *
4
+ * When a sub-agent is spawned with `thread: true` from a Discord conversation,
5
+ * Brigade runs that child in its OWN Discord thread: a fresh thread is created
6
+ * off the originating channel, an intro is posted, the child's session is routed
7
+ * INTO the thread (its key becomes `…:thread:<id>`), the child's completion reply
8
+ * lands in that thread, and a farewell is posted when the child ends.
9
+ *
10
+ * This is the Brigade idiom, NOT the heavier upstream design:
11
+ * - The binding is held as SESSION metadata (an in-process map keyed by the
12
+ * child session key) — there is no separate bindings file, no webhook pool,
13
+ * no ACP runtime, no session-binding adapter registry. Brigade is
14
+ * sub-agent-only; ACP is permanently dropped.
15
+ * - Intro + farewell go out via the BOT REST send (the same self-contained
16
+ * `rest-actions.ts` surface the `discord_action` tool uses) — no webhooks.
17
+ * - Everything here is gated on the spawn origin being Discord AND
18
+ * `thread: true`. A non-thread spawn or a non-Discord-origin spawn never
19
+ * enters this module, so those paths are byte-identical to before.
20
+ *
21
+ * The binding map survives for the process lifetime and is keyed by the child
22
+ * session key so a reload that re-reads the registry can reconcile (drop a
23
+ * binding whose child run no longer exists). It is deliberately lightweight —
24
+ * the central idle-reaper owns thread/session teardown; this module never
25
+ * deletes a thread.
26
+ */
27
+ import { createSubsystemLogger } from "../../../logging/subsystem-logger.js";
28
+ import { resolveThreadSessionKeys } from "../../routing/session-key.js";
29
+ import { resolveDiscordBotToken } from "./account-config.js";
30
+ import { sanitizeThreadName } from "./connection.js";
31
+ import { sendMessage, threadCreate } from "./rest-actions.js";
32
+ import { getDiscordSubagentThreadBinding, rememberDiscordSubagentThreadBinding, forgetDiscordSubagentThreadBinding, } from "./subagent-thread-binding-store.js";
33
+ // Re-export the dependency-light store surface so existing imports of the
34
+ // registry from this module keep working (the store is split out so hot paths
35
+ // can check for a binding without pulling in the Discord REST/connection deps).
36
+ export { getDiscordSubagentThreadBinding, hasDiscordSubagentThreadBinding, rememberDiscordSubagentThreadBinding, forgetDiscordSubagentThreadBinding, listDiscordSubagentThreadBindings, reconcileDiscordSubagentThreadBindings, resetDiscordSubagentThreadBindingsForTests, } from "./subagent-thread-binding-store.js";
37
+ const log = createSubsystemLogger("agents/channels/discord/subagent-thread-binding");
38
+ /**
39
+ * discord.js `ChannelType.PublicThread` (11) — the type a STANDALONE thread is
40
+ * created with via `POST /channels/{id}/threads`. (Type 12 is PRIVATE_THREAD.)
41
+ * A standalone create on a normal text channel must NOT carry a forum-only
42
+ * `message` starter field — that 400s. We create the empty thread, then post the
43
+ * intro as a separate message INTO it.
44
+ */
45
+ const DISCORD_CHANNEL_TYPE_PUBLIC_THREAD = 11;
46
+ /** Auto-archive a sub-agent thread after 24h of inactivity (Discord-allowed value). */
47
+ const DISCORD_SUBAGENT_THREAD_AUTO_ARCHIVE_MINUTES = 1_440;
48
+ /* ───────────────────────── intro / farewell text ───────────────────────── */
49
+ /** Intro posted into the freshly-created thread (best-effort, bot send). */
50
+ export function buildSubagentThreadIntro(params) {
51
+ const who = params.label?.trim() ? `${params.agentId} ("${params.label.trim()}")` : params.agentId;
52
+ const task = params.task.trim();
53
+ const summary = task.length > 280 ? `${task.slice(0, 277)}…` : task;
54
+ return `🧵 ${who} is on it: ${summary}`;
55
+ }
56
+ /** Farewell posted into the thread when the child ends (best-effort, bot send). */
57
+ export function buildSubagentThreadFarewell(params) {
58
+ const who = params.label?.trim() ? `${params.agentId} ("${params.label.trim()}")` : params.agentId;
59
+ switch (params.outcome) {
60
+ case "error":
61
+ return `✗ ${who} ended with an error.`;
62
+ case "timeout":
63
+ return `⏱ ${who} timed out.`;
64
+ case "abort":
65
+ return `⊘ ${who} was stopped.`;
66
+ default:
67
+ return `✓ ${who} done.`;
68
+ }
69
+ }
70
+ /**
71
+ * Create a Discord thread for a `thread: true` sub-agent, post the intro,
72
+ * re-root the child session into the thread, and stash the binding.
73
+ *
74
+ * Returns `null` (and logs best-effort) when the thread can't be created —
75
+ * the caller falls back to a non-threaded spawn (the child still runs, just in
76
+ * the parent channel's session) so a Discord hiccup never fails the spawn.
77
+ */
78
+ export async function materializeDiscordSubagentThread(params) {
79
+ const parentChannelId = params.parentChannelId?.trim();
80
+ if (!parentChannelId)
81
+ return null;
82
+ let cfg = params.cfg;
83
+ if (!cfg) {
84
+ try {
85
+ const { readConfigOrInit } = await import("../../../config/io.js");
86
+ cfg = readConfigOrInit();
87
+ }
88
+ catch (err) {
89
+ log.warn("subagent thread materialize: config load failed", {
90
+ error: err instanceof Error ? err.message : String(err),
91
+ });
92
+ return null;
93
+ }
94
+ }
95
+ const accountId = params.accountId?.trim() || "default";
96
+ const token = (params.botToken ?? (cfg ? resolveDiscordBotToken(cfg, accountId) : "")).trim();
97
+ if (!token) {
98
+ log.warn("subagent thread materialize: no Discord bot token", { accountId });
99
+ return null;
100
+ }
101
+ const restOpts = {
102
+ token,
103
+ ...(params.fetchImpl ? { fetchImpl: params.fetchImpl } : {}),
104
+ };
105
+ const threadName = sanitizeThreadName(params.label?.trim() ? `${params.agentId}: ${params.label.trim()}` : `${params.agentId}: ${params.task}`, params.baseChildSessionKey);
106
+ const intro = buildSubagentThreadIntro({
107
+ agentId: params.agentId,
108
+ task: params.task,
109
+ ...(params.label ? { label: params.label } : {}),
110
+ });
111
+ let threadId = "";
112
+ try {
113
+ // Create a STANDALONE public thread off the text channel. We deliberately
114
+ // DON'T pass a starter `content` — that maps to the forum/media-only
115
+ // `message` field, which Discord 400s on a normal text channel (the bug
116
+ // the verification caught). The intro is posted as a SEPARATE message into
117
+ // the thread below.
118
+ const created = (await threadCreate({
119
+ channelId: parentChannelId,
120
+ name: threadName,
121
+ type: DISCORD_CHANNEL_TYPE_PUBLIC_THREAD,
122
+ autoArchiveMinutes: DISCORD_SUBAGENT_THREAD_AUTO_ARCHIVE_MINUTES,
123
+ }, restOpts));
124
+ threadId = typeof created?.id === "string" ? created.id.trim() : "";
125
+ }
126
+ catch (err) {
127
+ log.warn("subagent thread materialize: threadCreate failed", {
128
+ parentChannelId,
129
+ error: err instanceof Error ? err.message : String(err),
130
+ });
131
+ return null;
132
+ }
133
+ if (!threadId) {
134
+ log.warn("subagent thread materialize: threadCreate returned no id", { parentChannelId });
135
+ return null;
136
+ }
137
+ // Post the intro as a separate message INTO the newly-created thread (the
138
+ // thread id IS a channel id). Best-effort — a failed intro must not undo a
139
+ // successfully-created thread, so the spawn still binds + runs threaded.
140
+ try {
141
+ await sendMessage({ to: threadId, content: intro }, restOpts);
142
+ }
143
+ catch (err) {
144
+ log.warn("subagent thread materialize: intro send failed (thread still created)", {
145
+ threadId,
146
+ error: err instanceof Error ? err.message : String(err),
147
+ });
148
+ }
149
+ // Re-root the child session into the thread so its `:thread:<id>` session IS
150
+ // the thread (the central session-reaper handles idle thread sessions).
151
+ const { sessionKey: childSessionKey } = resolveThreadSessionKeys({
152
+ baseSessionKey: params.baseChildSessionKey,
153
+ threadId,
154
+ });
155
+ const binding = {
156
+ childSessionKey,
157
+ threadId,
158
+ parentChannelId,
159
+ accountId,
160
+ agentId: params.agentId,
161
+ ...(params.label?.trim() ? { label: params.label.trim() } : {}),
162
+ boundAt: Date.now(),
163
+ };
164
+ rememberDiscordSubagentThreadBinding(binding);
165
+ return { threadId, childSessionKey, binding };
166
+ }
167
+ /**
168
+ * Best-effort farewell into the bound thread when the child ends, then drop the
169
+ * binding (the thread itself is left for the central idle-reaper). No-op when no
170
+ * binding is registered for the child session key.
171
+ */
172
+ export async function sendDiscordSubagentThreadFarewell(params) {
173
+ const binding = getDiscordSubagentThreadBinding(params.childSessionKey);
174
+ if (!binding)
175
+ return false;
176
+ const forget = params.forget !== false;
177
+ try {
178
+ let cfg = params.cfg;
179
+ if (!cfg) {
180
+ const { readConfigOrInit } = await import("../../../config/io.js");
181
+ cfg = readConfigOrInit();
182
+ }
183
+ const token = (params.botToken ?? (cfg ? resolveDiscordBotToken(cfg, binding.accountId) : "")).trim();
184
+ if (!token) {
185
+ log.warn("subagent thread farewell: no Discord bot token", { accountId: binding.accountId });
186
+ return false;
187
+ }
188
+ const text = buildSubagentThreadFarewell({
189
+ agentId: binding.agentId,
190
+ ...(binding.label ? { label: binding.label } : {}),
191
+ ...(params.outcome ? { outcome: params.outcome } : {}),
192
+ });
193
+ await sendMessage({ to: binding.threadId, content: text }, { token, ...(params.fetchImpl ? { fetchImpl: params.fetchImpl } : {}) });
194
+ return true;
195
+ }
196
+ catch (err) {
197
+ log.warn("subagent thread farewell: send failed", {
198
+ childSessionKey: params.childSessionKey,
199
+ error: err instanceof Error ? err.message : String(err),
200
+ });
201
+ return false;
202
+ }
203
+ finally {
204
+ if (forget)
205
+ forgetDiscordSubagentThreadBinding(params.childSessionKey);
206
+ }
207
+ }
208
+ //# sourceMappingURL=subagent-thread-binding.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subagent-thread-binding.js","sourceRoot":"","sources":["../../../../src/agents/channels/discord/subagent-thread-binding.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Discord SYSTEM-event notes.
3
+ *
4
+ * A Discord message whose `type` is not Default (0) or Reply (19) is a SYSTEM
5
+ * event — a member join, a pin, a server boost, a thread-created marker, an
6
+ * invite reminder, and so on. These carry no user `content`, so without special
7
+ * handling they normalize to empty text and the inbound pipeline drops them —
8
+ * the agent never learns the event happened.
9
+ *
10
+ * This module maps the recognized discord.js `MessageType` enum values to a
11
+ * concise `Discord system: <note>` string the connection synthesizes as the
12
+ * inbound `text` so the event routes through the SAME pipeline as a normal
13
+ * message (no debounce). An UNMAPPED system type returns null → still dropped
14
+ * (we don't flood the agent with every obscure marker).
15
+ *
16
+ * The enum values are inlined (not imported from discord.js) so this module
17
+ * stays dependency-light + unit-testable without a live gateway — the runtime
18
+ * message carries the same numeric `type`.
19
+ */
20
+ import { type DiscordMessageLike } from "./inbound-extras.js";
21
+ /** True when the message type is one Brigade treats as plain user content (Default or Reply). */
22
+ export declare function isDiscordUserMessageType(type: number | undefined): boolean;
23
+ /**
24
+ * A concise `Discord system: <actor> <action> in <location>` note for a
25
+ * recognized system message, or null when the type is Default / Reply / an
26
+ * unmapped system type (so the caller drops it). `location` is a short channel
27
+ * descriptor the caller supplies (e.g. the conversation id) so the agent knows
28
+ * where the event happened.
29
+ */
30
+ export declare function resolveDiscordSystemEvent(message: Pick<DiscordMessageLike, "type" | "author" | "member">, location: string): string | null;
31
+ //# sourceMappingURL=system-events.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"system-events.d.ts","sourceRoot":"","sources":["../../../../src/agents/channels/discord/system-events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAA0B,KAAK,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAgCtF,iGAAiG;AACjG,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAG1E;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,IAAI,CAAC,kBAAkB,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAS1I"}