groundswell 0.0.3 → 1.0.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 (292) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +26 -9
  3. package/dist/cache/cache-key.d.ts +20 -0
  4. package/dist/cache/cache-key.d.ts.map +1 -1
  5. package/dist/cache/cache-key.js +9 -0
  6. package/dist/cache/cache-key.js.map +1 -1
  7. package/dist/core/agent.d.ts +120 -29
  8. package/dist/core/agent.d.ts.map +1 -1
  9. package/dist/core/agent.js +584 -177
  10. package/dist/core/agent.js.map +1 -1
  11. package/dist/core/mcp-handler.d.ts +63 -5
  12. package/dist/core/mcp-handler.d.ts.map +1 -1
  13. package/dist/core/mcp-handler.js +184 -4
  14. package/dist/core/mcp-handler.js.map +1 -1
  15. package/dist/core/workflow-context.d.ts +6 -2
  16. package/dist/core/workflow-context.d.ts.map +1 -1
  17. package/dist/core/workflow-context.js +99 -4
  18. package/dist/core/workflow-context.js.map +1 -1
  19. package/dist/core/workflow.d.ts +315 -13
  20. package/dist/core/workflow.d.ts.map +1 -1
  21. package/dist/core/workflow.js +552 -30
  22. package/dist/core/workflow.js.map +1 -1
  23. package/dist/debugger/event-replayer.d.ts +422 -0
  24. package/dist/debugger/event-replayer.d.ts.map +1 -0
  25. package/dist/debugger/event-replayer.js +639 -0
  26. package/dist/debugger/event-replayer.js.map +1 -0
  27. package/dist/debugger/tree-debugger.d.ts +170 -1
  28. package/dist/debugger/tree-debugger.d.ts.map +1 -1
  29. package/dist/debugger/tree-debugger.js +423 -1
  30. package/dist/debugger/tree-debugger.js.map +1 -1
  31. package/dist/decorators/step.d.ts.map +1 -1
  32. package/dist/decorators/step.js +129 -47
  33. package/dist/decorators/step.js.map +1 -1
  34. package/dist/harnesses/claude-code-harness.d.ts +391 -0
  35. package/dist/harnesses/claude-code-harness.d.ts.map +1 -0
  36. package/dist/harnesses/claude-code-harness.js +1076 -0
  37. package/dist/harnesses/claude-code-harness.js.map +1 -0
  38. package/dist/harnesses/harness-registry.d.ts +440 -0
  39. package/dist/harnesses/harness-registry.d.ts.map +1 -0
  40. package/dist/harnesses/harness-registry.js +543 -0
  41. package/dist/harnesses/harness-registry.js.map +1 -0
  42. package/dist/harnesses/index.d.ts +12 -0
  43. package/dist/harnesses/index.d.ts.map +1 -0
  44. package/dist/harnesses/index.js +11 -0
  45. package/dist/harnesses/index.js.map +1 -0
  46. package/dist/harnesses/pi-harness.d.ts +219 -0
  47. package/dist/harnesses/pi-harness.d.ts.map +1 -0
  48. package/dist/harnesses/pi-harness.js +676 -0
  49. package/dist/harnesses/pi-harness.js.map +1 -0
  50. package/dist/harnesses/pi-schema-converter.d.ts +24 -0
  51. package/dist/harnesses/pi-schema-converter.d.ts.map +1 -0
  52. package/dist/harnesses/pi-schema-converter.js +81 -0
  53. package/dist/harnesses/pi-schema-converter.js.map +1 -0
  54. package/dist/harnesses/register-defaults.d.ts +24 -0
  55. package/dist/harnesses/register-defaults.d.ts.map +1 -0
  56. package/dist/harnesses/register-defaults.js +40 -0
  57. package/dist/harnesses/register-defaults.js.map +1 -0
  58. package/dist/harnesses/session-store.d.ts +201 -0
  59. package/dist/harnesses/session-store.d.ts.map +1 -0
  60. package/dist/harnesses/session-store.js +254 -0
  61. package/dist/harnesses/session-store.js.map +1 -0
  62. package/dist/index.d.ts +12 -2
  63. package/dist/index.d.ts.map +1 -1
  64. package/dist/index.js +17 -0
  65. package/dist/index.js.map +1 -1
  66. package/dist/reflection/reflection.d.ts.map +1 -1
  67. package/dist/reflection/reflection.js +19 -4
  68. package/dist/reflection/reflection.js.map +1 -1
  69. package/dist/types/agent.d.ts +1253 -2
  70. package/dist/types/agent.d.ts.map +1 -1
  71. package/dist/types/agent.js +418 -1
  72. package/dist/types/agent.js.map +1 -1
  73. package/dist/types/decorators.d.ts +10 -1
  74. package/dist/types/decorators.d.ts.map +1 -1
  75. package/dist/types/events.d.ts +26 -0
  76. package/dist/types/events.d.ts.map +1 -1
  77. package/dist/types/harnesses.d.ts +474 -0
  78. package/dist/types/harnesses.d.ts.map +1 -0
  79. package/dist/types/harnesses.js +2 -0
  80. package/dist/types/harnesses.js.map +1 -0
  81. package/dist/types/index.d.ts +9 -1
  82. package/dist/types/index.d.ts.map +1 -1
  83. package/dist/types/index.js +6 -0
  84. package/dist/types/index.js.map +1 -1
  85. package/dist/types/providers.d.ts +691 -0
  86. package/dist/types/providers.d.ts.map +1 -0
  87. package/dist/types/providers.js +14 -0
  88. package/dist/types/providers.js.map +1 -0
  89. package/dist/types/restart.d.ts +132 -0
  90. package/dist/types/restart.d.ts.map +1 -0
  91. package/dist/types/restart.js +2 -0
  92. package/dist/types/restart.js.map +1 -0
  93. package/dist/types/streaming.d.ts +194 -0
  94. package/dist/types/streaming.d.ts.map +1 -0
  95. package/dist/types/streaming.js +67 -0
  96. package/dist/types/streaming.js.map +1 -0
  97. package/dist/types/workflow-context.d.ts +137 -1
  98. package/dist/types/workflow-context.d.ts.map +1 -1
  99. package/dist/utils/agent-validation.d.ts +88 -0
  100. package/dist/utils/agent-validation.d.ts.map +1 -0
  101. package/dist/utils/agent-validation.js +87 -0
  102. package/dist/utils/agent-validation.js.map +1 -0
  103. package/dist/utils/delay.d.ts +7 -0
  104. package/dist/utils/delay.d.ts.map +1 -0
  105. package/dist/utils/delay.js +9 -0
  106. package/dist/utils/delay.js.map +1 -0
  107. package/dist/utils/harness-config.d.ts +180 -0
  108. package/dist/utils/harness-config.d.ts.map +1 -0
  109. package/dist/utils/harness-config.js +311 -0
  110. package/dist/utils/harness-config.js.map +1 -0
  111. package/dist/utils/index.d.ts +9 -1
  112. package/dist/utils/index.d.ts.map +1 -1
  113. package/dist/utils/index.js +8 -1
  114. package/dist/utils/index.js.map +1 -1
  115. package/dist/utils/model-spec.d.ts +110 -0
  116. package/dist/utils/model-spec.d.ts.map +1 -0
  117. package/dist/utils/model-spec.js +149 -0
  118. package/dist/utils/model-spec.js.map +1 -0
  119. package/dist/utils/provider-config.d.ts +10 -0
  120. package/dist/utils/provider-config.d.ts.map +1 -0
  121. package/dist/utils/provider-config.js +10 -0
  122. package/dist/utils/provider-config.js.map +1 -0
  123. package/dist/utils/restart-analysis.d.ts +202 -0
  124. package/dist/utils/restart-analysis.d.ts.map +1 -0
  125. package/dist/utils/restart-analysis.js +426 -0
  126. package/dist/utils/restart-analysis.js.map +1 -0
  127. package/dist/utils/session-serialization.d.ts +118 -0
  128. package/dist/utils/session-serialization.d.ts.map +1 -0
  129. package/dist/utils/session-serialization.js +217 -0
  130. package/dist/utils/session-serialization.js.map +1 -0
  131. package/package.json +31 -5
  132. package/CHANGELOG.md +0 -188
  133. package/dist/__tests__/adversarial/attachChild-performance.test.d.ts +0 -16
  134. package/dist/__tests__/adversarial/attachChild-performance.test.d.ts.map +0 -1
  135. package/dist/__tests__/adversarial/attachChild-performance.test.js +0 -187
  136. package/dist/__tests__/adversarial/attachChild-performance.test.js.map +0 -1
  137. package/dist/__tests__/adversarial/circular-reference.test.d.ts +0 -13
  138. package/dist/__tests__/adversarial/circular-reference.test.d.ts.map +0 -1
  139. package/dist/__tests__/adversarial/circular-reference.test.js +0 -92
  140. package/dist/__tests__/adversarial/circular-reference.test.js.map +0 -1
  141. package/dist/__tests__/adversarial/complex-circular-reference.test.d.ts +0 -16
  142. package/dist/__tests__/adversarial/complex-circular-reference.test.d.ts.map +0 -1
  143. package/dist/__tests__/adversarial/complex-circular-reference.test.js +0 -127
  144. package/dist/__tests__/adversarial/complex-circular-reference.test.js.map +0 -1
  145. package/dist/__tests__/adversarial/concurrent-task-failures.test.d.ts +0 -21
  146. package/dist/__tests__/adversarial/concurrent-task-failures.test.d.ts.map +0 -1
  147. package/dist/__tests__/adversarial/concurrent-task-failures.test.js +0 -667
  148. package/dist/__tests__/adversarial/concurrent-task-failures.test.js.map +0 -1
  149. package/dist/__tests__/adversarial/deep-analysis.test.d.ts +0 -6
  150. package/dist/__tests__/adversarial/deep-analysis.test.d.ts.map +0 -1
  151. package/dist/__tests__/adversarial/deep-analysis.test.js +0 -877
  152. package/dist/__tests__/adversarial/deep-analysis.test.js.map +0 -1
  153. package/dist/__tests__/adversarial/deep-hierarchy-stress.test.d.ts +0 -13
  154. package/dist/__tests__/adversarial/deep-hierarchy-stress.test.d.ts.map +0 -1
  155. package/dist/__tests__/adversarial/deep-hierarchy-stress.test.js +0 -186
  156. package/dist/__tests__/adversarial/deep-hierarchy-stress.test.js.map +0 -1
  157. package/dist/__tests__/adversarial/e2e-prd-validation.test.d.ts +0 -6
  158. package/dist/__tests__/adversarial/e2e-prd-validation.test.d.ts.map +0 -1
  159. package/dist/__tests__/adversarial/e2e-prd-validation.test.js +0 -626
  160. package/dist/__tests__/adversarial/e2e-prd-validation.test.js.map +0 -1
  161. package/dist/__tests__/adversarial/edge-case.test.d.ts +0 -6
  162. package/dist/__tests__/adversarial/edge-case.test.d.ts.map +0 -1
  163. package/dist/__tests__/adversarial/edge-case.test.js +0 -857
  164. package/dist/__tests__/adversarial/edge-case.test.js.map +0 -1
  165. package/dist/__tests__/adversarial/error-merge-strategy.test.d.ts +0 -20
  166. package/dist/__tests__/adversarial/error-merge-strategy.test.d.ts.map +0 -1
  167. package/dist/__tests__/adversarial/error-merge-strategy.test.js +0 -907
  168. package/dist/__tests__/adversarial/error-merge-strategy.test.js.map +0 -1
  169. package/dist/__tests__/adversarial/incremental-performance.test.d.ts +0 -2
  170. package/dist/__tests__/adversarial/incremental-performance.test.d.ts.map +0 -1
  171. package/dist/__tests__/adversarial/incremental-performance.test.js +0 -113
  172. package/dist/__tests__/adversarial/incremental-performance.test.js.map +0 -1
  173. package/dist/__tests__/adversarial/node-map-update-benchmarks.test.d.ts +0 -22
  174. package/dist/__tests__/adversarial/node-map-update-benchmarks.test.d.ts.map +0 -1
  175. package/dist/__tests__/adversarial/node-map-update-benchmarks.test.js +0 -383
  176. package/dist/__tests__/adversarial/node-map-update-benchmarks.test.js.map +0 -1
  177. package/dist/__tests__/adversarial/observer-propagation.test.d.ts +0 -21
  178. package/dist/__tests__/adversarial/observer-propagation.test.d.ts.map +0 -1
  179. package/dist/__tests__/adversarial/observer-propagation.test.js +0 -404
  180. package/dist/__tests__/adversarial/observer-propagation.test.js.map +0 -1
  181. package/dist/__tests__/adversarial/parent-validation.test.d.ts +0 -13
  182. package/dist/__tests__/adversarial/parent-validation.test.d.ts.map +0 -1
  183. package/dist/__tests__/adversarial/parent-validation.test.js +0 -128
  184. package/dist/__tests__/adversarial/parent-validation.test.js.map +0 -1
  185. package/dist/__tests__/adversarial/prd-12-2-compliance.test.d.ts +0 -20
  186. package/dist/__tests__/adversarial/prd-12-2-compliance.test.d.ts.map +0 -1
  187. package/dist/__tests__/adversarial/prd-12-2-compliance.test.js +0 -482
  188. package/dist/__tests__/adversarial/prd-12-2-compliance.test.js.map +0 -1
  189. package/dist/__tests__/adversarial/prd-compliance.test.d.ts +0 -6
  190. package/dist/__tests__/adversarial/prd-compliance.test.d.ts.map +0 -1
  191. package/dist/__tests__/adversarial/prd-compliance.test.js +0 -886
  192. package/dist/__tests__/adversarial/prd-compliance.test.js.map +0 -1
  193. package/dist/__tests__/compatibility/backward-compatibility.test.d.ts +0 -22
  194. package/dist/__tests__/compatibility/backward-compatibility.test.d.ts.map +0 -1
  195. package/dist/__tests__/compatibility/backward-compatibility.test.js +0 -1843
  196. package/dist/__tests__/compatibility/backward-compatibility.test.js.map +0 -1
  197. package/dist/__tests__/helpers/index.d.ts +0 -10
  198. package/dist/__tests__/helpers/index.d.ts.map +0 -1
  199. package/dist/__tests__/helpers/index.js +0 -10
  200. package/dist/__tests__/helpers/index.js.map +0 -1
  201. package/dist/__tests__/helpers/tree-verification.d.ts +0 -90
  202. package/dist/__tests__/helpers/tree-verification.d.ts.map +0 -1
  203. package/dist/__tests__/helpers/tree-verification.js +0 -202
  204. package/dist/__tests__/helpers/tree-verification.js.map +0 -1
  205. package/dist/__tests__/integration/agent-workflow.test.d.ts +0 -2
  206. package/dist/__tests__/integration/agent-workflow.test.d.ts.map +0 -1
  207. package/dist/__tests__/integration/agent-workflow.test.js +0 -256
  208. package/dist/__tests__/integration/agent-workflow.test.js.map +0 -1
  209. package/dist/__tests__/integration/bidirectional-consistency.test.d.ts +0 -14
  210. package/dist/__tests__/integration/bidirectional-consistency.test.d.ts.map +0 -1
  211. package/dist/__tests__/integration/bidirectional-consistency.test.js +0 -668
  212. package/dist/__tests__/integration/bidirectional-consistency.test.js.map +0 -1
  213. package/dist/__tests__/integration/observer-logging.test.d.ts +0 -2
  214. package/dist/__tests__/integration/observer-logging.test.d.ts.map +0 -1
  215. package/dist/__tests__/integration/observer-logging.test.js +0 -517
  216. package/dist/__tests__/integration/observer-logging.test.js.map +0 -1
  217. package/dist/__tests__/integration/tree-mirroring.test.d.ts +0 -2
  218. package/dist/__tests__/integration/tree-mirroring.test.d.ts.map +0 -1
  219. package/dist/__tests__/integration/tree-mirroring.test.js +0 -117
  220. package/dist/__tests__/integration/tree-mirroring.test.js.map +0 -1
  221. package/dist/__tests__/integration/workflow-reparenting.test.d.ts +0 -12
  222. package/dist/__tests__/integration/workflow-reparenting.test.d.ts.map +0 -1
  223. package/dist/__tests__/integration/workflow-reparenting.test.js +0 -239
  224. package/dist/__tests__/integration/workflow-reparenting.test.js.map +0 -1
  225. package/dist/__tests__/unit/agent.test.d.ts +0 -2
  226. package/dist/__tests__/unit/agent.test.d.ts.map +0 -1
  227. package/dist/__tests__/unit/agent.test.js +0 -143
  228. package/dist/__tests__/unit/agent.test.js.map +0 -1
  229. package/dist/__tests__/unit/cache-key.test.d.ts +0 -5
  230. package/dist/__tests__/unit/cache-key.test.d.ts.map +0 -1
  231. package/dist/__tests__/unit/cache-key.test.js +0 -145
  232. package/dist/__tests__/unit/cache-key.test.js.map +0 -1
  233. package/dist/__tests__/unit/cache.test.d.ts +0 -5
  234. package/dist/__tests__/unit/cache.test.d.ts.map +0 -1
  235. package/dist/__tests__/unit/cache.test.js +0 -132
  236. package/dist/__tests__/unit/cache.test.js.map +0 -1
  237. package/dist/__tests__/unit/context.test.d.ts +0 -2
  238. package/dist/__tests__/unit/context.test.d.ts.map +0 -1
  239. package/dist/__tests__/unit/context.test.js +0 -220
  240. package/dist/__tests__/unit/context.test.js.map +0 -1
  241. package/dist/__tests__/unit/decorators.test.d.ts +0 -2
  242. package/dist/__tests__/unit/decorators.test.d.ts.map +0 -1
  243. package/dist/__tests__/unit/decorators.test.js +0 -162
  244. package/dist/__tests__/unit/decorators.test.js.map +0 -1
  245. package/dist/__tests__/unit/introspection-tools.test.d.ts +0 -5
  246. package/dist/__tests__/unit/introspection-tools.test.d.ts.map +0 -1
  247. package/dist/__tests__/unit/introspection-tools.test.js +0 -191
  248. package/dist/__tests__/unit/introspection-tools.test.js.map +0 -1
  249. package/dist/__tests__/unit/logger.test.d.ts +0 -2
  250. package/dist/__tests__/unit/logger.test.d.ts.map +0 -1
  251. package/dist/__tests__/unit/logger.test.js +0 -241
  252. package/dist/__tests__/unit/logger.test.js.map +0 -1
  253. package/dist/__tests__/unit/observable.test.d.ts +0 -2
  254. package/dist/__tests__/unit/observable.test.d.ts.map +0 -1
  255. package/dist/__tests__/unit/observable.test.js +0 -251
  256. package/dist/__tests__/unit/observable.test.js.map +0 -1
  257. package/dist/__tests__/unit/prompt.test.d.ts +0 -2
  258. package/dist/__tests__/unit/prompt.test.d.ts.map +0 -1
  259. package/dist/__tests__/unit/prompt.test.js +0 -113
  260. package/dist/__tests__/unit/prompt.test.js.map +0 -1
  261. package/dist/__tests__/unit/reflection.test.d.ts +0 -5
  262. package/dist/__tests__/unit/reflection.test.d.ts.map +0 -1
  263. package/dist/__tests__/unit/reflection.test.js +0 -160
  264. package/dist/__tests__/unit/reflection.test.js.map +0 -1
  265. package/dist/__tests__/unit/tree-debugger-incremental.test.d.ts +0 -2
  266. package/dist/__tests__/unit/tree-debugger-incremental.test.d.ts.map +0 -1
  267. package/dist/__tests__/unit/tree-debugger-incremental.test.js +0 -136
  268. package/dist/__tests__/unit/tree-debugger-incremental.test.js.map +0 -1
  269. package/dist/__tests__/unit/tree-debugger.test.d.ts +0 -2
  270. package/dist/__tests__/unit/tree-debugger.test.d.ts.map +0 -1
  271. package/dist/__tests__/unit/tree-debugger.test.js +0 -69
  272. package/dist/__tests__/unit/tree-debugger.test.js.map +0 -1
  273. package/dist/__tests__/unit/utils/workflow-error-utils.test.d.ts +0 -2
  274. package/dist/__tests__/unit/utils/workflow-error-utils.test.d.ts.map +0 -1
  275. package/dist/__tests__/unit/utils/workflow-error-utils.test.js +0 -154
  276. package/dist/__tests__/unit/utils/workflow-error-utils.test.js.map +0 -1
  277. package/dist/__tests__/unit/workflow-detachChild.test.d.ts +0 -2
  278. package/dist/__tests__/unit/workflow-detachChild.test.d.ts.map +0 -1
  279. package/dist/__tests__/unit/workflow-detachChild.test.js +0 -76
  280. package/dist/__tests__/unit/workflow-detachChild.test.js.map +0 -1
  281. package/dist/__tests__/unit/workflow-emitEvent-childDetached.test.d.ts +0 -2
  282. package/dist/__tests__/unit/workflow-emitEvent-childDetached.test.d.ts.map +0 -1
  283. package/dist/__tests__/unit/workflow-emitEvent-childDetached.test.js +0 -122
  284. package/dist/__tests__/unit/workflow-emitEvent-childDetached.test.js.map +0 -1
  285. package/dist/__tests__/unit/workflow-isDescendantOf.test.d.ts +0 -2
  286. package/dist/__tests__/unit/workflow-isDescendantOf.test.d.ts.map +0 -1
  287. package/dist/__tests__/unit/workflow-isDescendantOf.test.js +0 -140
  288. package/dist/__tests__/unit/workflow-isDescendantOf.test.js.map +0 -1
  289. package/dist/__tests__/unit/workflow.test.d.ts +0 -2
  290. package/dist/__tests__/unit/workflow.test.d.ts.map +0 -1
  291. package/dist/__tests__/unit/workflow.test.js +0 -330
  292. package/dist/__tests__/unit/workflow.test.js.map +0 -1
@@ -0,0 +1,311 @@
1
+ /**
2
+ * Harness configuration cascade utilities (PRD §7.6 / §7.7).
3
+ *
4
+ * ## Dual-Cascade Architecture
5
+ *
6
+ * This module provides the v1.2 canonical harness-configuration functions that operate
7
+ * on the `GlobalHarnessConfig` / `HarnessId` / `HarnessOptions` types. It also contains
8
+ * deprecated legacy aliases (`configureProviders` / `getGlobalProviderConfig` /
9
+ * `resolveProviderConfig` / `resetGlobalConfig`) that preserve backward compatibility
10
+ * with existing consumers (`agent.ts`, legacy test files) during the migration.
11
+ *
12
+ * ## Dual-Singleton Design (Scope Decision)
13
+ *
14
+ * The legacy and harness functions validate **disjoint id sets**:
15
+ * - `configureHarnesses` accepts only `'pi' | 'claude-code'`
16
+ * - `configureProviders` accepts the full `ProviderId` superset (including `'anthropic'`)
17
+ *
18
+ * Existing consumers (agent.ts, 4 integration/override test files) pass `'anthropic'`
19
+ * through `configureProviders`. Making it delegate to `configureHarnesses` would throw and break
20
+ * those consumers, which are out of scope for this task (owned by P3.M1 / P4.M1).
21
+ *
22
+ * Therefore, **`configureProviders` keeps its own module-private singleton** with permissive
23
+ * validation, while `resolveProviderConfig` safely delegates to `resolveHarnessConfig` (which
24
+ * performs NO id validation — it treats ids as opaque string keys).
25
+ *
26
+ * Once P3.M1 rewires agent.ts to read the harness path and P4.M1 removes the legacy literals,
27
+ * the legacy singleton can be deleted and the aliases collapse to true delegation.
28
+ *
29
+ * @see {@link GlobalHarnessConfig} in types/harnesses.ts
30
+ * @see {@link GlobalProviderConfig} in types/providers.ts
31
+ */
32
+ // ============================================================================
33
+ // Harness Cascade — Module-Private Storage
34
+ // ============================================================================
35
+ /**
36
+ * Module-private harness configuration singleton.
37
+ *
38
+ * Not exported — access via `getGlobalHarnessConfig()` / `configureHarnesses()`.
39
+ * @internal
40
+ */
41
+ let globalHarnessConfig = null;
42
+ /**
43
+ * Default harness configuration (PRD §7.1 — `pi` is the vendor-neutral default).
44
+ * @internal
45
+ */
46
+ const DEFAULT_HARNESS_CONFIG = {
47
+ defaultHarness: 'pi',
48
+ };
49
+ // ============================================================================
50
+ // Harness Cascade — Validation Helpers
51
+ // ============================================================================
52
+ /**
53
+ * Type guard: checks if a string is a valid `HarnessId` ('pi' | 'claude-code').
54
+ */
55
+ function isValidHarnessId(value) {
56
+ return value === 'pi' || value === 'claude-code';
57
+ }
58
+ /**
59
+ * Formatted list of supported harnesses for error messages.
60
+ */
61
+ function getSupportedHarnessesList() {
62
+ return '"pi", "claude-code"';
63
+ }
64
+ // ============================================================================
65
+ // Harness Cascade — Public API
66
+ // ============================================================================
67
+ /**
68
+ * Configure the global harness settings.
69
+ *
70
+ * Validates and stores the harness configuration. Should be called once at
71
+ * application startup. The harness cascade (PRD §7.7) uses this as the lowest
72
+ * priority layer — agent and prompt overrides take precedence.
73
+ *
74
+ * ## Validation
75
+ *
76
+ * - `defaultHarness` must be `'pi'` or `'claude-code'`
77
+ * - `harnessDefaults` keys (if present) must be valid `HarnessId` values
78
+ * - `defaultModelProvider` is an **open set** (any string) — NOT validated (PRD §7.8)
79
+ *
80
+ * @param config - Global harness configuration
81
+ * @throws {Error} If `defaultHarness` is invalid
82
+ * @throws {Error} If `harnessDefaults` contains invalid harness IDs
83
+ *
84
+ * @example
85
+ * ```ts
86
+ * import { configureHarnesses } from 'groundswell';
87
+ *
88
+ * configureHarnesses({
89
+ * defaultHarness: 'pi',
90
+ * defaultModelProvider: 'anthropic',
91
+ * harnessDefaults: {
92
+ * 'claude-code': { apiKey: process.env.ANTHROPIC_API_KEY },
93
+ * },
94
+ * });
95
+ * ```
96
+ */
97
+ export function configureHarnesses(config) {
98
+ // Validate defaultHarness
99
+ if (!isValidHarnessId(config.defaultHarness)) {
100
+ throw new Error(`Invalid default harness: "${config.defaultHarness}". ` +
101
+ `Supported harnesses: ${getSupportedHarnessesList()}`);
102
+ }
103
+ // Validate harnessDefaults keys (if present)
104
+ if (config.harnessDefaults) {
105
+ for (const id of Object.keys(config.harnessDefaults)) {
106
+ if (!isValidHarnessId(id)) {
107
+ throw new Error(`Invalid harness in harnessDefaults: "${id}". ` +
108
+ `Supported harnesses: ${getSupportedHarnessesList()}`);
109
+ }
110
+ }
111
+ }
112
+ // Store configuration (defaultModelProvider is open set — no validation)
113
+ globalHarnessConfig = config;
114
+ }
115
+ /**
116
+ * Get the current global harness configuration.
117
+ *
118
+ * Returns the configured value if `configureHarnesses()` was called, otherwise
119
+ * returns the default configuration. **Never returns null.**
120
+ *
121
+ * @returns Current global harness configuration
122
+ *
123
+ * @example
124
+ * ```ts
125
+ * const config = getGlobalHarnessConfig();
126
+ * console.log(config.defaultHarness); // 'pi' (default)
127
+ * ```
128
+ */
129
+ export function getGlobalHarnessConfig() {
130
+ return globalHarnessConfig ?? DEFAULT_HARNESS_CONFIG;
131
+ }
132
+ /**
133
+ * Resolve harness configuration via the PRD §7.7 cascade.
134
+ *
135
+ * A **pure function** — takes `globalConfig` as a parameter and does NOT read
136
+ * the singleton. This purity enables both `resolveHarnessConfig` and the legacy
137
+ * `resolveProviderConfig` (which delegates to this function) to work correctly.
138
+ *
139
+ * ## Provider Resolution (nullish coalescing — prompt wins)
140
+ *
141
+ * ```ts
142
+ * const harness = promptHarness ?? agentHarness ?? globalConfig.defaultHarness;
143
+ * ```
144
+ *
145
+ * ## Options Merge (object spread — last write wins)
146
+ *
147
+ * ```ts
148
+ * const options = {
149
+ * ...globalConfig.harnessDefaults?.[harness], // base
150
+ * ...agentOptions, // middle
151
+ * ...promptOptions, // top
152
+ * };
153
+ * ```
154
+ *
155
+ * @param globalConfig - Global harness configuration
156
+ * @param agentHarness - Agent-level harness override (optional)
157
+ * @param agentOptions - Agent-level options override (optional)
158
+ * @param promptHarness - Prompt-level harness override (optional)
159
+ * @param promptOptions - Prompt-level options override (optional)
160
+ * @returns Resolved harness and merged options
161
+ */
162
+ export function resolveHarnessConfig(globalConfig, agentHarness, agentOptions, promptHarness, promptOptions) {
163
+ // Step 1: Resolve harness — prompt wins (first-defined-wins via ??)
164
+ const harness = promptHarness ?? agentHarness ?? globalConfig.defaultHarness;
165
+ // Step 2: Get global defaults for the resolved harness
166
+ const globalDefaults = globalConfig.harnessDefaults?.[harness];
167
+ // Step 3: Merge options — last write wins
168
+ const options = {
169
+ ...(globalDefaults ?? {}),
170
+ ...(agentOptions ?? {}),
171
+ ...(promptOptions ?? {}),
172
+ };
173
+ return { harness, options };
174
+ }
175
+ /**
176
+ * Reset the global harness configuration to defaults.
177
+ *
178
+ * **FOR TESTING PURPOSES ONLY.**
179
+ *
180
+ * @internal
181
+ */
182
+ export function resetGlobalHarnessConfig() {
183
+ globalHarnessConfig = null;
184
+ }
185
+ // ============================================================================
186
+ // Deprecated Legacy Aliases — Module-Private Storage (Separate Singleton)
187
+ // ============================================================================
188
+ /**
189
+ * Module-private LEGACY provider configuration singleton.
190
+ *
191
+ * **Separate from `globalHarnessConfig`** — the two validate disjoint id sets
192
+ * (see module-level Scope Decision). This singleton is owned by the deprecated
193
+ * `configureProviders` / `getGlobalProviderConfig` / `resetGlobalConfig` functions.
194
+ * @internal
195
+ */
196
+ let globalProviderConfig = null;
197
+ /**
198
+ * Default LEGACY provider configuration.
199
+ * @internal
200
+ */
201
+ const DEFAULT_PROVIDER_CONFIG = {
202
+ defaultProvider: 'anthropic',
203
+ providerDefaults: undefined,
204
+ };
205
+ // ============================================================================
206
+ // Deprecated Legacy Aliases — Validation Helpers
207
+ // ============================================================================
208
+ /**
209
+ * Type guard: checks if a string is a valid `ProviderId`.
210
+ *
211
+ * Accepts 'anthropic' (legacy), 'pi', and 'claude-code'.
212
+ */
213
+ function isValidProviderId(value) {
214
+ return value === 'anthropic' || value === 'pi' || value === 'claude-code';
215
+ }
216
+ /**
217
+ * Formatted list of supported providers for LEGACY error messages.
218
+ */
219
+ function getSupportedProvidersList() {
220
+ return '"anthropic", "pi", "claude-code"';
221
+ }
222
+ // ============================================================================
223
+ // Deprecated Legacy Aliases — Public API
224
+ // ============================================================================
225
+ /**
226
+ * Configure global provider settings.
227
+ *
228
+ * @deprecated Since v1.2. Use {@link configureHarnesses}.
229
+ *
230
+ * This function keeps its **own module-private singleton** (`globalProviderConfig`)
231
+ * with permissive `ProviderId` validation. It does NOT delegate to
232
+ * `configureHarnesses` because the harness validator rejects `'anthropic'`,
233
+ * which existing consumers still pass.
234
+ *
235
+ * @param config - Global provider configuration
236
+ * @throws {Error} If `defaultProvider` is invalid
237
+ * @throws {Error} If `providerDefaults` contains invalid provider IDs
238
+ */
239
+ export function configureProviders(config) {
240
+ // Step 1: Validate defaultProvider
241
+ if (!isValidProviderId(config.defaultProvider)) {
242
+ throw new Error(`Invalid default provider: "${config.defaultProvider}". ` +
243
+ `Supported providers: ${getSupportedProvidersList()}`);
244
+ }
245
+ // Step 2: Validate providerDefaults keys (if present)
246
+ if (config.providerDefaults) {
247
+ for (const providerId of Object.keys(config.providerDefaults)) {
248
+ if (!isValidProviderId(providerId)) {
249
+ throw new Error(`Invalid provider in providerDefaults: "${providerId}". ` +
250
+ `Supported providers: ${getSupportedProvidersList()}`);
251
+ }
252
+ }
253
+ }
254
+ // Step 3: Store configuration in the LEGACY singleton (NOT configureHarnesses)
255
+ globalProviderConfig = config;
256
+ }
257
+ /**
258
+ * Get the current global provider configuration.
259
+ *
260
+ * @deprecated Since v1.2. Use {@link getGlobalHarnessConfig}.
261
+ *
262
+ * Reads the legacy `globalProviderConfig` singleton. Returns the configured value
263
+ * if `configureProviders()` was called, otherwise the legacy default
264
+ * (`defaultProvider: 'anthropic'`). **Never returns null.**
265
+ *
266
+ * @returns Current global provider configuration
267
+ */
268
+ export function getGlobalProviderConfig() {
269
+ return globalProviderConfig ?? DEFAULT_PROVIDER_CONFIG;
270
+ }
271
+ /**
272
+ * Resolve provider configuration via the cascade.
273
+ *
274
+ * @deprecated Since v1.2. Use {@link resolveHarnessConfig}.
275
+ *
276
+ * **Delegates to {@link resolveHarnessConfig}** — the cascade algorithm is shared.
277
+ * Translates the `GlobalProviderConfig` shape to `GlobalHarnessConfig` (field renames),
278
+ * calls `resolveHarnessConfig` (which performs NO id validation — safe for legacy literals),
279
+ * and maps the result back to `{ provider, options }`.
280
+ *
281
+ * @param globalConfig - Global provider configuration from configureProviders()
282
+ * @param agentProvider - Agent-level provider override (optional)
283
+ * @param agentOptions - Agent-level options override (optional)
284
+ * @param promptProvider - Prompt-level provider override (optional)
285
+ * @param promptOptions - Prompt-level options override (optional)
286
+ * @returns Resolved provider and merged options
287
+ */
288
+ export function resolveProviderConfig(globalConfig, agentProvider, agentOptions, promptProvider, promptOptions) {
289
+ // Translate GlobalProviderConfig → GlobalHarnessConfig shape (unsound but runtime-safe casts)
290
+ const harnessGlobal = {
291
+ defaultHarness: globalConfig.defaultProvider,
292
+ harnessDefaults: globalConfig.providerDefaults,
293
+ };
294
+ // Delegate to resolveHarnessConfig (pure function — no singleton coupling)
295
+ const { harness, options } = resolveHarnessConfig(harnessGlobal, agentProvider, agentOptions, promptProvider, promptOptions);
296
+ // Map back to legacy shape
297
+ return { provider: harness, options: options };
298
+ }
299
+ /**
300
+ * Reset global configuration to defaults.
301
+ *
302
+ * @deprecated Since v1.2. Use {@link resetGlobalHarnessConfig}.
303
+ *
304
+ * **FOR TESTING PURPOSES ONLY.**
305
+ *
306
+ * @internal
307
+ */
308
+ export function resetGlobalConfig() {
309
+ globalProviderConfig = null;
310
+ }
311
+ //# sourceMappingURL=harness-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harness-config.js","sourceRoot":"","sources":["../../src/utils/harness-config.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,13 @@
1
+ export { analyzeErrorForRestart, TRANSIENT_ERROR_CODES, TRANSIENT_ERROR_SET } from './restart-analysis.js';
2
+ export { validateAgentResponse } from './agent-validation.js';
3
+ export { configureProviders } from './provider-config.js';
4
+ export { configureHarnesses, getGlobalHarnessConfig, resolveHarnessConfig, resetGlobalHarnessConfig } from './harness-config.js';
5
+ export { delay } from './delay.js';
1
6
  export { generateId } from './id.js';
2
- export { Observable } from './observable.js';
3
7
  export { mergeWorkflowErrors } from './workflow-error-utils.js';
8
+ export { parseModelSpec, formatModelForProvider } from './model-spec.js';
9
+ export { Observable } from './observable.js';
4
10
  export type { Subscription, Observer } from './observable.js';
11
+ export type { ValidationResult } from './agent-validation.js';
12
+ export { serializeSession, deserializeSession, SessionSerializationError } from './session-serialization.js';
5
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,YAAY,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC3G,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AACjI,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACzE,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,YAAY,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC9D,YAAY,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC"}
@@ -1,4 +1,11 @@
1
+ export { analyzeErrorForRestart, TRANSIENT_ERROR_CODES, TRANSIENT_ERROR_SET } from './restart-analysis.js';
2
+ export { validateAgentResponse } from './agent-validation.js';
3
+ export { configureProviders } from './provider-config.js';
4
+ export { configureHarnesses, getGlobalHarnessConfig, resolveHarnessConfig, resetGlobalHarnessConfig } from './harness-config.js';
5
+ export { delay } from './delay.js';
1
6
  export { generateId } from './id.js';
2
- export { Observable } from './observable.js';
3
7
  export { mergeWorkflowErrors } from './workflow-error-utils.js';
8
+ export { parseModelSpec, formatModelForProvider } from './model-spec.js';
9
+ export { Observable } from './observable.js';
10
+ export { serializeSession, deserializeSession, SessionSerializationError } from './session-serialization.js';
4
11
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC3G,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AACjI,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACzE,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAG7C,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC"}
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Model specification parsing utility
3
+ *
4
+ * Provides parsing and validation of model specification strings
5
+ * per PRD §7.8 (Model & Provider Specification).
6
+ *
7
+ * The provider axis is an OPEN set (PRD §7.8): any non-empty provider string
8
+ * is valid. The harness (pi | claude-code) is NEVER part of the model string —
9
+ * harness-qualified strings (e.g. `pi/anthropic/x`) are rejected.
10
+ */
11
+ import type { ModelSpec, ModelProviderId } from '../types/harnesses.js';
12
+ /**
13
+ * Parse a model specification string into a ModelSpec object
14
+ *
15
+ * ## Supported Formats
16
+ *
17
+ * ### Qualified Format (provider/model)
18
+ * Explicit provider specification with "/" separator.
19
+ * - Input: `"anthropic/claude-3-5-sonnet"`
20
+ * - Output: `{ provider: 'anthropic', model: 'claude-3-5-sonnet', raw: 'anthropic/claude-3-5-sonnet' }`
21
+ *
22
+ * ### Plain Format (model only)
23
+ * Uses default provider when no provider specified.
24
+ * - Input: `"claude-sonnet-4"` with defaultProvider: `'anthropic'`
25
+ * - Output: `{ provider: 'anthropic', model: 'claude-sonnet-4', raw: 'claude-sonnet-4' }`
26
+ *
27
+ * ## Validation Rules
28
+ *
29
+ * 1. Input cannot be empty or whitespace-only
30
+ * 2. Provider must be a non-empty string (open set — any LLM provider accepted)
31
+ * 3. Model name cannot be empty after provider split
32
+ * 4. Harness-qualified strings (3+ segments, e.g. `pi/anthropic/x`) are REJECTED
33
+ * (PRD §7.8 critical rule: the harness must never appear in the model string)
34
+ * 5. Input is trimmed before parsing, original preserved in `raw` field
35
+ *
36
+ * @param model - Model specification string to parse
37
+ * @param defaultProvider - Default provider to use when none specified (default: 'anthropic')
38
+ * @returns Parsed ModelSpec object with provider, model, and raw string
39
+ * @throws {Error} When model specification is invalid:
40
+ * - Empty or whitespace-only input
41
+ * - Empty provider or model parts
42
+ * - Harness-qualified strings (3+ segments)
43
+ *
44
+ * @example
45
+ * ```ts
46
+ * // Qualified format with known provider
47
+ * const spec1 = parseModelSpec('anthropic/claude-3-5-sonnet');
48
+ * // Returns: { provider: 'anthropic', model: 'claude-3-5-sonnet', raw: 'anthropic/claude-3-5-sonnet' }
49
+ *
50
+ * // Qualified format with custom provider (open set)
51
+ * const spec2 = parseModelSpec('openai/gpt-4o');
52
+ * // Returns: { provider: 'openai', model: 'gpt-4o', raw: 'openai/gpt-4o' }
53
+ *
54
+ * // Plain format — resolved against default provider
55
+ * const spec3 = parseModelSpec('claude-sonnet-4');
56
+ * // Returns: { provider: 'anthropic', model: 'claude-sonnet-4', raw: 'claude-sonnet-4' }
57
+ *
58
+ * // Harness-qualified — REJECTED (PRD §7.8 critical rule)
59
+ * parseModelSpec('pi/anthropic/claude-sonnet-4');
60
+ * // Throws: "Harness must not appear in model string …"
61
+ * ```
62
+ *
63
+ * @see {@link ModelSpec} for the return type structure
64
+ * @see {@link ModelProviderId} for the open provider set
65
+ */
66
+ export declare function parseModelSpec(model: string, defaultProvider?: ModelProviderId): ModelSpec;
67
+ /**
68
+ * Format a ModelSpec for a specific target provider
69
+ *
70
+ * ## Behavior
71
+ *
72
+ * ### Same Provider (Pass-Through)
73
+ * When `spec.provider` matches `targetProvider`, returns the model name only.
74
+ *
75
+ * **Example:**
76
+ * - Input: `{ provider: 'anthropic', model: 'claude-3-5-sonnet', raw: 'anthropic/claude-3-5-sonnet' }`, `'anthropic'`
77
+ * - Output: `"claude-3-5-sonnet"`
78
+ *
79
+ * ### Different Providers (Error)
80
+ * When providers differ, throws an error. Cross-provider model translation
81
+ * is not supported in the MVP.
82
+ *
83
+ * @param spec - ModelSpec from parseModelSpec() or Provider.normalizeModel()
84
+ * @param targetProvider - The provider to format the model for
85
+ * @returns Formatted model string for target provider (model name only)
86
+ * @throws {Error} When providers differ with message:
87
+ * "Cannot translate {source}/{model} to {target} provider. Cross-provider model translation is not supported."
88
+ *
89
+ * @example
90
+ * ```ts
91
+ * // Same provider: pass-through
92
+ * const spec = parseModelSpec('anthropic/claude-3-5-sonnet');
93
+ * const model = formatModelForProvider(spec, 'anthropic');
94
+ * console.log(model); // "claude-3-5-sonnet"
95
+ *
96
+ * // Different provider: error
97
+ * try {
98
+ * formatModelForProvider(spec, 'openai');
99
+ * } catch (error) {
100
+ * console.error((error as Error).message);
101
+ * // "Cannot translate anthropic/claude-3-5-sonnet to openai provider. ..."
102
+ * }
103
+ * ```
104
+ *
105
+ * @see {@link parseModelSpec} for creating ModelSpec objects
106
+ * @see {@link ModelSpec} for the input type structure
107
+ * @see {@link ModelProviderId} for the open provider set
108
+ */
109
+ export declare function formatModelForProvider(spec: ModelSpec, targetProvider: ModelProviderId): string;
110
+ //# sourceMappingURL=model-spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-spec.d.ts","sourceRoot":"","sources":["../../src/utils/model-spec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,MAAM,EACb,eAAe,GAAE,eAA6B,GAC7C,SAAS,CAiDX;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,SAAS,EACf,cAAc,EAAE,eAAe,GAC9B,MAAM,CAWR"}
@@ -0,0 +1,149 @@
1
+ /**
2
+ * Model specification parsing utility
3
+ *
4
+ * Provides parsing and validation of model specification strings
5
+ * per PRD §7.8 (Model & Provider Specification).
6
+ *
7
+ * The provider axis is an OPEN set (PRD §7.8): any non-empty provider string
8
+ * is valid. The harness (pi | claude-code) is NEVER part of the model string —
9
+ * harness-qualified strings (e.g. `pi/anthropic/x`) are rejected.
10
+ */
11
+ /**
12
+ * Parse a model specification string into a ModelSpec object
13
+ *
14
+ * ## Supported Formats
15
+ *
16
+ * ### Qualified Format (provider/model)
17
+ * Explicit provider specification with "/" separator.
18
+ * - Input: `"anthropic/claude-3-5-sonnet"`
19
+ * - Output: `{ provider: 'anthropic', model: 'claude-3-5-sonnet', raw: 'anthropic/claude-3-5-sonnet' }`
20
+ *
21
+ * ### Plain Format (model only)
22
+ * Uses default provider when no provider specified.
23
+ * - Input: `"claude-sonnet-4"` with defaultProvider: `'anthropic'`
24
+ * - Output: `{ provider: 'anthropic', model: 'claude-sonnet-4', raw: 'claude-sonnet-4' }`
25
+ *
26
+ * ## Validation Rules
27
+ *
28
+ * 1. Input cannot be empty or whitespace-only
29
+ * 2. Provider must be a non-empty string (open set — any LLM provider accepted)
30
+ * 3. Model name cannot be empty after provider split
31
+ * 4. Harness-qualified strings (3+ segments, e.g. `pi/anthropic/x`) are REJECTED
32
+ * (PRD §7.8 critical rule: the harness must never appear in the model string)
33
+ * 5. Input is trimmed before parsing, original preserved in `raw` field
34
+ *
35
+ * @param model - Model specification string to parse
36
+ * @param defaultProvider - Default provider to use when none specified (default: 'anthropic')
37
+ * @returns Parsed ModelSpec object with provider, model, and raw string
38
+ * @throws {Error} When model specification is invalid:
39
+ * - Empty or whitespace-only input
40
+ * - Empty provider or model parts
41
+ * - Harness-qualified strings (3+ segments)
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * // Qualified format with known provider
46
+ * const spec1 = parseModelSpec('anthropic/claude-3-5-sonnet');
47
+ * // Returns: { provider: 'anthropic', model: 'claude-3-5-sonnet', raw: 'anthropic/claude-3-5-sonnet' }
48
+ *
49
+ * // Qualified format with custom provider (open set)
50
+ * const spec2 = parseModelSpec('openai/gpt-4o');
51
+ * // Returns: { provider: 'openai', model: 'gpt-4o', raw: 'openai/gpt-4o' }
52
+ *
53
+ * // Plain format — resolved against default provider
54
+ * const spec3 = parseModelSpec('claude-sonnet-4');
55
+ * // Returns: { provider: 'anthropic', model: 'claude-sonnet-4', raw: 'claude-sonnet-4' }
56
+ *
57
+ * // Harness-qualified — REJECTED (PRD §7.8 critical rule)
58
+ * parseModelSpec('pi/anthropic/claude-sonnet-4');
59
+ * // Throws: "Harness must not appear in model string …"
60
+ * ```
61
+ *
62
+ * @see {@link ModelSpec} for the return type structure
63
+ * @see {@link ModelProviderId} for the open provider set
64
+ */
65
+ export function parseModelSpec(model, defaultProvider = 'anthropic') {
66
+ // Preserve ORIGINAL (untrimmed) input — existing consumers rely on raw being exact.
67
+ const raw = model;
68
+ // Trim for parsing — raw stays untouched.
69
+ const trimmed = model.trim();
70
+ if (trimmed.length === 0) {
71
+ throw new Error('Model specification cannot be empty. Expected format: "provider/model" or "model"');
72
+ }
73
+ // Split WITHOUT a limit — must observe ALL segments to detect harness-qualified forms.
74
+ const parts = trimmed.split('/');
75
+ if (parts.length === 1) {
76
+ // Plain format — resolve against defaultProvider (open set).
77
+ return { provider: defaultProvider, model: parts[0], raw };
78
+ }
79
+ if (parts.length === 2) {
80
+ const [provider, modelName] = parts;
81
+ if (provider.length === 0) {
82
+ throw new Error(`Invalid model specification: "${trimmed}". ` +
83
+ 'Provider cannot be empty. Expected format: "provider/model"');
84
+ }
85
+ if (modelName.length === 0) {
86
+ throw new Error(`Invalid model specification: "${trimmed}". ` +
87
+ 'Model name cannot be empty. Expected format: "provider/model"');
88
+ }
89
+ // Open set: ANY non-empty provider string is a valid ModelProviderId (PRD §7.8).
90
+ // No closed-union check — the harness enforces provider constraints at initialize/execute.
91
+ return { provider, model: modelName, raw };
92
+ }
93
+ // parts.length >= 3 → harness-qualified form (e.g. pi/anthropic/x, cc/anthropic/...).
94
+ // PRD §7.8 critical rule: the harness must NEVER appear in the model string.
95
+ throw new Error(`Harness must not appear in model string. ` +
96
+ `Expected format "provider/model" (e.g. "anthropic/claude-sonnet-4-20250514"), got "${raw}".`);
97
+ }
98
+ /**
99
+ * Format a ModelSpec for a specific target provider
100
+ *
101
+ * ## Behavior
102
+ *
103
+ * ### Same Provider (Pass-Through)
104
+ * When `spec.provider` matches `targetProvider`, returns the model name only.
105
+ *
106
+ * **Example:**
107
+ * - Input: `{ provider: 'anthropic', model: 'claude-3-5-sonnet', raw: 'anthropic/claude-3-5-sonnet' }`, `'anthropic'`
108
+ * - Output: `"claude-3-5-sonnet"`
109
+ *
110
+ * ### Different Providers (Error)
111
+ * When providers differ, throws an error. Cross-provider model translation
112
+ * is not supported in the MVP.
113
+ *
114
+ * @param spec - ModelSpec from parseModelSpec() or Provider.normalizeModel()
115
+ * @param targetProvider - The provider to format the model for
116
+ * @returns Formatted model string for target provider (model name only)
117
+ * @throws {Error} When providers differ with message:
118
+ * "Cannot translate {source}/{model} to {target} provider. Cross-provider model translation is not supported."
119
+ *
120
+ * @example
121
+ * ```ts
122
+ * // Same provider: pass-through
123
+ * const spec = parseModelSpec('anthropic/claude-3-5-sonnet');
124
+ * const model = formatModelForProvider(spec, 'anthropic');
125
+ * console.log(model); // "claude-3-5-sonnet"
126
+ *
127
+ * // Different provider: error
128
+ * try {
129
+ * formatModelForProvider(spec, 'openai');
130
+ * } catch (error) {
131
+ * console.error((error as Error).message);
132
+ * // "Cannot translate anthropic/claude-3-5-sonnet to openai provider. ..."
133
+ * }
134
+ * ```
135
+ *
136
+ * @see {@link parseModelSpec} for creating ModelSpec objects
137
+ * @see {@link ModelSpec} for the input type structure
138
+ * @see {@link ModelProviderId} for the open provider set
139
+ */
140
+ export function formatModelForProvider(spec, targetProvider) {
141
+ // Pass-through: same provider
142
+ if (spec.provider === targetProvider) {
143
+ return spec.model;
144
+ }
145
+ // Error: different providers (translation not supported in MVP).
146
+ throw new Error(`Cannot translate ${spec.provider}/${spec.model} to ${targetProvider} provider. ` +
147
+ 'Cross-provider model translation is not supported.');
148
+ }
149
+ //# sourceMappingURL=model-spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-spec.js","sourceRoot":"","sources":["../../src/utils/model-spec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AACH,MAAM,UAAU,cAAc,CAC5B,KAAa,EACb,kBAAmC,WAAW;IAE9C,oFAAoF;IACpF,MAAM,GAAG,GAAG,KAAK,CAAC;IAElB,0CAA0C;IAC1C,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAE7B,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CACb,mFAAmF,CACpF,CAAC;IACJ,CAAC;IAED,uFAAuF;IACvF,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAEjC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,6DAA6D;QAC7D,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC;IAC7D,CAAC;IAED,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,CAAC,QAAQ,EAAE,SAAS,CAAC,GAAG,KAAK,CAAC;QAEpC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CACb,iCAAiC,OAAO,KAAK;gBAC7C,6DAA6D,CAC9D,CAAC;QACJ,CAAC;QAED,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3B,MAAM,IAAI,KAAK,CACb,iCAAiC,OAAO,KAAK;gBAC7C,+DAA+D,CAChE,CAAC;QACJ,CAAC;QAED,iFAAiF;QACjF,2FAA2F;QAC3F,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC;IAC7C,CAAC;IAED,sFAAsF;IACtF,6EAA6E;IAC7E,MAAM,IAAI,KAAK,CACb,2CAA2C;QAC3C,sFAAsF,GAAG,IAAI,CAC9F,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,MAAM,UAAU,sBAAsB,CACpC,IAAe,EACf,cAA+B;IAE/B,8BAA8B;IAC9B,IAAI,IAAI,CAAC,QAAQ,KAAK,cAAc,EAAE,CAAC;QACrC,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED,iEAAiE;IACjE,MAAM,IAAI,KAAK,CACb,oBAAoB,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,KAAK,OAAO,cAAc,aAAa;QACjF,oDAAoD,CACrD,CAAC;AACJ,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Global provider configuration — DEPRECATED shim.
3
+ *
4
+ * @deprecated Since v1.2. All logic has moved to {@link ./harness-config.js}.
5
+ * Use `configureHarnesses` / `getGlobalHarnessConfig` / `resolveHarnessConfig` directly.
6
+ * This module remains only so existing imports (agent.ts, utils/index.ts, tests) keep
7
+ * resolving during the harness-vocabulary migration. Removed when P3.M1 rewires agent.ts.
8
+ */
9
+ export { configureProviders, getGlobalProviderConfig, resolveProviderConfig, resetGlobalConfig, } from './harness-config.js';
10
+ //# sourceMappingURL=provider-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-config.d.ts","sourceRoot":"","sources":["../../src/utils/provider-config.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EACL,kBAAkB,EAClB,uBAAuB,EACvB,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Global provider configuration — DEPRECATED shim.
3
+ *
4
+ * @deprecated Since v1.2. All logic has moved to {@link ./harness-config.js}.
5
+ * Use `configureHarnesses` / `getGlobalHarnessConfig` / `resolveHarnessConfig` directly.
6
+ * This module remains only so existing imports (agent.ts, utils/index.ts, tests) keep
7
+ * resolving during the harness-vocabulary migration. Removed when P3.M1 rewires agent.ts.
8
+ */
9
+ export { configureProviders, getGlobalProviderConfig, resolveProviderConfig, resetGlobalConfig, } from './harness-config.js';
10
+ //# sourceMappingURL=provider-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-config.js","sourceRoot":"","sources":["../../src/utils/provider-config.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EACL,kBAAkB,EAClB,uBAAuB,EACvB,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,qBAAqB,CAAC"}