@desplega.ai/agent-swarm 1.111.0 → 1.112.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.
Files changed (69) hide show
  1. package/dist/{actions-a4crgvtd.js → actions-eckb4g8r.js} +6 -6
  2. package/dist/{app-va5mr9yw.js → app-b05sgm02.js} +3 -3
  3. package/dist/{assistant-0jm4vy8r.js → assistant-7255sbyw.js} +9 -9
  4. package/dist/{boot-reembed-50zzk97q.js → boot-reembed-y1bt3ttn.js} +4 -4
  5. package/dist/{boot-reembed-0t815mmm.js → boot-reembed-yv7awg4y.js} +3 -3
  6. package/dist/{boot-scrub-logs-r99nbmxh.js → boot-scrub-logs-ddyaeqar.js} +2 -2
  7. package/dist/{cli-aqr96mny.js → cli-0c3n0eba.js} +1 -1
  8. package/dist/{cli-dza5039b.js → cli-15598a4k.js} +299 -38
  9. package/dist/{cli-jrmjxsnk.js → cli-30d2ct3k.js} +3 -3
  10. package/dist/{cli-4f6vw5vy.js → cli-3r5y6ayj.js} +2 -2
  11. package/dist/{cli-3cm8ar1c.js → cli-52tbq17n.js} +1 -1
  12. package/dist/{cli-gchg43wt.js → cli-56s4ehzw.js} +1 -1
  13. package/dist/{cli-0d5xddf1.js → cli-709t753c.js} +1 -1
  14. package/dist/{cli-xqzvygxp.js → cli-76d1vtvq.js} +11 -2
  15. package/dist/{cli-0d404x64.js → cli-7xkwgkrs.js} +1 -1
  16. package/dist/{cli-kavm2cv5.js → cli-8f0dwca2.js} +1 -1
  17. package/dist/{cli-r3y5chyg.js → cli-94vzr3nq.js} +4 -4
  18. package/dist/{cli-4ke1amr9.js → cli-951w7c79.js} +1 -1
  19. package/dist/{cli-2b0ew5v8.js → cli-9p0qhead.js} +1 -1
  20. package/dist/{cli-z626gjqj.js → cli-a5e8n5hg.js} +1 -1
  21. package/dist/{cli-y7v6e7yr.js → cli-h9jx5mag.js} +4 -4
  22. package/dist/{cli-f1f82qgv.js → cli-hm27prrc.js} +5 -5
  23. package/dist/{cli-t8m00q3b.js → cli-pt7c7qxz.js} +3 -3
  24. package/dist/{cli-ee3fkebs.js → cli-r1btc895.js} +2 -2
  25. package/dist/{cli-6tfm5fr1.js → cli-rp8pca98.js} +1 -1
  26. package/dist/{cli-h3vhr1gj.js → cli-s9pj3c7z.js} +2 -2
  27. package/dist/{cli-8j1h9627.js → cli-tch0ypp6.js} +61 -14
  28. package/dist/{cli-1aqc46he.js → cli-tg0f2wmz.js} +5 -5
  29. package/dist/{cli-fdnpymsv.js → cli-tp7e60s3.js} +4 -2
  30. package/dist/cli.js +8 -8
  31. package/dist/{commands-t3rdvb55.js → commands-14g7taf4.js} +2 -2
  32. package/dist/{db-mjqc8hj6.js → db-3tge4jrn.js} +2 -2
  33. package/dist/{handlers-mcmg5qgc.js → handlers-sy64egg6.js} +9 -9
  34. package/dist/{hook-0p4q1tym.js → hook-rnkzw8vp.js} +1 -1
  35. package/dist/{http-gs6fk9sg.js → http-xx9xptcs.js} +33 -31
  36. package/dist/{index-w15fk28m.js → index-3wr6v2mm.js} +7 -7
  37. package/dist/{index-744yvc8x.js → index-brma88d4.js} +9 -9
  38. package/dist/{index-6zpajd9e.js → index-ha8e9xn2.js} +6 -6
  39. package/dist/{index-20mf9wvc.js → index-zfx5p959.js} +8 -8
  40. package/dist/{keepalive-3gvnbxaj.js → keepalive-rbz05zad.js} +4 -4
  41. package/dist/{lead-yjbmhj3g.js → lead-s790v7r0.js} +17 -17
  42. package/dist/{maintenance-jbvy2bfg.js → maintenance-420rmadb.js} +4 -4
  43. package/dist/{onboard-sexatejh.js → onboard-jgqamqnw.js} +2 -2
  44. package/dist/{otel-impl-sgqvcw0f.js → otel-impl-ch3n3783.js} +1 -1
  45. package/dist/{pricing-refresh-rat240yg.js → pricing-refresh-rmtrby6h.js} +4 -4
  46. package/dist/{seed-pricing-7yfaf123.js → seed-pricing-1szjzfj1.js} +3 -3
  47. package/dist/{setup-vm5vcc6c.js → setup-8jfzkyzv.js} +2 -2
  48. package/dist/{worker-02y2r0r2.js → worker-40vq0pm8.js} +17 -17
  49. package/openapi.json +1 -1
  50. package/package.json +1 -1
  51. package/src/be/scripts/typecheck.ts +2 -0
  52. package/src/commands/runner.ts +154 -8
  53. package/src/github/handlers.ts +128 -25
  54. package/src/scripts-runtime/sdk-allowlist.ts +2 -0
  55. package/src/scripts-runtime/types/swarm-sdk.d.ts +2 -0
  56. package/src/server.ts +4 -0
  57. package/src/slack/message-text.ts +25 -0
  58. package/src/tests/github-event-filter.test.ts +3 -2
  59. package/src/tests/github-handlers-inline-comments.test.ts +114 -24
  60. package/src/tests/runner-repo-autostash.test.ts +168 -1
  61. package/src/tests/slack-delete.test.ts +157 -0
  62. package/src/tests/slack-message-text.test.ts +37 -1
  63. package/src/tests/slack-update.test.ts +167 -0
  64. package/src/tests/tool-annotations.test.ts +3 -0
  65. package/src/tools/slack-delete.ts +102 -0
  66. package/src/tools/slack-reply.ts +11 -3
  67. package/src/tools/slack-start-thread.ts +2 -0
  68. package/src/tools/slack-update.ts +127 -0
  69. package/src/tools/tool-config.ts +3 -1
@@ -14,13 +14,13 @@ import {
14
14
  getOAuthApp,
15
15
  getOAuthTokens,
16
16
  upsertOAuthApp
17
- } from "./cli-3cm8ar1c.js";
17
+ } from "./cli-52tbq17n.js";
18
18
  import {
19
19
  startPricingRefreshLoop
20
- } from "./cli-ee3fkebs.js";
20
+ } from "./cli-r1btc895.js";
21
21
  import {
22
22
  seedPricingFromModelsDev
23
- } from "./cli-6tfm5fr1.js";
23
+ } from "./cli-rp8pca98.js";
24
24
  import {
25
25
  createTrackerAgentMapping,
26
26
  createTrackerSync,
@@ -35,7 +35,7 @@ import {
35
35
  repointTrackerSyncBySwarmId,
36
36
  updateTrackerSync,
37
37
  updateTrackerSyncSwarmId
38
- } from "./cli-gchg43wt.js";
38
+ } from "./cli-56s4ehzw.js";
39
39
  import {
40
40
  ZodFirstPartyTypeKind,
41
41
  objectType
@@ -45,7 +45,7 @@ import {
45
45
  ensure,
46
46
  require_dist,
47
47
  runScheduleNow
48
- } from "./cli-r3y5chyg.js";
48
+ } from "./cli-94vzr3nq.js";
49
49
  import {
50
50
  TriggerSchemaError,
51
51
  applyDefinitionPatch,
@@ -57,7 +57,7 @@ import {
57
57
  startWorkflowExecution,
58
58
  validateDefinition,
59
59
  validateJsonSchema
60
- } from "./cli-1aqc46he.js";
60
+ } from "./cli-tg0f2wmz.js";
61
61
  import {
62
62
  getApiKey
63
63
  } from "./cli-f14fvzag.js";
@@ -73,7 +73,7 @@ import {
73
73
  setScriptConnectionEnabled,
74
74
  upsertCredentialBinding,
75
75
  upsertScriptConnection
76
- } from "./cli-4f6vw5vy.js";
76
+ } from "./cli-3r5y6ayj.js";
77
77
  import {
78
78
  CANDIDATE_SET_MULTIPLIER,
79
79
  computeScore,
@@ -85,16 +85,17 @@ import {
85
85
  isGraphExpansionEnabled,
86
86
  rerank,
87
87
  rowToCandidate
88
- } from "./cli-0d5xddf1.js";
88
+ } from "./cli-709t753c.js";
89
89
  import {
90
90
  createAdditiveBuffer,
91
- extractSlackMessageText
92
- } from "./cli-xqzvygxp.js";
91
+ extractSlackMessageText,
92
+ parseSlackTs
93
+ } from "./cli-76d1vtvq.js";
93
94
  import {
94
95
  getSlackApp,
95
96
  startSlackApp,
96
97
  stopSlackApp
97
- } from "./cli-f1f82qgv.js";
98
+ } from "./cli-hm27prrc.js";
98
99
  import {
99
100
  findOrCreateUserByEmail,
100
101
  findUserByEmail,
@@ -106,7 +107,7 @@ import {
106
107
  recordIdentityEvent,
107
108
  resolveUserByToken,
108
109
  unlinkIdentity
109
- } from "./cli-0d404x64.js";
110
+ } from "./cli-7xkwgkrs.js";
110
111
  import {
111
112
  markdownToSlack
112
113
  } from "./cli-6xd0bvf8.js";
@@ -117,7 +118,7 @@ import {
117
118
  createTaskWithSiblingAwareness,
118
119
  githubContextKey,
119
120
  linearContextKey
120
- } from "./cli-aqr96mny.js";
121
+ } from "./cli-0c3n0eba.js";
121
122
  import {
122
123
  getAppUrl,
123
124
  getMcpBaseUrl,
@@ -346,7 +347,7 @@ import {
346
347
  upsertService,
347
348
  upsertSwarmConfig,
348
349
  validateConfigValue
349
- } from "./cli-4ke1amr9.js";
350
+ } from "./cli-951w7c79.js";
350
351
  import {
351
352
  init_zod
352
353
  } from "./cli-q21d49ac.js";
@@ -390,7 +391,7 @@ import {
390
391
  import {
391
392
  init_package,
392
393
  package_default
393
- } from "./cli-kavm2cv5.js";
394
+ } from "./cli-8f0dwca2.js";
394
395
  import {
395
396
  init_secret_scrubber,
396
397
  refreshSecretScrubberCache,
@@ -19672,7 +19673,7 @@ async function initOtel(serviceRole = process.env.AGENT_ROLE || "api") {
19672
19673
  return;
19673
19674
  initialized2 = true;
19674
19675
  try {
19675
- const impl = await import("./otel-impl-sgqvcw0f.js");
19676
+ const impl = await import("./otel-impl-ch3n3783.js");
19676
19677
  await impl.boot(serviceRole);
19677
19678
  realWithSpan = impl.withSpan;
19678
19679
  realStartSpan = impl.startSpan;
@@ -28942,6 +28943,87 @@ var registerSkillUpdateTool = (server) => {
28942
28943
  }
28943
28944
  });
28944
28945
  };
28946
+ // src/tools/slack-delete.ts
28947
+ init_zod();
28948
+ init_db();
28949
+ var registerSlackDeleteTool = (server) => {
28950
+ createToolRegistrar(server)("slack-delete", {
28951
+ title: "Delete a Slack message",
28952
+ description: "Deletes a Slack message that THIS bot authored (e.g. a message previously posted via `slack-post`/`slack-reply`). Cannot delete messages authored by humans or other apps. Requires lead privileges.",
28953
+ annotations: { openWorldHint: true, destructiveHint: true },
28954
+ inputSchema: object({
28955
+ channelId: string().min(1).describe("The Slack channel ID the message is in."),
28956
+ messageTs: string().min(1).describe("Timestamp of the message to delete. Accepts the dotted form (1783411554.596189), the 'p' deep-link form (p1783411554596189), or a full Slack permalink URL.")
28957
+ }),
28958
+ outputSchema: object({
28959
+ success: boolean(),
28960
+ message: string()
28961
+ })
28962
+ }, async ({ channelId, messageTs }, requestInfo, _meta) => {
28963
+ if (!requestInfo.agentId) {
28964
+ return {
28965
+ content: [{ type: "text", text: "Agent ID not found." }],
28966
+ structuredContent: { success: false, message: "Agent ID not found." }
28967
+ };
28968
+ }
28969
+ const agent = getAgentById(requestInfo.agentId);
28970
+ if (!agent) {
28971
+ return {
28972
+ content: [{ type: "text", text: "Agent not found." }],
28973
+ structuredContent: { success: false, message: "Agent not found." }
28974
+ };
28975
+ }
28976
+ if (!agent.isLead) {
28977
+ return {
28978
+ content: [{ type: "text", text: "Deleting Slack messages requires lead privileges." }],
28979
+ structuredContent: {
28980
+ success: false,
28981
+ message: "Deleting Slack messages requires lead privileges."
28982
+ }
28983
+ };
28984
+ }
28985
+ const app = getSlackApp();
28986
+ if (!app) {
28987
+ return {
28988
+ content: [{ type: "text", text: "Slack not configured." }],
28989
+ structuredContent: { success: false, message: "Slack not configured." }
28990
+ };
28991
+ }
28992
+ try {
28993
+ const ts = parseSlackTs(messageTs);
28994
+ await app.client.chat.delete({ channel: channelId, ts });
28995
+ return {
28996
+ content: [{ type: "text", text: "Message deleted successfully." }],
28997
+ structuredContent: { success: true, message: "Message deleted successfully." }
28998
+ };
28999
+ } catch (error) {
29000
+ const errorCode = error?.data?.error;
29001
+ const errorMsg = error instanceof Error ? error.message : String(error);
29002
+ let message;
29003
+ switch (errorCode) {
29004
+ case "message_not_found":
29005
+ message = "No message found at that timestamp in this channel.";
29006
+ break;
29007
+ case "cant_delete_message":
29008
+ message = "Cannot delete this message — the bot can only delete messages it authored.";
29009
+ break;
29010
+ case "channel_not_found":
29011
+ message = "Channel not found or the bot has no access.";
29012
+ break;
29013
+ case "not_in_channel":
29014
+ message = "The bot is not in that channel.";
29015
+ break;
29016
+ default:
29017
+ message = `Failed to delete message: ${errorMsg}`;
29018
+ }
29019
+ return {
29020
+ content: [{ type: "text", text: message }],
29021
+ structuredContent: { success: false, message }
29022
+ };
29023
+ }
29024
+ });
29025
+ };
29026
+
28945
29027
  // src/tools/slack-download-file.ts
28946
29028
  init_zod();
28947
29029
  init_db();
@@ -29755,7 +29837,8 @@ var registerSlackReplyTool = (server) => {
29755
29837
  }),
29756
29838
  outputSchema: object({
29757
29839
  success: boolean(),
29758
- message: string()
29840
+ message: string(),
29841
+ messageTs: string().optional()
29759
29842
  })
29760
29843
  }, async ({ inboxMessageId, taskId, message }, requestInfo, _meta) => {
29761
29844
  if (!requestInfo.agentId) {
@@ -29827,7 +29910,7 @@ var registerSlackReplyTool = (server) => {
29827
29910
  }
29828
29911
  try {
29829
29912
  const slackMessage = markdownToSlack(message);
29830
- await withAutoJoin(app.client, slackChannelId, () => app.client.chat.postMessage({
29913
+ const result = await withAutoJoin(app.client, slackChannelId, () => app.client.chat.postMessage({
29831
29914
  channel: slackChannelId,
29832
29915
  thread_ts: slackThreadTs,
29833
29916
  text: slackMessage,
@@ -29843,13 +29926,19 @@ var registerSlackReplyTool = (server) => {
29843
29926
  }
29844
29927
  ]
29845
29928
  }));
29929
+ const messageTs = result.ts;
29846
29930
  if (taskId) {
29847
29931
  markTaskSlackReplySent(taskId);
29848
29932
  console.log(`[Slack] Marked slackReplySent=1 for task ${taskId}`);
29849
29933
  }
29850
29934
  return {
29851
- content: [{ type: "text", text: "Reply sent successfully." }],
29852
- structuredContent: { success: true, message: "Reply sent successfully." }
29935
+ content: [
29936
+ {
29937
+ type: "text",
29938
+ text: `Reply sent successfully.${messageTs ? ` Message timestamp: ${messageTs}` : ""}`
29939
+ }
29940
+ ],
29941
+ structuredContent: { success: true, message: "Reply sent successfully.", messageTs }
29853
29942
  };
29854
29943
  } catch (error) {
29855
29944
  return {
@@ -29876,7 +29965,8 @@ var registerSlackStartThreadTool = (server) => {
29876
29965
  success: boolean(),
29877
29966
  message: string(),
29878
29967
  channelId: string().optional(),
29879
- ts: string().optional()
29968
+ ts: string().optional(),
29969
+ messageTs: string().optional()
29880
29970
  })
29881
29971
  }, async ({ channelId, message }, requestInfo, _meta) => {
29882
29972
  if (!requestInfo.agentId) {
@@ -29953,7 +30043,8 @@ var registerSlackStartThreadTool = (server) => {
29953
30043
  success: true,
29954
30044
  message: "Thread started successfully.",
29955
30045
  channelId: resolvedChannelId,
29956
- ts
30046
+ ts,
30047
+ messageTs: ts
29957
30048
  }
29958
30049
  };
29959
30050
  } catch (error) {
@@ -29966,6 +30057,110 @@ var registerSlackStartThreadTool = (server) => {
29966
30057
  });
29967
30058
  };
29968
30059
 
30060
+ // src/tools/slack-update.ts
30061
+ init_zod();
30062
+ init_db();
30063
+ var registerSlackUpdateTool = (server) => {
30064
+ createToolRegistrar(server)("slack-update", {
30065
+ title: "Edit a Slack message",
30066
+ description: "Edits (in place) the text of a Slack message that THIS bot authored — use it to post corrections to your own messages. Cannot edit messages authored by humans or other apps. Note: editing may reset the message's display name/icon to the app default (Slack's chat.update cannot set the crown persona). Requires lead privileges.",
30067
+ annotations: { openWorldHint: true },
30068
+ inputSchema: object({
30069
+ channelId: string().min(1).describe("The Slack channel ID the message is in."),
30070
+ messageTs: string().min(1).describe("Timestamp of the message to edit (dotted, 'p' deep-link, or full permalink URL)."),
30071
+ message: string().min(1).max(4000).describe("The new message content.")
30072
+ }),
30073
+ outputSchema: object({
30074
+ success: boolean(),
30075
+ message: string(),
30076
+ messageTs: string().optional()
30077
+ })
30078
+ }, async ({ channelId, messageTs, message }, requestInfo, _meta) => {
30079
+ if (!requestInfo.agentId) {
30080
+ return {
30081
+ content: [{ type: "text", text: "Agent ID not found." }],
30082
+ structuredContent: { success: false, message: "Agent ID not found." }
30083
+ };
30084
+ }
30085
+ const agent = getAgentById(requestInfo.agentId);
30086
+ if (!agent) {
30087
+ return {
30088
+ content: [{ type: "text", text: "Agent not found." }],
30089
+ structuredContent: { success: false, message: "Agent not found." }
30090
+ };
30091
+ }
30092
+ if (!agent.isLead) {
30093
+ return {
30094
+ content: [{ type: "text", text: "Editing Slack messages requires lead privileges." }],
30095
+ structuredContent: {
30096
+ success: false,
30097
+ message: "Editing Slack messages requires lead privileges."
30098
+ }
30099
+ };
30100
+ }
30101
+ const app = getSlackApp();
30102
+ if (!app) {
30103
+ return {
30104
+ content: [{ type: "text", text: "Slack not configured." }],
30105
+ structuredContent: { success: false, message: "Slack not configured." }
30106
+ };
30107
+ }
30108
+ try {
30109
+ const ts = parseSlackTs(messageTs);
30110
+ const slackMessage = markdownToSlack(message);
30111
+ const result = await app.client.chat.update({
30112
+ channel: channelId,
30113
+ ts,
30114
+ text: slackMessage,
30115
+ blocks: [
30116
+ {
30117
+ type: "section",
30118
+ text: {
30119
+ type: "mrkdwn",
30120
+ text: slackMessage
30121
+ }
30122
+ }
30123
+ ]
30124
+ });
30125
+ return {
30126
+ content: [{ type: "text", text: "Message updated successfully." }],
30127
+ structuredContent: {
30128
+ success: true,
30129
+ message: "Message updated successfully.",
30130
+ messageTs: result.ts
30131
+ }
30132
+ };
30133
+ } catch (error) {
30134
+ const errorCode = error?.data?.error;
30135
+ const errorMsg = error instanceof Error ? error.message : String(error);
30136
+ let message2;
30137
+ switch (errorCode) {
30138
+ case "message_not_found":
30139
+ message2 = "No message found at that timestamp in this channel.";
30140
+ break;
30141
+ case "cant_update_message":
30142
+ message2 = "Cannot edit this message — the bot can only edit messages it authored.";
30143
+ break;
30144
+ case "edit_window_closed":
30145
+ message2 = "The edit window for this message has closed.";
30146
+ break;
30147
+ case "channel_not_found":
30148
+ message2 = "Channel not found or the bot has no access.";
30149
+ break;
30150
+ case "not_in_channel":
30151
+ message2 = "The bot is not in that channel.";
30152
+ break;
30153
+ default:
30154
+ message2 = `Failed to update message: ${errorMsg}`;
30155
+ }
30156
+ return {
30157
+ content: [{ type: "text", text: message2 }],
30158
+ structuredContent: { success: false, message: message2 }
30159
+ };
30160
+ }
30161
+ });
30162
+ };
30163
+
29969
30164
  // src/tools/slack-upload-file.ts
29970
30165
  init_zod();
29971
30166
  init_db();
@@ -32308,24 +32503,25 @@ function parseNextPageLink(linkHeader) {
32308
32503
  const match = linkHeader.match(/<([^>]+)>;\s*rel="next"/);
32309
32504
  return match ? match[1] ?? null : null;
32310
32505
  }
32311
- async function fetchReviewComments(repo, prNumber, reviewId, installationId) {
32312
- const token = await getInstallationToken(installationId);
32313
- if (!token) {
32314
- return [];
32315
- }
32316
- const headers = {
32506
+ var REVIEW_COMMENTS_EMPTY_RETRY_DELAYS_MS = [1500, 3000, 3000];
32507
+ var defaultReviewCommentsRetryDelay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
32508
+ var reviewCommentsRetryDelay = defaultReviewCommentsRetryDelay;
32509
+ function buildReviewCommentsHeaders(token) {
32510
+ return {
32317
32511
  Accept: "application/vnd.github+json",
32318
32512
  Authorization: `Bearer ${token}`,
32319
32513
  "X-GitHub-Api-Version": "2022-11-28"
32320
32514
  };
32515
+ }
32516
+ async function fetchPaginatedReviewComments(initialUrl, headers) {
32321
32517
  const allComments = [];
32322
- let url = `https://api.github.com/repos/${repo}/pulls/${prNumber}/reviews/${reviewId}/comments?per_page=100`;
32518
+ let url = initialUrl;
32323
32519
  try {
32324
32520
  while (url) {
32325
32521
  const response = await fetch(url, { headers });
32326
32522
  if (!response.ok) {
32327
32523
  console.error(`[GitHub] Failed to fetch review inline comments: ${response.status}`);
32328
- return allComments;
32524
+ return { comments: allComments, degraded: true };
32329
32525
  }
32330
32526
  const page = await response.json();
32331
32527
  if (Array.isArray(page)) {
@@ -32333,11 +32529,66 @@ async function fetchReviewComments(repo, prNumber, reviewId, installationId) {
32333
32529
  }
32334
32530
  url = parseNextPageLink(response.headers.get("link"));
32335
32531
  }
32336
- return allComments;
32532
+ return { comments: allComments, degraded: false };
32337
32533
  } catch (error) {
32338
32534
  console.error("[GitHub] Error fetching review inline comments:", error);
32339
- return allComments;
32535
+ return { comments: allComments, degraded: true };
32536
+ }
32537
+ }
32538
+ async function fetchReviewScopedComments(repo, prNumber, reviewId, headers) {
32539
+ const url = `https://api.github.com/repos/${repo}/pulls/${prNumber}/reviews/${reviewId}/comments?per_page=100`;
32540
+ let result = await fetchPaginatedReviewComments(url, headers);
32541
+ if (result.degraded || result.comments.length > 0) {
32542
+ return result;
32543
+ }
32544
+ for (const delayMs of REVIEW_COMMENTS_EMPTY_RETRY_DELAYS_MS) {
32545
+ await reviewCommentsRetryDelay(delayMs);
32546
+ result = await fetchPaginatedReviewComments(url, headers);
32547
+ if (result.degraded || result.comments.length > 0) {
32548
+ return result;
32549
+ }
32340
32550
  }
32551
+ return result;
32552
+ }
32553
+ async function fetchPrLevelReviewComments(repo, prNumber, reviewId, headers) {
32554
+ const url = `https://api.github.com/repos/${repo}/pulls/${prNumber}/comments?per_page=100`;
32555
+ const result = await fetchPaginatedReviewComments(url, headers);
32556
+ return {
32557
+ comments: result.comments.filter((comment) => comment.pull_request_review_id === reviewId),
32558
+ degraded: result.degraded
32559
+ };
32560
+ }
32561
+ function dedupeReviewComments(comments) {
32562
+ const byId = new Map;
32563
+ for (const comment of comments) {
32564
+ if (!byId.has(comment.id)) {
32565
+ byId.set(comment.id, comment);
32566
+ }
32567
+ }
32568
+ return [...byId.values()];
32569
+ }
32570
+ async function fetchReviewComments(repo, prNumber, reviewId, installationId) {
32571
+ const token = await getInstallationToken(installationId);
32572
+ if (!token) {
32573
+ return { comments: [], degraded: true };
32574
+ }
32575
+ const headers = buildReviewCommentsHeaders(token);
32576
+ const scopedResult = await fetchReviewScopedComments(repo, prNumber, reviewId, headers);
32577
+ if (!scopedResult.degraded && scopedResult.comments.length > 0) {
32578
+ return scopedResult;
32579
+ }
32580
+ const fallbackResult = await fetchPrLevelReviewComments(repo, prNumber, reviewId, headers);
32581
+ const mergedComments = dedupeReviewComments([
32582
+ ...scopedResult.comments,
32583
+ ...fallbackResult.comments
32584
+ ]);
32585
+ if (fallbackResult.comments.length > 0) {
32586
+ return { comments: mergedComments, degraded: false };
32587
+ }
32588
+ return {
32589
+ comments: mergedComments,
32590
+ degraded: scopedResult.degraded || fallbackResult.degraded
32591
+ };
32341
32592
  }
32342
32593
  function buildInlineCommentsSection(comments) {
32343
32594
  if (comments.length === 0)
@@ -32359,6 +32610,14 @@ ${items.join(`
32359
32610
 
32360
32611
  `)}`;
32361
32612
  }
32613
+ function buildInlineCommentsDegradedSection(repo, prNumber) {
32614
+ return `
32615
+
32616
+ ## ⚠️ Inline comments could NOT be auto-fetched
32617
+ The automatic inline-comment fetch failed or was unverifiable while the reviewer submitted this review. Inline comments ARE the change requests. BEFORE scoping or dispatching this task you MUST fetch them yourself:
32618
+ \`gh api "repos/${repo}/pulls/${prNumber}/comments?per_page=100" --jq '.[] | {id,path,line,body}'\`
32619
+ Reply to and resolve EVERY unresolved inline thread. Do NOT dispatch off the review body alone.`;
32620
+ }
32362
32621
  async function handlePullRequestReview(event) {
32363
32622
  const { action, review, pull_request: pr, repository, sender, installation } = event;
32364
32623
  const requestedByUserId = resolveGitHubSender(sender.login, "pull_request_review", `Review on PR #${pr.number}: ${review.state}`);
@@ -32369,8 +32628,8 @@ async function handlePullRequestReview(event) {
32369
32628
  if (isDuplicate2(eventKey)) {
32370
32629
  return { created: false };
32371
32630
  }
32372
- const inlineComments = installation?.id ? await fetchReviewComments(repository.full_name, pr.number, review.id, installation.id) : [];
32373
- if (review.state === "commented" && !review.body && inlineComments.length === 0) {
32631
+ const { comments: inlineComments, degraded } = installation?.id ? await fetchReviewComments(repository.full_name, pr.number, review.id, installation.id) : { comments: [], degraded: true };
32632
+ if (review.state === "commented" && !review.body && inlineComments.length === 0 && !degraded) {
32374
32633
  return { created: false };
32375
32634
  }
32376
32635
  const existingTask = findTaskByVcs(repository.full_name, pr.number);
@@ -32384,13 +32643,13 @@ async function handlePullRequestReview(event) {
32384
32643
 
32385
32644
  Review Comment:
32386
32645
  ${review.body}` : "";
32387
- const inlineCommentsSection = buildInlineCommentsSection(inlineComments);
32646
+ const inlineCommentsSection = buildInlineCommentsSection(inlineComments) + (degraded ? buildInlineCommentsDegradedSection(repository.full_name, pr.number) : "");
32388
32647
  const relatedTaskSection = existingTask ? `Related task: ${existingTask.id}
32389
32648
  \uD83D\uDD00 Consider routing to the same agent working on the related task.
32390
32649
  ` : "";
32391
32650
  const hasInlineComments = inlineComments.length > 0;
32392
32651
  const baseReviewSuggestion = review.state === "approved" ? "\uD83D\uDCA1 Suggested: Merge the PR or wait for additional reviews" : review.state === "changes_requested" ? "\uD83D\uDCA1 Suggested: Address the requested changes and update the PR" : "\uD83D\uDCA1 Suggested: Review the feedback and respond if needed";
32393
- const reviewSuggestions = hasInlineComments ? `${baseReviewSuggestion}
32652
+ const reviewSuggestions = hasInlineComments || degraded ? `${baseReviewSuggestion}
32394
32653
  \uD83D\uDCAC Address EVERY inline comment. After pushing fixes, reply to and resolve each inline review thread on GitHub so the reviewer sees visible confirmation.` : baseReviewSuggestion;
32395
32654
  const result = resolveTemplate("github.pull_request.review_submitted", {
32396
32655
  review_emoji: emoji,
@@ -33171,7 +33430,7 @@ async function notifySlack(text) {
33171
33430
  return;
33172
33431
  }
33173
33432
  try {
33174
- const { getSlackApp: getSlackApp2 } = await import("./app-va5mr9yw.js");
33433
+ const { getSlackApp: getSlackApp2 } = await import("./app-b05sgm02.js");
33175
33434
  const app = getSlackApp2();
33176
33435
  if (!app) {
33177
33436
  console.warn("[Jira webhook keepalive] Slack not available, cannot send notification");
@@ -33240,7 +33499,7 @@ async function deleteJiraWebhook(webhookId) {
33240
33499
  console.log(`[Jira webhook keepalive] Deleted webhook id=${webhookId}`);
33241
33500
  }
33242
33501
  async function overwriteWebhookIds(next) {
33243
- const { getDb: getDb2 } = await import("./db-mjqc8hj6.js");
33502
+ const { getDb: getDb2 } = await import("./db-3tge4jrn.js");
33244
33503
  const db = getDb2();
33245
33504
  const txn = db.transaction(() => {
33246
33505
  const row = db.query("SELECT metadata FROM oauth_apps WHERE provider = 'jira'").get();
@@ -110292,6 +110551,8 @@ function createServer() {
110292
110551
  registerSlackListChannelsTool(server);
110293
110552
  registerSlackUploadFileTool(server);
110294
110553
  registerSlackDownloadFileTool(server);
110554
+ registerSlackDeleteTool(server);
110555
+ registerSlackUpdateTool(server);
110295
110556
  registerRegisterAgentmailInboxTool(server);
110296
110557
  registerRegisterKapsoNumberTool(server);
110297
110558
  registerUnregisterKapsoNumberTool(server);
@@ -1,16 +1,16 @@
1
1
  import {
2
2
  recordUnmappedIdentity
3
- } from "./cli-2b0ew5v8.js";
3
+ } from "./cli-9p0qhead.js";
4
4
  import {
5
5
  findOrCreateUserByEmail,
6
6
  findUserByExternalId,
7
7
  linkIdentity
8
- } from "./cli-0d404x64.js";
8
+ } from "./cli-7xkwgkrs.js";
9
9
  import {
10
10
  getKv,
11
11
  init_db,
12
12
  upsertKv
13
- } from "./cli-4ke1amr9.js";
13
+ } from "./cli-951w7c79.js";
14
14
 
15
15
  // src/slack/enrich.ts
16
16
  init_db();
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  embedScript
3
- } from "./cli-h3vhr1gj.js";
3
+ } from "./cli-s9pj3c7z.js";
4
4
  import {
5
5
  computeContentHash,
6
6
  decryptSecret,
@@ -10,7 +10,7 @@ import {
10
10
  getSwarmConfigs,
11
11
  init_crypto,
12
12
  init_db
13
- } from "./cli-4ke1amr9.js";
13
+ } from "./cli-951w7c79.js";
14
14
  import {
15
15
  init_zod
16
16
  } from "./cli-q21d49ac.js";
@@ -3,7 +3,7 @@ import {
3
3
  init_date_utils,
4
4
  init_db,
5
5
  normalizeDateRequired
6
- } from "./cli-4ke1amr9.js";
6
+ } from "./cli-951w7c79.js";
7
7
 
8
8
  // src/be/db-queries/oauth.ts
9
9
  init_date_utils();
@@ -17,7 +17,7 @@ import {
17
17
  init_templates1 as init_templates,
18
18
  normalizeDateRequired,
19
19
  resolveTemplate
20
- } from "./cli-4ke1amr9.js";
20
+ } from "./cli-951w7c79.js";
21
21
 
22
22
  // src/be/db-queries/tracker.ts
23
23
  init_date_utils();
@@ -6,7 +6,7 @@ import {
6
6
  getDb,
7
7
  init_db,
8
8
  isSqliteVecAvailable
9
- } from "./cli-4ke1amr9.js";
9
+ } from "./cli-951w7c79.js";
10
10
  import {
11
11
  __esm,
12
12
  __export,
@@ -3,7 +3,7 @@ import {
3
3
  getAgentWorkingOnThread,
4
4
  getAllAgents,
5
5
  init_db
6
- } from "./cli-4ke1amr9.js";
6
+ } from "./cli-951w7c79.js";
7
7
 
8
8
  // src/slack/router.ts
9
9
  init_db();
@@ -181,6 +181,15 @@ ${bodyText}` : topText;
181
181
  }
182
182
  return bodyText || topText;
183
183
  }
184
+ function parseSlackTs(input) {
185
+ const trimmed = input.trim();
186
+ if (/^\d{6,}\.\d{1,}$/.test(trimmed))
187
+ return trimmed;
188
+ const m = trimmed.match(/p?(\d{10})(\d{6})(?:\D|$)/);
189
+ if (m)
190
+ return `${m[1]}.${m[2]}`;
191
+ return trimmed;
192
+ }
184
193
 
185
194
  // src/tasks/additive-buffer.ts
186
195
  function createAdditiveBuffer(options) {
@@ -252,4 +261,4 @@ function createAdditiveBuffer(options) {
252
261
  };
253
262
  }
254
263
 
255
- export { hasOtherUserMention, routeMessage, extractTaskFromMessage, extractSlackMessageText, createAdditiveBuffer };
264
+ export { hasOtherUserMention, routeMessage, extractTaskFromMessage, extractSlackMessageText, parseSlackTs, createAdditiveBuffer };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  getDb,
3
3
  init_db
4
- } from "./cli-4ke1amr9.js";
4
+ } from "./cli-951w7c79.js";
5
5
 
6
6
  // src/be/users.ts
7
7
  init_db();
@@ -7,7 +7,7 @@ var package_default;
7
7
  var init_package = __esm(() => {
8
8
  package_default = {
9
9
  name: "@desplega.ai/agent-swarm",
10
- version: "1.111.0",
10
+ version: "1.112.0",
11
11
  description: "Multi-agent orchestration for Claude Code, Codex, Gemini CLI, and other AI coding assistants",
12
12
  license: "MIT",
13
13
  author: "desplega.sh <contact@desplega.sh>",