@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/index.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
|
}
|
|
@@ -1433,6 +1375,9 @@ import {
|
|
|
1433
1375
|
ReadResourceRequestSchema
|
|
1434
1376
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
1435
1377
|
import { z } from "zod";
|
|
1378
|
+
|
|
1379
|
+
// src/api-client.ts
|
|
1380
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
1436
1381
|
// ../harmony-shared/dist/agentStaleness.js
|
|
1437
1382
|
var AGENT_HEARTBEAT_LIVENESS_MS = 5 * 60 * 1000;
|
|
1438
1383
|
var AGENT_MILESTONE_LIVENESS_MS = 30 * 60 * 1000;
|
|
@@ -1554,12 +1499,97 @@ var TIMINGS = {
|
|
|
1554
1499
|
QUERY_STALE_TIME: 1000 * 60 * 5,
|
|
1555
1500
|
QUERY_GC_TIME: 1000 * 60 * 60 * 24
|
|
1556
1501
|
};
|
|
1502
|
+
// ../harmony-shared/dist/declaredGateMetrics.js
|
|
1503
|
+
function declaredGateMetricsFromAgents(agents) {
|
|
1504
|
+
const names = new Set;
|
|
1505
|
+
let known = false;
|
|
1506
|
+
for (const agent of agents) {
|
|
1507
|
+
const declared = agent.declared_gate_metrics;
|
|
1508
|
+
if (!Array.isArray(declared))
|
|
1509
|
+
continue;
|
|
1510
|
+
known = true;
|
|
1511
|
+
for (const name of declared) {
|
|
1512
|
+
if (typeof name === "string" && name.trim())
|
|
1513
|
+
names.add(name.trim());
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
return { names, known };
|
|
1517
|
+
}
|
|
1518
|
+
// ../harmony-shared/dist/gateConfigError.js
|
|
1519
|
+
var GATE_CONFIG_ERROR_KEY = "configError";
|
|
1520
|
+
var GATE_CONFIG_ERROR_MARK = Object.freeze({ [GATE_CONFIG_ERROR_KEY]: true });
|
|
1557
1521
|
// ../harmony-shared/dist/playbookStage.js
|
|
1522
|
+
var DEFAULT_LOOP_MAX_ITERATIONS = 5;
|
|
1523
|
+
function normalizeLoopDef(raw) {
|
|
1524
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw))
|
|
1525
|
+
return null;
|
|
1526
|
+
const obj = raw;
|
|
1527
|
+
if (obj.mode !== "converge" && obj.mode !== "fanout")
|
|
1528
|
+
return null;
|
|
1529
|
+
const mode = obj.mode;
|
|
1530
|
+
const rawMax = obj.max_iterations;
|
|
1531
|
+
const maxInt = typeof rawMax === "number" && Number.isFinite(rawMax) && rawMax >= 1 ? Math.floor(rawMax) : DEFAULT_LOOP_MAX_ITERATIONS;
|
|
1532
|
+
const exitGate = obj.exit_gate && typeof obj.exit_gate === "object" && !Array.isArray(obj.exit_gate) ? obj.exit_gate : null;
|
|
1533
|
+
const def = { mode, max_iterations: maxInt };
|
|
1534
|
+
if (exitGate)
|
|
1535
|
+
def.exit_gate = exitGate;
|
|
1536
|
+
if (obj.item_source && typeof obj.item_source === "object" && !Array.isArray(obj.item_source)) {
|
|
1537
|
+
def.item_source = obj.item_source;
|
|
1538
|
+
}
|
|
1539
|
+
if (typeof obj.concurrency === "number" && obj.concurrency >= 1) {
|
|
1540
|
+
def.concurrency = Math.floor(obj.concurrency);
|
|
1541
|
+
}
|
|
1542
|
+
if (obj.on_item_fail === "continue" || obj.on_item_fail === "halt") {
|
|
1543
|
+
def.on_item_fail = obj.on_item_fail;
|
|
1544
|
+
}
|
|
1545
|
+
return def;
|
|
1546
|
+
}
|
|
1547
|
+
function readStageDefs(def) {
|
|
1548
|
+
if (def.steps_version !== 2)
|
|
1549
|
+
return [];
|
|
1550
|
+
return Array.isArray(def.steps) ? def.steps : [];
|
|
1551
|
+
}
|
|
1558
1552
|
var STAGE_DAEMON_OWNED_TOOLS = [
|
|
1559
1553
|
"mcp__harmony__harmony_end_agent_session",
|
|
1560
1554
|
"mcp__harmony__harmony_start_agent_session",
|
|
1561
1555
|
"mcp__harmony__harmony_move_card"
|
|
1562
1556
|
];
|
|
1557
|
+
function customGateMetric(gate) {
|
|
1558
|
+
if (gate === null || typeof gate !== "object" || Array.isArray(gate)) {
|
|
1559
|
+
return null;
|
|
1560
|
+
}
|
|
1561
|
+
const record = gate;
|
|
1562
|
+
if (record.kind !== "custom")
|
|
1563
|
+
return null;
|
|
1564
|
+
if (record.pendingEngine === true)
|
|
1565
|
+
return null;
|
|
1566
|
+
const metric = typeof record.metric === "string" ? record.metric.trim() : "";
|
|
1567
|
+
return metric ? metric : null;
|
|
1568
|
+
}
|
|
1569
|
+
function referencedGateMetrics(def) {
|
|
1570
|
+
const out = [];
|
|
1571
|
+
for (const stage of readStageDefs(def)) {
|
|
1572
|
+
if (!stage || typeof stage !== "object")
|
|
1573
|
+
continue;
|
|
1574
|
+
const stageId = typeof stage.id === "string" ? stage.id : "";
|
|
1575
|
+
const stageName = typeof stage.name === "string" ? stage.name : stageId;
|
|
1576
|
+
const gateMetric = customGateMetric(stage.gate);
|
|
1577
|
+
if (gateMetric) {
|
|
1578
|
+
out.push({ stageId, stageName, metric: gateMetric, source: "gate" });
|
|
1579
|
+
}
|
|
1580
|
+
const loop = normalizeLoopDef(stage.loop);
|
|
1581
|
+
const loopMetric = loop?.exit_gate ? customGateMetric(loop.exit_gate) : null;
|
|
1582
|
+
if (loopMetric) {
|
|
1583
|
+
out.push({
|
|
1584
|
+
stageId,
|
|
1585
|
+
stageName,
|
|
1586
|
+
metric: loopMetric,
|
|
1587
|
+
source: "loop_exit_gate"
|
|
1588
|
+
});
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
return out;
|
|
1592
|
+
}
|
|
1563
1593
|
// ../harmony-shared/dist/reviewTools.js
|
|
1564
1594
|
var REVIEW_DISALLOWED_TOOLS = [
|
|
1565
1595
|
...STAGE_DAEMON_OWNED_TOOLS,
|
|
@@ -1594,6 +1624,19 @@ function getRetryDelay(attempt) {
|
|
|
1594
1624
|
return Math.round(delay + delay * 0.25 * (Math.random() * 2 - 1));
|
|
1595
1625
|
}
|
|
1596
1626
|
var sleep2 = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
1627
|
+
function buildMemoryQuery(title, description) {
|
|
1628
|
+
const DESCRIPTION_CAP = 600;
|
|
1629
|
+
const trimmedTitle = title.trim();
|
|
1630
|
+
const trimmedBody = (description ?? "").trim();
|
|
1631
|
+
if (!trimmedTitle && !trimmedBody)
|
|
1632
|
+
return "";
|
|
1633
|
+
if (!trimmedTitle)
|
|
1634
|
+
return trimmedBody.slice(0, DESCRIPTION_CAP);
|
|
1635
|
+
if (!trimmedBody)
|
|
1636
|
+
return trimmedTitle;
|
|
1637
|
+
return `${trimmedTitle}
|
|
1638
|
+
${trimmedBody.slice(0, DESCRIPTION_CAP)}`;
|
|
1639
|
+
}
|
|
1597
1640
|
|
|
1598
1641
|
class Semaphore {
|
|
1599
1642
|
permits;
|
|
@@ -2064,6 +2107,14 @@ class HarmonyApiClient {
|
|
|
2064
2107
|
params.set("sinceSeq", String(sinceSeq));
|
|
2065
2108
|
return this.request("GET", `/cards/${cardId}/agent-messages?${params.toString()}`);
|
|
2066
2109
|
}
|
|
2110
|
+
async postBudgetDecision(cardId, data) {
|
|
2111
|
+
return this.request("POST", `/cards/${cardId}/budget-decisions`, data);
|
|
2112
|
+
}
|
|
2113
|
+
async getBudgetDecisions(cardId, sinceIso) {
|
|
2114
|
+
const params = new URLSearchParams;
|
|
2115
|
+
params.set("sinceIso", sinceIso);
|
|
2116
|
+
return this.request("GET", `/cards/${cardId}/budget-decisions?${params.toString()}`);
|
|
2117
|
+
}
|
|
2067
2118
|
async updateAgentProgress(cardId, data) {
|
|
2068
2119
|
return this.request("POST", `/cards/${cardId}/agent-context`, data);
|
|
2069
2120
|
}
|
|
@@ -2109,6 +2160,8 @@ class HarmonyApiClient {
|
|
|
2109
2160
|
params.set("offset", String(options.offset));
|
|
2110
2161
|
if (options.include_superseded)
|
|
2111
2162
|
params.set("include_superseded", "true");
|
|
2163
|
+
if (options.consumer)
|
|
2164
|
+
params.set("consumer", options.consumer);
|
|
2112
2165
|
if (options.include_episodes)
|
|
2113
2166
|
params.set("include_episodes", "true");
|
|
2114
2167
|
return this.request("GET", `/memory/entities?${params.toString()}`);
|
|
@@ -2164,6 +2217,12 @@ class HarmonyApiClient {
|
|
|
2164
2217
|
if (options.topK !== undefined) {
|
|
2165
2218
|
entities = entities.slice(0, options.topK);
|
|
2166
2219
|
}
|
|
2220
|
+
if (options.consumer) {
|
|
2221
|
+
const deliveredIds = entities.map((e) => e.id).filter((id) => typeof id === "string");
|
|
2222
|
+
if (deliveredIds.length > 0) {
|
|
2223
|
+
this.batchTouchMemoryEntities(deliveredIds, options.consumer).catch(() => {});
|
|
2224
|
+
}
|
|
2225
|
+
}
|
|
2167
2226
|
return { entities };
|
|
2168
2227
|
}
|
|
2169
2228
|
async deleteMemoryEntity(entityId) {
|
|
@@ -2172,9 +2231,10 @@ class HarmonyApiClient {
|
|
|
2172
2231
|
async touchMemoryEntity(entityId) {
|
|
2173
2232
|
return this.request("POST", `/memory/entities/${entityId}/touch`);
|
|
2174
2233
|
}
|
|
2175
|
-
async batchTouchMemoryEntities(entityIds) {
|
|
2234
|
+
async batchTouchMemoryEntities(entityIds, consumer) {
|
|
2176
2235
|
return this.request("POST", "/memory/entities/batch-touch", {
|
|
2177
|
-
entity_ids: entityIds
|
|
2236
|
+
entity_ids: entityIds,
|
|
2237
|
+
...consumer ? { consumer } : {}
|
|
2178
2238
|
});
|
|
2179
2239
|
}
|
|
2180
2240
|
async createMemoryRelation(data) {
|
|
@@ -2200,8 +2260,12 @@ class HarmonyApiClient {
|
|
|
2200
2260
|
params.append("tags", tag);
|
|
2201
2261
|
if (options?.include_superseded)
|
|
2202
2262
|
params.set("include_superseded", "true");
|
|
2263
|
+
if (options?.consumer)
|
|
2264
|
+
params.set("consumer", options.consumer);
|
|
2203
2265
|
if (options?.include_episodes)
|
|
2204
2266
|
params.set("include_episodes", "true");
|
|
2267
|
+
if (options?.assembly_id)
|
|
2268
|
+
params.set("assembly_id", options.assembly_id);
|
|
2205
2269
|
return this.request("GET", `/memory/search?${params.toString()}`);
|
|
2206
2270
|
}
|
|
2207
2271
|
async getVaultIndex(options) {
|
|
@@ -2213,6 +2277,8 @@ class HarmonyApiClient {
|
|
|
2213
2277
|
params.set("type", options.type);
|
|
2214
2278
|
if (options.limit !== undefined)
|
|
2215
2279
|
params.set("limit", String(options.limit));
|
|
2280
|
+
if (options.consumer)
|
|
2281
|
+
params.set("consumer", options.consumer);
|
|
2216
2282
|
if (options.include_episodes)
|
|
2217
2283
|
params.set("include_episodes", "true");
|
|
2218
2284
|
return this.request("GET", `/memory/index?${params.toString()}`);
|
|
@@ -2226,6 +2292,8 @@ class HarmonyApiClient {
|
|
|
2226
2292
|
params.set("type", options.type);
|
|
2227
2293
|
if (options.limit !== undefined)
|
|
2228
2294
|
params.set("limit", String(options.limit));
|
|
2295
|
+
if (options.consumer)
|
|
2296
|
+
params.set("consumer", options.consumer);
|
|
2229
2297
|
if (options.include_episodes)
|
|
2230
2298
|
params.set("include_episodes", "true");
|
|
2231
2299
|
return this.requestRaw("GET", `/memory/index?${params.toString()}`, undefined, {
|
|
@@ -2285,6 +2353,8 @@ class HarmonyApiClient {
|
|
|
2285
2353
|
params.set("type", options.type);
|
|
2286
2354
|
if (options?.limit !== undefined)
|
|
2287
2355
|
params.set("limit", String(options.limit));
|
|
2356
|
+
if (options?.consumer)
|
|
2357
|
+
params.set("consumer", options.consumer);
|
|
2288
2358
|
if (options?.include_episodes)
|
|
2289
2359
|
params.set("include_episodes", "true");
|
|
2290
2360
|
return this.requestRaw("GET", `/memory/search?${params.toString()}`, undefined, {
|
|
@@ -2390,14 +2460,15 @@ class HarmonyApiClient {
|
|
|
2390
2460
|
} catch {}
|
|
2391
2461
|
}
|
|
2392
2462
|
const variant = options.variant || "execute";
|
|
2393
|
-
const
|
|
2394
|
-
const assemblyId = undefined;
|
|
2463
|
+
const assemblyId = randomUUID2();
|
|
2395
2464
|
let memories;
|
|
2396
2465
|
try {
|
|
2397
2466
|
if (options.workspaceId && cardData.title) {
|
|
2398
|
-
const memoryResult = await this.searchMemoryEntities(options.workspaceId, cardData.title, {
|
|
2467
|
+
const memoryResult = await this.searchMemoryEntities(options.workspaceId, buildMemoryQuery(cardData.title, cardData.description), {
|
|
2399
2468
|
project_id: options.projectId,
|
|
2400
|
-
limit: 5
|
|
2469
|
+
limit: 5,
|
|
2470
|
+
consumer: "agent-prompt",
|
|
2471
|
+
assembly_id: assemblyId
|
|
2401
2472
|
});
|
|
2402
2473
|
if (memoryResult.entities?.length > 0) {
|
|
2403
2474
|
memories = memoryResult.entities.map((e) => ({
|
|
@@ -2421,7 +2492,6 @@ class HarmonyApiClient {
|
|
|
2421
2492
|
contextOptions: options.contextOptions,
|
|
2422
2493
|
customConstraints: options.customConstraints,
|
|
2423
2494
|
memories,
|
|
2424
|
-
assembledContext: assembledContextStr,
|
|
2425
2495
|
assemblyId
|
|
2426
2496
|
});
|
|
2427
2497
|
try {
|
|
@@ -3335,6 +3405,34 @@ async function onboardNewUser(params) {
|
|
|
3335
3405
|
};
|
|
3336
3406
|
}
|
|
3337
3407
|
|
|
3408
|
+
// src/playbook-metric-warnings.ts
|
|
3409
|
+
function playbookMetricWarnings(agents, steps) {
|
|
3410
|
+
if (!Array.isArray(steps))
|
|
3411
|
+
return [];
|
|
3412
|
+
const declared = declaredGateMetricsFromAgents(agents);
|
|
3413
|
+
if (!declared.known)
|
|
3414
|
+
return [];
|
|
3415
|
+
const warnings = [];
|
|
3416
|
+
const seen = new Set;
|
|
3417
|
+
for (const ref of referencedGateMetrics({ steps, steps_version: 2 })) {
|
|
3418
|
+
if (declared.names.has(ref.metric) || seen.has(ref.metric))
|
|
3419
|
+
continue;
|
|
3420
|
+
seen.add(ref.metric);
|
|
3421
|
+
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}.`);
|
|
3422
|
+
}
|
|
3423
|
+
return warnings;
|
|
3424
|
+
}
|
|
3425
|
+
async function collectPlaybookMetricWarnings(client3, workspaceId, steps) {
|
|
3426
|
+
if (!workspaceId || !Array.isArray(steps))
|
|
3427
|
+
return [];
|
|
3428
|
+
try {
|
|
3429
|
+
const { agents } = await client3.listWorkspaceAgents(workspaceId);
|
|
3430
|
+
return playbookMetricWarnings(agents, steps);
|
|
3431
|
+
} catch {
|
|
3432
|
+
return [];
|
|
3433
|
+
}
|
|
3434
|
+
}
|
|
3435
|
+
|
|
3338
3436
|
// src/skills.ts
|
|
3339
3437
|
import {
|
|
3340
3438
|
existsSync as existsSync4,
|
|
@@ -4379,7 +4477,7 @@ var TOOLS = {
|
|
|
4379
4477
|
}
|
|
4380
4478
|
},
|
|
4381
4479
|
harmony_classify_card: {
|
|
4382
|
-
description: "
|
|
4480
|
+
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`.",
|
|
4383
4481
|
inputSchema: {
|
|
4384
4482
|
type: "object",
|
|
4385
4483
|
properties: {
|
|
@@ -6892,7 +6990,7 @@ ${options}
|
|
|
6892
6990
|
if (trimmed.length > 0) {
|
|
6893
6991
|
const touchIds = trimmed.map(({ entity }) => entity?.id).filter((id) => typeof id === "string");
|
|
6894
6992
|
if (touchIds.length > 0) {
|
|
6895
|
-
client3.batchTouchMemoryEntities(touchIds).catch(() => {});
|
|
6993
|
+
client3.batchTouchMemoryEntities(touchIds, "mcp-tool").catch(() => {});
|
|
6896
6994
|
}
|
|
6897
6995
|
}
|
|
6898
6996
|
let sessionEntities = [];
|
|
@@ -7306,7 +7404,12 @@ ${options}
|
|
|
7306
7404
|
autoBind: args.autoBind,
|
|
7307
7405
|
catalogId: args.catalogId
|
|
7308
7406
|
});
|
|
7309
|
-
|
|
7407
|
+
const warnings = await collectPlaybookMetricWarnings(client3, workspaceId, args.steps);
|
|
7408
|
+
return {
|
|
7409
|
+
success: true,
|
|
7410
|
+
playbook: result.playbook,
|
|
7411
|
+
...warnings.length > 0 ? { warnings } : {}
|
|
7412
|
+
};
|
|
7310
7413
|
}
|
|
7311
7414
|
case "harmony_update_playbook": {
|
|
7312
7415
|
const playbookId = z.string().uuid().parse(args.playbookId);
|
|
@@ -7319,7 +7422,12 @@ ${options}
|
|
|
7319
7422
|
triggerType: args.triggerType,
|
|
7320
7423
|
..."autoBind" in args ? { autoBind: args.autoBind } : {}
|
|
7321
7424
|
});
|
|
7322
|
-
|
|
7425
|
+
const warnings = await collectPlaybookMetricWarnings(client3, result.playbook?.workspace_id, args.steps);
|
|
7426
|
+
return {
|
|
7427
|
+
success: true,
|
|
7428
|
+
playbook: result.playbook,
|
|
7429
|
+
...warnings.length > 0 ? { warnings } : {}
|
|
7430
|
+
};
|
|
7323
7431
|
}
|
|
7324
7432
|
case "harmony_save_card_as_playbook":
|
|
7325
7433
|
return deprecatedRemovedToolResult("harmony_save_card_as_playbook");
|