From d86bbd58357e094548662d5417723150be78a2dc Mon Sep 17 00:00:00 2001 From: HiranoMasaaki Date: Tue, 17 Feb 2026 12:59:16 +0900 Subject: [PATCH] Fix: False RetryActivity for proceeding/init checkpoint status When getActivities() encounters checkpoints with "proceeding" or "init" status and no tool calls, it now returns empty activities instead of creating a misleading RetryActivity with "No tool call or result found". These are normal intermediate states during parallel delegate execution. Co-Authored-By: Claude Opus 4.6 --- .changeset/fix-false-retry-activity.md | 7 ++ packages/core/src/utils/activity.test.ts | 88 +++++++++++++++++++++++- packages/core/src/utils/activity.ts | 6 ++ 3 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 .changeset/fix-false-retry-activity.md diff --git a/.changeset/fix-false-retry-activity.md b/.changeset/fix-false-retry-activity.md new file mode 100644 index 00000000..bcbacd7e --- /dev/null +++ b/.changeset/fix-false-retry-activity.md @@ -0,0 +1,7 @@ +--- +"@perstack/core": patch +--- + +fix: stop producing false RetryActivity for proceeding/init checkpoints + +When `getActivities()` encounters checkpoints with `proceeding` or `init` status and no tool calls, it now returns empty activities instead of creating a misleading RetryActivity with "No tool call or result found". These are normal intermediate states during parallel delegate execution. diff --git a/packages/core/src/utils/activity.test.ts b/packages/core/src/utils/activity.test.ts index c5048fbc..07abc98b 100644 --- a/packages/core/src/utils/activity.test.ts +++ b/packages/core/src/utils/activity.test.ts @@ -260,9 +260,95 @@ describe("getActivities", () => { }) }) + describe("intermediate status with no tool calls", () => { + it("returns empty activities for proceeding status with no tool calls", () => { + const checkpoint = createBaseCheckpoint({ status: "proceeding", stepNumber: 2 }) + const step = createBaseStep({ stepNumber: 2, toolCalls: [], toolResults: [] }) + + const activities = getActivities({ checkpoint, step }) + + expect(activities).toEqual([]) + }) + + it("returns only query activity for proceeding status at step 1", () => { + const checkpoint = createBaseCheckpoint({ status: "proceeding", stepNumber: 1 }) + const step = createBaseStep({ + stepNumber: 1, + toolCalls: [], + toolResults: [], + inputMessages: [ + { + id: "m-1", + type: "userMessage", + contents: [{ type: "textPart", id: "tp-1", text: "Hello" }], + }, + ], + }) + + const activities = getActivities({ checkpoint, step }) + + expect(activities).toHaveLength(1) + expect(activities[0].type).toBe("query") + }) + + it("returns empty activities for init status with no tool calls", () => { + const checkpoint = createBaseCheckpoint({ status: "init", stepNumber: 2 }) + const step = createBaseStep({ stepNumber: 2, toolCalls: [], toolResults: [] }) + + const activities = getActivities({ checkpoint, step }) + + expect(activities).toEqual([]) + }) + + it("returns empty activities for proceeding status when tool calls have no matching results", () => { + const checkpoint = createBaseCheckpoint({ status: "proceeding", stepNumber: 2 }) + const step = createBaseStep({ + stepNumber: 2, + toolCalls: [ + createToolCall({ + id: "tc-1", + toolName: "exec", + args: { command: "ls" }, + }), + ], + toolResults: [], + }) + + const activities = getActivities({ checkpoint, step }) + + expect(activities).toEqual([]) + }) + + it("still returns RetryActivity for stoppedByExceededMaxSteps with no tool calls", () => { + const checkpoint = createBaseCheckpoint({ + status: "stoppedByExceededMaxSteps", + stepNumber: 2, + }) + const step = createBaseStep({ stepNumber: 2, toolCalls: [], toolResults: [] }) + + const activities = getActivities({ checkpoint, step }) + + expect(activities).toHaveLength(1) + expect(activities[0].type).toBe("retry") + }) + + it("still returns RetryActivity for stoppedByCancellation with no tool calls", () => { + const checkpoint = createBaseCheckpoint({ + status: "stoppedByCancellation", + stepNumber: 2, + }) + const step = createBaseStep({ stepNumber: 2, toolCalls: [], toolResults: [] }) + + const activities = getActivities({ checkpoint, step }) + + expect(activities).toHaveLength(1) + expect(activities[0].type).toBe("retry") + }) + }) + describe("retry activity", () => { it("returns retry activity when no tool call or result", () => { - const checkpoint = createBaseCheckpoint() + const checkpoint = createBaseCheckpoint({ status: "stoppedByExceededMaxSteps" }) const step = createBaseStep({ newMessages: [ { diff --git a/packages/core/src/utils/activity.ts b/packages/core/src/utils/activity.ts index fdbf4b96..9f89fd0d 100644 --- a/packages/core/src/utils/activity.ts +++ b/packages/core/src/utils/activity.ts @@ -142,6 +142,9 @@ export function getActivities(params: GetActivitiesParams): ActivityOrGroup[] { if (status === "completed") { return prependQuery([createCompleteActivity(step.newMessages, reasoning)]) } + if (status === "proceeding" || status === "init") { + return prependQuery([]) + } return prependQuery([createRetryActivity(step.newMessages, reasoning)]) } @@ -166,6 +169,9 @@ export function getActivities(params: GetActivitiesParams): ActivityOrGroup[] { if (status === "completed") { return prependQuery([createCompleteActivity(step.newMessages, reasoning)]) } + if (status === "proceeding" || status === "init") { + return prependQuery([]) + } return prependQuery([createRetryActivity(step.newMessages, reasoning)]) }