@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.
Files changed (55) 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/overview.d.ts.map +1 -1
  29. package/dist/routes/pages/overview.js +6 -6
  30. package/dist/routes/pages/overview.js.map +1 -1
  31. package/dist/routes/pages/workspace.d.ts.map +1 -1
  32. package/dist/routes/pages/workspace.js +17 -6
  33. package/dist/routes/pages/workspace.js.map +1 -1
  34. package/dist/server/lib/app-creation-store.d.ts +1 -0
  35. package/dist/server/lib/app-creation-store.d.ts.map +1 -1
  36. package/dist/server/lib/app-creation-store.js +33 -0
  37. package/dist/server/lib/app-creation-store.js.map +1 -1
  38. package/dist/server/lib/workspace-resources-store.d.ts +29 -1
  39. package/dist/server/lib/workspace-resources-store.d.ts.map +1 -1
  40. package/dist/server/lib/workspace-resources-store.js +46 -0
  41. package/dist/server/lib/workspace-resources-store.js.map +1 -1
  42. package/package.json +2 -3
  43. package/src/actions/create-workspace-resource.ts +4 -4
  44. package/src/actions/grant-workspace-resources-to-app.ts +16 -0
  45. package/src/actions/index.ts +4 -0
  46. package/src/actions/list-workspace-resource-options.ts +16 -0
  47. package/src/actions/list-workspace-resources.ts +2 -2
  48. package/src/actions/start-workspace-app-creation.ts +7 -0
  49. package/src/actions/view-screen.ts +4 -0
  50. package/src/components/create-app-popover.tsx +152 -21
  51. package/src/db/schema.ts +2 -2
  52. package/src/routes/pages/overview.tsx +21 -19
  53. package/src/routes/pages/workspace.tsx +31 -5
  54. package/src/server/lib/app-creation-store.ts +49 -0
  55. package/src/server/lib/workspace-resources-store.ts +75 -1
@@ -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
  };