@gethmy/mcp 2.24.0 → 2.25.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/dist/cli.js +182 -74
- package/dist/index.js +182 -74
- package/dist/lib/api-client.js +59 -70
- package/package.json +1 -1
- package/src/api-client.ts +165 -5
- package/src/playbook-metric-warnings.ts +56 -0
- package/src/prompt-builder.ts +9 -120
- package/src/read-consumer.ts +16 -0
- package/src/server.ts +41 -11
package/dist/cli.js
CHANGED
|
@@ -354,15 +354,7 @@ ${lines.join(`
|
|
|
354
354
|
`)}`;
|
|
355
355
|
}
|
|
356
356
|
function generatePrompt(options) {
|
|
357
|
-
const {
|
|
358
|
-
card,
|
|
359
|
-
column,
|
|
360
|
-
variant,
|
|
361
|
-
customConstraints,
|
|
362
|
-
memories,
|
|
363
|
-
assembledContext,
|
|
364
|
-
assemblyId
|
|
365
|
-
} = options;
|
|
357
|
+
const { card, column, variant, customConstraints, memories, assemblyId } = options;
|
|
366
358
|
const contextOpts = {
|
|
367
359
|
includeTitle: true,
|
|
368
360
|
includeDescription: true,
|
|
@@ -440,10 +432,7 @@ ${card.description}`);
|
|
|
440
432
|
roleFraming.outputSuggestions.forEach((s) => {
|
|
441
433
|
sections.push(`- ${s}`);
|
|
442
434
|
});
|
|
443
|
-
if (
|
|
444
|
-
sections.push(`
|
|
445
|
-
${assembledContext}`);
|
|
446
|
-
} else if (memories && memories.length > 0) {
|
|
435
|
+
if (memories && memories.length > 0) {
|
|
447
436
|
sections.push(`
|
|
448
437
|
## Relevant Memories`);
|
|
449
438
|
sections.push(`*${memories.length} memories recalled from knowledge graph:*`);
|
|
@@ -454,7 +443,7 @@ ${assembledContext}`);
|
|
|
454
443
|
sections.push(memory.content);
|
|
455
444
|
}
|
|
456
445
|
}
|
|
457
|
-
const oneThingLine = synthesizeOneThing(card, subtasks, links
|
|
446
|
+
const oneThingLine = synthesizeOneThing(card, subtasks, links);
|
|
458
447
|
if (oneThingLine) {
|
|
459
448
|
sections.push(`
|
|
460
449
|
## Recommended Next Step
|
|
@@ -481,7 +470,7 @@ ${customConstraints}`);
|
|
|
481
470
|
*Card #${card.short_id} | Generated for ${variant} mode*`);
|
|
482
471
|
const prompt = sections.join(`
|
|
483
472
|
`);
|
|
484
|
-
const memoryCount =
|
|
473
|
+
const memoryCount = memories?.length ?? 0;
|
|
485
474
|
return {
|
|
486
475
|
prompt,
|
|
487
476
|
variant,
|
|
@@ -502,40 +491,7 @@ ${customConstraints}`);
|
|
|
502
491
|
version: PROMPT_TEMPLATE_VERSION
|
|
503
492
|
};
|
|
504
493
|
}
|
|
505
|
-
function
|
|
506
|
-
const result = {
|
|
507
|
-
lastSessionStatus: null,
|
|
508
|
-
lastSessionTask: null,
|
|
509
|
-
lastSessionProgress: null,
|
|
510
|
-
blockers: [],
|
|
511
|
-
procedureNextStep: null
|
|
512
|
-
};
|
|
513
|
-
const sessionMatches = assembledContext.match(/### Session:.*?\n([\s\S]*?)(?=\n###|\n## |\n---|\n\*Assembly|$)/g);
|
|
514
|
-
if (sessionMatches && sessionMatches.length > 0) {
|
|
515
|
-
const latest = sessionMatches[0];
|
|
516
|
-
if (/Completed work on/i.test(latest)) {
|
|
517
|
-
result.lastSessionStatus = "completed";
|
|
518
|
-
} else if (/Paused work on|status:\s*paused/i.test(latest)) {
|
|
519
|
-
result.lastSessionStatus = "paused";
|
|
520
|
-
}
|
|
521
|
-
const taskMatch = latest.match(/Final task:\s*(.+)/);
|
|
522
|
-
if (taskMatch)
|
|
523
|
-
result.lastSessionTask = taskMatch[1].trim();
|
|
524
|
-
const progressMatch = latest.match(/Progress:\s*(\d+)%/);
|
|
525
|
-
if (progressMatch)
|
|
526
|
-
result.lastSessionProgress = parseInt(progressMatch[1], 10);
|
|
527
|
-
}
|
|
528
|
-
const blockerMatches = assembledContext.match(/(?:blocker|blocked by|blocking):\s*(.+)/gi);
|
|
529
|
-
if (blockerMatches) {
|
|
530
|
-
result.blockers = blockerMatches.map((m) => m.replace(/(?:blocker|blocked by|blocking):\s*/i, "").trim());
|
|
531
|
-
}
|
|
532
|
-
const stepMatches = assembledContext.match(/^\d+\.\s+(?!.*\*\*\[key step\]\*\*.*✓)(.+?)(?:\s*\*\*\[key step\]\*\*)?$/gm);
|
|
533
|
-
if (stepMatches && stepMatches.length > 0) {
|
|
534
|
-
result.procedureNextStep = stepMatches[0].replace(/^\d+\.\s+/, "").replace(/\s*\*\*\[key step\]\*\*.*$/, "").trim();
|
|
535
|
-
}
|
|
536
|
-
return result;
|
|
537
|
-
}
|
|
538
|
-
function synthesizeOneThing(card, subtasks, links, assembledContext) {
|
|
494
|
+
function synthesizeOneThing(card, subtasks, links) {
|
|
539
495
|
if (card.done)
|
|
540
496
|
return null;
|
|
541
497
|
const blockers = links.filter((l) => l.display_type === "is_blocked_by" && l.direction === "incoming");
|
|
@@ -543,14 +499,6 @@ function synthesizeOneThing(card, subtasks, links, assembledContext) {
|
|
|
543
499
|
const blocker = blockers[0];
|
|
544
500
|
return `Unblock first: resolve #${blocker.target_card.short_id} "${blocker.target_card.title}" which is blocking this card.`;
|
|
545
501
|
}
|
|
546
|
-
const session = assembledContext ? extractSessionInsights(assembledContext) : null;
|
|
547
|
-
if (session?.blockers && session.blockers.length > 0) {
|
|
548
|
-
return `Resolve blocker: ${session.blockers[0]}`;
|
|
549
|
-
}
|
|
550
|
-
if (session?.lastSessionStatus === "paused" && session.lastSessionTask) {
|
|
551
|
-
const progress = session.lastSessionProgress ? ` (was ${session.lastSessionProgress}% complete)` : "";
|
|
552
|
-
return `Resume previous session${progress}: "${session.lastSessionTask}".`;
|
|
553
|
-
}
|
|
554
502
|
if (subtasks.length > 0) {
|
|
555
503
|
const completed = subtasks.filter((s) => s.completed).length;
|
|
556
504
|
if (completed === subtasks.length) {
|
|
@@ -561,12 +509,6 @@ function synthesizeOneThing(card, subtasks, links, assembledContext) {
|
|
|
561
509
|
return `Work on next subtask: "${nextSubtask.title}" (${completed}/${subtasks.length} done).`;
|
|
562
510
|
}
|
|
563
511
|
}
|
|
564
|
-
if (session?.procedureNextStep) {
|
|
565
|
-
return `Follow procedure: ${session.procedureNextStep}`;
|
|
566
|
-
}
|
|
567
|
-
if (session?.lastSessionStatus === "completed" && session.lastSessionTask) {
|
|
568
|
-
return `Previous session completed ("${session.lastSessionTask}"). Review results and continue with remaining work.`;
|
|
569
|
-
}
|
|
570
512
|
if (card.due_date && (card.priority === "urgent" || card.priority === "high")) {
|
|
571
513
|
return `High-priority task with deadline ${card.due_date}. Start implementation immediately.`;
|
|
572
514
|
}
|
|
@@ -1438,6 +1380,9 @@ import {
|
|
|
1438
1380
|
ReadResourceRequestSchema
|
|
1439
1381
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
1440
1382
|
import { z } from "zod";
|
|
1383
|
+
|
|
1384
|
+
// src/api-client.ts
|
|
1385
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
1441
1386
|
// ../harmony-shared/dist/agentStaleness.js
|
|
1442
1387
|
var AGENT_HEARTBEAT_LIVENESS_MS = 5 * 60 * 1000;
|
|
1443
1388
|
var AGENT_MILESTONE_LIVENESS_MS = 30 * 60 * 1000;
|
|
@@ -1559,12 +1504,97 @@ var TIMINGS = {
|
|
|
1559
1504
|
QUERY_STALE_TIME: 1000 * 60 * 5,
|
|
1560
1505
|
QUERY_GC_TIME: 1000 * 60 * 60 * 24
|
|
1561
1506
|
};
|
|
1507
|
+
// ../harmony-shared/dist/declaredGateMetrics.js
|
|
1508
|
+
function declaredGateMetricsFromAgents(agents) {
|
|
1509
|
+
const names = new Set;
|
|
1510
|
+
let known = false;
|
|
1511
|
+
for (const agent of agents) {
|
|
1512
|
+
const declared = agent.declared_gate_metrics;
|
|
1513
|
+
if (!Array.isArray(declared))
|
|
1514
|
+
continue;
|
|
1515
|
+
known = true;
|
|
1516
|
+
for (const name of declared) {
|
|
1517
|
+
if (typeof name === "string" && name.trim())
|
|
1518
|
+
names.add(name.trim());
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
return { names, known };
|
|
1522
|
+
}
|
|
1523
|
+
// ../harmony-shared/dist/gateConfigError.js
|
|
1524
|
+
var GATE_CONFIG_ERROR_KEY = "configError";
|
|
1525
|
+
var GATE_CONFIG_ERROR_MARK = Object.freeze({ [GATE_CONFIG_ERROR_KEY]: true });
|
|
1562
1526
|
// ../harmony-shared/dist/playbookStage.js
|
|
1527
|
+
var DEFAULT_LOOP_MAX_ITERATIONS = 5;
|
|
1528
|
+
function normalizeLoopDef(raw) {
|
|
1529
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw))
|
|
1530
|
+
return null;
|
|
1531
|
+
const obj = raw;
|
|
1532
|
+
if (obj.mode !== "converge" && obj.mode !== "fanout")
|
|
1533
|
+
return null;
|
|
1534
|
+
const mode = obj.mode;
|
|
1535
|
+
const rawMax = obj.max_iterations;
|
|
1536
|
+
const maxInt = typeof rawMax === "number" && Number.isFinite(rawMax) && rawMax >= 1 ? Math.floor(rawMax) : DEFAULT_LOOP_MAX_ITERATIONS;
|
|
1537
|
+
const exitGate = obj.exit_gate && typeof obj.exit_gate === "object" && !Array.isArray(obj.exit_gate) ? obj.exit_gate : null;
|
|
1538
|
+
const def = { mode, max_iterations: maxInt };
|
|
1539
|
+
if (exitGate)
|
|
1540
|
+
def.exit_gate = exitGate;
|
|
1541
|
+
if (obj.item_source && typeof obj.item_source === "object" && !Array.isArray(obj.item_source)) {
|
|
1542
|
+
def.item_source = obj.item_source;
|
|
1543
|
+
}
|
|
1544
|
+
if (typeof obj.concurrency === "number" && obj.concurrency >= 1) {
|
|
1545
|
+
def.concurrency = Math.floor(obj.concurrency);
|
|
1546
|
+
}
|
|
1547
|
+
if (obj.on_item_fail === "continue" || obj.on_item_fail === "halt") {
|
|
1548
|
+
def.on_item_fail = obj.on_item_fail;
|
|
1549
|
+
}
|
|
1550
|
+
return def;
|
|
1551
|
+
}
|
|
1552
|
+
function readStageDefs(def) {
|
|
1553
|
+
if (def.steps_version !== 2)
|
|
1554
|
+
return [];
|
|
1555
|
+
return Array.isArray(def.steps) ? def.steps : [];
|
|
1556
|
+
}
|
|
1563
1557
|
var STAGE_DAEMON_OWNED_TOOLS = [
|
|
1564
1558
|
"mcp__harmony__harmony_end_agent_session",
|
|
1565
1559
|
"mcp__harmony__harmony_start_agent_session",
|
|
1566
1560
|
"mcp__harmony__harmony_move_card"
|
|
1567
1561
|
];
|
|
1562
|
+
function customGateMetric(gate) {
|
|
1563
|
+
if (gate === null || typeof gate !== "object" || Array.isArray(gate)) {
|
|
1564
|
+
return null;
|
|
1565
|
+
}
|
|
1566
|
+
const record = gate;
|
|
1567
|
+
if (record.kind !== "custom")
|
|
1568
|
+
return null;
|
|
1569
|
+
if (record.pendingEngine === true)
|
|
1570
|
+
return null;
|
|
1571
|
+
const metric = typeof record.metric === "string" ? record.metric.trim() : "";
|
|
1572
|
+
return metric ? metric : null;
|
|
1573
|
+
}
|
|
1574
|
+
function referencedGateMetrics(def) {
|
|
1575
|
+
const out = [];
|
|
1576
|
+
for (const stage of readStageDefs(def)) {
|
|
1577
|
+
if (!stage || typeof stage !== "object")
|
|
1578
|
+
continue;
|
|
1579
|
+
const stageId = typeof stage.id === "string" ? stage.id : "";
|
|
1580
|
+
const stageName = typeof stage.name === "string" ? stage.name : stageId;
|
|
1581
|
+
const gateMetric = customGateMetric(stage.gate);
|
|
1582
|
+
if (gateMetric) {
|
|
1583
|
+
out.push({ stageId, stageName, metric: gateMetric, source: "gate" });
|
|
1584
|
+
}
|
|
1585
|
+
const loop = normalizeLoopDef(stage.loop);
|
|
1586
|
+
const loopMetric = loop?.exit_gate ? customGateMetric(loop.exit_gate) : null;
|
|
1587
|
+
if (loopMetric) {
|
|
1588
|
+
out.push({
|
|
1589
|
+
stageId,
|
|
1590
|
+
stageName,
|
|
1591
|
+
metric: loopMetric,
|
|
1592
|
+
source: "loop_exit_gate"
|
|
1593
|
+
});
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
return out;
|
|
1597
|
+
}
|
|
1568
1598
|
// ../harmony-shared/dist/reviewTools.js
|
|
1569
1599
|
var REVIEW_DISALLOWED_TOOLS = [
|
|
1570
1600
|
...STAGE_DAEMON_OWNED_TOOLS,
|
|
@@ -1599,6 +1629,19 @@ function getRetryDelay(attempt) {
|
|
|
1599
1629
|
return Math.round(delay + delay * 0.25 * (Math.random() * 2 - 1));
|
|
1600
1630
|
}
|
|
1601
1631
|
var sleep2 = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
1632
|
+
function buildMemoryQuery(title, description) {
|
|
1633
|
+
const DESCRIPTION_CAP = 600;
|
|
1634
|
+
const trimmedTitle = title.trim();
|
|
1635
|
+
const trimmedBody = (description ?? "").trim();
|
|
1636
|
+
if (!trimmedTitle && !trimmedBody)
|
|
1637
|
+
return "";
|
|
1638
|
+
if (!trimmedTitle)
|
|
1639
|
+
return trimmedBody.slice(0, DESCRIPTION_CAP);
|
|
1640
|
+
if (!trimmedBody)
|
|
1641
|
+
return trimmedTitle;
|
|
1642
|
+
return `${trimmedTitle}
|
|
1643
|
+
${trimmedBody.slice(0, DESCRIPTION_CAP)}`;
|
|
1644
|
+
}
|
|
1602
1645
|
|
|
1603
1646
|
class Semaphore {
|
|
1604
1647
|
permits;
|
|
@@ -2069,6 +2112,14 @@ class HarmonyApiClient {
|
|
|
2069
2112
|
params.set("sinceSeq", String(sinceSeq));
|
|
2070
2113
|
return this.request("GET", `/cards/${cardId}/agent-messages?${params.toString()}`);
|
|
2071
2114
|
}
|
|
2115
|
+
async postBudgetDecision(cardId, data) {
|
|
2116
|
+
return this.request("POST", `/cards/${cardId}/budget-decisions`, data);
|
|
2117
|
+
}
|
|
2118
|
+
async getBudgetDecisions(cardId, sinceIso) {
|
|
2119
|
+
const params = new URLSearchParams;
|
|
2120
|
+
params.set("sinceIso", sinceIso);
|
|
2121
|
+
return this.request("GET", `/cards/${cardId}/budget-decisions?${params.toString()}`);
|
|
2122
|
+
}
|
|
2072
2123
|
async updateAgentProgress(cardId, data) {
|
|
2073
2124
|
return this.request("POST", `/cards/${cardId}/agent-context`, data);
|
|
2074
2125
|
}
|
|
@@ -2114,6 +2165,8 @@ class HarmonyApiClient {
|
|
|
2114
2165
|
params.set("offset", String(options.offset));
|
|
2115
2166
|
if (options.include_superseded)
|
|
2116
2167
|
params.set("include_superseded", "true");
|
|
2168
|
+
if (options.consumer)
|
|
2169
|
+
params.set("consumer", options.consumer);
|
|
2117
2170
|
if (options.include_episodes)
|
|
2118
2171
|
params.set("include_episodes", "true");
|
|
2119
2172
|
return this.request("GET", `/memory/entities?${params.toString()}`);
|
|
@@ -2169,6 +2222,12 @@ class HarmonyApiClient {
|
|
|
2169
2222
|
if (options.topK !== undefined) {
|
|
2170
2223
|
entities = entities.slice(0, options.topK);
|
|
2171
2224
|
}
|
|
2225
|
+
if (options.consumer) {
|
|
2226
|
+
const deliveredIds = entities.map((e) => e.id).filter((id) => typeof id === "string");
|
|
2227
|
+
if (deliveredIds.length > 0) {
|
|
2228
|
+
this.batchTouchMemoryEntities(deliveredIds, options.consumer).catch(() => {});
|
|
2229
|
+
}
|
|
2230
|
+
}
|
|
2172
2231
|
return { entities };
|
|
2173
2232
|
}
|
|
2174
2233
|
async deleteMemoryEntity(entityId) {
|
|
@@ -2177,9 +2236,10 @@ class HarmonyApiClient {
|
|
|
2177
2236
|
async touchMemoryEntity(entityId) {
|
|
2178
2237
|
return this.request("POST", `/memory/entities/${entityId}/touch`);
|
|
2179
2238
|
}
|
|
2180
|
-
async batchTouchMemoryEntities(entityIds) {
|
|
2239
|
+
async batchTouchMemoryEntities(entityIds, consumer) {
|
|
2181
2240
|
return this.request("POST", "/memory/entities/batch-touch", {
|
|
2182
|
-
entity_ids: entityIds
|
|
2241
|
+
entity_ids: entityIds,
|
|
2242
|
+
...consumer ? { consumer } : {}
|
|
2183
2243
|
});
|
|
2184
2244
|
}
|
|
2185
2245
|
async createMemoryRelation(data) {
|
|
@@ -2205,8 +2265,12 @@ class HarmonyApiClient {
|
|
|
2205
2265
|
params.append("tags", tag);
|
|
2206
2266
|
if (options?.include_superseded)
|
|
2207
2267
|
params.set("include_superseded", "true");
|
|
2268
|
+
if (options?.consumer)
|
|
2269
|
+
params.set("consumer", options.consumer);
|
|
2208
2270
|
if (options?.include_episodes)
|
|
2209
2271
|
params.set("include_episodes", "true");
|
|
2272
|
+
if (options?.assembly_id)
|
|
2273
|
+
params.set("assembly_id", options.assembly_id);
|
|
2210
2274
|
return this.request("GET", `/memory/search?${params.toString()}`);
|
|
2211
2275
|
}
|
|
2212
2276
|
async getVaultIndex(options) {
|
|
@@ -2218,6 +2282,8 @@ class HarmonyApiClient {
|
|
|
2218
2282
|
params.set("type", options.type);
|
|
2219
2283
|
if (options.limit !== undefined)
|
|
2220
2284
|
params.set("limit", String(options.limit));
|
|
2285
|
+
if (options.consumer)
|
|
2286
|
+
params.set("consumer", options.consumer);
|
|
2221
2287
|
if (options.include_episodes)
|
|
2222
2288
|
params.set("include_episodes", "true");
|
|
2223
2289
|
return this.request("GET", `/memory/index?${params.toString()}`);
|
|
@@ -2231,6 +2297,8 @@ class HarmonyApiClient {
|
|
|
2231
2297
|
params.set("type", options.type);
|
|
2232
2298
|
if (options.limit !== undefined)
|
|
2233
2299
|
params.set("limit", String(options.limit));
|
|
2300
|
+
if (options.consumer)
|
|
2301
|
+
params.set("consumer", options.consumer);
|
|
2234
2302
|
if (options.include_episodes)
|
|
2235
2303
|
params.set("include_episodes", "true");
|
|
2236
2304
|
return this.requestRaw("GET", `/memory/index?${params.toString()}`, undefined, {
|
|
@@ -2290,6 +2358,8 @@ class HarmonyApiClient {
|
|
|
2290
2358
|
params.set("type", options.type);
|
|
2291
2359
|
if (options?.limit !== undefined)
|
|
2292
2360
|
params.set("limit", String(options.limit));
|
|
2361
|
+
if (options?.consumer)
|
|
2362
|
+
params.set("consumer", options.consumer);
|
|
2293
2363
|
if (options?.include_episodes)
|
|
2294
2364
|
params.set("include_episodes", "true");
|
|
2295
2365
|
return this.requestRaw("GET", `/memory/search?${params.toString()}`, undefined, {
|
|
@@ -2395,14 +2465,15 @@ class HarmonyApiClient {
|
|
|
2395
2465
|
} catch {}
|
|
2396
2466
|
}
|
|
2397
2467
|
const variant = options.variant || "execute";
|
|
2398
|
-
const
|
|
2399
|
-
const assemblyId = undefined;
|
|
2468
|
+
const assemblyId = randomUUID2();
|
|
2400
2469
|
let memories;
|
|
2401
2470
|
try {
|
|
2402
2471
|
if (options.workspaceId && cardData.title) {
|
|
2403
|
-
const memoryResult = await this.searchMemoryEntities(options.workspaceId, cardData.title, {
|
|
2472
|
+
const memoryResult = await this.searchMemoryEntities(options.workspaceId, buildMemoryQuery(cardData.title, cardData.description), {
|
|
2404
2473
|
project_id: options.projectId,
|
|
2405
|
-
limit: 5
|
|
2474
|
+
limit: 5,
|
|
2475
|
+
consumer: "agent-prompt",
|
|
2476
|
+
assembly_id: assemblyId
|
|
2406
2477
|
});
|
|
2407
2478
|
if (memoryResult.entities?.length > 0) {
|
|
2408
2479
|
memories = memoryResult.entities.map((e) => ({
|
|
@@ -2426,7 +2497,6 @@ class HarmonyApiClient {
|
|
|
2426
2497
|
contextOptions: options.contextOptions,
|
|
2427
2498
|
customConstraints: options.customConstraints,
|
|
2428
2499
|
memories,
|
|
2429
|
-
assembledContext: assembledContextStr,
|
|
2430
2500
|
assemblyId
|
|
2431
2501
|
});
|
|
2432
2502
|
try {
|
|
@@ -3340,6 +3410,34 @@ async function onboardNewUser(params) {
|
|
|
3340
3410
|
};
|
|
3341
3411
|
}
|
|
3342
3412
|
|
|
3413
|
+
// src/playbook-metric-warnings.ts
|
|
3414
|
+
function playbookMetricWarnings(agents, steps) {
|
|
3415
|
+
if (!Array.isArray(steps))
|
|
3416
|
+
return [];
|
|
3417
|
+
const declared = declaredGateMetricsFromAgents(agents);
|
|
3418
|
+
if (!declared.known)
|
|
3419
|
+
return [];
|
|
3420
|
+
const warnings = [];
|
|
3421
|
+
const seen = new Set;
|
|
3422
|
+
for (const ref of referencedGateMetrics({ steps, steps_version: 2 })) {
|
|
3423
|
+
if (declared.names.has(ref.metric) || seen.has(ref.metric))
|
|
3424
|
+
continue;
|
|
3425
|
+
seen.add(ref.metric);
|
|
3426
|
+
warnings.push(`Gate metric "${ref.metric}" (stage "${ref.stageName}") is not declared by any agent in this workspace — a stage run gating on it will hold until a daemon declares it under agent.playbooks.metrics.${ref.metric}.`);
|
|
3427
|
+
}
|
|
3428
|
+
return warnings;
|
|
3429
|
+
}
|
|
3430
|
+
async function collectPlaybookMetricWarnings(client3, workspaceId, steps) {
|
|
3431
|
+
if (!workspaceId || !Array.isArray(steps))
|
|
3432
|
+
return [];
|
|
3433
|
+
try {
|
|
3434
|
+
const { agents } = await client3.listWorkspaceAgents(workspaceId);
|
|
3435
|
+
return playbookMetricWarnings(agents, steps);
|
|
3436
|
+
} catch {
|
|
3437
|
+
return [];
|
|
3438
|
+
}
|
|
3439
|
+
}
|
|
3440
|
+
|
|
3343
3441
|
// src/skills.ts
|
|
3344
3442
|
import {
|
|
3345
3443
|
existsSync as existsSync4,
|
|
@@ -4384,7 +4482,7 @@ var TOOLS = {
|
|
|
4384
4482
|
}
|
|
4385
4483
|
},
|
|
4386
4484
|
harmony_classify_card: {
|
|
4387
|
-
description: "
|
|
4485
|
+
description: "DEPRECATED — run sizing now happens at daemon pickup and is run-scoped, so nothing reads `model_tier`, `intent` or `complexity_score` any more; this tool still writes them, but only the type label has an effect. Prefer letting card creation apply the type label. Sets `intent` (plan/think/implement/review), `complexity_score` (0-10), `model_tier` (simple/advanced/research), stamps `classified_at`, and applies the type label (feature/bug/idea). Idempotent; never touches the user-owned `model_override`.",
|
|
4388
4486
|
inputSchema: {
|
|
4389
4487
|
type: "object",
|
|
4390
4488
|
properties: {
|
|
@@ -6897,7 +6995,7 @@ ${options}
|
|
|
6897
6995
|
if (trimmed.length > 0) {
|
|
6898
6996
|
const touchIds = trimmed.map(({ entity }) => entity?.id).filter((id) => typeof id === "string");
|
|
6899
6997
|
if (touchIds.length > 0) {
|
|
6900
|
-
client3.batchTouchMemoryEntities(touchIds).catch(() => {});
|
|
6998
|
+
client3.batchTouchMemoryEntities(touchIds, "mcp-tool").catch(() => {});
|
|
6901
6999
|
}
|
|
6902
7000
|
}
|
|
6903
7001
|
let sessionEntities = [];
|
|
@@ -7311,7 +7409,12 @@ ${options}
|
|
|
7311
7409
|
autoBind: args.autoBind,
|
|
7312
7410
|
catalogId: args.catalogId
|
|
7313
7411
|
});
|
|
7314
|
-
|
|
7412
|
+
const warnings = await collectPlaybookMetricWarnings(client3, workspaceId, args.steps);
|
|
7413
|
+
return {
|
|
7414
|
+
success: true,
|
|
7415
|
+
playbook: result.playbook,
|
|
7416
|
+
...warnings.length > 0 ? { warnings } : {}
|
|
7417
|
+
};
|
|
7315
7418
|
}
|
|
7316
7419
|
case "harmony_update_playbook": {
|
|
7317
7420
|
const playbookId = z.string().uuid().parse(args.playbookId);
|
|
@@ -7324,7 +7427,12 @@ ${options}
|
|
|
7324
7427
|
triggerType: args.triggerType,
|
|
7325
7428
|
..."autoBind" in args ? { autoBind: args.autoBind } : {}
|
|
7326
7429
|
});
|
|
7327
|
-
|
|
7430
|
+
const warnings = await collectPlaybookMetricWarnings(client3, result.playbook?.workspace_id, args.steps);
|
|
7431
|
+
return {
|
|
7432
|
+
success: true,
|
|
7433
|
+
playbook: result.playbook,
|
|
7434
|
+
...warnings.length > 0 ? { warnings } : {}
|
|
7435
|
+
};
|
|
7328
7436
|
}
|
|
7329
7437
|
case "harmony_save_card_as_playbook":
|
|
7330
7438
|
return deprecatedRemovedToolResult("harmony_save_card_as_playbook");
|