@bastani/atomic 0.9.14-alpha.3 → 0.9.14-alpha.5

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 (150) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/builtin/intercom/package.json +1 -1
  3. package/dist/builtin/mcp/package.json +1 -1
  4. package/dist/builtin/subagents/CHANGELOG.md +6 -0
  5. package/dist/builtin/subagents/package.json +1 -1
  6. package/dist/builtin/subagents/src/extension/schemas.ts +5 -0
  7. package/dist/builtin/subagents/src/runs/shared/long-running-guard.ts +3 -1
  8. package/dist/builtin/subagents/src/runs/shared/progress-trend.ts +69 -0
  9. package/dist/builtin/subagents/src/runs/shared/subagent-control.ts +12 -1
  10. package/dist/builtin/subagents/src/shared/types-results.ts +2 -0
  11. package/dist/builtin/web-access/CHANGELOG.md +10 -0
  12. package/dist/builtin/web-access/README.md +3 -6
  13. package/dist/builtin/web-access/index.ts +1 -2
  14. package/dist/builtin/web-access/package.json +1 -1
  15. package/dist/builtin/web-access/web-search-config.ts +2 -8
  16. package/dist/builtin/web-access/web-search-tool.ts +2 -7
  17. package/dist/builtin/web-access/web-search-workflow.ts +10 -0
  18. package/dist/builtin/workflows/CHANGELOG.md +46 -0
  19. package/dist/builtin/workflows/README.md +6 -5
  20. package/dist/builtin/workflows/builtin/adversarial-verification-prompts.ts +13 -5
  21. package/dist/builtin/workflows/builtin/adversarial-verification-runner.ts +376 -89
  22. package/dist/builtin/workflows/builtin/adversarial-verification.d.ts +30 -6
  23. package/dist/builtin/workflows/builtin/adversarial-verification.ts +14 -9
  24. package/dist/builtin/workflows/builtin/generate-and-filter-prompts.ts +26 -3
  25. package/dist/builtin/workflows/builtin/generate-and-filter-runner.ts +18 -14
  26. package/dist/builtin/workflows/builtin/goal-artifacts.ts +9 -8
  27. package/dist/builtin/workflows/builtin/goal-convergence.ts +87 -0
  28. package/dist/builtin/workflows/builtin/goal-ledger.ts +4 -0
  29. package/dist/builtin/workflows/builtin/goal-prompts.ts +2 -0
  30. package/dist/builtin/workflows/builtin/goal-reducer.ts +6 -1
  31. package/dist/builtin/workflows/builtin/goal-reverify.ts +305 -0
  32. package/dist/builtin/workflows/builtin/goal-runner.ts +75 -10
  33. package/dist/builtin/workflows/builtin/goal-schemas.ts +7 -0
  34. package/dist/builtin/workflows/builtin/goal-types.ts +6 -0
  35. package/dist/builtin/workflows/builtin/loop-until-done-runner.ts +94 -6
  36. package/dist/builtin/workflows/builtin/loop-until-done.d.ts +8 -0
  37. package/dist/builtin/workflows/builtin/loop-until-done.ts +15 -0
  38. package/dist/builtin/workflows/builtin/progress-scoring.ts +230 -0
  39. package/dist/builtin/workflows/builtin/ralph-core.ts +11 -0
  40. package/dist/builtin/workflows/builtin/ralph-review-gate.ts +1 -0
  41. package/dist/builtin/workflows/builtin/ralph-reviewer-prompt.ts +2 -0
  42. package/dist/builtin/workflows/builtin/ralph-runner.ts +60 -10
  43. package/dist/builtin/workflows/builtin/selection-math.ts +156 -0
  44. package/dist/builtin/workflows/builtin/shared-prompts.ts +5 -0
  45. package/dist/builtin/workflows/builtin/tournament-prompts.ts +57 -75
  46. package/dist/builtin/workflows/builtin/tournament-runner.ts +384 -178
  47. package/dist/builtin/workflows/builtin/tournament.d.ts +46 -17
  48. package/dist/builtin/workflows/builtin/tournament.ts +66 -32
  49. package/dist/builtin/workflows/builtin/verification-criteria.ts +330 -0
  50. package/dist/builtin/workflows/builtin/verification-prompts.ts +206 -0
  51. package/dist/builtin/workflows/builtin/verification-usage.ts +44 -0
  52. package/dist/builtin/workflows/package.json +1 -1
  53. package/dist/builtin/workflows/skills/create-spec/SKILL.md +90 -30
  54. package/dist/builtin/workflows/skills/show-me/LICENSE.txt +21 -0
  55. package/dist/builtin/workflows/skills/show-me/SKILL.md +143 -0
  56. package/dist/builtin/workflows/src/authoring/workflow.ts +8 -0
  57. package/dist/builtin/workflows/src/authoring.d.ts +1 -1
  58. package/dist/builtin/workflows/src/durable/completed-catalog.ts +5 -2
  59. package/dist/builtin/workflows/src/durable/dbos-envelope.ts +1 -1
  60. package/dist/builtin/workflows/src/durable/resume-eligibility.ts +5 -3
  61. package/dist/builtin/workflows/src/durable/run-timing.ts +41 -10
  62. package/dist/builtin/workflows/src/durable/tool-primitive.ts +24 -2
  63. package/dist/builtin/workflows/src/engine/options.ts +1 -0
  64. package/dist/builtin/workflows/src/engine/primitives/workflow.ts +12 -3
  65. package/dist/builtin/workflows/src/engine/run-budget.ts +308 -0
  66. package/dist/builtin/workflows/src/engine/run-returned-status.ts +8 -0
  67. package/dist/builtin/workflows/src/engine/run-tool-node-lifecycle.ts +6 -0
  68. package/dist/builtin/workflows/src/engine/run.ts +124 -2
  69. package/dist/builtin/workflows/src/engine/runtime.ts +9 -0
  70. package/dist/builtin/workflows/src/extension/config-file-loader.ts +6 -0
  71. package/dist/builtin/workflows/src/extension/config-loader.ts +24 -1
  72. package/dist/builtin/workflows/src/extension/dispatcher.ts +6 -5
  73. package/dist/builtin/workflows/src/extension/extension-runtime-state.ts +2 -0
  74. package/dist/builtin/workflows/src/extension/index.bundle.mjs +3033 -872
  75. package/dist/builtin/workflows/src/extension/lifecycle-notifications.ts +51 -4
  76. package/dist/builtin/workflows/src/extension/public-types.ts +3 -1
  77. package/dist/builtin/workflows/src/extension/runtime-durable-resume.ts +7 -1
  78. package/dist/builtin/workflows/src/extension/runtime.ts +22 -10
  79. package/dist/builtin/workflows/src/extension/workflow-module-loader.ts +5 -0
  80. package/dist/builtin/workflows/src/extension/workflow-prompts.ts +1 -0
  81. package/dist/builtin/workflows/src/extension/workflow-schema.ts +16 -0
  82. package/dist/builtin/workflows/src/extension/workflow-status-summary.ts +44 -1
  83. package/dist/builtin/workflows/src/extension/workflow-tool-content.ts +10 -1
  84. package/dist/builtin/workflows/src/extension/workflow-tool-control.ts +21 -9
  85. package/dist/builtin/workflows/src/runs/foreground/executor-continuation.ts +14 -0
  86. package/dist/builtin/workflows/src/runs/foreground/executor-lifecycle.ts +15 -4
  87. package/dist/builtin/workflows/src/runs/foreground/executor-stage-call.ts +62 -5
  88. package/dist/builtin/workflows/src/runs/foreground/executor-stage-factory.ts +4 -0
  89. package/dist/builtin/workflows/src/runs/foreground/executor-stage-types.ts +2 -0
  90. package/dist/builtin/workflows/src/runs/foreground/executor-types.ts +3 -1
  91. package/dist/builtin/workflows/src/runs/foreground/stage-runner-controller.ts +10 -1
  92. package/dist/builtin/workflows/src/shared/authoring-contract-stage.d.ts +1 -0
  93. package/dist/builtin/workflows/src/shared/authoring-contract-stage.ts +1 -0
  94. package/dist/builtin/workflows/src/shared/authoring-contract-ui.d.ts +7 -0
  95. package/dist/builtin/workflows/src/shared/authoring-contract-ui.ts +7 -0
  96. package/dist/builtin/workflows/src/shared/authoring-contract.d.ts +1 -0
  97. package/dist/builtin/workflows/src/shared/budget-meter.ts +34 -0
  98. package/dist/builtin/workflows/src/shared/budget.d.ts +67 -0
  99. package/dist/builtin/workflows/src/shared/budget.ts +127 -0
  100. package/dist/builtin/workflows/src/shared/persistence-restore-helpers.ts +92 -8
  101. package/dist/builtin/workflows/src/shared/persistence-restore.ts +11 -1
  102. package/dist/builtin/workflows/src/shared/persistence-session-entries.ts +15 -3
  103. package/dist/builtin/workflows/src/shared/returned-run-status.ts +35 -2
  104. package/dist/builtin/workflows/src/shared/store-public-types.ts +4 -1
  105. package/dist/builtin/workflows/src/shared/store-run-methods.ts +8 -1
  106. package/dist/builtin/workflows/src/shared/store-stage-methods.ts +1 -0
  107. package/dist/builtin/workflows/src/shared/store-types.ts +24 -0
  108. package/dist/builtin/workflows/src/shared/types.ts +3 -0
  109. package/dist/builtin/workflows/src/shared/workflow-artifacts.ts +1 -0
  110. package/dist/builtin/workflows/src/shared/workflow-authoring-types.d.ts +3 -0
  111. package/dist/builtin/workflows/src/shared/workflow-authoring-types.ts +3 -0
  112. package/dist/builtin/workflows/src/tui/graph-theme.ts +11 -0
  113. package/dist/builtin/workflows/src/tui/graph-view-render.ts +19 -10
  114. package/dist/builtin/workflows/src/tui/tool-detail.ts +45 -26
  115. package/dist/core/atomic-guide-command.d.ts.map +1 -1
  116. package/dist/core/atomic-guide-command.js +1 -0
  117. package/dist/core/atomic-guide-command.js.map +1 -1
  118. package/dist/core/extensions/ui-types.d.ts +13 -3
  119. package/dist/core/extensions/ui-types.d.ts.map +1 -1
  120. package/dist/core/extensions/ui-types.js +15 -3
  121. package/dist/core/extensions/ui-types.js.map +1 -1
  122. package/dist/core/slash-commands.d.ts.map +1 -1
  123. package/dist/core/slash-commands.js +33 -3
  124. package/dist/core/slash-commands.js.map +1 -1
  125. package/dist/main-deferred-startup.d.ts.map +1 -1
  126. package/dist/main-deferred-startup.js +6 -2
  127. package/dist/main-deferred-startup.js.map +1 -1
  128. package/dist/modes/interactive/interactive-startup.js +4 -0
  129. package/dist/modes/interactive/interactive-startup.js.map +1 -1
  130. package/dist/modes/interactive/interactive-tui.d.ts.map +1 -1
  131. package/dist/modes/interactive/interactive-tui.js +19 -1
  132. package/dist/modes/interactive/interactive-tui.js.map +1 -1
  133. package/dist/modes/interactive-engine/isolated-runtime.d.ts +7 -0
  134. package/dist/modes/interactive-engine/isolated-runtime.d.ts.map +1 -1
  135. package/dist/modes/interactive-engine/isolated-runtime.js +94 -37
  136. package/dist/modes/interactive-engine/isolated-runtime.js.map +1 -1
  137. package/dist/modes/rpc/rpc-client.d.ts +1 -0
  138. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  139. package/dist/modes/rpc/rpc-client.js +15 -2
  140. package/dist/modes/rpc/rpc-client.js.map +1 -1
  141. package/dist/modes/rpc/rpc-input-scheduler.d.ts +3 -2
  142. package/dist/modes/rpc/rpc-input-scheduler.d.ts.map +1 -1
  143. package/dist/modes/rpc/rpc-input-scheduler.js +5 -2
  144. package/dist/modes/rpc/rpc-input-scheduler.js.map +1 -1
  145. package/docs/extensions.md +1 -1
  146. package/docs/quickstart.md +1 -0
  147. package/docs/skills.md +4 -0
  148. package/docs/workflows.md +75 -11
  149. package/npm-shrinkwrap.json +29 -29
  150. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -2,6 +2,29 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.9.14-alpha.5] - 2026-08-19
6
+
7
+ ### Added
8
+
9
+ - Added HumanLayer's bundled `show-me` skill for visual explanations, diagrams, code-shape sketches, and focused HTML artifacts. Sourced from https://github.com/humanlayer/skills and distributed under the MIT License.
10
+
11
+ ### Fixed
12
+
13
+ - Interactive startup now exits quietly when Ctrl+C stops the engine during first-paint binding, without leaking expected in-flight RPC transport failures.
14
+ - Bare `/` and slash-command drafts typed while explicitly loaded extension and workflow packages finish loading now remain in the interactive editor until Enter is pressed.
15
+ - Queue-control RPC frames now remain reachable during long-running prompts. When all overlapping remote `clear_queue` calls fail, Atomic restores the pre-clear host queue only if no later `queue_update` has supplied engine truth, including an empty queue ([#2516](https://github.com/bastani-inc/atomic/issues/2516)).
16
+ - The `ask_user_question` dialog no longer draws one preview row at the wrong horizontal offset. In side-by-side layout the preview box border landed 11-13 columns early on the row carrying the `❯` cursor, cutting into the option label, and the damage followed the cursor between options. The active-row mark that caused it now terminates its APC string with ST as ECMA-48 requires instead of BEL, and both interactive renderers strip the mark centrally before painting, so it no longer reaches the terminal from any host — including workflow stage chats and other custom-UI mounts that do not go through a reserving bottom overlay.
17
+
18
+ ## [0.9.14-alpha.4] - 2026-08-18
19
+
20
+ ### Changed
21
+
22
+ - Bundled `web_search` no longer opens the curator confirmation UI by default. Persist `"workflow": "summary-review"` in web-search config or run `/curator on` to opt in.
23
+
24
+ ### Removed
25
+
26
+ - The `workflow` parameter on bundled `web_search`. Models cannot select the curator per call.
27
+
5
28
  ## [0.9.14-alpha.3] - 2026-08-17
6
29
 
7
30
  ### Changed
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/intercom",
3
- "version": "0.9.14-alpha.3",
3
+ "version": "0.9.14-alpha.5",
4
4
  "private": true,
5
5
  "description": "Atomic extension providing a private coordination channel between parent and child agent sessions. Fork of: https://github.com/nicobailon/pi-intercom",
6
6
  "contributors": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/mcp",
3
- "version": "0.9.14-alpha.3",
3
+ "version": "0.9.14-alpha.5",
4
4
  "private": true,
5
5
  "description": "Atomic extension that adapts MCP (Model Context Protocol) servers into the coding agent. Fork of: https://github.com/nicobailon/pi-mcp-adapter",
6
6
  "contributors": [
@@ -2,6 +2,12 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.9.14-alpha.5] - 2026-08-19
6
+
7
+ ### Added
8
+
9
+ - Added pure progress-trend evidence and injected control score-series support that can raise wall-clock attention priority without creating failure states, suppressing signals, or scheduling model calls ([#2489](https://github.com/bastani-inc/atomic/issues/2489)).
10
+
5
11
  ## [0.9.14-alpha.3] - 2026-08-17
6
12
 
7
13
  ### Changed
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/subagents",
3
- "version": "0.9.14-alpha.3",
3
+ "version": "0.9.14-alpha.5",
4
4
  "private": true,
5
5
  "description": "Atomic extension for delegating tasks to subagents with parallel execution. Fork of: https://github.com/nicobailon/pi-subagents",
6
6
  "contributors": [
@@ -93,6 +93,11 @@ const ControlOverrides = Type.Object({
93
93
  description: "Consecutive mutating-tool failures before escalating to needs_attention (default: 3)",
94
94
  }),
95
95
  ),
96
+ progressScores: Type.Optional(
97
+ Type.Array(Type.Number(), {
98
+ description: "Optional injected progress scores used only to raise attention priority; never a run outcome.",
99
+ }),
100
+ ),
96
101
  notifyOn: Type.Optional(
97
102
  Type.Array(Type.String({ enum: ["active_long_running", "needs_attention"] }), {
98
103
  description:
@@ -1,4 +1,5 @@
1
1
  import type { ResolvedControlConfig } from "../../shared/types.js";
2
+ import { progressAwareThreshold } from "./progress-trend.js";
2
3
 
3
4
  interface LongRunningNoticeMetrics {
4
5
  startedAt: number;
@@ -121,7 +122,8 @@ export function nextLongRunningTrigger(
121
122
  config: ResolvedControlConfig,
122
123
  metrics: LongRunningNoticeMetrics,
123
124
  ): LongRunningTriggerReason | undefined {
124
- if (metrics.now - metrics.startedAt >= config.activeNoticeAfterMs) return "time_threshold";
125
+ const activeNoticeAfterMs = progressAwareThreshold(config.activeNoticeAfterMs, config.progressScores);
126
+ if (metrics.now - metrics.startedAt >= activeNoticeAfterMs) return "time_threshold";
125
127
  if (config.activeNoticeAfterTurns !== undefined && metrics.turns >= config.activeNoticeAfterTurns)
126
128
  return "turn_threshold";
127
129
  if (config.activeNoticeAfterTokens !== undefined && metrics.tokens >= config.activeNoticeAfterTokens)
@@ -0,0 +1,69 @@
1
+ /** Pure progress-trend evidence shared by subagent control decisions. */
2
+
3
+ export const DEFAULT_TREND_WINDOW = 3;
4
+ export const DEFAULT_RISE_DELTA = 1.5;
5
+ export const DEFAULT_FALL_DELTA = -1.5;
6
+ export const FLAT_LOW_SCORE_CEILING = 8;
7
+ export const PROGRESS_ATTENTION_THRESHOLD_FRACTION = 0.5;
8
+
9
+ export type TrendConfig = {
10
+ window?: number;
11
+ riseDelta?: number;
12
+ fallDelta?: number;
13
+ };
14
+
15
+ export type Trend = "rising" | "flat" | "regressing";
16
+
17
+ export type TrendResult = {
18
+ trend: Trend;
19
+ evidence: {
20
+ series: readonly number[];
21
+ window: number;
22
+ delta: number;
23
+ };
24
+ };
25
+
26
+ function mean(values: readonly number[]): number {
27
+ if (values.length === 0) return 0;
28
+ return values.reduce((total, value) => total + value, 0) / values.length;
29
+ }
30
+
31
+ /**
32
+ * Classify a score series using the same deterministic hysteresis as workflows.
33
+ * The result is evidence only: it carries no run action or terminal status.
34
+ */
35
+ export function classify_trend(series: readonly number[], config: TrendConfig = {}): TrendResult {
36
+ const window = config.window ?? DEFAULT_TREND_WINDOW;
37
+ const riseDelta = config.riseDelta ?? DEFAULT_RISE_DELTA;
38
+ const fallDelta = config.fallDelta ?? DEFAULT_FALL_DELTA;
39
+ if (series.length < window + 1) {
40
+ return { trend: "flat", evidence: { series, window, delta: 0 } };
41
+ }
42
+ const sample = series.slice(-2 * window);
43
+ const usableLength = sample.length % 2 === 1 ? sample.length - 1 : sample.length;
44
+ const halfLength = usableLength / 2;
45
+ const leading = sample.slice(0, halfLength);
46
+ const trailing = sample.slice(sample.length - halfLength);
47
+ const delta = halfLength === 0 ? 0 : mean(trailing) - mean(leading);
48
+ const trend: Trend = delta >= riseDelta ? "rising" : delta <= fallDelta ? "regressing" : "flat";
49
+ return { trend, evidence: { series, window, delta } };
50
+ }
51
+
52
+ function hasFiniteProgressScores(series: readonly number[] | undefined): series is readonly number[] {
53
+ return series !== undefined && series.length > 0 && series.every((score) => Number.isFinite(score));
54
+ }
55
+
56
+ export function hasProgressAttentionSignal(series: readonly number[] | undefined): boolean {
57
+ if (!hasFiniteProgressScores(series)) return false;
58
+ const result = classify_trend(series);
59
+ const latest = series.at(-1);
60
+ return (
61
+ result.trend === "regressing" ||
62
+ (result.trend === "flat" && latest !== undefined && latest <= FLAT_LOW_SCORE_CEILING)
63
+ );
64
+ }
65
+
66
+ /** Lower a wall-clock threshold only when progress evidence raises priority. */
67
+ export function progressAwareThreshold(baseMs: number, series: readonly number[] | undefined): number {
68
+ return hasProgressAttentionSignal(series) ? Math.max(1, baseMs * PROGRESS_ATTENTION_THRESHOLD_FRACTION) : baseMs;
69
+ }
@@ -6,6 +6,7 @@ import type {
6
6
  ControlNotificationChannel,
7
7
  ResolvedControlConfig,
8
8
  } from "../../shared/types.js";
9
+ import { progressAwareThreshold } from "./progress-trend.js";
9
10
 
10
11
  const CONTROL_EVENT_TYPES: ControlEventType[] = ["active_long_running", "needs_attention"];
11
12
  const CONTROL_NOTIFICATION_CHANNELS: ControlNotificationChannel[] = ["event", "intercom"];
@@ -34,6 +35,12 @@ function parseControlList<T extends string>(value: unknown, allowed: readonly T[
34
35
  return parsed.length > 0 ? Array.from(new Set(parsed)) : undefined;
35
36
  }
36
37
 
38
+ function parseProgressScores(value: unknown): number[] | undefined {
39
+ if (!Array.isArray(value)) return undefined;
40
+ const parsed = value.filter((entry): entry is number => typeof entry === "number" && Number.isFinite(entry));
41
+ return parsed.length > 0 || value.length === 0 ? parsed : undefined;
42
+ }
43
+
37
44
  export function resolveControlConfig(globalConfig?: ControlConfig, override?: ControlConfig): ResolvedControlConfig {
38
45
  const enabled = override?.enabled ?? globalConfig?.enabled ?? DEFAULT_CONTROL_CONFIG.enabled;
39
46
  const needsAttentionAfterMs =
@@ -52,6 +59,8 @@ export function resolveControlConfig(globalConfig?: ControlConfig, override?: Co
52
59
  parsePositiveInt(override?.failedToolAttemptsBeforeAttention) ??
53
60
  parsePositiveInt(globalConfig?.failedToolAttemptsBeforeAttention) ??
54
61
  DEFAULT_CONTROL_CONFIG.failedToolAttemptsBeforeAttention;
62
+ const progressScores =
63
+ parseProgressScores(override?.progressScores) ?? parseProgressScores(globalConfig?.progressScores);
55
64
  const notifyOn =
56
65
  parseControlList(override?.notifyOn, CONTROL_EVENT_TYPES) ??
57
66
  parseControlList(globalConfig?.notifyOn, CONTROL_EVENT_TYPES) ??
@@ -67,6 +76,7 @@ export function resolveControlConfig(globalConfig?: ControlConfig, override?: Co
67
76
  activeNoticeAfterTurns,
68
77
  activeNoticeAfterTokens,
69
78
  failedToolAttemptsBeforeAttention,
79
+ ...(progressScores === undefined ? {} : { progressScores: [...progressScores] }),
70
80
  notifyOn: [...notifyOn],
71
81
  notifyChannels: [...notifyChannels],
72
82
  };
@@ -82,7 +92,8 @@ export function deriveActivityState(input: {
82
92
  const now = input.now ?? Date.now();
83
93
  const lastActivity = input.lastActivityAt ?? input.startedAt;
84
94
  const ageMs = Math.max(0, now - lastActivity);
85
- return ageMs > input.config.needsAttentionAfterMs ? "needs_attention" : undefined;
95
+ const attentionThreshold = progressAwareThreshold(input.config.needsAttentionAfterMs, input.config.progressScores);
96
+ return ageMs > attentionThreshold ? "needs_attention" : undefined;
86
97
  }
87
98
 
88
99
  export function buildControlEvent(input: {
@@ -55,6 +55,7 @@ export interface ControlConfig {
55
55
  activeNoticeAfterTurns?: number;
56
56
  activeNoticeAfterTokens?: number;
57
57
  failedToolAttemptsBeforeAttention?: number;
58
+ progressScores?: number[];
58
59
  notifyOn?: ControlEventType[];
59
60
  notifyChannels?: ControlNotificationChannel[];
60
61
  }
@@ -66,6 +67,7 @@ export interface ResolvedControlConfig {
66
67
  activeNoticeAfterTurns?: number;
67
68
  activeNoticeAfterTokens?: number;
68
69
  failedToolAttemptsBeforeAttention: number;
70
+ progressScores?: number[];
69
71
  notifyOn: ControlEventType[];
70
72
  notifyChannels: ControlNotificationChannel[];
71
73
  }
@@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.9.14-alpha.4] - 2026-08-18
8
+
9
+ ### Changed
10
+
11
+ - The interactive search curator is now opt-in. `web_search` returns raw results unless you set `"workflow": "summary-review"` in web-search config or run `/curator on`.
12
+
13
+ ### Removed
14
+
15
+ - The `workflow` parameter on `web_search`. The curator is no longer selectable per tool call; enable it with web-search config or `/curator`.
16
+
7
17
  ## [0.9.14-alpha.3] - 2026-08-17
8
18
 
9
19
  ### Fixed
@@ -78,7 +78,7 @@ For batch search/fetch calls, partial success remains usable and retains item-le
78
78
 
79
79
  ### web_search
80
80
 
81
- Search the web via Exa, Perplexity AI, or Gemini. Returns a synthesized answer with source citations.
81
+ Search the web via Exa, Perplexity AI, or Gemini. Returns a synthesized answer with source citations. The curator UI is off by default; enable it with `/curator on` or `"workflow": "summary-review"` in config.
82
82
 
83
83
  ```typescript
84
84
  web_search({ query: "rust async programming" })
@@ -87,8 +87,6 @@ web_search({ query: "latest news", numResults: 10, recencyFilter: "week" })
87
87
  web_search({ query: "...", domainFilter: ["github.com"] })
88
88
  web_search({ query: "...", provider: "exa" })
89
89
  web_search({ query: "...", includeContent: true })
90
- web_search({ queries: ["query 1", "query 2"], workflow: "none" })
91
- web_search({ queries: ["query 1", "query 2"], workflow: "summary-review" })
92
90
  ```
93
91
 
94
92
  | Parameter | Description |
@@ -99,7 +97,6 @@ web_search({ queries: ["query 1", "query 2"], workflow: "summary-review" })
99
97
  | `domainFilter` | Limit to domains (prefix with `-` to exclude) |
100
98
  | `provider` | `auto` (default), `exa`, `perplexity`, or `gemini` |
101
99
  | `includeContent` | Fetch full page content from sources in background |
102
- | `workflow` | `none` (skip curator) or `summary-review` (auto-generate summary draft after search completion, default) |
103
100
 
104
101
  ### code_search
105
102
 
@@ -227,7 +224,7 @@ Toggle or configure the curator workflow at runtime.
227
224
  /curator summary-review # explicit workflow
228
225
  ```
229
226
 
230
- Persists to `~/.pi/web-search.json` and takes effect on the next `web_search` call. When disabled, `web_search` returns raw results without opening the curator window.
227
+ Persists to `~/.pi/web-search.json` and takes effect on the next `web_search` call. The curator is off by default. When disabled, `web_search` returns raw results without opening the curator window.
231
228
 
232
229
  ### /search
233
230
 
@@ -287,7 +284,7 @@ All config lives in `~/.pi/web-search.json`. Every field is optional.
287
284
  }
288
285
  ```
289
286
 
290
- `EXA_API_KEY`, `GEMINI_API_KEY`, and `PERPLEXITY_API_KEY` env vars take precedence over config file values. `provider` sets the default search provider: `"exa"`, `"perplexity"`, or `"gemini"`. This is also updated automatically when you change the provider in the curator UI. `workflow` sets the default curator mode: `"summary-review"` (default, opens curator with auto-generated summary draft) or `"none"` (raw results, no curator). Overridden per-call via the `workflow` parameter on `web_search`, or toggled at runtime with `/curator`. `chromeProfile` overrides the Chromium profile directory used for Gemini Web cookie lookup. `allowBrowserCookies` enables Chromium cookie extraction for Gemini Web; it defaults to `false` to avoid surprise macOS Keychain prompts. You can also set `PI_ALLOW_BROWSER_COOKIES=1`. `searchModel` overrides the Gemini API model used by `web_search` without changing URL, YouTube, or video extraction defaults. `summaryModel` sets the default model used for generating summary drafts in the curator UI (e.g. `"anthropic/claude-haiku-4-5"` or `"openai-codex/gpt-5.3-codex-spark"`). Only models available in your model registry are eligible; if the configured model is unavailable, the default falls back to the built-in preference list. `curatorTimeoutSeconds` controls the initial curator idle timeout (default `20`, max `600`); users can still adjust the timer in the curator UI.
287
+ `EXA_API_KEY`, `GEMINI_API_KEY`, and `PERPLEXITY_API_KEY` env vars take precedence over config file values. `provider` sets the default search provider: `"exa"`, `"perplexity"`, or `"gemini"`. This is also updated automatically when you change the provider in the curator UI. `workflow` sets the curator mode: `"none"` (default, raw results, no curator) or `"summary-review"` (opens curator with auto-generated summary draft). Enable it in this file or toggle at runtime with `/curator`. `chromeProfile` overrides the Chromium profile directory used for Gemini Web cookie lookup. `allowBrowserCookies` enables Chromium cookie extraction for Gemini Web; it defaults to `false` to avoid surprise macOS Keychain prompts. You can also set `PI_ALLOW_BROWSER_COOKIES=1`. `searchModel` overrides the Gemini API model used by `web_search` without changing URL, YouTube, or video extraction defaults. `summaryModel` sets the default model used for generating summary drafts in the curator UI (e.g. `"anthropic/claude-haiku-4-5"` or `"openai-codex/gpt-5.3-codex-spark"`). Only models available in your model registry are eligible; if the configured model is unavailable, the default falls back to the built-in preference list. `curatorTimeoutSeconds` controls the initial curator idle timeout (default `20`, max `600`); users can still adjust the timer in the curator UI.
291
288
 
292
289
  ### Shortcuts
293
290
 
@@ -320,7 +320,7 @@ export default function webAccess(pi: ExtensionAPI) {
320
320
  pi.registerTool({
321
321
  name: "web_search",
322
322
  label: "Web Search",
323
- description: "Search the web using Perplexity AI, Exa, or Gemini. Returns an AI-synthesized answer with source citations. For comprehensive research, prefer queries (plural) with 2-4 varied angles over a single query — each query gets its own synthesized answer, so varying phrasing and scope gives much broader coverage. When includeContent is true, full page content is fetched in the background. Searches auto-open the interactive browser curator and stream results live; set workflow to \"none\" to skip curation. Provider auto-selects: Exa (direct API with key, MCP fallback without), else Perplexity (needs key), else Gemini API (needs key), else Gemini Web (needs a supported Chromium-based browser login).",
323
+ description: "Search the web using Perplexity AI, Exa, or Gemini. Returns an AI-synthesized answer with source citations. For comprehensive research, prefer queries (plural) with 2-4 varied angles over a single query — each query gets its own synthesized answer, so varying phrasing and scope gives much broader coverage. When includeContent is true, full page content is fetched in the background. Searches return raw results by default. Enable the interactive browser curator with /curator on or by setting workflow to \"summary-review\" in web-search config. Provider auto-selects: Exa (direct API with key, MCP fallback without), else Perplexity (needs key), else Gemini API (needs key), else Gemini Web (needs a supported Chromium-based browser login).",
324
324
  promptSnippet: "Use for web research questions. Prefer {queries:[...]} with 2-4 varied angles over a single query for broader coverage.",
325
325
  parameters: Type.Object({
326
326
  query: Type.Optional(Type.String({ description: "Single search query. For research tasks, prefer 'queries' with multiple varied angles instead." })),
@@ -330,7 +330,6 @@ export default function webAccess(pi: ExtensionAPI) {
330
330
  recencyFilter: Type.Optional(Type.String({ enum: ["day", "week", "month", "year"], description: "Filter by recency" })),
331
331
  domainFilter: Type.Optional(Type.Array(Type.String(), { description: "Limit to domains (prefix with - to exclude)" })),
332
332
  provider: Type.Optional(Type.String({ enum: ["auto", "perplexity", "gemini", "exa"], description: "Search provider (default: auto)" })),
333
- workflow: Type.Optional(Type.String({ enum: ["none", "summary-review"], description: "Search workflow mode: none = no curator, summary-review = open curator with auto summary draft (default)" })),
334
333
  }),
335
334
  execute: (...args) => executeHeavyTool(loadHeavy, "web_search", args),
336
335
  renderResult: (...args) => renderHeavyToolResult(loadedHeavy?.heavy ?? null, "web_search", args),
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/web-access",
3
- "version": "0.9.14-alpha.3",
3
+ "version": "0.9.14-alpha.5",
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": [
@@ -33,8 +33,8 @@ export interface ProviderAvailability {
33
33
  gemini: boolean;
34
34
  }
35
35
 
36
- export type WebSearchWorkflow = "none" | "summary-review";
37
- export type CuratorWorkflow = "summary-review";
36
+ export type { CuratorWorkflow, WebSearchWorkflow } from "./web-search-workflow.js";
37
+ export { resolveWorkflow } from "./web-search-workflow.js";
38
38
 
39
39
  export interface CuratorBootstrap {
40
40
  availableProviders: ProviderAvailability;
@@ -99,12 +99,6 @@ function normalizeCuratorTimeoutSeconds(value: unknown): number | undefined {
99
99
  return Math.min(normalized, MAX_CURATOR_TIMEOUT_SECONDS);
100
100
  }
101
101
 
102
- export function resolveWorkflow(input: unknown, hasUI: boolean): WebSearchWorkflow {
103
- if (!hasUI) return "none";
104
- if (typeof input === "string" && input.trim().toLowerCase() === "none") return "none";
105
- return "summary-review";
106
- }
107
-
108
102
  export function normalizeQueryList(queryList: unknown[]): string[] {
109
103
  const normalized: string[] = [];
110
104
  for (const query of queryList) {
@@ -32,7 +32,7 @@ export function registerWebSearchTool(pi: ExtensionAPI, deps: RegisterWebSearchT
32
32
  name: "web_search",
33
33
  label: "Web Search",
34
34
  description:
35
- `Search the web using Perplexity AI, Exa, or Gemini. Returns an AI-synthesized answer with source citations. For comprehensive research, prefer queries (plural) with 2-4 varied angles over a single query — each query gets its own synthesized answer, so varying phrasing and scope gives much broader coverage. When includeContent is true, full page content is fetched in the background. Searches auto-open the interactive browser curator and stream results live; set workflow to "none" to skip curation. Provider auto-selects: Exa (direct API with key, MCP fallback without), else Perplexity (needs key), else Gemini API (needs key), else Gemini Web (needs a supported Chromium-based browser login).`,
35
+ `Search the web using Perplexity AI, Exa, or Gemini. Returns an AI-synthesized answer with source citations. For comprehensive research, prefer queries (plural) with 2-4 varied angles over a single query — each query gets its own synthesized answer, so varying phrasing and scope gives much broader coverage. When includeContent is true, full page content is fetched in the background. Searches return raw results by default. Enable the interactive browser curator with /curator on or by setting workflow to "summary-review" in web-search config. Provider auto-selects: Exa (direct API with key, MCP fallback without), else Perplexity (needs key), else Gemini API (needs key), else Gemini Web (needs a supported Chromium-based browser login).`,
36
36
  promptSnippet:
37
37
  "Use for web research questions. Prefer {queries:[...]} with 2-4 varied angles over a single query for broader coverage.",
38
38
  parameters: Type.Object({
@@ -47,11 +47,6 @@ export function registerWebSearchTool(pi: ExtensionAPI, deps: RegisterWebSearchT
47
47
  provider: Type.Optional(
48
48
  StringEnum(["auto", "perplexity", "gemini", "exa"], { description: "Search provider (default: auto)" }),
49
49
  ),
50
- workflow: Type.Optional(
51
- StringEnum(["none", "summary-review"], {
52
- description: "Search workflow mode: none = no curator, summary-review = open curator with auto summary draft (default)",
53
- }),
54
- ),
55
50
  }),
56
51
 
57
52
  async execute(_toolCallId, params, signal, onUpdate, ctx) {
@@ -60,7 +55,7 @@ export function registerWebSearchTool(pi: ExtensionAPI, deps: RegisterWebSearchT
60
55
  : (params.query !== undefined ? [params.query] : []);
61
56
  const queryList = normalizeQueryList(rawQueryList);
62
57
  const configWorkflow = loadConfigForExtensionInit().workflow;
63
- const workflow = resolveWorkflow(params.workflow ?? configWorkflow, ctx?.hasUI !== false);
58
+ const workflow = resolveWorkflow(configWorkflow, ctx?.hasUI !== false);
64
59
  const shouldCurate = workflow !== "none";
65
60
 
66
61
  if (queryList.length === 0) {
@@ -0,0 +1,10 @@
1
+ export type WebSearchWorkflow = "none" | "summary-review";
2
+ export type CuratorWorkflow = "summary-review";
3
+
4
+ export function resolveWorkflow(input: unknown, hasUI: boolean): WebSearchWorkflow {
5
+ if (!hasUI) return "none";
6
+ if (typeof input === "string" && input.trim().toLowerCase() === "summary-review") {
7
+ return "summary-review";
8
+ }
9
+ return "none";
10
+ }
@@ -6,6 +6,52 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.9.14-alpha.5] - 2026-08-19
10
+
11
+ ### Added
12
+
13
+ - Added optional run-budget declarations on workflow config, authored definitions, and `workflow` tool runs or resumes. `maxDurationMs`, `maxTokens`, `maxCost`, and `warnAtPercent` resolve per field with run overrides taking precedence over definition and config values; `0` disables a dimension. Invalid declarations now fail validation, and `budget_exceeded` is reserved as a resumable returned blocked status; these declarations provide the resolution core for the duration enforcement below ([#2212](https://github.com/bastani-inc/atomic/issues/2212)).
14
+ - Added duration enforcement for workflow budgets at stage and durable-tool boundaries. Runs emit one user-visible `budget_warning` lifecycle notice at the default 80% threshold or configured threshold, receive one wrap-up turn only when a frontier stage turn is already live, and stop on a resumable `budget_exceeded` blocked rail with a duration report; no new stage is created for wrap-up, no-live-turn boundaries leave the allowance unused, paused time is excluded, elapsed time carries across resume, a raised resume budget continues prior spend, the wrap-up turn's own usage is recorded as `wrapUpUsage` when available without tree-wide token/cost aggregation, root duration scope is enforced inside child workflows and wins simultaneous child exhaustion, and child-scoped exhaustion returns to the parent without stopping it ([#2212](https://github.com/bastani-inc/atomic/issues/2212)).
15
+ - Added tree-wide token and cost budget metering across nested workflow runs and stage retries. Uncached input and output tokens feed `maxTokens`, cache reads and writes remain reported counters, `usage.cost` feeds `maxCost`, and persisted accounting baselines prevent duplicate charges across boundaries and resume ([#2212](https://github.com/bastani-inc/atomic/issues/2212)).
16
+ - Added HumanLayer's bundled `show-me` skill for visual explanations, diagrams, code-shape sketches, and focused HTML artifacts. Sourced from https://github.com/humanlayer/skills and distributed under the MIT License.
17
+ - Updated the bundled `create-spec` skill to choose context-driven Path A or clarify-first Path B, inspect local precedent before design decisions, compare materially different alternatives, and use show-me shape-first visuals (pseudocode, call trees, component/file trees, Mermaid, diffs, and optional focused HTML). Working-method guidance is drawn from Dillon Mulroy's [tech-spec skill](https://github.com/dmmulroy/skills/blob/main/tech-spec/SKILL.md).
18
+ ### Breaking Changes
19
+
20
+ - `adversarial-verification` now accepts optional per-criterion `criteria`, `accept_mean`, and bounded `reask_limit` inputs, and returns deterministic graded mean/veto results through `mean_score` and `score_table_path`; callers must migrate from the removed `result`, `verifier_artifact_paths`, and `artifact_dir` outputs. Invalid verifier reports are recorded as invalid markers and re-asked instead of becoming fail votes ([#2487](https://github.com/bastani-inc/atomic/issues/2487)).
21
+ - Replaced `bracket_path`/`bracket.json` and the knockout tournament schedule with `comparisons_path`/`comparisons.json` on the soft-scored pivot-pairing schedule. Judge stages now use deterministic `judge-<a>-<b>-<criterion>-r<rep>` identities, and the reducer stage is named `comparisons-reducer`.
22
+ - Replaced `bracket_path`/`bracket.json` and the knockout tournament schedule with `comparisons_path`/`comparisons.json` on the soft-scored pivot-pairing schedule. Judge stages now use deterministic `judge-<a>-<b>-<criterion>-r<rep>` identities, and the reducer stage is named `comparisons-reducer`.
23
+ - `adversarial-verification` now accepts optional per-criterion `criteria`, `accept_mean`, and bounded `reask_limit` inputs, and returns deterministic graded mean/veto results through `mean_score` and `score_table_path`; callers must migrate from the removed `result`, `verifier_artifact_paths`, and `artifact_dir` outputs. Invalid verifier reports are recorded as invalid markers and re-asked instead of becoming fail votes ([#2487](https://github.com/bastani-inc/atomic/issues/2487)).
24
+
25
+ ### Added
26
+
27
+ - Added a shared `verification-criteria` module (`parse_rubric`, `normalize_criteria`, `select_criteria`, `VERIFICATION_SCALE`, `decide_verification`) so verification builtins can score one criterion at a time on an anchored 1–20 scale and accept only when a quorum mean clears the threshold with no veto finding. Unparseable reports cannot become scores and cannot shift the mean ([#2487](https://github.com/bastani-inc/atomic/issues/2487)).
28
+ - Added prefix-cache-aware verification prompts with a byte-identical shared head, UTF-8 bounded candidate inlining with whole-family read fallback, and warm-first verifier fan-out scheduling that preserves input order while releasing later phases after warm failures ([#2493](https://github.com/bastani-inc/atomic/issues/2493)).
29
+ - Added optional run-budget declarations on workflow config, authored definitions, and `workflow` tool runs or resumes. `maxDurationMs`, `maxTokens`, `maxCost`, and `warnAtPercent` resolve per field with run overrides taking precedence over definition and config values; `0` disables a dimension. Invalid declarations now fail validation, and `budget_exceeded` is reserved as a resumable returned blocked status; these declarations provide the resolution core for the duration enforcement below ([#2212](https://github.com/bastani-inc/atomic/issues/2212)).
30
+ - Added duration enforcement for workflow budgets at stage and durable-tool boundaries. Runs emit one user-visible `budget_warning` lifecycle notice at the default 80% threshold or configured threshold, receive one wrap-up turn only when a frontier stage turn is already live, and stop on a resumable `budget_exceeded` blocked rail with a duration report; no new stage is created for wrap-up, no-live-turn boundaries leave the allowance unused, paused time is excluded, elapsed time carries across resume, a raised resume budget continues prior spend, the wrap-up turn's own usage is recorded as `wrapUpUsage` when available without tree-wide token/cost aggregation, root duration scope is enforced inside child workflows and wins simultaneous child exhaustion, and child-scoped exhaustion returns to the parent without stopping it ([#2212](https://github.com/bastani-inc/atomic/issues/2212)).
31
+ - Added optional run-budget declarations on workflow config, authored definitions, and `workflow` tool runs. `maxDurationMs`, `maxTokens`, `maxCost`, and `warnAtPercent` resolve per field with run overrides taking precedence over definition and config values; `0` disables a dimension. Invalid declarations now fail validation, and `budget_exceeded` is reserved as a resumable returned blocked status. This core slice only resolves and validates budgets; it does not meter or stop runs yet ([#2212](https://github.com/bastani-inc/atomic/issues/2212)).
32
+ - Added a shared `verification-criteria` module (`parse_rubric`, `normalize_criteria`, `select_criteria`, `VERIFICATION_SCALE`, `decide_verification`) so verification builtins can score one criterion at a time on an anchored 1–20 scale and accept only when a quorum mean clears the threshold with no veto finding. Unparseable reports cannot become scores and cannot shift the mean ([#2487](https://github.com/bastani-inc/atomic/issues/2487)).
33
+ - Added prefix-cache-aware verification prompts with a byte-identical shared head, UTF-8 bounded candidate inlining with whole-family read fallback, and warm-first verifier fan-out scheduling that preserves input order while releasing later phases after warm failures ([#2493](https://github.com/bastani-inc/atomic/issues/2493)).
34
+ - Added Goal and Ralph review-round re-verification for low-confidence, single-reviewer blocking findings, using fresh 1–20 scoring stages, two-tier demotion bars, and durable per-repeat audit evidence while leaving `stop_review_loop` authoritative.
35
+ - Added pure, seeded selection math for probabilistic pivot tournaments: deterministic Hamiltonian ring and deduplicated pivot planning, criterion/repeat slot-swap jobs, normalized Bradley–Terry soft wins, count-normalized pivot selection, and complete candidate rankings ([#2488](https://github.com/bastani-inc/atomic/issues/2488)).
36
+ - Added tournament `n_evaluations`, `pivots`, `seed`, `criteria`, and heterogeneous `models` inputs, plus `ranking` and `seed` outputs and the auditable `comparisons.json` score ledger with invalid-report and budget records ([#2488](https://github.com/bastani-inc/atomic/issues/2488)).
37
+ - `adversarial-verification` now fans out one schema-validated 1–20 score for every criterion/verifier pair, applies mean-plus-veto acceptance, bounds invalid-report re-asks, and consolidates confirmed findings into repair guidance ([#2487](https://github.com/bastani-inc/atomic/issues/2487)).
38
+ - Added pure `fold_usage` accounting over every model attempt (including retries), returning `UsageTotals { calls, input, output, cacheRead, cacheWrite, cost, turns, cacheHitRate }` with a derived cache-hit rate; tournament comparisons and adversarial verification round summaries now record per-phase and total usage blocks ([#2494](https://github.com/bastani-inc/atomic/issues/2494)).
39
+ - Added observation-grounded progress scoring with batched checkpoint repeats, null-safe invalid-repeat handling, and a pure hysteretic trend classifier that reports evidence without an action ([#2489](https://github.com/bastani-inc/atomic/issues/2489)).
40
+ - Added Goal and Ralph per-round convergence evidence series with V7 trend classification, folded usage totals, and escalation-only `needs_human` evidence that never approves or terminates a review loop ([#2490](https://github.com/bastani-inc/atomic/issues/2490)).
41
+ - Added reviewer calibration rules that prioritize observed output over agent narration, plus audit-only 1–20 `criterion_scores` in Goal and Ralph review decisions without changing the authoritative closure gate ([#2494](https://github.com/bastani-inc/atomic/issues/2494)).
42
+ - Added loop-until-done progress consumers that record per-iteration advisory score curves, final trends, and calibration disclaimers in completion and exhaustion reports without changing stop decisions ([#2489](https://github.com/bastani-inc/atomic/issues/2489)).
43
+ - Added observation-grounded progress scoring with batched checkpoint repeats, null-safe invalid-repeat handling, and a pure hysteretic trend classifier that reports evidence without an action ([#2489](https://github.com/bastani-inc/atomic/issues/2489)).
44
+ - Added loop-until-done progress consumers that record per-iteration advisory score curves, final trends, and calibration disclaimers in completion and exhaustion reports without changing stop decisions ([#2489](https://github.com/bastani-inc/atomic/issues/2489)).
45
+ - Added pure, seeded selection math for probabilistic pivot tournaments: deterministic Hamiltonian ring and deduplicated pivot planning, criterion/repeat slot-swap jobs, normalized Bradley–Terry soft wins, count-normalized pivot selection, and complete candidate rankings ([#2488](https://github.com/bastani-inc/atomic/issues/2488)).
46
+ - Added tournament `n_evaluations`, `pivots`, `seed`, `criteria`, and heterogeneous `models` inputs, plus `ranking` and `seed` outputs and the auditable `comparisons.json` score ledger with invalid-report and budget records ([#2488](https://github.com/bastani-inc/atomic/issues/2488)).
47
+
48
+ ### Fixed
49
+
50
+ - Opened `ctx.tool` detail now matches the main-chat tool block: `Box(1,1)` inner padding, a blank row under the `$` header, full-width alignment with the orchestrator bars, unpainted canvas above and below the card, host `toolTitle`/`toolOutput` colors, a dim `ctrl+o Expand` hint, and second-resolution `Took`/`Elapsed` timing.
51
+ - Tournament criteria now accepts every documented V1 shape, including markdown rubrics, records, string lists, and CriterionInput objects with optional ids and names.
52
+ - Criterion ids are preserved in the ledger while judge artifact filenames are sanitized, collision-free, and contained under the artifact directory.
53
+ - Corrected the planned comparison budget for oversized pivot counts and recorded each comparison row's own soft preference separately from pair aggregates.
54
+
9
55
  ## [0.9.14-alpha.3] - 2026-08-17
10
56
 
11
57
  ### Changed
@@ -677,10 +677,11 @@ Raw stage-chat prompt answer replay is live-memory only. `StageSnapshot.promptAn
677
677
  ```json
678
678
  {
679
679
  "name": "workflow",
680
- "description": "Run named builtin, project, user, or package workflows; custom definitions may import reusable project/package workflows or builtin definitions from @bastani/workflows/builtin and nest them with ctx.workflow(...), including deeper composition within the configured maxDepth; when workflow execution fits but another shape would better achieve the task, author a custom TypeScript workflow({...}) inline with normal coding tools, reload it, and run it; after successfully creating and reloading a newly authored custom workflow, report the folder containing its generated code as 'Custom workflow created. You can inspect its code at: <workflow-folder-path>'; do this only for newly created custom workflows, never builtin or pre-existing workflows; discover with list/get/inputs/models, list session runs with status (no runId; statusFilter narrows the list), inspect status/stages/stage details, send prompt answers or steering only while the root workflow is nonterminal, pause/resume/interrupt/quit runs, and reload workflow resources. For primitive prompt answers, use booleans or the documented confirm labels, exact case-insensitive select labels or 1-based indexes, and text strings for input/editor; an invalid answer remains pending and returns guidance instead of choosing a default. For large stage handoffs, write context to files/artifacts, pass paths via reads, and prompt downstream agents to 'Read the file at <path>...' instead of injecting large previous text. Wrap critical parts of run inputs and steering messages in <keepContext>...</keepContext> so compaction preserves them verbatim in the stages that inherit them; tag role constraints, prohibitions, must-hold criteria, and identifiers, not background or bulk reference material. For transcripts, prefer status/stages/stage to get sessionFile/transcriptPath, quote the exact path without rewriting separators (Windows backslashes are valid), then search it with rg/grep and read small ranges; transcript is path-only by default when sessionFile/transcriptPath exists, explicit tail/limit returns bounded previews, and missing transcript paths fall back to a small preview. Use action 'models' to inspect models in the configured catalog; the result is a configured-auth snapshot showing what's present in the registry with configured authentication, not proof of credentials, entitlements, OAuth freshness, or live provider access. When authoring a workflow that should dynamically select a model, first call workflow({ action: 'models' }) to inspect the configured catalog, then select from the returned provider/id entries considering the isCurrent marker and available thinking levels.",
680
+ "description": "Run named builtin, project, user, or package workflows; custom definitions may import reusable project/package workflows or builtin definitions from @bastani/workflows/builtin and nest them with ctx.workflow(...), including deeper composition within the configured maxDepth; when workflow execution fits but another shape would better achieve the task, author a custom TypeScript workflow({...}) inline with normal coding tools, reload it, and run it; after successfully creating and reloading a newly authored custom workflow, report the folder containing its generated code as 'Custom workflow created. You can inspect its code at: <workflow-folder-path>'; do this only for newly created custom workflows, never builtin or pre-existing workflows; discover with list/get/inputs/models, list session runs with status (no runId; statusFilter narrows the list), inspect status/stages/stage details, send prompt answers or steering only while the root workflow is nonterminal, pause/resume/interrupt/quit runs, and reload workflow resources. For action 'run' and 'resume', budget accepts per-field duration, token, cost, and warning overrides; fields resolve over the workflow declaration and config, and 0 disables a field. For primitive prompt answers, use booleans or the documented confirm labels, exact case-insensitive select labels or 1-based indexes, and text strings for input/editor; an invalid answer remains pending and returns guidance instead of choosing a default. For large stage handoffs, write context to files/artifacts, pass paths via reads, and prompt downstream agents to 'Read the file at <path>...' instead of injecting large previous text. Wrap critical parts of run inputs and steering messages in <keepContext>...</keepContext> so compaction preserves them verbatim in the stages that inherit them; tag role constraints, prohibitions, must-hold criteria, and identifiers, not background or bulk reference material. For transcripts, prefer status/stages/stage to get sessionFile/transcriptPath, quote the exact path without rewriting separators (Windows backslashes are valid), then search it with rg/grep and read small ranges; transcript is path-only by default when sessionFile/transcriptPath exists, explicit tail/limit returns bounded previews, and missing transcript paths fall back to a small preview. Use action 'models' to inspect models in the configured catalog; the result is a configured-auth snapshot showing what's present in the registry with configured authentication, not proof of credentials, entitlements, OAuth freshness, or live provider access. When authoring a workflow that should dynamically select a model, first call workflow({ action: 'models' }) to inspect the configured catalog, then select from the returned provider/id entries considering the isCurrent marker and available thinking levels.",
681
681
  "parameters": {
682
682
  "workflow": "string (optional) — workflow ID or normalized name",
683
683
  "inputs": "object (optional) — key/value map of workflow inputs",
684
+ "budget": "optional run-only object: maxDurationMs/maxTokens non-negative integers; maxCost/warnAtPercent non-negative numbers; 0 disables a field",
684
685
  "action": "'run' | 'list' | 'get' | 'inputs' | 'models' | 'status' | 'stages' | 'stage' | 'transcript' | 'send' | 'pause' | 'interrupt' | 'quit' | 'resume' | 'reload'",
685
686
  "runId": "optional full 36-character run id; prefixes are rejected; control actions default to the active run where safe; use '--all' or all:true for pause/interrupt/quit all",
686
687
  "stageId": "optional exact stage id or exact stage name for stage-scoped actions; prefixes and partial names are rejected; cannot be combined with all:true",
@@ -772,10 +773,10 @@ The six common patterns ship as full builtins and are discoverable/runnable by n
772
773
  |---|---|---|---|
773
774
  | `classify-and-act` | structured classifier → deterministic action; HIL fallback for low confidence | `prompt`; `categories` (3 defaults), `confidence_threshold=0.75` | result, category/confidence, classification/action paths |
774
775
  | `fan-out-and-synthesize` | partition → bounded artifact fan-out → evidence synthesis barrier | `prompt`; `max_branches=4`, `max_concurrency=4` | result, partitions, branch/synthesis/manifest paths |
775
- | `adversarial-verification` | worker → fresh rubric verifiers → reducer → bounded repair | `task`; `verifier_count=3`, `max_repairs=2` | result, approval, repairs, candidate/review/verifier paths |
776
- | `generate-and-filter` | candidate fan-out → dedupe/filter → optional judge → shortlist | `prompt`; `num_candidates=8`, `shortlist_size=3`, `use_judge=true`, `max_concurrency=4` | result, shortlist and candidate/filter/judge/final/manifest paths |
777
- | `tournament` | independent attempts → order-balanced pairwise judges → bracket reducer | `prompt`; `num_attempts=4`, `max_concurrency=4` | result, winner, attempt/judge/bracket paths |
778
- | `loop-until-done` | durable ledger → iteration/evaluator loop → complete or inspectable exhaustion | `prompt`; `max_iterations=5` | result/status, ledger, iteration/evaluation paths, remaining work |
776
+ | `adversarial-verification` | worker → per-criterion fresh rubric verifiers → deterministic mean+veto reducer → findings consolidation / bounded repair; decompose rubrics into named criteria and aggregate by mean plus explicit veto, never a unanimity AND; `criteria.md` uses `## Criteria` with non-empty `### Name {#id}` bodies and the shared `verification-criteria` doors; consolidator cannot approve | `task`; `criteria` (record or criteria.md markdown, defaults to task_fit/evidence/completeness), `verifier_count=3` (1–5), `max_repairs=2` (0–5), `accept_mean=14`, `reask_limit=1`; invalid reports are re-asked within the bound and never become fail votes; normal calls per round: criteria.length × verifier_count | `approved`, `mean_score`, `score_table_path` (`verification-summary-<round>.json` with scores, mean, invalidCount, decision, and usage), `repairs_completed`, `candidate_path`, `review_report_path`, `remaining_work` |
777
+ | `generate-and-filter` | candidate fan-out → dedupe/filter → optional judge → shortlist; use graded per-criterion scores, Bradley–Terry preference from score gaps, and slot-swap repeats when ranking candidates; custom judges can reuse the shared `verification-criteria` module and `criteria.md` rubric format | `prompt`; `num_candidates=8`, `shortlist_size=3`, `use_judge=true`, `max_concurrency=4` | result, shortlist and candidate/filter/judge/final/manifest paths |
778
+ | `tournament` | independent attempts → seeded ring and pivot-round soft scoring with order-balanced pairwise judges → full ranking/comparisons reducer; derive a Bradley–Terry preference from graded score gaps and repeat with A/B slot swaps; `comparisons.json` preserves per-job score or invalid rows, pair aggregates, ranking, and planned/executed budget records | `prompt`; `num_attempts=4`, `max_concurrency=4`, `n_evaluations=2`, `pivots=1`, `seed=0`, optional `criteria` (markdown rubric, record, string list, or `CriterionInput` list)/`models` | result, winner, `attempt_artifact_paths`, `judge_artifact_paths`, `comparisons_path`, `ranking`, `seed` |
779
+ | `loop-until-done` | durable ledger → iteration/evaluator loop → complete or inspectable exhaustion; each scored iteration records `progress` (`score`, `perRepeat`, `trend`, `window`) and the ledger emits `progress_curve`, `final_trend`, and `progress_disclaimer`; trend is advisory and never a kill switch | `prompt`; `max_iterations=5`, `progress_scoring=true`, `progress_repeats=1` | result/status, ledger, iteration/evaluation paths, remaining work, `progress_curve`, `final_trend`, `progress_disclaimer` |
779
780
 
780
781
  All six are exported from `@bastani/workflows/builtin` as definitions (`classifyAndAct`, `fanOutAndSynthesize`, `adversarialVerification`, `generateAndFilter`, `tournament`, and `loopUntilDone`). Import and nest them through `ctx.workflow(definition, { inputs, stageName })`; nested calls respect `maxDepth`. Prefer composition over copying their prompts or graphs: children contribute their stages, dedicated prompts, gates, artifacts, HIL nodes, and declared outputs to the expanded parent graph.
781
782
 
@@ -1,3 +1,5 @@
1
+ import type { Criterion } from "./verification-criteria.js";
2
+
1
3
  const GROUNDED_REPORTING = "Before reporting progress, audit each claim against a tool result from this session. Report only work you can point to evidence for; say so explicitly when something is unverified.";
2
4
  const READABLE_REPORT = "Lead with the outcome. Keep facts, decisions, caveats, and next steps; drop background and repetition. Use complete, readable sentences rather than compressed fragments.";
3
5
 
@@ -5,14 +7,20 @@ export function renderWorkerPrompt(task: string): string {
5
7
  return `<role>\nYou produce a candidate solution for independent verification.\n</role>\n\n<success_criteria>\nThe task is complete and important claims have observable support. Preserve concrete evidence and state every validation performed.\n</success_criteria>\n\n<stop_rules>\nStop when the candidate is complete and validated where practical, or state the evidence still missing.\n</stop_rules>\n\n<output_format>\nA self-contained candidate with actions taken, evidence, validation, and remaining risks. ${READABLE_REPORT}\n${GROUNDED_REPORTING}\n</output_format>\n\n<objective>\n${task}\n</objective>`;
6
8
  }
7
9
 
8
- export function renderVerifierPrompt(task: string, candidatePath: string, rubricPath: string): string {
9
- return `<artifacts>\nRead the complete candidate at ${candidatePath} and rubric at ${rubricPath}.\n</artifacts>\n\n<role>\nYou are an independent adversarial verifier. Find blockers; do not rewrite the candidate.\n</role>\n\n<success_criteria>\nA pass requires concrete evidence for every rubric item; absence of evidence is not evidence of correctness.\n</success_criteria>\n\n<evidence_rules>\nTest important claims where practical. For each conclusion, report the command run, observed output, and file:line evidence where applicable. Report precise blocking findings. ${GROUNDED_REPORTING}\n</evidence_rules>\n\n<stop_rules>\nStop after every rubric item has pass evidence or a precise blocking finding.\n</stop_rules>\n\n<output_format>\nCall structured_output with verdict (pass or fail), concise evidence, and blocking_findings. ${READABLE_REPORT}\n</output_format>\n\n<objective>\nVerify the candidate against the task and rubric. Task: ${task}\n</objective>`;
10
+ export function renderVerifierPrompt(task: string, candidatePath: string, criteriaPath: string, criterion: Criterion): string {
11
+ return `<artifacts>\nRead the complete candidate at ${candidatePath} and resolved criteria at ${criteriaPath}.\n</artifacts>\n\n<role>\nYou are an independent adversarial verifier. Score exactly one criterion and do not rewrite the candidate.\n</role>\n\n<criterion>\nid: ${criterion.id}\nname: ${criterion.name}\ninstructions: ${criterion.description}\n</criterion>\n\n<scale>\nUse an integer score from 1 to 20: 1 = certainly fails 10 = borderline … 20 = verified correct.\n</scale>\n\n<evidence_rules>\nTest important claims where practical. Evidence must cite observable support; file findings cite file:line where applicable. Report precise findings and use severity veto only for a finding that unconditionally prevents acceptance. ${GROUNDED_REPORTING}\n</evidence_rules>\n\n<output_format>\nCall structured_output with exactly criterion_id (set to ${criterion.id}), score, evidence (string array), and findings (objects with finding and severity veto, blocking, or note). ${READABLE_REPORT}\n</output_format>\n\n<objective>\nVerify criterion ${criterion.id} for the candidate produced for: ${task}\n</objective>`;
10
12
  }
11
13
 
12
- export function renderReducerPrompt(task: string, candidatePath: string, verifierPaths: readonly string[], repairsCompleted: number, maxRepairs: number): string {
13
- return `<artifacts>\nCandidate: ${candidatePath}\nVerifier reports: ${verifierPaths.join(", ")}\n</artifacts>\n\n<role>\nYou reduce independent verification reports into one deterministic next action.\n</role>\n\n<decision_rules>\nAccept only when all material rubric requirements have pass evidence. Request repair when findings are actionable and repair budget remains (${repairsCompleted}/${maxRepairs}). Reject when evidence proves the candidate cannot satisfy the task or the repair budget is exhausted. Preserve unresolved blockers verbatim.\n</decision_rules>\n\n<success_criteria>\nThe decision follows the evidence and repair bound, with every unresolved blocker preserved.\n</success_criteria>\n\n<stop_rules>\nStop after selecting exactly one decision: accept, reject, or repair.\n</stop_rules>\n\n<output_format>\nCall structured_output with decision (accept, reject, or repair), a concise evidence-based rationale, and remaining_work. ${READABLE_REPORT}\n${GROUNDED_REPORTING}\n</output_format>\n\n<objective>\nDecide whether the candidate for ${task} is accepted, rejected, or needs repair.\n</objective>`;
14
+ export function renderConsolidatorPrompt(
15
+ task: string,
16
+ candidatePath: string,
17
+ scorePaths: readonly string[],
18
+ repairsCompleted: number,
19
+ maxRepairs: number,
20
+ ): string {
21
+ return `<artifacts>\nCandidate: ${candidatePath}\nConfirmed criterion score reports: ${scorePaths.join(", ")}\n</artifacts>\n\n<role>\nYou consolidate confirmed verifier findings into actionable repair guidance. You do not decide whether verification is approved; the deterministic verification gate already made that decision.\n</role>\n\n<decision_rules>\nPreserve every confirmed veto or blocking finding in remaining_work verbatim. Explain concrete repairs and validation steps. Repair budget: ${repairsCompleted}/${maxRepairs}.\n</decision_rules>\n\n<output_format>\nCall structured_output with repair_guidance (a concise actionable repair plan) and remaining_work (the unresolved findings as strings). Never emit an approval or rejection decision. ${READABLE_REPORT}\n${GROUNDED_REPORTING}\n</output_format>\n\n<objective>\nConsolidate findings for the candidate produced for: ${task}\n</objective>`;
14
22
  }
15
23
 
16
24
  export function renderRepairPrompt(task: string, candidatePath: string, reviewPath: string): string {
17
- return `<artifacts>\nRead the current candidate at ${candidatePath} and reducer report at ${reviewPath}.\n</artifacts>\n\n<role>\nYou repair a candidate using independent blocking findings.\n</role>\n\n<success_criteria>\nEvery actionable blocker is addressed, relevant validation is rerun, and valid prior work is retained.\n</success_criteria>\n\n<evidence_rules>\nDo not dismiss a finding without contrary evidence. Report commands run, observed output, and file:line evidence where applicable. ${GROUNDED_REPORTING}\n</evidence_rules>\n\n<stop_rules>\nStop when all actionable blockers are repaired and validated, or list any blocker that remains with the missing evidence.\n</stop_rules>\n\n<output_format>\nA complete replacement candidate with repair summary, evidence, validation, and remaining risks. ${READABLE_REPORT}\n</output_format>\n\n<objective>\nRepair the candidate for: ${task}\n</objective>`;
25
+ return `<artifacts>\nRead the current candidate at ${candidatePath} and consolidated findings at ${reviewPath}.\n</artifacts>\n\n<role>\nYou repair a candidate using independent blocking findings.\n</role>\n\n<success_criteria>\nEvery actionable blocker is addressed, relevant validation is rerun, and valid prior work is retained.\n</success_criteria>\n\n<evidence_rules>\nDo not dismiss a finding without contrary evidence. Report commands run, observed output, and file:line evidence where applicable. ${GROUNDED_REPORTING}\n</evidence_rules>\n\n<stop_rules>\nStop when all actionable blockers are repaired and validated, or list any blocker that remains with the missing evidence.\n</stop_rules>\n\n<output_format>\nA complete replacement candidate with repair summary, evidence, validation, and remaining risks. ${READABLE_REPORT}\n</output_format>\n\n<objective>\nRepair the candidate for: ${task}\n</objective>`;
18
26
  }