@qwen-code/channel-dingtalk 0.23.1 → 0.23.2-nightly.20260909.2e212144d3

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.
@@ -1,7 +1,7 @@
1
1
  import { ChannelBase } from '@qwen-code/channel-base';
2
2
  import { DingtalkInteractiveCardClient } from './interactive-card-client.js';
3
3
  import { type DingtalkCardCallback, type DingtalkCardCallbackResult } from './interactive-card-types.js';
4
- import type { BackgroundResponseContext, ChannelConfig, ChannelBaseOptions, Envelope, ChannelAgentBridge, ChannelOutputSegmentContext, ChannelOutputSegmentEndReason, ChannelTaskLifecycleEvent, ChannelUserInputRequestContext, SessionTarget, UserInputPresentationResult } from '@qwen-code/channel-base';
4
+ import type { BackgroundResponseContext, ChannelConfig, ChannelBaseOptions, Envelope, ChannelAgentBridge, ChannelOutputSegmentContext, ChannelOutputSegmentEndReason, ChannelPermissionRequestContext, ChannelTaskLifecycleEvent, ChannelUserInputRequestContext, SessionTarget, UserInputPresentationResult } from '@qwen-code/channel-base';
5
5
  export declare class DingtalkChannel extends ChannelBase {
6
6
  private client;
7
7
  private readonly atSender;
@@ -37,13 +37,12 @@ export declare class DingtalkChannel extends ChannelBase {
37
37
  protected readonly interactiveCardClient?: DingtalkInteractiveCardClient;
38
38
  private statusCardController?;
39
39
  private questionCardController?;
40
+ private permissionCardController?;
40
41
  private interactionPresenter?;
41
42
  private readonly inboundCardOwners;
42
43
  private readonly cardRunBySession;
43
44
  private readonly cardRuns;
44
45
  private readonly fileProjectors;
45
- private readonly blockFileProjectors;
46
- private readonly blockProjectionArmed;
47
46
  private readonly backgroundResponseAggregations;
48
47
  private readonly detachedBackgroundResponseAggregations;
49
48
  private readonly pendingBackgroundResponseTerminals;
@@ -135,18 +134,6 @@ export declare class DingtalkChannel extends ChannelBase {
135
134
  protected processInbound(envelope: Envelope): Promise<void>;
136
135
  private untrackBufferedMentionTarget;
137
136
  protected onPromptEnd(chatId: string, sessionId: string, messageId?: string): void;
138
- /**
139
- * Turn end is the only point where the block projector's held state can be
140
- * settled: ChannelBase drains the turn's queued block sends before calling
141
- * onPromptEnd, so everything already appended belongs to this turn. Flush
142
- * the held candidate bytes (a trailing `[FILE:` prefix the stream never
143
- * completed) before deleting the entry — a later delete-without-settle
144
- * would silently drop them from the delivered answer. Also disarm the
145
- * session: /clear eviction and session death settle WITHOUT draining the
146
- * turn's send chain, and any block that lands afterwards must be dropped
147
- * rather than recreating projector state.
148
- */
149
- private settleBlockFileProjector;
150
137
  /** Deliver every Agent segment immediately unless aggregation is enabled. */
151
138
  dispatchBackgroundResponse(sessionId: string, text: string, context?: BackgroundResponseContext): Promise<void>;
152
139
  private flushBackgroundResponseAggregation;
@@ -178,19 +165,22 @@ export declare class DingtalkChannel extends ChannelBase {
178
165
  private formatBackgroundAgentResponse;
179
166
  private formatBackgroundAgentLabel;
180
167
  /**
181
- * Out-of-turn one-shot sends (background responses) must not flow through
182
- * the session's block-streaming projector: a second sender interleaving with
183
- * mid-projection state can swallow the send or split a held marker.
168
+ * Body-identical to the base implementation — this override exists only to
169
+ * widen the signature, which the base seam does not declare. The background
170
+ * aggregation flush passes `prepared` so the body it already projected isn't
171
+ * projected a second time (that would re-upload its files), and
172
+ * `failOnHttpError` so a failed send throws instead of being swallowed,
173
+ * letting the flush capture the delivery plan for the next retry.
184
174
  */
185
175
  protected deliverBackgroundReply(chatId: string, text: string, sessionId: string, sourceLabel?: string, prepared?: boolean, failOnHttpError?: boolean): Promise<void>;
186
176
  protected sendResponseMessage(chatId: string, text: string, sessionId: string, sourceLabel?: string, prepared?: boolean, failOnHttpError?: boolean): Promise<void>;
187
- private projectBlockStreamChunk;
188
177
  private sendFallbackReply;
189
178
  protected onResponseComplete(chatId: string, text: string, sessionId: string, segment?: ChannelOutputSegmentContext): Promise<void>;
190
179
  protected onOutputSegmentEnd(_chatId: string, _sessionId: string, segment: ChannelOutputSegmentContext, reason: ChannelOutputSegmentEndReason): void | Promise<void>;
191
180
  protected onResponseChunk(_chatId: string, chunk: string, _sessionId: string, segment?: ChannelOutputSegmentContext): void;
192
181
  private deleteFileProjectorsForRun;
193
182
  protected presentUserInputRequest(context: ChannelUserInputRequestContext): Promise<UserInputPresentationResult>;
183
+ protected presentPermissionRequest(context: ChannelPermissionRequestContext): Promise<UserInputPresentationResult>;
194
184
  /**
195
185
  * Extract quoted/referenced message context from a reply.
196
186
  * DingTalk provides this via text.repliedMsg (newer) or quoteMessage (legacy).
@@ -1 +1 @@
1
- {"version":3,"file":"DingtalkAdapter.d.ts","sourceRoot":"","sources":["../src/DingtalkAdapter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"DingtalkAdapter.d.ts","sourceRoot":"","sources":["../src/DingtalkAdapter.ts"],"names":[],"mappings":"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"}
@@ -8,13 +8,14 @@ import { ChannelBase, isTerminalTaskLifecycleType, sanitizeLogText, sanitizeProm
8
8
  import { DINGTALK_CHUNK_LIMIT, escapeDingTalkMarkdown, normalizeDingTalkMarkdown, extractTitle, } from './markdown.js';
9
9
  import { downloadMedia } from './media.js';
10
10
  import { DingTalkMediaUploadError, findImageMarkers, readValidatedImage, replaceImageMarkers, uploadDingTalkImage, } from './outbound-image.js';
11
- import { FILE_UNAVAILABLE_NOTICE, OutboundFileProjector, projectFileText, readValidatedFile, safeFileName, uploadDingTalkFile, withFileUnavailableNotice, } from './outbound-file.js';
11
+ import { FILE_UNAVAILABLE_NOTICE, OutboundFileProjector, projectFileText, readValidatedFile, safeFileName, uploadDingTalkFile, } from './outbound-file.js';
12
12
  import { DingtalkConnectionManager, } from './DingtalkConnectionManager.js';
13
13
  import { DingtalkCardRequestError, DingtalkInteractiveCardClient, } from './interactive-card-client.js';
14
14
  import { parseDingtalkCardActorId, parseDingtalkCardCallback, parseDingtalkInteractiveCardConfig, } from './interactive-card-types.js';
15
15
  import { StatusCardController } from './status-card-controller.js';
16
16
  import { isChinesePresentationLanguage, lifecyclePresentationPhase, presentationPhaseLabel, } from './presentation-phase.js';
17
17
  import { QuestionCardController } from './question-card-controller.js';
18
+ import { PermissionCardController } from './permission-card-controller.js';
18
19
  import { DingtalkInteractionPresenter } from './interaction-presenter.js';
19
20
  function nonEmptyString(value) {
20
21
  if (typeof value !== 'string')
@@ -683,6 +684,7 @@ export class DingtalkChannel extends ChannelBase {
683
684
  interactiveCardClient;
684
685
  statusCardController;
685
686
  questionCardController;
687
+ permissionCardController;
686
688
  interactionPresenter;
687
689
  inboundCardOwners = new Map();
688
690
  cardRunBySession = new Map();
@@ -691,14 +693,6 @@ export class DingtalkChannel extends ChannelBase {
691
693
  // boundary, input requested) mints a fresh segment UUID but the projection
692
694
  // state must survive it, or a marker split across the reset leaks.
693
695
  fileProjectors = new Map();
694
- blockFileProjectors = new Map();
695
- // Sessions armed for block projection by onPromptStart and disarmed when
696
- // the turn settles (or the session dies). A block send that finds NO
697
- // projector state is only legitimate as a turn's FIRST block, which always
698
- // lands while armed: late sends from an evicted (/clear) or dead session
699
- // must be dropped, because recreating state would post the tail of a
700
- // force-split [FILE: ...] marker verbatim.
701
- blockProjectionArmed = new Set();
702
696
  backgroundResponseAggregations = new Map();
703
697
  detachedBackgroundResponseAggregations = new Set();
704
698
  pendingBackgroundResponseTerminals = new Map();
@@ -719,8 +713,7 @@ export class DingtalkChannel extends ChannelBase {
719
713
  else if (!this.config.instructions.includes('[IMAGE:')) {
720
714
  this.config.instructions += IMAGE_INSTRUCTIONS;
721
715
  }
722
- if (config.blockStreaming !== 'on' &&
723
- !this.config.instructions.includes('[FILE:')) {
716
+ if (!this.config.instructions.includes('[FILE:')) {
724
717
  this.config.instructions += FILE_INSTRUCTIONS;
725
718
  }
726
719
  this.interactiveCardConfig = parseDingtalkInteractiveCardConfig(config.interactiveCards);
@@ -752,8 +745,7 @@ export class DingtalkChannel extends ChannelBase {
752
745
  }
753
746
  },
754
747
  });
755
- if (this.interactiveCardConfig.statusCard.enabled &&
756
- config.blockStreaming !== 'on') {
748
+ if (this.interactiveCardConfig.statusCard.enabled) {
757
749
  this.statusCardController = new StatusCardController({
758
750
  client: this.interactiveCardClient,
759
751
  cancelRun: (sessionId, runId) => this.requestPromptRunCancellation(sessionId, runId),
@@ -777,18 +769,28 @@ export class DingtalkChannel extends ChannelBase {
777
769
  },
778
770
  });
779
771
  }
780
- if (this.statusCardController || this.questionCardController) {
772
+ if (this.interactiveCardConfig.permissionCard.enabled) {
773
+ this.permissionCardController = new PermissionCardController({
774
+ client: this.interactiveCardClient,
775
+ timeoutMs: this.interactiveCardConfig.permissionCard.timeoutMs,
776
+ locale: this.locale,
777
+ reserveRunProjection: (runId) => this.interactionPresenter?.reserveProjection(runId),
778
+ onError: (operation, error) => {
779
+ process.stderr.write(`[DingTalk:${this.name}] ${operation} failed: ${sanitizeLogText(String(error), 300)}\n`);
780
+ },
781
+ });
782
+ }
783
+ if (this.statusCardController ||
784
+ this.questionCardController ||
785
+ this.permissionCardController) {
781
786
  this.interactionPresenter = new DingtalkInteractionPresenter({
782
787
  statusCards: this.statusCardController,
783
788
  questionCards: this.questionCardController,
789
+ permissionCards: this.permissionCardController,
784
790
  ...(options?.displayLanguage
785
791
  ? { language: options.displayLanguage }
786
792
  : {}),
787
- ...(config.blockStreaming !== 'on'
788
- ? {
789
- sendFallback: (chatId, text, sessionId, sourceLabel) => this.sendFallbackReply(chatId, text, sessionId, sourceLabel),
790
- }
791
- : {}),
793
+ sendFallback: (chatId, text, sessionId, sourceLabel) => this.sendFallbackReply(chatId, text, sessionId, sourceLabel),
792
794
  });
793
795
  }
794
796
  }
@@ -877,7 +879,12 @@ export class DingtalkChannel extends ChannelBase {
877
879
  if (callback.actionId === 'btn_stop') {
878
880
  return (this.statusCardController?.claimStop(callback.outTrackId, callback.actorId) ?? { kind: 'ignored', actorId: callback.actorId });
879
881
  }
880
- return (this.questionCardController?.claim(callback) ?? {
882
+ const permissionResult = this.permissionCardController?.claim(callback);
883
+ if (permissionResult && permissionResult.kind !== 'ignored') {
884
+ return permissionResult;
885
+ }
886
+ return (this.questionCardController?.claim(callback) ??
887
+ permissionResult ?? {
881
888
  kind: 'ignored',
882
889
  actorId: callback.actorId,
883
890
  });
@@ -1095,10 +1102,6 @@ export class DingtalkChannel extends ChannelBase {
1095
1102
  if (projection.markerCount > 0 || streamedMarkers > 0) {
1096
1103
  process.stderr.write(`[DingTalk:${this.name}] file markers projected (final=${projection.markerCount}, streamed=${streamedMarkers})\n`);
1097
1104
  }
1098
- if (this.config.blockStreaming === 'on' &&
1099
- (projection.markerCount > 0 || streamedMarkers > 0)) {
1100
- return this.prepareOutgoingText(withFileUnavailableNotice(projection.text));
1101
- }
1102
1105
  const notices = [];
1103
1106
  if (projection.invalidMarkers > 0) {
1104
1107
  notices.push('[File delivery failed: invalid marker]');
@@ -1359,20 +1362,31 @@ export class DingtalkChannel extends ChannelBase {
1359
1362
  return data.access_token;
1360
1363
  }
1361
1364
  sendCardInteractionFeedback(actorId, target) {
1365
+ const copy = this.locale === 'zh'
1366
+ ? {
1367
+ title: '卡片操作',
1368
+ group: '仅任务发起人可以操作这张卡片,本次操作未生效。',
1369
+ direct: '你无权操作这张卡片,仅任务发起人可以提交或停止。',
1370
+ }
1371
+ : {
1372
+ title: 'Card interaction',
1373
+ group: 'Only the task initiator can operate this card. This action had no effect.',
1374
+ direct: 'You cannot operate this card. Only the task initiator can submit or stop it.',
1375
+ };
1362
1376
  if (target?.isGroup) {
1363
1377
  return this.sendProactiveChunk({
1364
1378
  channelName: this.name,
1365
1379
  senderId: actorId,
1366
1380
  chatId: target.chatId,
1367
1381
  isGroup: true,
1368
- }, '卡片操作', '仅任务发起人可以操作这张卡片,本次操作未生效。', 'card interaction feedback');
1382
+ }, copy.title, copy.group, 'card interaction feedback');
1369
1383
  }
1370
1384
  return this.sendProactiveChunk({
1371
1385
  channelName: this.name,
1372
1386
  senderId: actorId,
1373
1387
  chatId: actorId,
1374
1388
  isGroup: false,
1375
- }, '卡片操作', '你无权操作这张卡片,仅任务发起人可以提交或停止。', 'card interaction feedback');
1389
+ }, copy.title, copy.direct, 'card interaction feedback');
1376
1390
  }
1377
1391
  async sendProactiveChunk(target, title, text, chunkLabel) {
1378
1392
  return this.sendProactivePayload(target, PROACTIVE_MSG_KEY, { title, text }, chunkLabel);
@@ -1808,8 +1822,6 @@ export class DingtalkChannel extends ChannelBase {
1808
1822
  }
1809
1823
  /** Recall reactions left behind when a session dies without terminal lifecycle events. */
1810
1824
  onSessionDied(sessionId) {
1811
- this.blockProjectionArmed.delete(sessionId);
1812
- this.blockFileProjectors.delete(sessionId);
1813
1825
  for (const [runId, state] of this.fileProjectors) {
1814
1826
  if (state.sessionId === sessionId)
1815
1827
  this.fileProjectors.delete(runId);
@@ -1943,7 +1955,6 @@ export class DingtalkChannel extends ChannelBase {
1943
1955
  }
1944
1956
  }
1945
1957
  onPromptStart(chatId, sessionId, messageId) {
1946
- this.blockProjectionArmed.add(sessionId);
1947
1958
  if (messageId) {
1948
1959
  this.bufferedMentionTargets.delete(messageId);
1949
1960
  this.untrackBufferedMentionTarget(sessionId, messageId);
@@ -2009,38 +2020,9 @@ export class DingtalkChannel extends ChannelBase {
2009
2020
  this.bufferedMentionTargetsBySession.delete(sessionId);
2010
2021
  }
2011
2022
  onPromptEnd(chatId, sessionId, messageId) {
2012
- this.settleBlockFileProjector(chatId, sessionId);
2013
2023
  this.sessionMentionTargets.delete(sessionId);
2014
2024
  this.stopReaction(chatId, messageId, sessionId);
2015
2025
  }
2016
- /**
2017
- * Turn end is the only point where the block projector's held state can be
2018
- * settled: ChannelBase drains the turn's queued block sends before calling
2019
- * onPromptEnd, so everything already appended belongs to this turn. Flush
2020
- * the held candidate bytes (a trailing `[FILE:` prefix the stream never
2021
- * completed) before deleting the entry — a later delete-without-settle
2022
- * would silently drop them from the delivered answer. Also disarm the
2023
- * session: /clear eviction and session death settle WITHOUT draining the
2024
- * turn's send chain, and any block that lands afterwards must be dropped
2025
- * rather than recreating projector state.
2026
- */
2027
- settleBlockFileProjector(chatId, sessionId) {
2028
- this.blockProjectionArmed.delete(sessionId);
2029
- const state = this.blockFileProjectors.get(sessionId);
2030
- if (!state)
2031
- return;
2032
- this.blockFileProjectors.delete(sessionId);
2033
- const tail = state.projector.complete();
2034
- if (!tail.trim())
2035
- return;
2036
- // Deliberately fire-and-forget: complete() can only return a strict
2037
- // prefix of '[FILE:' (at most 5 chars, no path bytes), so the worst case
2038
- // is a stray fragment landing out of order with the next turn — not a
2039
- // leak — and blocking settle on a delivery that may hang is worse.
2040
- void this.sendReply(chatId, tail, undefined, this.getResponseSourceLabel(sessionId)).catch((err) => {
2041
- process.stderr.write(`[DingTalk:${this.name}] projector tail delivery failed for session ${sessionId}: ${err instanceof Error ? err.message : String(err)}\n`);
2042
- });
2043
- }
2044
2026
  /** Deliver every Agent segment immediately unless aggregation is enabled. */
2045
2027
  async dispatchBackgroundResponse(sessionId, text, context) {
2046
2028
  const target = this.router.getTarget(sessionId);
@@ -2683,54 +2665,23 @@ export class DingtalkChannel extends ChannelBase {
2683
2665
  return escapeDingTalkMarkdown(normalized || '后台任务');
2684
2666
  }
2685
2667
  /**
2686
- * Out-of-turn one-shot sends (background responses) must not flow through
2687
- * the session's block-streaming projector: a second sender interleaving with
2688
- * mid-projection state can swallow the send or split a held marker.
2668
+ * Body-identical to the base implementation — this override exists only to
2669
+ * widen the signature, which the base seam does not declare. The background
2670
+ * aggregation flush passes `prepared` so the body it already projected isn't
2671
+ * projected a second time (that would re-upload its files), and
2672
+ * `failOnHttpError` so a failed send throws instead of being swallowed,
2673
+ * letting the flush capture the delivery plan for the next retry.
2689
2674
  */
2690
2675
  async deliverBackgroundReply(chatId, text, sessionId, sourceLabel, prepared = false, failOnHttpError = false) {
2691
- if (this.config.blockStreaming !== 'on') {
2692
- return this.sendResponseMessage(chatId, text, sessionId, sourceLabel, prepared, failOnHttpError);
2693
- }
2694
- await this.sendReply(chatId, text, undefined, sourceLabel, prepared, failOnHttpError);
2676
+ await this.sendResponseMessage(chatId, text, sessionId, sourceLabel, prepared, failOnHttpError);
2695
2677
  }
2696
2678
  async sendResponseMessage(chatId, text, sessionId, sourceLabel, prepared = false, failOnHttpError = false) {
2697
- let outgoingText = text;
2698
- let consumesMention = true;
2699
- if (this.config.blockStreaming === 'on') {
2700
- const projected = this.projectBlockStreamChunk(text, sessionId);
2701
- if (!projected.text.trim())
2702
- return;
2703
- outgoingText = projected.text;
2704
- // A notice-only block must not consume the prompt's mention target:
2705
- // the @mention belongs to the block carrying the actual answer.
2706
- consumesMention = projected.hasContent;
2707
- }
2708
- const atUserId = consumesMention && this.atSender
2679
+ const atUserId = this.atSender
2709
2680
  ? this.sessionMentionTargets.get(sessionId)
2710
2681
  : undefined;
2711
2682
  if (atUserId)
2712
2683
  this.sessionMentionTargets.delete(sessionId);
2713
- await this.sendReply(chatId, outgoingText, atUserId, sourceLabel ?? this.getResponseSourceLabel(sessionId), prepared, failOnHttpError);
2714
- }
2715
- projectBlockStreamChunk(text, sessionId) {
2716
- let state = this.blockFileProjectors.get(sessionId);
2717
- if (!state) {
2718
- if (!this.blockProjectionArmed.has(sessionId)) {
2719
- return { text: '', hasContent: false };
2720
- }
2721
- state = { projector: new OutboundFileProjector(), reportedMarkers: 0 };
2722
- this.blockFileProjectors.set(sessionId, state);
2723
- }
2724
- const safe = state.projector.append(text);
2725
- const hasContent = safe.trim().length > 0;
2726
- const result = state.projector.result(safe);
2727
- let outgoingText = safe;
2728
- if (result.markerCount > state.reportedMarkers) {
2729
- outgoingText = withFileUnavailableNotice(safe);
2730
- state.reportedMarkers = result.markerCount;
2731
- process.stderr.write(`[DingTalk:${this.name}] file markers redacted in block stream (session ${sessionId}, markers=${result.markerCount})\n`);
2732
- }
2733
- return { text: outgoingText, hasContent };
2684
+ await this.sendReply(chatId, text, atUserId, sourceLabel ?? this.getResponseSourceLabel(sessionId), prepared, failOnHttpError);
2734
2685
  }
2735
2686
  async sendFallbackReply(chatId, text, sessionId, sourceLabel) {
2736
2687
  // Mid-run fallbacks must not consume the prompt's mention target: the
@@ -2794,6 +2745,16 @@ export class DingtalkChannel extends ChannelBase {
2794
2745
  }
2795
2746
  return this.interactionPresenter.presentInput(context);
2796
2747
  }
2748
+ async presentPermissionRequest(context) {
2749
+ const run = this.cardRuns.get(context.runId);
2750
+ if (!run || run.ownerId !== context.owner.id) {
2751
+ return { kind: 'unsupported' };
2752
+ }
2753
+ if (!this.permissionCardController || !this.interactionPresenter) {
2754
+ return { kind: 'unsupported' };
2755
+ }
2756
+ return this.interactionPresenter.presentPermission(context);
2757
+ }
2797
2758
  /**
2798
2759
  * Extract quoted/referenced message context from a reply.
2799
2760
  * DingTalk provides this via text.repliedMsg (newer) or quoteMessage (legacy).