@desplega.ai/agent-swarm 1.131.0 → 1.133.1
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.
- package/README.md +2 -2
- package/dist/{actions-w53mbns9.js → actions-cskpayd7.js} +11 -9
- package/dist/{anthropic-messages-rjmd2e7n.js → anthropic-messages-tpxtbx7b.js} +28 -12
- package/dist/{app-m9gwqsqv.js → app-ang77nbb.js} +8 -6
- package/dist/{assistant-bp65m6f3.js → assistant-b4exebdw.js} +16 -13
- package/dist/{azure-openai-responses-c0yjmbfd.js → azure-openai-responses-m3921t4y.js} +8 -8
- package/dist/{boot-reembed-62p0at2k.js → boot-reembed-ps2aftpb.js} +9 -7
- package/dist/{boot-reembed-xmfp6wdh.js → boot-reembed-znyk3nxb.js} +10 -8
- package/dist/{boot-scrub-logs-rr8q2dms.js → boot-scrub-logs-2ax3xgz1.js} +7 -5
- package/dist/{claude-adapter-2y47mrkd.js → claude-adapter-3ct8kgxw.js} +13 -9
- package/dist/{claude-managed-adapter-gv19tdew.js → claude-managed-adapter-akx4ntf2.js} +2 -2
- package/dist/{cli-t1xze5qh.js → cli-13w2hwkh.js} +1 -1
- package/dist/{cli-efxfnq6q.js → cli-1532szb1.js} +8 -6
- package/dist/{cli-d2tfkcga.js → cli-23d3rseb.js} +33 -17
- package/dist/{cli-nm5gtp1x.js → cli-2a75z9c5.js} +196 -7
- package/dist/{cli-fywms69x.js → cli-2t30rj0d.js} +1 -1
- package/dist/{cli-cvgjwnh1.js → cli-3c3tdgab.js} +10 -10
- package/dist/{cli-pm6xbh84.js → cli-3zzxa9jz.js} +46 -16
- package/dist/{cli-cdeg08wx.js → cli-4k6233gc.js} +1 -1
- package/dist/{cli-t55yw3a1.js → cli-5p2tz05r.js} +2 -2
- package/dist/{cli-0rcyq6d9.js → cli-5z2yhzvd.js} +125 -47
- package/dist/{cli-3kf51j15.js → cli-6zfq31nj.js} +9 -3
- package/dist/{cli-er3a4bb0.js → cli-8m9de10a.js} +6 -1
- package/dist/{cli-gm9cbhbq.js → cli-8t14vg9w.js} +27 -8
- package/dist/{cli-72q8m8gk.js → cli-9nfc2es8.js} +6 -6
- package/dist/{cli-5ze9rv97.js → cli-9ta3pwms.js} +11 -4
- package/dist/{cli-78xwvkhj.js → cli-avr0ns7z.js} +2 -2
- package/dist/{cli-n6h8qt8n.js → cli-c897wyhw.js} +12 -7
- package/dist/{cli-2q72qz2s.js → cli-dgk20ft7.js} +15 -6
- package/dist/cli-et1bbwqf.js +13 -0
- package/dist/{cli-bqw0mt1n.js → cli-f5w27pj3.js} +3 -3
- package/dist/{cli-2wadx7p2.js → cli-f8dre4bz.js} +2 -2
- package/dist/{cli-e7ckcg4v.js → cli-ftwpv451.js} +170 -387
- package/dist/{cli-g6b8x1h8.js → cli-g854s2dw.js} +1 -1
- package/dist/{cli-5kx36evn.js → cli-ggmdxeqa.js} +13 -8
- package/dist/cli-hkwfe3qj.js +530 -0
- package/dist/{cli-5dsbcjjw.js → cli-jqwnqx9m.js} +1 -1
- package/dist/{cli-9dr5qzc0.js → cli-ny122vh4.js} +21 -10
- package/dist/{cli-5v4rw6yd.js → cli-pvzqhbs9.js} +2 -2
- package/dist/cli-pzq5rjym.js +15 -0
- package/dist/{cli-xpxde7rm.js → cli-qes8ef67.js} +48 -30
- package/dist/{cli-sajgpkqg.js → cli-qs9hhsvb.js} +1723 -325
- package/dist/cli-qys3rhhz.js +27 -0
- package/dist/{cli-rzbpmxdr.js → cli-r6tpm37h.js} +2 -2
- package/dist/{cli-qv61hp37.js → cli-rch80c76.js} +2 -2
- package/dist/{cli-0258203w.js → cli-rpp0x4zy.js} +137 -17
- package/dist/{cli-5n6e5yfn.js → cli-s1k0wxhe.js} +12 -7
- package/dist/{cli-psh3c4va.js → cli-sfd9v7sj.js} +1 -1
- package/dist/{cli-70xqj0z3.js → cli-sh3ytsyj.js} +49 -39
- package/dist/{cli-zsspvvm2.js → cli-w1mp9jy7.js} +1 -1
- package/dist/{cli-d3x3x1c9.js → cli-xdb4mpnp.js} +2 -2
- package/dist/{cli-57khth04.js → cli-xmyqv00z.js} +120 -4
- package/dist/{cli-yw8vghx2.js → cli-xv8t8p3c.js} +65 -32
- package/dist/{cli-f1am9szv.js → cli-xxv5pn4f.js} +1 -1
- package/dist/{cli-t76vw5db.js → cli-y08jvxzx.js} +601 -140
- package/dist/{cli-a4jzdpa3.js → cli-zfbf3srj.js} +650 -245
- package/dist/{cli-a40rnf71.js → cli-zmq89k34.js} +6 -6
- package/dist/{cli-qmdnhpej.js → cli-zr9q011v.js} +23 -13
- package/dist/cli.js +22 -22
- package/dist/{codex-adapter-d24q75wk.js → codex-adapter-bef93pq4.js} +9 -7
- package/dist/{codex-hook-hq54t9ft.js → codex-hook-e8dba65p.js} +3 -2
- package/dist/{codex-session-runner-g50vcsex.js → codex-session-runner-5pkxys7x.js} +9 -7
- package/dist/{commands-c3nrc5hg.js → commands-e23s1bff.js} +9 -7
- package/dist/{db-nt7fg3tw.js → db-m3nabatn.js} +17 -5
- package/dist/{google-generative-ai-hysrd8qx.js → google-generative-ai-8maw55xh.js} +11 -8
- package/dist/{google-vertex-p5arrga0.js → google-vertex-8vjqmx8g.js} +11 -8
- package/dist/{handlers-fkr6swat.js → handlers-akazedvr.js} +16 -13
- package/dist/{hook-77wpvnjc.js → hook-ze4cpf0v.js} +55 -41
- package/dist/{hook-g019yehy.js → hook-zfqyyenw.js} +10 -7
- package/dist/{http-jewj0syk.js → http-sja0n6kw.js} +730 -150
- package/dist/index-0gr8vd6k.js +39 -0
- package/dist/{index-7n2xpbd4.js → index-354vkhdv.js} +154 -74
- package/dist/{index-070v4eyb.js → index-5qthek5t.js} +2 -3
- package/dist/{index-hfvggd0z.js → index-95t3cqq5.js} +16 -14
- package/dist/{index-9znhar3x.js → index-sea66619.js} +3 -4
- package/dist/{index-6gm5he8h.js → index-vpn36mma.js} +15 -13
- package/dist/{keepalive-j0fjvqfd.js → keepalive-3sp3meqj.js} +16 -10
- package/dist/{lead-1n8x30xe.js → lead-mefeewkf.js} +39 -36
- package/dist/{maintenance-gvev11sa.js → maintenance-8tbrbjfg.js} +10 -8
- package/dist/mistral-conversations-00zrp7g4.js +717 -0
- package/dist/{oauth-refresh-sweep-s9ecsvh0.js → oauth-refresh-sweep-srjwjz6m.js} +8 -6
- package/dist/{onboard-pvm5b38w.js → onboard-mt2tgyjg.js} +7 -7
- package/dist/{openai-codex-responses-5g27pd6f.js → openai-codex-responses-map32h2s.js} +18 -18
- package/dist/{openai-completions-3vkmfz2a.js → openai-completions-w871b1cq.js} +12 -11
- package/dist/{openai-responses-dg4t7j7a.js → openai-responses-b0j7xnw8.js} +15 -12
- package/dist/{opencode-adapter-1gm38aba.js → opencode-adapter-b39jefcj.js} +5 -3
- package/dist/{openrouter-images-g3gm8931.js → openrouter-images-srfkywy0.js} +4 -4
- package/dist/{otel-impl-p6jvgd9c.js → otel-impl-z6pxt3j9.js} +224 -9
- package/dist/{pi-mono-adapter-t091s27g.js → pi-mono-adapter-btvqmj58.js} +1138 -408
- package/dist/{pricing-refresh-k49rcyed.js → pricing-refresh-kyq8cezh.js} +9 -7
- package/dist/{rbac-roles-cr80g96t.js → rbac-roles-3ryfrfna.js} +8 -6
- package/dist/{rbac-roles-30pspvy7.js → rbac-roles-6qfvbee1.js} +9 -7
- package/dist/{render-v2-xm457ng1.js → render-v2-p61ppjey.js} +8 -6
- package/dist/{seed-pricing-yhpv35fr.js → seed-pricing-j56bbj4y.js} +8 -6
- package/dist/{setup-nytnmwwr.js → setup-tpr6c1kn.js} +2 -2
- package/dist/{worker-msytr2cw.js → worker-9w1v0mbb.js} +39 -36
- package/openapi.json +542 -16
- package/package.json +6 -6
- package/src/be/budget-refusal-notify.ts +14 -7
- package/src/be/db-queries/task-artifacts.ts +182 -0
- package/src/be/db.ts +783 -169
- package/src/be/events.ts +5 -0
- package/src/be/migrations/130_scratch_script_retention_grace.sql +6 -0
- package/src/be/migrations/131_session_logs_created_at_index.sql +2 -0
- package/src/be/migrations/132_multi_runtime_instances.sql +44 -0
- package/src/be/migrations/133_workflow_run_trigger_type.sql +12 -0
- package/src/be/migrations/135_backfill_task_pull_request_attachments.sql +228 -0
- package/src/be/migrations/136_task_requester_provenance.sql +37 -0
- package/src/be/migrations/137_memory_retrieval_composite_index.sql +1 -0
- package/src/be/multi-runtime.ts +430 -0
- package/src/be/scripts/db.ts +43 -1
- package/src/be/scripts/extract-schema.ts +3 -1
- package/src/be/scripts/retention.ts +164 -0
- package/src/be/scripts/run-saved.ts +16 -1
- package/src/be/scripts/typecheck.ts +3 -2
- package/src/be/seed-skills/index.ts +7 -0
- package/src/be/steering.ts +1 -1
- package/src/be/swarm-config-guard.ts +18 -0
- package/src/commands/credential-wait.ts +54 -0
- package/src/commands/onboard/steps/core-credentials.tsx +1 -1
- package/src/commands/onboard/steps/custom-templates.tsx +1 -1
- package/src/commands/onboard/steps/harness-credentials.tsx +1 -1
- package/src/commands/onboard/steps/health-check.tsx +1 -1
- package/src/commands/onboard/steps/start.tsx +1 -1
- package/src/commands/profile-sync-audit.ts +56 -0
- package/src/commands/profile-sync.ts +287 -15
- package/src/commands/provider-credentials.ts +46 -19
- package/src/commands/runner.ts +196 -33
- package/src/commands/templates.ts +22 -0
- package/src/github/handlers.ts +55 -9
- package/src/heartbeat/heartbeat.ts +71 -12
- package/src/hooks/hook.ts +49 -33
- package/src/http/active-sessions.ts +11 -1
- package/src/http/agents.ts +261 -30
- package/src/http/all-routes.ts +1 -0
- package/src/http/config.ts +13 -2
- package/src/http/core.ts +102 -4
- package/src/http/db-query-bounded.ts +313 -0
- package/src/http/db-query-shared.ts +249 -0
- package/src/http/db-query.ts +54 -42
- package/src/http/events.ts +2 -0
- package/src/http/index.ts +27 -2
- package/src/http/mcp-bridge.ts +37 -3
- package/src/http/mcp.ts +12 -0
- package/src/http/memory.ts +13 -2
- package/src/http/openapi.ts +1 -0
- package/src/http/poll.ts +70 -27
- package/src/http/route-def.ts +43 -9
- package/src/http/schedules.ts +2 -2
- package/src/http/scripts.ts +31 -4
- package/src/http/session-data.ts +44 -0
- package/src/http/tasks.ts +2 -2
- package/src/http/users.ts +74 -52
- package/src/http/webhooks.ts +5 -2
- package/src/http/workflows.ts +1 -4
- package/src/http/x.ts +14 -0
- package/src/integrations/kapso/inbound.ts +1 -1
- package/src/jira/outbound.ts +39 -8
- package/src/jira/webhook-lifecycle.ts +13 -2
- package/src/linear/outbound.ts +43 -10
- package/src/oauth/keepalive.ts +13 -2
- package/src/prompts/base-prompt.ts +1 -1
- package/src/prompts/session-templates.ts +45 -7
- package/src/providers/claude-adapter.ts +6 -0
- package/src/providers/claude-managed-models.ts +34 -10
- package/src/providers/codex-adapter.ts +6 -0
- package/src/providers/opencode-adapter.ts +6 -0
- package/src/providers/pi-mono-adapter.ts +8 -0
- package/src/providers/pricing-sources.md +24 -0
- package/src/queue-stall-alarm.ts +216 -0
- package/src/scheduler/scheduler.ts +7 -0
- package/src/script-workflows/executor.ts +2 -1
- package/src/scripts-runtime/executors/native.ts +2 -1
- package/src/scripts-runtime/executors/types.ts +5 -0
- package/src/scripts-runtime/loader.ts +5 -0
- package/src/scripts-runtime/swarm-config.ts +8 -0
- package/src/scripts-runtime/swarm-sdk.ts +7 -0
- package/src/scripts-runtime/types/stdlib.d.ts +1 -0
- package/src/scripts-runtime/types/swarm-sdk.d.ts +1 -0
- package/src/tests/budget-claim-gate.test.ts +20 -0
- package/src/tests/claude-adapter.test.ts +32 -0
- package/src/tests/claude-managed-adapter.test.ts +38 -7
- package/src/tests/codex-adapter.test.ts +30 -0
- package/src/tests/create-task-validation.test.ts +128 -0
- package/src/tests/credential-wait.test.ts +67 -1
- package/src/tests/db-query.test.ts +711 -2
- package/src/tests/http-api-integration.test.ts +81 -0
- package/src/tests/http-users.test.ts +166 -1
- package/src/tests/identity-field-budget.test.ts +176 -0
- package/src/tests/mcp-tools-user.test.ts +34 -0
- package/src/tests/mcp-user-route.test.ts +17 -0
- package/src/tests/metrics-http.test.ts +44 -0
- package/src/tests/multi-runtime-registration.test.ts +2703 -0
- package/src/tests/openapi-response-contract.test.ts +24 -0
- package/src/tests/openapi-route-headers.test.ts +83 -0
- package/src/tests/opencode-adapter.test.ts +40 -0
- package/src/tests/outbound-listener-rejection.test.ts +138 -0
- package/src/tests/pi-mono-adapter.test.ts +98 -0
- package/src/tests/profile-sync-warning.test.ts +277 -0
- package/src/tests/profile-sync.test.ts +232 -6
- package/src/tests/prompt-template-session.test.ts +32 -3
- package/src/tests/queue-stall-alarm.test.ts +226 -0
- package/src/tests/requester-comms.test.ts +32 -0
- package/src/tests/runner-requester-profile.test.ts +38 -0
- package/src/tests/sandboxed-process.test.ts +45 -0
- package/src/tests/schedule-target-type.test.ts +38 -0
- package/src/tests/script-connections.test.ts +16 -3
- package/src/tests/scripts-external-api.test.ts +8 -3
- package/src/tests/scripts-http.test.ts +12 -0
- package/src/tests/scripts-retention.test.ts +384 -0
- package/src/tests/scripts-runtime-identity.test.ts +375 -0
- package/src/tests/scripts-runtime.test.ts +4 -3
- package/src/tests/sdk-allowlist.test.ts +47 -0
- package/src/tests/send-task-requested-by.test.ts +9 -0
- package/src/tests/session-costs.test.ts +506 -1
- package/src/tests/shutdown-error-scrubbing.test.ts +92 -0
- package/src/tests/steering-core.test.ts +28 -0
- package/src/tests/task-artifacts.test.ts +386 -0
- package/src/tests/task-attachments-schema.test.ts +17 -1
- package/src/tests/task-completion-idempotency.test.ts +57 -0
- package/src/tests/task-pull-request-backfill-migration.test.ts +321 -0
- package/src/tests/update-profile-api.test.ts +67 -39
- package/src/tests/update-profile-auth.test.ts +48 -4
- package/src/tests/user-attribution-migration.test.ts +54 -0
- package/src/tests/user-identity.test.ts +72 -0
- package/src/tests/workflow-async-v2.test.ts +39 -1
- package/src/tests/workflow-http-v2.test.ts +3 -2
- package/src/tests/workflow-mcp-trigger-schema.test.ts +52 -3
- package/src/tests/workflow-retry-v2.test.ts +45 -1
- package/src/tests/workflow-swarm-script.test.ts +14 -5
- package/src/tools/db-query.ts +18 -8
- package/src/tools/get-task-details.ts +24 -3
- package/src/tools/manage-user.ts +36 -3
- package/src/tools/poll-task.ts +93 -14
- package/src/tools/script-common.ts +5 -0
- package/src/tools/send-task.ts +1 -4
- package/src/tools/store-progress.ts +13 -2
- package/src/tools/swarm-config/delete-config.ts +9 -0
- package/src/tools/swarm-config/set-config.ts +3 -2
- package/src/tools/task-action.ts +30 -0
- package/src/tools/task-tool-ctx.ts +8 -1
- package/src/tools/update-profile.ts +16 -21
- package/src/tools/utils.ts +5 -0
- package/src/tools/workflows/trigger-workflow.ts +7 -1
- package/src/types.ts +165 -1
- package/src/utils/github-pull-request.ts +76 -0
- package/src/utils/identity-field-budget.ts +73 -0
- package/src/utils/multi-runtime.ts +20 -0
- package/src/utils/requester-comms.ts +24 -0
- package/src/utils/sandboxed-process.ts +40 -14
- package/src/workflows/engine.ts +22 -2
- package/src/workflows/executors/swarm-script.ts +24 -1
- package/src/workflows/resume.ts +19 -11
- package/src/workflows/retry-poller.ts +1 -0
- package/src/workflows/triggers.ts +4 -0
- package/templates/schedules/daily-compounding-reflection/content.md +12 -3
- package/templates/skills/db-query-guidance/config.json +14 -0
- package/templates/skills/db-query-guidance/content.md +57 -0
- package/dist/cli-3te4jdkm.js +0 -225
- package/dist/cli-bmze986d.js +0 -7
- package/dist/cli-kh3ba2zf.js +0 -607
- package/dist/index-hp1kdbjj.js +0 -36
- package/dist/mistral-conversations-81fwav0j.js +0 -30578
- package/dist/otel-nqa12wf3.js +0 -3149
- package/dist/{pi-messages-tqj8q6b9.js → pi-messages-sssy32x4.js} +3 -3
package/src/tools/poll-task.ts
CHANGED
|
@@ -2,18 +2,23 @@ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
2
2
|
import { addMinutes } from "date-fns";
|
|
3
3
|
import * as z from "zod";
|
|
4
4
|
import {
|
|
5
|
+
getActiveTaskCount,
|
|
5
6
|
getAgentById,
|
|
6
7
|
getDb,
|
|
7
8
|
getOfferedTasksForAgent,
|
|
8
9
|
getPendingTaskForAgent,
|
|
9
10
|
getUnassignedTasksCount,
|
|
11
|
+
hasCapacity,
|
|
10
12
|
incrementEmptyPollCount,
|
|
11
13
|
MAX_EMPTY_POLLS,
|
|
12
14
|
resetEmptyPollCount,
|
|
13
15
|
startTask,
|
|
14
16
|
updateAgentStatus,
|
|
15
17
|
} from "@/be/db";
|
|
18
|
+
import { touchRuntimeInstance } from "@/be/multi-runtime";
|
|
16
19
|
import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "@/tools/utils";
|
|
20
|
+
import type { AgentTask } from "@/types";
|
|
21
|
+
import { isMultiRuntimeEnabled } from "@/utils/multi-runtime";
|
|
17
22
|
import { looseAgentTaskOutputSchema } from "./get-task-details";
|
|
18
23
|
|
|
19
24
|
const DEFAULT_POLL_INTERVAL_MS = 2000;
|
|
@@ -70,6 +75,28 @@ export const registerPollTaskTool = (server: McpServer) => {
|
|
|
70
75
|
// touching the bookkeeping path below.
|
|
71
76
|
const wasBudgetRefused: boolean = false;
|
|
72
77
|
|
|
78
|
+
// Second dispatch entrypoint alongside HTTP /api/poll, so it needs the
|
|
79
|
+
// same gate: a process whose runtime is gone must not pick up work
|
|
80
|
+
// beside its replacement. Touch validates ownership+liveness and
|
|
81
|
+
// refreshes it; it cannot revive a retired runtime.
|
|
82
|
+
if (
|
|
83
|
+
isMultiRuntimeEnabled() &&
|
|
84
|
+
!(
|
|
85
|
+
requestInfo.runtimeInstanceId &&
|
|
86
|
+
touchRuntimeInstance(requestInfo.runtimeInstanceId, agentId)
|
|
87
|
+
)
|
|
88
|
+
) {
|
|
89
|
+
return toolOk("No task available.", {
|
|
90
|
+
details: "No task available for this runtime.",
|
|
91
|
+
data: {
|
|
92
|
+
yourAgentId: requestInfo.agentId,
|
|
93
|
+
offeredTasks: [],
|
|
94
|
+
availableCount: 0,
|
|
95
|
+
waitedForSeconds: 0,
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
73
100
|
const agent = getAgentById(agentId);
|
|
74
101
|
if (!agent) {
|
|
75
102
|
return toolErr(`Agent with ID "${agentId}" not found in the swarm.`, {
|
|
@@ -104,26 +131,78 @@ export const registerPollTaskTool = (server: McpServer) => {
|
|
|
104
131
|
// Poll for pending tasks
|
|
105
132
|
while (new Date() < maxTime) {
|
|
106
133
|
// Fetch and update in a single transaction to avoid race conditions
|
|
107
|
-
const
|
|
108
|
-
|
|
134
|
+
const outcome = getDb().transaction(
|
|
135
|
+
(): AgentTask | "at-capacity" | "runtime-unavailable" | null => {
|
|
136
|
+
// The entry gate only proves liveness when the long poll began;
|
|
137
|
+
// the runtime must be live at the exact moment work is acquired,
|
|
138
|
+
// so revalidate in the same transaction that can start the task.
|
|
139
|
+
// Touch refreshes an already-live row and cannot revive one.
|
|
140
|
+
if (
|
|
141
|
+
isMultiRuntimeEnabled() &&
|
|
142
|
+
!(
|
|
143
|
+
requestInfo.runtimeInstanceId &&
|
|
144
|
+
touchRuntimeInstance(requestInfo.runtimeInstanceId, agentId)
|
|
145
|
+
)
|
|
146
|
+
) {
|
|
147
|
+
return "runtime-unavailable";
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const agentNow = getAgentById(agentId)!;
|
|
151
|
+
|
|
152
|
+
if (agentNow.status !== "busy") {
|
|
153
|
+
updateAgentStatus(agentId, "idle");
|
|
154
|
+
}
|
|
109
155
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}
|
|
156
|
+
const pendingTask = getPendingTaskForAgent(agentId);
|
|
157
|
+
if (!pendingTask) return null;
|
|
113
158
|
|
|
114
|
-
|
|
115
|
-
|
|
159
|
+
// Logical capacity is decided inside the same transaction as the
|
|
160
|
+
// start transition: several runtimes of one agent race this
|
|
161
|
+
// dispatch, and a check outside it would let each of them start a
|
|
162
|
+
// task past the agent's limit. Same gate as HTTP /api/poll.
|
|
163
|
+
if (!hasCapacity(agentId)) return "at-capacity";
|
|
116
164
|
|
|
117
|
-
|
|
165
|
+
const maybeTask = startTask(pendingTask.id);
|
|
118
166
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
167
|
+
if (maybeTask) {
|
|
168
|
+
// Update automatically in case the agent forgets xd
|
|
169
|
+
updateAgentStatus(agentId, "busy");
|
|
170
|
+
}
|
|
123
171
|
|
|
124
|
-
|
|
125
|
-
|
|
172
|
+
return maybeTask;
|
|
173
|
+
},
|
|
174
|
+
)();
|
|
175
|
+
|
|
176
|
+
if (outcome === "runtime-unavailable") {
|
|
177
|
+
// The runtime was retired while this call waited: stop immediately
|
|
178
|
+
// rather than keep polling as it, and skip the exit counter — this
|
|
179
|
+
// is a refusal, not an empty poll.
|
|
180
|
+
return toolOk("No task available.", {
|
|
181
|
+
details: "No task available for this runtime.",
|
|
182
|
+
data: {
|
|
183
|
+
yourAgentId: requestInfo.agentId,
|
|
184
|
+
offeredTasks: [],
|
|
185
|
+
availableCount: 0,
|
|
186
|
+
waitedForSeconds: Math.round((Date.now() - now.getTime()) / 1000),
|
|
187
|
+
},
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (outcome === "at-capacity") {
|
|
192
|
+
// A capacity refusal is not an empty poll (refused ≠ empty, D-R3):
|
|
193
|
+
// return without advancing the exit counter.
|
|
194
|
+
return toolOk("No task available.", {
|
|
195
|
+
details: `You are at capacity (${getActiveTaskCount(agentId)} active task(s)). Complete a task before polling for more.`,
|
|
196
|
+
data: {
|
|
197
|
+
yourAgentId: requestInfo.agentId,
|
|
198
|
+
offeredTasks: [],
|
|
199
|
+
availableCount: getUnassignedTasksCount(),
|
|
200
|
+
waitedForSeconds: Math.round((Date.now() - now.getTime()) / 1000),
|
|
201
|
+
},
|
|
202
|
+
});
|
|
203
|
+
}
|
|
126
204
|
|
|
205
|
+
const startedTask = outcome;
|
|
127
206
|
if (startedTask) {
|
|
128
207
|
// Reset empty poll count when task is assigned
|
|
129
208
|
resetEmptyPollCount(agentId);
|
|
@@ -172,6 +172,11 @@ export async function proxyScriptsApi(args: {
|
|
|
172
172
|
if (args.requestInfo.sourceTaskId) {
|
|
173
173
|
headers["X-Source-Task-Id"] = args.requestInfo.sourceTaskId;
|
|
174
174
|
}
|
|
175
|
+
// Runtime identity is process context from the worker's MCP session — it
|
|
176
|
+
// rides the proxy like the agent identity, never as tool or script input.
|
|
177
|
+
if (args.requestInfo.runtimeInstanceId) {
|
|
178
|
+
headers["X-Runtime-Instance-ID"] = args.requestInfo.runtimeInstanceId;
|
|
179
|
+
}
|
|
175
180
|
const res = await fetch(`${apiBaseUrl()}${args.path}`, {
|
|
176
181
|
method: args.method,
|
|
177
182
|
headers,
|
package/src/tools/send-task.ts
CHANGED
|
@@ -223,11 +223,8 @@ export async function sendTaskHandler(
|
|
|
223
223
|
|
|
224
224
|
const creatorAgentId = ctx.kind === "owner" ? ctx.agentId : undefined;
|
|
225
225
|
const sourceTaskId = ctx.kind === "owner" ? ctx.sourceTaskId : undefined;
|
|
226
|
-
const callerTask = sourceTaskId ? getTaskById(sourceTaskId) : null;
|
|
227
226
|
const requestedByUserId =
|
|
228
|
-
ctx.kind === "user"
|
|
229
|
-
? ctx.userId
|
|
230
|
-
: (inputRequestedByUserId ?? callerTask?.requestedByUserId ?? undefined);
|
|
227
|
+
ctx.kind === "user" ? ctx.userId : (inputRequestedByUserId ?? undefined);
|
|
231
228
|
|
|
232
229
|
if (ctx.kind === "owner" && agentId === ctx.agentId) {
|
|
233
230
|
return toolErr("Cannot send a task to yourself, are you drunk?", {
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
import { createWorkerTaskFollowUp } from "@/tasks/worker-follow-up";
|
|
25
25
|
import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "@/tools/utils";
|
|
26
26
|
import { AgentTaskStatusSchema, AttachmentInputSchema, isTerminalTaskStatus } from "@/types";
|
|
27
|
+
import { scrubSecrets } from "../utils/secret-scrubber";
|
|
27
28
|
|
|
28
29
|
// Phase 11: the `cost` / `costData` field was removed from this tool's input
|
|
29
30
|
// schema. Adapters (claude/codex/pi/opencode/devin/claude-managed) are the
|
|
@@ -400,7 +401,12 @@ export const registerStoreProgressTool = (server: McpServer) => {
|
|
|
400
401
|
} catch {
|
|
401
402
|
// Non-blocking — task completion memory failure should not affect task status
|
|
402
403
|
}
|
|
403
|
-
})()
|
|
404
|
+
})().catch((err) =>
|
|
405
|
+
console.error(
|
|
406
|
+
"[store-progress] task completion memory write failed:",
|
|
407
|
+
scrubSecrets(err instanceof Error ? err.message : String(err)),
|
|
408
|
+
),
|
|
409
|
+
);
|
|
404
410
|
}
|
|
405
411
|
|
|
406
412
|
if (shouldRunTerminalSideEffects) {
|
|
@@ -436,7 +442,12 @@ export const registerStoreProgressTool = (server: McpServer) => {
|
|
|
436
442
|
err instanceof Error ? err.message : String(err),
|
|
437
443
|
);
|
|
438
444
|
}
|
|
439
|
-
})()
|
|
445
|
+
})().catch((err) =>
|
|
446
|
+
console.error(
|
|
447
|
+
"[store-progress] server rater run failed:",
|
|
448
|
+
scrubSecrets(err instanceof Error ? err.message : String(err)),
|
|
449
|
+
),
|
|
450
|
+
);
|
|
440
451
|
}
|
|
441
452
|
|
|
442
453
|
// Create follow-up task for the lead when a worker task finishes.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import * as z from "zod";
|
|
3
3
|
import { deleteSwarmConfig, getAgentById, getSwarmConfigLookupById } from "@/be/db";
|
|
4
|
+
import { AGENT_MAX_TASKS_CONFIG_KEY, resetAgentMaxTasksMirror } from "@/be/multi-runtime";
|
|
4
5
|
import { scheduleIntegrationsReload } from "@/http/core";
|
|
5
6
|
import { can } from "@/rbac";
|
|
6
7
|
import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "@/tools/utils";
|
|
@@ -56,6 +57,14 @@ export const registerDeleteConfigTool = (server: McpServer) => {
|
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
const deleted = deleteSwarmConfig(id);
|
|
60
|
+
if (
|
|
61
|
+
deleted &&
|
|
62
|
+
existing.scope === "agent" &&
|
|
63
|
+
existing.scopeId &&
|
|
64
|
+
existing.key === AGENT_MAX_TASKS_CONFIG_KEY
|
|
65
|
+
) {
|
|
66
|
+
resetAgentMaxTasksMirror(existing.scopeId);
|
|
67
|
+
}
|
|
59
68
|
if (!deleted) {
|
|
60
69
|
return toolErr(`Failed to delete config entry "${id}".`, {
|
|
61
70
|
data: { yourAgentId: requestInfo.agentId },
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import * as z from "zod";
|
|
3
|
-
import { getAgentById, maskSecrets
|
|
3
|
+
import { getAgentById, maskSecrets } from "@/be/db";
|
|
4
|
+
import { upsertSwarmConfigWithPolicyMirror } from "@/be/multi-runtime";
|
|
4
5
|
import {
|
|
5
6
|
isReservedConfigKey,
|
|
6
7
|
reservedKeyError,
|
|
@@ -110,7 +111,7 @@ export const registerSetConfigTool = (server: McpServer) => {
|
|
|
110
111
|
return toolErr(validationError, { data: { yourAgentId: requestInfo.agentId } });
|
|
111
112
|
}
|
|
112
113
|
|
|
113
|
-
const config =
|
|
114
|
+
const config = upsertSwarmConfigWithPolicyMirror({
|
|
114
115
|
scope,
|
|
115
116
|
scopeId: scope === "global" ? null : scopeId,
|
|
116
117
|
key,
|
package/src/tools/task-action.ts
CHANGED
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
releaseTask,
|
|
28
28
|
updateTaskClaudeSessionId,
|
|
29
29
|
} from "@/be/db";
|
|
30
|
+
import { touchRuntimeInstance } from "@/be/multi-runtime";
|
|
30
31
|
import { assertOwnsTask, ownerCtx, type ToolCtx } from "@/tools/task-tool-ctx";
|
|
31
32
|
import {
|
|
32
33
|
createToolRegistrar,
|
|
@@ -42,6 +43,7 @@ import {
|
|
|
42
43
|
ReasoningEffortSchema,
|
|
43
44
|
splitLegacyModelAlias,
|
|
44
45
|
} from "@/types";
|
|
46
|
+
import { isMultiRuntimeEnabled } from "@/utils/multi-runtime";
|
|
45
47
|
import { looseAgentTaskOutputSchema } from "./get-task-details";
|
|
46
48
|
|
|
47
49
|
export const TaskActionSchema = z.enum([
|
|
@@ -140,6 +142,28 @@ function agentOnlyActionResult(): SwarmToolResult {
|
|
|
140
142
|
return toolErr("This action is only available to worker agents.");
|
|
141
143
|
}
|
|
142
144
|
|
|
145
|
+
/**
|
|
146
|
+
* Work-acquisition gate shared by claim and accept — the same dispatch gate
|
|
147
|
+
* as HTTP poll and poll-task: a process whose runtime is gone must not take
|
|
148
|
+
* work beside its replacement. Identity travels as request context, never as
|
|
149
|
+
* a tool argument; touch refreshes liveness and cannot revive a retired
|
|
150
|
+
* runtime. Returns null when acquisition may proceed.
|
|
151
|
+
*/
|
|
152
|
+
function staleRuntimeResult(ctx: ToolCtx, agentId: string): TaskActionResult | null {
|
|
153
|
+
if (!isMultiRuntimeEnabled()) return null;
|
|
154
|
+
if (
|
|
155
|
+
ctx.kind === "owner" &&
|
|
156
|
+
ctx.runtimeInstanceId &&
|
|
157
|
+
touchRuntimeInstance(ctx.runtimeInstanceId, agentId)
|
|
158
|
+
) {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
success: false,
|
|
163
|
+
message: "This runtime is no longer registered. Re-register before taking work.",
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
143
167
|
function taskActionResult(result: TaskActionResult, agentId?: string): SwarmToolResult {
|
|
144
168
|
const { refusalSideEffects: _omit, success, message, ...rest } = result;
|
|
145
169
|
const data = {
|
|
@@ -285,6 +309,8 @@ export async function taskActionHandler(
|
|
|
285
309
|
if (!taskId) {
|
|
286
310
|
return { success: false, message: "Task ID is required for 'claim' action." };
|
|
287
311
|
}
|
|
312
|
+
const staleClaimRuntime = staleRuntimeResult(ctx, agentId);
|
|
313
|
+
if (staleClaimRuntime) return staleClaimRuntime;
|
|
288
314
|
// Check capacity before claiming
|
|
289
315
|
if (!hasCapacity(agentId)) {
|
|
290
316
|
const activeCount = getActiveTaskCount(agentId);
|
|
@@ -388,6 +414,10 @@ export async function taskActionHandler(
|
|
|
388
414
|
if (!taskId) {
|
|
389
415
|
return { success: false, message: "Task ID is required for 'accept' action." };
|
|
390
416
|
}
|
|
417
|
+
// Accept is a work-acquisition transition too — gate it before the
|
|
418
|
+
// budget admission below so a retired process cannot take the offer.
|
|
419
|
+
const staleAcceptRuntime = staleRuntimeResult(ctx, agentId);
|
|
420
|
+
if (staleAcceptRuntime) return staleAcceptRuntime;
|
|
391
421
|
const existingTask = getTaskById(taskId);
|
|
392
422
|
if (!existingTask) {
|
|
393
423
|
return { success: false, message: `Task "${taskId}" not found.` };
|
|
@@ -3,13 +3,20 @@ import type { AgentTask, User } from "@/types";
|
|
|
3
3
|
import { type RequestInfo, type SwarmToolResult, toolErr } from "./utils";
|
|
4
4
|
|
|
5
5
|
export type ToolCtx =
|
|
6
|
-
| {
|
|
6
|
+
| {
|
|
7
|
+
kind: "owner";
|
|
8
|
+
agentId?: string;
|
|
9
|
+
runtimeInstanceId?: string;
|
|
10
|
+
sourceTaskId?: string;
|
|
11
|
+
sessionId?: string;
|
|
12
|
+
}
|
|
7
13
|
| { kind: "user"; userId: string; user: User; sessionId?: string };
|
|
8
14
|
|
|
9
15
|
export function ownerCtx(info: RequestInfo): ToolCtx {
|
|
10
16
|
return {
|
|
11
17
|
kind: "owner",
|
|
12
18
|
agentId: info.agentId,
|
|
19
|
+
runtimeInstanceId: info.runtimeInstanceId,
|
|
13
20
|
sourceTaskId: info.sourceTaskId,
|
|
14
21
|
sessionId: info.sessionId,
|
|
15
22
|
};
|
|
@@ -3,7 +3,7 @@ import { tmpdir } from "node:os";
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
5
|
import * as z from "zod";
|
|
6
|
-
import { getAgentById,
|
|
6
|
+
import { getAgentById, updateAgentProfile } from "@/be/db";
|
|
7
7
|
import { can } from "@/rbac";
|
|
8
8
|
import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "@/tools/utils";
|
|
9
9
|
import { type Agent, AgentAvatarSchema, AgentStatusSchema, ProviderNameSchema } from "@/types";
|
|
@@ -110,7 +110,7 @@ export const registerUpdateProfileTool = (server: McpServer) => {
|
|
|
110
110
|
.max(MAX_PROFILE_FILE_LENGTH)
|
|
111
111
|
.optional()
|
|
112
112
|
.describe(
|
|
113
|
-
"Personal CLAUDE.md content. Loaded on session start and synced back on session end.
|
|
113
|
+
"Personal CLAUDE.md content. Loaded on session start and synced back on session end. Above 20,000 characters, updates may only keep or reduce the stored size; move durable overflow into memory.",
|
|
114
114
|
),
|
|
115
115
|
soulMd: z
|
|
116
116
|
.string()
|
|
@@ -118,7 +118,7 @@ export const registerUpdateProfileTool = (server: McpServer) => {
|
|
|
118
118
|
.max(MAX_PROFILE_FILE_LENGTH)
|
|
119
119
|
.optional()
|
|
120
120
|
.describe(
|
|
121
|
-
"Soul content: persona and behavioral directives. Updates both DB and /workspace/SOUL.md. Must be at least 200 characters
|
|
121
|
+
"Soul content: persona and behavioral directives. Updates both DB and /workspace/SOUL.md. Must be at least 200 characters; above 10,000 characters, updates may only keep or reduce the stored size.",
|
|
122
122
|
),
|
|
123
123
|
identityMd: z
|
|
124
124
|
.string()
|
|
@@ -126,7 +126,7 @@ export const registerUpdateProfileTool = (server: McpServer) => {
|
|
|
126
126
|
.max(MAX_PROFILE_FILE_LENGTH)
|
|
127
127
|
.optional()
|
|
128
128
|
.describe(
|
|
129
|
-
"Identity content: expertise and working style. Updates both DB and /workspace/IDENTITY.md. Must be at least 200 characters
|
|
129
|
+
"Identity content: expertise and working style. Updates both DB and /workspace/IDENTITY.md. Must be at least 200 characters; above 10,000 characters, updates may only keep or reduce the stored size.",
|
|
130
130
|
),
|
|
131
131
|
setupScript: z
|
|
132
132
|
.string()
|
|
@@ -140,7 +140,7 @@ export const registerUpdateProfileTool = (server: McpServer) => {
|
|
|
140
140
|
.max(MAX_PROFILE_FILE_LENGTH)
|
|
141
141
|
.optional()
|
|
142
142
|
.describe(
|
|
143
|
-
"Environment-specific operational knowledge.
|
|
143
|
+
"Environment-specific operational knowledge. Synced to /workspace/TOOLS.md. Above 20,000 characters, updates may only keep or reduce the stored size; move durable overflow into memory.",
|
|
144
144
|
),
|
|
145
145
|
heartbeatMd: z
|
|
146
146
|
.string()
|
|
@@ -250,21 +250,14 @@ export const registerUpdateProfileTool = (server: McpServer) => {
|
|
|
250
250
|
}
|
|
251
251
|
}
|
|
252
252
|
|
|
253
|
-
//
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
return toolErr("Target agent not found.", {
|
|
258
|
-
data: { yourAgentId: requestInfo.agentId },
|
|
259
|
-
});
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
// Update profile fields if provided. `avatar` is spread in only when
|
|
264
|
-
// present so `null` (reset) stays distinguishable from "not provided".
|
|
253
|
+
// Apply name and profile fields in one DB transaction so validation or
|
|
254
|
+
// update failures cannot leave a partially-applied combined payload.
|
|
255
|
+
// `avatar` is spread in only when present so `null` (reset) stays
|
|
256
|
+
// distinguishable from "not provided".
|
|
265
257
|
agent = updateAgentProfile(
|
|
266
258
|
targetAgentId,
|
|
267
259
|
{
|
|
260
|
+
name,
|
|
268
261
|
description,
|
|
269
262
|
role,
|
|
270
263
|
capabilities,
|
|
@@ -282,6 +275,12 @@ export const registerUpdateProfileTool = (server: McpServer) => {
|
|
|
282
275
|
},
|
|
283
276
|
);
|
|
284
277
|
|
|
278
|
+
// Do not perform audit or workspace-file side effects when the target
|
|
279
|
+
// disappeared before the transactional update could find it.
|
|
280
|
+
if (!agent) {
|
|
281
|
+
return toolErr("Agent not found.", { data: { yourAgentId: requestInfo.agentId } });
|
|
282
|
+
}
|
|
283
|
+
|
|
285
284
|
if (setupScript !== undefined && previousSetupScript !== undefined) {
|
|
286
285
|
try {
|
|
287
286
|
const diff = await computeSetupScriptDiff(previousSetupScript, setupScript);
|
|
@@ -348,10 +347,6 @@ export const registerUpdateProfileTool = (server: McpServer) => {
|
|
|
348
347
|
}
|
|
349
348
|
}
|
|
350
349
|
|
|
351
|
-
if (!agent) {
|
|
352
|
-
return toolErr("Agent not found.", { data: { yourAgentId: requestInfo.agentId } });
|
|
353
|
-
}
|
|
354
|
-
|
|
355
350
|
const updatedFields: string[] = [];
|
|
356
351
|
if (name !== undefined) updatedFields.push("name");
|
|
357
352
|
if (description !== undefined) updatedFields.push("description");
|
package/src/tools/utils.ts
CHANGED
|
@@ -32,6 +32,8 @@ export function markScriptSdkRequestOrigin<T extends object>(meta: T): T {
|
|
|
32
32
|
export type RequestInfo = {
|
|
33
33
|
sessionId: string | undefined;
|
|
34
34
|
agentId: string | undefined;
|
|
35
|
+
/** Calling worker process, when multi-runtime mode is on. */
|
|
36
|
+
runtimeInstanceId: string | undefined;
|
|
35
37
|
sourceTaskId: string | undefined;
|
|
36
38
|
contextKey: string | undefined;
|
|
37
39
|
callOrigin: "mcp" | "script-sdk";
|
|
@@ -41,6 +43,8 @@ export const getRequestInfo = (req: Meta): RequestInfo => {
|
|
|
41
43
|
const agentIdHeader = req.requestInfo?.headers?.["x-agent-id"];
|
|
42
44
|
const sourceTaskIdHeader = req.requestInfo?.headers?.["x-source-task-id"];
|
|
43
45
|
const contextKeyHeader = req.requestInfo?.headers?.["x-context-key"];
|
|
46
|
+
const runtimeHeader = req.requestInfo?.headers?.["x-runtime-instance-id"];
|
|
47
|
+
const runtimeInstanceId = Array.isArray(runtimeHeader) ? runtimeHeader[0] : runtimeHeader;
|
|
44
48
|
|
|
45
49
|
let agentId: string | undefined;
|
|
46
50
|
if (Array.isArray(agentIdHeader)) {
|
|
@@ -66,6 +70,7 @@ export const getRequestInfo = (req: Meta): RequestInfo => {
|
|
|
66
70
|
return {
|
|
67
71
|
sessionId: req.sessionId || undefined,
|
|
68
72
|
agentId,
|
|
73
|
+
runtimeInstanceId: typeof runtimeInstanceId === "string" ? runtimeInstanceId : undefined,
|
|
69
74
|
sourceTaskId,
|
|
70
75
|
contextKey,
|
|
71
76
|
callOrigin: scriptSdkRequestOrigins.has(req) ? "script-sdk" : "mcp",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
import { resolveTaskAuditUserId } from "@/be/audit-user";
|
|
3
4
|
import { getWorkflow, getWorkflowRun } from "@/be/db";
|
|
4
5
|
import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "@/tools/utils";
|
|
5
6
|
import { getExecutorRegistry, startWorkflowExecution } from "@/workflows";
|
|
@@ -28,7 +29,7 @@ export const registerTriggerWorkflowTool = (server: McpServer) => {
|
|
|
28
29
|
triggerSchema: z.record(z.string(), z.unknown()).optional(),
|
|
29
30
|
}),
|
|
30
31
|
},
|
|
31
|
-
async ({ id, triggerData }) => {
|
|
32
|
+
async ({ id, triggerData }, requestInfo) => {
|
|
32
33
|
try {
|
|
33
34
|
const workflow = getWorkflow(id);
|
|
34
35
|
if (!workflow) {
|
|
@@ -41,6 +42,11 @@ export const registerTriggerWorkflowTool = (server: McpServer) => {
|
|
|
41
42
|
workflow,
|
|
42
43
|
triggerData ?? {},
|
|
43
44
|
getExecutorRegistry(),
|
|
45
|
+
{
|
|
46
|
+
triggerType: "manual",
|
|
47
|
+
requestedByUserId:
|
|
48
|
+
resolveTaskAuditUserId(requestInfo.sourceTaskId, requestInfo.agentId) ?? undefined,
|
|
49
|
+
},
|
|
44
50
|
);
|
|
45
51
|
|
|
46
52
|
// Check if the run was skipped due to cooldown
|