@geminixiang/mikan 1.0.0-beta.13 → 1.0.0-beta.14

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 (275) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +10 -10
  3. package/dist/adapters/discord/bot.d.ts.map +1 -1
  4. package/dist/adapters/discord/bot.js +26 -67
  5. package/dist/adapters/discord/bot.js.map +1 -1
  6. package/dist/adapters/discord/context.d.ts.map +1 -1
  7. package/dist/adapters/discord/context.js +17 -43
  8. package/dist/adapters/discord/context.js.map +1 -1
  9. package/dist/adapters/github/bot.d.ts +69 -1
  10. package/dist/adapters/github/bot.d.ts.map +1 -1
  11. package/dist/adapters/github/bot.js +428 -57
  12. package/dist/adapters/github/bot.js.map +1 -1
  13. package/dist/adapters/github/client.d.ts +32 -1
  14. package/dist/adapters/github/client.d.ts.map +1 -1
  15. package/dist/adapters/github/client.js +79 -0
  16. package/dist/adapters/github/client.js.map +1 -1
  17. package/dist/adapters/github/cloudbuild.d.ts +24 -0
  18. package/dist/adapters/github/cloudbuild.d.ts.map +1 -0
  19. package/dist/adapters/github/cloudbuild.js +71 -0
  20. package/dist/adapters/github/cloudbuild.js.map +1 -0
  21. package/dist/adapters/github/context.d.ts.map +1 -1
  22. package/dist/adapters/github/context.js +34 -31
  23. package/dist/adapters/github/context.js.map +1 -1
  24. package/dist/adapters/github/gcp-auth.d.ts +31 -0
  25. package/dist/adapters/github/gcp-auth.d.ts.map +1 -0
  26. package/dist/adapters/github/gcp-auth.js +196 -0
  27. package/dist/adapters/github/gcp-auth.js.map +1 -0
  28. package/dist/adapters/github/ids.d.ts +9 -0
  29. package/dist/adapters/github/ids.d.ts.map +1 -1
  30. package/dist/adapters/github/ids.js +14 -0
  31. package/dist/adapters/github/ids.js.map +1 -1
  32. package/dist/adapters/github/repo.d.ts +13 -2
  33. package/dist/adapters/github/repo.d.ts.map +1 -1
  34. package/dist/adapters/github/repo.js +80 -2
  35. package/dist/adapters/github/repo.js.map +1 -1
  36. package/dist/adapters/github/tool-pack.d.ts +3 -1
  37. package/dist/adapters/github/tool-pack.d.ts.map +1 -1
  38. package/dist/adapters/github/tool-pack.js +30 -4
  39. package/dist/adapters/github/tool-pack.js.map +1 -1
  40. package/dist/{tools/github-checks.d.ts → adapters/github/tools/checks.d.ts} +4 -2
  41. package/dist/adapters/github/tools/checks.d.ts.map +1 -0
  42. package/dist/{tools/github-checks.js → adapters/github/tools/checks.js} +28 -11
  43. package/dist/adapters/github/tools/checks.js.map +1 -0
  44. package/dist/adapters/github/tools/issue.d.ts +22 -0
  45. package/dist/adapters/github/tools/issue.d.ts.map +1 -0
  46. package/dist/adapters/github/tools/issue.js +59 -0
  47. package/dist/adapters/github/tools/issue.js.map +1 -0
  48. package/dist/{tools/github-pr.d.ts → adapters/github/tools/pr.d.ts} +2 -2
  49. package/dist/adapters/github/tools/pr.d.ts.map +1 -0
  50. package/dist/{tools/github-pr.js → adapters/github/tools/pr.js} +8 -4
  51. package/dist/adapters/github/tools/pr.js.map +1 -0
  52. package/dist/adapters/github/tools/read.d.ts +22 -0
  53. package/dist/adapters/github/tools/read.d.ts.map +1 -0
  54. package/dist/adapters/github/tools/read.js +152 -0
  55. package/dist/adapters/github/tools/read.js.map +1 -0
  56. package/dist/adapters/github/tools/review-reply.d.ts +19 -0
  57. package/dist/adapters/github/tools/review-reply.d.ts.map +1 -0
  58. package/dist/adapters/github/tools/review-reply.js +49 -0
  59. package/dist/adapters/github/tools/review-reply.js.map +1 -0
  60. package/dist/adapters/github/tools/sync.d.ts +17 -0
  61. package/dist/adapters/github/tools/sync.d.ts.map +1 -0
  62. package/dist/adapters/github/tools/sync.js +45 -0
  63. package/dist/adapters/github/tools/sync.js.map +1 -0
  64. package/dist/adapters/github/types.d.ts +174 -1
  65. package/dist/adapters/github/types.d.ts.map +1 -1
  66. package/dist/adapters/github/types.js.map +1 -1
  67. package/dist/adapters/intake.d.ts +16 -5
  68. package/dist/adapters/intake.d.ts.map +1 -1
  69. package/dist/adapters/intake.js +79 -22
  70. package/dist/adapters/intake.js.map +1 -1
  71. package/dist/adapters/slack/bot.d.ts +7 -6
  72. package/dist/adapters/slack/bot.d.ts.map +1 -1
  73. package/dist/adapters/slack/bot.js +65 -135
  74. package/dist/adapters/slack/bot.js.map +1 -1
  75. package/dist/adapters/slack/context.d.ts.map +1 -1
  76. package/dist/adapters/slack/context.js +3 -15
  77. package/dist/adapters/slack/context.js.map +1 -1
  78. package/dist/adapters/slack/response-lifecycle.d.ts.map +1 -1
  79. package/dist/adapters/slack/response-lifecycle.js +7 -16
  80. package/dist/adapters/slack/response-lifecycle.js.map +1 -1
  81. package/dist/adapters/slack/session.d.ts.map +1 -1
  82. package/dist/adapters/slack/session.js +5 -8
  83. package/dist/adapters/slack/session.js.map +1 -1
  84. package/dist/adapters/streaming.d.ts +4 -0
  85. package/dist/adapters/streaming.d.ts.map +1 -1
  86. package/dist/adapters/streaming.js +11 -0
  87. package/dist/adapters/streaming.js.map +1 -1
  88. package/dist/adapters/telegram/bot.d.ts +0 -2
  89. package/dist/adapters/telegram/bot.d.ts.map +1 -1
  90. package/dist/adapters/telegram/bot.js +16 -62
  91. package/dist/adapters/telegram/bot.js.map +1 -1
  92. package/dist/adapters/telegram/context.d.ts.map +1 -1
  93. package/dist/adapters/telegram/context.js +8 -28
  94. package/dist/adapters/telegram/context.js.map +1 -1
  95. package/dist/adapters/types.d.ts +28 -2
  96. package/dist/adapters/types.d.ts.map +1 -1
  97. package/dist/adapters/types.js.map +1 -1
  98. package/dist/agent.d.ts +21 -4
  99. package/dist/agent.d.ts.map +1 -1
  100. package/dist/agent.js +22 -9
  101. package/dist/agent.js.map +1 -1
  102. package/dist/commands/admin.d.ts.map +1 -1
  103. package/dist/commands/admin.js +2 -1
  104. package/dist/commands/admin.js.map +1 -1
  105. package/dist/commands/auto-reply.d.ts.map +1 -1
  106. package/dist/commands/auto-reply.js +2 -6
  107. package/dist/commands/auto-reply.js.map +1 -1
  108. package/dist/commands/extensions.d.ts.map +1 -1
  109. package/dist/commands/extensions.js +2 -1
  110. package/dist/commands/extensions.js.map +1 -1
  111. package/dist/commands/login.d.ts +2 -1
  112. package/dist/commands/login.d.ts.map +1 -1
  113. package/dist/commands/login.js +30 -1
  114. package/dist/commands/login.js.map +1 -1
  115. package/dist/commands/manifest.d.ts +72 -0
  116. package/dist/commands/manifest.d.ts.map +1 -0
  117. package/dist/commands/manifest.js +121 -0
  118. package/dist/commands/manifest.js.map +1 -0
  119. package/dist/commands/model.d.ts.map +1 -1
  120. package/dist/commands/model.js +2 -1
  121. package/dist/commands/model.js.map +1 -1
  122. package/dist/commands/new.d.ts.map +1 -1
  123. package/dist/commands/new.js +2 -1
  124. package/dist/commands/new.js.map +1 -1
  125. package/dist/commands/sandbox.d.ts.map +1 -1
  126. package/dist/commands/sandbox.js +3 -2
  127. package/dist/commands/sandbox.js.map +1 -1
  128. package/dist/commands/text.d.ts +2 -0
  129. package/dist/commands/text.d.ts.map +1 -0
  130. package/dist/commands/text.js +16 -0
  131. package/dist/commands/text.js.map +1 -0
  132. package/dist/commands/types.d.ts +12 -1
  133. package/dist/commands/types.d.ts.map +1 -1
  134. package/dist/commands/types.js.map +1 -1
  135. package/dist/config.d.ts +1 -1
  136. package/dist/config.d.ts.map +1 -1
  137. package/dist/config.js +49 -54
  138. package/dist/config.js.map +1 -1
  139. package/dist/events.d.ts +10 -0
  140. package/dist/events.d.ts.map +1 -1
  141. package/dist/events.js +14 -5
  142. package/dist/events.js.map +1 -1
  143. package/dist/execution-resolver.d.ts.map +1 -1
  144. package/dist/execution-resolver.js +3 -3
  145. package/dist/execution-resolver.js.map +1 -1
  146. package/dist/index.d.ts +1 -1
  147. package/dist/index.d.ts.map +1 -1
  148. package/dist/index.js.map +1 -1
  149. package/dist/main.js +25 -5
  150. package/dist/main.js.map +1 -1
  151. package/dist/provisioner.d.ts.map +1 -1
  152. package/dist/provisioner.js +2 -5
  153. package/dist/provisioner.js.map +1 -1
  154. package/dist/runtime/conversation-runtime.d.ts +1 -1
  155. package/dist/runtime/conversation-runtime.d.ts.map +1 -1
  156. package/dist/runtime/conversation-runtime.js +230 -37
  157. package/dist/runtime/conversation-runtime.js.map +1 -1
  158. package/dist/runtime/types.d.ts +19 -4
  159. package/dist/runtime/types.d.ts.map +1 -1
  160. package/dist/runtime/types.js.map +1 -1
  161. package/dist/sandbox/cloudflare.d.ts +2 -0
  162. package/dist/sandbox/cloudflare.d.ts.map +1 -1
  163. package/dist/sandbox/cloudflare.js +7 -0
  164. package/dist/sandbox/cloudflare.js.map +1 -1
  165. package/dist/sandbox/container.d.ts +2 -0
  166. package/dist/sandbox/container.d.ts.map +1 -1
  167. package/dist/sandbox/container.js +7 -1
  168. package/dist/sandbox/container.js.map +1 -1
  169. package/dist/sandbox/firecracker.d.ts +2 -0
  170. package/dist/sandbox/firecracker.d.ts.map +1 -1
  171. package/dist/sandbox/firecracker.js +7 -1
  172. package/dist/sandbox/firecracker.js.map +1 -1
  173. package/dist/sandbox/host.d.ts +2 -0
  174. package/dist/sandbox/host.d.ts.map +1 -1
  175. package/dist/sandbox/host.js +11 -0
  176. package/dist/sandbox/host.js.map +1 -1
  177. package/dist/sandbox/identity.d.ts +21 -0
  178. package/dist/sandbox/identity.d.ts.map +1 -0
  179. package/dist/sandbox/identity.js +35 -0
  180. package/dist/sandbox/identity.js.map +1 -0
  181. package/dist/sandbox/types.d.ts +12 -0
  182. package/dist/sandbox/types.d.ts.map +1 -1
  183. package/dist/sandbox/types.js.map +1 -1
  184. package/dist/sandbox/utils.d.ts +11 -0
  185. package/dist/sandbox/utils.d.ts.map +1 -1
  186. package/dist/sandbox/utils.js +35 -0
  187. package/dist/sandbox/utils.js.map +1 -1
  188. package/dist/sessions/agent-memory-file-manager.d.ts.map +1 -1
  189. package/dist/sessions/agent-memory-file-manager.js +16 -19
  190. package/dist/sessions/agent-memory-file-manager.js.map +1 -1
  191. package/dist/sessions/history-line.d.ts +20 -0
  192. package/dist/sessions/history-line.d.ts.map +1 -0
  193. package/dist/sessions/history-line.js +24 -0
  194. package/dist/sessions/history-line.js.map +1 -0
  195. package/dist/sessions/policy.d.ts.map +1 -1
  196. package/dist/sessions/policy.js +4 -3
  197. package/dist/sessions/policy.js.map +1 -1
  198. package/dist/sessions/session-key.d.ts +31 -0
  199. package/dist/sessions/session-key.d.ts.map +1 -0
  200. package/dist/sessions/session-key.js +44 -0
  201. package/dist/sessions/session-key.js.map +1 -0
  202. package/dist/sessions/store.d.ts.map +1 -1
  203. package/dist/sessions/store.js +2 -2
  204. package/dist/sessions/store.js.map +1 -1
  205. package/dist/tools/bash.d.ts +3 -1
  206. package/dist/tools/bash.d.ts.map +1 -1
  207. package/dist/tools/bash.js +24 -17
  208. package/dist/tools/bash.js.map +1 -1
  209. package/dist/tools/edit.d.ts.map +1 -1
  210. package/dist/tools/edit.js +9 -12
  211. package/dist/tools/edit.js.map +1 -1
  212. package/dist/tools/event.d.ts +1 -1
  213. package/dist/tools/event.d.ts.map +1 -1
  214. package/dist/tools/event.js +33 -4
  215. package/dist/tools/event.js.map +1 -1
  216. package/dist/tools/index.js +1 -1
  217. package/dist/tools/index.js.map +1 -1
  218. package/dist/tools/types.d.ts +6 -1
  219. package/dist/tools/types.d.ts.map +1 -1
  220. package/dist/tools/types.js.map +1 -1
  221. package/dist/tools/write.d.ts.map +1 -1
  222. package/dist/tools/write.js +1 -8
  223. package/dist/tools/write.js.map +1 -1
  224. package/dist/types.d.ts +13 -6
  225. package/dist/types.d.ts.map +1 -1
  226. package/dist/types.js.map +1 -1
  227. package/dist/vault/disabled.d.ts +9 -0
  228. package/dist/vault/disabled.d.ts.map +1 -0
  229. package/dist/vault/disabled.js +23 -0
  230. package/dist/vault/disabled.js.map +1 -0
  231. package/dist/vault/index.d.ts.map +1 -1
  232. package/dist/vault/index.js +2 -7
  233. package/dist/vault/index.js.map +1 -1
  234. package/dist/vault/routing.d.ts +1 -0
  235. package/dist/vault/routing.d.ts.map +1 -1
  236. package/dist/vault/routing.js +3 -10
  237. package/dist/vault/routing.js.map +1 -1
  238. package/dist/web/admin/portal.d.ts +27 -0
  239. package/dist/web/admin/portal.d.ts.map +1 -1
  240. package/dist/web/admin/portal.js +87 -126
  241. package/dist/web/admin/portal.js.map +1 -1
  242. package/dist/web/admin/types.d.ts +3 -0
  243. package/dist/web/admin/types.d.ts.map +1 -1
  244. package/dist/web/admin/types.js.map +1 -1
  245. package/dist/web/login/oauth.d.ts +2 -3
  246. package/dist/web/login/oauth.d.ts.map +1 -1
  247. package/dist/web/login/oauth.js +0 -31
  248. package/dist/web/login/oauth.js.map +1 -1
  249. package/dist/web/login/types.d.ts +0 -11
  250. package/dist/web/login/types.d.ts.map +1 -1
  251. package/dist/web/login/types.js.map +1 -1
  252. package/dist/web/server.d.ts.map +1 -1
  253. package/dist/web/server.js +7 -0
  254. package/dist/web/server.js.map +1 -1
  255. package/dist/web/session-view/command.d.ts.map +1 -1
  256. package/dist/web/session-view/command.js +4 -1
  257. package/dist/web/session-view/command.js.map +1 -1
  258. package/dist/web/session-view/portal.d.ts.map +1 -1
  259. package/dist/web/session-view/portal.js +3 -2
  260. package/dist/web/session-view/portal.js.map +1 -1
  261. package/dist/web/session-view/service.d.ts.map +1 -1
  262. package/dist/web/session-view/service.js +2 -1
  263. package/dist/web/session-view/service.js.map +1 -1
  264. package/dist/web/session-view/types.d.ts +2 -1
  265. package/dist/web/session-view/types.d.ts.map +1 -1
  266. package/dist/web/session-view/types.js.map +1 -1
  267. package/package.json +1 -1
  268. package/dist/runtime/agent-run-controller.d.ts +0 -31
  269. package/dist/runtime/agent-run-controller.d.ts.map +0 -1
  270. package/dist/runtime/agent-run-controller.js +0 -195
  271. package/dist/runtime/agent-run-controller.js.map +0 -1
  272. package/dist/tools/github-checks.d.ts.map +0 -1
  273. package/dist/tools/github-checks.js.map +0 -1
  274. package/dist/tools/github-pr.d.ts.map +0 -1
  275. package/dist/tools/github-pr.js.map +0 -1
@@ -3,13 +3,15 @@ import { WebClient } from "@slack/web-api";
3
3
  import { existsSync, readFileSync } from "fs";
4
4
  import { readFile } from "fs/promises";
5
5
  import { basename, join } from "path";
6
+ import { COMMAND_MANIFEST } from "../../commands/manifest.js";
6
7
  import { resolveConversationSettings } from "../../config.js";
7
8
  import * as log from "../../log.js";
8
- import { PRODUCT_NAME, formatForceStopped, formatNothingRunning } from "../../platform-messages.js";
9
- import { appendBotResponseLog, appendChannelLog, MessagingEventQueue, resolveOnlyScopedStopTarget, resolveStopTarget, withRetry, } from "../shared.js";
9
+ import { PRODUCT_NAME, formatForceStopped } from "../../platform-messages.js";
10
+ import { appendBotResponseLog, appendChannelLog, MessagingEventQueue, withRetry, } from "../shared.js";
10
11
  import { processMessageIntake } from "../intake.js";
11
12
  import { createSlackAdapters } from "./context.js";
12
13
  import { hasMaterializedChatSession, registerThreadSession, } from "../../sessions/agent-memory-file-manager.js";
14
+ import { conversationIdOf } from "../../sessions/session-key.js";
13
15
  import { isSlackThreadSessionKey, planSlackAdapterSession, planSlackEventAnchorRun, resolveSlackSessionKey, } from "./session.js";
14
16
  import { reportUserFacingError } from "../../observability/sentry.js";
15
17
  import { renderSlackBlocks } from "./blocks.js";
@@ -426,10 +428,17 @@ export class SlackMessagingBot {
426
428
  });
427
429
  }
428
430
  processSlackMessageIntake(options) {
429
- void processMessageIntake({
431
+ const absorbAttachmentFailure = () => {
432
+ void options.attachmentsPromise.catch((err) => {
433
+ log.logWarning("Failed to log Slack message", String(err));
434
+ });
435
+ };
436
+ processMessageIntake({
430
437
  eventBase: options.event,
431
438
  workingDir: this.workingDir,
432
439
  isAutoReplyCandidate: options.isAutoReplyCandidate,
440
+ magicWord: { addressed: options.addressed, scopeFallback: "top-level" },
441
+ busyPolicy: "queue",
433
442
  logEntryBase: {},
434
443
  processAttachments: () => options.attachmentsPromise,
435
444
  queueKey: options.queueKey,
@@ -437,8 +446,15 @@ export class SlackMessagingBot {
437
446
  handler: this.handler,
438
447
  bot: this,
439
448
  createContext: (event) => this.createContext(event),
440
- onNotTriggered: options.onNotTriggered,
441
449
  deferAttachmentsUntilRun: true,
450
+ }).then((outcome) => {
451
+ // Slack logs eagerly via logUserMessage; when intake does not enqueue,
452
+ // nothing awaits the attachment download, so absorb its failure here.
453
+ if (outcome !== "enqueued")
454
+ absorbAttachmentFailure();
455
+ }, (err) => {
456
+ log.logWarning("Slack message intake failed", String(err));
457
+ absorbAttachmentFailure();
442
458
  });
443
459
  }
444
460
  buildHomeView() {
@@ -476,7 +492,7 @@ export class SlackMessagingBot {
476
492
  // Threshold for "stuck" detection (10 minutes)
477
493
  const STUCK_THRESHOLD_MS = 10 * 60 * 1000;
478
494
  for (const session of runningSessions) {
479
- const channelId = session.sessionKey.split(":")[0];
495
+ const channelId = conversationIdOf(session.sessionKey);
480
496
  const channel = this.channels.get(channelId);
481
497
  const channelName = channel ? `#${channel.name}` : channelId;
482
498
  const elapsed = Math.floor((Date.now() - session.startedAt) / 60000);
@@ -516,7 +532,14 @@ export class SlackMessagingBot {
516
532
  {
517
533
  type: "button",
518
534
  text: { type: "plain_text", text: "Force Stop", emoji: true },
535
+ // The raw session key travels in `value` (Slack returns it
536
+ // verbatim); the action_id only routes and must be unique per
537
+ // view, so it carries a sanitized copy of the key. Never
538
+ // decode the key from the action_id — the `:`↔`_` rewrite is
539
+ // irreversible for conversation ids that contain `_` (GitHub's
540
+ // GH_owner_repo_number do, by design).
519
541
  action_id: `force_stop_${session.sessionKey.replace(/:/g, "_")}`,
542
+ value: session.sessionKey,
520
543
  style: "danger",
521
544
  },
522
545
  ],
@@ -575,22 +598,6 @@ export class SlackMessagingBot {
575
598
  });
576
599
  return { type: "home", blocks: blocks };
577
600
  }
578
- resolveStopTarget(channelId, threadTs) {
579
- const directTarget = resolveStopTarget({
580
- handler: this.handler,
581
- conversationId: channelId,
582
- sessionKey: resolveSlackSessionKey(channelId, threadTs),
583
- });
584
- if (directTarget)
585
- return directTarget;
586
- if (threadTs)
587
- return null;
588
- return resolveOnlyScopedStopTarget(this.handler, channelId);
589
- }
590
- isStopText(text) {
591
- const normalized = text.trim().toLowerCase();
592
- return normalized === "stop" || normalized === "/stop";
593
- }
594
601
  createCommandAdapters(conversationId, userId, userName, text, ts, options = {}) {
595
602
  const message = {
596
603
  id: ts,
@@ -681,10 +688,23 @@ export class SlackMessagingBot {
681
688
  const context = this.createCommandAdapters(conversationId, payload.user_id, userName, commandText, eventTs, isDirectMessage ? { threadTs } : { ephemeralChannelId: conversationId, threadTs });
682
689
  return { event, context };
683
690
  }
684
- async routeSlashLoginCommand(payload) {
691
+ /**
692
+ * Generic slash-command route: synthesize the command text per the
693
+ * manifest's SlackSlashRoute data and dispatch to the handler. `/pi-new`
694
+ * stays bespoke (routeSlashNewCommand) because it replies in 中文 and calls
695
+ * handleNewCommand directly.
696
+ */
697
+ async routeSlashCommand(route, payload) {
685
698
  const { event, context } = this.buildSlashCommandEvent(payload, {
686
- type: payload.channel_id.startsWith("D") ? "dm" : "private_command",
687
- includeText: true,
699
+ includeText: route.includeText,
700
+ thread: route.thread,
701
+ ...(route.privateCommand
702
+ ? {
703
+ type: payload.channel_id.startsWith("D")
704
+ ? "dm"
705
+ : "private_command",
706
+ }
707
+ : {}),
688
708
  });
689
709
  await this.handler.handleEvent(event, this, context);
690
710
  }
@@ -715,18 +735,6 @@ export class SlackMessagingBot {
715
735
  };
716
736
  await this.handler.handleNewCommand(conversationId, conversationId, commandMessagingBot);
717
737
  }
718
- async routeSlashModelCommand(payload) {
719
- const { event, context } = this.buildSlashCommandEvent(payload, { includeText: true });
720
- await this.handler.handleEvent(event, this, context);
721
- }
722
- async routeSlashSessionCommand(payload) {
723
- const { event, context } = this.buildSlashCommandEvent(payload, { thread: true });
724
- await this.handler.handleEvent(event, this, context);
725
- }
726
- async routeSlashAdminCommand(payload) {
727
- const { event, context } = this.buildSlashCommandEvent(payload, { thread: true });
728
- await this.handler.handleEvent(event, this, context);
729
- }
730
738
  setupEventHandlers() {
731
739
  this.socketClient.on("disconnect", (err) => {
732
740
  log.logWarning("Slack socket disconnect", err ? String(err) : "");
@@ -777,26 +785,12 @@ export class SlackMessagingBot {
777
785
  ack();
778
786
  return;
779
787
  }
780
- // Check for stop command - execute immediately, don't queue!
781
- if (this.isStopText(slackEvent.text)) {
782
- const stopTarget = this.resolveStopTarget(e.channel, e.thread_ts);
783
- if (stopTarget) {
784
- this.handler.handleStop(stopTarget, e.channel, this);
785
- }
786
- else {
787
- this.postMessage(e.channel, formatNothingRunning("slack"));
788
- }
789
- void attachmentsPromise.catch((err) => {
790
- log.logWarning("Failed to log Slack message", String(err));
791
- });
792
- ack();
793
- return;
794
- }
795
788
  this.processSlackMessageIntake({
796
789
  event: slackEvent,
797
790
  attachmentsPromise,
798
791
  queueKey: this.resolveQueueKey(e.channel, sessionKey),
799
792
  isAutoReplyCandidate: false,
793
+ addressed: true,
800
794
  });
801
795
  ack();
802
796
  }
@@ -869,59 +863,26 @@ export class SlackMessagingBot {
869
863
  ack();
870
864
  return;
871
865
  }
872
- // Stop command for DM only (app_mention handles shared-channel "@mikan stop").
873
- if (isDM && this.isStopText(slackEvent.text)) {
874
- const stopTarget = this.resolveStopTarget(e.channel, e.thread_ts);
875
- if (stopTarget) {
876
- this.handler.handleStop(stopTarget, e.channel, this);
877
- }
878
- else {
879
- this.postMessage(e.channel, formatNothingRunning("slack"));
880
- }
866
+ if (!isDM && isThreadReply) {
881
867
  void attachmentsPromise.catch((err) => {
882
868
  log.logWarning("Failed to log Slack message", String(err));
883
869
  });
884
870
  ack();
885
871
  return;
886
872
  }
887
- const enqueueTriggered = () => {
888
- const activeSessionKey = slackEvent.sessionKey ?? resolveSlackSessionKey(e.channel, e.thread_ts);
889
- // Auto-reply top-level channel messages start with no sessionKey because
890
- // they are only candidates until the policy allows them. Once triggered,
891
- // persist the resolved key on the event; otherwise the runtime fallback
892
- // treats the message ts as a thread session (`channel:ts`) instead of the
893
- // persistent top-level channel session.
894
- slackEvent.sessionKey = activeSessionKey;
895
- this.processSlackMessageIntake({
896
- event: slackEvent,
897
- attachmentsPromise,
898
- queueKey: this.resolveQueueKey(e.channel, activeSessionKey),
899
- isAutoReplyCandidate: false,
900
- });
901
- };
902
- const logOnly = () => {
903
- void attachmentsPromise.catch((err) => {
904
- log.logWarning("Failed to log Slack message", String(err));
905
- });
906
- };
907
- if (isDM) {
908
- enqueueTriggered();
909
- ack();
910
- return;
911
- }
912
- if (isThreadReply) {
913
- logOnly();
914
- ack();
915
- return;
916
- }
917
- const activeSessionKey = resolveSlackSessionKey(e.channel, e.thread_ts);
873
+ const activeSessionKey = slackEvent.sessionKey ?? resolveSlackSessionKey(e.channel, e.thread_ts);
874
+ // Auto-reply top-level channel messages start with no sessionKey because
875
+ // they are only candidates until the policy allows them. Persist the
876
+ // resolved key on the event; otherwise the runtime fallback treats the
877
+ // message ts as a thread session (`channel:ts`) instead of the persistent
878
+ // top-level channel session.
918
879
  slackEvent.sessionKey = activeSessionKey;
919
880
  this.processSlackMessageIntake({
920
881
  event: slackEvent,
921
882
  attachmentsPromise,
922
883
  queueKey: this.resolveQueueKey(e.channel, activeSessionKey),
923
- isAutoReplyCandidate: true,
924
- onNotTriggered: logOnly,
884
+ isAutoReplyCandidate: !isDM,
885
+ addressed: isDM,
925
886
  });
926
887
  ack();
927
888
  }
@@ -932,54 +893,20 @@ export class SlackMessagingBot {
932
893
  return;
933
894
  }
934
895
  const { command, text, channel_id, user_id, user_name, thread_ts } = payload;
896
+ const entry = COMMAND_MANIFEST.find((candidate) => candidate.slackCommand === command);
897
+ if (!entry)
898
+ return;
935
899
  let handlerPromise = null;
936
- if (command === "/pi-login") {
937
- handlerPromise = this.routeSlashLoginCommand({
938
- command,
939
- text,
940
- channel_id,
941
- user_id,
942
- user_name,
943
- });
944
- }
945
- else if (command === "/pi-new") {
900
+ if (entry.name === "new") {
946
901
  handlerPromise = this.routeSlashNewCommand({ command, channel_id, user_id, user_name });
947
902
  }
948
- else if (command === "/pi-session") {
949
- handlerPromise = this.routeSlashSessionCommand({
950
- command,
951
- channel_id,
952
- user_id,
953
- user_name,
954
- thread_ts,
955
- });
956
- }
957
- else if (command === "/pi-model") {
958
- handlerPromise = this.routeSlashModelCommand({
959
- command,
960
- text,
961
- channel_id,
962
- user_id,
963
- user_name,
964
- });
965
- }
966
- else if (command === "/pi-sandbox" ||
967
- command === "/pi-auto-reply" ||
968
- command === "/pi-extensions") {
969
- handlerPromise = this.routeSlashModelCommand({
903
+ else if (entry.slackRoute) {
904
+ handlerPromise = this.routeSlashCommand(entry.slackRoute, {
970
905
  command,
971
906
  text,
972
907
  channel_id,
973
908
  user_id,
974
909
  user_name,
975
- });
976
- }
977
- else if (command === "/pi-admin") {
978
- handlerPromise = this.routeSlashAdminCommand({
979
- command,
980
- channel_id,
981
- user_id,
982
- user_name,
983
910
  thread_ts,
984
911
  });
985
912
  }
@@ -1016,9 +943,12 @@ export class SlackMessagingBot {
1016
943
  return;
1017
944
  }
1018
945
  ack();
1019
- const sessionKey = action.action_id.replace("force_stop_", "").replace(/_/g, ":");
946
+ // Prefer the verbatim key from `value`; the action_id fallback only
947
+ // serves buttons rendered before `value` existed and misdecodes session
948
+ // keys whose conversation id contains "_".
949
+ const sessionKey = action.value ?? action.action_id.replace("force_stop_", "").replace(/_/g, ":");
1020
950
  const userId = body.user?.id;
1021
- const channelId = body.container?.channel_id || sessionKey.split(":")[0];
951
+ const channelId = body.container?.channel_id || conversationIdOf(sessionKey);
1022
952
  log.logInfo(`[Force Stop] User ${userId} requested force stop for ${sessionKey}`);
1023
953
  // Use handler's forceStop method
1024
954
  this.handler.forceStop(sessionKey);