@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/be/db.ts
CHANGED
|
@@ -195,6 +195,7 @@ import {
|
|
|
195
195
|
} from "./db/context-versions";
|
|
196
196
|
import { clearAgentModelProfileOnOverrideChange } from "./db/model-profiles";
|
|
197
197
|
import { getDb, getDbClient } from "./db/runtime";
|
|
198
|
+
import { type SessionViewer, sessionViewerCondition } from "./db/session-visibility";
|
|
198
199
|
import { classifyTaskHumanFree, reclassifyTaskHumanFree } from "./db/tasks/human-free";
|
|
199
200
|
import {
|
|
200
201
|
type AgentTaskRow,
|
|
@@ -203,6 +204,7 @@ import {
|
|
|
203
204
|
findExistingLinearTrackerContextWork,
|
|
204
205
|
getTaskById,
|
|
205
206
|
getTasksByAgentId,
|
|
207
|
+
readableSessionCondition,
|
|
206
208
|
rowToAgentTask,
|
|
207
209
|
rowToAgentTaskSummary,
|
|
208
210
|
} from "./db/tasks/read";
|
|
@@ -1731,7 +1733,9 @@ export async function moveAssetKey(input: {
|
|
|
1731
1733
|
id: string;
|
|
1732
1734
|
key: string;
|
|
1733
1735
|
changedBy?: string;
|
|
1734
|
-
|
|
1736
|
+
/** Task moves only: descendant task ids the caller already authorized to move with the task. */
|
|
1737
|
+
descendantTaskIds?: readonly string[];
|
|
1738
|
+
}): Promise<false | { movedTaskIds?: string[] }> {
|
|
1735
1739
|
const key = normalizeAssetKey(input.key);
|
|
1736
1740
|
// auditAssetKeys stays sync (shared with the boot audit); run it inside a
|
|
1737
1741
|
// client transaction so this request-path read cannot observe another
|
|
@@ -1746,7 +1750,7 @@ export async function moveAssetKey(input: {
|
|
|
1746
1750
|
}
|
|
1747
1751
|
const previousKey = await currentAssetKey(input.entityType, input.id);
|
|
1748
1752
|
if (!previousKey) return false;
|
|
1749
|
-
if (previousKey === key) return
|
|
1753
|
+
if (previousKey === key && input.entityType !== "task") return {};
|
|
1750
1754
|
if (input.entityType === "file") {
|
|
1751
1755
|
const mapping = await getAssetKeyMapping(input.id);
|
|
1752
1756
|
if (mapping?.sourceEntityType === "task-attachment") {
|
|
@@ -1757,48 +1761,64 @@ export async function moveAssetKey(input: {
|
|
|
1757
1761
|
}
|
|
1758
1762
|
const now = new Date().toISOString();
|
|
1759
1763
|
|
|
1760
|
-
|
|
1764
|
+
return getDbClient().transaction(async (tx) => {
|
|
1761
1765
|
switch (input.entityType) {
|
|
1762
1766
|
case "task": {
|
|
1763
|
-
const
|
|
1764
|
-
|
|
1767
|
+
const root = await getTaskById(input.id);
|
|
1768
|
+
if (!root) return false;
|
|
1769
|
+
const tasks = [root];
|
|
1770
|
+
for (const id of input.descendantTaskIds ?? []) {
|
|
1771
|
+
const task = id === root.id ? null : await getTaskById(id);
|
|
1772
|
+
if (task) tasks.push(task);
|
|
1773
|
+
}
|
|
1774
|
+
const movedTaskIds: string[] = [];
|
|
1775
|
+
for (const task of tasks) {
|
|
1776
|
+
if (task.key === key) continue;
|
|
1777
|
+
// A file shared with another task keeps the namespace of the task whose attachment
|
|
1778
|
+
// sources its mapping. Mappings with no attachment source follow any attaching task.
|
|
1779
|
+
const mappedFiles = await tx.query<{ id: string; key: string }>(
|
|
1780
|
+
`SELECT DISTINCT m.id, m."key" AS key
|
|
1765
1781
|
FROM asset_key_mappings m
|
|
1766
1782
|
JOIN task_attachments a
|
|
1767
1783
|
ON m.provider_id = COALESCE(NULLIF(a.provider_id, ''), 'agent-fs')
|
|
1768
1784
|
AND m.provider_org_id = COALESCE(a.agent_fs_org_id, '')
|
|
1769
1785
|
AND m.provider_drive_id = COALESCE(a.agent_fs_drive_id, '')
|
|
1770
1786
|
AND m.provider_key = COALESCE(NULLIF(a.provider_key, ''), a.path)
|
|
1771
|
-
WHERE a.task_id = ?`,
|
|
1772
|
-
[input.id],
|
|
1773
|
-
);
|
|
1774
|
-
await tx.run(
|
|
1775
|
-
'UPDATE agent_tasks SET "key" = ?, lastUpdatedAt = ?, updated_by = ? WHERE id = ?',
|
|
1776
|
-
[key, now, input.changedBy ?? null, input.id],
|
|
1777
|
-
);
|
|
1778
|
-
await tx.run(
|
|
1779
|
-
`UPDATE asset_key_mappings
|
|
1780
|
-
SET "key" = ?, updated_at = ?, updated_by = ?
|
|
1781
|
-
WHERE EXISTS (
|
|
1782
|
-
SELECT 1 FROM task_attachments a
|
|
1783
1787
|
WHERE a.task_id = ?
|
|
1784
|
-
AND
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1788
|
+
AND (m.source_entity_type IS NOT 'task-attachment'
|
|
1789
|
+
OR m.source_entity_id IN (SELECT id FROM task_attachments WHERE task_id = ?))`,
|
|
1790
|
+
[task.id, task.id],
|
|
1791
|
+
);
|
|
1792
|
+
// The namespace is metadata, and lastUpdatedAt tracks task activity, as in updateTaskTitle.
|
|
1793
|
+
await tx.run('UPDATE agent_tasks SET "key" = ?, updated_by = ? WHERE id = ?', [
|
|
1794
|
+
key,
|
|
1795
|
+
input.changedBy ?? null,
|
|
1796
|
+
task.id,
|
|
1797
|
+
]);
|
|
1798
|
+
for (const mapping of mappedFiles) {
|
|
1799
|
+
if (mapping.key === key) continue;
|
|
1800
|
+
await tx.run(
|
|
1801
|
+
'UPDATE asset_key_mappings SET "key" = ?, updated_at = ?, updated_by = ? WHERE id = ?',
|
|
1802
|
+
[key, now, input.changedBy ?? null, mapping.id],
|
|
1803
|
+
);
|
|
1804
|
+
await insertAssetKeyHistory({
|
|
1805
|
+
entityType: "file",
|
|
1806
|
+
entityId: mapping.id,
|
|
1807
|
+
previousKey: mapping.key,
|
|
1808
|
+
newKey: key,
|
|
1809
|
+
changedBy: input.changedBy,
|
|
1810
|
+
});
|
|
1811
|
+
}
|
|
1793
1812
|
await insertAssetKeyHistory({
|
|
1794
|
-
entityType: "
|
|
1795
|
-
entityId:
|
|
1796
|
-
previousKey:
|
|
1813
|
+
entityType: "task",
|
|
1814
|
+
entityId: task.id,
|
|
1815
|
+
previousKey: task.key,
|
|
1797
1816
|
newKey: key,
|
|
1798
1817
|
changedBy: input.changedBy,
|
|
1799
1818
|
});
|
|
1819
|
+
movedTaskIds.push(task.id);
|
|
1800
1820
|
}
|
|
1801
|
-
|
|
1821
|
+
return { movedTaskIds };
|
|
1802
1822
|
}
|
|
1803
1823
|
case "workflow":
|
|
1804
1824
|
await tx.run(
|
|
@@ -1849,8 +1869,8 @@ export async function moveAssetKey(input: {
|
|
|
1849
1869
|
newKey: key,
|
|
1850
1870
|
changedBy: input.changedBy,
|
|
1851
1871
|
});
|
|
1872
|
+
return {};
|
|
1852
1873
|
});
|
|
1853
|
-
return true;
|
|
1854
1874
|
}
|
|
1855
1875
|
|
|
1856
1876
|
export async function deleteTaskAttachment(id: string): Promise<boolean> {
|
|
@@ -2063,13 +2083,17 @@ export async function getPendingSteeringForTask(taskId: string): Promise<Steerin
|
|
|
2063
2083
|
return await getSteeringMessagesForTask(taskId, { status: "pending" });
|
|
2064
2084
|
}
|
|
2065
2085
|
|
|
2066
|
-
export async function getPendingSteeringForAgent(
|
|
2086
|
+
export async function getPendingSteeringForAgent(
|
|
2087
|
+
agentId: string,
|
|
2088
|
+
readableBy?: { userId: string | null },
|
|
2089
|
+
): Promise<SteeringMessage[]> {
|
|
2090
|
+
const readable = readableBy ? readableSessionCondition(readableBy.userId, "t.id") : undefined;
|
|
2067
2091
|
const rows = await getDbClient().query<TaskSteeringMessageRow>(
|
|
2068
2092
|
`${STEERING_MESSAGE_SELECT}
|
|
2069
2093
|
JOIN agent_tasks t ON t.id = m.task_id
|
|
2070
|
-
WHERE t.agentId = ? AND m.status = 'pending'
|
|
2094
|
+
WHERE t.agentId = ? AND m.status = 'pending'${readable ? ` AND ${readable.sql}` : ""}
|
|
2071
2095
|
ORDER BY m.created_at ASC, m.rowid ASC`,
|
|
2072
|
-
[agentId],
|
|
2096
|
+
[agentId, ...(readable?.params ?? [])],
|
|
2073
2097
|
);
|
|
2074
2098
|
return rows.map(rowToSteeringMessage);
|
|
2075
2099
|
}
|
|
@@ -2167,27 +2191,33 @@ export async function hasPendingSteering(taskId: string): Promise<boolean> {
|
|
|
2167
2191
|
// Combined Queries (Agent with Tasks)
|
|
2168
2192
|
// ============================================================================
|
|
2169
2193
|
|
|
2170
|
-
export async function getAgentWithTasks(
|
|
2194
|
+
export async function getAgentWithTasks(
|
|
2195
|
+
id: string,
|
|
2196
|
+
readableBy?: { userId: string | null },
|
|
2197
|
+
): Promise<AgentWithTasks | null> {
|
|
2171
2198
|
return await getDbClient().transaction(
|
|
2172
2199
|
async () => {
|
|
2173
2200
|
const agent = await getAgentById(id);
|
|
2174
2201
|
if (!agent) return null;
|
|
2175
2202
|
|
|
2176
|
-
const tasks = await getTasksByAgentId(id);
|
|
2203
|
+
const tasks = await getTasksByAgentId(id, readableBy);
|
|
2177
2204
|
return { ...agent, tasks };
|
|
2178
2205
|
},
|
|
2179
2206
|
{ readOnly: true },
|
|
2180
2207
|
);
|
|
2181
2208
|
}
|
|
2182
2209
|
|
|
2183
|
-
export async function getAllAgentsWithTasks(opts?: {
|
|
2210
|
+
export async function getAllAgentsWithTasks(opts?: {
|
|
2211
|
+
slim?: boolean;
|
|
2212
|
+
readableBy?: { userId: string | null };
|
|
2213
|
+
}): Promise<AgentWithTasks[]> {
|
|
2184
2214
|
return await getDbClient().transaction(
|
|
2185
2215
|
async () => {
|
|
2186
2216
|
const agents = await getAllAgents({ slim: opts?.slim ?? false });
|
|
2187
2217
|
return await Promise.all(
|
|
2188
2218
|
agents.map(async (agent) => ({
|
|
2189
2219
|
...agent,
|
|
2190
|
-
tasks: await getTasksByAgentId(agent.id),
|
|
2220
|
+
tasks: await getTasksByAgentId(agent.id, opts?.readableBy),
|
|
2191
2221
|
})),
|
|
2192
2222
|
);
|
|
2193
2223
|
},
|
|
@@ -2250,10 +2280,16 @@ export async function createLogEntry(entry: {
|
|
|
2250
2280
|
return rowToAgentLog(row);
|
|
2251
2281
|
}
|
|
2252
2282
|
|
|
2253
|
-
export async function getLogsByAgentId(
|
|
2283
|
+
export async function getLogsByAgentId(
|
|
2284
|
+
agentId: string,
|
|
2285
|
+
readableBy?: { userId: string | null },
|
|
2286
|
+
): Promise<AgentLog[]> {
|
|
2287
|
+
const readable = readableBy
|
|
2288
|
+
? readableSessionCondition(readableBy.userId, "agent_log.taskId", { nullable: true })
|
|
2289
|
+
: undefined;
|
|
2254
2290
|
const rows = await getDbClient().query<AgentLogRow>(
|
|
2255
|
-
|
|
2256
|
-
[agentId],
|
|
2291
|
+
`SELECT * FROM agent_log WHERE agentId = ?${readable ? ` AND ${readable.sql}` : ""} ORDER BY createdAt DESC`,
|
|
2292
|
+
[agentId, ...(readable?.params ?? [])],
|
|
2257
2293
|
);
|
|
2258
2294
|
return rows.map(rowToAgentLog);
|
|
2259
2295
|
}
|
|
@@ -2297,16 +2333,24 @@ export async function getLogsByEventType(eventType: AgentLogEventType): Promise<
|
|
|
2297
2333
|
return rows.map(rowToAgentLog);
|
|
2298
2334
|
}
|
|
2299
2335
|
|
|
2300
|
-
export async function getAllLogs(
|
|
2336
|
+
export async function getAllLogs(
|
|
2337
|
+
limit?: number,
|
|
2338
|
+
readableBy?: { userId: string | null },
|
|
2339
|
+
): Promise<AgentLog[]> {
|
|
2340
|
+
const readable = readableBy
|
|
2341
|
+
? readableSessionCondition(readableBy.userId, "agent_log.taskId", { nullable: true })
|
|
2342
|
+
: undefined;
|
|
2343
|
+
const readableParams = readable?.params ?? [];
|
|
2301
2344
|
if (limit) {
|
|
2302
2345
|
const rows = await getDbClient().query<AgentLogRow>(
|
|
2303
|
-
|
|
2304
|
-
[limit],
|
|
2346
|
+
`SELECT * FROM agent_log WHERE eventType != 'agent_status_change'${readable ? ` AND ${readable.sql}` : ""} ORDER BY createdAt DESC LIMIT ?`,
|
|
2347
|
+
[...readableParams, limit],
|
|
2305
2348
|
);
|
|
2306
2349
|
return rows.map(rowToAgentLog);
|
|
2307
2350
|
}
|
|
2308
2351
|
const rows = await getDbClient().query<AgentLogRow>(
|
|
2309
|
-
|
|
2352
|
+
`SELECT * FROM agent_log${readable ? ` WHERE ${readable.sql}` : ""} ORDER BY createdAt DESC`,
|
|
2353
|
+
readableParams,
|
|
2310
2354
|
);
|
|
2311
2355
|
return rows.map(rowToAgentLog);
|
|
2312
2356
|
}
|
|
@@ -5802,8 +5846,13 @@ export interface ConcurrentContext {
|
|
|
5802
5846
|
* Returns processing inbox messages, recent task delegations by leads,
|
|
5803
5847
|
* and currently active (in-progress) tasks across the swarm.
|
|
5804
5848
|
*/
|
|
5805
|
-
export async function getConcurrentContext(
|
|
5849
|
+
export async function getConcurrentContext(readableBy?: {
|
|
5850
|
+
userId: string | null;
|
|
5851
|
+
}): Promise<ConcurrentContext> {
|
|
5806
5852
|
const client = getDbClient();
|
|
5853
|
+
const readable = readableBy ? readableSessionCondition(readableBy.userId, "t.id") : undefined;
|
|
5854
|
+
const readableSql = readable ? ` AND ${readable.sql}` : "";
|
|
5855
|
+
const readableParams = readable?.params ?? [];
|
|
5807
5856
|
|
|
5808
5857
|
// 1. Inbox messages currently being processed (status = 'processing')
|
|
5809
5858
|
const processingInboxMessages = await client.query<{
|
|
@@ -5832,9 +5881,9 @@ export async function getConcurrentContext(): Promise<ConcurrentContext> {
|
|
|
5832
5881
|
FROM agent_tasks t
|
|
5833
5882
|
LEFT JOIN agents a ON t.agentId = a.id
|
|
5834
5883
|
WHERE t.createdAt > ?
|
|
5835
|
-
AND t.creatorAgentId IN (SELECT id FROM agents WHERE isLead = 1)
|
|
5884
|
+
AND t.creatorAgentId IN (SELECT id FROM agents WHERE isLead = 1)${readableSql}
|
|
5836
5885
|
ORDER BY t.createdAt DESC`,
|
|
5837
|
-
[fiveMinutesAgo],
|
|
5886
|
+
[fiveMinutesAgo, ...readableParams],
|
|
5838
5887
|
);
|
|
5839
5888
|
|
|
5840
5889
|
// 3. Currently in-progress tasks across the swarm
|
|
@@ -5850,8 +5899,9 @@ export async function getConcurrentContext(): Promise<ConcurrentContext> {
|
|
|
5850
5899
|
`SELECT t.id, t.task, t.agentId, a.name as agentName, t.status, t.createdAt, t.progress
|
|
5851
5900
|
FROM agent_tasks t
|
|
5852
5901
|
LEFT JOIN agents a ON t.agentId = a.id
|
|
5853
|
-
WHERE t.status = 'in_progress'
|
|
5902
|
+
WHERE t.status = 'in_progress'${readableSql}
|
|
5854
5903
|
ORDER BY t.createdAt DESC`,
|
|
5904
|
+
readableParams,
|
|
5855
5905
|
);
|
|
5856
5906
|
|
|
5857
5907
|
return {
|
|
@@ -7139,15 +7189,27 @@ export async function deleteActiveSessionById(id: string): Promise<boolean> {
|
|
|
7139
7189
|
return result.changes > 0;
|
|
7140
7190
|
}
|
|
7141
7191
|
|
|
7142
|
-
export async function getActiveSessions(
|
|
7192
|
+
export async function getActiveSessions(
|
|
7193
|
+
agentId?: string,
|
|
7194
|
+
readableBy?: { userId: string | null },
|
|
7195
|
+
): Promise<ActiveSession[]> {
|
|
7196
|
+
const conditions: string[] = [];
|
|
7197
|
+
const params: string[] = [];
|
|
7143
7198
|
if (agentId) {
|
|
7144
|
-
|
|
7145
|
-
|
|
7146
|
-
[agentId],
|
|
7147
|
-
);
|
|
7199
|
+
conditions.push("agentId = ?");
|
|
7200
|
+
params.push(agentId);
|
|
7148
7201
|
}
|
|
7202
|
+
if (readableBy) {
|
|
7203
|
+
const readable = readableSessionCondition(readableBy.userId, "active_sessions.taskId", {
|
|
7204
|
+
nullable: true,
|
|
7205
|
+
});
|
|
7206
|
+
conditions.push(readable.sql);
|
|
7207
|
+
params.push(...readable.params);
|
|
7208
|
+
}
|
|
7209
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
7149
7210
|
return await getDbClient().query<ActiveSession>(
|
|
7150
|
-
|
|
7211
|
+
`SELECT * FROM active_sessions ${where} ORDER BY startedAt DESC`,
|
|
7212
|
+
params,
|
|
7151
7213
|
);
|
|
7152
7214
|
}
|
|
7153
7215
|
|
|
@@ -12513,6 +12575,7 @@ interface InboxItemStateRow {
|
|
|
12513
12575
|
dismissedAt: string | null;
|
|
12514
12576
|
doneAt: string | null;
|
|
12515
12577
|
readAt: string | null;
|
|
12578
|
+
lastReadAt: string | null;
|
|
12516
12579
|
createdAt: string;
|
|
12517
12580
|
lastUpdatedAt: string;
|
|
12518
12581
|
}
|
|
@@ -12528,6 +12591,7 @@ function rowToInboxItemState(row: InboxItemStateRow): InboxItemState {
|
|
|
12528
12591
|
dismissedAt: row.dismissedAt ?? undefined,
|
|
12529
12592
|
doneAt: row.doneAt ?? undefined,
|
|
12530
12593
|
readAt: row.readAt ?? undefined,
|
|
12594
|
+
lastReadAt: row.lastReadAt ?? undefined,
|
|
12531
12595
|
createdAt: row.createdAt,
|
|
12532
12596
|
lastUpdatedAt: row.lastUpdatedAt,
|
|
12533
12597
|
};
|
|
@@ -12568,6 +12632,7 @@ export async function upsertInboxState(opts: {
|
|
|
12568
12632
|
doneAt?: string;
|
|
12569
12633
|
/** First-viewed timestamp. Sticky: once set, later calls never clear or move it. */
|
|
12570
12634
|
readAt?: string;
|
|
12635
|
+
lastReadAt?: string | null;
|
|
12571
12636
|
}): Promise<InboxItemState> {
|
|
12572
12637
|
const now = new Date().toISOString();
|
|
12573
12638
|
// Auto-derive timestamps from status when not explicitly provided.
|
|
@@ -12579,14 +12644,15 @@ export async function upsertInboxState(opts: {
|
|
|
12579
12644
|
// SQLite upsert via UNIQUE(userId, itemType, itemId). readAt is sticky: the
|
|
12580
12645
|
// first non-null value written wins on every subsequent conflict.
|
|
12581
12646
|
const row = await getDbClient().get<InboxItemStateRow>(
|
|
12582
|
-
`INSERT INTO inbox_item_state (userId, itemType, itemId, status, snoozeUntil, dismissedAt, doneAt, readAt, createdAt, lastUpdatedAt)
|
|
12583
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
12647
|
+
`INSERT INTO inbox_item_state (userId, itemType, itemId, status, snoozeUntil, dismissedAt, doneAt, readAt, lastReadAt, createdAt, lastUpdatedAt)
|
|
12648
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
12584
12649
|
ON CONFLICT(userId, itemType, itemId) DO UPDATE SET
|
|
12585
12650
|
status = excluded.status,
|
|
12586
12651
|
snoozeUntil = excluded.snoozeUntil,
|
|
12587
12652
|
dismissedAt = excluded.dismissedAt,
|
|
12588
12653
|
doneAt = excluded.doneAt,
|
|
12589
12654
|
readAt = COALESCE(inbox_item_state.readAt, excluded.readAt),
|
|
12655
|
+
lastReadAt = CASE WHEN ? THEN excluded.lastReadAt ELSE inbox_item_state.lastReadAt END,
|
|
12590
12656
|
lastUpdatedAt = excluded.lastUpdatedAt
|
|
12591
12657
|
RETURNING *`,
|
|
12592
12658
|
[
|
|
@@ -12598,8 +12664,10 @@ export async function upsertInboxState(opts: {
|
|
|
12598
12664
|
dismissedAt,
|
|
12599
12665
|
doneAt,
|
|
12600
12666
|
readAt,
|
|
12667
|
+
opts.lastReadAt ?? null,
|
|
12601
12668
|
now,
|
|
12602
12669
|
now,
|
|
12670
|
+
opts.lastReadAt !== undefined ? 1 : 0,
|
|
12603
12671
|
],
|
|
12604
12672
|
);
|
|
12605
12673
|
if (!row) throw new Error("Failed to upsert inbox state");
|
|
@@ -12881,6 +12949,8 @@ interface ListRecentSessionsOpts {
|
|
|
12881
12949
|
contextKeyPrefix?: string;
|
|
12882
12950
|
/** When true, return slim `SessionListItemSummary` rows (default: full). */
|
|
12883
12951
|
slim?: boolean;
|
|
12952
|
+
/** Restrict to sessions this viewer may open (session sharing). */
|
|
12953
|
+
viewer?: SessionViewer;
|
|
12884
12954
|
}
|
|
12885
12955
|
|
|
12886
12956
|
export async function listRecentSessions(
|
|
@@ -12899,7 +12969,7 @@ export async function listRecentSessions(
|
|
|
12899
12969
|
const requestedByUserId = opts?.requestedByUserId?.trim() || undefined;
|
|
12900
12970
|
|
|
12901
12971
|
const conditions: string[] = ["r.parentTaskId IS NULL"];
|
|
12902
|
-
const params: (string | number)[] = [];
|
|
12972
|
+
const params: (string | number | null)[] = [];
|
|
12903
12973
|
|
|
12904
12974
|
if (sources.length > 0) {
|
|
12905
12975
|
conditions.push(`r.source IN (${sources.map(() => "?").join(", ")})`);
|
|
@@ -12918,6 +12988,11 @@ export async function listRecentSessions(
|
|
|
12918
12988
|
conditions.push(`r.contextKey LIKE ? ESCAPE '\\'`);
|
|
12919
12989
|
params.push(literalPrefixPattern(opts.contextKeyPrefix));
|
|
12920
12990
|
}
|
|
12991
|
+
if (opts?.viewer) {
|
|
12992
|
+
const viewer = sessionViewerCondition(opts.viewer);
|
|
12993
|
+
conditions.push(viewer.sql);
|
|
12994
|
+
params.push(...viewer.params);
|
|
12995
|
+
}
|
|
12921
12996
|
params.push(limit, offset);
|
|
12922
12997
|
|
|
12923
12998
|
const rootRows = await getDbClient().query<
|
|
@@ -12989,35 +13064,43 @@ export async function listRecentSessions(
|
|
|
12989
13064
|
* a plain count, no recursive chain walk needed.
|
|
12990
13065
|
*/
|
|
12991
13066
|
export async function countSessions(
|
|
12992
|
-
opts?: Pick<
|
|
13067
|
+
opts?: Pick<
|
|
13068
|
+
ListRecentSessionsOpts,
|
|
13069
|
+
"source" | "q" | "requestedByUserId" | "contextKeyPrefix" | "viewer"
|
|
13070
|
+
>,
|
|
12993
13071
|
): Promise<number> {
|
|
12994
13072
|
const sources = opts?.source?.filter((s) => s.length > 0) ?? [];
|
|
12995
13073
|
const q = opts?.q?.trim();
|
|
12996
13074
|
const requestedByUserId = opts?.requestedByUserId?.trim() || undefined;
|
|
12997
13075
|
|
|
12998
|
-
const conditions: string[] = ["parentTaskId IS NULL"];
|
|
12999
|
-
const params: string[] = [];
|
|
13076
|
+
const conditions: string[] = ["r.parentTaskId IS NULL"];
|
|
13077
|
+
const params: (string | null)[] = [];
|
|
13000
13078
|
|
|
13001
13079
|
if (sources.length > 0) {
|
|
13002
|
-
conditions.push(`source IN (${sources.map(() => "?").join(", ")})`);
|
|
13080
|
+
conditions.push(`r.source IN (${sources.map(() => "?").join(", ")})`);
|
|
13003
13081
|
params.push(...sources);
|
|
13004
13082
|
}
|
|
13005
13083
|
if (q && q.length > 0) {
|
|
13006
|
-
conditions.push("(lower(task) LIKE ? OR lower(COALESCE(title, '')) LIKE ?)");
|
|
13084
|
+
conditions.push("(lower(r.task) LIKE ? OR lower(COALESCE(r.title, '')) LIKE ?)");
|
|
13007
13085
|
const like = `%${q.toLowerCase()}%`;
|
|
13008
13086
|
params.push(like, like);
|
|
13009
13087
|
}
|
|
13010
13088
|
if (requestedByUserId) {
|
|
13011
|
-
conditions.push("requestedByUserId = ?");
|
|
13089
|
+
conditions.push("r.requestedByUserId = ?");
|
|
13012
13090
|
params.push(requestedByUserId);
|
|
13013
13091
|
}
|
|
13014
13092
|
if (opts?.contextKeyPrefix) {
|
|
13015
|
-
conditions.push(`contextKey LIKE ? ESCAPE '\\'`);
|
|
13093
|
+
conditions.push(`r.contextKey LIKE ? ESCAPE '\\'`);
|
|
13016
13094
|
params.push(literalPrefixPattern(opts.contextKeyPrefix));
|
|
13017
13095
|
}
|
|
13096
|
+
if (opts?.viewer) {
|
|
13097
|
+
const viewer = sessionViewerCondition(opts.viewer);
|
|
13098
|
+
conditions.push(viewer.sql);
|
|
13099
|
+
params.push(...viewer.params);
|
|
13100
|
+
}
|
|
13018
13101
|
|
|
13019
13102
|
const row = await getDbClient().get<{ count: number }>(
|
|
13020
|
-
`SELECT COUNT(*) AS count FROM agent_tasks WHERE ${conditions.join(" AND ")}`,
|
|
13103
|
+
`SELECT COUNT(*) AS count FROM agent_tasks r WHERE ${conditions.join(" AND ")}`,
|
|
13021
13104
|
params,
|
|
13022
13105
|
);
|
|
13023
13106
|
return row?.count ?? 0;
|
package/src/be/events.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { EventCategory, EventName, EventSource, EventStatus, SwarmEvent } from "../types";
|
|
2
2
|
import { type ScrubbedText, scrubObject, scrubSecrets } from "../utils/secret-scrubber";
|
|
3
3
|
import { getDbClient } from "./db";
|
|
4
|
+
import { readableSessionCondition } from "./db/tasks/read";
|
|
4
5
|
import type { DbExecutor } from "./db-client";
|
|
5
6
|
import { breaksJsonValidity } from "./scrub-json";
|
|
6
7
|
import { recordSkillInvocation } from "./skill-invocations";
|
|
@@ -262,10 +263,20 @@ export async function getEventsFiltered(filters: {
|
|
|
262
263
|
* the agent's event history.
|
|
263
264
|
*/
|
|
264
265
|
latestPerDataField?: { events: EventName[]; dataFields: string[] };
|
|
266
|
+
/** Only events outside sessions this user may not read; `userId: null` is a guest. */
|
|
267
|
+
readableBy?: { userId: string | null };
|
|
265
268
|
}): Promise<SwarmEvent[]> {
|
|
266
269
|
const conditions: string[] = [];
|
|
267
270
|
const params: (string | number)[] = [];
|
|
268
271
|
|
|
272
|
+
if (filters.readableBy) {
|
|
273
|
+
const readable = readableSessionCondition(filters.readableBy.userId, "events.taskId", {
|
|
274
|
+
nullable: true,
|
|
275
|
+
});
|
|
276
|
+
conditions.push(readable.sql);
|
|
277
|
+
params.push(...readable.params);
|
|
278
|
+
}
|
|
279
|
+
|
|
269
280
|
if (filters.category) {
|
|
270
281
|
conditions.push("category = ?");
|
|
271
282
|
params.push(filters.category);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ALTER TABLE inbox_item_state ADD COLUMN lastReadAt TEXT;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
-- Session sharing. A session is a root task (parentTaskId IS NULL) plus its
|
|
2
|
+
-- descendants. A root with no active row stays private to its owner
|
|
3
|
+
-- (agent_tasks.requestedByUserId); a root with no owner stays open to all.
|
|
4
|
+
--
|
|
5
|
+
-- Rows are never deleted: revoking sets revokedAt/revokedBy, and an access
|
|
6
|
+
-- change revokes the active row and inserts a new one in one transaction, so
|
|
7
|
+
-- every row for a root is its share history. userId NULL means everyone.
|
|
8
|
+
CREATE TABLE session_shares (
|
|
9
|
+
id TEXT PRIMARY KEY,
|
|
10
|
+
rootTaskId TEXT NOT NULL REFERENCES agent_tasks(id) ON DELETE CASCADE,
|
|
11
|
+
userId TEXT REFERENCES users(id),
|
|
12
|
+
access TEXT NOT NULL CHECK (access IN ('read', 'write')),
|
|
13
|
+
createdAt TEXT NOT NULL,
|
|
14
|
+
revokedAt TEXT,
|
|
15
|
+
revokedBy TEXT REFERENCES users(id),
|
|
16
|
+
created_by TEXT REFERENCES users(id),
|
|
17
|
+
updated_by TEXT REFERENCES users(id)
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
-- At most one active row per (root, target); revoked rows pile up beside it.
|
|
21
|
+
CREATE UNIQUE INDEX session_shares_active
|
|
22
|
+
ON session_shares(rootTaskId, COALESCE(userId, '*')) WHERE revokedAt IS NULL;
|
|
23
|
+
CREATE INDEX session_shares_user ON session_shares(userId) WHERE revokedAt IS NULL;
|
package/src/be/rbac-roles.ts
CHANGED
|
@@ -138,8 +138,7 @@ async function buildState(ctx: any, leadId: string, nowMs: number, timeZone: str
|
|
|
138
138
|
// db_query_concurrency_cap on a busy swarm.
|
|
139
139
|
const lead = await q("SELECT heartbeatMd FROM agents WHERE id = ?", [leadId]);
|
|
140
140
|
const prev = await q(
|
|
141
|
-
`SELECT id, substr(output, 1, 400) AS output
|
|
142
|
-
substr(task, 1, instr(task, '## Your Standing Orders')) AS status
|
|
141
|
+
`SELECT id, substr(output, 1, 400) AS output
|
|
143
142
|
FROM agent_tasks
|
|
144
143
|
WHERE agentId = ? AND taskType = 'heartbeat-checklist' AND status = 'completed'
|
|
145
144
|
ORDER BY createdAt DESC LIMIT 1`,
|
|
@@ -163,9 +162,28 @@ async function buildState(ctx: any, leadId: string, nowMs: number, timeZone: str
|
|
|
163
162
|
"SELECT status, COUNT(*) AS n FROM agents WHERE COALESCE(role, '') != 'extension' GROUP BY status",
|
|
164
163
|
);
|
|
165
164
|
|
|
165
|
+
// Only the producer's receipt proves which full IDs the checklist actually showed.
|
|
166
|
+
// Old checklists and invalid receipts conservatively treat all current failures as new.
|
|
167
|
+
let reportedIds: unknown = [];
|
|
168
|
+
if (prev[0]?.id) {
|
|
169
|
+
try {
|
|
170
|
+
const receipt = await ctx.swarm.kv_getOrNull({
|
|
171
|
+
namespace: "heartbeat-checklist:reported-failures",
|
|
172
|
+
key: prev[0].id,
|
|
173
|
+
});
|
|
174
|
+
reportedIds = typeof receipt?.value === "string" ? JSON.parse(receipt.value) : receipt?.value;
|
|
175
|
+
} catch {
|
|
176
|
+
reportedIds = [];
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
const prevIds = new Set(
|
|
180
|
+
Array.isArray(reportedIds) && reportedIds.every((id) => typeof id === "string") ? reportedIds : [],
|
|
181
|
+
);
|
|
166
182
|
const fails = parseFailures(failed.map(renderFailureLine).join("\n"));
|
|
167
|
-
|
|
168
|
-
const newFails =
|
|
183
|
+
// Compare full IDs before rendering the replay's eight-character display IDs.
|
|
184
|
+
const newFails = parseFailures(
|
|
185
|
+
failed.filter((f) => !prevIds.has(f.id)).map(renderFailureLine).join("\n"),
|
|
186
|
+
);
|
|
169
187
|
|
|
170
188
|
const { stamp, zone } = localStamp(nowMs, timeZone);
|
|
171
189
|
const mmdd = new Date(nowMs).toISOString().slice(5, 10);
|