@agent-native/core 0.0.0-beta-20260820150521 → 0.0.0-beta-20260820171000
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/changelog/2026-08-20-staged-data-limit-recovery.md +5 -0
- package/corpus/templates/clips/changelog/2026-08-20-cross-tab-recording-controls.md +5 -0
- package/corpus/templates/clips/changelog/2026-08-20-overlay-follow-review-fixes.md +5 -0
- package/corpus/templates/clips/changelog/2026-08-20-provider-key-recovery.md +5 -0
- package/corpus/templates/clips/chrome-extension/PERMISSIONS.md +29 -46
- package/corpus/templates/clips/chrome-extension/STORE_DRAFT.md +1 -1
- package/corpus/templates/clips/chrome-extension/public/manifest.json +9 -2
- package/corpus/templates/clips/chrome-extension/src/background.ts +65 -46
- package/corpus/templates/clips/chrome-extension/src/content-script.ts +25 -17
- package/corpus/templates/clips/chrome-extension/src/overlay-follow.ts +25 -0
- package/corpus/templates/dispatch/changelog/2026-08-20-dispatch-chat-loads-openrouter-provider-reliably.md +6 -0
- package/corpus/templates/dispatch/package.json +2 -0
- package/corpus/templates/macros/netlify.toml +2 -0
- package/dist/client/chat/run-recovery.js +8 -3
- package/dist/client/integrations/IntegrationsPanel.js +12 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/mcp/screen-memory-stdio.d.ts +7 -7
- package/dist/mcp-client/oauth-routes.d.ts +1 -0
- package/dist/mcp-client/oauth-routes.js +37 -9
- package/dist/mcp-client/remote-store.d.ts +12 -0
- package/dist/mcp-client/remote-store.js +71 -0
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +3 -3
- package/dist/provider-api/actions/provider-api.js +16 -4
- package/dist/resources/handlers.d.ts +2 -2
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/package.json +2 -2
package/corpus/README.md
CHANGED
|
@@ -1,41 +1,45 @@
|
|
|
1
1
|
# Permissions model
|
|
2
2
|
|
|
3
|
-
This extension ships **
|
|
4
|
-
|
|
3
|
+
This extension ships **cross-tab follow for recording overlays**: it declares
|
|
4
|
+
the broad `<all_urls>` host permission and a matching declarative content script
|
|
5
|
+
so the camera bubble and controls can survive tab switches and navigations.
|
|
5
6
|
|
|
6
7
|
## Why
|
|
7
8
|
|
|
8
9
|
A declarative content script on `<all_urls>` (and the matching `<all_urls>` host
|
|
9
10
|
permission) triggers Chrome Web Store's **broad host permission in-depth review**,
|
|
10
|
-
which significantly delays publishing and updates. We
|
|
11
|
+
which significantly delays publishing and updates. We take that review path only
|
|
12
|
+
for the release that re-enables cross-tab follow.
|
|
11
13
|
|
|
12
|
-
The
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
The declarative content scripts are scoped to:
|
|
15
|
+
|
|
16
|
+
- `https://github.com/*` for Clips link previews in GitHub issue/PR markdown.
|
|
17
|
+
- `<all_urls>` for the recording overlay host that keeps the face bubble and
|
|
18
|
+
controls mounted across pages while a recording is active.
|
|
19
|
+
|
|
20
|
+
The GitHub preview script adds a narrow host disclosure, while the overlay host
|
|
21
|
+
is the broad review boundary.
|
|
16
22
|
|
|
17
23
|
## How the overlay gets on the page
|
|
18
24
|
|
|
19
25
|
When the user clicks the extension and starts a recording, the background service
|
|
20
|
-
worker
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
needed. The content script then mounts the overlay iframes (countdown, camera
|
|
25
|
-
bubble, controls). `web_accessible_resources` stays `<all_urls>` — that is **not**
|
|
26
|
-
a host permission and does not trigger the review.
|
|
26
|
+
worker mounts the overlay on the active tab and keeps rebroadcasting it as the
|
|
27
|
+
active tab changes. The content script then mounts the overlay iframes
|
|
28
|
+
(countdown, camera bubble, controls). `web_accessible_resources` stays
|
|
29
|
+
`<all_urls>` — that is **not** a host permission and does not trigger the review.
|
|
27
30
|
|
|
28
31
|
Declared permissions: `activeTab`, `debugger`, `offscreen`, `scripting`,
|
|
29
|
-
`storage`. Host permissions:
|
|
32
|
+
`storage`. Host permissions: the configured Clips app + `forms.agent-native.com`
|
|
30
33
|
|
|
31
34
|
- `localhost`/`127.0.0.1`.
|
|
32
|
-
- `https://github.com/*` is content-script scoped for link previews
|
|
35
|
+
- `https://github.com/*` is content-script scoped for link previews.
|
|
36
|
+
- `<all_urls>` is the overlay host required for cross-tab follow.
|
|
33
37
|
|
|
34
38
|
## What this costs
|
|
35
39
|
|
|
36
|
-
- The overlay (countdown, camera bubble, recording controls)
|
|
37
|
-
|
|
38
|
-
|
|
40
|
+
- The overlay (countdown, camera bubble, recording controls) follows the user
|
|
41
|
+
across tabs while a recording is active. Navigating to a new page or tab keeps
|
|
42
|
+
the face bubble and controls mounted once the page loads.
|
|
39
43
|
- **Recording itself is unaffected.** Capture runs in the offscreen document via
|
|
40
44
|
`getDisplayMedia`, independent of any tab's content script — full-screen,
|
|
41
45
|
window, and other-tab content are all still captured normally. Only the on-page
|
|
@@ -43,36 +47,15 @@ Declared permissions: `activeTab`, `debugger`, `offscreen`, `scripting`,
|
|
|
43
47
|
|
|
44
48
|
## Re-enabling cross-tab follow
|
|
45
49
|
|
|
46
|
-
The full cross-tab behavior is gated behind a single flag in
|
|
47
|
-
`src/background.ts`:
|
|
50
|
+
The full cross-tab behavior is gated behind a single flag in `src/background.ts`:
|
|
48
51
|
|
|
49
52
|
```ts
|
|
50
|
-
const CROSS_TAB_FOLLOW =
|
|
53
|
+
const CROSS_TAB_FOLLOW = true;
|
|
51
54
|
```
|
|
52
55
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
inject into arbitrary tabs.
|
|
57
|
-
|
|
58
|
-
1. **Static (simplest, but in-depth review):** re-add to `public/manifest.json`:
|
|
59
|
-
- `"<all_urls>"` in `host_permissions`, and
|
|
60
|
-
- the `content_scripts` block:
|
|
61
|
-
```json
|
|
62
|
-
"content_scripts": [
|
|
63
|
-
{
|
|
64
|
-
"matches": ["<all_urls>"],
|
|
65
|
-
"js": ["assets/content-script.js"],
|
|
66
|
-
"run_at": "document_idle",
|
|
67
|
-
"all_frames": false
|
|
68
|
-
}
|
|
69
|
-
],
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
2. **Optional (preferred — keeps the default install lean):** declare
|
|
73
|
-
`"optional_host_permissions": ["<all_urls>"]` in the manifest and call
|
|
74
|
-
`chrome.permissions.request({ origins: ["<all_urls>"] })` at runtime (e.g. from
|
|
75
|
-
a settings toggle) before enabling follow. The user grants the broad access
|
|
76
|
-
explicitly, only if they want cross-tab overlays.
|
|
56
|
+
That keeps the all-tabs broadcast in `broadcastMount()` / `broadcastUnmount()`
|
|
57
|
+
and the `chrome.tabs.onActivated` follow listener live. The manifest must keep
|
|
58
|
+
the broad host permission and the `<all_urls>` declarative content script in
|
|
59
|
+
place so the worker can inject into arbitrary tabs.
|
|
77
60
|
|
|
78
61
|
After any change: `pnpm build`, then re-zip `dist/`.
|
|
@@ -56,7 +56,7 @@ Upload the generated artifact (the version matches `public/manifest.json`; the
|
|
|
56
56
|
Chrome Web Store requires each upload to use a higher version than the last):
|
|
57
57
|
|
|
58
58
|
```txt
|
|
59
|
-
templates/clips/chrome-extension/releases/clips-chrome-extension-0.1.
|
|
59
|
+
templates/clips/chrome-extension/releases/clips-chrome-extension-0.1.19.zip
|
|
60
60
|
```
|
|
61
61
|
|
|
62
62
|
## Web App Rollout Gate
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"manifest_version": 3,
|
|
3
3
|
"name": "Agent-Native Clips",
|
|
4
4
|
"short_name": "Clips",
|
|
5
|
-
"version": "0.1.
|
|
5
|
+
"version": "0.1.19",
|
|
6
6
|
"description": "Start Clips recordings from Chrome, capture optional diagnostics, and preview Clips links on GitHub and Jira.",
|
|
7
7
|
"minimum_chrome_version": "116",
|
|
8
8
|
"action": {
|
|
@@ -31,7 +31,8 @@
|
|
|
31
31
|
"https://clips.agent-native.com/*",
|
|
32
32
|
"https://forms.agent-native.com/*",
|
|
33
33
|
"http://localhost/*",
|
|
34
|
-
"http://127.0.0.1/*"
|
|
34
|
+
"http://127.0.0.1/*",
|
|
35
|
+
"<all_urls>"
|
|
35
36
|
],
|
|
36
37
|
"content_scripts": [
|
|
37
38
|
{
|
|
@@ -43,6 +44,12 @@
|
|
|
43
44
|
"js": ["assets/github-preview-content.js"],
|
|
44
45
|
"run_at": "document_idle",
|
|
45
46
|
"all_frames": false
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"matches": ["<all_urls>"],
|
|
50
|
+
"js": ["assets/content-script.js"],
|
|
51
|
+
"run_at": "document_idle",
|
|
52
|
+
"all_frames": false
|
|
46
53
|
}
|
|
47
54
|
],
|
|
48
55
|
"web_accessible_resources": [
|
|
@@ -6,6 +6,10 @@ import {
|
|
|
6
6
|
shouldReconcilePersistedRecording,
|
|
7
7
|
type OffscreenRecordingState,
|
|
8
8
|
} from "./native-recording-state";
|
|
9
|
+
import {
|
|
10
|
+
sendWithInjectionFallback,
|
|
11
|
+
shouldFollowOverlay,
|
|
12
|
+
} from "./overlay-follow";
|
|
9
13
|
import { captureExtensionError, initExtensionSentry } from "./sentry";
|
|
10
14
|
|
|
11
15
|
initExtensionSentry("background");
|
|
@@ -276,16 +280,11 @@ let overlayBaseEpochMs = 0;
|
|
|
276
280
|
// up, for both camera-only AND screen+camera recording (recordingShowsBubble).
|
|
277
281
|
let overlayShowsBubble = false;
|
|
278
282
|
let recordingShowsBubble = false;
|
|
279
|
-
// Cross-tab follow
|
|
280
|
-
//
|
|
281
|
-
//
|
|
282
|
-
|
|
283
|
-
//
|
|
284
|
-
// getDisplayMedia regardless. See PERMISSIONS.md to re-enable.
|
|
285
|
-
// Typed `boolean` (not inferred literal `false`) so the cross-tab branches below
|
|
286
|
-
// don't read as unreachable code; flip the value to re-enable. See PERMISSIONS.md.
|
|
287
|
-
const CROSS_TAB_FOLLOW: boolean = false;
|
|
288
|
-
// The tab the recording was launched from — the only tab activeTab lets us touch.
|
|
283
|
+
// Cross-tab follow stays enabled for the next release. It needs the manifest's
|
|
284
|
+
// broad-host review path (`<all_urls>` + declarative content script coverage) so
|
|
285
|
+
// the face bubble and controls keep following the user across tabs and reloads.
|
|
286
|
+
const CROSS_TAB_FOLLOW: boolean = true;
|
|
287
|
+
// The launch tab stays the anchor for the explicit reload recovery path.
|
|
289
288
|
let overlayTabId: number | null = null;
|
|
290
289
|
let countdownEndsAtMs = 0;
|
|
291
290
|
let armingNativeRecordingSessionId: string | null = null;
|
|
@@ -438,20 +437,19 @@ async function restoreRuntimeState(): Promise<void> {
|
|
|
438
437
|
function sendTabMessage(
|
|
439
438
|
tabId: number,
|
|
440
439
|
message: Record<string, unknown>,
|
|
441
|
-
): Promise<
|
|
440
|
+
): Promise<boolean> {
|
|
442
441
|
return new Promise((resolve) => {
|
|
443
442
|
try {
|
|
444
443
|
chrome.tabs.sendMessage(tabId, message, () => {
|
|
445
|
-
|
|
446
|
-
resolve();
|
|
444
|
+
resolve(!chrome.runtime.lastError);
|
|
447
445
|
});
|
|
448
446
|
} catch {
|
|
449
|
-
resolve();
|
|
447
|
+
resolve(false);
|
|
450
448
|
}
|
|
451
449
|
});
|
|
452
450
|
}
|
|
453
451
|
|
|
454
|
-
async function
|
|
452
|
+
async function injectContentScript(tabId: number): Promise<boolean> {
|
|
455
453
|
const scripting = (
|
|
456
454
|
chrome as typeof chrome & {
|
|
457
455
|
scripting?: {
|
|
@@ -462,18 +460,32 @@ async function ensureContentScript(tabId: number): Promise<void> {
|
|
|
462
460
|
};
|
|
463
461
|
}
|
|
464
462
|
).scripting;
|
|
465
|
-
if (!scripting) return;
|
|
466
|
-
|
|
467
|
-
.executeScript({
|
|
468
|
-
|
|
463
|
+
if (!scripting) return false;
|
|
464
|
+
try {
|
|
465
|
+
await scripting.executeScript({
|
|
466
|
+
target: { tabId },
|
|
467
|
+
files: ["assets/content-script.js"],
|
|
468
|
+
});
|
|
469
|
+
return true;
|
|
470
|
+
} catch {
|
|
471
|
+
// Unsupported pages (chrome://, the Chrome Web Store, and similar) reject
|
|
472
|
+
// injection. They still need to record without an in-page overlay.
|
|
473
|
+
return false;
|
|
474
|
+
}
|
|
469
475
|
}
|
|
470
476
|
|
|
471
|
-
async function mountOverlayOnTab(
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
+
async function mountOverlayOnTab(
|
|
478
|
+
tabId: number,
|
|
479
|
+
parts: OverlayPart[] = desiredParts(),
|
|
480
|
+
): Promise<boolean> {
|
|
481
|
+
return sendWithInjectionFallback(
|
|
482
|
+
() =>
|
|
483
|
+
sendTabMessage(tabId, {
|
|
484
|
+
type: "CLIPS_OVERLAY_MOUNT",
|
|
485
|
+
parts,
|
|
486
|
+
}),
|
|
487
|
+
() => injectContentScript(tabId),
|
|
488
|
+
);
|
|
477
489
|
}
|
|
478
490
|
|
|
479
491
|
function allTabs(): Promise<chrome.tabs.Tab[]> {
|
|
@@ -483,9 +495,8 @@ function allTabs(): Promise<chrome.tabs.Tab[]> {
|
|
|
483
495
|
}
|
|
484
496
|
|
|
485
497
|
async function broadcastMount(): Promise<void> {
|
|
486
|
-
//
|
|
487
|
-
//
|
|
488
|
-
// gated behind CROSS_TAB_FOLLOW because it needs broad host access.
|
|
498
|
+
// Cross-tab follow uses the declarative content script path, so every mounted
|
|
499
|
+
// tab can receive the current overlay parts and survive reloads.
|
|
489
500
|
if (!CROSS_TAB_FOLLOW) {
|
|
490
501
|
if (overlayTabId !== null) await mountOverlayOnTab(overlayTabId);
|
|
491
502
|
return;
|
|
@@ -548,12 +559,9 @@ async function startPreview(wantsCamera: boolean): Promise<void> {
|
|
|
548
559
|
if (!tab || typeof tab.id !== "number") return;
|
|
549
560
|
if (previewTabId !== null && previewTabId !== tab.id) await stopPreview();
|
|
550
561
|
previewTabId = tab.id;
|
|
551
|
-
|
|
552
|
-
//
|
|
553
|
-
await
|
|
554
|
-
type: "CLIPS_OVERLAY_MOUNT",
|
|
555
|
-
parts: ["bubble"],
|
|
556
|
-
});
|
|
562
|
+
// Message delivery reuses the declarative script; fallback injection fails
|
|
563
|
+
// silently on unsupported pages (chrome://, the Web Store, and similar).
|
|
564
|
+
await mountOverlayOnTab(tab.id, ["bubble"]);
|
|
557
565
|
}
|
|
558
566
|
|
|
559
567
|
async function stopPreview(): Promise<void> {
|
|
@@ -2560,29 +2568,40 @@ async function dispatchRuntimeMessage(message: unknown): Promise<unknown> {
|
|
|
2560
2568
|
}
|
|
2561
2569
|
}
|
|
2562
2570
|
|
|
2563
|
-
// While a recording is active, keep the overlay following the user as
|
|
2564
|
-
// switch tabs
|
|
2565
|
-
//
|
|
2571
|
+
// While a recording is active or arming, keep the overlay following the user as
|
|
2572
|
+
// they switch tabs. The message-first path reuses declarative scripts and only
|
|
2573
|
+
// injects for tabs that predate the extension or otherwise have no receiver.
|
|
2566
2574
|
chrome.tabs.onActivated.addListener((info) => {
|
|
2567
|
-
if (!CROSS_TAB_FOLLOW) return;
|
|
2575
|
+
if (!CROSS_TAB_FOLLOW) return;
|
|
2568
2576
|
void (async () => {
|
|
2569
2577
|
await ensureRestored();
|
|
2570
|
-
if (
|
|
2578
|
+
if (
|
|
2579
|
+
!shouldFollowOverlay(
|
|
2580
|
+
overlayPhase,
|
|
2581
|
+
Boolean(activeNativeRecording),
|
|
2582
|
+
Boolean(armingNativeRecordingSessionId),
|
|
2583
|
+
)
|
|
2584
|
+
)
|
|
2585
|
+
return;
|
|
2571
2586
|
await mountOverlayOnTab(info.tabId);
|
|
2572
2587
|
})();
|
|
2573
2588
|
});
|
|
2574
2589
|
|
|
2575
|
-
//
|
|
2576
|
-
//
|
|
2577
|
-
//
|
|
2578
|
-
// Re-inject and re-mount once the launch tab finishes reloading while a recording
|
|
2579
|
-
// is active. Scoped to the launch tab (the only tab activeTab lets us touch); a
|
|
2580
|
-
// same-URL reload keeps that grant.
|
|
2590
|
+
// Keep the launch tab in sync after a reload. Declarative follow covers the
|
|
2591
|
+
// other tabs; this is the explicit recovery path for the tab that originally
|
|
2592
|
+
// started the recording.
|
|
2581
2593
|
chrome.tabs.onUpdated.addListener((tabId, changeInfo) => {
|
|
2582
2594
|
if (changeInfo.status !== "complete") return;
|
|
2583
2595
|
void (async () => {
|
|
2584
2596
|
await ensureRestored();
|
|
2585
|
-
if (
|
|
2597
|
+
if (
|
|
2598
|
+
!shouldFollowOverlay(
|
|
2599
|
+
overlayPhase,
|
|
2600
|
+
Boolean(activeNativeRecording),
|
|
2601
|
+
Boolean(armingNativeRecordingSessionId),
|
|
2602
|
+
)
|
|
2603
|
+
)
|
|
2604
|
+
return;
|
|
2586
2605
|
if (tabId !== overlayTabId) return;
|
|
2587
2606
|
await mountOverlayOnTab(tabId);
|
|
2588
2607
|
broadcastOverlayState();
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
// Loom-style in-page overlay host. The
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
// emits a single self-contained classic script with no module imports/exports
|
|
9
|
-
// and leaks no names into the shared global scope. Its only job is to
|
|
1
|
+
// Loom-style in-page overlay host. The next release declares this on every page
|
|
2
|
+
// so the face bubble and controls can survive tab switches and navigations; the
|
|
3
|
+
// background worker can also inject it into the launch tab for immediate and
|
|
4
|
+
// reload recovery. That broad "<all_urls>" host access is the Chrome Web Store
|
|
5
|
+
// in-depth review boundary. Wrapped in an IIFE so it emits a single self-contained
|
|
6
|
+
// classic script with no module imports/exports and leaks no names into the shared
|
|
7
|
+
// global scope. Its only job is to
|
|
10
8
|
// mount/unmount the overlay iframes; all UI and control logic lives inside the
|
|
11
9
|
// extension-origin overlay pages (src/overlay.html). The worker is the source of
|
|
12
10
|
// truth for which "parts" are visible and pushes them here.
|
|
@@ -18,6 +16,12 @@
|
|
|
18
16
|
const ALL_PARTS: OverlayPart[] = ["bubble", "countdown", "toolbar", "saving"];
|
|
19
17
|
const flags = window as unknown as { __clipsOverlayHostReady?: boolean };
|
|
20
18
|
|
|
19
|
+
// Declarative and recovery injection can target the same document. Return
|
|
20
|
+
// before any side effects so global listeners remain one-per-page.
|
|
21
|
+
if (flags.__clipsOverlayHostReady) return;
|
|
22
|
+
flags.__clipsOverlayHostReady = true;
|
|
23
|
+
let recordingActive = false;
|
|
24
|
+
|
|
21
25
|
function errorPayload(error: unknown): {
|
|
22
26
|
name: string;
|
|
23
27
|
message: string;
|
|
@@ -40,6 +44,7 @@
|
|
|
40
44
|
error: unknown,
|
|
41
45
|
context: Record<string, unknown> = {},
|
|
42
46
|
): void {
|
|
47
|
+
if (!recordingActive) return;
|
|
43
48
|
try {
|
|
44
49
|
chrome.runtime.sendMessage(
|
|
45
50
|
{
|
|
@@ -73,6 +78,15 @@
|
|
|
73
78
|
});
|
|
74
79
|
});
|
|
75
80
|
|
|
81
|
+
try {
|
|
82
|
+
chrome.storage.onChanged.addListener((changes, areaName) => {
|
|
83
|
+
if (areaName !== "local" || !changes.clipsRecordingActive) return;
|
|
84
|
+
recordingActive = changes.clipsRecordingActive.newValue === true;
|
|
85
|
+
});
|
|
86
|
+
} catch {
|
|
87
|
+
/* storage unavailable */
|
|
88
|
+
}
|
|
89
|
+
|
|
76
90
|
// ----- Draggable, resizable camera bubble ---------------------------------
|
|
77
91
|
// Size + position persist in storage so the bubble stays where the user put it
|
|
78
92
|
// across pages and recordings (like the desktop app). The iframe can't move or
|
|
@@ -339,7 +353,8 @@
|
|
|
339
353
|
try {
|
|
340
354
|
chrome.storage.local.get("clipsRecordingActive", (value) => {
|
|
341
355
|
if (chrome.runtime.lastError) return;
|
|
342
|
-
|
|
356
|
+
recordingActive = value?.clipsRecordingActive === true;
|
|
357
|
+
if (recordingActive) requestState();
|
|
343
358
|
});
|
|
344
359
|
} catch {
|
|
345
360
|
/* ignore */
|
|
@@ -593,13 +608,6 @@
|
|
|
593
608
|
lastWantedParts = wanted;
|
|
594
609
|
}
|
|
595
610
|
|
|
596
|
-
// Guard against rare double-injection (SPA soft-reloads re-running the script).
|
|
597
|
-
if (flags.__clipsOverlayHostReady) {
|
|
598
|
-
syncIfRecording();
|
|
599
|
-
return;
|
|
600
|
-
}
|
|
601
|
-
flags.__clipsOverlayHostReady = true;
|
|
602
|
-
|
|
603
611
|
chrome.runtime.onMessage.addListener((message) => {
|
|
604
612
|
if (!message || typeof message !== "object") return;
|
|
605
613
|
const type = (message as { type?: unknown }).type;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export type OverlayFollowPhase =
|
|
2
|
+
| "idle"
|
|
3
|
+
| "countdown"
|
|
4
|
+
| "recording"
|
|
5
|
+
| "paused"
|
|
6
|
+
| "saving";
|
|
7
|
+
|
|
8
|
+
export function shouldFollowOverlay(
|
|
9
|
+
phase: OverlayFollowPhase,
|
|
10
|
+
hasActiveNativeRecording: boolean,
|
|
11
|
+
hasArmingNativeRecording: boolean,
|
|
12
|
+
): boolean {
|
|
13
|
+
return (
|
|
14
|
+
phase !== "idle" && (hasActiveNativeRecording || hasArmingNativeRecording)
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function sendWithInjectionFallback(
|
|
19
|
+
sendMessage: () => Promise<boolean>,
|
|
20
|
+
injectContentScript: () => Promise<boolean>,
|
|
21
|
+
): Promise<boolean> {
|
|
22
|
+
if (await sendMessage()) return true;
|
|
23
|
+
if (!(await injectContentScript())) return false;
|
|
24
|
+
return sendMessage();
|
|
25
|
+
}
|
|
@@ -19,9 +19,11 @@
|
|
|
19
19
|
"@agent-native/toolkit": "workspace:*",
|
|
20
20
|
"@fontsource-variable/inter": "^5.2.8",
|
|
21
21
|
"@libsql/client": "^0.15.8",
|
|
22
|
+
"@openrouter/ai-sdk-provider": "^2.9.1",
|
|
22
23
|
"@radix-ui/react-tooltip": "^1.2.7",
|
|
23
24
|
"@tabler/icons-react": "catalog:",
|
|
24
25
|
"@tanstack/react-query": "^5.99.2",
|
|
26
|
+
"ai": "6.0.258",
|
|
25
27
|
"h3": "catalog:",
|
|
26
28
|
"isbot": "^5",
|
|
27
29
|
"next-themes": "^0.4.6",
|
|
@@ -12,6 +12,8 @@ NPM_CONFIG_PRODUCTION = "false"
|
|
|
12
12
|
|
|
13
13
|
[context.production.environment]
|
|
14
14
|
AGENT_NATIVE_HOSTED_HARNESS = "true"
|
|
15
|
+
AGENT_NATIVE_ENABLE_KEEP_WARM = "1"
|
|
16
|
+
AGENT_NATIVE_DISABLE_KEEP_WARM_BACKGROUND = "1"
|
|
15
17
|
# Macros owns its schema at release time (`migrate:production` above), so
|
|
16
18
|
# production request functions must not probe the database for migrations.
|
|
17
19
|
# Preview functions still run request-path migrations because previews do not
|
|
@@ -213,10 +213,15 @@ export function RunErrorRecoveryCard({ info, onContinue, onRetry, onFork, onDism
|
|
|
213
213
|
const shouldShowMissingProviderSetup = isMissingLlmProviderRunError(info);
|
|
214
214
|
const isProviderAuthError = isProviderAuthenticationError([info.message, info.details].filter(Boolean).join("\n"), info.errorCode);
|
|
215
215
|
// Blocked on something the reader goes and fixes elsewhere, then comes back
|
|
216
|
-
// to.
|
|
217
|
-
//
|
|
216
|
+
// to. Recoverable runs and email verification keep a retry path; rejected
|
|
217
|
+
// provider credentials use the setup flow below so the same bad key is not
|
|
218
|
+
// replayed.
|
|
218
219
|
const isUnblockableExternally = info.errorCode === "email_verification_required";
|
|
219
|
-
|
|
220
|
+
// Rejected provider keys already have a recovery path: update/connect the
|
|
221
|
+
// credential, then let the setup callback re-run the turn. Exposing a
|
|
222
|
+
// separate retry button here just replays the same rejected credential and
|
|
223
|
+
// turns a permanent auth failure into a loop.
|
|
224
|
+
const canRetry = canRecover || isUnblockableExternally;
|
|
220
225
|
const builderReconnectResolved = shouldShowBuilderReconnect &&
|
|
221
226
|
builderReconnect.hasFetchedStatus &&
|
|
222
227
|
builderReconnect.configured;
|
|
@@ -239,6 +239,15 @@ function compareMcpUrl(value) {
|
|
|
239
239
|
return value.trim().replace(/\/+$/, "");
|
|
240
240
|
}
|
|
241
241
|
}
|
|
242
|
+
function startMcpOAuthReconnect(server) {
|
|
243
|
+
const returnUrl = `${window.location.pathname}${window.location.search}${window.location.hash}`;
|
|
244
|
+
const params = new URLSearchParams({
|
|
245
|
+
serverId: server.id,
|
|
246
|
+
scope: server.scope,
|
|
247
|
+
return: returnUrl,
|
|
248
|
+
});
|
|
249
|
+
window.location.assign(agentNativePath(`/_agent-native/mcp/servers/oauth/start?${params}`));
|
|
250
|
+
}
|
|
242
251
|
function McpServerStatus({ server, onReconnect, reconnecting = false, reconnectError, }) {
|
|
243
252
|
const t = useT();
|
|
244
253
|
if (server.status.state === "connected") {
|
|
@@ -362,7 +371,9 @@ function McpIntegrationsSection({ query }) {
|
|
|
362
371
|
return (_jsxs("div", { className: "flex items-start gap-3 py-3.5", children: [_jsx("span", { className: "mt-0.5 flex size-9 shrink-0 items-center justify-center rounded-lg border border-border/70 bg-background text-muted-foreground", children: _jsx(IconServer, { className: "size-4" }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("span", { className: "truncate text-sm font-medium text-foreground", children: server.name }), _jsx("span", { className: "text-[11px] text-muted-foreground", children: server.scope === "user"
|
|
363
372
|
? t("mcpIntegrations.personal")
|
|
364
373
|
: t("mcpIntegrations.sharedWithWorkspace") })] }), _jsx(McpServerStatus, { server: server, onReconnect: server.status.state === "error"
|
|
365
|
-
? () =>
|
|
374
|
+
? () => server.authMode === "oauth"
|
|
375
|
+
? startMcpOAuthReconnect(server)
|
|
376
|
+
: void reconnect(server)
|
|
366
377
|
: undefined, reconnecting: reconnectingKey === key, reconnectError: reconnectError?.key === key
|
|
367
378
|
? reconnectError.message
|
|
368
379
|
: undefined })] }), canRemove && (_jsx("button", { type: "button", onClick: () => void removeServer(server), disabled: deleteServer.isPending, className: "shrink-0 rounded-md border border-border px-2.5 py-1.5 text-xs font-medium text-foreground transition-colors hover:bg-accent disabled:opacity-50", children: deleteTarget === key ? "Confirm" : "Remove" }))] }, key));
|
package/dist/collab/routes.d.ts
CHANGED
|
@@ -26,8 +26,8 @@ export declare const getCollabState: import("h3").EventHandlerWithFetch<import("
|
|
|
26
26
|
* Body: { update: string (base64), requestSource?: string }
|
|
27
27
|
*/
|
|
28
28
|
export declare const postCollabUpdate: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
29
|
-
ok?: undefined;
|
|
30
29
|
error: string;
|
|
30
|
+
ok?: undefined;
|
|
31
31
|
} | {
|
|
32
32
|
error?: undefined;
|
|
33
33
|
ok: boolean;
|
|
@@ -137,13 +137,13 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
137
137
|
inputSchema: {
|
|
138
138
|
type: string;
|
|
139
139
|
properties: {
|
|
140
|
-
count?: undefined;
|
|
141
140
|
query?: undefined;
|
|
142
141
|
minutes?: undefined;
|
|
143
142
|
limit?: undefined;
|
|
144
143
|
clientHint?: undefined;
|
|
145
144
|
timestamp?: undefined;
|
|
146
145
|
chapterId?: undefined;
|
|
146
|
+
count?: undefined;
|
|
147
147
|
startAt?: undefined;
|
|
148
148
|
endAt?: undefined;
|
|
149
149
|
reason?: undefined;
|
|
@@ -159,7 +159,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
159
159
|
inputSchema: {
|
|
160
160
|
type: string;
|
|
161
161
|
properties: {
|
|
162
|
-
count?: undefined;
|
|
163
162
|
query: {
|
|
164
163
|
type: string;
|
|
165
164
|
description: string;
|
|
@@ -175,6 +174,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
175
174
|
clientHint?: undefined;
|
|
176
175
|
timestamp?: undefined;
|
|
177
176
|
chapterId?: undefined;
|
|
177
|
+
count?: undefined;
|
|
178
178
|
startAt?: undefined;
|
|
179
179
|
endAt?: undefined;
|
|
180
180
|
reason?: undefined;
|
|
@@ -190,7 +190,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
190
190
|
inputSchema: {
|
|
191
191
|
type: string;
|
|
192
192
|
properties: {
|
|
193
|
-
count?: undefined;
|
|
194
193
|
minutes: {
|
|
195
194
|
type: string;
|
|
196
195
|
description: string;
|
|
@@ -200,6 +199,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
200
199
|
clientHint?: undefined;
|
|
201
200
|
timestamp?: undefined;
|
|
202
201
|
chapterId?: undefined;
|
|
202
|
+
count?: undefined;
|
|
203
203
|
startAt?: undefined;
|
|
204
204
|
endAt?: undefined;
|
|
205
205
|
reason?: undefined;
|
|
@@ -216,7 +216,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
216
216
|
type: string;
|
|
217
217
|
required: string[];
|
|
218
218
|
properties: {
|
|
219
|
-
count?: undefined;
|
|
220
219
|
query: {
|
|
221
220
|
type: string;
|
|
222
221
|
description: string;
|
|
@@ -235,6 +234,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
235
234
|
};
|
|
236
235
|
timestamp?: undefined;
|
|
237
236
|
chapterId?: undefined;
|
|
237
|
+
count?: undefined;
|
|
238
238
|
startAt?: undefined;
|
|
239
239
|
endAt?: undefined;
|
|
240
240
|
reason?: undefined;
|
|
@@ -250,7 +250,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
250
250
|
type: string;
|
|
251
251
|
required: string[];
|
|
252
252
|
properties: {
|
|
253
|
-
count?: undefined;
|
|
254
253
|
query?: undefined;
|
|
255
254
|
minutes?: undefined;
|
|
256
255
|
limit?: undefined;
|
|
@@ -264,6 +263,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
264
263
|
description: string;
|
|
265
264
|
};
|
|
266
265
|
chapterId?: undefined;
|
|
266
|
+
count?: undefined;
|
|
267
267
|
startAt?: undefined;
|
|
268
268
|
endAt?: undefined;
|
|
269
269
|
includeMicrophone?: undefined;
|
|
@@ -314,13 +314,13 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
314
314
|
type: string;
|
|
315
315
|
required: string[];
|
|
316
316
|
properties: {
|
|
317
|
-
count?: undefined;
|
|
318
317
|
query?: undefined;
|
|
319
318
|
minutes?: undefined;
|
|
320
319
|
limit?: undefined;
|
|
321
320
|
clientHint?: undefined;
|
|
322
321
|
timestamp?: undefined;
|
|
323
322
|
chapterId?: undefined;
|
|
323
|
+
count?: undefined;
|
|
324
324
|
startAt: {
|
|
325
325
|
type: string;
|
|
326
326
|
description: string;
|
|
@@ -349,13 +349,13 @@ export declare function screenMemoryMcpToolDefinitions(): ({
|
|
|
349
349
|
type: string;
|
|
350
350
|
required: string[];
|
|
351
351
|
properties: {
|
|
352
|
-
count?: undefined;
|
|
353
352
|
query?: undefined;
|
|
354
353
|
minutes?: undefined;
|
|
355
354
|
limit?: undefined;
|
|
356
355
|
clientHint?: undefined;
|
|
357
356
|
timestamp?: undefined;
|
|
358
357
|
chapterId?: undefined;
|
|
358
|
+
count?: undefined;
|
|
359
359
|
startAt?: undefined;
|
|
360
360
|
endAt?: undefined;
|
|
361
361
|
reason?: undefined;
|
|
@@ -8,7 +8,7 @@ import { getH3App } from "../server/framework-request-handler.js";
|
|
|
8
8
|
import { getAppUrl, resolveOAuthRedirectUri } from "../server/google-oauth.js";
|
|
9
9
|
import { runWithRequestContext } from "../server/request-context.js";
|
|
10
10
|
import { finishMcpOAuthAuthorization, startMcpOAuthAuthorization, validateMcpOAuthCallbackIssuer, } from "./oauth-client.js";
|
|
11
|
-
import { addOAuthRemoteServer, normalizeServerName, validateRemoteUrl, } from "./remote-store.js";
|
|
11
|
+
import { addOAuthRemoteServer, listRemoteServers, normalizeServerName, replaceOAuthRemoteServer, validateRemoteUrl, } from "./remote-store.js";
|
|
12
12
|
const FLOW_COOKIE = "an_mcp_oauth_flow";
|
|
13
13
|
const FLOW_TTL_SECONDS = 10 * 60;
|
|
14
14
|
const FLOW_COOKIE_CHUNK_SIZE = 2_800;
|
|
@@ -69,8 +69,33 @@ async function handleMcpOAuthStart(event) {
|
|
|
69
69
|
if (!session?.email)
|
|
70
70
|
return unauthorized(event);
|
|
71
71
|
const query = getQuery(event);
|
|
72
|
-
const
|
|
73
|
-
const
|
|
72
|
+
const reconnectServerId = text(query.serverId);
|
|
73
|
+
const reconnectScope = query.scope === "org" ? "org" : "user";
|
|
74
|
+
const reconnectOrg = reconnectScope === "org" ? await getOrgContext(event) : null;
|
|
75
|
+
const reconnectScopeId = reconnectScope === "user" ? session.email : (reconnectOrg?.orgId ?? "");
|
|
76
|
+
let reconnectServer;
|
|
77
|
+
if (reconnectServerId) {
|
|
78
|
+
if (reconnectScope === "org" &&
|
|
79
|
+
(!reconnectScopeId || !isOrgAdmin(reconnectOrg?.role))) {
|
|
80
|
+
setResponseStatus(event, reconnectScopeId ? 403 : 400);
|
|
81
|
+
return {
|
|
82
|
+
error: reconnectScopeId
|
|
83
|
+
? "Only organization owners and admins can reconnect an org MCP server."
|
|
84
|
+
: "Join an organization before reconnecting an org MCP server.",
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
reconnectServer = (await listRemoteServers(reconnectScope, reconnectScopeId)).find((server) => server.id === reconnectServerId);
|
|
88
|
+
if (!reconnectServer) {
|
|
89
|
+
setResponseStatus(event, 404);
|
|
90
|
+
return { error: "MCP server was not found." };
|
|
91
|
+
}
|
|
92
|
+
if (!reconnectServer.oauthSecretKey) {
|
|
93
|
+
setResponseStatus(event, 400);
|
|
94
|
+
return { error: "This MCP server does not use OAuth credentials." };
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const rawUrl = reconnectServer?.url ?? text(query.url);
|
|
98
|
+
const rawName = reconnectServer?.name ?? text(query.name);
|
|
74
99
|
const returnUrl = text(query.return);
|
|
75
100
|
if (!rawUrl || !rawName) {
|
|
76
101
|
setResponseStatus(event, 400);
|
|
@@ -159,6 +184,7 @@ async function handleMcpOAuthStart(event) {
|
|
|
159
184
|
? { discoveryState: started.discoveryState }
|
|
160
185
|
: {}),
|
|
161
186
|
...(safeReturnUrl ? { returnUrl: safeReturnUrl } : {}),
|
|
187
|
+
...(reconnectServerId ? { replaceServerId: reconnectServerId } : {}),
|
|
162
188
|
expiresAt: Date.now() + FLOW_TTL_SECONDS * 1_000,
|
|
163
189
|
};
|
|
164
190
|
setMcpOAuthFlowCookie(event, flow, redirectUri.startsWith("https://"));
|
|
@@ -245,12 +271,14 @@ async function handleMcpOAuthCallback(event, options) {
|
|
|
245
271
|
authorizationCode: code,
|
|
246
272
|
iss,
|
|
247
273
|
}));
|
|
248
|
-
const result =
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
274
|
+
const result = flow.replaceServerId
|
|
275
|
+
? await replaceOAuthRemoteServer(flow.scope, flow.scopeId, flow.replaceServerId, finished.credentials)
|
|
276
|
+
: await addOAuthRemoteServer(flow.scope, flow.scopeId, {
|
|
277
|
+
name: flow.name,
|
|
278
|
+
url: flow.url,
|
|
279
|
+
description: flow.description,
|
|
280
|
+
credentials: finished.credentials,
|
|
281
|
+
});
|
|
254
282
|
if (!result.ok) {
|
|
255
283
|
setResponseStatus(event, 400);
|
|
256
284
|
return { error: result.error };
|
|
@@ -107,6 +107,18 @@ export declare function addOAuthRemoteServer(scope: RemoteMcpScope, scopeId: str
|
|
|
107
107
|
ok: false;
|
|
108
108
|
error: string;
|
|
109
109
|
}>;
|
|
110
|
+
/**
|
|
111
|
+
* Replace the OAuth grant for an existing server without changing its id or
|
|
112
|
+
* display name. Reconnect must update the saved grant in place; registering a
|
|
113
|
+
* second row makes the original connection impossible to repair.
|
|
114
|
+
*/
|
|
115
|
+
export declare function replaceOAuthRemoteServer(scope: RemoteMcpScope, scopeId: string, serverId: string, credentials: McpOAuthCredentialBundle): Promise<{
|
|
116
|
+
ok: true;
|
|
117
|
+
server: StoredRemoteMcpServer;
|
|
118
|
+
} | {
|
|
119
|
+
ok: false;
|
|
120
|
+
error: string;
|
|
121
|
+
}>;
|
|
110
122
|
export declare function addFirstPartyRemoteServer(orgId: string, input: {
|
|
111
123
|
appId: string;
|
|
112
124
|
name: string;
|
|
@@ -193,6 +193,77 @@ export async function addOAuthRemoteServer(scope, scopeId, input) {
|
|
|
193
193
|
};
|
|
194
194
|
}
|
|
195
195
|
}
|
|
196
|
+
/**
|
|
197
|
+
* Replace the OAuth grant for an existing server without changing its id or
|
|
198
|
+
* display name. Reconnect must update the saved grant in place; registering a
|
|
199
|
+
* second row makes the original connection impossible to repair.
|
|
200
|
+
*/
|
|
201
|
+
export async function replaceOAuthRemoteServer(scope, scopeId, serverId, credentials) {
|
|
202
|
+
const credentialResource = validateRemoteUrl(credentials.serverUrl);
|
|
203
|
+
if (!credentialResource.ok || !credentialResource.url) {
|
|
204
|
+
return {
|
|
205
|
+
ok: false,
|
|
206
|
+
error: "MCP server URL must match the OAuth credential resource URL",
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
const existing = await readList(scope, scopeId);
|
|
210
|
+
const index = existing.findIndex((server) => server.id === serverId);
|
|
211
|
+
const current = index >= 0 ? existing[index] : undefined;
|
|
212
|
+
if (!current)
|
|
213
|
+
return { ok: false, error: "MCP server was not found" };
|
|
214
|
+
if (!current.oauthSecretKey) {
|
|
215
|
+
return {
|
|
216
|
+
ok: false,
|
|
217
|
+
error: "This MCP server does not use OAuth credentials",
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
if (current.url !== credentialResource.url.toString()) {
|
|
221
|
+
return {
|
|
222
|
+
ok: false,
|
|
223
|
+
error: "MCP server URL must match the saved OAuth connection",
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
const nextSecretKey = `mcp_oauth:${shortId()}`;
|
|
227
|
+
const nextCredentials = {
|
|
228
|
+
...credentials,
|
|
229
|
+
serverUrl: credentialResource.url.toString(),
|
|
230
|
+
};
|
|
231
|
+
try {
|
|
232
|
+
await saveMcpOAuthCredentials({
|
|
233
|
+
key: nextSecretKey,
|
|
234
|
+
scope,
|
|
235
|
+
scopeId,
|
|
236
|
+
credentials: nextCredentials,
|
|
237
|
+
});
|
|
238
|
+
const server = {
|
|
239
|
+
...current,
|
|
240
|
+
url: nextCredentials.serverUrl,
|
|
241
|
+
oauthSecretKey: nextSecretKey,
|
|
242
|
+
};
|
|
243
|
+
const next = [...existing];
|
|
244
|
+
next[index] = server;
|
|
245
|
+
await writeList(scope, scopeId, next);
|
|
246
|
+
await revokeMcpOAuthCredentials({
|
|
247
|
+
key: current.oauthSecretKey,
|
|
248
|
+
scope,
|
|
249
|
+
scopeId,
|
|
250
|
+
serverUrl: current.url,
|
|
251
|
+
}).catch(() => undefined);
|
|
252
|
+
return { ok: true, server };
|
|
253
|
+
}
|
|
254
|
+
catch (err) {
|
|
255
|
+
await revokeMcpOAuthCredentials({
|
|
256
|
+
key: nextSecretKey,
|
|
257
|
+
scope,
|
|
258
|
+
scopeId,
|
|
259
|
+
serverUrl: nextCredentials.serverUrl,
|
|
260
|
+
}).catch(() => undefined);
|
|
261
|
+
return {
|
|
262
|
+
ok: false,
|
|
263
|
+
error: `Failed to replace MCP OAuth credentials: ${err?.message ?? err}`,
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
}
|
|
196
267
|
export async function addFirstPartyRemoteServer(orgId, input) {
|
|
197
268
|
const trust = await isFirstPartyRemoteEndpointTrusted(orgId, input.appId, input.url);
|
|
198
269
|
if (!trust.ok)
|
|
@@ -16,18 +16,18 @@ export declare function createNotificationsHandler(): import("h3").EventHandlerW
|
|
|
16
16
|
ok?: undefined;
|
|
17
17
|
error?: undefined;
|
|
18
18
|
} | {
|
|
19
|
-
count?: undefined;
|
|
20
19
|
updated: number;
|
|
21
20
|
ok?: undefined;
|
|
22
21
|
error?: undefined;
|
|
23
|
-
} | {
|
|
24
22
|
count?: undefined;
|
|
23
|
+
} | {
|
|
25
24
|
updated?: undefined;
|
|
26
25
|
error: string;
|
|
27
26
|
ok?: undefined;
|
|
28
|
-
} | {
|
|
29
27
|
count?: undefined;
|
|
28
|
+
} | {
|
|
30
29
|
updated?: undefined;
|
|
31
30
|
ok: boolean;
|
|
32
31
|
error?: undefined;
|
|
32
|
+
count?: undefined;
|
|
33
33
|
}>>;
|
|
@@ -41,16 +41,16 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
|
|
|
41
41
|
thumbsUpRate: number;
|
|
42
42
|
avgEvalScore: number;
|
|
43
43
|
} | {
|
|
44
|
+
error?: undefined;
|
|
44
45
|
summary: import("./types.js").TraceSummary;
|
|
45
46
|
spans: import("./types.js").TraceSpan[];
|
|
46
47
|
id?: undefined;
|
|
47
|
-
error?: undefined;
|
|
48
48
|
ok?: undefined;
|
|
49
49
|
} | {
|
|
50
|
+
error?: undefined;
|
|
50
51
|
summary?: undefined;
|
|
51
52
|
spans?: undefined;
|
|
52
53
|
id: string;
|
|
53
|
-
error?: undefined;
|
|
54
54
|
ok?: undefined;
|
|
55
55
|
} | {
|
|
56
56
|
summary?: undefined;
|
|
@@ -59,9 +59,9 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
|
|
|
59
59
|
error: any;
|
|
60
60
|
ok?: undefined;
|
|
61
61
|
} | {
|
|
62
|
+
error?: undefined;
|
|
62
63
|
summary?: undefined;
|
|
63
64
|
spans?: undefined;
|
|
64
65
|
id?: undefined;
|
|
65
|
-
error?: undefined;
|
|
66
66
|
ok: boolean;
|
|
67
67
|
}>>;
|
|
@@ -193,6 +193,13 @@ export function createProviderApiDocsSchema(providerSchema = z.string().min(1))
|
|
|
193
193
|
search: WebContentSearchSchema.optional().describe("Optional post-fetch search over extracted content by default. Supports query, queries, terms, regex, source, maxMatches, and contextChars."),
|
|
194
194
|
});
|
|
195
195
|
}
|
|
196
|
+
const STAGED_DATASET_CAP_ERROR = /Staged dataset (?:byte )?cap exceeded/i;
|
|
197
|
+
function rethrowStagingError(error) {
|
|
198
|
+
if (error instanceof Error && STAGED_DATASET_CAP_ERROR.test(error.message)) {
|
|
199
|
+
throw new Error(`${error.message} Recover by deleting older staged datasets with list-staged-datasets/delete-staged-dataset, or switch this request to saveToFile / a smaller staged result before trying again.`, { cause: error });
|
|
200
|
+
}
|
|
201
|
+
throw error;
|
|
202
|
+
}
|
|
196
203
|
export function createProviderApiRequestAction(runtime, options = {}) {
|
|
197
204
|
const schema = options.schema ?? createProviderApiRequestSchema();
|
|
198
205
|
return defineAction({
|
|
@@ -244,10 +251,15 @@ export function createProviderApiRequestAction(runtime, options = {}) {
|
|
|
244
251
|
if (!ownerEmail) {
|
|
245
252
|
throw new Error("No authenticated context for provider API staging.");
|
|
246
253
|
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
254
|
+
try {
|
|
255
|
+
return await stagingExecuteRequest(args, runtime.executeRequest, {
|
|
256
|
+
appId: options.appId,
|
|
257
|
+
ownerEmail,
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
catch (error) {
|
|
261
|
+
rethrowStagingError(error);
|
|
262
|
+
}
|
|
251
263
|
}
|
|
252
264
|
return runtime.executeRequest(args);
|
|
253
265
|
},
|
|
@@ -51,8 +51,8 @@ export declare function handleDeleteResource(event: any): Promise<{
|
|
|
51
51
|
error: string;
|
|
52
52
|
ok?: undefined;
|
|
53
53
|
} | {
|
|
54
|
-
error?: undefined;
|
|
55
54
|
ok: boolean;
|
|
55
|
+
error?: undefined;
|
|
56
56
|
}>;
|
|
57
57
|
/** POST /_agent-native/resources/upload — upload a file as a resource */
|
|
58
58
|
export declare function handleUploadResource(event: any): Promise<import("./store.js").Resource | {
|
|
@@ -73,10 +73,10 @@ export declare function handleUploadResource(event: any): Promise<import("./stor
|
|
|
73
73
|
runId: string | null;
|
|
74
74
|
expiresAt: number | null;
|
|
75
75
|
metadata: string | null;
|
|
76
|
-
error?: undefined;
|
|
77
76
|
url: string;
|
|
78
77
|
provider: string;
|
|
79
78
|
storageSetupRequired?: undefined;
|
|
79
|
+
error?: undefined;
|
|
80
80
|
} | {
|
|
81
81
|
error: string;
|
|
82
82
|
storageSetupRequired: boolean;
|
|
@@ -27,10 +27,10 @@ export declare function resolveAgentEngineApiKeyWriteTarget(event: H3Event, scop
|
|
|
27
27
|
export declare function createAgentEngineApiKeyHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
28
28
|
error: any;
|
|
29
29
|
} | {
|
|
30
|
+
error?: undefined;
|
|
30
31
|
ok: boolean;
|
|
31
32
|
key: string;
|
|
32
33
|
baseUrlKey?: string;
|
|
33
34
|
scope: AgentEngineApiKeyScope;
|
|
34
|
-
error?: undefined;
|
|
35
35
|
}>>;
|
|
36
36
|
export {};
|
|
@@ -26,8 +26,8 @@ export declare function createRealtimeTokenHandler(): import("h3").EventHandlerW
|
|
|
26
26
|
expiresAt?: undefined;
|
|
27
27
|
ttlSeconds?: undefined;
|
|
28
28
|
} | {
|
|
29
|
+
error?: undefined;
|
|
29
30
|
token: string;
|
|
30
31
|
expiresAt: string;
|
|
31
32
|
ttlSeconds: number;
|
|
32
|
-
error?: undefined;
|
|
33
33
|
}>>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.0.0-beta-
|
|
3
|
+
"version": "0.0.0-beta-20260820171000",
|
|
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": {
|
|
@@ -428,7 +428,7 @@
|
|
|
428
428
|
"yjs": "^13.6.32",
|
|
429
429
|
"zod": "^4.3.6",
|
|
430
430
|
"@agent-native/recap-cli": "0.5.5",
|
|
431
|
-
"@agent-native/toolkit": "^0.
|
|
431
|
+
"@agent-native/toolkit": "^0.16.8"
|
|
432
432
|
},
|
|
433
433
|
"devDependencies": {
|
|
434
434
|
"@ai-sdk/anthropic": "^3.0.71",
|