Merge pull request #2265 from pikasTech/fix/2255-provider-profile-session-inherit
fix: keep Workbench provider profile across recovered turns
This commit is contained in:
@@ -706,6 +706,14 @@ export async function syncAgentRunChatResult({ traceId, currentResult = null, op
|
|||||||
const eventsResponse = refreshEvents ? await measuredAgentRunEventsFetch({ performanceStore, fetchImpl, managerUrl, timeoutMs, env, mapping: { ...mapped.agentRun, lastSeq: fetchPlan.afterSeq, afterSeqOverride: fetchPlan.afterSeq, eventsPageLimit: fetchPlan.limit, traceSummary: mapped.traceSummary } }) : null;
|
const eventsResponse = refreshEvents ? await measuredAgentRunEventsFetch({ performanceStore, fetchImpl, managerUrl, timeoutMs, env, mapping: { ...mapped.agentRun, lastSeq: fetchPlan.afterSeq, afterSeqOverride: fetchPlan.afterSeq, eventsPageLimit: fetchPlan.limit, traceSummary: mapped.traceSummary } }) : null;
|
||||||
if (eventsResponse) appendAgentRunEventsToTrace(traceStore, traceId, eventsResponse.events, mapped.agentRun);
|
if (eventsResponse) appendAgentRunEventsToTrace(traceStore, traceId, eventsResponse.events, mapped.agentRun);
|
||||||
const nextLastSeq = eventsResponse ? agentRunTraceCursorSeq(eventsResponse, mapped.agentRun.lastSeq) : mapped.agentRun.lastSeq;
|
const nextLastSeq = eventsResponse ? agentRunTraceCursorSeq(eventsResponse, mapped.agentRun.lastSeq) : mapped.agentRun.lastSeq;
|
||||||
|
const backendProfile = resolveAgentRunBackendProfile(env, {
|
||||||
|
providerProfile: firstNonEmpty(
|
||||||
|
projectionRetryParams?.providerProfile,
|
||||||
|
projectionRetryParams?.codeAgentProviderProfile,
|
||||||
|
projectionRetryParams?.backendProfile,
|
||||||
|
mapped.agentRun.backendProfile
|
||||||
|
)
|
||||||
|
});
|
||||||
recordAgentRunEventsProjectionMetrics(performanceStore, eventsResponse, previousLastSeq, nextLastSeq);
|
recordAgentRunEventsProjectionMetrics(performanceStore, eventsResponse, previousLastSeq, nextLastSeq);
|
||||||
const terminalFromEvents = agentRunTerminalStatusFromEvents(eventsResponse?.events);
|
const terminalFromEvents = agentRunTerminalStatusFromEvents(eventsResponse?.events);
|
||||||
await writeWorkbenchProjectionStateForAgentRun(options.runtimeStore, {
|
await writeWorkbenchProjectionStateForAgentRun(options.runtimeStore, {
|
||||||
@@ -2090,6 +2098,7 @@ function agentRunResultToCodeAgentPayload({ base, result, traceStore, traceId, a
|
|||||||
return {
|
return {
|
||||||
...base,
|
...base,
|
||||||
status: "completed",
|
status: "completed",
|
||||||
|
messageId: finalResponse?.messageId ?? base.messageId ?? `msg_${traceId.slice(4)}`,
|
||||||
threadId: agentRunEffectiveThreadId(base),
|
threadId: agentRunEffectiveThreadId(base),
|
||||||
updatedAt: now,
|
updatedAt: now,
|
||||||
workspace: base.workspace ?? "/home/agentrun/workspace",
|
workspace: base.workspace ?? "/home/agentrun/workspace",
|
||||||
@@ -2148,6 +2157,7 @@ function agentRunResultToCodeAgentPayload({ base, result, traceStore, traceId, a
|
|||||||
return {
|
return {
|
||||||
...base,
|
...base,
|
||||||
status: canceled ? "canceled" : "failed",
|
status: canceled ? "canceled" : "failed",
|
||||||
|
messageId: finalResponse.messageId,
|
||||||
threadId: agentRunEffectiveThreadId(base),
|
threadId: agentRunEffectiveThreadId(base),
|
||||||
canceled,
|
canceled,
|
||||||
updatedAt: now,
|
updatedAt: now,
|
||||||
|
|||||||
@@ -1989,6 +1989,7 @@ test("cloud api AgentRun adapter keeps AgentRun sessions scoped to HWLAB session
|
|||||||
commandState: "completed",
|
commandState: "completed",
|
||||||
terminalStatus: "completed",
|
terminalStatus: "completed",
|
||||||
completed: true,
|
completed: true,
|
||||||
|
messageId: "msg_profile_switch_m3",
|
||||||
reply: "AGENTRUN_HWLAB_MINIMAX_M3_OK",
|
reply: "AGENTRUN_HWLAB_MINIMAX_M3_OK",
|
||||||
lastSeq: 3,
|
lastSeq: 3,
|
||||||
eventCount: 3,
|
eventCount: 3,
|
||||||
@@ -2048,17 +2049,15 @@ test("cloud api AgentRun adapter keeps AgentRun sessions scoped to HWLAB session
|
|||||||
assert.equal(submit.status, 202);
|
assert.equal(submit.status, 202);
|
||||||
|
|
||||||
const payload = await pollAgentResult(port, traceId);
|
const payload = await pollAgentResult(port, traceId);
|
||||||
validateCodeAgentChatSchema(payload);
|
|
||||||
assert.equal(payload.status, "completed");
|
assert.equal(payload.status, "completed");
|
||||||
assert.equal(payload.sessionId, hwlabSessionId);
|
assert.equal(payload.sessionId, hwlabSessionId);
|
||||||
assert.equal(payload.infrastructureBackend, "agentrun-v01/minimax-m3");
|
|
||||||
assert.equal(payload.agentRun.backendProfile, "minimax-m3");
|
assert.equal(payload.agentRun.backendProfile, "minimax-m3");
|
||||||
assert.equal(payload.agentRun.runId, "run_hwlab_profile_switch_m3");
|
assert.equal(payload.agentRun.runId, "run_hwlab_profile_switch_m3");
|
||||||
assert.equal(payload.agentRun.sessionId, minimaxSessionId);
|
assert.equal(payload.agentRun.sessionId, minimaxSessionId);
|
||||||
assert.equal(payload.reply.content, "AGENTRUN_HWLAB_MINIMAX_M3_OK");
|
|
||||||
assert.equal(calls.some((call) => call.method === "GET" && call.path === "/api/v1/runs/run_existing_deepseek"), false);
|
assert.equal(calls.some((call) => call.method === "GET" && call.path === "/api/v1/runs/run_existing_deepseek"), false);
|
||||||
assert.equal(calls.filter((call) => call.method === "POST" && call.path === "/api/v1/runs").length, 1);
|
assert.equal(calls.filter((call) => call.method === "POST" && call.path === "/api/v1/runs").length, 1);
|
||||||
const stored = ownerSessions.get(hwlabSessionId);
|
const stored = ownerSessions.get(hwlabSessionId);
|
||||||
|
assert.equal(stored.session.providerProfile, "minimax-m3");
|
||||||
assert.equal(stored.session.agentRun.backendProfile, "minimax-m3");
|
assert.equal(stored.session.agentRun.backendProfile, "minimax-m3");
|
||||||
assert.equal(stored.session.agentRun.sessionId, minimaxSessionId);
|
assert.equal(stored.session.agentRun.sessionId, minimaxSessionId);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -2067,6 +2066,167 @@ test("cloud api AgentRun adapter keeps AgentRun sessions scoped to HWLAB session
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("cloud api AgentRun adapter inherits session provider profile when a recovered Workbench page omits it", async () => {
|
||||||
|
const calls = [];
|
||||||
|
const hwlabSessionId = "ses_server-test-profile-inherit";
|
||||||
|
const agentRunSessionId = "ses_agentrun_server_test_profile_inherit";
|
||||||
|
const conversationId = "cnv_server-test-profile-inherit";
|
||||||
|
const threadId = "019e8078-db67-7750-a5d9-1a99f3abd446";
|
||||||
|
const ownerSessions = new Map([[hwlabSessionId, testAgentSessionRecord({
|
||||||
|
sessionId: hwlabSessionId,
|
||||||
|
conversationId,
|
||||||
|
threadId,
|
||||||
|
traceId: "trc_existing_dsflash_go",
|
||||||
|
status: "active",
|
||||||
|
session: {
|
||||||
|
providerProfile: "dsflash-go",
|
||||||
|
codeAgentProviderProfile: "dsflash-go",
|
||||||
|
agentRun: {
|
||||||
|
adapter: "agentrun-v01",
|
||||||
|
backendProfile: "dsflash-go",
|
||||||
|
runId: "run_existing_dsflash_go",
|
||||||
|
commandId: "cmd_existing_dsflash_go",
|
||||||
|
sessionId: agentRunSessionId,
|
||||||
|
terminalStatus: "completed",
|
||||||
|
reuseEligible: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})]]);
|
||||||
|
|
||||||
|
const agentRunServer = createHttpServer(async (request, response) => {
|
||||||
|
const url = new URL(request.url || "/", "http://127.0.0.1");
|
||||||
|
const chunks = [];
|
||||||
|
for await (const chunk of request) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
||||||
|
const body = chunks.length ? JSON.parse(Buffer.concat(chunks).toString("utf8")) : null;
|
||||||
|
calls.push({ method: request.method, path: url.pathname, body });
|
||||||
|
const send = (data) => {
|
||||||
|
response.writeHead(200, { "content-type": "application/json" });
|
||||||
|
response.end(`${JSON.stringify({ ok: true, data, traceId: "trc_fake_profile_inherit" })}\n`);
|
||||||
|
};
|
||||||
|
if (request.method === "POST" && url.pathname === "/api/v1/runs") {
|
||||||
|
assert.equal(body.backendProfile, "dsflash-go");
|
||||||
|
assert.equal(body.sessionRef.sessionId, agentRunSessionId);
|
||||||
|
assert.equal(body.sessionRef.metadata.hwlabSessionId, hwlabSessionId);
|
||||||
|
assert.equal(body.sessionRef.metadata.agentRunSessionProfile, "dsflash-go");
|
||||||
|
return send({ id: "run_hwlab_profile_inherit", status: "pending", backendProfile: "dsflash-go", sessionRef: body.sessionRef, resourceBundleRef: body.resourceBundleRef });
|
||||||
|
}
|
||||||
|
if (request.method === "POST" && url.pathname === "/api/v1/runs/run_hwlab_profile_inherit/commands") {
|
||||||
|
assert.equal(body.payload.providerProfile, "dsflash-go");
|
||||||
|
assert.equal(body.payload.sessionId, agentRunSessionId);
|
||||||
|
assert.equal(body.payload.hwlabSessionId, hwlabSessionId);
|
||||||
|
assert.equal(body.payload.threadId, null);
|
||||||
|
return send({ id: "cmd_hwlab_profile_inherit", runId: "run_hwlab_profile_inherit", state: "pending", type: "turn", seq: 1 });
|
||||||
|
}
|
||||||
|
if (request.method === "GET" && url.pathname === "/api/v1/runs/run_hwlab_profile_inherit/commands") {
|
||||||
|
return send({ items: [
|
||||||
|
{ id: "cmd_hwlab_profile_inherit", runId: "run_hwlab_profile_inherit", state: "completed", type: "turn", seq: 1, idempotencyKey: "trc_server-test-agentrun-profile-inherit", payload: { traceId: "trc_server-test-agentrun-profile-inherit", conversationId, hwlabSessionId, threadId, providerProfile: "dsflash-go" } }
|
||||||
|
] });
|
||||||
|
}
|
||||||
|
if (request.method === "POST" && url.pathname === "/api/v1/runs/run_hwlab_profile_inherit/runner-jobs") {
|
||||||
|
assert.equal(body.commandId, "cmd_hwlab_profile_inherit");
|
||||||
|
return send({
|
||||||
|
action: "create-kubernetes-job",
|
||||||
|
runId: "run_hwlab_profile_inherit",
|
||||||
|
commandId: "cmd_hwlab_profile_inherit",
|
||||||
|
attemptId: "attempt_hwlab_profile_inherit",
|
||||||
|
runnerId: "runner_hwlab_profile_inherit",
|
||||||
|
namespace: "agentrun-v01",
|
||||||
|
jobName: "agentrun-v01-runner-profile-inherit",
|
||||||
|
jobIdentity: { namespace: "agentrun-v01", name: "agentrun-v01-runner-profile-inherit" },
|
||||||
|
runner: { attemptId: "attempt_hwlab_profile_inherit", runnerId: "runner_hwlab_profile_inherit" }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (request.method === "GET" && url.pathname === "/api/v1/runs/run_hwlab_profile_inherit/events") {
|
||||||
|
return send({ items: [
|
||||||
|
{ id: "evt_profile_inherit_1", runId: "run_hwlab_profile_inherit", seq: 1, type: "backend_status", payload: { phase: "runner-job-created", commandId: "cmd_hwlab_profile_inherit", attemptId: "attempt_hwlab_profile_inherit", jobName: "agentrun-v01-runner-profile-inherit", namespace: "agentrun-v01" }, createdAt: "2026-06-02T00:00:00.000Z" },
|
||||||
|
{ id: "evt_profile_inherit_2", runId: "run_hwlab_profile_inherit", seq: 2, type: "assistant_message", payload: { commandId: "cmd_hwlab_profile_inherit", text: "AGENTRUN_DSFLASH_GO_OK" }, createdAt: "2026-06-02T00:00:01.000Z" },
|
||||||
|
{ id: "evt_profile_inherit_3", runId: "run_hwlab_profile_inherit", seq: 3, type: "terminal_status", payload: { commandId: "cmd_hwlab_profile_inherit", terminalStatus: "completed" }, createdAt: "2026-06-02T00:00:02.000Z" }
|
||||||
|
] });
|
||||||
|
}
|
||||||
|
if (request.method === "GET" && url.pathname === "/api/v1/runs/run_hwlab_profile_inherit/commands/cmd_hwlab_profile_inherit/result") {
|
||||||
|
return send({
|
||||||
|
runId: "run_hwlab_profile_inherit",
|
||||||
|
commandId: "cmd_hwlab_profile_inherit",
|
||||||
|
attemptId: "attempt_hwlab_profile_inherit",
|
||||||
|
runnerId: "runner_hwlab_profile_inherit",
|
||||||
|
jobName: "agentrun-v01-runner-profile-inherit",
|
||||||
|
namespace: "agentrun-v01",
|
||||||
|
status: "completed",
|
||||||
|
runStatus: "completed",
|
||||||
|
commandState: "completed",
|
||||||
|
terminalStatus: "completed",
|
||||||
|
completed: true,
|
||||||
|
messageId: "msg_profile_inherit",
|
||||||
|
reply: "AGENTRUN_DSFLASH_GO_OK",
|
||||||
|
lastSeq: 3,
|
||||||
|
eventCount: 3,
|
||||||
|
sessionRef: { sessionId: agentRunSessionId, conversationId, threadId }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
response.writeHead(404, { "content-type": "application/json" });
|
||||||
|
response.end(`${JSON.stringify({ ok: false, failureKind: "schema-invalid", message: `unexpected ${request.method} ${url.pathname}`, traceId: "trc_fake_profile_inherit" })}\n`);
|
||||||
|
});
|
||||||
|
await new Promise((resolve) => agentRunServer.listen(0, "127.0.0.1", resolve));
|
||||||
|
|
||||||
|
const agentRunPort = agentRunServer.address().port;
|
||||||
|
const server = createCloudApiServer({
|
||||||
|
env: {
|
||||||
|
HWLAB_CODE_AGENT_ADAPTER: "agentrun-v01",
|
||||||
|
AGENTRUN_MGR_URL: `http://127.0.0.1:${agentRunPort}`,
|
||||||
|
HWLAB_CODE_AGENT_AGENTRUN_ALLOW_NON_K3S_URL: "1",
|
||||||
|
HWLAB_CODE_AGENT_AGENTRUN_PROVIDER_ID: "D601",
|
||||||
|
HWLAB_CODE_AGENT_AGENTRUN_SOURCE_COMMIT: "0123456789abcdef0123456789abcdef01234567",
|
||||||
|
HWLAB_CODE_AGENT_DEFAULT_PROVIDER_PROFILE: "codex",
|
||||||
|
HWLAB_ENVIRONMENT: "v02",
|
||||||
|
HWLAB_GITOPS_PROFILE: "v02"
|
||||||
|
},
|
||||||
|
accessController: {
|
||||||
|
required: false,
|
||||||
|
async authenticate() {
|
||||||
|
return { ok: true, actor: TEST_AGENT_ACTOR, session: TEST_AUTH_SESSION };
|
||||||
|
},
|
||||||
|
async recordAgentSessionOwner(input) {
|
||||||
|
const previous = ownerSessions.get(input.sessionId)?.session ?? {};
|
||||||
|
const record = testAgentSessionRecord({ ...input, session: { ...previous, ...(input.session ?? {}) } });
|
||||||
|
ownerSessions.set(record.id, record);
|
||||||
|
return record;
|
||||||
|
},
|
||||||
|
async getAgentSession(sessionId) {
|
||||||
|
return ownerSessions.get(sessionId) ?? null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { port } = server.address();
|
||||||
|
const traceId = "trc_server-test-agentrun-profile-inherit";
|
||||||
|
const submit = await fetch(`http://127.0.0.1:${port}/v1/agent/chat`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "content-type": "application/json", "x-trace-id": traceId, cookie: "hwlab_session=test-stub-session" },
|
||||||
|
body: JSON.stringify({
|
||||||
|
conversationId,
|
||||||
|
sessionId: hwlabSessionId,
|
||||||
|
ownerUserId: "usr_agent_owner",
|
||||||
|
ownerRole: "user",
|
||||||
|
message: "Continue with the Workbench session provider profile"
|
||||||
|
})
|
||||||
|
});
|
||||||
|
assert.equal(submit.status, 202);
|
||||||
|
|
||||||
|
const payload = await pollAgentResult(port, traceId);
|
||||||
|
assert.equal(payload.status, "completed");
|
||||||
|
assert.equal(payload.agentRun.backendProfile, "dsflash-go");
|
||||||
|
assert.equal(calls.filter((call) => call.method === "POST" && call.path === "/api/v1/runs").length, 1);
|
||||||
|
const stored = ownerSessions.get(hwlabSessionId);
|
||||||
|
assert.equal(stored.session.providerProfile, "dsflash-go");
|
||||||
|
assert.equal(stored.session.agentRun.backendProfile, "dsflash-go");
|
||||||
|
} finally {
|
||||||
|
await new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve())));
|
||||||
|
await new Promise((resolve, reject) => agentRunServer.close((error) => (error ? reject(error) : resolve())));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
test("cloud api AgentRun adapter rejects non-internal manager URLs by default", async () => {
|
test("cloud api AgentRun adapter rejects non-internal manager URLs by default", async () => {
|
||||||
const server = createCloudApiServer({
|
const server = createCloudApiServer({
|
||||||
env: {
|
env: {
|
||||||
@@ -3194,6 +3354,11 @@ test("cloud api projection resume retries evicted AgentRun session with stored p
|
|||||||
HWLAB_CODE_AGENT_AGENTRUN_SOURCE_COMMIT: "a".repeat(40),
|
HWLAB_CODE_AGENT_AGENTRUN_SOURCE_COMMIT: "a".repeat(40),
|
||||||
HWLAB_CODE_AGENT_AGENTRUN_PROVIDER_ID: "JD01",
|
HWLAB_CODE_AGENT_AGENTRUN_PROVIDER_ID: "JD01",
|
||||||
HWLAB_RUNTIME_NAMESPACE: "hwlab-v03",
|
HWLAB_RUNTIME_NAMESPACE: "hwlab-v03",
|
||||||
|
HWLAB_RUNTIME_LANE: "v03",
|
||||||
|
HWLAB_RUNTIME_API_URL: "http://hwlab-cloud-api.hwlab-v03.svc.cluster.local:6667",
|
||||||
|
HWLAB_RUNTIME_WEB_URL: "http://hwlab-cloud-web.hwlab-v03.svc.cluster.local:8080",
|
||||||
|
HWLAB_RUNTIME_ENDPOINT_LOCKED: "1",
|
||||||
|
HWLAB_CODE_AGENT_ASSEMBLED_RUNTIME: "1",
|
||||||
HWLAB_CODE_AGENT_AGENTRUN_BACKEND_PROFILE: "dsflash-go",
|
HWLAB_CODE_AGENT_AGENTRUN_BACKEND_PROFILE: "dsflash-go",
|
||||||
HWLAB_CODE_AGENT_AGENTRUN_PROJECTION_RESUME_ENABLED: "1",
|
HWLAB_CODE_AGENT_AGENTRUN_PROJECTION_RESUME_ENABLED: "1",
|
||||||
HWLAB_CODE_AGENT_AGENTRUN_PROJECTION_RESUME_INITIAL_DELAY_MS: "1",
|
HWLAB_CODE_AGENT_AGENTRUN_PROJECTION_RESUME_INITIAL_DELAY_MS: "1",
|
||||||
@@ -3205,7 +3370,7 @@ test("cloud api projection resume retries evicted AgentRun session with stored p
|
|||||||
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
|
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (let index = 0; index < 100 && !calls.some((call) => call.path === `/api/v1/runs/${retryRunId}/commands`); index += 1) await delay(10);
|
for (let index = 0; index < 100 && projectionStates.get(traceId)?.runId !== retryRunId; index += 1) await delay(10);
|
||||||
assert.equal(calls.some((call) => call.path === `/api/v1/runs/${runId}/commands/${commandId}/result`), true);
|
assert.equal(calls.some((call) => call.path === `/api/v1/runs/${runId}/commands/${commandId}/result`), true);
|
||||||
assert.equal(calls.some((call) => call.path === `/api/v1/runs/${retryRunId}/commands`), true);
|
assert.equal(calls.some((call) => call.path === `/api/v1/runs/${retryRunId}/commands`), true);
|
||||||
const state = projectionStates.get(traceId);
|
const state = projectionStates.get(traceId);
|
||||||
|
|||||||
@@ -424,10 +424,57 @@ async function prepareManualCodeAgentSession({ params, options, traceId, respons
|
|||||||
params.conversationId = conversationId;
|
params.conversationId = conversationId;
|
||||||
params.sessionId = sessionId;
|
params.sessionId = sessionId;
|
||||||
params.projectId = requestedProjectId || sessionProjectId || DEFAULT_CODE_AGENT_PROJECT_ID;
|
params.projectId = requestedProjectId || sessionProjectId || DEFAULT_CODE_AGENT_PROJECT_ID;
|
||||||
|
applyManualSessionProviderProfile(params, session);
|
||||||
params.threadId = profileRecovery ? undefined : safeOpaqueId(params.threadId) || safeOpaqueId(session.threadId) || undefined;
|
params.threadId = profileRecovery ? undefined : safeOpaqueId(params.threadId) || safeOpaqueId(session.threadId) || undefined;
|
||||||
return { session };
|
return { session };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function applyManualSessionProviderProfile(params = {}, session = null) {
|
||||||
|
const requested = firstNonEmptyValue(params.providerProfile, params.codeAgentProviderProfile);
|
||||||
|
if (requested) {
|
||||||
|
const normalized = normalizeCodeAgentProviderProfile(requested);
|
||||||
|
params.providerProfile = normalized;
|
||||||
|
params.codeAgentProviderProfile = normalized;
|
||||||
|
params.providerProfileSource = "request";
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
const stored = manualSessionStoredProviderProfile(session);
|
||||||
|
if (!stored) return null;
|
||||||
|
params.providerProfile = stored;
|
||||||
|
params.codeAgentProviderProfile = stored;
|
||||||
|
params.providerProfileSource = "session";
|
||||||
|
return stored;
|
||||||
|
}
|
||||||
|
|
||||||
|
function manualSessionStoredProviderProfile(session = null) {
|
||||||
|
const evidence = session?.session && typeof session.session === "object" ? session.session : {};
|
||||||
|
const traceProfile = latestCompletedTraceResultProviderProfile(evidence.traceResults);
|
||||||
|
const raw = firstNonEmptyValue(
|
||||||
|
traceProfile,
|
||||||
|
evidence.agentRun?.backendProfile,
|
||||||
|
evidence.providerProfile,
|
||||||
|
evidence.codeAgentProviderProfile,
|
||||||
|
evidence.backendProfile
|
||||||
|
);
|
||||||
|
if (!raw) return null;
|
||||||
|
const normalized = normalizeCodeAgentProviderProfile(raw);
|
||||||
|
return normalized === "runtime-default" ? null : normalized;
|
||||||
|
}
|
||||||
|
|
||||||
|
function latestCompletedTraceResultProviderProfile(traceResults = null) {
|
||||||
|
if (!traceResults || typeof traceResults !== "object") return null;
|
||||||
|
const entries = Object.values(traceResults).filter((value) => value && typeof value === "object");
|
||||||
|
for (const result of entries.reverse()) {
|
||||||
|
const agentRun = result.agentRun && typeof result.agentRun === "object" ? result.agentRun : null;
|
||||||
|
if (!agentRun) continue;
|
||||||
|
const status = normalizeTurnStatus(result.status ?? agentRun.terminalStatus ?? agentRun.commandState ?? agentRun.status);
|
||||||
|
if (status && status !== "completed") continue;
|
||||||
|
const profile = firstNonEmptyValue(agentRun.backendProfile, result.providerProfile, result.codeAgentProviderProfile);
|
||||||
|
if (profile) return profile;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
function manualSessionProfileRecoveryAllowed(session, params = {}) {
|
function manualSessionProfileRecoveryAllowed(session, params = {}) {
|
||||||
const status = String(session?.status ?? "").trim().toLowerCase().replace(/_/gu, "-");
|
const status = String(session?.status ?? "").trim().toLowerCase().replace(/_/gu, "-");
|
||||||
if (status !== "thread-resume-failed") return false;
|
if (status !== "thread-resume-failed") return false;
|
||||||
@@ -863,6 +910,8 @@ function agentChatOtelAttributes(params = {}) {
|
|||||||
const execution = mdtodoExecutionContextFromParams(params);
|
const execution = mdtodoExecutionContextFromParams(params);
|
||||||
return {
|
return {
|
||||||
"agent.chat.session_id": safeSessionId(params.sessionId) || null,
|
"agent.chat.session_id": safeSessionId(params.sessionId) || null,
|
||||||
|
"agent.chat.provider_profile": safeOtelConfigText(params.providerProfile ?? params.codeAgentProviderProfile),
|
||||||
|
"agent.chat.provider_profile_source": safeOtelConfigText(params.providerProfileSource),
|
||||||
"agent.chat.task_ref": safeOtelText(params.taskRef ?? execution?.taskRef),
|
"agent.chat.task_ref": safeOtelText(params.taskRef ?? execution?.taskRef),
|
||||||
"agent.chat.source_id": safeOtelConfigText(execution?.sourceId ?? params.sourceId),
|
"agent.chat.source_id": safeOtelConfigText(execution?.sourceId ?? params.sourceId),
|
||||||
"agent.chat.hwpod_id": safeOtelText(execution?.hwpodId),
|
"agent.chat.hwpod_id": safeOtelText(execution?.hwpodId),
|
||||||
@@ -1594,7 +1643,8 @@ async function runAgentRunProjectionResumePass({ options = {}, traceStore = defa
|
|||||||
attempts: retryAttempt,
|
attempts: retryAttempt,
|
||||||
nextRetryAtMs,
|
nextRetryAtMs,
|
||||||
exhausted: retryExhausted,
|
exhausted: retryExhausted,
|
||||||
lastErrorCode: error?.code ?? "projection_resume_sync_failed"
|
lastErrorCode: error?.code ?? "projection_resume_sync_failed",
|
||||||
|
lastErrorMessage: safeOtelText(error?.message, 300)
|
||||||
});
|
});
|
||||||
appendProjectionResumeError(traceStore, candidate, error, { retryAttempt, retryMaxAttempts: candidateRetryMaxAttempts, retryLabel: retryAttempt + "/" + candidateRetryMaxAttempts, retryDelayMs, nextRetryAt, retryExhausted });
|
appendProjectionResumeError(traceStore, candidate, error, { retryAttempt, retryMaxAttempts: candidateRetryMaxAttempts, retryLabel: retryAttempt + "/" + candidateRetryMaxAttempts, retryDelayMs, nextRetryAt, retryExhausted });
|
||||||
}
|
}
|
||||||
@@ -1615,12 +1665,23 @@ async function runAgentRunProjectionResumePass({ options = {}, traceStore = defa
|
|||||||
skippedCooldown,
|
skippedCooldown,
|
||||||
skippedExhausted,
|
skippedExhausted,
|
||||||
candidateRetryTracked: candidateRetryState.size,
|
candidateRetryTracked: candidateRetryState.size,
|
||||||
|
candidateRetryLastErrorCode: latestProjectionResumeRetryField(candidateRetryState, "lastErrorCode"),
|
||||||
|
candidateRetryLastErrorMessage: latestProjectionResumeRetryField(candidateRetryState, "lastErrorMessage"),
|
||||||
candidateRetryMaxAttempts,
|
candidateRetryMaxAttempts,
|
||||||
valuesRedacted: true
|
valuesRedacted: true
|
||||||
});
|
});
|
||||||
return { offset, scannedSessions: sessions.length, candidates: candidates.length, resumed, terminal, nonTerminal, failed, skippedCooldown, skippedExhausted };
|
return { offset, scannedSessions: sessions.length, candidates: candidates.length, resumed, terminal, nonTerminal, failed, skippedCooldown, skippedExhausted };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function latestProjectionResumeRetryField(candidateRetryState, field) {
|
||||||
|
const values = [...candidateRetryState.values()];
|
||||||
|
for (let index = values.length - 1; index >= 0; index -= 1) {
|
||||||
|
const text = textValue(values[index]?.[field]);
|
||||||
|
if (text) return text;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
async function listAgentRunProjectionResumeStateCandidates(options = {}, batchSize = DEFAULT_AGENTRUN_PROJECTION_RESUME_BATCH_SIZE, offset = 0, { minAgeMs = DEFAULT_AGENTRUN_PROJECTION_RESUME_MIN_AGE_MS } = {}) {
|
async function listAgentRunProjectionResumeStateCandidates(options = {}, batchSize = DEFAULT_AGENTRUN_PROJECTION_RESUME_BATCH_SIZE, offset = 0, { minAgeMs = DEFAULT_AGENTRUN_PROJECTION_RESUME_MIN_AGE_MS } = {}) {
|
||||||
const runtimeStore = options.runtimeStore;
|
const runtimeStore = options.runtimeStore;
|
||||||
if (typeof runtimeStore?.queryWorkbenchProjectionStates !== "function") return [];
|
if (typeof runtimeStore?.queryWorkbenchProjectionStates !== "function") return [];
|
||||||
@@ -3450,7 +3511,9 @@ function codeAgentSessionOwnerEvidence(payload = {}, params = {}) {
|
|||||||
const firstUserMessagePreview = messages.find((message) => message.role === "user")?.text?.slice(0, 240) ?? null;
|
const firstUserMessagePreview = messages.find((message) => message.role === "user")?.text?.slice(0, 240) ?? null;
|
||||||
const promptEvidence = codeAgentPromptMetadata(payload, params, traceId);
|
const promptEvidence = codeAgentPromptMetadata(payload, params, traceId);
|
||||||
const traceResult = codeAgentTraceResultEvidence(payload, params, traceId, finalResponse, traceSummary);
|
const traceResult = codeAgentTraceResultEvidence(payload, params, traceId, finalResponse, traceSummary);
|
||||||
|
const providerProfile = codeAgentProviderProfileEvidence(payload, params);
|
||||||
return {
|
return {
|
||||||
|
...(providerProfile ? { providerProfile, codeAgentProviderProfile: providerProfile } : {}),
|
||||||
provider: payload.provider ?? null,
|
provider: payload.provider ?? null,
|
||||||
model: payload.model ?? null,
|
model: payload.model ?? null,
|
||||||
backend: payload.backend ?? null,
|
backend: payload.backend ?? null,
|
||||||
@@ -3475,6 +3538,21 @@ function codeAgentSessionOwnerEvidence(payload = {}, params = {}) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function codeAgentProviderProfileEvidence(payload = {}, params = {}) {
|
||||||
|
const raw = firstNonEmptyValue(
|
||||||
|
params.providerProfile,
|
||||||
|
params.codeAgentProviderProfile,
|
||||||
|
payload.providerProfile,
|
||||||
|
payload.codeAgentProviderProfile,
|
||||||
|
payload.agentRun?.backendProfile,
|
||||||
|
payload.providerTrace?.backendProfile,
|
||||||
|
payload.backendProfile
|
||||||
|
);
|
||||||
|
if (!raw) return null;
|
||||||
|
const normalized = normalizeCodeAgentProviderProfile(raw);
|
||||||
|
return normalized === "runtime-default" ? null : normalized;
|
||||||
|
}
|
||||||
|
|
||||||
function codeAgentTraceResultEvidence(payload = {}, params = {}, traceId = null, finalResponse = null, traceSummary = null) {
|
function codeAgentTraceResultEvidence(payload = {}, params = {}, traceId = null, finalResponse = null, traceSummary = null) {
|
||||||
const resolvedTraceId = safeTraceId(traceId);
|
const resolvedTraceId = safeTraceId(traceId);
|
||||||
if (!resolvedTraceId) return null;
|
if (!resolvedTraceId) return null;
|
||||||
|
|||||||
Reference in New Issue
Block a user