@agent-native/dispatch 0.2.3 → 0.2.5
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/README.md +4 -4
- package/dist/actions/start-workspace-app-creation.js +1 -1
- package/dist/actions/start-workspace-app-creation.js.map +1 -1
- package/dist/components/agents-panel.d.ts.map +1 -1
- package/dist/components/agents-panel.js +68 -4
- package/dist/components/agents-panel.js.map +1 -1
- package/dist/components/create-app-popover.d.ts.map +1 -1
- package/dist/components/create-app-popover.js +2 -0
- package/dist/components/create-app-popover.js.map +1 -1
- package/dist/lib/overview-chat.d.ts +2 -0
- package/dist/lib/overview-chat.d.ts.map +1 -0
- package/dist/lib/overview-chat.js +20 -0
- package/dist/lib/overview-chat.js.map +1 -0
- package/dist/routes/pages/overview.d.ts.map +1 -1
- package/dist/routes/pages/overview.js +4 -8
- package/dist/routes/pages/overview.js.map +1 -1
- package/dist/server/lib/app-creation-store.d.ts.map +1 -1
- package/dist/server/lib/app-creation-store.js +5 -2
- package/dist/server/lib/app-creation-store.js.map +1 -1
- package/dist/server/plugins/integrations.d.ts.map +1 -1
- package/dist/server/plugins/integrations.js +2 -1
- package/dist/server/plugins/integrations.js.map +1 -1
- package/package.json +2 -2
- package/src/actions/start-workspace-app-creation.ts +1 -1
- package/src/components/agents-panel.tsx +117 -22
- package/src/components/create-app-popover.tsx +2 -0
- package/src/lib/overview-chat.spec.ts +48 -0
- package/src/lib/overview-chat.ts +24 -0
- package/src/routes/pages/overview.tsx +3 -8
- package/src/server/lib/app-creation-store.ts +5 -1
- package/src/server/plugins/integrations.ts +2 -1
|
@@ -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;AACrE,OAAO,EACL,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEzD,MAAM,kCAAkC,GAAG
|
|
1
|
+
{"version":3,"file":"integrations.js","sourceRoot":"","sources":["../../../src/server/plugins/integrations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,EACL,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEzD,MAAM,kCAAkC,GAAG;;;;;;;;;;;;;;;;;;;4FAmBiD,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,eAAe;QACxB,YAAY,EAAE,oBAAoB;QAClC,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\";\nimport {\n beforeDispatchProcess,\n resolveDispatchOwner,\n} from \"../lib/dispatch-integrations.js\";\nimport { getDispatchConfig } from \"../index.js\";\nimport { dispatchActions } from \"../../actions/index.js\";\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), videos (Remotion compositions), forms (form builder), clips (screen recordings), and design (visual designs).\n- Use list-connected-agents to see what agents are available before assuming a request must be handled locally.\n- When asked whether workspace apps expose agent cards or A2A endpoints, call list-workspace-apps with includeAgentCards=true. Without that probe, missing agent-card fields mean unchecked, not unavailable.\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, videos, etc., delegate via call-agent — do not re-implement the domain logic in dispatch.\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 the user explicitly asks for a new app or workspace app, call start-workspace-app-creation with their prompt. Do not satisfy a new-app request by adding a route, page, component, or file inside apps/starter 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>, and making it appear in the workspace apps list. 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. If it returns mode \"coming-soon\" or \"builder-unavailable\", explain the missing Builder setup and ask them to connect/configure Builder.\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: dispatchActions,\n resolveOwner: resolveDispatchOwner,\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.2.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"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.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
"typescript": "^6.0.3",
|
|
98
98
|
"vite": "8.0.3",
|
|
99
99
|
"vitest": "^4.1.5",
|
|
100
|
-
"@agent-native/core": "0.
|
|
100
|
+
"@agent-native/core": "0.11.4"
|
|
101
101
|
},
|
|
102
102
|
"scripts": {
|
|
103
103
|
"build": "tsc && tsc-alias --resolve-full-paths",
|
|
@@ -5,7 +5,7 @@ import { startWorkspaceAppCreation } from "../server/lib/app-creation-store.js";
|
|
|
5
5
|
|
|
6
6
|
export default defineAction({
|
|
7
7
|
description:
|
|
8
|
-
"Start creating a new workspace app from Dispatch. In local dev this returns a code-agent prompt; in production it creates a Builder branch when a Builder project is configured.",
|
|
8
|
+
"Start creating a new workspace app from Dispatch when the request truly needs its own app. In local dev this returns a code-agent prompt; in production it creates a Builder branch when a Builder project is configured. The result must be a separate workspace app under apps/<app-id>, not a new route or file in apps/starter.",
|
|
9
9
|
schema: z.object({
|
|
10
10
|
prompt: z.string().min(1).describe("The user's app creation request"),
|
|
11
11
|
appId: z
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useRef, useState } from "react";
|
|
1
|
+
import { useRef, useState, type FormEvent } from "react";
|
|
2
2
|
import { Button } from "@/components/ui/button";
|
|
3
3
|
import { Input } from "@/components/ui/input";
|
|
4
4
|
import {
|
|
@@ -27,6 +27,48 @@ export interface ConnectedAgent {
|
|
|
27
27
|
scope?: "shared" | "personal";
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
type AgentFormErrors = Partial<Record<"name" | "url" | "form", string>>;
|
|
31
|
+
|
|
32
|
+
function slugifyAgentName(value: string): string {
|
|
33
|
+
return value
|
|
34
|
+
.trim()
|
|
35
|
+
.toLowerCase()
|
|
36
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
37
|
+
.replace(/^-+|-+$/g, "");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function validateAgentForm(name: string, url: string): AgentFormErrors {
|
|
41
|
+
const errors: AgentFormErrors = {};
|
|
42
|
+
const trimmedName = name.trim();
|
|
43
|
+
const trimmedUrl = url.trim();
|
|
44
|
+
|
|
45
|
+
if (!trimmedName) {
|
|
46
|
+
errors.name = "Agent name is required.";
|
|
47
|
+
} else if (!slugifyAgentName(trimmedName)) {
|
|
48
|
+
errors.name = "Agent name must include at least one letter or number.";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (!trimmedUrl) {
|
|
52
|
+
errors.url = "Agent endpoint URL is required.";
|
|
53
|
+
} else {
|
|
54
|
+
try {
|
|
55
|
+
const parsed = new URL(trimmedUrl);
|
|
56
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
|
|
57
|
+
errors.url = "Use an http:// or https:// endpoint URL.";
|
|
58
|
+
} else if (!parsed.hostname) {
|
|
59
|
+
errors.url = "Enter a complete endpoint URL with a host.";
|
|
60
|
+
} else if (parsed.username || parsed.password) {
|
|
61
|
+
errors.url = "Do not include credentials in the endpoint URL.";
|
|
62
|
+
}
|
|
63
|
+
} catch {
|
|
64
|
+
errors.url =
|
|
65
|
+
"Enter a valid endpoint URL, such as https://app.example.com.";
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return errors;
|
|
70
|
+
}
|
|
71
|
+
|
|
30
72
|
export function AgentsPanel({
|
|
31
73
|
agents,
|
|
32
74
|
onRefresh,
|
|
@@ -38,6 +80,7 @@ export function AgentsPanel({
|
|
|
38
80
|
const [url, setUrl] = useState("");
|
|
39
81
|
const [description, setDescription] = useState("");
|
|
40
82
|
const [saving, setSaving] = useState(false);
|
|
83
|
+
const [errors, setErrors] = useState<AgentFormErrors>({});
|
|
41
84
|
const nameRef = useRef<HTMLInputElement>(null);
|
|
42
85
|
|
|
43
86
|
const customAgents = agents.filter((agent) => agent.source === "custom");
|
|
@@ -46,12 +89,17 @@ export function AgentsPanel({
|
|
|
46
89
|
);
|
|
47
90
|
const builtinAgents = agents.filter((agent) => agent.source === "builtin");
|
|
48
91
|
|
|
49
|
-
const handleAdd = async () => {
|
|
92
|
+
const handleAdd = async (event?: FormEvent<HTMLFormElement>) => {
|
|
93
|
+
event?.preventDefault();
|
|
50
94
|
const trimmedName = name.trim();
|
|
51
95
|
const trimmedUrl = url.trim();
|
|
52
|
-
|
|
96
|
+
const nextErrors = validateAgentForm(trimmedName, trimmedUrl);
|
|
97
|
+
if (Object.keys(nextErrors).length > 0) {
|
|
98
|
+
setErrors(nextErrors);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
53
101
|
|
|
54
|
-
const id = trimmedName
|
|
102
|
+
const id = slugifyAgentName(trimmedName);
|
|
55
103
|
const agentJson = JSON.stringify(
|
|
56
104
|
{
|
|
57
105
|
id,
|
|
@@ -79,9 +127,21 @@ export function AgentsPanel({
|
|
|
79
127
|
setName("");
|
|
80
128
|
setUrl("");
|
|
81
129
|
setDescription("");
|
|
130
|
+
setErrors({});
|
|
82
131
|
onRefresh();
|
|
83
132
|
nameRef.current?.focus();
|
|
133
|
+
} else {
|
|
134
|
+
setErrors({
|
|
135
|
+
form: `Could not add agent. Request failed with ${res.status}.`,
|
|
136
|
+
});
|
|
84
137
|
}
|
|
138
|
+
} catch (error) {
|
|
139
|
+
setErrors({
|
|
140
|
+
form:
|
|
141
|
+
error instanceof Error
|
|
142
|
+
? error.message
|
|
143
|
+
: "Could not add agent. Please try again.",
|
|
144
|
+
});
|
|
85
145
|
} finally {
|
|
86
146
|
setSaving(false);
|
|
87
147
|
}
|
|
@@ -230,31 +290,66 @@ export function AgentsPanel({
|
|
|
230
290
|
<p className="mt-1 text-xs leading-relaxed text-muted-foreground">
|
|
231
291
|
Add another A2A-compatible app by saving its agent endpoint here.
|
|
232
292
|
</p>
|
|
233
|
-
<
|
|
234
|
-
<
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
293
|
+
<form className="mt-4 space-y-3" onSubmit={handleAdd} noValidate>
|
|
294
|
+
<div className="space-y-1.5">
|
|
295
|
+
<Input
|
|
296
|
+
ref={nameRef}
|
|
297
|
+
value={name}
|
|
298
|
+
onChange={(event) => {
|
|
299
|
+
setName(event.target.value);
|
|
300
|
+
setErrors((current) => ({ ...current, name: undefined }));
|
|
301
|
+
}}
|
|
302
|
+
placeholder="Name"
|
|
303
|
+
aria-invalid={Boolean(errors.name)}
|
|
304
|
+
aria-describedby={
|
|
305
|
+
errors.name ? "external-agent-name-error" : undefined
|
|
306
|
+
}
|
|
307
|
+
/>
|
|
308
|
+
{errors.name ? (
|
|
309
|
+
<p
|
|
310
|
+
id="external-agent-name-error"
|
|
311
|
+
className="text-xs font-medium text-destructive"
|
|
312
|
+
>
|
|
313
|
+
{errors.name}
|
|
314
|
+
</p>
|
|
315
|
+
) : null}
|
|
316
|
+
</div>
|
|
317
|
+
<div className="space-y-1.5">
|
|
318
|
+
<Input
|
|
319
|
+
value={url}
|
|
320
|
+
onChange={(event) => {
|
|
321
|
+
setUrl(event.target.value);
|
|
322
|
+
setErrors((current) => ({ ...current, url: undefined }));
|
|
323
|
+
}}
|
|
324
|
+
placeholder="https://app.example.com"
|
|
325
|
+
aria-invalid={Boolean(errors.url)}
|
|
326
|
+
aria-describedby={
|
|
327
|
+
errors.url ? "external-agent-url-error" : undefined
|
|
328
|
+
}
|
|
329
|
+
/>
|
|
330
|
+
{errors.url ? (
|
|
331
|
+
<p
|
|
332
|
+
id="external-agent-url-error"
|
|
333
|
+
className="text-xs font-medium text-destructive"
|
|
334
|
+
>
|
|
335
|
+
{errors.url}
|
|
336
|
+
</p>
|
|
337
|
+
) : null}
|
|
338
|
+
</div>
|
|
245
339
|
<Input
|
|
246
340
|
value={description}
|
|
247
341
|
onChange={(event) => setDescription(event.target.value)}
|
|
248
342
|
placeholder="Description (optional)"
|
|
249
343
|
/>
|
|
250
|
-
|
|
251
|
-
className="
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
344
|
+
{errors.form ? (
|
|
345
|
+
<p className="text-xs font-medium text-destructive">
|
|
346
|
+
{errors.form}
|
|
347
|
+
</p>
|
|
348
|
+
) : null}
|
|
349
|
+
<Button type="submit" className="w-full" disabled={saving}>
|
|
255
350
|
{saving ? "Saving..." : "Add agent"}
|
|
256
351
|
</Button>
|
|
257
|
-
</
|
|
352
|
+
</form>
|
|
258
353
|
</div>
|
|
259
354
|
</div>
|
|
260
355
|
</section>
|
|
@@ -73,6 +73,7 @@ function buildAppCreationPrompt(input: {
|
|
|
73
73
|
|
|
74
74
|
return [
|
|
75
75
|
`Create a new agent-native app in this workspace.`,
|
|
76
|
+
`This is a new workspace app request, not a feature request for the current app.`,
|
|
76
77
|
``,
|
|
77
78
|
`Suggested app name: ${input.appId} (you may adjust the slug if it conflicts)`,
|
|
78
79
|
`User prompt: ${input.prompt.trim()}`,
|
|
@@ -80,6 +81,7 @@ function buildAppCreationPrompt(input: {
|
|
|
80
81
|
``,
|
|
81
82
|
`Pick a starter template that fits the user's prompt — analytics, calendar, content, design, dispatch, forms, mail, slides, videos, clips, or starter when none of the others fit.`,
|
|
82
83
|
`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.`,
|
|
84
|
+
`Do not satisfy this by adding a route, page, component, or file inside apps/starter or another existing app unless the user explicitly asks to modify that existing app.`,
|
|
83
85
|
keyList
|
|
84
86
|
? `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.`
|
|
85
87
|
: `Do not grant any Dispatch vault keys unless the user asks later.`,
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
|
|
3
|
+
const frameState = vi.hoisted(() => ({ inBuilderFrame: false }));
|
|
4
|
+
const sendToAgentChatMock = vi.hoisted(() => vi.fn(() => "chat-tab"));
|
|
5
|
+
|
|
6
|
+
vi.mock("@agent-native/core/client", () => ({
|
|
7
|
+
isInBuilderFrame: () => frameState.inBuilderFrame,
|
|
8
|
+
sendToAgentChat: sendToAgentChatMock,
|
|
9
|
+
}));
|
|
10
|
+
|
|
11
|
+
const { submitOverviewPrompt } = await import("./overview-chat.js");
|
|
12
|
+
|
|
13
|
+
describe("submitOverviewPrompt", () => {
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
frameState.inBuilderFrame = false;
|
|
16
|
+
sendToAgentChatMock.mockClear();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("sends overview prompts to a new local agent tab outside Builder", () => {
|
|
20
|
+
const tabId = submitOverviewPrompt(" build a metrics app ", "auto");
|
|
21
|
+
|
|
22
|
+
expect(tabId).toBe("chat-tab");
|
|
23
|
+
expect(sendToAgentChatMock).toHaveBeenCalledWith({
|
|
24
|
+
message: "build a metrics app",
|
|
25
|
+
submit: true,
|
|
26
|
+
newTab: true,
|
|
27
|
+
model: "auto",
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("routes overview prompts to Builder chat inside Builder", () => {
|
|
32
|
+
frameState.inBuilderFrame = true;
|
|
33
|
+
|
|
34
|
+
const tabId = submitOverviewPrompt("ship the onboarding flow", "auto");
|
|
35
|
+
|
|
36
|
+
expect(tabId).toBe("chat-tab");
|
|
37
|
+
expect(sendToAgentChatMock).toHaveBeenCalledWith({
|
|
38
|
+
message: "ship the onboarding flow",
|
|
39
|
+
submit: true,
|
|
40
|
+
type: "code",
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("ignores empty prompts", () => {
|
|
45
|
+
expect(submitOverviewPrompt(" ", "auto")).toBeNull();
|
|
46
|
+
expect(sendToAgentChatMock).not.toHaveBeenCalled();
|
|
47
|
+
});
|
|
48
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { isInBuilderFrame, sendToAgentChat } from "@agent-native/core/client";
|
|
2
|
+
|
|
3
|
+
export function submitOverviewPrompt(
|
|
4
|
+
message: string,
|
|
5
|
+
selectedModel?: string | null,
|
|
6
|
+
): string | null {
|
|
7
|
+
const trimmed = message.trim();
|
|
8
|
+
if (!trimmed) return null;
|
|
9
|
+
|
|
10
|
+
if (isInBuilderFrame()) {
|
|
11
|
+
return sendToAgentChat({
|
|
12
|
+
message: trimmed,
|
|
13
|
+
submit: true,
|
|
14
|
+
type: "code",
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return sendToAgentChat({
|
|
19
|
+
message: trimmed,
|
|
20
|
+
submit: true,
|
|
21
|
+
newTab: true,
|
|
22
|
+
model: selectedModel || undefined,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
@@ -2,7 +2,6 @@ import { useEffect, useMemo, useState } from "react";
|
|
|
2
2
|
import { Link } from "react-router";
|
|
3
3
|
import {
|
|
4
4
|
PromptComposer,
|
|
5
|
-
sendToAgentChat,
|
|
6
5
|
useActionQuery,
|
|
7
6
|
useChatModels,
|
|
8
7
|
agentNativePath,
|
|
@@ -34,6 +33,7 @@ import {
|
|
|
34
33
|
TooltipContent,
|
|
35
34
|
TooltipTrigger,
|
|
36
35
|
} from "@/components/ui/tooltip";
|
|
36
|
+
import { submitOverviewPrompt } from "@/lib/overview-chat";
|
|
37
37
|
|
|
38
38
|
interface IntegrationStatus {
|
|
39
39
|
platform: string;
|
|
@@ -99,17 +99,12 @@ function HomeChatPanel() {
|
|
|
99
99
|
const { selectedModel } = useChatModels();
|
|
100
100
|
|
|
101
101
|
const send = (message: string) => {
|
|
102
|
-
|
|
103
|
-
message,
|
|
104
|
-
submit: true,
|
|
105
|
-
newTab: true,
|
|
106
|
-
model: selectedModel || undefined,
|
|
107
|
-
});
|
|
102
|
+
submitOverviewPrompt(message, selectedModel);
|
|
108
103
|
};
|
|
109
104
|
|
|
110
105
|
return (
|
|
111
106
|
<section className="px-2 py-6 sm:py-10">
|
|
112
|
-
<div className="mx-auto w-full max-w-2xl space-y-
|
|
107
|
+
<div className="mx-auto w-full max-w-2xl space-y-8">
|
|
113
108
|
<h1 className="text-center text-2xl font-semibold tracking-tight text-foreground sm:text-3xl">
|
|
114
109
|
What should we do next?
|
|
115
110
|
</h1>
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
getBuilderBranchProjectId,
|
|
7
7
|
getRequestContext,
|
|
8
8
|
isIntegrationCallerRequest,
|
|
9
|
+
resolveBuilderBranchProjectId,
|
|
9
10
|
resolveBuilderCredentials,
|
|
10
11
|
runBuilderAgent,
|
|
11
12
|
} from "@agent-native/core/server";
|
|
@@ -596,6 +597,7 @@ export async function listWorkspaceApps(
|
|
|
596
597
|
|
|
597
598
|
export async function getAppCreationSettings(): Promise<AppCreationSettings> {
|
|
598
599
|
const envBuilderProjectId = getEnvBuilderProjectId();
|
|
600
|
+
const resolvedBuilderProjectId = await resolveBuilderBranchProjectId();
|
|
599
601
|
const raw = await readSettingsRecord();
|
|
600
602
|
const savedBuilderProjectId =
|
|
601
603
|
typeof raw?.builderProjectId === "string" && raw.builderProjectId.trim()
|
|
@@ -605,7 +607,9 @@ export async function getAppCreationSettings(): Promise<AppCreationSettings> {
|
|
|
605
607
|
const enableBuilder =
|
|
606
608
|
process.env.ENABLE_BUILDER === "true" || process.env.ENABLE_BUILDER === "1";
|
|
607
609
|
const effectiveBuilderProjectId =
|
|
608
|
-
builderProjectId ||
|
|
610
|
+
builderProjectId ||
|
|
611
|
+
resolvedBuilderProjectId ||
|
|
612
|
+
(enableBuilder ? getBuilderBranchProjectId() : null);
|
|
609
613
|
|
|
610
614
|
return {
|
|
611
615
|
builderProjectId: effectiveBuilderProjectId,
|
|
@@ -20,7 +20,8 @@ When a user asks for something:
|
|
|
20
20
|
- If it belongs to analytics, content, slides, videos, etc., delegate via call-agent — do not re-implement the domain logic in dispatch.
|
|
21
21
|
- 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.
|
|
22
22
|
- 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.
|
|
23
|
-
- If the user asks to create, build, make, scaffold, or generate
|
|
23
|
+
- 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.
|
|
24
|
+
- If the user explicitly asks for a new app or workspace app, call start-workspace-app-creation with their prompt. Do not satisfy a new-app request by adding a route, page, component, or file inside apps/starter 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>, and making it appear in the workspace apps list. 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. If it returns mode "coming-soon" or "builder-unavailable", explain the missing Builder setup and ask them to connect/configure Builder.
|
|
24
25
|
- For digests, reminders, or saved behavior, prefer recurring jobs, resources, or destinations over chat replies.
|
|
25
26
|
- Keep responses concise and operational — messaging platforms have character limits.
|
|
26
27
|
- Use markdown sparingly (bold and lists are fine, avoid complex formatting).
|