@agent-relay/sdk 3.2.9 → 3.2.10

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 (70) hide show
  1. package/bin/agent-relay-broker-darwin-arm64 +0 -0
  2. package/bin/agent-relay-broker-darwin-x64 +0 -0
  3. package/bin/agent-relay-broker-linux-arm64 +0 -0
  4. package/bin/agent-relay-broker-linux-x64 +0 -0
  5. package/dist/workflows/__tests__/cli-session-collector.test.d.ts +2 -0
  6. package/dist/workflows/__tests__/cli-session-collector.test.d.ts.map +1 -0
  7. package/dist/workflows/__tests__/cli-session-collector.test.js +54 -0
  8. package/dist/workflows/__tests__/cli-session-collector.test.js.map +1 -0
  9. package/dist/workflows/__tests__/collectors/claude.test.d.ts +2 -0
  10. package/dist/workflows/__tests__/collectors/claude.test.d.ts.map +1 -0
  11. package/dist/workflows/__tests__/collectors/claude.test.js +85 -0
  12. package/dist/workflows/__tests__/collectors/claude.test.js.map +1 -0
  13. package/dist/workflows/__tests__/collectors/codex.test.d.ts +2 -0
  14. package/dist/workflows/__tests__/collectors/codex.test.d.ts.map +1 -0
  15. package/dist/workflows/__tests__/collectors/codex.test.js +67 -0
  16. package/dist/workflows/__tests__/collectors/codex.test.js.map +1 -0
  17. package/dist/workflows/__tests__/collectors/opencode.test.d.ts +2 -0
  18. package/dist/workflows/__tests__/collectors/opencode.test.d.ts.map +1 -0
  19. package/dist/workflows/__tests__/collectors/opencode.test.js +119 -0
  20. package/dist/workflows/__tests__/collectors/opencode.test.js.map +1 -0
  21. package/dist/workflows/__tests__/run-summary-table.test.d.ts +2 -0
  22. package/dist/workflows/__tests__/run-summary-table.test.d.ts.map +1 -0
  23. package/dist/workflows/__tests__/run-summary-table.test.js +130 -0
  24. package/dist/workflows/__tests__/run-summary-table.test.js.map +1 -0
  25. package/dist/workflows/__tests__/step-cwd.test.d.ts +2 -0
  26. package/dist/workflows/__tests__/step-cwd.test.d.ts.map +1 -0
  27. package/dist/workflows/__tests__/step-cwd.test.js +42 -0
  28. package/dist/workflows/__tests__/step-cwd.test.js.map +1 -0
  29. package/dist/workflows/builder.d.ts +2 -0
  30. package/dist/workflows/builder.d.ts.map +1 -1
  31. package/dist/workflows/builder.js +4 -0
  32. package/dist/workflows/builder.js.map +1 -1
  33. package/dist/workflows/cli-session-collector.d.ts +39 -0
  34. package/dist/workflows/cli-session-collector.d.ts.map +1 -0
  35. package/dist/workflows/cli-session-collector.js +23 -0
  36. package/dist/workflows/cli-session-collector.js.map +1 -0
  37. package/dist/workflows/cli.js +228 -48
  38. package/dist/workflows/cli.js.map +1 -1
  39. package/dist/workflows/collectors/claude.d.ts +6 -0
  40. package/dist/workflows/collectors/claude.d.ts.map +1 -0
  41. package/dist/workflows/collectors/claude.js +330 -0
  42. package/dist/workflows/collectors/claude.js.map +1 -0
  43. package/dist/workflows/collectors/codex.d.ts +18 -0
  44. package/dist/workflows/collectors/codex.d.ts.map +1 -0
  45. package/dist/workflows/collectors/codex.js +265 -0
  46. package/dist/workflows/collectors/codex.js.map +1 -0
  47. package/dist/workflows/collectors/opencode.d.ts +6 -0
  48. package/dist/workflows/collectors/opencode.d.ts.map +1 -0
  49. package/dist/workflows/collectors/opencode.js +178 -0
  50. package/dist/workflows/collectors/opencode.js.map +1 -0
  51. package/dist/workflows/index.d.ts +3 -0
  52. package/dist/workflows/index.d.ts.map +1 -1
  53. package/dist/workflows/index.js +3 -0
  54. package/dist/workflows/index.js.map +1 -1
  55. package/dist/workflows/listr-renderer.d.ts +26 -0
  56. package/dist/workflows/listr-renderer.d.ts.map +1 -0
  57. package/dist/workflows/listr-renderer.js +232 -0
  58. package/dist/workflows/listr-renderer.js.map +1 -0
  59. package/dist/workflows/run-summary-table.d.ts +4 -0
  60. package/dist/workflows/run-summary-table.d.ts.map +1 -0
  61. package/dist/workflows/run-summary-table.js +98 -0
  62. package/dist/workflows/run-summary-table.js.map +1 -0
  63. package/dist/workflows/runner.d.ts +11 -0
  64. package/dist/workflows/runner.d.ts.map +1 -1
  65. package/dist/workflows/runner.js +91 -26
  66. package/dist/workflows/runner.js.map +1 -1
  67. package/dist/workflows/types.d.ts +2 -0
  68. package/dist/workflows/types.d.ts.map +1 -1
  69. package/dist/workflows/types.js.map +1 -1
  70. package/package.json +4 -2
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=cli-session-collector.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-session-collector.test.d.ts","sourceRoot":"","sources":["../../../src/workflows/__tests__/cli-session-collector.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,54 @@
1
+ import { afterEach, describe, expect, it, vi } from 'vitest';
2
+ import { mkdtempSync, rmSync } from 'node:fs';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { collectCliSession } from '../cli-session-collector.js';
6
+ const tempDirs = [];
7
+ const originalHome = process.env.HOME;
8
+ function makeTempDir(prefix) {
9
+ const dir = mkdtempSync(path.join(os.tmpdir(), prefix));
10
+ tempDirs.push(dir);
11
+ return dir;
12
+ }
13
+ async function importCollectorsWithHome(homeDir) {
14
+ process.env.HOME = homeDir;
15
+ vi.resetModules();
16
+ const [claudeModule, opencodeModule] = await Promise.all([
17
+ import('../collectors/claude.js'),
18
+ import('../collectors/opencode.js'),
19
+ ]);
20
+ return {
21
+ ClaudeCodeCollector: claudeModule.ClaudeCodeCollector,
22
+ OpenCodeCollector: opencodeModule.OpenCodeCollector,
23
+ };
24
+ }
25
+ afterEach(() => {
26
+ vi.resetModules();
27
+ process.env.HOME = originalHome;
28
+ while (tempDirs.length > 0) {
29
+ rmSync(tempDirs.pop(), { recursive: true, force: true });
30
+ }
31
+ });
32
+ describe('cli-session-collector', () => {
33
+ it('returns null for an unknown CLI', async () => {
34
+ const report = await collectCliSession({
35
+ cli: 'gemini',
36
+ cwd: '/tmp/project',
37
+ startedAt: 1000,
38
+ completedAt: 2000,
39
+ });
40
+ expect(report).toBeNull();
41
+ });
42
+ it('reports canCollect=false when configured data stores do not exist', async () => {
43
+ const homeDir = makeTempDir('cli-session-collector-empty-home-');
44
+ const { ClaudeCodeCollector, OpenCodeCollector } = await importCollectorsWithHome(homeDir);
45
+ const { CodexCollector } = await import('../collectors/codex.js');
46
+ expect(new ClaudeCodeCollector().canCollect()).toBe(false);
47
+ expect(new OpenCodeCollector().canCollect()).toBe(false);
48
+ expect(new CodexCollector({
49
+ historyPath: path.join(homeDir, 'missing-history.jsonl'),
50
+ statePath: path.join(homeDir, 'missing-state.sqlite'),
51
+ }).canCollect()).toBe(false);
52
+ });
53
+ });
54
+ //# sourceMappingURL=cli-session-collector.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-session-collector.test.js","sourceRoot":"","sources":["../../../src/workflows/__tests__/cli-session-collector.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAEhE,MAAM,QAAQ,GAAa,EAAE,CAAC;AAC9B,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;AAEtC,SAAS,WAAW,CAAC,MAAc;IACjC,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC;IACxD,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACnB,OAAO,GAAG,CAAC;AACb,CAAC;AAED,KAAK,UAAU,wBAAwB,CAAC,OAAe;IACrD,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,OAAO,CAAC;IAC3B,EAAE,CAAC,YAAY,EAAE,CAAC;IAClB,MAAM,CAAC,YAAY,EAAE,cAAc,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QACvD,MAAM,CAAC,yBAAyB,CAAC;QACjC,MAAM,CAAC,2BAA2B,CAAC;KACpC,CAAC,CAAC;IACH,OAAO;QACL,mBAAmB,EAAE,YAAY,CAAC,mBAAmB;QACrD,iBAAiB,EAAE,cAAc,CAAC,iBAAiB;KACpD,CAAC;AACJ,CAAC;AAED,SAAS,CAAC,GAAG,EAAE;IACb,EAAE,CAAC,YAAY,EAAE,CAAC;IAClB,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,YAAY,CAAC;IAChC,OAAO,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3B,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5D,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;IACrC,EAAE,CAAC,iCAAiC,EAAE,KAAK,IAAI,EAAE;QAC/C,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC;YACrC,GAAG,EAAE,QAAQ;YACb,GAAG,EAAE,cAAc;YACnB,SAAS,EAAE,IAAI;YACf,WAAW,EAAE,IAAI;SAClB,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC5B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mEAAmE,EAAE,KAAK,IAAI,EAAE;QACjF,MAAM,OAAO,GAAG,WAAW,CAAC,mCAAmC,CAAC,CAAC;QACjE,MAAM,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,GAAG,MAAM,wBAAwB,CAAC,OAAO,CAAC,CAAC;QAC3F,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC,wBAAwB,CAAC,CAAC;QAElE,MAAM,CAAC,IAAI,mBAAmB,EAAE,CAAC,UAAU,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC3D,MAAM,CAAC,IAAI,iBAAiB,EAAE,CAAC,UAAU,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACzD,MAAM,CACJ,IAAI,cAAc,CAAC;YACjB,WAAW,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,uBAAuB,CAAC;YACxD,SAAS,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,sBAAsB,CAAC;SACtD,CAAC,CAAC,UAAU,EAAE,CAChB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAChB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=claude.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude.test.d.ts","sourceRoot":"","sources":["../../../../src/workflows/__tests__/collectors/claude.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,85 @@
1
+ import { afterEach, describe, expect, it, vi } from 'vitest';
2
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ const tempDirs = [];
6
+ const originalHome = process.env.HOME;
7
+ function makeTempDir(prefix) {
8
+ const dir = mkdtempSync(path.join(os.tmpdir(), prefix));
9
+ tempDirs.push(dir);
10
+ return dir;
11
+ }
12
+ function encodeProjectPath(projectPath) {
13
+ return projectPath.replace(/\//g, '--').replace(/^-+/, '');
14
+ }
15
+ function createClaudeFixture(homeDir, cwd, timestamp) {
16
+ const claudeHome = path.join(homeDir, '.claude');
17
+ const projectsRoot = path.join(claudeHome, 'projects', encodeProjectPath(cwd));
18
+ mkdirSync(projectsRoot, { recursive: true });
19
+ const sessionId = 'session-claude-1';
20
+ writeFileSync(path.join(claudeHome, 'history.jsonl'), [
21
+ JSON.stringify({ timestamp: timestamp - 1000, project: '/other/project', sessionId: 'ignored-session' }),
22
+ JSON.stringify({ timestamp, project: cwd, sessionId }),
23
+ ].join('\n'));
24
+ writeFileSync(path.join(projectsRoot, `${sessionId}.jsonl`), [
25
+ JSON.stringify({ type: 'user', text: 'Investigate the failing command' }),
26
+ JSON.stringify({ type: 'tool_use', name: 'bash' }),
27
+ JSON.stringify({
28
+ type: 'assistant',
29
+ message: {
30
+ model: 'claude-sonnet-4',
31
+ provider: 'anthropic',
32
+ usage: {
33
+ input_tokens: 42,
34
+ output_tokens: 24,
35
+ cache_read_input_tokens: 7,
36
+ },
37
+ content: [{ text: 'Final concise summary' }],
38
+ },
39
+ }),
40
+ ].join('\n'));
41
+ return sessionId;
42
+ }
43
+ async function importCollectorWithHome(homeDir) {
44
+ process.env.HOME = homeDir;
45
+ vi.resetModules();
46
+ const module = await import('../../collectors/claude.js');
47
+ return module.ClaudeCodeCollector;
48
+ }
49
+ afterEach(() => {
50
+ vi.resetModules();
51
+ process.env.HOME = originalHome;
52
+ while (tempDirs.length > 0) {
53
+ rmSync(tempDirs.pop(), { recursive: true, force: true });
54
+ }
55
+ });
56
+ describe('ClaudeCodeCollector', () => {
57
+ it('matches by project path and timestamp and reads the session jsonl', async () => {
58
+ const homeDir = makeTempDir('claude-home-');
59
+ const cwd = '/repo/project';
60
+ const timestamp = 50_000;
61
+ const sessionId = createClaudeFixture(homeDir, cwd, timestamp);
62
+ const ClaudeCodeCollector = await importCollectorWithHome(homeDir);
63
+ const report = await new ClaudeCodeCollector().collect({
64
+ cli: 'claude',
65
+ cwd,
66
+ startedAt: timestamp - 100,
67
+ completedAt: timestamp + 2_000,
68
+ });
69
+ expect(report).not.toBeNull();
70
+ expect(report?.sessionId).toBe(sessionId);
71
+ expect(report?.model).toBe('claude-sonnet-4');
72
+ expect(report?.provider).toBe('anthropic');
73
+ expect(report?.tokens).toEqual({ input: 42, output: 24, cacheRead: 7 });
74
+ expect(report?.turns).toBe(1);
75
+ expect(report?.toolCalls).toEqual([{ name: 'bash', count: 1 }]);
76
+ expect(report?.summary).toBe('Final concise summary');
77
+ expect(report?.finalStatus).toBe('completed');
78
+ });
79
+ it('returns false from canCollect when history and project files are missing', async () => {
80
+ const homeDir = makeTempDir('claude-empty-home-');
81
+ const ClaudeCodeCollector = await importCollectorWithHome(homeDir);
82
+ expect(new ClaudeCodeCollector().canCollect()).toBe(false);
83
+ });
84
+ });
85
+ //# sourceMappingURL=claude.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude.test.js","sourceRoot":"","sources":["../../../../src/workflows/__tests__/collectors/claude.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=codex.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex.test.d.ts","sourceRoot":"","sources":["../../../../src/workflows/__tests__/collectors/codex.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,67 @@
1
+ import { afterEach, describe, expect, it } from 'vitest';
2
+ import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { DatabaseSync } from 'node:sqlite';
6
+ import { CodexCollector } from '../../collectors/codex.js';
7
+ const tempDirs = [];
8
+ function makeTempDir(prefix) {
9
+ const dir = mkdtempSync(path.join(os.tmpdir(), prefix));
10
+ tempDirs.push(dir);
11
+ return dir;
12
+ }
13
+ function createCodexFixture(tempDir, cwd, createdAtSeconds) {
14
+ const statePath = path.join(tempDir, 'state_5.sqlite');
15
+ const historyPath = path.join(tempDir, 'history.jsonl');
16
+ const db = new DatabaseSync(statePath);
17
+ db.exec(`
18
+ CREATE TABLE threads (
19
+ id TEXT PRIMARY KEY,
20
+ cwd TEXT,
21
+ model_provider TEXT,
22
+ tokens_used INTEGER,
23
+ created_at INTEGER,
24
+ updated_at INTEGER
25
+ );
26
+ CREATE TABLE logs (
27
+ thread_id TEXT,
28
+ ts INTEGER,
29
+ level TEXT,
30
+ message TEXT,
31
+ line INTEGER
32
+ );
33
+ `);
34
+ db.prepare('INSERT INTO threads (id, cwd, model_provider, tokens_used, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)').run('thread-1', cwd, 'openai/gpt-5', 321, createdAtSeconds, createdAtSeconds + 3);
35
+ db.prepare('INSERT INTO logs (thread_id, ts, level, message, line) VALUES (?, ?, ?, ?, ?)').run('thread-1', createdAtSeconds + 1, 'error', 'Command failed: bad exit code', 12);
36
+ db.close();
37
+ writeFileSync(historyPath, `${JSON.stringify({ session_id: 'thread-1', ts: createdAtSeconds, text: 'history' })}\n`);
38
+ return { statePath, historyPath };
39
+ }
40
+ afterEach(() => {
41
+ while (tempDirs.length > 0) {
42
+ rmSync(tempDirs.pop(), { recursive: true, force: true });
43
+ }
44
+ });
45
+ describe('CodexCollector', () => {
46
+ it('matches by cwd and time window and extracts errors from logs', async () => {
47
+ const tempDir = makeTempDir('codex-fixture-');
48
+ const cwd = '/repo/codex-project';
49
+ const createdAtSeconds = 100;
50
+ const { statePath, historyPath } = createCodexFixture(tempDir, cwd, createdAtSeconds);
51
+ const collector = new CodexCollector({ statePath, historyPath });
52
+ const report = await collector.collect({
53
+ cli: 'codex',
54
+ cwd,
55
+ startedAt: 100_000,
56
+ completedAt: 105_000,
57
+ });
58
+ expect(report).not.toBeNull();
59
+ expect(report?.sessionId).toBe('thread-1');
60
+ expect(report?.provider).toBe('openai');
61
+ expect(report?.model).toBe('gpt-5');
62
+ expect(report?.tokens).toEqual({ input: 321, output: 0, cacheRead: 0 });
63
+ expect(report?.errors).toEqual([{ turn: 1, text: 'Command failed: bad exit code' }]);
64
+ expect(report?.finalStatus).toBe('failed');
65
+ });
66
+ });
67
+ //# sourceMappingURL=codex.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex.test.js","sourceRoot":"","sources":["../../../../src/workflows/__tests__/collectors/codex.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=opencode.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opencode.test.d.ts","sourceRoot":"","sources":["../../../../src/workflows/__tests__/collectors/opencode.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,119 @@
1
+ import { afterEach, describe, expect, it, vi } from 'vitest';
2
+ import { mkdirSync, mkdtempSync, rmSync } from 'node:fs';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { DatabaseSync } from 'node:sqlite';
6
+ const tempDirs = [];
7
+ const originalHome = process.env.HOME;
8
+ function makeTempDir(prefix) {
9
+ const dir = mkdtempSync(path.join(os.tmpdir(), prefix));
10
+ tempDirs.push(dir);
11
+ return dir;
12
+ }
13
+ function createOpenCodeFixture(homeDir, cwd, sessionCreatedAt) {
14
+ const dbDir = path.join(homeDir, '.local', 'share', 'opencode');
15
+ mkdirSync(dbDir, { recursive: true });
16
+ const dbPath = path.join(dbDir, 'opencode.db');
17
+ const db = new DatabaseSync(dbPath);
18
+ db.exec(`
19
+ CREATE TABLE session (id TEXT PRIMARY KEY, directory TEXT, time_created INTEGER);
20
+ CREATE TABLE message (id TEXT PRIMARY KEY, session_id TEXT, time_created INTEGER, data TEXT);
21
+ CREATE TABLE part (id TEXT PRIMARY KEY, message_id TEXT, session_id TEXT, time_created INTEGER, data TEXT);
22
+ `);
23
+ const insertSession = db.prepare('INSERT INTO session (id, directory, time_created) VALUES (?, ?, ?)');
24
+ const insertMessage = db.prepare('INSERT INTO message (id, session_id, time_created, data) VALUES (?, ?, ?, ?)');
25
+ const insertPart = db.prepare('INSERT INTO part (id, message_id, session_id, time_created, data) VALUES (?, ?, ?, ?, ?)');
26
+ insertSession.run('session-1', cwd, sessionCreatedAt);
27
+ insertSession.run('session-2', '/other/project', sessionCreatedAt + 1000);
28
+ insertMessage.run('msg-1', 'session-1', sessionCreatedAt + 10, JSON.stringify({ role: 'user', tokens: { input: 10, output: 0, cache: { read: 1 } } }));
29
+ insertMessage.run('msg-2', 'session-1', sessionCreatedAt + 20, JSON.stringify({
30
+ role: 'assistant',
31
+ modelID: 'gpt-5',
32
+ providerID: 'openai',
33
+ finish: 'error',
34
+ cost: 1.25,
35
+ tokens: { input: 15, output: 20, cache: { read: 4 } },
36
+ }));
37
+ insertMessage.run('msg-other', 'session-2', sessionCreatedAt + 30, JSON.stringify({ role: 'assistant', modelID: 'ignore-me', providerID: 'other', finish: 'completed' }));
38
+ insertPart.run('part-1', 'msg-1', 'session-1', sessionCreatedAt + 11, JSON.stringify({ type: 'text', text: 'Planning work' }));
39
+ insertPart.run('part-2', 'msg-2', 'session-1', sessionCreatedAt + 21, JSON.stringify({ type: 'tool_call', name: 'write_file' }));
40
+ insertPart.run('part-3', 'msg-2', 'session-1', sessionCreatedAt + 22, JSON.stringify({ type: 'text', text: 'Error: database locked\nCleanup afterwards' }));
41
+ insertPart.run('part-4', 'msg-2', 'session-1', sessionCreatedAt + 23, JSON.stringify({ type: 'text', text: 'Completed summary output' }));
42
+ db.close();
43
+ return dbPath;
44
+ }
45
+ async function importCollectorWithHome(homeDir) {
46
+ process.env.HOME = homeDir;
47
+ vi.resetModules();
48
+ vi.doMock('node:module', () => ({
49
+ createRequire: () => (id) => {
50
+ if (id !== 'better-sqlite3') {
51
+ throw new Error(`Unexpected module request: ${id}`);
52
+ }
53
+ return class BetterSqliteCompat {
54
+ db;
55
+ constructor(filename) {
56
+ this.db = new DatabaseSync(filename, { open: true, readOnly: true });
57
+ }
58
+ prepare(sql) {
59
+ const statement = this.db.prepare(sql);
60
+ return {
61
+ get(params) {
62
+ return statement.get(params);
63
+ },
64
+ all(params) {
65
+ return statement.all(params);
66
+ },
67
+ };
68
+ }
69
+ pragma(_source) {
70
+ return undefined;
71
+ }
72
+ close() {
73
+ this.db.close();
74
+ }
75
+ };
76
+ },
77
+ }));
78
+ const module = await import('../../collectors/opencode.js');
79
+ return module.OpenCodeCollector;
80
+ }
81
+ afterEach(() => {
82
+ vi.resetModules();
83
+ process.env.HOME = originalHome;
84
+ while (tempDirs.length > 0) {
85
+ rmSync(tempDirs.pop(), { recursive: true, force: true });
86
+ }
87
+ });
88
+ describe('OpenCodeCollector', () => {
89
+ it('matches by directory and time window, aggregates tokens, and extracts errors', async () => {
90
+ const homeDir = makeTempDir('opencode-home-');
91
+ const cwd = path.join(homeDir, 'workspace');
92
+ const sessionCreatedAt = 10_000;
93
+ createOpenCodeFixture(homeDir, cwd, sessionCreatedAt);
94
+ const OpenCodeCollector = await importCollectorWithHome(homeDir);
95
+ const collector = new OpenCodeCollector();
96
+ const report = await collector.collect({
97
+ cli: 'opencode',
98
+ cwd,
99
+ startedAt: sessionCreatedAt + 100,
100
+ completedAt: sessionCreatedAt + 500,
101
+ });
102
+ expect(report).not.toBeNull();
103
+ expect(report?.sessionId).toBe('session-1');
104
+ expect(report?.model).toBe('gpt-5');
105
+ expect(report?.provider).toBe('openai');
106
+ expect(report?.tokens).toEqual({ input: 25, output: 20, cacheRead: 5 });
107
+ expect(report?.cost).toBe(1.25);
108
+ expect(report?.toolCalls).toEqual([{ name: 'write_file', count: 1 }]);
109
+ expect(report?.errors).toEqual([{ turn: 3, text: 'Error: database locked' }]);
110
+ expect(report?.finalStatus).toBe('failed');
111
+ expect(report?.summary).toBe('Completed summary output');
112
+ });
113
+ it('returns false from canCollect when the database is missing', async () => {
114
+ const homeDir = makeTempDir('opencode-missing-home-');
115
+ const OpenCodeCollector = await importCollectorWithHome(homeDir);
116
+ expect(new OpenCodeCollector().canCollect()).toBe(false);
117
+ });
118
+ });
119
+ //# sourceMappingURL=opencode.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opencode.test.js","sourceRoot":"","sources":["../../../../src/workflows/__tests__/collectors/opencode.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC7D,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACzD,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,QAAQ,GAAa,EAAE,CAAC;AAC9B,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;AAEtC,SAAS,WAAW,CAAC,MAAc;IACjC,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC;IACxD,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACnB,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,qBAAqB,CAAC,OAAe,EAAE,GAAW,EAAE,gBAAwB;IACnF,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;IAChE,SAAS,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;IAC/C,MAAM,EAAE,GAAG,IAAI,YAAY,CAAC,MAAM,CAAC,CAAC;IAEpC,EAAE,CAAC,IAAI,CAAC;;;;GAIP,CAAC,CAAC;IAEH,MAAM,aAAa,GAAG,EAAE,CAAC,OAAO,CAAC,oEAAoE,CAAC,CAAC;IACvG,MAAM,aAAa,GAAG,EAAE,CAAC,OAAO,CAAC,8EAA8E,CAAC,CAAC;IACjH,MAAM,UAAU,GAAG,EAAE,CAAC,OAAO,CAAC,0FAA0F,CAAC,CAAC;IAE1H,aAAa,CAAC,GAAG,CAAC,WAAW,EAAE,GAAG,EAAE,gBAAgB,CAAC,CAAC;IACtD,aAAa,CAAC,GAAG,CAAC,WAAW,EAAE,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAC,CAAC;IAE1E,aAAa,CAAC,GAAG,CACf,OAAO,EACP,WAAW,EACX,gBAAgB,GAAG,EAAE,EACrB,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CACvF,CAAC;IACF,aAAa,CAAC,GAAG,CACf,OAAO,EACP,WAAW,EACX,gBAAgB,GAAG,EAAE,EACrB,IAAI,CAAC,SAAS,CAAC;QACb,IAAI,EAAE,WAAW;QACjB,OAAO,EAAE,OAAO;QAChB,UAAU,EAAE,QAAQ;QACpB,MAAM,EAAE,OAAO;QACf,IAAI,EAAE,IAAI;QACV,MAAM,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE;KACtD,CAAC,CACH,CAAC;IACF,aAAa,CAAC,GAAG,CACf,WAAW,EACX,WAAW,EACX,gBAAgB,GAAG,EAAE,EACrB,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CACtG,CAAC;IAEF,UAAU,CAAC,GAAG,CACZ,QAAQ,EACR,OAAO,EACP,WAAW,EACX,gBAAgB,GAAG,EAAE,EACrB,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC,CACxD,CAAC;IACF,UAAU,CAAC,GAAG,CACZ,QAAQ,EACR,OAAO,EACP,WAAW,EACX,gBAAgB,GAAG,EAAE,EACrB,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,CAC1D,CAAC;IACF,UAAU,CAAC,GAAG,CACZ,QAAQ,EACR,OAAO,EACP,WAAW,EACX,gBAAgB,GAAG,EAAE,EACrB,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,4CAA4C,EAAE,CAAC,CACrF,CAAC;IACF,UAAU,CAAC,GAAG,CACZ,QAAQ,EACR,OAAO,EACP,WAAW,EACX,gBAAgB,GAAG,EAAE,EACrB,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,0BAA0B,EAAE,CAAC,CACnE,CAAC;IAEF,EAAE,CAAC,KAAK,EAAE,CAAC;IACX,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,KAAK,UAAU,uBAAuB,CAAC,OAAe;IACpD,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,OAAO,CAAC;IAC3B,EAAE,CAAC,YAAY,EAAE,CAAC;IAClB,EAAE,CAAC,MAAM,CAAC,aAAa,EAAE,GAAG,EAAE,CAAC,CAAC;QAC9B,aAAa,EAAE,GAAG,EAAE,CAAC,CAAC,EAAU,EAAE,EAAE;YAClC,IAAI,EAAE,KAAK,gBAAgB,EAAE,CAAC;gBAC5B,MAAM,IAAI,KAAK,CAAC,8BAA8B,EAAE,EAAE,CAAC,CAAC;YACtD,CAAC;YAED,OAAO,MAAM,kBAAkB;gBACZ,EAAE,CAAe;gBAElC,YAAY,QAAgB;oBAC1B,IAAI,CAAC,EAAE,GAAG,IAAI,YAAY,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;gBACvE,CAAC;gBAED,OAAO,CAAC,GAAW;oBACjB,MAAM,SAAS,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;oBACvC,OAAO;wBACL,GAAG,CAAI,MAAgB;4BACrB,OAAO,SAAS,CAAC,GAAG,CAAC,MAAe,CAAkB,CAAC;wBACzD,CAAC;wBACD,GAAG,CAAI,MAAgB;4BACrB,OAAO,SAAS,CAAC,GAAG,CAAC,MAAe,CAAQ,CAAC;wBAC/C,CAAC;qBACF,CAAC;gBACJ,CAAC;gBAED,MAAM,CAAC,OAAe;oBACpB,OAAO,SAAS,CAAC;gBACnB,CAAC;gBAED,KAAK;oBACH,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;gBAClB,CAAC;aACF,CAAC;QACJ,CAAC;KACF,CAAC,CAAC,CAAC;IACJ,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,8BAA8B,CAAC,CAAC;IAC5D,OAAO,MAAM,CAAC,iBAAiB,CAAC;AAClC,CAAC;AAED,SAAS,CAAC,GAAG,EAAE;IACb,EAAE,CAAC,YAAY,EAAE,CAAC;IAClB,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,YAAY,CAAC;IAChC,OAAO,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3B,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5D,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,EAAE,CAAC,8EAA8E,EAAE,KAAK,IAAI,EAAE;QAC5F,MAAM,OAAO,GAAG,WAAW,CAAC,gBAAgB,CAAC,CAAC;QAC9C,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;QAC5C,MAAM,gBAAgB,GAAG,MAAM,CAAC;QAChC,qBAAqB,CAAC,OAAO,EAAE,GAAG,EAAE,gBAAgB,CAAC,CAAC;QACtD,MAAM,iBAAiB,GAAG,MAAM,uBAAuB,CAAC,OAAO,CAAC,CAAC;QAEjE,MAAM,SAAS,GAAG,IAAI,iBAAiB,EAAE,CAAC;QAC1C,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,OAAO,CAAC;YACrC,GAAG,EAAE,UAAU;YACf,GAAG;YACH,SAAS,EAAE,gBAAgB,GAAG,GAAG;YACjC,WAAW,EAAE,gBAAgB,GAAG,GAAG;SACpC,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;QAC9B,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC5C,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACxC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC;QACxE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QACtE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,wBAAwB,EAAE,CAAC,CAAC,CAAC;QAC9E,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC3C,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4DAA4D,EAAE,KAAK,IAAI,EAAE;QAC1E,MAAM,OAAO,GAAG,WAAW,CAAC,wBAAwB,CAAC,CAAC;QACtD,MAAM,iBAAiB,GAAG,MAAM,uBAAuB,CAAC,OAAO,CAAC,CAAC;QAEjE,MAAM,CAAC,IAAI,iBAAiB,EAAE,CAAC,UAAU,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=run-summary-table.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-summary-table.test.d.ts","sourceRoot":"","sources":["../../../src/workflows/__tests__/run-summary-table.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,130 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ import { formatRunSummaryTable } from '../run-summary-table.js';
3
+ vi.mock('@relaycast/sdk', () => ({
4
+ RelayCast: vi.fn(),
5
+ RelayError: class RelayError extends Error {
6
+ },
7
+ }));
8
+ vi.mock('../../relay.js', () => ({
9
+ AgentRelay: vi.fn(),
10
+ }));
11
+ const { WorkflowRunner } = await import('../runner.js');
12
+ describe('formatRunSummaryTable', () => {
13
+ it('renders all-passing steps', () => {
14
+ const output = formatRunSummaryTable([
15
+ { name: 'plan', agent: 'lead', status: 'completed', attempts: 1, durationMs: 1_000 },
16
+ { name: 'implement', agent: 'worker', status: 'completed', attempts: 1, durationMs: 2_000 },
17
+ ], new Map([
18
+ [
19
+ 'plan',
20
+ {
21
+ cli: 'claude',
22
+ sessionId: 's1',
23
+ model: 'claude-sonnet-4',
24
+ provider: 'anthropic',
25
+ durationMs: 1_200,
26
+ cost: 0.75,
27
+ tokens: { input: 100, output: 50, cacheRead: 10 },
28
+ turns: 2,
29
+ toolCalls: [],
30
+ errors: [],
31
+ finalStatus: 'completed',
32
+ summary: 'planned',
33
+ },
34
+ ],
35
+ [
36
+ 'implement',
37
+ {
38
+ cli: 'codex',
39
+ sessionId: 's2',
40
+ model: 'gpt-5',
41
+ provider: 'openai',
42
+ durationMs: 3_400,
43
+ cost: 1.25,
44
+ tokens: { input: 300, output: 90, cacheRead: 20 },
45
+ turns: 4,
46
+ toolCalls: [],
47
+ errors: [{ turn: 2, text: 'Error: recovered after retry' }],
48
+ finalStatus: 'completed',
49
+ summary: 'implemented',
50
+ },
51
+ ],
52
+ ]));
53
+ expect(output).toMatchInlineSnapshot(`
54
+ " Step Status Model Cost Tokens Duration Errors
55
+ plan pass claude-sonnet-4 $0.75 160 1s --
56
+ implement pass gpt-5 $1.25 410 3s 1 (fixed)
57
+ ────────────────────────────────────────────────────────────────────────────────────────────
58
+ Total $2.00 570 5s "
59
+ `);
60
+ });
61
+ it('renders a failed step with the first error line', () => {
62
+ const output = formatRunSummaryTable([{ name: 'broken-step', agent: 'worker', status: 'failed', attempts: 1, durationMs: 1_500, error: 'boom' }], new Map([
63
+ [
64
+ 'broken-step',
65
+ {
66
+ cli: 'opencode',
67
+ sessionId: 's3',
68
+ model: 'gpt-5',
69
+ provider: 'openai',
70
+ durationMs: 1_500,
71
+ cost: 0.01,
72
+ tokens: { input: 10, output: 5, cacheRead: 0 },
73
+ turns: 1,
74
+ toolCalls: [],
75
+ errors: [{ turn: 1, text: 'Error: database locked' }],
76
+ finalStatus: 'failed',
77
+ summary: null,
78
+ },
79
+ ],
80
+ ]));
81
+ expect(output).toContain('broken-step FAIL');
82
+ expect(output).toContain(' └─ Error [turn 1] Error: database locked');
83
+ });
84
+ it('renders deterministic steps without reports using placeholder columns', () => {
85
+ const output = formatRunSummaryTable([{ name: 'lint', agent: 'shell', status: 'completed', attempts: 1, durationMs: 900 }], new Map());
86
+ expect(output).toContain('lint pass --');
87
+ expect(output).toContain('--');
88
+ // No reports means no cost column
89
+ expect(output).not.toContain('Cost');
90
+ });
91
+ it('hides Cost column when no report has reliable cost data', () => {
92
+ const output = formatRunSummaryTable([
93
+ { name: 'gen-code', agent: 'worker', status: 'completed', attempts: 1, durationMs: 5_000 },
94
+ ], new Map([
95
+ [
96
+ 'gen-code',
97
+ {
98
+ cli: 'claude',
99
+ sessionId: 's1',
100
+ model: 'claude-sonnet-4',
101
+ provider: 'anthropic',
102
+ durationMs: 5_000,
103
+ cost: null,
104
+ tokens: { input: 200, output: 80, cacheRead: 0 },
105
+ turns: 3,
106
+ toolCalls: [],
107
+ errors: [],
108
+ finalStatus: 'completed',
109
+ summary: 'done',
110
+ },
111
+ ],
112
+ ]));
113
+ expect(output).not.toContain('Cost');
114
+ expect(output).toContain('Tokens');
115
+ expect(output).toContain('280');
116
+ });
117
+ });
118
+ describe('WorkflowRunner logRunSummary', () => {
119
+ it('falls back to the legacy summary format when no reports exist', () => {
120
+ const runner = new WorkflowRunner({ cwd: '/tmp/workflow-runner' });
121
+ const logSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
122
+ runner.logRunSummary('sample-workflow', [{ name: 'lint', agent: 'shell', status: 'completed', attempts: 1, output: 'ok' }], 'run-1');
123
+ const combined = logSpy.mock.calls.flat().join('\n');
124
+ expect(combined).toContain('Workflow "sample-workflow"');
125
+ expect(combined).toContain('✓ lint [shell]');
126
+ expect(combined).not.toContain('Step Status');
127
+ logSpy.mockRestore();
128
+ });
129
+ });
130
+ //# sourceMappingURL=run-summary-table.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-summary-table.test.js","sourceRoot":"","sources":["../../../src/workflows/__tests__/run-summary-table.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=step-cwd.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"step-cwd.test.d.ts","sourceRoot":"","sources":["../../../src/workflows/__tests__/step-cwd.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,42 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import path from 'node:path';
3
+ vi.mock('@relaycast/sdk', () => ({
4
+ RelayCast: vi.fn(),
5
+ RelayError: class RelayError extends Error {
6
+ },
7
+ }));
8
+ vi.mock('../../relay.js', () => ({
9
+ AgentRelay: vi.fn(),
10
+ }));
11
+ const { WorkflowRunner } = await import('../runner.js');
12
+ describe('WorkflowRunner step cwd resolution', () => {
13
+ it('prefers step.cwd over agent.cwd and runner cwd', () => {
14
+ const runnerRoot = '/runner-root';
15
+ const runner = new WorkflowRunner({ cwd: runnerRoot });
16
+ const resolved = runner.resolveEffectiveCwd({ name: 'generate', agent: 'worker', task: 'Generate', cwd: 'steps/generate' }, { name: 'worker', cli: 'claude', cwd: 'agents/worker' });
17
+ expect(resolved).toBe(path.resolve(runnerRoot, 'steps/generate'));
18
+ });
19
+ it('respects step.cwd for deterministic steps', () => {
20
+ const runnerRoot = '/runner-root';
21
+ const runner = new WorkflowRunner({ cwd: runnerRoot });
22
+ const resolved = runner.resolveEffectiveCwd({
23
+ name: 'scaffold',
24
+ type: 'deterministic',
25
+ command: 'mkdir -p out',
26
+ cwd: 'deterministic/setup',
27
+ });
28
+ expect(resolved).toBe(path.resolve(runnerRoot, 'deterministic/setup'));
29
+ });
30
+ it('falls back through step.cwd to step.workdir to agent.cwd to runner.cwd', () => {
31
+ const runnerRoot = '/runner-root';
32
+ const namedPath = '/named/workdir';
33
+ const runner = new WorkflowRunner({ cwd: runnerRoot });
34
+ runner.resolvedPaths.set('generated', namedPath);
35
+ const agentDef = { name: 'worker', cli: 'claude', cwd: 'agents/worker' };
36
+ expect(runner.resolveEffectiveCwd({ name: 's1', agent: 'worker', task: 'Do work', cwd: 'steps/explicit', workdir: 'generated' }, agentDef)).toBe(path.resolve(runnerRoot, 'steps/explicit'));
37
+ expect(runner.resolveEffectiveCwd({ name: 's2', agent: 'worker', task: 'Do work', workdir: 'generated' }, agentDef)).toBe(namedPath);
38
+ expect(runner.resolveEffectiveCwd({ name: 's3', agent: 'worker', task: 'Do work' }, agentDef)).toBe(path.resolve(runnerRoot, 'agents/worker'));
39
+ expect(runner.resolveEffectiveCwd({ name: 's4', type: 'deterministic', command: 'pwd' })).toBe(runnerRoot);
40
+ });
41
+ });
42
+ //# sourceMappingURL=step-cwd.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"step-cwd.test.js","sourceRoot":"","sources":["../../../src/workflows/__tests__/step-cwd.test.ts"],"names":[],"mappings":"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"}
@@ -22,6 +22,7 @@ export interface AgentOptions {
22
22
  export interface AgentStepOptions {
23
23
  agent: string;
24
24
  task: string;
25
+ cwd?: string;
25
26
  dependsOn?: string[];
26
27
  verification?: VerificationCheck;
27
28
  timeoutMs?: number;
@@ -31,6 +32,7 @@ export interface AgentStepOptions {
31
32
  export interface DeterministicStepOptions {
32
33
  type: 'deterministic';
33
34
  command: string;
35
+ cwd?: string;
34
36
  /** Capture stdout as step output for downstream steps. Default: true. */
35
37
  captureOutput?: boolean;
36
38
  /** Fail if command exit code is non-zero. Default: true. */