@outputai/cli 0.10.1-next.3c76007.0 → 0.10.1-next.47f491f.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.
@@ -66,22 +66,29 @@ export interface Workflow {
66
66
  /** Alternative names that resolve to this workflow */
67
67
  aliases?: string[];
68
68
  }
69
+ /**
70
+ * Legacy trace information containing nested destinations
71
+ * @nullable
72
+ */
73
+ export type TraceInfoV1 = {
74
+ /** Available destinations for trace data */
75
+ destinations?: {
76
+ /** Absolute path to local trace file, omitted if not saved locally */
77
+ local?: string;
78
+ /** Remote trace location (e.g., S3 URI), omitted if not saved remotely */
79
+ remote?: string;
80
+ };
81
+ } | null;
69
82
  /**
70
83
  * Available destinations for trace data
84
+ * @nullable
71
85
  */
72
- export type TraceInfoDestinations = {
86
+ export type TraceInfoV2 = {
73
87
  /** Absolute path to local trace file, omitted if not saved locally */
74
88
  local?: string;
75
89
  /** Remote trace location (e.g., S3 URI), omitted if not saved remotely */
76
90
  remote?: string;
77
- };
78
- /**
79
- * An object with information about the trace generated by the execution
80
- */
81
- export interface TraceInfo {
82
- /** Available destinations for trace data */
83
- destinations?: TraceInfoDestinations;
84
- }
91
+ } | null;
85
92
  /**
86
93
  * The workflow input
87
94
  */
@@ -148,7 +155,7 @@ export declare const WorkflowRunInfoStatus: {
148
155
  readonly running: "running";
149
156
  readonly completed: "completed";
150
157
  readonly failed: "failed";
151
- readonly canceled: "canceled";
158
+ readonly cancelled: "cancelled";
152
159
  readonly terminated: "terminated";
153
160
  readonly timed_out: "timed_out";
154
161
  readonly continued_as_new: "continued_as_new";
@@ -180,7 +187,7 @@ export interface WorkflowRunsResponse {
180
187
  */
181
188
  export type WorkflowStatusResponseStatus = typeof WorkflowStatusResponseStatus[keyof typeof WorkflowStatusResponseStatus];
182
189
  export declare const WorkflowStatusResponseStatus: {
183
- readonly canceled: "canceled";
190
+ readonly cancelled: "cancelled";
184
191
  readonly completed: "completed";
185
192
  readonly continued_as_new: "continued_as_new";
186
193
  readonly failed: "failed";
@@ -204,20 +211,33 @@ export interface WorkflowStatusResponse {
204
211
  /**
205
212
  * The workflow execution status
206
213
  */
207
- export type WorkflowResultResponseStatus = typeof WorkflowResultResponseStatus[keyof typeof WorkflowResultResponseStatus];
208
- export declare const WorkflowResultResponseStatus: {
214
+ export type WorkflowResultStatus = typeof WorkflowResultStatus[keyof typeof WorkflowResultStatus];
215
+ export declare const WorkflowResultStatus: {
209
216
  readonly completed: "completed";
210
217
  readonly failed: "failed";
211
- readonly canceled: "canceled";
218
+ readonly cancelled: "cancelled";
212
219
  readonly terminated: "terminated";
213
220
  readonly timed_out: "timed_out";
214
221
  readonly continued_as_new: "continued_as_new";
215
222
  };
223
+ /**
224
+ * Structured error details captured from the workflow or activity failure
225
+ * @nullable
226
+ */
227
+ export type SerializedWorkflowError = {
228
+ /** Failing activity type, omitted when the failure did not originate in an activity */
229
+ activityType?: string;
230
+ /** Original error class name */
231
+ name?: string;
232
+ /** Original error message */
233
+ message?: string;
234
+ [key: string]: unknown;
235
+ } | null;
216
236
  /**
217
237
  * Structured failure details if the workflow failed, null otherwise
218
238
  * @nullable
219
239
  */
220
- export type WorkflowResultResponseErrorDetails = {
240
+ export type WorkflowResultV1ResponseErrorDetails = {
221
241
  /**
222
242
  * Friendly failure message (from the underlying application error)
223
243
  * @nullable
@@ -246,29 +266,64 @@ export type WorkflowResultResponseErrorDetails = {
246
266
  [key: string]: unknown;
247
267
  } | null;
248
268
  } | null | null;
249
- export interface WorkflowResultResponse {
269
+ /**
270
+ * Legacy wrapped workflow result
271
+ * @deprecated
272
+ */
273
+ export interface WorkflowResultV1Response {
250
274
  /** The workflow execution id */
251
- workflowId?: string;
252
- /** The specific run id for this execution */
253
- runId?: string;
275
+ workflowId: string;
276
+ /**
277
+ * The specific run id for this execution
278
+ * @nullable
279
+ */
280
+ runId: string | null;
281
+ status: WorkflowResultStatus;
254
282
  /** The original input passed to the workflow, null if unavailable */
255
- input?: unknown;
283
+ input: unknown | null;
256
284
  /** The result of workflow, null if workflow failed */
257
- output?: unknown;
258
- trace?: TraceInfo;
259
- /** The workflow execution status */
260
- status?: WorkflowResultResponseStatus;
285
+ output: unknown | null;
286
+ trace: TraceInfoV1 | null;
261
287
  /**
262
288
  * Error message if workflow failed, null otherwise
263
289
  * @nullable
264
290
  */
265
- error?: string | null;
291
+ error: string | null;
266
292
  /**
267
293
  * Structured failure details if the workflow failed, null otherwise
268
294
  * @nullable
269
295
  */
270
- errorDetails?: WorkflowResultResponseErrorDetails;
296
+ errorDetails: WorkflowResultV1ResponseErrorDetails;
297
+ }
298
+ /**
299
+ * Workflow result response version
300
+ */
301
+ export type WorkflowResultV2ResponseV = typeof WorkflowResultV2ResponseV[keyof typeof WorkflowResultV2ResponseV];
302
+ export declare const WorkflowResultV2ResponseV: {
303
+ readonly NUMBER_2: "2";
304
+ };
305
+ /**
306
+ * Current workflow result with direct output and memo-based trace information
307
+ */
308
+ export interface WorkflowResultV2Response {
309
+ /** Workflow result response version */
310
+ v: WorkflowResultV2ResponseV;
311
+ /** The workflow execution id */
312
+ workflowId: string;
313
+ /**
314
+ * The specific run id for this execution
315
+ * @nullable
316
+ */
317
+ runId: string | null;
318
+ status: WorkflowResultStatus;
319
+ /** The original input passed to the workflow, null if unavailable */
320
+ input: unknown | null;
321
+ /** Direct workflow output, null if no output is available */
322
+ output: unknown | null;
323
+ trace: TraceInfoV2 | null;
324
+ error: (SerializedWorkflowError | null) | null;
271
325
  }
326
+ export type WorkflowResultResponse = WorkflowResultV2Response | WorkflowResultV1Response;
272
327
  export interface WorkflowInputResponse {
273
328
  /** The workflow execution id */
274
329
  workflowId: string;
@@ -19,13 +19,13 @@ export const WorkflowRunInfoStatus = {
19
19
  running: 'running',
20
20
  completed: 'completed',
21
21
  failed: 'failed',
22
- canceled: 'canceled',
22
+ cancelled: 'cancelled',
23
23
  terminated: 'terminated',
24
24
  timed_out: 'timed_out',
25
25
  continued_as_new: 'continued_as_new',
26
26
  };
27
27
  export const WorkflowStatusResponseStatus = {
28
- canceled: 'canceled',
28
+ cancelled: 'cancelled',
29
29
  completed: 'completed',
30
30
  continued_as_new: 'continued_as_new',
31
31
  failed: 'failed',
@@ -34,14 +34,17 @@ export const WorkflowStatusResponseStatus = {
34
34
  timed_out: 'timed_out',
35
35
  unspecified: 'unspecified',
36
36
  };
37
- export const WorkflowResultResponseStatus = {
37
+ export const WorkflowResultStatus = {
38
38
  completed: 'completed',
39
39
  failed: 'failed',
40
- canceled: 'canceled',
40
+ cancelled: 'cancelled',
41
41
  terminated: 'terminated',
42
42
  timed_out: 'timed_out',
43
43
  continued_as_new: 'continued_as_new',
44
44
  };
45
+ export const WorkflowResultV2ResponseV = {
46
+ NUMBER_2: '2',
47
+ };
45
48
  ;
46
49
  export const getGetHealthUrl = () => {
47
50
  return `/health`;
@@ -80,7 +80,7 @@ services:
80
80
  condition: service_healthy
81
81
  worker:
82
82
  condition: service_healthy
83
- image: outputai/api:${OUTPUT_API_VERSION:-0.10.1-next.3c76007.0}
83
+ image: outputai/api:${OUTPUT_API_VERSION:-0.10.1-next.47f491f.0}
84
84
  init: true
85
85
  networks:
86
86
  - main
@@ -118,7 +118,7 @@ services:
118
118
  - OUTPUT_CATALOG_ID=${OUTPUT_CATALOG_ID:-main}
119
119
  - OUTPUT_REDIS_URL=redis://redis:6379
120
120
  - OUTPUT_TRACE_LOCAL_ON=${OUTPUT_TRACE_LOCAL_ON:-true}
121
- - OUTPUT_TRACE_HOST_PATH=${PWD}/logs
121
+ - OUTPUT_TRACE_HOST_PATH=${OUTPUT_TRACE_HOST_PATH:-${PWD}/logs}
122
122
  - OUTPUT_TRACE_HTTP_VERBOSE=${OUTPUT_TRACE_HTTP_VERBOSE:-true}
123
123
  - OUTPUT_ENABLE_ATTRIBUTE_SIGNAL_EMISSION=${OUTPUT_ENABLE_ATTRIBUTE_SIGNAL_EMISSION:-false}
124
124
  - TEMPORAL_ADDRESS=temporal:7233
@@ -1,6 +1,6 @@
1
1
  import { Args, Command } from '@oclif/core';
2
2
  import { getWorkflowIdResult } from '#api/generated/api.js';
3
- import { formatWorkflowResult, ERROR_STATUSES } from '#utils/format_workflow_result.js';
3
+ import { formatWorkflowResult, isErrorWorkflowStatus } from '#utils/format_workflow_result.js';
4
4
  import { handleApiError } from '#utils/error_handler.js';
5
5
  export default class WorkflowResult extends Command {
6
6
  static description = 'Get workflow execution result';
@@ -24,7 +24,7 @@ export default class WorkflowResult extends Command {
24
24
  }
25
25
  const data = response.data;
26
26
  this.log(`\n${formatWorkflowResult(data)}`);
27
- if (ERROR_STATUSES.has(data.status)) {
27
+ if (isErrorWorkflowStatus(data.status)) {
28
28
  process.exitCode = 1;
29
29
  }
30
30
  return data;
@@ -1,10 +1,12 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
1
2
  import { describe, it, expect, vi, beforeEach } from 'vitest';
2
- vi.mock('../../api/generated/api.js', () => ({
3
+ vi.mock('#api/generated/api.js', () => ({
3
4
  getWorkflowIdResult: vi.fn()
4
5
  }));
5
6
  describe('workflow result command', () => {
6
7
  beforeEach(() => {
7
8
  vi.clearAllMocks();
9
+ process.exitCode = undefined;
8
10
  });
9
11
  describe('command definition', () => {
10
12
  it('should export a valid OCLIF command', async () => {
@@ -18,4 +20,66 @@ describe('workflow result command', () => {
18
20
  expect(WorkflowResult.enableJsonFlag).toBe(true);
19
21
  });
20
22
  });
23
+ describe('run()', () => {
24
+ const runCommand = async (data) => {
25
+ const WorkflowResult = (await import('./result.js')).default;
26
+ const { getWorkflowIdResult } = await import('#api/generated/api.js');
27
+ const cmd = new WorkflowResult(['wf-1'], {});
28
+ cmd.log = vi.fn();
29
+ cmd.parse = vi.fn().mockResolvedValue({ args: { workflowId: 'wf-1' } });
30
+ vi.mocked(getWorkflowIdResult).mockResolvedValue({
31
+ data,
32
+ status: 200,
33
+ headers: new Headers()
34
+ });
35
+ const result = await cmd.run();
36
+ return { cmd, result, getWorkflowIdResult };
37
+ };
38
+ it('returns and formats a legacy result', async () => {
39
+ const data = {
40
+ workflowId: 'wf-1',
41
+ runId: 'run-1',
42
+ status: 'failed',
43
+ input: {},
44
+ output: null,
45
+ trace: null,
46
+ error: 'Legacy failure',
47
+ errorDetails: null
48
+ };
49
+ const { cmd, result, getWorkflowIdResult } = await runCommand(data);
50
+ expect(getWorkflowIdResult).toHaveBeenCalledWith('wf-1');
51
+ expect(cmd.log).toHaveBeenCalledWith(expect.stringContaining('Error: Legacy failure'));
52
+ expect(result).toEqual(data);
53
+ expect(process.exitCode).toBe(1);
54
+ });
55
+ it('returns and formats a current result', async () => {
56
+ const data = {
57
+ v: '2',
58
+ workflowId: 'wf-1',
59
+ runId: 'run-1',
60
+ status: 'failed',
61
+ input: {},
62
+ output: null,
63
+ trace: null,
64
+ error: { name: 'ValidationError', message: 'Invalid input' }
65
+ };
66
+ const { result } = await runCommand(data);
67
+ expect(result).toEqual(data);
68
+ expect(process.exitCode).toBe(1);
69
+ });
70
+ it('sets a failure exit code for the previous canceled spelling', async () => {
71
+ const data = {
72
+ workflowId: 'wf-1',
73
+ runId: 'run-1',
74
+ status: 'canceled',
75
+ input: {},
76
+ output: null,
77
+ trace: null,
78
+ error: 'Workflow was canceled',
79
+ errorDetails: null
80
+ };
81
+ await runCommand(data);
82
+ expect(process.exitCode).toBe(1);
83
+ });
84
+ });
21
85
  });
@@ -1,6 +1,6 @@
1
1
  import { Args, Command, Flags } from '@oclif/core';
2
2
  import { postWorkflowRun } from '#api/generated/api.js';
3
- import { formatWorkflowResult, ERROR_STATUSES } from '#utils/format_workflow_result.js';
3
+ import { formatWorkflowResult, isErrorWorkflowStatus } from '#utils/format_workflow_result.js';
4
4
  import { handleApiError } from '#utils/error_handler.js';
5
5
  import { resolveInput } from '#utils/resolve_input.js';
6
6
  import { getRetryDelayFromResponse } from '#utils/header_utils.js';
@@ -86,7 +86,7 @@ export default class WorkflowRun extends Command {
86
86
  }
87
87
  const data = response.data;
88
88
  this.log(`\n${formatWorkflowResult(data)}`);
89
- if (ERROR_STATUSES.has(data.status)) {
89
+ if (isErrorWorkflowStatus(data.status)) {
90
90
  process.exitCode = 1;
91
91
  }
92
92
  return data;
@@ -59,7 +59,16 @@ describe('workflow run command', () => {
59
59
  const { cmd, postWorkflowRun, resolveInput } = await createCommand();
60
60
  resolveInput.mockResolvedValue({ key: 'value' });
61
61
  postWorkflowRun.mockResolvedValue({
62
- data: { status: 'completed', result: { output: 'ok' } },
62
+ data: {
63
+ v: '2',
64
+ workflowId: 'wf-1',
65
+ runId: 'run-1',
66
+ status: 'completed',
67
+ input: { key: 'value' },
68
+ output: 'ok',
69
+ trace: null,
70
+ error: null
71
+ },
63
72
  status: 200,
64
73
  headers: new Headers()
65
74
  });
@@ -83,7 +92,16 @@ describe('workflow run command', () => {
83
92
  });
84
93
  resolveInput.mockResolvedValue({ key: 'value' });
85
94
  postWorkflowRun.mockResolvedValue({
86
- data: { status: 'completed', result: {} },
95
+ data: {
96
+ v: '2',
97
+ workflowId: 'wf-1',
98
+ runId: 'run-1',
99
+ status: 'completed',
100
+ input: { key: 'value' },
101
+ output: {},
102
+ trace: null,
103
+ error: null
104
+ },
87
105
  status: 200,
88
106
  headers: new Headers()
89
107
  });
@@ -103,7 +121,16 @@ describe('workflow run command', () => {
103
121
  postWorkflowRun
104
122
  .mockRejectedValueOnce(new HttpError('Unavailable', { status: 503, headers }))
105
123
  .mockResolvedValueOnce({
106
- data: { status: 'completed', result: {} },
124
+ data: {
125
+ v: '2',
126
+ workflowId: 'wf-1',
127
+ runId: 'run-1',
128
+ status: 'completed',
129
+ input: {},
130
+ output: {},
131
+ trace: null,
132
+ error: null
133
+ },
107
134
  status: 200,
108
135
  headers: new Headers()
109
136
  });
@@ -2,7 +2,7 @@ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
2
  vi.mock('../../api/generated/api.js', () => ({
3
3
  getWorkflowIdStatus: vi.fn(),
4
4
  GetWorkflowIdStatus200Status: {
5
- canceled: 'canceled',
5
+ cancelled: 'cancelled',
6
6
  completed: 'completed',
7
7
  continued_as_new: 'continued_as_new',
8
8
  failed: 'failed',
@@ -1,7 +1,6 @@
1
1
  import { Command } from '@oclif/core';
2
2
  import type { EvalOutput } from '@outputai/evals';
3
3
  export default class WorkflowTest extends Command {
4
- static aliases: string[];
5
4
  static description: string;
6
5
  static enableJsonFlag: boolean;
7
6
  static examples: string[];
@@ -7,7 +7,6 @@ import { diagnoseMissingEvalWorkflow } from '#utils/eval_diagnostics.js';
7
7
  import { handleApiError } from '#utils/error_handler.js';
8
8
  import { getEvalWorkflowName, renderEvalOutput, computeExitCode, EvalOutputSchema } from '@outputai/evals';
9
9
  export default class WorkflowTest extends Command {
10
- static aliases = ['workflow:test'];
11
10
  static description = 'Run evaluations against a workflow using its datasets';
12
11
  static enableJsonFlag = true;
13
12
  static examples = [
@@ -45,11 +45,11 @@ describe('workflow test command', () => {
45
45
  });
46
46
  describe('command definition', () => {
47
47
  it('enables the built-in --json flag', async () => {
48
- const WorkflowTest = (await import('./test_eval.js')).default;
48
+ const WorkflowTest = (await import('./test.js')).default;
49
49
  expect(WorkflowTest.enableJsonFlag).toBe(true);
50
50
  });
51
51
  it('binds the catalog flag to OUTPUT_CATALOG_ID', async () => {
52
- const WorkflowTest = (await import('./test_eval.js')).default;
52
+ const WorkflowTest = (await import('./test.js')).default;
53
53
  expect(WorkflowTest.flags).toHaveProperty('catalog');
54
54
  expect(WorkflowTest.flags.catalog.env).toBe('OUTPUT_CATALOG_ID');
55
55
  expect(WorkflowTest.flags.catalog.char).toBe('c');
@@ -57,7 +57,7 @@ describe('workflow test command', () => {
57
57
  });
58
58
  describe('run()', () => {
59
59
  const createCommand = async (jsonEnabled) => {
60
- const WorkflowTest = (await import('./test_eval.js')).default;
60
+ const WorkflowTest = (await import('./test.js')).default;
61
61
  const { postWorkflowRun } = await import('#api/generated/api.js');
62
62
  const cmd = new WorkflowTest(['simple'], {});
63
63
  cmd.log = vi.fn();
@@ -99,7 +99,7 @@ describe('workflow test command', () => {
99
99
  expect(process.exitCode).toBe(1);
100
100
  });
101
101
  it('routes registration, dataset runs, and the eval run to the resolved catalog', async () => {
102
- const WorkflowTest = (await import('./test_eval.js')).default;
102
+ const WorkflowTest = (await import('./test.js')).default;
103
103
  const { postWorkflowRun } = await import('#api/generated/api.js');
104
104
  const { fetchWorkflowCatalog } = await import('#api/workflow_catalog.js');
105
105
  const cmd = new WorkflowTest(['my_workflow'], {});
@@ -1,3 +1,3 @@
1
1
  {
2
- "framework": "0.10.1-next.3c76007.0"
2
+ "framework": "0.10.1-next.47f491f.0"
3
3
  }
@@ -1,6 +1,7 @@
1
- import type { WorkflowResultResponse, WorkflowResultResponseStatus } from '../api/generated/api.js';
1
+ import type { WorkflowResultResponse, WorkflowResultStatus } from '../api/generated/api.js';
2
2
  type WorkflowResult = Pick<WorkflowResultResponse, 'workflowId' | 'output' | 'status' | 'error'>;
3
- export declare const ERROR_STATUSES: ReadonlySet<WorkflowResultResponseStatus | undefined>;
3
+ export declare const ERROR_STATUSES: ReadonlySet<WorkflowResultStatus>;
4
+ export declare const isErrorWorkflowStatus: (status: string | null | undefined) => boolean;
4
5
  export declare const TERMINAL_STATUSES: ReadonlySet<string>;
5
6
  export declare function formatWorkflowResult(result: WorkflowResult): string;
6
7
  export {};
@@ -1,5 +1,6 @@
1
1
  import { normalizeWorkflowStatus } from './normalize_workflow_status.js';
2
- export const ERROR_STATUSES = new Set(['failed', 'canceled', 'terminated', 'timed_out']);
2
+ export const ERROR_STATUSES = new Set(['failed', 'cancelled', 'terminated', 'timed_out']);
3
+ export const isErrorWorkflowStatus = (status) => ERROR_STATUSES.has(normalizeWorkflowStatus(status));
3
4
  // Every error status plus the one success status — derived so the two sets can't
4
5
  // silently drift apart as error statuses evolve. Shared by `workflow monitor` and
5
6
  // the dev TUI's `useRunDetail`/`useStepGraph` so both agree on what "done" means.
@@ -17,7 +18,10 @@ export function formatWorkflowResult(result) {
17
18
  else {
18
19
  lines.push(`Status: ${status || 'unknown'}`);
19
20
  if (result.error) {
20
- lines.push(`Error: ${result.error}`);
21
+ const error = typeof result.error === 'string' ?
22
+ result.error :
23
+ result.error.message ?? JSON.stringify(result.error, null, 2);
24
+ lines.push(`Error: ${error}`);
21
25
  }
22
26
  }
23
27
  return lines.join('\n');
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect } from 'vitest';
2
- import { formatWorkflowResult } from './format_workflow_result.js';
2
+ import { formatWorkflowResult, isErrorWorkflowStatus } from './format_workflow_result.js';
3
3
  describe('formatWorkflowResult', () => {
4
4
  it('should display output for completed workflows', () => {
5
5
  const result = formatWorkflowResult({
@@ -13,7 +13,7 @@ describe('formatWorkflowResult', () => {
13
13
  expect(result).toContain('"values"');
14
14
  expect(result).not.toContain('Status:');
15
15
  });
16
- it('should display error details for failed workflows', () => {
16
+ it('should display legacy string errors for failed workflows', () => {
17
17
  const result = formatWorkflowResult({
18
18
  workflowId: 'wf-456',
19
19
  status: 'failed',
@@ -25,6 +25,29 @@ describe('formatWorkflowResult', () => {
25
25
  expect(result).toContain('Error: Activity task failed');
26
26
  expect(result).not.toContain('Output:');
27
27
  });
28
+ it('should display the message from structured errors', () => {
29
+ const result = formatWorkflowResult({
30
+ workflowId: 'wf-v2',
31
+ status: 'failed',
32
+ output: null,
33
+ error: {
34
+ name: 'ValidationError',
35
+ message: 'Input is invalid',
36
+ code: 'INVALID_INPUT'
37
+ }
38
+ });
39
+ expect(result).toContain('Error: Input is invalid');
40
+ expect(result).not.toContain('[object Object]');
41
+ });
42
+ it('should serialize structured errors without a message', () => {
43
+ const result = formatWorkflowResult({
44
+ workflowId: 'wf-v2',
45
+ status: 'failed',
46
+ output: null,
47
+ error: { code: 'UNKNOWN' }
48
+ });
49
+ expect(result).toContain('"code": "UNKNOWN"');
50
+ });
28
51
  it('should display status for terminated workflows', () => {
29
52
  const result = formatWorkflowResult({
30
53
  workflowId: 'wf-term',
@@ -35,15 +58,25 @@ describe('formatWorkflowResult', () => {
35
58
  expect(result).toContain('Status: terminated');
36
59
  expect(result).toContain('Error: Workflow terminated by user');
37
60
  });
38
- it('should display status for canceled workflows', () => {
61
+ it('should display status for cancelled workflows', () => {
39
62
  const result = formatWorkflowResult({
63
+ workflowId: 'wf-cancel',
64
+ status: 'cancelled',
65
+ output: null,
66
+ error: 'Workflow was cancelled'
67
+ });
68
+ expect(result).toContain('Status: cancelled');
69
+ expect(result).toContain('Error: Workflow was cancelled');
70
+ });
71
+ it('normalizes canceled responses without changing the current status type', () => {
72
+ const legacyResult = {
40
73
  workflowId: 'wf-cancel',
41
74
  status: 'canceled',
42
75
  output: null,
43
76
  error: 'Workflow was canceled'
44
- });
45
- expect(result).toContain('Status: canceled');
46
- expect(result).toContain('Error: Workflow was canceled');
77
+ };
78
+ expect(formatWorkflowResult(legacyResult)).toContain('Status: cancelled');
79
+ expect(isErrorWorkflowStatus('canceled')).toBe(true);
47
80
  });
48
81
  it('should display status without error line for continued_as_new workflows', () => {
49
82
  const result = formatWorkflowResult({
@@ -1,8 +1,9 @@
1
1
  /**
2
- * Temporary compatibility for API responses produced before CONTINUED_AS_NEW
3
- * was exposed as `continued_as_new`.
2
+ * Normalizes statuses from earlier API contracts.
3
+ *
4
+ * This can be removed after Aug, 2026
4
5
  *
5
6
  * @param status - Workflow status from the API
6
7
  * @returns Normalized workflow status
7
8
  */
8
- export declare const normalizeWorkflowStatus: <T extends string | null | undefined>(status: T) => T | "continued_as_new";
9
+ export declare const normalizeWorkflowStatus: <T extends string | null | undefined>(status: T) => T | "continued_as_new" | "cancelled";
@@ -1,8 +1,17 @@
1
1
  /**
2
- * Temporary compatibility for API responses produced before CONTINUED_AS_NEW
3
- * was exposed as `continued_as_new`.
2
+ * Normalizes statuses from earlier API contracts.
3
+ *
4
+ * This can be removed after Aug, 2026
4
5
  *
5
6
  * @param status - Workflow status from the API
6
7
  * @returns Normalized workflow status
7
8
  */
8
- export const normalizeWorkflowStatus = (status) => status === 'continued' ? 'continued_as_new' : status;
9
+ export const normalizeWorkflowStatus = (status) => {
10
+ if (status === 'continued') {
11
+ return 'continued_as_new';
12
+ }
13
+ if (status === 'canceled') {
14
+ return 'cancelled';
15
+ }
16
+ return status;
17
+ };
@@ -4,6 +4,9 @@ describe('normalizeWorkflowStatus', () => {
4
4
  it('temporarily maps continued to continued_as_new', () => {
5
5
  expect(normalizeWorkflowStatus('continued')).toBe('continued_as_new');
6
6
  });
7
+ it('maps the previous canceled spelling to cancelled', () => {
8
+ expect(normalizeWorkflowStatus('canceled')).toBe('cancelled');
9
+ });
7
10
  it('leaves other statuses and nullish values unchanged', () => {
8
11
  expect(normalizeWorkflowStatus('completed')).toBe('completed');
9
12
  expect(normalizeWorkflowStatus('continued_as_new')).toBe('continued_as_new');
@@ -4,7 +4,7 @@ const WORKFLOW_STATUS_MAP = {
4
4
  running: { icon: '●', color: 'yellow' },
5
5
  completed: { icon: '●', color: 'green' },
6
6
  failed: { icon: '✗', color: 'red' },
7
- canceled: { icon: '○', color: 'gray' },
7
+ cancelled: { icon: '○', color: 'gray' },
8
8
  terminated: { icon: '✗', color: 'gray' },
9
9
  timed_out: { icon: '✗', color: 'red' },
10
10
  continued_as_new: { icon: '↻', color: 'blue' }
@@ -73,7 +73,7 @@ const readTraceLog = async (source) => {
73
73
  return JSON.parse(content);
74
74
  };
75
75
  // Run detail and trace fetches are best-effort. Many statuses (in-progress,
76
- // failed, canceled) don't have a fully-formed result or trace available at
76
+ // failed, cancelled) don't have a fully-formed result or trace available at
77
77
  // any given moment, and that's expected — the caller falls back to
78
78
  // EMPTY_DETAIL and the UI renders whatever's there. Swallow everything.
79
79
  const fetchTrace = async (workflowId, runId) => {
@@ -4,7 +4,7 @@ describe('isTerminalRunStatus', () => {
4
4
  it('returns true for completed states', () => {
5
5
  expect(isTerminalRunStatus('completed')).toBe(true);
6
6
  expect(isTerminalRunStatus('failed')).toBe(true);
7
- expect(isTerminalRunStatus('canceled')).toBe(true);
7
+ expect(isTerminalRunStatus('cancelled')).toBe(true);
8
8
  expect(isTerminalRunStatus('terminated')).toBe(true);
9
9
  expect(isTerminalRunStatus('timed_out')).toBe(true);
10
10
  });
@@ -21,7 +21,7 @@ const STATUS_ORDER = {
21
21
  failed: 1,
22
22
  timed_out: 2,
23
23
  terminated: 3,
24
- canceled: 4,
24
+ cancelled: 4,
25
25
  continued_as_new: 5,
26
26
  completed: 6
27
27
  };
@@ -110,7 +110,7 @@ const DetailPane = ({ run, pane, rows }) => {
110
110
  }
111
111
  return _jsx(Text, { dimColor: true, children: "\u2014" });
112
112
  }
113
- if (activePane === 'output' && hasJsonValue(pane.error)) {
113
+ if (activePane === 'output' && typeof pane.error === 'string') {
114
114
  const lines = String(pane.error).split('\n').slice(0, tabContentRows);
115
115
  return (_jsx(Box, { flexDirection: "column", children: lines.map((line, i) => (_jsx(Text, { color: "red", wrap: "truncate-end", children: line }, i))) }));
116
116
  }
@@ -1398,10 +1398,8 @@
1398
1398
  "terminate.js"
1399
1399
  ]
1400
1400
  },
1401
- "workflow:test_eval": {
1402
- "aliases": [
1403
- "workflow:test"
1404
- ],
1401
+ "workflow:test": {
1402
+ "aliases": [],
1405
1403
  "args": {
1406
1404
  "workflowName": {
1407
1405
  "description": "Name of the workflow to test",
@@ -1470,7 +1468,7 @@
1470
1468
  },
1471
1469
  "hasDynamicHelp": false,
1472
1470
  "hiddenAliases": [],
1473
- "id": "workflow:test_eval",
1471
+ "id": "workflow:test",
1474
1472
  "pluginAlias": "@outputai/cli",
1475
1473
  "pluginName": "@outputai/cli",
1476
1474
  "pluginType": "core",
@@ -1481,7 +1479,7 @@
1481
1479
  "dist",
1482
1480
  "commands",
1483
1481
  "workflow",
1484
- "test_eval.js"
1482
+ "test.js"
1485
1483
  ]
1486
1484
  },
1487
1485
  "workflow:dataset:generate": {
@@ -1715,5 +1713,5 @@
1715
1713
  ]
1716
1714
  }
1717
1715
  },
1718
- "version": "0.10.1-next.3c76007.0"
1716
+ "version": "0.10.1-next.47f491f.0"
1719
1717
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@outputai/cli",
3
- "version": "0.10.1-next.3c76007.0",
3
+ "version": "0.10.1-next.47f491f.0",
4
4
  "description": "CLI for Output.ai workflow generation",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -36,9 +36,9 @@
36
36
  "react": "19.2.5",
37
37
  "semver": "7.7.4",
38
38
  "undici": "8.9.0",
39
- "@outputai/credentials": "0.10.1-next.3c76007.0",
40
- "@outputai/evals": "0.10.1-next.3c76007.0",
41
- "@outputai/llm": "0.10.1-next.3c76007.0"
39
+ "@outputai/credentials": "0.10.1-next.47f491f.0",
40
+ "@outputai/llm": "0.10.1-next.47f491f.0",
41
+ "@outputai/evals": "0.10.1-next.47f491f.0"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@types/cli-progress": "3.11.6",