@agent-native/dispatch 0.0.0-beta-20260819184657 → 0.0.0-beta-20260820150521
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/server/lib/app-creation-store.d.ts.map +1 -1
- package/dist/server/lib/app-creation-store.js +178 -14
- package/dist/server/lib/app-creation-store.js.map +1 -1
- package/package.json +4 -4
- package/src/server/lib/app-creation-store.spec.ts +296 -19
- package/src/server/lib/app-creation-store.ts +218 -14
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/dispatch",
|
|
3
|
-
"version": "0.0.0-beta-
|
|
3
|
+
"version": "0.0.0-beta-20260820150521",
|
|
4
4
|
"description": "Dispatch — workspace control plane for agent-native apps. Vault, integrations, destinations, scheduled jobs, and cross-app delegation, shipped as a single drop-in package.",
|
|
5
5
|
"homepage": "https://github.com/BuilderIO/agent-native#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -87,7 +87,7 @@
|
|
|
87
87
|
"tailwind-merge": "^3.5.0",
|
|
88
88
|
"vaul": "^1.1.2",
|
|
89
89
|
"zod": "^4.3.6",
|
|
90
|
-
"@agent-native/toolkit": "0.
|
|
90
|
+
"@agent-native/toolkit": "0.0.0-beta-20260820150521"
|
|
91
91
|
},
|
|
92
92
|
"devDependencies": {
|
|
93
93
|
"@react-router/dev": "^8.1.0",
|
|
@@ -101,10 +101,10 @@
|
|
|
101
101
|
"typescript-7": "npm:typescript@^7.0.2",
|
|
102
102
|
"vite": "8.1.0",
|
|
103
103
|
"vitest": "^4.1.5",
|
|
104
|
-
"@agent-native/core": "0.0.0-beta-
|
|
104
|
+
"@agent-native/core": "0.0.0-beta-20260820150521"
|
|
105
105
|
},
|
|
106
106
|
"peerDependencies": {
|
|
107
|
-
"@agent-native/core": "0.0.0-beta-
|
|
107
|
+
"@agent-native/core": "0.0.0-beta-20260820150521",
|
|
108
108
|
"react": ">=19.2.7",
|
|
109
109
|
"react-dom": ">=19.2.7",
|
|
110
110
|
"react-router": ">=8"
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
getAppCreationSettings,
|
|
7
7
|
listAvailableWorkspaceTemplates,
|
|
8
8
|
listWorkspaceApps,
|
|
9
|
+
scaffoldWorkspaceAppFromTemplate,
|
|
9
10
|
setAppCreationSettings,
|
|
10
11
|
startWorkspaceAppCreation,
|
|
11
12
|
updateWorkspaceAppMetadata,
|
|
@@ -23,6 +24,13 @@ const mocks = vi.hoisted(() => {
|
|
|
23
24
|
settings,
|
|
24
25
|
state,
|
|
25
26
|
getSetting: vi.fn(async (key: string) => settings.get(key) ?? null),
|
|
27
|
+
mutateSetting: vi.fn(
|
|
28
|
+
async (key: string, updater: (current: any) => any) => {
|
|
29
|
+
const next = await updater(settings.get(key) ?? null);
|
|
30
|
+
settings.set(key, next);
|
|
31
|
+
return next;
|
|
32
|
+
},
|
|
33
|
+
),
|
|
26
34
|
putSetting: vi.fn(async (key: string, value: unknown) => {
|
|
27
35
|
settings.set(key, value);
|
|
28
36
|
}),
|
|
@@ -32,9 +40,19 @@ const mocks = vi.hoisted(() => {
|
|
|
32
40
|
resource: {},
|
|
33
41
|
})),
|
|
34
42
|
getDbExec: vi.fn(() => ({
|
|
35
|
-
execute: vi.fn(async () =>
|
|
36
|
-
|
|
37
|
-
|
|
43
|
+
execute: vi.fn(async (statement: unknown) => {
|
|
44
|
+
const sql =
|
|
45
|
+
typeof statement === "string"
|
|
46
|
+
? statement
|
|
47
|
+
: String((statement as { sql?: unknown })?.sql ?? "");
|
|
48
|
+
if (sql.includes("SELECT id FROM workspace_apps")) {
|
|
49
|
+
return { rows: [], rowsAffected: 0 };
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
rows: state.orgRole ? [{ role: state.orgRole }] : [],
|
|
53
|
+
rowsAffected: 0,
|
|
54
|
+
};
|
|
55
|
+
}),
|
|
38
56
|
})),
|
|
39
57
|
resolveBuilderCredentialsDetailed: vi.fn(async () => ({
|
|
40
58
|
privateKey: null as string | null,
|
|
@@ -78,6 +96,7 @@ vi.mock("@agent-native/core/db", async (importOriginal) => {
|
|
|
78
96
|
|
|
79
97
|
vi.mock("@agent-native/core/settings", () => ({
|
|
80
98
|
getSetting: (...args: any[]) => mocks.getSetting(...args),
|
|
99
|
+
mutateSetting: (...args: any[]) => mocks.mutateSetting(...args),
|
|
81
100
|
putSetting: (...args: any[]) => mocks.putSetting(...args),
|
|
82
101
|
getOrgSetting: (...args: any[]) => mocks.getOrgSetting(...args),
|
|
83
102
|
}));
|
|
@@ -119,12 +138,32 @@ afterEach(() => {
|
|
|
119
138
|
vi.unstubAllGlobals();
|
|
120
139
|
vi.clearAllMocks();
|
|
121
140
|
mocks.settings.clear();
|
|
141
|
+
mocks.getOrgSetting.mockReset();
|
|
142
|
+
mocks.getOrgSetting.mockResolvedValue(null);
|
|
143
|
+
mocks.mutateSetting.mockReset();
|
|
144
|
+
mocks.mutateSetting.mockImplementation(
|
|
145
|
+
async (key: string, updater: (current: any) => any) => {
|
|
146
|
+
const next = await updater(mocks.settings.get(key) ?? null);
|
|
147
|
+
mocks.settings.set(key, next);
|
|
148
|
+
return next;
|
|
149
|
+
},
|
|
150
|
+
);
|
|
122
151
|
mocks.state.orgRole = "admin";
|
|
123
152
|
mocks.getDbExec.mockReset();
|
|
124
153
|
mocks.getDbExec.mockImplementation(() => ({
|
|
125
|
-
execute: vi.fn(async () =>
|
|
126
|
-
|
|
127
|
-
|
|
154
|
+
execute: vi.fn(async (statement: unknown) => {
|
|
155
|
+
const sql =
|
|
156
|
+
typeof statement === "string"
|
|
157
|
+
? statement
|
|
158
|
+
: String((statement as { sql?: unknown })?.sql ?? "");
|
|
159
|
+
if (sql.includes("SELECT id FROM workspace_apps")) {
|
|
160
|
+
return { rows: [], rowsAffected: 0 };
|
|
161
|
+
}
|
|
162
|
+
return {
|
|
163
|
+
rows: mocks.state.orgRole ? [{ role: mocks.state.orgRole }] : [],
|
|
164
|
+
rowsAffected: 0,
|
|
165
|
+
};
|
|
166
|
+
}),
|
|
128
167
|
}));
|
|
129
168
|
mocks.resolveAccess.mockReset();
|
|
130
169
|
mocks.resolveAccess.mockResolvedValue({ role: "viewer", resource: {} });
|
|
@@ -368,7 +407,7 @@ describe("listWorkspaceApps", () => {
|
|
|
368
407
|
expect(apps.map((app) => app.id)).toEqual(["dispatch"]);
|
|
369
408
|
});
|
|
370
409
|
|
|
371
|
-
it("
|
|
410
|
+
it("keeps legacy apps organization-visible when the org default cannot be read", async () => {
|
|
372
411
|
stubManifest([
|
|
373
412
|
{ id: "dispatch", name: "Dispatch", path: "/dispatch" },
|
|
374
413
|
{ id: "private-app", name: "Private app", path: "/private-app" },
|
|
@@ -377,12 +416,12 @@ describe("listWorkspaceApps", () => {
|
|
|
377
416
|
new Error("settings store unavailable"),
|
|
378
417
|
);
|
|
379
418
|
|
|
380
|
-
await
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
).
|
|
419
|
+
const apps = await runWithRequestContext(
|
|
420
|
+
{ userEmail: "dev@example.test", orgId: "org-123" },
|
|
421
|
+
() => listWorkspaceApps({ includeAgentCards: false }),
|
|
422
|
+
);
|
|
423
|
+
|
|
424
|
+
expect(apps.map((app) => app.id)).toEqual(["dispatch", "private-app"]);
|
|
386
425
|
});
|
|
387
426
|
|
|
388
427
|
it("fails closed when the workspace-app access schema is unavailable", async () => {
|
|
@@ -417,7 +456,7 @@ describe("listWorkspaceApps", () => {
|
|
|
417
456
|
expect(mocks.resolveAccess).not.toHaveBeenCalled();
|
|
418
457
|
});
|
|
419
458
|
|
|
420
|
-
it("
|
|
459
|
+
it("does not apply the current default retroactively to legacy apps", async () => {
|
|
421
460
|
stubNoPendingContext();
|
|
422
461
|
stubManifest([
|
|
423
462
|
{
|
|
@@ -451,9 +490,8 @@ describe("listWorkspaceApps", () => {
|
|
|
451
490
|
);
|
|
452
491
|
|
|
453
492
|
expect(apps.find((app) => app.id === "legacy-app")?.visibility).toBe("org");
|
|
454
|
-
expect(apps.find((app) => app.id === "new-app")?.visibility).toBe(
|
|
455
|
-
|
|
456
|
-
);
|
|
493
|
+
expect(apps.find((app) => app.id === "new-app")?.visibility).toBe("org");
|
|
494
|
+
expect(mocks.getOrgSetting).not.toHaveBeenCalled();
|
|
457
495
|
const inserts = execute.mock.calls.filter(([statement]) =>
|
|
458
496
|
String((statement as { sql?: unknown })?.sql ?? "").includes(
|
|
459
497
|
"INSERT INTO workspace_apps",
|
|
@@ -461,7 +499,7 @@ describe("listWorkspaceApps", () => {
|
|
|
461
499
|
);
|
|
462
500
|
expect(inserts.map(([statement]) => (statement as any).args?.[3])).toEqual([
|
|
463
501
|
"org",
|
|
464
|
-
"
|
|
502
|
+
"org",
|
|
465
503
|
]);
|
|
466
504
|
});
|
|
467
505
|
|
|
@@ -592,6 +630,61 @@ describe("listWorkspaceApps", () => {
|
|
|
592
630
|
expect(apps.map((app) => app.id)).toEqual(["dispatch", "legacy"]);
|
|
593
631
|
});
|
|
594
632
|
|
|
633
|
+
it("hides private pending apps from non-creators", async () => {
|
|
634
|
+
stubNoPendingContext();
|
|
635
|
+
stubManifest();
|
|
636
|
+
mocks.settings.set(settingsKey, {
|
|
637
|
+
pendingApps: [
|
|
638
|
+
pendingApp("private-pending", {
|
|
639
|
+
visibility: "private",
|
|
640
|
+
createdBy: "creator@example.test",
|
|
641
|
+
owner: "creator@example.test",
|
|
642
|
+
}),
|
|
643
|
+
],
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
const apps = await runWithRequestContext(
|
|
647
|
+
{ userEmail: "viewer@example.test", orgId: "org-123" },
|
|
648
|
+
() => listWorkspaceApps({ includeAgentCards: false }),
|
|
649
|
+
);
|
|
650
|
+
|
|
651
|
+
expect(apps.map((app) => app.id)).toEqual(["dispatch"]);
|
|
652
|
+
});
|
|
653
|
+
|
|
654
|
+
it("fails closed when a private app access record cannot be inserted", async () => {
|
|
655
|
+
stubNoPendingContext();
|
|
656
|
+
stubManifest([
|
|
657
|
+
{ id: "scaffolded", name: "Scaffolded", path: "/scaffolded" },
|
|
658
|
+
]);
|
|
659
|
+
mocks.settings.set("workspace-app-metadata:org:org-123", {
|
|
660
|
+
apps: {
|
|
661
|
+
scaffolded: {
|
|
662
|
+
visibility: "private",
|
|
663
|
+
createdBy: "creator@example.test",
|
|
664
|
+
},
|
|
665
|
+
},
|
|
666
|
+
});
|
|
667
|
+
const execute = vi.fn(async (statement: unknown) => {
|
|
668
|
+
const sql =
|
|
669
|
+
typeof statement === "string"
|
|
670
|
+
? statement
|
|
671
|
+
: String((statement as { sql?: unknown })?.sql ?? "");
|
|
672
|
+
if (sql.includes("SELECT id, owner_email, org_id, visibility")) {
|
|
673
|
+
return { rows: [], rowsAffected: 0 };
|
|
674
|
+
}
|
|
675
|
+
throw new Error("workspace app access insert failed");
|
|
676
|
+
});
|
|
677
|
+
mocks.getDbExec.mockReturnValue({ execute });
|
|
678
|
+
mocks.resolveAccess.mockResolvedValue(null);
|
|
679
|
+
|
|
680
|
+
const apps = await runWithRequestContext(
|
|
681
|
+
{ userEmail: "viewer@example.test", orgId: "org-123" },
|
|
682
|
+
() => listWorkspaceApps({ includeAgentCards: false }),
|
|
683
|
+
);
|
|
684
|
+
|
|
685
|
+
expect(apps).toEqual([]);
|
|
686
|
+
});
|
|
687
|
+
|
|
595
688
|
it("hides expired pending Builder app rows", async () => {
|
|
596
689
|
stubManifest();
|
|
597
690
|
vi.stubEnv("BRANCH", "feature-a");
|
|
@@ -783,6 +876,179 @@ describe("startWorkspaceAppCreation", () => {
|
|
|
783
876
|
);
|
|
784
877
|
}
|
|
785
878
|
|
|
879
|
+
it("persists the private default for local-agent app creation", async () => {
|
|
880
|
+
mocks.getOrgSetting.mockResolvedValueOnce({ visibility: "private" });
|
|
881
|
+
|
|
882
|
+
const result = (await create("onboarding", {
|
|
883
|
+
userEmail: "dev@example.test",
|
|
884
|
+
orgId: "org-123",
|
|
885
|
+
})) as any;
|
|
886
|
+
|
|
887
|
+
expect(result.mode).toBe("local-agent");
|
|
888
|
+
expect(
|
|
889
|
+
mocks.settings.get("workspace-app-metadata:org:org-123"),
|
|
890
|
+
).toMatchObject({
|
|
891
|
+
apps: {
|
|
892
|
+
onboarding: { visibility: "private" },
|
|
893
|
+
},
|
|
894
|
+
});
|
|
895
|
+
});
|
|
896
|
+
|
|
897
|
+
it("rejects a cross-member collision with an active pending app id", async () => {
|
|
898
|
+
stubHostedRuntime();
|
|
899
|
+
stubBuilderProjectConfigured();
|
|
900
|
+
mocks.getOrgSetting.mockResolvedValueOnce({ visibility: "private" });
|
|
901
|
+
mocks.settings.set("dispatch-app-creation-settings:org:org-123", {
|
|
902
|
+
pendingApps: [
|
|
903
|
+
{
|
|
904
|
+
id: "onboarding",
|
|
905
|
+
name: "Onboarding",
|
|
906
|
+
description: "Already being created",
|
|
907
|
+
path: "/onboarding",
|
|
908
|
+
builderUrl:
|
|
909
|
+
"https://builder.io/app/projects/project-1/branch/onboarding",
|
|
910
|
+
branchName: "onboarding",
|
|
911
|
+
projectId: "project-1",
|
|
912
|
+
createdBy: "creator@example.test",
|
|
913
|
+
owner: "creator@example.test",
|
|
914
|
+
createdAt: "2026-08-19T21:00:00.000Z",
|
|
915
|
+
updatedAt: "2026-08-19T21:00:00.000Z",
|
|
916
|
+
expiresAt: "2999-01-01T00:00:00.000Z",
|
|
917
|
+
},
|
|
918
|
+
],
|
|
919
|
+
});
|
|
920
|
+
|
|
921
|
+
await expect(
|
|
922
|
+
create("onboarding", {
|
|
923
|
+
userEmail: "other@example.test",
|
|
924
|
+
orgId: "org-123",
|
|
925
|
+
}),
|
|
926
|
+
).rejects.toThrow("already being created by another member");
|
|
927
|
+
expect(mocks.runBuilderAgent).not.toHaveBeenCalled();
|
|
928
|
+
expect(
|
|
929
|
+
mocks.settings.get("dispatch-app-creation-settings:org:org-123"),
|
|
930
|
+
).toMatchObject({
|
|
931
|
+
pendingApps: [
|
|
932
|
+
expect.objectContaining({ createdBy: "creator@example.test" }),
|
|
933
|
+
],
|
|
934
|
+
});
|
|
935
|
+
});
|
|
936
|
+
|
|
937
|
+
it("rejects a collision from another deployment context", async () => {
|
|
938
|
+
stubHostedRuntime();
|
|
939
|
+
stubBuilderProjectConfigured();
|
|
940
|
+
mocks.settings.set("dispatch-app-creation-settings:org:org-123", {
|
|
941
|
+
pendingApps: [
|
|
942
|
+
{
|
|
943
|
+
id: "onboarding",
|
|
944
|
+
name: "Onboarding",
|
|
945
|
+
description: "Already being created",
|
|
946
|
+
path: "/onboarding",
|
|
947
|
+
contextId: "branch:other-context",
|
|
948
|
+
createdBy: "creator@example.test",
|
|
949
|
+
owner: "creator@example.test",
|
|
950
|
+
createdAt: "2026-08-19T21:00:00.000Z",
|
|
951
|
+
updatedAt: "2026-08-19T21:00:00.000Z",
|
|
952
|
+
expiresAt: "2999-01-01T00:00:00.000Z",
|
|
953
|
+
},
|
|
954
|
+
],
|
|
955
|
+
});
|
|
956
|
+
|
|
957
|
+
await expect(
|
|
958
|
+
create("onboarding", {
|
|
959
|
+
userEmail: "other@example.test",
|
|
960
|
+
orgId: "org-123",
|
|
961
|
+
}),
|
|
962
|
+
).rejects.toThrow("already being created by another member");
|
|
963
|
+
expect(mocks.runBuilderAgent).not.toHaveBeenCalled();
|
|
964
|
+
});
|
|
965
|
+
|
|
966
|
+
it("atomically reserves an app id before starting Builder", async () => {
|
|
967
|
+
stubHostedRuntime();
|
|
968
|
+
stubBuilderProjectConfigured();
|
|
969
|
+
mocks.resolveBuilderCredentialsDetailed.mockResolvedValue(
|
|
970
|
+
credentials({
|
|
971
|
+
privateKey: "priv",
|
|
972
|
+
publicKey: "pub",
|
|
973
|
+
userId: "builder-user-42",
|
|
974
|
+
}),
|
|
975
|
+
);
|
|
976
|
+
mocks.runBuilderAgent.mockImplementation(async () => {
|
|
977
|
+
expect(
|
|
978
|
+
mocks.settings.get("dispatch-app-creation-settings:org:org-123"),
|
|
979
|
+
).toMatchObject({
|
|
980
|
+
pendingApps: [
|
|
981
|
+
expect.objectContaining({
|
|
982
|
+
id: "onboarding",
|
|
983
|
+
createdBy: "dev@example.test",
|
|
984
|
+
}),
|
|
985
|
+
],
|
|
986
|
+
});
|
|
987
|
+
return {
|
|
988
|
+
branchName: "onboarding1",
|
|
989
|
+
url: "https://builder.io/app/projects/project-1/branch/onboarding1",
|
|
990
|
+
status: "processing",
|
|
991
|
+
};
|
|
992
|
+
});
|
|
993
|
+
|
|
994
|
+
const result = (await create("onboarding", {
|
|
995
|
+
userEmail: "dev@example.test",
|
|
996
|
+
orgId: "org-123",
|
|
997
|
+
})) as any;
|
|
998
|
+
|
|
999
|
+
expect(result.mode).toBe("builder");
|
|
1000
|
+
expect(mocks.mutateSetting).toHaveBeenCalled();
|
|
1001
|
+
});
|
|
1002
|
+
|
|
1003
|
+
it("releases the reservation when Builder handoff fails", async () => {
|
|
1004
|
+
stubHostedRuntime();
|
|
1005
|
+
stubBuilderProjectConfigured();
|
|
1006
|
+
mocks.resolveBuilderCredentialsDetailed.mockResolvedValue(
|
|
1007
|
+
credentials({
|
|
1008
|
+
privateKey: "priv",
|
|
1009
|
+
publicKey: "pub",
|
|
1010
|
+
userId: "builder-user-42",
|
|
1011
|
+
}),
|
|
1012
|
+
);
|
|
1013
|
+
mocks.runBuilderAgent.mockRejectedValue(new Error("Builder unavailable"));
|
|
1014
|
+
|
|
1015
|
+
const result = (await create("onboarding", {
|
|
1016
|
+
userEmail: "dev@example.test",
|
|
1017
|
+
orgId: "org-123",
|
|
1018
|
+
})) as any;
|
|
1019
|
+
|
|
1020
|
+
expect(result).toMatchObject({ mode: "builder-unavailable" });
|
|
1021
|
+
expect(
|
|
1022
|
+
mocks.settings.get("dispatch-app-creation-settings:org:org-123"),
|
|
1023
|
+
).toMatchObject({ pendingApps: [] });
|
|
1024
|
+
expect(mocks.settings.get("workspace-app-metadata:org:org-123")).toBe(
|
|
1025
|
+
undefined,
|
|
1026
|
+
);
|
|
1027
|
+
});
|
|
1028
|
+
|
|
1029
|
+
it("rejects a scaffold id already registered in the shared app registry", async () => {
|
|
1030
|
+
mocks.getDbExec.mockReturnValue({
|
|
1031
|
+
execute: vi.fn(async (statement: unknown) => {
|
|
1032
|
+
const sql = String((statement as { sql?: unknown })?.sql ?? "");
|
|
1033
|
+
if (sql.includes("SELECT id FROM workspace_apps")) {
|
|
1034
|
+
return { rows: [{ id: "mail" }], rowsAffected: 0 };
|
|
1035
|
+
}
|
|
1036
|
+
return { rows: [], rowsAffected: 0 };
|
|
1037
|
+
}),
|
|
1038
|
+
});
|
|
1039
|
+
|
|
1040
|
+
await expect(
|
|
1041
|
+
runWithRequestContext(
|
|
1042
|
+
{ userEmail: "dev@example.test", orgId: "org-123" },
|
|
1043
|
+
() =>
|
|
1044
|
+
scaffoldWorkspaceAppFromTemplate({
|
|
1045
|
+
template: "mail",
|
|
1046
|
+
appId: "mail",
|
|
1047
|
+
}),
|
|
1048
|
+
),
|
|
1049
|
+
).rejects.toThrow("already registered");
|
|
1050
|
+
});
|
|
1051
|
+
|
|
786
1052
|
it("returns builder-not-connected without leaking the project id when no Builder credentials are configured", async () => {
|
|
787
1053
|
stubHostedRuntime();
|
|
788
1054
|
stubBuilderProjectConfigured();
|
|
@@ -835,6 +1101,7 @@ describe("startWorkspaceAppCreation", () => {
|
|
|
835
1101
|
it("starts the Builder branch and passes the resolved userId through", async () => {
|
|
836
1102
|
stubHostedRuntime();
|
|
837
1103
|
stubBuilderProjectConfigured();
|
|
1104
|
+
mocks.getOrgSetting.mockResolvedValueOnce({ visibility: "private" });
|
|
838
1105
|
mocks.resolveBuilderCredentialsDetailed.mockResolvedValue(
|
|
839
1106
|
credentials({
|
|
840
1107
|
privateKey: "priv",
|
|
@@ -848,7 +1115,10 @@ describe("startWorkspaceAppCreation", () => {
|
|
|
848
1115
|
status: "processing",
|
|
849
1116
|
});
|
|
850
1117
|
|
|
851
|
-
const result = (await create(
|
|
1118
|
+
const result = (await create("onboarding", {
|
|
1119
|
+
userEmail: "dev@example.test",
|
|
1120
|
+
orgId: "org-123",
|
|
1121
|
+
})) as any;
|
|
852
1122
|
|
|
853
1123
|
expect(result.mode).toBe("builder");
|
|
854
1124
|
expect(mocks.runBuilderAgent).toHaveBeenCalledWith(
|
|
@@ -867,6 +1137,13 @@ describe("startWorkspaceAppCreation", () => {
|
|
|
867
1137
|
expect(builderPrompt).toContain(
|
|
868
1138
|
"Treat the source brief's unknowns and follow-up items as assumptions",
|
|
869
1139
|
);
|
|
1140
|
+
expect(
|
|
1141
|
+
mocks.settings.get("workspace-app-metadata:org:org-123"),
|
|
1142
|
+
).toMatchObject({
|
|
1143
|
+
apps: {
|
|
1144
|
+
onboarding: { visibility: "private" },
|
|
1145
|
+
},
|
|
1146
|
+
});
|
|
870
1147
|
});
|
|
871
1148
|
|
|
872
1149
|
it("provisions and remembers the workspace Builder project when none is configured", async () => {
|