cli-jaw 2.17.43 → 2.17.44

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 (223) hide show
  1. package/README.md +13 -2
  2. package/dist/bin/commands/slack.js +73 -7
  3. package/dist/bin/commands/slack.js.map +1 -1
  4. package/dist/server.js +46 -1
  5. package/dist/server.js.map +1 -1
  6. package/dist/src/agent/calendar-context.js +23 -0
  7. package/dist/src/agent/calendar-context.js.map +1 -0
  8. package/dist/src/agent/claude-runtime-run.js +10 -1
  9. package/dist/src/agent/claude-runtime-run.js.map +1 -1
  10. package/dist/src/agent/cursor-acp-models.js +139 -0
  11. package/dist/src/agent/cursor-acp-models.js.map +1 -0
  12. package/dist/src/agent/events/cursor.js +28 -4
  13. package/dist/src/agent/events/cursor.js.map +1 -1
  14. package/dist/src/agent/lifecycle-handler.js +8 -2
  15. package/dist/src/agent/lifecycle-handler.js.map +1 -1
  16. package/dist/src/agent/native-runtime-run.js +22 -1
  17. package/dist/src/agent/native-runtime-run.js.map +1 -1
  18. package/dist/src/agent/runtime/acp/config.js +76 -11
  19. package/dist/src/agent/runtime/acp/config.js.map +1 -1
  20. package/dist/src/agent/runtime/acp/cursor-session.js +6 -1
  21. package/dist/src/agent/runtime/acp/cursor-session.js.map +1 -1
  22. package/dist/src/agent/runtime/liveness.js +24 -0
  23. package/dist/src/agent/runtime/liveness.js.map +1 -0
  24. package/dist/src/agent/runtime/start-failure.js +61 -0
  25. package/dist/src/agent/runtime/start-failure.js.map +1 -0
  26. package/dist/src/agent/spawn/queue.js +11 -6
  27. package/dist/src/agent/spawn/queue.js.map +1 -1
  28. package/dist/src/agent/spawn.js +59 -17
  29. package/dist/src/agent/spawn.js.map +1 -1
  30. package/dist/src/cli/api-auth.js +6 -0
  31. package/dist/src/cli/api-auth.js.map +1 -1
  32. package/dist/src/manager/design/watcher.js +10 -2
  33. package/dist/src/manager/design/watcher.js.map +1 -1
  34. package/dist/src/memory/heartbeat.js +6 -0
  35. package/dist/src/memory/heartbeat.js.map +1 -1
  36. package/dist/src/messaging/channel-validate.js +5 -3
  37. package/dist/src/messaging/channel-validate.js.map +1 -1
  38. package/dist/src/messaging/redact.js +30 -8
  39. package/dist/src/messaging/redact.js.map +1 -1
  40. package/dist/src/messaging/send.js +7 -0
  41. package/dist/src/messaging/send.js.map +1 -1
  42. package/dist/src/orchestrator/collect.js +5 -3
  43. package/dist/src/orchestrator/collect.js.map +1 -1
  44. package/dist/src/orchestrator/gateway.js +10 -3
  45. package/dist/src/orchestrator/gateway.js.map +1 -1
  46. package/dist/src/orchestrator/pipeline.js +21 -9
  47. package/dist/src/orchestrator/pipeline.js.map +1 -1
  48. package/dist/src/orchestrator/request-registry.js +2 -0
  49. package/dist/src/orchestrator/request-registry.js.map +1 -1
  50. package/dist/src/prompt/builder.js +74 -6
  51. package/dist/src/prompt/builder.js.map +1 -1
  52. package/dist/src/prompt/templates/a1-system.md +22 -5
  53. package/dist/src/routes/messaging.js +105 -27
  54. package/dist/src/routes/messaging.js.map +1 -1
  55. package/dist/src/routes/settings.js +20 -8
  56. package/dist/src/routes/settings.js.map +1 -1
  57. package/dist/src/routes/slack-tools.js +176 -0
  58. package/dist/src/routes/slack-tools.js.map +1 -0
  59. package/dist/src/slack/action-rate.js +43 -0
  60. package/dist/src/slack/action-rate.js.map +1 -0
  61. package/dist/src/slack/action-runtime.js +243 -0
  62. package/dist/src/slack/action-runtime.js.map +1 -0
  63. package/dist/src/slack/action-store.js +84 -0
  64. package/dist/src/slack/action-store.js.map +1 -0
  65. package/dist/src/slack/action-types.js +6 -0
  66. package/dist/src/slack/action-types.js.map +1 -0
  67. package/dist/src/slack/actions-canvas.js +108 -0
  68. package/dist/src/slack/actions-canvas.js.map +1 -0
  69. package/dist/src/slack/actions-channel.js +88 -0
  70. package/dist/src/slack/actions-channel.js.map +1 -0
  71. package/dist/src/slack/actions-interactions.js +204 -0
  72. package/dist/src/slack/actions-interactions.js.map +1 -0
  73. package/dist/src/slack/actions-lists.js +308 -0
  74. package/dist/src/slack/actions-lists.js.map +1 -0
  75. package/dist/src/slack/actions-message.js +117 -0
  76. package/dist/src/slack/actions-message.js.map +1 -0
  77. package/dist/src/slack/actions-resources.js +62 -0
  78. package/dist/src/slack/actions-resources.js.map +1 -0
  79. package/dist/src/slack/actions-rts.js +88 -0
  80. package/dist/src/slack/actions-rts.js.map +1 -0
  81. package/dist/src/slack/actions-schedule.js +214 -0
  82. package/dist/src/slack/actions-schedule.js.map +1 -0
  83. package/dist/src/slack/actions.js +17 -0
  84. package/dist/src/slack/actions.js.map +1 -0
  85. package/dist/src/slack/api.js +56 -9
  86. package/dist/src/slack/api.js.map +1 -1
  87. package/dist/src/slack/blocks.js +162 -0
  88. package/dist/src/slack/blocks.js.map +1 -0
  89. package/dist/src/slack/bot.js +562 -378
  90. package/dist/src/slack/bot.js.map +1 -1
  91. package/dist/src/slack/commands.js +24 -5
  92. package/dist/src/slack/commands.js.map +1 -1
  93. package/dist/src/slack/context.js +15 -4
  94. package/dist/src/slack/context.js.map +1 -1
  95. package/dist/src/slack/conversation.js +135 -33
  96. package/dist/src/slack/conversation.js.map +1 -1
  97. package/dist/src/slack/events.js +41 -17
  98. package/dist/src/slack/events.js.map +1 -1
  99. package/dist/src/slack/format.js +181 -0
  100. package/dist/src/slack/format.js.map +1 -1
  101. package/dist/src/slack/history.js +133 -8
  102. package/dist/src/slack/history.js.map +1 -1
  103. package/dist/src/slack/ingress.js +26 -9
  104. package/dist/src/slack/ingress.js.map +1 -1
  105. package/dist/src/slack/interaction-store.js +56 -0
  106. package/dist/src/slack/interaction-store.js.map +1 -0
  107. package/dist/src/slack/manifest.js +3 -0
  108. package/dist/src/slack/manifest.js.map +1 -1
  109. package/dist/src/slack/message-content.js +132 -0
  110. package/dist/src/slack/message-content.js.map +1 -0
  111. package/dist/src/slack/message.js +58 -0
  112. package/dist/src/slack/message.js.map +1 -0
  113. package/dist/src/slack/notice-transport.js +49 -19
  114. package/dist/src/slack/notice-transport.js.map +1 -1
  115. package/dist/src/slack/operator-auth.js +62 -0
  116. package/dist/src/slack/operator-auth.js.map +1 -0
  117. package/dist/src/slack/progress-activity.js +210 -0
  118. package/dist/src/slack/progress-activity.js.map +1 -0
  119. package/dist/src/slack/progress-detail.js +473 -0
  120. package/dist/src/slack/progress-detail.js.map +1 -0
  121. package/dist/src/slack/progress-files.js +70 -0
  122. package/dist/src/slack/progress-files.js.map +1 -0
  123. package/dist/src/slack/progress-lifecycle.js +223 -0
  124. package/dist/src/slack/progress-lifecycle.js.map +1 -0
  125. package/dist/src/slack/progress-restore.js +81 -0
  126. package/dist/src/slack/progress-restore.js.map +1 -0
  127. package/dist/src/slack/progress.js +325 -143
  128. package/dist/src/slack/progress.js.map +1 -1
  129. package/dist/src/slack/quote.js +227 -0
  130. package/dist/src/slack/quote.js.map +1 -0
  131. package/dist/src/slack/render-features.js +105 -0
  132. package/dist/src/slack/render-features.js.map +1 -0
  133. package/dist/src/slack/reply-delivery.js +110 -0
  134. package/dist/src/slack/reply-delivery.js.map +1 -0
  135. package/dist/src/slack/rts-output-store.js +143 -0
  136. package/dist/src/slack/rts-output-store.js.map +1 -0
  137. package/dist/src/slack/scope-status.js +17 -3
  138. package/dist/src/slack/scope-status.js.map +1 -1
  139. package/dist/src/slack/search-quote.js +70 -0
  140. package/dist/src/slack/search-quote.js.map +1 -0
  141. package/dist/src/slack/search.js +54 -0
  142. package/dist/src/slack/search.js.map +1 -0
  143. package/dist/src/slack/send-handler.js +11 -7
  144. package/dist/src/slack/send-handler.js.map +1 -1
  145. package/dist/src/slack/send-only-client.js +116 -79
  146. package/dist/src/slack/send-only-client.js.map +1 -1
  147. package/dist/src/slack/table-content.js +253 -0
  148. package/dist/src/slack/table-content.js.map +1 -0
  149. package/dist/src/slack/table-verification.js +64 -0
  150. package/dist/src/slack/table-verification.js.map +1 -0
  151. package/dist/src/slack/task-input.js +55 -0
  152. package/dist/src/slack/task-input.js.map +1 -0
  153. package/dist/src/slack/tool-access.js +127 -0
  154. package/dist/src/slack/tool-access.js.map +1 -0
  155. package/dist/src/slack/tool-capabilities.js +47 -0
  156. package/dist/src/slack/tool-capabilities.js.map +1 -0
  157. package/dist/src/slack/tool-context.js +91 -0
  158. package/dist/src/slack/tool-context.js.map +1 -0
  159. package/dist/src/slack/verified-workspace.js +17 -6
  160. package/dist/src/slack/verified-workspace.js.map +1 -1
  161. package/dist/src/trace/redact.js +2 -1
  162. package/dist/src/trace/redact.js.map +1 -1
  163. package/package.json +1 -1
  164. package/public/dist/.vite/manifest.json +102 -102
  165. package/public/dist/assets/{Agent-CQ4EBgY8.js → Agent-CNad_A6t.js} +1 -1
  166. package/public/dist/assets/{ChannelEnablementControl-JsYvNMlM.js → ChannelEnablementControl-DwUDKjX9.js} +4 -4
  167. package/public/dist/assets/{ChannelSetupEntry-BpyRWEUE.js → ChannelSetupEntry-DH0AuNNX.js} +1 -1
  168. package/public/dist/assets/{ChannelsDiscord-DNfvz1jn.js → ChannelsDiscord-7NmoTjR7.js} +1 -1
  169. package/public/dist/assets/{ChannelsSlack-DzpxIdgW.js → ChannelsSlack-Ba0ax6iT.js} +1 -1
  170. package/public/dist/assets/{ChannelsTelegram-CUhFc5s6.js → ChannelsTelegram-DKA9atf2.js} +1 -1
  171. package/public/dist/assets/{app-Q0prKOFx.js → app-9OQNfmqw.js} +4 -4
  172. package/public/dist/assets/{bgtask-badge-DAcWaUfS.js → bgtask-badge-BvxHqMGi.js} +1 -1
  173. package/public/dist/assets/bgtask-badge-DeSZJGNN.js +1 -0
  174. package/public/dist/assets/{employees-CWS3Juca.js → employees-CkUqN4oh.js} +1 -1
  175. package/public/dist/assets/{iframe-renderer-DCDKBMb7.js → iframe-renderer-C2gchtOj.js} +2 -2
  176. package/public/dist/assets/iframe-renderer-DfvACXIn.js +1 -0
  177. package/public/dist/assets/{manager-v6GYfoPB.js → manager-rb5X-4DJ.js} +1 -1
  178. package/public/dist/assets/{memory-DhNdKZLO.js → memory-BiWzpoBq.js} +1 -1
  179. package/public/dist/assets/memory-Ckp1Eq5a.js +1 -0
  180. package/public/dist/assets/message-history-Bv0l_t7O.js +1 -0
  181. package/public/dist/assets/{message-history-DbRTl1P0.js → message-history-CuFefS02.js} +1 -1
  182. package/public/dist/assets/{render-DP8SJtdZ.js → render-DUGqq9Mu.js} +1 -1
  183. package/public/dist/assets/{settings-85n9NjvX.js → settings-BqUHavbH.js} +1 -1
  184. package/public/dist/assets/settings-C81Kf9uX.js +1 -0
  185. package/public/dist/assets/{settings-C9_D-pn0.js → settings-DQ1fLygB.js} +31 -30
  186. package/public/dist/assets/{settings-core-BcVD2FgV.js → settings-core-CzdXfoOK.js} +1 -1
  187. package/public/dist/assets/settings-core-DO_5M7Lv.js +1 -0
  188. package/public/dist/assets/{settings-embedding-BSpq785l.js → settings-embedding-BAKFrVLV.js} +1 -1
  189. package/public/dist/assets/{settings-types-B09BmpYe.js → settings-types-D3cd6SWy.js} +1 -1
  190. package/public/dist/assets/{sidebar-BVhLb5Ft.js → sidebar-CvGbp9x5.js} +3 -3
  191. package/public/dist/assets/skills-DFpPmMUR.js +1 -0
  192. package/public/dist/assets/{skills-X7qxmHka.js → skills-abLMqY5_.js} +1 -1
  193. package/public/dist/assets/slash-commands-BU58xYih.js +1 -0
  194. package/public/dist/assets/{slash-commands-BnDH4IU_.js → slash-commands-D8o9pqOS.js} +1 -1
  195. package/public/dist/assets/{trace-drawer-DasaS1z4.js → trace-drawer-C6RvkUAd.js} +1 -1
  196. package/public/dist/assets/{ui-BtiBTXng.js → ui-Bxapl_Eu.js} +1 -1
  197. package/public/dist/assets/ui-CUB4iwz3.js +1 -0
  198. package/public/dist/index.html +1 -1
  199. package/public/dist/manager/index.html +1 -1
  200. package/public/dist/settings/index.html +1 -1
  201. package/public/js/features/settings-cli-status.ts +11 -1
  202. package/public/js/features/settings-types.ts +21 -0
  203. package/public/locales/en.json +34 -1
  204. package/public/locales/ja.json +34 -1
  205. package/public/locales/ko.json +34 -1
  206. package/public/locales/zh.json +34 -1
  207. package/scripts/check-native-load.cjs +17 -4
  208. package/scripts/release-gates.mjs +84 -3
  209. package/scripts/service-artifact.mjs +157 -0
  210. package/scripts/service-shell/zsh/.zlogin +23 -0
  211. package/scripts/service-shell/zsh/.zlogout +23 -0
  212. package/scripts/service-shell/zsh/.zprofile +23 -0
  213. package/scripts/service-shell/zsh/.zshenv +23 -0
  214. package/scripts/service-shell/zsh/.zshrc +23 -0
  215. package/public/dist/assets/bgtask-badge-DHA2OQm8.js +0 -1
  216. package/public/dist/assets/iframe-renderer-RpvBly2x.js +0 -1
  217. package/public/dist/assets/memory-4YH8sz0L.js +0 -1
  218. package/public/dist/assets/message-history-lVcLpAw8.js +0 -1
  219. package/public/dist/assets/settings--EGy3_B9.js +0 -1
  220. package/public/dist/assets/settings-core-8wBl1mg-.js +0 -1
  221. package/public/dist/assets/skills-6GFkHTlQ.js +0 -1
  222. package/public/dist/assets/slash-commands-qu8fPAZ3.js +0 -1
  223. package/public/dist/assets/ui-BucJ1zMA.js +0 -1
@@ -0,0 +1,227 @@
1
+ import { sanitizeIdentityName } from './identity.js';
2
+ import { createHash } from 'node:crypto';
3
+ import { redactChannelSecrets } from '../messaging/redact.js';
4
+ import { slackApi } from './api.js';
5
+ import { readExactSlackMessage, readSlackMessageSnapshot, slackMessageRevision } from './message.js';
6
+ import { buildSlackBlockPayloads } from './blocks.js';
7
+ import { verifiedSlackWorkspace } from './verified-workspace.js';
8
+ import { sendSlackText } from './send-only-client.js';
9
+ import { slackToolDenied, withSlackToolAccess } from './tool-access.js';
10
+ import { getRtsOutputStore, RTS_OUTPUT_MARKER } from './rts-output-store.js';
11
+ import { slackCredentialKey } from './tool-context.js';
12
+ function quoteContent(blocks) {
13
+ let nodes = 0;
14
+ const walk = (value, depth = 0) => {
15
+ if (++nodes > 1000 || depth > 12)
16
+ throw slackToolDenied('slack_quote_readback_invalid', 502);
17
+ if (Array.isArray(value))
18
+ return value.map(item => walk(item, depth + 1));
19
+ if (!value || typeof value !== 'object')
20
+ throw slackToolDenied('slack_quote_readback_invalid', 502);
21
+ const node = value;
22
+ if (node['type'] === 'text')
23
+ return { type: 'text', text: node['text'] };
24
+ if (node['type'] === 'link')
25
+ return { type: 'link', text: node['text'], url: node['url'] };
26
+ if (['rich_text', 'rich_text_section', 'rich_text_quote'].includes(String(node['type'])) && Array.isArray(node['elements'])) {
27
+ return { type: node['type'], elements: walk(node['elements'], depth + 1) };
28
+ }
29
+ throw slackToolDenied('slack_quote_readback_invalid', 502);
30
+ };
31
+ return JSON.stringify(walk(blocks));
32
+ }
33
+ /** Explicit text leaves quote source data literally, without mention/Markdown execution. */
34
+ export async function publishSlackQuote(token, principal, input, options = {}, rtsInvocation) {
35
+ const destination = principal.kind === 'turn' ? principal.grant.destination : input.destination;
36
+ if (!destination || destination.channel !== 'slack')
37
+ throw slackToolDenied('slack_quote_destination_required', 400);
38
+ if (principal.kind === 'turn' && input.destination && (input.destination.targetId !== destination.targetId
39
+ || (input.destination.threadId ?? '') !== (destination.threadId ?? '')))
40
+ throw slackToolDenied('slack_destination_mismatch');
41
+ const sensitive = { ...options, sensitiveResponse: true };
42
+ return withSlackToolAccess(token, principal, input.source.channel, async (signal) => {
43
+ const callOptions = { ...sensitive, ...(signal ? { signal } : {}) };
44
+ const snapshot = await readSlackMessageSnapshot(token, input.source, callOptions);
45
+ if (input.expectedAuthorId && snapshot.message.user !== input.expectedAuthorId)
46
+ throw slackToolDenied('slack_source_author_changed', 409);
47
+ if (!(typeof snapshot.message.user === 'string' && /^[UW][A-Z0-9]{1,63}$/.test(snapshot.message.user))
48
+ && !(typeof snapshot.message.botId === 'string' && /^B[A-Z0-9]{1,63}$/.test(snapshot.message.botId)))
49
+ throw slackToolDenied('slack_source_author_unverified', 409);
50
+ if (snapshot.message.edited && !/^\d{1,13}\.\d{1,6}$/.test(snapshot.message.edited.ts))
51
+ throw slackToolDenied('slack_source_metadata_unverified', 409);
52
+ const sourceText = snapshot.message.text;
53
+ if (!sourceText)
54
+ throw slackToolDenied('slack_source_text_unavailable', 409);
55
+ if (input.excerpt !== undefined && !sourceText.includes(input.excerpt))
56
+ throw slackToolDenied('slack_quote_not_in_source', 400);
57
+ const selected = input.summary ?? input.excerpt ?? sourceText.slice(0, 2500).replace(/[\uD800-\uDBFF]$/, '');
58
+ const text = redactChannelSecrets(selected);
59
+ const normalized = snapshot.message.textFromBlocks || text !== selected;
60
+ const label = input.summary !== undefined ? '요약 · 원문 링크 확인' : normalized ? '원문에서 읽은 내용' : '직접 인용';
61
+ const excerpt = !input.summary && selected !== sourceText ? ' · 발췌' : '';
62
+ let author = snapshot.message.user ? `사용자 ${snapshot.message.user}` : `봇 ${snapshot.message.botId ?? 'unknown'}`;
63
+ if (snapshot.message.user) {
64
+ const identity = await slackApi(token, 'users.info', { user: snapshot.message.user }, callOptions);
65
+ if (identity.error === 'ratelimited' || identity.error === 'rate_limited')
66
+ throw slackToolDenied('slack_tool_rate_limited', 429);
67
+ if (identity.ok && identity.data?.user?.id === snapshot.message.user) {
68
+ const name = identity.data.user.profile?.display_name || identity.data.user.real_name || identity.data.user.name;
69
+ if (typeof name === 'string')
70
+ author = `${sanitizeIdentityName(name, snapshot.message.user)} (${snapshot.message.user})`;
71
+ }
72
+ }
73
+ const when = new Date(Number(snapshot.message.ts) * 1000).toISOString();
74
+ const heading = redactChannelSecrets(`${label}${excerpt} · ${author}\n${when} · ts=${snapshot.message.ts}${snapshot.message.edited ? ` · 수정 ts=${snapshot.message.edited.ts}` : ''}\n원문 확인: ${new Date().toISOString()}`);
75
+ const outputMarker = rtsInvocation ? `${RTS_OUTPUT_MARKER}:${rtsInvocation}` : undefined;
76
+ const blocks = [{ type: 'rich_text', ...(outputMarker ? { block_id: outputMarker } : {}), elements: [
77
+ { type: 'rich_text_section', elements: [{ type: 'text', text: heading }] },
78
+ { type: input.summary !== undefined ? 'rich_text_section' : 'rich_text_quote', elements: [{ type: 'text', text }] },
79
+ { type: 'rich_text_section', elements: [{ type: 'link', url: snapshot.permalink, text: '원문' }] },
80
+ ] }];
81
+ const current = await readExactSlackMessage(token, input.source, callOptions);
82
+ if (slackMessageRevision(current) !== snapshot.revision)
83
+ throw slackToolDenied('slack_source_changed', 409);
84
+ await withSlackToolAccess(token, principal, input.source.channel, async () => true, options.fetchImpl, undefined, true);
85
+ if (options.currentCredential && options.currentCredential() !== token)
86
+ throw slackToolDenied('slack_credential_changed', 409);
87
+ const store = rtsInvocation ? getRtsOutputStore() : null;
88
+ if (rtsInvocation && (!store || principal.kind !== 'turn'))
89
+ throw slackToolDenied('slack_rts_privacy_unavailable', 503);
90
+ const teamId = principal.kind === 'turn' ? principal.grant.teamId : '';
91
+ let rtsPayload;
92
+ let publisherBot;
93
+ if (rtsInvocation) {
94
+ if (!/^[A-Za-z0-9:_-]{1,64}$/.test(rtsInvocation))
95
+ throw slackToolDenied('slack_rts_invocation_invalid', 400);
96
+ const prepared = buildSlackBlockPayloads('원문을 확인한 인용을 전달했습니다.', blocks);
97
+ if (prepared.length !== 1 || Buffer.byteLength(JSON.stringify(prepared[0])) > 65536 || text.length > 9000)
98
+ throw slackToolDenied('slack_rts_single_payload_required', 400);
99
+ rtsPayload = prepared[0];
100
+ const identity = await verifiedSlackWorkspace(token, { ...callOptions, refresh: true });
101
+ if (identity?.teamId !== teamId || !identity.userId)
102
+ throw slackToolDenied('slack_rts_publisher_unverified', 403);
103
+ publisherBot = identity.userId;
104
+ if (options.currentCredential && options.currentCredential() !== token)
105
+ throw slackToolDenied('slack_credential_changed', 409);
106
+ if (signal?.aborted)
107
+ throw slackToolDenied('slack_rts_publisher_cancelled', 409);
108
+ }
109
+ // A hold is created only after the source reread; normal source access cannot mask itself.
110
+ if (rtsInvocation && !store.begin(teamId, destination.targetId, rtsInvocation, { threadTs: destination.threadId ?? null, expectedOutputs: 1, actor: principal.kind === 'turn' ? principal.grant.actorId : '', botUserId: publisherBot, credentialKey: slackCredentialKey(token) }))
111
+ throw slackToolDenied('slack_rts_publication_pending', 409);
112
+ const lease = rtsInvocation ? store.publication(teamId, destination.targetId, rtsInvocation).lease : undefined;
113
+ let contentVerified = false;
114
+ let sent = 'unknown';
115
+ const messageTs = [];
116
+ try {
117
+ // RTS owns exactly one direct POST; the ordinary quote path retains its renderer/retry policy.
118
+ const result = rtsInvocation ? await (async () => {
119
+ const posted = await slackApi(token, 'chat.postMessage', {
120
+ channel: destination.targetId, ...rtsPayload, ...(destination.threadId ? { thread_ts: destination.threadId } : {}),
121
+ }, callOptions);
122
+ return { ok: posted.ok, ts: posted.data?.ts, sent: posted.ok };
123
+ })() : await sendSlackText(token, destination, '원문을 확인한 인용을 전달했습니다.', { ...callOptions, blocks });
124
+ const ids = ('delivery' in result ? result.delivery?.messageTs : undefined) ?? (result.ts ? [result.ts] : []);
125
+ if (!Array.isArray(ids) || ids.some(id => typeof id !== 'string' || !/^\d{1,13}\.\d{1,6}$/.test(id))) {
126
+ sent = 'unknown';
127
+ throw new Error('slack_quote_output_id_invalid');
128
+ }
129
+ sent = ids.length > 0 && (result.ok || result.sent === true) ? true : 'unknown';
130
+ messageTs.push(...ids);
131
+ if (store)
132
+ for (const id of ids)
133
+ store.record(teamId, destination.targetId, id);
134
+ if (!result.ok || ids.length !== 1)
135
+ return { ok: false, sent, retryable: false, messageTs, sourceVerification: 'failed', contentVerification: 'failed', error: 'slack_quote_delivery_unverified' };
136
+ const readback = await slackApi(token, destination.threadId ? 'conversations.replies' : 'conversations.history', {
137
+ channel: destination.targetId, ...(destination.threadId ? { ts: destination.threadId } : {}),
138
+ oldest: ids[0], latest: ids[0], inclusive: true, limit: 2,
139
+ }, { ...callOptions, form: true });
140
+ const matches = Array.isArray(readback.data?.messages) ? readback.data.messages.filter(message => message?.ts === ids[0]) : [];
141
+ const saved = matches.length === 1 ? matches[0] : undefined;
142
+ const marker = !rtsInvocation || (Array.isArray(saved?.blocks) && saved.blocks.some(block => block?.block_id === outputMarker));
143
+ const owned = !rtsInvocation || (!!saved && saved.user === publisherBot && (destination.threadId ? saved.thread_ts === destination.threadId : !saved.thread_ts || saved.thread_ts === saved.ts));
144
+ if (!readback.ok || !saved || !marker || !owned || quoteContent(saved.blocks) !== quoteContent(blocks)) {
145
+ return { ok: false, sent: true, retryable: false, messageTs, sourceVerification: 'failed', contentVerification: 'failed', error: 'slack_quote_content_mismatch' };
146
+ }
147
+ if (signal?.aborted)
148
+ return { ok: false, sent: true, retryable: false, messageTs, sourceVerification: 'failed', contentVerification: 'verified', error: 'slack_quote_cancelled_after_send' };
149
+ contentVerified = true;
150
+ return { ok: true, sent: true, retryable: false, messageTs,
151
+ sourceVerification: input.summary !== undefined ? 'references_only' : normalized ? 'normalized_quote' : 'exact_quote', contentVerification: 'verified' };
152
+ }
153
+ catch {
154
+ return { ok: false, sent, retryable: false, messageTs, sourceVerification: 'failed', contentVerification: 'failed', error: 'slack_quote_publication_unknown' };
155
+ }
156
+ finally {
157
+ if (store && rtsInvocation && lease) {
158
+ try {
159
+ store.terminal(teamId, destination.targetId, rtsInvocation, lease, messageTs, contentVerified);
160
+ if (contentVerified)
161
+ store.finish(teamId, destination.targetId, rtsInvocation);
162
+ }
163
+ catch {
164
+ // A failed terminal write deliberately leaves the durable destination hold.
165
+ return { ok: false, sent, retryable: false, messageTs, sourceVerification: 'failed', contentVerification: 'failed', error: 'slack_rts_terminal_unrecorded' };
166
+ }
167
+ }
168
+ }
169
+ }, options.fetchImpl, receipt => ({ ...receipt, ok: false, retryable: false, error: 'slack_quote_cancelled_after_dispatch' }), true);
170
+ }
171
+ const turnInvocations = new WeakMap();
172
+ /** No source bodies/results stored: only a request digest and publication receipt. */
173
+ export async function runSlackQuoteInvocation(token, principal, id, input, run) {
174
+ const hash = createHash('sha256').update(JSON.stringify(input)).digest('hex');
175
+ if (principal.kind === 'operator') {
176
+ const store = getRtsOutputStore();
177
+ if (!store)
178
+ throw slackToolDenied('slack_quote_store_unavailable', 503);
179
+ const owner = slackCredentialKey(token);
180
+ const previous = store.quoteOperation(owner, id);
181
+ if (previous) {
182
+ if (previous.request_hash !== hash || previous.status !== 'completed' || !previous.receipt_json)
183
+ throw slackToolDenied('slack_invocation_conflict_or_unknown', 409);
184
+ try {
185
+ return JSON.parse(previous.receipt_json);
186
+ }
187
+ catch {
188
+ throw slackToolDenied('slack_invocation_receipt_invalid', 409);
189
+ }
190
+ }
191
+ if (!store.reserveQuote(owner, id, hash))
192
+ throw slackToolDenied('slack_invocation_capacity', 429);
193
+ try {
194
+ const receipt = await run();
195
+ store.settleQuote(owner, id, receipt);
196
+ return receipt;
197
+ }
198
+ catch (error) {
199
+ store.settleQuote(owner, id);
200
+ throw error;
201
+ }
202
+ }
203
+ const key = id;
204
+ const map = turnInvocations.get(principal.grant) ?? new Map();
205
+ turnInvocations.set(principal.grant, map);
206
+ const existing = map.get(key);
207
+ if (existing) {
208
+ if (existing.hash !== hash || existing.pending || existing.receipt === undefined)
209
+ throw slackToolDenied('slack_invocation_conflict_or_unknown', 409);
210
+ return existing.receipt;
211
+ }
212
+ if (map.size >= 64)
213
+ throw slackToolDenied('slack_invocation_capacity', 429);
214
+ const record = { hash, pending: true };
215
+ map.set(key, record);
216
+ try {
217
+ const result = await run();
218
+ record.receipt = result;
219
+ record.pending = false;
220
+ return result;
221
+ }
222
+ catch (error) {
223
+ record.pending = false;
224
+ throw error;
225
+ }
226
+ }
227
+ //# sourceMappingURL=quote.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quote.js","sourceRoot":"","sources":["../../../src/slack/quote.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,105 @@
1
+ import { marked } from 'marked';
2
+ const LANGUAGES = { python: 'python', py: 'python', javascript: 'javascript', js: 'javascript', typescript: 'typescript', ts: 'typescript', json: 'json' };
3
+ // Markdown token -> semantic feature. An image is stored by Slack as an image
4
+ // block, not a link, so both sides of the comparison must call it image.
5
+ const MARKDOWN_FEATURES = { heading: 'heading', hr: 'divider', strong: 'bold', em: 'italic', del: 'strike', codespan: 'inline_code', link: 'link', image: 'image', blockquote: 'quote', code: 'code' };
6
+ function collectMarkdownFeatures(text, found) {
7
+ const tokens = marked.lexer(text, { gfm: true });
8
+ for (const root of tokens) {
9
+ if (root.type === 'table')
10
+ continue;
11
+ marked.walkTokens([root], token => {
12
+ const feature = MARKDOWN_FEATURES[token.type];
13
+ if (feature)
14
+ found.add(feature);
15
+ if (token.type === 'code') {
16
+ const language = LANGUAGES[(token.lang ?? '').split(/\s+/)[0].toLowerCase()];
17
+ if (language)
18
+ found.add('language:' + language);
19
+ }
20
+ if (token.type === 'list') {
21
+ const list = token;
22
+ found.add(list.ordered ? 'ordered_list' : 'bullet_list');
23
+ for (const item of list.items) {
24
+ if (item.task) {
25
+ found.add('checklist');
26
+ found.add(item.checked ? 'checked' : 'unchecked');
27
+ }
28
+ if (item.tokens.some(t => t.type === 'list'))
29
+ found.add('nested_list');
30
+ }
31
+ }
32
+ });
33
+ }
34
+ }
35
+ /** Semantic features that can be checked in persisted Slack blocks. Table
36
+ * cells retain their separate table contract; do not let their styles satisfy
37
+ * an unrelated prose feature elsewhere in the message. */
38
+ export function storedRichFeatures(blocks) {
39
+ const found = new Set();
40
+ const pending = Array.isArray(blocks) ? [...blocks] : [];
41
+ while (pending.length) {
42
+ const value = pending.pop();
43
+ if (!value || typeof value !== 'object')
44
+ continue;
45
+ const node = value;
46
+ if (node['type'] === 'table')
47
+ continue;
48
+ const type = node['type'];
49
+ // Slack can persist a posted markdown block verbatim; its features still
50
+ // count as delivered, so lex it instead of skipping it.
51
+ if (type === 'markdown' && typeof node['text'] === 'string') {
52
+ collectMarkdownFeatures(node['text'], found);
53
+ continue;
54
+ }
55
+ if (type === 'header')
56
+ found.add('heading');
57
+ if (type === 'divider')
58
+ found.add('divider');
59
+ if (type === 'image')
60
+ found.add('image');
61
+ if (type === 'link')
62
+ found.add('link');
63
+ if (type === 'rich_text_quote')
64
+ found.add('quote');
65
+ if (type === 'rich_text_preformatted') {
66
+ found.add('code');
67
+ const language = LANGUAGES[String(node['language']).toLowerCase()];
68
+ if (language)
69
+ found.add(`language:${language}`);
70
+ }
71
+ if (type === 'rich_text_list') {
72
+ found.add(node['style'] === 'ordered' ? 'ordered_list' : 'bullet_list');
73
+ if (Number(node['indent']) > 0)
74
+ found.add('nested_list');
75
+ if (node['checklist'])
76
+ found.add('checklist');
77
+ }
78
+ if (typeof node['checked'] === 'boolean')
79
+ found.add(node['checked'] ? 'checked' : 'unchecked');
80
+ if (node['style'] && typeof node['style'] === 'object') {
81
+ const style = node['style'];
82
+ for (const key of ['bold', 'italic', 'strike'])
83
+ if (style[key])
84
+ found.add(key);
85
+ if (style['code'])
86
+ found.add('inline_code');
87
+ }
88
+ for (const key of ['elements', 'blocks'])
89
+ if (Array.isArray(node[key]))
90
+ pending.push(...node[key]);
91
+ }
92
+ return [...found].sort();
93
+ }
94
+ export function expectedRichFeatures(blocks) {
95
+ const found = new Set(storedRichFeatures(blocks));
96
+ if (!Array.isArray(blocks))
97
+ return [...found];
98
+ for (const block of blocks) {
99
+ if (block?.type !== 'markdown' || typeof block.text !== 'string')
100
+ continue;
101
+ collectMarkdownFeatures(block.text, found);
102
+ }
103
+ return [...found].sort();
104
+ }
105
+ //# sourceMappingURL=render-features.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-features.js","sourceRoot":"","sources":["../../../src/slack/render-features.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,110 @@
1
+ import { nextDeliverySeq, SELF_DELIVERY_TTL_MS } from '../messaging/turn-delivery.js';
2
+ const MAX_RECORDS = 1024;
3
+ // Retain start proof independently of the 20-minute display window. This is
4
+ // bounded process-local deduplication, not distributed exactly-once delivery.
5
+ const RETENTION_MS = 4 * 60 * 60_000 + SELF_DELIVERY_TTL_MS;
6
+ const validId = (value) => typeof value === 'string' && value.length > 0 && value.length <= 256;
7
+ function address(target) {
8
+ if (target.channel !== 'slack' || !validId(target.targetId)
9
+ || (target.threadId !== undefined && !validId(target.threadId)))
10
+ return null;
11
+ return JSON.stringify([target.channel, target.targetId, target.threadId ?? '']);
12
+ }
13
+ export function createSlackReplyDeliveryLedger(options = {}) {
14
+ const now = options.now ?? Date.now;
15
+ const nextAnchor = options.nextAnchor ?? nextDeliverySeq;
16
+ const records = new Map();
17
+ // Never evict a pending vendor operation: duplicates must join its promise.
18
+ const flights = new Map();
19
+ function prune() {
20
+ for (const [id, entry] of records)
21
+ if (now() - entry.at >= RETENTION_MS)
22
+ records.delete(id);
23
+ while (records.size > MAX_RECORDS)
24
+ records.delete(records.keys().next().value);
25
+ }
26
+ function lookup(id, target) {
27
+ prune();
28
+ const key = address(target), entry = flights.get(id)?.entry ?? records.get(id);
29
+ return key && entry?.target === key ? entry : undefined;
30
+ }
31
+ function remember(id, target, scope) {
32
+ const key = address(target);
33
+ if (!validId(id) || !validId(scope) || !key)
34
+ return;
35
+ prune();
36
+ const existing = flights.get(id)?.entry ?? records.get(id);
37
+ if (existing)
38
+ return; // A later notification cannot retarget a request.
39
+ records.set(id, { target: key, scope, attempted: false, at: now() });
40
+ prune();
41
+ }
42
+ function started(id, target, scope, observedAnchor) {
43
+ remember(id, target, scope);
44
+ const entry = lookup(id, target);
45
+ if (!entry || entry.scope !== scope)
46
+ return undefined;
47
+ if (entry.attempted)
48
+ return entry.anchor;
49
+ if (entry.anchor === undefined) {
50
+ const anchor = observedAnchor ?? nextAnchor();
51
+ if (!Number.isSafeInteger(anchor) || anchor <= 0)
52
+ return undefined;
53
+ entry.anchor = anchor;
54
+ entry.at = now();
55
+ }
56
+ return entry.anchor;
57
+ }
58
+ function deliver(id, target, action) {
59
+ const key = address(target);
60
+ if (!key)
61
+ return Promise.resolve();
62
+ // A legacy completion without identity must not be confused with another
63
+ // request. Preserve its existing fail-open behavior, without guessed proof.
64
+ if (!validId(id))
65
+ return action(undefined);
66
+ const flight = flights.get(id);
67
+ if (flight)
68
+ return flight.entry.target === key ? flight.promise : Promise.resolve();
69
+ prune();
70
+ const previous = records.get(id);
71
+ if (previous && (previous.target !== key || previous.attempted))
72
+ return Promise.resolve();
73
+ const entry = previous ?? { target: key, scope: 'default', attempted: false, at: now() };
74
+ entry.attempted = true;
75
+ let resolve, reject;
76
+ const promise = new Promise((done, fail) => { resolve = done; reject = fail; });
77
+ flights.set(id, { entry, promise });
78
+ records.set(id, entry);
79
+ const finish = (failed, error) => {
80
+ entry.at = now();
81
+ records.delete(id);
82
+ records.set(id, entry);
83
+ prune();
84
+ flights.delete(id);
85
+ if (failed)
86
+ reject(error);
87
+ else
88
+ resolve();
89
+ };
90
+ // Invoke synchronously after the claim so the caller can reserve a body
91
+ // lane before its producer returns and schedules the next turn.
92
+ try {
93
+ void Promise.resolve(action(entry.anchor)).then(() => finish(false), error => finish(true, error));
94
+ }
95
+ catch (error) {
96
+ finish(true, error);
97
+ }
98
+ return promise;
99
+ }
100
+ return {
101
+ remember, started, deliver,
102
+ anchor: (id, target) => lookup(id, target)?.anchor,
103
+ scope: (id, target) => lookup(id, target)?.scope,
104
+ claimed(id, _target) {
105
+ prune();
106
+ return flights.has(id) || records.get(id)?.attempted === true;
107
+ },
108
+ };
109
+ }
110
+ //# sourceMappingURL=reply-delivery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reply-delivery.js","sourceRoot":"","sources":["../../../src/slack/reply-delivery.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,143 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { verifiedSlackWorkspace } from './verified-workspace.js';
3
+ export const RTS_OUTPUT_MARKER = 'jaw_rts_response_v1';
4
+ const EXCLUDED_TEXT = '[검색 응답은 Slack에서 확인하세요: 모델 기록에서 제외됨]';
5
+ const OUTPUT_CAP = 100000;
6
+ const HOLD_CAP = 1024;
7
+ const PUBLICATION_CAP = 100000;
8
+ const timestamp = (value) => typeof value === 'string' && /^\d{1,13}\.\d{1,6}$/.test(value);
9
+ /** Stores only our newly published output addresses, never RTS source data. */
10
+ export class RtsOutputStore {
11
+ database;
12
+ constructor(database) {
13
+ this.database = database;
14
+ database.exec(`CREATE TABLE IF NOT EXISTS slack_rts_outputs (
15
+ workspace TEXT NOT NULL, destination TEXT NOT NULL, output_ts TEXT NOT NULL,
16
+ PRIMARY KEY(workspace,destination,output_ts));
17
+ CREATE TABLE IF NOT EXISTS slack_rts_holds (
18
+ workspace TEXT NOT NULL, destination TEXT NOT NULL, invocation_id TEXT NOT NULL,
19
+ PRIMARY KEY(workspace,destination,invocation_id));
20
+ CREATE TABLE IF NOT EXISTS slack_rts_publications (
21
+ workspace TEXT NOT NULL, destination TEXT NOT NULL, invocation_id TEXT NOT NULL,
22
+ proof_json TEXT NOT NULL, PRIMARY KEY(workspace,destination,invocation_id));
23
+ CREATE TABLE IF NOT EXISTS slack_quote_operations (
24
+ owner_hash TEXT NOT NULL, invocation_id TEXT NOT NULL, request_hash TEXT NOT NULL,
25
+ status TEXT NOT NULL, receipt_json TEXT,
26
+ PRIMARY KEY(owner_hash,invocation_id));`);
27
+ }
28
+ quoteOperation(owner, invocation) {
29
+ return this.database.prepare('SELECT request_hash,status,receipt_json FROM slack_quote_operations WHERE owner_hash=? AND invocation_id=?').get(owner, invocation);
30
+ }
31
+ reserveQuote(owner, invocation, hash) {
32
+ const count = this.database.prepare('SELECT count(*) AS n FROM slack_quote_operations').get();
33
+ if (count.n >= 10000)
34
+ return false;
35
+ return this.database.prepare('INSERT OR IGNORE INTO slack_quote_operations(owner_hash,invocation_id,request_hash,status) VALUES (?,?,?,?)').run(owner, invocation, hash, 'pending').changes === 1;
36
+ }
37
+ settleQuote(owner, invocation, receipt) {
38
+ const json = receipt === undefined ? null : JSON.stringify(receipt);
39
+ if (json && Buffer.byteLength(json) > 4096)
40
+ throw new Error('slack_quote_receipt_too_large');
41
+ this.database.prepare('UPDATE slack_quote_operations SET status=?,receipt_json=? WHERE owner_hash=? AND invocation_id=?').run(json ? 'completed' : 'unknown', json, owner, invocation);
42
+ }
43
+ begin(workspace, destination, invocation, input) {
44
+ return this.database.transaction(() => {
45
+ const outputs = this.database.prepare('SELECT count(*) AS n FROM slack_rts_outputs').get();
46
+ const holds = this.database.prepare('SELECT count(*) AS n FROM slack_rts_holds').get();
47
+ const publications = this.database.prepare('SELECT count(*) AS n FROM slack_rts_publications').get();
48
+ if (outputs.n >= OUTPUT_CAP || holds.n >= HOLD_CAP || (input && publications.n >= PUBLICATION_CAP))
49
+ return false;
50
+ if (this.publication(workspace, destination, invocation))
51
+ return false;
52
+ if (input && (input.expectedOutputs !== 1 || (input.threadTs !== null && !timestamp(input.threadTs))
53
+ || !input.actor || !input.botUserId || !input.credentialKey))
54
+ return false;
55
+ const inserted = this.database.prepare('INSERT OR IGNORE INTO slack_rts_holds(workspace,destination,invocation_id) VALUES (?,?,?)').run(workspace, destination, invocation).changes === 1;
56
+ if (inserted && input) {
57
+ const proof = { ...input, lease: randomUUID(), state: 'active', terminalAt: null, knownOutputs: [], verified: false };
58
+ this.database.prepare('INSERT INTO slack_rts_publications VALUES (?,?,?,?)').run(workspace, destination, invocation, JSON.stringify(proof));
59
+ }
60
+ return inserted;
61
+ })();
62
+ }
63
+ record(workspace, destination, outputTs) {
64
+ const count = this.database.prepare('SELECT count(*) AS n FROM slack_rts_outputs').get();
65
+ if (count.n >= OUTPUT_CAP)
66
+ throw new Error('slack_rts_output_capacity');
67
+ this.database.prepare('INSERT OR IGNORE INTO slack_rts_outputs(workspace,destination,output_ts) VALUES (?,?,?)').run(workspace, destination, outputTs);
68
+ }
69
+ publication(workspace, destination, invocation) {
70
+ const row = this.database.prepare('SELECT proof_json FROM slack_rts_publications WHERE workspace=? AND destination=? AND invocation_id=?').get(workspace, destination, invocation);
71
+ if (!row)
72
+ return undefined;
73
+ const proof = JSON.parse(row.proof_json);
74
+ if (!proof || typeof proof.lease !== 'string' || !['active', 'terminal', 'reconciled'].includes(proof.state)
75
+ || proof.expectedOutputs !== 1 || !Array.isArray(proof.knownOutputs) || proof.knownOutputs.some(id => !timestamp(id)))
76
+ throw new Error('slack_rts_proof_invalid');
77
+ return proof;
78
+ }
79
+ terminal(workspace, destination, invocation, lease, knownOutputs, verified) {
80
+ this.database.transaction(() => {
81
+ const proof = this.publication(workspace, destination, invocation);
82
+ if (!proof || proof.lease !== lease || proof.state !== 'active')
83
+ throw new Error('slack_rts_publisher_not_owned');
84
+ if (knownOutputs.length > proof.expectedOutputs || knownOutputs.some(id => !timestamp(id)))
85
+ throw new Error('slack_rts_output_count_invalid');
86
+ const next = { ...proof, state: 'terminal', terminalAt: Date.now(), knownOutputs: [...knownOutputs], verified };
87
+ this.database.prepare('UPDATE slack_rts_publications SET proof_json=? WHERE workspace=? AND destination=? AND invocation_id=?').run(JSON.stringify(next), workspace, destination, invocation);
88
+ })();
89
+ }
90
+ reconcile(workspace, destination, invocation, lease, outputs) {
91
+ return this.database.transaction(() => {
92
+ const proof = this.publication(workspace, destination, invocation);
93
+ if (!proof || proof.lease !== lease || proof.state !== 'terminal' || !Number.isFinite(proof.terminalAt)
94
+ || outputs.length !== proof.expectedOutputs || new Set(outputs).size !== outputs.length || outputs.some(id => !timestamp(id))
95
+ || proof.knownOutputs.some(id => !outputs.includes(id)))
96
+ return false;
97
+ if (!this.database.prepare('SELECT 1 FROM slack_rts_holds WHERE workspace=? AND destination=? AND invocation_id=?').get(workspace, destination, invocation))
98
+ return false;
99
+ for (const output of outputs)
100
+ this.record(workspace, destination, output);
101
+ this.database.prepare('UPDATE slack_rts_publications SET proof_json=? WHERE workspace=? AND destination=? AND invocation_id=?').run(JSON.stringify({ ...proof, state: 'reconciled', knownOutputs: outputs }), workspace, destination, invocation);
102
+ this.database.prepare('DELETE FROM slack_rts_holds WHERE workspace=? AND destination=? AND invocation_id=?').run(workspace, destination, invocation);
103
+ return true;
104
+ })();
105
+ }
106
+ finish(workspace, destination, invocation) {
107
+ const proof = this.publication(workspace, destination, invocation);
108
+ if (proof?.state === 'terminal' && proof.verified)
109
+ this.reconcile(workspace, destination, invocation, proof.lease, proof.knownOutputs);
110
+ }
111
+ held(workspace, destination) {
112
+ return Boolean(this.database.prepare('SELECT 1 FROM slack_rts_holds WHERE workspace=? AND destination=? LIMIT 1').get(workspace, destination));
113
+ }
114
+ contains(workspace, destination, outputTs) {
115
+ return Boolean(this.database.prepare('SELECT 1 FROM slack_rts_outputs WHERE workspace=? AND destination=? AND output_ts=?').get(workspace, destination, outputTs));
116
+ }
117
+ }
118
+ let runtimeStore;
119
+ /** Undefined is the pure-library mode; the server always explicitly configures readiness. */
120
+ export function configureRtsOutputStore(store) { runtimeStore = store; }
121
+ export function getRtsOutputStore() { return runtimeStore ?? null; }
122
+ export function isRtsOutput(blocks) {
123
+ return Array.isArray(blocks) && blocks.slice(0, 50).some(block => block && typeof block === 'object'
124
+ && typeof block['block_id'] === 'string'
125
+ && (block['block_id'] === RTS_OUTPUT_MARKER || String(block['block_id']).startsWith(`${RTS_OUTPUT_MARKER}:`)));
126
+ }
127
+ export function excludedRtsMessage(ts, threadTs) {
128
+ return { ts, ...(threadTs ? { threadTs } : {}), text: EXCLUDED_TEXT, contentExcluded: true, contentTruncated: true };
129
+ }
130
+ export async function filterRtsOutputs(token, channel, messages, signal, fetchImpl) {
131
+ const store = runtimeStore;
132
+ if (store === undefined)
133
+ return messages;
134
+ if (store === null)
135
+ throw new Error('slack_privacy_store_unavailable');
136
+ const workspace = await verifiedSlackWorkspace(token, { sensitiveResponse: true, ...(fetchImpl ? { fetchImpl } : {}), ...(signal ? { signal } : {}) });
137
+ if (!workspace || signal?.aborted)
138
+ throw new Error('slack_privacy_workspace_unverified');
139
+ const held = store.held(workspace.teamId, channel);
140
+ return messages.map(message => held || store.contains(workspace.teamId, channel, message.ts)
141
+ ? excludedRtsMessage(message.ts, message.threadTs) : message);
142
+ }
143
+ //# sourceMappingURL=rts-output-store.js.map