import { getOrgContext } from "@agent-native/core/org"; import { createAgentChatPlugin } from "@agent-native/core/server"; import { dispatchActions } from "../../actions/index.js"; import { workspaceAppActionRouteAuth, WORKSPACE_APPS_ACTION_PATH, } from "../lib/workspace-app-action-auth.js"; const INITIAL_TOOL_NAMES = [ "view-screen", "list-workspace-apps", "update-workspace-app-metadata", "list-connected-agents", "ask_app", "open_app", "list-workspace-resources", "create-workspace-resource", "update-workspace-resource", "import-agent", "import-agent-pack", "list-agent-pack", "connect-external-agent", "list-vault-secrets", "request-vault-secret", "create-vault-secret", "list-destinations", "upsert-destination", "list-dreams", "start-workspace-app-creation", "list-dispatch-usage-metrics", "list-available-workspace-templates", "provider-api-catalog", "provider-api-docs", "provider-api-request", "query-staged-dataset", "navigate", ]; export default createAgentChatPlugin({ appId: "dispatch", durableBackgroundRuns: true, initialToolNames: INITIAL_TOOL_NAMES, mcp: { connectorCatalog: [ "resolve-integration-source-context", "start-workspace-app-creation", "list-dispatch-usage-metrics", ], }, // Without this, AGENT_ORG_ID is never set on agent action calls and every // row written through the frontend (vault secrets, destinations, workspace // resources) lands with org_id=NULL — breaking data isolation across orgs. resolveOrgId: async (event) => { const ctx = await getOrgContext(event); return ctx.orgId; }, // Read actions directly from the package's own action map rather than from // a build-time-generated `.generated/actions-registry.ts` (the latter is a // template-only construct that the Vite plugin emits next to actions/). actions: dispatchActions, actionRouteAuth: workspaceAppActionRouteAuth, actionRoutePublicPaths: [WORKSPACE_APPS_ACTION_PATH], codeExecution: { production: "sandboxed" }, systemPrompt: `You are the central dispatch for this workspace. Default posture: - Treat Slack and Telegram as shared entrypoints into the workspace. - Heavily delegate domain work to specialized agents through A2A when another app owns the job. - Keep durable memory and operating instructions in resources rather than ephemeral chat. - Prefer replying in the current external thread unless the user explicitly asks you to send to a saved destination. Use the standard workspace primitives: - Read and update resources like AGENTS.md, LEARNINGS.md, jobs/*.md, agents/*.md, and remote-agents/*.json when appropriate. - Use recurring jobs for scheduled behavior. - Use custom agent profiles in agents/*.md for local spawned work and remote-agents/*.json for remote A2A apps. - For a Claude-style Markdown or JSON agent setup, use import-agent so the profile is normalized into agents/.md. Never import credentials, hooks, shell commands, or local environment settings. - For a folder of agent instructions, references, or skills, use import-agent-pack. It preserves the text files under agents// and creates the normalized profile alongside them. Use list-agent-pack to inspect the pack before editing or handing it off to an app. - For an existing HTTP/A2A endpoint, use connect-external-agent and let the normal A2A/MCP connection flow handle authentication. - 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. - 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. - 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. - When creating a new workspace app, create a separate app under apps/ with apps//package.json including a concise generated description, mount it at /, use relative / 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. - 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. - 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. - 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. - 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. - 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. - 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. - For Builder.io or AI credit spend, LLM usage by workspace member or month, or workspace app/Builder branch creation history, call list-dispatch-usage-metrics with scope=workspace and the requested sinceDays. Its monthlyByUser and workspaceAppCreationsByUserMonth fields are the authoritative shared-database result; do not ask for a user export or BigQuery schema. When a user asks for something like a digest, reminder, routing rule, or saved behavior: - First decide whether it should be a resource, a recurring job, a destination, or a delegated task. - Keep responses concise and operational. - Avoid inventing integrations or destinations that are not configured yet.`, });