@agent-native/core 0.72.4 → 0.74.0
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/core/CHANGELOG.md +67 -0
- package/corpus/core/docs/content/skills-guide.md +14 -0
- package/corpus/core/docs/content/template-clips.md +7 -1
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/durable-background.ts +35 -12
- package/corpus/core/src/agent/production-agent.ts +55 -8
- package/corpus/core/src/cli/create.ts +1 -1
- package/corpus/core/src/cli/skills.ts +450 -19
- package/corpus/core/src/client/ConnectBuilderCard.tsx +7 -7
- package/corpus/core/src/client/NewWorkspaceAppFlow.tsx +1 -1
- package/corpus/core/src/client/blocks/library/AnnotatedCodeBlock.tsx +33 -18
- package/corpus/core/src/client/blocks/library/annotation-rail.tsx +3 -4
- package/corpus/core/src/client/components/CodeRequiredDialog.tsx +5 -8
- package/corpus/core/src/server/agent-chat-plugin.ts +7 -7
- package/corpus/core/src/server/auth.ts +7 -0
- package/corpus/core/src/server/onboarding-html.ts +95 -9
- package/corpus/core/src/server/self-dispatch.ts +8 -1
- package/corpus/core/src/templates/default/AGENTS.md +4 -0
- package/corpus/core/src/templates/default/package.json +2 -1
- package/corpus/core/src/templates/headless/AGENTS.md +4 -0
- package/corpus/core/src/templates/headless/package.json +1 -0
- package/corpus/core/src/templates/workspace-root/AGENTS.md +4 -0
- package/corpus/core/src/templates/workspace-root/README.md +4 -0
- package/corpus/core/src/templates/workspace-root/package.json +1 -0
- package/corpus/templates/analytics/actions/compose-dashboard.ts +34 -0
- package/corpus/templates/analytics/actions/install-dashboard-template.ts +51 -3
- package/corpus/templates/analytics/actions/update-dashboard.ts +3 -4
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +175 -48
- package/corpus/templates/analytics/app/global.css +15 -13
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/PanelEditorDialog.tsx +9 -32
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +0 -26
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +0 -23
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/pivot.ts +65 -2
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/types.ts +3 -4
- package/corpus/templates/analytics/changelog/2026-06-24-dashboard-charts-now-fill-sparse-template-time-series-suppor.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-24-dashboard-filters-now-apply-consistently-to-first-party-traf.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-24-dashboard-rows-now-auto-fit-panels-so-one-two-or-three-chart.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-24-dashboard-traffic-tables-now-show-top-urls-and-shared-clips-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-24-first-party-signup-and-pageview-panels-now-honor-dashboard-filters.md +5 -0
- package/corpus/templates/analytics/changelog/2026-06-24-first-party-template-dashboards-now-include-repeat-users-ret.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-24-retention-charts-now-break-out-1-day-and-7-day-return-rates-.md +6 -0
- package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +330 -49
- package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +7 -2
- package/corpus/templates/analytics/server/lib/first-party-analytics.ts +35 -8
- package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +337 -45
- package/corpus/templates/calendar/changelog/2026-06-23-added-a-heads-up-explaining-google-s-app-not-verified-screen.md +1 -1
- package/corpus/templates/calendar/server/plugins/auth.ts +12 -0
- package/corpus/templates/clips/.agents/skills/recording/SKILL.md +7 -7
- package/corpus/templates/clips/AGENTS.md +6 -2
- package/corpus/templates/clips/app/components/capture-install-options.tsx +20 -4
- package/corpus/templates/clips/app/components/library/library-layout.tsx +7 -0
- package/corpus/templates/clips/app/components/player/player-controls.tsx +62 -32
- package/corpus/templates/clips/app/lib/capture-install-options.ts +39 -5
- package/corpus/templates/clips/app/routes/_app.settings._index.tsx +786 -323
- package/corpus/templates/clips/app/routes/download.tsx +2 -2
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +1 -1
- package/corpus/templates/clips/changelog/2026-06-24-inline-slack-playback-controls-no-longer-shift-when-adjustin.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-24-settings-now-puts-what-s-new-in-a-compact-sidebar-prioritize.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-24-shared-clips-now-expose-their-full-console-log-stream-all-le.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-24-shared-clips-now-expose-their-full-fetch-xhr-network-request.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-24-the-chrome-extension-option-now-only-appears-on-supported-hosts.md +5 -0
- package/corpus/templates/clips/chrome-extension/STORE_DRAFT.md +13 -8
- package/corpus/templates/clips/chrome-extension/public/manifest.json +1 -1
- package/corpus/templates/clips/chrome-extension/scripts/package.ts +15 -2
- package/corpus/templates/clips/chrome-extension/src/overlay.css +4 -25
- package/corpus/templates/clips/chrome-extension/src/overlay.ts +1 -3
- package/corpus/templates/clips/server/lib/public-agent-context.ts +67 -24
- package/corpus/templates/mail/server/plugins/auth.ts +4 -1
- package/corpus/templates/plan/app/pages/PlansPage.tsx +271 -197
- package/corpus/templates/plan/changelog/2026-06-24-annotated-code-callouts-now-use-numbered-gutter-markers-that.md +6 -0
- package/corpus/templates/plan/server/plan-content.ts +45 -2
- package/dist/agent/durable-background.d.ts +9 -4
- package/dist/agent/durable-background.d.ts.map +1 -1
- package/dist/agent/durable-background.js +34 -11
- package/dist/agent/durable-background.js.map +1 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +52 -8
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/cli/create.js +1 -1
- package/dist/cli/create.js.map +1 -1
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +361 -19
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/ConnectBuilderCard.d.ts.map +1 -1
- package/dist/client/ConnectBuilderCard.js +6 -6
- package/dist/client/ConnectBuilderCard.js.map +1 -1
- package/dist/client/NewWorkspaceAppFlow.js +1 -1
- package/dist/client/NewWorkspaceAppFlow.js.map +1 -1
- package/dist/client/blocks/library/AnnotatedCodeBlock.d.ts.map +1 -1
- package/dist/client/blocks/library/AnnotatedCodeBlock.js +14 -10
- package/dist/client/blocks/library/AnnotatedCodeBlock.js.map +1 -1
- package/dist/client/blocks/library/annotation-rail.d.ts +3 -4
- package/dist/client/blocks/library/annotation-rail.d.ts.map +1 -1
- package/dist/client/blocks/library/annotation-rail.js +3 -4
- package/dist/client/blocks/library/annotation-rail.js.map +1 -1
- package/dist/client/components/CodeRequiredDialog.d.ts.map +1 -1
- package/dist/client/components/CodeRequiredDialog.js +2 -1
- package/dist/client/components/CodeRequiredDialog.js.map +1 -1
- package/dist/server/agent-chat-plugin.js +7 -7
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/auth.d.ts +7 -0
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +1 -0
- package/dist/server/auth.js.map +1 -1
- package/dist/server/onboarding-html.d.ts +15 -0
- package/dist/server/onboarding-html.d.ts.map +1 -1
- package/dist/server/onboarding-html.js +73 -9
- package/dist/server/onboarding-html.js.map +1 -1
- package/dist/server/self-dispatch.d.ts.map +1 -1
- package/dist/server/self-dispatch.js +5 -1
- package/dist/server/self-dispatch.js.map +1 -1
- package/dist/templates/default/AGENTS.md +4 -0
- package/dist/templates/default/package.json +2 -1
- package/dist/templates/headless/AGENTS.md +4 -0
- package/dist/templates/headless/package.json +1 -0
- package/dist/templates/workspace-root/AGENTS.md +4 -0
- package/dist/templates/workspace-root/README.md +4 -0
- package/dist/templates/workspace-root/package.json +1 -0
- package/docs/content/skills-guide.md +14 -0
- package/docs/content/template-clips.md +7 -1
- package/package.json +1 -1
- package/src/templates/default/AGENTS.md +4 -0
- package/src/templates/default/package.json +2 -1
- package/src/templates/headless/AGENTS.md +4 -0
- package/src/templates/headless/package.json +1 -0
- package/src/templates/workspace-root/AGENTS.md +4 -0
- package/src/templates/workspace-root/README.md +4 -0
- package/src/templates/workspace-root/package.json +1 -0
|
@@ -8369,6 +8369,12 @@ function PlanHistorySheet({
|
|
|
8369
8369
|
const [selectedVersionId, setSelectedVersionId] = useState<string | null>(
|
|
8370
8370
|
null,
|
|
8371
8371
|
);
|
|
8372
|
+
// Version id pending a confirm before restore. Set from the per-row "Restore
|
|
8373
|
+
// this version" action in the list so restore is reachable without first
|
|
8374
|
+
// opening the detail preview.
|
|
8375
|
+
const [restoreCandidateId, setRestoreCandidateId] = useState<string | null>(
|
|
8376
|
+
null,
|
|
8377
|
+
);
|
|
8372
8378
|
const versionsQuery = usePlanVersions(planId, open);
|
|
8373
8379
|
const versionQuery = usePlanVersion(open ? planId : null, selectedVersionId);
|
|
8374
8380
|
const restoreVersion = useRestorePlanVersion();
|
|
@@ -8401,14 +8407,12 @@ function PlanHistorySheet({
|
|
|
8401
8407
|
onOpenChange(nextOpen);
|
|
8402
8408
|
};
|
|
8403
8409
|
|
|
8404
|
-
const
|
|
8405
|
-
if (!
|
|
8410
|
+
const restoreVersionById = async (versionId: string | null) => {
|
|
8411
|
+
if (!versionId) return;
|
|
8406
8412
|
try {
|
|
8407
|
-
await restoreVersion.mutateAsync({
|
|
8408
|
-
planId,
|
|
8409
|
-
versionId: selectedVersionId,
|
|
8410
|
-
});
|
|
8413
|
+
await restoreVersion.mutateAsync({ planId, versionId });
|
|
8411
8414
|
toast.success("Plan version restored.");
|
|
8415
|
+
setRestoreCandidateId(null);
|
|
8412
8416
|
close(false);
|
|
8413
8417
|
} catch (error) {
|
|
8414
8418
|
toast.error(
|
|
@@ -8418,209 +8422,279 @@ function PlanHistorySheet({
|
|
|
8418
8422
|
);
|
|
8419
8423
|
}
|
|
8420
8424
|
};
|
|
8425
|
+
const restoreCandidate =
|
|
8426
|
+
versions.find((version) => version.id === restoreCandidateId) ?? null;
|
|
8421
8427
|
|
|
8422
8428
|
return (
|
|
8423
|
-
|
|
8424
|
-
<
|
|
8425
|
-
<
|
|
8426
|
-
<
|
|
8427
|
-
|
|
8428
|
-
|
|
8429
|
-
|
|
8430
|
-
|
|
8431
|
-
|
|
8432
|
-
|
|
8433
|
-
|
|
8434
|
-
|
|
8435
|
-
|
|
8436
|
-
|
|
8437
|
-
<>
|
|
8438
|
-
<IconHistory className="size-4 text-primary" />
|
|
8439
|
-
<span>Plan history</span>
|
|
8440
|
-
</>
|
|
8441
|
-
)}
|
|
8442
|
-
</SheetTitle>
|
|
8443
|
-
<SheetDescription className="sr-only">
|
|
8444
|
-
Browse saved plan versions and restore a previous snapshot.
|
|
8445
|
-
</SheetDescription>
|
|
8446
|
-
</SheetHeader>
|
|
8447
|
-
<Separator className="mt-3" />
|
|
8448
|
-
|
|
8449
|
-
{selectedVersionId ? (
|
|
8450
|
-
<div className="flex h-[calc(100%-60px)] min-h-0 flex-col">
|
|
8451
|
-
<div className="border-b border-border px-4 py-3">
|
|
8452
|
-
{versionQuery.isLoading ? (
|
|
8453
|
-
<div className="space-y-2">
|
|
8454
|
-
<Skeleton className="h-4 w-2/3" />
|
|
8455
|
-
<Skeleton className="h-3 w-1/3" />
|
|
8456
|
-
</div>
|
|
8429
|
+
<>
|
|
8430
|
+
<Sheet open={open} onOpenChange={close}>
|
|
8431
|
+
<SheetContent side="right" className="w-[92vw] max-w-[720px] p-0">
|
|
8432
|
+
<SheetHeader className="px-4 pt-4 pb-0">
|
|
8433
|
+
<SheetTitle className="flex min-w-0 items-center gap-2 text-sm font-medium">
|
|
8434
|
+
{selectedVersionId ? (
|
|
8435
|
+
<button
|
|
8436
|
+
type="button"
|
|
8437
|
+
onClick={() => setSelectedVersionId(null)}
|
|
8438
|
+
className="inline-flex min-w-0 items-center gap-1.5 text-muted-foreground transition-colors hover:text-foreground"
|
|
8439
|
+
>
|
|
8440
|
+
<IconArrowLeft className="size-4" />
|
|
8441
|
+
<span>Back to history</span>
|
|
8442
|
+
</button>
|
|
8457
8443
|
) : (
|
|
8458
8444
|
<>
|
|
8459
|
-
<
|
|
8460
|
-
|
|
8461
|
-
</p>
|
|
8462
|
-
<p className="mt-0.5 text-[11px] text-muted-foreground">
|
|
8463
|
-
{selectedVersion
|
|
8464
|
-
? `${shortDate(selectedVersion.createdAt)} · ${planVersionSurfaceLabel(selectedVersion)}`
|
|
8465
|
-
: "Snapshot unavailable"}
|
|
8466
|
-
</p>
|
|
8445
|
+
<IconHistory className="size-4 text-primary" />
|
|
8446
|
+
<span>Plan history</span>
|
|
8467
8447
|
</>
|
|
8468
8448
|
)}
|
|
8449
|
+
</SheetTitle>
|
|
8450
|
+
<SheetDescription className="sr-only">
|
|
8451
|
+
Browse saved plan versions and restore a previous snapshot.
|
|
8452
|
+
</SheetDescription>
|
|
8453
|
+
</SheetHeader>
|
|
8454
|
+
<Separator className="mt-3" />
|
|
8455
|
+
|
|
8456
|
+
{selectedVersionId ? (
|
|
8457
|
+
<div className="flex h-[calc(100%-60px)] min-h-0 flex-col">
|
|
8458
|
+
<div className="border-b border-border px-4 py-3">
|
|
8459
|
+
{versionQuery.isLoading ? (
|
|
8460
|
+
<div className="space-y-2">
|
|
8461
|
+
<Skeleton className="h-4 w-2/3" />
|
|
8462
|
+
<Skeleton className="h-3 w-1/3" />
|
|
8463
|
+
</div>
|
|
8464
|
+
) : (
|
|
8465
|
+
<>
|
|
8466
|
+
<p className="truncate text-sm font-medium">
|
|
8467
|
+
{selectedVersion?.title || "Untitled plan"}
|
|
8468
|
+
</p>
|
|
8469
|
+
<p className="mt-0.5 text-[11px] text-muted-foreground">
|
|
8470
|
+
{selectedVersion
|
|
8471
|
+
? `${shortDate(selectedVersion.createdAt)} · ${planVersionSurfaceLabel(selectedVersion)}`
|
|
8472
|
+
: "Snapshot unavailable"}
|
|
8473
|
+
</p>
|
|
8474
|
+
</>
|
|
8475
|
+
)}
|
|
8476
|
+
</div>
|
|
8477
|
+
<ScrollArea className="min-h-0 flex-1 bg-plan-document">
|
|
8478
|
+
{versionQuery.isLoading ? (
|
|
8479
|
+
<div className="space-y-3 p-4">
|
|
8480
|
+
<Skeleton className="h-48 w-full rounded-lg" />
|
|
8481
|
+
<Skeleton className="h-28 w-full rounded-lg" />
|
|
8482
|
+
<Skeleton className="h-28 w-full rounded-lg" />
|
|
8483
|
+
</div>
|
|
8484
|
+
) : selectedVersion?.plan.content ? (
|
|
8485
|
+
<PlanContentRenderer
|
|
8486
|
+
content={selectedVersion.plan.content}
|
|
8487
|
+
fallbackTitle={selectedVersion.plan.title}
|
|
8488
|
+
fallbackBrief={selectedVersion.plan.brief}
|
|
8489
|
+
contentUpdatedAt={selectedVersion.plan.updatedAt}
|
|
8490
|
+
editingDisabled
|
|
8491
|
+
isRecap={selectedVersion.plan.kind === "recap"}
|
|
8492
|
+
planId={null}
|
|
8493
|
+
/>
|
|
8494
|
+
) : selectedVersion?.html ? (
|
|
8495
|
+
<iframe
|
|
8496
|
+
title="Plan version preview"
|
|
8497
|
+
srcDoc={selectedVersion.html}
|
|
8498
|
+
// Stored plan HTML is agent-authored and may carry
|
|
8499
|
+
// prompt-injected markup. Match the main document iframe
|
|
8500
|
+
// (search "allow-forms allow-scripts"): run scripts only in
|
|
8501
|
+
// an opaque origin — never allow-same-origin — so a malicious
|
|
8502
|
+
// snapshot cannot reach the app origin's cookies, DOM, or
|
|
8503
|
+
// actions.
|
|
8504
|
+
sandbox="allow-forms allow-scripts"
|
|
8505
|
+
className="h-[calc(100vh-142px)] w-full border-0 bg-background"
|
|
8506
|
+
/>
|
|
8507
|
+
) : (
|
|
8508
|
+
<div className="px-6 py-14 text-center text-sm text-muted-foreground">
|
|
8509
|
+
This snapshot has no previewable content.
|
|
8510
|
+
</div>
|
|
8511
|
+
)}
|
|
8512
|
+
</ScrollArea>
|
|
8513
|
+
{canRestore ? (
|
|
8514
|
+
<div className="border-t border-border p-3">
|
|
8515
|
+
<Button
|
|
8516
|
+
type="button"
|
|
8517
|
+
size="sm"
|
|
8518
|
+
className="w-full gap-1.5"
|
|
8519
|
+
onClick={() => void restoreVersionById(selectedVersionId)}
|
|
8520
|
+
disabled={
|
|
8521
|
+
restoreVersion.isPending || versionQuery.isLoading
|
|
8522
|
+
}
|
|
8523
|
+
>
|
|
8524
|
+
{restoreVersion.isPending ? (
|
|
8525
|
+
<IconLoader2 className="size-4 animate-spin" />
|
|
8526
|
+
) : (
|
|
8527
|
+
<IconRestore className="size-4" />
|
|
8528
|
+
)}
|
|
8529
|
+
Restore this version
|
|
8530
|
+
</Button>
|
|
8531
|
+
<p className="mt-2 text-center text-[11px] text-muted-foreground">
|
|
8532
|
+
Your current version is saved to history first.
|
|
8533
|
+
</p>
|
|
8534
|
+
</div>
|
|
8535
|
+
) : null}
|
|
8469
8536
|
</div>
|
|
8470
|
-
|
|
8471
|
-
|
|
8472
|
-
|
|
8473
|
-
|
|
8474
|
-
|
|
8475
|
-
|
|
8537
|
+
) : (
|
|
8538
|
+
<ScrollArea className="h-[calc(100%-60px)]">
|
|
8539
|
+
{versionsQuery.isLoading ? (
|
|
8540
|
+
<div className="space-y-2 p-3">
|
|
8541
|
+
{Array.from({ length: 5 }).map((_, index) => (
|
|
8542
|
+
<Skeleton key={index} className="h-20 w-full rounded-lg" />
|
|
8543
|
+
))}
|
|
8544
|
+
</div>
|
|
8545
|
+
) : versions.length ? (
|
|
8546
|
+
<div className="p-2">
|
|
8547
|
+
{versions.map((version, index) => {
|
|
8548
|
+
// Compute a diff summary when both this version and its
|
|
8549
|
+
// predecessor have been loaded into the cache. Versions are
|
|
8550
|
+
// ordered newest-first, so index+1 is the older snapshot.
|
|
8551
|
+
// The oldest entry (no predecessor) shows "Initial version".
|
|
8552
|
+
const cache = versionDetailCache.current;
|
|
8553
|
+
const thisDetail = cache.get(version.id);
|
|
8554
|
+
const olderVersion = versions[index + 1];
|
|
8555
|
+
const olderDetail = olderVersion
|
|
8556
|
+
? cache.get(olderVersion.id)
|
|
8557
|
+
: undefined;
|
|
8558
|
+
// Show a diff when: this version's detail is loaded AND
|
|
8559
|
+
// (it's the oldest OR the older neighbour's detail is loaded).
|
|
8560
|
+
const isOldest = index === versions.length - 1;
|
|
8561
|
+
const diffSummary =
|
|
8562
|
+
thisDetail && (isOldest || olderDetail)
|
|
8563
|
+
? formatVersionDiffSummary(
|
|
8564
|
+
diffPlanVersions(
|
|
8565
|
+
{
|
|
8566
|
+
content: thisDetail.plan.content,
|
|
8567
|
+
sections: thisDetail.sections,
|
|
8568
|
+
html: thisDetail.html,
|
|
8569
|
+
},
|
|
8570
|
+
isOldest
|
|
8571
|
+
? null
|
|
8572
|
+
: {
|
|
8573
|
+
content: olderDetail!.plan.content,
|
|
8574
|
+
sections: olderDetail!.sections,
|
|
8575
|
+
html: olderDetail!.html,
|
|
8576
|
+
},
|
|
8577
|
+
),
|
|
8578
|
+
)
|
|
8579
|
+
: null;
|
|
8580
|
+
|
|
8581
|
+
return (
|
|
8582
|
+
<div
|
|
8583
|
+
key={version.id}
|
|
8584
|
+
className="group relative rounded-lg transition-colors hover:bg-accent"
|
|
8585
|
+
>
|
|
8586
|
+
<button
|
|
8587
|
+
type="button"
|
|
8588
|
+
onClick={() => setSelectedVersionId(version.id)}
|
|
8589
|
+
className="w-full rounded-lg px-3 py-2.5 text-left"
|
|
8590
|
+
>
|
|
8591
|
+
<div className="flex min-w-0 items-start gap-3">
|
|
8592
|
+
<div className="mt-1 flex size-7 shrink-0 items-center justify-center rounded-md border border-border bg-muted/45">
|
|
8593
|
+
<IconHistory className="size-4 text-muted-foreground" />
|
|
8594
|
+
</div>
|
|
8595
|
+
<div className="min-w-0 flex-1">
|
|
8596
|
+
<div className="flex min-w-0 items-center gap-2 pr-7">
|
|
8597
|
+
<p className="truncate text-sm font-medium">
|
|
8598
|
+
{version.title || "Untitled plan"}
|
|
8599
|
+
</p>
|
|
8600
|
+
<span className="shrink-0 text-[10px] text-muted-foreground">
|
|
8601
|
+
{planVersionSurfaceLabel(version)}
|
|
8602
|
+
</span>
|
|
8603
|
+
</div>
|
|
8604
|
+
<p className="mt-0.5 text-[11px] text-muted-foreground">
|
|
8605
|
+
{shortDate(version.createdAt)}
|
|
8606
|
+
{version.label ? ` · ${version.label}` : ""}
|
|
8607
|
+
</p>
|
|
8608
|
+
{diffSummary ? (
|
|
8609
|
+
<p className="mt-0.5 truncate text-[11px] text-muted-foreground/70">
|
|
8610
|
+
{diffSummary}
|
|
8611
|
+
</p>
|
|
8612
|
+
) : version.preview ? (
|
|
8613
|
+
<p className="mt-1 line-clamp-2 text-[11px] leading-4 text-muted-foreground/80">
|
|
8614
|
+
{version.preview}
|
|
8615
|
+
</p>
|
|
8616
|
+
) : null}
|
|
8617
|
+
</div>
|
|
8618
|
+
</div>
|
|
8619
|
+
</button>
|
|
8620
|
+
{canRestore ? (
|
|
8621
|
+
<div className="absolute right-2 top-2 opacity-0 transition-opacity group-hover:opacity-100 group-focus-within:opacity-100">
|
|
8622
|
+
<DropdownMenu>
|
|
8623
|
+
<DropdownMenuTrigger asChild>
|
|
8624
|
+
<button
|
|
8625
|
+
type="button"
|
|
8626
|
+
className="flex size-7 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
8627
|
+
aria-label="Version actions"
|
|
8628
|
+
>
|
|
8629
|
+
<IconDots className="size-4" />
|
|
8630
|
+
</button>
|
|
8631
|
+
</DropdownMenuTrigger>
|
|
8632
|
+
<DropdownMenuContent align="end" className="w-52">
|
|
8633
|
+
<DropdownMenuItem
|
|
8634
|
+
onClick={(event) => {
|
|
8635
|
+
event.preventDefault();
|
|
8636
|
+
setRestoreCandidateId(version.id);
|
|
8637
|
+
}}
|
|
8638
|
+
>
|
|
8639
|
+
<IconRestore className="size-4" />
|
|
8640
|
+
Restore this version
|
|
8641
|
+
</DropdownMenuItem>
|
|
8642
|
+
</DropdownMenuContent>
|
|
8643
|
+
</DropdownMenu>
|
|
8644
|
+
</div>
|
|
8645
|
+
) : null}
|
|
8646
|
+
</div>
|
|
8647
|
+
);
|
|
8648
|
+
})}
|
|
8476
8649
|
</div>
|
|
8477
|
-
) : selectedVersion?.plan.content ? (
|
|
8478
|
-
<PlanContentRenderer
|
|
8479
|
-
content={selectedVersion.plan.content}
|
|
8480
|
-
fallbackTitle={selectedVersion.plan.title}
|
|
8481
|
-
fallbackBrief={selectedVersion.plan.brief}
|
|
8482
|
-
contentUpdatedAt={selectedVersion.plan.updatedAt}
|
|
8483
|
-
editingDisabled
|
|
8484
|
-
isRecap={selectedVersion.plan.kind === "recap"}
|
|
8485
|
-
planId={null}
|
|
8486
|
-
/>
|
|
8487
|
-
) : selectedVersion?.html ? (
|
|
8488
|
-
<iframe
|
|
8489
|
-
title="Plan version preview"
|
|
8490
|
-
srcDoc={selectedVersion.html}
|
|
8491
|
-
// Stored plan HTML is agent-authored and may carry
|
|
8492
|
-
// prompt-injected markup. Match the main document iframe
|
|
8493
|
-
// (search "allow-forms allow-scripts"): run scripts only in
|
|
8494
|
-
// an opaque origin — never allow-same-origin — so a malicious
|
|
8495
|
-
// snapshot cannot reach the app origin's cookies, DOM, or
|
|
8496
|
-
// actions.
|
|
8497
|
-
sandbox="allow-forms allow-scripts"
|
|
8498
|
-
className="h-[calc(100vh-142px)] w-full border-0 bg-background"
|
|
8499
|
-
/>
|
|
8500
8650
|
) : (
|
|
8501
|
-
<div className="px-6 py-14 text-center
|
|
8502
|
-
|
|
8651
|
+
<div className="px-6 py-14 text-center">
|
|
8652
|
+
<IconHistory className="mx-auto mb-3 size-6 text-muted-foreground/60" />
|
|
8653
|
+
<p className="text-sm font-medium">No saved versions yet</p>
|
|
8654
|
+
<p className="mt-1 text-xs leading-5 text-muted-foreground">
|
|
8655
|
+
Versions are saved automatically before future plan edits.
|
|
8656
|
+
</p>
|
|
8503
8657
|
</div>
|
|
8504
8658
|
)}
|
|
8505
8659
|
</ScrollArea>
|
|
8506
|
-
|
|
8507
|
-
|
|
8508
|
-
|
|
8509
|
-
|
|
8510
|
-
|
|
8511
|
-
|
|
8512
|
-
|
|
8513
|
-
|
|
8514
|
-
|
|
8515
|
-
|
|
8516
|
-
|
|
8517
|
-
|
|
8518
|
-
|
|
8519
|
-
|
|
8520
|
-
|
|
8521
|
-
|
|
8522
|
-
|
|
8523
|
-
|
|
8524
|
-
|
|
8525
|
-
|
|
8526
|
-
|
|
8527
|
-
|
|
8528
|
-
|
|
8529
|
-
|
|
8530
|
-
|
|
8531
|
-
|
|
8532
|
-
|
|
8533
|
-
|
|
8534
|
-
)
|
|
8535
|
-
|
|
8536
|
-
|
|
8537
|
-
|
|
8538
|
-
|
|
8539
|
-
|
|
8540
|
-
|
|
8541
|
-
|
|
8542
|
-
|
|
8543
|
-
|
|
8544
|
-
const thisDetail = cache.get(version.id);
|
|
8545
|
-
const olderVersion = versions[index + 1];
|
|
8546
|
-
const olderDetail = olderVersion
|
|
8547
|
-
? cache.get(olderVersion.id)
|
|
8548
|
-
: undefined;
|
|
8549
|
-
// Show a diff when: this version's detail is loaded AND
|
|
8550
|
-
// (it's the oldest OR the older neighbour's detail is loaded).
|
|
8551
|
-
const isOldest = index === versions.length - 1;
|
|
8552
|
-
const diffSummary =
|
|
8553
|
-
thisDetail && (isOldest || olderDetail)
|
|
8554
|
-
? formatVersionDiffSummary(
|
|
8555
|
-
diffPlanVersions(
|
|
8556
|
-
{
|
|
8557
|
-
content: thisDetail.plan.content,
|
|
8558
|
-
sections: thisDetail.sections,
|
|
8559
|
-
html: thisDetail.html,
|
|
8560
|
-
},
|
|
8561
|
-
isOldest
|
|
8562
|
-
? null
|
|
8563
|
-
: {
|
|
8564
|
-
content: olderDetail!.plan.content,
|
|
8565
|
-
sections: olderDetail!.sections,
|
|
8566
|
-
html: olderDetail!.html,
|
|
8567
|
-
},
|
|
8568
|
-
),
|
|
8569
|
-
)
|
|
8570
|
-
: null;
|
|
8571
|
-
|
|
8572
|
-
return (
|
|
8573
|
-
<button
|
|
8574
|
-
key={version.id}
|
|
8575
|
-
type="button"
|
|
8576
|
-
onClick={() => setSelectedVersionId(version.id)}
|
|
8577
|
-
className="w-full rounded-lg px-3 py-2.5 text-left transition-colors hover:bg-accent"
|
|
8578
|
-
>
|
|
8579
|
-
<div className="flex min-w-0 items-start gap-3">
|
|
8580
|
-
<div className="mt-1 flex size-7 shrink-0 items-center justify-center rounded-md border border-border bg-muted/45">
|
|
8581
|
-
<IconHistory className="size-4 text-muted-foreground" />
|
|
8582
|
-
</div>
|
|
8583
|
-
<div className="min-w-0 flex-1">
|
|
8584
|
-
<div className="flex min-w-0 items-center gap-2">
|
|
8585
|
-
<p className="truncate text-sm font-medium">
|
|
8586
|
-
{version.title || "Untitled plan"}
|
|
8587
|
-
</p>
|
|
8588
|
-
<span className="shrink-0 text-[10px] text-muted-foreground">
|
|
8589
|
-
{planVersionSurfaceLabel(version)}
|
|
8590
|
-
</span>
|
|
8591
|
-
</div>
|
|
8592
|
-
<p className="mt-0.5 text-[11px] text-muted-foreground">
|
|
8593
|
-
{shortDate(version.createdAt)}
|
|
8594
|
-
{version.label ? ` · ${version.label}` : ""}
|
|
8595
|
-
</p>
|
|
8596
|
-
{diffSummary ? (
|
|
8597
|
-
<p className="mt-0.5 truncate text-[11px] text-muted-foreground/70">
|
|
8598
|
-
{diffSummary}
|
|
8599
|
-
</p>
|
|
8600
|
-
) : version.preview ? (
|
|
8601
|
-
<p className="mt-1 line-clamp-2 text-[11px] leading-4 text-muted-foreground/80">
|
|
8602
|
-
{version.preview}
|
|
8603
|
-
</p>
|
|
8604
|
-
) : null}
|
|
8605
|
-
</div>
|
|
8606
|
-
</div>
|
|
8607
|
-
</button>
|
|
8608
|
-
);
|
|
8609
|
-
})}
|
|
8610
|
-
</div>
|
|
8611
|
-
) : (
|
|
8612
|
-
<div className="px-6 py-14 text-center">
|
|
8613
|
-
<IconHistory className="mx-auto mb-3 size-6 text-muted-foreground/60" />
|
|
8614
|
-
<p className="text-sm font-medium">No saved versions yet</p>
|
|
8615
|
-
<p className="mt-1 text-xs leading-5 text-muted-foreground">
|
|
8616
|
-
Versions are saved automatically before future plan edits.
|
|
8617
|
-
</p>
|
|
8618
|
-
</div>
|
|
8619
|
-
)}
|
|
8620
|
-
</ScrollArea>
|
|
8621
|
-
)}
|
|
8622
|
-
</SheetContent>
|
|
8623
|
-
</Sheet>
|
|
8660
|
+
)}
|
|
8661
|
+
</SheetContent>
|
|
8662
|
+
</Sheet>
|
|
8663
|
+
<AlertDialog
|
|
8664
|
+
open={Boolean(restoreCandidateId)}
|
|
8665
|
+
onOpenChange={(next) => {
|
|
8666
|
+
if (!next) setRestoreCandidateId(null);
|
|
8667
|
+
}}
|
|
8668
|
+
>
|
|
8669
|
+
<AlertDialogContent>
|
|
8670
|
+
<AlertDialogHeader>
|
|
8671
|
+
<AlertDialogTitle>Restore this version?</AlertDialogTitle>
|
|
8672
|
+
<AlertDialogDescription>
|
|
8673
|
+
This replaces the current plan with the snapshot
|
|
8674
|
+
{restoreCandidate
|
|
8675
|
+
? ` from ${shortDate(restoreCandidate.createdAt)}`
|
|
8676
|
+
: ""}
|
|
8677
|
+
. Your current version is saved to history first, so you can undo
|
|
8678
|
+
this.
|
|
8679
|
+
</AlertDialogDescription>
|
|
8680
|
+
</AlertDialogHeader>
|
|
8681
|
+
<AlertDialogFooter>
|
|
8682
|
+
<AlertDialogCancel disabled={restoreVersion.isPending}>
|
|
8683
|
+
Cancel
|
|
8684
|
+
</AlertDialogCancel>
|
|
8685
|
+
<AlertDialogAction
|
|
8686
|
+
onClick={(event) => {
|
|
8687
|
+
event.preventDefault();
|
|
8688
|
+
void restoreVersionById(restoreCandidateId);
|
|
8689
|
+
}}
|
|
8690
|
+
disabled={restoreVersion.isPending}
|
|
8691
|
+
>
|
|
8692
|
+
{restoreVersion.isPending ? "Restoring…" : "Restore"}
|
|
8693
|
+
</AlertDialogAction>
|
|
8694
|
+
</AlertDialogFooter>
|
|
8695
|
+
</AlertDialogContent>
|
|
8696
|
+
</AlertDialog>
|
|
8697
|
+
</>
|
|
8624
8698
|
);
|
|
8625
8699
|
}
|
|
8626
8700
|
|
|
@@ -424,6 +424,17 @@ function sanitizeMaybeBlocks(blocks: unknown) {
|
|
|
424
424
|
sanitizeMaybeBlocks((tab as Record<string, unknown>).blocks);
|
|
425
425
|
}
|
|
426
426
|
}
|
|
427
|
+
// `columns` is the recommended before/after recap primitive and nests child
|
|
428
|
+
// blocks (commonly wireframes). It must recurse like `tabs` does — otherwise
|
|
429
|
+
// a nested wireframe authored as a full HTML document never gets its
|
|
430
|
+
// scaffold stripped and the whole columns block degrades to an "Unsupported
|
|
431
|
+
// block" card at validation time.
|
|
432
|
+
if (record.type === "columns" && data && Array.isArray(data.columns)) {
|
|
433
|
+
for (const column of data.columns) {
|
|
434
|
+
if (!column || typeof column !== "object") continue;
|
|
435
|
+
sanitizeMaybeBlocks((column as Record<string, unknown>).blocks);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
427
438
|
}
|
|
428
439
|
}
|
|
429
440
|
|
|
@@ -454,9 +465,29 @@ function preSanitizePlanContentInput(input: unknown): unknown {
|
|
|
454
465
|
return content;
|
|
455
466
|
}
|
|
456
467
|
|
|
468
|
+
/**
|
|
469
|
+
* Coerce a full HTML document into a bounded fragment. Wireframe, custom-html,
|
|
470
|
+
* and diagram blocks must be bounded fragments (the renderer owns the
|
|
471
|
+
* surrounding document, theme, and styling), so the schema rejects any value
|
|
472
|
+
* carrying document scaffolding. Agents frequently author one of these blocks
|
|
473
|
+
* as a standalone page anyway; rather than degrade the whole block to an
|
|
474
|
+
* "Unsupported block" card, drop the scaffold and keep the body content. The
|
|
475
|
+
* `<head>` is removed wholesale because its `<style>`/`<meta>`/`<link>` are
|
|
476
|
+
* renderer-owned and stripped elsewhere regardless. Fragments without
|
|
477
|
+
* scaffolding pass through untouched.
|
|
478
|
+
*/
|
|
479
|
+
function stripDocumentScaffold(value: string): string {
|
|
480
|
+
if (!/<!doctype|<\s*\/?\s*(?:html|head|body)\b/i.test(value)) return value;
|
|
481
|
+
return value
|
|
482
|
+
.replace(/<!doctype[^>]*>/gi, "")
|
|
483
|
+
.replace(/<head\b[^>]*>[\s\S]*?<\/\s*head\s*>/gi, "")
|
|
484
|
+
.replace(/<\/?\s*(?:html|head|body)\b[^>]*>/gi, "")
|
|
485
|
+
.trim();
|
|
486
|
+
}
|
|
487
|
+
|
|
457
488
|
/** Strip the dangerous surface from a stored custom-html / css string. */
|
|
458
489
|
export function sanitizeCustomHtml(value: string): string {
|
|
459
|
-
let out = value;
|
|
490
|
+
let out = stripDocumentScaffold(value);
|
|
460
491
|
// Iterate element-stripping so nested / sequential cases collapse fully.
|
|
461
492
|
for (let i = 0; i < 4; i += 1) {
|
|
462
493
|
const next = out.replace(FORBIDDEN_ELEMENT, "");
|
|
@@ -479,7 +510,7 @@ export function sanitizeCustomHtml(value: string): string {
|
|
|
479
510
|
}
|
|
480
511
|
|
|
481
512
|
export function sanitizeDiagramHtml(value: string): string {
|
|
482
|
-
let out = value;
|
|
513
|
+
let out = stripDocumentScaffold(value);
|
|
483
514
|
for (let i = 0; i < 4; i += 1) {
|
|
484
515
|
const next = out.replace(DIAGRAM_FORBIDDEN_ELEMENT, "");
|
|
485
516
|
if (next === out) break;
|
|
@@ -615,6 +646,18 @@ function sanitizeBlock(block: PlanBlock): PlanBlock {
|
|
|
615
646
|
},
|
|
616
647
|
};
|
|
617
648
|
}
|
|
649
|
+
if (block.type === "columns") {
|
|
650
|
+
return {
|
|
651
|
+
...block,
|
|
652
|
+
data: {
|
|
653
|
+
...block.data,
|
|
654
|
+
columns: block.data.columns.map((column) => ({
|
|
655
|
+
...column,
|
|
656
|
+
blocks: column.blocks.map(sanitizeBlock),
|
|
657
|
+
})),
|
|
658
|
+
},
|
|
659
|
+
};
|
|
660
|
+
}
|
|
618
661
|
return block;
|
|
619
662
|
}
|
|
620
663
|
|
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
* function, so it inherits the 15-min budget.
|
|
5
5
|
*/
|
|
6
6
|
export declare const AGENT_CHAT_PROCESS_RUN_PATH = "/_agent-native/agent-chat/_process-run";
|
|
7
|
-
/**
|
|
7
|
+
/**
|
|
8
|
+
* Env flag for durable background runs. DEFAULT-ON: unset means enabled; an app
|
|
9
|
+
* opts OUT with an explicit falsy value (`false`/`0`/`no`/`off`).
|
|
10
|
+
*/
|
|
8
11
|
export declare const AGENT_CHAT_DURABLE_BACKGROUND_ENV = "AGENT_CHAT_DURABLE_BACKGROUND";
|
|
9
12
|
/**
|
|
10
13
|
* Body field the foreground handler injects when self-dispatching to the
|
|
@@ -21,9 +24,11 @@ export declare const AGENT_CHAT_BACKGROUND_RUN_FIELD = "__backgroundRun";
|
|
|
21
24
|
*/
|
|
22
25
|
export declare function isHostedRuntimeForDurableBackground(): boolean;
|
|
23
26
|
/**
|
|
24
|
-
* The single gate. True
|
|
25
|
-
* A2A_SECRET is configured. False otherwise — and
|
|
26
|
-
* synchronous behavior is used, unchanged.
|
|
27
|
+
* The single gate. True when the flag is not explicitly disabled (default-on)
|
|
28
|
+
* AND the runtime is hosted AND A2A_SECRET is configured. False otherwise — and
|
|
29
|
+
* false means the current synchronous behavior is used, unchanged. So a local /
|
|
30
|
+
* non-hosted / unconfigured app stays synchronous even with the flag defaulting
|
|
31
|
+
* on; durable only engages where the runtime actually supports it.
|
|
27
32
|
*/
|
|
28
33
|
export declare function isAgentChatDurableBackgroundEnabled(): boolean;
|
|
29
34
|
/** Decision returned by `prepareProcessRunRequest`. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"durable-background.d.ts","sourceRoot":"","sources":["../../src/agent/durable-background.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"durable-background.d.ts","sourceRoot":"","sources":["../../src/agent/durable-background.ts"],"names":[],"mappings":"AA6CA;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,2CACE,CAAC;AAE3C;;;GAGG;AACH,eAAO,MAAM,iCAAiC,kCACb,CAAC;AAElC;;;;;;GAMG;AACH,eAAO,MAAM,+BAA+B,oBAAoB,CAAC;AAEjE;;;;GAIG;AACH,wBAAgB,mCAAmC,IAAI,OAAO,CAsB7D;AAyBD;;;;;;GAMG;AACH,wBAAgB,mCAAmC,IAAI,OAAO,CAM7D;AAED,uDAAuD;AACvD,MAAM,MAAM,qBAAqB,GAC7B;IACE,EAAE,EAAE,IAAI,CAAC;IACT,+DAA+D;IAC/D,KAAK,EAAE,MAAM,CAAC;IACd,4EAA4E;IAC5E,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B,GACD;IACE,EAAE,EAAE,KAAK,CAAC;IACV,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,qBAAqB;IACrB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEN;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,OAAO,EACb,UAAU,EAAE,MAAM,GAAG,SAAS,GAC7B,qBAAqB,CA0CvB"}
|