@agent-native/dispatch 0.20.3 → 0.21.0
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/provider-api-register.d.ts +6 -6
- package/dist/actions/start-workspace-app-creation.js +1 -1
- package/dist/actions/start-workspace-app-creation.js.map +1 -1
- package/dist/components/automation-run-history-dialog.d.ts.map +1 -1
- package/dist/components/automation-run-history-dialog.js +1 -0
- package/dist/components/automation-run-history-dialog.js.map +1 -1
- package/dist/components/create-app-popover.d.ts.map +1 -1
- package/dist/components/create-app-popover.js +5 -71
- package/dist/components/create-app-popover.js.map +1 -1
- package/dist/components/layout/Layout.d.ts +23 -1
- package/dist/components/layout/Layout.d.ts.map +1 -1
- package/dist/components/layout/Layout.js +702 -15
- package/dist/components/layout/Layout.js.map +1 -1
- package/dist/lib/automation-display.d.ts +6 -0
- package/dist/lib/automation-display.d.ts.map +1 -1
- package/dist/lib/automation-display.js +8 -0
- package/dist/lib/automation-display.js.map +1 -1
- package/dist/lib/automations.d.ts +1 -0
- package/dist/lib/automations.d.ts.map +1 -1
- package/dist/lib/automations.js.map +1 -1
- package/dist/lib/workspace-apps.d.ts +6 -0
- package/dist/lib/workspace-apps.d.ts.map +1 -1
- package/dist/lib/workspace-apps.js +21 -0
- package/dist/lib/workspace-apps.js.map +1 -1
- package/dist/routes/pages/automations.d.ts.map +1 -1
- package/dist/routes/pages/automations.js +16 -6
- package/dist/routes/pages/automations.js.map +1 -1
- package/dist/routes/pages/settings.d.ts.map +1 -1
- package/dist/routes/pages/settings.js +21 -1
- package/dist/routes/pages/settings.js.map +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/lib/app-creation-store.d.ts.map +1 -1
- package/dist/server/lib/app-creation-store.js +13 -0
- package/dist/server/lib/app-creation-store.js.map +1 -1
- package/dist/server/plugins/agent-chat.js +1 -0
- package/dist/server/plugins/agent-chat.js.map +1 -1
- package/dist/server/plugins/db.d.ts +1 -0
- package/dist/server/plugins/db.d.ts.map +1 -1
- package/dist/server/plugins/db.js +1 -1
- package/dist/server/plugins/db.js.map +1 -1
- package/dist/server/plugins/integrations.js +1 -1
- package/dist/server/plugins/integrations.js.map +1 -1
- package/package.json +3 -3
- package/src/actions/start-workspace-app-creation.ts +1 -1
- package/src/components/automation-run-history-dialog.tsx +1 -0
- package/src/components/create-app-popover.spec.tsx +22 -0
- package/src/components/create-app-popover.tsx +30 -94
- package/src/components/layout/Layout.spec.tsx +16 -1
- package/src/components/layout/Layout.tsx +1356 -34
- package/src/lib/automation-display.spec.ts +7 -0
- package/src/lib/automation-display.ts +11 -0
- package/src/lib/automations.ts +1 -0
- package/src/lib/workspace-apps.ts +24 -0
- package/src/routes/pages/automations.tsx +45 -6
- package/src/routes/pages/settings.tsx +72 -0
- package/src/server/index.ts +4 -1
- package/src/server/lib/app-creation-store.spec.ts +13 -0
- package/src/server/lib/app-creation-store.ts +13 -0
- package/src/server/lib/mcp-gateway.spec.ts +18 -0
- package/src/server/plugins/agent-chat.ts +1 -0
- package/src/server/plugins/db.ts +1 -1
- package/src/server/plugins/integrations.ts +1 -1
- package/src/styles/dispatch.css +29 -0
|
@@ -62,6 +62,7 @@ Use the standard workspace primitives:
|
|
|
62
62
|
- Hosted/connected A2A neighbors such as Analytics and Content come from the available-apps context or list-connected-agents. list-workspace-apps only inventories apps mounted inside this workspace deployment; never use a missing row there to conclude that a connected agent is unavailable.
|
|
63
63
|
- When answering whether a mounted workspace app exposes an agent card or A2A endpoint, call list-workspace-apps with includeAgentCards=true. If you have not requested that probe, absence of agent-card fields means unchecked, not unavailable.
|
|
64
64
|
- When creating a new workspace app, create a separate app under apps/<app-id> with apps/<app-id>/package.json including a concise generated description, mount it at /<app-id>, use relative /<app-id> links, never hardcode localhost or dev ports, use shadcn/ui with @tabler/icons-react rather than lucide-react, and ensure the React Router client entry preserves APP_BASE_PATH/VITE_APP_BASE_PATH via appBasePath(). There is no separate workspace app registry to edit.
|
|
65
|
+
- When an explicit app-creation request already includes a source brief or a concrete repeatable workflow, call start-workspace-app-creation without asking non-blocking product or UX questions. Choose recommended defaults, let the Builder handoff record assumptions, and ask only for authorization, credentials, a destructive action, an ambiguous target workspace, or a genuinely missing workflow.
|
|
65
66
|
- If the chat template is used, treat it as scaffolding only: the finished app must be branded as the requested app with its own home screen/navigation/package metadata/manifest, and must not leave visible "Chat", "Starter", "Blank app", or "New app" UI behind.
|
|
66
67
|
- Treat first-party apps such as Mail, Calendar, Analytics, Brain, Assets, and Dispatch as existing hosted/connected neighbors available through links and A2A/default connected agents. Do not create wrapper apps, child apps, nested routes, or cloned template copies just to give a new app access to them; build only the genuinely new workflow and delegate cross-app work to those existing apps.
|
|
67
68
|
- Integration grants are not provider capability limits. For ad hoc provider inspection, querying, reporting, or troubleshooting, call provider-api-catalog/provider-api-docs, then provider-api-request against the provider's real HTTP API. Use connectionId for a specific shared grant and accountId for a specific OAuth account. Never expose secret values or silently widen app access while doing this.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-chat.js","sourceRoot":"","sources":["../../../src/server/plugins/agent-chat.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAElE,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEzD,MAAM,kBAAkB,GAAG;IACzB,aAAa;IACb,qBAAqB;IACrB,uBAAuB;IACvB,SAAS;IACT,UAAU;IACV,0BAA0B;IAC1B,2BAA2B;IAC3B,2BAA2B;IAC3B,oBAAoB;IACpB,sBAAsB;IACtB,qBAAqB;IACrB,mBAAmB;IACnB,oBAAoB;IACpB,aAAa;IACb,8BAA8B;IAC9B,oCAAoC;IACpC,sBAAsB;IACtB,mBAAmB;IACnB,sBAAsB;IACtB,sBAAsB;IACtB,UAAU;CACX,CAAC;AAEF,eAAe,qBAAqB,CAAC;IACnC,KAAK,EAAE,UAAU;IACjB,qBAAqB,EAAE,IAAI;IAC3B,gBAAgB,EAAE,kBAAkB;IACpC,GAAG,EAAE;QACH,gBAAgB,EAAE;YAChB,oCAAoC;YACpC,8BAA8B;SAC/B;KACF;IACD,0EAA0E;IAC1E,2EAA2E;IAC3E,2EAA2E;IAC3E,YAAY,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;QAC5B,MAAM,GAAG,GAAG,MAAM,aAAa,CAAC,KAAK,CAAC,CAAC;QACvC,OAAO,GAAG,CAAC,KAAK,CAAC;IACnB,CAAC;IACD,2EAA2E;IAC3E,2EAA2E;IAC3E,wEAAwE;IACxE,OAAO,EAAE,eAAe;IACxB,aAAa,EAAE,EAAE,UAAU,EAAE,WAAW,EAAE;IAC1C,YAAY,EAAE
|
|
1
|
+
{"version":3,"file":"agent-chat.js","sourceRoot":"","sources":["../../../src/server/plugins/agent-chat.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAElE,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEzD,MAAM,kBAAkB,GAAG;IACzB,aAAa;IACb,qBAAqB;IACrB,uBAAuB;IACvB,SAAS;IACT,UAAU;IACV,0BAA0B;IAC1B,2BAA2B;IAC3B,2BAA2B;IAC3B,oBAAoB;IACpB,sBAAsB;IACtB,qBAAqB;IACrB,mBAAmB;IACnB,oBAAoB;IACpB,aAAa;IACb,8BAA8B;IAC9B,oCAAoC;IACpC,sBAAsB;IACtB,mBAAmB;IACnB,sBAAsB;IACtB,sBAAsB;IACtB,UAAU;CACX,CAAC;AAEF,eAAe,qBAAqB,CAAC;IACnC,KAAK,EAAE,UAAU;IACjB,qBAAqB,EAAE,IAAI;IAC3B,gBAAgB,EAAE,kBAAkB;IACpC,GAAG,EAAE;QACH,gBAAgB,EAAE;YAChB,oCAAoC;YACpC,8BAA8B;SAC/B;KACF;IACD,0EAA0E;IAC1E,2EAA2E;IAC3E,2EAA2E;IAC3E,YAAY,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;QAC5B,MAAM,GAAG,GAAG,MAAM,aAAa,CAAC,KAAK,CAAC,CAAC;QACvC,OAAO,GAAG,CAAC,KAAK,CAAC;IACnB,CAAC;IACD,2EAA2E;IAC3E,2EAA2E;IAC3E,wEAAwE;IACxE,OAAO,EAAE,eAAe;IACxB,aAAa,EAAE,EAAE,UAAU,EAAE,WAAW,EAAE;IAC1C,YAAY,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;4EAyB4D;CAC3E,CAAC,CAAC","sourcesContent":["import { getOrgContext } from \"@agent-native/core/org\";\nimport { createAgentChatPlugin } from \"@agent-native/core/server\";\n\nimport { dispatchActions } from \"../../actions/index.js\";\n\nconst INITIAL_TOOL_NAMES = [\n \"view-screen\",\n \"list-workspace-apps\",\n \"list-connected-agents\",\n \"ask_app\",\n \"open_app\",\n \"list-workspace-resources\",\n \"create-workspace-resource\",\n \"update-workspace-resource\",\n \"list-vault-secrets\",\n \"request-vault-secret\",\n \"create-vault-secret\",\n \"list-destinations\",\n \"upsert-destination\",\n \"list-dreams\",\n \"start-workspace-app-creation\",\n \"list-available-workspace-templates\",\n \"provider-api-catalog\",\n \"provider-api-docs\",\n \"provider-api-request\",\n \"query-staged-dataset\",\n \"navigate\",\n];\n\nexport default createAgentChatPlugin({\n appId: \"dispatch\",\n durableBackgroundRuns: true,\n initialToolNames: INITIAL_TOOL_NAMES,\n mcp: {\n connectorCatalog: [\n \"resolve-integration-source-context\",\n \"start-workspace-app-creation\",\n ],\n },\n // Without this, AGENT_ORG_ID is never set on agent action calls and every\n // row written through the frontend (vault secrets, destinations, workspace\n // resources) lands with org_id=NULL — breaking data isolation across orgs.\n resolveOrgId: async (event) => {\n const ctx = await getOrgContext(event);\n return ctx.orgId;\n },\n // Read actions directly from the package's own action map rather than from\n // a build-time-generated `.generated/actions-registry.ts` (the latter is a\n // template-only construct that the Vite plugin emits next to actions/).\n actions: dispatchActions,\n codeExecution: { production: \"sandboxed\" },\n systemPrompt: `You are the central dispatch for this workspace.\n\nDefault posture:\n- Treat Slack and Telegram as shared entrypoints into the workspace.\n- Heavily delegate domain work to specialized agents through A2A when another app owns the job.\n- Keep durable memory and operating instructions in resources rather than ephemeral chat.\n- Prefer replying in the current external thread unless the user explicitly asks you to send to a saved destination.\n\nUse the standard workspace primitives:\n- Read and update resources like AGENTS.md, LEARNINGS.md, jobs/*.md, agents/*.md, and remote-agents/*.json when appropriate.\n- Use recurring jobs for scheduled behavior.\n- Use custom agent profiles in agents/*.md for local spawned work and remote-agents/*.json for remote A2A apps.\n- You receive a compact available-apps block with sibling workspace app names and descriptions. Use it to pick the right A2A target, and call list-connected-agents or tool-search only when you need fresh details.\n- Hosted/connected A2A neighbors such as Analytics and Content come from the available-apps context or list-connected-agents. list-workspace-apps only inventories apps mounted inside this workspace deployment; never use a missing row there to conclude that a connected agent is unavailable.\n- When answering whether a mounted workspace app exposes an agent card or A2A endpoint, call list-workspace-apps with includeAgentCards=true. If you have not requested that probe, absence of agent-card fields means unchecked, not unavailable.\n- When creating a new workspace app, create a separate app under apps/<app-id> with apps/<app-id>/package.json including a concise generated description, mount it at /<app-id>, use relative /<app-id> links, never hardcode localhost or dev ports, use shadcn/ui with @tabler/icons-react rather than lucide-react, and ensure the React Router client entry preserves APP_BASE_PATH/VITE_APP_BASE_PATH via appBasePath(). There is no separate workspace app registry to edit.\n- When an explicit app-creation request already includes a source brief or a concrete repeatable workflow, call start-workspace-app-creation without asking non-blocking product or UX questions. Choose recommended defaults, let the Builder handoff record assumptions, and ask only for authorization, credentials, a destructive action, an ambiguous target workspace, or a genuinely missing workflow.\n- If the chat template is used, treat it as scaffolding only: the finished app must be branded as the requested app with its own home screen/navigation/package metadata/manifest, and must not leave visible \"Chat\", \"Starter\", \"Blank app\", or \"New app\" UI behind.\n- Treat first-party apps such as Mail, Calendar, Analytics, Brain, Assets, and Dispatch as existing hosted/connected neighbors available through links and A2A/default connected agents. Do not create wrapper apps, child apps, nested routes, or cloned template copies just to give a new app access to them; build only the genuinely new workflow and delegate cross-app work to those existing apps.\n- Integration grants are not provider capability limits. For ad hoc provider inspection, querying, reporting, or troubleshooting, call provider-api-catalog/provider-api-docs, then provider-api-request against the provider's real HTTP API. Use connectionId for a specific shared grant and accountId for a specific OAuth account. Never expose secret values or silently widen app access while doing this.\n- For broad provider searches, joins, classification, corpus counts, or absence claims, fetch every relevant page or an explicitly bounded cohort, stage/save large responses with stageAs/saveToFile/fetchAllPages, and reduce them with query-staged-dataset or run-code. Report source, filters, row counts, pagination, truncation, failed pages, and uncovered gaps.\n\nWhen a user asks for something like a digest, reminder, routing rule, or saved behavior:\n- First decide whether it should be a resource, a recurring job, a destination, or a delegated task.\n- Keep responses concise and operational.\n- Avoid inventing integrations or destinations that are not configured yet.`,\n});\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../../src/server/plugins/db.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../../src/server/plugins/db.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,qBAAqB,yCAEhC,CAAC;;AAEH;;;;;;GAMG;0BACmB,QAAQ,EAAE,GAAG"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { runMigrations } from "@agent-native/core/db";
|
|
2
2
|
import { dispatchMigrations } from "../../db/migrations.js";
|
|
3
3
|
import { scheduleVaultBootResync } from "../lib/vault-boot-resync.js";
|
|
4
|
-
const runDispatchMigrations = runMigrations(dispatchMigrations, {
|
|
4
|
+
export const runDispatchMigrations = runMigrations(dispatchMigrations, {
|
|
5
5
|
table: "dispatch_migrations",
|
|
6
6
|
});
|
|
7
7
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db.js","sourceRoot":"","sources":["../../../src/server/plugins/db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AAEtE,MAAM,qBAAqB,GAAG,aAAa,CAAC,kBAAkB,EAAE;
|
|
1
|
+
{"version":3,"file":"db.js","sourceRoot":"","sources":["../../../src/server/plugins/db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AAEtE,MAAM,CAAC,MAAM,qBAAqB,GAAG,aAAa,CAAC,kBAAkB,EAAE;IACrE,KAAK,EAAE,qBAAqB;CAC7B,CAAC,CAAC;AAEH;;;;;;GAMG;AACH,eAAe,KAAK,EAAE,QAAa,EAAE,EAAE;IACrC,MAAM,qBAAqB,CAAC,QAAQ,CAAC,CAAC;IACtC,uBAAuB,EAAE,CAAC;AAC5B,CAAC,CAAC","sourcesContent":["import { runMigrations } from \"@agent-native/core/db\";\n\nimport { dispatchMigrations } from \"../../db/migrations.js\";\nimport { scheduleVaultBootResync } from \"../lib/vault-boot-resync.js\";\n\nexport const runDispatchMigrations = runMigrations(dispatchMigrations, {\n table: \"dispatch_migrations\",\n});\n\n/**\n * Run dispatch's own migrations first (this is what guarantees\n * `vault_secrets` exists), then kick off the vault boot resync. The resync\n * itself waits several more seconds before touching the DB — see\n * vault-boot-resync.ts — so this ordering is a belt-and-suspenders\n * guarantee, not a hard dependency.\n */\nexport default async (nitroApp: any) => {\n await runDispatchMigrations(nitroApp);\n scheduleVaultBootResync();\n};\n"]}
|
|
@@ -36,7 +36,7 @@ When a user asks for something:
|
|
|
36
36
|
- If the user asks to create, build, make, scaffold, or generate an "agent" from Dispatch chat or by tagging @agent-native in Slack, email, or Telegram, first classify the ask. If it is a simple Dispatch-native behavior like a reminder, digest, monitor, routing rule, saved instruction, or recurring workflow, create or update the recurring job/resource/destination in Dispatch. If it is a robust unique product or teammate that needs its own UI, data model, actions, integrations, or domain workflow, treat it as a new workspace app and call start-workspace-app-creation.
|
|
37
37
|
- If a new-app prompt asks for access to Mail, Calendar, Analytics, Brain, Assets, or similar first-party app data/agents, keep using the existing hosted/connected app and A2A path. Do not ask Builder to scaffold those apps as children of the new app unless the user explicitly asks for a customized app from that template.
|
|
38
38
|
- If the chat template is used, treat it as scaffolding only: the finished app must be branded as the requested app with its own home screen/navigation/package metadata/manifest, and must not leave visible "Chat", "Starter", "Blank app", or "New app" UI behind.
|
|
39
|
-
- If the user explicitly asks for a new app or workspace app, call start-workspace-app-creation with their prompt and include a concise generated description by default. Do not satisfy a new-app request by adding a route, page, component, or file inside apps/chat or another existing app unless the user explicitly asks to modify that existing app. If the request is too vague to classify, ask one concise follow-up. If the action returns mode "builder", reply with the Builder branch URL; Builder is responsible for creating the separate workspace app under apps/<app-id>, mounting it at /<app-id>, ensuring apps/<app-id>/package.json exists with name/displayName and description so Dispatch discovers it, using relative /<app-id> links instead of hardcoded localhost/dev ports, and preserving APP_BASE_PATH/VITE_APP_BASE_PATH
|
|
39
|
+
- If the user explicitly asks for a new app or workspace app, call start-workspace-app-creation with their prompt and include a concise generated description by default. Do not satisfy a new-app request by adding a route, page, component, or file inside apps/chat or another existing app unless the user explicitly asks to modify that existing app. When the prompt contains a source brief or concrete repeatable workflow, choose recommended defaults and do not ask non-blocking product or UX questions; ask only for authorization, credentials, a destructive action, an ambiguous target workspace, or a genuinely missing workflow. If the request is too vague to classify, ask one concise follow-up. If the action returns mode "builder", reply with the Builder branch URL; Builder is responsible for creating the separate workspace app under apps/<app-id>, mounting it at /<app-id>, ensuring apps/<app-id>/package.json exists with name/displayName and description so Dispatch discovers it, using relative /<app-id> links instead of hardcoded localhost/dev ports, and preserving APP_BASE_PATH/VITE_APP_BASE_PATH in the React Router client entry. The new app lives at the workspace root /<app-id>, NOT under /dispatch/<app-id>, /apps/<app-id>, or any other Dispatch tab — when telling the user where to find it, link to /<app-id> only. There is no separate workspace app registry to edit. If it returns mode "local-agent", tell the user it is ready for the local code agent and include the returned app path/prompt summary. Dispatch reuses the connected workspace Builder project or provisions it through the Builder Projects API when an organization owner/admin starts the first app; members need an owner/admin to configure the project first. Do not ask the user to use Builder org/beta settings. If it returns mode "builder-unavailable", the action also returns a \`reason\` code plus a user-facing \`message\` and (for some reasons) an operator-facing \`detail\`. Relay the \`message\` to the user as-is; only surface \`detail\` when the user is clearly an operator debugging the deployment (e.g. troubleshooting a broken Builder connection for the workspace), never as routine detail for an end user.
|
|
40
40
|
- For digests, reminders, or saved behavior, prefer recurring jobs, resources, or destinations over chat replies.
|
|
41
41
|
- Keep responses concise and operational — messaging platforms have character limits.
|
|
42
42
|
- Use markdown sparingly (bold and lists are fine, avoid complex formatting).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integrations.js","sourceRoot":"","sources":["../../../src/server/plugins/integrations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAErE,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EACL,qBAAqB,EACrB,+BAA+B,GAChC,MAAM,iCAAiC,CAAC;AAEzC,MAAM,0BAA0B,GAAG;IACjC,GAAG,eAAe;IAClB,2EAA2E;IAC3E,6EAA6E;IAC7E,6EAA6E;IAC7E,OAAO,EAAE;QACP,GAAG,eAAe,CAAC,OAAO;QAC1B,SAAS,EAAE,KAAK;KACjB;CACF,CAAC;AAEF,MAAM,kCAAkC,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;4FA4BiD,CAAC;AAE7F;;;;GAIG;AACH,MAAM,0BAA0B,GAAG,KAAK,EAAE,QAAa,EAAE,EAAE;IACzD,MAAM,EAAE,YAAY,GAAG,EAAE,EAAE,GAAG,iBAAiB,EAAE,CAAC;IAClD,MAAM,cAAc,GAAG,YAAY,CAAC,YAAY,CAAC;IACjD,MAAM,YAAY,GAChB,OAAO,cAAc,KAAK,QAAQ;QAChC,CAAC,CAAC,cAAc;QAChB,CAAC,CAAC,OAAO,cAAc,KAAK,UAAU;YACpC,CAAC,CAAC,cAAc,CAAC,kCAAkC,CAAC;YACpD,CAAC,CAAC,kCAAkC,CAAC;IAE3C,MAAM,MAAM,GAAG,wBAAwB,CAAC;QACtC,KAAK,EAAE,UAAU;QACjB,OAAO,EAAE,0BAA0B;QACnC,uBAAuB,EAAE,+BAA+B;QACxD,aAAa,EAAE,qBAAqB;QACpC,YAAY;QACZ,wDAAwD;QACxD,yEAAyE;QACzE,+DAA+D;QAC/D,6EAA6E;KAC9E,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC;AAC1B,CAAC,CAAC;AAEF,eAAe,0BAA0B,CAAC","sourcesContent":["import { createIntegrationsPlugin } from \"@agent-native/core/server\";\n\nimport { dispatchActions } from \"../../actions/index.js\";\nimport { getDispatchConfig } from \"../index.js\";\nimport {\n beforeDispatchProcess,\n resolveDispatchExecutionContext,\n} from \"../lib/dispatch-integrations.js\";\n\nconst dispatchIntegrationActions = {\n ...dispatchActions,\n // Messaging integrations should use the core call-agent tool for cross-app\n // delegation because it queues A2A continuations when serverless budgets are\n // tight. The MCP-facing ask_app action is still available outside this path.\n ask_app: {\n ...dispatchActions.ask_app,\n agentTool: false,\n },\n};\n\nconst DISPATCH_INTEGRATION_SYSTEM_PROMPT = `You are the central dispatch for this workspace, responding via a messaging platform integration (Slack, Telegram, email, etc.).\n\nDefault posture:\n- Treat Slack, Telegram, and email as shared entrypoints into the workspace.\n- Heavily delegate domain work to specialized agents through A2A (call-agent) when another app owns the job. Apps you can delegate to include slides (decks/presentations), analytics (data/dashboards), content (docs/articles), forms (form builder), clips (screen recordings), design (visual designs), and assets (brand libraries plus generated images/videos).\n- Use the available-apps prompt context first, then list-connected-agents when you need fresh details, to see what agents are available before assuming a request must be handled locally.\n- Hosted/connected A2A neighbors such as Analytics and Content come from the available-apps context or list-connected-agents. list-workspace-apps only inventories apps mounted inside this workspace deployment; never use a missing row there to conclude that a connected agent is unavailable.\n- When asked whether a mounted workspace app exposes an agent card or A2A endpoint, call list-workspace-apps with includeAgentCards=true. Without that probe, missing agent-card fields mean unchecked, not unavailable.\n- Treat first-party apps such as Mail, Calendar, Analytics, Brain, Assets, and Dispatch as existing hosted/connected neighbors available through links and A2A/default connected agents. Do not create wrapper apps, child apps, nested routes, or cloned template copies just to give a new app access to them; build only the genuinely new workflow and delegate cross-app work to those existing apps.\n- Integration grants are not provider capability limits. For ad hoc provider inspection, querying, reporting, or troubleshooting, call provider-api-catalog/provider-api-docs, then provider-api-request against the provider's real HTTP API. Use connectionId for a specific shared grant and accountId for a specific OAuth account. Never expose secret values or silently widen app access while doing this.\n- Keep durable memory and operating instructions in resources rather than ephemeral chat.\n- Reply in the originating thread unless the user explicitly asks you to send to a saved destination.\n\nWhen a user asks for something:\n- If it belongs to analytics, content, slides, clips, assets, etc., delegate via call-agent — do not re-implement the domain logic in dispatch.\n- Synthetic uptime, health-check, and URL-availability monitors belong to Analytics, even when the monitored target is another app such as Clips. Delegate creation to Analytics and relay its exact monitor URL. Dispatch-native recurring jobs are for reminders, digests, and agent workflows, not HTTP uptime probes.\n- Route by the requested artifact type, not by organization-specific names stored in code. For structured records, databases, tables, queues, boards, and intake forms, resolve the owning app and canonical destination from loaded workspace instructions/resources plus discovered app capabilities; do not assume Content, a database ID, schema, owner, or required fields. Visual designs, mockups, wireframes, screens, and interfaces belong to Design. A trusted Required target agent hint in integration context is authoritative.\n- When delegating structured intake to the resolved owning app, preserve the exact Source thread URL and the workspace instruction context, inspect the destination's current required fields, ask only for missing values, submit once, verify the saved record, and return the exact link.\n- In messaging integrations, use call-agent for cross-app delegation; do not use ask_app.\n- After call-agent returns an answer, RELAY IT DIRECTLY to the user with at most a one-line preface — do not rephrase, summarize, or add commentary. The downstream agent already crafted the answer; your job is delivery, not editing. This minimizes round-trips and keeps the user-visible reply fast.\n- Exception: if the downstream agent reports a missing model/provider credential, do not name exact env vars, Vault keys, tokens, or secrets. Say the target app needs an LLM connection and recommend connecting Builder/managed LLM for that app; keep bring-your-own provider keys as a secondary option only if the user asks.\n- If the user asks to create, build, make, scaffold, or generate an \"agent\" from Dispatch chat or by tagging @agent-native in Slack, email, or Telegram, first classify the ask. If it is a simple Dispatch-native behavior like a reminder, digest, monitor, routing rule, saved instruction, or recurring workflow, create or update the recurring job/resource/destination in Dispatch. If it is a robust unique product or teammate that needs its own UI, data model, actions, integrations, or domain workflow, treat it as a new workspace app and call start-workspace-app-creation.\n- If a new-app prompt asks for access to Mail, Calendar, Analytics, Brain, Assets, or similar first-party app data/agents, keep using the existing hosted/connected app and A2A path. Do not ask Builder to scaffold those apps as children of the new app unless the user explicitly asks for a customized app from that template.\n- If the chat template is used, treat it as scaffolding only: the finished app must be branded as the requested app with its own home screen/navigation/package metadata/manifest, and must not leave visible \"Chat\", \"Starter\", \"Blank app\", or \"New app\" UI behind.\n- If the user explicitly asks for a new app or workspace app, call start-workspace-app-creation with their prompt and include a concise generated description by default. Do not satisfy a new-app request by adding a route, page, component, or file inside apps/chat or another existing app unless the user explicitly asks to modify that existing app. If the request is too vague to classify, ask one concise follow-up. If the action returns mode \"builder\", reply with the Builder branch URL; Builder is responsible for creating the separate workspace app under apps/<app-id>, mounting it at /<app-id>, ensuring apps/<app-id>/package.json exists with name/displayName and description so Dispatch discovers it, using relative /<app-id> links instead of hardcoded localhost/dev ports, and preserving APP_BASE_PATH/VITE_APP_BASE_PATH via appBasePath() in the React Router client entry. The new app lives at the workspace root /<app-id>, NOT under /dispatch/<app-id>, /apps/<app-id>, or any other Dispatch tab — when telling the user where to find it, link to /<app-id> only. There is no separate workspace app registry to edit. If it returns mode \"local-agent\", tell the user it is ready for the local code agent and include the returned app path/prompt summary. Dispatch reuses the connected workspace Builder project or provisions it through the Builder Projects API when an organization owner/admin starts the first app; members need an owner/admin to configure the project first. Do not ask the user to use Builder org/beta settings. If it returns mode \"builder-unavailable\", the action also returns a \\`reason\\` code plus a user-facing \\`message\\` and (for some reasons) an operator-facing \\`detail\\`. Relay the \\`message\\` to the user as-is; only surface \\`detail\\` when the user is clearly an operator debugging the deployment (e.g. troubleshooting a broken Builder connection for the workspace), never as routine detail for an end user.\n- For digests, reminders, or saved behavior, prefer recurring jobs, resources, or destinations over chat replies.\n- Keep responses concise and operational — messaging platforms have character limits.\n- Use markdown sparingly (bold and lists are fine, avoid complex formatting).\n- If a task requires many steps, summarize what you did rather than streaming every detail.`;\n\n/**\n * Defer plugin construction until the Nitro plugin actually fires so the\n * config-aware system prompt resolves AFTER `setupDispatch(config)` has\n * stamped the active config (plugin module load order is not guaranteed).\n */\nconst dispatchIntegrationsPlugin = async (nitroApp: any) => {\n const { integrations = {} } = getDispatchConfig();\n const promptOverride = integrations.systemPrompt;\n const systemPrompt =\n typeof promptOverride === \"string\"\n ? promptOverride\n : typeof promptOverride === \"function\"\n ? promptOverride(DISPATCH_INTEGRATION_SYSTEM_PROMPT)\n : DISPATCH_INTEGRATION_SYSTEM_PROMPT;\n\n const plugin = createIntegrationsPlugin({\n appId: \"dispatch\",\n actions: dispatchIntegrationActions,\n resolveExecutionContext: resolveDispatchExecutionContext,\n beforeProcess: beforeDispatchProcess,\n systemPrompt,\n // Inherit the framework default (claude-sonnet-4-6 from\n // packages/core/src/integrations/plugin.ts). Haiku was tried for latency\n // but hallucinated URLs/IDs after delegated call-agent results\n // (e.g. inventing `https://slides.workspace.com/deck/builder-io-deck-2024`).\n });\n\n return plugin(nitroApp);\n};\n\nexport default dispatchIntegrationsPlugin;\n"]}
|
|
1
|
+
{"version":3,"file":"integrations.js","sourceRoot":"","sources":["../../../src/server/plugins/integrations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAErE,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EACL,qBAAqB,EACrB,+BAA+B,GAChC,MAAM,iCAAiC,CAAC;AAEzC,MAAM,0BAA0B,GAAG;IACjC,GAAG,eAAe;IAClB,2EAA2E;IAC3E,6EAA6E;IAC7E,6EAA6E;IAC7E,OAAO,EAAE;QACP,GAAG,eAAe,CAAC,OAAO;QAC1B,SAAS,EAAE,KAAK;KACjB;CACF,CAAC;AAEF,MAAM,kCAAkC,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;4FA4BiD,CAAC;AAE7F;;;;GAIG;AACH,MAAM,0BAA0B,GAAG,KAAK,EAAE,QAAa,EAAE,EAAE;IACzD,MAAM,EAAE,YAAY,GAAG,EAAE,EAAE,GAAG,iBAAiB,EAAE,CAAC;IAClD,MAAM,cAAc,GAAG,YAAY,CAAC,YAAY,CAAC;IACjD,MAAM,YAAY,GAChB,OAAO,cAAc,KAAK,QAAQ;QAChC,CAAC,CAAC,cAAc;QAChB,CAAC,CAAC,OAAO,cAAc,KAAK,UAAU;YACpC,CAAC,CAAC,cAAc,CAAC,kCAAkC,CAAC;YACpD,CAAC,CAAC,kCAAkC,CAAC;IAE3C,MAAM,MAAM,GAAG,wBAAwB,CAAC;QACtC,KAAK,EAAE,UAAU;QACjB,OAAO,EAAE,0BAA0B;QACnC,uBAAuB,EAAE,+BAA+B;QACxD,aAAa,EAAE,qBAAqB;QACpC,YAAY;QACZ,wDAAwD;QACxD,yEAAyE;QACzE,+DAA+D;QAC/D,6EAA6E;KAC9E,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC;AAC1B,CAAC,CAAC;AAEF,eAAe,0BAA0B,CAAC","sourcesContent":["import { createIntegrationsPlugin } from \"@agent-native/core/server\";\n\nimport { dispatchActions } from \"../../actions/index.js\";\nimport { getDispatchConfig } from \"../index.js\";\nimport {\n beforeDispatchProcess,\n resolveDispatchExecutionContext,\n} from \"../lib/dispatch-integrations.js\";\n\nconst dispatchIntegrationActions = {\n ...dispatchActions,\n // Messaging integrations should use the core call-agent tool for cross-app\n // delegation because it queues A2A continuations when serverless budgets are\n // tight. The MCP-facing ask_app action is still available outside this path.\n ask_app: {\n ...dispatchActions.ask_app,\n agentTool: false,\n },\n};\n\nconst DISPATCH_INTEGRATION_SYSTEM_PROMPT = `You are the central dispatch for this workspace, responding via a messaging platform integration (Slack, Telegram, email, etc.).\n\nDefault posture:\n- Treat Slack, Telegram, and email as shared entrypoints into the workspace.\n- Heavily delegate domain work to specialized agents through A2A (call-agent) when another app owns the job. Apps you can delegate to include slides (decks/presentations), analytics (data/dashboards), content (docs/articles), forms (form builder), clips (screen recordings), design (visual designs), and assets (brand libraries plus generated images/videos).\n- Use the available-apps prompt context first, then list-connected-agents when you need fresh details, to see what agents are available before assuming a request must be handled locally.\n- Hosted/connected A2A neighbors such as Analytics and Content come from the available-apps context or list-connected-agents. list-workspace-apps only inventories apps mounted inside this workspace deployment; never use a missing row there to conclude that a connected agent is unavailable.\n- When asked whether a mounted workspace app exposes an agent card or A2A endpoint, call list-workspace-apps with includeAgentCards=true. Without that probe, missing agent-card fields mean unchecked, not unavailable.\n- Treat first-party apps such as Mail, Calendar, Analytics, Brain, Assets, and Dispatch as existing hosted/connected neighbors available through links and A2A/default connected agents. Do not create wrapper apps, child apps, nested routes, or cloned template copies just to give a new app access to them; build only the genuinely new workflow and delegate cross-app work to those existing apps.\n- Integration grants are not provider capability limits. For ad hoc provider inspection, querying, reporting, or troubleshooting, call provider-api-catalog/provider-api-docs, then provider-api-request against the provider's real HTTP API. Use connectionId for a specific shared grant and accountId for a specific OAuth account. Never expose secret values or silently widen app access while doing this.\n- Keep durable memory and operating instructions in resources rather than ephemeral chat.\n- Reply in the originating thread unless the user explicitly asks you to send to a saved destination.\n\nWhen a user asks for something:\n- If it belongs to analytics, content, slides, clips, assets, etc., delegate via call-agent — do not re-implement the domain logic in dispatch.\n- Synthetic uptime, health-check, and URL-availability monitors belong to Analytics, even when the monitored target is another app such as Clips. Delegate creation to Analytics and relay its exact monitor URL. Dispatch-native recurring jobs are for reminders, digests, and agent workflows, not HTTP uptime probes.\n- Route by the requested artifact type, not by organization-specific names stored in code. For structured records, databases, tables, queues, boards, and intake forms, resolve the owning app and canonical destination from loaded workspace instructions/resources plus discovered app capabilities; do not assume Content, a database ID, schema, owner, or required fields. Visual designs, mockups, wireframes, screens, and interfaces belong to Design. A trusted Required target agent hint in integration context is authoritative.\n- When delegating structured intake to the resolved owning app, preserve the exact Source thread URL and the workspace instruction context, inspect the destination's current required fields, ask only for missing values, submit once, verify the saved record, and return the exact link.\n- In messaging integrations, use call-agent for cross-app delegation; do not use ask_app.\n- After call-agent returns an answer, RELAY IT DIRECTLY to the user with at most a one-line preface — do not rephrase, summarize, or add commentary. The downstream agent already crafted the answer; your job is delivery, not editing. This minimizes round-trips and keeps the user-visible reply fast.\n- Exception: if the downstream agent reports a missing model/provider credential, do not name exact env vars, Vault keys, tokens, or secrets. Say the target app needs an LLM connection and recommend connecting Builder/managed LLM for that app; keep bring-your-own provider keys as a secondary option only if the user asks.\n- If the user asks to create, build, make, scaffold, or generate an \"agent\" from Dispatch chat or by tagging @agent-native in Slack, email, or Telegram, first classify the ask. If it is a simple Dispatch-native behavior like a reminder, digest, monitor, routing rule, saved instruction, or recurring workflow, create or update the recurring job/resource/destination in Dispatch. If it is a robust unique product or teammate that needs its own UI, data model, actions, integrations, or domain workflow, treat it as a new workspace app and call start-workspace-app-creation.\n- If a new-app prompt asks for access to Mail, Calendar, Analytics, Brain, Assets, or similar first-party app data/agents, keep using the existing hosted/connected app and A2A path. Do not ask Builder to scaffold those apps as children of the new app unless the user explicitly asks for a customized app from that template.\n- If the chat template is used, treat it as scaffolding only: the finished app must be branded as the requested app with its own home screen/navigation/package metadata/manifest, and must not leave visible \"Chat\", \"Starter\", \"Blank app\", or \"New app\" UI behind.\n- If the user explicitly asks for a new app or workspace app, call start-workspace-app-creation with their prompt and include a concise generated description by default. Do not satisfy a new-app request by adding a route, page, component, or file inside apps/chat or another existing app unless the user explicitly asks to modify that existing app. When the prompt contains a source brief or concrete repeatable workflow, choose recommended defaults and do not ask non-blocking product or UX questions; ask only for authorization, credentials, a destructive action, an ambiguous target workspace, or a genuinely missing workflow. If the request is too vague to classify, ask one concise follow-up. If the action returns mode \"builder\", reply with the Builder branch URL; Builder is responsible for creating the separate workspace app under apps/<app-id>, mounting it at /<app-id>, ensuring apps/<app-id>/package.json exists with name/displayName and description so Dispatch discovers it, using relative /<app-id> links instead of hardcoded localhost/dev ports, and preserving APP_BASE_PATH/VITE_APP_BASE_PATH in the React Router client entry. The new app lives at the workspace root /<app-id>, NOT under /dispatch/<app-id>, /apps/<app-id>, or any other Dispatch tab — when telling the user where to find it, link to /<app-id> only. There is no separate workspace app registry to edit. If it returns mode \"local-agent\", tell the user it is ready for the local code agent and include the returned app path/prompt summary. Dispatch reuses the connected workspace Builder project or provisions it through the Builder Projects API when an organization owner/admin starts the first app; members need an owner/admin to configure the project first. Do not ask the user to use Builder org/beta settings. If it returns mode \"builder-unavailable\", the action also returns a \\`reason\\` code plus a user-facing \\`message\\` and (for some reasons) an operator-facing \\`detail\\`. Relay the \\`message\\` to the user as-is; only surface \\`detail\\` when the user is clearly an operator debugging the deployment (e.g. troubleshooting a broken Builder connection for the workspace), never as routine detail for an end user.\n- For digests, reminders, or saved behavior, prefer recurring jobs, resources, or destinations over chat replies.\n- Keep responses concise and operational — messaging platforms have character limits.\n- Use markdown sparingly (bold and lists are fine, avoid complex formatting).\n- If a task requires many steps, summarize what you did rather than streaming every detail.`;\n\n/**\n * Defer plugin construction until the Nitro plugin actually fires so the\n * config-aware system prompt resolves AFTER `setupDispatch(config)` has\n * stamped the active config (plugin module load order is not guaranteed).\n */\nconst dispatchIntegrationsPlugin = async (nitroApp: any) => {\n const { integrations = {} } = getDispatchConfig();\n const promptOverride = integrations.systemPrompt;\n const systemPrompt =\n typeof promptOverride === \"string\"\n ? promptOverride\n : typeof promptOverride === \"function\"\n ? promptOverride(DISPATCH_INTEGRATION_SYSTEM_PROMPT)\n : DISPATCH_INTEGRATION_SYSTEM_PROMPT;\n\n const plugin = createIntegrationsPlugin({\n appId: \"dispatch\",\n actions: dispatchIntegrationActions,\n resolveExecutionContext: resolveDispatchExecutionContext,\n beforeProcess: beforeDispatchProcess,\n systemPrompt,\n // Inherit the framework default (claude-sonnet-4-6 from\n // packages/core/src/integrations/plugin.ts). Haiku was tried for latency\n // but hallucinated URLs/IDs after delegated call-agent results\n // (e.g. inventing `https://slides.workspace.com/deck/builder-io-deck-2024`).\n });\n\n return plugin(nitroApp);\n};\n\nexport default dispatchIntegrationsPlugin;\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/dispatch",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
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": {
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"tailwind-merge": "^3.5.0",
|
|
85
85
|
"vaul": "^1.1.2",
|
|
86
86
|
"zod": "^4.3.6",
|
|
87
|
-
"@agent-native/toolkit": "0.13.
|
|
87
|
+
"@agent-native/toolkit": "0.13.6"
|
|
88
88
|
},
|
|
89
89
|
"devDependencies": {
|
|
90
90
|
"@react-router/dev": "^8.1.0",
|
|
@@ -98,7 +98,7 @@
|
|
|
98
98
|
"typescript-7": "npm:typescript@^7.0.2",
|
|
99
99
|
"vite": "8.1.0",
|
|
100
100
|
"vitest": "^4.1.5",
|
|
101
|
-
"@agent-native/core": "0.
|
|
101
|
+
"@agent-native/core": "0.147.0"
|
|
102
102
|
},
|
|
103
103
|
"peerDependencies": {
|
|
104
104
|
"@agent-native/core": ">=0.8.0",
|
|
@@ -6,7 +6,7 @@ import { startWorkspaceAppCreation } from "../server/lib/app-creation-store.js";
|
|
|
6
6
|
|
|
7
7
|
export default defineAction({
|
|
8
8
|
description:
|
|
9
|
-
'Start creating a new workspace app from Dispatch when the request truly needs its own app. For Claude Web, ChatGPT Web, or either host\'s web Project, this action is the Builder handoff: call it after forming a bounded source brief and do not scaffold or edit files in the host sandbox. Callers should include a concise generated description by default; Dispatch generates one from the prompt when omitted. In local dev this returns a code-agent prompt; in production it reuses the connected Agent-Native workspace project or, for an organization owner/admin, provisions it through the Builder Projects API before starting the Builder Cloud Agent branch. If no project is configured, organization members must ask an owner/admin to configure it first. The result must be a separate workspace app under apps/<app-id>, not a new route or file in apps/chat. If chat is used as the source template, the finished app must be branded as the requested app and must not leave visible "Chat", "Starter", "Blank app", or "New app" UI behind. If the request needs Mail, Calendar, Analytics, Brain, Assets, or another first-party app, use the existing hosted/connected app via links or A2A; do not wrap or nest those apps inside the new app unless the user explicitly asks for a customized app from that template.',
|
|
9
|
+
'Start creating a new workspace app from Dispatch when the request truly needs its own app. For Claude Web, ChatGPT Web, or either host\'s web Project, this action is the Builder handoff: call it after forming a bounded source brief and do not scaffold or edit files in the host sandbox. This handoff is autonomous once the brief exists: do not ask the user for non-blocking product or UX choices; choose recommended defaults and let Builder record assumptions while it implements. Callers should include a concise generated description by default; Dispatch generates one from the prompt when omitted. In local dev this returns a code-agent prompt; in production it reuses the connected Agent-Native workspace project or, for an organization owner/admin, provisions it through the Builder Projects API before starting the Builder Cloud Agent branch. If no project is configured, organization members must ask an owner/admin to configure it first. The result must be a separate workspace app under apps/<app-id>, not a new route or file in apps/chat. If chat is used as the source template, the finished app must be branded as the requested app and must not leave visible "Chat", "Starter", "Blank app", or "New app" UI behind. If the request needs Mail, Calendar, Analytics, Brain, Assets, or another first-party app, use the existing hosted/connected app via links or A2A; do not wrap or nest those apps inside the new app unless the user explicitly asks for a customized app from that template.',
|
|
10
10
|
schema: z.object({
|
|
11
11
|
prompt: z.string().min(1).describe("The user's app creation request"),
|
|
12
12
|
appId: z
|
|
@@ -189,6 +189,28 @@ describe("CreateAppFlow", () => {
|
|
|
189
189
|
connectButton.click();
|
|
190
190
|
});
|
|
191
191
|
expect(builderConnectFlowState.start).toHaveBeenCalledTimes(1);
|
|
192
|
+
const localLink = container.querySelector<HTMLAnchorElement>(
|
|
193
|
+
"[data-create-app-local-link]",
|
|
194
|
+
);
|
|
195
|
+
expect(localLink?.textContent).toContain("Create locally");
|
|
196
|
+
expect(localLink?.href).toBe(
|
|
197
|
+
"https://agent-native.com/docs/multi-app-workspace#adding-a-new-app",
|
|
198
|
+
);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it("sends the shared scaffold prompt to Builder when already in Builder", async () => {
|
|
202
|
+
frameState.inBuilderFrame = true;
|
|
203
|
+
await renderAndSubmit("Build a quality dashboard");
|
|
204
|
+
|
|
205
|
+
expect(sendToAgentChatMock).toHaveBeenCalledTimes(1);
|
|
206
|
+
expect(sendToAgentChatMock).toHaveBeenCalledWith(
|
|
207
|
+
expect.objectContaining({ submit: true, type: "code" }),
|
|
208
|
+
);
|
|
209
|
+
expect(
|
|
210
|
+
fetchSpy.mock.calls.some(([input]) =>
|
|
211
|
+
String(input).includes("start-workspace-app-creation"),
|
|
212
|
+
),
|
|
213
|
+
).toBe(false);
|
|
192
214
|
});
|
|
193
215
|
|
|
194
216
|
it("renders the error affordance and a Try again control for builder-error, without a Connect Builder control", async () => {
|
|
@@ -7,7 +7,11 @@ import {
|
|
|
7
7
|
import { PromptComposer } from "@agent-native/core/client/composer";
|
|
8
8
|
import { isInBuilderFrame } from "@agent-native/core/client/host";
|
|
9
9
|
import { useBuilderConnectFlow } from "@agent-native/core/client/settings/useBuilderStatus";
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
buildChatFirstAppCreationPrompt,
|
|
12
|
+
getWorkspaceAppIdValidationError,
|
|
13
|
+
titleFromChatFirstAppPrompt,
|
|
14
|
+
} from "@agent-native/core/shared";
|
|
11
15
|
import {
|
|
12
16
|
IconAlertTriangle,
|
|
13
17
|
IconArrowLeft,
|
|
@@ -57,87 +61,6 @@ interface CreateAppPopoverProps {
|
|
|
57
61
|
align?: "start" | "center" | "end";
|
|
58
62
|
}
|
|
59
63
|
|
|
60
|
-
function slugify(value: string): string {
|
|
61
|
-
return value
|
|
62
|
-
.toLowerCase()
|
|
63
|
-
.replace(/[^a-z0-9]+/g, "-")
|
|
64
|
-
.replace(/^-+|-+$/g, "")
|
|
65
|
-
.replace(/^[^a-z]+/, "")
|
|
66
|
-
.slice(0, 48);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function titleFromPrompt(prompt: string): string {
|
|
70
|
-
const cleaned = prompt
|
|
71
|
-
.replace(/\b(build|create|make|an?|the|app|tool|dashboard)\b/gi, " ")
|
|
72
|
-
.replace(/\s+/g, " ")
|
|
73
|
-
.trim();
|
|
74
|
-
return slugify(cleaned || "new-app") || "new-app";
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function buildAppCreationPrompt(input: {
|
|
78
|
-
appId: string;
|
|
79
|
-
prompt: string;
|
|
80
|
-
selectedKeys: string[];
|
|
81
|
-
selectedResources: WorkspaceResourceOption[];
|
|
82
|
-
vaultAccessMode: VaultAccessMode;
|
|
83
|
-
}): string {
|
|
84
|
-
const keyList = input.selectedKeys.join(", ");
|
|
85
|
-
const grantRequest =
|
|
86
|
-
input.vaultAccessMode === "all-apps"
|
|
87
|
-
? `Dispatch vault access: all saved vault keys are available to every workspace app by default. No per-app vault grants are needed.`
|
|
88
|
-
: keyList
|
|
89
|
-
? `Requested Dispatch vault key grants for this app: ${keyList}`
|
|
90
|
-
: `Requested Dispatch vault key grants for this app: none`;
|
|
91
|
-
const resourceList = input.selectedResources.length
|
|
92
|
-
? input.selectedResources
|
|
93
|
-
.map(
|
|
94
|
-
(resource) =>
|
|
95
|
-
`- ${resource.name} (${resource.kind}, ${resource.path})`,
|
|
96
|
-
)
|
|
97
|
-
.join("\n")
|
|
98
|
-
: "none";
|
|
99
|
-
|
|
100
|
-
return [
|
|
101
|
-
`Create a new agent-native app in this workspace.`,
|
|
102
|
-
`This is a new workspace app request, not a feature request for the current app.`,
|
|
103
|
-
``,
|
|
104
|
-
`Suggested app name: ${input.appId} (you may adjust the slug if it conflicts)`,
|
|
105
|
-
`User prompt: ${input.prompt.trim()}`,
|
|
106
|
-
`Generate a concise one-sentence app description from the user prompt before coding; save it in apps/${input.appId}/package.json "description" so Dispatch and A2A can describe the app.`,
|
|
107
|
-
`If the user mentions a product or company such as Granola, Loom, Superhuman, Linear, or Notion, treat it as product inspiration unless they explicitly ask to connect to that service. Do not invent or require third-party API keys like GRANOLA_API_KEY just because a product is named.`,
|
|
108
|
-
grantRequest,
|
|
109
|
-
`Requested Dispatch workspace resources for this app:\n${resourceList}`,
|
|
110
|
-
`Dispatch workspace resources with scope=all are inherited workspace context. Do not copy or sync them into the new app; every workspace app reads them at runtime and may override with app shared or personal resources.`,
|
|
111
|
-
``,
|
|
112
|
-
`Pick a UI template that fits the user's prompt — analytics, assets, brain, calendar, chat, content, design, dispatch, forms, mail, slides, or clips when none of the others fit.`,
|
|
113
|
-
`If you use the chat template, treat it as scaffolding only: the finished app must use the requested app's real name, home screen, navigation, package metadata, and manifest, and it must not leave visible "Chat", "Starter", "Blank app", or "New app" UI behind.`,
|
|
114
|
-
`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.`,
|
|
115
|
-
`Important routing rule: from outside the app, link to /${input.appId}; inside apps/${input.appId}, React Router routes are app-local. Use <Link to="/review"> and navigate("/review"), not "/${input.appId}/review"; APP_BASE_PATH supplies the mounted prefix, and hardcoding it causes doubled URLs like /${input.appId}/${input.appId}/review.`,
|
|
116
|
-
`Prefer useActionQuery/useActionMutation for actions. If you must raw-fetch framework endpoints, wrap them with agentNativePath("/_agent-native/actions/<name>") so mounted apps call the right URL.`,
|
|
117
|
-
`Use relative workspace links like /${input.appId}. Do not hardcode localhost, 127.0.0.1, 8080, 8100, or any dev port; the active workspace gateway/browser origin owns the port.`,
|
|
118
|
-
`Use the framework/template UI stack: shadcn/ui components and @tabler/icons-react. Do not add lucide-react or another icon library for standard UI.`,
|
|
119
|
-
`Existing first-party apps are neighbors, not implementation details for this app. If the user's prompt mentions Mail, Calendar, Analytics, Brain, Assets, Dispatch, or other templates, treat them as existing hosted/connected apps that this app can link to or call through A2A/default connected agents. For example, Mail, Calendar, Analytics, Brain, and Assets already exist at https://mail.agent-native.com, https://calendar.agent-native.com, https://analytics.agent-native.com, https://brain.agent-native.com, and https://assets.agent-native.com.`,
|
|
120
|
-
`Do not create wrapper apps or scaffold child apps/routes for Mail, Calendar, Analytics, Brain, Assets, etc. inside apps/${input.appId} just so this app can access them. If the request is a cross-app dashboard or overview, build only the new dashboard/overview app and delegate to the existing apps for domain work.`,
|
|
121
|
-
`Only create another first-party app when the user explicitly asks for a customized app from that template; otherwise keep using the hosted/shared app so improvements to the base app keep flowing to users.`,
|
|
122
|
-
`Do not satisfy this by adding a route, page, component, or file inside apps/chat or another existing app unless the user explicitly asks to modify that existing app.`,
|
|
123
|
-
input.vaultAccessMode === "all-apps"
|
|
124
|
-
? `Do not create per-app Dispatch vault grants unless the workspace switches vault access to manual or the user explicitly asks for manual grants.`
|
|
125
|
-
: keyList
|
|
126
|
-
? `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.`
|
|
127
|
-
: `Do not grant any Dispatch vault keys unless the user asks later.`,
|
|
128
|
-
input.selectedResources.length
|
|
129
|
-
? `After the app exists, grant the selected Dispatch workspace resources to appId "${input.appId}". Do not sync All-app workspace resources; they are inherited.`
|
|
130
|
-
: `Do not grant any selected-only Dispatch workspace resources unless the user asks later.`,
|
|
131
|
-
``,
|
|
132
|
-
`App readiness requirements before handing off:`,
|
|
133
|
-
`- Ensure apps/${input.appId}/package.json exists with displayName/name and a concise description; Dispatch discovers workspace apps from apps/<app-id>/package.json, not a separate app registry.`,
|
|
134
|
-
`- Update the app manifest/package/deploy metadata needed by the existing workspace deployment model.`,
|
|
135
|
-
`- Ensure the React Router client entry preserves APP_BASE_PATH/VITE_APP_BASE_PATH via appBasePath() so /${input.appId} hydrates correctly.`,
|
|
136
|
-
`- Verify the app's agent card/A2A metadata is ready so Dispatch can discover and delegate to the app after deployment. Every sibling workspace app is available over A2A by default through call-agent, with names and descriptions from the workspace app registry.`,
|
|
137
|
-
`When it is ready, start or update the workspace dev server and navigate the user to the absolute path /${input.appId} on the workspace origin. Do not prefix with /dispatch/, /apps/, /workspace/, or any other Dispatch tab — the new app is mounted at the workspace root, not under Dispatch. If you have a navigate tool available, pass /${input.appId} verbatim; if you only have a window.location-style escape hatch, set it to /${input.appId}.`,
|
|
138
|
-
].join("\n");
|
|
139
|
-
}
|
|
140
|
-
|
|
141
64
|
async function fetchJson(url: string, init?: RequestInit): Promise<any> {
|
|
142
65
|
const res = await fetch(url, init);
|
|
143
66
|
const data = await res.json().catch(() => null);
|
|
@@ -168,6 +91,8 @@ const ERROR_FAILURE_REASONS = new Set([
|
|
|
168
91
|
"credential-store-unavailable",
|
|
169
92
|
"settings-management-required",
|
|
170
93
|
]);
|
|
94
|
+
const LOCAL_APP_DOCS_URL =
|
|
95
|
+
"https://agent-native.com/docs/multi-app-workspace#adding-a-new-app";
|
|
171
96
|
|
|
172
97
|
function isErrorFailureReason(reason: string | null): boolean {
|
|
173
98
|
return !!reason && ERROR_FAILURE_REASONS.has(reason);
|
|
@@ -292,14 +217,14 @@ export function CreateAppFlow({
|
|
|
292
217
|
async function submit(rawPrompt: string) {
|
|
293
218
|
const trimmed = rawPrompt.trim();
|
|
294
219
|
if (!trimmed || isSubmitting) return;
|
|
295
|
-
const appId =
|
|
220
|
+
const appId = titleFromChatFirstAppPrompt(trimmed);
|
|
296
221
|
const validationError = getWorkspaceAppIdValidationError(appId);
|
|
297
222
|
if (validationError) {
|
|
298
223
|
setStatusMessage(validationError);
|
|
299
224
|
return;
|
|
300
225
|
}
|
|
301
226
|
|
|
302
|
-
const message =
|
|
227
|
+
const message = buildChatFirstAppCreationPrompt({
|
|
303
228
|
appId,
|
|
304
229
|
prompt: trimmed,
|
|
305
230
|
selectedKeys:
|
|
@@ -653,16 +578,27 @@ export function CreateAppFlow({
|
|
|
653
578
|
) : null}
|
|
654
579
|
</div>
|
|
655
580
|
{failureReason === "builder-not-connected" ? (
|
|
656
|
-
<
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
581
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
582
|
+
<Button
|
|
583
|
+
type="button"
|
|
584
|
+
size="sm"
|
|
585
|
+
variant="outline"
|
|
586
|
+
onClick={() => connectFlow.start()}
|
|
587
|
+
disabled={connectFlow.connecting}
|
|
588
|
+
className="w-fit"
|
|
589
|
+
>
|
|
590
|
+
{connectFlow.connecting ? "Connecting..." : "Connect Builder"}
|
|
591
|
+
</Button>
|
|
592
|
+
<a
|
|
593
|
+
href={LOCAL_APP_DOCS_URL}
|
|
594
|
+
target="_blank"
|
|
595
|
+
rel="noreferrer"
|
|
596
|
+
data-create-app-local-link
|
|
597
|
+
className="inline-flex items-center gap-1 text-xs font-medium text-foreground underline underline-offset-2"
|
|
598
|
+
>
|
|
599
|
+
Create locally <IconArrowUpRight className="h-3 w-3" />
|
|
600
|
+
</a>
|
|
601
|
+
</div>
|
|
666
602
|
) : null}
|
|
667
603
|
{failureReason === "credential-store-unavailable" ||
|
|
668
604
|
failureReason === "builder-error" ? (
|
|
@@ -6,7 +6,11 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
6
6
|
|
|
7
7
|
import { AdminShell } from "../admin-navigation";
|
|
8
8
|
import { TooltipProvider } from "../ui/tooltip";
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
buildChatFirstEmbedSessionInput,
|
|
11
|
+
formatThreadAge,
|
|
12
|
+
NavContent,
|
|
13
|
+
} from "./Layout";
|
|
10
14
|
|
|
11
15
|
const clientState = vi.hoisted(() => ({
|
|
12
16
|
createThread: vi.fn<() => Promise<string | null>>(),
|
|
@@ -36,6 +40,7 @@ vi.mock("@agent-native/core/client/agent-chat", () => ({
|
|
|
36
40
|
}));
|
|
37
41
|
|
|
38
42
|
vi.mock("@agent-native/core/client/api-path", () => ({
|
|
43
|
+
agentNativePath: (path: string) => path,
|
|
39
44
|
appBasePath: () => "",
|
|
40
45
|
appPath: (path: string) => path,
|
|
41
46
|
}));
|
|
@@ -96,6 +101,16 @@ describe("formatThreadAge", () => {
|
|
|
96
101
|
});
|
|
97
102
|
});
|
|
98
103
|
|
|
104
|
+
describe("chat-first embed sessions", () => {
|
|
105
|
+
it("keeps the granted app id on app-relative embed requests", () => {
|
|
106
|
+
expect(buildChatFirstEmbedSessionInput("mail", "/mail/inbox")).toEqual({
|
|
107
|
+
app: "mail",
|
|
108
|
+
path: "/mail/inbox",
|
|
109
|
+
chrome: "minimal",
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
99
114
|
describe("Dispatch NavContent", () => {
|
|
100
115
|
let container: HTMLDivElement;
|
|
101
116
|
let root: Root;
|