@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/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,100 @@ 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/fanoutSource.js
|
|
1524
|
+
var FANOUT_KEY_MARKER = "harmony:fanout-item";
|
|
1525
|
+
var FANOUT_KEY_RE = new RegExp(`^\\[${FANOUT_KEY_MARKER}\\]:\\s*#(\\S+)\\s*$`, "m");
|
|
1526
|
+
// ../harmony-shared/dist/gateConfigError.js
|
|
1527
|
+
var GATE_CONFIG_ERROR_KEY = "configError";
|
|
1528
|
+
var GATE_CONFIG_ERROR_MARK = Object.freeze({ [GATE_CONFIG_ERROR_KEY]: true });
|
|
1562
1529
|
// ../harmony-shared/dist/playbookStage.js
|
|
1530
|
+
var DEFAULT_LOOP_MAX_ITERATIONS = 5;
|
|
1531
|
+
function normalizeLoopDef(raw) {
|
|
1532
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw))
|
|
1533
|
+
return null;
|
|
1534
|
+
const obj = raw;
|
|
1535
|
+
if (obj.mode !== "converge" && obj.mode !== "fanout")
|
|
1536
|
+
return null;
|
|
1537
|
+
const mode = obj.mode;
|
|
1538
|
+
const rawMax = obj.max_iterations;
|
|
1539
|
+
const maxInt = typeof rawMax === "number" && Number.isFinite(rawMax) && rawMax >= 1 ? Math.floor(rawMax) : DEFAULT_LOOP_MAX_ITERATIONS;
|
|
1540
|
+
const exitGate = obj.exit_gate && typeof obj.exit_gate === "object" && !Array.isArray(obj.exit_gate) ? obj.exit_gate : null;
|
|
1541
|
+
const def = { mode, max_iterations: maxInt };
|
|
1542
|
+
if (exitGate)
|
|
1543
|
+
def.exit_gate = exitGate;
|
|
1544
|
+
if (obj.item_source && typeof obj.item_source === "object" && !Array.isArray(obj.item_source)) {
|
|
1545
|
+
def.item_source = obj.item_source;
|
|
1546
|
+
}
|
|
1547
|
+
if (typeof obj.concurrency === "number" && obj.concurrency >= 1) {
|
|
1548
|
+
def.concurrency = Math.floor(obj.concurrency);
|
|
1549
|
+
}
|
|
1550
|
+
if (obj.on_item_fail === "continue" || obj.on_item_fail === "halt") {
|
|
1551
|
+
def.on_item_fail = obj.on_item_fail;
|
|
1552
|
+
}
|
|
1553
|
+
return def;
|
|
1554
|
+
}
|
|
1555
|
+
function readStageDefs(def) {
|
|
1556
|
+
if (def.steps_version !== 2)
|
|
1557
|
+
return [];
|
|
1558
|
+
return Array.isArray(def.steps) ? def.steps : [];
|
|
1559
|
+
}
|
|
1563
1560
|
var STAGE_DAEMON_OWNED_TOOLS = [
|
|
1564
1561
|
"mcp__harmony__harmony_end_agent_session",
|
|
1565
1562
|
"mcp__harmony__harmony_start_agent_session",
|
|
1566
1563
|
"mcp__harmony__harmony_move_card"
|
|
1567
1564
|
];
|
|
1565
|
+
function customGateMetric(gate) {
|
|
1566
|
+
if (gate === null || typeof gate !== "object" || Array.isArray(gate)) {
|
|
1567
|
+
return null;
|
|
1568
|
+
}
|
|
1569
|
+
const record = gate;
|
|
1570
|
+
if (record.kind !== "custom")
|
|
1571
|
+
return null;
|
|
1572
|
+
if (record.pendingEngine === true)
|
|
1573
|
+
return null;
|
|
1574
|
+
const metric = typeof record.metric === "string" ? record.metric.trim() : "";
|
|
1575
|
+
return metric ? metric : null;
|
|
1576
|
+
}
|
|
1577
|
+
function referencedGateMetrics(def) {
|
|
1578
|
+
const out = [];
|
|
1579
|
+
for (const stage of readStageDefs(def)) {
|
|
1580
|
+
if (!stage || typeof stage !== "object")
|
|
1581
|
+
continue;
|
|
1582
|
+
const stageId = typeof stage.id === "string" ? stage.id : "";
|
|
1583
|
+
const stageName = typeof stage.name === "string" ? stage.name : stageId;
|
|
1584
|
+
const gateMetric = customGateMetric(stage.gate);
|
|
1585
|
+
if (gateMetric) {
|
|
1586
|
+
out.push({ stageId, stageName, metric: gateMetric, source: "gate" });
|
|
1587
|
+
}
|
|
1588
|
+
const loop = normalizeLoopDef(stage.loop);
|
|
1589
|
+
const loopMetric = loop?.exit_gate ? customGateMetric(loop.exit_gate) : null;
|
|
1590
|
+
if (loopMetric) {
|
|
1591
|
+
out.push({
|
|
1592
|
+
stageId,
|
|
1593
|
+
stageName,
|
|
1594
|
+
metric: loopMetric,
|
|
1595
|
+
source: "loop_exit_gate"
|
|
1596
|
+
});
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
return out;
|
|
1600
|
+
}
|
|
1568
1601
|
// ../harmony-shared/dist/reviewTools.js
|
|
1569
1602
|
var REVIEW_DISALLOWED_TOOLS = [
|
|
1570
1603
|
...STAGE_DAEMON_OWNED_TOOLS,
|
|
@@ -1599,6 +1632,19 @@ function getRetryDelay(attempt) {
|
|
|
1599
1632
|
return Math.round(delay + delay * 0.25 * (Math.random() * 2 - 1));
|
|
1600
1633
|
}
|
|
1601
1634
|
var sleep2 = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
1635
|
+
function buildMemoryQuery(title, description) {
|
|
1636
|
+
const DESCRIPTION_CAP = 600;
|
|
1637
|
+
const trimmedTitle = title.trim();
|
|
1638
|
+
const trimmedBody = (description ?? "").trim();
|
|
1639
|
+
if (!trimmedTitle && !trimmedBody)
|
|
1640
|
+
return "";
|
|
1641
|
+
if (!trimmedTitle)
|
|
1642
|
+
return trimmedBody.slice(0, DESCRIPTION_CAP);
|
|
1643
|
+
if (!trimmedBody)
|
|
1644
|
+
return trimmedTitle;
|
|
1645
|
+
return `${trimmedTitle}
|
|
1646
|
+
${trimmedBody.slice(0, DESCRIPTION_CAP)}`;
|
|
1647
|
+
}
|
|
1602
1648
|
|
|
1603
1649
|
class Semaphore {
|
|
1604
1650
|
permits;
|
|
@@ -1904,8 +1950,12 @@ class HarmonyApiClient {
|
|
|
1904
1950
|
async updateCard(cardId, updates) {
|
|
1905
1951
|
return this.request("PATCH", `/cards/${cardId}`, updates);
|
|
1906
1952
|
}
|
|
1907
|
-
async claimCard(cardId, agentId) {
|
|
1908
|
-
const res = await this.request("PATCH", `/cards/${cardId}`, {
|
|
1953
|
+
async claimCard(cardId, agentId, opts) {
|
|
1954
|
+
const res = await this.request("PATCH", `/cards/${cardId}`, {
|
|
1955
|
+
assignedAgentId: agentId,
|
|
1956
|
+
ifAssignedAgentNull: true,
|
|
1957
|
+
...opts?.requireUnassigned ? { ifAssigneeNull: true } : {}
|
|
1958
|
+
});
|
|
1909
1959
|
return { claimed: res.claimed !== false };
|
|
1910
1960
|
}
|
|
1911
1961
|
async moveCard(cardId, columnId, position) {
|
|
@@ -2069,6 +2119,14 @@ class HarmonyApiClient {
|
|
|
2069
2119
|
params.set("sinceSeq", String(sinceSeq));
|
|
2070
2120
|
return this.request("GET", `/cards/${cardId}/agent-messages?${params.toString()}`);
|
|
2071
2121
|
}
|
|
2122
|
+
async postBudgetDecision(cardId, data) {
|
|
2123
|
+
return this.request("POST", `/cards/${cardId}/budget-decisions`, data);
|
|
2124
|
+
}
|
|
2125
|
+
async getBudgetDecisions(cardId, sinceIso) {
|
|
2126
|
+
const params = new URLSearchParams;
|
|
2127
|
+
params.set("sinceIso", sinceIso);
|
|
2128
|
+
return this.request("GET", `/cards/${cardId}/budget-decisions?${params.toString()}`);
|
|
2129
|
+
}
|
|
2072
2130
|
async updateAgentProgress(cardId, data) {
|
|
2073
2131
|
return this.request("POST", `/cards/${cardId}/agent-context`, data);
|
|
2074
2132
|
}
|
|
@@ -2114,6 +2172,8 @@ class HarmonyApiClient {
|
|
|
2114
2172
|
params.set("offset", String(options.offset));
|
|
2115
2173
|
if (options.include_superseded)
|
|
2116
2174
|
params.set("include_superseded", "true");
|
|
2175
|
+
if (options.consumer)
|
|
2176
|
+
params.set("consumer", options.consumer);
|
|
2117
2177
|
if (options.include_episodes)
|
|
2118
2178
|
params.set("include_episodes", "true");
|
|
2119
2179
|
return this.request("GET", `/memory/entities?${params.toString()}`);
|
|
@@ -2169,6 +2229,12 @@ class HarmonyApiClient {
|
|
|
2169
2229
|
if (options.topK !== undefined) {
|
|
2170
2230
|
entities = entities.slice(0, options.topK);
|
|
2171
2231
|
}
|
|
2232
|
+
if (options.consumer) {
|
|
2233
|
+
const deliveredIds = entities.map((e) => e.id).filter((id) => typeof id === "string");
|
|
2234
|
+
if (deliveredIds.length > 0) {
|
|
2235
|
+
this.batchTouchMemoryEntities(deliveredIds, options.consumer).catch(() => {});
|
|
2236
|
+
}
|
|
2237
|
+
}
|
|
2172
2238
|
return { entities };
|
|
2173
2239
|
}
|
|
2174
2240
|
async deleteMemoryEntity(entityId) {
|
|
@@ -2177,9 +2243,10 @@ class HarmonyApiClient {
|
|
|
2177
2243
|
async touchMemoryEntity(entityId) {
|
|
2178
2244
|
return this.request("POST", `/memory/entities/${entityId}/touch`);
|
|
2179
2245
|
}
|
|
2180
|
-
async batchTouchMemoryEntities(entityIds) {
|
|
2246
|
+
async batchTouchMemoryEntities(entityIds, consumer) {
|
|
2181
2247
|
return this.request("POST", "/memory/entities/batch-touch", {
|
|
2182
|
-
entity_ids: entityIds
|
|
2248
|
+
entity_ids: entityIds,
|
|
2249
|
+
...consumer ? { consumer } : {}
|
|
2183
2250
|
});
|
|
2184
2251
|
}
|
|
2185
2252
|
async createMemoryRelation(data) {
|
|
@@ -2205,8 +2272,12 @@ class HarmonyApiClient {
|
|
|
2205
2272
|
params.append("tags", tag);
|
|
2206
2273
|
if (options?.include_superseded)
|
|
2207
2274
|
params.set("include_superseded", "true");
|
|
2275
|
+
if (options?.consumer)
|
|
2276
|
+
params.set("consumer", options.consumer);
|
|
2208
2277
|
if (options?.include_episodes)
|
|
2209
2278
|
params.set("include_episodes", "true");
|
|
2279
|
+
if (options?.assembly_id)
|
|
2280
|
+
params.set("assembly_id", options.assembly_id);
|
|
2210
2281
|
return this.request("GET", `/memory/search?${params.toString()}`);
|
|
2211
2282
|
}
|
|
2212
2283
|
async getVaultIndex(options) {
|
|
@@ -2218,6 +2289,8 @@ class HarmonyApiClient {
|
|
|
2218
2289
|
params.set("type", options.type);
|
|
2219
2290
|
if (options.limit !== undefined)
|
|
2220
2291
|
params.set("limit", String(options.limit));
|
|
2292
|
+
if (options.consumer)
|
|
2293
|
+
params.set("consumer", options.consumer);
|
|
2221
2294
|
if (options.include_episodes)
|
|
2222
2295
|
params.set("include_episodes", "true");
|
|
2223
2296
|
return this.request("GET", `/memory/index?${params.toString()}`);
|
|
@@ -2231,6 +2304,8 @@ class HarmonyApiClient {
|
|
|
2231
2304
|
params.set("type", options.type);
|
|
2232
2305
|
if (options.limit !== undefined)
|
|
2233
2306
|
params.set("limit", String(options.limit));
|
|
2307
|
+
if (options.consumer)
|
|
2308
|
+
params.set("consumer", options.consumer);
|
|
2234
2309
|
if (options.include_episodes)
|
|
2235
2310
|
params.set("include_episodes", "true");
|
|
2236
2311
|
return this.requestRaw("GET", `/memory/index?${params.toString()}`, undefined, {
|
|
@@ -2290,6 +2365,8 @@ class HarmonyApiClient {
|
|
|
2290
2365
|
params.set("type", options.type);
|
|
2291
2366
|
if (options?.limit !== undefined)
|
|
2292
2367
|
params.set("limit", String(options.limit));
|
|
2368
|
+
if (options?.consumer)
|
|
2369
|
+
params.set("consumer", options.consumer);
|
|
2293
2370
|
if (options?.include_episodes)
|
|
2294
2371
|
params.set("include_episodes", "true");
|
|
2295
2372
|
return this.requestRaw("GET", `/memory/search?${params.toString()}`, undefined, {
|
|
@@ -2395,14 +2472,15 @@ class HarmonyApiClient {
|
|
|
2395
2472
|
} catch {}
|
|
2396
2473
|
}
|
|
2397
2474
|
const variant = options.variant || "execute";
|
|
2398
|
-
const
|
|
2399
|
-
const assemblyId = undefined;
|
|
2475
|
+
const assemblyId = randomUUID2();
|
|
2400
2476
|
let memories;
|
|
2401
2477
|
try {
|
|
2402
2478
|
if (options.workspaceId && cardData.title) {
|
|
2403
|
-
const memoryResult = await this.searchMemoryEntities(options.workspaceId, cardData.title, {
|
|
2479
|
+
const memoryResult = await this.searchMemoryEntities(options.workspaceId, buildMemoryQuery(cardData.title, cardData.description), {
|
|
2404
2480
|
project_id: options.projectId,
|
|
2405
|
-
limit: 5
|
|
2481
|
+
limit: 5,
|
|
2482
|
+
consumer: "agent-prompt",
|
|
2483
|
+
assembly_id: assemblyId
|
|
2406
2484
|
});
|
|
2407
2485
|
if (memoryResult.entities?.length > 0) {
|
|
2408
2486
|
memories = memoryResult.entities.map((e) => ({
|
|
@@ -2426,7 +2504,6 @@ class HarmonyApiClient {
|
|
|
2426
2504
|
contextOptions: options.contextOptions,
|
|
2427
2505
|
customConstraints: options.customConstraints,
|
|
2428
2506
|
memories,
|
|
2429
|
-
assembledContext: assembledContextStr,
|
|
2430
2507
|
assemblyId
|
|
2431
2508
|
});
|
|
2432
2509
|
try {
|
|
@@ -3340,6 +3417,34 @@ async function onboardNewUser(params) {
|
|
|
3340
3417
|
};
|
|
3341
3418
|
}
|
|
3342
3419
|
|
|
3420
|
+
// src/playbook-metric-warnings.ts
|
|
3421
|
+
function playbookMetricWarnings(agents, steps) {
|
|
3422
|
+
if (!Array.isArray(steps))
|
|
3423
|
+
return [];
|
|
3424
|
+
const declared = declaredGateMetricsFromAgents(agents);
|
|
3425
|
+
if (!declared.known)
|
|
3426
|
+
return [];
|
|
3427
|
+
const warnings = [];
|
|
3428
|
+
const seen = new Set;
|
|
3429
|
+
for (const ref of referencedGateMetrics({ steps, steps_version: 2 })) {
|
|
3430
|
+
if (declared.names.has(ref.metric) || seen.has(ref.metric))
|
|
3431
|
+
continue;
|
|
3432
|
+
seen.add(ref.metric);
|
|
3433
|
+
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}.`);
|
|
3434
|
+
}
|
|
3435
|
+
return warnings;
|
|
3436
|
+
}
|
|
3437
|
+
async function collectPlaybookMetricWarnings(client3, workspaceId, steps) {
|
|
3438
|
+
if (!workspaceId || !Array.isArray(steps))
|
|
3439
|
+
return [];
|
|
3440
|
+
try {
|
|
3441
|
+
const { agents } = await client3.listWorkspaceAgents(workspaceId);
|
|
3442
|
+
return playbookMetricWarnings(agents, steps);
|
|
3443
|
+
} catch {
|
|
3444
|
+
return [];
|
|
3445
|
+
}
|
|
3446
|
+
}
|
|
3447
|
+
|
|
3343
3448
|
// src/skills.ts
|
|
3344
3449
|
import {
|
|
3345
3450
|
existsSync as existsSync4,
|
|
@@ -4384,7 +4489,7 @@ var TOOLS = {
|
|
|
4384
4489
|
}
|
|
4385
4490
|
},
|
|
4386
4491
|
harmony_classify_card: {
|
|
4387
|
-
description: "
|
|
4492
|
+
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
4493
|
inputSchema: {
|
|
4389
4494
|
type: "object",
|
|
4390
4495
|
properties: {
|
|
@@ -6897,7 +7002,7 @@ ${options}
|
|
|
6897
7002
|
if (trimmed.length > 0) {
|
|
6898
7003
|
const touchIds = trimmed.map(({ entity }) => entity?.id).filter((id) => typeof id === "string");
|
|
6899
7004
|
if (touchIds.length > 0) {
|
|
6900
|
-
client3.batchTouchMemoryEntities(touchIds).catch(() => {});
|
|
7005
|
+
client3.batchTouchMemoryEntities(touchIds, "mcp-tool").catch(() => {});
|
|
6901
7006
|
}
|
|
6902
7007
|
}
|
|
6903
7008
|
let sessionEntities = [];
|
|
@@ -7311,7 +7416,12 @@ ${options}
|
|
|
7311
7416
|
autoBind: args.autoBind,
|
|
7312
7417
|
catalogId: args.catalogId
|
|
7313
7418
|
});
|
|
7314
|
-
|
|
7419
|
+
const warnings = await collectPlaybookMetricWarnings(client3, workspaceId, args.steps);
|
|
7420
|
+
return {
|
|
7421
|
+
success: true,
|
|
7422
|
+
playbook: result.playbook,
|
|
7423
|
+
...warnings.length > 0 ? { warnings } : {}
|
|
7424
|
+
};
|
|
7315
7425
|
}
|
|
7316
7426
|
case "harmony_update_playbook": {
|
|
7317
7427
|
const playbookId = z.string().uuid().parse(args.playbookId);
|
|
@@ -7324,7 +7434,12 @@ ${options}
|
|
|
7324
7434
|
triggerType: args.triggerType,
|
|
7325
7435
|
..."autoBind" in args ? { autoBind: args.autoBind } : {}
|
|
7326
7436
|
});
|
|
7327
|
-
|
|
7437
|
+
const warnings = await collectPlaybookMetricWarnings(client3, result.playbook?.workspace_id, args.steps);
|
|
7438
|
+
return {
|
|
7439
|
+
success: true,
|
|
7440
|
+
playbook: result.playbook,
|
|
7441
|
+
...warnings.length > 0 ? { warnings } : {}
|
|
7442
|
+
};
|
|
7328
7443
|
}
|
|
7329
7444
|
case "harmony_save_card_as_playbook":
|
|
7330
7445
|
return deprecatedRemovedToolResult("harmony_save_card_as_playbook");
|