vibeman 0.0.5 → 0.0.7
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/api.js +43 -0
- package/dist/index.js +227 -104
- package/dist/ui/assets/index-C_kQPI1m.js +9 -0
- package/dist/ui/index.html +12 -0
- package/package.json +13 -46
- package/README.md +0 -12
- package/dist/runtime/api/.tsbuildinfo +0 -1
- package/dist/runtime/api/agent/agent-service.d.ts +0 -229
- package/dist/runtime/api/agent/agent-service.js +0 -963
- package/dist/runtime/api/agent/ai-providers/amp-cli-provider.d.ts +0 -38
- package/dist/runtime/api/agent/ai-providers/amp-cli-provider.js +0 -268
- package/dist/runtime/api/agent/ai-providers/claude-code-adapter.d.ts +0 -61
- package/dist/runtime/api/agent/ai-providers/claude-code-adapter.js +0 -362
- package/dist/runtime/api/agent/ai-providers/codex-cli-provider.d.ts +0 -36
- package/dist/runtime/api/agent/ai-providers/codex-cli-provider.js +0 -375
- package/dist/runtime/api/agent/ai-providers/gemini-cli-provider.d.ts +0 -24
- package/dist/runtime/api/agent/ai-providers/gemini-cli-provider.js +0 -291
- package/dist/runtime/api/agent/ai-providers/index.d.ts +0 -9
- package/dist/runtime/api/agent/ai-providers/index.js +0 -9
- package/dist/runtime/api/agent/ai-providers/types.d.ts +0 -185
- package/dist/runtime/api/agent/ai-providers/types.js +0 -5
- package/dist/runtime/api/agent/amp-cli-provider.test.d.ts +0 -1
- package/dist/runtime/api/agent/amp-cli-provider.test.js +0 -99
- package/dist/runtime/api/agent/codex-cli-provider.test.d.ts +0 -1
- package/dist/runtime/api/agent/codex-cli-provider.test.js +0 -172
- package/dist/runtime/api/agent/core-agent-service.d.ts +0 -119
- package/dist/runtime/api/agent/core-agent-service.js +0 -267
- package/dist/runtime/api/agent/parsers.d.ts +0 -16
- package/dist/runtime/api/agent/parsers.js +0 -308
- package/dist/runtime/api/agent/prompt-service.d.ts +0 -30
- package/dist/runtime/api/agent/prompt-service.js +0 -452
- package/dist/runtime/api/agent/prompt-service.test.d.ts +0 -1
- package/dist/runtime/api/agent/prompt-service.test.js +0 -265
- package/dist/runtime/api/agent/routing-policy.d.ts +0 -171
- package/dist/runtime/api/agent/routing-policy.js +0 -196
- package/dist/runtime/api/agent/routing-policy.test.d.ts +0 -1
- package/dist/runtime/api/agent/routing-policy.test.js +0 -63
- package/dist/runtime/api/api/router-helpers.d.ts +0 -32
- package/dist/runtime/api/api/router-helpers.js +0 -31
- package/dist/runtime/api/api/routers/ai.d.ts +0 -200
- package/dist/runtime/api/api/routers/ai.js +0 -396
- package/dist/runtime/api/api/routers/executions.d.ts +0 -93
- package/dist/runtime/api/api/routers/executions.js +0 -94
- package/dist/runtime/api/api/routers/git.d.ts +0 -45
- package/dist/runtime/api/api/routers/git.js +0 -35
- package/dist/runtime/api/api/routers/provider-config.d.ts +0 -199
- package/dist/runtime/api/api/routers/provider-config.js +0 -252
- package/dist/runtime/api/api/routers/settings.d.ts +0 -158
- package/dist/runtime/api/api/routers/settings.js +0 -129
- package/dist/runtime/api/api/routers/tasks.d.ts +0 -141
- package/dist/runtime/api/api/routers/tasks.js +0 -238
- package/dist/runtime/api/api/routers/workflows.d.ts +0 -275
- package/dist/runtime/api/api/routers/workflows.js +0 -311
- package/dist/runtime/api/api/routers/worktrees.d.ts +0 -101
- package/dist/runtime/api/api/routers/worktrees.js +0 -80
- package/dist/runtime/api/api/trpc.d.ts +0 -118
- package/dist/runtime/api/api/trpc.js +0 -34
- package/dist/runtime/api/index.d.ts +0 -9
- package/dist/runtime/api/index.js +0 -117
- package/dist/runtime/api/lib/id-generator.d.ts +0 -70
- package/dist/runtime/api/lib/id-generator.js +0 -123
- package/dist/runtime/api/lib/local-config.d.ts +0 -335
- package/dist/runtime/api/lib/local-config.js +0 -304
- package/dist/runtime/api/lib/logger.d.ts +0 -11
- package/dist/runtime/api/lib/logger.js +0 -188
- package/dist/runtime/api/lib/provider-detection.d.ts +0 -61
- package/dist/runtime/api/lib/provider-detection.js +0 -326
- package/dist/runtime/api/lib/server/agent-service-singleton.d.ts +0 -6
- package/dist/runtime/api/lib/server/agent-service-singleton.js +0 -27
- package/dist/runtime/api/lib/server/bootstrap.d.ts +0 -38
- package/dist/runtime/api/lib/server/bootstrap.js +0 -197
- package/dist/runtime/api/lib/server/git-service-singleton.d.ts +0 -6
- package/dist/runtime/api/lib/server/git-service-singleton.js +0 -47
- package/dist/runtime/api/lib/server/project-root.d.ts +0 -2
- package/dist/runtime/api/lib/server/project-root.js +0 -61
- package/dist/runtime/api/lib/server/task-service-singleton.d.ts +0 -7
- package/dist/runtime/api/lib/server/task-service-singleton.js +0 -58
- package/dist/runtime/api/lib/server/vibeman-info.d.ts +0 -5
- package/dist/runtime/api/lib/server/vibeman-info.js +0 -85
- package/dist/runtime/api/lib/server/vibing-orchestrator-singleton.d.ts +0 -7
- package/dist/runtime/api/lib/server/vibing-orchestrator-singleton.js +0 -57
- package/dist/runtime/api/lib/trpc/server.d.ts +0 -965
- package/dist/runtime/api/lib/trpc/server.js +0 -11
- package/dist/runtime/api/lib/trpc/ws-server.d.ts +0 -8
- package/dist/runtime/api/lib/trpc/ws-server.js +0 -33
- package/dist/runtime/api/persistence/database-service.d.ts +0 -14
- package/dist/runtime/api/persistence/database-service.js +0 -74
- package/dist/runtime/api/persistence/execution-log-persistence.d.ts +0 -90
- package/dist/runtime/api/persistence/execution-log-persistence.js +0 -426
- package/dist/runtime/api/persistence/execution-log-persistence.test.d.ts +0 -1
- package/dist/runtime/api/persistence/execution-log-persistence.test.js +0 -170
- package/dist/runtime/api/router.d.ts +0 -968
- package/dist/runtime/api/router.js +0 -34
- package/dist/runtime/api/settings-service.d.ts +0 -110
- package/dist/runtime/api/settings-service.js +0 -678
- package/dist/runtime/api/tasks/file-watcher.d.ts +0 -23
- package/dist/runtime/api/tasks/file-watcher.js +0 -88
- package/dist/runtime/api/tasks/task-file-parser.d.ts +0 -14
- package/dist/runtime/api/tasks/task-file-parser.js +0 -180
- package/dist/runtime/api/tasks/task-service.d.ts +0 -36
- package/dist/runtime/api/tasks/task-service.js +0 -173
- package/dist/runtime/api/tasks/task-updater.d.ts +0 -62
- package/dist/runtime/api/tasks/task-updater.js +0 -260
- package/dist/runtime/api/tasks/task-updater.test.d.ts +0 -1
- package/dist/runtime/api/tasks/task-updater.test.js +0 -303
- package/dist/runtime/api/types/index.d.ts +0 -186
- package/dist/runtime/api/types/index.js +0 -1
- package/dist/runtime/api/types/settings.d.ts +0 -105
- package/dist/runtime/api/types/settings.js +0 -2
- package/dist/runtime/api/types.d.ts +0 -2
- package/dist/runtime/api/types.js +0 -1
- package/dist/runtime/api/utils/env.d.ts +0 -6
- package/dist/runtime/api/utils/env.js +0 -12
- package/dist/runtime/api/utils/stripNextEnv.d.ts +0 -7
- package/dist/runtime/api/utils/stripNextEnv.js +0 -22
- package/dist/runtime/api/utils/title-slug.d.ts +0 -6
- package/dist/runtime/api/utils/title-slug.js +0 -77
- package/dist/runtime/api/utils/url.d.ts +0 -2
- package/dist/runtime/api/utils/url.js +0 -19
- package/dist/runtime/api/vcs/git-history-service.d.ts +0 -57
- package/dist/runtime/api/vcs/git-history-service.js +0 -228
- package/dist/runtime/api/vcs/git-service.d.ts +0 -136
- package/dist/runtime/api/vcs/git-service.js +0 -307
- package/dist/runtime/api/vcs/worktree-service.d.ts +0 -93
- package/dist/runtime/api/vcs/worktree-service.js +0 -518
- package/dist/runtime/api/vcs/worktree-service.test.d.ts +0 -1
- package/dist/runtime/api/vcs/worktree-service.test.js +0 -20
- package/dist/runtime/api/workflows/quality-pipeline.d.ts +0 -58
- package/dist/runtime/api/workflows/quality-pipeline.js +0 -401
- package/dist/runtime/api/workflows/vibing-orchestrator.d.ts +0 -406
- package/dist/runtime/api/workflows/vibing-orchestrator.js +0 -2462
- package/dist/runtime/api/workflows/workflow-effects.d.ts +0 -45
- package/dist/runtime/api/workflows/workflow-effects.js +0 -49
- package/dist/runtime/api/workflows/workflow-reconciler.d.ts +0 -65
- package/dist/runtime/api/workflows/workflow-reconciler.js +0 -226
- package/dist/runtime/api/workflows/workflow-reducer.d.ts +0 -26
- package/dist/runtime/api/workflows/workflow-reducer.js +0 -288
- package/dist/runtime/api/workflows/workflow-reducer.test.d.ts +0 -1
- package/dist/runtime/api/workflows/workflow-reducer.test.js +0 -247
- package/dist/runtime/api/workflows/workflow-schema.d.ts +0 -546
- package/dist/runtime/api/workflows/workflow-schema.js +0 -256
- package/dist/runtime/web/.next/BUILD_ID +0 -1
- package/dist/runtime/web/.next/app-build-manifest.json +0 -66
- package/dist/runtime/web/.next/app-path-routes-manifest.json +0 -8
- package/dist/runtime/web/.next/build-manifest.json +0 -33
- package/dist/runtime/web/.next/package.json +0 -1
- package/dist/runtime/web/.next/prerender-manifest.json +0 -61
- package/dist/runtime/web/.next/react-loadable-manifest.json +0 -8
- package/dist/runtime/web/.next/required-server-files.json +0 -334
- package/dist/runtime/web/.next/routes-manifest.json +0 -70
- package/dist/runtime/web/.next/server/app/.vibeman/assets/images/[...path]/route.js +0 -1
- package/dist/runtime/web/.next/server/app/.vibeman/assets/images/[...path]/route.js.nft.json +0 -1
- package/dist/runtime/web/.next/server/app/.vibeman/assets/images/[...path]/route_client-reference-manifest.js +0 -1
- package/dist/runtime/web/.next/server/app/_not-found/page.js +0 -2
- package/dist/runtime/web/.next/server/app/_not-found/page.js.nft.json +0 -1
- package/dist/runtime/web/.next/server/app/_not-found/page_client-reference-manifest.js +0 -1
- package/dist/runtime/web/.next/server/app/_not-found.html +0 -7
- package/dist/runtime/web/.next/server/app/_not-found.meta +0 -8
- package/dist/runtime/web/.next/server/app/_not-found.rsc +0 -22
- package/dist/runtime/web/.next/server/app/api/health/route.js +0 -1
- package/dist/runtime/web/.next/server/app/api/health/route.js.nft.json +0 -1
- package/dist/runtime/web/.next/server/app/api/health/route_client-reference-manifest.js +0 -1
- package/dist/runtime/web/.next/server/app/api/images/[...path]/route.js +0 -1
- package/dist/runtime/web/.next/server/app/api/images/[...path]/route.js.nft.json +0 -1
- package/dist/runtime/web/.next/server/app/api/images/[...path]/route_client-reference-manifest.js +0 -1
- package/dist/runtime/web/.next/server/app/api/upload/route.js +0 -1
- package/dist/runtime/web/.next/server/app/api/upload/route.js.nft.json +0 -1
- package/dist/runtime/web/.next/server/app/api/upload/route_client-reference-manifest.js +0 -1
- package/dist/runtime/web/.next/server/app/index.html +0 -7
- package/dist/runtime/web/.next/server/app/index.meta +0 -7
- package/dist/runtime/web/.next/server/app/index.rsc +0 -27
- package/dist/runtime/web/.next/server/app/page.js +0 -112
- package/dist/runtime/web/.next/server/app/page.js.nft.json +0 -1
- package/dist/runtime/web/.next/server/app/page_client-reference-manifest.js +0 -1
- package/dist/runtime/web/.next/server/app-paths-manifest.json +0 -8
- package/dist/runtime/web/.next/server/chunks/210.js +0 -1
- package/dist/runtime/web/.next/server/chunks/291.js +0 -18
- package/dist/runtime/web/.next/server/chunks/552.js +0 -22
- package/dist/runtime/web/.next/server/chunks/780.js +0 -1
- package/dist/runtime/web/.next/server/chunks/905.js +0 -6
- package/dist/runtime/web/.next/server/chunks/98.js +0 -1
- package/dist/runtime/web/.next/server/functions-config-manifest.json +0 -4
- package/dist/runtime/web/.next/server/middleware-build-manifest.js +0 -1
- package/dist/runtime/web/.next/server/middleware-manifest.json +0 -6
- package/dist/runtime/web/.next/server/middleware-react-loadable-manifest.js +0 -1
- package/dist/runtime/web/.next/server/next-font-manifest.js +0 -1
- package/dist/runtime/web/.next/server/next-font-manifest.json +0 -1
- package/dist/runtime/web/.next/server/pages/404.html +0 -7
- package/dist/runtime/web/.next/server/pages/500.html +0 -1
- package/dist/runtime/web/.next/server/pages/_app.js +0 -1
- package/dist/runtime/web/.next/server/pages/_app.js.nft.json +0 -1
- package/dist/runtime/web/.next/server/pages/_document.js +0 -1
- package/dist/runtime/web/.next/server/pages/_document.js.nft.json +0 -1
- package/dist/runtime/web/.next/server/pages/_error.js +0 -19
- package/dist/runtime/web/.next/server/pages/_error.js.nft.json +0 -1
- package/dist/runtime/web/.next/server/pages-manifest.json +0 -6
- package/dist/runtime/web/.next/server/server-reference-manifest.js +0 -1
- package/dist/runtime/web/.next/server/server-reference-manifest.json +0 -1
- package/dist/runtime/web/.next/server/webpack-runtime.js +0 -1
- package/dist/runtime/web/.next/static/LJFZk_8tvKFN_Ee4HqUuM/_buildManifest.js +0 -1
- package/dist/runtime/web/.next/static/LJFZk_8tvKFN_Ee4HqUuM/_ssgManifest.js +0 -1
- package/dist/runtime/web/.next/static/chunks/05c91ade-7d09b2b280adffd1.js +0 -1
- package/dist/runtime/web/.next/static/chunks/201-51bef3fa8c832e2e.js +0 -1
- package/dist/runtime/web/.next/static/chunks/524-89747ed9b0294f8a.js +0 -1
- package/dist/runtime/web/.next/static/chunks/554-8bec6e9cca6acc67.js +0 -1
- package/dist/runtime/web/.next/static/chunks/764.86e9503a69d45a85.js +0 -1
- package/dist/runtime/web/.next/static/chunks/7ab4dc20-239138e0ae7af24a.js +0 -1
- package/dist/runtime/web/.next/static/chunks/905-342391e3d3a3678f.js +0 -20
- package/dist/runtime/web/.next/static/chunks/a8a5ce16-4edea7df2d9b544a.js +0 -79
- package/dist/runtime/web/.next/static/chunks/ad74d572-4c1b162e2c15acaa.js +0 -1
- package/dist/runtime/web/.next/static/chunks/app/.vibeman/assets/images/[...path]/route-7b752a8641f96c1f.js +0 -1
- package/dist/runtime/web/.next/static/chunks/app/_not-found/page-34e66b251c2b5044.js +0 -1
- package/dist/runtime/web/.next/static/chunks/app/api/health/route-7b752a8641f96c1f.js +0 -1
- package/dist/runtime/web/.next/static/chunks/app/api/images/[...path]/route-7b752a8641f96c1f.js +0 -1
- package/dist/runtime/web/.next/static/chunks/app/api/upload/route-7b752a8641f96c1f.js +0 -1
- package/dist/runtime/web/.next/static/chunks/app/layout-df9ac93cb02b2385.js +0 -1
- package/dist/runtime/web/.next/static/chunks/app/page-6610743f7de5f92a.js +0 -1
- package/dist/runtime/web/.next/static/chunks/c25e0690-e9b798b8de667da1.js +0 -1
- package/dist/runtime/web/.next/static/chunks/framework-57157ec4d37f64aa.js +0 -1
- package/dist/runtime/web/.next/static/chunks/main-app-156cc0c60371bd78.js +0 -1
- package/dist/runtime/web/.next/static/chunks/main-df25d367c47b1fec.js +0 -1
- package/dist/runtime/web/.next/static/chunks/pages/_app-9f629a5e1131d19f.js +0 -1
- package/dist/runtime/web/.next/static/chunks/pages/_error-9238238274c7efcd.js +0 -1
- package/dist/runtime/web/.next/static/chunks/polyfills-42372ed130431b0a.js +0 -1
- package/dist/runtime/web/.next/static/chunks/webpack-cd50e39b423d1808.js +0 -1
- package/dist/runtime/web/.next/static/css/2728291c68f99cb1.css +0 -3
- package/dist/runtime/web/.next/static/css/4fbf378a264bd4ea.css +0 -1
- package/dist/runtime/web/.next/static/css/521bd69cc298cd1a.css +0 -1
- package/dist/runtime/web/.next/static/css/537e22821e101b87.css +0 -1
- package/dist/runtime/web/.next/static/media/19cfc7226ec3afaa-s.woff2 +0 -0
- package/dist/runtime/web/.next/static/media/21350d82a1f187e9-s.woff2 +0 -0
- package/dist/runtime/web/.next/static/media/8e9860b6e62d6359-s.woff2 +0 -0
- package/dist/runtime/web/.next/static/media/ba9851c3c22cd980-s.woff2 +0 -0
- package/dist/runtime/web/.next/static/media/c5fe6dc8356a8c31-s.woff2 +0 -0
- package/dist/runtime/web/.next/static/media/df0a9ae256c0569c-s.woff2 +0 -0
- package/dist/runtime/web/.next/static/media/e4af272ccee01ff0-s.p.woff2 +0 -0
- package/dist/runtime/web/package.json +0 -65
- package/dist/runtime/web/server.js +0 -44
- package/dist/tsconfig.tsbuildinfo +0 -1
|
@@ -1,963 +0,0 @@
|
|
|
1
|
-
import { EventEmitter } from 'events';
|
|
2
|
-
import { WorktreeService } from '../vcs/worktree-service.js';
|
|
3
|
-
import { ExecutionLogPersistence } from '../persistence/execution-log-persistence.js';
|
|
4
|
-
import { log } from '../lib/logger.js';
|
|
5
|
-
import { generateId } from '../lib/id-generator.js';
|
|
6
|
-
import { getProjectRoot } from '../lib/server/project-root.js';
|
|
7
|
-
import { CoreAgentService } from './core-agent-service.js';
|
|
8
|
-
import { ClaudeCodeAdapter, CodexCliProvider, GeminiCliProvider, AmpCliProvider, } from './ai-providers/index.js';
|
|
9
|
-
import { PromptService } from './prompt-service.js';
|
|
10
|
-
import { RoutingPolicyManager, } from './routing-policy.js';
|
|
11
|
-
import { isTestEnv } from '../utils/env.js';
|
|
12
|
-
import { parseImprovementResult, parseReviewResult } from './parsers.js';
|
|
13
|
-
import { getSettingsService } from '../settings-service.js';
|
|
14
|
-
const fullTools = [
|
|
15
|
-
'Bash',
|
|
16
|
-
'Edit',
|
|
17
|
-
'Glob',
|
|
18
|
-
'Grep',
|
|
19
|
-
'MultiEdit',
|
|
20
|
-
'NotebookEdit',
|
|
21
|
-
'NotebookRead',
|
|
22
|
-
'Read',
|
|
23
|
-
'Task',
|
|
24
|
-
'TodoWrite',
|
|
25
|
-
'WebSearch',
|
|
26
|
-
'WebFetch',
|
|
27
|
-
'Write',
|
|
28
|
-
];
|
|
29
|
-
export class AgentService extends EventEmitter {
|
|
30
|
-
constructor(taskService, projectRoot = getProjectRoot(), worktreeService) {
|
|
31
|
-
super();
|
|
32
|
-
this.taskService = taskService;
|
|
33
|
-
this.projectRoot = projectRoot;
|
|
34
|
-
// Keep only minimal execution context locally; defer status/messages to CoreAgentService
|
|
35
|
-
this.executionRegistry = new Map();
|
|
36
|
-
this.initialized = false;
|
|
37
|
-
// Track lightweight status for generic (non-core) executions
|
|
38
|
-
this.genericStates = new Map();
|
|
39
|
-
this.completionInterceptors = new Map();
|
|
40
|
-
// Centralized default tool sets per operation
|
|
41
|
-
// TODO: need to be more granular, based on the task type and the toolset available for the AI provider
|
|
42
|
-
this.defaultTools = {
|
|
43
|
-
execute_task: fullTools,
|
|
44
|
-
quality_checks: fullTools,
|
|
45
|
-
improve_task: fullTools,
|
|
46
|
-
code_review: fullTools,
|
|
47
|
-
ai_merge: fullTools,
|
|
48
|
-
};
|
|
49
|
-
// Centralized system prompts for all AI operations
|
|
50
|
-
this.systemPrompts = {
|
|
51
|
-
execute_task: {
|
|
52
|
-
base: 'You are an expert software engineer. Follow best practices, write clean maintainable code, and ensure your implementation is robust and well-tested.',
|
|
53
|
-
rerun: 'You are continuing a rerun of this task. Use prior context and focus on fixes.',
|
|
54
|
-
},
|
|
55
|
-
improve_task: 'You are an expert at improving task specifications. Detect the language of the input task content and respond in that SAME language for all markdown sections. Keep enum fields (type, priority) in English. Respond with ONLY a valid JSON object containing type, priority, and content fields. No additional text or formatting.',
|
|
56
|
-
ai_codereview: 'You are a senior developer performing thorough code review. Focus on quality, security, and best practices. Respond with ONLY a valid JSON object. Ultrathink.',
|
|
57
|
-
ai_merge: 'You are an expert at resolving merge conflicts and integrating code changes. Ensure clean merges that maintain code consistency and functionality.',
|
|
58
|
-
};
|
|
59
|
-
this.worktreeService = worktreeService || new WorktreeService(projectRoot);
|
|
60
|
-
this.logPersistence = new ExecutionLogPersistence();
|
|
61
|
-
this.initialize();
|
|
62
|
-
// Log streaming updates for verification in dev environments
|
|
63
|
-
if (!isTestEnv()) {
|
|
64
|
-
this.on('executionUpdated', (update) => {
|
|
65
|
-
const exec = this.getExecutionStatus(update.executionId);
|
|
66
|
-
const last = exec?.logs?.[exec.logs.length - 1];
|
|
67
|
-
if (last) {
|
|
68
|
-
log.info('Execution update', { executionId: update.executionId, message: last }, 'agent-service');
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
// Initialize worktree manager
|
|
73
|
-
this.initializeWorktrees();
|
|
74
|
-
}
|
|
75
|
-
// ============ Generic (non-AI) execution helpers for local tasks (e.g., validation) ============
|
|
76
|
-
/**
|
|
77
|
-
* Start a generic execution that is not backed by an AI provider.
|
|
78
|
-
* This enables persistent logs and status queries via existing APIs.
|
|
79
|
-
*/
|
|
80
|
-
async startGenericExecution(taskId, options) {
|
|
81
|
-
const executionId = generateId('exec');
|
|
82
|
-
const startTime = new Date().toISOString();
|
|
83
|
-
const workingDirectory = options?.workingDirectory || this.projectRoot;
|
|
84
|
-
// Register minimal context so status queries work
|
|
85
|
-
this.executionRegistry.set(executionId, { taskId, workingDirectory, startTime });
|
|
86
|
-
const initial = {
|
|
87
|
-
id: executionId,
|
|
88
|
-
taskId,
|
|
89
|
-
status: 'pending',
|
|
90
|
-
startTime,
|
|
91
|
-
logs: [],
|
|
92
|
-
workingDirectory,
|
|
93
|
-
};
|
|
94
|
-
await this.logPersistence.startExecution(initial, options?.workflowId);
|
|
95
|
-
this.genericStates.set(executionId, { status: 'running' });
|
|
96
|
-
// Emit an update to notify subscribers
|
|
97
|
-
this.emit('executionUpdated', { executionId, status: 'running', logs: [] });
|
|
98
|
-
return executionId;
|
|
99
|
-
}
|
|
100
|
-
/** Append a log line to a generic execution and notify listeners (best-effort). */
|
|
101
|
-
async logGenericExecution(executionId, message, level = 'info', data) {
|
|
102
|
-
await this.logPersistence.logMessage(executionId, message, level, data);
|
|
103
|
-
try {
|
|
104
|
-
const logs = (await this.logPersistence.readExecutionLogs(executionId)).logs.map((e) => e.message);
|
|
105
|
-
this.emit('executionUpdated', { executionId, status: 'running', logs });
|
|
106
|
-
}
|
|
107
|
-
catch {
|
|
108
|
-
// ignore
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
/** Mark a generic execution as completed/failed/cancelled and notify listeners. */
|
|
112
|
-
async completeGenericExecution(executionId, status, error) {
|
|
113
|
-
await this.logPersistence.completeExecution(executionId, status, error);
|
|
114
|
-
this.genericStates.set(executionId, { status, endTime: new Date().toISOString(), error });
|
|
115
|
-
try {
|
|
116
|
-
const logs = (await this.logPersistence.readExecutionLogs(executionId)).logs.map((e) => e.message);
|
|
117
|
-
this.emit('executionUpdated', { executionId, status, logs, error });
|
|
118
|
-
}
|
|
119
|
-
catch {
|
|
120
|
-
this.emit('executionUpdated', { executionId, status, logs: [], error });
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
getAgentConfig() {
|
|
124
|
-
try {
|
|
125
|
-
const settings = getSettingsService().getSettings();
|
|
126
|
-
return {
|
|
127
|
-
executionTimeout: 30 * 60 * 1000, // 30 minutes fixed
|
|
128
|
-
defaultProvider: settings.agents.defaultProvider || 'claude-code',
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
catch {
|
|
132
|
-
return { executionTimeout: 30 * 60 * 1000, defaultProvider: 'claude-code' };
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
initialize() {
|
|
136
|
-
if (this.initialized)
|
|
137
|
-
return;
|
|
138
|
-
// Initialize core agent service with settings
|
|
139
|
-
const agentConfig = this.getAgentConfig();
|
|
140
|
-
this.coreAgentService = new CoreAgentService({
|
|
141
|
-
defaultProvider: agentConfig.defaultProvider,
|
|
142
|
-
executionTimeout: agentConfig.executionTimeout,
|
|
143
|
-
});
|
|
144
|
-
// Register Claude Code adapter
|
|
145
|
-
const claudeAdapter = new ClaudeCodeAdapter({
|
|
146
|
-
defaultWorkingDirectory: this.projectRoot,
|
|
147
|
-
defaultModel: 'claude-sonnet-4-20250514',
|
|
148
|
-
});
|
|
149
|
-
this.coreAgentService.registerProvider('claude-code', claudeAdapter);
|
|
150
|
-
// Register Codex CLI provider (not default)
|
|
151
|
-
const codexProvider = new CodexCliProvider({
|
|
152
|
-
defaultWorkingDirectory: this.projectRoot,
|
|
153
|
-
});
|
|
154
|
-
this.coreAgentService.registerProvider('codex', codexProvider);
|
|
155
|
-
// Register Gemini CLI provider (not default)
|
|
156
|
-
const geminiProvider = new GeminiCliProvider({
|
|
157
|
-
defaultWorkingDirectory: this.projectRoot,
|
|
158
|
-
});
|
|
159
|
-
this.coreAgentService.registerProvider('gemini', geminiProvider);
|
|
160
|
-
// Register Amp CLI provider (not default)
|
|
161
|
-
const ampProvider = new AmpCliProvider({
|
|
162
|
-
defaultWorkingDirectory: this.projectRoot,
|
|
163
|
-
});
|
|
164
|
-
this.coreAgentService.registerProvider('amp', ampProvider);
|
|
165
|
-
// Initialize prompt service
|
|
166
|
-
this.promptService = new PromptService(this.projectRoot, this.taskService);
|
|
167
|
-
// Initialize routing policy manager
|
|
168
|
-
this.routingPolicyManager = new RoutingPolicyManager();
|
|
169
|
-
// Set up event forwarding
|
|
170
|
-
this.setupEventForwarding();
|
|
171
|
-
this.initialized = true;
|
|
172
|
-
}
|
|
173
|
-
setupEventForwarding() {
|
|
174
|
-
// Persist every message and enrich realtime UI for non-assistant types
|
|
175
|
-
this.coreAgentService.on('executionMessage', (data) => {
|
|
176
|
-
const state = this.coreAgentService.getExecutionStatus(data.executionId);
|
|
177
|
-
const status = state?.status || 'running';
|
|
178
|
-
const summary = this.summarizeMessage(data.message);
|
|
179
|
-
void this.logPersistence.logMessage(data.executionId, summary, data.message.type === 'error' ? 'error' : 'info', { raw: data.message });
|
|
180
|
-
const logs = this.coreAgentService
|
|
181
|
-
.getExecutionMessages(data.executionId)
|
|
182
|
-
.map((m) => this.summarizeMessage(m));
|
|
183
|
-
this.emit('executionUpdated', {
|
|
184
|
-
executionId: data.executionId,
|
|
185
|
-
status,
|
|
186
|
-
logs,
|
|
187
|
-
});
|
|
188
|
-
});
|
|
189
|
-
this.coreAgentService.on('executionCompleted', (data) => {
|
|
190
|
-
const interceptor = this.completionInterceptors.get(data.executionId);
|
|
191
|
-
if (interceptor) {
|
|
192
|
-
this.completionInterceptors.delete(data.executionId);
|
|
193
|
-
Promise.resolve(interceptor(data))
|
|
194
|
-
.then((handled) => {
|
|
195
|
-
if (handled)
|
|
196
|
-
return;
|
|
197
|
-
void this.logPersistence.completeExecution(data.executionId, 'completed');
|
|
198
|
-
const logs = this.coreAgentService
|
|
199
|
-
.getExecutionMessages(data.executionId)
|
|
200
|
-
.map((m) => this.summarizeMessage(m));
|
|
201
|
-
this.emit('executionUpdated', {
|
|
202
|
-
executionId: data.executionId,
|
|
203
|
-
status: 'completed',
|
|
204
|
-
logs,
|
|
205
|
-
});
|
|
206
|
-
})
|
|
207
|
-
.catch((error) => log.error('Completion interceptor failed', error, 'agent-service'));
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
void this.logPersistence.completeExecution(data.executionId, 'completed');
|
|
211
|
-
const logs = this.coreAgentService
|
|
212
|
-
.getExecutionMessages(data.executionId)
|
|
213
|
-
.map((m) => this.summarizeMessage(m));
|
|
214
|
-
this.emit('executionUpdated', {
|
|
215
|
-
executionId: data.executionId,
|
|
216
|
-
status: 'completed',
|
|
217
|
-
logs,
|
|
218
|
-
});
|
|
219
|
-
});
|
|
220
|
-
this.coreAgentService.on('executionFailed', (data) => {
|
|
221
|
-
this.completionInterceptors.delete(data.executionId);
|
|
222
|
-
void this.logPersistence.completeExecution(data.executionId, 'failed', data.error);
|
|
223
|
-
const logs = this.coreAgentService
|
|
224
|
-
.getExecutionMessages(data.executionId)
|
|
225
|
-
.map((m) => this.summarizeMessage(m));
|
|
226
|
-
this.emit('executionUpdated', {
|
|
227
|
-
executionId: data.executionId,
|
|
228
|
-
status: 'failed',
|
|
229
|
-
logs,
|
|
230
|
-
error: data.error,
|
|
231
|
-
});
|
|
232
|
-
});
|
|
233
|
-
this.coreAgentService.on('executionCancelled', (data) => {
|
|
234
|
-
this.completionInterceptors.delete(data.executionId);
|
|
235
|
-
void this.logPersistence.completeExecution(data.executionId, 'cancelled');
|
|
236
|
-
const logs = this.coreAgentService
|
|
237
|
-
.getExecutionMessages(data.executionId)
|
|
238
|
-
.map((m) => this.summarizeMessage(m));
|
|
239
|
-
this.emit('executionUpdated', {
|
|
240
|
-
executionId: data.executionId,
|
|
241
|
-
status: 'cancelled',
|
|
242
|
-
logs,
|
|
243
|
-
});
|
|
244
|
-
});
|
|
245
|
-
}
|
|
246
|
-
registerCompletionInterceptor(executionId, handler) {
|
|
247
|
-
this.completionInterceptors.set(executionId, handler);
|
|
248
|
-
}
|
|
249
|
-
clearCompletionInterceptor(executionId) {
|
|
250
|
-
this.completionInterceptors.delete(executionId);
|
|
251
|
-
}
|
|
252
|
-
async finalizeExecution(executionId, status, error, logs) {
|
|
253
|
-
this.completionInterceptors.delete(executionId);
|
|
254
|
-
if (status === 'completed') {
|
|
255
|
-
await this.logPersistence.completeExecution(executionId, 'completed');
|
|
256
|
-
}
|
|
257
|
-
else {
|
|
258
|
-
await this.logPersistence.completeExecution(executionId, status, error);
|
|
259
|
-
}
|
|
260
|
-
const finalLogs = logs ?? this.getExecutionLogs(executionId);
|
|
261
|
-
this.emit('executionUpdated', {
|
|
262
|
-
executionId,
|
|
263
|
-
status,
|
|
264
|
-
logs: finalLogs,
|
|
265
|
-
error,
|
|
266
|
-
});
|
|
267
|
-
}
|
|
268
|
-
/**
|
|
269
|
-
* Create a concise, human-readable line from a structured execution message
|
|
270
|
-
*/
|
|
271
|
-
summarizeMessage(msg) {
|
|
272
|
-
const ts = new Date(msg.timestamp || Date.now()).toLocaleTimeString();
|
|
273
|
-
switch (msg.type) {
|
|
274
|
-
case 'init':
|
|
275
|
-
return `[${ts}] Init: session started`;
|
|
276
|
-
case 'assistant':
|
|
277
|
-
return typeof msg.content === 'string' ? msg.content : '[assistant message]';
|
|
278
|
-
case 'tool_use': {
|
|
279
|
-
const tool = msg.tool || 'tool';
|
|
280
|
-
const input = msg.input ? JSON.stringify(msg.input) : '';
|
|
281
|
-
return `[${ts}] TOOL ${tool} ${input}`;
|
|
282
|
-
}
|
|
283
|
-
case 'result': {
|
|
284
|
-
const success = msg.success;
|
|
285
|
-
return `[${ts}] Result: ${success ? 'success' : 'error'}`;
|
|
286
|
-
}
|
|
287
|
-
case 'system':
|
|
288
|
-
return `[${ts}] SYSTEM: ${typeof msg.content === 'string' ? msg.content : ''}`;
|
|
289
|
-
case 'error':
|
|
290
|
-
return `[${ts}] ERROR: ${String(msg.content || '')}`;
|
|
291
|
-
default:
|
|
292
|
-
// Ensure template-literal receives a string; avoid `never` type in lint
|
|
293
|
-
return `[${ts}] ${String(msg.type)}`;
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
/**
|
|
297
|
-
* Initialize worktree manager
|
|
298
|
-
*/
|
|
299
|
-
async initializeWorktrees() {
|
|
300
|
-
try {
|
|
301
|
-
await this.worktreeService.initialize();
|
|
302
|
-
}
|
|
303
|
-
catch (error) {
|
|
304
|
-
log.warn('Failed to initialize WorktreeService', error, 'agent-service');
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
/**
|
|
308
|
-
* Execute a task using AI in an isolated worktree
|
|
309
|
-
*/
|
|
310
|
-
async executeTask(taskId, workflowId, options) {
|
|
311
|
-
const task = this.taskService.getTask(taskId);
|
|
312
|
-
if (!task) {
|
|
313
|
-
throw new Error(`Task ${taskId} not found`);
|
|
314
|
-
}
|
|
315
|
-
const executionId = generateId('exec');
|
|
316
|
-
// Pre-register minimal context for this execution
|
|
317
|
-
const initialStart = new Date().toISOString();
|
|
318
|
-
this.executionRegistry.set(executionId, {
|
|
319
|
-
taskId,
|
|
320
|
-
workingDirectory: this.projectRoot,
|
|
321
|
-
startTime: initialStart,
|
|
322
|
-
});
|
|
323
|
-
// Start persistent logging with workflow context
|
|
324
|
-
const initialExecution = {
|
|
325
|
-
id: executionId,
|
|
326
|
-
taskId,
|
|
327
|
-
status: 'pending',
|
|
328
|
-
startTime: initialStart,
|
|
329
|
-
logs: [],
|
|
330
|
-
workingDirectory: this.projectRoot,
|
|
331
|
-
};
|
|
332
|
-
await this.logPersistence.startExecution(initialExecution, workflowId);
|
|
333
|
-
// Update task status to in-progress
|
|
334
|
-
await this.taskService.updateTask(taskId, { status: 'in-progress' });
|
|
335
|
-
// Fire-and-forget creation event if needed by listeners
|
|
336
|
-
// Include workflowId so orchestrators can associate execution to workflows
|
|
337
|
-
this.emit('executionCreated', { executionId, taskId, workflowId });
|
|
338
|
-
// Create worktree for isolated execution
|
|
339
|
-
let worktree;
|
|
340
|
-
let workingDirectory = this.projectRoot;
|
|
341
|
-
try {
|
|
342
|
-
worktree = await this.worktreeService.createWorktree(task);
|
|
343
|
-
if (worktree?.path) {
|
|
344
|
-
workingDirectory = worktree.path;
|
|
345
|
-
log.info('Task executing in worktree', { taskId, workingDirectory }, 'agent-service');
|
|
346
|
-
// Update registry with actual working directory
|
|
347
|
-
this.executionRegistry.set(executionId, {
|
|
348
|
-
taskId,
|
|
349
|
-
workingDirectory,
|
|
350
|
-
startTime: initialStart,
|
|
351
|
-
});
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
catch (error) {
|
|
355
|
-
const msg = `Failed to create worktree for task ${taskId}: ${error instanceof Error ? error.message : String(error)}`;
|
|
356
|
-
log.error(msg, error, 'agent-service');
|
|
357
|
-
await this.logPersistence.logMessage(executionId, msg, 'error', { taskId, workflowId });
|
|
358
|
-
await this.completeGenericExecution(executionId, 'failed', msg);
|
|
359
|
-
throw new Error(msg);
|
|
360
|
-
}
|
|
361
|
-
// Generate task prompt with workflow configuration for dynamic instructions
|
|
362
|
-
const prompt = await this.promptService.generateTaskPrompt(task, options?.workflowConfig);
|
|
363
|
-
// Build system prompt - use rerun context if available, otherwise base prompt
|
|
364
|
-
let appendSystemPrompt;
|
|
365
|
-
if (options?.rerunContext) {
|
|
366
|
-
appendSystemPrompt = await this.buildRerunSystemPrompt(task, options.rerunContext, workingDirectory);
|
|
367
|
-
}
|
|
368
|
-
else {
|
|
369
|
-
appendSystemPrompt = this.systemPrompts.execute_task.base;
|
|
370
|
-
}
|
|
371
|
-
// Persist prompt for auditing
|
|
372
|
-
void this.logPersistence.logMessage(executionId, 'Prompt prepared', 'info', {
|
|
373
|
-
prompt,
|
|
374
|
-
appendSystemPrompt, // include system/rerun append
|
|
375
|
-
options: { workingDirectory, tools: this.defaultTools.execute_task },
|
|
376
|
-
taskId: task.id,
|
|
377
|
-
workflowId,
|
|
378
|
-
rerunContext: options?.rerunContext,
|
|
379
|
-
});
|
|
380
|
-
await this.executeWithRouting('execute_task', prompt, {
|
|
381
|
-
workingDirectory,
|
|
382
|
-
tools: this.defaultTools.execute_task,
|
|
383
|
-
timeout: 30 * 60 * 1000,
|
|
384
|
-
appendSystemPrompt,
|
|
385
|
-
dangerouslyBypassApprovalsAndSandbox: true,
|
|
386
|
-
executionId,
|
|
387
|
-
metadata: {
|
|
388
|
-
operation: 'execute_task',
|
|
389
|
-
taskId,
|
|
390
|
-
workflowId,
|
|
391
|
-
worktree,
|
|
392
|
-
rerun: !!options?.rerunContext,
|
|
393
|
-
previousExecutionId: options?.rerunContext?.previousExecutionId,
|
|
394
|
-
},
|
|
395
|
-
}, options?.providerOverride);
|
|
396
|
-
return executionId;
|
|
397
|
-
}
|
|
398
|
-
/**
|
|
399
|
-
* Build an appended system prompt with rerun context (reason, logs, failed checks)
|
|
400
|
-
*/
|
|
401
|
-
async buildRerunSystemPrompt(task, rerun, workingDirectory) {
|
|
402
|
-
const parts = [];
|
|
403
|
-
// Start with base system prompt, then add rerun context
|
|
404
|
-
parts.push(this.systemPrompts.execute_task.base);
|
|
405
|
-
parts.push('\n\n' + this.systemPrompts.execute_task.rerun);
|
|
406
|
-
parts.push(`Rerun reason: ${rerun.reason}`);
|
|
407
|
-
if (rerun.attempt)
|
|
408
|
-
parts.push(`Attempt: ${String(rerun.attempt)}`);
|
|
409
|
-
if (rerun.failurePhase)
|
|
410
|
-
parts.push(`Previous failure phase: ${rerun.failurePhase}`);
|
|
411
|
-
parts.push(`Working directory: ${workingDirectory}`);
|
|
412
|
-
// Try to get persisted logs tail if not provided and we have an execution id
|
|
413
|
-
let logs = rerun.lastLogs || [];
|
|
414
|
-
if ((!logs || logs.length === 0) && rerun.previousExecutionId) {
|
|
415
|
-
try {
|
|
416
|
-
const persisted = await this.getPersistedExecutionLogs(rerun.previousExecutionId);
|
|
417
|
-
const tail = persisted.logs.slice(-30).map((e) => e.message || JSON.stringify(e));
|
|
418
|
-
logs = tail;
|
|
419
|
-
}
|
|
420
|
-
catch {
|
|
421
|
-
const mem = this.getExecutionLogs(rerun.previousExecutionId);
|
|
422
|
-
logs = mem.slice(-30);
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
if (logs && logs.length) {
|
|
426
|
-
parts.push('Recent logs (tail):');
|
|
427
|
-
parts.push(logs.join('\n'));
|
|
428
|
-
}
|
|
429
|
-
const q = rerun.qualityResults;
|
|
430
|
-
if (q) {
|
|
431
|
-
const failed = Object.entries(q.details || {}).filter(([, ok]) => !ok);
|
|
432
|
-
if (failed.length) {
|
|
433
|
-
parts.push('Quality checks failed previously:');
|
|
434
|
-
for (const [name] of failed) {
|
|
435
|
-
const output = q.outputs?.[name];
|
|
436
|
-
parts.push(`- ${name}: ${output ? String(output).slice(0, 2000) : 'failed'}`);
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
const review = rerun.aiReview;
|
|
441
|
-
if (review) {
|
|
442
|
-
parts.push('AI code review feedback from previous attempt:');
|
|
443
|
-
if (typeof review.score === 'number') {
|
|
444
|
-
parts.push(`- Quality score: ${review.score}`);
|
|
445
|
-
}
|
|
446
|
-
if (review.summary) {
|
|
447
|
-
parts.push(`- Summary: ${review.summary}`);
|
|
448
|
-
}
|
|
449
|
-
const recs = Array.isArray(review.recommendations)
|
|
450
|
-
? review.recommendations.filter((r) => typeof r === 'string' && r.trim().length)
|
|
451
|
-
: [];
|
|
452
|
-
if (recs.length) {
|
|
453
|
-
parts.push('Recommendations:');
|
|
454
|
-
for (const item of recs.slice(0, 5)) {
|
|
455
|
-
parts.push(` * ${item}`);
|
|
456
|
-
}
|
|
457
|
-
if (recs.length > 5) {
|
|
458
|
-
parts.push(` * ...and ${recs.length - 5} more recommendations`);
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
parts.push('Instruction: Address the reasons above. Avoid repeating prior mistakes. Keep changes scoped and test thoroughly. ');
|
|
463
|
-
return parts.join('\n');
|
|
464
|
-
}
|
|
465
|
-
/**
|
|
466
|
-
* Stop a running execution and cleanup worktree
|
|
467
|
-
*/
|
|
468
|
-
async stopExecution(executionId) {
|
|
469
|
-
const registry = this.executionRegistry.get(executionId);
|
|
470
|
-
if (!registry) {
|
|
471
|
-
throw new Error(`Execution ${executionId} not found`);
|
|
472
|
-
}
|
|
473
|
-
try {
|
|
474
|
-
await this.coreAgentService.stopExecution(executionId);
|
|
475
|
-
}
|
|
476
|
-
catch (error) {
|
|
477
|
-
log.warn('Failed to stop execution', error, 'agent-service');
|
|
478
|
-
}
|
|
479
|
-
const logs = this.coreAgentService
|
|
480
|
-
.getExecutionMessages(executionId)
|
|
481
|
-
.map((m) => this.summarizeMessage(m));
|
|
482
|
-
this.emit('executionUpdated', {
|
|
483
|
-
executionId,
|
|
484
|
-
status: 'cancelled',
|
|
485
|
-
logs,
|
|
486
|
-
});
|
|
487
|
-
}
|
|
488
|
-
/**
|
|
489
|
-
* Get execution status
|
|
490
|
-
*/
|
|
491
|
-
getExecutionStatus(executionId) {
|
|
492
|
-
const registry = this.executionRegistry.get(executionId);
|
|
493
|
-
const state = this.coreAgentService.getExecutionStatus(executionId);
|
|
494
|
-
if (registry && state) {
|
|
495
|
-
const logs = this.coreAgentService
|
|
496
|
-
.getExecutionMessages(executionId)
|
|
497
|
-
.map((m) => this.summarizeMessage(m));
|
|
498
|
-
return {
|
|
499
|
-
id: executionId,
|
|
500
|
-
taskId: registry.taskId,
|
|
501
|
-
status: state.status,
|
|
502
|
-
startTime: registry.startTime || state.startTime,
|
|
503
|
-
endTime: state.endTime,
|
|
504
|
-
logs,
|
|
505
|
-
error: state.error,
|
|
506
|
-
provider: state.provider,
|
|
507
|
-
usage: state.usage,
|
|
508
|
-
workingDirectory: registry.workingDirectory,
|
|
509
|
-
};
|
|
510
|
-
}
|
|
511
|
-
// Fallback for generic/local executions (non-core) using in-memory state
|
|
512
|
-
if (registry && this.genericStates.has(executionId)) {
|
|
513
|
-
const gs = this.genericStates.get(executionId);
|
|
514
|
-
return {
|
|
515
|
-
id: executionId,
|
|
516
|
-
taskId: registry.taskId,
|
|
517
|
-
status: gs.status,
|
|
518
|
-
startTime: registry.startTime,
|
|
519
|
-
endTime: gs.endTime,
|
|
520
|
-
logs: [],
|
|
521
|
-
error: gs.error,
|
|
522
|
-
provider: 'local',
|
|
523
|
-
workingDirectory: registry.workingDirectory,
|
|
524
|
-
};
|
|
525
|
-
}
|
|
526
|
-
return null;
|
|
527
|
-
}
|
|
528
|
-
/**
|
|
529
|
-
* Get execution logs
|
|
530
|
-
*/
|
|
531
|
-
getExecutionLogs(executionId) {
|
|
532
|
-
return this.coreAgentService
|
|
533
|
-
.getExecutionMessages(executionId)
|
|
534
|
-
.map((m) => this.summarizeMessage(m));
|
|
535
|
-
}
|
|
536
|
-
/**
|
|
537
|
-
* Get all executions for a task
|
|
538
|
-
*/
|
|
539
|
-
getTaskExecutions(taskId) {
|
|
540
|
-
const results = [];
|
|
541
|
-
for (const [id, meta] of this.executionRegistry.entries()) {
|
|
542
|
-
if (meta.taskId !== taskId)
|
|
543
|
-
continue;
|
|
544
|
-
const state = this.coreAgentService.getExecutionStatus(id);
|
|
545
|
-
if (!state)
|
|
546
|
-
continue;
|
|
547
|
-
results.push({
|
|
548
|
-
id,
|
|
549
|
-
taskId: meta.taskId,
|
|
550
|
-
status: state.status,
|
|
551
|
-
startTime: meta.startTime || state.startTime,
|
|
552
|
-
endTime: state.endTime,
|
|
553
|
-
logs: [],
|
|
554
|
-
error: state.error,
|
|
555
|
-
workingDirectory: meta.workingDirectory,
|
|
556
|
-
});
|
|
557
|
-
}
|
|
558
|
-
return results.sort((a, b) => new Date(b.startTime).getTime() - new Date(a.startTime).getTime());
|
|
559
|
-
}
|
|
560
|
-
/**
|
|
561
|
-
* Improve task content using AI
|
|
562
|
-
*/
|
|
563
|
-
async improveTaskContent(task, taskData, executionId) {
|
|
564
|
-
const finalExecutionId = executionId || generateId('improve');
|
|
565
|
-
try {
|
|
566
|
-
// Pre-register execution to enable streaming logs and persistence
|
|
567
|
-
if (!this.executionRegistry.has(finalExecutionId)) {
|
|
568
|
-
const startTime = new Date().toISOString();
|
|
569
|
-
this.executionRegistry.set(finalExecutionId, {
|
|
570
|
-
taskId: task.id,
|
|
571
|
-
workingDirectory: this.projectRoot,
|
|
572
|
-
startTime,
|
|
573
|
-
});
|
|
574
|
-
const execRec = {
|
|
575
|
-
id: finalExecutionId,
|
|
576
|
-
taskId: task.id,
|
|
577
|
-
status: 'pending',
|
|
578
|
-
startTime,
|
|
579
|
-
logs: [],
|
|
580
|
-
workingDirectory: this.projectRoot,
|
|
581
|
-
};
|
|
582
|
-
await this.logPersistence.startExecution(execRec);
|
|
583
|
-
}
|
|
584
|
-
// Generate improvement prompt
|
|
585
|
-
const prompt = await this.promptService.generateImprovementPrompt(task, taskData);
|
|
586
|
-
void this.logPersistence.logMessage(finalExecutionId, 'Improvement prompt prepared', 'info', {
|
|
587
|
-
prompt,
|
|
588
|
-
options: { workingDirectory: this.projectRoot, tools: this.defaultTools.improve_task },
|
|
589
|
-
taskId: task.id,
|
|
590
|
-
});
|
|
591
|
-
// Execute improvement with AI
|
|
592
|
-
const result = await this.executeWithRouting('improve_task', prompt, {
|
|
593
|
-
workingDirectory: this.projectRoot,
|
|
594
|
-
appendSystemPrompt: this.systemPrompts.improve_task,
|
|
595
|
-
tools: this.defaultTools.improve_task,
|
|
596
|
-
timeout: 5 * 60 * 1000,
|
|
597
|
-
dangerouslyBypassApprovalsAndSandbox: false,
|
|
598
|
-
executionId: finalExecutionId,
|
|
599
|
-
metadata: {
|
|
600
|
-
operation: 'improve_task',
|
|
601
|
-
taskId: task.id,
|
|
602
|
-
},
|
|
603
|
-
});
|
|
604
|
-
// Parse the result
|
|
605
|
-
const improvement = this.parseImprovementResult(result, taskData);
|
|
606
|
-
const payload = { ...improvement, executionId: finalExecutionId };
|
|
607
|
-
// Ensure registry exists (already handled above) and mark completion in persistence
|
|
608
|
-
await this.logPersistence.completeExecution(finalExecutionId, 'completed');
|
|
609
|
-
return payload;
|
|
610
|
-
}
|
|
611
|
-
catch (error) {
|
|
612
|
-
await this.logPersistence.completeExecution(finalExecutionId, 'failed', error instanceof Error ? error.message : String(error));
|
|
613
|
-
throw new Error(`Failed to improve task content: ${error instanceof Error ? error.message : String(error)}`);
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
/**
|
|
617
|
-
* Perform AI code review of changes in a worktree
|
|
618
|
-
*/
|
|
619
|
-
async aiReviewCode(taskId, reviewContext, options) {
|
|
620
|
-
const task = this.taskService.getTask(taskId);
|
|
621
|
-
if (!task) {
|
|
622
|
-
throw new Error(`Task ${taskId} not found`);
|
|
623
|
-
}
|
|
624
|
-
const worktree = this.worktreeService.getWorktree(taskId);
|
|
625
|
-
if (!worktree) {
|
|
626
|
-
throw new Error(`No worktree found for task ${taskId}`);
|
|
627
|
-
}
|
|
628
|
-
const executionId = options?.executionId ?? generateId('review');
|
|
629
|
-
try {
|
|
630
|
-
// Pre-register execution for streaming/persistence
|
|
631
|
-
if (!this.executionRegistry.has(executionId)) {
|
|
632
|
-
const startTime = new Date().toISOString();
|
|
633
|
-
this.executionRegistry.set(executionId, {
|
|
634
|
-
taskId,
|
|
635
|
-
workingDirectory: this.projectRoot,
|
|
636
|
-
startTime,
|
|
637
|
-
});
|
|
638
|
-
const execRec = {
|
|
639
|
-
id: executionId,
|
|
640
|
-
taskId,
|
|
641
|
-
status: 'pending',
|
|
642
|
-
startTime,
|
|
643
|
-
logs: [],
|
|
644
|
-
workingDirectory: this.projectRoot,
|
|
645
|
-
};
|
|
646
|
-
await this.logPersistence.startExecution(execRec, options?.workflowId);
|
|
647
|
-
}
|
|
648
|
-
// Generate review prompt
|
|
649
|
-
const reviewPrompt = await this.promptService.generateReviewPrompt(task, worktree, reviewContext);
|
|
650
|
-
void this.logPersistence.logMessage(executionId, 'Review prompt prepared', 'info', {
|
|
651
|
-
prompt: reviewPrompt,
|
|
652
|
-
options: { workingDirectory: worktree.path, tools: this.defaultTools.code_review },
|
|
653
|
-
taskId,
|
|
654
|
-
});
|
|
655
|
-
// Execute review with AI
|
|
656
|
-
const result = await this.executeWithRouting('ai_codereview', reviewPrompt, {
|
|
657
|
-
workingDirectory: worktree.path,
|
|
658
|
-
appendSystemPrompt: this.systemPrompts.ai_codereview,
|
|
659
|
-
tools: this.defaultTools.code_review,
|
|
660
|
-
timeout: 10 * 60 * 1000,
|
|
661
|
-
executionId,
|
|
662
|
-
metadata: {
|
|
663
|
-
operation: 'ai_codereview',
|
|
664
|
-
taskId,
|
|
665
|
-
},
|
|
666
|
-
}, options?.overrides);
|
|
667
|
-
// Parse review result
|
|
668
|
-
const review = this.parseReviewResult(result);
|
|
669
|
-
const payload = { executionId, ...review };
|
|
670
|
-
// Ensure registry exists for lookups
|
|
671
|
-
if (!this.executionRegistry.has(executionId)) {
|
|
672
|
-
this.executionRegistry.set(executionId, {
|
|
673
|
-
taskId,
|
|
674
|
-
workingDirectory: this.projectRoot,
|
|
675
|
-
startTime: new Date().toISOString(),
|
|
676
|
-
});
|
|
677
|
-
}
|
|
678
|
-
return payload;
|
|
679
|
-
}
|
|
680
|
-
catch (error) {
|
|
681
|
-
throw new Error(`Failed to review code: ${error instanceof Error ? error.message : String(error)}`);
|
|
682
|
-
}
|
|
683
|
-
}
|
|
684
|
-
/**
|
|
685
|
-
* Perform AI-assisted merge of a task's worktree into the base project
|
|
686
|
-
*/
|
|
687
|
-
async aiMerge(taskId, options) {
|
|
688
|
-
const task = this.taskService.getTask(taskId);
|
|
689
|
-
if (!task) {
|
|
690
|
-
throw new Error(`Task ${taskId} not found`);
|
|
691
|
-
}
|
|
692
|
-
const worktree = this.worktreeService.getWorktree(taskId);
|
|
693
|
-
if (!worktree) {
|
|
694
|
-
throw new Error(`No worktree found for task ${taskId}`);
|
|
695
|
-
}
|
|
696
|
-
const executionId = options?.executionId ?? generateId('merge');
|
|
697
|
-
const workingDirectory = worktree.path || this.projectRoot;
|
|
698
|
-
try {
|
|
699
|
-
// Pre-register execution for streaming/persistence
|
|
700
|
-
if (!this.executionRegistry.has(executionId)) {
|
|
701
|
-
const startTime = new Date().toISOString();
|
|
702
|
-
this.executionRegistry.set(executionId, {
|
|
703
|
-
taskId,
|
|
704
|
-
workingDirectory,
|
|
705
|
-
startTime,
|
|
706
|
-
});
|
|
707
|
-
const execRec = {
|
|
708
|
-
id: executionId,
|
|
709
|
-
taskId,
|
|
710
|
-
status: 'pending',
|
|
711
|
-
startTime,
|
|
712
|
-
logs: [],
|
|
713
|
-
workingDirectory,
|
|
714
|
-
};
|
|
715
|
-
await this.logPersistence.startExecution(execRec, options?.workflowId);
|
|
716
|
-
}
|
|
717
|
-
// Generate merge prompt
|
|
718
|
-
const mergePrompt = await this.promptService.generateAIMergePrompt(task, worktree, options?.baseBranch);
|
|
719
|
-
void this.logPersistence.logMessage(executionId, 'Merge prompt prepared', 'info', {
|
|
720
|
-
prompt: mergePrompt,
|
|
721
|
-
options: { workingDirectory, tools: this.defaultTools.ai_merge },
|
|
722
|
-
taskId,
|
|
723
|
-
baseBranch: options?.baseBranch,
|
|
724
|
-
});
|
|
725
|
-
// Execute merge with AI
|
|
726
|
-
await this.executeWithRouting('ai_merge', mergePrompt, {
|
|
727
|
-
workingDirectory,
|
|
728
|
-
appendSystemPrompt: this.systemPrompts.ai_merge,
|
|
729
|
-
tools: this.defaultTools.ai_merge,
|
|
730
|
-
timeout: 15 * 60 * 1000,
|
|
731
|
-
executionId,
|
|
732
|
-
metadata: {
|
|
733
|
-
operation: 'ai_merge',
|
|
734
|
-
taskId,
|
|
735
|
-
baseBranch: options?.baseBranch,
|
|
736
|
-
},
|
|
737
|
-
}, options?.overrides);
|
|
738
|
-
return { executionId };
|
|
739
|
-
}
|
|
740
|
-
catch (error) {
|
|
741
|
-
throw new Error(`AI merge failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
/**
|
|
745
|
-
* Get persistent execution logs
|
|
746
|
-
*/
|
|
747
|
-
async getPersistedExecutionLogs(executionId) {
|
|
748
|
-
return await this.logPersistence.readExecutionLogs(executionId);
|
|
749
|
-
}
|
|
750
|
-
/**
|
|
751
|
-
* List all execution metadata from persistence
|
|
752
|
-
*/
|
|
753
|
-
async listAllExecutions() {
|
|
754
|
-
return await this.logPersistence.listExecutions();
|
|
755
|
-
}
|
|
756
|
-
/**
|
|
757
|
-
* Clean up old execution logs
|
|
758
|
-
*/
|
|
759
|
-
async cleanupOldExecutionLogs(maxAgeDays = 30) {
|
|
760
|
-
return await this.logPersistence.cleanupOldLogs(maxAgeDays);
|
|
761
|
-
}
|
|
762
|
-
/**
|
|
763
|
-
* Remove execution logs for a specific task
|
|
764
|
-
*/
|
|
765
|
-
async removeExecutionLogsByTask(taskId) {
|
|
766
|
-
return await this.logPersistence.removeExecutionsByTask(taskId);
|
|
767
|
-
}
|
|
768
|
-
/**
|
|
769
|
-
* Get execution log statistics
|
|
770
|
-
*/
|
|
771
|
-
async getExecutionLogStats() {
|
|
772
|
-
return await this.logPersistence.getStats();
|
|
773
|
-
}
|
|
774
|
-
/**
|
|
775
|
-
* Get statistics about executions
|
|
776
|
-
*/
|
|
777
|
-
getExecutionStats() {
|
|
778
|
-
const states = this.coreAgentService.getAllExecutions();
|
|
779
|
-
const worktreeCounts = this.worktreeService.getWorktreeCounts();
|
|
780
|
-
return {
|
|
781
|
-
total: states.length,
|
|
782
|
-
pending: states.filter((e) => e.status === 'pending').length,
|
|
783
|
-
running: states.filter((e) => e.status === 'running').length,
|
|
784
|
-
completed: states.filter((e) => e.status === 'completed').length,
|
|
785
|
-
failed: states.filter((e) => e.status === 'failed').length,
|
|
786
|
-
cancelled: states.filter((e) => e.status === 'cancelled').length,
|
|
787
|
-
worktrees: {
|
|
788
|
-
active: worktreeCounts.active,
|
|
789
|
-
total: worktreeCounts.total,
|
|
790
|
-
enabled: true,
|
|
791
|
-
},
|
|
792
|
-
};
|
|
793
|
-
}
|
|
794
|
-
/**
|
|
795
|
-
* Get worktree service instance
|
|
796
|
-
*/
|
|
797
|
-
getWorktreeService() {
|
|
798
|
-
return this.worktreeService;
|
|
799
|
-
}
|
|
800
|
-
/**
|
|
801
|
-
* Get worktree information for a task
|
|
802
|
-
*/
|
|
803
|
-
getWorktreeInfo(taskId) {
|
|
804
|
-
const wt = this.worktreeService.getWorktree(taskId);
|
|
805
|
-
if (!wt)
|
|
806
|
-
return null;
|
|
807
|
-
const { git, ...rest } = wt;
|
|
808
|
-
return rest;
|
|
809
|
-
}
|
|
810
|
-
/**
|
|
811
|
-
* Get core agent service metrics
|
|
812
|
-
*/
|
|
813
|
-
getCoreMetrics() {
|
|
814
|
-
const states = this.coreAgentService.getAllExecutions();
|
|
815
|
-
return {
|
|
816
|
-
totalExecutions: states.length,
|
|
817
|
-
activeExecutions: states.filter((e) => e.status === 'running').length,
|
|
818
|
-
completedExecutions: states.filter((e) => e.status === 'completed').length,
|
|
819
|
-
failedExecutions: states.filter((e) => e.status === 'failed').length,
|
|
820
|
-
cancelledExecutions: states.filter((e) => e.status === 'cancelled').length,
|
|
821
|
-
};
|
|
822
|
-
}
|
|
823
|
-
/**
|
|
824
|
-
* Get available AI providers
|
|
825
|
-
*/
|
|
826
|
-
getAvailableProviders() {
|
|
827
|
-
return this.coreAgentService.getProviders();
|
|
828
|
-
}
|
|
829
|
-
/**
|
|
830
|
-
* Validate provider setup
|
|
831
|
-
*/
|
|
832
|
-
async validateProviders() {
|
|
833
|
-
return await this.coreAgentService.validateProviders();
|
|
834
|
-
}
|
|
835
|
-
/**
|
|
836
|
-
* Get available models from all providers
|
|
837
|
-
*/
|
|
838
|
-
async getAvailableModels() {
|
|
839
|
-
return await this.coreAgentService.getAvailableModels();
|
|
840
|
-
}
|
|
841
|
-
/**
|
|
842
|
-
* Parse improvement result from AI response
|
|
843
|
-
*/
|
|
844
|
-
parseImprovementResult(result, originalData) {
|
|
845
|
-
return parseImprovementResult(result, originalData);
|
|
846
|
-
}
|
|
847
|
-
/**
|
|
848
|
-
* Parse review result from AI response
|
|
849
|
-
*/
|
|
850
|
-
parseReviewResult(result) {
|
|
851
|
-
return parseReviewResult(result);
|
|
852
|
-
}
|
|
853
|
-
/**
|
|
854
|
-
* Resolve provider for specific operation with failover support
|
|
855
|
-
*/
|
|
856
|
-
async resolveProviderForOperation(operation, overrides) {
|
|
857
|
-
return await this.routingPolicyManager.resolveProviderForOperation(operation, overrides);
|
|
858
|
-
}
|
|
859
|
-
async previewProviderForOperation(operation, overrides) {
|
|
860
|
-
return await this.resolveProviderForOperation(operation, overrides);
|
|
861
|
-
}
|
|
862
|
-
/**
|
|
863
|
-
* Execute with provider resolution and failover
|
|
864
|
-
*/
|
|
865
|
-
async executeWithRouting(operation, prompt, options = {}, overrides) {
|
|
866
|
-
const resolved = await this.resolveProviderForOperation(operation, overrides);
|
|
867
|
-
// Merge resolved options with base options
|
|
868
|
-
const executionOptions = {
|
|
869
|
-
...options,
|
|
870
|
-
...resolved.options,
|
|
871
|
-
model: resolved.model || options.model,
|
|
872
|
-
// Don't override workingDirectory or timeout from base options
|
|
873
|
-
workingDirectory: options.workingDirectory,
|
|
874
|
-
timeout: options.timeout,
|
|
875
|
-
};
|
|
876
|
-
// Persist a clear log line indicating chosen provider/model for this operation
|
|
877
|
-
try {
|
|
878
|
-
const execId = options?.executionId;
|
|
879
|
-
if (execId) {
|
|
880
|
-
await this.logPersistence.logMessage(execId, `Using provider: ${resolved.provider}${resolved.model ? ` (model: ${resolved.model})` : ''}`, 'info', { operation, resolved });
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
catch {
|
|
884
|
-
// best-effort logging; ignore
|
|
885
|
-
}
|
|
886
|
-
// Default maxTokens from settings when not provided (unlimited if omitted)
|
|
887
|
-
try {
|
|
888
|
-
if (executionOptions.maxTokens === undefined) {
|
|
889
|
-
const s = getSettingsService().getSettings();
|
|
890
|
-
const def = operation === 'ai_codereview'
|
|
891
|
-
? s.agents?.judgeAgent?.maxTokens
|
|
892
|
-
: s.agents?.codingAgent?.maxTokens;
|
|
893
|
-
if (typeof def === 'number') {
|
|
894
|
-
executionOptions.maxTokens = def;
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
}
|
|
898
|
-
catch {
|
|
899
|
-
// best effort; ignore if settings unavailable
|
|
900
|
-
}
|
|
901
|
-
const providersToTry = [resolved.provider, ...(resolved.fallbacks || [])];
|
|
902
|
-
// Ensure Codex executes with write permissions when it is in the routing set.
|
|
903
|
-
// Some overrides may omit the flag, so default it on for task execution.
|
|
904
|
-
if (operation === 'execute_task' &&
|
|
905
|
-
providersToTry.includes('codex') &&
|
|
906
|
-
executionOptions.dangerouslyBypassApprovalsAndSandbox === undefined) {
|
|
907
|
-
executionOptions.dangerouslyBypassApprovalsAndSandbox = true;
|
|
908
|
-
}
|
|
909
|
-
let lastError = null;
|
|
910
|
-
for (let i = 0; i < providersToTry.length; i++) {
|
|
911
|
-
const provider = providersToTry[i];
|
|
912
|
-
const isLastProvider = i === providersToTry.length - 1;
|
|
913
|
-
try {
|
|
914
|
-
log.debug(`Attempting execution with provider: ${provider} (attempt ${i + 1}/${providersToTry.length})`, { operation, provider, isLastProvider }, 'agent-service');
|
|
915
|
-
const result = await this.coreAgentService.execute({
|
|
916
|
-
prompt,
|
|
917
|
-
provider,
|
|
918
|
-
options: executionOptions,
|
|
919
|
-
metadata: {
|
|
920
|
-
...options.metadata,
|
|
921
|
-
operation,
|
|
922
|
-
provider,
|
|
923
|
-
model: resolved.model,
|
|
924
|
-
providerAttempt: i + 1,
|
|
925
|
-
totalProviderAttempts: providersToTry.length,
|
|
926
|
-
},
|
|
927
|
-
executionId: options.executionId,
|
|
928
|
-
});
|
|
929
|
-
// Success - log provider usage
|
|
930
|
-
log.info(`Successful execution with provider: ${provider}`, {
|
|
931
|
-
operation,
|
|
932
|
-
provider,
|
|
933
|
-
model: resolved.model,
|
|
934
|
-
attempt: i + 1,
|
|
935
|
-
fallbackUsed: i > 0,
|
|
936
|
-
}, 'agent-service');
|
|
937
|
-
return result;
|
|
938
|
-
}
|
|
939
|
-
catch (error) {
|
|
940
|
-
lastError = error instanceof Error ? error : new Error(String(error));
|
|
941
|
-
log.warn(`Provider ${provider} failed for operation ${operation}`, {
|
|
942
|
-
operation,
|
|
943
|
-
provider,
|
|
944
|
-
attempt: i + 1,
|
|
945
|
-
error: lastError.message,
|
|
946
|
-
willRetry: !isLastProvider,
|
|
947
|
-
}, 'agent-service');
|
|
948
|
-
// If this is not the last provider, continue to next
|
|
949
|
-
if (!isLastProvider) {
|
|
950
|
-
continue;
|
|
951
|
-
}
|
|
952
|
-
}
|
|
953
|
-
}
|
|
954
|
-
// All providers failed
|
|
955
|
-
throw new Error(`All providers failed for operation ${operation}. Last error: ${lastError?.message}`);
|
|
956
|
-
}
|
|
957
|
-
/**
|
|
958
|
-
* Get routing policy manager (for tRPC router access)
|
|
959
|
-
*/
|
|
960
|
-
getRoutingPolicyManager() {
|
|
961
|
-
return this.routingPolicyManager;
|
|
962
|
-
}
|
|
963
|
-
}
|