Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
32b6944
feat(core,analytics): final-answer retry hook with real-data guard fo…
steve8708 May 6, 2026
1b28f44
feat(design): generate-design action improvements + TweaksPanel polish
steve8708 May 6, 2026
23903dd
feat(design): MultiScreenCanvas + CanvasCommentPins polish
steve8708 May 6, 2026
a7db2a2
feat(design): DesignCanvas + types polish
steve8708 May 6, 2026
c69d52e
feat(design): DesignEditor expansion
steve8708 May 6, 2026
8454eac
feat(design): design-systems lib tweaks
steve8708 May 6, 2026
cb69297
fix(design): DesignEditor minor adjustment
steve8708 May 6, 2026
31f2720
feat(design): CanvasCommentPins + DesignEditor follow-up tweaks
steve8708 May 6, 2026
788fdb2
feat(calendar): event video conferencing helper + zoom tweaks
steve8708 May 6, 2026
934f00b
feat(calendar): wire video conferencing helper into create/update eve…
steve8708 May 6, 2026
f3f6159
feat(calendar): add get-zoom-status action + view-screen + handlers w…
steve8708 May 6, 2026
aeb0384
feat(calendar): EventDetailPopover follow-up tweaks
steve8708 May 6, 2026
b04eda5
feat(calendar): CreateEventDialog + Settings page tweaks
steve8708 May 6, 2026
4a46c7a
feat(calendar): event-management SKILL + AGENTS docs + CreateEventDia…
steve8708 May 6, 2026
c3a8c08
feat(calendar): more event/dialog/settings/handler tweaks
steve8708 May 6, 2026
bb83b43
feat(core): MultiTabAssistantChat sidebar loading header
steve8708 May 6, 2026
55ebcef
feat(core,clips): builder-browser tweak + clips transcript-panel + re…
steve8708 May 6, 2026
aa11321
feat(core,clips): core-routes plugin tweak + clips regenerate-title +…
steve8708 May 6, 2026
83b1c48
feat(core,clips,mail): AssistantChat + ConnectBuilderCard + SettingsP…
steve8708 May 6, 2026
d41c30f
feat(core,dispatch,clips): ConnectBuilderCard + useBuilderStatus + ex…
steve8708 May 6, 2026
3c65ccb
chore: add changeset for dispatch + sweep AGENTS docs + clips share/e…
steve8708 May 6, 2026
6fbbee0
feat(core,clips): AgentPanel + MultiTabAssistantChat + TiptapComposer…
steve8708 May 6, 2026
3f001f9
feat(core,clips,mail): ExtensionsListPage + ensure-builder-orgs scrip…
steve8708 May 6, 2026
5c53818
feat(calendar,clips,mail): list-events google-api wiring + clips tran…
steve8708 May 6, 2026
3c429bb
feat(calendar,clips): calendar AGENTS docs + agent-chat plugin tweak;…
steve8708 May 6, 2026
27c3eb6
feat(content): get/update document + LinkHoverPreview + VisualEditor …
steve8708 May 6, 2026
0e44244
feat(content): VersionHistoryPanel polish
steve8708 May 6, 2026
8d3d8e4
feat(clips,content,mail): clips r.recordingId polish + content editor…
steve8708 May 6, 2026
40102df
feat(content): VisualEditor + CodeBlockNode + ImageBlock + global.css…
steve8708 May 6, 2026
f98bd7a
feat(core): ConnectBuilderCard polish
steve8708 May 6, 2026
fefdf6e
feat(clips,content): r.recordingId + share.shareId + p.$id route polish
steve8708 May 6, 2026
2580b77
feat(mail): ComposeModal + InlineReplyComposer + use-compose-state po…
steve8708 May 6, 2026
066ae10
feat(mail): add get/update mail-settings actions + manage-draft + han…
steve8708 May 6, 2026
3d796a3
feat(mail): SettingsPage polish
steve8708 May 6, 2026
73e1a50
feat(mail): AGENTS docs + production-system-prompt + agent-chat plugi…
steve8708 May 6, 2026
1814a02
feat(mail): email-drafts skill + AGENTS docs + navigate action + use-…
steve8708 May 6, 2026
8d6d642
feat(mail): get-mail-settings + signature module follow-ups
steve8708 May 6, 2026
1f18354
fix(lint): prettier-format 6 files flagged by CI
steve8708 May 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 11 additions & 12 deletions .agents/skills/extensions/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ a ... extension" (or the older phrasings "make a tool" / "create a tool"):**
Extensions have full access to app data via helpers injected into the iframe:

- `appAction(name, params)` — call any app action
- `appFetch(path, options)` — call any app endpoint
- `appFetch(path, options)` — call allowed framework endpoints under
`/_agent-native/*`
- `dbQuery(sql, args)` — read from SQL
- `dbExec(sql, args)` — write to SQL
- `extensionFetch(url, options)` — call external APIs via proxy. Legacy
Expand Down Expand Up @@ -202,21 +203,19 @@ auto-mounted at `/_agent-native/actions/:name`.
</div>
```

### `appFetch(path, options)` — Call any app endpoint
### `appFetch(path, options)` — Call allowed framework endpoints

General-purpose fetch to any app endpoint (e.g. `/api/emails`,
`/_agent-native/application-state/navigation`). Automatically adds
credentials and JSON content type.
General-purpose fetch to allowed framework endpoints (for example,
`/_agent-native/application-state/navigation`). Automatically adds credentials
and JSON content type. Template `/api/*` routes are intentionally blocked by
the extension bridge; use `appAction(name, params)` for app data instead.

```javascript
// Read application state
const nav = await appFetch('/_agent-native/application-state/navigation');

// Call a custom API route
const data = await appFetch('/api/custom-endpoint', {
method: 'POST',
body: JSON.stringify({ key: 'value' }),
});
// Call a framework route
const nav = await appFetch('/_agent-native/application-state/navigation');
```

### `dbQuery(sql)` — Read from the app's database
Expand Down Expand Up @@ -259,7 +258,7 @@ await dbExec("UPDATE notes SET title = 'Updated Title' WHERE id = 'abc'");
| Helper | Use for | Example |
|--------|---------|---------|
| `appAction(name, params)` | Call app actions (CRUD, queries) | `appAction('list-emails', { view: 'inbox' })` |
| `appFetch(path, options)` | Call any app endpoint | `appFetch('/api/settings')` |
| `appFetch(path, options)` | Call allowed framework endpoints | `appFetch('/_agent-native/application-state/navigation')` |
| `dbQuery(sql)` | Read from the app's SQL database | `dbQuery('SELECT * FROM notes LIMIT 10')` |
| `dbExec(sql)` | Write to the app's SQL database | `dbExec("INSERT INTO notes ...")` |
| `extensionFetch(url, options)` | Call external APIs via proxy (alias `toolFetch`) | `extensionFetch('https://api.github.com/user', { headers: { 'Authorization': 'Bearer ${keys.GITHUB_TOKEN}' } })` |
Expand Down Expand Up @@ -572,7 +571,7 @@ Persistent notes using localStorage -- no API key needed:
- **Keep extensions focused.** One extension, one job. A "GitHub PR Dashboard" should show PRs, not also manage issues.
- **Handle loading and error states.** Always show a loading indicator during fetch and handle failures gracefully.
- **All functions referenced in Alpine expressions must be defined in `x-data`.** If you use `@click="add()"`, there must be an `add()` method in the component's `x-data` object. Undefined references cause runtime errors.
- **Use the right fetch helper.** `appAction()` for app actions, `appFetch()` for app endpoints, `extensionFetch()` for external APIs. Never use raw `fetch()` -- secrets won't be injected and CORS will block external APIs.
- **Use the right fetch helper.** `appAction()` for app actions and app data, `appFetch()` for allowed framework `/_agent-native/*` endpoints, and `extensionFetch()` for external APIs. Never call template `/api/*` routes from an extension and never use raw `fetch()` -- secrets won't be injected and CORS will block external APIs.
- **Single quotes around `${keys.*}`** to prevent browser-side template literal evaluation.
- **Prefer patches over full rewrites** when editing existing extensions. Smaller diffs are less error-prone.

Expand Down
6 changes: 6 additions & 0 deletions .changeset/clear-dispatch-new-app-builder.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@agent-native/core": patch
"@agent-native/dispatch": patch
---

Clarify Dispatch new-app instructions so Builder branches scaffold separate workspace apps instead of editing starter.
5 changes: 5 additions & 0 deletions .changeset/dispatch-app-creation-store-tweaks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@agent-native/dispatch": patch
---

Internal app-creation-store tweaks for spawned dispatch apps.
5 changes: 5 additions & 0 deletions .changeset/real-data-final-guard.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@agent-native/core": patch
---

Add a template hook for retrying guarded final agent answers before they are shown.
5 changes: 5 additions & 0 deletions .changeset/sidebar-loading-header.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@agent-native/core": patch
---

Match the agent sidebar loading header height to the loaded panel header.
24 changes: 13 additions & 11 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,20 +149,22 @@ Extensions are mini sandboxed Alpine.js apps that run inside iframes. The agent

Extensions are 100% self-contained. They have FULL access to app data, external APIs, and their own persistent storage — **without any source code changes, new files, Builder, or schema migrations.**

| Helper | Purpose | Example |
| ------------------------------------------------ | -------------------------- | ------------------------------------------------- |
| `extensionData.set(collection, id, data, opts?)` | Persist data per-extension | `extensionData.set('notes', id, { text: '...' })` |
| `extensionData.list(collection, opts?)` | List persisted items | `extensionData.list('notes', { scope: 'all' })` |
| `extensionData.get(collection, id, opts?)` | Get a single item | `extensionData.get('notes', 'note-1')` |
| `extensionData.remove(collection, id, opts?)` | Delete persisted item | `extensionData.remove('notes', 'note-1')` |
| `appAction(name, params)` | Call any app action | `appAction('list-emails', { view: 'inbox' })` |
| `dbQuery(sql, args)` | Read from SQL | `dbQuery('SELECT * FROM tools')` |
| `dbExec(sql, args)` | Write to SQL | `dbExec('INSERT INTO ...')` |
| `appFetch(path, options)` | Call any app endpoint | `appFetch('/api/settings')` |
| `extensionFetch(url, options)` | External API via proxy | `extensionFetch('https://api.github.com/...')` |
| Helper | Purpose | Example |
| ------------------------------------------------ | --------------------------------------------------------- | --------------------------------------------------------- |
| `extensionData.set(collection, id, data, opts?)` | Persist data per-extension | `extensionData.set('notes', id, { text: '...' })` |
| `extensionData.list(collection, opts?)` | List persisted items | `extensionData.list('notes', { scope: 'all' })` |
| `extensionData.get(collection, id, opts?)` | Get a single item | `extensionData.get('notes', 'note-1')` |
| `extensionData.remove(collection, id, opts?)` | Delete persisted item | `extensionData.remove('notes', 'note-1')` |
| `appAction(name, params)` | Call any app action | `appAction('list-emails', { view: 'inbox' })` |
| `dbQuery(sql, args)` | Read from SQL | `dbQuery('SELECT * FROM tools')` |
| `dbExec(sql, args)` | Write to SQL | `dbExec('INSERT INTO ...')` |
| `appFetch(path, options)` | Call allowed framework endpoints under `/_agent-native/*` | `appFetch('/_agent-native/application-state/navigation')` |
| `extensionFetch(url, options)` | External API via proxy | `extensionFetch('https://api.github.com/...')` |

> Legacy aliases `toolFetch` and `toolData` are still exposed inside the iframe for backward compatibility with existing extension HTML — prefer the `extension*` names in new code.

Use `appAction(name, params)` for template data/actions such as `list-events` or `list-emails`. Extension `appFetch()` is limited to framework `/_agent-native/*` endpoints; template `/api/*` routes are intentionally blocked by the iframe bridge.

**`extensionData` is a built-in per-extension key-value store with user/org scoping.** When a user asks to "add persistence", "save data", or "remember state" in an extension, use `extensionData` — no SQL schema, no new tables, no source code, no Builder. Data is automatically scoped by extension ID. All methods accept an optional `{ scope }` option: `'user'` (default, private), `'org'` (shared with org), or `'all'` (list/get only — returns both).

**NEVER suggest Builder, source code changes, or new files for extension modifications.** All extension changes go through `update-extension-content` (to edit the Alpine.js HTML) or `extensionData` (to persist data).
Expand Down
23 changes: 12 additions & 11 deletions packages/core/docs/content/extensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,21 +134,22 @@ If you want to change something later, just say so: "Add a search box to my cont

Inside the iframe sandbox, every extension has these helpers on `window`:

| Helper | Purpose | Example |
| ------------------------------------------------ | ----------------------------------------------------- | ------------------------------------------------- |
| `appAction(name, params)` | Call any of the host template's actions | `appAction('list-emails', { view: 'inbox' })` |
| `appFetch(path, options)` | Call any app endpoint | `appFetch('/api/settings')` |
| `dbQuery(sql, args)` | Read from SQL (auto-scoped to the user) | `dbQuery('SELECT id, name FROM tools')` |
| `dbExec(sql, args)` | Write to SQL | `dbExec('INSERT INTO ...')` |
| `extensionFetch(url, options)` | Hit external APIs through a secure proxy with secrets | `extensionFetch('https://api.github.com/user')` |
| `extensionData.set(collection, id, data, opts?)` | Persist data per-extension (user / org scoping) | `extensionData.set('notes', id, { text: '...' })` |
| `extensionData.list(collection, opts?)` | List persisted items | `extensionData.list('notes', { scope: 'all' })` |
| `extensionData.get(collection, id, opts?)` | Get a single item | `extensionData.get('notes', 'note-1')` |
| `extensionData.remove(collection, id, opts?)` | Delete a persisted item | `extensionData.remove('notes', 'note-1')` |
| Helper | Purpose | Example |
| ------------------------------------------------ | --------------------------------------------------------- | --------------------------------------------------------- |
| `appAction(name, params)` | Call any of the host template's actions | `appAction('list-emails', { view: 'inbox' })` |
| `appFetch(path, options)` | Call allowed framework endpoints under `/_agent-native/*` | `appFetch('/_agent-native/application-state/navigation')` |
| `dbQuery(sql, args)` | Read from SQL (auto-scoped to the user) | `dbQuery('SELECT id, name FROM tools')` |
| `dbExec(sql, args)` | Write to SQL | `dbExec('INSERT INTO ...')` |
| `extensionFetch(url, options)` | Hit external APIs through a secure proxy with secrets | `extensionFetch('https://api.github.com/user')` |
| `extensionData.set(collection, id, data, opts?)` | Persist data per-extension (user / org scoping) | `extensionData.set('notes', id, { text: '...' })` |
| `extensionData.list(collection, opts?)` | List persisted items | `extensionData.list('notes', { scope: 'all' })` |
| `extensionData.get(collection, id, opts?)` | Get a single item | `extensionData.get('notes', 'note-1')` |
| `extensionData.remove(collection, id, opts?)` | Delete a persisted item | `extensionData.remove('notes', 'note-1')` |

Two rules of thumb:

- **Prefer `appAction` over `dbQuery`.** Actions are the template's official surface — they handle access control, scoping, and validation for you. Reach for raw SQL only when no action fits.
- **Use `appAction` for template data.** Extension `appFetch` is limited to framework `/_agent-native/*` endpoints; template `/api/*` routes are blocked by the iframe bridge.
- **Prefer `extensionData` over making new tables.** Each extension gets its own isolated key-value store. No schema, no migration. Set `{ scope: 'org' }` to share with the user's org, `'user'` (default) for private.

```html
Expand Down
5 changes: 5 additions & 0 deletions packages/core/src/agent/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ export {
type ActionEntry,
type ScriptEntry,
type ProductionAgentOptions,
type AgentLoopFinalResponseGuard,
type AgentLoopFinalResponseGuardContext,
type AgentLoopFinalResponseGuardResult,
type AgentLoopToolCallSummary,
type AgentLoopToolResultSummary,
} from "./production-agent.js";
export {
type ActionTool,
Expand Down
Loading
Loading