@desplega.ai/agent-swarm 1.102.0 → 1.104.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 (174) hide show
  1. package/README.md +2 -0
  2. package/dist/{actions-j9399ktm.js → actions-2expzt47.js} +6 -6
  3. package/dist/{app-tnq1pz27.js → app-dr8hwx7z.js} +3 -3
  4. package/dist/{assistant-c7y92gkw.js → assistant-9rhgsw7v.js} +9 -10
  5. package/dist/{boot-reembed-b97r699t.js → boot-reembed-3q1aywnz.js} +5 -4
  6. package/dist/{boot-reembed-k7e6fc1p.js → boot-reembed-9kt97g1z.js} +4 -3
  7. package/dist/{boot-scrub-logs-14kbc61y.js → boot-scrub-logs-vskq108e.js} +2 -2
  8. package/dist/{claude-managed-adapter-g07dbz3b.js → claude-managed-adapter-z29hjgzz.js} +3 -3
  9. package/dist/{cli-d0t4854c.js → cli-0jw6j755.js} +220 -478
  10. package/dist/{cli-vgck9mqq.js → cli-23zk7xqy.js} +171 -49
  11. package/dist/{cli-g5px3d7x.js → cli-2957pqpj.js} +24 -1
  12. package/dist/{cli-zmf7refz.js → cli-2g9jf9ek.js} +1 -1
  13. package/dist/{cli-mhj07xbq.js → cli-2hap19dv.js} +3 -3
  14. package/dist/{cli-wjyyv518.js → cli-3j01d33e.js} +9 -14
  15. package/dist/{cli-zk4gn40w.js → cli-54h5daha.js} +41 -31
  16. package/dist/{cli-2aaa9mqr.js → cli-5pf0b84v.js} +2 -2
  17. package/dist/{cli-stqqf5kh.js → cli-7v4y3hyd.js} +1 -1
  18. package/dist/{cli-7xdq3jg3.js → cli-92fqyjcv.js} +1 -1
  19. package/dist/{cli-g861fwjp.js → cli-d4z1csms.js} +30 -5
  20. package/dist/{cli-r68m8arr.js → cli-e7bwjntj.js} +1 -1
  21. package/dist/{cli-q6jzg6wj.js → cli-etvgz5x8.js} +5 -7
  22. package/dist/{cli-9qascwj7.js → cli-gj2n2ec1.js} +1 -1
  23. package/dist/{cli-axtg1zaa.js → cli-khe4x17a.js} +1 -1
  24. package/dist/{cli-zctkpdmd.js → cli-nzc0h54v.js} +121 -2
  25. package/dist/{cli-k81y1cjb.js → cli-p4cj4vhc.js} +351 -24
  26. package/dist/{cli-fjx4x7nn.js → cli-q5tshxd6.js} +1 -1
  27. package/dist/{http-eq6wxve6.js → cli-qvzgncd8.js} +85356 -77793
  28. package/dist/{cli-k1ghyj8b.js → cli-s42f54ja.js} +1 -1
  29. package/dist/{cli-dgab5x2h.js → cli-t9v14cnd.js} +23 -4
  30. package/dist/{cli-qbc6mncp.js → cli-v1syqkcb.js} +900 -352
  31. package/dist/{cli-xde8vhhp.js → cli-w1nfskkg.js} +2 -2
  32. package/dist/{cli-ap4md0gc.js → cli-y9p3e94h.js} +30 -5
  33. package/dist/{cli-hc4rd6sv.js → cli-yjt8a36k.js} +3 -3
  34. package/dist/{cli-s8jw5bmg.js → cli-yp2krx5r.js} +2 -2
  35. package/dist/{cli-5bqa41fd.js → cli-yvektv79.js} +1 -1
  36. package/dist/{cli-sygq3fhn.js → cli-zaxksbgj.js} +5 -5
  37. package/dist/{cli-t2jk33ta.js → cli-zghef0em.js} +18 -6
  38. package/dist/cli.js +11 -11
  39. package/dist/{codex-adapter-wf119mnw.js → codex-adapter-w0xharsg.js} +3 -3
  40. package/dist/{codex-session-runner-1gknw7mh.js → codex-session-runner-kzbjcm2k.js} +3 -3
  41. package/dist/{commands-2wyn3swb.js → commands-wfrkef57.js} +2 -2
  42. package/dist/{db-567ntpxv.js → db-c42pxt7h.js} +2 -2
  43. package/dist/{handlers-wvwpk2kj.js → handlers-n36zkfvx.js} +10 -12
  44. package/dist/{hook-fatkywc0.js → hook-gw3vhv6s.js} +8 -6
  45. package/dist/http-59ygpn2h.js +17263 -0
  46. package/dist/{index-d1rm4ea3.js → index-881c1baf.js} +9 -8
  47. package/dist/{index-400x6p3j.js → index-jca7jdpt.js} +23 -10
  48. package/dist/{index-6aj8966p.js → index-xf6fp4y1.js} +5 -5
  49. package/dist/{index-9wqh5352.js → index-xz8vaa5c.js} +14 -11
  50. package/dist/{keepalive-6x9nx92g.js → keepalive-mj4302vw.js} +4 -4
  51. package/dist/{lead-5fgqwe13.js → lead-qm0xjvkv.js} +23 -23
  52. package/dist/{maintenance-eajx5k1k.js → maintenance-xppxbe4t.js} +5 -4
  53. package/dist/{onboard-jc1w1edg.js → onboard-ejx6yxh8.js} +2 -2
  54. package/dist/{otel-impl-8k375amf.js → otel-impl-zs5exkpk.js} +1 -1
  55. package/dist/{pi-mono-adapter-hf8sw7hs.js → pi-mono-adapter-zf7dt1wg.js} +1 -1
  56. package/dist/{pricing-refresh-zed6916t.js → pricing-refresh-nqyf8bkb.js} +4 -4
  57. package/dist/{seed-pricing-5805qzkz.js → seed-pricing-w3x550cj.js} +3 -3
  58. package/dist/{setup-wz05vxh0.js → setup-7729ktfj.js} +2 -2
  59. package/dist/{worker-j359graf.js → worker-4ttemwhw.js} +23 -23
  60. package/openapi.json +159 -4
  61. package/package.json +3 -3
  62. package/src/be/db.ts +83 -13
  63. package/src/be/memory/constants.ts +6 -0
  64. package/src/be/memory/providers/sqlite-store.ts +468 -71
  65. package/src/be/memory/raters/retrieval.ts +4 -2
  66. package/src/be/memory/reranker.ts +5 -2
  67. package/src/be/memory/retrieval-store.ts +2 -0
  68. package/src/be/memory/types.ts +46 -0
  69. package/src/be/migrations/098_repo_hooks_config.sql +3 -0
  70. package/src/be/migrations/099_memory_structured_key_versioning.sql +41 -0
  71. package/src/be/migrations/100_memory_retrieval_source.sql +8 -0
  72. package/src/be/migrations/101_script_connections.sql +67 -0
  73. package/src/be/script-connections.ts +744 -0
  74. package/src/be/script-credential-broker.ts +38 -0
  75. package/src/be/scripts/typecheck.ts +23 -2
  76. package/src/be/seed-scripts/catalog/boot-triage.inline.ts +16 -4
  77. package/src/be/seed-scripts/catalog/boot-triage.ts +16 -4
  78. package/src/be/task-lifecycle-events.ts +42 -0
  79. package/src/commands/runner.ts +182 -31
  80. package/src/github/task-reactions.ts +21 -0
  81. package/src/heartbeat/heartbeat.ts +42 -1
  82. package/src/heartbeat/index.ts +7 -1
  83. package/src/hooks/tool-loop-detection.test.ts +40 -0
  84. package/src/hooks/tool-loop-detection.ts +37 -1
  85. package/src/http/agents.ts +7 -0
  86. package/src/http/index.ts +16 -2
  87. package/src/http/memory.ts +127 -14
  88. package/src/http/prompt-templates.ts +1 -1
  89. package/src/http/repos.ts +5 -1
  90. package/src/http/schedules.ts +1 -1
  91. package/src/http/scripts.ts +15 -14
  92. package/src/http/session-data.ts +2 -0
  93. package/src/http/tasks.ts +2 -1
  94. package/src/http/workflows.ts +1 -0
  95. package/src/prompts/resolver.ts +1 -1
  96. package/src/scheduler/scheduler.ts +25 -2
  97. package/src/scripts-runtime/api-client.ts +100 -0
  98. package/src/scripts-runtime/api-types.ts +45 -0
  99. package/src/scripts-runtime/credential-broker/README.md +40 -0
  100. package/src/scripts-runtime/credential-broker/broker.ts +51 -0
  101. package/src/scripts-runtime/credential-broker/default-bindings.ts +19 -0
  102. package/src/scripts-runtime/credential-broker/fetch-patch.ts +102 -0
  103. package/src/scripts-runtime/credential-broker/index.ts +17 -0
  104. package/src/scripts-runtime/credential-broker/store.ts +72 -0
  105. package/src/scripts-runtime/credential-broker/types.ts +54 -0
  106. package/src/scripts-runtime/ctx.ts +11 -1
  107. package/src/scripts-runtime/egress-secrets.ts +14 -76
  108. package/src/scripts-runtime/eval-harness.ts +1 -1
  109. package/src/scripts-runtime/executors/types.ts +5 -5
  110. package/src/scripts-runtime/loader.ts +6 -1
  111. package/src/scripts-runtime/sdk-allowlist.ts +1 -0
  112. package/src/scripts-runtime/types/stdlib.d.ts +11 -0
  113. package/src/scripts-runtime/types/swarm-sdk.d.ts +11 -0
  114. package/src/server-runtime-counters.ts +12 -0
  115. package/src/server-user.ts +1 -2
  116. package/src/server.ts +11 -0
  117. package/src/telemetry.ts +18 -1
  118. package/src/tests/credential-broker.test.ts +281 -0
  119. package/src/tests/heartbeat.test.ts +239 -0
  120. package/src/tests/http-api-integration.test.ts +4 -0
  121. package/src/tests/memory-edit.test.ts +177 -0
  122. package/src/tests/memory-hybrid.test.ts +212 -0
  123. package/src/tests/model-control.test.ts +6 -6
  124. package/src/tests/runner-repo-autostash.test.ts +39 -8
  125. package/src/tests/script-connections.test.ts +360 -0
  126. package/src/tests/scripts-http.test.ts +16 -10
  127. package/src/tests/scripts-runtime-secret-egress.test.ts +26 -5
  128. package/src/tests/swarm-config-reserved-keys.test.ts +23 -0
  129. package/src/tests/swarm-repos.test.ts +25 -2
  130. package/src/tests/task-lifecycle-events.test.ts +53 -0
  131. package/src/tests/task-lifecycle-telemetry.test.ts +37 -1
  132. package/src/tests/telemetry-init.test.ts +32 -0
  133. package/src/tests/update-schedule-mcp-tool.test.ts +83 -4
  134. package/src/tools/credential-bindings/index.ts +1 -0
  135. package/src/tools/credential-bindings/tool.ts +238 -0
  136. package/src/tools/memory-edit.ts +148 -0
  137. package/src/tools/memory-search.ts +17 -10
  138. package/src/tools/prompt-templates/preview.ts +1 -1
  139. package/src/tools/repos/update-repo.ts +6 -1
  140. package/src/tools/schedules/create-schedule.ts +1 -1
  141. package/src/tools/schedules/delete-schedule.ts +18 -8
  142. package/src/tools/schedules/update-schedule.ts +5 -10
  143. package/src/tools/script-connections/index.ts +1 -0
  144. package/src/tools/script-connections/tool.ts +167 -0
  145. package/src/tools/send-task.ts +7 -2
  146. package/src/tools/swarm-config/delete-config.ts +5 -0
  147. package/src/tools/swarm-config/set-config.ts +23 -1
  148. package/src/tools/task-action.ts +6 -2
  149. package/src/tools/tool-config.ts +6 -1
  150. package/src/types.ts +161 -1
  151. package/src/utils/template.ts +127 -0
  152. package/src/workflows/engine.ts +7 -0
  153. package/src/workflows/executors/agent-task.ts +1 -2
  154. package/src/workflows/executors/swarm-script.ts +2 -0
  155. package/src/workflows/template.ts +12 -121
  156. package/src/workflows/triggers.ts +6 -2
  157. package/templates/community/code-health-reports/PLAYBOOK.md +355 -0
  158. package/templates/community/code-health-reports/README.md +38 -0
  159. package/templates/community/code-health-reports/lead-prompt.md +52 -0
  160. package/templates/community/code-health-reports/report.mjs +378 -0
  161. package/templates/community/code-health-reports/run.sh +143 -0
  162. package/templates/community/dora-metrics/PLAYBOOK.md +294 -0
  163. package/templates/community/dora-metrics/README.md +39 -0
  164. package/templates/community/dora-metrics/lead-prompt.md +56 -0
  165. package/templates/community/dora-metrics/report.mjs +393 -0
  166. package/templates/community/dora-metrics/run.sh +113 -0
  167. package/templates/schedules/weekly-code-health-reports/config.json +13 -0
  168. package/templates/schedules/weekly-code-health-reports/content.md +60 -0
  169. package/templates/schedules/weekly-dora-metrics/config.json +13 -0
  170. package/templates/schedules/weekly-dora-metrics/content.md +61 -0
  171. package/dist/cli-2deg0cen.js +0 -120
  172. package/dist/cli-tsz1q7gk.js +0 -23871
  173. package/src/model-tiers.ts +0 -140
  174. package/templates/community/.gitkeep +0 -0
@@ -1,20 +1,14 @@
1
- import {
2
- contentSha256,
3
- resolveClaudeMdPath,
4
- syncProfileFilesToServer,
5
- writeIdentityBaselines
6
- } from "./cli-wjyyv518.js";
7
1
  import {
8
2
  authJsonToCredentialSelection,
9
3
  checkCodexCredentials,
10
4
  credentialsToAuthJson
11
- } from "./cli-axtg1zaa.js";
5
+ } from "./cli-khe4x17a.js";
12
6
  import {
13
7
  loadAllCodexOAuthSlots
14
8
  } from "./cli-4wmnp8q3.js";
15
9
  import {
16
10
  checkClaudeManagedCredentials
17
- } from "./cli-s8jw5bmg.js";
11
+ } from "./cli-yp2krx5r.js";
18
12
  import {
19
13
  checkDevinCredentials
20
14
  } from "./cli-tyfxrrdd.js";
@@ -29,7 +23,7 @@ import {
29
23
  withSpan,
30
24
  withSpanContext,
31
25
  writeSkillsToFilesystem
32
- } from "./cli-tsz1q7gk.js";
26
+ } from "./cli-qvzgncd8.js";
33
27
  import {
34
28
  checkClaudeCredentials
35
29
  } from "./cli-p04j6hjv.js";
@@ -46,13 +40,20 @@ import {
46
40
  import {
47
41
  getApiKey
48
42
  } from "./cli-f14fvzag.js";
43
+ import {
44
+ contentSha256,
45
+ init_profile_sync,
46
+ resolveClaudeMdPath,
47
+ syncProfileFilesToServer,
48
+ writeIdentityBaselines
49
+ } from "./cli-3j01d33e.js";
49
50
  import {
50
51
  ensure,
51
52
  initialize
52
- } from "./cli-ap4md0gc.js";
53
+ } from "./cli-y9p3e94h.js";
53
54
  import {
54
55
  validateJsonSchema
55
- } from "./cli-t2jk33ta.js";
56
+ } from "./cli-zghef0em.js";
56
57
  import {
57
58
  getMcpBaseUrl
58
59
  } from "./cli-dh55d5fg.js";
@@ -65,7 +66,6 @@ import {
65
66
  generateDefaultToolsMd,
66
67
  initTelemetry,
67
68
  init_defaults,
68
- init_model_tiers,
69
69
  init_resolver,
70
70
  init_session_templates,
71
71
  init_telemetry,
@@ -76,7 +76,7 @@ import {
76
76
  resolveTaskModelSelection,
77
77
  resolveTemplateAsync,
78
78
  telemetry
79
- } from "./cli-d0t4854c.js";
79
+ } from "./cli-0jw6j755.js";
80
80
  import {
81
81
  init_credentials,
82
82
  resolveCredentialPools
@@ -92,7 +92,6 @@ import {
92
92
  // src/commands/runner.ts
93
93
  import { existsSync, statSync } from "node:fs";
94
94
  import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
95
- init_model_tiers();
96
95
 
97
96
  // src/prompts/base-prompt.ts
98
97
  init_resolver();
@@ -441,15 +440,15 @@ async function createProviderAdapter(provider) {
441
440
  return new ClaudeAdapter;
442
441
  }
443
442
  case "pi": {
444
- const { PiMonoAdapter } = await import("./pi-mono-adapter-hf8sw7hs.js");
443
+ const { PiMonoAdapter } = await import("./pi-mono-adapter-zf7dt1wg.js");
445
444
  return new PiMonoAdapter;
446
445
  }
447
446
  case "codex": {
448
- const { CodexAdapter } = await import("./codex-adapter-wf119mnw.js");
447
+ const { CodexAdapter } = await import("./codex-adapter-w0xharsg.js");
449
448
  return new CodexAdapter;
450
449
  }
451
450
  case "claude-managed": {
452
- const { ClaudeManagedAdapter } = await import("./claude-managed-adapter-g07dbz3b.js");
451
+ const { ClaudeManagedAdapter } = await import("./claude-managed-adapter-z29hjgzz.js");
453
452
  return new ClaudeManagedAdapter;
454
453
  }
455
454
  case "devin": {
@@ -467,6 +466,7 @@ async function createProviderAdapter(provider) {
467
466
 
468
467
  // src/commands/runner.ts
469
468
  init_telemetry();
469
+ init_types();
470
470
 
471
471
  // src/utils/budget-backoff.ts
472
472
  var BUDGET_BACKOFF_CAP_MS = 5 * 60 * 1000;
@@ -802,6 +802,9 @@ async function refreshSkillsIfChanged(ctx, lastHashRef, homeOverride) {
802
802
  return { changed: true, summary };
803
803
  }
804
804
 
805
+ // src/commands/runner.ts
806
+ init_template();
807
+
805
808
  // src/vcs/index.ts
806
809
  function detectVcsProvider(url) {
807
810
  if (url.includes("gitlab.com") || url.includes("gitlab."))
@@ -811,9 +814,6 @@ function detectVcsProvider(url) {
811
814
  return null;
812
815
  }
813
816
 
814
- // src/commands/runner.ts
815
- init_template();
816
-
817
817
  // src/commands/context-preamble.ts
818
818
  init_secret_scrubber();
819
819
  var CONTEXT_PREAMBLE_MAX_TOKENS = Number(process.env.CONTEXT_PREAMBLE_MAX_TOKENS || "2000");
@@ -1108,7 +1108,7 @@ async function checkProviderCredentials(provider, env, opts) {
1108
1108
  case "opencode":
1109
1109
  return checkOpencodeCredentials(env, opts);
1110
1110
  case "pi": {
1111
- const { checkPiMonoCredentials } = await import("./pi-mono-adapter-hf8sw7hs.js");
1111
+ const { checkPiMonoCredentials } = await import("./pi-mono-adapter-zf7dt1wg.js");
1112
1112
  return checkPiMonoCredentials(env, opts);
1113
1113
  }
1114
1114
  default:
@@ -1464,6 +1464,9 @@ async function awaitCredentials(opts) {
1464
1464
  return status;
1465
1465
  }
1466
1466
 
1467
+ // src/commands/runner.ts
1468
+ init_profile_sync();
1469
+
1467
1470
  // src/commands/resume-session.ts
1468
1471
  var RESUME_DEPRECATED_REASON = "native resume deprecated — using context preamble";
1469
1472
  function resolveResumeSession(_currentProvider, candidates) {
@@ -1540,7 +1543,7 @@ async function readClaudeMd(clonePath, role) {
1540
1543
  }
1541
1544
  async function listSwarmAutostashes(clonePath, role) {
1542
1545
  try {
1543
- const result = await Bun.$`cd ${clonePath} && git stash list --format=%gd%x09%s`.quiet();
1546
+ const result = await Bun.$`env -u GIT_DIR -u GIT_WORK_TREE -u GIT_INDEX_FILE -u GIT_PREFIX git -C ${clonePath} stash list --format=%gd%x09%s`.quiet();
1544
1547
  return result.text().split(`
1545
1548
  `).map((line) => line.trim()).filter((line) => line.includes("swarm-autostash")).flatMap((line) => {
1546
1549
  const [ref, ...messageParts] = line.split("\t");
@@ -1555,14 +1558,14 @@ async function listSwarmAutostashes(clonePath, role) {
1555
1558
  }
1556
1559
  async function refreshExistingRepoForTask(repoConfig, role) {
1557
1560
  const { name, clonePath, defaultBranch } = repoConfig;
1558
- const statusResult = await Bun.$`cd ${clonePath} && git status --porcelain`.quiet();
1561
+ const statusResult = await Bun.$`env -u GIT_DIR -u GIT_WORK_TREE -u GIT_INDEX_FILE -u GIT_PREFIX git -C ${clonePath} status --porcelain`.quiet();
1559
1562
  const statusOutput = statusResult.text().trim();
1560
1563
  let stashMessage = null;
1561
1564
  if (statusOutput !== "") {
1562
1565
  stashMessage = `swarm-autostash ${defaultBranch} ${new Date().toISOString()}`;
1563
1566
  try {
1564
1567
  console.log(`[${role}] Auto-stashing pending work in ${name}: ${stashMessage}`);
1565
- await Bun.$`cd ${clonePath} && git stash push --include-untracked -m ${stashMessage}`.quiet();
1568
+ await Bun.$`env -u GIT_DIR -u GIT_WORK_TREE -u GIT_INDEX_FILE -u GIT_PREFIX git -C ${clonePath} stash push --include-untracked -m ${stashMessage}`.quiet();
1566
1569
  console.log(`[${role}] Auto-stashed pending work in ${name}`);
1567
1570
  } catch (err) {
1568
1571
  const errorMsg = scrubSecrets(err.message);
@@ -1574,20 +1577,38 @@ async function refreshExistingRepoForTask(repoConfig, role) {
1574
1577
  console.log(`[${role}] Refreshing ${name} from origin/${defaultBranch}...`);
1575
1578
  const fetchSpec = `${defaultBranch}:refs/remotes/origin/${defaultBranch}`;
1576
1579
  const remoteRef = `refs/remotes/origin/${defaultBranch}`;
1577
- await Bun.$`cd ${clonePath} && git fetch origin ${fetchSpec}`.quiet();
1578
- await Bun.$`cd ${clonePath} && git merge --no-edit --no-stat ${remoteRef}`.quiet();
1580
+ await Bun.$`env -u GIT_DIR -u GIT_WORK_TREE -u GIT_INDEX_FILE -u GIT_PREFIX git -C ${clonePath} fetch origin ${fetchSpec}`.quiet();
1581
+ await Bun.$`env -u GIT_DIR -u GIT_WORK_TREE -u GIT_INDEX_FILE -u GIT_PREFIX git -C ${clonePath} merge --no-edit --no-stat ${remoteRef}`.quiet();
1579
1582
  console.log(`[${role}] Refreshed ${name}`);
1580
1583
  return null;
1581
1584
  } catch (err) {
1582
1585
  const errorMsg = scrubSecrets(err.message);
1583
1586
  console.warn(`[${role}] Could not refresh ${name}: ${errorMsg}`);
1584
1587
  try {
1585
- await Bun.$`cd ${clonePath} && git merge --abort`.quiet();
1588
+ await Bun.$`env -u GIT_DIR -u GIT_WORK_TREE -u GIT_INDEX_FILE -u GIT_PREFIX git -C ${clonePath} merge --abort`.quiet();
1586
1589
  } catch {}
1587
1590
  const stashNote = stashMessage ? ` Pending work was preserved in git stash "${stashMessage}".` : "";
1588
1591
  return `The repo "${name}" at ${clonePath} could not be refreshed from origin/${defaultBranch}: ${errorMsg}.${stashNote}`;
1589
1592
  }
1590
1593
  }
1594
+ function getInstallRepoHooksScriptPath() {
1595
+ const imagePath = "/usr/local/bin/install-repo-hooks.sh";
1596
+ if (existsSync(imagePath))
1597
+ return imagePath;
1598
+ return `${process.cwd()}/scripts/install-repo-hooks.sh`;
1599
+ }
1600
+ async function installRepoHooksForTask(repoConfig, role) {
1601
+ if (repoConfig.hooks?.enabled !== true)
1602
+ return;
1603
+ try {
1604
+ const scriptPath = getInstallRepoHooksScriptPath();
1605
+ await Bun.$`bash ${scriptPath} ${repoConfig.clonePath} ${repoConfig.name}`;
1606
+ console.log(`[${role}] Installed git hooks for ${repoConfig.name}`);
1607
+ } catch (err) {
1608
+ const errorMsg = scrubSecrets(err.message);
1609
+ console.warn(`[${role}] Could not install git hooks for ${repoConfig.name}: ${errorMsg}`);
1610
+ }
1611
+ }
1591
1612
  async function ensureRepoForTask(repoConfig, role) {
1592
1613
  const { url, name, clonePath, defaultBranch } = repoConfig;
1593
1614
  try {
@@ -1611,6 +1632,7 @@ async function ensureRepoForTask(repoConfig, role) {
1611
1632
  console.log(`[${role}] Repo ${name} already cloned at ${clonePath}`);
1612
1633
  warning = await refreshExistingRepoForTask({ name, clonePath, defaultBranch }, role);
1613
1634
  }
1635
+ await installRepoHooksForTask(repoConfig, role);
1614
1636
  const claudeMd = await readClaudeMd(clonePath, role);
1615
1637
  const autoStashes = await listSwarmAutostashes(clonePath, role);
1616
1638
  return { clonePath, claudeMd, warning, autoStashes };
@@ -2359,7 +2381,11 @@ function setupShutdownHandlers(role, apiConfig, getRunnerState) {
2359
2381
  }
2360
2382
  }
2361
2383
  if (apiConfig) {
2362
- telemetry.session("ended", { agentId: apiConfig.agentId });
2384
+ telemetry.session("ended", {
2385
+ agentId: apiConfig.agentId,
2386
+ durationMs: state ? Date.now() - state.startedAt : undefined,
2387
+ tasksProcessed: state?.tasksProcessed ?? 0
2388
+ });
2363
2389
  await closeAgent(apiConfig, role);
2364
2390
  }
2365
2391
  await savePm2State(role);
@@ -3023,6 +3049,18 @@ function providerEventAttributes(event) {
3023
3049
  return {};
3024
3050
  }
3025
3051
  }
3052
+ function normalizeSessionErrorCategory(category) {
3053
+ switch (category) {
3054
+ case "rate_limit":
3055
+ case "api_error":
3056
+ case "context_overflow":
3057
+ case "timeout":
3058
+ case "provider_error":
3059
+ return category;
3060
+ default:
3061
+ return "unknown";
3062
+ }
3063
+ }
3026
3064
  function implicitCloseActiveToolSpans(activeToolSpans, now = Date.now()) {
3027
3065
  let closed = 0;
3028
3066
  for (const [toolCallId, active] of activeToolSpans) {
@@ -3151,6 +3189,9 @@ async function spawnProviderProcess(adapter, opts, logDir, isYolo) {
3151
3189
  const eventFlushTimer = setInterval(flushEvents, EVENT_FLUSH_INTERVAL_MS);
3152
3190
  const sessionStartTime = Date.now();
3153
3191
  let providerSessionId = session.sessionId;
3192
+ let pendingHarnessVariant;
3193
+ let pendingHarnessVariantMeta;
3194
+ let runningTaskForSessionInit;
3154
3195
  const activeToolSpans = new Map;
3155
3196
  if (providerSessionId) {
3156
3197
  sessionSpan.setAttribute("agentswarm.provider.session_id", providerSessionId);
@@ -3159,11 +3200,34 @@ async function spawnProviderProcess(adapter, opts, logDir, isYolo) {
3159
3200
  let lastContextPostTime = 0;
3160
3201
  const CONTEXT_THROTTLE_MS = 30000;
3161
3202
  let cumulativeProgressOutputTokens = 0;
3203
+ function bufferSessionLogLine(content) {
3204
+ if (!shouldStream)
3205
+ return;
3206
+ logBuffer.lines.push(content);
3207
+ const shouldFlush = logBuffer.lines.length >= LOG_BUFFER_SIZE || Date.now() - logBuffer.lastFlush >= LOG_FLUSH_INTERVAL_MS;
3208
+ if (shouldFlush) {
3209
+ flushLogBuffer(logBuffer, {
3210
+ apiUrl: opts.apiUrl,
3211
+ apiKey: opts.apiKey,
3212
+ agentId: opts.agentId,
3213
+ sessionId: opts.runnerSessionId,
3214
+ iteration: opts.iteration,
3215
+ taskId: effectiveTaskId,
3216
+ cli: adapter.name
3217
+ }).catch(() => {});
3218
+ }
3219
+ }
3162
3220
  session.onEvent((event) => withSpanContext(sessionSpan, () => {
3163
3221
  sessionSpan.addEvent(`provider.${event.type}`, providerEventAttributes(event));
3164
3222
  switch (event.type) {
3165
3223
  case "session_init":
3166
3224
  providerSessionId = event.sessionId;
3225
+ pendingHarnessVariant = event.harnessVariant;
3226
+ pendingHarnessVariantMeta = event.harnessVariantMeta;
3227
+ if (runningTaskForSessionInit) {
3228
+ runningTaskForSessionInit.harnessVariant = event.harnessVariant;
3229
+ runningTaskForSessionInit.harnessVariantMeta = event.harnessVariantMeta;
3230
+ }
3167
3231
  sessionSpan.setAttributes({
3168
3232
  "agentswarm.provider.session_id": providerSessionId,
3169
3233
  "agentswarm.provider.name": event.provider,
@@ -3350,6 +3414,11 @@ async function spawnProviderProcess(adapter, opts, logDir, isYolo) {
3350
3414
  break;
3351
3415
  }
3352
3416
  case "compaction": {
3417
+ telemetry.compaction("triggered", {
3418
+ compactTrigger: event.compactTrigger,
3419
+ preCompactTokens: event.preCompactTokens,
3420
+ contextTotalTokens: event.contextTotalTokens
3421
+ });
3353
3422
  fetch(`${opts.apiUrl}/api/tasks/${realTaskId}/context`, {
3354
3423
  method: "POST",
3355
3424
  headers: {
@@ -3369,24 +3438,15 @@ async function spawnProviderProcess(adapter, opts, logDir, isYolo) {
3369
3438
  }
3370
3439
  case "raw_log":
3371
3440
  prettyPrintLine(event.content, opts.role);
3372
- if (shouldStream) {
3373
- logBuffer.lines.push(event.content);
3374
- const shouldFlush = logBuffer.lines.length >= LOG_BUFFER_SIZE || Date.now() - logBuffer.lastFlush >= LOG_FLUSH_INTERVAL_MS;
3375
- if (shouldFlush) {
3376
- flushLogBuffer(logBuffer, {
3377
- apiUrl: opts.apiUrl,
3378
- apiKey: opts.apiKey,
3379
- agentId: opts.agentId,
3380
- sessionId: opts.runnerSessionId,
3381
- iteration: opts.iteration,
3382
- taskId: effectiveTaskId,
3383
- cli: adapter.name
3384
- }).catch(() => {});
3385
- }
3386
- }
3441
+ bufferSessionLogLine(event.content);
3387
3442
  break;
3388
3443
  case "raw_stderr":
3389
3444
  prettyPrintStderr(event.content, opts.role);
3445
+ bufferSessionLogLine(JSON.stringify({
3446
+ type: "stderr",
3447
+ content: event.content,
3448
+ timestamp: new Date().toISOString()
3449
+ }));
3390
3450
  break;
3391
3451
  case "progress": {
3392
3452
  if (effectiveTaskId && opts.apiUrl) {
@@ -3454,6 +3514,21 @@ async function spawnProviderProcess(adapter, opts, logDir, isYolo) {
3454
3514
  "gen_ai.usage.output_tokens": result.cost.outputTokens ?? 0,
3455
3515
  "agentswarm.cost.total_usd": result.cost.totalCostUsd ?? 0
3456
3516
  });
3517
+ telemetry.session("cost", {
3518
+ agentId: opts.agentId,
3519
+ model: result.cost.model,
3520
+ provider: result.cost.provider ?? opts.harnessProvider,
3521
+ inputTokens: result.cost.inputTokens ?? 0,
3522
+ outputTokens: result.cost.outputTokens ?? 0,
3523
+ cacheReadTokens: result.cost.cacheReadTokens,
3524
+ cacheWriteTokens: result.cost.cacheWriteTokens,
3525
+ reasoningOutputTokens: result.cost.reasoningOutputTokens,
3526
+ thinkingTokens: result.cost.thinkingTokens,
3527
+ totalCostUsd: result.cost.totalCostUsd,
3528
+ durationMs: result.cost.durationMs,
3529
+ numTurns: result.cost.numTurns,
3530
+ isError: result.cost.isError
3531
+ });
3457
3532
  try {
3458
3533
  await saveCostData({ ...result.cost, taskId: realTaskId, sessionId: opts.runnerSessionId }, opts.apiUrl, opts.apiKey);
3459
3534
  } catch (err) {
@@ -3530,8 +3605,16 @@ async function spawnProviderProcess(adapter, opts, logDir, isYolo) {
3530
3605
  result: null,
3531
3606
  credentialInfo,
3532
3607
  harnessProvider: opts.harnessProvider,
3533
- hasLocalEnvironment: adapter.traits.hasLocalEnvironment
3608
+ hasLocalEnvironment: adapter.traits.hasLocalEnvironment,
3609
+ model: model || undefined
3534
3610
  };
3611
+ runningTaskForSessionInit = runningTask;
3612
+ if (pendingHarnessVariant !== undefined) {
3613
+ runningTask.harnessVariant = pendingHarnessVariant;
3614
+ }
3615
+ if (pendingHarnessVariantMeta !== undefined) {
3616
+ runningTask.harnessVariantMeta = pendingHarnessVariantMeta;
3617
+ }
3535
3618
  promise.then((r) => {
3536
3619
  runningTask.result = r;
3537
3620
  }).catch(() => {
@@ -3552,7 +3635,11 @@ async function checkCompletedProcesses(state, role, apiConfig, cancelledSignaled
3552
3635
  workingDir: task.workingDir,
3553
3636
  credentialInfo: task.credentialInfo,
3554
3637
  harnessProvider: task.harnessProvider,
3555
- hasLocalEnvironment: task.hasLocalEnvironment
3638
+ hasLocalEnvironment: task.hasLocalEnvironment,
3639
+ harnessVariant: task.harnessVariant,
3640
+ harnessVariantMeta: task.harnessVariantMeta,
3641
+ model: task.model,
3642
+ durationMs: Date.now() - task.startTime.getTime()
3556
3643
  });
3557
3644
  }
3558
3645
  }
@@ -3562,7 +3649,11 @@ async function checkCompletedProcesses(state, role, apiConfig, cancelledSignaled
3562
3649
  cursorUpdates,
3563
3650
  workingDir,
3564
3651
  credentialInfo,
3565
- harnessProvider
3652
+ harnessProvider,
3653
+ harnessVariant,
3654
+ harnessVariantMeta,
3655
+ model,
3656
+ durationMs
3566
3657
  } of completedTasks) {
3567
3658
  state.activeTasks.delete(taskId);
3568
3659
  vcsDetectedTasks.delete(taskId);
@@ -3625,6 +3716,28 @@ async function checkCompletedProcesses(state, role, apiConfig, cancelledSignaled
3625
3716
  const bridgeFailureDiagnostics = bridgeDiagnostics?.paneTail != null ? `Claude bridge final tmux pane tail (${bridgeDiagnostics.artifactPath}):
3626
3717
  ${bridgeDiagnostics.paneTail}` : undefined;
3627
3718
  await ensureTaskFinished(apiConfig, role, taskId, result.exitCode, failureReason, result.output, harnessProvider, bridgeFailureDiagnostics);
3719
+ telemetry.taskEvent("session_completed", {
3720
+ taskId,
3721
+ agentId: apiConfig.agentId,
3722
+ provider: result.cost?.provider ?? harnessProvider,
3723
+ model: result.cost?.model ?? model,
3724
+ harnessVariant,
3725
+ harnessVersion: typeof harnessVariantMeta?.version === "string" || typeof harnessVariantMeta?.version === "number" ? String(harnessVariantMeta.version) : undefined,
3726
+ exitCode: result.exitCode,
3727
+ isError: result.exitCode !== 0,
3728
+ durationMs
3729
+ });
3730
+ if (result.exitCode !== 0 || result.isError) {
3731
+ telemetry.session("failure", {
3732
+ agentId: apiConfig.agentId,
3733
+ errorCategory: normalizeSessionErrorCategory(result.errorCategory),
3734
+ provider: result.cost?.provider ?? harnessProvider,
3735
+ model: result.cost?.model ?? model,
3736
+ durationMs: result.cost?.durationMs ?? durationMs,
3737
+ wasRateLimited: result.rateLimitResetAt != null
3738
+ });
3739
+ }
3740
+ state.tasksProcessed += 1;
3628
3741
  if (result.exitCode === 0 && credentialInfo) {
3629
3742
  reportKeyClearRateLimit(apiConfig.apiUrl, apiConfig.apiKey, credentialInfo.keyType, credentialInfo.keySuffix).catch(() => {});
3630
3743
  }
@@ -3733,10 +3846,12 @@ async function runAgent(config, opts) {
3733
3846
  const swarmUrl = process.env.SWARM_URL || "localhost";
3734
3847
  const apiKey = getApiKey();
3735
3848
  let bootProvider;
3849
+ let bootModel = process.env.MODEL_OVERRIDE || "";
3736
3850
  let bootCooldownMs = resolveCodexCreditsExhaustedCooldownMs(undefined);
3737
3851
  try {
3738
3852
  const bootEnv = await fetchResolvedEnv(apiUrl, apiKey, agentId);
3739
3853
  bootProvider = bootEnv.resolvedProvider;
3854
+ bootModel = bootEnv.env.MODEL_OVERRIDE || bootModel;
3740
3855
  bootCooldownMs = resolveCodexCreditsExhaustedCooldownMs(bootEnv.env.CODEX_CREDITS_EXHAUSTED_COOLDOWN_MS);
3741
3856
  } catch (err) {
3742
3857
  console.warn(`[runner] fetchResolvedEnv failed at boot, falling back to env for provider and the default cooldown: ${err}`);
@@ -3780,7 +3895,12 @@ async function runAgent(config, opts) {
3780
3895
  } catch {}
3781
3896
  });
3782
3897
  }
3783
- telemetry.session("started", { agentId });
3898
+ telemetry.session("started", {
3899
+ agentId,
3900
+ harnessProvider: bootProvider,
3901
+ model: bootModel || undefined,
3902
+ role
3903
+ });
3784
3904
  let capabilities = config.capabilities;
3785
3905
  let agentSoulMd;
3786
3906
  let agentIdentityMd;
@@ -3889,6 +4009,8 @@ ${additionalSystemPrompt}` : basePrompt;
3889
4009
  const state = {
3890
4010
  activeTasks: new Map,
3891
4011
  maxConcurrent,
4012
+ startedAt: Date.now(),
4013
+ tasksProcessed: 0,
3892
4014
  harnessProvider: bootProvider,
3893
4015
  codexCreditsExhaustedCooldownMs: bootCooldownMs
3894
4016
  };
@@ -4309,7 +4431,7 @@ ${additionalSystemPrompt}` : basePrompt;
4309
4431
  const effectiveConfig = repoConfig ?? {
4310
4432
  url: task.vcsRepo,
4311
4433
  name: task.vcsRepo.split("/").pop() || task.vcsRepo,
4312
- clonePath: `/workspace/repos/${task.vcsRepo.split("/").pop() || task.vcsRepo}`,
4434
+ clonePath: `/workspace/personal/repos/${task.vcsRepo.split("/").pop() || task.vcsRepo}`,
4313
4435
  defaultBranch: "main"
4314
4436
  };
4315
4437
  const repoContext = await ensureRepoForTask(effectiveConfig, role);
@@ -4537,7 +4659,7 @@ ${additionalSystemPrompt}` : basePrompt;
4537
4659
  const effectiveConfig = repoConfig ?? {
4538
4660
  url: taskVcsRepo,
4539
4661
  name: taskVcsRepo.split("/").pop() || taskVcsRepo,
4540
- clonePath: `/workspace/repos/${taskVcsRepo.split("/").pop() || taskVcsRepo}`,
4662
+ clonePath: `/workspace/personal/repos/${taskVcsRepo.split("/").pop() || taskVcsRepo}`,
4541
4663
  defaultBranch: "main"
4542
4664
  };
4543
4665
  const repoResult = await ensureRepoForTask(effectiveConfig, role);
@@ -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
  init_date_utils,
4
4
  init_db,
5
5
  normalizeDateRequired
6
- } from "./cli-d0t4854c.js";
6
+ } from "./cli-0jw6j755.js";
7
7
 
8
8
  // src/be/db-queries/oauth.ts
9
9
  init_date_utils();
@@ -1,16 +1,16 @@
1
1
  import {
2
2
  recordUnmappedIdentity
3
- } from "./cli-9qascwj7.js";
3
+ } from "./cli-gj2n2ec1.js";
4
4
  import {
5
5
  findOrCreateUserByEmail,
6
6
  findUserByExternalId,
7
7
  linkIdentity
8
- } from "./cli-5bqa41fd.js";
8
+ } from "./cli-yvektv79.js";
9
9
  import {
10
10
  getKv,
11
11
  init_db,
12
12
  upsertKv
13
- } from "./cli-d0t4854c.js";
13
+ } from "./cli-0jw6j755.js";
14
14
 
15
15
  // src/slack/enrich.ts
16
16
  init_db();
@@ -2,15 +2,11 @@ import {
2
2
  init_secret_scrubber,
3
3
  scrubSecrets
4
4
  } from "./cli-3pp1362w.js";
5
+ import {
6
+ __esm
7
+ } from "./cli-p9swy5t3.js";
5
8
 
6
9
  // src/commands/profile-sync.ts
7
- init_secret_scrubber();
8
- var SOUL_MD_PATH = "/workspace/SOUL.md";
9
- var IDENTITY_MD_PATH = "/workspace/IDENTITY.md";
10
- var TOOLS_MD_PATH = "/workspace/TOOLS.md";
11
- var HEARTBEAT_MD_PATH = "/workspace/HEARTBEAT.md";
12
- var SETUP_SCRIPT_PATH = "/workspace/start-up.sh";
13
- var IDENTITY_BASELINES_PATH = "/tmp/identity-baselines.json";
14
10
  function contentSha256(content) {
15
11
  return new Bun.CryptoHasher("sha256").update(content).digest("hex");
16
12
  }
@@ -27,12 +23,6 @@ async function readIdentityBaselines(readFile = readFileIfExists) {
27
23
  return null;
28
24
  }
29
25
  }
30
- var CLAUDE_MD_PATH = `${process.env.HOME}/.claude/CLAUDE.md`;
31
- var WORKSPACE_CLAUDE_MD_PATH = "/workspace/CLAUDE.md";
32
- var IDENTITY_FILE_MIN_LENGTH = 500;
33
- var MAX_FILE_LENGTH = 65536;
34
- var SETUP_MARKER_START = "# === Agent-managed setup (from DB) ===";
35
- var SETUP_MARKER_END = "# === End agent-managed setup ===";
36
26
  function resolveClaudeMdPath(completedProviders) {
37
27
  const anyNonClaude = completedProviders.some((p) => p !== "claude");
38
28
  return anyNonClaude ? WORKSPACE_CLAUDE_MD_PATH : CLAUDE_MD_PATH;
@@ -163,5 +153,10 @@ async function syncProfileFilesToServer(opts) {
163
153
  await postProfileUpdate(opts, payload);
164
154
  }
165
155
  }
156
+ var SOUL_MD_PATH = "/workspace/SOUL.md", IDENTITY_MD_PATH = "/workspace/IDENTITY.md", TOOLS_MD_PATH = "/workspace/TOOLS.md", HEARTBEAT_MD_PATH = "/workspace/HEARTBEAT.md", SETUP_SCRIPT_PATH = "/workspace/start-up.sh", IDENTITY_BASELINES_PATH = "/tmp/identity-baselines.json", CLAUDE_MD_PATH, WORKSPACE_CLAUDE_MD_PATH = "/workspace/CLAUDE.md", IDENTITY_FILE_MIN_LENGTH = 500, MAX_FILE_LENGTH = 65536, SETUP_MARKER_START = "# === Agent-managed setup (from DB) ===", SETUP_MARKER_END = "# === End agent-managed setup ===";
157
+ var init_profile_sync = __esm(() => {
158
+ init_secret_scrubber();
159
+ CLAUDE_MD_PATH = `${process.env.HOME}/.claude/CLAUDE.md`;
160
+ });
166
161
 
167
- export { contentSha256, writeIdentityBaselines, readIdentityBaselines, resolveClaudeMdPath, syncProfileFilesToServer };
162
+ export { contentSha256, writeIdentityBaselines, readIdentityBaselines, resolveClaudeMdPath, syncProfileFilesToServer, init_profile_sync };