runtimeuse 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/.claude/settings.local.json +7 -0
  2. package/README.md +3 -4
  3. package/dist/artifact-manager.d.ts.map +1 -1
  4. package/dist/artifact-manager.js +0 -11
  5. package/dist/artifact-manager.js.map +1 -1
  6. package/dist/artifact-manager.test.js +1 -16
  7. package/dist/artifact-manager.test.js.map +1 -1
  8. package/dist/cli.js +0 -2
  9. package/dist/cli.js.map +1 -1
  10. package/dist/command-handler.d.ts +8 -4
  11. package/dist/command-handler.d.ts.map +1 -1
  12. package/dist/command-handler.js +10 -16
  13. package/dist/command-handler.js.map +1 -1
  14. package/dist/command-handler.test.js +101 -113
  15. package/dist/command-handler.test.js.map +1 -1
  16. package/dist/index.d.ts +2 -2
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +1 -1
  19. package/dist/index.js.map +1 -1
  20. package/dist/server.d.ts +0 -1
  21. package/dist/server.d.ts.map +1 -1
  22. package/dist/server.js +0 -1
  23. package/dist/server.js.map +1 -1
  24. package/dist/session.d.ts +0 -1
  25. package/dist/session.d.ts.map +1 -1
  26. package/dist/session.js +30 -67
  27. package/dist/session.js.map +1 -1
  28. package/dist/session.test.js +15 -27
  29. package/dist/session.test.js.map +1 -1
  30. package/dist/types.d.ts +3 -8
  31. package/dist/types.d.ts.map +1 -1
  32. package/package.json +1 -1
  33. package/src/artifact-manager.test.ts +1 -19
  34. package/src/artifact-manager.ts +0 -14
  35. package/src/cli.ts +0 -2
  36. package/src/command-handler.test.ts +111 -182
  37. package/src/command-handler.ts +18 -25
  38. package/src/index.ts +1 -5
  39. package/src/server.ts +0 -2
  40. package/src/session.test.ts +28 -48
  41. package/src/session.ts +30 -72
  42. package/src/types.ts +2 -17
  43. package/dist/default-handler.d.ts +0 -3
  44. package/dist/default-handler.d.ts.map +0 -1
  45. package/dist/default-handler.js +0 -76
  46. package/dist/default-handler.js.map +0 -1
package/src/server.ts CHANGED
@@ -7,7 +7,6 @@ import { defaultLogger, type Logger } from "./logger.js";
7
7
  export interface RuntimeUseServerConfig {
8
8
  handler: AgentHandler;
9
9
  port?: number;
10
- defaultModel?: string;
11
10
  uploadTimeoutMs?: number;
12
11
  artifactWaitMs?: number;
13
12
  postInvocationDelayMs?: number;
@@ -34,7 +33,6 @@ export class RuntimeUseServer {
34
33
  const sessionConfig: SessionConfig = {
35
34
  handler: this.config.handler,
36
35
  uploadTracker,
37
- defaultModel: this.config.defaultModel,
38
36
  uploadTimeoutMs: this.config.uploadTimeoutMs,
39
37
  artifactWaitMs: this.config.artifactWaitMs,
40
38
  postInvocationDelayMs: this.config.postInvocationDelayMs,
@@ -39,17 +39,29 @@ vi.mock("fs", async () => {
39
39
  ...actual,
40
40
  default: {
41
41
  ...actual,
42
- createWriteStream: vi.fn(() => ({ write: vi.fn(), end: vi.fn(), close: vi.fn() })),
42
+ createWriteStream: vi.fn(() => ({
43
+ write: vi.fn(),
44
+ end: vi.fn(),
45
+ close: vi.fn(),
46
+ })),
43
47
  },
44
48
  };
45
49
  });
46
50
 
47
51
  import { WebSocketSession, type SessionConfig } from "./session.js";
48
52
  import { UploadTracker } from "./upload-tracker.js";
49
- import type { AgentHandler, AgentInvocation, AgentResult, MessageSender } from "./agent-handler.js";
53
+ import type {
54
+ AgentHandler,
55
+ AgentInvocation,
56
+ AgentResult,
57
+ MessageSender,
58
+ } from "./agent-handler.js";
50
59
  import CommandHandler from "./command-handler.js";
51
60
 
52
- const mockHandlerRun = vi.fn<(invocation: AgentInvocation, sender: MessageSender) => Promise<AgentResult>>();
61
+ const mockHandlerRun =
62
+ vi.fn<
63
+ (invocation: AgentInvocation, sender: MessageSender) => Promise<AgentResult>
64
+ >();
53
65
 
54
66
  const mockHandler: AgentHandler = {
55
67
  run: mockHandlerRun,
@@ -87,6 +99,7 @@ const INVOCATION_MSG = {
87
99
  system_prompt: "You are a tester",
88
100
  user_prompt: "Test the login flow",
89
101
  secrets_to_redact: ["secret123"],
102
+ artifacts_dir: "/tmp/artifacts",
90
103
  output_format_json_schema_str: JSON.stringify({
91
104
  type: "json_schema",
92
105
  schema: { type: "object" },
@@ -135,8 +148,7 @@ describe("WebSocketSession", () => {
135
148
  mockHandlerRun.mockImplementation(
136
149
  () =>
137
150
  new Promise((r) => {
138
- resolveAgent = () =>
139
- r({ structuredOutput: { success: true } });
151
+ resolveAgent = () => r({ structuredOutput: { success: true } });
140
152
  }),
141
153
  );
142
154
 
@@ -158,8 +170,7 @@ describe("WebSocketSession", () => {
158
170
  mockHandlerRun.mockImplementation(
159
171
  () =>
160
172
  new Promise((r) => {
161
- resolveAgent = () =>
162
- r({ structuredOutput: { success: true } });
173
+ resolveAgent = () => r({ structuredOutput: { success: true } });
163
174
  }),
164
175
  );
165
176
 
@@ -251,9 +262,7 @@ describe("WebSocketSession", () => {
251
262
  });
252
263
 
253
264
  it("sends error message when agent throws", async () => {
254
- mockHandlerRun.mockRejectedValueOnce(
255
- new Error("agent crashed"),
256
- );
265
+ mockHandlerRun.mockRejectedValueOnce(new Error("agent crashed"));
257
266
 
258
267
  const { session, ws } = createSession();
259
268
  const done = session.run();
@@ -336,36 +345,7 @@ describe("WebSocketSession", () => {
336
345
  });
337
346
 
338
347
  describe("pre-agent invocation commands", () => {
339
- it("executes pre-agent commands and sends success result", async () => {
340
- mockCommandExecute.mockResolvedValueOnce({ exitCode: 0 });
341
-
342
- const { session, ws } = createSession();
343
- const done = session.run();
344
- sendMessage(ws, {
345
- ...INVOCATION_MSG,
346
- pre_agent_invocation_commands: [
347
- { command: "npm test", cwd: "/app", env: { CI: "true" } },
348
- ],
349
- });
350
- await done;
351
-
352
- expect(CommandHandler).toHaveBeenCalledWith(
353
- { command: "npm test", cwd: "/app", env: { CI: "true" } },
354
- expect.any(Object),
355
- expect.any(Object),
356
- expect.any(Object),
357
- expect.any(AbortController),
358
- expect.any(Function),
359
- expect.any(Function),
360
- );
361
-
362
- const sent = parseSentMessages(ws);
363
- const result = sent.find((m) => m.message_type === "result_message");
364
- expect(result).toBeDefined();
365
- expect(result!.structured_output.success).toBe(true);
366
- });
367
-
368
- it("does not call handler.run when pre-agent command succeeds", async () => {
348
+ it("continues to agent when pre-agent command exits with 0", async () => {
369
349
  mockCommandExecute.mockResolvedValueOnce({ exitCode: 0 });
370
350
 
371
351
  const { session, ws } = createSession();
@@ -376,10 +356,10 @@ describe("WebSocketSession", () => {
376
356
  });
377
357
  await done;
378
358
 
379
- expect(mockHandlerRun).not.toHaveBeenCalled();
359
+ expect(mockHandlerRun).toHaveBeenCalled();
380
360
  });
381
361
 
382
- it("sends failure result when pre-agent command exits with non-zero code", async () => {
362
+ it("sends error message when pre-agent command exits with non-zero code", async () => {
383
363
  mockCommandExecute.mockResolvedValueOnce({
384
364
  exitCode: 1,
385
365
  error: new Error("test failed"),
@@ -394,9 +374,9 @@ describe("WebSocketSession", () => {
394
374
  await done;
395
375
 
396
376
  const sent = parseSentMessages(ws);
397
- const result = sent.find((m) => m.message_type === "result_message");
398
- expect(result).toBeDefined();
399
- expect(result!.structured_output.success).toBe(false);
377
+ const error = sent.find((m) => m.message_type === "error_message");
378
+ expect(error).toBeDefined();
379
+ expect(error!.error).toContain("Command failed with exit code: 1");
400
380
  });
401
381
 
402
382
  it("sends error and result messages when command execution throws", async () => {
@@ -414,9 +394,9 @@ describe("WebSocketSession", () => {
414
394
  const errors = sent.filter((m) => m.message_type === "error_message");
415
395
  expect(errors.length).toBeGreaterThan(0);
416
396
 
417
- const result = sent.find((m) => m.message_type === "result_message");
418
- expect(result).toBeDefined();
419
- expect(result!.structured_output.success).toBe(false);
397
+ const error = sent.find((m) => m.message_type === "error_message");
398
+ expect(error).toBeDefined();
399
+ expect(error!.error).toContain("spawn failed");
420
400
  });
421
401
 
422
402
  it("runs agent normally when no pre-agent commands are provided", async () => {
package/src/session.ts CHANGED
@@ -1,5 +1,3 @@
1
- import fs from "fs";
2
- import path from "path";
3
1
  import { WebSocket } from "ws";
4
2
 
5
3
  import type { AgentHandler, MessageSender } from "./agent-handler.js";
@@ -13,14 +11,12 @@ import type {
13
11
  } from "./types.js";
14
12
  import { sleep } from "./utils.js";
15
13
  import { createLogger, defaultLogger, type Logger } from "./logger.js";
16
- import { DEFAULT_ARTIFACTS_DIR } from "./constants.js";
17
14
  import CommandHandler from "./command-handler.js";
18
15
  import DownloadHandler from "./download-handler.js";
19
16
 
20
17
  export interface SessionConfig {
21
18
  handler: AgentHandler;
22
19
  uploadTracker: UploadTracker;
23
- defaultModel?: string;
24
20
  uploadTimeoutMs?: number;
25
21
  artifactWaitMs?: number;
26
22
  postInvocationDelayMs?: number;
@@ -129,20 +125,21 @@ export class WebSocketSession {
129
125
  this.logger = createLogger(message.source_id);
130
126
  this.config.uploadTracker.setLogger(this.logger);
131
127
 
132
- const artifactsDir = message.artifacts_dir ?? DEFAULT_ARTIFACTS_DIR;
133
- this.artifactManager = new ArtifactManager({
134
- artifactsDir,
135
- uploadTracker: this.config.uploadTracker,
136
- send: (msg) => this.send(msg),
137
- });
138
- this.artifactManager.setLogger(this.logger);
128
+ const artifactsDir = message.artifacts_dir;
129
+ if (artifactsDir) {
130
+ this.artifactManager = new ArtifactManager({
131
+ artifactsDir,
132
+ uploadTracker: this.config.uploadTracker,
133
+ send: (msg) => this.send(msg),
134
+ });
135
+ this.artifactManager.setLogger(this.logger);
136
+ }
139
137
 
140
138
  const outputFormat = JSON.parse(message.output_format_json_schema_str) as {
141
139
  type: "json_schema";
142
140
  schema: Record<string, unknown>;
143
141
  };
144
- const model =
145
- message.preferred_model ?? this.config.defaultModel ?? "default";
142
+ const model = message.preferred_model;
146
143
 
147
144
  this.logger.log(`Received invocation: model=${model}`);
148
145
 
@@ -157,87 +154,48 @@ export class WebSocketSession {
157
154
  }
158
155
  }
159
156
  if (message.pre_agent_invocation_commands) {
160
- const stdoutFile = path.join(artifactsDir, `cmd-stdout.out`);
161
- const stderrFile = path.join(artifactsDir, `cmd-stderr.out`);
162
- const stdoutStream = fs.createWriteStream(stdoutFile);
163
- const stderrStream = fs.createWriteStream(stderrFile);
164
-
165
157
  for (const command of message.pre_agent_invocation_commands) {
166
158
  try {
167
159
  this.logger.log(
168
160
  `Executing command: ${command.command} in directory: ${command.cwd}`,
169
161
  );
170
- const commandHandler = new CommandHandler(
162
+ const commandHandler = new CommandHandler({
171
163
  command,
172
- this.logger,
173
- stdoutStream,
174
- stderrStream,
175
- this.abortController,
176
- (stdout) =>
164
+ logger: this.logger,
165
+ abortController: this.abortController,
166
+ onStdout: (stdout) =>
177
167
  this.send({
178
168
  message_type: "assistant_message",
179
169
  text_blocks: [stdout],
180
170
  }),
181
- (stderr) =>
171
+ onStderr: (stderr) =>
182
172
  this.send({
183
173
  message_type: "assistant_message",
184
174
  text_blocks: [stderr],
185
175
  }),
186
- );
187
- try {
188
- const result = await commandHandler.execute();
189
- if (result.exitCode === 0) {
190
- this.send({
191
- message_type: "result_message",
192
- structured_output: {
193
- success: true,
194
- steps: [],
195
- summary: "Workflow completed successfully",
196
- },
197
- metadata: {},
198
- });
199
- return;
200
- } else {
201
- this.logger.error(
202
- "Command failed with exit code:",
203
- result.exitCode,
204
- );
205
- this.send({
206
- message_type: "result_message",
207
- structured_output: {
208
- success: false,
209
- steps: [],
210
- summary: "Workflow completed with failure",
211
- },
212
- metadata: {},
213
- });
214
- return;
215
- }
216
- } catch (error) {
217
- this.logger.error("Error executing command:", error);
176
+ });
177
+
178
+ const result = await commandHandler.execute();
179
+ if (result.exitCode !== 0) {
180
+ this.logger.error(
181
+ "Command failed with exit code:",
182
+ result.exitCode,
183
+ );
218
184
  this.send({
219
185
  message_type: "error_message",
220
- error: String(error),
221
- metadata: {},
222
- });
223
- this.send({
224
- message_type: "result_message",
225
- structured_output: {
226
- success: false,
227
- steps: [],
228
- summary: "Workflow completed with failure",
229
- },
186
+ error: "Command failed with exit code: " + result.exitCode,
230
187
  metadata: {},
231
188
  });
232
- throw error;
189
+ return;
233
190
  }
234
191
  } catch (error) {
235
- this.logger.error("Error executing command:", error);
192
+ this.logger.error(
193
+ "Error executing pre-agent invocation command:",
194
+ error,
195
+ );
236
196
  throw error;
237
197
  }
238
198
  }
239
- stderrStream.close();
240
- stdoutStream.close();
241
199
  }
242
200
 
243
201
  const sender: MessageSender = {
@@ -289,7 +247,7 @@ export class WebSocketSession {
289
247
  this.logger.error("Error in agent execution:", error);
290
248
  this.send({
291
249
  message_type: "error_message",
292
- error: String(error),
250
+ error: error instanceof Error ? error.message : JSON.stringify(error),
293
251
  metadata: {},
294
252
  });
295
253
  }
package/src/types.ts CHANGED
@@ -17,7 +17,7 @@ interface InvocationMessage {
17
17
  secrets_to_redact: string[];
18
18
  agent_env?: Record<string, string>;
19
19
  output_format_json_schema_str: string;
20
- preferred_model?: string;
20
+ preferred_model: string;
21
21
  artifacts_dir?: string;
22
22
  pre_agent_invocation_commands?: Command[];
23
23
  post_agent_invocation_commands?: Command[];
@@ -42,14 +42,6 @@ interface AssistantMessage {
42
42
 
43
43
  interface ArtifactUploadRequestMessage {
44
44
  message_type: "artifact_upload_request_message";
45
- artifact_type:
46
- | "screenshot"
47
- | "video"
48
- | "tool_calls"
49
- | "javascript"
50
- | "python"
51
- | "shellscript"
52
- | "other";
53
45
  filename: string;
54
46
  filepath: string;
55
47
  }
@@ -68,17 +60,11 @@ interface ErrorMessage {
68
60
  metadata: Record<string, unknown>;
69
61
  }
70
62
 
71
- interface StreamEndMessage {
72
- message_type: "stream_end";
73
- content: string;
74
- }
75
-
76
63
  type OutgoingMessage =
77
64
  | ResultMessage
78
65
  | AssistantMessage
79
66
  | ArtifactUploadRequestMessage
80
- | ErrorMessage
81
- | StreamEndMessage;
67
+ | ErrorMessage;
82
68
 
83
69
  type IncomingMessage =
84
70
  | InvocationMessage
@@ -95,7 +81,6 @@ export type {
95
81
  ArtifactUploadRequestMessage,
96
82
  ArtifactUploadResponseMessage,
97
83
  ErrorMessage,
98
- StreamEndMessage,
99
84
  Command,
100
85
  RuntimeEnvironmentDownloadable,
101
86
  };
@@ -1,3 +0,0 @@
1
- import type { AgentHandler } from "./agent-handler.js";
2
- export declare const defaultHandler: AgentHandler;
3
- //# sourceMappingURL=default-handler.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"default-handler.d.ts","sourceRoot":"","sources":["../src/default-handler.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,YAAY,EAIb,MAAM,oBAAoB,CAAC;AAkB5B,eAAO,MAAM,cAAc,EAAE,YAsE5B,CAAC"}
@@ -1,76 +0,0 @@
1
- import { query } from "@anthropic-ai/claude-agent-sdk";
2
- function extractTextFromContent(content) {
3
- const parts = [];
4
- for (const block of content) {
5
- if (typeof block === "object" &&
6
- block !== null &&
7
- "type" in block &&
8
- block.type === "text" &&
9
- "text" in block) {
10
- parts.push(String(block.text));
11
- }
12
- }
13
- return parts.join("\n");
14
- }
15
- export const defaultHandler = {
16
- async run(invocation, sender) {
17
- const abortController = new AbortController();
18
- const onAbort = () => abortController.abort();
19
- invocation.signal.addEventListener("abort", onAbort, { once: true });
20
- try {
21
- const conversation = query({
22
- prompt: invocation.userPrompt,
23
- options: {
24
- systemPrompt: invocation.systemPrompt,
25
- model: invocation.model,
26
- outputFormat: invocation.outputFormat,
27
- abortController,
28
- cwd: process.cwd(),
29
- env: { ...process.env, ...invocation.env },
30
- tools: { type: "preset", preset: "claude_code" },
31
- permissionMode: "bypassPermissions",
32
- allowDangerouslySkipPermissions: true,
33
- },
34
- });
35
- let structuredOutput = {};
36
- const metadata = {};
37
- for await (const message of conversation) {
38
- if (message.type === "assistant") {
39
- const text = extractTextFromContent(message.message?.content ?? []);
40
- if (text) {
41
- sender.sendAssistantMessage([text]);
42
- }
43
- }
44
- else if (message.type === "result") {
45
- metadata.duration_ms = message.duration_ms;
46
- metadata.duration_api_ms = message.duration_api_ms;
47
- metadata.num_turns = message.num_turns;
48
- metadata.total_cost_usd = message.total_cost_usd;
49
- metadata.usage = message.usage;
50
- metadata.session_id = message.session_id;
51
- if (message.subtype === "success") {
52
- if (message.structured_output != null) {
53
- structuredOutput =
54
- message.structured_output;
55
- }
56
- else {
57
- structuredOutput = { result: message.result };
58
- }
59
- }
60
- else {
61
- structuredOutput = {
62
- error: message.subtype,
63
- errors: "errors" in message ? message.errors : [],
64
- };
65
- sender.sendErrorMessage(`Agent ended with ${message.subtype}`, metadata);
66
- }
67
- }
68
- }
69
- return { structuredOutput, metadata };
70
- }
71
- finally {
72
- invocation.signal.removeEventListener("abort", onAbort);
73
- }
74
- },
75
- };
76
- //# sourceMappingURL=default-handler.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"default-handler.js","sourceRoot":"","sources":["../src/default-handler.ts"],"names":[],"mappings":"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"}