Skip to content

Commit cb10ab0

Browse files
committed
fix: it works! somehow
1 parent 5e2ee6a commit cb10ab0

File tree

6 files changed

+110
-51
lines changed

6 files changed

+110
-51
lines changed

apps/array/forge.config.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,8 @@ function copySync(dependency: string, destinationRoot: string, source: string) {
127127
const config: ForgeConfig = {
128128
packagerConfig: {
129129
asar: {
130-
unpack: "{**/*.node,**/spawn-helper,**/.vite/build/claude-cli/**}",
130+
unpack:
131+
"{**/*.node,**/spawn-helper,**/.vite/build/claude-cli/**,**/node_modules/node-pty/**}",
131132
},
132133
prune: false,
133134
name: "Array",

apps/array/src/main/services/agent.ts

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { randomUUID } from "node:crypto";
2-
import { rmSync } from "node:fs";
2+
import { mkdirSync, rmSync, symlinkSync } from "node:fs";
3+
import { tmpdir } from "node:os";
34
import { join } from "node:path";
45
import { Agent, PermissionMode } from "@posthog/agent";
56
import {
@@ -128,6 +129,15 @@ export function registerAgentIpc(
128129
ts: Date.now(),
129130
message: `[Claude stderr] ${text}`,
130131
});
132+
133+
// Propagate spawn errors specifically to help debugging
134+
if (text.includes("spawn") && text.includes("ENOENT")) {
135+
emitToRenderer({
136+
type: "error",
137+
ts: Date.now(),
138+
message: `Critical Agent Error: ${text}`,
139+
});
140+
}
131141
};
132142

133143
const agent = new Agent({
@@ -191,14 +201,35 @@ export function registerAgentIpc(
191201
(async () => {
192202
const resolvedPermission = resolvePermissionMode(permissionMode);
193203
try {
204+
// Create a temporary directory to mock 'node' in PATH
205+
// Electron apps don't have 'node' in PATH, and process.execPath points to the App binary
206+
// We symlink 'node' -> process.execPath and add this dir to PATH
207+
const mockNodeDir = join(tmpdir(), `array-agent-node-${taskId}`);
208+
try {
209+
mkdirSync(mockNodeDir, { recursive: true });
210+
const nodeSymlinkPath = join(mockNodeDir, "node");
211+
// Remove existing symlink if it exists
212+
try {
213+
rmSync(nodeSymlinkPath, { force: true });
214+
} catch {}
215+
symlinkSync(process.execPath, nodeSymlinkPath);
216+
} catch (err) {
217+
console.warn("[agent] Failed to setup mock node environment", err);
218+
}
219+
220+
const newPath = `${mockNodeDir}:${process.env.PATH || ""}`;
221+
194222
const envOverrides = {
195223
...process.env,
224+
PATH: newPath,
196225
POSTHOG_API_KEY: apiKey,
197226
POSTHOG_API_HOST: apiHost,
198227
POSTHOG_AUTH_HEADER: `Bearer ${apiKey}`,
199228
ANTHROPIC_API_KEY: apiKey,
200229
ANTHROPIC_AUTH_TOKEN: apiKey,
201230
ANTHROPIC_BASE_URL: `${apiHost}/api/projects/${projectId}/llm_gateway`,
231+
// Ensure we can run node in the packaged app
232+
ELECTRON_RUN_AS_NODE: "1",
202233
};
203234

204235
const mcpOverrides = {};
@@ -216,9 +247,16 @@ export function registerAgentIpc(
216247
env: envOverrides,
217248
mcpServers: mcpOverrides,
218249
pathToClaudeCodeExecutable: getClaudeCliPath(),
250+
// Still pass this, but the PATH hack above is the real fix
251+
nodePath: process.execPath,
219252
},
220253
});
221254

255+
// Clean up mock node dir
256+
try {
257+
rmSync(mockNodeDir, { recursive: true, force: true });
258+
} catch {}
259+
222260
emitToRenderer({ type: "done", success: true, ts: Date.now() });
223261
} catch (err) {
224262
console.error("[agent] task execution failed", err);

packages/agent/src/agent.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -247,15 +247,20 @@ export class Agent {
247247
});
248248

249249
const results = [];
250-
for await (const message of response) {
251-
this.logger.debug("Received message in direct run", message);
252-
// Emit raw SDK event
253-
this.emitEvent(this.adapter.createRawSDKEvent(message));
254-
const transformedEvents = this.adapter.transform(message);
255-
for (const event of transformedEvents) {
256-
this.emitEvent(event);
250+
try {
251+
for await (const message of response) {
252+
this.logger.debug("Received message in direct run", message);
253+
// Emit raw SDK event
254+
this.emitEvent(this.adapter.createRawSDKEvent(message));
255+
const transformedEvents = this.adapter.transform(message);
256+
for (const event of transformedEvents) {
257+
this.emitEvent(event);
258+
}
259+
results.push(message);
257260
}
258-
results.push(message);
261+
} catch (error) {
262+
this.logger.error("Error during direct run", error);
263+
throw error;
259264
}
260265

261266
return { results };

packages/agent/src/workflow/steps/build.ts

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -87,20 +87,25 @@ export const buildStep: WorkflowStepRunner = async ({ step, context }) => {
8787
// Track todos from TodoWrite tool calls
8888
const todoManager = new TodoManager(context.fileManager, stepLogger);
8989

90-
for await (const message of response) {
91-
emitEvent(adapter.createRawSDKEvent(message));
92-
const transformedEvents = adapter.transform(message);
93-
for (const event of transformedEvents) {
94-
emitEvent(event);
95-
}
96-
97-
const todoList = await todoManager.checkAndPersistFromMessage(
98-
message,
99-
task.id,
100-
);
101-
if (todoList) {
102-
emitEvent(adapter.createArtifactEvent("todos", todoList));
90+
try {
91+
for await (const message of response) {
92+
emitEvent(adapter.createRawSDKEvent(message));
93+
const transformedEvents = adapter.transform(message);
94+
for (const event of transformedEvents) {
95+
emitEvent(event);
96+
}
97+
98+
const todoList = await todoManager.checkAndPersistFromMessage(
99+
message,
100+
task.id,
101+
);
102+
if (todoList) {
103+
emitEvent(adapter.createArtifactEvent("todos", todoList));
104+
}
103105
}
106+
} catch (error) {
107+
stepLogger.error("Error during build step query", error);
108+
throw error;
104109
}
105110

106111
// Finalize: commit any remaining changes and optionally push

packages/agent/src/workflow/steps/plan.ts

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -110,29 +110,34 @@ export const planStep: WorkflowStepRunner = async ({ step, context }) => {
110110
const todoManager = new TodoManager(fileManager, stepLogger);
111111

112112
let planContent = "";
113-
for await (const message of response) {
114-
emitEvent(adapter.createRawSDKEvent(message));
115-
const transformedEvents = adapter.transform(message);
116-
for (const event of transformedEvents) {
117-
emitEvent(event);
118-
}
113+
try {
114+
for await (const message of response) {
115+
emitEvent(adapter.createRawSDKEvent(message));
116+
const transformedEvents = adapter.transform(message);
117+
for (const event of transformedEvents) {
118+
emitEvent(event);
119+
}
119120

120-
const todoList = await todoManager.checkAndPersistFromMessage(
121-
message,
122-
task.id,
123-
);
124-
if (todoList) {
125-
emitEvent(adapter.createArtifactEvent("todos", todoList));
126-
}
121+
const todoList = await todoManager.checkAndPersistFromMessage(
122+
message,
123+
task.id,
124+
);
125+
if (todoList) {
126+
emitEvent(adapter.createArtifactEvent("todos", todoList));
127+
}
127128

128-
// Extract text content for plan
129-
if (message.type === "assistant" && message.message?.content) {
130-
for (const block of message.message.content) {
131-
if (block.type === "text" && block.text) {
132-
planContent += `${block.text}\n`;
129+
// Extract text content for plan
130+
if (message.type === "assistant" && message.message?.content) {
131+
for (const block of message.message.content) {
132+
if (block.type === "text" && block.text) {
133+
planContent += `${block.text}\n`;
134+
}
133135
}
134136
}
135137
}
138+
} catch (error) {
139+
stepLogger.error("Error during plan step query", error);
140+
throw error;
136141
}
137142

138143
if (planContent.trim()) {

packages/agent/src/workflow/steps/research.ts

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -87,19 +87,24 @@ export const researchStep: WorkflowStepRunner = async ({ step, context }) => {
8787
});
8888

8989
let jsonContent = "";
90-
for await (const message of response) {
91-
emitEvent(adapter.createRawSDKEvent(message));
92-
const transformedEvents = adapter.transform(message);
93-
for (const event of transformedEvents) {
94-
emitEvent(event);
95-
}
96-
if (message.type === "assistant" && message.message?.content) {
97-
for (const c of message.message.content) {
98-
if (c.type === "text" && c.text) {
99-
jsonContent += c.text;
90+
try {
91+
for await (const message of response) {
92+
emitEvent(adapter.createRawSDKEvent(message));
93+
const transformedEvents = adapter.transform(message);
94+
for (const event of transformedEvents) {
95+
emitEvent(event);
96+
}
97+
if (message.type === "assistant" && message.message?.content) {
98+
for (const c of message.message.content) {
99+
if (c.type === "text" && c.text) {
100+
jsonContent += c.text;
101+
}
100102
}
101103
}
102104
}
105+
} catch (error) {
106+
stepLogger.error("Error during research step query", error);
107+
throw error;
103108
}
104109

105110
if (!jsonContent.trim()) {

0 commit comments

Comments
 (0)