@agent-native/dispatch 0.20.3 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/actions/provider-api-register.d.ts +6 -6
- package/dist/actions/start-workspace-app-creation.js +1 -1
- package/dist/actions/start-workspace-app-creation.js.map +1 -1
- package/dist/components/automation-run-history-dialog.d.ts.map +1 -1
- package/dist/components/automation-run-history-dialog.js +1 -0
- package/dist/components/automation-run-history-dialog.js.map +1 -1
- package/dist/components/create-app-popover.d.ts.map +1 -1
- package/dist/components/create-app-popover.js +5 -71
- package/dist/components/create-app-popover.js.map +1 -1
- package/dist/components/layout/Layout.d.ts +23 -1
- package/dist/components/layout/Layout.d.ts.map +1 -1
- package/dist/components/layout/Layout.js +702 -15
- package/dist/components/layout/Layout.js.map +1 -1
- package/dist/lib/automation-display.d.ts +6 -0
- package/dist/lib/automation-display.d.ts.map +1 -1
- package/dist/lib/automation-display.js +8 -0
- package/dist/lib/automation-display.js.map +1 -1
- package/dist/lib/automations.d.ts +1 -0
- package/dist/lib/automations.d.ts.map +1 -1
- package/dist/lib/automations.js.map +1 -1
- package/dist/lib/workspace-apps.d.ts +6 -0
- package/dist/lib/workspace-apps.d.ts.map +1 -1
- package/dist/lib/workspace-apps.js +21 -0
- package/dist/lib/workspace-apps.js.map +1 -1
- package/dist/routes/pages/automations.d.ts.map +1 -1
- package/dist/routes/pages/automations.js +16 -6
- package/dist/routes/pages/automations.js.map +1 -1
- package/dist/routes/pages/settings.d.ts.map +1 -1
- package/dist/routes/pages/settings.js +21 -1
- package/dist/routes/pages/settings.js.map +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/lib/app-creation-store.d.ts.map +1 -1
- package/dist/server/lib/app-creation-store.js +13 -0
- package/dist/server/lib/app-creation-store.js.map +1 -1
- package/dist/server/plugins/agent-chat.js +1 -0
- package/dist/server/plugins/agent-chat.js.map +1 -1
- package/dist/server/plugins/db.d.ts +1 -0
- package/dist/server/plugins/db.d.ts.map +1 -1
- package/dist/server/plugins/db.js +1 -1
- package/dist/server/plugins/db.js.map +1 -1
- package/dist/server/plugins/integrations.js +1 -1
- package/dist/server/plugins/integrations.js.map +1 -1
- package/package.json +3 -3
- package/src/actions/start-workspace-app-creation.ts +1 -1
- package/src/components/automation-run-history-dialog.tsx +1 -0
- package/src/components/create-app-popover.spec.tsx +22 -0
- package/src/components/create-app-popover.tsx +30 -94
- package/src/components/layout/Layout.spec.tsx +16 -1
- package/src/components/layout/Layout.tsx +1356 -34
- package/src/lib/automation-display.spec.ts +7 -0
- package/src/lib/automation-display.ts +11 -0
- package/src/lib/automations.ts +1 -0
- package/src/lib/workspace-apps.ts +24 -0
- package/src/routes/pages/automations.tsx +45 -6
- package/src/routes/pages/settings.tsx +72 -0
- package/src/server/index.ts +4 -1
- package/src/server/lib/app-creation-store.spec.ts +13 -0
- package/src/server/lib/app-creation-store.ts +13 -0
- package/src/server/lib/mcp-gateway.spec.ts +18 -0
- package/src/server/plugins/agent-chat.ts +1 -0
- package/src/server/plugins/db.ts +1 -1
- package/src/server/plugins/integrations.ts +1 -1
- package/src/styles/dispatch.css +29 -0
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
automationStatus,
|
|
8
8
|
automationTarget,
|
|
9
9
|
automationTroubleshootPath,
|
|
10
|
+
belongsToDispatch,
|
|
10
11
|
sortAutomations,
|
|
11
12
|
} from "./automation-display.js";
|
|
12
13
|
import type { DispatchAutomationItem } from "./automations.js";
|
|
@@ -34,6 +35,12 @@ describe("automation-display", () => {
|
|
|
34
35
|
).toBe("/admin/thread-debug?query=coach+onboarding+reminder");
|
|
35
36
|
});
|
|
36
37
|
|
|
38
|
+
it("keeps Dispatch-owned and legacy automations in the default view", () => {
|
|
39
|
+
expect(belongsToDispatch({})).toBe(true);
|
|
40
|
+
expect(belongsToDispatch({ appId: "dispatch" })).toBe(true);
|
|
41
|
+
expect(belongsToDispatch({ appId: "mail" })).toBe(false);
|
|
42
|
+
});
|
|
43
|
+
|
|
37
44
|
it("prefers event names, then schedule descriptions", () => {
|
|
38
45
|
expect(
|
|
39
46
|
automationTarget(
|
|
@@ -13,6 +13,17 @@ export function automationIdentity(
|
|
|
13
13
|
return `${item.owner}:${item.path}`;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Dispatch is the workspace-wide surface. Keep legacy resources without an
|
|
18
|
+
* app owner visible in its default view while making an explicit app owner
|
|
19
|
+
* opt into the global filter.
|
|
20
|
+
*/
|
|
21
|
+
export function belongsToDispatch(
|
|
22
|
+
item: Pick<DispatchAutomationItem, "appId">,
|
|
23
|
+
): boolean {
|
|
24
|
+
return !item.appId || item.appId === "dispatch";
|
|
25
|
+
}
|
|
26
|
+
|
|
16
27
|
export function automationTroubleshootPath(
|
|
17
28
|
item: Pick<DispatchAutomationItem, "name">,
|
|
18
29
|
): string {
|
package/src/lib/automations.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { CHAT_FIRST_DEFAULT_APP_IDS } from "@agent-native/core/client/chat-first";
|
|
1
2
|
import { withBuilderUtmTrackingParams } from "@agent-native/core/shared/builder-link-tracking";
|
|
2
3
|
|
|
3
4
|
export interface WorkspaceAppSummary {
|
|
@@ -45,3 +46,26 @@ export function workspaceAppHref(app: WorkspaceAppSummary): string | null {
|
|
|
45
46
|
export function isPendingBuilderHref(app: WorkspaceAppSummary): boolean {
|
|
46
47
|
return app.status === "pending" && !!app.builderUrl;
|
|
47
48
|
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Keep the chat-first rail useful before a workspace manifest is populated.
|
|
52
|
+
* Mounted workspace rows still win, so custom names and routes remain the
|
|
53
|
+
* source of truth once an app exists in the workspace.
|
|
54
|
+
*/
|
|
55
|
+
export function mergeChatFirstWorkspaceApps(
|
|
56
|
+
apps: readonly WorkspaceAppSummary[] | undefined,
|
|
57
|
+
): WorkspaceAppSummary[] {
|
|
58
|
+
const merged = new Map<string, WorkspaceAppSummary>();
|
|
59
|
+
for (const id of CHAT_FIRST_DEFAULT_APP_IDS) {
|
|
60
|
+
merged.set(id, {
|
|
61
|
+
id,
|
|
62
|
+
name: id.charAt(0).toUpperCase() + id.slice(1),
|
|
63
|
+
path: `/${id}`,
|
|
64
|
+
url: null,
|
|
65
|
+
status: "ready",
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
for (const app of apps ?? []) merged.set(app.id, app);
|
|
69
|
+
|
|
70
|
+
return [...merged.values()];
|
|
71
|
+
}
|
|
@@ -21,6 +21,13 @@ import {
|
|
|
21
21
|
PopoverContent,
|
|
22
22
|
PopoverTrigger,
|
|
23
23
|
} from "../../components/ui/popover";
|
|
24
|
+
import {
|
|
25
|
+
Select,
|
|
26
|
+
SelectContent,
|
|
27
|
+
SelectItem,
|
|
28
|
+
SelectTrigger,
|
|
29
|
+
SelectValue,
|
|
30
|
+
} from "../../components/ui/select";
|
|
24
31
|
import { Skeleton } from "../../components/ui/skeleton";
|
|
25
32
|
import { Switch } from "../../components/ui/switch";
|
|
26
33
|
import {
|
|
@@ -32,6 +39,7 @@ import {
|
|
|
32
39
|
automationStatus,
|
|
33
40
|
automationTarget,
|
|
34
41
|
automationTroubleshootPath,
|
|
42
|
+
belongsToDispatch,
|
|
35
43
|
sortAutomations,
|
|
36
44
|
type AutomationStatusTone,
|
|
37
45
|
} from "../../lib/automation-display";
|
|
@@ -179,14 +187,25 @@ function CreateAutomationButton() {
|
|
|
179
187
|
}
|
|
180
188
|
|
|
181
189
|
export default function AutomationsRoute() {
|
|
190
|
+
const [view, setView] = useState<"dispatch" | "all">("dispatch");
|
|
182
191
|
const [detailsTarget, setDetailsTarget] =
|
|
183
192
|
useState<DispatchAutomationItem | null>(null);
|
|
184
193
|
const automationsQuery = useAutomations();
|
|
185
194
|
const toggleAutomation = useToggleAutomation();
|
|
186
195
|
const automations = automationsQuery.data ?? [];
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
|
|
196
|
+
const visibleAutomations = useMemo(
|
|
197
|
+
() =>
|
|
198
|
+
view === "all"
|
|
199
|
+
? automations
|
|
200
|
+
: automations.filter((item) => belongsToDispatch(item)),
|
|
201
|
+
[automations, view],
|
|
202
|
+
);
|
|
203
|
+
const ordered = useMemo(
|
|
204
|
+
() => sortAutomations(visibleAutomations),
|
|
205
|
+
[visibleAutomations],
|
|
206
|
+
);
|
|
207
|
+
const enabledCount = visibleAutomations.filter((item) => item.enabled).length;
|
|
208
|
+
const errorCount = visibleAutomations.filter(
|
|
190
209
|
(item) =>
|
|
191
210
|
item.enabled &&
|
|
192
211
|
(item.lastStatus === "error" || item.lastStatus === "skipped"),
|
|
@@ -211,7 +230,26 @@ export default function AutomationsRoute() {
|
|
|
211
230
|
{errorCount > 0 ? ` · ${errorCount} errors` : ""}
|
|
212
231
|
</span>
|
|
213
232
|
</div>
|
|
214
|
-
<
|
|
233
|
+
<div className="flex flex-wrap items-center justify-end gap-2">
|
|
234
|
+
<Select
|
|
235
|
+
value={view}
|
|
236
|
+
onValueChange={(value) => {
|
|
237
|
+
if (value === "dispatch" || value === "all") setView(value);
|
|
238
|
+
}}
|
|
239
|
+
>
|
|
240
|
+
<SelectTrigger
|
|
241
|
+
className="h-8 w-[10.5rem] text-xs"
|
|
242
|
+
aria-label="Automation view"
|
|
243
|
+
>
|
|
244
|
+
<SelectValue />
|
|
245
|
+
</SelectTrigger>
|
|
246
|
+
<SelectContent>
|
|
247
|
+
<SelectItem value="dispatch">Dispatch automations</SelectItem>
|
|
248
|
+
<SelectItem value="all">All apps</SelectItem>
|
|
249
|
+
</SelectContent>
|
|
250
|
+
</Select>
|
|
251
|
+
<CreateAutomationButton />
|
|
252
|
+
</div>
|
|
215
253
|
</div>
|
|
216
254
|
|
|
217
255
|
<div className="divide-y rounded-lg bg-card">
|
|
@@ -306,8 +344,9 @@ export default function AutomationsRoute() {
|
|
|
306
344
|
})
|
|
307
345
|
) : (
|
|
308
346
|
<div className="px-4 py-10 text-center text-sm text-muted-foreground">
|
|
309
|
-
|
|
310
|
-
|
|
347
|
+
{view === "all"
|
|
348
|
+
? "No automations yet. Create one here, or ask Dispatch to set up a scheduled or event-triggered job."
|
|
349
|
+
: "No Dispatch automations yet. Switch to All apps to inspect workspace automations."}
|
|
311
350
|
</div>
|
|
312
351
|
)}
|
|
313
352
|
</div>
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CHAT_FIRST_MODE_CHANGED_EVENT,
|
|
3
|
+
readChatFirstModeState,
|
|
4
|
+
writeChatFirstMode,
|
|
5
|
+
} from "@agent-native/core/client/agent-chat";
|
|
1
6
|
import { ChangelogSettingsCard } from "@agent-native/core/client/changelog";
|
|
2
7
|
import { LanguagePicker, useT } from "@agent-native/core/client/i18n";
|
|
3
8
|
import { TeamPage } from "@agent-native/core/client/org";
|
|
@@ -5,6 +10,7 @@ import {
|
|
|
5
10
|
SettingsTabsPage,
|
|
6
11
|
useAgentSettingsTabs,
|
|
7
12
|
} from "@agent-native/core/client/settings";
|
|
13
|
+
import { useState } from "react";
|
|
8
14
|
import { Link } from "react-router";
|
|
9
15
|
|
|
10
16
|
import changelog from "../../../CHANGELOG.md?raw";
|
|
@@ -18,6 +24,7 @@ import {
|
|
|
18
24
|
CardTitle,
|
|
19
25
|
} from "../../components/ui/card";
|
|
20
26
|
import { Label } from "../../components/ui/label";
|
|
27
|
+
import { Switch } from "../../components/ui/switch";
|
|
21
28
|
|
|
22
29
|
export function meta() {
|
|
23
30
|
return [{ title: "Settings - Dispatch" }];
|
|
@@ -26,6 +33,34 @@ export function meta() {
|
|
|
26
33
|
export default function SettingsRoute() {
|
|
27
34
|
const t = useT();
|
|
28
35
|
const agentSettingsTabs = useAgentSettingsTabs();
|
|
36
|
+
const [chatFirstModeState] = useState(() => readChatFirstModeState());
|
|
37
|
+
const [chatFirstMode, setChatFirstMode] = useState(
|
|
38
|
+
() => chatFirstModeState.enabled,
|
|
39
|
+
);
|
|
40
|
+
const [chatFirstStorageNotice, setChatFirstStorageNotice] = useState<
|
|
41
|
+
string | null
|
|
42
|
+
>(
|
|
43
|
+
chatFirstModeState.availability === "unavailable"
|
|
44
|
+
? "This browser is not allowing local preferences, so Chat-first mode cannot be persisted."
|
|
45
|
+
: null,
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
function updateChatFirstMode(enabled: boolean) {
|
|
49
|
+
const result = writeChatFirstMode(enabled);
|
|
50
|
+
if (!result.ok) {
|
|
51
|
+
setChatFirstStorageNotice(
|
|
52
|
+
"This browser blocked local preferences, so Chat-first mode was not changed.",
|
|
53
|
+
);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
setChatFirstStorageNotice(null);
|
|
57
|
+
setChatFirstMode(enabled);
|
|
58
|
+
window.dispatchEvent(
|
|
59
|
+
new CustomEvent(CHAT_FIRST_MODE_CHANGED_EVENT, {
|
|
60
|
+
detail: { enabled },
|
|
61
|
+
}),
|
|
62
|
+
);
|
|
63
|
+
}
|
|
29
64
|
|
|
30
65
|
return (
|
|
31
66
|
<DispatchShell
|
|
@@ -51,6 +86,43 @@ export default function SettingsRoute() {
|
|
|
51
86
|
</CardContent>
|
|
52
87
|
</Card>
|
|
53
88
|
|
|
89
|
+
<Card>
|
|
90
|
+
<CardHeader>
|
|
91
|
+
<CardTitle className="text-base">
|
|
92
|
+
Chat-first workspace
|
|
93
|
+
</CardTitle>
|
|
94
|
+
<CardDescription>
|
|
95
|
+
Keep chats at the center and open workspace apps in a
|
|
96
|
+
contextual pane beside them.
|
|
97
|
+
</CardDescription>
|
|
98
|
+
</CardHeader>
|
|
99
|
+
<CardContent className="flex items-center justify-between gap-4">
|
|
100
|
+
<div className="space-y-1">
|
|
101
|
+
<Label htmlFor="dispatch-chat-first-mode">
|
|
102
|
+
Use chat-first navigation
|
|
103
|
+
</Label>
|
|
104
|
+
<p className="text-sm text-muted-foreground">
|
|
105
|
+
This preference only changes your local Dispatch shell.
|
|
106
|
+
</p>
|
|
107
|
+
<p className="text-sm text-muted-foreground">
|
|
108
|
+
Session watch and follow-up messaging work in this browser
|
|
109
|
+
pane too. Local CLI subscription detection stays in the
|
|
110
|
+
Electron app; Dispatch uses workspace/provider credentials.
|
|
111
|
+
</p>
|
|
112
|
+
</div>
|
|
113
|
+
<Switch
|
|
114
|
+
id="dispatch-chat-first-mode"
|
|
115
|
+
checked={chatFirstMode}
|
|
116
|
+
onCheckedChange={updateChatFirstMode}
|
|
117
|
+
/>
|
|
118
|
+
</CardContent>
|
|
119
|
+
{chatFirstStorageNotice ? (
|
|
120
|
+
<p className="px-6 pb-4 text-sm text-destructive" role="alert">
|
|
121
|
+
{chatFirstStorageNotice}
|
|
122
|
+
</p>
|
|
123
|
+
) : null}
|
|
124
|
+
</Card>
|
|
125
|
+
|
|
54
126
|
<Card>
|
|
55
127
|
<CardHeader>
|
|
56
128
|
<CardTitle className="text-base">
|
package/src/server/index.ts
CHANGED
|
@@ -73,7 +73,10 @@ export function setupDispatch(config: DispatchConfig = {}): NitroPluginDef {
|
|
|
73
73
|
export { default as dispatchAuthPlugin } from "./plugins/auth.js";
|
|
74
74
|
export { default as dispatchIntegrationsPlugin } from "./plugins/integrations.js";
|
|
75
75
|
export { default as dispatchAgentChatPlugin } from "./plugins/agent-chat.js";
|
|
76
|
-
export {
|
|
76
|
+
export {
|
|
77
|
+
default as dispatchDbPlugin,
|
|
78
|
+
runDispatchMigrations,
|
|
79
|
+
} from "./plugins/db.js";
|
|
77
80
|
export { default as dispatchCoreRoutesPlugin } from "./plugins/core-routes.js";
|
|
78
81
|
|
|
79
82
|
export type { DispatchConfig } from "../config.js";
|
|
@@ -555,6 +555,19 @@ describe("startWorkspaceAppCreation", () => {
|
|
|
555
555
|
expect(mocks.runBuilderAgent).toHaveBeenCalledWith(
|
|
556
556
|
expect.objectContaining({ userId: "builder-user-42" }),
|
|
557
557
|
);
|
|
558
|
+
const builderPrompt = String(
|
|
559
|
+
mocks.runBuilderAgent.mock.calls.at(-1)?.[0]?.prompt ?? "",
|
|
560
|
+
);
|
|
561
|
+
expect(builderPrompt).toContain("Autonomous Builder handoff contract:");
|
|
562
|
+
expect(builderPrompt).toContain(
|
|
563
|
+
"do not invoke a clarification, guided-question, or choice flow",
|
|
564
|
+
);
|
|
565
|
+
expect(builderPrompt).toContain(
|
|
566
|
+
"choose the most direct, conservative default",
|
|
567
|
+
);
|
|
568
|
+
expect(builderPrompt).toContain(
|
|
569
|
+
"Treat the source brief's unknowns and follow-up items as assumptions",
|
|
570
|
+
);
|
|
558
571
|
});
|
|
559
572
|
|
|
560
573
|
it("provisions and remembers the workspace Builder project when none is configured", async () => {
|
|
@@ -57,6 +57,17 @@ const PENDING_WORKSPACE_APP_TTL_MS = 7 * 24 * 60 * 60 * 1_000;
|
|
|
57
57
|
const AGENT_CARD_PATH = "/.well-known/agent-card.json";
|
|
58
58
|
const AGENT_CARD_FETCH_TIMEOUT_MS = 1_500;
|
|
59
59
|
const DEFAULT_WORKSPACE_APP_AUDIENCE = "internal";
|
|
60
|
+
const AUTONOMOUS_WORKSPACE_APP_CREATION_CONTRACT = [
|
|
61
|
+
"Autonomous Builder handoff contract:",
|
|
62
|
+
"- This is a background implementation run launched by the turn-into-app workflow. Treat the source brief and latest user request as authorization to build the app now; do not return a proposal or wait for another turn.",
|
|
63
|
+
"- Do not ask the user questions during the initial build and do not invoke a clarification, guided-question, or choice flow for a non-blocking decision.",
|
|
64
|
+
"- When the source or a tool presents a recommended option, choose it and continue. When no recommendation is present, choose the most direct, conservative default supported by the source and normal Agent-Native conventions.",
|
|
65
|
+
"- Resolve product, visual, copy, layout, route, data-model, dependency, and integration choices yourself. If an input is missing, use an empty state or clearly labeled representative sample so the workflow is demonstrable; never invent private facts or credentials.",
|
|
66
|
+
"- Treat the source brief's unknowns and follow-up items as assumptions to record in the app README or a visible Assumptions / Review section, not as questions to send back to the user.",
|
|
67
|
+
"- If a nonessential integration or provider is unavailable, build the supported boundary and leave a precise setup note; do not stop to ask which equivalent to use.",
|
|
68
|
+
"- Pause only for a true hard blocker: missing authorization required to create or access the branch, a destructive or irreversible external action, an ambiguous target workspace/project, or the absence of any identifiable repeatable workflow. Otherwise make the best grounded choice and proceed.",
|
|
69
|
+
"- Complete the UI, actions, instructions, application state, representative happy path, and verification in this run. Do not stop after planning, scaffolding, or a question.",
|
|
70
|
+
].join("\n");
|
|
60
71
|
const pendingBuilderProjectProvisioning = new Map<
|
|
61
72
|
string,
|
|
62
73
|
Promise<{ projectId: string }>
|
|
@@ -1931,6 +1942,8 @@ function buildWorkspaceAppPrompt(input: {
|
|
|
1931
1942
|
prompt: [
|
|
1932
1943
|
"Create a new agent-native app in this workspace.",
|
|
1933
1944
|
"",
|
|
1945
|
+
AUTONOMOUS_WORKSPACE_APP_CREATION_CONTRACT,
|
|
1946
|
+
"",
|
|
1934
1947
|
`App name: ${appId}`,
|
|
1935
1948
|
`App description: ${appDescription}`,
|
|
1936
1949
|
`Template to start from: ${input.template || "starter"}`,
|
|
@@ -880,6 +880,24 @@ describe("createGrantedDispatchMcpEmbedSession", () => {
|
|
|
880
880
|
).rejects.toThrow(/safe app-relative route/);
|
|
881
881
|
});
|
|
882
882
|
|
|
883
|
+
it("rejects a URL that does not belong to the explicitly named app", async () => {
|
|
884
|
+
await expect(
|
|
885
|
+
runWithRequestContext(
|
|
886
|
+
{
|
|
887
|
+
userEmail: "owner@example.test",
|
|
888
|
+
requestOrigin: "http://localhost:8092",
|
|
889
|
+
},
|
|
890
|
+
() =>
|
|
891
|
+
createGrantedDispatchMcpEmbedSession({
|
|
892
|
+
app: "analytics",
|
|
893
|
+
url: "https://mail.example.com/inbox",
|
|
894
|
+
}),
|
|
895
|
+
),
|
|
896
|
+
).rejects.toThrow(
|
|
897
|
+
/Embed URL must belong to an app granted through Dispatch/,
|
|
898
|
+
);
|
|
899
|
+
});
|
|
900
|
+
|
|
883
901
|
it("routes same-origin mounted app embed URLs to the mounted app", async () => {
|
|
884
902
|
mocks.discoverAgents.mockResolvedValue([
|
|
885
903
|
{
|
|
@@ -65,6 +65,7 @@ Use the standard workspace primitives:
|
|
|
65
65
|
- 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.
|
|
66
66
|
- 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.
|
|
67
67
|
- 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.
|
|
68
|
+
- 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.
|
|
68
69
|
- 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.
|
|
69
70
|
- 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.
|
|
70
71
|
- 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.
|
package/src/server/plugins/db.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { runMigrations } from "@agent-native/core/db";
|
|
|
3
3
|
import { dispatchMigrations } from "../../db/migrations.js";
|
|
4
4
|
import { scheduleVaultBootResync } from "../lib/vault-boot-resync.js";
|
|
5
5
|
|
|
6
|
-
const runDispatchMigrations = runMigrations(dispatchMigrations, {
|
|
6
|
+
export const runDispatchMigrations = runMigrations(dispatchMigrations, {
|
|
7
7
|
table: "dispatch_migrations",
|
|
8
8
|
});
|
|
9
9
|
|
|
@@ -42,7 +42,7 @@ When a user asks for something:
|
|
|
42
42
|
- 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.
|
|
43
43
|
- 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.
|
|
44
44
|
- 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.
|
|
45
|
-
- If the user explicitly asks for a new app or workspace app, call start-workspace-app-creation with their prompt and include a concise generated description by default. Do not satisfy a new-app request by adding a route, page, component, or file inside apps/chat or another existing app unless the user explicitly asks to modify that existing app. If the request is too vague to classify, ask one concise follow-up. If the action returns mode "builder", reply with the Builder branch URL; Builder is responsible for creating the separate workspace app under apps/<app-id>, mounting it at /<app-id>, ensuring apps/<app-id>/package.json exists with name/displayName and description so Dispatch discovers it, using relative /<app-id> links instead of hardcoded localhost/dev ports, and preserving APP_BASE_PATH/VITE_APP_BASE_PATH
|
|
45
|
+
- 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.
|
|
46
46
|
- For digests, reminders, or saved behavior, prefer recurring jobs, resources, or destinations over chat replies.
|
|
47
47
|
- Keep responses concise and operational — messaging platforms have character limits.
|
|
48
48
|
- Use markdown sparingly (bold and lists are fine, avoid complex formatting).
|
package/src/styles/dispatch.css
CHANGED
|
@@ -7,6 +7,35 @@
|
|
|
7
7
|
@source "../components/**/*.{js,mjs,ts,tsx}";
|
|
8
8
|
@source "../hooks/**/*.{js,mjs,ts,tsx}";
|
|
9
9
|
@source "../routes/**/*.{js,mjs,ts,tsx}";
|
|
10
|
+
@source "../../../core/src/client/chat-first/**/*.{ts,tsx}";
|
|
11
|
+
|
|
12
|
+
.dispatch-chat-first-chats .an-chat-history-rail__new-chat {
|
|
13
|
+
display: none;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.dispatch-chat-first-notice {
|
|
17
|
+
display: flex;
|
|
18
|
+
align-items: center;
|
|
19
|
+
justify-content: space-between;
|
|
20
|
+
gap: 0.75rem;
|
|
21
|
+
border-bottom: 1px solid hsl(var(--border));
|
|
22
|
+
background: hsl(var(--muted) / 0.35);
|
|
23
|
+
padding: 0.5rem 0.75rem;
|
|
24
|
+
color: hsl(var(--muted-foreground));
|
|
25
|
+
font-size: 0.75rem;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.dispatch-chat-first-notice button {
|
|
29
|
+
flex: none;
|
|
30
|
+
color: hsl(var(--foreground));
|
|
31
|
+
font-weight: 600;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@media (max-width: 767px) {
|
|
35
|
+
.dispatch-chat-first-surface {
|
|
36
|
+
position: relative;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
10
39
|
|
|
11
40
|
.dispatch-chat-panel [data-agent-empty-state="centered"] {
|
|
12
41
|
justify-content: center;
|