@agent-native/dispatch 0.23.3 → 0.24.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/list-connected-agents.d.ts +1 -0
- package/dist/actions/list-connected-agents.js +5 -1
- package/dist/actions/list-connected-agents.js.map +1 -1
- package/dist/actions/list-dispatch-usage-metrics.d.ts +2 -0
- package/dist/actions/list-dispatch-usage-metrics.js +11 -1
- package/dist/actions/list-dispatch-usage-metrics.js.map +1 -1
- package/dist/actions/provider-api-register.d.ts +11 -11
- package/dist/actions/remix-workspace-template.js +4 -3
- package/dist/actions/remix-workspace-template.js.map +1 -1
- package/dist/actions/scaffold-workspace-app.js +3 -2
- package/dist/actions/scaffold-workspace-app.js.map +1 -1
- package/dist/actions/start-workspace-app-creation.js +3 -2
- package/dist/actions/start-workspace-app-creation.js.map +1 -1
- package/dist/actions/view-screen.js +11 -1
- package/dist/actions/view-screen.js.map +1 -1
- package/dist/components/app-icon.js +1 -1
- package/dist/components/app-icon.js.map +1 -1
- package/dist/components/app-list-row.d.ts +1 -1
- package/dist/components/app-list-row.d.ts.map +1 -1
- package/dist/components/app-list-row.js +2 -2
- package/dist/components/app-list-row.js.map +1 -1
- package/dist/components/connected-app-card.d.ts.map +1 -1
- package/dist/components/connected-app-card.js +2 -1
- package/dist/components/connected-app-card.js.map +1 -1
- package/dist/components/create-app-popover.d.ts.map +1 -1
- package/dist/components/create-app-popover.js +11 -0
- package/dist/components/create-app-popover.js.map +1 -1
- package/dist/components/dispatch-control-plane.d.ts.map +1 -1
- package/dist/components/dispatch-control-plane.js +3 -2
- package/dist/components/dispatch-control-plane.js.map +1 -1
- package/dist/components/layout/Layout.d.ts +1 -0
- package/dist/components/layout/Layout.d.ts.map +1 -1
- package/dist/components/layout/Layout.js +47 -10
- package/dist/components/layout/Layout.js.map +1 -1
- package/dist/components/layout/workspace-apps-rail.d.ts.map +1 -1
- package/dist/components/layout/workspace-apps-rail.js +2 -2
- package/dist/components/layout/workspace-apps-rail.js.map +1 -1
- package/dist/components/other-apps-section.d.ts.map +1 -1
- package/dist/components/other-apps-section.js +5 -1
- package/dist/components/other-apps-section.js.map +1 -1
- package/dist/components/workspace-template-card.d.ts.map +1 -1
- package/dist/components/workspace-template-card.js +8 -14
- package/dist/components/workspace-template-card.js.map +1 -1
- package/dist/config.d.ts +2 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/hooks/use-navigation-state.d.ts +2 -0
- package/dist/hooks/use-navigation-state.d.ts.map +1 -1
- package/dist/hooks/use-navigation-state.js +10 -0
- package/dist/hooks/use-navigation-state.js.map +1 -1
- package/dist/lib/embed-session-recovery.d.ts +3 -0
- package/dist/lib/embed-session-recovery.d.ts.map +1 -0
- package/dist/lib/embed-session-recovery.js +18 -0
- package/dist/lib/embed-session-recovery.js.map +1 -0
- package/dist/lib/other-apps.d.ts +2 -0
- package/dist/lib/other-apps.d.ts.map +1 -1
- package/dist/lib/other-apps.js +2 -0
- package/dist/lib/other-apps.js.map +1 -1
- package/dist/lib/workspace-apps.d.ts +7 -0
- package/dist/lib/workspace-apps.d.ts.map +1 -1
- package/dist/lib/workspace-apps.js +17 -0
- package/dist/lib/workspace-apps.js.map +1 -1
- package/dist/lib/workspace-apps.test.d.ts +2 -0
- package/dist/lib/workspace-apps.test.d.ts.map +1 -0
- package/dist/lib/workspace-apps.test.js +16 -0
- package/dist/lib/workspace-apps.test.js.map +1 -0
- package/dist/routes/pages/apps.$appId.d.ts.map +1 -1
- package/dist/routes/pages/apps.$appId.js +10 -11
- package/dist/routes/pages/apps.$appId.js.map +1 -1
- package/dist/routes/pages/apps.d.ts.map +1 -1
- package/dist/routes/pages/apps.js +2 -1
- package/dist/routes/pages/apps.js.map +1 -1
- package/dist/routes/pages/chat.d.ts.map +1 -1
- package/dist/routes/pages/chat.js +7 -2
- package/dist/routes/pages/chat.js.map +1 -1
- package/dist/routes/pages/metrics.d.ts.map +1 -1
- package/dist/routes/pages/metrics.js +143 -17
- package/dist/routes/pages/metrics.js.map +1 -1
- package/dist/routes/pages/settings.d.ts.map +1 -1
- package/dist/routes/pages/settings.js +13 -1
- package/dist/routes/pages/settings.js.map +1 -1
- package/dist/server/lib/usage-metrics-store.d.ts +17 -0
- package/dist/server/lib/usage-metrics-store.d.ts.map +1 -1
- package/dist/server/lib/usage-metrics-store.js +187 -30
- package/dist/server/lib/usage-metrics-store.js.map +1 -1
- package/dist/server/plugins/agent-chat.js +2 -0
- package/dist/server/plugins/agent-chat.js.map +1 -1
- package/dist/server/plugins/auth.d.ts.map +1 -1
- package/dist/server/plugins/auth.js +1 -0
- package/dist/server/plugins/auth.js.map +1 -1
- package/dist/server/plugins/integrations.d.ts.map +1 -1
- package/dist/server/plugins/integrations.js +1 -0
- package/dist/server/plugins/integrations.js.map +1 -1
- package/package.json +3 -3
- package/src/actions/list-connected-agents.spec.ts +73 -0
- package/src/actions/list-connected-agents.ts +6 -2
- package/src/actions/list-dispatch-usage-metrics.ts +11 -1
- package/src/actions/remix-workspace-template.spec.ts +29 -0
- package/src/actions/remix-workspace-template.ts +13 -6
- package/src/actions/scaffold-workspace-app.ts +12 -5
- package/src/actions/start-workspace-app-creation.ts +8 -2
- package/src/actions/view-screen.ts +13 -1
- package/src/components/app-icon.spec.tsx +34 -0
- package/src/components/app-icon.tsx +1 -1
- package/src/components/app-list-row.tsx +3 -2
- package/src/components/connected-app-card.tsx +3 -1
- package/src/components/create-app-popover.spec.tsx +26 -2
- package/src/components/create-app-popover.tsx +10 -0
- package/src/components/dispatch-control-plane.spec.tsx +9 -1
- package/src/components/dispatch-control-plane.tsx +8 -2
- package/src/components/layout/Layout.spec.tsx +12 -0
- package/src/components/layout/Layout.tsx +84 -13
- package/src/components/layout/workspace-apps-rail.tsx +2 -1
- package/src/components/other-apps-section.tsx +8 -1
- package/src/components/workspace-template-card.spec.tsx +1 -1
- package/src/components/workspace-template-card.tsx +9 -16
- package/src/config.ts +2 -0
- package/src/hooks/use-navigation-state.ts +12 -0
- package/src/lib/embed-session-recovery.spec.ts +63 -0
- package/src/lib/embed-session-recovery.ts +26 -0
- package/src/lib/other-apps.spec.ts +26 -0
- package/src/lib/other-apps.ts +5 -0
- package/src/lib/workspace-apps.spec.ts +24 -1
- package/src/lib/workspace-apps.test.ts +20 -0
- package/src/lib/workspace-apps.ts +25 -0
- package/src/routes/pages/apps.$appId.tsx +10 -11
- package/src/routes/pages/apps.tsx +5 -2
- package/src/routes/pages/chat.spec.tsx +10 -0
- package/src/routes/pages/chat.tsx +8 -0
- package/src/routes/pages/metrics.tsx +452 -102
- package/src/routes/pages/settings.tsx +13 -1
- package/src/server/lib/usage-metrics-store.spec.ts +117 -0
- package/src/server/lib/usage-metrics-store.ts +249 -30
- package/src/server/plugins/agent-chat.spec.ts +4 -0
- package/src/server/plugins/agent-chat.ts +2 -0
- package/src/server/plugins/auth.spec.ts +45 -0
- package/src/server/plugins/auth.ts +1 -0
- package/src/server/plugins/integrations.ts +1 -0
- package/src/styles/dispatch.css +9 -0
|
@@ -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,+BAA+B;IAC/B,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;;;;;;;;;;;;;;;;;;;;;;;;;;4EA0B4D;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 \"update-workspace-app-metadata\",\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 a user asks to rename an existing workspace app or change its Dispatch title/description, call update-workspace-app-metadata with the existing appId from list-workspace-apps. This is a metadata-only edit — never call start-workspace-app-creation, which creates a new app and Builder branch.\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":"auth.d.ts","sourceRoot":"","sources":["../../../src/server/plugins/auth.ts"],"names":[],"mappings":"AAeA;;;;;GAKG;AACH,QAAA,MAAM,kBAAkB,aAAoB,GAAG,
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/server/plugins/auth.ts"],"names":[],"mappings":"AAeA;;;;;GAKG;AACH,QAAA,MAAM,kBAAkB,aAAoB,GAAG,kBAY9C,CAAC;eAEa,kBAAkB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../src/server/plugins/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAE7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,iBAAiB,GAAG;IACxB,OAAO,EAAE,UAAU;IACnB,OAAO,EACL,sGAAsG;IACxG,QAAQ,EAAE;QACR,4DAA4D;QAC5D,6DAA6D;QAC7D,oDAAoD;KACrD;CACO,CAAC;AAEX;;;;;GAKG;AACH,MAAM,kBAAkB,GAAG,KAAK,EAAE,QAAa,EAAE,EAAE;IACjD,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,EAAE,EAAE,GAAG,iBAAiB,EAAE,CAAC;IACtD,MAAM,UAAU,GAAG,UAAU,CAAC,UAAU,IAAI,KAAK,CAAC;IAClD,MAAM,SAAS,GACZ,UAAU,CAAC,SAAiD;QAC7D,iBAAiB,CAAC;IACpB,MAAM,MAAM,GAAG,gBAAgB,CAAC;QAC9B,UAAU;QACV,SAAS,EAAE,SAAgB;
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../src/server/plugins/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAE7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,iBAAiB,GAAG;IACxB,OAAO,EAAE,UAAU;IACnB,OAAO,EACL,sGAAsG;IACxG,QAAQ,EAAE;QACR,4DAA4D;QAC5D,6DAA6D;QAC7D,oDAAoD;KACrD;CACO,CAAC;AAEX;;;;;GAKG;AACH,MAAM,kBAAkB,GAAG,KAAK,EAAE,QAAa,EAAE,EAAE;IACjD,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,EAAE,EAAE,GAAG,iBAAiB,EAAE,CAAC;IACtD,MAAM,UAAU,GAAG,UAAU,CAAC,UAAU,IAAI,KAAK,CAAC;IAClD,MAAM,SAAS,GACZ,UAAU,CAAC,SAAiD;QAC7D,iBAAiB,CAAC;IACpB,MAAM,MAAM,GAAG,gBAAgB,CAAC;QAC9B,UAAU;QACV,SAAS,EAAE,SAAgB;QAC3B,WAAW,EAAE,UAAU,CAAC,WAAW;KACpC,CAAC,CAAC;IACH,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC;AAC1B,CAAC,CAAC;AAEF,eAAe,kBAAkB,CAAC","sourcesContent":["import { createAuthPlugin } from \"@agent-native/core/server\";\n\nimport { getDispatchConfig } from \"../index.js\";\n\nconst DEFAULT_MARKETING = {\n appName: \"Dispatch\",\n tagline:\n \"Your AI agent manages secrets, orchestrates other agents, and routes messages across your workspace.\",\n features: [\n \"Centralized vault for secrets with granular per-app grants\",\n \"Cross-agent orchestration and delegation to specialist apps\",\n \"Slack and Telegram routing with approval workflows\",\n ],\n} as const;\n\n/**\n * Defer config + plugin construction until the Nitro plugin actually fires.\n * This way `setupDispatch(config)` can run after plugin module-load order\n * (Nitro doesn't guarantee load order across plugin files) and still feed\n * `googleOnly` / `marketing` into `createAuthPlugin`.\n */\nconst dispatchAuthPlugin = async (nitroApp: any) => {\n const { auth: authConfig = {} } = getDispatchConfig();\n const googleOnly = authConfig.googleOnly ?? false;\n const marketing =\n (authConfig.marketing as Record<string, unknown> | undefined) ??\n DEFAULT_MARKETING;\n const plugin = createAuthPlugin({\n googleOnly,\n marketing: marketing as any,\n publicPaths: authConfig.publicPaths,\n });\n return plugin(nitroApp);\n};\n\nexport default dispatchAuthPlugin;\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integrations.d.ts","sourceRoot":"","sources":["../../../src/server/plugins/integrations.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"integrations.d.ts","sourceRoot":"","sources":["../../../src/server/plugins/integrations.ts"],"names":[],"mappings":"AAmDA;;;;GAIG;AACH,QAAA,MAAM,0BAA0B,aAAoB,GAAG,kBAuBtD,CAAC;eAEa,0BAA0B"}
|
|
@@ -34,6 +34,7 @@ When a user asks for something:
|
|
|
34
34
|
- 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.
|
|
35
35
|
- 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.
|
|
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
|
+
- If a user asks to rename an existing workspace app or change its Dispatch title/description, call update-workspace-app-metadata with the existing appId from list-workspace-apps. Do not call start-workspace-app-creation for a metadata-only edit because that starts a new app and Builder branch.
|
|
37
38
|
- 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
39
|
- 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
40
|
- 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.
|
|
@@ -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. 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"]}
|
|
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4FA6BiD,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 user asks to rename an existing workspace app or change its Dispatch title/description, call update-workspace-app-metadata with the existing appId from list-workspace-apps. Do not call start-workspace-app-creation for a metadata-only edit because that starts a new app and Builder branch.\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.24.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.
|
|
87
|
+
"@agent-native/toolkit": "0.14.0"
|
|
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.152.0"
|
|
102
102
|
},
|
|
103
103
|
"peerDependencies": {
|
|
104
104
|
"@agent-native/core": ">=0.8.0",
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
|
|
3
|
+
const mocks = vi.hoisted(() => ({
|
|
4
|
+
discoverAgents: vi.fn(),
|
|
5
|
+
getBuiltinAgents: vi.fn(),
|
|
6
|
+
getRequestUserEmail: vi.fn(() => "owner@example.test"),
|
|
7
|
+
resourceGet: vi.fn(),
|
|
8
|
+
resourceListAccessible: vi.fn(async () => []),
|
|
9
|
+
}));
|
|
10
|
+
|
|
11
|
+
vi.mock("@agent-native/core", () => ({
|
|
12
|
+
defineAction: (options: unknown) => options,
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
vi.mock("@agent-native/core/resources/metadata", () => ({
|
|
16
|
+
parseRemoteAgentManifest: vi.fn(),
|
|
17
|
+
REMOTE_AGENT_RESOURCE_PREFIXES: [],
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
vi.mock("@agent-native/core/resources/store", () => ({
|
|
21
|
+
resourceGet: (...args: unknown[]) => mocks.resourceGet(...args),
|
|
22
|
+
resourceListAccessible: (...args: unknown[]) =>
|
|
23
|
+
mocks.resourceListAccessible(...args),
|
|
24
|
+
SHARED_OWNER: "shared",
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
vi.mock("@agent-native/core/server", () => ({
|
|
28
|
+
getRequestUserEmail: () => mocks.getRequestUserEmail(),
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
vi.mock("@agent-native/core/server/agent-discovery", () => ({
|
|
32
|
+
discoverAgents: (...args: unknown[]) => mocks.discoverAgents(...args),
|
|
33
|
+
getBuiltinAgents: (...args: unknown[]) => mocks.getBuiltinAgents(...args),
|
|
34
|
+
normalizeAgentId: (id: string) => id.trim().toLowerCase(),
|
|
35
|
+
shouldIncludeRemoteAgentManifest: vi.fn(),
|
|
36
|
+
}));
|
|
37
|
+
|
|
38
|
+
vi.mock("../server/index.js", () => ({
|
|
39
|
+
getDispatchConfig: vi.fn(async () => ({ hiddenAgentIds: [] })),
|
|
40
|
+
}));
|
|
41
|
+
|
|
42
|
+
describe("list-connected-agents", () => {
|
|
43
|
+
it("keeps a built-in app home URL separate from its A2A endpoint", async () => {
|
|
44
|
+
mocks.getBuiltinAgents.mockReturnValue([
|
|
45
|
+
{
|
|
46
|
+
id: "clips",
|
|
47
|
+
name: "Clips",
|
|
48
|
+
description: "Record and share",
|
|
49
|
+
url: "https://clips.agent-native.com",
|
|
50
|
+
color: "#000000",
|
|
51
|
+
},
|
|
52
|
+
]);
|
|
53
|
+
mocks.discoverAgents.mockResolvedValue([
|
|
54
|
+
{
|
|
55
|
+
id: "clips",
|
|
56
|
+
name: "Clips",
|
|
57
|
+
description: "Record and share",
|
|
58
|
+
url: "https://clips.agent-native.com/share/WrA8ZQ3oxa2T?ref=clip_share",
|
|
59
|
+
color: "#000000",
|
|
60
|
+
},
|
|
61
|
+
]);
|
|
62
|
+
|
|
63
|
+
const { default: action } = await import("./list-connected-agents.js");
|
|
64
|
+
const [clips] = await action.run({});
|
|
65
|
+
|
|
66
|
+
expect(clips).toMatchObject({
|
|
67
|
+
id: "clips",
|
|
68
|
+
url: "https://clips.agent-native.com/share/WrA8ZQ3oxa2T?ref=clip_share",
|
|
69
|
+
homeUrl: "https://clips.agent-native.com",
|
|
70
|
+
source: "builtin",
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
});
|
|
@@ -27,8 +27,10 @@ export default defineAction({
|
|
|
27
27
|
(m) => m.getDispatchConfig(),
|
|
28
28
|
);
|
|
29
29
|
const discovered = await discoverAgents("dispatch");
|
|
30
|
-
const
|
|
31
|
-
|
|
30
|
+
const builtins = getBuiltinAgents("dispatch");
|
|
31
|
+
const builtinIds = new Set(builtins.map((agent) => agent.id));
|
|
32
|
+
const builtinHomeUrls = new Map(
|
|
33
|
+
builtins.map((agent) => [agent.id, agent.url]),
|
|
32
34
|
);
|
|
33
35
|
const ownerEmail = getRequestUserEmail();
|
|
34
36
|
if (!ownerEmail) throw new Error("no authenticated user");
|
|
@@ -78,8 +80,10 @@ export default defineAction({
|
|
|
78
80
|
const connected = discovered.map((agent) => {
|
|
79
81
|
const custom = customById.get(agent.id);
|
|
80
82
|
const isBuiltin = builtinIds.has(agent.id);
|
|
83
|
+
const homeUrl = isBuiltin ? builtinHomeUrls.get(agent.id) : undefined;
|
|
81
84
|
return {
|
|
82
85
|
...agent,
|
|
86
|
+
...(homeUrl ? { homeUrl } : {}),
|
|
83
87
|
source: isBuiltin ? "builtin" : custom ? "custom" : "workspace",
|
|
84
88
|
resourceId: custom?.resourceId,
|
|
85
89
|
path: custom?.path,
|
|
@@ -5,7 +5,7 @@ import { listDispatchUsageMetrics } from "../server/lib/usage-metrics-store.js";
|
|
|
5
5
|
|
|
6
6
|
export default defineAction({
|
|
7
7
|
description:
|
|
8
|
-
"Get workspace
|
|
8
|
+
"Get personal or workspace LLM usage, spend or Builder.io credit spend, app attribution, prompt previews, and recent activity metrics for Dispatch.",
|
|
9
9
|
schema: z.object({
|
|
10
10
|
sinceDays: z.coerce
|
|
11
11
|
.number()
|
|
@@ -14,6 +14,16 @@ export default defineAction({
|
|
|
14
14
|
.max(365)
|
|
15
15
|
.default(30)
|
|
16
16
|
.describe("Lookback window in days. Defaults to 30."),
|
|
17
|
+
scope: z
|
|
18
|
+
.enum(["me", "workspace"])
|
|
19
|
+
.default("workspace")
|
|
20
|
+
.describe("Whether to inspect your account or the whole workspace."),
|
|
21
|
+
userEmail: z
|
|
22
|
+
.string()
|
|
23
|
+
.trim()
|
|
24
|
+
.min(1)
|
|
25
|
+
.optional()
|
|
26
|
+
.describe("Optional workspace member email to filter all usage to."),
|
|
17
27
|
}),
|
|
18
28
|
http: { method: "GET" },
|
|
19
29
|
run: async (args) => listDispatchUsageMetrics(args),
|
|
@@ -68,6 +68,35 @@ describe("remix-workspace-template action", () => {
|
|
|
68
68
|
);
|
|
69
69
|
});
|
|
70
70
|
|
|
71
|
+
it("normalizes a human-friendly app name before scaffolding", async () => {
|
|
72
|
+
vi.stubEnv("NODE_ENV", "test");
|
|
73
|
+
vi.stubEnv("NETLIFY", "");
|
|
74
|
+
mocks.scaffoldWorkspaceAppFromTemplate.mockResolvedValue({
|
|
75
|
+
appId: "customer-portal",
|
|
76
|
+
template: "mail",
|
|
77
|
+
output: "scaffolded",
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
await action.run({
|
|
81
|
+
templateId: "mail",
|
|
82
|
+
appId: "Customer Portal",
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
expect(mocks.scaffoldWorkspaceAppFromTemplate).toHaveBeenCalledWith({
|
|
86
|
+
template: "mail",
|
|
87
|
+
appId: "customer-portal",
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("rejects names that normalize to an empty app id", async () => {
|
|
92
|
+
await expect(
|
|
93
|
+
action.run({ templateId: "mail", appId: "---" }),
|
|
94
|
+
).rejects.toThrow(
|
|
95
|
+
"Use a non-reserved app id with lowercase letters, numbers, and hyphens.",
|
|
96
|
+
);
|
|
97
|
+
expect(mocks.scaffoldWorkspaceAppFromTemplate).not.toHaveBeenCalled();
|
|
98
|
+
});
|
|
99
|
+
|
|
71
100
|
it("starts a hosted private app without copying data or secrets", async () => {
|
|
72
101
|
vi.stubEnv("NODE_ENV", "production");
|
|
73
102
|
mocks.startWorkspaceAppCreation.mockResolvedValue({
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { defineAction } from "@agent-native/core";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
getWorkspaceAppIdValidationError,
|
|
4
|
+
normalizeWorkspaceAppId,
|
|
5
|
+
} from "@agent-native/core/shared";
|
|
3
6
|
import { z } from "zod";
|
|
4
7
|
|
|
5
8
|
import {
|
|
@@ -42,14 +45,18 @@ export default defineAction({
|
|
|
42
45
|
appId: z
|
|
43
46
|
.string()
|
|
44
47
|
.max(64)
|
|
48
|
+
.transform(normalizeWorkspaceAppId)
|
|
45
49
|
.optional()
|
|
46
50
|
.nullable()
|
|
47
|
-
.refine(
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
+
.refine(
|
|
52
|
+
(appId) => appId == null || !getWorkspaceAppIdValidationError(appId),
|
|
53
|
+
{
|
|
54
|
+
message:
|
|
55
|
+
"Use a non-reserved app id with lowercase letters, numbers, and hyphens.",
|
|
56
|
+
},
|
|
57
|
+
)
|
|
51
58
|
.describe(
|
|
52
|
-
"Optional target workspace app id. Defaults to <template>-remix so the source app remains independent.",
|
|
59
|
+
"Optional target workspace app id or human-friendly name. Names are converted to lowercase, hyphenated ids. Defaults to <template>-remix so the source app remains independent.",
|
|
53
60
|
),
|
|
54
61
|
description: z
|
|
55
62
|
.string()
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { defineAction } from "@agent-native/core";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
getWorkspaceAppIdValidationError,
|
|
4
|
+
normalizeWorkspaceAppId,
|
|
5
|
+
} from "@agent-native/core/shared";
|
|
3
6
|
import { z } from "zod";
|
|
4
7
|
|
|
5
8
|
import { scaffoldWorkspaceAppFromTemplate } from "../server/lib/app-creation-store.js";
|
|
@@ -27,12 +30,16 @@ export default defineAction({
|
|
|
27
30
|
appId: z
|
|
28
31
|
.string()
|
|
29
32
|
.max(64)
|
|
33
|
+
.transform(normalizeWorkspaceAppId)
|
|
30
34
|
.optional()
|
|
31
35
|
.nullable()
|
|
32
|
-
.refine(
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
.refine(
|
|
37
|
+
(appId) => appId == null || !getWorkspaceAppIdValidationError(appId),
|
|
38
|
+
{
|
|
39
|
+
message:
|
|
40
|
+
"Use a non-reserved app id with lowercase letters, numbers, and hyphens.",
|
|
41
|
+
},
|
|
42
|
+
)
|
|
36
43
|
.describe(
|
|
37
44
|
"Optional override for the apps/<id>/ directory name; defaults to the template name",
|
|
38
45
|
),
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { defineAction } from "@agent-native/core";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
getWorkspaceAppIdValidationError,
|
|
4
|
+
normalizeWorkspaceAppId,
|
|
5
|
+
} from "@agent-native/core/shared";
|
|
3
6
|
import { z } from "zod";
|
|
4
7
|
|
|
5
8
|
import { startWorkspaceAppCreation } from "../server/lib/app-creation-store.js";
|
|
@@ -12,13 +15,16 @@ export default defineAction({
|
|
|
12
15
|
appId: z
|
|
13
16
|
.string()
|
|
14
17
|
.max(64)
|
|
18
|
+
.transform(normalizeWorkspaceAppId)
|
|
15
19
|
.refine((appId) => !getWorkspaceAppIdValidationError(appId), {
|
|
16
20
|
message:
|
|
17
21
|
"Use a non-reserved app id with lowercase letters, numbers, and hyphens.",
|
|
18
22
|
})
|
|
19
23
|
.optional()
|
|
20
24
|
.nullable()
|
|
21
|
-
.describe(
|
|
25
|
+
.describe(
|
|
26
|
+
"Desired workspace app id/path or human-friendly name. Names are converted to lowercase, hyphenated ids.",
|
|
27
|
+
),
|
|
22
28
|
template: z
|
|
23
29
|
.string()
|
|
24
30
|
.optional()
|
|
@@ -155,9 +155,21 @@ export default defineAction({
|
|
|
155
155
|
}
|
|
156
156
|
if (navigation?.view === "metrics") {
|
|
157
157
|
try {
|
|
158
|
-
const
|
|
158
|
+
const usageScope =
|
|
159
|
+
navigation.usageScope === "workspace" ? "workspace" : "me";
|
|
160
|
+
const usageUserEmail =
|
|
161
|
+
typeof navigation.usageUserEmail === "string"
|
|
162
|
+
? navigation.usageUserEmail
|
|
163
|
+
: undefined;
|
|
164
|
+
const metrics = await listDispatchUsageMetrics({
|
|
165
|
+
sinceDays: 30,
|
|
166
|
+
scope: usageScope,
|
|
167
|
+
userEmail: usageUserEmail,
|
|
168
|
+
});
|
|
159
169
|
screen.usageMetrics = {
|
|
160
170
|
billing: metrics.billing,
|
|
171
|
+
viewScope: metrics.viewScope,
|
|
172
|
+
selectedUserEmail: metrics.selectedUserEmail,
|
|
161
173
|
totals: metrics.totals,
|
|
162
174
|
byApp: metrics.byApp.slice(0, 8),
|
|
163
175
|
byUser: metrics.byUser.slice(0, 8),
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// @vitest-environment happy-dom
|
|
2
|
+
import React, { act } from "react";
|
|
3
|
+
import { createRoot, type Root } from "react-dom/client";
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
5
|
+
|
|
6
|
+
import { AppIcon } from "./app-icon";
|
|
7
|
+
|
|
8
|
+
describe("AppIcon", () => {
|
|
9
|
+
let container: HTMLDivElement;
|
|
10
|
+
let root: Root;
|
|
11
|
+
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
container = document.createElement("div");
|
|
14
|
+
document.body.appendChild(container);
|
|
15
|
+
root = createRoot(container);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
afterEach(() => {
|
|
19
|
+
act(() => root.unmount());
|
|
20
|
+
container.remove();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("gives Plan a colored generated accent instead of a neutral gray", () => {
|
|
24
|
+
act(() => {
|
|
25
|
+
root.render(<AppIcon id="plan" name="Plan" />);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const icon = container.firstElementChild as HTMLElement;
|
|
29
|
+
expect(icon.style.getPropertyValue("--dispatch-app-icon-color-rgb")).toBe(
|
|
30
|
+
"47 111 237",
|
|
31
|
+
);
|
|
32
|
+
expect(icon.style.color).toBe("rgb(var(--dispatch-app-icon-color-rgb))");
|
|
33
|
+
});
|
|
34
|
+
});
|
|
@@ -67,7 +67,7 @@ const APP_VISUALS_BY_ID: Record<
|
|
|
67
67
|
forms: { icon: IconStack2, colorRgb: "6 182 212" },
|
|
68
68
|
macros: { icon: IconCode, colorRgb: "113 113 122" },
|
|
69
69
|
mail: { icon: IconMail, colorRgb: "59 130 246" },
|
|
70
|
-
plan: { icon: IconLayoutBoard, colorRgb: "
|
|
70
|
+
plan: { icon: IconLayoutBoard, colorRgb: "47 111 237" },
|
|
71
71
|
slides: { icon: IconPresentation, colorRgb: "236 72 153" },
|
|
72
72
|
tasks: { icon: IconListCheck, colorRgb: "99 102 241" },
|
|
73
73
|
};
|
|
@@ -4,7 +4,8 @@ import { cn } from "../lib/utils";
|
|
|
4
4
|
|
|
5
5
|
export const APP_LIST_GRID_CLASS =
|
|
6
6
|
"mx-auto w-full max-w-[1000px] xl:grid xl:grid-cols-2 xl:gap-3 xl:overflow-visible xl:rounded-none xl:bg-transparent";
|
|
7
|
-
export const APP_LIST_GRID_ROW_CLASS =
|
|
7
|
+
export const APP_LIST_GRID_ROW_CLASS =
|
|
8
|
+
"xl:rounded-2xl xl:border xl:border-transparent xl:bg-card xl:transition-[background-color,border-color] xl:hover:border-border xl:focus-within:border-ring/50";
|
|
8
9
|
|
|
9
10
|
export function AppList({
|
|
10
11
|
children,
|
|
@@ -30,7 +31,7 @@ export function AppListRow({
|
|
|
30
31
|
return (
|
|
31
32
|
<div
|
|
32
33
|
className={cn(
|
|
33
|
-
"group flex min-w-0 items-center gap-3 border-b px-4 py-3.5 last:border-b-0 transition-[background-color] hover:bg-muted/30 focus-within:bg-muted/30",
|
|
34
|
+
"group flex min-w-0 items-center gap-3 border-b px-4 py-3.5 last:border-b-0 transition-[background-color,border-color] hover:bg-muted/30 focus-within:bg-muted/30",
|
|
34
35
|
className,
|
|
35
36
|
)}
|
|
36
37
|
>
|
|
@@ -12,6 +12,8 @@ export function ConnectedAppCard({
|
|
|
12
12
|
app: ConnectedAppSummary;
|
|
13
13
|
className?: string;
|
|
14
14
|
}) {
|
|
15
|
+
const launchUrl = app.homeUrl ?? app.url;
|
|
16
|
+
|
|
15
17
|
return (
|
|
16
18
|
<AppListRow className={className}>
|
|
17
19
|
<AppIcon id={app.id} name={app.name} color={app.color} size="sm" />
|
|
@@ -25,7 +27,7 @@ export function ConnectedAppCard({
|
|
|
25
27
|
</div>
|
|
26
28
|
<AppOpenActions
|
|
27
29
|
name={app.name}
|
|
28
|
-
href={withBuilderUtmTrackingParams(
|
|
30
|
+
href={withBuilderUtmTrackingParams(launchUrl, {
|
|
29
31
|
campaign: "product",
|
|
30
32
|
content: "dispatch_app",
|
|
31
33
|
})}
|
|
@@ -152,9 +152,12 @@ describe("CreateAppFlow", () => {
|
|
|
152
152
|
vi.unstubAllGlobals();
|
|
153
153
|
});
|
|
154
154
|
|
|
155
|
-
async function renderAndSubmit(
|
|
155
|
+
async function renderAndSubmit(
|
|
156
|
+
prompt: string,
|
|
157
|
+
props: { onClose?: () => void } = {},
|
|
158
|
+
) {
|
|
156
159
|
await act(async () => {
|
|
157
|
-
root.render(React.createElement(CreateAppFlow,
|
|
160
|
+
root.render(React.createElement(CreateAppFlow, props));
|
|
158
161
|
});
|
|
159
162
|
|
|
160
163
|
changeValue(
|
|
@@ -213,6 +216,27 @@ describe("CreateAppFlow", () => {
|
|
|
213
216
|
).toBe(false);
|
|
214
217
|
});
|
|
215
218
|
|
|
219
|
+
it("opens a fresh local chat when the server hands off app creation", async () => {
|
|
220
|
+
startWorkspaceAppCreationResponse.result = {
|
|
221
|
+
mode: "local-agent",
|
|
222
|
+
appId: "quality-dashboard",
|
|
223
|
+
prompt: "Create the quality dashboard in the new workspace app.",
|
|
224
|
+
message: "Starting the local coding chat.",
|
|
225
|
+
};
|
|
226
|
+
const onClose = vi.fn();
|
|
227
|
+
|
|
228
|
+
await renderAndSubmit("Build a quality dashboard", { onClose });
|
|
229
|
+
|
|
230
|
+
expect(sendToAgentChatMock).toHaveBeenCalledWith({
|
|
231
|
+
message: "Create the quality dashboard in the new workspace app.",
|
|
232
|
+
submit: true,
|
|
233
|
+
type: "code",
|
|
234
|
+
newTab: true,
|
|
235
|
+
reuseEmptyTab: true,
|
|
236
|
+
});
|
|
237
|
+
expect(onClose).toHaveBeenCalledTimes(1);
|
|
238
|
+
});
|
|
239
|
+
|
|
216
240
|
it("renders the error affordance and a Try again control for builder-error, without a Connect Builder control", async () => {
|
|
217
241
|
startWorkspaceAppCreationResponse.result = {
|
|
218
242
|
mode: "builder-unavailable",
|
|
@@ -269,6 +269,16 @@ export function CreateAppFlow({
|
|
|
269
269
|
if (result?.mode === "builder") {
|
|
270
270
|
setBranchUrl(result?.url || null);
|
|
271
271
|
setStatusMessage("Builder branch created.");
|
|
272
|
+
} else if (result?.mode === "local-agent") {
|
|
273
|
+
sendToAgentChat({
|
|
274
|
+
message: result.prompt ?? message,
|
|
275
|
+
submit: true,
|
|
276
|
+
type: "code",
|
|
277
|
+
newTab: true,
|
|
278
|
+
reuseEmptyTab: true,
|
|
279
|
+
});
|
|
280
|
+
setStatusMessage("Sent to the local agent.");
|
|
281
|
+
onClose?.();
|
|
272
282
|
} else {
|
|
273
283
|
setStatusMessage(
|
|
274
284
|
result?.message ||
|
|
@@ -142,6 +142,7 @@ describe("DispatchControlPlane", () => {
|
|
|
142
142
|
selectedEffort: "medium",
|
|
143
143
|
selectedEngine: "",
|
|
144
144
|
selectedModel: "auto",
|
|
145
|
+
rootClassName: "bg-card",
|
|
145
146
|
});
|
|
146
147
|
|
|
147
148
|
await act(async () => {
|
|
@@ -230,7 +231,9 @@ describe("DispatchControlPlane", () => {
|
|
|
230
231
|
id: "clips",
|
|
231
232
|
name: "Clips",
|
|
232
233
|
description: "Record and share",
|
|
233
|
-
url: "https://clips.agent-native.com",
|
|
234
|
+
url: "https://clips.agent-native.com/share/WrA8ZQ3oxa2T?ref=clip_share",
|
|
235
|
+
homeUrl: "https://clips.agent-native.com",
|
|
236
|
+
source: "builtin",
|
|
234
237
|
},
|
|
235
238
|
{
|
|
236
239
|
id: "onboarding",
|
|
@@ -281,5 +284,10 @@ describe("DispatchControlPlane", () => {
|
|
|
281
284
|
anchor.getAttribute("href")?.includes("onboarding"),
|
|
282
285
|
),
|
|
283
286
|
).toHaveLength(1);
|
|
287
|
+
const clipsHref = Array.from(container.querySelectorAll("a"))
|
|
288
|
+
.map((anchor) => anchor.getAttribute("href"))
|
|
289
|
+
.find((href) => href?.includes("clips.agent-native.com"));
|
|
290
|
+
expect(clipsHref).toContain("https://clips.agent-native.com");
|
|
291
|
+
expect(clipsHref).not.toContain("/share/");
|
|
284
292
|
});
|
|
285
293
|
});
|