cohorte 2.10.1 → 3.0.0-dev.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (211) hide show
  1. package/README.md +7 -0
  2. package/assets/manifest.json +193 -0
  3. package/assets/migrations/config/README.md +12 -0
  4. package/assets/migrations/state/0001_init.sql +154 -0
  5. package/assets/prompts/README.md +11 -0
  6. package/assets/prompts/agents/fixer.md +7 -0
  7. package/assets/prompts/agents/implementer.md +12 -0
  8. package/assets/prompts/agents/reviewer.md +8 -0
  9. package/assets/prompts/agents/security-reviewer.md +7 -0
  10. package/assets/prompts/phases/build.md +5 -0
  11. package/assets/prompts/phases/review.md +5 -0
  12. package/assets/prompts/phases/test.md +5 -0
  13. package/assets/prompts/system.md +11 -0
  14. package/assets/schemas/agent-output.schema.json +225 -0
  15. package/assets/schemas/auth-status.schema.json +87 -0
  16. package/assets/schemas/command-result.schema.json +192 -0
  17. package/assets/schemas/commands.schema.json +952 -0
  18. package/assets/schemas/config.schema.json +2259 -0
  19. package/assets/schemas/doctor-report.schema.json +104 -0
  20. package/assets/schemas/events.schema.json +8514 -0
  21. package/assets/schemas/fake-script.schema.json +549 -0
  22. package/assets/schemas/inspect.schema.json +3133 -0
  23. package/assets/schemas/manifest.schema.json +79 -0
  24. package/assets/schemas/ownership.schema.json +52 -0
  25. package/assets/schemas/policy-verdict.schema.json +282 -0
  26. package/assets/schemas/project-model.schema.json +783 -0
  27. package/assets/schemas/project-status.schema.json +217 -0
  28. package/assets/schemas/reconcile-plan.schema.json +155 -0
  29. package/assets/schemas/run-diff.schema.json +177 -0
  30. package/assets/schemas/run-snapshot-manifest.schema.json +2104 -0
  31. package/assets/schemas/run-state.schema.json +1755 -0
  32. package/assets/schemas/runtime-capabilities.schema.json +1545 -0
  33. package/assets/schemas/sandbox-capabilities.schema.json +116 -0
  34. package/assets/schemas/skill.schema.json +87 -0
  35. package/assets/schemas/spec.schema.json +202 -0
  36. package/assets/schemas/tool-catalogue.schema.json +655 -0
  37. package/assets/schemas/trust-record.schema.json +38 -0
  38. package/assets/skills/README.md +9 -0
  39. package/assets/skills/inspect-surface.md +4 -0
  40. package/dist/agent-host.mjs +50 -0
  41. package/dist/agent-host.mjs.map +1 -0
  42. package/dist/bundle-manifest.json +102 -0
  43. package/dist/chunks/__host-DgGPyco9.mjs +18 -0
  44. package/dist/chunks/__host-DgGPyco9.mjs.map +1 -0
  45. package/dist/chunks/approve-Cv135UqI.mjs +19 -0
  46. package/dist/chunks/approve-Cv135UqI.mjs.map +1 -0
  47. package/dist/chunks/auth-BT5oVp76.mjs +132 -0
  48. package/dist/chunks/auth-BT5oVp76.mjs.map +1 -0
  49. package/dist/chunks/auth-D8rCVY33.mjs +84 -0
  50. package/dist/chunks/auth-D8rCVY33.mjs.map +1 -0
  51. package/dist/chunks/brainstorm-CU10T1tm.mjs +7 -0
  52. package/dist/chunks/brainstorm-CU10T1tm.mjs.map +1 -0
  53. package/dist/chunks/cancel-KV_cpsqS.mjs +15 -0
  54. package/dist/chunks/cancel-KV_cpsqS.mjs.map +1 -0
  55. package/dist/chunks/canonical-C8UxeEmB.mjs +57 -0
  56. package/dist/chunks/canonical-C8UxeEmB.mjs.map +1 -0
  57. package/dist/chunks/catalogue-BvwSrM_j.mjs +1549 -0
  58. package/dist/chunks/catalogue-BvwSrM_j.mjs.map +1 -0
  59. package/dist/chunks/compose-Cr0hP0qm.mjs +7303 -0
  60. package/dist/chunks/compose-Cr0hP0qm.mjs.map +1 -0
  61. package/dist/chunks/config-DefHA2Lw.mjs +96 -0
  62. package/dist/chunks/config-DefHA2Lw.mjs.map +1 -0
  63. package/dist/chunks/contract-B0ra2-bm.mjs +295 -0
  64. package/dist/chunks/contract-B0ra2-bm.mjs.map +1 -0
  65. package/dist/chunks/contract-vYbwYr0Y.mjs +123 -0
  66. package/dist/chunks/contract-vYbwYr0Y.mjs.map +1 -0
  67. package/dist/chunks/deny-ClTxSvHJ.mjs +15 -0
  68. package/dist/chunks/deny-ClTxSvHJ.mjs.map +1 -0
  69. package/dist/chunks/diff-Qw0II0sj.mjs +101 -0
  70. package/dist/chunks/diff-Qw0II0sj.mjs.map +1 -0
  71. package/dist/chunks/discover-30Ciya_0.mjs +20 -0
  72. package/dist/chunks/discover-30Ciya_0.mjs.map +1 -0
  73. package/dist/chunks/doctor-DfCrKpfT.mjs +222 -0
  74. package/dist/chunks/doctor-DfCrKpfT.mjs.map +1 -0
  75. package/dist/chunks/fix-D4Tj8RVA.mjs +18 -0
  76. package/dist/chunks/fix-D4Tj8RVA.mjs.map +1 -0
  77. package/dist/chunks/gc-BBXlBF5w.mjs +161 -0
  78. package/dist/chunks/gc-BBXlBF5w.mjs.map +1 -0
  79. package/dist/chunks/init-Okz4mlhW.mjs +30 -0
  80. package/dist/chunks/init-Okz4mlhW.mjs.map +1 -0
  81. package/dist/chunks/inspect-Buby4cQD.mjs +22 -0
  82. package/dist/chunks/inspect-Buby4cQD.mjs.map +1 -0
  83. package/dist/chunks/logs-Giq5sUau.mjs +25 -0
  84. package/dist/chunks/logs-Giq5sUau.mjs.map +1 -0
  85. package/dist/chunks/migrate-DRoFUg53.mjs +19 -0
  86. package/dist/chunks/migrate-DRoFUg53.mjs.map +1 -0
  87. package/dist/chunks/models-Btp6X2YZ.mjs +24 -0
  88. package/dist/chunks/models-Btp6X2YZ.mjs.map +1 -0
  89. package/dist/chunks/pause-BASBfKLO.mjs +16 -0
  90. package/dist/chunks/pause-BASBfKLO.mjs.map +1 -0
  91. package/dist/chunks/policy-DUIgzF3W.mjs +139 -0
  92. package/dist/chunks/policy-DUIgzF3W.mjs.map +1 -0
  93. package/dist/chunks/providers-Dx78imKj.mjs +19 -0
  94. package/dist/chunks/providers-Dx78imKj.mjs.map +1 -0
  95. package/dist/chunks/reconcile-BfD785gd.mjs +26 -0
  96. package/dist/chunks/reconcile-BfD785gd.mjs.map +1 -0
  97. package/dist/chunks/render-DyZr5Lgi.mjs +53 -0
  98. package/dist/chunks/render-DyZr5Lgi.mjs.map +1 -0
  99. package/dist/chunks/resume-GJQRb2Eb.mjs +15 -0
  100. package/dist/chunks/resume-GJQRb2Eb.mjs.map +1 -0
  101. package/dist/chunks/retry-CLrUem1H.mjs +15 -0
  102. package/dist/chunks/retry-CLrUem1H.mjs.map +1 -0
  103. package/dist/chunks/review-BDdjb6kB.mjs +17 -0
  104. package/dist/chunks/review-BDdjb6kB.mjs.map +1 -0
  105. package/dist/chunks/run-CgTWYlcd.mjs +82 -0
  106. package/dist/chunks/run-CgTWYlcd.mjs.map +1 -0
  107. package/dist/chunks/run-tool-C3rAv4XF.mjs +26 -0
  108. package/dist/chunks/run-tool-C3rAv4XF.mjs.map +1 -0
  109. package/dist/chunks/schema-C9QbUW4H.mjs +578 -0
  110. package/dist/chunks/schema-C9QbUW4H.mjs.map +1 -0
  111. package/dist/chunks/send-BdnJ85Nz.mjs +19 -0
  112. package/dist/chunks/send-BdnJ85Nz.mjs.map +1 -0
  113. package/dist/chunks/ship-DTuLGoaJ.mjs +19 -0
  114. package/dist/chunks/ship-DTuLGoaJ.mjs.map +1 -0
  115. package/dist/chunks/shutdown-BM92Iyv2.mjs +15 -0
  116. package/dist/chunks/shutdown-BM92Iyv2.mjs.map +1 -0
  117. package/dist/chunks/skip-DZG5kpcH.mjs +18 -0
  118. package/dist/chunks/skip-DZG5kpcH.mjs.map +1 -0
  119. package/dist/chunks/spec-Bq0P3jVh.mjs +20 -0
  120. package/dist/chunks/spec-Bq0P3jVh.mjs.map +1 -0
  121. package/dist/chunks/src-CId6kMsY.mjs +1689 -0
  122. package/dist/chunks/src-CId6kMsY.mjs.map +1 -0
  123. package/dist/chunks/src-CfAOde7C.mjs +5403 -0
  124. package/dist/chunks/src-CfAOde7C.mjs.map +1 -0
  125. package/dist/chunks/src-Cu3TxNob.mjs +783 -0
  126. package/dist/chunks/src-Cu3TxNob.mjs.map +1 -0
  127. package/dist/chunks/src-DcU4IXMe.mjs +117 -0
  128. package/dist/chunks/src-DcU4IXMe.mjs.map +1 -0
  129. package/dist/chunks/src-JxawBQBn.mjs +446 -0
  130. package/dist/chunks/src-JxawBQBn.mjs.map +1 -0
  131. package/dist/chunks/src-s7YRvk0t.mjs +2116 -0
  132. package/dist/chunks/src-s7YRvk0t.mjs.map +1 -0
  133. package/dist/chunks/status-SMjiOMYy.mjs +29 -0
  134. package/dist/chunks/status-SMjiOMYy.mjs.map +1 -0
  135. package/dist/chunks/tail-BuhT-2PH.mjs +25 -0
  136. package/dist/chunks/tail-BuhT-2PH.mjs.map +1 -0
  137. package/dist/chunks/update-BXIppsbi.mjs +16 -0
  138. package/dist/chunks/update-BXIppsbi.mjs.map +1 -0
  139. package/dist/cli.mjs +175 -0
  140. package/dist/cli.mjs.map +1 -0
  141. package/package.json +17 -32
  142. package/CHANGELOG.md +0 -1461
  143. package/bin/cli.js +0 -817
  144. package/bin/report.js +0 -141
  145. package/core/adapter/render.js +0 -415
  146. package/core/agents/implementer.template.md +0 -142
  147. package/core/agents/profile-reader.md +0 -50
  148. package/core/agents/release.md +0 -58
  149. package/core/agents/review.md +0 -179
  150. package/core/commands/cohorte-align-ds.md +0 -33
  151. package/core/commands/cohorte-audit.md +0 -58
  152. package/core/commands/cohorte-brainstorm.md +0 -70
  153. package/core/commands/cohorte-build.md +0 -219
  154. package/core/commands/cohorte-doctor.md +0 -194
  155. package/core/commands/cohorte-fix.md +0 -80
  156. package/core/commands/cohorte-fleet.md +0 -103
  157. package/core/commands/cohorte-init-pipeline.md +0 -31
  158. package/core/commands/cohorte-intake.md +0 -92
  159. package/core/commands/cohorte-patch.md +0 -118
  160. package/core/commands/cohorte-refactor.md +0 -59
  161. package/core/commands/cohorte-retro.md +0 -85
  162. package/core/commands/cohorte-review.md +0 -270
  163. package/core/commands/cohorte-ship.md +0 -122
  164. package/core/commands/cohorte-spec.md +0 -101
  165. package/core/commands/cohorte-update-pipeline.md +0 -165
  166. package/core/hooks/gate.py +0 -588
  167. package/core/runtimes/claude.json +0 -74
  168. package/core/runtimes/codex.json +0 -85
  169. package/core/runtimes/cursor.json +0 -76
  170. package/core/runtimes/gemini.json +0 -76
  171. package/core/runtimes/opencode.json +0 -73
  172. package/core/templates/agent-handoff.md +0 -34
  173. package/core/templates/brainstorm-return.md +0 -36
  174. package/core/templates/decisions.template.md +0 -42
  175. package/core/templates/design-brief.md +0 -44
  176. package/core/templates/patch.template.md +0 -86
  177. package/core/templates/pr-body.md +0 -25
  178. package/core/templates/review-feedback.md +0 -38
  179. package/core/templates/spec.template.md +0 -87
  180. package/core/templates/steps/init-pipeline/01-detect-stack.md +0 -44
  181. package/core/templates/steps/init-pipeline/02-interview-gaps.md +0 -55
  182. package/core/templates/steps/init-pipeline/03-draft-profile.md +0 -10
  183. package/core/templates/steps/init-pipeline/04-write-render.md +0 -145
  184. package/core/templates/steps/init-pipeline/05-report.md +0 -12
  185. package/core/workflows/audit.js +0 -240
  186. package/core/workflows/loop.js +0 -641
  187. package/core/workflows/refactor.js +0 -276
  188. package/core/workflows/review.js +0 -456
  189. package/install.ps1 +0 -103
  190. package/install.sh +0 -114
  191. package/lib/doctor.js +0 -534
  192. package/lib/runtime.js +0 -139
  193. package/lib/versions.js +0 -149
  194. package/lib/yaml.js +0 -129
  195. package/profile/PIPELINE.template.md +0 -259
  196. package/profile/SCHEMA.md +0 -735
  197. package/profile/cohorte.config.template.yaml +0 -43
  198. package/scripts/assert-gate-hook.mjs +0 -75
  199. package/scripts/kanban-move.sh +0 -313
  200. package/scripts/metrics/collect.mjs +0 -531
  201. package/scripts/metrics/prices.json +0 -42
  202. package/scripts/new-feature.sh.template +0 -98
  203. package/scripts/preflight.sh +0 -124
  204. package/scripts/remove-feature.sh.template +0 -56
  205. package/scripts/test-adapter.mjs +0 -437
  206. package/scripts/test-gate.mjs +0 -443
  207. package/scripts/test-kanban.mjs +0 -304
  208. package/scripts/test-lib.mjs +0 -356
  209. package/scripts/test-metrics.mjs +0 -156
  210. package/scripts/test-workflows.mjs +0 -758
  211. package/scripts/validate-core.mjs +0 -325
@@ -0,0 +1,783 @@
1
+ import { randomFillSync } from "node:crypto";
2
+ import { Type } from "typebox";
3
+ //#region ../../packages/base/src/json.ts
4
+ const JsonValueCyclic = Type.Cyclic({ JsonValue: Type.Union([
5
+ Type.Null(),
6
+ Type.Boolean(),
7
+ Type.Number(),
8
+ Type.String(),
9
+ Type.Array(Type.Ref("JsonValue")),
10
+ Type.Record(Type.String(), Type.Ref("JsonValue"))
11
+ ]) }, "JsonValue");
12
+ /**
13
+ * The schema of {@link JsonValue}. Its static type is the hand-written alias rather than the type derived from
14
+ * the cyclic schema: the two are the same type (the assertion below breaks `tsc` if they drift), but the alias
15
+ * costs the compiler nothing where the derived one is re-expanded inside every schema that embeds a JSON value.
16
+ *
17
+ * NOT named `JsonValue` like its type, on purpose. Biome 2.5.14 (type-aware because of nursery/noFloatingPromises)
18
+ * overflows its stack on a RECURSIVE UNION whose name is shared by a TypeBox const, as soon as a class that extends
19
+ * Error holds a value of that type — `CohorteError.info.details` does. It then exits 0, so the lint looks green
20
+ * while nothing was checked. DESIGN 2.1 lists `JsonValue` as a type only (it is not `[S]`), so no contract moves.
21
+ * KEEP THE EXPLICIT ANNOTATION too: having to INFER this call is the other way into the same overflow (a
22
+ * `typeof JsonValueSchema`, or a namespace import of the barrel, is enough). See docs/v3/requests/U0.02.md R1.
23
+ */
24
+ const JsonValueSchema = Type.Unsafe(JsonValueCyclic);
25
+ const isPlainObject = (value) => {
26
+ const proto = Object.getPrototypeOf(value);
27
+ return proto === Object.prototype || proto === null;
28
+ };
29
+ const isJson = (value, ancestors) => {
30
+ if (value === null) return true;
31
+ switch (typeof value) {
32
+ case "boolean":
33
+ case "string": return true;
34
+ case "number": return Number.isFinite(value);
35
+ case "object": break;
36
+ default: return false;
37
+ }
38
+ if (ancestors.has(value)) return false;
39
+ ancestors.add(value);
40
+ try {
41
+ if (Array.isArray(value)) {
42
+ for (let index = 0; index < value.length; index += 1) if (!(index in value) || !isJson(value[index], ancestors)) return false;
43
+ return true;
44
+ }
45
+ if (!isPlainObject(value)) return false;
46
+ return Object.values(value).every((member) => isJson(member, ancestors));
47
+ } finally {
48
+ ancestors.delete(value);
49
+ }
50
+ };
51
+ /** Structural guard: finite numbers only, no `undefined`, no hole, plain objects only, no cycle. Never throws on data. */
52
+ function isJsonValue(value) {
53
+ try {
54
+ return isJson(value, /* @__PURE__ */ new Set());
55
+ } catch {
56
+ return false;
57
+ }
58
+ }
59
+ //#endregion
60
+ //#region ../../packages/base/src/errors.ts
61
+ /** Spec 24, in spec order. Closed: a fourteenth class would be a MAJOR of both frontiers. */
62
+ const ERROR_CLASSES = [
63
+ "configuration",
64
+ "validation",
65
+ "permission",
66
+ "security",
67
+ "provider-transient",
68
+ "provider-terminal",
69
+ "tool-transient",
70
+ "tool-terminal",
71
+ "conflict",
72
+ "budget",
73
+ "timeout",
74
+ "corruption",
75
+ "human-required"
76
+ ];
77
+ const ErrorClass = Type.Union([
78
+ Type.Literal("configuration"),
79
+ Type.Literal("validation"),
80
+ Type.Literal("permission"),
81
+ Type.Literal("security"),
82
+ Type.Literal("provider-transient"),
83
+ Type.Literal("provider-terminal"),
84
+ Type.Literal("tool-transient"),
85
+ Type.Literal("tool-terminal"),
86
+ Type.Literal("conflict"),
87
+ Type.Literal("budget"),
88
+ Type.Literal("timeout"),
89
+ Type.Literal("corruption"),
90
+ Type.Literal("human-required")
91
+ ]);
92
+ const ERROR_CODE_PATTERN = "^[a-z]+(?:-[a-z]+)*/[a-z0-9]+(?:-[a-z0-9]+)*$";
93
+ const ErrorInfoCyclic = Type.Cyclic({ ErrorInfo: Type.Object({
94
+ code: Type.String({ pattern: ERROR_CODE_PATTERN }),
95
+ class: ErrorClass,
96
+ message: Type.String(),
97
+ impact: Type.String(),
98
+ retryable: Type.Boolean(),
99
+ retryAfterMs: Type.Optional(Type.Number({ minimum: 0 })),
100
+ remediation: Type.String(),
101
+ cause: Type.Optional(Type.Ref("ErrorInfo")),
102
+ details: Type.Optional(Type.Record(Type.String(), JsonValueSchema))
103
+ }, { additionalProperties: false }) }, "ErrorInfo");
104
+ /**
105
+ * The schema of {@link ErrorInfo}, under the name of its type ([S], DESIGN §2). The static type is the DESIGN
106
+ * interface and the assertion below keeps it equal to what the schema derives: an `ErrorInfo` sits in dozens of
107
+ * event payloads, and a derived recursive type would be re-expanded in each of them.
108
+ *
109
+ * KEEP THE EXPLICIT `TUnsafe<ErrorInfo>` ANNOTATION. Biome 2.5.14 is type-aware (nursery/noFloatingPromises) and
110
+ * overflows its stack when it has to INFER this call for a const that shares its name with a recursive type, as
111
+ * soon as a class extending Error holds that type — `CohorteError.info` does. Biome then exits 0: the lint looks
112
+ * green while nothing was checked. With the annotation it has nothing to infer. See docs/v3/requests/U0.02.md R1.
113
+ */
114
+ const ErrorInfo = Type.Unsafe(ErrorInfoCyclic);
115
+ //#endregion
116
+ //#region ../../packages/base/src/catalogue.ts
117
+ /** DESIGN 2.8, column "CLI exit": the exit code of a process that waited for a run which ended on an error of that class. */
118
+ const EXIT_CODE_BY_CLASS = Object.freeze({
119
+ configuration: 10,
120
+ validation: 11,
121
+ permission: 12,
122
+ security: 13,
123
+ "provider-transient": 14,
124
+ "provider-terminal": 14,
125
+ "tool-transient": 15,
126
+ "tool-terminal": 15,
127
+ conflict: 16,
128
+ budget: 17,
129
+ timeout: 18,
130
+ corruption: 19,
131
+ "human-required": 20
132
+ });
133
+ const RETRY = true;
134
+ const NO_RETRY = false;
135
+ const row = (code, retryable, impact, remediation) => ({
136
+ code,
137
+ retryable,
138
+ impact,
139
+ remediation
140
+ });
141
+ /**
142
+ * APPEND-ONLY. A code is a public, stable identifier (spec 24): never rename one, never remove one, never move one
143
+ * to another class; add new rows at the END of their class block. `impact` says what the error means for the run,
144
+ * `remediation` is the imperative next action (spec 21). Every `<class>/unexpected` row is the landing place of a
145
+ * throwable nobody classified (DESIGN 2.8) and is never retried.
146
+ */
147
+ const ERROR_CATALOGUE_ROWS = [
148
+ row("configuration/policy-invalid", NO_RETRY, "The security policy is missing or cannot be parsed, so nothing may execute and the run does not start.", "Fix the policy keys named in the message in .cohorte/config.yaml, then run `cohorte doctor`."),
149
+ row("configuration/phase-not-available", NO_RETRY, "The requested phase or pipeline profile is not part of this version, so the run does not start.", "Choose a profile that `cohorte doctor` lists as available, or upgrade Cohorte."),
150
+ row("configuration/incompatible-state-schema", NO_RETRY, "The state database was written by an incompatible Cohorte version and is left untouched.", "Run the Cohorte version named in the message, or back up the state and run `cohorte migrate --apply`."),
151
+ row("configuration/platform-unsupported", NO_RETRY, "This operating system or architecture cannot provide what the configuration requires, so the run does not start.", "Run on a supported platform, or relax the requirement named in the message in your user configuration."),
152
+ row("configuration/telemetry-remote-unavailable", NO_RETRY, "Remote telemetry is configured but does not exist in this version; nothing was sent.", "Remove the remote telemetry settings; local logs and metrics stay available."),
153
+ row("configuration/worktree-root-protected", NO_RETRY, "The configured worktree root lies inside a protected directory, so no worktree can be created there.", "Set git.worktreeRoot to a directory outside the protected roots, or remove the setting to use the default."),
154
+ row("configuration/provision-store-unavailable", NO_RETRY, "Dependencies cannot be provisioned offline because the package manager store is not reachable.", "Populate the package manager store (install once with network access), then start the run again."),
155
+ row("configuration/engine-init", NO_RETRY, "The agent runtime could not initialise its model engine, so the agent never started.", "Run `cohorte doctor` and fix the provider or model configuration it reports."),
156
+ row("configuration/fake-script-unmatched", NO_RETRY, "The fake runtime script has no entry for this spawn request, so the scripted run cannot continue.", "Add a matching entry to the fake runtime script, or correct the spawn fields it matches on."),
157
+ row("configuration/unexpected", NO_RETRY, "An unclassified configuration failure stopped the run before or while it started.", "Read the message, run `cohorte doctor`, and report the error if the configuration looks correct."),
158
+ row("validation/tool-input", NO_RETRY, "A tool call did not match the input schema of the tool and was not executed.", "Nothing to do if the agent recovers; otherwise inspect the rejected call with `cohorte logs`."),
159
+ row("validation/agent-output", NO_RETRY, "The final output of the agent does not match the schema of its phase, so it cannot be used.", "Inspect the agent transcript; retry the phase or escalate the model tier."),
160
+ row("validation/agent-no-result", NO_RETRY, "The agent stopped without delivering a structured result, so its phase has no outcome.", "Retry the phase; if it repeats, inspect the transcript and escalate the model tier."),
161
+ row("validation/spec", NO_RETRY, "The spec file is invalid, so the run cannot be planned from it.", "Fix the fields named in the message in the spec file, then start the run again."),
162
+ row("validation/invalid-id", NO_RETRY, "A value is not a well-formed identifier of the expected kind and was refused before reaching a path, a ref or the store.", "Pass an identifier of the shape named in the message, as printed by `cohorte status`."),
163
+ row("validation/unexpected", NO_RETRY, "An unclassified validation failure made a value unusable.", "Read the message, inspect the offending input with `cohorte logs`, and report the error if the input looks valid."),
164
+ row("permission/tool-not-granted", NO_RETRY, "The agent called a tool its role was not granted; the call was denied and nothing executed.", "Nothing to do if the agent adapts; grant the tool to the role in the configuration if the call was legitimate."),
165
+ row("permission/path-outside-grant", NO_RETRY, "The agent addressed a path outside what its grant allows; the call was denied and nothing was read or written.", "Nothing to do if the agent adapts; widen the ownership of the surface if the path legitimately belongs to it."),
166
+ row("permission/command-not-allowed", NO_RETRY, "The command is not in the allowlist of the command policy; it was denied and nothing executed.", "Add a rule for the command to the command policy if it is legitimate, then resume."),
167
+ row("permission/network-denied", NO_RETRY, "The call needs network access, which agents never have in this version; it was denied.", "Provide what the agent needed locally, for example through provisioning, then resume."),
168
+ row("permission/denied-by-human", NO_RETRY, "A human denied the approval request; the call was not executed.", "Nothing to do; approve a new request if the denial was a mistake."),
169
+ row("permission/unexpected", NO_RETRY, "An unclassified permission failure denied the call; nothing executed.", "Read the message and inspect the denied call with `cohorte logs`."),
170
+ row("security/symlink-escape", NO_RETRY, "A path resolves through a symbolic link to a location outside its allowed root; the run is blocked and its worktrees are frozen.", "Inspect the link named in the message, remove it if it is hostile, then acknowledge with `cohorte resume --ack`."),
171
+ row("security/protected-path", NO_RETRY, "The agent targeted a protected path such as .git, .cohorte or a credential file; the run is blocked.", "Inspect the attempted call with `cohorte logs`, then acknowledge with `cohorte resume --ack` or cancel the run."),
172
+ row("security/write-outside-ownership", NO_RETRY, "Changes were found outside the ownership of the surface that produced them; they are not integrated and the run is blocked.", "Inspect the diff of the frozen worktree, revert or re-assign the files, then acknowledge with `cohorte resume --ack`."),
173
+ row("security/runtime-pin-mismatch", NO_RETRY, "The installed runtime no longer matches the pin taken when the run started; no agent is spawned.", "Reinstall the pinned Cohorte and runtime versions named in the message, then resume."),
174
+ row("security/asset-hash-mismatch", NO_RETRY, "An embedded prompt, skill, schema or migration does not match its recorded hash; the installation is not trusted.", "Reinstall Cohorte from the registry and compare with `cohorte doctor`."),
175
+ row("security/pin-tampered", NO_RETRY, "The recorded runtime pin or run snapshot was modified after the run started; the run is blocked.", "Inspect the run directory for tampering; start a new run if the snapshot cannot be trusted."),
176
+ row("security/runtime-inside-target", NO_RETRY, "Cohorte is running from inside the repository it would modify, so agents could rewrite their own supervisor; the run does not start.", "Run an installed Cohorte that lives outside the target repository."),
177
+ row("security/auth-mode-violation", NO_RETRY, "A request was about to use another billing mode than the one the run plan allows; it was stopped before leaving the machine.", "Check the provider authentication with `cohorte auth status`; opt in to metered billing explicitly if that is intended."),
178
+ row("security/auth-endpoint-mismatch", NO_RETRY, "The provider endpoint differs from the pinned one for this authentication mode; no credential was sent.", "Remove the endpoint override, or pin the endpoint in your user configuration if it is legitimate."),
179
+ row("security/command-auth-invalid", NO_RETRY, "A control command carried a missing, unknown or wrong authenticator; it was rejected and never applied.", "Send the command again through the `cohorte` CLI as the user who owns the project key."),
180
+ row("security/event-chain-broken", NO_RETRY, "The event history of the run fails its hash chain or anchor check, so it may have been rewritten; the run is blocked.", "Inspect the state database and its backups; never delete the run, restore a verified backup instead."),
181
+ row("security/root-refused", NO_RETRY, "Cohorte refuses to run as root; nothing was started.", "Run Cohorte as an unprivileged user."),
182
+ row("security/sandbox-unavailable", NO_RETRY, "The required operating-system sandbox is not available, so commands would run unconfined; the run does not start.", "Follow the sandbox remediation printed by `cohorte doctor`, or opt in to best-effort isolation in your user configuration."),
183
+ row("security/secret-staged", NO_RETRY, "A secret was detected in the changes about to be committed; the commit was not created and the run is blocked.", "Remove the secret from the worktree and rotate it, then acknowledge with `cohorte resume --ack`."),
184
+ row("security/command-trampoline", NO_RETRY, "The command would launch another program on behalf of the agent (a shell, an interpreter flag, an exec wrapper); it was denied.", "Inspect the attempted command with `cohorte logs`; expose the real program through the command policy if it is legitimate."),
185
+ row("security/project-policy-untrusted", NO_RETRY, "The configuration of the repository loosens security settings that only the local user may loosen; the run does not start.", "Review the keys named in the message, then grant them with `cohorte config trust` or set them in your user configuration."),
186
+ row("security/deps-tampered", NO_RETRY, "Provisioned dependencies changed after they were installed and sealed; the run is blocked.", "Inspect the slot named in the message, then provision it again from the lockfile."),
187
+ row("security/review-ref-mutated", NO_RETRY, "The immutable ref under review changed while it was being reviewed; the review result cannot be trusted.", "Inspect who moved the ref, then run the review again on a fresh ref."),
188
+ row("security/command-global-option", NO_RETRY, "The command carries a global option that redirects the program (for git: -C, -c, --git-dir, --work-tree); it was denied.", "Inspect the attempted command with `cohorte logs`; no configuration allows these options."),
189
+ row("security/gate-internal-error", NO_RETRY, "A stage of the permission gate failed unexpectedly; the call was denied because the gate fails closed.", "Report the error with the run logs; resume once the cause is understood."),
190
+ row("security/redaction-failed", NO_RETRY, "The redactor failed on an event; the payload was dropped instead of being stored unredacted.", "Report the error with the run logs; the dropped payload is not recoverable."),
191
+ row("security/unexpected", NO_RETRY, "An unclassified security failure blocked the run; its worktrees are frozen.", "Inspect the run with `cohorte logs` before acknowledging with `cohorte resume --ack`."),
192
+ row("provider-transient/rate-limited", RETRY, "The provider is rate limiting requests; the request is retried with a bounded back-off.", "Nothing to do; if it persists, lower the agent concurrency or wait for the limit to reset."),
193
+ row("provider-transient/overloaded", RETRY, "The provider is overloaded or returned a server error; the request is retried with a bounded back-off.", "Nothing to do; check the status page of the provider if it persists."),
194
+ row("provider-transient/network", RETRY, "The connection to the provider failed; the request is retried with a bounded back-off.", "Check the network connection and any proxy settings if it persists."),
195
+ row("provider-transient/credential-store-locked", RETRY, "The credential store is locked by another process; the request is retried shortly.", "Close other programs that use the same login if it persists."),
196
+ row("provider-transient/unexpected", NO_RETRY, "An unclassified provider failure interrupted a model request; it is not retried because its cause is unknown.", "Read the message, then resume the run; report the error if it repeats."),
197
+ row("provider-terminal/auth-required", NO_RETRY, "The provider needs a new login; the run is suspended until authentication is restored.", "Run `cohorte auth login`, then `cohorte resume`."),
198
+ row("provider-terminal/quota-exceeded", NO_RETRY, "The plan limit of the provider is reached; the run is suspended until the quota resets.", "Wait for the reset time shown by `cohorte status`, then resume; automatic resume does it when enabled."),
199
+ row("provider-terminal/entitlement", NO_RETRY, "The account is not entitled to this model or usage; the request cannot succeed as configured.", "Choose a model your plan includes, or change the plan with the provider."),
200
+ row("provider-terminal/model-not-found", NO_RETRY, "The configured model does not exist for this provider; the agent cannot start.", "Correct the model in the tier table of the configuration; `cohorte doctor` lists the known models."),
201
+ row("provider-terminal/policy-refused", NO_RETRY, "The provider refused the request under its usage policies; repeating it unchanged will not help.", "Inspect the refused request in the transcript and rephrase the task or the spec."),
202
+ row("provider-terminal/unexpected", NO_RETRY, "An unclassified provider failure ended a model request for good.", "Read the message and the transcript; report the error if the request looks valid."),
203
+ row("tool-transient/spawn-failed", RETRY, "The process of a tool could not be started; the call is retried once.", "Check that the program exists on the pinned PATH reported by `cohorte doctor` if it persists."),
204
+ row("tool-transient/interrupted", RETRY, "A tool call was interrupted before it finished, for example by a host restart; it is replayed when that is safe.", "Nothing to do; inspect the effect journal with `cohorte inspect` if the call is reported in doubt."),
205
+ row("tool-transient/agent-process-exit", RETRY, "The process of the agent exited without settling; a fresh incarnation takes over from the file ledger.", "Nothing to do; check the memory and the logs of the machine if it repeats."),
206
+ row("tool-transient/cancelled", RETRY, "The tool call was cancelled before it finished because its agent or its run was paused or cancelled.", "Nothing to do; resume the run to let the agent issue the call again."),
207
+ row("tool-transient/unexpected", NO_RETRY, "An unclassified failure interrupted a tool call; it is not retried because its cause is unknown.", "Read the message and inspect the call with `cohorte logs`; report the error if it repeats."),
208
+ row("tool-terminal/nonzero-exit", NO_RETRY, "The command exited with a non-zero status; the result was handed to the agent.", "Nothing to do; the agent sees the output and decides what to do next."),
209
+ row("tool-terminal/output-cap", NO_RETRY, "The output of the tool exceeded its cap and was truncated or the process was stopped.", "Nothing to do; narrow the command if the agent keeps hitting the cap."),
210
+ row("tool-terminal/patch-preimage-mismatch", NO_RETRY, "The file no longer matches the content the patch was written against; nothing was written.", "Nothing to do; the agent must read the file again before patching it."),
211
+ row("tool-terminal/unexpected", NO_RETRY, "An unclassified failure ended a tool call; an error result was handed to the agent.", "Read the message and inspect the call with `cohorte logs`."),
212
+ row("conflict/merge", NO_RETRY, "The branch of a surface does not merge cleanly into the integration branch; the conflict goes to a fix round or to a human.", "Let the fix round resolve it, or resolve the conflict on the integration branch and resume."),
213
+ row("conflict/zone-reserved", NO_RETRY, "Another agent holds the lock on an overlapping zone of files; the writer must wait for it.", "Nothing to do; it clears when the holder finishes. Cancel the holder if it is stuck."),
214
+ row("conflict/incarnation-exists", NO_RETRY, "An incarnation of this agent is already registered; a second one was refused.", "Nothing to do; if no such process is alive, `cohorte resume` sweeps the stale incarnation."),
215
+ row("conflict/command-id-reuse", NO_RETRY, "A command identifier was reused with a different body; the second command was rejected.", "Send the command again with a fresh identifier."),
216
+ row("conflict/run-host-alive", NO_RETRY, "A run host already drives this run; a second one was refused.", "Observe the run with `cohorte tail`; stop the other host first if you mean to replace it."),
217
+ row("conflict/lease-lost", NO_RETRY, "This run host lost its lease to a newer one and was fenced off; it stopped writing.", "Nothing to do; the newer host owns the run. Observe it with `cohorte status`."),
218
+ row("conflict/reconcile-human-edit", NO_RETRY, "A generated file was edited by hand since it was rendered; reconcile will not overwrite it.", "Keep your edit and mark the field as overridden, or restore the generated content, then plan the reconcile again."),
219
+ row("conflict/not-running", NO_RETRY, "The command applies to a running pipeline only and the run is not running; it was rejected.", "Check the state with `cohorte status` and use the command that fits it."),
220
+ row("conflict/run-terminal", NO_RETRY, "The run has already ended; the command was rejected.", "Start a new run; a completed or cancelled run cannot change."),
221
+ row("conflict/run-active", NO_RETRY, "The run is active, and this command applies to a halted run only; it was rejected.", "Wait for the run to halt, or pause it, before sending this command."),
222
+ row("conflict/run-halted", NO_RETRY, "The run is halted (FAILED or BLOCKED) rather than running; the command was rejected. A halted run is not over.", "Resume it the way its state allows — `cohorte retry` out of FAILED, `cohorte resume --ack` out of BLOCKED."),
223
+ row("conflict/use-retry", NO_RETRY, "The run is FAILED, and this command resumes a suspended run only; it was rejected.", "Use `cohorte retry` to resume a FAILED run."),
224
+ row("conflict/use-resume", NO_RETRY, "The run is suspended, and this command resumes a FAILED run only; it was rejected.", "Use `cohorte resume` to resume a suspended run."),
225
+ row("conflict/use-resume-ack", NO_RETRY, "The run is BLOCKED by a security stop, and this command does not acknowledge it; it was rejected.", "Read the stop record, then use `cohorte resume --ack` to acknowledge it and resume."),
226
+ row("conflict/run-blocked", NO_RETRY, "The run is BLOCKED by a security stop; the command was rejected while the stop stands.", "Read the stop record and resume with `cohorte resume --ack` before sending this command again."),
227
+ row("conflict/unexpected", NO_RETRY, "An unclassified conflict stopped an operation; the state it competed for was left as it was.", "Check the state with `cohorte status`, then repeat the operation."),
228
+ row("budget/tokens", NO_RETRY, "A token budget is exhausted; the run waits for a human decision.", "Raise the budget with `cohorte approve`, or cancel the run."),
229
+ row("budget/tool-calls-exhausted", NO_RETRY, "The tool-call budget of the agent is exhausted; further calls are refused.", "Raise the tool-call budget with `cohorte approve`, or let the agent conclude with what it has."),
230
+ row("budget/context-window", NO_RETRY, "The conversation no longer fits the context window of the model; a fresh incarnation continues with a smaller context built by Cohorte.", "Nothing to do; split the task or narrow the ownership if it repeats."),
231
+ row("budget/fix-rounds", NO_RETRY, "The maximum number of fix rounds is reached with findings still open; the run waits for a human decision.", "Review the open findings, then raise the limit with `cohorte approve` or take over by hand."),
232
+ row("budget/provider", NO_RETRY, "The budget configured for this provider is exhausted; the run waits for a human decision.", "Raise the provider budget with `cohorte approve`, or cancel the run."),
233
+ row("budget/estimated-quota", NO_RETRY, "The estimated share of the plan quota used by this run reached its limit; the run waits for a human decision.", "Raise the limit with `cohorte approve`, or wait for the quota window to reset."),
234
+ row("budget/incarnations", NO_RETRY, "The agent was restarted as many times as allowed and is now failed.", "Inspect why the agent keeps dying with `cohorte logs`, then retry the phase."),
235
+ row("budget/unexpected", NO_RETRY, "An unclassified budget failure stopped the run from spending more.", "Check the counters with `cohorte status`, then approve a higher budget or cancel the run."),
236
+ row("timeout/tool", RETRY, "A tool call exceeded its time limit and its process tree was killed; the call is retried once.", "Raise the timeout of the command in the configuration if the command is legitimately slow."),
237
+ row("timeout/model-request", RETRY, "A model request exceeded its time limit; it is retried with a bounded back-off.", "Nothing to do; check the network and the status of the provider if it persists."),
238
+ row("timeout/agent", RETRY, "The agent exceeded its wall-clock limit and was stopped; the retry policy decides what follows.", "Raise the agent time limit, or split the task, if the work is legitimately long."),
239
+ row("timeout/run", NO_RETRY, "The run exceeded its wall-clock budget; it waits for a human decision.", "Extend the run with `cohorte approve`, or cancel it."),
240
+ row("timeout/unexpected", NO_RETRY, "An unclassified timeout stopped an operation; it is not retried because its cause is unknown.", "Read the message, then resume the run; report the error if it repeats."),
241
+ row("corruption/event-gap", NO_RETRY, "The event sequence of the run has a gap or a duplicate; the store refuses to open it and deletes nothing.", "Restore the state database from a backup; keep the damaged file for inspection."),
242
+ row("corruption/projection-mismatch", NO_RETRY, "The stored projections disagree with what the events replay to; the run is not resumed from them.", "Run `cohorte doctor --verify-state` to see which projection differs from the event log, and report it; the events are the truth."),
243
+ row("corruption/snapshot-hash", NO_RETRY, "A state snapshot does not match its recorded hash; it is ignored and an older one is used when available.", "Nothing to do if resume succeeds; otherwise restore the state database from a backup."),
244
+ row("corruption/incompatible-schema", NO_RETRY, "The schema of the state database is not one this version can read; it refuses to open it and never migrates on its own.", "Run the Cohorte version that wrote the database, or back up the state and run `cohorte migrate --apply`."),
245
+ row("corruption/unexpected", NO_RETRY, "An unclassified integrity failure made stored state untrustworthy; nothing was deleted.", "Back up the .cohorte/state directory before anything else, then run `cohorte doctor`."),
246
+ row("human-required/approval", NO_RETRY, "A call needs the approval of a human; the run waits and nothing executes meanwhile.", "Review the request with `cohorte status`, then `cohorte approve` or `cohorte deny`."),
247
+ row("human-required/approval-timeout", NO_RETRY, "An approval request expired without an answer; the call was not executed.", "Resume the run to let the agent ask again, then answer in time."),
248
+ row("human-required/blocked-ack", NO_RETRY, "The run is blocked after a security event and needs a human to inspect it.", "Inspect the run with `cohorte logs`, then acknowledge with `cohorte resume --ack` or cancel it."),
249
+ row("human-required/in-doubt-effect", NO_RETRY, "An effect was interrupted and cannot be proven done or not done; repeating it blindly could apply it twice.", "Inspect the effect with `cohorte inspect`, then resolve it by hand and resume."),
250
+ row("human-required/unexpected", NO_RETRY, "An unclassified situation needs a human decision; the run waits.", "Check what the run waits for with `cohorte status`, then answer it.")
251
+ ];
252
+ const classOf = (code) => {
253
+ const prefix = code.slice(0, code.indexOf("/"));
254
+ const found = ERROR_CLASSES.find((candidate) => candidate === prefix);
255
+ if (!found) throw new TypeError(`error catalogue: ${code} does not start with an error class`);
256
+ return found;
257
+ };
258
+ const build = () => {
259
+ const catalogue = Object.create(null);
260
+ for (const { code, retryable, impact, remediation } of ERROR_CATALOGUE_ROWS) {
261
+ if (code in catalogue) throw new TypeError(`error catalogue: ${code} is listed twice`);
262
+ const errorClass = classOf(code);
263
+ catalogue[code] = Object.freeze({
264
+ class: errorClass,
265
+ retryable,
266
+ impact,
267
+ remediation,
268
+ exit: EXIT_CODE_BY_CLASS[errorClass]
269
+ });
270
+ }
271
+ return Object.freeze(catalogue);
272
+ };
273
+ /** DESIGN 2.8 (PLAN PC-1: the data lives in base so that L2 packages can mint complete `ErrorInfo`s). */
274
+ const ERROR_CATALOGUE = build();
275
+ function isErrorCode(code) {
276
+ return Object.hasOwn(ERROR_CATALOGUE, code);
277
+ }
278
+ //#endregion
279
+ //#region ../../packages/base/src/taxonomy.ts
280
+ const MAX_MESSAGE_LENGTH = 2e3;
281
+ /** "single paragraph" (DESIGN 2.1): one line, bounded. Redaction is NOT done here: only a Redactor seals. */
282
+ const paragraph = (text) => {
283
+ const flat = text.replace(/\s+/g, " ").trim();
284
+ return flat.length > MAX_MESSAGE_LENGTH ? `${flat.slice(0, 1999)}…` : flat;
285
+ };
286
+ /** Returns `info` itself when its chain is already within bounds, a truncated copy otherwise. */
287
+ const capCause = (info, remaining) => {
288
+ if (!info.cause) return info;
289
+ if (remaining === 0) {
290
+ const { cause: _dropped, ...rest } = info;
291
+ return {
292
+ ...rest,
293
+ details: {
294
+ ...rest.details,
295
+ causeChainTruncated: true
296
+ }
297
+ };
298
+ }
299
+ const capped = capCause(info.cause, remaining - 1);
300
+ return capped === info.cause ? info : {
301
+ ...info,
302
+ cause: capped
303
+ };
304
+ };
305
+ /** A frozen copy of the chain, so that a thrown error cannot be edited on its way up. `details` are shared, not copied. */
306
+ const frozenChain = (info) => Object.freeze(info.cause ? {
307
+ ...info,
308
+ cause: frozenChain(info.cause)
309
+ } : { ...info });
310
+ /**
311
+ * Mints a complete `ErrorInfo` from the catalogue: class, retryability, impact and remediation come from the row of
312
+ * `code`, never from the call site (PLAN PC-1).
313
+ */
314
+ function errorOf(code, message, extra = {}) {
315
+ const entry = ERROR_CATALOGUE[code];
316
+ if (!entry || !isErrorCode(code)) throw new TypeError(`errorOf: ${String(code)} is not in ERROR_CATALOGUE`);
317
+ const { retryAfterMs, cause, details } = extra;
318
+ const info = {
319
+ code,
320
+ class: entry.class,
321
+ message: paragraph(message),
322
+ impact: entry.impact,
323
+ retryable: entry.retryable,
324
+ ...isRetryAfterMs(retryAfterMs) ? { retryAfterMs } : {},
325
+ remediation: entry.remediation,
326
+ ...cause === void 0 || cause === null ? {} : { cause: isCanonical(cause, 5) ? cause : wireSafe(cause, 4, UNCLASSIFIED) },
327
+ ...details === void 0 ? {} : { details: isDetails(details) ? details : { detailsDropped: true } }
328
+ };
329
+ return capCause(info, 5);
330
+ }
331
+ /** Where an error lands when even the caller's fallback class is not one of the thirteen (a JS caller, a cast, a decoded wire value). */
332
+ const UNCLASSIFIED = "validation/unexpected";
333
+ const isClassified = (info) => {
334
+ try {
335
+ return isErrorCode(info.code) && ERROR_CATALOGUE[info.code]?.class === info.class;
336
+ } catch {
337
+ return false;
338
+ }
339
+ };
340
+ const INFO_KEYS = /* @__PURE__ */ new Set([
341
+ "code",
342
+ "class",
343
+ "message",
344
+ "impact",
345
+ "retryable",
346
+ "retryAfterMs",
347
+ "remediation",
348
+ "cause",
349
+ "details"
350
+ ]);
351
+ const isRetryAfterMs = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0;
352
+ const isDetails = (value) => typeof value === "object" && value !== null && !Array.isArray(value) && isJsonValue(value);
353
+ /**
354
+ * True when `info` is exactly what `errorOf` would mint: strict JSON, a catalogue code under its own class, the
355
+ * catalogue's impact, retryability and remediation, no foreign member, and a cause chain of the same kind no deeper
356
+ * than `remaining`. "Is JSON" alone is not enough: a cast can carry a security code as a retryable tool error.
357
+ */
358
+ const isCanonical = (info, remaining) => {
359
+ try {
360
+ return isJsonValue(info) && isClassified(info) && hasCanonicalMembers(info, remaining);
361
+ } catch {
362
+ return false;
363
+ }
364
+ };
365
+ const hasCanonicalMembers = (info, remaining) => {
366
+ const raw = info;
367
+ const entry = isErrorCode(info.code) ? ERROR_CATALOGUE[info.code] : void 0;
368
+ if (!entry || raw.impact !== entry.impact || raw.retryable !== entry.retryable) return false;
369
+ if (raw.remediation !== entry.remediation || typeof raw.message !== "string") return false;
370
+ if (!Object.keys(info).every((key) => INFO_KEYS.has(key))) return false;
371
+ if ("retryAfterMs" in info && !isRetryAfterMs(raw.retryAfterMs)) return false;
372
+ if ("details" in info && !isDetails(raw.details)) return false;
373
+ if (!("cause" in info)) return true;
374
+ return remaining > 0 && typeof raw.cause === "object" && raw.cause !== null && isCanonical(info.cause, remaining - 1);
375
+ };
376
+ /**
377
+ * Rebuilds an `ErrorInfo` that is not what `errorOf` mints (an `undefined` optional, a Date in `details`, a cast, a
378
+ * call-site `retryable`) WITHOUT reclassifying it: a `security/*` error must stay one across every boundary (I2).
379
+ * The catalogue row of its own code restores class, impact, retryability and remediation; `fallback` is used only
380
+ * when the code is not a catalogue code of the claimed class. Never throws.
381
+ */
382
+ const wireSafe = (info, remaining, fallback) => {
383
+ if (isCanonical(info, remaining)) return info;
384
+ try {
385
+ const raw = info;
386
+ const code = isClassified(info) && isErrorCode(info.code) ? info.code : fallback;
387
+ const extra = {};
388
+ if (isRetryAfterMs(raw.retryAfterMs)) extra.retryAfterMs = raw.retryAfterMs;
389
+ let truncated = false;
390
+ if (typeof raw.cause === "object" && raw.cause !== null) {
391
+ if (remaining > 0) extra.cause = wireSafe(raw.cause, remaining - 1, fallback);
392
+ else truncated = true;
393
+ }
394
+ if (raw.details !== void 0) extra.details = isDetails(raw.details) ? raw.details : { detailsDropped: true };
395
+ if (truncated) extra.details = {
396
+ ...extra.details,
397
+ causeChainTruncated: true
398
+ };
399
+ return errorOf(code, typeof raw.message === "string" ? raw.message : "unknown error", extra);
400
+ } catch {
401
+ return errorOf(fallback, "unknown error (unreadable)", { details: { thrown: "unreadable" } });
402
+ }
403
+ };
404
+ var CohorteError = class extends Error {
405
+ info;
406
+ constructor(info, options) {
407
+ super(`${info.code}: ${info.message}`, options && "cause" in options ? { cause: options.cause } : void 0);
408
+ this.name = "CohorteError";
409
+ const safe = isClassified(info) ? wireSafe(info, 5, UNCLASSIFIED) : info;
410
+ this.info = frozenChain(capCause(safe, 5));
411
+ }
412
+ };
413
+ /** What a Wave-0 typed stub throws (DESIGN 10.1 rule 3). Deliberately not a `CohorteError`: reaching one is a bug, not a run outcome. */
414
+ var NotImplemented = class extends Error {
415
+ constructor(what) {
416
+ super(what ? `not implemented: ${what}` : "not implemented");
417
+ this.name = "NotImplemented";
418
+ }
419
+ };
420
+ const describeThrown = (thrown) => {
421
+ const kind = thrown === null ? "null" : typeof thrown;
422
+ const unknown = `unknown error (${kind})`;
423
+ switch (typeof thrown) {
424
+ case "string": return {
425
+ message: thrown || unknown,
426
+ details: { thrown: kind }
427
+ };
428
+ case "number":
429
+ case "bigint":
430
+ case "boolean":
431
+ case "symbol": return {
432
+ message: String(thrown),
433
+ details: { thrown: kind }
434
+ };
435
+ case "object":
436
+ case "function": break;
437
+ default: return {
438
+ message: unknown,
439
+ details: { thrown: kind }
440
+ };
441
+ }
442
+ if (thrown === null) return {
443
+ message: unknown,
444
+ details: { thrown: kind }
445
+ };
446
+ const record = thrown;
447
+ const isError = thrown instanceof Error;
448
+ const name = isError && typeof record.name === "string" && record.name ? record.name : void 0;
449
+ const text = typeof record.message === "string" && record.message ? record.message : name ?? unknown;
450
+ const details = { thrown: name ?? kind };
451
+ if (isError && typeof record.code === "string" && record.code) details.errorCode = record.code;
452
+ return {
453
+ message: text,
454
+ details
455
+ };
456
+ };
457
+ const fallbackCode = (fallback) => {
458
+ try {
459
+ if (isErrorCode(fallback.code) && ERROR_CATALOGUE[fallback.code]?.class === fallback.class) return fallback.code;
460
+ const unexpected = `${fallback.class}/unexpected`;
461
+ return isErrorCode(unexpected) ? unexpected : UNCLASSIFIED;
462
+ } catch {
463
+ return UNCLASSIFIED;
464
+ }
465
+ };
466
+ const convert = (thrown, fallback, remaining) => {
467
+ let info;
468
+ let jsCause;
469
+ try {
470
+ if (thrown instanceof CohorteError) {
471
+ info = thrown.info;
472
+ jsCause = info.cause ? void 0 : thrown.cause;
473
+ } else {
474
+ const { message, details } = describeThrown(thrown);
475
+ info = errorOf(fallback, message, { details });
476
+ jsCause = thrown instanceof Error ? thrown.cause : void 0;
477
+ }
478
+ } catch {
479
+ info = errorOf(fallback, "unknown error (unreadable)", { details: { thrown: "unreadable" } });
480
+ jsCause = void 0;
481
+ }
482
+ if (jsCause === void 0 || jsCause === null) return capCause(info, remaining);
483
+ if (remaining === 0) return {
484
+ ...info,
485
+ details: {
486
+ ...info.details,
487
+ causeChainTruncated: true
488
+ }
489
+ };
490
+ return {
491
+ ...info,
492
+ cause: convert(jsCause, fallback, remaining - 1)
493
+ };
494
+ };
495
+ /**
496
+ * Total: never throws, whatever was thrown and whatever `fallback` holds. A `CohorteError` keeps its own
497
+ * classification, even when its `ErrorInfo` has to be rebuilt to be strict JSON. Anything else becomes
498
+ * `fallback.code` when that is a catalogue code of `fallback.class`, and `<fallback.class>/unexpected` otherwise
499
+ * (DESIGN 2.8), so the result is always a catalogue code with its impact and remediation. `Error.cause` is
500
+ * followed, at most MAX_CAUSE_DEPTH deep.
501
+ */
502
+ function toErrorInfo(e, fallback) {
503
+ const code = fallbackCode(fallback);
504
+ return wireSafe(convert(e, code, 5), 5, code);
505
+ }
506
+ //#endregion
507
+ //#region ../../packages/base/src/ids.ts
508
+ /** Token-safe for François' single ${token} slot, for refs and for paths. */
509
+ const ID_PATTERN = "^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$";
510
+ const ISO_INSTANT_PATTERN = "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]{3}Z$";
511
+ /** The shape each kind of DESIGN 2.1 has, as a pattern (for schemas) and as words (for a human). */
512
+ const ID_SHAPES = {
513
+ RunId: {
514
+ pattern: "^run_[0-9a-f]{32}$",
515
+ shape: "run_<32 lowercase hex digits>"
516
+ },
517
+ AgentId: {
518
+ pattern: "^agt_[A-Za-z0-9.-]+_[A-Za-z0-9_.-]+$",
519
+ shape: "agt_<role>_<surface|main>[_<n>]"
520
+ },
521
+ PhaseRunId: {
522
+ pattern: "^phs_[A-Z][A-Z_]*_[0-9]+$",
523
+ shape: "phs_<STATE>_<iteration>"
524
+ },
525
+ EventId: {
526
+ pattern: "^evt_[0-9a-f]{32}$",
527
+ shape: "evt_<32 lowercase hex digits>"
528
+ },
529
+ CommandId: {
530
+ pattern: "^cmd_[0-9a-f]{32}$",
531
+ shape: "cmd_<32 lowercase hex digits>"
532
+ },
533
+ ApprovalId: {
534
+ pattern: "^apr_[0-9a-f]{32}$",
535
+ shape: "apr_<32 lowercase hex digits>"
536
+ },
537
+ ToolCallId: {
538
+ pattern: "^tc_[0-9]+_[0-9]+$",
539
+ shape: "tc_<incarnation>_<ordinal>"
540
+ },
541
+ EffectId: {
542
+ pattern: "^eff_[0-9a-f]{32}$",
543
+ shape: "eff_<32 lowercase hex digits>"
544
+ },
545
+ ArtifactId: {
546
+ pattern: "^art_[0-9a-f]{32}$",
547
+ shape: "art_<32 lowercase hex digits>"
548
+ },
549
+ FindingId: {
550
+ pattern: "^fnd_[0-9a-f]{16}$",
551
+ shape: "fnd_<16 lowercase hex digits>"
552
+ },
553
+ SpecId: {
554
+ pattern: ID_PATTERN,
555
+ shape: "letters, digits, \"_\", \".\" and \"-\" (at most 128, not starting with \".\" or \"-\")"
556
+ },
557
+ SurfaceId: {
558
+ pattern: ID_PATTERN,
559
+ shape: "letters, digits, \"_\", \".\" and \"-\" (at most 128, not starting with \".\" or \"-\")"
560
+ },
561
+ Sha256: {
562
+ pattern: "^[0-9a-f]{64}$",
563
+ shape: "64 lowercase hex digits"
564
+ },
565
+ IsoInstant: {
566
+ pattern: ISO_INSTANT_PATTERN,
567
+ shape: "an RFC 3339 UTC instant with milliseconds, such as 2026-01-01T00:00:00.000Z"
568
+ }
569
+ };
570
+ ID_SHAPES.SpecId;
571
+ const compiled = /* @__PURE__ */ new Map();
572
+ const regExpOf = (pattern) => {
573
+ let found = compiled.get(pattern);
574
+ if (!found) {
575
+ found = new RegExp(pattern);
576
+ compiled.set(pattern, found);
577
+ }
578
+ return found;
579
+ };
580
+ /**
581
+ * ID_PATTERN plus the three things it lets through that `git check-ref-format` refuses in a ref component:
582
+ * "..", a trailing "." and a trailing ".lock". Ids reach branch names (`cohorte/<runId>/<slot>/<n>`, DESIGN 5.1).
583
+ */
584
+ function isSafeId(raw) {
585
+ return regExpOf("^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$").test(raw) && !raw.includes("..") && !raw.endsWith(".") && !raw.endsWith(".lock");
586
+ }
587
+ /** Renders a date as an IsoInstant. Throws a RangeError on an invalid date or a year outside 0000-9999. */
588
+ function toIsoInstant(value) {
589
+ const text = new Date(value).toISOString();
590
+ if (!regExpOf(ISO_INSTANT_PATTERN).test(text)) throw new RangeError(`toIsoInstant: ${text} is outside the years 0000-9999`);
591
+ return text;
592
+ }
593
+ const idSchema = (kind, options = {}) => Type.Unsafe(Type.String({
594
+ ...options,
595
+ pattern: ID_SHAPES[kind].pattern
596
+ }));
597
+ const RunId = idSchema("RunId");
598
+ const AgentId = idSchema("AgentId", { maxLength: 128 });
599
+ const PhaseRunId = idSchema("PhaseRunId", { maxLength: 128 });
600
+ const EventId = idSchema("EventId");
601
+ const CommandId = idSchema("CommandId");
602
+ const ApprovalId = idSchema("ApprovalId");
603
+ const ToolCallId = idSchema("ToolCallId", { maxLength: 128 });
604
+ const EffectId = idSchema("EffectId");
605
+ const ArtifactId = idSchema("ArtifactId");
606
+ const FindingId = idSchema("FindingId");
607
+ const SpecId = idSchema("SpecId");
608
+ const SurfaceId = idSchema("SurfaceId");
609
+ const Sha256 = idSchema("Sha256");
610
+ const IsoInstant = idSchema("IsoInstant", { format: "date-time" });
611
+ //#endregion
612
+ //#region ../../packages/base/src/model.ts
613
+ const ModelCapability = Type.Union([
614
+ Type.Literal("fast"),
615
+ Type.Literal("coding"),
616
+ Type.Literal("reasoning"),
617
+ Type.Literal("vision"),
618
+ Type.Literal("cheap")
619
+ ]);
620
+ /** Spec 10, VERBATIM. Thinking level is NOT part of ModelRef: it travels next to it (SpawnRequest.thinking, tier table). */
621
+ const ModelRef = Type.Object({
622
+ provider: Type.String(),
623
+ model: Type.String(),
624
+ capability: Type.Optional(ModelCapability)
625
+ }, { additionalProperties: false });
626
+ const ThinkingLevel = Type.Union([
627
+ Type.Literal("off"),
628
+ Type.Literal("minimal"),
629
+ Type.Literal("low"),
630
+ Type.Literal("medium"),
631
+ Type.Literal("high"),
632
+ Type.Literal("xhigh")
633
+ ]);
634
+ /** spec 10: exactly these two */
635
+ const AuthMode = Type.Union([Type.Literal("subscription"), Type.Literal("api")]);
636
+ /** A number exists only for a metered leg (authMode 'api'). NEVER 'not_applicable' for a metered leg (I8). */
637
+ const MonetaryCost = Type.Union([Type.Literal("not_applicable"), Type.Object({
638
+ currency: Type.Literal("USD"),
639
+ amount: Type.Number({ minimum: 0 }),
640
+ basis: Type.Union([Type.Literal("catalogue"), Type.Literal("estimate")]),
641
+ priceCatalogVersion: Type.String()
642
+ }, { additionalProperties: false })]);
643
+ //#endregion
644
+ //#region ../../packages/base/src/ports.ts
645
+ const ok = (value) => ({
646
+ ok: true,
647
+ value
648
+ });
649
+ const err = (error) => ({
650
+ ok: false,
651
+ error
652
+ });
653
+ /** The wall clock. `sleep` rejects with `signal.reason` when aborted, and keeps the process alive while it waits. */
654
+ const systemClock = Object.freeze({
655
+ now: () => toIsoInstant(Date.now()),
656
+ monotonicMs: () => performance.now(),
657
+ sleep: (ms, signal) => new Promise((resolve, reject) => {
658
+ if (signal?.aborted) {
659
+ reject(signal.reason);
660
+ return;
661
+ }
662
+ const onAbort = () => {
663
+ clearTimeout(timer);
664
+ reject(signal?.reason);
665
+ };
666
+ const timer = setTimeout(() => {
667
+ signal?.removeEventListener("abort", onAbort);
668
+ resolve();
669
+ }, ms);
670
+ signal?.addEventListener("abort", onAbort, { once: true });
671
+ })
672
+ });
673
+ /** What comes before the underscore of a generated id: `run`, `evt`, `cmd`, `apr`, `eff`, ... */
674
+ const ID_PREFIX_PATTERN = "^[a-z][a-z0-9]{0,15}$";
675
+ const idPrefix = new RegExp(ID_PREFIX_PATTERN);
676
+ /** Shared by every IdSource so that the real one and the test one refuse the same prefixes. */
677
+ function assertIdPrefix(prefix) {
678
+ if (typeof prefix !== "string" || !idPrefix.test(prefix)) throw new TypeError(`IdSource: invalid prefix ${JSON.stringify(prefix)} (expected ${ID_PREFIX_PATTERN})`);
679
+ }
680
+ const TAIL_LIMIT = 1n << 74n;
681
+ const RAND_B_MASK = (1n << 62n) - 1n;
682
+ const VARIANT = 2n << 62n;
683
+ const MAX_TIMESTAMP = 2 ** 48 - 1;
684
+ /**
685
+ * The real IdSource: `<prefix>_<uuidv7 without dashes>`.
686
+ *
687
+ * Not `crypto.randomUUIDv7()`: that one draws all 74 random bits afresh, so two ids of the same millisecond come
688
+ * out in random order (measured on Node 24.21: about half of consecutive pairs are inverted), and `@types/node` 24
689
+ * does not declare it. Ids are event, command and effect keys: they must sort in minting order. This generator is
690
+ * RFC 9562 method 2 (monotonic random): inside one millisecond, or while the wall clock stalls or steps back, the
691
+ * 74-bit tail is INCREMENTED by a random step; when it would overflow, the timestamp moves one millisecond ahead.
692
+ * Every id of one source is therefore strictly greater than the previous one, whatever its prefix.
693
+ */
694
+ function createUuidV7IdSource(options = {}) {
695
+ const now = options.now ?? Date.now;
696
+ const random = options.random ?? ((bytes) => void randomFillSync(bytes));
697
+ const tailBytes = /* @__PURE__ */ new Uint8Array(10);
698
+ const stepBytes = /* @__PURE__ */ new Uint8Array(4);
699
+ let lastTimestamp = -1;
700
+ let tail = 0n;
701
+ const bigintOf = (bytes) => {
702
+ let value = 0n;
703
+ for (const byte of bytes) value = value << 8n | BigInt(byte);
704
+ return value;
705
+ };
706
+ const freshTail = () => {
707
+ random(tailBytes);
708
+ return bigintOf(tailBytes) & TAIL_LIMIT - 1n;
709
+ };
710
+ const nextHex = () => {
711
+ const wallClock = Math.min(Math.max(Math.trunc(now()), 0), MAX_TIMESTAMP);
712
+ if (wallClock > lastTimestamp) {
713
+ lastTimestamp = wallClock;
714
+ tail = freshTail();
715
+ } else {
716
+ random(stepBytes);
717
+ tail += 1n + bigintOf(stepBytes);
718
+ if (tail >= TAIL_LIMIT) {
719
+ lastTimestamp += 1;
720
+ tail = freshTail();
721
+ }
722
+ }
723
+ const randA = tail >> 62n;
724
+ const randB = tail & RAND_B_MASK;
725
+ return lastTimestamp.toString(16).padStart(12, "0") + "7" + randA.toString(16).padStart(3, "0") + (VARIANT | randB).toString(16).padStart(16, "0");
726
+ };
727
+ return { next(prefix) {
728
+ assertIdPrefix(prefix);
729
+ return `${prefix}_${nextHex()}`;
730
+ } };
731
+ }
732
+ const Redaction = Type.Object({
733
+ path: Type.String({ pattern: "^(?:/(?:[^/~]|~[01])*)*$" }),
734
+ reason: Type.Union([
735
+ Type.Literal("secret-value"),
736
+ Type.Literal("secret-pattern"),
737
+ Type.Literal("env-value"),
738
+ Type.Literal("private-key"),
739
+ Type.Literal("sensitive-path"),
740
+ Type.Literal("size")
741
+ ]),
742
+ detector: Type.String(),
743
+ sha256: Type.Optional(Sha256)
744
+ }, { additionalProperties: false });
745
+ //#endregion
746
+ //#region ../../packages/base/src/usage.ts
747
+ const count = () => Type.Integer({ minimum: 0 });
748
+ const TokenUsage = Type.Object({
749
+ input: count(),
750
+ output: count(),
751
+ cacheRead: count(),
752
+ cacheWrite: count(),
753
+ total: count()
754
+ }, { additionalProperties: false });
755
+ const QuotaWindow = Type.Object({
756
+ name: Type.String(),
757
+ usedPercent: Type.Optional(Type.Number({ minimum: 0 })),
758
+ resetsAt: Type.Optional(IsoInstant),
759
+ limitLabel: Type.Optional(Type.String())
760
+ }, { additionalProperties: false });
761
+ const QuotaInfo = Type.Union([Type.Object({ known: Type.Literal(false) }, { additionalProperties: false }), Type.Object({
762
+ known: Type.Literal(true),
763
+ source: Type.Union([Type.Literal("response-headers"), Type.Literal("error")]),
764
+ provider: Type.String(),
765
+ windows: Type.Array(QuotaWindow),
766
+ observedAt: IsoInstant
767
+ }, { additionalProperties: false })]);
768
+ /** Lives in base (not in the protocol vocabulary) because `security` (BudgetReader, 2.6.1) needs it and may not import `protocol`. `protocol` re-exports it. */
769
+ const BudgetCounters = Type.Object({
770
+ tokens: Type.Optional(count()),
771
+ modelRequests: Type.Optional(count()),
772
+ toolCalls: Type.Optional(count()),
773
+ wallClockMs: Type.Optional(Type.Number({ minimum: 0 })),
774
+ retries: Type.Optional(count()),
775
+ fixRounds: Type.Optional(count()),
776
+ contextTokens: Type.Optional(count()),
777
+ concurrentAgents: Type.Optional(count()),
778
+ estimatedQuotaPercent: Type.Optional(Type.Number({ minimum: 0 }))
779
+ }, { additionalProperties: false });
780
+ //#endregion
781
+ export { toIsoInstant as A, ErrorInfo as B, PhaseRunId as C, SurfaceId as D, SpecId as E, EXIT_CODE_BY_CLASS as F, isErrorCode as I, ERROR_CLASSES as L, NotImplemented as M, errorOf as N, ToolCallId as O, toErrorInfo as P, ERROR_CODE_PATTERN as R, IsoInstant as S, Sha256 as T, JsonValueSchema as V, CommandId as _, createUuidV7IdSource as a, FindingId as b, systemClock as c, ModelRef as d, MonetaryCost as f, ArtifactId as g, ApprovalId as h, Redaction as i, CohorteError as j, isSafeId as k, AuthMode as l, AgentId as m, QuotaInfo as n, err as o, ThinkingLevel as p, TokenUsage as r, ok as s, BudgetCounters as t, ModelCapability as u, EffectId as v, RunId as w, ID_PATTERN as x, EventId as y, ErrorClass as z };
782
+
783
+ //# sourceMappingURL=src-Cu3TxNob.mjs.map