@desplega.ai/agent-swarm 2.2.0 → 2.4.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 +1 -0
- package/dist/{acp-adapter-k0vwfyvn.js → acp-adapter-kedstxx6.js} +6 -6
- package/dist/{actions-w4pnb8vz.js → actions-vget6r1k.js} +18 -16
- package/dist/{amp-adapter-0cmxb2t5.js → amp-adapter-xeb5mktv.js} +2 -2
- package/dist/{app-zneem7g9.js → app-mqap8t8m.js} +3 -3
- package/dist/{assistant-wbzd2rnr.js → assistant-h0gv97mv.js} +20 -18
- package/dist/{boot-reembed-4kx69j3g.js → boot-reembed-qr8a894z.js} +1 -1
- package/dist/{boot-reembed-db0wwgy1.js → boot-reembed-rj85jjhw.js} +1 -1
- package/dist/{boot-scrub-sweep-tfv4d3mm.js → boot-scrub-sweep-eyfvn6v9.js} +3 -3
- package/dist/{boot-seeds-w2hmkxn5.js → boot-seeds-xs9a8dx1.js} +4 -4
- package/dist/{claude-adapter-5qrc2paq.js → claude-adapter-bm23cye7.js} +5 -5
- package/dist/{claude-managed-adapter-n7ahjyx6.js → claude-managed-adapter-1mwrsm9d.js} +2 -2
- package/dist/{claude-managed-setup-7ra32em4.js → claude-managed-setup-gkfgks4j.js} +1 -1
- package/dist/{claude-sdk-session-cd02gc76.js → claude-sdk-session-apb4ypz3.js} +5 -5
- package/dist/cli-02vhhssk.js +848 -0
- package/dist/{cli-4ync7m60.js → cli-0ebey9hg.js} +1 -1
- package/dist/{cli-665j3feb.js → cli-0xk505e8.js} +1 -1
- package/dist/{cli-8c1m9mhy.js → cli-1cevz46c.js} +5 -5
- package/dist/{cli-gzvb3d4a.js → cli-1jx0srjt.js} +3 -3
- package/dist/{cli-zj4d6afs.js → cli-1pa3rz69.js} +21 -6
- package/dist/{cli-vmbakbq0.js → cli-2rwet3r7.js} +1 -1
- package/dist/{cli-azs9bhc5.js → cli-3wjs22x3.js} +2 -2
- package/dist/{cli-tba8fxf9.js → cli-4fhyq711.js} +45 -36
- package/dist/{cli-ak4c12mh.js → cli-5ff2sjyt.js} +1 -1
- package/dist/{cli-6q7pyd3k.js → cli-5s5hx57y.js} +5 -3
- package/dist/{cli-h23mr2s6.js → cli-6ftrrwm1.js} +9 -371
- package/dist/{cli-9ac0mkg5.js → cli-8b9wvjps.js} +1 -1
- package/dist/cli-8cxkw1f5.js +382 -0
- package/dist/{cli-xhgtay7w.js → cli-8rwvksbc.js} +2 -2
- package/dist/{cli-e09hj50q.js → cli-8wkb09q5.js} +11 -11
- package/dist/{cli-ksrphveb.js → cli-a1esmmee.js} +14 -2
- package/dist/{cli-xnp2vkx7.js → cli-avmqhatn.js} +7 -7
- package/dist/{cli-xx13gqf3.js → cli-b80a1100.js} +1 -1
- package/dist/{cli-z1hh99j0.js → cli-bfc3k1x5.js} +3 -3
- package/dist/{cli-0k9rcwfy.js → cli-bhwa8rzc.js} +33 -15
- package/dist/{cli-8cvvytad.js → cli-bn26xbcs.js} +2 -2
- package/dist/{cli-k28p6xh7.js → cli-c8fwpgqb.js} +1 -1
- package/dist/{cli-rc3r0bnh.js → cli-ccn0eybf.js} +1 -1
- package/dist/{cli-9td5ss65.js → cli-dsgj3e7a.js} +2 -2
- package/dist/{cli-jrk8mj0t.js → cli-ecshf504.js} +2 -2
- package/dist/{cli-raejy0mp.js → cli-eqgfs202.js} +1 -1
- package/dist/{cli-ppapy34w.js → cli-f3nx1h8z.js} +2 -2
- package/dist/{cli-c9vrwcpb.js → cli-ffbrkh1r.js} +74 -35
- package/dist/{cli-mnry16kh.js → cli-kk2r80n5.js} +4 -4
- package/dist/{cli-c0dpzwnx.js → cli-kz116kvw.js} +3 -3
- package/dist/{cli-e5y285rg.js → cli-mvg2hf8p.js} +4 -4
- package/dist/{cli-2jm992rm.js → cli-mz1ezzzh.js} +2 -2
- package/dist/{cli-dw0njd9q.js → cli-nme7wery.js} +6 -6
- package/dist/{cli-mvsfbbgq.js → cli-nrrg0fvy.js} +18 -4
- package/dist/{cli-8jtrekpy.js → cli-p1m2b6gc.js} +7 -7
- package/dist/{cli-4c7k75rq.js → cli-rg7ntypm.js} +378 -84
- package/dist/{cli-z3dk189k.js → cli-rsd7ga7v.js} +10 -8
- package/dist/{cli-etvn5ej2.js → cli-sppc0cmw.js} +1 -1
- package/dist/{cli-8w65vwee.js → cli-tg9ksbn8.js} +1 -1
- package/dist/{cli-th8krqbf.js → cli-tgvpmnwz.js} +2 -2
- package/dist/{cli-hwc4a9xm.js → cli-tj4j2ts4.js} +6 -6
- package/dist/{cli-jhhb2v9n.js → cli-ttaz6c7m.js} +2 -2
- package/dist/{cli-w7z94e6c.js → cli-tx00c0h6.js} +4 -2
- package/dist/{cli-4g629sgr.js → cli-wrv09v7q.js} +20 -1
- package/dist/{cli-cby329ny.js → cli-wyrr38xm.js} +2 -2
- package/dist/{cli-5ks6x36q.js → cli-x1p5dhq2.js} +3 -3
- package/dist/{cli-bddxamg7.js → cli-x719j3vs.js} +220 -629
- package/dist/{cli-ztcxv5nv.js → cli-xfe63zx3.js} +1 -1
- package/dist/{cli-q6gw45wj.js → cli-ywyrffff.js} +1 -1
- package/dist/{cli-p46xdr1m.js → cli-zvze9rbm.js} +1 -1
- package/dist/cli.js +15 -15
- package/dist/{codex-adapter-m0fx3v3s.js → codex-adapter-pp2gq8gr.js} +3 -3
- package/dist/{codex-hook-x6tm6y5m.js → codex-hook-qwzccd64.js} +1 -1
- package/dist/{codex-session-runner-j9m2gfyy.js → codex-session-runner-0vzn09t8.js} +3 -3
- package/dist/{commands-ktnqwrv4.js → commands-psqhkttb.js} +2 -2
- package/dist/{controller-nex2he8v.js → controller-t20vz2k8.js} +2 -2
- package/dist/{controllers-connect-6yhwqv9v.js → controllers-connect-gkxmqw9d.js} +24 -20
- package/dist/{cursor-adapter-02s5ekva.js → cursor-adapter-3v542qe5.js} +1 -1
- package/dist/{db-jvr86v7f.js → db-2xbyenjr.js} +2 -2
- package/dist/{db-19tw9r5s.js → db-nc1rd869.js} +3 -2
- package/dist/{dispatcher-d3mnsg2y.js → dispatcher-ewceb8ts.js} +17 -15
- package/dist/{dsh-adapter-63rvej88.js → dsh-adapter-75xwh78t.js} +1 -1
- package/dist/{easter-egg-3d-6hg2r942.js → easter-egg-3d-xsc47jwf.js} +1 -1
- package/dist/{execute-bcqc69a5.js → execute-gzmw1mnw.js} +1 -1
- package/dist/{external-channel-44cwer32.js → external-channel-a5dsmxn4.js} +2 -2
- package/dist/{google-generative-ai-phpmwxvg.js → google-generative-ai-bmq8gkf3.js} +1 -1
- package/dist/{google-vertex-es9ybgdy.js → google-vertex-b1rvp52s.js} +1 -1
- package/dist/{grok-adapter-ysp907f6.js → grok-adapter-vj88xr23.js} +7 -7
- package/dist/handlers-zbs90bny.js +39 -0
- package/dist/{heartbeat-2etacsqq.js → heartbeat-t2m9r3wa.js} +20 -18
- package/dist/{hook-mzxxs5n2.js → hook-8femx1bg.js} +4 -4
- package/dist/{hook-3kvmax6d.js → hook-xa677f8r.js} +5 -5
- package/dist/{http-fn978m0g.js → http-xpmmq028.js} +940 -269
- package/dist/{index-e4tsa1h7.js → index-8pwc1jc8.js} +17 -15
- package/dist/{index-w2wr89xd.js → index-cdzyfrsp.js} +1 -1
- package/dist/{index-1r8hw4vd.js → index-dg2tmj99.js} +17 -15
- package/dist/index-nxa6me0q.js +41 -0
- package/dist/{index-039x2sag.js → index-s7ckdajq.js} +42 -14
- package/dist/{index-wgsw9xcz.js → index-syztqdt6.js} +1 -1
- package/dist/{index-x0q2a4f9.js → index-ysd45kgc.js} +1 -1
- package/dist/{index-g909y089.js → index-zgvqzjsw.js} +8 -8
- package/dist/{keepalive-d4nzs76x.js → keepalive-3nr4st3r.js} +3 -3
- package/dist/{lead-8h5n9k5f.js → lead-td8h2sfn.js} +33 -31
- package/dist/{maintenance-ea6jfw42.js → maintenance-verwpe2c.js} +1 -1
- package/dist/{oauth-refresh-sweep-3f25djq3.js → oauth-refresh-sweep-9q2t0y3q.js} +2 -2
- package/dist/{onboard-pvk7hdb2.js → onboard-aytqedmq.js} +2 -2
- package/dist/{opencode-adapter-eeesgfqk.js → opencode-adapter-f4hryjny.js} +4 -4
- package/dist/{otel-impl-qb7nhgdp.js → otel-impl-hckw4wt1.js} +1 -1
- package/dist/{pi-mono-adapter-zasp3ze3.js → pi-mono-adapter-hxby0ce1.js} +6 -6
- package/dist/{pricing-refresh-6ehwkcmp.js → pricing-refresh-5nvme3sm.js} +3 -3
- package/dist/{rbac-roles-km0f9cmm.js → rbac-roles-pd8c26m1.js} +5 -3
- package/dist/{rbac-roles-9648nh00.js → rbac-roles-vbpy21mg.js} +2 -2
- package/dist/{render-v2-xkze1wef.js → render-v2-7nqgvk2q.js} +3 -3
- package/dist/{resume-tq0bbvt5.js → resume-b6djcj3c.js} +6 -6
- package/dist/{runtime-ar8310ba.js → runtime-txrxc9za.js} +1 -1
- package/dist/session-access-fc3f9y12.js +25 -0
- package/dist/{setup-rnrazxvd.js → setup-n39szm6b.js} +1 -1
- package/dist/{virtual-modules-wzk6jf3a.js → virtual-modules-wyp2k6z3.js} +2 -2
- package/dist/{work-objects-e75q2rqs.js → work-objects-jem2fre0.js} +20 -18
- package/dist/{worker-4ww4hjef.js → worker-wr6xgfr2.js} +33 -31
- package/openapi.json +1011 -115
- package/package.json +2 -2
- package/src/apps/action-caller.ts +6 -0
- package/src/apps/script-types.ts +16 -3
- package/src/be/asset-key-audit.ts +4 -13
- package/src/be/db/active-sessions.ts +5 -0
- package/src/be/db/session-visibility.ts +29 -0
- package/src/be/db/tasks/read.ts +61 -3
- package/src/be/db/tasks/write.ts +15 -6
- package/src/be/db.ts +149 -66
- package/src/be/events.ts +11 -0
- package/src/be/migrations/227_inbox_item_state_last_read_at.sql +1 -0
- package/src/be/migrations/230_session_shares.sql +23 -0
- package/src/be/rbac-roles.ts +2 -0
- package/src/be/seed-scripts/catalog/heartbeat-jev-gate.ts +22 -4
- package/src/be/session-shares.ts +244 -0
- package/src/be/swarm-config-guard.ts +23 -2
- package/src/commands/runner.ts +11 -0
- package/src/heartbeat/heartbeat.ts +25 -7
- package/src/http/active-sessions.ts +13 -2
- package/src/http/agents.ts +10 -3
- package/src/http/all-routes.ts +1 -0
- package/src/http/api-keys.ts +3 -1
- package/src/http/approval-requests.ts +2 -1
- package/src/http/apps.ts +36 -11
- package/src/http/assets.ts +26 -4
- package/src/http/config.ts +3 -0
- package/src/http/context.ts +2 -0
- package/src/http/core.ts +39 -2
- package/src/http/events.ts +6 -2
- package/src/http/favorite-owner.ts +1 -1
- package/src/http/fs.ts +38 -3
- package/src/http/inbox-state.ts +58 -8
- package/src/http/index.ts +3 -1
- package/src/http/memory.ts +3 -1
- package/src/http/ranking.ts +367 -0
- package/src/http/realtime.ts +26 -21
- package/src/http/route-def.ts +77 -11
- package/src/http/script-runs.ts +2 -1
- package/src/http/scripts.ts +8 -0
- package/src/http/session-access.ts +179 -0
- package/src/http/session-data.ts +10 -2
- package/src/http/sessions.ts +212 -12
- package/src/http/stats.ts +7 -3
- package/src/http/tasks.ts +83 -7
- package/src/http/used-by.ts +11 -3
- package/src/kv-reserved-namespaces.ts +1 -0
- package/src/prompts/session-templates.ts +3 -1
- package/src/providers/amp-adapter.ts +1 -1
- package/src/ranking/telemetry-ranking.ts +147 -0
- package/src/ranking/usage.ts +57 -0
- package/src/rbac/admission.ts +22 -7
- package/src/rbac/legacy-policy.ts +2 -0
- package/src/rbac/permissions.ts +9 -0
- package/src/realtime/auth.ts +69 -0
- package/src/realtime/tickets.ts +4 -1
- package/src/realtime/transport.ts +77 -9
- package/src/slack/blocks.ts +18 -0
- package/src/slack/dm.ts +29 -0
- package/src/telemetry-contract/EVENTS.md +3 -0
- package/src/telemetry-contract/SOURCE.json +37 -4
- package/src/telemetry-contract/catalog.json +69 -0
- package/src/telemetry-contract/ranking/fixtures/expected.json +30 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/bad-category.json +11 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/bad-claim.json +11 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/blank-description.json +11 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/blank-name.json +11 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/description-too-long.json +11 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/domain-uppercase.json +11 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/domain-with-scheme.json +11 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/domain-with-www.json +11 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/http-source-url.json +11 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/missing-description.json +10 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/missing-version.json +10 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/name-not-string.json +11 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/name-too-long.json +11 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/not-object.json +1 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/null.json +1 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/other-with-subcategory.json +11 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/oversize.json +1 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/short-claim.json +11 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/subcategory-other-category.json +11 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/trailing-data.json +1 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/unknown-field.json +12 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/unsupported-version.json +11 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/updated-at-not-date.json +11 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/updated-at-offset.json +11 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/wrong-case-field.json +11 -0
- package/src/telemetry-contract/ranking/fixtures/valid/category-without-subcategory.json +11 -0
- package/src/telemetry-contract/ranking/fixtures/valid/full.json +11 -0
- package/src/telemetry-contract/ranking/fixtures/valid/minimal.json +11 -0
- package/src/telemetry-contract/ranking/fixtures/valid/padded-text.json +11 -0
- package/src/telemetry-contract/ranking/fixtures/valid/unicode-name.json +11 -0
- package/src/telemetry-contract/ranking/listing.v1.schema.json +61 -0
- package/src/telemetry-contract/ranking/taxonomy.json +8 -0
- package/src/telemetry-contract/ranking-taxonomy.gen.ts +11 -0
- package/src/telemetry-contract/types.gen.ts +708 -1
- package/src/telemetry.ts +26 -2
- package/src/tests/apps-rbac.test.ts +189 -2
- package/src/tests/apps-script-types.test.ts +16 -3
- package/src/tests/asset-key-api.test.ts +128 -0
- package/src/tests/asset-key-invariants.test.ts +179 -3
- package/src/tests/asset-key-migration.test.ts +35 -0
- package/src/tests/check-pr-body.test.ts +23 -0
- package/src/tests/check-ranking-entry.test.ts +146 -0
- package/src/tests/db-extraction-contract.test.ts +1 -0
- package/src/tests/db-task-read-contract.test.ts +1 -0
- package/src/tests/docs-index-footprint.test.js +2 -2
- package/src/tests/heartbeat-checklist.test.ts +88 -0
- package/src/tests/heartbeat-jev-gate.test.ts +394 -10
- package/src/tests/inbox-state.test.ts +167 -0
- package/src/tests/kv-tool.test.ts +15 -0
- package/src/tests/org-identity.test.ts +44 -0
- package/src/tests/ranking-listing.test.ts +112 -0
- package/src/tests/ranking-route.test.ts +319 -0
- package/src/tests/ranking-usage.test.ts +50 -0
- package/src/tests/rbac-admission-e2e.test.ts +8 -0
- package/src/tests/rbac-admission.test.ts +38 -0
- package/src/tests/rbac-charact-http.test.ts +4 -3
- package/src/tests/rbac-engine.test.ts +2 -0
- package/src/tests/rbac-roles.test.ts +2 -0
- package/src/tests/realtime-room-surfaces.test.ts +3 -1
- package/src/tests/realtime-session-join-race.test.ts +142 -0
- package/src/tests/realtime-tickets.test.ts +24 -17
- package/src/tests/realtime-transport.test.ts +91 -0
- package/src/tests/script-connections.test.ts +2 -0
- package/src/tests/session-route-posture.test.ts +639 -0
- package/src/tests/session-sharing-routes.test.ts +282 -0
- package/src/tests/session-sharing.test.ts +526 -0
- package/src/tests/sessions-detail-batch.test.ts +7 -1
- package/src/tests/slack-tool-blocks.test.ts +101 -0
- package/src/tests/slack-update.test.ts +55 -0
- package/src/tests/slack-user-dm.test.ts +177 -0
- package/src/tests/used-by-route.test.ts +10 -0
- package/src/tools/slack-post.ts +18 -12
- package/src/tools/slack-read.ts +13 -1
- package/src/tools/slack-reply.ts +7 -10
- package/src/tools/slack-start-thread.ts +18 -11
- package/src/tools/slack-update.ts +2 -9
- package/src/types.ts +37 -0
- package/src/utils/org-identity.ts +33 -0
- package/templates/skills/apps/SKILL.md +17 -0
- package/templates/skills/apps/content.md +17 -0
- package/templates/skills/code-quality/content.md +2 -2
- package/templates/skills/pages/content.md +4 -0
- package/templates/skills/swarm-scripts/SKILL.md +6 -0
- package/templates/skills/swarm-scripts/content.md +6 -0
- package/dist/handlers-t5pbabwf.js +0 -37
- package/dist/index-qyjbb7br.js +0 -39
- package/dist/{cli-n5mrsg0f.js → cli-60cgh1rv.js} +3 -3
- /package/dist/{cli-j0v75y13.js → cli-y83zk5sb.js} +0 -0
|
@@ -69,28 +69,6 @@ import {
|
|
|
69
69
|
resolveKvView,
|
|
70
70
|
toolErr,
|
|
71
71
|
createToolRegistrar,
|
|
72
|
-
duplicateGuardedQueryKey,
|
|
73
|
-
warnIfCorsAllowsAnyOrigin,
|
|
74
|
-
isOriginAllowedForCredentials,
|
|
75
|
-
allowedCredentialRedirect,
|
|
76
|
-
setCorsHeaders,
|
|
77
|
-
parseQueryParams,
|
|
78
|
-
getPathSegments,
|
|
79
|
-
wireHttpSpanLifecycle,
|
|
80
|
-
safeRequestUrlForLog,
|
|
81
|
-
agentWithCapacity,
|
|
82
|
-
parseBody,
|
|
83
|
-
BODY_TOO_LARGE,
|
|
84
|
-
enforceContentLengthCap,
|
|
85
|
-
json,
|
|
86
|
-
jsonError,
|
|
87
|
-
writeUnhandledError,
|
|
88
|
-
triggerSchemaErrorResponse,
|
|
89
|
-
deriveApiBaseUrl,
|
|
90
|
-
httpServerSemconvAttributes,
|
|
91
|
-
runtimeInstanceHeader,
|
|
92
|
-
describeRequestRoute,
|
|
93
|
-
route,
|
|
94
72
|
handleMcpBridge,
|
|
95
73
|
Redacted,
|
|
96
74
|
SCRIPT_SDK_RESPONSE_LIMIT_BYTES,
|
|
@@ -199,9 +177,6 @@ import {
|
|
|
199
177
|
scheduleIntegrationsReload,
|
|
200
178
|
handleCore,
|
|
201
179
|
userCtx,
|
|
202
|
-
resolveTaskAuditUserId,
|
|
203
|
-
resolveHttpAuditUserId,
|
|
204
|
-
resolveHttpFavoriteOwner,
|
|
205
180
|
handleApps,
|
|
206
181
|
cancelApprovalRequest,
|
|
207
182
|
getUserCommsPrefs,
|
|
@@ -262,6 +237,8 @@ import {
|
|
|
262
237
|
resolveRoomNamespace,
|
|
263
238
|
roomRequestInfo,
|
|
264
239
|
authorizeRoomNamespace,
|
|
240
|
+
sessionAccessTopic,
|
|
241
|
+
sessionPresenceRoot,
|
|
265
242
|
RoomOperationSchema,
|
|
266
243
|
removeAwarenessStates,
|
|
267
244
|
roomTopic,
|
|
@@ -303,7 +280,7 @@ import {
|
|
|
303
280
|
hasCapability,
|
|
304
281
|
getEnabledCapabilities,
|
|
305
282
|
createServer
|
|
306
|
-
} from "./cli-
|
|
283
|
+
} from "./cli-x719j3vs.js";
|
|
307
284
|
import {
|
|
308
285
|
chatThreadOfContextKey,
|
|
309
286
|
agentContextKey,
|
|
@@ -440,6 +417,17 @@ import {
|
|
|
440
417
|
CHAT_SDK_ADAPTER_NAMES,
|
|
441
418
|
isAdapterEntryEnabled,
|
|
442
419
|
init_schema,
|
|
420
|
+
TELEMETRY_RANKING_KEY,
|
|
421
|
+
PUBLIC_RANKING_ENABLED_KEY,
|
|
422
|
+
RANKING_CATEGORIES,
|
|
423
|
+
readTelemetryRanking,
|
|
424
|
+
rankingFormSchema,
|
|
425
|
+
mintRankingClaim,
|
|
426
|
+
buildListing,
|
|
427
|
+
rankingEntryFile,
|
|
428
|
+
rankingPrUrl,
|
|
429
|
+
rankingLeaveUrl,
|
|
430
|
+
init_telemetry_ranking,
|
|
443
431
|
API_DRAINING_HEADER,
|
|
444
432
|
init_api_drain,
|
|
445
433
|
parseTaskToolManifest,
|
|
@@ -724,26 +712,26 @@ import {
|
|
|
724
712
|
countScriptRunJournalSteps2,
|
|
725
713
|
countScriptRunJournalAgentTaskSteps2,
|
|
726
714
|
init_db3
|
|
727
|
-
} from "./cli-
|
|
715
|
+
} from "./cli-rg7ntypm.js";
|
|
728
716
|
import {
|
|
729
717
|
getUserGrant2,
|
|
730
718
|
init_rbac_roles
|
|
731
|
-
} from "./cli-
|
|
732
|
-
import"./cli-
|
|
719
|
+
} from "./cli-5s5hx57y.js";
|
|
720
|
+
import"./cli-kk2r80n5.js";
|
|
733
721
|
import {
|
|
734
722
|
normalizeModelKey
|
|
735
|
-
} from "./cli-
|
|
723
|
+
} from "./cli-tg9ksbn8.js";
|
|
736
724
|
import {
|
|
737
725
|
upsertModelCatalogOverlay,
|
|
738
726
|
deleteModelCatalogOverlay,
|
|
739
727
|
loadModelsCatalog,
|
|
740
728
|
requestModelCatalogRefresh2
|
|
741
|
-
} from "./cli-
|
|
729
|
+
} from "./cli-ttaz6c7m.js";
|
|
742
730
|
import {
|
|
743
731
|
AMP_ESTIMATE_MODELS,
|
|
744
732
|
ampBillsCacheWrites,
|
|
745
733
|
resolveAmpModel
|
|
746
|
-
} from "./cli-
|
|
734
|
+
} from "./cli-2rwet3r7.js";
|
|
747
735
|
import {
|
|
748
736
|
automationIntegrationFixUrl,
|
|
749
737
|
preflightAutomation,
|
|
@@ -755,7 +743,7 @@ import {
|
|
|
755
743
|
cancelWorkflowRun2,
|
|
756
744
|
TriggerSchemaError,
|
|
757
745
|
startWorkflowExecution2
|
|
758
|
-
} from "./cli-
|
|
746
|
+
} from "./cli-nme7wery.js";
|
|
759
747
|
import {
|
|
760
748
|
require_dist2,
|
|
761
749
|
isInvalidClientError,
|
|
@@ -835,14 +823,14 @@ import {
|
|
|
835
823
|
typecheckWithAmbient,
|
|
836
824
|
typecheckScript,
|
|
837
825
|
parseSkillContent
|
|
838
|
-
} from "./cli-
|
|
826
|
+
} from "./cli-nrrg0fvy.js";
|
|
839
827
|
import {
|
|
840
828
|
buildAuthorizationUrl,
|
|
841
829
|
exchangeAuthorizationCode,
|
|
842
830
|
oauthAppRowToProviderConfig,
|
|
843
831
|
forceRefreshAuthorizationOrThrow,
|
|
844
832
|
forceRefreshTokenOrThrow
|
|
845
|
-
} from "./cli-
|
|
833
|
+
} from "./cli-b80a1100.js";
|
|
846
834
|
import {
|
|
847
835
|
signalProcessGroup,
|
|
848
836
|
detachedProcessGroup,
|
|
@@ -870,15 +858,15 @@ import {
|
|
|
870
858
|
getSlackApp2,
|
|
871
859
|
startSlackApp2,
|
|
872
860
|
stopSlackApp2
|
|
873
|
-
} from "./cli-
|
|
874
|
-
import"./cli-
|
|
861
|
+
} from "./cli-4fhyq711.js";
|
|
862
|
+
import"./cli-xfe63zx3.js";
|
|
875
863
|
import {
|
|
876
864
|
createApprovalFollowUpTask,
|
|
877
865
|
generateEdges,
|
|
878
866
|
definitionNodeIds,
|
|
879
867
|
validateDefinition
|
|
880
|
-
} from "./cli-
|
|
881
|
-
import"./cli-
|
|
868
|
+
} from "./cli-tj4j2ts4.js";
|
|
869
|
+
import"./cli-bn26xbcs.js";
|
|
882
870
|
import {
|
|
883
871
|
_fetchHolder,
|
|
884
872
|
CLIENT_ID,
|
|
@@ -894,6 +882,48 @@ import {
|
|
|
894
882
|
workflowEventBus2,
|
|
895
883
|
init_event_bus
|
|
896
884
|
} from "./cli-ptkpaqfy.js";
|
|
885
|
+
import {
|
|
886
|
+
duplicateGuardedQueryKey,
|
|
887
|
+
warnIfCorsAllowsAnyOrigin,
|
|
888
|
+
isOriginAllowedForCredentials,
|
|
889
|
+
allowedCredentialRedirect,
|
|
890
|
+
setCorsHeaders,
|
|
891
|
+
parseQueryParams,
|
|
892
|
+
getPathSegments,
|
|
893
|
+
wireHttpSpanLifecycle,
|
|
894
|
+
safeRequestUrlForLog,
|
|
895
|
+
agentWithCapacity,
|
|
896
|
+
parseBody,
|
|
897
|
+
BODY_TOO_LARGE,
|
|
898
|
+
enforceContentLengthCap,
|
|
899
|
+
json,
|
|
900
|
+
jsonError,
|
|
901
|
+
writeUnhandledError,
|
|
902
|
+
triggerSchemaErrorResponse,
|
|
903
|
+
deriveApiBaseUrl,
|
|
904
|
+
httpServerSemconvAttributes,
|
|
905
|
+
hasSessionAccess,
|
|
906
|
+
getSessionRoot,
|
|
907
|
+
sessionAccessForRoot,
|
|
908
|
+
listActiveSessionShares,
|
|
909
|
+
toSessionSharesState,
|
|
910
|
+
setSessionShares,
|
|
911
|
+
resolveTaskAuditUserId,
|
|
912
|
+
resolveHttpAuditUserId,
|
|
913
|
+
dashboardUserIdHeader,
|
|
914
|
+
resolveHttpFavoriteOwner,
|
|
915
|
+
resolveSessionActor2,
|
|
916
|
+
sessionAccessFor2,
|
|
917
|
+
SESSION_READ_DENIED2,
|
|
918
|
+
canReadSession2,
|
|
919
|
+
readableByFor2,
|
|
920
|
+
SESSION_WRITE_DENIED2,
|
|
921
|
+
runtimeInstanceHeader,
|
|
922
|
+
bodyTask,
|
|
923
|
+
bodyListTask,
|
|
924
|
+
describeRequestRoute,
|
|
925
|
+
route
|
|
926
|
+
} from "./cli-02vhhssk.js";
|
|
897
927
|
import {
|
|
898
928
|
isMultiRuntimeEnabled
|
|
899
929
|
} from "./cli-7a91w22y.js";
|
|
@@ -906,24 +936,6 @@ import {
|
|
|
906
936
|
} from "./cli-qje9k2nh.js";
|
|
907
937
|
import {
|
|
908
938
|
RuntimePoolError2,
|
|
909
|
-
registerSessionTokenConnection,
|
|
910
|
-
init_session_token_connections,
|
|
911
|
-
findUserById,
|
|
912
|
-
getUserIdentities,
|
|
913
|
-
listUserEvents,
|
|
914
|
-
listUserTokens,
|
|
915
|
-
recordIdentityEvent,
|
|
916
|
-
linkIdentity,
|
|
917
|
-
unlinkIdentity,
|
|
918
|
-
mintToken,
|
|
919
|
-
revokeToken,
|
|
920
|
-
resolveUserByToken,
|
|
921
|
-
InvalidRuntimeTokenError,
|
|
922
|
-
mintSessionToken,
|
|
923
|
-
mintRuntimeToken,
|
|
924
|
-
revokeSessionToken,
|
|
925
|
-
fingerprintApiKey,
|
|
926
|
-
init_users,
|
|
927
939
|
mintControllerToken,
|
|
928
940
|
init_tokens,
|
|
929
941
|
listRuntimeControllers2,
|
|
@@ -942,7 +954,7 @@ import {
|
|
|
942
954
|
resolvePoolDelivery2,
|
|
943
955
|
createPoolAgent2,
|
|
944
956
|
init_db
|
|
945
|
-
} from "./cli-
|
|
957
|
+
} from "./cli-6ftrrwm1.js";
|
|
946
958
|
import {
|
|
947
959
|
RATING_MODEL_MAX_LENGTH,
|
|
948
960
|
REFERENCES_SOURCE_MAX_LENGTH,
|
|
@@ -958,8 +970,8 @@ import {
|
|
|
958
970
|
checkHeartbeatChecklist2,
|
|
959
971
|
runHeartbeatSweep2,
|
|
960
972
|
stopHeartbeat2
|
|
961
|
-
} from "./cli-
|
|
962
|
-
import"./cli-
|
|
973
|
+
} from "./cli-bhwa8rzc.js";
|
|
974
|
+
import"./cli-eqgfs202.js";
|
|
963
975
|
import {
|
|
964
976
|
import_websocket,
|
|
965
977
|
import_websocket_server
|
|
@@ -968,7 +980,7 @@ import {
|
|
|
968
980
|
runtimeSupervisorStatus,
|
|
969
981
|
startRuntimeSupervisor,
|
|
970
982
|
stopRuntimeSupervisor
|
|
971
|
-
} from "./cli-
|
|
983
|
+
} from "./cli-rsd7ga7v.js";
|
|
972
984
|
import"./cli-ns59dc6r.js";
|
|
973
985
|
import {
|
|
974
986
|
ACP_TARGET_IDS
|
|
@@ -980,20 +992,22 @@ import {
|
|
|
980
992
|
import {
|
|
981
993
|
MAX_RATE_LIMIT_RESET_MS
|
|
982
994
|
} from "./cli-gx4rm0qx.js";
|
|
983
|
-
import {
|
|
984
|
-
inviteEmailToSharedOrg,
|
|
985
|
-
ensureAgentFsCredentialsForAgent
|
|
986
|
-
} from "./cli-ak4c12mh.js";
|
|
987
|
-
import {
|
|
988
|
-
Anthropic
|
|
989
|
-
} from "./cli-er83cjbn.js";
|
|
990
995
|
import {
|
|
991
996
|
isLeadChannelMonitoringEnabled,
|
|
997
|
+
ORG_IDENTITY_KEYS,
|
|
998
|
+
orgIdentityStatus,
|
|
992
999
|
SIMILARITY_THRESHOLD,
|
|
993
1000
|
memoryRelevance,
|
|
994
1001
|
isUnknownModelError,
|
|
995
1002
|
writeSkillsToFilesystem
|
|
996
|
-
} from "./cli-
|
|
1003
|
+
} from "./cli-wrv09v7q.js";
|
|
1004
|
+
import {
|
|
1005
|
+
inviteEmailToSharedOrg,
|
|
1006
|
+
ensureAgentFsCredentialsForAgent
|
|
1007
|
+
} from "./cli-5ff2sjyt.js";
|
|
1008
|
+
import {
|
|
1009
|
+
Anthropic
|
|
1010
|
+
} from "./cli-er83cjbn.js";
|
|
997
1011
|
import {
|
|
998
1012
|
isCodexOAuthConfigKey
|
|
999
1013
|
} from "./cli-j4bwnpkm.js";
|
|
@@ -1005,7 +1019,27 @@ import"./cli-13p25grh.js";
|
|
|
1005
1019
|
import"./cli-0adyhvq2.js";
|
|
1006
1020
|
import {
|
|
1007
1021
|
require_content_type
|
|
1008
|
-
} from "./cli-
|
|
1022
|
+
} from "./cli-1jx0srjt.js";
|
|
1023
|
+
import {
|
|
1024
|
+
registerSessionTokenConnection,
|
|
1025
|
+
init_session_token_connections,
|
|
1026
|
+
findUserById,
|
|
1027
|
+
getUserIdentities,
|
|
1028
|
+
listUserEvents,
|
|
1029
|
+
listUserTokens,
|
|
1030
|
+
recordIdentityEvent,
|
|
1031
|
+
linkIdentity,
|
|
1032
|
+
unlinkIdentity,
|
|
1033
|
+
mintToken,
|
|
1034
|
+
revokeToken,
|
|
1035
|
+
resolveUserByToken,
|
|
1036
|
+
InvalidRuntimeTokenError,
|
|
1037
|
+
mintSessionToken,
|
|
1038
|
+
mintRuntimeToken,
|
|
1039
|
+
revokeSessionToken,
|
|
1040
|
+
fingerprintApiKey,
|
|
1041
|
+
init_users
|
|
1042
|
+
} from "./cli-8cxkw1f5.js";
|
|
1009
1043
|
import"./cli-v41kn3cd.js";
|
|
1010
1044
|
import"./cli-wgtk7xxr.js";
|
|
1011
1045
|
import {
|
|
@@ -1019,7 +1053,7 @@ import {
|
|
|
1019
1053
|
clearAuditSink,
|
|
1020
1054
|
can,
|
|
1021
1055
|
init_rbac
|
|
1022
|
-
} from "./cli-
|
|
1056
|
+
} from "./cli-1pa3rz69.js";
|
|
1023
1057
|
import {
|
|
1024
1058
|
isSafeBundlePath,
|
|
1025
1059
|
init_bundle_path,
|
|
@@ -1039,6 +1073,10 @@ import {
|
|
|
1039
1073
|
ProviderNameSchema,
|
|
1040
1074
|
ModelProfileNameSchema,
|
|
1041
1075
|
ModelProfileSchema,
|
|
1076
|
+
SessionAccessSchema,
|
|
1077
|
+
SessionOwnerSchema,
|
|
1078
|
+
SessionSharesSchema,
|
|
1079
|
+
SessionSharesUpdateSchema,
|
|
1042
1080
|
SteerModeSchema,
|
|
1043
1081
|
SteeringSourceSchema,
|
|
1044
1082
|
SteeringMessageSchema,
|
|
@@ -1156,12 +1194,12 @@ import {
|
|
|
1156
1194
|
KvKeySchema,
|
|
1157
1195
|
KvEntrySchema,
|
|
1158
1196
|
init_types
|
|
1159
|
-
} from "./cli-
|
|
1197
|
+
} from "./cli-a1esmmee.js";
|
|
1160
1198
|
import {
|
|
1161
1199
|
IDENTITY_FIELD_BUDGETS,
|
|
1162
1200
|
IdentityFieldBudgetError,
|
|
1163
1201
|
init_identity_field_budget
|
|
1164
|
-
} from "./cli-
|
|
1202
|
+
} from "./cli-wyrr38xm.js";
|
|
1165
1203
|
import {
|
|
1166
1204
|
getSlackConfiguration,
|
|
1167
1205
|
init_config
|
|
@@ -1179,7 +1217,7 @@ import {
|
|
|
1179
1217
|
resolveTemplate,
|
|
1180
1218
|
init_resolver,
|
|
1181
1219
|
init_session_templates
|
|
1182
|
-
} from "./cli-
|
|
1220
|
+
} from "./cli-tx00c0h6.js";
|
|
1183
1221
|
import {
|
|
1184
1222
|
MODEL_SCOPED_WINDOWS,
|
|
1185
1223
|
activeModelBlocks,
|
|
@@ -1201,6 +1239,7 @@ import {
|
|
|
1201
1239
|
import {
|
|
1202
1240
|
NEVER,
|
|
1203
1241
|
toJSONSchema,
|
|
1242
|
+
ZodError,
|
|
1204
1243
|
string2,
|
|
1205
1244
|
email,
|
|
1206
1245
|
uuid,
|
|
@@ -1228,7 +1267,7 @@ import {
|
|
|
1228
1267
|
import {
|
|
1229
1268
|
init_package,
|
|
1230
1269
|
package_default
|
|
1231
|
-
} from "./cli-
|
|
1270
|
+
} from "./cli-mz1ezzzh.js";
|
|
1232
1271
|
import {
|
|
1233
1272
|
realtimeBus,
|
|
1234
1273
|
init_bus
|
|
@@ -2367,7 +2406,8 @@ function identityFromHttpAuth(req, auth) {
|
|
|
2367
2406
|
agentId,
|
|
2368
2407
|
sourceTaskId: auth.kind === "agent" ? auth.taskId ?? undefined : header(req, "x-source-task-id"),
|
|
2369
2408
|
userId: auth.kind === "user" ? auth.userId : undefined,
|
|
2370
|
-
isOperator: auth.kind === "operator"
|
|
2409
|
+
isOperator: auth.kind === "operator",
|
|
2410
|
+
dashboardUserId: auth.kind === "operator" ? auth.dashboardUserId : undefined
|
|
2371
2411
|
},
|
|
2372
2412
|
me: {
|
|
2373
2413
|
userId: auth.kind === "user" ? auth.userId : agentId ?? guestId,
|
|
@@ -2560,6 +2600,9 @@ function connect(ws, auth, runtimeId) {
|
|
|
2560
2600
|
if (namespace.startsWith("presence:comb:") && msg.name !== "default") {
|
|
2561
2601
|
throw new Error("Comb presence uses the default room");
|
|
2562
2602
|
}
|
|
2603
|
+
if (namespace.startsWith("presence:session:") && msg.name !== "default") {
|
|
2604
|
+
throw new Error("Session presence uses the default room");
|
|
2605
|
+
}
|
|
2563
2606
|
const denied = await authorizeRoomNamespace(namespace, auth.identity, msg.op, msg.name);
|
|
2564
2607
|
if (denied)
|
|
2565
2608
|
throw new Error(typeof denied === "string" ? denied : "Forbidden");
|
|
@@ -2600,7 +2643,7 @@ function connect(ws, auth, runtimeId) {
|
|
|
2600
2643
|
} catch (error) {
|
|
2601
2644
|
if (!(error instanceof Error) || error.message !== "room does not exist")
|
|
2602
2645
|
throw error;
|
|
2603
|
-
const creationDenied = await authorizeRoomNamespace(namespace, auth.identity, namespace.startsWith("presence:comb:") ? "join" : true);
|
|
2646
|
+
const creationDenied = await authorizeRoomNamespace(namespace, auth.identity, namespace.startsWith("presence:comb:") || namespace.startsWith("presence:session:") ? "join" : true);
|
|
2604
2647
|
if (creationDenied)
|
|
2605
2648
|
throw new Error(creationDenied);
|
|
2606
2649
|
if (closed)
|
|
@@ -2610,6 +2653,8 @@ function connect(ws, auth, runtimeId) {
|
|
|
2610
2653
|
if (closed)
|
|
2611
2654
|
return;
|
|
2612
2655
|
const stopRoom = realtimeBus.subscribe(key, (event) => {
|
|
2656
|
+
if (rooms.get(key)?.suspended)
|
|
2657
|
+
return;
|
|
2613
2658
|
const view = event;
|
|
2614
2659
|
const type = view.type;
|
|
2615
2660
|
if (type === "deleted") {
|
|
@@ -2631,15 +2676,62 @@ function connect(ws, auth, runtimeId) {
|
|
|
2631
2676
|
}).catch(() => ws.close(1011, "Room reset failed"));
|
|
2632
2677
|
}
|
|
2633
2678
|
});
|
|
2634
|
-
const stopPresence = realtimeBus.subscribe(`presence:${key}`, (peers) =>
|
|
2679
|
+
const stopPresence = realtimeBus.subscribe(`presence:${key}`, (peers) => {
|
|
2680
|
+
if (rooms.get(key)?.suspended)
|
|
2681
|
+
return;
|
|
2682
|
+
send({ type: "presence", name: msg.name, namespace, peers });
|
|
2683
|
+
});
|
|
2684
|
+
const sessionRoot = sessionPresenceRoot(namespace);
|
|
2685
|
+
let accessCheck = 0;
|
|
2686
|
+
const stopAccess = sessionRoot ? realtimeBus.subscribe(sessionAccessTopic(sessionRoot), () => {
|
|
2687
|
+
const joined = rooms.get(key);
|
|
2688
|
+
if (!joined)
|
|
2689
|
+
return;
|
|
2690
|
+
joined.suspended = true;
|
|
2691
|
+
const check = ++accessCheck;
|
|
2692
|
+
authorizeRoomNamespace(namespace, auth.identity, "join").catch(() => "Session access check failed").then((denied) => {
|
|
2693
|
+
const current = rooms.get(key);
|
|
2694
|
+
if (closed || !current || check !== accessCheck)
|
|
2695
|
+
return;
|
|
2696
|
+
if (denied) {
|
|
2697
|
+
leave(key);
|
|
2698
|
+
send({ type: "error", name: msg.name, namespace, error: denied });
|
|
2699
|
+
return;
|
|
2700
|
+
}
|
|
2701
|
+
current.suspended = false;
|
|
2702
|
+
send({
|
|
2703
|
+
type: "presence",
|
|
2704
|
+
name: msg.name,
|
|
2705
|
+
namespace,
|
|
2706
|
+
peers: [...current.room.awareness.getStates().values()]
|
|
2707
|
+
});
|
|
2708
|
+
});
|
|
2709
|
+
}) : undefined;
|
|
2635
2710
|
rooms.set(key, {
|
|
2636
2711
|
room,
|
|
2637
2712
|
version: msg.schemaVersion,
|
|
2638
2713
|
stop: () => {
|
|
2639
2714
|
stopRoom();
|
|
2640
2715
|
stopPresence();
|
|
2641
|
-
|
|
2716
|
+
stopAccess?.();
|
|
2717
|
+
},
|
|
2718
|
+
suspended: sessionRoot !== undefined
|
|
2642
2719
|
});
|
|
2720
|
+
if (sessionRoot) {
|
|
2721
|
+
const check = ++accessCheck;
|
|
2722
|
+
const denied = await authorizeRoomNamespace(namespace, auth.identity, "join").catch(() => "Session access check failed");
|
|
2723
|
+
if (closed)
|
|
2724
|
+
return;
|
|
2725
|
+
if (denied) {
|
|
2726
|
+
leave(key);
|
|
2727
|
+
throw new Error(denied);
|
|
2728
|
+
}
|
|
2729
|
+
const current = rooms.get(key);
|
|
2730
|
+
if (!current)
|
|
2731
|
+
throw new Error("You don't have access to this session");
|
|
2732
|
+
if (check === accessCheck)
|
|
2733
|
+
current.suspended = false;
|
|
2734
|
+
}
|
|
2643
2735
|
room.awareness.states.set(clientId, { ...auth.me, data: {} });
|
|
2644
2736
|
room.awareness.meta.set(clientId, { clock: 1, lastUpdated: Date.now() });
|
|
2645
2737
|
publishPresence(room);
|
|
@@ -3863,6 +3955,7 @@ var createActiveSession = route({
|
|
|
3863
3955
|
method: "post",
|
|
3864
3956
|
path: "/api/active-sessions",
|
|
3865
3957
|
pattern: ["api", "active-sessions"],
|
|
3958
|
+
session: bodyTask("taskId"),
|
|
3866
3959
|
summary: "Create a new active session",
|
|
3867
3960
|
tags: ["Active Sessions"],
|
|
3868
3961
|
body: object2({
|
|
@@ -3883,6 +3976,7 @@ var deleteSessionByTask = route({
|
|
|
3883
3976
|
method: "delete",
|
|
3884
3977
|
path: "/api/active-sessions/by-task/{taskId}",
|
|
3885
3978
|
pattern: ["api", "active-sessions", "by-task", null],
|
|
3979
|
+
session: { taskParam: "taskId" },
|
|
3886
3980
|
summary: "Delete active session by task ID",
|
|
3887
3981
|
tags: ["Active Sessions"],
|
|
3888
3982
|
params: object2({ taskId: string2() }),
|
|
@@ -3894,6 +3988,7 @@ var deleteSessionById = route({
|
|
|
3894
3988
|
method: "delete",
|
|
3895
3989
|
path: "/api/active-sessions/{id}",
|
|
3896
3990
|
pattern: ["api", "active-sessions", null],
|
|
3991
|
+
session: { activeSessionParam: "id" },
|
|
3897
3992
|
summary: "Delete active session by ID",
|
|
3898
3993
|
tags: ["Active Sessions"],
|
|
3899
3994
|
params: object2({ id: string2() }),
|
|
@@ -3905,6 +4000,7 @@ var heartbeatSession = route({
|
|
|
3905
4000
|
method: "put",
|
|
3906
4001
|
path: "/api/active-sessions/heartbeat/{taskId}",
|
|
3907
4002
|
pattern: ["api", "active-sessions", "heartbeat", null],
|
|
4003
|
+
session: { taskParam: "taskId" },
|
|
3908
4004
|
summary: "Update heartbeat for an active session",
|
|
3909
4005
|
tags: ["Active Sessions"],
|
|
3910
4006
|
params: object2({ taskId: string2() }),
|
|
@@ -3916,6 +4012,7 @@ var updateProviderSession = route({
|
|
|
3916
4012
|
method: "put",
|
|
3917
4013
|
path: "/api/active-sessions/provider-session/{taskId}",
|
|
3918
4014
|
pattern: ["api", "active-sessions", "provider-session", null],
|
|
4015
|
+
session: { taskParam: "taskId" },
|
|
3919
4016
|
summary: "Update provider session ID on an active session",
|
|
3920
4017
|
tags: ["Active Sessions"],
|
|
3921
4018
|
params: object2({ taskId: string2() }),
|
|
@@ -3928,6 +4025,7 @@ var cleanupSessions = route({
|
|
|
3928
4025
|
method: "post",
|
|
3929
4026
|
path: "/api/active-sessions/cleanup",
|
|
3930
4027
|
pattern: ["api", "active-sessions", "cleanup"],
|
|
4028
|
+
session: { agentWide: "Deletes every session of an agent, or every stale session." },
|
|
3931
4029
|
summary: "Clean up stale sessions",
|
|
3932
4030
|
tags: ["Active Sessions"],
|
|
3933
4031
|
body: object2({
|
|
@@ -3942,6 +4040,7 @@ var recoverOrphanedTasks = route({
|
|
|
3942
4040
|
method: "post",
|
|
3943
4041
|
path: "/api/active-sessions/recover-orphaned-tasks",
|
|
3944
4042
|
pattern: ["api", "active-sessions", "recover-orphaned-tasks"],
|
|
4043
|
+
session: { none: "Its handler admits only the calling agent, never a user." },
|
|
3945
4044
|
summary: "Recover orphaned in-progress tasks for an agent",
|
|
3946
4045
|
tags: ["Active Sessions"],
|
|
3947
4046
|
body: object2({
|
|
@@ -3961,7 +4060,7 @@ async function handleActiveSessions(req, res, pathSegments, queryParams, myAgent
|
|
|
3961
4060
|
const parsed = await listActiveSessions.parse(req, res, pathSegments, queryParams);
|
|
3962
4061
|
if (!parsed)
|
|
3963
4062
|
return true;
|
|
3964
|
-
const sessions = await getActiveSessions2(parsed.query.agentId || undefined);
|
|
4063
|
+
const sessions = await getActiveSessions2(parsed.query.agentId || undefined, readableByFor2(await resolveSessionActor2(req, myAgentId)));
|
|
3965
4064
|
listActiveSessions.respond(res, 200, { sessions });
|
|
3966
4065
|
return true;
|
|
3967
4066
|
}
|
|
@@ -4610,14 +4709,17 @@ async function handleAgentRegister(req, res, pathSegments, myAgentId) {
|
|
|
4610
4709
|
}
|
|
4611
4710
|
return false;
|
|
4612
4711
|
}
|
|
4613
|
-
async function handleAgentsRest(req, res, pathSegments, queryParams,
|
|
4712
|
+
async function handleAgentsRest(req, res, pathSegments, queryParams, myAgentId) {
|
|
4614
4713
|
if (listAgents.match(req.method, pathSegments)) {
|
|
4615
4714
|
const parsed = await listAgents.parse(req, res, pathSegments, queryParams);
|
|
4616
4715
|
if (!parsed)
|
|
4617
4716
|
return true;
|
|
4618
4717
|
const includeTasks = parsed.query.include === "tasks";
|
|
4619
4718
|
const slim = parsed.query.fields !== "full";
|
|
4620
|
-
const agents = includeTasks ? await getAllAgentsWithTasks2({
|
|
4719
|
+
const agents = includeTasks ? await getAllAgentsWithTasks2({
|
|
4720
|
+
slim,
|
|
4721
|
+
readableBy: readableByFor2(await resolveSessionActor2(req, myAgentId))
|
|
4722
|
+
}) : await getAllAgents2({ slim });
|
|
4621
4723
|
const [agentsWithCapacity, transports] = await Promise.all([
|
|
4622
4724
|
Promise.all(agents.map(agentWithCapacity)),
|
|
4623
4725
|
resolveListClaudeTransports(agents)
|
|
@@ -5155,7 +5257,7 @@ async function handleAgentsRest(req, res, pathSegments, queryParams, _myAgentId)
|
|
|
5155
5257
|
if (!parsed)
|
|
5156
5258
|
return true;
|
|
5157
5259
|
const includeTasks = parsed.query.include === "tasks";
|
|
5158
|
-
const agent = includeTasks ? await getAgentWithTasks2(parsed.params.id) : await getAgentById2(parsed.params.id);
|
|
5260
|
+
const agent = includeTasks ? await getAgentWithTasks2(parsed.params.id, readableByFor2(await resolveSessionActor2(req, myAgentId))) : await getAgentById2(parsed.params.id);
|
|
5159
5261
|
if (!agent) {
|
|
5160
5262
|
jsonError(res, "Agent not found", 404);
|
|
5161
5263
|
return true;
|
|
@@ -5234,6 +5336,7 @@ var reportUsage = route({
|
|
|
5234
5336
|
method: "post",
|
|
5235
5337
|
path: "/api/keys/report-usage",
|
|
5236
5338
|
pattern: ["api", "keys", "report-usage"],
|
|
5339
|
+
session: bodyTask("taskId"),
|
|
5237
5340
|
summary: "Record which API key was used for a task",
|
|
5238
5341
|
tags: ["API Keys"],
|
|
5239
5342
|
body: object2({
|
|
@@ -5277,6 +5380,7 @@ var reportAuthFailure = route({
|
|
|
5277
5380
|
method: "post",
|
|
5278
5381
|
path: "/api/keys/report-auth-failure",
|
|
5279
5382
|
pattern: ["api", "keys", "report-auth-failure"],
|
|
5383
|
+
session: bodyTask("taskId"),
|
|
5280
5384
|
summary: "Record an auth failure for a pooled key; bench it after 2 in a row",
|
|
5281
5385
|
tags: ["API Keys"],
|
|
5282
5386
|
body: object2({
|
|
@@ -5974,6 +6078,7 @@ var createRoute = route({
|
|
|
5974
6078
|
method: "post",
|
|
5975
6079
|
path: "/api/approval-requests",
|
|
5976
6080
|
pattern: ["api", "approval-requests"],
|
|
6081
|
+
session: bodyTask("sourceTaskId"),
|
|
5977
6082
|
summary: "Create a new approval request",
|
|
5978
6083
|
tags: ["ApprovalRequests"],
|
|
5979
6084
|
body: object2({
|
|
@@ -6522,10 +6627,14 @@ function ensureOperator(req, res) {
|
|
|
6522
6627
|
return false;
|
|
6523
6628
|
}
|
|
6524
6629
|
var moveAssetBodySchema = object2({ key: AssetKeySchema });
|
|
6630
|
+
var moveGenericAssetBodySchema = moveAssetBodySchema.extend({
|
|
6631
|
+
cascade: boolean().optional().describe("For tasks only, also move every descendant task recursively through parentTaskId.")
|
|
6632
|
+
});
|
|
6525
6633
|
var moveAssetSuccessSchema = object2({
|
|
6526
6634
|
entityType: AssetEntityTypeSchema,
|
|
6527
6635
|
id: string2(),
|
|
6528
|
-
key: AssetKeySchema
|
|
6636
|
+
key: AssetKeySchema,
|
|
6637
|
+
movedTaskIds: array2(string2()).optional()
|
|
6529
6638
|
});
|
|
6530
6639
|
var moveAssetResponses = {
|
|
6531
6640
|
200: { description: "Asset namespace updated", schema: moveAssetSuccessSchema },
|
|
@@ -6564,7 +6673,7 @@ var moveAssetRoute = route({
|
|
|
6564
6673
|
description: "Updates namespace metadata only. Provider-backed files keep the same provider key, org, and drive; no remote move occurs. Personal keys are labels, not a privacy guarantee.",
|
|
6565
6674
|
tags: ["Assets"],
|
|
6566
6675
|
params: object2({ entityType: AssetEntityTypeSchema, id: string2().min(1) }),
|
|
6567
|
-
body:
|
|
6676
|
+
body: moveGenericAssetBodySchema,
|
|
6568
6677
|
responses: moveAssetResponses,
|
|
6569
6678
|
rbac: {
|
|
6570
6679
|
ungated: "preserves each entity's current mutation posture; task moves use task.fs.mutate, app moves use app.manage, scripts preserve agent ownership/global-lead rules, file moves require operator authentication, and personal destinations require a matching trusted user"
|
|
@@ -6671,18 +6780,27 @@ async function handleAssets(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
6671
6780
|
const parsed = await moveAssetRoute.parse(req, res, pathSegments, queryParams);
|
|
6672
6781
|
if (!parsed)
|
|
6673
6782
|
return true;
|
|
6783
|
+
if (parsed.body.cascade && parsed.params.entityType !== "task") {
|
|
6784
|
+
jsonError(res, "Cascade is only supported for task namespace moves", 400);
|
|
6785
|
+
return true;
|
|
6786
|
+
}
|
|
6674
6787
|
if (parsed.params.entityType === "file" && !ensureOperator(req, res))
|
|
6675
6788
|
return true;
|
|
6789
|
+
let descendantTaskIds;
|
|
6676
6790
|
if (parsed.params.entityType === "task") {
|
|
6677
6791
|
const task = await getTaskById2(parsed.params.id);
|
|
6678
6792
|
if (!task) {
|
|
6679
6793
|
jsonError(res, "Asset not found", 404);
|
|
6680
6794
|
return true;
|
|
6681
6795
|
}
|
|
6682
|
-
|
|
6683
|
-
|
|
6684
|
-
|
|
6796
|
+
const tasks = parsed.body.cascade ? await getRootTaskChain2(task.id) : [task];
|
|
6797
|
+
for (const target of tasks) {
|
|
6798
|
+
if (!await canMutateTaskNamespace(target, myAgentId, req)) {
|
|
6799
|
+
jsonError(res, "Not authorized to move this task namespace", 403);
|
|
6800
|
+
return true;
|
|
6801
|
+
}
|
|
6685
6802
|
}
|
|
6803
|
+
descendantTaskIds = tasks.filter((target) => target.id !== task.id).map((target) => target.id);
|
|
6686
6804
|
}
|
|
6687
6805
|
if (parsed.params.entityType === "app") {
|
|
6688
6806
|
if (!await getApp(parsed.params.id)) {
|
|
@@ -6712,7 +6830,8 @@ async function handleAssets(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
6712
6830
|
entityType: parsed.params.entityType,
|
|
6713
6831
|
id: parsed.params.id,
|
|
6714
6832
|
key,
|
|
6715
|
-
changedBy: actor ?? undefined
|
|
6833
|
+
changedBy: actor ?? undefined,
|
|
6834
|
+
descendantTaskIds
|
|
6716
6835
|
});
|
|
6717
6836
|
if (!moved) {
|
|
6718
6837
|
jsonError(res, "Asset not found", 404);
|
|
@@ -6721,7 +6840,8 @@ async function handleAssets(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
6721
6840
|
moveAssetRoute.respond(res, 200, {
|
|
6722
6841
|
entityType: parsed.params.entityType,
|
|
6723
6842
|
id: parsed.params.id,
|
|
6724
|
-
key
|
|
6843
|
+
key,
|
|
6844
|
+
...moved
|
|
6725
6845
|
});
|
|
6726
6846
|
} catch (error) {
|
|
6727
6847
|
if (error instanceof AssetKeyAuthorizationError) {
|
|
@@ -7575,6 +7695,396 @@ init_rbac();
|
|
|
7575
7695
|
init_types();
|
|
7576
7696
|
init_request_auth_context();
|
|
7577
7697
|
init_secret_scrubber();
|
|
7698
|
+
|
|
7699
|
+
// src/http/ranking.ts
|
|
7700
|
+
init_zod();
|
|
7701
|
+
init_db3();
|
|
7702
|
+
init_telemetry_ranking();
|
|
7703
|
+
init_telemetry();
|
|
7704
|
+
init_env_flag();
|
|
7705
|
+
|
|
7706
|
+
// src/http/used-by.ts
|
|
7707
|
+
init_zod();
|
|
7708
|
+
init_db3();
|
|
7709
|
+
init_telemetry();
|
|
7710
|
+
init_org_website();
|
|
7711
|
+
var CONSENT_KEY = "SWARM_USED_BY_CONSENT";
|
|
7712
|
+
var CONSENT_AT_KEY = "SWARM_USED_BY_CONSENT_AT";
|
|
7713
|
+
var GRANTED_NAME_KEY = "SWARM_USED_BY_ORG_NAME";
|
|
7714
|
+
var GRANTED_WEBSITE_KEY = "SWARM_USED_BY_ORG_WEBSITE";
|
|
7715
|
+
var TIMEOUT_MS = 1e4;
|
|
7716
|
+
var usedBy = route({
|
|
7717
|
+
method: "post",
|
|
7718
|
+
path: "/api/used-by",
|
|
7719
|
+
pattern: ["api", "used-by"],
|
|
7720
|
+
summary: "Grant or revoke the 'used by' logo consent",
|
|
7721
|
+
description: "Records whether agent-swarm.dev may show this organization's logo as a company that uses Agent Swarm. Sends the organization name and website to the telemetry proxy, where a person approves each logo by hand. An explicit admin action, so it is not gated by the telemetry opt-out.",
|
|
7722
|
+
tags: ["Telemetry"],
|
|
7723
|
+
body: object2({ granted: boolean() }),
|
|
7724
|
+
responses: {
|
|
7725
|
+
200: {
|
|
7726
|
+
description: "Consent recorded",
|
|
7727
|
+
schema: object2({ granted: boolean(), changedAt: string2() })
|
|
7728
|
+
},
|
|
7729
|
+
400: {
|
|
7730
|
+
description: "Organization name or website is not set (a revoke falls back to the identity of the last grant)"
|
|
7731
|
+
},
|
|
7732
|
+
401: { description: "Unauthorized" },
|
|
7733
|
+
403: { description: "Only a config admin can change the consent" },
|
|
7734
|
+
502: { description: "The telemetry proxy did not accept the consent" }
|
|
7735
|
+
},
|
|
7736
|
+
rbac: { permission: "config.write.any" }
|
|
7737
|
+
});
|
|
7738
|
+
async function globalConfig(key) {
|
|
7739
|
+
const stored = (await getSwarmConfigs2({ scope: "global", key }))?.[0]?.value?.trim();
|
|
7740
|
+
return stored || process.env[key]?.trim() || undefined;
|
|
7741
|
+
}
|
|
7742
|
+
async function telemetryProxyEndpoint(path) {
|
|
7743
|
+
const feedback = await globalConfig("feedback_endpoint");
|
|
7744
|
+
if (feedback && /^https?:\/\/[^/]+\/v1\/feedback\/?$/.test(feedback)) {
|
|
7745
|
+
return feedback.replace(/\/v1\/feedback\/?$/, path);
|
|
7746
|
+
}
|
|
7747
|
+
return `${TELEMETRY_PROXY_ORIGIN}${path}`;
|
|
7748
|
+
}
|
|
7749
|
+
function usedByEndpoint() {
|
|
7750
|
+
return telemetryProxyEndpoint("/v1/used-by");
|
|
7751
|
+
}
|
|
7752
|
+
async function handleUsedBy(req, res, pathSegments, queryParams, myAgentId) {
|
|
7753
|
+
if (!usedBy.match(req.method, pathSegments))
|
|
7754
|
+
return false;
|
|
7755
|
+
const parsed = await usedBy.parse(req, res, pathSegments, queryParams);
|
|
7756
|
+
if (!parsed)
|
|
7757
|
+
return true;
|
|
7758
|
+
if (!await ensureConfigAdmin(req, res, "config.write.any"))
|
|
7759
|
+
return true;
|
|
7760
|
+
const { granted } = parsed.body;
|
|
7761
|
+
const current = orgIdentityStatus({
|
|
7762
|
+
SWARM_ORG_NAME: await globalConfig("SWARM_ORG_NAME"),
|
|
7763
|
+
SWARM_ORG_WEBSITE: await globalConfig("SWARM_ORG_WEBSITE")
|
|
7764
|
+
});
|
|
7765
|
+
const currentName = current.name ?? undefined;
|
|
7766
|
+
const currentWebsite = current.website;
|
|
7767
|
+
const grantedName = granted ? undefined : await globalConfig(GRANTED_NAME_KEY);
|
|
7768
|
+
const grantedWebsite = granted ? null : normalizeOrgWebsite(await globalConfig(GRANTED_WEBSITE_KEY));
|
|
7769
|
+
const [orgName, orgWebsite] = grantedName && grantedWebsite ? [grantedName, grantedWebsite] : [currentName, currentWebsite];
|
|
7770
|
+
if (!orgName || !orgWebsite) {
|
|
7771
|
+
jsonError(res, "Set the organization name and website first", 400);
|
|
7772
|
+
return true;
|
|
7773
|
+
}
|
|
7774
|
+
const installId = isTelemetryEnabled() ? await globalConfig("telemetry_installation_id") : undefined;
|
|
7775
|
+
const orgDomain = await globalConfig("telemetry_org_domain");
|
|
7776
|
+
const changedAt = new Date().toISOString();
|
|
7777
|
+
try {
|
|
7778
|
+
const response = await fetch(await usedByEndpoint(), {
|
|
7779
|
+
method: "POST",
|
|
7780
|
+
headers: { "Content-Type": "application/json" },
|
|
7781
|
+
body: JSON.stringify({
|
|
7782
|
+
...installId ? { install_id: installId } : {},
|
|
7783
|
+
org_name: orgName,
|
|
7784
|
+
org_website: orgWebsite,
|
|
7785
|
+
...orgDomain ? { org_domain: orgDomain } : {},
|
|
7786
|
+
granted,
|
|
7787
|
+
changed_at: changedAt
|
|
7788
|
+
}),
|
|
7789
|
+
signal: AbortSignal.timeout(TIMEOUT_MS)
|
|
7790
|
+
});
|
|
7791
|
+
if (!response.ok) {
|
|
7792
|
+
jsonError(res, `The telemetry proxy answered ${response.status}`, 502);
|
|
7793
|
+
return true;
|
|
7794
|
+
}
|
|
7795
|
+
} catch {
|
|
7796
|
+
jsonError(res, "Could not reach the telemetry proxy", 502);
|
|
7797
|
+
return true;
|
|
7798
|
+
}
|
|
7799
|
+
await upsertSwarmConfig2({ scope: "global", key: CONSENT_KEY, value: String(granted) });
|
|
7800
|
+
await upsertSwarmConfig2({ scope: "global", key: CONSENT_AT_KEY, value: changedAt });
|
|
7801
|
+
if (granted) {
|
|
7802
|
+
await upsertSwarmConfig2({ scope: "global", key: GRANTED_NAME_KEY, value: orgName });
|
|
7803
|
+
await upsertSwarmConfig2({ scope: "global", key: GRANTED_WEBSITE_KEY, value: orgWebsite });
|
|
7804
|
+
}
|
|
7805
|
+
scheduleIntegrationsReload();
|
|
7806
|
+
const owner = await resolveHttpFavoriteOwner(req, myAgentId);
|
|
7807
|
+
trackRelayed("used_by.consent_changed", { granted }, { actor: owner?.userId ? { userId: owner.userId } : undefined, emitter: "ui" });
|
|
7808
|
+
usedBy.respond(res, 200, { granted, changedAt });
|
|
7809
|
+
return true;
|
|
7810
|
+
}
|
|
7811
|
+
|
|
7812
|
+
// src/http/ranking.ts
|
|
7813
|
+
var ListingSchema = object2({
|
|
7814
|
+
v: literal2(1),
|
|
7815
|
+
claim: string2(),
|
|
7816
|
+
name: string2(),
|
|
7817
|
+
domain: string2(),
|
|
7818
|
+
category: _enum2(RANKING_CATEGORIES),
|
|
7819
|
+
subcategory: string2().nullable(),
|
|
7820
|
+
description: string2().nullable(),
|
|
7821
|
+
sourceCodeUrl: string2().nullable(),
|
|
7822
|
+
updatedAt: string2()
|
|
7823
|
+
});
|
|
7824
|
+
var RankingStateSchema = object2({
|
|
7825
|
+
eligible: boolean(),
|
|
7826
|
+
missing: array2(_enum2(ORG_IDENTITY_KEYS)),
|
|
7827
|
+
telemetryOn: boolean(),
|
|
7828
|
+
enabled: boolean(),
|
|
7829
|
+
name: string2().nullable(),
|
|
7830
|
+
domain: string2().nullable(),
|
|
7831
|
+
listing: ListingSchema.nullable(),
|
|
7832
|
+
listingMatchesDomain: boolean(),
|
|
7833
|
+
fileJson: string2().nullable(),
|
|
7834
|
+
prUrl: string2().nullable(),
|
|
7835
|
+
verifyUrl: string2().nullable()
|
|
7836
|
+
});
|
|
7837
|
+
var OptInResponseSchema = object2({
|
|
7838
|
+
domain: string2(),
|
|
7839
|
+
claim: string2(),
|
|
7840
|
+
listing: ListingSchema,
|
|
7841
|
+
fileJson: string2(),
|
|
7842
|
+
prUrl: string2(),
|
|
7843
|
+
verifyUrl: string2()
|
|
7844
|
+
});
|
|
7845
|
+
var OptOutResponseSchema = object2({
|
|
7846
|
+
enabled: literal2(false),
|
|
7847
|
+
leaveUrl: string2().nullable()
|
|
7848
|
+
});
|
|
7849
|
+
var getRanking = route({
|
|
7850
|
+
method: "get",
|
|
7851
|
+
path: "/api/ranking",
|
|
7852
|
+
pattern: ["api", "ranking"],
|
|
7853
|
+
summary: "Public ranking state",
|
|
7854
|
+
description: "Whether this install can join the public agent-swarm.dev ranking (telemetry on, organization name and website set), whether it is opted in, and its stored listing. The listing carries the private ranking claim, so only a config admin can read it.",
|
|
7855
|
+
tags: ["Telemetry"],
|
|
7856
|
+
responses: {
|
|
7857
|
+
200: { description: "Ranking state", schema: RankingStateSchema },
|
|
7858
|
+
401: { description: "Unauthorized" },
|
|
7859
|
+
403: { description: "Only a config admin can read the ranking state" }
|
|
7860
|
+
},
|
|
7861
|
+
rbac: { permission: "config.write.any" }
|
|
7862
|
+
});
|
|
7863
|
+
var optIn = route({
|
|
7864
|
+
method: "post",
|
|
7865
|
+
path: "/api/ranking/opt-in",
|
|
7866
|
+
pattern: ["api", "ranking", "opt-in"],
|
|
7867
|
+
summary: "Opt this install into the public ranking",
|
|
7868
|
+
description: "Builds the telemetry_ranking listing from the organization name and website plus the chosen category, stores it, turns PUBLIC_RANKING_ENABLED on and sends ranking.opted_in. The install is ranked only after a ranking PR with the returned file is merged into desplega-ai/agent-swarm.",
|
|
7869
|
+
tags: ["Telemetry"],
|
|
7870
|
+
body: rankingFormSchema,
|
|
7871
|
+
responses: {
|
|
7872
|
+
200: { description: "Opted in", schema: OptInResponseSchema },
|
|
7873
|
+
400: { description: "A listing field is invalid" },
|
|
7874
|
+
401: { description: "Unauthorized" },
|
|
7875
|
+
403: { description: "Only a config admin can opt in" },
|
|
7876
|
+
409: {
|
|
7877
|
+
description: "telemetry_off: telemetry is off or the install has no ID. org_identity_missing: the organization name or website is not set (`missing` lists which)."
|
|
7878
|
+
}
|
|
7879
|
+
},
|
|
7880
|
+
rbac: { permission: "config.write.any" }
|
|
7881
|
+
});
|
|
7882
|
+
var optOut = route({
|
|
7883
|
+
method: "post",
|
|
7884
|
+
path: "/api/ranking/opt-out",
|
|
7885
|
+
pattern: ["api", "ranking", "opt-out"],
|
|
7886
|
+
summary: "Leave the public ranking",
|
|
7887
|
+
description: "Turns PUBLIC_RANKING_ENABLED off and sends ranking.opted_out, so workers stop sending ranking.usage. Returns the GitHub page that deletes the ranking entry.",
|
|
7888
|
+
tags: ["Telemetry"],
|
|
7889
|
+
responses: {
|
|
7890
|
+
200: { description: "Opted out", schema: OptOutResponseSchema },
|
|
7891
|
+
401: { description: "Unauthorized" },
|
|
7892
|
+
403: { description: "Only a config admin can opt out" }
|
|
7893
|
+
},
|
|
7894
|
+
rbac: { permission: "config.write.any" }
|
|
7895
|
+
});
|
|
7896
|
+
var RANKING_CONFIG_KEYS = new Set([...ORG_IDENTITY_KEYS, PUBLIC_RANKING_ENABLED_KEY]);
|
|
7897
|
+
async function currentIdentity(changed) {
|
|
7898
|
+
const read = async (key) => changed?.key === key ? changed.value : await globalConfig(key);
|
|
7899
|
+
return orgIdentityStatus({
|
|
7900
|
+
SWARM_ORG_NAME: await read("SWARM_ORG_NAME"),
|
|
7901
|
+
SWARM_ORG_WEBSITE: await read("SWARM_ORG_WEBSITE")
|
|
7902
|
+
});
|
|
7903
|
+
}
|
|
7904
|
+
async function storedListing() {
|
|
7905
|
+
return readTelemetryRanking(await globalConfig(TELEMETRY_RANKING_KEY));
|
|
7906
|
+
}
|
|
7907
|
+
async function rankingEnabled() {
|
|
7908
|
+
const raw = await globalConfig(PUBLIC_RANKING_ENABLED_KEY);
|
|
7909
|
+
return isEnvFlagEnabled(PUBLIC_RANKING_ENABLED_KEY, false, {
|
|
7910
|
+
[PUBLIC_RANKING_ENABLED_KEY]: raw
|
|
7911
|
+
});
|
|
7912
|
+
}
|
|
7913
|
+
async function telemetryReady() {
|
|
7914
|
+
return isTelemetryEnabled() && Boolean(await globalConfig("telemetry_installation_id"));
|
|
7915
|
+
}
|
|
7916
|
+
async function verifyUrl(listing) {
|
|
7917
|
+
const params = new URLSearchParams({ domain: listing.domain, claim: listing.claim });
|
|
7918
|
+
return `${await telemetryProxyEndpoint("/v1/ranking/verify")}?${params.toString()}`;
|
|
7919
|
+
}
|
|
7920
|
+
async function setEnabled(enabled) {
|
|
7921
|
+
await upsertSwarmConfig2({
|
|
7922
|
+
scope: "global",
|
|
7923
|
+
key: PUBLIC_RANKING_ENABLED_KEY,
|
|
7924
|
+
value: String(enabled)
|
|
7925
|
+
});
|
|
7926
|
+
}
|
|
7927
|
+
function emitOptedIn(listing) {
|
|
7928
|
+
telemetry.ranking("opted_in", { listing: JSON.stringify(listing) }, { orgName: listing.name, orgWebsite: listing.domain });
|
|
7929
|
+
}
|
|
7930
|
+
function emitOptedOut(domain) {
|
|
7931
|
+
telemetry.ranking("opted_out", { domain });
|
|
7932
|
+
}
|
|
7933
|
+
async function buildRankingState() {
|
|
7934
|
+
const [identity, listing, enabled, telemetryOn] = await Promise.all([
|
|
7935
|
+
currentIdentity(),
|
|
7936
|
+
storedListing(),
|
|
7937
|
+
rankingEnabled(),
|
|
7938
|
+
telemetryReady()
|
|
7939
|
+
]);
|
|
7940
|
+
const listingMatchesDomain = Boolean(listing && listing.domain === identity.website);
|
|
7941
|
+
const current = listingMatchesDomain ? listing : null;
|
|
7942
|
+
return {
|
|
7943
|
+
eligible: telemetryOn && identity.ok,
|
|
7944
|
+
missing: identity.missing,
|
|
7945
|
+
telemetryOn,
|
|
7946
|
+
enabled: enabled && listingMatchesDomain,
|
|
7947
|
+
name: identity.name,
|
|
7948
|
+
domain: identity.website,
|
|
7949
|
+
listing,
|
|
7950
|
+
listingMatchesDomain,
|
|
7951
|
+
fileJson: current ? rankingEntryFile(current) : null,
|
|
7952
|
+
prUrl: current ? rankingPrUrl(current) : null,
|
|
7953
|
+
verifyUrl: current ? await verifyUrl(current) : null
|
|
7954
|
+
};
|
|
7955
|
+
}
|
|
7956
|
+
async function syncRankingAfterConfigChange(key, value) {
|
|
7957
|
+
if (!RANKING_CONFIG_KEYS.has(key))
|
|
7958
|
+
return;
|
|
7959
|
+
const listing = await storedListing();
|
|
7960
|
+
if (!listing)
|
|
7961
|
+
return;
|
|
7962
|
+
if (key === PUBLIC_RANKING_ENABLED_KEY) {
|
|
7963
|
+
if (!isEnvFlagEnabled(key, false, { [key]: value ?? undefined })) {
|
|
7964
|
+
emitOptedOut(listing.domain);
|
|
7965
|
+
}
|
|
7966
|
+
return;
|
|
7967
|
+
}
|
|
7968
|
+
if (!await rankingEnabled())
|
|
7969
|
+
return;
|
|
7970
|
+
const identity = await currentIdentity({ key, value });
|
|
7971
|
+
let rebuilt = null;
|
|
7972
|
+
if (identity.ok && identity.website === listing.domain) {
|
|
7973
|
+
if (identity.name === listing.name)
|
|
7974
|
+
return;
|
|
7975
|
+
try {
|
|
7976
|
+
rebuilt = buildListing(identity, listing, listing.claim);
|
|
7977
|
+
} catch {}
|
|
7978
|
+
}
|
|
7979
|
+
if (!rebuilt) {
|
|
7980
|
+
await setEnabled(false);
|
|
7981
|
+
emitOptedOut(listing.domain);
|
|
7982
|
+
return;
|
|
7983
|
+
}
|
|
7984
|
+
await upsertSwarmConfig2({
|
|
7985
|
+
scope: "global",
|
|
7986
|
+
key: TELEMETRY_RANKING_KEY,
|
|
7987
|
+
value: JSON.stringify(rebuilt)
|
|
7988
|
+
});
|
|
7989
|
+
emitOptedIn(rebuilt);
|
|
7990
|
+
}
|
|
7991
|
+
function conflict(res, body) {
|
|
7992
|
+
res.writeHead(409, { "Content-Type": "application/json" });
|
|
7993
|
+
res.end(JSON.stringify(body));
|
|
7994
|
+
}
|
|
7995
|
+
async function handleRanking(req, res, pathSegments, queryParams) {
|
|
7996
|
+
if (getRanking.match(req.method, pathSegments)) {
|
|
7997
|
+
const parsed = await getRanking.parse(req, res, pathSegments, queryParams);
|
|
7998
|
+
if (!parsed)
|
|
7999
|
+
return true;
|
|
8000
|
+
if (!await ensureConfigAdmin(req, res, "config.write.any"))
|
|
8001
|
+
return true;
|
|
8002
|
+
getRanking.respond(res, 200, await buildRankingState());
|
|
8003
|
+
return true;
|
|
8004
|
+
}
|
|
8005
|
+
if (optIn.match(req.method, pathSegments)) {
|
|
8006
|
+
const parsed = await optIn.parse(req, res, pathSegments, queryParams);
|
|
8007
|
+
if (!parsed)
|
|
8008
|
+
return true;
|
|
8009
|
+
if (!await ensureConfigAdmin(req, res, "config.write.any"))
|
|
8010
|
+
return true;
|
|
8011
|
+
if (!await telemetryReady()) {
|
|
8012
|
+
conflict(res, {
|
|
8013
|
+
error: "telemetry_off",
|
|
8014
|
+
message: "Turn telemetry on first: the ranking counts tokens from telemetry."
|
|
8015
|
+
});
|
|
8016
|
+
return true;
|
|
8017
|
+
}
|
|
8018
|
+
const identity = await currentIdentity();
|
|
8019
|
+
if (!identity.ok || !identity.website) {
|
|
8020
|
+
conflict(res, {
|
|
8021
|
+
error: "org_identity_missing",
|
|
8022
|
+
message: "Set the organization name and website first",
|
|
8023
|
+
missing: identity.missing
|
|
8024
|
+
});
|
|
8025
|
+
return true;
|
|
8026
|
+
}
|
|
8027
|
+
const website = identity.website;
|
|
8028
|
+
const db = getDbClient2();
|
|
8029
|
+
const built = await db.transaction(async () => {
|
|
8030
|
+
const stored = await storedListing();
|
|
8031
|
+
const claim = stored?.domain === website ? stored.claim : mintRankingClaim();
|
|
8032
|
+
let listing;
|
|
8033
|
+
try {
|
|
8034
|
+
listing = buildListing(identity, parsed.body, claim);
|
|
8035
|
+
} catch (error) {
|
|
8036
|
+
return { ok: false, error };
|
|
8037
|
+
}
|
|
8038
|
+
await upsertSwarmConfig2({
|
|
8039
|
+
scope: "global",
|
|
8040
|
+
key: TELEMETRY_RANKING_KEY,
|
|
8041
|
+
value: JSON.stringify(listing)
|
|
8042
|
+
});
|
|
8043
|
+
await setEnabled(true);
|
|
8044
|
+
db.afterCommit(() => emitOptedIn(listing));
|
|
8045
|
+
return { ok: true, listing };
|
|
8046
|
+
});
|
|
8047
|
+
if (!built.ok) {
|
|
8048
|
+
const issue = built.error instanceof ZodError ? built.error.issues[0] : undefined;
|
|
8049
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
8050
|
+
res.end(JSON.stringify({
|
|
8051
|
+
error: issue ? `Invalid ${issue.path.join(".") || "listing"}: ${issue.message}` : "Invalid listing"
|
|
8052
|
+
}));
|
|
8053
|
+
return true;
|
|
8054
|
+
}
|
|
8055
|
+
const { listing } = built;
|
|
8056
|
+
scheduleIntegrationsReload();
|
|
8057
|
+
optIn.respond(res, 200, {
|
|
8058
|
+
domain: listing.domain,
|
|
8059
|
+
claim: listing.claim,
|
|
8060
|
+
listing,
|
|
8061
|
+
fileJson: rankingEntryFile(listing),
|
|
8062
|
+
prUrl: rankingPrUrl(listing),
|
|
8063
|
+
verifyUrl: await verifyUrl(listing)
|
|
8064
|
+
});
|
|
8065
|
+
return true;
|
|
8066
|
+
}
|
|
8067
|
+
if (optOut.match(req.method, pathSegments)) {
|
|
8068
|
+
const parsed = await optOut.parse(req, res, pathSegments, queryParams);
|
|
8069
|
+
if (!parsed)
|
|
8070
|
+
return true;
|
|
8071
|
+
if (!await ensureConfigAdmin(req, res, "config.write.any"))
|
|
8072
|
+
return true;
|
|
8073
|
+
const listing = await storedListing();
|
|
8074
|
+
await setEnabled(false);
|
|
8075
|
+
if (listing)
|
|
8076
|
+
emitOptedOut(listing.domain);
|
|
8077
|
+
scheduleIntegrationsReload();
|
|
8078
|
+
optOut.respond(res, 200, {
|
|
8079
|
+
enabled: false,
|
|
8080
|
+
leaveUrl: listing ? rankingLeaveUrl(listing.domain) : null
|
|
8081
|
+
});
|
|
8082
|
+
return true;
|
|
8083
|
+
}
|
|
8084
|
+
return false;
|
|
8085
|
+
}
|
|
8086
|
+
|
|
8087
|
+
// src/http/config.ts
|
|
7578
8088
|
var MAX_ENV_PRESENCE_KEYS = 200;
|
|
7579
8089
|
var SECRETS_FORCE_MASK_NOTE = " (secret values masked: this token cannot read secrets in this scope)";
|
|
7580
8090
|
function stripApiOnlyKeys(configs) {
|
|
@@ -7944,6 +8454,8 @@ async function handleConfig(req, res, pathSegments, queryParams) {
|
|
|
7944
8454
|
if (scope === "global" && key === "SWARM_ORG_WEBSITE") {
|
|
7945
8455
|
await fillOrgLogoFromWebsite(String(value));
|
|
7946
8456
|
}
|
|
8457
|
+
if (scope === "global")
|
|
8458
|
+
await syncRankingAfterConfigChange(key, String(value));
|
|
7947
8459
|
if (scope === "global") {
|
|
7948
8460
|
scheduleIntegrationsReload();
|
|
7949
8461
|
}
|
|
@@ -7979,6 +8491,7 @@ async function handleConfig(req, res, pathSegments, queryParams) {
|
|
|
7979
8491
|
return true;
|
|
7980
8492
|
}
|
|
7981
8493
|
if (existing.scope === "global") {
|
|
8494
|
+
await syncRankingAfterConfigChange(existing.key, null);
|
|
7982
8495
|
scheduleIntegrationsReload();
|
|
7983
8496
|
}
|
|
7984
8497
|
if (existing.scope === "agent" && existing.scopeId && existing.key === AGENT_MAX_TASKS_CONFIG_KEY) {
|
|
@@ -8815,6 +9328,7 @@ var postContext = route({
|
|
|
8815
9328
|
method: "post",
|
|
8816
9329
|
path: "/api/tasks/{id}/context",
|
|
8817
9330
|
pattern: ["api", "tasks", null, "context"],
|
|
9331
|
+
session: { taskParam: "id" },
|
|
8818
9332
|
summary: "Record a context usage snapshot for a task",
|
|
8819
9333
|
tags: ["Tasks"],
|
|
8820
9334
|
params: object2({ id: string2() }),
|
|
@@ -8841,6 +9355,7 @@ var getContext = route({
|
|
|
8841
9355
|
method: "get",
|
|
8842
9356
|
path: "/api/tasks/{id}/context",
|
|
8843
9357
|
pattern: ["api", "tasks", null, "context"],
|
|
9358
|
+
session: { taskParam: "id" },
|
|
8844
9359
|
summary: "Get context usage history for a task",
|
|
8845
9360
|
tags: ["Tasks"],
|
|
8846
9361
|
params: object2({ id: string2() }),
|
|
@@ -8921,6 +9436,7 @@ var createEventRoute = route({
|
|
|
8921
9436
|
method: "post",
|
|
8922
9437
|
path: "/api/events",
|
|
8923
9438
|
pattern: ["api", "events"],
|
|
9439
|
+
session: bodyTask("taskId"),
|
|
8924
9440
|
summary: "Store a single event",
|
|
8925
9441
|
tags: ["Events"],
|
|
8926
9442
|
body: eventBodySchema,
|
|
@@ -8937,6 +9453,7 @@ var createEventsBatchRoute = route({
|
|
|
8937
9453
|
method: "post",
|
|
8938
9454
|
path: "/api/events/batch",
|
|
8939
9455
|
pattern: ["api", "events", "batch"],
|
|
9456
|
+
session: bodyListTask("events", "taskId"),
|
|
8940
9457
|
summary: "Store multiple events in a batch",
|
|
8941
9458
|
tags: ["Events"],
|
|
8942
9459
|
body: object2({
|
|
@@ -9011,7 +9528,7 @@ var getEventCountsRoute = route({
|
|
|
9011
9528
|
auth: { apiKey: true }
|
|
9012
9529
|
});
|
|
9013
9530
|
var MAX_LATEST_PAIRS = 50;
|
|
9014
|
-
async function handleEvents(req, res, pathSegments, queryParams,
|
|
9531
|
+
async function handleEvents(req, res, pathSegments, queryParams, myAgentId) {
|
|
9015
9532
|
if (createEventsBatchRoute.match(req.method, pathSegments)) {
|
|
9016
9533
|
const parsed = await createEventsBatchRoute.parse(req, res, pathSegments, queryParams);
|
|
9017
9534
|
if (!parsed)
|
|
@@ -9085,7 +9602,8 @@ async function handleEvents(req, res, pathSegments, queryParams, _myAgentId) {
|
|
|
9085
9602
|
until: parsed.query.until || undefined,
|
|
9086
9603
|
limit: parsed.query.limit ?? 100,
|
|
9087
9604
|
events: eventFilter,
|
|
9088
|
-
latestPerDataField: latestPerDataField ? { events: latestEvents, dataFields } : undefined
|
|
9605
|
+
latestPerDataField: latestPerDataField ? { events: latestEvents, dataFields } : undefined,
|
|
9606
|
+
readableBy: readableByFor2(await resolveSessionActor2(req, myAgentId))
|
|
9089
9607
|
});
|
|
9090
9608
|
getEventsRoute.respond(res, 200, latestPerDataField ? { events, latestPerDataField: true } : { events });
|
|
9091
9609
|
return true;
|
|
@@ -12509,110 +13027,6 @@ async function getLocalFleetStats(now = new Date) {
|
|
|
12509
13027
|
|
|
12510
13028
|
// src/http/fleet-benchmark.ts
|
|
12511
13029
|
init_telemetry();
|
|
12512
|
-
|
|
12513
|
-
// src/http/used-by.ts
|
|
12514
|
-
init_zod();
|
|
12515
|
-
init_db3();
|
|
12516
|
-
init_telemetry();
|
|
12517
|
-
init_org_website();
|
|
12518
|
-
var CONSENT_KEY = "SWARM_USED_BY_CONSENT";
|
|
12519
|
-
var CONSENT_AT_KEY = "SWARM_USED_BY_CONSENT_AT";
|
|
12520
|
-
var GRANTED_NAME_KEY = "SWARM_USED_BY_ORG_NAME";
|
|
12521
|
-
var GRANTED_WEBSITE_KEY = "SWARM_USED_BY_ORG_WEBSITE";
|
|
12522
|
-
var TIMEOUT_MS = 1e4;
|
|
12523
|
-
var usedBy = route({
|
|
12524
|
-
method: "post",
|
|
12525
|
-
path: "/api/used-by",
|
|
12526
|
-
pattern: ["api", "used-by"],
|
|
12527
|
-
summary: "Grant or revoke the 'used by' logo consent",
|
|
12528
|
-
description: "Records whether agent-swarm.dev may show this organization's logo as a company that uses Agent Swarm. Sends the organization name and website to the telemetry proxy, where a person approves each logo by hand. An explicit admin action, so it is not gated by the telemetry opt-out.",
|
|
12529
|
-
tags: ["Telemetry"],
|
|
12530
|
-
body: object2({ granted: boolean() }),
|
|
12531
|
-
responses: {
|
|
12532
|
-
200: {
|
|
12533
|
-
description: "Consent recorded",
|
|
12534
|
-
schema: object2({ granted: boolean(), changedAt: string2() })
|
|
12535
|
-
},
|
|
12536
|
-
400: {
|
|
12537
|
-
description: "Organization name or website is not set (a revoke falls back to the identity of the last grant)"
|
|
12538
|
-
},
|
|
12539
|
-
401: { description: "Unauthorized" },
|
|
12540
|
-
403: { description: "Only a config admin can change the consent" },
|
|
12541
|
-
502: { description: "The telemetry proxy did not accept the consent" }
|
|
12542
|
-
},
|
|
12543
|
-
rbac: { permission: "config.write.any" }
|
|
12544
|
-
});
|
|
12545
|
-
async function globalConfig(key) {
|
|
12546
|
-
const stored = (await getSwarmConfigs2({ scope: "global", key }))?.[0]?.value?.trim();
|
|
12547
|
-
return stored || process.env[key]?.trim() || undefined;
|
|
12548
|
-
}
|
|
12549
|
-
async function telemetryProxyEndpoint(path) {
|
|
12550
|
-
const feedback = await globalConfig("feedback_endpoint");
|
|
12551
|
-
if (feedback && /^https?:\/\/[^/]+\/v1\/feedback\/?$/.test(feedback)) {
|
|
12552
|
-
return feedback.replace(/\/v1\/feedback\/?$/, path);
|
|
12553
|
-
}
|
|
12554
|
-
return `${TELEMETRY_PROXY_ORIGIN}${path}`;
|
|
12555
|
-
}
|
|
12556
|
-
function usedByEndpoint() {
|
|
12557
|
-
return telemetryProxyEndpoint("/v1/used-by");
|
|
12558
|
-
}
|
|
12559
|
-
async function handleUsedBy(req, res, pathSegments, queryParams, myAgentId) {
|
|
12560
|
-
if (!usedBy.match(req.method, pathSegments))
|
|
12561
|
-
return false;
|
|
12562
|
-
const parsed = await usedBy.parse(req, res, pathSegments, queryParams);
|
|
12563
|
-
if (!parsed)
|
|
12564
|
-
return true;
|
|
12565
|
-
if (!await ensureConfigAdmin(req, res, "config.write.any"))
|
|
12566
|
-
return true;
|
|
12567
|
-
const { granted } = parsed.body;
|
|
12568
|
-
const currentName = await globalConfig("SWARM_ORG_NAME");
|
|
12569
|
-
const currentWebsite = normalizeOrgWebsite(await globalConfig("SWARM_ORG_WEBSITE"));
|
|
12570
|
-
const grantedName = granted ? undefined : await globalConfig(GRANTED_NAME_KEY);
|
|
12571
|
-
const grantedWebsite = granted ? null : normalizeOrgWebsite(await globalConfig(GRANTED_WEBSITE_KEY));
|
|
12572
|
-
const [orgName, orgWebsite] = grantedName && grantedWebsite ? [grantedName, grantedWebsite] : [currentName, currentWebsite];
|
|
12573
|
-
if (!orgName || !orgWebsite) {
|
|
12574
|
-
jsonError(res, "Set the organization name and website first", 400);
|
|
12575
|
-
return true;
|
|
12576
|
-
}
|
|
12577
|
-
const installId = isTelemetryEnabled() ? await globalConfig("telemetry_installation_id") : undefined;
|
|
12578
|
-
const orgDomain = await globalConfig("telemetry_org_domain");
|
|
12579
|
-
const changedAt = new Date().toISOString();
|
|
12580
|
-
try {
|
|
12581
|
-
const response = await fetch(await usedByEndpoint(), {
|
|
12582
|
-
method: "POST",
|
|
12583
|
-
headers: { "Content-Type": "application/json" },
|
|
12584
|
-
body: JSON.stringify({
|
|
12585
|
-
...installId ? { install_id: installId } : {},
|
|
12586
|
-
org_name: orgName,
|
|
12587
|
-
org_website: orgWebsite,
|
|
12588
|
-
...orgDomain ? { org_domain: orgDomain } : {},
|
|
12589
|
-
granted,
|
|
12590
|
-
changed_at: changedAt
|
|
12591
|
-
}),
|
|
12592
|
-
signal: AbortSignal.timeout(TIMEOUT_MS)
|
|
12593
|
-
});
|
|
12594
|
-
if (!response.ok) {
|
|
12595
|
-
jsonError(res, `The telemetry proxy answered ${response.status}`, 502);
|
|
12596
|
-
return true;
|
|
12597
|
-
}
|
|
12598
|
-
} catch {
|
|
12599
|
-
jsonError(res, "Could not reach the telemetry proxy", 502);
|
|
12600
|
-
return true;
|
|
12601
|
-
}
|
|
12602
|
-
await upsertSwarmConfig2({ scope: "global", key: CONSENT_KEY, value: String(granted) });
|
|
12603
|
-
await upsertSwarmConfig2({ scope: "global", key: CONSENT_AT_KEY, value: changedAt });
|
|
12604
|
-
if (granted) {
|
|
12605
|
-
await upsertSwarmConfig2({ scope: "global", key: GRANTED_NAME_KEY, value: orgName });
|
|
12606
|
-
await upsertSwarmConfig2({ scope: "global", key: GRANTED_WEBSITE_KEY, value: orgWebsite });
|
|
12607
|
-
}
|
|
12608
|
-
scheduleIntegrationsReload();
|
|
12609
|
-
const owner = await resolveHttpFavoriteOwner(req, myAgentId);
|
|
12610
|
-
trackRelayed("used_by.consent_changed", { granted }, { actor: owner?.userId ? { userId: owner.userId } : undefined, emitter: "ui" });
|
|
12611
|
-
usedBy.respond(res, 200, { granted, changedAt });
|
|
12612
|
-
return true;
|
|
12613
|
-
}
|
|
12614
|
-
|
|
12615
|
-
// src/http/fleet-benchmark.ts
|
|
12616
13030
|
var FLEET_TTL_MS = 24 * 60 * 60 * 1000;
|
|
12617
13031
|
var FLEET_FAILURE_TTL_MS = 60 * 60 * 1000;
|
|
12618
13032
|
var LOCAL_TTL_MS = 10 * 60 * 1000;
|
|
@@ -12871,6 +13285,7 @@ var listTaskFilesRoute = route({
|
|
|
12871
13285
|
method: "get",
|
|
12872
13286
|
path: "/api/fs/tasks/{taskId}/files",
|
|
12873
13287
|
pattern: ["api", "fs", "tasks", null, "files"],
|
|
13288
|
+
session: { taskParam: "taskId" },
|
|
12874
13289
|
summary: "List task file attachments",
|
|
12875
13290
|
tags: ["FS"],
|
|
12876
13291
|
params: taskParams,
|
|
@@ -12886,6 +13301,7 @@ var uploadTaskFileRoute = route({
|
|
|
12886
13301
|
method: "post",
|
|
12887
13302
|
path: "/api/fs/tasks/{taskId}/files",
|
|
12888
13303
|
pattern: ["api", "fs", "tasks", null, "files"],
|
|
13304
|
+
session: { taskParam: "taskId" },
|
|
12889
13305
|
summary: "Upload a binary task file attachment",
|
|
12890
13306
|
description: "Accepts a raw binary request body. Pass the display/path name as the `name` query parameter.",
|
|
12891
13307
|
tags: ["FS"],
|
|
@@ -12905,6 +13321,7 @@ var getTaskFileRoute = route({
|
|
|
12905
13321
|
method: "get",
|
|
12906
13322
|
path: "/api/fs/tasks/{taskId}/files/{attachmentId}",
|
|
12907
13323
|
pattern: ["api", "fs", "tasks", null, "files", null],
|
|
13324
|
+
session: { taskParam: "taskId" },
|
|
12908
13325
|
summary: "Get task file attachment metadata",
|
|
12909
13326
|
tags: ["FS"],
|
|
12910
13327
|
params: attachmentParams,
|
|
@@ -12917,6 +13334,7 @@ var downloadTaskFileRoute = route({
|
|
|
12917
13334
|
method: "get",
|
|
12918
13335
|
path: "/api/fs/tasks/{taskId}/files/{attachmentId}/raw",
|
|
12919
13336
|
pattern: ["api", "fs", "tasks", null, "files", null, "raw"],
|
|
13337
|
+
session: { taskParam: "taskId" },
|
|
12920
13338
|
summary: "Download raw task file bytes",
|
|
12921
13339
|
description: "Streams raw bytes. File content is not secret-scrubbed.",
|
|
12922
13340
|
tags: ["FS"],
|
|
@@ -12933,6 +13351,7 @@ var signedUrlTaskFileRoute = route({
|
|
|
12933
13351
|
method: "get",
|
|
12934
13352
|
path: "/api/fs/tasks/{taskId}/files/{attachmentId}/signed-url",
|
|
12935
13353
|
pattern: ["api", "fs", "tasks", null, "files", null, "signed-url"],
|
|
13354
|
+
session: { taskParam: "taskId" },
|
|
12936
13355
|
summary: "Create a provider signed GET URL for a task file",
|
|
12937
13356
|
tags: ["FS"],
|
|
12938
13357
|
params: attachmentParams,
|
|
@@ -12947,6 +13366,7 @@ var deleteTaskFileRoute = route({
|
|
|
12947
13366
|
method: "delete",
|
|
12948
13367
|
path: "/api/fs/tasks/{taskId}/files/{attachmentId}",
|
|
12949
13368
|
pattern: ["api", "fs", "tasks", null, "files", null],
|
|
13369
|
+
session: { taskParam: "taskId" },
|
|
12950
13370
|
summary: "Delete a task file attachment",
|
|
12951
13371
|
tags: ["FS"],
|
|
12952
13372
|
params: attachmentParams,
|
|
@@ -12995,7 +13415,7 @@ async function handleFs(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
12995
13415
|
const parsed = await downloadTaskFileRoute.parse(req, res, pathSegments, queryParams);
|
|
12996
13416
|
if (!parsed)
|
|
12997
13417
|
return true;
|
|
12998
|
-
const attachment = await findAttachment(parsed.params.taskId, parsed.params.attachmentId, res);
|
|
13418
|
+
const attachment = await findAttachment(req, myAgentId, parsed.params.taskId, parsed.params.attachmentId, res);
|
|
12999
13419
|
if (!attachment)
|
|
13000
13420
|
return true;
|
|
13001
13421
|
return sendDownload(res, attachment);
|
|
@@ -13004,7 +13424,7 @@ async function handleFs(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
13004
13424
|
const parsed = await signedUrlTaskFileRoute.parse(req, res, pathSegments, queryParams);
|
|
13005
13425
|
if (!parsed)
|
|
13006
13426
|
return true;
|
|
13007
|
-
const attachment = await findAttachment(parsed.params.taskId, parsed.params.attachmentId, res);
|
|
13427
|
+
const attachment = await findAttachment(req, myAgentId, parsed.params.taskId, parsed.params.attachmentId, res);
|
|
13008
13428
|
if (!attachment)
|
|
13009
13429
|
return true;
|
|
13010
13430
|
return sendSignedUrl(res, attachment, parsed.query.expiresIn);
|
|
@@ -13013,7 +13433,7 @@ async function handleFs(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
13013
13433
|
const parsed = await getTaskFileRoute.parse(req, res, pathSegments, queryParams);
|
|
13014
13434
|
if (!parsed)
|
|
13015
13435
|
return true;
|
|
13016
|
-
const attachment = await findAttachment(parsed.params.taskId, parsed.params.attachmentId, res);
|
|
13436
|
+
const attachment = await findAttachment(req, myAgentId, parsed.params.taskId, parsed.params.attachmentId, res);
|
|
13017
13437
|
if (!attachment)
|
|
13018
13438
|
return true;
|
|
13019
13439
|
getTaskFileRoute.respond(res, 200, attachment);
|
|
@@ -13052,6 +13472,10 @@ async function handleFs(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
13052
13472
|
return true;
|
|
13053
13473
|
if (!await getTaskById2(parsed.params.taskId))
|
|
13054
13474
|
return notFound(res, "Task not found");
|
|
13475
|
+
if (!await canReadSession2(req, myAgentId, parsed.params.taskId)) {
|
|
13476
|
+
jsonError(res, SESSION_READ_DENIED2, 403);
|
|
13477
|
+
return true;
|
|
13478
|
+
}
|
|
13055
13479
|
listTaskFilesRoute.respond(res, 200, {
|
|
13056
13480
|
attachments: await getTaskAttachments2(parsed.params.taskId)
|
|
13057
13481
|
});
|
|
@@ -13187,11 +13611,15 @@ function backingProviderId(attachment) {
|
|
|
13187
13611
|
return "local-fs";
|
|
13188
13612
|
return null;
|
|
13189
13613
|
}
|
|
13190
|
-
async function findAttachment(taskId, attachmentId, res) {
|
|
13614
|
+
async function findAttachment(req, myAgentId, taskId, attachmentId, res) {
|
|
13191
13615
|
if (!await getTaskById2(taskId)) {
|
|
13192
13616
|
notFound(res, "Task not found");
|
|
13193
13617
|
return null;
|
|
13194
13618
|
}
|
|
13619
|
+
if (!await canReadSession2(req, myAgentId, taskId)) {
|
|
13620
|
+
jsonError(res, SESSION_READ_DENIED2, 403);
|
|
13621
|
+
return null;
|
|
13622
|
+
}
|
|
13195
13623
|
const attachment = (await getTaskAttachments2(taskId)).find((item) => item.id === attachmentId);
|
|
13196
13624
|
if (!attachment) {
|
|
13197
13625
|
notFound(res, "Attachment not found");
|
|
@@ -13333,20 +13761,21 @@ var listState = route({
|
|
|
13333
13761
|
},
|
|
13334
13762
|
auth: { apiKey: true }
|
|
13335
13763
|
});
|
|
13764
|
+
var inboxStateItemSchema = object2({
|
|
13765
|
+
itemType: InboxItemTypeSchema,
|
|
13766
|
+
itemId: string2().min(1),
|
|
13767
|
+
status: InboxItemStatusSchema,
|
|
13768
|
+
snoozeUntil: string2().datetime().optional(),
|
|
13769
|
+
readAt: string2().datetime().optional(),
|
|
13770
|
+
lastReadAt: string2().datetime().nullable().optional().describe("Omit to preserve the read cursor, send null to clear it, or send a timestamp to set it, including an earlier time.")
|
|
13771
|
+
});
|
|
13336
13772
|
var upsertState = route({
|
|
13337
13773
|
method: "patch",
|
|
13338
13774
|
path: "/api/inbox-state",
|
|
13339
13775
|
pattern: ["api", "inbox-state"],
|
|
13340
13776
|
summary: "Upsert per-user dismiss/snooze/done state for an inbox item",
|
|
13341
13777
|
tags: ["Inbox State"],
|
|
13342
|
-
body:
|
|
13343
|
-
userId: string2(),
|
|
13344
|
-
itemType: InboxItemTypeSchema,
|
|
13345
|
-
itemId: string2().min(1),
|
|
13346
|
-
status: InboxItemStatusSchema,
|
|
13347
|
-
snoozeUntil: string2().datetime().optional(),
|
|
13348
|
-
readAt: string2().datetime().optional()
|
|
13349
|
-
}),
|
|
13778
|
+
body: inboxStateItemSchema.extend({ userId: string2() }),
|
|
13350
13779
|
responses: {
|
|
13351
13780
|
200: {
|
|
13352
13781
|
description: "Upserted inbox state row",
|
|
@@ -13357,6 +13786,27 @@ var upsertState = route({
|
|
|
13357
13786
|
},
|
|
13358
13787
|
auth: { apiKey: true }
|
|
13359
13788
|
});
|
|
13789
|
+
var bulkUpsertState = route({
|
|
13790
|
+
method: "patch",
|
|
13791
|
+
path: "/api/inbox-state/bulk",
|
|
13792
|
+
pattern: ["api", "inbox-state", "bulk"],
|
|
13793
|
+
summary: "Upsert inbox-item state in one transaction",
|
|
13794
|
+
tags: ["Inbox State"],
|
|
13795
|
+
body: object2({
|
|
13796
|
+
userId: string2(),
|
|
13797
|
+
items: array2(inboxStateItemSchema).min(1).max(200)
|
|
13798
|
+
}),
|
|
13799
|
+
responses: {
|
|
13800
|
+
200: {
|
|
13801
|
+
description: "Upserted inbox state rows in request order",
|
|
13802
|
+
schema: object2({ items: array2(InboxItemStateSchema) })
|
|
13803
|
+
},
|
|
13804
|
+
400: { description: "Validation error" },
|
|
13805
|
+
401: { description: "Unauthorized" }
|
|
13806
|
+
},
|
|
13807
|
+
auth: { apiKey: true },
|
|
13808
|
+
rbac: { ungated: "per-user inbox UI state, same posture as PATCH /api/inbox-state" }
|
|
13809
|
+
});
|
|
13360
13810
|
async function handleInboxState(req, res, pathSegments, queryParams) {
|
|
13361
13811
|
if (listState.match(req.method, pathSegments)) {
|
|
13362
13812
|
const parsed = await listState.parse(req, res, pathSegments, queryParams);
|
|
@@ -13370,6 +13820,24 @@ async function handleInboxState(req, res, pathSegments, queryParams) {
|
|
|
13370
13820
|
listState.respond(res, 200, { items });
|
|
13371
13821
|
return true;
|
|
13372
13822
|
}
|
|
13823
|
+
if (bulkUpsertState.match(req.method, pathSegments)) {
|
|
13824
|
+
const parsed = await bulkUpsertState.parse(req, res, pathSegments, queryParams);
|
|
13825
|
+
if (!parsed)
|
|
13826
|
+
return true;
|
|
13827
|
+
try {
|
|
13828
|
+
const items = await getDbClient2().transaction(async () => {
|
|
13829
|
+
const rows = [];
|
|
13830
|
+
for (const item of parsed.body.items) {
|
|
13831
|
+
rows.push(await upsertInboxState2({ ...item, userId: parsed.body.userId }));
|
|
13832
|
+
}
|
|
13833
|
+
return rows;
|
|
13834
|
+
});
|
|
13835
|
+
bulkUpsertState.respond(res, 200, { items });
|
|
13836
|
+
} catch (err) {
|
|
13837
|
+
jsonError(res, err instanceof Error ? err.message : "Failed to upsert inbox state", 500);
|
|
13838
|
+
}
|
|
13839
|
+
return true;
|
|
13840
|
+
}
|
|
13373
13841
|
if (upsertState.match(req.method, pathSegments)) {
|
|
13374
13842
|
const parsed = await upsertState.parse(req, res, pathSegments, queryParams);
|
|
13375
13843
|
if (!parsed)
|
|
@@ -13381,7 +13849,8 @@ async function handleInboxState(req, res, pathSegments, queryParams) {
|
|
|
13381
13849
|
itemId: parsed.body.itemId,
|
|
13382
13850
|
status: parsed.body.status,
|
|
13383
13851
|
snoozeUntil: parsed.body.snoozeUntil,
|
|
13384
|
-
readAt: parsed.body.readAt
|
|
13852
|
+
readAt: parsed.body.readAt,
|
|
13853
|
+
lastReadAt: parsed.body.lastReadAt
|
|
13385
13854
|
});
|
|
13386
13855
|
upsertState.respond(res, 200, { item });
|
|
13387
13856
|
} catch (err) {
|
|
@@ -17790,6 +18259,7 @@ var indexMemory = route({
|
|
|
17790
18259
|
method: "post",
|
|
17791
18260
|
path: "/api/memory/index",
|
|
17792
18261
|
pattern: ["api", "memory", "index"],
|
|
18262
|
+
session: bodyTask("sourceTaskId"),
|
|
17793
18263
|
summary: "Ingest content into memory system (async embedding)",
|
|
17794
18264
|
tags: ["Memory"],
|
|
17795
18265
|
rbac: { permission: "memory.write.consolidated" },
|
|
@@ -18267,6 +18737,7 @@ var rateMemory = route({
|
|
|
18267
18737
|
method: "post",
|
|
18268
18738
|
path: "/api/memory/rate",
|
|
18269
18739
|
pattern: ["api", "memory", "rate"],
|
|
18740
|
+
session: { none: "worker-only: it needs X-Agent-ID, which a user token cannot send" },
|
|
18270
18741
|
summary: "Submit RatingEvents to update memory usefulness posteriors",
|
|
18271
18742
|
tags: ["Memory"],
|
|
18272
18743
|
auth: { apiKey: true, agentId: true },
|
|
@@ -21714,7 +22185,7 @@ var listTasks = route({
|
|
|
21714
22185
|
path: "/api/tasks",
|
|
21715
22186
|
pattern: ["api", "tasks"],
|
|
21716
22187
|
summary: "List tasks with filters",
|
|
21717
|
-
description: "Returns tasks with the full `task` text replaced by a bounded `taskPreview` and completion/integration blobs dropped by default — list views only need the preview. Pass `fields=full` to restore the full `AgentTask`, or `fields=timeline` for the narrow shape the dashboard timeline draws. Fetch a single task in full via `GET /api/tasks/{id}`. `total` (the filtered row count, ignoring limit/offset) is computed only with `includeTotal=true`.",
|
|
22188
|
+
description: "Returns tasks with the full `task` text replaced by a bounded `taskPreview` and completion/integration blobs dropped by default — list views only need the preview. Pass `fields=full` to restore the full `AgentTask`, or `fields=timeline` for the narrow shape the dashboard timeline draws. Every shape gives a user caller only tasks of sessions they can read. Fetch a single task in full via `GET /api/tasks/{id}`. `total` (the filtered row count, ignoring limit/offset) is computed only with `includeTotal=true`.",
|
|
21718
22189
|
tags: ["Tasks"],
|
|
21719
22190
|
query: object2({
|
|
21720
22191
|
status: string2().optional(),
|
|
@@ -21753,6 +22224,7 @@ var createTask = route({
|
|
|
21753
22224
|
method: "post",
|
|
21754
22225
|
path: "/api/tasks",
|
|
21755
22226
|
pattern: ["api", "tasks"],
|
|
22227
|
+
session: bodyTask("parentTaskId"),
|
|
21756
22228
|
summary: "Create a new task",
|
|
21757
22229
|
tags: ["Tasks"],
|
|
21758
22230
|
body: object2({
|
|
@@ -21808,6 +22280,7 @@ var promoteDraftTaskRoute = route({
|
|
|
21808
22280
|
method: "post",
|
|
21809
22281
|
path: "/api/tasks/{id}/promote-draft",
|
|
21810
22282
|
pattern: ["api", "tasks", null, "promote-draft"],
|
|
22283
|
+
session: { taskParam: "id" },
|
|
21811
22284
|
summary: "Promote a draft task out of the pre-dispatch draft state",
|
|
21812
22285
|
description: "Transitions a `draft` task (#1240 — created with attachments still uploading) to its normal dispatch-eligible status: `offered` if it was offered to an agent, `pending` if it has an owning agent (the common case — UI-composer tasks default to Lead), otherwise `unassigned`. Called by the UI composer once its attachment upload batch settles, whether every file uploaded, some failed, or all failed — a draft must never be stranded by an upload error. Idempotent: calling it on a task that already left `draft` returns the current task unchanged rather than erroring, so a retried request is safe.",
|
|
21813
22286
|
tags: ["Tasks"],
|
|
@@ -21823,6 +22296,7 @@ var updateSession = route({
|
|
|
21823
22296
|
method: "put",
|
|
21824
22297
|
path: "/api/tasks/{id}/session",
|
|
21825
22298
|
pattern: ["api", "tasks", null, "session"],
|
|
22299
|
+
session: { taskParam: "id" },
|
|
21826
22300
|
summary: "Update provider session ID and harness metadata for a task",
|
|
21827
22301
|
tags: ["Tasks"],
|
|
21828
22302
|
params: object2({ id: string2() }),
|
|
@@ -21856,6 +22330,7 @@ var cancelTaskRoute = route({
|
|
|
21856
22330
|
method: "post",
|
|
21857
22331
|
path: "/api/tasks/{id}/cancel",
|
|
21858
22332
|
pattern: ["api", "tasks", null, "cancel"],
|
|
22333
|
+
session: { taskParam: "id" },
|
|
21859
22334
|
summary: "Cancel a pending or in-progress task",
|
|
21860
22335
|
tags: ["Tasks"],
|
|
21861
22336
|
params: object2({ id: string2() }),
|
|
@@ -21870,6 +22345,7 @@ var steerTaskRoute = route({
|
|
|
21870
22345
|
method: "post",
|
|
21871
22346
|
path: "/api/tasks/{id}/steer",
|
|
21872
22347
|
pattern: ["api", "tasks", null, "steer"],
|
|
22348
|
+
session: { taskParam: "id" },
|
|
21873
22349
|
summary: "Deliver a steering message to a running task",
|
|
21874
22350
|
tags: ["Tasks"],
|
|
21875
22351
|
params: object2({ id: string2() }),
|
|
@@ -21898,6 +22374,7 @@ var getTaskSteeringMessagesRoute = route({
|
|
|
21898
22374
|
method: "get",
|
|
21899
22375
|
path: "/api/tasks/{id}/steering-messages",
|
|
21900
22376
|
pattern: ["api", "tasks", null, "steering-messages"],
|
|
22377
|
+
session: { taskParam: "id" },
|
|
21901
22378
|
summary: "List steering messages for a task",
|
|
21902
22379
|
tags: ["Tasks"],
|
|
21903
22380
|
params: object2({ id: string2() }),
|
|
@@ -21924,6 +22401,7 @@ var markSteeringDeliveredRoute = route({
|
|
|
21924
22401
|
method: "post",
|
|
21925
22402
|
path: "/api/steering-messages/{id}/delivered",
|
|
21926
22403
|
pattern: ["api", "steering-messages", null, "delivered"],
|
|
22404
|
+
session: { steeringParam: "id" },
|
|
21927
22405
|
summary: "Mark a steering message delivered",
|
|
21928
22406
|
tags: ["Tasks"],
|
|
21929
22407
|
params: object2({ id: string2() }),
|
|
@@ -21947,6 +22425,7 @@ var markSteeringHandledRoute = route({
|
|
|
21947
22425
|
method: "post",
|
|
21948
22426
|
path: "/api/steering-messages/{id}/handled",
|
|
21949
22427
|
pattern: ["api", "steering-messages", null, "handled"],
|
|
22428
|
+
session: { steeringParam: "id" },
|
|
21950
22429
|
summary: "Mark a steering message handled",
|
|
21951
22430
|
description: "Optionally accepts a JSON body `{ note?: string }` — a short acceptance note describing how the steering was incorporated, persisted as `handledNote`.",
|
|
21952
22431
|
tags: ["Tasks"],
|
|
@@ -21969,6 +22448,7 @@ var markSteeringUndeliverableRoute = route({
|
|
|
21969
22448
|
method: "post",
|
|
21970
22449
|
path: "/api/steering-messages/{id}/undeliverable",
|
|
21971
22450
|
pattern: ["api", "steering-messages", null, "undeliverable"],
|
|
22451
|
+
session: { steeringParam: "id" },
|
|
21972
22452
|
summary: "Promote an undeliverable steering message",
|
|
21973
22453
|
tags: ["Tasks"],
|
|
21974
22454
|
params: object2({ id: string2() }),
|
|
@@ -21992,6 +22472,7 @@ var getTask = route({
|
|
|
21992
22472
|
method: "get",
|
|
21993
22473
|
path: "/api/tasks/{id}",
|
|
21994
22474
|
pattern: ["api", "tasks", null],
|
|
22475
|
+
session: { taskParam: "id" },
|
|
21995
22476
|
summary: "Get task details with logs and attachments",
|
|
21996
22477
|
description: "Returns the full `AgentTask` row decorated with `logs` (capped by `logsLimit`) and `attachments` (pointer-based artifacts stored on the task, ordered by `created_at`).",
|
|
21997
22478
|
tags: ["Tasks"],
|
|
@@ -22001,6 +22482,7 @@ var getTask = route({
|
|
|
22001
22482
|
}),
|
|
22002
22483
|
responses: {
|
|
22003
22484
|
200: { description: "Task with logs and attachments", schema: GetTaskResponseSchema },
|
|
22485
|
+
403: { description: "A user caller cannot read the task's session" },
|
|
22004
22486
|
404: { description: "Task not found" }
|
|
22005
22487
|
}
|
|
22006
22488
|
});
|
|
@@ -22008,6 +22490,7 @@ var updateTaskProgressRoute = route({
|
|
|
22008
22490
|
method: "post",
|
|
22009
22491
|
path: "/api/tasks/{id}/progress",
|
|
22010
22492
|
pattern: ["api", "tasks", null, "progress"],
|
|
22493
|
+
session: { taskParam: "id" },
|
|
22011
22494
|
summary: "Update task progress text",
|
|
22012
22495
|
tags: ["Tasks"],
|
|
22013
22496
|
params: object2({ id: string2() }),
|
|
@@ -22025,6 +22508,7 @@ var finishTask = route({
|
|
|
22025
22508
|
method: "post",
|
|
22026
22509
|
path: "/api/tasks/{id}/finish",
|
|
22027
22510
|
pattern: ["api", "tasks", null, "finish"],
|
|
22511
|
+
session: { taskParam: "id" },
|
|
22028
22512
|
summary: "Mark task as completed or failed (runner endpoint)",
|
|
22029
22513
|
tags: ["Tasks"],
|
|
22030
22514
|
params: object2({ id: string2() }),
|
|
@@ -22070,6 +22554,7 @@ var pauseTaskRoute = route({
|
|
|
22070
22554
|
method: "post",
|
|
22071
22555
|
path: "/api/tasks/{id}/pause",
|
|
22072
22556
|
pattern: ["api", "tasks", null, "pause"],
|
|
22557
|
+
session: { taskParam: "id" },
|
|
22073
22558
|
summary: "Pause an in-progress task",
|
|
22074
22559
|
tags: ["Tasks"],
|
|
22075
22560
|
params: object2({ id: string2() }),
|
|
@@ -22084,6 +22569,7 @@ var resumeTaskRoute = route({
|
|
|
22084
22569
|
method: "post",
|
|
22085
22570
|
path: "/api/tasks/{id}/resume",
|
|
22086
22571
|
pattern: ["api", "tasks", null, "resume"],
|
|
22572
|
+
session: { taskParam: "id" },
|
|
22087
22573
|
summary: "Resume a paused task",
|
|
22088
22574
|
tags: ["Tasks"],
|
|
22089
22575
|
params: object2({ id: string2() }),
|
|
@@ -22098,6 +22584,7 @@ var supersedeTaskRoute = route({
|
|
|
22098
22584
|
method: "post",
|
|
22099
22585
|
path: "/api/tasks/{id}/supersede",
|
|
22100
22586
|
pattern: ["api", "tasks", null, "supersede"],
|
|
22587
|
+
session: { taskParam: "id" },
|
|
22101
22588
|
summary: "Supersede an in-progress task (terminate + spawn resume follow-up)",
|
|
22102
22589
|
description: 'Marks the original task `superseded` (terminal) and creates a fresh `taskType="resume"` follow-up so a worker can pick up the work in a new provider session. Workflow-step tasks (those with `workflowRunStepId`) are carved out: the original is marked `failed` with reason `superseded_workflow_task` and no follow-up is created — the workflow engine\'s retry/failure policy applies.',
|
|
22103
22590
|
tags: ["Tasks"],
|
|
@@ -22118,6 +22605,7 @@ var updateTaskVcsRoute = route({
|
|
|
22118
22605
|
method: "patch",
|
|
22119
22606
|
path: "/api/tasks/{id}/vcs",
|
|
22120
22607
|
pattern: ["api", "tasks", null, "vcs"],
|
|
22608
|
+
session: { taskParam: "id" },
|
|
22121
22609
|
summary: "Update VCS (PR/MR) info for a task",
|
|
22122
22610
|
tags: ["Tasks"],
|
|
22123
22611
|
params: object2({ id: string2() }),
|
|
@@ -22138,6 +22626,7 @@ var updateTaskTitleRoute = route({
|
|
|
22138
22626
|
method: "patch",
|
|
22139
22627
|
path: "/api/tasks/{id}/title",
|
|
22140
22628
|
pattern: ["api", "tasks", null, "title"],
|
|
22629
|
+
session: { taskParam: "id" },
|
|
22141
22630
|
summary: "Set or clear a task's display title (session rename)",
|
|
22142
22631
|
description: "Sets a human-facing display title override on a task. The sessions UI only reads this from root tasks (session list items), but titles on child tasks are harmless. Pass `title: null` (or an empty string) to clear the override and fall back to the task prompt.",
|
|
22143
22632
|
tags: ["Tasks"],
|
|
@@ -22154,17 +22643,21 @@ var updateTaskTitleRoute = route({
|
|
|
22154
22643
|
ungated: "mirrors the pre-RBAC PATCH /api/tasks/{id}/vcs sibling route: bearer auth only"
|
|
22155
22644
|
}
|
|
22156
22645
|
});
|
|
22157
|
-
async function
|
|
22646
|
+
async function hasSessionWrite(req, myAgentId, taskId) {
|
|
22647
|
+
const access = await sessionAccessFor2(await resolveSessionActor2(req, myAgentId), taskId);
|
|
22648
|
+
return hasSessionAccess(access, "write");
|
|
22649
|
+
}
|
|
22650
|
+
async function canSteerTask(req, myAgentId, task, sessionAccess = "none") {
|
|
22651
|
+
const auth = getRequestAuth(req);
|
|
22158
22652
|
const resource = {
|
|
22159
22653
|
kind: "task",
|
|
22160
22654
|
taskId: task.id,
|
|
22161
|
-
requestedByUserId: task.requestedByUserId,
|
|
22655
|
+
requestedByUserId: auth?.kind === "user" && sessionAccess === "write" ? auth.userId : task.requestedByUserId,
|
|
22162
22656
|
creatorAgentId: task.creatorAgentId,
|
|
22163
22657
|
agentId: task.agentId
|
|
22164
22658
|
};
|
|
22165
22659
|
let principal;
|
|
22166
22660
|
let verb;
|
|
22167
|
-
const auth = getRequestAuth(req);
|
|
22168
22661
|
if (auth?.kind === "operator") {
|
|
22169
22662
|
principal = { kind: "operator" };
|
|
22170
22663
|
verb = "task.steer.own";
|
|
@@ -22273,6 +22766,7 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
22273
22766
|
if (validated.length > 0)
|
|
22274
22767
|
source = validated;
|
|
22275
22768
|
}
|
|
22769
|
+
const actor = await resolveSessionActor2(req, myAgentId);
|
|
22276
22770
|
const filters = {
|
|
22277
22771
|
status,
|
|
22278
22772
|
agentId: parsed.query.agentId || undefined,
|
|
@@ -22288,7 +22782,8 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
22288
22782
|
requestedByUserIdIsNull: parsed.query.requestedByUserId === "none" || undefined,
|
|
22289
22783
|
orderBy: parsed.query.orderBy,
|
|
22290
22784
|
limit: parsed.query.limit,
|
|
22291
|
-
offset: parsed.query.offset
|
|
22785
|
+
offset: parsed.query.offset,
|
|
22786
|
+
readableBy: actor.kind === "user" ? { userId: actor.userId } : undefined
|
|
22292
22787
|
};
|
|
22293
22788
|
const tasks = parsed.query.fields === "full" ? await getAllTasks2(filters) : parsed.query.fields === "timeline" ? await getAllTasks2(filters, { fields: "timeline" }) : await getAllTasks2(filters, { slim: true });
|
|
22294
22789
|
if (parsed.query.includeTotal === "true") {
|
|
@@ -22346,6 +22841,10 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
22346
22841
|
jsonError(res, `You don't have context for parent task ${parentTask.id}.`, 403);
|
|
22347
22842
|
return true;
|
|
22348
22843
|
}
|
|
22844
|
+
if (parentTask && !await hasSessionWrite(req, myAgentId, parentTask.id)) {
|
|
22845
|
+
jsonError(res, SESSION_WRITE_DENIED2, 403);
|
|
22846
|
+
return true;
|
|
22847
|
+
}
|
|
22349
22848
|
if (principal.kind === "agent" && parsed.body.contextKey && parsed.body.contextKey !== parentTask?.contextKey && chatThreadOfContextKey(parsed.body.contextKey) && !can({ principal, verb: "integration.chat.post", resource: { kind: "none" }, source: "http" }).allow) {
|
|
22350
22849
|
jsonError(res, "Permission denied: only a lead can bind a task to a chat thread.", 403);
|
|
22351
22850
|
return true;
|
|
@@ -22545,7 +23044,13 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
22545
23044
|
jsonError(res, "Task not found", 404);
|
|
22546
23045
|
return true;
|
|
22547
23046
|
}
|
|
22548
|
-
|
|
23047
|
+
const sessionActor = await resolveSessionActor2(req, myAgentId);
|
|
23048
|
+
const steerAccess = await sessionAccessFor2(sessionActor, task.id);
|
|
23049
|
+
if (!hasSessionAccess(steerAccess, "write")) {
|
|
23050
|
+
jsonError(res, SESSION_WRITE_DENIED2, 403);
|
|
23051
|
+
return true;
|
|
23052
|
+
}
|
|
23053
|
+
if (!await canSteerTask(req, myAgentId, task, steerAccess)) {
|
|
22549
23054
|
jsonError(res, "Forbidden: caller cannot steer this task", 403);
|
|
22550
23055
|
return true;
|
|
22551
23056
|
}
|
|
@@ -22585,6 +23090,10 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
22585
23090
|
jsonError(res, "Task not found", 404);
|
|
22586
23091
|
return true;
|
|
22587
23092
|
}
|
|
23093
|
+
if (!await canReadSession2(req, myAgentId, parsed.params.id)) {
|
|
23094
|
+
jsonError(res, SESSION_READ_DENIED2, 403);
|
|
23095
|
+
return true;
|
|
23096
|
+
}
|
|
22588
23097
|
getTaskSteeringMessagesRoute.respond(res, 200, {
|
|
22589
23098
|
messages: await getSteeringMessagesForTask2(parsed.params.id)
|
|
22590
23099
|
});
|
|
@@ -22603,13 +23112,18 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
22603
23112
|
return true;
|
|
22604
23113
|
}
|
|
22605
23114
|
if (parsed.query.taskId) {
|
|
23115
|
+
if (!await canReadSession2(req, myAgentId, parsed.query.taskId)) {
|
|
23116
|
+
jsonError(res, SESSION_READ_DENIED2, 403);
|
|
23117
|
+
return true;
|
|
23118
|
+
}
|
|
22606
23119
|
const task = await getTaskById2(parsed.query.taskId);
|
|
22607
23120
|
const messages = task?.agentId === myAgentId ? await getPendingSteeringForTask2(parsed.query.taskId) : [];
|
|
22608
23121
|
getPendingSteeringMessagesRoute.respond(res, 200, { messages });
|
|
22609
23122
|
return true;
|
|
22610
23123
|
}
|
|
23124
|
+
const actor = await resolveSessionActor2(req, myAgentId);
|
|
22611
23125
|
getPendingSteeringMessagesRoute.respond(res, 200, {
|
|
22612
|
-
messages: await getPendingSteeringForAgent2(myAgentId)
|
|
23126
|
+
messages: await getPendingSteeringForAgent2(myAgentId, readableByFor2(actor))
|
|
22613
23127
|
});
|
|
22614
23128
|
return true;
|
|
22615
23129
|
}
|
|
@@ -22750,6 +23264,11 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
22750
23264
|
jsonError(res, "Task not found", 404);
|
|
22751
23265
|
return true;
|
|
22752
23266
|
}
|
|
23267
|
+
const readAccess = await sessionAccessFor2(await resolveSessionActor2(req, myAgentId), task.id);
|
|
23268
|
+
if (!hasSessionAccess(readAccess, "read")) {
|
|
23269
|
+
jsonError(res, "You don't have access to this session. Ask the owner to share it.", 403);
|
|
23270
|
+
return true;
|
|
23271
|
+
}
|
|
22753
23272
|
const logs = await getLogsByTaskId2(parsed.params.id, parsed.query.logsLimit ?? 200);
|
|
22754
23273
|
const attachments = await getTaskAttachments2(parsed.params.id);
|
|
22755
23274
|
getTask.respond(res, 200, {
|
|
@@ -22901,7 +23420,7 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
22901
23420
|
jsonError(res, "Missing X-Agent-ID header", 400);
|
|
22902
23421
|
return true;
|
|
22903
23422
|
}
|
|
22904
|
-
const pausedTasks = await getPausedTasksForAgent2(myAgentId);
|
|
23423
|
+
const pausedTasks = await getPausedTasksForAgent2(myAgentId, readableByFor2(await resolveSessionActor2(req, myAgentId)));
|
|
22905
23424
|
const tasks = await Promise.all(pausedTasks.map(async (task) => {
|
|
22906
23425
|
const requestedBy = await buildTaskRequestedBy(task);
|
|
22907
23426
|
return {
|
|
@@ -26764,6 +27283,7 @@ ${templateBody}` : templateBody;
|
|
|
26764
27283
|
|
|
26765
27284
|
// src/http/realtime.ts
|
|
26766
27285
|
init_zod();
|
|
27286
|
+
init_users();
|
|
26767
27287
|
init_rbac();
|
|
26768
27288
|
|
|
26769
27289
|
// src/realtime/browser.generated.txt
|
|
@@ -26777,14 +27297,14 @@ var realtimeTicketRoute = route({
|
|
|
26777
27297
|
pattern: ["api", "realtime", "ticket"],
|
|
26778
27298
|
summary: "Issue a one-shot dashboard realtime ticket",
|
|
26779
27299
|
tags: ["Rooms"],
|
|
26780
|
-
|
|
27300
|
+
description: "The ticket authorizes one dashboard socket for presence rooms: Comb drives (`comb.presence`) and sessions (`session.presence`). An operator-key tab's picked user (`X-Swarm-User-Id`) rides on the ticket so session presence can check that user's access.",
|
|
27301
|
+
rbac: { permission: "session.presence", alsoAdmits: ["comb.presence"] },
|
|
26781
27302
|
responses: {
|
|
26782
27303
|
200: {
|
|
26783
27304
|
description: "One-shot realtime ticket",
|
|
26784
27305
|
schema: object2({ ticket: string2(), expiresAt: number2().int() })
|
|
26785
27306
|
},
|
|
26786
|
-
403: { description: "
|
|
26787
|
-
404: { description: "Comb is not enabled" }
|
|
27307
|
+
403: { description: "Presence is not allowed" }
|
|
26788
27308
|
}
|
|
26789
27309
|
});
|
|
26790
27310
|
route({
|
|
@@ -26801,29 +27321,28 @@ route({
|
|
|
26801
27321
|
async function handleRealtimeTicket(req, res, pathSegments, queryParams) {
|
|
26802
27322
|
if (!realtimeTicketRoute.match(req.method, pathSegments))
|
|
26803
27323
|
return false;
|
|
26804
|
-
if (!getCombConfig().enabled) {
|
|
26805
|
-
jsonError(res, "Comb is not enabled", 404);
|
|
26806
|
-
return true;
|
|
26807
|
-
}
|
|
26808
27324
|
const parsed = await realtimeTicketRoute.parse(req, res, pathSegments, queryParams);
|
|
26809
27325
|
if (!parsed)
|
|
26810
27326
|
return true;
|
|
26811
27327
|
const auth = getRequestAuth(req);
|
|
26812
27328
|
if (!auth || auth.kind === "agent" || auth.kind === "guest" || auth.kind === "controller") {
|
|
26813
|
-
jsonError(res, "
|
|
27329
|
+
jsonError(res, "Presence requires dashboard authentication", 403);
|
|
26814
27330
|
return true;
|
|
26815
27331
|
}
|
|
26816
27332
|
const principal = auth.kind === "operator" ? { kind: "operator" } : { kind: "user", userId: auth.userId };
|
|
26817
|
-
|
|
26818
|
-
|
|
26819
|
-
|
|
26820
|
-
resource: { kind: "none" },
|
|
26821
|
-
source: "http"
|
|
26822
|
-
}).allow) {
|
|
26823
|
-
jsonError(res, "Comb presence requires the comb.presence permission", 403);
|
|
27333
|
+
const allowed = (verb) => can({ principal, verb, resource: { kind: "none" }, source: "http" }).allow;
|
|
27334
|
+
if (!allowed("comb.presence") && !allowed("session.presence")) {
|
|
27335
|
+
jsonError(res, "Presence requires the comb.presence or session.presence permission", 403);
|
|
26824
27336
|
return true;
|
|
26825
27337
|
}
|
|
26826
|
-
|
|
27338
|
+
let dashboardUserId;
|
|
27339
|
+
if (auth.kind === "operator" && !auth.page) {
|
|
27340
|
+
const picked = dashboardUserIdHeader(req);
|
|
27341
|
+
const user = picked ? await findUserById(picked) : null;
|
|
27342
|
+
if (user?.status === "active")
|
|
27343
|
+
dashboardUserId = user.id;
|
|
27344
|
+
}
|
|
27345
|
+
realtimeTicketRoute.respond(res, 200, issueRealtimeTicket(dashboardUserId ? { ...auth, dashboardUserId } : auth));
|
|
26827
27346
|
return true;
|
|
26828
27347
|
}
|
|
26829
27348
|
async function handleRealtimeAsset(req, res) {
|
|
@@ -30712,6 +31231,7 @@ var agentTaskRoute = route({
|
|
|
30712
31231
|
method: "post",
|
|
30713
31232
|
path: "/api/internal/script-runs/{runId}/agent-task",
|
|
30714
31233
|
pattern: ["api", "internal", "script-runs", null, "agent-task"],
|
|
31234
|
+
session: bodyTask("parentTaskId"),
|
|
30715
31235
|
operationId: "script_runs_internal_agent_task",
|
|
30716
31236
|
summary: "Create or wait for a script workflow agent task step",
|
|
30717
31237
|
tags: ["Script Runs"],
|
|
@@ -31191,6 +31711,9 @@ var upsertRoute2 = route({
|
|
|
31191
31711
|
method: "post",
|
|
31192
31712
|
path: "/api/scripts/upsert",
|
|
31193
31713
|
pattern: ["api", "scripts", "upsert"],
|
|
31714
|
+
auth: {
|
|
31715
|
+
userNamesAgent: "dashboard script playground: a user runs or saves a script in a chosen agent's scope"
|
|
31716
|
+
},
|
|
31194
31717
|
operationId: "scripts_upsert",
|
|
31195
31718
|
summary: "Create or update a reusable script",
|
|
31196
31719
|
description: "Explicit script upserts run a TypeScript typecheck before writing.",
|
|
@@ -31215,6 +31738,9 @@ var runRoute = route({
|
|
|
31215
31738
|
method: "post",
|
|
31216
31739
|
path: "/api/scripts/run",
|
|
31217
31740
|
pattern: ["api", "scripts", "run"],
|
|
31741
|
+
auth: {
|
|
31742
|
+
userNamesAgent: "dashboard script playground: a user runs or saves a script in a chosen agent's scope"
|
|
31743
|
+
},
|
|
31218
31744
|
operationId: "scripts_run",
|
|
31219
31745
|
summary: "Run a reusable or inline script",
|
|
31220
31746
|
description: "Inline source skips typecheck and is auto-saved as a scratch script only on success.",
|
|
@@ -32185,6 +32711,7 @@ var createSessionLogsRoute = route({
|
|
|
32185
32711
|
method: "post",
|
|
32186
32712
|
path: "/api/session-logs",
|
|
32187
32713
|
pattern: ["api", "session-logs"],
|
|
32714
|
+
session: bodyTask("taskId"),
|
|
32188
32715
|
summary: "Store session logs",
|
|
32189
32716
|
tags: ["Session Data"],
|
|
32190
32717
|
body: object2({
|
|
@@ -32206,6 +32733,7 @@ var getSessionLogsByTask = route({
|
|
|
32206
32733
|
method: "get",
|
|
32207
32734
|
path: "/api/tasks/{taskId}/session-logs",
|
|
32208
32735
|
pattern: ["api", "tasks", null, "session-logs"],
|
|
32736
|
+
session: { taskParam: "taskId" },
|
|
32209
32737
|
summary: "Get session logs for a task",
|
|
32210
32738
|
tags: ["Session Data"],
|
|
32211
32739
|
params: object2({ taskId: string2() }),
|
|
@@ -32224,6 +32752,7 @@ var createSessionCostRoute = route({
|
|
|
32224
32752
|
method: "post",
|
|
32225
32753
|
path: "/api/session-costs",
|
|
32226
32754
|
pattern: ["api", "session-costs"],
|
|
32755
|
+
session: bodyTask("taskId"),
|
|
32227
32756
|
summary: "Store session cost record",
|
|
32228
32757
|
tags: ["Session Data"],
|
|
32229
32758
|
body: object2({
|
|
@@ -32322,7 +32851,7 @@ var listSessionCosts = route({
|
|
|
32322
32851
|
}
|
|
32323
32852
|
}
|
|
32324
32853
|
});
|
|
32325
|
-
async function handleSessionData(req, res, pathSegments, queryParams,
|
|
32854
|
+
async function handleSessionData(req, res, pathSegments, queryParams, myAgentId) {
|
|
32326
32855
|
if (createSessionLogsRoute.match(req.method, pathSegments)) {
|
|
32327
32856
|
const parsed = await createSessionLogsRoute.parse(req, res, pathSegments, queryParams);
|
|
32328
32857
|
if (!parsed)
|
|
@@ -32361,6 +32890,10 @@ async function handleSessionData(req, res, pathSegments, queryParams, _myAgentId
|
|
|
32361
32890
|
jsonError(res, "Task not found", 404);
|
|
32362
32891
|
return true;
|
|
32363
32892
|
}
|
|
32893
|
+
if (!await canReadSession2(req, myAgentId, task.id)) {
|
|
32894
|
+
jsonError(res, SESSION_READ_DENIED2, 403);
|
|
32895
|
+
return true;
|
|
32896
|
+
}
|
|
32364
32897
|
const logs = await getSessionLogsByTaskId2(parsed.params.taskId, parsed.query?.limit);
|
|
32365
32898
|
getSessionLogsByTask.respond(res, 200, { logs });
|
|
32366
32899
|
return true;
|
|
@@ -32507,6 +33040,8 @@ init_zod();
|
|
|
32507
33040
|
init_db3();
|
|
32508
33041
|
init_steering();
|
|
32509
33042
|
init_users();
|
|
33043
|
+
init_rbac();
|
|
33044
|
+
init_bus();
|
|
32510
33045
|
init_types();
|
|
32511
33046
|
init_request_auth_context();
|
|
32512
33047
|
init_runtime_pools_flag();
|
|
@@ -32555,6 +33090,9 @@ var mintSessionTokenRoute = route({
|
|
|
32555
33090
|
method: "post",
|
|
32556
33091
|
path: "/api/sessions/tokens",
|
|
32557
33092
|
pattern: ["api", "sessions", "tokens"],
|
|
33093
|
+
session: {
|
|
33094
|
+
none: "user tokens cannot mint: the handler admits only the operator key or the task's own runtime"
|
|
33095
|
+
},
|
|
32558
33096
|
summary: "Mint an ephemeral task or runtime token",
|
|
32559
33097
|
description: "Operators can mint task or runtime tokens. Runtime tokens can mint task tokens for their own assigned tasks. Child tokens expire with their parent runtime.",
|
|
32560
33098
|
tags: ["Sessions"],
|
|
@@ -32617,7 +33155,8 @@ var listSessions = route({
|
|
|
32617
33155
|
q: string2().optional(),
|
|
32618
33156
|
requestedByUserId: string2().min(1).optional(),
|
|
32619
33157
|
contextKeyPrefix: string2().min(1).optional(),
|
|
32620
|
-
fields: _enum2(["full", "slim"]).optional()
|
|
33158
|
+
fields: _enum2(["full", "slim"]).optional(),
|
|
33159
|
+
scope: _enum2(["mine", "shared"]).optional()
|
|
32621
33160
|
}),
|
|
32622
33161
|
responses: {
|
|
32623
33162
|
200: {
|
|
@@ -32637,23 +33176,68 @@ var getSession = route({
|
|
|
32637
33176
|
method: "get",
|
|
32638
33177
|
path: "/api/sessions/{rootTaskId}",
|
|
32639
33178
|
pattern: ["api", "sessions", null],
|
|
33179
|
+
session: { taskParam: "rootTaskId" },
|
|
32640
33180
|
summary: "Get a session — root task + the entire descendant chain",
|
|
32641
33181
|
tags: ["Sessions"],
|
|
32642
33182
|
params: object2({ rootTaskId: string2() }),
|
|
32643
33183
|
responses: {
|
|
32644
33184
|
200: {
|
|
32645
|
-
description: "Root task + chain (ordered by createdAt)",
|
|
33185
|
+
description: "Root task + chain (ordered by createdAt), plus the caller's `access` and the session `owner`",
|
|
32646
33186
|
schema: object2({
|
|
32647
33187
|
root: TaskWithSteeringSchema,
|
|
32648
|
-
chain: array2(TaskWithSteeringSchema)
|
|
33188
|
+
chain: array2(TaskWithSteeringSchema),
|
|
33189
|
+
access: SessionAccessSchema,
|
|
33190
|
+
owner: SessionOwnerSchema.nullable()
|
|
32649
33191
|
})
|
|
32650
33192
|
},
|
|
32651
33193
|
401: { description: "Unauthorized" },
|
|
33194
|
+
403: { description: "The caller has no access to this session" },
|
|
33195
|
+
404: { description: "Root task not found" }
|
|
33196
|
+
},
|
|
33197
|
+
auth: { apiKey: true }
|
|
33198
|
+
});
|
|
33199
|
+
var getSessionShares = route({
|
|
33200
|
+
method: "get",
|
|
33201
|
+
path: "/api/sessions/{rootTaskId}/shares",
|
|
33202
|
+
pattern: ["api", "sessions", null, "shares"],
|
|
33203
|
+
session: { taskParam: "rootTaskId" },
|
|
33204
|
+
summary: "List a session's active shares",
|
|
33205
|
+
description: "Active shares only. `everyone` is the access every user has; `users` are per-person shares. A session with no shares is private to its owner.",
|
|
33206
|
+
tags: ["Sessions"],
|
|
33207
|
+
params: object2({ rootTaskId: string2() }),
|
|
33208
|
+
responses: {
|
|
33209
|
+
200: { description: "Active shares", schema: SessionSharesSchema },
|
|
33210
|
+
401: { description: "Unauthorized" },
|
|
33211
|
+
403: { description: "The caller has no access to this session" },
|
|
32652
33212
|
404: { description: "Root task not found" }
|
|
32653
33213
|
},
|
|
32654
33214
|
auth: { apiKey: true }
|
|
32655
33215
|
});
|
|
32656
|
-
|
|
33216
|
+
var putSessionShares = route({
|
|
33217
|
+
method: "put",
|
|
33218
|
+
path: "/api/sessions/{rootTaskId}/shares",
|
|
33219
|
+
pattern: ["api", "sessions", null, "shares"],
|
|
33220
|
+
session: { taskParam: "rootTaskId" },
|
|
33221
|
+
summary: "Set who can view or edit a session",
|
|
33222
|
+
description: "Owner only. The body is the full desired state: users left out lose access. Changed targets have their active share revoked and a new one inserted in one transaction; shares are never deleted.",
|
|
33223
|
+
tags: ["Sessions"],
|
|
33224
|
+
params: object2({ rootTaskId: string2() }),
|
|
33225
|
+
body: SessionSharesUpdateSchema,
|
|
33226
|
+
responses: {
|
|
33227
|
+
200: { description: "Active shares after the change", schema: SessionSharesSchema },
|
|
33228
|
+
400: { description: "Validation error or unknown user" },
|
|
33229
|
+
401: { description: "Unauthorized" },
|
|
33230
|
+
403: { description: "Only the session owner can share it" },
|
|
33231
|
+
404: { description: "Root task not found" },
|
|
33232
|
+
409: { description: "The session has no owner, so it is already open to everyone" }
|
|
33233
|
+
},
|
|
33234
|
+
auth: { apiKey: true },
|
|
33235
|
+
rbac: { permission: "session.share" }
|
|
33236
|
+
});
|
|
33237
|
+
async function accessForRoot(actor, root) {
|
|
33238
|
+
return actor.kind === "bypass" ? "owner" : sessionAccessForRoot(actor.userId, root);
|
|
33239
|
+
}
|
|
33240
|
+
async function handleSessions(req, res, pathSegments, queryParams, myAgentId) {
|
|
32657
33241
|
if (mintSessionTokenRoute.match(req.method, pathSegments)) {
|
|
32658
33242
|
const auth = getRequestAuth(req);
|
|
32659
33243
|
if (auth?.kind !== "operator" && !(auth?.kind === "agent" && auth.runtimeId)) {
|
|
@@ -32730,21 +33314,30 @@ async function handleSessions(req, res, pathSegments, queryParams) {
|
|
|
32730
33314
|
if (!parsed)
|
|
32731
33315
|
return true;
|
|
32732
33316
|
const sources = parsed.query.source ? parsed.query.source.split(",").map((s) => s.trim()).filter(Boolean) : undefined;
|
|
32733
|
-
const
|
|
32734
|
-
|
|
32735
|
-
|
|
33317
|
+
const actor = await resolveSessionActor2(req, myAgentId);
|
|
33318
|
+
if (actor.kind === "bypass" && parsed.query.scope) {
|
|
33319
|
+
listSessions.respond(res, 200, {
|
|
33320
|
+
sessions: [],
|
|
33321
|
+
total: 0,
|
|
33322
|
+
limit: parsed.query.limit ?? 25,
|
|
33323
|
+
offset: parsed.query.offset ?? 0
|
|
33324
|
+
});
|
|
33325
|
+
return true;
|
|
33326
|
+
}
|
|
33327
|
+
const filterOpts = {
|
|
32736
33328
|
source: sources,
|
|
32737
33329
|
q: parsed.query.q,
|
|
32738
33330
|
requestedByUserId: parsed.query.requestedByUserId,
|
|
32739
|
-
contextKeyPrefix: parsed.query.contextKeyPrefix
|
|
33331
|
+
contextKeyPrefix: parsed.query.contextKeyPrefix,
|
|
33332
|
+
...actor.kind === "user" ? { viewer: { userId: actor.userId, scope: parsed.query.scope ?? "all" } } : {}
|
|
33333
|
+
};
|
|
33334
|
+
const baseOpts = {
|
|
33335
|
+
...filterOpts,
|
|
33336
|
+
limit: parsed.query.limit,
|
|
33337
|
+
offset: parsed.query.offset
|
|
32740
33338
|
};
|
|
32741
33339
|
const sessions = parsed.query.fields === "full" ? await listRecentSessions2(baseOpts) : await listRecentSessions2({ ...baseOpts, slim: true });
|
|
32742
|
-
const total = await countSessions2(
|
|
32743
|
-
source: sources,
|
|
32744
|
-
q: parsed.query.q,
|
|
32745
|
-
requestedByUserId: parsed.query.requestedByUserId,
|
|
32746
|
-
contextKeyPrefix: parsed.query.contextKeyPrefix
|
|
32747
|
-
});
|
|
33340
|
+
const total = await countSessions2(filterOpts);
|
|
32748
33341
|
listSessions.respond(res, 200, {
|
|
32749
33342
|
sessions,
|
|
32750
33343
|
total,
|
|
@@ -32762,6 +33355,20 @@ async function handleSessions(req, res, pathSegments, queryParams) {
|
|
|
32762
33355
|
jsonError(res, "Root task not found", 404);
|
|
32763
33356
|
return true;
|
|
32764
33357
|
}
|
|
33358
|
+
const sessionRoot = await getSessionRoot(root.id) ?? {
|
|
33359
|
+
id: root.id,
|
|
33360
|
+
requestedByUserId: root.requestedByUserId ?? null
|
|
33361
|
+
};
|
|
33362
|
+
const access = await accessForRoot(await resolveSessionActor2(req, myAgentId), sessionRoot);
|
|
33363
|
+
if (access === "none") {
|
|
33364
|
+
jsonError(res, "You don't have access to this session. Ask the owner to share it.", 403);
|
|
33365
|
+
return true;
|
|
33366
|
+
}
|
|
33367
|
+
const ownerUser = sessionRoot.requestedByUserId ? await findUserById(sessionRoot.requestedByUserId) : null;
|
|
33368
|
+
const owner = sessionRoot.requestedByUserId ? {
|
|
33369
|
+
id: sessionRoot.requestedByUserId,
|
|
33370
|
+
name: ownerUser?.name ?? sessionRoot.requestedByUserId
|
|
33371
|
+
} : null;
|
|
32765
33372
|
const chain = await getRootTaskChain2(parsed.params.rootTaskId);
|
|
32766
33373
|
const tasks = [root, ...chain];
|
|
32767
33374
|
const [steering, citations] = await Promise.all([
|
|
@@ -32773,7 +33380,69 @@ async function handleSessions(req, res, pathSegments, queryParams) {
|
|
|
32773
33380
|
...steering.get(task.id),
|
|
32774
33381
|
citations: citations.get(task.id)
|
|
32775
33382
|
});
|
|
32776
|
-
getSession.respond(res, 200, {
|
|
33383
|
+
getSession.respond(res, 200, {
|
|
33384
|
+
root: decorate(root),
|
|
33385
|
+
chain: chain.map(decorate),
|
|
33386
|
+
access,
|
|
33387
|
+
owner
|
|
33388
|
+
});
|
|
33389
|
+
return true;
|
|
33390
|
+
}
|
|
33391
|
+
if (getSessionShares.match(req.method, pathSegments)) {
|
|
33392
|
+
const parsed = await getSessionShares.parse(req, res, pathSegments, queryParams);
|
|
33393
|
+
if (!parsed)
|
|
33394
|
+
return true;
|
|
33395
|
+
const root = await getSessionRoot(parsed.params.rootTaskId);
|
|
33396
|
+
if (!root) {
|
|
33397
|
+
jsonError(res, "Root task not found", 404);
|
|
33398
|
+
return true;
|
|
33399
|
+
}
|
|
33400
|
+
const access = await accessForRoot(await resolveSessionActor2(req, myAgentId), root);
|
|
33401
|
+
if (access === "none") {
|
|
33402
|
+
jsonError(res, "You don't have access to this session. Ask the owner to share it.", 403);
|
|
33403
|
+
return true;
|
|
33404
|
+
}
|
|
33405
|
+
getSessionShares.respond(res, 200, toSessionSharesState(await listActiveSessionShares(root.id)));
|
|
33406
|
+
return true;
|
|
33407
|
+
}
|
|
33408
|
+
if (putSessionShares.match(req.method, pathSegments)) {
|
|
33409
|
+
const parsed = await putSessionShares.parse(req, res, pathSegments, queryParams);
|
|
33410
|
+
if (!parsed)
|
|
33411
|
+
return true;
|
|
33412
|
+
const auth = getRequestAuth(req);
|
|
33413
|
+
const actor = await resolveSessionActor2(req, myAgentId);
|
|
33414
|
+
const principal = auth?.kind === "user" ? { kind: "user", userId: auth.userId } : auth?.kind === "operator" && !myAgentId ? { kind: "operator" } : null;
|
|
33415
|
+
if (!principal || !can({ principal, verb: "session.share", resource: { kind: "none" }, source: "http" }).allow) {
|
|
33416
|
+
jsonError(res, "Only the session owner can share it", 403);
|
|
33417
|
+
return true;
|
|
33418
|
+
}
|
|
33419
|
+
const root = await getSessionRoot(parsed.params.rootTaskId);
|
|
33420
|
+
if (!root) {
|
|
33421
|
+
jsonError(res, "Root task not found", 404);
|
|
33422
|
+
return true;
|
|
33423
|
+
}
|
|
33424
|
+
if (!root.requestedByUserId) {
|
|
33425
|
+
jsonError(res, "This session has no owner, so it is already open to everyone", 409);
|
|
33426
|
+
return true;
|
|
33427
|
+
}
|
|
33428
|
+
if (actor.kind === "user" && actor.userId !== root.requestedByUserId) {
|
|
33429
|
+
jsonError(res, "Only the session owner can share it", 403);
|
|
33430
|
+
return true;
|
|
33431
|
+
}
|
|
33432
|
+
const userIds = [...new Set(parsed.body.users.map((user) => user.userId))];
|
|
33433
|
+
if (userIds.length !== parsed.body.users.length) {
|
|
33434
|
+
jsonError(res, "Each user may appear only once", 400);
|
|
33435
|
+
return true;
|
|
33436
|
+
}
|
|
33437
|
+
for (const userId of userIds) {
|
|
33438
|
+
if (!await findUserById(userId)) {
|
|
33439
|
+
jsonError(res, `Unknown user ${userId}`, 400);
|
|
33440
|
+
return true;
|
|
33441
|
+
}
|
|
33442
|
+
}
|
|
33443
|
+
const state = await setSessionShares(root, parsed.body, actor.kind === "user" ? actor.userId : null);
|
|
33444
|
+
realtimeBus.publish(sessionAccessTopic(root.id), null);
|
|
33445
|
+
putSessionShares.respond(res, 200, state);
|
|
32777
33446
|
return true;
|
|
32778
33447
|
}
|
|
32779
33448
|
return false;
|
|
@@ -34175,11 +34844,12 @@ async function handleStats(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
34175
34844
|
return true;
|
|
34176
34845
|
const limit = parsed.query.limit ?? 100;
|
|
34177
34846
|
const agentId = parsed.query.agentId;
|
|
34847
|
+
const readableBy = readableByFor2(await resolveSessionActor2(req, myAgentId));
|
|
34178
34848
|
let logs = [];
|
|
34179
34849
|
if (agentId) {
|
|
34180
|
-
logs = (await getLogsByAgentId2(agentId)).slice(0, limit);
|
|
34850
|
+
logs = (await getLogsByAgentId2(agentId, readableBy)).slice(0, limit);
|
|
34181
34851
|
} else {
|
|
34182
|
-
logs = await getAllLogs2(limit);
|
|
34852
|
+
logs = await getAllLogs2(limit, readableBy);
|
|
34183
34853
|
}
|
|
34184
34854
|
listLogs.respond(res, 200, { logs });
|
|
34185
34855
|
return true;
|
|
@@ -34235,7 +34905,7 @@ async function handleStats(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
34235
34905
|
return true;
|
|
34236
34906
|
}
|
|
34237
34907
|
if (getConcurrentContextRoute.match(req.method, pathSegments)) {
|
|
34238
|
-
const context = await getConcurrentContext2();
|
|
34908
|
+
const context = await getConcurrentContext2(readableByFor2(await resolveSessionActor2(req, myAgentId)));
|
|
34239
34909
|
getConcurrentContextRoute.respond(res, 200, context);
|
|
34240
34910
|
return true;
|
|
34241
34911
|
}
|
|
@@ -38306,10 +38976,11 @@ var httpServer = createHttpServer(async (req, res) => {
|
|
|
38306
38976
|
() => handleExtensions(req, res, pathSegments, queryParams, myAgentId),
|
|
38307
38977
|
() => handleFavorites(req, res, pathSegments, queryParams, myAgentId),
|
|
38308
38978
|
() => handleUsers(req, res, pathSegments, queryParams),
|
|
38309
|
-
() => handleSessions(req, res, pathSegments, queryParams),
|
|
38979
|
+
() => handleSessions(req, res, pathSegments, queryParams, myAgentId),
|
|
38310
38980
|
() => handleInboxState(req, res, pathSegments, queryParams),
|
|
38311
38981
|
() => handleTelemetryUi(req, res, pathSegments, queryParams, myAgentId),
|
|
38312
38982
|
() => handleUsedBy(req, res, pathSegments, queryParams, myAgentId),
|
|
38983
|
+
() => handleRanking(req, res, pathSegments, queryParams),
|
|
38313
38984
|
() => handleFleetBenchmark(req, res, pathSegments, queryParams),
|
|
38314
38985
|
() => handleTaskTemplates(req, res, pathSegments, queryParams),
|
|
38315
38986
|
() => handleMcp(req, res, transports, transportActivity, mcpSessionAgents),
|
|
@@ -38341,7 +39012,7 @@ var httpServer = createHttpServer(async (req, res) => {
|
|
|
38341
39012
|
});
|
|
38342
39013
|
});
|
|
38343
39014
|
var detachRealtime = attachRealtimeTransport(httpServer);
|
|
38344
|
-
var { attachControllerTransport } = await import("./controllers-connect-
|
|
39015
|
+
var { attachControllerTransport } = await import("./controllers-connect-gkxmqw9d.js");
|
|
38345
39016
|
var detachControllers = attachControllerTransport(httpServer);
|
|
38346
39017
|
var stopWorkerNudges = startWorkerNudgeListener();
|
|
38347
39018
|
var stopRoomDeletion = realtimeBus.subscribe("room:namespace-deleted", (namespace) => {
|
|
@@ -38370,7 +39041,7 @@ async function shutdown() {
|
|
|
38370
39041
|
sessionsProcessed: getServerSessionsProcessed()
|
|
38371
39042
|
});
|
|
38372
39043
|
if (hasCapability("scheduling")) {
|
|
38373
|
-
const { stopScheduler } = await import("./index-
|
|
39044
|
+
const { stopScheduler } = await import("./index-8pwc1jc8.js");
|
|
38374
39045
|
stopScheduler();
|
|
38375
39046
|
}
|
|
38376
39047
|
stopHeartbeat2();
|
|
@@ -38382,7 +39053,7 @@ async function shutdown() {
|
|
|
38382
39053
|
await stopSlackApp2();
|
|
38383
39054
|
await stopChatSdk();
|
|
38384
39055
|
if (process.env.OAUTH_KEEPALIVE_DISABLE !== "true") {
|
|
38385
|
-
const { stopOAuthKeepalive } = await import("./keepalive-
|
|
39056
|
+
const { stopOAuthKeepalive } = await import("./keepalive-3nr4st3r.js");
|
|
38386
39057
|
await stopOAuthKeepalive();
|
|
38387
39058
|
}
|
|
38388
39059
|
stopOAuthPendingGc();
|
|
@@ -38462,21 +39133,21 @@ try {
|
|
|
38462
39133
|
console.warn("[startup] CAPABILITIES upgrade seed failed (non-fatal):", err);
|
|
38463
39134
|
}
|
|
38464
39135
|
try {
|
|
38465
|
-
const { ensurePricingSeeded } = await import("./boot-seeds-
|
|
39136
|
+
const { ensurePricingSeeded } = await import("./boot-seeds-xs9a8dx1.js");
|
|
38466
39137
|
ensurePricingSeeded();
|
|
38467
|
-
const { startPricingRefreshLoop } = await import("./pricing-refresh-
|
|
39138
|
+
const { startPricingRefreshLoop } = await import("./pricing-refresh-5nvme3sm.js");
|
|
38468
39139
|
startPricingRefreshLoop();
|
|
38469
39140
|
} catch (err) {
|
|
38470
39141
|
console.error("[startup] Failed to seed pricing rows:", err);
|
|
38471
39142
|
}
|
|
38472
39143
|
try {
|
|
38473
|
-
const { ensureModelProfilesSeeded } = await import("./boot-seeds-
|
|
39144
|
+
const { ensureModelProfilesSeeded } = await import("./boot-seeds-xs9a8dx1.js");
|
|
38474
39145
|
await ensureModelProfilesSeeded();
|
|
38475
39146
|
} catch (err) {
|
|
38476
39147
|
console.error("[startup] Failed to seed model profiles:", err);
|
|
38477
39148
|
}
|
|
38478
39149
|
try {
|
|
38479
|
-
const { ensureRbacSeeded } = await import("./boot-seeds-
|
|
39150
|
+
const { ensureRbacSeeded } = await import("./boot-seeds-xs9a8dx1.js");
|
|
38480
39151
|
ensureRbacSeeded();
|
|
38481
39152
|
} catch (err) {
|
|
38482
39153
|
console.error("[startup] Failed to sync RBAC seed rows:", err);
|
|
@@ -38484,7 +39155,7 @@ try {
|
|
|
38484
39155
|
throw err;
|
|
38485
39156
|
}
|
|
38486
39157
|
try {
|
|
38487
|
-
const { runAllSeeders } = await import("./index-
|
|
39158
|
+
const { runAllSeeders } = await import("./index-s7ckdajq.js");
|
|
38488
39159
|
await runAllSeeders({ scriptEmbeddingMode: "skip" });
|
|
38489
39160
|
} catch (err) {
|
|
38490
39161
|
console.error("[startup] Failed to seed built-in entities:", err);
|
|
@@ -38546,39 +39217,39 @@ httpServer.listen(port, async () => {
|
|
|
38546
39217
|
await initWorkflows2();
|
|
38547
39218
|
await startScriptRunSupervisor(getMcpBaseUrl());
|
|
38548
39219
|
if (hasCapability("scheduling")) {
|
|
38549
|
-
const { startScheduler } = await import("./index-
|
|
38550
|
-
const { getExecutorRegistry } = await import("./index-
|
|
39220
|
+
const { startScheduler } = await import("./index-8pwc1jc8.js");
|
|
39221
|
+
const { getExecutorRegistry } = await import("./index-dg2tmj99.js");
|
|
38551
39222
|
const intervalMs = Number(process.env.SCHEDULER_INTERVAL_MS) || 1e4;
|
|
38552
39223
|
startScheduler(getExecutorRegistry(), intervalMs, {
|
|
38553
39224
|
runId: globalState.__runId
|
|
38554
39225
|
});
|
|
38555
39226
|
}
|
|
38556
39227
|
if (!isEnvFlagEnabled("HEARTBEAT_DISABLE", false)) {
|
|
38557
|
-
const { startHeartbeat } = await import("./index-
|
|
39228
|
+
const { startHeartbeat } = await import("./index-nxa6me0q.js");
|
|
38558
39229
|
const heartbeatMs = Number(process.env.HEARTBEAT_INTERVAL_MS) || 90000;
|
|
38559
39230
|
startHeartbeat(heartbeatMs);
|
|
38560
39231
|
}
|
|
38561
39232
|
startPoolRouting();
|
|
38562
39233
|
startRuntimeSupervisor();
|
|
38563
39234
|
if (process.env.OAUTH_KEEPALIVE_DISABLE !== "true") {
|
|
38564
|
-
const { startOAuthKeepalive } = await import("./keepalive-
|
|
39235
|
+
const { startOAuthKeepalive } = await import("./keepalive-3nr4st3r.js");
|
|
38565
39236
|
startOAuthKeepalive();
|
|
38566
39237
|
}
|
|
38567
39238
|
if (process.env.OAUTH_REFRESH_SWEEP_DISABLE !== "true") {
|
|
38568
|
-
const { startOAuthRefreshSweep } = await import("./oauth-refresh-sweep-
|
|
39239
|
+
const { startOAuthRefreshSweep } = await import("./oauth-refresh-sweep-9q2t0y3q.js");
|
|
38569
39240
|
startOAuthRefreshSweep();
|
|
38570
39241
|
}
|
|
38571
39242
|
startOAuthPendingGc();
|
|
38572
39243
|
await startMemoryGc();
|
|
38573
39244
|
await startDbRetention();
|
|
38574
39245
|
startHumanFreeDrain();
|
|
38575
|
-
import("./boot-reembed-
|
|
39246
|
+
import("./boot-reembed-qr8a894z.js").then(({ runBootReembed }) => runBootReembed()).catch((err) => {
|
|
38576
39247
|
console.error("[boot-reembed] startup backfill failed (non-fatal):", err);
|
|
38577
39248
|
});
|
|
38578
|
-
import("./boot-reembed-
|
|
39249
|
+
import("./boot-reembed-rj85jjhw.js").then(({ runBootReembedScripts }) => runBootReembedScripts()).catch((err) => {
|
|
38579
39250
|
console.error("[boot-reembed-scripts] startup backfill failed (non-fatal):", err);
|
|
38580
39251
|
});
|
|
38581
|
-
import("./boot-scrub-sweep-
|
|
39252
|
+
import("./boot-scrub-sweep-eyfvn6v9.js").then(({ runBootScrubSweep }) => runBootScrubSweep()).catch((err) => {
|
|
38582
39253
|
console.error("[boot-scrub-sweep] startup scrub failed (non-fatal):", err);
|
|
38583
39254
|
});
|
|
38584
39255
|
}).on("error", (err) => {
|