agents: address execution correctness review fixes

This commit is contained in:
Eva
2026-04-11 01:32:07 +07:00
committed by Peter Steinberger
parent aa5bec4bdf
commit f65ffdff96
7 changed files with 94 additions and 37 deletions

View File

@@ -1503,6 +1503,21 @@ export async function runEmbeddedPiAgent(
"Please try again, or increase `agents.defaults.llm.idleTimeoutSeconds` in your config (set to 0 to disable)."
: "Request timed out before a response was generated. " +
"Please try again, or increase `agents.defaults.timeoutSeconds` in your config.";
const replayInvalid = resolveReplayInvalidFlag({
attempt,
incompleteTurnText: null,
});
const livenessState = resolveRunLivenessState({
payloadCount: payloads.length,
aborted,
timedOut,
attempt,
incompleteTurnText: null,
});
attempt.setTerminalLifecycleMeta?.({
replayInvalid,
livenessState,
});
return {
payloads: [
{
@@ -1516,17 +1531,8 @@ export async function runEmbeddedPiAgent(
aborted,
systemPromptReport: attempt.systemPromptReport,
finalAssistantVisibleText,
replayInvalid: resolveReplayInvalidFlag({
attempt,
incompleteTurnText: null,
}),
livenessState: resolveRunLivenessState({
payloadCount: payloads.length,
aborted,
timedOut,
attempt,
incompleteTurnText: null,
}),
replayInvalid,
livenessState,
},
didSendViaMessagingTool: attempt.didSendViaMessagingTool,
didSendDeterministicApprovalPrompt: attempt.didSendDeterministicApprovalPrompt,
@@ -1619,6 +1625,21 @@ export async function runEmbeddedPiAgent(
};
}
if (incompleteTurnText) {
const replayInvalid = resolveReplayInvalidFlag({
attempt,
incompleteTurnText,
});
const livenessState = resolveRunLivenessState({
payloadCount: payloads.length,
aborted,
timedOut,
attempt,
incompleteTurnText,
});
attempt.setTerminalLifecycleMeta?.({
replayInvalid,
livenessState,
});
const incompleteStopReason = attempt.lastAssistant?.stopReason;
log.warn(
`incomplete turn detected: runId=${params.runId} sessionId=${params.sessionId} ` +
@@ -1647,17 +1668,8 @@ export async function runEmbeddedPiAgent(
aborted,
systemPromptReport: attempt.systemPromptReport,
finalAssistantVisibleText,
replayInvalid: resolveReplayInvalidFlag({
attempt,
incompleteTurnText,
}),
livenessState: resolveRunLivenessState({
payloadCount: payloads.length,
aborted,
timedOut,
attempt,
incompleteTurnText,
}),
replayInvalid,
livenessState,
},
didSendViaMessagingTool: attempt.didSendViaMessagingTool,
didSendDeterministicApprovalPrompt: attempt.didSendDeterministicApprovalPrompt,
@@ -1684,6 +1696,21 @@ export async function runEmbeddedPiAgent(
agentDir: params.agentDir,
});
}
const replayInvalid = resolveReplayInvalidFlag({
attempt,
incompleteTurnText: null,
});
const livenessState = resolveRunLivenessState({
payloadCount: payloads.length,
aborted,
timedOut,
attempt,
incompleteTurnText: null,
});
attempt.setTerminalLifecycleMeta?.({
replayInvalid,
livenessState,
});
return {
payloads: payloadsWithToolMedia?.length ? payloadsWithToolMedia : undefined,
meta: {
@@ -1692,17 +1719,8 @@ export async function runEmbeddedPiAgent(
aborted,
systemPromptReport: attempt.systemPromptReport,
finalAssistantVisibleText,
replayInvalid: resolveReplayInvalidFlag({
attempt,
incompleteTurnText: null,
}),
livenessState: resolveRunLivenessState({
payloadCount: payloads.length,
aborted,
timedOut,
attempt,
incompleteTurnText: null,
}),
replayInvalid,
livenessState,
// Handle client tool calls (OpenResponses hosted tools)
// Propagate the LLM stop reason so callers (lifecycle events,
// ACP bridge) can distinguish end_turn from max_tokens.

View File

@@ -1447,6 +1447,7 @@ export async function runEmbeddedAttempt(
getSuccessfulCronAdds,
didSendViaMessagingTool,
getLastToolError,
setTerminalLifecycleMeta,
getUsageTotals,
getCompactionCount,
} = subscription;
@@ -2277,6 +2278,7 @@ export async function runEmbeddedAttempt(
successfulCronAdds: getSuccessfulCronAdds(),
}),
itemLifecycle: getItemLifecycle(),
setTerminalLifecycleMeta,
aborted,
timedOut,
idleTimedOut,

View File

@@ -8,6 +8,7 @@ import type { PluginHookBeforeAgentStartResult } from "../../../plugins/types.js
import type { MessagingToolSend } from "../../pi-embedded-messaging.js";
import type { ToolErrorSummary } from "../../tool-error-summary.js";
import type { NormalizedUsage } from "../../usage.js";
import type { EmbeddedRunLivenessState } from "../types.js";
import type { RunEmbeddedPiAgentParams } from "./params.js";
import type { PreemptiveCompactionRoute } from "./preemptive-compaction.js";
@@ -98,4 +99,8 @@ export type EmbeddedRunAttemptResult = {
completedCount: number;
activeCount: number;
};
setTerminalLifecycleMeta?: (meta: {
replayInvalid?: boolean;
livenessState?: EmbeddedRunLivenessState;
}) => void;
};

View File

@@ -68,7 +68,9 @@ export function handleAutoCompactionEnd(
ctx.resetForCompactionRetry();
ctx.log.debug(`embedded run compaction retry: runId=${ctx.params.runId}`);
} else {
ctx.state.livenessState = "working";
if (!wasAborted) {
ctx.state.livenessState = "working";
}
ctx.maybeResolveCompactionWait();
clearStaleAssistantUsageOnSessionMessages(ctx);
}

View File

@@ -15,6 +15,7 @@ import {
normalizeTextForComparison,
} from "./pi-embedded-helpers.js";
import type { BlockReplyPayload } from "./pi-embedded-payloads.js";
import type { EmbeddedRunLivenessState } from "./pi-embedded-runner/types.js";
import { createEmbeddedPiSessionEventHandler } from "./pi-embedded-subscribe.handlers.js";
import { consumePendingToolMediaIntoReply } from "./pi-embedded-subscribe.handlers.messages.js";
import type {
@@ -776,6 +777,17 @@ export function subscribeEmbeddedPiSession(params: SubscribeEmbeddedPiSessionPar
assistantTexts,
toolMetas,
unsubscribe,
setTerminalLifecycleMeta: (meta: {
replayInvalid?: boolean;
livenessState?: EmbeddedRunLivenessState;
}) => {
if (typeof meta.replayInvalid === "boolean") {
state.replayInvalid = meta.replayInvalid;
}
if (meta.livenessState) {
state.livenessState = meta.livenessState;
}
},
isCompacting: () => state.compactionInFlight || state.pendingCompactionRetry > 0,
isCompactionInFlight: () => state.compactionInFlight,
getMessagingToolSentTexts: () => messagingToolSentTexts.slice(),

View File

@@ -166,7 +166,13 @@ export function normalizeOpenAIToolSchemas(
return ctx.tools;
}
return ctx.tools.map((tool) => {
if (!tool.parameters || typeof tool.parameters !== "object") {
if (tool.parameters == null) {
return {
...tool,
parameters: normalizeOpenAIStrictCompatSchema({}),
};
}
if (typeof tool.parameters !== "object") {
return tool;
}
return {
@@ -292,13 +298,23 @@ function normalizeOpenAIStrictCompatSchemaRecursive(schema: unknown): unknown {
return changed ? normalized : schema;
}
export function findOpenAIStrictSchemaViolations(schema: unknown, path: string): string[] {
export function findOpenAIStrictSchemaViolations(
schema: unknown,
path: string,
options?: { requireObjectRoot?: boolean },
): string[] {
if (Array.isArray(schema)) {
if (options?.requireObjectRoot) {
return [`${path}.type`];
}
return schema.flatMap((item, index) =>
findOpenAIStrictSchemaViolations(item, `${path}[${index}]`),
);
}
if (!schema || typeof schema !== "object") {
if (options?.requireObjectRoot) {
return [`${path}.type`];
}
return [];
}
@@ -363,8 +379,9 @@ export function inspectOpenAIToolSchemas(
}
return ctx.tools.flatMap((tool, toolIndex) => {
const violations = findOpenAIStrictSchemaViolations(
normalizeOpenAIStrictCompatSchema(tool.parameters),
normalizeOpenAIStrictCompatSchema(tool.parameters ?? {}),
`${tool.name}.parameters`,
{ requireObjectRoot: true },
);
if (violations.length === 0) {
return [];

View File

@@ -63,6 +63,7 @@ const EXPECTED_SHARED_FAMILY_CONTRACTS: Record<string, ExpectedSharedFamilyContr
},
openai: {
streamFamilies: ["openai-responses-defaults"],
toolCompatFamilies: ["openai"],
},
opencode: {
replayFamilies: ["passthrough-gemini"],