@desplega.ai/agent-swarm 1.171.0 → 1.172.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 (183) hide show
  1. package/dist/{acp-adapter-1z9p3fvx.js → acp-adapter-fpecx1g1.js} +23 -5
  2. package/dist/{actions-rc2yjk8a.js → actions-pw87a1a8.js} +24 -25
  3. package/dist/{amp-adapter-ypyf5s1x.js → amp-adapter-8jem2bc0.js} +1 -1
  4. package/dist/{anthropic-messages-etkbxhhy.js → anthropic-messages-8ee2f22q.js} +55 -22
  5. package/dist/{app-04ab2s0z.js → app-wmf0m843.js} +7 -8
  6. package/dist/{assistant-vhmjd8wf.js → assistant-emp1e108.js} +26 -27
  7. package/dist/{azure-openai-responses-xap57x4b.js → azure-openai-responses-ap1gwxgr.js} +3 -3
  8. package/dist/{boot-reembed-3p0m259x.js → boot-reembed-1de4f4aj.js} +6 -7
  9. package/dist/{boot-reembed-wew3gwk8.js → boot-reembed-4ymn9atn.js} +6 -7
  10. package/dist/{boot-scrub-sweep-5ytwtq24.js → boot-scrub-sweep-adhk1s8c.js} +7 -8
  11. package/dist/{boot-seeds-s8p2xry8.js → boot-seeds-8q637aew.js} +9 -10
  12. package/dist/{claude-adapter-akrzg6jh.js → claude-adapter-t26cv8a4.js} +16 -17
  13. package/dist/{claude-managed-adapter-kqqqhp2q.js → claude-managed-adapter-xgxhqrt2.js} +2 -2
  14. package/dist/{claude-sdk-session-exq1jadx.js → claude-sdk-session-gsfsh662.js} +16 -17
  15. package/dist/{cli-n8ybnv1k.js → cli-04emfe2q.js} +1 -1
  16. package/dist/{cli-w0bwte75.js → cli-104zsxba.js} +2 -1
  17. package/dist/{cli-x6z90nq9.js → cli-19njxg5g.js} +2 -4
  18. package/dist/{cli-8mr695sb.js → cli-1hgat6pa.js} +5 -5
  19. package/dist/{cli-1qtgd1h1.js → cli-1m2xxxe2.js} +2 -2
  20. package/dist/{cli-231av4y6.js → cli-1x7da0de.js} +2 -4
  21. package/dist/{cli-x3fy2tgp.js → cli-388egyf6.js} +1 -1
  22. package/dist/{cli-j0w59p15.js → cli-4fnb6884.js} +4 -4
  23. package/dist/{cli-y1j1ggbf.js → cli-4nz8p1p5.js} +1372 -588
  24. package/dist/{cli-s88my3br.js → cli-5rv1j9ga.js} +6 -6
  25. package/dist/{cli-mn8qkd8v.js → cli-61625k3x.js} +1 -1
  26. package/dist/{cli-mzj3aqac.js → cli-6jb2rf77.js} +3 -3
  27. package/dist/{cli-ec7hrgqp.js → cli-8hzfb6xz.js} +8 -8
  28. package/dist/{cli-n805bb98.js → cli-8vs9trd4.js} +1 -1
  29. package/dist/{cli-xf713zz0.js → cli-9fxpr83v.js} +1 -1
  30. package/dist/{cli-q1vnz8wv.js → cli-9s17naqf.js} +1 -1
  31. package/dist/{cli-nkjwe4z7.js → cli-9xsmdpdt.js} +59 -54
  32. package/dist/{cli-6xxxb8qt.js → cli-aspcaef3.js} +5 -5
  33. package/dist/cli-bf7485pm.js +514 -0
  34. package/dist/{cli-s9y81s7j.js → cli-bt8k5h3s.js} +625 -150
  35. package/dist/{cli-jxsgrnmq.js → cli-c2pq2rjb.js} +2 -2
  36. package/dist/{cli-13xwcqvj.js → cli-c5km5znr.js} +2 -2
  37. package/dist/{cli-5y67yrt6.js → cli-cceskcvt.js} +112 -176
  38. package/dist/{cli-ph3145b7.js → cli-cf69s3ew.js} +4 -4
  39. package/dist/{cli-k2v5x13t.js → cli-d603qf86.js} +1 -1
  40. package/dist/{cli-r455yd58.js → cli-e0a9sk00.js} +1 -1
  41. package/dist/{cli-cgj4hc12.js → cli-eh1ytktb.js} +1872 -190
  42. package/dist/{cli-erbp6b4g.js → cli-esnpehf5.js} +7 -8
  43. package/dist/cli-ev91t6xp.js +148 -0
  44. package/dist/{cli-e2prcx5m.js → cli-fdvbjtbw.js} +235 -5
  45. package/dist/{cli-62bwwc2q.js → cli-g4f5pj26.js} +3 -3
  46. package/dist/{cli-tg8q0b5y.js → cli-g6t9texm.js} +5 -5
  47. package/dist/{cli-p2pjfq1r.js → cli-gj81e47t.js} +1 -1
  48. package/dist/{cli-srmej5tp.js → cli-gp1rtyvk.js} +2 -2
  49. package/dist/{cli-rb9m6rpe.js → cli-h6vtvt5c.js} +139 -1
  50. package/dist/cli-hd27d2md.js +1057 -0
  51. package/dist/{cli-hsk5gd15.js → cli-hwwvy36w.js} +3 -3
  52. package/dist/{cli-2rbt7at8.js → cli-hz99wfxp.js} +1 -1
  53. package/dist/cli-j1pmc07z.js +706 -0
  54. package/dist/{cli-01qndrvw.js → cli-j6kpj9az.js} +14 -8
  55. package/dist/{cli-2qbs2tvf.js → cli-k46r9hmk.js} +6 -6
  56. package/dist/{cli-wfvedq5w.js → cli-kb4ehzx5.js} +5 -5
  57. package/dist/{cli-agamgm0p.js → cli-mvzyxwg3.js} +1 -1
  58. package/dist/{cli-280gnz4v.js → cli-nv4zd0b9.js} +1 -1
  59. package/dist/{cli-rrbctenq.js → cli-nyhb1r6x.js} +3 -5
  60. package/dist/{cli-g7k7zbcz.js → cli-p7z6q2m6.js} +2 -1
  61. package/dist/{cli-yn0a417a.js → cli-phre6gz0.js} +22 -4
  62. package/dist/{cli-m4csbs67.js → cli-pj6x9qce.js} +1 -1
  63. package/dist/{cli-w0zx9ya1.js → cli-prrt9fa0.js} +4 -4
  64. package/dist/{cli-ctret352.js → cli-pstq9888.js} +33 -33
  65. package/dist/{cli-y195hv1a.js → cli-pxeeyc53.js} +1 -1
  66. package/dist/{cli-4bm25xtw.js → cli-qsztpfry.js} +1 -1
  67. package/dist/{cli-5t5my0h8.js → cli-qybkr9n3.js} +278 -1267
  68. package/dist/{cli-xezn9nkh.js → cli-r44t9wqc.js} +29 -472
  69. package/dist/{cli-x207g4w1.js → cli-r8smj8b8.js} +1 -1
  70. package/dist/{cli-bxqez1p0.js → cli-rcp5aqdw.js} +9 -39
  71. package/dist/{cli-vr5avem2.js → cli-smcrrmhy.js} +14 -18
  72. package/dist/{cli-0d2rqhvq.js → cli-tgpfnm6r.js} +15 -17
  73. package/dist/cli-vb8g5x02.js +549 -0
  74. package/dist/{cli-gt4hxb8p.js → cli-vv0qtfpx.js} +1 -2
  75. package/dist/{cli-95zs84qw.js → cli-wd0hz6ah.js} +671 -388
  76. package/dist/{cli-nkqjj42q.js → cli-x4m6feqt.js} +1 -1
  77. package/dist/{cli-x61srfpp.js → cli-xczpdp9e.js} +2 -2
  78. package/dist/cli-z1kd834t.js +36 -0
  79. package/dist/{cli-p8f7dqgt.js → cli-ztrv9y21.js} +48 -33
  80. package/dist/cli.js +24 -25
  81. package/dist/{cloudflare-workers-ai-system-one-sta45jf9.js → cloudflare-workers-ai-system-one-vnpyfr7e.js} +3 -3
  82. package/dist/{codex-adapter-9ag75whm.js → codex-adapter-m4tgdysy.js} +12 -12
  83. package/dist/{codex-hook-7ba15hjd.js → codex-hook-xch6z5wt.js} +2 -3
  84. package/dist/{codex-session-runner-9fp7y4cf.js → codex-session-runner-ymwzm6ea.js} +12 -12
  85. package/dist/{commands-d7eah0y9.js → commands-n0hshcvs.js} +6 -7
  86. package/dist/{controllers-connect-8t0k65x2.js → controllers-connect-y07cqcvy.js} +24 -25
  87. package/dist/{cursor-adapter-t4pr1m3c.js → cursor-adapter-q8pgv5qa.js} +2 -3
  88. package/dist/{db-sp39qv72.js → db-4w7tmt1g.js} +6 -7
  89. package/dist/{db-2rmd23wq.js → db-hkqvrdcb.js} +7 -8
  90. package/dist/{dispatcher-g7fsybsd.js → dispatcher-5sd69y14.js} +23 -24
  91. package/dist/{easter-egg-3d-66zxervv.js → easter-egg-3d-m8rvz40s.js} +4 -3
  92. package/dist/execute-107xtsp1.js +3745 -0
  93. package/dist/{google-generative-ai-pvhm0dq1.js → google-generative-ai-7pavrbv7.js} +3 -3
  94. package/dist/{google-vertex-tek9sqe2.js → google-vertex-3f1jk41f.js} +3 -3
  95. package/dist/{grok-adapter-1err1me7.js → grok-adapter-8m9yqzbq.js} +24 -6
  96. package/dist/{handlers-c7yggm37.js → handlers-s20xr10r.js} +26 -27
  97. package/dist/{heartbeat-dkm924fk.js → heartbeat-3k31q4g2.js} +25 -26
  98. package/dist/{hook-wgx4exw6.js → hook-gdn6a2fn.js} +13 -14
  99. package/dist/{hook-g8e7sfnf.js → hook-ygwerrdh.js} +14 -15
  100. package/dist/{http-1d23k928.js → http-jdteeg81.js} +53 -55
  101. package/dist/{index-y5vvczrs.js → index-6ksewd5y.js} +20 -18
  102. package/dist/{index-kkeftv5h.js → index-82rk11v7.js} +4 -4
  103. package/dist/{index-gm10tmye.js → index-e0snym4t.js} +23 -24
  104. package/dist/{index-vertaxs1.js → index-ffqzqgwb.js} +6 -7
  105. package/dist/{index-q7cj9bn2.js → index-tsszt7g9.js} +25 -26
  106. package/dist/{index-w097eg0x.js → index-vccv018g.js} +1 -1
  107. package/dist/{index-va4zbvzp.js → index-vm3bm9t7.js} +24 -25
  108. package/dist/{keepalive-1jc5e7ct.js → keepalive-jekc896t.js} +8 -9
  109. package/dist/{lead-yxw2f1sg.js → lead-4k9s9szx.js} +40 -41
  110. package/dist/{maintenance-h7w2m10y.js → maintenance-shazch4n.js} +6 -7
  111. package/dist/{mistral-conversations-rjxa9s1j.js → mistral-conversations-vjc1ggpd.js} +29 -15
  112. package/dist/{oauth-refresh-sweep-wh9ypd1b.js → oauth-refresh-sweep-5gb36kn8.js} +7 -8
  113. package/dist/{onboard-dfr9j762.js → onboard-ydrbnvww.js} +2 -2
  114. package/dist/{openai-codex-responses-pgvtddvv.js → openai-codex-responses-33qhkkdy.js} +7 -39
  115. package/dist/{openai-completions-44gb0zsk.js → openai-completions-s88xm3ns.js} +2 -2
  116. package/dist/{openai-decisions-vj9nkpf8.js → openai-decisions-fctnj8xd.js} +2 -2
  117. package/dist/{openai-responses-9dyzwzkr.js → openai-responses-7xd2f2e2.js} +3 -3
  118. package/dist/opencode-adapter-5zh0he20.js +55 -0
  119. package/dist/{otel-impl-and31m5s.js → otel-impl-6pf5cv86.js} +1 -1
  120. package/dist/{pi-mono-adapter-fx6k5wwh.js → pi-mono-adapter-ef9wwx9m.js} +25 -500
  121. package/dist/{pricing-refresh-23zr6j3w.js → pricing-refresh-s52nsa5q.js} +8 -9
  122. package/dist/{rbac-roles-wnx35x4x.js → rbac-roles-aq3msh4e.js} +6 -7
  123. package/dist/{rbac-roles-nc7k9d0t.js → rbac-roles-q5gb5e8v.js} +7 -8
  124. package/dist/{render-v2-81cg1y52.js → render-v2-pd1rg54q.js} +7 -8
  125. package/dist/{resume-d4b6j2n2.js → resume-gw4a2c6v.js} +14 -15
  126. package/dist/{runtime-h9ha1mq6.js → runtime-qfry88v2.js} +4 -7
  127. package/dist/{setup-g68r1w28.js → setup-jda1ksdh.js} +2 -2
  128. package/dist/{typesafe-system-one-e988vty7.js → typesafe-system-one-zzzcx4s3.js} +3 -3
  129. package/dist/{virtual-modules-rdjmcg7q.js → virtual-modules-b3ga2m65.js} +13 -12
  130. package/dist/{work-objects-0zzdnbt9.js → work-objects-tb7ycye8.js} +26 -27
  131. package/dist/{worker-p1g8qm6h.js → worker-njjccz5d.js} +40 -41
  132. package/openapi.json +1 -1
  133. package/package.json +7 -8
  134. package/src/be/scripts/extract-schema.ts +4 -0
  135. package/src/github/handlers.ts +61 -18
  136. package/src/github/types.ts +2 -1
  137. package/src/prompts/goal-reminder.ts +30 -0
  138. package/src/prompts/session-templates.ts +22 -0
  139. package/src/providers/acp-targets.ts +3 -4
  140. package/src/providers/amp-adapter.ts +1 -1
  141. package/src/providers/harness-version.ts +0 -1
  142. package/src/providers/opencode-adapter.ts +442 -723
  143. package/src/providers/opencode-swarm-plugin.ts +88 -0
  144. package/src/providers/pi-mono-extension.ts +58 -37
  145. package/src/scripts-runtime/executors/native.ts +6 -5
  146. package/src/scripts-runtime/executors/quickjs-bundle.ts +18 -11
  147. package/src/scripts-runtime/extract-args-schema.ts +22 -4
  148. package/src/scripts-runtime/import-allowlist.ts +1 -1
  149. package/src/scripts-runtime/zod-mini-bundle.ts +5 -0
  150. package/src/slack/render-v2.ts +2 -2
  151. package/src/tests/entrypoint-acp-target-binary.test.ts +19 -1
  152. package/src/tests/github-handlers-inline-comments.test.ts +131 -2
  153. package/src/tests/github-handlers-reactions.test.ts +9 -0
  154. package/src/tests/harness-version.test.ts +0 -20
  155. package/src/tests/opencode-adapter.test.ts +536 -1389
  156. package/src/tests/opencode-mcp-admission-sdk.test.ts +106 -0
  157. package/src/tests/opencode-swarm-plugin.test.ts +130 -0
  158. package/src/tests/pi-mono-extension.test.ts +59 -0
  159. package/src/tests/prompt-template-session.test.ts +4 -3
  160. package/src/tests/provider-registration.test.ts +3 -0
  161. package/src/tests/script-executor-quickjs.test.ts +17 -12
  162. package/src/tests/scripts-http.test.ts +24 -16
  163. package/src/tests/scripts-import-allowlist.test.ts +1 -1
  164. package/src/tests/scripts-runtime.test.ts +78 -40
  165. package/src/tests/seed-scripts.test.ts +13 -0
  166. package/src/tests/slack-render-v2.test.ts +4 -2
  167. package/src/tests/ui-logs-parser.test.ts +28 -0
  168. package/src/tools/script-upsert.ts +1 -1
  169. package/src/utils/memory-raters.ts +7 -3
  170. package/src/utils/openrouter-base-url.ts +0 -2
  171. package/templates/schedules/weekly-harness-upgrade-check/content.md +4 -6
  172. package/templates/skills/heartbeat-runbook/content.md +2 -0
  173. package/templates/skills/swarm-scripts/SKILL.md +1 -0
  174. package/templates/skills/swarm-scripts/content.md +1 -0
  175. package/dist/cli-5bfdm1ah.js +0 -462
  176. package/dist/cli-5c2j5yvx.js +0 -222
  177. package/dist/cli-rhy7z1rp.js +0 -532
  178. package/dist/cli-ywh2a87k.js +0 -2359
  179. package/dist/execute-h4jgevyw.js +0 -1387
  180. package/dist/opencode-adapter-f1zby94p.js +0 -34
  181. package/plugin/opencode-plugins/lib/memory-raters.ts +0 -7
  182. package/src/tests/opencode-plugin.test.ts +0 -580
  183. package/src/tests/providers/opencode-cost.test.ts +0 -74
@@ -0,0 +1,514 @@
1
+ import {
2
+ summarizeSession
3
+ } from "./cli-cf69s3ew.js";
4
+ import {
5
+ checkToolLoop,
6
+ clearToolHistory
7
+ } from "./cli-hp1n6zq3.js";
8
+ import {
9
+ buildRatingsFromLlm,
10
+ fetchRetrievalsForTask,
11
+ getMemoryRaterNames,
12
+ postRatings
13
+ } from "./cli-104zsxba.js";
14
+ import {
15
+ init_resolver,
16
+ init_session_templates,
17
+ resolveTemplateAsync,
18
+ resolveTemplateFromCode
19
+ } from "./cli-fdvbjtbw.js";
20
+ import {
21
+ getOpenRouterAttributionHeaders,
22
+ init_openrouter_base_url
23
+ } from "./cli-a35hzdve.js";
24
+ import {
25
+ init_secret_scrubber,
26
+ scrubSecrets
27
+ } from "./cli-d161g69q.js";
28
+
29
+ // src/prompts/goal-reminder.ts
30
+ init_session_templates();
31
+ init_resolver();
32
+ var GOAL_REMINDER_TEMPLATE = "system.agent.compaction.goal_reminder";
33
+ async function renderGoalReminder(task) {
34
+ const variables = {
35
+ taskId: task.id,
36
+ task: task.task,
37
+ progressLine: task.progress ? `
38
+ Current Progress: ${task.progress}` : ""
39
+ };
40
+ try {
41
+ const result = await resolveTemplateAsync(GOAL_REMINDER_TEMPLATE, variables);
42
+ return result.skipped ? "" : result.text;
43
+ } catch {
44
+ return resolveTemplateFromCode(GOAL_REMINDER_TEMPLATE, variables).text;
45
+ }
46
+ }
47
+
48
+ // src/providers/pi-mono-extension.ts
49
+ init_openrouter_base_url();
50
+ init_secret_scrubber();
51
+ function apiHeaders(config) {
52
+ return {
53
+ "Content-Type": "application/json",
54
+ ...config.apiKey ? { Authorization: `Bearer ${config.apiKey}` } : {},
55
+ "X-Agent-ID": config.agentId
56
+ };
57
+ }
58
+ function fireAndForget(url, init) {
59
+ fetch(url, init).catch(() => {});
60
+ }
61
+ async function isTaskCancelled(config) {
62
+ try {
63
+ const resp = await fetch(`${config.apiUrl}/cancelled-tasks?taskId=${encodeURIComponent(config.taskId)}`, { method: "GET", headers: apiHeaders(config) });
64
+ if (!resp.ok)
65
+ return { cancelled: false };
66
+ const data = await resp.json();
67
+ const match = data.cancelled?.find((t) => t.id === config.taskId);
68
+ return match ? { cancelled: true, reason: match.failureReason } : { cancelled: false };
69
+ } catch {
70
+ return { cancelled: false };
71
+ }
72
+ }
73
+ async function checkShouldBlockPolling(config) {
74
+ try {
75
+ const resp = await fetch(`${config.apiUrl}/me`, {
76
+ method: "GET",
77
+ headers: apiHeaders(config)
78
+ });
79
+ if (!resp.ok)
80
+ return false;
81
+ const data = await resp.json();
82
+ return data.shouldBlockPolling === true;
83
+ } catch {
84
+ return false;
85
+ }
86
+ }
87
+ async function fetchTaskDetails(config) {
88
+ try {
89
+ const resp = await fetch(`${config.apiUrl}/api/tasks/${config.taskId}`, {
90
+ method: "GET",
91
+ headers: apiHeaders(config)
92
+ });
93
+ if (!resp.ok)
94
+ return null;
95
+ return await resp.json();
96
+ } catch {
97
+ return null;
98
+ }
99
+ }
100
+ async function syncIdentityFilesToServer(config, changeSource = "session_sync", signal) {
101
+ const updates = {};
102
+ const paths = {
103
+ soulMd: "/workspace/SOUL.md",
104
+ identityMd: "/workspace/IDENTITY.md",
105
+ toolsMd: "/workspace/TOOLS.md"
106
+ };
107
+ for (const [key, path] of Object.entries(paths)) {
108
+ try {
109
+ const file = Bun.file(path);
110
+ if (await file.exists()) {
111
+ const content = await file.text();
112
+ if (content.trim() && content.length <= 65536) {
113
+ updates[key] = content;
114
+ }
115
+ }
116
+ } catch {}
117
+ }
118
+ if (Object.keys(updates).length === 0)
119
+ return;
120
+ try {
121
+ await fetch(`${config.apiUrl}/api/agents/${config.agentId}/profile`, {
122
+ method: "PUT",
123
+ headers: apiHeaders(config),
124
+ body: JSON.stringify({ ...updates, changeSource }),
125
+ signal
126
+ });
127
+ } catch {}
128
+ }
129
+ async function syncSetupScriptToServer(config, changeSource = "session_sync") {
130
+ try {
131
+ const file = Bun.file("/workspace/start-up.sh");
132
+ if (!await file.exists())
133
+ return;
134
+ const raw = await file.text();
135
+ if (!raw.trim())
136
+ return;
137
+ const markerStart = "# === Agent-managed setup (from DB) ===";
138
+ const markerEnd = "# === End agent-managed setup ===";
139
+ const startIdx = raw.indexOf(markerStart);
140
+ const endIdx = raw.indexOf(markerEnd);
141
+ let content;
142
+ if (startIdx !== -1 && endIdx !== -1) {
143
+ content = raw.substring(startIdx + markerStart.length, endIdx).trim();
144
+ } else {
145
+ content = raw.replace(/^#!\/bin\/bash\n/, "").trim();
146
+ }
147
+ if (!content || content.length > 65536)
148
+ return;
149
+ await fetch(`${config.apiUrl}/api/agents/${config.agentId}/profile`, {
150
+ method: "PUT",
151
+ headers: apiHeaders(config),
152
+ body: JSON.stringify({ setupScript: content, changeSource })
153
+ });
154
+ } catch {}
155
+ }
156
+ async function autoIndexMemoryFile(config, editedPath) {
157
+ try {
158
+ const fileContent = await Bun.file(editedPath).text();
159
+ const isShared = editedPath.startsWith("/workspace/shared/");
160
+ const fileName = editedPath.split("/").pop() ?? "unnamed";
161
+ await fetch(`${config.apiUrl}/api/memory/index`, {
162
+ method: "POST",
163
+ headers: apiHeaders(config),
164
+ body: JSON.stringify({
165
+ agentId: config.agentId,
166
+ content: fileContent,
167
+ name: fileName.replace(/\.\w+$/, ""),
168
+ scope: isShared ? "swarm" : "agent",
169
+ source: "file_index",
170
+ sourcePath: editedPath
171
+ })
172
+ });
173
+ } catch {}
174
+ }
175
+ async function fetchConcurrentContext(config) {
176
+ try {
177
+ const resp = await fetch(`${config.apiUrl}/api/concurrent-context`, {
178
+ method: "GET",
179
+ headers: apiHeaders(config)
180
+ });
181
+ if (!resp.ok)
182
+ return null;
183
+ const data = await resp.json();
184
+ const lines = [];
185
+ if (data.processingInboxMessages.length > 0) {
186
+ lines.push("=== CONCURRENT SESSION AWARENESS ===");
187
+ lines.push("");
188
+ lines.push("**Other sessions are currently processing these inbox messages:**");
189
+ for (const msg of data.processingInboxMessages) {
190
+ const preview = msg.content.length > 120 ? `${msg.content.slice(0, 120)}...` : msg.content;
191
+ lines.push(`- [${msg.source}] "${preview}" (received ${msg.createdAt})`);
192
+ }
193
+ }
194
+ if (data.recentTaskDelegations.length > 0) {
195
+ if (lines.length === 0)
196
+ lines.push("=== CONCURRENT SESSION AWARENESS ===");
197
+ lines.push("");
198
+ lines.push("**Recent task delegations (last 5 min):**");
199
+ for (const task of data.recentTaskDelegations) {
200
+ const preview = task.task.length > 120 ? `${task.task.slice(0, 120)}...` : task.task;
201
+ lines.push(`- "${preview}" → ${task.agentName ?? "unassigned"} [${task.status}]`);
202
+ }
203
+ }
204
+ if (data.activeSwarmTasks.length > 0) {
205
+ if (lines.length === 0)
206
+ lines.push("=== CONCURRENT SESSION AWARENESS ===");
207
+ lines.push("");
208
+ lines.push("**Currently active tasks across the swarm:**");
209
+ for (const task of data.activeSwarmTasks) {
210
+ const preview = task.task.length > 100 ? `${task.task.slice(0, 100)}...` : task.task;
211
+ lines.push(`- ${task.agentName ?? "unassigned"}: "${preview}" [${task.status}]`);
212
+ }
213
+ }
214
+ if (lines.length > 0) {
215
+ lines.push("");
216
+ lines.push("IMPORTANT: Avoid duplicating work that is already being handled by other sessions or agents.");
217
+ lines.push("=== END CONCURRENT SESSION AWARENESS ===");
218
+ return lines.join(`
219
+ `);
220
+ }
221
+ return null;
222
+ } catch {
223
+ return null;
224
+ }
225
+ }
226
+ async function summarizeSessionForPi(config, sessionFile, deps = {}) {
227
+ if (!sessionFile) {
228
+ console.warn("session_summary skipped (pi): session transcript file unavailable");
229
+ return;
230
+ }
231
+ let transcript = "";
232
+ try {
233
+ const fullTranscript = await Bun.file(sessionFile).text();
234
+ transcript = fullTranscript.length > 20000 ? fullTranscript.slice(-20000) : fullTranscript;
235
+ } catch (err) {
236
+ console.warn(scrubSecrets(`session_summary skipped (pi): cannot read ${sessionFile}: ${String(err)}`));
237
+ return;
238
+ }
239
+ await summarizeTranscript(config, "pi", transcript, deps);
240
+ }
241
+ async function summarizeTranscript(config, harness, transcript, deps = {}) {
242
+ const _runSummarize = deps.runSummarize ?? summarizeSession;
243
+ const _fetchRetrievals = deps.fetchRetrievalsForTask ?? fetchRetrievalsForTask;
244
+ const _postRatings = deps.postRatings ?? postRatings;
245
+ const _buildRatings = deps.buildRatingsFromLlm ?? buildRatingsFromLlm;
246
+ try {
247
+ if (transcript.length <= 100) {
248
+ console.warn(`session_summary skipped (${harness}): transcript too short (${transcript.length} chars)`);
249
+ return;
250
+ }
251
+ const sourceTaskId = config.taskId;
252
+ const agentId = config.agentId;
253
+ if (!sourceTaskId || !agentId) {
254
+ console.warn(`session_summary skipped (${harness}): ${!sourceTaskId ? "task id unavailable" : "agent id unavailable"}`);
255
+ return;
256
+ }
257
+ const taskDetails = await fetchTaskDetails(config).catch(() => null);
258
+ const wantRatings = getMemoryRaterNames().includes("llm");
259
+ const retrievals = wantRatings ? await _fetchRetrievals({
260
+ apiUrl: config.apiUrl,
261
+ apiKey: config.apiKey,
262
+ agentId,
263
+ taskId: sourceTaskId
264
+ }).catch(() => []) : [];
265
+ const result = await _runSummarize({
266
+ harness,
267
+ transcript,
268
+ retrievals,
269
+ taskContext: {
270
+ sourceTaskId,
271
+ agentId,
272
+ prompt: taskDetails?.task
273
+ },
274
+ apiUrl: config.apiUrl,
275
+ apiKey: config.apiKey,
276
+ env: config.env
277
+ });
278
+ if (!result) {
279
+ console.warn(`session_summary skipped (${harness}): summarizer returned no result`);
280
+ return;
281
+ }
282
+ const summary = result.summary.trim();
283
+ if (summary.length <= 20 || summary.toLowerCase().includes("no significant learnings")) {
284
+ console.debug(`session_summary skipped (${harness}): summary failed quality gate (${summary.length} chars)`);
285
+ return;
286
+ }
287
+ const indexResp = await fetch(`${config.apiUrl}/api/memory/index`, {
288
+ method: "POST",
289
+ headers: apiHeaders(config),
290
+ body: JSON.stringify({
291
+ scope: "agent",
292
+ source: "session_summary",
293
+ sourceTaskId,
294
+ content: summary,
295
+ name: "session-summary",
296
+ agentId
297
+ })
298
+ });
299
+ if (!indexResp.ok) {
300
+ const text = await indexResp.text().catch(() => "");
301
+ console.error(`session_summary: /api/memory/index POST failed (${harness}):`, indexResp.status, scrubSecrets(text));
302
+ return;
303
+ }
304
+ if (wantRatings && result.ratings && result.ratings.length > 0) {
305
+ const ratingEvents = _buildRatings(result.ratings, retrievals, result.model);
306
+ if (ratingEvents.length > 0) {
307
+ await _postRatings({
308
+ apiUrl: config.apiUrl,
309
+ apiKey: config.apiKey,
310
+ agentId,
311
+ taskId: sourceTaskId,
312
+ events: ratingEvents
313
+ }).catch((err) => console.error(`session_summary: postRatings failed (${harness}):`, scrubError(err)));
314
+ }
315
+ }
316
+ } catch (err) {
317
+ console.error(`session_summary failed (${harness}):`, scrubError(err));
318
+ }
319
+ }
320
+ function scrubError(err) {
321
+ return scrubSecrets(err instanceof Error ? err.stack ?? err.message : String(err));
322
+ }
323
+ var SWARM_NUDGE_CUSTOM_TYPE = "swarm-nudge";
324
+ function sendNudge(pi, text) {
325
+ pi.sendMessage({
326
+ customType: SWARM_NUDGE_CUSTOM_TYPE,
327
+ content: [{ type: "text", text }],
328
+ display: false
329
+ });
330
+ }
331
+ function createSwarmHooksExtension(config) {
332
+ return (pi) => {
333
+ let lastContextPostTime = 0;
334
+ pi.on("before_provider_headers", (event, ctx) => {
335
+ Object.assign(event.headers, getOpenRouterAttributionHeaders(ctx.model?.baseUrl ?? "", config.env));
336
+ });
337
+ pi.on("session_start", async (_event, _ctx) => {
338
+ for (const [name, server] of Object.entries(config.mcpServers ?? {})) {
339
+ try {
340
+ pi.registerMcpServer(name, server);
341
+ } catch (err) {
342
+ console.warn(`[pi] Failed to register MCP server "${name}": ${err}`);
343
+ }
344
+ }
345
+ fireAndForget(`${config.apiUrl}/ping`, {
346
+ method: "POST",
347
+ headers: apiHeaders(config)
348
+ });
349
+ if (config.taskId) {
350
+ await clearToolHistory(config.taskId);
351
+ }
352
+ if (config.isLead) {
353
+ const ctx = await fetchConcurrentContext(config);
354
+ if (ctx) {
355
+ sendNudge(pi, ctx);
356
+ }
357
+ }
358
+ });
359
+ pi.on("tool_call", async (event, _ctx) => {
360
+ if (!config.isLead && config.taskId) {
361
+ const { cancelled, reason } = await isTaskCancelled(config);
362
+ if (cancelled) {
363
+ const cancelReason = reason || "Task cancelled by lead or creator";
364
+ return {
365
+ block: true,
366
+ reason: `\uD83D\uDED1 TASK CANCELLED: Your current task (${config.taskId.slice(0, 8)}) has been cancelled. Reason: "${cancelReason}". ` + `Stop working on this task immediately. Do NOT continue making tool calls. ` + `Use store-progress to acknowledge the cancellation and mark the task as failed, then wait for new tasks.`
367
+ };
368
+ }
369
+ }
370
+ if (!config.isLead && config.taskId) {
371
+ const toolName = event.toolName;
372
+ const toolInput = "input" in event ? event.input : {};
373
+ const loopResult = await checkToolLoop(config.taskId, toolName, toolInput);
374
+ if (loopResult.blocked) {
375
+ return {
376
+ block: true,
377
+ reason: `LOOP DETECTED: ${loopResult.reason} ` + "Stop repeating this action and try a fundamentally different approach. " + "If you're truly stuck, use store-progress to report the blocker."
378
+ };
379
+ }
380
+ if (loopResult.severity === "warning" && loopResult.reason) {
381
+ sendNudge(pi, `Warning: ${loopResult.reason}`);
382
+ }
383
+ }
384
+ if (event.toolName?.endsWith("poll-task")) {
385
+ const shouldBlock = await checkShouldBlockPolling(config);
386
+ if (shouldBlock) {
387
+ return {
388
+ block: true,
389
+ reason: "\uD83D\uDED1 POLLING LIMIT REACHED: You have exceeded the maximum empty poll attempts. " + "EXIT NOW - do not make any more tool calls."
390
+ };
391
+ }
392
+ }
393
+ return;
394
+ });
395
+ pi.on("tool_result", async (event, ctx) => {
396
+ if (!config.isLead && config.taskId) {
397
+ fireAndForget(`${config.apiUrl}/api/active-sessions/heartbeat/${config.taskId}`, {
398
+ method: "PUT",
399
+ headers: apiHeaders(config)
400
+ });
401
+ }
402
+ fireAndForget(`${config.apiUrl}/api/agents/${config.agentId}/activity`, {
403
+ method: "PUT",
404
+ headers: apiHeaders(config)
405
+ });
406
+ const usage = ctx.getContextUsage?.();
407
+ if (config.taskId && usage?.tokens != null) {
408
+ const now = Date.now();
409
+ if (now - lastContextPostTime >= 30000) {
410
+ lastContextPostTime = now;
411
+ fireAndForget(`${config.apiUrl}/api/tasks/${config.taskId}/context`, {
412
+ method: "POST",
413
+ headers: apiHeaders(config),
414
+ body: JSON.stringify({
415
+ eventType: "progress",
416
+ sessionId: `pi-${config.taskId}`,
417
+ contextUsedTokens: usage.tokens,
418
+ contextTotalTokens: usage.contextWindow,
419
+ contextPercent: usage.percent
420
+ })
421
+ });
422
+ }
423
+ }
424
+ const toolName = event.toolName;
425
+ const input = event.input;
426
+ const editedPath = input?.file_path || input?.path;
427
+ if ((toolName === "write" || toolName === "edit") && editedPath) {
428
+ if (editedPath === "/workspace/SOUL.md" || editedPath === "/workspace/IDENTITY.md" || editedPath === "/workspace/TOOLS.md") {
429
+ syncIdentityFilesToServer(config, "self_edit");
430
+ }
431
+ if (editedPath.startsWith("/workspace/start-up")) {
432
+ syncSetupScriptToServer(config, "self_edit");
433
+ }
434
+ if (editedPath.startsWith("/workspace/personal/memory/") || editedPath.startsWith("/workspace/shared/memory/")) {
435
+ autoIndexMemoryFile(config, editedPath);
436
+ }
437
+ }
438
+ if (config.isLead && event.toolName?.endsWith("send-task")) {
439
+ sendNudge(pi, "Task sent successfully. Monitor progress using the get-task-details tool periodically.");
440
+ }
441
+ return;
442
+ });
443
+ pi.on("session_compact", async () => {
444
+ if (!config.taskId)
445
+ return;
446
+ const taskDetails = await fetchTaskDetails(config);
447
+ if (taskDetails) {
448
+ const reminder = await renderGoalReminder(taskDetails);
449
+ if (reminder)
450
+ sendNudge(pi, reminder);
451
+ }
452
+ });
453
+ pi.on("context", async (_event, ctx) => {
454
+ if (!config.taskId)
455
+ return;
456
+ const usage = ctx.getContextUsage?.();
457
+ if (usage) {
458
+ fireAndForget(`${config.apiUrl}/api/tasks/${config.taskId}/context`, {
459
+ method: "POST",
460
+ headers: apiHeaders(config),
461
+ body: JSON.stringify({
462
+ eventType: "compaction",
463
+ sessionId: `pi-${config.taskId}`,
464
+ contextUsedTokens: usage.tokens ?? undefined,
465
+ contextTotalTokens: usage.contextWindow,
466
+ contextPercent: usage.percent ?? undefined
467
+ })
468
+ });
469
+ }
470
+ return;
471
+ });
472
+ pi.on("input", async (_event, _ctx) => {
473
+ if (!config.isLead && config.taskId) {
474
+ const { cancelled, reason } = await isTaskCancelled(config);
475
+ if (cancelled) {
476
+ const cancelReason = reason || "Task cancelled by lead or creator";
477
+ sendNudge(pi, `\uD83D\uDED1 TASK CANCELLED: ${cancelReason}. Stop working and use store-progress to acknowledge.`);
478
+ return { action: "handled" };
479
+ }
480
+ }
481
+ return;
482
+ });
483
+ pi.on("session_shutdown", async (_event, ctx) => {
484
+ const usage = ctx.getContextUsage?.();
485
+ if (config.taskId && usage) {
486
+ await fetch(`${config.apiUrl}/api/tasks/${config.taskId}/context`, {
487
+ method: "POST",
488
+ headers: apiHeaders(config),
489
+ body: JSON.stringify({
490
+ eventType: "completion",
491
+ sessionId: `pi-${config.taskId}`,
492
+ contextTotalTokens: usage.contextWindow,
493
+ contextPercent: usage.percent ?? undefined,
494
+ contextUsedTokens: usage.tokens ?? undefined
495
+ })
496
+ }).catch(() => {});
497
+ }
498
+ await syncIdentityFilesToServer(config);
499
+ await syncSetupScriptToServer(config);
500
+ const sessionFile = ctx.sessionManager.getSessionFile?.();
501
+ if (!process.env.SKIP_SESSION_SUMMARY) {
502
+ await summarizeSessionForPi(config, sessionFile);
503
+ } else {
504
+ console.debug("session_summary skipped (pi): SKIP_SESSION_SUMMARY is set");
505
+ }
506
+ fireAndForget(`${config.apiUrl}/close`, {
507
+ method: "POST",
508
+ headers: apiHeaders(config)
509
+ });
510
+ });
511
+ };
512
+ }
513
+
514
+ export { renderGoalReminder, apiHeaders, fireAndForget, isTaskCancelled, checkShouldBlockPolling, fetchTaskDetails, syncIdentityFilesToServer, autoIndexMemoryFile, fetchConcurrentContext, summarizeTranscript, createSwarmHooksExtension };