@agent-native/core 0.180.0-nightly-20260915193344 → 0.180.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/templates/analytics/CHANGELOG.md +10 -0
- package/corpus/templates/calendar/CHANGELOG.md +39 -0
- package/corpus/templates/clips/.agents/skills/video-sharing/SKILL.md +2 -0
- package/corpus/templates/clips/CHANGELOG.md +19 -0
- package/corpus/templates/clips/actions/save-browser-diagnostics.ts +43 -4
- package/corpus/templates/clips/app/components/player/browser-diagnostics-panel.tsx +114 -1
- package/corpus/templates/clips/app/i18n/en-US.ts +7 -0
- package/corpus/templates/clips/app/lib/browser-diagnostics-capture.ts +108 -0
- package/corpus/templates/clips/app/routes/record.tsx +1 -0
- package/corpus/templates/clips/changelog/2026-09-15-recordings-include-a-structured-timeline-of-browser-interact.md +6 -0
- package/corpus/templates/clips/chrome-extension/public/manifest.json +7 -0
- package/corpus/templates/clips/chrome-extension/src/background.ts +235 -9
- package/corpus/templates/clips/chrome-extension/src/content-history-bridge.ts +80 -0
- package/corpus/templates/clips/chrome-extension/src/content-script.ts +166 -5
- package/corpus/templates/clips/chrome-extension/vite.config.ts +4 -0
- package/corpus/templates/clips/server/db/schema.ts +3 -0
- package/corpus/templates/clips/server/lib/public-agent-context.ts +40 -2
- package/corpus/templates/clips/server/plugins/db.ts +5 -0
- package/corpus/templates/clips/shared/browser-diagnostics.ts +171 -0
- package/corpus/templates/content/.agents/skills/content/SKILL.md +4 -2
- package/corpus/templates/content/.agents/skills/document-editing/SKILL.md +8 -1
- package/corpus/templates/content/AGENTS.md +5 -6
- package/corpus/templates/content/CHANGELOG.md +24 -0
- package/corpus/templates/content/actions/_document-edit-mutation.ts +74 -23
- package/corpus/templates/content/actions/edit-document.ts +48 -8
- package/corpus/templates/content/server/plugins/agent-chat.ts +1 -1
- package/corpus/templates/design/CHANGELOG.md +22 -0
- package/corpus/templates/factory/CHANGELOG.md +11 -0
- package/corpus/templates/forms/CHANGELOG.md +6 -0
- package/corpus/templates/mail/CHANGELOG.md +68 -0
- package/corpus/templates/slides/CHANGELOG.md +32 -0
- package/dist/automation/index.d.ts +1 -1
- package/dist/client/CommandMenu.js +14 -2
- package/dist/client/settings/AccountSettingsCard.js +3 -2
- package/dist/client/settings/SettingsTabsPage.js +9 -1
- package/dist/client/settings/agent-settings-search.js +5 -1
- package/dist/client/sign-out.d.ts +1 -0
- package/dist/client/sign-out.js +8 -0
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +3 -3
- package/dist/progress/routes.d.ts +1 -1
- package/dist/secrets/routes.d.ts +6 -6
- package/dist/server/core-routes-plugin.d.ts +1 -1
- package/package.json +4 -4
|
@@ -64,7 +64,8 @@ function validateDocumentEditSnapshot(
|
|
|
64
64
|
document: typeof schema.documents.$inferSelect | undefined,
|
|
65
65
|
base: { revision: number; contentHash: string },
|
|
66
66
|
baseRevision: string,
|
|
67
|
-
edits: DocumentTextEdit[],
|
|
67
|
+
edits: DocumentTextEdit[] | undefined,
|
|
68
|
+
initializeContent?: string,
|
|
68
69
|
) {
|
|
69
70
|
if (!document) {
|
|
70
71
|
throw new ActionContractError("Document not found.", {
|
|
@@ -88,7 +89,19 @@ function validateDocumentEditSnapshot(
|
|
|
88
89
|
currentContentHash: beforeHash,
|
|
89
90
|
});
|
|
90
91
|
}
|
|
91
|
-
const resolved =
|
|
92
|
+
const resolved = edits
|
|
93
|
+
? resolveDocumentTextEdits(beforeContent, edits)
|
|
94
|
+
: beforeContent === ""
|
|
95
|
+
? {
|
|
96
|
+
ok: true as const,
|
|
97
|
+
content: initializeContent as string,
|
|
98
|
+
ranges: [{ start: 0, end: 0 }],
|
|
99
|
+
}
|
|
100
|
+
: conflict(
|
|
101
|
+
"DOCUMENT_BODY_NOT_EMPTY",
|
|
102
|
+
"Document initialization requires a literally empty body.",
|
|
103
|
+
{ currentRevision: baseRevision },
|
|
104
|
+
);
|
|
92
105
|
if (!resolved.ok) {
|
|
93
106
|
conflict(
|
|
94
107
|
resolved.error.kind === "missing"
|
|
@@ -178,19 +191,24 @@ export interface DocumentEditCreativeContext {
|
|
|
178
191
|
}>;
|
|
179
192
|
}
|
|
180
193
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
+
type DocumentBodyMutation =
|
|
195
|
+
| { edits: DocumentTextEdit[]; initializeContent?: never }
|
|
196
|
+
| { edits?: never; initializeContent: string };
|
|
197
|
+
|
|
198
|
+
export async function mutateDocumentBody(
|
|
199
|
+
args: {
|
|
200
|
+
documentId: string;
|
|
201
|
+
baseRevision: string;
|
|
202
|
+
idempotencyKey: string;
|
|
203
|
+
creativeContext?: DocumentEditCreativeContext;
|
|
204
|
+
creativeContextDigest?: unknown;
|
|
205
|
+
resolveCreativeContext?: () => Promise<
|
|
206
|
+
DocumentEditCreativeContext | undefined
|
|
207
|
+
>;
|
|
208
|
+
ctx: ActionRunContext;
|
|
209
|
+
db?: Db;
|
|
210
|
+
} & DocumentBodyMutation,
|
|
211
|
+
): Promise<DocumentEditMutationResult> {
|
|
194
212
|
const db = args.db ?? getDb();
|
|
195
213
|
const scope = callerScope(args.ctx);
|
|
196
214
|
const base = parseDocumentRevisionToken(args.baseRevision);
|
|
@@ -203,16 +221,47 @@ export async function mutateDocumentBody(args: {
|
|
|
203
221
|
},
|
|
204
222
|
);
|
|
205
223
|
}
|
|
206
|
-
const normalizedEdits = args.edits
|
|
224
|
+
const normalizedEdits = args.edits?.map(({ find, replace }) => ({
|
|
207
225
|
find,
|
|
208
226
|
replace,
|
|
209
227
|
}));
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
228
|
+
const initializationContent = args.initializeContent;
|
|
229
|
+
if (!normalizedEdits && initializationContent === undefined) {
|
|
230
|
+
throw new ActionContractError("A document body mutation is required.", {
|
|
231
|
+
errorCode: "DOCUMENT_EDIT_MODE_REQUIRED",
|
|
232
|
+
statusCode: 400,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
if (
|
|
236
|
+
initializationContent !== undefined &&
|
|
237
|
+
initializationContent.trim().length === 0
|
|
238
|
+
) {
|
|
239
|
+
throw new ActionContractError(
|
|
240
|
+
"initializeContent must contain non-whitespace content.",
|
|
241
|
+
{
|
|
242
|
+
errorCode: "DOCUMENT_INITIALIZATION_CONTENT_REQUIRED",
|
|
243
|
+
statusCode: 400,
|
|
244
|
+
},
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
const payloadDigest = digest(
|
|
248
|
+
normalizedEdits
|
|
249
|
+
? {
|
|
250
|
+
documentId: args.documentId,
|
|
251
|
+
baseRevision: args.baseRevision,
|
|
252
|
+
edits: normalizedEdits,
|
|
253
|
+
creativeContext:
|
|
254
|
+
args.creativeContextDigest ?? args.creativeContext ?? null,
|
|
255
|
+
}
|
|
256
|
+
: {
|
|
257
|
+
documentId: args.documentId,
|
|
258
|
+
baseRevision: args.baseRevision,
|
|
259
|
+
operation: "initialize",
|
|
260
|
+
initializeContent: initializationContent,
|
|
261
|
+
creativeContext:
|
|
262
|
+
args.creativeContextDigest ?? args.creativeContext ?? null,
|
|
263
|
+
},
|
|
264
|
+
);
|
|
216
265
|
|
|
217
266
|
const readReplay = async (client: Db) => {
|
|
218
267
|
const [stored] = await client
|
|
@@ -250,6 +299,7 @@ export async function mutateDocumentBody(args: {
|
|
|
250
299
|
base,
|
|
251
300
|
args.baseRevision,
|
|
252
301
|
normalizedEdits,
|
|
302
|
+
initializationContent,
|
|
253
303
|
);
|
|
254
304
|
const creativeContext = args.resolveCreativeContext
|
|
255
305
|
? await args.resolveCreativeContext()
|
|
@@ -274,6 +324,7 @@ export async function mutateDocumentBody(args: {
|
|
|
274
324
|
base,
|
|
275
325
|
args.baseRevision,
|
|
276
326
|
normalizedEdits,
|
|
327
|
+
initializationContent,
|
|
277
328
|
);
|
|
278
329
|
const { beforeContent, beforeHash, resolved } = validated;
|
|
279
330
|
|
|
@@ -352,7 +403,7 @@ export async function mutateDocumentBody(args: {
|
|
|
352
403
|
}));
|
|
353
404
|
const result: DocumentEditMutationResult = {
|
|
354
405
|
applied: changed ? resolved.ranges.length : 0,
|
|
355
|
-
total: normalizedEdits
|
|
406
|
+
total: normalizedEdits?.length ?? 1,
|
|
356
407
|
receipt: {
|
|
357
408
|
receiptId,
|
|
358
409
|
outcome: changed ? "applied" : "unchanged",
|
|
@@ -96,6 +96,16 @@ const editDocumentSchema = z.object({
|
|
|
96
96
|
.describe(
|
|
97
97
|
"JSON array of {find, replace} objects for a snapshot-stable batch; use instead of find/replace.",
|
|
98
98
|
),
|
|
99
|
+
initializeContent: z
|
|
100
|
+
.string()
|
|
101
|
+
.min(1)
|
|
102
|
+
.refine((value) => value.trim().length > 0, {
|
|
103
|
+
message: "initializeContent must contain non-whitespace content.",
|
|
104
|
+
})
|
|
105
|
+
.optional()
|
|
106
|
+
.describe(
|
|
107
|
+
"Exact Markdown containing non-whitespace content, used only to initialize a literally empty document body; mutually exclusive with find, replace, and edits.",
|
|
108
|
+
),
|
|
99
109
|
contextPackId: z
|
|
100
110
|
.string()
|
|
101
111
|
.optional()
|
|
@@ -207,7 +217,7 @@ async function resolveEditCreativeContext(args: {
|
|
|
207
217
|
|
|
208
218
|
export default defineAction({
|
|
209
219
|
description:
|
|
210
|
-
"
|
|
220
|
+
"Edit an existing document's Markdown with exact search-and-replace operations, or initialize a literally empty body with initializeContent. Every find string must match exactly once in the immutable base. First call get-document, then pass its baseRevision and a caller-generated idempotencyKey.",
|
|
211
221
|
deferLoading: false,
|
|
212
222
|
mcpTool: true,
|
|
213
223
|
agentInputSchema: externalEditDocumentSchema,
|
|
@@ -223,9 +233,24 @@ export default defineAction({
|
|
|
223
233
|
const isAgentCaller =
|
|
224
234
|
ctx?.caller === "tool" || ctx?.caller === "mcp" || ctx?.caller === "a2a";
|
|
225
235
|
|
|
226
|
-
let edits: TextEdit[];
|
|
236
|
+
let edits: TextEdit[] = [];
|
|
237
|
+
const initializesBody = args.initializeContent !== undefined;
|
|
227
238
|
|
|
228
|
-
if (
|
|
239
|
+
if (
|
|
240
|
+
initializesBody &&
|
|
241
|
+
(args.find !== undefined ||
|
|
242
|
+
args.replace !== undefined ||
|
|
243
|
+
args.edits !== undefined)
|
|
244
|
+
) {
|
|
245
|
+
throw new ActionContractError(
|
|
246
|
+
"initializeContent is mutually exclusive with find, replace, and edits.",
|
|
247
|
+
{ errorCode: "DOCUMENT_EDIT_MODE_CONFLICT", statusCode: 400 },
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (initializesBody) {
|
|
252
|
+
edits = [];
|
|
253
|
+
} else if (Array.isArray(args.edits)) {
|
|
229
254
|
edits = args.edits;
|
|
230
255
|
} else if (args.edits !== undefined) {
|
|
231
256
|
throw new Error("--edits must be a JSON array");
|
|
@@ -233,7 +258,10 @@ export default defineAction({
|
|
|
233
258
|
if (!args.find) throw new Error("--find cannot be empty");
|
|
234
259
|
edits = [{ find: args.find, replace: args.replace ?? "" }];
|
|
235
260
|
} else {
|
|
236
|
-
throw new
|
|
261
|
+
throw new ActionContractError(
|
|
262
|
+
"One of initializeContent, find, or edits is required.",
|
|
263
|
+
{ errorCode: "DOCUMENT_EDIT_MODE_REQUIRED", statusCode: 400 },
|
|
264
|
+
);
|
|
237
265
|
}
|
|
238
266
|
|
|
239
267
|
const access = await assertAccess("document", id, "editor");
|
|
@@ -243,13 +271,19 @@ export default defineAction({
|
|
|
243
271
|
ctx?.caller === "mcp" ||
|
|
244
272
|
ctx?.caller === "webmcp" ||
|
|
245
273
|
ctx?.caller === "a2a";
|
|
246
|
-
if (isExternalCaller) {
|
|
274
|
+
if (isExternalCaller || initializesBody) {
|
|
247
275
|
if (!args.baseRevision || !args.idempotencyKey) {
|
|
248
276
|
throw new ActionContractError(
|
|
249
277
|
"External document edits require baseRevision and idempotencyKey from get-document.",
|
|
250
278
|
{ errorCode: "DOCUMENT_EDIT_PROTOCOL_REQUIRED", statusCode: 400 },
|
|
251
279
|
);
|
|
252
280
|
}
|
|
281
|
+
if (!ctx) {
|
|
282
|
+
throw new ActionContractError(
|
|
283
|
+
"Revisioned document edits require an authenticated caller context.",
|
|
284
|
+
{ errorCode: "CALLER_SCOPE_REQUIRED", statusCode: 401 },
|
|
285
|
+
);
|
|
286
|
+
}
|
|
253
287
|
const isLinkedLocalSource =
|
|
254
288
|
existing.sourceMode === "local-files" &&
|
|
255
289
|
existing.sourceKind !== "folder" &&
|
|
@@ -265,11 +299,14 @@ export default defineAction({
|
|
|
265
299
|
},
|
|
266
300
|
);
|
|
267
301
|
}
|
|
302
|
+
const mutation = initializesBody
|
|
303
|
+
? { initializeContent: args.initializeContent as string }
|
|
304
|
+
: { edits };
|
|
268
305
|
const result = await mutateDocumentBody({
|
|
269
306
|
documentId: id,
|
|
270
307
|
baseRevision: args.baseRevision,
|
|
271
308
|
idempotencyKey: args.idempotencyKey,
|
|
272
|
-
|
|
309
|
+
...mutation,
|
|
273
310
|
creativeContextDigest: {
|
|
274
311
|
contextPackId: args.contextPackId ?? null,
|
|
275
312
|
contextModeOverride: args.contextModeOverride ?? null,
|
|
@@ -290,7 +327,10 @@ export default defineAction({
|
|
|
290
327
|
edit: {
|
|
291
328
|
descriptor: {
|
|
292
329
|
kind: "text",
|
|
293
|
-
quote:
|
|
330
|
+
quote:
|
|
331
|
+
args.initializeContent?.slice(0, 80) ??
|
|
332
|
+
edits?.[0]?.replace.slice(0, 80) ??
|
|
333
|
+
"",
|
|
294
334
|
},
|
|
295
335
|
label: existing.title || undefined,
|
|
296
336
|
},
|
|
@@ -307,7 +347,7 @@ export default defineAction({
|
|
|
307
347
|
output_id: id,
|
|
308
348
|
output_type: "document",
|
|
309
349
|
edit_count: result.applied,
|
|
310
|
-
refine_type: "exact_replace",
|
|
350
|
+
refine_type: initializesBody ? "full_update" : "exact_replace",
|
|
311
351
|
},
|
|
312
352
|
ctx,
|
|
313
353
|
);
|
|
@@ -32,7 +32,7 @@ export default createAgentChatPlugin({
|
|
|
32
32
|
mcp: {
|
|
33
33
|
externalAgents: { writes: "allowlisted" },
|
|
34
34
|
instructions:
|
|
35
|
-
"Find documents with list-documents or search-documents; read
|
|
35
|
+
"Find documents with list-documents or search-documents; read with get-document (pull-document for raw Markdown). Author and persist content with create-document. For body changes use revision-guarded edit-document; pass initializeContent only when get-document returns an empty body. Use update-document for metadata and browser rewrites. For provider data use provider-api-catalog → provider-api-docs → provider-api-request.",
|
|
36
36
|
},
|
|
37
37
|
anonymousOwner: resolvePublicViewerOwner,
|
|
38
38
|
extraContext: publicDocumentExtraContext,
|
|
@@ -9,8 +9,30 @@ from the command menu (Cmd+K → "What's new") or from Settings.
|
|
|
9
9
|
|
|
10
10
|
- A message that only mentions a design, or pastes Design's own guidance back into chat, no longer ends with a false "couldn't confirm that a Design artifact was saved" reply.
|
|
11
11
|
|
|
12
|
+
## 2026-09-14
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- Component duplicates stay linked to their main across Screens, and detaching keeps their current appearance
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- Rapid layout and padding edits preserve previously committed styles.
|
|
21
|
+
- Screen creation preserves drawn dimensions and layer identity for immediate visual edits.
|
|
22
|
+
- Selecting an element in a live React app now anchors to the authored component file and owner instead of the JSX runtime, so source edits and repeated-instance counts target the real code
|
|
23
|
+
- Undo and redo follow the order of edits across screens.
|
|
24
|
+
- Zoom to selection includes selected layers across Screens and the board, and multiline text stays a single Text layer.
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- Creative Context is now in Labs and off by default
|
|
29
|
+
|
|
12
30
|
## 2026-09-13
|
|
13
31
|
|
|
32
|
+
### Improved
|
|
33
|
+
|
|
34
|
+
- Screen layout, style edits, keyboard shortcuts, and edits to AI-generated designs work more reliably through undo and reload
|
|
35
|
+
|
|
14
36
|
### Fixed
|
|
15
37
|
|
|
16
38
|
- Connect Code imports explain when a design-system name is already in use
|
|
@@ -3,6 +3,17 @@
|
|
|
3
3
|
All notable user-facing changes to Chat are documented here. Open it any
|
|
4
4
|
time from the command menu (Cmd+K → "What's new").
|
|
5
5
|
|
|
6
|
+
## 2026-09-14
|
|
7
|
+
|
|
8
|
+
### Improved
|
|
9
|
+
|
|
10
|
+
- Factory PR babysit uses a fair queue so re-queued pull requests are not starved, re-prioritizes open PRs when new review work arrives without a commit, and posts a v2 ping comment with per-thread disposition guidance.
|
|
11
|
+
- PR babysit now terminalizes merged and closed pull requests instead of re-listing them with "Seen before".
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- Factory automations keep display names, Slack channels, and custom prompts through metadata repair, tab switches, and accidental clears.
|
|
16
|
+
|
|
6
17
|
## 2026-09-11
|
|
7
18
|
|
|
8
19
|
### Improved
|
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
All notable user-facing changes to Agent-Native Forms are documented here. Open it any
|
|
4
4
|
time from the command menu (Cmd+K → "What's new").
|
|
5
5
|
|
|
6
|
+
## 2026-09-15
|
|
7
|
+
|
|
8
|
+
### Fixed
|
|
9
|
+
|
|
10
|
+
- Asking the agent for a second, unrelated form now creates a separate form instead of overwriting the one already open in the chat.
|
|
11
|
+
|
|
6
12
|
## 2026-09-12
|
|
7
13
|
|
|
8
14
|
### Fixed
|
|
@@ -3,6 +3,74 @@
|
|
|
3
3
|
All notable user-facing changes to Agent-Native Mail are documented here. Open it any
|
|
4
4
|
time from the command menu (Cmd+K → "What's new") or from Settings.
|
|
5
5
|
|
|
6
|
+
## 2026-09-15
|
|
7
|
+
|
|
8
|
+
### Improved
|
|
9
|
+
|
|
10
|
+
- Mail labels recipient and account controls for keyboard and assistive-technology parity.
|
|
11
|
+
- Match Superhuman's compact compose card geometry on desktop
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- New drafts focus the To field immediately for keyboard-first composing
|
|
16
|
+
|
|
17
|
+
## 2026-09-14
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- Find implemented keyboard shortcuts by context from the Command menu.
|
|
22
|
+
|
|
23
|
+
### Improved
|
|
24
|
+
|
|
25
|
+
- Choose a send time by typing a date or time in natural language.
|
|
26
|
+
- Keyboard navigation keeps recipient suggestions visible
|
|
27
|
+
- Mail Trash shortcuts now also support #
|
|
28
|
+
- Make Mail search clear buttons keyboard-activatable while preserving input focus
|
|
29
|
+
- New and reopened drafts open in a compact compose card, with fullscreen opt-in.
|
|
30
|
+
- Replies can be sent and marked Done with a shortcut or preference
|
|
31
|
+
- Screen readers can identify Mail's Search and clear controls.
|
|
32
|
+
- Send Later and Send + Mark Done are available in the compose command palette
|
|
33
|
+
|
|
34
|
+
### Fixed
|
|
35
|
+
|
|
36
|
+
- Canceled inbox swipes no longer suppress the next tap on a message.
|
|
37
|
+
- Expired multi-key shortcuts no longer fire after a layout refresh.
|
|
38
|
+
- Mail move actions stay on the selected mailbox account
|
|
39
|
+
- Mail now prompts you to finish or clear recipient text before sending or scheduling.
|
|
40
|
+
- Mail triage actions stay on the selected mailbox account
|
|
41
|
+
- Recipient autocomplete resets its highlighted suggestion when the search query changes.
|
|
42
|
+
- Search works on keyboard layouts that require Shift to type the slash key.
|
|
43
|
+
- Starting another draft brings it into view even when an existing draft is minimized.
|
|
44
|
+
- The message-list A shortcut now opens a Reply All draft
|
|
45
|
+
- Forward drafts now retain the original message attachments
|
|
46
|
+
- Undo for archive and trash actions now targets only the latest operation and expires after 10 seconds.
|
|
47
|
+
|
|
48
|
+
## 2026-09-13
|
|
49
|
+
|
|
50
|
+
### Added
|
|
51
|
+
|
|
52
|
+
- Compose can suggest common phrases on desktop when autocomplete is enabled
|
|
53
|
+
|
|
54
|
+
### Improved
|
|
55
|
+
|
|
56
|
+
- Escape clears the command search before closing the palette and returns focus to the control that opened it.
|
|
57
|
+
- Tab stays in compose fields, and a shortcut opens Bcc directly.
|
|
58
|
+
|
|
59
|
+
### Fixed
|
|
60
|
+
|
|
61
|
+
- All Mail and Archive now show their correct keyboard shortcuts in the command palette.
|
|
62
|
+
- Drafts and sent messages now use the selected Gmail account.
|
|
63
|
+
- Fixed Mail to keep scheduled sends bound to the selected account, validate recipients, and report incomplete account and move operations.
|
|
64
|
+
- G+A now opens All Mail instead of Archive.
|
|
65
|
+
- Gmail drafts report account refresh failures clearly, and multi-account inbox sync avoids repeated lookups.
|
|
66
|
+
- Harden scheduled sends and mixed-account moves; localize move confirmations.
|
|
67
|
+
- Mail chooses a usable connected account when the default mailbox cannot refresh
|
|
68
|
+
- Mail fetches labels from a managed Gmail account when its cache is empty alongside OAuth accounts
|
|
69
|
+
- Mail no longer shows a failed OAuth account as connected through a same-address workspace grant.
|
|
70
|
+
- Mail reports Gmail account read failures instead of showing incomplete results as empty.
|
|
71
|
+
- Search and recipient autocomplete keep keyboard selection aligned with current suggestions
|
|
72
|
+
- Search opens and focuses from the command menu
|
|
73
|
+
|
|
6
74
|
## 2026-09-12
|
|
7
75
|
|
|
8
76
|
### Improved
|
|
@@ -3,6 +3,38 @@
|
|
|
3
3
|
All notable user-facing changes to Agent-Native Slides are documented here. Open it any
|
|
4
4
|
time from the command menu (Cmd+K → "What's new").
|
|
5
5
|
|
|
6
|
+
## 2026-09-14
|
|
7
|
+
|
|
8
|
+
### Improved
|
|
9
|
+
|
|
10
|
+
- Deck filter button now shows when the list is limited to your own decks.
|
|
11
|
+
- Slides comments can now target slide objects and positions with full thread actions, reactions, and Google-style shortcut behavior.
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- An attached PDF, PowerPoint, or Word reference is now read before a new deck is generated, and its typography, palette, and page proportions steer the design instead of being silently dropped
|
|
16
|
+
- Creating a design system no longer fails silently when Builder indexing is unavailable.
|
|
17
|
+
- Design system creation from onboarding now opens inline instead of a new tab, so onboarding progress is no longer lost
|
|
18
|
+
- Every icon in the collapsed left sidebar now shows its name on hover
|
|
19
|
+
- Sending a selection, drawing, or pending update to the agent now opens the agent panel instead of appearing to do nothing.
|
|
20
|
+
- Style requests like matching one slide's background across the deck now succeed instead of failing after repeated rejected edits.
|
|
21
|
+
- Switching organizations now shows only decks from the current organization
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- Creative Context is now in Labs and off by default
|
|
26
|
+
|
|
27
|
+
## 2026-09-13
|
|
28
|
+
|
|
29
|
+
### Improved
|
|
30
|
+
|
|
31
|
+
- Control+M adds a slide with the current slide's layout.
|
|
32
|
+
|
|
33
|
+
### Fixed
|
|
34
|
+
|
|
35
|
+
- Slides support keyboard navigation, selection, and reordering in the slide rail.
|
|
36
|
+
- Slides route navigation, selection, clipboard, and reorder shortcuts through the focused editor surface.
|
|
37
|
+
|
|
6
38
|
## 2026-09-12
|
|
7
39
|
|
|
8
40
|
### Fixed
|
|
@@ -186,8 +186,8 @@ export declare function createAutomationCallbackHandler(runtime: AutomationRunti
|
|
|
186
186
|
duplicate?: undefined;
|
|
187
187
|
eventId?: undefined;
|
|
188
188
|
} | {
|
|
189
|
-
error?: undefined;
|
|
190
189
|
accepted: boolean;
|
|
191
190
|
duplicate: boolean;
|
|
192
191
|
eventId: string;
|
|
192
|
+
error?: undefined;
|
|
193
193
|
}>;
|
|
@@ -16,7 +16,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
16
16
|
* </CommandMenu>
|
|
17
17
|
*/
|
|
18
18
|
import { Command as CommandPrimitive, CommandGroup as CommandGroupPrimitive, CommandInput as CommandInputPrimitive, CommandItem as CommandItemPrimitive, CommandList as CommandListPrimitive, CommandSeparator as CommandSeparatorPrimitive, CommandShortcut as CommandShortcutPrimitive, } from "@agent-native/toolkit/ui/command";
|
|
19
|
-
import { IconBook2, IconExternalLink, IconInfoCircle, IconMessage, IconHistory, } from "@tabler/icons-react";
|
|
19
|
+
import { IconBook2, IconExternalLink, IconInfoCircle, IconMessage, IconHistory, IconLogout, } from "@tabler/icons-react";
|
|
20
20
|
import React, { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, } from "react";
|
|
21
21
|
import { parseChangelog } from "../changelog/parse.js";
|
|
22
22
|
import { AboutAgentNativeDialog } from "./AboutAgentNativeDialog.js";
|
|
@@ -24,6 +24,7 @@ import { sendToAgentChat } from "./agent-chat.js";
|
|
|
24
24
|
import { ChangelogDialog, useChangelogSeen } from "./changelog/Changelog.js";
|
|
25
25
|
import { Dialog, DialogContent, DialogTitle } from "./components/ui/dialog.js";
|
|
26
26
|
import { useT } from "./i18n.js";
|
|
27
|
+
import { signOut, SIGN_OUT_SEARCH_TERMS } from "./sign-out.js";
|
|
27
28
|
import { cn } from "./utils.js";
|
|
28
29
|
const CommandMenuContext = createContext(null);
|
|
29
30
|
function useCommandMenuContext() {
|
|
@@ -235,6 +236,16 @@ export function CommandMenu({ open, onOpenChange, children, renderResults, rende
|
|
|
235
236
|
.toLowerCase()
|
|
236
237
|
.includes(search.toLowerCase());
|
|
237
238
|
const showAboutRow = showAbout && aboutRowMatches;
|
|
239
|
+
const signOutLabel = t("agentChat.auth.logOut");
|
|
240
|
+
const showSignOutRow = !search ||
|
|
241
|
+
[signOutLabel, ...SIGN_OUT_SEARCH_TERMS]
|
|
242
|
+
.join(" ")
|
|
243
|
+
.toLowerCase()
|
|
244
|
+
.includes(search.toLowerCase());
|
|
245
|
+
const handleSignOut = useCallback(() => {
|
|
246
|
+
onOpenChange(false);
|
|
247
|
+
void signOut();
|
|
248
|
+
}, [onOpenChange]);
|
|
238
249
|
// Filter children based on search
|
|
239
250
|
const filterChildren = (nodes) => {
|
|
240
251
|
return React.Children.map(nodes, (child) => {
|
|
@@ -298,9 +309,10 @@ export function CommandMenu({ open, onOpenChange, children, renderResults, rende
|
|
|
298
309
|
const hasResults = React.Children.toArray(filteredChildren).some((child) => React.isValidElement(child) &&
|
|
299
310
|
(child.type === CommandGroup || child.type === CommandDocsGroup));
|
|
300
311
|
const dynamicResults = open ? renderResults?.(search) : null;
|
|
301
|
-
const renderList = (results = dynamicResults) => (_jsxs(CommandListPrimitive, { children: [results, hasResults && filteredChildren, showChangelogRow && (_jsxs(_Fragment, { children: [hasResults && _jsx(CommandSeparator, {}), _jsx("div", { className: "p-1", children: _jsxs(CommandItemPrimitive, { className: "cursor-pointer gap-2 py-2", onSelect: openChangelog, children: [_jsx(IconHistory, { className: "h-4 w-4 text-muted-foreground" }), _jsx("span", { children: changelogLabel }), changelogUnseen && (_jsx("span", { className: "ms-auto h-2 w-2 rounded-full bg-primary", "aria-label": "New updates available" }))] }) })] })), showAboutRow && (_jsxs(_Fragment, { children: [(hasResults || showChangelogRow) && _jsx(CommandSeparator, {}), _jsx("div", { className: "p-1", children: _jsxs(CommandItemPrimitive, { className: "cursor-pointer gap-2 py-2", onSelect: openAbout, children: [_jsx(IconInfoCircle, { className: "h-4 w-4 text-muted-foreground" }), _jsx("span", { children: aboutLabel })] }) })] })), showAgentFallback && (_jsxs(_Fragment, { children: [(hasResults ||
|
|
312
|
+
const renderList = (results = dynamicResults) => (_jsxs(CommandListPrimitive, { children: [results, hasResults && filteredChildren, showChangelogRow && (_jsxs(_Fragment, { children: [hasResults && _jsx(CommandSeparator, {}), _jsx("div", { className: "p-1", children: _jsxs(CommandItemPrimitive, { className: "cursor-pointer gap-2 py-2", onSelect: openChangelog, children: [_jsx(IconHistory, { className: "h-4 w-4 text-muted-foreground" }), _jsx("span", { children: changelogLabel }), changelogUnseen && (_jsx("span", { className: "ms-auto h-2 w-2 rounded-full bg-primary", "aria-label": "New updates available" }))] }) })] })), showAboutRow && (_jsxs(_Fragment, { children: [(hasResults || showChangelogRow) && _jsx(CommandSeparator, {}), _jsx("div", { className: "p-1", children: _jsxs(CommandItemPrimitive, { className: "cursor-pointer gap-2 py-2", onSelect: openAbout, children: [_jsx(IconInfoCircle, { className: "h-4 w-4 text-muted-foreground" }), _jsx("span", { children: aboutLabel })] }) })] })), showSignOutRow && (_jsxs(_Fragment, { children: [(hasResults || showChangelogRow || showAboutRow) && (_jsx(CommandSeparator, {})), _jsx("div", { className: "p-1", children: _jsxs(CommandItemPrimitive, { className: "cursor-pointer gap-2 py-2", onSelect: handleSignOut, children: [_jsx(IconLogout, { className: "h-4 w-4 text-muted-foreground rtl:-scale-x-100" }), _jsx("span", { children: signOutLabel })] }) })] })), showAgentFallback && (_jsxs(_Fragment, { children: [(hasResults ||
|
|
302
313
|
showChangelogRow ||
|
|
303
314
|
showAboutRow ||
|
|
315
|
+
showSignOutRow ||
|
|
304
316
|
Boolean(results)) && _jsx(CommandSeparator, {}), _jsx("div", { className: "p-1", children: _jsxs(CommandItemPrimitive, { className: "cursor-pointer gap-2 py-2", onSelect: handleSubmitToAgent, children: [_jsx(IconMessage, { className: "h-4 w-4 text-muted-foreground" }), _jsx("span", { children: search.trim() ? (_jsxs(_Fragment, { children: ["Ask AI:", " ", _jsxs("span", { className: "text-muted-foreground", children: ["\"", search, "\""] })] })) : (_jsx("span", { className: "text-muted-foreground", children: "Ask AI anything..." })) }), search.trim() && (_jsx("span", { className: "ms-auto text-xs text-muted-foreground", children: "\u21B5" }))] }) })] }))] }));
|
|
305
317
|
return (_jsxs(_Fragment, { children: [_jsx(Dialog, { open: open, onOpenChange: (nextOpen) => {
|
|
306
318
|
if (!nextOpen && nestedDialogsRef.current.length > 0)
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { ActionButton, Avatar, IconButton, TextField, } from "@agent-native/toolkit/design-system";
|
|
3
|
-
import { IconCamera, IconCheck, IconLock, IconPencil, } from "@tabler/icons-react";
|
|
3
|
+
import { IconCamera, IconCheck, IconLock, IconLogout, IconPencil, } from "@tabler/icons-react";
|
|
4
4
|
import { useEffect, useRef, useState } from "react";
|
|
5
5
|
import { PASSWORD_MIN_LENGTH } from "../../shared/password-policy.js";
|
|
6
6
|
import { agentNativePath } from "../api-path.js";
|
|
7
7
|
import { Popover, PopoverContent, PopoverTrigger, } from "../components/ui/popover.js";
|
|
8
8
|
import { Tooltip, TooltipContent, TooltipTrigger, } from "../components/ui/tooltip.js";
|
|
9
9
|
import { useT } from "../i18n.js";
|
|
10
|
+
import { signOut } from "../sign-out.js";
|
|
10
11
|
import { useActionMutation, useActionQuery } from "../use-action.js";
|
|
11
12
|
import { uploadAvatar, useAvatarUrl } from "../use-avatar.js";
|
|
12
13
|
import { useSession } from "../use-session.js";
|
|
@@ -236,7 +237,7 @@ export function AccountSettingsForm({ compact = false, }) {
|
|
|
236
237
|
? t("settings.profileSaving")
|
|
237
238
|
: t("settings.profileSave") })] })) : (_jsxs("div", { className: "flex w-full items-center justify-end gap-2 sm:w-80", children: [_jsx("span", { className: "min-w-0 flex-1 truncate text-sm text-foreground", children: displayName }), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(IconButton, { type: "button", intent: "neutral", emphasis: "ghost", size: "compact", icon: _jsx(IconPencil, { size: 14 }), label: t("settings.profileNameEdit"), title: t("settings.profileNameEdit"), disabled: !email, onPress: handleProfileEdit }) }), _jsx(TooltipContent, { children: t("settings.profileNameEdit") })] })] })) }), _jsx(SettingsRow, { id: "timezone", label: t("settings.timezoneLabel", { defaultValue: "Timezone" }), description: t("settings.timezoneHint", {
|
|
238
239
|
defaultValue: "Used for timestamps and scheduled automations.",
|
|
239
|
-
}), control: _jsx(SchedulingTimezoneField, { compact: true }) }), _jsx(PasswordSettings, {})] }));
|
|
240
|
+
}), control: _jsx(SchedulingTimezoneField, { compact: true }) }), _jsx(PasswordSettings, {}), _jsx(SettingsRow, { id: "sign-out", label: t("agentChat.auth.logOut"), icon: _jsx(IconLogout, { className: "size-4" }), control: _jsx(ActionButton, { type: "button", intent: "danger", emphasis: "outline", size: "compact", onPress: () => void signOut(), children: t("agentChat.auth.logOut") }) })] }));
|
|
240
241
|
}
|
|
241
242
|
export function AccountSettingsCard({ className }) {
|
|
242
243
|
return (_jsx("div", { className: cn("w-full", className), children: _jsx(AccountSettingsForm, {}) }));
|
|
@@ -5,7 +5,9 @@ import { useCallback, useEffect, useMemo, useRef, useState, } from "react";
|
|
|
5
5
|
import { Link, useInRouterContext, useLocation } from "react-router";
|
|
6
6
|
import { appMountPath, appMountedPath } from "../../client/api-path.js";
|
|
7
7
|
import { buildSettingsRoute, STANDARD_APP_ROUTES, } from "../../navigation/index.js";
|
|
8
|
+
import { useT } from "../i18n.js";
|
|
8
9
|
import { LabsSettings } from "../labs/LabsSettings.js";
|
|
10
|
+
import { SIGN_OUT_SEARCH_TERMS } from "../sign-out.js";
|
|
9
11
|
import { cn } from "../utils.js";
|
|
10
12
|
function normalizeTabId(value) {
|
|
11
13
|
let decoded = value?.replace(/^#/, "").trim() ?? "";
|
|
@@ -175,6 +177,7 @@ function SettingsTabsPageContent({ general, account, team, whatsNew, extraTabs =
|
|
|
175
177
|
const searchInputRef = useRef(null);
|
|
176
178
|
const autoFocusedSearchRef = useRef(false);
|
|
177
179
|
const controlledHashRef = useRef(null);
|
|
180
|
+
const t = useT();
|
|
178
181
|
const tabs = useMemo(() => {
|
|
179
182
|
const hasOrganizationTab = extraTabs.some((tab) => tab.id === "organization");
|
|
180
183
|
const inlineTabs = extraTabs.filter((tab) => !tab.href);
|
|
@@ -194,7 +197,11 @@ function SettingsTabsPageContent({ general, account, team, whatsNew, extraTabs =
|
|
|
194
197
|
label: accountLabel,
|
|
195
198
|
icon: IconUserCircle,
|
|
196
199
|
content: account,
|
|
197
|
-
keywords:
|
|
200
|
+
keywords: [
|
|
201
|
+
"profile photo avatar identity signed in email name",
|
|
202
|
+
...SIGN_OUT_SEARCH_TERMS,
|
|
203
|
+
t("agentChat.auth.logOut"),
|
|
204
|
+
].join(" "),
|
|
198
205
|
});
|
|
199
206
|
}
|
|
200
207
|
next.push(...inlineTabs);
|
|
@@ -248,6 +255,7 @@ function SettingsTabsPageContent({ general, account, team, whatsNew, extraTabs =
|
|
|
248
255
|
teamLabel,
|
|
249
256
|
whatsNew,
|
|
250
257
|
whatsNewLabel,
|
|
258
|
+
t,
|
|
251
259
|
]);
|
|
252
260
|
const fallbackTab = tabs.some((tab) => tab.id === defaultTab)
|
|
253
261
|
? defaultTab
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { mcpSettingsMessagesForLocale } from "../../localization/mcp-settings-messages.js";
|
|
2
|
+
import { SIGN_OUT_SEARCH_TERMS } from "../sign-out.js";
|
|
2
3
|
export const SETTINGS_SECTION_IDS = new Set([
|
|
3
4
|
"account",
|
|
4
5
|
"llm",
|
|
@@ -61,7 +62,10 @@ export const WORKSPACE_SETTINGS_SECTIONS = [
|
|
|
61
62
|
const SETTINGS_SECTION_SEARCH_META = {
|
|
62
63
|
account: {
|
|
63
64
|
label: "Account",
|
|
64
|
-
keywords:
|
|
65
|
+
keywords: [
|
|
66
|
+
"profile photo avatar identity signed in email name timezone time zone schedule scheduling clock",
|
|
67
|
+
...SIGN_OUT_SEARCH_TERMS,
|
|
68
|
+
].join(" "),
|
|
65
69
|
},
|
|
66
70
|
llm: {
|
|
67
71
|
label: "LLM",
|
package/dist/client/sign-out.js
CHANGED
|
@@ -27,6 +27,14 @@ import { buildSignInReturnHref } from "./require-session.js";
|
|
|
27
27
|
import { beginSignOut, completeSignOut } from "./use-session.js";
|
|
28
28
|
const LOGOUT_PATH = "/_agent-native/auth/logout";
|
|
29
29
|
const SIGN_OUT_REQUEST_TIMEOUT_MS = 15_000;
|
|
30
|
+
export const SIGN_OUT_SEARCH_TERMS = [
|
|
31
|
+
"log out",
|
|
32
|
+
"log-out",
|
|
33
|
+
"logout",
|
|
34
|
+
"sign out",
|
|
35
|
+
"sign-out",
|
|
36
|
+
"signout",
|
|
37
|
+
];
|
|
30
38
|
let signOutOperation = null;
|
|
31
39
|
function setBetaRedirectSignOutSignal() {
|
|
32
40
|
const signOutWindow = window;
|
|
@@ -62,11 +62,11 @@ export declare const postAwareness: import("h3").EventHandlerWithFetch<import("h
|
|
|
62
62
|
error: string;
|
|
63
63
|
states?: undefined;
|
|
64
64
|
} | {
|
|
65
|
+
error?: undefined;
|
|
65
66
|
states: {
|
|
66
67
|
clientId: number;
|
|
67
68
|
state: string;
|
|
68
69
|
}[];
|
|
69
|
-
error?: undefined;
|
|
70
70
|
}>>;
|
|
71
71
|
/**
|
|
72
72
|
* GET /_agent-native/collab/:docId/users
|
|
@@ -77,9 +77,9 @@ export declare const getActiveUsers: import("h3").EventHandlerWithFetch<import("
|
|
|
77
77
|
error: string;
|
|
78
78
|
users?: undefined;
|
|
79
79
|
} | {
|
|
80
|
+
error?: undefined;
|
|
80
81
|
users: {
|
|
81
82
|
clientId: number;
|
|
82
83
|
lastSeen: number;
|
|
83
84
|
}[];
|
|
84
|
-
error?: undefined;
|
|
85
85
|
}>>;
|
package/dist/collab/routes.d.ts
CHANGED
|
@@ -26,8 +26,8 @@ export declare const getCollabState: import("h3").EventHandlerWithFetch<import("
|
|
|
26
26
|
* Body: { update: string (base64), requestSource?: string }
|
|
27
27
|
*/
|
|
28
28
|
export declare const postCollabUpdate: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
29
|
-
ok?: undefined;
|
|
30
29
|
error: string;
|
|
30
|
+
ok?: undefined;
|
|
31
31
|
} | {
|
|
32
32
|
error?: undefined;
|
|
33
33
|
ok: boolean;
|