@agent-native/core 0.92.2 → 0.92.4
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 +32 -0
- package/corpus/core/docs/content/locales/ar-SA/server.mdx +35 -0
- package/corpus/core/docs/content/locales/de-DE/server.mdx +35 -0
- package/corpus/core/docs/content/locales/es-ES/server.mdx +35 -0
- package/corpus/core/docs/content/locales/fr-FR/server.mdx +35 -0
- package/corpus/core/docs/content/locales/hi-IN/server.mdx +35 -0
- package/corpus/core/docs/content/locales/ja-JP/server.mdx +35 -0
- package/corpus/core/docs/content/locales/ko-KR/server.mdx +35 -0
- package/corpus/core/docs/content/locales/pt-BR/server.mdx +35 -0
- package/corpus/core/docs/content/locales/zh-CN/server.mdx +35 -0
- package/corpus/core/docs/content/locales/zh-TW/server.mdx +35 -0
- package/corpus/core/docs/content/server.mdx +35 -0
- package/corpus/core/package.json +2 -2
- package/corpus/core/src/agent/engine/ai-sdk-engine.ts +14 -4
- package/corpus/core/src/agent/engine/anthropic-engine.ts +15 -3
- package/corpus/core/src/agent/engine/builder-engine.ts +30 -4
- package/corpus/core/src/agent/engine/registry.ts +63 -2
- package/corpus/core/src/agent/model-config.ts +0 -2
- package/corpus/core/src/agent/production-agent.ts +12 -1
- package/corpus/core/src/agent/run-manager.ts +57 -36
- package/corpus/core/src/agent/run-store.ts +81 -2
- package/corpus/core/src/agent/thread-data-builder.ts +33 -2
- package/corpus/core/src/client/AssistantChat.tsx +314 -96
- package/corpus/core/src/client/agent-chat-adapter.ts +17 -1
- package/corpus/core/src/client/chat/repo-helpers.ts +45 -0
- package/corpus/core/src/client/chat/tool-call-display.tsx +36 -27
- package/corpus/core/src/client/chat-model-groups.ts +1 -5
- package/corpus/core/src/client/composer/TiptapComposer.tsx +0 -2
- package/corpus/core/src/client/embed-auth.ts +20 -0
- package/corpus/core/src/client/settings/SettingsSection.tsx +66 -5
- package/corpus/core/src/client/sse-event-processor.ts +49 -17
- package/corpus/core/src/client/use-db-sync.ts +31 -9
- package/corpus/core/src/collab/agent-presence.ts +6 -1
- package/corpus/core/src/collab/awareness-store.ts +185 -0
- package/corpus/core/src/collab/awareness.ts +58 -2
- package/corpus/core/src/scripts/agent-engines/manage-agent-engine.ts +7 -8
- package/corpus/core/src/scripts/agent-engines/set-agent-engine.ts +7 -18
- package/corpus/core/src/server/core-routes-plugin.ts +21 -2
- package/corpus/core/src/server/embed-session.ts +20 -4
- package/corpus/core/src/styles/agent-native.css +51 -0
- package/corpus/core/src/tracking/providers.ts +33 -2
- package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/index.tsx +8 -1
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +9 -1
- package/corpus/templates/analytics/app/pages/monitoring/uptime/MonitorDetail.tsx +41 -11
- package/corpus/templates/analytics/app/pages/monitoring/uptime/types.ts +44 -0
- package/corpus/templates/analytics/changelog/2026-07-09-dashboard-action-menus-now-close-cleanly-before-opening-hist.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-09-dashboard-email-reports-still-send-when-the-screenshot-captu.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-09-uptime-alerts-now-suppress-recovery-noise-during-flapping-an.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-09-uptime-check-history-now-shows-timing-diagnostics-so-slow-or.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-09-uptime-recovery-alerts-now-send-when-the-original-outage-not.md +6 -0
- package/corpus/templates/analytics/docs/uptime-monitoring.md +29 -16
- package/corpus/templates/analytics/netlify.toml +3 -0
- package/corpus/templates/analytics/scripts/emit-netlify-dashboard-report-cron.ts +126 -0
- package/corpus/templates/analytics/server/db/schema-monitoring.ts +5 -0
- package/corpus/templates/analytics/server/jobs/uptime-monitors.ts +5 -1
- package/corpus/templates/analytics/server/lib/uptime-monitors.ts +305 -31
- package/corpus/templates/analytics/server/plugins/db.ts +15 -0
- package/corpus/templates/analytics/server/plugins/uptime-monitor-jobs.ts +1 -1
- package/corpus/templates/analytics/server/routes/api/uptime-monitors/run.post.ts +57 -0
- package/corpus/templates/clips/actions/list-meetings.ts +12 -0
- package/corpus/templates/clips/changelog/2026-07-09-meeting-reminders-now-ignore-obvious-solo-personal-calendar-.md +6 -0
- package/corpus/templates/clips/desktop/src-tauri/src/meetings_watcher.rs +1 -0
- package/corpus/templates/clips/server/lib/calendar-event-classification.ts +79 -0
- package/dist/agent/engine/ai-sdk-engine.d.ts.map +1 -1
- package/dist/agent/engine/ai-sdk-engine.js +13 -4
- package/dist/agent/engine/ai-sdk-engine.js.map +1 -1
- package/dist/agent/engine/anthropic-engine.d.ts.map +1 -1
- package/dist/agent/engine/anthropic-engine.js +14 -3
- package/dist/agent/engine/anthropic-engine.js.map +1 -1
- package/dist/agent/engine/builder-engine.d.ts +1 -1
- package/dist/agent/engine/builder-engine.d.ts.map +1 -1
- package/dist/agent/engine/builder-engine.js +26 -4
- package/dist/agent/engine/builder-engine.js.map +1 -1
- package/dist/agent/engine/registry.d.ts.map +1 -1
- package/dist/agent/engine/registry.js +47 -2
- package/dist/agent/engine/registry.js.map +1 -1
- package/dist/agent/model-config.d.ts +2 -2
- package/dist/agent/model-config.d.ts.map +1 -1
- package/dist/agent/model-config.js +0 -2
- package/dist/agent/model-config.js.map +1 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +12 -1
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +52 -33
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/run-store.d.ts +24 -0
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +64 -2
- package/dist/agent/run-store.js.map +1 -1
- package/dist/agent/thread-data-builder.d.ts.map +1 -1
- package/dist/agent/thread-data-builder.js +28 -2
- package/dist/agent/thread-data-builder.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +6 -0
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +243 -36
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +15 -1
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/chat/repo-helpers.d.ts.map +1 -1
- package/dist/client/chat/repo-helpers.js +43 -0
- package/dist/client/chat/repo-helpers.js.map +1 -1
- package/dist/client/chat/tool-call-display.d.ts.map +1 -1
- package/dist/client/chat/tool-call-display.js +4 -4
- package/dist/client/chat/tool-call-display.js.map +1 -1
- package/dist/client/chat-model-groups.d.ts.map +1 -1
- package/dist/client/chat-model-groups.js +1 -3
- package/dist/client/chat-model-groups.js.map +1 -1
- package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/client/composer/TiptapComposer.js +0 -2
- package/dist/client/composer/TiptapComposer.js.map +1 -1
- package/dist/client/embed-auth.d.ts.map +1 -1
- package/dist/client/embed-auth.js +14 -0
- package/dist/client/embed-auth.js.map +1 -1
- package/dist/client/settings/SettingsSection.d.ts.map +1 -1
- package/dist/client/settings/SettingsSection.js +32 -3
- package/dist/client/settings/SettingsSection.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +44 -17
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/client/use-db-sync.d.ts.map +1 -1
- package/dist/client/use-db-sync.js +29 -9
- package/dist/client/use-db-sync.js.map +1 -1
- package/dist/collab/agent-presence.d.ts.map +1 -1
- package/dist/collab/agent-presence.js +5 -2
- package/dist/collab/agent-presence.js.map +1 -1
- package/dist/collab/awareness-store.d.ts +1 -1
- package/dist/collab/awareness-store.d.ts.map +1 -1
- package/dist/collab/awareness-store.js +0 -0
- package/dist/collab/awareness-store.js.map +1 -1
- package/dist/collab/awareness.d.ts +4 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/awareness.js +46 -2
- package/dist/collab/awareness.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/notifications/routes.d.ts +2 -2
- package/dist/observability/routes.d.ts +3 -3
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/scripts/agent-engines/manage-agent-engine.d.ts.map +1 -1
- package/dist/scripts/agent-engines/manage-agent-engine.js +6 -6
- package/dist/scripts/agent-engines/manage-agent-engine.js.map +1 -1
- package/dist/scripts/agent-engines/set-agent-engine.d.ts.map +1 -1
- package/dist/scripts/agent-engines/set-agent-engine.js +5 -13
- package/dist/scripts/agent-engines/set-agent-engine.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/core-routes-plugin.d.ts +5 -0
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +10 -3
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/embed-session.d.ts.map +1 -1
- package/dist/server/embed-session.js +17 -4
- package/dist/server/embed-session.js.map +1 -1
- package/dist/styles/agent-native.css +51 -0
- package/dist/tracking/providers.d.ts.map +1 -1
- package/dist/tracking/providers.js +23 -5
- package/dist/tracking/providers.js.map +1 -1
- package/docs/content/locales/ar-SA/server.mdx +35 -0
- package/docs/content/locales/de-DE/server.mdx +35 -0
- package/docs/content/locales/es-ES/server.mdx +35 -0
- package/docs/content/locales/fr-FR/server.mdx +35 -0
- package/docs/content/locales/hi-IN/server.mdx +35 -0
- package/docs/content/locales/ja-JP/server.mdx +35 -0
- package/docs/content/locales/ko-KR/server.mdx +35 -0
- package/docs/content/locales/pt-BR/server.mdx +35 -0
- package/docs/content/locales/zh-CN/server.mdx +35 -0
- package/docs/content/locales/zh-TW/server.mdx +35 -0
- package/docs/content/server.mdx +35 -0
- package/package.json +2 -2
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQL-backed awareness store — makes presence work across serverless
|
|
3
|
+
* instances.
|
|
4
|
+
*
|
|
5
|
+
* The in-memory awareness map in `awareness.ts` is per-process: on a
|
|
6
|
+
* multi-instance/serverless deployment, a cursor published to instance A is
|
|
7
|
+
* invisible to a client polling instance B, and an agent action running in
|
|
8
|
+
* its own invocation can't reach the SSE instance's memory at all. This
|
|
9
|
+
* store mirrors awareness rows into a `_collab_awareness` table (SQLite +
|
|
10
|
+
* Postgres portable) so every instance serves the same participant set.
|
|
11
|
+
*
|
|
12
|
+
* Everything here is best-effort: presence must never fail or slow down an
|
|
13
|
+
* edit. Writes are throttled per (docId, clientId) and skipped when the
|
|
14
|
+
* state hasn't changed; expired rows are purged opportunistically.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { getDbExec, isPostgres } from "../db/client.js";
|
|
18
|
+
import { ensureTableExists } from "../db/ddl-guard.js";
|
|
19
|
+
import type { AwarenessEntry } from "./awareness.js";
|
|
20
|
+
|
|
21
|
+
/** Rows older than this are treated as expired (matches AWARENESS_TIMEOUT). */
|
|
22
|
+
const ROW_TTL_MS = 30_000;
|
|
23
|
+
|
|
24
|
+
/** Minimum interval between DB writes for an unchanged (docId, clientId). */
|
|
25
|
+
const WRITE_THROTTLE_MS = 2_000;
|
|
26
|
+
|
|
27
|
+
/** Minimum interval between opportunistic purges per document. */
|
|
28
|
+
const PURGE_INTERVAL_MS = 30_000;
|
|
29
|
+
|
|
30
|
+
let _initPromise: Promise<void> | undefined;
|
|
31
|
+
|
|
32
|
+
async function ensureTable(): Promise<void> {
|
|
33
|
+
if (!_initPromise) {
|
|
34
|
+
_initPromise = (async () => {
|
|
35
|
+
const client = getDbExec();
|
|
36
|
+
// client_id is a Yjs uint32 (can exceed int32 max) and last_seen is
|
|
37
|
+
// epoch milliseconds — both need BIGINT-range columns.
|
|
38
|
+
const createSql = `
|
|
39
|
+
CREATE TABLE IF NOT EXISTS _collab_awareness (
|
|
40
|
+
doc_id TEXT NOT NULL,
|
|
41
|
+
client_id BIGINT NOT NULL,
|
|
42
|
+
state TEXT NOT NULL,
|
|
43
|
+
last_seen BIGINT NOT NULL,
|
|
44
|
+
PRIMARY KEY (doc_id, client_id)
|
|
45
|
+
)
|
|
46
|
+
`;
|
|
47
|
+
if (isPostgres()) {
|
|
48
|
+
await ensureTableExists("_collab_awareness", createSql);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
await client.execute(createSql);
|
|
52
|
+
})().catch((err) => {
|
|
53
|
+
_initPromise = undefined;
|
|
54
|
+
throw err;
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
return _initPromise;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// (docId\0clientId) → { state, writtenAt } for write throttling.
|
|
61
|
+
const _lastWrites = new Map<string, { state: string; writtenAt: number }>();
|
|
62
|
+
// docId → last purge time.
|
|
63
|
+
const _lastPurges = new Map<string, number>();
|
|
64
|
+
|
|
65
|
+
function writeKey(docId: string, clientId: number): string {
|
|
66
|
+
return `${docId}\0${clientId}`;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Mirror a client's awareness state to SQL. Throttled: unchanged state is
|
|
71
|
+
* rewritten at most every {@link WRITE_THROTTLE_MS} (to refresh last_seen).
|
|
72
|
+
* Never throws.
|
|
73
|
+
*/
|
|
74
|
+
export async function upsertAwarenessRow(
|
|
75
|
+
docId: string,
|
|
76
|
+
clientId: number,
|
|
77
|
+
state: string,
|
|
78
|
+
lastSeen: number,
|
|
79
|
+
): Promise<void> {
|
|
80
|
+
try {
|
|
81
|
+
const key = writeKey(docId, clientId);
|
|
82
|
+
const prev = _lastWrites.get(key);
|
|
83
|
+
if (
|
|
84
|
+
prev &&
|
|
85
|
+
prev.state === state &&
|
|
86
|
+
lastSeen - prev.writtenAt < WRITE_THROTTLE_MS
|
|
87
|
+
) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
await ensureTable();
|
|
92
|
+
const client = getDbExec();
|
|
93
|
+
if (isPostgres()) {
|
|
94
|
+
await client.execute({
|
|
95
|
+
sql: `INSERT INTO _collab_awareness (doc_id, client_id, state, last_seen)
|
|
96
|
+
VALUES (?, ?, ?, ?)
|
|
97
|
+
ON CONFLICT (doc_id, client_id)
|
|
98
|
+
DO UPDATE SET state = EXCLUDED.state, last_seen = EXCLUDED.last_seen`,
|
|
99
|
+
args: [docId, clientId, state, lastSeen],
|
|
100
|
+
});
|
|
101
|
+
} else {
|
|
102
|
+
await client.execute({
|
|
103
|
+
sql: `INSERT OR REPLACE INTO _collab_awareness (doc_id, client_id, state, last_seen)
|
|
104
|
+
VALUES (?, ?, ?, ?)`,
|
|
105
|
+
args: [docId, clientId, state, lastSeen],
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
_lastWrites.set(key, { state, writtenAt: lastSeen });
|
|
109
|
+
|
|
110
|
+
await maybePurge(docId, lastSeen);
|
|
111
|
+
} catch {
|
|
112
|
+
// Best-effort — presence never fails a request.
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Remove a client's row (participant left). Never throws. */
|
|
117
|
+
export async function deleteAwarenessRow(
|
|
118
|
+
docId: string,
|
|
119
|
+
clientId: number,
|
|
120
|
+
maxLastSeen?: number,
|
|
121
|
+
): Promise<void> {
|
|
122
|
+
try {
|
|
123
|
+
await ensureTable();
|
|
124
|
+
const client = getDbExec();
|
|
125
|
+
if (maxLastSeen == null) {
|
|
126
|
+
await client.execute({
|
|
127
|
+
sql: `DELETE FROM _collab_awareness WHERE doc_id = ? AND client_id = ?`,
|
|
128
|
+
args: [docId, clientId],
|
|
129
|
+
});
|
|
130
|
+
} else {
|
|
131
|
+
await client.execute({
|
|
132
|
+
sql: `DELETE FROM _collab_awareness
|
|
133
|
+
WHERE doc_id = ? AND client_id = ? AND last_seen <= ?`,
|
|
134
|
+
args: [docId, clientId, maxLastSeen],
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
_lastWrites.delete(writeKey(docId, clientId));
|
|
138
|
+
} catch {
|
|
139
|
+
// Best-effort.
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Load non-expired awareness rows for a document. Returns [] on any
|
|
145
|
+
* failure (memory-only degradation).
|
|
146
|
+
*/
|
|
147
|
+
export async function loadAwarenessRows(
|
|
148
|
+
docId: string,
|
|
149
|
+
now: number = Date.now(),
|
|
150
|
+
): Promise<AwarenessEntry[]> {
|
|
151
|
+
try {
|
|
152
|
+
await ensureTable();
|
|
153
|
+
const client = getDbExec();
|
|
154
|
+
const { rows } = await client.execute({
|
|
155
|
+
sql: `SELECT client_id, state, last_seen FROM _collab_awareness
|
|
156
|
+
WHERE doc_id = ? AND last_seen >= ?`,
|
|
157
|
+
args: [docId, now - ROW_TTL_MS],
|
|
158
|
+
});
|
|
159
|
+
return rows.map((row: any) => ({
|
|
160
|
+
clientId: Number(row.client_id),
|
|
161
|
+
state: String(row.state),
|
|
162
|
+
lastSeen: Number(row.last_seen),
|
|
163
|
+
}));
|
|
164
|
+
} catch {
|
|
165
|
+
return [];
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async function maybePurge(docId: string, now: number): Promise<void> {
|
|
170
|
+
const last = _lastPurges.get(docId) ?? 0;
|
|
171
|
+
if (now - last < PURGE_INTERVAL_MS) return;
|
|
172
|
+
_lastPurges.set(docId, now);
|
|
173
|
+
const client = getDbExec();
|
|
174
|
+
await client.execute({
|
|
175
|
+
sql: `DELETE FROM _collab_awareness WHERE doc_id = ? AND last_seen < ?`,
|
|
176
|
+
args: [docId, now - ROW_TTL_MS * 2],
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** Test hook: reset module-level throttles. */
|
|
181
|
+
export function _resetAwarenessStoreForTests(): void {
|
|
182
|
+
_lastWrites.clear();
|
|
183
|
+
_lastPurges.clear();
|
|
184
|
+
_initPromise = undefined;
|
|
185
|
+
}
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
} from "./awareness-store.js";
|
|
25
25
|
|
|
26
26
|
const AWARENESS_TIMEOUT = 30_000; // 30 seconds
|
|
27
|
+
const CLEAR_TOMBSTONE_TTL = AWARENESS_TIMEOUT + 5_000;
|
|
27
28
|
|
|
28
29
|
export interface AwarenessEntry {
|
|
29
30
|
clientId: number;
|
|
@@ -106,6 +107,53 @@ export function emitAwarenessChange(
|
|
|
106
107
|
|
|
107
108
|
// docId → Map<clientId, AwarenessEntry>
|
|
108
109
|
const _awarenessMap = new Map<string, Map<number, AwarenessEntry>>();
|
|
110
|
+
// docId + clientId -> clearedAt. Prevents a stale SQL mirror from resurrecting
|
|
111
|
+
// a participant after an explicit leave/delete raced with the next poll.
|
|
112
|
+
const _awarenessClearTombstones = new Map<string, number>();
|
|
113
|
+
|
|
114
|
+
function awarenessKey(docId: string, clientId: number): string {
|
|
115
|
+
return `${docId}\0${clientId}`;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function pruneAwarenessClearTombstones(now: number): void {
|
|
119
|
+
for (const [key, clearedAt] of _awarenessClearTombstones) {
|
|
120
|
+
if (now - clearedAt > CLEAR_TOMBSTONE_TTL) {
|
|
121
|
+
_awarenessClearTombstones.delete(key);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function rememberAwarenessClear(
|
|
127
|
+
docId: string,
|
|
128
|
+
clientId: number,
|
|
129
|
+
clearedAt: number = Date.now(),
|
|
130
|
+
): void {
|
|
131
|
+
pruneAwarenessClearTombstones(clearedAt);
|
|
132
|
+
const key = awarenessKey(docId, clientId);
|
|
133
|
+
const prev = _awarenessClearTombstones.get(key);
|
|
134
|
+
if (prev == null || clearedAt > prev) {
|
|
135
|
+
_awarenessClearTombstones.set(key, clearedAt);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function forgetAwarenessClear(docId: string, clientId: number): void {
|
|
140
|
+
_awarenessClearTombstones.delete(awarenessKey(docId, clientId));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function isBlockedByAwarenessClear(
|
|
144
|
+
docId: string,
|
|
145
|
+
clientId: number,
|
|
146
|
+
lastSeen: number,
|
|
147
|
+
now: number,
|
|
148
|
+
): boolean {
|
|
149
|
+
pruneAwarenessClearTombstones(now);
|
|
150
|
+
const key = awarenessKey(docId, clientId);
|
|
151
|
+
const clearedAt = _awarenessClearTombstones.get(key);
|
|
152
|
+
if (clearedAt == null) return false;
|
|
153
|
+
if (lastSeen <= clearedAt) return true;
|
|
154
|
+
_awarenessClearTombstones.delete(key);
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
109
157
|
|
|
110
158
|
export function getDocAwareness(docId: string): Map<number, AwarenessEntry> {
|
|
111
159
|
let map = _awarenessMap.get(docId);
|
|
@@ -143,8 +191,13 @@ async function mergeStoredAwareness(
|
|
|
143
191
|
docId: string,
|
|
144
192
|
map: Map<number, AwarenessEntry>,
|
|
145
193
|
): Promise<void> {
|
|
146
|
-
const
|
|
194
|
+
const now = Date.now();
|
|
195
|
+
const rows = await loadAwarenessRows(docId, now);
|
|
147
196
|
for (const row of rows) {
|
|
197
|
+
if (isBlockedByAwarenessClear(docId, row.clientId, row.lastSeen, now)) {
|
|
198
|
+
void deleteAwarenessRow(docId, row.clientId, row.lastSeen);
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
148
201
|
const existing = map.get(row.clientId);
|
|
149
202
|
if (!existing || row.lastSeen > existing.lastSeen) {
|
|
150
203
|
map.set(row.clientId, row);
|
|
@@ -187,10 +240,13 @@ export const postAwareness = defineEventHandler(async (event: H3Event) => {
|
|
|
187
240
|
const map = getDocAwareness(docId);
|
|
188
241
|
|
|
189
242
|
if (state === null) {
|
|
243
|
+
const clearedAt = Date.now();
|
|
190
244
|
map.delete(clientId);
|
|
245
|
+
rememberAwarenessClear(docId, clientId, clearedAt);
|
|
191
246
|
// Best-effort cross-instance removal (never blocks the response).
|
|
192
|
-
void deleteAwarenessRow(docId, clientId);
|
|
247
|
+
void deleteAwarenessRow(docId, clientId, clearedAt);
|
|
193
248
|
} else {
|
|
249
|
+
forgetAwarenessClear(docId, clientId);
|
|
194
250
|
// Store this client's state
|
|
195
251
|
const entry = { clientId, state, lastSeen: Date.now() };
|
|
196
252
|
map.set(clientId, entry);
|
|
@@ -113,12 +113,7 @@ async function runSetAppDefault(args: Record<string, string>): Promise<string> {
|
|
|
113
113
|
if (!isAgentEnginePackageInstalled(entry)) {
|
|
114
114
|
return `Error: Engine "${engine}" requires optional packages that are not installed in this app. Run: pnpm add ${entry.installPackage}`;
|
|
115
115
|
}
|
|
116
|
-
|
|
117
|
-
entry.name === "builder" &&
|
|
118
|
-
normalizeModelForEngine(entry, model) !== model
|
|
119
|
-
) {
|
|
120
|
-
return `Error: Model "${model}" is not supported by Builder. Choose one of: ${entry.supportedModels.join(", ")}`;
|
|
121
|
-
}
|
|
116
|
+
const normalizedModel = normalizeModelForEngine(entry, model);
|
|
122
117
|
|
|
123
118
|
const ctx = currentContext();
|
|
124
119
|
const canUpdate = await canUpdateAgentAppModelDefaultSettings(
|
|
@@ -133,14 +128,18 @@ async function runSetAppDefault(args: Record<string, string>): Promise<string> {
|
|
|
133
128
|
|
|
134
129
|
const settings = await writeAgentAppModelDefaultSettings(ctx, appId, {
|
|
135
130
|
engine,
|
|
136
|
-
model,
|
|
131
|
+
model: normalizedModel,
|
|
137
132
|
updatedBy: ctx.userEmail,
|
|
138
133
|
});
|
|
134
|
+
const normalizedNote =
|
|
135
|
+
normalizedModel === model
|
|
136
|
+
? ""
|
|
137
|
+
: ` Requested model "${model}" is no longer supported, so "${normalizedModel}" was saved instead.`;
|
|
139
138
|
return JSON.stringify(
|
|
140
139
|
{
|
|
141
140
|
ok: true,
|
|
142
141
|
...settings,
|
|
143
|
-
message: `Default model for ${appId} set to ${
|
|
142
|
+
message: `Default model for ${appId} set to ${normalizedModel} via ${entry.label}.${normalizedNote}`,
|
|
144
143
|
},
|
|
145
144
|
null,
|
|
146
145
|
2,
|
|
@@ -53,20 +53,8 @@ export async function run(args: Record<string, string>): Promise<string> {
|
|
|
53
53
|
return `Error: Engine "${engineName}" requires optional packages that are not installed in this app. Run: pnpm add ${entry.installPackage}`;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
entry.name === "builder" &&
|
|
59
|
-
normalizeModelForEngine(entry, resolvedModel) !== resolvedModel
|
|
60
|
-
) {
|
|
61
|
-
return `Error: Model "${resolvedModel}" is not supported by Builder. Choose one of: ${entry.supportedModels.join(", ")}`;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// supportedModels is a suggestion list, not an allowlist — accept any
|
|
65
|
-
// string for BYO/custom engines (OpenRouter / Ollama / previews) and flag
|
|
66
|
-
// uncurated saves. Builder is an enumerated gateway and is checked above.
|
|
67
|
-
const modelIsCurated =
|
|
68
|
-
entry.supportedModels.length === 0 ||
|
|
69
|
-
entry.supportedModels.includes(resolvedModel);
|
|
56
|
+
const requestedModel = model ?? entry.defaultModel;
|
|
57
|
+
const resolvedModel = normalizeModelForEngine(entry, requestedModel);
|
|
70
58
|
|
|
71
59
|
const usable = await isStoredEngineUsableForRequest(
|
|
72
60
|
{ engine: engineName },
|
|
@@ -82,14 +70,15 @@ export async function run(args: Record<string, string>): Promise<string> {
|
|
|
82
70
|
model: resolvedModel,
|
|
83
71
|
});
|
|
84
72
|
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
73
|
+
const normalizedNote =
|
|
74
|
+
resolvedModel === requestedModel
|
|
75
|
+
? ""
|
|
76
|
+
: ` Requested model "${requestedModel}" is no longer supported, so "${resolvedModel}" was saved instead.`;
|
|
88
77
|
|
|
89
78
|
return JSON.stringify({
|
|
90
79
|
ok: true,
|
|
91
80
|
engine: engineName,
|
|
92
81
|
model: resolvedModel,
|
|
93
|
-
message: `Agent engine set to ${entry.label} with model ${resolvedModel}. Takes effect on the next conversation.${
|
|
82
|
+
message: `Agent engine set to ${entry.label} with model ${resolvedModel}. Takes effect on the next conversation.${normalizedNote}`,
|
|
94
83
|
});
|
|
95
84
|
}
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
detectEngineFromEnv,
|
|
26
26
|
detectEngineFromUserSecrets,
|
|
27
27
|
isStoredEngineUsableForRequest,
|
|
28
|
+
normalizeModelForEngine,
|
|
28
29
|
} from "../agent/engine/registry.js";
|
|
29
30
|
import {
|
|
30
31
|
canUpdateAgentLoopSettings,
|
|
@@ -175,6 +176,16 @@ export const FRAMEWORK_ROUTE_PREFIX = "/_agent-native";
|
|
|
175
176
|
export const FRAMEWORK_EVENTS_ROUTE = `${FRAMEWORK_ROUTE_PREFIX}/events`;
|
|
176
177
|
export const LEGACY_FRAMEWORK_EVENTS_ROUTE = `${FRAMEWORK_ROUTE_PREFIX}/poll-events`;
|
|
177
178
|
|
|
179
|
+
export function normalizeAgentEngineStatusModel(
|
|
180
|
+
entry:
|
|
181
|
+
| { name: string; defaultModel: string; supportedModels: readonly string[] }
|
|
182
|
+
| undefined,
|
|
183
|
+
model: string | null | undefined,
|
|
184
|
+
): string {
|
|
185
|
+
if (!entry) return model ?? DEFAULT_MODEL;
|
|
186
|
+
return normalizeModelForEngine(entry, model ?? entry.defaultModel);
|
|
187
|
+
}
|
|
188
|
+
|
|
178
189
|
export function getFrameworkEnvKeys(): EnvKeyConfig[] {
|
|
179
190
|
return [
|
|
180
191
|
{ key: "ENABLE_BUILDER", label: "Enable Builder.io features" },
|
|
@@ -2727,10 +2738,14 @@ export function createCoreRoutesPlugin(
|
|
|
2727
2738
|
if (isAgentEngineSettingConfigured(stored)) {
|
|
2728
2739
|
const engine = (stored as { engine: string }).engine;
|
|
2729
2740
|
const entry = getAgentEngineEntry(engine);
|
|
2741
|
+
const model = normalizeAgentEngineStatusModel(
|
|
2742
|
+
entry,
|
|
2743
|
+
stored?.model,
|
|
2744
|
+
);
|
|
2730
2745
|
return {
|
|
2731
2746
|
configured: true,
|
|
2732
2747
|
engine,
|
|
2733
|
-
model
|
|
2748
|
+
model,
|
|
2734
2749
|
source: "settings" as const,
|
|
2735
2750
|
openAiBaseUrlConfigured,
|
|
2736
2751
|
};
|
|
@@ -2775,10 +2790,14 @@ export function createCoreRoutesPlugin(
|
|
|
2775
2790
|
isStoredEngineUsableForRequest(stored, entry),
|
|
2776
2791
|
))
|
|
2777
2792
|
) {
|
|
2793
|
+
const model = normalizeAgentEngineStatusModel(
|
|
2794
|
+
entry,
|
|
2795
|
+
stored.model,
|
|
2796
|
+
);
|
|
2778
2797
|
return {
|
|
2779
2798
|
configured: true,
|
|
2780
2799
|
engine: stored.engine,
|
|
2781
|
-
model
|
|
2800
|
+
model,
|
|
2782
2801
|
source: "env" as const,
|
|
2783
2802
|
envVar: entry.requiredEnvVars[0],
|
|
2784
2803
|
openAiBaseUrlConfigured,
|
|
@@ -44,10 +44,22 @@ const EMBED_ROUTE_ALIASES: Record<string, string[]> = {
|
|
|
44
44
|
// should survive that first-hop redirect instead of falling back to the
|
|
45
45
|
// private deployment token gate.
|
|
46
46
|
"/": ["/overview"],
|
|
47
|
-
"/dashboard": [
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
47
|
+
"/dashboard": [
|
|
48
|
+
"/dashboards/agent-native-templates-first-party",
|
|
49
|
+
"/adhoc/agent-native-templates-first-party",
|
|
50
|
+
],
|
|
51
|
+
"/dashboards": [
|
|
52
|
+
"/dashboards/agent-native-templates-first-party",
|
|
53
|
+
"/adhoc/agent-native-templates-first-party",
|
|
54
|
+
],
|
|
55
|
+
"/traffic": [
|
|
56
|
+
"/dashboards/agent-native-templates-first-party",
|
|
57
|
+
"/adhoc/agent-native-templates-first-party",
|
|
58
|
+
],
|
|
59
|
+
"/traffic-dashboard": [
|
|
60
|
+
"/dashboards/agent-native-templates-first-party",
|
|
61
|
+
"/adhoc/agent-native-templates-first-party",
|
|
62
|
+
],
|
|
51
63
|
};
|
|
52
64
|
|
|
53
65
|
let _initPromise: Promise<void> | undefined;
|
|
@@ -263,6 +275,10 @@ function openRouteTargetPathnames(targetPath: string): Set<string> {
|
|
|
263
275
|
targets,
|
|
264
276
|
`/adhoc/${encodeURIComponent(dashboardId)}`,
|
|
265
277
|
);
|
|
278
|
+
addResolvedOpenRoutePath(
|
|
279
|
+
targets,
|
|
280
|
+
`/dashboards/${encodeURIComponent(dashboardId)}`,
|
|
281
|
+
);
|
|
266
282
|
}
|
|
267
283
|
const analysisId = safePathSegment(url.searchParams.get("analysisId"));
|
|
268
284
|
if (view === "analyses" && analysisId) {
|
|
@@ -515,6 +515,57 @@
|
|
|
515
515
|
--ring: 215 12% 60%;
|
|
516
516
|
}
|
|
517
517
|
|
|
518
|
+
/* Settings sections: animate between closed and intrinsic open height. */
|
|
519
|
+
.agent-native-settings-section-body {
|
|
520
|
+
display: flow-root;
|
|
521
|
+
height: auto;
|
|
522
|
+
overflow: hidden;
|
|
523
|
+
overflow: clip;
|
|
524
|
+
opacity: 1;
|
|
525
|
+
clip-path: inset(0);
|
|
526
|
+
interpolate-size: allow-keywords;
|
|
527
|
+
transition:
|
|
528
|
+
height 200ms cubic-bezier(0.2, 0, 0, 1),
|
|
529
|
+
opacity 150ms ease,
|
|
530
|
+
clip-path 200ms cubic-bezier(0.2, 0, 0, 1);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
.agent-native-settings-section-body[data-state="closed"] {
|
|
534
|
+
height: 0;
|
|
535
|
+
opacity: 0;
|
|
536
|
+
clip-path: inset(0 0 100% 0);
|
|
537
|
+
pointer-events: none;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
@starting-style {
|
|
541
|
+
.agent-native-settings-section-body[data-state="open"] {
|
|
542
|
+
height: 0;
|
|
543
|
+
opacity: 0;
|
|
544
|
+
clip-path: inset(0 0 100% 0);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
@supports not (interpolate-size: allow-keywords) {
|
|
549
|
+
.agent-native-settings-section-body {
|
|
550
|
+
height: auto;
|
|
551
|
+
max-height: 64rem;
|
|
552
|
+
transition:
|
|
553
|
+
max-height 200ms cubic-bezier(0.2, 0, 0, 1),
|
|
554
|
+
opacity 150ms ease,
|
|
555
|
+
clip-path 200ms cubic-bezier(0.2, 0, 0, 1);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
.agent-native-settings-section-body[data-state="closed"] {
|
|
559
|
+
max-height: 0;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
@media (prefers-reduced-motion: reduce) {
|
|
564
|
+
.agent-native-settings-section-body {
|
|
565
|
+
transition: none;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
|
|
518
569
|
/* Full settings page section bodies.
|
|
519
570
|
The framework's shared settings sections (LLM, App model defaults, Agent
|
|
520
571
|
limits, Email, Account, Secrets, Usage, Voice, Automations, A2A, hosting
|
|
@@ -30,6 +30,10 @@ interface QueuedEvent {
|
|
|
30
30
|
headers?: Record<string, string>;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
interface EnqueueOptions {
|
|
34
|
+
flushImmediately?: boolean;
|
|
35
|
+
}
|
|
36
|
+
|
|
33
37
|
// Use globalThis so multiple ESM graph instances (Vite dev + Nitro symlinks)
|
|
34
38
|
// share one queue, matching the same pattern as the tracking registry.
|
|
35
39
|
const QUEUE_KEY = Symbol.for("@agent-native/core/tracking.queue");
|
|
@@ -59,10 +63,11 @@ function enqueue(
|
|
|
59
63
|
url: string,
|
|
60
64
|
body: string,
|
|
61
65
|
headers?: Record<string, string>,
|
|
66
|
+
options?: EnqueueOptions,
|
|
62
67
|
): void {
|
|
63
68
|
const queue = getQueue();
|
|
64
69
|
queue.push({ url, body, headers });
|
|
65
|
-
if (queue.length >= MAX_BATCH_SIZE) {
|
|
70
|
+
if (options?.flushImmediately || queue.length >= MAX_BATCH_SIZE) {
|
|
66
71
|
void drainQueue();
|
|
67
72
|
} else if (!getTimer()) {
|
|
68
73
|
const timer = setTimeout(() => {
|
|
@@ -132,6 +137,25 @@ function shouldSkipAgentNativeAnalyticsForLocalhost(): boolean {
|
|
|
132
137
|
].some(isLocalhostUrl);
|
|
133
138
|
}
|
|
134
139
|
|
|
140
|
+
function isServerlessRuntime(): boolean {
|
|
141
|
+
return Boolean(
|
|
142
|
+
process.env.NETLIFY ||
|
|
143
|
+
process.env.VERCEL ||
|
|
144
|
+
process.env.AWS_LAMBDA_FUNCTION_NAME ||
|
|
145
|
+
process.env.AWS_EXECUTION_ENV ||
|
|
146
|
+
process.env.LAMBDA_TASK_ROOT ||
|
|
147
|
+
process.env.FUNCTION_NAME,
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function agentNativeAnalyticsFlushesImmediately(): boolean {
|
|
152
|
+
const mode =
|
|
153
|
+
process.env.AGENT_NATIVE_ANALYTICS_FLUSH_MODE?.trim().toLowerCase();
|
|
154
|
+
if (mode === "batch") return false;
|
|
155
|
+
if (mode === "immediate") return true;
|
|
156
|
+
return isServerlessRuntime();
|
|
157
|
+
}
|
|
158
|
+
|
|
135
159
|
// ─── PostHog ───────────────────────────────────────────────────────────────
|
|
136
160
|
|
|
137
161
|
function isPostHogAiObservabilityEvent(eventName: string): boolean {
|
|
@@ -307,6 +331,7 @@ function createAgentNativeAnalyticsProvider(
|
|
|
307
331
|
publicKey: string,
|
|
308
332
|
endpoint: string,
|
|
309
333
|
): TrackingProvider {
|
|
334
|
+
const flushImmediately = agentNativeAnalyticsFlushesImmediately();
|
|
310
335
|
return {
|
|
311
336
|
name: "agent-native-analytics",
|
|
312
337
|
track(event: TrackingEvent) {
|
|
@@ -319,6 +344,8 @@ function createAgentNativeAnalyticsProvider(
|
|
|
319
344
|
userId: event.userId,
|
|
320
345
|
timestamp: event.timestamp,
|
|
321
346
|
}),
|
|
347
|
+
undefined,
|
|
348
|
+
{ flushImmediately },
|
|
322
349
|
);
|
|
323
350
|
},
|
|
324
351
|
identify(userId, traits) {
|
|
@@ -331,6 +358,8 @@ function createAgentNativeAnalyticsProvider(
|
|
|
331
358
|
properties: traits ?? {},
|
|
332
359
|
timestamp: new Date().toISOString(),
|
|
333
360
|
}),
|
|
361
|
+
undefined,
|
|
362
|
+
{ flushImmediately },
|
|
334
363
|
);
|
|
335
364
|
},
|
|
336
365
|
flush: () => {
|
|
@@ -366,7 +395,9 @@ export function registerBuiltinProviders(): void {
|
|
|
366
395
|
registerTrackingProvider(createAmplitudeProvider(amplitudeKey));
|
|
367
396
|
}
|
|
368
397
|
|
|
369
|
-
const agentNativeAnalyticsKey =
|
|
398
|
+
const agentNativeAnalyticsKey =
|
|
399
|
+
process.env.AGENT_NATIVE_ANALYTICS_PUBLIC_KEY ||
|
|
400
|
+
process.env.VITE_AGENT_NATIVE_ANALYTICS_PUBLIC_KEY;
|
|
370
401
|
if (
|
|
371
402
|
agentNativeAnalyticsKey &&
|
|
372
403
|
!shouldSkipAgentNativeAnalyticsForLocalhost()
|
|
@@ -199,6 +199,7 @@ export default function ExplorerDashboardPage() {
|
|
|
199
199
|
const [loaded, setLoaded] = useState(false);
|
|
200
200
|
const [confirmDeleteOpen, setConfirmDeleteOpen] = useState(false);
|
|
201
201
|
const [historyOpen, setHistoryOpen] = useState(false);
|
|
202
|
+
const [dashboardActionsOpen, setDashboardActionsOpen] = useState(false);
|
|
202
203
|
const [activeDragChartId, setActiveDragChartId] = useState<string | null>(
|
|
203
204
|
null,
|
|
204
205
|
);
|
|
@@ -620,7 +621,10 @@ export default function ExplorerDashboardPage() {
|
|
|
620
621
|
</Button>
|
|
621
622
|
) : null}
|
|
622
623
|
{dashboardId || canEdit || canManage ? (
|
|
623
|
-
<DropdownMenu
|
|
624
|
+
<DropdownMenu
|
|
625
|
+
open={dashboardActionsOpen}
|
|
626
|
+
onOpenChange={setDashboardActionsOpen}
|
|
627
|
+
>
|
|
624
628
|
<Tooltip>
|
|
625
629
|
<TooltipTrigger asChild>
|
|
626
630
|
<DropdownMenuTrigger asChild>
|
|
@@ -643,6 +647,7 @@ export default function ExplorerDashboardPage() {
|
|
|
643
647
|
<DropdownMenuItem
|
|
644
648
|
onSelect={(event) => {
|
|
645
649
|
event.preventDefault();
|
|
650
|
+
setDashboardActionsOpen(false);
|
|
646
651
|
setHistoryOpen(true);
|
|
647
652
|
}}
|
|
648
653
|
>
|
|
@@ -657,6 +662,7 @@ export default function ExplorerDashboardPage() {
|
|
|
657
662
|
<DropdownMenuItem
|
|
658
663
|
onSelect={(event) => {
|
|
659
664
|
event.preventDefault();
|
|
665
|
+
setDashboardActionsOpen(false);
|
|
660
666
|
void handleArchive();
|
|
661
667
|
}}
|
|
662
668
|
>
|
|
@@ -671,6 +677,7 @@ export default function ExplorerDashboardPage() {
|
|
|
671
677
|
<DropdownMenuItem
|
|
672
678
|
onSelect={(event) => {
|
|
673
679
|
event.preventDefault();
|
|
680
|
+
setDashboardActionsOpen(false);
|
|
674
681
|
setConfirmDeleteOpen(true);
|
|
675
682
|
}}
|
|
676
683
|
className="text-destructive focus:text-destructive"
|
|
@@ -464,6 +464,7 @@ export default function SqlDashboardPage() {
|
|
|
464
464
|
const [confirmDeleteOpen, setConfirmDeleteOpen] = useState(false);
|
|
465
465
|
const [emailReportOpen, setEmailReportOpen] = useState(false);
|
|
466
466
|
const [historyOpen, setHistoryOpen] = useState(false);
|
|
467
|
+
const [dashboardActionsOpen, setDashboardActionsOpen] = useState(false);
|
|
467
468
|
const [activeDropSlot, setActiveDropSlot] =
|
|
468
469
|
useState<DashboardDropSlot | null>(null);
|
|
469
470
|
const [activeDragPanelId, setActiveDragPanelId] = useState<string | null>(
|
|
@@ -1395,7 +1396,10 @@ export default function SqlDashboardPage() {
|
|
|
1395
1396
|
</Button>
|
|
1396
1397
|
</AddPanelPopover>
|
|
1397
1398
|
) : null}
|
|
1398
|
-
<DropdownMenu
|
|
1399
|
+
<DropdownMenu
|
|
1400
|
+
open={dashboardActionsOpen}
|
|
1401
|
+
onOpenChange={setDashboardActionsOpen}
|
|
1402
|
+
>
|
|
1399
1403
|
<Tooltip>
|
|
1400
1404
|
<TooltipTrigger asChild>
|
|
1401
1405
|
<DropdownMenuTrigger asChild>
|
|
@@ -1468,6 +1472,7 @@ export default function SqlDashboardPage() {
|
|
|
1468
1472
|
<DropdownMenuItem
|
|
1469
1473
|
onSelect={(event) => {
|
|
1470
1474
|
event.preventDefault();
|
|
1475
|
+
setDashboardActionsOpen(false);
|
|
1471
1476
|
setEmailReportOpen(true);
|
|
1472
1477
|
}}
|
|
1473
1478
|
>
|
|
@@ -1477,6 +1482,7 @@ export default function SqlDashboardPage() {
|
|
|
1477
1482
|
<DropdownMenuItem
|
|
1478
1483
|
onSelect={(event) => {
|
|
1479
1484
|
event.preventDefault();
|
|
1485
|
+
setDashboardActionsOpen(false);
|
|
1480
1486
|
setHistoryOpen(true);
|
|
1481
1487
|
}}
|
|
1482
1488
|
>
|
|
@@ -1489,6 +1495,7 @@ export default function SqlDashboardPage() {
|
|
|
1489
1495
|
<DropdownMenuItem
|
|
1490
1496
|
onSelect={(event) => {
|
|
1491
1497
|
event.preventDefault();
|
|
1498
|
+
setDashboardActionsOpen(false);
|
|
1492
1499
|
void handleArchive();
|
|
1493
1500
|
}}
|
|
1494
1501
|
>
|
|
@@ -1503,6 +1510,7 @@ export default function SqlDashboardPage() {
|
|
|
1503
1510
|
<DropdownMenuItem
|
|
1504
1511
|
onSelect={(event) => {
|
|
1505
1512
|
event.preventDefault();
|
|
1513
|
+
setDashboardActionsOpen(false);
|
|
1506
1514
|
setConfirmDeleteOpen(true);
|
|
1507
1515
|
}}
|
|
1508
1516
|
className="text-destructive focus:text-destructive"
|