@agent-native/core-corpus 0.209.2 → 0.209.3-nightly-20261016183441

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 (62) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
  3. package/corpus/templates/analytics/actions/update-dashboard.ts +9 -0
  4. package/corpus/templates/analytics/app/components/auth/SqlQuerySnapshotSessionBridge.tsx +25 -0
  5. package/corpus/templates/analytics/app/components/dashboard/PanelFreshnessClock.tsx +140 -0
  6. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +104 -48
  7. package/corpus/templates/analytics/app/components/dashboard/panel-freshness.ts +171 -0
  8. package/corpus/templates/analytics/app/global.css +59 -0
  9. package/corpus/templates/analytics/app/i18n/en-US.ts +8 -0
  10. package/corpus/templates/analytics/app/lib/cost-tracker.ts +122 -16
  11. package/corpus/templates/analytics/app/lib/query-metrics.ts +50 -13
  12. package/corpus/templates/analytics/app/lib/sql-query-cache.ts +697 -0
  13. package/corpus/templates/analytics/app/lib/sql-query-freshness.ts +85 -0
  14. package/corpus/templates/analytics/app/lib/sql-query-snapshot-session.ts +145 -0
  15. package/corpus/templates/analytics/app/lib/sql-query.ts +366 -36
  16. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +73 -18
  17. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/filter-vars.ts +45 -12
  18. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/types.ts +5 -0
  19. package/corpus/templates/analytics/app/root.tsx +2 -0
  20. package/corpus/templates/analytics/changelog/2026-10-14-dashboard-charts-now-reappear-instantly-after-a-reload-from-.md +6 -0
  21. package/corpus/templates/analytics/changelog/2026-10-14-dashboard-panels-show-fetch-time-in-a-clock-and-label-only-st.md +6 -0
  22. package/corpus/templates/analytics/changelog/2026-10-15-dashboard-query-costs-and-refresh-confirmations-reflect-actu.md +6 -0
  23. package/corpus/templates/analytics/changelog/2026-10-15-dashboard-results-disappear-when-access-is-withdrawn-and-exp.md +6 -0
  24. package/corpus/templates/analytics/changelog/2026-10-16-dashboard-freshness-stays-in-the-title-row-with-compact-upda.md +6 -0
  25. package/corpus/templates/analytics/changelog/2026-10-16-dashboard-update-status-remains-visible-for-sources-without-.md +6 -0
  26. package/corpus/templates/analytics/server/lib/bigquery.ts +35 -4
  27. package/corpus/templates/analytics/server/lib/dashboard-panel-query.ts +2 -3
  28. package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +9 -6
  29. package/corpus/templates/analytics/server/lib/first-party-analytics-cache.ts +2 -2
  30. package/corpus/templates/analytics/server/lib/first-party-analytics.ts +4 -2
  31. package/corpus/templates/analytics/shared/sql-query-contract.ts +49 -0
  32. package/corpus/templates/assets/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
  33. package/corpus/templates/brain/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
  34. package/corpus/templates/calendar/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
  35. package/corpus/templates/chat/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
  36. package/corpus/templates/clips/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
  37. package/corpus/templates/content/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
  38. package/corpus/templates/crm/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
  39. package/corpus/templates/design/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
  40. package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +24 -0
  41. package/corpus/templates/design/app/components/design/types.ts +1 -0
  42. package/corpus/templates/design/app/lib/design-import.ts +35 -0
  43. package/corpus/templates/design/app/pages/design-editor/clone-and-pen-edit.ts +11 -0
  44. package/corpus/templates/design/app/pages/design-editor/command-types.ts +2 -0
  45. package/corpus/templates/design/app/pages/design-editor/commands/copy-selection.ts +3 -0
  46. package/corpus/templates/design/app/pages/design-editor/commands/get-selected-layer-snapshots.ts +19 -0
  47. package/corpus/templates/design/app/pages/design-editor/commands/paste-selection.ts +118 -20
  48. package/corpus/templates/design/app/pages/design-editor/domains/use-editor-layer-actions.ts +15 -3
  49. package/corpus/templates/design/app/pages/design-editor/relative-position-offset.ts +109 -0
  50. package/corpus/templates/design/changelog/2026-10-15-dragged-layers-use-the-final-release-position.md +6 -0
  51. package/corpus/templates/design/changelog/2026-10-15-repeated-pastes-preserve-relative-layer-offsets.md +6 -0
  52. package/corpus/templates/design/changelog/2026-10-15-text-wrapping-hug-widths.md +6 -0
  53. package/corpus/templates/design/shared/code-layer.ts +2 -1
  54. package/corpus/templates/design/shared/relative-position-containing-block.ts +40 -0
  55. package/corpus/templates/dispatch/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
  56. package/corpus/templates/forms/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
  57. package/corpus/templates/mail/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
  58. package/corpus/templates/plan/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
  59. package/corpus/templates/slides/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
  60. package/corpus/templates/slides/app/components/editor/NewDeckReferenceStep.tsx +137 -118
  61. package/corpus/templates/slides/changelog/2026-10-16-search-and-preview-reference-decks-before-choosing-one.md +6 -0
  62. package/package.json +1 -1
@@ -1,28 +1,96 @@
1
+ // An estimate: `totalBytesProcessed` at the on-demand list price. It is not
2
+ // the bill and not a bound on it. BigQuery bills `totalBytesBilled`, and
3
+ // capacity pricing or the free allowance can make the actual bill lower.
1
4
  const COST_PER_BYTE = 6.25 / 1_000_000_000_000;
2
- const STORAGE_KEY = "analytics_query_cost";
5
+ const STORAGE_KEY = "analytics_query_cost/v3";
6
+ // Earlier counters included cache hits (v1) or unconfirmed misses (v2).
7
+ // Aggregated totals cannot recover that provenance.
8
+ const LEGACY_STORAGE_KEYS = ["analytics_query_cost", "analytics_query_cost/v2"];
3
9
  const TTL_MS = 24 * 60 * 60 * 1000;
4
10
 
5
11
  interface StoredCost {
12
+ /** Bytes processed by queries that actually scanned. */
6
13
  bytes: number;
14
+ /** Failed/cancelled queries or positive bytes with unknown cache provenance. */
15
+ unknownScans: number;
7
16
  startedAt: number;
8
17
  }
9
18
 
19
+ export type QueryCostRecord = {
20
+ status: "success" | "error" | "cancelled";
21
+ /** App-side cache (L1/L2) served the result; absent means unknown. */
22
+ cached?: boolean;
23
+ /** BigQuery served the result from its own query cache; absent means unknown. */
24
+ providerCacheHit?: boolean;
25
+ /** Rows came from a browser snapshot, so no request produced them. */
26
+ restored?: boolean;
27
+ bytesProcessed?: unknown;
28
+ };
29
+
30
+ export type QueryCostOutcome =
31
+ | { counted: number; reason: "scanned" }
32
+ | {
33
+ counted: 0;
34
+ reason:
35
+ | "app-cache"
36
+ | "provider-cache"
37
+ | "restored"
38
+ | "no-bytes"
39
+ | "invalid-bytes";
40
+ }
41
+ | { counted: 0; reason: "unknown-scan" };
42
+
43
+ function fresh(): StoredCost {
44
+ return { bytes: 0, unknownScans: 0, startedAt: Date.now() };
45
+ }
46
+
47
+ function isCount(value: unknown): value is number {
48
+ return typeof value === "number" && Number.isFinite(value) && value >= 0;
49
+ }
50
+
51
+ function warn(message: string, error?: unknown) {
52
+ // Names only: stored values and messages are not logged.
53
+ console.warn(
54
+ `[cost-tracker] ${message}`,
55
+ error instanceof Error ? error.name : undefined,
56
+ );
57
+ }
58
+
10
59
  function load(): StoredCost {
11
- if (typeof window === "undefined") {
12
- return { bytes: 0, startedAt: Date.now() };
13
- }
60
+ if (typeof window === "undefined") return fresh();
14
61
 
15
62
  try {
63
+ for (const key of LEGACY_STORAGE_KEYS) {
64
+ if (window.localStorage.getItem(key) !== null) {
65
+ window.localStorage.removeItem(key);
66
+ warn("discarded a legacy query cost with unverified cache provenance");
67
+ }
68
+ }
16
69
  const raw = window.localStorage.getItem(STORAGE_KEY);
17
- if (!raw) return { bytes: 0, startedAt: Date.now() };
18
- const parsed: StoredCost = JSON.parse(raw);
70
+ if (!raw) return fresh();
71
+ const parsed: Partial<StoredCost> | null = JSON.parse(raw);
72
+ if (
73
+ !parsed ||
74
+ !isCount(parsed.bytes) ||
75
+ !isCount(parsed.unknownScans) ||
76
+ !isCount(parsed.startedAt)
77
+ ) {
78
+ warn("discarded an invalid stored query cost");
79
+ window.localStorage.removeItem(STORAGE_KEY);
80
+ return fresh();
81
+ }
19
82
  if (Date.now() - parsed.startedAt > TTL_MS) {
20
83
  window.localStorage.removeItem(STORAGE_KEY);
21
- return { bytes: 0, startedAt: Date.now() };
84
+ return fresh();
22
85
  }
23
- return parsed;
24
- } catch {
25
- return { bytes: 0, startedAt: Date.now() };
86
+ return {
87
+ bytes: parsed.bytes,
88
+ unknownScans: parsed.unknownScans,
89
+ startedAt: parsed.startedAt,
90
+ };
91
+ } catch (error) {
92
+ warn("could not read stored query cost", error);
93
+ return fresh();
26
94
  }
27
95
  }
28
96
 
@@ -30,8 +98,9 @@ function save(data: StoredCost) {
30
98
  if (typeof window === "undefined") return;
31
99
  try {
32
100
  window.localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
33
- } catch {
34
- // Ignore storage failures; cost tracking is advisory UI state.
101
+ } catch (error) {
102
+ // Cost tracking is advisory UI state; the in-memory total still counts.
103
+ warn("could not store query cost", error);
35
104
  }
36
105
  }
37
106
 
@@ -42,17 +111,54 @@ function notify() {
42
111
  listeners.forEach((fn) => fn());
43
112
  }
44
113
 
45
- export function addBytesProcessed(bytes: number) {
46
- if (bytes <= 0) return;
47
- state.bytes += bytes;
114
+ function queryCostOutcome(record: QueryCostRecord): QueryCostOutcome {
115
+ if (record.status !== "success") {
116
+ return { counted: 0, reason: "unknown-scan" };
117
+ }
118
+ if (record.restored) return { counted: 0, reason: "restored" };
119
+ if (record.cached) return { counted: 0, reason: "app-cache" };
120
+ if (record.providerCacheHit) return { counted: 0, reason: "provider-cache" };
121
+ const bytes = record.bytesProcessed;
122
+ if (bytes === undefined || bytes === null || bytes === 0) {
123
+ return { counted: 0, reason: "no-bytes" };
124
+ }
125
+ if (typeof bytes !== "number" || !Number.isFinite(bytes) || bytes <= 0) {
126
+ return { counted: 0, reason: "invalid-bytes" };
127
+ }
128
+ if (record.cached !== false || record.providerCacheHit !== false) {
129
+ return { counted: 0, reason: "unknown-scan" };
130
+ }
131
+ return { counted: bytes, reason: "scanned" };
132
+ }
133
+
134
+ /**
135
+ * Cache hits can carry the original scan's bytes. Positive bytes count only
136
+ * when both cache layers confirm a miss; missing provenance and failed or
137
+ * cancelled requests remain unknown rather than being priced as fresh scans.
138
+ */
139
+ export function recordQueryCost(record: QueryCostRecord): QueryCostOutcome {
140
+ const outcome = queryCostOutcome(record);
141
+ if (outcome.reason === "unknown-scan") state.unknownScans += 1;
142
+ else if (outcome.reason === "scanned") state.bytes += outcome.counted;
143
+ else return outcome;
48
144
  save(state);
49
145
  notify();
146
+ return outcome;
147
+ }
148
+
149
+ /** Legacy callers supply no cache provenance, so positive bytes stay unknown. */
150
+ export function addBytesProcessed(bytes: number): QueryCostOutcome {
151
+ return recordQueryCost({ status: "success", bytesProcessed: bytes });
50
152
  }
51
153
 
52
154
  export function getTotalBytes(): number {
53
155
  return state.bytes;
54
156
  }
55
157
 
158
+ export function getUnknownScans(): number {
159
+ return state.unknownScans;
160
+ }
161
+
56
162
  export function getTotalCost(): number {
57
163
  return state.bytes * COST_PER_BYTE;
58
164
  }
@@ -78,7 +184,7 @@ export function subscribe(fn: () => void): () => void {
78
184
  }
79
185
 
80
186
  export function resetSession() {
81
- state = { bytes: 0, startedAt: Date.now() };
187
+ state = fresh();
82
188
  save(state);
83
189
  notify();
84
190
  }
@@ -2,7 +2,7 @@ import { appApiPath } from "@agent-native/core/client/api-path";
2
2
  import { useQuery } from "@tanstack/react-query";
3
3
 
4
4
  import { getIdToken } from "./auth";
5
- import { addBytesProcessed } from "./cost-tracker";
5
+ import { recordQueryCost } from "./cost-tracker";
6
6
 
7
7
  export interface QueryMetricsResult {
8
8
  rows: Record<string, unknown>[];
@@ -12,16 +12,31 @@ export interface QueryMetricsResult {
12
12
 
13
13
  export async function queryMetrics(sql: string): Promise<QueryMetricsResult> {
14
14
  const token = await getIdToken();
15
- const res = await fetch(appApiPath("/api/query"), {
16
- method: "POST",
17
- headers: {
18
- "Content-Type": "application/json",
19
- ...(token && { Authorization: `Bearer ${token}` }),
20
- },
21
- body: JSON.stringify({ query: sql }),
22
- });
15
+ let res: Response;
16
+ try {
17
+ res = await fetch(appApiPath("/api/query"), {
18
+ method: "POST",
19
+ headers: {
20
+ "Content-Type": "application/json",
21
+ ...(token && { Authorization: `Bearer ${token}` }),
22
+ },
23
+ body: JSON.stringify({ query: sql }),
24
+ });
25
+ } catch (error) {
26
+ // A request that was sent and lost may still have scanned.
27
+ recordQueryCost({
28
+ status:
29
+ error instanceof Error && error.name === "AbortError"
30
+ ? "cancelled"
31
+ : "error",
32
+ });
33
+ throw error;
34
+ }
23
35
 
24
36
  if (!res.ok) {
37
+ // /api/query returns 401 before runQuery; provider failures are mapped to
38
+ // 400. Keep other failures unknown because they may have scanned.
39
+ if (res.status !== 401) recordQueryCost({ status: "error" });
25
40
  const body = await res.json().catch(() => ({}));
26
41
  return {
27
42
  rows: [],
@@ -29,12 +44,34 @@ export async function queryMetrics(sql: string): Promise<QueryMetricsResult> {
29
44
  };
30
45
  }
31
46
 
32
- const data = await res.json();
33
-
34
- if (data.bytesProcessed) {
35
- addBytesProcessed(data.bytesProcessed);
47
+ let data: {
48
+ rows?: Record<string, unknown>[];
49
+ schema?: { name: string; type: string }[];
50
+ bytesProcessed?: unknown;
51
+ cached?: unknown;
52
+ providerCacheHit?: unknown;
53
+ };
54
+ try {
55
+ data = await res.json();
56
+ } catch (error) {
57
+ recordQueryCost({
58
+ status:
59
+ error instanceof Error && error.name === "AbortError"
60
+ ? "cancelled"
61
+ : "error",
62
+ });
63
+ throw error;
36
64
  }
37
65
 
66
+ recordQueryCost({
67
+ status: "success",
68
+ ...(typeof data.cached === "boolean" ? { cached: data.cached } : {}),
69
+ ...(typeof data.providerCacheHit === "boolean"
70
+ ? { providerCacheHit: data.providerCacheHit }
71
+ : {}),
72
+ bytesProcessed: data.bytesProcessed,
73
+ });
74
+
38
75
  return {
39
76
  rows: data.rows ?? [],
40
77
  schema: data.schema,