@agent-native/core 0.157.7 → 0.157.9

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 (69) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/actions/purge-first-party-analytics-postgres.ts +26 -8
  3. package/corpus/templates/analytics/scripts/first-party-analytics-postgres-retention-dry-run.sql +258 -0
  4. package/corpus/templates/analytics/server/lib/first-party-analytics-purge.ts +59 -27
  5. package/corpus/templates/analytics/server/lib/workspace-feature-flags.ts +4 -1
  6. package/corpus/templates/slides/actions/export-pptx.ts +261 -71
  7. package/corpus/templates/slides/actions/import-file.ts +12 -0
  8. package/corpus/templates/slides/actions/import-pptx.ts +9 -0
  9. package/corpus/templates/slides/app/lib/export-pptx-client.ts +45 -5
  10. package/corpus/templates/slides/server/handlers/import/html-converter.ts +69 -1
  11. package/corpus/templates/slides/server/handlers/import/pdf-fidelity-parser.ts +316 -64
  12. package/corpus/templates/slides/server/lib/source-import.ts +10 -1
  13. package/dist/agent/harness/migrations.d.ts +4 -0
  14. package/dist/agent/harness/migrations.js +39 -0
  15. package/dist/agent/production-agent.d.ts +2 -0
  16. package/dist/agent/production-agent.js +4 -0
  17. package/dist/agent/run-loop-with-resume.d.ts +1 -0
  18. package/dist/agent/run-loop-with-resume.js +45 -30
  19. package/dist/agent/run-migrations.d.ts +8 -0
  20. package/dist/agent/run-migrations.js +61 -0
  21. package/dist/chat-threads/migrations.d.ts +1 -0
  22. package/dist/chat-threads/migrations.js +1 -0
  23. package/dist/chat-threads/schema-migrations.d.ts +8 -0
  24. package/dist/chat-threads/schema-migrations.js +60 -0
  25. package/dist/client/AssistantChat.js +4 -0
  26. package/dist/client/chat/message-components.d.ts +2 -0
  27. package/dist/client/chat/message-components.js +11 -1
  28. package/dist/client/observability/ThumbsFeedback.js +35 -22
  29. package/dist/client/observability/useObservability.d.ts +1 -1
  30. package/dist/client/observability/useObservability.js +6 -3
  31. package/dist/collab/awareness.d.ts +2 -2
  32. package/dist/collab/struct-routes.d.ts +1 -1
  33. package/dist/feature-flags/a2a-action-route.js +4 -2
  34. package/dist/feature-flags/permissions.d.ts +2 -0
  35. package/dist/feature-flags/permissions.js +6 -1
  36. package/dist/ingestion/pptx.d.ts +14 -1
  37. package/dist/ingestion/pptx.js +222 -41
  38. package/dist/localization/core-messages/ar-SA.js +4 -0
  39. package/dist/localization/core-messages/de-DE.js +4 -0
  40. package/dist/localization/core-messages/en-US.d.ts +4 -0
  41. package/dist/localization/core-messages/en-US.js +4 -0
  42. package/dist/localization/core-messages/es-ES.js +4 -0
  43. package/dist/localization/core-messages/fr-FR.js +4 -0
  44. package/dist/localization/core-messages/hi-IN.js +4 -0
  45. package/dist/localization/core-messages/ja-JP.js +4 -0
  46. package/dist/localization/core-messages/ko-KR.js +4 -0
  47. package/dist/localization/core-messages/pt-BR.js +4 -0
  48. package/dist/localization/core-messages/zh-CN.js +4 -0
  49. package/dist/localization/core-messages/zh-TW.js +4 -0
  50. package/dist/localization/core-messages.d.ts +4 -0
  51. package/dist/notifications/routes.d.ts +1 -1
  52. package/dist/observability/feedback.js +40 -16
  53. package/dist/observability/routes.d.ts +1 -1
  54. package/dist/observability/routes.js +15 -3
  55. package/dist/observability/store.js +2 -0
  56. package/dist/observability/traces.js +5 -1
  57. package/dist/provider-api/actions/custom-provider-registration.d.ts +12 -12
  58. package/dist/provider-api/actions/provider-api.d.ts +10 -10
  59. package/dist/resources/handlers.d.ts +1 -1
  60. package/dist/server/action-routes.d.ts +7 -5
  61. package/dist/server/action-routes.js +9 -4
  62. package/dist/server/better-auth-instance.js +4 -0
  63. package/dist/server/poll.d.ts +4 -4
  64. package/dist/server/poll.js +45 -13
  65. package/dist/server/realtime-voice.js +2 -1
  66. package/dist/server/release-migrations.js +16 -0
  67. package/dist/usage/migrations.d.ts +4 -0
  68. package/dist/usage/migrations.js +45 -0
  69. package/package.json +3 -3
package/corpus/README.md CHANGED
@@ -31,4 +31,4 @@ rg -n "defineAction|useActionQuery" node_modules/@agent-native/core/corpus
31
31
 
32
32
  ## Generated Counts
33
33
 
34
- - template files: 8256
34
+ - template files: 8257
@@ -88,11 +88,20 @@ export default defineAction({
88
88
  startReceivedAt,
89
89
  startEventDate: startReceivedAt.slice(0, 10),
90
90
  };
91
- const postgres = await countFirstPartyAnalyticsPostgresRows(
92
- scope,
93
- includeLegacyOwnerRows,
94
- window,
95
- );
91
+ let postgres: Awaited<
92
+ ReturnType<typeof countFirstPartyAnalyticsPostgresRows>
93
+ > | null = null;
94
+ let postgresError: string | null = null;
95
+ try {
96
+ postgres = await countFirstPartyAnalyticsPostgresRows(
97
+ scope,
98
+ includeLegacyOwnerRows,
99
+ window,
100
+ );
101
+ } catch (error) {
102
+ if (!dryRun) throw error;
103
+ postgresError = error instanceof Error ? error.message : String(error);
104
+ }
96
105
  let bigQuery: {
97
106
  eventCount: number;
98
107
  dailyRollupRows: number;
@@ -115,12 +124,15 @@ export default defineAction({
115
124
  }
116
125
  }
117
126
 
118
- const uncopiedEventRows = bigQuery
119
- ? Math.max(0, postgres.eventRows - bigQuery.eventCount)
120
- : null;
127
+ const uncopiedEventRows =
128
+ bigQuery && postgres
129
+ ? Math.max(0, postgres.eventRows - bigQuery.eventCount)
130
+ : null;
121
131
  const safeToDelete =
122
132
  backend.sink === "bigquery" &&
123
133
  backend.backfillCompleted === true &&
134
+ postgresError === null &&
135
+ postgres !== null &&
124
136
  bigQueryError === null &&
125
137
  bigQuery !== null &&
126
138
  uncopiedEventRows === 0;
@@ -136,6 +148,11 @@ export default defineAction({
136
148
  "Postgres purge is blocked until the durable BigQuery backfill reports completed.",
137
149
  );
138
150
  }
151
+ if (postgresError || !postgres) {
152
+ throw new Error(
153
+ `Postgres purge is blocked because the scoped Postgres inventory could not be verified: ${postgresError ?? "unknown Postgres state"}`,
154
+ );
155
+ }
139
156
  if (bigQueryError || !bigQuery) {
140
157
  throw new Error(
141
158
  `Postgres purge is blocked because BigQuery readiness could not be verified: ${bigQueryError ?? "unknown warehouse state"}`,
@@ -179,6 +196,7 @@ export default defineAction({
179
196
  sink: backend.sink,
180
197
  backfillCompleted: backend.backfillCompleted === true,
181
198
  postgres,
199
+ postgresError,
182
200
  bigQuery,
183
201
  bigQueryError,
184
202
  uncopiedEventRows,
@@ -0,0 +1,258 @@
1
+ -- FIRST-PARTY ANALYTICS POSTGRES RETENTION PREVIEW - READ ONLY
2
+ --
3
+ -- This file intentionally contains no INSERT, UPDATE, DELETE, TRUNCATE, or DDL.
4
+ -- It reports only rows in the explicitly resolved Builder.io organization and
5
+ -- the exact 60-day window that was copied to BigQuery by the migration.
6
+ --
7
+ -- The candidate set excludes http.response because that event class was
8
+ -- intentionally omitted from the BigQuery backfill. Rows older than the
9
+ -- lookback window and legacy owner-only rows are reported separately and are
10
+ -- never included in the candidate count. Session replay, exception issues,
11
+ -- public-key metadata, rollups, alerts, and pressure data are reported as
12
+ -- preserved SQL rows; they are not deletion candidates.
13
+
14
+ WITH params AS (
15
+ SELECT
16
+ 'PlRt3bfcpJNnOyF_Wfgsh'::text AS org_id,
17
+ 'steve@builder.io'::text AS owner_email,
18
+ '2026-06-10T18:50:43.370Z'::text AS lookback_start,
19
+ '2026-08-09T19:11:44.834Z'::text AS cutoff,
20
+ 'http.response'::text AS excluded_event_name
21
+ ),
22
+ org_window AS (
23
+ SELECT e.id, e.event_name, e.received_at
24
+ FROM analytics_events AS e
25
+ CROSS JOIN params AS p
26
+ WHERE e.org_id = p.org_id
27
+ AND e.received_at >= p.lookback_start
28
+ AND e.received_at < p.cutoff
29
+ ),
30
+ org_older AS (
31
+ SELECT e.received_at
32
+ FROM analytics_events AS e
33
+ CROSS JOIN params AS p
34
+ WHERE e.org_id = p.org_id
35
+ AND e.received_at < p.lookback_start
36
+ ),
37
+ legacy_window AS (
38
+ SELECT e.received_at
39
+ FROM analytics_events AS e
40
+ CROSS JOIN params AS p
41
+ WHERE e.org_id IS NULL
42
+ AND e.owner_email = p.owner_email
43
+ AND e.received_at >= p.lookback_start
44
+ AND e.received_at < p.cutoff
45
+ ),
46
+ candidate AS (
47
+ SELECT w.id, w.event_name, w.received_at
48
+ FROM org_window AS w
49
+ CROSS JOIN params AS p
50
+ WHERE w.event_name IS DISTINCT FROM p.excluded_event_name
51
+ ),
52
+ metrics AS (
53
+ SELECT
54
+ 'events'::text AS section,
55
+ 'candidate_non_http_response'::text AS metric,
56
+ COUNT(*) AS row_count,
57
+ MIN(received_at) AS first_seen,
58
+ MAX(received_at) AS last_seen,
59
+ 'Only rows in the copied 60-day org window; not a delete.'::text AS notes
60
+ FROM candidate
61
+
62
+ UNION ALL
63
+ SELECT
64
+ 'events',
65
+ 'excluded_http_response',
66
+ COUNT(*),
67
+ MIN(received_at),
68
+ MAX(received_at),
69
+ 'Excluded from BigQuery backfill; do not delete without a separate decision.'
70
+ FROM org_window AS w
71
+ CROSS JOIN params AS p
72
+ WHERE w.event_name = p.excluded_event_name
73
+
74
+ UNION ALL
75
+ SELECT
76
+ 'events',
77
+ 'older_than_lookback',
78
+ COUNT(*),
79
+ MIN(received_at),
80
+ MAX(received_at),
81
+ 'Not copied by the 60-day migration; not a delete candidate.'
82
+ FROM org_older
83
+
84
+ UNION ALL
85
+ SELECT
86
+ 'events',
87
+ 'legacy_owner_only_in_window',
88
+ COUNT(*),
89
+ MIN(received_at),
90
+ MAX(received_at),
91
+ 'org_id IS NULL branch; migration checkpoint copied zero legacy rows.'
92
+ FROM legacy_window
93
+
94
+ UNION ALL
95
+ SELECT
96
+ 'event_names',
97
+ event_name,
98
+ COUNT(*),
99
+ MIN(received_at),
100
+ MAX(received_at),
101
+ 'Candidate rows grouped by event name.'
102
+ FROM candidate
103
+ GROUP BY event_name
104
+
105
+ UNION ALL
106
+ SELECT
107
+ 'preserved_sql',
108
+ 'analytics_event_daily_rollups',
109
+ COUNT(*),
110
+ NULL,
111
+ NULL,
112
+ 'Preserved derived event counts.'
113
+ FROM analytics_event_daily_rollups AS r
114
+ CROSS JOIN params AS p
115
+ WHERE r.org_id = p.org_id
116
+
117
+ UNION ALL
118
+ SELECT
119
+ 'preserved_sql',
120
+ 'analytics_user_days',
121
+ COUNT(*),
122
+ NULL,
123
+ NULL,
124
+ 'Preserved active-user/retention rows.'
125
+ FROM analytics_user_days AS u
126
+ CROSS JOIN params AS p
127
+ WHERE u.org_id = p.org_id
128
+
129
+ UNION ALL
130
+ SELECT
131
+ 'preserved_sql',
132
+ 'analytics_event_volume_usage',
133
+ COUNT(*),
134
+ NULL,
135
+ NULL,
136
+ 'Preserved volume-cap state.'
137
+ FROM analytics_event_volume_usage AS v
138
+ CROSS JOIN params AS p
139
+ WHERE v.org_id = p.org_id
140
+
141
+ UNION ALL
142
+ SELECT
143
+ 'preserved_sql',
144
+ 'analytics_query_pressure_daily',
145
+ COUNT(*),
146
+ NULL,
147
+ NULL,
148
+ 'Preserved query-pressure diagnostics.'
149
+ FROM analytics_query_pressure_daily AS q
150
+ CROSS JOIN params AS p
151
+ WHERE q.org_id = p.org_id
152
+
153
+ UNION ALL
154
+ SELECT
155
+ 'preserved_sql',
156
+ 'analytics_alert_rules',
157
+ COUNT(*),
158
+ NULL,
159
+ NULL,
160
+ 'Preserved alert definitions.'
161
+ FROM analytics_alert_rules AS a
162
+ CROSS JOIN params AS p
163
+ WHERE a.org_id = p.org_id
164
+
165
+ UNION ALL
166
+ SELECT
167
+ 'preserved_sql',
168
+ 'analytics_alert_incidents',
169
+ COUNT(*),
170
+ NULL,
171
+ NULL,
172
+ 'Preserved alert history.'
173
+ FROM analytics_alert_incidents AS i
174
+ CROSS JOIN params AS p
175
+ WHERE i.org_id = p.org_id
176
+
177
+ UNION ALL
178
+ SELECT
179
+ 'preserved_sql',
180
+ 'session_recordings',
181
+ COUNT(*),
182
+ MIN(started_at),
183
+ MAX(COALESCE(ended_at, started_at)),
184
+ 'Session replay summaries; never delete with event rows.'
185
+ FROM session_recordings AS s
186
+ CROSS JOIN params AS p
187
+ WHERE s.org_id = p.org_id
188
+
189
+ UNION ALL
190
+ SELECT
191
+ 'preserved_sql',
192
+ 'session_replay_chunks',
193
+ COUNT(*),
194
+ MIN(created_at),
195
+ MAX(created_at),
196
+ 'Session replay payload references; never delete with event rows.'
197
+ FROM session_replay_chunks AS c
198
+ CROSS JOIN params AS p
199
+ WHERE c.org_id = p.org_id
200
+
201
+ UNION ALL
202
+ SELECT
203
+ 'preserved_sql',
204
+ 'session_replay_ingests',
205
+ COUNT(*),
206
+ MIN(created_at),
207
+ MAX(created_at),
208
+ 'Session replay ingest audit rows; never delete with event rows.'
209
+ FROM session_replay_ingests AS g
210
+ CROSS JOIN params AS p
211
+ WHERE g.org_id = p.org_id
212
+
213
+ UNION ALL
214
+ SELECT
215
+ 'preserved_sql',
216
+ 'error_issues',
217
+ COUNT(*),
218
+ MIN(first_seen_at),
219
+ MAX(last_seen_at),
220
+ 'Grouped exception issues; preserved separately from analytics_events.'
221
+ FROM error_issues AS x
222
+ CROSS JOIN params AS p
223
+ WHERE x.org_id = p.org_id
224
+
225
+ UNION ALL
226
+ SELECT
227
+ 'preserved_sql',
228
+ 'error_events',
229
+ COUNT(*),
230
+ MIN(occurred_at),
231
+ MAX(occurred_at),
232
+ 'Exception occurrences; preserved separately from analytics_events.'
233
+ FROM error_events AS x
234
+ CROSS JOIN params AS p
235
+ WHERE x.org_id = p.org_id
236
+
237
+ UNION ALL
238
+ SELECT
239
+ 'preserved_sql',
240
+ 'analytics_public_keys',
241
+ COUNT(*),
242
+ MIN(created_at),
243
+ MAX(created_at),
244
+ 'Public-key metadata; preserved separately from analytics_events.'
245
+ FROM analytics_public_keys AS k
246
+ CROSS JOIN params AS p
247
+ WHERE k.org_id = p.org_id
248
+ )
249
+ SELECT section, metric, row_count, first_seen, last_seen, notes
250
+ FROM metrics
251
+ ORDER BY
252
+ CASE section
253
+ WHEN 'events' THEN 1
254
+ WHEN 'event_names' THEN 2
255
+ ELSE 3
256
+ END,
257
+ row_count DESC,
258
+ metric;
@@ -1,4 +1,5 @@
1
- import { and, count, eq, gte, isNull, or } from "drizzle-orm";
1
+ import { getDbExec } from "@agent-native/core/db";
2
+ import { and, eq, gte, isNull, or } from "drizzle-orm";
2
3
 
3
4
  import { getDb, schema } from "../db/index.js";
4
5
 
@@ -46,13 +47,47 @@ function windowPredicate(
46
47
  : and(predicate, gte(table.eventDate, window.startEventDate));
47
48
  }
48
49
 
49
- async function countScopedRows(table: any, predicate: any): Promise<number> {
50
- const [row] = await (getDb() as any)
51
- .select({ count: count() })
52
- .from(table)
53
- .where(predicate);
54
- const value = Number(row?.count ?? 0);
55
- return Number.isFinite(value) ? value : 0;
50
+ type CountTable =
51
+ | "analytics_events"
52
+ | "analytics_event_daily_rollups"
53
+ | "analytics_user_days";
54
+
55
+ type CountTimeColumn = "received_at" | "event_date";
56
+
57
+ async function countScopedRows(
58
+ table: CountTable,
59
+ timeColumn: CountTimeColumn,
60
+ scope: FirstPartyAnalyticsPurgeScope,
61
+ includeLegacyOwnerRows: boolean,
62
+ window: FirstPartyAnalyticsPurgeWindow,
63
+ ): Promise<number> {
64
+ const scopeSql = includeLegacyOwnerRows
65
+ ? "(org_id = ? OR (org_id IS NULL AND owner_email = ?))"
66
+ : "org_id = ?";
67
+ const args: unknown[] = includeLegacyOwnerRows
68
+ ? [scope.orgId, scope.userEmail]
69
+ : [scope.orgId];
70
+ args.push(
71
+ timeColumn === "received_at"
72
+ ? window.startReceivedAt
73
+ : window.startEventDate,
74
+ );
75
+
76
+ const { rows } = await getDbExec().execute({
77
+ sql: `SELECT COUNT(*) AS row_count
78
+ FROM ${table}
79
+ WHERE ${scopeSql}
80
+ AND ${timeColumn} >= ?`,
81
+ args,
82
+ timeoutMs: 5_000,
83
+ maxAttempts: 1,
84
+ });
85
+ const rawCount = (rows[0] as { row_count?: unknown } | undefined)?.row_count;
86
+ const value = Number(rawCount);
87
+ if (!Number.isFinite(value)) {
88
+ throw new Error(`Postgres count returned an invalid value for ${table}`);
89
+ }
90
+ return value;
56
91
  }
57
92
 
58
93
  export async function countFirstPartyAnalyticsPostgresRows(
@@ -60,31 +95,28 @@ export async function countFirstPartyAnalyticsPostgresRows(
60
95
  includeLegacyOwnerRows: boolean,
61
96
  window: FirstPartyAnalyticsPurgeWindow,
62
97
  ): Promise<FirstPartyAnalyticsPostgresPurgeCounts> {
63
- const predicates = [
64
- windowPredicate(
65
- schema.analyticsEvents,
66
- scopePredicate(schema.analyticsEvents, scope, includeLegacyOwnerRows),
98
+ const [eventRows, dailyRollupRows, userDayRows] = await Promise.all([
99
+ countScopedRows(
100
+ "analytics_events",
101
+ "received_at",
102
+ scope,
103
+ includeLegacyOwnerRows,
67
104
  window,
68
105
  ),
69
- windowPredicate(
70
- schema.analyticsEventDailyRollups,
71
- scopePredicate(
72
- schema.analyticsEventDailyRollups,
73
- scope,
74
- includeLegacyOwnerRows,
75
- ),
106
+ countScopedRows(
107
+ "analytics_event_daily_rollups",
108
+ "event_date",
109
+ scope,
110
+ includeLegacyOwnerRows,
76
111
  window,
77
112
  ),
78
- windowPredicate(
79
- schema.analyticsUserDays,
80
- scopePredicate(schema.analyticsUserDays, scope, includeLegacyOwnerRows),
113
+ countScopedRows(
114
+ "analytics_user_days",
115
+ "event_date",
116
+ scope,
117
+ includeLegacyOwnerRows,
81
118
  window,
82
119
  ),
83
- ];
84
- const [eventRows, dailyRollupRows, userDayRows] = await Promise.all([
85
- countScopedRows(schema.analyticsEvents, predicates[0]),
86
- countScopedRows(schema.analyticsEventDailyRollups, predicates[1]),
87
- countScopedRows(schema.analyticsUserDays, predicates[2]),
88
120
  ]);
89
121
  return { eventRows, dailyRollupRows, userDayRows };
90
122
  }
@@ -68,6 +68,7 @@ export function validateWorkspaceFeatureFlagMutation(
68
68
  expected: {
69
69
  key: string;
70
70
  orgDomain: string;
71
+ allowExplicitNoOrgTarget?: boolean;
71
72
  rules?: Record<string, unknown>;
72
73
  enabledForEmail?: string;
73
74
  },
@@ -82,7 +83,8 @@ export function validateWorkspaceFeatureFlagMutation(
82
83
  !Array.isArray(payload.rules) &&
83
84
  !!payload.scope &&
84
85
  typeof payload.scope === "object" &&
85
- payload.scope.orgDomain === expected.orgDomain;
86
+ (payload.scope.orgDomain === expected.orgDomain ||
87
+ (expected.allowExplicitNoOrgTarget && payload.scope.orgDomain === null));
86
88
  if (!valid)
87
89
  throw new Error(
88
90
  "The target app returned an unsupported or unverified feature flag mutation response.",
@@ -360,6 +362,7 @@ export async function setWorkspaceFeatureFlag(
360
362
  return validateWorkspaceFeatureFlagMutation(result.body, {
361
363
  key: input.key,
362
364
  orgDomain,
365
+ allowExplicitNoOrgTarget: true,
363
366
  ...(input.operation === "replace-rules" && input.rules
364
367
  ? {
365
368
  rules: {