openclaw-openagent 1.0.1 → 1.0.2
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.
- package/dist/index.d.ts +25 -0
- package/dist/index.js +105 -0
- package/dist/src/app/channel-tools.d.ts +28 -0
- package/dist/src/app/channel-tools.js +251 -0
- package/dist/src/app/discovery-tools.d.ts +35 -0
- package/dist/src/app/discovery-tools.js +243 -0
- package/dist/src/app/download-file-tool.d.ts +10 -0
- package/dist/src/app/download-file-tool.js +104 -0
- package/dist/src/app/hooks.d.ts +14 -0
- package/dist/src/app/hooks.js +118 -0
- package/dist/src/app/index.d.ts +13 -0
- package/dist/src/app/index.js +44 -0
- package/dist/src/app/messaging-tools.d.ts +11 -0
- package/dist/src/app/messaging-tools.js +79 -0
- package/dist/src/app/ops-tools.d.ts +21 -0
- package/dist/src/app/ops-tools.js +158 -0
- package/dist/src/app/remote-agent-tool.d.ts +27 -0
- package/dist/src/app/remote-agent-tool.js +461 -0
- package/dist/src/app/types.d.ts +61 -0
- package/dist/src/app/types.js +11 -0
- package/dist/src/app/upload-file-tool.d.ts +16 -0
- package/dist/src/app/upload-file-tool.js +353 -0
- package/dist/src/app/verbose-preflight.d.ts +2 -0
- package/dist/src/app/verbose-preflight.js +145 -0
- package/dist/src/auth/config.d.ts +79 -0
- package/dist/src/auth/config.js +133 -0
- package/dist/src/auth/credential-manager.d.ts +65 -0
- package/dist/src/auth/credential-manager.js +122 -0
- package/dist/src/auth/index.d.ts +6 -0
- package/dist/src/auth/index.js +6 -0
- package/dist/src/auth/verify.d.ts +42 -0
- package/dist/src/auth/verify.js +60 -0
- package/dist/src/channel.d.ts +269 -0
- package/dist/src/channel.js +488 -0
- package/dist/src/compat.d.ts +37 -0
- package/dist/src/compat.js +70 -0
- package/dist/src/config/config-schema.d.ts +56 -0
- package/dist/src/config/config-schema.js +34 -0
- package/dist/src/messaging/aggregator.d.ts +25 -0
- package/dist/src/messaging/aggregator.js +90 -0
- package/dist/src/messaging/collector.d.ts +27 -0
- package/dist/src/messaging/collector.js +76 -0
- package/dist/src/messaging/executor.d.ts +14 -0
- package/dist/src/messaging/executor.js +59 -0
- package/dist/src/messaging/inbound.d.ts +97 -0
- package/dist/src/messaging/inbound.js +63 -0
- package/dist/src/messaging/index.d.ts +10 -0
- package/dist/src/messaging/index.js +9 -0
- package/dist/src/messaging/mention-protocol.d.ts +42 -0
- package/dist/src/messaging/mention-protocol.js +74 -0
- package/dist/src/messaging/process-c2c-request.d.ts +55 -0
- package/dist/src/messaging/process-c2c-request.js +445 -0
- package/dist/src/messaging/process-message.d.ts +62 -0
- package/dist/src/messaging/process-message.js +282 -0
- package/dist/src/messaging/scheduler.d.ts +17 -0
- package/dist/src/messaging/scheduler.js +47 -0
- package/dist/src/messaging/types.d.ts +34 -0
- package/dist/src/messaging/types.js +4 -0
- package/dist/src/plugin-ui/assets/openagent-override.js +9267 -0
- package/dist/src/plugin-ui/index.d.ts +13 -0
- package/dist/src/plugin-ui/index.js +16 -0
- package/dist/src/plugin-ui/ui-extension-loader/backup.d.ts +23 -0
- package/dist/src/plugin-ui/ui-extension-loader/backup.js +82 -0
- package/dist/src/plugin-ui/ui-extension-loader/index.d.ts +28 -0
- package/dist/src/plugin-ui/ui-extension-loader/index.js +240 -0
- package/dist/src/plugin-ui/ui-extension-loader/locator.d.ts +35 -0
- package/dist/src/plugin-ui/ui-extension-loader/locator.js +129 -0
- package/dist/src/plugin-ui/ui-extension-loader/manifest.d.ts +26 -0
- package/dist/src/plugin-ui/ui-extension-loader/manifest.js +45 -0
- package/dist/src/plugin-ui/ui-extension-loader/registry-regex.d.ts +9 -0
- package/dist/src/plugin-ui/ui-extension-loader/registry-regex.js +701 -0
- package/dist/src/plugin-ui/ui-extension-loader/removed-extensions.d.ts +8 -0
- package/dist/src/plugin-ui/ui-extension-loader/removed-extensions.js +58 -0
- package/dist/src/plugin-ui/ui-extension-loader/types.d.ts +55 -0
- package/dist/src/plugin-ui/ui-extension-loader/types.js +23 -0
- package/dist/src/proxy/auth-proxy.d.ts +20 -0
- package/dist/src/proxy/auth-proxy.js +337 -0
- package/dist/src/runtime/account.d.ts +156 -0
- package/dist/src/runtime/account.js +491 -0
- package/dist/src/runtime/index.d.ts +5 -0
- package/dist/src/runtime/index.js +6 -0
- package/dist/src/runtime/plugin-runtime.d.ts +40 -0
- package/dist/src/runtime/plugin-runtime.js +72 -0
- package/dist/src/runtime/registry.d.ts +41 -0
- package/dist/src/runtime/registry.js +60 -0
- package/dist/src/sdk/CLASS_MAP.md +143 -0
- package/dist/src/sdk/index.d.ts +126 -0
- package/dist/src/sdk/index.js +23990 -0
- package/dist/src/sdk/modules/cloud-search-module.js +1117 -0
- package/dist/src/sdk/modules/follow-module.js +1069 -0
- package/dist/src/sdk/modules/group-module.js +7397 -0
- package/dist/src/sdk/modules/relationship-module.js +2269 -0
- package/dist/src/sdk/modules/signaling-module.js +1468 -0
- package/dist/src/sdk/modules/tim-upload-plugin.js +730 -0
- package/dist/src/sdk/node-env/http-request.js +90 -0
- package/dist/src/sdk/node-env/index.js +57 -0
- package/dist/src/sdk/node-env/storage.js +114 -0
- package/dist/src/sdk/package.json +10 -0
- package/dist/src/sdk/sdk/CLASS_MAP.md +143 -0
- package/dist/src/sdk/sdk/index.d.ts +126 -0
- package/dist/src/sdk/sdk/index.js +23990 -0
- package/dist/src/sdk/sdk/modules/cloud-search-module.js +1117 -0
- package/dist/src/sdk/sdk/modules/follow-module.js +1069 -0
- package/dist/src/sdk/sdk/modules/group-module.js +7397 -0
- package/dist/src/sdk/sdk/modules/relationship-module.js +2269 -0
- package/dist/src/sdk/sdk/modules/signaling-module.js +1468 -0
- package/dist/src/sdk/sdk/modules/tim-upload-plugin.js +730 -0
- package/dist/src/sdk/sdk/node-env/http-request.js +90 -0
- package/dist/src/sdk/sdk/node-env/index.js +57 -0
- package/dist/src/sdk/sdk/node-env/storage.js +114 -0
- package/dist/src/sdk/sdk/package.json +10 -0
- package/dist/src/sdk/sdk/tsconfig.json +16 -0
- package/dist/src/sdk/tsconfig.json +16 -0
- package/dist/src/state/pending-invocation-store.d.ts +26 -0
- package/dist/src/state/pending-invocation-store.js +39 -0
- package/dist/src/state/store.d.ts +165 -0
- package/dist/src/state/store.js +535 -0
- package/dist/src/tim/c2c.d.ts +83 -0
- package/dist/src/tim/c2c.js +318 -0
- package/dist/src/tim/channels.d.ts +95 -0
- package/dist/src/tim/channels.js +279 -0
- package/dist/src/tim/client.d.ts +54 -0
- package/dist/src/tim/client.js +268 -0
- package/dist/src/tim/index.d.ts +6 -0
- package/dist/src/tim/index.js +6 -0
- package/dist/src/tim/messages.d.ts +50 -0
- package/dist/src/tim/messages.js +104 -0
- package/dist/src/tim/sdk-logger-init.d.ts +13 -0
- package/dist/src/tim/sdk-logger-init.js +46 -0
- package/dist/src/tools.d.ts +9 -0
- package/dist/src/tools.js +8 -0
- package/dist/src/transport/factory.d.ts +63 -0
- package/dist/src/transport/factory.js +54 -0
- package/dist/src/transport/oasn/index.d.ts +12 -0
- package/dist/src/transport/oasn/index.js +9 -0
- package/dist/src/transport/oasn/oasn-agent-card.d.ts +38 -0
- package/dist/src/transport/oasn/oasn-agent-card.js +102 -0
- package/dist/src/transport/oasn/oasn-discovery.d.ts +33 -0
- package/dist/src/transport/oasn/oasn-discovery.js +97 -0
- package/dist/src/transport/oasn/oasn-files.d.ts +64 -0
- package/dist/src/transport/oasn/oasn-files.js +174 -0
- package/dist/src/transport/oasn/oasn-http.d.ts +98 -0
- package/dist/src/transport/oasn/oasn-http.js +362 -0
- package/dist/src/transport/oasn/oasn-invocation.d.ts +154 -0
- package/dist/src/transport/oasn/oasn-invocation.js +432 -0
- package/dist/src/transport/oasn/oasn-normalize.d.ts +6 -0
- package/dist/src/transport/oasn/oasn-normalize.js +112 -0
- package/dist/src/transport/oasn/oasn-register.d.ts +19 -0
- package/dist/src/transport/oasn/oasn-register.js +24 -0
- package/dist/src/transport/oasn/oasn-transport.d.ts +114 -0
- package/dist/src/transport/oasn/oasn-transport.js +230 -0
- package/dist/src/transport/oasn/oasn-types.d.ts +331 -0
- package/dist/src/transport/oasn/oasn-types.js +44 -0
- package/dist/src/transport/tim/index.d.ts +7 -0
- package/dist/src/transport/tim/index.js +6 -0
- package/dist/src/transport/tim/tim-transport.d.ts +122 -0
- package/dist/src/transport/tim/tim-transport.js +402 -0
- package/dist/src/transport/types.d.ts +450 -0
- package/dist/src/transport/types.js +38 -0
- package/dist/src/util/http.d.ts +21 -0
- package/dist/src/util/http.js +93 -0
- package/dist/src/util/logger.d.ts +20 -0
- package/dist/src/util/logger.js +100 -0
- package/dist/src/util/url-resolver.d.ts +7 -0
- package/dist/src/util/url-resolver.js +20 -0
- package/index.ts +11 -0
- package/openclaw.plugin.json +9 -0
- package/package.json +7 -4
- package/src/app/download-file-tool.ts +133 -0
- package/src/app/hooks.ts +89 -5
- package/src/app/index.ts +6 -0
- package/src/app/remote-agent-tool.ts +46 -0
- package/src/app/types.ts +1 -0
- package/src/app/upload-file-tool.ts +411 -0
- package/src/plugin-ui/assets/openagent-override.js +1 -63
- package/src/plugin-ui/modules/agent-book/panel/agent-book.js +0 -61
- package/src/plugin-ui/modules/agent-book/panel/agent-data.js +0 -1
- package/src/plugin-ui/ui-extension-loader/registry-regex.ts +30 -13
- package/src/runtime/account.ts +0 -23
- package/src/transport/oasn/oasn-files.ts +5 -5
- package/src/transport/oasn/oasn-invocation.ts +44 -2
- package/src/transport/oasn/oasn-normalize.ts +0 -26
- package/src/transport/oasn/oasn-register.ts +8 -81
- package/src/transport/oasn/oasn-transport.ts +9 -32
- package/src/transport/oasn/oasn-types.ts +53 -6
- package/src/transport/types.ts +33 -36
- package/src/util/url-resolver.ts +17 -0
|
@@ -0,0 +1,701 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UIExtension 注册表 — 正则模式链方案(无需 source map)
|
|
3
|
+
*
|
|
4
|
+
* 用正则锚点定位注入位置,无需 source map。
|
|
5
|
+
*
|
|
6
|
+
* 验证来源:full-inject.mjs — 21 版本 × 6 扩展点,acorn 语法 100% 合法。
|
|
7
|
+
*/
|
|
8
|
+
import { readFileSync, writeFileSync, copyFileSync, unlinkSync, existsSync } from 'node:fs';
|
|
9
|
+
import { createHash } from 'node:crypto';
|
|
10
|
+
import { join } from 'node:path';
|
|
11
|
+
import { logger } from '../../util/logger.js';
|
|
12
|
+
import { findFileByPrefix } from './locator.js';
|
|
13
|
+
import { backupFile, restoreFile } from './backup.js';
|
|
14
|
+
import { makeMarker, hasMarker } from './types.js';
|
|
15
|
+
// ── 常量 ──────────────────────────────────────────────────────────────────
|
|
16
|
+
const OVERRIDE_ASSET_URL = new URL('../assets/openagent-override.js', import.meta.url);
|
|
17
|
+
function getOverrideAssetHash() {
|
|
18
|
+
return createHash('sha256')
|
|
19
|
+
.update(readFileSync(OVERRIDE_ASSET_URL))
|
|
20
|
+
.digest('hex')
|
|
21
|
+
.slice(0, 12);
|
|
22
|
+
}
|
|
23
|
+
// ── 辅助函数 ──────────────────────────────────────────────────────────────
|
|
24
|
+
/** 获取 bundle 路径(不要求 source map 存在)。 */
|
|
25
|
+
function resolveBundlePath(controlUiDir) {
|
|
26
|
+
const assetsDir = join(controlUiDir, 'assets');
|
|
27
|
+
const bundlePath = findFileByPrefix(assetsDir, 'index-');
|
|
28
|
+
if (!bundlePath) {
|
|
29
|
+
logger.debug('[ui-ext:regex] Main bundle not found');
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
return bundlePath;
|
|
33
|
+
}
|
|
34
|
+
/** 在 push({...}) 内找 object literal 闭合 } 的位置。 */
|
|
35
|
+
function findObjectLiteralClose(code, pushParenStart) {
|
|
36
|
+
let i = pushParenStart;
|
|
37
|
+
while (i < code.length && code[i] !== '{')
|
|
38
|
+
i++;
|
|
39
|
+
if (i >= code.length)
|
|
40
|
+
return -1;
|
|
41
|
+
let depth = 1;
|
|
42
|
+
let inTemplate = false;
|
|
43
|
+
for (let j = i + 1; j < code.length; j++) {
|
|
44
|
+
const ch = code[j];
|
|
45
|
+
if (ch === '`') {
|
|
46
|
+
inTemplate = !inTemplate;
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (inTemplate)
|
|
50
|
+
continue;
|
|
51
|
+
if (ch === '{')
|
|
52
|
+
depth++;
|
|
53
|
+
if (ch === '}') {
|
|
54
|
+
depth--;
|
|
55
|
+
if (depth === 0)
|
|
56
|
+
return j;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return -1;
|
|
60
|
+
}
|
|
61
|
+
function findFunctionByAnchor(content, anchor, lookBack = 5000) {
|
|
62
|
+
const anchorIdx = content.indexOf(anchor);
|
|
63
|
+
if (anchorIdx < 0)
|
|
64
|
+
return null;
|
|
65
|
+
const searchStart = Math.max(0, anchorIdx - lookBack);
|
|
66
|
+
const regionBefore = content.substring(searchStart, anchorIdx);
|
|
67
|
+
const funcRe = /function\s+(\w+)\(([^)]*)\)\{/g;
|
|
68
|
+
let lastFunc = null;
|
|
69
|
+
let funcMatch;
|
|
70
|
+
while ((funcMatch = funcRe.exec(regionBefore)) !== null)
|
|
71
|
+
lastFunc = funcMatch;
|
|
72
|
+
if (!lastFunc)
|
|
73
|
+
return null;
|
|
74
|
+
const bodyStart = searchStart + lastFunc.index + lastFunc[0].length;
|
|
75
|
+
let braceDepth = 1;
|
|
76
|
+
let bodyEnd = bodyStart;
|
|
77
|
+
for (let i = bodyStart; i < content.length && braceDepth > 0; i++) {
|
|
78
|
+
if (content[i] === '{')
|
|
79
|
+
braceDepth++;
|
|
80
|
+
else if (content[i] === '}')
|
|
81
|
+
braceDepth--;
|
|
82
|
+
bodyEnd = i + 1;
|
|
83
|
+
}
|
|
84
|
+
if (braceDepth !== 0)
|
|
85
|
+
return null;
|
|
86
|
+
return {
|
|
87
|
+
anchorIdx,
|
|
88
|
+
funcName: lastFunc[1],
|
|
89
|
+
params: lastFunc[2],
|
|
90
|
+
bodyStart,
|
|
91
|
+
bodyEnd,
|
|
92
|
+
body: content.substring(bodyStart, bodyEnd),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
// ── Extension 1: override-script ─────────────────────────────────────────
|
|
96
|
+
const overrideScript = {
|
|
97
|
+
id: 'override-script',
|
|
98
|
+
version: 1,
|
|
99
|
+
alwaysApply: true,
|
|
100
|
+
apply(controlUiDir) {
|
|
101
|
+
if (!existsSync(OVERRIDE_ASSET_URL)) {
|
|
102
|
+
logger.warn('[ui-ext:regex] override-script: asset missing');
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
const dest = join(controlUiDir, 'openagent-override.js');
|
|
106
|
+
if (existsSync(dest)) {
|
|
107
|
+
const srcContent = readFileSync(OVERRIDE_ASSET_URL, 'utf-8');
|
|
108
|
+
const destContent = readFileSync(dest, 'utf-8');
|
|
109
|
+
if (srcContent === destContent) {
|
|
110
|
+
logger.debug('[ui-ext:regex] override-script: content unchanged, skip');
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
copyFileSync(OVERRIDE_ASSET_URL, dest);
|
|
115
|
+
logger.info('[ui-ext:regex] override-script: deployed openagent-override.js');
|
|
116
|
+
return true;
|
|
117
|
+
},
|
|
118
|
+
revert(controlUiDir) {
|
|
119
|
+
const dest = join(controlUiDir, 'openagent-override.js');
|
|
120
|
+
if (existsSync(dest)) {
|
|
121
|
+
unlinkSync(dest);
|
|
122
|
+
logger.info('[ui-ext:regex] override-script: removed');
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
// ── Extension 2: html-script-tag ─────────────────────────────────────────
|
|
127
|
+
const htmlScriptTag = {
|
|
128
|
+
id: 'html-script-tag',
|
|
129
|
+
version: 4,
|
|
130
|
+
alwaysApply: true,
|
|
131
|
+
apply(controlUiDir) {
|
|
132
|
+
const indexPath = join(controlUiDir, 'index.html');
|
|
133
|
+
if (!existsSync(indexPath)) {
|
|
134
|
+
logger.warn('[ui-ext:regex] html-script-tag: index.html not found');
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
let html = readFileSync(indexPath, 'utf-8');
|
|
138
|
+
const htmlMarker = `<!--openagent:${this.id}-->`;
|
|
139
|
+
const overrideHash = getOverrideAssetHash();
|
|
140
|
+
const scriptTag = `${htmlMarker}<script src="./openagent-override.js?v=${overrideHash}"></script>`;
|
|
141
|
+
const markedScriptPattern = /(?:\/\*openagent:html-script-tag\*\/|<!--openagent:html-script-tag-->)<script[^>]*openagent-override[^>]*><\/script>\n?\s*/g;
|
|
142
|
+
if (html.includes(htmlMarker) || hasMarker(html, this.id)) {
|
|
143
|
+
const nextHtml = html.replace(markedScriptPattern, `${scriptTag}\n `);
|
|
144
|
+
if (nextHtml === html) {
|
|
145
|
+
logger.debug('[ui-ext:regex] html-script-tag: already applied');
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
writeFileSync(indexPath, nextHtml, 'utf-8');
|
|
149
|
+
logger.info(`[ui-ext:regex] html-script-tag: updated override cache-bust (${overrideHash})`);
|
|
150
|
+
}
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
const oldScriptPattern = /<script[^>]*openagent-override[^>]*><\/script>\n?\s*/g;
|
|
154
|
+
if (oldScriptPattern.test(html)) {
|
|
155
|
+
html = html.replace(oldScriptPattern, '');
|
|
156
|
+
}
|
|
157
|
+
const viteAnchor = '<script type="module" crossorigin src="./assets/index-';
|
|
158
|
+
const anchorIdx = html.indexOf(viteAnchor);
|
|
159
|
+
if (anchorIdx >= 0) {
|
|
160
|
+
html = html.substring(0, anchorIdx) + scriptTag + '\n ' + html.substring(anchorIdx);
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
html = html.replace('</head>', `${scriptTag}\n</head>`);
|
|
164
|
+
}
|
|
165
|
+
writeFileSync(indexPath, html, 'utf-8');
|
|
166
|
+
logger.info('[ui-ext:regex] html-script-tag: injected');
|
|
167
|
+
return true;
|
|
168
|
+
},
|
|
169
|
+
revert(controlUiDir) {
|
|
170
|
+
const indexPath = join(controlUiDir, 'index.html');
|
|
171
|
+
if (!existsSync(indexPath))
|
|
172
|
+
return;
|
|
173
|
+
let html = readFileSync(indexPath, 'utf-8');
|
|
174
|
+
html = html.replace(/(?:\/\*openagent:html-script-tag\*\/|<!--openagent:html-script-tag-->)<script[^>]*openagent-override[^>]*><\/script>\n?\s*/g, '');
|
|
175
|
+
writeFileSync(indexPath, html, 'utf-8');
|
|
176
|
+
logger.info('[ui-ext:regex] html-script-tag: removed');
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
// ── Extension 4: toolcard-render(ToolCard DOM 结构锚点定位)────────────────
|
|
180
|
+
const toolcardRender = {
|
|
181
|
+
id: 'toolcard-render',
|
|
182
|
+
version: 6,
|
|
183
|
+
sharedFile: 'index-bundle',
|
|
184
|
+
apply(controlUiDir) {
|
|
185
|
+
const bundlePath = resolveBundlePath(controlUiDir);
|
|
186
|
+
if (!bundlePath)
|
|
187
|
+
return false;
|
|
188
|
+
let content = readFileSync(bundlePath, 'utf-8');
|
|
189
|
+
if (hasMarker(content, this.id)) {
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
if (!backupFile(bundlePath))
|
|
193
|
+
return false;
|
|
194
|
+
// 用 chat-tool-card__header 做 primary anchor — 3.28/4.11/latest 均存在且唯一
|
|
195
|
+
const classAnchor = 'chat-tool-card__header';
|
|
196
|
+
const innerFn = findFunctionByAnchor(content, classAnchor);
|
|
197
|
+
if (!innerFn) {
|
|
198
|
+
logger.warn('[ui-ext:regex] toolcard-render: chat-tool-card__header anchor not found');
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
const funcName = innerFn.funcName;
|
|
202
|
+
const params = innerFn.params;
|
|
203
|
+
const fnBody = innerFn.body;
|
|
204
|
+
// 校验:函数体必须包含 ToolCard DOM 结构
|
|
205
|
+
if (!fnBody.includes('chat-tool-card')) {
|
|
206
|
+
logger.warn('[ui-ext:regex] toolcard-render: candidate function does not contain chat-tool-card class');
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
// 排除 markdown builder:如果函数体包含 markdown builder 特征,说明定位到了错误函数
|
|
210
|
+
if (fnBody.includes('### Tool output') && !fnBody.includes('chat-tool-card__header')) {
|
|
211
|
+
logger.warn('[ui-ext:regex] toolcard-render: candidate function looks like markdown builder, not ToolCard renderer');
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
// 找 Lit html tag: return X`...` — 只在候选函数体内搜索
|
|
215
|
+
const returnTagMatch = fnBody.match(/return\s+([a-zA-Z_$]\w*)\s*`/);
|
|
216
|
+
if (!returnTagMatch) {
|
|
217
|
+
logger.warn('[ui-ext:regex] toolcard-render: Lit html tag not found in candidate function');
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
const litHtmlVar = returnTagMatch[1];
|
|
221
|
+
const paramParts = params.split(',').map((s) => s.trim());
|
|
222
|
+
const cardParam = paramParts[0] || 'e';
|
|
223
|
+
const sidebarParam = paramParts[1] || undefined;
|
|
224
|
+
const sidebarArg = sidebarParam || 'undefined';
|
|
225
|
+
const marker = makeMarker(this.id);
|
|
226
|
+
const hook = `${marker}if(window.__openagentRenderToolCard){let __cl=window.__openagentRenderToolCard(${cardParam},${sidebarArg});if(__cl)return ${litHtmlVar}\`\${__cl}\`}`;
|
|
227
|
+
content = content.substring(0, innerFn.bodyStart) + hook + content.substring(innerFn.bodyStart);
|
|
228
|
+
// 4.11+ introduced an outer manual-collapse renderer. The inner ToolCard
|
|
229
|
+
// renderer above only runs after the user expands the native card, so patch
|
|
230
|
+
// the wrapper too to let OpenAgent own collapsed remote-agent cards from the
|
|
231
|
+
// first render.
|
|
232
|
+
const wrapperAnchor = 'chat-tool-msg-collapse--manual';
|
|
233
|
+
const hasManualCollapseWrapper = content.includes(wrapperAnchor);
|
|
234
|
+
let patchedWrapper = false;
|
|
235
|
+
if (hasManualCollapseWrapper) {
|
|
236
|
+
const wrapperFn = findFunctionByAnchor(content, wrapperAnchor);
|
|
237
|
+
if (!wrapperFn) {
|
|
238
|
+
logger.warn('[ui-ext:regex] toolcard-render: manual collapse wrapper function not found');
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
if (!wrapperFn.body.includes('chat-tool-msg-body') || !wrapperFn.body.includes('onToggleExpanded')) {
|
|
242
|
+
logger.warn('[ui-ext:regex] toolcard-render: manual collapse wrapper shape mismatch');
|
|
243
|
+
return false;
|
|
244
|
+
}
|
|
245
|
+
const wrapperReturnTagMatch = wrapperFn.body.match(/return\s+([a-zA-Z_$]\w*)\s*`/);
|
|
246
|
+
if (!wrapperReturnTagMatch) {
|
|
247
|
+
logger.warn('[ui-ext:regex] toolcard-render: manual collapse Lit html tag not found');
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
250
|
+
const wrapperHtmlVar = wrapperReturnTagMatch[1];
|
|
251
|
+
const wrapperParams = wrapperFn.params.split(',').map((s) => s.trim());
|
|
252
|
+
const wrapperCardParam = wrapperParams[0] || 'e';
|
|
253
|
+
const wrapperOptionsParam = wrapperParams[1] || '';
|
|
254
|
+
const wrapperSidebarArg = wrapperOptionsParam ? `${wrapperOptionsParam}&&${wrapperOptionsParam}.onOpenSidebar` : 'undefined';
|
|
255
|
+
const wrapperHook = `${marker}if(window.__openagentRenderToolCard){let __cl=window.__openagentRenderToolCard(${wrapperCardParam},${wrapperSidebarArg});if(__cl)return ${wrapperHtmlVar}\`\${__cl}\`}`;
|
|
256
|
+
content = content.substring(0, wrapperFn.bodyStart) + wrapperHook + content.substring(wrapperFn.bodyStart);
|
|
257
|
+
patchedWrapper = true;
|
|
258
|
+
logger.info(`[ui-ext:regex] toolcard-render: patched manual wrapper ${wrapperFn.funcName}(${wrapperFn.params.substring(0, 20)}) html=${wrapperHtmlVar}`);
|
|
259
|
+
}
|
|
260
|
+
// 4.11+ also renders tool-result messages through the grouped message
|
|
261
|
+
// renderer itself instead of nb(). That path only calls the inner ToolCard
|
|
262
|
+
// renderer after the user expands the native "Tool output" row. Patch the
|
|
263
|
+
// grouped message function so OpenAgent result cards are owned immediately.
|
|
264
|
+
const toolMessageAnchor = 'toolmsg:${';
|
|
265
|
+
let patchedToolMessageWrapper = false;
|
|
266
|
+
if (content.includes(toolMessageAnchor)) {
|
|
267
|
+
const toolMessageFn = findFunctionByAnchor(content, toolMessageAnchor, 8000);
|
|
268
|
+
if (!toolMessageFn) {
|
|
269
|
+
logger.warn('[ui-ext:regex] toolcard-render: tool message wrapper function not found');
|
|
270
|
+
return false;
|
|
271
|
+
}
|
|
272
|
+
if (!toolMessageFn.body.includes('chat-tool-msg-collapse--manual')
|
|
273
|
+
|| !toolMessageFn.body.includes('chat-tool-msg-body')) {
|
|
274
|
+
logger.warn('[ui-ext:regex] toolcard-render: tool message wrapper shape mismatch');
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
const toolMessageReturnTagMatch = toolMessageFn.body.match(/return\s+([a-zA-Z_$]\w*)\s*`/);
|
|
278
|
+
if (!toolMessageReturnTagMatch) {
|
|
279
|
+
logger.warn('[ui-ext:regex] toolcard-render: tool message Lit html tag not found');
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
const toolMessageHtmlVar = toolMessageReturnTagMatch[1];
|
|
283
|
+
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*)\?/);
|
|
284
|
+
if (!singleCardMatch) {
|
|
285
|
+
logger.warn('[ui-ext:regex] toolcard-render: tool message single-card guard not found');
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
const cardsVar = singleCardMatch[2];
|
|
289
|
+
const mediaVar = singleCardMatch[4];
|
|
290
|
+
const chatBubbleClassMatch = toolMessageFn.body.match(/[,;]([a-zA-Z_$]\w*)=\[`chat-bubble`,/);
|
|
291
|
+
if (!chatBubbleClassMatch) {
|
|
292
|
+
logger.warn('[ui-ext:regex] toolcard-render: tool message chat-bubble class var not found');
|
|
293
|
+
return false;
|
|
294
|
+
}
|
|
295
|
+
const chatBubbleClassVar = chatBubbleClassMatch[1];
|
|
296
|
+
const toolMessageParams = toolMessageFn.params.split(',').map((s) => s.trim());
|
|
297
|
+
const toolMessageSidebarArg = toolMessageParams[3] || 'undefined';
|
|
298
|
+
const toolMessageReturnIdx = toolMessageFn.body.indexOf(`return ${toolMessageHtmlVar}\``);
|
|
299
|
+
if (toolMessageReturnIdx < 0) {
|
|
300
|
+
logger.warn('[ui-ext:regex] toolcard-render: tool message return anchor not found');
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
303
|
+
const toolMessageHook = `${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>\`;`;
|
|
304
|
+
const toolMessageInsertAt = toolMessageFn.bodyStart + toolMessageReturnIdx;
|
|
305
|
+
content = content.substring(0, toolMessageInsertAt) + toolMessageHook + content.substring(toolMessageInsertAt);
|
|
306
|
+
patchedToolMessageWrapper = true;
|
|
307
|
+
logger.info(`[ui-ext:regex] toolcard-render: patched tool message wrapper ${toolMessageFn.funcName}(${toolMessageFn.params.substring(0, 20)}) html=${toolMessageHtmlVar}`);
|
|
308
|
+
}
|
|
309
|
+
writeFileSync(bundlePath, content, 'utf-8');
|
|
310
|
+
logger.info(`[ui-ext:regex] toolcard-render: patched ${funcName}(${params.substring(0, 20)}) html=${litHtmlVar}${patchedWrapper ? ' + wrapper' : ''}${patchedToolMessageWrapper ? ' + tool-message' : ''}`);
|
|
311
|
+
return true;
|
|
312
|
+
},
|
|
313
|
+
revert(controlUiDir) {
|
|
314
|
+
const bundlePath = resolveBundlePath(controlUiDir);
|
|
315
|
+
if (bundlePath) {
|
|
316
|
+
restoreFile(bundlePath);
|
|
317
|
+
}
|
|
318
|
+
},
|
|
319
|
+
};
|
|
320
|
+
// ── Extension 5: toolcallid-propagate(正则定位,双规则锚点)──────────────
|
|
321
|
+
const toolcallidPropagate = {
|
|
322
|
+
id: 'toolcallid-propagate',
|
|
323
|
+
version: 4,
|
|
324
|
+
sharedFile: 'index-bundle',
|
|
325
|
+
apply(controlUiDir) {
|
|
326
|
+
const bundlePath = resolveBundlePath(controlUiDir);
|
|
327
|
+
if (!bundlePath)
|
|
328
|
+
return false;
|
|
329
|
+
let content = readFileSync(bundlePath, 'utf-8');
|
|
330
|
+
if (hasMarker(content, this.id)) {
|
|
331
|
+
return true;
|
|
332
|
+
}
|
|
333
|
+
if (!backupFile(bundlePath))
|
|
334
|
+
return false;
|
|
335
|
+
// 双规则锚点定位 extractToolCards
|
|
336
|
+
const ruleV4 = /\[`toolcall`,`tool_call`,`tooluse`,`tool_use`\]\.includes\(\w+\)/;
|
|
337
|
+
const ruleV3 = /(\w+)\.push\(\{kind:`call`,name:/;
|
|
338
|
+
const anchorMatch = content.match(ruleV4) || content.match(ruleV3);
|
|
339
|
+
if (!anchorMatch) {
|
|
340
|
+
logger.warn('[ui-ext:regex] toolcallid-propagate: anchor not found');
|
|
341
|
+
return false;
|
|
342
|
+
}
|
|
343
|
+
// 向前找函数声明(支持多参数+默认值)
|
|
344
|
+
const anchorPos = anchorMatch.index;
|
|
345
|
+
const lookBack = content.substring(Math.max(0, anchorPos - 3000), anchorPos);
|
|
346
|
+
const funcDeclRe = /function\s+(\w+)\(([^)]+)\)\{/g;
|
|
347
|
+
let lastFuncDecl = null;
|
|
348
|
+
let fm;
|
|
349
|
+
while ((fm = funcDeclRe.exec(lookBack)) !== null)
|
|
350
|
+
lastFuncDecl = fm;
|
|
351
|
+
if (!lastFuncDecl) {
|
|
352
|
+
logger.warn('[ui-ext:regex] toolcallid-propagate: function decl not found');
|
|
353
|
+
return false;
|
|
354
|
+
}
|
|
355
|
+
const funcStartGlobal = Math.max(0, anchorPos - 3000) + lastFuncDecl.index;
|
|
356
|
+
const msgParam = lastFuncDecl[2].split(',')[0].trim();
|
|
357
|
+
const bodyStart = funcStartGlobal + lastFuncDecl[0].length;
|
|
358
|
+
// 找函数结束
|
|
359
|
+
let braceDepth = 1;
|
|
360
|
+
let funcEnd = -1;
|
|
361
|
+
for (let i = bodyStart; i < content.length && i < bodyStart + 5000; i++) {
|
|
362
|
+
if (content[i] === '{')
|
|
363
|
+
braceDepth++;
|
|
364
|
+
if (content[i] === '}') {
|
|
365
|
+
braceDepth--;
|
|
366
|
+
if (braceDepth === 0) {
|
|
367
|
+
funcEnd = i + 1;
|
|
368
|
+
break;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
if (funcEnd < 0) {
|
|
373
|
+
logger.warn('[ui-ext:regex] toolcallid-propagate: function end not found');
|
|
374
|
+
return false;
|
|
375
|
+
}
|
|
376
|
+
const funcBody = content.substring(bodyStart, funcEnd);
|
|
377
|
+
// 提取变量名
|
|
378
|
+
const mVarMatch = funcBody.match(/let\s+(\w+)\s*=\s*\w+\s*,/);
|
|
379
|
+
if (!mVarMatch) {
|
|
380
|
+
logger.warn('[ui-ext:regex] toolcallid-propagate: message alias not found');
|
|
381
|
+
return false;
|
|
382
|
+
}
|
|
383
|
+
const mVar = mVarMatch[1];
|
|
384
|
+
const cardsMatch = funcBody.match(/,(\w+)=\[\](?:;|,)/);
|
|
385
|
+
if (!cardsMatch) {
|
|
386
|
+
logger.warn('[ui-ext:regex] toolcallid-propagate: cards array not found');
|
|
387
|
+
return false;
|
|
388
|
+
}
|
|
389
|
+
const cardsVar = cardsMatch[1];
|
|
390
|
+
// 循环变量:for-of 或 for-index
|
|
391
|
+
const forOfMatches = [...funcBody.matchAll(/for\(let\s+(\w+)\s+of\s+\w+\)/g)];
|
|
392
|
+
let callLoopVar, resultLoopVar;
|
|
393
|
+
if (forOfMatches.length > 0) {
|
|
394
|
+
callLoopVar = forOfMatches[0]?.[1] ?? null;
|
|
395
|
+
resultLoopVar = forOfMatches[1]?.[1] ?? forOfMatches[0]?.[1] ?? null;
|
|
396
|
+
}
|
|
397
|
+
else {
|
|
398
|
+
const indexLoopMatch = funcBody.match(/for\(let\s+(\w+)=0;\1<(\w+)\.length;\1\+\+\)/);
|
|
399
|
+
if (indexLoopMatch) {
|
|
400
|
+
const loopBodyStart = funcBody.indexOf(indexLoopMatch[0]) + indexLoopMatch[0].length;
|
|
401
|
+
const loopBody = funcBody.substring(loopBodyStart, loopBodyStart + 100);
|
|
402
|
+
const elemMatch = loopBody.match(/\{let\s+(\w+)=/);
|
|
403
|
+
callLoopVar = elemMatch ? elemMatch[1] : null;
|
|
404
|
+
resultLoopVar = callLoopVar;
|
|
405
|
+
}
|
|
406
|
+
else {
|
|
407
|
+
callLoopVar = null;
|
|
408
|
+
resultLoopVar = null;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
logger.debug(`[ui-ext:regex] toolcallid-propagate: mVar=${mVar} cards=${cardsVar} callLoop=${callLoopVar} resultLoop=${resultLoopVar}`);
|
|
412
|
+
// 计算注入内容
|
|
413
|
+
const marker = makeMarker(this.id);
|
|
414
|
+
const tcidCapture = `${marker}var __tcid=(typeof ${mVar}.toolCallId==='string'&&${mVar}.toolCallId)||(typeof ${mVar}.tool_call_id==='string'&&${mVar}.tool_call_id)||'';`;
|
|
415
|
+
const inlinePatchCall = callLoopVar
|
|
416
|
+
? `,toolCallId:__tcid||${callLoopVar}.id||${callLoopVar}.toolCallId||${callLoopVar}.tool_call_id||''`
|
|
417
|
+
: `,toolCallId:__tcid`;
|
|
418
|
+
const inlinePatchResult = resultLoopVar
|
|
419
|
+
? `,toolCallId:__tcid||${resultLoopVar}.tool_use_id||''`
|
|
420
|
+
: `,toolCallId:__tcid`;
|
|
421
|
+
const inlinePatchFallback = `,toolCallId:${mVar}.toolCallId||${mVar}.tool_call_id||''`;
|
|
422
|
+
// 找 cards 数组初始化位置
|
|
423
|
+
const arrInitPattern = `,${cardsVar}=[];`;
|
|
424
|
+
let arrInitIdx = funcBody.indexOf(arrInitPattern);
|
|
425
|
+
if (arrInitIdx < 0) {
|
|
426
|
+
// v4.11+: 可能是 ,i=[](后接分号或逗号之前的位置不同)
|
|
427
|
+
const altPattern = `,${cardsVar}=[]`;
|
|
428
|
+
arrInitIdx = funcBody.indexOf(altPattern);
|
|
429
|
+
if (arrInitIdx < 0) {
|
|
430
|
+
logger.warn('[ui-ext:regex] toolcallid-propagate: cards init not found');
|
|
431
|
+
return false;
|
|
432
|
+
}
|
|
433
|
+
// 找到 ] 后面的分隔符位置
|
|
434
|
+
arrInitIdx += altPattern.length;
|
|
435
|
+
// captureInsertOffset 在 ; 或 , 之后
|
|
436
|
+
}
|
|
437
|
+
else {
|
|
438
|
+
arrInitIdx += arrInitPattern.length;
|
|
439
|
+
}
|
|
440
|
+
const captureInsertOffset = bodyStart + arrInitIdx;
|
|
441
|
+
// 找 push 位置(支持 kind: 前缀和无前缀)
|
|
442
|
+
let pushAnchor = `${cardsVar}.push({kind:`;
|
|
443
|
+
if (funcBody.indexOf(pushAnchor) < 0) {
|
|
444
|
+
pushAnchor = `${cardsVar}.push({`;
|
|
445
|
+
}
|
|
446
|
+
const literalCloseOffsets = [];
|
|
447
|
+
let searchPos = 0;
|
|
448
|
+
while (literalCloseOffsets.length < 4) {
|
|
449
|
+
const idx = funcBody.indexOf(pushAnchor, searchPos);
|
|
450
|
+
if (idx < 0)
|
|
451
|
+
break;
|
|
452
|
+
const pushParenStart = bodyStart + idx + `${cardsVar}.push`.length;
|
|
453
|
+
const objClose = findObjectLiteralClose(content, pushParenStart);
|
|
454
|
+
if (objClose < 0) {
|
|
455
|
+
logger.warn(`[ui-ext:regex] toolcallid-propagate: object literal close not found at ${idx}`);
|
|
456
|
+
return false;
|
|
457
|
+
}
|
|
458
|
+
literalCloseOffsets.push(objClose);
|
|
459
|
+
searchPos = idx + pushAnchor.length;
|
|
460
|
+
}
|
|
461
|
+
if (literalCloseOffsets.length < 2) {
|
|
462
|
+
logger.warn(`[ui-ext:regex] toolcallid-propagate: expected >=2 push sites, found ${literalCloseOffsets.length}`);
|
|
463
|
+
return false;
|
|
464
|
+
}
|
|
465
|
+
// guard 扩展 — P0: guard patch 必须成功,否则 result card 无法识别 message-level tool result
|
|
466
|
+
// Shape A (old): &&!cards.some(...)
|
|
467
|
+
// Shape B (4.11/latest): &&cards.length===0
|
|
468
|
+
const guardAnchorA = `&&!${cardsVar}.some(`;
|
|
469
|
+
const guardAnchorB = `&&${cardsVar}.length===0`;
|
|
470
|
+
const guardIdxA = funcBody.indexOf(guardAnchorA);
|
|
471
|
+
const guardIdxB = funcBody.indexOf(guardAnchorB);
|
|
472
|
+
let guardPatch = null;
|
|
473
|
+
if (guardIdxA >= 0) {
|
|
474
|
+
// Shape A: if(<original> && !cards.some(...)) { ... }
|
|
475
|
+
let ifStart = guardIdxA - 1;
|
|
476
|
+
while (ifStart > 0 && funcBody.substring(ifStart, ifStart + 3) !== 'if(')
|
|
477
|
+
ifStart--;
|
|
478
|
+
if (funcBody.substring(ifStart, ifStart + 3) !== 'if(') {
|
|
479
|
+
logger.warn('[ui-ext:regex] toolcallid-propagate: Shape A guard if() not found');
|
|
480
|
+
return false;
|
|
481
|
+
}
|
|
482
|
+
const originalGuard = funcBody.substring(ifStart + 3, guardIdxA);
|
|
483
|
+
const expandedGuard = `(${originalGuard}||typeof ${mVar}.toolCallId==\`string\`||typeof ${mVar}.tool_call_id==\`string\`||typeof ${mVar}.toolName==\`string\`||typeof ${mVar}.tool_name==\`string\`)`;
|
|
484
|
+
guardPatch = [bodyStart + ifStart + 3, originalGuard, expandedGuard];
|
|
485
|
+
logger.debug('[ui-ext:regex] toolcallid-propagate: using Shape A guard');
|
|
486
|
+
}
|
|
487
|
+
else if (guardIdxB >= 0) {
|
|
488
|
+
// Shape B: if((<original>) && cards.length===0) { ... }
|
|
489
|
+
let ifStart = guardIdxB - 1;
|
|
490
|
+
// Shape B may have extra parens: if((...) && cards.length===0)
|
|
491
|
+
// Walk back past closing paren to find the if(
|
|
492
|
+
while (ifStart > 0 && funcBody.substring(ifStart, ifStart + 3) !== 'if(')
|
|
493
|
+
ifStart--;
|
|
494
|
+
if (funcBody.substring(ifStart, ifStart + 3) !== 'if(') {
|
|
495
|
+
logger.warn('[ui-ext:regex] toolcallid-propagate: Shape B guard if() not found');
|
|
496
|
+
return false;
|
|
497
|
+
}
|
|
498
|
+
// The original condition is between if( and &&cards.length===0
|
|
499
|
+
// It might be wrapped: if((<cond>)&&cards.length===0)
|
|
500
|
+
let originalGuard = funcBody.substring(ifStart + 3, guardIdxB);
|
|
501
|
+
// Strip outer parens if present: (expr) → expr
|
|
502
|
+
if (originalGuard.startsWith('(') && originalGuard.endsWith(')')) {
|
|
503
|
+
originalGuard = originalGuard.substring(1, originalGuard.length - 1);
|
|
504
|
+
}
|
|
505
|
+
const expandedGuard = `(${originalGuard}||typeof ${mVar}.toolCallId==\`string\`||typeof ${mVar}.tool_call_id==\`string\`||typeof ${mVar}.toolName==\`string\`||typeof ${mVar}.tool_name==\`string\`)`;
|
|
506
|
+
guardPatch = [bodyStart + ifStart + 3, funcBody.substring(ifStart + 3, guardIdxB), expandedGuard];
|
|
507
|
+
logger.debug('[ui-ext:regex] toolcallid-propagate: using Shape B guard');
|
|
508
|
+
}
|
|
509
|
+
else {
|
|
510
|
+
logger.warn('[ui-ext:regex] toolcallid-propagate: neither Shape A nor Shape B guard found — refusing to patch');
|
|
511
|
+
return false;
|
|
512
|
+
}
|
|
513
|
+
// 从后往前插入
|
|
514
|
+
const insertions = [];
|
|
515
|
+
if (literalCloseOffsets.length >= 3) {
|
|
516
|
+
insertions.push([literalCloseOffsets[2], inlinePatchFallback]);
|
|
517
|
+
}
|
|
518
|
+
if (literalCloseOffsets.length >= 2) {
|
|
519
|
+
insertions.push([literalCloseOffsets[1], inlinePatchResult]);
|
|
520
|
+
}
|
|
521
|
+
insertions.push([literalCloseOffsets[0], inlinePatchCall]);
|
|
522
|
+
insertions.push([captureInsertOffset, tcidCapture]);
|
|
523
|
+
if (guardPatch) {
|
|
524
|
+
const [offset, oldText, newText] = guardPatch;
|
|
525
|
+
const globalGuardIdx = content.indexOf(oldText, offset);
|
|
526
|
+
if (globalGuardIdx >= 0 && globalGuardIdx < offset + oldText.length + 10) {
|
|
527
|
+
content = content.substring(0, globalGuardIdx) + newText + content.substring(globalGuardIdx + oldText.length);
|
|
528
|
+
const delta = newText.length - oldText.length;
|
|
529
|
+
for (let i = 0; i < insertions.length; i++) {
|
|
530
|
+
if (insertions[i][0] > globalGuardIdx) {
|
|
531
|
+
insertions[i] = [insertions[i][0] + delta, insertions[i][1]];
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
for (const [offset, text] of insertions) {
|
|
537
|
+
content = content.substring(0, offset) + text + content.substring(offset);
|
|
538
|
+
}
|
|
539
|
+
writeFileSync(bundlePath, content, 'utf-8');
|
|
540
|
+
const pushCount = literalCloseOffsets.length;
|
|
541
|
+
const patchCount = (guardPatch ? 1 : 0) + pushCount + 1;
|
|
542
|
+
logger.info(`[ui-ext:regex] toolcallid-propagate: patched ${patchCount} injection points (pushes=${pushCount})`);
|
|
543
|
+
return true;
|
|
544
|
+
},
|
|
545
|
+
revert(controlUiDir) {
|
|
546
|
+
const bundlePath = resolveBundlePath(controlUiDir);
|
|
547
|
+
if (bundlePath) {
|
|
548
|
+
restoreFile(bundlePath);
|
|
549
|
+
}
|
|
550
|
+
},
|
|
551
|
+
};
|
|
552
|
+
// ── Extension 6: remote-agent-message-short-circuit(三策略 A/B/C)───────
|
|
553
|
+
const remoteAgentMessageShortCircuit = {
|
|
554
|
+
id: 'remote-agent-message-short-circuit',
|
|
555
|
+
version: 2,
|
|
556
|
+
sharedFile: 'index-bundle',
|
|
557
|
+
apply(controlUiDir) {
|
|
558
|
+
const bundlePath = resolveBundlePath(controlUiDir);
|
|
559
|
+
if (!bundlePath)
|
|
560
|
+
return false;
|
|
561
|
+
let content = readFileSync(bundlePath, 'utf-8');
|
|
562
|
+
if (hasMarker(content, this.id)) {
|
|
563
|
+
return true;
|
|
564
|
+
}
|
|
565
|
+
if (!backupFile(bundlePath))
|
|
566
|
+
return false;
|
|
567
|
+
// 找 showToolCalls 赋值 → 提取 toolCardsVar / hasToolCardsVar
|
|
568
|
+
const assignRe = /(\w+)=\w+\.showToolCalls[^;]*\(\w+(?:,\w+)?\)\s*:\s*\[\]\s*,\s*(\w+)\s*=\s*\1\.length\s*>\s*0/;
|
|
569
|
+
const assignMatch = content.match(assignRe);
|
|
570
|
+
if (!assignMatch) {
|
|
571
|
+
logger.warn('[ui-ext:regex] remote-agent-msg: showToolCalls assignment not found');
|
|
572
|
+
return false;
|
|
573
|
+
}
|
|
574
|
+
const toolCardsVar = assignMatch[1];
|
|
575
|
+
const hasToolCardsVar = assignMatch[2];
|
|
576
|
+
const marker = makeMarker(this.id);
|
|
577
|
+
// 策略A (v3.23~v4.10): if(!markdown&&hasToolCards&&isToolResult)
|
|
578
|
+
const ifReA = new RegExp(`if\\(!(\\w+)&&${hasToolCardsVar}&&(\\w+)\\)`);
|
|
579
|
+
const ifMatchA = content.match(ifReA);
|
|
580
|
+
if (ifMatchA) {
|
|
581
|
+
const markdownVar = ifMatchA[1];
|
|
582
|
+
const isToolResultVar = ifMatchA[2];
|
|
583
|
+
const patched = `if(${marker}(!${markdownVar}||window.__openagentOwnsToolCards&&window.__openagentOwnsToolCards(${toolCardsVar}))&&${hasToolCardsVar}&&${isToolResultVar})`;
|
|
584
|
+
content = content.substring(0, ifMatchA.index) + patched + content.substring(ifMatchA.index + ifMatchA[0].length);
|
|
585
|
+
writeFileSync(bundlePath, content, 'utf-8');
|
|
586
|
+
logger.info(`[ui-ext:regex] remote-agent-msg: patched (strategy A, md=${markdownVar})`);
|
|
587
|
+
return true;
|
|
588
|
+
}
|
|
589
|
+
// 策略C (2026.4.x): if(!VAR.showToolCalls&&...toolresult...)continue
|
|
590
|
+
// 直接匹配 continue 跳过语句,注入 openagentOwnsToolCards 条件
|
|
591
|
+
const continueRe = /if\(!(\w+)\.showToolCalls&&(\w+)\.role\.toLowerCase\(\)===`toolresult`\)continue/;
|
|
592
|
+
const continueMatch = content.match(continueRe);
|
|
593
|
+
if (continueMatch) {
|
|
594
|
+
const scVar = continueMatch[1];
|
|
595
|
+
const roleVar = continueMatch[2];
|
|
596
|
+
const patched = `if(!${scVar}.showToolCalls&&!window.__openagentOwnsToolCards?.(${toolCardsVar})&&${roleVar}.role.toLowerCase()===\`toolresult\`)continue`;
|
|
597
|
+
content = content.substring(0, continueMatch.index) + marker + patched + content.substring(continueMatch.index + continueMatch[0].length);
|
|
598
|
+
writeFileSync(bundlePath, content, 'utf-8');
|
|
599
|
+
logger.info(`[ui-ext:regex] remote-agent-msg: patched (strategy C, sc=${scVar})`);
|
|
600
|
+
return true;
|
|
601
|
+
}
|
|
602
|
+
// 策略B (v4.11~v4.1x fallback): compound = hasToolCards && (showToolCalls ?? !0)
|
|
603
|
+
const compoundRe = new RegExp(`(\\w+)=${hasToolCardsVar}&&\\(\\w+\\.showToolCalls\\?\\?!0\\)`);
|
|
604
|
+
const compoundMatch = content.match(compoundRe);
|
|
605
|
+
if (!compoundMatch) {
|
|
606
|
+
logger.warn('[ui-ext:regex] remote-agent-msg: all strategies (A/C/B) failed');
|
|
607
|
+
return false;
|
|
608
|
+
}
|
|
609
|
+
const compoundVar = compoundMatch[1];
|
|
610
|
+
const ifReB = new RegExp(`if\\(!(\\w+)&&!${compoundVar}&&`);
|
|
611
|
+
const ifMatchB = content.match(ifReB);
|
|
612
|
+
if (!ifMatchB) {
|
|
613
|
+
logger.warn(`[ui-ext:regex] remote-agent-msg: compound var ${compoundVar} found but if not matched`);
|
|
614
|
+
return false;
|
|
615
|
+
}
|
|
616
|
+
const markdownVarB = ifMatchB[1];
|
|
617
|
+
const patchedCond = `if(${marker}(!${markdownVarB}||window.__openagentOwnsToolCards&&window.__openagentOwnsToolCards(${toolCardsVar}))&&!${compoundVar}&&`;
|
|
618
|
+
content = content.substring(0, ifMatchB.index) + patchedCond + content.substring(ifMatchB.index + ifMatchB[0].length);
|
|
619
|
+
writeFileSync(bundlePath, content, 'utf-8');
|
|
620
|
+
logger.info(`[ui-ext:regex] remote-agent-msg: patched (strategy B, md=${markdownVarB}, compound=${compoundVar})`);
|
|
621
|
+
return true;
|
|
622
|
+
},
|
|
623
|
+
revert(controlUiDir) {
|
|
624
|
+
const bundlePath = resolveBundlePath(controlUiDir);
|
|
625
|
+
if (bundlePath) {
|
|
626
|
+
restoreFile(bundlePath);
|
|
627
|
+
}
|
|
628
|
+
},
|
|
629
|
+
};
|
|
630
|
+
// ── Extension 7: mention-hooks ───────────────────────────────────────────
|
|
631
|
+
const mentionHooks = {
|
|
632
|
+
id: 'mention-hooks',
|
|
633
|
+
version: 2,
|
|
634
|
+
sharedFile: 'index-bundle',
|
|
635
|
+
apply(controlUiDir) {
|
|
636
|
+
const bundlePath = resolveBundlePath(controlUiDir);
|
|
637
|
+
if (!bundlePath)
|
|
638
|
+
return false;
|
|
639
|
+
let content = readFileSync(bundlePath, 'utf-8');
|
|
640
|
+
if (hasMarker(content, this.id)) {
|
|
641
|
+
return true;
|
|
642
|
+
}
|
|
643
|
+
if (!backupFile(bundlePath))
|
|
644
|
+
return false;
|
|
645
|
+
// 注入点 A: handleKeyDown
|
|
646
|
+
// 匹配 return;if(key===Enter&&!shiftKey){if(isComposing 结构
|
|
647
|
+
// 兼容 2026.4.x: return;if (无 }) 和旧版 ;return}if (有 })
|
|
648
|
+
const keydownRe = /;return\}?if\(([a-zA-Z_$][a-zA-Z0-9_$]*)\.key===`Enter`&&!\1\.shiftKey\)\{if\(\1\.isComposing/;
|
|
649
|
+
const keydownMatch = content.match(keydownRe);
|
|
650
|
+
if (!keydownMatch) {
|
|
651
|
+
logger.warn('[ui-ext:regex] mention-hooks: keydown anchor not found');
|
|
652
|
+
return false;
|
|
653
|
+
}
|
|
654
|
+
const eventParam = keydownMatch[1];
|
|
655
|
+
const keydownInsertIdx = content.indexOf(keydownMatch[0]) + keydownMatch[0].indexOf('if(');
|
|
656
|
+
const markerA = makeMarker(this.id);
|
|
657
|
+
const hookA = `${markerA}if(window.__clMention?.onKeyDown(${eventParam}))return;`;
|
|
658
|
+
content = content.substring(0, keydownInsertIdx) + hookA + content.substring(keydownInsertIdx);
|
|
659
|
+
// 注入点 B: handleInput — 支持两种形态
|
|
660
|
+
// Shape A (old): .onDraftChange(target.value)}}
|
|
661
|
+
// Shape B (4.11/latest): .onDraftChange(target.value) (在 arrow function 内,无 }})
|
|
662
|
+
const inputReA = /\.onDraftChange\(([a-zA-Z_$][a-zA-Z0-9_$]*)\.value\)\}\}/;
|
|
663
|
+
const inputReB = /\.onDraftChange\(([a-zA-Z_$][a-zA-Z0-9_$]*)\.value\)/;
|
|
664
|
+
let inputMatch = content.match(inputReA);
|
|
665
|
+
let inputShape = 'A';
|
|
666
|
+
if (!inputMatch) {
|
|
667
|
+
inputMatch = content.match(inputReB);
|
|
668
|
+
inputShape = 'B';
|
|
669
|
+
}
|
|
670
|
+
if (!inputMatch) {
|
|
671
|
+
logger.warn('[ui-ext:regex] mention-hooks: input anchor not found (neither Shape A nor B)');
|
|
672
|
+
return false;
|
|
673
|
+
}
|
|
674
|
+
const targetVar = inputMatch[1];
|
|
675
|
+
const inputAnchorIdx = content.indexOf(inputMatch[0]);
|
|
676
|
+
// 计算 onDraftChange(x.value) 调用结束位置
|
|
677
|
+
const draftCallEnd = inputShape === 'A'
|
|
678
|
+
? inputAnchorIdx + inputMatch[0].length - 2 // 在 }} 之前
|
|
679
|
+
: inputAnchorIdx + inputMatch[0].length; // 在 ) 之后
|
|
680
|
+
const hookB = `;window.__clMention?.onInput?.(${targetVar})`;
|
|
681
|
+
content = content.substring(0, draftCallEnd) + hookB + content.substring(draftCallEnd);
|
|
682
|
+
writeFileSync(bundlePath, content, 'utf-8');
|
|
683
|
+
logger.info(`[ui-ext:regex] mention-hooks: injected (event=${eventParam}, target=${targetVar})`);
|
|
684
|
+
return true;
|
|
685
|
+
},
|
|
686
|
+
revert(controlUiDir) {
|
|
687
|
+
const bundlePath = resolveBundlePath(controlUiDir);
|
|
688
|
+
if (bundlePath) {
|
|
689
|
+
restoreFile(bundlePath);
|
|
690
|
+
}
|
|
691
|
+
},
|
|
692
|
+
};
|
|
693
|
+
// ── Registry ─────────────────────────────────────────────────────────────
|
|
694
|
+
export const UI_EXTENSIONS = [
|
|
695
|
+
overrideScript,
|
|
696
|
+
htmlScriptTag,
|
|
697
|
+
toolcardRender,
|
|
698
|
+
toolcallidPropagate,
|
|
699
|
+
remoteAgentMessageShortCircuit,
|
|
700
|
+
mentionHooks,
|
|
701
|
+
];
|