@gethmy/mcp 2.13.4 → 2.15.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
@@ -1457,6 +1457,8 @@ function serializeCommentThread(comments, options = {}) {
1457
1457
  const tags = [];
1458
1458
  if (c.edited_at)
1459
1459
  tags.push("edited");
1460
+ if (c.reply_to_id)
1461
+ tags.push(`reply to ${ref(c.reply_to_id)}`);
1460
1462
  if (c.supersedes_id)
1461
1463
  tags.push(`supersedes ${ref(c.supersedes_id)}`);
1462
1464
  if (c.confirms_id)
@@ -1791,6 +1793,33 @@ class HarmonyApiClient {
1791
1793
  const query = params.toString() ? `?${params.toString()}` : "";
1792
1794
  return this.request("GET", `/board/${projectId}${query}`);
1793
1795
  }
1796
+ async getFullBoard(projectId, options) {
1797
+ const { pageSize = 200, ...boardOpts } = options ?? {};
1798
+ const cards = [];
1799
+ let offset = 0;
1800
+ let last = null;
1801
+ for (;; ) {
1802
+ const page = await this.getBoard(projectId, {
1803
+ ...boardOpts,
1804
+ limit: pageSize,
1805
+ offset
1806
+ });
1807
+ last = page;
1808
+ const pageCards = page.cards ?? [];
1809
+ cards.push(...pageCards);
1810
+ const hasMore = page.pagination?.hasMore ?? false;
1811
+ if (!hasMore || pageCards.length === 0)
1812
+ break;
1813
+ offset += pageSize;
1814
+ }
1815
+ return {
1816
+ project: last?.project,
1817
+ columns: last?.columns ?? [],
1818
+ cards,
1819
+ labels: last?.labels ?? [],
1820
+ totalCards: last?.pagination?.totalCards ?? cards.length
1821
+ };
1822
+ }
1794
1823
  async createCard(projectId, data) {
1795
1824
  return this.request("POST", "/cards", { projectId, ...data });
1796
1825
  }
@@ -1868,6 +1897,12 @@ class HarmonyApiClient {
1868
1897
  async getCardExternalLinks(cardId) {
1869
1898
  return this.request("GET", `/cards/${cardId}/external-links`);
1870
1899
  }
1900
+ async addExternalLink(cardId, url, title) {
1901
+ return this.request("POST", `/cards/${cardId}/external-links`, {
1902
+ url,
1903
+ title
1904
+ });
1905
+ }
1871
1906
  async uploadArtifact(data) {
1872
1907
  return this.request("POST", "/artifacts", data);
1873
1908
  }
@@ -1919,6 +1954,7 @@ class HarmonyApiClient {
1919
1954
  commentType: opts?.commentType,
1920
1955
  supersedesId: opts?.supersedesId,
1921
1956
  confirmsId: opts?.confirmsId,
1957
+ replyToId: opts?.replyToId,
1922
1958
  agentSessionId: opts?.agentSessionId
1923
1959
  });
1924
1960
  }
@@ -2371,12 +2407,6 @@ ${planContent.trim()}`;
2371
2407
  async updatePlaybook(playbookId, updates) {
2372
2408
  return this.request("PATCH", `/playbooks/${playbookId}`, updates);
2373
2409
  }
2374
- async runPlaybook(playbookId) {
2375
- return this.request("POST", `/playbooks/${playbookId}/run`);
2376
- }
2377
- async savePlaybookFromCard(data) {
2378
- return this.request("POST", "/playbooks/from-card", data);
2379
- }
2380
2410
  }
2381
2411
  var _promptModules = null;
2382
2412
  async function loadPromptModules() {
@@ -2457,7 +2487,7 @@ function initAutoSession(callback, getClient2, getClientInfo, scopeId = DEFAULT_
2457
2487
  scope.clientGetter = getClient2;
2458
2488
  scope.clientInfoGetter = getClientInfo ?? null;
2459
2489
  if (!inactivityTimer) {
2460
- inactivityTimer = setInterval(checkInactivity, CHECK_INTERVAL_MS);
2490
+ inactivityTimer = setInterval(sweepTick, CHECK_INTERVAL_MS);
2461
2491
  }
2462
2492
  }
2463
2493
  async function trackActivity(cardId, options) {
@@ -2499,7 +2529,8 @@ async function trackActivity(cardId, options) {
2499
2529
  lastActivityAt: now,
2500
2530
  isExplicit: false,
2501
2531
  agentIdentifier,
2502
- agentName
2532
+ agentName,
2533
+ status: "working"
2503
2534
  });
2504
2535
  }
2505
2536
  function markExplicit(cardId, options) {
@@ -2518,7 +2549,8 @@ function markExplicit(cardId, options) {
2518
2549
  lastActivityAt: Date.now(),
2519
2550
  isExplicit: true,
2520
2551
  agentIdentifier: options?.agentIdentifier ?? "explicit",
2521
- agentName: options?.agentName ?? "Explicit Agent"
2552
+ agentName: options?.agentName ?? "Explicit Agent",
2553
+ status: "working"
2522
2554
  });
2523
2555
  }
2524
2556
  }
@@ -2564,6 +2596,31 @@ function checkInactivity() {
2564
2596
  }
2565
2597
  }
2566
2598
  }
2599
+ function noteSessionStatus(cardId, status, scopeId = DEFAULT_SCOPE) {
2600
+ const session = scopes.get(scopeId)?.sessions.get(cardId);
2601
+ if (session)
2602
+ session.status = status;
2603
+ }
2604
+ function heartbeatActiveSessions() {
2605
+ for (const scope of scopes.values()) {
2606
+ const client3 = scope.clientGetter?.();
2607
+ if (!client3)
2608
+ continue;
2609
+ for (const session of scope.sessions.values()) {
2610
+ if ((session.status ?? "working") !== "working")
2611
+ continue;
2612
+ client3.updateAgentProgress(session.cardId, {
2613
+ agentIdentifier: session.agentIdentifier,
2614
+ agentName: session.agentName,
2615
+ status: "working"
2616
+ }).catch(() => {});
2617
+ }
2618
+ }
2619
+ }
2620
+ function sweepTick() {
2621
+ checkInactivity();
2622
+ heartbeatActiveSessions();
2623
+ }
2567
2624
  async function autoEndSession(scope, client3, cardId, status) {
2568
2625
  if (!scope.sessions.delete(cardId))
2569
2626
  return;
@@ -3240,7 +3297,7 @@ Start work on a Harmony card. Card reference: $ARGUMENTS
3240
3297
  ## 1. Find & Fetch Card
3241
3298
 
3242
3299
  Parse the reference and fetch the card:
3243
- - \`#42\` or \`42\` → \`harmony_get_card_by_short_id\` with \`shortId: 42\`
3300
+ - \`#42\` or \`42\` → \`harmony_get_card\` with \`shortId: 42\`
3244
3301
  - UUID → \`harmony_get_card\` with \`cardId\`
3245
3302
  - Name/text → \`harmony_search_cards\` with \`query\`
3246
3303
 
@@ -3294,7 +3351,7 @@ If pausing: \`harmony_end_agent_session\` with \`status: "paused"\`
3294
3351
 
3295
3352
  ## Key Tools Reference
3296
3353
 
3297
- **Cards:** \`harmony_get_card\`, \`harmony_get_card_by_short_id\`, \`harmony_search_cards\`, \`harmony_create_card\`, \`harmony_update_card\`, \`harmony_move_card\`, \`harmony_delete_card\`, \`harmony_assign_card\`
3354
+ **Cards:** \`harmony_get_card\` (by \`cardId\`, \`shortId\`, or \`shortIds\`), \`harmony_search_cards\`, \`harmony_create_card\`, \`harmony_update_card\`, \`harmony_move_card\`, \`harmony_delete_card\`, \`harmony_assign_card\`
3298
3355
 
3299
3356
  **Subtasks:** \`harmony_create_subtask\`, \`harmony_toggle_subtask\`, \`harmony_delete_subtask\`
3300
3357
 
@@ -3488,6 +3545,37 @@ function requireExactlyOneScope(scope) {
3488
3545
  throw new Error("Provide exactly one of cardId, planId, or workspaceId.");
3489
3546
  }
3490
3547
  }
3548
+ var DEPRECATED_TOOL_ALIASES = {
3549
+ harmony_get_card_by_short_id: "harmony_get_card",
3550
+ harmony_bulk_get_cards: "harmony_get_card",
3551
+ harmony_upload_artifact: "harmony_upload",
3552
+ harmony_upload_card_attachment: "harmony_upload",
3553
+ harmony_request_artifact_upload_url: "harmony_request_upload_url",
3554
+ harmony_request_card_attachment_upload_url: "harmony_request_upload_url",
3555
+ harmony_finalize_artifact: "harmony_finalize_upload",
3556
+ harmony_finalize_card_attachment: "harmony_finalize_upload"
3557
+ };
3558
+ var _warnedDeprecatedTools = new Set;
3559
+ function warnDeprecatedTool(oldName) {
3560
+ if (_warnedDeprecatedTools.has(oldName))
3561
+ return;
3562
+ _warnedDeprecatedTools.add(oldName);
3563
+ const replacement = DEPRECATED_TOOL_ALIASES[oldName];
3564
+ console.error(`[harmony-mcp] Tool "${oldName}" is deprecated (still accepted) — ` + `use "${replacement}" instead. The old name is no longer advertised and ` + `will be removed in a future major version.`);
3565
+ }
3566
+ var DEPRECATED_REMOVED_TOOLS = {
3567
+ harmony_run_playbook: "Playbooks are stage models run by the board and agent daemon as a card moves through their stages — there is no server-side macro to trigger. This tool no longer runs anything.",
3568
+ harmony_save_card_as_playbook: "The steps_version=1 automation macro was removed. Build a stage playbook with harmony_create_playbook instead."
3569
+ };
3570
+ var _warnedRemovedTools = new Set;
3571
+ function deprecatedRemovedToolResult(name) {
3572
+ const message = DEPRECATED_REMOVED_TOOLS[name];
3573
+ if (!_warnedRemovedTools.has(name)) {
3574
+ _warnedRemovedTools.add(name);
3575
+ console.error(`[harmony-mcp] Tool "${name}" is deprecated and no longer functional — ${message}`);
3576
+ }
3577
+ return { success: false, deprecated: true, message };
3578
+ }
3491
3579
  var SIGNED_UPLOAD_TIMEOUT_MS = 30000;
3492
3580
  async function putToSignedUrl(uploadUrl, bytes, contentType) {
3493
3581
  let res;
@@ -3815,44 +3903,29 @@ var TOOLS = {
3815
3903
  }
3816
3904
  },
3817
3905
  harmony_get_card: {
3818
- description: "Get detailed information about a specific card by UUID",
3819
- inputSchema: {
3820
- type: "object",
3821
- properties: {
3822
- cardId: { type: "string", description: "Card UUID" }
3823
- },
3824
- required: ["cardId"]
3825
- }
3826
- },
3827
- harmony_get_card_by_short_id: {
3828
- description: "Get a card by its short ID (e.g., #42) within a project",
3906
+ description: "Fetch one or many cards. Provide exactly one of: `cardId` (UUID, full detail), " + "`shortId` (e.g. 42, full detail), or `shortIds` (array, max 100 — compact " + "summaries: id, shortId, title, column, priority, assignee, labels, archived, " + "plus any short ids not found). `shortId`/`shortIds` need project context. " + "Prefer one `shortIds` call over repeated single fetches for multiple cards.",
3829
3907
  inputSchema: {
3830
3908
  type: "object",
3831
3909
  properties: {
3832
- projectId: {
3910
+ cardId: {
3833
3911
  type: "string",
3834
- description: "Project ID (optional if context set)"
3912
+ description: "Card UUID full detail for one card."
3835
3913
  },
3836
3914
  shortId: {
3837
3915
  type: "number",
3838
- description: "Short ID number (e.g., 42 for card #42)"
3839
- }
3840
- },
3841
- required: ["shortId"]
3842
- }
3843
- },
3844
- harmony_bulk_get_cards: {
3845
- description: "Fetch multiple cards by short id in one call. Returns compact summaries " + "(id, shortId, title, column, priority, assignee, labels, archived) plus " + "any short ids not found. Requires project context. Prefer this over " + "repeated harmony_get_card_by_short_id when referencing multiple cards.",
3846
- inputSchema: {
3847
- type: "object",
3848
- properties: {
3916
+ description: "Short ID (e.g. 42 for card #42) — full detail, project-scoped."
3917
+ },
3849
3918
  shortIds: {
3850
3919
  type: "array",
3851
3920
  items: { type: "number" },
3852
- description: "Card short ids, e.g. [400, 401, 402]. Max 100 per call."
3921
+ description: "Short IDs, e.g. [400, 401, 402] compact summaries in one call. Max 100."
3922
+ },
3923
+ projectId: {
3924
+ type: "string",
3925
+ description: "Project ID for shortId/shortIds (optional if context set)."
3853
3926
  }
3854
3927
  },
3855
- required: ["shortIds"]
3928
+ required: []
3856
3929
  }
3857
3930
  },
3858
3931
  harmony_bulk_archive_cards: {
@@ -4007,174 +4080,144 @@ var TOOLS = {
4007
4080
  required: ["cardId"]
4008
4081
  }
4009
4082
  },
4010
- harmony_upload_card_attachment: {
4011
- description: "Upload a file attachment to a card. Provide `filePath` (local path the server reads, direct-to-storage — local/stdio mode) or `base64Data` (works everywhere, small-file fallback). Max 5MB; allowed: PNG, JPEG, GIF, WebP, HEIC/HEIF, PDF, DOC/DOCX, XLS/XLSX, TXT, CSV. Returns the attachment + a signed URL. Large files on the hosted MCP server: use harmony_request_card_attachment_upload_url + harmony_finalize_card_attachment instead.",
4083
+ harmony_upload: {
4084
+ description: 'Upload a file in one call. `target: "card_attachment"` attaches a file to a card ' + "(max 5MB; PNG/JPEG/GIF/WebP/HEIC/HEIF/PDF/DOC(X)/XLS(X)/TXT/CSV) — requires cardId. " + '`target: "artifact"` hosts a self-contained HTML doc (text/html, max 2MB) linked to ' + "exactly one of cardId/planId/workspaceId, rendered in-app in a sandboxed iframe. Provide " + "the bytes as `filePath` (local, direct-to-storage) or `base64Data` (small-file fallback). " + "Returns the attachment/artifact + a signed URL (artifact: use harmony_share_artifact for a " + "public link). Large files on the hosted MCP server: use harmony_request_upload_url + " + "harmony_finalize_upload instead.",
4012
4085
  inputSchema: {
4013
4086
  type: "object",
4014
4087
  properties: {
4015
- cardId: { type: "string", description: "Card UUID" },
4088
+ target: {
4089
+ type: "string",
4090
+ enum: ["card_attachment", "artifact"],
4091
+ description: "What to upload: a card file attachment or a hosted HTML artifact."
4092
+ },
4093
+ cardId: {
4094
+ type: "string",
4095
+ description: "Card UUID. Required for card_attachment; one of cardId/planId/workspaceId for artifact."
4096
+ },
4097
+ planId: {
4098
+ type: "string",
4099
+ description: "Plan UUID (artifact only — link the artifact to a plan)."
4100
+ },
4101
+ workspaceId: {
4102
+ type: "string",
4103
+ description: "Workspace UUID (artifact only — standalone artifact)."
4104
+ },
4016
4105
  filePath: {
4017
4106
  type: "string",
4018
- description: "Absolute path to a local file the MCP server process can read. Mutually exclusive with base64Data."
4107
+ description: "Absolute path to a local file the server can read. Mutually exclusive with base64Data."
4019
4108
  },
4020
4109
  base64Data: {
4021
4110
  type: "string",
4022
- description: "Base64-encoded file bytes (a `data:` URL prefix is accepted and stripped). Requires fileName. Mutually exclusive with filePath."
4111
+ description: "Base64-encoded bytes (a `data:` URL prefix is accepted and stripped). Mutually exclusive with filePath."
4023
4112
  },
4024
4113
  fileName: {
4025
4114
  type: "string",
4026
- description: "File name including extension (e.g. 'screenshot.png'). Required with base64Data; defaults to the basename of filePath otherwise."
4115
+ description: "File name including extension (card_attachment: required with base64Data, else defaults to the filePath basename)."
4116
+ },
4117
+ title: {
4118
+ type: "string",
4119
+ description: "Artifact display title (defaults to the file basename)."
4027
4120
  },
4028
4121
  contentType: {
4029
4122
  type: "string",
4030
- description: "Optional MIME type (e.g. 'image/png'). Inferred from the file extension when omitted."
4123
+ description: "Optional MIME type (card_attachment; inferred from the extension when omitted)."
4031
4124
  }
4032
4125
  },
4033
- required: ["cardId"]
4126
+ required: ["target"]
4034
4127
  }
4035
4128
  },
4036
- harmony_request_card_attachment_upload_url: {
4037
- description: "Step 1 of the large-file / hosted-MCP upload handshake (use when the server can't read local disk). Mints a one-shot signed Storage upload URL under the card. Returns { uploadUrl, token, storagePath, fileType }. Then PUT the raw bytes to uploadUrl (no bytes through the model context) and call harmony_finalize_card_attachment with storagePath. Max 5MB; same allowed types as harmony_upload_card_attachment.",
4129
+ harmony_request_upload_url: {
4130
+ description: "Step 1 of the large-file / hosted-MCP upload handshake (use when the server can't read " + 'local disk). Mints a one-shot signed Storage upload URL. `target: "card_attachment"` ' + '(requires cardId, fileName, size; max 5MB) or `target: "artifact"` (one of ' + "cardId/planId/workspaceId; text/html, max 2MB). Returns { uploadUrl, token, storagePath, " + "[fileType] }. Then PUT the raw bytes to uploadUrl (no bytes through the model context) and " + "call harmony_finalize_upload with the storagePath.",
4038
4131
  inputSchema: {
4039
4132
  type: "object",
4040
4133
  properties: {
4041
- cardId: { type: "string", description: "Card UUID" },
4042
- fileName: {
4134
+ target: {
4043
4135
  type: "string",
4044
- description: "File name including extension (e.g. 'screenshot.png')."
4136
+ enum: ["card_attachment", "artifact"],
4137
+ description: "What to upload: a card file attachment or a hosted HTML artifact."
4045
4138
  },
4046
- fileType: {
4139
+ cardId: {
4047
4140
  type: "string",
4048
- description: "Optional MIME type (e.g. 'image/png'). Inferred from the file extension when omitted."
4141
+ description: "Card UUID. Required for card_attachment; one of cardId/planId/workspaceId for artifact."
4049
4142
  },
4050
- size: {
4051
- type: "number",
4052
- description: "File size in bytes (rejected early if over 5MB)."
4053
- }
4054
- },
4055
- required: ["cardId", "fileName", "size"]
4056
- }
4057
- },
4058
- harmony_finalize_card_attachment: {
4059
- description: "Step 2 of the upload handshake. After PUTting the bytes to the signed uploadUrl, call this with storagePath to validate and register the attachment. The server re-downloads the object and enforces size + an allowlisted content-type (magic-byte sniff, never the declared type) + an optional sha256 integrity check, deleting and failing on any mismatch. Returns the attachment + a signed URL.",
4060
- inputSchema: {
4061
- type: "object",
4062
- properties: {
4063
- cardId: { type: "string", description: "Card UUID" },
4064
- storagePath: {
4143
+ planId: { type: "string", description: "Plan UUID (artifact only)." },
4144
+ workspaceId: {
4065
4145
  type: "string",
4066
- description: "The storagePath returned by harmony_request_card_attachment_upload_url."
4146
+ description: "Workspace UUID (artifact only)."
4067
4147
  },
4068
4148
  fileName: {
4069
4149
  type: "string",
4070
- description: "File name including extension (e.g. 'screenshot.png')."
4150
+ description: "File name including extension (required for card_attachment)."
4071
4151
  },
4152
+ title: { type: "string", description: "Artifact display title." },
4072
4153
  fileType: {
4073
4154
  type: "string",
4074
- description: "Optional MIME type; inferred from the extension when omitted."
4155
+ description: "card_attachment MIME type (inferred from the extension when omitted)."
4075
4156
  },
4076
- sha256: {
4157
+ contentType: {
4077
4158
  type: "string",
4078
- description: "Optional hex SHA-256 of the uploaded bytes; verified against the stored object."
4159
+ description: "artifact MIME type; only 'text/html' is accepted (the default)."
4079
4160
  },
4080
4161
  size: {
4081
4162
  type: "number",
4082
- description: "Optional byte size (advisory; re-validated server-side)."
4163
+ description: "File size in bytes (rejected early if over the target's limit)."
4083
4164
  }
4084
4165
  },
4085
- required: ["cardId", "storagePath", "fileName"]
4166
+ required: ["target"]
4086
4167
  }
4087
4168
  },
4088
- harmony_classify_card: {
4089
- description: "Classify a card with the LLM classifier: 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). Call right after creating a card to classify it in-flow. Idempotent; never touches the user-owned `model_override`.",
4169
+ harmony_finalize_upload: {
4170
+ description: "Step 2 of the upload handshake. After PUTting the bytes to the signed uploadUrl, call this " + "with the storagePath to validate and register. The server re-downloads the object and " + "enforces size + content-type (magic-byte sniff, never the declared type) + an optional " + 'sha256 integrity check, deleting and failing on any mismatch. `target: "card_attachment"` ' + '(cardId, storagePath, fileName) or `target: "artifact"` (storagePath + the same one of ' + "cardId/planId/workspaceId used for the upload URL). Returns the attachment/artifact + a signed URL.",
4090
4171
  inputSchema: {
4091
4172
  type: "object",
4092
4173
  properties: {
4093
- cardId: { type: "string", description: "Card UUID" }
4094
- },
4095
- required: ["cardId"]
4096
- }
4097
- },
4098
- harmony_upload_artifact: {
4099
- description: "Host a self-contained HTML document (design draft, diagram) and link it to a card, plan, or workspace; rendered in-app in a sandboxed iframe. Provide exactly one of cardId/planId/workspaceId, and the HTML as `filePath` (local, direct-to-storage) or `base64Data` (small-file fallback). text/html only, max 2MB. Returns the artifact + a signed URL; use harmony_share_artifact for a public link. Large files on the hosted MCP server: use harmony_request_artifact_upload_url + harmony_finalize_artifact instead.",
4100
- inputSchema: {
4101
- type: "object",
4102
- properties: {
4103
- title: {
4174
+ target: {
4104
4175
  type: "string",
4105
- description: "Display title (defaults to the file basename)."
4176
+ enum: ["card_attachment", "artifact"],
4177
+ description: "What to finalize: a card file attachment or a hosted HTML artifact."
4106
4178
  },
4107
- cardId: { type: "string", description: "Link to this card (UUID)." },
4108
- planId: { type: "string", description: "Link to this plan (UUID)." },
4109
- workspaceId: {
4179
+ storagePath: {
4110
4180
  type: "string",
4111
- description: "Attach to this workspace as a standalone artifact (UUID)."
4181
+ description: "The storagePath returned by harmony_request_upload_url."
4112
4182
  },
4113
- filePath: {
4183
+ cardId: {
4114
4184
  type: "string",
4115
- description: "Absolute path to a local .html file the MCP server process can read. Mutually exclusive with base64Data."
4185
+ description: "Card UUID. Required for card_attachment; scope for artifact."
4116
4186
  },
4117
- base64Data: {
4187
+ planId: { type: "string", description: "Plan UUID (artifact only)." },
4188
+ workspaceId: {
4118
4189
  type: "string",
4119
- description: "Base64-encoded HTML bytes (a `data:` URL prefix is accepted and stripped). Mutually exclusive with filePath."
4120
- }
4121
- },
4122
- required: []
4123
- }
4124
- },
4125
- harmony_request_artifact_upload_url: {
4126
- description: "Step 1 of the large-file / hosted-MCP artifact upload handshake (use when the server can't read local disk). Mints a one-shot signed Storage upload URL. Provide exactly one of cardId/planId/workspaceId. Returns { uploadUrl, token, storagePath }. Then PUT the HTML bytes to uploadUrl and call harmony_finalize_artifact with storagePath. text/html only, max 2MB.",
4127
- inputSchema: {
4128
- type: "object",
4129
- properties: {
4130
- title: {
4190
+ description: "Workspace UUID (artifact only)."
4191
+ },
4192
+ fileName: {
4131
4193
  type: "string",
4132
- description: "Display title (defaults to the file basename at finalize)."
4194
+ description: "File name including extension (required for card_attachment)."
4133
4195
  },
4134
- cardId: { type: "string", description: "Link to this card (UUID)." },
4135
- planId: { type: "string", description: "Link to this plan (UUID)." },
4136
- workspaceId: {
4196
+ title: { type: "string", description: "Artifact display title." },
4197
+ fileType: {
4137
4198
  type: "string",
4138
- description: "Attach to this workspace as a standalone artifact (UUID)."
4199
+ description: "card_attachment MIME type; inferred from the extension when omitted."
4139
4200
  },
4140
- contentType: {
4201
+ sha256: {
4141
4202
  type: "string",
4142
- description: "MIME type; only 'text/html' is accepted (the default)."
4203
+ description: "Optional hex SHA-256 of the uploaded bytes; verified against the stored object."
4143
4204
  },
4144
4205
  size: {
4145
4206
  type: "number",
4146
- description: "File size in bytes (rejected early if over 2MB)."
4207
+ description: "Optional byte size (advisory; re-validated server-side)."
4147
4208
  }
4148
4209
  },
4149
- required: []
4210
+ required: ["target", "storagePath"]
4150
4211
  }
4151
4212
  },
4152
- harmony_finalize_artifact: {
4153
- description: "Step 2 of the artifact upload handshake. After PUTting the HTML bytes to the signed uploadUrl, call this with storagePath to validate and register the artifact. The server re-downloads the object and enforces size + text/html (magic-byte sniff) + an optional sha256 integrity check, deleting and failing on any mismatch. Pass the same one of cardId/planId/workspaceId used for the upload URL. Returns the artifact + a signed URL; use harmony_share_artifact for a public link.",
4213
+ harmony_classify_card: {
4214
+ description: "Classify a card with the LLM classifier: 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). Call right after creating a card to classify it in-flow. Idempotent; never touches the user-owned `model_override`.",
4154
4215
  inputSchema: {
4155
4216
  type: "object",
4156
4217
  properties: {
4157
- storagePath: {
4158
- type: "string",
4159
- description: "The storagePath returned by harmony_request_artifact_upload_url."
4160
- },
4161
- title: { type: "string", description: "Display title." },
4162
- cardId: { type: "string", description: "Link to this card (UUID)." },
4163
- planId: { type: "string", description: "Link to this plan (UUID)." },
4164
- workspaceId: {
4165
- type: "string",
4166
- description: "Attach to this workspace as a standalone artifact (UUID)."
4167
- },
4168
- sha256: {
4169
- type: "string",
4170
- description: "Optional hex SHA-256 of the uploaded bytes; verified against the stored object."
4171
- },
4172
- size: {
4173
- type: "number",
4174
- description: "Optional byte size (advisory; re-validated server-side)."
4175
- }
4218
+ cardId: { type: "string", description: "Card UUID" }
4176
4219
  },
4177
- required: ["storagePath"]
4220
+ required: ["cardId"]
4178
4221
  }
4179
4222
  },
4180
4223
  harmony_share_artifact: {
@@ -4204,6 +4247,27 @@ var TOOLS = {
4204
4247
  required: ["cardId"]
4205
4248
  }
4206
4249
  },
4250
+ harmony_add_external_link: {
4251
+ description: "Attach an external reference URL (e.g. a PR/MR link, doc, or dashboard) to a card. Durable — survives description edits. Use this in addition to writing the PR link into the description.",
4252
+ inputSchema: {
4253
+ type: "object",
4254
+ properties: {
4255
+ cardId: {
4256
+ type: "string",
4257
+ description: "Card UUID"
4258
+ },
4259
+ url: {
4260
+ type: "string",
4261
+ description: "The URL to attach"
4262
+ },
4263
+ title: {
4264
+ type: "string",
4265
+ description: "Optional human-readable title for the link"
4266
+ }
4267
+ },
4268
+ required: ["cardId", "url"]
4269
+ }
4270
+ },
4207
4271
  harmony_create_subtask: {
4208
4272
  description: "Create a subtask on a card",
4209
4273
  inputSchema: {
@@ -4255,7 +4319,7 @@ var TOOLS = {
4255
4319
  }
4256
4320
  },
4257
4321
  harmony_add_comment: {
4258
- description: "Post a comment on a card as the agent — converse with the human in the open: report progress, ask a question, record a decision, or note a finding, instead of editing the card description. Set supersedesId to correct an earlier comment, confirmsId to reaffirm one.",
4322
+ description: "Post a comment on a card as the agent — converse with the human in the open: report progress, ask a question, record a decision, or note a finding, instead of editing the card description. Set supersedesId to correct an earlier comment, confirmsId to reaffirm one. To answer an open question, reply to it with replyToId.",
4259
4323
  inputSchema: {
4260
4324
  type: "object",
4261
4325
  properties: {
@@ -4281,6 +4345,10 @@ var TOOLS = {
4281
4345
  confirmsId: {
4282
4346
  type: "string",
4283
4347
  description: "Comment id this comment reaffirms"
4348
+ },
4349
+ replyToId: {
4350
+ type: "string",
4351
+ description: "Comment id this is a one-level reply to. To answer an open question, reply to it. If you also set supersedesId/confirmsId, it must equal replyToId."
4284
4352
  }
4285
4353
  },
4286
4354
  required: ["cardId", "body"]
@@ -5151,7 +5219,7 @@ var TOOLS = {
5151
5219
  }
5152
5220
  },
5153
5221
  harmony_list_playbook: {
5154
- description: "List a workspace's playbooks (reusable process definitions). Returns each playbook's name, version, steps_version, and state. Read-only.",
5222
+ description: "List a workspace's playbooks (reusable process definitions). Returns each playbook's name, version, and state. Read-only.",
5155
5223
  inputSchema: {
5156
5224
  type: "object",
5157
5225
  properties: {
@@ -5173,21 +5241,8 @@ var TOOLS = {
5173
5241
  required: ["playbookId"]
5174
5242
  }
5175
5243
  },
5176
- harmony_run_playbook: {
5177
- description: "Run a playbook server-side and return the finalized run. Only legacy automation playbooks (steps_version 1) are runnable; stage playbooks (steps_version 2) are rejected. The server drives every step to completion.",
5178
- inputSchema: {
5179
- type: "object",
5180
- properties: {
5181
- playbookId: {
5182
- type: "string",
5183
- description: "Playbook ID to run (UUID)"
5184
- }
5185
- },
5186
- required: ["playbookId"]
5187
- }
5188
- },
5189
5244
  harmony_create_playbook: {
5190
- description: "Create a new playbook in a workspace. Default steps_version 1 is a legacy automation macro (an array of tool steps); steps_version 2 is the Method stage model (an array of stage objects).",
5245
+ description: "Create a new playbook (a reusable process definition) in a workspace. A playbook is an ordered set of stages bound to board columns, run by people and the agent daemon as a card moves through them.",
5191
5246
  inputSchema: {
5192
5247
  type: "object",
5193
5248
  properties: {
@@ -5197,14 +5252,9 @@ var TOOLS = {
5197
5252
  },
5198
5253
  name: { type: "string", description: "Playbook name" },
5199
5254
  description: { type: "string", description: "Playbook description" },
5200
- stepsVersion: {
5201
- type: "number",
5202
- enum: [1, 2],
5203
- description: "1 = automation macro (tool steps), 2 = Method stage model (stage objects). Default 1."
5204
- },
5205
5255
  steps: {
5206
5256
  type: "array",
5207
- description: "Steps (steps_version 1: tool-step objects) or stages (steps_version 2: stage objects).",
5257
+ description: "The playbook's ordered stage objects.",
5208
5258
  items: { type: "object" }
5209
5259
  }
5210
5260
  },
@@ -5224,7 +5274,7 @@ var TOOLS = {
5224
5274
  description: { type: "string", description: "New description" },
5225
5275
  steps: {
5226
5276
  type: "array",
5227
- description: "New steps (v1) or stages (v2) array.",
5277
+ description: "New ordered stage objects.",
5228
5278
  items: { type: "object" }
5229
5279
  },
5230
5280
  enabled: {
@@ -5240,20 +5290,6 @@ var TOOLS = {
5240
5290
  required: ["playbookId"]
5241
5291
  }
5242
5292
  },
5243
- harmony_save_card_as_playbook: {
5244
- description: "Save an existing card as a new steps_version 1 (automation) playbook, seeding one create-card step from the card. Returns the created playbook.",
5245
- inputSchema: {
5246
- type: "object",
5247
- properties: {
5248
- cardId: { type: "string", description: "Card ID to template (UUID)" },
5249
- name: {
5250
- type: "string",
5251
- description: "Name for the new playbook (defaults to the card title)"
5252
- }
5253
- },
5254
- required: ["cardId"]
5255
- }
5256
- },
5257
5293
  harmony_signup: {
5258
5294
  description: "Create a new user account. Returns a JWT session for subsequent authenticated calls. No API key required.",
5259
5295
  inputSchema: {
@@ -5653,23 +5689,33 @@ async function handleToolCall(name, args, deps) {
5653
5689
  });
5654
5690
  return { success: true, ...result, count: result.cards.length };
5655
5691
  }
5692
+ case "harmony_get_card_by_short_id":
5693
+ case "harmony_bulk_get_cards":
5656
5694
  case "harmony_get_card": {
5695
+ if (name !== "harmony_get_card")
5696
+ warnDeprecatedTool(name);
5697
+ const hasShortIds = args.shortIds != null;
5698
+ const hasShortId = args.shortId != null;
5699
+ const hasCardId = args.cardId != null;
5700
+ if ([hasShortIds, hasShortId, hasCardId].filter(Boolean).length !== 1) {
5701
+ throw new Error("Provide exactly one of: cardId (UUID), shortId (number), or shortIds (number[]).");
5702
+ }
5703
+ if (hasShortIds) {
5704
+ const shortIds = z.array(z.number().int().positive()).min(1).max(100).parse(args.shortIds);
5705
+ const projectId = getProjectId();
5706
+ const result2 = await client3.bulkGetCards(projectId, shortIds);
5707
+ return { success: true, ...result2 };
5708
+ }
5709
+ if (hasShortId) {
5710
+ const shortId = z.number().int().positive().parse(args.shortId);
5711
+ const projectId = args.projectId || getProjectId();
5712
+ const result2 = await client3.getCardByShortId(projectId, shortId);
5713
+ return { success: true, ...result2 };
5714
+ }
5657
5715
  const cardId = z.string().uuid().parse(args.cardId);
5658
5716
  const result = await client3.getCard(cardId);
5659
5717
  return { success: true, ...result };
5660
5718
  }
5661
- case "harmony_get_card_by_short_id": {
5662
- const shortId = z.number().int().positive().parse(args.shortId);
5663
- const projectId = args.projectId || getProjectId();
5664
- const result = await client3.getCardByShortId(projectId, shortId);
5665
- return { success: true, ...result };
5666
- }
5667
- case "harmony_bulk_get_cards": {
5668
- const shortIds = z.array(z.number().int().positive()).min(1).max(100).parse(args.shortIds);
5669
- const projectId = getProjectId();
5670
- const result = await client3.bulkGetCards(projectId, shortIds);
5671
- return { success: true, ...result };
5672
- }
5673
5719
  case "harmony_bulk_archive_cards": {
5674
5720
  const shortIds = z.array(z.number().int().positive()).min(1).max(100).parse(args.shortIds);
5675
5721
  const projectId = getProjectId();
@@ -5769,58 +5815,58 @@ async function handleToolCall(name, args, deps) {
5769
5815
  const result = await client3.getCardAttachments(cardId);
5770
5816
  return result;
5771
5817
  }
5772
- case "harmony_upload_card_attachment": {
5773
- const cardId = z.string().uuid().parse(args.cardId);
5818
+ case "harmony_upload_card_attachment":
5819
+ case "harmony_upload_artifact":
5820
+ case "harmony_upload": {
5821
+ if (name !== "harmony_upload")
5822
+ warnDeprecatedTool(name);
5823
+ const target = name === "harmony_upload_card_attachment" ? "card_attachment" : name === "harmony_upload_artifact" ? "artifact" : z.enum(["card_attachment", "artifact"]).parse(args.target);
5774
5824
  const filePath = args.filePath != null ? z.string().parse(args.filePath) : undefined;
5775
5825
  const base64Data = args.base64Data != null ? z.string().parse(args.base64Data) : undefined;
5776
- const fileName = args.fileName != null ? z.string().parse(args.fileName) : undefined;
5777
- const contentType = args.contentType != null ? z.string().parse(args.contentType) : undefined;
5778
5826
  if (filePath && base64Data) {
5779
5827
  throw new Error("Provide either filePath or base64Data, not both.");
5780
5828
  }
5781
- if (filePath) {
5782
- const bytes = await readFileForUpload(filePath, MAX_ATTACHMENT_SIZE, "attachment");
5783
- const resolvedName = fileName || basename(filePath);
5784
- const signed = await client3.requestCardAttachmentUploadUrl(cardId, {
5785
- fileName: resolvedName,
5786
- fileType: contentType,
5787
- size: bytes.byteLength
5788
- });
5789
- await putToSignedUrl(signed.uploadUrl, bytes, contentType || signed.fileType || "application/octet-stream");
5790
- return await client3.finalizeCardAttachment(cardId, {
5791
- storagePath: signed.storagePath,
5792
- fileName: resolvedName,
5793
- fileType: contentType || signed.fileType,
5794
- sha256: sha256Hex(bytes),
5795
- size: bytes.byteLength
5796
- });
5797
- }
5798
- if (base64Data) {
5799
- if (!fileName) {
5800
- throw new Error("fileName is required when using base64Data.");
5829
+ if (target === "card_attachment") {
5830
+ const cardId2 = z.string().uuid().parse(args.cardId);
5831
+ const fileName = args.fileName != null ? z.string().parse(args.fileName) : undefined;
5832
+ const contentType = args.contentType != null ? z.string().parse(args.contentType) : undefined;
5833
+ if (filePath) {
5834
+ const bytes = await readFileForUpload(filePath, MAX_ATTACHMENT_SIZE, "attachment");
5835
+ const resolvedName = fileName || basename(filePath);
5836
+ const signed = await client3.requestCardAttachmentUploadUrl(cardId2, {
5837
+ fileName: resolvedName,
5838
+ fileType: contentType,
5839
+ size: bytes.byteLength
5840
+ });
5841
+ await putToSignedUrl(signed.uploadUrl, bytes, contentType || signed.fileType || "application/octet-stream");
5842
+ return await client3.finalizeCardAttachment(cardId2, {
5843
+ storagePath: signed.storagePath,
5844
+ fileName: resolvedName,
5845
+ fileType: contentType || signed.fileType,
5846
+ sha256: sha256Hex(bytes),
5847
+ size: bytes.byteLength
5848
+ });
5801
5849
  }
5802
- if (base64ByteLength(base64Data) > MAX_ATTACHMENT_SIZE) {
5803
- throw new Error(`File is over the 5MB attachment limit. Use the harmony_request_card_attachment_upload_url + harmony_finalize_card_attachment handshake for large files.`);
5850
+ if (base64Data) {
5851
+ if (!fileName) {
5852
+ throw new Error("fileName is required when using base64Data.");
5853
+ }
5854
+ if (base64ByteLength(base64Data) > MAX_ATTACHMENT_SIZE) {
5855
+ throw new Error(`File is over the 5MB attachment limit. Use the harmony_request_upload_url + harmony_finalize_upload handshake for large files.`);
5856
+ }
5857
+ return await client3.uploadCardAttachment(cardId2, {
5858
+ fileName,
5859
+ data: base64Data,
5860
+ fileType: contentType
5861
+ });
5804
5862
  }
5805
- return await client3.uploadCardAttachment(cardId, {
5806
- fileName,
5807
- data: base64Data,
5808
- fileType: contentType
5809
- });
5863
+ throw new Error("Provide either filePath or base64Data.");
5810
5864
  }
5811
- throw new Error("Provide either filePath or base64Data.");
5812
- }
5813
- case "harmony_upload_artifact": {
5814
5865
  const title = args.title != null ? z.string().parse(args.title) : undefined;
5815
5866
  const cardId = args.cardId != null ? z.string().uuid().parse(args.cardId) : undefined;
5816
5867
  const planId = args.planId != null ? z.string().uuid().parse(args.planId) : undefined;
5817
5868
  const workspaceId = args.workspaceId != null ? z.string().uuid().parse(args.workspaceId) : undefined;
5818
5869
  requireExactlyOneScope({ cardId, planId, workspaceId });
5819
- const filePath = args.filePath != null ? z.string().parse(args.filePath) : undefined;
5820
- const base64Data = args.base64Data != null ? z.string().parse(args.base64Data) : undefined;
5821
- if (filePath && base64Data) {
5822
- throw new Error("Provide either filePath or base64Data, not both.");
5823
- }
5824
5870
  if (filePath) {
5825
5871
  const bytes = await readFileForUpload(filePath, MAX_ARTIFACT_SIZE, "artifact");
5826
5872
  const resolvedTitle = title || basename(filePath);
@@ -5845,7 +5891,7 @@ async function handleToolCall(name, args, deps) {
5845
5891
  }
5846
5892
  if (base64Data) {
5847
5893
  if (base64ByteLength(base64Data) > MAX_ARTIFACT_SIZE) {
5848
- throw new Error(`Artifact is over the 2MB limit. Use the harmony_request_artifact_upload_url + harmony_finalize_artifact handshake for large files.`);
5894
+ throw new Error(`Artifact is over the 2MB limit. Use the harmony_request_upload_url + harmony_finalize_upload handshake for large files.`);
5849
5895
  }
5850
5896
  return await client3.uploadArtifact({
5851
5897
  title,
@@ -5857,7 +5903,26 @@ async function handleToolCall(name, args, deps) {
5857
5903
  }
5858
5904
  throw new Error("Provide either filePath or base64Data.");
5859
5905
  }
5860
- case "harmony_request_artifact_upload_url": {
5906
+ case "harmony_request_artifact_upload_url":
5907
+ case "harmony_request_card_attachment_upload_url":
5908
+ case "harmony_request_upload_url": {
5909
+ if (name !== "harmony_request_upload_url")
5910
+ warnDeprecatedTool(name);
5911
+ const target = name === "harmony_request_card_attachment_upload_url" ? "card_attachment" : name === "harmony_request_artifact_upload_url" ? "artifact" : z.enum(["card_attachment", "artifact"]).parse(args.target);
5912
+ if (target === "card_attachment") {
5913
+ const cardId2 = z.string().uuid().parse(args.cardId);
5914
+ const fileName = z.string().parse(args.fileName);
5915
+ const fileType = args.fileType != null ? z.string().parse(args.fileType) : undefined;
5916
+ const size2 = z.number().positive().parse(args.size);
5917
+ if (size2 > MAX_ATTACHMENT_SIZE) {
5918
+ throw new Error(`Declared size ${size2} bytes is over the ${MAX_ATTACHMENT_SIZE}-byte (5MB) attachment limit.`);
5919
+ }
5920
+ return await client3.requestCardAttachmentUploadUrl(cardId2, {
5921
+ fileName,
5922
+ fileType,
5923
+ size: size2
5924
+ });
5925
+ }
5861
5926
  const title = args.title != null ? z.string().parse(args.title) : undefined;
5862
5927
  const cardId = args.cardId != null ? z.string().uuid().parse(args.cardId) : undefined;
5863
5928
  const planId = args.planId != null ? z.string().uuid().parse(args.planId) : undefined;
@@ -5877,8 +5942,27 @@ async function handleToolCall(name, args, deps) {
5877
5942
  size
5878
5943
  });
5879
5944
  }
5880
- case "harmony_finalize_artifact": {
5945
+ case "harmony_finalize_artifact":
5946
+ case "harmony_finalize_card_attachment":
5947
+ case "harmony_finalize_upload": {
5948
+ if (name !== "harmony_finalize_upload")
5949
+ warnDeprecatedTool(name);
5950
+ const target = name === "harmony_finalize_card_attachment" ? "card_attachment" : name === "harmony_finalize_artifact" ? "artifact" : z.enum(["card_attachment", "artifact"]).parse(args.target);
5881
5951
  const storagePath = z.string().parse(args.storagePath);
5952
+ if (target === "card_attachment") {
5953
+ const cardId2 = z.string().uuid().parse(args.cardId);
5954
+ const fileName = z.string().parse(args.fileName);
5955
+ const fileType = args.fileType != null ? z.string().parse(args.fileType) : undefined;
5956
+ const sha2562 = args.sha256 != null ? z.string().parse(args.sha256) : undefined;
5957
+ const size2 = args.size != null ? z.number().positive().parse(args.size) : undefined;
5958
+ return await client3.finalizeCardAttachment(cardId2, {
5959
+ storagePath,
5960
+ fileName,
5961
+ fileType,
5962
+ sha256: sha2562,
5963
+ size: size2
5964
+ });
5965
+ }
5882
5966
  const title = args.title != null ? z.string().parse(args.title) : undefined;
5883
5967
  const cardId = args.cardId != null ? z.string().uuid().parse(args.cardId) : undefined;
5884
5968
  const planId = args.planId != null ? z.string().uuid().parse(args.planId) : undefined;
@@ -5896,35 +5980,6 @@ async function handleToolCall(name, args, deps) {
5896
5980
  workspaceId
5897
5981
  });
5898
5982
  }
5899
- case "harmony_request_card_attachment_upload_url": {
5900
- const cardId = z.string().uuid().parse(args.cardId);
5901
- const fileName = z.string().parse(args.fileName);
5902
- const fileType = args.fileType != null ? z.string().parse(args.fileType) : undefined;
5903
- const size = z.number().positive().parse(args.size);
5904
- if (size > MAX_ATTACHMENT_SIZE) {
5905
- throw new Error(`Declared size ${size} bytes is over the ${MAX_ATTACHMENT_SIZE}-byte (5MB) attachment limit.`);
5906
- }
5907
- return await client3.requestCardAttachmentUploadUrl(cardId, {
5908
- fileName,
5909
- fileType,
5910
- size
5911
- });
5912
- }
5913
- case "harmony_finalize_card_attachment": {
5914
- const cardId = z.string().uuid().parse(args.cardId);
5915
- const storagePath = z.string().parse(args.storagePath);
5916
- const fileName = z.string().parse(args.fileName);
5917
- const fileType = args.fileType != null ? z.string().parse(args.fileType) : undefined;
5918
- const sha256 = args.sha256 != null ? z.string().parse(args.sha256) : undefined;
5919
- const size = args.size != null ? z.number().positive().parse(args.size) : undefined;
5920
- return await client3.finalizeCardAttachment(cardId, {
5921
- storagePath,
5922
- fileName,
5923
- fileType,
5924
- sha256,
5925
- size
5926
- });
5927
- }
5928
5983
  case "harmony_share_artifact": {
5929
5984
  const artifactId = z.string().uuid().parse(args.artifactId);
5930
5985
  const expiresInDays = args.expiresInDays != null ? z.number().positive().parse(args.expiresInDays) : undefined;
@@ -5937,6 +5992,13 @@ async function handleToolCall(name, args, deps) {
5937
5992
  const result = await client3.getCardExternalLinks(cardId);
5938
5993
  return result;
5939
5994
  }
5995
+ case "harmony_add_external_link": {
5996
+ const cardId = z.string().uuid().parse(args.cardId);
5997
+ const url = z.string().min(1).max(2048).parse(args.url);
5998
+ const title = args.title ? z.string().max(200).parse(args.title) : undefined;
5999
+ const result = await client3.addExternalLink(cardId, url, title);
6000
+ return { success: true, ...result };
6001
+ }
5940
6002
  case "harmony_classify_card": {
5941
6003
  const cardId = z.string().uuid().parse(args.cardId);
5942
6004
  const result = await client3.classifyCard(cardId);
@@ -5990,10 +6052,12 @@ async function handleToolCall(name, args, deps) {
5990
6052
  ]).parse(args.commentType) : undefined;
5991
6053
  const supersedesId = args.supersedesId !== undefined ? z.string().uuid().parse(args.supersedesId) : undefined;
5992
6054
  const confirmsId = args.confirmsId !== undefined ? z.string().uuid().parse(args.confirmsId) : undefined;
6055
+ const replyToId = args.replyToId !== undefined ? z.string().uuid().parse(args.replyToId) : undefined;
5993
6056
  const result = await client3.addComment(cardId, body, {
5994
6057
  commentType,
5995
6058
  supersedesId,
5996
- confirmsId
6059
+ confirmsId,
6060
+ replyToId
5997
6061
  });
5998
6062
  return { success: true, ...result };
5999
6063
  }
@@ -6193,10 +6257,14 @@ async function handleToolCall(name, args, deps) {
6193
6257
  mergedRecentActions = callerRecentActions;
6194
6258
  }
6195
6259
  const runActivity = (callerActions || []).map((a) => a.description).filter((d) => typeof d === "string" && d.length > 0);
6260
+ const reportedStatus = args.status;
6261
+ if (reportedStatus) {
6262
+ noteSessionStatus(cardId, reportedStatus, deps.getScopeId?.());
6263
+ }
6196
6264
  const result = await client3.updateAgentProgress(cardId, {
6197
6265
  agentIdentifier,
6198
6266
  agentName,
6199
- status: args.status,
6267
+ status: reportedStatus,
6200
6268
  progressPercent,
6201
6269
  currentTask: args.currentTask,
6202
6270
  blockers: args.blockers,
@@ -6853,21 +6921,16 @@ async function handleToolCall(name, args, deps) {
6853
6921
  const result = await client3.getPlaybook(playbookId);
6854
6922
  return { success: true, playbook: result.playbook, runs: result.runs };
6855
6923
  }
6856
- case "harmony_run_playbook": {
6857
- const playbookId = z.string().uuid().parse(args.playbookId);
6858
- const result = await client3.runPlaybook(playbookId);
6859
- return { success: true, run: result.run };
6860
- }
6924
+ case "harmony_run_playbook":
6925
+ return deprecatedRemovedToolResult("harmony_run_playbook");
6861
6926
  case "harmony_create_playbook": {
6862
6927
  const workspaceId = args.workspaceId || getWorkspaceId();
6863
6928
  const name2 = z.string().min(1).max(200).parse(args.name);
6864
- const stepsVersion = args.stepsVersion !== undefined ? z.union([z.literal(1), z.literal(2)]).parse(args.stepsVersion) : undefined;
6865
6929
  const result = await client3.createPlaybook({
6866
6930
  workspaceId,
6867
6931
  name: name2,
6868
6932
  description: args.description,
6869
- steps: args.steps,
6870
- stepsVersion
6933
+ steps: args.steps
6871
6934
  });
6872
6935
  return { success: true, playbook: result.playbook };
6873
6936
  }
@@ -6882,14 +6945,8 @@ async function handleToolCall(name, args, deps) {
6882
6945
  });
6883
6946
  return { success: true, playbook: result.playbook };
6884
6947
  }
6885
- case "harmony_save_card_as_playbook": {
6886
- const cardId = z.string().uuid().parse(args.cardId);
6887
- const result = await client3.savePlaybookFromCard({
6888
- cardId,
6889
- name: args.name
6890
- });
6891
- return { success: true, playbook: result.playbook };
6892
- }
6948
+ case "harmony_save_card_as_playbook":
6949
+ return deprecatedRemovedToolResult("harmony_save_card_as_playbook");
6893
6950
  case "harmony_signup": {
6894
6951
  const email = z.string().email().max(254).parse(args.email);
6895
6952
  const password = z.string().min(8).max(128).parse(args.password);