@mcoda/core 0.1.26 → 0.1.28
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/AgentsApi.d.ts +9 -1
- package/dist/api/AgentsApi.d.ts.map +1 -1
- package/dist/api/AgentsApi.js +53 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/services/execution/QaTasksService.d.ts +2 -0
- package/dist/services/execution/QaTasksService.d.ts.map +1 -1
- package/dist/services/execution/QaTasksService.js +181 -20
- package/dist/services/execution/WorkOnTasksService.d.ts.map +1 -1
- package/dist/services/execution/WorkOnTasksService.js +183 -7
- package/dist/services/planning/CreateTasksService.d.ts +9 -0
- package/dist/services/planning/CreateTasksService.d.ts.map +1 -1
- package/dist/services/planning/CreateTasksService.js +424 -92
- package/dist/services/planning/SdsPreflightService.d.ts +98 -0
- package/dist/services/planning/SdsPreflightService.d.ts.map +1 -0
- package/dist/services/planning/SdsPreflightService.js +1093 -0
- package/dist/services/planning/TaskSufficiencyService.d.ts +1 -0
- package/dist/services/planning/TaskSufficiencyService.d.ts.map +1 -1
- package/dist/services/planning/TaskSufficiencyService.js +376 -77
- package/dist/services/review/CodeReviewService.d.ts.map +1 -1
- package/dist/services/review/CodeReviewService.js +149 -7
- package/package.json +6 -6
|
@@ -207,6 +207,90 @@ const normalizeSlugList = (input) => {
|
|
|
207
207
|
}
|
|
208
208
|
return Array.from(cleaned);
|
|
209
209
|
};
|
|
210
|
+
const isRecord = (value) => Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
211
|
+
const normalizeFailoverEvents = (value) => {
|
|
212
|
+
if (!Array.isArray(value))
|
|
213
|
+
return [];
|
|
214
|
+
const events = [];
|
|
215
|
+
for (const entry of value) {
|
|
216
|
+
if (!isRecord(entry))
|
|
217
|
+
continue;
|
|
218
|
+
if (typeof entry.type !== "string" || entry.type.trim().length === 0)
|
|
219
|
+
continue;
|
|
220
|
+
events.push({ ...entry });
|
|
221
|
+
}
|
|
222
|
+
return events;
|
|
223
|
+
};
|
|
224
|
+
const mergeFailoverEvents = (left, right) => {
|
|
225
|
+
if (!left.length)
|
|
226
|
+
return right;
|
|
227
|
+
if (!right.length)
|
|
228
|
+
return left;
|
|
229
|
+
const seen = new Set();
|
|
230
|
+
const merged = [];
|
|
231
|
+
const signature = (event) => [
|
|
232
|
+
event.type ?? "",
|
|
233
|
+
event.fromAgentId ?? "",
|
|
234
|
+
event.toAgentId ?? "",
|
|
235
|
+
event.at ?? "",
|
|
236
|
+
event.until ?? "",
|
|
237
|
+
event.durationMs ?? "",
|
|
238
|
+
].join("|");
|
|
239
|
+
for (const event of [...left, ...right]) {
|
|
240
|
+
const key = signature(event);
|
|
241
|
+
if (seen.has(key))
|
|
242
|
+
continue;
|
|
243
|
+
seen.add(key);
|
|
244
|
+
merged.push(event);
|
|
245
|
+
}
|
|
246
|
+
return merged;
|
|
247
|
+
};
|
|
248
|
+
const mergeInvocationMetadata = (current, incoming) => {
|
|
249
|
+
if (!current && !incoming)
|
|
250
|
+
return undefined;
|
|
251
|
+
if (!incoming)
|
|
252
|
+
return current;
|
|
253
|
+
if (!current)
|
|
254
|
+
return { ...incoming };
|
|
255
|
+
const merged = { ...current, ...incoming };
|
|
256
|
+
const currentEvents = normalizeFailoverEvents(current.failoverEvents);
|
|
257
|
+
const incomingEvents = normalizeFailoverEvents(incoming.failoverEvents);
|
|
258
|
+
if (currentEvents.length > 0 || incomingEvents.length > 0) {
|
|
259
|
+
merged.failoverEvents = mergeFailoverEvents(currentEvents, incomingEvents);
|
|
260
|
+
}
|
|
261
|
+
return merged;
|
|
262
|
+
};
|
|
263
|
+
const summarizeFailoverEvent = (event) => {
|
|
264
|
+
const type = String(event.type ?? "unknown");
|
|
265
|
+
if (type === "switch_agent") {
|
|
266
|
+
const from = typeof event.fromAgentId === "string" ? event.fromAgentId : "unknown";
|
|
267
|
+
const to = typeof event.toAgentId === "string" ? event.toAgentId : "unknown";
|
|
268
|
+
return `switch_agent ${from} -> ${to}`;
|
|
269
|
+
}
|
|
270
|
+
if (type === "sleep_until_reset") {
|
|
271
|
+
const duration = typeof event.durationMs === "number" && Number.isFinite(event.durationMs)
|
|
272
|
+
? `${Math.round(event.durationMs / 1000)}s`
|
|
273
|
+
: "unknown duration";
|
|
274
|
+
const until = typeof event.until === "string" ? event.until : "unknown";
|
|
275
|
+
return `sleep_until_reset ${duration} (until ${until})`;
|
|
276
|
+
}
|
|
277
|
+
if (type === "stream_restart_after_limit") {
|
|
278
|
+
const from = typeof event.fromAgentId === "string" ? event.fromAgentId : "unknown";
|
|
279
|
+
return `stream_restart_after_limit from ${from}`;
|
|
280
|
+
}
|
|
281
|
+
return type;
|
|
282
|
+
};
|
|
283
|
+
const resolveFailoverAgentId = (events, fallbackAgentId) => {
|
|
284
|
+
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
285
|
+
const event = events[index];
|
|
286
|
+
if (event?.type !== "switch_agent")
|
|
287
|
+
continue;
|
|
288
|
+
if (typeof event.toAgentId === "string" && event.toAgentId.trim().length > 0) {
|
|
289
|
+
return event.toAgentId;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return fallbackAgentId;
|
|
293
|
+
};
|
|
210
294
|
const normalizePath = (value) => value
|
|
211
295
|
.replace(/\\/g, "/")
|
|
212
296
|
.replace(/^\.\//, "")
|
|
@@ -1990,6 +2074,45 @@ export class CodeReviewService {
|
|
|
1990
2074
|
metadata: { commandName: "code-review", phase, action: phase, attempt },
|
|
1991
2075
|
});
|
|
1992
2076
|
};
|
|
2077
|
+
const logFailoverEvents = async (events) => {
|
|
2078
|
+
if (!events.length)
|
|
2079
|
+
return;
|
|
2080
|
+
for (const event of events) {
|
|
2081
|
+
await this.deps.workspaceRepo.insertTaskLog({
|
|
2082
|
+
taskRunId: taskRun.id,
|
|
2083
|
+
sequence: this.sequenceForTask(taskRun.id),
|
|
2084
|
+
timestamp: new Date().toISOString(),
|
|
2085
|
+
source: "agent_failover",
|
|
2086
|
+
message: `Agent failover: ${summarizeFailoverEvent(event)}`,
|
|
2087
|
+
details: event,
|
|
2088
|
+
});
|
|
2089
|
+
}
|
|
2090
|
+
};
|
|
2091
|
+
const resolveUsageAgent = async (fallback, events) => {
|
|
2092
|
+
const usageAgentId = resolveFailoverAgentId(events, fallback.id);
|
|
2093
|
+
if (usageAgentId === fallback.id)
|
|
2094
|
+
return fallback;
|
|
2095
|
+
const resolver = this.deps.agentService?.resolveAgent;
|
|
2096
|
+
if (typeof resolver !== "function")
|
|
2097
|
+
return fallback;
|
|
2098
|
+
try {
|
|
2099
|
+
const resolved = await resolver.call(this.deps.agentService, usageAgentId);
|
|
2100
|
+
return {
|
|
2101
|
+
id: resolved.id,
|
|
2102
|
+
defaultModel: typeof resolved.defaultModel === "string" ? resolved.defaultModel : fallback.defaultModel,
|
|
2103
|
+
};
|
|
2104
|
+
}
|
|
2105
|
+
catch (error) {
|
|
2106
|
+
await this.deps.workspaceRepo.insertTaskLog({
|
|
2107
|
+
taskRunId: taskRun.id,
|
|
2108
|
+
sequence: this.sequenceForTask(taskRun.id),
|
|
2109
|
+
timestamp: new Date().toISOString(),
|
|
2110
|
+
source: "agent_failover",
|
|
2111
|
+
message: `Unable to resolve failover agent (${usageAgentId}) for usage accounting: ${error instanceof Error ? error.message : String(error)}`,
|
|
2112
|
+
});
|
|
2113
|
+
return fallback;
|
|
2114
|
+
}
|
|
2115
|
+
};
|
|
1993
2116
|
agentOutput = "";
|
|
1994
2117
|
let durationSeconds = 0;
|
|
1995
2118
|
let lastStreamMeta;
|
|
@@ -2002,7 +2125,7 @@ export class CodeReviewService {
|
|
|
2002
2125
|
if (useStream && this.deps.agentService.invokeStream) {
|
|
2003
2126
|
const stream = await withAbort(this.deps.agentService.invokeStream(agentToUse.id, {
|
|
2004
2127
|
input: prompt,
|
|
2005
|
-
metadata: { taskKey: task.key, retry: logSource === "agent_retry" },
|
|
2128
|
+
metadata: { command: "code-review", taskKey: task.key, retry: logSource === "agent_retry" },
|
|
2006
2129
|
}));
|
|
2007
2130
|
while (true) {
|
|
2008
2131
|
abortIfSignaled();
|
|
@@ -2011,7 +2134,7 @@ export class CodeReviewService {
|
|
|
2011
2134
|
break;
|
|
2012
2135
|
const chunk = value;
|
|
2013
2136
|
output += chunk.output ?? "";
|
|
2014
|
-
metadata = chunk.metadata
|
|
2137
|
+
metadata = mergeInvocationMetadata(metadata, chunk.metadata);
|
|
2015
2138
|
await this.deps.workspaceRepo.insertTaskLog({
|
|
2016
2139
|
taskRunId: taskRun.id,
|
|
2017
2140
|
sequence: this.sequenceForTask(taskRun.id),
|
|
@@ -2024,10 +2147,10 @@ export class CodeReviewService {
|
|
|
2024
2147
|
else {
|
|
2025
2148
|
const response = await withAbort(this.deps.agentService.invoke(agentToUse.id, {
|
|
2026
2149
|
input: prompt,
|
|
2027
|
-
metadata: { taskKey: task.key, retry: logSource === "agent_retry" },
|
|
2150
|
+
metadata: { command: "code-review", taskKey: task.key, retry: logSource === "agent_retry" },
|
|
2028
2151
|
}));
|
|
2029
2152
|
output = response.output ?? "";
|
|
2030
|
-
metadata = response.metadata;
|
|
2153
|
+
metadata = mergeInvocationMetadata(metadata, response.metadata);
|
|
2031
2154
|
await this.deps.workspaceRepo.insertTaskLog({
|
|
2032
2155
|
taskRunId: taskRun.id,
|
|
2033
2156
|
sequence: this.sequenceForTask(taskRun.id),
|
|
@@ -2074,7 +2197,15 @@ export class CodeReviewService {
|
|
|
2074
2197
|
model: (lastStreamMeta.model ?? lastStreamMeta.model_name ?? null),
|
|
2075
2198
|
}
|
|
2076
2199
|
: undefined;
|
|
2077
|
-
|
|
2200
|
+
const mainFailoverEvents = normalizeFailoverEvents(lastStreamMeta?.failoverEvents);
|
|
2201
|
+
await logFailoverEvents(mainFailoverEvents);
|
|
2202
|
+
const usageAgentForOutput = await resolveUsageAgent({
|
|
2203
|
+
id: agentUsedForOutput.id,
|
|
2204
|
+
defaultModel: typeof agentUsedForOutput?.defaultModel === "string"
|
|
2205
|
+
? agentUsedForOutput.defaultModel
|
|
2206
|
+
: undefined,
|
|
2207
|
+
}, mainFailoverEvents);
|
|
2208
|
+
await recordUsage("review_main", prompt, agentOutput, durationSeconds, tokenMetaMain, usageAgentForOutput, outputAttempt);
|
|
2078
2209
|
const primaryOutput = agentOutput;
|
|
2079
2210
|
let retryOutput;
|
|
2080
2211
|
let retryAgentUsed;
|
|
@@ -2126,7 +2257,10 @@ export class CodeReviewService {
|
|
|
2126
2257
|
message: `Retrying with JSON-only agent override: ${retryAgentUsed.slug ?? retryAgentUsed.id}`,
|
|
2127
2258
|
});
|
|
2128
2259
|
}
|
|
2129
|
-
const retryResp = await withAbort(this.deps.agentService.invoke(retryAgentUsed.id, {
|
|
2260
|
+
const retryResp = await withAbort(this.deps.agentService.invoke(retryAgentUsed.id, {
|
|
2261
|
+
input: retryPrompt,
|
|
2262
|
+
metadata: { command: "code-review", taskKey: task.key, retry: true },
|
|
2263
|
+
}));
|
|
2130
2264
|
retryOutput = retryResp.output ?? "";
|
|
2131
2265
|
const retryDuration = Math.round(((Date.now() - retryStarted) / 1000) * 1000) / 1000;
|
|
2132
2266
|
await this.deps.workspaceRepo.insertTaskLog({
|
|
@@ -2150,7 +2284,15 @@ export class CodeReviewService {
|
|
|
2150
2284
|
model: (retryResp.metadata.model ?? retryResp.metadata.model_name ?? null),
|
|
2151
2285
|
}
|
|
2152
2286
|
: undefined;
|
|
2153
|
-
|
|
2287
|
+
const retryFailoverEvents = normalizeFailoverEvents(retryResp.metadata?.failoverEvents);
|
|
2288
|
+
await logFailoverEvents(retryFailoverEvents);
|
|
2289
|
+
const retryUsageAgent = await resolveUsageAgent({
|
|
2290
|
+
id: retryAgentUsed.id,
|
|
2291
|
+
defaultModel: typeof retryAgentUsed?.defaultModel === "string"
|
|
2292
|
+
? retryAgentUsed.defaultModel
|
|
2293
|
+
: undefined,
|
|
2294
|
+
}, retryFailoverEvents);
|
|
2295
|
+
await recordUsage("review_retry", retryPrompt, retryOutput, retryDuration, retryTokenMeta, retryUsageAgent, 2);
|
|
2154
2296
|
normalization = normalizeReviewOutput(retryOutput);
|
|
2155
2297
|
parsed = normalization.result;
|
|
2156
2298
|
validationError = validateReviewOutput(parsed, { requireCommentSlugs });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mcoda/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.28",
|
|
4
4
|
"description": "Core services and APIs for the mcoda CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -32,11 +32,11 @@
|
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@apidevtools/swagger-parser": "^10.1.0",
|
|
34
34
|
"yaml": "^2.4.2",
|
|
35
|
-
"@mcoda/shared": "0.1.
|
|
36
|
-
"@mcoda/
|
|
37
|
-
"@mcoda/
|
|
38
|
-
"@mcoda/
|
|
39
|
-
"@mcoda/
|
|
35
|
+
"@mcoda/shared": "0.1.28",
|
|
36
|
+
"@mcoda/generators": "0.1.28",
|
|
37
|
+
"@mcoda/db": "0.1.28",
|
|
38
|
+
"@mcoda/agents": "0.1.28",
|
|
39
|
+
"@mcoda/integrations": "0.1.28"
|
|
40
40
|
},
|
|
41
41
|
"scripts": {
|
|
42
42
|
"build": "tsc -p tsconfig.json",
|