@bastani/atomic 0.8.26-alpha.4 → 0.8.26-alpha.6

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/CHANGELOG.md +12 -0
  2. package/dist/builtin/intercom/CHANGELOG.md +12 -0
  3. package/dist/builtin/intercom/package.json +1 -1
  4. package/dist/builtin/mcp/CHANGELOG.md +12 -0
  5. package/dist/builtin/mcp/package.json +1 -1
  6. package/dist/builtin/subagents/CHANGELOG.md +12 -0
  7. package/dist/builtin/subagents/package.json +1 -1
  8. package/dist/builtin/subagents/src/runs/background/subagent-runner.ts +48 -10
  9. package/dist/builtin/subagents/src/runs/foreground/execution.ts +30 -9
  10. package/dist/builtin/subagents/src/runs/shared/final-drain.ts +34 -0
  11. package/dist/builtin/subagents/src/runs/shared/model-fallback.ts +416 -7
  12. package/dist/builtin/web-access/CHANGELOG.md +12 -0
  13. package/dist/builtin/web-access/package.json +1 -1
  14. package/dist/builtin/workflows/CHANGELOG.md +12 -0
  15. package/dist/builtin/workflows/builtin/deep-research-codebase.ts +6 -6
  16. package/dist/builtin/workflows/builtin/goal.ts +10 -10
  17. package/dist/builtin/workflows/builtin/open-claude-design.ts +4 -4
  18. package/dist/builtin/workflows/builtin/ralph.ts +16 -16
  19. package/dist/builtin/workflows/package.json +1 -1
  20. package/dist/builtin/workflows/src/extension/index.ts +10 -2
  21. package/dist/builtin/workflows/src/extension/runtime.ts +35 -3
  22. package/dist/builtin/workflows/src/runs/background/status.ts +52 -6
  23. package/dist/builtin/workflows/src/runs/foreground/executor.ts +441 -15
  24. package/dist/builtin/workflows/src/runs/foreground/stage-runner.ts +69 -8
  25. package/dist/builtin/workflows/src/runs/shared/model-fallback.ts +402 -8
  26. package/dist/builtin/workflows/src/shared/persistence-restore.ts +182 -6
  27. package/dist/builtin/workflows/src/shared/persistence-session-entries.ts +76 -6
  28. package/dist/builtin/workflows/src/shared/stage-prompt.ts +33 -2
  29. package/dist/builtin/workflows/src/shared/store-types.ts +31 -0
  30. package/dist/builtin/workflows/src/shared/store.ts +99 -11
  31. package/dist/builtin/workflows/src/shared/workflow-failures.ts +758 -132
  32. package/dist/builtin/workflows/src/tui/store-widget-installer.ts +74 -74
  33. package/dist/core/tools/ask-user-question/tool/format-answer.d.ts +5 -5
  34. package/dist/core/tools/ask-user-question/tool/format-answer.d.ts.map +1 -1
  35. package/dist/core/tools/ask-user-question/tool/format-answer.js +5 -5
  36. package/dist/core/tools/ask-user-question/tool/format-answer.js.map +1 -1
  37. package/dist/core/tools/ask-user-question/tool/response-envelope.d.ts +16 -3
  38. package/dist/core/tools/ask-user-question/tool/response-envelope.d.ts.map +1 -1
  39. package/dist/core/tools/ask-user-question/tool/response-envelope.js +21 -3
  40. package/dist/core/tools/ask-user-question/tool/response-envelope.js.map +1 -1
  41. package/package.json +1 -1
@@ -62,7 +62,7 @@ export function currentModelFullId(model: { provider: string; id: string } | und
62
62
  return `${String(model.provider)}/${model.id}`;
63
63
  }
64
64
 
65
- const RETRYABLE_MODEL_FAILURE_PATTERNS = [
65
+ const RETRYABLE_MODEL_FAILURE_PATTERNS: readonly RegExp[] = [
66
66
  /rate\s*limit/i,
67
67
  /too many requests/i,
68
68
  /\b429\b/,
@@ -71,6 +71,7 @@ const RETRYABLE_MODEL_FAILURE_PATTERNS = [
71
71
  /credit/i,
72
72
  /auth(?:entication)?/i,
73
73
  /unauthori[sz]ed/i,
74
+ /\b40[13]\b/,
74
75
  /forbidden/i,
75
76
  /api key/i,
76
77
  /token expired/i,
@@ -90,14 +91,422 @@ const RETRYABLE_MODEL_FAILURE_PATTERNS = [
90
91
  /upstream/i,
91
92
  /timed? out/i,
92
93
  /timeout/i,
93
- /\b502\b/,
94
- /\b503\b/,
95
- /\b504\b/,
94
+ /\b50[0-4]\b/,
96
95
  ];
97
96
 
98
- export function isRetryableModelFailure(error: string | undefined): boolean {
99
- if (!error) return false;
100
- return RETRYABLE_MODEL_FAILURE_PATTERNS.some((pattern) => pattern.test(error));
97
+ const NON_RETRYABLE_FAILURE_PATTERNS: readonly RegExp[] = [
98
+ /command failed/i,
99
+ /tests? failed/i,
100
+ /shell/i,
101
+ /missing file/i,
102
+ /no such file/i,
103
+ /completion guard/i,
104
+ /cancel/i,
105
+ /abort/i,
106
+ /interrupted/i,
107
+ ];
108
+
109
+ const CANCELLED_FAILURE_PATTERNS: readonly RegExp[] = [
110
+ /cancel/i,
111
+ /abort/i,
112
+ /interrupted/i,
113
+ ];
114
+
115
+ export type ModelFallbackFailureKind =
116
+ | "auth_on_candidate_provider"
117
+ | "rate_limit"
118
+ | "provider_unavailable"
119
+ | "network_timeout"
120
+ | "model_unavailable"
121
+ | "cancelled"
122
+ | "task_failure"
123
+ | "unknown";
124
+
125
+ export type ModelFallbackFailureSource =
126
+ | "assistant_message"
127
+ | "diagnostic"
128
+ | "throw"
129
+ | "structured"
130
+ | "string_fallback";
131
+
132
+ export interface ModelFallbackFailureSignal {
133
+ readonly kind: ModelFallbackFailureKind;
134
+ readonly message: string;
135
+ readonly source: ModelFallbackFailureSource;
136
+ readonly stopReason?: string;
137
+ readonly status?: number;
138
+ readonly code?: string | number;
139
+ readonly name?: string;
140
+ }
141
+
142
+ const FALLBACKABLE_FAILURE_KINDS: ReadonlySet<ModelFallbackFailureKind> = new Set([
143
+ "auth_on_candidate_provider",
144
+ "rate_limit",
145
+ "provider_unavailable",
146
+ "network_timeout",
147
+ "model_unavailable",
148
+ ]);
149
+
150
+ function asRecord(value: unknown): Record<string, unknown> | undefined {
151
+ return value !== null && typeof value === "object" ? value as Record<string, unknown> : undefined;
152
+ }
153
+
154
+ function field(value: unknown, key: string): unknown {
155
+ return asRecord(value)?.[key];
156
+ }
157
+
158
+ function stringField(value: unknown, key: string): string | undefined {
159
+ const raw = field(value, key);
160
+ return typeof raw === "string" && raw.trim().length > 0 ? raw : undefined;
161
+ }
162
+
163
+ function errorName(value: unknown): string | undefined {
164
+ return value instanceof Error ? value.name : stringField(value, "name");
165
+ }
166
+
167
+ function directMessageFrom(value: unknown): string | undefined {
168
+ return stringField(value, "errorMessage")
169
+ ?? stringField(value, "message")
170
+ ?? stringField(value, "statusText");
171
+ }
172
+
173
+ function integerFrom(value: unknown): number | undefined {
174
+ if (typeof value === "number" && Number.isInteger(value)) return value;
175
+ if (typeof value !== "string" || value.trim().length === 0) return undefined;
176
+ const parsed = Number(value.trim());
177
+ return Number.isInteger(parsed) ? parsed : undefined;
178
+ }
179
+
180
+ function statusFrom(value: unknown): number | undefined {
181
+ return integerFrom(field(value, "status"))
182
+ ?? integerFrom(field(value, "statusCode"))
183
+ ?? integerFrom(field(value, "httpStatus"));
184
+ }
185
+
186
+ function codeFrom(value: unknown): string | number | undefined {
187
+ const rawCode = field(value, "code");
188
+ return typeof rawCode === "string" || typeof rawCode === "number" ? rawCode : undefined;
189
+ }
190
+
191
+ function stopReasonFrom(value: unknown): string | undefined {
192
+ return stringField(value, "stopReason");
193
+ }
194
+
195
+ function finishReasonFrom(value: unknown): string | undefined {
196
+ return stringField(value, "finish_reason") ?? stringField(value, "finishReason");
197
+ }
198
+
199
+ function causeOf(value: unknown): unknown {
200
+ return value instanceof Error ? value.cause : field(value, "cause");
201
+ }
202
+
203
+ function diagnosticErrors(value: unknown): readonly unknown[] {
204
+ const diagnostics = field(value, "diagnostics");
205
+ if (!Array.isArray(diagnostics)) return [];
206
+ const errors: unknown[] = [];
207
+ for (const diagnostic of diagnostics) {
208
+ const diagnosticError = field(diagnostic, "error");
209
+ errors.push(diagnosticError ?? diagnostic);
210
+ }
211
+ return errors;
212
+ }
213
+
214
+ function normalizeCode(value: string | number | undefined): string | undefined {
215
+ if (value === undefined) return undefined;
216
+ const normalized = String(value)
217
+ .trim()
218
+ .toLowerCase()
219
+ .replace(/[^a-z0-9]+/g, "_")
220
+ .replace(/^_+|_+$/g, "");
221
+ return normalized.length > 0 ? normalized : undefined;
222
+ }
223
+
224
+ function kindFromStatus(status: number | undefined): ModelFallbackFailureKind | undefined {
225
+ switch (status) {
226
+ case 401:
227
+ case 403:
228
+ return "auth_on_candidate_provider";
229
+ case 408:
230
+ return "network_timeout";
231
+ case 404:
232
+ return "model_unavailable";
233
+ case 429:
234
+ return "rate_limit";
235
+ default:
236
+ if (status !== undefined && status >= 500 && status <= 599) return "provider_unavailable";
237
+ return undefined;
238
+ }
239
+ }
240
+
241
+ function refusalKindFromCode(code: string | number | undefined): ModelFallbackFailureKind | undefined {
242
+ const normalizedCode = normalizeCode(code);
243
+ if (normalizedCode === undefined) return undefined;
244
+ if (normalizedCode.includes("content_filter") || normalizedCode.includes("contentfilter")) return "task_failure";
245
+ if (normalizedCode.includes("safety") || normalizedCode.includes("policy")) return "task_failure";
246
+ switch (normalizedCode) {
247
+ case "blocked":
248
+ case "blocked_by_provider":
249
+ case "blocked_by_safety":
250
+ case "blocked_by_policy":
251
+ case "provider_refusal":
252
+ case "refusal":
253
+ case "tool_refusal":
254
+ case "tool_call_refusal":
255
+ case "tool_use_refusal":
256
+ return "task_failure";
257
+ default:
258
+ return undefined;
259
+ }
260
+ }
261
+
262
+ function kindFromCode(code: string | number | undefined): ModelFallbackFailureKind | undefined {
263
+ const normalizedCode = normalizeCode(code);
264
+ if (normalizedCode === undefined) return undefined;
265
+ const refusalKind = refusalKindFromCode(code);
266
+ if (refusalKind !== undefined) return refusalKind;
267
+ const httpStatusKind = kindFromStatus(integerFrom(code));
268
+ if (httpStatusKind !== undefined) return httpStatusKind;
269
+
270
+ switch (normalizedCode) {
271
+ case "auth":
272
+ case "auth_required":
273
+ case "authentication_required":
274
+ case "unauthorized":
275
+ case "forbidden":
276
+ case "invalid_api_key":
277
+ case "missing_api_key":
278
+ case "invalid_key":
279
+ return "auth_on_candidate_provider";
280
+ case "etimedout":
281
+ case "econnreset":
282
+ case "econnrefused":
283
+ case "enotfound":
284
+ case "eai_again":
285
+ case "fetch_failed":
286
+ case "network_error":
287
+ case "timeout":
288
+ case "timeout_error":
289
+ case "und_err_connect_timeout":
290
+ return "network_timeout";
291
+ case "rate_limit":
292
+ case "rate_limit_exceeded":
293
+ case "too_many_requests":
294
+ case "quota_exceeded":
295
+ return "rate_limit";
296
+ case "aborterror":
297
+ case "aborted":
298
+ case "cancelled":
299
+ case "canceled":
300
+ return "cancelled";
301
+ case "model_not_found":
302
+ case "model_unavailable":
303
+ case "model_disabled":
304
+ case "unknown_model":
305
+ return "model_unavailable";
306
+ case "provider_error":
307
+ case "api_error":
308
+ case "service_unavailable":
309
+ case "temporarily_unavailable":
310
+ case "overloaded":
311
+ return "provider_unavailable";
312
+ default:
313
+ return undefined;
314
+ }
315
+ }
316
+
317
+ const PROVIDER_REFUSAL_FAILURE_PATTERNS: readonly RegExp[] = [
318
+ /\bfinish[_\s-]?reason\b[^\n]*\bcontent[_\s-]?filter\b/i,
319
+ /\bcontent[_\s-]?filter(?:ed|ing)?\b/i,
320
+ /\b(?:safety|policy)\b[^\n]*\b(?:refus(?:e|al|ed|es|ing)?|block(?:ed|ing)?|filter(?:ed|ing)?|violat(?:e|ion|ed|ing)?|disallow(?:ed|ing)?|reject(?:ed|ion|ing)?)\b/i,
321
+ /\b(?:refus(?:e|al|ed|es|ing)?|block(?:ed|ing)?|filter(?:ed|ing)?|violat(?:e|ion|ed|ing)?|disallow(?:ed|ing)?|reject(?:ed|ion|ing)?)\b[^\n]*\b(?:safety|policy)\b/i,
322
+ /\btool[_\s-]?(?:call|use)?[_\s-]?refus(?:e|al|ed|es|ing)?\b/i,
323
+ /\btool(?:\s+call|\s+use)?\b[^\n]*\brefus(?:e|al|ed|es|ing)?\b/i,
324
+ /\brefus(?:e|al|ed|es|ing)?\b[^\n]*\btool(?:\s+call|\s+use)?\b/i,
325
+ /\bprovider[_\s-]?refus(?:e|al|ed|es|ing)?\b/i,
326
+ /\bprovider\b[^\n]*\brefus(?:e|al|ed|es|ing)?\b[^\n]*\b(?:prompt|request|content|policy|safety)\b/i,
327
+ ];
328
+
329
+ function refusalKindFromMessage(message: string): ModelFallbackFailureKind | undefined {
330
+ if (CANCELLED_FAILURE_PATTERNS.some((pattern) => pattern.test(message))) return "cancelled";
331
+ if (NON_RETRYABLE_FAILURE_PATTERNS.some((pattern) => pattern.test(message))) return "task_failure";
332
+ if (PROVIDER_REFUSAL_FAILURE_PATTERNS.some((pattern) => pattern.test(message))) return "task_failure";
333
+ return undefined;
334
+ }
335
+
336
+ function fallbackKindFromMessage(message: string, name: string | undefined): ModelFallbackFailureKind | undefined {
337
+ const refusalKind = refusalKindFromMessage(message);
338
+ if (refusalKind !== undefined) return refusalKind;
339
+ const nameKind = kindFromCode(name);
340
+ if (nameKind !== undefined) return nameKind;
341
+ if (!RETRYABLE_MODEL_FAILURE_PATTERNS.some((pattern) => pattern.test(message))) return undefined;
342
+ if (/rate\s*limit|too many requests|\b429\b|quota|billing|credit/i.test(message)) return "rate_limit";
343
+ if (/auth|unauthori[sz]ed|\b40[13]\b|api key|token expired|forbidden|invalid key/i.test(message)) return "auth_on_candidate_provider";
344
+ if (/model.*(?:unavailable|disabled|not found|unknown)|(?:unavailable|disabled|not found|unknown).*model/i.test(message)) return "model_unavailable";
345
+ if (/network|fetch failed|socket|connection refused|timeout|timed? out/i.test(message)) return "network_timeout";
346
+ return "provider_unavailable";
347
+ }
348
+
349
+ function signalSource(value: unknown, fallback: ModelFallbackFailureSource | undefined): ModelFallbackFailureSource {
350
+ if (fallback !== undefined) return fallback;
351
+ if (stopReasonFrom(value) !== undefined || diagnosticErrors(value).length > 0) return "assistant_message";
352
+ if (value instanceof Error) return "throw";
353
+ return "structured";
354
+ }
355
+
356
+ function makeSignal(
357
+ kind: ModelFallbackFailureKind,
358
+ value: unknown,
359
+ source: ModelFallbackFailureSource | undefined,
360
+ ): ModelFallbackFailureSignal {
361
+ const status = statusFrom(value);
362
+ const code = codeFrom(value);
363
+ const name = errorName(value);
364
+ const stopReason = stopReasonFrom(value);
365
+ return {
366
+ kind,
367
+ message: modelFailureMessage(value),
368
+ source: signalSource(value, source),
369
+ ...(stopReason !== undefined ? { stopReason } : {}),
370
+ ...(status !== undefined ? { status } : {}),
371
+ ...(code !== undefined ? { code } : {}),
372
+ ...(name !== undefined ? { name } : {}),
373
+ };
374
+ }
375
+
376
+ function fallbackSignalFromMessage(
377
+ value: unknown,
378
+ source: ModelFallbackFailureSource | undefined,
379
+ ): ModelFallbackFailureSignal | undefined {
380
+ const message = modelFailureMessage(value);
381
+ if (!message.trim()) return undefined;
382
+ const kind = fallbackKindFromMessage(message, errorName(value));
383
+ return kind === undefined ? undefined : makeSignal(kind, value, source);
384
+ }
385
+
386
+ function classifyAssistantRefusalSignal(
387
+ value: unknown,
388
+ source: ModelFallbackFailureSource | undefined,
389
+ ): ModelFallbackFailureSignal | undefined {
390
+ const codeRefusalKind = refusalKindFromCode(codeFrom(value))
391
+ ?? refusalKindFromCode(errorName(value))
392
+ ?? refusalKindFromCode(finishReasonFrom(value));
393
+ if (codeRefusalKind !== undefined) return makeSignal(codeRefusalKind, value, source);
394
+
395
+ const messageRefusalKind = refusalKindFromMessage(directMessageFrom(value) ?? "");
396
+ return messageRefusalKind === undefined ? undefined : makeSignal(messageRefusalKind, value, source);
397
+ }
398
+
399
+ function isRefusalSignal(signal: ModelFallbackFailureSignal): boolean {
400
+ return signal.kind === "cancelled" || signal.kind === "task_failure";
401
+ }
402
+
403
+ function structuredSignal(
404
+ value: unknown,
405
+ seen: Set<unknown>,
406
+ source?: ModelFallbackFailureSource,
407
+ ): ModelFallbackFailureSignal | undefined {
408
+ if (value === undefined || value === null || seen.has(value)) return undefined;
409
+ if (typeof value === "object") seen.add(value);
410
+
411
+ const stopReason = stopReasonFrom(value)?.toLowerCase();
412
+ if (stopReason === "aborted") return makeSignal("cancelled", value, source);
413
+
414
+ const directRefusalSignal = classifyAssistantRefusalSignal(value, source);
415
+ if (directRefusalSignal !== undefined) return directRefusalSignal;
416
+
417
+ const codeKind = kindFromCode(codeFrom(value));
418
+ const nameKind = kindFromCode(errorName(value));
419
+ if (codeKind === "cancelled" || nameKind === "cancelled") return makeSignal("cancelled", value, source);
420
+
421
+ let firstNestedFallbackSignal: ModelFallbackFailureSignal | undefined;
422
+ const nestedSeen = new Set(seen);
423
+ for (const diagnosticError of diagnosticErrors(value)) {
424
+ const diagnosticSignal = structuredSignal(diagnosticError, nestedSeen, "diagnostic")
425
+ ?? fallbackSignalFromMessage(diagnosticError, "diagnostic");
426
+ if (diagnosticSignal === undefined) continue;
427
+ if (isRefusalSignal(diagnosticSignal)) return diagnosticSignal;
428
+ firstNestedFallbackSignal ??= diagnosticSignal;
429
+ }
430
+
431
+ const cause = causeOf(value);
432
+ const causeSignal = structuredSignal(cause, nestedSeen, source)
433
+ ?? fallbackSignalFromMessage(cause, source);
434
+ if (causeSignal !== undefined) {
435
+ if (isRefusalSignal(causeSignal)) return causeSignal;
436
+ firstNestedFallbackSignal ??= causeSignal;
437
+ }
438
+
439
+ const statusKind = kindFromStatus(statusFrom(value));
440
+ if (statusKind !== undefined) return makeSignal(statusKind, value, source);
441
+ if (codeKind !== undefined) return makeSignal(codeKind, value, source);
442
+ if (nameKind !== undefined) return makeSignal(nameKind, value, source);
443
+
444
+ if (firstNestedFallbackSignal !== undefined) return firstNestedFallbackSignal;
445
+
446
+ if (stopReason === "error") return makeSignal("provider_unavailable", value, source);
447
+
448
+ return undefined;
449
+ }
450
+
451
+ function messageFromUnknown(value: unknown, seen: Set<unknown>): string | undefined {
452
+ if (value === undefined || value === null || seen.has(value)) return undefined;
453
+ if (typeof value === "string") return value.trim().length > 0 ? value : undefined;
454
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") return String(value);
455
+ if (typeof value === "symbol" || typeof value === "function") return undefined;
456
+ seen.add(value);
457
+
458
+ if (value instanceof Error && value.message.trim().length > 0) return value.message;
459
+ const directMessage = directMessageFrom(value);
460
+ if (directMessage !== undefined) return directMessage;
461
+
462
+ for (const diagnosticError of diagnosticErrors(value)) {
463
+ const diagnosticMessage = messageFromUnknown(diagnosticError, seen);
464
+ if (diagnosticMessage !== undefined) return diagnosticMessage;
465
+ }
466
+
467
+ const causeMessage = messageFromUnknown(causeOf(value), seen);
468
+ if (causeMessage !== undefined) return causeMessage;
469
+
470
+ const stopReason = stopReasonFrom(value);
471
+ if (stopReason !== undefined) return `Assistant message ended with stopReason:${stopReason}`;
472
+ const finishReason = finishReasonFrom(value);
473
+ if (finishReason !== undefined) return `Model request finished with finish_reason:${finishReason}`;
474
+ const status = statusFrom(value);
475
+ if (status !== undefined) return `Model request failed with status ${status}`;
476
+ const code = codeFrom(value);
477
+ if (code !== undefined) return `Model request failed with code ${String(code)}`;
478
+
479
+ return undefined;
480
+ }
481
+
482
+ export function modelFailureMessage(error: unknown): string {
483
+ const structuredMessage = messageFromUnknown(error, new Set());
484
+ if (structuredMessage !== undefined) return structuredMessage;
485
+ const rendered = String(error);
486
+ return rendered === "[object Object]" ? "Model request failed" : rendered;
487
+ }
488
+
489
+ export function normalizeModelFailureSignal(error: unknown): ModelFallbackFailureSignal {
490
+ const structured = structuredSignal(error, new Set());
491
+ if (structured !== undefined) return structured;
492
+
493
+ const message = modelFailureMessage(error);
494
+ const name = errorName(error);
495
+ const fallbackKind = message.trim().length > 0
496
+ ? fallbackKindFromMessage(message, name)
497
+ : undefined;
498
+ return {
499
+ kind: fallbackKind ?? "unknown",
500
+ message,
501
+ source: "string_fallback",
502
+ ...(name !== undefined ? { name } : {}),
503
+ };
504
+ }
505
+
506
+ export function isRetryableModelFailure(error: unknown): boolean {
507
+ if (error === undefined) return false;
508
+ const signal = normalizeModelFailureSignal(error);
509
+ return FALLBACKABLE_FAILURE_KINDS.has(signal.kind);
101
510
  }
102
511
 
103
512
  export function formatModelAttemptNote(attempt: ModelAttemptSummary, nextModel?: string): string {
@@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.8.26-alpha.6] - 2026-06-06
8
+
9
+ ### Changed
10
+
11
+ - Bumped package version for the Atomic 0.8.26-alpha.6 prerelease.
12
+
13
+ ## [0.8.26-alpha.5] - 2026-06-06
14
+
15
+ ### Changed
16
+
17
+ - Bumped package version for the Atomic 0.8.26-alpha.5 prerelease.
18
+
7
19
  ## [0.8.26-alpha.4] - 2026-06-05
8
20
 
9
21
  ### Changed
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/web-access",
3
- "version": "0.8.26-alpha.4",
3
+ "version": "0.8.26-alpha.6",
4
4
  "private": true,
5
5
  "description": "Atomic extension for web search, URL fetching, GitHub repo cloning, PDF/video extraction. Fork of: https://github.com/nicobailon/pi-web-access",
6
6
  "contributors": [
@@ -6,6 +6,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.8.26-alpha.6] - 2026-06-06
10
+
11
+ ### Changed
12
+
13
+ - Bumped package version for the Atomic 0.8.26-alpha.6 prerelease.
14
+
15
+ ## [0.8.26-alpha.5] - 2026-06-06
16
+
17
+ ### Fixed
18
+
19
+ - Fixed the workflow global tool-event hook ignoring unscoped parent-session prompts instead of attributing them to running stages, preventing false `awaiting_input` / "needs attention" states from unrelated `ask_user_question` calls ([#1261](https://github.com/bastani-inc/atomic/issues/1261)).
20
+
9
21
  ## [0.8.26-alpha.4] - 2026-06-05
10
22
 
11
23
  ### Changed
@@ -412,23 +412,23 @@ export default defineWorkflow("deep-research-codebase")
412
412
  );
413
413
 
414
414
  const plannerModelConfig = {
415
- model: "openai/gpt-5.5:xhigh",
415
+ model: "openai-codex/gpt-5.5:xhigh",
416
416
  fallbackModels: [
417
- "openai-codex/gpt-5.5:xhigh",
418
417
  "github-copilot/gpt-5.5:xhigh",
419
- "anthropic/claude-opus-4-8:xhigh",
418
+ "openai/gpt-5.5:xhigh",
420
419
  "github-copilot/claude-opus-4.8:xhigh",
420
+ "anthropic/claude-opus-4-8:xhigh"
421
421
  ],
422
422
  excludedTools: ["ask_user_question"],
423
423
  };
424
424
 
425
425
  const explorerModelConfig = {
426
- model: "openai/gpt-5.4-mini:low",
426
+ model: "openai-codex/gpt-5.4-mini:low",
427
427
  fallbackModels: [
428
- "openai-codex/gpt-5.4-mini:low",
429
428
  "github-copilot/gpt-5.4-mini:low",
430
- "anthropic/claude-haiku-4-5:low",
429
+ "openai/gpt-5.4-mini:low",
431
430
  "github-copilot/claude-haiku-4.5:low",
431
+ "anthropic/claude-haiku-4-5:low",
432
432
  ],
433
433
  excludedTools: ["ask_user_question"],
434
434
  };
@@ -1026,23 +1026,23 @@ export default defineWorkflow("goal")
1026
1026
  const { ledger, ledgerPath, artifactDir } = await createGoalLedger(objective);
1027
1027
 
1028
1028
  const workerModelConfig = {
1029
- model: "openai/gpt-5.5:medium",
1029
+ model: "openai-codex/gpt-5.5:medium",
1030
1030
  fallbackModels: [
1031
- "openai-codex/gpt-5.5:medium",
1032
- "github-copilot/gpt-5.5:medium",
1033
- "anthropic/claude-opus-4-8:medium",
1034
- "github-copilot/claude-opus-4.8:medium",
1031
+ "github-copilot/gpt-5.5:medium",
1032
+ "openai/gpt-5.5:medium",
1033
+ "github-copilot/claude-opus-4.8:medium",
1034
+ "anthropic/claude-opus-4-8:medium",
1035
1035
  ],
1036
1036
  tools: goalRunnerTools,
1037
1037
  };
1038
1038
 
1039
1039
  const reviewerModelConfig = {
1040
- model: "openai/gpt-5.5:xhigh",
1040
+ model: "openai-codex/gpt-5.5:xhigh",
1041
1041
  fallbackModels: [
1042
- "openai-codex/gpt-5.5:xhigh",
1043
- "github-copilot/gpt-5.5:xhigh",
1044
- "anthropic/claude-opus-4-8:xhigh",
1045
- "github-copilot/claude-opus-4.8:xhigh",
1042
+ "github-copilot/gpt-5.5:xhigh",
1043
+ "openai/gpt-5.5:xhigh",
1044
+ "github-copilot/claude-opus-4.8:xhigh",
1045
+ "anthropic/claude-opus-4-8:xhigh"
1046
1046
  ],
1047
1047
  tools: [...goalRunnerTools, reviewDecisionTool.name],
1048
1048
  customTools: [reviewDecisionTool],
@@ -226,11 +226,11 @@ export default defineWorkflow("open-claude-design")
226
226
  const specFileUrl = `file://${specPath}`;
227
227
 
228
228
  const designModelConfig = {
229
- model: "anthropic/claude-opus-4-8:xhigh",
229
+ model: "github-copilot/claude-opus-4.8:xhigh",
230
230
  fallbackModels: [
231
- "github-copilot/claude-opus-4.8:xhigh",
232
- "anthropic/claude-sonnet-4-6:high",
233
- "github-copilot/claude-sonnet-4.6:high",
231
+ "anthropic/claude-opus-4-8:xhigh",
232
+ "github-copilot/claude-sonnet-4.6:high",
233
+ "anthropic/claude-sonnet-4-6:high",
234
234
  ],
235
235
  };
236
236
 
@@ -615,45 +615,45 @@ async function runRalphWorkflow(
615
615
  let iterationsCompleted = 0;
616
616
 
617
617
  const plannerModelConfig = {
618
- model: "openai/gpt-5.5:xhigh",
618
+ model: "openai-codex/gpt-5.5:xhigh",
619
619
  fallbackModels: [
620
- "openai-codex/gpt-5.5:xhigh",
621
- "github-copilot/gpt-5.5:xhigh",
622
- "anthropic/claude-opus-4-8:xhigh",
623
- "github-copilot/claude-opus-4.8:xhigh",
620
+ "github-copilot/gpt-5.5:xhigh",
621
+ "openai/gpt-5.5:xhigh",
622
+ "github-copilot/claude-opus-4.8:xhigh",
623
+ "anthropic/claude-opus-4-8:xhigh",
624
624
  ],
625
625
  excludedTools: ["ask_user_question"],
626
626
  };
627
627
 
628
628
  const orchestratorModelConfig = {
629
- model: "openai/gpt-5.5:medium",
629
+ model: "openai-codex/gpt-5.5:medium",
630
630
  fallbackModels: [
631
- "openai-codex/gpt-5.5:medium",
632
- "github-copilot/gpt-5.5:medium",
633
- "anthropic/claude-opus-4-8:medium",
634
- "github-copilot/claude-opus-4.8:medium",
631
+ "github-copilot/gpt-5.5:medium",
632
+ "openai/gpt-5.5:medium",
633
+ "github-copilot/claude-opus-4.8:medium",
634
+ "anthropic/claude-opus-4-8:medium",
635
635
  ],
636
636
  excludedTools: ["ask_user_question"],
637
637
  };
638
638
 
639
639
  const simplifierModelConfig = {
640
- model: "openai/gpt-5.5:medium",
640
+ model: "openai-codex/gpt-5.5:medium",
641
641
  fallbackModels: [
642
- "openai-codex/gpt-5.5:medium",
643
642
  "github-copilot/gpt-5.5:medium",
644
- "anthropic/claude-opus-4-8:medium",
643
+ "openai/gpt-5.5:medium",
645
644
  "github-copilot/claude-opus-4.8:medium",
645
+ "anthropic/claude-opus-4-8:medium",
646
646
  ],
647
647
  excludedTools: ["ask_user_question"],
648
648
  };
649
649
 
650
650
  const reviewerModelConfig = {
651
- model: "openai/gpt-5.5:xhigh",
651
+ model: "openai-codex/gpt-5.5:xhigh",
652
652
  fallbackModels: [
653
- "openai-codex/gpt-5.5:xhigh",
654
653
  "github-copilot/gpt-5.5:xhigh",
655
- "anthropic/claude-opus-4-8:xhigh",
654
+ "openai/gpt-5.5:xhigh",
656
655
  "github-copilot/claude-opus-4.8:xhigh",
656
+ "anthropic/claude-opus-4-8:xhigh"
657
657
  ],
658
658
  excludedTools: ["ask_user_question"],
659
659
  customTools: [reviewDecisionTool],
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/workflows",
3
- "version": "0.8.26-alpha.4",
3
+ "version": "0.8.26-alpha.6",
4
4
  "private": true,
5
5
  "description": "Atomic extension for multi-stage workflow authoring and execution.",
6
6
  "contributors": [
@@ -1795,7 +1795,11 @@ export function makeExecuteWorkflowTool(
1795
1795
  const isPaused =
1796
1796
  run?.status === "paused" ||
1797
1797
  (run?.stages.some((s) => s.status === "paused") ?? false);
1798
- if (!isPaused && run?.status === "failed" && run.endedAt !== undefined && run.resumable !== false) {
1798
+ const isResumableContinuation = run !== undefined && !isPaused && (
1799
+ (run.status === "failed" && run.endedAt !== undefined && run.resumable !== false) ||
1800
+ (run.endedAt === undefined && run.resumable === true && run.failureRecoverability === "recoverable")
1801
+ );
1802
+ if (isResumableContinuation) {
1799
1803
  const continuation = activeRuntime.resumeFailedRun(stageRunId, stage.stageId, { policy });
1800
1804
  return {
1801
1805
  action: "resume",
@@ -3133,7 +3137,11 @@ function factory(pi: ExtensionAPI): void {
3133
3137
  const isPaused =
3134
3138
  run?.status === "paused" ||
3135
3139
  (run?.stages.some((s) => s.status === "paused") ?? false);
3136
- if (!isPaused && run?.status === "failed" && run.endedAt !== undefined && run.resumable !== false) {
3140
+ const isResumableContinuation = run !== undefined && !isPaused && (
3141
+ (run.status === "failed" && run.endedAt !== undefined && run.resumable !== false) ||
3142
+ (run.endedAt === undefined && run.resumable === true && run.failureRecoverability === "recoverable")
3143
+ );
3144
+ if (isResumableContinuation) {
3137
3145
  const continuation = runtimeForContext(ctx).resumeFailedRun(stageRunId, stageId, { policy });
3138
3146
  if (continuation.ok) {
3139
3147
  print(continuation.message);