@desplega.ai/agent-swarm 2.7.0 → 2.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{acp-adapter-ma1js5n7.js → acp-adapter-8dn5tq56.js} +6 -6
- package/dist/{actions-1vhmtvg9.js → actions-3r6gw7dy.js} +18 -18
- package/dist/{amp-adapter-sxqqvmdk.js → amp-adapter-wjtq413p.js} +3 -3
- package/dist/{anthropic-messages-txs190dd.js → anthropic-messages-n4cby1rz.js} +4 -4
- package/dist/{app-mwx1d5a9.js → app-d53yhykk.js} +3 -3
- package/dist/{assistant-yx1p6g82.js → assistant-ksrawj3k.js} +20 -20
- package/dist/{boot-reembed-7h78vmvv.js → boot-reembed-fxfw17m2.js} +10 -3
- package/dist/{boot-reembed-5h37a2c2.js → boot-reembed-k5y5xbj0.js} +15 -8
- package/dist/{boot-scrub-sweep-8mawd4gw.js → boot-scrub-sweep-se41j9fd.js} +3 -3
- package/dist/{boot-seeds-ca84dhnn.js → boot-seeds-r3kwwv8b.js} +4 -4
- package/dist/{claude-adapter-wqt4hfkq.js → claude-adapter-ahmyf231.js} +7 -7
- package/dist/{claude-managed-adapter-6fjqtr2z.js → claude-managed-adapter-5knamegc.js} +3 -3
- package/dist/{claude-managed-setup-rqv98sea.js → claude-managed-setup-mdc7dy6b.js} +2 -2
- package/dist/{claude-sdk-session-f29f3b2x.js → claude-sdk-session-n5wen9zk.js} +8728 -8599
- package/dist/{cli-m5e802r7.js → cli-0dqtv2jg.js} +2 -2
- package/dist/{cli-e1595bgx.js → cli-0tww7dnz.js} +2 -2
- package/dist/{cli-2y7v133v.js → cli-0w4e4v58.js} +7 -7
- package/dist/{cli-d20qvpr2.js → cli-2sjm3t4r.js} +1 -1
- package/dist/{cli-gf2850q7.js → cli-3cywb44q.js} +18 -1
- package/dist/{cli-8wkb09q5.js → cli-3k9jej0c.js} +2 -2
- package/dist/{cli-twctnkng.js → cli-3v2cyqb8.js} +3 -3
- package/dist/{cli-he9gbdx0.js → cli-5qt9vzmq.js} +39 -20
- package/dist/{cli-a6ds2epe.js → cli-5rp1w4cr.js} +1 -1
- package/dist/{cli-6srv653c.js → cli-6vwjsetx.js} +4 -4
- package/dist/{cli-1hhbzkbb.js → cli-6w2yrrc0.js} +8 -8
- package/dist/{cli-f12mr20z.js → cli-75tdydgv.js} +1 -1
- package/dist/{cli-j3y0x2n4.js → cli-8feswf2v.js} +43 -33
- package/dist/{cli-07q03xm2.js → cli-8wd3mbyj.js} +1 -1
- package/dist/{cli-ymjpkxgn.js → cli-9csfthbw.js} +8 -6
- package/dist/{cli-f1e0ptjj.js → cli-b41p764a.js} +21 -2
- package/dist/{cli-kv7sbc2c.js → cli-c1t533hh.js} +3 -3
- package/dist/{cli-54sj64fc.js → cli-c28jxpcv.js} +1 -1
- package/dist/{cli-8mbc9za9.js → cli-cb50g9cd.js} +1 -1
- package/dist/{cli-c668vvcf.js → cli-d8gaphtr.js} +1 -1
- package/dist/{cli-86ehn4s5.js → cli-dgtfysne.js} +2 -2
- package/dist/{cli-sjctjr48.js → cli-dscnhn18.js} +1 -1
- package/dist/{cli-j0ppywdr.js → cli-dv11pgpv.js} +12 -12
- package/dist/{cli-1m9x8dr5.js → cli-ekptgk2m.js} +5 -5
- package/dist/{cli-9gsm2rzp.js → cli-f3mzy662.js} +17 -17
- package/dist/{cli-s0mgx214.js → cli-fk7k8s9x.js} +2 -2
- package/dist/{cli-vbhgkw9h.js → cli-h3e7bhvn.js} +3 -3
- package/dist/{cli-yd2nreqw.js → cli-hkxqdmnb.js} +2 -2
- package/dist/{cli-8wmj18cz.js → cli-hpkvgdad.js} +7 -4
- package/dist/{cli-wqr7cea4.js → cli-hq2v4rr5.js} +37 -9
- package/dist/{cli-sgqm2582.js → cli-k1zb7raw.js} +5 -5
- package/dist/{cli-4rvr7zfx.js → cli-k4awgvzm.js} +5 -0
- package/dist/{cli-ay40dnj3.js → cli-k7d3hvya.js} +2 -2
- package/dist/{cli-j9bpmykh.js → cli-kbtdftbw.js} +5 -5
- package/dist/{cli-x37r7qwf.js → cli-kj69b9tw.js} +25 -5
- package/dist/{cli-gsfk58nw.js → cli-kk9jy4nm.js} +1 -1
- package/dist/{cli-39aet655.js → cli-m52t4cta.js} +1 -1
- package/dist/{cli-h5jeg46p.js → cli-mdadewqp.js} +5 -5
- package/dist/{cli-a89zdqz9.js → cli-nf6k60sn.js} +1 -1
- package/dist/{cli-v6shg7na.js → cli-nzdf09js.js} +1 -1
- package/dist/{cli-rr7zx19j.js → cli-prcyzecg.js} +12 -12
- package/dist/{cli-6a9qfa8b.js → cli-pzafyqtk.js} +118 -48
- package/dist/{cli-48gxjmcz.js → cli-qvaa1gjp.js} +87 -24
- package/dist/{cli-qj442fws.js → cli-rg4b2g6j.js} +1 -1
- package/dist/{cli-aypd09nf.js → cli-rm66vqdz.js} +1 -1
- package/dist/{cli-9pshx6rd.js → cli-s504c3w7.js} +1 -1
- package/dist/{cli-ptfq202x.js → cli-swkx9jm2.js} +3 -3
- package/dist/{cli-m30k9777.js → cli-thsaq4pd.js} +16 -16
- package/dist/{cli-60sty27p.js → cli-twjnyvs8.js} +2 -2
- package/dist/{cli-rd5m70gj.js → cli-typdpe8g.js} +4 -4
- package/dist/{cli-yxbsmx56.js → cli-vg8pwmsd.js} +6 -6
- package/dist/{cli-s09j9vp5.js → cli-vhpckzvp.js} +1 -1
- package/dist/{cli-dv97kdzv.js → cli-wdxgsdz5.js} +2 -2
- package/dist/{cli-7a6p4e65.js → cli-wtjg6je3.js} +1 -1
- package/dist/{cli-b3yzyc57.js → cli-x3cngb1h.js} +44 -12
- package/dist/{cli-6mgdba42.js → cli-xbz6mcg4.js} +2 -2
- package/dist/{cli-2pvzmas1.js → cli-y4tdxsqb.js} +3 -3
- package/dist/{cli-f4dbh7a5.js → cli-yqrsp3ny.js} +7 -7
- package/dist/cli.js +27 -19
- package/dist/{codex-adapter-zk59x2zh.js → codex-adapter-7wepzq15.js} +5 -5
- package/dist/{codex-hook-xyjhvxwd.js → codex-hook-pbdh4xhx.js} +2 -2
- package/dist/{codex-session-runner-kh6s4j3n.js → codex-session-runner-02sb49e4.js} +5 -5
- package/dist/{commands-68et1qv2.js → commands-3rcspxct.js} +2 -2
- package/dist/{controller-fyrbnzg4.js → controller-8ar4avd6.js} +3 -3
- package/dist/{controllers-connect-nep9h2xv.js → controllers-connect-ajvh9h0v.js} +19 -19
- package/dist/{cursor-adapter-yss3pg7t.js → cursor-adapter-gd0sa7bh.js} +1 -1
- package/dist/{db-28gjyvxj.js → db-dv9j7ts5.js} +2 -2
- package/dist/{db-8ftwvzba.js → db-z26vgdvj.js} +3 -3
- package/dist/{dispatcher-hayexmkr.js → dispatcher-8m0htjq8.js} +17 -17
- package/dist/{dsh-adapter-46wawff9.js → dsh-adapter-y8gyk130.js} +1 -1
- package/dist/{e2b-fn3zacvt.js → e2b-74vg9b7e.js} +1 -1
- package/dist/{e2b-222hsajc.js → e2b-jyay8wzn.js} +1 -1
- package/dist/{easter-egg-3d-8d1e0h4m.js → easter-egg-3d-28f880xx.js} +1 -1
- package/dist/{execute-cjrfxfnm.js → execute-9seb79vh.js} +1 -1
- package/dist/{external-channel-xbmxcg09.js → external-channel-smz7jyth.js} +2 -2
- package/dist/{getMachineId-bsd-brn0xym3.js → getMachineId-bsd-whfre6gr.js} +3 -3
- package/dist/{getMachineId-darwin-t339sfxm.js → getMachineId-darwin-5zjynb19.js} +3 -3
- package/dist/{getMachineId-linux-5eq95d8b.js → getMachineId-linux-8mksrrsq.js} +3 -3
- package/dist/{getMachineId-unsupported-qavrtc7j.js → getMachineId-unsupported-fxtqsdt2.js} +3 -3
- package/dist/{getMachineId-win-q7fe1wx7.js → getMachineId-win-nzfmz857.js} +3 -3
- package/dist/{grok-adapter-ag4wks2a.js → grok-adapter-z0nnvtbn.js} +7 -7
- package/dist/handlers-pspryh6g.js +41 -0
- package/dist/{heartbeat-psfvcgwp.js → heartbeat-f928aqyp.js} +20 -20
- package/dist/{hook-cyq4mryn.js → hook-d7c605r4.js} +7 -7
- package/dist/{hook-8pz04697.js → hook-q9hv5xf9.js} +5 -5
- package/dist/{http-haj1nd6k.js → http-q6j4k989.js} +934 -180
- package/dist/{index-z5h22pes.js → index-5nb266jr.js} +17 -17
- package/dist/{index-ysd45kgc.js → index-8fxt5eah.js} +10 -10
- package/dist/{index-zgvqzjsw.js → index-cekzwhez.js} +5 -5
- package/dist/{index-zyq8ct52.js → index-ch0c1xff.js} +17 -17
- package/dist/{index-syvhnvtw.js → index-ebnjhxh1.js} +1 -1
- package/dist/{index-y1ajx04a.js → index-ed8dy8rn.js} +20 -20
- package/dist/{index-jwt2bj6f.js → index-ejqdkkqx.js} +11 -10
- package/dist/{index-dr94zbnh.js → index-farm1zsn.js} +32661 -29443
- package/dist/{index-q52f47pe.js → index-yn1yxg85.js} +1 -1
- package/dist/{keepalive-xdvsamvj.js → keepalive-zzc3hks1.js} +3 -3
- package/dist/{lead-hqg57kdp.js → lead-c0pjcaw4.js} +36 -36
- package/dist/{maintenance-4y2xzh3a.js → maintenance-kqsxzpzx.js} +1 -1
- package/dist/{oauth-refresh-sweep-jd4r7zbs.js → oauth-refresh-sweep-7qt0g55z.js} +2 -2
- package/dist/{onboard-18447q9q.js → onboard-k7x13fqg.js} +118 -5
- package/dist/{opencode-adapter-26b2frdf.js → opencode-adapter-tqvrtter.js} +5 -5
- package/dist/{otel-impl-f9tgr8yw.js → otel-impl-sqkf6k3b.js} +97 -97
- package/dist/{pi-mono-adapter-qn6qbga1.js → pi-mono-adapter-m2qnafxm.js} +63 -9
- package/dist/{pricing-refresh-dw2tgb53.js → pricing-refresh-wkpwhgvs.js} +3 -3
- package/dist/{rbac-roles-s4nwj2m3.js → rbac-roles-dpn0c0wj.js} +7 -4
- package/dist/{rbac-roles-aygm2760.js → rbac-roles-wk7xd6a6.js} +2 -2
- package/dist/{render-v2-yfvdfr0x.js → render-v2-ex2m4g2k.js} +3 -3
- package/dist/{resume-1bmvjbf3.js → resume-725g9b0b.js} +7 -7
- package/dist/{runtime-f7tpzedy.js → runtime-kajas15g.js} +1 -1
- package/dist/{session-access-r5garej6.js → session-access-0j2ezjje.js} +8 -4
- package/dist/{session-source-backfill-424wzv0r.js → session-source-backfill-62202awe.js} +2 -2
- package/dist/{setup-8tygvxkq.js → setup-9sj11ax6.js} +1 -1
- package/dist/{virtual-modules-xsw323b3.js → virtual-modules-c6fcq6zr.js} +2 -2
- package/dist/{work-objects-ggnghpgf.js → work-objects-0y07495y.js} +20 -20
- package/dist/{worker-sqjd1nqf.js → worker-d4q9sqbc.js} +36 -36
- package/openapi.json +1070 -9
- package/package.json +3 -3
- package/src/be/connector-codes.ts +65 -22
- package/src/be/memory/boot-reembed.ts +4 -1
- package/src/be/memory/constants.ts +6 -0
- package/src/be/memory/embedding-recovery.ts +48 -0
- package/src/be/memory/index-content.ts +5 -2
- package/src/be/memory/providers/openai-embedding.ts +65 -14
- package/src/be/memory/task-learning.ts +110 -8
- package/src/be/memory/types.ts +13 -2
- package/src/be/migrations/246_connector_code_kind.sql +11 -0
- package/src/be/onboarding.ts +125 -15
- package/src/be/rbac-roles.ts +4 -1
- package/src/be/scripts/embeddings.ts +2 -2
- package/src/be/seed/agent-fs-provision.ts +75 -14
- package/src/be/swarm-config-guard.ts +3 -0
- package/src/cli.tsx +9 -0
- package/src/comb/config.ts +6 -1
- package/src/commands/onboard/agent-fs-version.ts +5 -0
- package/src/commands/onboard/compose-generator.ts +112 -1
- package/src/commands/onboard/env-generator.ts +9 -0
- package/src/commands/onboard/tei-image.ts +12 -0
- package/src/commands/onboard/types.ts +7 -0
- package/src/commands/onboard.tsx +11 -1
- package/src/commands/runner.ts +14 -1
- package/src/http/agentfs-proxy.ts +369 -0
- package/src/http/all-routes.ts +1 -0
- package/src/http/codex-oauth-device.ts +8 -1
- package/src/http/fs.ts +64 -10
- package/src/http/inbox-state.ts +27 -1
- package/src/http/index.ts +4 -1
- package/src/http/memory.ts +7 -4
- package/src/http/onboarding.ts +139 -11
- package/src/http/session-access.ts +24 -0
- package/src/http/sessions.ts +10 -6
- package/src/http/tasks.ts +85 -11
- package/src/http/telemetry-ui.ts +1 -1
- package/src/http/user-roles.ts +4 -1
- package/src/http/users.ts +272 -3
- package/src/http/utils.ts +22 -3
- package/src/prompts/base-prompt.ts +24 -9
- package/src/prompts/session-templates.ts +26 -2
- package/src/providers/amp-adapter.ts +1 -1
- package/src/providers/codex-adapter.ts +6 -1
- package/src/providers/cursor-adapter.ts +1 -1
- package/src/providers/pi-mono-adapter.ts +58 -3
- package/src/rbac/legacy-policy.ts +1 -0
- package/src/rbac/permissions.ts +4 -0
- package/src/tests/agent-fs-provision-seeder.test.ts +261 -92
- package/src/tests/agentfs-proxy.test.ts +1106 -0
- package/src/tests/base-prompt.test.ts +69 -2
- package/src/tests/codex-adapter.test.ts +46 -8
- package/src/tests/codex-oauth-device.test.ts +12 -1
- package/src/tests/comb-config-guard.test.ts +20 -0
- package/src/tests/comb-links.test.ts +79 -1
- package/src/tests/comb-status.test.ts +44 -0
- package/src/tests/docs-index-footprint.test.js +3 -3
- package/src/tests/fixtures/onboard/baseline-states.ts +68 -0
- package/src/tests/fixtures/onboard/content.compose.yml.txt +190 -0
- package/src/tests/fixtures/onboard/content.env.txt +29 -0
- package/src/tests/fixtures/onboard/dev.compose.yml.txt +155 -0
- package/src/tests/fixtures/onboard/dev.env.txt +28 -0
- package/src/tests/fixtures/onboard/full-all.compose.yml.txt +582 -0
- package/src/tests/fixtures/onboard/full-all.env.txt +59 -0
- package/src/tests/fixtures/onboard/full.compose.yml.txt +435 -0
- package/src/tests/fixtures/onboard/full.env.txt +36 -0
- package/src/tests/fixtures/onboard/research.compose.yml.txt +155 -0
- package/src/tests/fixtures/onboard/research.env.txt +28 -0
- package/src/tests/fixtures/onboard/solo.compose.yml.txt +85 -0
- package/src/tests/fixtures/onboard/solo.env.txt +26 -0
- package/src/tests/http-log-scrubbing.test.ts +34 -1
- package/src/tests/http-task-write-ownership.test.ts +5 -1
- package/src/tests/invites-http.test.ts +464 -0
- package/src/tests/memory-embedding-input-cap.test.ts +122 -1
- package/src/tests/memory-embedding-recovery.test.ts +137 -0
- package/src/tests/onboard-compose.test.ts +142 -0
- package/src/tests/onboard-env.test.ts +24 -0
- package/src/tests/onboard-non-interactive.test.ts +29 -0
- package/src/tests/onboard-tei-image.test.ts +36 -0
- package/src/tests/onboarding.test.ts +451 -4
- package/src/tests/openai-embedding-provider.test.ts +105 -2
- package/src/tests/pi-mono-session-cost-on-abort.test.ts +270 -0
- package/src/tests/preload-env.test.ts +4 -0
- package/src/tests/preload.ts +4 -0
- package/src/tests/prompt-template-session.test.ts +19 -3
- package/src/tests/rbac-admission-e2e.test.ts +22 -6
- package/src/tests/rbac-admission.test.ts +342 -4
- package/src/tests/rbac-engine.test.ts +1 -0
- package/src/tests/rbac-roles.test.ts +9 -1
- package/src/tests/send-task-requester-gate.test.ts +64 -3
- package/src/tests/sessions-move.test.ts +39 -1
- package/src/tests/store-progress-learning.test.ts +95 -2
- package/src/tests/whoami-route.test.ts +41 -0
- package/src/tools/inject-learning.ts +1 -1
- package/src/tools/memory-edit.ts +1 -1
- package/src/tools/memory-search.ts +1 -1
- package/src/tools/store-progress.ts +5 -1
- package/src/tools/utils.ts +12 -1
- package/src/utils/constants.ts +39 -0
- package/src/utils/task-attachment-links.ts +15 -3
- package/templates/skills/swarm-docs/content.md +1 -1
- package/dist/handlers-tzmevqb5.js +0 -41
- package/dist/{azure-openai-responses-9kfwhmn7.js → azure-openai-responses-43asa23s.js} +0 -0
- package/dist/{node-vxx896fa.js → node-t28cc56b.js} +0 -0
- package/dist/{openai-codex-responses-79s8xnbh.js → openai-codex-responses-jhr3sjtm.js} +3 -3
- package/dist/{openai-completions-tdbyts0x.js → openai-completions-gs6yn37k.js} +3 -3
- /package/dist/{openai-responses-dm3t5vqp.js → openai-responses-jqm9vwwk.js} +0 -0
package/src/be/onboarding.ts
CHANGED
|
@@ -2,7 +2,13 @@ import { telemetry } from "../telemetry";
|
|
|
2
2
|
import { ProviderNameSchema } from "../types";
|
|
3
3
|
import { getCurrentRequestUserId } from "../utils/request-auth-context";
|
|
4
4
|
import { z } from "../utils/zod-openapi";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
deleteSwarmConfigByKey,
|
|
7
|
+
getDbClient,
|
|
8
|
+
getSwarmConfigs,
|
|
9
|
+
getTaskById,
|
|
10
|
+
upsertSwarmConfig,
|
|
11
|
+
} from "./db";
|
|
6
12
|
import { validateConfigValue } from "./swarm-config-guard";
|
|
7
13
|
|
|
8
14
|
const ONBOARDING_CONFIG_KEY = "onboarding_state";
|
|
@@ -14,6 +20,7 @@ export const OnboardingStepIdSchema = z.enum([
|
|
|
14
20
|
"agents",
|
|
15
21
|
"memory",
|
|
16
22
|
"integrations",
|
|
23
|
+
"files",
|
|
17
24
|
"first_task",
|
|
18
25
|
]);
|
|
19
26
|
export type OnboardingStepId = z.infer<typeof OnboardingStepIdSchema>;
|
|
@@ -54,6 +61,19 @@ export const OnboardingMemoryPresetSchema = z.enum([
|
|
|
54
61
|
"custom",
|
|
55
62
|
"existing",
|
|
56
63
|
]);
|
|
64
|
+
/**
|
|
65
|
+
* Where the embeddings endpoint runs: `local` (this server or its private
|
|
66
|
+
* network), the hosted preset whose host it calls, or `custom` (another host).
|
|
67
|
+
*/
|
|
68
|
+
export const OnboardingMemoryProviderSchema = z.enum([
|
|
69
|
+
"local",
|
|
70
|
+
"openai",
|
|
71
|
+
"openrouter",
|
|
72
|
+
"vercel",
|
|
73
|
+
"azure",
|
|
74
|
+
"custom",
|
|
75
|
+
]);
|
|
76
|
+
export type OnboardingMemoryProvider = z.infer<typeof OnboardingMemoryProviderSchema>;
|
|
57
77
|
export const OnboardingIntegrationMethodSchema = z.enum([
|
|
58
78
|
"slack",
|
|
59
79
|
"github",
|
|
@@ -62,6 +82,8 @@ export const OnboardingIntegrationMethodSchema = z.enum([
|
|
|
62
82
|
"linear_oauth",
|
|
63
83
|
"jira_oauth",
|
|
64
84
|
]);
|
|
85
|
+
/** `created`: the Files step registered a new agent-fs account. `connected`: it reused one. */
|
|
86
|
+
export const OnboardingFilesMethodSchema = z.enum(["created", "connected"]);
|
|
65
87
|
export const OnboardingFirstTaskMethodSchema = z.enum(["suggestion", "free_form"]);
|
|
66
88
|
export const OnboardingStepMethodSchema = z.union([
|
|
67
89
|
OnboardingConnectMethodSchema,
|
|
@@ -70,6 +92,7 @@ export const OnboardingStepMethodSchema = z.union([
|
|
|
70
92
|
OnboardingAgentsMethodSchema,
|
|
71
93
|
OnboardingMemoryPresetSchema,
|
|
72
94
|
OnboardingIntegrationMethodSchema,
|
|
95
|
+
OnboardingFilesMethodSchema,
|
|
73
96
|
OnboardingFirstTaskMethodSchema,
|
|
74
97
|
]);
|
|
75
98
|
export type OnboardingStepMethod = z.infer<typeof OnboardingStepMethodSchema>;
|
|
@@ -98,6 +121,7 @@ const OnboardingMemoryStepStateSchema = onboardingStepStateSchema(OnboardingMemo
|
|
|
98
121
|
const OnboardingIntegrationStepStateSchema = onboardingStepStateSchema(
|
|
99
122
|
OnboardingIntegrationMethodSchema,
|
|
100
123
|
);
|
|
124
|
+
const OnboardingFilesStepStateSchema = onboardingStepStateSchema(OnboardingFilesMethodSchema);
|
|
101
125
|
const OnboardingFirstTaskStepStateSchema = onboardingStepStateSchema(
|
|
102
126
|
OnboardingFirstTaskMethodSchema,
|
|
103
127
|
);
|
|
@@ -118,6 +142,7 @@ export const OnboardingStateSchema = z.object({
|
|
|
118
142
|
agents: OnboardingAgentsStepStateSchema,
|
|
119
143
|
memory: OnboardingMemoryStepStateSchema,
|
|
120
144
|
integrations: OnboardingIntegrationStepStateSchema,
|
|
145
|
+
files: OnboardingFilesStepStateSchema,
|
|
121
146
|
first_task: OnboardingFirstTaskStepStateSchema,
|
|
122
147
|
}),
|
|
123
148
|
});
|
|
@@ -139,6 +164,21 @@ export const OnboardingSignalsSchema = z.object({
|
|
|
139
164
|
embeddings: z.object({
|
|
140
165
|
configured: z.boolean(),
|
|
141
166
|
dimensions: z.number().int().positive(),
|
|
167
|
+
/** The embedding model memory search uses. Null while it is off. */
|
|
168
|
+
model: z.string().nullable(),
|
|
169
|
+
/**
|
|
170
|
+
* The endpoint runs on this server or its private network (loopback, a
|
|
171
|
+
* private address, or a single-label host such as `tei`). False while off.
|
|
172
|
+
*/
|
|
173
|
+
local: z.boolean(),
|
|
174
|
+
/** Where the endpoint runs, from its host only (never the URL). Null while off. */
|
|
175
|
+
provider: OnboardingMemoryProviderSchema.nullable(),
|
|
176
|
+
/**
|
|
177
|
+
* The local setup of the deployment environment, also while a stored
|
|
178
|
+
* hosted setup replaces it, so `/setup` can offer it again: the model of
|
|
179
|
+
* its local endpoint. Null when the environment has no local endpoint.
|
|
180
|
+
*/
|
|
181
|
+
localDefault: z.object({ model: z.string() }).nullable(),
|
|
142
182
|
}),
|
|
143
183
|
integrations: z.object({
|
|
144
184
|
slack: z.boolean(),
|
|
@@ -153,6 +193,8 @@ export const OnboardingSignalsSchema = z.object({
|
|
|
153
193
|
workersOnline: z.number().int().nonnegative(),
|
|
154
194
|
}),
|
|
155
195
|
firstTask: z.object({ id: z.string(), status: z.string() }).nullable(),
|
|
196
|
+
/** The Files step applies: Comb is on, so the browser can create an agent-fs account. */
|
|
197
|
+
files: z.object({ enabled: z.boolean() }),
|
|
156
198
|
});
|
|
157
199
|
export type OnboardingSignals = z.infer<typeof OnboardingSignalsSchema>;
|
|
158
200
|
|
|
@@ -198,10 +240,17 @@ export const OnboardingActionSchema = z.union([
|
|
|
198
240
|
method: OnboardingIntegrationMethodSchema,
|
|
199
241
|
})
|
|
200
242
|
.strict(),
|
|
243
|
+
z
|
|
244
|
+
.object({
|
|
245
|
+
action: z.literal("complete"),
|
|
246
|
+
step: z.literal("files"),
|
|
247
|
+
method: OnboardingFilesMethodSchema,
|
|
248
|
+
})
|
|
249
|
+
.strict(),
|
|
201
250
|
z
|
|
202
251
|
.object({
|
|
203
252
|
action: z.literal("skip"),
|
|
204
|
-
step: z.enum(["name", "ai", "agents", "memory", "integrations", "first_task"]),
|
|
253
|
+
step: z.enum(["name", "ai", "agents", "memory", "integrations", "files", "first_task"]),
|
|
205
254
|
})
|
|
206
255
|
.strict(),
|
|
207
256
|
z
|
|
@@ -243,8 +292,18 @@ export const OnboardingMemoryRequestSchema = z
|
|
|
243
292
|
model: z.string().trim().min(1).optional(),
|
|
244
293
|
apiKey: z.string().min(1).optional(),
|
|
245
294
|
reuseKey: z.enum(["OPENAI_API_KEY", "OPENROUTER_API_KEY"]).optional(),
|
|
295
|
+
/**
|
|
296
|
+
* With preset `existing`: test the setup of the deployment environment,
|
|
297
|
+
* and when it works, delete the stored embeddings config so that setup
|
|
298
|
+
* applies again. A failed test changes no config.
|
|
299
|
+
*/
|
|
300
|
+
restoreDefault: z.boolean().optional(),
|
|
246
301
|
})
|
|
247
|
-
.strict()
|
|
302
|
+
.strict()
|
|
303
|
+
.refine((body) => !body.restoreDefault || body.preset === "existing", {
|
|
304
|
+
message: "restoreDefault needs preset existing",
|
|
305
|
+
path: ["restoreDefault"],
|
|
306
|
+
});
|
|
248
307
|
export type OnboardingMemoryRequest = z.infer<typeof OnboardingMemoryRequestSchema>;
|
|
249
308
|
|
|
250
309
|
export const OnboardingMemoryResponseSchema = z.object({
|
|
@@ -320,6 +379,7 @@ function freshState(now: string, autoCompleted: boolean): OnboardingState {
|
|
|
320
379
|
agents: emptyStep(),
|
|
321
380
|
memory: emptyStep(),
|
|
322
381
|
integrations: emptyStep(),
|
|
382
|
+
files: emptyStep(),
|
|
323
383
|
first_task: emptyStep(),
|
|
324
384
|
},
|
|
325
385
|
};
|
|
@@ -535,6 +595,20 @@ function deriveState(
|
|
|
535
595
|
return changed;
|
|
536
596
|
}
|
|
537
597
|
|
|
598
|
+
/**
|
|
599
|
+
* The response view of the state. Without Comb the Files step cannot run, so a
|
|
600
|
+
* stored `todo` reads as `skipped`. Response only: never persisted and never a
|
|
601
|
+
* `step_skipped` event, so installs without Comb send no funnel noise and the
|
|
602
|
+
* step comes back as `todo` once Comb is turned on.
|
|
603
|
+
*/
|
|
604
|
+
function presentState(state: OnboardingState, signals: OnboardingSignals): OnboardingState {
|
|
605
|
+
if (signals.files.enabled || state.steps.files.status !== "todo") return state;
|
|
606
|
+
return {
|
|
607
|
+
...state,
|
|
608
|
+
steps: { ...state.steps, files: { ...state.steps.files, status: "skipped" } },
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
|
|
538
612
|
export class OnboardingTaskNotFoundError extends Error {}
|
|
539
613
|
|
|
540
614
|
async function applyAction(
|
|
@@ -612,7 +686,8 @@ export async function readOrUpdateOnboarding(
|
|
|
612
686
|
const read = await client.transaction(async () => await loadAndDerive(loadSignals, []), {
|
|
613
687
|
readOnly: true,
|
|
614
688
|
});
|
|
615
|
-
if (!read.dirty)
|
|
689
|
+
if (!read.dirty)
|
|
690
|
+
return { state: presentState(read.state, read.signals), signals: read.signals };
|
|
616
691
|
}
|
|
617
692
|
|
|
618
693
|
return await client.transaction(async () => {
|
|
@@ -627,14 +702,43 @@ export async function readOrUpdateOnboarding(
|
|
|
627
702
|
}
|
|
628
703
|
if (dirty) await persistState(state);
|
|
629
704
|
queueTelemetry(events);
|
|
630
|
-
return { state, signals };
|
|
705
|
+
return { state: presentState(state, signals), signals };
|
|
631
706
|
});
|
|
632
707
|
}
|
|
633
708
|
|
|
709
|
+
/** The embeddings rows a memory probe writes or deletes. */
|
|
710
|
+
export const EMBEDDING_PREFIX_KEYS = [
|
|
711
|
+
"EMBEDDING_QUERY_PREFIX",
|
|
712
|
+
"EMBEDDING_DOCUMENT_PREFIX",
|
|
713
|
+
] as const;
|
|
714
|
+
export const EMBEDDING_CONFIG_KEYS = [
|
|
715
|
+
"EMBEDDING_API_BASE_URL",
|
|
716
|
+
"EMBEDDING_MODEL",
|
|
717
|
+
"EMBEDDING_API_KEY",
|
|
718
|
+
...EMBEDDING_PREFIX_KEYS,
|
|
719
|
+
] as const;
|
|
720
|
+
|
|
721
|
+
/**
|
|
722
|
+
* The config change of a passed memory probe:
|
|
723
|
+
* - `save`: store the tested endpoint. `prefixes` sets a task prefix to an
|
|
724
|
+
* empty value (`""`), or deletes its stored row (`null`).
|
|
725
|
+
* - `restore`: delete the stored embeddings rows, so the deployment
|
|
726
|
+
* environment applies again.
|
|
727
|
+
*/
|
|
728
|
+
export type MemoryConfigChange =
|
|
729
|
+
| {
|
|
730
|
+
kind: "save";
|
|
731
|
+
baseUrl: string;
|
|
732
|
+
model: string;
|
|
733
|
+
apiKey?: string;
|
|
734
|
+
prefixes: ReadonlyArray<{ key: (typeof EMBEDDING_PREFIX_KEYS)[number]; value: "" | null }>;
|
|
735
|
+
}
|
|
736
|
+
| { kind: "restore" };
|
|
737
|
+
|
|
634
738
|
export async function updateOnboardingMemory(
|
|
635
739
|
preset: OnboardingMemoryPreset,
|
|
636
740
|
result: { ok: true } | { ok: false; errorClass: OnboardingErrorClass },
|
|
637
|
-
|
|
741
|
+
change?: MemoryConfigChange,
|
|
638
742
|
): Promise<boolean> {
|
|
639
743
|
return await getDbClient().transaction(async () => {
|
|
640
744
|
const events: PendingTelemetry[] = [];
|
|
@@ -642,11 +746,11 @@ export async function updateOnboardingMemory(
|
|
|
642
746
|
let dirty = loaded.dirty;
|
|
643
747
|
const eventsForState = loaded.state.autoCompleted ? [] : events;
|
|
644
748
|
|
|
645
|
-
if (
|
|
749
|
+
if (change?.kind === "save") {
|
|
646
750
|
const configValues = [
|
|
647
|
-
["EMBEDDING_API_BASE_URL",
|
|
648
|
-
["EMBEDDING_MODEL",
|
|
649
|
-
...(
|
|
751
|
+
["EMBEDDING_API_BASE_URL", change.baseUrl],
|
|
752
|
+
["EMBEDDING_MODEL", change.model],
|
|
753
|
+
...(change.apiKey ? [["EMBEDDING_API_KEY", change.apiKey]] : []),
|
|
650
754
|
] as const;
|
|
651
755
|
for (const [key, value] of configValues) {
|
|
652
756
|
const validationError = validateConfigValue(key, value);
|
|
@@ -655,17 +759,23 @@ export async function updateOnboardingMemory(
|
|
|
655
759
|
await upsertSwarmConfig({
|
|
656
760
|
scope: "global",
|
|
657
761
|
key: "EMBEDDING_API_BASE_URL",
|
|
658
|
-
value:
|
|
762
|
+
value: change.baseUrl,
|
|
659
763
|
});
|
|
660
|
-
await upsertSwarmConfig({ scope: "global", key: "EMBEDDING_MODEL", value:
|
|
661
|
-
if (
|
|
764
|
+
await upsertSwarmConfig({ scope: "global", key: "EMBEDDING_MODEL", value: change.model });
|
|
765
|
+
if (change.apiKey) {
|
|
662
766
|
await upsertSwarmConfig({
|
|
663
767
|
scope: "global",
|
|
664
768
|
key: "EMBEDDING_API_KEY",
|
|
665
|
-
value:
|
|
769
|
+
value: change.apiKey,
|
|
666
770
|
isSecret: true,
|
|
667
771
|
});
|
|
668
772
|
}
|
|
773
|
+
for (const { key, value } of change.prefixes) {
|
|
774
|
+
if (value === null) await deleteSwarmConfigByKey("global", null, key);
|
|
775
|
+
else await upsertSwarmConfig({ scope: "global", key, value });
|
|
776
|
+
}
|
|
777
|
+
} else if (change?.kind === "restore") {
|
|
778
|
+
for (const key of EMBEDDING_CONFIG_KEYS) await deleteSwarmConfigByKey("global", null, key);
|
|
669
779
|
}
|
|
670
780
|
|
|
671
781
|
if (result.ok) {
|
|
@@ -676,7 +786,7 @@ export async function updateOnboardingMemory(
|
|
|
676
786
|
|
|
677
787
|
if (dirty) await persistState(loaded.state);
|
|
678
788
|
queueTelemetry(events);
|
|
679
|
-
return
|
|
789
|
+
return change !== undefined;
|
|
680
790
|
});
|
|
681
791
|
}
|
|
682
792
|
|
package/src/be/rbac-roles.ts
CHANGED
|
@@ -38,6 +38,9 @@ export const BUILTIN_ROLES = [
|
|
|
38
38
|
"favorite.write.own",
|
|
39
39
|
"session.share",
|
|
40
40
|
"session.presence",
|
|
41
|
+
"task.steer.own",
|
|
42
|
+
"comb.presence",
|
|
43
|
+
"fs.join.own",
|
|
41
44
|
],
|
|
42
45
|
},
|
|
43
46
|
{
|
|
@@ -46,7 +49,7 @@ export const BUILTIN_ROLES = [
|
|
|
46
49
|
description: "Read-only: no task, approval or script writes.",
|
|
47
50
|
isBuiltin: true,
|
|
48
51
|
grantsAll: false,
|
|
49
|
-
verbs: ["favorite.write.own", "session.presence"],
|
|
52
|
+
verbs: ["favorite.write.own", "session.presence", "comb.presence", "fs.join.own"],
|
|
50
53
|
},
|
|
51
54
|
] satisfies (BuiltinRoleDefinition & { isBuiltin: true })[];
|
|
52
55
|
|
|
@@ -81,7 +81,7 @@ function rowToScript(row: ScriptEmbeddingCandidateRow): ScriptRecord {
|
|
|
81
81
|
export async function embedScript(script: ScriptRecord): Promise<void> {
|
|
82
82
|
const text = scriptEmbeddingText(script);
|
|
83
83
|
const provider = embeddingProvider();
|
|
84
|
-
const embedding = await provider.embed(text);
|
|
84
|
+
const embedding = await provider.embed(text, { kind: "document" });
|
|
85
85
|
if (!embedding) return;
|
|
86
86
|
|
|
87
87
|
if (embedding.length !== provider.dimensions) {
|
|
@@ -207,7 +207,7 @@ export async function searchScripts(args: {
|
|
|
207
207
|
limit?: number;
|
|
208
208
|
}): Promise<ScriptSearchResult[]> {
|
|
209
209
|
const provider = embeddingProvider();
|
|
210
|
-
const queryEmbedding = await provider.embed(args.query);
|
|
210
|
+
const queryEmbedding = await provider.embed(args.query, { kind: "query" });
|
|
211
211
|
if (!queryEmbedding) return lexicalFallback(args);
|
|
212
212
|
|
|
213
213
|
const candidates = await candidateRows(args.scope, args.scopeId);
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
getSwarmConfigs,
|
|
9
9
|
upsertSwarmConfig,
|
|
10
10
|
} from "../db";
|
|
11
|
+
import { getUserGrant } from "../rbac-roles";
|
|
11
12
|
import type { Seeder, SeedItem } from "./types";
|
|
12
13
|
|
|
13
14
|
const KIND = "agent-fs-provision";
|
|
@@ -16,6 +17,10 @@ const PROVISION_VERSION = 1;
|
|
|
16
17
|
const ORG_NAME = "swarm";
|
|
17
18
|
const DRIVE_NAME = "shared";
|
|
18
19
|
const PROVISION_HASH_KEY = "AGENT_FS_PROVISION_HASH";
|
|
20
|
+
// Stored in place of the hash when invites failed transiently. upstreamHash() maps it to
|
|
21
|
+
// null, so the next boot re-applies even when the org, drive and key rows already exist
|
|
22
|
+
// (otherwise the adopt-current-state fallback below would mark the failed run as in sync).
|
|
23
|
+
const PROVISION_RETRY_MARKER = "retry-pending";
|
|
19
24
|
const API_KEY_CONFIG = "API_AGENT_FS_API_KEY";
|
|
20
25
|
const LEGACY_SHARED_KEY_CONFIG = "AGENT_FS_API_KEY";
|
|
21
26
|
|
|
@@ -80,6 +85,7 @@ export const agentFsProvisionSeeder: Seeder<AgentFsSeedItem> = {
|
|
|
80
85
|
|
|
81
86
|
async upstreamHash(): Promise<string | null> {
|
|
82
87
|
const existingHash = await getConfigValue(PROVISION_HASH_KEY);
|
|
88
|
+
if (existingHash === PROVISION_RETRY_MARKER) return null;
|
|
83
89
|
if (existingHash) return existingHash;
|
|
84
90
|
|
|
85
91
|
const hasOrg = !!(await getConfigValue("AGENT_FS_DEFAULT_ORG_ID"));
|
|
@@ -113,16 +119,33 @@ async function provisionAgentFs(item: AgentFsSeedItem): Promise<void> {
|
|
|
113
119
|
shared.orgId,
|
|
114
120
|
);
|
|
115
121
|
|
|
122
|
+
let transientFailures = 0;
|
|
116
123
|
for (const invite of item.invites) {
|
|
117
124
|
const currentRole = currentRoles.get(invite.email.toLowerCase());
|
|
118
125
|
if (currentRole && roleRank(currentRole) >= roleRank(invite.role)) continue;
|
|
119
|
-
await inviteToSharedOrg(item.apiUrl, shared.authHeaders, shared.orgId, invite);
|
|
126
|
+
const outcome = await inviteToSharedOrg(item.apiUrl, shared.authHeaders, shared.orgId, invite);
|
|
127
|
+
if (outcome === "transient-failure") transientFailures++;
|
|
120
128
|
}
|
|
129
|
+
// "no-account" is not a failure to retry: agent-fs has no pending invites. The person's
|
|
130
|
+
// account is created later in the browser, by the /setup Files step or Comb's connect card,
|
|
131
|
+
// and both invite in the same flow. A transient failure (network, 5xx other than not-found,
|
|
132
|
+
// 401/403) must not store the hash, so the next boot re-runs the invites. The org and drive
|
|
133
|
+
// config rows were already written by ensureAgentFsSharedProvisioning above.
|
|
134
|
+
if (transientFailures > 0) {
|
|
135
|
+
await storeProvisionHash(PROVISION_RETRY_MARKER);
|
|
136
|
+
throw new Error(
|
|
137
|
+
`agent-fs invites failed for ${transientFailures} member(s); will retry on next boot`,
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
await storeProvisionHash(item.contentHash);
|
|
142
|
+
}
|
|
121
143
|
|
|
144
|
+
async function storeProvisionHash(value: string): Promise<void> {
|
|
122
145
|
await upsertSwarmConfig({
|
|
123
146
|
scope: "global",
|
|
124
147
|
key: PROVISION_HASH_KEY,
|
|
125
|
-
value
|
|
148
|
+
value,
|
|
126
149
|
isSecret: false,
|
|
127
150
|
description: "Internal hash for agent-fs provisioning reconciliation",
|
|
128
151
|
});
|
|
@@ -203,17 +226,31 @@ export async function ensureAgentFsSharedProvisioning(options?: {
|
|
|
203
226
|
return { apiUrl, apiKey, orgId, driveId, authHeaders };
|
|
204
227
|
}
|
|
205
228
|
|
|
229
|
+
export type SharedOrgInviteResult = {
|
|
230
|
+
orgId: string;
|
|
231
|
+
invited: boolean;
|
|
232
|
+
status: "already_member" | "added" | "pending";
|
|
233
|
+
inviteId?: string;
|
|
234
|
+
expiresAt?: string;
|
|
235
|
+
acceptUrl?: string;
|
|
236
|
+
};
|
|
237
|
+
|
|
206
238
|
/**
|
|
207
239
|
* Invite an external (non-agent) member into the shared org using the
|
|
208
240
|
* API-owned bootstrap credentials. Powers `POST /api/fs/members/invite` so
|
|
209
241
|
* callers holding the tenant API key (e.g. the cloud control plane's
|
|
210
242
|
* Connect-to-Drive flow) never need the bootstrap key itself — it is
|
|
211
243
|
* API-only and never served over HTTP.
|
|
244
|
+
*
|
|
245
|
+
* agent-fs (>= 0.18.9) adds an email that has an account directly
|
|
246
|
+
* (`status: "added"`) and answers an email without one with a pending invite
|
|
247
|
+
* (`status: "pending"`, plus `acceptUrl` when its app URL is configured).
|
|
248
|
+
* agent-fs sends no email, so the accept link is passed back to the caller.
|
|
212
249
|
*/
|
|
213
250
|
export async function inviteEmailToSharedOrg(
|
|
214
251
|
email: string,
|
|
215
252
|
role: Role,
|
|
216
|
-
): Promise<
|
|
253
|
+
): Promise<SharedOrgInviteResult> {
|
|
217
254
|
const shared = await ensureAgentFsSharedProvisioning();
|
|
218
255
|
const currentRoles = await getCurrentOrgMemberRoles(
|
|
219
256
|
shared.apiUrl,
|
|
@@ -222,15 +259,31 @@ export async function inviteEmailToSharedOrg(
|
|
|
222
259
|
);
|
|
223
260
|
const currentRole = currentRoles.get(email.trim().toLowerCase());
|
|
224
261
|
if (currentRole && roleRank(currentRole) >= roleRank(role)) {
|
|
225
|
-
return { orgId: shared.orgId, invited: false };
|
|
262
|
+
return { orgId: shared.orgId, invited: false, status: "already_member" };
|
|
226
263
|
}
|
|
227
|
-
await agentFsRequest
|
|
264
|
+
const response = await agentFsRequest<{
|
|
265
|
+
status?: unknown;
|
|
266
|
+
inviteId?: unknown;
|
|
267
|
+
expiresAt?: unknown;
|
|
268
|
+
acceptUrl?: unknown;
|
|
269
|
+
}>(shared.apiUrl, `/orgs/${encodeURIComponent(shared.orgId)}/members/invite`, {
|
|
228
270
|
method: "POST",
|
|
229
271
|
headers: shared.authHeaders,
|
|
230
272
|
body: { email: email.trim(), role },
|
|
231
273
|
allowConflict: true,
|
|
232
274
|
});
|
|
233
|
-
|
|
275
|
+
// Older agent-fs answers a direct add with a bare `{ ok: true }`.
|
|
276
|
+
if (response.status !== "pending") {
|
|
277
|
+
return { orgId: shared.orgId, invited: true, status: "added" };
|
|
278
|
+
}
|
|
279
|
+
return {
|
|
280
|
+
orgId: shared.orgId,
|
|
281
|
+
invited: true,
|
|
282
|
+
status: "pending",
|
|
283
|
+
...(typeof response.inviteId === "string" ? { inviteId: response.inviteId } : {}),
|
|
284
|
+
...(typeof response.expiresAt === "string" ? { expiresAt: response.expiresAt } : {}),
|
|
285
|
+
...(typeof response.acceptUrl === "string" ? { acceptUrl: response.acceptUrl } : {}),
|
|
286
|
+
};
|
|
234
287
|
}
|
|
235
288
|
|
|
236
289
|
export async function ensureAgentFsCredentialsForAgent(agentId: string): Promise<{
|
|
@@ -396,7 +449,7 @@ async function inviteToSharedOrg(
|
|
|
396
449
|
headers: Record<string, string>,
|
|
397
450
|
orgId: string,
|
|
398
451
|
invite: InviteTarget,
|
|
399
|
-
): Promise<
|
|
452
|
+
): Promise<"invited" | "no-account" | "transient-failure"> {
|
|
400
453
|
try {
|
|
401
454
|
await agentFsRequest(apiUrl, `/orgs/${encodeURIComponent(orgId)}/members/invite`, {
|
|
402
455
|
method: "POST",
|
|
@@ -404,6 +457,7 @@ async function inviteToSharedOrg(
|
|
|
404
457
|
body: invite,
|
|
405
458
|
allowConflict: true,
|
|
406
459
|
});
|
|
460
|
+
return "invited";
|
|
407
461
|
} catch (error) {
|
|
408
462
|
console.warn(
|
|
409
463
|
scrubSecrets(
|
|
@@ -412,6 +466,12 @@ async function inviteToSharedOrg(
|
|
|
412
466
|
}`,
|
|
413
467
|
),
|
|
414
468
|
);
|
|
469
|
+
// agent-fs answers an unregistered invitee with HTTP 500 "User with email ... not found".
|
|
470
|
+
const noAccount =
|
|
471
|
+
error instanceof AgentFsRequestError &&
|
|
472
|
+
error.status === 500 &&
|
|
473
|
+
/not found/i.test(error.message);
|
|
474
|
+
return noAccount ? "no-account" : "transient-failure";
|
|
415
475
|
}
|
|
416
476
|
}
|
|
417
477
|
|
|
@@ -499,18 +559,19 @@ async function resolveInviteTargets(): Promise<InviteTarget[]> {
|
|
|
499
559
|
const targets = new Map<string, InviteTarget>();
|
|
500
560
|
|
|
501
561
|
for (const user of await getAllUsers()) {
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
562
|
+
const email = user.email?.trim();
|
|
563
|
+
if (!email) continue;
|
|
564
|
+
// Only active users get a seat: invited and suspended users cannot sign in yet.
|
|
565
|
+
if ((user.status ?? "active") !== "active") continue;
|
|
566
|
+
// Follow the RBAC grant, not the free-text profile role, so an RBAC Viewer stays a viewer.
|
|
567
|
+
const grant = await getUserGrant(user.id);
|
|
568
|
+
const role: Role = grant.grantsAll || grant.verbs.has("task.fs.mutate") ? "editor" : "viewer";
|
|
569
|
+
targets.set(email.toLowerCase(), { email, role });
|
|
505
570
|
}
|
|
506
571
|
|
|
507
572
|
return [...targets.values()].sort((a, b) => a.email.localeCompare(b.email));
|
|
508
573
|
}
|
|
509
574
|
|
|
510
|
-
function isExplicitViewerRole(role: string | undefined): boolean {
|
|
511
|
-
return /\b(viewer|read[-\s]?only|guest)\b/i.test(role ?? "");
|
|
512
|
-
}
|
|
513
|
-
|
|
514
575
|
async function provisionHashFromCurrentState(): Promise<string> {
|
|
515
576
|
return provisionHash({
|
|
516
577
|
apiUrl: await resolveApiUrl(),
|
|
@@ -410,6 +410,8 @@ const VALIDATED_KEYS: Record<string, ConfigValidator> = {
|
|
|
410
410
|
value,
|
|
411
411
|
"Invalid AGENT_FS_PUBLIC_URL (must be an http(s) URL with no query string or fragment, e.g. https://agent-fs.example.com. Leave blank to use AGENT_FS_API_URL)",
|
|
412
412
|
),
|
|
413
|
+
// Where people-facing file links point. Blank keeps the automatic choice.
|
|
414
|
+
...enumValidator("AGENT_FS_LINK_TARGET", ["dashboard", "live", ""]),
|
|
413
415
|
HARNESS_PROVIDER: (value) => {
|
|
414
416
|
const parsed = ProviderNameSchema.safeParse(value);
|
|
415
417
|
if (parsed.success) return null;
|
|
@@ -520,6 +522,7 @@ const VALIDATED_KEYS: Record<string, ConfigValidator> = {
|
|
|
520
522
|
"DB_RETENTION_DRY_RUN",
|
|
521
523
|
"MODEL_AUTO_UPGRADE",
|
|
522
524
|
"COMB_ENABLED",
|
|
525
|
+
"AGENT_FS_PROXY",
|
|
523
526
|
]),
|
|
524
527
|
...enumValidator("SLACK_MODE", ["socket", "http"]),
|
|
525
528
|
...enumValidator("SLACK_THREAD_STEERING", ["off", "lead", "all"]),
|
package/src/cli.tsx
CHANGED
|
@@ -41,6 +41,7 @@ interface ParsedArgs {
|
|
|
41
41
|
maxConcurrentTasks: string | undefined;
|
|
42
42
|
pullPolicy: string | undefined;
|
|
43
43
|
containerEngine: string | undefined;
|
|
44
|
+
noAgentFs: boolean;
|
|
44
45
|
open: boolean;
|
|
45
46
|
showHelp: boolean;
|
|
46
47
|
dbPath: string;
|
|
@@ -63,6 +64,7 @@ function parseArgs(args: string[]): ParsedArgs {
|
|
|
63
64
|
let maxConcurrentTasks: string | undefined;
|
|
64
65
|
let pullPolicy: string | undefined;
|
|
65
66
|
let containerEngine: string | undefined;
|
|
67
|
+
let noAgentFs = false;
|
|
66
68
|
let open = false;
|
|
67
69
|
let showHelp = false;
|
|
68
70
|
let dbPath = "";
|
|
@@ -122,6 +124,8 @@ function parseArgs(args: string[]): ParsedArgs {
|
|
|
122
124
|
if (value && !value.startsWith("-")) i++;
|
|
123
125
|
} else if (arg?.startsWith("--container-engine=")) {
|
|
124
126
|
containerEngine = arg.slice("--container-engine=".length);
|
|
127
|
+
} else if (arg === "--no-agent-fs") {
|
|
128
|
+
noAgentFs = true;
|
|
125
129
|
} else if (arg === "--open") {
|
|
126
130
|
open = true;
|
|
127
131
|
} else if (arg === "--help" || arg === "-h") {
|
|
@@ -149,6 +153,7 @@ function parseArgs(args: string[]): ParsedArgs {
|
|
|
149
153
|
maxConcurrentTasks,
|
|
150
154
|
pullPolicy,
|
|
151
155
|
containerEngine,
|
|
156
|
+
noAgentFs,
|
|
152
157
|
open,
|
|
153
158
|
showHelp,
|
|
154
159
|
dbPath,
|
|
@@ -185,6 +190,7 @@ const COMMAND_HELP: Record<
|
|
|
185
190
|
" --max-concurrent-tasks <n> Tasks per agent (1-100; default: lead 2, worker 1)",
|
|
186
191
|
" --pull-policy <policy> Pull policy: always, missing, never (default: always)",
|
|
187
192
|
" --container-engine <engine> auto, docker, podman (default: $AGENT_SWARM_CONTAINER_ENGINE, else auto; auto prefers Docker)",
|
|
193
|
+
" --no-agent-fs With --yes: skip the bundled agent-fs (files, Comb) and TEI (memory embeddings)",
|
|
188
194
|
" -h, --help Show this help",
|
|
189
195
|
].join("\n"),
|
|
190
196
|
examples: [
|
|
@@ -192,6 +198,7 @@ const COMMAND_HELP: Record<
|
|
|
192
198
|
` ${binName} onboard --dry-run`,
|
|
193
199
|
` ${binName} onboard --yes --preset=full`,
|
|
194
200
|
` ${binName} onboard --yes --preset=dev`,
|
|
201
|
+
` ${binName} onboard --yes --preset=solo --no-agent-fs`,
|
|
195
202
|
` ${binName} onboard --container-engine=podman`,
|
|
196
203
|
` ANTHROPIC_API_KEY=sk-... ${binName} onboard --yes --preset=solo`,
|
|
197
204
|
].join("\n"),
|
|
@@ -693,6 +700,7 @@ function App({ args }: { args: ParsedArgs }) {
|
|
|
693
700
|
maxConcurrentTasks,
|
|
694
701
|
pullPolicy,
|
|
695
702
|
containerEngine,
|
|
703
|
+
noAgentFs,
|
|
696
704
|
} = args;
|
|
697
705
|
|
|
698
706
|
switch (command) {
|
|
@@ -707,6 +715,7 @@ function App({ args }: { args: ParsedArgs }) {
|
|
|
707
715
|
maxConcurrentTasks,
|
|
708
716
|
pullPolicy,
|
|
709
717
|
containerEngine,
|
|
718
|
+
noAgentFs,
|
|
710
719
|
}}
|
|
711
720
|
/>
|
|
712
721
|
);
|
package/src/comb/config.ts
CHANGED
|
@@ -2,6 +2,8 @@ import {
|
|
|
2
2
|
getAgentFsDefaultDriveId,
|
|
3
3
|
getAgentFsDefaultOrgId,
|
|
4
4
|
getAgentFsLiveUrl,
|
|
5
|
+
getPublicMcpBaseUrl,
|
|
6
|
+
isAgentFsProxyEnabled,
|
|
5
7
|
isCombEnabled,
|
|
6
8
|
} from "../utils/constants";
|
|
7
9
|
|
|
@@ -27,9 +29,12 @@ function trimUrl(value: string | undefined): string | null {
|
|
|
27
29
|
*/
|
|
28
30
|
export function getCombConfig(): CombConfig {
|
|
29
31
|
const internalUrl = trimUrl(process.env.AGENT_FS_API_URL);
|
|
32
|
+
// With the proxy on, the browser reaches agent-fs through the API itself.
|
|
33
|
+
const browserDefault =
|
|
34
|
+
isAgentFsProxyEnabled() && internalUrl ? `${getPublicMcpBaseUrl()}/agentfs` : internalUrl;
|
|
30
35
|
return {
|
|
31
36
|
enabled: isCombEnabled(),
|
|
32
|
-
apiUrl: trimUrl(process.env.AGENT_FS_PUBLIC_URL) ??
|
|
37
|
+
apiUrl: trimUrl(process.env.AGENT_FS_PUBLIC_URL) ?? browserDefault,
|
|
33
38
|
liveUrl: getAgentFsLiveUrl(),
|
|
34
39
|
orgId: getAgentFsDefaultOrgId() ?? null,
|
|
35
40
|
driveId: getAgentFsDefaultDriveId() ?? null,
|