vibeman 0.0.0 → 0.0.1

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 (220) hide show
  1. package/README.md +12 -0
  2. package/dist/index.js +116 -0
  3. package/dist/runtime/api/.tsbuildinfo +1 -0
  4. package/dist/runtime/api/agent/agent-service.d.ts +226 -0
  5. package/dist/runtime/api/agent/agent-service.js +901 -0
  6. package/dist/runtime/api/agent/ai-providers/claude-code-adapter.d.ts +61 -0
  7. package/dist/runtime/api/agent/ai-providers/claude-code-adapter.js +373 -0
  8. package/dist/runtime/api/agent/ai-providers/codex-cli-provider.d.ts +34 -0
  9. package/dist/runtime/api/agent/ai-providers/codex-cli-provider.js +281 -0
  10. package/dist/runtime/api/agent/ai-providers/index.d.ts +9 -0
  11. package/dist/runtime/api/agent/ai-providers/index.js +7 -0
  12. package/dist/runtime/api/agent/ai-providers/types.d.ts +180 -0
  13. package/dist/runtime/api/agent/ai-providers/types.js +5 -0
  14. package/dist/runtime/api/agent/codex-cli-provider.test.d.ts +1 -0
  15. package/dist/runtime/api/agent/codex-cli-provider.test.js +88 -0
  16. package/dist/runtime/api/agent/core-agent-service.d.ts +119 -0
  17. package/dist/runtime/api/agent/core-agent-service.js +267 -0
  18. package/dist/runtime/api/agent/parsers.d.ts +15 -0
  19. package/dist/runtime/api/agent/parsers.js +241 -0
  20. package/dist/runtime/api/agent/prompt-service.d.ts +17 -0
  21. package/dist/runtime/api/agent/prompt-service.js +340 -0
  22. package/dist/runtime/api/agent/routing-policy.d.ts +188 -0
  23. package/dist/runtime/api/agent/routing-policy.js +246 -0
  24. package/dist/runtime/api/api/router-helpers.d.ts +32 -0
  25. package/dist/runtime/api/api/router-helpers.js +31 -0
  26. package/dist/runtime/api/api/routers/ai.d.ts +188 -0
  27. package/dist/runtime/api/api/routers/ai.js +410 -0
  28. package/dist/runtime/api/api/routers/executions.d.ts +98 -0
  29. package/dist/runtime/api/api/routers/executions.js +103 -0
  30. package/dist/runtime/api/api/routers/git.d.ts +45 -0
  31. package/dist/runtime/api/api/routers/git.js +35 -0
  32. package/dist/runtime/api/api/routers/settings.d.ts +139 -0
  33. package/dist/runtime/api/api/routers/settings.js +113 -0
  34. package/dist/runtime/api/api/routers/tasks.d.ts +141 -0
  35. package/dist/runtime/api/api/routers/tasks.js +238 -0
  36. package/dist/runtime/api/api/routers/workflows.d.ts +268 -0
  37. package/dist/runtime/api/api/routers/workflows.js +308 -0
  38. package/dist/runtime/api/api/routers/worktrees.d.ts +102 -0
  39. package/dist/runtime/api/api/routers/worktrees.js +80 -0
  40. package/dist/runtime/api/api/trpc.d.ts +118 -0
  41. package/dist/runtime/api/api/trpc.js +34 -0
  42. package/dist/runtime/api/index.d.ts +9 -0
  43. package/dist/runtime/api/index.js +125 -0
  44. package/dist/runtime/api/lib/id-generator.d.ts +70 -0
  45. package/dist/runtime/api/lib/id-generator.js +123 -0
  46. package/dist/runtime/api/lib/image-paste-drop-extension.d.ts +26 -0
  47. package/dist/runtime/api/lib/image-paste-drop-extension.js +125 -0
  48. package/dist/runtime/api/lib/logger.d.ts +11 -0
  49. package/dist/runtime/api/lib/logger.js +188 -0
  50. package/dist/runtime/api/lib/markdown-utils.d.ts +8 -0
  51. package/dist/runtime/api/lib/markdown-utils.js +282 -0
  52. package/dist/runtime/api/lib/markdown-utils.test.d.ts +1 -0
  53. package/dist/runtime/api/lib/markdown-utils.test.js +348 -0
  54. package/dist/runtime/api/lib/server/agent-service-singleton.d.ts +6 -0
  55. package/dist/runtime/api/lib/server/agent-service-singleton.js +27 -0
  56. package/dist/runtime/api/lib/server/git-service-singleton.d.ts +6 -0
  57. package/dist/runtime/api/lib/server/git-service-singleton.js +47 -0
  58. package/dist/runtime/api/lib/server/project-root.d.ts +2 -0
  59. package/dist/runtime/api/lib/server/project-root.js +38 -0
  60. package/dist/runtime/api/lib/server/task-service-singleton.d.ts +7 -0
  61. package/dist/runtime/api/lib/server/task-service-singleton.js +58 -0
  62. package/dist/runtime/api/lib/server/vibing-orchestrator-singleton.d.ts +7 -0
  63. package/dist/runtime/api/lib/server/vibing-orchestrator-singleton.js +57 -0
  64. package/dist/runtime/api/lib/tiptap-utils.clamp-selection.test.d.ts +1 -0
  65. package/dist/runtime/api/lib/tiptap-utils.clamp-selection.test.js +27 -0
  66. package/dist/runtime/api/lib/tiptap-utils.d.ts +130 -0
  67. package/dist/runtime/api/lib/tiptap-utils.js +327 -0
  68. package/dist/runtime/api/lib/trpc/client.d.ts +1 -0
  69. package/dist/runtime/api/lib/trpc/client.js +5 -0
  70. package/dist/runtime/api/lib/trpc/server.d.ts +822 -0
  71. package/dist/runtime/api/lib/trpc/server.js +11 -0
  72. package/dist/runtime/api/lib/trpc/ws-server.d.ts +8 -0
  73. package/dist/runtime/api/lib/trpc/ws-server.js +33 -0
  74. package/dist/runtime/api/persistence/database-service.d.ts +14 -0
  75. package/dist/runtime/api/persistence/database-service.js +74 -0
  76. package/dist/runtime/api/persistence/execution-log-persistence.d.ts +90 -0
  77. package/dist/runtime/api/persistence/execution-log-persistence.js +410 -0
  78. package/dist/runtime/api/persistence/execution-log-persistence.test.d.ts +1 -0
  79. package/dist/runtime/api/persistence/execution-log-persistence.test.js +170 -0
  80. package/dist/runtime/api/router.d.ts +825 -0
  81. package/dist/runtime/api/router.js +56 -0
  82. package/dist/runtime/api/settings-service.d.ts +110 -0
  83. package/dist/runtime/api/settings-service.js +611 -0
  84. package/dist/runtime/api/tasks/file-watcher.d.ts +23 -0
  85. package/dist/runtime/api/tasks/file-watcher.js +88 -0
  86. package/dist/runtime/api/tasks/task-file-parser.d.ts +13 -0
  87. package/dist/runtime/api/tasks/task-file-parser.js +161 -0
  88. package/dist/runtime/api/tasks/task-service.d.ts +36 -0
  89. package/dist/runtime/api/tasks/task-service.js +173 -0
  90. package/dist/runtime/api/types/index.d.ts +179 -0
  91. package/dist/runtime/api/types/index.js +1 -0
  92. package/dist/runtime/api/types/settings.d.ts +81 -0
  93. package/dist/runtime/api/types/settings.js +2 -0
  94. package/dist/runtime/api/types.d.ts +2 -0
  95. package/dist/runtime/api/types.js +1 -0
  96. package/dist/runtime/api/utils/env.d.ts +6 -0
  97. package/dist/runtime/api/utils/env.js +12 -0
  98. package/dist/runtime/api/utils/stripNextEnv.d.ts +7 -0
  99. package/dist/runtime/api/utils/stripNextEnv.js +22 -0
  100. package/dist/runtime/api/utils/title-slug.d.ts +6 -0
  101. package/dist/runtime/api/utils/title-slug.js +77 -0
  102. package/dist/runtime/api/utils/url.d.ts +2 -0
  103. package/dist/runtime/api/utils/url.js +19 -0
  104. package/dist/runtime/api/vcs/git-history-service.d.ts +57 -0
  105. package/dist/runtime/api/vcs/git-history-service.js +228 -0
  106. package/dist/runtime/api/vcs/git-service.d.ts +127 -0
  107. package/dist/runtime/api/vcs/git-service.js +284 -0
  108. package/dist/runtime/api/vcs/worktree-service.d.ts +93 -0
  109. package/dist/runtime/api/vcs/worktree-service.js +506 -0
  110. package/dist/runtime/api/vcs/worktree-service.test.d.ts +1 -0
  111. package/dist/runtime/api/vcs/worktree-service.test.js +20 -0
  112. package/dist/runtime/api/workflows/quality-pipeline.d.ts +58 -0
  113. package/dist/runtime/api/workflows/quality-pipeline.js +400 -0
  114. package/dist/runtime/api/workflows/vibing-orchestrator.d.ts +313 -0
  115. package/dist/runtime/api/workflows/vibing-orchestrator.js +1861 -0
  116. package/dist/runtime/web/.next/BUILD_ID +1 -0
  117. package/dist/runtime/web/.next/app-build-manifest.json +59 -0
  118. package/dist/runtime/web/.next/app-path-routes-manifest.json +7 -0
  119. package/dist/runtime/web/.next/build-manifest.json +33 -0
  120. package/dist/runtime/web/.next/package.json +1 -0
  121. package/dist/runtime/web/.next/prerender-manifest.json +61 -0
  122. package/dist/runtime/web/.next/react-loadable-manifest.json +39 -0
  123. package/dist/runtime/web/.next/required-server-files.json +334 -0
  124. package/dist/runtime/web/.next/routes-manifest.json +62 -0
  125. package/dist/runtime/web/.next/server/app/_not-found/page.js +2 -0
  126. package/dist/runtime/web/.next/server/app/_not-found/page.js.nft.json +1 -0
  127. package/dist/runtime/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -0
  128. package/dist/runtime/web/.next/server/app/_not-found.html +7 -0
  129. package/dist/runtime/web/.next/server/app/_not-found.meta +8 -0
  130. package/dist/runtime/web/.next/server/app/_not-found.rsc +22 -0
  131. package/dist/runtime/web/.next/server/app/api/health/route.js +1 -0
  132. package/dist/runtime/web/.next/server/app/api/health/route.js.nft.json +1 -0
  133. package/dist/runtime/web/.next/server/app/api/health/route_client-reference-manifest.js +1 -0
  134. package/dist/runtime/web/.next/server/app/api/images/[...path]/route.js +1 -0
  135. package/dist/runtime/web/.next/server/app/api/images/[...path]/route.js.nft.json +1 -0
  136. package/dist/runtime/web/.next/server/app/api/images/[...path]/route_client-reference-manifest.js +1 -0
  137. package/dist/runtime/web/.next/server/app/api/upload/route.js +1 -0
  138. package/dist/runtime/web/.next/server/app/api/upload/route.js.nft.json +1 -0
  139. package/dist/runtime/web/.next/server/app/api/upload/route_client-reference-manifest.js +1 -0
  140. package/dist/runtime/web/.next/server/app/index.html +7 -0
  141. package/dist/runtime/web/.next/server/app/index.meta +7 -0
  142. package/dist/runtime/web/.next/server/app/index.rsc +27 -0
  143. package/dist/runtime/web/.next/server/app/page.js +147 -0
  144. package/dist/runtime/web/.next/server/app/page.js.nft.json +1 -0
  145. package/dist/runtime/web/.next/server/app/page_client-reference-manifest.js +1 -0
  146. package/dist/runtime/web/.next/server/app-paths-manifest.json +7 -0
  147. package/dist/runtime/web/.next/server/chunks/217.js +1 -0
  148. package/dist/runtime/web/.next/server/chunks/383.js +6 -0
  149. package/dist/runtime/web/.next/server/chunks/458.js +1 -0
  150. package/dist/runtime/web/.next/server/chunks/576.js +18 -0
  151. package/dist/runtime/web/.next/server/chunks/635.js +22 -0
  152. package/dist/runtime/web/.next/server/chunks/761.js +1 -0
  153. package/dist/runtime/web/.next/server/chunks/777.js +3 -0
  154. package/dist/runtime/web/.next/server/chunks/825.js +1 -0
  155. package/dist/runtime/web/.next/server/chunks/838.js +1 -0
  156. package/dist/runtime/web/.next/server/chunks/973.js +15 -0
  157. package/dist/runtime/web/.next/server/functions-config-manifest.json +4 -0
  158. package/dist/runtime/web/.next/server/middleware-build-manifest.js +1 -0
  159. package/dist/runtime/web/.next/server/middleware-manifest.json +6 -0
  160. package/dist/runtime/web/.next/server/middleware-react-loadable-manifest.js +1 -0
  161. package/dist/runtime/web/.next/server/next-font-manifest.js +1 -0
  162. package/dist/runtime/web/.next/server/next-font-manifest.json +1 -0
  163. package/dist/runtime/web/.next/server/pages/404.html +7 -0
  164. package/dist/runtime/web/.next/server/pages/500.html +1 -0
  165. package/dist/runtime/web/.next/server/pages/_app.js +1 -0
  166. package/dist/runtime/web/.next/server/pages/_app.js.nft.json +1 -0
  167. package/dist/runtime/web/.next/server/pages/_document.js +1 -0
  168. package/dist/runtime/web/.next/server/pages/_document.js.nft.json +1 -0
  169. package/dist/runtime/web/.next/server/pages/_error.js +19 -0
  170. package/dist/runtime/web/.next/server/pages/_error.js.nft.json +1 -0
  171. package/dist/runtime/web/.next/server/pages-manifest.json +6 -0
  172. package/dist/runtime/web/.next/server/server-reference-manifest.js +1 -0
  173. package/dist/runtime/web/.next/server/server-reference-manifest.json +1 -0
  174. package/dist/runtime/web/.next/server/webpack-runtime.js +1 -0
  175. package/dist/runtime/web/.next/static/1HR8N0rJkCvFRtbTPJMyH/_buildManifest.js +1 -0
  176. package/dist/runtime/web/.next/static/1HR8N0rJkCvFRtbTPJMyH/_ssgManifest.js +1 -0
  177. package/dist/runtime/web/.next/static/chunks/18-15c10d3288afef2e.js +1 -0
  178. package/dist/runtime/web/.next/static/chunks/1c0ca389.537bbe362e3ffbd9.js +3 -0
  179. package/dist/runtime/web/.next/static/chunks/22747d63-ad5da0c19f4cfe41.js +71 -0
  180. package/dist/runtime/web/.next/static/chunks/277-0142a939f08738c3.js +63 -0
  181. package/dist/runtime/web/.next/static/chunks/355.056c2645878a799a.js +1 -0
  182. package/dist/runtime/web/.next/static/chunks/420.a5ccf151c9e2b2f1.js +1 -0
  183. package/dist/runtime/web/.next/static/chunks/439.1be0c6242fd248d5.js +15 -0
  184. package/dist/runtime/web/.next/static/chunks/440.c52e7c0f797e22b2.js +1 -0
  185. package/dist/runtime/web/.next/static/chunks/575-e2478287c27da87b.js +1 -0
  186. package/dist/runtime/web/.next/static/chunks/691.920d88c115087314.js +1 -0
  187. package/dist/runtime/web/.next/static/chunks/765-e838910065b50c3d.js +1 -0
  188. package/dist/runtime/web/.next/static/chunks/87c73c54-09e1ba5c70e60a51.js +1 -0
  189. package/dist/runtime/web/.next/static/chunks/891cff7f.0f71fc028f87e683.js +1 -0
  190. package/dist/runtime/web/.next/static/chunks/8bb4d8db-3e2aa02b0a2384b9.js +1 -0
  191. package/dist/runtime/web/.next/static/chunks/9af238c7-271a911d4e99ab18.js +1 -0
  192. package/dist/runtime/web/.next/static/chunks/app/_not-found/page-1cb74d1cba27d0ab.js +1 -0
  193. package/dist/runtime/web/.next/static/chunks/app/api/health/route-105a61ae865ba536.js +1 -0
  194. package/dist/runtime/web/.next/static/chunks/app/api/images/[...path]/route-105a61ae865ba536.js +1 -0
  195. package/dist/runtime/web/.next/static/chunks/app/api/upload/route-105a61ae865ba536.js +1 -0
  196. package/dist/runtime/web/.next/static/chunks/app/layout-dc0cfd29075b2160.js +1 -0
  197. package/dist/runtime/web/.next/static/chunks/app/page-f34a8b196b18850b.js +1 -0
  198. package/dist/runtime/web/.next/static/chunks/cac567b0-5b77dd12911823cd.js +1 -0
  199. package/dist/runtime/web/.next/static/chunks/framework-2518f1345b5b2806.js +1 -0
  200. package/dist/runtime/web/.next/static/chunks/main-17665e5e39de9a8a.js +1 -0
  201. package/dist/runtime/web/.next/static/chunks/main-app-c0b0f5ba4f7f9d75.js +1 -0
  202. package/dist/runtime/web/.next/static/chunks/pages/_app-d6f6b3bbc3d81ee1.js +1 -0
  203. package/dist/runtime/web/.next/static/chunks/pages/_error-75a96cf1997cc3b9.js +1 -0
  204. package/dist/runtime/web/.next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
  205. package/dist/runtime/web/.next/static/chunks/webpack-c8de37305b4635cf.js +1 -0
  206. package/dist/runtime/web/.next/static/css/08c950681f1a9a92.css +1 -0
  207. package/dist/runtime/web/.next/static/css/2728291c68f99cb1.css +3 -0
  208. package/dist/runtime/web/.next/static/css/521bd69cc298cd1a.css +1 -0
  209. package/dist/runtime/web/.next/static/css/537e22821e101b87.css +1 -0
  210. package/dist/runtime/web/.next/static/media/19cfc7226ec3afaa-s.woff2 +0 -0
  211. package/dist/runtime/web/.next/static/media/21350d82a1f187e9-s.woff2 +0 -0
  212. package/dist/runtime/web/.next/static/media/8e9860b6e62d6359-s.woff2 +0 -0
  213. package/dist/runtime/web/.next/static/media/ba9851c3c22cd980-s.woff2 +0 -0
  214. package/dist/runtime/web/.next/static/media/c5fe6dc8356a8c31-s.woff2 +0 -0
  215. package/dist/runtime/web/.next/static/media/df0a9ae256c0569c-s.woff2 +0 -0
  216. package/dist/runtime/web/.next/static/media/e4af272ccee01ff0-s.p.woff2 +0 -0
  217. package/dist/runtime/web/package.json +65 -0
  218. package/dist/runtime/web/server.js +44 -0
  219. package/dist/tsconfig.tsbuildinfo +1 -0
  220. package/package.json +80 -7
@@ -0,0 +1,246 @@
1
+ /**
2
+ * AI Routing Policy Management
3
+ * Handles provider selection per operation with fallbacks and configuration
4
+ */
5
+ import { z } from 'zod';
6
+ import fs from 'fs/promises';
7
+ import path from 'path';
8
+ import { log } from '../lib/logger.js';
9
+ import { getSettingsService } from '../settings-service.js';
10
+ import { getVibeDir } from '../lib/server/project-root.js';
11
+ /**
12
+ * Generation options for AI execution
13
+ */
14
+ export const GenerationOptionsSchema = z.object({
15
+ temperature: z.number().min(0).max(2).optional(),
16
+ maxTokens: z.number().positive().optional(),
17
+ tools: z.array(z.string()).optional(),
18
+ });
19
+ /**
20
+ * Per-operation routing configuration
21
+ */
22
+ export const OperationConfigSchema = z.object({
23
+ provider: z.string(),
24
+ model: z.string().optional(),
25
+ options: GenerationOptionsSchema.optional(),
26
+ fallback: z.array(z.string()).optional(),
27
+ });
28
+ /**
29
+ * Complete routing policy schema
30
+ */
31
+ export const RoutingPolicySchema = z.object({
32
+ defaultProvider: z.string(),
33
+ operations: z
34
+ .record(z.enum(['execute_task', 'improve_task', 'ai_codereview', 'ai_merge']), OperationConfigSchema)
35
+ .optional(),
36
+ });
37
+ /**
38
+ * Routing Policy Manager
39
+ * Manages AI provider routing policies with hot-reload capability
40
+ */
41
+ export class RoutingPolicyManager {
42
+ constructor() {
43
+ this.policy = null;
44
+ this.lastModified = 0;
45
+ this.policyFilePath = path.join(getVibeDir(), 'ai-routing.json');
46
+ }
47
+ /**
48
+ * Get current effective policy with hot-reload
49
+ */
50
+ async getPolicy() {
51
+ await this.loadPolicyIfChanged();
52
+ // Return default policy if none exists
53
+ if (!this.policy) {
54
+ const settings = getSettingsService().getSettings();
55
+ return {
56
+ defaultProvider: settings.agents.defaultProvider || 'claude-code',
57
+ operations: {},
58
+ };
59
+ }
60
+ return this.policy;
61
+ }
62
+ /**
63
+ * Update routing policy and persist to disk
64
+ */
65
+ async updatePolicy(updates) {
66
+ const currentPolicy = await this.getPolicy();
67
+ const newPolicy = {
68
+ defaultProvider: updates.defaultProvider || currentPolicy.defaultProvider,
69
+ operations: {
70
+ ...currentPolicy.operations,
71
+ ...updates.operations,
72
+ },
73
+ };
74
+ // Validate the new policy
75
+ const validated = RoutingPolicySchema.parse(newPolicy);
76
+ // Ensure .vibeman directory exists
77
+ const vibemanDir = path.dirname(this.policyFilePath);
78
+ await fs.mkdir(vibemanDir, { recursive: true });
79
+ // Write to file
80
+ await fs.writeFile(this.policyFilePath, JSON.stringify(validated, null, 2), 'utf-8');
81
+ // Update in-memory policy
82
+ this.policy = validated;
83
+ this.lastModified = Date.now();
84
+ log.info('Updated AI routing policy', { policy: validated }, 'routing-policy');
85
+ }
86
+ /**
87
+ * Resolve provider for a specific operation
88
+ */
89
+ async resolveProviderForOperation(operation, overrides) {
90
+ const policy = await this.getPolicy();
91
+ const operationConfig = policy.operations?.[operation];
92
+ // Pull provider/model defaults from settings per operation
93
+ const settings = getSettingsService().getSettings();
94
+ const coding = settings.agents?.codingAgent;
95
+ const judge = settings.agents?.judgeAgent;
96
+ const settingsProviderModel = (() => {
97
+ switch (operation) {
98
+ case 'ai_codereview':
99
+ return { provider: judge?.provider, model: judge?.model };
100
+ case 'execute_task':
101
+ case 'improve_task':
102
+ case 'ai_merge':
103
+ default:
104
+ return { provider: coding?.provider, model: coding?.model };
105
+ }
106
+ })();
107
+ // Start with operation-specific config or fall back to settings, then default policy
108
+ const baseProvider = operationConfig?.provider || settingsProviderModel.provider || policy.defaultProvider;
109
+ const baseModel = operationConfig?.model || settingsProviderModel.model;
110
+ const baseOptions = operationConfig?.options;
111
+ const baseFallbacks = operationConfig?.fallback || [];
112
+ // Apply overrides
113
+ const resolved = {
114
+ provider: overrides?.provider || baseProvider,
115
+ model: overrides?.model || baseModel,
116
+ options: overrides?.options || baseOptions,
117
+ fallbacks: overrides?.fallbacks || baseFallbacks,
118
+ };
119
+ log.debug(`Resolved provider for ${operation}`, { operation, resolved }, 'routing-policy');
120
+ return resolved;
121
+ }
122
+ /**
123
+ * Set default provider
124
+ */
125
+ async setDefaultProvider(provider) {
126
+ await this.updatePolicy({ defaultProvider: provider });
127
+ }
128
+ /**
129
+ * Set operation-specific routing
130
+ */
131
+ async setOperationConfig(operation, config) {
132
+ const currentPolicy = await this.getPolicy();
133
+ await this.updatePolicy({
134
+ operations: {
135
+ ...currentPolicy.operations,
136
+ [operation]: config,
137
+ },
138
+ });
139
+ }
140
+ /**
141
+ * Validate policy against available providers
142
+ */
143
+ validatePolicy(policy, availableProviders) {
144
+ const errors = [];
145
+ // Check default provider
146
+ if (!availableProviders.has(policy.defaultProvider)) {
147
+ errors.push(`Default provider '${policy.defaultProvider}' is not available`);
148
+ }
149
+ // Check operation providers
150
+ if (policy.operations) {
151
+ for (const [operation, config] of Object.entries(policy.operations)) {
152
+ if (!availableProviders.has(config.provider)) {
153
+ errors.push(`Provider '${config.provider}' for operation '${operation}' is not available`);
154
+ }
155
+ // Check fallback providers
156
+ if (config.fallback) {
157
+ for (const fallbackProvider of config.fallback) {
158
+ if (!availableProviders.has(fallbackProvider)) {
159
+ errors.push(`Fallback provider '${fallbackProvider}' for operation '${operation}' is not available`);
160
+ }
161
+ }
162
+ }
163
+ }
164
+ }
165
+ return errors;
166
+ }
167
+ /**
168
+ * Load policy from file if it has changed
169
+ */
170
+ async loadPolicyIfChanged() {
171
+ try {
172
+ const stats = await fs.stat(this.policyFilePath);
173
+ const fileModified = stats.mtime.getTime();
174
+ // Only reload if file has changed
175
+ if (fileModified <= this.lastModified) {
176
+ return;
177
+ }
178
+ const content = await fs.readFile(this.policyFilePath, 'utf-8');
179
+ const parsed = JSON.parse(content);
180
+ const validated = RoutingPolicySchema.parse(parsed);
181
+ this.policy = validated;
182
+ this.lastModified = fileModified;
183
+ log.info('Loaded AI routing policy', { policy: validated }, 'routing-policy');
184
+ }
185
+ catch (error) {
186
+ if (error.code === 'ENOENT') {
187
+ // File doesn't exist yet, use default
188
+ this.policy = null;
189
+ return;
190
+ }
191
+ log.warn('Failed to load routing policy, using defaults', error, 'routing-policy');
192
+ this.policy = null;
193
+ }
194
+ }
195
+ /**
196
+ * Create example policy file if it doesn't exist
197
+ */
198
+ async createExamplePolicy() {
199
+ const examplePath = path.join(getVibeDir(), 'ai-routing.example.json');
200
+ // Do not recreate if it already exists
201
+ try {
202
+ await fs.stat(examplePath);
203
+ return; // already exists; avoid noisy logs
204
+ }
205
+ catch (err) {
206
+ if (err?.code && err.code !== 'ENOENT') {
207
+ log.warn('Unable to stat example AI routing policy', err, 'routing-policy');
208
+ return;
209
+ }
210
+ }
211
+ const settings = getSettingsService().getSettings();
212
+ const example = {
213
+ defaultProvider: settings.agents.defaultProvider || 'claude-code',
214
+ operations: {
215
+ execute_task: {
216
+ provider: settings.agents.defaultProvider || 'claude-code',
217
+ model: 'claude-sonnet-4-20250514',
218
+ fallback: ['codex'],
219
+ },
220
+ improve_task: {
221
+ provider: settings.agents.defaultProvider || 'claude-code',
222
+ model: 'claude-sonnet-4-20250514',
223
+ },
224
+ ai_codereview: {
225
+ provider: 'codex',
226
+ model: 'gpt-5',
227
+ },
228
+ ai_merge: {
229
+ provider: settings.agents.defaultProvider || 'claude-code',
230
+ model: 'claude-sonnet-4-20250514',
231
+ },
232
+ },
233
+ };
234
+ // Ensure .vibeman directory exists
235
+ const vibemanDir = path.dirname(examplePath);
236
+ await fs.mkdir(vibemanDir, { recursive: true });
237
+ await fs.writeFile(examplePath, JSON.stringify(example, null, 2), 'utf-8');
238
+ log.info('Created example AI routing policy', { path: examplePath }, 'routing-policy');
239
+ }
240
+ /**
241
+ * Get policy file path for external access
242
+ */
243
+ getPolicyFilePath() {
244
+ return this.policyFilePath;
245
+ }
246
+ }
@@ -0,0 +1,32 @@
1
+ import { z } from 'zod';
2
+ import type { TaskService } from '../tasks/task-service.js';
3
+ export declare const TaskIdSchema: z.ZodObject<{
4
+ taskId: z.ZodString;
5
+ }, "strip", z.ZodTypeAny, {
6
+ taskId: string;
7
+ }, {
8
+ taskId: string;
9
+ }>;
10
+ export declare const ExecutionIdSchema: z.ZodObject<{
11
+ executionId: z.ZodString;
12
+ }, "strip", z.ZodTypeAny, {
13
+ executionId: string;
14
+ }, {
15
+ executionId: string;
16
+ }>;
17
+ export declare const WorkflowIdSchema: z.ZodObject<{
18
+ workflowId: z.ZodString;
19
+ }, "strip", z.ZodTypeAny, {
20
+ workflowId: string;
21
+ }, {
22
+ workflowId: string;
23
+ }>;
24
+ export declare const WorktreePathSchema: z.ZodObject<{
25
+ worktreePath: z.ZodString;
26
+ }, "strip", z.ZodTypeAny, {
27
+ worktreePath: string;
28
+ }, {
29
+ worktreePath: string;
30
+ }>;
31
+ export declare const withErrors: <TArgs extends any[], TRes>(action: string, tag: string, fn: (...args: TArgs) => Promise<TRes>) => (...args: TArgs) => Promise<TRes>;
32
+ export declare function makeAssertTaskActive(taskService: TaskService): (id: string) => import("../types/index.js").Task;
@@ -0,0 +1,31 @@
1
+ import { z } from 'zod';
2
+ import { log } from '../lib/logger.js';
3
+ // Shared input schemas
4
+ export const TaskIdSchema = z.object({ taskId: z.string() });
5
+ export const ExecutionIdSchema = z.object({ executionId: z.string() });
6
+ export const WorkflowIdSchema = z.object({ workflowId: z.string() });
7
+ export const WorktreePathSchema = z.object({ worktreePath: z.string() });
8
+ // Centralized error formatter for consistent messages
9
+ export const withErrors = (action, tag, fn) => {
10
+ return async (...args) => {
11
+ try {
12
+ return await fn(...args);
13
+ }
14
+ catch (error) {
15
+ log.error(`Error in ${action}`, error, tag);
16
+ const msg = error instanceof Error ? error.message : String(error);
17
+ throw new Error(`Failed to ${action}: ${msg}`);
18
+ }
19
+ };
20
+ };
21
+ // Factory for active-task assertion to avoid repeating checks
22
+ export function makeAssertTaskActive(taskService) {
23
+ return (id) => {
24
+ const t = taskService.getTask(id);
25
+ if (!t)
26
+ throw new Error(`Task ${id} not found`);
27
+ if (t.deleted_at)
28
+ throw new Error(`Task ${id} is deleted`);
29
+ return t;
30
+ };
31
+ }
@@ -0,0 +1,188 @@
1
+ /**
2
+ * AI Router - Multi-Model Orchestration API
3
+ * Provides tRPC endpoints for managing AI providers and routing policies
4
+ */
5
+ import type { AgentService } from '../../agent/agent-service.js';
6
+ import { type RoutingPolicy, type RoutableOperation } from '../../agent/routing-policy.js';
7
+ /**
8
+ * Build AI router with required dependencies
9
+ */
10
+ export declare function buildAIRoutes(options: {
11
+ requireAgentService: () => AgentService;
12
+ }): {
13
+ /**
14
+ * List all registered AI providers with health status
15
+ */
16
+ listProviders: import("@trpc/server").TRPCQueryProcedure<{
17
+ input: void;
18
+ output: {
19
+ name: string;
20
+ displayName: string;
21
+ available: boolean;
22
+ error: string | undefined;
23
+ capabilities: import("../../agent/ai-providers/types.js").ProviderCapabilities;
24
+ models: import("../../agent/ai-providers/types.js").ModelInfo[];
25
+ }[];
26
+ meta: object;
27
+ }>;
28
+ /**
29
+ * List available models from providers
30
+ */
31
+ listModels: import("@trpc/server").TRPCQueryProcedure<{
32
+ input: {
33
+ provider?: string | undefined;
34
+ } | undefined;
35
+ output: import("../../agent/ai-providers/types.js").ModelInfo[];
36
+ meta: object;
37
+ }>;
38
+ /**
39
+ * Get current routing policy
40
+ */
41
+ getRoutingPolicy: import("@trpc/server").TRPCQueryProcedure<{
42
+ input: void;
43
+ output: any;
44
+ meta: object;
45
+ }>;
46
+ /**
47
+ * Update routing policy
48
+ */
49
+ updateRoutingPolicy: import("@trpc/server").TRPCMutationProcedure<{
50
+ input: {
51
+ policy: {
52
+ defaultProvider?: string | undefined;
53
+ operations?: Partial<Record<"execute_task" | "improve_task" | "ai_codereview" | "ai_merge", {
54
+ provider: string;
55
+ options?: {
56
+ maxTokens?: number | undefined;
57
+ tools?: string[] | undefined;
58
+ temperature?: number | undefined;
59
+ } | undefined;
60
+ model?: string | undefined;
61
+ fallback?: string[] | undefined;
62
+ }>> | undefined;
63
+ };
64
+ };
65
+ output: {
66
+ success: boolean;
67
+ policy: any;
68
+ };
69
+ meta: object;
70
+ }>;
71
+ /**
72
+ * Set default provider
73
+ */
74
+ setDefaultProvider: import("@trpc/server").TRPCMutationProcedure<{
75
+ input: {
76
+ provider: string;
77
+ };
78
+ output: {
79
+ success: boolean;
80
+ provider: string;
81
+ };
82
+ meta: object;
83
+ }>;
84
+ /**
85
+ * Set operation-specific configuration
86
+ */
87
+ setOperationConfig: import("@trpc/server").TRPCMutationProcedure<{
88
+ input: {
89
+ config: {
90
+ provider: string;
91
+ options?: {
92
+ maxTokens?: number | undefined;
93
+ tools?: string[] | undefined;
94
+ temperature?: number | undefined;
95
+ } | undefined;
96
+ model?: string | undefined;
97
+ fallback?: string[] | undefined;
98
+ };
99
+ operation: "execute_task" | "improve_task" | "ai_codereview" | "ai_merge";
100
+ };
101
+ output: {
102
+ success: boolean;
103
+ };
104
+ meta: object;
105
+ }>;
106
+ /**
107
+ * Validate provider setup and refresh health status
108
+ */
109
+ validateProviders: import("@trpc/server").TRPCMutationProcedure<{
110
+ input: {
111
+ force?: boolean | undefined;
112
+ } | undefined;
113
+ output: {
114
+ success: boolean;
115
+ results: {
116
+ provider: string;
117
+ available: boolean;
118
+ error: string | undefined;
119
+ modelCount: number;
120
+ capabilities: import("../../agent/ai-providers/types.js").ProviderCapabilities;
121
+ }[];
122
+ totalProviders: number;
123
+ availableProviders: number;
124
+ };
125
+ meta: object;
126
+ }>;
127
+ };
128
+ /**
129
+ * Test-friendly plain handlers mirroring the AI routes, without tRPC wrappers.
130
+ * Useful for unit tests that call functions directly.
131
+ */
132
+ export declare function buildAIRouteHandlers(options: {
133
+ requireAgentService: () => AgentService;
134
+ }): {
135
+ readonly listProviders: () => Promise<{
136
+ name: string;
137
+ displayName: any;
138
+ available: boolean;
139
+ error: string | undefined;
140
+ capabilities: any;
141
+ models: import("../../agent/ai-providers/types.js").ModelInfo[];
142
+ }[]>;
143
+ readonly listModels: (input?: {
144
+ provider?: string;
145
+ }) => Promise<any>;
146
+ readonly getRoutingPolicy: () => Promise<any>;
147
+ readonly updateRoutingPolicy: (input: {
148
+ policy: Partial<RoutingPolicy>;
149
+ }) => Promise<{
150
+ readonly success: true;
151
+ readonly policy: any;
152
+ }>;
153
+ readonly setDefaultProvider: (input: {
154
+ provider: string;
155
+ }) => Promise<{
156
+ readonly success: true;
157
+ readonly provider: string;
158
+ }>;
159
+ readonly setOperationConfig: (input: {
160
+ operation: RoutableOperation;
161
+ config: any;
162
+ }) => Promise<{
163
+ readonly success: true;
164
+ }>;
165
+ readonly validateProviders: () => Promise<{
166
+ success: true;
167
+ results: {
168
+ provider: string;
169
+ available: boolean;
170
+ error: string | undefined;
171
+ modelCount: number;
172
+ capabilities: import("../../agent/ai-providers/types.js").ProviderCapabilities;
173
+ }[];
174
+ totalProviders: number;
175
+ availableProviders: number;
176
+ }>;
177
+ readonly getProviderHealth: () => Promise<{
178
+ totalProviders: number;
179
+ availableProviders: number;
180
+ unavailableProviders: number;
181
+ providers: Array<{
182
+ name: string;
183
+ available: boolean;
184
+ error?: string;
185
+ modelCount: number;
186
+ }>;
187
+ }>;
188
+ };