@agent-native/dispatch 0.20.4 → 0.22.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/get-agent-thread-debug.d.ts +2 -0
- package/dist/actions/provider-api-register.d.ts +7 -7
- package/dist/actions/search-agent-threads.js +2 -2
- package/dist/actions/search-agent-threads.js.map +1 -1
- package/dist/actions/start-workspace-app-creation.js +1 -1
- package/dist/actions/start-workspace-app-creation.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/HeaderActions.d.ts +1 -17
- package/dist/components/layout/HeaderActions.d.ts.map +1 -1
- package/dist/components/layout/HeaderActions.js +1 -51
- package/dist/components/layout/HeaderActions.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 +707 -18
- package/dist/components/layout/Layout.js.map +1 -1
- package/dist/components/ui/dialog.d.ts +1 -19
- package/dist/components/ui/dialog.d.ts.map +1 -1
- package/dist/components/ui/dialog.js +1 -22
- package/dist/components/ui/dialog.js.map +1 -1
- package/dist/components/ui/dropdown-menu.d.ts +1 -27
- package/dist/components/ui/dropdown-menu.d.ts.map +1 -1
- package/dist/components/ui/dropdown-menu.js +1 -35
- package/dist/components/ui/dropdown-menu.js.map +1 -1
- package/dist/components/ui/popover.d.ts +2 -1
- package/dist/components/ui/popover.d.ts.map +1 -1
- package/dist/components/ui/popover.js +2 -1
- package/dist/components/ui/popover.js.map +1 -1
- package/dist/components/ui/select.d.ts +1 -13
- package/dist/components/ui/select.d.ts.map +1 -1
- package/dist/components/ui/select.js +1 -26
- package/dist/components/ui/select.js.map +1 -1
- package/dist/hooks/use-mobile.d.ts +1 -1
- package/dist/hooks/use-mobile.d.ts.map +1 -1
- package/dist/hooks/use-mobile.js +1 -15
- package/dist/hooks/use-mobile.js.map +1 -1
- package/dist/lib/automation-display.js +1 -1
- package/dist/lib/automation-display.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/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/routes/pages/thread-debug.d.ts.map +1 -1
- package/dist/routes/pages/thread-debug.js +288 -118
- package/dist/routes/pages/thread-debug.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/lib/thread-debug-store.d.ts +2 -0
- package/dist/server/lib/thread-debug-store.d.ts.map +1 -1
- package/dist/server/lib/thread-debug-store.js +6 -2
- package/dist/server/lib/thread-debug-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/integrations.js +1 -1
- package/dist/server/plugins/integrations.js.map +1 -1
- package/package.json +3 -3
- package/src/actions/search-agent-threads.ts +2 -2
- package/src/actions/start-workspace-app-creation.ts +1 -1
- package/src/components/create-app-popover.spec.tsx +22 -0
- package/src/components/create-app-popover.tsx +30 -94
- package/src/components/layout/HeaderActions.tsx +1 -84
- package/src/components/layout/Layout.spec.tsx +35 -10
- package/src/components/layout/Layout.tsx +1394 -74
- package/src/components/ui/dialog.tsx +1 -120
- package/src/components/ui/dropdown-menu.tsx +1 -198
- package/src/components/ui/popover.tsx +3 -1
- package/src/components/ui/select.tsx +1 -158
- package/src/hooks/use-mobile.tsx +1 -21
- package/src/lib/automation-display.spec.ts +1 -1
- package/src/lib/automation-display.ts +1 -1
- package/src/lib/workspace-apps.ts +24 -0
- package/src/routes/pages/settings.tsx +72 -0
- package/src/routes/pages/thread-debug.spec.tsx +103 -5
- package/src/routes/pages/thread-debug.tsx +1051 -451
- 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/lib/thread-debug-store.spec.ts +17 -0
- package/src/server/lib/thread-debug-store.ts +9 -2
- package/src/server/plugins/agent-chat.ts +1 -0
- package/src/server/plugins/integrations.ts +1 -1
- package/src/styles/dispatch.css +29 -0
|
@@ -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
|
{
|
|
@@ -52,6 +52,8 @@ const run = {
|
|
|
52
52
|
worker_stage: "model",
|
|
53
53
|
diag_stage: '{"stage":"model"}',
|
|
54
54
|
peak_rss_mb: 321,
|
|
55
|
+
in_flight_since: 8,
|
|
56
|
+
dispatch_payload: '{"request":"retained"}',
|
|
55
57
|
};
|
|
56
58
|
|
|
57
59
|
function rowsForThreadLookup(sql: string, args: unknown[]) {
|
|
@@ -117,6 +119,19 @@ describe("thread-debug-store", () => {
|
|
|
117
119
|
expect(result.threads[0]?.id).toBe(thread.id);
|
|
118
120
|
});
|
|
119
121
|
|
|
122
|
+
it("matches owner email from the main search query", async () => {
|
|
123
|
+
const result = await searchAgentThreads({ query: "owner@example.com" });
|
|
124
|
+
|
|
125
|
+
expect(result.threads).toHaveLength(1);
|
|
126
|
+
const searchRequest = mocks.currentExecute.mock.calls.find(
|
|
127
|
+
([request]) =>
|
|
128
|
+
typeof request?.sql === "string" &&
|
|
129
|
+
request.sql.includes("LOWER(owner_email) LIKE"),
|
|
130
|
+
)?.[0];
|
|
131
|
+
expect(searchRequest?.sql).toContain("LOWER(owner_email) LIKE");
|
|
132
|
+
expect(searchRequest?.args).toContain("%owner@example.com%");
|
|
133
|
+
});
|
|
134
|
+
|
|
120
135
|
it("resolves a run id and returns rich run diagnostics", async () => {
|
|
121
136
|
const result = await getAgentThreadDebug({ runId: run.id });
|
|
122
137
|
|
|
@@ -137,6 +152,8 @@ describe("thread-debug-store", () => {
|
|
|
137
152
|
workerStage: "model",
|
|
138
153
|
diagStage: '{"stage":"model"}',
|
|
139
154
|
peakRssMb: 321,
|
|
155
|
+
inFlightSince: 8,
|
|
156
|
+
hasDispatchPayload: true,
|
|
140
157
|
});
|
|
141
158
|
expect(
|
|
142
159
|
mocks.currentExecute.mock.calls.some(([request]) =>
|
|
@@ -79,6 +79,8 @@ interface AgentRunRow {
|
|
|
79
79
|
worker_stage?: string | null;
|
|
80
80
|
diag_stage?: string | null;
|
|
81
81
|
peak_rss_mb?: number | string | null;
|
|
82
|
+
in_flight_since?: number | string | null;
|
|
83
|
+
dispatch_payload?: string | null;
|
|
82
84
|
}
|
|
83
85
|
|
|
84
86
|
const execCache = new Map<string, Promise<DbExec>>();
|
|
@@ -586,6 +588,11 @@ function serializeRun(row: AgentRunRow, events: any[] = []) {
|
|
|
586
588
|
workerStage: row.worker_stage ? String(row.worker_stage) : null,
|
|
587
589
|
diagStage: row.diag_stage ? String(row.diag_stage) : null,
|
|
588
590
|
peakRssMb: nullableNumberField(row.peak_rss_mb),
|
|
591
|
+
inFlightSince: nullableNumberField(row.in_flight_since),
|
|
592
|
+
hasDispatchPayload:
|
|
593
|
+
row.dispatch_payload !== null &&
|
|
594
|
+
row.dispatch_payload !== undefined &&
|
|
595
|
+
String(row.dispatch_payload).length > 0,
|
|
589
596
|
events,
|
|
590
597
|
};
|
|
591
598
|
}
|
|
@@ -906,11 +913,11 @@ export async function searchAgentThreads(input: {
|
|
|
906
913
|
? " OR id IN (" + runThreadIds.map(() => "?").join(", ") + ")"
|
|
907
914
|
: "";
|
|
908
915
|
where.push(
|
|
909
|
-
"(LOWER(title) LIKE ? ESCAPE '\\' OR LOWER(preview) LIKE ? ESCAPE '\\' OR LOWER(thread_data) LIKE ? ESCAPE '\\'" +
|
|
916
|
+
"(LOWER(title) LIKE ? ESCAPE '\\' OR LOWER(preview) LIKE ? ESCAPE '\\' OR LOWER(owner_email) LIKE ? ESCAPE '\\' OR LOWER(thread_data) LIKE ? ESCAPE '\\'" +
|
|
910
917
|
runIdClause +
|
|
911
918
|
")",
|
|
912
919
|
);
|
|
913
|
-
args.push(pattern, pattern, pattern);
|
|
920
|
+
args.push(pattern, pattern, pattern, pattern);
|
|
914
921
|
args.push(...runThreadIds);
|
|
915
922
|
}
|
|
916
923
|
args.push(limit);
|
|
@@ -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.
|
|
@@ -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;
|