@agent-native/core 0.172.1 → 0.172.3
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/calendar/changelog/2026-08-24-overlapping-events-use-space-better.md +1 -0
- package/corpus/templates/clips/actions/lib/finalize-ended-meetings.ts +37 -0
- package/corpus/templates/clips/actions/request-transcript.ts +4 -0
- package/corpus/templates/clips/actions/save-browser-transcript.ts +4 -0
- package/corpus/templates/clips/desktop/src/app.tsx +436 -365
- package/corpus/templates/clips/desktop/src/components/ReadinessPanel.tsx +17 -2
- package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +21 -2
- package/corpus/templates/clips/desktop/src/overlays/meeting-notification.tsx +1 -0
- package/corpus/templates/clips/desktop/src/styles.css +17 -3
- package/corpus/templates/clips/desktop/src-tauri/src/adhoc_meetings_watcher.rs +114 -20
- package/corpus/templates/clips/desktop/src-tauri/src/meetings_watcher.rs +130 -15
- package/corpus/templates/clips/desktop/src-tauri/src/permission_status.rs +3 -4
- package/corpus/templates/content/actions/_database-row-mutation.ts +22 -2
- package/dist/agent/production-agent.js +18 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/observability/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/vite/client.js +1 -0
- package/package.json +1 -1
|
@@ -1177,6 +1177,8 @@ export function App({
|
|
|
1177
1177
|
const [meetings, setMeetings] = useState<PopoverMeeting[]>([]);
|
|
1178
1178
|
const [meetingsLoading, setMeetingsLoading] = useState(false);
|
|
1179
1179
|
const [meetingsError, setMeetingsError] = useState<string | null>(null);
|
|
1180
|
+
const [meetingsCalendarNeedsReauth, setMeetingsCalendarNeedsReauth] =
|
|
1181
|
+
useState(false);
|
|
1180
1182
|
const [meetingStartMessage, setMeetingStartMessage] = useState<string | null>(
|
|
1181
1183
|
null,
|
|
1182
1184
|
);
|
|
@@ -1974,17 +1976,29 @@ export function App({
|
|
|
1974
1976
|
if (authStatus !== "authed") {
|
|
1975
1977
|
setMeetings([]);
|
|
1976
1978
|
setMeetingsError(null);
|
|
1979
|
+
setMeetingsCalendarNeedsReauth(false);
|
|
1977
1980
|
return;
|
|
1978
1981
|
}
|
|
1979
1982
|
|
|
1980
1983
|
setMeetingsLoading(true);
|
|
1981
1984
|
setMeetingsError(null);
|
|
1982
1985
|
try {
|
|
1983
|
-
const result = await callClipsAction<{
|
|
1986
|
+
const result = await callClipsAction<{
|
|
1987
|
+
meetings?: unknown[];
|
|
1988
|
+
calendarErrors?: Array<{ needsReauth?: boolean }>;
|
|
1989
|
+
}>(
|
|
1984
1990
|
"list-meetings",
|
|
1985
|
-
{
|
|
1991
|
+
{
|
|
1992
|
+
view: "upcoming",
|
|
1993
|
+
limit: 3,
|
|
1994
|
+
upcomingWithinMin: 24 * 60,
|
|
1995
|
+
},
|
|
1986
1996
|
{ method: "GET" },
|
|
1987
1997
|
);
|
|
1998
|
+
setMeetingsCalendarNeedsReauth(
|
|
1999
|
+
result.calendarErrors?.some((error) => error.needsReauth === true) ===
|
|
2000
|
+
true,
|
|
2001
|
+
);
|
|
1988
2002
|
const list = Array.isArray(result.meetings) ? result.meetings : [];
|
|
1989
2003
|
setMeetings(
|
|
1990
2004
|
list.slice(0, 3).map((raw) => {
|
|
@@ -2002,6 +2016,7 @@ export function App({
|
|
|
2002
2016
|
);
|
|
2003
2017
|
} catch (err) {
|
|
2004
2018
|
setMeetings([]);
|
|
2019
|
+
setMeetingsCalendarNeedsReauth(false);
|
|
2005
2020
|
setMeetingsError(
|
|
2006
2021
|
err instanceof Error ? err.message : "Could not load meetings.",
|
|
2007
2022
|
);
|
|
@@ -4150,6 +4165,7 @@ export function App({
|
|
|
4150
4165
|
onStartNotes={startMeetingNotes}
|
|
4151
4166
|
onStartNotesAndJoin={startMeetingNotesAndJoin}
|
|
4152
4167
|
onShowActiveMeeting={showActiveMeetingPill}
|
|
4168
|
+
calendarNeedsReauth={meetingsCalendarNeedsReauth}
|
|
4153
4169
|
/>
|
|
4154
4170
|
</div>
|
|
4155
4171
|
);
|
|
@@ -5409,6 +5425,7 @@ function MeetingsPopoverView({
|
|
|
5409
5425
|
onStartNotes,
|
|
5410
5426
|
onStartNotesAndJoin,
|
|
5411
5427
|
onShowActiveMeeting,
|
|
5428
|
+
calendarNeedsReauth,
|
|
5412
5429
|
}: {
|
|
5413
5430
|
meetings: PopoverMeeting[];
|
|
5414
5431
|
loading: boolean;
|
|
@@ -5431,23 +5448,8 @@ function MeetingsPopoverView({
|
|
|
5431
5448
|
includeFromMeetingStart?: boolean,
|
|
5432
5449
|
) => void;
|
|
5433
5450
|
onShowActiveMeeting: (meetingId: string) => void;
|
|
5451
|
+
calendarNeedsReauth: boolean;
|
|
5434
5452
|
}) {
|
|
5435
|
-
const [includeHistoryFor, setIncludeHistoryFor] = useState<Set<string>>(
|
|
5436
|
-
() => new Set(),
|
|
5437
|
-
);
|
|
5438
|
-
|
|
5439
|
-
const consumeIncludeHistoryChoice = (meeting: PopoverMeeting): boolean => {
|
|
5440
|
-
const include = includeHistoryFor.has(meeting.id);
|
|
5441
|
-
// This is intentionally a one-shot choice. It never follows the next
|
|
5442
|
-
// meeting, an automatic start, or a tray action around like a lost duck.
|
|
5443
|
-
setIncludeHistoryFor((current) => {
|
|
5444
|
-
const next = new Set(current);
|
|
5445
|
-
next.delete(meeting.id);
|
|
5446
|
-
return next;
|
|
5447
|
-
});
|
|
5448
|
-
return include;
|
|
5449
|
-
};
|
|
5450
|
-
|
|
5451
5453
|
return (
|
|
5452
5454
|
<div className="setup popover-view">
|
|
5453
5455
|
<PopoverSubViewHeader
|
|
@@ -5483,9 +5485,21 @@ function MeetingsPopoverView({
|
|
|
5483
5485
|
<div className="popover-empty-card">
|
|
5484
5486
|
<strong>Could not load meetings</strong>
|
|
5485
5487
|
<p>{error}</p>
|
|
5488
|
+
{calendarNeedsReauth ? (
|
|
5489
|
+
<p>Reconnect your calendar from the Meetings page.</p>
|
|
5490
|
+
) : null}
|
|
5486
5491
|
<button type="button" className="secondary" onClick={onRefresh}>
|
|
5487
5492
|
Try again
|
|
5488
5493
|
</button>
|
|
5494
|
+
{calendarNeedsReauth ? (
|
|
5495
|
+
<button
|
|
5496
|
+
type="button"
|
|
5497
|
+
className="secondary"
|
|
5498
|
+
onClick={onOpenMeetings}
|
|
5499
|
+
>
|
|
5500
|
+
Open Meetings
|
|
5501
|
+
</button>
|
|
5502
|
+
) : null}
|
|
5489
5503
|
</div>
|
|
5490
5504
|
) : loading ? (
|
|
5491
5505
|
<div className="popover-empty-card">
|
|
@@ -5494,8 +5508,16 @@ function MeetingsPopoverView({
|
|
|
5494
5508
|
</div>
|
|
5495
5509
|
) : meetings.length === 0 ? (
|
|
5496
5510
|
<div className="popover-empty-card">
|
|
5497
|
-
<strong>
|
|
5498
|
-
|
|
5511
|
+
<strong>
|
|
5512
|
+
{calendarNeedsReauth
|
|
5513
|
+
? "Reconnect your calendar"
|
|
5514
|
+
: "No meetings ready"}
|
|
5515
|
+
</strong>
|
|
5516
|
+
<p>
|
|
5517
|
+
{calendarNeedsReauth
|
|
5518
|
+
? "Your calendar connection needs attention before Clips can match meeting titles."
|
|
5519
|
+
: "Connect Google Calendar or open the Meetings page to see setup."}
|
|
5520
|
+
</p>
|
|
5499
5521
|
<button type="button" className="secondary" onClick={onOpenMeetings}>
|
|
5500
5522
|
Open Meetings
|
|
5501
5523
|
</button>
|
|
@@ -5507,7 +5529,6 @@ function MeetingsPopoverView({
|
|
|
5507
5529
|
const hasJoin = Boolean(meeting.joinUrl);
|
|
5508
5530
|
const isActive = activeMeetingId === meeting.id;
|
|
5509
5531
|
const rewindHistory = rewindHistoryAvailability[meeting.id];
|
|
5510
|
-
const includeHistory = includeHistoryFor.has(meeting.id);
|
|
5511
5532
|
return (
|
|
5512
5533
|
<div className="popover-list-item" key={meeting.id}>
|
|
5513
5534
|
<div className="popover-list-icon">
|
|
@@ -5519,24 +5540,6 @@ function MeetingsPopoverView({
|
|
|
5519
5540
|
{formatMeetingWhen(meeting)}
|
|
5520
5541
|
{meeting.platform ? ` · ${meeting.platform}` : ""}
|
|
5521
5542
|
</div>
|
|
5522
|
-
{canStart && rewindHistory?.available ? (
|
|
5523
|
-
<label className="popover-list-sub">
|
|
5524
|
-
<input
|
|
5525
|
-
type="checkbox"
|
|
5526
|
-
checked={includeHistory}
|
|
5527
|
-
onChange={(event) => {
|
|
5528
|
-
const checked = event.currentTarget.checked;
|
|
5529
|
-
setIncludeHistoryFor((current) => {
|
|
5530
|
-
const next = new Set(current);
|
|
5531
|
-
if (checked) next.add(meeting.id);
|
|
5532
|
-
else next.delete(meeting.id);
|
|
5533
|
-
return next;
|
|
5534
|
-
});
|
|
5535
|
-
}}
|
|
5536
|
-
/>{" "}
|
|
5537
|
-
Include from meeting start
|
|
5538
|
-
</label>
|
|
5539
|
-
) : null}
|
|
5540
5543
|
</div>
|
|
5541
5544
|
{isActive ? (
|
|
5542
5545
|
<button
|
|
@@ -5556,11 +5559,11 @@ function MeetingsPopoverView({
|
|
|
5556
5559
|
hasJoin
|
|
5557
5560
|
? onStartNotesAndJoin(
|
|
5558
5561
|
meeting,
|
|
5559
|
-
|
|
5562
|
+
rewindHistory?.available === true,
|
|
5560
5563
|
)
|
|
5561
5564
|
: onStartNotes(
|
|
5562
5565
|
meeting,
|
|
5563
|
-
|
|
5566
|
+
rewindHistory?.available === true,
|
|
5564
5567
|
)
|
|
5565
5568
|
}
|
|
5566
5569
|
title={
|
|
@@ -5850,6 +5853,8 @@ function Setup({
|
|
|
5850
5853
|
featureConfig?.screenMemory ?? DEFAULT_SCREEN_MEMORY_CONFIG;
|
|
5851
5854
|
const [screenMemory, setScreenMemory] = useState(observedScreenMemory);
|
|
5852
5855
|
const [rewindConsentOpen, setRewindConsentOpen] = useState(false);
|
|
5856
|
+
const [rewindManageOpen, setRewindManageOpen] = useState(false);
|
|
5857
|
+
const [rewindShowAdvanced, setRewindShowAdvanced] = useState(false);
|
|
5853
5858
|
const screenMemoryRef = useRef(observedScreenMemory);
|
|
5854
5859
|
const screenMemoryMutationRef = useRef(0);
|
|
5855
5860
|
const screenMemoryMutationVersionRef = useRef(0);
|
|
@@ -6969,31 +6974,11 @@ function Setup({
|
|
|
6969
6974
|
);
|
|
6970
6975
|
}
|
|
6971
6976
|
|
|
6972
|
-
/**
|
|
6973
|
-
* Rewind's settings, flat. Every one of these used to live behind a Manage
|
|
6974
|
-
* button inside a popover launched from another popover — three layers deep,
|
|
6975
|
-
* with tooltips escaping behind the window. They are ordinary rows now, in
|
|
6976
|
-
* the order a user reasons about them: does it run, what does it keep, who
|
|
6977
|
-
* may see it, where does it live.
|
|
6978
|
-
*/
|
|
6979
6977
|
function renderRewindSettings() {
|
|
6980
6978
|
const rewindOn = screenMemory.enabled === true;
|
|
6981
6979
|
return (
|
|
6982
6980
|
<div className="mx-auto grid w-full max-w-[620px] gap-7 pb-4">
|
|
6983
6981
|
<SettingsGroup>
|
|
6984
|
-
{/* A confirmation, so a dialog rather than a takeover: the user is
|
|
6985
|
-
answering one question about the screen behind it, not moving to
|
|
6986
|
-
a new place. What it remembers is chosen afterwards, in the row
|
|
6987
|
-
below — asking before they have agreed puts the options in front
|
|
6988
|
-
of the decision.
|
|
6989
|
-
|
|
6990
|
-
The copy names what is captured and what can leave, and nothing
|
|
6991
|
-
else. Two claims are tempting and both are false: this buffer
|
|
6992
|
-
holds screen *video* (hence the GB disk limit below), not app
|
|
6993
|
-
and window notes; and it cannot promise "never leaves this Mac"
|
|
6994
|
-
because the agent-handoff path uploads an approved range. A
|
|
6995
|
-
consent screen is the one place a comforting simplification is
|
|
6996
|
-
indistinguishable from a lie. */}
|
|
6997
6982
|
<UiAlertDialog
|
|
6998
6983
|
open={rewindConsentOpen}
|
|
6999
6984
|
onOpenChange={setRewindConsentOpen}
|
|
@@ -7038,24 +7023,397 @@ function Setup({
|
|
|
7038
7023
|
label="Rewind"
|
|
7039
7024
|
description="Keeps a rolling record of your recent screen on this device"
|
|
7040
7025
|
control={
|
|
7041
|
-
<
|
|
7042
|
-
|
|
7043
|
-
|
|
7044
|
-
|
|
7045
|
-
|
|
7046
|
-
|
|
7047
|
-
|
|
7048
|
-
|
|
7049
|
-
|
|
7026
|
+
<div className="flex items-center gap-2">
|
|
7027
|
+
<SettingsPopover
|
|
7028
|
+
title="Rewind settings"
|
|
7029
|
+
open={rewindManageOpen}
|
|
7030
|
+
onOpenChange={setRewindManageOpen}
|
|
7031
|
+
className="w-[480px] max-w-[calc(100vw-24px)]"
|
|
7032
|
+
trigger={
|
|
7033
|
+
<SettingsActionButton
|
|
7034
|
+
aria-expanded={rewindManageOpen}
|
|
7035
|
+
aria-haspopup="dialog"
|
|
7036
|
+
>
|
|
7037
|
+
Manage
|
|
7038
|
+
</SettingsActionButton>
|
|
7050
7039
|
}
|
|
7051
|
-
|
|
7052
|
-
|
|
7053
|
-
|
|
7054
|
-
|
|
7055
|
-
|
|
7056
|
-
|
|
7057
|
-
|
|
7058
|
-
|
|
7040
|
+
>
|
|
7041
|
+
<div className="grid max-h-[min(620px,calc(100vh-80px))] gap-3 overflow-y-auto pr-1">
|
|
7042
|
+
{rewindOn ? (
|
|
7043
|
+
<>
|
|
7044
|
+
<SettingsGroup label="Capture">
|
|
7045
|
+
<SettingsRow
|
|
7046
|
+
label="Remember"
|
|
7047
|
+
description="Choose what Rewind captures"
|
|
7048
|
+
control={
|
|
7049
|
+
<SettingsSelect
|
|
7050
|
+
ariaLabel="What Rewind remembers"
|
|
7051
|
+
value={screenMemory.captureMode ?? "visuals"}
|
|
7052
|
+
onValueChange={(value) => {
|
|
7053
|
+
void setScreenMemoryConfig({
|
|
7054
|
+
captureMode: value as RewindCaptureMode,
|
|
7055
|
+
});
|
|
7056
|
+
}}
|
|
7057
|
+
disabled={
|
|
7058
|
+
screenMemoryConfigBusy ||
|
|
7059
|
+
captureControlsLocked
|
|
7060
|
+
}
|
|
7061
|
+
options={[
|
|
7062
|
+
{ value: "visuals", label: "Screen only" },
|
|
7063
|
+
{
|
|
7064
|
+
value: "visuals-audio",
|
|
7065
|
+
label: "Screen + audio",
|
|
7066
|
+
},
|
|
7067
|
+
]}
|
|
7068
|
+
/>
|
|
7069
|
+
}
|
|
7070
|
+
/>
|
|
7071
|
+
<SettingsRow
|
|
7072
|
+
label="Time limit"
|
|
7073
|
+
description="Choose how long Rewind keeps your screen history"
|
|
7074
|
+
control={
|
|
7075
|
+
<SettingsSelect
|
|
7076
|
+
ariaLabel="Rewind time limit"
|
|
7077
|
+
placeholder={`${screenMemory.retentionHours} hours`}
|
|
7078
|
+
value={String(screenMemory.retentionHours)}
|
|
7079
|
+
onValueChange={(value) => {
|
|
7080
|
+
void setScreenMemoryConfig({
|
|
7081
|
+
retentionHours: Number(value),
|
|
7082
|
+
});
|
|
7083
|
+
}}
|
|
7084
|
+
disabled={screenMemoryConfigBusy}
|
|
7085
|
+
options={[
|
|
7086
|
+
{ value: "8", label: "8 hours" },
|
|
7087
|
+
{ value: "24", label: "24 hours" },
|
|
7088
|
+
]}
|
|
7089
|
+
/>
|
|
7090
|
+
}
|
|
7091
|
+
/>
|
|
7092
|
+
<SettingsRow
|
|
7093
|
+
label="Storage limit"
|
|
7094
|
+
description="Choose how much space Rewind can use on this device"
|
|
7095
|
+
control={
|
|
7096
|
+
<SettingsSelect
|
|
7097
|
+
ariaLabel="Rewind storage limit"
|
|
7098
|
+
placeholder={formatStorageBytes(
|
|
7099
|
+
screenMemory.maxBytes,
|
|
7100
|
+
)}
|
|
7101
|
+
value={String(screenMemory.maxBytes)}
|
|
7102
|
+
onValueChange={(value) => {
|
|
7103
|
+
void setScreenMemoryConfig({
|
|
7104
|
+
maxBytes: Number(value),
|
|
7105
|
+
});
|
|
7106
|
+
}}
|
|
7107
|
+
disabled={screenMemoryConfigBusy}
|
|
7108
|
+
options={[
|
|
7109
|
+
{
|
|
7110
|
+
value: String(5 * 1024 * 1024 * 1024),
|
|
7111
|
+
label: "5 GB",
|
|
7112
|
+
},
|
|
7113
|
+
{
|
|
7114
|
+
value: String(20 * 1024 * 1024 * 1024),
|
|
7115
|
+
label: "20 GB",
|
|
7116
|
+
},
|
|
7117
|
+
{
|
|
7118
|
+
value: String(50 * 1024 * 1024 * 1024),
|
|
7119
|
+
label: "50 GB",
|
|
7120
|
+
},
|
|
7121
|
+
]}
|
|
7122
|
+
/>
|
|
7123
|
+
}
|
|
7124
|
+
/>
|
|
7125
|
+
</SettingsGroup>
|
|
7126
|
+
<SettingsActionButton
|
|
7127
|
+
emphasis="quiet"
|
|
7128
|
+
className="justify-self-start"
|
|
7129
|
+
aria-expanded={rewindShowAdvanced}
|
|
7130
|
+
onClick={() =>
|
|
7131
|
+
setRewindShowAdvanced((current) => !current)
|
|
7132
|
+
}
|
|
7133
|
+
>
|
|
7134
|
+
{rewindShowAdvanced
|
|
7135
|
+
? "Hide advanced"
|
|
7136
|
+
: "Show advanced"}
|
|
7137
|
+
</SettingsActionButton>
|
|
7138
|
+
{rewindShowAdvanced ? (
|
|
7139
|
+
<>
|
|
7140
|
+
<SettingsGroup label="Privacy">
|
|
7141
|
+
<SettingsRow
|
|
7142
|
+
label="Excluded apps"
|
|
7143
|
+
description="Apps Rewind never captures"
|
|
7144
|
+
control={
|
|
7145
|
+
<SettingsActionButton
|
|
7146
|
+
onClick={() =>
|
|
7147
|
+
void chooseExcludedApplications()
|
|
7148
|
+
}
|
|
7149
|
+
disabled={excludedAppsBusy}
|
|
7150
|
+
>
|
|
7151
|
+
Choose apps
|
|
7152
|
+
</SettingsActionButton>
|
|
7153
|
+
}
|
|
7154
|
+
>
|
|
7155
|
+
{excludedAppGroups.length > 0 ? (
|
|
7156
|
+
<div className="grid gap-1">
|
|
7157
|
+
{excludedAppGroups.map((app) => (
|
|
7158
|
+
<div
|
|
7159
|
+
key={app.bundleIds.join(",")}
|
|
7160
|
+
className="flex items-center justify-between gap-2"
|
|
7161
|
+
>
|
|
7162
|
+
<span className="truncate">
|
|
7163
|
+
{app.name}
|
|
7164
|
+
</span>
|
|
7165
|
+
<SettingsActionButton
|
|
7166
|
+
emphasis="quiet"
|
|
7167
|
+
onClick={() =>
|
|
7168
|
+
removeExcludedApplications(
|
|
7169
|
+
app.bundleIds,
|
|
7170
|
+
)
|
|
7171
|
+
}
|
|
7172
|
+
>
|
|
7173
|
+
Remove
|
|
7174
|
+
</SettingsActionButton>
|
|
7175
|
+
</div>
|
|
7176
|
+
))}
|
|
7177
|
+
</div>
|
|
7178
|
+
) : null}
|
|
7179
|
+
</SettingsRow>
|
|
7180
|
+
<SettingsRow
|
|
7181
|
+
label="Review before sending"
|
|
7182
|
+
description="Approve each visual or audio range before an agent receives it"
|
|
7183
|
+
control={
|
|
7184
|
+
<SettingsSwitch
|
|
7185
|
+
checked={
|
|
7186
|
+
screenMemory.reviewBeforeSending !== false
|
|
7187
|
+
}
|
|
7188
|
+
onCheckedChange={(next) => {
|
|
7189
|
+
void setScreenMemoryConfig({
|
|
7190
|
+
reviewBeforeSending: next,
|
|
7191
|
+
});
|
|
7192
|
+
}}
|
|
7193
|
+
disabled={screenMemoryConfigBusy}
|
|
7194
|
+
label="Review before sending"
|
|
7195
|
+
/>
|
|
7196
|
+
}
|
|
7197
|
+
/>
|
|
7198
|
+
<SettingsRow
|
|
7199
|
+
label="Preview before sending"
|
|
7200
|
+
description="Open the range locally so you see exactly what is sent"
|
|
7201
|
+
control={
|
|
7202
|
+
<SettingsSwitch
|
|
7203
|
+
checked={
|
|
7204
|
+
screenMemory.autoPreviewBeforeSending ===
|
|
7205
|
+
true
|
|
7206
|
+
}
|
|
7207
|
+
onCheckedChange={(next) => {
|
|
7208
|
+
void setScreenMemoryConfig({
|
|
7209
|
+
autoPreviewBeforeSending: next,
|
|
7210
|
+
});
|
|
7211
|
+
}}
|
|
7212
|
+
disabled={screenMemoryConfigBusy}
|
|
7213
|
+
label="Preview before sending"
|
|
7214
|
+
/>
|
|
7215
|
+
}
|
|
7216
|
+
/>
|
|
7217
|
+
<SettingsRow
|
|
7218
|
+
label="Keep agent Clips"
|
|
7219
|
+
description="Choose how long Clips made for agents stay in your library"
|
|
7220
|
+
control={
|
|
7221
|
+
<SettingsSelect
|
|
7222
|
+
ariaLabel="How long agent-created Clips are kept"
|
|
7223
|
+
value={screenMemory.agentClipRetention}
|
|
7224
|
+
onValueChange={(value) => {
|
|
7225
|
+
void setScreenMemoryConfig({
|
|
7226
|
+
agentClipRetention:
|
|
7227
|
+
value as ScreenMemoryStatus["config"]["agentClipRetention"],
|
|
7228
|
+
});
|
|
7229
|
+
}}
|
|
7230
|
+
disabled={screenMemoryConfigBusy}
|
|
7231
|
+
options={[
|
|
7232
|
+
{
|
|
7233
|
+
value: "forever",
|
|
7234
|
+
label: "Forever",
|
|
7235
|
+
},
|
|
7236
|
+
{
|
|
7237
|
+
value: "24-hours",
|
|
7238
|
+
label: "24 hours",
|
|
7239
|
+
},
|
|
7240
|
+
{
|
|
7241
|
+
value: "7-days",
|
|
7242
|
+
label: "7 days",
|
|
7243
|
+
},
|
|
7244
|
+
{
|
|
7245
|
+
value: "30-days",
|
|
7246
|
+
label: "30 days",
|
|
7247
|
+
},
|
|
7248
|
+
]}
|
|
7249
|
+
/>
|
|
7250
|
+
}
|
|
7251
|
+
/>
|
|
7252
|
+
<SettingsRow
|
|
7253
|
+
label="Agent activity"
|
|
7254
|
+
description="Each time an agent searched this device's memory, newest first"
|
|
7255
|
+
>
|
|
7256
|
+
{rewindEgressEvents.length === 0 ? (
|
|
7257
|
+
<p>No agent has searched it yet.</p>
|
|
7258
|
+
) : (
|
|
7259
|
+
<div className="grid gap-1">
|
|
7260
|
+
{rewindEgressEvents
|
|
7261
|
+
.slice(0, 10)
|
|
7262
|
+
.map((event) => (
|
|
7263
|
+
<div
|
|
7264
|
+
key={`${event.requestId}-${event.state}`}
|
|
7265
|
+
className="flex items-center justify-between gap-2"
|
|
7266
|
+
>
|
|
7267
|
+
<span className="truncate">
|
|
7268
|
+
{new Date(
|
|
7269
|
+
event.occurredAt,
|
|
7270
|
+
).toLocaleString()}
|
|
7271
|
+
</span>
|
|
7272
|
+
<span className="shrink-0">
|
|
7273
|
+
{event.state} ·{" "}
|
|
7274
|
+
{`${event.evidenceCount} item${event.evidenceCount === 1 ? "" : "s"}`}
|
|
7275
|
+
</span>
|
|
7276
|
+
</div>
|
|
7277
|
+
))}
|
|
7278
|
+
</div>
|
|
7279
|
+
)}
|
|
7280
|
+
</SettingsRow>
|
|
7281
|
+
</SettingsGroup>
|
|
7282
|
+
|
|
7283
|
+
<SettingsGroup label="Agents">
|
|
7284
|
+
<SettingsRow
|
|
7285
|
+
label="Setup prompt"
|
|
7286
|
+
description="Paste it into an agent once to install Rewind's instructions"
|
|
7287
|
+
control={
|
|
7288
|
+
<>
|
|
7289
|
+
<SettingsActionButton
|
|
7290
|
+
emphasis="quiet"
|
|
7291
|
+
onClick={onOpenRewindDocs}
|
|
7292
|
+
>
|
|
7293
|
+
Learn more
|
|
7294
|
+
</SettingsActionButton>
|
|
7295
|
+
<SettingsActionButton
|
|
7296
|
+
onClick={onCopyRewindAgentPrompt}
|
|
7297
|
+
>
|
|
7298
|
+
{rewindAgentPromptCopied
|
|
7299
|
+
? "Copied"
|
|
7300
|
+
: "Copy"}
|
|
7301
|
+
</SettingsActionButton>
|
|
7302
|
+
</>
|
|
7303
|
+
}
|
|
7304
|
+
/>
|
|
7305
|
+
<SettingsRow
|
|
7306
|
+
label="Connect an agent"
|
|
7307
|
+
description="Gives a local agent access to this device's Rewind memory"
|
|
7308
|
+
control={
|
|
7309
|
+
<>
|
|
7310
|
+
<SettingsActionButton
|
|
7311
|
+
onClick={() =>
|
|
7312
|
+
void installRewindAgentConnection(
|
|
7313
|
+
"codex",
|
|
7314
|
+
)
|
|
7315
|
+
}
|
|
7316
|
+
disabled={agentConnectionBusy !== null}
|
|
7317
|
+
>
|
|
7318
|
+
Codex
|
|
7319
|
+
</SettingsActionButton>
|
|
7320
|
+
<SettingsActionButton
|
|
7321
|
+
onClick={() =>
|
|
7322
|
+
void installRewindAgentConnection(
|
|
7323
|
+
"claude-code",
|
|
7324
|
+
)
|
|
7325
|
+
}
|
|
7326
|
+
disabled={agentConnectionBusy !== null}
|
|
7327
|
+
>
|
|
7328
|
+
Claude Code
|
|
7329
|
+
</SettingsActionButton>
|
|
7330
|
+
</>
|
|
7331
|
+
}
|
|
7332
|
+
>
|
|
7333
|
+
{agentConnectionMessage ? (
|
|
7334
|
+
<p
|
|
7335
|
+
className={
|
|
7336
|
+
agentConnectionMessage.kind === "ok"
|
|
7337
|
+
? "text-xs text-success"
|
|
7338
|
+
: "text-xs text-destructive"
|
|
7339
|
+
}
|
|
7340
|
+
>
|
|
7341
|
+
{agentConnectionMessage.text}
|
|
7342
|
+
</p>
|
|
7343
|
+
) : null}
|
|
7344
|
+
</SettingsRow>
|
|
7345
|
+
</SettingsGroup>
|
|
7346
|
+
|
|
7347
|
+
<SettingsGroup label="Storage">
|
|
7348
|
+
<SettingsRow
|
|
7349
|
+
label="Search memory"
|
|
7350
|
+
description="Find and replay a recent moment yourself"
|
|
7351
|
+
control={
|
|
7352
|
+
<SettingsActionButton onClick={onOpenMemory}>
|
|
7353
|
+
Search
|
|
7354
|
+
</SettingsActionButton>
|
|
7355
|
+
}
|
|
7356
|
+
/>
|
|
7357
|
+
<SettingsRow
|
|
7358
|
+
label="Save last 5 minutes"
|
|
7359
|
+
description="Exports recent memory as video files on this device. Nothing is uploaded."
|
|
7360
|
+
control={
|
|
7361
|
+
<SettingsActionButton
|
|
7362
|
+
onClick={() =>
|
|
7363
|
+
void exportScreenMemoryRecent()
|
|
7364
|
+
}
|
|
7365
|
+
disabled={screenMemoryBusy}
|
|
7366
|
+
>
|
|
7367
|
+
Save
|
|
7368
|
+
</SettingsActionButton>
|
|
7369
|
+
}
|
|
7370
|
+
/>
|
|
7371
|
+
<SettingsRow
|
|
7372
|
+
label="On this device"
|
|
7373
|
+
description={`${screenMemorySegments.length} ${screenMemorySegments.length === 1 ? "segment" : "segments"} · ${formatStorageBytes(screenMemoryTotalBytes)}`}
|
|
7374
|
+
control={
|
|
7375
|
+
<>
|
|
7376
|
+
<SettingsActionButton
|
|
7377
|
+
onClick={openScreenMemoryFolder}
|
|
7378
|
+
>
|
|
7379
|
+
Open folder
|
|
7380
|
+
</SettingsActionButton>
|
|
7381
|
+
<SettingsActionButton
|
|
7382
|
+
emphasis="destructive"
|
|
7383
|
+
onClick={() => void clearScreenMemory()}
|
|
7384
|
+
disabled={screenMemoryBusy}
|
|
7385
|
+
>
|
|
7386
|
+
Delete all
|
|
7387
|
+
</SettingsActionButton>
|
|
7388
|
+
</>
|
|
7389
|
+
}
|
|
7390
|
+
/>
|
|
7391
|
+
</SettingsGroup>
|
|
7392
|
+
</>
|
|
7393
|
+
) : null}
|
|
7394
|
+
</>
|
|
7395
|
+
) : (
|
|
7396
|
+
<SettingsGroup>
|
|
7397
|
+
<div className="p-3 text-sm text-muted-foreground">
|
|
7398
|
+
Turn on Rewind to manage what it remembers.
|
|
7399
|
+
</div>
|
|
7400
|
+
</SettingsGroup>
|
|
7401
|
+
)}
|
|
7402
|
+
</div>
|
|
7403
|
+
</SettingsPopover>
|
|
7404
|
+
<SettingsSwitch
|
|
7405
|
+
checked={rewindOn}
|
|
7406
|
+
onCheckedChange={(next) => {
|
|
7407
|
+
if (next) {
|
|
7408
|
+
setRewindConsentOpen(true);
|
|
7409
|
+
return;
|
|
7410
|
+
}
|
|
7411
|
+
void setScreenMemoryConfig({ enabled: false });
|
|
7412
|
+
}}
|
|
7413
|
+
disabled={screenMemoryConfigBusy || captureControlsLocked}
|
|
7414
|
+
label="Rewind"
|
|
7415
|
+
/>
|
|
7416
|
+
</div>
|
|
7059
7417
|
}
|
|
7060
7418
|
>
|
|
7061
7419
|
{screenMemoryMessage ? (
|
|
@@ -7074,294 +7432,7 @@ function Setup({
|
|
|
7074
7432
|
</p>
|
|
7075
7433
|
) : null}
|
|
7076
7434
|
</SettingsRow>
|
|
7077
|
-
{rewindOn ? (
|
|
7078
|
-
<>
|
|
7079
|
-
<SettingsRow
|
|
7080
|
-
label="Remember"
|
|
7081
|
-
description="Choose what Rewind captures"
|
|
7082
|
-
control={
|
|
7083
|
-
<SettingsSelect
|
|
7084
|
-
ariaLabel="What Rewind remembers"
|
|
7085
|
-
value={screenMemory.captureMode ?? "visuals"}
|
|
7086
|
-
onValueChange={(value) => {
|
|
7087
|
-
void setScreenMemoryConfig({
|
|
7088
|
-
captureMode: value as RewindCaptureMode,
|
|
7089
|
-
});
|
|
7090
|
-
}}
|
|
7091
|
-
disabled={screenMemoryConfigBusy || captureControlsLocked}
|
|
7092
|
-
options={[
|
|
7093
|
-
{ value: "visuals", label: "Screen only" },
|
|
7094
|
-
{ value: "visuals-audio", label: "Screen + audio" },
|
|
7095
|
-
]}
|
|
7096
|
-
/>
|
|
7097
|
-
}
|
|
7098
|
-
/>
|
|
7099
|
-
<SettingsRow
|
|
7100
|
-
label="Time limit"
|
|
7101
|
-
description="Choose how long Rewind keeps your screen history"
|
|
7102
|
-
control={
|
|
7103
|
-
<SettingsSelect
|
|
7104
|
-
ariaLabel="Rewind time limit"
|
|
7105
|
-
placeholder={`${screenMemory.retentionHours} hours`}
|
|
7106
|
-
value={String(screenMemory.retentionHours)}
|
|
7107
|
-
onValueChange={(value) => {
|
|
7108
|
-
void setScreenMemoryConfig({
|
|
7109
|
-
retentionHours: Number(value),
|
|
7110
|
-
});
|
|
7111
|
-
}}
|
|
7112
|
-
disabled={screenMemoryConfigBusy}
|
|
7113
|
-
options={[
|
|
7114
|
-
{ value: "8", label: "8 hours" },
|
|
7115
|
-
{ value: "24", label: "24 hours" },
|
|
7116
|
-
]}
|
|
7117
|
-
/>
|
|
7118
|
-
}
|
|
7119
|
-
/>
|
|
7120
|
-
<SettingsRow
|
|
7121
|
-
label="Storage limit"
|
|
7122
|
-
description="Choose how much space Rewind can use on this device"
|
|
7123
|
-
control={
|
|
7124
|
-
<SettingsSelect
|
|
7125
|
-
ariaLabel="Rewind storage limit"
|
|
7126
|
-
placeholder={formatStorageBytes(screenMemory.maxBytes)}
|
|
7127
|
-
value={String(screenMemory.maxBytes)}
|
|
7128
|
-
onValueChange={(value) => {
|
|
7129
|
-
void setScreenMemoryConfig({ maxBytes: Number(value) });
|
|
7130
|
-
}}
|
|
7131
|
-
disabled={screenMemoryConfigBusy}
|
|
7132
|
-
options={[
|
|
7133
|
-
{ value: String(5 * 1024 * 1024 * 1024), label: "5 GB" },
|
|
7134
|
-
{
|
|
7135
|
-
value: String(20 * 1024 * 1024 * 1024),
|
|
7136
|
-
label: "20 GB",
|
|
7137
|
-
},
|
|
7138
|
-
{
|
|
7139
|
-
value: String(50 * 1024 * 1024 * 1024),
|
|
7140
|
-
label: "50 GB",
|
|
7141
|
-
},
|
|
7142
|
-
]}
|
|
7143
|
-
/>
|
|
7144
|
-
}
|
|
7145
|
-
/>
|
|
7146
|
-
</>
|
|
7147
|
-
) : null}
|
|
7148
7435
|
</SettingsGroup>
|
|
7149
|
-
|
|
7150
|
-
{rewindOn ? (
|
|
7151
|
-
<>
|
|
7152
|
-
<SettingsGroup label="Privacy">
|
|
7153
|
-
<SettingsRow
|
|
7154
|
-
label="Excluded apps"
|
|
7155
|
-
description={"Apps Rewind never captures"}
|
|
7156
|
-
control={
|
|
7157
|
-
<SettingsActionButton
|
|
7158
|
-
onClick={() => void chooseExcludedApplications()}
|
|
7159
|
-
disabled={excludedAppsBusy}
|
|
7160
|
-
>
|
|
7161
|
-
Choose apps
|
|
7162
|
-
</SettingsActionButton>
|
|
7163
|
-
}
|
|
7164
|
-
>
|
|
7165
|
-
{excludedAppGroups.length > 0 ? (
|
|
7166
|
-
<div className="grid gap-1">
|
|
7167
|
-
{excludedAppGroups.map((app) => (
|
|
7168
|
-
<div
|
|
7169
|
-
key={app.bundleIds.join(",")}
|
|
7170
|
-
className="flex items-center justify-between gap-2"
|
|
7171
|
-
>
|
|
7172
|
-
<span className="truncate">{app.name}</span>
|
|
7173
|
-
<SettingsActionButton
|
|
7174
|
-
emphasis="quiet"
|
|
7175
|
-
onClick={() =>
|
|
7176
|
-
removeExcludedApplications(app.bundleIds)
|
|
7177
|
-
}
|
|
7178
|
-
>
|
|
7179
|
-
Remove
|
|
7180
|
-
</SettingsActionButton>
|
|
7181
|
-
</div>
|
|
7182
|
-
))}
|
|
7183
|
-
</div>
|
|
7184
|
-
) : null}
|
|
7185
|
-
</SettingsRow>
|
|
7186
|
-
<SettingsRow
|
|
7187
|
-
label="Review before sending"
|
|
7188
|
-
description="Approve each visual or audio range before an agent receives it"
|
|
7189
|
-
control={
|
|
7190
|
-
<SettingsSwitch
|
|
7191
|
-
checked={screenMemory.reviewBeforeSending !== false}
|
|
7192
|
-
onCheckedChange={(next) => {
|
|
7193
|
-
void setScreenMemoryConfig({
|
|
7194
|
-
reviewBeforeSending: next,
|
|
7195
|
-
});
|
|
7196
|
-
}}
|
|
7197
|
-
disabled={screenMemoryConfigBusy}
|
|
7198
|
-
label="Review before sending"
|
|
7199
|
-
/>
|
|
7200
|
-
}
|
|
7201
|
-
/>
|
|
7202
|
-
<SettingsRow
|
|
7203
|
-
label="Preview before sending"
|
|
7204
|
-
description="Open the range locally so you see exactly what is sent"
|
|
7205
|
-
control={
|
|
7206
|
-
<SettingsSwitch
|
|
7207
|
-
checked={screenMemory.autoPreviewBeforeSending === true}
|
|
7208
|
-
onCheckedChange={(next) => {
|
|
7209
|
-
void setScreenMemoryConfig({
|
|
7210
|
-
autoPreviewBeforeSending: next,
|
|
7211
|
-
});
|
|
7212
|
-
}}
|
|
7213
|
-
disabled={screenMemoryConfigBusy}
|
|
7214
|
-
label="Preview before sending"
|
|
7215
|
-
/>
|
|
7216
|
-
}
|
|
7217
|
-
/>
|
|
7218
|
-
<SettingsRow
|
|
7219
|
-
label="Keep agent Clips"
|
|
7220
|
-
description="Choose how long Clips made for agents stay in your library"
|
|
7221
|
-
control={
|
|
7222
|
-
<SettingsSelect
|
|
7223
|
-
ariaLabel="How long agent-created Clips are kept"
|
|
7224
|
-
value={screenMemory.agentClipRetention}
|
|
7225
|
-
onValueChange={(value) => {
|
|
7226
|
-
void setScreenMemoryConfig({
|
|
7227
|
-
agentClipRetention:
|
|
7228
|
-
value as ScreenMemoryStatus["config"]["agentClipRetention"],
|
|
7229
|
-
});
|
|
7230
|
-
}}
|
|
7231
|
-
disabled={screenMemoryConfigBusy}
|
|
7232
|
-
options={[
|
|
7233
|
-
{ value: "forever", label: "Forever" },
|
|
7234
|
-
{ value: "24-hours", label: "24 hours" },
|
|
7235
|
-
{ value: "7-days", label: "7 days" },
|
|
7236
|
-
{ value: "30-days", label: "30 days" },
|
|
7237
|
-
]}
|
|
7238
|
-
/>
|
|
7239
|
-
}
|
|
7240
|
-
/>
|
|
7241
|
-
<SettingsRow
|
|
7242
|
-
label="Agent activity"
|
|
7243
|
-
description="Each time an agent searched this device's memory, newest first"
|
|
7244
|
-
>
|
|
7245
|
-
{rewindEgressEvents.length === 0 ? (
|
|
7246
|
-
<p>No agent has searched it yet.</p>
|
|
7247
|
-
) : (
|
|
7248
|
-
<div className="grid gap-1">
|
|
7249
|
-
{rewindEgressEvents.slice(0, 10).map((event) => (
|
|
7250
|
-
<div
|
|
7251
|
-
key={`${event.requestId}-${event.state}`}
|
|
7252
|
-
className="flex items-center justify-between gap-2"
|
|
7253
|
-
>
|
|
7254
|
-
<span className="truncate">
|
|
7255
|
-
{new Date(event.occurredAt).toLocaleString()}
|
|
7256
|
-
</span>
|
|
7257
|
-
<span className="shrink-0">
|
|
7258
|
-
{event.state} ·{" "}
|
|
7259
|
-
{`${event.evidenceCount} item${event.evidenceCount === 1 ? "" : "s"}`}
|
|
7260
|
-
</span>
|
|
7261
|
-
</div>
|
|
7262
|
-
))}
|
|
7263
|
-
</div>
|
|
7264
|
-
)}
|
|
7265
|
-
</SettingsRow>
|
|
7266
|
-
</SettingsGroup>
|
|
7267
|
-
|
|
7268
|
-
<SettingsGroup label="Agents">
|
|
7269
|
-
<SettingsRow
|
|
7270
|
-
label="Setup prompt"
|
|
7271
|
-
description="Paste it into an agent once to install Rewind's instructions"
|
|
7272
|
-
control={
|
|
7273
|
-
<>
|
|
7274
|
-
<SettingsActionButton
|
|
7275
|
-
emphasis="quiet"
|
|
7276
|
-
onClick={onOpenRewindDocs}
|
|
7277
|
-
>
|
|
7278
|
-
Learn more
|
|
7279
|
-
</SettingsActionButton>
|
|
7280
|
-
<SettingsActionButton onClick={onCopyRewindAgentPrompt}>
|
|
7281
|
-
{rewindAgentPromptCopied ? "Copied" : "Copy"}
|
|
7282
|
-
</SettingsActionButton>
|
|
7283
|
-
</>
|
|
7284
|
-
}
|
|
7285
|
-
/>
|
|
7286
|
-
<SettingsRow
|
|
7287
|
-
label="Connect an agent"
|
|
7288
|
-
description="Gives a local agent access to this device's Rewind memory"
|
|
7289
|
-
control={
|
|
7290
|
-
<>
|
|
7291
|
-
<SettingsActionButton
|
|
7292
|
-
onClick={() => void installRewindAgentConnection("codex")}
|
|
7293
|
-
disabled={agentConnectionBusy !== null}
|
|
7294
|
-
>
|
|
7295
|
-
Codex
|
|
7296
|
-
</SettingsActionButton>
|
|
7297
|
-
<SettingsActionButton
|
|
7298
|
-
onClick={() =>
|
|
7299
|
-
void installRewindAgentConnection("claude-code")
|
|
7300
|
-
}
|
|
7301
|
-
disabled={agentConnectionBusy !== null}
|
|
7302
|
-
>
|
|
7303
|
-
Claude Code
|
|
7304
|
-
</SettingsActionButton>
|
|
7305
|
-
</>
|
|
7306
|
-
}
|
|
7307
|
-
>
|
|
7308
|
-
{agentConnectionMessage ? (
|
|
7309
|
-
<p
|
|
7310
|
-
className={
|
|
7311
|
-
agentConnectionMessage.kind === "ok"
|
|
7312
|
-
? "text-xs text-success"
|
|
7313
|
-
: "text-xs text-destructive"
|
|
7314
|
-
}
|
|
7315
|
-
>
|
|
7316
|
-
{agentConnectionMessage.text}
|
|
7317
|
-
</p>
|
|
7318
|
-
) : null}
|
|
7319
|
-
</SettingsRow>
|
|
7320
|
-
</SettingsGroup>
|
|
7321
|
-
|
|
7322
|
-
<SettingsGroup label="Storage">
|
|
7323
|
-
<SettingsRow
|
|
7324
|
-
label="Search memory"
|
|
7325
|
-
description="Find and replay a recent moment yourself"
|
|
7326
|
-
control={
|
|
7327
|
-
<SettingsActionButton onClick={onOpenMemory}>
|
|
7328
|
-
Search
|
|
7329
|
-
</SettingsActionButton>
|
|
7330
|
-
}
|
|
7331
|
-
/>
|
|
7332
|
-
<SettingsRow
|
|
7333
|
-
label="Save last 5 minutes"
|
|
7334
|
-
description="Exports recent memory as video files on this device. Nothing is uploaded."
|
|
7335
|
-
control={
|
|
7336
|
-
<SettingsActionButton
|
|
7337
|
-
onClick={() => void exportScreenMemoryRecent()}
|
|
7338
|
-
disabled={screenMemoryBusy}
|
|
7339
|
-
>
|
|
7340
|
-
Save
|
|
7341
|
-
</SettingsActionButton>
|
|
7342
|
-
}
|
|
7343
|
-
/>
|
|
7344
|
-
<SettingsRow
|
|
7345
|
-
label="On this device"
|
|
7346
|
-
description={`${screenMemorySegments.length} ${screenMemorySegments.length === 1 ? "segment" : "segments"} · ${formatStorageBytes(screenMemoryTotalBytes)}`}
|
|
7347
|
-
control={
|
|
7348
|
-
<>
|
|
7349
|
-
<SettingsActionButton onClick={openScreenMemoryFolder}>
|
|
7350
|
-
Open folder
|
|
7351
|
-
</SettingsActionButton>
|
|
7352
|
-
<SettingsActionButton
|
|
7353
|
-
emphasis="destructive"
|
|
7354
|
-
onClick={() => void clearScreenMemory()}
|
|
7355
|
-
disabled={screenMemoryBusy}
|
|
7356
|
-
>
|
|
7357
|
-
Delete all
|
|
7358
|
-
</SettingsActionButton>
|
|
7359
|
-
</>
|
|
7360
|
-
}
|
|
7361
|
-
/>
|
|
7362
|
-
</SettingsGroup>
|
|
7363
|
-
</>
|
|
7364
|
-
) : null}
|
|
7365
7436
|
</div>
|
|
7366
7437
|
);
|
|
7367
7438
|
}
|