@agent-native/core 0.120.0 → 0.120.2
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/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +16 -0
- package/corpus/core/docs/content/extensions.mdx +23 -4
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/production-agent.ts +4 -0
- package/corpus/core/src/agent/run-manager.ts +36 -10
- package/corpus/core/src/agent/types.ts +2 -0
- package/corpus/core/src/client/MultiTabAssistantChat.tsx +45 -0
- package/corpus/core/src/client/extensions/ExtensionsListPage.tsx +16 -3
- package/corpus/core/src/client/settings/ExtensionsSettingsContent.tsx +24 -0
- package/corpus/core/src/client/settings/SettingsPanel.tsx +10 -0
- package/corpus/core/src/client/use-chat-models.ts +45 -0
- package/corpus/core/src/deploy/build.ts +3 -0
- package/corpus/core/src/server/analytics.ts +164 -20
- package/corpus/core/src/server/auth.ts +17 -13
- package/corpus/core/src/server/better-auth-instance.ts +13 -1
- package/corpus/core/src/templates/chat/app/components/layout/Sidebar.tsx +35 -6
- package/corpus/core/src/templates/chat/app/routes/extensions._index.tsx +2 -2
- package/corpus/core/src/templates/chat/netlify.toml +1 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +16 -0
- package/corpus/core/src/vite/client.ts +6 -0
- package/corpus/templates/analytics/.agents/skills/adhoc-analysis/SKILL.md +27 -43
- package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +19 -8
- package/corpus/templates/analytics/.agents/skills/data-querying/SKILL.md +2 -2
- package/corpus/templates/analytics/AGENTS.md +20 -8
- package/corpus/templates/analytics/actions/get-explorer-dashboard.ts +3 -0
- package/corpus/templates/analytics/actions/list-dashboard-usage-stats.ts +140 -46
- package/corpus/templates/analytics/actions/migrate-analytics-artifacts.ts +48 -0
- package/corpus/templates/analytics/actions/save-analysis.ts +2 -3
- package/corpus/templates/analytics/actions/view-screen.ts +1 -1
- package/corpus/templates/analytics/app/components/dashboard/DashboardMetadata.tsx +77 -0
- package/corpus/templates/analytics/app/components/dashboard/DashboardPanelSkeleton.tsx +7 -1
- package/corpus/templates/analytics/app/components/layout/NewDashboardDialog.tsx +1 -0
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +103 -502
- package/corpus/templates/analytics/app/global.css +30 -0
- package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +2 -2
- package/corpus/templates/analytics/app/i18n-data.ts +80 -0
- package/corpus/templates/analytics/app/pages/Agents.tsx +49 -2
- package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/index.tsx +54 -11
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +3 -53
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +23 -25
- package/corpus/templates/analytics/app/pages/analyses/AnalysisDetail.tsx +2 -2
- package/corpus/templates/analytics/app/routes/analyses._index.tsx +3 -2
- package/corpus/templates/analytics/app/routes/extensions._index.tsx +2 -2
- package/corpus/templates/analytics/changelog/2026-07-23-analytics-now-groups-saved-analyses-and-embedded-extensions-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-23-dashboard-menus-show-who-created-and-last-updated-each-dashb.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-23-dashboard-usage-now-loads-reliably-for-organizations-with-ma.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-23-long-dashboard-building-requests-now-continue-without-stoppi.md +6 -0
- package/corpus/templates/analytics/netlify.toml +4 -0
- package/corpus/templates/analytics/server/db/schema.ts +4 -0
- package/corpus/templates/analytics/server/lib/agent-chat-plan-mode.ts +0 -1
- package/corpus/templates/analytics/server/lib/agent-readable-resource-context.ts +1 -0
- package/corpus/templates/analytics/server/lib/migrate-analytics-artifacts.ts +1062 -0
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +6 -52
- package/corpus/templates/analytics/server/plugins/core-routes.ts +1 -1
- package/corpus/templates/analytics/server/plugins/db.ts +10 -0
- package/corpus/templates/assets/actions/generate-image-batch.ts +36 -1
- package/corpus/templates/assets/actions/generate-image.ts +28 -1
- package/corpus/templates/assets/app/components/layout/Sidebar.tsx +47 -9
- package/corpus/templates/assets/app/routes/extensions._index.tsx +2 -2
- package/corpus/templates/assets/changelog/2026-07-23-assets-image-generation-now-recovers-after-a-completed-tool-.md +6 -0
- package/corpus/templates/assets/changelog/2026-07-23-new-chat-aligns-with-sidebar-controls.md +6 -0
- package/corpus/templates/assets/netlify.toml +1 -0
- package/corpus/templates/brain/app/components/layout/Sidebar.tsx +38 -8
- package/corpus/templates/brain/app/routes/extensions._index.tsx +2 -2
- package/corpus/templates/brain/netlify.toml +1 -0
- package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +43 -5
- package/corpus/templates/calendar/app/routes/_app.extensions._index.tsx +2 -16
- package/corpus/templates/calendar/netlify.toml +1 -0
- package/corpus/templates/chat/app/components/layout/Sidebar.tsx +35 -6
- package/corpus/templates/chat/app/routes/extensions._index.tsx +2 -2
- package/corpus/templates/chat/netlify.toml +1 -0
- package/corpus/templates/clips/app/components/library/library-layout.tsx +52 -6
- package/corpus/templates/clips/app/routes/_app.extensions._index.tsx +2 -16
- package/corpus/templates/clips/changelog/2026-07-23-agent-settings-are-clearly-labeled-manage-agent-in-the-sideb.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-23-meeting-recording-meters-and-dismissed-prompts.md +6 -0
- package/corpus/templates/clips/desktop/src/hooks/useMicMeter.ts +1 -1
- package/corpus/templates/clips/desktop/src/lib/meeting-notification-dismissal.ts +2 -0
- package/corpus/templates/clips/desktop/src/lib/voice-dictation.ts +1 -3
- package/corpus/templates/clips/desktop/src/overlays/live-audio-bars.tsx +86 -0
- package/corpus/templates/clips/desktop/src/overlays/meeting-notification.tsx +24 -0
- package/corpus/templates/clips/desktop/src/overlays/recording-pill.tsx +5 -69
- package/corpus/templates/clips/desktop/src/styles.css +35 -3
- package/corpus/templates/clips/desktop/src-tauri/src/adhoc_meetings_watcher.rs +13 -44
- package/corpus/templates/clips/desktop/src-tauri/src/notifications.rs +119 -17
- package/corpus/templates/clips/netlify.toml +1 -0
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +3 -7
- package/corpus/templates/content/app/routes/_app.extensions._index.tsx +2 -2
- package/corpus/templates/content/changelog/2026-07-23-agent-settings-are-clearly-labeled-manage-agent-in-the-sideb.md +6 -0
- package/corpus/templates/content/netlify.toml +1 -0
- package/corpus/templates/design/app/components/layout/Sidebar.tsx +55 -17
- package/corpus/templates/design/app/routes/extensions._index.tsx +2 -2
- package/corpus/templates/design/changelog/2026-07-23-agent-settings-are-clearly-labeled-manage-agent-in-the-sideb.md +6 -0
- package/corpus/templates/design/netlify.toml +1 -0
- package/corpus/templates/dispatch/app/dispatch-extensions.tsx +1 -1
- package/corpus/templates/dispatch/changelog/2026-07-23-agent-settings-are-clearly-labeled-manage-agent-in-the-sideb.md +6 -0
- package/corpus/templates/dispatch/netlify.toml +1 -0
- package/corpus/templates/forms/.agents/skills/form-publishing/SKILL.md +9 -3
- package/corpus/templates/forms/AGENTS.md +11 -1
- package/corpus/templates/forms/README.md +24 -1
- package/corpus/templates/forms/actions/update-form.ts +12 -5
- package/corpus/templates/forms/app/components/builder/FieldPropertiesPanel.tsx +199 -1
- package/corpus/templates/forms/app/components/layout/Sidebar.tsx +41 -45
- package/corpus/templates/forms/app/i18n/en-US.ts +13 -1
- package/corpus/templates/forms/app/pages/FormBuilderPage.tsx +35 -4
- package/corpus/templates/forms/app/pages/FormFillPage.tsx +7 -16
- package/corpus/templates/forms/app/routes/_app.extensions._index.tsx +2 -2
- package/corpus/templates/forms/changelog/2026-07-23-agent-settings-are-clearly-labeled-manage-agent-in-the-sideb.md +6 -0
- package/corpus/templates/forms/changelog/2026-07-23-forms-can-branch-into-follow-up-questions-and-route-response.md +6 -0
- package/corpus/templates/forms/netlify.toml +1 -0
- package/corpus/templates/forms/server/handlers/submissions.ts +22 -31
- package/corpus/templates/forms/server/lib/integrations.ts +11 -2
- package/corpus/templates/forms/server/lib/public-form-ssr.ts +26 -5
- package/corpus/templates/forms/server/lib/validate-fields.ts +48 -11
- package/corpus/templates/forms/server/plugins/agent-chat.ts +1 -0
- package/corpus/templates/forms/shared/conditional.ts +67 -0
- package/corpus/templates/macros/app/components/layout/AppLayout.tsx +36 -5
- package/corpus/templates/macros/app/routes/extensions._index.tsx +2 -2
- package/corpus/templates/macros/netlify.toml +1 -0
- package/corpus/templates/mail/app/components/layout/AppLayout.tsx +0 -9
- package/corpus/templates/mail/app/routes/extensions._index.tsx +2 -2
- package/corpus/templates/mail/netlify.toml +1 -0
- package/corpus/templates/plan/app/components/layout/Sidebar.tsx +38 -5
- package/corpus/templates/plan/app/routes/extensions._index.tsx +2 -2
- package/corpus/templates/plan/netlify.toml +1 -0
- package/corpus/templates/slides/app/components/layout/Sidebar.tsx +47 -4
- package/corpus/templates/slides/app/routes/extensions._index.tsx +2 -2
- package/corpus/templates/slides/netlify.toml +1 -0
- package/corpus/templates/tasks/app/components/layout/Sidebar.tsx +37 -6
- package/corpus/templates/tasks/app/routes/extensions._index.tsx +2 -2
- package/corpus/templates/tasks/app/routes/settings.tsx +31 -0
- package/corpus/templates/tasks/netlify.toml +1 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +4 -0
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-manager.d.ts +9 -0
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +25 -10
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/types.d.ts +2 -2
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +35 -0
- package/dist/client/MultiTabAssistantChat.js.map +1 -1
- package/dist/client/extensions/ExtensionsListPage.d.ts +5 -1
- package/dist/client/extensions/ExtensionsListPage.d.ts.map +1 -1
- package/dist/client/extensions/ExtensionsListPage.js +5 -3
- package/dist/client/extensions/ExtensionsListPage.js.map +1 -1
- package/dist/client/settings/ExtensionsSettingsContent.d.ts +2 -0
- package/dist/client/settings/ExtensionsSettingsContent.d.ts.map +1 -0
- package/dist/client/settings/ExtensionsSettingsContent.js +10 -0
- package/dist/client/settings/ExtensionsSettingsContent.js.map +1 -0
- package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
- package/dist/client/settings/SettingsPanel.js +10 -1
- package/dist/client/settings/SettingsPanel.js.map +1 -1
- package/dist/client/use-chat-models.d.ts +1 -0
- package/dist/client/use-chat-models.d.ts.map +1 -1
- package/dist/client/use-chat-models.js +33 -0
- package/dist/client/use-chat-models.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/deploy/build.js +1 -0
- package/dist/deploy/build.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/observability/routes.d.ts +3 -3
- package/dist/provider-api/actions/custom-provider-registration.d.ts +4 -4
- package/dist/provider-api/actions/provider-api.d.ts +2 -2
- package/dist/server/analytics.d.ts +17 -4
- package/dist/server/analytics.d.ts.map +1 -1
- package/dist/server/analytics.js +122 -17
- package/dist/server/analytics.js.map +1 -1
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +5 -4
- package/dist/server/auth.js.map +1 -1
- package/dist/server/better-auth-instance.d.ts +8 -0
- package/dist/server/better-auth-instance.d.ts.map +1 -1
- package/dist/server/better-auth-instance.js +10 -1
- package/dist/server/better-auth-instance.js.map +1 -1
- package/dist/templates/chat/app/components/layout/Sidebar.tsx +35 -6
- package/dist/templates/chat/app/routes/extensions._index.tsx +2 -2
- package/dist/templates/chat/netlify.toml +1 -0
- package/dist/templates/workspace-core/.agents/skills/extensions/SKILL.md +16 -0
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +2 -0
- package/dist/vite/client.js.map +1 -1
- package/docs/content/extensions.mdx +23 -4
- package/package.json +1 -1
- package/src/agent/production-agent.ts +4 -0
- package/src/agent/run-manager.ts +36 -10
- package/src/agent/types.ts +2 -0
- package/src/client/MultiTabAssistantChat.tsx +45 -0
- package/src/client/extensions/ExtensionsListPage.tsx +16 -3
- package/src/client/settings/ExtensionsSettingsContent.tsx +24 -0
- package/src/client/settings/SettingsPanel.tsx +10 -0
- package/src/client/use-chat-models.ts +45 -0
- package/src/deploy/build.ts +3 -0
- package/src/server/analytics.ts +164 -20
- package/src/server/auth.ts +17 -13
- package/src/server/better-auth-instance.ts +13 -1
- package/src/templates/chat/app/components/layout/Sidebar.tsx +35 -6
- package/src/templates/chat/app/routes/extensions._index.tsx +2 -2
- package/src/templates/chat/netlify.toml +1 -0
- package/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +16 -0
- package/src/vite/client.ts +6 -0
- package/corpus/templates/analytics/app/components/layout/NewAnalysisDialog.tsx +0 -165
- package/corpus/templates/analytics/app/pages/analyses/AnalysesList.tsx +0 -175
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
use serde::{Deserialize, Serialize};
|
|
8
8
|
use serde_json::Value;
|
|
9
|
+
use std::collections::HashMap;
|
|
9
10
|
use std::sync::atomic::{AtomicBool, Ordering};
|
|
10
11
|
use std::sync::Mutex;
|
|
11
12
|
use std::time::Duration;
|
|
@@ -28,9 +29,16 @@ const NOTIFICATION_W_LOGICAL: u32 = 504;
|
|
|
28
29
|
const NOTIFICATION_H_LOGICAL: u32 = 120;
|
|
29
30
|
const NOTIFICATION_TOP_MARGIN_LOGICAL: u32 = 44;
|
|
30
31
|
const NOTIFICATION_RIGHT_MARGIN_LOGICAL: u32 = 0;
|
|
32
|
+
const DISMISSAL_TOMBSTONE_SECS: i64 = 30 * 60;
|
|
31
33
|
|
|
32
34
|
#[derive(Default)]
|
|
33
|
-
pub struct MeetingNotificationState(
|
|
35
|
+
pub struct MeetingNotificationState(Mutex<MeetingNotificationStateInner>);
|
|
36
|
+
|
|
37
|
+
#[derive(Default)]
|
|
38
|
+
struct MeetingNotificationStateInner {
|
|
39
|
+
pending: Option<Value>,
|
|
40
|
+
dismissed_until: HashMap<String, i64>,
|
|
41
|
+
}
|
|
34
42
|
|
|
35
43
|
#[allow(dead_code)]
|
|
36
44
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
@@ -176,22 +184,54 @@ pub fn show_meeting_notification_window(app: &AppHandle) -> Result<(), String> {
|
|
|
176
184
|
Ok(())
|
|
177
185
|
}
|
|
178
186
|
|
|
179
|
-
fn store_pending_meeting_notification(app: &AppHandle, payload: &Value) {
|
|
180
|
-
if let Some(state) = app.try_state::<MeetingNotificationState>() {
|
|
181
|
-
if let Ok(mut pending) = state.0.lock() {
|
|
182
|
-
*pending = Some(payload.clone());
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
187
|
#[tauri::command]
|
|
188
188
|
pub fn take_pending_meeting_notification(app: AppHandle) -> Result<Option<Value>, String> {
|
|
189
189
|
let state = app.state::<MeetingNotificationState>();
|
|
190
|
-
let mut
|
|
190
|
+
let mut state = state
|
|
191
191
|
.0
|
|
192
192
|
.lock()
|
|
193
193
|
.map_err(|_| "meeting notification state lock poisoned".to_string())?;
|
|
194
|
-
|
|
194
|
+
let now = chrono::Utc::now().timestamp();
|
|
195
|
+
state.dismissed_until.retain(|_, until| *until > now);
|
|
196
|
+
if state.pending.as_ref().is_some_and(|payload| {
|
|
197
|
+
state
|
|
198
|
+
.dismissed_until
|
|
199
|
+
.contains_key(¬ification_key(payload))
|
|
200
|
+
}) {
|
|
201
|
+
state.pending = None;
|
|
202
|
+
}
|
|
203
|
+
Ok(state.pending.take())
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
fn notification_key(payload: &Value) -> String {
|
|
207
|
+
format!(
|
|
208
|
+
"{}|{}|{}",
|
|
209
|
+
payload
|
|
210
|
+
.get("type")
|
|
211
|
+
.and_then(Value::as_str)
|
|
212
|
+
.unwrap_or("calendar"),
|
|
213
|
+
payload
|
|
214
|
+
.get("meetingId")
|
|
215
|
+
.and_then(Value::as_str)
|
|
216
|
+
.unwrap_or_default(),
|
|
217
|
+
payload
|
|
218
|
+
.get("scheduledStart")
|
|
219
|
+
.and_then(Value::as_str)
|
|
220
|
+
.unwrap_or_default(),
|
|
221
|
+
)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
fn notification_key_from_parts(
|
|
225
|
+
notification_type: &str,
|
|
226
|
+
meeting_id: &str,
|
|
227
|
+
scheduled_start: Option<&str>,
|
|
228
|
+
) -> String {
|
|
229
|
+
format!(
|
|
230
|
+
"{}|{}|{}",
|
|
231
|
+
notification_type,
|
|
232
|
+
meeting_id,
|
|
233
|
+
scheduled_start.unwrap_or_default()
|
|
234
|
+
)
|
|
195
235
|
}
|
|
196
236
|
|
|
197
237
|
fn clear_pending_notification(pending: &mut Option<Value>, meeting_id: &str) -> bool {
|
|
@@ -211,19 +251,38 @@ pub fn dismiss_meeting_notification(
|
|
|
211
251
|
app: AppHandle,
|
|
212
252
|
meeting_id: String,
|
|
213
253
|
notification_type: String,
|
|
254
|
+
join_url: Option<String>,
|
|
214
255
|
platform: Option<String>,
|
|
215
256
|
scheduled_start: Option<String>,
|
|
216
257
|
scheduled_end: Option<String>,
|
|
217
258
|
) -> Result<(), String> {
|
|
218
259
|
let state = app.state::<MeetingNotificationState>();
|
|
219
|
-
let mut
|
|
260
|
+
let mut state = state
|
|
220
261
|
.0
|
|
221
262
|
.lock()
|
|
222
263
|
.map_err(|_| "meeting notification state lock poisoned".to_string())?;
|
|
223
|
-
|
|
224
|
-
|
|
264
|
+
let now = chrono::Utc::now().timestamp();
|
|
265
|
+
state.dismissed_until.retain(|_, until| *until > now);
|
|
266
|
+
state.dismissed_until.insert(
|
|
267
|
+
notification_key_from_parts(¬ification_type, &meeting_id, scheduled_start.as_deref()),
|
|
268
|
+
now + DISMISSAL_TOMBSTONE_SECS,
|
|
269
|
+
);
|
|
270
|
+
clear_pending_notification(&mut state.pending, &meeting_id);
|
|
271
|
+
drop(state);
|
|
225
272
|
|
|
226
|
-
|
|
273
|
+
let suppression_platform = platform.as_deref().or_else(|| {
|
|
274
|
+
join_url.as_deref().and_then(|url| {
|
|
275
|
+
let url = url.to_ascii_lowercase();
|
|
276
|
+
if url.contains("zoom") {
|
|
277
|
+
Some("zoom")
|
|
278
|
+
} else if url.contains("teams.microsoft") || url.contains("teams") {
|
|
279
|
+
Some("teams")
|
|
280
|
+
} else {
|
|
281
|
+
None
|
|
282
|
+
}
|
|
283
|
+
})
|
|
284
|
+
});
|
|
285
|
+
if let Some(platform) = suppression_platform {
|
|
227
286
|
crate::adhoc_meetings_watcher::refresh_dismissal_suppression(&app, platform)?;
|
|
228
287
|
}
|
|
229
288
|
|
|
@@ -312,14 +371,41 @@ pub async fn notify_meeting_starting(
|
|
|
312
371
|
"type": kind,
|
|
313
372
|
"title": title,
|
|
314
373
|
"subtitle": body,
|
|
315
|
-
"meetingId": meeting_id,
|
|
374
|
+
"meetingId": meeting_id.clone(),
|
|
316
375
|
"joinUrl": join_url,
|
|
317
376
|
"platform": platform,
|
|
318
377
|
"scheduledStart": scheduled_start,
|
|
319
378
|
"scheduledEnd": scheduled_end,
|
|
320
379
|
"autoStart": auto_start.unwrap_or(false),
|
|
321
380
|
});
|
|
322
|
-
|
|
381
|
+
let state = app.state::<MeetingNotificationState>();
|
|
382
|
+
let should_show = {
|
|
383
|
+
let mut state = state
|
|
384
|
+
.0
|
|
385
|
+
.lock()
|
|
386
|
+
.map_err(|_| "meeting notification state lock poisoned".to_string())?;
|
|
387
|
+
let now = chrono::Utc::now().timestamp();
|
|
388
|
+
state.dismissed_until.retain(|_, until| *until > now);
|
|
389
|
+
if state
|
|
390
|
+
.dismissed_until
|
|
391
|
+
.get(¬ification_key(&payload))
|
|
392
|
+
.copied()
|
|
393
|
+
.unwrap_or_default()
|
|
394
|
+
> now
|
|
395
|
+
{
|
|
396
|
+
false
|
|
397
|
+
} else {
|
|
398
|
+
state.pending = Some(payload.clone());
|
|
399
|
+
true
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
if !should_show {
|
|
403
|
+
dlog!(
|
|
404
|
+
"[clips-tray] skipped dismissed meeting notification id={}",
|
|
405
|
+
meeting_id
|
|
406
|
+
);
|
|
407
|
+
return Ok(());
|
|
408
|
+
}
|
|
323
409
|
let _ = app.emit("meetings:show-notification", payload.clone());
|
|
324
410
|
|
|
325
411
|
// Ensure the overlay window exists / is visible for cold starts.
|
|
@@ -346,4 +432,20 @@ mod tests {
|
|
|
346
432
|
assert!(clear_pending_notification(&mut pending, "meeting-1"));
|
|
347
433
|
assert!(pending.is_none());
|
|
348
434
|
}
|
|
435
|
+
|
|
436
|
+
#[test]
|
|
437
|
+
fn dismissal_key_changes_when_a_meeting_is_rescheduled() {
|
|
438
|
+
let first = serde_json::json!({
|
|
439
|
+
"type": "calendar",
|
|
440
|
+
"meetingId": "meeting-1",
|
|
441
|
+
"scheduledStart": "2026-07-23T17:00:00Z"
|
|
442
|
+
});
|
|
443
|
+
let moved = serde_json::json!({
|
|
444
|
+
"type": "calendar",
|
|
445
|
+
"meetingId": "meeting-1",
|
|
446
|
+
"scheduledStart": "2026-07-23T18:00:00Z"
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
assert_ne!(notification_key(&first), notification_key(&moved));
|
|
450
|
+
}
|
|
349
451
|
}
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import { useCodeMode } from "@agent-native/core/client/agent-chat";
|
|
2
2
|
import { appPath } from "@agent-native/core/client/api-path";
|
|
3
3
|
import { DevDatabaseLink } from "@agent-native/core/client/db-admin";
|
|
4
|
-
import {
|
|
5
|
-
ExtensionSlot,
|
|
6
|
-
ExtensionsSidebarSection,
|
|
7
|
-
} from "@agent-native/core/client/extensions";
|
|
4
|
+
import { ExtensionSlot } from "@agent-native/core/client/extensions";
|
|
8
5
|
import {
|
|
9
6
|
setClientAppState,
|
|
10
7
|
useActionMutation,
|
|
@@ -1293,7 +1290,7 @@ export function DocumentSidebar({
|
|
|
1293
1290
|
>
|
|
1294
1291
|
<IconHierarchy2 size={15} className="shrink-0" />
|
|
1295
1292
|
<span className="min-w-0 flex-1 truncate text-start">
|
|
1296
|
-
{t("
|
|
1293
|
+
{t("settings.agentTitle")}
|
|
1297
1294
|
</span>
|
|
1298
1295
|
</Link>
|
|
1299
1296
|
);
|
|
@@ -1861,7 +1858,7 @@ export function DocumentSidebar({
|
|
|
1861
1858
|
<IconHierarchy2 size={16} />
|
|
1862
1859
|
</Link>
|
|
1863
1860
|
</TooltipTrigger>
|
|
1864
|
-
<TooltipContent>{t("
|
|
1861
|
+
<TooltipContent>{t("settings.agentTitle")}</TooltipContent>
|
|
1865
1862
|
</Tooltip>
|
|
1866
1863
|
<Tooltip>
|
|
1867
1864
|
<TooltipTrigger asChild>
|
|
@@ -2092,7 +2089,6 @@ export function DocumentSidebar({
|
|
|
2092
2089
|
className="px-2 py-2"
|
|
2093
2090
|
toolClassName="overflow-hidden rounded-md"
|
|
2094
2091
|
/>
|
|
2095
|
-
<ExtensionsSidebarSection />
|
|
2096
2092
|
</div>
|
|
2097
2093
|
|
|
2098
2094
|
<div className="shrink-0 px-3 py-2">
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { appPath } from "@agent-native/core/client/api-path";
|
|
2
2
|
import { DevDatabaseLink } from "@agent-native/core/client/db-admin";
|
|
3
|
-
import { ExtensionsSidebarSection } from "@agent-native/core/client/extensions";
|
|
4
3
|
import { useT } from "@agent-native/core/client/i18n";
|
|
5
4
|
import { OrgSwitcher } from "@agent-native/core/client/org";
|
|
6
5
|
import { FeedbackButton } from "@agent-native/core/client/ui";
|
|
@@ -32,7 +31,10 @@ const navItems = [
|
|
|
32
31
|
labelKey: "navigation.designSystems",
|
|
33
32
|
href: "/design-systems",
|
|
34
33
|
},
|
|
35
|
-
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
const bottomNavItems = [
|
|
37
|
+
{ icon: IconHierarchy2, labelKey: "settings.agentTitle", href: "/agent" },
|
|
36
38
|
{ icon: IconSettings, labelKey: "navigation.settings", href: "/settings" },
|
|
37
39
|
];
|
|
38
40
|
|
|
@@ -156,25 +158,61 @@ export function Sidebar() {
|
|
|
156
158
|
})}
|
|
157
159
|
</nav>
|
|
158
160
|
|
|
159
|
-
|
|
160
|
-
<
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
161
|
+
<div className="mt-auto shrink-0">
|
|
162
|
+
<nav
|
|
163
|
+
className={cn(
|
|
164
|
+
"grid gap-1",
|
|
165
|
+
collapsed ? "justify-items-center px-1.5 py-1" : "px-2 py-1",
|
|
166
|
+
)}
|
|
167
|
+
>
|
|
168
|
+
{bottomNavItems.map((item) => {
|
|
169
|
+
const Icon = item.icon;
|
|
170
|
+
const isActive = location.pathname.startsWith(item.href);
|
|
171
|
+
const link = (
|
|
172
|
+
<Link
|
|
173
|
+
to={item.href}
|
|
174
|
+
className={cn(
|
|
175
|
+
"flex items-center rounded-lg text-sm",
|
|
176
|
+
collapsed ? "h-9 w-9 justify-center" : "gap-3 px-3 py-2",
|
|
177
|
+
isActive
|
|
178
|
+
? "bg-sidebar-accent text-sidebar-accent-foreground"
|
|
179
|
+
: "text-muted-foreground hover:bg-sidebar-accent/50 hover:text-foreground",
|
|
180
|
+
)}
|
|
181
|
+
aria-label={collapsed ? t(item.labelKey) : undefined}
|
|
182
|
+
>
|
|
183
|
+
<Icon className="h-4 w-4 shrink-0" />
|
|
184
|
+
{!collapsed && t(item.labelKey)}
|
|
185
|
+
</Link>
|
|
186
|
+
);
|
|
187
|
+
return collapsed ? (
|
|
188
|
+
<Tooltip key={item.href} delayDuration={0}>
|
|
189
|
+
<TooltipTrigger asChild>{link}</TooltipTrigger>
|
|
190
|
+
<TooltipContent side="right">
|
|
191
|
+
{t(item.labelKey)}
|
|
192
|
+
</TooltipContent>
|
|
193
|
+
</Tooltip>
|
|
194
|
+
) : (
|
|
195
|
+
<div key={item.href}>{link}</div>
|
|
196
|
+
);
|
|
197
|
+
})}
|
|
198
|
+
</nav>
|
|
164
199
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
200
|
+
{!collapsed && (
|
|
201
|
+
<div className="mt-auto shrink-0">
|
|
202
|
+
<div className="px-3 py-2">
|
|
203
|
+
<OrgSwitcher reserveSpace />
|
|
204
|
+
</div>
|
|
168
205
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
206
|
+
<div className="px-3 py-2">
|
|
207
|
+
<DevDatabaseLink />
|
|
208
|
+
<div className="flex items-center justify-end gap-1">
|
|
209
|
+
<FeedbackButton className="min-w-0 flex-1" />
|
|
210
|
+
<ThemeToggle className="h-8 w-8 shrink-0" />
|
|
211
|
+
</div>
|
|
174
212
|
</div>
|
|
175
213
|
</div>
|
|
176
|
-
|
|
177
|
-
|
|
214
|
+
)}
|
|
215
|
+
</div>
|
|
178
216
|
</div>
|
|
179
217
|
</aside>
|
|
180
218
|
);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Navigate } from "react-router";
|
|
2
2
|
|
|
3
3
|
import { messagesByLocale } from "@/i18n-data";
|
|
4
4
|
|
|
@@ -7,5 +7,5 @@ export function meta() {
|
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
export default function ExtensionsRoute() {
|
|
10
|
-
return <
|
|
10
|
+
return <Navigate to="/settings#extensions" replace />;
|
|
11
11
|
}
|
|
@@ -77,7 +77,7 @@ Each form has a `settings` JSON object:
|
|
|
77
77
|
| `showProgressBar` | boolean | Show progress bar for multi-section forms |
|
|
78
78
|
| `emailOnNewResponses` | boolean | Email the form owner's account when someone submits a response |
|
|
79
79
|
| `anonymous` | boolean | Suppress IP, submitter identity, chat/run ids, page URL, and client-surface metadata for every response |
|
|
80
|
-
| `integrations` | array | Webhook/Slack/Discord notification configs |
|
|
80
|
+
| `integrations` | array | Webhook/Slack/Discord/Google Sheets notification configs |
|
|
81
81
|
|
|
82
82
|
For a genuinely anonymous form, set `anonymous: true` when creating the form.
|
|
83
83
|
Do not describe an ordinary published form as anonymous: published forms accept
|
|
@@ -91,9 +91,15 @@ Forms can notify external services on submission:
|
|
|
91
91
|
| Type | Description |
|
|
92
92
|
| --------------- | ------------------------------- |
|
|
93
93
|
| `webhook` | POST JSON to any URL |
|
|
94
|
-
| `slack` | Send to a Slack channel
|
|
94
|
+
| `slack` | Send to a Slack channel through an Incoming Webhook URL |
|
|
95
95
|
| `discord` | Send to a Discord webhook |
|
|
96
|
-
| `google-sheets` |
|
|
96
|
+
| `google-sheets` | Send response JSON to a deployed Apps Script `/exec` URL |
|
|
97
|
+
|
|
98
|
+
These are outbound form destinations configured in the form builder's
|
|
99
|
+
**Integrations** tab. They are separate from the managed Slack/Messaging
|
|
100
|
+
connection. Google Sheets Apps Script handlers should parse
|
|
101
|
+
`JSON.parse(e.postData.contents)`; a spreadsheet URL or `/dev` URL will not
|
|
102
|
+
receive submissions.
|
|
97
103
|
|
|
98
104
|
## Related Skills
|
|
99
105
|
|
|
@@ -40,7 +40,17 @@ ladder.
|
|
|
40
40
|
- To email the form owner when someone submits a response, set
|
|
41
41
|
`settings.emailOnNewResponses: true` through `create-form` or `update-form`.
|
|
42
42
|
Delivery uses the configured framework email provider (`RESEND_API_KEY` or
|
|
43
|
-
`SENDGRID_API_KEY`)
|
|
43
|
+
`SENDGRID_API_KEY`) in the form owner's request context and sends to the form
|
|
44
|
+
owner's account email. A public submission can succeed even when delivery
|
|
45
|
+
fails, so check the server logs and provider configuration when debugging.
|
|
46
|
+
- Conditional fields use `conditional: { fieldId, operator, value }`. The
|
|
47
|
+
`fieldId` must reference an earlier field; supported operators are `equals`,
|
|
48
|
+
`not_equals`, and `contains`. Hidden fields and their stale values are not
|
|
49
|
+
persisted or delivered to integrations.
|
|
50
|
+
- Form integrations are outbound webhooks. Slack requires an Incoming Webhook
|
|
51
|
+
URL, and Google Sheets requires a deployed Google Apps Script `/exec` URL that
|
|
52
|
+
parses `JSON.parse(e.postData.contents)` and appends the received values.
|
|
53
|
+
They are separate from the managed Slack/Messaging connection.
|
|
44
54
|
- Form UX should stay focused: clear labels, sensible validation, minimal
|
|
45
55
|
required fields, and progressive disclosure for advanced settings.
|
|
46
56
|
- Public form submission endpoints must be intentionally public; keep management
|
|
@@ -17,7 +17,8 @@ them.
|
|
|
17
17
|
- Shipped field types: text, email, number, long text, select, multi-select,
|
|
18
18
|
checkbox, radio, date, rating, and scale.
|
|
19
19
|
- Submissions stored in SQL with a per-response detail view and dashboard.
|
|
20
|
-
- Route submissions to webhooks, Slack, Discord, or Google Sheets
|
|
20
|
+
- Route submissions to webhooks, Slack, Discord, or Google Sheets via Apps
|
|
21
|
+
Script.
|
|
21
22
|
- Publish public form URLs with a thank-you message.
|
|
22
23
|
|
|
23
24
|
## Develop locally
|
|
@@ -32,3 +33,25 @@ pnpm dev
|
|
|
32
33
|
```
|
|
33
34
|
|
|
34
35
|
Full docs: [agent-native.com/docs/template-forms](https://agent-native.com/docs/template-forms).
|
|
36
|
+
|
|
37
|
+
## Submission destinations
|
|
38
|
+
|
|
39
|
+
Open a form's **Integrations** tab and save a destination before publishing.
|
|
40
|
+
Slack uses an Incoming Webhook URL. Google Sheets uses a deployed Apps Script
|
|
41
|
+
web app URL ending in `/exec` (not a spreadsheet URL or `/dev` URL). The script
|
|
42
|
+
should parse the JSON body and append the values, for example:
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
function doPost(e) {
|
|
46
|
+
const payload = JSON.parse(e.postData.contents);
|
|
47
|
+
SpreadsheetApp.getActiveSpreadsheet()
|
|
48
|
+
.getSheets()[0]
|
|
49
|
+
.appendRow([payload.submittedAt, payload.formTitle, payload.responseId]);
|
|
50
|
+
return ContentService.createTextOutput(JSON.stringify({ ok: true }));
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Deploy it as a web app that executes as you and allows anyone with the URL to
|
|
55
|
+
access it, then paste the deployed `/exec` URL into Forms. The destination
|
|
56
|
+
receives the form metadata plus one property per field label; duplicate labels
|
|
57
|
+
are disambiguated with the field ID.
|
|
@@ -85,22 +85,29 @@ export default defineAction({
|
|
|
85
85
|
updates.fields = JSON.stringify(parsedFields);
|
|
86
86
|
}
|
|
87
87
|
if (args.settings !== undefined) {
|
|
88
|
-
let
|
|
88
|
+
let incomingSettings: FormSettings;
|
|
89
89
|
if (typeof args.settings === "string") {
|
|
90
90
|
try {
|
|
91
|
-
|
|
92
|
-
updates.settings = args.settings;
|
|
91
|
+
incomingSettings = JSON.parse(args.settings) as FormSettings;
|
|
93
92
|
} catch {
|
|
94
93
|
throw new Error("--settings must be valid JSON");
|
|
95
94
|
}
|
|
96
95
|
} else {
|
|
97
|
-
|
|
98
|
-
updates.settings = JSON.stringify(args.settings);
|
|
96
|
+
incomingSettings = args.settings as unknown as FormSettings;
|
|
99
97
|
}
|
|
98
|
+
let existingSettings: FormSettings = {};
|
|
99
|
+
try {
|
|
100
|
+
existingSettings = JSON.parse(existing.settings) as FormSettings;
|
|
101
|
+
} catch {
|
|
102
|
+
// Keep malformed legacy settings recoverable by replacing them with
|
|
103
|
+
// the valid settings supplied by this update.
|
|
104
|
+
}
|
|
105
|
+
const parsedSettings = { ...existingSettings, ...incomingSettings };
|
|
100
106
|
// Reject blocked integration URLs at save time (private IPs,
|
|
101
107
|
// cloud-metadata, non-http(s) schemes). fireIntegrations also
|
|
102
108
|
// re-checks at runtime as defense-in-depth.
|
|
103
109
|
assertIntegrationUrlsAllowed(parsedSettings);
|
|
110
|
+
updates.settings = JSON.stringify(parsedSettings);
|
|
104
111
|
}
|
|
105
112
|
if (args.status !== undefined) updates.status = args.status;
|
|
106
113
|
|