@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.
- package/corpus/README.md +1 -1
- package/corpus/templates/analytics/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
- package/corpus/templates/analytics/actions/update-dashboard.ts +9 -0
- package/corpus/templates/analytics/app/components/auth/SqlQuerySnapshotSessionBridge.tsx +25 -0
- package/corpus/templates/analytics/app/components/dashboard/PanelFreshnessClock.tsx +140 -0
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +104 -48
- package/corpus/templates/analytics/app/components/dashboard/panel-freshness.ts +171 -0
- package/corpus/templates/analytics/app/global.css +59 -0
- package/corpus/templates/analytics/app/i18n/en-US.ts +8 -0
- package/corpus/templates/analytics/app/lib/cost-tracker.ts +122 -16
- package/corpus/templates/analytics/app/lib/query-metrics.ts +50 -13
- package/corpus/templates/analytics/app/lib/sql-query-cache.ts +697 -0
- package/corpus/templates/analytics/app/lib/sql-query-freshness.ts +85 -0
- package/corpus/templates/analytics/app/lib/sql-query-snapshot-session.ts +145 -0
- package/corpus/templates/analytics/app/lib/sql-query.ts +366 -36
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +73 -18
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/filter-vars.ts +45 -12
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/types.ts +5 -0
- package/corpus/templates/analytics/app/root.tsx +2 -0
- package/corpus/templates/analytics/changelog/2026-10-14-dashboard-charts-now-reappear-instantly-after-a-reload-from-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-14-dashboard-panels-show-fetch-time-in-a-clock-and-label-only-st.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-15-dashboard-query-costs-and-refresh-confirmations-reflect-actu.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-15-dashboard-results-disappear-when-access-is-withdrawn-and-exp.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-16-dashboard-freshness-stays-in-the-title-row-with-compact-upda.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-16-dashboard-update-status-remains-visible-for-sources-without-.md +6 -0
- package/corpus/templates/analytics/server/lib/bigquery.ts +35 -4
- package/corpus/templates/analytics/server/lib/dashboard-panel-query.ts +2 -3
- package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +9 -6
- package/corpus/templates/analytics/server/lib/first-party-analytics-cache.ts +2 -2
- package/corpus/templates/analytics/server/lib/first-party-analytics.ts +4 -2
- package/corpus/templates/analytics/shared/sql-query-contract.ts +49 -0
- package/corpus/templates/assets/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
- package/corpus/templates/brain/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
- package/corpus/templates/calendar/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
- package/corpus/templates/chat/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
- package/corpus/templates/clips/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
- package/corpus/templates/content/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
- package/corpus/templates/crm/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
- package/corpus/templates/design/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
- package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +24 -0
- package/corpus/templates/design/app/components/design/types.ts +1 -0
- package/corpus/templates/design/app/lib/design-import.ts +35 -0
- package/corpus/templates/design/app/pages/design-editor/clone-and-pen-edit.ts +11 -0
- package/corpus/templates/design/app/pages/design-editor/command-types.ts +2 -0
- package/corpus/templates/design/app/pages/design-editor/commands/copy-selection.ts +3 -0
- package/corpus/templates/design/app/pages/design-editor/commands/get-selected-layer-snapshots.ts +19 -0
- package/corpus/templates/design/app/pages/design-editor/commands/paste-selection.ts +118 -20
- package/corpus/templates/design/app/pages/design-editor/domains/use-editor-layer-actions.ts +15 -3
- package/corpus/templates/design/app/pages/design-editor/relative-position-offset.ts +109 -0
- package/corpus/templates/design/changelog/2026-10-15-dragged-layers-use-the-final-release-position.md +6 -0
- package/corpus/templates/design/changelog/2026-10-15-repeated-pastes-preserve-relative-layer-offsets.md +6 -0
- package/corpus/templates/design/changelog/2026-10-15-text-wrapping-hug-widths.md +6 -0
- package/corpus/templates/design/shared/code-layer.ts +2 -1
- package/corpus/templates/design/shared/relative-position-containing-block.ts +40 -0
- package/corpus/templates/dispatch/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
- package/corpus/templates/forms/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
- package/corpus/templates/mail/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
- package/corpus/templates/plan/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
- package/corpus/templates/slides/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
- package/corpus/templates/slides/app/components/editor/NewDeckReferenceStep.tsx +137 -118
- package/corpus/templates/slides/changelog/2026-10-16-search-and-preview-reference-decks-before-choosing-one.md +6 -0
- 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
|
|
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
|
|
84
|
+
return fresh();
|
|
22
85
|
}
|
|
23
|
-
return
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
46
|
-
if (
|
|
47
|
-
|
|
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 =
|
|
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 {
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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,
|