openclaw-openagent 1.0.6 → 1.0.8

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.
@@ -111,10 +111,10 @@ function injectStyles() {
111
111
  .openagent-agent-panel {
112
112
  position: fixed;
113
113
  max-height: none;
114
- background: #fff;
115
- border: 1.2px solid rgba(47, 47, 51, 0.12);
114
+ background: var(--oa-panel-bg, #fff);
115
+ border: 1.2px solid var(--oa-border, rgba(47, 47, 51, 0.12));
116
116
  border-radius: 18px;
117
- box-shadow: 0 -4px 24px rgba(0,0,0,0.08), 0 2px 8px rgba(0,0,0,0.04);
117
+ box-shadow: 0 -4px 24px var(--oa-shadow-color, rgba(0,0,0,0.08)), 0 2px 8px var(--oa-shadow-color, rgba(0,0,0,0.04));
118
118
  z-index: 99999;
119
119
  overflow: hidden;
120
120
  display: none;
@@ -158,7 +158,7 @@ function injectStyles() {
158
158
  flex-shrink: 0;
159
159
  }
160
160
  .openagent-panel-close {
161
- background: rgba(249, 249, 249, 1);
161
+ background: var(--oa-card-bg, rgba(249, 249, 249, 1));
162
162
  border: none;
163
163
  color: rgba(47, 47, 51, 0.6);
164
164
  cursor: pointer;
@@ -618,41 +618,61 @@ function injectStyles() {
618
618
  padding: 32px; text-align: center; color: #aaa; font-size: 13px;
619
619
  }
620
620
 
621
- /* ── 深色主题(统一) ── */
622
- [data-theme-mode="dark"] .openagent-agent-panel {
621
+ /* ── 深色主题(v4: data-theme-mode / v5: data-theme) ── */
622
+ [data-theme-mode="dark"] .openagent-agent-panel,
623
+ [data-theme="dark"] .openagent-agent-panel {
623
624
  background: #1c1c1c; border-color: rgba(255,255,255,0.12);
624
625
  box-shadow: 0 -4px 24px rgba(0,0,0,0.3);
625
626
  }
626
- [data-theme-mode="dark"] .openagent-panel-head { border-color: #2a2a2a; }
627
- [data-theme-mode="dark"] .openagent-panel-title { color: rgba(224, 224, 224, 0.8); }
628
- [data-theme-mode="dark"] .openagent-panel-close { background: #2a2a2a; color: #888; }
629
- [data-theme-mode="dark"] .openagent-panel-close:hover { background: #333; color: #ccc; }
630
- [data-theme-mode="dark"] .openagent-section-heading { color: rgba(224, 224, 224, 0.45); }
631
- [data-theme-mode="dark"] .openagent-parent-heading { color: rgba(224, 224, 224, 0.3); }
632
- [data-theme-mode="dark"] .openagent-panel-divider { border-color: rgba(255,255,255,0.1); }
633
-
634
- [data-theme-mode="dark"] .agent-card__name { color: #e0e0e0; }
635
- [data-theme-mode="dark"] .agent-card__bio { color: rgba(224, 224, 224, 0.7); }
636
- [data-theme-mode="dark"] .agent-card__stat { color: #64748b; }
637
-
638
- [data-theme-mode="dark"] .agent-card--mention { background: transparent; }
639
- [data-theme-mode="dark"] .agent-card--mention:hover { background: #252525; }
640
- [data-theme-mode="dark"] .agent-card--mention .agent-card__action {
627
+ [data-theme-mode="dark"] .openagent-panel-head,
628
+ [data-theme="dark"] .openagent-panel-head { border-color: #2a2a2a; }
629
+ [data-theme-mode="dark"] .openagent-panel-title,
630
+ [data-theme="dark"] .openagent-panel-title { color: rgba(224, 224, 224, 0.8); }
631
+ [data-theme-mode="dark"] .openagent-panel-close,
632
+ [data-theme="dark"] .openagent-panel-close { background: #2a2a2a; color: #888; }
633
+ [data-theme-mode="dark"] .openagent-panel-close:hover,
634
+ [data-theme="dark"] .openagent-panel-close:hover { background: #333; color: #ccc; }
635
+ [data-theme-mode="dark"] .openagent-section-heading,
636
+ [data-theme="dark"] .openagent-section-heading { color: rgba(224, 224, 224, 0.45); }
637
+ [data-theme-mode="dark"] .openagent-parent-heading,
638
+ [data-theme="dark"] .openagent-parent-heading { color: rgba(224, 224, 224, 0.3); }
639
+ [data-theme-mode="dark"] .openagent-panel-divider,
640
+ [data-theme="dark"] .openagent-panel-divider { border-color: rgba(255,255,255,0.1); }
641
+
642
+ [data-theme-mode="dark"] .agent-card__name,
643
+ [data-theme="dark"] .agent-card__name { color: #e0e0e0; }
644
+ [data-theme-mode="dark"] .agent-card__bio,
645
+ [data-theme="dark"] .agent-card__bio { color: rgba(224, 224, 224, 0.7); }
646
+ [data-theme-mode="dark"] .agent-card__stat,
647
+ [data-theme="dark"] .agent-card__stat { color: #64748b; }
648
+
649
+ [data-theme-mode="dark"] .agent-card--mention,
650
+ [data-theme="dark"] .agent-card--mention { background: transparent; }
651
+ [data-theme-mode="dark"] .agent-card--mention:hover,
652
+ [data-theme="dark"] .agent-card--mention:hover { background: #252525; }
653
+ [data-theme-mode="dark"] .agent-card--mention .agent-card__action,
654
+ [data-theme="dark"] .agent-card--mention .agent-card__action {
641
655
  background: #333; border-color: #555; color: #ddd;
642
656
  }
643
- [data-theme-mode="dark"] .agent-card--mention .agent-card__action:hover { background: #444; }
644
- [data-theme-mode="dark"] .agent-card--search:hover { background: #252525; }
645
- [data-theme-mode="dark"] .agent-card--search.openagent-card-selected {
657
+ [data-theme-mode="dark"] .agent-card--mention .agent-card__action:hover,
658
+ [data-theme="dark"] .agent-card--mention .agent-card__action:hover { background: #444; }
659
+ [data-theme-mode="dark"] .agent-card--search:hover,
660
+ [data-theme="dark"] .agent-card--search:hover { background: #252525; }
661
+ [data-theme-mode="dark"] .agent-card--search.openagent-card-selected,
662
+ [data-theme="dark"] .agent-card--search.openagent-card-selected {
646
663
  background: rgba(99, 102, 241, 0.12);
647
664
  outline-color: rgba(99, 102, 241, 0.4);
648
665
  }
649
- [data-theme-mode="dark"] .agent-card--search .agent-card__action {
666
+ [data-theme-mode="dark"] .agent-card--search .agent-card__action,
667
+ [data-theme="dark"] .agent-card--search .agent-card__action {
650
668
  background: #333; border-color: #555; color: #ddd;
651
669
  }
652
- [data-theme-mode="dark"] .agent-card--search .agent-card__action:hover { background: #444; }
670
+ [data-theme-mode="dark"] .agent-card--search .agent-card__action:hover,
671
+ [data-theme="dark"] .agent-card--search .agent-card__action:hover { background: #444; }
653
672
 
654
673
 
655
- [data-theme-mode="dark"] .agent-card--inline {
674
+ [data-theme-mode="dark"] .agent-card--inline,
675
+ [data-theme="dark"] .agent-card--inline {
656
676
  background: linear-gradient(135deg, #1e1b3a 0%, #1a1a2e 100%);
657
677
  border-color: #2d2b55;
658
678
  }
@@ -662,7 +682,8 @@ function injectStyles() {
662
682
  }
663
683
  [data-theme-mode="dark"] .agent-card--inline .agent-card__name { color: #e2e8f0; }
664
684
  [data-theme-mode="dark"] .agent-card--inline .agent-card__avatar { border-color: #2d2b55; }
665
- [data-theme-mode="dark"] .agent-card__loading {
685
+ [data-theme-mode="dark"] .agent-card__loading,
686
+ [data-theme="dark"] .agent-card__loading {
666
687
  background: #1e1b3a; border-color: #2d2b55; color: #64748b;
667
688
  }
668
689
  `;
@@ -12,14 +12,20 @@
12
12
  var SHELL_SELECTORS = [
13
13
  '.chat-tool-card:not(:has(.cl-remote-agent-card))',
14
14
  '.chat-tool-msg-collapse:not(:has(.cl-remote-agent-card))',
15
- '.chat-tools-collapse:not(:has(.cl-remote-agent-card))'
15
+ '.chat-tools-collapse:not(:has(.cl-remote-agent-card))',
16
+ // v5.x+ variants
17
+ '.tool-card:not(:has(.cl-remote-agent-card))',
18
+ '.chat-tool-card__wrapper:not(:has(.cl-remote-agent-card))'
16
19
  ];
17
20
 
18
21
  var SUMMARY_SELECTORS = [
19
22
  '.chat-tool-card__header',
20
23
  '.chat-tool-card__title',
21
24
  '.chat-tool-msg-summary:not(.cl-remote-agent-host-shell *)',
22
- '.chat-tools-summary:not(.cl-remote-agent-host-shell *)'
25
+ '.chat-tools-summary:not(.cl-remote-agent-host-shell *)',
26
+ // v5.x+ variants
27
+ '.tool-card__header',
28
+ '.tool-card__title'
23
29
  ];
24
30
 
25
31
  var INDENT_SELECTORS = [
@@ -258,27 +258,25 @@ const toolcardRender: UIExtension = {
258
258
  if (hasManualCollapseWrapper) {
259
259
  const wrapperFn = findFunctionByAnchor(content, wrapperAnchor);
260
260
  if (!wrapperFn) {
261
- logger.warn('[ui-ext:regex] toolcard-render: manual collapse wrapper function not found');
262
- return false;
263
- }
264
- if (!wrapperFn.body.includes('chat-tool-msg-body') || !wrapperFn.body.includes('onToggleExpanded')) {
265
- logger.warn('[ui-ext:regex] toolcard-render: manual collapse wrapper shape mismatch');
266
- return false;
267
- }
268
- const wrapperReturnTagMatch = wrapperFn.body.match(/return\s+([a-zA-Z_$]\w*)\s*`/);
269
- if (!wrapperReturnTagMatch) {
270
- logger.warn('[ui-ext:regex] toolcard-render: manual collapse Lit html tag not found');
271
- return false;
261
+ logger.warn('[ui-ext:regex] toolcard-render: manual collapse wrapper function not found, skipping wrapper patch');
262
+ } else if (!wrapperFn.body.includes('chat-tool-msg-body') || !wrapperFn.body.includes('onToggleExpanded')) {
263
+ logger.warn('[ui-ext:regex] toolcard-render: manual collapse wrapper shape mismatch, skipping wrapper patch');
264
+ } else {
265
+ const wrapperReturnTagMatch = wrapperFn.body.match(/return\s+([a-zA-Z_$]\w*)\s*`/);
266
+ if (!wrapperReturnTagMatch) {
267
+ logger.warn('[ui-ext:regex] toolcard-render: manual collapse Lit html tag not found, skipping wrapper patch');
268
+ } else {
269
+ const wrapperHtmlVar = wrapperReturnTagMatch[1];
270
+ const wrapperParams = wrapperFn.params.split(',').map((s: string) => s.trim());
271
+ const wrapperCardParam = wrapperParams[0] || 'e';
272
+ const wrapperOptionsParam = wrapperParams[1] || '';
273
+ const wrapperSidebarArg = wrapperOptionsParam ? `${wrapperOptionsParam}&&${wrapperOptionsParam}.onOpenSidebar` : 'undefined';
274
+ const wrapperHook = `${marker}if(window.__openagentRenderToolCard){let __cl=window.__openagentRenderToolCard(${wrapperCardParam},${wrapperSidebarArg});if(__cl)return ${wrapperHtmlVar}\`\${__cl}\`}`;
275
+ content = content.substring(0, wrapperFn.bodyStart) + wrapperHook + content.substring(wrapperFn.bodyStart);
276
+ patchedWrapper = true;
277
+ logger.info(`[ui-ext:regex] toolcard-render: patched manual wrapper ${wrapperFn.funcName}(${wrapperFn.params.substring(0, 20)}) html=${wrapperHtmlVar}`);
278
+ }
272
279
  }
273
- const wrapperHtmlVar = wrapperReturnTagMatch[1];
274
- const wrapperParams = wrapperFn.params.split(',').map((s: string) => s.trim());
275
- const wrapperCardParam = wrapperParams[0] || 'e';
276
- const wrapperOptionsParam = wrapperParams[1] || '';
277
- const wrapperSidebarArg = wrapperOptionsParam ? `${wrapperOptionsParam}&&${wrapperOptionsParam}.onOpenSidebar` : 'undefined';
278
- const wrapperHook = `${marker}if(window.__openagentRenderToolCard){let __cl=window.__openagentRenderToolCard(${wrapperCardParam},${wrapperSidebarArg});if(__cl)return ${wrapperHtmlVar}\`\${__cl}\`}`;
279
- content = content.substring(0, wrapperFn.bodyStart) + wrapperHook + content.substring(wrapperFn.bodyStart);
280
- patchedWrapper = true;
281
- logger.info(`[ui-ext:regex] toolcard-render: patched manual wrapper ${wrapperFn.funcName}(${wrapperFn.params.substring(0, 20)}) html=${wrapperHtmlVar}`);
282
280
  }
283
281
 
284
282
  // 4.11+ also renders tool-result messages through the grouped message
@@ -291,13 +289,13 @@ const toolcardRender: UIExtension = {
291
289
  let toolMessageOk = true;
292
290
  const toolMessageFn = findFunctionByAnchor(content, toolMessageAnchor, 8000);
293
291
  if (!toolMessageFn) {
294
- logger.warn('[ui-ext:regex] toolcard-render: tool message wrapper function not found');
292
+ logger.warn('[ui-ext:regex] toolcard-render: tool message wrapper function not found, skipping');
295
293
  toolMessageOk = false;
296
294
  }
297
295
 
298
296
  if (toolMessageOk && (!toolMessageFn!.body.includes('chat-tool-msg-collapse--manual')
299
297
  || !toolMessageFn!.body.includes('chat-tool-msg-body'))) {
300
- logger.warn('[ui-ext:regex] toolcard-render: tool message wrapper shape mismatch');
298
+ logger.warn('[ui-ext:regex] toolcard-render: tool message wrapper shape mismatch, skipping');
301
299
  toolMessageOk = false;
302
300
  }
303
301
 
@@ -313,14 +311,33 @@ const toolcardRender: UIExtension = {
313
311
  }
314
312
 
315
313
  if (toolMessageOk) {
314
+ // 双模式匹配单卡 guard:
315
+ // Shape A (2026.4.x): ,Z=X.length===1?X[0]:null,Y=Z&&!A&&!B?
316
+ // Shape B (2026.5.x): ,N=u.length===1?u[0]:null,...ae=P?`Tool error`:ne&&!C&&!h?ne:...
316
317
  const singleCardMatch = toolMessageFn!.body.match(/(?:let\s+|,)([a-zA-Z_$]\w*)=([a-zA-Z_$]\w*)\.length===1\?\2\[0\]:null,\w+=\1&&!([a-zA-Z_$]\w*)&&!([a-zA-Z_$]\w*)\?/);
317
- if (!singleCardMatch) {
318
- logger.warn('[ui-ext:regex] toolcard-render: tool message single-card guard not found');
319
- toolMessageOk = false;
318
+ let cardsVar = '';
319
+ let mediaVar = '';
320
+ if (singleCardMatch) {
321
+ // Shape A: old bundle
322
+ cardsVar = singleCardMatch[2];
323
+ mediaVar = singleCardMatch[4];
324
+ } else {
325
+ // Shape B: 2026.5.x bundle
326
+ const shapeB = toolMessageFn!.body.match(/,(?:let\s+)?([a-zA-Z_$]\w*)=([a-zA-Z_$]\w*)\.length===1\?\2\[0\]:null/);
327
+ if (!shapeB) {
328
+ logger.warn('[ui-ext:regex] toolcard-render: tool message single-card guard not found');
329
+ toolMessageOk = false;
330
+ } else {
331
+ cardsVar = shapeB[2];
332
+ // 从 ae=P?`Tool error`:ne&&!C&&!h 中提取排除变量 C, h
333
+ const aeMatch = toolMessageFn!.body.match(/,\w+=\w+\?`Tool error`:(\w+)&&!(\w+)&&!(\w+)\?/);
334
+ if (aeMatch) {
335
+ mediaVar = `(${aeMatch[2]}||${aeMatch[3]})`;
336
+ } else {
337
+ mediaVar = 'false';
338
+ }
339
+ }
320
340
  }
321
- if (toolMessageOk) {
322
- const cardsVar = singleCardMatch![2];
323
- const mediaVar = singleCardMatch![4];
324
341
  const chatBubbleClassMatch = toolMessageFn!.body.match(/[,;]([a-zA-Z_$]\w*)=\[`chat-bubble`,/);
325
342
  if (!chatBubbleClassMatch) {
326
343
  logger.warn('[ui-ext:regex] toolcard-render: tool message chat-bubble class var not found');
@@ -337,7 +354,7 @@ const toolcardRender: UIExtension = {
337
354
  }
338
355
  if (toolMessageOk) {
339
356
  const toolMessageHook =
340
- `${marker}let __openagentToolMsgCard=null;if(!${mediaVar}&&window.__openagentRenderToolCard){for(let __openagentI=${cardsVar}.length-1;__openagentI>=0&&!__openagentToolMsgCard;__openagentI--){let __openagentCandidate=${cardsVar}[__openagentI];if(__openagentCandidate&&(__openagentCandidate.outputText||__openagentCandidate.text))__openagentToolMsgCard=window.__openagentRenderToolCard(__openagentCandidate,${toolMessageSidebarArg})}for(let __openagentI=0;__openagentI<${cardsVar}.length&&!__openagentToolMsgCard;__openagentI++){__openagentToolMsgCard=window.__openagentRenderToolCard(${cardsVar}[__openagentI],${toolMessageSidebarArg})}}if(__openagentToolMsgCard)return ${toolMessageHtmlVar}\`<div class="\${${chatBubbleClassVar}}">\${__openagentToolMsgCard}</div>\`;`;
357
+ `${marker}let __openagentToolMsgCard=null;console.log('[openagent] toolMsg hook: cards='+${cardsVar}.length+' mediaVar='+${mediaVar});if(!${mediaVar}&&window.__openagentRenderToolCard){for(let __openagentI=${cardsVar}.length-1;__openagentI>=0&&!__openagentToolMsgCard;__openagentI--){let __openagentCandidate=${cardsVar}[__openagentI];if(__openagentCandidate&&(__openagentCandidate.outputText||__openagentCandidate.text))__openagentToolMsgCard=window.__openagentRenderToolCard(__openagentCandidate,${toolMessageSidebarArg})}for(let __openagentI=0;__openagentI<${cardsVar}.length&&!__openagentToolMsgCard;__openagentI++){__openagentToolMsgCard=window.__openagentRenderToolCard(${cardsVar}[__openagentI],${toolMessageSidebarArg})}}if(__openagentToolMsgCard)return ${toolMessageHtmlVar}\`<div class="\${${chatBubbleClassVar}}">\${__openagentToolMsgCard}</div>\`;`;
341
358
  const toolMessageInsertAt = toolMessageFn!.bodyStart + toolMessageReturnIdx;
342
359
  content = content.substring(0, toolMessageInsertAt) + toolMessageHook + content.substring(toolMessageInsertAt);
343
360
  patchedToolMessageWrapper = true;
@@ -346,9 +363,8 @@ const toolcardRender: UIExtension = {
346
363
  }
347
364
  }
348
365
  }
349
- }
350
366
 
351
- writeFileSync(bundlePath, content, 'utf-8');
367
+ writeFileSync(bundlePath, content, 'utf-8');
352
368
  logger.info(`[ui-ext:regex] toolcard-render: patched ${funcName}(${params.substring(0, 20)}) html=${litHtmlVar}${patchedWrapper ? ' + wrapper' : ''}${patchedToolMessageWrapper ? ' + tool-message' : ''}`);
353
369
  return true;
354
370
  },
@@ -650,11 +666,41 @@ const remoteAgentMessageShortCircuit: UIExtension = {
650
666
  return true;
651
667
  }
652
668
 
669
+ // Strategy D (v5.17+ fallback): search for any if-condition referencing
670
+ // hasToolCards alongside a role-like variable — v5.17+ may replace the
671
+ // ternary/compound patterns of A/B/C with a different expression shape.
672
+ const strategyDRe = new RegExp(
673
+ `if\\(!` + `([a-zA-Z_$][a-zA-Z0-9_$]*)\\s*&&\\s*` +
674
+ hasToolCardsVar + `\\s*&&\\s*([a-zA-Z_$][a-zA-Z0-9_$]*)\\s*\\)`
675
+ );
676
+ const strategyDMatch = content.match(strategyDRe);
677
+ if (strategyDMatch) {
678
+ const markdownVarD = strategyDMatch[1]!;
679
+ const isToolResultVarD = strategyDMatch[2]!;
680
+ const dMatchFull = strategyDMatch[0];
681
+ const dIdx = strategyDMatch.index!;
682
+ const patched = `if(` + marker +
683
+ `(!` + markdownVarD + `||window.__openagentOwnsToolCards&&window.__openagentOwnsToolCards(` +
684
+ toolCardsVar + `))&&` + hasToolCardsVar + `&&` + isToolResultVarD + `)`;
685
+ content = content.substring(0, dIdx) + patched + content.substring(dIdx + dMatchFull.length);
686
+ writeFileSync(bundlePath, content, 'utf-8');
687
+ logger.info(`[ui-ext:regex] remote-agent-msg: patched (strategy D, md=${markdownVarD})`);
688
+ return true;
689
+ }
690
+
653
691
  // 策略B (v4.11~v4.1x fallback): compound = hasToolCards && (showToolCalls ?? !0)
654
692
  const compoundRe = new RegExp(`(\\w+)=${hasToolCardsVar}&&\\(\\w+\\.showToolCalls\\?\\?!0\\)`);
655
693
  const compoundMatch = content.match(compoundRe);
656
694
  if (!compoundMatch) {
657
- logger.warn('[ui-ext:regex] remote-agent-msg: all strategies (A/C/B) failed');
695
+ // All strategies (A/C/D/B) failed — dump context for diagnostics
696
+ const anchorHintIdx = content.indexOf(hasToolCardsVar);
697
+ const ctxStart = anchorHintIdx > 200 ? anchorHintIdx - 200 : 0;
698
+ const ctxEnd = Math.min(anchorHintIdx + 200, content.length);
699
+ logger.warn(
700
+ `[ui-ext:regex] remote-agent-msg: all strategies (A/C/D/B) failed for toolCards=${toolCardsVar}. ` +
701
+ `Context around hasToolCardsVar (at ${anchorHintIdx}): ` +
702
+ content.substring(ctxStart, ctxEnd)
703
+ );
658
704
  return false;
659
705
  }
660
706
 
@@ -709,17 +755,18 @@ const mentionHooks: UIExtension = {
709
755
  const hookA = `${markerA}if(window.__clMention?.onKeyDown(${eventParam}))return;`;
710
756
  content = content.substring(0, keydownInsertIdx) + hookA + content.substring(keydownInsertIdx);
711
757
 
712
- // 注入点 B: handleInput — 支持三种形态
758
+ // 注入点 B: handleInput — 四种形态兼容
713
759
  // Shape A (old): .onDraftChange(target.value)}}
714
760
  // Shape B (4.11~4.x): .onDraftChange(target.value) (arrow function 内)
715
- // Shape C (2026.6.x): be=t=>{let n=t.target;pL(e,n.value)} — 引入 pL helper
716
- // arrow body 末尾不再出现 onDraftChange 字面
761
+ // Shape C (2026.6.x): be=t=>{let n=t.target;pL(e,n.value)} — 引入 pL helper
762
+ // Shape D (generic): .onDraftChange(任意.target.value) 不加结尾限制
717
763
  const inputReA = /\.onDraftChange\(([a-zA-Z_$][a-zA-Z0-9_$]*)\.value\)\}\}/;
718
764
  const inputReB = /\.onDraftChange\(([a-zA-Z_$][a-zA-Z0-9_$]*)\.value\)/;
719
765
  const inputReC = /let ([a-zA-Z_$][a-zA-Z0-9_$]*)=[a-zA-Z_$][a-zA-Z0-9_$]*\.target;[a-zA-Z_$][a-zA-Z0-9_$]*\([a-zA-Z_$][a-zA-Z0-9_$]*,\1\.value\)\}/;
766
+ const inputReD = /\.onDraftChange\(([a-zA-Z_$][a-zA-Z0-9_$]*)\.target\.value\)/;
720
767
 
721
768
  let inputMatch = content.match(inputReA);
722
- let inputShape: 'A' | 'B' | 'C' = 'A';
769
+ let inputShape: 'A' | 'B' | 'C' | 'D' = 'A';
723
770
 
724
771
  if (!inputMatch) {
725
772
  inputMatch = content.match(inputReC);
@@ -729,14 +776,26 @@ const mentionHooks: UIExtension = {
729
776
  inputMatch = content.match(inputReB);
730
777
  inputShape = 'B';
731
778
  }
779
+ if (!inputMatch) {
780
+ inputMatch = content.match(inputReD);
781
+ inputShape = 'D';
782
+ }
732
783
 
733
784
  if (!inputMatch) {
734
- logger.warn('[ui-ext:regex] mention-hooks: input anchor not found (Shape A/B/C all missed)');
785
+ // All shapes missed dump context for diagnostics
786
+ const draftHintIdx = content.indexOf('.onDraftChange');
787
+ const ctxStart = draftHintIdx > 200 ? draftHintIdx - 200 : 0;
788
+ const ctxEnd = Math.min(draftHintIdx + 200, content.length);
789
+ logger.warn(
790
+ `[ui-ext:regex] mention-hooks: input anchor not found (Shape A/B/C/D all missed). ` +
791
+ `Context around onDraftChange (at ${draftHintIdx}): ` +
792
+ content.substring(ctxStart, ctxEnd)
793
+ );
735
794
  return false;
736
795
  }
737
796
  const targetVar = inputMatch[1];
738
797
  const inputAnchorIdx = content.indexOf(inputMatch[0]);
739
- // Shape A: }} 之前;Shape C: 末尾 } 之前;Shape B: ) 之后
798
+ // Shape A: }} 之前;Shape C: 末尾 } 之前;Shape B/D: ) 之后
740
799
  const draftCallEnd = inputShape === 'A'
741
800
  ? inputAnchorIdx + inputMatch[0].length - 2
742
801
  : inputShape === 'C'
@@ -15,3 +15,47 @@ export function resolveOasnAccessUrl(accessApiBase: string | undefined, url: str
15
15
  return url;
16
16
  }
17
17
  }
18
+
19
+ /**
20
+ * Resolve a WebUI continuation URL for user-facing browser clicks.
21
+ *
22
+ * WebUI auth is Owner-cookie based, so the clickable URL must use the same
23
+ * public origin as the claim/login flow. API-key calls can use an internal
24
+ * Access base, but browser links cannot reuse cookies across IP/domain origins.
25
+ */
26
+ export function resolveOasnWebuiUrl(
27
+ accessApiBase: string | undefined,
28
+ claimUrl: string | undefined,
29
+ url: string,
30
+ ): string {
31
+ if (!url) return url;
32
+
33
+ const claimOrigin = originOf(claimUrl);
34
+ if (!claimOrigin || !isWebuiUrl(url, accessApiBase || claimOrigin)) {
35
+ return resolveOasnAccessUrl(accessApiBase, url);
36
+ }
37
+
38
+ try {
39
+ const absolute = new URL(url, accessApiBase || claimOrigin);
40
+ return new URL(`${absolute.pathname}${absolute.search}${absolute.hash}`, claimOrigin).toString();
41
+ } catch {
42
+ return resolveOasnAccessUrl(accessApiBase, url);
43
+ }
44
+ }
45
+
46
+ function originOf(value: string | undefined): string | undefined {
47
+ if (!value) return undefined;
48
+ try {
49
+ return new URL(value).origin;
50
+ } catch {
51
+ return undefined;
52
+ }
53
+ }
54
+
55
+ function isWebuiUrl(url: string, base: string): boolean {
56
+ try {
57
+ return new URL(url, base).pathname.startsWith('/webui/');
58
+ } catch {
59
+ return false;
60
+ }
61
+ }