@agent-native/dispatch 0.2.11 → 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.
Files changed (51) hide show
  1. package/dist/actions/create-workspace-resource.js +4 -4
  2. package/dist/actions/create-workspace-resource.js.map +1 -1
  3. package/dist/actions/grant-workspace-resources-to-app.d.ts +3 -0
  4. package/dist/actions/grant-workspace-resources-to-app.d.ts.map +1 -0
  5. package/dist/actions/grant-workspace-resources-to-app.js +15 -0
  6. package/dist/actions/grant-workspace-resources-to-app.js.map +1 -0
  7. package/dist/actions/index.d.ts.map +1 -1
  8. package/dist/actions/index.js +4 -0
  9. package/dist/actions/index.js.map +1 -1
  10. package/dist/actions/list-workspace-resource-options.d.ts +3 -0
  11. package/dist/actions/list-workspace-resource-options.d.ts.map +1 -0
  12. package/dist/actions/list-workspace-resource-options.js +15 -0
  13. package/dist/actions/list-workspace-resource-options.js.map +1 -0
  14. package/dist/actions/list-workspace-resources.js +2 -2
  15. package/dist/actions/list-workspace-resources.js.map +1 -1
  16. package/dist/actions/start-workspace-app-creation.d.ts.map +1 -1
  17. package/dist/actions/start-workspace-app-creation.js +5 -0
  18. package/dist/actions/start-workspace-app-creation.js.map +1 -1
  19. package/dist/actions/view-screen.d.ts.map +1 -1
  20. package/dist/actions/view-screen.js +4 -0
  21. package/dist/actions/view-screen.js.map +1 -1
  22. package/dist/components/create-app-popover.d.ts +1 -1
  23. package/dist/components/create-app-popover.d.ts.map +1 -1
  24. package/dist/components/create-app-popover.js +63 -20
  25. package/dist/components/create-app-popover.js.map +1 -1
  26. package/dist/db/schema.js +2 -2
  27. package/dist/db/schema.js.map +1 -1
  28. package/dist/routes/pages/workspace.d.ts.map +1 -1
  29. package/dist/routes/pages/workspace.js +17 -6
  30. package/dist/routes/pages/workspace.js.map +1 -1
  31. package/dist/server/lib/app-creation-store.d.ts +1 -0
  32. package/dist/server/lib/app-creation-store.d.ts.map +1 -1
  33. package/dist/server/lib/app-creation-store.js +33 -0
  34. package/dist/server/lib/app-creation-store.js.map +1 -1
  35. package/dist/server/lib/workspace-resources-store.d.ts +29 -1
  36. package/dist/server/lib/workspace-resources-store.d.ts.map +1 -1
  37. package/dist/server/lib/workspace-resources-store.js +46 -0
  38. package/dist/server/lib/workspace-resources-store.js.map +1 -1
  39. package/package.json +2 -2
  40. package/src/actions/create-workspace-resource.ts +4 -4
  41. package/src/actions/grant-workspace-resources-to-app.ts +16 -0
  42. package/src/actions/index.ts +4 -0
  43. package/src/actions/list-workspace-resource-options.ts +16 -0
  44. package/src/actions/list-workspace-resources.ts +2 -2
  45. package/src/actions/start-workspace-app-creation.ts +7 -0
  46. package/src/actions/view-screen.ts +4 -0
  47. package/src/components/create-app-popover.tsx +152 -21
  48. package/src/db/schema.ts +2 -2
  49. package/src/routes/pages/workspace.tsx +31 -5
  50. package/src/server/lib/app-creation-store.ts +49 -0
  51. package/src/server/lib/workspace-resources-store.ts +75 -1
@@ -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
- : "Code Style Guide"
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
- : "# Instructions\n\nBehavioral rules and guidance for agents across apps..."
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, and agent profiles across workspace apps. Scope to all apps or grant per-app."
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
  );
@@ -20,6 +20,11 @@ import {
20
20
  } from "./dispatch-store.js";
21
21
  import { identityKeyForIncoming } from "./dispatch-integrations.js";
22
22
  import { createRequest, listSecrets } from "./vault-store.js";
23
+ import {
24
+ grantWorkspaceResourcesToApp,
25
+ listWorkspaceResourceOptions,
26
+ type WorkspaceResourceOption,
27
+ } from "./workspace-resources-store.js";
23
28
 
24
29
  const SETTINGS_KEY = "dispatch-app-creation-settings";
25
30
  const WORKSPACE_APPS_ENV_KEY = "AGENT_NATIVE_WORKSPACE_APPS_JSON";
@@ -827,6 +832,7 @@ function buildWorkspaceAppPrompt(input: {
827
832
  appId?: string | null;
828
833
  template?: string | null;
829
834
  selectedKeys?: string[];
835
+ selectedResources?: WorkspaceResourceOption[];
830
836
  }): { appId: string; prompt: string } {
831
837
  const appId =
832
838
  slugify(input.appId || "") ||
@@ -835,6 +841,15 @@ function buildWorkspaceAppPrompt(input: {
835
841
  ) ||
836
842
  "new-app";
837
843
  const selectedKeys = input.selectedKeys || [];
844
+ const selectedResources = input.selectedResources || [];
845
+ const resourceList = selectedResources.length
846
+ ? selectedResources
847
+ .map(
848
+ (resource) =>
849
+ `- ${resource.name} (${resource.kind}, ${resource.path})`,
850
+ )
851
+ .join("\n")
852
+ : "none";
838
853
  return {
839
854
  appId,
840
855
  prompt: [
@@ -846,11 +861,15 @@ function buildWorkspaceAppPrompt(input: {
846
861
  selectedKeys.length
847
862
  ? `Dispatch vault keys selected for this app: ${selectedKeys.join(", ")}`
848
863
  : "Dispatch vault keys selected for this app: none",
864
+ `Dispatch workspace resources selected for this app:\n${resourceList}`,
849
865
  "",
850
866
  `Use the workspace app layout: create it under apps/${appId}, mount it at /${appId}, keep it on the shared workspace database/hosting model, and avoid table-name collisions by namespacing any new domain tables to the app.`,
851
867
  selectedKeys.length
852
868
  ? `Dispatch will create pending vault requests for the selected keys for appId "${appId}" after this app creation request is accepted. Do not grant or sync vault keys directly from the app-creation branch.`
853
869
  : "Do not grant or request any Dispatch vault keys unless the user asks later.",
870
+ selectedResources.length
871
+ ? `Dispatch will create workspace resource grants for the selected resources for appId "${appId}". After the app exists, sync workspace resources so the app receives those shared resources. Add a short note to apps/${appId}/AGENTS.md telling the app agent to read relevant shared resources under context/ or the selected resource paths before doing GTM/domain work.`
872
+ : "Do not grant any Dispatch workspace resources unless the user asks later.",
854
873
  "",
855
874
  "Agent-native rules (these are the framework's contract — not optional):",
856
875
  `- Persist ALL data in SQL via Drizzle. Add tables to apps/${appId}/server/db/schema.ts and migrations to apps/${appId}/server/plugins/db.ts. NEVER use localStorage, sessionStorage, IndexedDB, or in-memory state for anything the user expects to persist — agent and UI must read the same source of truth.`,
@@ -890,11 +909,29 @@ async function requestSelectedVaultKeys(input: {
890
909
  );
891
910
  }
892
911
 
912
+ async function selectedWorkspaceResourcesForIds(
913
+ resourceIds: string[] | undefined,
914
+ ): Promise<WorkspaceResourceOption[]> {
915
+ if (!resourceIds?.length) return [];
916
+ const requested = new Set(resourceIds);
917
+ const resources = await listWorkspaceResourceOptions();
918
+ return resources.filter((resource) => requested.has(resource.id));
919
+ }
920
+
921
+ async function grantSelectedWorkspaceResources(input: {
922
+ appId: string;
923
+ resourceIds: string[];
924
+ }) {
925
+ if (input.resourceIds.length === 0) return;
926
+ await grantWorkspaceResourcesToApp(input);
927
+ }
928
+
893
929
  export async function startWorkspaceAppCreation(input: {
894
930
  prompt: string;
895
931
  appId?: string | null;
896
932
  template?: string | null;
897
933
  secretIds?: string[];
934
+ resourceIds?: string[];
898
935
  }) {
899
936
  const initial = buildWorkspaceAppPrompt({
900
937
  prompt: input.prompt,
@@ -920,11 +957,15 @@ export async function startWorkspaceAppCreation(input: {
920
957
  .filter((secret) => input.secretIds?.includes(secret.id))
921
958
  .map((secret) => secret.credentialKey)
922
959
  : [];
960
+ const selectedResources = await selectedWorkspaceResourcesForIds(
961
+ input.resourceIds,
962
+ );
923
963
  const built = buildWorkspaceAppPrompt({
924
964
  prompt: input.prompt,
925
965
  appId: input.appId,
926
966
  template: input.template,
927
967
  selectedKeys,
968
+ selectedResources,
928
969
  });
929
970
  const prompt = built.prompt;
930
971
 
@@ -933,6 +974,10 @@ export async function startWorkspaceAppCreation(input: {
933
974
  appId: built.appId,
934
975
  selectedKeys,
935
976
  });
977
+ await grantSelectedWorkspaceResources({
978
+ appId: built.appId,
979
+ resourceIds: selectedResources.map((resource) => resource.id),
980
+ });
936
981
  return {
937
982
  mode: "local-agent",
938
983
  appId: built.appId,
@@ -997,6 +1042,10 @@ export async function startWorkspaceAppCreation(input: {
997
1042
  appId: built.appId,
998
1043
  selectedKeys,
999
1044
  });
1045
+ await grantSelectedWorkspaceResources({
1046
+ appId: built.appId,
1047
+ resourceIds: selectedResources.map((resource) => resource.id),
1048
+ });
1000
1049
 
1001
1050
  return {
1002
1051
  mode: "builder",
@@ -54,7 +54,11 @@ function orgFilter<T extends { ownerEmail: any; orgId: any }>(table: T) {
54
54
 
55
55
  // ─── Workspace Resources CRUD ──────────────────────────────────
56
56
 
57
- export type WorkspaceResourceKind = "skill" | "instruction" | "agent";
57
+ export type WorkspaceResourceKind =
58
+ | "skill"
59
+ | "instruction"
60
+ | "agent"
61
+ | "knowledge";
58
62
  export type WorkspaceResourceScope = "all" | "selected";
59
63
 
60
64
  export interface WorkspaceResourceInput {
@@ -66,6 +70,16 @@ export interface WorkspaceResourceInput {
66
70
  scope: WorkspaceResourceScope;
67
71
  }
68
72
 
73
+ export interface WorkspaceResourceOption {
74
+ id: string;
75
+ kind: WorkspaceResourceKind;
76
+ name: string;
77
+ description: string | null;
78
+ path: string;
79
+ scope: WorkspaceResourceScope;
80
+ updatedAt: number;
81
+ }
82
+
69
83
  export async function listWorkspaceResources(filter?: { kind?: string }) {
70
84
  const db = getDb();
71
85
  const conditions = [orgFilter(schema.workspaceResources)];
@@ -79,6 +93,21 @@ export async function listWorkspaceResources(filter?: { kind?: string }) {
79
93
  .orderBy(desc(schema.workspaceResources.updatedAt));
80
94
  }
81
95
 
96
+ export async function listWorkspaceResourceOptions(filter?: {
97
+ kind?: string;
98
+ }): Promise<WorkspaceResourceOption[]> {
99
+ const resources = await listWorkspaceResources(filter);
100
+ return resources.map((resource) => ({
101
+ id: resource.id,
102
+ kind: resource.kind as WorkspaceResourceKind,
103
+ name: resource.name,
104
+ description: resource.description,
105
+ path: resource.path,
106
+ scope: resource.scope as WorkspaceResourceScope,
107
+ updatedAt: resource.updatedAt,
108
+ }));
109
+ }
110
+
82
111
  export async function getWorkspaceResource(
83
112
  resourceId: string,
84
113
  ctx: WorkspaceResourceCtx = requireWorkspaceResourceCtx(),
@@ -248,6 +277,13 @@ export async function createResourceGrant(resourceId: string, appId: string) {
248
277
  const resource = await getWorkspaceResource(resourceId, ctx);
249
278
  if (!resource) throw new Error("Workspace resource not found");
250
279
 
280
+ const activeExisting = (await listResourceGrants({ resourceId, appId })).find(
281
+ (grant) => grant.status === "active",
282
+ );
283
+ if (activeExisting) {
284
+ return activeExisting;
285
+ }
286
+
251
287
  const timestamp = now();
252
288
  const grantId = id();
253
289
  const actor = currentOwnerEmail();
@@ -274,6 +310,42 @@ export async function createResourceGrant(resourceId: string, appId: string) {
274
310
  return getResourceGrant(grantId);
275
311
  }
276
312
 
313
+ export async function grantWorkspaceResourcesToApp(input: {
314
+ appId: string;
315
+ resourceIds: string[];
316
+ }) {
317
+ const uniqueResourceIds = [...new Set(input.resourceIds.filter(Boolean))];
318
+ if (uniqueResourceIds.length === 0) {
319
+ return { appId: input.appId, granted: [], skipped: [] };
320
+ }
321
+
322
+ const granted: Array<{ id: string; resourceId: string; appId: string }> = [];
323
+ const skipped: Array<{ resourceId: string; reason: string }> = [];
324
+
325
+ for (const resourceId of uniqueResourceIds) {
326
+ const resource = await getWorkspaceResource(resourceId).catch(() => null);
327
+ if (!resource) {
328
+ skipped.push({ resourceId, reason: "not-found" });
329
+ continue;
330
+ }
331
+ if (resource.scope === "all") {
332
+ skipped.push({ resourceId, reason: "already-all-apps" });
333
+ continue;
334
+ }
335
+
336
+ const grant = await createResourceGrant(resourceId, input.appId);
337
+ if (grant) {
338
+ granted.push({
339
+ id: grant.id,
340
+ resourceId: grant.resourceId,
341
+ appId: grant.appId,
342
+ });
343
+ }
344
+ }
345
+
346
+ return { appId: input.appId, granted, skipped };
347
+ }
348
+
277
349
  export async function revokeResourceGrant(
278
350
  grantId: string,
279
351
  ctx: WorkspaceResourceCtx = requireWorkspaceResourceCtx(),
@@ -433,12 +505,14 @@ export async function listWorkspaceResourcesOverview() {
433
505
  const skills = resources.filter((r) => r.kind === "skill");
434
506
  const instructions = resources.filter((r) => r.kind === "instruction");
435
507
  const agents = resources.filter((r) => r.kind === "agent");
508
+ const knowledge = resources.filter((r) => r.kind === "knowledge");
436
509
  const activeGrants = grants.filter((g) => g.status === "active");
437
510
 
438
511
  return {
439
512
  skillCount: skills.length,
440
513
  instructionCount: instructions.length,
441
514
  agentCount: agents.length,
515
+ knowledgeCount: knowledge.length,
442
516
  totalResources: resources.length,
443
517
  activeGrantCount: activeGrants.length,
444
518
  };