@agent-native/core 0.12.14 → 0.12.16
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/agent/engine/index.d.ts +1 -0
- package/dist/agent/engine/index.d.ts.map +1 -1
- package/dist/agent/engine/index.js +1 -0
- package/dist/agent/engine/index.js.map +1 -1
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +56 -42
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/cli/workspace-dev.js +26 -5
- package/dist/cli/workspace-dev.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +33 -6
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +7 -2
- package/dist/client/MultiTabAssistantChat.js.map +1 -1
- package/dist/client/NewWorkspaceAppFlow.d.ts.map +1 -1
- package/dist/client/NewWorkspaceAppFlow.js +2 -0
- package/dist/client/NewWorkspaceAppFlow.js.map +1 -1
- package/dist/client/extensions/EmbeddedExtension.d.ts.map +1 -1
- package/dist/client/extensions/EmbeddedExtension.js +13 -12
- package/dist/client/extensions/EmbeddedExtension.js.map +1 -1
- package/dist/client/extensions/ExtensionEditor.d.ts.map +1 -1
- package/dist/client/extensions/ExtensionEditor.js +23 -10
- package/dist/client/extensions/ExtensionEditor.js.map +1 -1
- package/dist/client/extensions/ExtensionViewer.d.ts.map +1 -1
- package/dist/client/extensions/ExtensionViewer.js +15 -10
- package/dist/client/extensions/ExtensionViewer.js.map +1 -1
- package/dist/client/extensions/ExtensionsListPage.d.ts.map +1 -1
- package/dist/client/extensions/ExtensionsListPage.js +12 -6
- package/dist/client/extensions/ExtensionsListPage.js.map +1 -1
- package/dist/client/extensions/ExtensionsSidebarSection.d.ts.map +1 -1
- package/dist/client/extensions/ExtensionsSidebarSection.js +8 -9
- package/dist/client/extensions/ExtensionsSidebarSection.js.map +1 -1
- package/dist/client/extensions/delete-extension.d.ts +12 -0
- package/dist/client/extensions/delete-extension.d.ts.map +1 -0
- package/dist/client/extensions/delete-extension.js +49 -0
- package/dist/client/extensions/delete-extension.js.map +1 -0
- package/dist/client/sharing/ShareButton.js +6 -1
- package/dist/client/sharing/ShareButton.js.map +1 -1
- package/dist/client/sharing/ShareButton.spec.d.ts +2 -0
- package/dist/client/sharing/ShareButton.spec.d.ts.map +1 -0
- package/dist/client/sharing/ShareButton.spec.js +90 -0
- package/dist/client/sharing/ShareButton.spec.js.map +1 -0
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +10 -2
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/client/use-chat-threads.d.ts.map +1 -1
- package/dist/client/use-chat-threads.js +19 -2
- package/dist/client/use-chat-threads.js.map +1 -1
- package/dist/client/use-db-sync.d.ts.map +1 -1
- package/dist/client/use-db-sync.js +3 -0
- package/dist/client/use-db-sync.js.map +1 -1
- package/dist/extensions/routes.d.ts.map +1 -1
- package/dist/extensions/routes.js +41 -5
- package/dist/extensions/routes.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +11 -7
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/templates/default/AGENTS.md +7 -1
- package/dist/templates/default/DEVELOPING.md +12 -0
- package/dist/templates/default/app/hooks/use-navigation-state.ts +81 -0
- package/dist/templates/default/app/root.tsx +11 -5
- package/dist/templates/workspace-root/AGENTS.md +13 -0
- package/dist/templates/workspace-root/README.md +6 -0
- package/docs/content/multi-app-workspace.md +2 -0
- package/package.json +1 -1
- package/src/templates/default/AGENTS.md +7 -1
- package/src/templates/default/DEVELOPING.md +12 -0
- package/src/templates/default/app/hooks/use-navigation-state.ts +81 -0
- package/src/templates/default/app/root.tsx +11 -5
- package/src/templates/workspace-root/AGENTS.md +13 -0
- package/src/templates/workspace-root/README.md +6 -0
|
@@ -50,6 +50,12 @@ Ephemeral UI state is stored in the SQL `application_state` table, accessed via
|
|
|
50
50
|
|
|
51
51
|
The `navigation` key is written by the UI whenever the route changes. The `navigate` key is a one-shot command: the agent writes it, the UI reads and executes the navigation, then deletes it.
|
|
52
52
|
|
|
53
|
+
## Mounted Workspace Routing
|
|
54
|
+
|
|
55
|
+
This app may be mounted under `/<app-id>` in a workspace. Inside app source, React Router paths are app-local: use `<Link to="/review">` and `navigate("/review")`, not `/<app-id>/review`. The workspace gateway and `APP_BASE_PATH` add the mounted prefix in the browser; hardcoding it inside React Router links causes doubled URLs such as `/<app-id>/<app-id>/review`.
|
|
56
|
+
|
|
57
|
+
For raw paths outside React Router, use the core helpers: `appPath()` for static assets or normal hrefs, `appApiPath()` for `/api/*`, and `agentNativePath()` for `/_agent-native/*`.
|
|
58
|
+
|
|
53
59
|
## Agent Operations
|
|
54
60
|
|
|
55
61
|
**Always know what the user is currently viewing before you edit anything.** The user's view can change mid-conversation. Stale IDs lead to editing the wrong record.
|
|
@@ -101,7 +107,7 @@ Skills in `.agents/skills/` provide detailed guidance for each architectural rul
|
|
|
101
107
|
|
|
102
108
|
As you build out this app, follow this checklist for each new feature:
|
|
103
109
|
|
|
104
|
-
1. **Add navigation state entries** --
|
|
110
|
+
1. **Add navigation state entries** -- extend `app/hooks/use-navigation-state.ts` to track new routes
|
|
105
111
|
2. **Enhance view-screen** -- make the view-screen script return relevant context for the new view
|
|
106
112
|
3. **Create domain actions** -- add scripts for CRUD operations on new data models
|
|
107
113
|
4. **Create domain skills** -- add `.agents/skills/<feature>/SKILL.md` documenting the data model, storage patterns, and agent operations
|
|
@@ -20,6 +20,18 @@ app/routes/inbox.$threadId.tsx → /inbox/:threadId
|
|
|
20
20
|
app/routes/$id.tsx → /:id (dynamic param)
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
+
## Mounted Workspace Routing
|
|
24
|
+
|
|
25
|
+
In a workspace, this app can be mounted under `/<app-id>`. React Router already receives `APP_BASE_PATH`/`VITE_APP_BASE_PATH` through `appBasePath()`, so route code stays app-local:
|
|
26
|
+
|
|
27
|
+
| Route file | App-internal route | Mounted browser URL |
|
|
28
|
+
| ----------------------- | ------------------ | ------------------- |
|
|
29
|
+
| `app/routes/_index.tsx` | `/` | `/<app-id>` |
|
|
30
|
+
| `app/routes/review.tsx` | `/review` | `/<app-id>/review` |
|
|
31
|
+
| `app/routes/$id.tsx` | `/:id` | `/<app-id>/:id` |
|
|
32
|
+
|
|
33
|
+
Use `<Link to="/review">` and `navigate("/review")` inside this app. Do not prefix React Router paths with `/<app-id>` or the URL can double-prefix, e.g. `/<app-id>/<app-id>/review`. Use `appPath()` for raw `href`s/static assets, `appApiPath()` for `/api/*`, and `agentNativePath()` for `/_agent-native/*`.
|
|
34
|
+
|
|
23
35
|
Each route file exports a default component and optional `meta()`:
|
|
24
36
|
|
|
25
37
|
```tsx
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { useEffect } from "react";
|
|
2
|
+
import { useLocation, useNavigate } from "react-router";
|
|
3
|
+
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
4
|
+
import {
|
|
5
|
+
agentNativePath,
|
|
6
|
+
appBasePath,
|
|
7
|
+
appPath,
|
|
8
|
+
} from "@agent-native/core/client";
|
|
9
|
+
|
|
10
|
+
export interface NavigationState {
|
|
11
|
+
view: string;
|
|
12
|
+
path?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function useNavigationState() {
|
|
16
|
+
const location = useLocation();
|
|
17
|
+
const navigate = useNavigate();
|
|
18
|
+
const qc = useQueryClient();
|
|
19
|
+
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
const state: NavigationState = {
|
|
22
|
+
view: viewFromPath(location.pathname),
|
|
23
|
+
path: appPath(location.pathname),
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
fetch(agentNativePath("/_agent-native/application-state/navigation"), {
|
|
27
|
+
method: "PUT",
|
|
28
|
+
keepalive: true,
|
|
29
|
+
headers: { "Content-Type": "application/json" },
|
|
30
|
+
body: JSON.stringify(state),
|
|
31
|
+
}).catch(() => {});
|
|
32
|
+
}, [location.pathname]);
|
|
33
|
+
|
|
34
|
+
const { data: navCommand } = useQuery({
|
|
35
|
+
queryKey: ["navigate-command"],
|
|
36
|
+
queryFn: async () => {
|
|
37
|
+
const res = await fetch(
|
|
38
|
+
agentNativePath("/_agent-native/application-state/navigate"),
|
|
39
|
+
);
|
|
40
|
+
if (!res.ok) return null;
|
|
41
|
+
const data = await res.json();
|
|
42
|
+
return data ? { ...data, _ts: Date.now() } : null;
|
|
43
|
+
},
|
|
44
|
+
refetchInterval: 2_000,
|
|
45
|
+
refetchIntervalInBackground: true,
|
|
46
|
+
structuralSharing: false,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
if (!navCommand) return;
|
|
51
|
+
fetch(agentNativePath("/_agent-native/application-state/navigate"), {
|
|
52
|
+
method: "DELETE",
|
|
53
|
+
headers: { "X-Agent-Native-CSRF": "1" },
|
|
54
|
+
}).catch(() => {});
|
|
55
|
+
const cmd = navCommand as NavigationState;
|
|
56
|
+
|
|
57
|
+
const path = routerPath(cmd.path || pathFromView(cmd.view));
|
|
58
|
+
navigate(path);
|
|
59
|
+
qc.setQueryData(["navigate-command"], null);
|
|
60
|
+
}, [navCommand, navigate, qc]);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function viewFromPath(pathname: string): string {
|
|
64
|
+
if (!pathname || pathname === "/") return "home";
|
|
65
|
+
return pathname.replace(/^\/+/, "") || "home";
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function pathFromView(view: string | undefined): string {
|
|
69
|
+
if (!view || view === "home") return "/";
|
|
70
|
+
return `/${view.replace(/^\/+/, "")}`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function routerPath(path: string): string {
|
|
74
|
+
const basePath = appBasePath();
|
|
75
|
+
if (!basePath) return path;
|
|
76
|
+
if (path === basePath) return "/";
|
|
77
|
+
if (path.startsWith(`${basePath}/`)) {
|
|
78
|
+
return path.slice(basePath.length) || "/";
|
|
79
|
+
}
|
|
80
|
+
return path;
|
|
81
|
+
}
|
|
@@ -16,10 +16,15 @@ import {
|
|
|
16
16
|
} from "@tanstack/react-query";
|
|
17
17
|
import { ThemeProvider } from "next-themes";
|
|
18
18
|
import { useDbSync } from "@agent-native/core";
|
|
19
|
-
import {
|
|
20
|
-
|
|
19
|
+
import {
|
|
20
|
+
ClientOnly,
|
|
21
|
+
DefaultSpinner,
|
|
22
|
+
appPath,
|
|
23
|
+
getThemeInitScript,
|
|
24
|
+
} from "@agent-native/core/client";
|
|
21
25
|
import { Toaster } from "sonner";
|
|
22
26
|
import { configureTracking } from "@agent-native/core/client";
|
|
27
|
+
import { useNavigationState } from "./hooks/use-navigation-state";
|
|
23
28
|
configureTracking({
|
|
24
29
|
getDefaultProps: (_name, properties) => ({
|
|
25
30
|
...properties,
|
|
@@ -56,8 +61,8 @@ export function Layout({ children }: { children: React.ReactNode }) {
|
|
|
56
61
|
suppressHydrationWarning
|
|
57
62
|
dangerouslySetInnerHTML={{ __html: THEME_INIT_SCRIPT }}
|
|
58
63
|
/>
|
|
59
|
-
<link rel="manifest" href="/manifest.json" />
|
|
60
|
-
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
64
|
+
<link rel="manifest" href={appPath("/manifest.json")} />
|
|
65
|
+
<link rel="icon" type="image/svg+xml" href={appPath("/favicon.svg")} />
|
|
61
66
|
<meta name="theme-color" content="#111111" />
|
|
62
67
|
<meta name="mobile-web-app-capable" content="yes" />
|
|
63
68
|
<meta
|
|
@@ -65,7 +70,7 @@ export function Layout({ children }: { children: React.ReactNode }) {
|
|
|
65
70
|
content="black-translucent"
|
|
66
71
|
/>
|
|
67
72
|
<meta name="apple-mobile-web-app-title" content="App" />
|
|
68
|
-
<link rel="apple-touch-icon" href="/icon-180.svg" />
|
|
73
|
+
<link rel="apple-touch-icon" href={appPath("/icon-180.svg")} />
|
|
69
74
|
<Meta />
|
|
70
75
|
<Links />
|
|
71
76
|
</head>
|
|
@@ -80,6 +85,7 @@ export function Layout({ children }: { children: React.ReactNode }) {
|
|
|
80
85
|
|
|
81
86
|
function DbSyncSetup() {
|
|
82
87
|
const qc = useQueryClient();
|
|
88
|
+
useNavigationState();
|
|
83
89
|
useDbSync({ queryClient: qc, queryKeys: ["files", "data"] });
|
|
84
90
|
return null;
|
|
85
91
|
}
|
|
@@ -29,6 +29,19 @@ in `apps/<app>/AGENTS.md`; shared cross-app behavior belongs in
|
|
|
29
29
|
- Do not satisfy a new-app request by adding a route, page, component, or file
|
|
30
30
|
to `apps/starter` or another existing app unless the user explicitly asks to
|
|
31
31
|
modify that existing app.
|
|
32
|
+
- Treat first-party apps such as Mail, Calendar, Analytics, and Dispatch as
|
|
33
|
+
existing hosted/connected neighbors available through links and A2A/default
|
|
34
|
+
connected agents. For example, Mail, Calendar, and Analytics already exist at
|
|
35
|
+
`https://mail.agent-native.com`, `https://calendar.agent-native.com`, and
|
|
36
|
+
`https://analytics.agent-native.com`.
|
|
37
|
+
- If a new app needs to use Mail, Calendar, Analytics, or similar first-party
|
|
38
|
+
data/agents, build only the genuinely new workflow and delegate/link to those
|
|
39
|
+
existing apps. Do not create wrapper apps, child apps, nested template copies,
|
|
40
|
+
or cloned Mail/Calendar/Analytics implementations inside the new app just to
|
|
41
|
+
provide access.
|
|
42
|
+
- Only create a first-party app copy when the user explicitly asks for a
|
|
43
|
+
customized fork/copy of that app. Otherwise prefer the hosted/shared app so
|
|
44
|
+
base template improvements continue to flow automatically.
|
|
32
45
|
- Workspace apps are discovered from `apps/<app-id>/package.json`. There is no
|
|
33
46
|
separate workspace app registry to edit for Dispatch to list the app.
|
|
34
47
|
- Use relative workspace links like `/<app-id>`. Never hardcode
|
|
@@ -84,6 +84,12 @@ separate workspace app registry to edit. React Router apps must preserve
|
|
|
84
84
|
For requests phrased as creating an "agent", classify the scope first: simple
|
|
85
85
|
recurring Dispatch behavior can stay in Dispatch, while a robust app-like
|
|
86
86
|
teammate should become a real workspace app listed with the rest of the apps.
|
|
87
|
+
First-party apps such as Mail, Calendar, Analytics, and Dispatch should be
|
|
88
|
+
treated as existing hosted or connected neighbors. If a new app needs access to
|
|
89
|
+
their data or agents, link/delegate to those apps through the workspace/A2A
|
|
90
|
+
path rather than creating wrapper apps, child apps, or cloned template copies
|
|
91
|
+
inside the new app. Only fork one of those apps when the user explicitly asks
|
|
92
|
+
for a customized copy.
|
|
87
93
|
|
|
88
94
|
## Editing shared behavior
|
|
89
95
|
|
|
@@ -31,6 +31,8 @@ Anything every app in your org should agree on can live in `packages/shared`:
|
|
|
31
31
|
|
|
32
32
|
Each individual app becomes _just a set of screens_ — routes, dashboards, views, domain-specific actions. Framework defaults cover the rest until you add a real workspace customization.
|
|
33
33
|
|
|
34
|
+
That same boundary applies when your app wants to use another first-party app. A new workspace dashboard that needs email, calendar, and analytics context should use the existing Mail, Calendar, and Analytics apps as connected neighbors over links or A2A. It should not clone those templates, create a wrapper app that nests them, or scaffold child apps inside itself just to get access to their data or agents. For example, the hosted first-party apps already live at [mail.agent-native.com](https://mail.agent-native.com), [calendar.agent-native.com](https://calendar.agent-native.com), and [analytics.agent-native.com](https://analytics.agent-native.com). Fork or scaffold a copy only when you explicitly want to customize that app; otherwise, using the hosted/shared app keeps base template improvements flowing automatically.
|
|
35
|
+
|
|
34
36
|
## Getting started {#getting-started}
|
|
35
37
|
|
|
36
38
|
Workspace is the default shape of an agent-native project. Scaffold one with:
|
package/package.json
CHANGED
|
@@ -50,6 +50,12 @@ Ephemeral UI state is stored in the SQL `application_state` table, accessed via
|
|
|
50
50
|
|
|
51
51
|
The `navigation` key is written by the UI whenever the route changes. The `navigate` key is a one-shot command: the agent writes it, the UI reads and executes the navigation, then deletes it.
|
|
52
52
|
|
|
53
|
+
## Mounted Workspace Routing
|
|
54
|
+
|
|
55
|
+
This app may be mounted under `/<app-id>` in a workspace. Inside app source, React Router paths are app-local: use `<Link to="/review">` and `navigate("/review")`, not `/<app-id>/review`. The workspace gateway and `APP_BASE_PATH` add the mounted prefix in the browser; hardcoding it inside React Router links causes doubled URLs such as `/<app-id>/<app-id>/review`.
|
|
56
|
+
|
|
57
|
+
For raw paths outside React Router, use the core helpers: `appPath()` for static assets or normal hrefs, `appApiPath()` for `/api/*`, and `agentNativePath()` for `/_agent-native/*`.
|
|
58
|
+
|
|
53
59
|
## Agent Operations
|
|
54
60
|
|
|
55
61
|
**Always know what the user is currently viewing before you edit anything.** The user's view can change mid-conversation. Stale IDs lead to editing the wrong record.
|
|
@@ -101,7 +107,7 @@ Skills in `.agents/skills/` provide detailed guidance for each architectural rul
|
|
|
101
107
|
|
|
102
108
|
As you build out this app, follow this checklist for each new feature:
|
|
103
109
|
|
|
104
|
-
1. **Add navigation state entries** --
|
|
110
|
+
1. **Add navigation state entries** -- extend `app/hooks/use-navigation-state.ts` to track new routes
|
|
105
111
|
2. **Enhance view-screen** -- make the view-screen script return relevant context for the new view
|
|
106
112
|
3. **Create domain actions** -- add scripts for CRUD operations on new data models
|
|
107
113
|
4. **Create domain skills** -- add `.agents/skills/<feature>/SKILL.md` documenting the data model, storage patterns, and agent operations
|
|
@@ -20,6 +20,18 @@ app/routes/inbox.$threadId.tsx → /inbox/:threadId
|
|
|
20
20
|
app/routes/$id.tsx → /:id (dynamic param)
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
+
## Mounted Workspace Routing
|
|
24
|
+
|
|
25
|
+
In a workspace, this app can be mounted under `/<app-id>`. React Router already receives `APP_BASE_PATH`/`VITE_APP_BASE_PATH` through `appBasePath()`, so route code stays app-local:
|
|
26
|
+
|
|
27
|
+
| Route file | App-internal route | Mounted browser URL |
|
|
28
|
+
| ----------------------- | ------------------ | ------------------- |
|
|
29
|
+
| `app/routes/_index.tsx` | `/` | `/<app-id>` |
|
|
30
|
+
| `app/routes/review.tsx` | `/review` | `/<app-id>/review` |
|
|
31
|
+
| `app/routes/$id.tsx` | `/:id` | `/<app-id>/:id` |
|
|
32
|
+
|
|
33
|
+
Use `<Link to="/review">` and `navigate("/review")` inside this app. Do not prefix React Router paths with `/<app-id>` or the URL can double-prefix, e.g. `/<app-id>/<app-id>/review`. Use `appPath()` for raw `href`s/static assets, `appApiPath()` for `/api/*`, and `agentNativePath()` for `/_agent-native/*`.
|
|
34
|
+
|
|
23
35
|
Each route file exports a default component and optional `meta()`:
|
|
24
36
|
|
|
25
37
|
```tsx
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { useEffect } from "react";
|
|
2
|
+
import { useLocation, useNavigate } from "react-router";
|
|
3
|
+
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
4
|
+
import {
|
|
5
|
+
agentNativePath,
|
|
6
|
+
appBasePath,
|
|
7
|
+
appPath,
|
|
8
|
+
} from "@agent-native/core/client";
|
|
9
|
+
|
|
10
|
+
export interface NavigationState {
|
|
11
|
+
view: string;
|
|
12
|
+
path?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function useNavigationState() {
|
|
16
|
+
const location = useLocation();
|
|
17
|
+
const navigate = useNavigate();
|
|
18
|
+
const qc = useQueryClient();
|
|
19
|
+
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
const state: NavigationState = {
|
|
22
|
+
view: viewFromPath(location.pathname),
|
|
23
|
+
path: appPath(location.pathname),
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
fetch(agentNativePath("/_agent-native/application-state/navigation"), {
|
|
27
|
+
method: "PUT",
|
|
28
|
+
keepalive: true,
|
|
29
|
+
headers: { "Content-Type": "application/json" },
|
|
30
|
+
body: JSON.stringify(state),
|
|
31
|
+
}).catch(() => {});
|
|
32
|
+
}, [location.pathname]);
|
|
33
|
+
|
|
34
|
+
const { data: navCommand } = useQuery({
|
|
35
|
+
queryKey: ["navigate-command"],
|
|
36
|
+
queryFn: async () => {
|
|
37
|
+
const res = await fetch(
|
|
38
|
+
agentNativePath("/_agent-native/application-state/navigate"),
|
|
39
|
+
);
|
|
40
|
+
if (!res.ok) return null;
|
|
41
|
+
const data = await res.json();
|
|
42
|
+
return data ? { ...data, _ts: Date.now() } : null;
|
|
43
|
+
},
|
|
44
|
+
refetchInterval: 2_000,
|
|
45
|
+
refetchIntervalInBackground: true,
|
|
46
|
+
structuralSharing: false,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
if (!navCommand) return;
|
|
51
|
+
fetch(agentNativePath("/_agent-native/application-state/navigate"), {
|
|
52
|
+
method: "DELETE",
|
|
53
|
+
headers: { "X-Agent-Native-CSRF": "1" },
|
|
54
|
+
}).catch(() => {});
|
|
55
|
+
const cmd = navCommand as NavigationState;
|
|
56
|
+
|
|
57
|
+
const path = routerPath(cmd.path || pathFromView(cmd.view));
|
|
58
|
+
navigate(path);
|
|
59
|
+
qc.setQueryData(["navigate-command"], null);
|
|
60
|
+
}, [navCommand, navigate, qc]);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function viewFromPath(pathname: string): string {
|
|
64
|
+
if (!pathname || pathname === "/") return "home";
|
|
65
|
+
return pathname.replace(/^\/+/, "") || "home";
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function pathFromView(view: string | undefined): string {
|
|
69
|
+
if (!view || view === "home") return "/";
|
|
70
|
+
return `/${view.replace(/^\/+/, "")}`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function routerPath(path: string): string {
|
|
74
|
+
const basePath = appBasePath();
|
|
75
|
+
if (!basePath) return path;
|
|
76
|
+
if (path === basePath) return "/";
|
|
77
|
+
if (path.startsWith(`${basePath}/`)) {
|
|
78
|
+
return path.slice(basePath.length) || "/";
|
|
79
|
+
}
|
|
80
|
+
return path;
|
|
81
|
+
}
|
|
@@ -16,10 +16,15 @@ import {
|
|
|
16
16
|
} from "@tanstack/react-query";
|
|
17
17
|
import { ThemeProvider } from "next-themes";
|
|
18
18
|
import { useDbSync } from "@agent-native/core";
|
|
19
|
-
import {
|
|
20
|
-
|
|
19
|
+
import {
|
|
20
|
+
ClientOnly,
|
|
21
|
+
DefaultSpinner,
|
|
22
|
+
appPath,
|
|
23
|
+
getThemeInitScript,
|
|
24
|
+
} from "@agent-native/core/client";
|
|
21
25
|
import { Toaster } from "sonner";
|
|
22
26
|
import { configureTracking } from "@agent-native/core/client";
|
|
27
|
+
import { useNavigationState } from "./hooks/use-navigation-state";
|
|
23
28
|
configureTracking({
|
|
24
29
|
getDefaultProps: (_name, properties) => ({
|
|
25
30
|
...properties,
|
|
@@ -56,8 +61,8 @@ export function Layout({ children }: { children: React.ReactNode }) {
|
|
|
56
61
|
suppressHydrationWarning
|
|
57
62
|
dangerouslySetInnerHTML={{ __html: THEME_INIT_SCRIPT }}
|
|
58
63
|
/>
|
|
59
|
-
<link rel="manifest" href="/manifest.json" />
|
|
60
|
-
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
64
|
+
<link rel="manifest" href={appPath("/manifest.json")} />
|
|
65
|
+
<link rel="icon" type="image/svg+xml" href={appPath("/favicon.svg")} />
|
|
61
66
|
<meta name="theme-color" content="#111111" />
|
|
62
67
|
<meta name="mobile-web-app-capable" content="yes" />
|
|
63
68
|
<meta
|
|
@@ -65,7 +70,7 @@ export function Layout({ children }: { children: React.ReactNode }) {
|
|
|
65
70
|
content="black-translucent"
|
|
66
71
|
/>
|
|
67
72
|
<meta name="apple-mobile-web-app-title" content="App" />
|
|
68
|
-
<link rel="apple-touch-icon" href="/icon-180.svg" />
|
|
73
|
+
<link rel="apple-touch-icon" href={appPath("/icon-180.svg")} />
|
|
69
74
|
<Meta />
|
|
70
75
|
<Links />
|
|
71
76
|
</head>
|
|
@@ -80,6 +85,7 @@ export function Layout({ children }: { children: React.ReactNode }) {
|
|
|
80
85
|
|
|
81
86
|
function DbSyncSetup() {
|
|
82
87
|
const qc = useQueryClient();
|
|
88
|
+
useNavigationState();
|
|
83
89
|
useDbSync({ queryClient: qc, queryKeys: ["files", "data"] });
|
|
84
90
|
return null;
|
|
85
91
|
}
|
|
@@ -29,6 +29,19 @@ in `apps/<app>/AGENTS.md`; shared cross-app behavior belongs in
|
|
|
29
29
|
- Do not satisfy a new-app request by adding a route, page, component, or file
|
|
30
30
|
to `apps/starter` or another existing app unless the user explicitly asks to
|
|
31
31
|
modify that existing app.
|
|
32
|
+
- Treat first-party apps such as Mail, Calendar, Analytics, and Dispatch as
|
|
33
|
+
existing hosted/connected neighbors available through links and A2A/default
|
|
34
|
+
connected agents. For example, Mail, Calendar, and Analytics already exist at
|
|
35
|
+
`https://mail.agent-native.com`, `https://calendar.agent-native.com`, and
|
|
36
|
+
`https://analytics.agent-native.com`.
|
|
37
|
+
- If a new app needs to use Mail, Calendar, Analytics, or similar first-party
|
|
38
|
+
data/agents, build only the genuinely new workflow and delegate/link to those
|
|
39
|
+
existing apps. Do not create wrapper apps, child apps, nested template copies,
|
|
40
|
+
or cloned Mail/Calendar/Analytics implementations inside the new app just to
|
|
41
|
+
provide access.
|
|
42
|
+
- Only create a first-party app copy when the user explicitly asks for a
|
|
43
|
+
customized fork/copy of that app. Otherwise prefer the hosted/shared app so
|
|
44
|
+
base template improvements continue to flow automatically.
|
|
32
45
|
- Workspace apps are discovered from `apps/<app-id>/package.json`. There is no
|
|
33
46
|
separate workspace app registry to edit for Dispatch to list the app.
|
|
34
47
|
- Use relative workspace links like `/<app-id>`. Never hardcode
|
|
@@ -84,6 +84,12 @@ separate workspace app registry to edit. React Router apps must preserve
|
|
|
84
84
|
For requests phrased as creating an "agent", classify the scope first: simple
|
|
85
85
|
recurring Dispatch behavior can stay in Dispatch, while a robust app-like
|
|
86
86
|
teammate should become a real workspace app listed with the rest of the apps.
|
|
87
|
+
First-party apps such as Mail, Calendar, Analytics, and Dispatch should be
|
|
88
|
+
treated as existing hosted or connected neighbors. If a new app needs access to
|
|
89
|
+
their data or agents, link/delegate to those apps through the workspace/A2A
|
|
90
|
+
path rather than creating wrapper apps, child apps, or cloned template copies
|
|
91
|
+
inside the new app. Only fork one of those apps when the user explicitly asks
|
|
92
|
+
for a customized copy.
|
|
87
93
|
|
|
88
94
|
## Editing shared behavior
|
|
89
95
|
|