@krischoichoi/channel-harness 0.6.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 (202) hide show
  1. package/README.md +110 -0
  2. package/lib/access/controller.d.ts +37 -0
  3. package/lib/access/controller.d.ts.map +1 -0
  4. package/lib/access/controller.js +51 -0
  5. package/lib/access/controller.js.map +1 -0
  6. package/lib/access/decision.d.ts +18 -0
  7. package/lib/access/decision.d.ts.map +1 -0
  8. package/lib/access/decision.js +2 -0
  9. package/lib/access/decision.js.map +1 -0
  10. package/lib/access/resolver.d.ts +41 -0
  11. package/lib/access/resolver.d.ts.map +1 -0
  12. package/lib/access/resolver.js +31 -0
  13. package/lib/access/resolver.js.map +1 -0
  14. package/lib/agent-manager.d.ts +314 -0
  15. package/lib/agent-manager.d.ts.map +1 -0
  16. package/lib/agent-manager.js +603 -0
  17. package/lib/agent-manager.js.map +1 -0
  18. package/lib/agent-router.d.ts +37 -0
  19. package/lib/agent-router.d.ts.map +1 -0
  20. package/lib/agent-router.js +33 -0
  21. package/lib/agent-router.js.map +1 -0
  22. package/lib/bind-support.d.ts +23 -0
  23. package/lib/bind-support.d.ts.map +1 -0
  24. package/lib/bind-support.js +63 -0
  25. package/lib/bind-support.js.map +1 -0
  26. package/lib/binding-store.d.ts +104 -0
  27. package/lib/binding-store.d.ts.map +1 -0
  28. package/lib/binding-store.js +354 -0
  29. package/lib/binding-store.js.map +1 -0
  30. package/lib/bridge.d.ts +248 -0
  31. package/lib/bridge.d.ts.map +1 -0
  32. package/lib/bridge.js +961 -0
  33. package/lib/bridge.js.map +1 -0
  34. package/lib/channel-label.d.ts +46 -0
  35. package/lib/channel-label.d.ts.map +1 -0
  36. package/lib/channel-label.js +89 -0
  37. package/lib/channel-label.js.map +1 -0
  38. package/lib/channel-session-factory.d.ts +76 -0
  39. package/lib/channel-session-factory.d.ts.map +1 -0
  40. package/lib/channel-session-factory.js +187 -0
  41. package/lib/channel-session-factory.js.map +1 -0
  42. package/lib/commands/bind.d.ts +21 -0
  43. package/lib/commands/bind.d.ts.map +1 -0
  44. package/lib/commands/bind.js +91 -0
  45. package/lib/commands/bind.js.map +1 -0
  46. package/lib/commands/help.d.ts +15 -0
  47. package/lib/commands/help.d.ts.map +1 -0
  48. package/lib/commands/help.js +89 -0
  49. package/lib/commands/help.js.map +1 -0
  50. package/lib/commands/index.d.ts +122 -0
  51. package/lib/commands/index.d.ts.map +1 -0
  52. package/lib/commands/index.js +63 -0
  53. package/lib/commands/index.js.map +1 -0
  54. package/lib/commands/locale.d.ts +9 -0
  55. package/lib/commands/locale.d.ts.map +1 -0
  56. package/lib/commands/locale.js +27 -0
  57. package/lib/commands/locale.js.map +1 -0
  58. package/lib/commands/mirror.d.ts +14 -0
  59. package/lib/commands/mirror.d.ts.map +1 -0
  60. package/lib/commands/mirror.js +44 -0
  61. package/lib/commands/mirror.js.map +1 -0
  62. package/lib/commands/model.d.ts +20 -0
  63. package/lib/commands/model.d.ts.map +1 -0
  64. package/lib/commands/model.js +106 -0
  65. package/lib/commands/model.js.map +1 -0
  66. package/lib/commands/models.d.ts +14 -0
  67. package/lib/commands/models.d.ts.map +1 -0
  68. package/lib/commands/models.js +67 -0
  69. package/lib/commands/models.js.map +1 -0
  70. package/lib/commands/new.d.ts +14 -0
  71. package/lib/commands/new.d.ts.map +1 -0
  72. package/lib/commands/new.js +37 -0
  73. package/lib/commands/new.js.map +1 -0
  74. package/lib/commands/status.d.ts +12 -0
  75. package/lib/commands/status.d.ts.map +1 -0
  76. package/lib/commands/status.js +30 -0
  77. package/lib/commands/status.js.map +1 -0
  78. package/lib/commands/stop.d.ts +16 -0
  79. package/lib/commands/stop.d.ts.map +1 -0
  80. package/lib/commands/stop.js +27 -0
  81. package/lib/commands/stop.js.map +1 -0
  82. package/lib/commands/version.d.ts +25 -0
  83. package/lib/commands/version.d.ts.map +1 -0
  84. package/lib/commands/version.js +57 -0
  85. package/lib/commands/version.js.map +1 -0
  86. package/lib/config.d.ts +119 -0
  87. package/lib/config.d.ts.map +1 -0
  88. package/lib/config.js +60 -0
  89. package/lib/config.js.map +1 -0
  90. package/lib/debug-logger.d.ts +8 -0
  91. package/lib/debug-logger.d.ts.map +1 -0
  92. package/lib/debug-logger.js +32 -0
  93. package/lib/debug-logger.js.map +1 -0
  94. package/lib/dsh-home.d.ts +12 -0
  95. package/lib/dsh-home.d.ts.map +1 -0
  96. package/lib/dsh-home.js +31 -0
  97. package/lib/dsh-home.js.map +1 -0
  98. package/lib/failure-display.d.ts +10 -0
  99. package/lib/failure-display.d.ts.map +1 -0
  100. package/lib/failure-display.js +42 -0
  101. package/lib/failure-display.js.map +1 -0
  102. package/lib/file-provider.d.ts +96 -0
  103. package/lib/file-provider.d.ts.map +1 -0
  104. package/lib/file-provider.js +44 -0
  105. package/lib/file-provider.js.map +1 -0
  106. package/lib/index.d.ts +31 -0
  107. package/lib/index.d.ts.map +1 -0
  108. package/lib/index.js +31 -0
  109. package/lib/index.js.map +1 -0
  110. package/lib/interactions/index.d.ts +13 -0
  111. package/lib/interactions/index.d.ts.map +1 -0
  112. package/lib/interactions/index.js +13 -0
  113. package/lib/interactions/index.js.map +1 -0
  114. package/lib/interactions/question-backend.d.ts +133 -0
  115. package/lib/interactions/question-backend.d.ts.map +1 -0
  116. package/lib/interactions/question-backend.js +37 -0
  117. package/lib/interactions/question-backend.js.map +1 -0
  118. package/lib/interactions/question-presenter.d.ts +68 -0
  119. package/lib/interactions/question-presenter.d.ts.map +1 -0
  120. package/lib/interactions/question-presenter.js +425 -0
  121. package/lib/interactions/question-presenter.js.map +1 -0
  122. package/lib/interactions/question-renderer.d.ts +37 -0
  123. package/lib/interactions/question-renderer.d.ts.map +1 -0
  124. package/lib/interactions/question-renderer.js +79 -0
  125. package/lib/interactions/question-renderer.js.map +1 -0
  126. package/lib/interactions/question-state.d.ts +106 -0
  127. package/lib/interactions/question-state.d.ts.map +1 -0
  128. package/lib/interactions/question-state.js +99 -0
  129. package/lib/interactions/question-state.js.map +1 -0
  130. package/lib/interactions/question-text-answer.d.ts +37 -0
  131. package/lib/interactions/question-text-answer.d.ts.map +1 -0
  132. package/lib/interactions/question-text-answer.js +70 -0
  133. package/lib/interactions/question-text-answer.js.map +1 -0
  134. package/lib/interactions/question-waterfall-backend.d.ts +42 -0
  135. package/lib/interactions/question-waterfall-backend.d.ts.map +1 -0
  136. package/lib/interactions/question-waterfall-backend.js +162 -0
  137. package/lib/interactions/question-waterfall-backend.js.map +1 -0
  138. package/lib/lifecycle.d.ts +36 -0
  139. package/lib/lifecycle.d.ts.map +1 -0
  140. package/lib/lifecycle.js +303 -0
  141. package/lib/lifecycle.js.map +1 -0
  142. package/lib/loggable-error.d.ts +10 -0
  143. package/lib/loggable-error.d.ts.map +1 -0
  144. package/lib/loggable-error.js +32 -0
  145. package/lib/loggable-error.js.map +1 -0
  146. package/lib/message-converter.d.ts +64 -0
  147. package/lib/message-converter.d.ts.map +1 -0
  148. package/lib/message-converter.js +191 -0
  149. package/lib/message-converter.js.map +1 -0
  150. package/lib/model-selection.d.ts +54 -0
  151. package/lib/model-selection.d.ts.map +1 -0
  152. package/lib/model-selection.js +116 -0
  153. package/lib/model-selection.js.map +1 -0
  154. package/lib/outbox/binding-resolver.d.ts +23 -0
  155. package/lib/outbox/binding-resolver.d.ts.map +1 -0
  156. package/lib/outbox/binding-resolver.js +38 -0
  157. package/lib/outbox/binding-resolver.js.map +1 -0
  158. package/lib/outbox/capabilities.d.ts +48 -0
  159. package/lib/outbox/capabilities.d.ts.map +1 -0
  160. package/lib/outbox/capabilities.js +35 -0
  161. package/lib/outbox/capabilities.js.map +1 -0
  162. package/lib/outbox/index.d.ts +14 -0
  163. package/lib/outbox/index.d.ts.map +1 -0
  164. package/lib/outbox/index.js +14 -0
  165. package/lib/outbox/index.js.map +1 -0
  166. package/lib/outbox/service.d.ts +48 -0
  167. package/lib/outbox/service.d.ts.map +1 -0
  168. package/lib/outbox/service.js +74 -0
  169. package/lib/outbox/service.js.map +1 -0
  170. package/lib/outbox/target.d.ts +17 -0
  171. package/lib/outbox/target.d.ts.map +1 -0
  172. package/lib/outbox/target.js +15 -0
  173. package/lib/outbox/target.js.map +1 -0
  174. package/lib/outbox/tool-send.d.ts +19 -0
  175. package/lib/outbox/tool-send.d.ts.map +1 -0
  176. package/lib/outbox/tool-send.js +77 -0
  177. package/lib/outbox/tool-send.js.map +1 -0
  178. package/lib/outbox/types.d.ts +42 -0
  179. package/lib/outbox/types.d.ts.map +1 -0
  180. package/lib/outbox/types.js +30 -0
  181. package/lib/outbox/types.js.map +1 -0
  182. package/lib/plugin.d.ts +27 -0
  183. package/lib/plugin.d.ts.map +1 -0
  184. package/lib/plugin.js +47 -0
  185. package/lib/plugin.js.map +1 -0
  186. package/lib/reply-context-store.d.ts +69 -0
  187. package/lib/reply-context-store.d.ts.map +1 -0
  188. package/lib/reply-context-store.js +75 -0
  189. package/lib/reply-context-store.js.map +1 -0
  190. package/lib/reply-router.d.ts +138 -0
  191. package/lib/reply-router.d.ts.map +1 -0
  192. package/lib/reply-router.js +674 -0
  193. package/lib/reply-router.js.map +1 -0
  194. package/lib/session-router.d.ts +49 -0
  195. package/lib/session-router.d.ts.map +1 -0
  196. package/lib/session-router.js +45 -0
  197. package/lib/session-router.js.map +1 -0
  198. package/lib/workspace-resolver.d.ts +67 -0
  199. package/lib/workspace-resolver.d.ts.map +1 -0
  200. package/lib/workspace-resolver.js +102 -0
  201. package/lib/workspace-resolver.js.map +1 -0
  202. package/package.json +103 -0
@@ -0,0 +1,425 @@
1
+ import { renderQuestionMessage } from './question-renderer.js';
2
+ import { parseQuestionTextAnswer } from './question-text-answer.js';
3
+ import { newReplyToken, QuestionStateStore, } from './question-state.js';
4
+ function targetKey(target) {
5
+ return `${target.channelId}:${target.accountId}:${target.conversationId}:${target.threadId ?? ''}`;
6
+ }
7
+ function eventKey(event) {
8
+ return `${event.channel}:${event.accountId}:${event.conversation.id}:${event.conversation.threadId ?? ''}`;
9
+ }
10
+ function textOf(event) {
11
+ return event.message.content
12
+ .filter((part) => part.type === 'text')
13
+ .map((part) => part.text)
14
+ .join('')
15
+ .trim();
16
+ }
17
+ /**
18
+ * Presents Harness-origin questions (the official `user-questions/request`
19
+ * waterfall) through generic channel actions.
20
+ */
21
+ export class ChannelQuestionPresenter {
22
+ options;
23
+ state = new QuestionStateStore();
24
+ constructor(options) {
25
+ this.options = options;
26
+ }
27
+ /** Connect the sink and open the backend transport. */
28
+ start() {
29
+ this.options.backend.start(this);
30
+ }
31
+ /** Cancel every open question, then tear the backend transport down. */
32
+ async stop() {
33
+ for (const pending of this.state.all()) {
34
+ await this.cancel(pending, 'channel question bridge stopped');
35
+ }
36
+ await this.options.backend.stop();
37
+ }
38
+ // ---------------------------------------------------------------------------
39
+ // QuestionInteractionSink — harness -> channel
40
+ // ---------------------------------------------------------------------------
41
+ /**
42
+ * Present one question batch on the channel conversation bound to the
43
+ * asking session. Returns false when the channel cannot take ownership
44
+ * (no active reply context, no binding, adapter absent / text unsupported,
45
+ * or another question already pending on that conversation) — the backend
46
+ * decides what a decline means per transport.
47
+ *
48
+ * `interactiveActions` decides only presentation quality, never admission:
49
+ * any `text: true` adapter can answer via numbered plain text; a
50
+ * `interactiveActions: true` adapter upgrades to native buttons.
51
+ */
52
+ async questionRequested(request) {
53
+ // Mux replay of a still-pending question (the official stream reuses the
54
+ // rpcId verbatim on reopen): already owned, keep presenting it.
55
+ if (this.state.getByKey(request.key))
56
+ return true;
57
+ const sessionId = request.sessionId;
58
+ const active = this.options.replyContexts.getActiveForSession(sessionId);
59
+ const binding = this.options.agentManager.bindingFor(sessionId);
60
+ if (!active || !binding || !active.context.senderId)
61
+ return false;
62
+ const adapter = this.options.getAdapter(binding.channelId);
63
+ if (!adapter || !adapter.capabilities.text)
64
+ return false;
65
+ const presentationMode = adapter.capabilities.interactiveActions === true ? 'actions' : 'text';
66
+ const target = {
67
+ channelId: binding.channelId,
68
+ accountId: binding.accountId,
69
+ conversationId: binding.conversationId,
70
+ conversationType: active.context.conversationType,
71
+ ...(binding.threadId ? { threadId: binding.threadId } : {}),
72
+ ...(active.context.replyToMessageId
73
+ ? { replyToMessageId: active.context.replyToMessageId }
74
+ : {}),
75
+ ...(active.context.raw === undefined ? {} : { raw: active.context.raw }),
76
+ ...(active.context.runId ? { runId: active.context.runId } : {}),
77
+ };
78
+ const pending = {
79
+ key: request.key,
80
+ sessionId,
81
+ questions: request.questions,
82
+ answers: [],
83
+ questionIndex: 0,
84
+ selected: new Set(),
85
+ awaitingCustom: false,
86
+ state: 'pending',
87
+ processing: false,
88
+ responding: false,
89
+ target,
90
+ conversationKey: targetKey(target),
91
+ allowedSenderId: active.context.senderId,
92
+ actionIds: new Set(),
93
+ presentationMode,
94
+ replyToken: newReplyToken(),
95
+ };
96
+ if (!this.state.register(pending)) {
97
+ // One pending question per conversation at a time.
98
+ this.options.logger.warn('[channel-harness] channel question already pending', {
99
+ channel: binding.channelId,
100
+ account: binding.accountId,
101
+ conversation: binding.conversationId,
102
+ });
103
+ return false;
104
+ }
105
+ this.state.armTimeout(pending, this.options.timeoutMs, (expired) => {
106
+ void this.cancel(expired, '问题已超时,请重新发起。', 'expired');
107
+ });
108
+ this.options.logger.info('[channel-harness] presenting user question on channel', {
109
+ sessionId,
110
+ channel: binding.channelId,
111
+ questionCount: request.questions.length,
112
+ presentationMode,
113
+ });
114
+ try {
115
+ await this.present(pending, adapter, false);
116
+ }
117
+ catch (error) {
118
+ this.options.logger.error('[channel-harness] failed to present user question', error);
119
+ await this.cancel(pending, '无法在当前渠道展示问题,已取消。');
120
+ }
121
+ return true;
122
+ }
123
+ /** Another client answered, or the owning tool call aborted: drop controls. */
124
+ async questionSettledExternally(key, state = 'externally-settled') {
125
+ const pending = this.state.getByKey(key);
126
+ if (!pending)
127
+ return;
128
+ // Mark terminal before the first await. A channel handler may still hold
129
+ // this object after the store indexes are removed.
130
+ if (pending.state === 'pending')
131
+ pending.state = state;
132
+ await this.cleanup(pending);
133
+ }
134
+ // ---------------------------------------------------------------------------
135
+ // Channel -> answer collection
136
+ // ---------------------------------------------------------------------------
137
+ /** Consume an already-authorized channel answer before it reaches the Agent inbox. */
138
+ async handleChannelEvent(event) {
139
+ if (event.type === 'interaction.received')
140
+ return this.handleInteraction(event);
141
+ if (event.type === 'message.received')
142
+ return this.handleMessage(event);
143
+ return false;
144
+ }
145
+ async present(pending, adapter, edit) {
146
+ if (pending.state !== 'pending')
147
+ return;
148
+ const question = pending.questions[pending.questionIndex];
149
+ if (!question)
150
+ return this.submit(pending);
151
+ this.state.clearActions(pending);
152
+ const message = renderQuestionMessage(pending, question, (action) => this.state.bindAction(pending, action));
153
+ pending.renderedText = message.text;
154
+ if (edit && pending.messageId && adapter.edit) {
155
+ await adapter.edit(pending.target, pending.messageId, message);
156
+ return;
157
+ }
158
+ let result;
159
+ try {
160
+ result = await adapter.send(pending.target, message);
161
+ }
162
+ catch (error) {
163
+ // A DECLARED native-actions capability can still be unusable in a live
164
+ // deployment: a DingTalk interactive-card template that is not published
165
+ // in THIS org, a missing card/markdown-keyboard permission, … The
166
+ // capability flag is a declaration, never a probe, so a failed
167
+ // interactive presentation must NOT kill the question — every
168
+ // `text: true` adapter can answer the numbered-text form. Degrade this
169
+ // batch to text and re-render (that rendering adds the reply
170
+ // instructions and, in a group, the correlation token).
171
+ if (pending.presentationMode !== 'actions')
172
+ throw error;
173
+ this.options.logger.warn('[channel-harness] interactive actions failed to present; falling back to numbered text', {
174
+ channel: pending.target.channelId,
175
+ conversationType: pending.target.conversationType,
176
+ error: error instanceof Error ? error.message : String(error),
177
+ });
178
+ pending.presentationMode = 'text';
179
+ return this.present(pending, adapter, false);
180
+ }
181
+ if (pending.state !== 'pending') {
182
+ if (message.actions?.length && result.messageId && adapter.edit) {
183
+ await adapter.edit(pending.target, result.messageId, { actions: [] }).catch(() => { });
184
+ }
185
+ return;
186
+ }
187
+ if (message.replyPrompt)
188
+ pending.promptMessageId = result.messageId;
189
+ else
190
+ pending.messageId = result.messageId;
191
+ }
192
+ async handleInteraction(event) {
193
+ const ref = this.state.findAction(event.action);
194
+ if (!ref || ref.pending.conversationKey !== eventKey(event))
195
+ return false;
196
+ const pending = ref.pending;
197
+ if (String(event.sender.id) !== pending.allowedSenderId)
198
+ return true;
199
+ if (pending.state !== 'pending' || pending.processing || pending.responding)
200
+ return true;
201
+ const question = pending.questions[pending.questionIndex];
202
+ if (!question)
203
+ return true;
204
+ const action = ref.action;
205
+ pending.processing = true;
206
+ try {
207
+ if (action.kind === 'skip') {
208
+ await this.advance(pending, { id: question.id, selected: [] });
209
+ }
210
+ else if (action.kind === 'custom') {
211
+ pending.awaitingCustom = true;
212
+ const adapter = this.options.getAdapter(pending.target.channelId);
213
+ if (adapter) {
214
+ await this.removeButtons(pending);
215
+ if (pending.state !== 'pending')
216
+ return true;
217
+ await this.present(pending, adapter, false);
218
+ }
219
+ }
220
+ else if (action.kind === 'done') {
221
+ if (pending.selected.size > 0) {
222
+ await this.advance(pending, { id: question.id, selected: [...pending.selected] });
223
+ }
224
+ }
225
+ else if (action.optionIndex !== undefined) {
226
+ const option = question.options?.[action.optionIndex];
227
+ if (!option)
228
+ return true;
229
+ if (!question.multiSelect) {
230
+ await this.advance(pending, { id: question.id, selected: [option.label] });
231
+ }
232
+ else {
233
+ if (pending.selected.has(option.label))
234
+ pending.selected.delete(option.label);
235
+ else
236
+ pending.selected.add(option.label);
237
+ const adapter = this.options.getAdapter(pending.target.channelId);
238
+ if (adapter)
239
+ await this.present(pending, adapter, true);
240
+ }
241
+ }
242
+ }
243
+ catch (error) {
244
+ this.options.logger.error('[channel-harness] failed to process question interaction', error);
245
+ await this.cancel(pending, '提交答案失败,问题已取消。');
246
+ }
247
+ finally {
248
+ pending.processing = false;
249
+ }
250
+ return true;
251
+ }
252
+ /**
253
+ * Route one authorized text message into the question flow. DM answers parse
254
+ * directly; group/thread answers must correlate via the platform `replyTo`
255
+ * mapping or the per-question `replyToken` (stripped before parsing).
256
+ * Returns true when the message was consumed by a pending question (never
257
+ * falls through to Agent routing); false otherwise.
258
+ */
259
+ async handleMessage(event) {
260
+ const pending = this.state.getByConversation(eventKey(event));
261
+ if (!pending)
262
+ return false;
263
+ if (String(event.sender.id) !== pending.allowedSenderId)
264
+ return false;
265
+ const text = textOf(event);
266
+ if (!text || text.startsWith('/'))
267
+ return false;
268
+ if (pending.state !== 'pending' || pending.processing || pending.responding)
269
+ return true;
270
+ const question = pending.questions[pending.questionIndex];
271
+ if (!question)
272
+ return false;
273
+ const body = this.correlateMessageBody(pending, event, text);
274
+ if (body === undefined)
275
+ return false;
276
+ pending.processing = true;
277
+ try {
278
+ const parsed = parseQuestionTextAnswer(question, body);
279
+ if (parsed.kind === 'invalid') {
280
+ if (parsed.reason === 'option-out-of-range') {
281
+ this.options.logger.debug('[channel-harness] invalid text answer for pending question', {
282
+ channel: pending.target.channelId,
283
+ conversationType: pending.target.conversationType,
284
+ reason: parsed.reason,
285
+ });
286
+ const adapter = this.options.getAdapter(pending.target.channelId);
287
+ const n = question.options?.length ?? 0;
288
+ // Keep pending — do NOT cancel, do NOT re-arm the timeout. Only a
289
+ // genuine send/backend failure, timeout or external settlement ends.
290
+ if (adapter) {
291
+ await adapter
292
+ .send(pending.target, { text: `选项无效,请回复 1-${n};多选可回复 1,3。` })
293
+ .catch(() => { });
294
+ }
295
+ }
296
+ return true;
297
+ }
298
+ if (parsed.kind === 'skip') {
299
+ await this.advance(pending, { id: question.id, selected: [] });
300
+ return true;
301
+ }
302
+ if (parsed.kind === 'selected') {
303
+ await this.advance(pending, { id: question.id, selected: parsed.labels });
304
+ return true;
305
+ }
306
+ await this.advance(pending, {
307
+ id: question.id,
308
+ selected: question.multiSelect ? [...pending.selected] : [],
309
+ custom: parsed.text,
310
+ });
311
+ }
312
+ catch (error) {
313
+ this.options.logger.error('[channel-harness] failed to process question reply', error);
314
+ await this.cancel(pending, '提交答案失败,问题已取消。');
315
+ }
316
+ finally {
317
+ pending.processing = false;
318
+ }
319
+ return true;
320
+ }
321
+ /**
322
+ * Resolve the answerable text of an inbound message against a pending
323
+ * question. Returns:
324
+ * - the message body for DMs (parse directly), or
325
+ * - the token-stripped body when the platform `replyTo` maps to the prompt,
326
+ * or
327
+ * - the token-stripped body when the reply carries `pending.replyToken`
328
+ * anywhere (`Q-XXXX 2`, `Q-XXXX: 2`, `@bot Q-XXXX 2`), or
329
+ * - `undefined` when nothing correlates (leave for ordinary routing).
330
+ */
331
+ correlateMessageBody(pending, event, text) {
332
+ if (pending.target.conversationType !== 'group')
333
+ return text;
334
+ if (pending.promptMessageId && event.message.replyTo === pending.promptMessageId) {
335
+ return text;
336
+ }
337
+ // A reliable adapter-supplied mention is an explicit correlation signal:
338
+ // after the Access Gate has authorized + activated it, `@bot 2` from the
339
+ // pending question's allowed sender is an answer, not a new queued turn.
340
+ if (event.message.activation?.mentionedBot === true)
341
+ return text;
342
+ const token = pending.replyToken;
343
+ if (!token)
344
+ return undefined;
345
+ const index = text.indexOf(token);
346
+ if (index === -1)
347
+ return undefined;
348
+ const before = text
349
+ .slice(0, index)
350
+ .replace(/@\S+\s*$/u, '')
351
+ .replace(/[\s::,,]+$/u, '');
352
+ const after = text.slice(index + token.length).replace(/^[\s::,,]+/u, '');
353
+ const body = `${before} ${after}`.trim();
354
+ return body.length > 0 ? body : undefined;
355
+ }
356
+ async advance(pending, answer) {
357
+ if (pending.state !== 'pending')
358
+ return;
359
+ await this.removeButtons(pending);
360
+ if (pending.state !== 'pending')
361
+ return;
362
+ pending.answers.push(answer);
363
+ pending.questionIndex += 1;
364
+ // Reset per-question transient state so a delayed answer to a previous
365
+ // question does not leak into the next one.
366
+ pending.selected.clear();
367
+ pending.awaitingCustom = false;
368
+ pending.promptMessageId = undefined;
369
+ pending.replyToken = newReplyToken();
370
+ const adapter = this.options.getAdapter(pending.target.channelId);
371
+ if (!adapter)
372
+ return this.cancel(pending, '渠道已断开,问题已取消。');
373
+ await this.present(pending, adapter, false);
374
+ }
375
+ async submit(pending) {
376
+ if (pending.state !== 'pending' || pending.responding)
377
+ return;
378
+ pending.responding = true;
379
+ try {
380
+ await this.options.backend.resolve({
381
+ key: pending.key,
382
+ sessionId: pending.sessionId,
383
+ answer: { answers: pending.answers },
384
+ });
385
+ if (pending.state === 'pending')
386
+ pending.state = 'resolved';
387
+ await this.cleanup(pending);
388
+ }
389
+ catch (error) {
390
+ if (pending.state !== 'pending') {
391
+ await this.cleanup(pending);
392
+ return;
393
+ }
394
+ pending.responding = false;
395
+ throw error;
396
+ }
397
+ }
398
+ async cancel(pending, notice, state = 'aborted') {
399
+ if (pending.state !== 'pending' || pending.responding)
400
+ return;
401
+ pending.state = state;
402
+ pending.responding = true;
403
+ await this.removeButtons(pending);
404
+ const adapter = this.options.getAdapter(pending.target.channelId);
405
+ if (adapter)
406
+ await adapter.send(pending.target, { text: notice }).catch(() => { });
407
+ await this.options.backend.cancel({ key: pending.key, reason: notice }).catch(() => undefined);
408
+ await this.cleanup(pending);
409
+ }
410
+ async removeButtons(pending) {
411
+ this.state.clearActions(pending);
412
+ if (!pending.messageId)
413
+ return;
414
+ const adapter = this.options.getAdapter(pending.target.channelId);
415
+ if (adapter?.edit) {
416
+ await adapter.edit(pending.target, pending.messageId, { actions: [] }).catch(() => { });
417
+ }
418
+ pending.messageId = undefined;
419
+ }
420
+ async cleanup(pending) {
421
+ await this.removeButtons(pending);
422
+ this.state.remove(pending);
423
+ }
424
+ }
425
+ //# sourceMappingURL=question-presenter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"question-presenter.js","sourceRoot":"","sources":["../../src/interactions/question-presenter.ts"],"names":[],"mappings":"AAuDA,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,EACL,aAAa,EACb,kBAAkB,GAGnB,MAAM,qBAAqB,CAAC;AAW7B,SAAS,SAAS,CAAC,MAAqB;IACtC,OAAO,GAAG,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,cAAc,IAAI,MAAM,CAAC,QAAQ,IAAI,EAAE,EAAE,CAAC;AACrG,CAAC;AAED,SAAS,QAAQ,CAAC,KAA4C;IAC5D,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,YAAY,CAAC,EAAE,IAAI,KAAK,CAAC,YAAY,CAAC,QAAQ,IAAI,EAAE,EAAE,CAAC;AAC7G,CAAC;AAED,SAAS,MAAM,CAAC,KAAsB;IACpC,OAAO,KAAK,CAAC,OAAO,CAAC,OAAO;SACzB,MAAM,CAAC,CAAC,IAAI,EAA0C,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC;SAC9E,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC;SACxB,IAAI,CAAC,EAAE,CAAC;SACR,IAAI,EAAE,CAAC;AACZ,CAAC;AAED;;;GAGG;AACH,MAAM,OAAO,wBAAwB;IAGN;IAFZ,KAAK,GAAG,IAAI,kBAAkB,EAAE,CAAC;IAElD,YAA6B,OAAwC;QAAxC,YAAO,GAAP,OAAO,CAAiC;IAAG,CAAC;IAEzE,uDAAuD;IACvD,KAAK;QACH,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC;IAED,wEAAwE;IACxE,KAAK,CAAC,IAAI;QACR,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,CAAC;YACvC,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,iCAAiC,CAAC,CAAC;QAChE,CAAC;QACD,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;IACpC,CAAC;IAED,8EAA8E;IAC9E,+CAA+C;IAC/C,8EAA8E;IAE9E;;;;;;;;;;OAUG;IACH,KAAK,CAAC,iBAAiB,CAAC,OAAmC;QACzD,yEAAyE;QACzE,gEAAgE;QAChE,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC;QAElD,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;QACpC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,mBAAmB,CAAC,SAAS,CAAC,CAAC;QACzE,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;QAChE,IAAI,CAAC,MAAM,IAAI,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ;YAAE,OAAO,KAAK,CAAC;QAClE,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAC3D,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,IAAI;YAAE,OAAO,KAAK,CAAC;QACzD,MAAM,gBAAgB,GACpB,OAAO,CAAC,YAAY,CAAC,kBAAkB,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC;QAExE,MAAM,MAAM,GAAkB;YAC5B,SAAS,EAAE,OAAO,CAAC,SAAuC;YAC1D,SAAS,EAAE,OAAO,CAAC,SAAuC;YAC1D,cAAc,EAAE,OAAO,CAAC,cAAiD;YACzE,gBAAgB,EAAE,MAAM,CAAC,OAAO,CAAC,gBAAgB;YACjD,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAqC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACxF,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,gBAAgB;gBACjC,CAAC,CAAC,EAAE,gBAAgB,EAAE,MAAM,CAAC,OAAO,CAAC,gBAAqD,EAAE;gBAC5F,CAAC,CAAC,EAAE,CAAC;YACP,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;YACxE,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACjE,CAAC;QACF,MAAM,OAAO,GAA2B;YACtC,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,SAAS;YACT,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,OAAO,EAAE,EAAE;YACX,aAAa,EAAE,CAAC;YAChB,QAAQ,EAAE,IAAI,GAAG,EAAE;YACnB,cAAc,EAAE,KAAK;YACrB,KAAK,EAAE,SAAS;YAChB,UAAU,EAAE,KAAK;YACjB,UAAU,EAAE,KAAK;YACjB,MAAM;YACN,eAAe,EAAE,SAAS,CAAC,MAAM,CAAC;YAClC,eAAe,EAAE,MAAM,CAAC,OAAO,CAAC,QAAQ;YACxC,SAAS,EAAE,IAAI,GAAG,EAAE;YACpB,gBAAgB;YAChB,UAAU,EAAE,aAAa,EAAE;SAC5B,CAAC;QACF,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YAClC,mDAAmD;YACnD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,oDAAoD,EAAE;gBAC7E,OAAO,EAAE,OAAO,CAAC,SAAS;gBAC1B,OAAO,EAAE,OAAO,CAAC,SAAS;gBAC1B,YAAY,EAAE,OAAO,CAAC,cAAc;aACrC,CAAC,CAAC;YACH,OAAO,KAAK,CAAC;QACf,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,OAAO,EAAE,EAAE;YACjE,KAAK,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,cAAc,EAAE,SAAS,CAAC,CAAC;QACvD,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,uDAAuD,EAAE;YAChF,SAAS;YACT,OAAO,EAAE,OAAO,CAAC,SAAS;YAC1B,aAAa,EAAE,OAAO,CAAC,SAAS,CAAC,MAAM;YACvC,gBAAgB;SACjB,CAAC,CAAC;QACH,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;QAC9C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,mDAAmD,EAAE,KAAK,CAAC,CAAC;YACtF,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAC;QACjD,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,+EAA+E;IAC/E,KAAK,CAAC,yBAAyB,CAC7B,GAAW,EACX,QAA0C,oBAAoB;QAE9D,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QACzC,IAAI,CAAC,OAAO;YAAE,OAAO;QACrB,yEAAyE;QACzE,mDAAmD;QACnD,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS;YAAE,OAAO,CAAC,KAAK,GAAG,KAAK,CAAC;QACvD,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9B,CAAC;IAED,8EAA8E;IAC9E,+BAA+B;IAC/B,8EAA8E;IAE9E,sFAAsF;IACtF,KAAK,CAAC,kBAAkB,CAAC,KAAmB;QAC1C,IAAI,KAAK,CAAC,IAAI,KAAK,sBAAsB;YAAE,OAAO,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC;QAChF,IAAI,KAAK,CAAC,IAAI,KAAK,kBAAkB;YAAE,OAAO,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QACxE,OAAO,KAAK,CAAC;IACf,CAAC;IAEO,KAAK,CAAC,OAAO,CACnB,OAA+B,EAC/B,OAAuB,EACvB,IAAa;QAEb,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS;YAAE,OAAO;QACxC,MAAM,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QAC1D,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC3C,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;QACjC,MAAM,OAAO,GAAG,qBAAqB,CAAC,OAAO,EAAE,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE,CAClE,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,CACvC,CAAC;QACF,OAAO,CAAC,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;QACpC,IAAI,IAAI,IAAI,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;YAC9C,MAAM,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;YAC/D,OAAO;QACT,CAAC;QACD,IAAI,MAAM,CAAC;QACX,IAAI,CAAC;YACH,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACvD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,uEAAuE;YACvE,yEAAyE;YACzE,kEAAkE;YAClE,+DAA+D;YAC/D,8DAA8D;YAC9D,uEAAuE;YACvE,6DAA6D;YAC7D,wDAAwD;YACxD,IAAI,OAAO,CAAC,gBAAgB,KAAK,SAAS;gBAAE,MAAM,KAAK,CAAC;YACxD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CACtB,wFAAwF,EACxF;gBACE,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS;gBACjC,gBAAgB,EAAE,OAAO,CAAC,MAAM,CAAC,gBAAgB;gBACjD,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;aAC9D,CACF,CAAC;YACF,OAAO,CAAC,gBAAgB,GAAG,MAAM,CAAC;YAClC,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;QAC/C,CAAC;QACD,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;YAChC,IAAI,OAAO,CAAC,OAAO,EAAE,MAAM,IAAI,MAAM,CAAC,SAAS,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;gBAChE,MAAM,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,SAAS,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YACxF,CAAC;YACD,OAAO;QACT,CAAC;QACD,IAAI,OAAO,CAAC,WAAW;YAAE,OAAO,CAAC,eAAe,GAAG,MAAM,CAAC,SAAS,CAAC;;YAC/D,OAAO,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;IAC5C,CAAC;IAEO,KAAK,CAAC,iBAAiB,CAAC,KAA0B;QACxD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAChD,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,eAAe,KAAK,QAAQ,CAAC,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QAC1E,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC;QAC5B,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC,eAAe;YAAE,OAAO,IAAI,CAAC;QACrE,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS,IAAI,OAAO,CAAC,UAAU,IAAI,OAAO,CAAC,UAAU;YAAE,OAAO,IAAI,CAAC;QACzF,MAAM,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QAC1D,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAI,CAAC;QAC3B,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;QAE1B,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;QAC1B,IAAI,CAAC;YACH,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBAC3B,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;YACjE,CAAC;iBAAM,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACpC,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;gBAC9B,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;gBAClE,IAAI,OAAO,EAAE,CAAC;oBACZ,MAAM,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;oBAClC,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS;wBAAE,OAAO,IAAI,CAAC;oBAC7C,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;gBAC9C,CAAC;YACH,CAAC;iBAAM,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBAClC,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;oBAC9B,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;gBACpF,CAAC;YACH,CAAC;iBAAM,IAAI,MAAM,CAAC,WAAW,KAAK,SAAS,EAAE,CAAC;gBAC5C,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;gBACtD,IAAI,CAAC,MAAM;oBAAE,OAAO,IAAI,CAAC;gBACzB,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC;oBAC1B,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;gBAC7E,CAAC;qBAAM,CAAC;oBACN,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC;wBAAE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;;wBACzE,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;oBACxC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;oBAClE,IAAI,OAAO;wBAAE,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;gBAC1D,CAAC;YACH,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,0DAA0D,EAAE,KAAK,CAAC,CAAC;YAC7F,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC;QAC9C,CAAC;gBAAS,CAAC;YACT,OAAO,CAAC,UAAU,GAAG,KAAK,CAAC;QAC7B,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;;OAMG;IACK,KAAK,CAAC,aAAa,CAAC,KAAsB;QAChD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;QAC9D,IAAI,CAAC,OAAO;YAAE,OAAO,KAAK,CAAC;QAC3B,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC,eAAe;YAAE,OAAO,KAAK,CAAC;QACtE,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAC3B,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;QAChD,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS,IAAI,OAAO,CAAC,UAAU,IAAI,OAAO,CAAC,UAAU;YAAE,OAAO,IAAI,CAAC;QACzF,MAAM,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QAC1D,IAAI,CAAC,QAAQ;YAAE,OAAO,KAAK,CAAC;QAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,oBAAoB,CAAC,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;QAC7D,IAAI,IAAI,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;QAErC,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;QAC1B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,uBAAuB,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;YACvD,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;gBAC9B,IAAI,MAAM,CAAC,MAAM,KAAK,qBAAqB,EAAE,CAAC;oBAC5C,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,4DAA4D,EAAE;wBACtF,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS;wBACjC,gBAAgB,EAAE,OAAO,CAAC,MAAM,CAAC,gBAAgB;wBACjD,MAAM,EAAE,MAAM,CAAC,MAAM;qBACtB,CAAC,CAAC;oBACH,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;oBAClE,MAAM,CAAC,GAAG,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC,CAAC;oBACxC,kEAAkE;oBAClE,qEAAqE;oBACrE,IAAI,OAAO,EAAE,CAAC;wBACZ,MAAM,OAAO;6BACV,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,cAAc,CAAC,aAAa,EAAE,CAAC;6BAC5D,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;oBACrB,CAAC;gBACH,CAAC;gBACD,OAAO,IAAI,CAAC;YACd,CAAC;YACD,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBAC3B,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;gBAC/D,OAAO,IAAI,CAAC;YACd,CAAC;YACD,IAAI,MAAM,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;gBAC/B,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;gBAC1E,OAAO,IAAI,CAAC;YACd,CAAC;YACD,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE;gBAC1B,EAAE,EAAE,QAAQ,CAAC,EAAE;gBACf,QAAQ,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE;gBAC3D,MAAM,EAAE,MAAM,CAAC,IAAI;aACpB,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,oDAAoD,EAAE,KAAK,CAAC,CAAC;YACvF,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC;QAC9C,CAAC;gBAAS,CAAC;YACT,OAAO,CAAC,UAAU,GAAG,KAAK,CAAC;QAC7B,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;;;;;OASG;IACK,oBAAoB,CAC1B,OAA+B,EAC/B,KAAsB,EACtB,IAAY;QAEZ,IAAI,OAAO,CAAC,MAAM,CAAC,gBAAgB,KAAK,OAAO;YAAE,OAAO,IAAI,CAAC;QAC7D,IAAI,OAAO,CAAC,eAAe,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,KAAK,OAAO,CAAC,eAAe,EAAE,CAAC;YACjF,OAAO,IAAI,CAAC;QACd,CAAC;QACD,yEAAyE;QACzE,yEAAyE;QACzE,yEAAyE;QACzE,IAAI,KAAK,CAAC,OAAO,CAAC,UAAU,EAAE,YAAY,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;QACjE,MAAM,KAAK,GAAG,OAAO,CAAC,UAAU,CAAC;QACjC,IAAI,CAAC,KAAK;YAAE,OAAO,SAAS,CAAC;QAC7B,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAClC,IAAI,KAAK,KAAK,CAAC,CAAC;YAAE,OAAO,SAAS,CAAC;QACnC,MAAM,MAAM,GAAG,IAAI;aAChB,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC;aACf,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC;aACxB,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;QAC9B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;QAC1E,MAAM,IAAI,GAAG,GAAG,MAAM,IAAI,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC;QACzC,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;IAC5C,CAAC;IAEO,KAAK,CAAC,OAAO,CAAC,OAA+B,EAAE,MAAiC;QACtF,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS;YAAE,OAAO;QACxC,MAAM,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QAClC,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS;YAAE,OAAO;QACxC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC7B,OAAO,CAAC,aAAa,IAAI,CAAC,CAAC;QAC3B,uEAAuE;QACvE,4CAA4C;QAC5C,OAAO,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;QACzB,OAAO,CAAC,cAAc,GAAG,KAAK,CAAC;QAC/B,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC;QACpC,OAAO,CAAC,UAAU,GAAG,aAAa,EAAE,CAAC;QACrC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAClE,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;QAC1D,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;IAC9C,CAAC;IAEO,KAAK,CAAC,MAAM,CAAC,OAA+B;QAClD,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS,IAAI,OAAO,CAAC,UAAU;YAAE,OAAO;QAC9D,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;QAC1B,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC;gBACjC,GAAG,EAAE,OAAO,CAAC,GAAG;gBAChB,SAAS,EAAE,OAAO,CAAC,SAAS;gBAC5B,MAAM,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE;aACrC,CAAC,CAAC;YACH,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS;gBAAE,OAAO,CAAC,KAAK,GAAG,UAAU,CAAC;YAC5D,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC9B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;gBAChC,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;gBAC5B,OAAO;YACT,CAAC;YACD,OAAO,CAAC,UAAU,GAAG,KAAK,CAAC;YAC3B,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,MAAM,CAClB,OAA+B,EAC/B,MAAc,EACd,QAA+B,SAAS;QAExC,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS,IAAI,OAAO,CAAC,UAAU;YAAE,OAAO;QAC9D,OAAO,CAAC,KAAK,GAAG,KAAK,CAAC;QACtB,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;QAC1B,MAAM,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QAClC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAClE,IAAI,OAAO;YAAE,MAAM,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAClF,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAC/F,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9B,CAAC;IAEO,KAAK,CAAC,aAAa,CAAC,OAA+B;QACzD,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;QACjC,IAAI,CAAC,OAAO,CAAC,SAAS;YAAE,OAAO;QAC/B,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAClE,IAAI,OAAO,EAAE,IAAI,EAAE,CAAC;YAClB,MAAM,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,SAAS,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACzF,CAAC;QACD,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;IAChC,CAAC;IAEO,KAAK,CAAC,OAAO,CAAC,OAA+B;QACnD,MAAM,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QAClC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;CACF"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Pure renderer for one pending Harness question -> `OutboundMessage`.
3
+ *
4
+ * Pure by construction: given the pending presentation state and the official
5
+ * `AskUserQuestionItem`, it produces the exact outbound message — no adapter
6
+ * sends, no backend calls, no session lookups, no platform payloads. The only
7
+ * injected dependency is `bindAction`, which maps a pending action intent to
8
+ * its bound opaque channel-facing id (the presenter wires
9
+ * `state.bindAction`), keeping this module a plain unit-test target.
10
+ *
11
+ * Presentation is capability-driven:
12
+ *
13
+ * - `'actions'` — native buttons via `OutboundMessage.actions` (option rows,
14
+ * `其他` custom, `完成` done for multi-select, `跳过本题` skip; `✓ ` toggle
15
+ * for multi-select; plan-review approve gets `primary`).
16
+ * - `'text'` — numbered plain-text options ALWAYS emitted when options
17
+ * exist (description is an indented note, never a gate for showing the
18
+ * option), plus free-text instructions. No `actions` attached. In group
19
+ * chats a short correlation hint (`replyToken`) is appended so the answer
20
+ * can be routed without platform reply correlation.
21
+ *
22
+ * `replyPrompt` stays a best-effort hint for free-text questions
23
+ * (`needsTextReply`), never a usability precondition.
24
+ */
25
+ import type { AskUserQuestionItem } from '@deepseek-ai/dsh-user-questions/types';
26
+ import type { OutboundMessage } from '@krischoichoi/channel-core';
27
+ import type { PendingChannelQuestion, PendingQuestionAction } from './question-state.js';
28
+ /**
29
+ * Render the current question of a pending batch as an outbound channel
30
+ * message. Every official field is honoured: `header` / `question` / `detail`
31
+ * as text, `options` / `multiSelect` as numbered options (+ native actions
32
+ * when available), and `intent` as a minimal presentation cue (a plan-review
33
+ * heading tag plus a primary-styled approve button — the answer encoding is
34
+ * identical either way, so nothing is lost when a UI ignores the tag).
35
+ */
36
+ export declare function renderQuestionMessage(pending: PendingChannelQuestion, question: AskUserQuestionItem, bindAction: (action: PendingQuestionAction) => string): OutboundMessage;
37
+ //# sourceMappingURL=question-renderer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"question-renderer.d.ts","sourceRoot":"","sources":["../../src/interactions/question-renderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uCAAuC,CAAC;AACjF,OAAO,KAAK,EAAqB,eAAe,EAAE,MAAM,4BAA4B,CAAC;AACrF,OAAO,KAAK,EACV,sBAAsB,EACtB,qBAAqB,EACtB,MAAM,qBAAqB,CAAC;AAS7B;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,sBAAsB,EAC/B,QAAQ,EAAE,mBAAmB,EAC7B,UAAU,EAAE,CAAC,MAAM,EAAE,qBAAqB,KAAK,MAAM,GACpD,eAAe,CAoEjB"}
@@ -0,0 +1,79 @@
1
+ /** Options-mode reply instructions shown in text presentation (with options). */
2
+ const TEXT_OPTION_INSTRUCTIONS = [
3
+ '回复 1 / 2 / 3,或直接回复选项文字。',
4
+ '也可以直接输入自定义答案。',
5
+ '回复"跳过"可跳过本题。',
6
+ ];
7
+ /**
8
+ * Render the current question of a pending batch as an outbound channel
9
+ * message. Every official field is honoured: `header` / `question` / `detail`
10
+ * as text, `options` / `multiSelect` as numbered options (+ native actions
11
+ * when available), and `intent` as a minimal presentation cue (a plan-review
12
+ * heading tag plus a primary-styled approve button — the answer encoding is
13
+ * identical either way, so nothing is lost when a UI ignores the tag).
14
+ */
15
+ export function renderQuestionMessage(pending, question, bindAction) {
16
+ const textMode = pending.presentationMode === 'text';
17
+ const intent = question.intent;
18
+ const planReview = intent?.kind === 'plan-review';
19
+ const heading = question.header ? `**${question.header}**` : '';
20
+ const lines = [
21
+ planReview ? (heading ? `${heading}(计划评审)` : '**计划评审**') : heading,
22
+ question.question,
23
+ question.detail ?? '',
24
+ ];
25
+ // Numbered options are ALWAYS emitted when options exist — a description is
26
+ // an indented note, never a condition for showing the option.
27
+ if (question.options?.length) {
28
+ lines.push(...question.options.map((option, index) => `${index + 1}. ${option.label}${option.description ? `\n ${option.description}` : ''}`));
29
+ }
30
+ const needsTextReply = pending.awaitingCustom || !question.options?.length;
31
+ if (pending.awaitingCustom) {
32
+ lines.push('请直接回复你的自定义答案。');
33
+ }
34
+ else if (!question.options?.length) {
35
+ lines.push('请直接回复文字,或输入"跳过"。');
36
+ }
37
+ else if (textMode) {
38
+ lines.push(...TEXT_OPTION_INSTRUCTIONS);
39
+ }
40
+ // Group text presentation carries a platform-independent correlation hint so
41
+ // an answer can be routed when replyTo correlation is unavailable.
42
+ if (textMode && pending.target.conversationType === 'group' && pending.replyToken) {
43
+ lines.push(`如果当前渠道无法关联回复,请发送:${pending.replyToken} 2`);
44
+ }
45
+ if (textMode) {
46
+ return {
47
+ text: lines.filter(Boolean).join('\n\n'),
48
+ ...(needsTextReply ? { replyPrompt: { kind: 'text' } } : {}),
49
+ };
50
+ }
51
+ const approveLabel = planReview ? intent.approve : undefined;
52
+ const actions = [];
53
+ if (!needsTextReply) {
54
+ for (const [index, option] of (question.options ?? []).entries()) {
55
+ const selected = pending.selected.has(option.label);
56
+ actions.push({
57
+ actions: [{
58
+ id: bindAction({ kind: 'option', optionIndex: index }),
59
+ label: `${selected ? '✓ ' : ''}${option.label}`,
60
+ ...(option.label === approveLabel ? { style: 'primary' } : {}),
61
+ }],
62
+ });
63
+ }
64
+ if (question.options?.length) {
65
+ actions.push({ actions: [{ id: bindAction({ kind: 'custom' }), label: '其他' }] });
66
+ if (question.multiSelect) {
67
+ actions.push({ actions: [{ id: bindAction({ kind: 'done' }), label: '完成', style: 'primary' }] });
68
+ }
69
+ }
70
+ }
71
+ if (!needsTextReply) {
72
+ actions.push({ actions: [{ id: bindAction({ kind: 'skip' }), label: '跳过本题' }] });
73
+ }
74
+ return {
75
+ text: lines.filter(Boolean).join('\n\n'),
76
+ ...(needsTextReply ? { replyPrompt: { kind: 'text' } } : { actions }),
77
+ };
78
+ }
79
+ //# sourceMappingURL=question-renderer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"question-renderer.js","sourceRoot":"","sources":["../../src/interactions/question-renderer.ts"],"names":[],"mappings":"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"}