@core-workspace/infoflow-openclaw-plugin 2026.3.8 → 2026.3.27-beta.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 (55) hide show
  1. package/CHANGELOG.md +91 -0
  2. package/CLAUDE.md +135 -0
  3. package/COLLABORATION_REPORT.md +209 -0
  4. package/PROJECT_GUIDE.md +355 -0
  5. package/README.md +158 -66
  6. package/docs/dev-guide.md +63 -50
  7. package/docs/qa-feature-list.md +452 -0
  8. package/docs/webhook-guide.md +178 -0
  9. package/index.ts +28 -2
  10. package/openclaw.plugin.json +131 -21
  11. package/package.json +20 -3
  12. package/scripts/deploy.sh +66 -7
  13. package/scripts/postinstall.cjs +80 -0
  14. package/skills/infoflow-dev/SKILL.md +2 -2
  15. package/skills/infoflow-dev/references/api.md +1 -1
  16. package/src/adapter/inbound/webhook-parser.ts +27 -5
  17. package/src/adapter/inbound/ws-receiver.ts +304 -43
  18. package/src/adapter/outbound/markdown-local-images.ts +80 -0
  19. package/src/adapter/outbound/reply-dispatcher.ts +146 -65
  20. package/src/adapter/outbound/target-resolver.ts +4 -3
  21. package/src/channel/accounts.ts +97 -22
  22. package/src/channel/channel.ts +456 -12
  23. package/src/channel/media.ts +20 -6
  24. package/src/channel/monitor.ts +8 -3
  25. package/src/channel/outbound.ts +358 -21
  26. package/src/channel/streaming.ts +740 -0
  27. package/src/commands/changelog.ts +80 -0
  28. package/src/commands/doctor.ts +545 -0
  29. package/src/commands/logs.ts +449 -0
  30. package/src/commands/version.ts +20 -0
  31. package/src/compat/openclaw-sdk.ts +218 -0
  32. package/src/handler/message-handler.ts +673 -166
  33. package/src/logging.ts +1 -1
  34. package/src/runtime.ts +1 -1
  35. package/src/security/dm-policy.ts +1 -4
  36. package/src/security/group-policy.ts +174 -51
  37. package/src/tools/actions/index.ts +15 -13
  38. package/src/tools/cron/relay.ts +1154 -0
  39. package/src/tools/hooks/index.ts +13 -1
  40. package/src/tools/index.ts +714 -32
  41. package/src/types.ts +144 -25
  42. package/src/utils/audio/g722/dct_tables.ts +381 -0
  43. package/src/utils/audio/g722/decoder.ts +919 -0
  44. package/src/utils/audio/g722/defs.ts +105 -0
  45. package/src/utils/audio/g722/hd-parser.ts +247 -0
  46. package/src/utils/audio/g722/huff_tables.ts +240 -0
  47. package/src/utils/audio/g722/index.ts +78 -0
  48. package/src/utils/audio/g722/output_decoded.pcm +0 -0
  49. package/src/utils/audio/g722/output_decoded.wav +0 -0
  50. package/src/utils/audio/g722/tables.ts +173 -0
  51. package/src/utils/audio/g722/test_api.ts +31 -0
  52. package/src/utils/audio/g722/test_voice.hd +0 -0
  53. package/src/utils/bos/im-bos-client.ts +219 -0
  54. package/src/utils/group-agent-cache.ts +142 -0
  55. package/src/utils/token-adapter.ts +120 -51
@@ -0,0 +1,78 @@
1
+ /**
2
+ * G.722.1 音频解码器模块
3
+ * 导出所有公共API
4
+ */
5
+
6
+ import { readFileSync, writeFileSync } from 'node:fs';
7
+ import { G722Type } from './defs.js';
8
+ import { decodeHdFile, pcmToWav } from './hd-parser.js';
9
+
10
+ export * from './defs.js';
11
+ export * from './decoder.js';
12
+ export * from './hd-parser.js';
13
+
14
+ /**
15
+ * 解码HD文件并保存为WAV格式
16
+ * @param hdFilePath - 输入的HD文件路径
17
+ * @param wavFilePath - 输出的WAV文件路径
18
+ * @param codecType - 编解码器类型,默认为 G722_1_16
19
+ * @returns 解码结果信息,失败返回null
20
+ */
21
+ export function decodeHdToWav(
22
+ hdFilePath: string,
23
+ wavFilePath: string,
24
+ codecType: G722Type = G722Type.G722_1_16
25
+ ): { sampleRate: number; frames: number; samples: number; duration: number } | null {
26
+ // 读取HD文件
27
+ const hdData = readFileSync(hdFilePath);
28
+
29
+ // 解码
30
+ const result = decodeHdFile(new Uint8Array(hdData), codecType);
31
+ if (!result) {
32
+ return null;
33
+ }
34
+
35
+ // 转换为WAV并写入文件
36
+ const wavData = pcmToWav(result.pcmData, result.sampleRate);
37
+ writeFileSync(wavFilePath, wavData);
38
+
39
+ return {
40
+ sampleRate: result.sampleRate,
41
+ frames: result.frames,
42
+ samples: result.pcmData.length,
43
+ duration: result.pcmData.length / result.sampleRate
44
+ };
45
+ }
46
+
47
+ /**
48
+ * 解码HD文件并保存为PCM格式(原始16-bit little-endian采样数据)
49
+ * @param hdFilePath - 输入的HD文件路径
50
+ * @param pcmFilePath - 输出的PCM文件路径
51
+ * @param codecType - 编解码器类型,默认为 G722_1_16
52
+ * @returns 解码结果信息,失败返回null
53
+ */
54
+ export function decodeHdToPcm(
55
+ hdFilePath: string,
56
+ pcmFilePath: string,
57
+ codecType: G722Type = G722Type.G722_1_16
58
+ ): { sampleRate: number; frames: number; samples: number; duration: number } | null {
59
+ // 读取HD文件
60
+ const hdData = readFileSync(hdFilePath);
61
+
62
+ // 解码
63
+ const result = decodeHdFile(new Uint8Array(hdData), codecType);
64
+ if (!result) {
65
+ return null;
66
+ }
67
+
68
+ // 写入PCM文件
69
+ const pcmBuffer = Buffer.from(result.pcmData.buffer, result.pcmData.byteOffset, result.pcmData.byteLength);
70
+ writeFileSync(pcmFilePath, pcmBuffer);
71
+
72
+ return {
73
+ sampleRate: result.sampleRate,
74
+ frames: result.frames,
75
+ samples: result.pcmData.length,
76
+ duration: result.pcmData.length / result.sampleRate
77
+ };
78
+ }
@@ -0,0 +1,173 @@
1
+ /**
2
+ * G.722.1 Decoder Tables
3
+ * 窗函数、DCT表、霍夫曼表等
4
+ */
5
+
6
+ // 7kHz带宽窗函数 (320 samples)
7
+ export const g722Window7kHz: Float32Array = new Float32Array([
8
+ 2.454367e-03, 7.363042e-03, 1.227154e-02, 1.717974e-02, 2.208753e-02, 2.699478e-02, 3.190139e-02, 3.680722e-02,
9
+ 4.171217e-02, 4.661612e-02, 5.151894e-02, 5.642051e-02, 6.132074e-02, 6.621948e-02, 7.111662e-02, 7.601206e-02,
10
+ 8.090566e-02, 8.579732e-02, 9.068689e-02, 9.557429e-02, 1.004594e-01, 1.053421e-01, 1.102222e-01, 1.150997e-01,
11
+ 1.199744e-01, 1.248462e-01, 1.297150e-01, 1.345807e-01, 1.394431e-01, 1.443022e-01, 1.491578e-01, 1.540098e-01,
12
+ 1.588582e-01, 1.637026e-01, 1.685432e-01, 1.733796e-01, 1.782119e-01, 1.830399e-01, 1.878635e-01, 1.926825e-01,
13
+ 1.974969e-01, 2.023066e-01, 2.071114e-01, 2.119112e-01, 2.167058e-01, 2.214953e-01, 2.262794e-01, 2.310581e-01,
14
+ 2.358312e-01, 2.405986e-01, 2.453603e-01, 2.501160e-01, 2.548657e-01, 2.596092e-01, 2.643465e-01, 2.690774e-01,
15
+ 2.738018e-01, 2.785197e-01, 2.832308e-01, 2.879351e-01, 2.926325e-01, 2.973228e-01, 3.020060e-01, 3.066818e-01,
16
+ 3.113503e-01, 3.160113e-01, 3.206646e-01, 3.253103e-01, 3.299480e-01, 3.345779e-01, 3.391997e-01, 3.438134e-01,
17
+ 3.484187e-01, 3.530156e-01, 3.576041e-01, 3.621838e-01, 3.667550e-01, 3.713172e-01, 3.758705e-01, 3.804147e-01,
18
+ 3.849498e-01, 3.894756e-01, 3.939921e-01, 3.984990e-01, 4.029963e-01, 4.074839e-01, 4.119617e-01, 4.164296e-01,
19
+ 4.208874e-01, 4.253351e-01, 4.297726e-01, 4.341996e-01, 4.386162e-01, 4.430223e-01, 4.474177e-01, 4.518022e-01,
20
+ 4.561760e-01, 4.605387e-01, 4.648904e-01, 4.692307e-01, 4.735598e-01, 4.778776e-01, 4.821838e-01, 4.864783e-01,
21
+ 4.907612e-01, 4.950323e-01, 4.992913e-01, 5.035384e-01, 5.077733e-01, 5.119960e-01, 5.162064e-01, 5.204043e-01,
22
+ 5.245896e-01, 5.287624e-01, 5.329224e-01, 5.370696e-01, 5.412039e-01, 5.453250e-01, 5.494330e-01, 5.535278e-01,
23
+ 5.576093e-01, 5.616774e-01, 5.657318e-01, 5.697727e-01, 5.737998e-01, 5.778131e-01, 5.818125e-01, 5.857978e-01,
24
+ 5.897691e-01, 5.937262e-01, 5.976688e-01, 6.015972e-01, 6.055111e-01, 6.094102e-01, 6.132948e-01, 6.171647e-01,
25
+ 6.210196e-01, 6.248595e-01, 6.286844e-01, 6.324941e-01, 6.362886e-01, 6.400678e-01, 6.438315e-01, 6.475798e-01,
26
+ 6.513124e-01, 6.550294e-01, 6.587305e-01, 6.624158e-01, 6.660851e-01, 6.697383e-01, 6.733754e-01, 6.769964e-01,
27
+ 6.806009e-01, 6.841892e-01, 6.877609e-01, 6.913160e-01, 6.948545e-01, 6.983762e-01, 7.018812e-01, 7.053691e-01,
28
+ 7.088401e-01, 7.122941e-01, 7.157309e-01, 7.191503e-01, 7.225525e-01, 7.259373e-01, 7.293046e-01, 7.326543e-01,
29
+ 7.359864e-01, 7.393007e-01, 7.425972e-01, 7.458757e-01, 7.491364e-01, 7.523790e-01, 7.556035e-01, 7.588098e-01,
30
+ 7.619976e-01, 7.651672e-01, 7.683185e-01, 7.714511e-01, 7.745652e-01, 7.776606e-01, 7.807373e-01, 7.837951e-01,
31
+ 7.868340e-01, 7.898540e-01, 7.928551e-01, 7.958369e-01, 7.987996e-01, 8.017431e-01, 8.046672e-01, 8.075719e-01,
32
+ 8.104571e-01, 8.133229e-01, 8.161692e-01, 8.189957e-01, 8.218024e-01, 8.245893e-01, 8.273564e-01, 8.301036e-01,
33
+ 8.328306e-01, 8.355377e-01, 8.382248e-01, 8.408916e-01, 8.435379e-01, 8.461641e-01, 8.487699e-01, 8.513552e-01,
34
+ 8.539200e-01, 8.564642e-01, 8.589878e-01, 8.614907e-01, 8.639729e-01, 8.664342e-01, 8.688745e-01, 8.712941e-01,
35
+ 8.736927e-01, 8.760701e-01, 8.784265e-01, 8.807617e-01, 8.830756e-01, 8.853683e-01, 8.876396e-01, 8.898896e-01,
36
+ 8.921182e-01, 8.943251e-01, 8.965106e-01, 8.986744e-01, 9.008167e-01, 9.029371e-01, 9.050359e-01, 9.071130e-01,
37
+ 9.091681e-01, 9.112011e-01, 9.132124e-01, 9.152017e-01, 9.171688e-01, 9.191139e-01, 9.210368e-01, 9.229375e-01,
38
+ 9.248160e-01, 9.266722e-01, 9.285061e-01, 9.303177e-01, 9.321068e-01, 9.338733e-01, 9.356174e-01, 9.373389e-01,
39
+ 9.390381e-01, 9.407144e-01, 9.423681e-01, 9.439991e-01, 9.456074e-01, 9.471928e-01, 9.487555e-01, 9.502953e-01,
40
+ 9.518121e-01, 9.533060e-01, 9.547770e-01, 9.562250e-01, 9.576499e-01, 9.590518e-01, 9.604305e-01, 9.617861e-01,
41
+ 9.631186e-01, 9.644278e-01, 9.657138e-01, 9.669764e-01, 9.682158e-01, 9.694319e-01, 9.706247e-01, 9.717940e-01,
42
+ 9.729399e-01, 9.740624e-01, 9.751615e-01, 9.762369e-01, 9.772890e-01, 9.783174e-01, 9.793222e-01, 9.803036e-01,
43
+ 9.812612e-01, 9.821951e-01, 9.831055e-01, 9.839922e-01, 9.848551e-01, 9.856943e-01, 9.865097e-01, 9.873014e-01,
44
+ 9.880693e-01, 9.888133e-01, 9.895336e-01, 9.902301e-01, 9.909026e-01, 9.915513e-01, 9.921762e-01, 9.927770e-01,
45
+ 9.933539e-01, 9.939070e-01, 9.944361e-01, 9.949412e-01, 9.954223e-01, 9.958794e-01, 9.963125e-01, 9.967218e-01,
46
+ 9.971069e-01, 9.974681e-01, 9.978050e-01, 9.981182e-01, 9.984072e-01, 9.986719e-01, 9.989129e-01, 9.991297e-01,
47
+ 9.993224e-01, 9.994911e-01, 9.996356e-01, 9.997561e-01, 9.998524e-01, 9.999247e-01, 9.999729e-01, 9.999970e-01
48
+ ]);
49
+
50
+ // 14kHz带宽窗函数 (640 samples)
51
+ export const g722Window14kHz: Float32Array = new Float32Array([
52
+ 1.227184e-03, 3.681545e-03, 6.135885e-03, 8.590187e-03, 1.104444e-02, 1.349862e-02, 1.595272e-02, 1.840673e-02,
53
+ 2.086062e-02, 2.331439e-02, 2.576803e-02, 2.822150e-02, 3.067480e-02, 3.312792e-02, 3.558084e-02, 3.803354e-02,
54
+ 4.048602e-02, 4.293826e-02, 4.539022e-02, 4.784193e-02, 5.029335e-02, 5.274445e-02, 5.519524e-02, 5.764570e-02,
55
+ 6.009582e-02, 6.254557e-02, 6.499493e-02, 6.744391e-02, 6.989249e-02, 7.234065e-02, 7.478837e-02, 7.723563e-02,
56
+ 7.968244e-02, 8.212876e-02, 8.457459e-02, 8.701991e-02, 8.946470e-02, 9.190895e-02, 9.435266e-02, 9.679579e-02,
57
+ 9.923834e-02, 1.016803e-01, 1.041216e-01, 1.065623e-01, 1.090024e-01, 1.114418e-01, 1.138806e-01, 1.163186e-01,
58
+ 1.187560e-01, 1.211926e-01, 1.236285e-01, 1.260637e-01, 1.284981e-01, 1.309317e-01, 1.333646e-01, 1.357966e-01,
59
+ 1.382278e-01, 1.406583e-01, 1.430878e-01, 1.455165e-01, 1.479443e-01, 1.503712e-01, 1.527972e-01, 1.552223e-01,
60
+ 1.576464e-01, 1.600696e-01, 1.624919e-01, 1.649131e-01, 1.673334e-01, 1.697526e-01, 1.721709e-01, 1.745881e-01,
61
+ 1.770042e-01, 1.794193e-01, 1.818333e-01, 1.842462e-01, 1.866580e-01, 1.890687e-01, 1.914782e-01, 1.938866e-01,
62
+ 1.962938e-01, 1.986998e-01, 2.011046e-01, 2.035083e-01, 2.059106e-01, 2.083118e-01, 2.107117e-01, 2.131103e-01,
63
+ 2.155076e-01, 2.179037e-01, 2.202984e-01, 2.226919e-01, 2.250839e-01, 2.274746e-01, 2.298640e-01, 2.322519e-01,
64
+ 2.346385e-01, 2.370236e-01, 2.394073e-01, 2.417896e-01, 2.441704e-01, 2.465498e-01, 2.489276e-01, 2.513039e-01,
65
+ 2.536788e-01, 2.560521e-01, 2.584239e-01, 2.607941e-01, 2.631628e-01, 2.655298e-01, 2.678953e-01, 2.702591e-01,
66
+ 2.726214e-01, 2.749819e-01, 2.773409e-01, 2.796981e-01, 2.820537e-01, 2.844075e-01, 2.867597e-01, 2.891101e-01,
67
+ 2.914588e-01, 2.938057e-01, 2.961509e-01, 2.984942e-01, 3.008359e-01, 3.031756e-01, 3.055135e-01, 3.078496e-01,
68
+ 3.101839e-01, 3.125163e-01, 3.148467e-01, 3.171753e-01, 3.195020e-01, 3.218267e-01, 3.241496e-01, 3.264705e-01,
69
+ 3.287894e-01, 3.311063e-01, 3.334212e-01, 3.357342e-01, 3.380450e-01, 3.403539e-01, 3.426607e-01, 3.449655e-01,
70
+ 3.472681e-01, 3.495687e-01, 3.518672e-01, 3.541635e-01, 3.564577e-01, 3.587498e-01, 3.610397e-01, 3.633275e-01,
71
+ 3.656130e-01, 3.678964e-01, 3.701774e-01, 3.724563e-01, 3.747330e-01, 3.770074e-01, 3.792795e-01, 3.815494e-01,
72
+ 3.838170e-01, 3.860822e-01, 3.883450e-01, 3.906056e-01, 3.928638e-01, 3.951196e-01, 3.973732e-01, 3.996242e-01,
73
+ 4.018729e-01, 4.041191e-01, 4.063629e-01, 4.086043e-01, 4.108432e-01, 4.130796e-01, 4.153135e-01, 4.175450e-01,
74
+ 4.197739e-01, 4.220002e-01, 4.242241e-01, 4.264454e-01, 4.286641e-01, 4.308803e-01, 4.330938e-01, 4.353047e-01,
75
+ 4.375130e-01, 4.397187e-01, 4.419218e-01, 4.441221e-01, 4.463198e-01, 4.485148e-01, 4.507071e-01, 4.528967e-01,
76
+ 4.550836e-01, 4.572677e-01, 4.594491e-01, 4.616277e-01, 4.638035e-01, 4.659765e-01, 4.681467e-01, 4.703141e-01,
77
+ 4.724786e-01, 4.746404e-01, 4.767992e-01, 4.789552e-01, 4.811083e-01, 4.832585e-01, 4.854058e-01, 4.875501e-01,
78
+ 4.896915e-01, 4.918301e-01, 4.939656e-01, 4.960981e-01, 4.982276e-01, 5.003542e-01, 5.024777e-01, 5.045983e-01,
79
+ 5.067158e-01, 5.088301e-01, 5.109415e-01, 5.130498e-01, 5.151549e-01, 5.172570e-01, 5.193560e-01, 5.214519e-01,
80
+ 5.235445e-01, 5.256340e-01, 5.277205e-01, 5.298037e-01, 5.318836e-01, 5.339604e-01, 5.360340e-01, 5.381044e-01,
81
+ 5.401715e-01, 5.422353e-01, 5.442959e-01, 5.463533e-01, 5.484072e-01, 5.504580e-01, 5.525054e-01, 5.545494e-01,
82
+ 5.565901e-01, 5.586276e-01, 5.606616e-01, 5.626922e-01, 5.647194e-01, 5.667433e-01, 5.687637e-01, 5.707807e-01,
83
+ 5.727943e-01, 5.748044e-01, 5.768111e-01, 5.788143e-01, 5.808139e-01, 5.828102e-01, 5.848028e-01, 5.867920e-01,
84
+ 5.887776e-01, 5.907598e-01, 5.927382e-01, 5.947132e-01, 5.966845e-01, 5.986523e-01, 6.006165e-01, 6.025770e-01,
85
+ 6.045340e-01, 6.064872e-01, 6.084368e-01, 6.103828e-01, 6.123251e-01, 6.142636e-01, 6.161986e-01, 6.181298e-01,
86
+ 6.200572e-01, 6.219809e-01, 6.239009e-01, 6.258171e-01, 6.277296e-01, 6.296382e-01, 6.315432e-01, 6.334442e-01,
87
+ 6.353414e-01, 6.372349e-01, 6.391244e-01, 6.410102e-01, 6.428920e-01, 6.447701e-01, 6.466442e-01, 6.485143e-01,
88
+ 6.503807e-01, 6.522431e-01, 6.541016e-01, 6.559562e-01, 6.578067e-01, 6.596534e-01, 6.614959e-01, 6.633346e-01,
89
+ 6.651692e-01, 6.670000e-01, 6.688265e-01, 6.706491e-01, 6.724678e-01, 6.742823e-01, 6.760926e-01, 6.778990e-01,
90
+ 6.797014e-01, 6.814995e-01, 6.832937e-01, 6.850837e-01, 6.868695e-01, 6.886512e-01, 6.904288e-01, 6.922022e-01,
91
+ 6.939714e-01, 6.957366e-01, 6.974975e-01, 6.992541e-01, 7.010065e-01, 7.027547e-01, 7.044987e-01, 7.062385e-01,
92
+ 7.079740e-01, 7.097053e-01, 7.114322e-01, 7.131548e-01, 7.148733e-01, 7.165873e-01, 7.182971e-01, 7.200026e-01,
93
+ 7.217036e-01, 7.234003e-01, 7.250928e-01, 7.267807e-01, 7.284644e-01, 7.301437e-01, 7.318186e-01, 7.334889e-01,
94
+ 7.351550e-01, 7.368166e-01, 7.384738e-01, 7.401264e-01, 7.417747e-01, 7.434185e-01, 7.450578e-01, 7.466926e-01,
95
+ 7.483229e-01, 7.499487e-01, 7.515700e-01, 7.531868e-01, 7.547991e-01, 7.564067e-01, 7.580098e-01, 7.596085e-01,
96
+ 7.612024e-01, 7.627917e-01, 7.643766e-01, 7.659568e-01, 7.675323e-01, 7.691033e-01, 7.706697e-01, 7.722313e-01,
97
+ 7.737884e-01, 7.753407e-01, 7.768885e-01, 7.784315e-01, 7.799699e-01, 7.815035e-01, 7.830324e-01, 7.845566e-01,
98
+ 7.860761e-01, 7.875909e-01, 7.891009e-01, 7.906061e-01, 7.921066e-01, 7.936022e-01, 7.950933e-01, 7.965794e-01,
99
+ 7.980608e-01, 7.995373e-01, 8.010090e-01, 8.024758e-01, 8.039380e-01, 8.053951e-01, 8.068475e-01, 8.082951e-01,
100
+ 8.097378e-01, 8.111754e-01, 8.126083e-01, 8.140363e-01, 8.154594e-01, 8.168776e-01, 8.182908e-01, 8.196992e-01,
101
+ 8.211024e-01, 8.225010e-01, 8.238944e-01, 8.252829e-01, 8.266665e-01, 8.280450e-01, 8.294187e-01, 8.307873e-01,
102
+ 8.321507e-01, 8.335093e-01, 8.348629e-01, 8.362114e-01, 8.375549e-01, 8.388933e-01, 8.402267e-01, 8.415550e-01,
103
+ 8.428782e-01, 8.441964e-01, 8.455094e-01, 8.468174e-01, 8.481204e-01, 8.494181e-01, 8.507108e-01, 8.519983e-01,
104
+ 8.532807e-01, 8.545579e-01, 8.558301e-01, 8.570971e-01, 8.583588e-01, 8.596155e-01, 8.608669e-01, 8.621132e-01,
105
+ 8.633543e-01, 8.645901e-01, 8.658207e-01, 8.670463e-01, 8.682664e-01, 8.694815e-01, 8.706912e-01, 8.718957e-01,
106
+ 8.730950e-01, 8.742889e-01, 8.754777e-01, 8.766612e-01, 8.778394e-01, 8.790122e-01, 8.801798e-01, 8.813421e-01,
107
+ 8.824991e-01, 8.836508e-01, 8.847971e-01, 8.859381e-01, 8.870737e-01, 8.882041e-01, 8.893291e-01, 8.904487e-01,
108
+ 8.915630e-01, 8.926719e-01, 8.937754e-01, 8.948735e-01, 8.959663e-01, 8.970535e-01, 8.981355e-01, 8.992120e-01,
109
+ 9.002831e-01, 9.013489e-01, 9.024092e-01, 9.034639e-01, 9.045133e-01, 9.055573e-01, 9.065958e-01, 9.076288e-01,
110
+ 9.086562e-01, 9.096783e-01, 9.106949e-01, 9.117061e-01, 9.127117e-01, 9.137116e-01, 9.147064e-01, 9.156954e-01,
111
+ 9.166791e-01, 9.176571e-01, 9.186296e-01, 9.195966e-01, 9.205582e-01, 9.215139e-01, 9.224645e-01, 9.234092e-01,
112
+ 9.243484e-01, 9.252821e-01, 9.262102e-01, 9.271328e-01, 9.280497e-01, 9.289610e-01, 9.298668e-01, 9.307670e-01,
113
+ 9.316615e-01, 9.325504e-01, 9.334337e-01, 9.343114e-01, 9.351835e-01, 9.360499e-01, 9.369107e-01, 9.377659e-01,
114
+ 9.386153e-01, 9.394591e-01, 9.402975e-01, 9.411299e-01, 9.419568e-01, 9.427779e-01, 9.435935e-01, 9.444033e-01,
115
+ 9.452074e-01, 9.460059e-01, 9.467985e-01, 9.475855e-01, 9.483669e-01, 9.491426e-01, 9.499125e-01, 9.506765e-01,
116
+ 9.514350e-01, 9.521877e-01, 9.529348e-01, 9.536759e-01, 9.544115e-01, 9.551412e-01, 9.558652e-01, 9.565833e-01,
117
+ 9.572958e-01, 9.580025e-01, 9.587034e-01, 9.593986e-01, 9.600880e-01, 9.607716e-01, 9.614494e-01, 9.621214e-01,
118
+ 9.627876e-01, 9.634480e-01, 9.641026e-01, 9.647514e-01, 9.653944e-01, 9.660316e-01, 9.666629e-01, 9.672886e-01,
119
+ 9.679081e-01, 9.685222e-01, 9.691301e-01, 9.697324e-01, 9.703287e-01, 9.709192e-01, 9.715040e-01, 9.720827e-01,
120
+ 9.726557e-01, 9.732227e-01, 9.737841e-01, 9.743394e-01, 9.748889e-01, 9.754326e-01, 9.759703e-01, 9.765021e-01,
121
+ 9.770281e-01, 9.775482e-01, 9.780625e-01, 9.785708e-01, 9.790733e-01, 9.795697e-01, 9.800604e-01, 9.805451e-01,
122
+ 9.810239e-01, 9.814969e-01, 9.819638e-01, 9.824249e-01, 9.828801e-01, 9.833294e-01, 9.837727e-01, 9.842100e-01,
123
+ 9.846416e-01, 9.850671e-01, 9.854867e-01, 9.859003e-01, 9.863081e-01, 9.867099e-01, 9.871058e-01, 9.874956e-01,
124
+ 9.878796e-01, 9.882575e-01, 9.886296e-01, 9.889956e-01, 9.893559e-01, 9.897099e-01, 9.900581e-01, 9.904005e-01,
125
+ 9.907368e-01, 9.910671e-01, 9.913914e-01, 9.917096e-01, 9.920221e-01, 9.923285e-01, 9.926291e-01, 9.929235e-01,
126
+ 9.932119e-01, 9.934944e-01, 9.937710e-01, 9.940415e-01, 9.943060e-01, 9.945645e-01, 9.948172e-01, 9.950637e-01,
127
+ 9.953042e-01, 9.955388e-01, 9.957674e-01, 9.959900e-01, 9.962067e-01, 9.964171e-01, 9.966217e-01, 9.968203e-01,
128
+ 9.970129e-01, 9.971993e-01, 9.973800e-01, 9.975546e-01, 9.977230e-01, 9.978856e-01, 9.980421e-01, 9.981926e-01,
129
+ 9.983372e-01, 9.984756e-01, 9.986080e-01, 9.987345e-01, 9.988549e-01, 9.989693e-01, 9.990777e-01, 9.991800e-01,
130
+ 9.992764e-01, 9.993669e-01, 9.994512e-01, 9.995295e-01, 9.996017e-01, 9.996679e-01, 9.997282e-01, 9.997824e-01,
131
+ 9.998305e-01, 9.998728e-01, 9.999090e-01, 9.999391e-01, 9.999631e-01, 9.999811e-01, 9.999932e-01, 9.999992e-01
132
+ ]);
133
+
134
+ // 区域标准差表
135
+ export const regionStandardDeviationTable: Float32Array = new Float32Array([
136
+ 2.441406e-04, 3.452670e-04, 4.882812e-04, 6.905340e-04, 9.765625e-04, 1.381068e-03, 1.953125e-03, 2.762136e-03,
137
+ 3.906250e-03, 5.524272e-03, 7.812500e-03, 1.104854e-02, 1.562500e-02, 2.209709e-02, 3.125000e-02, 4.419417e-02,
138
+ 6.250000e-02, 8.838835e-02, 1.250000e-01, 1.767767e-01, 2.500000e-01, 3.535534e-01, 5.000000e-01, 7.071068e-01,
139
+ 1.000000e+00, 1.414214e+00, 2.000000e+00, 2.828427e+00, 4.000000e+00, 5.656854e+00, 8.000000e+00, 1.131371e+01,
140
+ 1.600000e+01, 2.262742e+01, 3.200000e+01, 4.525483e+01, 6.400000e+01, 9.050967e+01, 1.280000e+02, 1.810193e+02,
141
+ 2.560000e+02, 3.620387e+02, 5.120000e+02, 7.240773e+02, 1.024000e+03, 1.448155e+03, 2.048000e+03, 2.896309e+03,
142
+ 4.096000e+03, 5.792619e+03, 8.192000e+03, 1.158524e+04, 1.638400e+04, 2.317047e+04, 3.276800e+04, 4.634095e+04,
143
+ 6.553600e+04, 9.268190e+04, 1.310720e+05, 1.853638e+05, 2.621440e+05, 3.707276e+05, 5.242880e+05, 7.414552e+05
144
+ ]);
145
+
146
+ // 向量维度
147
+ export const vectorDimension: Int32Array = new Int32Array([2, 2, 2, 4, 4, 5, 5, 1]);
148
+
149
+ // 向量数量
150
+ export const numberOfVectors: Int32Array = new Int32Array([10, 10, 10, 5, 5, 4, 4, 20]);
151
+
152
+ // 最大bin数
153
+ export const maxBin: Int32Array = new Int32Array([13, 9, 6, 4, 3, 2, 1, 1]);
154
+
155
+ // maxBin+1的逆
156
+ export const maxBinPlusOneInverse: Int32Array = new Int32Array([2341, 3277, 4682, 6554, 8193, 10923, 16385, 16385]);
157
+
158
+ // 期望比特表
159
+ export const expectedBitsTable: Int32Array = new Int32Array([52, 47, 43, 37, 29, 22, 16, 0]);
160
+
161
+ // region_size
162
+ export const regionSize = 20;
163
+
164
+ // MLT量化中心
165
+ export const mltQuantCentroid: Float32Array[] = [
166
+ new Float32Array([0.0, 0.392, 0.761, 1.120, 1.477, 1.832, 2.183, 2.541, 2.893, 3.245, 3.598, 3.942, 4.288, 4.724, 0.0, 0.0]),
167
+ new Float32Array([0.0, 0.544, 1.060, 1.563, 2.068, 2.571, 3.072, 3.562, 4.070, 4.620, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]),
168
+ new Float32Array([0.0, 0.746, 1.464, 2.180, 2.882, 3.584, 4.316, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]),
169
+ new Float32Array([0.0, 1.006, 2.000, 2.993, 3.985, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]),
170
+ new Float32Array([0.0, 1.321, 2.703, 3.983, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]),
171
+ new Float32Array([0.0, 1.657, 3.491, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]),
172
+ new Float32Array([0.0, 1.964, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0])
173
+ ];
@@ -0,0 +1,31 @@
1
+ /**
2
+ * G.722.1 解码器 API 测试
3
+ *
4
+ * 使用方式: npx tsx src/util/audio/g722/test_api.ts
5
+ */
6
+ import { decodeHdToWav, decodeHdToPcm } from './index.js';
7
+ import { dirname, join } from 'node:path';
8
+ import { fileURLToPath } from 'node:url';
9
+
10
+ const __filename = fileURLToPath(import.meta.url);
11
+ const __dirname = dirname(__filename);
12
+
13
+ const hdFile = join(__dirname, './test_voice.hd');
14
+
15
+ // 测试 decodeHdToWav
16
+ console.log('测试 decodeHdToWav...');
17
+ const wavResult = decodeHdToWav(
18
+ hdFile,
19
+ join(__dirname, './api_output.wav')
20
+ );
21
+ console.log('decodeHdToWav 结果:', JSON.stringify(wavResult, null, 2));
22
+
23
+ // 测试 decodeHdToPcm
24
+ console.log('\n测试 decodeHdToPcm...');
25
+ const pcmResult = decodeHdToPcm(
26
+ hdFile,
27
+ join(__dirname, './api_output.pcm')
28
+ );
29
+ console.log('decodeHdToPcm 结果:', JSON.stringify(pcmResult, null, 2));
30
+
31
+ console.log('\n测试完成!');
@@ -0,0 +1,219 @@
1
+ /**
2
+ * IM BOS Client — 调用 open-im-service 的 /im/bos/* 接口
3
+ *
4
+ * 与 client.ts (Weiyun AK/SK 签名) 不同,本模块使用如流 Bearer-token 鉴权,
5
+ * 调用部署在 open-im-service 上的 BOS 文件管理接口。
6
+ *
7
+ * 接口:
8
+ * - POST /im/bos/upload 上传文件
9
+ * - GET /im/bos/getUrl 获取预签名下载 URL
10
+ */
11
+
12
+ import { getAppAccessToken, ensureHttps } from "../../channel/outbound.js";
13
+ import { getInfoflowSendLog, formatInfoflowError, logVerbose } from "../../logging.js";
14
+
15
+ //优先使用固定域名;否则使用传入的 apiHost
16
+ const BOS_FIXED_API_HOST = "http://infoflow-open-gateway.baidu.com";
17
+
18
+ const BOS_UPLOAD_PATH = "/im/bos/upload";
19
+ const BOS_GET_URL_PATH = "/im/bos/getUrl";
20
+ const DEFAULT_UPLOAD_TIMEOUT_MS = 60_000;
21
+ const DEFAULT_GET_URL_TIMEOUT_MS = 15_000;
22
+
23
+ // ---------------------------------------------------------------------------
24
+ // Upload
25
+ // ---------------------------------------------------------------------------
26
+
27
+ export type ImBosUploadResult = {
28
+ ok: boolean;
29
+ objectKey?: string;
30
+ eTag?: string;
31
+ error?: string;
32
+ };
33
+
34
+ export async function imBosUpload(params: {
35
+ apiHost: string;
36
+ appKey: string;
37
+ appSecret: string;
38
+ fileContent: Buffer;
39
+ fileName: string;
40
+ objectKey?: string;
41
+ timeoutMs?: number;
42
+ }): Promise<ImBosUploadResult> {
43
+ const {
44
+ apiHost,
45
+ appKey,
46
+ appSecret,
47
+ fileContent,
48
+ fileName,
49
+ objectKey,
50
+ timeoutMs = DEFAULT_UPLOAD_TIMEOUT_MS,
51
+ } = params;
52
+
53
+ const log = getInfoflowSendLog();
54
+
55
+ // 1. 获取 token:始终使用传入的 apiHost,与 BOS 域名无关
56
+ const tokenResult = await getAppAccessToken({ apiHost, appKey, appSecret });
57
+ if (!tokenResult.ok || !tokenResult.token) {
58
+ log.error(`[bos:upload] token error: ${tokenResult.error}`);
59
+ return { ok: false, error: tokenResult.error ?? "failed to get token" };
60
+ }
61
+
62
+ // 2. 构建 multipart/form-data
63
+ const form = new FormData();
64
+ const blob = new Blob(
65
+ [new Uint8Array(fileContent.buffer, fileContent.byteOffset, fileContent.byteLength)],
66
+ { type: "application/octet-stream" },
67
+ );
68
+ form.append("file", blob, fileName);
69
+ if (objectKey) {
70
+ form.append("objectKey", objectKey);
71
+ }
72
+
73
+ // BOS 接口调用:有固定域名则使用固定域名,否则使用传入的 apiHost
74
+ const bosApiHost = BOS_FIXED_API_HOST || apiHost;
75
+ const url = `${ensureHttps(bosApiHost)}${BOS_UPLOAD_PATH}`;
76
+ logVerbose(
77
+ `[bos:upload] POST ${url}, file=${fileName}, size=${fileContent.length}, objectKey=${objectKey ?? "(auto)"} (using ${BOS_FIXED_API_HOST ? "fixed" : "dynamic"} host)`,
78
+ );
79
+
80
+ const controller = new AbortController();
81
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
82
+
83
+ try {
84
+ const res = await fetch(url, {
85
+ method: "POST",
86
+ headers: {
87
+ Authorization: `Bearer-${tokenResult.token}`,
88
+ },
89
+ body: form,
90
+ signal: controller.signal,
91
+ });
92
+
93
+ const responseText = await res.text();
94
+ logVerbose(`[bos:upload] response: status=${res.status}, body=${responseText.slice(0, 500)}`);
95
+
96
+ if (!res.ok) {
97
+ return { ok: false, error: `HTTP ${res.status}: ${responseText.slice(0, 200)}` };
98
+ }
99
+
100
+ const data = JSON.parse(responseText) as Record<string, unknown>;
101
+
102
+ if (data.code !== 200) {
103
+ const msg = String(data.message ?? `code=${data.code}`);
104
+ return { ok: false, error: msg };
105
+ }
106
+
107
+ const inner = data.data as Record<string, unknown> | undefined;
108
+ return {
109
+ ok: true,
110
+ objectKey: (inner?.object_key as string) ?? undefined,
111
+ eTag: (inner?.e_tag as string) ?? undefined,
112
+ };
113
+ } catch (err) {
114
+ const msg = formatInfoflowError(err);
115
+ if (msg.includes("abort") || msg.includes("signal")) {
116
+ log.error(`[bos:upload] timeout after ${timeoutMs}ms`);
117
+ return { ok: false, error: `upload timed out after ${timeoutMs}ms` };
118
+ }
119
+ log.error(`[bos:upload] exception: ${msg}`);
120
+ return { ok: false, error: msg };
121
+ } finally {
122
+ clearTimeout(timer);
123
+ }
124
+ }
125
+
126
+ // ---------------------------------------------------------------------------
127
+ // Get pre-signed URL
128
+ // ---------------------------------------------------------------------------
129
+
130
+ export type ImBosGetUrlResult = {
131
+ ok: boolean;
132
+ url?: string;
133
+ expirationSeconds?: number;
134
+ error?: string;
135
+ };
136
+
137
+ export async function imBosGetUrl(params: {
138
+ apiHost: string;
139
+ appKey: string;
140
+ appSecret: string;
141
+ objectKey: string;
142
+ expirationSeconds?: number;
143
+ timeoutMs?: number;
144
+ }): Promise<ImBosGetUrlResult> {
145
+ const {
146
+ apiHost,
147
+ appKey,
148
+ appSecret,
149
+ objectKey,
150
+ expirationSeconds = 3600,
151
+ timeoutMs = DEFAULT_GET_URL_TIMEOUT_MS,
152
+ } = params;
153
+
154
+ const log = getInfoflowSendLog();
155
+
156
+ // 1. 获取 token:始终使用传入的 apiHost,与 BOS 域名无关
157
+ const tokenResult = await getAppAccessToken({ apiHost, appKey, appSecret });
158
+ if (!tokenResult.ok || !tokenResult.token) {
159
+ log.error(`[bos:getUrl] token error: ${tokenResult.error}`);
160
+ return { ok: false, error: tokenResult.error ?? "failed to get token" };
161
+ }
162
+
163
+ // 2. 构建 GET 请求:BOS 接口调用使用固定域名(若有),否则用 apiHost
164
+ const bosApiHost = BOS_FIXED_API_HOST || apiHost;
165
+ const baseUrl = `${ensureHttps(bosApiHost)}${BOS_GET_URL_PATH}`;
166
+ const queryParams = new URLSearchParams({
167
+ objectKey,
168
+ expirationSeconds: String(expirationSeconds),
169
+ });
170
+ const url = `${baseUrl}?${queryParams.toString()}`;
171
+
172
+ logVerbose(
173
+ `[bos:getUrl] GET ${url} (using ${BOS_FIXED_API_HOST ? "fixed" : "dynamic"} bos host)`,
174
+ );
175
+
176
+ const controller = new AbortController();
177
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
178
+
179
+ try {
180
+ const res = await fetch(url, {
181
+ method: "GET",
182
+ headers: {
183
+ Authorization: `Bearer-${tokenResult.token}`,
184
+ },
185
+ signal: controller.signal,
186
+ });
187
+
188
+ const responseText = await res.text();
189
+ logVerbose(`[bos:getUrl] response: status=${res.status}, body=${responseText.slice(0, 500)}`);
190
+
191
+ if (!res.ok) {
192
+ return { ok: false, error: `HTTP ${res.status}: ${responseText.slice(0, 200)}` };
193
+ }
194
+
195
+ const data = JSON.parse(responseText) as Record<string, unknown>;
196
+
197
+ if (data.code !== 200) {
198
+ const msg = String(data.message ?? `code=${data.code}`);
199
+ return { ok: false, error: msg };
200
+ }
201
+
202
+ const inner = data.data as Record<string, unknown> | undefined;
203
+ return {
204
+ ok: true,
205
+ url: (inner?.url as string) ?? undefined,
206
+ expirationSeconds: (inner?.expiration_seconds as number) ?? expirationSeconds,
207
+ };
208
+ } catch (err) {
209
+ const msg = formatInfoflowError(err);
210
+ if (msg.includes("abort") || msg.includes("signal")) {
211
+ log.error(`[bos:getUrl] timeout after ${timeoutMs}ms`);
212
+ return { ok: false, error: `getUrl timed out after ${timeoutMs}ms` };
213
+ }
214
+ log.error(`[bos:getUrl] exception: ${msg}`);
215
+ return { ok: false, error: msg };
216
+ } finally {
217
+ clearTimeout(timer);
218
+ }
219
+ }
@@ -0,0 +1,142 @@
1
+ /**
2
+ * In-memory cache for group robot name→agentId mappings.
3
+ * Used to resolve robot names to the correct agentId for outbound @mentions.
4
+ */
5
+
6
+ import { formatInfoflowError, logVerbose } from "../logging.js";
7
+ import type { InfoflowGroupAgentInfo, ResolvedInfoflowAccount } from "../types.js";
8
+
9
+ /** 群机器人 name→agentId 缓存,key=groupId */
10
+ const groupAgentNameCache = new Map<number, Map<string, number>>();
11
+
12
+ /** 缓存更新时间戳,key=groupId */
13
+ const groupAgentCacheTimestamp = new Map<number, number>();
14
+
15
+ /** Cache TTL: 5 minutes */
16
+ const CACHE_TTL_MS = 5 * 60 * 1000;
17
+
18
+ /** Dynamically-set fetch function to avoid circular dependency with outbound.ts */
19
+ let fetchFn:
20
+ | ((params: {
21
+ account: ResolvedInfoflowAccount;
22
+ groupId: number;
23
+ }) => Promise<{ ok: boolean; agents?: InfoflowGroupAgentInfo[]; error?: string }>)
24
+ | undefined;
25
+
26
+ /** Register the fetch function (called once from outbound.ts at import time) */
27
+ export function registerGroupMemberListFetcher(fn: typeof fetchFn): void {
28
+ fetchFn = fn;
29
+ }
30
+
31
+ /**
32
+ * Returns true if the cache for a group is present and not expired.
33
+ */
34
+ export function isCacheValid(groupId: number): boolean {
35
+ const ts = groupAgentCacheTimestamp.get(groupId);
36
+ return ts != null && Date.now() - ts < CACHE_TTL_MS;
37
+ }
38
+
39
+ /**
40
+ * Updates the cache for a group with the given agent list.
41
+ */
42
+ export function updateGroupAgentCache(groupId: number, agents: InfoflowGroupAgentInfo[]): void {
43
+ const nameMap = new Map<string, number>();
44
+ for (const agent of agents) {
45
+ nameMap.set(agent.name, agent.agentId);
46
+ }
47
+ groupAgentNameCache.set(groupId, nameMap);
48
+ groupAgentCacheTimestamp.set(groupId, Date.now());
49
+ logVerbose(
50
+ `[group-agent-cache] updated groupId=${groupId}: ${agents.map((a) => `${a.name}→${a.agentId}`).join(", ")}`,
51
+ );
52
+ }
53
+
54
+ /**
55
+ * Resolves a robot's agentId by name from the group member cache.
56
+ * On cache miss, fetches the member list and retries once.
57
+ */
58
+ export async function resolveAgentIdByName(params: {
59
+ account: ResolvedInfoflowAccount;
60
+ groupId: number;
61
+ name: string;
62
+ }): Promise<number | undefined> {
63
+ const { account, groupId, name } = params;
64
+ const cached = groupAgentNameCache.get(groupId);
65
+ if (cached && isCacheValid(groupId)) {
66
+ const id = cached.get(name);
67
+ if (id !== undefined) {
68
+ logVerbose(
69
+ `[group-agent-cache] resolve: groupId=${groupId}, name="${name}" → agentId=${id} (cache hit)`,
70
+ );
71
+ return id;
72
+ }
73
+ logVerbose(
74
+ `[group-agent-cache] resolve: groupId=${groupId}, name="${name}" → miss (cached names: ${[...cached.keys()].join(", ")})`,
75
+ );
76
+ }
77
+
78
+ // Cache miss — fetch member list and rebuild cache
79
+ if (!fetchFn) return undefined;
80
+ try {
81
+ const result = await fetchFn({ account, groupId });
82
+ if (result.ok && result.agents) {
83
+ updateGroupAgentCache(groupId, result.agents);
84
+ const resolved = groupAgentNameCache.get(groupId)?.get(name);
85
+ logVerbose(
86
+ `[group-agent-cache] resolve after fetch: groupId=${groupId}, name="${name}" → ${resolved ?? "still not found"}`,
87
+ );
88
+ return resolved;
89
+ }
90
+ } catch (err) {
91
+ logVerbose(`[group-agent-cache] fetch failed: ${formatInfoflowError(err)}`);
92
+ }
93
+ return undefined;
94
+ }
95
+
96
+ /**
97
+ * Scans text for `@名称` patterns (including CJK) and replaces matched robot
98
+ * names with `@agentId` (numeric). Returns transformed text and resolved agentIds.
99
+ */
100
+ export async function replaceAgentNameMentions(params: {
101
+ text: string;
102
+ account: ResolvedInfoflowAccount;
103
+ groupId: number;
104
+ }): Promise<{ text: string; newAgentIds: number[] }> {
105
+ const { account, groupId } = params;
106
+ const newAgentIds: number[] = [];
107
+
108
+ // Match @followed-by-non-whitespace (supports CJK, letters, digits, etc.)
109
+ const mentionPattern = /@([^\s@]+)/g;
110
+ const matches: { full: string; name: string; index: number }[] = [];
111
+ let m: RegExpExecArray | null;
112
+ while ((m = mentionPattern.exec(params.text)) !== null) {
113
+ matches.push({ full: m[0], name: m[1], index: m.index });
114
+ }
115
+
116
+ if (matches.length === 0) return { text: params.text, newAgentIds };
117
+
118
+ logVerbose(
119
+ `[group-agent-cache] replaceAgentNameMentions: groupId=${groupId}, matches=[${matches.map((m) => m.name).join(", ")}]`,
120
+ );
121
+
122
+ let result = params.text;
123
+ // Process in reverse order to preserve indices during replacement
124
+ for (let i = matches.length - 1; i >= 0; i--) {
125
+ const match = matches[i];
126
+ // Skip if it already looks like a numeric ID
127
+ if (/^\d+$/.test(match.name)) continue;
128
+
129
+ const agentId = await resolveAgentIdByName({ account, groupId, name: match.name });
130
+ if (agentId !== undefined) {
131
+ result =
132
+ result.slice(0, match.index) +
133
+ `@${agentId}` +
134
+ result.slice(match.index + match.full.length);
135
+ if (!newAgentIds.includes(agentId)) {
136
+ newAgentIds.push(agentId);
137
+ }
138
+ }
139
+ }
140
+
141
+ return { text: result, newAgentIds };
142
+ }