@smithers-orchestrator/errors 0.16.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 (41) hide show
  1. package/LICENSE +21 -0
  2. package/package.json +33 -0
  3. package/src/AgentCliError.js +9 -0
  4. package/src/DbWriteFailed.js +9 -0
  5. package/src/ERROR_REFERENCE_URL.js +1 -0
  6. package/src/EngineError.js +17 -0
  7. package/src/EngineErrorCode.ts +21 -0
  8. package/src/ErrorWrapOptions.ts +6 -0
  9. package/src/InvalidInput.js +9 -0
  10. package/src/KnownSmithersErrorCode.ts +3 -0
  11. package/src/RunNotFound.js +14 -0
  12. package/src/SmithersError.js +55 -0
  13. package/src/SmithersErrorCategory.ts +11 -0
  14. package/src/SmithersErrorCode.ts +3 -0
  15. package/src/SmithersErrorDefinition.ts +7 -0
  16. package/src/SmithersErrorOptions.ts +5 -0
  17. package/src/SmithersTaggedError.ts +18 -0
  18. package/src/SmithersTaggedErrorPayload.ts +52 -0
  19. package/src/SmithersTaggedErrorTag.ts +3 -0
  20. package/src/TaggedErrorDetails.ts +6 -0
  21. package/src/TaskAborted.js +17 -0
  22. package/src/TaskHeartbeatTimeout.js +19 -0
  23. package/src/TaskTimeout.js +16 -0
  24. package/src/WorkflowFailed.js +15 -0
  25. package/src/errorToJson.js +35 -0
  26. package/src/fromTaggedError.js +63 -0
  27. package/src/fromTaggedErrorPayload.js +68 -0
  28. package/src/getSmithersErrorDefinition.js +14 -0
  29. package/src/getSmithersErrorDocsUrl.js +10 -0
  30. package/src/index.d.ts +901 -0
  31. package/src/index.js +40 -0
  32. package/src/isKnownSmithersErrorCode.js +10 -0
  33. package/src/isSmithersError.js +12 -0
  34. package/src/isSmithersTaggedError.js +12 -0
  35. package/src/isSmithersTaggedErrorTag.js +11 -0
  36. package/src/knownSmithersErrorCodes.js +7 -0
  37. package/src/smithersErrorDefinitions.js +382 -0
  38. package/src/smithersTaggedErrorCodes.js +10 -0
  39. package/src/tagged.js +24 -0
  40. package/src/toSmithersError.js +64 -0
  41. package/src/toTaggedErrorPayload.js +92 -0
package/src/index.js ADDED
@@ -0,0 +1,40 @@
1
+ // @smithers-type-exports-begin
2
+ /** @typedef {import("./EngineErrorCode.ts").EngineErrorCode} EngineErrorCode */
3
+ /** @typedef {import("./ErrorWrapOptions.ts").ErrorWrapOptions} ErrorWrapOptions */
4
+ /** @typedef {import("./TaggedErrorDetails.ts").GenericTaggedErrorArgs} GenericTaggedErrorArgs */
5
+ /** @typedef {import("./KnownSmithersErrorCode.ts").KnownSmithersErrorCode} KnownSmithersErrorCode */
6
+ /** @typedef {import("./SmithersErrorCategory.ts").SmithersErrorCategory} SmithersErrorCategory */
7
+ /** @typedef {import("./SmithersErrorCode.ts").SmithersErrorCode} SmithersErrorCode */
8
+ /** @typedef {import("./SmithersErrorDefinition.ts").SmithersErrorDefinition} SmithersErrorDefinition */
9
+ /** @typedef {import("./SmithersErrorOptions.ts").SmithersErrorOptions} SmithersErrorOptions */
10
+ /** @typedef {import("./SmithersTaggedError.ts").SmithersTaggedError} SmithersTaggedError */
11
+ /** @typedef {import("./SmithersTaggedErrorPayload.ts").SmithersTaggedErrorPayload} SmithersTaggedErrorPayload */
12
+ /** @typedef {import("./SmithersTaggedErrorTag.ts").SmithersTaggedErrorTag} SmithersTaggedErrorTag */
13
+ /** @typedef {import("./TaggedErrorDetails.ts").TaggedErrorDetails} TaggedErrorDetails */
14
+ // @smithers-type-exports-end
15
+
16
+ export * from "./AgentCliError.js";
17
+ export * from "./DbWriteFailed.js";
18
+ export * from "./EngineError.js";
19
+ export * from "./ERROR_REFERENCE_URL.js";
20
+ export * from "./InvalidInput.js";
21
+ export * from "./RunNotFound.js";
22
+ export * from "./SmithersError.js";
23
+ export * from "./TaskAborted.js";
24
+ export * from "./TaskHeartbeatTimeout.js";
25
+ export * from "./TaskTimeout.js";
26
+ export * from "./WorkflowFailed.js";
27
+ export * from "./errorToJson.js";
28
+ export * from "./fromTaggedError.js";
29
+ export * from "./fromTaggedErrorPayload.js";
30
+ export * from "./getSmithersErrorDefinition.js";
31
+ export * from "./getSmithersErrorDocsUrl.js";
32
+ export * from "./isKnownSmithersErrorCode.js";
33
+ export * from "./isSmithersError.js";
34
+ export * from "./isSmithersTaggedError.js";
35
+ export * from "./isSmithersTaggedErrorTag.js";
36
+ export * from "./knownSmithersErrorCodes.js";
37
+ export * from "./smithersErrorDefinitions.js";
38
+ export * from "./smithersTaggedErrorCodes.js";
39
+ export * from "./toSmithersError.js";
40
+ export * from "./toTaggedErrorPayload.js";
@@ -0,0 +1,10 @@
1
+ import { smithersErrorDefinitions } from "./smithersErrorDefinitions.js";
2
+ /** @typedef {import("./KnownSmithersErrorCode.ts").KnownSmithersErrorCode} KnownSmithersErrorCode */
3
+
4
+ /**
5
+ * @param {string} code
6
+ * @returns {code is KnownSmithersErrorCode}
7
+ */
8
+ export function isKnownSmithersErrorCode(code) {
9
+ return code in smithersErrorDefinitions;
10
+ }
@@ -0,0 +1,12 @@
1
+
2
+ /** @typedef {import("./SmithersError.js").SmithersError} SmithersError */
3
+ /**
4
+ * @param {unknown} value
5
+ * @returns {value is SmithersError}
6
+ */
7
+ export function isSmithersError(value) {
8
+ return Boolean(value &&
9
+ typeof value === "object" &&
10
+ "code" in value &&
11
+ "message" in value);
12
+ }
@@ -0,0 +1,12 @@
1
+ import { isSmithersTaggedErrorTag } from "./isSmithersTaggedErrorTag.js";
2
+ /** @typedef {import("./SmithersTaggedError.ts").SmithersTaggedError} SmithersTaggedError */
3
+
4
+ /**
5
+ * @param {unknown} value
6
+ * @returns {value is SmithersTaggedError}
7
+ */
8
+ export function isSmithersTaggedError(value) {
9
+ return Boolean(value &&
10
+ typeof value === "object" &&
11
+ isSmithersTaggedErrorTag(value._tag));
12
+ }
@@ -0,0 +1,11 @@
1
+ import { smithersTaggedErrorCodes } from "./smithersTaggedErrorCodes.js";
2
+ /** @typedef {import("./SmithersTaggedErrorTag.ts").SmithersTaggedErrorTag} SmithersTaggedErrorTag */
3
+
4
+ /**
5
+ * @param {unknown} value
6
+ * @returns {value is SmithersTaggedErrorTag}
7
+ */
8
+ export function isSmithersTaggedErrorTag(value) {
9
+ return (typeof value === "string" &&
10
+ Object.prototype.hasOwnProperty.call(smithersTaggedErrorCodes, value));
11
+ }
@@ -0,0 +1,7 @@
1
+ import { smithersErrorDefinitions } from "./smithersErrorDefinitions.js";
2
+
3
+ /** @typedef {import("./KnownSmithersErrorCode.ts").KnownSmithersErrorCode} KnownSmithersErrorCode */
4
+
5
+ export const knownSmithersErrorCodes = /** @type {KnownSmithersErrorCode[]} */ (
6
+ Object.keys(smithersErrorDefinitions)
7
+ );
@@ -0,0 +1,382 @@
1
+ export const smithersErrorDefinitions = {
2
+ INVALID_INPUT: {
3
+ category: "engine",
4
+ when: "Workflow input fails validation or the runtime receives a non-object input payload.",
5
+ },
6
+ MISSING_INPUT: {
7
+ category: "engine",
8
+ when: "A resume run references an input row that is missing from the database.",
9
+ },
10
+ MISSING_INPUT_TABLE: {
11
+ category: "engine",
12
+ when: "The workflow schema does not expose the expected input table during resume or hydration.",
13
+ },
14
+ RESUME_METADATA_MISMATCH: {
15
+ category: "engine",
16
+ when: "Stored run metadata no longer matches the workflow being resumed.",
17
+ },
18
+ UNKNOWN_OUTPUT_SCHEMA: {
19
+ category: "engine",
20
+ when: "A task references an output table that is not present in the schema registry.",
21
+ },
22
+ INVALID_OUTPUT: {
23
+ category: "engine",
24
+ when: "Agent output cannot be parsed or validated against the declared output schema.",
25
+ },
26
+ WORKTREE_CREATE_FAILED: {
27
+ category: "engine",
28
+ when: "Smithers fails to create or hydrate a git or jj worktree for a task.",
29
+ details: "{ worktreePath, vcsType, branch? }",
30
+ },
31
+ VCS_NOT_FOUND: {
32
+ category: "engine",
33
+ when: "No supported git or jj repository root can be found for the workflow.",
34
+ details: "{ rootDir }",
35
+ },
36
+ SNAPSHOT_NOT_FOUND: {
37
+ category: "engine",
38
+ when: "A requested time-travel snapshot or frame does not exist.",
39
+ details: "{ runId, frameNo }",
40
+ },
41
+ VCS_WORKSPACE_CREATE_FAILED: {
42
+ category: "engine",
43
+ when: "Smithers fails to materialize a jj workspace for time-travel or replay.",
44
+ details: "{ runId, frameNo, vcsPointer, workspacePath }",
45
+ },
46
+ TASK_TIMEOUT: {
47
+ category: "engine",
48
+ when: "A task compute callback exceeds its configured timeout.",
49
+ details: "{ nodeId, attempt, timeoutMs }",
50
+ },
51
+ RUN_NOT_FOUND: {
52
+ category: "engine",
53
+ when: "A CLI or engine command references a run ID that does not exist in the database.",
54
+ details: "{ runId }",
55
+ },
56
+ NODE_NOT_FOUND: {
57
+ category: "engine",
58
+ when: "A CLI command references a node ID that does not exist for the given run.",
59
+ details: "{ runId, nodeId }",
60
+ },
61
+ INVALID_EVENTS_OPTIONS: {
62
+ category: "cli",
63
+ when: "The smithers events command receives invalid filter options.",
64
+ details: "{}",
65
+ },
66
+ SANDBOX_BUNDLE_INVALID: {
67
+ category: "engine",
68
+ when: "A sandbox bundle fails validation.",
69
+ details: "{ bundlePath }",
70
+ },
71
+ SANDBOX_BUNDLE_TOO_LARGE: {
72
+ category: "engine",
73
+ when: "A sandbox bundle exceeds the maximum allowed size.",
74
+ details: "{ bundlePath, maxBytes }",
75
+ },
76
+ WORKFLOW_EXECUTION_FAILED: {
77
+ category: "engine",
78
+ when: "A child or builder workflow exits unsuccessfully without surfacing a typed error payload.",
79
+ details: "{ status }",
80
+ },
81
+ SANDBOX_EXECUTION_FAILED: {
82
+ category: "engine",
83
+ when: "Sandbox setup or execution fails before a more specific sandbox error can be emitted.",
84
+ details: "{ sandboxId, runId?, maxConcurrent?, activeSandboxCount? }",
85
+ },
86
+ TASK_HEARTBEAT_TIMEOUT: {
87
+ category: "engine",
88
+ when: "A task heartbeat timeout is exceeded while the task is still in progress.",
89
+ details: "{ nodeId, iteration, attempt, timeoutMs, staleForMs }",
90
+ },
91
+ HEARTBEAT_PAYLOAD_TOO_LARGE: {
92
+ category: "engine",
93
+ when: "A task heartbeat payload exceeds the maximum persisted checkpoint size.",
94
+ details: "{ dataSizeBytes, maxBytes }",
95
+ },
96
+ HEARTBEAT_PAYLOAD_NOT_JSON_SERIALIZABLE: {
97
+ category: "engine",
98
+ when: "A task heartbeat payload contains values that cannot be serialized to JSON.",
99
+ details: "{ path, valueType? }",
100
+ },
101
+ TASK_ABORTED: {
102
+ category: "engine",
103
+ when: "A running task is aborted through an AbortSignal or shutdown path.",
104
+ },
105
+ RUN_CANCELLED: {
106
+ category: "engine",
107
+ when: "A run is cancelled while runtime work is still active.",
108
+ details: "{ runId }",
109
+ },
110
+ RUN_NOT_RESUMABLE: {
111
+ category: "engine",
112
+ when: "A resume request targets a run state that cannot be resumed.",
113
+ details: "{ runId, status }",
114
+ },
115
+ RUN_OWNER_ALIVE: {
116
+ category: "engine",
117
+ when: "Resume recovery is skipped because the previous runtime owner is still heartbeating.",
118
+ details: "{ runId, runtimeOwnerId }",
119
+ },
120
+ RUN_STILL_RUNNING: {
121
+ category: "engine",
122
+ when: "A recovery or resume operation finds a run that is still active.",
123
+ details: "{ runId }",
124
+ },
125
+ RUN_RESUME_CLAIM_LOST: {
126
+ category: "engine",
127
+ when: "A runtime loses the resume claim before it can update the run.",
128
+ details: "{ runId, runtimeOwnerId }",
129
+ },
130
+ RUN_RESUME_CLAIM_FAILED: {
131
+ category: "engine",
132
+ when: "A runtime cannot claim a stale run for resume.",
133
+ details: "{ runId, runtimeOwnerId }",
134
+ },
135
+ RUN_RESUME_ACTIVATION_FAILED: {
136
+ category: "engine",
137
+ when: "A claimed run cannot be moved back into active execution.",
138
+ details: "{ runId, runtimeOwnerId }",
139
+ },
140
+ RUN_HIJACKED: {
141
+ category: "engine",
142
+ when: "A run is interrupted because another runtime hijacked execution.",
143
+ details: "{ runId, hijackTarget }",
144
+ },
145
+ CONTINUATION_STATE_TOO_LARGE: {
146
+ category: "engine",
147
+ when: "Continue-as-new state exceeds the configured serialized size limit.",
148
+ details: "{ runId, sizeBytes, maxBytes }",
149
+ },
150
+ INVALID_CONTINUATION_STATE: {
151
+ category: "engine",
152
+ when: "Continue-as-new state cannot be parsed or applied.",
153
+ },
154
+ RALPH_MAX_REACHED: {
155
+ category: "engine",
156
+ when: "A Ralph loop reaches maxIterations with fail-on-max behavior.",
157
+ details: "{ ralphId, maxIterations }",
158
+ },
159
+ SCHEDULER_ERROR: {
160
+ category: "engine",
161
+ when: "The scheduler cannot produce a valid execution decision.",
162
+ },
163
+ SESSION_ERROR: {
164
+ category: "engine",
165
+ when: "The workflow session state machine reaches an invalid or failed state.",
166
+ },
167
+ TASK_ID_REQUIRED: {
168
+ category: "components",
169
+ when: "<Task> is missing a valid string id.",
170
+ },
171
+ TASK_MISSING_OUTPUT: {
172
+ category: "components",
173
+ when: "<Task> is missing its output prop.",
174
+ details: "{ nodeId }",
175
+ },
176
+ DUPLICATE_ID: {
177
+ category: "components",
178
+ when: "Two nodes with the same runtime id are mounted in one workflow graph.",
179
+ details: "{ kind, id }",
180
+ },
181
+ NESTED_LOOP: {
182
+ category: "components",
183
+ when: "<Loop> or <Ralph> is nested inside another loop construct that Smithers does not support.",
184
+ },
185
+ WORKTREE_EMPTY_PATH: {
186
+ category: "components",
187
+ when: "<Worktree> is mounted with an empty path.",
188
+ },
189
+ MDX_PRELOAD_INACTIVE: {
190
+ category: "components",
191
+ when: "A prompt object is rendered without the MDX preload layer being active.",
192
+ },
193
+ CONTEXT_OUTSIDE_WORKFLOW: {
194
+ category: "components",
195
+ when: "Workflow context access happens outside an active Smithers workflow render.",
196
+ },
197
+ MISSING_OUTPUT: {
198
+ category: "components",
199
+ when: "Code calls ctx.output() for a node result that does not exist.",
200
+ details: "{ nodeId, iteration }",
201
+ },
202
+ DEP_NOT_SATISFIED: {
203
+ category: "components",
204
+ when: "A typed dep on <Task> references an upstream output that has not been produced yet.",
205
+ details: "{ taskId, depKey, resolvedNodeId }",
206
+ },
207
+ ASPECT_BUDGET_EXCEEDED: {
208
+ category: "components",
209
+ when: "An Aspects budget has been exceeded.",
210
+ details: "{ kind, limit, current }",
211
+ },
212
+ APPROVAL_OUTSIDE_TASK: {
213
+ category: "components",
214
+ when: "<Approval> is resolved outside the active task runtime.",
215
+ },
216
+ APPROVAL_OPTIONS_REQUIRED: {
217
+ category: "components",
218
+ when: "An approval mode that requires explicit options is missing them.",
219
+ },
220
+ WORKFLOW_MISSING_DEFAULT: {
221
+ category: "components",
222
+ when: "A workflow module does not export a default Smithers workflow.",
223
+ },
224
+ TOOL_PATH_INVALID: {
225
+ category: "tools",
226
+ when: "A filesystem tool receives a non-string path.",
227
+ },
228
+ TOOL_PATH_ESCAPE: {
229
+ category: "tools",
230
+ when: "A filesystem tool resolves a path outside the sandbox root, including through symlinks.",
231
+ },
232
+ TOOL_FILE_TOO_LARGE: {
233
+ category: "tools",
234
+ when: "A read or edit operation exceeds the configured file size limit.",
235
+ },
236
+ TOOL_CONTENT_TOO_LARGE: {
237
+ category: "tools",
238
+ when: "A write operation exceeds the configured content size limit.",
239
+ },
240
+ TOOL_PATCH_TOO_LARGE: {
241
+ category: "tools",
242
+ when: "An edit patch exceeds the configured patch size limit.",
243
+ },
244
+ TOOL_PATCH_FAILED: {
245
+ category: "tools",
246
+ when: "A unified diff patch cannot be applied to the target file.",
247
+ },
248
+ TOOL_NETWORK_DISABLED: {
249
+ category: "tools",
250
+ when: "The bash tool tries to access the network while network access is disabled.",
251
+ },
252
+ TOOL_GIT_REMOTE_DISABLED: {
253
+ category: "tools",
254
+ when: "The bash tool attempts a remote git operation while network access is disabled.",
255
+ },
256
+ TOOL_COMMAND_FAILED: {
257
+ category: "tools",
258
+ when: "A bash tool command exits with a non-zero status.",
259
+ },
260
+ TOOL_GREP_FAILED: {
261
+ category: "tools",
262
+ when: "The grep tool fails with an rg execution error.",
263
+ },
264
+ AGENT_CLI_ERROR: {
265
+ category: "agents",
266
+ when: "A CLI-backed agent exits unsuccessfully, streams an explicit error, or its RPC transport fails.",
267
+ },
268
+ AGENT_RPC_FILE_ARGS: {
269
+ category: "agents",
270
+ when: "Pi RPC mode is used with file arguments that the transport does not support.",
271
+ },
272
+ AGENT_BUILD_COMMAND: {
273
+ category: "agents",
274
+ when: "An agent implementation forbids buildCommand() because it uses a custom generate() transport.",
275
+ },
276
+ AGENT_DIAGNOSTIC_TIMEOUT: {
277
+ category: "agents",
278
+ when: "An internal agent diagnostic check exceeds the per-check timeout budget.",
279
+ },
280
+ DB_MISSING_COLUMNS: {
281
+ category: "database",
282
+ when: "A table used by Smithers does not expose required columns such as runId or nodeId.",
283
+ },
284
+ DB_REQUIRES_BUN_SQLITE: {
285
+ category: "database",
286
+ when: "The database adapter is not backed by a Bun SQLite client with exec().",
287
+ },
288
+ DB_QUERY_FAILED: {
289
+ category: "database",
290
+ when: "A database read query throws or rejects while running inside an Effect.",
291
+ },
292
+ DB_WRITE_FAILED: {
293
+ category: "database",
294
+ when: "A database write or migration fails, including after SQLite retry exhaustion.",
295
+ },
296
+ STORAGE_ERROR: {
297
+ category: "database",
298
+ when: "A storage service operation fails before surfacing a more specific database code.",
299
+ },
300
+ INTERNAL_ERROR: {
301
+ category: "effect",
302
+ when: "An unexpected internal exception crossed an Effect boundary without a more specific Smithers code.",
303
+ },
304
+ PROCESS_ABORTED: {
305
+ category: "effect",
306
+ when: "A spawned child process is aborted by signal or shutdown.",
307
+ details: "{ command, args, cwd }",
308
+ },
309
+ PROCESS_TIMEOUT: {
310
+ category: "effect",
311
+ when: "A spawned child process exceeds its total timeout.",
312
+ details: "{ command, args, cwd, timeoutMs }",
313
+ },
314
+ PROCESS_IDLE_TIMEOUT: {
315
+ category: "effect",
316
+ when: "A spawned child process stops producing output longer than its idle timeout.",
317
+ details: "{ command, args, cwd, idleTimeoutMs }",
318
+ },
319
+ PROCESS_SPAWN_FAILED: {
320
+ category: "effect",
321
+ when: "The runtime cannot spawn the requested child process.",
322
+ details: "{ command, args, cwd }",
323
+ },
324
+ TASK_RUNTIME_UNAVAILABLE: {
325
+ category: "effect",
326
+ when: "Builder task runtime APIs are accessed outside an executing step.",
327
+ },
328
+ SCHEMA_CHANGE_HOT: {
329
+ category: "hot",
330
+ when: "Hot reload detects a schema change that requires a full restart.",
331
+ },
332
+ HOT_OVERLAY_FAILED: {
333
+ category: "hot",
334
+ when: "Building or cleaning the generated hot-reload overlay fails.",
335
+ },
336
+ HOT_RELOAD_INVALID_MODULE: {
337
+ category: "hot",
338
+ when: "A hot-reloaded workflow module does not export a valid default workflow build.",
339
+ },
340
+ SCORER_FAILED: {
341
+ category: "scorers",
342
+ when: "A scorer throws or rejects while Smithers is evaluating a result.",
343
+ },
344
+ WORKFLOW_EXISTS: {
345
+ category: "cli",
346
+ when: "The workflow creation CLI refuses to overwrite an existing workflow file.",
347
+ },
348
+ CLI_DB_NOT_FOUND: {
349
+ category: "cli",
350
+ when: "A CLI command cannot find a nearby smithers.db file.",
351
+ },
352
+ CLI_AGENT_UNSUPPORTED: {
353
+ category: "cli",
354
+ when: "The ask command selects an agent integration that Smithers does not support in that mode.",
355
+ },
356
+ PI_HTTP_ERROR: {
357
+ category: "integrations",
358
+ when: "The Pi or server integration receives a non-success HTTP response from Smithers.",
359
+ },
360
+ EXTERNAL_BUILD_FAILED: {
361
+ category: "integrations",
362
+ when: "An external workflow host fails to build a Smithers HostNode payload.",
363
+ details: "{ scriptPath, error?, exitCode?, stderr?, stdout? }",
364
+ },
365
+ SCHEMA_DISCOVERY_FAILED: {
366
+ category: "integrations",
367
+ when: "External workflow schema discovery fails or returns invalid output.",
368
+ details: "{ scriptPath, error?, exitCode?, stderr? }",
369
+ },
370
+ OPENAPI_SPEC_LOAD_FAILED: {
371
+ category: "integrations",
372
+ when: "An OpenAPI spec cannot be loaded or parsed.",
373
+ },
374
+ OPENAPI_OPERATION_NOT_FOUND: {
375
+ category: "integrations",
376
+ when: "The requested operationId does not exist in the OpenAPI spec.",
377
+ },
378
+ OPENAPI_TOOL_EXECUTION_FAILED: {
379
+ category: "integrations",
380
+ when: "An OpenAPI tool call fails during HTTP execution.",
381
+ },
382
+ };
@@ -0,0 +1,10 @@
1
+ export const smithersTaggedErrorCodes = /** @type {const} */ ({
2
+ TaskAborted: "TASK_ABORTED",
3
+ TaskTimeout: "TASK_TIMEOUT",
4
+ TaskHeartbeatTimeout: "TASK_HEARTBEAT_TIMEOUT",
5
+ RunNotFound: "RUN_NOT_FOUND",
6
+ InvalidInput: "INVALID_INPUT",
7
+ DbWriteFailed: "DB_WRITE_FAILED",
8
+ AgentCliError: "AGENT_CLI_ERROR",
9
+ WorkflowFailed: "WORKFLOW_EXECUTION_FAILED",
10
+ });
package/src/tagged.js ADDED
@@ -0,0 +1,24 @@
1
+ // @smithers-type-exports-begin
2
+ /** @typedef {import("./EngineErrorCode.ts").EngineErrorCode} EngineErrorCode */
3
+ /** @typedef {import("./TaggedErrorDetails.ts").GenericTaggedErrorArgs} GenericTaggedErrorArgs */
4
+ /** @typedef {import("./SmithersTaggedError.ts").SmithersTaggedError} SmithersTaggedError */
5
+ /** @typedef {import("./SmithersTaggedErrorPayload.ts").SmithersTaggedErrorPayload} SmithersTaggedErrorPayload */
6
+ /** @typedef {import("./SmithersTaggedErrorTag.ts").SmithersTaggedErrorTag} SmithersTaggedErrorTag */
7
+ /** @typedef {import("./TaggedErrorDetails.ts").TaggedErrorDetails} TaggedErrorDetails */
8
+ // @smithers-type-exports-end
9
+
10
+ export * from "./AgentCliError.js";
11
+ export * from "./DbWriteFailed.js";
12
+ export * from "./EngineError.js";
13
+ export * from "./InvalidInput.js";
14
+ export * from "./RunNotFound.js";
15
+ export * from "./TaskAborted.js";
16
+ export * from "./TaskHeartbeatTimeout.js";
17
+ export * from "./TaskTimeout.js";
18
+ export * from "./WorkflowFailed.js";
19
+ export * from "./fromTaggedError.js";
20
+ export * from "./fromTaggedErrorPayload.js";
21
+ export * from "./isSmithersTaggedError.js";
22
+ export * from "./isSmithersTaggedErrorTag.js";
23
+ export * from "./smithersTaggedErrorCodes.js";
24
+ export * from "./toTaggedErrorPayload.js";
@@ -0,0 +1,64 @@
1
+ import { SmithersError } from "./SmithersError.js";
2
+ import { EngineError } from "./EngineError.js";
3
+ import { fromTaggedError } from "./fromTaggedError.js";
4
+ import { isSmithersError } from "./isSmithersError.js";
5
+ /** @typedef {import("./ErrorWrapOptions.ts").ErrorWrapOptions} ErrorWrapOptions */
6
+
7
+ /**
8
+ * @param {unknown} cause
9
+ * @returns {string}
10
+ */
11
+ function causeSummary(cause) {
12
+ if (isSmithersErrorLike(cause)) {
13
+ return typeof cause.summary === "string" ? cause.summary : cause.message;
14
+ }
15
+ if (cause instanceof EngineError) {
16
+ return cause.message;
17
+ }
18
+ if (cause instanceof Error) {
19
+ return cause.message;
20
+ }
21
+ return String(cause);
22
+ }
23
+ /**
24
+ * @param {unknown} cause
25
+ * @returns {cause is SmithersError}
26
+ */
27
+ function isSmithersErrorLike(cause) {
28
+ return cause instanceof SmithersError || isSmithersError(cause);
29
+ }
30
+ /**
31
+ * @param {unknown} cause
32
+ * @param {string} [label]
33
+ * @param {ErrorWrapOptions} [options]
34
+ * @returns {SmithersError}
35
+ */
36
+ export function toSmithersError(cause, label, options = {}) {
37
+ const taggedError = fromTaggedError(cause);
38
+ const normalizedCause = taggedError ?? cause;
39
+ const smithersCause = isSmithersErrorLike(normalizedCause);
40
+ if (smithersCause &&
41
+ !label &&
42
+ !options.code &&
43
+ !options.details) {
44
+ return normalizedCause;
45
+ }
46
+ const code = options.code ??
47
+ (smithersCause
48
+ ? normalizedCause.code
49
+ : normalizedCause instanceof EngineError
50
+ ? normalizedCause.code
51
+ : "INTERNAL_ERROR");
52
+ const details = {
53
+ ...(smithersCause ? normalizedCause.details : {}),
54
+ ...(normalizedCause instanceof EngineError ? normalizedCause.context : {}),
55
+ ...options.details,
56
+ };
57
+ if (label && details.operation === undefined) {
58
+ details.operation = label;
59
+ }
60
+ const summary = label
61
+ ? `${label}: ${causeSummary(normalizedCause)}`
62
+ : causeSummary(normalizedCause);
63
+ return new SmithersError(code, summary, Object.keys(details).length > 0 ? details : undefined, { cause: normalizedCause });
64
+ }
@@ -0,0 +1,92 @@
1
+ import { isSmithersTaggedError } from "./isSmithersTaggedError.js";
2
+ /** @typedef {import("./TaggedErrorDetails.ts").TaggedErrorDetails} TaggedErrorDetails */
3
+
4
+ /** @typedef {import("./SmithersTaggedErrorPayload.ts").SmithersTaggedErrorPayload} SmithersTaggedErrorPayload */
5
+
6
+ /**
7
+ * @param {unknown} value
8
+ * @returns {value is TaggedErrorDetails}
9
+ */
10
+ function isRecord(value) {
11
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
12
+ }
13
+ /**
14
+ * @param {unknown} error
15
+ * @returns {SmithersTaggedErrorPayload | undefined}
16
+ */
17
+ export function toTaggedErrorPayload(error) {
18
+ if (!isSmithersTaggedError(error)) {
19
+ return undefined;
20
+ }
21
+ switch (error._tag) {
22
+ case "TaskAborted":
23
+ return {
24
+ _tag: "TaskAborted",
25
+ message: String(error.message),
26
+ details: isRecord(error.details)
27
+ ? error.details
28
+ : undefined,
29
+ name: typeof error.name === "string" ? error.name : undefined,
30
+ };
31
+ case "TaskTimeout":
32
+ return {
33
+ _tag: "TaskTimeout",
34
+ message: String(error.message),
35
+ nodeId: String(error.nodeId),
36
+ attempt: Number(error.attempt),
37
+ timeoutMs: Number(error.timeoutMs),
38
+ };
39
+ case "TaskHeartbeatTimeout":
40
+ return {
41
+ _tag: "TaskHeartbeatTimeout",
42
+ message: String(error.message),
43
+ nodeId: String(error.nodeId),
44
+ iteration: Number(error.iteration),
45
+ attempt: Number(error.attempt),
46
+ timeoutMs: Number(error.timeoutMs),
47
+ staleForMs: Number(error.staleForMs),
48
+ lastHeartbeatAtMs: Number(error.lastHeartbeatAtMs),
49
+ };
50
+ case "RunNotFound":
51
+ return {
52
+ _tag: "RunNotFound",
53
+ message: String(error.message),
54
+ runId: String(error.runId),
55
+ };
56
+ case "InvalidInput":
57
+ return {
58
+ _tag: "InvalidInput",
59
+ message: String(error.message),
60
+ details: isRecord(error.details)
61
+ ? error.details
62
+ : undefined,
63
+ };
64
+ case "DbWriteFailed":
65
+ return {
66
+ _tag: "DbWriteFailed",
67
+ message: String(error.message),
68
+ details: isRecord(error.details)
69
+ ? error.details
70
+ : undefined,
71
+ };
72
+ case "AgentCliError":
73
+ return {
74
+ _tag: "AgentCliError",
75
+ message: String(error.message),
76
+ details: isRecord(error.details)
77
+ ? error.details
78
+ : undefined,
79
+ };
80
+ case "WorkflowFailed":
81
+ return {
82
+ _tag: "WorkflowFailed",
83
+ message: String(error.message),
84
+ details: isRecord(error.details)
85
+ ? error.details
86
+ : undefined,
87
+ status: typeof error.status === "number"
88
+ ? error.status
89
+ : undefined,
90
+ };
91
+ }
92
+ }