@gethmy/mcp 2.23.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 +389 -141
- package/dist/index.js +356 -118
- package/dist/lib/api-client.js +137 -96
- package/dist/lib/config.js +79 -24
- package/dist/lib/oauth-refresh.js +76 -24
- package/package.json +1 -1
- package/src/api-client.ts +171 -5
- package/src/cli.ts +21 -12
- package/src/config.ts +201 -27
- package/src/playbook-metric-warnings.ts +56 -0
- package/src/prompt-builder.ts +9 -120
- package/src/read-consumer.ts +16 -0
- package/src/remote.ts +34 -6
- package/src/server.ts +195 -23
- package/src/tui/setup.ts +21 -6
package/dist/lib/api-client.js
CHANGED
|
@@ -17,7 +17,7 @@ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
|
17
17
|
// src/config.ts
|
|
18
18
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
19
19
|
import { homedir } from "node:os";
|
|
20
|
-
import { join } from "node:path";
|
|
20
|
+
import { dirname, join, parse, resolve } from "node:path";
|
|
21
21
|
function getConfigDir() {
|
|
22
22
|
return join(homedir(), ".harmony-mcp");
|
|
23
23
|
}
|
|
@@ -27,6 +27,22 @@ function getConfigPath() {
|
|
|
27
27
|
function getLocalConfigPath(cwd) {
|
|
28
28
|
return join(cwd || process.cwd(), LOCAL_CONFIG_FILENAME);
|
|
29
29
|
}
|
|
30
|
+
function findLocalConfigPath(cwd) {
|
|
31
|
+
const home = resolve(homedir());
|
|
32
|
+
let dir = resolve(cwd || process.cwd());
|
|
33
|
+
const { root } = parse(dir);
|
|
34
|
+
for (;; ) {
|
|
35
|
+
if (dir !== home && dir !== root) {
|
|
36
|
+
const candidate = join(dir, LOCAL_CONFIG_FILENAME);
|
|
37
|
+
if (existsSync(candidate))
|
|
38
|
+
return candidate;
|
|
39
|
+
}
|
|
40
|
+
const parent = dirname(dir);
|
|
41
|
+
if (parent === dir)
|
|
42
|
+
return null;
|
|
43
|
+
dir = parent;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
30
46
|
function emptyConfig() {
|
|
31
47
|
return {
|
|
32
48
|
apiKey: null,
|
|
@@ -78,8 +94,8 @@ function saveConfig(config) {
|
|
|
78
94
|
});
|
|
79
95
|
}
|
|
80
96
|
function loadLocalConfig(cwd) {
|
|
81
|
-
const localConfigPath =
|
|
82
|
-
if (
|
|
97
|
+
const localConfigPath = findLocalConfigPath(cwd);
|
|
98
|
+
if (localConfigPath === null) {
|
|
83
99
|
return null;
|
|
84
100
|
}
|
|
85
101
|
try {
|
|
@@ -94,7 +110,7 @@ function loadLocalConfig(cwd) {
|
|
|
94
110
|
}
|
|
95
111
|
}
|
|
96
112
|
function saveLocalConfig(config, cwd) {
|
|
97
|
-
const localConfigPath = getLocalConfigPath(cwd);
|
|
113
|
+
const localConfigPath = findLocalConfigPath(cwd) ?? getLocalConfigPath(cwd);
|
|
98
114
|
const existingConfig = loadLocalConfig(cwd) || {
|
|
99
115
|
workspaceId: null,
|
|
100
116
|
projectId: null
|
|
@@ -108,7 +124,7 @@ function saveLocalConfig(config, cwd) {
|
|
|
108
124
|
writeFileSync(localConfigPath, JSON.stringify(cleanConfig, null, 2));
|
|
109
125
|
}
|
|
110
126
|
function hasLocalConfig(cwd) {
|
|
111
|
-
return
|
|
127
|
+
return findLocalConfigPath(cwd) !== null;
|
|
112
128
|
}
|
|
113
129
|
function getActiveCredential() {
|
|
114
130
|
const config = loadConfig();
|
|
@@ -133,33 +149,69 @@ function getUserEmail() {
|
|
|
133
149
|
function setUserEmail(email) {
|
|
134
150
|
saveConfig({ userEmail: email });
|
|
135
151
|
}
|
|
136
|
-
function
|
|
137
|
-
if (options?.
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
152
|
+
function setActiveContext(context, options) {
|
|
153
|
+
if (options?.global) {
|
|
154
|
+
saveConfig({
|
|
155
|
+
activeWorkspaceId: context.workspaceId,
|
|
156
|
+
activeProjectId: context.projectId
|
|
157
|
+
});
|
|
158
|
+
return;
|
|
141
159
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
saveLocalConfig({ projectId }, options.cwd);
|
|
160
|
+
const localPath = findLocalConfigPath(options?.cwd);
|
|
161
|
+
if (options?.local || localPath !== null) {
|
|
162
|
+
saveLocalConfig({ workspaceId: context.workspaceId, projectId: context.projectId }, options?.cwd);
|
|
146
163
|
} else {
|
|
147
|
-
saveConfig({
|
|
164
|
+
saveConfig({
|
|
165
|
+
activeWorkspaceId: context.workspaceId,
|
|
166
|
+
activeProjectId: context.projectId
|
|
167
|
+
});
|
|
148
168
|
}
|
|
149
169
|
}
|
|
150
|
-
function
|
|
170
|
+
function setActiveWorkspace(workspaceId, options) {
|
|
171
|
+
const currentWorkspaceId = getActiveWorkspaceId(options?.cwd);
|
|
172
|
+
const keepProject = currentWorkspaceId === workspaceId;
|
|
173
|
+
setActiveContext({
|
|
174
|
+
workspaceId,
|
|
175
|
+
projectId: keepProject ? getActiveProjectId(options?.cwd) : null
|
|
176
|
+
}, options);
|
|
177
|
+
}
|
|
178
|
+
function readActiveContext(cwd) {
|
|
151
179
|
const localConfig = loadLocalConfig(cwd);
|
|
152
|
-
if (localConfig
|
|
153
|
-
return
|
|
180
|
+
if (localConfig) {
|
|
181
|
+
return {
|
|
182
|
+
workspaceId: localConfig.workspaceId ?? null,
|
|
183
|
+
projectId: localConfig.projectId ?? null
|
|
184
|
+
};
|
|
154
185
|
}
|
|
155
|
-
|
|
186
|
+
const globalConfig = loadConfig();
|
|
187
|
+
return {
|
|
188
|
+
workspaceId: globalConfig.activeWorkspaceId,
|
|
189
|
+
projectId: globalConfig.activeProjectId
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
function getActiveWorkspaceId(cwd) {
|
|
193
|
+
return readActiveContext(cwd).workspaceId;
|
|
156
194
|
}
|
|
157
195
|
function getActiveProjectId(cwd) {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
196
|
+
return readActiveContext(cwd).projectId;
|
|
197
|
+
}
|
|
198
|
+
function getActiveContext(cwd) {
|
|
199
|
+
return describeActiveContext({
|
|
200
|
+
projectId: getActiveProjectId(cwd),
|
|
201
|
+
workspaceId: getActiveWorkspaceId(cwd)
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
function describeActiveContext(context) {
|
|
205
|
+
const { projectId, workspaceId } = context;
|
|
206
|
+
if (projectId && !workspaceId) {
|
|
207
|
+
return {
|
|
208
|
+
projectId,
|
|
209
|
+
workspaceId,
|
|
210
|
+
consistent: false,
|
|
211
|
+
note: `An active project (${projectId}) is set with no active workspace, so ` + "workspace-scoped tools cannot resolve one from it. Re-set it with " + "harmony_set_project_context, or pass workspaceId explicitly."
|
|
212
|
+
};
|
|
161
213
|
}
|
|
162
|
-
return
|
|
214
|
+
return { projectId, workspaceId, consistent: true, note: null };
|
|
163
215
|
}
|
|
164
216
|
function isConfigured() {
|
|
165
217
|
const config = loadConfig();
|
|
@@ -238,7 +290,7 @@ function lockPath() {
|
|
|
238
290
|
return join2(getConfigDir(), LOCK_FILENAME);
|
|
239
291
|
}
|
|
240
292
|
function sleep(ms) {
|
|
241
|
-
return new Promise((
|
|
293
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
242
294
|
}
|
|
243
295
|
async function withRefreshLock(fn) {
|
|
244
296
|
const path = lockPath();
|
|
@@ -436,15 +488,7 @@ ${lines.join(`
|
|
|
436
488
|
`)}`;
|
|
437
489
|
}
|
|
438
490
|
function generatePrompt(options) {
|
|
439
|
-
const {
|
|
440
|
-
card,
|
|
441
|
-
column,
|
|
442
|
-
variant,
|
|
443
|
-
customConstraints,
|
|
444
|
-
memories,
|
|
445
|
-
assembledContext,
|
|
446
|
-
assemblyId
|
|
447
|
-
} = options;
|
|
491
|
+
const { card, column, variant, customConstraints, memories, assemblyId } = options;
|
|
448
492
|
const contextOpts = {
|
|
449
493
|
includeTitle: true,
|
|
450
494
|
includeDescription: true,
|
|
@@ -522,10 +566,7 @@ ${card.description}`);
|
|
|
522
566
|
roleFraming.outputSuggestions.forEach((s) => {
|
|
523
567
|
sections.push(`- ${s}`);
|
|
524
568
|
});
|
|
525
|
-
if (
|
|
526
|
-
sections.push(`
|
|
527
|
-
${assembledContext}`);
|
|
528
|
-
} else if (memories && memories.length > 0) {
|
|
569
|
+
if (memories && memories.length > 0) {
|
|
529
570
|
sections.push(`
|
|
530
571
|
## Relevant Memories`);
|
|
531
572
|
sections.push(`*${memories.length} memories recalled from knowledge graph:*`);
|
|
@@ -536,7 +577,7 @@ ${assembledContext}`);
|
|
|
536
577
|
sections.push(memory.content);
|
|
537
578
|
}
|
|
538
579
|
}
|
|
539
|
-
const oneThingLine = synthesizeOneThing(card, subtasks, links
|
|
580
|
+
const oneThingLine = synthesizeOneThing(card, subtasks, links);
|
|
540
581
|
if (oneThingLine) {
|
|
541
582
|
sections.push(`
|
|
542
583
|
## Recommended Next Step
|
|
@@ -563,7 +604,7 @@ ${customConstraints}`);
|
|
|
563
604
|
*Card #${card.short_id} | Generated for ${variant} mode*`);
|
|
564
605
|
const prompt = sections.join(`
|
|
565
606
|
`);
|
|
566
|
-
const memoryCount =
|
|
607
|
+
const memoryCount = memories?.length ?? 0;
|
|
567
608
|
return {
|
|
568
609
|
prompt,
|
|
569
610
|
variant,
|
|
@@ -584,40 +625,7 @@ ${customConstraints}`);
|
|
|
584
625
|
version: PROMPT_TEMPLATE_VERSION
|
|
585
626
|
};
|
|
586
627
|
}
|
|
587
|
-
function
|
|
588
|
-
const result = {
|
|
589
|
-
lastSessionStatus: null,
|
|
590
|
-
lastSessionTask: null,
|
|
591
|
-
lastSessionProgress: null,
|
|
592
|
-
blockers: [],
|
|
593
|
-
procedureNextStep: null
|
|
594
|
-
};
|
|
595
|
-
const sessionMatches = assembledContext.match(/### Session:.*?\n([\s\S]*?)(?=\n###|\n## |\n---|\n\*Assembly|$)/g);
|
|
596
|
-
if (sessionMatches && sessionMatches.length > 0) {
|
|
597
|
-
const latest = sessionMatches[0];
|
|
598
|
-
if (/Completed work on/i.test(latest)) {
|
|
599
|
-
result.lastSessionStatus = "completed";
|
|
600
|
-
} else if (/Paused work on|status:\s*paused/i.test(latest)) {
|
|
601
|
-
result.lastSessionStatus = "paused";
|
|
602
|
-
}
|
|
603
|
-
const taskMatch = latest.match(/Final task:\s*(.+)/);
|
|
604
|
-
if (taskMatch)
|
|
605
|
-
result.lastSessionTask = taskMatch[1].trim();
|
|
606
|
-
const progressMatch = latest.match(/Progress:\s*(\d+)%/);
|
|
607
|
-
if (progressMatch)
|
|
608
|
-
result.lastSessionProgress = parseInt(progressMatch[1], 10);
|
|
609
|
-
}
|
|
610
|
-
const blockerMatches = assembledContext.match(/(?:blocker|blocked by|blocking):\s*(.+)/gi);
|
|
611
|
-
if (blockerMatches) {
|
|
612
|
-
result.blockers = blockerMatches.map((m) => m.replace(/(?:blocker|blocked by|blocking):\s*/i, "").trim());
|
|
613
|
-
}
|
|
614
|
-
const stepMatches = assembledContext.match(/^\d+\.\s+(?!.*\*\*\[key step\]\*\*.*✓)(.+?)(?:\s*\*\*\[key step\]\*\*)?$/gm);
|
|
615
|
-
if (stepMatches && stepMatches.length > 0) {
|
|
616
|
-
result.procedureNextStep = stepMatches[0].replace(/^\d+\.\s+/, "").replace(/\s*\*\*\[key step\]\*\*.*$/, "").trim();
|
|
617
|
-
}
|
|
618
|
-
return result;
|
|
619
|
-
}
|
|
620
|
-
function synthesizeOneThing(card, subtasks, links, assembledContext) {
|
|
628
|
+
function synthesizeOneThing(card, subtasks, links) {
|
|
621
629
|
if (card.done)
|
|
622
630
|
return null;
|
|
623
631
|
const blockers = links.filter((l) => l.display_type === "is_blocked_by" && l.direction === "incoming");
|
|
@@ -625,14 +633,6 @@ function synthesizeOneThing(card, subtasks, links, assembledContext) {
|
|
|
625
633
|
const blocker = blockers[0];
|
|
626
634
|
return `Unblock first: resolve #${blocker.target_card.short_id} "${blocker.target_card.title}" which is blocking this card.`;
|
|
627
635
|
}
|
|
628
|
-
const session = assembledContext ? extractSessionInsights(assembledContext) : null;
|
|
629
|
-
if (session?.blockers && session.blockers.length > 0) {
|
|
630
|
-
return `Resolve blocker: ${session.blockers[0]}`;
|
|
631
|
-
}
|
|
632
|
-
if (session?.lastSessionStatus === "paused" && session.lastSessionTask) {
|
|
633
|
-
const progress = session.lastSessionProgress ? ` (was ${session.lastSessionProgress}% complete)` : "";
|
|
634
|
-
return `Resume previous session${progress}: "${session.lastSessionTask}".`;
|
|
635
|
-
}
|
|
636
636
|
if (subtasks.length > 0) {
|
|
637
637
|
const completed = subtasks.filter((s) => s.completed).length;
|
|
638
638
|
if (completed === subtasks.length) {
|
|
@@ -643,12 +643,6 @@ function synthesizeOneThing(card, subtasks, links, assembledContext) {
|
|
|
643
643
|
return `Work on next subtask: "${nextSubtask.title}" (${completed}/${subtasks.length} done).`;
|
|
644
644
|
}
|
|
645
645
|
}
|
|
646
|
-
if (session?.procedureNextStep) {
|
|
647
|
-
return `Follow procedure: ${session.procedureNextStep}`;
|
|
648
|
-
}
|
|
649
|
-
if (session?.lastSessionStatus === "completed" && session.lastSessionTask) {
|
|
650
|
-
return `Previous session completed ("${session.lastSessionTask}"). Review results and continue with remaining work.`;
|
|
651
|
-
}
|
|
652
646
|
if (card.due_date && (card.priority === "urgent" || card.priority === "high")) {
|
|
653
647
|
return `High-priority task with deadline ${card.due_date}. Start implementation immediately.`;
|
|
654
648
|
}
|
|
@@ -833,6 +827,9 @@ var init_prompt_builder = __esm(() => {
|
|
|
833
827
|
execute: `EXECUTE MODE: Implement this task completely. Write production-ready code following best practices. Include necessary tests and documentation.`
|
|
834
828
|
};
|
|
835
829
|
});
|
|
830
|
+
|
|
831
|
+
// src/api-client.ts
|
|
832
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
836
833
|
// ../harmony-shared/dist/agentStaleness.js
|
|
837
834
|
var AGENT_HEARTBEAT_LIVENESS_MS = 5 * 60 * 1000;
|
|
838
835
|
var AGENT_MILESTONE_LIVENESS_MS = 30 * 60 * 1000;
|
|
@@ -954,6 +951,9 @@ var TIMINGS = {
|
|
|
954
951
|
QUERY_STALE_TIME: 1000 * 60 * 5,
|
|
955
952
|
QUERY_GC_TIME: 1000 * 60 * 60 * 24
|
|
956
953
|
};
|
|
954
|
+
// ../harmony-shared/dist/gateConfigError.js
|
|
955
|
+
var GATE_CONFIG_ERROR_KEY = "configError";
|
|
956
|
+
var GATE_CONFIG_ERROR_MARK = Object.freeze({ [GATE_CONFIG_ERROR_KEY]: true });
|
|
957
957
|
// ../harmony-shared/dist/playbookStage.js
|
|
958
958
|
var STAGE_DAEMON_OWNED_TOOLS = [
|
|
959
959
|
"mcp__harmony__harmony_end_agent_session",
|
|
@@ -993,7 +993,20 @@ function getRetryDelay(attempt) {
|
|
|
993
993
|
const delay = Math.min(RETRY_CONFIG.baseDelayMs * 2 ** attempt, RETRY_CONFIG.maxDelayMs);
|
|
994
994
|
return Math.round(delay + delay * 0.25 * (Math.random() * 2 - 1));
|
|
995
995
|
}
|
|
996
|
-
var sleep2 = (ms) => new Promise((
|
|
996
|
+
var sleep2 = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
997
|
+
function buildMemoryQuery(title, description) {
|
|
998
|
+
const DESCRIPTION_CAP = 600;
|
|
999
|
+
const trimmedTitle = title.trim();
|
|
1000
|
+
const trimmedBody = (description ?? "").trim();
|
|
1001
|
+
if (!trimmedTitle && !trimmedBody)
|
|
1002
|
+
return "";
|
|
1003
|
+
if (!trimmedTitle)
|
|
1004
|
+
return trimmedBody.slice(0, DESCRIPTION_CAP);
|
|
1005
|
+
if (!trimmedBody)
|
|
1006
|
+
return trimmedTitle;
|
|
1007
|
+
return `${trimmedTitle}
|
|
1008
|
+
${trimmedBody.slice(0, DESCRIPTION_CAP)}`;
|
|
1009
|
+
}
|
|
997
1010
|
|
|
998
1011
|
class Semaphore {
|
|
999
1012
|
permits;
|
|
@@ -1006,7 +1019,7 @@ class Semaphore {
|
|
|
1006
1019
|
this.permits--;
|
|
1007
1020
|
return;
|
|
1008
1021
|
}
|
|
1009
|
-
return new Promise((
|
|
1022
|
+
return new Promise((resolve2) => this.queue.push(resolve2));
|
|
1010
1023
|
}
|
|
1011
1024
|
release() {
|
|
1012
1025
|
const next = this.queue.shift();
|
|
@@ -1464,6 +1477,14 @@ class HarmonyApiClient {
|
|
|
1464
1477
|
params.set("sinceSeq", String(sinceSeq));
|
|
1465
1478
|
return this.request("GET", `/cards/${cardId}/agent-messages?${params.toString()}`);
|
|
1466
1479
|
}
|
|
1480
|
+
async postBudgetDecision(cardId, data) {
|
|
1481
|
+
return this.request("POST", `/cards/${cardId}/budget-decisions`, data);
|
|
1482
|
+
}
|
|
1483
|
+
async getBudgetDecisions(cardId, sinceIso) {
|
|
1484
|
+
const params = new URLSearchParams;
|
|
1485
|
+
params.set("sinceIso", sinceIso);
|
|
1486
|
+
return this.request("GET", `/cards/${cardId}/budget-decisions?${params.toString()}`);
|
|
1487
|
+
}
|
|
1467
1488
|
async updateAgentProgress(cardId, data) {
|
|
1468
1489
|
return this.request("POST", `/cards/${cardId}/agent-context`, data);
|
|
1469
1490
|
}
|
|
@@ -1509,6 +1530,8 @@ class HarmonyApiClient {
|
|
|
1509
1530
|
params.set("offset", String(options.offset));
|
|
1510
1531
|
if (options.include_superseded)
|
|
1511
1532
|
params.set("include_superseded", "true");
|
|
1533
|
+
if (options.consumer)
|
|
1534
|
+
params.set("consumer", options.consumer);
|
|
1512
1535
|
if (options.include_episodes)
|
|
1513
1536
|
params.set("include_episodes", "true");
|
|
1514
1537
|
return this.request("GET", `/memory/entities?${params.toString()}`);
|
|
@@ -1564,6 +1587,12 @@ class HarmonyApiClient {
|
|
|
1564
1587
|
if (options.topK !== undefined) {
|
|
1565
1588
|
entities = entities.slice(0, options.topK);
|
|
1566
1589
|
}
|
|
1590
|
+
if (options.consumer) {
|
|
1591
|
+
const deliveredIds = entities.map((e) => e.id).filter((id) => typeof id === "string");
|
|
1592
|
+
if (deliveredIds.length > 0) {
|
|
1593
|
+
this.batchTouchMemoryEntities(deliveredIds, options.consumer).catch(() => {});
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1567
1596
|
return { entities };
|
|
1568
1597
|
}
|
|
1569
1598
|
async deleteMemoryEntity(entityId) {
|
|
@@ -1572,9 +1601,10 @@ class HarmonyApiClient {
|
|
|
1572
1601
|
async touchMemoryEntity(entityId) {
|
|
1573
1602
|
return this.request("POST", `/memory/entities/${entityId}/touch`);
|
|
1574
1603
|
}
|
|
1575
|
-
async batchTouchMemoryEntities(entityIds) {
|
|
1604
|
+
async batchTouchMemoryEntities(entityIds, consumer) {
|
|
1576
1605
|
return this.request("POST", "/memory/entities/batch-touch", {
|
|
1577
|
-
entity_ids: entityIds
|
|
1606
|
+
entity_ids: entityIds,
|
|
1607
|
+
...consumer ? { consumer } : {}
|
|
1578
1608
|
});
|
|
1579
1609
|
}
|
|
1580
1610
|
async createMemoryRelation(data) {
|
|
@@ -1600,8 +1630,12 @@ class HarmonyApiClient {
|
|
|
1600
1630
|
params.append("tags", tag);
|
|
1601
1631
|
if (options?.include_superseded)
|
|
1602
1632
|
params.set("include_superseded", "true");
|
|
1633
|
+
if (options?.consumer)
|
|
1634
|
+
params.set("consumer", options.consumer);
|
|
1603
1635
|
if (options?.include_episodes)
|
|
1604
1636
|
params.set("include_episodes", "true");
|
|
1637
|
+
if (options?.assembly_id)
|
|
1638
|
+
params.set("assembly_id", options.assembly_id);
|
|
1605
1639
|
return this.request("GET", `/memory/search?${params.toString()}`);
|
|
1606
1640
|
}
|
|
1607
1641
|
async getVaultIndex(options) {
|
|
@@ -1613,6 +1647,8 @@ class HarmonyApiClient {
|
|
|
1613
1647
|
params.set("type", options.type);
|
|
1614
1648
|
if (options.limit !== undefined)
|
|
1615
1649
|
params.set("limit", String(options.limit));
|
|
1650
|
+
if (options.consumer)
|
|
1651
|
+
params.set("consumer", options.consumer);
|
|
1616
1652
|
if (options.include_episodes)
|
|
1617
1653
|
params.set("include_episodes", "true");
|
|
1618
1654
|
return this.request("GET", `/memory/index?${params.toString()}`);
|
|
@@ -1626,6 +1662,8 @@ class HarmonyApiClient {
|
|
|
1626
1662
|
params.set("type", options.type);
|
|
1627
1663
|
if (options.limit !== undefined)
|
|
1628
1664
|
params.set("limit", String(options.limit));
|
|
1665
|
+
if (options.consumer)
|
|
1666
|
+
params.set("consumer", options.consumer);
|
|
1629
1667
|
if (options.include_episodes)
|
|
1630
1668
|
params.set("include_episodes", "true");
|
|
1631
1669
|
return this.requestRaw("GET", `/memory/index?${params.toString()}`, undefined, {
|
|
@@ -1685,6 +1723,8 @@ class HarmonyApiClient {
|
|
|
1685
1723
|
params.set("type", options.type);
|
|
1686
1724
|
if (options?.limit !== undefined)
|
|
1687
1725
|
params.set("limit", String(options.limit));
|
|
1726
|
+
if (options?.consumer)
|
|
1727
|
+
params.set("consumer", options.consumer);
|
|
1688
1728
|
if (options?.include_episodes)
|
|
1689
1729
|
params.set("include_episodes", "true");
|
|
1690
1730
|
return this.requestRaw("GET", `/memory/search?${params.toString()}`, undefined, {
|
|
@@ -1790,14 +1830,15 @@ class HarmonyApiClient {
|
|
|
1790
1830
|
} catch {}
|
|
1791
1831
|
}
|
|
1792
1832
|
const variant = options.variant || "execute";
|
|
1793
|
-
const
|
|
1794
|
-
const assemblyId = undefined;
|
|
1833
|
+
const assemblyId = randomUUID2();
|
|
1795
1834
|
let memories;
|
|
1796
1835
|
try {
|
|
1797
1836
|
if (options.workspaceId && cardData.title) {
|
|
1798
|
-
const memoryResult = await this.searchMemoryEntities(options.workspaceId, cardData.title, {
|
|
1837
|
+
const memoryResult = await this.searchMemoryEntities(options.workspaceId, buildMemoryQuery(cardData.title, cardData.description), {
|
|
1799
1838
|
project_id: options.projectId,
|
|
1800
|
-
limit: 5
|
|
1839
|
+
limit: 5,
|
|
1840
|
+
consumer: "agent-prompt",
|
|
1841
|
+
assembly_id: assemblyId
|
|
1801
1842
|
});
|
|
1802
1843
|
if (memoryResult.entities?.length > 0) {
|
|
1803
1844
|
memories = memoryResult.entities.map((e) => ({
|
|
@@ -1821,7 +1862,6 @@ class HarmonyApiClient {
|
|
|
1821
1862
|
contextOptions: options.contextOptions,
|
|
1822
1863
|
customConstraints: options.customConstraints,
|
|
1823
1864
|
memories,
|
|
1824
|
-
assembledContext: assembledContextStr,
|
|
1825
1865
|
assemblyId
|
|
1826
1866
|
});
|
|
1827
1867
|
try {
|
|
@@ -1931,6 +1971,7 @@ export {
|
|
|
1931
1971
|
resetClient,
|
|
1932
1972
|
requestWithBearer,
|
|
1933
1973
|
getClient,
|
|
1974
|
+
buildMemoryQuery,
|
|
1934
1975
|
HarmonyUnauthorizedError,
|
|
1935
1976
|
HarmonyApiClient
|
|
1936
1977
|
};
|
package/dist/lib/config.js
CHANGED
|
@@ -17,7 +17,7 @@ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
|
17
17
|
// src/config.ts
|
|
18
18
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
19
19
|
import { homedir } from "node:os";
|
|
20
|
-
import { join } from "node:path";
|
|
20
|
+
import { dirname, join, parse, resolve } from "node:path";
|
|
21
21
|
function getConfigDir() {
|
|
22
22
|
return join(homedir(), ".harmony-mcp");
|
|
23
23
|
}
|
|
@@ -27,6 +27,22 @@ function getConfigPath() {
|
|
|
27
27
|
function getLocalConfigPath(cwd) {
|
|
28
28
|
return join(cwd || process.cwd(), LOCAL_CONFIG_FILENAME);
|
|
29
29
|
}
|
|
30
|
+
function findLocalConfigPath(cwd) {
|
|
31
|
+
const home = resolve(homedir());
|
|
32
|
+
let dir = resolve(cwd || process.cwd());
|
|
33
|
+
const { root } = parse(dir);
|
|
34
|
+
for (;; ) {
|
|
35
|
+
if (dir !== home && dir !== root) {
|
|
36
|
+
const candidate = join(dir, LOCAL_CONFIG_FILENAME);
|
|
37
|
+
if (existsSync(candidate))
|
|
38
|
+
return candidate;
|
|
39
|
+
}
|
|
40
|
+
const parent = dirname(dir);
|
|
41
|
+
if (parent === dir)
|
|
42
|
+
return null;
|
|
43
|
+
dir = parent;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
30
46
|
function emptyConfig() {
|
|
31
47
|
return {
|
|
32
48
|
apiKey: null,
|
|
@@ -78,8 +94,8 @@ function saveConfig(config) {
|
|
|
78
94
|
});
|
|
79
95
|
}
|
|
80
96
|
function loadLocalConfig(cwd) {
|
|
81
|
-
const localConfigPath =
|
|
82
|
-
if (
|
|
97
|
+
const localConfigPath = findLocalConfigPath(cwd);
|
|
98
|
+
if (localConfigPath === null) {
|
|
83
99
|
return null;
|
|
84
100
|
}
|
|
85
101
|
try {
|
|
@@ -94,7 +110,7 @@ function loadLocalConfig(cwd) {
|
|
|
94
110
|
}
|
|
95
111
|
}
|
|
96
112
|
function saveLocalConfig(config, cwd) {
|
|
97
|
-
const localConfigPath = getLocalConfigPath(cwd);
|
|
113
|
+
const localConfigPath = findLocalConfigPath(cwd) ?? getLocalConfigPath(cwd);
|
|
98
114
|
const existingConfig = loadLocalConfig(cwd) || {
|
|
99
115
|
workspaceId: null,
|
|
100
116
|
projectId: null
|
|
@@ -108,7 +124,7 @@ function saveLocalConfig(config, cwd) {
|
|
|
108
124
|
writeFileSync(localConfigPath, JSON.stringify(cleanConfig, null, 2));
|
|
109
125
|
}
|
|
110
126
|
function hasLocalConfig(cwd) {
|
|
111
|
-
return
|
|
127
|
+
return findLocalConfigPath(cwd) !== null;
|
|
112
128
|
}
|
|
113
129
|
function getActiveCredential() {
|
|
114
130
|
const config = loadConfig();
|
|
@@ -133,33 +149,69 @@ function getUserEmail() {
|
|
|
133
149
|
function setUserEmail(email) {
|
|
134
150
|
saveConfig({ userEmail: email });
|
|
135
151
|
}
|
|
136
|
-
function
|
|
137
|
-
if (options?.
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
152
|
+
function setActiveContext(context, options) {
|
|
153
|
+
if (options?.global) {
|
|
154
|
+
saveConfig({
|
|
155
|
+
activeWorkspaceId: context.workspaceId,
|
|
156
|
+
activeProjectId: context.projectId
|
|
157
|
+
});
|
|
158
|
+
return;
|
|
141
159
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
saveLocalConfig({ projectId }, options.cwd);
|
|
160
|
+
const localPath = findLocalConfigPath(options?.cwd);
|
|
161
|
+
if (options?.local || localPath !== null) {
|
|
162
|
+
saveLocalConfig({ workspaceId: context.workspaceId, projectId: context.projectId }, options?.cwd);
|
|
146
163
|
} else {
|
|
147
|
-
saveConfig({
|
|
164
|
+
saveConfig({
|
|
165
|
+
activeWorkspaceId: context.workspaceId,
|
|
166
|
+
activeProjectId: context.projectId
|
|
167
|
+
});
|
|
148
168
|
}
|
|
149
169
|
}
|
|
150
|
-
function
|
|
170
|
+
function setActiveWorkspace(workspaceId, options) {
|
|
171
|
+
const currentWorkspaceId = getActiveWorkspaceId(options?.cwd);
|
|
172
|
+
const keepProject = currentWorkspaceId === workspaceId;
|
|
173
|
+
setActiveContext({
|
|
174
|
+
workspaceId,
|
|
175
|
+
projectId: keepProject ? getActiveProjectId(options?.cwd) : null
|
|
176
|
+
}, options);
|
|
177
|
+
}
|
|
178
|
+
function readActiveContext(cwd) {
|
|
151
179
|
const localConfig = loadLocalConfig(cwd);
|
|
152
|
-
if (localConfig
|
|
153
|
-
return
|
|
180
|
+
if (localConfig) {
|
|
181
|
+
return {
|
|
182
|
+
workspaceId: localConfig.workspaceId ?? null,
|
|
183
|
+
projectId: localConfig.projectId ?? null
|
|
184
|
+
};
|
|
154
185
|
}
|
|
155
|
-
|
|
186
|
+
const globalConfig = loadConfig();
|
|
187
|
+
return {
|
|
188
|
+
workspaceId: globalConfig.activeWorkspaceId,
|
|
189
|
+
projectId: globalConfig.activeProjectId
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
function getActiveWorkspaceId(cwd) {
|
|
193
|
+
return readActiveContext(cwd).workspaceId;
|
|
156
194
|
}
|
|
157
195
|
function getActiveProjectId(cwd) {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
196
|
+
return readActiveContext(cwd).projectId;
|
|
197
|
+
}
|
|
198
|
+
function getActiveContext(cwd) {
|
|
199
|
+
return describeActiveContext({
|
|
200
|
+
projectId: getActiveProjectId(cwd),
|
|
201
|
+
workspaceId: getActiveWorkspaceId(cwd)
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
function describeActiveContext(context) {
|
|
205
|
+
const { projectId, workspaceId } = context;
|
|
206
|
+
if (projectId && !workspaceId) {
|
|
207
|
+
return {
|
|
208
|
+
projectId,
|
|
209
|
+
workspaceId,
|
|
210
|
+
consistent: false,
|
|
211
|
+
note: `An active project (${projectId}) is set with no active workspace, so ` + "workspace-scoped tools cannot resolve one from it. Re-set it with " + "harmony_set_project_context, or pass workspaceId explicitly."
|
|
212
|
+
};
|
|
161
213
|
}
|
|
162
|
-
return
|
|
214
|
+
return { projectId, workspaceId, consistent: true, note: null };
|
|
163
215
|
}
|
|
164
216
|
function isConfigured() {
|
|
165
217
|
const config = loadConfig();
|
|
@@ -214,7 +266,7 @@ init_config();
|
|
|
214
266
|
export {
|
|
215
267
|
setUserEmail,
|
|
216
268
|
setActiveWorkspace,
|
|
217
|
-
|
|
269
|
+
setActiveContext,
|
|
218
270
|
saveLocalConfig,
|
|
219
271
|
saveConfig,
|
|
220
272
|
loadLocalConfig,
|
|
@@ -232,5 +284,8 @@ export {
|
|
|
232
284
|
getActiveWorkspaceId,
|
|
233
285
|
getActiveProjectId,
|
|
234
286
|
getActiveCredential,
|
|
287
|
+
getActiveContext,
|
|
288
|
+
findLocalConfigPath,
|
|
289
|
+
describeActiveContext,
|
|
235
290
|
areSkillsInstalled
|
|
236
291
|
};
|