@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
package/src/realtime/auth.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { IncomingMessage } from "node:http";
|
|
2
2
|
import { getAgentById, getTaskById } from "../be/db";
|
|
3
3
|
import { getUserGrant } from "../be/rbac-roles";
|
|
4
|
+
import { getSessionRoot, sessionAccessForRoot } from "../be/session-shares";
|
|
4
5
|
import { getCombConfig } from "../comb/config";
|
|
5
6
|
import { mcpOverflowAuthError } from "../kv-overflow";
|
|
6
7
|
import { reservedNamespaceError } from "../kv-reserved-namespaces";
|
|
@@ -19,6 +20,8 @@ export type RoomAuthorizationInfo = RoomNamespaceInfo & {
|
|
|
19
20
|
callOrigin?: "http" | "mcp" | "ws";
|
|
20
21
|
userId?: string;
|
|
21
22
|
isOperator?: boolean;
|
|
23
|
+
/** The user an operator-key dashboard tab picked (`X-Swarm-User-Id`), captured on its ticket. */
|
|
24
|
+
dashboardUserId?: string;
|
|
22
25
|
};
|
|
23
26
|
|
|
24
27
|
export type ResolvedRoomNamespace = {
|
|
@@ -39,6 +42,7 @@ export type RoomNamespaceOperation =
|
|
|
39
42
|
|
|
40
43
|
const COMB_PRESENCE_NAMESPACE = /^presence:comb:[A-Za-z0-9_-]{1,64}:[A-Za-z0-9_-]{1,64}$/;
|
|
41
44
|
const COMB_PRESENCE_OPERATIONS = new Set<RoomNamespaceOperation>(["join", "leave", "presence"]);
|
|
45
|
+
const SESSION_PRESENCE_NAMESPACE = /^presence:session:([A-Za-z0-9_-]{1,64})$/;
|
|
42
46
|
|
|
43
47
|
function pageNamespace(pageId: string): string | null {
|
|
44
48
|
try {
|
|
@@ -162,6 +166,10 @@ export async function authorizeRoomNamespace(
|
|
|
162
166
|
return null;
|
|
163
167
|
}
|
|
164
168
|
|
|
169
|
+
if (namespace.startsWith("presence:session:")) {
|
|
170
|
+
return authorizeSessionPresence(namespace, info, access);
|
|
171
|
+
}
|
|
172
|
+
|
|
165
173
|
const reservedError = reservedNamespaceError(namespace);
|
|
166
174
|
if (reservedError) return reservedError;
|
|
167
175
|
|
|
@@ -203,3 +211,64 @@ export async function authorizeRoomNamespace(
|
|
|
203
211
|
|
|
204
212
|
return null;
|
|
205
213
|
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Bus topic published after a session's shares change. Sockets in that
|
|
217
|
+
* session's presence room re-check access and are evicted when it ended.
|
|
218
|
+
*/
|
|
219
|
+
export function sessionAccessTopic(rootTaskId: string): string {
|
|
220
|
+
return `session-access:${rootTaskId}`;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/** The root task id of a `presence:session:<rootTaskId>` namespace, else undefined. */
|
|
224
|
+
export function sessionPresenceRoot(namespace: string): string | undefined {
|
|
225
|
+
return SESSION_PRESENCE_NAMESPACE.exec(namespace)?.[1];
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* `presence:session:<rootTaskId>`: who is viewing (and typing in) a session.
|
|
230
|
+
* Presence-only, dashboard-only, and gated on read access so it never reveals
|
|
231
|
+
* who is inside a private session. An operator-key tab with no picked user
|
|
232
|
+
* keeps the access it has everywhere else.
|
|
233
|
+
*/
|
|
234
|
+
async function authorizeSessionPresence(
|
|
235
|
+
namespace: string,
|
|
236
|
+
info: RoomAuthorizationInfo,
|
|
237
|
+
access: boolean | RoomNamespaceOperation,
|
|
238
|
+
): Promise<string | null> {
|
|
239
|
+
const match = SESSION_PRESENCE_NAMESPACE.exec(namespace);
|
|
240
|
+
if (!match) return "invalid session presence namespace";
|
|
241
|
+
if (info.pageId || info.agentId || (!info.isOperator && !info.userId)) {
|
|
242
|
+
return "Session presence requires dashboard authentication";
|
|
243
|
+
}
|
|
244
|
+
if (typeof access !== "string" || !COMB_PRESENCE_OPERATIONS.has(access)) {
|
|
245
|
+
return "Session presence rooms only allow join, leave, and presence";
|
|
246
|
+
}
|
|
247
|
+
const principal = info.isOperator
|
|
248
|
+
? ({ kind: "operator" } as const)
|
|
249
|
+
: ({ kind: "user", userId: info.userId! } as const);
|
|
250
|
+
if (
|
|
251
|
+
!can({
|
|
252
|
+
principal,
|
|
253
|
+
verb: "session.presence",
|
|
254
|
+
resource: { kind: "kv-namespace", namespace },
|
|
255
|
+
source: info.callOrigin === "mcp" ? "mcp" : "http",
|
|
256
|
+
}).allow
|
|
257
|
+
) {
|
|
258
|
+
return "Session presence requires the session.presence permission";
|
|
259
|
+
}
|
|
260
|
+
if (info.userId && isRbacEnabled()) {
|
|
261
|
+
const grant = await getUserGrant(info.userId);
|
|
262
|
+
if (!grant.grantsAll && !grant.verbs.has("session.presence")) {
|
|
263
|
+
return "Session presence requires the session.presence permission";
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
const viewerId = info.isOperator ? info.dashboardUserId : info.userId;
|
|
267
|
+
if (info.isOperator && !viewerId) return null;
|
|
268
|
+
const root = await getSessionRoot(match[1]!);
|
|
269
|
+
if (!root || root.id !== match[1]) return "session not found";
|
|
270
|
+
if ((await sessionAccessForRoot(viewerId ?? null, root)) === "none") {
|
|
271
|
+
return "You don't have access to this session";
|
|
272
|
+
}
|
|
273
|
+
return null;
|
|
274
|
+
}
|
package/src/realtime/tickets.ts
CHANGED
|
@@ -3,7 +3,10 @@ import type { HttpRequestAuth } from "../utils/request-auth-context";
|
|
|
3
3
|
export const REALTIME_TICKET_TTL_MS = 60_000;
|
|
4
4
|
export const REALTIME_TICKET_CAPACITY = 10_000;
|
|
5
5
|
|
|
6
|
-
export type RealtimeTicketAuth = Extract<HttpRequestAuth, { kind: "operator" | "user" }
|
|
6
|
+
export type RealtimeTicketAuth = Extract<HttpRequestAuth, { kind: "operator" | "user" }> & {
|
|
7
|
+
/** The user an operator-key dashboard tab picked (`X-Swarm-User-Id`), for session presence. */
|
|
8
|
+
dashboardUserId?: string;
|
|
9
|
+
};
|
|
7
10
|
|
|
8
11
|
type StoredTicket = {
|
|
9
12
|
auth: RealtimeTicketAuth;
|
|
@@ -10,7 +10,12 @@ import { extractAndVerifyCookie } from "../utils/page-session";
|
|
|
10
10
|
import type { HttpRequestAuth } from "../utils/request-auth-context";
|
|
11
11
|
import { scrubSecrets } from "../utils/secret-scrubber";
|
|
12
12
|
import { registerSessionTokenConnection } from "../utils/session-token-connections";
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
authorizeRoomNamespace,
|
|
15
|
+
resolveRoomNamespace,
|
|
16
|
+
sessionAccessTopic,
|
|
17
|
+
sessionPresenceRoot,
|
|
18
|
+
} from "./auth";
|
|
14
19
|
import { realtimeBus } from "./bus";
|
|
15
20
|
import {
|
|
16
21
|
addWorkerSubscription,
|
|
@@ -64,6 +69,7 @@ type Identity = {
|
|
|
64
69
|
pageId?: string;
|
|
65
70
|
userId?: string;
|
|
66
71
|
isOperator?: boolean;
|
|
72
|
+
dashboardUserId?: string;
|
|
67
73
|
};
|
|
68
74
|
type Viewer = { userId: string; name: string; kind: "user" | "guest" | "agent" };
|
|
69
75
|
type AuthenticatedConnection = {
|
|
@@ -81,7 +87,7 @@ function header(req: IncomingMessage, name: string): string | undefined {
|
|
|
81
87
|
|
|
82
88
|
function identityFromHttpAuth(
|
|
83
89
|
req: IncomingMessage,
|
|
84
|
-
auth: HttpRequestAuth,
|
|
90
|
+
auth: HttpRequestAuth & { dashboardUserId?: string },
|
|
85
91
|
): AuthenticatedConnection {
|
|
86
92
|
if (auth.kind === "controller") throw new Error("Controller tokens cannot use realtime");
|
|
87
93
|
if (auth.kind === "agent") {
|
|
@@ -110,6 +116,7 @@ function identityFromHttpAuth(
|
|
|
110
116
|
auth.kind === "agent" ? (auth.taskId ?? undefined) : header(req, "x-source-task-id"),
|
|
111
117
|
userId: auth.kind === "user" ? auth.userId : undefined,
|
|
112
118
|
isOperator: auth.kind === "operator",
|
|
119
|
+
dashboardUserId: auth.kind === "operator" ? auth.dashboardUserId : undefined,
|
|
113
120
|
} satisfies Identity,
|
|
114
121
|
me: {
|
|
115
122
|
userId: auth.kind === "user" ? auth.userId : (agentId ?? guestId),
|
|
@@ -228,7 +235,12 @@ function connect(
|
|
|
228
235
|
)
|
|
229
236
|
: undefined;
|
|
230
237
|
void auth.sessionToken?.check().catch(() => ws.close(1008, "Session token revoked or expired"));
|
|
231
|
-
|
|
238
|
+
// `suspended`: a session presence room whose access is being re-checked
|
|
239
|
+
// after a share change; nothing is forwarded until the check passes.
|
|
240
|
+
const rooms = new Map<
|
|
241
|
+
string,
|
|
242
|
+
{ room: LiveRoom; version: number; stop: () => void; suspended?: boolean }
|
|
243
|
+
>();
|
|
232
244
|
const channels = new Map<string, () => void>();
|
|
233
245
|
const pending = new Map<number, number>();
|
|
234
246
|
const clientId = crypto.getRandomValues(new Uint32Array(1))[0]!;
|
|
@@ -313,6 +325,9 @@ function connect(
|
|
|
313
325
|
if (namespace.startsWith("presence:comb:") && msg.name !== "default") {
|
|
314
326
|
throw new Error("Comb presence uses the default room");
|
|
315
327
|
}
|
|
328
|
+
if (namespace.startsWith("presence:session:") && msg.name !== "default") {
|
|
329
|
+
throw new Error("Session presence uses the default room");
|
|
330
|
+
}
|
|
316
331
|
const denied = await authorizeRoomNamespace(namespace, auth.identity, msg.op, msg.name);
|
|
317
332
|
if (denied) throw new Error(typeof denied === "string" ? denied : "Forbidden");
|
|
318
333
|
if (closed) return;
|
|
@@ -352,12 +367,14 @@ function connect(
|
|
|
352
367
|
room = await getRoom(namespace, msg.name, msg.schemaVersion, { create: false });
|
|
353
368
|
} catch (error) {
|
|
354
369
|
if (!(error instanceof Error) || error.message !== "room does not exist") throw error;
|
|
355
|
-
// Creating a room is a write. Comb presence rooms take
|
|
356
|
-
// operations, so they check creation as a join.
|
|
370
|
+
// Creating a room is a write. Comb and session presence rooms take
|
|
371
|
+
// only presence operations, so they check creation as a join.
|
|
357
372
|
const creationDenied = await authorizeRoomNamespace(
|
|
358
373
|
namespace,
|
|
359
374
|
auth.identity,
|
|
360
|
-
namespace.startsWith("presence:comb:")
|
|
375
|
+
namespace.startsWith("presence:comb:") || namespace.startsWith("presence:session:")
|
|
376
|
+
? "join"
|
|
377
|
+
: true,
|
|
361
378
|
);
|
|
362
379
|
if (creationDenied) throw new Error(creationDenied);
|
|
363
380
|
if (closed) return;
|
|
@@ -365,6 +382,7 @@ function connect(
|
|
|
365
382
|
}
|
|
366
383
|
if (closed) return;
|
|
367
384
|
const stopRoom = realtimeBus.subscribe(key, (event) => {
|
|
385
|
+
if (rooms.get(key)?.suspended) return;
|
|
368
386
|
const view = event as ReturnType<typeof roomView> & { type: string };
|
|
369
387
|
const type = view.type;
|
|
370
388
|
if (type === "deleted") {
|
|
@@ -387,17 +405,67 @@ function connect(
|
|
|
387
405
|
.catch(() => ws.close(1011, "Room reset failed"));
|
|
388
406
|
}
|
|
389
407
|
});
|
|
390
|
-
const stopPresence = realtimeBus.subscribe(`presence:${key}`, (peers) =>
|
|
391
|
-
|
|
392
|
-
|
|
408
|
+
const stopPresence = realtimeBus.subscribe(`presence:${key}`, (peers) => {
|
|
409
|
+
if (rooms.get(key)?.suspended) return;
|
|
410
|
+
send({ type: "presence", name: msg.name, namespace, peers });
|
|
411
|
+
});
|
|
412
|
+
const sessionRoot = sessionPresenceRoot(namespace);
|
|
413
|
+
let accessCheck = 0;
|
|
414
|
+
// A share change can end this viewer's read access: stop forwarding
|
|
415
|
+
// at once, then evict, or resume if access remains.
|
|
416
|
+
const stopAccess = sessionRoot
|
|
417
|
+
? realtimeBus.subscribe(sessionAccessTopic(sessionRoot), () => {
|
|
418
|
+
const joined = rooms.get(key);
|
|
419
|
+
if (!joined) return;
|
|
420
|
+
joined.suspended = true;
|
|
421
|
+
const check = ++accessCheck;
|
|
422
|
+
void authorizeRoomNamespace(namespace, auth.identity, "join")
|
|
423
|
+
.catch(() => "Session access check failed")
|
|
424
|
+
.then((denied) => {
|
|
425
|
+
const current = rooms.get(key);
|
|
426
|
+
if (closed || !current || check !== accessCheck) return;
|
|
427
|
+
if (denied) {
|
|
428
|
+
leave(key);
|
|
429
|
+
send({ type: "error", name: msg.name, namespace, error: denied });
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
current.suspended = false;
|
|
433
|
+
send({
|
|
434
|
+
type: "presence",
|
|
435
|
+
name: msg.name,
|
|
436
|
+
namespace,
|
|
437
|
+
peers: [...current.room.awareness.getStates().values()],
|
|
438
|
+
});
|
|
439
|
+
});
|
|
440
|
+
})
|
|
441
|
+
: undefined;
|
|
393
442
|
rooms.set(key, {
|
|
394
443
|
room,
|
|
395
444
|
version: msg.schemaVersion,
|
|
396
445
|
stop: () => {
|
|
397
446
|
stopRoom();
|
|
398
447
|
stopPresence();
|
|
448
|
+
stopAccess?.();
|
|
399
449
|
},
|
|
450
|
+
suspended: sessionRoot !== undefined,
|
|
400
451
|
});
|
|
452
|
+
if (sessionRoot) {
|
|
453
|
+
// A share change published while the room loaded reached no
|
|
454
|
+
// subscriber, so check again now that the access subscriber is
|
|
455
|
+
// live. Anything later is that subscriber's to handle.
|
|
456
|
+
const check = ++accessCheck;
|
|
457
|
+
const denied = await authorizeRoomNamespace(namespace, auth.identity, "join").catch(
|
|
458
|
+
() => "Session access check failed",
|
|
459
|
+
);
|
|
460
|
+
if (closed) return;
|
|
461
|
+
if (denied) {
|
|
462
|
+
leave(key);
|
|
463
|
+
throw new Error(denied);
|
|
464
|
+
}
|
|
465
|
+
const current = rooms.get(key);
|
|
466
|
+
if (!current) throw new Error("You don't have access to this session");
|
|
467
|
+
if (check === accessCheck) current.suspended = false;
|
|
468
|
+
}
|
|
401
469
|
room.awareness.states.set(clientId, { ...auth.me, data: {} });
|
|
402
470
|
room.awareness.meta.set(clientId, { clock: 1, lastUpdated: Date.now() });
|
|
403
471
|
publishPresence(room);
|
package/src/slack/blocks.ts
CHANGED
|
@@ -97,6 +97,10 @@ export function splitSlackSectionText(text: string): string[] {
|
|
|
97
97
|
if (splitIdx < MAX_SECTION_LENGTH / 2) {
|
|
98
98
|
// No good break point at all, hard split
|
|
99
99
|
splitIdx = MAX_SECTION_LENGTH;
|
|
100
|
+
// Keep a surrogate pair (emoji, astral char) together: never end a
|
|
101
|
+
// chunk on a lone high surrogate.
|
|
102
|
+
const prev = remaining.charCodeAt(splitIdx - 1);
|
|
103
|
+
if (prev >= 0xd800 && prev <= 0xdbff) splitIdx -= 1;
|
|
100
104
|
}
|
|
101
105
|
chunks.push(remaining.slice(0, splitIdx));
|
|
102
106
|
remaining = remaining.slice(splitIdx).trimStart();
|
|
@@ -104,6 +108,20 @@ export function splitSlackSectionText(text: string): string[] {
|
|
|
104
108
|
return chunks;
|
|
105
109
|
}
|
|
106
110
|
|
|
111
|
+
// Slack's hard cap on section text.
|
|
112
|
+
const SLACK_SECTION_TEXT_LIMIT = 3000;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Default blocks for a tool message sent without explicit `blocks`: one
|
|
116
|
+
* mrkdwn section per chunk, so text over the section limit still sends.
|
|
117
|
+
* Text Slack accepts in one section stays one section, so links and code
|
|
118
|
+
* fences are never split below the limit.
|
|
119
|
+
*/
|
|
120
|
+
export function buildMrkdwnSectionBlocks(text: string): SlackBlock[] {
|
|
121
|
+
if (text.length <= SLACK_SECTION_TEXT_LIMIT) return [sectionBlock(text)];
|
|
122
|
+
return splitSlackSectionText(text).map((chunk) => sectionBlock(chunk));
|
|
123
|
+
}
|
|
124
|
+
|
|
107
125
|
// --- Block primitives ---
|
|
108
126
|
|
|
109
127
|
function contextBlock(...elements: string[]): SlackBlock {
|
package/src/slack/dm.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { WebClient } from "@slack/web-api";
|
|
2
|
+
|
|
3
|
+
/** Slack user ids start with U (or W on Enterprise Grid); conversation ids start with C, G or D. */
|
|
4
|
+
export function isSlackUserId(id: string): boolean {
|
|
5
|
+
return /^[UW][A-Z0-9]+$/.test(id);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Resolves a user id to the bot's own DM with that user; returns any other id unchanged.
|
|
10
|
+
*
|
|
11
|
+
* chat.postMessage accepts a user id as `channel`, but Slack documents that the
|
|
12
|
+
* message then lands in the user's DM with Slackbot (USLACK), which the bot is
|
|
13
|
+
* not a member of. conversations.replies and chat.postMessage on the returned
|
|
14
|
+
* D id fail with channel_not_found, and human replies never reach the bot as
|
|
15
|
+
* message.im events. conversations.open returns the bot's own IM, which it can
|
|
16
|
+
* read, write, and receives events for.
|
|
17
|
+
*/
|
|
18
|
+
export async function resolveSlackConversationId(
|
|
19
|
+
client: WebClient,
|
|
20
|
+
channelId: string,
|
|
21
|
+
): Promise<string> {
|
|
22
|
+
if (!isSlackUserId(channelId)) return channelId;
|
|
23
|
+
const result = await client.conversations.open({ users: channelId });
|
|
24
|
+
const dmId = result.channel?.id;
|
|
25
|
+
if (!dmId) {
|
|
26
|
+
throw new Error(`conversations.open returned no DM channel for user ${channelId}`);
|
|
27
|
+
}
|
|
28
|
+
return dmId;
|
|
29
|
+
}
|
|
@@ -64,6 +64,9 @@ Common properties, allowed on every event of this product:
|
|
|
64
64
|
| `popup.dismissed` | ui | 2026-10-12 | A dashboard pop-up was closed without submitting. | `method` (later \| close \| escape, required)<br>`popup` (feedback \| org_name \| used_by, required) |
|
|
65
65
|
| `popup.shown` | ui | 2026-10-12 | A dashboard pop-up was shown. Telemetry-gated: never sent while telemetry is off. | `popup` (feedback \| org_name \| used_by, required)<br>`trigger` (task_failed \| tasks_completed \| workflow_completed \| quarterly \| missing_org_name, required) |
|
|
66
66
|
| `popup.submitted` | ui | 2026-10-12 | A dashboard pop-up was submitted. Content stays out of telemetry: only flags for what was filled. | `has_contact` (boolean, required)<br>`has_message` (boolean, required)<br>`popup` (feedback \| org_name \| used_by, required)<br>`score` (integer ≥0) |
|
|
67
|
+
| `ranking.opted_in` | api-server | 2026-10-18 | An admin opted the install into the public ranking, or the API rebuilt its listing after an org identity change. | `listing` (string, required) |
|
|
68
|
+
| `ranking.opted_out` | api-server | 2026-10-18 | The install left the public ranking: the admin turned it off, or the org name or website was cleared or changed. | `domain` (string, required) |
|
|
69
|
+
| `ranking.usage` | worker | 2026-10-18 | Token usage of one session on an install opted into the public ranking. Sent next to session.cost. | `cacheReadTokens` (integer ≥0)<br>`cacheWriteTokens` (integer ≥0)<br>`inputTokens` (integer ≥0, required)<br>`model` (string)<br>`outputTokens` (integer ≥0, required)<br>`provider` (string)<br>`totalTokens` (integer ≥0, required) |
|
|
67
70
|
| `schedule.error` | api-server | 2026-09-30 | A schedule failed to fire. | `consecutiveErrors` (integer, required)<br>`scheduleType` (string, required)<br>`triggeredWorkflows` (boolean, required)<br>`wasRecovered` (boolean, required) |
|
|
68
71
|
| `schedule.executed` | api-server | 2026-09-30 | A schedule fired. | `scheduleType` (string, required)<br>`triggeredWorkflows` (boolean, required)<br>`wasRecovered` (boolean, required) |
|
|
69
72
|
| `server.shutdown` | api-server | 2026-09-30 | The API server shut down. | `sessionsProcessed` (integer, required)<br>`signal` (string, required)<br>`uptimeMs` (integer, required) |
|
|
@@ -1,9 +1,42 @@
|
|
|
1
1
|
{
|
|
2
2
|
"repo": "desplega-ai/proxy",
|
|
3
|
-
"commit": "
|
|
3
|
+
"commit": "6260f39827fc57784642b1adc88a43f61d882a6f",
|
|
4
4
|
"files": {
|
|
5
|
-
"types.gen.ts": "
|
|
6
|
-
"catalog.json": "
|
|
7
|
-
"EVENTS.md": "
|
|
5
|
+
"types.gen.ts": "192c6769164a65b6e804ea088b38de6827d34fac26a7ecaccce881a46e98b1a7",
|
|
6
|
+
"catalog.json": "1b2b052f8e43f082e9d6efc2768752d6f194c0cbf0d26ae9c35643cce0e6486c",
|
|
7
|
+
"EVENTS.md": "9451da995ef04a049888715f960a1641bbc52fecb3dce97610c3c391bead1faf",
|
|
8
|
+
"ranking/fixtures/expected.json": "6d506546f48d99a089666bed2a4c08752ebb63a029392c76ffd057e9520adc70",
|
|
9
|
+
"ranking/fixtures/invalid/bad-category.json": "a4c36da6079c36d15e5e8c8e6322fc97b172de015a80a26cee8cab699f666236",
|
|
10
|
+
"ranking/fixtures/invalid/bad-claim.json": "cee14d465545d057cfb61922301a5119187ba17d70efabb16528a5e305d7b065",
|
|
11
|
+
"ranking/fixtures/invalid/blank-description.json": "bf1ae1a834613b67b59f42c158e892448f23268b41f1c851653f25e4843a20b0",
|
|
12
|
+
"ranking/fixtures/invalid/blank-name.json": "7e2cc0156ac5f5d42342a99c38fe0f822b893784f17cdcfa371277c847d83a2e",
|
|
13
|
+
"ranking/fixtures/invalid/description-too-long.json": "c96ed93d014fbdac8c674ab8266d41bca83547d42a0d6dec53f275a0258d1adf",
|
|
14
|
+
"ranking/fixtures/invalid/domain-uppercase.json": "5a3cefdceddf7716d0b6300453a928e122e3ca8fa55493d48666701ca9853ebb",
|
|
15
|
+
"ranking/fixtures/invalid/domain-with-scheme.json": "389f6216e350c8b13f472658b15bc485693a0064cf837a5a66a3f74cef8f26c3",
|
|
16
|
+
"ranking/fixtures/invalid/domain-with-www.json": "b936fd61b21ad5f0219e739686372b16e4e97c2101f239ed3497bb73b0307205",
|
|
17
|
+
"ranking/fixtures/invalid/http-source-url.json": "dcab46205d9113864f31966affe74edb9a9ffe1b2fc9bfb3923997daa30cf651",
|
|
18
|
+
"ranking/fixtures/invalid/missing-description.json": "9a3a3a1104c9e1c3dea17d3a81be87dcd96188d64729e601ecc94848215aea49",
|
|
19
|
+
"ranking/fixtures/invalid/missing-version.json": "81b54c7cae034bd6327b981ec17609e926f5ee03465b589bc997c5ec9ddfc60b",
|
|
20
|
+
"ranking/fixtures/invalid/name-not-string.json": "46af1d97dbe77735a8020dd3c7ea1b67f71d8310cc065341b82173229d6f9487",
|
|
21
|
+
"ranking/fixtures/invalid/name-too-long.json": "8c07bce282182b8e0d9476a70a78863c708ce25d6cce3c994e29c0ec10972255",
|
|
22
|
+
"ranking/fixtures/invalid/not-object.json": "53bd6a73a22019f092e9e6e454936e8ba172201553f897f43f478f4946effaea",
|
|
23
|
+
"ranking/fixtures/invalid/null.json": "38e0b9de817f645c4bec37c0d4a3e58baecccb040f5718dc069a72c7385a0bed",
|
|
24
|
+
"ranking/fixtures/invalid/other-with-subcategory.json": "e5bdaf6982b5bf8fedf08b31c40232808f91884177a88cae5c6ab45489aec892",
|
|
25
|
+
"ranking/fixtures/invalid/oversize.json": "e57b2d8030a3b62b6621fcd81eb90959c22d190d4bfb5ee834086808249ddbe2",
|
|
26
|
+
"ranking/fixtures/invalid/short-claim.json": "051e789f8989b052173ab90a21d6358200f45cadfb729f36081c462ba9a8096b",
|
|
27
|
+
"ranking/fixtures/invalid/subcategory-other-category.json": "3d4ca6ce612ace116e382b08579ee451b2c1cbf93716cb01ad61489b7cecbcd8",
|
|
28
|
+
"ranking/fixtures/invalid/trailing-data.json": "ffb822a83c7a2bfb41c6fdfab375c11232db70e5063ace529a1e4cc7b6be44ff",
|
|
29
|
+
"ranking/fixtures/invalid/unknown-field.json": "c24c57f208523d4de535057a636de1fc5abcad329d514baabb180cbcfdd6a5b9",
|
|
30
|
+
"ranking/fixtures/invalid/unsupported-version.json": "32e5c26b9c646090a3e94822594b208d629e60796c00f964216859ee8d086dc7",
|
|
31
|
+
"ranking/fixtures/invalid/updated-at-not-date.json": "a03111ddc50bcfc818b30e977a4b94c566884b04322e1600e7f70a04c120d5c1",
|
|
32
|
+
"ranking/fixtures/invalid/updated-at-offset.json": "91b78160eb83da274bb3d93fbd812b72f2e648935a31fb5685b32037391e63fc",
|
|
33
|
+
"ranking/fixtures/invalid/wrong-case-field.json": "76c8a019d0aabdd1ca394f3ea552511a98b2458e7b850b75289f88a665682087",
|
|
34
|
+
"ranking/fixtures/valid/category-without-subcategory.json": "45c5b872aec232bdcba791e6ba1a7aba698c825f389e194a38e96ad141f56e5a",
|
|
35
|
+
"ranking/fixtures/valid/full.json": "d6d93c4a6159e814c3f089c69083df700fb19359f127ccd2b71529a058dfe5e9",
|
|
36
|
+
"ranking/fixtures/valid/minimal.json": "de6629d6ee2c4b67598abfc5ec7f6470a93dd9893b07e6e7c93d018cf5445593",
|
|
37
|
+
"ranking/fixtures/valid/padded-text.json": "db71248bf83bb1ffb639208ec467a0228e5c37f604299d09838ab25dc61986a3",
|
|
38
|
+
"ranking/fixtures/valid/unicode-name.json": "01a9d2cc8c5fec128af3eef35bf16696286f28636dac21b9dec107b28fddf743",
|
|
39
|
+
"ranking/listing.v1.schema.json": "1895c57075bbb4b0945e1c5daf813d6c4eaccf1f4480cabd6dc0ce2f0d146776",
|
|
40
|
+
"ranking/taxonomy.json": "c6abe7e437635c9a3c1a82a3477a8757ba6c0cb7cbeae86c8c3d175fb37f402c"
|
|
8
41
|
}
|
|
9
42
|
}
|
|
@@ -1376,6 +1376,75 @@
|
|
|
1376
1376
|
}
|
|
1377
1377
|
}
|
|
1378
1378
|
},
|
|
1379
|
+
{
|
|
1380
|
+
"product": "agent-swarm",
|
|
1381
|
+
"event": "ranking.opted_in",
|
|
1382
|
+
"description": "An admin opted the install into the public ranking, or the API rebuilt its listing after an org identity change.",
|
|
1383
|
+
"since": "2026-10-18",
|
|
1384
|
+
"source": "api-server",
|
|
1385
|
+
"properties": {
|
|
1386
|
+
"listing": {
|
|
1387
|
+
"type": "string",
|
|
1388
|
+
"required": true,
|
|
1389
|
+
"description": "The stringified telemetry_ranking listing (contracts/telemetry/ranking/listing.v1.schema.json). The ingest also requires its name and domain to equal context.org_name and context.org_website."
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
},
|
|
1393
|
+
{
|
|
1394
|
+
"product": "agent-swarm",
|
|
1395
|
+
"event": "ranking.opted_out",
|
|
1396
|
+
"description": "The install left the public ranking: the admin turned it off, or the org name or website was cleared or changed.",
|
|
1397
|
+
"since": "2026-10-18",
|
|
1398
|
+
"source": "api-server",
|
|
1399
|
+
"properties": {
|
|
1400
|
+
"domain": {
|
|
1401
|
+
"type": "string",
|
|
1402
|
+
"required": true,
|
|
1403
|
+
"description": "The listing domain that leaves the ranking: a lowercase hostname."
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
},
|
|
1407
|
+
{
|
|
1408
|
+
"product": "agent-swarm",
|
|
1409
|
+
"event": "ranking.usage",
|
|
1410
|
+
"description": "Token usage of one session on an install opted into the public ranking. Sent next to session.cost.",
|
|
1411
|
+
"since": "2026-10-18",
|
|
1412
|
+
"source": "worker",
|
|
1413
|
+
"properties": {
|
|
1414
|
+
"totalTokens": {
|
|
1415
|
+
"type": "integer",
|
|
1416
|
+
"required": true,
|
|
1417
|
+
"minimum": 0,
|
|
1418
|
+
"description": "inputTokens + cacheReadTokens + cacheWriteTokens + outputTokens. The ranking sums this."
|
|
1419
|
+
},
|
|
1420
|
+
"inputTokens": {
|
|
1421
|
+
"type": "integer",
|
|
1422
|
+
"required": true,
|
|
1423
|
+
"minimum": 0
|
|
1424
|
+
},
|
|
1425
|
+
"outputTokens": {
|
|
1426
|
+
"type": "integer",
|
|
1427
|
+
"required": true,
|
|
1428
|
+
"minimum": 0
|
|
1429
|
+
},
|
|
1430
|
+
"cacheReadTokens": {
|
|
1431
|
+
"type": "integer",
|
|
1432
|
+
"minimum": 0
|
|
1433
|
+
},
|
|
1434
|
+
"cacheWriteTokens": {
|
|
1435
|
+
"type": "integer",
|
|
1436
|
+
"minimum": 0
|
|
1437
|
+
},
|
|
1438
|
+
"model": {
|
|
1439
|
+
"type": "string",
|
|
1440
|
+
"description": "The session's model, the same value as session.cost.model. Usage without it, or with a value the ranking does not accept, is shown as the model unknown."
|
|
1441
|
+
},
|
|
1442
|
+
"provider": {
|
|
1443
|
+
"type": "string",
|
|
1444
|
+
"description": "The session's provider, the same value as session.cost.provider. Missing means unknown."
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
},
|
|
1379
1448
|
{
|
|
1380
1449
|
"product": "agent-fs",
|
|
1381
1450
|
"event": "server.started",
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$comment": "Every file in valid/ must parse in the proxy (ParseListing) and in agent-swarm (parseTelemetryRanking). Every file in invalid/ must fail in both; the value is the field the proxy names in its invalid_property message. A file's exact bytes are the listing string.",
|
|
3
|
+
"invalid": {
|
|
4
|
+
"bad-claim.json": "listing.claim",
|
|
5
|
+
"short-claim.json": "listing.claim",
|
|
6
|
+
"missing-description.json": "listing.description",
|
|
7
|
+
"unknown-field.json": "listing.logo",
|
|
8
|
+
"wrong-case-field.json": "listing.Claim",
|
|
9
|
+
"unsupported-version.json": "listing.v",
|
|
10
|
+
"missing-version.json": "listing.v",
|
|
11
|
+
"bad-category.json": "listing.category",
|
|
12
|
+
"subcategory-other-category.json": "listing.subcategory",
|
|
13
|
+
"other-with-subcategory.json": "listing.subcategory",
|
|
14
|
+
"http-source-url.json": "listing.sourceCodeUrl",
|
|
15
|
+
"domain-with-scheme.json": "listing.domain",
|
|
16
|
+
"domain-with-www.json": "listing.domain",
|
|
17
|
+
"domain-uppercase.json": "listing.domain",
|
|
18
|
+
"name-too-long.json": "listing.name",
|
|
19
|
+
"blank-name.json": "listing.name",
|
|
20
|
+
"blank-description.json": "listing.description",
|
|
21
|
+
"description-too-long.json": "listing.description",
|
|
22
|
+
"name-not-string.json": "listing.name",
|
|
23
|
+
"updated-at-offset.json": "listing.updatedAt",
|
|
24
|
+
"updated-at-not-date.json": "listing.updatedAt",
|
|
25
|
+
"not-object.json": "listing",
|
|
26
|
+
"null.json": "listing",
|
|
27
|
+
"trailing-data.json": "listing",
|
|
28
|
+
"oversize.json": "listing"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"v": 1,
|
|
3
|
+
"claim": "rk_3f9a1c0e7b2d4a6f8e1c3b5d7f9a2c4e",
|
|
4
|
+
"name": "Example Org",
|
|
5
|
+
"domain": "example.com",
|
|
6
|
+
"category": "games",
|
|
7
|
+
"subcategory": null,
|
|
8
|
+
"description": "QA and release automation for our web apps",
|
|
9
|
+
"sourceCodeUrl": "https://example.org/agents",
|
|
10
|
+
"updatedAt": "2026-10-18T10:00:00.000Z"
|
|
11
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"v": 1,
|
|
3
|
+
"claim": "rk_3F9A1C0E7B2D4A6F8E1C3B5D7F9A2C4E",
|
|
4
|
+
"name": "Example Org",
|
|
5
|
+
"domain": "example.com",
|
|
6
|
+
"category": "engineering",
|
|
7
|
+
"subcategory": "qa-testing",
|
|
8
|
+
"description": "QA and release automation for our web apps",
|
|
9
|
+
"sourceCodeUrl": "https://example.org/agents",
|
|
10
|
+
"updatedAt": "2026-10-18T10:00:00.000Z"
|
|
11
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"v": 1,
|
|
3
|
+
"claim": "rk_3f9a1c0e7b2d4a6f8e1c3b5d7f9a2c4e",
|
|
4
|
+
"name": "Example Org",
|
|
5
|
+
"domain": "example.com",
|
|
6
|
+
"category": "engineering",
|
|
7
|
+
"subcategory": "qa-testing",
|
|
8
|
+
"description": " ",
|
|
9
|
+
"sourceCodeUrl": "https://example.org/agents",
|
|
10
|
+
"updatedAt": "2026-10-18T10:00:00.000Z"
|
|
11
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"v": 1,
|
|
3
|
+
"claim": "rk_3f9a1c0e7b2d4a6f8e1c3b5d7f9a2c4e",
|
|
4
|
+
"name": " ",
|
|
5
|
+
"domain": "example.com",
|
|
6
|
+
"category": "engineering",
|
|
7
|
+
"subcategory": "qa-testing",
|
|
8
|
+
"description": "QA and release automation for our web apps",
|
|
9
|
+
"sourceCodeUrl": "https://example.org/agents",
|
|
10
|
+
"updatedAt": "2026-10-18T10:00:00.000Z"
|
|
11
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"v": 1,
|
|
3
|
+
"claim": "rk_3f9a1c0e7b2d4a6f8e1c3b5d7f9a2c4e",
|
|
4
|
+
"name": "Example Org",
|
|
5
|
+
"domain": "example.com",
|
|
6
|
+
"category": "engineering",
|
|
7
|
+
"subcategory": "qa-testing",
|
|
8
|
+
"description": "ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
|
|
9
|
+
"sourceCodeUrl": "https://example.org/agents",
|
|
10
|
+
"updatedAt": "2026-10-18T10:00:00.000Z"
|
|
11
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"v": 1,
|
|
3
|
+
"claim": "rk_3f9a1c0e7b2d4a6f8e1c3b5d7f9a2c4e",
|
|
4
|
+
"name": "Example Org",
|
|
5
|
+
"domain": "Example.com",
|
|
6
|
+
"category": "engineering",
|
|
7
|
+
"subcategory": "qa-testing",
|
|
8
|
+
"description": "QA and release automation for our web apps",
|
|
9
|
+
"sourceCodeUrl": "https://example.org/agents",
|
|
10
|
+
"updatedAt": "2026-10-18T10:00:00.000Z"
|
|
11
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"v": 1,
|
|
3
|
+
"claim": "rk_3f9a1c0e7b2d4a6f8e1c3b5d7f9a2c4e",
|
|
4
|
+
"name": "Example Org",
|
|
5
|
+
"domain": "https://example.com",
|
|
6
|
+
"category": "engineering",
|
|
7
|
+
"subcategory": "qa-testing",
|
|
8
|
+
"description": "QA and release automation for our web apps",
|
|
9
|
+
"sourceCodeUrl": "https://example.org/agents",
|
|
10
|
+
"updatedAt": "2026-10-18T10:00:00.000Z"
|
|
11
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"v": 1,
|
|
3
|
+
"claim": "rk_3f9a1c0e7b2d4a6f8e1c3b5d7f9a2c4e",
|
|
4
|
+
"name": "Example Org",
|
|
5
|
+
"domain": "www.example.com",
|
|
6
|
+
"category": "engineering",
|
|
7
|
+
"subcategory": "qa-testing",
|
|
8
|
+
"description": "QA and release automation for our web apps",
|
|
9
|
+
"sourceCodeUrl": "https://example.org/agents",
|
|
10
|
+
"updatedAt": "2026-10-18T10:00:00.000Z"
|
|
11
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"v": 1,
|
|
3
|
+
"claim": "rk_3f9a1c0e7b2d4a6f8e1c3b5d7f9a2c4e",
|
|
4
|
+
"name": "Example Org",
|
|
5
|
+
"domain": "example.com",
|
|
6
|
+
"category": "engineering",
|
|
7
|
+
"subcategory": "qa-testing",
|
|
8
|
+
"description": "QA and release automation for our web apps",
|
|
9
|
+
"sourceCodeUrl": "http://github.com/example/agents",
|
|
10
|
+
"updatedAt": "2026-10-18T10:00:00.000Z"
|
|
11
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"v": 1,
|
|
3
|
+
"claim": "rk_3f9a1c0e7b2d4a6f8e1c3b5d7f9a2c4e",
|
|
4
|
+
"name": "Example Org",
|
|
5
|
+
"domain": "example.com",
|
|
6
|
+
"category": "engineering",
|
|
7
|
+
"subcategory": "qa-testing",
|
|
8
|
+
"sourceCodeUrl": "https://example.org/agents",
|
|
9
|
+
"updatedAt": "2026-10-18T10:00:00.000Z"
|
|
10
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"claim": "rk_3f9a1c0e7b2d4a6f8e1c3b5d7f9a2c4e",
|
|
3
|
+
"name": "Example Org",
|
|
4
|
+
"domain": "example.com",
|
|
5
|
+
"category": "engineering",
|
|
6
|
+
"subcategory": "qa-testing",
|
|
7
|
+
"description": "QA and release automation for our web apps",
|
|
8
|
+
"sourceCodeUrl": "https://example.org/agents",
|
|
9
|
+
"updatedAt": "2026-10-18T10:00:00.000Z"
|
|
10
|
+
}
|