@agent-native/core 0.90.3 → 0.90.5
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/bin/agent-native.js +25 -3
- package/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +17 -0
- package/corpus/core/bin/agent-native.js +25 -3
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/durable-background.ts +18 -18
- package/corpus/core/src/agent/engine/ai-sdk-engine.ts +8 -2
- package/corpus/core/src/agent/engine/anthropic-engine.ts +11 -1
- package/corpus/core/src/agent/engine/index.ts +1 -0
- package/corpus/core/src/agent/engine/registry.ts +63 -2
- package/corpus/core/src/agent/production-agent.ts +22 -11
- package/corpus/core/src/agent/run-manager.ts +17 -6
- package/corpus/core/src/agent/run-store.ts +16 -11
- package/corpus/core/src/cli/create.ts +35 -0
- package/corpus/core/src/cli/design-connect.ts +263 -1
- package/corpus/core/src/cli/index.ts +84 -7
- package/corpus/core/src/cli/workspacify.ts +34 -0
- package/corpus/core/src/client/KeepTabOpenNotice.tsx +20 -15
- package/corpus/core/src/client/RunStuckBanner.tsx +14 -13
- package/corpus/core/src/client/agent-chat-adapter.ts +28 -17
- package/corpus/core/src/client/history/VersionHistoryPanel.tsx +77 -47
- package/corpus/core/src/client/history/index.ts +3 -0
- package/corpus/core/src/client/history/use-history.ts +28 -0
- package/corpus/core/src/client/index.ts +8 -0
- package/corpus/core/src/client/review/index.ts +5 -0
- package/corpus/core/src/client/review/use-review.ts +40 -2
- package/corpus/core/src/client/use-run-stuck-detection.ts +8 -5
- package/corpus/core/src/history/actions/create-resource-version.ts +11 -8
- package/corpus/core/src/history/index.ts +0 -1
- package/corpus/core/src/history/registry.ts +24 -12
- package/corpus/core/src/history/store.ts +84 -53
- package/corpus/core/src/review/actions/consume-review-feedback.ts +15 -5
- package/corpus/core/src/review/actions/delete-review-comment.ts +15 -3
- package/corpus/core/src/review/actions/resolve-review-thread.ts +12 -5
- package/corpus/core/src/review/index.ts +0 -5
- package/corpus/core/src/review/registry.ts +24 -12
- package/corpus/core/src/review/store.ts +10 -7
- package/corpus/core/src/server/agent-chat-plugin.ts +6 -0
- package/corpus/core/src/server/credential-provider.ts +13 -3
- package/corpus/templates/assets/actions/_preset-skeleton-validation.ts +11 -11
- package/corpus/templates/assets/actions/generate-image.ts +64 -28
- package/corpus/templates/assets/app/components/generation/GenerationResults.tsx +1 -1
- package/corpus/templates/assets/app/routes/brand-kits.$id_.presets.$presetId.tsx +91 -74
- package/corpus/templates/assets/server/lib/generation.ts +29 -4
- package/corpus/templates/assets/server/lib/image-processing.ts +131 -0
- package/corpus/templates/calendar/.agents/skills/event-management/SKILL.md +24 -2
- package/corpus/templates/calendar/AGENTS.md +3 -0
- package/corpus/templates/calendar/actions/create-event.ts +8 -34
- package/corpus/templates/calendar/actions/event-action-helpers.ts +52 -0
- package/corpus/templates/calendar/actions/manage-event-draft.ts +7 -34
- package/corpus/templates/calendar/actions/update-event.ts +10 -23
- package/corpus/templates/calendar/app/components/calendar/AttendeeAutocomplete.tsx +47 -0
- package/corpus/templates/calendar/app/components/calendar/CreateEventDialog.tsx +23 -0
- package/corpus/templates/calendar/app/components/calendar/EventAttendeesSection.tsx +59 -2
- package/corpus/templates/calendar/app/components/calendar/EventDetailPanel.tsx +40 -1
- package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +34 -1
- package/corpus/templates/calendar/app/hooks/use-events.ts +6 -0
- package/corpus/templates/calendar/app/i18n/zh-TW.ts +4 -0
- package/corpus/templates/calendar/app/i18n-data.ts +40 -0
- package/corpus/templates/calendar/changelog/2026-07-07-you-can-mark-event-guests-as-optional-while-inviting-them-or.md +6 -0
- package/corpus/templates/calendar/server/handlers/events.ts +1 -0
- package/corpus/templates/calendar/server/lib/google-calendar.ts +4 -0
- package/corpus/templates/calendar/server/plugins/agent-chat.ts +1 -1
- package/corpus/templates/calendar/shared/api.ts +2 -0
- package/corpus/templates/clips/.agents/skills/ai-video-tools/SKILL.md +29 -3
- package/corpus/templates/clips/actions/generate-workflow.ts +19 -8
- package/corpus/templates/clips/actions/get-clips-ai-prefs.ts +36 -0
- package/corpus/templates/clips/actions/lib/clips-ai-prefs.ts +28 -0
- package/corpus/templates/clips/actions/regenerate-chapters.ts +21 -7
- package/corpus/templates/clips/actions/regenerate-summary.ts +26 -8
- package/corpus/templates/clips/actions/regenerate-title.ts +44 -7
- package/corpus/templates/clips/actions/update-clips-ai-prefs.ts +50 -0
- package/corpus/templates/clips/app/components/meetings/meeting-card.tsx +7 -2
- package/corpus/templates/clips/app/hooks/use-auto-title.ts +25 -10
- package/corpus/templates/clips/app/i18n/ar-SA.ts +28 -6
- package/corpus/templates/clips/app/i18n/de-DE.ts +28 -6
- package/corpus/templates/clips/app/i18n/en-US.ts +29 -6
- package/corpus/templates/clips/app/i18n/es-ES.ts +29 -6
- package/corpus/templates/clips/app/i18n/fr-FR.ts +30 -6
- package/corpus/templates/clips/app/i18n/hi-IN.ts +26 -6
- package/corpus/templates/clips/app/i18n/ja-JP.ts +27 -6
- package/corpus/templates/clips/app/i18n/ko-KR.ts +28 -6
- package/corpus/templates/clips/app/i18n/pt-BR.ts +28 -6
- package/corpus/templates/clips/app/i18n/zh-CN.ts +26 -6
- package/corpus/templates/clips/app/i18n/zh-TW.ts +25 -6
- package/corpus/templates/clips/app/routes/_app.meetings._index.tsx +73 -0
- package/corpus/templates/clips/app/routes/_app.settings._index.tsx +1 -0
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +70 -2
- package/corpus/templates/clips/changelog/2026-07-07-ai-tools-can-include-the-full-video-not-just-the-transcript-.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-07-clips-desktop-now-surfaces-meetings-and-dictate-directly-in-.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-07-meeting-notes-now-explain-exactly-how-to-start-granola-style.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-07-meeting-start-alerts-now-include-separate-start-notes-and-jo.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-07-recordings-start-more-predictably-and-newly-saved-clips-refr.md +6 -0
- package/corpus/templates/clips/chrome-extension/public/manifest.json +1 -1
- package/corpus/templates/clips/desktop/src/app.tsx +435 -105
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +27 -21
- package/corpus/templates/clips/desktop/src/overlays/meeting-notification.tsx +23 -22
- package/corpus/templates/clips/desktop/src/styles.css +167 -2
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +1 -1
- package/corpus/templates/clips/desktop/src-tauri/src/notifications.rs +3 -7
- package/corpus/templates/clips/desktop/src-tauri/src/tray_meetings.rs +5 -4
- package/corpus/templates/clips/server/routes/_agent-native/clips/user-prefs.put.ts +16 -8
- package/corpus/templates/clips/shared/clips-ai-prefs.ts +81 -0
- package/corpus/templates/content/AGENTS.md +1 -0
- package/corpus/templates/content/actions/_database-source-utils.ts +73 -8
- package/corpus/templates/content/actions/_database-utils.ts +2 -1
- package/corpus/templates/content/actions/prepare-builder-source-review.ts +20 -1
- package/corpus/templates/content/actions/stage-builder-source-bulk-update.ts +663 -0
- package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +33 -7
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +53 -4
- package/corpus/templates/content/app/components/editor/database/settings.tsx +24 -0
- package/corpus/templates/content/app/hooks/use-content-database.ts +70 -1
- package/corpus/templates/content/app/hooks/use-document-properties.ts +63 -5
- package/corpus/templates/content/app/i18n/zh-TW.ts +3 -0
- package/corpus/templates/content/app/i18n-data.ts +31 -0
- package/corpus/templates/content/changelog/2026-07-01-builder-source-rows-can-be-staged-in-bulk-with-a-reviewable.md +6 -0
- package/corpus/templates/content/changelog/2026-07-07-builder-backed-database-fields-now-edit-like-normal-content-fields.md +6 -0
- package/corpus/templates/content/parity/matrix.md +24 -24
- package/corpus/templates/content/parity/matrix.ts +2 -0
- package/corpus/templates/content/shared/api.ts +59 -4
- package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +16 -1
- package/corpus/templates/design/actions/add-localhost-screens.ts +1 -0
- package/corpus/templates/design/actions/connect-localhost.ts +26 -3
- package/corpus/templates/design/actions/open-visual-edit.ts +4 -37
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +362 -12
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +89 -14
- package/corpus/templates/design/app/components/design/bridge-security.ts +7 -1
- package/corpus/templates/design/app/components/design/multi-screen/types.ts +1 -1
- package/corpus/templates/design/app/i18n/zh-TW.ts +3 -0
- package/corpus/templates/design/app/i18n-data.ts +41 -0
- package/corpus/templates/design/changelog/2026-07-07-local-visual-edit-keeps-bridge-access-when-opening-previousl.md +6 -0
- package/corpus/templates/design/changelog/2026-07-07-local-visual-edit-now-keeps-the-live-app-connection-authenti.md +6 -0
- package/corpus/templates/design/changelog/2026-07-07-local-visual-edit-uses-live-app-iframes-for-click-to-edit-pr.md +6 -0
- package/dist/agent/durable-background.d.ts +12 -12
- package/dist/agent/durable-background.js +18 -18
- package/dist/agent/durable-background.js.map +1 -1
- package/dist/agent/engine/ai-sdk-engine.d.ts.map +1 -1
- package/dist/agent/engine/ai-sdk-engine.js +8 -2
- 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 +11 -1
- package/dist/agent/engine/anthropic-engine.js.map +1 -1
- package/dist/agent/engine/index.d.ts +1 -1
- package/dist/agent/engine/index.d.ts.map +1 -1
- package/dist/agent/engine/index.js +1 -1
- package/dist/agent/engine/index.js.map +1 -1
- package/dist/agent/engine/registry.d.ts +14 -0
- package/dist/agent/engine/registry.d.ts.map +1 -1
- package/dist/agent/engine/registry.js +56 -2
- package/dist/agent/engine/registry.js.map +1 -1
- package/dist/agent/production-agent.d.ts +2 -2
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +18 -11
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-manager.d.ts +7 -1
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +6 -1
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/run-store.d.ts +1 -1
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +15 -10
- package/dist/agent/run-store.js.map +1 -1
- package/dist/cli/create.d.ts.map +1 -1
- package/dist/cli/create.js +31 -0
- package/dist/cli/create.js.map +1 -1
- package/dist/cli/design-connect.d.ts.map +1 -1
- package/dist/cli/design-connect.js +197 -1
- package/dist/cli/design-connect.js.map +1 -1
- package/dist/cli/index.js +76 -9
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/workspacify.d.ts.map +1 -1
- package/dist/cli/workspacify.js +30 -0
- package/dist/cli/workspacify.js.map +1 -1
- package/dist/client/KeepTabOpenNotice.d.ts +9 -10
- package/dist/client/KeepTabOpenNotice.d.ts.map +1 -1
- package/dist/client/KeepTabOpenNotice.js +8 -4
- package/dist/client/KeepTabOpenNotice.js.map +1 -1
- package/dist/client/RunStuckBanner.d.ts.map +1 -1
- package/dist/client/RunStuckBanner.js +14 -13
- package/dist/client/RunStuckBanner.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +25 -17
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/history/VersionHistoryPanel.d.ts +1 -1
- package/dist/client/history/VersionHistoryPanel.d.ts.map +1 -1
- package/dist/client/history/VersionHistoryPanel.js +12 -5
- package/dist/client/history/VersionHistoryPanel.js.map +1 -1
- package/dist/client/history/index.d.ts +1 -1
- package/dist/client/history/index.d.ts.map +1 -1
- package/dist/client/history/index.js +1 -1
- package/dist/client/history/index.js.map +1 -1
- package/dist/client/history/use-history.d.ts +12 -0
- package/dist/client/history/use-history.d.ts.map +1 -1
- package/dist/client/history/use-history.js +6 -0
- package/dist/client/history/use-history.js.map +1 -1
- package/dist/client/index.d.ts +2 -2
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +2 -2
- package/dist/client/index.js.map +1 -1
- package/dist/client/review/index.d.ts +1 -1
- package/dist/client/review/index.d.ts.map +1 -1
- package/dist/client/review/index.js +1 -1
- package/dist/client/review/index.js.map +1 -1
- package/dist/client/review/use-review.d.ts +23 -0
- package/dist/client/review/use-review.d.ts.map +1 -1
- package/dist/client/review/use-review.js +8 -0
- package/dist/client/review/use-review.js.map +1 -1
- package/dist/client/use-run-stuck-detection.d.ts +1 -1
- package/dist/client/use-run-stuck-detection.d.ts.map +1 -1
- package/dist/client/use-run-stuck-detection.js +6 -4
- package/dist/client/use-run-stuck-detection.js.map +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/history/actions/create-resource-version.js +7 -4
- package/dist/history/actions/create-resource-version.js.map +1 -1
- package/dist/history/index.d.ts +1 -1
- package/dist/history/index.d.ts.map +1 -1
- package/dist/history/index.js +1 -1
- package/dist/history/index.js.map +1 -1
- package/dist/history/registry.d.ts.map +1 -1
- package/dist/history/registry.js +14 -12
- package/dist/history/registry.js.map +1 -1
- package/dist/history/store.d.ts.map +1 -1
- package/dist/history/store.js +74 -49
- package/dist/history/store.js.map +1 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +3 -3
- package/dist/review/actions/consume-review-feedback.d.ts +1 -0
- package/dist/review/actions/consume-review-feedback.js +8 -2
- package/dist/review/actions/consume-review-feedback.js.map +1 -1
- package/dist/review/actions/delete-review-comment.d.ts +2 -1
- package/dist/review/actions/delete-review-comment.js +11 -3
- package/dist/review/actions/delete-review-comment.js.map +1 -1
- package/dist/review/actions/resolve-review-thread.d.ts +2 -1
- package/dist/review/actions/resolve-review-thread.js +5 -2
- package/dist/review/actions/resolve-review-thread.js.map +1 -1
- package/dist/review/index.d.ts +1 -1
- package/dist/review/index.d.ts.map +1 -1
- package/dist/review/index.js +1 -1
- package/dist/review/index.js.map +1 -1
- package/dist/review/registry.d.ts.map +1 -1
- package/dist/review/registry.js +14 -12
- package/dist/review/registry.js.map +1 -1
- package/dist/review/store.d.ts +3 -3
- package/dist/review/store.d.ts.map +1 -1
- package/dist/review/store.js +7 -4
- package/dist/review/store.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +5 -0
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/credential-provider.d.ts +2 -0
- package/dist/server/credential-provider.d.ts.map +1 -1
- package/dist/server/credential-provider.js +13 -3
- package/dist/server/credential-provider.js.map +1 -1
- package/package.json +1 -1
|
@@ -33,6 +33,16 @@ const BRIDGE_OPERATIONS = [
|
|
|
33
33
|
|
|
34
34
|
type BridgeOperation = (typeof BRIDGE_OPERATIONS)[number];
|
|
35
35
|
|
|
36
|
+
const SERVER_REGISTRATION_BRIDGE_OPERATIONS = new Set<BridgeOperation>([
|
|
37
|
+
"select",
|
|
38
|
+
"resolveNodeToFile",
|
|
39
|
+
"readFile",
|
|
40
|
+
"applyEdit",
|
|
41
|
+
"writeFile",
|
|
42
|
+
"captureSnapshot",
|
|
43
|
+
"captureState",
|
|
44
|
+
]);
|
|
45
|
+
|
|
36
46
|
/** Additive manifest capability flags advertised alongside the operation list. */
|
|
37
47
|
const MANIFEST_CAPABILITIES = {
|
|
38
48
|
listFiles: true,
|
|
@@ -507,6 +517,47 @@ function sendJson(
|
|
|
507
517
|
res.end(`${JSON.stringify(body, null, 2)}\n`);
|
|
508
518
|
}
|
|
509
519
|
|
|
520
|
+
function sendText(
|
|
521
|
+
res: ServerResponse,
|
|
522
|
+
statusCode: number,
|
|
523
|
+
body: string,
|
|
524
|
+
contentType: string,
|
|
525
|
+
) {
|
|
526
|
+
res.writeHead(statusCode, {
|
|
527
|
+
"content-type": contentType,
|
|
528
|
+
"access-control-allow-origin": "*",
|
|
529
|
+
"access-control-allow-methods": "GET, POST, OPTIONS",
|
|
530
|
+
"access-control-allow-headers": "content-type, x-bridge-token",
|
|
531
|
+
"access-control-allow-private-network": "true",
|
|
532
|
+
});
|
|
533
|
+
res.end(body);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
function sendBytes(
|
|
537
|
+
res: ServerResponse,
|
|
538
|
+
statusCode: number,
|
|
539
|
+
body: Buffer,
|
|
540
|
+
headers: Headers,
|
|
541
|
+
) {
|
|
542
|
+
const responseHeaders: Record<string, string> = {
|
|
543
|
+
"access-control-allow-origin": "*",
|
|
544
|
+
"access-control-allow-methods": "GET, HEAD, POST, OPTIONS",
|
|
545
|
+
"access-control-allow-headers": "content-type, x-bridge-token",
|
|
546
|
+
"access-control-allow-private-network": "true",
|
|
547
|
+
};
|
|
548
|
+
for (const name of [
|
|
549
|
+
"content-type",
|
|
550
|
+
"cache-control",
|
|
551
|
+
"etag",
|
|
552
|
+
"last-modified",
|
|
553
|
+
]) {
|
|
554
|
+
const value = headers.get(name);
|
|
555
|
+
if (value) responseHeaders[name] = value;
|
|
556
|
+
}
|
|
557
|
+
res.writeHead(statusCode, responseHeaders);
|
|
558
|
+
res.end(body);
|
|
559
|
+
}
|
|
560
|
+
|
|
510
561
|
function sameOrigin(a: string, b: string): boolean {
|
|
511
562
|
try {
|
|
512
563
|
return new URL(a).origin === new URL(b).origin;
|
|
@@ -531,6 +582,23 @@ function resolvePreviewSnapshotUrl(
|
|
|
531
582
|
return parsed.toString();
|
|
532
583
|
}
|
|
533
584
|
|
|
585
|
+
function resolvePreviewProxyUrl(
|
|
586
|
+
devServerUrl: string,
|
|
587
|
+
requestUrl: string | undefined,
|
|
588
|
+
): string {
|
|
589
|
+
const base = normalizeHttpUrl(devServerUrl);
|
|
590
|
+
const parsedRequest = new URL(requestUrl ?? "/", base);
|
|
591
|
+
const parsed = new URL(
|
|
592
|
+
`${parsedRequest.pathname}${parsedRequest.search}`,
|
|
593
|
+
`${base}/`,
|
|
594
|
+
);
|
|
595
|
+
parsed.hash = "";
|
|
596
|
+
if (!sameOrigin(parsed.toString(), base)) {
|
|
597
|
+
throw new Error("Proxy URL must stay on the connected dev server.");
|
|
598
|
+
}
|
|
599
|
+
return parsed.toString();
|
|
600
|
+
}
|
|
601
|
+
|
|
534
602
|
async function fetchPreviewSnapshot(
|
|
535
603
|
devServerUrl: string,
|
|
536
604
|
targetUrl: string,
|
|
@@ -579,6 +647,79 @@ async function fetchPreviewSnapshot(
|
|
|
579
647
|
}
|
|
580
648
|
}
|
|
581
649
|
|
|
650
|
+
async function fetchPreviewProxyResource(
|
|
651
|
+
devServerUrl: string,
|
|
652
|
+
targetUrl: string,
|
|
653
|
+
redirects = 0,
|
|
654
|
+
): Promise<{
|
|
655
|
+
url: string;
|
|
656
|
+
status: number;
|
|
657
|
+
headers: Headers;
|
|
658
|
+
body: Buffer;
|
|
659
|
+
}> {
|
|
660
|
+
if (redirects > 5) {
|
|
661
|
+
throw new Error("Too many redirects while proxying preview resource.");
|
|
662
|
+
}
|
|
663
|
+
const controller = new AbortController();
|
|
664
|
+
const timeout = setTimeout(() => controller.abort(), 10_000);
|
|
665
|
+
try {
|
|
666
|
+
const response = await fetch(targetUrl, {
|
|
667
|
+
method: "GET",
|
|
668
|
+
redirect: "manual",
|
|
669
|
+
signal: controller.signal,
|
|
670
|
+
});
|
|
671
|
+
const location = response.headers.get("location");
|
|
672
|
+
if (location && response.status >= 300 && response.status < 400) {
|
|
673
|
+
const redirected = new URL(location, targetUrl);
|
|
674
|
+
redirected.hash = "";
|
|
675
|
+
if (!sameOrigin(redirected.toString(), devServerUrl)) {
|
|
676
|
+
throw new Error("Proxy redirect left the connected dev server.");
|
|
677
|
+
}
|
|
678
|
+
return fetchPreviewProxyResource(
|
|
679
|
+
devServerUrl,
|
|
680
|
+
redirected.toString(),
|
|
681
|
+
redirects + 1,
|
|
682
|
+
);
|
|
683
|
+
}
|
|
684
|
+
return {
|
|
685
|
+
url: response.url || targetUrl,
|
|
686
|
+
status: response.status,
|
|
687
|
+
headers: response.headers,
|
|
688
|
+
body: Buffer.from(await response.arrayBuffer()),
|
|
689
|
+
};
|
|
690
|
+
} finally {
|
|
691
|
+
clearTimeout(timeout);
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
function addLiveEditBaseHref(html: string, href: string): string {
|
|
696
|
+
const escapedHref = href.replace(/&/g, "&").replace(/"/g, """);
|
|
697
|
+
const baseTag = `<base href="${escapedHref}">`;
|
|
698
|
+
if (/<base\b/i.test(html)) return html;
|
|
699
|
+
if (/<head\b[^>]*>/i.test(html)) {
|
|
700
|
+
return html.replace(/<head\b[^>]*>/i, (match) => `${match}${baseTag}`);
|
|
701
|
+
}
|
|
702
|
+
if (/<html\b[^>]*>/i.test(html)) {
|
|
703
|
+
return html.replace(
|
|
704
|
+
/<html\b[^>]*>/i,
|
|
705
|
+
(match) => `${match}<head>${baseTag}</head>`,
|
|
706
|
+
);
|
|
707
|
+
}
|
|
708
|
+
return `<!DOCTYPE html><html><head>${baseTag}<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"></head><body>${html}</body></html>`;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
function injectLiveEditBridge(html: string, targetUrl: string, script: string) {
|
|
712
|
+
const withBase = addLiveEditBaseHref(html, targetUrl);
|
|
713
|
+
if (!script) return withBase;
|
|
714
|
+
if (withBase.includes("</body>")) {
|
|
715
|
+
return withBase.replace("</body>", `${script}</body>`);
|
|
716
|
+
}
|
|
717
|
+
if (withBase.includes("</html>")) {
|
|
718
|
+
return withBase.replace("</html>", `${script}</html>`);
|
|
719
|
+
}
|
|
720
|
+
return `${withBase}${script}`;
|
|
721
|
+
}
|
|
722
|
+
|
|
582
723
|
/** Read the full request body as a UTF-8 string. */
|
|
583
724
|
async function readRequestBody(req: IncomingMessage): Promise<string> {
|
|
584
725
|
return new Promise<string>((resolve, reject) => {
|
|
@@ -1046,6 +1187,7 @@ export async function startDesignConnectBridge(
|
|
|
1046
1187
|
// an unauthenticated caller cannot read it. The server-side grant action
|
|
1047
1188
|
// obtains it out-of-band (via the exported bridge reference).
|
|
1048
1189
|
const bridgeToken = crypto.randomBytes(32).toString("hex");
|
|
1190
|
+
let liveEditBridgeScript = "";
|
|
1049
1191
|
|
|
1050
1192
|
const server = http.createServer(
|
|
1051
1193
|
(req: IncomingMessage, res: ServerResponse) => {
|
|
@@ -1077,6 +1219,97 @@ export async function startDesignConnectBridge(
|
|
|
1077
1219
|
sendJson(res, 200, { ok: true, source: manifest.source });
|
|
1078
1220
|
return;
|
|
1079
1221
|
}
|
|
1222
|
+
if (pathname === "/live-edit-bridge") {
|
|
1223
|
+
if (req.method !== "POST") {
|
|
1224
|
+
sendJson(res, 405, { ok: false, error: "method not allowed" });
|
|
1225
|
+
return;
|
|
1226
|
+
}
|
|
1227
|
+
const tokenHeader = req.headers["x-bridge-token"];
|
|
1228
|
+
const providedToken =
|
|
1229
|
+
typeof tokenHeader === "string" ? tokenHeader : "";
|
|
1230
|
+
let tokenValid = false;
|
|
1231
|
+
try {
|
|
1232
|
+
tokenValid =
|
|
1233
|
+
providedToken.length === bridgeToken.length &&
|
|
1234
|
+
crypto.timingSafeEqual(
|
|
1235
|
+
Buffer.from(providedToken, "utf8"),
|
|
1236
|
+
Buffer.from(bridgeToken, "utf8"),
|
|
1237
|
+
);
|
|
1238
|
+
} catch {
|
|
1239
|
+
tokenValid = false;
|
|
1240
|
+
}
|
|
1241
|
+
if (!tokenValid) {
|
|
1242
|
+
sendJson(res, 401, {
|
|
1243
|
+
ok: false,
|
|
1244
|
+
error: "invalid or missing bridge token",
|
|
1245
|
+
});
|
|
1246
|
+
return;
|
|
1247
|
+
}
|
|
1248
|
+
void (async () => {
|
|
1249
|
+
try {
|
|
1250
|
+
const raw = await readRequestBody(req);
|
|
1251
|
+
const body = JSON.parse(raw) as Record<string, unknown>;
|
|
1252
|
+
const script =
|
|
1253
|
+
typeof body["script"] === "string" ? body["script"] : "";
|
|
1254
|
+
if (!script.includes("agent-native:editor-chrome-ready")) {
|
|
1255
|
+
sendJson(res, 400, {
|
|
1256
|
+
ok: false,
|
|
1257
|
+
error: "script must install the Agent Native editor bridge",
|
|
1258
|
+
});
|
|
1259
|
+
return;
|
|
1260
|
+
}
|
|
1261
|
+
liveEditBridgeScript = script;
|
|
1262
|
+
sendJson(res, 200, { ok: true });
|
|
1263
|
+
} catch (err: unknown) {
|
|
1264
|
+
sendJson(res, 400, {
|
|
1265
|
+
ok: false,
|
|
1266
|
+
error: err instanceof Error ? err.message : String(err),
|
|
1267
|
+
});
|
|
1268
|
+
}
|
|
1269
|
+
})();
|
|
1270
|
+
return;
|
|
1271
|
+
}
|
|
1272
|
+
if (pathname === "/live-edit") {
|
|
1273
|
+
if (req.method !== "GET") {
|
|
1274
|
+
sendJson(res, 405, { ok: false, error: "method not allowed" });
|
|
1275
|
+
return;
|
|
1276
|
+
}
|
|
1277
|
+
void (async () => {
|
|
1278
|
+
try {
|
|
1279
|
+
const requestUrl = new URL(req.url ?? "/", manifest.bridgeUrl);
|
|
1280
|
+
const targetUrl = resolvePreviewSnapshotUrl(
|
|
1281
|
+
manifest.devServerUrl,
|
|
1282
|
+
requestUrl.searchParams.get("url") ??
|
|
1283
|
+
requestUrl.searchParams.get("path"),
|
|
1284
|
+
);
|
|
1285
|
+
const snapshot = await fetchPreviewSnapshot(
|
|
1286
|
+
manifest.devServerUrl,
|
|
1287
|
+
targetUrl,
|
|
1288
|
+
);
|
|
1289
|
+
const includeEditorBridge =
|
|
1290
|
+
requestUrl.searchParams.get("bridge") !== "0";
|
|
1291
|
+
const html = injectLiveEditBridge(
|
|
1292
|
+
snapshot.html,
|
|
1293
|
+
new URL("/", manifest.bridgeUrl).toString(),
|
|
1294
|
+
includeEditorBridge ? liveEditBridgeScript : "",
|
|
1295
|
+
);
|
|
1296
|
+
sendText(
|
|
1297
|
+
res,
|
|
1298
|
+
snapshot.status >= 400 ? snapshot.status : 200,
|
|
1299
|
+
html,
|
|
1300
|
+
snapshot.contentType.includes("html")
|
|
1301
|
+
? snapshot.contentType
|
|
1302
|
+
: "text/html; charset=utf-8",
|
|
1303
|
+
);
|
|
1304
|
+
} catch (err: unknown) {
|
|
1305
|
+
sendJson(res, 400, {
|
|
1306
|
+
ok: false,
|
|
1307
|
+
error: err instanceof Error ? err.message : String(err),
|
|
1308
|
+
});
|
|
1309
|
+
}
|
|
1310
|
+
})();
|
|
1311
|
+
return;
|
|
1312
|
+
}
|
|
1080
1313
|
if (pathname === "/snapshot") {
|
|
1081
1314
|
if (req.method !== "GET") {
|
|
1082
1315
|
sendJson(res, 405, { ok: false, error: "method not allowed" });
|
|
@@ -1356,6 +1589,33 @@ export async function startDesignConnectBridge(
|
|
|
1356
1589
|
return;
|
|
1357
1590
|
}
|
|
1358
1591
|
|
|
1592
|
+
if (req.method === "GET" || req.method === "HEAD") {
|
|
1593
|
+
void (async () => {
|
|
1594
|
+
try {
|
|
1595
|
+
const targetUrl = resolvePreviewProxyUrl(
|
|
1596
|
+
manifest.devServerUrl,
|
|
1597
|
+
req.url,
|
|
1598
|
+
);
|
|
1599
|
+
const proxied = await fetchPreviewProxyResource(
|
|
1600
|
+
manifest.devServerUrl,
|
|
1601
|
+
targetUrl,
|
|
1602
|
+
);
|
|
1603
|
+
sendBytes(
|
|
1604
|
+
res,
|
|
1605
|
+
proxied.status >= 400 ? proxied.status : 200,
|
|
1606
|
+
req.method === "HEAD" ? Buffer.alloc(0) : proxied.body,
|
|
1607
|
+
proxied.headers,
|
|
1608
|
+
);
|
|
1609
|
+
} catch (err: unknown) {
|
|
1610
|
+
sendJson(res, 400, {
|
|
1611
|
+
ok: false,
|
|
1612
|
+
error: err instanceof Error ? err.message : String(err),
|
|
1613
|
+
});
|
|
1614
|
+
}
|
|
1615
|
+
})();
|
|
1616
|
+
return;
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1359
1619
|
sendJson(res, 404, { ok: false, error: "not found" });
|
|
1360
1620
|
},
|
|
1361
1621
|
);
|
|
@@ -1432,7 +1692,9 @@ export async function registerConnectionWithServer(
|
|
|
1432
1692
|
devServerUrl: manifest.devServerUrl,
|
|
1433
1693
|
bridgeUrl: manifest.bridgeUrl,
|
|
1434
1694
|
rootPath: manifest.rootPath,
|
|
1435
|
-
capabilities: manifest.capabilities
|
|
1695
|
+
capabilities: manifest.capabilities.filter((capability) =>
|
|
1696
|
+
SERVER_REGISTRATION_BRIDGE_OPERATIONS.has(capability.operation),
|
|
1697
|
+
),
|
|
1436
1698
|
routeManifest: {
|
|
1437
1699
|
version: 1 as const,
|
|
1438
1700
|
sourceType: "localhost" as const,
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { execSync, spawn } from "child_process";
|
|
4
4
|
import fs from "fs";
|
|
5
|
+
import { createRequire } from "module";
|
|
5
6
|
import path from "path";
|
|
6
7
|
import { fileURLToPath } from "url";
|
|
7
8
|
|
|
@@ -253,11 +254,20 @@ function handleScaffoldImportError(err: any): never {
|
|
|
253
254
|
throw err;
|
|
254
255
|
}
|
|
255
256
|
|
|
257
|
+
function findBinUpwards(binName: string): string | undefined {
|
|
258
|
+
let dir = process.cwd();
|
|
259
|
+
for (let i = 0; i < 20; i++) {
|
|
260
|
+
const candidate = path.join(dir, "node_modules", ".bin", binName);
|
|
261
|
+
if (fs.existsSync(candidate)) return candidate;
|
|
262
|
+
const parent = path.dirname(dir);
|
|
263
|
+
if (parent === dir) break;
|
|
264
|
+
dir = parent;
|
|
265
|
+
}
|
|
266
|
+
return undefined;
|
|
267
|
+
}
|
|
268
|
+
|
|
256
269
|
function findViteBin(): string {
|
|
257
|
-
|
|
258
|
-
const localVite = path.resolve("node_modules/.bin/vite");
|
|
259
|
-
if (fs.existsSync(localVite)) return localVite;
|
|
260
|
-
return "vite"; // fallback to PATH
|
|
270
|
+
return findBinUpwards("vite") ?? "vite";
|
|
261
271
|
}
|
|
262
272
|
|
|
263
273
|
function findTsxBin(): string {
|
|
@@ -277,9 +287,7 @@ function findTypeScriptCompilerBin(): string {
|
|
|
277
287
|
}
|
|
278
288
|
|
|
279
289
|
function findReactRouterBin(): string {
|
|
280
|
-
|
|
281
|
-
if (fs.existsSync(localBin)) return localBin;
|
|
282
|
-
return "react-router";
|
|
290
|
+
return findBinUpwards("react-router") ?? "react-router";
|
|
283
291
|
}
|
|
284
292
|
|
|
285
293
|
/** Check if the project uses React Router framework mode (has react-router.config.ts) */
|
|
@@ -290,6 +298,73 @@ function isReactRouterFramework(): boolean {
|
|
|
290
298
|
);
|
|
291
299
|
}
|
|
292
300
|
|
|
301
|
+
function canResolveFromProject(specifier: string): boolean {
|
|
302
|
+
try {
|
|
303
|
+
const requireFromProject = createRequire(path.resolve("package.json"));
|
|
304
|
+
requireFromProject.resolve(specifier);
|
|
305
|
+
return true;
|
|
306
|
+
} catch {
|
|
307
|
+
return false;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function projectUsesFsRoutes(): boolean {
|
|
312
|
+
for (const file of [
|
|
313
|
+
path.resolve("app/routes.ts"),
|
|
314
|
+
path.resolve("app/routes.tsx"),
|
|
315
|
+
path.resolve("routes.ts"),
|
|
316
|
+
path.resolve("routes.tsx"),
|
|
317
|
+
]) {
|
|
318
|
+
try {
|
|
319
|
+
if (
|
|
320
|
+
fs.existsSync(file) &&
|
|
321
|
+
fs.readFileSync(file, "utf-8").includes("@react-router/fs-routes")
|
|
322
|
+
) {
|
|
323
|
+
return true;
|
|
324
|
+
}
|
|
325
|
+
} catch {}
|
|
326
|
+
}
|
|
327
|
+
return false;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function validateReactRouterBuildDependencies(): void {
|
|
331
|
+
const required: Array<[packageName: string, specifier: string]> = [
|
|
332
|
+
["react-router", "react-router"],
|
|
333
|
+
["@react-router/dev", "@react-router/dev/vite"],
|
|
334
|
+
["vite", "vite"],
|
|
335
|
+
];
|
|
336
|
+
if (projectUsesFsRoutes()) {
|
|
337
|
+
required.push(["@react-router/fs-routes", "@react-router/fs-routes"]);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const missing = required
|
|
341
|
+
.filter(([, specifier]) => !canResolveFromProject(specifier))
|
|
342
|
+
.map(([packageName]) => packageName);
|
|
343
|
+
|
|
344
|
+
if (missing.length === 0) return;
|
|
345
|
+
|
|
346
|
+
const packageManager = fs.existsSync(path.resolve("pnpm-lock.yaml"))
|
|
347
|
+
? "pnpm"
|
|
348
|
+
: fs.existsSync(path.resolve("yarn.lock"))
|
|
349
|
+
? "yarn"
|
|
350
|
+
: "npm";
|
|
351
|
+
const installCommand =
|
|
352
|
+
packageManager === "pnpm"
|
|
353
|
+
? `pnpm add ${missing.join(" ")}`
|
|
354
|
+
: packageManager === "yarn"
|
|
355
|
+
? `yarn add ${missing.join(" ")}`
|
|
356
|
+
: `npm install ${missing.join(" ")}`;
|
|
357
|
+
|
|
358
|
+
console.error(
|
|
359
|
+
[
|
|
360
|
+
"React Router framework mode requires build packages that are not installed from this app root.",
|
|
361
|
+
`Missing: ${missing.join(", ")}`,
|
|
362
|
+
`Install them with: ${installCommand}`,
|
|
363
|
+
].join("\n"),
|
|
364
|
+
);
|
|
365
|
+
process.exit(1);
|
|
366
|
+
}
|
|
367
|
+
|
|
293
368
|
function isWorkspaceRoot(): boolean {
|
|
294
369
|
const pkgPath = path.resolve("package.json");
|
|
295
370
|
if (!fs.existsSync(pkgPath)) return false;
|
|
@@ -488,6 +563,7 @@ switch (command) {
|
|
|
488
563
|
// continuation only runs on success.
|
|
489
564
|
(async () => {
|
|
490
565
|
if (isReactRouterFramework()) {
|
|
566
|
+
validateReactRouterBuildDependencies();
|
|
491
567
|
const rr = findReactRouterBin();
|
|
492
568
|
console.log("Building (React Router framework mode)...");
|
|
493
569
|
await runBuildStep(rr, ["build"], { label: "react-router-build" });
|
|
@@ -592,6 +668,7 @@ switch (command) {
|
|
|
592
668
|
// Run TypeScript type checking
|
|
593
669
|
// React Router framework mode generates route types first
|
|
594
670
|
if (isReactRouterFramework()) {
|
|
671
|
+
validateReactRouterBuildDependencies();
|
|
595
672
|
const rr = findReactRouterBin();
|
|
596
673
|
try {
|
|
597
674
|
execSync(`${rr} typegen`, { stdio: "inherit" });
|
|
@@ -24,6 +24,12 @@ import fs from "fs";
|
|
|
24
24
|
import path from "path";
|
|
25
25
|
|
|
26
26
|
const POSTGRES_DEPENDENCY_VERSION = "^3.4.9";
|
|
27
|
+
const REACT_ROUTER_BUILD_DEPENDENCIES = [
|
|
28
|
+
"@react-router/dev",
|
|
29
|
+
"@react-router/fs-routes",
|
|
30
|
+
"react-router",
|
|
31
|
+
"vite",
|
|
32
|
+
] as const;
|
|
27
33
|
|
|
28
34
|
export interface WorkspacifyOptions {
|
|
29
35
|
/** Target app directory (already populated with the copied template) */
|
|
@@ -87,6 +93,7 @@ export function workspacifyApp(opts: WorkspacifyOptions): void {
|
|
|
87
93
|
// Add the runtime package to workspace apps so production bundles do
|
|
88
94
|
// not fail only after a hosted Postgres database is configured.
|
|
89
95
|
pkg.dependencies.postgres ??= POSTGRES_DEPENDENCY_VERSION;
|
|
96
|
+
ensureReactRouterBuildDependencies(pkg);
|
|
90
97
|
// pnpm build-script approvals belong at the workspace root. Leaving the
|
|
91
98
|
// template's per-app setting in place makes pnpm warn on every install.
|
|
92
99
|
if (pkg.pnpm && typeof pkg.pnpm === "object") {
|
|
@@ -156,6 +163,33 @@ export function workspacifyApp(opts: WorkspacifyOptions): void {
|
|
|
156
163
|
}
|
|
157
164
|
}
|
|
158
165
|
|
|
166
|
+
function ensureReactRouterBuildDependencies(pkg: Record<string, any>): void {
|
|
167
|
+
const allDeps = {
|
|
168
|
+
...pkg.dependencies,
|
|
169
|
+
...pkg.devDependencies,
|
|
170
|
+
...pkg.peerDependencies,
|
|
171
|
+
};
|
|
172
|
+
if (
|
|
173
|
+
!allDeps["@react-router/dev"] &&
|
|
174
|
+
!allDeps["react-router"] &&
|
|
175
|
+
!allDeps["@react-router/fs-routes"]
|
|
176
|
+
) {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
pkg.dependencies = pkg.dependencies ?? {};
|
|
181
|
+
for (const key of REACT_ROUTER_BUILD_DEPENDENCIES) {
|
|
182
|
+
const existing =
|
|
183
|
+
pkg.dependencies[key] ??
|
|
184
|
+
pkg.devDependencies?.[key] ??
|
|
185
|
+
pkg.peerDependencies?.[key];
|
|
186
|
+
if (!existing) continue;
|
|
187
|
+
pkg.dependencies[key] = existing;
|
|
188
|
+
delete pkg.devDependencies?.[key];
|
|
189
|
+
delete pkg.peerDependencies?.[key];
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
159
193
|
function writeInheritedChatPlugin(
|
|
160
194
|
appDir: string,
|
|
161
195
|
workspaceCoreName: string,
|
|
@@ -9,23 +9,22 @@ import { cn } from "./utils.js";
|
|
|
9
9
|
export const DEFAULT_KEEP_TAB_OPEN_AFTER_MS = 30_000;
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
* Subtle, non-blocking notice that a long-running
|
|
13
|
-
* depends on this tab staying open.
|
|
12
|
+
* Subtle, non-blocking notice that a long-running client-continued foreground
|
|
13
|
+
* agent turn depends on this tab staying open.
|
|
14
14
|
*
|
|
15
15
|
* On hosted deployments a foreground turn runs in ~40s chunks and the CLIENT
|
|
16
|
-
* re-
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* tab, and for those this notice never shows.
|
|
16
|
+
* may need to re-POST `auto_continue` to start each next chunk. Server-owned
|
|
17
|
+
* runs (`dispatchMode` starting with "background" or equal to
|
|
18
|
+
* "foreground-self-chain") survive a closed tab, and for those this notice
|
|
19
|
+
* never shows.
|
|
21
20
|
*
|
|
22
21
|
* Visibility rules (show only while the condition is true — no new
|
|
23
22
|
* always-visible chrome):
|
|
24
|
-
* - a
|
|
23
|
+
* - a client-continued foreground run for this thread has been continuously
|
|
25
24
|
* running for `showAfterMs` (default 30s — approaching the hosted ~40s
|
|
26
25
|
* chunk boundary where the client-driven continuation starts), and
|
|
27
|
-
* - the run is
|
|
28
|
-
*
|
|
26
|
+
* - the run is not server-continued (hidden immediately when the server owns
|
|
27
|
+
* recovery), and
|
|
29
28
|
* - this is a production client bundle (`hosted` auto-detect): local dev
|
|
30
29
|
* runs a turn unbounded in a single chunk that survives a closed tab,
|
|
31
30
|
* so the notice would be wrong there.
|
|
@@ -78,6 +77,13 @@ function isDevClientBundle(): boolean {
|
|
|
78
77
|
}
|
|
79
78
|
}
|
|
80
79
|
|
|
80
|
+
function isServerContinuedDispatch(dispatchMode: string | null): boolean {
|
|
81
|
+
return (
|
|
82
|
+
dispatchMode === "foreground-self-chain" ||
|
|
83
|
+
dispatchMode?.startsWith("background") === true
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
81
87
|
export function KeepTabOpenNotice({
|
|
82
88
|
threadId,
|
|
83
89
|
enabled = true,
|
|
@@ -93,10 +99,9 @@ export function KeepTabOpenNotice({
|
|
|
93
99
|
apiUrl,
|
|
94
100
|
pollIntervalMs: NOTICE_POLL_INTERVAL_MS,
|
|
95
101
|
});
|
|
96
|
-
const
|
|
97
|
-
state.dispatchMode?.startsWith("background") === true;
|
|
102
|
+
const isServerContinued = isServerContinuedDispatch(state.dispatchMode);
|
|
98
103
|
const foregroundRunning =
|
|
99
|
-
state.status === "running" && Boolean(state.runId) && !
|
|
104
|
+
state.status === "running" && Boolean(state.runId) && !isServerContinued;
|
|
100
105
|
|
|
101
106
|
const [visible, setVisible] = useState(false);
|
|
102
107
|
const runningSinceRef = useRef<number | null>(null);
|
|
@@ -109,7 +114,7 @@ export function KeepTabOpenNotice({
|
|
|
109
114
|
}, [threadId]);
|
|
110
115
|
|
|
111
116
|
useEffect(() => {
|
|
112
|
-
if (!effectiveHosted ||
|
|
117
|
+
if (!effectiveHosted || isServerContinued) {
|
|
113
118
|
// Server-owned turn (or non-hosted client): the tab is not load-bearing.
|
|
114
119
|
// Hide immediately and reset — no linger.
|
|
115
120
|
runningSinceRef.current = null;
|
|
@@ -143,7 +148,7 @@ export function KeepTabOpenNotice({
|
|
|
143
148
|
}, [
|
|
144
149
|
effectiveHosted,
|
|
145
150
|
foregroundRunning,
|
|
146
|
-
|
|
151
|
+
isServerContinued,
|
|
147
152
|
showAfterMs,
|
|
148
153
|
visible,
|
|
149
154
|
]);
|
|
@@ -175,15 +175,16 @@ export function RunStuckBanner({
|
|
|
175
175
|
}
|
|
176
176
|
const ownerId = autoRetryOwnerId ?? generatedOwnerIdRef.current;
|
|
177
177
|
const backgroundWorkerStillAlive = isFreshBackgroundWorker(state);
|
|
178
|
-
//
|
|
179
|
-
//
|
|
180
|
-
//
|
|
181
|
-
//
|
|
182
|
-
//
|
|
183
|
-
//
|
|
184
|
-
//
|
|
185
|
-
//
|
|
186
|
-
const
|
|
178
|
+
// Server-continued runs are recovered by the SERVER (chained continuation
|
|
179
|
+
// chunks + lost-handoff sweep); an automatic client abort would kill a live
|
|
180
|
+
// server-chained run. Auto-retry is therefore disabled unconditionally for
|
|
181
|
+
// these modes — not just a fresh-heartbeat worker — and the
|
|
182
|
+
// localStorage/Web-Locks auto-retry claim below is never taken for them (the
|
|
183
|
+
// adapter's follow loop is read-only, so multiple tabs need no retry dedup).
|
|
184
|
+
// Only the manual banner remains, on the wider server-owned threshold from
|
|
185
|
+
// useRunStuckDetection.
|
|
186
|
+
const isServerContinuedDispatch =
|
|
187
|
+
state.dispatchMode === "foreground-self-chain" ||
|
|
187
188
|
state.dispatchMode?.startsWith("background") === true;
|
|
188
189
|
|
|
189
190
|
const lastReportedRef = useRef<{
|
|
@@ -224,9 +225,9 @@ export function RunStuckBanner({
|
|
|
224
225
|
useEffect(() => {
|
|
225
226
|
if (
|
|
226
227
|
!autoRetry ||
|
|
227
|
-
// Server owns recovery for
|
|
228
|
-
//
|
|
229
|
-
|
|
228
|
+
// Server owns recovery for these dispatch modes — never auto-abort (see
|
|
229
|
+
// comment on isServerContinuedDispatch above).
|
|
230
|
+
isServerContinuedDispatch ||
|
|
230
231
|
backgroundWorkerStillAlive ||
|
|
231
232
|
!state.isStuck ||
|
|
232
233
|
!state.runId ||
|
|
@@ -263,7 +264,7 @@ export function RunStuckBanner({
|
|
|
263
264
|
autoRetry,
|
|
264
265
|
backgroundWorkerStillAlive,
|
|
265
266
|
busy,
|
|
266
|
-
|
|
267
|
+
isServerContinuedDispatch,
|
|
267
268
|
onRetry,
|
|
268
269
|
ownerId,
|
|
269
270
|
state.isStuck,
|