@bastani/atomic 0.9.16-alpha.6 → 0.9.16-alpha.8

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 (214) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/builtin/intercom/CHANGELOG.md +6 -0
  3. package/dist/builtin/intercom/README.md +4 -4
  4. package/dist/builtin/intercom/broker/broker.ts +617 -22
  5. package/dist/builtin/intercom/broker/client-message-validation.ts +9 -0
  6. package/dist/builtin/intercom/broker/client.ts +200 -10
  7. package/dist/builtin/intercom/broker/pending-send-registry.ts +4 -0
  8. package/dist/builtin/intercom/broker/send-handler.ts +90 -36
  9. package/dist/builtin/intercom/broker/send-signature.ts +20 -8
  10. package/dist/builtin/intercom/group.ts +85 -0
  11. package/dist/builtin/intercom/index.bundle.mjs +680 -53
  12. package/dist/builtin/intercom/package.json +1 -1
  13. package/dist/builtin/intercom/session-target.ts +54 -0
  14. package/dist/builtin/intercom/skills/intercom/SKILL.md +18 -3
  15. package/dist/builtin/intercom/source-ownership.ts +31 -0
  16. package/dist/builtin/intercom/types.ts +115 -0
  17. package/dist/builtin/mcp/index.bundle.mjs +109 -35
  18. package/dist/builtin/mcp/package.json +1 -1
  19. package/dist/builtin/subagents/CHANGELOG.md +6 -0
  20. package/dist/builtin/subagents/package.json +1 -1
  21. package/dist/builtin/subagents/skills/qlty/SKILL.md +123 -0
  22. package/dist/builtin/subagents/skills/qlty/references/coding-with-ai-agents.md +64 -0
  23. package/dist/builtin/subagents/skills/qlty/references/commands.md +291 -0
  24. package/dist/builtin/subagents/skills/qlty/references/plugins-and-extensions.md +228 -0
  25. package/dist/builtin/subagents/skills/qlty/references/quickstart.md +110 -0
  26. package/dist/builtin/subagents/src/extension/index.bundle.mjs +173 -51
  27. package/dist/builtin/web-access/index.bundle.mjs +109 -35
  28. package/dist/builtin/web-access/package.json +1 -1
  29. package/dist/builtin/workflows/CHANGELOG.md +23 -0
  30. package/dist/builtin/workflows/README.md +40 -37
  31. package/dist/builtin/workflows/builtin/adversarial-verification.js +13 -0
  32. package/dist/builtin/workflows/builtin/{shared-prompts.ts → chunk-0x6e303p.js} +137 -133
  33. package/dist/builtin/workflows/builtin/chunk-29wrp38a.js +199 -0
  34. package/dist/builtin/workflows/builtin/chunk-2dqb5s2q.js +602 -0
  35. package/dist/builtin/workflows/builtin/chunk-4febxsv4.js +264 -0
  36. package/dist/builtin/workflows/builtin/chunk-5n10x7j2.js +69730 -0
  37. package/dist/builtin/workflows/builtin/chunk-5wgwscd0.js +4 -0
  38. package/dist/builtin/workflows/builtin/chunk-6fqs7c01.js +155 -0
  39. package/dist/builtin/workflows/builtin/chunk-7430zyas.js +126 -0
  40. package/dist/builtin/workflows/builtin/chunk-7at6dnkr.js +33 -0
  41. package/dist/builtin/workflows/builtin/chunk-82ha8p41.js +175 -0
  42. package/dist/builtin/workflows/builtin/chunk-bfkmzv9h.js +156 -0
  43. package/dist/builtin/workflows/builtin/chunk-c53y8bdh.js +345 -0
  44. package/dist/builtin/workflows/builtin/chunk-cdtd3m3w.js +529 -0
  45. package/dist/builtin/workflows/builtin/chunk-cg9tmks0.js +206 -0
  46. package/dist/builtin/workflows/builtin/chunk-hdpj1dkw.js +2447 -0
  47. package/dist/builtin/workflows/builtin/chunk-hzzn6adg.js +221 -0
  48. package/dist/builtin/workflows/builtin/chunk-nqr34qp3.js +1061 -0
  49. package/dist/builtin/workflows/builtin/chunk-qwzvgxnq.js +404 -0
  50. package/dist/builtin/workflows/builtin/chunk-qx2ptjs3.js +215 -0
  51. package/dist/builtin/workflows/builtin/chunk-v04c5qmd.js +1207 -0
  52. package/dist/builtin/workflows/builtin/chunk-wpckd35c.js +12 -0
  53. package/dist/builtin/workflows/builtin/chunk-x2ghn0je.js +4110 -0
  54. package/dist/builtin/workflows/builtin/chunk-zf28603f.js +1080 -0
  55. package/dist/builtin/workflows/builtin/classify-and-act.js +12 -0
  56. package/dist/builtin/workflows/builtin/fan-out-and-synthesize.js +10 -0
  57. package/dist/builtin/workflows/builtin/generate-and-filter.js +12 -0
  58. package/dist/builtin/workflows/builtin/goal.js +16 -0
  59. package/dist/builtin/workflows/builtin/index.js +49 -0
  60. package/dist/builtin/workflows/builtin/loop-until-done.js +12 -0
  61. package/dist/builtin/workflows/builtin/open-claude-design.js +11 -0
  62. package/dist/builtin/workflows/builtin/ralph.js +16 -0
  63. package/dist/builtin/workflows/builtin/steering-context.d.ts +6 -0
  64. package/dist/builtin/workflows/builtin/steering-context.js +7 -0
  65. package/dist/builtin/workflows/builtin/tournament.js +13 -0
  66. package/dist/builtin/workflows/package.json +10 -7
  67. package/dist/builtin/workflows/src/authoring/typebox-defaults.d.ts +41 -0
  68. package/dist/builtin/workflows/src/extension/index.bundle.mjs +1250 -428
  69. package/dist/builtin/workflows/src/{index.bundle.mjs → index.js} +956 -427
  70. package/dist/builtin/workflows/src/shared/authoring-contract-stage.d.ts +403 -0
  71. package/dist/builtin/workflows/src/shared/authoring-contract-ui.d.ts +327 -0
  72. package/dist/builtin/workflows/src/shared/budget.d.ts +67 -0
  73. package/dist/builtin/workflows/src/shared/workflow-authoring-types.d.ts +60 -0
  74. package/dist/builtin/workflows/src/shared/workflow-heartbeat-contract.d.ts +21 -0
  75. package/dist/core/agent-session-custom-message-commit.d.ts.map +1 -1
  76. package/dist/core/agent-session-custom-message-commit.js +12 -5
  77. package/dist/core/agent-session-custom-message-commit.js.map +1 -1
  78. package/dist/core/agent-session-persistent-custom-messages.d.ts.map +1 -1
  79. package/dist/core/agent-session-persistent-custom-messages.js +2 -0
  80. package/dist/core/agent-session-persistent-custom-messages.js.map +1 -1
  81. package/dist/core/agent-session.d.ts.map +1 -1
  82. package/dist/core/agent-session.js +2 -1
  83. package/dist/core/agent-session.js.map +1 -1
  84. package/dist/core/auth-storage.d.ts +2 -0
  85. package/dist/core/auth-storage.d.ts.map +1 -1
  86. package/dist/core/auth-storage.js +4 -0
  87. package/dist/core/auth-storage.js.map +1 -1
  88. package/dist/core/builtin-install-layout.d.ts +1 -1
  89. package/dist/core/builtin-install-layout.d.ts.map +1 -1
  90. package/dist/core/builtin-install-layout.js +1 -1
  91. package/dist/core/builtin-install-layout.js.map +1 -1
  92. package/dist/core/codex-fast-mode.d.ts +10 -7
  93. package/dist/core/codex-fast-mode.d.ts.map +1 -1
  94. package/dist/core/codex-fast-mode.js +31 -13
  95. package/dist/core/codex-fast-mode.js.map +1 -1
  96. package/dist/core/extensions/context-types.d.ts +35 -0
  97. package/dist/core/extensions/context-types.d.ts.map +1 -1
  98. package/dist/core/extensions/context-types.js.map +1 -1
  99. package/dist/core/extensions/index.d.ts +1 -1
  100. package/dist/core/extensions/index.d.ts.map +1 -1
  101. package/dist/core/extensions/index.js.map +1 -1
  102. package/dist/core/extensions/loader-virtual-modules.d.ts.map +1 -1
  103. package/dist/core/extensions/loader-virtual-modules.js +6 -1
  104. package/dist/core/extensions/loader-virtual-modules.js.map +1 -1
  105. package/dist/core/model-runtime.d.ts +2 -0
  106. package/dist/core/model-runtime.d.ts.map +1 -1
  107. package/dist/core/model-runtime.js +4 -0
  108. package/dist/core/model-runtime.js.map +1 -1
  109. package/dist/core/runtime-credentials.d.ts +1 -0
  110. package/dist/core/runtime-credentials.d.ts.map +1 -1
  111. package/dist/core/runtime-credentials.js +8 -0
  112. package/dist/core/runtime-credentials.js.map +1 -1
  113. package/dist/core/sdk.d.ts.map +1 -1
  114. package/dist/core/sdk.js +5 -5
  115. package/dist/core/sdk.js.map +1 -1
  116. package/dist/core/session-manager-storage.d.ts +2 -0
  117. package/dist/core/session-manager-storage.d.ts.map +1 -1
  118. package/dist/core/session-manager-storage.js +4 -2
  119. package/dist/core/session-manager-storage.js.map +1 -1
  120. package/dist/core/slash-commands.js +1 -1
  121. package/dist/core/slash-commands.js.map +1 -1
  122. package/dist/core/workflow-stage-admission.d.ts +6 -1
  123. package/dist/core/workflow-stage-admission.d.ts.map +1 -1
  124. package/dist/core/workflow-stage-admission.js +8 -9
  125. package/dist/core/workflow-stage-admission.js.map +1 -1
  126. package/dist/index.d.ts +0 -1
  127. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  128. package/dist/modes/interactive/components/footer.js +1 -1
  129. package/dist/modes/interactive/components/footer.js.map +1 -1
  130. package/dist/modes/interactive/interactive-autocomplete.js +1 -1
  131. package/dist/modes/interactive/interactive-autocomplete.js.map +1 -1
  132. package/dist/modes/interactive/interactive-startup.js +1 -1
  133. package/dist/modes/interactive/interactive-startup.js.map +1 -1
  134. package/dist/modes/interactive-engine/isolated-runtime.d.ts +4 -0
  135. package/dist/modes/interactive-engine/isolated-runtime.d.ts.map +1 -1
  136. package/dist/modes/interactive-engine/isolated-runtime.js +69 -10
  137. package/dist/modes/interactive-engine/isolated-runtime.js.map +1 -1
  138. package/dist/modes/rpc/rpc-client-api.d.ts +18 -2
  139. package/dist/modes/rpc/rpc-client-api.d.ts.map +1 -1
  140. package/dist/modes/rpc/rpc-client-api.js +33 -5
  141. package/dist/modes/rpc/rpc-client-api.js.map +1 -1
  142. package/dist/modes/rpc/rpc-command-handler.d.ts.map +1 -1
  143. package/dist/modes/rpc/rpc-command-handler.js +8 -4
  144. package/dist/modes/rpc/rpc-command-handler.js.map +1 -1
  145. package/dist/modes/rpc/rpc-types.d.ts +13 -0
  146. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  147. package/dist/modes/rpc/rpc-types.js.map +1 -1
  148. package/docs/intercom.md +15 -6
  149. package/docs/packages.md +2 -2
  150. package/docs/providers.md +8 -3
  151. package/docs/quickstart.md +3 -2
  152. package/docs/rpc.md +21 -4
  153. package/docs/settings.md +6 -4
  154. package/docs/skills.md +4 -0
  155. package/docs/subagents.md +1 -1
  156. package/docs/usage.md +1 -1
  157. package/docs/workflows.md +112 -239
  158. package/npm-shrinkwrap.json +32 -32
  159. package/package.json +9 -13
  160. package/dist/builtin/workflows/ambient.d.ts +0 -61
  161. package/dist/builtin/workflows/builtin/adversarial-verification-prompts.ts +0 -26
  162. package/dist/builtin/workflows/builtin/adversarial-verification-runner.ts +0 -391
  163. package/dist/builtin/workflows/builtin/adversarial-verification.ts +0 -34
  164. package/dist/builtin/workflows/builtin/classify-and-act-prompts.ts +0 -14
  165. package/dist/builtin/workflows/builtin/classify-and-act-runner.ts +0 -108
  166. package/dist/builtin/workflows/builtin/classify-and-act.ts +0 -40
  167. package/dist/builtin/workflows/builtin/fan-out-and-synthesize-prompts.ts +0 -14
  168. package/dist/builtin/workflows/builtin/fan-out-and-synthesize-runner.ts +0 -96
  169. package/dist/builtin/workflows/builtin/fan-out-and-synthesize.ts +0 -37
  170. package/dist/builtin/workflows/builtin/generate-and-filter-prompts.ts +0 -41
  171. package/dist/builtin/workflows/builtin/generate-and-filter-runner.ts +0 -90
  172. package/dist/builtin/workflows/builtin/generate-and-filter.ts +0 -31
  173. package/dist/builtin/workflows/builtin/goal-artifacts.ts +0 -64
  174. package/dist/builtin/workflows/builtin/goal-convergence.ts +0 -87
  175. package/dist/builtin/workflows/builtin/goal-ledger.ts +0 -139
  176. package/dist/builtin/workflows/builtin/goal-models.ts +0 -72
  177. package/dist/builtin/workflows/builtin/goal-orchestrator-prompts.ts +0 -94
  178. package/dist/builtin/workflows/builtin/goal-prompts.ts +0 -211
  179. package/dist/builtin/workflows/builtin/goal-reducer.ts +0 -176
  180. package/dist/builtin/workflows/builtin/goal-reports.ts +0 -76
  181. package/dist/builtin/workflows/builtin/goal-reverify.ts +0 -305
  182. package/dist/builtin/workflows/builtin/goal-review.ts +0 -139
  183. package/dist/builtin/workflows/builtin/goal-runner.ts +0 -504
  184. package/dist/builtin/workflows/builtin/goal-schemas.ts +0 -89
  185. package/dist/builtin/workflows/builtin/goal-types.ts +0 -164
  186. package/dist/builtin/workflows/builtin/goal.ts +0 -78
  187. package/dist/builtin/workflows/builtin/index.ts +0 -16
  188. package/dist/builtin/workflows/builtin/loop-until-done-prompts.ts +0 -87
  189. package/dist/builtin/workflows/builtin/loop-until-done-runner.ts +0 -282
  190. package/dist/builtin/workflows/builtin/loop-until-done.ts +0 -51
  191. package/dist/builtin/workflows/builtin/open-claude-design-live-protocol.ts +0 -305
  192. package/dist/builtin/workflows/builtin/open-claude-design-phases.ts +0 -337
  193. package/dist/builtin/workflows/builtin/open-claude-design-runner.ts +0 -347
  194. package/dist/builtin/workflows/builtin/open-claude-design-setup.ts +0 -313
  195. package/dist/builtin/workflows/builtin/open-claude-design-utils.ts +0 -311
  196. package/dist/builtin/workflows/builtin/open-claude-design.ts +0 -57
  197. package/dist/builtin/workflows/builtin/pattern-artifact-root.ts +0 -28
  198. package/dist/builtin/workflows/builtin/progress-scoring.ts +0 -230
  199. package/dist/builtin/workflows/builtin/ralph-core.ts +0 -449
  200. package/dist/builtin/workflows/builtin/ralph-forked-prompts.ts +0 -100
  201. package/dist/builtin/workflows/builtin/ralph-models.ts +0 -183
  202. package/dist/builtin/workflows/builtin/ralph-review-gate.ts +0 -103
  203. package/dist/builtin/workflows/builtin/ralph-reviewer-prompt.ts +0 -100
  204. package/dist/builtin/workflows/builtin/ralph-runner.ts +0 -455
  205. package/dist/builtin/workflows/builtin/ralph.ts +0 -82
  206. package/dist/builtin/workflows/builtin/review-convergence.ts +0 -229
  207. package/dist/builtin/workflows/builtin/selection-math.ts +0 -156
  208. package/dist/builtin/workflows/builtin/steering-context.ts +0 -51
  209. package/dist/builtin/workflows/builtin/tournament-prompts.ts +0 -70
  210. package/dist/builtin/workflows/builtin/tournament-runner.ts +0 -400
  211. package/dist/builtin/workflows/builtin/tournament.ts +0 -73
  212. package/dist/builtin/workflows/builtin/verification-criteria.ts +0 -330
  213. package/dist/builtin/workflows/builtin/verification-prompts.ts +0 -206
  214. package/dist/builtin/workflows/builtin/verification-usage.ts +0 -44
@@ -1,330 +0,0 @@
1
- /**
2
- * Shared verification rubric primitive.
3
- *
4
- * Doors: `parse_rubric`, `normalize_criteria`, `select_criteria`,
5
- * `decide_verification`. A CriterionScore can only exist as a schema-valid
6
- * structured report — an unparseable verifier output is unrepresentable as a
7
- * vote, so it cannot shift the mean.
8
- */
9
- import { Type, type TSchema } from "typebox";
10
-
11
- export interface Criterion {
12
- readonly id: string;
13
- readonly name: string;
14
- readonly description: string;
15
- }
16
-
17
- export interface Criteria {
18
- readonly groundTruthNote: string;
19
- readonly criteria: readonly Criterion[];
20
- }
21
-
22
- export interface CriterionInput {
23
- readonly id?: string;
24
- readonly name?: string;
25
- readonly description?: string;
26
- }
27
-
28
- export interface Finding {
29
- readonly finding: string;
30
- readonly severity: "veto" | "blocking" | "note";
31
- }
32
-
33
- export interface CriterionScore {
34
- readonly criterionId: string;
35
- readonly score: number;
36
- readonly evidence: readonly string[];
37
- readonly findings: readonly Finding[];
38
- }
39
-
40
- export type RubricErrorCode = "NoCriteria" | "EmptyCriterion";
41
-
42
- export class RubricError extends Error {
43
- readonly code: RubricErrorCode;
44
-
45
- constructor(message: string, code: RubricErrorCode) {
46
- super(message);
47
- this.name = code;
48
- this.code = code;
49
- }
50
- }
51
-
52
- export class NoCriteria extends RubricError {
53
- constructor(message = "no criteria found — check the `## Criteria` section and its `### Name {#id}` headings") {
54
- super(message, "NoCriteria");
55
- this.name = "NoCriteria";
56
- }
57
- }
58
-
59
- export class EmptyCriterion extends RubricError {
60
- readonly ids: readonly string[];
61
-
62
- constructor(ids: readonly string[]) {
63
- super(`criteria have empty instructions: ${ids.join(", ")}`, "EmptyCriterion");
64
- this.name = "EmptyCriterion";
65
- this.ids = ids;
66
- }
67
- }
68
-
69
- const CRITERION_ID_ANCHOR = /^(.*?)\s*\{#([A-Za-z0-9_-]+)\}\s*$/;
70
- const SLUG_MAX_LENGTH = 40;
71
-
72
- function slug_id(text: string): string {
73
- const slug = text
74
- .toLowerCase()
75
- .replace(/[^a-z0-9]+/g, "_")
76
- .replace(/^_+|_+$/g, "");
77
- return slug.slice(0, SLUG_MAX_LENGTH).replace(/_+$/g, "") || "criterion";
78
- }
79
-
80
- function dedup_id(cid: string, seen: Set<string>): string {
81
- let out = cid;
82
- let n = 1;
83
- while (seen.has(out)) {
84
- n += 1;
85
- out = `${cid}_${n}`;
86
- }
87
- seen.add(out);
88
- return out;
89
- }
90
-
91
- function criterion(id: string, name: string, description: string): Criterion {
92
- return { id, name, description };
93
- }
94
-
95
- function strip_html_comments(markdown: string): string {
96
- let sanitized = markdown;
97
- while (true) {
98
- const opener = sanitized.indexOf("<!--");
99
- if (opener === -1) return sanitized;
100
- const closer = sanitized.indexOf("-->", opener + 4);
101
- if (closer === -1) return sanitized.slice(0, opener);
102
- sanitized = sanitized.slice(0, opener) + sanitized.slice(closer + 3);
103
- }
104
- }
105
-
106
- /**
107
- * Parse a `criteria.md` document into normalized criteria.
108
- *
109
- * `# title` is ignored. `## Ground Truth Note` is optional; the first section
110
- * wins. `## Criteria` owns `### Name {#id}` headings. HTML comments are stripped
111
- * before parse. Ids slug to lowercase alnum/underscore, ≤40 chars, fallback
112
- * `"criterion"`, with encounter-order `_2` / `_3` dedup.
113
- *
114
- * @throws {NoCriteria} when the Criteria section yields no headings
115
- * @throws {EmptyCriterion} when any criterion has an empty body
116
- */
117
- export function parse_rubric(markdown: string): Criteria {
118
- const lines = strip_html_comments(markdown).split(/\r?\n/);
119
-
120
- let groundTruthNote = "";
121
- let seenGroundTruth = false;
122
- const criteria: Criterion[] = [];
123
- const seen = new Set<string>();
124
-
125
- let section: "ground_truth" | "criteria" | null = null;
126
- let currentName: string | null = null;
127
- let currentId: string | null = null;
128
- let buf: string[] = [];
129
-
130
- const flush = (): void => {
131
- const text = buf.join("\n").trim();
132
- buf = [];
133
- if (section === "ground_truth") {
134
- if (!seenGroundTruth) {
135
- groundTruthNote = text;
136
- seenGroundTruth = true;
137
- }
138
- return;
139
- }
140
- if (currentName === null || currentId === null) return;
141
- criteria.push(criterion(currentId, currentName, text));
142
- currentName = null;
143
- currentId = null;
144
- };
145
-
146
- for (const line of lines) {
147
- if (line.startsWith("## ") && !line.startsWith("### ")) {
148
- flush();
149
- const heading = line.slice(3).trim().toLowerCase();
150
- if (heading.includes("ground truth")) {
151
- section = "ground_truth";
152
- } else if (heading.includes("criteri")) {
153
- section = "criteria";
154
- } else {
155
- section = null;
156
- }
157
- continue;
158
- }
159
- if (line.startsWith("### ") && section === "criteria") {
160
- flush();
161
- const heading = line.slice(4).trim();
162
- const anchored = CRITERION_ID_ANCHOR.exec(heading);
163
- const name = anchored ? anchored[1]!.trim() : heading;
164
- const rawId = anchored ? anchored[2]!.trim() : slug_id(heading);
165
- currentName = name;
166
- currentId = dedup_id(rawId, seen);
167
- continue;
168
- }
169
- if (line.startsWith("# ")) {
170
- continue;
171
- }
172
- buf.push(line);
173
- }
174
- flush();
175
-
176
- if (criteria.length === 0) {
177
- throw new NoCriteria();
178
- }
179
- const empty = criteria.filter((item) => item.description.length === 0).map((item) => item.id);
180
- if (empty.length > 0) {
181
- throw new EmptyCriterion(empty);
182
- }
183
- return { groundTruthNote, criteria };
184
- }
185
-
186
- function isRecord(value: object): value is Record<string, string> {
187
- return Object.values(value).every((entry) => typeof entry === "string");
188
- }
189
-
190
- function isOptionalString(value: object, key: "id" | "name" | "description"): boolean {
191
- if (!(key in value)) return true;
192
- return typeof (value as CriterionInput)[key] === "string";
193
- }
194
-
195
- function isCriterionInput(value: object): value is CriterionInput {
196
- return isOptionalString(value, "id") && isOptionalString(value, "name") && isOptionalString(value, "description");
197
- }
198
-
199
- function normalize_one(raw: string | CriterionInput, index: number, seen: Set<string>): Criterion {
200
- const cidRaw = typeof raw === "string" ? "" : String(raw.id ?? "");
201
- const nameRaw = typeof raw === "string" ? raw : String(raw.name ?? "");
202
- const desc = typeof raw === "string" ? raw : String(raw.description ?? "");
203
- if (desc.length === 0) {
204
- throw new EmptyCriterion([cidRaw || nameRaw || String(index)]);
205
- }
206
- const name = nameRaw || cidRaw || slug_id(desc);
207
- const id = dedup_id(cidRaw || slug_id(name), seen);
208
- return criterion(id, name, desc);
209
- }
210
-
211
- /**
212
- * Canonicalize any accepted criteria shape into `{id,name,description}[]`.
213
- *
214
- * Same slug/dedup rules as `parse_rubric`. Empty descriptions throw
215
- * `EmptyCriterion`; an empty collection throws `NoCriteria`.
216
- */
217
- export function normalize_criteria(
218
- input: Record<string, string> | readonly string[] | readonly CriterionInput[],
219
- ): readonly Criterion[] {
220
- if (input === null || typeof input !== "object") {
221
- throw new TypeError(`criteria must be a record, string[], or CriterionInput[], got ${typeof input}`);
222
- }
223
-
224
- const rawItems: Array<string | CriterionInput> = [];
225
- if (Array.isArray(input)) {
226
- for (const [index, raw] of input.entries()) {
227
- if (typeof raw === "string") {
228
- rawItems.push(raw);
229
- continue;
230
- }
231
- if (isCriterionInput(raw)) {
232
- rawItems.push(raw);
233
- continue;
234
- }
235
- throw new TypeError(`criteria[${index}] must be a string or CriterionInput`);
236
- }
237
- } else if (isRecord(input)) {
238
- for (const [name, description] of Object.entries(input)) {
239
- rawItems.push({ name, description });
240
- }
241
- } else {
242
- throw new TypeError("criteria record values must be strings");
243
- }
244
-
245
- const seen = new Set<string>();
246
- const out = rawItems.map((raw, index) => normalize_one(raw, index, seen));
247
- if (out.length === 0) {
248
- throw new NoCriteria("criteria is empty");
249
- }
250
- return out;
251
- }
252
-
253
- /**
254
- * Return the subset of `criteria` in `ids` order.
255
- *
256
- * Omitted `ids` returns every criterion in the original order. An unknown id
257
- * throws — nothing is silently dropped.
258
- */
259
- export function select_criteria(criteria: readonly Criterion[], ids?: readonly string[]): readonly Criterion[] {
260
- if (ids === undefined) return criteria;
261
- const byId = new Map(criteria.map((item) => [item.id, item]));
262
- const missing = ids.filter((id) => !byId.has(id));
263
- if (missing.length > 0) {
264
- throw new Error(`criteria not found: ${missing.join(", ")}`);
265
- }
266
- return ids.map((id) => byId.get(id)!);
267
- }
268
-
269
- export const VERIFICATION_SCALE: {
270
- readonly min: 1;
271
- readonly max: 20;
272
- readonly anchors: string;
273
- readonly schema: TSchema;
274
- } = {
275
- min: 1,
276
- max: 20,
277
- anchors: "1 = certainly fails … 10 = borderline … 20 = verified correct",
278
- schema: Type.Integer({ minimum: 1, maximum: 20 }),
279
- };
280
-
281
- export type Accept = { readonly kind: "accept"; readonly mean: number };
282
- export type Repair = { readonly kind: "repair"; readonly mean: number; readonly findings: readonly Finding[] };
283
- export type Indeterminate = { readonly kind: "indeterminate"; readonly missing: number };
284
- export type VerificationDecision = Accept | Repair | Indeterminate;
285
-
286
- export type VerificationRound = {
287
- readonly scores: readonly CriterionScore[];
288
- readonly invalidCount: number;
289
- readonly expectedCount: number;
290
- };
291
-
292
- export type VerificationPolicy = {
293
- readonly acceptMean: number;
294
- readonly quorumFraction: number;
295
- };
296
-
297
- function mean_score(scores: readonly CriterionScore[]): number {
298
- if (scores.length === 0) return 0;
299
- let total = 0;
300
- for (const item of scores) total += item.score;
301
- return total / scores.length;
302
- }
303
-
304
- /**
305
- * Map one round of schema-valid scores to accept / repair / indeterminate.
306
- *
307
- * Accept iff quorum holds AND mean ≥ acceptMean AND no `severity: "veto"`
308
- * finding exists. Any veto forces Repair regardless of mean. Below quorum
309
- * (`valid < ceil(expected × quorumFraction)`) is Indeterminate.
310
- *
311
- * `invalidCount` is metadata only: there is no constructor that turns an
312
- * unparseable report into a CriterionScore, so a parse failure cannot shift
313
- * the mean.
314
- */
315
- export function decide_verification(round: VerificationRound, policy: VerificationPolicy): VerificationDecision {
316
- // `invalidCount` is intentionally unread. It cannot enter the mean.
317
- const { scores, expectedCount } = round;
318
- const valid = scores.length;
319
- const quorumNeeded = Math.ceil(expectedCount * policy.quorumFraction);
320
- if (valid < quorumNeeded) {
321
- return { kind: "indeterminate", missing: Math.max(0, expectedCount - valid) };
322
- }
323
-
324
- const mean = mean_score(scores);
325
- const findings = scores.flatMap((item) => item.findings);
326
- if (findings.some((item) => item.severity === "veto") || mean < policy.acceptMean) {
327
- return { kind: "repair", mean, findings };
328
- }
329
- return { kind: "accept", mean };
330
- }
@@ -1,206 +0,0 @@
1
- /**
2
- * Prefix-cache-aware verification prompts and warm-first fan-out.
3
- *
4
- * A scoring-family prompt is always `SHARED HEAD ‖ VARYING TAIL`: the head
5
- * contains the task statement, ground-truth note, candidate bodies (or read
6
- * references when the family is too large), and scale anchors, in that order.
7
- * The tail contains only the criterion name and description plus the output
8
- * format instruction. Keeping this ordering invariant means sibling criteria
9
- * can share the provider's cached prefix byte-for-byte.
10
- *
11
- * A pathless candidate is valid while the whole family is inline. If any body
12
- * exceeds the inline bound, every candidate must provide its caller-bound path;
13
- * this layer refuses the family rather than inventing a read path.
14
- */
15
- import type {
16
- WorkflowParallelOptions,
17
- WorkflowRunContext,
18
- WorkflowTaskResult,
19
- WorkflowTaskStep,
20
- } from "../src/shared/types.js";
21
- import type { Criterion } from "./verification-criteria.js";
22
- import { VERIFICATION_SCALE } from "./verification-criteria.js";
23
-
24
- /** Maximum UTF-8 byte length of one candidate body eligible for inlining. */
25
- export const MAX_INLINE_CANDIDATE_BYTES = 32 * 1024;
26
-
27
- export interface ScoringCandidate {
28
- /**
29
- * Optional only while every candidate body is inline. An oversized family
30
- * requires a caller-bound path for every candidate; paths are preserved
31
- * verbatim and are never synthesized.
32
- */
33
- readonly path?: string;
34
- readonly body: string;
35
- }
36
-
37
- export interface SharedHead {
38
- readonly task: string;
39
- readonly groundTruthNote: string;
40
- readonly candidates: readonly ScoringCandidate[];
41
- readonly scaleAnchors?: string;
42
- readonly outputFormat?: string;
43
- }
44
-
45
- export interface WarmFirstFanOutOptions extends WorkflowParallelOptions {
46
- /** Maximum number of distinct prefixes warmed at once. */
47
- readonly warmConcurrency?: number;
48
- }
49
-
50
- export type WarmFirstFanOutContext = Pick<WorkflowRunContext, "parallel">;
51
-
52
- const DEFAULT_WARM_CONCURRENCY = 4;
53
- const DEFAULT_OUTPUT_FORMAT =
54
- "Call structured_output with criterion_id, score (1–20), evidence, and findings containing finding and severity.";
55
-
56
- function inlineCandidates(head: SharedHead): boolean {
57
- return head.candidates.every(
58
- (candidate) => Buffer.byteLength(candidate.body, "utf8") <= MAX_INLINE_CANDIDATE_BYTES,
59
- );
60
- }
61
-
62
- function fallbackReadPaths(head: SharedHead): readonly string[] | undefined {
63
- if (inlineCandidates(head)) return undefined;
64
- const paths: string[] = [];
65
- for (const [index, candidate] of head.candidates.entries()) {
66
- if (candidate.path === undefined) {
67
- throw new TypeError(
68
- `Oversized candidate family requires a caller-bound path for every candidate; candidate ${index + 1} has no path.`,
69
- );
70
- }
71
- paths.push(candidate.path);
72
- }
73
- return paths;
74
- }
75
-
76
- function candidateSection(head: SharedHead, readPaths: readonly string[] | undefined): string {
77
- return head.candidates
78
- .map((candidate, index) => {
79
- if (readPaths === undefined) {
80
- return `<candidate index="${index + 1}">\n${candidate.body}\n</candidate>`;
81
- }
82
- return `<candidate index="${index + 1}" source="read">\nRead candidate from ${readPaths[index]!}.\n</candidate>`;
83
- })
84
- .join("\n");
85
- }
86
-
87
- /**
88
- * Return candidate paths required by the read fallback.
89
- *
90
- * The whole family switches together: either every body is in the shared head,
91
- * or every candidate is named as a caller-provided read. Order and duplicate
92
- * paths are kept; an oversized pathless family throws instead of guessing.
93
- */
94
- export function scoring_prompt_reads(head: SharedHead): readonly string[] {
95
- return fallbackReadPaths(head) ?? [];
96
- }
97
-
98
- /**
99
- * Build one scoring prompt with a byte-identical shared head and criterion tail.
100
- * Candidate bodies are measured in UTF-8 bytes, not JavaScript code units. If
101
- * any body exceeds `MAX_INLINE_CANDIDATE_BYTES`, no body is inlined for the
102
- * family, preserving the same head for every sibling criterion.
103
- */
104
- export function build_scoring_prompt(head: SharedHead, criterion: Criterion): string {
105
- const readPaths = fallbackReadPaths(head);
106
- const sharedHead = [
107
- "<scoring_head>",
108
- "<task_statement>",
109
- head.task,
110
- "</task_statement>",
111
- "<ground_truth_note>",
112
- head.groundTruthNote,
113
- "</ground_truth_note>",
114
- "<candidates>",
115
- candidateSection(head, readPaths),
116
- "</candidates>",
117
- "<scale_anchors>",
118
- head.scaleAnchors ?? VERIFICATION_SCALE.anchors,
119
- "</scale_anchors>",
120
- ].join("\n");
121
- const varyingTail = [
122
- "<criterion>",
123
- `<name>${criterion.name}</name>`,
124
- `<description>${criterion.description}</description>`,
125
- "</criterion>",
126
- "<output_format>",
127
- head.outputFormat ?? DEFAULT_OUTPUT_FORMAT,
128
- "</output_format>",
129
- ].join("\n");
130
- return `${sharedHead}\n\n${varyingTail}`;
131
- }
132
-
133
- function boundedConcurrency(
134
- stepCount: number,
135
- phaseCap: number | undefined,
136
- inheritedCap: number | undefined,
137
- ): number {
138
- return Math.min(stepCount, phaseCap ?? stepCount, inheritedCap ?? stepCount);
139
- }
140
-
141
- /**
142
- * Run one warm step per prefix, then release the remaining steps. Prefix keys
143
- * are caller-owned; first-seen order is deterministic. Warm failures are
144
- * observed without fail-fast, and the second phase is always attempted before
145
- * the warm error is rethrown. Successful results are returned in input order.
146
- */
147
- export async function warm_first_fan_out<K>(
148
- ctx: WarmFirstFanOutContext,
149
- steps: readonly WorkflowTaskStep[],
150
- prefixKeyOf: (step: WorkflowTaskStep, index: number) => K,
151
- options: WarmFirstFanOutOptions = {},
152
- ): Promise<WorkflowTaskResult[]> {
153
- const warmIndices: number[] = [];
154
- const restIndices: number[] = [];
155
- const seen = new Set<K>();
156
- for (const [index, step] of steps.entries()) {
157
- const prefixKey = prefixKeyOf(step, index);
158
- if (seen.has(prefixKey)) restIndices.push(index);
159
- else {
160
- seen.add(prefixKey);
161
- warmIndices.push(index);
162
- }
163
- }
164
-
165
- const { warmConcurrency, ...parallelOptions } = options;
166
- const inheritedConcurrency = parallelOptions.concurrency;
167
- const resultsByIndex = new Map<number, WorkflowTaskResult>();
168
- let warmFailure: Error | undefined;
169
- if (warmIndices.length > 0) {
170
- const warmSteps = warmIndices.map((index) => steps[index]!);
171
- try {
172
- const warmResults = await ctx.parallel(warmSteps, {
173
- ...parallelOptions,
174
- concurrency: boundedConcurrency(
175
- warmSteps.length,
176
- warmConcurrency ?? DEFAULT_WARM_CONCURRENCY,
177
- inheritedConcurrency,
178
- ),
179
- failFast: false,
180
- });
181
- for (const [resultIndex, result] of warmResults.entries()) {
182
- const originalIndex = warmIndices[resultIndex];
183
- if (originalIndex !== undefined) resultsByIndex.set(originalIndex, result);
184
- }
185
- } catch (error) {
186
- warmFailure = error instanceof Error ? error : new Error(String(error));
187
- }
188
- }
189
-
190
- if (restIndices.length > 0) {
191
- const restSteps = restIndices.map((index) => steps[index]!);
192
- const restResults = await ctx.parallel(restSteps, {
193
- ...parallelOptions,
194
- concurrency: boundedConcurrency(restSteps.length, undefined, inheritedConcurrency),
195
- });
196
- for (const [resultIndex, result] of restResults.entries()) {
197
- const originalIndex = restIndices[resultIndex];
198
- if (originalIndex !== undefined) resultsByIndex.set(originalIndex, result);
199
- }
200
- }
201
-
202
- if (warmFailure !== undefined) throw warmFailure;
203
- return steps
204
- .map((_, index) => resultsByIndex.get(index))
205
- .filter((result): result is WorkflowTaskResult => result !== undefined);
206
- }
@@ -1,44 +0,0 @@
1
- import type { WorkflowTaskResult } from "../src/shared/types.js";
2
-
3
- export type UsageTotals = {
4
- calls: number;
5
- input: number;
6
- output: number;
7
- cacheRead: number;
8
- cacheWrite: number;
9
- cost: number;
10
- turns: number;
11
- cacheHitRate: number;
12
- };
13
-
14
- export function fold_usage(results: readonly WorkflowTaskResult[]): UsageTotals {
15
- const totals = {
16
- calls: 0,
17
- input: 0,
18
- output: 0,
19
- cacheRead: 0,
20
- cacheWrite: 0,
21
- cost: 0,
22
- turns: 0,
23
- };
24
-
25
- for (const result of results) {
26
- for (const attempt of result.modelAttempts ?? []) {
27
- totals.calls += 1;
28
- const usage = attempt.usage;
29
- if (usage === undefined) continue;
30
- totals.input += usage.input ?? 0;
31
- totals.output += usage.output ?? 0;
32
- totals.cacheRead += usage.cacheRead ?? 0;
33
- totals.cacheWrite += usage.cacheWrite ?? 0;
34
- totals.cost += usage.cost ?? 0;
35
- totals.turns += usage.turns ?? 0;
36
- }
37
- }
38
-
39
- const cacheDenominator = totals.input + totals.cacheRead;
40
- return {
41
- ...totals,
42
- cacheHitRate: cacheDenominator === 0 ? 0 : totals.cacheRead / cacheDenominator,
43
- };
44
- }