@desplega.ai/agent-swarm 1.84.1 → 1.86.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 (116) hide show
  1. package/README.md +1 -0
  2. package/openapi.json +1 -1
  3. package/package.json +8 -6
  4. package/src/be/db-queries/oauth.ts +33 -0
  5. package/src/be/db.ts +51 -1
  6. package/src/be/migrations/077_oauth_refresh_locks.sql +8 -0
  7. package/src/be/migrations/078_backfill_gpt_5_5_pricing.sql +15 -0
  8. package/src/be/modelsdev-cache.json +152028 -0
  9. package/src/be/modelsdev-cache.ts +46 -0
  10. package/src/be/seed-pricing.ts +7 -44
  11. package/src/cli.tsx +12 -2
  12. package/src/commands/codex-session-runner.ts +132 -0
  13. package/src/commands/credential-wait.ts +2 -2
  14. package/src/commands/provider-credentials.ts +10 -5
  15. package/src/commands/runner.ts +62 -9
  16. package/src/http/index.ts +11 -3
  17. package/src/http/tasks.ts +17 -0
  18. package/src/http/utils.ts +17 -0
  19. package/src/oauth/ensure-token.ts +97 -11
  20. package/src/prompts/base-prompt.ts +49 -3
  21. package/src/providers/claude-adapter.ts +83 -2
  22. package/src/providers/claude-managed-models.ts +18 -2
  23. package/src/providers/codex-adapter.ts +417 -97
  24. package/src/providers/codex-models.ts +9 -2
  25. package/src/providers/index.ts +28 -19
  26. package/src/providers/pi-mono-adapter.ts +44 -25
  27. package/src/providers/pricing-sources.md +7 -4
  28. package/src/providers/swarm-events-shared.ts +14 -0
  29. package/src/server.ts +2 -0
  30. package/src/slack/HEURISTICS.md +5 -1
  31. package/src/slack/handlers.test.ts +35 -0
  32. package/src/slack/handlers.ts +79 -2
  33. package/src/tasks/worker-follow-up.ts +82 -0
  34. package/src/tests/agents-list-model-display.test.ts +13 -1
  35. package/src/tests/base-prompt.test.ts +46 -8
  36. package/src/tests/claude-managed-adapter.test.ts +4 -4
  37. package/src/tests/codex-adapter-otel.test.ts +4 -4
  38. package/src/tests/codex-adapter.test.ts +20 -7
  39. package/src/tests/codex-swarm-events.test.ts +35 -0
  40. package/src/tests/context-window.test.ts +1 -0
  41. package/src/tests/credential-check.test.ts +48 -29
  42. package/src/tests/db-queries-oauth.test.ts +27 -0
  43. package/src/tests/ensure-token.test.ts +71 -0
  44. package/src/tests/entrypoint-config-env-export.test.ts +81 -0
  45. package/src/tests/follow-up-redelivery-guard.test.ts +165 -0
  46. package/src/tests/http-log-scrubbing.test.ts +24 -0
  47. package/src/tests/list-endpoint-slimming.test.ts +22 -1
  48. package/src/tests/migration-046-budgets.test.ts +6 -5
  49. package/src/tests/oauth-access-token-tool.test.ts +138 -0
  50. package/src/tests/pi-mono-adapter.test.ts +37 -1
  51. package/src/tests/pricing-routes.test.ts +6 -5
  52. package/src/tests/provider-adapter.test.ts +10 -10
  53. package/src/tests/provider-command-format.test.ts +4 -4
  54. package/src/tests/runner-fallback-output.test.ts +118 -39
  55. package/src/tests/session-costs-codex-recompute.test.ts +25 -0
  56. package/src/tests/task-completion-idempotency.test.ts +89 -0
  57. package/src/tools/oauth-access-token.ts +118 -0
  58. package/src/tools/send-task.ts +30 -9
  59. package/src/tools/store-progress.ts +12 -77
  60. package/src/tools/tool-config.ts +2 -1
  61. package/src/types.ts +5 -0
  62. package/src/utils/context-window.ts +1 -0
  63. package/src/utils/secret-scrubber.ts +23 -0
  64. package/templates/schedules/daily-blocker-digest/config.json +13 -0
  65. package/templates/schedules/daily-blocker-digest/content.md +150 -0
  66. package/templates/schedules/daily-compounding-reflection/config.json +21 -0
  67. package/templates/schedules/daily-compounding-reflection/content.md +210 -0
  68. package/templates/schedules/daily-hn-briefing/config.json +13 -0
  69. package/templates/schedules/daily-hn-briefing/content.md +97 -0
  70. package/templates/schedules/daily-workflow-health-audit/config.json +13 -0
  71. package/templates/schedules/daily-workflow-health-audit/content.md +189 -0
  72. package/templates/schedules/gtm-weekly-review/config.json +13 -0
  73. package/templates/schedules/gtm-weekly-review/content.md +58 -0
  74. package/templates/schedules/weekly-dependabot-triage/config.json +13 -0
  75. package/templates/schedules/weekly-dependabot-triage/content.md +45 -0
  76. package/templates/schema.ts +26 -0
  77. package/templates/skills/agentmail-sending/config.json +13 -0
  78. package/templates/skills/agentmail-sending/content.md +48 -0
  79. package/templates/skills/artifacts/config.json +13 -0
  80. package/templates/skills/artifacts/content.md +87 -0
  81. package/templates/skills/browser-use-cloud/config.json +13 -0
  82. package/templates/skills/browser-use-cloud/content.md +155 -0
  83. package/templates/skills/desloppify/config.json +13 -0
  84. package/templates/skills/desloppify/content.md +201 -0
  85. package/templates/skills/exa-search/config.json +13 -0
  86. package/templates/skills/exa-search/content.md +106 -0
  87. package/templates/skills/jira-interaction/config.json +13 -0
  88. package/templates/skills/jira-interaction/content.md +252 -0
  89. package/templates/skills/kapso-whatsapp/config.json +13 -0
  90. package/templates/skills/kapso-whatsapp/content.md +369 -0
  91. package/templates/skills/kv-storage/config.json +13 -0
  92. package/templates/skills/kv-storage/content.md +111 -0
  93. package/templates/skills/linear-interaction/config.json +20 -0
  94. package/templates/skills/linear-interaction/content.md +230 -0
  95. package/templates/skills/pages/config.json +18 -0
  96. package/templates/skills/pages/content.md +85 -0
  97. package/templates/skills/profile-corruption-escalation/config.json +13 -0
  98. package/templates/skills/profile-corruption-escalation/content.md +105 -0
  99. package/templates/skills/scheduled-task-resilience/config.json +13 -0
  100. package/templates/skills/scheduled-task-resilience/content.md +95 -0
  101. package/templates/skills/sprite-cli/config.json +13 -0
  102. package/templates/skills/sprite-cli/content.md +133 -0
  103. package/templates/skills/turso-interaction/config.json +13 -0
  104. package/templates/skills/turso-interaction/content.md +192 -0
  105. package/templates/skills/workflow-iterate/config.json +18 -0
  106. package/templates/skills/workflow-iterate/content.md +399 -0
  107. package/templates/skills/workflow-structured-output/config.json +13 -0
  108. package/templates/skills/workflow-structured-output/content.md +101 -0
  109. package/templates/skills/x-api-interactions/config.json +13 -0
  110. package/templates/skills/x-api-interactions/content.md +109 -0
  111. package/templates/workflows/autopilot/config.json +13 -0
  112. package/templates/workflows/autopilot/content.md +58 -0
  113. package/templates/workflows/linear-drain-loop/config.json +21 -0
  114. package/templates/workflows/linear-drain-loop/content.md +72 -0
  115. package/templates/workflows/ralph-loop/config.json +13 -0
  116. package/templates/workflows/ralph-loop/content.md +75 -0
package/README.md CHANGED
@@ -124,6 +124,7 @@ Check [our templates](https://templates.agent-swarm.dev) for a quick start.
124
124
  - **Workflow engine with Human-in-the-Loop** — DAG-based automation with approval gates, retries, and structured I/O. [Workflows →](https://docs.agent-swarm.dev/docs/concepts/workflows)
125
125
  - **Scheduled & recurring tasks** — cron-based automation for standing work. [Scheduling →](https://docs.agent-swarm.dev/docs/concepts/scheduling)
126
126
  - **Harness & LLM agnostic** — run with Claude Code, OpenAI Codex, pi-mono, Devin, Claude Managed Agents, raw LLMs, or opencode. [Harness config →](https://docs.agent-swarm.dev/docs/guides/harness-configuration) · [Add a new provider →](https://docs.agent-swarm.dev/docs/guides/harness-providers)
127
+ - **Follow-up continuity across all harnesses** — child tasks inherit bounded prior-task context even on providers without native session resume, while resumable providers still reuse prior sessions when possible. [Task lifecycle →](https://docs.agent-swarm.dev/docs/concepts/task-lifecycle)
127
128
  - **Skills & MCP servers** — reusable procedural knowledge and per-agent MCP servers with scope cascade. [MCP tools →](https://docs.agent-swarm.dev/docs/reference/mcp-tools)
128
129
  - **DB-backed pages** — agents publish HTML or JSON pages (reports, dashboards, action specs) via the `create_page` MCP tool with public / authed / password modes, version history, view counters, diff helpers, and PDF export. [MCP tools → Pages](https://docs.agent-swarm.dev/docs/reference/mcp-tools#pages-tools)
129
130
  - **KV store** — Redis-like namespaced key/value store with auto-scoped context (Slack thread / PR / Linear issue / page). [MCP tools → KV](https://docs.agent-swarm.dev/docs/reference/mcp-tools#kv-tools)
package/openapi.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "openapi": "3.1.0",
3
3
  "info": {
4
4
  "title": "Agent Swarm API",
5
- "version": "1.84.1",
5
+ "version": "1.86.0",
6
6
  "description": "Multi-agent orchestration API for Claude Code, Codex, and Gemini CLI. Enables task distribution, agent communication, and service discovery.\n\nMCP tools are documented separately in [MCP.md](./MCP.md)."
7
7
  },
8
8
  "servers": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@desplega.ai/agent-swarm",
3
- "version": "1.84.1",
3
+ "version": "1.86.0",
4
4
  "description": "Multi-agent orchestration for Claude Code, Codex, Gemini CLI, and other AI coding assistants",
5
5
  "license": "MIT",
6
6
  "author": "desplega.sh <contact@desplega.sh>",
@@ -45,6 +45,8 @@
45
45
  "tsc:check": "bun tsc --noEmit",
46
46
  "check:db-boundary": "bash scripts/check-db-boundary.sh",
47
47
  "check:api-key-boundary": "bash scripts/check-api-key-boundary.sh",
48
+ "sync-chart-version": "bun scripts/sync-chart-version.ts",
49
+ "check-chart-version": "bun scripts/sync-chart-version.ts --check-if-package-version-changed",
48
50
  "cli": "bun src/cli.tsx",
49
51
  "hook": "bun src/hooks/hook.ts",
50
52
  "claude": "bun src/cli.tsx claude",
@@ -108,12 +110,12 @@
108
110
  "@desplega.ai/localtunnel": "^2.2.0",
109
111
  "@inkjs/ui": "^2.0.0",
110
112
  "@linear/sdk": "^77.0.0",
111
- "@earendil-works/pi-agent-core": "^0.75.5",
112
- "@earendil-works/pi-ai": "^0.75.5",
113
- "@earendil-works/pi-coding-agent": "^0.75.5",
113
+ "@earendil-works/pi-agent-core": "^0.76.0",
114
+ "@earendil-works/pi-ai": "^0.76.0",
115
+ "@earendil-works/pi-coding-agent": "^0.76.0",
114
116
  "@modelcontextprotocol/sdk": "^1.25.1",
115
- "@openai/codex-sdk": "^0.133.0",
116
- "@opencode-ai/sdk": "^1.15.10",
117
+ "@openai/codex-sdk": "^0.135.0",
118
+ "@opencode-ai/sdk": "^1.15.12",
117
119
  "@openfort/openfort-node": "^0.9.1",
118
120
  "@opentelemetry/api": "^1.9.1",
119
121
  "@opentelemetry/exporter-trace-otlp-http": "^0.218.0",
@@ -180,3 +180,36 @@ export function isTokenExpiringSoon(provider: string, bufferMs = 5 * 60 * 1000):
180
180
  const expiresAt = new Date(tokens.expiresAt).getTime();
181
181
  return expiresAt - Date.now() < bufferMs;
182
182
  }
183
+
184
+ // ── OAuth Refresh Locks ──
185
+
186
+ export function acquireOAuthRefreshLock(provider: string, ttlMs: number): string | null {
187
+ const owner = crypto.randomUUID();
188
+ const now = Date.now();
189
+ const expiresAt = new Date(now + ttlMs).toISOString();
190
+ const nowIso = new Date(now).toISOString();
191
+
192
+ getDb()
193
+ .query(
194
+ `INSERT INTO oauth_refresh_locks (provider, owner, expiresAt, createdAt, updatedAt)
195
+ VALUES (?, ?, ?, ?, ?)
196
+ ON CONFLICT(provider) DO UPDATE SET
197
+ owner = excluded.owner,
198
+ expiresAt = excluded.expiresAt,
199
+ updatedAt = excluded.updatedAt
200
+ WHERE oauth_refresh_locks.expiresAt <= ?`,
201
+ )
202
+ .run(provider, owner, expiresAt, nowIso, nowIso, nowIso);
203
+
204
+ const row = getDb()
205
+ .query("SELECT owner FROM oauth_refresh_locks WHERE provider = ?")
206
+ .get(provider) as { owner: string } | null;
207
+
208
+ return row?.owner === owner ? owner : null;
209
+ }
210
+
211
+ export function releaseOAuthRefreshLock(provider: string, owner: string): void {
212
+ getDb()
213
+ .query("DELETE FROM oauth_refresh_locks WHERE provider = ? AND owner = ?")
214
+ .run(provider, owner);
215
+ }
package/src/be/db.ts CHANGED
@@ -1012,6 +1012,7 @@ type AgentTaskRow = {
1012
1012
  swarmVersion: string | null;
1013
1013
  provider: string | null;
1014
1014
  providerMeta: string | null;
1015
+ totalCostUsd?: number | null;
1015
1016
  };
1016
1017
 
1017
1018
  function rowToAgentTask(row: AgentTaskRow): AgentTask {
@@ -1075,6 +1076,7 @@ function rowToAgentTask(row: AgentTaskRow): AgentTask {
1075
1076
  swarmVersion: row.swarmVersion ?? undefined,
1076
1077
  provider: (row.provider as ProviderName | null) ?? undefined,
1077
1078
  providerMeta: parseProviderMeta(row.provider as ProviderName | null, row.providerMeta),
1079
+ totalCostUsd: row.totalCostUsd ?? undefined,
1078
1080
  };
1079
1081
  }
1080
1082
 
@@ -1110,6 +1112,7 @@ function rowToAgentTaskSummary(row: AgentTaskRow): AgentTaskSummary {
1110
1112
  lastUpdatedAt: t.lastUpdatedAt,
1111
1113
  finishedAt: t.finishedAt,
1112
1114
  peakContextPercent: t.peakContextPercent,
1115
+ totalCostUsd: t.totalCostUsd,
1113
1116
  };
1114
1117
  }
1115
1118
 
@@ -1504,7 +1507,10 @@ export function getAllTasks(
1504
1507
  const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
1505
1508
  const limit = filters?.limit ?? 25;
1506
1509
  const offset = filters?.offset ?? 0;
1507
- const query = `SELECT * FROM agent_tasks ${whereClause} ORDER BY lastUpdatedAt DESC, priority DESC LIMIT ${limit} OFFSET ${offset}`;
1510
+ const query = `SELECT agent_tasks.*,
1511
+ (SELECT SUM(totalCostUsd) FROM session_costs WHERE session_costs.taskId = agent_tasks.id) AS totalCostUsd
1512
+ FROM agent_tasks ${whereClause}
1513
+ ORDER BY lastUpdatedAt DESC, priority DESC LIMIT ${limit} OFFSET ${offset}`;
1508
1514
 
1509
1515
  const rows = getDb()
1510
1516
  .prepare<AgentTaskRow, (string | AgentTaskStatus)[]>(query)
@@ -1869,6 +1875,50 @@ export function findCompletedTaskInThread(
1869
1875
  return row ? rowToAgentTask(row) : null;
1870
1876
  }
1871
1877
 
1878
+ /**
1879
+ * Find the most recent CANCELLED task in a Slack thread. Used by the
1880
+ * follow-up re-delegation guard so a cancellation (worker SIGTERM,
1881
+ * runner-side abort, swarm-events tool-loop abort) doesn't permanently
1882
+ * jam re-dispatch when an earlier sibling task in the same thread also
1883
+ * completed.
1884
+ *
1885
+ * Matches both:
1886
+ * - `status = 'cancelled'` (the canonical terminal state from cancelTask)
1887
+ * - `status = 'failed'` with a failureReason that starts with "cancelled"
1888
+ * or "exit 130" or contains "cancelled" (the codex-adapter abort path
1889
+ * emits `failureReason: "cancelled"` and exits 130).
1890
+ */
1891
+ export function findRecentCancelledTaskInThread(
1892
+ channelId: string,
1893
+ threadTs: string,
1894
+ windowMinutes: number,
1895
+ ): AgentTask | null {
1896
+ const since = new Date(Date.now() - windowMinutes * 60 * 1000).toISOString();
1897
+ const row = getDb()
1898
+ .prepare<AgentTaskRow, [string, string, string]>(
1899
+ `SELECT * FROM agent_tasks
1900
+ WHERE slackChannelId = ?
1901
+ AND slackThreadTs = ?
1902
+ AND lastUpdatedAt > ?
1903
+ AND (
1904
+ status = 'cancelled'
1905
+ OR (
1906
+ status = 'failed'
1907
+ AND failureReason IS NOT NULL
1908
+ AND (
1909
+ failureReason LIKE 'cancelled%'
1910
+ OR failureReason LIKE 'exit 130%'
1911
+ OR failureReason LIKE '%cancelled%'
1912
+ )
1913
+ )
1914
+ )
1915
+ ORDER BY lastUpdatedAt DESC
1916
+ LIMIT 1`,
1917
+ )
1918
+ .get(channelId, threadTs, since);
1919
+ return row ? rowToAgentTask(row) : null;
1920
+ }
1921
+
1872
1922
  export function completeTask(id: string, output?: string): AgentTask | null {
1873
1923
  const oldTask = getTaskById(id);
1874
1924
  if (!oldTask) return null;
@@ -0,0 +1,8 @@
1
+ -- Cross-process mutex for OAuth refresh-token rotation.
2
+ CREATE TABLE IF NOT EXISTS oauth_refresh_locks (
3
+ provider TEXT PRIMARY KEY,
4
+ owner TEXT NOT NULL,
5
+ expiresAt TEXT NOT NULL,
6
+ createdAt TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
7
+ updatedAt TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
8
+ );
@@ -0,0 +1,15 @@
1
+ -- 078_backfill_gpt_5_5_pricing.sql
2
+ -- Backfill Codex GPT-5.5 pricing into existing databases.
3
+ --
4
+ -- The vendored models.dev cache already contains gpt-5.5, and fresh server
5
+ -- boots seed it from src/be/seed-pricing.ts. Existing long-lived DBs can still
6
+ -- be missing those rows, which makes real gpt-5.5 Codex runs land as
7
+ -- costSource='unpriced'. Keep this migration idempotent so every environment
8
+ -- gets the baseline Standard-tier rates.
9
+
10
+ INSERT OR IGNORE INTO pricing
11
+ (provider, model, token_class, effective_from, price_per_million_usd, createdAt, lastUpdatedAt)
12
+ VALUES
13
+ ('codex', 'gpt-5.5', 'input', 0, 5.0, 0, 0),
14
+ ('codex', 'gpt-5.5', 'cached_input', 0, 0.5, 0, 0),
15
+ ('codex', 'gpt-5.5', 'output', 0, 30.0, 0, 0);