mirror of
https://github.com/volcengine/OpenViking.git
synced 2026-09-30 09:17:51 +08:00
Use ZCode rollout logs as the authoritative incremental source, advance capture state only for the acknowledged prefix, and persist host turn identity with the OpenViking turn_id contract. Detach Stop writes, package ZCode in the TOS marketplace artifact, add end-to-end regressions, and move the integration docs under community plugins. Co-authored-by: TRAE CLI <noreply@bytedance.com>
83 lines
2.6 KiB
JavaScript
83 lines
2.6 KiB
JavaScript
import { stableHash } from "./shared/agent-hook-runtime.mjs";
|
|
import { cleanZcodeText } from "./zcode-turns.mjs";
|
|
|
|
export function zcodeTurnDedupKey(turn) {
|
|
return turn.turnId
|
|
? `${turn.turnId}:${turn.role}`
|
|
: stableHash(turn.role, turn.content);
|
|
}
|
|
|
|
export function buildZcodeCapturePlan(turns, state = {}) {
|
|
const capturedTurnIds = new Set(
|
|
Array.isArray(state.capturedTurnIds) ? state.capturedTurnIds : [],
|
|
);
|
|
const candidates = turns.map((turn) => ({
|
|
dedupKey: zcodeTurnDedupKey(turn),
|
|
turn,
|
|
}));
|
|
const toSend = candidates.filter((item) => !capturedTurnIds.has(item.dedupKey));
|
|
const payloads = toSend.map(({ turn }) => ({
|
|
role: turn.role,
|
|
content: turn.content,
|
|
...(turn.turnId ? { turn_id: turn.turnId } : {}),
|
|
}));
|
|
return { candidates, toSend, payloads };
|
|
}
|
|
|
|
function acknowledgedCursor(candidates, acknowledged) {
|
|
let cursor = null;
|
|
let currentTurnId = null;
|
|
let currentComplete = true;
|
|
|
|
for (const item of candidates) {
|
|
const turnId = item.turn.turnId || null;
|
|
if (!turnId) continue;
|
|
if (currentTurnId !== turnId) {
|
|
if (currentTurnId && currentComplete) cursor = currentTurnId;
|
|
if (currentTurnId && !currentComplete) return cursor;
|
|
currentTurnId = turnId;
|
|
currentComplete = true;
|
|
}
|
|
if (!acknowledged.has(item.dedupKey)) currentComplete = false;
|
|
}
|
|
|
|
if (currentTurnId && currentComplete) cursor = currentTurnId;
|
|
return cursor;
|
|
}
|
|
|
|
export function applyZcodeCaptureResult(state, plan, result) {
|
|
const acknowledged = new Set(
|
|
Array.isArray(state.capturedTurnIds) ? state.capturedTurnIds : [],
|
|
);
|
|
const captured = Math.min(
|
|
plan.toSend.length,
|
|
Math.max(0, Number(result?.sent || 0) + Number(result?.queued || 0)),
|
|
);
|
|
const newlyAcknowledged = plan.toSend.slice(0, captured);
|
|
for (const item of newlyAcknowledged) acknowledged.add(item.dedupKey);
|
|
|
|
const cursor = acknowledgedCursor(plan.candidates, acknowledged);
|
|
const pendingPrompt = cleanZcodeText(state.pendingPrompt?.prompt || "");
|
|
let pendingPromptItem = null;
|
|
if (pendingPrompt) {
|
|
for (let index = plan.candidates.length - 1; index >= 0; index--) {
|
|
const item = plan.candidates[index];
|
|
if (item.turn.role === "user" && cleanZcodeText(item.turn.content) === pendingPrompt) {
|
|
pendingPromptItem = item;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
const pendingPromptAcknowledged = pendingPromptItem
|
|
? acknowledged.has(pendingPromptItem.dedupKey)
|
|
: false;
|
|
|
|
return {
|
|
...state,
|
|
capturedTurnIds: [...acknowledged].slice(-1000),
|
|
pendingPrompt: pendingPromptAcknowledged ? null : state.pendingPrompt,
|
|
lastTurnId: cursor || state.lastTurnId || null,
|
|
captured,
|
|
};
|
|
}
|