diff --git a/packages/provider-pi/src/server/adapter.test.ts b/packages/provider-pi/src/server/adapter.test.ts index 944ffe2bee7c60b41d6b691bfdc4f2ffab206e4f..2e79ea472d765cc9a94609fc775e8a762e9fbbcd 100644 --- a/packages/provider-pi/src/server/adapter.test.ts +++ b/packages/provider-pi/src/server/adapter.test.ts @@ -2821,6 +2821,67 @@ describe("PiAdapterV2", () => { ), ); + it.effect.each([ + { toolName: "bash", exitCode: 0, legacy: false }, + { toolName: "powershell", exitCode: 2, legacy: false }, + { toolName: "bash", exitCode: 1, legacy: true }, + { toolName: "powershell", exitCode: 0, legacy: true }, + ])( + "normalizes $toolName command results, exit=$exitCode legacy=$legacy", + ({ toolName, exitCode, legacy }) => + Effect.gen(function* () { + const fake = yield* makeFakePi; + const { runtime, takeEvent } = yield* openRuntime(fake); + const providerThread = yield* runtime.ensureThread({ + threadId: THREAD_ID, + modelSelection: modelSelection("default"), + runtimePolicy, + }); + yield* startTurn(runtime, providerThread); + yield* fake.takeRequest("prompt"); + yield* fake.emit({ + type: "tool_execution_start", + toolCallId: "shell-call", + toolName, + args: { command: "echo hello" }, + }); + const running = yield* takeEvent( + (event) => + event.type === "turn_item.updated" && event.turnItem.type === "command_execution", + ); + assert.isTrue( + running.type === "turn_item.updated" && + running.turnItem.type === "command_execution" && + running.turnItem.status === "running" && + running.turnItem.input === "echo hello", + ); + yield* fake.emit({ + type: "tool_execution_end", + toolCallId: "shell-call", + toolName, + isError: exitCode !== 0, + result: { + content: [{ type: "text", text: "hello" }], + details: { exitCode: legacy ? exitCode : 99 }, + ...(legacy ? {} : { structuredContent: { exit_code: exitCode } }), + }, + }); + const completed = yield* takeEvent( + (event) => + event.type === "turn_item.updated" && event.turnItem.type === "command_execution", + ); + assert.isTrue( + completed.type === "turn_item.updated" && + completed.turnItem.type === "command_execution" && + completed.turnItem.title === toolName && + completed.turnItem.input === "echo hello" && + completed.turnItem.output === "hello" && + completed.turnItem.exitCode === exitCode && + completed.turnItem.status === (exitCode === 0 ? "completed" : "failed"), + ); + }).pipe(Effect.scoped, Effect.provide(layerTest)), + ); + it.effect("emits session-start dialogs before a turn exists", () => Effect.gen(function* () { const fake = yield* makeFakePi; diff --git a/packages/provider-pi/src/server/adapter.ts b/packages/provider-pi/src/server/adapter.ts index 7022be2135ab688d2a5dce19b7253e094727bd96..191486fbce753f27d73573e4362f4a45be9a3673 100644 --- a/packages/provider-pi/src/server/adapter.ts +++ b/packages/provider-pi/src/server/adapter.ts @@ -1109,8 +1109,10 @@ export const makePiAdapterV2 = Effect.fn("makePiAdapterV2")(function* ( status, completedAt: completed ? emittedAt : null, } as const; - if (toolName === "bash") { - const exitCode = recordNumber(recordField(resultRecord, "details"), "exitCode"); + if (toolName === "bash" || toolName === "powershell") { + const exitCode = + recordNumber(recordField(resultRecord, "structuredContent"), "exit_code") ?? + recordNumber(recordField(resultRecord, "details"), "exitCode"); yield* emit({ type: "turn_item.updated", driver: PI_PROVIDER,