vectorforce 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/mcp_config.json +17 -0
- package/.codex/config.toml +8 -0
- package/AGENTS.md +43 -0
- package/LICENSE +21 -0
- package/README.md +173 -2
- package/bin/vf.js +756 -0
- package/codex.json +14 -0
- package/commandcode.json +11 -0
- package/musecode.json +18 -0
- package/package.json +39 -4
- package/src/core/errors.js +45 -0
- package/src/core/hash.js +56 -0
- package/src/core/types.js +194 -0
- package/src/index.js +17 -0
- package/src/mcp/harness-config.js +498 -0
- package/src/mcp/mcp-server.js +507 -0
- package/src/operations/briefing-service.js +313 -0
- package/src/operations/checkpoint-service.js +120 -0
- package/src/operations/handoff-service.js +343 -0
- package/src/operations/project-service.js +132 -0
- package/src/operations/reasoning-service.js +356 -0
- package/src/policy/authorizer.js +147 -0
- package/src/providers/code-index-provider.js +285 -0
- package/src/providers/document-provider.js +105 -0
- package/src/providers/git-provider.js +98 -0
- package/src/providers/llm-provider.js +410 -0
- package/src/storage/sqlite-store.js +387 -0
- package/src/studio/server.js +456 -0
- package/studio/dist/assets/index-DTKNBk3V.css +2 -0
- package/studio/dist/assets/index-X-wHcJ6r.js +9 -0
- package/studio/dist/favicon.svg +1 -0
- package/studio/dist/icons.svg +24 -0
- package/studio/dist/index.html +17 -0
|
@@ -0,0 +1,507 @@
|
|
|
1
|
+
import { ErrorCode, VectorForceError } from '../core/errors.js';
|
|
2
|
+
|
|
3
|
+
export const MCP_PROTOCOL_VERSION = '2024-11-05';
|
|
4
|
+
|
|
5
|
+
export const MCP_TOOLS = [
|
|
6
|
+
{
|
|
7
|
+
name: 'save_checkpoint',
|
|
8
|
+
description: 'Capture an immutable, policy-bounded checkpoint of the current engineering task.',
|
|
9
|
+
inputSchema: {
|
|
10
|
+
type: 'object',
|
|
11
|
+
required: ['task_id', 'summary', 'goals', 'constraints'],
|
|
12
|
+
properties: {
|
|
13
|
+
task_id: { type: 'string', description: 'Kebab-case task slug' },
|
|
14
|
+
summary: { type: 'string', description: 'Concise summary of progress and current state' },
|
|
15
|
+
goals: { type: 'array', items: { type: 'string' }, description: 'Active objectives' },
|
|
16
|
+
constraints: { type: 'array', items: { type: 'string' }, description: 'Non-negotiable architectural invariants' },
|
|
17
|
+
decisions: {
|
|
18
|
+
type: 'array',
|
|
19
|
+
items: {
|
|
20
|
+
type: 'object',
|
|
21
|
+
required: ['id', 'title', 'rationale'],
|
|
22
|
+
properties: {
|
|
23
|
+
id: { type: 'string' },
|
|
24
|
+
title: { type: 'string' },
|
|
25
|
+
rationale: { type: 'string' }
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
description: 'Key architectural choices made during this step'
|
|
29
|
+
},
|
|
30
|
+
next_steps: { type: 'array', items: { type: 'string' }, description: 'Immediate upcoming tasks' },
|
|
31
|
+
open_questions: { type: 'array', items: { type: 'string' } },
|
|
32
|
+
include_git: { type: 'boolean', default: true },
|
|
33
|
+
requested_documents: { type: 'array', items: { type: 'string' }, description: 'Allowlisted relative document paths' }
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
name: 'get_resume_briefing',
|
|
39
|
+
description: 'Retrieve an authorized, bounded resume briefing for a specific task and checkpoint.',
|
|
40
|
+
inputSchema: {
|
|
41
|
+
type: 'object',
|
|
42
|
+
required: ['task_id'],
|
|
43
|
+
properties: {
|
|
44
|
+
task_id: { type: 'string' },
|
|
45
|
+
checkpoint_id: { type: 'string', description: 'Optional specific checkpoint ID. Defaults to latest head.' },
|
|
46
|
+
max_tokens: { type: 'integer', default: 4000, maximum: 8000 },
|
|
47
|
+
destination_harness: { type: 'string', enum: ['antigravity', 'command-code', 'muse', 'muse-code', 'pi', 'codex', 'cli'] }
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: 'list_tasks',
|
|
53
|
+
description: 'List engineering tasks registered in the active workspace.',
|
|
54
|
+
inputSchema: {
|
|
55
|
+
type: 'object',
|
|
56
|
+
properties: {
|
|
57
|
+
status: { type: 'string', enum: ['active', 'completed', 'abandoned', 'all'], default: 'active' }
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: 'check_workspace_drift',
|
|
63
|
+
description: 'Compare current workspace Git state and document hashes against the last recorded checkpoint.',
|
|
64
|
+
inputSchema: {
|
|
65
|
+
type: 'object',
|
|
66
|
+
required: ['task_id'],
|
|
67
|
+
properties: {
|
|
68
|
+
task_id: { type: 'string' },
|
|
69
|
+
checkpoint_id: { type: 'string' }
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
name: 'set_document_policy',
|
|
75
|
+
description: 'Update the exact allowlist of document paths that can be captured in checkpoints.',
|
|
76
|
+
inputSchema: {
|
|
77
|
+
type: 'object',
|
|
78
|
+
required: ['allowed_paths'],
|
|
79
|
+
properties: {
|
|
80
|
+
allowed_paths: { type: 'array', items: { type: 'string' } }
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
name: 'export_handoff',
|
|
86
|
+
description: 'Export an authorized, boundary-safe handoff package for another developer or team.',
|
|
87
|
+
inputSchema: {
|
|
88
|
+
type: 'object',
|
|
89
|
+
required: ['task_id', 'recipient'],
|
|
90
|
+
properties: {
|
|
91
|
+
task_id: { type: 'string', description: 'Kebab-case task slug' },
|
|
92
|
+
recipient: { type: 'string', description: 'Target developer or audience identity' },
|
|
93
|
+
checkpoint_id: { type: 'string', description: 'Optional specific checkpoint ID to export' },
|
|
94
|
+
destination_harness: { type: 'string', enum: ['antigravity', 'command-code', 'muse', 'muse-code', 'pi', 'codex', 'cli', 'any'], default: 'any' },
|
|
95
|
+
preview: { type: 'boolean', default: false, description: 'Dry run preview without writing file' },
|
|
96
|
+
out_path: { type: 'string', description: 'Optional custom bundle file destination' }
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
name: 'import_handoff',
|
|
102
|
+
description: 'Import a verified handoff package into the current workspace with explicit context reconciliation.',
|
|
103
|
+
inputSchema: {
|
|
104
|
+
type: 'object',
|
|
105
|
+
required: ['bundle_path'],
|
|
106
|
+
properties: {
|
|
107
|
+
bundle_path: { type: 'string', description: 'Path to handoff bundle JSON file' },
|
|
108
|
+
target_task_id: { type: 'string', description: 'Optional target task ID to import into' }
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
name: 'get_timeline',
|
|
114
|
+
description: 'Retrieve a chronological timeline and weekly recap of engineering checkpoints, decisions, and progress.',
|
|
115
|
+
inputSchema: {
|
|
116
|
+
type: 'object',
|
|
117
|
+
properties: {
|
|
118
|
+
days: { type: 'integer', default: 7, description: 'Number of past days to include in the recap (defaults to 7)' },
|
|
119
|
+
task_id: { type: 'string', description: 'Optional specific task ID to filter by' },
|
|
120
|
+
destination_harness: { type: 'string', enum: ['antigravity', 'command-code', 'muse', 'muse-code', 'pi', 'codex', 'cli'] }
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
name: 'reason_about_task',
|
|
126
|
+
description: 'Reason over the full engineering memory (checkpoints, decisions, constraints, drift) and the live codebase with Llama to produce a narrative continuation briefing, risk analysis, and recommended next actions.',
|
|
127
|
+
inputSchema: {
|
|
128
|
+
type: 'object',
|
|
129
|
+
required: ['task_id'],
|
|
130
|
+
properties: {
|
|
131
|
+
task_id: { type: 'string', description: 'Kebab-case task slug' },
|
|
132
|
+
checkpoint_id: { type: 'string', description: 'Optional specific checkpoint ID. Defaults to latest head.' },
|
|
133
|
+
destination_harness: { type: 'string', enum: ['antigravity', 'command-code', 'muse', 'muse-code', 'pi', 'codex', 'cli'] },
|
|
134
|
+
model: { type: 'string', description: 'Optional model override (e.g. a Meta Llama or Muse model id)' },
|
|
135
|
+
focus: { type: 'string', description: 'Optional question or focus for the reasoning pass' },
|
|
136
|
+
include_code: { type: 'boolean', default: true, description: 'Ground reasoning in the live codebase index' }
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
];
|
|
141
|
+
|
|
142
|
+
export class McpServer {
|
|
143
|
+
/**
|
|
144
|
+
* @param {object} services
|
|
145
|
+
* @param {import('../storage/sqlite-store.js').SqliteStore} services.store
|
|
146
|
+
* @param {import('../operations/project-service.js').ProjectService} services.projectService
|
|
147
|
+
* @param {import('../operations/checkpoint-service.js').CheckpointService} services.checkpointService
|
|
148
|
+
* @param {import('../operations/briefing-service.js').BriefingService} services.briefingService
|
|
149
|
+
* @param {import('../operations/handoff-service.js').HandoffService} [services.handoffService]
|
|
150
|
+
* @param {import('../operations/reasoning-service.js').ReasoningService} [services.reasoningService]
|
|
151
|
+
* @param {string} [workspacePath]
|
|
152
|
+
*/
|
|
153
|
+
constructor(services, workspacePath = process.cwd()) {
|
|
154
|
+
this.store = services.store;
|
|
155
|
+
this.projectService = services.projectService;
|
|
156
|
+
this.checkpointService = services.checkpointService;
|
|
157
|
+
this.briefingService = services.briefingService;
|
|
158
|
+
this.handoffService = services.handoffService;
|
|
159
|
+
this.reasoningService = services.reasoningService;
|
|
160
|
+
this.workspacePath = workspacePath;
|
|
161
|
+
this.buffer = '';
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Connect to readable and writable streams (typically stdin and stdout).
|
|
166
|
+
* @param {import('node:stream').Readable} inStream
|
|
167
|
+
* @param {import('node:stream').Writable} outStream
|
|
168
|
+
*/
|
|
169
|
+
connect(inStream, outStream) {
|
|
170
|
+
this.outStream = outStream;
|
|
171
|
+
|
|
172
|
+
inStream.setEncoding('utf8');
|
|
173
|
+
inStream.on('data', (chunk) => {
|
|
174
|
+
this.buffer += chunk;
|
|
175
|
+
let newlineIndex;
|
|
176
|
+
while ((newlineIndex = this.buffer.indexOf('\n')) !== -1) {
|
|
177
|
+
const line = this.buffer.slice(0, newlineIndex).trim();
|
|
178
|
+
this.buffer = this.buffer.slice(newlineIndex + 1);
|
|
179
|
+
if (line.length > 0) {
|
|
180
|
+
this._handleLine(line);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
_send(message) {
|
|
187
|
+
if (this.outStream && !this.outStream.destroyed) {
|
|
188
|
+
this.outStream.write(JSON.stringify(message) + '\n');
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
_sendResult(id, result) {
|
|
193
|
+
this._send({
|
|
194
|
+
jsonrpc: '2.0',
|
|
195
|
+
id,
|
|
196
|
+
result
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
_sendError(id, code, message, data = null) {
|
|
201
|
+
this._send({
|
|
202
|
+
jsonrpc: '2.0',
|
|
203
|
+
id,
|
|
204
|
+
error: { code, message, data }
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
_handleLine(line) {
|
|
209
|
+
let req;
|
|
210
|
+
try {
|
|
211
|
+
req = JSON.parse(line);
|
|
212
|
+
} catch (err) {
|
|
213
|
+
this._sendError(null, -32700, `Parse error: ${err.message}`);
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const { id, method, params } = req;
|
|
218
|
+
|
|
219
|
+
// Notifications (no id)
|
|
220
|
+
if (id === undefined || id === null) {
|
|
221
|
+
if (method === 'notifications/initialized') {
|
|
222
|
+
// Client handshake complete
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
try {
|
|
229
|
+
switch (method) {
|
|
230
|
+
case 'initialize': {
|
|
231
|
+
this._sendResult(id, {
|
|
232
|
+
protocolVersion: MCP_PROTOCOL_VERSION,
|
|
233
|
+
serverInfo: {
|
|
234
|
+
name: 'vectorforce',
|
|
235
|
+
version: '0.1.0'
|
|
236
|
+
},
|
|
237
|
+
capabilities: {
|
|
238
|
+
tools: {
|
|
239
|
+
listChanged: false
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
break;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
case 'ping': {
|
|
247
|
+
this._sendResult(id, {});
|
|
248
|
+
break;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
case 'tools/list': {
|
|
252
|
+
this._sendResult(id, {
|
|
253
|
+
tools: MCP_TOOLS
|
|
254
|
+
});
|
|
255
|
+
break;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
case 'tools/call': {
|
|
259
|
+
this._handleToolCall(id, params);
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
default:
|
|
264
|
+
this._sendError(id, -32601, `Method not found: ${method}`);
|
|
265
|
+
break;
|
|
266
|
+
}
|
|
267
|
+
} catch (err) {
|
|
268
|
+
this._sendError(id, -32603, `Internal error: ${err.message}`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
_handleToolCall(id, params) {
|
|
273
|
+
if (!params || !params.name) {
|
|
274
|
+
this._sendError(id, -32602, 'Invalid params: tool name required');
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const toolName = params.name;
|
|
279
|
+
const args = params.arguments || {};
|
|
280
|
+
|
|
281
|
+
try {
|
|
282
|
+
switch (toolName) {
|
|
283
|
+
case 'save_checkpoint': {
|
|
284
|
+
const receipt = this.checkpointService.captureCheckpoint({
|
|
285
|
+
workspacePath: this.workspacePath,
|
|
286
|
+
taskId: args.task_id,
|
|
287
|
+
summary: args.summary,
|
|
288
|
+
goals: args.goals || [],
|
|
289
|
+
constraints: args.constraints || [],
|
|
290
|
+
decisions: args.decisions || [],
|
|
291
|
+
nextSteps: args.next_steps || [],
|
|
292
|
+
openQuestions: args.open_questions || [],
|
|
293
|
+
requestedDocuments: args.requested_documents || [],
|
|
294
|
+
includeGit: args.include_git !== false,
|
|
295
|
+
authorName: 'agent',
|
|
296
|
+
authorHarness: process.env.VF_HARNESS || 'antigravity'
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
this._sendResult(id, {
|
|
300
|
+
content: [
|
|
301
|
+
{
|
|
302
|
+
type: 'text',
|
|
303
|
+
text: JSON.stringify(receipt, null, 2)
|
|
304
|
+
}
|
|
305
|
+
],
|
|
306
|
+
isError: false
|
|
307
|
+
});
|
|
308
|
+
break;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
case 'get_resume_briefing': {
|
|
312
|
+
const result = this.briefingService.generateBriefing({
|
|
313
|
+
workspacePath: this.workspacePath,
|
|
314
|
+
taskId: args.task_id,
|
|
315
|
+
checkpointId: args.checkpoint_id,
|
|
316
|
+
destination: args.destination_harness || process.env.VF_HARNESS || 'antigravity',
|
|
317
|
+
maxTokens: args.max_tokens || 4000
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
this._sendResult(id, {
|
|
321
|
+
content: [
|
|
322
|
+
{
|
|
323
|
+
type: 'text',
|
|
324
|
+
text: result.briefingText
|
|
325
|
+
}
|
|
326
|
+
],
|
|
327
|
+
isError: false
|
|
328
|
+
});
|
|
329
|
+
break;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
case 'list_tasks': {
|
|
333
|
+
const project = this.projectService.getProjectForWorkspace(this.workspacePath);
|
|
334
|
+
const tasks = this.store.listTasks(project.project_id, args.status || 'active');
|
|
335
|
+
this._sendResult(id, {
|
|
336
|
+
content: [
|
|
337
|
+
{
|
|
338
|
+
type: 'text',
|
|
339
|
+
text: JSON.stringify({ project: project.name, tasks }, null, 2)
|
|
340
|
+
}
|
|
341
|
+
],
|
|
342
|
+
isError: false
|
|
343
|
+
});
|
|
344
|
+
break;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
case 'check_workspace_drift': {
|
|
348
|
+
const result = this.briefingService.generateBriefing({
|
|
349
|
+
workspacePath: this.workspacePath,
|
|
350
|
+
taskId: args.task_id,
|
|
351
|
+
checkpointId: args.checkpoint_id,
|
|
352
|
+
destination: 'cli'
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
const summary = {
|
|
356
|
+
task_id: args.task_id,
|
|
357
|
+
drift_detected: result.driftDetected,
|
|
358
|
+
warnings: result.warnings
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
this._sendResult(id, {
|
|
362
|
+
content: [
|
|
363
|
+
{
|
|
364
|
+
type: 'text',
|
|
365
|
+
text: JSON.stringify(summary, null, 2)
|
|
366
|
+
}
|
|
367
|
+
],
|
|
368
|
+
isError: false
|
|
369
|
+
});
|
|
370
|
+
break;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
case 'set_document_policy': {
|
|
374
|
+
const project = this.projectService.getProjectForWorkspace(this.workspacePath);
|
|
375
|
+
const updated = this.store.setDocumentPolicy(project.project_id, args.allowed_paths || []);
|
|
376
|
+
this._sendResult(id, {
|
|
377
|
+
content: [
|
|
378
|
+
{
|
|
379
|
+
type: 'text',
|
|
380
|
+
text: JSON.stringify({ project_id: project.project_id, allowed_paths: updated }, null, 2)
|
|
381
|
+
}
|
|
382
|
+
],
|
|
383
|
+
isError: false
|
|
384
|
+
});
|
|
385
|
+
break;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
case 'export_handoff': {
|
|
389
|
+
if (!this.handoffService) {
|
|
390
|
+
throw new VectorForceError(ErrorCode.INTERNAL_STORE_ERROR, 'HandoffService is not configured on this server');
|
|
391
|
+
}
|
|
392
|
+
const result = this.handoffService.exportHandoff({
|
|
393
|
+
workspacePath: this.workspacePath,
|
|
394
|
+
taskId: args.task_id,
|
|
395
|
+
checkpointId: args.checkpoint_id,
|
|
396
|
+
recipient: args.recipient,
|
|
397
|
+
destinationHarness: args.destination_harness,
|
|
398
|
+
preview: args.preview === true,
|
|
399
|
+
outPath: args.out_path
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
this._sendResult(id, {
|
|
403
|
+
content: [
|
|
404
|
+
{
|
|
405
|
+
type: 'text',
|
|
406
|
+
text: JSON.stringify(result.preview ? result : result.receipt, null, 2)
|
|
407
|
+
}
|
|
408
|
+
],
|
|
409
|
+
isError: false
|
|
410
|
+
});
|
|
411
|
+
break;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
case 'import_handoff': {
|
|
415
|
+
if (!this.handoffService) {
|
|
416
|
+
throw new VectorForceError(ErrorCode.INTERNAL_STORE_ERROR, 'HandoffService is not configured on this server');
|
|
417
|
+
}
|
|
418
|
+
const result = this.handoffService.importHandoff({
|
|
419
|
+
workspacePath: this.workspacePath,
|
|
420
|
+
bundlePath: args.bundle_path,
|
|
421
|
+
targetTaskId: args.target_task_id
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
this._sendResult(id, {
|
|
425
|
+
content: [
|
|
426
|
+
{
|
|
427
|
+
type: 'text',
|
|
428
|
+
text: JSON.stringify(result.receipt, null, 2)
|
|
429
|
+
}
|
|
430
|
+
],
|
|
431
|
+
isError: false
|
|
432
|
+
});
|
|
433
|
+
break;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
case 'get_timeline': {
|
|
437
|
+
const result = this.briefingService.generateRecap({
|
|
438
|
+
workspacePath: this.workspacePath,
|
|
439
|
+
days: args.days ?? 7,
|
|
440
|
+
taskId: args.task_id,
|
|
441
|
+
destination: args.destination_harness || process.env.VF_HARNESS || 'antigravity'
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
this._sendResult(id, {
|
|
445
|
+
content: [
|
|
446
|
+
{
|
|
447
|
+
type: 'text',
|
|
448
|
+
text: result.recapText
|
|
449
|
+
}
|
|
450
|
+
],
|
|
451
|
+
isError: false
|
|
452
|
+
});
|
|
453
|
+
break;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
case 'reason_about_task': {
|
|
457
|
+
if (!this.reasoningService) {
|
|
458
|
+
throw new VectorForceError(ErrorCode.INTERNAL_STORE_ERROR, 'ReasoningService is not configured on this server');
|
|
459
|
+
}
|
|
460
|
+
this.reasoningService.reasonAboutTask({
|
|
461
|
+
workspacePath: this.workspacePath,
|
|
462
|
+
taskId: args.task_id,
|
|
463
|
+
checkpointId: args.checkpoint_id,
|
|
464
|
+
destination: args.destination_harness || process.env.VF_HARNESS || 'cli',
|
|
465
|
+
model: args.model,
|
|
466
|
+
focus: args.focus,
|
|
467
|
+
includeCode: args.include_code !== false
|
|
468
|
+
}).then((result) => {
|
|
469
|
+
const header = {
|
|
470
|
+
task_id: args.task_id,
|
|
471
|
+
degraded: result.degraded,
|
|
472
|
+
provider: result.provider,
|
|
473
|
+
model: result.model,
|
|
474
|
+
drift_detected: result.driftDetected,
|
|
475
|
+
code_files_considered: result.codeFilesConsidered
|
|
476
|
+
};
|
|
477
|
+
this._sendResult(id, {
|
|
478
|
+
content: [
|
|
479
|
+
{ type: 'text', text: result.narrativeText },
|
|
480
|
+
{ type: 'text', text: JSON.stringify(header, null, 2) }
|
|
481
|
+
],
|
|
482
|
+
isError: false
|
|
483
|
+
});
|
|
484
|
+
}).catch((err) => {
|
|
485
|
+
this._sendResult(id, {
|
|
486
|
+
content: [{ type: 'text', text: `Tool error: ${err.message}` }],
|
|
487
|
+
isError: true
|
|
488
|
+
});
|
|
489
|
+
});
|
|
490
|
+
break;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
default:
|
|
494
|
+
this._sendResult(id, {
|
|
495
|
+
content: [{ type: 'text', text: `Unknown tool: ${toolName}` }],
|
|
496
|
+
isError: true
|
|
497
|
+
});
|
|
498
|
+
break;
|
|
499
|
+
}
|
|
500
|
+
} catch (err) {
|
|
501
|
+
this._sendResult(id, {
|
|
502
|
+
content: [{ type: 'text', text: `Tool error: ${err.message}` }],
|
|
503
|
+
isError: true
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
}
|