@agent-native/core 0.101.5 → 0.101.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +6 -0
- package/corpus/core/docs/content/locales/ar-SA/pure-agent-apps.mdx +2 -2
- package/corpus/core/docs/content/locales/de-DE/pure-agent-apps.mdx +2 -2
- package/corpus/core/docs/content/locales/es-ES/pure-agent-apps.mdx +2 -2
- package/corpus/core/docs/content/locales/fr-FR/pure-agent-apps.mdx +2 -2
- package/corpus/core/docs/content/locales/hi-IN/pure-agent-apps.mdx +2 -2
- package/corpus/core/docs/content/locales/ja-JP/pure-agent-apps.mdx +2 -2
- package/corpus/core/docs/content/locales/ko-KR/pure-agent-apps.mdx +2 -2
- package/corpus/core/docs/content/locales/pt-BR/pure-agent-apps.mdx +2 -2
- package/corpus/core/docs/content/locales/zh-CN/pure-agent-apps.mdx +2 -2
- package/corpus/core/docs/content/locales/zh-TW/pure-agent-apps.mdx +2 -2
- package/corpus/core/docs/content/pure-agent-apps.mdx +58 -14
- package/corpus/core/package.json +1 -1
- package/corpus/templates/analytics/AGENTS.md +6 -0
- package/corpus/templates/analytics/README.md +1 -1
- package/corpus/templates/analytics/actions/data-source-status.ts +19 -1
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +5 -1
- package/corpus/templates/analytics/app/routes/_index.tsx +2 -2
- package/corpus/templates/analytics/changelog/2026-07-14-analytics-chat-guides-you-to-connect-a-data-source-with-a-di.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-14-analytics-landing-pages-now-clearly-position-the-app-as-an-o.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-15-daily-dashboard-emails-now-include-the-complete-dashboard-sc.md +6 -0
- package/corpus/templates/analytics/server/jobs/dashboard-report.ts +8 -1
- package/corpus/templates/analytics/server/lib/dashboard-report.ts +64 -12
- package/corpus/templates/analytics/server/lib/real-data-actions.ts +15 -3
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +364 -4
- package/corpus/templates/clips/app/components/dictate/vocabulary-section.tsx +1 -0
- package/corpus/templates/clips/app/components/library/folder-tree.tsx +12 -9
- package/corpus/templates/clips/app/components/library/library-layout.tsx +19 -16
- package/corpus/templates/clips/app/routes/_app.spaces._index.tsx +13 -9
- package/corpus/templates/clips/changelog/2026-07-15-folders-and-sidebar-links-are-clickable-again-immediately-af.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-15-hovering-a-truncated-folder-name-now-shows-the-full-name.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-15-space-creation-controls-are-now-shown-only-to-organization-a.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-15-vocabulary-terms-can-now-be-deleted-from-the-dictation-dicti.md +6 -0
- package/corpus/templates/clips/desktop/src/styles.css +23 -1
- package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +49 -7
- package/corpus/templates/clips/server/lib/recordings.ts +5 -2
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +3 -3
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/docs/content/locales/ar-SA/pure-agent-apps.mdx +2 -2
- package/docs/content/locales/de-DE/pure-agent-apps.mdx +2 -2
- package/docs/content/locales/es-ES/pure-agent-apps.mdx +2 -2
- package/docs/content/locales/fr-FR/pure-agent-apps.mdx +2 -2
- package/docs/content/locales/hi-IN/pure-agent-apps.mdx +2 -2
- package/docs/content/locales/ja-JP/pure-agent-apps.mdx +2 -2
- package/docs/content/locales/ko-KR/pure-agent-apps.mdx +2 -2
- package/docs/content/locales/pt-BR/pure-agent-apps.mdx +2 -2
- package/docs/content/locales/zh-CN/pure-agent-apps.mdx +2 -2
- package/docs/content/locales/zh-TW/pure-agent-apps.mdx +2 -2
- package/docs/content/pure-agent-apps.mdx +58 -14
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -91,7 +91,7 @@ Edit local Markdown/MDX files, generate rich interactive custom blocks, and draf
|
|
|
91
91
|
|
|
92
92
|
<a href="https://agent-native.com/templates/analytics"><img src="https://cdn.builder.io/api/v1/image/assets%2FYJIGb4i01jvw0SRdL5Bt%2F4933a80cc3134d7e874631f688be828a?format=webp&width=800" alt="Analytics app" width="100%" /></a>
|
|
93
93
|
|
|
94
|
-
**
|
|
94
|
+
**Open-Source Alternative to Amplitude and FullStory**
|
|
95
95
|
|
|
96
96
|
Connect analytics data sources, prompt for real charts, and build reusable dashboards.
|
|
97
97
|
|
package/corpus/README.md
CHANGED
package/corpus/core/CHANGELOG.md
CHANGED
|
@@ -68,9 +68,9 @@ Automation-first app هو Agent-Native app بلا متصفح من أجل schedul
|
|
|
68
68
|
|
|
69
69
|
→ [**Agent Surfaces — Headless agent**](/docs/agent-surfaces#headless)
|
|
70
70
|
|
|
71
|
-
## أضف
|
|
71
|
+
## أضف واجهة مستخدم لاحقاً {#add-chat-later}
|
|
72
72
|
|
|
73
|
-
يختار `--headless` السقالة الأولية فقط، ولا يوجد أمر `agent-native` يحوّل مشروعاً قائماً بلا واجهة إلى تطبيق
|
|
73
|
+
يختار `--headless` السقالة الأولية فقط، ولا يوجد أمر `agent-native` يحوّل مشروعاً قائماً بلا واجهة إلى تطبيق متصفح في مكانه. عندما يحتاج الأشخاص إلى واجهة مستخدم، استخدم [قالب Chat](/docs/template-chat) كمرجع للترحيل: احتفظ بالـ actions وبيانات SQL والتعليمات والمهارات، ثم أضف shell المتصفح وserver plugins. وإذا كانت الواجهة محادثة، أضف `<AgentChatSurface>` وplugin للدردشة يحمّل actions الحالية. تحقّق من `pnpm typecheck` ومن تدفق واجهة يستدعي action قائمة. استخدم `agent-native add` لمخططات التكامل، لا لهذا التحويل.
|
|
74
74
|
|
|
75
75
|
## ما هي الخطوة التالية
|
|
76
76
|
|
|
@@ -70,9 +70,9 @@ Die Box, das Gerüst, der Repo-Zugriff und die Lauffreigabe sind jetzt an einem
|
|
|
70
70
|
|
|
71
71
|
→ [**Agent Surfaces — Headless agent**](/docs/agent-surfaces#headless)
|
|
72
72
|
|
|
73
|
-
##
|
|
73
|
+
## Später eine UI hinzufügen {#add-chat-later}
|
|
74
74
|
|
|
75
|
-
`--headless` wählt nur das anfängliche Scaffold; es gibt keinen `agent-native`-Befehl, der ein bestehendes action-only-Projekt direkt in eine
|
|
75
|
+
`--headless` wählt nur das anfängliche Scaffold; es gibt keinen `agent-native`-Befehl, der ein bestehendes action-only-Projekt direkt in eine Browser-App umwandelt. Wenn Menschen eine UI brauchen, nutze das [Chat-Template](/docs/template-chat) als Migrationsreferenz: Behalte Actions, SQL-Daten, Anweisungen und Skills bei und übernimm Browser-Shell und Server-Plugins. Für eine Chat-Oberfläche kommen `<AgentChatSurface>` und ein Chat-Plugin hinzu, das die vorhandenen Actions lädt. Prüfe `pnpm typecheck` und einen UI-Ablauf, der eine vorhandene Action aufruft. `agent-native add` ist für Integrations-Blaupausen gedacht, nicht für diese Umwandlung.
|
|
76
76
|
|
|
77
77
|
## Was kommt als nächstes
|
|
78
78
|
|
|
@@ -70,9 +70,9 @@ la caja, el andamio, el acceso al repositorio y el uso compartido de ejecuciones
|
|
|
70
70
|
|
|
71
71
|
→ [**Agent Surfaces — Headless agent**](/docs/agent-surfaces#headless)
|
|
72
72
|
|
|
73
|
-
## Añade
|
|
73
|
+
## Añade una UI más adelante {#add-chat-later}
|
|
74
74
|
|
|
75
|
-
`--headless` solo elige el scaffold inicial; no hay un comando de `agent-native` que convierta en el sitio un proyecto existente solo de acciones en una app de
|
|
75
|
+
`--headless` solo elige el scaffold inicial; no hay un comando de `agent-native` que convierta en el sitio un proyecto existente solo de acciones en una app de navegador. Cuando las personas necesiten una UI, usa la [plantilla Chat](/docs/template-chat) como referencia de migración: conserva las actions, los datos SQL, las instrucciones y las skills, y añade el shell del navegador y los plugins de servidor. Para una superficie de chat, añade `<AgentChatSurface>` y un plugin de chat que cargue las actions existentes. Comprueba `pnpm typecheck` y un flujo de UI que llame una action existente. `agent-native add` es para planos de integración, no para esta conversión.
|
|
76
76
|
|
|
77
77
|
## ¿Qué sigue?
|
|
78
78
|
|
|
@@ -68,9 +68,9 @@ la boîte, l'échafaudage, l'accès au dépôt et le partage d'exécution se tro
|
|
|
68
68
|
|
|
69
69
|
→ [**Agent Surfaces — Headless agent**](/docs/agent-surfaces#headless)
|
|
70
70
|
|
|
71
|
-
## Ajouter
|
|
71
|
+
## Ajouter une UI plus tard {#add-chat-later}
|
|
72
72
|
|
|
73
|
-
`--headless` choisit seulement le scaffold initial ; aucune commande `agent-native` ne convertit directement un projet existant fondé uniquement sur des actions en application
|
|
73
|
+
`--headless` choisit seulement le scaffold initial ; aucune commande `agent-native` ne convertit directement un projet existant fondé uniquement sur des actions en application navigateur. Quand les personnes ont besoin d'une UI, utilisez le [modèle Chat](/docs/template-chat) comme référence de migration : conservez les actions, les données SQL, les instructions et les skills, puis ajoutez le shell navigateur et les plugins serveur. Pour une surface de chat, ajoutez `<AgentChatSurface>` et un plugin de chat qui charge les actions existantes. Vérifiez `pnpm typecheck` et un flux UI appelant une action existante. `agent-native add` sert aux blueprints d'intégration, pas à cette conversion.
|
|
74
74
|
|
|
75
75
|
## Quelle est la prochaine étape
|
|
76
76
|
|
|
@@ -68,9 +68,9 @@ Automation-first app scheduled jobs, queues, scripts, integration workers और
|
|
|
68
68
|
|
|
69
69
|
→ [**Agent Surfaces — Headless agent**](/docs/agent-surfaces#headless)
|
|
70
70
|
|
|
71
|
-
## बाद में
|
|
71
|
+
## बाद में UI जोड़ें {#add-chat-later}
|
|
72
72
|
|
|
73
|
-
`--headless` केवल शुरुआती scaffold चुनता है; कोई `agent-native` command किसी मौजूदा action-only project को in-place
|
|
73
|
+
`--headless` केवल शुरुआती scaffold चुनता है; कोई `agent-native` command किसी मौजूदा action-only project को in-place browser app में नहीं बदलती। जब लोगों को UI चाहिए, [Chat template](/docs/template-chat) को migration reference की तरह उपयोग करें: actions, SQL data, instructions और skills रखें, फिर browser shell और server plugins जोड़ें। Chat surface के लिए `<AgentChatSurface>` और मौजूदा actions लोड करने वाला chat plugin भी जोड़ें। `pnpm typecheck` और एक ऐसा UI flow जाँचें जो किसी मौजूदा action को call करे। `agent-native add` integration blueprints के लिए है, इस conversion के लिए नहीं।
|
|
74
74
|
|
|
75
75
|
## आगे क्या है
|
|
76
76
|
|
|
@@ -68,9 +68,9 @@ Automation-first app は、scheduled jobs、queues、scripts、integration worke
|
|
|
68
68
|
|
|
69
69
|
→ [**Agent Surfaces — Headless agent**](/docs/agent-surfaces#headless)
|
|
70
70
|
|
|
71
|
-
##
|
|
71
|
+
## 後から UI を追加する {#add-chat-later}
|
|
72
72
|
|
|
73
|
-
`--headless` が選ぶのは初期 scaffold だけで、既存の action-only
|
|
73
|
+
`--headless` が選ぶのは初期 scaffold だけで、既存の action-only プロジェクトをその場でブラウザーアプリに変換する `agent-native` コマンドはありません。UI が必要になったら、移行の参照として [Chat template](/docs/template-chat) を使います。actions、SQL データ、instructions、skills は保持し、browser shell と server plugins を追加してください。チャット surface には `<AgentChatSurface>` と既存 actions を読み込む chat plugin も追加します。`pnpm typecheck` と既存 action を呼ぶ UI フローを確認します。`agent-native add` は integration blueprints 用であり、この変換用ではありません。
|
|
74
74
|
|
|
75
75
|
## 次は何ですか
|
|
76
76
|
|
|
@@ -66,9 +66,9 @@ Automation-first app은 scheduled jobs, queues, scripts, integration workers, ex
|
|
|
66
66
|
|
|
67
67
|
→ [**Agent Surfaces — Headless agent**](/docs/agent-surfaces#headless)
|
|
68
68
|
|
|
69
|
-
## 나중에
|
|
69
|
+
## 나중에 UI 추가하기 {#add-chat-later}
|
|
70
70
|
|
|
71
|
-
`--headless`는 초기 scaffold만 선택하며, 기존 action-only 프로젝트를 제자리에서
|
|
71
|
+
`--headless`는 초기 scaffold만 선택하며, 기존 action-only 프로젝트를 제자리에서 브라우저 앱으로 변환하는 `agent-native` 명령은 없습니다. UI가 필요해지면 [Chat 템플릿](/docs/template-chat)을 마이그레이션 기준으로 사용하세요. actions, SQL 데이터, instructions, skills를 유지하고 browser shell과 server plugins를 추가합니다. Chat surface에는 `<AgentChatSurface>`와 기존 actions를 로드하는 chat plugin도 추가합니다. `pnpm typecheck`와 기존 action을 호출하는 UI flow를 확인하세요. `agent-native add`는 이 변환이 아니라 integration blueprints용입니다.
|
|
72
72
|
|
|
73
73
|
## 다음 단계
|
|
74
74
|
|
|
@@ -68,9 +68,9 @@ a caixa, o scaffold, o acesso ao repositório e o compartilhamento de execução
|
|
|
68
68
|
|
|
69
69
|
→ [**Agent Surfaces — Headless agent**](/docs/agent-surfaces#headless)
|
|
70
70
|
|
|
71
|
-
## Adicione
|
|
71
|
+
## Adicione uma UI depois {#add-chat-later}
|
|
72
72
|
|
|
73
|
-
`--headless` escolhe somente o scaffold inicial; não há um comando `agent-native` que converta no lugar um projeto existente apenas com actions em um app de
|
|
73
|
+
`--headless` escolhe somente o scaffold inicial; não há um comando `agent-native` que converta no lugar um projeto existente apenas com actions em um app de navegador. Quando as pessoas precisarem de uma UI, use o [template Chat](/docs/template-chat) como referência de migração: mantenha as actions, os dados SQL, as instructions e as skills e adicione o shell do navegador e os server plugins. Para uma superfície de chat, adicione `<AgentChatSurface>` e um chat plugin que carregue as actions existentes. Verifique `pnpm typecheck` e um fluxo de UI que chame uma action existente. `agent-native add` é para integration blueprints, não para essa conversão.
|
|
74
74
|
|
|
75
75
|
## O que vem a seguir
|
|
76
76
|
|
|
@@ -66,9 +66,9 @@ Automation-first app 是无浏览器的 Agent-Native app,用于 scheduled jobs
|
|
|
66
66
|
|
|
67
67
|
→ [**Agent Surfaces — Headless agent**](/docs/agent-surfaces#headless)
|
|
68
68
|
|
|
69
|
-
##
|
|
69
|
+
## 稍后添加 UI {#add-chat-later}
|
|
70
70
|
|
|
71
|
-
`--headless` 只选择初始 scaffold;没有可将现有仅 actions
|
|
71
|
+
`--headless` 只选择初始 scaffold;没有可将现有仅 actions 项目原地转换为浏览器应用的 `agent-native` 命令。当用户需要 UI 时,请将 [Chat 模板](/docs/template-chat) 用作迁移参考:保留 actions、SQL 数据、instructions 和 skills,并添加浏览器 shell 和 server plugins。对于聊天界面,再添加 `<AgentChatSurface>` 以及加载现有 actions 的 chat plugin。验证 `pnpm typecheck`,并完成一次调用现有 action 的 UI 流程。`agent-native add` 用于 integration blueprints,不用于此转换。
|
|
72
72
|
|
|
73
73
|
## 下一步是什么
|
|
74
74
|
|
|
@@ -66,9 +66,9 @@ Automation-first app 是無瀏覽器的 Agent-Native app,用於 scheduled jobs
|
|
|
66
66
|
|
|
67
67
|
→ [**Agent Surfaces — Headless agent**](/docs/agent-surfaces#headless)
|
|
68
68
|
|
|
69
|
-
##
|
|
69
|
+
## 稍後新增 UI {#add-chat-later}
|
|
70
70
|
|
|
71
|
-
`--headless` 只會選擇初始 scaffold;沒有可將現有僅 actions
|
|
71
|
+
`--headless` 只會選擇初始 scaffold;沒有可將現有僅 actions 專案原地轉換為瀏覽器應用程式的 `agent-native` 命令。當使用者需要 UI 時,請將 [Chat 範本](/docs/template-chat) 作為遷移參考:保留 actions、SQL 資料、instructions 和 skills,並新增瀏覽器 shell 與 server plugins。對於聊天介面,再新增 `<AgentChatSurface>` 與載入現有 actions 的 chat plugin。請驗證 `pnpm typecheck`,並完成一次呼叫現有 action 的 UI 流程。`agent-native add` 用於 integration blueprints,不用於此轉換。
|
|
72
72
|
|
|
73
73
|
## 下一步是什麼
|
|
74
74
|
|
|
@@ -95,22 +95,66 @@ The `--headless` flag only chooses the initial scaffold. It does not choose a
|
|
|
95
95
|
different architecture. You can later move the same `actions/`, instructions,
|
|
96
96
|
skills, and SQL state into a chat app or full application surface.
|
|
97
97
|
|
|
98
|
-
## Add
|
|
98
|
+
## Add a UI later {#add-chat-later}
|
|
99
99
|
|
|
100
100
|
`--headless` is not a permanent runtime mode, and there is no `agent-native`
|
|
101
|
-
command that converts an existing action-only project into a
|
|
102
|
-
When people need a
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
101
|
+
command that converts an existing action-only project into a browser app in
|
|
102
|
+
place. When people need a UI, use the [Chat template](/docs/template-chat) as
|
|
103
|
+
the migration reference and keep the durable action and database contract:
|
|
104
|
+
|
|
105
|
+
<Diagram id="doc-block-ui-later" title="One contract, multiple surfaces" summary={"Start with a headless workflow, then add a browser UI without rewriting the actions, data, instructions, or skills underneath."}>
|
|
106
|
+
|
|
107
|
+
```html
|
|
108
|
+
<div class="diagram-ui-path">
|
|
109
|
+
<div class="diagram-panel" data-rough>
|
|
110
|
+
<span class="diagram-pill accent">Start headless</span
|
|
111
|
+
><small class="diagram-muted">jobs · CLI · external agents</small>
|
|
112
|
+
</div>
|
|
113
|
+
<div class="diagram-arrow diagram-muted" aria-hidden="true">→</div>
|
|
114
|
+
<div class="diagram-panel" data-rough>
|
|
115
|
+
<span class="diagram-pill">Add a UI</span
|
|
116
|
+
><small class="diagram-muted">chat · routes · durable pages</small>
|
|
117
|
+
</div>
|
|
118
|
+
<div class="diagram-arrow diagram-muted" aria-hidden="true">→</div>
|
|
119
|
+
<div class="diagram-panel ok" data-rough>
|
|
120
|
+
<span class="diagram-pill ok">Keep the contract</span
|
|
121
|
+
><small class="diagram-muted">actions · SQL · instructions · skills</small>
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
```css
|
|
127
|
+
.diagram-ui-path {
|
|
128
|
+
display: flex;
|
|
129
|
+
align-items: center;
|
|
130
|
+
justify-content: center;
|
|
131
|
+
gap: 14px;
|
|
132
|
+
flex-wrap: wrap;
|
|
133
|
+
}
|
|
134
|
+
.diagram-ui-path .diagram-panel {
|
|
135
|
+
display: flex;
|
|
136
|
+
min-width: 150px;
|
|
137
|
+
flex-direction: column;
|
|
138
|
+
align-items: center;
|
|
139
|
+
gap: 4px;
|
|
140
|
+
text-align: center;
|
|
141
|
+
}
|
|
142
|
+
.diagram-ui-path .diagram-arrow {
|
|
143
|
+
font-size: 22px;
|
|
144
|
+
line-height: 1;
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
</Diagram>
|
|
149
|
+
|
|
150
|
+
1. Scaffold the first workflow with `npx @agent-native/core@latest create my-agent --headless`.
|
|
151
|
+
2. When a browser surface becomes useful, bring in the template's shell, server
|
|
152
|
+
plugins, and UI dependencies. For chat, that includes `<AgentChatSurface>`
|
|
153
|
+
and an `agent-chat` plugin that loads the existing action registry.
|
|
154
|
+
3. Keep your action names, Zod schemas, SQL data, instructions, and skills. A
|
|
155
|
+
UI calls those same actions; it does not replace them.
|
|
156
|
+
4. Verify both surfaces with `pnpm typecheck`, `pnpm dev`, and one UI flow that
|
|
157
|
+
calls an existing action.
|
|
114
158
|
|
|
115
159
|
Use `agent-native add` for integration blueprints, not for this conversion.
|
|
116
160
|
Choose chat when people need conversational steering, approvals, or durable
|
package/corpus/core/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.101.
|
|
3
|
+
"version": "0.101.6",
|
|
4
4
|
"description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
|
|
5
5
|
"homepage": "https://github.com/BuilderIO/agent-native#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -20,6 +20,12 @@ details live in `.agents/skills/`.
|
|
|
20
20
|
status) and call `list-data-dictionary` with a focused search to learn what
|
|
21
21
|
exists and which table/columns/join paths to use. Don't fan out blind queries
|
|
22
22
|
when the catalog already answers where a fact lives.
|
|
23
|
+
- `data-source-status` always includes the built-in first-party Analytics source
|
|
24
|
+
and returns `hasConnectedExternalDataSources`,
|
|
25
|
+
`dataSourcesSetupLink` (a real deep link to `/data-sources`). Chat stays
|
|
26
|
+
available when no external provider is connected; for a request that needs
|
|
27
|
+
one, explain the missing source in context and include that returned link so
|
|
28
|
+
the user can connect it. Do not replace this with a generic canned response.
|
|
23
29
|
- Simple time-bounded metric fast path: when the data dictionary or a known
|
|
24
30
|
canonical source already identifies the metric, run one bounded aggregate.
|
|
25
31
|
Once that query returns a valid result, answer immediately with the source,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Analytics
|
|
2
2
|
|
|
3
|
-
An open-source, agent-native alternative to Amplitude
|
|
3
|
+
An open-source, agent-native alternative to Amplitude and FullStory. Ask
|
|
4
4
|
analytics questions in plain English and get charts, dashboards, and re-runnable
|
|
5
5
|
investigations back — you own the code, the queries, and the data.
|
|
6
6
|
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
getWorkspaceConnectionProvider,
|
|
4
4
|
listWorkspaceConnectionProvidersForTemplate,
|
|
5
5
|
} from "@agent-native/core/connections";
|
|
6
|
+
import { buildDeepLink } from "@agent-native/core/server";
|
|
6
7
|
import {
|
|
7
8
|
listWorkspaceConnectionGrants,
|
|
8
9
|
listWorkspaceConnections,
|
|
@@ -22,6 +23,11 @@ import { getGitHubAccessToken } from "../server/lib/github-oauth";
|
|
|
22
23
|
import { resolveAnalyticsProviderCredential } from "../server/lib/provider-credentials";
|
|
23
24
|
|
|
24
25
|
const APP_ID = "analytics";
|
|
26
|
+
const DATA_SOURCES_SETUP_LINK = buildDeepLink({
|
|
27
|
+
app: APP_ID,
|
|
28
|
+
view: "data-sources",
|
|
29
|
+
to: "/data-sources",
|
|
30
|
+
});
|
|
25
31
|
|
|
26
32
|
const BUILT_IN_FIRST_PARTY_PROVIDER = {
|
|
27
33
|
provider: "first-party",
|
|
@@ -68,7 +74,7 @@ async function listWorkspaceConnectionsForStatus(): Promise<{
|
|
|
68
74
|
|
|
69
75
|
export default defineAction({
|
|
70
76
|
description:
|
|
71
|
-
"List which analytics data sources are available without revealing secret values. This always includes the built-in first-party Analytics event store, which is queried with `query-agent-native-analytics`; it also reports configured credentials and granted workspace connections. The `key` arg accepts exact credential names like JIRA_API_TOKEN and provider aliases like jira, pylon, bigquery, github, hubspot, gong, or slack.",
|
|
77
|
+
"List which analytics data sources are available without revealing secret values. This always includes the built-in first-party Analytics event store, which is queried with `query-agent-native-analytics`; it also reports configured credentials and granted workspace connections. The result includes `hasConnectedExternalDataSources`, `connectedExternalDataSourceCount`, and `dataSourcesSetupLink`; when a requested provider is unavailable, use that link for contextual setup guidance. The `key` arg accepts exact credential names like JIRA_API_TOKEN and provider aliases like jira, pylon, bigquery, github, hubspot, gong, or slack.",
|
|
72
78
|
schema: z.object({
|
|
73
79
|
key: z
|
|
74
80
|
.string()
|
|
@@ -87,6 +93,7 @@ export default defineAction({
|
|
|
87
93
|
label: "Authentication",
|
|
88
94
|
message: "Sign in to view credential status.",
|
|
89
95
|
settingsPath: "/data-sources",
|
|
96
|
+
dataSourcesSetupLink: DATA_SOURCES_SETUP_LINK,
|
|
90
97
|
};
|
|
91
98
|
}
|
|
92
99
|
|
|
@@ -221,12 +228,18 @@ export default defineAction({
|
|
|
221
228
|
: "credentials",
|
|
222
229
|
})),
|
|
223
230
|
];
|
|
231
|
+
const connectedExternalDataSources = configuredDataSources.filter(
|
|
232
|
+
(source) => source.provider !== BUILT_IN_FIRST_PARTY_PROVIDER.provider,
|
|
233
|
+
);
|
|
224
234
|
return {
|
|
225
235
|
// Keep a compact, explicit summary first so models do not infer source
|
|
226
236
|
// availability from the much larger per-credential list below.
|
|
227
237
|
hasConfiguredDataSources: configuredDataSources.length > 0,
|
|
228
238
|
configuredDataSourceCount: configuredDataSources.length,
|
|
229
239
|
configuredDataSources,
|
|
240
|
+
hasConnectedExternalDataSources: connectedExternalDataSources.length > 0,
|
|
241
|
+
connectedExternalDataSourceCount: connectedExternalDataSources.length,
|
|
242
|
+
dataSourcesSetupLink: DATA_SOURCES_SETUP_LINK,
|
|
230
243
|
credentials: results,
|
|
231
244
|
providers: [BUILT_IN_FIRST_PARTY_PROVIDER, ...providers],
|
|
232
245
|
total: results.length,
|
|
@@ -238,4 +251,9 @@ export default defineAction({
|
|
|
238
251
|
},
|
|
239
252
|
};
|
|
240
253
|
},
|
|
254
|
+
link: () => ({
|
|
255
|
+
url: DATA_SOURCES_SETUP_LINK,
|
|
256
|
+
label: "Open Analytics data sources",
|
|
257
|
+
view: "data-sources",
|
|
258
|
+
}),
|
|
241
259
|
});
|
|
@@ -1147,12 +1147,16 @@ export default function SqlDashboardPage() {
|
|
|
1147
1147
|
}, [dashboard]);
|
|
1148
1148
|
|
|
1149
1149
|
const requestedTab = searchParams.get("tab");
|
|
1150
|
-
const
|
|
1150
|
+
const selectedTab =
|
|
1151
1151
|
tabs.length > 0
|
|
1152
1152
|
? requestedTab && tabs.includes(requestedTab)
|
|
1153
1153
|
? requestedTab
|
|
1154
1154
|
: tabs[0]
|
|
1155
1155
|
: null;
|
|
1156
|
+
// Report captures need the complete dashboard in one image. The report
|
|
1157
|
+
// URL intentionally has no `tab` parameter, so do not apply the normal
|
|
1158
|
+
// first-tab selection while rendering the screenshot surface.
|
|
1159
|
+
const activeTab = reportScreenshot ? null : selectedTab;
|
|
1156
1160
|
const groupedTabs = useMemo(() => groupDashboardTabs(tabs), [tabs]);
|
|
1157
1161
|
const activeTabGroup = activeTab
|
|
1158
1162
|
? groupedTabs.groups.find((group) =>
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { redirect, type LoaderFunctionArgs } from "react-router";
|
|
2
2
|
|
|
3
3
|
const SEO_TITLE =
|
|
4
|
-
"Agent-Native Analytics - Open Source
|
|
4
|
+
"Agent-Native Analytics - Open Source Alternative to Amplitude & FullStory";
|
|
5
5
|
const SEO_DESCRIPTION =
|
|
6
|
-
"Open Source analytics app where AI agents connect to warehouses, product analytics, and CRM data to answer questions and build dashboards.";
|
|
6
|
+
"Open Source analytics app and alternative to Amplitude and FullStory where AI agents connect to warehouses, product analytics, and CRM data to answer questions and build dashboards.";
|
|
7
7
|
|
|
8
8
|
export function meta() {
|
|
9
9
|
return [
|
|
@@ -51,6 +51,7 @@ export async function runDashboardReportsOnce(): Promise<{
|
|
|
51
51
|
() =>
|
|
52
52
|
sendDashboardReportSubscription(sub, {
|
|
53
53
|
skipEmailWithoutScreenshot: retryAt !== null,
|
|
54
|
+
allowLimitedFallback: retryAt === null,
|
|
54
55
|
}),
|
|
55
56
|
);
|
|
56
57
|
if (!result.screenshotAttached) {
|
|
@@ -78,7 +79,13 @@ export async function runDashboardReportsOnce(): Promise<{
|
|
|
78
79
|
await markDashboardReportResult(sub, "error", message);
|
|
79
80
|
continue;
|
|
80
81
|
}
|
|
81
|
-
|
|
82
|
+
if (result.screenshotMode !== "full" && result.screenshotError) {
|
|
83
|
+
const detail = `sent with ${result.screenshotMode} screenshot; earlier attempts failed: ${result.screenshotError}`;
|
|
84
|
+
console.warn(`[dashboard-report] Subscription ${sub.id} ${detail}`);
|
|
85
|
+
await markDashboardReportResult(sub, "success", detail);
|
|
86
|
+
} else {
|
|
87
|
+
await markDashboardReportResult(sub, "success");
|
|
88
|
+
}
|
|
82
89
|
} catch (err: any) {
|
|
83
90
|
failed++;
|
|
84
91
|
const message = err?.message ?? String(err);
|
|
@@ -49,13 +49,17 @@ const DASHBOARD_REPORT_CID = "dashboard-report-snapshot";
|
|
|
49
49
|
const LOCAL_SCREENSHOT_TIMEOUT_MS = 90_000;
|
|
50
50
|
const SERVERLESS_SCREENSHOT_TIMEOUT_MS = 90_000;
|
|
51
51
|
const SERVERLESS_SECOND_READY_TIMEOUT_MS = 45_000;
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
// Keep enough room under Netlify's 300s background-function limit for the
|
|
53
|
+
// bounded browser-close/profile-cleanup steps after each failed attempt and
|
|
54
|
+
// for the final email send. The three attempts total 225s; cleanup can spend
|
|
55
|
+
// up to 60s, leaving a small delivery buffer.
|
|
56
|
+
const SERVERLESS_FULL_ATTEMPT_TIMEOUT_MS = 110_000;
|
|
57
|
+
const SERVERLESS_LIGHTWEIGHT_ATTEMPT_TIMEOUT_MS = 70_000;
|
|
54
58
|
const BROWSER_CLEANUP_TIMEOUT_MS = 10_000;
|
|
55
59
|
const SCREENSHOT_VIEWPORT_PADDING = 64;
|
|
56
60
|
|
|
57
61
|
type DashboardScreenshotAttempt = {
|
|
58
|
-
label: "full" | "full-lightweight";
|
|
62
|
+
label: "full" | "full-lightweight" | "limited";
|
|
59
63
|
viewport: { width: number; height: number };
|
|
60
64
|
captureScale?: number;
|
|
61
65
|
readyTimeout?: number;
|
|
@@ -770,9 +774,10 @@ function storedAttemptError(message: string): string {
|
|
|
770
774
|
async function captureDashboardPngWithFallback(
|
|
771
775
|
sub: DashboardReportSubscription,
|
|
772
776
|
snapshot: ReportSnapshot,
|
|
777
|
+
options?: { includeLimitedFallback?: boolean },
|
|
773
778
|
): Promise<{
|
|
774
779
|
png: Buffer | null;
|
|
775
|
-
mode: "full" | "full-lightweight" | "none";
|
|
780
|
+
mode: "full" | "full-lightweight" | "limited" | "none";
|
|
776
781
|
error?: string;
|
|
777
782
|
}> {
|
|
778
783
|
const serverless = isServerlessBrowserRuntime();
|
|
@@ -792,7 +797,6 @@ async function captureDashboardPngWithFallback(
|
|
|
792
797
|
label: "full-lightweight",
|
|
793
798
|
viewport: { width: 1200, height: 1400 },
|
|
794
799
|
captureScale: 0.7,
|
|
795
|
-
reportPanelLimit: 8,
|
|
796
800
|
...(serverless
|
|
797
801
|
? {
|
|
798
802
|
readyTimeout: 55_000,
|
|
@@ -802,13 +806,34 @@ async function captureDashboardPngWithFallback(
|
|
|
802
806
|
: {}),
|
|
803
807
|
},
|
|
804
808
|
];
|
|
809
|
+
if (options?.includeLimitedFallback) {
|
|
810
|
+
// Only added on the final sweep after the 1-hour retry window, when the
|
|
811
|
+
// alternative is a no-image fallback email. All three serverless attempt
|
|
812
|
+
// totalTimeouts sum to 225s, leaving room for bounded cleanup and email
|
|
813
|
+
// delivery under the 300s Netlify background-function timeout.
|
|
814
|
+
attempts.push({
|
|
815
|
+
label: "limited",
|
|
816
|
+
viewport: { width: 1200, height: 1400 },
|
|
817
|
+
captureScale: 0.7,
|
|
818
|
+
reportPanelLimit: 8,
|
|
819
|
+
...(serverless
|
|
820
|
+
? {
|
|
821
|
+
readyTimeout: 40_000,
|
|
822
|
+
secondReadyTimeout: 10_000,
|
|
823
|
+
totalTimeout: 45_000,
|
|
824
|
+
}
|
|
825
|
+
: {}),
|
|
826
|
+
});
|
|
827
|
+
}
|
|
805
828
|
const errors: string[] = [];
|
|
806
829
|
|
|
807
830
|
for (const attempt of attempts) {
|
|
808
831
|
try {
|
|
832
|
+
const png = await captureDashboardPng(sub, snapshot, attempt);
|
|
809
833
|
return {
|
|
810
|
-
png
|
|
834
|
+
png,
|
|
811
835
|
mode: attempt.label,
|
|
836
|
+
...(errors.length ? { error: errors.join(" | ") } : {}),
|
|
812
837
|
};
|
|
813
838
|
} catch (err) {
|
|
814
839
|
const attemptError = errorMessage(err);
|
|
@@ -837,7 +862,10 @@ function reportDate(snapshot: ReportSnapshot): string {
|
|
|
837
862
|
|
|
838
863
|
function renderReportEmailHtml(
|
|
839
864
|
snapshot: ReportSnapshot,
|
|
840
|
-
options: {
|
|
865
|
+
options: {
|
|
866
|
+
screenshotAttached: boolean;
|
|
867
|
+
screenshotMode: "full" | "full-lightweight" | "limited" | "none";
|
|
868
|
+
},
|
|
841
869
|
): string {
|
|
842
870
|
const title = escapeHtml(snapshot.title);
|
|
843
871
|
const dashboardUrl = escapeHtml(snapshot.dashboardUrl);
|
|
@@ -850,6 +878,12 @@ function renderReportEmailHtml(
|
|
|
850
878
|
: `<div style="margin:18px 0;padding:14px 16px;border:1px solid #e5e7eb;border-radius:8px;background:#f9fafb;color:#374151;font-size:14px;line-height:1.5;">
|
|
851
879
|
The dashboard image was unavailable for this run. Open the live dashboard to view the latest report.
|
|
852
880
|
</div>`;
|
|
881
|
+
const limitedScreenshotNotice =
|
|
882
|
+
options.screenshotMode === "limited"
|
|
883
|
+
? `<div style="margin:12px 0 0;padding:12px 14px;border:1px solid #f3c46b;border-radius:8px;background:#fff8e6;color:#6b4f14;font-size:13px;line-height:1.45;">
|
|
884
|
+
This is a limited fallback image and may omit some dashboard panels. <a href="${dashboardUrl}" style="color:#2563eb;text-decoration:none;">Open the full dashboard</a> to see every panel.
|
|
885
|
+
</div>`
|
|
886
|
+
: "";
|
|
853
887
|
|
|
854
888
|
return `<!doctype html>
|
|
855
889
|
<html>
|
|
@@ -858,6 +892,7 @@ function renderReportEmailHtml(
|
|
|
858
892
|
Here's your report of <a href="${dashboardUrl}" style="color:#2563eb;text-decoration:none;">${title}</a> for ${date}
|
|
859
893
|
</p>
|
|
860
894
|
${screenshotBlock}
|
|
895
|
+
${limitedScreenshotNotice}
|
|
861
896
|
<p style="margin:18px 0 0;color:#525866;font-size:13px;line-height:1.45;">
|
|
862
897
|
<a href="${dashboardUrl}" style="color:#2563eb;text-decoration:none;">Open dashboard</a>
|
|
863
898
|
<span style="color:#9ca3af;"> · </span>
|
|
@@ -869,7 +904,10 @@ function renderReportEmailHtml(
|
|
|
869
904
|
|
|
870
905
|
function renderReportText(
|
|
871
906
|
snapshot: ReportSnapshot,
|
|
872
|
-
options: {
|
|
907
|
+
options: {
|
|
908
|
+
screenshotAttached: boolean;
|
|
909
|
+
screenshotMode: "full" | "full-lightweight" | "limited" | "none";
|
|
910
|
+
},
|
|
873
911
|
): string {
|
|
874
912
|
const lines = [
|
|
875
913
|
`Daily dashboard report: ${snapshot.title}`,
|
|
@@ -880,6 +918,11 @@ function renderReportText(
|
|
|
880
918
|
if (!options.screenshotAttached) {
|
|
881
919
|
lines.push("Dashboard image unavailable for this run.");
|
|
882
920
|
}
|
|
921
|
+
if (options.screenshotMode === "limited") {
|
|
922
|
+
lines.push(
|
|
923
|
+
`This is a limited fallback image and may omit some dashboard panels. Open the full dashboard: ${snapshot.dashboardUrl}`,
|
|
924
|
+
);
|
|
925
|
+
}
|
|
883
926
|
return lines.join("\n");
|
|
884
927
|
}
|
|
885
928
|
|
|
@@ -897,17 +940,20 @@ export async function sendDashboardReportSubscription(
|
|
|
897
940
|
options: {
|
|
898
941
|
requireScreenshot?: boolean;
|
|
899
942
|
skipEmailWithoutScreenshot?: boolean;
|
|
943
|
+
allowLimitedFallback?: boolean;
|
|
900
944
|
} = {},
|
|
901
945
|
): Promise<{
|
|
902
946
|
dashboardUrl: string;
|
|
903
947
|
recipientCount: number;
|
|
904
948
|
screenshotAttached: boolean;
|
|
905
|
-
screenshotMode: "full" | "full-lightweight" | "none";
|
|
949
|
+
screenshotMode: "full" | "full-lightweight" | "limited" | "none";
|
|
906
950
|
screenshotError?: string;
|
|
907
951
|
emailsSent: boolean;
|
|
908
952
|
}> {
|
|
909
953
|
const snapshot = await collectReportSnapshot(sub);
|
|
910
|
-
const capture = await captureDashboardPngWithFallback(sub, snapshot
|
|
954
|
+
const capture = await captureDashboardPngWithFallback(sub, snapshot, {
|
|
955
|
+
includeLimitedFallback: options?.allowLimitedFallback,
|
|
956
|
+
});
|
|
911
957
|
if (!capture.png && options.requireScreenshot) {
|
|
912
958
|
throw new Error(
|
|
913
959
|
capture.error
|
|
@@ -926,8 +972,14 @@ export async function sendDashboardReportSubscription(
|
|
|
926
972
|
};
|
|
927
973
|
}
|
|
928
974
|
const screenshotAttached = Boolean(capture.png);
|
|
929
|
-
const html = renderReportEmailHtml(snapshot, {
|
|
930
|
-
|
|
975
|
+
const html = renderReportEmailHtml(snapshot, {
|
|
976
|
+
screenshotAttached,
|
|
977
|
+
screenshotMode: capture.mode,
|
|
978
|
+
});
|
|
979
|
+
const text = renderReportText(snapshot, {
|
|
980
|
+
screenshotAttached,
|
|
981
|
+
screenshotMode: capture.mode,
|
|
982
|
+
});
|
|
931
983
|
const subject = `Daily dashboard: ${snapshot.title}`;
|
|
932
984
|
|
|
933
985
|
for (const to of sub.recipients) {
|