@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.
Files changed (118) hide show
  1. package/README.md +22 -22
  2. package/dist/a2a/server.d.ts +2 -2
  3. package/dist/a2a/server.js +13 -2
  4. package/dist/agent/thread-data-builder.d.ts +4 -0
  5. package/dist/agent/thread-data-builder.js +8 -0
  6. package/dist/agent-sql/analysis.d.ts +31 -0
  7. package/dist/agent-sql/analysis.js +169 -0
  8. package/dist/agent-sql/index.d.ts +4 -0
  9. package/dist/agent-sql/index.js +4 -0
  10. package/dist/agent-sql/lexer.d.ts +37 -0
  11. package/dist/agent-sql/lexer.js +357 -0
  12. package/dist/agent-sql/postgres.d.ts +51 -0
  13. package/dist/agent-sql/postgres.js +197 -0
  14. package/dist/agent-sql/query.d.ts +30 -0
  15. package/dist/agent-sql/query.js +514 -0
  16. package/dist/catalog.json +24 -24
  17. package/dist/client/analytics/index.d.ts +1 -1
  18. package/dist/client/analytics/index.js +1 -1
  19. package/dist/client/analytics.d.ts +8 -0
  20. package/dist/client/analytics.js +17 -2
  21. package/dist/client/session-replay.js +3 -2
  22. package/dist/identity/offboard.js +31 -3
  23. package/dist/identity/rekey.d.ts +12 -1
  24. package/dist/identity/rekey.js +77 -6
  25. package/dist/identity/retired-emails.d.ts +23 -0
  26. package/dist/identity/retired-emails.js +55 -0
  27. package/dist/mcp/actions/create-org-service-token.js +20 -8
  28. package/dist/mcp/actions/service-token-access.d.ts +1 -0
  29. package/dist/mcp/actions/service-token-access.js +41 -24
  30. package/dist/mcp/build-server.d.ts +20 -6
  31. package/dist/mcp/build-server.js +131 -35
  32. package/dist/mcp/connect-route.d.ts +4 -3
  33. package/dist/mcp/connect-route.js +140 -82
  34. package/dist/mcp/connect-store.d.ts +17 -11
  35. package/dist/mcp/connect-store.js +88 -60
  36. package/dist/mcp/credential-issuance.d.ts +23 -0
  37. package/dist/mcp/credential-issuance.js +77 -0
  38. package/dist/mcp/credential-membership.d.ts +26 -0
  39. package/dist/mcp/credential-membership.js +67 -0
  40. package/dist/mcp/oauth-route.js +133 -45
  41. package/dist/mcp/oauth-store.d.ts +15 -5
  42. package/dist/mcp/oauth-store.js +119 -34
  43. package/dist/mcp/oauth-token.d.ts +4 -0
  44. package/dist/mcp/oauth-token.js +5 -0
  45. package/dist/mcp/screen-memory-stdio.d.ts +7 -7
  46. package/dist/mcp/server.js +14 -0
  47. package/dist/onboarding/default-steps.js +1 -1
  48. package/dist/org/handlers.d.ts +1 -1
  49. package/dist/org/index.d.ts +2 -2
  50. package/dist/org/membership.d.ts +3 -1
  51. package/dist/org/membership.js +5 -2
  52. package/dist/provider-api/actions/custom-provider-registration.d.ts +12 -12
  53. package/dist/provider-api/actions/provider-api.d.ts +10 -10
  54. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  55. package/dist/resource-changes/store.d.ts +2 -0
  56. package/dist/resource-changes/store.js +4 -0
  57. package/dist/resources/handlers.d.ts +1 -1
  58. package/dist/scripts/db/exec.js +28 -101
  59. package/dist/scripts/db/patch.js +14 -34
  60. package/dist/scripts/db/postgres-client.d.ts +8 -1
  61. package/dist/scripts/db/postgres-client.js +20 -6
  62. package/dist/scripts/db/query.js +31 -33
  63. package/dist/scripts/db/safety.d.ts +47 -2
  64. package/dist/scripts/db/safety.js +383 -137
  65. package/dist/scripts/db/scoping.d.ts +1 -0
  66. package/dist/scripts/db/scoping.js +3 -2
  67. package/dist/search/index-store.d.ts +21 -0
  68. package/dist/search/index-store.js +47 -0
  69. package/dist/search/indexer.js +35 -32
  70. package/dist/search/registry.d.ts +6 -3
  71. package/dist/search/registry.js +30 -8
  72. package/dist/secrets/routes.d.ts +6 -6
  73. package/dist/server/agent-chat-plugin.js +18 -6
  74. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  75. package/dist/server/agent-engine-default-model-route.d.ts +1 -1
  76. package/dist/server/agent-run-context.js +9 -0
  77. package/dist/server/attribution.d.ts +2 -0
  78. package/dist/server/attribution.js +7 -1
  79. package/dist/server/auth.js +9 -0
  80. package/dist/server/credential-membership-unavailable.d.ts +13 -0
  81. package/dist/server/credential-membership-unavailable.js +22 -0
  82. package/dist/server/index.d.ts +1 -0
  83. package/dist/server/index.js +1 -0
  84. package/dist/server/onboarding-html.js +1 -1
  85. package/dist/server/realtime-token.d.ts +1 -1
  86. package/dist/server/recap-image-route.js +4 -0
  87. package/dist/server/release-schema.js +4 -0
  88. package/dist/shared/docs-url.d.ts +3 -1
  89. package/dist/shared/docs-url.js +6 -3
  90. package/dist/shared/environment-lanes.d.ts +8 -0
  91. package/dist/shared/environment-lanes.js +26 -0
  92. package/dist/templates/chat/.agents/skills/security/SKILL.md +9 -2
  93. package/dist/templates/chat/.agents/skills/storing-data/SKILL.md +12 -7
  94. package/dist/templates/chat/README.md +2 -2
  95. package/dist/templates/default/.agents/skills/security/SKILL.md +9 -2
  96. package/dist/templates/default/.agents/skills/storing-data/SKILL.md +12 -7
  97. package/dist/templates/default/app/routes/home.tsx +5 -1
  98. package/dist/templates/headless/.agents/skills/security/SKILL.md +9 -2
  99. package/dist/templates/headless/.agents/skills/storing-data/SKILL.md +12 -7
  100. package/dist/templates/workspace-core/.agents/skills/security/SKILL.md +9 -2
  101. package/dist/templates/workspace-core/.agents/skills/storing-data/SKILL.md +12 -7
  102. package/dist/tracking/providers.js +13 -1
  103. package/docs/content/authentication.mdx +5 -1
  104. package/docs/content/durable-background-runs.mdx +17 -8
  105. package/docs/content/external-agents.mdx +3 -1
  106. package/docs/content/mcp-protocol.mdx +6 -2
  107. package/docs/content/security.mdx +1 -1
  108. package/package.json +5 -4
  109. package/src/templates/chat/.agents/skills/security/SKILL.md +9 -2
  110. package/src/templates/chat/.agents/skills/storing-data/SKILL.md +12 -7
  111. package/src/templates/chat/README.md +2 -2
  112. package/src/templates/default/.agents/skills/security/SKILL.md +9 -2
  113. package/src/templates/default/.agents/skills/storing-data/SKILL.md +12 -7
  114. package/src/templates/default/app/routes/home.tsx +5 -1
  115. package/src/templates/headless/.agents/skills/security/SKILL.md +9 -2
  116. package/src/templates/headless/.agents/skills/storing-data/SKILL.md +12 -7
  117. package/src/templates/workspace-core/.agents/skills/security/SKILL.md +9 -2
  118. package/src/templates/workspace-core/.agents/skills/storing-data/SKILL.md +12 -7
@@ -1,39 +1,12 @@
1
1
  import path from "node:path";
2
- import { getDatabaseUrl, toPostgresParams } from "../../db/client.js";
2
+ import { getDatabaseUrl } from "../../db/client.js";
3
3
  import { parseArgs, fail } from "../utils.js";
4
4
  import { createPostgresScriptClient } from "./postgres-client.js";
5
- import { assertNoRawDbAccessControlPatchTarget, assertNoSensitiveFrameworkTables, } from "./safety.js";
5
+ import { assertNoRawDbAccessControlPatchTarget, assertNoSensitiveFrameworkTables, finalRawDbSql, readRawDbPatchStatement, validateRawDbPatchWhere, verifyRawDbStatement, } from "./safety.js";
6
6
  import { buildScopingPostgres } from "./scoping.js";
7
7
  function isValidIdentifier(value) {
8
8
  return /^[A-Za-z_][A-Za-z0-9_]*$/.test(value);
9
9
  }
10
- function validateWhere(where) {
11
- if (where.includes(";"))
12
- fail("--where must not contain ';'");
13
- const stripped = where
14
- .replace(/'(?:''|[^'])*'/g, "''")
15
- .replace(/"(?:""|[^"])*"/g, '""')
16
- .toUpperCase();
17
- const blocked = [
18
- " INSERT ",
19
- " UPDATE ",
20
- " DELETE ",
21
- " DROP ",
22
- " ALTER ",
23
- " CREATE ",
24
- " TRUNCATE ",
25
- " GRANT ",
26
- " REVOKE ",
27
- "--",
28
- "/*",
29
- ];
30
- const padded = ` ${stripped} `;
31
- for (const keyword of blocked) {
32
- if (padded.includes(keyword)) {
33
- fail(`--where must not contain "${keyword.trim()}"`);
34
- }
35
- }
36
- }
37
10
  function parseEdits(parsed) {
38
11
  let edits;
39
12
  if (parsed.edits) {
@@ -352,10 +325,12 @@ Options:
352
325
  if (!isValidIdentifier(table) || !isValidIdentifier(column)) {
353
326
  fail("--table and --column must be plain identifiers");
354
327
  }
355
- validateWhere(where);
328
+ validateRawDbPatchWhere(where);
356
329
  assertNoSensitiveFrameworkTables(table, "patch");
357
330
  assertNoRawDbAccessControlPatchTarget(table, column);
358
- assertNoSensitiveFrameworkTables(where, "read");
331
+ // `--where` is spliced into both statements, so each is read as a whole.
332
+ const select = finalRawDbSql(readRawDbPatchStatement(`SELECT "${column}" AS __val FROM "${table}" WHERE ${where}`, "select").sql, "read");
333
+ const update = finalRawDbSql(readRawDbPatchStatement(`UPDATE "${table}" SET "${column}" = ? WHERE ${where}`, "update").sql, "patch");
359
334
  const jsonOps = parseJsonOps(parsed);
360
335
  const options = {
361
336
  table,
@@ -377,8 +352,10 @@ Options:
377
352
  for (const statement of scoping.setup)
378
353
  await tx.unsafe(statement);
379
354
  try {
380
- const selectSql = `SELECT "${column}" AS __val FROM "${table}" WHERE ${where}`;
381
- const selected = await tx.unsafe(selectSql);
355
+ await verifyRawDbStatement(tx, select.statement);
356
+ const selected = await tx.unsafe(select.sql, [], {
357
+ singleStatement: true,
358
+ });
382
359
  if (selected.length === 0) {
383
360
  fail(`No rows matched: ${table} WHERE ${where}. ` +
384
361
  "(Database scoping filters results to the current user; the row may exist but be owned by someone else.)");
@@ -392,7 +369,10 @@ Options:
392
369
  }
393
370
  const edited = applyEditsToValue(original, options);
394
371
  if (edited.applied > 0) {
395
- await tx.unsafe(toPostgresParams(`UPDATE "${table}" SET "${column}" = ? WHERE ${where}`), [edited.content]);
372
+ await verifyRawDbStatement(tx, update.statement);
373
+ await tx.unsafe(update.sql, [edited.content], {
374
+ singleStatement: true,
375
+ });
396
376
  }
397
377
  output = {
398
378
  table,
@@ -1,8 +1,15 @@
1
1
  export interface PostgresScriptRows extends Array<Record<string, unknown>> {
2
2
  count?: number;
3
3
  }
4
+ export interface PostgresScriptQueryOptions {
5
+ /**
6
+ * Have the server refuse text with more than one statement. Set it for
7
+ * agent-written SQL, so the database enforces what the guards checked.
8
+ */
9
+ singleStatement?: boolean;
10
+ }
4
11
  export interface PostgresScriptClient {
5
- unsafe(sql: string, args?: unknown[]): Promise<PostgresScriptRows>;
12
+ unsafe(sql: string, args?: unknown[], options?: PostgresScriptQueryOptions): Promise<PostgresScriptRows>;
6
13
  begin<T>(fn: (tx: PostgresScriptClient) => Promise<T>): Promise<T>;
7
14
  end(): Promise<void>;
8
15
  }
@@ -5,6 +5,8 @@ function rowsResult(rows, affectedRows) {
5
5
  result.count = affectedRows ?? 0;
6
6
  return result;
7
7
  }
8
+ // PGlite's query() always uses the extended protocol, which runs one
9
+ // statement, so singleStatement needs no handling here.
8
10
  function pgliteClient(client) {
9
11
  return {
10
12
  async unsafe(sql, args) {
@@ -30,16 +32,28 @@ export async function createPostgresScriptClient(url) {
30
32
  const { default: postgres } = await import("postgres");
31
33
  const client = postgres(url);
32
34
  return {
33
- unsafe(sql, args) {
34
- return args === undefined
35
- ? client.unsafe(sql)
36
- : client.unsafe(sql, args);
37
- },
35
+ ...postgresJsQueries(client),
38
36
  async begin(fn) {
39
- return client.begin((tx) => fn(tx));
37
+ return client.begin((tx) => fn({
38
+ ...postgresJsQueries(tx),
39
+ begin: () => {
40
+ throw new Error("Nested transactions are not supported.");
41
+ },
42
+ end: async () => { },
43
+ }));
40
44
  },
41
45
  end() {
42
46
  return client.end();
43
47
  },
44
48
  };
45
49
  }
50
+ function postgresJsQueries(sql) {
51
+ return {
52
+ unsafe(query, args, options) {
53
+ // postgres.js uses the simple protocol when there are no arguments, and
54
+ // that protocol runs every statement in the text. The extended protocol
55
+ // runs one.
56
+ return sql.unsafe(query, args ?? [], options?.singleStatement ? { simple: false } : undefined);
57
+ },
58
+ };
59
+ }
@@ -1,10 +1,10 @@
1
1
  import path from "node:path";
2
- import { assertHostedRuntimeDatabase, getLocalDatabaseUrl, toPostgresParams, } from "../../db/client.js";
2
+ import { assertHostedRuntimeDatabase, getLocalDatabaseUrl, } from "../../db/client.js";
3
3
  import { getRequestOrgId, getRequestUserEmail, } from "../../server/request-context.js";
4
4
  import { parseArgs, fail } from "../utils.js";
5
5
  import { tryForwardDbQueryToDevServer } from "./dev-query-proxy.js";
6
6
  import { createPostgresScriptClient } from "./postgres-client.js";
7
- import { assertNoSchemaQualifiedTables, assertNoSensitiveFrameworkTables, } from "./safety.js";
7
+ import { finalRawDbSql, readRawDbReadStatement, verifyRawDbStatement, } from "./safety.js";
8
8
  import { buildScopingPostgres } from "./scoping.js";
9
9
  function parseSqlArgs(raw) {
10
10
  if (!raw)
@@ -46,48 +46,46 @@ function printTable(rows, sql, format) {
46
46
  .join(" | "));
47
47
  }
48
48
  }
49
+ // Thrown to end db-query's transaction with a rollback. The per-user views
50
+ // are created inside it, and the read-only switch blocks dropping them.
51
+ const READ_ONLY_ROLLBACK = Symbol("db-query rollback");
49
52
  export async function runDbQuery(options) {
50
53
  const sqlArgs = options.sqlArgs ?? [];
51
- const stripped = options.sql
52
- .replace(/^\s*--[^\n]*\n/gm, "")
53
- .replace(/\/\*[\s\S]*?\*\//g, "")
54
- .trim();
55
- const upper = stripped.toUpperCase();
56
- if (!upper.startsWith("SELECT") &&
57
- !upper.startsWith("WITH") &&
58
- !upper.startsWith("EXPLAIN")) {
59
- fail("Only SELECT, WITH, and EXPLAIN queries are allowed. Use db-exec for writes.");
60
- }
61
- assertNoSensitiveFrameworkTables(stripped, "read");
62
- assertNoSchemaQualifiedTables(stripped, "read");
63
- let query = options.sql;
54
+ const statement = readRawDbReadStatement(options.sql);
55
+ let query = statement.sql;
64
56
  if (options.limit &&
65
- (upper.startsWith("SELECT") || upper.startsWith("WITH")) &&
66
- !/\bLIMIT\b/i.test(stripped)) {
67
- query = `${options.sql} LIMIT ${options.limit}`;
57
+ (statement.keyword === "select" || statement.keyword === "with") &&
58
+ !statement.tokens.some((token) => token.kind === "word" && token.value === "limit")) {
59
+ query = `${statement.sql}\nLIMIT ${options.limit}`;
68
60
  }
61
+ const executed = finalRawDbSql(query, "read");
69
62
  if (!options.databaseUrl)
70
63
  assertHostedRuntimeDatabase();
71
64
  const url = options.databaseUrl ?? getLocalDatabaseUrl("pglite:./data/pglite");
72
65
  const client = await createPostgresScriptClient(url);
73
66
  try {
74
67
  let rows = [];
75
- const finalSql = toPostgresParams(query);
76
- await client.begin(async (tx) => {
77
- const scoping = await buildScopingPostgres(tx);
78
- for (const statement of scoping.setup)
79
- await tx.unsafe(statement);
80
- try {
81
- const result = await tx.unsafe(finalSql, sqlArgs);
68
+ try {
69
+ await client.begin(async (tx) => {
70
+ const scoping = await buildScopingPostgres(tx);
71
+ for (const setup of scoping.setup)
72
+ await tx.unsafe(setup);
73
+ // From here on the transaction cannot write, so a data-modifying CTE,
74
+ // SELECT INTO, or EXPLAIN ANALYZE of a write fails instead of running.
75
+ await tx.unsafe("SET TRANSACTION READ ONLY");
76
+ await verifyRawDbStatement(tx, executed.statement);
77
+ const result = await tx.unsafe(executed.sql, sqlArgs, {
78
+ singleStatement: true,
79
+ });
82
80
  rows = Array.from(result);
83
- }
84
- finally {
85
- for (const statement of scoping.teardown) {
86
- await tx.unsafe(statement).catch(() => { });
87
- }
88
- }
89
- });
90
- return { rows, sql: finalSql };
81
+ throw READ_ONLY_ROLLBACK;
82
+ });
83
+ }
84
+ catch (error) {
85
+ if (error !== READ_ONLY_ROLLBACK)
86
+ throw error;
87
+ }
88
+ return { rows, sql: executed.sql };
91
89
  }
92
90
  finally {
93
91
  await client.end();
@@ -1,4 +1,49 @@
1
+ import { type AgentPostgresStatement, type AgentSqlQueryRunner } from "../../agent-sql/postgres.js";
1
2
  export declare function assertNoSensitiveFrameworkTables(sql: string, operation: "read" | "write" | "patch"): void;
2
- export declare function assertNoSchemaQualifiedTables(sql: string, operation: "read" | "write"): void;
3
- export declare function assertNoRawDbAccessControlWrite(sql: string): void;
3
+ /**
4
+ * Reads one db-query statement and applies every check that needs no
5
+ * database. Returns the statement as it will run.
6
+ */
7
+ export declare function readRawDbReadStatement(sql: string): AgentPostgresStatement;
8
+ /**
9
+ * Reads one db-exec statement and applies every check that needs no
10
+ * database, including the access-control write rules. `label` prefixes
11
+ * errors, e.g. "Statement 2".
12
+ */
13
+ export declare function readRawDbWriteStatement(sql: string, label?: string): AgentPostgresStatement;
14
+ /**
15
+ * Converts `?` placeholders and re-reads the result, so the guards have seen
16
+ * exactly the text that executes. Refuses if converting again would change
17
+ * the text, since the PGlite client converts a second time.
18
+ */
19
+ export declare function finalRawDbSql(sql: string, kind: "read" | "write" | "patch", label?: string): {
20
+ sql: string;
21
+ statement: AgentPostgresStatement;
22
+ };
23
+ /**
24
+ * The database-side checks: run inside the transaction, after the per-user
25
+ * views exist and immediately before the statement executes.
26
+ * `columnsByTable` comes from the scoping context; it lets an INSERT without a
27
+ * column list be refused when the table has access-control columns.
28
+ */
29
+ export declare function verifyRawDbStatement(db: AgentSqlQueryRunner, statement: AgentPostgresStatement, options?: {
30
+ columnsByTable?: Map<string, string[]>;
31
+ label?: string;
32
+ }): Promise<void>;
33
+ /**
34
+ * The table an INSERT writes and the columns it lists, or null when the
35
+ * statement is not an INSERT the guards can read. `columns` is null when the
36
+ * INSERT has no column list.
37
+ */
38
+ export declare function rawDbInsertTarget(statement: AgentPostgresStatement): {
39
+ table: string;
40
+ columns: string[] | null;
41
+ } | null;
4
42
  export declare function assertNoRawDbAccessControlPatchTarget(table: string, column: string): void;
43
+ /**
44
+ * Validates db-patch's `--where`, which is spliced into a SELECT and an
45
+ * UPDATE. Both full statements are read and verified again before they run.
46
+ */
47
+ export declare function validateRawDbPatchWhere(where: string): void;
48
+ /** Reads a statement db-patch built from its arguments. */
49
+ export declare function readRawDbPatchStatement(sql: string, keyword: "select" | "update"): AgentPostgresStatement;