@gethmy/mcp 2.24.0 → 2.26.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 +191 -76
- package/dist/index.js +191 -76
- package/dist/lib/api-client.js +68 -72
- package/package.json +1 -1
- package/src/api-client.ts +178 -6
- 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,100 @@ 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/fanoutSource.js
|
|
1519
|
+
var FANOUT_KEY_MARKER = "harmony:fanout-item";
|
|
1520
|
+
var FANOUT_KEY_RE = new RegExp(`^\\[${FANOUT_KEY_MARKER}\\]:\\s*#(\\S+)\\s*$`, "m");
|
|
1521
|
+
// ../harmony-shared/dist/gateConfigError.js
|
|
1522
|
+
var GATE_CONFIG_ERROR_KEY = "configError";
|
|
1523
|
+
var GATE_CONFIG_ERROR_MARK = Object.freeze({ [GATE_CONFIG_ERROR_KEY]: true });
|
|
1557
1524
|
// ../harmony-shared/dist/playbookStage.js
|
|
1525
|
+
var DEFAULT_LOOP_MAX_ITERATIONS = 5;
|
|
1526
|
+
function normalizeLoopDef(raw) {
|
|
1527
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw))
|
|
1528
|
+
return null;
|
|
1529
|
+
const obj = raw;
|
|
1530
|
+
if (obj.mode !== "converge" && obj.mode !== "fanout")
|
|
1531
|
+
return null;
|
|
1532
|
+
const mode = obj.mode;
|
|
1533
|
+
const rawMax = obj.max_iterations;
|
|
1534
|
+
const maxInt = typeof rawMax === "number" && Number.isFinite(rawMax) && rawMax >= 1 ? Math.floor(rawMax) : DEFAULT_LOOP_MAX_ITERATIONS;
|
|
1535
|
+
const exitGate = obj.exit_gate && typeof obj.exit_gate === "object" && !Array.isArray(obj.exit_gate) ? obj.exit_gate : null;
|
|
1536
|
+
const def = { mode, max_iterations: maxInt };
|
|
1537
|
+
if (exitGate)
|
|
1538
|
+
def.exit_gate = exitGate;
|
|
1539
|
+
if (obj.item_source && typeof obj.item_source === "object" && !Array.isArray(obj.item_source)) {
|
|
1540
|
+
def.item_source = obj.item_source;
|
|
1541
|
+
}
|
|
1542
|
+
if (typeof obj.concurrency === "number" && obj.concurrency >= 1) {
|
|
1543
|
+
def.concurrency = Math.floor(obj.concurrency);
|
|
1544
|
+
}
|
|
1545
|
+
if (obj.on_item_fail === "continue" || obj.on_item_fail === "halt") {
|
|
1546
|
+
def.on_item_fail = obj.on_item_fail;
|
|
1547
|
+
}
|
|
1548
|
+
return def;
|
|
1549
|
+
}
|
|
1550
|
+
function readStageDefs(def) {
|
|
1551
|
+
if (def.steps_version !== 2)
|
|
1552
|
+
return [];
|
|
1553
|
+
return Array.isArray(def.steps) ? def.steps : [];
|
|
1554
|
+
}
|
|
1558
1555
|
var STAGE_DAEMON_OWNED_TOOLS = [
|
|
1559
1556
|
"mcp__harmony__harmony_end_agent_session",
|
|
1560
1557
|
"mcp__harmony__harmony_start_agent_session",
|
|
1561
1558
|
"mcp__harmony__harmony_move_card"
|
|
1562
1559
|
];
|
|
1560
|
+
function customGateMetric(gate) {
|
|
1561
|
+
if (gate === null || typeof gate !== "object" || Array.isArray(gate)) {
|
|
1562
|
+
return null;
|
|
1563
|
+
}
|
|
1564
|
+
const record = gate;
|
|
1565
|
+
if (record.kind !== "custom")
|
|
1566
|
+
return null;
|
|
1567
|
+
if (record.pendingEngine === true)
|
|
1568
|
+
return null;
|
|
1569
|
+
const metric = typeof record.metric === "string" ? record.metric.trim() : "";
|
|
1570
|
+
return metric ? metric : null;
|
|
1571
|
+
}
|
|
1572
|
+
function referencedGateMetrics(def) {
|
|
1573
|
+
const out = [];
|
|
1574
|
+
for (const stage of readStageDefs(def)) {
|
|
1575
|
+
if (!stage || typeof stage !== "object")
|
|
1576
|
+
continue;
|
|
1577
|
+
const stageId = typeof stage.id === "string" ? stage.id : "";
|
|
1578
|
+
const stageName = typeof stage.name === "string" ? stage.name : stageId;
|
|
1579
|
+
const gateMetric = customGateMetric(stage.gate);
|
|
1580
|
+
if (gateMetric) {
|
|
1581
|
+
out.push({ stageId, stageName, metric: gateMetric, source: "gate" });
|
|
1582
|
+
}
|
|
1583
|
+
const loop = normalizeLoopDef(stage.loop);
|
|
1584
|
+
const loopMetric = loop?.exit_gate ? customGateMetric(loop.exit_gate) : null;
|
|
1585
|
+
if (loopMetric) {
|
|
1586
|
+
out.push({
|
|
1587
|
+
stageId,
|
|
1588
|
+
stageName,
|
|
1589
|
+
metric: loopMetric,
|
|
1590
|
+
source: "loop_exit_gate"
|
|
1591
|
+
});
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
return out;
|
|
1595
|
+
}
|
|
1563
1596
|
// ../harmony-shared/dist/reviewTools.js
|
|
1564
1597
|
var REVIEW_DISALLOWED_TOOLS = [
|
|
1565
1598
|
...STAGE_DAEMON_OWNED_TOOLS,
|
|
@@ -1594,6 +1627,19 @@ function getRetryDelay(attempt) {
|
|
|
1594
1627
|
return Math.round(delay + delay * 0.25 * (Math.random() * 2 - 1));
|
|
1595
1628
|
}
|
|
1596
1629
|
var sleep2 = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
1630
|
+
function buildMemoryQuery(title, description) {
|
|
1631
|
+
const DESCRIPTION_CAP = 600;
|
|
1632
|
+
const trimmedTitle = title.trim();
|
|
1633
|
+
const trimmedBody = (description ?? "").trim();
|
|
1634
|
+
if (!trimmedTitle && !trimmedBody)
|
|
1635
|
+
return "";
|
|
1636
|
+
if (!trimmedTitle)
|
|
1637
|
+
return trimmedBody.slice(0, DESCRIPTION_CAP);
|
|
1638
|
+
if (!trimmedBody)
|
|
1639
|
+
return trimmedTitle;
|
|
1640
|
+
return `${trimmedTitle}
|
|
1641
|
+
${trimmedBody.slice(0, DESCRIPTION_CAP)}`;
|
|
1642
|
+
}
|
|
1597
1643
|
|
|
1598
1644
|
class Semaphore {
|
|
1599
1645
|
permits;
|
|
@@ -1899,8 +1945,12 @@ class HarmonyApiClient {
|
|
|
1899
1945
|
async updateCard(cardId, updates) {
|
|
1900
1946
|
return this.request("PATCH", `/cards/${cardId}`, updates);
|
|
1901
1947
|
}
|
|
1902
|
-
async claimCard(cardId, agentId) {
|
|
1903
|
-
const res = await this.request("PATCH", `/cards/${cardId}`, {
|
|
1948
|
+
async claimCard(cardId, agentId, opts) {
|
|
1949
|
+
const res = await this.request("PATCH", `/cards/${cardId}`, {
|
|
1950
|
+
assignedAgentId: agentId,
|
|
1951
|
+
ifAssignedAgentNull: true,
|
|
1952
|
+
...opts?.requireUnassigned ? { ifAssigneeNull: true } : {}
|
|
1953
|
+
});
|
|
1904
1954
|
return { claimed: res.claimed !== false };
|
|
1905
1955
|
}
|
|
1906
1956
|
async moveCard(cardId, columnId, position) {
|
|
@@ -2064,6 +2114,14 @@ class HarmonyApiClient {
|
|
|
2064
2114
|
params.set("sinceSeq", String(sinceSeq));
|
|
2065
2115
|
return this.request("GET", `/cards/${cardId}/agent-messages?${params.toString()}`);
|
|
2066
2116
|
}
|
|
2117
|
+
async postBudgetDecision(cardId, data) {
|
|
2118
|
+
return this.request("POST", `/cards/${cardId}/budget-decisions`, data);
|
|
2119
|
+
}
|
|
2120
|
+
async getBudgetDecisions(cardId, sinceIso) {
|
|
2121
|
+
const params = new URLSearchParams;
|
|
2122
|
+
params.set("sinceIso", sinceIso);
|
|
2123
|
+
return this.request("GET", `/cards/${cardId}/budget-decisions?${params.toString()}`);
|
|
2124
|
+
}
|
|
2067
2125
|
async updateAgentProgress(cardId, data) {
|
|
2068
2126
|
return this.request("POST", `/cards/${cardId}/agent-context`, data);
|
|
2069
2127
|
}
|
|
@@ -2109,6 +2167,8 @@ class HarmonyApiClient {
|
|
|
2109
2167
|
params.set("offset", String(options.offset));
|
|
2110
2168
|
if (options.include_superseded)
|
|
2111
2169
|
params.set("include_superseded", "true");
|
|
2170
|
+
if (options.consumer)
|
|
2171
|
+
params.set("consumer", options.consumer);
|
|
2112
2172
|
if (options.include_episodes)
|
|
2113
2173
|
params.set("include_episodes", "true");
|
|
2114
2174
|
return this.request("GET", `/memory/entities?${params.toString()}`);
|
|
@@ -2164,6 +2224,12 @@ class HarmonyApiClient {
|
|
|
2164
2224
|
if (options.topK !== undefined) {
|
|
2165
2225
|
entities = entities.slice(0, options.topK);
|
|
2166
2226
|
}
|
|
2227
|
+
if (options.consumer) {
|
|
2228
|
+
const deliveredIds = entities.map((e) => e.id).filter((id) => typeof id === "string");
|
|
2229
|
+
if (deliveredIds.length > 0) {
|
|
2230
|
+
this.batchTouchMemoryEntities(deliveredIds, options.consumer).catch(() => {});
|
|
2231
|
+
}
|
|
2232
|
+
}
|
|
2167
2233
|
return { entities };
|
|
2168
2234
|
}
|
|
2169
2235
|
async deleteMemoryEntity(entityId) {
|
|
@@ -2172,9 +2238,10 @@ class HarmonyApiClient {
|
|
|
2172
2238
|
async touchMemoryEntity(entityId) {
|
|
2173
2239
|
return this.request("POST", `/memory/entities/${entityId}/touch`);
|
|
2174
2240
|
}
|
|
2175
|
-
async batchTouchMemoryEntities(entityIds) {
|
|
2241
|
+
async batchTouchMemoryEntities(entityIds, consumer) {
|
|
2176
2242
|
return this.request("POST", "/memory/entities/batch-touch", {
|
|
2177
|
-
entity_ids: entityIds
|
|
2243
|
+
entity_ids: entityIds,
|
|
2244
|
+
...consumer ? { consumer } : {}
|
|
2178
2245
|
});
|
|
2179
2246
|
}
|
|
2180
2247
|
async createMemoryRelation(data) {
|
|
@@ -2200,8 +2267,12 @@ class HarmonyApiClient {
|
|
|
2200
2267
|
params.append("tags", tag);
|
|
2201
2268
|
if (options?.include_superseded)
|
|
2202
2269
|
params.set("include_superseded", "true");
|
|
2270
|
+
if (options?.consumer)
|
|
2271
|
+
params.set("consumer", options.consumer);
|
|
2203
2272
|
if (options?.include_episodes)
|
|
2204
2273
|
params.set("include_episodes", "true");
|
|
2274
|
+
if (options?.assembly_id)
|
|
2275
|
+
params.set("assembly_id", options.assembly_id);
|
|
2205
2276
|
return this.request("GET", `/memory/search?${params.toString()}`);
|
|
2206
2277
|
}
|
|
2207
2278
|
async getVaultIndex(options) {
|
|
@@ -2213,6 +2284,8 @@ class HarmonyApiClient {
|
|
|
2213
2284
|
params.set("type", options.type);
|
|
2214
2285
|
if (options.limit !== undefined)
|
|
2215
2286
|
params.set("limit", String(options.limit));
|
|
2287
|
+
if (options.consumer)
|
|
2288
|
+
params.set("consumer", options.consumer);
|
|
2216
2289
|
if (options.include_episodes)
|
|
2217
2290
|
params.set("include_episodes", "true");
|
|
2218
2291
|
return this.request("GET", `/memory/index?${params.toString()}`);
|
|
@@ -2226,6 +2299,8 @@ class HarmonyApiClient {
|
|
|
2226
2299
|
params.set("type", options.type);
|
|
2227
2300
|
if (options.limit !== undefined)
|
|
2228
2301
|
params.set("limit", String(options.limit));
|
|
2302
|
+
if (options.consumer)
|
|
2303
|
+
params.set("consumer", options.consumer);
|
|
2229
2304
|
if (options.include_episodes)
|
|
2230
2305
|
params.set("include_episodes", "true");
|
|
2231
2306
|
return this.requestRaw("GET", `/memory/index?${params.toString()}`, undefined, {
|
|
@@ -2285,6 +2360,8 @@ class HarmonyApiClient {
|
|
|
2285
2360
|
params.set("type", options.type);
|
|
2286
2361
|
if (options?.limit !== undefined)
|
|
2287
2362
|
params.set("limit", String(options.limit));
|
|
2363
|
+
if (options?.consumer)
|
|
2364
|
+
params.set("consumer", options.consumer);
|
|
2288
2365
|
if (options?.include_episodes)
|
|
2289
2366
|
params.set("include_episodes", "true");
|
|
2290
2367
|
return this.requestRaw("GET", `/memory/search?${params.toString()}`, undefined, {
|
|
@@ -2390,14 +2467,15 @@ class HarmonyApiClient {
|
|
|
2390
2467
|
} catch {}
|
|
2391
2468
|
}
|
|
2392
2469
|
const variant = options.variant || "execute";
|
|
2393
|
-
const
|
|
2394
|
-
const assemblyId = undefined;
|
|
2470
|
+
const assemblyId = randomUUID2();
|
|
2395
2471
|
let memories;
|
|
2396
2472
|
try {
|
|
2397
2473
|
if (options.workspaceId && cardData.title) {
|
|
2398
|
-
const memoryResult = await this.searchMemoryEntities(options.workspaceId, cardData.title, {
|
|
2474
|
+
const memoryResult = await this.searchMemoryEntities(options.workspaceId, buildMemoryQuery(cardData.title, cardData.description), {
|
|
2399
2475
|
project_id: options.projectId,
|
|
2400
|
-
limit: 5
|
|
2476
|
+
limit: 5,
|
|
2477
|
+
consumer: "agent-prompt",
|
|
2478
|
+
assembly_id: assemblyId
|
|
2401
2479
|
});
|
|
2402
2480
|
if (memoryResult.entities?.length > 0) {
|
|
2403
2481
|
memories = memoryResult.entities.map((e) => ({
|
|
@@ -2421,7 +2499,6 @@ class HarmonyApiClient {
|
|
|
2421
2499
|
contextOptions: options.contextOptions,
|
|
2422
2500
|
customConstraints: options.customConstraints,
|
|
2423
2501
|
memories,
|
|
2424
|
-
assembledContext: assembledContextStr,
|
|
2425
2502
|
assemblyId
|
|
2426
2503
|
});
|
|
2427
2504
|
try {
|
|
@@ -3335,6 +3412,34 @@ async function onboardNewUser(params) {
|
|
|
3335
3412
|
};
|
|
3336
3413
|
}
|
|
3337
3414
|
|
|
3415
|
+
// src/playbook-metric-warnings.ts
|
|
3416
|
+
function playbookMetricWarnings(agents, steps) {
|
|
3417
|
+
if (!Array.isArray(steps))
|
|
3418
|
+
return [];
|
|
3419
|
+
const declared = declaredGateMetricsFromAgents(agents);
|
|
3420
|
+
if (!declared.known)
|
|
3421
|
+
return [];
|
|
3422
|
+
const warnings = [];
|
|
3423
|
+
const seen = new Set;
|
|
3424
|
+
for (const ref of referencedGateMetrics({ steps, steps_version: 2 })) {
|
|
3425
|
+
if (declared.names.has(ref.metric) || seen.has(ref.metric))
|
|
3426
|
+
continue;
|
|
3427
|
+
seen.add(ref.metric);
|
|
3428
|
+
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}.`);
|
|
3429
|
+
}
|
|
3430
|
+
return warnings;
|
|
3431
|
+
}
|
|
3432
|
+
async function collectPlaybookMetricWarnings(client3, workspaceId, steps) {
|
|
3433
|
+
if (!workspaceId || !Array.isArray(steps))
|
|
3434
|
+
return [];
|
|
3435
|
+
try {
|
|
3436
|
+
const { agents } = await client3.listWorkspaceAgents(workspaceId);
|
|
3437
|
+
return playbookMetricWarnings(agents, steps);
|
|
3438
|
+
} catch {
|
|
3439
|
+
return [];
|
|
3440
|
+
}
|
|
3441
|
+
}
|
|
3442
|
+
|
|
3338
3443
|
// src/skills.ts
|
|
3339
3444
|
import {
|
|
3340
3445
|
existsSync as existsSync4,
|
|
@@ -4379,7 +4484,7 @@ var TOOLS = {
|
|
|
4379
4484
|
}
|
|
4380
4485
|
},
|
|
4381
4486
|
harmony_classify_card: {
|
|
4382
|
-
description: "
|
|
4487
|
+
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
4488
|
inputSchema: {
|
|
4384
4489
|
type: "object",
|
|
4385
4490
|
properties: {
|
|
@@ -6892,7 +6997,7 @@ ${options}
|
|
|
6892
6997
|
if (trimmed.length > 0) {
|
|
6893
6998
|
const touchIds = trimmed.map(({ entity }) => entity?.id).filter((id) => typeof id === "string");
|
|
6894
6999
|
if (touchIds.length > 0) {
|
|
6895
|
-
client3.batchTouchMemoryEntities(touchIds).catch(() => {});
|
|
7000
|
+
client3.batchTouchMemoryEntities(touchIds, "mcp-tool").catch(() => {});
|
|
6896
7001
|
}
|
|
6897
7002
|
}
|
|
6898
7003
|
let sessionEntities = [];
|
|
@@ -7306,7 +7411,12 @@ ${options}
|
|
|
7306
7411
|
autoBind: args.autoBind,
|
|
7307
7412
|
catalogId: args.catalogId
|
|
7308
7413
|
});
|
|
7309
|
-
|
|
7414
|
+
const warnings = await collectPlaybookMetricWarnings(client3, workspaceId, args.steps);
|
|
7415
|
+
return {
|
|
7416
|
+
success: true,
|
|
7417
|
+
playbook: result.playbook,
|
|
7418
|
+
...warnings.length > 0 ? { warnings } : {}
|
|
7419
|
+
};
|
|
7310
7420
|
}
|
|
7311
7421
|
case "harmony_update_playbook": {
|
|
7312
7422
|
const playbookId = z.string().uuid().parse(args.playbookId);
|
|
@@ -7319,7 +7429,12 @@ ${options}
|
|
|
7319
7429
|
triggerType: args.triggerType,
|
|
7320
7430
|
..."autoBind" in args ? { autoBind: args.autoBind } : {}
|
|
7321
7431
|
});
|
|
7322
|
-
|
|
7432
|
+
const warnings = await collectPlaybookMetricWarnings(client3, result.playbook?.workspace_id, args.steps);
|
|
7433
|
+
return {
|
|
7434
|
+
success: true,
|
|
7435
|
+
playbook: result.playbook,
|
|
7436
|
+
...warnings.length > 0 ? { warnings } : {}
|
|
7437
|
+
};
|
|
7323
7438
|
}
|
|
7324
7439
|
case "harmony_save_card_as_playbook":
|
|
7325
7440
|
return deprecatedRemovedToolResult("harmony_save_card_as_playbook");
|