@agent-native/core 0.77.6 → 0.77.8

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.
Files changed (177) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +29 -0
  3. package/corpus/core/package.json +3 -3
  4. package/corpus/core/src/agent/production-agent.ts +18 -0
  5. package/corpus/core/src/agent/thread-data-builder.ts +21 -2
  6. package/corpus/core/src/cli/create.ts +35 -1
  7. package/corpus/core/src/cli/skills.ts +8 -0
  8. package/corpus/core/src/client/AgentPanel.tsx +57 -25
  9. package/corpus/core/src/client/MultiTabAssistantChat.tsx +31 -1
  10. package/corpus/core/src/client/analytics.ts +24 -2
  11. package/corpus/core/src/client/use-agent-engine-configured.ts +29 -15
  12. package/corpus/core/src/client/use-chat-threads.ts +104 -55
  13. package/corpus/core/src/db/ddl-guard.ts +190 -0
  14. package/corpus/core/src/secrets/storage.ts +36 -2
  15. package/corpus/core/src/server/analytics.ts +27 -3
  16. package/corpus/core/src/settings/store.ts +40 -6
  17. package/corpus/core/src/sharing/access.ts +35 -13
  18. package/corpus/core/src/sharing/actions/share-resource.ts +32 -9
  19. package/corpus/core/src/sharing/actions/unshare-resource.ts +25 -2
  20. package/corpus/core/src/sharing/registry.ts +9 -0
  21. package/corpus/core/src/vite/client.ts +3 -0
  22. package/corpus/templates/analytics/actions/data-source-status.ts +25 -14
  23. package/corpus/templates/analytics/app/components/layout/NewAnalysisDialog.tsx +2 -2
  24. package/corpus/templates/analytics/app/lib/data-source-status.ts +47 -6
  25. package/corpus/templates/analytics/app/lib/data-sources.ts +14 -3
  26. package/corpus/templates/analytics/app/pages/DataSources.tsx +24 -5
  27. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/PanelEditorDialog.tsx +15 -43
  28. package/corpus/templates/analytics/changelog/2026-06-25-fixed-hubspot-data-source-status-so-private-app-tokens-legac.md +6 -0
  29. package/corpus/templates/analytics/netlify.toml +1 -0
  30. package/corpus/templates/analytics/server/lib/credential-keys.ts +3 -1
  31. package/corpus/templates/analytics/server/routes/api/test-connection.post.ts +12 -3
  32. package/corpus/templates/assets/app/components/layout/Sidebar.tsx +5 -2
  33. package/corpus/templates/assets/changelog/2026-06-25-create-now-returns-from-agent-settings-to-the-asset-chat-eve.md +6 -0
  34. package/corpus/templates/assets/netlify.toml +1 -0
  35. package/corpus/templates/brain/netlify.toml +1 -0
  36. package/corpus/templates/calendar/app/i18n-data.ts +12 -0
  37. package/corpus/templates/calendar/app/lib/clipboard.ts +58 -0
  38. package/corpus/templates/calendar/app/pages/AvailabilitySettings.tsx +15 -9
  39. package/corpus/templates/calendar/app/pages/BookingLinksPage.tsx +6 -2
  40. package/corpus/templates/calendar/changelog/2026-06-25-booking-link-copy-buttons-no-longer-fail-when-clipboard-perm.md +6 -0
  41. package/corpus/templates/calendar/netlify.toml +1 -0
  42. package/corpus/templates/chat/netlify.toml +1 -0
  43. package/corpus/templates/clips/AGENTS.md +9 -7
  44. package/corpus/templates/clips/actions/add-vocabulary-term.ts +2 -2
  45. package/corpus/templates/clips/actions/archive-recording.ts +5 -2
  46. package/corpus/templates/clips/actions/clear-edits.ts +5 -2
  47. package/corpus/templates/clips/actions/create-folder.ts +7 -2
  48. package/corpus/templates/clips/actions/delete-folder.ts +7 -4
  49. package/corpus/templates/clips/actions/delete-recording-permanent.ts +5 -2
  50. package/corpus/templates/clips/actions/finalize-recording.ts +5 -2
  51. package/corpus/templates/clips/actions/import-loom-recording.ts +2 -1
  52. package/corpus/templates/clips/actions/list-notifications.ts +9 -5
  53. package/corpus/templates/clips/actions/list-organization-state.ts +2 -1
  54. package/corpus/templates/clips/actions/list-recordings.ts +4 -1
  55. package/corpus/templates/clips/actions/move-recording.ts +5 -1
  56. package/corpus/templates/clips/actions/rename-folder.ts +6 -3
  57. package/corpus/templates/clips/actions/request-transcript.ts +5 -2
  58. package/corpus/templates/clips/actions/restore-recording.ts +5 -2
  59. package/corpus/templates/clips/actions/save-browser-diagnostics.ts +21 -8
  60. package/corpus/templates/clips/actions/set-chapters.ts +5 -2
  61. package/corpus/templates/clips/actions/set-thumbnail.ts +5 -2
  62. package/corpus/templates/clips/actions/split-recording.ts +5 -2
  63. package/corpus/templates/clips/actions/stitch-recordings.ts +6 -2
  64. package/corpus/templates/clips/actions/trash-recording.ts +5 -2
  65. package/corpus/templates/clips/actions/undo-edit.ts +5 -2
  66. package/corpus/templates/clips/actions/view-screen.ts +2 -1
  67. package/corpus/templates/clips/app/components/library/recording-card.tsx +24 -7
  68. package/corpus/templates/clips/app/lib/browser-diagnostics-capture.ts +5 -1
  69. package/corpus/templates/clips/changelog/2026-06-25-chrome-extension-recordings-now-upload-large-capture-bursts.md +6 -0
  70. package/corpus/templates/clips/changelog/2026-06-25-console-diagnostics-keep-more-useful-non-secret-details-when.md +6 -0
  71. package/corpus/templates/clips/changelog/2026-06-25-fixed-owner-recognition-so-clips-created-from-desktop-and-we.md +6 -0
  72. package/corpus/templates/clips/chrome-extension/package.json +3 -0
  73. package/corpus/templates/clips/chrome-extension/public/manifest.json +1 -1
  74. package/corpus/templates/clips/chrome-extension/src/background.ts +85 -9
  75. package/corpus/templates/clips/chrome-extension/src/content-script.ts +55 -0
  76. package/corpus/templates/clips/chrome-extension/src/offscreen.ts +79 -3
  77. package/corpus/templates/clips/chrome-extension/src/options.ts +10 -0
  78. package/corpus/templates/clips/chrome-extension/src/overlay.ts +6 -0
  79. package/corpus/templates/clips/chrome-extension/src/permission.ts +8 -1
  80. package/corpus/templates/clips/chrome-extension/src/popup.ts +65 -39
  81. package/corpus/templates/clips/chrome-extension/src/sentry.ts +206 -0
  82. package/corpus/templates/clips/chrome-extension/vite.config.ts +47 -0
  83. package/corpus/templates/clips/netlify.toml +1 -0
  84. package/corpus/templates/clips/server/db/index.ts +1 -0
  85. package/corpus/templates/clips/server/lib/google-calendar-oauth.ts +35 -23
  86. package/corpus/templates/clips/server/lib/public-agent-context.ts +8 -1
  87. package/corpus/templates/clips/server/lib/recordings.ts +18 -2
  88. package/corpus/templates/clips/server/lib/slack-oauth.ts +9 -6
  89. package/corpus/templates/clips/server/plugins/onboarding-calendar.ts +6 -2
  90. package/corpus/templates/clips/server/routes/api/public-recording.get.ts +2 -2
  91. package/corpus/templates/clips/server/routes/api/recordings/[recordingId]/thumbnail.post.ts +5 -2
  92. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/abort.post.ts +5 -2
  93. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +5 -2
  94. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/reset-chunks.post.ts +5 -2
  95. package/corpus/templates/clips/shared/browser-diagnostics.ts +14 -4
  96. package/corpus/templates/content/netlify.toml +1 -0
  97. package/corpus/templates/design/netlify.toml +1 -0
  98. package/corpus/templates/dispatch/netlify.toml +1 -0
  99. package/corpus/templates/forms/netlify.toml +1 -0
  100. package/corpus/templates/macros/netlify.toml +1 -0
  101. package/corpus/templates/mail/app/components/email/EmailList.tsx +7 -3
  102. package/corpus/templates/mail/app/components/email/EmailThread.tsx +4 -1
  103. package/corpus/templates/mail/app/components/layout/AppLayout.tsx +10 -2
  104. package/corpus/templates/mail/app/hooks/use-compose-state.ts +15 -5
  105. package/corpus/templates/mail/app/hooks/use-google-auth.ts +47 -9
  106. package/corpus/templates/mail/changelog/2026-06-25-account-avatars-stay-stable-while-mail-refreshes-google-acco.md +6 -0
  107. package/corpus/templates/mail/changelog/2026-06-25-mail-background-draft-saves-and-thread-prefetches-no-longer.md +6 -0
  108. package/corpus/templates/mail/netlify.toml +1 -0
  109. package/corpus/templates/plan/.agents/skills/visual-plan/references/canvas.md +8 -0
  110. package/corpus/templates/plan/netlify.toml +1 -0
  111. package/corpus/templates/slides/netlify.toml +1 -0
  112. package/corpus/templates/videos/netlify.toml +1 -0
  113. package/dist/agent/production-agent.d.ts.map +1 -1
  114. package/dist/agent/production-agent.js +18 -0
  115. package/dist/agent/production-agent.js.map +1 -1
  116. package/dist/agent/thread-data-builder.d.ts.map +1 -1
  117. package/dist/agent/thread-data-builder.js +23 -2
  118. package/dist/agent/thread-data-builder.js.map +1 -1
  119. package/dist/cli/create.d.ts.map +1 -1
  120. package/dist/cli/create.js +28 -1
  121. package/dist/cli/create.js.map +1 -1
  122. package/dist/cli/skills.d.ts +1 -1
  123. package/dist/cli/skills.d.ts.map +1 -1
  124. package/dist/cli/skills.js +8 -0
  125. package/dist/cli/skills.js.map +1 -1
  126. package/dist/client/AgentPanel.d.ts +8 -2
  127. package/dist/client/AgentPanel.d.ts.map +1 -1
  128. package/dist/client/AgentPanel.js +29 -15
  129. package/dist/client/AgentPanel.js.map +1 -1
  130. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  131. package/dist/client/MultiTabAssistantChat.js +4 -4
  132. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  133. package/dist/client/analytics.d.ts.map +1 -1
  134. package/dist/client/analytics.js +22 -2
  135. package/dist/client/analytics.js.map +1 -1
  136. package/dist/client/use-agent-engine-configured.d.ts.map +1 -1
  137. package/dist/client/use-agent-engine-configured.js +23 -13
  138. package/dist/client/use-agent-engine-configured.js.map +1 -1
  139. package/dist/client/use-chat-threads.d.ts +4 -0
  140. package/dist/client/use-chat-threads.d.ts.map +1 -1
  141. package/dist/client/use-chat-threads.js +49 -4
  142. package/dist/client/use-chat-threads.js.map +1 -1
  143. package/dist/collab/routes.d.ts +2 -2
  144. package/dist/db/ddl-guard.d.ts +88 -0
  145. package/dist/db/ddl-guard.d.ts.map +1 -0
  146. package/dist/db/ddl-guard.js +180 -0
  147. package/dist/db/ddl-guard.js.map +1 -0
  148. package/dist/notifications/routes.d.ts +1 -1
  149. package/dist/observability/routes.d.ts +5 -5
  150. package/dist/progress/routes.d.ts +1 -1
  151. package/dist/resources/handlers.d.ts +3 -3
  152. package/dist/secrets/storage.d.ts.map +1 -1
  153. package/dist/secrets/storage.js +26 -2
  154. package/dist/secrets/storage.js.map +1 -1
  155. package/dist/server/agent-engine-api-key-route.d.ts +2 -2
  156. package/dist/server/analytics.d.ts +4 -0
  157. package/dist/server/analytics.d.ts.map +1 -1
  158. package/dist/server/analytics.js +19 -3
  159. package/dist/server/analytics.js.map +1 -1
  160. package/dist/server/transcribe-voice.d.ts +1 -1
  161. package/dist/settings/store.d.ts.map +1 -1
  162. package/dist/settings/store.js +32 -6
  163. package/dist/settings/store.js.map +1 -1
  164. package/dist/sharing/access.d.ts.map +1 -1
  165. package/dist/sharing/access.js +26 -12
  166. package/dist/sharing/access.js.map +1 -1
  167. package/dist/sharing/actions/share-resource.js +20 -9
  168. package/dist/sharing/actions/share-resource.js.map +1 -1
  169. package/dist/sharing/actions/unshare-resource.js +13 -2
  170. package/dist/sharing/actions/unshare-resource.js.map +1 -1
  171. package/dist/sharing/registry.d.ts +9 -0
  172. package/dist/sharing/registry.d.ts.map +1 -1
  173. package/dist/sharing/registry.js.map +1 -1
  174. package/dist/vite/client.d.ts.map +1 -1
  175. package/dist/vite/client.js +1 -0
  176. package/dist/vite/client.js.map +1 -1
  177. package/package.json +3 -3
@@ -24,7 +24,7 @@ import {
24
24
  type BrowserDiagnosticConsoleLevel,
25
25
  } from "../shared/browser-diagnostics.js";
26
26
 
27
- const REDACTION_VERSION = 1;
27
+ const REDACTION_VERSION = 2;
28
28
 
29
29
  const consoleLevelSchema = z.enum(["debug", "log", "info", "warn", "error"]);
30
30
 
@@ -53,12 +53,19 @@ function truncate(value: string, maxLength: number): string {
53
53
  return value.length > maxLength ? `${value.slice(0, maxLength)}...` : value;
54
54
  }
55
55
 
56
- function redactString(value: string): string {
57
- return redactBrowserDiagnosticString(value);
56
+ function redactString(
57
+ value: string,
58
+ options?: { redactQueryValues?: boolean },
59
+ ): string {
60
+ return redactBrowserDiagnosticString(value, options);
61
+ }
62
+
63
+ function redactUrlString(value: string): string {
64
+ return redactBrowserDiagnosticString(value, { redactQueryValues: true });
58
65
  }
59
66
 
60
67
  function sanitizeUrl(raw: string): string {
61
- const redacted = redactString(raw);
68
+ const redacted = redactUrlString(raw);
62
69
  try {
63
70
  const parsed = new URL(redacted, "https://clips.local");
64
71
  parsed.username = "";
@@ -93,14 +100,17 @@ function sanitizeConsoleLog(entry: z.infer<typeof consoleLogSchema>): {
93
100
  stack?: string;
94
101
  } {
95
102
  const stack = entry.stack
96
- ? truncate(redactString(entry.stack), MAX_BROWSER_DIAGNOSTIC_MESSAGE_LENGTH)
103
+ ? truncate(
104
+ redactString(entry.stack, { redactQueryValues: true }),
105
+ MAX_BROWSER_DIAGNOSTIC_MESSAGE_LENGTH,
106
+ )
97
107
  : "";
98
108
  return {
99
109
  timestampMs: Math.round(entry.timestampMs),
100
110
  elapsedMs: Math.round(entry.elapsedMs),
101
111
  level: entry.level,
102
112
  message: truncate(
103
- redactString(entry.message),
113
+ redactString(entry.message, { redactQueryValues: true }),
104
114
  MAX_BROWSER_DIAGNOSTIC_MESSAGE_LENGTH,
105
115
  ),
106
116
  ...(stack ? { stack } : {}),
@@ -109,10 +119,13 @@ function sanitizeConsoleLog(entry: z.infer<typeof consoleLogSchema>): {
109
119
 
110
120
  function sanitizeNetworkRequest(entry: z.infer<typeof networkRequestSchema>) {
111
121
  const statusText = entry.statusText
112
- ? truncate(redactString(entry.statusText), 120)
122
+ ? truncate(redactString(entry.statusText, { redactQueryValues: true }), 120)
113
123
  : "";
114
124
  const error = entry.error
115
- ? truncate(redactString(entry.error), MAX_BROWSER_DIAGNOSTIC_MESSAGE_LENGTH)
125
+ ? truncate(
126
+ redactString(entry.error, { redactQueryValues: true }),
127
+ MAX_BROWSER_DIAGNOSTIC_MESSAGE_LENGTH,
128
+ )
116
129
  : "";
117
130
  return {
118
131
  timestampMs: Math.round(entry.timestampMs),
@@ -15,7 +15,10 @@ import { and, eq } from "drizzle-orm";
15
15
  import { z } from "zod";
16
16
 
17
17
  import { getDb, schema } from "../server/db/index.js";
18
- import { getCurrentOwnerEmail } from "../server/lib/recordings.js";
18
+ import {
19
+ getCurrentOwnerEmail,
20
+ ownerEmailMatches,
21
+ } from "../server/lib/recordings.js";
19
22
 
20
23
  const ChapterSchema = z.object({
21
24
  startMs: z.coerce.number().int().min(0),
@@ -61,7 +64,7 @@ export default defineAction({
61
64
  .where(
62
65
  and(
63
66
  eq(schema.recordings.id, args.recordingId),
64
- eq(schema.recordings.ownerEmail, ownerEmail),
67
+ ownerEmailMatches(schema.recordings.ownerEmail, ownerEmail),
65
68
  ),
66
69
  );
67
70
  if (!existing) {
@@ -26,7 +26,10 @@ import { z } from "zod";
26
26
 
27
27
  import { parseEdits, serializeEdits } from "../app/lib/timestamp-mapping.js";
28
28
  import { getDb, schema } from "../server/db/index.js";
29
- import { getCurrentOwnerEmail } from "../server/lib/recordings.js";
29
+ import {
30
+ getCurrentOwnerEmail,
31
+ ownerEmailMatches,
32
+ } from "../server/lib/recordings.js";
30
33
  import { assertNativeRecordingMedia } from "./lib/native-media.js";
31
34
 
32
35
  function decodeDataUrl(dataUrl: string): { bytes: Uint8Array; mime: string } {
@@ -88,7 +91,7 @@ export default defineAction({
88
91
  .where(
89
92
  and(
90
93
  eq(schema.recordings.id, args.recordingId),
91
- eq(schema.recordings.ownerEmail, ownerEmail),
94
+ ownerEmailMatches(schema.recordings.ownerEmail, ownerEmail),
92
95
  ),
93
96
  );
94
97
  if (!existing) {
@@ -20,7 +20,10 @@ import {
20
20
  serializeEdits,
21
21
  } from "../app/lib/timestamp-mapping.js";
22
22
  import { getDb, schema } from "../server/db/index.js";
23
- import { getCurrentOwnerEmail } from "../server/lib/recordings.js";
23
+ import {
24
+ getCurrentOwnerEmail,
25
+ ownerEmailMatches,
26
+ } from "../server/lib/recordings.js";
24
27
  import { assertNativeRecordingMedia } from "./lib/native-media.js";
25
28
 
26
29
  export default defineAction({
@@ -44,7 +47,7 @@ export default defineAction({
44
47
  .where(
45
48
  and(
46
49
  eq(schema.recordings.id, args.recordingId),
47
- eq(schema.recordings.ownerEmail, ownerEmail),
50
+ ownerEmailMatches(schema.recordings.ownerEmail, ownerEmail),
48
51
  ),
49
52
  );
50
53
  if (!existing) {
@@ -34,7 +34,11 @@ import { z } from "zod";
34
34
 
35
35
  import { parseEdits, serializeEdits } from "../app/lib/timestamp-mapping.js";
36
36
  import { getDb, schema } from "../server/db/index.js";
37
- import { getCurrentOwnerEmail, nanoid } from "../server/lib/recordings.js";
37
+ import {
38
+ getCurrentOwnerEmail,
39
+ nanoid,
40
+ ownerEmailMatches,
41
+ } from "../server/lib/recordings.js";
38
42
  import { assertNativeRecordingMedia } from "./lib/native-media.js";
39
43
 
40
44
  export default defineAction({
@@ -95,7 +99,7 @@ export default defineAction({
95
99
  .where(
96
100
  and(
97
101
  inArray(schema.recordings.id, ids),
98
- eq(schema.recordings.ownerEmail, ownerEmail),
102
+ ownerEmailMatches(schema.recordings.ownerEmail, ownerEmail),
99
103
  ),
100
104
  );
101
105
  if (sources.length !== ids.length) {
@@ -11,7 +11,10 @@ import { and, eq } from "drizzle-orm";
11
11
  import { z } from "zod";
12
12
 
13
13
  import { getDb, schema } from "../server/db/index.js";
14
- import { getCurrentOwnerEmail } from "../server/lib/recordings.js";
14
+ import {
15
+ getCurrentOwnerEmail,
16
+ ownerEmailMatches,
17
+ } from "../server/lib/recordings.js";
15
18
 
16
19
  export default defineAction({
17
20
  description:
@@ -29,7 +32,7 @@ export default defineAction({
29
32
  .where(
30
33
  and(
31
34
  eq(schema.recordings.id, args.id),
32
- eq(schema.recordings.ownerEmail, ownerEmail),
35
+ ownerEmailMatches(schema.recordings.ownerEmail, ownerEmail),
33
36
  ),
34
37
  );
35
38
  if (!existing) throw new Error(`Recording not found: ${args.id}`);
@@ -19,7 +19,10 @@ import {
19
19
  serializeEdits,
20
20
  } from "../app/lib/timestamp-mapping.js";
21
21
  import { getDb, schema } from "../server/db/index.js";
22
- import { getCurrentOwnerEmail } from "../server/lib/recordings.js";
22
+ import {
23
+ getCurrentOwnerEmail,
24
+ ownerEmailMatches,
25
+ } from "../server/lib/recordings.js";
23
26
  import { assertNativeRecordingMedia } from "./lib/native-media.js";
24
27
 
25
28
  export default defineAction({
@@ -38,7 +41,7 @@ export default defineAction({
38
41
  .where(
39
42
  and(
40
43
  eq(schema.recordings.id, args.recordingId),
41
- eq(schema.recordings.ownerEmail, ownerEmail),
44
+ ownerEmailMatches(schema.recordings.ownerEmail, ownerEmail),
42
45
  ),
43
46
  );
44
47
  if (!existing) {
@@ -23,6 +23,7 @@ import { z } from "zod";
23
23
  import { getDb, schema } from "../server/db/index.js";
24
24
  import {
25
25
  getActiveOrganizationId,
26
+ ownerEmailMatches,
26
27
  parseSpaceIds,
27
28
  } from "../server/lib/recordings.js";
28
29
  import { parseBrowserDiagnosticsRow } from "../shared/browser-diagnostics.js";
@@ -168,7 +169,7 @@ async function fetchFoldersForSpace(spaceId: string | null) {
168
169
  .from(schema.folders)
169
170
  .where(
170
171
  and(
171
- eq(schema.folders.ownerEmail, ownerEmail),
172
+ ownerEmailMatches(schema.folders.ownerEmail, ownerEmail),
172
173
  spaceId
173
174
  ? eq(schema.folders.spaceId, spaceId)
174
175
  : isNull(schema.folders.spaceId),
@@ -117,13 +117,29 @@ export function RecordingCard({
117
117
  return (local || "?").slice(0, 2).toUpperCase();
118
118
  }, [recording.ownerEmail]);
119
119
 
120
- const handleOpen = useCallback(() => {
121
- if (selectionMode) {
122
- onToggleSelect?.(recording.id);
123
- } else {
124
- navigate(`/r/${recording.id}`);
125
- }
126
- }, [navigate, onToggleSelect, recording.id, selectionMode]);
120
+ const recordingPath = `/r/${recording.id}`;
121
+
122
+ const handleOpen = useCallback(
123
+ (event: React.MouseEvent<HTMLElement>) => {
124
+ const shouldOpenNewTab =
125
+ event.button === 1 ||
126
+ (event.button === 0 && (event.metaKey || event.ctrlKey));
127
+
128
+ if (shouldOpenNewTab) {
129
+ event.preventDefault();
130
+ event.stopPropagation();
131
+ window.open(recordingPath, "_blank", "noopener,noreferrer");
132
+ return;
133
+ }
134
+
135
+ if (selectionMode) {
136
+ onToggleSelect?.(recording.id);
137
+ } else {
138
+ navigate(recordingPath);
139
+ }
140
+ },
141
+ [navigate, onToggleSelect, recording.id, recordingPath, selectionMode],
142
+ );
127
143
 
128
144
  const handleCheckbox = useCallback(
129
145
  (e: React.MouseEvent) => {
@@ -145,6 +161,7 @@ export function RecordingCard({
145
161
  <div
146
162
  role="article"
147
163
  onClick={handleOpen}
164
+ onAuxClick={handleOpen}
148
165
  onMouseEnter={() => setHovered(true)}
149
166
  onMouseLeave={() => setHovered(false)}
150
167
  className={cn(
@@ -26,8 +26,12 @@ function redactString(value: string): string {
26
26
  return redactBrowserDiagnosticString(value);
27
27
  }
28
28
 
29
+ function redactUrlString(value: string): string {
30
+ return redactBrowserDiagnosticString(value, { redactQueryValues: true });
31
+ }
32
+
29
33
  function sanitizeUrl(raw: string): string {
30
- const redacted = redactString(raw);
34
+ const redacted = redactUrlString(raw);
31
35
  try {
32
36
  const parsed = new URL(redacted, window.location.href);
33
37
  parsed.username = "";
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-06-25
4
+ ---
5
+
6
+ Chrome extension recordings now upload large capture bursts reliably and report extension errors to Sentry.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-06-25
4
+ ---
5
+
6
+ Console diagnostics keep more useful non-secret details when clips are shared with agents.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-06-25
4
+ ---
5
+
6
+ Fixed owner recognition so Clips created from desktop and web sessions remain editable by the same signed-in user.
@@ -10,6 +10,9 @@
10
10
  "typecheck": "tsgo --noEmit",
11
11
  "package": "pnpm build && tsx scripts/package.ts"
12
12
  },
13
+ "dependencies": {
14
+ "@sentry/browser": "^10.61.0"
15
+ },
13
16
  "devDependencies": {
14
17
  "@types/chrome": "^0.2.0",
15
18
  "@typescript/native-preview": "catalog:",
@@ -2,7 +2,7 @@
2
2
  "manifest_version": 3,
3
3
  "name": "Agent-Native Clips",
4
4
  "short_name": "Clips",
5
- "version": "0.1.2",
5
+ "version": "0.1.3",
6
6
  "description": "Start Clips recordings from Chrome with optional redacted console and network diagnostics.",
7
7
  "minimum_chrome_version": "116",
8
8
  "action": {
@@ -1,3 +1,7 @@
1
+ import { captureExtensionError, initExtensionSentry } from "./sentry";
2
+
3
+ initExtensionSentry("background");
4
+
1
5
  const DEFAULT_CLIPS_BASE_URL = "https://clips.agent-native.com";
2
6
  const DEBUGGER_PROTOCOL_VERSION = "1.3";
3
7
  const MAX_CONSOLE_LOGS = 400;
@@ -149,6 +153,15 @@ type OffscreenStatusMessage = {
149
153
  error?: string;
150
154
  };
151
155
 
156
+ type ExtensionErrorMessage = {
157
+ type: "CLIPS_EXTENSION_ERROR";
158
+ surface?: string;
159
+ name?: string;
160
+ message?: string;
161
+ stack?: string;
162
+ context?: Record<string, unknown>;
163
+ };
164
+
152
165
  type PendingNetworkRequest = {
153
166
  requestId: string;
154
167
  timestampMs: number;
@@ -516,19 +529,24 @@ function truncate(value: string, maxLength: number): string {
516
529
  return value.length > maxLength ? `${value.slice(0, maxLength)}...` : value;
517
530
  }
518
531
 
519
- function redactString(value: string): string {
520
- return value
532
+ function redactString(
533
+ value: string,
534
+ options: { redactQueryValues?: boolean } = {},
535
+ ): string {
536
+ const redacted = value
521
537
  .replace(AUTHORIZATION_SCHEME_RE, "$1$2<redacted>")
522
538
  .replace(/\b(bearer|basic)\s+[a-z0-9._~+/-]+=*/gi, "$1 <redacted>")
523
539
  .replace(DOUBLE_QUOTED_SECRET_VALUE_RE, '$1$2$1$3"<redacted>"')
524
540
  .replace(SINGLE_QUOTED_SECRET_VALUE_RE, "$1$2$1$3'<redacted>'")
525
- .replace(UNQUOTED_SECRET_VALUE_RE, "$1$2$1$3<redacted>")
526
- .replace(/([?&][^=\s&?#]+)=([^&\s#]+)/g, "$1=<redacted>");
541
+ .replace(UNQUOTED_SECRET_VALUE_RE, "$1$2$1$3<redacted>");
542
+ return options.redactQueryValues
543
+ ? redacted.replace(/([?&][^=\s&?#]+)=([^&\s#]+)/g, "$1=<redacted>")
544
+ : redacted;
527
545
  }
528
546
 
529
547
  function sanitizeUrl(raw: string | null | undefined): string | null {
530
548
  if (!raw) return null;
531
- const redacted = redactString(raw);
549
+ const redacted = redactString(raw, { redactQueryValues: true });
532
550
  try {
533
551
  const parsed = new URL(redacted);
534
552
  parsed.username = "";
@@ -1016,6 +1034,17 @@ async function armRecording(args: {
1016
1034
  visibility: "public",
1017
1035
  });
1018
1036
  } catch (err) {
1037
+ captureExtensionError(err, {
1038
+ tags: {
1039
+ surface: "background",
1040
+ recordingStep: "create-recording",
1041
+ },
1042
+ extra: {
1043
+ captureSurface: settings.captureSurface,
1044
+ includeCamera: settings.includeCamera,
1045
+ includeMicrophone: settings.includeMicrophone,
1046
+ },
1047
+ });
1019
1048
  await abortArming();
1020
1049
  throw err;
1021
1050
  }
@@ -1070,6 +1099,18 @@ async function armRecording(args: {
1070
1099
  });
1071
1100
  } catch (err) {
1072
1101
  console.error("[clips-bg] arm: BEGIN failed", err);
1102
+ captureExtensionError(err, {
1103
+ tags: {
1104
+ surface: "background",
1105
+ recordingStep: "offscreen-begin",
1106
+ },
1107
+ extra: {
1108
+ recordingId: created.id,
1109
+ captureSurface: settings.captureSurface,
1110
+ includeCamera: settings.includeCamera,
1111
+ includeMicrophone: settings.includeMicrophone,
1112
+ },
1113
+ });
1073
1114
  await cancelRecording();
1074
1115
  throw err;
1075
1116
  }
@@ -1553,9 +1594,15 @@ function pushConsole(
1553
1594
  const timestampMs = Number.isFinite(entry.timestampMs)
1554
1595
  ? (entry.timestampMs as number)
1555
1596
  : nowMs();
1556
- const message = truncate(redactString(entry.message), MAX_MESSAGE_LENGTH);
1597
+ const message = truncate(
1598
+ redactString(entry.message, { redactQueryValues: true }),
1599
+ MAX_MESSAGE_LENGTH,
1600
+ );
1557
1601
  const stack = entry.stack
1558
- ? truncate(redactString(entry.stack), MAX_MESSAGE_LENGTH)
1602
+ ? truncate(
1603
+ redactString(entry.stack, { redactQueryValues: true }),
1604
+ MAX_MESSAGE_LENGTH,
1605
+ )
1559
1606
  : "";
1560
1607
  session.consoleLogs.push({
1561
1608
  timestampMs,
@@ -1762,7 +1809,10 @@ function handleResponseReceived(
1762
1809
  pending.ok = response.status >= 200 && response.status < 400;
1763
1810
  }
1764
1811
  if (typeof response.statusText === "string") {
1765
- pending.statusText = truncate(redactString(response.statusText), 120);
1812
+ pending.statusText = truncate(
1813
+ redactString(response.statusText, { redactQueryValues: true }),
1814
+ 120,
1815
+ );
1766
1816
  }
1767
1817
  }
1768
1818
 
@@ -1792,7 +1842,12 @@ function finalizeNetworkRequest(
1792
1842
  ...(typeof pending.ok === "boolean" ? { ok: pending.ok } : {}),
1793
1843
  durationMs: Math.round(durationMs),
1794
1844
  ...(error
1795
- ? { error: truncate(redactString(error), MAX_MESSAGE_LENGTH) }
1845
+ ? {
1846
+ error: truncate(
1847
+ redactString(error, { redactQueryValues: true }),
1848
+ MAX_MESSAGE_LENGTH,
1849
+ ),
1850
+ }
1796
1851
  : {}),
1797
1852
  });
1798
1853
  }
@@ -1923,6 +1978,12 @@ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
1923
1978
  (message as { type?: unknown })?.type,
1924
1979
  err,
1925
1980
  );
1981
+ captureExtensionError(err, {
1982
+ tags: {
1983
+ surface: "background",
1984
+ messageType: String((message as { type?: unknown })?.type ?? ""),
1985
+ },
1986
+ });
1926
1987
  response = {
1927
1988
  ok: false,
1928
1989
  error: err instanceof Error ? err.message : "Could not use Clips.",
@@ -1940,6 +2001,21 @@ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
1940
2001
  async function dispatchRuntimeMessage(message: unknown): Promise<unknown> {
1941
2002
  const type = (message as { type?: unknown }).type;
1942
2003
 
2004
+ if (type === "CLIPS_EXTENSION_ERROR") {
2005
+ const report = message as ExtensionErrorMessage;
2006
+ const error = new Error(report.message || "Chrome extension error");
2007
+ error.name = report.name || "ExtensionError";
2008
+ if (report.stack) error.stack = report.stack;
2009
+ captureExtensionError(error, {
2010
+ tags: {
2011
+ surface: report.surface || "content-script",
2012
+ mechanism: "content-script-report",
2013
+ },
2014
+ extra: report.context,
2015
+ });
2016
+ return { ok: true };
2017
+ }
2018
+
1943
2019
  // Status updates streamed from the offscreen recorder.
1944
2020
  if (type === "CLIPS_NATIVE_STATUS") {
1945
2021
  const status = message as OffscreenStatusMessage;
@@ -18,6 +18,61 @@
18
18
  const ALL_PARTS: OverlayPart[] = ["bubble", "countdown", "toolbar", "saving"];
19
19
  const flags = window as unknown as { __clipsOverlayHostReady?: boolean };
20
20
 
21
+ function errorPayload(error: unknown): {
22
+ name: string;
23
+ message: string;
24
+ stack?: string;
25
+ } {
26
+ if (error instanceof Error) {
27
+ return {
28
+ name: error.name || "Error",
29
+ message: error.message || "Unknown content-script error",
30
+ stack: error.stack,
31
+ };
32
+ }
33
+ return {
34
+ name: "Error",
35
+ message: String(error ?? "Unknown content-script error"),
36
+ };
37
+ }
38
+
39
+ function reportContentScriptError(
40
+ error: unknown,
41
+ context: Record<string, unknown> = {},
42
+ ): void {
43
+ try {
44
+ chrome.runtime.sendMessage(
45
+ {
46
+ type: "CLIPS_EXTENSION_ERROR",
47
+ surface: "content-script",
48
+ ...errorPayload(error),
49
+ context: {
50
+ ...context,
51
+ pageUrl: location.href,
52
+ },
53
+ },
54
+ () => void chrome.runtime.lastError,
55
+ );
56
+ } catch {
57
+ /* background unavailable */
58
+ }
59
+ }
60
+
61
+ window.addEventListener("error", (event) => {
62
+ reportContentScriptError(event.error || event.message, {
63
+ mechanism: "global-error",
64
+ filename: event.filename,
65
+ lineno: event.lineno,
66
+ colno: event.colno,
67
+ });
68
+ });
69
+
70
+ window.addEventListener("unhandledrejection", (event) => {
71
+ reportContentScriptError(event.reason, {
72
+ mechanism: "unhandled-rejection",
73
+ });
74
+ });
75
+
21
76
  // ----- Draggable, resizable camera bubble ---------------------------------
22
77
  // Size + position persist in storage so the bubble stays where the user put it
23
78
  // across pages and recordings (like the desktop app). The iframe can't move or