diff --git a/Dockerfile.gh-build b/Dockerfile.gh-build new file mode 100644 index 00000000..514441eb --- /dev/null +++ b/Dockerfile.gh-build @@ -0,0 +1,21 @@ +FROM ubuntu:24.04 + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update \ + && apt-get install -y curl unzip git xz-utils ca-certificates \ + && curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \ + && apt-get install -y nodejs \ + && BUN_VERSION=1.2.20 \ + && ARCH=$(dpkg --print-architecture) \ + && if [ "$ARCH" = "amd64" ]; then BUN_ARCH="linux-x64"; else BUN_ARCH="linux-aarch64"; fi \ + && curl -fsSL "https://github.com/oven-sh/bun/releases/download/bun-v${BUN_VERSION}/bun-${BUN_ARCH}.zip" -o /tmp/bun.zip \ + && unzip /tmp/bun.zip -d /tmp \ + && mv /tmp/bun-${BUN_ARCH}/bun /usr/local/bin/bun \ + && chmod +x /usr/local/bin/bun \ + && rm -rf /tmp/bun.zip /tmp/bun-linux-x64 \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /repo + +ENTRYPOINT ["/bin/bash", "-lc"] diff --git a/packages/ai/src/providers/transform-messages.ts b/packages/ai/src/providers/transform-messages.ts index 253416bb..e0e5f8d7 100644 --- a/packages/ai/src/providers/transform-messages.ts +++ b/packages/ai/src/providers/transform-messages.ts @@ -1,11 +1,11 @@ import type { Api, AssistantMessage, Message, Model, ToolCall, ToolResultMessage } from "../types.js"; /** - * Normalize tool call ID for GitHub Copilot cross-API compatibility. + * Normalize tool call ID for cross-provider compatibility. * OpenAI Responses API generates IDs that are 450+ chars with special characters like `|`. - * Other APIs (Claude, etc.) require max 40 chars and only alphanumeric + underscore + hyphen. + * Anthropic APIs require IDs matching ^[a-zA-Z0-9_-]+$ (max 64 chars). */ -function normalizeCopilotToolCallId(id: string): string { +function normalizeToolCallId(id: string): string { return id.replace(/[^a-zA-Z0-9_-]/g, "").slice(0, 40); } @@ -38,11 +38,17 @@ export function transformMessages(messages: Message[], model: return msg; } - // Check if we need to normalize tool call IDs (github-copilot cross-API) - const needsToolCallIdNormalization = + // Check if we need to normalize tool call IDs + // Anthropic APIs require IDs matching ^[a-zA-Z0-9_-]+$ (max 64 chars) + // OpenAI Responses API generates IDs with `|` and 450+ chars + // GitHub Copilot routes to Anthropic for Claude models + const targetRequiresStrictIds = model.api === "anthropic-messages" || model.provider === "github-copilot"; + const crossProviderSwitch = assistantMsg.provider !== model.provider; + const copilotCrossApiSwitch = assistantMsg.provider === "github-copilot" && model.provider === "github-copilot" && assistantMsg.api !== model.api; + const needsToolCallIdNormalization = targetRequiresStrictIds && (crossProviderSwitch || copilotCrossApiSwitch); // Transform message from different provider/model const transformedContent = assistantMsg.content.flatMap((block) => { @@ -54,10 +60,10 @@ export function transformMessages(messages: Message[], model: text: block.thinking, }; } - // Normalize tool call IDs for github-copilot cross-API switches + // Normalize tool call IDs when target API requires strict format if (block.type === "toolCall" && needsToolCallIdNormalization) { const toolCall = block as ToolCall; - const normalizedId = normalizeCopilotToolCallId(toolCall.id); + const normalizedId = normalizeToolCallId(toolCall.id); if (normalizedId !== toolCall.id) { toolCallIdMap.set(toolCall.id, normalizedId); return { ...toolCall, id: normalizedId }; diff --git a/packages/coding-agent/.gitignore b/packages/coding-agent/.gitignore new file mode 100644 index 00000000..db154f29 --- /dev/null +++ b/packages/coding-agent/.gitignore @@ -0,0 +1 @@ +*.bun-build diff --git a/packages/coding-agent/src/core/extensions/loader.ts b/packages/coding-agent/src/core/extensions/loader.ts index e5dfb092..05f97a7e 100644 --- a/packages/coding-agent/src/core/extensions/loader.ts +++ b/packages/coding-agent/src/core/extensions/loader.ts @@ -33,6 +33,12 @@ function getAliases(): Record { const __dirname = path.dirname(fileURLToPath(import.meta.url)); const packageIndex = path.resolve(__dirname, "../..", "index.js"); + // Debug: log what we're resolving + if (process.env.DEBUG_EXTENSIONS) { + console.error("[DEBUG] import.meta.url:", import.meta.url); + console.error("[DEBUG] __dirname:", __dirname); + } + const typeboxEntry = require.resolve("@sinclair/typebox"); const typeboxRoot = typeboxEntry.replace(/\/build\/cjs\/index\.js$/, ""); @@ -43,6 +49,11 @@ function getAliases(): Record { "@mariozechner/pi-ai": require.resolve("@mariozechner/pi-ai"), "@sinclair/typebox": typeboxRoot, }; + + if (process.env.DEBUG_EXTENSIONS) { + console.error("[DEBUG] aliases:", JSON.stringify(_aliases, null, 2)); + } + return _aliases; } diff --git a/scripts/gh-build-docker.sh b/scripts/gh-build-docker.sh new file mode 100755 index 00000000..94b2062f --- /dev/null +++ b/scripts/gh-build-docker.sh @@ -0,0 +1,40 @@ +#!/bin/bash +set -euo pipefail + +echo "=== Versions ===" +node --version +bun --version +npm --version + +echo "=== Install dependencies ===" +npm ci + +echo "=== Install cross-platform bindings ===" +npm install --no-save --force \ + @mariozechner/clipboard-darwin-arm64@0.3.0 \ + @mariozechner/clipboard-darwin-x64@0.3.0 \ + @mariozechner/clipboard-linux-x64-gnu@0.3.0 \ + @mariozechner/clipboard-linux-arm64-gnu@0.3.0 \ + @mariozechner/clipboard-win32-x64-msvc@0.3.0 + +npm install --no-save --force \ + @img/sharp-darwin-arm64@0.34.5 \ + @img/sharp-darwin-x64@0.34.5 \ + @img/sharp-linux-x64@0.34.5 \ + @img/sharp-linux-arm64@0.34.5 \ + @img/sharp-win32-x64@0.34.5 \ + @img/sharp-libvips-darwin-arm64@1.2.4 \ + @img/sharp-libvips-darwin-x64@1.2.4 \ + @img/sharp-libvips-linux-x64@1.2.4 \ + @img/sharp-libvips-linux-arm64@1.2.4 + +echo "=== Build all packages ===" +npm run build + +echo "=== Build darwin-arm64 binary ===" +mkdir -p /repo/.tmp +cd packages/coding-agent +bun build --compile --target=bun-darwin-arm64 ./dist/cli.js --outfile /repo/.tmp/pi-darwin-arm64 + +echo "=== Done ===" +ls -la /repo/.tmp/pi-darwin-arm64 diff --git a/scripts/test-gh-build.sh b/scripts/test-gh-build.sh new file mode 100644 index 00000000..1c17db1f --- /dev/null +++ b/scripts/test-gh-build.sh @@ -0,0 +1,45 @@ +#!/bin/bash +set -e + +# Simulate GH Actions build locally + +cd /Users/badlogic/workspaces/pi-mono + +echo "=== Cleaning node_modules ===" +rm -rf node_modules packages/*/node_modules + +echo "=== npm ci ===" +npm ci + +echo "=== Install cross-platform bindings (like GH Actions) ===" +npm install --no-save --force \ + @mariozechner/clipboard-darwin-arm64@0.3.0 \ + @mariozechner/clipboard-darwin-x64@0.3.0 \ + @mariozechner/clipboard-linux-x64-gnu@0.3.0 \ + @mariozechner/clipboard-linux-arm64-gnu@0.3.0 \ + @mariozechner/clipboard-win32-x64-msvc@0.3.0 + +npm install --no-save --force \ + @img/sharp-darwin-arm64@0.34.5 \ + @img/sharp-darwin-x64@0.34.5 \ + @img/sharp-linux-x64@0.34.5 \ + @img/sharp-linux-arm64@0.34.5 \ + @img/sharp-win32-x64@0.34.5 \ + @img/sharp-libvips-darwin-arm64@1.2.4 \ + @img/sharp-libvips-darwin-x64@1.2.4 \ + @img/sharp-libvips-linux-x64@1.2.4 \ + @img/sharp-libvips-linux-arm64@1.2.4 + +echo "=== Build all packages ===" +npm run build + +echo "=== Build binary with cross-compile flag ===" +cd packages/coding-agent +bun build --compile --target=bun-darwin-arm64 ./dist/cli.js --outfile /tmp/pi-gh-sim/pi +cp package.json /tmp/pi-gh-sim/ + +echo "=== Test the binary ===" +/tmp/pi-gh-sim/pi -e /Users/badlogic/workspaces/pi-doom --help 2>&1 | head -5 + +echo "=== Binary size ===" +ls -la /tmp/pi-gh-sim/pi