@agent-native/core 0.161.10 → 0.161.14
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 +1 -1
- package/corpus/templates/analytics/actions/clear-selected-dashboard-object.ts +71 -0
- package/corpus/templates/analytics/actions/view-screen.ts +68 -4
- package/corpus/templates/analytics/app/hooks/use-dashboard-chat-context.ts +41 -66
- package/corpus/templates/analytics/app/lib/selected-object.ts +53 -0
- package/corpus/templates/analytics/app/pages/Ask.tsx +21 -0
- package/corpus/templates/analytics/changelog/2026-08-18-ask-no-stale-dashboard-context.md +6 -0
- package/corpus/templates/calendar/app/root.tsx +12 -4
- package/corpus/templates/calendar/changelog/2026-08-18-calendar-stays-responsive-in-the-desktop-app-without-focus-t.md +6 -0
- package/corpus/templates/clips/app/components/library/recording-card.tsx +19 -3
- package/corpus/templates/clips/changelog/2026-08-18-deleting-a-recording-no-longer-freezes-clips.md +6 -0
- package/corpus/templates/clips/desktop/src/app.tsx +9 -10
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +4 -3
- package/corpus/templates/clips/desktop/src/lib/recording-start-guard.ts +26 -0
- package/corpus/templates/content/actions/export-document.ts +68 -3
- package/corpus/templates/content/changelog/2026-08-06-database-exports-now-include-their-pages.md +6 -0
- package/corpus/templates/content/shared/document-export.ts +25 -0
- package/corpus/templates/design/.agents/skills/creative-context/SKILL.md +3 -0
- package/corpus/templates/design/app/components/editor/PromptDialog.tsx +66 -0
- package/corpus/templates/design/app/pages/Index.tsx +45 -1
- package/corpus/templates/design/app/pages/design-editor/creative-context-precedent.ts +233 -0
- package/corpus/templates/design/changelog/2026-08-11-new-designs-now-skip-the-intake-questions-and-follow-your-ex.md +6 -0
- package/corpus/templates/dispatch/app/chat-first-pages.ts +40 -40
- package/corpus/templates/dispatch/app/i18n-data.ts +5 -5
- package/corpus/templates/factory/AGENTS.md +30 -29
- package/corpus/templates/slides/app/components/layout/AgentWorkIndicator.tsx +56 -9
- package/dist/action.js +68 -2
- package/dist/agent/chat-health-alert.js +10 -1
- package/dist/client/AssistantChat.js +1 -0
- package/dist/client/chat-first/app-open-actions.js +1 -1
- package/dist/client/chat-first/copy.js +2 -2
- package/dist/client/conversation/use-near-bottom-autoscroll.js +34 -4
- package/dist/client/embed-auth.js +40 -3
- package/dist/client/mcp-apps/McpAppRenderer.js +7 -2
- package/dist/client/route-state.js +42 -5
- package/dist/client/use-change-version.js +71 -25
- package/dist/client/use-db-sync.d.ts +1 -0
- package/dist/client/use-db-sync.js +223 -45
- package/dist/collab/awareness.js +37 -4
- package/dist/collab/client.js +17 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/mcp/embed-app.js +71 -2
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +3 -3
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/poll.d.ts +22 -14
- package/dist/server/poll.js +243 -67
- package/dist/styles/agent-conversation.css +25 -4
- package/package.json +1 -1
package/dist/mcp/embed-app.js
CHANGED
|
@@ -47,6 +47,8 @@ export function embedApp(options = {}) {
|
|
|
47
47
|
iframe { display: block; width: 100%; height: var(--agent-native-viewport-height); border: 0; background: Canvas; }
|
|
48
48
|
.message { display: grid; place-items: center; min-height: var(--agent-native-viewport-height); padding: 18px; color: color-mix(in srgb, CanvasText 62%, Canvas); font-size: 13px; line-height: 1.45; text-align: center; }
|
|
49
49
|
.fallback { display: grid; align-content: center; justify-items: center; gap: 12px; min-height: var(--agent-native-viewport-height); padding: 24px; background: Canvas; color: CanvasText; text-align: center; }
|
|
50
|
+
.fallback-overlay { position: absolute; inset: 0; z-index: 2; display: grid; align-items: start; justify-items: end; padding: 12px; pointer-events: none; }
|
|
51
|
+
.fallback-overlay .fallback { width: min(100%, 560px); min-height: 0; border: 1px solid color-mix(in srgb, CanvasText 18%, Canvas); border-radius: 9px; background: color-mix(in srgb, Canvas 96%, transparent); box-shadow: 0 8px 24px color-mix(in srgb, CanvasText 16%, transparent); pointer-events: auto; }
|
|
50
52
|
.fallback-title { max-width: 440px; font-size: 14px; font-weight: 700; }
|
|
51
53
|
.fallback-copy { max-width: 520px; color: color-mix(in srgb, CanvasText 64%, Canvas); font-size: 13px; line-height: 1.45; }
|
|
52
54
|
.fallback-actions { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 8px; }
|
|
@@ -1073,14 +1075,64 @@ export function embedApp(options = {}) {
|
|
|
1073
1075
|
} catch {}
|
|
1074
1076
|
}
|
|
1075
1077
|
|
|
1078
|
+
function clearFallbackOverlay() {
|
|
1079
|
+
stage.querySelector("[data-fallback-overlay]")?.remove();
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
function renderFallbackOverlay({ title, copyHtml, retryLabel, onRetry }) {
|
|
1083
|
+
if (!appFrame || appFrame.parentElement !== stage) return false;
|
|
1084
|
+
clearFallbackOverlay();
|
|
1085
|
+
const overlay = document.createElement("div");
|
|
1086
|
+
overlay.className = "fallback-overlay";
|
|
1087
|
+
overlay.dataset.fallbackOverlay = "1";
|
|
1088
|
+
overlay.innerHTML =
|
|
1089
|
+
'<div class="fallback">' +
|
|
1090
|
+
'<div class="fallback-title">' + esc(title) + '</div>' +
|
|
1091
|
+
'<div class="fallback-copy">' + copyHtml + '</div>' +
|
|
1092
|
+
'<div class="fallback-actions">' +
|
|
1093
|
+
'<button type="button" class="primary" data-fallback-open>Open in new tab</button>' +
|
|
1094
|
+
'<button type="button" data-fallback-retry>' + esc(retryLabel) + '</button>' +
|
|
1095
|
+
'</div>' +
|
|
1096
|
+
(openUrl ? '<a class="fallback-url" href="' + esc(openUrl) + '" target="_blank" rel="noreferrer">' + esc(openUrl) + '</a>' : '') +
|
|
1097
|
+
'</div>';
|
|
1098
|
+
stage.appendChild(overlay);
|
|
1099
|
+
|
|
1100
|
+
const fallbackOpen = overlay.querySelector("[data-fallback-open]");
|
|
1101
|
+
const fallbackRetry = overlay.querySelector("[data-fallback-retry]");
|
|
1102
|
+
if (fallbackOpen) {
|
|
1103
|
+
fallbackOpen.disabled = !openUrl;
|
|
1104
|
+
fallbackOpen.onclick = () => {
|
|
1105
|
+
if (openUrl) void openFallbackExternal();
|
|
1106
|
+
};
|
|
1107
|
+
}
|
|
1108
|
+
if (fallbackRetry) {
|
|
1109
|
+
fallbackRetry.disabled = typeof onRetry !== "function";
|
|
1110
|
+
fallbackRetry.onclick = () => {
|
|
1111
|
+
if (typeof onRetry === "function") void onRetry();
|
|
1112
|
+
};
|
|
1113
|
+
}
|
|
1114
|
+
return true;
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1076
1117
|
function renderFrameFallback() {
|
|
1077
1118
|
clearFrameReadyTimer();
|
|
1078
1119
|
clearFrameLoadTimer();
|
|
1079
|
-
|
|
1120
|
+
appFrameReady = false;
|
|
1080
1121
|
const fallbackCopy = openUrl
|
|
1081
1122
|
? "This chat host did not allow the embedded app frame to load inline. You can still open the same app route through the host or use the URL below."
|
|
1082
1123
|
: "This chat host did not allow the embedded app frame to load inline.";
|
|
1083
1124
|
reportEmbedError("frame-fallback", fallbackCopy);
|
|
1125
|
+
if (renderFallbackOverlay({
|
|
1126
|
+
title: "Open this app in its own tab",
|
|
1127
|
+
copyHtml: esc(fallbackCopy),
|
|
1128
|
+
retryLabel: "Try inline again",
|
|
1129
|
+
onRetry: lastFrameSrc
|
|
1130
|
+
? () => renderFrame(lastFrameSrc)
|
|
1131
|
+
: undefined
|
|
1132
|
+
})) {
|
|
1133
|
+
return;
|
|
1134
|
+
}
|
|
1135
|
+
appFrame = null;
|
|
1084
1136
|
stage.innerHTML =
|
|
1085
1137
|
'<div class="fallback">' +
|
|
1086
1138
|
'<div class="fallback-title">Open this app in its own tab</div>' +
|
|
@@ -1110,7 +1162,7 @@ export function embedApp(options = {}) {
|
|
|
1110
1162
|
function renderAppLaunchError(message) {
|
|
1111
1163
|
clearFrameReadyTimer();
|
|
1112
1164
|
clearFrameLoadTimer();
|
|
1113
|
-
|
|
1165
|
+
appFrameReady = false;
|
|
1114
1166
|
const fallbackCopy = openUrl
|
|
1115
1167
|
? "The inline MCP app could not load in this chat host. You can open the same app route in a new tab or retry the inline load."
|
|
1116
1168
|
: "The inline MCP app could not load in this chat host.";
|
|
@@ -1118,6 +1170,18 @@ export function embedApp(options = {}) {
|
|
|
1118
1170
|
const copyHtml = message
|
|
1119
1171
|
? '<div>' + esc(message) + '</div><div>' + esc(fallbackCopy) + '</div>'
|
|
1120
1172
|
: esc(fallbackCopy);
|
|
1173
|
+
if (renderFallbackOverlay({
|
|
1174
|
+
title: "App did not load",
|
|
1175
|
+
copyHtml,
|
|
1176
|
+
retryLabel: "Retry",
|
|
1177
|
+
onRetry: () => {
|
|
1178
|
+
startedFor = "";
|
|
1179
|
+
void launchEmbed();
|
|
1180
|
+
}
|
|
1181
|
+
})) {
|
|
1182
|
+
return;
|
|
1183
|
+
}
|
|
1184
|
+
appFrame = null;
|
|
1121
1185
|
stage.innerHTML =
|
|
1122
1186
|
'<div class="fallback">' +
|
|
1123
1187
|
'<div class="fallback-title">App did not load</div>' +
|
|
@@ -1178,6 +1242,10 @@ export function embedApp(options = {}) {
|
|
|
1178
1242
|
sendFrameReadyMessages(frame);
|
|
1179
1243
|
startFrameReadyTimer(frame);
|
|
1180
1244
|
});
|
|
1245
|
+
frame.addEventListener("error", () => {
|
|
1246
|
+
if (appFrame !== frame) return;
|
|
1247
|
+
renderFrameFallback();
|
|
1248
|
+
});
|
|
1181
1249
|
stage.replaceChildren(frame);
|
|
1182
1250
|
notifyHostHeight();
|
|
1183
1251
|
appFrameLoadTimer = setTimeout(() => {
|
|
@@ -1475,6 +1543,7 @@ export function embedApp(options = {}) {
|
|
|
1475
1543
|
const data = event.data.data || {};
|
|
1476
1544
|
if (message.type === "agentNative.embeddedAppReady") {
|
|
1477
1545
|
appFrameReady = true;
|
|
1546
|
+
clearFallbackOverlay();
|
|
1478
1547
|
embedSessionRefreshAttempts = 0;
|
|
1479
1548
|
clearFrameLoadTimer();
|
|
1480
1549
|
clearFrameReadyTimer();
|
|
@@ -42,22 +42,22 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
|
|
|
42
42
|
avgEvalScore: number;
|
|
43
43
|
} | {
|
|
44
44
|
error?: undefined;
|
|
45
|
-
ok?: undefined;
|
|
46
45
|
summary: import("./types.js").TraceSummary;
|
|
47
46
|
spans: import("./types.js").TraceSpan[];
|
|
48
47
|
id?: undefined;
|
|
48
|
+
ok?: undefined;
|
|
49
49
|
} | {
|
|
50
50
|
error?: undefined;
|
|
51
|
-
ok?: undefined;
|
|
52
51
|
summary?: undefined;
|
|
53
52
|
spans?: undefined;
|
|
54
53
|
id: string;
|
|
55
|
-
} | {
|
|
56
54
|
ok?: undefined;
|
|
55
|
+
} | {
|
|
57
56
|
summary?: undefined;
|
|
58
57
|
spans?: undefined;
|
|
59
58
|
id?: undefined;
|
|
60
59
|
error: any;
|
|
60
|
+
ok?: undefined;
|
|
61
61
|
} | {
|
|
62
62
|
error?: undefined;
|
|
63
63
|
summary?: undefined;
|
package/dist/secrets/routes.d.ts
CHANGED
|
@@ -34,37 +34,37 @@ export declare function createListSecretsHandler(): import("h3").EventHandlerWit
|
|
|
34
34
|
/** POST /_agent-native/secrets/:key — write a secret. */
|
|
35
35
|
export declare function createWriteSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
36
36
|
error: string;
|
|
37
|
-
ok?: undefined;
|
|
38
37
|
status?: undefined;
|
|
38
|
+
ok?: undefined;
|
|
39
39
|
} | {
|
|
40
|
-
error?: undefined;
|
|
41
40
|
ok: boolean;
|
|
42
41
|
status: string;
|
|
42
|
+
error?: undefined;
|
|
43
43
|
} | {
|
|
44
|
-
ok?: undefined;
|
|
45
44
|
error: string;
|
|
46
45
|
removed?: undefined;
|
|
46
|
+
ok?: undefined;
|
|
47
47
|
} | {
|
|
48
|
-
error?: undefined;
|
|
49
48
|
ok: boolean;
|
|
50
49
|
removed: boolean;
|
|
50
|
+
error?: undefined;
|
|
51
51
|
}>>;
|
|
52
52
|
/**
|
|
53
53
|
* POST /_agent-native/secrets/:key/test — validate an optional candidate value
|
|
54
54
|
* or the current stored value without changing anything.
|
|
55
55
|
*/
|
|
56
56
|
export declare function createTestSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
57
|
-
ok?: undefined;
|
|
58
57
|
error: string;
|
|
59
58
|
note?: undefined;
|
|
59
|
+
ok?: undefined;
|
|
60
60
|
} | {
|
|
61
|
-
error?: undefined;
|
|
62
61
|
ok: boolean;
|
|
63
62
|
note?: undefined;
|
|
64
|
-
} | {
|
|
65
63
|
error?: undefined;
|
|
64
|
+
} | {
|
|
66
65
|
ok: boolean;
|
|
67
66
|
note: string;
|
|
67
|
+
error?: undefined;
|
|
68
68
|
} | {
|
|
69
69
|
note?: undefined;
|
|
70
70
|
ok: boolean;
|
|
@@ -95,11 +95,11 @@ export interface AdHocSecretPayload {
|
|
|
95
95
|
export declare function createAdHocSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<AdHocSecretPayload[] | {
|
|
96
96
|
error: string;
|
|
97
97
|
} | {
|
|
98
|
-
error?: undefined;
|
|
99
98
|
ok: boolean;
|
|
100
99
|
key: string;
|
|
101
|
-
} | {
|
|
102
100
|
error?: undefined;
|
|
101
|
+
} | {
|
|
103
102
|
ok: boolean;
|
|
104
103
|
removed: boolean;
|
|
104
|
+
error?: undefined;
|
|
105
105
|
}>>;
|
package/dist/server/poll.d.ts
CHANGED
|
@@ -23,6 +23,8 @@ import { EventEmitter } from "node:events";
|
|
|
23
23
|
import { type DbExec } from "../db/client.js";
|
|
24
24
|
export interface ChangeEvent {
|
|
25
25
|
version: number;
|
|
26
|
+
/** Stable durable-row tie-breaker for events sharing a millisecond version. */
|
|
27
|
+
cursorId?: string;
|
|
26
28
|
source: string;
|
|
27
29
|
type: string;
|
|
28
30
|
key?: string;
|
|
@@ -53,9 +55,15 @@ export interface ChangeEvent {
|
|
|
53
55
|
}
|
|
54
56
|
export declare const DURABLE_LEGACY_DB_CHECK_INTERVAL_MS = 30000;
|
|
55
57
|
export declare const POLL_CHANGE_EVENT = "poll-change";
|
|
58
|
+
type SyncCursor = {
|
|
59
|
+
version: number;
|
|
60
|
+
id: string;
|
|
61
|
+
};
|
|
56
62
|
export type ChangeReadResult = {
|
|
57
63
|
version: number;
|
|
58
64
|
events: ChangeEvent[];
|
|
65
|
+
/** Composite `(version,id)` cursor for durable rows sharing a version. */
|
|
66
|
+
cursor?: string;
|
|
59
67
|
/**
|
|
60
68
|
* True when the returned version is an intentional cursor stop, not the
|
|
61
69
|
* source high-water mark. This happens when access is still pending or when a
|
|
@@ -143,11 +151,13 @@ export declare class AppSyncState {
|
|
|
143
151
|
private dbVersionFallbacks;
|
|
144
152
|
private warnedListenerThrow;
|
|
145
153
|
private version;
|
|
154
|
+
private latestCursor;
|
|
146
155
|
private readonly buffer;
|
|
147
156
|
private readonly pollEmitter;
|
|
148
157
|
private syncEventsInitPromise;
|
|
149
158
|
private lastDurablePrune;
|
|
150
159
|
private durablePruneFailures;
|
|
160
|
+
private durableWriteFailures;
|
|
151
161
|
/**
|
|
152
162
|
* Whether we've seeded `version` from the DB. In serverless (Netlify,
|
|
153
163
|
* Vercel, etc.) each invocation starts fresh — without seeding, `version`
|
|
@@ -175,8 +185,9 @@ export declare class AppSyncState {
|
|
|
175
185
|
* after a restart (where an existing row would look like a fresh bump).
|
|
176
186
|
*/
|
|
177
187
|
private lastScreenRefreshTs;
|
|
188
|
+
private lastScreenRefreshSessionId;
|
|
178
189
|
private screenRefreshInitialized;
|
|
179
|
-
private
|
|
190
|
+
private screenRefreshHasMore;
|
|
180
191
|
private localEmittersWired;
|
|
181
192
|
/**
|
|
182
193
|
* TTL'd access cache for the access-aware branch of `canSeeChangeForUser`,
|
|
@@ -232,13 +243,15 @@ export declare class AppSyncState {
|
|
|
232
243
|
* `sync_events_created_at_id_idx` drives the batch and the delete is a
|
|
233
244
|
* bounded index range scan.
|
|
234
245
|
*
|
|
235
|
-
* The `lastDurablePrune` throttle below is per-process
|
|
236
|
-
*
|
|
246
|
+
* The `lastDurablePrune` throttle below is per-process and starts at process
|
|
247
|
+
* initialization, so a cold serverless worker does not run a potentially
|
|
248
|
+
* large delete during its first request. Postgres workers also take a
|
|
237
249
|
* transaction-scoped advisory lease for each batch. The lease and delete
|
|
238
250
|
* are one bounded statement, so a serverless worker killed after the
|
|
239
251
|
* statement starts cannot leave an open transaction.
|
|
240
252
|
*/
|
|
241
253
|
private pruneDurableEvents;
|
|
254
|
+
private reportDurableWriteFailure;
|
|
242
255
|
persistSyncEvent(event: ChangeEvent, dedupeKey?: string, presetId?: string): Promise<void>;
|
|
243
256
|
/**
|
|
244
257
|
* Persist with a DB-allocated version (see ALLOCATING_INSERT_SQL). Returns
|
|
@@ -340,12 +353,9 @@ export declare class AppSyncState {
|
|
|
340
353
|
* Get changes after a given version, filtered to events the caller is
|
|
341
354
|
* allowed to see.
|
|
342
355
|
*/
|
|
343
|
-
getChangesSinceForUser(since: number, userEmail: string, orgId: string | undefined): ChangeReadResult;
|
|
344
|
-
getDurableChangesSinceForUser(since: number, userEmail: string, orgId: string | undefined): Promise<ChangeReadResult>;
|
|
345
|
-
getCombinedChangesSinceForUser(since: number, userEmail: string, orgId: string | undefined, useDurableEvents: boolean): Promise<
|
|
346
|
-
version: number;
|
|
347
|
-
events: ChangeEvent[];
|
|
348
|
-
}>;
|
|
356
|
+
getChangesSinceForUser(since: number, userEmail: string, orgId: string | undefined, cursor?: SyncCursor): ChangeReadResult;
|
|
357
|
+
getDurableChangesSinceForUser(since: number, userEmail: string, orgId: string | undefined, cursor?: SyncCursor): Promise<ChangeReadResult>;
|
|
358
|
+
getCombinedChangesSinceForUser(since: number, userEmail: string, orgId: string | undefined, useDurableEvents: boolean, cursor?: SyncCursor): Promise<ChangeReadResult>;
|
|
349
359
|
/**
|
|
350
360
|
* Seed `version` from DB timestamps on the first call so serverless cold
|
|
351
361
|
* starts don't return version 0 and confuse polling clients.
|
|
@@ -395,7 +405,7 @@ export declare function getChangesSinceForUser(since: number, userEmail: string,
|
|
|
395
405
|
/**
|
|
396
406
|
* Create an H3 handler for the poll endpoint.
|
|
397
407
|
*
|
|
398
|
-
* GET /_agent-native/poll?since=N → { version, events[] }
|
|
408
|
+
* GET /_agent-native/poll?since=N[&cursor=N.id] → { version, events[] }
|
|
399
409
|
*
|
|
400
410
|
* Requires an authenticated session. Events are filtered to the caller's
|
|
401
411
|
* tenant — global events (owner-less, table-level pings) reach every
|
|
@@ -403,9 +413,7 @@ export declare function getChangesSinceForUser(since: number, userEmail: string,
|
|
|
403
413
|
* Without auth + filtering, an anonymous attacker could poll the deployment
|
|
404
414
|
* and infer cross-tenant activity from the global event stream.
|
|
405
415
|
*/
|
|
406
|
-
export declare function createPollHandler(state?: AppSyncState): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
407
|
-
version: number;
|
|
408
|
-
events: ChangeEvent[];
|
|
409
|
-
} | {
|
|
416
|
+
export declare function createPollHandler(state?: AppSyncState): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<ChangeReadResult | {
|
|
410
417
|
error: string;
|
|
411
418
|
}>>;
|
|
419
|
+
export {};
|