@desplega.ai/agent-swarm 1.102.0 → 1.103.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 (107) hide show
  1. package/dist/{actions-j9399ktm.js → actions-7txktrdj.js} +6 -6
  2. package/dist/{app-tnq1pz27.js → app-xm3xsdye.js} +3 -3
  3. package/dist/{assistant-c7y92gkw.js → assistant-qpv63wk6.js} +9 -9
  4. package/dist/{boot-reembed-k7e6fc1p.js → boot-reembed-9cxqka3e.js} +3 -3
  5. package/dist/{boot-reembed-b97r699t.js → boot-reembed-chxhrwv8.js} +4 -4
  6. package/dist/{boot-scrub-logs-14kbc61y.js → boot-scrub-logs-8qtfkcz8.js} +2 -2
  7. package/dist/{claude-managed-adapter-g07dbz3b.js → claude-managed-adapter-z29hjgzz.js} +3 -3
  8. package/dist/{cli-tsz1q7gk.js → cli-2307phk1.js} +375 -23
  9. package/dist/{cli-g5px3d7x.js → cli-2957pqpj.js} +24 -1
  10. package/dist/{cli-9qascwj7.js → cli-2qynerth.js} +1 -1
  11. package/dist/{cli-k1ghyj8b.js → cli-4fnp2tc3.js} +1 -1
  12. package/dist/{cli-hc4rd6sv.js → cli-85pf4z38.js} +1 -1
  13. package/dist/{cli-zk4gn40w.js → cli-8hyv6c2v.js} +6 -14
  14. package/dist/{cli-k81y1cjb.js → cli-8w95v19d.js} +1 -1
  15. package/dist/{cli-7xdq3jg3.js → cli-92fqyjcv.js} +1 -1
  16. package/dist/{cli-zctkpdmd.js → cli-dz3d6zjg.js} +1 -1
  17. package/dist/{cli-d0t4854c.js → cli-fedn86nz.js} +213 -476
  18. package/dist/{cli-g861fwjp.js → cli-fwxdt7kt.js} +3 -3
  19. package/dist/{cli-2aaa9mqr.js → cli-fygaw191.js} +2 -2
  20. package/dist/{cli-qbc6mncp.js → cli-gzepjz54.js} +2 -2
  21. package/dist/{cli-r68m8arr.js → cli-jt9d9fjw.js} +1 -1
  22. package/dist/{cli-axtg1zaa.js → cli-khe4x17a.js} +1 -1
  23. package/dist/{cli-sygq3fhn.js → cli-kk51f3tf.js} +5 -5
  24. package/dist/{cli-dgab5x2h.js → cli-krvxq8bc.js} +1 -1
  25. package/dist/{cli-xde8vhhp.js → cli-ksq38x4n.js} +2 -2
  26. package/dist/{cli-stqqf5kh.js → cli-m8f6qzck.js} +1 -1
  27. package/dist/{cli-ap4md0gc.js → cli-ns0r7mkr.js} +30 -5
  28. package/dist/{cli-t2jk33ta.js → cli-rttgde5f.js} +18 -6
  29. package/dist/{cli-mhj07xbq.js → cli-rvae030h.js} +3 -3
  30. package/dist/{cli-q6jzg6wj.js → cli-s14sb64w.js} +4 -4
  31. package/dist/{cli-zmf7refz.js → cli-v9283nma.js} +1 -1
  32. package/dist/{cli-fjx4x7nn.js → cli-xmtsan9k.js} +1 -1
  33. package/dist/{cli-vgck9mqq.js → cli-yeemwevj.js} +155 -37
  34. package/dist/{cli-5bqa41fd.js → cli-yfwwjeft.js} +1 -1
  35. package/dist/{cli-s8jw5bmg.js → cli-yp2krx5r.js} +2 -2
  36. package/dist/cli.js +9 -9
  37. package/dist/{codex-adapter-wf119mnw.js → codex-adapter-w0xharsg.js} +3 -3
  38. package/dist/{codex-session-runner-1gknw7mh.js → codex-session-runner-kzbjcm2k.js} +3 -3
  39. package/dist/{commands-2wyn3swb.js → commands-aqn63mcw.js} +2 -2
  40. package/dist/{db-567ntpxv.js → db-azrvwsnj.js} +2 -2
  41. package/dist/{handlers-wvwpk2kj.js → handlers-hf61har3.js} +9 -9
  42. package/dist/{hook-fatkywc0.js → hook-306p3yz4.js} +2 -2
  43. package/dist/{http-eq6wxve6.js → http-zts4haq7.js} +81 -62
  44. package/dist/{index-400x6p3j.js → index-29vg51x6.js} +6 -6
  45. package/dist/{index-9wqh5352.js → index-3c651yfk.js} +10 -10
  46. package/dist/{index-6aj8966p.js → index-bfh9hgek.js} +5 -5
  47. package/dist/{index-d1rm4ea3.js → index-h2yqjyk0.js} +8 -8
  48. package/dist/{keepalive-6x9nx92g.js → keepalive-a0dg5pds.js} +4 -4
  49. package/dist/{lead-5fgqwe13.js → lead-7mcxedkc.js} +21 -21
  50. package/dist/{maintenance-eajx5k1k.js → maintenance-mrzqd53t.js} +4 -4
  51. package/dist/{onboard-jc1w1edg.js → onboard-4dkpv9wa.js} +2 -2
  52. package/dist/{otel-impl-8k375amf.js → otel-impl-jt7gpyp9.js} +1 -1
  53. package/dist/{pi-mono-adapter-hf8sw7hs.js → pi-mono-adapter-zf7dt1wg.js} +1 -1
  54. package/dist/{pricing-refresh-zed6916t.js → pricing-refresh-xqnzh8dt.js} +4 -4
  55. package/dist/{seed-pricing-5805qzkz.js → seed-pricing-56tjm1fj.js} +3 -3
  56. package/dist/{setup-wz05vxh0.js → setup-tbwk8j10.js} +2 -2
  57. package/dist/{worker-j359graf.js → worker-e03za92t.js} +21 -21
  58. package/openapi.json +70 -1
  59. package/package.json +1 -1
  60. package/src/be/db.ts +83 -13
  61. package/src/be/migrations/098_repo_hooks_config.sql +3 -0
  62. package/src/be/task-lifecycle-events.ts +42 -0
  63. package/src/commands/runner.ts +174 -25
  64. package/src/github/task-reactions.ts +21 -0
  65. package/src/hooks/tool-loop-detection.test.ts +40 -0
  66. package/src/hooks/tool-loop-detection.ts +37 -1
  67. package/src/http/agents.ts +7 -0
  68. package/src/http/index.ts +16 -2
  69. package/src/http/prompt-templates.ts +1 -1
  70. package/src/http/repos.ts +5 -1
  71. package/src/http/schedules.ts +1 -1
  72. package/src/http/session-data.ts +2 -0
  73. package/src/http/tasks.ts +2 -1
  74. package/src/http/workflows.ts +1 -0
  75. package/src/prompts/resolver.ts +1 -1
  76. package/src/scheduler/scheduler.ts +25 -2
  77. package/src/server-runtime-counters.ts +12 -0
  78. package/src/server-user.ts +1 -2
  79. package/src/server.ts +5 -0
  80. package/src/telemetry.ts +18 -1
  81. package/src/tests/http-api-integration.test.ts +4 -0
  82. package/src/tests/model-control.test.ts +6 -6
  83. package/src/tests/swarm-repos.test.ts +25 -2
  84. package/src/tests/task-lifecycle-events.test.ts +53 -0
  85. package/src/tests/task-lifecycle-telemetry.test.ts +37 -1
  86. package/src/tests/telemetry-init.test.ts +32 -0
  87. package/src/tools/prompt-templates/preview.ts +1 -1
  88. package/src/tools/repos/update-repo.ts +6 -1
  89. package/src/tools/schedules/create-schedule.ts +1 -1
  90. package/src/tools/schedules/update-schedule.ts +1 -1
  91. package/src/tools/send-task.ts +7 -2
  92. package/src/tools/task-action.ts +6 -2
  93. package/src/types.ts +155 -1
  94. package/src/utils/template.ts +127 -0
  95. package/src/workflows/engine.ts +7 -0
  96. package/src/workflows/executors/agent-task.ts +1 -2
  97. package/src/workflows/template.ts +12 -121
  98. package/src/workflows/triggers.ts +6 -2
  99. package/templates/community/code-health-reports/PLAYBOOK.md +355 -0
  100. package/templates/community/code-health-reports/README.md +38 -0
  101. package/templates/community/code-health-reports/lead-prompt.md +52 -0
  102. package/templates/community/code-health-reports/report.mjs +378 -0
  103. package/templates/community/code-health-reports/run.sh +143 -0
  104. package/templates/schedules/weekly-code-health-reports/config.json +13 -0
  105. package/templates/schedules/weekly-code-health-reports/content.md +60 -0
  106. package/src/model-tiers.ts +0 -140
  107. package/templates/community/.gitkeep +0 -0
@@ -13,13 +13,13 @@ import {
13
13
  ensureTokenOrThrow,
14
14
  getOAuthApp,
15
15
  getOAuthTokens
16
- } from "./cli-zmf7refz.js";
16
+ } from "./cli-v9283nma.js";
17
17
  import {
18
18
  startPricingRefreshLoop
19
- } from "./cli-xde8vhhp.js";
19
+ } from "./cli-ksq38x4n.js";
20
20
  import {
21
21
  seedPricingFromModelsDev
22
- } from "./cli-stqqf5kh.js";
22
+ } from "./cli-m8f6qzck.js";
23
23
  import {
24
24
  createTrackerAgentMapping,
25
25
  createTrackerSync,
@@ -27,13 +27,13 @@ import {
27
27
  deleteTrackerSync,
28
28
  getAllTrackerSyncs,
29
29
  repointTrackerSyncBySwarmId
30
- } from "./cli-k1ghyj8b.js";
30
+ } from "./cli-4fnp2tc3.js";
31
31
  import {
32
32
  cancelWorkflowRun,
33
33
  getExecutorRegistry,
34
34
  retryFailedRun,
35
35
  snapshotWorkflow
36
- } from "./cli-zk4gn40w.js";
36
+ } from "./cli-8hyv6c2v.js";
37
37
  import {
38
38
  getApiKey
39
39
  } from "./cli-f14fvzag.js";
@@ -47,7 +47,7 @@ import {
47
47
  getEmbeddingProvider,
48
48
  getMemoryStore,
49
49
  init_constants
50
- } from "./cli-k81y1cjb.js";
50
+ } from "./cli-8w95v19d.js";
51
51
  import {
52
52
  ZodFirstPartyTypeKind,
53
53
  objectType
@@ -57,7 +57,7 @@ import {
57
57
  ensure,
58
58
  require_dist,
59
59
  runScheduleNow
60
- } from "./cli-ap4md0gc.js";
60
+ } from "./cli-ns0r7mkr.js";
61
61
  import {
62
62
  TriggerSchemaError,
63
63
  applyDefinitionPatch,
@@ -65,13 +65,13 @@ import {
65
65
  startWorkflowExecution,
66
66
  validateDefinition,
67
67
  validateJsonSchema
68
- } from "./cli-t2jk33ta.js";
68
+ } from "./cli-rttgde5f.js";
69
69
  import {
70
70
  extractSlackMessageText
71
71
  } from "./cli-2deg0cen.js";
72
72
  import {
73
73
  getSlackApp
74
- } from "./cli-sygq3fhn.js";
74
+ } from "./cli-kk51f3tf.js";
75
75
  import {
76
76
  findUserByEmail,
77
77
  findUserByExternalId,
@@ -80,7 +80,7 @@ import {
80
80
  linkIdentity,
81
81
  recordIdentityEvent,
82
82
  unlinkIdentity
83
- } from "./cli-5bqa41fd.js";
83
+ } from "./cli-yfwwjeft.js";
84
84
  import {
85
85
  markdownToSlack
86
86
  } from "./cli-z92pkwzn.js";
@@ -91,7 +91,7 @@ import {
91
91
  } from "./cli-dh55d5fg.js";
92
92
  import {
93
93
  agentContextKey
94
- } from "./cli-r68m8arr.js";
94
+ } from "./cli-jt9d9fjw.js";
95
95
  import {
96
96
  AgentLogSchema,
97
97
  AgentMemorySchema,
@@ -123,6 +123,7 @@ import {
123
123
  PromptTemplateScopeSchema,
124
124
  PromptTemplateStateSchema,
125
125
  RepoGuidelinesSchema,
126
+ RepoHooksSchema,
126
127
  ScriptRunStatusSchema,
127
128
  ServiceSchema,
128
129
  ServiceStatusSchema,
@@ -243,11 +244,11 @@ import {
243
244
  initDb,
244
245
  init_db,
245
246
  init_defaults,
246
- init_model_tiers,
247
247
  init_registry,
248
248
  init_request_auth_context,
249
249
  init_resolver,
250
250
  init_swarm_config_guard,
251
+ init_task_lifecycle_events,
251
252
  init_template,
252
253
  init_types,
253
254
  insertTaskAttachment,
@@ -266,6 +267,7 @@ import {
266
267
  maskSecrets,
267
268
  moveTaskFromBacklog,
268
269
  moveTaskToBacklog,
270
+ onTaskStarted,
269
271
  postMessage,
270
272
  reassociateSessionLogs,
271
273
  recordBudgetRefusalNotification,
@@ -302,7 +304,7 @@ import {
302
304
  upsertService,
303
305
  upsertSwarmConfig,
304
306
  validateConfigValue
305
- } from "./cli-d0t4854c.js";
307
+ } from "./cli-fedn86nz.js";
306
308
  import {
307
309
  init_zod
308
310
  } from "./cli-q21d49ac.js";
@@ -346,7 +348,7 @@ import {
346
348
  import {
347
349
  init_package,
348
350
  package_default
349
- } from "./cli-hc4rd6sv.js";
351
+ } from "./cli-85pf4z38.js";
350
352
  import {
351
353
  init_secret_scrubber,
352
354
  registerVolatileSecret,
@@ -11208,6 +11210,356 @@ var EMPTY_COMPLETION_RESULT = {
11208
11210
  init_package();
11209
11211
  init_db();
11210
11212
 
11213
+ // src/github/task-reactions.ts
11214
+ init_task_lifecycle_events();
11215
+
11216
+ // src/github/app.ts
11217
+ var initialized = false;
11218
+ var webhookSecret = null;
11219
+ var appId = null;
11220
+ var privateKey = null;
11221
+ var tokenCache = new Map;
11222
+ function isGitHubEnabled() {
11223
+ const githubDisable = process.env.GITHUB_DISABLE;
11224
+ if (githubDisable === "true" || githubDisable === "1") {
11225
+ return false;
11226
+ }
11227
+ return !!process.env.GITHUB_WEBHOOK_SECRET;
11228
+ }
11229
+ function resetGitHub() {
11230
+ initialized = false;
11231
+ webhookSecret = null;
11232
+ appId = null;
11233
+ privateKey = null;
11234
+ tokenCache.clear();
11235
+ }
11236
+ function initGitHub() {
11237
+ if (initialized) {
11238
+ console.log("[GitHub] Already initialized, skipping");
11239
+ return isGitHubEnabled();
11240
+ }
11241
+ initialized = true;
11242
+ const githubDisable = process.env.GITHUB_DISABLE;
11243
+ if (githubDisable === "true" || githubDisable === "1") {
11244
+ console.log("[GitHub] Disabled via GITHUB_DISABLE");
11245
+ return false;
11246
+ }
11247
+ webhookSecret = process.env.GITHUB_WEBHOOK_SECRET ?? null;
11248
+ if (!webhookSecret) {
11249
+ console.log("[GitHub] Missing GITHUB_WEBHOOK_SECRET, GitHub integration disabled");
11250
+ return false;
11251
+ }
11252
+ appId = process.env.GITHUB_APP_ID ?? null;
11253
+ const rawPrivateKey = process.env.GITHUB_APP_PRIVATE_KEY ?? null;
11254
+ if (appId && rawPrivateKey) {
11255
+ if (rawPrivateKey.startsWith("-----BEGIN")) {
11256
+ privateKey = rawPrivateKey.replace(/\\n/g, `
11257
+ `);
11258
+ } else {
11259
+ privateKey = Buffer.from(rawPrivateKey, "base64").toString("utf-8");
11260
+ }
11261
+ console.log("[GitHub] App credentials loaded for bot reactions");
11262
+ } else {
11263
+ console.log("[GitHub] No App credentials, bot reactions disabled");
11264
+ }
11265
+ console.log("[GitHub] Webhook handler initialized");
11266
+ return true;
11267
+ }
11268
+ function isReactionsEnabled() {
11269
+ return !!(appId && privateKey);
11270
+ }
11271
+ async function verifyWebhookSignature(payload, signature) {
11272
+ if (!webhookSecret || !signature) {
11273
+ console.log(`[GitHub] Signature verification failed: webhookSecret=${!!webhookSecret}, signature=${!!signature}`);
11274
+ return false;
11275
+ }
11276
+ if (!signature.startsWith("sha256=")) {
11277
+ return false;
11278
+ }
11279
+ const expectedSig = signature.slice(7);
11280
+ const encoder = new TextEncoder;
11281
+ const key = await crypto.subtle.importKey("raw", encoder.encode(webhookSecret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
11282
+ const signatureBuffer = await crypto.subtle.sign("HMAC", key, encoder.encode(payload));
11283
+ const computedSig = Array.from(new Uint8Array(signatureBuffer)).map((b) => b.toString(16).padStart(2, "0")).join("");
11284
+ if (computedSig.length !== expectedSig.length) {
11285
+ console.log(`[GitHub] Signature length mismatch: computed=${computedSig.length}, expected=${expectedSig.length}`);
11286
+ return false;
11287
+ }
11288
+ let result = 0;
11289
+ for (let i = 0;i < computedSig.length; i++) {
11290
+ result |= computedSig.charCodeAt(i) ^ expectedSig.charCodeAt(i);
11291
+ }
11292
+ const isValid = result === 0;
11293
+ if (!isValid) {
11294
+ console.log(`[GitHub] Signature mismatch:`);
11295
+ console.log(` Expected: ${expectedSig}`);
11296
+ console.log(` Computed: ${computedSig}`);
11297
+ }
11298
+ return isValid;
11299
+ }
11300
+ async function generateAppJWT() {
11301
+ if (!appId || !privateKey) {
11302
+ return null;
11303
+ }
11304
+ const now = Math.floor(Date.now() / 1000);
11305
+ const payload = {
11306
+ iat: now - 60,
11307
+ exp: now + 600,
11308
+ iss: appId
11309
+ };
11310
+ const base64url = (data) => {
11311
+ const base64 = Buffer.isBuffer(data) ? data.toString("base64") : Buffer.from(data).toString("base64");
11312
+ return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
11313
+ };
11314
+ const header = { alg: "RS256", typ: "JWT" };
11315
+ const headerB64 = base64url(JSON.stringify(header));
11316
+ const payloadB64 = base64url(JSON.stringify(payload));
11317
+ const signingInput = `${headerB64}.${payloadB64}`;
11318
+ try {
11319
+ const crypto2 = await import("node:crypto");
11320
+ const sign = crypto2.createSign("RSA-SHA256");
11321
+ sign.update(signingInput);
11322
+ const signature = sign.sign(privateKey);
11323
+ const signatureB64 = base64url(signature);
11324
+ return `${signingInput}.${signatureB64}`;
11325
+ } catch (error) {
11326
+ console.error("[GitHub] Failed to generate JWT:", error);
11327
+ return null;
11328
+ }
11329
+ }
11330
+ async function getInstallationToken(installationId) {
11331
+ const cached = tokenCache.get(installationId);
11332
+ if (cached && cached.expiresAt > Date.now() + 60000) {
11333
+ return cached.token;
11334
+ }
11335
+ const jwt = await generateAppJWT();
11336
+ if (!jwt) {
11337
+ console.log("[GitHub] No JWT available, cannot get installation token");
11338
+ return null;
11339
+ }
11340
+ try {
11341
+ const response = await fetch(`https://api.github.com/app/installations/${installationId}/access_tokens`, {
11342
+ method: "POST",
11343
+ headers: {
11344
+ Accept: "application/vnd.github+json",
11345
+ Authorization: `Bearer ${jwt}`,
11346
+ "X-GitHub-Api-Version": "2022-11-28"
11347
+ }
11348
+ });
11349
+ if (!response.ok) {
11350
+ const errorText = await response.text();
11351
+ console.error(`[GitHub] Failed to get installation token: ${response.status} ${errorText}`);
11352
+ return null;
11353
+ }
11354
+ const data = await response.json();
11355
+ const expiresAt = new Date(data.expires_at).getTime();
11356
+ tokenCache.set(installationId, { token: data.token, expiresAt });
11357
+ console.log(`[GitHub] Got installation token for ${installationId}, expires at ${data.expires_at}`);
11358
+ return data.token;
11359
+ } catch (error) {
11360
+ console.error("[GitHub] Error getting installation token:", error);
11361
+ return null;
11362
+ }
11363
+ }
11364
+
11365
+ // src/github/reactions.ts
11366
+ async function addReaction(repo, commentId, reaction, installationId) {
11367
+ if (!isReactionsEnabled()) {
11368
+ console.log("[GitHub] Reactions not enabled, skipping");
11369
+ return false;
11370
+ }
11371
+ const token = await getInstallationToken(installationId);
11372
+ if (!token) {
11373
+ console.log("[GitHub] No installation token, skipping reaction");
11374
+ return false;
11375
+ }
11376
+ try {
11377
+ const response = await fetch(`https://api.github.com/repos/${repo}/issues/comments/${commentId}/reactions`, {
11378
+ method: "POST",
11379
+ headers: {
11380
+ Accept: "application/vnd.github+json",
11381
+ Authorization: `Bearer ${token}`,
11382
+ "X-GitHub-Api-Version": "2022-11-28",
11383
+ "Content-Type": "application/json"
11384
+ },
11385
+ body: JSON.stringify({ content: reaction })
11386
+ });
11387
+ if (!response.ok) {
11388
+ const errorText = await response.text();
11389
+ console.error(`[GitHub] Failed to add reaction: ${response.status} ${errorText}`);
11390
+ return false;
11391
+ }
11392
+ console.log(`[GitHub] Added ${reaction} reaction to comment ${commentId}`);
11393
+ return true;
11394
+ } catch (error) {
11395
+ console.error("[GitHub] Error adding reaction:", error);
11396
+ return false;
11397
+ }
11398
+ }
11399
+ async function addIssueReaction(repo, issueNumber, reaction, installationId) {
11400
+ if (!isReactionsEnabled()) {
11401
+ console.log("[GitHub] Reactions not enabled, skipping");
11402
+ return false;
11403
+ }
11404
+ const token = await getInstallationToken(installationId);
11405
+ if (!token) {
11406
+ console.log("[GitHub] No installation token, skipping reaction");
11407
+ return false;
11408
+ }
11409
+ try {
11410
+ const response = await fetch(`https://api.github.com/repos/${repo}/issues/${issueNumber}/reactions`, {
11411
+ method: "POST",
11412
+ headers: {
11413
+ Accept: "application/vnd.github+json",
11414
+ Authorization: `Bearer ${token}`,
11415
+ "X-GitHub-Api-Version": "2022-11-28",
11416
+ "Content-Type": "application/json"
11417
+ },
11418
+ body: JSON.stringify({ content: reaction })
11419
+ });
11420
+ if (!response.ok) {
11421
+ const errorText = await response.text();
11422
+ console.error(`[GitHub] Failed to add issue reaction: ${response.status} ${errorText}`);
11423
+ return false;
11424
+ }
11425
+ console.log(`[GitHub] Added ${reaction} reaction to issue/PR #${issueNumber}`);
11426
+ return true;
11427
+ } catch (error) {
11428
+ console.error("[GitHub] Error adding issue reaction:", error);
11429
+ return false;
11430
+ }
11431
+ }
11432
+ async function addPullReviewCommentReaction(repo, commentId, reaction, installationId) {
11433
+ if (!isReactionsEnabled()) {
11434
+ console.log("[GitHub] Reactions not enabled, skipping");
11435
+ return false;
11436
+ }
11437
+ const token = await getInstallationToken(installationId);
11438
+ if (!token) {
11439
+ console.log("[GitHub] No installation token, skipping reaction");
11440
+ return false;
11441
+ }
11442
+ try {
11443
+ const response = await fetch(`https://api.github.com/repos/${repo}/pulls/comments/${commentId}/reactions`, {
11444
+ method: "POST",
11445
+ headers: {
11446
+ Accept: "application/vnd.github+json",
11447
+ Authorization: `Bearer ${token}`,
11448
+ "X-GitHub-Api-Version": "2022-11-28",
11449
+ "Content-Type": "application/json"
11450
+ },
11451
+ body: JSON.stringify({ content: reaction })
11452
+ });
11453
+ if (!response.ok) {
11454
+ const errorText = await response.text();
11455
+ console.error(`[GitHub] Failed to add PR review comment reaction: ${response.status} ${errorText}`);
11456
+ return false;
11457
+ }
11458
+ console.log(`[GitHub] Added ${reaction} reaction to PR review comment ${commentId}`);
11459
+ return true;
11460
+ } catch (error) {
11461
+ console.error("[GitHub] Error adding PR review comment reaction:", error);
11462
+ return false;
11463
+ }
11464
+ }
11465
+ async function addGraphQLReaction(nodeId, reaction, installationId) {
11466
+ if (!isReactionsEnabled()) {
11467
+ console.log("[GitHub] Reactions not enabled, skipping");
11468
+ return false;
11469
+ }
11470
+ const token = await getInstallationToken(installationId);
11471
+ if (!token) {
11472
+ console.log("[GitHub] No installation token, skipping GraphQL reaction");
11473
+ return false;
11474
+ }
11475
+ try {
11476
+ const response = await fetch("https://api.github.com/graphql", {
11477
+ method: "POST",
11478
+ headers: {
11479
+ Authorization: `Bearer ${token}`,
11480
+ "Content-Type": "application/json"
11481
+ },
11482
+ body: JSON.stringify({
11483
+ query: `mutation AddReaction($input: AddReactionInput!) {
11484
+ addReaction(input: $input) {
11485
+ reaction { content }
11486
+ }
11487
+ }`,
11488
+ variables: {
11489
+ input: { subjectId: nodeId, content: reaction }
11490
+ }
11491
+ })
11492
+ });
11493
+ if (!response.ok) {
11494
+ const errorText = await response.text();
11495
+ console.error(`[GitHub] GraphQL reaction failed: ${response.status} ${errorText}`);
11496
+ return false;
11497
+ }
11498
+ const data = await response.json();
11499
+ if (data.errors?.length) {
11500
+ console.error(`[GitHub] GraphQL reaction errors: ${JSON.stringify(data.errors)}`);
11501
+ return false;
11502
+ }
11503
+ console.log(`[GitHub] Added ${reaction} GraphQL reaction to node ${nodeId}`);
11504
+ return true;
11505
+ } catch (error) {
11506
+ console.error("[GitHub] Error adding GraphQL reaction:", error);
11507
+ return false;
11508
+ }
11509
+ }
11510
+
11511
+ // src/github/task-reactions.ts
11512
+ async function addEyesReactionOnTaskStart(task) {
11513
+ if (task.source !== "github" || task.vcsProvider !== "github")
11514
+ return;
11515
+ if (!task.vcsInstallationId)
11516
+ return;
11517
+ const installationId = task.vcsInstallationId;
11518
+ const repo = task.vcsRepo;
11519
+ if (!repo)
11520
+ return;
11521
+ try {
11522
+ switch (task.vcsEventType) {
11523
+ case "issue_comment": {
11524
+ if (task.vcsCommentId) {
11525
+ await addReaction(repo, task.vcsCommentId, "eyes", installationId);
11526
+ }
11527
+ break;
11528
+ }
11529
+ case "pull_request_review_comment": {
11530
+ if (task.vcsCommentId) {
11531
+ await addPullReviewCommentReaction(repo, task.vcsCommentId, "eyes", installationId);
11532
+ }
11533
+ break;
11534
+ }
11535
+ case "pull_request_review": {
11536
+ if (task.vcsNodeId) {
11537
+ await addGraphQLReaction(task.vcsNodeId, "EYES", installationId);
11538
+ }
11539
+ break;
11540
+ }
11541
+ case "pull_request":
11542
+ case "issues": {
11543
+ if (task.vcsNumber) {
11544
+ await addIssueReaction(repo, task.vcsNumber, "eyes", installationId);
11545
+ }
11546
+ break;
11547
+ }
11548
+ }
11549
+ } catch (error) {
11550
+ console.error("[GitHub] Failed to add eyes reaction on task start:", error);
11551
+ }
11552
+ }
11553
+ var registered = false;
11554
+ function registerGithubTaskReactions() {
11555
+ if (registered)
11556
+ return;
11557
+ registered = true;
11558
+ onTaskStarted((task) => {
11559
+ addEyesReactionOnTaskStart(task).catch(() => {});
11560
+ });
11561
+ }
11562
+
11211
11563
  // src/tools/cancel-task.ts
11212
11564
  init_zod();
11213
11565
  init_db();
@@ -11255,7 +11607,7 @@ var NOOP_SPAN = {
11255
11607
  setStatus: () => NOOP_SPAN,
11256
11608
  end: () => {}
11257
11609
  };
11258
- var initialized = false;
11610
+ var initialized2 = false;
11259
11611
  var realWithSpan;
11260
11612
  var realStartSpan;
11261
11613
  var realWithRemoteContext;
@@ -11267,11 +11619,11 @@ function isPollTracingEnabled() {
11267
11619
  return v === "1" || v === "true" || v === "yes" || v === "on";
11268
11620
  }
11269
11621
  async function initOtel(serviceRole = process.env.AGENT_ROLE || "api") {
11270
- if (!enabled || initialized)
11622
+ if (!enabled || initialized2)
11271
11623
  return;
11272
- initialized = true;
11624
+ initialized2 = true;
11273
11625
  try {
11274
- const impl = await import("./otel-impl-8k375amf.js");
11626
+ const impl = await import("./otel-impl-jt7gpyp9.js");
11275
11627
  await impl.boot(serviceRole);
11276
11628
  realWithSpan = impl.withSpan;
11277
11629
  realStartSpan = impl.startSpan;
@@ -16565,6 +16917,7 @@ var registerUpdateRepoTool = (server) => {
16565
16917
  clonePath: string().optional().describe("New clone path."),
16566
16918
  defaultBranch: string().optional().describe("New default branch."),
16567
16919
  autoClone: boolean().optional().describe("Whether to auto-clone."),
16920
+ hooks: RepoHooksSchema.nullable().optional().describe("Repository hook install config. Set { enabled: true } to opt into best-effort worker hook installation, or null to disable."),
16568
16921
  guidelines: RepoGuidelinesSchema.nullable().optional().describe("Repository guidelines: prChecks (commands before PR), mergeChecks (conditions before merge), allowMerge (default false), review (guidance for reviewers). Pass null to clear.")
16569
16922
  }),
16570
16923
  outputSchema: object({
@@ -16753,7 +17106,7 @@ function resolveHttpAuditUserId(req, callerAgentId) {
16753
17106
 
16754
17107
  // src/tools/schedules/create-schedule.ts
16755
17108
  init_db();
16756
- init_model_tiers();
17109
+ init_types();
16757
17110
  var createScheduleInputSchema = object({
16758
17111
  name: string().min(1).max(100).describe("Unique name for the schedule (e.g., 'daily-cleanup')"),
16759
17112
  taskTemplate: string().min(1).describe("The task description that will be created each time"),
@@ -17327,7 +17680,7 @@ function validateRecurringTiming(merged) {
17327
17680
  }
17328
17681
 
17329
17682
  // src/tools/schedules/update-schedule.ts
17330
- init_model_tiers();
17683
+ init_types();
17331
17684
  var updateScheduleInputSchema = object({
17332
17685
  scheduleId: string().uuid().optional().describe("Schedule ID to update"),
17333
17686
  name: string().optional().describe("Schedule name to update (alternative to ID)"),
@@ -17908,7 +18261,6 @@ function findDuplicateTask(opts) {
17908
18261
 
17909
18262
  // src/tools/send-task.ts
17910
18263
  init_types();
17911
- init_model_tiers();
17912
18264
  var sendTaskInputSchema = object({
17913
18265
  agentId: uuid().optional().describe("The agent to assign/offer task to. Omit to create unassigned task for pool."),
17914
18266
  task: string().min(1).describe("The task description to send."),
@@ -21673,7 +22025,6 @@ function emitBudgetRefusalSideEffects(ctx, inserted) {
21673
22025
  // src/tools/task-action.ts
21674
22026
  init_db();
21675
22027
  init_types();
21676
- init_model_tiers();
21677
22028
  var TaskActionSchema = _enum([
21678
22029
  "create",
21679
22030
  "claim",
@@ -23586,6 +23937,7 @@ function createServer() {
23586
23937
  initDb(process.env.DATABASE_PATH);
23587
23938
  seedPricingFromModelsDev();
23588
23939
  startPricingRefreshLoop();
23940
+ registerGithubTaskReactions();
23589
23941
  const server = new McpServer({
23590
23942
  name: package_default.name,
23591
23943
  version: package_default.version,
@@ -23868,4 +24220,4 @@ function writeSkillsToFilesystem(entries, harnessType = "all", home) {
23868
24220
  return { synced, removed, errors };
23869
24221
  }
23870
24222
 
23871
- export { DEFAULT_NEGOTIATED_PROTOCOL_VERSION, SUPPORTED_PROTOCOL_VERSIONS, isJSONRPCRequest, isJSONRPCResultResponse, isJSONRPCErrorResponse, JSONRPCMessageSchema, isInitializeRequest, McpServer, userCtx, isPollTracingEnabled, initOtel, withSpan, startSpan, withSpanContext, withRemoteContext, injectTraceContext, createToolRegistrar, cancelTaskInputSchema, cancelTaskOutputSchema, cancelTaskHandler, setCorsHeaders, parseQueryParams, getPathSegments, safeRequestUrlForLog, agentWithCapacity, parseBody, BODY_TOO_LARGE, enforceContentLengthCap, json, jsonError, triggerSchemaErrorResponse, deriveApiBaseUrl, httpServerSemconvAttributes, routeRegistry, isPublicRoute, describeRequestRoute, route, assertSelectOnlyQuery, executeReadOnlyQuery, handleDbQuery, snapshotMetric, snapshotPage, getTaskDetailsInputSchema, getTaskDetailsOutputSchema, getTaskDetailsHandler, getTasksInputSchema, getTasksOutputSchema, getTasksHandler, recordRetrievals, rerank, markKapsoMessageRead, sendKapsoReaction, getKapsoConfig, getKapsoNumberMapping, markKapsoMessageSeen, resolveHttpAuditUserId, mergeScheduleTiming, validateRecurringTiming, sendTaskOutputSchema, sendTaskHandler, parseSkillContent, ExplicitSelfDuplicateError, applyRating, shouldPersistAutomaticTaskMemory, canClaim, emitBudgetRefusalSideEffects, taskActionInputSchema, taskActionOutputSchema, taskActionHandler, hasCapability, getEnabledCapabilities, createServer, writeSkillsToFilesystem };
24223
+ export { DEFAULT_NEGOTIATED_PROTOCOL_VERSION, SUPPORTED_PROTOCOL_VERSIONS, isJSONRPCRequest, isJSONRPCResultResponse, isJSONRPCErrorResponse, JSONRPCMessageSchema, isInitializeRequest, McpServer, isGitHubEnabled, resetGitHub, initGitHub, verifyWebhookSignature, getInstallationToken, addReaction, addIssueReaction, userCtx, isPollTracingEnabled, initOtel, withSpan, startSpan, withSpanContext, withRemoteContext, injectTraceContext, createToolRegistrar, cancelTaskInputSchema, cancelTaskOutputSchema, cancelTaskHandler, setCorsHeaders, parseQueryParams, getPathSegments, safeRequestUrlForLog, agentWithCapacity, parseBody, BODY_TOO_LARGE, enforceContentLengthCap, json, jsonError, triggerSchemaErrorResponse, deriveApiBaseUrl, httpServerSemconvAttributes, routeRegistry, isPublicRoute, describeRequestRoute, route, assertSelectOnlyQuery, executeReadOnlyQuery, handleDbQuery, snapshotMetric, snapshotPage, getTaskDetailsInputSchema, getTaskDetailsOutputSchema, getTaskDetailsHandler, getTasksInputSchema, getTasksOutputSchema, getTasksHandler, recordRetrievals, rerank, markKapsoMessageRead, sendKapsoReaction, getKapsoConfig, getKapsoNumberMapping, markKapsoMessageSeen, resolveHttpAuditUserId, mergeScheduleTiming, validateRecurringTiming, sendTaskOutputSchema, sendTaskHandler, parseSkillContent, ExplicitSelfDuplicateError, applyRating, shouldPersistAutomaticTaskMemory, canClaim, emitBudgetRefusalSideEffects, taskActionInputSchema, taskActionOutputSchema, taskActionHandler, hasCapability, getEnabledCapabilities, createServer, writeSkillsToFilesystem };
@@ -3,6 +3,7 @@ var HISTORY_DIR = "/tmp/agent-swarm-tool-history";
3
3
  var MAX_HISTORY = 30;
4
4
  var REPEAT_WARNING_THRESHOLD = 8;
5
5
  var REPEAT_CRITICAL_THRESHOLD = 15;
6
+ var LOW_CARDINALITY_FILE_CHANGE_CRITICAL_THRESHOLD = 24;
6
7
  var PINGPONG_WARNING_THRESHOLD = 6;
7
8
  var PINGPONG_CRITICAL_THRESHOLD = 12;
8
9
  function hashArgs(args) {
@@ -15,6 +16,27 @@ function hashArgs(args) {
15
16
  }
16
17
  return hash.toString(36);
17
18
  }
19
+ function isCodexFileChangeArgs(toolName, toolInput) {
20
+ if (toolName !== "Edit" && toolName !== "Write" && toolName !== "Delete") {
21
+ return false;
22
+ }
23
+ const topLevelKeys = Object.keys(toolInput);
24
+ if (topLevelKeys.length !== 1 || topLevelKeys[0] !== "changes") {
25
+ return false;
26
+ }
27
+ const changes = toolInput.changes;
28
+ if (!Array.isArray(changes) || changes.length === 0) {
29
+ return false;
30
+ }
31
+ return changes.every((change) => {
32
+ if (!change || typeof change !== "object" || Array.isArray(change)) {
33
+ return false;
34
+ }
35
+ const entry = change;
36
+ const keys = Object.keys(entry).sort();
37
+ return keys.length === 2 && keys[0] === "kind" && keys[1] === "path" && typeof entry.path === "string" && (entry.kind === "add" || entry.kind === "update" || entry.kind === "delete");
38
+ });
39
+ }
18
40
  async function loadHistory(sessionKey) {
19
41
  try {
20
42
  const file = Bun.file(`${HISTORY_DIR}/${sessionKey}.json`);
@@ -39,13 +61,14 @@ async function checkToolLoop(sessionKey, toolName, toolInput) {
39
61
  return { blocked: false };
40
62
  }
41
63
  const key = `${toolName}:${argsHash}`;
64
+ const repeatCriticalThreshold = isCodexFileChangeArgs(toolName, toolInput) ? LOW_CARDINALITY_FILE_CHANGE_CRITICAL_THRESHOLD : REPEAT_CRITICAL_THRESHOLD;
42
65
  let repeatCount = 0;
43
66
  for (const record of history) {
44
67
  if (`${record.toolName}:${record.argsHash}` === key) {
45
68
  repeatCount++;
46
69
  }
47
70
  }
48
- if (repeatCount >= REPEAT_CRITICAL_THRESHOLD) {
71
+ if (repeatCount >= repeatCriticalThreshold) {
49
72
  return {
50
73
  blocked: true,
51
74
  severity: "critical",
@@ -3,7 +3,7 @@ import {
3
3
  incrKv,
4
4
  init_db,
5
5
  upsertKv
6
- } from "./cli-d0t4854c.js";
6
+ } from "./cli-fedn86nz.js";
7
7
 
8
8
  // src/be/unmapped-identities.ts
9
9
  init_db();
@@ -14,7 +14,7 @@ import {
14
14
  init_templates1 as init_templates,
15
15
  normalizeDateRequired,
16
16
  resolveTemplate
17
- } from "./cli-d0t4854c.js";
17
+ } from "./cli-fedn86nz.js";
18
18
 
19
19
  // src/be/db-queries/tracker.ts
20
20
  init_date_utils();
@@ -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.102.0",
10
+ version: "1.103.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>",