@agent-native/core 0.133.3 → 0.134.1
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 +2 -2
- package/corpus/core/CHANGELOG.md +62 -0
- package/corpus/core/docs/content/observability.mdx +41 -1
- package/corpus/core/docs/content/template-content-developers.mdx +1 -1
- package/corpus/core/docs/content/tracking.mdx +25 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/run-manager.ts +7 -0
- package/corpus/core/src/client/agent-chat-adapter.ts +10 -0
- package/corpus/core/src/client/analytics.ts +105 -1
- package/corpus/core/src/deploy/build.ts +38 -1
- package/corpus/core/src/observability/posthog-ai.ts +294 -0
- package/corpus/core/src/observability/routes.ts +29 -6
- package/corpus/core/src/observability/traces.ts +287 -12
- package/corpus/core/src/observability/types.ts +11 -0
- package/corpus/core/src/secrets/register-framework-secrets.ts +16 -0
- package/corpus/core/src/server/agent-run-context.ts +21 -0
- package/corpus/core/src/server/capture-error.ts +8 -0
- package/corpus/core/src/server/core-routes-plugin.ts +75 -4
- package/corpus/core/src/server/deploy-environment.ts +57 -0
- package/corpus/core/src/server/error-noise-filter.ts +268 -0
- package/corpus/core/src/server/posthog-config.ts +75 -0
- package/corpus/core/src/server/request-context.ts +6 -0
- package/corpus/core/src/server/sentry-config.ts +4 -8
- package/corpus/core/src/server/sentry-plugin.ts +13 -29
- package/corpus/core/src/server/sentry.ts +13 -156
- package/corpus/core/src/server/ssr-handler.ts +6 -1
- package/corpus/core/src/shared/mcp-embed-headers.ts +1 -1
- package/corpus/core/src/templates/workspace-core/.agents/skills/observability/SKILL.md +36 -10
- package/corpus/core/src/templates/workspace-core/.agents/skills/tracking/SKILL.md +55 -1
- package/corpus/core/src/tracking/error-capture.ts +44 -109
- package/corpus/core/src/tracking/index.ts +11 -0
- package/corpus/core/src/tracking/posthog-exception.ts +305 -0
- package/corpus/core/src/tracking/providers.ts +91 -14
- package/corpus/core/src/tracking/redaction.ts +105 -0
- package/corpus/templates/clips/changelog/2026-07-29-meeting-notes-no-longer-transcribe-the-other-side-twice-when.md +6 -0
- package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +37 -48
- package/corpus/templates/clips/desktop/src/lib/transcription-capture.ts +7 -5
- package/corpus/templates/clips/desktop/src/lib/transcription-engine.ts +182 -106
- package/corpus/templates/clips/desktop/src/overlays/live-transcript.tsx +43 -14
- package/corpus/templates/clips/desktop/src/overlays/recording-pill.tsx +3 -3
- package/corpus/templates/clips/desktop/src-tauri/src/echo_guard.rs +443 -0
- package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +1 -0
- package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +125 -16
- package/corpus/templates/content/.agents/skills/document-editing/references/databases.md +6 -4
- package/corpus/templates/content/actions/_database-membership-lock.ts +25 -0
- package/corpus/templates/content/actions/_database-row-batch.ts +6 -2
- package/corpus/templates/content/actions/_database-source-utils.ts +289 -129
- package/corpus/templates/content/actions/_database-utils.ts +12 -3
- package/corpus/templates/content/actions/add-content-database-source-field-property.ts +39 -21
- package/corpus/templates/content/actions/attach-content-database-source.ts +4 -1
- package/corpus/templates/content/actions/change-content-database-source-role.ts +3 -5
- package/corpus/templates/content/actions/duplicate-document-property.ts +59 -46
- package/corpus/templates/content/actions/execute-builder-source-execution.ts +77 -71
- package/corpus/templates/content/actions/materialize-builder-required-fields.ts +7 -0
- package/corpus/templates/content/actions/remove-database-items.ts +183 -0
- package/corpus/templates/content/actions/set-document-property.ts +57 -39
- package/corpus/templates/content/actions/sync-local-folder-source.ts +7 -0
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +471 -329
- package/corpus/templates/content/app/components/editor/database/row-access.ts +45 -0
- package/corpus/templates/content/app/components/editor/database/shared.tsx +141 -38
- package/corpus/templates/content/app/hooks/use-content-database.ts +2 -2
- package/corpus/templates/content/app/i18n-data.ts +130 -0
- package/corpus/templates/content/changelog/2026-07-30-database-rows-can-now-be-removed-without-deleting-their-page.md +6 -0
- package/corpus/templates/content/parity/eval-scenarios.ts +2 -2
- package/corpus/templates/content/parity/matrix.md +1 -1
- package/corpus/templates/content/parity/matrix.ts +4 -5
- package/corpus/templates/content/shared/api.ts +4 -0
- package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +26 -11
- package/corpus/templates/design/actions/take-design-screenshot.ts +7 -0
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +11 -3
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +4 -1
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +54 -17
- package/corpus/templates/design/app/components/design/design-canvas/local-runtime.ts +126 -0
- package/corpus/templates/design/app/components/templates/TemplatePreview.tsx +2 -0
- package/corpus/templates/design/app/pages/Index.tsx +3 -1
- package/corpus/templates/design/app/pages/Present.tsx +2 -1
- package/corpus/templates/design/changelog/2026-07-30-a-broken-inline-script-in-a-generated-screen-is-now-caught-o.md +6 -0
- package/corpus/templates/design/changelog/2026-07-30-design-html-is-now-validated-with-a-spec-grade-html-parser-i.md +6 -0
- package/corpus/templates/design/changelog/2026-07-30-designs-now-load-their-tailwind-and-alpine-runtimes-from-the.md +6 -0
- package/corpus/templates/design/changelog/2026-07-30-designs-with-a-broken-alpine-expression-are-now-caught-on-sa.md +6 -0
- package/corpus/templates/design/changelog/2026-07-30-editing-a-design-no-longer-strips-its-styling-the-canvas-kep.md +6 -0
- package/corpus/templates/design/package.json +4 -0
- package/corpus/templates/design/shared/html-integrity.ts +802 -556
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +7 -0
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +12 -0
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/analytics.d.ts +8 -0
- package/dist/client/analytics.d.ts.map +1 -1
- package/dist/client/analytics.js +95 -1
- package/dist/client/analytics.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +38 -1
- package/dist/deploy/build.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/posthog-ai.d.ts +126 -0
- package/dist/observability/posthog-ai.d.ts.map +1 -0
- package/dist/observability/posthog-ai.js +190 -0
- package/dist/observability/posthog-ai.js.map +1 -0
- package/dist/observability/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts.map +1 -1
- package/dist/observability/routes.js +26 -6
- package/dist/observability/routes.js.map +1 -1
- package/dist/observability/traces.d.ts +9 -0
- package/dist/observability/traces.d.ts.map +1 -1
- package/dist/observability/traces.js +219 -10
- package/dist/observability/traces.js.map +1 -1
- package/dist/observability/types.d.ts +10 -0
- package/dist/observability/types.d.ts.map +1 -1
- package/dist/observability/types.js +1 -0
- package/dist/observability/types.js.map +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/register-framework-secrets.d.ts.map +1 -1
- package/dist/secrets/register-framework-secrets.js +14 -0
- package/dist/secrets/register-framework-secrets.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/agent-run-context.d.ts +8 -0
- package/dist/server/agent-run-context.d.ts.map +1 -1
- package/dist/server/agent-run-context.js +18 -0
- package/dist/server/agent-run-context.js.map +1 -1
- package/dist/server/capture-error.d.ts +8 -0
- package/dist/server/capture-error.d.ts.map +1 -1
- package/dist/server/capture-error.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts +0 -23
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +71 -7
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/deploy-environment.d.ts +17 -0
- package/dist/server/deploy-environment.d.ts.map +1 -0
- package/dist/server/deploy-environment.js +47 -0
- package/dist/server/deploy-environment.js.map +1 -0
- package/dist/server/error-noise-filter.d.ts +80 -0
- package/dist/server/error-noise-filter.d.ts.map +1 -0
- package/dist/server/error-noise-filter.js +173 -0
- package/dist/server/error-noise-filter.js.map +1 -0
- package/dist/server/posthog-config.d.ts +28 -0
- package/dist/server/posthog-config.d.ts.map +1 -0
- package/dist/server/posthog-config.js +53 -0
- package/dist/server/posthog-config.js.map +1 -0
- package/dist/server/request-context.d.ts +6 -0
- package/dist/server/request-context.d.ts.map +1 -1
- package/dist/server/request-context.js.map +1 -1
- package/dist/server/sentry-config.d.ts +1 -0
- package/dist/server/sentry-config.d.ts.map +1 -1
- package/dist/server/sentry-config.js +3 -1
- package/dist/server/sentry-config.js.map +1 -1
- package/dist/server/sentry-plugin.d.ts.map +1 -1
- package/dist/server/sentry-plugin.js +12 -27
- package/dist/server/sentry-plugin.js.map +1 -1
- package/dist/server/sentry.d.ts.map +1 -1
- package/dist/server/sentry.js +8 -124
- package/dist/server/sentry.js.map +1 -1
- package/dist/server/ssr-handler.d.ts.map +1 -1
- package/dist/server/ssr-handler.js +6 -1
- package/dist/server/ssr-handler.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/shared/mcp-embed-headers.d.ts +1 -1
- package/dist/shared/mcp-embed-headers.d.ts.map +1 -1
- package/dist/shared/mcp-embed-headers.js +1 -1
- package/dist/shared/mcp-embed-headers.js.map +1 -1
- package/dist/templates/workspace-core/.agents/skills/observability/SKILL.md +36 -10
- package/dist/templates/workspace-core/.agents/skills/tracking/SKILL.md +55 -1
- package/dist/tracking/error-capture.d.ts +7 -0
- package/dist/tracking/error-capture.d.ts.map +1 -1
- package/dist/tracking/error-capture.js +10 -73
- package/dist/tracking/error-capture.js.map +1 -1
- package/dist/tracking/index.d.ts +1 -0
- package/dist/tracking/index.d.ts.map +1 -1
- package/dist/tracking/index.js +1 -0
- package/dist/tracking/index.js.map +1 -1
- package/dist/tracking/posthog-exception.d.ts +103 -0
- package/dist/tracking/posthog-exception.d.ts.map +1 -0
- package/dist/tracking/posthog-exception.js +181 -0
- package/dist/tracking/posthog-exception.js.map +1 -0
- package/dist/tracking/providers.d.ts +14 -0
- package/dist/tracking/providers.d.ts.map +1 -1
- package/dist/tracking/providers.js +63 -11
- package/dist/tracking/providers.js.map +1 -1
- package/dist/tracking/redaction.d.ts +25 -0
- package/dist/tracking/redaction.d.ts.map +1 -0
- package/dist/tracking/redaction.js +85 -0
- package/dist/tracking/redaction.js.map +1 -0
- package/docs/content/observability.mdx +41 -1
- package/docs/content/template-content-developers.mdx +1 -1
- package/docs/content/tracking.mdx +25 -0
- package/package.json +1 -1
- package/src/agent/run-manager.ts +7 -0
- package/src/client/agent-chat-adapter.ts +10 -0
- package/src/client/analytics.ts +105 -1
- package/src/deploy/build.ts +38 -1
- package/src/observability/posthog-ai.ts +294 -0
- package/src/observability/routes.ts +29 -6
- package/src/observability/traces.ts +287 -12
- package/src/observability/types.ts +11 -0
- package/src/secrets/register-framework-secrets.ts +16 -0
- package/src/server/agent-run-context.ts +21 -0
- package/src/server/capture-error.ts +8 -0
- package/src/server/core-routes-plugin.ts +75 -4
- package/src/server/deploy-environment.ts +57 -0
- package/src/server/error-noise-filter.ts +268 -0
- package/src/server/posthog-config.ts +75 -0
- package/src/server/request-context.ts +6 -0
- package/src/server/sentry-config.ts +4 -8
- package/src/server/sentry-plugin.ts +13 -29
- package/src/server/sentry.ts +13 -156
- package/src/server/ssr-handler.ts +6 -1
- package/src/shared/mcp-embed-headers.ts +1 -1
- package/src/templates/workspace-core/.agents/skills/observability/SKILL.md +36 -10
- package/src/templates/workspace-core/.agents/skills/tracking/SKILL.md +55 -1
- package/src/tracking/error-capture.ts +44 -109
- package/src/tracking/index.ts +11 -0
- package/src/tracking/posthog-exception.ts +305 -0
- package/src/tracking/providers.ts +91 -14
- package/src/tracking/redaction.ts +105 -0
- package/corpus/templates/content/actions/delete-database-items.ts +0 -89
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* automatically by the core-routes plugin).
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
+
import { reshapeTrackedExceptionProperties } from "./posthog-exception.js";
|
|
16
17
|
import { registerTrackingProvider } from "./registry.js";
|
|
17
18
|
import type { TrackingProvider, TrackingEvent } from "./types.js";
|
|
18
19
|
|
|
@@ -162,27 +163,61 @@ function isPostHogAiObservabilityEvent(eventName: string): boolean {
|
|
|
162
163
|
return eventName.startsWith("$ai_");
|
|
163
164
|
}
|
|
164
165
|
|
|
165
|
-
|
|
166
|
+
/**
|
|
167
|
+
* `$ai_*` and `$exception` are ingested through PostHog's dedicated endpoint
|
|
168
|
+
* rather than `/capture/`, and `$exception` additionally has to carry
|
|
169
|
+
* `$exception_list` — the framework's own `captureException()` emits camelCase
|
|
170
|
+
* fields that PostHog would otherwise render as an empty, ungroupable issue.
|
|
171
|
+
*/
|
|
172
|
+
function createPostHogProvider(
|
|
173
|
+
apiKey: string,
|
|
174
|
+
host: string,
|
|
175
|
+
errorTracking: boolean,
|
|
176
|
+
): TrackingProvider {
|
|
177
|
+
const sendToEventsEndpoint = (
|
|
178
|
+
event: TrackingEvent,
|
|
179
|
+
properties: Record<string, unknown> | undefined,
|
|
180
|
+
distinctId: string,
|
|
181
|
+
): void => {
|
|
182
|
+
enqueue(
|
|
183
|
+
`${host}/i/v0/e/`,
|
|
184
|
+
JSON.stringify({
|
|
185
|
+
api_key: apiKey,
|
|
186
|
+
event: event.name,
|
|
187
|
+
properties: {
|
|
188
|
+
distinct_id: distinctId,
|
|
189
|
+
...properties,
|
|
190
|
+
timestamp: event.timestamp,
|
|
191
|
+
},
|
|
192
|
+
}),
|
|
193
|
+
);
|
|
194
|
+
};
|
|
195
|
+
|
|
166
196
|
return {
|
|
167
197
|
name: "posthog",
|
|
168
198
|
track(event: TrackingEvent) {
|
|
169
199
|
const distinctId = event.userId || "anonymous";
|
|
170
200
|
if (isPostHogAiObservabilityEvent(event.name)) {
|
|
171
|
-
|
|
172
|
-
`${host}/i/v0/e/`,
|
|
173
|
-
JSON.stringify({
|
|
174
|
-
api_key: apiKey,
|
|
175
|
-
event: event.name,
|
|
176
|
-
properties: {
|
|
177
|
-
distinct_id: distinctId,
|
|
178
|
-
...event.properties,
|
|
179
|
-
timestamp: event.timestamp,
|
|
180
|
-
},
|
|
181
|
-
}),
|
|
182
|
-
);
|
|
201
|
+
sendToEventsEndpoint(event, event.properties, distinctId);
|
|
183
202
|
return;
|
|
184
203
|
}
|
|
185
204
|
|
|
205
|
+
if (event.name === "$exception") {
|
|
206
|
+
// `POSTHOG_ERROR_TRACKING=false` keeps product analytics flowing while
|
|
207
|
+
// another backend owns crashes. Drop rather than downgrade to
|
|
208
|
+
// `/capture/`: a malformed exception is what this branch exists to
|
|
209
|
+
// prevent.
|
|
210
|
+
if (!errorTracking) return;
|
|
211
|
+
const reshaped = reshapeTrackedExceptionProperties(event.properties);
|
|
212
|
+
if (reshaped) {
|
|
213
|
+
sendToEventsEndpoint(event, reshaped, distinctId);
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
// No recognizable exception fields. Fall through to `/capture/` so the
|
|
217
|
+
// event is still recorded as-is rather than becoming an issue with
|
|
218
|
+
// nothing in it.
|
|
219
|
+
}
|
|
220
|
+
|
|
186
221
|
enqueue(
|
|
187
222
|
`${host}/capture/`,
|
|
188
223
|
JSON.stringify({
|
|
@@ -213,6 +248,42 @@ function createPostHogProvider(apiKey: string, host: string): TrackingProvider {
|
|
|
213
248
|
};
|
|
214
249
|
}
|
|
215
250
|
|
|
251
|
+
/**
|
|
252
|
+
* Send one event to PostHog only, bypassing the provider fan-out.
|
|
253
|
+
*
|
|
254
|
+
* For payloads whose *shape* is PostHog-specific and whose *content* other
|
|
255
|
+
* backends must not receive. `track()` broadcasts to every configured provider,
|
|
256
|
+
* so a PostHog-only integration (e.g. enabling a survey id) would otherwise
|
|
257
|
+
* start exporting that integration's content — including user-authored text —
|
|
258
|
+
* to Mixpanel, Amplitude, webhooks, and Agent Native Analytics as a side
|
|
259
|
+
* effect nobody opted into.
|
|
260
|
+
*
|
|
261
|
+
* Returns `false` when PostHog is not configured, so callers can tell "not
|
|
262
|
+
* sent" from "sent".
|
|
263
|
+
*/
|
|
264
|
+
export function sendPostHogEvent(
|
|
265
|
+
name: string,
|
|
266
|
+
properties: Record<string, unknown>,
|
|
267
|
+
distinctId: string,
|
|
268
|
+
): boolean {
|
|
269
|
+
const apiKey = process.env.POSTHOG_API_KEY;
|
|
270
|
+
if (!apiKey) return false;
|
|
271
|
+
const host = (process.env.POSTHOG_HOST || POSTHOG_DEFAULT_HOST).replace(
|
|
272
|
+
/\/+$/,
|
|
273
|
+
"",
|
|
274
|
+
);
|
|
275
|
+
enqueue(
|
|
276
|
+
`${host}/capture/`,
|
|
277
|
+
JSON.stringify({
|
|
278
|
+
api_key: apiKey,
|
|
279
|
+
event: name,
|
|
280
|
+
distinct_id: distinctId,
|
|
281
|
+
properties: { ...properties, timestamp: new Date().toISOString() },
|
|
282
|
+
}),
|
|
283
|
+
);
|
|
284
|
+
return true;
|
|
285
|
+
}
|
|
286
|
+
|
|
216
287
|
// ─── Mixpanel ──────────────────────────────────────────────────────────────
|
|
217
288
|
|
|
218
289
|
function createMixpanelProvider(token: string): TrackingProvider {
|
|
@@ -383,7 +454,13 @@ export function registerBuiltinProviders(): void {
|
|
|
383
454
|
/\/+$/,
|
|
384
455
|
"",
|
|
385
456
|
);
|
|
386
|
-
registerTrackingProvider(
|
|
457
|
+
registerTrackingProvider(
|
|
458
|
+
createPostHogProvider(
|
|
459
|
+
posthogKey,
|
|
460
|
+
host,
|
|
461
|
+
process.env.POSTHOG_ERROR_TRACKING?.trim().toLowerCase() !== "false",
|
|
462
|
+
),
|
|
463
|
+
);
|
|
387
464
|
}
|
|
388
465
|
|
|
389
466
|
const mixpanelToken = process.env.MIXPANEL_TOKEN;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bounding and redaction helpers shared by every exception emitter.
|
|
3
|
+
*
|
|
4
|
+
* Kept free of `process.env` and Node built-ins so the browser bundle can use
|
|
5
|
+
* the same rules — an exception shaped one way on the server and another way
|
|
6
|
+
* in the client is how a leak ships in only one of them.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export const MAX_MESSAGE_LENGTH = 1000;
|
|
10
|
+
export const MAX_STACK_LENGTH = 8000;
|
|
11
|
+
export const MAX_TAGS = 30;
|
|
12
|
+
export const MAX_EXTRA_KEYS = 30;
|
|
13
|
+
export const MAX_EXTRA_VALUE_LENGTH = 1000;
|
|
14
|
+
|
|
15
|
+
const SECRET_RE = /\b(?:bearer|basic)\s+[^\s]+/gi;
|
|
16
|
+
|
|
17
|
+
export const SECRET_KEY_RE =
|
|
18
|
+
/(?:authorization|cookie|set[-_]?cookie|token|secret|password|passwd|pwd|api[-_]?key|apikey|credential)/i;
|
|
19
|
+
|
|
20
|
+
export function redact(value: string): string {
|
|
21
|
+
return value
|
|
22
|
+
.replace(SECRET_RE, (match) => `${match.split(/\s+/, 1)[0]} <redacted>`)
|
|
23
|
+
.replace(
|
|
24
|
+
/([A-Za-z0-9_$.-]*(?:authorization|cookie|token|secret|password|passwd|pwd|api[-_]?key|apikey|credential)[A-Za-z0-9_$.-]*\s*[:=]\s*)([^\s,;}]+)/gi,
|
|
25
|
+
"$1<redacted>",
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function boundedText(value: unknown, max: number): string {
|
|
30
|
+
const text = typeof value === "string" ? value : String(value ?? "");
|
|
31
|
+
const safe = redact(text);
|
|
32
|
+
return safe.length > max ? safe.slice(0, max) : safe;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function safeValue(value: unknown, depth = 2): unknown {
|
|
36
|
+
if (
|
|
37
|
+
value == null ||
|
|
38
|
+
typeof value === "boolean" ||
|
|
39
|
+
typeof value === "number"
|
|
40
|
+
) {
|
|
41
|
+
return value;
|
|
42
|
+
}
|
|
43
|
+
if (typeof value === "string")
|
|
44
|
+
return boundedText(value, MAX_EXTRA_VALUE_LENGTH);
|
|
45
|
+
if (depth <= 0) return boundedText(value, MAX_EXTRA_VALUE_LENGTH);
|
|
46
|
+
if (Array.isArray(value)) {
|
|
47
|
+
return value.slice(0, 20).map((item) => safeValue(item, depth - 1));
|
|
48
|
+
}
|
|
49
|
+
if (typeof value === "object") {
|
|
50
|
+
const out: Record<string, unknown> = {};
|
|
51
|
+
for (const [key, child] of Object.entries(value)) {
|
|
52
|
+
if (Object.keys(out).length >= MAX_EXTRA_KEYS) break;
|
|
53
|
+
const safeKey = boundedText(key, 100);
|
|
54
|
+
out[safeKey] = SECRET_KEY_RE.test(safeKey)
|
|
55
|
+
? "<redacted>"
|
|
56
|
+
: safeValue(child, depth - 1);
|
|
57
|
+
}
|
|
58
|
+
return out;
|
|
59
|
+
}
|
|
60
|
+
return boundedText(value, MAX_EXTRA_VALUE_LENGTH);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function safeTags(
|
|
64
|
+
tags: Record<string, string | undefined> | undefined,
|
|
65
|
+
): Record<string, string> {
|
|
66
|
+
const out: Record<string, string> = {};
|
|
67
|
+
for (const [key, value] of Object.entries(tags ?? {})) {
|
|
68
|
+
if (Object.keys(out).length >= MAX_TAGS) break;
|
|
69
|
+
// Skip, don't stop: callers build tag objects with optional entries
|
|
70
|
+
// (`{ ...tags, route, method, userAgent }`), so breaking on the first
|
|
71
|
+
// undefined dropped every tag after it depending on key order.
|
|
72
|
+
if (value == null) continue;
|
|
73
|
+
const safeKey = boundedText(key, 100);
|
|
74
|
+
out[safeKey] = SECRET_KEY_RE.test(safeKey)
|
|
75
|
+
? "<redacted>"
|
|
76
|
+
: boundedText(value, 200);
|
|
77
|
+
}
|
|
78
|
+
return out;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface ExceptionParts {
|
|
82
|
+
type: string;
|
|
83
|
+
message: string;
|
|
84
|
+
stack?: string;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Split an unknown thrown value into bounded, redacted type/message/stack. */
|
|
88
|
+
export function exceptionParts(error: unknown): ExceptionParts {
|
|
89
|
+
if (error instanceof Error) {
|
|
90
|
+
return {
|
|
91
|
+
type: boundedText(error.name || "Error", 200),
|
|
92
|
+
message: boundedText(
|
|
93
|
+
error.message || error.name || "Error",
|
|
94
|
+
MAX_MESSAGE_LENGTH,
|
|
95
|
+
),
|
|
96
|
+
...(error.stack
|
|
97
|
+
? { stack: boundedText(error.stack, MAX_STACK_LENGTH) }
|
|
98
|
+
: {}),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
type: "Error",
|
|
103
|
+
message: boundedText(error, MAX_MESSAGE_LENGTH),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
@@ -8,11 +8,14 @@ import {
|
|
|
8
8
|
appendFinalTranscript,
|
|
9
9
|
onFinalTranscript,
|
|
10
10
|
restartTranscriptionEngine,
|
|
11
|
-
speakerFor,
|
|
12
11
|
startTranscriptionEngine,
|
|
13
12
|
stopTranscriptionEngine,
|
|
13
|
+
transcriptFullText,
|
|
14
|
+
transcriptLineFromSegment,
|
|
15
|
+
transcriptSegments,
|
|
14
16
|
type SourcedTranscriptSegment,
|
|
15
17
|
type TranscriptionEngine,
|
|
18
|
+
type TranscriptLine,
|
|
16
19
|
} from "../lib/transcription-engine";
|
|
17
20
|
import { normalizeServerUrl } from "../lib/url";
|
|
18
21
|
|
|
@@ -31,8 +34,7 @@ export interface MeetingTranscriptionPayload {
|
|
|
31
34
|
interface MeetingTranscriptionSession {
|
|
32
35
|
meetingId: string;
|
|
33
36
|
recordingId: string;
|
|
34
|
-
lines:
|
|
35
|
-
segments: SourcedTranscriptSegment[];
|
|
37
|
+
lines: TranscriptLine[];
|
|
36
38
|
unlisten: Array<() => void>;
|
|
37
39
|
flushTimer: ReturnType<typeof setTimeout> | null;
|
|
38
40
|
stopping: boolean;
|
|
@@ -52,6 +54,22 @@ interface MeetingTranscriptionSession {
|
|
|
52
54
|
dirtySeq: number;
|
|
53
55
|
}
|
|
54
56
|
|
|
57
|
+
/** What the pill overlay needs to render a line: text, side, and timestamp.
|
|
58
|
+
* The verbatim segments stay behind in the session. */
|
|
59
|
+
interface PillTranscriptLine {
|
|
60
|
+
text: string;
|
|
61
|
+
source: "mic" | "system";
|
|
62
|
+
startMs?: number;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function pillTranscriptLines(lines: TranscriptLine[]): PillTranscriptLine[] {
|
|
66
|
+
return lines.map((line) => ({
|
|
67
|
+
text: line.text,
|
|
68
|
+
source: line.source,
|
|
69
|
+
startMs: line.startMs ?? undefined,
|
|
70
|
+
}));
|
|
71
|
+
}
|
|
72
|
+
|
|
55
73
|
type CallClipsAction = <T>(
|
|
56
74
|
name: string,
|
|
57
75
|
body: Record<string, unknown>,
|
|
@@ -79,11 +97,7 @@ export function useMeetingTranscription({
|
|
|
79
97
|
const pendingPillInitRef = useRef<{
|
|
80
98
|
meetingId: string;
|
|
81
99
|
initialNotes: string;
|
|
82
|
-
preloadedLines?:
|
|
83
|
-
text: string;
|
|
84
|
-
source: "mic" | "system";
|
|
85
|
-
startMs?: number;
|
|
86
|
-
}>;
|
|
100
|
+
preloadedLines?: PillTranscriptLine[];
|
|
87
101
|
} | null>(null);
|
|
88
102
|
|
|
89
103
|
const normalizedServerUrl = useMemo(
|
|
@@ -119,8 +133,8 @@ export function useMeetingTranscription({
|
|
|
119
133
|
const run = (async () => {
|
|
120
134
|
await callClipsAction("save-browser-transcript", {
|
|
121
135
|
recordingId: session.recordingId,
|
|
122
|
-
fullText: session.lines
|
|
123
|
-
segments: session.
|
|
136
|
+
fullText: transcriptFullText(session.lines),
|
|
137
|
+
segments: transcriptSegments(session.lines),
|
|
124
138
|
source: session.engine,
|
|
125
139
|
overwriteReady: true,
|
|
126
140
|
});
|
|
@@ -314,7 +328,6 @@ export function useMeetingTranscription({
|
|
|
314
328
|
meetingId: resolvedMeetingId,
|
|
315
329
|
recordingId,
|
|
316
330
|
lines: [],
|
|
317
|
-
segments: [],
|
|
318
331
|
unlisten: [],
|
|
319
332
|
flushTimer: null,
|
|
320
333
|
stopping: false,
|
|
@@ -363,13 +376,7 @@ export function useMeetingTranscription({
|
|
|
363
376
|
})),
|
|
364
377
|
}
|
|
365
378
|
: event;
|
|
366
|
-
if (
|
|
367
|
-
appendFinalTranscript(
|
|
368
|
-
timelineEvent,
|
|
369
|
-
session.lines,
|
|
370
|
-
session.segments,
|
|
371
|
-
)
|
|
372
|
-
) {
|
|
379
|
+
if (appendFinalTranscript(timelineEvent, session.lines)) {
|
|
373
380
|
scheduleFlush();
|
|
374
381
|
}
|
|
375
382
|
}),
|
|
@@ -539,11 +546,7 @@ export function useMeetingTranscription({
|
|
|
539
546
|
pendingPillInitRef.current = {
|
|
540
547
|
meetingId: resolvedMeetingId,
|
|
541
548
|
initialNotes,
|
|
542
|
-
preloadedLines: session.
|
|
543
|
-
text: segment.text,
|
|
544
|
-
source: segment.source,
|
|
545
|
-
startMs: segment.startMs,
|
|
546
|
-
})),
|
|
549
|
+
preloadedLines: pillTranscriptLines(session.lines),
|
|
547
550
|
};
|
|
548
551
|
emit("clips:meeting-notes-init", {
|
|
549
552
|
meetingId: resolvedMeetingId,
|
|
@@ -561,25 +564,16 @@ export function useMeetingTranscription({
|
|
|
561
564
|
source?: "mic" | "system";
|
|
562
565
|
}>;
|
|
563
566
|
if (segs.length > 0) {
|
|
564
|
-
const
|
|
565
|
-
(
|
|
567
|
+
const storedLines = segs.map((s) =>
|
|
568
|
+
transcriptLineFromSegment({
|
|
569
|
+
startMs: s.startMs ?? 0,
|
|
570
|
+
endMs: s.endMs ?? 0,
|
|
571
|
+
text: s.text,
|
|
572
|
+
source: s.source ?? "mic",
|
|
573
|
+
}),
|
|
566
574
|
);
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
endMs: s.endMs ?? 0,
|
|
570
|
-
text: s.text,
|
|
571
|
-
source: s.source ?? ("mic" as const),
|
|
572
|
-
}));
|
|
573
|
-
session.lines = [...preloadedLineStrings, ...session.lines];
|
|
574
|
-
session.segments = [
|
|
575
|
-
...preloadedSegments,
|
|
576
|
-
...session.segments,
|
|
577
|
-
];
|
|
578
|
-
const preloadedLines = session.segments.map((s) => ({
|
|
579
|
-
text: s.text,
|
|
580
|
-
source: s.source,
|
|
581
|
-
startMs: s.startMs,
|
|
582
|
-
}));
|
|
575
|
+
session.lines = [...storedLines, ...session.lines];
|
|
576
|
+
const preloadedLines = pillTranscriptLines(session.lines);
|
|
583
577
|
// Store in ref so clips:pill-ready can re-emit if the
|
|
584
578
|
// pill window mounts after this fetch resolves.
|
|
585
579
|
if (
|
|
@@ -648,15 +642,10 @@ export function useMeetingTranscription({
|
|
|
648
642
|
.then((history) => {
|
|
649
643
|
if (sessionRef.current !== session) return;
|
|
650
644
|
const historyLines = history.segments.map(
|
|
651
|
-
|
|
645
|
+
transcriptLineFromSegment,
|
|
652
646
|
);
|
|
653
647
|
session.lines = [...historyLines, ...session.lines];
|
|
654
|
-
|
|
655
|
-
const preloadedLines = session.segments.map((segment) => ({
|
|
656
|
-
text: segment.text,
|
|
657
|
-
source: segment.source,
|
|
658
|
-
startMs: segment.startMs,
|
|
659
|
-
}));
|
|
648
|
+
const preloadedLines = pillTranscriptLines(session.lines);
|
|
660
649
|
if (pendingPillInitRef.current?.meetingId === resolvedMeetingId) {
|
|
661
650
|
pendingPillInitRef.current = {
|
|
662
651
|
...pendingPillInitRef.current,
|
|
@@ -19,7 +19,10 @@ import {
|
|
|
19
19
|
startTranscriptionEngine,
|
|
20
20
|
stopTranscriptionEngine,
|
|
21
21
|
TranscriptionEngine,
|
|
22
|
+
transcriptFullText,
|
|
23
|
+
transcriptSegments,
|
|
22
24
|
type SourcedTranscriptSegment,
|
|
25
|
+
type TranscriptLine,
|
|
23
26
|
} from "./transcription-engine";
|
|
24
27
|
|
|
25
28
|
/** Grace period after stop for whisper to emit any flushed trailing finals. */
|
|
@@ -307,8 +310,7 @@ export async function startTranscriptionCapture(
|
|
|
307
310
|
voiceProcessing?: boolean;
|
|
308
311
|
},
|
|
309
312
|
): Promise<TranscriptionCapture | null> {
|
|
310
|
-
const lines:
|
|
311
|
-
const segments: SourcedTranscriptSegment[] = [];
|
|
313
|
+
const lines: TranscriptLine[] = [];
|
|
312
314
|
let disposed = false;
|
|
313
315
|
let paused = false;
|
|
314
316
|
let desiredPaused = false;
|
|
@@ -331,8 +333,8 @@ export async function startTranscriptionCapture(
|
|
|
331
333
|
};
|
|
332
334
|
|
|
333
335
|
const captured = (): CapturedTranscript => ({
|
|
334
|
-
text: lines
|
|
335
|
-
segments,
|
|
336
|
+
text: transcriptFullText(lines),
|
|
337
|
+
segments: transcriptSegments(lines),
|
|
336
338
|
});
|
|
337
339
|
|
|
338
340
|
let engine: TranscriptionEngine;
|
|
@@ -340,7 +342,7 @@ export async function startTranscriptionCapture(
|
|
|
340
342
|
unlistens.push(
|
|
341
343
|
await onFinalTranscript((event) => {
|
|
342
344
|
if (disposed) return;
|
|
343
|
-
appendFinalTranscript(event, lines
|
|
345
|
+
appendFinalTranscript(event, lines);
|
|
344
346
|
}),
|
|
345
347
|
);
|
|
346
348
|
|