@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/index.js CHANGED
@@ -1452,6 +1452,8 @@ function serializeCommentThread(comments, options = {}) {
1452
1452
  const tags = [];
1453
1453
  if (c.edited_at)
1454
1454
  tags.push("edited");
1455
+ if (c.reply_to_id)
1456
+ tags.push(`reply to ${ref(c.reply_to_id)}`);
1455
1457
  if (c.supersedes_id)
1456
1458
  tags.push(`supersedes ${ref(c.supersedes_id)}`);
1457
1459
  if (c.confirms_id)
@@ -1786,6 +1788,33 @@ class HarmonyApiClient {
1786
1788
  const query = params.toString() ? `?${params.toString()}` : "";
1787
1789
  return this.request("GET", `/board/${projectId}${query}`);
1788
1790
  }
1791
+ async getFullBoard(projectId, options) {
1792
+ const { pageSize = 200, ...boardOpts } = options ?? {};
1793
+ const cards = [];
1794
+ let offset = 0;
1795
+ let last = null;
1796
+ for (;; ) {
1797
+ const page = await this.getBoard(projectId, {
1798
+ ...boardOpts,
1799
+ limit: pageSize,
1800
+ offset
1801
+ });
1802
+ last = page;
1803
+ const pageCards = page.cards ?? [];
1804
+ cards.push(...pageCards);
1805
+ const hasMore = page.pagination?.hasMore ?? false;
1806
+ if (!hasMore || pageCards.length === 0)
1807
+ break;
1808
+ offset += pageSize;
1809
+ }
1810
+ return {
1811
+ project: last?.project,
1812
+ columns: last?.columns ?? [],
1813
+ cards,
1814
+ labels: last?.labels ?? [],
1815
+ totalCards: last?.pagination?.totalCards ?? cards.length
1816
+ };
1817
+ }
1789
1818
  async createCard(projectId, data) {
1790
1819
  return this.request("POST", "/cards", { projectId, ...data });
1791
1820
  }
@@ -1863,6 +1892,12 @@ class HarmonyApiClient {
1863
1892
  async getCardExternalLinks(cardId) {
1864
1893
  return this.request("GET", `/cards/${cardId}/external-links`);
1865
1894
  }
1895
+ async addExternalLink(cardId, url, title) {
1896
+ return this.request("POST", `/cards/${cardId}/external-links`, {
1897
+ url,
1898
+ title
1899
+ });
1900
+ }
1866
1901
  async uploadArtifact(data) {
1867
1902
  return this.request("POST", "/artifacts", data);
1868
1903
  }
@@ -1914,6 +1949,7 @@ class HarmonyApiClient {
1914
1949
  commentType: opts?.commentType,
1915
1950
  supersedesId: opts?.supersedesId,
1916
1951
  confirmsId: opts?.confirmsId,
1952
+ replyToId: opts?.replyToId,
1917
1953
  agentSessionId: opts?.agentSessionId
1918
1954
  });
1919
1955
  }
@@ -2366,12 +2402,6 @@ ${planContent.trim()}`;
2366
2402
  async updatePlaybook(playbookId, updates) {
2367
2403
  return this.request("PATCH", `/playbooks/${playbookId}`, updates);
2368
2404
  }
2369
- async runPlaybook(playbookId) {
2370
- return this.request("POST", `/playbooks/${playbookId}/run`);
2371
- }
2372
- async savePlaybookFromCard(data) {
2373
- return this.request("POST", "/playbooks/from-card", data);
2374
- }
2375
2405
  }
2376
2406
  var _promptModules = null;
2377
2407
  async function loadPromptModules() {
@@ -2452,7 +2482,7 @@ function initAutoSession(callback, getClient2, getClientInfo, scopeId = DEFAULT_
2452
2482
  scope.clientGetter = getClient2;
2453
2483
  scope.clientInfoGetter = getClientInfo ?? null;
2454
2484
  if (!inactivityTimer) {
2455
- inactivityTimer = setInterval(checkInactivity, CHECK_INTERVAL_MS);
2485
+ inactivityTimer = setInterval(sweepTick, CHECK_INTERVAL_MS);
2456
2486
  }
2457
2487
  }
2458
2488
  async function trackActivity(cardId, options) {
@@ -2494,7 +2524,8 @@ async function trackActivity(cardId, options) {
2494
2524
  lastActivityAt: now,
2495
2525
  isExplicit: false,
2496
2526
  agentIdentifier,
2497
- agentName
2527
+ agentName,
2528
+ status: "working"
2498
2529
  });
2499
2530
  }
2500
2531
  function markExplicit(cardId, options) {
@@ -2513,7 +2544,8 @@ function markExplicit(cardId, options) {
2513
2544
  lastActivityAt: Date.now(),
2514
2545
  isExplicit: true,
2515
2546
  agentIdentifier: options?.agentIdentifier ?? "explicit",
2516
- agentName: options?.agentName ?? "Explicit Agent"
2547
+ agentName: options?.agentName ?? "Explicit Agent",
2548
+ status: "working"
2517
2549
  });
2518
2550
  }
2519
2551
  }
@@ -2559,6 +2591,31 @@ function checkInactivity() {
2559
2591
  }
2560
2592
  }
2561
2593
  }
2594
+ function noteSessionStatus(cardId, status, scopeId = DEFAULT_SCOPE) {
2595
+ const session = scopes.get(scopeId)?.sessions.get(cardId);
2596
+ if (session)
2597
+ session.status = status;
2598
+ }
2599
+ function heartbeatActiveSessions() {
2600
+ for (const scope of scopes.values()) {
2601
+ const client3 = scope.clientGetter?.();
2602
+ if (!client3)
2603
+ continue;
2604
+ for (const session of scope.sessions.values()) {
2605
+ if ((session.status ?? "working") !== "working")
2606
+ continue;
2607
+ client3.updateAgentProgress(session.cardId, {
2608
+ agentIdentifier: session.agentIdentifier,
2609
+ agentName: session.agentName,
2610
+ status: "working"
2611
+ }).catch(() => {});
2612
+ }
2613
+ }
2614
+ }
2615
+ function sweepTick() {
2616
+ checkInactivity();
2617
+ heartbeatActiveSessions();
2618
+ }
2562
2619
  async function autoEndSession(scope, client3, cardId, status) {
2563
2620
  if (!scope.sessions.delete(cardId))
2564
2621
  return;
@@ -3235,7 +3292,7 @@ Start work on a Harmony card. Card reference: $ARGUMENTS
3235
3292
  ## 1. Find & Fetch Card
3236
3293
 
3237
3294
  Parse the reference and fetch the card:
3238
- - \`#42\` or \`42\` → \`harmony_get_card_by_short_id\` with \`shortId: 42\`
3295
+ - \`#42\` or \`42\` → \`harmony_get_card\` with \`shortId: 42\`
3239
3296
  - UUID → \`harmony_get_card\` with \`cardId\`
3240
3297
  - Name/text → \`harmony_search_cards\` with \`query\`
3241
3298
 
@@ -3289,7 +3346,7 @@ If pausing: \`harmony_end_agent_session\` with \`status: "paused"\`
3289
3346
 
3290
3347
  ## Key Tools Reference
3291
3348
 
3292
- **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\`
3349
+ **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\`
3293
3350
 
3294
3351
  **Subtasks:** \`harmony_create_subtask\`, \`harmony_toggle_subtask\`, \`harmony_delete_subtask\`
3295
3352
 
@@ -3483,6 +3540,37 @@ function requireExactlyOneScope(scope) {
3483
3540
  throw new Error("Provide exactly one of cardId, planId, or workspaceId.");
3484
3541
  }
3485
3542
  }
3543
+ var DEPRECATED_TOOL_ALIASES = {
3544
+ harmony_get_card_by_short_id: "harmony_get_card",
3545
+ harmony_bulk_get_cards: "harmony_get_card",
3546
+ harmony_upload_artifact: "harmony_upload",
3547
+ harmony_upload_card_attachment: "harmony_upload",
3548
+ harmony_request_artifact_upload_url: "harmony_request_upload_url",
3549
+ harmony_request_card_attachment_upload_url: "harmony_request_upload_url",
3550
+ harmony_finalize_artifact: "harmony_finalize_upload",
3551
+ harmony_finalize_card_attachment: "harmony_finalize_upload"
3552
+ };
3553
+ var _warnedDeprecatedTools = new Set;
3554
+ function warnDeprecatedTool(oldName) {
3555
+ if (_warnedDeprecatedTools.has(oldName))
3556
+ return;
3557
+ _warnedDeprecatedTools.add(oldName);
3558
+ const replacement = DEPRECATED_TOOL_ALIASES[oldName];
3559
+ 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.`);
3560
+ }
3561
+ var DEPRECATED_REMOVED_TOOLS = {
3562
+ 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.",
3563
+ harmony_save_card_as_playbook: "The steps_version=1 automation macro was removed. Build a stage playbook with harmony_create_playbook instead."
3564
+ };
3565
+ var _warnedRemovedTools = new Set;
3566
+ function deprecatedRemovedToolResult(name) {
3567
+ const message = DEPRECATED_REMOVED_TOOLS[name];
3568
+ if (!_warnedRemovedTools.has(name)) {
3569
+ _warnedRemovedTools.add(name);
3570
+ console.error(`[harmony-mcp] Tool "${name}" is deprecated and no longer functional — ${message}`);
3571
+ }
3572
+ return { success: false, deprecated: true, message };
3573
+ }
3486
3574
  var SIGNED_UPLOAD_TIMEOUT_MS = 30000;
3487
3575
  async function putToSignedUrl(uploadUrl, bytes, contentType) {
3488
3576
  let res;
@@ -3810,44 +3898,29 @@ var TOOLS = {
3810
3898
  }
3811
3899
  },
3812
3900
  harmony_get_card: {
3813
- description: "Get detailed information about a specific card by UUID",
3814
- inputSchema: {
3815
- type: "object",
3816
- properties: {
3817
- cardId: { type: "string", description: "Card UUID" }
3818
- },
3819
- required: ["cardId"]
3820
- }
3821
- },
3822
- harmony_get_card_by_short_id: {
3823
- description: "Get a card by its short ID (e.g., #42) within a project",
3901
+ 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.",
3824
3902
  inputSchema: {
3825
3903
  type: "object",
3826
3904
  properties: {
3827
- projectId: {
3905
+ cardId: {
3828
3906
  type: "string",
3829
- description: "Project ID (optional if context set)"
3907
+ description: "Card UUID full detail for one card."
3830
3908
  },
3831
3909
  shortId: {
3832
3910
  type: "number",
3833
- description: "Short ID number (e.g., 42 for card #42)"
3834
- }
3835
- },
3836
- required: ["shortId"]
3837
- }
3838
- },
3839
- harmony_bulk_get_cards: {
3840
- 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.",
3841
- inputSchema: {
3842
- type: "object",
3843
- properties: {
3911
+ description: "Short ID (e.g. 42 for card #42) — full detail, project-scoped."
3912
+ },
3844
3913
  shortIds: {
3845
3914
  type: "array",
3846
3915
  items: { type: "number" },
3847
- description: "Card short ids, e.g. [400, 401, 402]. Max 100 per call."
3916
+ description: "Short IDs, e.g. [400, 401, 402] compact summaries in one call. Max 100."
3917
+ },
3918
+ projectId: {
3919
+ type: "string",
3920
+ description: "Project ID for shortId/shortIds (optional if context set)."
3848
3921
  }
3849
3922
  },
3850
- required: ["shortIds"]
3923
+ required: []
3851
3924
  }
3852
3925
  },
3853
3926
  harmony_bulk_archive_cards: {
@@ -4002,174 +4075,144 @@ var TOOLS = {
4002
4075
  required: ["cardId"]
4003
4076
  }
4004
4077
  },
4005
- harmony_upload_card_attachment: {
4006
- 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.",
4078
+ harmony_upload: {
4079
+ 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.",
4007
4080
  inputSchema: {
4008
4081
  type: "object",
4009
4082
  properties: {
4010
- cardId: { type: "string", description: "Card UUID" },
4083
+ target: {
4084
+ type: "string",
4085
+ enum: ["card_attachment", "artifact"],
4086
+ description: "What to upload: a card file attachment or a hosted HTML artifact."
4087
+ },
4088
+ cardId: {
4089
+ type: "string",
4090
+ description: "Card UUID. Required for card_attachment; one of cardId/planId/workspaceId for artifact."
4091
+ },
4092
+ planId: {
4093
+ type: "string",
4094
+ description: "Plan UUID (artifact only — link the artifact to a plan)."
4095
+ },
4096
+ workspaceId: {
4097
+ type: "string",
4098
+ description: "Workspace UUID (artifact only — standalone artifact)."
4099
+ },
4011
4100
  filePath: {
4012
4101
  type: "string",
4013
- description: "Absolute path to a local file the MCP server process can read. Mutually exclusive with base64Data."
4102
+ description: "Absolute path to a local file the server can read. Mutually exclusive with base64Data."
4014
4103
  },
4015
4104
  base64Data: {
4016
4105
  type: "string",
4017
- description: "Base64-encoded file bytes (a `data:` URL prefix is accepted and stripped). Requires fileName. Mutually exclusive with filePath."
4106
+ description: "Base64-encoded bytes (a `data:` URL prefix is accepted and stripped). Mutually exclusive with filePath."
4018
4107
  },
4019
4108
  fileName: {
4020
4109
  type: "string",
4021
- description: "File name including extension (e.g. 'screenshot.png'). Required with base64Data; defaults to the basename of filePath otherwise."
4110
+ description: "File name including extension (card_attachment: required with base64Data, else defaults to the filePath basename)."
4111
+ },
4112
+ title: {
4113
+ type: "string",
4114
+ description: "Artifact display title (defaults to the file basename)."
4022
4115
  },
4023
4116
  contentType: {
4024
4117
  type: "string",
4025
- description: "Optional MIME type (e.g. 'image/png'). Inferred from the file extension when omitted."
4118
+ description: "Optional MIME type (card_attachment; inferred from the extension when omitted)."
4026
4119
  }
4027
4120
  },
4028
- required: ["cardId"]
4121
+ required: ["target"]
4029
4122
  }
4030
4123
  },
4031
- harmony_request_card_attachment_upload_url: {
4032
- 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.",
4124
+ harmony_request_upload_url: {
4125
+ 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.",
4033
4126
  inputSchema: {
4034
4127
  type: "object",
4035
4128
  properties: {
4036
- cardId: { type: "string", description: "Card UUID" },
4037
- fileName: {
4129
+ target: {
4038
4130
  type: "string",
4039
- description: "File name including extension (e.g. 'screenshot.png')."
4131
+ enum: ["card_attachment", "artifact"],
4132
+ description: "What to upload: a card file attachment or a hosted HTML artifact."
4040
4133
  },
4041
- fileType: {
4134
+ cardId: {
4042
4135
  type: "string",
4043
- description: "Optional MIME type (e.g. 'image/png'). Inferred from the file extension when omitted."
4136
+ description: "Card UUID. Required for card_attachment; one of cardId/planId/workspaceId for artifact."
4044
4137
  },
4045
- size: {
4046
- type: "number",
4047
- description: "File size in bytes (rejected early if over 5MB)."
4048
- }
4049
- },
4050
- required: ["cardId", "fileName", "size"]
4051
- }
4052
- },
4053
- harmony_finalize_card_attachment: {
4054
- 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.",
4055
- inputSchema: {
4056
- type: "object",
4057
- properties: {
4058
- cardId: { type: "string", description: "Card UUID" },
4059
- storagePath: {
4138
+ planId: { type: "string", description: "Plan UUID (artifact only)." },
4139
+ workspaceId: {
4060
4140
  type: "string",
4061
- description: "The storagePath returned by harmony_request_card_attachment_upload_url."
4141
+ description: "Workspace UUID (artifact only)."
4062
4142
  },
4063
4143
  fileName: {
4064
4144
  type: "string",
4065
- description: "File name including extension (e.g. 'screenshot.png')."
4145
+ description: "File name including extension (required for card_attachment)."
4066
4146
  },
4147
+ title: { type: "string", description: "Artifact display title." },
4067
4148
  fileType: {
4068
4149
  type: "string",
4069
- description: "Optional MIME type; inferred from the extension when omitted."
4150
+ description: "card_attachment MIME type (inferred from the extension when omitted)."
4070
4151
  },
4071
- sha256: {
4152
+ contentType: {
4072
4153
  type: "string",
4073
- description: "Optional hex SHA-256 of the uploaded bytes; verified against the stored object."
4154
+ description: "artifact MIME type; only 'text/html' is accepted (the default)."
4074
4155
  },
4075
4156
  size: {
4076
4157
  type: "number",
4077
- description: "Optional byte size (advisory; re-validated server-side)."
4158
+ description: "File size in bytes (rejected early if over the target's limit)."
4078
4159
  }
4079
4160
  },
4080
- required: ["cardId", "storagePath", "fileName"]
4081
- }
4082
- },
4083
- harmony_classify_card: {
4084
- 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`.",
4085
- inputSchema: {
4086
- type: "object",
4087
- properties: {
4088
- cardId: { type: "string", description: "Card UUID" }
4089
- },
4090
- required: ["cardId"]
4161
+ required: ["target"]
4091
4162
  }
4092
4163
  },
4093
- harmony_upload_artifact: {
4094
- 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.",
4164
+ harmony_finalize_upload: {
4165
+ 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.",
4095
4166
  inputSchema: {
4096
4167
  type: "object",
4097
4168
  properties: {
4098
- title: {
4169
+ target: {
4099
4170
  type: "string",
4100
- description: "Display title (defaults to the file basename)."
4171
+ enum: ["card_attachment", "artifact"],
4172
+ description: "What to finalize: a card file attachment or a hosted HTML artifact."
4101
4173
  },
4102
- cardId: { type: "string", description: "Link to this card (UUID)." },
4103
- planId: { type: "string", description: "Link to this plan (UUID)." },
4104
- workspaceId: {
4174
+ storagePath: {
4105
4175
  type: "string",
4106
- description: "Attach to this workspace as a standalone artifact (UUID)."
4176
+ description: "The storagePath returned by harmony_request_upload_url."
4107
4177
  },
4108
- filePath: {
4178
+ cardId: {
4109
4179
  type: "string",
4110
- description: "Absolute path to a local .html file the MCP server process can read. Mutually exclusive with base64Data."
4180
+ description: "Card UUID. Required for card_attachment; scope for artifact."
4111
4181
  },
4112
- base64Data: {
4182
+ planId: { type: "string", description: "Plan UUID (artifact only)." },
4183
+ workspaceId: {
4113
4184
  type: "string",
4114
- description: "Base64-encoded HTML bytes (a `data:` URL prefix is accepted and stripped). Mutually exclusive with filePath."
4115
- }
4116
- },
4117
- required: []
4118
- }
4119
- },
4120
- harmony_request_artifact_upload_url: {
4121
- 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.",
4122
- inputSchema: {
4123
- type: "object",
4124
- properties: {
4125
- title: {
4185
+ description: "Workspace UUID (artifact only)."
4186
+ },
4187
+ fileName: {
4126
4188
  type: "string",
4127
- description: "Display title (defaults to the file basename at finalize)."
4189
+ description: "File name including extension (required for card_attachment)."
4128
4190
  },
4129
- cardId: { type: "string", description: "Link to this card (UUID)." },
4130
- planId: { type: "string", description: "Link to this plan (UUID)." },
4131
- workspaceId: {
4191
+ title: { type: "string", description: "Artifact display title." },
4192
+ fileType: {
4132
4193
  type: "string",
4133
- description: "Attach to this workspace as a standalone artifact (UUID)."
4194
+ description: "card_attachment MIME type; inferred from the extension when omitted."
4134
4195
  },
4135
- contentType: {
4196
+ sha256: {
4136
4197
  type: "string",
4137
- description: "MIME type; only 'text/html' is accepted (the default)."
4198
+ description: "Optional hex SHA-256 of the uploaded bytes; verified against the stored object."
4138
4199
  },
4139
4200
  size: {
4140
4201
  type: "number",
4141
- description: "File size in bytes (rejected early if over 2MB)."
4202
+ description: "Optional byte size (advisory; re-validated server-side)."
4142
4203
  }
4143
4204
  },
4144
- required: []
4205
+ required: ["target", "storagePath"]
4145
4206
  }
4146
4207
  },
4147
- harmony_finalize_artifact: {
4148
- 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.",
4208
+ harmony_classify_card: {
4209
+ 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`.",
4149
4210
  inputSchema: {
4150
4211
  type: "object",
4151
4212
  properties: {
4152
- storagePath: {
4153
- type: "string",
4154
- description: "The storagePath returned by harmony_request_artifact_upload_url."
4155
- },
4156
- title: { type: "string", description: "Display title." },
4157
- cardId: { type: "string", description: "Link to this card (UUID)." },
4158
- planId: { type: "string", description: "Link to this plan (UUID)." },
4159
- workspaceId: {
4160
- type: "string",
4161
- description: "Attach to this workspace as a standalone artifact (UUID)."
4162
- },
4163
- sha256: {
4164
- type: "string",
4165
- description: "Optional hex SHA-256 of the uploaded bytes; verified against the stored object."
4166
- },
4167
- size: {
4168
- type: "number",
4169
- description: "Optional byte size (advisory; re-validated server-side)."
4170
- }
4213
+ cardId: { type: "string", description: "Card UUID" }
4171
4214
  },
4172
- required: ["storagePath"]
4215
+ required: ["cardId"]
4173
4216
  }
4174
4217
  },
4175
4218
  harmony_share_artifact: {
@@ -4199,6 +4242,27 @@ var TOOLS = {
4199
4242
  required: ["cardId"]
4200
4243
  }
4201
4244
  },
4245
+ harmony_add_external_link: {
4246
+ 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.",
4247
+ inputSchema: {
4248
+ type: "object",
4249
+ properties: {
4250
+ cardId: {
4251
+ type: "string",
4252
+ description: "Card UUID"
4253
+ },
4254
+ url: {
4255
+ type: "string",
4256
+ description: "The URL to attach"
4257
+ },
4258
+ title: {
4259
+ type: "string",
4260
+ description: "Optional human-readable title for the link"
4261
+ }
4262
+ },
4263
+ required: ["cardId", "url"]
4264
+ }
4265
+ },
4202
4266
  harmony_create_subtask: {
4203
4267
  description: "Create a subtask on a card",
4204
4268
  inputSchema: {
@@ -4250,7 +4314,7 @@ var TOOLS = {
4250
4314
  }
4251
4315
  },
4252
4316
  harmony_add_comment: {
4253
- 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.",
4317
+ 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.",
4254
4318
  inputSchema: {
4255
4319
  type: "object",
4256
4320
  properties: {
@@ -4276,6 +4340,10 @@ var TOOLS = {
4276
4340
  confirmsId: {
4277
4341
  type: "string",
4278
4342
  description: "Comment id this comment reaffirms"
4343
+ },
4344
+ replyToId: {
4345
+ type: "string",
4346
+ 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."
4279
4347
  }
4280
4348
  },
4281
4349
  required: ["cardId", "body"]
@@ -5146,7 +5214,7 @@ var TOOLS = {
5146
5214
  }
5147
5215
  },
5148
5216
  harmony_list_playbook: {
5149
- description: "List a workspace's playbooks (reusable process definitions). Returns each playbook's name, version, steps_version, and state. Read-only.",
5217
+ description: "List a workspace's playbooks (reusable process definitions). Returns each playbook's name, version, and state. Read-only.",
5150
5218
  inputSchema: {
5151
5219
  type: "object",
5152
5220
  properties: {
@@ -5168,21 +5236,8 @@ var TOOLS = {
5168
5236
  required: ["playbookId"]
5169
5237
  }
5170
5238
  },
5171
- harmony_run_playbook: {
5172
- 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.",
5173
- inputSchema: {
5174
- type: "object",
5175
- properties: {
5176
- playbookId: {
5177
- type: "string",
5178
- description: "Playbook ID to run (UUID)"
5179
- }
5180
- },
5181
- required: ["playbookId"]
5182
- }
5183
- },
5184
5239
  harmony_create_playbook: {
5185
- 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).",
5240
+ 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.",
5186
5241
  inputSchema: {
5187
5242
  type: "object",
5188
5243
  properties: {
@@ -5192,14 +5247,9 @@ var TOOLS = {
5192
5247
  },
5193
5248
  name: { type: "string", description: "Playbook name" },
5194
5249
  description: { type: "string", description: "Playbook description" },
5195
- stepsVersion: {
5196
- type: "number",
5197
- enum: [1, 2],
5198
- description: "1 = automation macro (tool steps), 2 = Method stage model (stage objects). Default 1."
5199
- },
5200
5250
  steps: {
5201
5251
  type: "array",
5202
- description: "Steps (steps_version 1: tool-step objects) or stages (steps_version 2: stage objects).",
5252
+ description: "The playbook's ordered stage objects.",
5203
5253
  items: { type: "object" }
5204
5254
  }
5205
5255
  },
@@ -5219,7 +5269,7 @@ var TOOLS = {
5219
5269
  description: { type: "string", description: "New description" },
5220
5270
  steps: {
5221
5271
  type: "array",
5222
- description: "New steps (v1) or stages (v2) array.",
5272
+ description: "New ordered stage objects.",
5223
5273
  items: { type: "object" }
5224
5274
  },
5225
5275
  enabled: {
@@ -5235,20 +5285,6 @@ var TOOLS = {
5235
5285
  required: ["playbookId"]
5236
5286
  }
5237
5287
  },
5238
- harmony_save_card_as_playbook: {
5239
- 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.",
5240
- inputSchema: {
5241
- type: "object",
5242
- properties: {
5243
- cardId: { type: "string", description: "Card ID to template (UUID)" },
5244
- name: {
5245
- type: "string",
5246
- description: "Name for the new playbook (defaults to the card title)"
5247
- }
5248
- },
5249
- required: ["cardId"]
5250
- }
5251
- },
5252
5288
  harmony_signup: {
5253
5289
  description: "Create a new user account. Returns a JWT session for subsequent authenticated calls. No API key required.",
5254
5290
  inputSchema: {
@@ -5648,23 +5684,33 @@ async function handleToolCall(name, args, deps) {
5648
5684
  });
5649
5685
  return { success: true, ...result, count: result.cards.length };
5650
5686
  }
5687
+ case "harmony_get_card_by_short_id":
5688
+ case "harmony_bulk_get_cards":
5651
5689
  case "harmony_get_card": {
5690
+ if (name !== "harmony_get_card")
5691
+ warnDeprecatedTool(name);
5692
+ const hasShortIds = args.shortIds != null;
5693
+ const hasShortId = args.shortId != null;
5694
+ const hasCardId = args.cardId != null;
5695
+ if ([hasShortIds, hasShortId, hasCardId].filter(Boolean).length !== 1) {
5696
+ throw new Error("Provide exactly one of: cardId (UUID), shortId (number), or shortIds (number[]).");
5697
+ }
5698
+ if (hasShortIds) {
5699
+ const shortIds = z.array(z.number().int().positive()).min(1).max(100).parse(args.shortIds);
5700
+ const projectId = getProjectId();
5701
+ const result2 = await client3.bulkGetCards(projectId, shortIds);
5702
+ return { success: true, ...result2 };
5703
+ }
5704
+ if (hasShortId) {
5705
+ const shortId = z.number().int().positive().parse(args.shortId);
5706
+ const projectId = args.projectId || getProjectId();
5707
+ const result2 = await client3.getCardByShortId(projectId, shortId);
5708
+ return { success: true, ...result2 };
5709
+ }
5652
5710
  const cardId = z.string().uuid().parse(args.cardId);
5653
5711
  const result = await client3.getCard(cardId);
5654
5712
  return { success: true, ...result };
5655
5713
  }
5656
- case "harmony_get_card_by_short_id": {
5657
- const shortId = z.number().int().positive().parse(args.shortId);
5658
- const projectId = args.projectId || getProjectId();
5659
- const result = await client3.getCardByShortId(projectId, shortId);
5660
- return { success: true, ...result };
5661
- }
5662
- case "harmony_bulk_get_cards": {
5663
- const shortIds = z.array(z.number().int().positive()).min(1).max(100).parse(args.shortIds);
5664
- const projectId = getProjectId();
5665
- const result = await client3.bulkGetCards(projectId, shortIds);
5666
- return { success: true, ...result };
5667
- }
5668
5714
  case "harmony_bulk_archive_cards": {
5669
5715
  const shortIds = z.array(z.number().int().positive()).min(1).max(100).parse(args.shortIds);
5670
5716
  const projectId = getProjectId();
@@ -5764,58 +5810,58 @@ async function handleToolCall(name, args, deps) {
5764
5810
  const result = await client3.getCardAttachments(cardId);
5765
5811
  return result;
5766
5812
  }
5767
- case "harmony_upload_card_attachment": {
5768
- const cardId = z.string().uuid().parse(args.cardId);
5813
+ case "harmony_upload_card_attachment":
5814
+ case "harmony_upload_artifact":
5815
+ case "harmony_upload": {
5816
+ if (name !== "harmony_upload")
5817
+ warnDeprecatedTool(name);
5818
+ const target = name === "harmony_upload_card_attachment" ? "card_attachment" : name === "harmony_upload_artifact" ? "artifact" : z.enum(["card_attachment", "artifact"]).parse(args.target);
5769
5819
  const filePath = args.filePath != null ? z.string().parse(args.filePath) : undefined;
5770
5820
  const base64Data = args.base64Data != null ? z.string().parse(args.base64Data) : undefined;
5771
- const fileName = args.fileName != null ? z.string().parse(args.fileName) : undefined;
5772
- const contentType = args.contentType != null ? z.string().parse(args.contentType) : undefined;
5773
5821
  if (filePath && base64Data) {
5774
5822
  throw new Error("Provide either filePath or base64Data, not both.");
5775
5823
  }
5776
- if (filePath) {
5777
- const bytes = await readFileForUpload(filePath, MAX_ATTACHMENT_SIZE, "attachment");
5778
- const resolvedName = fileName || basename(filePath);
5779
- const signed = await client3.requestCardAttachmentUploadUrl(cardId, {
5780
- fileName: resolvedName,
5781
- fileType: contentType,
5782
- size: bytes.byteLength
5783
- });
5784
- await putToSignedUrl(signed.uploadUrl, bytes, contentType || signed.fileType || "application/octet-stream");
5785
- return await client3.finalizeCardAttachment(cardId, {
5786
- storagePath: signed.storagePath,
5787
- fileName: resolvedName,
5788
- fileType: contentType || signed.fileType,
5789
- sha256: sha256Hex(bytes),
5790
- size: bytes.byteLength
5791
- });
5792
- }
5793
- if (base64Data) {
5794
- if (!fileName) {
5795
- throw new Error("fileName is required when using base64Data.");
5824
+ if (target === "card_attachment") {
5825
+ const cardId2 = z.string().uuid().parse(args.cardId);
5826
+ const fileName = args.fileName != null ? z.string().parse(args.fileName) : undefined;
5827
+ const contentType = args.contentType != null ? z.string().parse(args.contentType) : undefined;
5828
+ if (filePath) {
5829
+ const bytes = await readFileForUpload(filePath, MAX_ATTACHMENT_SIZE, "attachment");
5830
+ const resolvedName = fileName || basename(filePath);
5831
+ const signed = await client3.requestCardAttachmentUploadUrl(cardId2, {
5832
+ fileName: resolvedName,
5833
+ fileType: contentType,
5834
+ size: bytes.byteLength
5835
+ });
5836
+ await putToSignedUrl(signed.uploadUrl, bytes, contentType || signed.fileType || "application/octet-stream");
5837
+ return await client3.finalizeCardAttachment(cardId2, {
5838
+ storagePath: signed.storagePath,
5839
+ fileName: resolvedName,
5840
+ fileType: contentType || signed.fileType,
5841
+ sha256: sha256Hex(bytes),
5842
+ size: bytes.byteLength
5843
+ });
5796
5844
  }
5797
- if (base64ByteLength(base64Data) > MAX_ATTACHMENT_SIZE) {
5798
- 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.`);
5845
+ if (base64Data) {
5846
+ if (!fileName) {
5847
+ throw new Error("fileName is required when using base64Data.");
5848
+ }
5849
+ if (base64ByteLength(base64Data) > MAX_ATTACHMENT_SIZE) {
5850
+ throw new Error(`File is over the 5MB attachment limit. Use the harmony_request_upload_url + harmony_finalize_upload handshake for large files.`);
5851
+ }
5852
+ return await client3.uploadCardAttachment(cardId2, {
5853
+ fileName,
5854
+ data: base64Data,
5855
+ fileType: contentType
5856
+ });
5799
5857
  }
5800
- return await client3.uploadCardAttachment(cardId, {
5801
- fileName,
5802
- data: base64Data,
5803
- fileType: contentType
5804
- });
5858
+ throw new Error("Provide either filePath or base64Data.");
5805
5859
  }
5806
- throw new Error("Provide either filePath or base64Data.");
5807
- }
5808
- case "harmony_upload_artifact": {
5809
5860
  const title = args.title != null ? z.string().parse(args.title) : undefined;
5810
5861
  const cardId = args.cardId != null ? z.string().uuid().parse(args.cardId) : undefined;
5811
5862
  const planId = args.planId != null ? z.string().uuid().parse(args.planId) : undefined;
5812
5863
  const workspaceId = args.workspaceId != null ? z.string().uuid().parse(args.workspaceId) : undefined;
5813
5864
  requireExactlyOneScope({ cardId, planId, workspaceId });
5814
- const filePath = args.filePath != null ? z.string().parse(args.filePath) : undefined;
5815
- const base64Data = args.base64Data != null ? z.string().parse(args.base64Data) : undefined;
5816
- if (filePath && base64Data) {
5817
- throw new Error("Provide either filePath or base64Data, not both.");
5818
- }
5819
5865
  if (filePath) {
5820
5866
  const bytes = await readFileForUpload(filePath, MAX_ARTIFACT_SIZE, "artifact");
5821
5867
  const resolvedTitle = title || basename(filePath);
@@ -5840,7 +5886,7 @@ async function handleToolCall(name, args, deps) {
5840
5886
  }
5841
5887
  if (base64Data) {
5842
5888
  if (base64ByteLength(base64Data) > MAX_ARTIFACT_SIZE) {
5843
- throw new Error(`Artifact is over the 2MB limit. Use the harmony_request_artifact_upload_url + harmony_finalize_artifact handshake for large files.`);
5889
+ throw new Error(`Artifact is over the 2MB limit. Use the harmony_request_upload_url + harmony_finalize_upload handshake for large files.`);
5844
5890
  }
5845
5891
  return await client3.uploadArtifact({
5846
5892
  title,
@@ -5852,7 +5898,26 @@ async function handleToolCall(name, args, deps) {
5852
5898
  }
5853
5899
  throw new Error("Provide either filePath or base64Data.");
5854
5900
  }
5855
- case "harmony_request_artifact_upload_url": {
5901
+ case "harmony_request_artifact_upload_url":
5902
+ case "harmony_request_card_attachment_upload_url":
5903
+ case "harmony_request_upload_url": {
5904
+ if (name !== "harmony_request_upload_url")
5905
+ warnDeprecatedTool(name);
5906
+ 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);
5907
+ if (target === "card_attachment") {
5908
+ const cardId2 = z.string().uuid().parse(args.cardId);
5909
+ const fileName = z.string().parse(args.fileName);
5910
+ const fileType = args.fileType != null ? z.string().parse(args.fileType) : undefined;
5911
+ const size2 = z.number().positive().parse(args.size);
5912
+ if (size2 > MAX_ATTACHMENT_SIZE) {
5913
+ throw new Error(`Declared size ${size2} bytes is over the ${MAX_ATTACHMENT_SIZE}-byte (5MB) attachment limit.`);
5914
+ }
5915
+ return await client3.requestCardAttachmentUploadUrl(cardId2, {
5916
+ fileName,
5917
+ fileType,
5918
+ size: size2
5919
+ });
5920
+ }
5856
5921
  const title = args.title != null ? z.string().parse(args.title) : undefined;
5857
5922
  const cardId = args.cardId != null ? z.string().uuid().parse(args.cardId) : undefined;
5858
5923
  const planId = args.planId != null ? z.string().uuid().parse(args.planId) : undefined;
@@ -5872,8 +5937,27 @@ async function handleToolCall(name, args, deps) {
5872
5937
  size
5873
5938
  });
5874
5939
  }
5875
- case "harmony_finalize_artifact": {
5940
+ case "harmony_finalize_artifact":
5941
+ case "harmony_finalize_card_attachment":
5942
+ case "harmony_finalize_upload": {
5943
+ if (name !== "harmony_finalize_upload")
5944
+ warnDeprecatedTool(name);
5945
+ const target = name === "harmony_finalize_card_attachment" ? "card_attachment" : name === "harmony_finalize_artifact" ? "artifact" : z.enum(["card_attachment", "artifact"]).parse(args.target);
5876
5946
  const storagePath = z.string().parse(args.storagePath);
5947
+ if (target === "card_attachment") {
5948
+ const cardId2 = z.string().uuid().parse(args.cardId);
5949
+ const fileName = z.string().parse(args.fileName);
5950
+ const fileType = args.fileType != null ? z.string().parse(args.fileType) : undefined;
5951
+ const sha2562 = args.sha256 != null ? z.string().parse(args.sha256) : undefined;
5952
+ const size2 = args.size != null ? z.number().positive().parse(args.size) : undefined;
5953
+ return await client3.finalizeCardAttachment(cardId2, {
5954
+ storagePath,
5955
+ fileName,
5956
+ fileType,
5957
+ sha256: sha2562,
5958
+ size: size2
5959
+ });
5960
+ }
5877
5961
  const title = args.title != null ? z.string().parse(args.title) : undefined;
5878
5962
  const cardId = args.cardId != null ? z.string().uuid().parse(args.cardId) : undefined;
5879
5963
  const planId = args.planId != null ? z.string().uuid().parse(args.planId) : undefined;
@@ -5891,35 +5975,6 @@ async function handleToolCall(name, args, deps) {
5891
5975
  workspaceId
5892
5976
  });
5893
5977
  }
5894
- case "harmony_request_card_attachment_upload_url": {
5895
- const cardId = z.string().uuid().parse(args.cardId);
5896
- const fileName = z.string().parse(args.fileName);
5897
- const fileType = args.fileType != null ? z.string().parse(args.fileType) : undefined;
5898
- const size = z.number().positive().parse(args.size);
5899
- if (size > MAX_ATTACHMENT_SIZE) {
5900
- throw new Error(`Declared size ${size} bytes is over the ${MAX_ATTACHMENT_SIZE}-byte (5MB) attachment limit.`);
5901
- }
5902
- return await client3.requestCardAttachmentUploadUrl(cardId, {
5903
- fileName,
5904
- fileType,
5905
- size
5906
- });
5907
- }
5908
- case "harmony_finalize_card_attachment": {
5909
- const cardId = z.string().uuid().parse(args.cardId);
5910
- const storagePath = z.string().parse(args.storagePath);
5911
- const fileName = z.string().parse(args.fileName);
5912
- const fileType = args.fileType != null ? z.string().parse(args.fileType) : undefined;
5913
- const sha256 = args.sha256 != null ? z.string().parse(args.sha256) : undefined;
5914
- const size = args.size != null ? z.number().positive().parse(args.size) : undefined;
5915
- return await client3.finalizeCardAttachment(cardId, {
5916
- storagePath,
5917
- fileName,
5918
- fileType,
5919
- sha256,
5920
- size
5921
- });
5922
- }
5923
5978
  case "harmony_share_artifact": {
5924
5979
  const artifactId = z.string().uuid().parse(args.artifactId);
5925
5980
  const expiresInDays = args.expiresInDays != null ? z.number().positive().parse(args.expiresInDays) : undefined;
@@ -5932,6 +5987,13 @@ async function handleToolCall(name, args, deps) {
5932
5987
  const result = await client3.getCardExternalLinks(cardId);
5933
5988
  return result;
5934
5989
  }
5990
+ case "harmony_add_external_link": {
5991
+ const cardId = z.string().uuid().parse(args.cardId);
5992
+ const url = z.string().min(1).max(2048).parse(args.url);
5993
+ const title = args.title ? z.string().max(200).parse(args.title) : undefined;
5994
+ const result = await client3.addExternalLink(cardId, url, title);
5995
+ return { success: true, ...result };
5996
+ }
5935
5997
  case "harmony_classify_card": {
5936
5998
  const cardId = z.string().uuid().parse(args.cardId);
5937
5999
  const result = await client3.classifyCard(cardId);
@@ -5985,10 +6047,12 @@ async function handleToolCall(name, args, deps) {
5985
6047
  ]).parse(args.commentType) : undefined;
5986
6048
  const supersedesId = args.supersedesId !== undefined ? z.string().uuid().parse(args.supersedesId) : undefined;
5987
6049
  const confirmsId = args.confirmsId !== undefined ? z.string().uuid().parse(args.confirmsId) : undefined;
6050
+ const replyToId = args.replyToId !== undefined ? z.string().uuid().parse(args.replyToId) : undefined;
5988
6051
  const result = await client3.addComment(cardId, body, {
5989
6052
  commentType,
5990
6053
  supersedesId,
5991
- confirmsId
6054
+ confirmsId,
6055
+ replyToId
5992
6056
  });
5993
6057
  return { success: true, ...result };
5994
6058
  }
@@ -6188,10 +6252,14 @@ async function handleToolCall(name, args, deps) {
6188
6252
  mergedRecentActions = callerRecentActions;
6189
6253
  }
6190
6254
  const runActivity = (callerActions || []).map((a) => a.description).filter((d) => typeof d === "string" && d.length > 0);
6255
+ const reportedStatus = args.status;
6256
+ if (reportedStatus) {
6257
+ noteSessionStatus(cardId, reportedStatus, deps.getScopeId?.());
6258
+ }
6191
6259
  const result = await client3.updateAgentProgress(cardId, {
6192
6260
  agentIdentifier,
6193
6261
  agentName,
6194
- status: args.status,
6262
+ status: reportedStatus,
6195
6263
  progressPercent,
6196
6264
  currentTask: args.currentTask,
6197
6265
  blockers: args.blockers,
@@ -6848,21 +6916,16 @@ async function handleToolCall(name, args, deps) {
6848
6916
  const result = await client3.getPlaybook(playbookId);
6849
6917
  return { success: true, playbook: result.playbook, runs: result.runs };
6850
6918
  }
6851
- case "harmony_run_playbook": {
6852
- const playbookId = z.string().uuid().parse(args.playbookId);
6853
- const result = await client3.runPlaybook(playbookId);
6854
- return { success: true, run: result.run };
6855
- }
6919
+ case "harmony_run_playbook":
6920
+ return deprecatedRemovedToolResult("harmony_run_playbook");
6856
6921
  case "harmony_create_playbook": {
6857
6922
  const workspaceId = args.workspaceId || getWorkspaceId();
6858
6923
  const name2 = z.string().min(1).max(200).parse(args.name);
6859
- const stepsVersion = args.stepsVersion !== undefined ? z.union([z.literal(1), z.literal(2)]).parse(args.stepsVersion) : undefined;
6860
6924
  const result = await client3.createPlaybook({
6861
6925
  workspaceId,
6862
6926
  name: name2,
6863
6927
  description: args.description,
6864
- steps: args.steps,
6865
- stepsVersion
6928
+ steps: args.steps
6866
6929
  });
6867
6930
  return { success: true, playbook: result.playbook };
6868
6931
  }
@@ -6877,14 +6940,8 @@ async function handleToolCall(name, args, deps) {
6877
6940
  });
6878
6941
  return { success: true, playbook: result.playbook };
6879
6942
  }
6880
- case "harmony_save_card_as_playbook": {
6881
- const cardId = z.string().uuid().parse(args.cardId);
6882
- const result = await client3.savePlaybookFromCard({
6883
- cardId,
6884
- name: args.name
6885
- });
6886
- return { success: true, playbook: result.playbook };
6887
- }
6943
+ case "harmony_save_card_as_playbook":
6944
+ return deprecatedRemovedToolResult("harmony_save_card_as_playbook");
6888
6945
  case "harmony_signup": {
6889
6946
  const email = z.string().email().max(254).parse(args.email);
6890
6947
  const password = z.string().min(8).max(128).parse(args.password);