@gethmy/mcp 2.5.7 → 2.6.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 CHANGED
@@ -1479,6 +1479,51 @@ class HarmonyApiClient {
1479
1479
  async updateMemoryEntity(entityId, updates) {
1480
1480
  return this.request("PUT", `/memory/entities/${entityId}`, updates);
1481
1481
  }
1482
+ async harmonyRecall(options) {
1483
+ const fetchLimit = Math.max(options.topK ?? 3, 50);
1484
+ let entities = [];
1485
+ if (options.query) {
1486
+ const search = await this.searchMemoryEntities(options.workspaceId, options.query, {
1487
+ project_id: options.projectId,
1488
+ type: options.type?.length === 1 ? options.type[0] : undefined,
1489
+ limit: fetchLimit
1490
+ });
1491
+ entities = search.entities ?? [];
1492
+ } else {
1493
+ const list = await this.listMemoryEntities({
1494
+ workspace_id: options.workspaceId,
1495
+ project_id: options.projectId,
1496
+ scope: options.scope,
1497
+ type: options.type?.length === 1 ? options.type[0] : undefined,
1498
+ tags: options.tags,
1499
+ min_confidence: options.minConfidence,
1500
+ limit: fetchLimit
1501
+ });
1502
+ entities = list.entities ?? [];
1503
+ }
1504
+ if (options.type && options.type.length > 1) {
1505
+ const allowed = new Set(options.type);
1506
+ entities = entities.filter((e) => allowed.has(e.type));
1507
+ }
1508
+ if (options.memory_tier) {
1509
+ entities = entities.filter((e) => e.memory_tier === options.memory_tier);
1510
+ }
1511
+ if (options.scope) {
1512
+ entities = entities.filter((e) => e.scope === options.scope);
1513
+ }
1514
+ if (options.tags?.length) {
1515
+ const wanted = new Set(options.tags);
1516
+ entities = entities.filter((e) => (e.tags ?? []).some((t) => wanted.has(t)));
1517
+ }
1518
+ if (typeof options.minConfidence === "number") {
1519
+ const threshold = options.minConfidence;
1520
+ entities = entities.filter((e) => typeof e.confidence === "number" && e.confidence >= threshold);
1521
+ }
1522
+ if (options.topK !== undefined) {
1523
+ entities = entities.slice(0, options.topK);
1524
+ }
1525
+ return { entities };
1526
+ }
1482
1527
  async deleteMemoryEntity(entityId) {
1483
1528
  return this.request("DELETE", `/memory/entities/${entityId}`);
1484
1529
  }
@@ -1785,11 +1830,8 @@ function resolveAgentIdentity(info) {
1785
1830
  var AUTO_START_TRIGGERS = new Set([
1786
1831
  "harmony_generate_prompt",
1787
1832
  "harmony_update_card",
1788
- "harmony_move_card",
1789
1833
  "harmony_create_subtask",
1790
- "harmony_toggle_subtask",
1791
- "harmony_add_label_to_card",
1792
- "harmony_remove_label_from_card"
1834
+ "harmony_toggle_subtask"
1793
1835
  ]);
1794
1836
  var INACTIVITY_TIMEOUT_MS = 10 * 60 * 1000;
1795
1837
  var CHECK_INTERVAL_MS = 60 * 1000;
package/dist/index.js CHANGED
@@ -1475,6 +1475,51 @@ class HarmonyApiClient {
1475
1475
  async updateMemoryEntity(entityId, updates) {
1476
1476
  return this.request("PUT", `/memory/entities/${entityId}`, updates);
1477
1477
  }
1478
+ async harmonyRecall(options) {
1479
+ const fetchLimit = Math.max(options.topK ?? 3, 50);
1480
+ let entities = [];
1481
+ if (options.query) {
1482
+ const search = await this.searchMemoryEntities(options.workspaceId, options.query, {
1483
+ project_id: options.projectId,
1484
+ type: options.type?.length === 1 ? options.type[0] : undefined,
1485
+ limit: fetchLimit
1486
+ });
1487
+ entities = search.entities ?? [];
1488
+ } else {
1489
+ const list = await this.listMemoryEntities({
1490
+ workspace_id: options.workspaceId,
1491
+ project_id: options.projectId,
1492
+ scope: options.scope,
1493
+ type: options.type?.length === 1 ? options.type[0] : undefined,
1494
+ tags: options.tags,
1495
+ min_confidence: options.minConfidence,
1496
+ limit: fetchLimit
1497
+ });
1498
+ entities = list.entities ?? [];
1499
+ }
1500
+ if (options.type && options.type.length > 1) {
1501
+ const allowed = new Set(options.type);
1502
+ entities = entities.filter((e) => allowed.has(e.type));
1503
+ }
1504
+ if (options.memory_tier) {
1505
+ entities = entities.filter((e) => e.memory_tier === options.memory_tier);
1506
+ }
1507
+ if (options.scope) {
1508
+ entities = entities.filter((e) => e.scope === options.scope);
1509
+ }
1510
+ if (options.tags?.length) {
1511
+ const wanted = new Set(options.tags);
1512
+ entities = entities.filter((e) => (e.tags ?? []).some((t) => wanted.has(t)));
1513
+ }
1514
+ if (typeof options.minConfidence === "number") {
1515
+ const threshold = options.minConfidence;
1516
+ entities = entities.filter((e) => typeof e.confidence === "number" && e.confidence >= threshold);
1517
+ }
1518
+ if (options.topK !== undefined) {
1519
+ entities = entities.slice(0, options.topK);
1520
+ }
1521
+ return { entities };
1522
+ }
1478
1523
  async deleteMemoryEntity(entityId) {
1479
1524
  return this.request("DELETE", `/memory/entities/${entityId}`);
1480
1525
  }
@@ -1781,11 +1826,8 @@ function resolveAgentIdentity(info) {
1781
1826
  var AUTO_START_TRIGGERS = new Set([
1782
1827
  "harmony_generate_prompt",
1783
1828
  "harmony_update_card",
1784
- "harmony_move_card",
1785
1829
  "harmony_create_subtask",
1786
- "harmony_toggle_subtask",
1787
- "harmony_add_label_to_card",
1788
- "harmony_remove_label_from_card"
1830
+ "harmony_toggle_subtask"
1789
1831
  ]);
1790
1832
  var INACTIVITY_TIMEOUT_MS = 10 * 60 * 1000;
1791
1833
  var CHECK_INTERVAL_MS = 60 * 1000;
@@ -1082,6 +1082,51 @@ class HarmonyApiClient {
1082
1082
  async updateMemoryEntity(entityId, updates) {
1083
1083
  return this.request("PUT", `/memory/entities/${entityId}`, updates);
1084
1084
  }
1085
+ async harmonyRecall(options) {
1086
+ const fetchLimit = Math.max(options.topK ?? 3, 50);
1087
+ let entities = [];
1088
+ if (options.query) {
1089
+ const search = await this.searchMemoryEntities(options.workspaceId, options.query, {
1090
+ project_id: options.projectId,
1091
+ type: options.type?.length === 1 ? options.type[0] : undefined,
1092
+ limit: fetchLimit
1093
+ });
1094
+ entities = search.entities ?? [];
1095
+ } else {
1096
+ const list = await this.listMemoryEntities({
1097
+ workspace_id: options.workspaceId,
1098
+ project_id: options.projectId,
1099
+ scope: options.scope,
1100
+ type: options.type?.length === 1 ? options.type[0] : undefined,
1101
+ tags: options.tags,
1102
+ min_confidence: options.minConfidence,
1103
+ limit: fetchLimit
1104
+ });
1105
+ entities = list.entities ?? [];
1106
+ }
1107
+ if (options.type && options.type.length > 1) {
1108
+ const allowed = new Set(options.type);
1109
+ entities = entities.filter((e) => allowed.has(e.type));
1110
+ }
1111
+ if (options.memory_tier) {
1112
+ entities = entities.filter((e) => e.memory_tier === options.memory_tier);
1113
+ }
1114
+ if (options.scope) {
1115
+ entities = entities.filter((e) => e.scope === options.scope);
1116
+ }
1117
+ if (options.tags?.length) {
1118
+ const wanted = new Set(options.tags);
1119
+ entities = entities.filter((e) => (e.tags ?? []).some((t) => wanted.has(t)));
1120
+ }
1121
+ if (typeof options.minConfidence === "number") {
1122
+ const threshold = options.minConfidence;
1123
+ entities = entities.filter((e) => typeof e.confidence === "number" && e.confidence >= threshold);
1124
+ }
1125
+ if (options.topK !== undefined) {
1126
+ entities = entities.slice(0, options.topK);
1127
+ }
1128
+ return { entities };
1129
+ }
1085
1130
  async deleteMemoryEntity(entityId) {
1086
1131
  return this.request("DELETE", `/memory/entities/${entityId}`);
1087
1132
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gethmy/mcp",
3
- "version": "2.5.7",
3
+ "version": "2.6.0",
4
4
  "description": "MCP server for Harmony Kanban board - enables AI coding agents to manage your boards",
5
5
  "publishConfig": {
6
6
  "access": "public"
package/src/api-client.ts CHANGED
@@ -743,15 +743,101 @@ export class HarmonyApiClient {
743
743
  scope?: string;
744
744
  type?: string;
745
745
  memory_tier?: string;
746
- // AGP lifecycle fields. Backend may not yet whitelist these — extra keys
747
- // are dropped server-side, leaving the call as a no-op for those fields.
746
+ // Supersede semantics used by Phase 1.5 review-reject back-fill to
747
+ // tombstone the original implement episode without hard-deleting it.
748
+ // The backend sets superseded_at automatically when superseded_by lands.
748
749
  superseded_by?: string | null;
750
+ superseded_at?: string | null;
749
751
  version?: number;
750
752
  },
751
753
  ): Promise<{ entity: unknown; warnings?: string[] }> {
752
754
  return this.request("PUT", `/memory/entities/${entityId}`, updates);
753
755
  }
754
756
 
757
+ /**
758
+ * Retrieve memories filtered by type/tier/scope, optionally ranked by a
759
+ * free-text query. Wraps `searchMemoryEntities` (when a query is given)
760
+ * or `listMemoryEntities` and applies client-side filters that the REST
761
+ * surface doesn't natively expose (multi-type, memory_tier).
762
+ *
763
+ * Used by the agent daemon's read hook to surface similar past episodes
764
+ * before building a new task prompt (Phase 1.5).
765
+ */
766
+ async harmonyRecall(options: {
767
+ workspaceId: string;
768
+ projectId?: string;
769
+ query?: string;
770
+ type?: string[];
771
+ memory_tier?: string;
772
+ scope?: string;
773
+ tags?: string[];
774
+ minConfidence?: number;
775
+ topK?: number;
776
+ }): Promise<{ entities: unknown[] }> {
777
+ // Over-fetch beyond topK so client-side filters (multi-type, memory_tier,
778
+ // tags) have headroom — matches the MCP server's recall path (server.ts).
779
+ const fetchLimit = Math.max(options.topK ?? 3, 50);
780
+ let entities: Array<Record<string, unknown>> = [];
781
+
782
+ if (options.query) {
783
+ // searchMemoryEntities accepts a single type — refine client-side
784
+ // when the caller passed multiple.
785
+ const search = await this.searchMemoryEntities(
786
+ options.workspaceId,
787
+ options.query,
788
+ {
789
+ project_id: options.projectId,
790
+ type: options.type?.length === 1 ? options.type[0] : undefined,
791
+ limit: fetchLimit,
792
+ },
793
+ );
794
+ entities = (search.entities ?? []) as Array<Record<string, unknown>>;
795
+ } else {
796
+ const list = await this.listMemoryEntities({
797
+ workspace_id: options.workspaceId,
798
+ project_id: options.projectId,
799
+ scope: options.scope,
800
+ type: options.type?.length === 1 ? options.type[0] : undefined,
801
+ tags: options.tags,
802
+ min_confidence: options.minConfidence,
803
+ limit: fetchLimit,
804
+ });
805
+ entities = (list.entities ?? []) as Array<Record<string, unknown>>;
806
+ }
807
+
808
+ // Client-side filters: REST surface lacks multi-type and memory_tier.
809
+ if (options.type && options.type.length > 1) {
810
+ const allowed = new Set(options.type);
811
+ entities = entities.filter((e) => allowed.has(e.type as string));
812
+ }
813
+ if (options.memory_tier) {
814
+ entities = entities.filter((e) => e.memory_tier === options.memory_tier);
815
+ }
816
+ if (options.scope) {
817
+ entities = entities.filter((e) => e.scope === options.scope);
818
+ }
819
+ if (options.tags?.length) {
820
+ const wanted = new Set(options.tags);
821
+ entities = entities.filter((e) =>
822
+ ((e.tags as string[]) ?? []).some((t) => wanted.has(t)),
823
+ );
824
+ }
825
+ if (typeof options.minConfidence === "number") {
826
+ const threshold = options.minConfidence;
827
+ entities = entities.filter(
828
+ (e) =>
829
+ typeof e.confidence === "number" &&
830
+ (e.confidence as number) >= threshold,
831
+ );
832
+ }
833
+
834
+ if (options.topK !== undefined) {
835
+ entities = entities.slice(0, options.topK);
836
+ }
837
+
838
+ return { entities };
839
+ }
840
+
755
841
  async deleteMemoryEntity(entityId: string): Promise<{ success: boolean }> {
756
842
  return this.request("DELETE", `/memory/entities/${entityId}`);
757
843
  }
@@ -65,15 +65,19 @@ export function resolveAgentIdentity(info: ClientInfo | null): {
65
65
  return { agentIdentifier: key, agentName: displayName };
66
66
  }
67
67
 
68
- /** Tools that trigger auto-start of a session */
68
+ /**
69
+ * Tools that trigger auto-start of a session.
70
+ *
71
+ * Restricted to tools that signal real work on a card. Board-management ops
72
+ * (move, label add/remove) are excluded — they're routinely used for triage
73
+ * and would create false-positive sessions whose side effect (the auto-added
74
+ * `agent` label on the card) confuses both UI and humans.
75
+ */
69
76
  export const AUTO_START_TRIGGERS = new Set([
70
77
  "harmony_generate_prompt",
71
78
  "harmony_update_card",
72
- "harmony_move_card",
73
79
  "harmony_create_subtask",
74
80
  "harmony_toggle_subtask",
75
- "harmony_add_label_to_card",
76
- "harmony_remove_label_from_card",
77
81
  ]);
78
82
 
79
83
  export const INACTIVITY_TIMEOUT_MS = 10 * 60 * 1000; // 10 minutes