@agentistics/mcp 2.17.0 → 2.18.1
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/agentistics-mcp.js +330 -7
- package/package.json +1 -1
package/dist/agentistics-mcp.js
CHANGED
|
@@ -380,7 +380,7 @@ function filterSessions(sessions, harness) {
|
|
|
380
380
|
return sessions;
|
|
381
381
|
return sessions.filter((s) => sessionHarness(s) === harness);
|
|
382
382
|
}
|
|
383
|
-
function
|
|
383
|
+
function sessionTokens2(s) {
|
|
384
384
|
const input = s.input_tokens ?? 0;
|
|
385
385
|
const output = s.output_tokens ?? 0;
|
|
386
386
|
const cacheRead = s.cache_read_input_tokens ?? 0;
|
|
@@ -453,6 +453,20 @@ async function apiGet(path) {
|
|
|
453
453
|
async function getPrefs() {
|
|
454
454
|
return apiGet("/api/preferences");
|
|
455
455
|
}
|
|
456
|
+
async function apiSend(method, path, body) {
|
|
457
|
+
const res = await fetch(`${API}${path}`, {
|
|
458
|
+
method,
|
|
459
|
+
...body === undefined ? {} : {
|
|
460
|
+
headers: { "Content-Type": "application/json" },
|
|
461
|
+
body: JSON.stringify(body)
|
|
462
|
+
}
|
|
463
|
+
});
|
|
464
|
+
if (!res.ok) {
|
|
465
|
+
const detail = await res.text().catch(() => "");
|
|
466
|
+
throw new Error(`${method} ${path} → HTTP ${res.status}${detail ? ` ${detail.slice(0, 400)}` : ""}`);
|
|
467
|
+
}
|
|
468
|
+
return res.json().catch(() => ({}));
|
|
469
|
+
}
|
|
456
470
|
async function putPrefs(patch) {
|
|
457
471
|
const res = await fetch(`${API}/api/preferences`, {
|
|
458
472
|
method: "PUT",
|
|
@@ -503,6 +517,188 @@ function fillGaps(items, gridW = 12) {
|
|
|
503
517
|
}
|
|
504
518
|
var server = new Server({ name: "agentistics", version: "1.0.0" }, { capabilities: { tools: {} } });
|
|
505
519
|
var TOOLS = [
|
|
520
|
+
{
|
|
521
|
+
name: "agentistics_tasks",
|
|
522
|
+
description: "BETA — the task board is new and still changing; its shapes may move between releases. List every task on the ALM board with its rollup (cost, rounds, sessions used, tokens) plus a board-wide overview: tasks in flight, delivered, average cost per task and per delivery, top models and harnesses. START HERE to see what work exists and what the other assistants are on.",
|
|
523
|
+
inputSchema: { type: "object", properties: {}, required: [] }
|
|
524
|
+
},
|
|
525
|
+
{
|
|
526
|
+
name: "agentistics_task",
|
|
527
|
+
description: "BETA — the task board is new and still changing; its shapes may move between releases. Open ONE task: its attempts (one per configuration tried), the sessions filed under it with their live state, its comments, subtasks, files, links and full metrics (models, harnesses, agent runs, tokens, delivery time). `ref` is the task id or its exact title.",
|
|
528
|
+
inputSchema: {
|
|
529
|
+
type: "object",
|
|
530
|
+
properties: { ref: { type: "string", description: "Task id or exact title." } },
|
|
531
|
+
required: ["ref"]
|
|
532
|
+
}
|
|
533
|
+
},
|
|
534
|
+
{
|
|
535
|
+
name: "agentistics_task_create",
|
|
536
|
+
description: "BETA — the task board is new and still changing; its shapes may move between releases. Create a task. A title is required; a description is optional. Creating a title that already exists returns the existing task rather than a duplicate, so this is safe to call before filing work under a name you are not sure exists.",
|
|
537
|
+
inputSchema: {
|
|
538
|
+
type: "object",
|
|
539
|
+
properties: { title: { type: "string" }, detail: { type: "string" } },
|
|
540
|
+
required: ["title"]
|
|
541
|
+
}
|
|
542
|
+
},
|
|
543
|
+
{
|
|
544
|
+
name: "agentistics_task_status",
|
|
545
|
+
description: "BETA — the task board is new and still changing; its shapes may move between releases. Move a task: backlog | todo | in_progress | blocked | in_review | done | abandoned. Only `done` stamps a delivery and closes rounds-to-delivery; `abandoned` records that the work was given up on, which is a real outcome and not a failure to report. **`blocked` REQUIRES a `reason` or a `blockedBy` list** and is refused (422) without one: it is the status that names a problem somebody has to go and solve, and a blocked card that does not say what it is waiting on cannot be unblocked by anyone but you. Pass `actor` (who you are) so the move is recorded against you in the activity log.",
|
|
546
|
+
inputSchema: {
|
|
547
|
+
type: "object",
|
|
548
|
+
properties: {
|
|
549
|
+
ref: { type: "string" },
|
|
550
|
+
status: {
|
|
551
|
+
type: "string",
|
|
552
|
+
enum: ["backlog", "todo", "in_progress", "blocked", "in_review", "done", "abandoned"]
|
|
553
|
+
},
|
|
554
|
+
actor: { type: "string" },
|
|
555
|
+
reason: {
|
|
556
|
+
type: "string",
|
|
557
|
+
description: "Why it is blocked, in your own words. Required by `blocked` unless you pass `blockedBy`."
|
|
558
|
+
},
|
|
559
|
+
blockedBy: {
|
|
560
|
+
type: "array",
|
|
561
|
+
items: { type: "string" },
|
|
562
|
+
description: "Task ids that must finish first — the other way to answer 'blocked on what'."
|
|
563
|
+
}
|
|
564
|
+
},
|
|
565
|
+
required: ["ref", "status"]
|
|
566
|
+
}
|
|
567
|
+
},
|
|
568
|
+
{
|
|
569
|
+
name: "agentistics_task_comment",
|
|
570
|
+
description: "BETA — the task board is new and still changing; its shapes may move between releases. Leave a comment on a task. Use it to tell the person and the other assistants what you did, what you found, or what you are blocked on. `author` is free text — say who you are (e.g. 'claude:3f5f').",
|
|
571
|
+
inputSchema: {
|
|
572
|
+
type: "object",
|
|
573
|
+
properties: { ref: { type: "string" }, body: { type: "string" }, author: { type: "string" } },
|
|
574
|
+
required: ["ref", "body"]
|
|
575
|
+
}
|
|
576
|
+
},
|
|
577
|
+
{
|
|
578
|
+
name: "agentistics_task_subtask",
|
|
579
|
+
description: "BETA — the task board is new and still changing; its shapes may move between releases. Add a subtask, or change one. Pass `title` to add; pass `id` with `done`, `status`, `assignee`, `dueDate` or `startDate` to edit one. A subtask carries the same columns its parent does, but no cost of its own: cost is measured per SESSION and rolls up to the task. To put a session ON a subtask use agentistics_task_session with `subtaskId` — a subtask holds ANY NUMBER of sessions, so the link lives on the session, not in a field here.",
|
|
580
|
+
inputSchema: {
|
|
581
|
+
type: "object",
|
|
582
|
+
properties: {
|
|
583
|
+
ref: { type: "string" },
|
|
584
|
+
title: { type: "string" },
|
|
585
|
+
id: { type: "string" },
|
|
586
|
+
done: { type: "boolean" },
|
|
587
|
+
status: {
|
|
588
|
+
type: "string",
|
|
589
|
+
enum: ["backlog", "todo", "in_progress", "blocked", "in_review", "done", "abandoned"]
|
|
590
|
+
},
|
|
591
|
+
assignee: { type: "string" },
|
|
592
|
+
dueDate: { type: "string" },
|
|
593
|
+
startDate: { type: "string" }
|
|
594
|
+
},
|
|
595
|
+
required: ["ref"]
|
|
596
|
+
}
|
|
597
|
+
},
|
|
598
|
+
{
|
|
599
|
+
name: "agentistics_task_link",
|
|
600
|
+
description: "BETA — the task board is new and still changing; its shapes may move between releases. Attach a link to a task — a pull request, an issue, a document. Pass `url` (http/https only) with an optional `label` and `kind` ('pr', 'issue', …). Pass `remove` with a link id to detach one.",
|
|
601
|
+
inputSchema: {
|
|
602
|
+
type: "object",
|
|
603
|
+
properties: {
|
|
604
|
+
ref: { type: "string" },
|
|
605
|
+
url: { type: "string" },
|
|
606
|
+
label: { type: "string" },
|
|
607
|
+
kind: { type: "string" },
|
|
608
|
+
remove: { type: "string", description: "Link id to remove instead of adding." }
|
|
609
|
+
},
|
|
610
|
+
required: ["ref"]
|
|
611
|
+
}
|
|
612
|
+
},
|
|
613
|
+
{
|
|
614
|
+
name: "agentistics_task_blocked_by",
|
|
615
|
+
description: "BETA — the task board is new and still changing; its shapes may move between releases. Set which tasks must finish before this one can proceed. Pass the full list of blocker task ids (it replaces, it does not append). A task cannot block itself.",
|
|
616
|
+
inputSchema: {
|
|
617
|
+
type: "object",
|
|
618
|
+
properties: { ref: { type: "string" }, blockedBy: { type: "array", items: { type: "string" } } },
|
|
619
|
+
required: ["ref", "blockedBy"]
|
|
620
|
+
}
|
|
621
|
+
},
|
|
622
|
+
{
|
|
623
|
+
name: "agentistics_task_next",
|
|
624
|
+
description: "BETA — the task board is new and still changing; its shapes may move between releases. THE ORCHESTRATION CALL: what you can pick up right now. Returns the tasks that are open, not blocked by unfinished work, and not in another agent's hands — most urgent first, numbered from 1. It also returns `withheld`, every task that is NOT available with the reason (closed / blocked / claimed / status), because an agent told 'nothing' learns nothing; and `progress`, which separates 'it is all done' from 'everything is in somebody's hands right now'. Pass `actor` (who you are) so a task you already hold comes back as available to you rather than as taken.",
|
|
625
|
+
inputSchema: {
|
|
626
|
+
type: "object",
|
|
627
|
+
properties: {
|
|
628
|
+
actor: { type: "string", description: "Who is asking, e.g. 'claude:3f5f'." },
|
|
629
|
+
limit: { type: "number" }
|
|
630
|
+
},
|
|
631
|
+
required: []
|
|
632
|
+
}
|
|
633
|
+
},
|
|
634
|
+
{
|
|
635
|
+
name: "agentistics_task_claim",
|
|
636
|
+
description: "BETA — the task board is new and still changing; its shapes may move between releases. TAKE a task before working on it, so two agents cannot do the same one twice. This is a LEASE, not a lock: it expires (default 30 minutes) and the task returns to the board on its own, so an agent that dies does not hold work forever. Re-claiming as the same `by` REFRESHES the lease — do that while you are still working. Pass `release: true` to give it back when you finish or stop. A refusal names who holds it and until when; `takeover` is for a person overriding a live claim on purpose and an agent should not send it.",
|
|
637
|
+
inputSchema: {
|
|
638
|
+
type: "object",
|
|
639
|
+
properties: {
|
|
640
|
+
ref: { type: "string" },
|
|
641
|
+
by: { type: "string", description: "Who you are. Free text, e.g. 'claude:3f5f'." },
|
|
642
|
+
release: { type: "boolean" },
|
|
643
|
+
leaseMs: { type: "number", description: "How long the claim lasts. Default 30 minutes." },
|
|
644
|
+
sessionId: { type: "string", description: "The managed session doing the work, if any." },
|
|
645
|
+
note: { type: "string" },
|
|
646
|
+
takeover: { type: "boolean" },
|
|
647
|
+
force: { type: "boolean", description: "Release a claim you do not hold." }
|
|
648
|
+
},
|
|
649
|
+
required: ["ref", "by"]
|
|
650
|
+
}
|
|
651
|
+
},
|
|
652
|
+
{
|
|
653
|
+
name: "agentistics_task_activity",
|
|
654
|
+
description: "BETA — the task board is new and still changing; its shapes may move between releases. What has been HAPPENING, newest first — status moves, claims, releases, priority and assignee changes, sessions filed. Pass `ref` for one task, or nothing for the whole board. On a board several agents drive this is how you find out what the others did without asking them.",
|
|
655
|
+
inputSchema: {
|
|
656
|
+
type: "object",
|
|
657
|
+
properties: { ref: { type: "string" }, limit: { type: "number" } },
|
|
658
|
+
required: []
|
|
659
|
+
}
|
|
660
|
+
},
|
|
661
|
+
{
|
|
662
|
+
name: "agentistics_task_edit",
|
|
663
|
+
description: "BETA — the task board is new and still changing; its shapes may move between releases. Set a task's fields: `title`, `detail`, `priority` (urgent | high | medium | low | none), `assignee`, `dueDate` / `startDate` (yyyy-mm-dd), `labels`. An absent field is left alone; an EMPTY STRING clears it. `priority` defaults to `none`, which means 'nobody has said' and is not the same as `low`. Pass `actor` so the change is recorded against you in the activity log.",
|
|
664
|
+
inputSchema: {
|
|
665
|
+
type: "object",
|
|
666
|
+
properties: {
|
|
667
|
+
ref: { type: "string" },
|
|
668
|
+
title: { type: "string" },
|
|
669
|
+
detail: { type: "string" },
|
|
670
|
+
priority: { type: "string", enum: ["urgent", "high", "medium", "low", "none"] },
|
|
671
|
+
assignee: { type: "string" },
|
|
672
|
+
dueDate: { type: "string" },
|
|
673
|
+
startDate: { type: "string" },
|
|
674
|
+
labels: { type: "array", items: { type: "string" } },
|
|
675
|
+
actor: { type: "string" }
|
|
676
|
+
},
|
|
677
|
+
required: ["ref"]
|
|
678
|
+
}
|
|
679
|
+
},
|
|
680
|
+
{
|
|
681
|
+
name: "agentistics_task_session",
|
|
682
|
+
description: "BETA — the task board is new and still changing; its shapes may move between releases. File a SESSION under a SUBTASK of a task, which is what makes the task measurable: its cost, tokens, rounds and harness all come from the sessions filed under its parts. **A session is NEVER filed under a task directly — always name a `subtaskId`.** A task is the unit of DELIVERY and a subtask is the unit of WORK; filing at both levels made 'does this cost include the subtasks' unanswerable, so the server refuses it. Use agentistics_task to read the subtasks, or agentistics_task_subtask to create one, then pass `ref` + `sessionId` (a managed session id or conversation id) + `subtaskId`. Pass `detach` with a session id to unfile one. The task inherits the session's repository when it has none.",
|
|
683
|
+
inputSchema: {
|
|
684
|
+
type: "object",
|
|
685
|
+
properties: {
|
|
686
|
+
ref: { type: "string" },
|
|
687
|
+
sessionId: { type: "string" },
|
|
688
|
+
subtaskId: {
|
|
689
|
+
type: "string",
|
|
690
|
+
description: "The subtask to file it under. REQUIRED to file — a task does not take sessions itself."
|
|
691
|
+
},
|
|
692
|
+
detach: { type: "string" }
|
|
693
|
+
},
|
|
694
|
+
required: []
|
|
695
|
+
}
|
|
696
|
+
},
|
|
697
|
+
{
|
|
698
|
+
name: "agentistics_task_delete",
|
|
699
|
+
description: "BETA — the task board is new and still changing; its shapes may move between releases. Delete a task and its comments, subtasks, files and links. The SESSIONS filed under it are kept — deleting a board entry never deletes work.",
|
|
700
|
+
inputSchema: { type: "object", properties: { ref: { type: "string" } }, required: ["ref"] }
|
|
701
|
+
},
|
|
506
702
|
{
|
|
507
703
|
name: "agentistics_summary",
|
|
508
704
|
description: "Get an overview of AI coding usage metrics (across all tracked harnesses — Claude Code, Codex, Gemini, Copilot, Antigravity — or scoped to one): total tokens, estimated cost, sessions, streak, most used model, and top project. Good starting point for any metrics question.",
|
|
@@ -711,6 +907,133 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
711
907
|
const { name, arguments: args } = req.params;
|
|
712
908
|
try {
|
|
713
909
|
switch (name) {
|
|
910
|
+
case "agentistics_tasks": {
|
|
911
|
+
const body = await apiGet("/api/tasks");
|
|
912
|
+
return { content: [{ type: "text", text: JSON.stringify(body, null, 2) }] };
|
|
913
|
+
}
|
|
914
|
+
case "agentistics_task": {
|
|
915
|
+
const ref = String(args?.ref ?? "");
|
|
916
|
+
const body = await apiGet(`/api/tasks/${encodeURIComponent(ref)}`);
|
|
917
|
+
return { content: [{ type: "text", text: JSON.stringify(body, null, 2) }] };
|
|
918
|
+
}
|
|
919
|
+
case "agentistics_task_create": {
|
|
920
|
+
const a = args;
|
|
921
|
+
const body = await apiSend("POST", "/api/tasks", { title: a?.title, detail: a?.detail });
|
|
922
|
+
return { content: [{ type: "text", text: JSON.stringify(body, null, 2) }] };
|
|
923
|
+
}
|
|
924
|
+
case "agentistics_task_status": {
|
|
925
|
+
const a = args;
|
|
926
|
+
const body = await apiSend("POST", `/api/tasks/${encodeURIComponent(String(a?.ref))}`, {
|
|
927
|
+
status: a?.status,
|
|
928
|
+
...typeof a?.actor === "string" ? { actor: a.actor } : {},
|
|
929
|
+
...typeof a?.reason === "string" ? { reason: a.reason } : {},
|
|
930
|
+
...Array.isArray(a?.blockedBy) ? { blockedBy: a.blockedBy } : {}
|
|
931
|
+
});
|
|
932
|
+
return { content: [{ type: "text", text: JSON.stringify(body, null, 2) }] };
|
|
933
|
+
}
|
|
934
|
+
case "agentistics_task_comment": {
|
|
935
|
+
const a = args;
|
|
936
|
+
const body = await apiSend("POST", `/api/tasks/${encodeURIComponent(String(a?.ref))}/comments`, {
|
|
937
|
+
body: a?.body,
|
|
938
|
+
author: a?.author ?? "assistant"
|
|
939
|
+
});
|
|
940
|
+
return { content: [{ type: "text", text: JSON.stringify(body, null, 2) }] };
|
|
941
|
+
}
|
|
942
|
+
case "agentistics_task_subtask": {
|
|
943
|
+
const a = args;
|
|
944
|
+
const cols = ["status", "assignee", "dueDate", "startDate", "title"];
|
|
945
|
+
const named = cols.filter((c) => typeof a?.[c] === "string");
|
|
946
|
+
const payload = a?.id !== undefined ? named.length > 0 ? { id: a.id, ...Object.fromEntries(named.map((c) => [c, a[c]])) } : { id: a.id, done: a?.done === true } : { title: a?.title };
|
|
947
|
+
const body = await apiSend("POST", `/api/tasks/${encodeURIComponent(String(a?.ref))}/subtasks`, payload);
|
|
948
|
+
return { content: [{ type: "text", text: JSON.stringify(body, null, 2) }] };
|
|
949
|
+
}
|
|
950
|
+
case "agentistics_task_link": {
|
|
951
|
+
const a = args;
|
|
952
|
+
const payload = a?.remove ? { remove: a.remove } : { url: a?.url, label: a?.label, kind: a?.kind };
|
|
953
|
+
const body = await apiSend("POST", `/api/tasks/${encodeURIComponent(String(a?.ref))}/links`, payload);
|
|
954
|
+
return { content: [{ type: "text", text: JSON.stringify(body, null, 2) }] };
|
|
955
|
+
}
|
|
956
|
+
case "agentistics_task_blocked_by": {
|
|
957
|
+
const a = args;
|
|
958
|
+
const body = await apiSend("POST", `/api/tasks/${encodeURIComponent(String(a?.ref))}`, {
|
|
959
|
+
blockedBy: a?.blockedBy ?? []
|
|
960
|
+
});
|
|
961
|
+
return { content: [{ type: "text", text: JSON.stringify(body, null, 2) }] };
|
|
962
|
+
}
|
|
963
|
+
case "agentistics_task_next": {
|
|
964
|
+
const a = args;
|
|
965
|
+
const q = new URLSearchParams;
|
|
966
|
+
if (a?.actor)
|
|
967
|
+
q.set("actor", String(a.actor));
|
|
968
|
+
if (a?.limit)
|
|
969
|
+
q.set("limit", String(a.limit));
|
|
970
|
+
const body = await apiGet(`/api/tasks/next${q.toString() ? `?${q}` : ""}`);
|
|
971
|
+
return { content: [{ type: "text", text: JSON.stringify(body, null, 2) }] };
|
|
972
|
+
}
|
|
973
|
+
case "agentistics_task_claim": {
|
|
974
|
+
const a = args;
|
|
975
|
+
const ref = encodeURIComponent(String(a?.ref ?? ""));
|
|
976
|
+
const body = await apiSend("POST", `/api/tasks/${ref}/claim`, {
|
|
977
|
+
by: a?.by,
|
|
978
|
+
...a?.release === true ? { release: true } : {},
|
|
979
|
+
...a?.leaseMs !== undefined ? { leaseMs: a.leaseMs } : {},
|
|
980
|
+
...a?.sessionId ? { sessionId: a.sessionId } : {},
|
|
981
|
+
...a?.note ? { note: a.note } : {},
|
|
982
|
+
...a?.takeover === true ? { takeover: true } : {},
|
|
983
|
+
...a?.force === true ? { force: true } : {}
|
|
984
|
+
});
|
|
985
|
+
return { content: [{ type: "text", text: JSON.stringify(body, null, 2) }] };
|
|
986
|
+
}
|
|
987
|
+
case "agentistics_task_activity": {
|
|
988
|
+
const a = args;
|
|
989
|
+
const q = new URLSearchParams;
|
|
990
|
+
if (a?.ref)
|
|
991
|
+
q.set("task", String(a.ref));
|
|
992
|
+
if (a?.limit)
|
|
993
|
+
q.set("limit", String(a.limit));
|
|
994
|
+
const body = await apiGet(`/api/tasks/activity${q.toString() ? `?${q}` : ""}`);
|
|
995
|
+
return { content: [{ type: "text", text: JSON.stringify(body, null, 2) }] };
|
|
996
|
+
}
|
|
997
|
+
case "agentistics_task_edit": {
|
|
998
|
+
const a = args;
|
|
999
|
+
const ref = encodeURIComponent(String(a?.ref ?? ""));
|
|
1000
|
+
const patch = {};
|
|
1001
|
+
for (const f of ["title", "detail", "priority", "assignee", "dueDate", "startDate", "actor"]) {
|
|
1002
|
+
if (typeof a?.[f] === "string")
|
|
1003
|
+
patch[f] = a[f];
|
|
1004
|
+
}
|
|
1005
|
+
if (Array.isArray(a?.labels))
|
|
1006
|
+
patch.labels = a.labels;
|
|
1007
|
+
const body = await apiSend("POST", `/api/tasks/${ref}`, patch);
|
|
1008
|
+
return { content: [{ type: "text", text: JSON.stringify(body, null, 2) }] };
|
|
1009
|
+
}
|
|
1010
|
+
case "agentistics_task_session": {
|
|
1011
|
+
const a = args;
|
|
1012
|
+
if (a?.detach) {
|
|
1013
|
+
const body = await apiSend("POST", "/api/tasks/x/sessions", { detach: a.detach });
|
|
1014
|
+
return { content: [{ type: "text", text: JSON.stringify(body, null, 2) }] };
|
|
1015
|
+
}
|
|
1016
|
+
const ref = encodeURIComponent(String(a?.ref ?? ""));
|
|
1017
|
+
if (!a?.subtaskId) {
|
|
1018
|
+
return {
|
|
1019
|
+
content: [{
|
|
1020
|
+
type: "text",
|
|
1021
|
+
text: "A session is filed under a SUBTASK, never under the delivery itself — a task is the " + "unit of delivery, a subtask the unit of work. Read the parts with agentistics_task, or " + "create one with agentistics_task_subtask, then call this again with `subtaskId`."
|
|
1022
|
+
}],
|
|
1023
|
+
isError: true
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
1026
|
+
const body = await apiSend("POST", `/api/tasks/${ref}/sessions`, {
|
|
1027
|
+
sessionId: a?.sessionId,
|
|
1028
|
+
subtaskId: a.subtaskId
|
|
1029
|
+
});
|
|
1030
|
+
return { content: [{ type: "text", text: JSON.stringify(body, null, 2) }] };
|
|
1031
|
+
}
|
|
1032
|
+
case "agentistics_task_delete": {
|
|
1033
|
+
const ref = String(args?.ref ?? "");
|
|
1034
|
+
const body = await apiSend("DELETE", `/api/tasks/${encodeURIComponent(ref)}`);
|
|
1035
|
+
return { content: [{ type: "text", text: JSON.stringify(body, null, 2) }] };
|
|
1036
|
+
}
|
|
714
1037
|
case "agentistics_summary": {
|
|
715
1038
|
const harness = args?.harness;
|
|
716
1039
|
const unified = !harness || harness === "all";
|
|
@@ -724,7 +1047,7 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
724
1047
|
const projectSessions = {};
|
|
725
1048
|
const activeDates = new Set;
|
|
726
1049
|
for (const s of allSessions) {
|
|
727
|
-
const { input, output, cacheRead, cacheWrite, cost } =
|
|
1050
|
+
const { input, output, cacheRead, cacheWrite, cost } = sessionTokens2(s);
|
|
728
1051
|
totalInput += input;
|
|
729
1052
|
totalOutput += output;
|
|
730
1053
|
totalCacheRead += cacheRead;
|
|
@@ -767,7 +1090,7 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
767
1090
|
const hs = sessions.filter((s) => sessionHarness(s) === h);
|
|
768
1091
|
let input = 0, output = 0, cacheRead = 0, cacheWrite = 0, cost = 0, messages = 0, lastActive = "";
|
|
769
1092
|
for (const s of hs) {
|
|
770
|
-
const t =
|
|
1093
|
+
const t = sessionTokens2(s);
|
|
771
1094
|
input += t.input;
|
|
772
1095
|
output += t.output;
|
|
773
1096
|
cacheRead += t.cacheRead;
|
|
@@ -804,7 +1127,7 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
804
1127
|
if (!byPath[key])
|
|
805
1128
|
byPath[key] = { sessions: 0, inputTokens: 0, outputTokens: 0, cacheRead: 0, cacheWrite: 0, costUSD: 0, messages: 0, lastActive: "", languages: [] };
|
|
806
1129
|
const agg = byPath[key];
|
|
807
|
-
const { input, output, cacheRead, cacheWrite, cost } =
|
|
1130
|
+
const { input, output, cacheRead, cacheWrite, cost } = sessionTokens2(s);
|
|
808
1131
|
agg.sessions += 1;
|
|
809
1132
|
agg.inputTokens += input;
|
|
810
1133
|
agg.outputTokens += output;
|
|
@@ -841,7 +1164,7 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
841
1164
|
const harness = args?.harness;
|
|
842
1165
|
const data = await apiGet("/api/data");
|
|
843
1166
|
const rows = filterSessions(data.sessions ?? [], harness).slice(0, limit).map((s) => {
|
|
844
|
-
const { input, output, cacheRead, cacheWrite, cost } =
|
|
1167
|
+
const { input, output, cacheRead, cacheWrite, cost } = sessionTokens2(s);
|
|
845
1168
|
return {
|
|
846
1169
|
id: s.session_id,
|
|
847
1170
|
harness: sessionHarness(s),
|
|
@@ -882,7 +1205,7 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
882
1205
|
if (!byModel[model])
|
|
883
1206
|
byModel[model] = { inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, estimatedCostUSD: 0 };
|
|
884
1207
|
const agg = byModel[model];
|
|
885
|
-
const { input, output, cacheRead, cacheWrite, cost } =
|
|
1208
|
+
const { input, output, cacheRead, cacheWrite, cost } = sessionTokens2(s);
|
|
886
1209
|
agg.inputTokens += input;
|
|
887
1210
|
agg.outputTokens += output;
|
|
888
1211
|
agg.cacheReadTokens += cacheRead;
|
|
@@ -1142,7 +1465,7 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
1142
1465
|
if (!byRemote[key])
|
|
1143
1466
|
byRemote[key] = { sessions: 0, messages: 0, inputTokens: 0, outputTokens: 0, cacheRead: 0, cacheWrite: 0, costUSD: 0, lastActive: "" };
|
|
1144
1467
|
const agg = byRemote[key];
|
|
1145
|
-
const { input, output, cacheRead, cacheWrite, cost } =
|
|
1468
|
+
const { input, output, cacheRead, cacheWrite, cost } = sessionTokens2(s);
|
|
1146
1469
|
agg.sessions += 1;
|
|
1147
1470
|
agg.messages += sessionMessages(s);
|
|
1148
1471
|
agg.inputTokens += input;
|