92 lines
3.4 KiB
TypeScript
92 lines
3.4 KiB
TypeScript
const ACTIVE_STATUSES = Object.freeze(["running", "pending", "accepted", "processing"]);
|
|
const TERMINAL_STATUSES = Object.freeze(["completed", "source", "failed", "timeout", "canceled", "cancelled", "error", "blocked"]);
|
|
|
|
export function computeCodeAgentComposerState(input = {}) {
|
|
const latest = latestAgentMessage(input.messages ?? input.chatMessages);
|
|
const current = input.currentRequest && typeof input.currentRequest === "object" ? input.currentRequest : null;
|
|
const workspace = input.workspace && typeof input.workspace === "object" ? input.workspace : null;
|
|
const explicitStatus = normalizedStatus(input.status ?? input.sessionStatus ?? workspace?.sessionStatus);
|
|
const traceId = firstNonEmptyString(
|
|
input.targetTraceId,
|
|
current?.traceId,
|
|
runningTraceId(input.messages ?? input.chatMessages),
|
|
workspace?.activeTraceId,
|
|
latest?.status === "running" ? latest?.traceId : null
|
|
);
|
|
const active = Boolean(traceId) && (
|
|
activeStatus(current?.status) ||
|
|
activeStatus(latest?.status) ||
|
|
activeStatus(explicitStatus) ||
|
|
(firstNonEmptyString(input.activeTraceId, workspace?.activeTraceId) === traceId && !terminalStatus(explicitStatus)) ||
|
|
input.chatPending === true
|
|
);
|
|
const mode = active ? "steer" : "turn";
|
|
return pruneUndefined({
|
|
contract: "hwlab-code-agent-composer-policy-v1",
|
|
webEquivalent: true,
|
|
locked: false,
|
|
disabled: false,
|
|
submitMode: mode,
|
|
mode,
|
|
route: mode === "steer" ? "/v1/agent/chat/steer" : "/v1/agent/chat",
|
|
method: "POST",
|
|
targetTraceId: active ? traceId : null,
|
|
conversationId: firstNonEmptyString(input.conversationId, current?.conversationId, latest?.conversationId, workspace?.selectedConversationId),
|
|
sessionId: firstNonEmptyString(input.sessionId, current?.sessionId, latest?.sessionId, workspace?.selectedAgentSessionId),
|
|
threadId: firstNonEmptyString(input.threadId, current?.threadId, latest?.threadId, workspace?.threadId),
|
|
workspaceId: firstNonEmptyString(input.workspaceId, workspace?.workspaceId),
|
|
workspaceRevision: integerOrNull(input.workspaceRevision ?? workspace?.revision),
|
|
reason: active ? "active-turn-steer" : terminalStatus(explicitStatus) ? "terminal-turn" : "idle-turn"
|
|
});
|
|
}
|
|
|
|
export function latestAgentMessage(messages = []) {
|
|
if (!Array.isArray(messages)) return null;
|
|
for (const message of [...messages].reverse()) {
|
|
if (message?.role === "agent") return message;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function runningTraceId(messages = []) {
|
|
if (!Array.isArray(messages)) return null;
|
|
for (const message of [...messages].reverse()) {
|
|
if (message?.status !== "running") continue;
|
|
const traceId = firstNonEmptyString(message?.traceId, message?.runnerTrace?.traceId);
|
|
if (traceId) return traceId;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function activeStatus(value) {
|
|
return ACTIVE_STATUSES.includes(normalizedStatus(value));
|
|
}
|
|
|
|
function terminalStatus(value) {
|
|
return TERMINAL_STATUSES.includes(normalizedStatus(value));
|
|
}
|
|
|
|
function normalizedStatus(value) {
|
|
return String(value ?? "").trim().toLowerCase();
|
|
}
|
|
|
|
function firstNonEmptyString(...values) {
|
|
for (const value of values) {
|
|
const text = String(value ?? "").trim();
|
|
if (text) return text;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function integerOrNull(value) {
|
|
return Number.isInteger(value) ? value : null;
|
|
}
|
|
|
|
function pruneUndefined(value) {
|
|
const out = {};
|
|
for (const [key, item] of Object.entries(value)) {
|
|
if (item !== undefined) out[key] = item;
|
|
}
|
|
return out;
|
|
}
|