@gonzih/cc-agent 0.15.11 → 0.15.16
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/LICENSE +203 -0
- package/README.md +2 -0
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +47 -44
- package/dist/agent.js.map +1 -1
- package/dist/cron.d.ts.map +1 -1
- package/dist/cron.js +18 -12
- package/dist/cron.js.map +1 -1
- package/dist/index.js +218 -39
- package/dist/index.js.map +1 -1
- package/dist/preamble.d.ts +1 -1
- package/dist/preamble.d.ts.map +1 -1
- package/dist/preamble.js +9 -3
- package/dist/preamble.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -687,6 +687,42 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
687
687
|
description: "List all available agent drivers and their status (binary found / API key configured). Use this to check which drivers are ready to use before calling spawn_agent with agent_driver.",
|
|
688
688
|
inputSchema: { type: "object", properties: {} },
|
|
689
689
|
},
|
|
690
|
+
{
|
|
691
|
+
name: "export_jobs",
|
|
692
|
+
description: "Export all job records as JSONL or JSON for statistical analysis. Each record includes id, status, repo_url, task (truncated to 500 chars), started_at, finished_at, exit_code, output_lines count, score, and duration_seconds. Use this to pull job traces, compute success rates, and study failure modes.",
|
|
693
|
+
inputSchema: {
|
|
694
|
+
type: "object",
|
|
695
|
+
properties: {
|
|
696
|
+
days: { type: "number", description: "How many days back to export (default: 7)" },
|
|
697
|
+
format: { type: "string", description: "Output format: 'jsonl' (one record per line) or 'json' (array). Default: 'jsonl'" },
|
|
698
|
+
status: { type: "string", description: "Filter by status: 'done' | 'failed' | 'cancelled' | 'running' (optional)" },
|
|
699
|
+
},
|
|
700
|
+
},
|
|
701
|
+
},
|
|
702
|
+
{
|
|
703
|
+
name: "get_cost_report",
|
|
704
|
+
description: "Longitudinal cost breakdown for research budget tracking. Returns grouped cost summary with total USD spent, job count, avg cost per job, and avg score. Useful for tracking spending by repo, day, or outcome.",
|
|
705
|
+
inputSchema: {
|
|
706
|
+
type: "object",
|
|
707
|
+
properties: {
|
|
708
|
+
days: { type: "number", description: "How many days back to include (default: 30)" },
|
|
709
|
+
group_by: { type: "string", description: "Group by 'repo' | 'day' | 'status' (default: 'repo')" },
|
|
710
|
+
},
|
|
711
|
+
},
|
|
712
|
+
},
|
|
713
|
+
{
|
|
714
|
+
name: "search_jobs",
|
|
715
|
+
description: "Find jobs by content of task prompt. Returns matching jobs with a task snippet showing match context. Useful for finding all jobs that involved a specific tool, repo, or task type.",
|
|
716
|
+
inputSchema: {
|
|
717
|
+
type: "object",
|
|
718
|
+
properties: {
|
|
719
|
+
query: { type: "string", description: "Search term to look for in task prompts (case-insensitive)" },
|
|
720
|
+
days: { type: "number", description: "How many days back to search (default: 30)" },
|
|
721
|
+
status: { type: "string", description: "Filter by status: 'done' | 'failed' | 'cancelled' | 'running' (optional)" },
|
|
722
|
+
},
|
|
723
|
+
required: ["query"],
|
|
724
|
+
},
|
|
725
|
+
},
|
|
690
726
|
],
|
|
691
727
|
}));
|
|
692
728
|
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
@@ -695,7 +731,7 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
695
731
|
switch (name) {
|
|
696
732
|
case "spawn_agent": {
|
|
697
733
|
const repoUrl = normalizeRepoUrl(a.repo_url);
|
|
698
|
-
logger.info("
|
|
734
|
+
logger.info("[mcp] spawn_agent", { repo_url: repoUrl, task: a.task?.slice(0, 80) });
|
|
699
735
|
const owner = extractGithubOwner(repoUrl);
|
|
700
736
|
const isTrusted = !owner || TRUSTED_OWNERS.includes(owner);
|
|
701
737
|
const jobId = await manager.spawn({
|
|
@@ -786,7 +822,7 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
786
822
|
};
|
|
787
823
|
}
|
|
788
824
|
case "get_job_status": {
|
|
789
|
-
logger.info("
|
|
825
|
+
logger.info("[mcp] get_job_status", { job_id: a.job_id });
|
|
790
826
|
const job = manager.getJob(a.job_id);
|
|
791
827
|
if (!job) {
|
|
792
828
|
return { content: [{ type: "text", text: JSON.stringify({ error: "Job not found" }) }] };
|
|
@@ -822,7 +858,7 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
822
858
|
case "wait_for_job": {
|
|
823
859
|
const waitJobId = a.job_id;
|
|
824
860
|
const timeoutSeconds = typeof a.timeout_seconds === "number" ? a.timeout_seconds : 300;
|
|
825
|
-
logger.info("
|
|
861
|
+
logger.info("[mcp] wait_for_job", { job_id: waitJobId, timeout_seconds: timeoutSeconds });
|
|
826
862
|
const TERMINAL_STATUSES = new Set(["done", "failed", "cancelled", "rejected", "interrupted"]);
|
|
827
863
|
const deadlineMs = Date.now() + timeoutSeconds * 1000;
|
|
828
864
|
let waitRecord = await jobStore.getJob(waitJobId);
|
|
@@ -858,7 +894,7 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
858
894
|
};
|
|
859
895
|
}
|
|
860
896
|
case "get_job_output": {
|
|
861
|
-
logger.info("
|
|
897
|
+
logger.info("[mcp] get_job_output", { job_id: a.job_id, offset: a.offset });
|
|
862
898
|
const offset = typeof a.offset === "number" ? a.offset : 0;
|
|
863
899
|
const { lines, done, toolCalls } = await manager.getOutput(a.job_id, offset);
|
|
864
900
|
return {
|
|
@@ -878,7 +914,7 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
878
914
|
};
|
|
879
915
|
}
|
|
880
916
|
case "list_jobs": {
|
|
881
|
-
logger.info("
|
|
917
|
+
logger.info("[mcp] list_jobs");
|
|
882
918
|
const minScore = typeof a.min_score === "number" ? a.min_score : undefined;
|
|
883
919
|
let jobs = (await jobStore.listJobs()) ?? [];
|
|
884
920
|
if (minScore !== undefined) {
|
|
@@ -896,7 +932,7 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
896
932
|
};
|
|
897
933
|
}
|
|
898
934
|
case "cancel_job": {
|
|
899
|
-
logger.info("
|
|
935
|
+
logger.info("[mcp] cancel_job", { job_id: a.job_id });
|
|
900
936
|
const cancelled = manager.cancel(a.job_id);
|
|
901
937
|
return {
|
|
902
938
|
content: [
|
|
@@ -908,7 +944,7 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
908
944
|
};
|
|
909
945
|
}
|
|
910
946
|
case "send_message": {
|
|
911
|
-
logger.info("
|
|
947
|
+
logger.info("[mcp] send_message", { job_id: a.job_id });
|
|
912
948
|
const result = await manager.sendMessage(a.job_id, a.message);
|
|
913
949
|
return {
|
|
914
950
|
content: [
|
|
@@ -922,7 +958,7 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
922
958
|
};
|
|
923
959
|
}
|
|
924
960
|
case "cost_summary": {
|
|
925
|
-
logger.info("
|
|
961
|
+
logger.info("[mcp] cost_summary");
|
|
926
962
|
// Use jobStore (Redis/disk) to include all persisted jobs, not just in-memory ones
|
|
927
963
|
const allRecords = await jobStore.listJobs();
|
|
928
964
|
let totalCostUsd = 0;
|
|
@@ -955,12 +991,12 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
955
991
|
};
|
|
956
992
|
}
|
|
957
993
|
case "get_version":
|
|
958
|
-
logger.info("
|
|
994
|
+
logger.info("[mcp] get_version");
|
|
959
995
|
return {
|
|
960
996
|
content: [{ type: "text", text: JSON.stringify({ version: PKG_VERSION }) }],
|
|
961
997
|
};
|
|
962
998
|
case "create_profile": {
|
|
963
|
-
logger.info("
|
|
999
|
+
logger.info("[mcp] create_profile", { name: a.name });
|
|
964
1000
|
const profileName = a.name;
|
|
965
1001
|
if (!/^[\w-]+$/.test(profileName)) {
|
|
966
1002
|
return {
|
|
@@ -982,7 +1018,7 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
982
1018
|
};
|
|
983
1019
|
}
|
|
984
1020
|
case "list_profiles": {
|
|
985
|
-
logger.info("
|
|
1021
|
+
logger.info("[mcp] list_profiles");
|
|
986
1022
|
const profiles = (await loadProfiles()).map(({ name, repoUrl, description, defaultBudgetUsd, builtin }) => ({
|
|
987
1023
|
name,
|
|
988
1024
|
repoUrl,
|
|
@@ -996,7 +1032,7 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
996
1032
|
};
|
|
997
1033
|
}
|
|
998
1034
|
case "delete_profile": {
|
|
999
|
-
logger.info("
|
|
1035
|
+
logger.info("[mcp] delete_profile", { name: a.name });
|
|
1000
1036
|
const deleted = await deleteProfile(a.name);
|
|
1001
1037
|
return {
|
|
1002
1038
|
content: [
|
|
@@ -1010,7 +1046,7 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
1010
1046
|
};
|
|
1011
1047
|
}
|
|
1012
1048
|
case "spawn_from_profile": {
|
|
1013
|
-
logger.info("
|
|
1049
|
+
logger.info("[mcp] spawn_from_profile", { profile_name: a.profile_name });
|
|
1014
1050
|
const profile = await getProfile(a.profile_name);
|
|
1015
1051
|
if (!profile) {
|
|
1016
1052
|
return {
|
|
@@ -1038,7 +1074,7 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
1038
1074
|
};
|
|
1039
1075
|
}
|
|
1040
1076
|
case "create_plan": {
|
|
1041
|
-
logger.info("
|
|
1077
|
+
logger.info("[mcp] create_plan", { goal: a.goal?.slice(0, 80) });
|
|
1042
1078
|
const goal = a.goal;
|
|
1043
1079
|
const steps = a.steps;
|
|
1044
1080
|
const stepIdToJobId = new Map();
|
|
@@ -1134,14 +1170,14 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
1134
1170
|
};
|
|
1135
1171
|
}
|
|
1136
1172
|
case "wake_job": {
|
|
1137
|
-
logger.info("
|
|
1173
|
+
logger.info("[mcp] wake_job", { job_id: a.job_id });
|
|
1138
1174
|
const result = await manager.wakeJob(a.job_id);
|
|
1139
1175
|
return {
|
|
1140
1176
|
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
1141
1177
|
};
|
|
1142
1178
|
}
|
|
1143
1179
|
case "list_model_ratings": {
|
|
1144
|
-
logger.info("
|
|
1180
|
+
logger.info("[mcp] list_model_ratings");
|
|
1145
1181
|
const ratingsFile = join(homedir(), ".cc-agent", "model-ratings.jsonl");
|
|
1146
1182
|
let ratings = [];
|
|
1147
1183
|
try {
|
|
@@ -1157,7 +1193,7 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
1157
1193
|
};
|
|
1158
1194
|
}
|
|
1159
1195
|
case "get_logs": {
|
|
1160
|
-
logger.info("
|
|
1196
|
+
logger.info("[mcp] get_logs", { lines: a.lines });
|
|
1161
1197
|
const n = Math.min(typeof a.lines === "number" ? a.lines : 100, 500);
|
|
1162
1198
|
const logFile = join(homedir(), ".cc-agent", "logs", "cc-agent.log");
|
|
1163
1199
|
let lines = [];
|
|
@@ -1174,7 +1210,7 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
1174
1210
|
};
|
|
1175
1211
|
}
|
|
1176
1212
|
case "list_project_issues": {
|
|
1177
|
-
logger.info("
|
|
1213
|
+
logger.info("[mcp] list_project_issues", { repo: a.repo });
|
|
1178
1214
|
const repo = a.repo;
|
|
1179
1215
|
const state = a.state ?? "open";
|
|
1180
1216
|
const labels = a.labels;
|
|
@@ -1196,7 +1232,7 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
1196
1232
|
}
|
|
1197
1233
|
}
|
|
1198
1234
|
case "work_on_issue": {
|
|
1199
|
-
logger.info("
|
|
1235
|
+
logger.info("[mcp] work_on_issue", { repo: a.repo, issue_number: a.issue_number });
|
|
1200
1236
|
const repo = a.repo;
|
|
1201
1237
|
const issueNumber = a.issue_number;
|
|
1202
1238
|
const extraContext = a.extra_context;
|
|
@@ -1240,7 +1276,7 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
1240
1276
|
};
|
|
1241
1277
|
}
|
|
1242
1278
|
case "comment_on_issue": {
|
|
1243
|
-
logger.info("
|
|
1279
|
+
logger.info("[mcp] comment_on_issue", { repo: a.repo, issue_number: a.issue_number });
|
|
1244
1280
|
const repo = a.repo;
|
|
1245
1281
|
const issueNumber = a.issue_number;
|
|
1246
1282
|
const body = a.body;
|
|
@@ -1253,7 +1289,7 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
1253
1289
|
}
|
|
1254
1290
|
}
|
|
1255
1291
|
case "close_issue": {
|
|
1256
|
-
logger.info("
|
|
1292
|
+
logger.info("[mcp] close_issue", { repo: a.repo, issue_number: a.issue_number });
|
|
1257
1293
|
const repo = a.repo;
|
|
1258
1294
|
const issueNumber = a.issue_number;
|
|
1259
1295
|
const comment = a.comment;
|
|
@@ -1269,14 +1305,14 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
1269
1305
|
}
|
|
1270
1306
|
}
|
|
1271
1307
|
case "approve_job": {
|
|
1272
|
-
logger.info("
|
|
1308
|
+
logger.info("[mcp] approve_job", { job_id: a.job_id });
|
|
1273
1309
|
const result = await manager.approveJob(a.job_id);
|
|
1274
1310
|
return {
|
|
1275
1311
|
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
1276
1312
|
};
|
|
1277
1313
|
}
|
|
1278
1314
|
case "set_job_score": {
|
|
1279
|
-
logger.info("
|
|
1315
|
+
logger.info("[mcp] set_job_score", { job_id: a.job_id, score: a.score });
|
|
1280
1316
|
const result = manager.setJobScore(a.job_id, a.score, a.reason);
|
|
1281
1317
|
return {
|
|
1282
1318
|
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
@@ -1286,7 +1322,7 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
1286
1322
|
const repoParam = a.repo;
|
|
1287
1323
|
const ns = repoParam ?? a.namespace ?? getNamespace();
|
|
1288
1324
|
const limit = typeof a.limit === "number" ? a.limit : 10;
|
|
1289
|
-
logger.info("
|
|
1325
|
+
logger.info("[mcp] get_learnings", { key: ns, limit });
|
|
1290
1326
|
const learnings = await learningsStore.getLearnings(ns, limit);
|
|
1291
1327
|
return {
|
|
1292
1328
|
content: [{
|
|
@@ -1297,7 +1333,7 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
1297
1333
|
}
|
|
1298
1334
|
case "clear_learnings": {
|
|
1299
1335
|
const ns = a.namespace ?? getNamespace();
|
|
1300
|
-
logger.info("
|
|
1336
|
+
logger.info("[mcp] clear_learnings", { namespace: ns });
|
|
1301
1337
|
await learningsStore.clearLearnings(ns);
|
|
1302
1338
|
return {
|
|
1303
1339
|
content: [{
|
|
@@ -1307,7 +1343,7 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
1307
1343
|
};
|
|
1308
1344
|
}
|
|
1309
1345
|
case "docker_ps": {
|
|
1310
|
-
logger.info("
|
|
1346
|
+
logger.info("[mcp] docker_ps");
|
|
1311
1347
|
const containers = await listCcAgentContainers();
|
|
1312
1348
|
return {
|
|
1313
1349
|
content: [{
|
|
@@ -1317,7 +1353,7 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
1317
1353
|
};
|
|
1318
1354
|
}
|
|
1319
1355
|
case "list_token_status": {
|
|
1320
|
-
logger.info("
|
|
1356
|
+
logger.info("[mcp] list_token_status");
|
|
1321
1357
|
const tokens = loadTokens();
|
|
1322
1358
|
const status = await getTokenStatus();
|
|
1323
1359
|
const tokenList = tokens.map((t, i) => ({
|
|
@@ -1338,12 +1374,12 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
1338
1374
|
};
|
|
1339
1375
|
}
|
|
1340
1376
|
case "list_crons": {
|
|
1341
|
-
logger.info("
|
|
1377
|
+
logger.info("[mcp] list_crons");
|
|
1342
1378
|
const crons = await cronEngine.listCrons();
|
|
1343
1379
|
return { content: [{ type: "text", text: JSON.stringify({ crons, total: crons.length }) }] };
|
|
1344
1380
|
}
|
|
1345
1381
|
case "create_cron": {
|
|
1346
|
-
logger.info("
|
|
1382
|
+
logger.info("[mcp] create_cron", { schedule: a.schedule });
|
|
1347
1383
|
const cron = await cronEngine.addCron({
|
|
1348
1384
|
chatId: typeof a.chat_id === "number" ? a.chat_id : 0,
|
|
1349
1385
|
intervalMs: a.interval_ms,
|
|
@@ -1355,12 +1391,12 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
1355
1391
|
return { content: [{ type: "text", text: JSON.stringify(cron) }] };
|
|
1356
1392
|
}
|
|
1357
1393
|
case "delete_cron": {
|
|
1358
|
-
logger.info("
|
|
1394
|
+
logger.info("[mcp] delete_cron", { cron_id: a.cron_id });
|
|
1359
1395
|
const deleted = await cronEngine.deleteCron(a.cron_id);
|
|
1360
1396
|
return { content: [{ type: "text", text: JSON.stringify({ deleted, cron_id: a.cron_id }) }] };
|
|
1361
1397
|
}
|
|
1362
1398
|
case "update_cron": {
|
|
1363
|
-
logger.info("
|
|
1399
|
+
logger.info("[mcp] update_cron", { cron_id: a.cron_id });
|
|
1364
1400
|
const updates = {};
|
|
1365
1401
|
if (typeof a.interval_ms === "number")
|
|
1366
1402
|
updates.intervalMs = a.interval_ms;
|
|
@@ -1374,7 +1410,7 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
1374
1410
|
return { content: [{ type: "text", text: JSON.stringify(updated ?? { error: "cron not found" }) }] };
|
|
1375
1411
|
}
|
|
1376
1412
|
case "list_notifications": {
|
|
1377
|
-
logger.info("
|
|
1413
|
+
logger.info("[mcp] list_notifications");
|
|
1378
1414
|
const ns = getNamespace();
|
|
1379
1415
|
const redis = getRedis();
|
|
1380
1416
|
let messages = [];
|
|
@@ -1384,7 +1420,7 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
1384
1420
|
return { content: [{ type: "text", text: JSON.stringify({ messages, total: messages.length, namespace: ns }) }] };
|
|
1385
1421
|
}
|
|
1386
1422
|
case "list_active_repos": {
|
|
1387
|
-
logger.info("
|
|
1423
|
+
logger.info("[mcp] list_active_repos");
|
|
1388
1424
|
const redis = getRedis();
|
|
1389
1425
|
if (!redis)
|
|
1390
1426
|
return { content: [{ type: "text", text: "Redis unavailable" }] };
|
|
@@ -1431,7 +1467,7 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
1431
1467
|
};
|
|
1432
1468
|
}
|
|
1433
1469
|
case "get_pubsub_status": {
|
|
1434
|
-
logger.info("
|
|
1470
|
+
logger.info("[mcp] get_pubsub_status");
|
|
1435
1471
|
const redis = getRedis();
|
|
1436
1472
|
if (!redis)
|
|
1437
1473
|
return { content: [{ type: "text", text: "Redis unavailable" }] };
|
|
@@ -1459,7 +1495,7 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
1459
1495
|
};
|
|
1460
1496
|
}
|
|
1461
1497
|
case "start_meta_agent": {
|
|
1462
|
-
logger.info("
|
|
1498
|
+
logger.info("[mcp] start_meta_agent", { namespace: a.namespace });
|
|
1463
1499
|
const ns = a.namespace;
|
|
1464
1500
|
const repoUrl = a.repo_url;
|
|
1465
1501
|
try {
|
|
@@ -1481,7 +1517,7 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
1481
1517
|
}
|
|
1482
1518
|
}
|
|
1483
1519
|
case "message_meta_agent": {
|
|
1484
|
-
logger.info("
|
|
1520
|
+
logger.info("[mcp] message_meta_agent", { namespace: a.namespace });
|
|
1485
1521
|
const ns = a.namespace;
|
|
1486
1522
|
const message = a.message;
|
|
1487
1523
|
try {
|
|
@@ -1503,7 +1539,7 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
1503
1539
|
}
|
|
1504
1540
|
}
|
|
1505
1541
|
case "list_meta_agents": {
|
|
1506
|
-
logger.info("
|
|
1542
|
+
logger.info("[mcp] list_meta_agents");
|
|
1507
1543
|
const agents = await metaAgentManager.listMetaAgents();
|
|
1508
1544
|
return {
|
|
1509
1545
|
content: [{
|
|
@@ -1513,7 +1549,7 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
1513
1549
|
};
|
|
1514
1550
|
}
|
|
1515
1551
|
case "stop_meta_agent": {
|
|
1516
|
-
logger.info("
|
|
1552
|
+
logger.info("[mcp] stop_meta_agent", { namespace: a.namespace });
|
|
1517
1553
|
const ns = a.namespace;
|
|
1518
1554
|
try {
|
|
1519
1555
|
await metaAgentManager.stopMetaAgent(ns);
|
|
@@ -1534,7 +1570,7 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
1534
1570
|
}
|
|
1535
1571
|
}
|
|
1536
1572
|
case "list_drivers": {
|
|
1537
|
-
logger.info("
|
|
1573
|
+
logger.info("[mcp] list_drivers");
|
|
1538
1574
|
const drivers = getDriverStatus();
|
|
1539
1575
|
return {
|
|
1540
1576
|
content: [{
|
|
@@ -1547,6 +1583,137 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
1547
1583
|
}],
|
|
1548
1584
|
};
|
|
1549
1585
|
}
|
|
1586
|
+
case "export_jobs": {
|
|
1587
|
+
logger.info("[mcp] export_jobs");
|
|
1588
|
+
const days = typeof a.days === "number" ? a.days : 7;
|
|
1589
|
+
const format = a.format === "json" ? "json" : "jsonl";
|
|
1590
|
+
const statusFilter = typeof a.status === "string" ? a.status : undefined;
|
|
1591
|
+
const cutoff = Date.now() - days * 24 * 60 * 60 * 1000;
|
|
1592
|
+
const allRecords = await jobStore.listJobs();
|
|
1593
|
+
const filtered = allRecords.filter((r) => {
|
|
1594
|
+
if (r.startedAt && new Date(r.startedAt).getTime() < cutoff)
|
|
1595
|
+
return false;
|
|
1596
|
+
if (!r.startedAt)
|
|
1597
|
+
return false;
|
|
1598
|
+
if (statusFilter && r.status !== statusFilter)
|
|
1599
|
+
return false;
|
|
1600
|
+
return true;
|
|
1601
|
+
});
|
|
1602
|
+
const records = filtered.map((r) => {
|
|
1603
|
+
let durationSeconds = null;
|
|
1604
|
+
if (r.startedAt && r.finishedAt) {
|
|
1605
|
+
durationSeconds = Math.round((new Date(r.finishedAt).getTime() - new Date(r.startedAt).getTime()) / 1000);
|
|
1606
|
+
}
|
|
1607
|
+
return {
|
|
1608
|
+
id: r.id,
|
|
1609
|
+
status: r.status,
|
|
1610
|
+
repo_url: r.repoUrl,
|
|
1611
|
+
task: (r.task ?? "").slice(0, 500),
|
|
1612
|
+
started_at: r.startedAt ?? null,
|
|
1613
|
+
finished_at: r.finishedAt ?? null,
|
|
1614
|
+
exit_code: r.exitCode ?? null,
|
|
1615
|
+
output_lines: r.outputLineCount ?? 0,
|
|
1616
|
+
score: r.score ?? null,
|
|
1617
|
+
duration_seconds: durationSeconds,
|
|
1618
|
+
};
|
|
1619
|
+
});
|
|
1620
|
+
const text = format === "json"
|
|
1621
|
+
? JSON.stringify(records)
|
|
1622
|
+
: records.map((r) => JSON.stringify(r)).join("\n");
|
|
1623
|
+
return {
|
|
1624
|
+
content: [{ type: "text", text }],
|
|
1625
|
+
};
|
|
1626
|
+
}
|
|
1627
|
+
case "get_cost_report": {
|
|
1628
|
+
logger.info("[mcp] get_cost_report");
|
|
1629
|
+
const days = typeof a.days === "number" ? a.days : 30;
|
|
1630
|
+
const groupBy = a.group_by === "day" ? "day" : a.group_by === "status" ? "status" : "repo";
|
|
1631
|
+
const cutoff = Date.now() - days * 24 * 60 * 60 * 1000;
|
|
1632
|
+
const allRecords = await jobStore.listJobs();
|
|
1633
|
+
const filtered = allRecords.filter((r) => {
|
|
1634
|
+
if (!r.startedAt)
|
|
1635
|
+
return false;
|
|
1636
|
+
return new Date(r.startedAt).getTime() >= cutoff;
|
|
1637
|
+
});
|
|
1638
|
+
const groups = new Map();
|
|
1639
|
+
for (const r of filtered) {
|
|
1640
|
+
let key;
|
|
1641
|
+
if (groupBy === "day") {
|
|
1642
|
+
key = r.startedAt ? r.startedAt.slice(0, 10) : "unknown";
|
|
1643
|
+
}
|
|
1644
|
+
else if (groupBy === "status") {
|
|
1645
|
+
key = r.status;
|
|
1646
|
+
}
|
|
1647
|
+
else {
|
|
1648
|
+
key = r.repoUrl;
|
|
1649
|
+
}
|
|
1650
|
+
const g = groups.get(key) ?? { total_usd: 0, job_count: 0, scores: [] };
|
|
1651
|
+
g.total_usd += r.costUsd ?? 0;
|
|
1652
|
+
g.job_count += 1;
|
|
1653
|
+
if (r.score != null)
|
|
1654
|
+
g.scores.push(r.score);
|
|
1655
|
+
groups.set(key, g);
|
|
1656
|
+
}
|
|
1657
|
+
const summary = Array.from(groups.entries())
|
|
1658
|
+
.map(([key, g]) => ({
|
|
1659
|
+
group: key,
|
|
1660
|
+
total_usd: Math.round(g.total_usd * 10000) / 10000,
|
|
1661
|
+
job_count: g.job_count,
|
|
1662
|
+
avg_cost_usd: g.job_count > 0 ? Math.round((g.total_usd / g.job_count) * 10000) / 10000 : 0,
|
|
1663
|
+
avg_score: g.scores.length > 0 ? Math.round((g.scores.reduce((s, v) => s + v, 0) / g.scores.length) * 1000) / 1000 : null,
|
|
1664
|
+
}))
|
|
1665
|
+
.sort((a, b) => b.total_usd - a.total_usd);
|
|
1666
|
+
return {
|
|
1667
|
+
content: [{
|
|
1668
|
+
type: "text",
|
|
1669
|
+
text: JSON.stringify({ group_by: groupBy, days, total_groups: summary.length, summary }),
|
|
1670
|
+
}],
|
|
1671
|
+
};
|
|
1672
|
+
}
|
|
1673
|
+
case "search_jobs": {
|
|
1674
|
+
logger.info("[mcp] search_jobs", { query: a.query });
|
|
1675
|
+
const query = (a.query ?? "").toLowerCase();
|
|
1676
|
+
const days = typeof a.days === "number" ? a.days : 30;
|
|
1677
|
+
const statusFilter = typeof a.status === "string" ? a.status : undefined;
|
|
1678
|
+
const cutoff = Date.now() - days * 24 * 60 * 60 * 1000;
|
|
1679
|
+
if (!query) {
|
|
1680
|
+
return {
|
|
1681
|
+
content: [{ type: "text", text: JSON.stringify({ error: "query is required" }) }],
|
|
1682
|
+
};
|
|
1683
|
+
}
|
|
1684
|
+
const allRecords = await jobStore.listJobs();
|
|
1685
|
+
const matches = allRecords
|
|
1686
|
+
.filter((r) => {
|
|
1687
|
+
if (!r.startedAt)
|
|
1688
|
+
return false;
|
|
1689
|
+
if (new Date(r.startedAt).getTime() < cutoff)
|
|
1690
|
+
return false;
|
|
1691
|
+
if (statusFilter && r.status !== statusFilter)
|
|
1692
|
+
return false;
|
|
1693
|
+
return (r.task ?? "").toLowerCase().includes(query);
|
|
1694
|
+
})
|
|
1695
|
+
.map((r) => {
|
|
1696
|
+
const task = r.task ?? "";
|
|
1697
|
+
const idx = task.toLowerCase().indexOf(query);
|
|
1698
|
+
const start = Math.max(0, idx - 50);
|
|
1699
|
+
const end = Math.min(task.length, idx + query.length + 50);
|
|
1700
|
+
const snippet = (start > 0 ? "…" : "") + task.slice(start, end) + (end < task.length ? "…" : "");
|
|
1701
|
+
return {
|
|
1702
|
+
id: r.id,
|
|
1703
|
+
status: r.status,
|
|
1704
|
+
repo_url: r.repoUrl,
|
|
1705
|
+
started_at: r.startedAt ?? null,
|
|
1706
|
+
score: r.score ?? null,
|
|
1707
|
+
task_snippet: snippet,
|
|
1708
|
+
};
|
|
1709
|
+
});
|
|
1710
|
+
return {
|
|
1711
|
+
content: [{
|
|
1712
|
+
type: "text",
|
|
1713
|
+
text: JSON.stringify({ query: a.query, days, total: matches.length, matches }),
|
|
1714
|
+
}],
|
|
1715
|
+
};
|
|
1716
|
+
}
|
|
1550
1717
|
default:
|
|
1551
1718
|
throw new Error(`Unknown tool: ${name}`);
|
|
1552
1719
|
}
|
|
@@ -1569,6 +1736,18 @@ await manager.init();
|
|
|
1569
1736
|
await seedBuiltinProfiles(profileStore);
|
|
1570
1737
|
await coordinator.start();
|
|
1571
1738
|
await cronEngine.start();
|
|
1739
|
+
// Startup summary — logged after all engines are initialized
|
|
1740
|
+
logger.info("[cc-agent] started", { version: PKG_VERSION, namespace });
|
|
1741
|
+
const _startupJobs = manager.list();
|
|
1742
|
+
const _jobCounts = {};
|
|
1743
|
+
for (const j of _startupJobs)
|
|
1744
|
+
_jobCounts[j.status] = (_jobCounts[j.status] ?? 0) + 1;
|
|
1745
|
+
logger.info("[cc-agent] startup", { jobs_total: _startupJobs.length, ..._jobCounts });
|
|
1746
|
+
const _startupCrons = await cronEngine.listCrons();
|
|
1747
|
+
logger.info("[cc-agent] startup", { crons_loaded: _startupCrons.length });
|
|
1748
|
+
for (const _cron of _startupCrons) {
|
|
1749
|
+
logger.info("[cron] registered", { id: _cron.id, schedule: _cron.schedule, intervalMs: _cron.intervalMs });
|
|
1750
|
+
}
|
|
1572
1751
|
metaAgentManager.startPoller();
|
|
1573
1752
|
const transport = new StdioServerTransport();
|
|
1574
1753
|
await server.connect(transport);
|