@agent-native/core 0.201.0-nightly-20261005153421 → 0.201.0
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/README.md +22 -22
- package/dist/a2a/server.d.ts +2 -2
- package/dist/a2a/server.js +13 -2
- package/dist/agent/thread-data-builder.d.ts +4 -0
- package/dist/agent/thread-data-builder.js +8 -0
- package/dist/agent-sql/analysis.d.ts +31 -0
- package/dist/agent-sql/analysis.js +169 -0
- package/dist/agent-sql/index.d.ts +4 -0
- package/dist/agent-sql/index.js +4 -0
- package/dist/agent-sql/lexer.d.ts +37 -0
- package/dist/agent-sql/lexer.js +357 -0
- package/dist/agent-sql/postgres.d.ts +51 -0
- package/dist/agent-sql/postgres.js +197 -0
- package/dist/agent-sql/query.d.ts +30 -0
- package/dist/agent-sql/query.js +514 -0
- package/dist/catalog.json +24 -24
- package/dist/client/analytics/index.d.ts +1 -1
- package/dist/client/analytics/index.js +1 -1
- package/dist/client/analytics.d.ts +8 -0
- package/dist/client/analytics.js +17 -2
- package/dist/client/session-replay.js +3 -2
- package/dist/identity/offboard.js +31 -3
- package/dist/identity/rekey.d.ts +12 -1
- package/dist/identity/rekey.js +77 -6
- package/dist/identity/retired-emails.d.ts +23 -0
- package/dist/identity/retired-emails.js +55 -0
- package/dist/mcp/actions/create-org-service-token.js +20 -8
- package/dist/mcp/actions/service-token-access.d.ts +1 -0
- package/dist/mcp/actions/service-token-access.js +41 -24
- package/dist/mcp/build-server.d.ts +20 -6
- package/dist/mcp/build-server.js +131 -35
- package/dist/mcp/connect-route.d.ts +4 -3
- package/dist/mcp/connect-route.js +140 -82
- package/dist/mcp/connect-store.d.ts +17 -11
- package/dist/mcp/connect-store.js +88 -60
- package/dist/mcp/credential-issuance.d.ts +23 -0
- package/dist/mcp/credential-issuance.js +77 -0
- package/dist/mcp/credential-membership.d.ts +26 -0
- package/dist/mcp/credential-membership.js +67 -0
- package/dist/mcp/oauth-route.js +133 -45
- package/dist/mcp/oauth-store.d.ts +15 -5
- package/dist/mcp/oauth-store.js +119 -34
- package/dist/mcp/oauth-token.d.ts +4 -0
- package/dist/mcp/oauth-token.js +5 -0
- package/dist/mcp/screen-memory-stdio.d.ts +7 -7
- package/dist/mcp/server.js +14 -0
- package/dist/onboarding/default-steps.js +1 -1
- package/dist/org/handlers.d.ts +1 -1
- package/dist/org/index.d.ts +2 -2
- package/dist/org/membership.d.ts +3 -1
- package/dist/org/membership.js +5 -2
- package/dist/provider-api/actions/custom-provider-registration.d.ts +12 -12
- package/dist/provider-api/actions/provider-api.d.ts +10 -10
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/resource-changes/store.d.ts +2 -0
- package/dist/resource-changes/store.js +4 -0
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/scripts/db/exec.js +28 -101
- package/dist/scripts/db/patch.js +14 -34
- package/dist/scripts/db/postgres-client.d.ts +8 -1
- package/dist/scripts/db/postgres-client.js +20 -6
- package/dist/scripts/db/query.js +31 -33
- package/dist/scripts/db/safety.d.ts +47 -2
- package/dist/scripts/db/safety.js +383 -137
- package/dist/scripts/db/scoping.d.ts +1 -0
- package/dist/scripts/db/scoping.js +3 -2
- package/dist/search/index-store.d.ts +21 -0
- package/dist/search/index-store.js +47 -0
- package/dist/search/indexer.js +35 -32
- package/dist/search/registry.d.ts +6 -3
- package/dist/search/registry.js +30 -8
- package/dist/secrets/routes.d.ts +6 -6
- package/dist/server/agent-chat-plugin.js +18 -6
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/agent-engine-default-model-route.d.ts +1 -1
- package/dist/server/agent-run-context.js +9 -0
- package/dist/server/attribution.d.ts +2 -0
- package/dist/server/attribution.js +7 -1
- package/dist/server/auth.js +9 -0
- package/dist/server/credential-membership-unavailable.d.ts +13 -0
- package/dist/server/credential-membership-unavailable.js +22 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +1 -0
- package/dist/server/onboarding-html.js +1 -1
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/recap-image-route.js +4 -0
- package/dist/server/release-schema.js +4 -0
- package/dist/shared/docs-url.d.ts +3 -1
- package/dist/shared/docs-url.js +6 -3
- package/dist/shared/environment-lanes.d.ts +8 -0
- package/dist/shared/environment-lanes.js +26 -0
- package/dist/templates/chat/.agents/skills/security/SKILL.md +9 -2
- package/dist/templates/chat/.agents/skills/storing-data/SKILL.md +12 -7
- package/dist/templates/chat/README.md +2 -2
- package/dist/templates/default/.agents/skills/security/SKILL.md +9 -2
- package/dist/templates/default/.agents/skills/storing-data/SKILL.md +12 -7
- package/dist/templates/default/app/routes/home.tsx +5 -1
- package/dist/templates/headless/.agents/skills/security/SKILL.md +9 -2
- package/dist/templates/headless/.agents/skills/storing-data/SKILL.md +12 -7
- package/dist/templates/workspace-core/.agents/skills/security/SKILL.md +9 -2
- package/dist/templates/workspace-core/.agents/skills/storing-data/SKILL.md +12 -7
- package/dist/tracking/providers.js +13 -1
- package/docs/content/authentication.mdx +5 -1
- package/docs/content/durable-background-runs.mdx +17 -8
- package/docs/content/external-agents.mdx +3 -1
- package/docs/content/mcp-protocol.mdx +6 -2
- package/docs/content/security.mdx +1 -1
- package/package.json +5 -4
- package/src/templates/chat/.agents/skills/security/SKILL.md +9 -2
- package/src/templates/chat/.agents/skills/storing-data/SKILL.md +12 -7
- package/src/templates/chat/README.md +2 -2
- package/src/templates/default/.agents/skills/security/SKILL.md +9 -2
- package/src/templates/default/.agents/skills/storing-data/SKILL.md +12 -7
- package/src/templates/default/app/routes/home.tsx +5 -1
- package/src/templates/headless/.agents/skills/security/SKILL.md +9 -2
- package/src/templates/headless/.agents/skills/storing-data/SKILL.md +12 -7
- package/src/templates/workspace-core/.agents/skills/security/SKILL.md +9 -2
- package/src/templates/workspace-core/.agents/skills/storing-data/SKILL.md +12 -7
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
import { AgentSqlSyntaxError, lexAgentSql, } from "./lexer.js";
|
|
2
|
+
const CLAUSES = new Set([
|
|
3
|
+
"where",
|
|
4
|
+
"group",
|
|
5
|
+
"having",
|
|
6
|
+
"window",
|
|
7
|
+
"qualify",
|
|
8
|
+
"order",
|
|
9
|
+
"limit",
|
|
10
|
+
"offset",
|
|
11
|
+
"fetch",
|
|
12
|
+
"for",
|
|
13
|
+
]);
|
|
14
|
+
const JOIN_WORDS = new Set([
|
|
15
|
+
"join",
|
|
16
|
+
"inner",
|
|
17
|
+
"left",
|
|
18
|
+
"right",
|
|
19
|
+
"full",
|
|
20
|
+
"cross",
|
|
21
|
+
"natural",
|
|
22
|
+
]);
|
|
23
|
+
const RESERVED_SOURCE_WORDS = new Set([
|
|
24
|
+
...CLAUSES,
|
|
25
|
+
...JOIN_WORDS,
|
|
26
|
+
"select",
|
|
27
|
+
"with",
|
|
28
|
+
"from",
|
|
29
|
+
"as",
|
|
30
|
+
"on",
|
|
31
|
+
"using",
|
|
32
|
+
"outer",
|
|
33
|
+
"union",
|
|
34
|
+
"intersect",
|
|
35
|
+
"except",
|
|
36
|
+
"only",
|
|
37
|
+
"lateral",
|
|
38
|
+
"rows",
|
|
39
|
+
"table",
|
|
40
|
+
"values",
|
|
41
|
+
"tablesample",
|
|
42
|
+
"pivot",
|
|
43
|
+
"unpivot",
|
|
44
|
+
"match_recognize",
|
|
45
|
+
]);
|
|
46
|
+
function isName(token) {
|
|
47
|
+
return token?.kind === "word" || token?.kind === "quoted-identifier";
|
|
48
|
+
}
|
|
49
|
+
function word(token) {
|
|
50
|
+
return token?.kind === "word" ? token.value : undefined;
|
|
51
|
+
}
|
|
52
|
+
function punctuation(token, text) {
|
|
53
|
+
return token?.kind === "punctuation" && token.text === text;
|
|
54
|
+
}
|
|
55
|
+
class QueryReader {
|
|
56
|
+
sql;
|
|
57
|
+
tokens;
|
|
58
|
+
dialect;
|
|
59
|
+
sources = [];
|
|
60
|
+
ctes = [];
|
|
61
|
+
pairs = new Map();
|
|
62
|
+
constructor(sql, tokens, dialect) {
|
|
63
|
+
this.sql = sql;
|
|
64
|
+
this.tokens = tokens;
|
|
65
|
+
this.dialect = dialect;
|
|
66
|
+
if (tokens.length > 50_000)
|
|
67
|
+
this.fail("The SQL has too many tokens.", 0);
|
|
68
|
+
const stack = [];
|
|
69
|
+
for (let index = 0; index < tokens.length; index++) {
|
|
70
|
+
const token = tokens[index];
|
|
71
|
+
if (punctuation(token, "(") || punctuation(token, "[")) {
|
|
72
|
+
stack.push(index);
|
|
73
|
+
if (stack.length > 128)
|
|
74
|
+
this.fail("The SQL is nested too deeply.", index);
|
|
75
|
+
}
|
|
76
|
+
else if (punctuation(token, ")") || punctuation(token, "]")) {
|
|
77
|
+
const open = stack.pop();
|
|
78
|
+
if (open === undefined ||
|
|
79
|
+
(token.text === ")"
|
|
80
|
+
? tokens[open].text !== "("
|
|
81
|
+
: tokens[open].text !== "[")) {
|
|
82
|
+
this.fail("The SQL has mismatched delimiters.", index);
|
|
83
|
+
}
|
|
84
|
+
this.pairs.set(open, index);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (stack.length > 0)
|
|
88
|
+
this.fail("The SQL has an unclosed delimiter.", stack[0]);
|
|
89
|
+
}
|
|
90
|
+
fail(message, index) {
|
|
91
|
+
throw new AgentSqlSyntaxError("unsupported_syntax", message, this.tokens[index]?.start ?? this.sql.length);
|
|
92
|
+
}
|
|
93
|
+
close(index) {
|
|
94
|
+
const close = this.pairs.get(index);
|
|
95
|
+
if (close === undefined)
|
|
96
|
+
this.fail("Expected a delimited SQL expression.", index);
|
|
97
|
+
return close;
|
|
98
|
+
}
|
|
99
|
+
name(token) {
|
|
100
|
+
// BigQuery dataset/table names preserve case even when unquoted.
|
|
101
|
+
const name = this.dialect === "postgres"
|
|
102
|
+
? token.value
|
|
103
|
+
: token.kind === "word"
|
|
104
|
+
? token.text
|
|
105
|
+
: token.value;
|
|
106
|
+
if (this.dialect === "postgres" &&
|
|
107
|
+
new TextEncoder().encode(name).length > 63) {
|
|
108
|
+
this.fail("Postgres identifiers longer than 63 bytes are not supported.", this.tokens.indexOf(token));
|
|
109
|
+
}
|
|
110
|
+
return name;
|
|
111
|
+
}
|
|
112
|
+
binding(name) {
|
|
113
|
+
return this.dialect === "bigquery"
|
|
114
|
+
? name.replace(/[A-Z]/g, (letter) => letter.toLowerCase())
|
|
115
|
+
: name;
|
|
116
|
+
}
|
|
117
|
+
identifier(index) {
|
|
118
|
+
const token = this.tokens[index];
|
|
119
|
+
if (!isName(token) ||
|
|
120
|
+
(token.kind === "word" && RESERVED_SOURCE_WORDS.has(token.value))) {
|
|
121
|
+
this.fail("Expected an unambiguous SQL source identifier.", index);
|
|
122
|
+
}
|
|
123
|
+
return token;
|
|
124
|
+
}
|
|
125
|
+
queryStart(start, end) {
|
|
126
|
+
while (punctuation(this.tokens[start], "(") &&
|
|
127
|
+
this.close(start) === end - 1) {
|
|
128
|
+
start++;
|
|
129
|
+
end--;
|
|
130
|
+
}
|
|
131
|
+
const keyword = word(this.tokens[start]);
|
|
132
|
+
// TABLE reads a relation without FROM and must not become an ordinary expression.
|
|
133
|
+
if (this.dialect === "postgres" &&
|
|
134
|
+
(keyword === "table" || keyword === "values")) {
|
|
135
|
+
this.fail("Only SELECT query expressions are supported.", start);
|
|
136
|
+
}
|
|
137
|
+
return keyword === "select" || keyword === "with";
|
|
138
|
+
}
|
|
139
|
+
columns(start) {
|
|
140
|
+
const end = this.close(start);
|
|
141
|
+
let cursor = start + 1;
|
|
142
|
+
if (cursor === end)
|
|
143
|
+
this.fail("Expected at least one column name.", cursor);
|
|
144
|
+
while (cursor < end) {
|
|
145
|
+
this.identifier(cursor++);
|
|
146
|
+
if (cursor === end)
|
|
147
|
+
break;
|
|
148
|
+
if (!punctuation(this.tokens[cursor++], ",") || cursor === end) {
|
|
149
|
+
this.fail("Expected a comma-separated column name list.", cursor - 1);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return end + 1;
|
|
153
|
+
}
|
|
154
|
+
withClause(start, end, inherited) {
|
|
155
|
+
let cursor = start + 1;
|
|
156
|
+
const recursive = word(this.tokens[cursor]) === "recursive";
|
|
157
|
+
if (recursive)
|
|
158
|
+
cursor++;
|
|
159
|
+
const definitions = [];
|
|
160
|
+
const names = new Set();
|
|
161
|
+
while (cursor < end) {
|
|
162
|
+
const token = this.identifier(cursor++);
|
|
163
|
+
const cte = {
|
|
164
|
+
name: this.name(token),
|
|
165
|
+
quoted: token.kind === "quoted-identifier",
|
|
166
|
+
start: token.start,
|
|
167
|
+
end: token.end,
|
|
168
|
+
};
|
|
169
|
+
const key = this.binding(cte.name);
|
|
170
|
+
if (names.has(key))
|
|
171
|
+
this.fail("Duplicate CTE names are not supported.", cursor - 1);
|
|
172
|
+
names.add(key);
|
|
173
|
+
if (punctuation(this.tokens[cursor], "("))
|
|
174
|
+
cursor = this.columns(cursor);
|
|
175
|
+
if (word(this.tokens[cursor++]) !== "as")
|
|
176
|
+
this.fail("Expected AS in the CTE declaration.", cursor - 1);
|
|
177
|
+
if (this.dialect === "postgres") {
|
|
178
|
+
if (word(this.tokens[cursor]) === "not") {
|
|
179
|
+
cursor++;
|
|
180
|
+
if (word(this.tokens[cursor++]) !== "materialized")
|
|
181
|
+
this.fail("Expected MATERIALIZED after NOT.", cursor - 1);
|
|
182
|
+
}
|
|
183
|
+
else if (word(this.tokens[cursor]) === "materialized")
|
|
184
|
+
cursor++;
|
|
185
|
+
}
|
|
186
|
+
if (!punctuation(this.tokens[cursor], "("))
|
|
187
|
+
this.fail("Expected a parenthesized CTE query.", cursor);
|
|
188
|
+
const close = this.close(cursor);
|
|
189
|
+
if (close >= end)
|
|
190
|
+
this.fail("The CTE query exceeds its enclosing query.", cursor);
|
|
191
|
+
definitions.push({ cte, bodyStart: cursor + 1, bodyEnd: close });
|
|
192
|
+
cursor = close + 1;
|
|
193
|
+
if (!punctuation(this.tokens[cursor], ","))
|
|
194
|
+
break;
|
|
195
|
+
cursor++;
|
|
196
|
+
}
|
|
197
|
+
const scope = new Set(inherited);
|
|
198
|
+
if (recursive)
|
|
199
|
+
for (const name of names)
|
|
200
|
+
scope.add(name);
|
|
201
|
+
for (const definition of definitions) {
|
|
202
|
+
this.ctes.push(definition.cte);
|
|
203
|
+
this.query(definition.bodyStart, definition.bodyEnd, scope);
|
|
204
|
+
scope.add(this.binding(definition.cte.name));
|
|
205
|
+
}
|
|
206
|
+
return { start: cursor, scope };
|
|
207
|
+
}
|
|
208
|
+
query(start, end, inherited) {
|
|
209
|
+
while (punctuation(this.tokens[start], "(") &&
|
|
210
|
+
this.close(start) === end - 1) {
|
|
211
|
+
start++;
|
|
212
|
+
end--;
|
|
213
|
+
}
|
|
214
|
+
let scope = inherited;
|
|
215
|
+
if (word(this.tokens[start]) === "with")
|
|
216
|
+
({ start, scope } = this.withClause(start, end, inherited));
|
|
217
|
+
let branch = start;
|
|
218
|
+
for (let cursor = start; cursor < end; cursor++) {
|
|
219
|
+
if (punctuation(this.tokens[cursor], "(") ||
|
|
220
|
+
punctuation(this.tokens[cursor], "[")) {
|
|
221
|
+
cursor = this.close(cursor);
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
const keyword = word(this.tokens[cursor]);
|
|
225
|
+
if (keyword !== "union" &&
|
|
226
|
+
keyword !== "intersect" &&
|
|
227
|
+
keyword !== "except")
|
|
228
|
+
continue;
|
|
229
|
+
// BigQuery's SELECT * EXCEPT(columns) is not a set operation.
|
|
230
|
+
if (this.dialect === "bigquery" &&
|
|
231
|
+
keyword === "except" &&
|
|
232
|
+
punctuation(this.tokens[cursor + 1], "(") &&
|
|
233
|
+
!this.queryStart(cursor + 2, this.close(cursor + 1)))
|
|
234
|
+
continue;
|
|
235
|
+
this.select(branch, cursor, scope);
|
|
236
|
+
cursor++;
|
|
237
|
+
if (word(this.tokens[cursor]) === "all" ||
|
|
238
|
+
word(this.tokens[cursor]) === "distinct")
|
|
239
|
+
cursor++;
|
|
240
|
+
branch = cursor;
|
|
241
|
+
cursor--;
|
|
242
|
+
}
|
|
243
|
+
this.select(branch, end, scope);
|
|
244
|
+
}
|
|
245
|
+
expression(start, end, scope) {
|
|
246
|
+
for (let cursor = start; cursor < end; cursor++) {
|
|
247
|
+
if (!punctuation(this.tokens[cursor], "(") &&
|
|
248
|
+
!punctuation(this.tokens[cursor], "["))
|
|
249
|
+
continue;
|
|
250
|
+
const close = this.close(cursor);
|
|
251
|
+
if (punctuation(this.tokens[cursor], "(") &&
|
|
252
|
+
this.queryStart(cursor + 1, close)) {
|
|
253
|
+
this.query(cursor + 1, close, scope);
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
this.expression(cursor + 1, close, scope);
|
|
257
|
+
}
|
|
258
|
+
cursor = close;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
select(start, end, scope) {
|
|
262
|
+
if (punctuation(this.tokens[start], "(")) {
|
|
263
|
+
const close = this.close(start);
|
|
264
|
+
if (!this.queryStart(start + 1, close))
|
|
265
|
+
this.fail("Only SELECT query expressions are supported.", start);
|
|
266
|
+
this.query(start + 1, close, scope);
|
|
267
|
+
if (close + 1 < end) {
|
|
268
|
+
if (!CLAUSES.has(word(this.tokens[close + 1]) ?? ""))
|
|
269
|
+
this.fail("Unsupported text after a query expression.", close + 1);
|
|
270
|
+
this.expression(close + 1, end, scope);
|
|
271
|
+
}
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
if (word(this.tokens[start]) !== "select")
|
|
275
|
+
this.fail("Only SELECT query expressions are supported.", start);
|
|
276
|
+
let sawFrom = false;
|
|
277
|
+
let afterFrom = false;
|
|
278
|
+
for (let cursor = start + 1; cursor < end; cursor++) {
|
|
279
|
+
if (punctuation(this.tokens[cursor], "(") ||
|
|
280
|
+
punctuation(this.tokens[cursor], "[")) {
|
|
281
|
+
const close = this.close(cursor);
|
|
282
|
+
this.expression(cursor, close + 1, scope);
|
|
283
|
+
cursor = close;
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
const keyword = word(this.tokens[cursor]);
|
|
287
|
+
if (keyword === "from") {
|
|
288
|
+
if (sawFrom || afterFrom)
|
|
289
|
+
this.fail("Unexpected FROM at this query depth.", cursor);
|
|
290
|
+
sawFrom = true;
|
|
291
|
+
cursor = this.from(cursor + 1, end, scope) - 1;
|
|
292
|
+
}
|
|
293
|
+
else if (keyword && CLAUSES.has(keyword)) {
|
|
294
|
+
afterFrom = true;
|
|
295
|
+
}
|
|
296
|
+
else if (keyword === "select" ||
|
|
297
|
+
keyword === "with" ||
|
|
298
|
+
keyword === "join") {
|
|
299
|
+
this.fail("Unexpected query structure at this query depth.", cursor);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
alias(cursor, end) {
|
|
304
|
+
let explicit = false;
|
|
305
|
+
if (word(this.tokens[cursor]) === "as") {
|
|
306
|
+
explicit = true;
|
|
307
|
+
cursor++;
|
|
308
|
+
}
|
|
309
|
+
const token = this.tokens[cursor];
|
|
310
|
+
if (cursor < end &&
|
|
311
|
+
isName(token) &&
|
|
312
|
+
(token.kind === "quoted-identifier" ||
|
|
313
|
+
!RESERVED_SOURCE_WORDS.has(token.value))) {
|
|
314
|
+
const alias = {
|
|
315
|
+
name: this.name(token),
|
|
316
|
+
quoted: token.kind === "quoted-identifier",
|
|
317
|
+
};
|
|
318
|
+
cursor++;
|
|
319
|
+
if (punctuation(this.tokens[cursor], "("))
|
|
320
|
+
cursor = this.columns(cursor);
|
|
321
|
+
return { cursor, alias };
|
|
322
|
+
}
|
|
323
|
+
if (explicit)
|
|
324
|
+
this.fail("Expected a source alias after AS.", cursor);
|
|
325
|
+
return { cursor };
|
|
326
|
+
}
|
|
327
|
+
source(start, end, scope, commaSeparated) {
|
|
328
|
+
let cursor = start;
|
|
329
|
+
const modifiers = [];
|
|
330
|
+
if (word(this.tokens[cursor]) === "lateral") {
|
|
331
|
+
modifiers.push("lateral");
|
|
332
|
+
cursor++;
|
|
333
|
+
}
|
|
334
|
+
const only = this.dialect === "postgres" && word(this.tokens[cursor]) === "only";
|
|
335
|
+
if (only) {
|
|
336
|
+
modifiers.push("only");
|
|
337
|
+
cursor++;
|
|
338
|
+
}
|
|
339
|
+
if (!only && punctuation(this.tokens[cursor], "(")) {
|
|
340
|
+
const close = this.close(cursor);
|
|
341
|
+
if (!this.queryStart(cursor + 1, close))
|
|
342
|
+
this.fail("Grouped tables and joins are not supported.", cursor);
|
|
343
|
+
this.query(cursor + 1, close, scope);
|
|
344
|
+
return this.alias(close + 1, end).cursor;
|
|
345
|
+
}
|
|
346
|
+
const wrapped = only && punctuation(this.tokens[cursor], "(");
|
|
347
|
+
const wrapper = cursor;
|
|
348
|
+
if (wrapped)
|
|
349
|
+
cursor++;
|
|
350
|
+
const first = this.identifier(cursor++);
|
|
351
|
+
let last = first;
|
|
352
|
+
const parts = this.dialect === "bigquery"
|
|
353
|
+
? this.name(first).split(".")
|
|
354
|
+
: [this.name(first)];
|
|
355
|
+
while (punctuation(this.tokens[cursor], ".")) {
|
|
356
|
+
last = this.identifier(cursor + 1);
|
|
357
|
+
parts.push(...(this.dialect === "bigquery"
|
|
358
|
+
? this.name(last).split(".")
|
|
359
|
+
: [this.name(last)]));
|
|
360
|
+
cursor += 2;
|
|
361
|
+
}
|
|
362
|
+
if (parts.some((part) => part.length === 0))
|
|
363
|
+
this.fail("Empty source path components are not supported.", start);
|
|
364
|
+
if (this.dialect === "bigquery" &&
|
|
365
|
+
parts.some((part) => /[*@$]/.test(part))) {
|
|
366
|
+
this.fail("Wildcard and decorated BigQuery tables are not supported.", start);
|
|
367
|
+
}
|
|
368
|
+
if (punctuation(this.tokens[cursor], "("))
|
|
369
|
+
this.fail("Table functions are not supported.", cursor);
|
|
370
|
+
if (wrapped) {
|
|
371
|
+
if (cursor !== this.close(wrapper))
|
|
372
|
+
this.fail("ONLY must contain exactly one relation name.", cursor);
|
|
373
|
+
cursor++;
|
|
374
|
+
}
|
|
375
|
+
const result = this.alias(cursor, end);
|
|
376
|
+
const name = parts[parts.length - 1];
|
|
377
|
+
this.sources.push({
|
|
378
|
+
name,
|
|
379
|
+
qualifiers: parts.slice(0, -1),
|
|
380
|
+
quoted: last.kind === "quoted-identifier",
|
|
381
|
+
start: first.start,
|
|
382
|
+
end: last.end,
|
|
383
|
+
cte: parts.length === 1 && scope.has(this.binding(name)),
|
|
384
|
+
commaSeparated,
|
|
385
|
+
modifiers,
|
|
386
|
+
...(result.alias ? { alias: result.alias } : {}),
|
|
387
|
+
});
|
|
388
|
+
return result.cursor;
|
|
389
|
+
}
|
|
390
|
+
join(cursor) {
|
|
391
|
+
if (word(this.tokens[cursor]) === "natural")
|
|
392
|
+
cursor++;
|
|
393
|
+
const kind = word(this.tokens[cursor]);
|
|
394
|
+
if (kind === "inner" ||
|
|
395
|
+
kind === "left" ||
|
|
396
|
+
kind === "right" ||
|
|
397
|
+
kind === "full" ||
|
|
398
|
+
kind === "cross") {
|
|
399
|
+
cursor++;
|
|
400
|
+
if (word(this.tokens[cursor]) === "outer" &&
|
|
401
|
+
kind !== "inner" &&
|
|
402
|
+
kind !== "cross")
|
|
403
|
+
cursor++;
|
|
404
|
+
}
|
|
405
|
+
if (word(this.tokens[cursor]) !== "join")
|
|
406
|
+
this.fail("Unsupported JOIN structure.", cursor);
|
|
407
|
+
return cursor + 1;
|
|
408
|
+
}
|
|
409
|
+
from(start, end, scope) {
|
|
410
|
+
let cursor = this.source(start, end, scope, false);
|
|
411
|
+
while (cursor < end) {
|
|
412
|
+
const keyword = word(this.tokens[cursor]);
|
|
413
|
+
if (keyword && CLAUSES.has(keyword))
|
|
414
|
+
return cursor;
|
|
415
|
+
if (punctuation(this.tokens[cursor], ",")) {
|
|
416
|
+
cursor = this.source(cursor + 1, end, scope, true);
|
|
417
|
+
}
|
|
418
|
+
else if (keyword && JOIN_WORDS.has(keyword)) {
|
|
419
|
+
cursor = this.source(this.join(cursor), end, scope, false);
|
|
420
|
+
}
|
|
421
|
+
else if (keyword === "using") {
|
|
422
|
+
if (!punctuation(this.tokens[cursor + 1], "("))
|
|
423
|
+
this.fail("Expected JOIN USING columns.", cursor + 1);
|
|
424
|
+
cursor = this.columns(cursor + 1);
|
|
425
|
+
}
|
|
426
|
+
else if (keyword === "on") {
|
|
427
|
+
const expressionStart = ++cursor;
|
|
428
|
+
while (cursor < end) {
|
|
429
|
+
if (punctuation(this.tokens[cursor], "(") ||
|
|
430
|
+
punctuation(this.tokens[cursor], "[")) {
|
|
431
|
+
cursor = this.close(cursor) + 1;
|
|
432
|
+
continue;
|
|
433
|
+
}
|
|
434
|
+
const next = word(this.tokens[cursor]);
|
|
435
|
+
if (punctuation(this.tokens[cursor], ",") ||
|
|
436
|
+
(next && (CLAUSES.has(next) || JOIN_WORDS.has(next))))
|
|
437
|
+
break;
|
|
438
|
+
cursor++;
|
|
439
|
+
}
|
|
440
|
+
if (cursor === expressionStart)
|
|
441
|
+
this.fail("Expected a JOIN condition.", cursor);
|
|
442
|
+
this.expression(expressionStart, cursor, scope);
|
|
443
|
+
}
|
|
444
|
+
else {
|
|
445
|
+
this.fail("The SQL source boundary could not be established safely.", cursor);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
return cursor;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
/** Reads sources in a bounded SELECT grammar; unsupported source syntax throws. */
|
|
452
|
+
export function readAgentSqlQuery(sql, options) {
|
|
453
|
+
const tokens = lexAgentSql(sql, options);
|
|
454
|
+
let end = tokens.length;
|
|
455
|
+
if (punctuation(tokens[end - 1], ";"))
|
|
456
|
+
end--;
|
|
457
|
+
if (end === 0)
|
|
458
|
+
throw new AgentSqlSyntaxError("unsupported_syntax", "The SQL query is empty.", 0);
|
|
459
|
+
const semicolon = tokens
|
|
460
|
+
.slice(0, end)
|
|
461
|
+
.find((token) => punctuation(token, ";"));
|
|
462
|
+
if (semicolon)
|
|
463
|
+
throw new AgentSqlSyntaxError("unsupported_syntax", "Expected exactly one SQL query.", semicolon.start);
|
|
464
|
+
const reader = new QueryReader(sql, tokens, options.dialect);
|
|
465
|
+
reader.query(0, end, new Set());
|
|
466
|
+
return {
|
|
467
|
+
sql,
|
|
468
|
+
dialect: options.dialect,
|
|
469
|
+
tokens,
|
|
470
|
+
sources: reader.sources.sort((left, right) => left.start - right.start),
|
|
471
|
+
ctes: reader.ctes.sort((left, right) => left.start - right.start),
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
/** Replaces only analyzed source-name spans, preserving all other source text. */
|
|
475
|
+
export function rewriteAgentSqlQuerySources(query, replacer) {
|
|
476
|
+
const sources = [...query.sources].sort((left, right) => left.start - right.start);
|
|
477
|
+
const tokenStarts = new Map(query.tokens.map((token, index) => [token.start, index]));
|
|
478
|
+
const tokenEnds = new Map(query.tokens.map((token, index) => [token.end, index]));
|
|
479
|
+
let cursor = 0;
|
|
480
|
+
let sql = "";
|
|
481
|
+
for (const source of sources) {
|
|
482
|
+
if (!Number.isInteger(source.start) ||
|
|
483
|
+
!Number.isInteger(source.end) ||
|
|
484
|
+
source.start < cursor ||
|
|
485
|
+
source.end <= source.start ||
|
|
486
|
+
source.end > query.sql.length) {
|
|
487
|
+
throw new AgentSqlSyntaxError("unsupported_syntax", "SQL source spans overlap or exceed the query.", source.start);
|
|
488
|
+
}
|
|
489
|
+
const first = tokenStarts.get(source.start);
|
|
490
|
+
const last = tokenEnds.get(source.end);
|
|
491
|
+
if (first === undefined ||
|
|
492
|
+
last === undefined ||
|
|
493
|
+
last < first ||
|
|
494
|
+
(last - first) % 2 !== 0) {
|
|
495
|
+
throw new AgentSqlSyntaxError("unsupported_syntax", "SQL source spans must follow identifier token boundaries.", source.start);
|
|
496
|
+
}
|
|
497
|
+
for (let index = first; index <= last; index++) {
|
|
498
|
+
const token = query.tokens[index];
|
|
499
|
+
if (((index - first) % 2 === 0
|
|
500
|
+
? !isName(token)
|
|
501
|
+
: !punctuation(token, ".")) ||
|
|
502
|
+
query.sql.slice(token.start, token.end) !== token.text) {
|
|
503
|
+
throw new AgentSqlSyntaxError("unsupported_syntax", "SQL source spans must contain exactly one identifier path.", source.start);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
const replacement = replacer(source);
|
|
507
|
+
if (typeof replacement !== "string" || replacement.trim().length === 0) {
|
|
508
|
+
throw new AgentSqlSyntaxError("unsupported_syntax", "A SQL source replacement must be nonempty text.", source.start);
|
|
509
|
+
}
|
|
510
|
+
sql += query.sql.slice(cursor, source.start) + replacement;
|
|
511
|
+
cursor = source.end;
|
|
512
|
+
}
|
|
513
|
+
return sql + query.sql.slice(cursor);
|
|
514
|
+
}
|
package/dist/catalog.json
CHANGED
|
@@ -10,30 +10,30 @@
|
|
|
10
10
|
"@radix-ui/react-popover": "^1.1.19",
|
|
11
11
|
"@radix-ui/react-select": "^2.3.3",
|
|
12
12
|
"@radix-ui/react-tooltip": "^1.2.12",
|
|
13
|
-
"@tiptap/core": "3.
|
|
14
|
-
"@tiptap/extension-blockquote": "3.
|
|
15
|
-
"@tiptap/extension-bold": "3.
|
|
16
|
-
"@tiptap/extension-bubble-menu": "3.
|
|
17
|
-
"@tiptap/extension-code-block-lowlight": "3.
|
|
18
|
-
"@tiptap/extension-collaboration": "3.
|
|
19
|
-
"@tiptap/extension-collaboration-caret": "3.
|
|
20
|
-
"@tiptap/extension-color": "3.
|
|
21
|
-
"@tiptap/extension-floating-menu": "3.
|
|
22
|
-
"@tiptap/extension-horizontal-rule": "3.
|
|
23
|
-
"@tiptap/extension-image": "3.
|
|
24
|
-
"@tiptap/extension-link": "3.
|
|
25
|
-
"@tiptap/extension-list": "3.
|
|
26
|
-
"@tiptap/extension-placeholder": "3.
|
|
27
|
-
"@tiptap/extension-table": "3.
|
|
28
|
-
"@tiptap/extension-table-cell": "3.
|
|
29
|
-
"@tiptap/extension-table-header": "3.
|
|
30
|
-
"@tiptap/extension-table-row": "3.
|
|
31
|
-
"@tiptap/extension-task-item": "3.
|
|
32
|
-
"@tiptap/extension-task-list": "3.
|
|
33
|
-
"@tiptap/extension-text-style": "3.
|
|
34
|
-
"@tiptap/pm": "3.
|
|
35
|
-
"@tiptap/react": "3.
|
|
36
|
-
"@tiptap/starter-kit": "3.
|
|
13
|
+
"@tiptap/core": "3.31.4",
|
|
14
|
+
"@tiptap/extension-blockquote": "3.31.4",
|
|
15
|
+
"@tiptap/extension-bold": "3.31.4",
|
|
16
|
+
"@tiptap/extension-bubble-menu": "3.31.4",
|
|
17
|
+
"@tiptap/extension-code-block-lowlight": "3.31.4",
|
|
18
|
+
"@tiptap/extension-collaboration": "3.31.4",
|
|
19
|
+
"@tiptap/extension-collaboration-caret": "3.31.4",
|
|
20
|
+
"@tiptap/extension-color": "3.31.4",
|
|
21
|
+
"@tiptap/extension-floating-menu": "3.31.4",
|
|
22
|
+
"@tiptap/extension-horizontal-rule": "3.31.4",
|
|
23
|
+
"@tiptap/extension-image": "3.31.4",
|
|
24
|
+
"@tiptap/extension-link": "3.31.4",
|
|
25
|
+
"@tiptap/extension-list": "3.31.4",
|
|
26
|
+
"@tiptap/extension-placeholder": "3.31.4",
|
|
27
|
+
"@tiptap/extension-table": "3.31.4",
|
|
28
|
+
"@tiptap/extension-table-cell": "3.31.4",
|
|
29
|
+
"@tiptap/extension-table-header": "3.31.4",
|
|
30
|
+
"@tiptap/extension-table-row": "3.31.4",
|
|
31
|
+
"@tiptap/extension-task-item": "3.31.4",
|
|
32
|
+
"@tiptap/extension-task-list": "3.31.4",
|
|
33
|
+
"@tiptap/extension-text-style": "3.31.4",
|
|
34
|
+
"@tiptap/pm": "3.31.4",
|
|
35
|
+
"@tiptap/react": "3.31.4",
|
|
36
|
+
"@tiptap/starter-kit": "3.31.4",
|
|
37
37
|
"@tiptap/y-tiptap": "3.0.7",
|
|
38
38
|
"@tabler/icons-react": "3.44.0",
|
|
39
39
|
"@tailwindcss/vite": "^4.2.4",
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { AGENT_NATIVE_EXCEPTION_EVENT_NAME, addErrorBreadcrumb, captureClientException, captureError, captureException, captureMessage, clearAnalyticsSessionId, configureTracking, getAnalyticsAnonymousId, getAnalyticsSessionId, getFirstTouchAttribution, getSessionReplayContext, getSessionReplayUrl, isErrorCaptureInstalled, maybeStartSessionReplay, setAnalyticsSessionId, setSentryUser, setTrackingContentCaptureEnabled, setTrackingIdentity, startSessionReplay, stopSessionReplay, trackAgentChatLifecycle, trackAnonymousEvent, trackEvent, trackLifecycleEvent, trackSessionStatus, type AgentChatLifecycleEvent, type CaptureExceptionContext, type CapturedExceptionEvent, type ClientCaptureContext, type ConfigureTrackingOptions, type ErrorCaptureConfigOptions, type ExceptionBreadcrumb, type ExceptionLevel, type FirstTouchAttribution, type SessionReplayContext, type SessionReplayLinkOptions, type SessionReplayOptions, type SessionReplayStartResult, type SessionReplayUrlMatcher, type TrackingIdentityUser, } from "../analytics.js";
|
|
1
|
+
export { AGENT_NATIVE_EXCEPTION_EVENT_NAME, addErrorBreadcrumb, captureAttribution, captureClientException, captureError, captureException, captureMessage, clearAnalyticsSessionId, configureTracking, getAnalyticsAnonymousId, getAnalyticsSessionId, getFirstTouchAttribution, getSessionReplayContext, getSessionReplayUrl, isErrorCaptureInstalled, maybeStartSessionReplay, setAnalyticsSessionId, setSentryUser, setTrackingContentCaptureEnabled, setTrackingIdentity, startSessionReplay, stopSessionReplay, trackAgentChatLifecycle, trackAnonymousEvent, trackEvent, trackLifecycleEvent, trackSessionStatus, type AgentChatLifecycleEvent, type CaptureExceptionContext, type CapturedExceptionEvent, type ClientCaptureContext, type ConfigureTrackingOptions, type ErrorCaptureConfigOptions, type ExceptionBreadcrumb, type ExceptionLevel, type FirstTouchAttribution, type SessionReplayContext, type SessionReplayLinkOptions, type SessionReplayOptions, type SessionReplayStartResult, type SessionReplayUrlMatcher, type TrackingIdentityUser, } from "../analytics.js";
|
|
2
2
|
export { track } from "../track.js";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { AGENT_NATIVE_EXCEPTION_EVENT_NAME, addErrorBreadcrumb, captureClientException, captureError, captureException, captureMessage, clearAnalyticsSessionId, configureTracking, getAnalyticsAnonymousId, getAnalyticsSessionId, getFirstTouchAttribution, getSessionReplayContext, getSessionReplayUrl, isErrorCaptureInstalled, maybeStartSessionReplay, setAnalyticsSessionId, setSentryUser, setTrackingContentCaptureEnabled, setTrackingIdentity, startSessionReplay, stopSessionReplay, trackAgentChatLifecycle, trackAnonymousEvent, trackEvent, trackLifecycleEvent, trackSessionStatus, } from "../analytics.js";
|
|
1
|
+
export { AGENT_NATIVE_EXCEPTION_EVENT_NAME, addErrorBreadcrumb, captureAttribution, captureClientException, captureError, captureException, captureMessage, clearAnalyticsSessionId, configureTracking, getAnalyticsAnonymousId, getAnalyticsSessionId, getFirstTouchAttribution, getSessionReplayContext, getSessionReplayUrl, isErrorCaptureInstalled, maybeStartSessionReplay, setAnalyticsSessionId, setSentryUser, setTrackingContentCaptureEnabled, setTrackingIdentity, startSessionReplay, stopSessionReplay, trackAgentChatLifecycle, trackAnonymousEvent, trackEvent, trackLifecycleEvent, trackSessionStatus, } from "../analytics.js";
|
|
2
2
|
export { track } from "../track.js";
|
|
@@ -81,6 +81,8 @@ export interface FirstTouchAttribution {
|
|
|
81
81
|
vector_source?: string;
|
|
82
82
|
landing_path?: string;
|
|
83
83
|
landing_referrer?: string;
|
|
84
|
+
site_referrer?: string;
|
|
85
|
+
site_landing_path?: string;
|
|
84
86
|
landed_at?: string;
|
|
85
87
|
capture_truncated?: string;
|
|
86
88
|
}
|
|
@@ -88,6 +90,12 @@ export declare function getAnalyticsIdentityKey(): string | undefined;
|
|
|
88
90
|
export declare function getAnalyticsAnonymousId(): string | undefined;
|
|
89
91
|
export declare function getAnalyticsSessionId(): string | undefined;
|
|
90
92
|
export declare function getFirstTouchAttribution(): FirstTouchAttribution | null;
|
|
93
|
+
/**
|
|
94
|
+
* Store the visitor's first touch now instead of in `configureTracking()`,
|
|
95
|
+
* for a page that reads it before tracking starts. It runs once per page
|
|
96
|
+
* load, so tracking's own capture is then a no-op.
|
|
97
|
+
*/
|
|
98
|
+
export declare function captureAttribution(): void;
|
|
91
99
|
export declare function setSentryUser(user: TrackingIdentityUser | null, orgId?: string | null): void;
|
|
92
100
|
export declare function setTrackingIdentity(user: TrackingIdentityUser | null, orgId?: string | null): void;
|
|
93
101
|
export interface ClientCaptureContext {
|
package/dist/client/analytics.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { recordTrackingEvent } from "../observability/tracing.js";
|
|
2
2
|
import { AGENT_NATIVE_LIFECYCLE_EVENTS, canonicalTrackingEvent, legacyLifecycleEvent, normalizeTrackingDimension, withCanonicalTrackingProperties, } from "../shared/analytics-events.js";
|
|
3
3
|
import { ANALYTICS_CLIENT_PLATFORM_PROPERTY, } from "../shared/analytics-platform.js";
|
|
4
|
+
import { resolveLaneEndpoint } from "../shared/environment-lanes.js";
|
|
4
5
|
import { classifyErrorNoise, } from "../shared/error-noise.js";
|
|
5
6
|
import { llmConnectionTrackingProperties, } from "../shared/llm-connection.js";
|
|
6
7
|
import { loadOptionalPeer } from "../shared/optional-peer.js";
|
|
@@ -80,6 +81,8 @@ const FIRST_TOUCH_QUERY_FIELDS = [
|
|
|
80
81
|
"gclid",
|
|
81
82
|
"msclkid",
|
|
82
83
|
"vector_source",
|
|
84
|
+
"site_referrer",
|
|
85
|
+
"site_landing_path",
|
|
83
86
|
];
|
|
84
87
|
const FIRST_TOUCH_COOKIE_FIELD_PRIORITY = [
|
|
85
88
|
"gclid",
|
|
@@ -94,6 +97,8 @@ const FIRST_TOUCH_COOKIE_FIELD_PRIORITY = [
|
|
|
94
97
|
"utm_term",
|
|
95
98
|
"landing_path",
|
|
96
99
|
"landing_referrer",
|
|
100
|
+
"site_referrer",
|
|
101
|
+
"site_landing_path",
|
|
97
102
|
"landed_at",
|
|
98
103
|
];
|
|
99
104
|
let _firstTouchCaptured = false;
|
|
@@ -513,6 +518,16 @@ export function getFirstTouchAttribution() {
|
|
|
513
518
|
return null;
|
|
514
519
|
}
|
|
515
520
|
}
|
|
521
|
+
/**
|
|
522
|
+
* Store the visitor's first touch now instead of in `configureTracking()`,
|
|
523
|
+
* for a page that reads it before tracking starts. It runs once per page
|
|
524
|
+
* load, so tracking's own capture is then a no-op.
|
|
525
|
+
*/
|
|
526
|
+
export function captureAttribution() {
|
|
527
|
+
if (isSyntheticBrowserTraffic())
|
|
528
|
+
return;
|
|
529
|
+
captureFirstTouchAttribution();
|
|
530
|
+
}
|
|
516
531
|
function isLocalAnalyticsHostname(hostname) {
|
|
517
532
|
const h = (hostname || "").toLowerCase();
|
|
518
533
|
return (h === "localhost" ||
|
|
@@ -1696,11 +1711,11 @@ function sendAgentNativeAnalytics(name, properties) {
|
|
|
1696
1711
|
?.VITE_AGENT_NATIVE_ANALYTICS_PUBLIC_KEY;
|
|
1697
1712
|
if (!publicKey)
|
|
1698
1713
|
return;
|
|
1699
|
-
const endpoint = _agentNativeAnalyticsEndpoint ||
|
|
1714
|
+
const endpoint = resolveLaneEndpoint(_agentNativeAnalyticsEndpoint ||
|
|
1700
1715
|
window.__AGENT_NATIVE_CONFIG__?.agentNativeAnalyticsEndpoint ||
|
|
1701
1716
|
import.meta.env
|
|
1702
1717
|
?.VITE_AGENT_NATIVE_ANALYTICS_ENDPOINT ||
|
|
1703
|
-
AGENT_NATIVE_ANALYTICS_DEFAULT_ENDPOINT;
|
|
1718
|
+
AGENT_NATIVE_ANALYTICS_DEFAULT_ENDPOINT, window.location.hostname);
|
|
1704
1719
|
const userId = typeof properties.userId === "string" ? properties.userId : undefined;
|
|
1705
1720
|
const body = JSON.stringify({
|
|
1706
1721
|
publicKey,
|