@desplega.ai/agent-swarm 1.129.0 → 1.131.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.
- package/README.md +2 -2
- package/dist/{actions-kfv2dawe.js → actions-w53mbns9.js} +11 -11
- package/dist/{anthropic-messages-3xgme8my.js → anthropic-messages-rjmd2e7n.js} +5 -5
- package/dist/{app-q8vjk6e8.js → app-m9gwqsqv.js} +8 -8
- package/dist/{artifact-hg4t1nx7.js → artifact-jysj3wrg.js} +2 -2
- package/dist/{assistant-g037r345.js → assistant-bp65m6f3.js} +13 -13
- package/dist/{azure-openai-responses-xa4sh9f6.js → azure-openai-responses-c0yjmbfd.js} +8 -5
- package/dist/{boot-reembed-18pzsrxa.js → boot-reembed-62p0at2k.js} +6 -6
- package/dist/{boot-reembed-wpks2cj8.js → boot-reembed-xmfp6wdh.js} +7 -7
- package/dist/{boot-scrub-logs-n7szd57r.js → boot-scrub-logs-rr8q2dms.js} +5 -5
- package/dist/{claude-adapter-qz4r0ajt.js → claude-adapter-2y47mrkd.js} +9 -9
- package/dist/{cli-mhjnbgb6.js → cli-0rcyq6d9.js} +450 -186
- package/dist/{cli-ttkmehy0.js → cli-2q72qz2s.js} +274 -25
- package/dist/{cli-gztgc3hz.js → cli-2wadx7p2.js} +2 -2
- package/dist/cli-3fj60etf.js +153 -0
- package/dist/{cli-7ecgm5v0.js → cli-3kf51j15.js} +1 -1
- package/dist/{cli-vgexpfsr.js → cli-57khth04.js} +6 -3
- package/dist/{cli-2mbe7vk0.js → cli-5dsbcjjw.js} +1 -1
- package/dist/{cli-etr62zy6.js → cli-5kx36evn.js} +2 -2
- package/dist/{cli-v2tr6amf.js → cli-5n6e5yfn.js} +30 -8
- package/dist/{cli-hx02s9zq.js → cli-5v4rw6yd.js} +2 -2
- package/dist/{cli-v37yr6mf.js → cli-5ze9rv97.js} +4 -4
- package/dist/{cli-ch7t5v7n.js → cli-6map995v.js} +9 -4
- package/dist/{cli-h0457a67.js → cli-70xqj0z3.js} +3 -3
- package/dist/{cli-kk4f9r17.js → cli-72q8m8gk.js} +8 -6
- package/dist/{cli-7padk69a.js → cli-78xwvkhj.js} +2 -2
- package/dist/{cli-dxtxacy7.js → cli-9dr5qzc0.js} +45 -8
- package/dist/{cli-17zxdgsq.js → cli-a40rnf71.js} +6 -6
- package/dist/{cli-t53vnnxz.js → cli-a4jzdpa3.js} +2514 -1055
- package/dist/{cli-w10fkrf1.js → cli-akdvwc9v.js} +354 -115
- package/dist/{cli-1qkk5er0.js → cli-bmze986d.js} +1 -1
- package/dist/{cli-ga12dc1d.js → cli-bqw0mt1n.js} +3 -3
- package/dist/{cli-0sf92efp.js → cli-cdeg08wx.js} +1 -1
- package/dist/{cli-a8ynhkyj.js → cli-cvgjwnh1.js} +17 -15
- package/dist/{cli-9fxg3v5m.js → cli-d2tfkcga.js} +4 -4
- package/dist/{cli-nhcd7x43.js → cli-d3x3x1c9.js} +2 -2
- package/dist/{cli-6nh2qxvh.js → cli-darcwax3.js} +1 -1
- package/dist/{cli-yjbgmysx.js → cli-e7ckcg4v.js} +12 -12
- package/dist/{cli-4n2gcjpd.js → cli-efxfnq6q.js} +4 -3
- package/dist/{cli-z9xqqhfk.js → cli-er3a4bb0.js} +1 -1
- package/dist/{cli-xg2eezyv.js → cli-f1am9szv.js} +1 -1
- package/dist/{cli-xy4cwsm9.js → cli-fywms69x.js} +1 -1
- package/dist/{cli-33jj76y8.js → cli-g6b8x1h8.js} +1 -1
- package/dist/{cli-epsvdckx.js → cli-gm9cbhbq.js} +20 -10
- package/dist/{cli-rkndnn90.js → cli-k9qkf1hw.js} +6 -4
- package/dist/{cli-zejvt395.js → cli-kh3ba2zf.js} +1 -1
- package/dist/{cli-gb25p1h2.js → cli-nm5gtp1x.js} +2 -2
- package/dist/{cli-akxnpx17.js → cli-pm6xbh84.js} +1829 -1629
- package/dist/{cli-2k0ztbfp.js → cli-psh3c4va.js} +1 -1
- package/dist/{cli-39vgrsep.js → cli-qv61hp37.js} +2 -2
- package/dist/{cli-hv2fndhm.js → cli-r6kqqbj0.js} +6 -4
- package/dist/{cli-1y1cm570.js → cli-rzbpmxdr.js} +1 -1
- package/dist/{cli-mmrk00vp.js → cli-t1xze5qh.js} +1 -1
- package/dist/{cli-g252a9ky.js → cli-t55yw3a1.js} +2 -2
- package/dist/{cli-7bz8dzqa.js → cli-t76vw5db.js} +1184 -1522
- package/dist/{cli-4n4mdv21.js → cli-xpxde7rm.js} +7 -3
- package/dist/{cli-fpaa5k2q.js → cli-yw8vghx2.js} +54 -18
- package/dist/cli-zsspvvm2.js +490 -0
- package/dist/cli.js +27 -25
- package/dist/{codex-adapter-7v6t7ff6.js → codex-adapter-d24q75wk.js} +7 -7
- package/dist/{codex-hook-amndawyb.js → codex-hook-hq54t9ft.js} +2 -2
- package/dist/{codex-session-runner-e7j4khe2.js → codex-session-runner-g50vcsex.js} +7 -7
- package/dist/{commands-ex0k5tbf.js → commands-c3nrc5hg.js} +5 -5
- package/dist/{db-df7jatfc.js → db-nt7fg3tw.js} +5 -5
- package/dist/{google-generative-ai-y42hnw9p.js → google-generative-ai-hysrd8qx.js} +7 -5
- package/dist/{google-vertex-4sg66gb6.js → google-vertex-p5arrga0.js} +7 -5
- package/dist/{handlers-1j2cfqt2.js → handlers-fkr6swat.js} +13 -13
- package/dist/{hook-nx9a0mj3.js → hook-77wpvnjc.js} +7 -7
- package/dist/{hook-w6yaf6sy.js → hook-g019yehy.js} +8 -8
- package/dist/{http-4tz5h8wg.js → http-jewj0syk.js} +2993 -716
- package/dist/{index-0bpkzb09.js → index-070v4eyb.js} +3 -3
- package/dist/{index-0s67qv7h.js → index-6gm5he8h.js} +11 -11
- package/dist/{index-35gtv6gj.js → index-7n2xpbd4.js} +310 -40
- package/dist/{index-ker3gccb.js → index-9znhar3x.js} +4 -4
- package/dist/{index-5n2c8yay.js → index-hase6zbe.js} +2 -2
- package/dist/{index-kcf3tm9h.js → index-hfvggd0z.js} +12 -12
- package/dist/{index-7c0jqna0.js → index-hp1kdbjj.js} +13 -13
- package/dist/{keepalive-3j4a9168.js → keepalive-j0fjvqfd.js} +7 -7
- package/dist/{lead-6kpevymb.js → lead-1n8x30xe.js} +33 -33
- package/dist/{maintenance-m1k82ean.js → maintenance-gvev11sa.js} +7 -7
- package/dist/{mistral-conversations-bjkdfb6z.js → mistral-conversations-81fwav0j.js} +6 -6
- package/dist/{oauth-refresh-sweep-881k8e4q.js → oauth-refresh-sweep-s9ecsvh0.js} +6 -6
- package/dist/{onboard-c0ykffjn.js → onboard-pvm5b38w.js} +2 -2
- package/dist/{openai-codex-responses-xetseqvt.js → openai-codex-responses-5g27pd6f.js} +43 -23
- package/dist/{openai-completions-5r2g5axa.js → openai-completions-3vkmfz2a.js} +55 -11
- package/dist/{openai-responses-ce67hd23.js → openai-responses-dg4t7j7a.js} +8 -5
- package/dist/{openrouter-images-pzfp22jb.js → openrouter-images-g3gm8931.js} +1 -1
- package/dist/{otel-impl-sqagjerv.js → otel-impl-p6jvgd9c.js} +6 -4
- package/dist/{otel-3mp81ppd.js → otel-nqa12wf3.js} +2 -2
- package/dist/{pi-mono-adapter-k584k16g.js → pi-mono-adapter-t091s27g.js} +17117 -7517
- package/dist/{pricing-refresh-y82whbf9.js → pricing-refresh-k49rcyed.js} +7 -7
- package/dist/{rbac-roles-nvkqmzyd.js → rbac-roles-30pspvy7.js} +7 -7
- package/dist/{rbac-roles-4ht8gegc.js → rbac-roles-cr80g96t.js} +6 -6
- package/dist/{render-v2-wvqdgvvp.js → render-v2-xm457ng1.js} +8 -8
- package/dist/{seed-pricing-fy3rxhde.js → seed-pricing-yhpv35fr.js} +6 -6
- package/dist/{setup-ak5cnc6f.js → setup-nytnmwwr.js} +2 -2
- package/dist/{worker-d8769q8a.js → worker-msytr2cw.js} +33 -33
- package/openapi.json +34039 -11036
- package/package.json +8 -6
- package/src/apps/definition.ts +368 -26
- package/src/apps/row-store.ts +66 -10
- package/src/apps/schema-migrate.ts +95 -1
- package/src/apps/store.ts +13 -4
- package/src/apps/sync-run-as.ts +18 -0
- package/src/apps/sync.ts +934 -0
- package/src/apps/version.ts +16 -2
- package/src/assets/key.ts +8 -1
- package/src/be/asset-key-audit.ts +3 -1
- package/src/be/db-queries/mcp-oauth.ts +297 -24
- package/src/be/db.ts +42 -1
- package/src/be/migrations/129_asset_keys_apps_scripts.sql +161 -0
- package/src/be/scripts/db.ts +5 -2
- package/src/be/scripts/typecheck.ts +1 -0
- package/src/be/seed-scripts/catalog/app-sync-run.ts +32 -0
- package/src/be/seed-scripts/catalog/github-issues-pull.ts +102 -0
- package/src/be/seed-scripts/index.ts +18 -0
- package/src/be/seed-skills/index.ts +7 -0
- package/src/http/active-sessions.ts +26 -18
- package/src/http/agents.ts +94 -27
- package/src/http/api-keys.ts +91 -17
- package/src/http/approval-requests.ts +87 -10
- package/src/http/apps.ts +361 -53
- package/src/http/assets.ts +165 -31
- package/src/http/budgets.ts +7 -5
- package/src/http/codex-oauth-keep-warm.ts +21 -3
- package/src/http/config.ts +46 -16
- package/src/http/context.ts +33 -6
- package/src/http/ecosystem.ts +20 -6
- package/src/http/events.ts +24 -9
- package/src/http/favorites.ts +20 -6
- package/src/http/fs.ts +60 -17
- package/src/http/heartbeat.ts +18 -4
- package/src/http/inbox-state.ts +12 -6
- package/src/http/integrations.ts +25 -5
- package/src/http/kv.ts +36 -15
- package/src/http/mcp-bridge.ts +5 -1
- package/src/http/mcp-oauth.ts +332 -17
- package/src/http/mcp-servers.ts +56 -18
- package/src/http/memory.ts +282 -29
- package/src/http/metrics.ts +80 -17
- package/src/http/oauth-callback.ts +10 -4
- package/src/http/oauth-generic.ts +5 -1
- package/src/http/oauth-locks.ts +6 -3
- package/src/http/openapi.ts +57 -9
- package/src/http/page-proxy.ts +25 -5
- package/src/http/pages-public.ts +37 -20
- package/src/http/pages.ts +93 -28
- package/src/http/poll.ts +89 -5
- package/src/http/pricing.ts +9 -6
- package/src/http/prompt-templates.ts +85 -21
- package/src/http/route-def.ts +67 -4
- package/src/http/schedules.ts +63 -14
- package/src/http/script-connection-proxy.ts +30 -4
- package/src/http/script-connections.ts +393 -43
- package/src/http/script-runs.ts +73 -19
- package/src/http/scripts.ts +257 -39
- package/src/http/session-data.ts +87 -14
- package/src/http/sessions.ts +80 -5
- package/src/http/skills.ts +115 -35
- package/src/http/stats.ts +94 -13
- package/src/http/task-templates.ts +6 -4
- package/src/http/tasks.ts +230 -61
- package/src/http/trackers/jira.ts +90 -30
- package/src/http/trackers/linear.ts +46 -12
- package/src/http/users.ts +162 -25
- package/src/http/utils.ts +12 -2
- package/src/http/webhooks.ts +30 -16
- package/src/http/workflow-events.ts +18 -5
- package/src/http/workflows.ts +128 -39
- package/src/http/x.ts +37 -6
- package/src/oauth/ensure-mcp-token.ts +16 -1
- package/src/oauth/mcp-wrapper.ts +281 -26
- package/src/providers/pi-mono-adapter.ts +1 -1
- package/src/scripts-runtime/credential-broker/README.md +1 -1
- package/src/scripts-runtime/credential-broker/fetch-patch.ts +12 -2
- package/src/scripts-runtime/sdk-allowlist.ts +1 -0
- package/src/scripts-runtime/stdlib/fetch.ts +6 -0
- package/src/scripts-runtime/types/stdlib.d.ts +1 -0
- package/src/scripts-runtime/types/swarm-sdk.d.ts +1 -0
- package/src/server.ts +2 -0
- package/src/tests/apps-rbac.test.ts +110 -0
- package/src/tests/apps-spike2.test.ts +4 -1
- package/src/tests/apps-sync-engine.test.ts +2540 -0
- package/src/tests/apps-sync.test.ts +1627 -0
- package/src/tests/asset-key-api.test.ts +48 -2
- package/src/tests/asset-key-invariants.test.ts +92 -0
- package/src/tests/asset-key-migration.test.ts +174 -8
- package/src/tests/asset-key-normalization.test.ts +2 -0
- package/src/tests/credential-broker.test.ts +27 -2
- package/src/tests/mcp-oauth-dcr-reuse.test.ts +743 -0
- package/src/tests/mcp-oauth-ensure-token.test.ts +45 -0
- package/src/tests/mcp-oauth-manual-client.test.ts +135 -2
- package/src/tests/mcp-oauth-queries.test.ts +153 -0
- package/src/tests/mcp-oauth-wrapper.test.ts +432 -0
- package/src/tests/memory-http-recall-gating.test.ts +2 -0
- package/src/tests/openapi-response-contract.test.ts +138 -0
- package/src/tests/rbac-admission-e2e.test.ts +68 -0
- package/src/tests/script-connections.test.ts +3 -0
- package/src/tests/scripts-runtime-fetch.test.ts +121 -0
- package/src/tests/scripts-runtime-secret-egress.test.ts +4 -2
- package/src/tests/seed-scripts.test.ts +2 -2
- package/src/tests/swarm-tool-result-gate.test.ts +32 -0
- package/src/tests/task-working-dir.test.ts +8 -4
- package/src/tests/whoami-route.test.ts +127 -0
- package/src/tests/workflow-engine-v2.test.ts +80 -7
- package/src/tests/workflow-swarm-script.test.ts +104 -0
- package/src/tools/app-get.ts +10 -2
- package/src/tools/app-rollback.ts +1 -0
- package/src/tools/app-sync.ts +112 -0
- package/src/tools/script-common.ts +20 -0
- package/src/tools/tool-config.ts +2 -1
- package/src/tools/utils.ts +11 -0
- package/src/tools/workflows/create-workflow.ts +4 -1
- package/src/types.ts +1394 -1206
- package/src/utils/template.ts +8 -1
- package/src/utils/zod-openapi.ts +11 -0
- package/src/workflows/engine.ts +27 -4
- package/src/workflows/executors/swarm-script.ts +28 -0
- package/src/workflows/retry-poller.ts +19 -2
- package/templates/skills/apps/content.md +94 -5
- package/templates/skills/asset-namespaces/content.md +2 -2
- package/templates/skills/scheduled-task-resilience/SKILL.md +102 -0
- package/templates/skills/scheduled-task-resilience/config.json +14 -0
- package/templates/skills/scheduled-task-resilience/content.md +97 -0
- package/dist/cli-7wmybrt5.js +0 -115
- package/dist/cli-d91pvmq1.js +0 -14
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
-- Extend canonical asset namespaces to apps and scripts.
|
|
2
|
+
|
|
3
|
+
ALTER TABLE apps ADD COLUMN "key" TEXT NOT NULL DEFAULT 'shared/';
|
|
4
|
+
ALTER TABLE scripts ADD COLUMN "key" TEXT NOT NULL DEFAULT 'shared/';
|
|
5
|
+
|
|
6
|
+
UPDATE apps SET "key" = 'shared/' WHERE "key" IS NULL OR trim("key") = '';
|
|
7
|
+
UPDATE scripts SET "key" = 'shared/' WHERE "key" IS NULL OR trim("key") = '';
|
|
8
|
+
|
|
9
|
+
CREATE INDEX idx_apps_asset_key ON apps("key");
|
|
10
|
+
CREATE INDEX idx_scripts_asset_key ON scripts("key");
|
|
11
|
+
|
|
12
|
+
-- SQLite cannot widen a CHECK constraint in place. Rebuild the append-only
|
|
13
|
+
-- history table with app and script added to the existing entity-type set.
|
|
14
|
+
CREATE TABLE asset_key_history_new (
|
|
15
|
+
id TEXT PRIMARY KEY,
|
|
16
|
+
entity_type TEXT NOT NULL CHECK (entity_type IN ('task', 'workflow', 'schedule', 'page', 'app', 'script', 'file')),
|
|
17
|
+
entity_id TEXT NOT NULL,
|
|
18
|
+
previous_key TEXT,
|
|
19
|
+
new_key TEXT NOT NULL,
|
|
20
|
+
changed_by TEXT REFERENCES users(id),
|
|
21
|
+
changed_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
INSERT INTO asset_key_history_new (
|
|
25
|
+
id,
|
|
26
|
+
entity_type,
|
|
27
|
+
entity_id,
|
|
28
|
+
previous_key,
|
|
29
|
+
new_key,
|
|
30
|
+
changed_by,
|
|
31
|
+
changed_at
|
|
32
|
+
)
|
|
33
|
+
SELECT
|
|
34
|
+
id,
|
|
35
|
+
entity_type,
|
|
36
|
+
entity_id,
|
|
37
|
+
previous_key,
|
|
38
|
+
new_key,
|
|
39
|
+
changed_by,
|
|
40
|
+
changed_at
|
|
41
|
+
FROM asset_key_history;
|
|
42
|
+
|
|
43
|
+
DROP TABLE asset_key_history;
|
|
44
|
+
ALTER TABLE asset_key_history_new RENAME TO asset_key_history;
|
|
45
|
+
|
|
46
|
+
CREATE INDEX idx_asset_key_history_entity
|
|
47
|
+
ON asset_key_history(entity_type, entity_id, changed_at DESC);
|
|
48
|
+
|
|
49
|
+
-- Keep these bodies byte-for-byte aligned with migration 115's entity-table
|
|
50
|
+
-- triggers. SQLite cannot express the remaining NFKC rule.
|
|
51
|
+
CREATE TRIGGER validate_apps_asset_key_insert
|
|
52
|
+
BEFORE INSERT ON apps
|
|
53
|
+
WHEN NEW."key" IS NULL
|
|
54
|
+
OR length(NEW."key") = 0
|
|
55
|
+
OR NEW."key" != trim(NEW."key")
|
|
56
|
+
OR length(NEW."key") > 255
|
|
57
|
+
OR substr(NEW."key", -1, 1) != '/'
|
|
58
|
+
OR instr(NEW."key", char(0)) > 0
|
|
59
|
+
OR instr(NEW."key", char(92)) > 0
|
|
60
|
+
OR instr(NEW."key", '//') > 0
|
|
61
|
+
OR instr(NEW."key", '/../') > 0
|
|
62
|
+
OR instr(NEW."key", '/./') > 0
|
|
63
|
+
OR NEW."key" != lower(NEW."key")
|
|
64
|
+
OR NOT (
|
|
65
|
+
NEW."key" = 'shared/'
|
|
66
|
+
OR NEW."key" LIKE 'shared/%'
|
|
67
|
+
OR (
|
|
68
|
+
NEW."key" LIKE 'personal/%/%'
|
|
69
|
+
AND EXISTS (
|
|
70
|
+
SELECT 1 FROM users
|
|
71
|
+
WHERE id = substr(NEW."key", 10, instr(substr(NEW."key", 10), '/') - 1)
|
|
72
|
+
)
|
|
73
|
+
)
|
|
74
|
+
)
|
|
75
|
+
BEGIN
|
|
76
|
+
SELECT RAISE(ABORT, 'invalid asset namespace key');
|
|
77
|
+
END;
|
|
78
|
+
|
|
79
|
+
CREATE TRIGGER validate_apps_asset_key_update
|
|
80
|
+
BEFORE UPDATE OF "key" ON apps
|
|
81
|
+
WHEN NEW."key" IS NULL
|
|
82
|
+
OR length(NEW."key") = 0
|
|
83
|
+
OR NEW."key" != trim(NEW."key")
|
|
84
|
+
OR length(NEW."key") > 255
|
|
85
|
+
OR substr(NEW."key", -1, 1) != '/'
|
|
86
|
+
OR instr(NEW."key", char(0)) > 0
|
|
87
|
+
OR instr(NEW."key", char(92)) > 0
|
|
88
|
+
OR instr(NEW."key", '//') > 0
|
|
89
|
+
OR instr(NEW."key", '/../') > 0
|
|
90
|
+
OR instr(NEW."key", '/./') > 0
|
|
91
|
+
OR NEW."key" != lower(NEW."key")
|
|
92
|
+
OR NOT (
|
|
93
|
+
NEW."key" = 'shared/'
|
|
94
|
+
OR NEW."key" LIKE 'shared/%'
|
|
95
|
+
OR (
|
|
96
|
+
NEW."key" LIKE 'personal/%/%'
|
|
97
|
+
AND EXISTS (
|
|
98
|
+
SELECT 1 FROM users
|
|
99
|
+
WHERE id = substr(NEW."key", 10, instr(substr(NEW."key", 10), '/') - 1)
|
|
100
|
+
)
|
|
101
|
+
)
|
|
102
|
+
)
|
|
103
|
+
BEGIN
|
|
104
|
+
SELECT RAISE(ABORT, 'invalid asset namespace key');
|
|
105
|
+
END;
|
|
106
|
+
|
|
107
|
+
CREATE TRIGGER validate_scripts_asset_key_insert
|
|
108
|
+
BEFORE INSERT ON scripts
|
|
109
|
+
WHEN NEW."key" IS NULL
|
|
110
|
+
OR length(NEW."key") = 0
|
|
111
|
+
OR NEW."key" != trim(NEW."key")
|
|
112
|
+
OR length(NEW."key") > 255
|
|
113
|
+
OR substr(NEW."key", -1, 1) != '/'
|
|
114
|
+
OR instr(NEW."key", char(0)) > 0
|
|
115
|
+
OR instr(NEW."key", char(92)) > 0
|
|
116
|
+
OR instr(NEW."key", '//') > 0
|
|
117
|
+
OR instr(NEW."key", '/../') > 0
|
|
118
|
+
OR instr(NEW."key", '/./') > 0
|
|
119
|
+
OR NEW."key" != lower(NEW."key")
|
|
120
|
+
OR NOT (
|
|
121
|
+
NEW."key" = 'shared/'
|
|
122
|
+
OR NEW."key" LIKE 'shared/%'
|
|
123
|
+
OR (
|
|
124
|
+
NEW."key" LIKE 'personal/%/%'
|
|
125
|
+
AND EXISTS (
|
|
126
|
+
SELECT 1 FROM users
|
|
127
|
+
WHERE id = substr(NEW."key", 10, instr(substr(NEW."key", 10), '/') - 1)
|
|
128
|
+
)
|
|
129
|
+
)
|
|
130
|
+
)
|
|
131
|
+
BEGIN
|
|
132
|
+
SELECT RAISE(ABORT, 'invalid asset namespace key');
|
|
133
|
+
END;
|
|
134
|
+
|
|
135
|
+
CREATE TRIGGER validate_scripts_asset_key_update
|
|
136
|
+
BEFORE UPDATE OF "key" ON scripts
|
|
137
|
+
WHEN NEW."key" IS NULL
|
|
138
|
+
OR length(NEW."key") = 0
|
|
139
|
+
OR NEW."key" != trim(NEW."key")
|
|
140
|
+
OR length(NEW."key") > 255
|
|
141
|
+
OR substr(NEW."key", -1, 1) != '/'
|
|
142
|
+
OR instr(NEW."key", char(0)) > 0
|
|
143
|
+
OR instr(NEW."key", char(92)) > 0
|
|
144
|
+
OR instr(NEW."key", '//') > 0
|
|
145
|
+
OR instr(NEW."key", '/../') > 0
|
|
146
|
+
OR instr(NEW."key", '/./') > 0
|
|
147
|
+
OR NEW."key" != lower(NEW."key")
|
|
148
|
+
OR NOT (
|
|
149
|
+
NEW."key" = 'shared/'
|
|
150
|
+
OR NEW."key" LIKE 'shared/%'
|
|
151
|
+
OR (
|
|
152
|
+
NEW."key" LIKE 'personal/%/%'
|
|
153
|
+
AND EXISTS (
|
|
154
|
+
SELECT 1 FROM users
|
|
155
|
+
WHERE id = substr(NEW."key", 10, instr(substr(NEW."key", 10), '/') - 1)
|
|
156
|
+
)
|
|
157
|
+
)
|
|
158
|
+
)
|
|
159
|
+
BEGIN
|
|
160
|
+
SELECT RAISE(ABORT, 'invalid asset namespace key');
|
|
161
|
+
END;
|
package/src/be/scripts/db.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { defaultAssetKey } from "../../assets/key";
|
|
1
2
|
import type {
|
|
2
3
|
ScriptApiAuthMode,
|
|
3
4
|
ScriptApiRecord,
|
|
@@ -121,6 +122,7 @@ export function insertScript(args: ScriptWriteArgs): ScriptRecord {
|
|
|
121
122
|
.prepare<
|
|
122
123
|
ScriptRow,
|
|
123
124
|
[
|
|
125
|
+
string,
|
|
124
126
|
string,
|
|
125
127
|
string,
|
|
126
128
|
ScriptScope,
|
|
@@ -142,15 +144,16 @@ export function insertScript(args: ScriptWriteArgs): ScriptRecord {
|
|
|
142
144
|
]
|
|
143
145
|
>(
|
|
144
146
|
`INSERT INTO scripts (
|
|
145
|
-
id, name, scope, scopeId, source, description, intent, signatureJson,
|
|
147
|
+
id, "key", name, scope, scopeId, source, description, intent, signatureJson,
|
|
146
148
|
argsJsonSchema, contentHash, isScratch, typeChecked, fsMode, createdByAgentId, createdAt, updatedAt,
|
|
147
149
|
created_by, updated_by
|
|
148
150
|
)
|
|
149
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
151
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
150
152
|
RETURNING *`,
|
|
151
153
|
)
|
|
152
154
|
.get(
|
|
153
155
|
id,
|
|
156
|
+
defaultAssetKey("script", id),
|
|
154
157
|
args.name,
|
|
155
158
|
args.scope,
|
|
156
159
|
scopeId,
|
|
@@ -292,6 +292,7 @@ export interface SwarmSdk {
|
|
|
292
292
|
migration?: Record<string, unknown>;
|
|
293
293
|
forceElementBreak?: string[];
|
|
294
294
|
}): Promise<unknown>;
|
|
295
|
+
app_sync(args: { appId: string; model?: string; source?: string }): Promise<unknown>;
|
|
295
296
|
app_upsert(args: {
|
|
296
297
|
name: string;
|
|
297
298
|
description?: string;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
export const argsSchema = z.object({
|
|
4
|
+
appId: z.string().describe("App id to sync"),
|
|
5
|
+
model: z.string().optional().describe("Only sync this model (default: every model with sources)"),
|
|
6
|
+
source: z.string().optional().describe("Only sync this named source (default: every source)"),
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
/** Run an app's sync passes — the schedulable entry point for refreshing source-backed app rows. */
|
|
10
|
+
export default async function appSyncRun(args: any, ctx: any) {
|
|
11
|
+
const parsed = argsSchema.safeParse(args);
|
|
12
|
+
if (!parsed.success) throw new Error("invalid args: " + parsed.error.message);
|
|
13
|
+
const { appId, model, source } = parsed.data;
|
|
14
|
+
const result: any = await ctx.swarm.app_sync({
|
|
15
|
+
appId,
|
|
16
|
+
...(model ? { model } : {}),
|
|
17
|
+
...(source ? { source } : {}),
|
|
18
|
+
});
|
|
19
|
+
// The bridge serializes a FAILED sync as structured content in an HTTP 200:
|
|
20
|
+
// returning it would exit 0 and the scheduler would record a successful run.
|
|
21
|
+
// Throw so schedule error accounting sees the failure.
|
|
22
|
+
const payload = result && result.data !== undefined ? result.data : result;
|
|
23
|
+
const failed =
|
|
24
|
+
!result ||
|
|
25
|
+
result.success === false ||
|
|
26
|
+
(payload && (payload.success === false || payload.ok === false));
|
|
27
|
+
if (failed) {
|
|
28
|
+
const why = payload && typeof payload.error === "string" ? payload.error : "sync pass failed";
|
|
29
|
+
throw new Error("app sync failed: " + why);
|
|
30
|
+
}
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
export const argsSchema = z.object({
|
|
4
|
+
repo: z.string().describe("Repository in 'owner/name' form, e.g. 'owner/name'"),
|
|
5
|
+
state: z
|
|
6
|
+
.enum(["open", "closed", "all"])
|
|
7
|
+
.optional()
|
|
8
|
+
.describe("Which issues to pull (default 'open')"),
|
|
9
|
+
limit: z
|
|
10
|
+
.number()
|
|
11
|
+
.int()
|
|
12
|
+
.positive()
|
|
13
|
+
.max(100)
|
|
14
|
+
.optional()
|
|
15
|
+
.describe("Page size, GitHub's per_page cap is 100 (default 100)"),
|
|
16
|
+
connection: z
|
|
17
|
+
.string()
|
|
18
|
+
.optional()
|
|
19
|
+
.describe(
|
|
20
|
+
"Connection slug the app source names; echoed back for provenance — credentials are resolved at the egress layer, not here",
|
|
21
|
+
),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
/** Pull a repository's GitHub issues as sync records: {records, complete} with pull requests filtered out. */
|
|
25
|
+
export default async function githubIssuesPull(args: any, ctx: any) {
|
|
26
|
+
const parsed = argsSchema.safeParse(args);
|
|
27
|
+
// Failures THROW: a returned {error} object exits 0 and the sync engine
|
|
28
|
+
// reports it as a generic invalid-payload error, burying the real cause.
|
|
29
|
+
if (!parsed.success) throw new Error("invalid args: " + parsed.error.message);
|
|
30
|
+
const { repo, state = "open", limit = 100, connection } = parsed.data;
|
|
31
|
+
|
|
32
|
+
const segments = repo.split("/");
|
|
33
|
+
if (
|
|
34
|
+
segments.length !== 2 ||
|
|
35
|
+
segments.some((segment) => segment === "" || segment === "." || segment === "..")
|
|
36
|
+
) {
|
|
37
|
+
throw new Error("repo must be in 'owner/name' form");
|
|
38
|
+
}
|
|
39
|
+
const path = segments.map((segment) => encodeURIComponent(segment)).join("/");
|
|
40
|
+
|
|
41
|
+
const payload: any = await ctx.stdlib.fetchJson(
|
|
42
|
+
"https://api.github.com/repos/" + path + "/issues?state=" + state + "&per_page=" + limit,
|
|
43
|
+
{
|
|
44
|
+
headers: {
|
|
45
|
+
Accept: "application/vnd.github+json",
|
|
46
|
+
"User-Agent": "agent-swarm-apps-sync",
|
|
47
|
+
// The placeholder is the point: the sandbox's egress layer swaps it for
|
|
48
|
+
// the real token on the way out, and only toward hosts allowlisted for
|
|
49
|
+
// the run-as identity. Never resolve the secret yourself — no
|
|
50
|
+
// resolved-config read, no token arg.
|
|
51
|
+
Authorization: "Bearer [REDACTED:GITHUB_TOKEN]",
|
|
52
|
+
},
|
|
53
|
+
signal: AbortSignal.timeout(10_000),
|
|
54
|
+
},
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
// fetchJson hands back the parsed body, not the status — a GitHub error is an
|
|
58
|
+
// object carrying `message`, a successful issues page is an array.
|
|
59
|
+
if (!Array.isArray(payload)) {
|
|
60
|
+
const why =
|
|
61
|
+
payload && typeof payload.message === "string" ? payload.message : "unexpected response";
|
|
62
|
+
throw new Error("GitHub issues " + repo + ": " + why);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Completeness is computed on the RAW page, before the pull-request filter: a
|
|
66
|
+
// full raw page means "there may be more". Filtering first would let a page of
|
|
67
|
+
// mostly PRs look short and wrongly claim a complete window, and the engine
|
|
68
|
+
// would sweep still-live rows stale.
|
|
69
|
+
const complete = payload.length < limit;
|
|
70
|
+
|
|
71
|
+
// The issues endpoint interleaves pull requests; only PR entries carry
|
|
72
|
+
// `pull_request`.
|
|
73
|
+
const records = payload
|
|
74
|
+
.filter((issue: any) => issue && !issue.pull_request)
|
|
75
|
+
.map((issue: any) => ({
|
|
76
|
+
key: String(issue.number),
|
|
77
|
+
fields: {
|
|
78
|
+
number: issue.number,
|
|
79
|
+
id: issue.id,
|
|
80
|
+
title: issue.title,
|
|
81
|
+
state: issue.state,
|
|
82
|
+
// The FULL body goes back: the engine scrubs pulled values whole at
|
|
83
|
+
// its trusted boundary, and truncating here first could split a
|
|
84
|
+
// secret across the cut and defeat exact-value redaction. GitHub
|
|
85
|
+
// bounds issue bodies (~65k), so rows stay sane without a cap here.
|
|
86
|
+
body: typeof issue.body === "string" ? issue.body : "",
|
|
87
|
+
userLogin: issue.user ? issue.user.login : null,
|
|
88
|
+
labelsCsv: Array.isArray(issue.labels)
|
|
89
|
+
? issue.labels
|
|
90
|
+
.map((label: any) => (typeof label === "string" ? label : label && label.name))
|
|
91
|
+
.filter((name: any) => typeof name === "string" && name.length > 0)
|
|
92
|
+
.join(",")
|
|
93
|
+
: "",
|
|
94
|
+
comments: issue.comments,
|
|
95
|
+
htmlUrl: issue.html_url,
|
|
96
|
+
createdAt: issue.created_at,
|
|
97
|
+
updatedAt: issue.updated_at,
|
|
98
|
+
},
|
|
99
|
+
}));
|
|
100
|
+
|
|
101
|
+
return { records, complete, ...(connection ? { connection } : {}) };
|
|
102
|
+
}
|
|
@@ -22,6 +22,7 @@ import { getScript, upsertScriptByName } from "../scripts/db";
|
|
|
22
22
|
import { extractArgsJsonSchema } from "../scripts/extract-schema";
|
|
23
23
|
import { typecheckScript } from "../scripts/typecheck";
|
|
24
24
|
import type { Seeder, SeederRunOptions, SeedItem } from "../seed/types";
|
|
25
|
+
import appSyncRunSrc from "./catalog/app-sync-run.ts" with { type: "text" };
|
|
25
26
|
import bootTriageSrc from "./catalog/boot-triage.inline.ts" with { type: "text" };
|
|
26
27
|
// @ts-expect-error Bun text imports synthesize a default string for this helper.
|
|
27
28
|
import catalogReportSrc from "./catalog/catalog-report.inline.ts" with { type: "text" };
|
|
@@ -32,6 +33,7 @@ import delegateSrc from "./catalog/delegate.ts" with { type: "text" };
|
|
|
32
33
|
import fetchReadableSrc from "./catalog/fetch-readable.ts" with { type: "text" };
|
|
33
34
|
import getChildOutputsSrc from "./catalog/get-child-outputs.ts" with { type: "text" };
|
|
34
35
|
import ghPrSnapshotSrc from "./catalog/gh-pr-snapshot.ts" with { type: "text" };
|
|
36
|
+
import githubIssuesPullSrc from "./catalog/github-issues-pull.ts" with { type: "text" };
|
|
35
37
|
import groupCountSrc from "./catalog/group-count.ts" with { type: "text" };
|
|
36
38
|
import jsonQuerySrc from "./catalog/json-query.ts" with { type: "text" };
|
|
37
39
|
import linearIssueSrc from "./catalog/linear-issue.ts" with { type: "text" };
|
|
@@ -77,6 +79,22 @@ export const SEED_SCRIPTS: SeedScript[] = [
|
|
|
77
79
|
"Triage a GitHub PR's status in a single call instead of running several gh pr view / gh pr checks / gh api invocations.",
|
|
78
80
|
source: asText(ghPrSnapshotSrc),
|
|
79
81
|
},
|
|
82
|
+
{
|
|
83
|
+
name: "github-issues-pull",
|
|
84
|
+
description:
|
|
85
|
+
"Pull a GitHub repository's issues (pull requests filtered out) as app-sync records: number, title, state, body, author, labels, comments and timestamps, with a completeness flag for the pull window.",
|
|
86
|
+
intent:
|
|
87
|
+
"Back a Swarm App model with GitHub issues — the worked example of a sync source script returning {records, complete} with placeholder-based auth.",
|
|
88
|
+
source: asText(githubIssuesPullSrc),
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
name: "app-sync-run",
|
|
92
|
+
description:
|
|
93
|
+
"Run a Swarm App's sync passes — refreshes every source-backed model, or just one model/source, and returns the per-pass created/updated/refreshed tallies.",
|
|
94
|
+
intent:
|
|
95
|
+
"Keep an app's synced rows fresh on a schedule or on demand; the script a targetType:'script' schedule points at to sync an app.",
|
|
96
|
+
source: asText(appSyncRunSrc),
|
|
97
|
+
},
|
|
80
98
|
{
|
|
81
99
|
name: "fetch-readable",
|
|
82
100
|
description:
|
|
@@ -130,6 +130,12 @@ import researchingContent from "../../../templates/skills/researching/content.md
|
|
|
130
130
|
};
|
|
131
131
|
import reviewingConfig from "../../../templates/skills/reviewing/config.json" with { type: "text" };
|
|
132
132
|
import reviewingContent from "../../../templates/skills/reviewing/content.md" with { type: "text" };
|
|
133
|
+
import scheduledTaskResilienceConfig from "../../../templates/skills/scheduled-task-resilience/config.json" with {
|
|
134
|
+
type: "text",
|
|
135
|
+
};
|
|
136
|
+
import scheduledTaskResilienceContent from "../../../templates/skills/scheduled-task-resilience/content.md" with {
|
|
137
|
+
type: "text",
|
|
138
|
+
};
|
|
133
139
|
import scriptBuilderConfig from "../../../templates/skills/script-builder/config.json" with {
|
|
134
140
|
type: "text",
|
|
135
141
|
};
|
|
@@ -280,6 +286,7 @@ const BUILT_IN_SKILL_SOURCES = [
|
|
|
280
286
|
{ config: questioningConfig, body: questioningContent },
|
|
281
287
|
{ config: researchingConfig, body: researchingContent },
|
|
282
288
|
{ config: reviewingConfig, body: reviewingContent },
|
|
289
|
+
{ config: scheduledTaskResilienceConfig, body: scheduledTaskResilienceContent },
|
|
283
290
|
{ config: scriptBuilderConfig, body: scriptBuilderContent },
|
|
284
291
|
{ config: scriptWorkflowsConfig, body: scriptWorkflowsContent },
|
|
285
292
|
{ config: stepRunningConfig, body: stepRunningContent },
|
|
@@ -11,8 +11,9 @@ import {
|
|
|
11
11
|
resetOrphanedInProgressTasksForAgent,
|
|
12
12
|
updateActiveSessionProviderSessionId,
|
|
13
13
|
} from "../be/db";
|
|
14
|
+
import { ActiveSessionSchema, AgentTaskSchema } from "../types";
|
|
14
15
|
import { route } from "./route-def";
|
|
15
|
-
import {
|
|
16
|
+
import { jsonError } from "./utils";
|
|
16
17
|
|
|
17
18
|
// ─── Route Definitions ───────────────────────────────────────────────────────
|
|
18
19
|
|
|
@@ -26,7 +27,10 @@ const listActiveSessions = route({
|
|
|
26
27
|
agentId: z.string().optional(),
|
|
27
28
|
}),
|
|
28
29
|
responses: {
|
|
29
|
-
200: {
|
|
30
|
+
200: {
|
|
31
|
+
description: "Active session list",
|
|
32
|
+
schema: z.object({ sessions: z.array(ActiveSessionSchema) }),
|
|
33
|
+
},
|
|
30
34
|
},
|
|
31
35
|
});
|
|
32
36
|
|
|
@@ -45,7 +49,7 @@ const createActiveSession = route({
|
|
|
45
49
|
runnerSessionId: z.string().optional(),
|
|
46
50
|
}),
|
|
47
51
|
responses: {
|
|
48
|
-
201: { description: "Session created" },
|
|
52
|
+
201: { description: "Session created", schema: z.object({ session: ActiveSessionSchema }) },
|
|
49
53
|
400: { description: "Validation error" },
|
|
50
54
|
},
|
|
51
55
|
});
|
|
@@ -58,7 +62,7 @@ const deleteSessionByTask = route({
|
|
|
58
62
|
tags: ["Active Sessions"],
|
|
59
63
|
params: z.object({ taskId: z.string() }),
|
|
60
64
|
responses: {
|
|
61
|
-
200: { description: "Session deleted" },
|
|
65
|
+
200: { description: "Session deleted", schema: z.object({ deleted: z.boolean() }) },
|
|
62
66
|
},
|
|
63
67
|
});
|
|
64
68
|
|
|
@@ -70,7 +74,7 @@ const deleteSessionById = route({
|
|
|
70
74
|
tags: ["Active Sessions"],
|
|
71
75
|
params: z.object({ id: z.string() }),
|
|
72
76
|
responses: {
|
|
73
|
-
200: { description: "Session deleted" },
|
|
77
|
+
200: { description: "Session deleted", schema: z.object({ deleted: z.boolean() }) },
|
|
74
78
|
},
|
|
75
79
|
});
|
|
76
80
|
|
|
@@ -82,7 +86,7 @@ const heartbeatSession = route({
|
|
|
82
86
|
tags: ["Active Sessions"],
|
|
83
87
|
params: z.object({ taskId: z.string() }),
|
|
84
88
|
responses: {
|
|
85
|
-
200: { description: "Heartbeat updated" },
|
|
89
|
+
200: { description: "Heartbeat updated", schema: z.object({ updated: z.boolean() }) },
|
|
86
90
|
},
|
|
87
91
|
});
|
|
88
92
|
|
|
@@ -95,7 +99,7 @@ const updateProviderSession = route({
|
|
|
95
99
|
params: z.object({ taskId: z.string() }),
|
|
96
100
|
body: z.object({ providerSessionId: z.string().min(1) }),
|
|
97
101
|
responses: {
|
|
98
|
-
200: { description: "Provider session ID updated" },
|
|
102
|
+
200: { description: "Provider session ID updated", schema: z.object({ updated: z.boolean() }) },
|
|
99
103
|
},
|
|
100
104
|
});
|
|
101
105
|
|
|
@@ -112,7 +116,7 @@ const cleanupSessions = route({
|
|
|
112
116
|
})
|
|
113
117
|
.optional(),
|
|
114
118
|
responses: {
|
|
115
|
-
200: { description: "Cleanup result" },
|
|
119
|
+
200: { description: "Cleanup result", schema: z.object({ cleaned: z.number().int() }) },
|
|
116
120
|
},
|
|
117
121
|
});
|
|
118
122
|
|
|
@@ -127,7 +131,11 @@ const recoverOrphanedTasks = route({
|
|
|
127
131
|
minAgeSeconds: z.number().int().positive().optional(),
|
|
128
132
|
}),
|
|
129
133
|
responses: {
|
|
130
|
-
200: {
|
|
134
|
+
200: {
|
|
135
|
+
description: "Recovery result",
|
|
136
|
+
schema: z.object({ recovered: z.number().int(), tasks: z.array(AgentTaskSchema) }),
|
|
137
|
+
},
|
|
138
|
+
403: { description: "Can only recover orphaned tasks for the calling agent" },
|
|
131
139
|
},
|
|
132
140
|
});
|
|
133
141
|
|
|
@@ -144,7 +152,7 @@ export async function handleActiveSessions(
|
|
|
144
152
|
const parsed = await listActiveSessions.parse(req, res, pathSegments, queryParams);
|
|
145
153
|
if (!parsed) return true;
|
|
146
154
|
const sessions = getActiveSessions(parsed.query.agentId || undefined);
|
|
147
|
-
|
|
155
|
+
listActiveSessions.respond(res, 200, { sessions });
|
|
148
156
|
return true;
|
|
149
157
|
}
|
|
150
158
|
|
|
@@ -159,7 +167,7 @@ export async function handleActiveSessions(
|
|
|
159
167
|
taskDescription: parsed.body.taskDescription,
|
|
160
168
|
runnerSessionId: parsed.body.runnerSessionId,
|
|
161
169
|
});
|
|
162
|
-
|
|
170
|
+
createActiveSession.respond(res, 201, { session });
|
|
163
171
|
return true;
|
|
164
172
|
}
|
|
165
173
|
|
|
@@ -167,7 +175,7 @@ export async function handleActiveSessions(
|
|
|
167
175
|
const parsed = await deleteSessionByTask.parse(req, res, pathSegments, queryParams);
|
|
168
176
|
if (!parsed) return true;
|
|
169
177
|
const deleted = deleteActiveSession(parsed.params.taskId);
|
|
170
|
-
|
|
178
|
+
deleteSessionByTask.respond(res, 200, { deleted });
|
|
171
179
|
return true;
|
|
172
180
|
}
|
|
173
181
|
|
|
@@ -175,7 +183,7 @@ export async function handleActiveSessions(
|
|
|
175
183
|
const parsed = await deleteSessionById.parse(req, res, pathSegments, queryParams);
|
|
176
184
|
if (!parsed) return true;
|
|
177
185
|
const deleted = deleteActiveSessionById(parsed.params.id);
|
|
178
|
-
|
|
186
|
+
deleteSessionById.respond(res, 200, { deleted });
|
|
179
187
|
return true;
|
|
180
188
|
}
|
|
181
189
|
|
|
@@ -183,7 +191,7 @@ export async function handleActiveSessions(
|
|
|
183
191
|
const parsed = await heartbeatSession.parse(req, res, pathSegments, queryParams);
|
|
184
192
|
if (!parsed) return true;
|
|
185
193
|
const updated = heartbeatActiveSession(parsed.params.taskId);
|
|
186
|
-
|
|
194
|
+
heartbeatSession.respond(res, 200, { updated });
|
|
187
195
|
return true;
|
|
188
196
|
}
|
|
189
197
|
|
|
@@ -194,7 +202,7 @@ export async function handleActiveSessions(
|
|
|
194
202
|
parsed.params.taskId,
|
|
195
203
|
parsed.body.providerSessionId,
|
|
196
204
|
);
|
|
197
|
-
|
|
205
|
+
updateProviderSession.respond(res, 200, { updated });
|
|
198
206
|
return true;
|
|
199
207
|
}
|
|
200
208
|
|
|
@@ -207,7 +215,7 @@ export async function handleActiveSessions(
|
|
|
207
215
|
} else {
|
|
208
216
|
cleaned = cleanupStaleSessions(parsed.body?.maxAgeMinutes ?? 30);
|
|
209
217
|
}
|
|
210
|
-
|
|
218
|
+
cleanupSessions.respond(res, 200, { cleaned });
|
|
211
219
|
return true;
|
|
212
220
|
}
|
|
213
221
|
|
|
@@ -215,14 +223,14 @@ export async function handleActiveSessions(
|
|
|
215
223
|
const parsed = await recoverOrphanedTasks.parse(req, res, pathSegments, queryParams);
|
|
216
224
|
if (!parsed) return true;
|
|
217
225
|
if (!myAgentId || parsed.body.agentId !== myAgentId) {
|
|
218
|
-
|
|
226
|
+
jsonError(res, "Can only recover orphaned tasks for the calling agent", 403);
|
|
219
227
|
return true;
|
|
220
228
|
}
|
|
221
229
|
const tasks = resetOrphanedInProgressTasksForAgent(
|
|
222
230
|
parsed.body.agentId,
|
|
223
231
|
parsed.body.minAgeSeconds ?? 60,
|
|
224
232
|
);
|
|
225
|
-
|
|
233
|
+
recoverOrphanedTasks.respond(res, 200, { recovered: tasks.length, tasks });
|
|
226
234
|
return true;
|
|
227
235
|
}
|
|
228
236
|
|