@agent-native/core-corpus 0.210.2 → 0.211.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/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +41 -2
- package/corpus/templates/analytics/.agents/skills/data-querying/SKILL.md +4 -2
- package/corpus/templates/analytics/CHANGELOG.md +18 -0
- package/corpus/templates/analytics/actions/build-data-index.ts +11 -2
- package/corpus/templates/analytics/actions/configure-content-collection.ts +29 -4
- package/corpus/templates/analytics/actions/configure-first-party-events-model.ts +9 -7
- package/corpus/templates/analytics/actions/create-analytics-public-key.ts +4 -0
- package/corpus/templates/analytics/actions/estimate-content-signups-cost.ts +72 -0
- package/corpus/templates/analytics/actions/estimate-query-cost.ts +16 -134
- package/corpus/templates/analytics/actions/list-sigma-workbooks.ts +123 -42
- package/corpus/templates/analytics/actions/migrate-sigma-workbook.ts +20 -17
- package/corpus/templates/analytics/actions/revoke-analytics-public-key.ts +4 -0
- package/corpus/templates/analytics/app/components/dashboard/SigmaMigrationDialog.tsx +246 -680
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +1 -1
- package/corpus/templates/analytics/app/i18n/en-US.ts +54 -55
- package/corpus/templates/analytics/app/pages/DashboardOverview.tsx +0 -1
- package/corpus/templates/analytics/app/pages/DataSources.tsx +56 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/filter-vars.ts +4 -3
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +26 -8
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/interpolate.ts +3 -1
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/panel-cost-notice.ts +172 -0
- package/corpus/templates/analytics/app/pages/data-sources/SearchConsoleDetail.tsx +441 -0
- package/corpus/templates/analytics/changelog/2026-10-15-a-content-dashboard-lists-every-blog-article-with-its-author.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-15-data-sources-has-a-google-search-console-card-that-shows-the.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-16-the-content-dashboard-can-show-signups-per-blog-article.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-19-creating-or-revoking-a-public-analytics-key-now-asks-you-to-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-19-search-sigma-workbooks-by-name-and-open-ones-a-teammate-alre.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-20-blog-signup-attribution-excludes-signups-before-the-first-vi.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-20-collapsed-analytics-navigation-starts-at-the-top.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-20-content-keeps-successful-coverage-dates-and-missing-day-gaps.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-20-private-github-repositories-can-be-indexed-with-your-connect.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-20-search-console-preserves-backfill-and-paused-collection.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-20-search-console-refreshes-historical-coverage-when-properties.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-20-search-console-setup-keeps-saved-properties-readable-and-sup.md +6 -0
- package/corpus/templates/analytics/seeds/dashboards/content-view.json +182 -0
- package/corpus/templates/analytics/server/db/schema.ts +1 -0
- package/corpus/templates/analytics/server/lib/bi-migration/external-dashboards-store.ts +56 -18
- package/corpus/templates/analytics/server/lib/bi-migration/unfinished-import.ts +64 -0
- package/corpus/templates/analytics/server/lib/builtin-dashboard-pass.ts +17 -2
- package/corpus/templates/analytics/server/lib/content-metrics/collect.ts +2 -0
- package/corpus/templates/analytics/server/lib/content-metrics/config.ts +92 -3
- package/corpus/templates/analytics/server/lib/content-metrics/legacy-content-view.json +172 -0
- package/corpus/templates/analytics/server/lib/content-metrics/migration.ts +2 -0
- package/corpus/templates/analytics/server/lib/content-metrics/steps/daily-metrics.ts +8 -3
- package/corpus/templates/analytics/server/lib/content-metrics/steps/search-console.ts +16 -3
- package/corpus/templates/analytics/server/lib/content-metrics/steps/signups.ts +240 -0
- package/corpus/templates/analytics/server/lib/content-metrics/store.ts +5 -0
- package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +21 -0
- package/corpus/templates/analytics/server/lib/dashboard-panel-verification.ts +300 -5
- package/corpus/templates/analytics/server/lib/dashboard-seeds.ts +2 -0
- package/corpus/templates/analytics/server/lib/dashboards-store.ts +28 -0
- package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +128 -24
- package/corpus/templates/analytics/server/lib/google-analytics.ts +56 -16
- package/corpus/templates/analytics/server/lib/index-build-github.ts +5 -0
- package/corpus/templates/analytics/server/lib/provider-credentials.ts +47 -2
- package/corpus/templates/analytics/server/lib/query-cost-estimate.ts +184 -5
- package/corpus/templates/analytics/server/plugins/db.ts +9 -1
- package/corpus/templates/analytics/shared/builtin-dashboard-ids.ts +2 -0
- package/corpus/templates/analytics/shared/search-console-property.ts +78 -0
- package/corpus/templates/assets/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/brain/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/calendar/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/calendar/actions/delete-event.ts +1 -3
- package/corpus/templates/calendar/actions/delete-events.ts +1 -2
- package/corpus/templates/calendar/actions/event-action-helpers.ts +0 -4
- package/corpus/templates/calendar/actions/update-event.ts +2 -6
- package/corpus/templates/chat/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/clips/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/clips/CHANGELOG.md +11 -24
- package/corpus/templates/clips/actions/create-recording-upload.ts +5 -1
- package/corpus/templates/clips/app/components/player/chapter-list.tsx +7 -6
- package/corpus/templates/clips/app/components/player/comment-composer.tsx +5 -1
- package/corpus/templates/clips/app/components/player/player-controls.tsx +1 -0
- package/corpus/templates/clips/app/components/player/scrubber.tsx +4 -2
- package/corpus/templates/clips/app/i18n/en-US.ts +3 -3
- package/corpus/templates/clips/app/lib/extension-auth.ts +70 -0
- package/corpus/templates/clips/app/root.tsx +22 -10
- package/corpus/templates/clips/app/routes/_app.notifications.tsx +3 -0
- package/corpus/templates/clips/app/routes/import.tsx +115 -110
- package/corpus/templates/clips/changelog/2026-10-16-agent-uploads-can-use-free-clips-storage.md +6 -0
- package/corpus/templates/clips/changelog/2026-10-19-agents-see-a-clip-s-frames-as-soon-as-it-finishes-processing.md +6 -0
- package/corpus/templates/clips/changelog/2026-10-19-chapter-list-times-now-match-the-player-s-clock-and-each-cha.md +6 -0
- package/corpus/templates/clips/changelog/2026-10-19-editing-a-comment-puts-the-cursor-after-its-text-and-reply-o.md +6 -0
- package/corpus/templates/clips/changelog/2026-10-19-youtube-import-followups.md +6 -0
- package/corpus/templates/clips/changelog/2026-10-20-locally-saved-desktop-recordings-now-appear-in-the-recovery-.md +6 -0
- package/corpus/templates/clips/desktop/src/app.tsx +29 -186
- package/corpus/templates/clips/desktop/src/components/RecordingRecovery.tsx +171 -16
- package/corpus/templates/clips/desktop/src/lib/record-first.ts +9 -32
- package/corpus/templates/clips/desktop/src/lib/recording-recovery.ts +30 -1
- package/corpus/templates/clips/server/lib/agent-frame-request.ts +5 -2
- package/corpus/templates/clips/server/lib/recording-upload-ticket.ts +8 -0
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +2 -0
- package/corpus/templates/content/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/content/CHANGELOG.md +12 -0
- package/corpus/templates/content/actions/_content-database-personal-view.ts +5 -15
- package/corpus/templates/content/actions/_database-navigation.ts +3 -3
- package/corpus/templates/content/actions/_database-utils.ts +10 -22
- package/corpus/templates/content/actions/_database-view-setup.ts +16 -76
- package/corpus/templates/content/actions/create-comment-ai-suggestion.ts +93 -44
- package/corpus/templates/content/actions/export-document.ts +2 -14
- package/corpus/templates/content/actions/suggest-document-edit.ts +5 -7
- package/corpus/templates/content/actions/update-content-database-view.ts +2 -14
- package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +86 -30
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +64 -28
- package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +6 -46
- package/corpus/templates/content/app/components/editor/PageDraftRecovery.tsx +72 -7
- package/corpus/templates/content/app/components/editor/TableHoverControls.tsx +292 -103
- package/corpus/templates/content/app/components/editor/ThreadSuggestion.tsx +147 -40
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +108 -11
- package/corpus/templates/content/app/components/editor/database/ContentTable.tsx +2 -1
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +150 -166
- package/corpus/templates/content/app/components/editor/database/database-cell-comments.tsx +515 -0
- package/corpus/templates/content/app/components/editor/database/database-item-comments.tsx +5 -43
- package/corpus/templates/content/app/components/editor/database/filter-sort.ts +8 -783
- package/corpus/templates/content/app/components/editor/live-editor-session.ts +5 -1
- package/corpus/templates/content/app/components/editor/page-draft-journal.ts +61 -1
- package/corpus/templates/content/app/components/editor/property-comments.tsx +35 -4
- package/corpus/templates/content/app/components/editor/property-date-format.ts +45 -0
- package/corpus/templates/content/app/components/editor/thread-suggestions.ts +30 -0
- package/corpus/templates/content/app/components/editor/useDocumentReconcileRecovery.ts +23 -4
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +36 -10
- package/corpus/templates/content/app/global.css +23 -0
- package/corpus/templates/content/app/i18n-data.ts +1 -0
- package/corpus/templates/content/changelog/2026-10-16-opening-a-page-in-a-new-tab-while-another-tab-is-still-savin.md +6 -0
- package/corpus/templates/content/changelog/2026-10-16-typing-steadily-in-one-tab-no-longer-makes-another-open-tab-.md +6 -0
- package/corpus/templates/content/changelog/2026-10-19-database-views-can-filter-the-same-property-more-than-once-n.md +6 -0
- package/corpus/templates/content/changelog/2026-10-19-edits-made-from-a-block-s-menu-or-a-table-s-row-and-column-m.md +6 -0
- package/corpus/templates/content/changelog/2026-10-19-starting-a-comment-puts-the-cursor-in-the-comment-box-ready-.md +6 -0
- package/corpus/templates/content/changelog/2026-10-19-table-row-and-column-options-now-appear-for-the-row-or-colum.md +6 -0
- package/corpus/templates/content/docs/product/capabilities/content.comment.page-owned.md +5 -1
- package/corpus/templates/content/docs/product/capabilities/content.revision.suggestions.md +3 -1
- package/corpus/templates/content/e2e/helpers.ts +10 -0
- package/corpus/templates/content/shared/api.ts +18 -12
- package/corpus/templates/content/shared/database-query.ts +107 -13
- package/corpus/templates/content/shared/suggestion-diff.ts +35 -0
- package/corpus/templates/crm/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/design/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/design/CHANGELOG.md +2 -0
- package/corpus/templates/design/actions/delete-file.ts +1 -4
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +10 -1
- package/corpus/templates/design/app/components/visual-editor/ReviewCanvasPins.tsx +24 -4
- package/corpus/templates/design/app/pages/design-editor/commands/mode-change.ts +14 -9
- package/corpus/templates/design/app/pages/design-editor/domains/use-editor-modes.ts +23 -2
- package/corpus/templates/design/app/pages/design-editor/tool-state.ts +29 -0
- package/corpus/templates/design/changelog/2026-10-19-reply-and-edit-on-a-comment-in-the-sidebar-or-on-a-canvas-pi.md +6 -0
- package/corpus/templates/design/changelog/2026-10-20-comments-return-to-the-mode-they-came-from-and-layers-search.md +6 -0
- package/corpus/templates/design/e2e/global-setup.ts +185 -85
- package/corpus/templates/dispatch/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/dispatch/CHANGELOG.md +4 -0
- package/corpus/templates/dispatch/actions/apply-workspace-connection-setup.ts +4 -0
- package/corpus/templates/dispatch/actions/delete-workspace-connection.ts +4 -0
- package/corpus/templates/dispatch/actions/set-workspace-connection-grant.ts +4 -0
- package/corpus/templates/dispatch/actions/upsert-workspace-connection.ts +4 -0
- package/corpus/templates/dispatch/changelog/2026-10-19-vault-secrets-grants-link-tokens-app-access-and-workspace-co.md +6 -0
- package/corpus/templates/factory/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +105 -33
- package/corpus/templates/factory/.agents/skills/review-latest-feedback/references/channel-inventory.md +128 -74
- package/corpus/templates/factory/.agents/skills/review-latest-feedback/references/slack-reaction-replies.md +39 -11
- package/corpus/templates/forms/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/mail/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/mail/.agents/skills/contacts-and-crm/SKILL.md +22 -17
- package/corpus/templates/mail/AGENTS.md +1 -1
- package/corpus/templates/mail/CHANGELOG.md +14 -0
- package/corpus/templates/mail/actions/provider-api-request.ts +3 -3
- package/corpus/templates/mail/app/components/email/IntegrationsSidebar.tsx +211 -54
- package/corpus/templates/mail/app/hooks/use-inbox-threads.ts +26 -2
- package/corpus/templates/mail/app/i18n/en-US.ts +5 -1
- package/corpus/templates/mail/app/lib/gong-connection.ts +27 -0
- package/corpus/templates/mail/app/lib/thread-cache.ts +110 -9
- package/corpus/templates/mail/app/root.tsx +10 -2
- package/corpus/templates/mail/changelog/2026-10-20-connected-gong-settings-now-open-the-shared-integration-mana.md +6 -0
- package/corpus/templates/mail/changelog/2026-10-20-gong-connection-details-now-match-the-call-data-available-in.md +6 -0
- package/corpus/templates/mail/changelog/2026-10-20-improve-gong-setup-with-a-click-to-connect-flow-and-permissi.md +6 -0
- package/corpus/templates/mail/changelog/2026-10-20-mail-lets-you-retry-when-it-cannot-check-your-gong-access.md +6 -0
- package/corpus/templates/mail/changelog/2026-10-20-mail-thread-cache-stays-isolated-when-mailbox-scope-changes.md +6 -0
- package/corpus/templates/mail/changelog/2026-10-20-the-inbox-and-recently-opened-threads-now-open-instantly-fro.md +6 -0
- package/corpus/templates/mail/server/handlers/emails.ts +9 -0
- package/corpus/templates/plan/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/plan/CHANGELOG.md +4 -0
- package/corpus/templates/plan/app/pages/PlansPage.tsx +173 -33
- package/corpus/templates/plan/changelog/2026-10-19-opening-a-comment-thread-puts-the-cursor-in-the-reply-box-an.md +6 -0
- package/corpus/templates/slides/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +6 -42
- package/corpus/templates/slides/app/components/editor/FloatingToolbar.tsx +3 -7
- package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +5 -14
- package/corpus/templates/slides/app/i18n/en-US.ts +0 -2
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +27 -33
- package/corpus/templates/slides/scripts/edit-fidelity/run.ts +1 -3
- package/corpus/templates/tasks/.agents/skills/actions/references/action-fields.md +5 -3
- package/package.json +1 -1
|
@@ -8,16 +8,18 @@ export type VideoStorageStatus =
|
|
|
8
8
|
| "missing"
|
|
9
9
|
| "unavailable";
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
export function shouldPollVideoStorageStatus(
|
|
12
|
+
authenticated: boolean,
|
|
13
|
+
status: VideoStorageStatus,
|
|
14
|
+
): boolean {
|
|
15
|
+
return authenticated && status !== "configured";
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Storage status never enables local recording when the configured mode is off. */
|
|
16
19
|
export function effectiveLocalRecordingMode(
|
|
17
20
|
mode: LocalRecordingMode,
|
|
18
|
-
storageStatus: VideoStorageStatus,
|
|
19
21
|
): LocalRecordingMode {
|
|
20
|
-
return mode
|
|
22
|
+
return mode;
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
/** A recording saved to disk because storage was not connected yet. */
|
|
@@ -32,31 +34,6 @@ export interface RecordFirstFile extends LocalExportedFile {
|
|
|
32
34
|
stagedRecordingId?: string;
|
|
33
35
|
}
|
|
34
36
|
|
|
35
|
-
/**
|
|
36
|
-
* What one file of a capture holds. A separate desktop file carries the
|
|
37
|
-
* screen and the take's audio but no camera; a separate camera file holds
|
|
38
|
-
* only camera video.
|
|
39
|
-
*/
|
|
40
|
-
export function recordFirstMediaFlags(
|
|
41
|
-
role: string,
|
|
42
|
-
take: { hasAudio: boolean; hasCamera: boolean },
|
|
43
|
-
): { hasAudio: boolean; hasCamera: boolean } {
|
|
44
|
-
if (role === "desktop") return { hasAudio: take.hasAudio, hasCamera: false };
|
|
45
|
-
if (role === "camera") return { hasAudio: false, hasCamera: true };
|
|
46
|
-
return take;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* The files a stopped record-first capture leaves to upload: its composed
|
|
51
|
-
* file, or every file it wrote when there is none.
|
|
52
|
-
*/
|
|
53
|
-
export function recordFirstFilesToQueue<T extends { role: string }>(
|
|
54
|
-
files: readonly T[],
|
|
55
|
-
): T[] {
|
|
56
|
-
const composed = files.find((file) => file.role === "composed");
|
|
57
|
-
return composed ? [composed] : [...files];
|
|
58
|
-
}
|
|
59
|
-
|
|
60
37
|
/**
|
|
61
38
|
* Per server and account, so a file only ever uploads to who recorded it. A
|
|
62
39
|
* file saved while signed out (an expired session) goes under "unclaimed"
|
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
import type { PendingBrowserRecordingUpload } from "./recorder";
|
|
2
2
|
|
|
3
|
+
export type RecordFirstFileUploadError = {
|
|
4
|
+
message: string;
|
|
5
|
+
missing: boolean;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export type LocalRecoveryFile = {
|
|
9
|
+
path: string;
|
|
10
|
+
fileName: string;
|
|
11
|
+
savedAt: string;
|
|
12
|
+
error?: string;
|
|
13
|
+
missing?: boolean;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export function mapRecordFirstFilesForRecovery(
|
|
17
|
+
files: readonly Pick<LocalRecoveryFile, "path" | "fileName" | "savedAt">[],
|
|
18
|
+
errors: Record<string, RecordFirstFileUploadError>,
|
|
19
|
+
): LocalRecoveryFile[] {
|
|
20
|
+
return files.map((file) => {
|
|
21
|
+
const failure = errors[file.path];
|
|
22
|
+
return {
|
|
23
|
+
path: file.path,
|
|
24
|
+
fileName: file.fileName,
|
|
25
|
+
savedAt: file.savedAt,
|
|
26
|
+
...(failure ? { error: failure.message, missing: failure.missing } : {}),
|
|
27
|
+
};
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
3
31
|
export interface PendingNativeUpload {
|
|
4
32
|
kind: "native";
|
|
5
33
|
recordingId: string;
|
|
@@ -37,8 +65,9 @@ export function recordingRecoveryKey(upload: PendingDesktopUpload): string {
|
|
|
37
65
|
export function shouldShowRecordingRecoveryBanner(
|
|
38
66
|
view: string,
|
|
39
67
|
authenticated: boolean,
|
|
68
|
+
hasLocalFiles = false,
|
|
40
69
|
): boolean {
|
|
41
|
-
return
|
|
70
|
+
return view === "recorder" && (authenticated || hasLocalFiles);
|
|
42
71
|
}
|
|
43
72
|
|
|
44
73
|
export function classifyRecordingRecoveryError(
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
REDACTION_HOLD_MESSAGE,
|
|
22
22
|
} from "./pending-redactions.js";
|
|
23
23
|
import {
|
|
24
|
+
applyAgentJsonHeaders,
|
|
24
25
|
CLIPS_AGENT_ACCESS_PARAM,
|
|
25
26
|
describeAgentAccessFailure,
|
|
26
27
|
loadPublicAgentAccess,
|
|
@@ -37,8 +38,10 @@ export function sendFrameJson<T>(
|
|
|
37
38
|
headers: Record<string, string> = {},
|
|
38
39
|
): T {
|
|
39
40
|
setResponseStatus(event, status);
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
// These paths end in .jpg, so an error without its own Cache-Control gets
|
|
42
|
+
// the CDN's 4-hour image default, and a client could keep a "still
|
|
43
|
+
// processing" answer after the clip is ready.
|
|
44
|
+
applyAgentJsonHeaders(event);
|
|
42
45
|
for (const [name, value] of Object.entries(headers)) {
|
|
43
46
|
setResponseHeader(event, name, value);
|
|
44
47
|
}
|
|
@@ -27,6 +27,8 @@ export interface RecordingUploadTicket extends Record<string, unknown> {
|
|
|
27
27
|
tokenHash: string;
|
|
28
28
|
reservedAt: number;
|
|
29
29
|
expiresAt: number;
|
|
30
|
+
/** The creator's auth user id, which a hosted-storage reservation is held under. */
|
|
31
|
+
authUserId?: string;
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
interface RecordingUploadTickets extends Record<string, unknown> {
|
|
@@ -37,6 +39,7 @@ export interface RecordingUploadTicketOwner {
|
|
|
37
39
|
recordingId: string;
|
|
38
40
|
ownerEmail: string;
|
|
39
41
|
orgId: string;
|
|
42
|
+
authUserId?: string;
|
|
40
43
|
}
|
|
41
44
|
|
|
42
45
|
function tokenHash(token: string): string {
|
|
@@ -180,6 +183,7 @@ async function compareAndSwapTickets(
|
|
|
180
183
|
export async function createRecordingUploadTicket(
|
|
181
184
|
ownerEmail: string,
|
|
182
185
|
recordingId: string,
|
|
186
|
+
authUserId?: string,
|
|
183
187
|
): Promise<RecordingUploadTicket & { token: string }> {
|
|
184
188
|
const token = `${TOKEN_PREFIX}${encodeOwner(ownerEmail)}.${crypto.randomBytes(32).toString("base64url")}`;
|
|
185
189
|
const reservedAt = Date.now();
|
|
@@ -188,6 +192,7 @@ export async function createRecordingUploadTicket(
|
|
|
188
192
|
tokenHash: tokenHash(token),
|
|
189
193
|
reservedAt,
|
|
190
194
|
expiresAt: reservedAt + RECORDING_UPLOAD_TICKET_TTL_MS,
|
|
195
|
+
...(authUserId ? { authUserId } : {}),
|
|
191
196
|
};
|
|
192
197
|
|
|
193
198
|
for (let attempt = 0; attempt < MAX_CAS_ATTEMPTS; attempt += 1) {
|
|
@@ -296,5 +301,8 @@ export async function requireRecordingUploadTicketOwner(
|
|
|
296
301
|
recordingId,
|
|
297
302
|
ownerEmail: verified.ownerEmail,
|
|
298
303
|
orgId: recording.organizationId,
|
|
304
|
+
...(typeof verified.ticket.authUserId === "string"
|
|
305
|
+
? { authUserId: verified.ticket.authUserId }
|
|
306
|
+
: {}),
|
|
299
307
|
};
|
|
300
308
|
}
|
|
@@ -510,6 +510,7 @@ export async function handleRecordingChunk(
|
|
|
510
510
|
recordingId?: string;
|
|
511
511
|
ownerEmail?: string;
|
|
512
512
|
orgId?: string;
|
|
513
|
+
authUserId?: string;
|
|
513
514
|
},
|
|
514
515
|
) {
|
|
515
516
|
const recordingId =
|
|
@@ -579,6 +580,7 @@ export async function handleRecordingChunk(
|
|
|
579
580
|
if (override?.ownerEmail) {
|
|
580
581
|
ownerEmail = override.ownerEmail;
|
|
581
582
|
orgId = override.orgId;
|
|
583
|
+
authUserId = override.authUserId;
|
|
582
584
|
} else {
|
|
583
585
|
try {
|
|
584
586
|
const context = await getEventOwnerContext(event);
|
|
@@ -95,9 +95,9 @@ MCP clients see no `outputSchema` by default. Set `mcpOutputSchema: true` (requi
|
|
|
95
95
|
|
|
96
96
|
`authorize` decides whether the caller may run the action at all. It wraps
|
|
97
97
|
`run`, so it holds at **all six dispatch sites** — agent tool, HTTP, frontend
|
|
98
|
-
hook, MCP, A2A, CLI
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
hook, MCP, A2A, CLI. Use `authorize` for "not everyone may do this"; use
|
|
99
|
+
`needsApproval` to ask a human to bless one call a permitted caller is already
|
|
100
|
+
allowed to make.
|
|
101
101
|
|
|
102
102
|
```ts
|
|
103
103
|
import { coachAccess } from "../lib/access.js"; // defineAppRoles(...)
|
|
@@ -140,4 +140,6 @@ export default defineAction({
|
|
|
140
140
|
|
|
141
141
|
When the gate is truthy and the call isn't yet approved, the loop emits an `approval_required` event and **stops the turn — `run()` never executes**. A predicate gates conditionally (e.g. only external recipients) and **fails closed**: a throw is treated as "approval required". It receives its own copy of the prepared snapshot of the validated arguments, defaults applied, and the approval binds to the snapshot's hash, so an approved call runs only with the arguments that were approved (a Date binds by its time). The human approves via the chat UI's Approve affordance, which re-issues the turn with the call's `approvalKey`, and only then does the action run.
|
|
142
142
|
|
|
143
|
+
Approval is given in the app, so HTTP never grants it. The HTTP action route and the generated Workers entry run a gated call only for the proven app UI (a frontend call from the signed-in user, same origin, holding the short-lived UI capability; `isProvenUiRequest`, the proof `uiOnly` uses) or the app's MCP directory widget. Every other HTTP caller (bearer or API key, MCP OAuth token, org service token, A2A, extension iframe, mobile app) gets `409 approval_required`, and a service identity can never approve. The shared client presents the capability with every write from a signed-in session (minted once per user, reused for nine minutes, never for reads or public pages), so a gated button works on its first request. A call without proof gets `403 ui_capability_required`; the client then re-mints and retries once, which is only the fallback for an expired or rejected capability. Every surface evaluates the gate through `preparedCallNeedsApproval`, so a throwing predicate asks there too. A caller that must run unattended needs a different action, never a bypass of the gate.
|
|
144
|
+
|
|
143
145
|
**Keep approvals rare** — the default is off and almost every action should leave it off. The canonical example is Mail's `send-email` (`needsApproval: true`). See the `security` skill and the Human Approval doc.
|
|
@@ -5,6 +5,16 @@ time from the command menu (Cmd+K → "What's new").
|
|
|
5
5
|
|
|
6
6
|
## 2026-10-19
|
|
7
7
|
|
|
8
|
+
### Improved
|
|
9
|
+
|
|
10
|
+
- Database views can filter one property more than once, number filters add ≥ and ≤, and filter chips name their operator, like "Status: is not done".
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- Edits made from a block's menu or a table's row and column menus, such as deleting a table, adding a row or turning off the header row, are now saved instead of coming back after a reload
|
|
15
|
+
- Starting a comment puts the cursor in the comment box, ready to type.
|
|
16
|
+
- Table row and column options now appear for the row or column under the pointer, Escape selects the cell before leaving the editor, wide tables keep their add-column control on screen on phones, and a tap near a table's edge on a touch screen edits the cell there instead of adding a row or column
|
|
17
|
+
|
|
8
18
|
### Changed
|
|
9
19
|
|
|
10
20
|
- Apps no longer keep server functions and databases warm by default; set `AGENT_NATIVE_ENABLE_KEEP_WARM=1` to opt in.
|
|
@@ -32,6 +42,8 @@ time from the command menu (Cmd+K → "What's new").
|
|
|
32
42
|
|
|
33
43
|
### Fixed
|
|
34
44
|
|
|
45
|
+
- Opening a page in a new tab while another tab is still saving no longer sends a second save or files a copy to History.
|
|
46
|
+
- Typing steadily in one tab no longer makes another open tab show "This page changed elsewhere" after its edits saved.
|
|
35
47
|
- Reopening a page after someone else renamed it no longer adds a copy of your edits to Version History
|
|
36
48
|
|
|
37
49
|
## 2026-10-12
|
|
@@ -4,7 +4,10 @@ import { and, eq, isNull } from "drizzle-orm";
|
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
|
|
6
6
|
import { getDb, schema } from "../server/db/index.js";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
CONTENT_DATABASE_FILTER_OPERATORS,
|
|
9
|
+
CONTENT_DATABASE_PERSONAL_VIEW_OVERRIDES_VERSION,
|
|
10
|
+
} from "../shared/api.js";
|
|
8
11
|
import { resolveContentSpaceAccess } from "./_content-space-access.js";
|
|
9
12
|
import { filesParentPropertyId } from "./_files-system-properties.js";
|
|
10
13
|
|
|
@@ -23,20 +26,7 @@ export const sortSchema = z.object({
|
|
|
23
26
|
export const filterSchema = z.object({
|
|
24
27
|
key: z.string(),
|
|
25
28
|
label: z.string(),
|
|
26
|
-
operator: z.enum(
|
|
27
|
-
"contains",
|
|
28
|
-
"equals",
|
|
29
|
-
"does_not_equal",
|
|
30
|
-
"greater_than",
|
|
31
|
-
"less_than",
|
|
32
|
-
"before",
|
|
33
|
-
"after",
|
|
34
|
-
"between",
|
|
35
|
-
"is_checked",
|
|
36
|
-
"is_unchecked",
|
|
37
|
-
"is_empty",
|
|
38
|
-
"is_not_empty",
|
|
39
|
-
]),
|
|
29
|
+
operator: z.enum(CONTENT_DATABASE_FILTER_OPERATORS),
|
|
40
30
|
value: z.string(),
|
|
41
31
|
filterGroupId: z.string().optional(),
|
|
42
32
|
parentFilterGroupId: z.string().optional(),
|
|
@@ -29,6 +29,7 @@ import type {
|
|
|
29
29
|
ContentDatabaseNavigationSort,
|
|
30
30
|
ContentDatabasePersonalViewOverrides,
|
|
31
31
|
} from "../shared/api.js";
|
|
32
|
+
import { contentDatabaseFilterOperatorNeedsValue } from "../shared/database-query.js";
|
|
32
33
|
import { favoritesSystemIds } from "./_content-favorites.js";
|
|
33
34
|
import { softDeletedDatabaseDocumentExclusions } from "./_document-discovery-query.js";
|
|
34
35
|
import { parseDatabaseViewConfig } from "./_property-utils.js";
|
|
@@ -114,9 +115,8 @@ function selectedFilterValues(value: string) {
|
|
|
114
115
|
|
|
115
116
|
function activeNavigationFilter(filter: ContentDatabaseFilter) {
|
|
116
117
|
return (
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
) || selectedFilterValues(filter.value).length > 0
|
|
118
|
+
!contentDatabaseFilterOperatorNeedsValue(filter.operator) ||
|
|
119
|
+
selectedFilterValues(filter.value).length > 0
|
|
120
120
|
);
|
|
121
121
|
}
|
|
122
122
|
|
|
@@ -32,14 +32,15 @@ import {
|
|
|
32
32
|
parseDocumentFavorite,
|
|
33
33
|
parseDocumentHideFromSearch,
|
|
34
34
|
} from "../server/lib/documents.js";
|
|
35
|
-
import
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
35
|
+
import {
|
|
36
|
+
CONTENT_DATABASE_FILTER_OPERATORS,
|
|
37
|
+
type ContentDatabaseBodyHydration,
|
|
38
|
+
type ContentDatabaseItem,
|
|
39
|
+
type ContentDatabaseMembership,
|
|
40
|
+
type ContentDatabaseResponse,
|
|
41
|
+
type ContentDatabaseTableQuery,
|
|
42
|
+
type ContentSidebarViewOrder,
|
|
43
|
+
type DocumentProperty,
|
|
43
44
|
} from "../shared/api.js";
|
|
44
45
|
import {
|
|
45
46
|
applyContentDatabaseTableQuery,
|
|
@@ -147,20 +148,7 @@ export const contentDatabaseTableQuerySchema = z
|
|
|
147
148
|
z.object({
|
|
148
149
|
key: z.string(),
|
|
149
150
|
label: z.string(),
|
|
150
|
-
operator: z.enum(
|
|
151
|
-
"contains",
|
|
152
|
-
"equals",
|
|
153
|
-
"does_not_equal",
|
|
154
|
-
"greater_than",
|
|
155
|
-
"less_than",
|
|
156
|
-
"before",
|
|
157
|
-
"after",
|
|
158
|
-
"between",
|
|
159
|
-
"is_checked",
|
|
160
|
-
"is_unchecked",
|
|
161
|
-
"is_empty",
|
|
162
|
-
"is_not_empty",
|
|
163
|
-
]),
|
|
151
|
+
operator: z.enum(CONTENT_DATABASE_FILTER_OPERATORS),
|
|
164
152
|
value: z.string(),
|
|
165
153
|
filterGroupId: z.string().optional(),
|
|
166
154
|
parentFilterGroupId: z.string().optional(),
|
|
@@ -5,12 +5,13 @@ import { z } from "zod";
|
|
|
5
5
|
|
|
6
6
|
import { getDb, schema } from "../server/db/index.js";
|
|
7
7
|
import { syncPrivateViewIconReferences } from "../server/lib/private-icon-references.js";
|
|
8
|
-
import
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
ContentDatabaseView,
|
|
12
|
-
ContentDatabaseViewConfig,
|
|
8
|
+
import {
|
|
9
|
+
CONTENT_DATABASE_FILTER_OPERATORS,
|
|
10
|
+
type ContentDatabaseFilter,
|
|
11
|
+
type ContentDatabaseView,
|
|
12
|
+
type ContentDatabaseViewConfig,
|
|
13
13
|
} from "../shared/api.js";
|
|
14
|
+
import { contentDatabaseFilterOperatorsForType } from "../shared/database-query.js";
|
|
14
15
|
import type { DocumentPropertyType } from "../shared/properties.js";
|
|
15
16
|
import {
|
|
16
17
|
runDatabaseSetupMutation,
|
|
@@ -31,20 +32,7 @@ const filterSchema = z
|
|
|
31
32
|
.object({
|
|
32
33
|
key: z.string().trim().min(1).max(200),
|
|
33
34
|
label: z.string().max(500),
|
|
34
|
-
operator: z.enum(
|
|
35
|
-
"contains",
|
|
36
|
-
"equals",
|
|
37
|
-
"does_not_equal",
|
|
38
|
-
"greater_than",
|
|
39
|
-
"less_than",
|
|
40
|
-
"before",
|
|
41
|
-
"after",
|
|
42
|
-
"between",
|
|
43
|
-
"is_checked",
|
|
44
|
-
"is_unchecked",
|
|
45
|
-
"is_empty",
|
|
46
|
-
"is_not_empty",
|
|
47
|
-
]),
|
|
35
|
+
operator: z.enum(CONTENT_DATABASE_FILTER_OPERATORS),
|
|
48
36
|
value: z.string(),
|
|
49
37
|
filterGroupId: z.string().max(200).optional(),
|
|
50
38
|
parentFilterGroupId: z.string().max(200).optional(),
|
|
@@ -147,59 +135,6 @@ export type UpdateContentDatabaseViewAgentInput = z.infer<
|
|
|
147
135
|
typeof updateContentDatabaseViewAgentSchema
|
|
148
136
|
>;
|
|
149
137
|
|
|
150
|
-
const operatorsByType: Partial<
|
|
151
|
-
Record<DocumentPropertyType, readonly ContentDatabaseFilterOperator[]>
|
|
152
|
-
> = {
|
|
153
|
-
checkbox: ["is_checked", "is_unchecked"],
|
|
154
|
-
select: ["contains", "does_not_equal", "is_empty", "is_not_empty"],
|
|
155
|
-
status: ["contains", "does_not_equal", "is_empty", "is_not_empty"],
|
|
156
|
-
multi_select: ["contains", "does_not_equal", "is_empty", "is_not_empty"],
|
|
157
|
-
person: ["contains", "does_not_equal", "is_empty", "is_not_empty"],
|
|
158
|
-
number: [
|
|
159
|
-
"equals",
|
|
160
|
-
"does_not_equal",
|
|
161
|
-
"greater_than",
|
|
162
|
-
"less_than",
|
|
163
|
-
"is_empty",
|
|
164
|
-
"is_not_empty",
|
|
165
|
-
],
|
|
166
|
-
date: [
|
|
167
|
-
"equals",
|
|
168
|
-
"does_not_equal",
|
|
169
|
-
"before",
|
|
170
|
-
"after",
|
|
171
|
-
"between",
|
|
172
|
-
"is_empty",
|
|
173
|
-
"is_not_empty",
|
|
174
|
-
],
|
|
175
|
-
created_time: [
|
|
176
|
-
"equals",
|
|
177
|
-
"does_not_equal",
|
|
178
|
-
"before",
|
|
179
|
-
"after",
|
|
180
|
-
"between",
|
|
181
|
-
"is_empty",
|
|
182
|
-
"is_not_empty",
|
|
183
|
-
],
|
|
184
|
-
last_edited_time: [
|
|
185
|
-
"equals",
|
|
186
|
-
"does_not_equal",
|
|
187
|
-
"before",
|
|
188
|
-
"after",
|
|
189
|
-
"between",
|
|
190
|
-
"is_empty",
|
|
191
|
-
"is_not_empty",
|
|
192
|
-
],
|
|
193
|
-
};
|
|
194
|
-
|
|
195
|
-
const defaultOperators: readonly ContentDatabaseFilterOperator[] = [
|
|
196
|
-
"contains",
|
|
197
|
-
"equals",
|
|
198
|
-
"does_not_equal",
|
|
199
|
-
"is_empty",
|
|
200
|
-
"is_not_empty",
|
|
201
|
-
];
|
|
202
|
-
|
|
203
138
|
function uniqueReferences(values: readonly string[], field: string) {
|
|
204
139
|
if (new Set(values).size !== values.length) {
|
|
205
140
|
setupError(
|
|
@@ -228,9 +163,10 @@ function validateTablePatch(
|
|
|
228
163
|
values: readonly string[] | undefined,
|
|
229
164
|
allowed: ReadonlySet<string>,
|
|
230
165
|
field: string,
|
|
166
|
+
{ unique = true }: { unique?: boolean } = {},
|
|
231
167
|
) => {
|
|
232
168
|
if (!values) return;
|
|
233
|
-
uniqueReferences(values, field);
|
|
169
|
+
if (unique) uniqueReferences(values, field);
|
|
234
170
|
const invalid = values.find((value) => !allowed.has(value));
|
|
235
171
|
if (invalid) {
|
|
236
172
|
setupError(
|
|
@@ -248,10 +184,13 @@ function validateTablePatch(
|
|
|
248
184
|
columnIds,
|
|
249
185
|
"sorts",
|
|
250
186
|
);
|
|
187
|
+
// A view may filter one property more than once ("Rank is not empty" and
|
|
188
|
+
// "Rank ≤ 25"), so filter keys only need to exist.
|
|
251
189
|
assertReferences(
|
|
252
190
|
patch.filters?.map((filter) => filter.key),
|
|
253
191
|
columnIds,
|
|
254
192
|
"filters",
|
|
193
|
+
{ unique: false },
|
|
255
194
|
);
|
|
256
195
|
assertReferences(
|
|
257
196
|
patch.columnWidths ? Object.keys(patch.columnWidths) : undefined,
|
|
@@ -277,8 +216,9 @@ function validateTablePatch(
|
|
|
277
216
|
}
|
|
278
217
|
for (const filter of patch.filters ?? []) {
|
|
279
218
|
const type = filter.key === "name" ? "text" : typeById.get(filter.key)!;
|
|
280
|
-
|
|
281
|
-
|
|
219
|
+
if (
|
|
220
|
+
!contentDatabaseFilterOperatorsForType(type).includes(filter.operator)
|
|
221
|
+
) {
|
|
282
222
|
setupError(
|
|
283
223
|
"INVALID_VIEW_REFERENCE",
|
|
284
224
|
`Filter operator \"${filter.operator}\" is not valid for property \"${filter.key}\".`,
|
|
@@ -589,5 +529,5 @@ export function filterIsCompatible(
|
|
|
589
529
|
filter: ContentDatabaseFilter,
|
|
590
530
|
type: DocumentPropertyType,
|
|
591
531
|
) {
|
|
592
|
-
return (
|
|
532
|
+
return contentDatabaseFilterOperatorsForType(type).includes(filter.operator);
|
|
593
533
|
}
|