@gethmy/mcp 2.10.0 → 2.11.1
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 +43 -2
- package/dist/index.js +43 -2
- package/dist/lib/api-client.js +6 -0
- package/package.json +1 -1
- package/src/api-client.ts +24 -0
- package/src/server.ts +61 -1
package/dist/cli.js
CHANGED
|
@@ -1913,6 +1913,12 @@ class HarmonyApiClient {
|
|
|
1913
1913
|
async startAgentSession(cardId, data) {
|
|
1914
1914
|
return this.request("POST", `/cards/${cardId}/agent-context`, data);
|
|
1915
1915
|
}
|
|
1916
|
+
async getPendingMessages(cardId, sessionId, sinceSeq) {
|
|
1917
|
+
const params = new URLSearchParams;
|
|
1918
|
+
params.set("sessionId", sessionId);
|
|
1919
|
+
params.set("sinceSeq", String(sinceSeq));
|
|
1920
|
+
return this.request("GET", `/cards/${cardId}/agent-messages?${params.toString()}`);
|
|
1921
|
+
}
|
|
1916
1922
|
async updateAgentProgress(cardId, data) {
|
|
1917
1923
|
return this.request("POST", `/cards/${cardId}/agent-context`, data);
|
|
1918
1924
|
}
|
|
@@ -4172,6 +4178,10 @@ var TOOLS = {
|
|
|
4172
4178
|
type: "array",
|
|
4173
4179
|
items: { type: "string" },
|
|
4174
4180
|
description: 'Label names to add to card (e.g., ["agent"]). Case-insensitive match.'
|
|
4181
|
+
},
|
|
4182
|
+
steerable: {
|
|
4183
|
+
type: "boolean",
|
|
4184
|
+
description: "Set true only if this session will poll harmony_get_pending_messages at its checkpoints. Enables the live steering composer for the run; leave unset/false if you won't consume steering messages."
|
|
4175
4185
|
}
|
|
4176
4186
|
},
|
|
4177
4187
|
required: ["cardId", "agentIdentifier", "agentName"]
|
|
@@ -4262,6 +4272,27 @@ var TOOLS = {
|
|
|
4262
4272
|
required: ["cardId"]
|
|
4263
4273
|
}
|
|
4264
4274
|
},
|
|
4275
|
+
harmony_get_pending_messages: {
|
|
4276
|
+
description: "Drain queued steering messages a teammate sent to your live agent session (card #473). Call at your progress checkpoints with the session id from harmony_start_agent_session and the highest seq you've already consumed; returns user messages with seq > sinceSeq, oldest first. Fold them into your next step and advance sinceSeq to the largest returned seq so each is handled exactly once.",
|
|
4277
|
+
inputSchema: {
|
|
4278
|
+
type: "object",
|
|
4279
|
+
properties: {
|
|
4280
|
+
cardId: {
|
|
4281
|
+
type: "string",
|
|
4282
|
+
description: "Card ID with the live session"
|
|
4283
|
+
},
|
|
4284
|
+
sessionId: {
|
|
4285
|
+
type: "string",
|
|
4286
|
+
description: "Agent session id (the `session.id` returned by harmony_start_agent_session)"
|
|
4287
|
+
},
|
|
4288
|
+
sinceSeq: {
|
|
4289
|
+
type: "number",
|
|
4290
|
+
description: "Return only messages with seq greater than this (the last seq you consumed). Defaults to 0 to fetch all."
|
|
4291
|
+
}
|
|
4292
|
+
},
|
|
4293
|
+
required: ["cardId", "sessionId"]
|
|
4294
|
+
}
|
|
4295
|
+
},
|
|
4265
4296
|
harmony_generate_prompt: {
|
|
4266
4297
|
description: "Generate an AI-ready prompt from a card. Automatically infers role and focus based on labels (bug, feature, design, etc.). Use this to create context-rich prompts for working on cards.",
|
|
4267
4298
|
inputSchema: {
|
|
@@ -5597,7 +5628,8 @@ async function handleToolCall(name, args, deps) {
|
|
|
5597
5628
|
agentName,
|
|
5598
5629
|
status: "working",
|
|
5599
5630
|
currentTask: args.currentTask,
|
|
5600
|
-
estimatedMinutesRemaining: args.estimatedMinutesRemaining
|
|
5631
|
+
estimatedMinutesRemaining: args.estimatedMinutesRemaining,
|
|
5632
|
+
steerable: args.steerable === true || args.steerable === "true" ? true : undefined
|
|
5601
5633
|
});
|
|
5602
5634
|
markExplicit(cardId, {
|
|
5603
5635
|
agentIdentifier,
|
|
@@ -5636,6 +5668,7 @@ async function handleToolCall(name, args, deps) {
|
|
|
5636
5668
|
} else if (callerRecentActions.length > 0) {
|
|
5637
5669
|
mergedRecentActions = callerRecentActions;
|
|
5638
5670
|
}
|
|
5671
|
+
const runActivity = (callerActions || []).map((a) => a.description).filter((d) => typeof d === "string" && d.length > 0);
|
|
5639
5672
|
const result = await client3.updateAgentProgress(cardId, {
|
|
5640
5673
|
agentIdentifier,
|
|
5641
5674
|
agentName,
|
|
@@ -5644,7 +5677,8 @@ async function handleToolCall(name, args, deps) {
|
|
|
5644
5677
|
currentTask: args.currentTask,
|
|
5645
5678
|
blockers: args.blockers,
|
|
5646
5679
|
estimatedMinutesRemaining: args.estimatedMinutesRemaining,
|
|
5647
|
-
...mergedRecentActions && { recentActions: mergedRecentActions }
|
|
5680
|
+
...mergedRecentActions && { recentActions: mergedRecentActions },
|
|
5681
|
+
...runActivity.length > 0 && { runActivity }
|
|
5648
5682
|
});
|
|
5649
5683
|
return { success: true, midSessionLearnings: 0, ...result };
|
|
5650
5684
|
}
|
|
@@ -5700,6 +5734,13 @@ async function handleToolCall(name, args, deps) {
|
|
|
5700
5734
|
});
|
|
5701
5735
|
return { success: true, ...result };
|
|
5702
5736
|
}
|
|
5737
|
+
case "harmony_get_pending_messages": {
|
|
5738
|
+
const cardId = z.string().uuid().parse(args.cardId);
|
|
5739
|
+
const sessionId = z.string().min(1).parse(args.sessionId);
|
|
5740
|
+
const sinceSeq = args.sinceSeq !== undefined ? z.number().int().min(0).parse(args.sinceSeq) : 0;
|
|
5741
|
+
const result = await client3.getPendingMessages(cardId, sessionId, sinceSeq);
|
|
5742
|
+
return { success: true, ...result };
|
|
5743
|
+
}
|
|
5703
5744
|
case "harmony_generate_prompt": {
|
|
5704
5745
|
let cardId;
|
|
5705
5746
|
if (args.cardId) {
|
package/dist/index.js
CHANGED
|
@@ -1908,6 +1908,12 @@ class HarmonyApiClient {
|
|
|
1908
1908
|
async startAgentSession(cardId, data) {
|
|
1909
1909
|
return this.request("POST", `/cards/${cardId}/agent-context`, data);
|
|
1910
1910
|
}
|
|
1911
|
+
async getPendingMessages(cardId, sessionId, sinceSeq) {
|
|
1912
|
+
const params = new URLSearchParams;
|
|
1913
|
+
params.set("sessionId", sessionId);
|
|
1914
|
+
params.set("sinceSeq", String(sinceSeq));
|
|
1915
|
+
return this.request("GET", `/cards/${cardId}/agent-messages?${params.toString()}`);
|
|
1916
|
+
}
|
|
1911
1917
|
async updateAgentProgress(cardId, data) {
|
|
1912
1918
|
return this.request("POST", `/cards/${cardId}/agent-context`, data);
|
|
1913
1919
|
}
|
|
@@ -4167,6 +4173,10 @@ var TOOLS = {
|
|
|
4167
4173
|
type: "array",
|
|
4168
4174
|
items: { type: "string" },
|
|
4169
4175
|
description: 'Label names to add to card (e.g., ["agent"]). Case-insensitive match.'
|
|
4176
|
+
},
|
|
4177
|
+
steerable: {
|
|
4178
|
+
type: "boolean",
|
|
4179
|
+
description: "Set true only if this session will poll harmony_get_pending_messages at its checkpoints. Enables the live steering composer for the run; leave unset/false if you won't consume steering messages."
|
|
4170
4180
|
}
|
|
4171
4181
|
},
|
|
4172
4182
|
required: ["cardId", "agentIdentifier", "agentName"]
|
|
@@ -4257,6 +4267,27 @@ var TOOLS = {
|
|
|
4257
4267
|
required: ["cardId"]
|
|
4258
4268
|
}
|
|
4259
4269
|
},
|
|
4270
|
+
harmony_get_pending_messages: {
|
|
4271
|
+
description: "Drain queued steering messages a teammate sent to your live agent session (card #473). Call at your progress checkpoints with the session id from harmony_start_agent_session and the highest seq you've already consumed; returns user messages with seq > sinceSeq, oldest first. Fold them into your next step and advance sinceSeq to the largest returned seq so each is handled exactly once.",
|
|
4272
|
+
inputSchema: {
|
|
4273
|
+
type: "object",
|
|
4274
|
+
properties: {
|
|
4275
|
+
cardId: {
|
|
4276
|
+
type: "string",
|
|
4277
|
+
description: "Card ID with the live session"
|
|
4278
|
+
},
|
|
4279
|
+
sessionId: {
|
|
4280
|
+
type: "string",
|
|
4281
|
+
description: "Agent session id (the `session.id` returned by harmony_start_agent_session)"
|
|
4282
|
+
},
|
|
4283
|
+
sinceSeq: {
|
|
4284
|
+
type: "number",
|
|
4285
|
+
description: "Return only messages with seq greater than this (the last seq you consumed). Defaults to 0 to fetch all."
|
|
4286
|
+
}
|
|
4287
|
+
},
|
|
4288
|
+
required: ["cardId", "sessionId"]
|
|
4289
|
+
}
|
|
4290
|
+
},
|
|
4260
4291
|
harmony_generate_prompt: {
|
|
4261
4292
|
description: "Generate an AI-ready prompt from a card. Automatically infers role and focus based on labels (bug, feature, design, etc.). Use this to create context-rich prompts for working on cards.",
|
|
4262
4293
|
inputSchema: {
|
|
@@ -5592,7 +5623,8 @@ async function handleToolCall(name, args, deps) {
|
|
|
5592
5623
|
agentName,
|
|
5593
5624
|
status: "working",
|
|
5594
5625
|
currentTask: args.currentTask,
|
|
5595
|
-
estimatedMinutesRemaining: args.estimatedMinutesRemaining
|
|
5626
|
+
estimatedMinutesRemaining: args.estimatedMinutesRemaining,
|
|
5627
|
+
steerable: args.steerable === true || args.steerable === "true" ? true : undefined
|
|
5596
5628
|
});
|
|
5597
5629
|
markExplicit(cardId, {
|
|
5598
5630
|
agentIdentifier,
|
|
@@ -5631,6 +5663,7 @@ async function handleToolCall(name, args, deps) {
|
|
|
5631
5663
|
} else if (callerRecentActions.length > 0) {
|
|
5632
5664
|
mergedRecentActions = callerRecentActions;
|
|
5633
5665
|
}
|
|
5666
|
+
const runActivity = (callerActions || []).map((a) => a.description).filter((d) => typeof d === "string" && d.length > 0);
|
|
5634
5667
|
const result = await client3.updateAgentProgress(cardId, {
|
|
5635
5668
|
agentIdentifier,
|
|
5636
5669
|
agentName,
|
|
@@ -5639,7 +5672,8 @@ async function handleToolCall(name, args, deps) {
|
|
|
5639
5672
|
currentTask: args.currentTask,
|
|
5640
5673
|
blockers: args.blockers,
|
|
5641
5674
|
estimatedMinutesRemaining: args.estimatedMinutesRemaining,
|
|
5642
|
-
...mergedRecentActions && { recentActions: mergedRecentActions }
|
|
5675
|
+
...mergedRecentActions && { recentActions: mergedRecentActions },
|
|
5676
|
+
...runActivity.length > 0 && { runActivity }
|
|
5643
5677
|
});
|
|
5644
5678
|
return { success: true, midSessionLearnings: 0, ...result };
|
|
5645
5679
|
}
|
|
@@ -5695,6 +5729,13 @@ async function handleToolCall(name, args, deps) {
|
|
|
5695
5729
|
});
|
|
5696
5730
|
return { success: true, ...result };
|
|
5697
5731
|
}
|
|
5732
|
+
case "harmony_get_pending_messages": {
|
|
5733
|
+
const cardId = z.string().uuid().parse(args.cardId);
|
|
5734
|
+
const sessionId = z.string().min(1).parse(args.sessionId);
|
|
5735
|
+
const sinceSeq = args.sinceSeq !== undefined ? z.number().int().min(0).parse(args.sinceSeq) : 0;
|
|
5736
|
+
const result = await client3.getPendingMessages(cardId, sessionId, sinceSeq);
|
|
5737
|
+
return { success: true, ...result };
|
|
5738
|
+
}
|
|
5698
5739
|
case "harmony_generate_prompt": {
|
|
5699
5740
|
let cardId;
|
|
5700
5741
|
if (args.cardId) {
|
package/dist/lib/api-client.js
CHANGED
|
@@ -1361,6 +1361,12 @@ class HarmonyApiClient {
|
|
|
1361
1361
|
async startAgentSession(cardId, data) {
|
|
1362
1362
|
return this.request("POST", `/cards/${cardId}/agent-context`, data);
|
|
1363
1363
|
}
|
|
1364
|
+
async getPendingMessages(cardId, sessionId, sinceSeq) {
|
|
1365
|
+
const params = new URLSearchParams;
|
|
1366
|
+
params.set("sessionId", sessionId);
|
|
1367
|
+
params.set("sinceSeq", String(sinceSeq));
|
|
1368
|
+
return this.request("GET", `/cards/${cardId}/agent-messages?${params.toString()}`);
|
|
1369
|
+
}
|
|
1364
1370
|
async updateAgentProgress(cardId, data) {
|
|
1365
1371
|
return this.request("POST", `/cards/${cardId}/agent-context`, data);
|
|
1366
1372
|
}
|
package/package.json
CHANGED
package/src/api-client.ts
CHANGED
|
@@ -792,6 +792,8 @@ export class HarmonyApiClient {
|
|
|
792
792
|
agentIdentifier: string;
|
|
793
793
|
agentName: string;
|
|
794
794
|
agentId?: string | null;
|
|
795
|
+
/** Advertise that this session polls for queued steering messages (card #473). */
|
|
796
|
+
steerable?: boolean;
|
|
795
797
|
status?: "working" | "blocked" | "paused" | "completed";
|
|
796
798
|
progressPercent?: number;
|
|
797
799
|
currentTask?: string;
|
|
@@ -802,6 +804,26 @@ export class HarmonyApiClient {
|
|
|
802
804
|
return this.request("POST", `/cards/${cardId}/agent-context`, data);
|
|
803
805
|
}
|
|
804
806
|
|
|
807
|
+
/**
|
|
808
|
+
* Drain queued `user_message` steering events for a session (card #473).
|
|
809
|
+
* Wraps the existing `GET /cards/{id}/agent-messages` endpoint that the daemon
|
|
810
|
+
* already polls — exposed so MCP/human sessions (e.g. /hmy) can consume steering
|
|
811
|
+
* at their checkpoints. Returns messages with `seq > sinceSeq`, oldest first.
|
|
812
|
+
*/
|
|
813
|
+
async getPendingMessages(
|
|
814
|
+
cardId: string,
|
|
815
|
+
sessionId: string,
|
|
816
|
+
sinceSeq: number,
|
|
817
|
+
): Promise<{ messages: unknown[] }> {
|
|
818
|
+
const params = new URLSearchParams();
|
|
819
|
+
params.set("sessionId", sessionId);
|
|
820
|
+
params.set("sinceSeq", String(sinceSeq));
|
|
821
|
+
return this.request(
|
|
822
|
+
"GET",
|
|
823
|
+
`/cards/${cardId}/agent-messages?${params.toString()}`,
|
|
824
|
+
);
|
|
825
|
+
}
|
|
826
|
+
|
|
805
827
|
async updateAgentProgress(
|
|
806
828
|
cardId: string,
|
|
807
829
|
data: {
|
|
@@ -828,6 +850,8 @@ export class HarmonyApiClient {
|
|
|
828
850
|
modelName?: string;
|
|
829
851
|
numTurns?: number;
|
|
830
852
|
recentActions?: { action: string; ts: string }[];
|
|
853
|
+
/** Per-call activity descriptions → one assistant_text timeline event each (card #470). */
|
|
854
|
+
runActivity?: string[];
|
|
831
855
|
failureReason?:
|
|
832
856
|
| "verification"
|
|
833
857
|
| "review"
|
package/src/server.ts
CHANGED
|
@@ -1002,6 +1002,11 @@ export const TOOLS = {
|
|
|
1002
1002
|
description:
|
|
1003
1003
|
'Label names to add to card (e.g., ["agent"]). Case-insensitive match.',
|
|
1004
1004
|
},
|
|
1005
|
+
steerable: {
|
|
1006
|
+
type: "boolean",
|
|
1007
|
+
description:
|
|
1008
|
+
"Set true only if this session will poll harmony_get_pending_messages at its checkpoints. Enables the live steering composer for the run; leave unset/false if you won't consume steering messages.",
|
|
1009
|
+
},
|
|
1005
1010
|
},
|
|
1006
1011
|
required: ["cardId", "agentIdentifier", "agentName"],
|
|
1007
1012
|
},
|
|
@@ -1097,6 +1102,30 @@ export const TOOLS = {
|
|
|
1097
1102
|
required: ["cardId"],
|
|
1098
1103
|
},
|
|
1099
1104
|
},
|
|
1105
|
+
harmony_get_pending_messages: {
|
|
1106
|
+
description:
|
|
1107
|
+
"Drain queued steering messages a teammate sent to your live agent session (card #473). Call at your progress checkpoints with the session id from harmony_start_agent_session and the highest seq you've already consumed; returns user messages with seq > sinceSeq, oldest first. Fold them into your next step and advance sinceSeq to the largest returned seq so each is handled exactly once.",
|
|
1108
|
+
inputSchema: {
|
|
1109
|
+
type: "object",
|
|
1110
|
+
properties: {
|
|
1111
|
+
cardId: {
|
|
1112
|
+
type: "string",
|
|
1113
|
+
description: "Card ID with the live session",
|
|
1114
|
+
},
|
|
1115
|
+
sessionId: {
|
|
1116
|
+
type: "string",
|
|
1117
|
+
description:
|
|
1118
|
+
"Agent session id (the `session.id` returned by harmony_start_agent_session)",
|
|
1119
|
+
},
|
|
1120
|
+
sinceSeq: {
|
|
1121
|
+
type: "number",
|
|
1122
|
+
description:
|
|
1123
|
+
"Return only messages with seq greater than this (the last seq you consumed). Defaults to 0 to fetch all.",
|
|
1124
|
+
},
|
|
1125
|
+
},
|
|
1126
|
+
required: ["cardId", "sessionId"],
|
|
1127
|
+
},
|
|
1128
|
+
},
|
|
1100
1129
|
|
|
1101
1130
|
// Prompt generation
|
|
1102
1131
|
harmony_generate_prompt: {
|
|
@@ -2287,7 +2316,11 @@ async function handleToolCall(
|
|
|
2287
2316
|
"Pass at most one of assigneeId / agentId — a card is assigned to a human OR an agent, never both.",
|
|
2288
2317
|
);
|
|
2289
2318
|
}
|
|
2290
|
-
//
|
|
2319
|
+
// Send only the side being set. The MCP `agentId` arg maps to the
|
|
2320
|
+
// backend's `assignedAgentId` field (DB column `assigned_agent_id`);
|
|
2321
|
+
// `assigneeId` maps straight through. When one side is set to a non-null
|
|
2322
|
+
// value the backend clears the other, so we never send both — that keeps
|
|
2323
|
+
// a card from ending up assigned to a human AND an agent at once.
|
|
2291
2324
|
const updates: {
|
|
2292
2325
|
assigneeId?: string | null;
|
|
2293
2326
|
assignedAgentId?: string | null;
|
|
@@ -2832,6 +2865,10 @@ async function handleToolCall(
|
|
|
2832
2865
|
estimatedMinutesRemaining: args.estimatedMinutesRemaining as
|
|
2833
2866
|
| number
|
|
2834
2867
|
| undefined,
|
|
2868
|
+
steerable:
|
|
2869
|
+
args.steerable === true || args.steerable === "true"
|
|
2870
|
+
? true
|
|
2871
|
+
: undefined,
|
|
2835
2872
|
});
|
|
2836
2873
|
|
|
2837
2874
|
// Mark as explicit so auto-session won't interfere
|
|
@@ -2892,6 +2929,13 @@ async function handleToolCall(
|
|
|
2892
2929
|
mergedRecentActions = callerRecentActions;
|
|
2893
2930
|
}
|
|
2894
2931
|
|
|
2932
|
+
// Per-call deltas for the run timeline. Sourced from THIS call's `actions`
|
|
2933
|
+
// only (not the cumulative `recentActions` cache), so each lands as exactly
|
|
2934
|
+
// one assistant_text event without duplication. (card #470)
|
|
2935
|
+
const runActivity = (callerActions || [])
|
|
2936
|
+
.map((a) => a.description)
|
|
2937
|
+
.filter((d): d is string => typeof d === "string" && d.length > 0);
|
|
2938
|
+
|
|
2895
2939
|
const result = await client.updateAgentProgress(cardId, {
|
|
2896
2940
|
agentIdentifier,
|
|
2897
2941
|
agentName,
|
|
@@ -2908,6 +2952,7 @@ async function handleToolCall(
|
|
|
2908
2952
|
| number
|
|
2909
2953
|
| undefined,
|
|
2910
2954
|
...(mergedRecentActions && { recentActions: mergedRecentActions }),
|
|
2955
|
+
...(runActivity.length > 0 && { runActivity }),
|
|
2911
2956
|
});
|
|
2912
2957
|
|
|
2913
2958
|
// Phase 0 (memory architecture v2): mid-session learning extraction removed.
|
|
@@ -3004,6 +3049,21 @@ async function handleToolCall(
|
|
|
3004
3049
|
return { success: true, ...result };
|
|
3005
3050
|
}
|
|
3006
3051
|
|
|
3052
|
+
case "harmony_get_pending_messages": {
|
|
3053
|
+
const cardId = z.string().uuid().parse(args.cardId);
|
|
3054
|
+
const sessionId = z.string().min(1).parse(args.sessionId);
|
|
3055
|
+
const sinceSeq =
|
|
3056
|
+
args.sinceSeq !== undefined
|
|
3057
|
+
? z.number().int().min(0).parse(args.sinceSeq)
|
|
3058
|
+
: 0;
|
|
3059
|
+
const result = await client.getPendingMessages(
|
|
3060
|
+
cardId,
|
|
3061
|
+
sessionId,
|
|
3062
|
+
sinceSeq,
|
|
3063
|
+
);
|
|
3064
|
+
return { success: true, ...result };
|
|
3065
|
+
}
|
|
3066
|
+
|
|
3007
3067
|
// Prompt generation
|
|
3008
3068
|
// TODO Phase 1: rebuild full context assembly per docs/superpowers/plans/2026-05-07-memory-architecture-v2.md §10
|
|
3009
3069
|
case "harmony_generate_prompt": {
|