ForcePilot/web/src/utils/subagentThread.js
2026-06-05 21:02:13 +08:00

104 lines
3.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 复刻后端 yuxi/utils/subagent_thread_utils.py 的 make_child_thread_id
// child_thread_id = "subagent_" + sha256("{parent}:{slug}:{tool_call_id}")[:55]
// 子智能体由 graph.ainvoke 独立调用,流式事件不带 tool_call_id前端据此自行推算 child_thread_id。
const PREFIX = 'subagent_'
const DIGEST_LENGTH = 64 - PREFIX.length
const SHA256_K = [
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
]
const rightRotate = (value, shift) => ((value >>> shift) | (value << (32 - shift))) >>> 0
const bytesToHex = (bytes) =>
Array.from(bytes)
.map((byte) => byte.toString(16).padStart(2, '0'))
.join('')
const sha256Hex = (bytes) => {
const paddedLength = Math.ceil((bytes.length + 9) / 64) * 64
const padded = new Uint8Array(paddedLength)
padded.set(bytes)
padded[bytes.length] = 0x80
const view = new DataView(padded.buffer)
const bitLength = bytes.length * 8
view.setUint32(paddedLength - 8, Math.floor(bitLength / 0x100000000))
view.setUint32(paddedLength - 4, bitLength >>> 0)
const words = new Uint32Array(64)
const hash = [
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
]
for (let offset = 0; offset < paddedLength; offset += 64) {
for (let index = 0; index < 16; index += 1) {
words[index] = view.getUint32(offset + index * 4)
}
for (let index = 16; index < 64; index += 1) {
const s0 =
rightRotate(words[index - 15], 7) ^
rightRotate(words[index - 15], 18) ^
(words[index - 15] >>> 3)
const s1 =
rightRotate(words[index - 2], 17) ^
rightRotate(words[index - 2], 19) ^
(words[index - 2] >>> 10)
words[index] = (words[index - 16] + s0 + words[index - 7] + s1) >>> 0
}
let [a, b, c, d, e, f, g, h] = hash
for (let index = 0; index < 64; index += 1) {
const s1 = rightRotate(e, 6) ^ rightRotate(e, 11) ^ rightRotate(e, 25)
const ch = (e & f) ^ (~e & g)
const temp1 = (h + s1 + ch + SHA256_K[index] + words[index]) >>> 0
const s0 = rightRotate(a, 2) ^ rightRotate(a, 13) ^ rightRotate(a, 22)
const maj = (a & b) ^ (a & c) ^ (b & c)
const temp2 = (s0 + maj) >>> 0
h = g
g = f
f = e
e = (d + temp1) >>> 0
d = c
c = b
b = a
a = (temp1 + temp2) >>> 0
}
hash[0] = (hash[0] + a) >>> 0
hash[1] = (hash[1] + b) >>> 0
hash[2] = (hash[2] + c) >>> 0
hash[3] = (hash[3] + d) >>> 0
hash[4] = (hash[4] + e) >>> 0
hash[5] = (hash[5] + f) >>> 0
hash[6] = (hash[6] + g) >>> 0
hash[7] = (hash[7] + h) >>> 0
}
return hash.map((word) => word.toString(16).padStart(8, '0')).join('')
}
const digestSha256Hex = async (bytes) => {
const subtle = globalThis.crypto?.subtle
if (typeof subtle?.digest === 'function') {
const buffer = await subtle.digest('SHA-256', bytes)
return bytesToHex(new Uint8Array(buffer))
}
return sha256Hex(bytes)
}
export async function makeChildThreadId(parentThreadId, agentSlug, toolCallId) {
if (!parentThreadId || !agentSlug || !toolCallId) return ''
const data = new TextEncoder().encode(`${parentThreadId}:${agentSlug}:${toolCallId}`)
const hex = await digestSha256Hex(data)
return `${PREFIX}${hex.slice(0, DIGEST_LENGTH)}`
}