vibeman 0.0.2 → 0.0.3
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.
- package/dist/runtime/api/.tsbuildinfo +1 -1
- package/dist/runtime/api/agent/agent-service.d.ts +7 -6
- package/dist/runtime/api/agent/agent-service.js +36 -27
- package/dist/runtime/api/agent/ai-providers/codex-cli-provider.d.ts +2 -0
- package/dist/runtime/api/agent/ai-providers/codex-cli-provider.js +62 -30
- package/dist/runtime/api/agent/codex-cli-provider.test.js +47 -2
- package/dist/runtime/api/agent/routing-policy.d.ts +13 -30
- package/dist/runtime/api/agent/routing-policy.js +82 -132
- package/dist/runtime/api/agent/routing-policy.test.js +63 -0
- package/dist/runtime/api/api/routers/ai.d.ts +15 -3
- package/dist/runtime/api/api/routers/ai.js +7 -6
- package/dist/runtime/api/api/routers/executions.d.ts +1 -1
- package/dist/runtime/api/api/routers/tasks.d.ts +3 -3
- package/dist/runtime/api/api/routers/workflows.d.ts +8 -0
- package/dist/runtime/api/api/routers/workflows.js +2 -1
- package/dist/runtime/api/api/trpc.d.ts +6 -6
- package/dist/runtime/api/lib/trpc/server.d.ts +27 -7
- package/dist/runtime/api/router.d.ts +27 -7
- package/dist/runtime/api/settings-service.js +49 -1
- package/dist/runtime/api/types/index.d.ts +8 -1
- package/dist/runtime/api/types/settings.d.ts +15 -2
- package/dist/runtime/api/workflows/vibing-orchestrator.js +32 -1
- package/dist/runtime/web/.next/BUILD_ID +1 -1
- package/dist/runtime/web/.next/app-build-manifest.json +18 -11
- package/dist/runtime/web/.next/app-path-routes-manifest.json +2 -1
- package/dist/runtime/web/.next/build-manifest.json +2 -2
- package/dist/runtime/web/.next/prerender-manifest.json +10 -10
- package/dist/runtime/web/.next/routes-manifest.json +8 -0
- package/dist/runtime/web/.next/server/app/.vibeman/assets/images/[...path]/route.js +1 -0
- package/dist/runtime/web/.next/server/app/.vibeman/assets/images/[...path]/route.js.nft.json +1 -0
- package/dist/runtime/web/.next/server/app/.vibeman/assets/images/[...path]/route_client-reference-manifest.js +1 -0
- package/dist/runtime/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/runtime/web/.next/server/app/_not-found.html +2 -2
- package/dist/runtime/web/.next/server/app/_not-found.rsc +1 -1
- package/dist/runtime/web/.next/server/app/api/health/route_client-reference-manifest.js +1 -1
- package/dist/runtime/web/.next/server/app/api/images/[...path]/route.js +1 -1
- package/dist/runtime/web/.next/server/app/api/images/[...path]/route_client-reference-manifest.js +1 -1
- package/dist/runtime/web/.next/server/app/api/upload/route.js +1 -1
- package/dist/runtime/web/.next/server/app/api/upload/route_client-reference-manifest.js +1 -1
- package/dist/runtime/web/.next/server/app/index.html +2 -2
- package/dist/runtime/web/.next/server/app/index.rsc +2 -2
- package/dist/runtime/web/.next/server/app/page.js +21 -21
- package/dist/runtime/web/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/runtime/web/.next/server/app-paths-manifest.json +2 -1
- package/dist/runtime/web/.next/server/pages/404.html +2 -2
- package/dist/runtime/web/.next/server/pages/500.html +1 -1
- package/dist/runtime/web/.next/server/server-reference-manifest.json +1 -1
- package/dist/runtime/web/.next/static/5_15u1WQCxN1_eHZpldCv/_buildManifest.js +1 -0
- package/dist/runtime/web/.next/static/chunks/{277-0142a939f08738c3.js → 823-6f371a6e829adbba.js} +1 -1
- package/dist/runtime/web/.next/static/chunks/app/.vibeman/assets/images/[...path]/route-751c9265a65409e5.js +1 -0
- package/dist/runtime/web/.next/static/chunks/app/api/health/route-751c9265a65409e5.js +1 -0
- package/dist/runtime/web/.next/static/chunks/app/api/images/[...path]/route-751c9265a65409e5.js +1 -0
- package/dist/runtime/web/.next/static/chunks/app/api/upload/route-751c9265a65409e5.js +1 -0
- package/dist/runtime/web/.next/static/chunks/app/page-9fe7d75095b4ccec.js +1 -0
- package/package.json +1 -1
- package/dist/runtime/api/lib/image-paste-drop-extension.d.ts +0 -26
- package/dist/runtime/api/lib/image-paste-drop-extension.js +0 -125
- package/dist/runtime/api/lib/markdown-utils.d.ts +0 -8
- package/dist/runtime/api/lib/markdown-utils.js +0 -282
- package/dist/runtime/api/lib/markdown-utils.test.js +0 -348
- package/dist/runtime/api/lib/tiptap-utils.clamp-selection.test.d.ts +0 -1
- package/dist/runtime/api/lib/tiptap-utils.clamp-selection.test.js +0 -27
- package/dist/runtime/api/lib/tiptap-utils.d.ts +0 -130
- package/dist/runtime/api/lib/tiptap-utils.js +0 -327
- package/dist/runtime/web/.next/static/chunks/app/api/health/route-105a61ae865ba536.js +0 -1
- package/dist/runtime/web/.next/static/chunks/app/api/images/[...path]/route-105a61ae865ba536.js +0 -1
- package/dist/runtime/web/.next/static/chunks/app/api/upload/route-105a61ae865ba536.js +0 -1
- package/dist/runtime/web/.next/static/chunks/app/page-8c3ba579efc6f918.js +0 -1
- package/dist/runtime/web/.next/static/mRpNgPfbYR_0wrODzlg_4/_buildManifest.js +0 -1
- /package/dist/runtime/api/{lib/markdown-utils.test.d.ts → agent/routing-policy.test.d.ts} +0 -0
- /package/dist/runtime/web/.next/static/{mRpNgPfbYR_0wrODzlg_4 → 5_15u1WQCxN1_eHZpldCv}/_ssgManifest.js +0 -0
|
@@ -3,11 +3,18 @@
|
|
|
3
3
|
* Handles provider selection per operation with fallbacks and configuration
|
|
4
4
|
*/
|
|
5
5
|
import { z } from 'zod';
|
|
6
|
-
import fs from 'fs/promises';
|
|
7
|
-
import path from 'path';
|
|
8
6
|
import { log } from '../lib/logger.js';
|
|
9
7
|
import { getSettingsService } from '../settings-service.js';
|
|
10
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Operation types that support AI routing
|
|
10
|
+
*/
|
|
11
|
+
const ROUTABLE_OPERATIONS = [
|
|
12
|
+
'execute_task',
|
|
13
|
+
'quality_checks',
|
|
14
|
+
'ai_codereview',
|
|
15
|
+
'ai_merge',
|
|
16
|
+
'improve_task',
|
|
17
|
+
];
|
|
11
18
|
/**
|
|
12
19
|
* Generation options for AI execution
|
|
13
20
|
*/
|
|
@@ -30,67 +37,83 @@ export const OperationConfigSchema = z.object({
|
|
|
30
37
|
*/
|
|
31
38
|
export const RoutingPolicySchema = z.object({
|
|
32
39
|
defaultProvider: z.string(),
|
|
33
|
-
operations: z
|
|
34
|
-
.record(z.enum(['execute_task', 'improve_task', 'ai_codereview', 'ai_merge']), OperationConfigSchema)
|
|
35
|
-
.optional(),
|
|
40
|
+
operations: z.record(z.enum(ROUTABLE_OPERATIONS), OperationConfigSchema).optional(),
|
|
36
41
|
});
|
|
42
|
+
export const ROUTABLE_OPERATION_LIST = ROUTABLE_OPERATIONS;
|
|
37
43
|
/**
|
|
38
44
|
* Routing Policy Manager
|
|
39
|
-
* Manages AI provider routing policies
|
|
45
|
+
* Manages AI provider routing policies stored inside settings.json
|
|
40
46
|
*/
|
|
41
47
|
export class RoutingPolicyManager {
|
|
42
48
|
constructor() {
|
|
43
49
|
this.policy = null;
|
|
44
|
-
this.
|
|
45
|
-
|
|
50
|
+
this.settingsService = getSettingsService();
|
|
51
|
+
// Invalidate cached policy when settings change
|
|
52
|
+
this.settingsService.on('settingsUpdated', () => {
|
|
53
|
+
this.policy = null;
|
|
54
|
+
});
|
|
46
55
|
}
|
|
47
56
|
/**
|
|
48
|
-
* Get current effective policy
|
|
57
|
+
* Get current effective policy (lazy loads from settings)
|
|
49
58
|
*/
|
|
50
59
|
async getPolicy() {
|
|
51
|
-
await this.loadPolicyIfChanged();
|
|
52
|
-
// Return default policy if none exists
|
|
53
60
|
if (!this.policy) {
|
|
54
|
-
|
|
55
|
-
return {
|
|
56
|
-
defaultProvider: settings.agents.defaultProvider || 'claude-code',
|
|
57
|
-
operations: {},
|
|
58
|
-
};
|
|
61
|
+
this.policy = this.buildPolicyFromSettings();
|
|
59
62
|
}
|
|
60
63
|
return this.policy;
|
|
61
64
|
}
|
|
62
65
|
/**
|
|
63
|
-
* Update routing policy and persist
|
|
66
|
+
* Update routing policy and persist via settings service
|
|
64
67
|
*/
|
|
65
68
|
async updatePolicy(updates) {
|
|
66
69
|
const currentPolicy = await this.getPolicy();
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
operations
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
const mergedOperations = {
|
|
71
|
+
...(currentPolicy.operations ?? {}),
|
|
72
|
+
...(updates.operations ?? {}),
|
|
73
|
+
};
|
|
74
|
+
const operationsKeys = Object.keys(mergedOperations);
|
|
75
|
+
const nextPolicy = {
|
|
76
|
+
defaultProvider: updates.defaultProvider ?? currentPolicy.defaultProvider,
|
|
77
|
+
...(operationsKeys.length > 0 ? { operations: mergedOperations } : {}),
|
|
73
78
|
};
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
|
79
|
+
const validated = RoutingPolicySchema.parse(nextPolicy);
|
|
80
|
+
await this.settingsService.updateSettings([
|
|
81
|
+
{ path: ['agents', 'routingPolicy'], value: validated },
|
|
82
|
+
]);
|
|
82
83
|
this.policy = validated;
|
|
83
|
-
this.lastModified = Date.now();
|
|
84
84
|
log.info('Updated AI routing policy', { policy: validated }, 'routing-policy');
|
|
85
85
|
}
|
|
86
86
|
/**
|
|
87
87
|
* Resolve provider for a specific operation
|
|
88
88
|
*/
|
|
89
|
+
async getEffectivePolicy() {
|
|
90
|
+
const base = await this.getPolicy();
|
|
91
|
+
const operations = {
|
|
92
|
+
...base.operations,
|
|
93
|
+
};
|
|
94
|
+
for (const op of ROUTABLE_OPERATIONS) {
|
|
95
|
+
if (!operations[op]) {
|
|
96
|
+
const resolved = await this.resolveProviderForOperation(op);
|
|
97
|
+
operations[op] = {
|
|
98
|
+
provider: resolved.provider,
|
|
99
|
+
...(resolved.model ? { model: resolved.model } : {}),
|
|
100
|
+
...(resolved.options ? { options: resolved.options } : {}),
|
|
101
|
+
...(resolved.fallbacks && resolved.fallbacks.length
|
|
102
|
+
? { fallback: resolved.fallbacks }
|
|
103
|
+
: {}),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
defaultProvider: base.defaultProvider,
|
|
109
|
+
operations,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
89
112
|
async resolveProviderForOperation(operation, overrides) {
|
|
90
113
|
const policy = await this.getPolicy();
|
|
91
114
|
const operationConfig = policy.operations?.[operation];
|
|
92
115
|
// Pull provider/model defaults from settings per operation
|
|
93
|
-
const settings =
|
|
116
|
+
const settings = this.settingsService.getSettings();
|
|
94
117
|
const coding = settings.agents?.codingAgent;
|
|
95
118
|
const judge = settings.agents?.judgeAgent;
|
|
96
119
|
const settingsProviderModel = (() => {
|
|
@@ -98,61 +121,54 @@ export class RoutingPolicyManager {
|
|
|
98
121
|
case 'ai_codereview':
|
|
99
122
|
return { provider: judge?.provider, model: judge?.model };
|
|
100
123
|
case 'execute_task':
|
|
101
|
-
case '
|
|
124
|
+
case 'quality_checks':
|
|
102
125
|
case 'ai_merge':
|
|
126
|
+
case 'improve_task':
|
|
103
127
|
default:
|
|
104
128
|
return { provider: coding?.provider, model: coding?.model };
|
|
105
129
|
}
|
|
106
130
|
})();
|
|
107
131
|
// Start with operation-specific config or fall back to settings, then default policy
|
|
108
132
|
const baseProvider = operationConfig?.provider || settingsProviderModel.provider || policy.defaultProvider;
|
|
109
|
-
const baseModel = operationConfig?.model
|
|
133
|
+
const baseModel = operationConfig?.model
|
|
134
|
+
? operationConfig.model
|
|
135
|
+
: operationConfig?.provider
|
|
136
|
+
? undefined
|
|
137
|
+
: settingsProviderModel.model;
|
|
110
138
|
const baseOptions = operationConfig?.options;
|
|
111
139
|
const baseFallbacks = operationConfig?.fallback || [];
|
|
112
140
|
// Apply overrides
|
|
141
|
+
const overrideProviderOnly = overrides?.provider && overrides.model === undefined;
|
|
113
142
|
const resolved = {
|
|
114
143
|
provider: overrides?.provider || baseProvider,
|
|
115
|
-
model: overrides?.model || baseModel,
|
|
144
|
+
model: overrideProviderOnly ? undefined : overrides?.model || baseModel,
|
|
116
145
|
options: overrides?.options || baseOptions,
|
|
117
146
|
fallbacks: overrides?.fallbacks || baseFallbacks,
|
|
118
147
|
};
|
|
119
148
|
log.debug(`Resolved provider for ${operation}`, { operation, resolved }, 'routing-policy');
|
|
120
149
|
return resolved;
|
|
121
150
|
}
|
|
122
|
-
/**
|
|
123
|
-
* Set default provider
|
|
124
|
-
*/
|
|
125
151
|
async setDefaultProvider(provider) {
|
|
126
152
|
await this.updatePolicy({ defaultProvider: provider });
|
|
127
153
|
}
|
|
128
|
-
/**
|
|
129
|
-
* Set operation-specific routing
|
|
130
|
-
*/
|
|
131
154
|
async setOperationConfig(operation, config) {
|
|
132
155
|
const currentPolicy = await this.getPolicy();
|
|
133
|
-
|
|
134
|
-
operations
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
});
|
|
156
|
+
const operations = {
|
|
157
|
+
...(currentPolicy.operations ?? {}),
|
|
158
|
+
[operation]: config,
|
|
159
|
+
};
|
|
160
|
+
await this.updatePolicy({ operations });
|
|
139
161
|
}
|
|
140
|
-
/**
|
|
141
|
-
* Validate policy against available providers
|
|
142
|
-
*/
|
|
143
162
|
validatePolicy(policy, availableProviders) {
|
|
144
163
|
const errors = [];
|
|
145
|
-
// Check default provider
|
|
146
164
|
if (!availableProviders.has(policy.defaultProvider)) {
|
|
147
165
|
errors.push(`Default provider '${policy.defaultProvider}' is not available`);
|
|
148
166
|
}
|
|
149
|
-
// Check operation providers
|
|
150
167
|
if (policy.operations) {
|
|
151
168
|
for (const [operation, config] of Object.entries(policy.operations)) {
|
|
152
169
|
if (!availableProviders.has(config.provider)) {
|
|
153
170
|
errors.push(`Provider '${config.provider}' for operation '${operation}' is not available`);
|
|
154
171
|
}
|
|
155
|
-
// Check fallback providers
|
|
156
172
|
if (config.fallback) {
|
|
157
173
|
for (const fallbackProvider of config.fallback) {
|
|
158
174
|
if (!availableProviders.has(fallbackProvider)) {
|
|
@@ -164,83 +180,17 @@ export class RoutingPolicyManager {
|
|
|
164
180
|
}
|
|
165
181
|
return errors;
|
|
166
182
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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
|
-
},
|
|
183
|
+
buildPolicyFromSettings() {
|
|
184
|
+
const settings = this.settingsService.getSettings();
|
|
185
|
+
const settingsPolicy = settings.agents?.routingPolicy;
|
|
186
|
+
const defaultProvider = settingsPolicy?.defaultProvider || settings.agents.defaultProvider || 'claude-code';
|
|
187
|
+
const operations = settingsPolicy?.operations
|
|
188
|
+
? { ...settingsPolicy.operations }
|
|
189
|
+
: undefined;
|
|
190
|
+
const policy = {
|
|
191
|
+
defaultProvider,
|
|
192
|
+
...(operations ? { operations } : {}),
|
|
233
193
|
};
|
|
234
|
-
|
|
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;
|
|
194
|
+
return RoutingPolicySchema.parse(policy);
|
|
245
195
|
}
|
|
246
196
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { describe, it, expect, beforeAll, afterAll, afterEach } from 'vitest';
|
|
2
|
+
import { RoutingPolicyManager, } from './routing-policy.js';
|
|
3
|
+
import { getSettingsService } from '../settings-service.js';
|
|
4
|
+
const settingsService = getSettingsService();
|
|
5
|
+
const BASE_POLICY = {
|
|
6
|
+
defaultProvider: 'claude-code',
|
|
7
|
+
operations: {},
|
|
8
|
+
};
|
|
9
|
+
let originalRoutingPolicy;
|
|
10
|
+
async function setRoutingPolicy(value) {
|
|
11
|
+
const result = await settingsService.updateSettings([
|
|
12
|
+
{ path: ['agents', 'routingPolicy'], value },
|
|
13
|
+
]);
|
|
14
|
+
if (!result.success) {
|
|
15
|
+
throw new Error(`Failed to set routing policy: ${JSON.stringify(result.errors)}`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
describe('RoutingPolicyManager resolve precedence', () => {
|
|
19
|
+
beforeAll(async () => {
|
|
20
|
+
await settingsService.initialize();
|
|
21
|
+
const manager = new RoutingPolicyManager();
|
|
22
|
+
originalRoutingPolicy = await manager.getPolicy();
|
|
23
|
+
await setRoutingPolicy(BASE_POLICY);
|
|
24
|
+
});
|
|
25
|
+
afterEach(async () => {
|
|
26
|
+
await setRoutingPolicy(BASE_POLICY);
|
|
27
|
+
});
|
|
28
|
+
afterAll(async () => {
|
|
29
|
+
await setRoutingPolicy(originalRoutingPolicy);
|
|
30
|
+
});
|
|
31
|
+
it('falls back to settings when no operation config or overrides', async () => {
|
|
32
|
+
const rpm = new RoutingPolicyManager();
|
|
33
|
+
const resolved = await rpm.resolveProviderForOperation('execute_task');
|
|
34
|
+
expect(resolved.provider).toBe('claude-code');
|
|
35
|
+
expect(resolved.model).toBe('claude-sonnet-4-20250514');
|
|
36
|
+
});
|
|
37
|
+
it('uses operation-specific policy when provided', async () => {
|
|
38
|
+
const rpm = new RoutingPolicyManager();
|
|
39
|
+
await rpm.updatePolicy({
|
|
40
|
+
operations: {
|
|
41
|
+
ai_merge: { provider: 'codex', model: 'gpt-5' },
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
const resolved = await rpm.resolveProviderForOperation('ai_merge');
|
|
45
|
+
expect(resolved.provider).toBe('codex');
|
|
46
|
+
expect(resolved.model).toBe('gpt-5');
|
|
47
|
+
});
|
|
48
|
+
it('when override sets provider without model, does not inherit mismatched model', async () => {
|
|
49
|
+
const rpm = new RoutingPolicyManager();
|
|
50
|
+
await rpm.updatePolicy({ operations: {} });
|
|
51
|
+
const override = { provider: 'codex' };
|
|
52
|
+
const resolved = await rpm.resolveProviderForOperation('execute_task', override);
|
|
53
|
+
expect(resolved.provider).toBe('codex');
|
|
54
|
+
expect(resolved.model).toBeUndefined();
|
|
55
|
+
});
|
|
56
|
+
it('override with provider+model takes full precedence', async () => {
|
|
57
|
+
const rpm = new RoutingPolicyManager();
|
|
58
|
+
const override = { provider: 'codex', model: 'gpt-5' };
|
|
59
|
+
const resolved = await rpm.resolveProviderForOperation('execute_task', override);
|
|
60
|
+
expect(resolved.provider).toBe('codex');
|
|
61
|
+
expect(resolved.model).toBe('gpt-5');
|
|
62
|
+
});
|
|
63
|
+
});
|
|
@@ -40,7 +40,19 @@ export declare function buildAIRoutes(options: {
|
|
|
40
40
|
*/
|
|
41
41
|
getRoutingPolicy: import("@trpc/server").TRPCQueryProcedure<{
|
|
42
42
|
input: void;
|
|
43
|
-
output:
|
|
43
|
+
output: {
|
|
44
|
+
defaultProvider: string;
|
|
45
|
+
operations?: Partial<Record<"execute_task" | "quality_checks" | "improve_task" | "ai_merge" | "ai_codereview", {
|
|
46
|
+
provider: string;
|
|
47
|
+
options?: {
|
|
48
|
+
temperature?: number | undefined;
|
|
49
|
+
maxTokens?: number | undefined;
|
|
50
|
+
tools?: string[] | undefined;
|
|
51
|
+
} | undefined;
|
|
52
|
+
model?: string | undefined;
|
|
53
|
+
fallback?: string[] | undefined;
|
|
54
|
+
}>> | undefined;
|
|
55
|
+
};
|
|
44
56
|
meta: object;
|
|
45
57
|
}>;
|
|
46
58
|
/**
|
|
@@ -50,7 +62,7 @@ export declare function buildAIRoutes(options: {
|
|
|
50
62
|
input: {
|
|
51
63
|
policy: {
|
|
52
64
|
defaultProvider?: string | undefined;
|
|
53
|
-
operations?: Partial<Record<"execute_task" | "improve_task" | "ai_merge" | "ai_codereview", {
|
|
65
|
+
operations?: Partial<Record<"execute_task" | "quality_checks" | "improve_task" | "ai_merge" | "ai_codereview", {
|
|
54
66
|
provider: string;
|
|
55
67
|
options?: {
|
|
56
68
|
temperature?: number | undefined;
|
|
@@ -86,7 +98,7 @@ export declare function buildAIRoutes(options: {
|
|
|
86
98
|
*/
|
|
87
99
|
setOperationConfig: import("@trpc/server").TRPCMutationProcedure<{
|
|
88
100
|
input: {
|
|
89
|
-
operation: "execute_task" | "improve_task" | "ai_merge" | "ai_codereview";
|
|
101
|
+
operation: "execute_task" | "quality_checks" | "improve_task" | "ai_merge" | "ai_codereview";
|
|
90
102
|
config: {
|
|
91
103
|
provider: string;
|
|
92
104
|
options?: {
|
|
@@ -20,7 +20,7 @@ const SetDefaultProviderInputSchema = z.object({
|
|
|
20
20
|
provider: z.string(),
|
|
21
21
|
});
|
|
22
22
|
const SetOperationConfigInputSchema = z.object({
|
|
23
|
-
operation: z.enum(['execute_task', '
|
|
23
|
+
operation: z.enum(['execute_task', 'quality_checks', 'ai_codereview', 'ai_merge', 'improve_task']),
|
|
24
24
|
config: OperationConfigSchema,
|
|
25
25
|
});
|
|
26
26
|
const ValidateProvidersInputSchema = z.object({
|
|
@@ -92,16 +92,17 @@ export function buildAIRoutes(options) {
|
|
|
92
92
|
*/
|
|
93
93
|
getRoutingPolicy: publicProcedure.query(async () => {
|
|
94
94
|
try {
|
|
95
|
-
const
|
|
96
|
-
if (!
|
|
97
|
-
// Return default policy if none exists
|
|
95
|
+
const rpm = agentService.getRoutingPolicyManager?.();
|
|
96
|
+
if (!rpm) {
|
|
98
97
|
const settings = getSettingsService().getSettings();
|
|
99
|
-
|
|
98
|
+
return {
|
|
100
99
|
defaultProvider: settings.agents.defaultProvider || 'claude-code',
|
|
101
100
|
operations: {},
|
|
102
101
|
};
|
|
103
|
-
return defaultPolicy;
|
|
104
102
|
}
|
|
103
|
+
const policy = (rpm.getEffectivePolicy
|
|
104
|
+
? await rpm.getEffectivePolicy()
|
|
105
|
+
: await rpm.getPolicy());
|
|
105
106
|
log.debug('Retrieved routing policy', { policy }, 'ai-router');
|
|
106
107
|
return policy;
|
|
107
108
|
}
|
|
@@ -67,7 +67,7 @@ export declare function buildExecutionRoutes({ taskService, vibingOrchestrator }
|
|
|
67
67
|
input: {
|
|
68
68
|
title: string;
|
|
69
69
|
type: "feature" | "bug" | "chore" | "refactor" | "test" | "doc";
|
|
70
|
-
priority: "
|
|
70
|
+
priority: "low" | "medium" | "high";
|
|
71
71
|
content: string;
|
|
72
72
|
taskId: string;
|
|
73
73
|
executionId?: string | undefined;
|
|
@@ -6,7 +6,7 @@ export declare function buildTaskRoutes(taskService: TaskService, gitService?: G
|
|
|
6
6
|
status?: "review" | "in-progress" | "backlog" | "done" | undefined;
|
|
7
7
|
type?: "feature" | "bug" | "chore" | "refactor" | "test" | "doc" | undefined;
|
|
8
8
|
tags?: string[] | undefined;
|
|
9
|
-
priority?: "
|
|
9
|
+
priority?: "low" | "medium" | "high" | undefined;
|
|
10
10
|
search?: string | undefined;
|
|
11
11
|
includeDeleted?: boolean | undefined;
|
|
12
12
|
onlyDeleted?: boolean | undefined;
|
|
@@ -31,7 +31,7 @@ export declare function buildTaskRoutes(taskService: TaskService, gitService?: G
|
|
|
31
31
|
tags?: string[] | undefined;
|
|
32
32
|
due_date?: string | undefined;
|
|
33
33
|
assignee?: string[] | undefined;
|
|
34
|
-
priority?: "
|
|
34
|
+
priority?: "low" | "medium" | "high" | undefined;
|
|
35
35
|
comments?: string[] | undefined;
|
|
36
36
|
};
|
|
37
37
|
output: import("../../types/index.js").Task;
|
|
@@ -46,7 +46,7 @@ export declare function buildTaskRoutes(taskService: TaskService, gitService?: G
|
|
|
46
46
|
tags?: string[] | undefined;
|
|
47
47
|
due_date?: string | undefined;
|
|
48
48
|
assignee?: string[] | undefined;
|
|
49
|
-
priority?: "
|
|
49
|
+
priority?: "low" | "medium" | "high" | undefined;
|
|
50
50
|
comments?: string[] | undefined;
|
|
51
51
|
content?: string | undefined;
|
|
52
52
|
deleted_at?: string | undefined;
|
|
@@ -19,6 +19,10 @@ export declare function buildWorkflowRoutes({ vibingOrchestrator }: Deps): {
|
|
|
19
19
|
provider?: string | undefined;
|
|
20
20
|
model?: string | undefined;
|
|
21
21
|
} | undefined;
|
|
22
|
+
quality_checks?: {
|
|
23
|
+
provider?: string | undefined;
|
|
24
|
+
model?: string | undefined;
|
|
25
|
+
} | undefined;
|
|
22
26
|
improve_task?: {
|
|
23
27
|
provider?: string | undefined;
|
|
24
28
|
model?: string | undefined;
|
|
@@ -55,6 +59,10 @@ export declare function buildWorkflowRoutes({ vibingOrchestrator }: Deps): {
|
|
|
55
59
|
provider?: string | undefined;
|
|
56
60
|
model?: string | undefined;
|
|
57
61
|
} | undefined;
|
|
62
|
+
quality_checks?: {
|
|
63
|
+
provider?: string | undefined;
|
|
64
|
+
model?: string | undefined;
|
|
65
|
+
} | undefined;
|
|
58
66
|
improve_task?: {
|
|
59
67
|
provider?: string | undefined;
|
|
60
68
|
model?: string | undefined;
|
|
@@ -10,9 +10,10 @@ export function buildWorkflowRoutes({ vibingOrchestrator }) {
|
|
|
10
10
|
const WorkflowAIRoutingOverrides = z
|
|
11
11
|
.object({
|
|
12
12
|
execute_task: OperationOverrideSchema.optional(),
|
|
13
|
-
|
|
13
|
+
quality_checks: OperationOverrideSchema.optional(),
|
|
14
14
|
ai_codereview: OperationOverrideSchema.optional(),
|
|
15
15
|
ai_merge: OperationOverrideSchema.optional(),
|
|
16
|
+
improve_task: OperationOverrideSchema.optional(),
|
|
16
17
|
})
|
|
17
18
|
.partial();
|
|
18
19
|
const WorkflowConfig = z.object({
|
|
@@ -36,7 +36,7 @@ export declare const TaskCreateSchema: z.ZodObject<{
|
|
|
36
36
|
type: "feature" | "bug" | "chore" | "refactor" | "test" | "doc";
|
|
37
37
|
tags: string[];
|
|
38
38
|
assignee: string[];
|
|
39
|
-
priority: "
|
|
39
|
+
priority: "low" | "medium" | "high";
|
|
40
40
|
comments: string[];
|
|
41
41
|
content: string;
|
|
42
42
|
id?: string | undefined;
|
|
@@ -50,7 +50,7 @@ export declare const TaskCreateSchema: z.ZodObject<{
|
|
|
50
50
|
tags?: string[] | undefined;
|
|
51
51
|
due_date?: string | undefined;
|
|
52
52
|
assignee?: string[] | undefined;
|
|
53
|
-
priority?: "
|
|
53
|
+
priority?: "low" | "medium" | "high" | undefined;
|
|
54
54
|
comments?: string[] | undefined;
|
|
55
55
|
}>;
|
|
56
56
|
export declare const TaskUpdateSchema: z.ZodObject<{
|
|
@@ -74,7 +74,7 @@ export declare const TaskUpdateSchema: z.ZodObject<{
|
|
|
74
74
|
tags?: string[] | undefined;
|
|
75
75
|
due_date?: string | undefined;
|
|
76
76
|
assignee?: string[] | undefined;
|
|
77
|
-
priority?: "
|
|
77
|
+
priority?: "low" | "medium" | "high" | undefined;
|
|
78
78
|
comments?: string[] | undefined;
|
|
79
79
|
content?: string | undefined;
|
|
80
80
|
deleted_at?: string | undefined;
|
|
@@ -86,7 +86,7 @@ export declare const TaskUpdateSchema: z.ZodObject<{
|
|
|
86
86
|
tags?: string[] | undefined;
|
|
87
87
|
due_date?: string | undefined;
|
|
88
88
|
assignee?: string[] | undefined;
|
|
89
|
-
priority?: "
|
|
89
|
+
priority?: "low" | "medium" | "high" | undefined;
|
|
90
90
|
comments?: string[] | undefined;
|
|
91
91
|
content?: string | undefined;
|
|
92
92
|
deleted_at?: string | undefined;
|
|
@@ -103,7 +103,7 @@ export declare const TaskFilterSchema: z.ZodObject<{
|
|
|
103
103
|
status?: "review" | "in-progress" | "backlog" | "done" | undefined;
|
|
104
104
|
type?: "feature" | "bug" | "chore" | "refactor" | "test" | "doc" | undefined;
|
|
105
105
|
tags?: string[] | undefined;
|
|
106
|
-
priority?: "
|
|
106
|
+
priority?: "low" | "medium" | "high" | undefined;
|
|
107
107
|
search?: string | undefined;
|
|
108
108
|
includeDeleted?: boolean | undefined;
|
|
109
109
|
onlyDeleted?: boolean | undefined;
|
|
@@ -111,7 +111,7 @@ export declare const TaskFilterSchema: z.ZodObject<{
|
|
|
111
111
|
status?: "review" | "in-progress" | "backlog" | "done" | undefined;
|
|
112
112
|
type?: "feature" | "bug" | "chore" | "refactor" | "test" | "doc" | undefined;
|
|
113
113
|
tags?: string[] | undefined;
|
|
114
|
-
priority?: "
|
|
114
|
+
priority?: "low" | "medium" | "high" | undefined;
|
|
115
115
|
search?: string | undefined;
|
|
116
116
|
includeDeleted?: boolean | undefined;
|
|
117
117
|
onlyDeleted?: boolean | undefined;
|
|
@@ -117,14 +117,26 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
117
117
|
}>;
|
|
118
118
|
getRoutingPolicy: import("@trpc/server").TRPCQueryProcedure<{
|
|
119
119
|
input: void;
|
|
120
|
-
output:
|
|
120
|
+
output: {
|
|
121
|
+
defaultProvider: string;
|
|
122
|
+
operations?: Partial<Record<"execute_task" | "quality_checks" | "improve_task" | "ai_merge" | "ai_codereview", {
|
|
123
|
+
provider: string;
|
|
124
|
+
options?: {
|
|
125
|
+
temperature?: number | undefined;
|
|
126
|
+
maxTokens?: number | undefined;
|
|
127
|
+
tools?: string[] | undefined;
|
|
128
|
+
} | undefined;
|
|
129
|
+
model?: string | undefined;
|
|
130
|
+
fallback?: string[] | undefined;
|
|
131
|
+
}>> | undefined;
|
|
132
|
+
};
|
|
121
133
|
meta: object;
|
|
122
134
|
}>;
|
|
123
135
|
updateRoutingPolicy: import("@trpc/server").TRPCMutationProcedure<{
|
|
124
136
|
input: {
|
|
125
137
|
policy: {
|
|
126
138
|
defaultProvider?: string | undefined;
|
|
127
|
-
operations?: Partial<Record<"execute_task" | "improve_task" | "ai_merge" | "ai_codereview", {
|
|
139
|
+
operations?: Partial<Record<"execute_task" | "quality_checks" | "improve_task" | "ai_merge" | "ai_codereview", {
|
|
128
140
|
provider: string;
|
|
129
141
|
options?: {
|
|
130
142
|
temperature?: number | undefined;
|
|
@@ -154,7 +166,7 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
154
166
|
}>;
|
|
155
167
|
setOperationConfig: import("@trpc/server").TRPCMutationProcedure<{
|
|
156
168
|
input: {
|
|
157
|
-
operation: "execute_task" | "improve_task" | "ai_merge" | "ai_codereview";
|
|
169
|
+
operation: "execute_task" | "quality_checks" | "improve_task" | "ai_merge" | "ai_codereview";
|
|
158
170
|
config: {
|
|
159
171
|
provider: string;
|
|
160
172
|
options?: {
|
|
@@ -347,6 +359,10 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
347
359
|
provider?: string | undefined;
|
|
348
360
|
model?: string | undefined;
|
|
349
361
|
} | undefined;
|
|
362
|
+
quality_checks?: {
|
|
363
|
+
provider?: string | undefined;
|
|
364
|
+
model?: string | undefined;
|
|
365
|
+
} | undefined;
|
|
350
366
|
improve_task?: {
|
|
351
367
|
provider?: string | undefined;
|
|
352
368
|
model?: string | undefined;
|
|
@@ -383,6 +399,10 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
383
399
|
provider?: string | undefined;
|
|
384
400
|
model?: string | undefined;
|
|
385
401
|
} | undefined;
|
|
402
|
+
quality_checks?: {
|
|
403
|
+
provider?: string | undefined;
|
|
404
|
+
model?: string | undefined;
|
|
405
|
+
} | undefined;
|
|
386
406
|
improve_task?: {
|
|
387
407
|
provider?: string | undefined;
|
|
388
408
|
model?: string | undefined;
|
|
@@ -747,7 +767,7 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
747
767
|
input: {
|
|
748
768
|
title: string;
|
|
749
769
|
type: "feature" | "bug" | "chore" | "refactor" | "test" | "doc";
|
|
750
|
-
priority: "
|
|
770
|
+
priority: "low" | "medium" | "high";
|
|
751
771
|
content: string;
|
|
752
772
|
taskId: string;
|
|
753
773
|
executionId?: string | undefined;
|
|
@@ -779,7 +799,7 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
779
799
|
status?: "review" | "in-progress" | "backlog" | "done" | undefined;
|
|
780
800
|
type?: "feature" | "bug" | "chore" | "refactor" | "test" | "doc" | undefined;
|
|
781
801
|
tags?: string[] | undefined;
|
|
782
|
-
priority?: "
|
|
802
|
+
priority?: "low" | "medium" | "high" | undefined;
|
|
783
803
|
search?: string | undefined;
|
|
784
804
|
includeDeleted?: boolean | undefined;
|
|
785
805
|
onlyDeleted?: boolean | undefined;
|
|
@@ -804,7 +824,7 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
804
824
|
tags?: string[] | undefined;
|
|
805
825
|
due_date?: string | undefined;
|
|
806
826
|
assignee?: string[] | undefined;
|
|
807
|
-
priority?: "
|
|
827
|
+
priority?: "low" | "medium" | "high" | undefined;
|
|
808
828
|
comments?: string[] | undefined;
|
|
809
829
|
};
|
|
810
830
|
output: import("../../types/index.js").Task;
|
|
@@ -819,7 +839,7 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
819
839
|
tags?: string[] | undefined;
|
|
820
840
|
due_date?: string | undefined;
|
|
821
841
|
assignee?: string[] | undefined;
|
|
822
|
-
priority?: "
|
|
842
|
+
priority?: "low" | "medium" | "high" | undefined;
|
|
823
843
|
comments?: string[] | undefined;
|
|
824
844
|
content?: string | undefined;
|
|
825
845
|
deleted_at?: string | undefined;
|