@agent-native/dispatch 0.2.10 → 0.2.12
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/actions/create-workspace-resource.js +4 -4
- package/dist/actions/create-workspace-resource.js.map +1 -1
- package/dist/actions/grant-workspace-resources-to-app.d.ts +3 -0
- package/dist/actions/grant-workspace-resources-to-app.d.ts.map +1 -0
- package/dist/actions/grant-workspace-resources-to-app.js +15 -0
- package/dist/actions/grant-workspace-resources-to-app.js.map +1 -0
- package/dist/actions/index.d.ts.map +1 -1
- package/dist/actions/index.js +4 -0
- package/dist/actions/index.js.map +1 -1
- package/dist/actions/list-workspace-resource-options.d.ts +3 -0
- package/dist/actions/list-workspace-resource-options.d.ts.map +1 -0
- package/dist/actions/list-workspace-resource-options.js +15 -0
- package/dist/actions/list-workspace-resource-options.js.map +1 -0
- package/dist/actions/list-workspace-resources.js +2 -2
- package/dist/actions/list-workspace-resources.js.map +1 -1
- package/dist/actions/start-workspace-app-creation.d.ts.map +1 -1
- package/dist/actions/start-workspace-app-creation.js +5 -0
- package/dist/actions/start-workspace-app-creation.js.map +1 -1
- package/dist/actions/view-screen.d.ts.map +1 -1
- package/dist/actions/view-screen.js +4 -0
- package/dist/actions/view-screen.js.map +1 -1
- package/dist/components/create-app-popover.d.ts +1 -1
- package/dist/components/create-app-popover.d.ts.map +1 -1
- package/dist/components/create-app-popover.js +63 -20
- package/dist/components/create-app-popover.js.map +1 -1
- package/dist/db/schema.js +2 -2
- package/dist/db/schema.js.map +1 -1
- package/dist/routes/pages/overview.d.ts.map +1 -1
- package/dist/routes/pages/overview.js +6 -6
- package/dist/routes/pages/overview.js.map +1 -1
- package/dist/routes/pages/workspace.d.ts.map +1 -1
- package/dist/routes/pages/workspace.js +17 -6
- package/dist/routes/pages/workspace.js.map +1 -1
- package/dist/server/lib/app-creation-store.d.ts +1 -0
- package/dist/server/lib/app-creation-store.d.ts.map +1 -1
- package/dist/server/lib/app-creation-store.js +33 -0
- package/dist/server/lib/app-creation-store.js.map +1 -1
- package/dist/server/lib/workspace-resources-store.d.ts +29 -1
- package/dist/server/lib/workspace-resources-store.d.ts.map +1 -1
- package/dist/server/lib/workspace-resources-store.js +46 -0
- package/dist/server/lib/workspace-resources-store.js.map +1 -1
- package/package.json +2 -3
- package/src/actions/create-workspace-resource.ts +4 -4
- package/src/actions/grant-workspace-resources-to-app.ts +16 -0
- package/src/actions/index.ts +4 -0
- package/src/actions/list-workspace-resource-options.ts +16 -0
- package/src/actions/list-workspace-resources.ts +2 -2
- package/src/actions/start-workspace-app-creation.ts +7 -0
- package/src/actions/view-screen.ts +4 -0
- package/src/components/create-app-popover.tsx +152 -21
- package/src/db/schema.ts +2 -2
- package/src/routes/pages/overview.tsx +21 -19
- package/src/routes/pages/workspace.tsx +31 -5
- package/src/server/lib/app-creation-store.ts +49 -0
- package/src/server/lib/workspace-resources-store.ts +75 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/dispatch",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.12",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"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.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -74,7 +74,6 @@
|
|
|
74
74
|
"embla-carousel-react": "^8.6.0",
|
|
75
75
|
"h3": "^2.0.1-rc.20",
|
|
76
76
|
"input-otp": "^1.4.2",
|
|
77
|
-
"lucide-react": "^1.8.0",
|
|
78
77
|
"next-themes": "^0.4.6",
|
|
79
78
|
"react-day-picker": "^9.14.0",
|
|
80
79
|
"react-hook-form": "^7.71.2",
|
|
@@ -97,7 +96,7 @@
|
|
|
97
96
|
"typescript": "^6.0.3",
|
|
98
97
|
"vite": "8.0.3",
|
|
99
98
|
"vitest": "^4.1.5",
|
|
100
|
-
"@agent-native/core": "0.12.
|
|
99
|
+
"@agent-native/core": "0.12.14"
|
|
101
100
|
},
|
|
102
101
|
"scripts": {
|
|
103
102
|
"build": "tsc && tsc-alias --resolve-full-paths",
|
|
@@ -4,17 +4,17 @@ import { createWorkspaceResource } from "../server/lib/workspace-resources-store
|
|
|
4
4
|
|
|
5
5
|
export default defineAction({
|
|
6
6
|
description:
|
|
7
|
-
'Create a workspace-wide skill, instruction, or
|
|
7
|
+
'Create a workspace-wide skill, instruction, agent profile, or knowledge pack. Set scope to "all" to push to every app, or "selected" to grant per-app.',
|
|
8
8
|
schema: z.object({
|
|
9
9
|
kind: z
|
|
10
|
-
.enum(["skill", "instruction", "agent"])
|
|
11
|
-
.describe("Resource kind: skill, instruction, or
|
|
10
|
+
.enum(["skill", "instruction", "agent", "knowledge"])
|
|
11
|
+
.describe("Resource kind: skill, instruction, agent, or knowledge"),
|
|
12
12
|
name: z.string().describe("Human-readable name"),
|
|
13
13
|
description: z.string().optional().describe("Short description"),
|
|
14
14
|
path: z
|
|
15
15
|
.string()
|
|
16
16
|
.describe(
|
|
17
|
-
'Resource path, e.g. "skills/designer.md", "agents/researcher.md", or "remote-agents/researcher.json"',
|
|
17
|
+
'Resource path, e.g. "skills/designer.md", "agents/researcher.md", "context/gtm-messaging.md", or "remote-agents/researcher.json"',
|
|
18
18
|
),
|
|
19
19
|
content: z
|
|
20
20
|
.string()
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { defineAction } from "@agent-native/core";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { grantWorkspaceResourcesToApp } from "../server/lib/workspace-resources-store.js";
|
|
4
|
+
|
|
5
|
+
export default defineAction({
|
|
6
|
+
description:
|
|
7
|
+
"Grant several selected workspace resources or knowledge packs to an app, skipping existing active grants.",
|
|
8
|
+
schema: z.object({
|
|
9
|
+
appId: z.string().describe("App ID receiving the resources"),
|
|
10
|
+
resourceIds: z
|
|
11
|
+
.array(z.string())
|
|
12
|
+
.max(100)
|
|
13
|
+
.describe("Workspace resource IDs to grant"),
|
|
14
|
+
}),
|
|
15
|
+
run: async (args) => grantWorkspaceResourcesToApp(args),
|
|
16
|
+
});
|
package/src/actions/index.ts
CHANGED
|
@@ -13,6 +13,7 @@ import denyVaultRequest from "./deny-vault-request.js";
|
|
|
13
13
|
import getAppCreationSettings from "./get-app-creation-settings.js";
|
|
14
14
|
import getDispatchSettings from "./get-dispatch-settings.js";
|
|
15
15
|
import getWorkspaceInfo from "./get-workspace-info.js";
|
|
16
|
+
import grantWorkspaceResourcesToApp from "./grant-workspace-resources-to-app.js";
|
|
16
17
|
import grantVaultSecretsToApp from "./grant-vault-secrets-to-app.js";
|
|
17
18
|
import listConnectedAgents from "./list-connected-agents.js";
|
|
18
19
|
import listDestinations from "./list-destinations.js";
|
|
@@ -28,6 +29,7 @@ import listVaultRequests from "./list-vault-requests.js";
|
|
|
28
29
|
import listVaultSecretOptions from "./list-vault-secret-options.js";
|
|
29
30
|
import listVaultSecrets from "./list-vault-secrets.js";
|
|
30
31
|
import listWorkspaceApps from "./list-workspace-apps.js";
|
|
32
|
+
import listWorkspaceResourceOptions from "./list-workspace-resource-options.js";
|
|
31
33
|
import listWorkspaceResourceGrants from "./list-workspace-resource-grants.js";
|
|
32
34
|
import listWorkspaceResources from "./list-workspace-resources.js";
|
|
33
35
|
import navigate from "./navigate.js";
|
|
@@ -68,6 +70,7 @@ export const dispatchActions: Record<string, ActionEntry> = {
|
|
|
68
70
|
"get-app-creation-settings": getAppCreationSettings,
|
|
69
71
|
"get-dispatch-settings": getDispatchSettings,
|
|
70
72
|
"get-workspace-info": getWorkspaceInfo,
|
|
73
|
+
"grant-workspace-resources-to-app": grantWorkspaceResourcesToApp,
|
|
71
74
|
"grant-vault-secrets-to-app": grantVaultSecretsToApp,
|
|
72
75
|
"list-connected-agents": listConnectedAgents,
|
|
73
76
|
"list-destinations": listDestinations,
|
|
@@ -83,6 +86,7 @@ export const dispatchActions: Record<string, ActionEntry> = {
|
|
|
83
86
|
"list-vault-secret-options": listVaultSecretOptions,
|
|
84
87
|
"list-vault-secrets": listVaultSecrets,
|
|
85
88
|
"list-workspace-apps": listWorkspaceApps,
|
|
89
|
+
"list-workspace-resource-options": listWorkspaceResourceOptions,
|
|
86
90
|
"list-workspace-resource-grants": listWorkspaceResourceGrants,
|
|
87
91
|
"list-workspace-resources": listWorkspaceResources,
|
|
88
92
|
navigate: navigate,
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { defineAction } from "@agent-native/core";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { listWorkspaceResourceOptions } from "../server/lib/workspace-resources-store.js";
|
|
4
|
+
|
|
5
|
+
export default defineAction({
|
|
6
|
+
description:
|
|
7
|
+
"List lightweight workspace resource options for selectors, without returning full content.",
|
|
8
|
+
schema: z.object({
|
|
9
|
+
kind: z
|
|
10
|
+
.enum(["skill", "instruction", "agent", "knowledge"])
|
|
11
|
+
.optional()
|
|
12
|
+
.describe("Filter by resource kind"),
|
|
13
|
+
}),
|
|
14
|
+
http: { method: "GET" },
|
|
15
|
+
run: async (args) => listWorkspaceResourceOptions(args),
|
|
16
|
+
});
|
|
@@ -4,10 +4,10 @@ import { listWorkspaceResources } from "../server/lib/workspace-resources-store.
|
|
|
4
4
|
|
|
5
5
|
export default defineAction({
|
|
6
6
|
description:
|
|
7
|
-
"List all workspace-wide resources (skills, instructions, agents) that can be shared across apps.",
|
|
7
|
+
"List all workspace-wide resources (skills, instructions, agents, and knowledge packs) that can be shared across apps.",
|
|
8
8
|
schema: z.object({
|
|
9
9
|
kind: z
|
|
10
|
-
.enum(["skill", "instruction", "agent"])
|
|
10
|
+
.enum(["skill", "instruction", "agent", "knowledge"])
|
|
11
11
|
.optional()
|
|
12
12
|
.describe("Filter by resource kind"),
|
|
13
13
|
}),
|
|
@@ -28,6 +28,13 @@ export default defineAction({
|
|
|
28
28
|
.max(100)
|
|
29
29
|
.optional()
|
|
30
30
|
.describe("Dispatch vault secret IDs to grant to the app"),
|
|
31
|
+
resourceIds: z
|
|
32
|
+
.array(z.string())
|
|
33
|
+
.max(100)
|
|
34
|
+
.optional()
|
|
35
|
+
.describe(
|
|
36
|
+
"Dispatch workspace resource IDs or knowledge packs to grant to the app",
|
|
37
|
+
),
|
|
31
38
|
}),
|
|
32
39
|
run: async (args) => startWorkspaceAppCreation(args),
|
|
33
40
|
});
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
} from "../server/lib/vault-store.js";
|
|
20
20
|
import { listWorkspaceApps } from "../server/lib/app-creation-store.js";
|
|
21
21
|
import { listDispatchUsageMetrics } from "../server/lib/usage-metrics-store.js";
|
|
22
|
+
import { listWorkspaceResourceOptions } from "../server/lib/workspace-resources-store.js";
|
|
22
23
|
|
|
23
24
|
export default defineAction({
|
|
24
25
|
description:
|
|
@@ -88,6 +89,9 @@ export default defineAction({
|
|
|
88
89
|
.map((g) => ({ secretId: g.secretId, appId: g.appId }));
|
|
89
90
|
screen.vaultPendingRequests = requests;
|
|
90
91
|
}
|
|
92
|
+
if (navigation?.view === "workspace" || navigation?.view === "new-app") {
|
|
93
|
+
screen.workspaceResources = await listWorkspaceResourceOptions();
|
|
94
|
+
}
|
|
91
95
|
|
|
92
96
|
if (Object.keys(screen).length === 0) {
|
|
93
97
|
return "No application state found. Is the app running?";
|
|
@@ -11,8 +11,10 @@ import { getWorkspaceAppIdValidationError } from "@agent-native/core/shared";
|
|
|
11
11
|
import {
|
|
12
12
|
IconArrowLeft,
|
|
13
13
|
IconArrowUpRight,
|
|
14
|
+
IconBook,
|
|
14
15
|
IconCheck,
|
|
15
16
|
IconChevronDown,
|
|
17
|
+
IconFileText,
|
|
16
18
|
IconKey,
|
|
17
19
|
IconLoader2,
|
|
18
20
|
IconPlus,
|
|
@@ -32,6 +34,16 @@ interface VaultSecretOption {
|
|
|
32
34
|
description?: string | null;
|
|
33
35
|
}
|
|
34
36
|
|
|
37
|
+
interface WorkspaceResourceOption {
|
|
38
|
+
id: string;
|
|
39
|
+
kind: "skill" | "instruction" | "agent" | "knowledge";
|
|
40
|
+
name: string;
|
|
41
|
+
description?: string | null;
|
|
42
|
+
path: string;
|
|
43
|
+
scope: "all" | "selected";
|
|
44
|
+
updatedAt?: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
35
47
|
interface CreateAppPopoverProps {
|
|
36
48
|
/**
|
|
37
49
|
* Custom trigger element. Defaults to a dashed-border tile that matches the
|
|
@@ -65,11 +77,20 @@ function buildAppCreationPrompt(input: {
|
|
|
65
77
|
appId: string;
|
|
66
78
|
prompt: string;
|
|
67
79
|
selectedKeys: string[];
|
|
80
|
+
selectedResources: WorkspaceResourceOption[];
|
|
68
81
|
}): string {
|
|
69
82
|
const keyList = input.selectedKeys.join(", ");
|
|
70
83
|
const grantRequest = keyList
|
|
71
84
|
? `Requested Dispatch vault key grants for this app: ${keyList}`
|
|
72
85
|
: `Requested Dispatch vault key grants for this app: none`;
|
|
86
|
+
const resourceList = input.selectedResources.length
|
|
87
|
+
? input.selectedResources
|
|
88
|
+
.map(
|
|
89
|
+
(resource) =>
|
|
90
|
+
`- ${resource.name} (${resource.kind}, ${resource.path})`,
|
|
91
|
+
)
|
|
92
|
+
.join("\n")
|
|
93
|
+
: "none";
|
|
73
94
|
|
|
74
95
|
return [
|
|
75
96
|
`Create a new agent-native app in this workspace.`,
|
|
@@ -78,6 +99,7 @@ function buildAppCreationPrompt(input: {
|
|
|
78
99
|
`Suggested app name: ${input.appId} (you may adjust the slug if it conflicts)`,
|
|
79
100
|
`User prompt: ${input.prompt.trim()}`,
|
|
80
101
|
grantRequest,
|
|
102
|
+
`Requested Dispatch workspace resources for this app:\n${resourceList}`,
|
|
81
103
|
``,
|
|
82
104
|
`Pick a starter template that fits the user's prompt — analytics, calendar, content, design, dispatch, forms, mail, slides, clips, or starter when none of the others fit.`,
|
|
83
105
|
`Use the workspace app layout: create it under apps/${input.appId}, mount it at /${input.appId}, keep it on the shared workspace database/hosting model, and avoid table-name collisions by namespacing any new domain tables to the app.`,
|
|
@@ -88,6 +110,9 @@ function buildAppCreationPrompt(input: {
|
|
|
88
110
|
keyList
|
|
89
111
|
? `After the app exists, grant the selected Dispatch vault keys to appId "${input.appId}" and sync them once the app server is available. Treat these as requested grants, not active grants before creation succeeds.`
|
|
90
112
|
: `Do not grant any Dispatch vault keys unless the user asks later.`,
|
|
113
|
+
input.selectedResources.length
|
|
114
|
+
? `After the app exists, grant the selected Dispatch workspace resources to appId "${input.appId}" and sync them once the app server is available. Add a short note to apps/${input.appId}/AGENTS.md telling the app agent to read relevant shared resources under context/ or the selected resource paths before doing GTM/domain work.`
|
|
115
|
+
: `Do not grant any Dispatch workspace resources unless the user asks later.`,
|
|
91
116
|
``,
|
|
92
117
|
`App readiness requirements before handing off:`,
|
|
93
118
|
`- Ensure apps/${input.appId}/package.json exists; Dispatch discovers workspace apps from apps/<app-id>/package.json, not a separate app registry.`,
|
|
@@ -123,7 +148,7 @@ function actionUrl(basePath: string | null, action: string): string {
|
|
|
123
148
|
}
|
|
124
149
|
|
|
125
150
|
/**
|
|
126
|
-
* Inline two-step app-creation flow: prompt → optional
|
|
151
|
+
* Inline two-step app-creation flow: prompt → optional access picker → submit.
|
|
127
152
|
* Used both in the popover form and in the dedicated `/new-app` page so the
|
|
128
153
|
* same UX shows up everywhere a teammate kicks off a new workspace app.
|
|
129
154
|
*/
|
|
@@ -134,11 +159,14 @@ export function CreateAppFlow({
|
|
|
134
159
|
onClose?: () => void;
|
|
135
160
|
className?: string;
|
|
136
161
|
}) {
|
|
137
|
-
const [step, setStep] = useState<"prompt" | "
|
|
162
|
+
const [step, setStep] = useState<"prompt" | "access">("prompt");
|
|
138
163
|
const [prompt, setPrompt] = useState("");
|
|
139
164
|
const [selectedSecretIds, setSelectedSecretIds] = useState<string[]>([]);
|
|
165
|
+
const [selectedResourceIds, setSelectedResourceIds] = useState<string[]>([]);
|
|
140
166
|
const [secrets, setSecrets] = useState<VaultSecretOption[]>([]);
|
|
167
|
+
const [resources, setResources] = useState<WorkspaceResourceOption[]>([]);
|
|
141
168
|
const [secretsError, setSecretsError] = useState<string | null>(null);
|
|
169
|
+
const [resourcesError, setResourcesError] = useState<string | null>(null);
|
|
142
170
|
const [statusMessage, setStatusMessage] = useState<string | null>(null);
|
|
143
171
|
const [branchUrl, setBranchUrl] = useState<string | null>(null);
|
|
144
172
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
@@ -146,8 +174,7 @@ export function CreateAppFlow({
|
|
|
146
174
|
|
|
147
175
|
const basePath = useMemo(() => defaultDispatchBasePath(), []);
|
|
148
176
|
|
|
149
|
-
// Fetch
|
|
150
|
-
// taps "Choose keys" — no spinner, no pause between steps.
|
|
177
|
+
// Fetch access options eagerly so step 2 has them ready immediately.
|
|
151
178
|
useEffect(() => {
|
|
152
179
|
let cancelled = false;
|
|
153
180
|
fetchJson(actionUrl(basePath, "list-vault-secret-options"))
|
|
@@ -161,6 +188,17 @@ export function CreateAppFlow({
|
|
|
161
188
|
setSecrets([]);
|
|
162
189
|
setSecretsError(err?.message || "Could not load Dispatch keys");
|
|
163
190
|
});
|
|
191
|
+
fetchJson(actionUrl(basePath, "list-workspace-resource-options"))
|
|
192
|
+
.then((data) => {
|
|
193
|
+
if (cancelled) return;
|
|
194
|
+
setResources(Array.isArray(data) ? data : []);
|
|
195
|
+
setResourcesError(null);
|
|
196
|
+
})
|
|
197
|
+
.catch((err) => {
|
|
198
|
+
if (cancelled) return;
|
|
199
|
+
setResources([]);
|
|
200
|
+
setResourcesError(err?.message || "Could not load Dispatch resources");
|
|
201
|
+
});
|
|
164
202
|
return () => {
|
|
165
203
|
cancelled = true;
|
|
166
204
|
};
|
|
@@ -170,10 +208,21 @@ export function CreateAppFlow({
|
|
|
170
208
|
() => secrets.filter((s) => selectedSecretIds.includes(s.id)),
|
|
171
209
|
[secrets, selectedSecretIds],
|
|
172
210
|
);
|
|
211
|
+
const selectedResources = useMemo(
|
|
212
|
+
() => resources.filter((r) => selectedResourceIds.includes(r.id)),
|
|
213
|
+
[resources, selectedResourceIds],
|
|
214
|
+
);
|
|
173
215
|
const selectedSecretLabel =
|
|
174
216
|
selectedSecretIds.length === 0
|
|
175
217
|
? "no keys"
|
|
176
218
|
: `${selectedSecretIds.length} key${selectedSecretIds.length === 1 ? "" : "s"}`;
|
|
219
|
+
const selectedResourceLabel =
|
|
220
|
+
selectedResourceIds.length === 0
|
|
221
|
+
? "no resources"
|
|
222
|
+
: `${selectedResourceIds.length} resource${selectedResourceIds.length === 1 ? "" : "s"}`;
|
|
223
|
+
const selectedAccessLabel = [selectedSecretLabel, selectedResourceLabel].join(
|
|
224
|
+
" · ",
|
|
225
|
+
);
|
|
177
226
|
|
|
178
227
|
function toggleSecret(id: string) {
|
|
179
228
|
setSelectedSecretIds((cur) =>
|
|
@@ -181,7 +230,13 @@ export function CreateAppFlow({
|
|
|
181
230
|
);
|
|
182
231
|
}
|
|
183
232
|
|
|
184
|
-
|
|
233
|
+
function toggleResource(id: string) {
|
|
234
|
+
setSelectedResourceIds((cur) =>
|
|
235
|
+
cur.includes(id) ? cur.filter((x) => x !== id) : [...cur, id],
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
async function submit(rawPrompt: string) {
|
|
185
240
|
const trimmed = rawPrompt.trim();
|
|
186
241
|
if (!trimmed || isSubmitting) return;
|
|
187
242
|
const appId = titleFromPrompt(trimmed);
|
|
@@ -194,7 +249,8 @@ export function CreateAppFlow({
|
|
|
194
249
|
const message = buildAppCreationPrompt({
|
|
195
250
|
appId,
|
|
196
251
|
prompt: trimmed,
|
|
197
|
-
selectedKeys,
|
|
252
|
+
selectedKeys: selectedSecrets.map((s) => s.credentialKey),
|
|
253
|
+
selectedResources,
|
|
198
254
|
});
|
|
199
255
|
setIsSubmitting(true);
|
|
200
256
|
setStatusMessage(null);
|
|
@@ -218,7 +274,9 @@ export function CreateAppFlow({
|
|
|
218
274
|
body: JSON.stringify({
|
|
219
275
|
prompt: trimmed,
|
|
220
276
|
appId,
|
|
221
|
-
secretIds:
|
|
277
|
+
secretIds: selectedSecretIds.length > 0 ? selectedSecretIds : [],
|
|
278
|
+
resourceIds:
|
|
279
|
+
selectedResourceIds.length > 0 ? selectedResourceIds : [],
|
|
222
280
|
}),
|
|
223
281
|
},
|
|
224
282
|
);
|
|
@@ -239,11 +297,7 @@ export function CreateAppFlow({
|
|
|
239
297
|
}
|
|
240
298
|
}
|
|
241
299
|
|
|
242
|
-
const
|
|
243
|
-
submit(
|
|
244
|
-
prompt,
|
|
245
|
-
selectedSecrets.map((s) => s.credentialKey),
|
|
246
|
-
);
|
|
300
|
+
const submitWithSelectedAccess = () => submit(prompt);
|
|
247
301
|
|
|
248
302
|
return (
|
|
249
303
|
<div className={`flex flex-col gap-3 ${className}`}>
|
|
@@ -253,11 +307,11 @@ export function CreateAppFlow({
|
|
|
253
307
|
<p className="text-sm font-semibold text-foreground">Create app</p>
|
|
254
308
|
<button
|
|
255
309
|
type="button"
|
|
256
|
-
onClick={() => setStep("
|
|
310
|
+
onClick={() => setStep("access")}
|
|
257
311
|
className="inline-flex cursor-pointer items-center gap-1 rounded-md border border-border bg-background/40 px-2 py-1 text-[11px] text-muted-foreground hover:text-foreground hover:bg-accent/50"
|
|
258
312
|
>
|
|
259
313
|
<IconKey size={11} />
|
|
260
|
-
{
|
|
314
|
+
{selectedAccessLabel}
|
|
261
315
|
</button>
|
|
262
316
|
</div>
|
|
263
317
|
<PromptComposer
|
|
@@ -267,10 +321,7 @@ export function CreateAppFlow({
|
|
|
267
321
|
draftScope="dispatch:create-app"
|
|
268
322
|
onSubmit={(text) => {
|
|
269
323
|
setPrompt(text);
|
|
270
|
-
submit(
|
|
271
|
-
text,
|
|
272
|
-
selectedSecrets.map((s) => s.credentialKey),
|
|
273
|
-
);
|
|
324
|
+
submit(text);
|
|
274
325
|
}}
|
|
275
326
|
/>
|
|
276
327
|
</>
|
|
@@ -286,10 +337,14 @@ export function CreateAppFlow({
|
|
|
286
337
|
Back
|
|
287
338
|
</button>
|
|
288
339
|
<span className="text-[11px] text-muted-foreground/70">
|
|
289
|
-
{
|
|
340
|
+
{selectedAccessLabel}
|
|
290
341
|
</span>
|
|
291
342
|
</div>
|
|
292
|
-
<div className="max-h-[
|
|
343
|
+
<div className="max-h-[180px] space-y-2 overflow-y-auto rounded-md border border-border bg-card p-2">
|
|
344
|
+
<div className="flex items-center gap-1.5 px-1 pb-1 text-[11px] font-medium text-muted-foreground">
|
|
345
|
+
<IconKey size={12} />
|
|
346
|
+
Dispatch keys
|
|
347
|
+
</div>
|
|
293
348
|
{secretsError ? (
|
|
294
349
|
<p className="rounded-md border border-dashed border-border px-3 py-3 text-xs text-muted-foreground">
|
|
295
350
|
{secretsError}
|
|
@@ -355,11 +410,87 @@ export function CreateAppFlow({
|
|
|
355
410
|
})
|
|
356
411
|
)}
|
|
357
412
|
</div>
|
|
413
|
+
<div className="max-h-[180px] space-y-2 overflow-y-auto rounded-md border border-border bg-card p-2">
|
|
414
|
+
<div className="flex items-center gap-1.5 px-1 pb-1 text-[11px] font-medium text-muted-foreground">
|
|
415
|
+
<IconBook size={12} />
|
|
416
|
+
Resource packs
|
|
417
|
+
</div>
|
|
418
|
+
{resourcesError ? (
|
|
419
|
+
<p className="rounded-md border border-dashed border-border px-3 py-3 text-xs text-muted-foreground">
|
|
420
|
+
{resourcesError}
|
|
421
|
+
</p>
|
|
422
|
+
) : resources.length === 0 ? (
|
|
423
|
+
<p className="rounded-md border border-dashed border-border px-3 py-3 text-xs text-muted-foreground">
|
|
424
|
+
No Dispatch resource packs found yet.
|
|
425
|
+
</p>
|
|
426
|
+
) : (
|
|
427
|
+
resources.map((resource) => {
|
|
428
|
+
const selected = selectedResourceIds.includes(resource.id);
|
|
429
|
+
return (
|
|
430
|
+
<div
|
|
431
|
+
key={resource.id}
|
|
432
|
+
className={`group rounded-md border text-sm ${
|
|
433
|
+
selected
|
|
434
|
+
? "border-primary/45 bg-primary/5"
|
|
435
|
+
: "border-border hover:border-muted-foreground/40 hover:bg-accent/35"
|
|
436
|
+
}`}
|
|
437
|
+
>
|
|
438
|
+
<button
|
|
439
|
+
type="button"
|
|
440
|
+
aria-pressed={selected}
|
|
441
|
+
onClick={() => toggleResource(resource.id)}
|
|
442
|
+
className="flex w-full cursor-pointer items-start gap-3 rounded-md px-3 py-2 text-left"
|
|
443
|
+
>
|
|
444
|
+
<span
|
|
445
|
+
className={`mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded border ${
|
|
446
|
+
selected
|
|
447
|
+
? "border-primary/60 bg-primary/10 text-primary"
|
|
448
|
+
: "border-muted-foreground/35 text-transparent"
|
|
449
|
+
}`}
|
|
450
|
+
>
|
|
451
|
+
{selected ? <IconCheck className="h-3 w-3" /> : null}
|
|
452
|
+
</span>
|
|
453
|
+
<span className="min-w-0 flex-1">
|
|
454
|
+
<span className="flex min-w-0 items-center gap-1.5">
|
|
455
|
+
<IconFileText className="h-3.5 w-3.5 shrink-0 text-muted-foreground/70" />
|
|
456
|
+
<span className="block truncate font-medium">
|
|
457
|
+
{resource.name}
|
|
458
|
+
</span>
|
|
459
|
+
</span>
|
|
460
|
+
<span className="block truncate text-xs text-muted-foreground/70">
|
|
461
|
+
{resource.kind} · {resource.path}
|
|
462
|
+
</span>
|
|
463
|
+
</span>
|
|
464
|
+
</button>
|
|
465
|
+
<details className="group/details border-t border-border/60 px-3 py-1.5 text-xs text-muted-foreground/75">
|
|
466
|
+
<summary className="flex cursor-pointer list-none items-center gap-1.5 text-[11px] hover:text-muted-foreground [&::-webkit-details-marker]:hidden">
|
|
467
|
+
<IconChevronDown className="h-3 w-3 transition-transform group-open/details:rotate-180" />
|
|
468
|
+
Details
|
|
469
|
+
</summary>
|
|
470
|
+
<div className="mt-1.5 space-y-1 pb-0.5 pl-4">
|
|
471
|
+
<div className="truncate">
|
|
472
|
+
Scope:{" "}
|
|
473
|
+
{resource.scope === "all"
|
|
474
|
+
? "All apps"
|
|
475
|
+
: "Selected apps"}
|
|
476
|
+
</div>
|
|
477
|
+
{resource.description ? (
|
|
478
|
+
<div className="line-clamp-2">
|
|
479
|
+
{resource.description}
|
|
480
|
+
</div>
|
|
481
|
+
) : null}
|
|
482
|
+
</div>
|
|
483
|
+
</details>
|
|
484
|
+
</div>
|
|
485
|
+
);
|
|
486
|
+
})
|
|
487
|
+
)}
|
|
488
|
+
</div>
|
|
358
489
|
<div className="flex items-center justify-end gap-2">
|
|
359
490
|
<Button
|
|
360
491
|
type="button"
|
|
361
492
|
size="sm"
|
|
362
|
-
onClick={
|
|
493
|
+
onClick={submitWithSelectedAccess}
|
|
363
494
|
disabled={!prompt.trim() || isSubmitting}
|
|
364
495
|
>
|
|
365
496
|
{isSubmitting ? (
|
package/src/db/schema.ts
CHANGED
|
@@ -130,13 +130,13 @@ export const vaultAuditLog = table("vault_audit_log", {
|
|
|
130
130
|
createdAt: integer("created_at").notNull(),
|
|
131
131
|
});
|
|
132
132
|
|
|
133
|
-
// ─── Workspace Resources: shared skills, instructions, agents ──────
|
|
133
|
+
// ─── Workspace Resources: shared skills, instructions, agents, knowledge ──────
|
|
134
134
|
|
|
135
135
|
export const workspaceResources = table("workspace_resources", {
|
|
136
136
|
id: text("id").primaryKey(),
|
|
137
137
|
ownerEmail: text("owner_email").notNull(),
|
|
138
138
|
orgId: text("org_id"),
|
|
139
|
-
kind: text("kind").notNull(), // "skill" | "instruction" | "agent"
|
|
139
|
+
kind: text("kind").notNull(), // "skill" | "instruction" | "agent" | "knowledge"
|
|
140
140
|
name: text("name").notNull(),
|
|
141
141
|
description: text("description"),
|
|
142
142
|
path: text("path").notNull(), // resource path, e.g. "skills/designer.md"
|
|
@@ -108,25 +108,27 @@ function HomeChatPanel() {
|
|
|
108
108
|
<h1 className="text-center text-2xl font-semibold tracking-tight text-foreground sm:text-3xl">
|
|
109
109
|
What should we do next?
|
|
110
110
|
</h1>
|
|
111
|
-
<
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
111
|
+
<div className="flex flex-col gap-4">
|
|
112
|
+
<PromptComposer
|
|
113
|
+
placeholder="Message agent…"
|
|
114
|
+
onSubmit={(text) => {
|
|
115
|
+
const trimmed = text.trim();
|
|
116
|
+
if (!trimmed) return;
|
|
117
|
+
send(trimmed);
|
|
118
|
+
}}
|
|
119
|
+
/>
|
|
120
|
+
<div className="flex flex-wrap justify-center gap-2">
|
|
121
|
+
{HOME_CHAT_SUGGESTIONS.map((suggestion) => (
|
|
122
|
+
<button
|
|
123
|
+
key={suggestion}
|
|
124
|
+
type="button"
|
|
125
|
+
onClick={() => send(suggestion)}
|
|
126
|
+
className="cursor-pointer rounded-full border border-border bg-card px-3 py-1.5 text-xs text-muted-foreground transition hover:border-foreground/30 hover:text-foreground"
|
|
127
|
+
>
|
|
128
|
+
{suggestion}
|
|
129
|
+
</button>
|
|
130
|
+
))}
|
|
131
|
+
</div>
|
|
130
132
|
</div>
|
|
131
133
|
</div>
|
|
132
134
|
</section>
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
IconChevronDown,
|
|
7
7
|
IconChevronRight,
|
|
8
8
|
IconCode,
|
|
9
|
+
IconFileText,
|
|
9
10
|
IconPlus,
|
|
10
11
|
IconRefresh,
|
|
11
12
|
IconTrash,
|
|
@@ -72,6 +73,13 @@ const KIND_CONFIG = {
|
|
|
72
73
|
description:
|
|
73
74
|
"Reusable agent profiles — specialist agents shared across apps",
|
|
74
75
|
},
|
|
76
|
+
knowledge: {
|
|
77
|
+
label: "Knowledge",
|
|
78
|
+
icon: IconFileText,
|
|
79
|
+
pathPrefix: "context/",
|
|
80
|
+
description:
|
|
81
|
+
"Knowledge packs — reusable GTM, product, and domain context for apps",
|
|
82
|
+
},
|
|
75
83
|
} as const;
|
|
76
84
|
|
|
77
85
|
function AddResourceDialog() {
|
|
@@ -127,6 +135,7 @@ function AddResourceDialog() {
|
|
|
127
135
|
<SelectItem value="skill">Skill</SelectItem>
|
|
128
136
|
<SelectItem value="instruction">Instruction</SelectItem>
|
|
129
137
|
<SelectItem value="agent">Agent</SelectItem>
|
|
138
|
+
<SelectItem value="knowledge">Knowledge pack</SelectItem>
|
|
130
139
|
</SelectContent>
|
|
131
140
|
</Select>
|
|
132
141
|
</div>
|
|
@@ -151,7 +160,9 @@ function AddResourceDialog() {
|
|
|
151
160
|
? "Frontend Designer"
|
|
152
161
|
: kind === "agent"
|
|
153
162
|
? "Research Specialist"
|
|
154
|
-
:
|
|
163
|
+
: kind === "knowledge"
|
|
164
|
+
? "Core GTM Messaging"
|
|
165
|
+
: "Code Style Guide"
|
|
155
166
|
}
|
|
156
167
|
value={name}
|
|
157
168
|
onChange={(e) => setName(e.target.value)}
|
|
@@ -167,7 +178,7 @@ function AddResourceDialog() {
|
|
|
167
178
|
/>
|
|
168
179
|
<p className="text-xs text-muted-foreground">
|
|
169
180
|
Resource path in target apps. Skills go in skills/, agents in
|
|
170
|
-
agents/.
|
|
181
|
+
agents/, knowledge packs in context/.
|
|
171
182
|
</p>
|
|
172
183
|
</div>
|
|
173
184
|
<div className="space-y-2">
|
|
@@ -186,7 +197,9 @@ function AddResourceDialog() {
|
|
|
186
197
|
? "---\nname: my-skill\ndescription: What this skill teaches\n---\n\n# My Skill\n\n..."
|
|
187
198
|
: kind === "agent"
|
|
188
199
|
? "---\nname: Research Specialist\ndescription: Handles research tasks\n---\n\n# Instructions\n\n..."
|
|
189
|
-
:
|
|
200
|
+
: kind === "knowledge"
|
|
201
|
+
? "# Core GTM Messaging\n\n## Positioning\n\n## ICP\n\n## Proof points\n\n## Source\n\n"
|
|
202
|
+
: "# Instructions\n\nBehavioral rules and guidance for agents across apps..."
|
|
190
203
|
}
|
|
191
204
|
value={content}
|
|
192
205
|
onChange={(e) => setContent(e.target.value)}
|
|
@@ -199,7 +212,7 @@ function AddResourceDialog() {
|
|
|
199
212
|
<Button
|
|
200
213
|
onClick={() =>
|
|
201
214
|
create.mutate({
|
|
202
|
-
kind: kind as "skill" | "instruction" | "agent",
|
|
215
|
+
kind: kind as "skill" | "instruction" | "agent" | "knowledge",
|
|
203
216
|
name,
|
|
204
217
|
description: description || undefined,
|
|
205
218
|
path:
|
|
@@ -504,6 +517,9 @@ export default function WorkspaceRoute() {
|
|
|
504
517
|
(r: any) => r.kind === "instruction",
|
|
505
518
|
);
|
|
506
519
|
const agents = (resources || []).filter((r: any) => r.kind === "agent");
|
|
520
|
+
const knowledge = (resources || []).filter(
|
|
521
|
+
(r: any) => r.kind === "knowledge",
|
|
522
|
+
);
|
|
507
523
|
|
|
508
524
|
function ResourceList({
|
|
509
525
|
items,
|
|
@@ -535,7 +551,7 @@ export default function WorkspaceRoute() {
|
|
|
535
551
|
return (
|
|
536
552
|
<DispatchShell
|
|
537
553
|
title="Workspace Resources"
|
|
538
|
-
description="Share skills, instructions,
|
|
554
|
+
description="Share skills, instructions, agent profiles, and knowledge packs across workspace apps. Scope to all apps or grant per-app."
|
|
539
555
|
>
|
|
540
556
|
<div className="flex items-center justify-between">
|
|
541
557
|
<div className="text-sm text-muted-foreground">
|
|
@@ -570,6 +586,9 @@ export default function WorkspaceRoute() {
|
|
|
570
586
|
<TabsTrigger value="agents">
|
|
571
587
|
Agents {agents.length > 0 && `(${agents.length})`}
|
|
572
588
|
</TabsTrigger>
|
|
589
|
+
<TabsTrigger value="knowledge">
|
|
590
|
+
Knowledge {knowledge.length > 0 && `(${knowledge.length})`}
|
|
591
|
+
</TabsTrigger>
|
|
573
592
|
</TabsList>
|
|
574
593
|
|
|
575
594
|
<TabsContent value="skills" className="mt-4">
|
|
@@ -592,6 +611,13 @@ export default function WorkspaceRoute() {
|
|
|
592
611
|
emptyText="No workspace agents yet. Add a reusable agent profile to share specialist agents across apps."
|
|
593
612
|
/>
|
|
594
613
|
</TabsContent>
|
|
614
|
+
|
|
615
|
+
<TabsContent value="knowledge" className="mt-4">
|
|
616
|
+
<ResourceList
|
|
617
|
+
items={knowledge}
|
|
618
|
+
emptyText="No knowledge packs yet. Add GTM, product, or domain context that apps can reuse."
|
|
619
|
+
/>
|
|
620
|
+
</TabsContent>
|
|
595
621
|
</Tabs>
|
|
596
622
|
</DispatchShell>
|
|
597
623
|
);
|