@agent-native/core 0.210.0 → 0.210.1-nightly-20261020010058

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 (82) hide show
  1. package/dist/action.js +16 -1
  2. package/dist/agent/production-agent.d.ts +1 -0
  3. package/dist/agent/production-agent.js +2 -0
  4. package/dist/agent/reaped-run-thread.d.ts +1 -1
  5. package/dist/agent/reaped-run-thread.js +21 -6
  6. package/dist/agent/run-store.fencing.worker.js +58 -0
  7. package/dist/agent/run-store.js +7 -5
  8. package/dist/agent/run-thread-projector.d.ts +27 -0
  9. package/dist/agent/run-thread-projector.js +79 -0
  10. package/dist/agent/thread-data-builder.js +38 -13
  11. package/dist/automation/index.d.ts +1 -1
  12. package/dist/cli/create.d.ts +1 -1
  13. package/dist/cli/create.js +30 -27
  14. package/dist/cli/package-lifecycle.js +3 -2
  15. package/dist/cli/setup-agents.d.ts +5 -0
  16. package/dist/cli/setup-agents.js +91 -34
  17. package/dist/cli/skills.js +8 -14
  18. package/dist/cli/tar-command.d.ts +1 -0
  19. package/dist/cli/tar-command.js +9 -0
  20. package/dist/cli/template-baseline.js +6 -3
  21. package/dist/cli/template-sync.d.ts +2 -1
  22. package/dist/cli/template-sync.js +2 -1
  23. package/dist/client/agent-chat.d.ts +1 -0
  24. package/dist/client/agent-chat.js +1 -1
  25. package/dist/client/api-path.d.ts +1 -0
  26. package/dist/client/api-path.js +6 -0
  27. package/dist/client/chat/agentkit-agent-native.js +6 -2
  28. package/dist/client/chat/runtime.js +3 -0
  29. package/dist/client/use-agent-chat-context.js +6 -3
  30. package/dist/client/use-db-sync.js +3 -0
  31. package/dist/collab/awareness.d.ts +2 -2
  32. package/dist/collab/routes.d.ts +2 -2
  33. package/dist/deploy/immutable-assets.d.ts +1 -1
  34. package/dist/deploy/immutable-assets.js +1 -1
  35. package/dist/deploy/workspace-deploy.js +12 -5
  36. package/dist/file-upload/actions/upload-image.d.ts +3 -3
  37. package/dist/jobs/actions/manage-recurring-job.d.ts +1 -1
  38. package/dist/jobs/background-automation-runner.js +16 -7
  39. package/dist/mcp/build-server.js +5 -1
  40. package/dist/mcp/embed-app.js +84 -17
  41. package/dist/notifications/routes.d.ts +3 -3
  42. package/dist/observability/routes.d.ts +7 -7
  43. package/dist/org/actions/invite-organization-member.js +1 -0
  44. package/dist/org/actions/list-organization-members.js +1 -0
  45. package/dist/org/index.d.ts +1 -1
  46. package/dist/progress/routes.d.ts +1 -1
  47. package/dist/provider-api/actions/custom-provider-registration.d.ts +6 -6
  48. package/dist/provider-api/actions/provider-api.d.ts +8 -8
  49. package/dist/scripts/db/exec.js +18 -6
  50. package/dist/scripts/db/patch.js +20 -8
  51. package/dist/scripts/db/postgres-client.d.ts +2 -0
  52. package/dist/scripts/db/postgres-client.js +17 -1
  53. package/dist/scripts/db/query.js +7 -6
  54. package/dist/scripts/db/safety.js +2 -1
  55. package/dist/secrets/routes.d.ts +10 -10
  56. package/dist/server/agent-chat-plugin.js +17 -8
  57. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  58. package/dist/server/agent-engine-default-model-route.d.ts +1 -1
  59. package/dist/server/agent-teams.js +9 -3
  60. package/dist/server/framework-request-handler.js +19 -0
  61. package/dist/server/fusion-app.d.ts +7 -0
  62. package/dist/server/fusion-app.js +11 -2
  63. package/dist/templates/builder-code-starter/.agents/skills/actions/SKILL.md.patch +1 -1
  64. package/dist/templates/builder-code-starter/app/hooks/use-navigation-state.ts.patch +26 -45
  65. package/dist/templates/builder-code-starter/server/plugins/agent-chat.ts.patch +2 -1
  66. package/dist/templates/chat/.agents/skills/context-awareness/SKILL.md +5 -0
  67. package/dist/templates/chat/app/components/chat/ChatHomeRedirect.tsx +2 -2
  68. package/dist/templates/chat/app/hooks/use-navigation-state.ts +51 -22
  69. package/dist/templates/chat/server/plugins/agent-chat.ts +1 -0
  70. package/dist/templates/default/.agents/skills/context-awareness/SKILL.md +5 -0
  71. package/dist/templates/workspace-core/.agents/skills/context-awareness/SKILL.md +5 -0
  72. package/dist/triggers/actions/manage-automation.d.ts +5 -5
  73. package/package.json +5 -5
  74. package/src/templates/builder-code-starter/.agents/skills/actions/SKILL.md.patch +1 -1
  75. package/src/templates/builder-code-starter/app/hooks/use-navigation-state.ts.patch +26 -45
  76. package/src/templates/builder-code-starter/server/plugins/agent-chat.ts.patch +2 -1
  77. package/src/templates/chat/.agents/skills/context-awareness/SKILL.md +5 -0
  78. package/src/templates/chat/app/components/chat/ChatHomeRedirect.tsx +2 -2
  79. package/src/templates/chat/app/hooks/use-navigation-state.ts +51 -22
  80. package/src/templates/chat/server/plugins/agent-chat.ts +1 -0
  81. package/src/templates/default/.agents/skills/context-awareness/SKILL.md +5 -0
  82. package/src/templates/workspace-core/.agents/skills/context-awareness/SKILL.md +5 -0
@@ -1266,18 +1266,53 @@ export function embedApp(options = {}) {
1266
1266
 
1267
1267
  // Best-effort telemetry sink so inline-embed failures are visible in
1268
1268
  // Sentry (handshake timeout, transplant fetch status/CORS, auth, CSP).
1269
- // The shell is a sandboxed opaque-origin iframe, so it POSTs to the app
1270
- // origin's CORS-open /_agent-native/mcp/embed-error route, derived from the
1271
- // known app URLs. Deduped per stage+message so a retry loop can't spam.
1269
+ // The shell is a sandboxed opaque-origin iframe, so it POSTs to the app's
1270
+ // CORS-open /_agent-native/mcp/embed-error route. Deduped per stage+message
1271
+ // so a retry loop can't spam.
1272
1272
  const reportedEmbedErrorKeys = new Set();
1273
1273
  let embedErrorReportCount = 0;
1274
- function embedReportOrigin() {
1275
- for (const value of [openStartUrl, openUrl, lastFrameSrc]) {
1274
+ function embedReportUrls() {
1275
+ const embedStartPath = "/_agent-native/embed/start";
1276
+ const reportPath = "/_agent-native/mcp/embed-error";
1277
+ const values = [openStartUrl, openUrl, lastFrameSrc];
1278
+ for (const value of values) {
1276
1279
  if (typeof value === "string" && value) {
1277
- try { return new URL(value, window.location.href).origin; } catch {}
1280
+ if (!URL.canParse(value, window.location.href)) continue;
1281
+ const url = new URL(value, window.location.href);
1282
+ url.username = "";
1283
+ url.password = "";
1284
+ if (!url.pathname.endsWith(embedStartPath)) continue;
1285
+ const appBasePath = url.pathname.slice(0, -embedStartPath.length);
1286
+ url.pathname = appBasePath + "/_agent-native/mcp/embed-error";
1287
+ url.search = "";
1288
+ url.hash = "";
1289
+ return [url.href];
1278
1290
  }
1279
1291
  }
1280
- return "";
1292
+
1293
+ const value = [openUrl, lastFrameSrc, openStartUrl].find(
1294
+ (candidate) =>
1295
+ typeof candidate === "string" &&
1296
+ candidate &&
1297
+ URL.canParse(candidate, window.location.href),
1298
+ );
1299
+ if (!value) return [];
1300
+
1301
+ const url = new URL(value, window.location.href);
1302
+ url.username = "";
1303
+ url.password = "";
1304
+ const routeMarker = url.pathname.indexOf("/_agent-native/");
1305
+ if (routeMarker >= 0) {
1306
+ url.pathname = url.pathname.slice(0, routeMarker) + reportPath;
1307
+ url.search = "";
1308
+ url.hash = "";
1309
+ return [url.href];
1310
+ }
1311
+
1312
+ url.pathname = reportPath;
1313
+ url.search = "";
1314
+ url.hash = "";
1315
+ return [url.href];
1281
1316
  }
1282
1317
  function reportEmbedError(stage, message, detail, status) {
1283
1318
  try {
@@ -1285,28 +1320,60 @@ export function embedApp(options = {}) {
1285
1320
  if (reportedEmbedErrorKeys.has(key) || embedErrorReportCount >= 8) return;
1286
1321
  reportedEmbedErrorKeys.add(key);
1287
1322
  embedErrorReportCount += 1;
1288
- const origin = embedReportOrigin();
1289
- if (!origin) return;
1323
+ const reportUrls = embedReportUrls();
1324
+ if (!reportUrls.length) return;
1325
+ const contextUrl = [openStartUrl, openUrl, lastFrameSrc].find(
1326
+ (value) =>
1327
+ typeof value === "string" &&
1328
+ value &&
1329
+ URL.canParse(value, window.location.href),
1330
+ );
1331
+ let embedUrl = "";
1332
+ if (contextUrl) {
1333
+ const url = new URL(contextUrl, window.location.href);
1334
+ // Embed paths can contain private resource identifiers.
1335
+ if (url.protocol === "http:" || url.protocol === "https:") {
1336
+ embedUrl = url.origin;
1337
+ }
1338
+ }
1290
1339
  let renderMode = "";
1291
1340
  try { renderMode = renderModeSource().mode || ""; } catch {}
1292
1341
  const body = JSON.stringify({
1293
1342
  stage: String(stage || ""),
1294
1343
  message: String(message || "").slice(0, 500),
1295
1344
  detail: detail ? String(detail).slice(0, 1200) : undefined,
1296
- url: openStartUrl || openUrl || lastFrameSrc || "",
1345
+ url: embedUrl,
1297
1346
  status: typeof status === "number" ? status : undefined,
1298
1347
  host: window.location.hostname || "",
1299
1348
  renderMode: renderMode,
1300
1349
  bridge: openAiBridge ? "openai" : app ? "native" : "none",
1301
1350
  userAgent: navigator.userAgent || ""
1302
1351
  });
1303
- fetch(origin + "/_agent-native/mcp/embed-error", {
1304
- method: "POST",
1305
- credentials: "omit",
1306
- keepalive: true,
1307
- headers: { "Content-Type": "application/json" },
1308
- body: body
1309
- }).catch(() => {});
1352
+ void (async () => {
1353
+ let lastFetchError;
1354
+ let lastResponseStatus;
1355
+ for (const reportUrl of reportUrls) {
1356
+ try {
1357
+ const response = await fetch(reportUrl, {
1358
+ method: "POST",
1359
+ credentials: "omit",
1360
+ keepalive: true,
1361
+ headers: { "Content-Type": "application/json" },
1362
+ body: body
1363
+ });
1364
+ if (response.status === 204) return;
1365
+ lastResponseStatus = response.status;
1366
+ } catch (error) {
1367
+ lastFetchError = error;
1368
+ }
1369
+ }
1370
+ if (lastFetchError || lastResponseStatus !== undefined) {
1371
+ console.warn("[agent-native] MCP embed error report was not accepted", {
1372
+ status: lastResponseStatus,
1373
+ error: lastFetchError
1374
+ });
1375
+ }
1376
+ })();
1310
1377
  } catch {}
1311
1378
  }
1312
1379
 
@@ -1,12 +1,12 @@
1
1
  export declare function createNotificationsHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<"" | import("./types.js").Notification[] | {
2
- error?: undefined;
3
2
  count: number;
4
3
  updated?: undefined;
4
+ error?: undefined;
5
5
  ok?: undefined;
6
6
  } | {
7
- error?: undefined;
8
7
  count?: undefined;
9
8
  updated: number;
9
+ error?: undefined;
10
10
  ok?: undefined;
11
11
  } | {
12
12
  count?: undefined;
@@ -14,8 +14,8 @@ export declare function createNotificationsHandler(): import("h3").EventHandlerW
14
14
  error: string;
15
15
  ok?: undefined;
16
16
  } | {
17
- error?: undefined;
18
17
  count?: undefined;
19
18
  updated?: undefined;
19
+ error?: undefined;
20
20
  ok: boolean;
21
21
  }>>;
@@ -27,43 +27,43 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
27
27
  thumbsUpRate: number;
28
28
  avgEvalScore: number;
29
29
  } | {
30
- ok?: undefined;
31
30
  error: string;
32
31
  message: string;
33
32
  summary?: undefined;
34
33
  spans?: undefined;
35
- } | {
36
- error?: undefined;
37
34
  ok?: undefined;
35
+ } | {
38
36
  message?: undefined;
39
37
  summary: import("./types.js").TraceSummary;
40
38
  spans: import("./types.js").TraceSpan[];
41
- } | {
42
39
  error?: undefined;
43
40
  ok?: undefined;
41
+ } | {
44
42
  message?: undefined;
45
43
  summary?: undefined;
46
44
  spans?: undefined;
47
45
  traceMissing: true;
48
46
  id: string;
49
- } | {
50
47
  error?: undefined;
51
48
  ok?: undefined;
49
+ } | {
52
50
  message?: undefined;
53
51
  summary?: undefined;
54
52
  spans?: undefined;
55
53
  traceMissing?: undefined;
56
54
  id: string;
57
- } | {
55
+ error?: undefined;
58
56
  ok?: undefined;
57
+ } | {
59
58
  message?: undefined;
60
59
  summary?: undefined;
61
60
  spans?: undefined;
62
61
  error: any;
62
+ ok?: undefined;
63
63
  } | {
64
- error?: undefined;
65
64
  message?: undefined;
66
65
  summary?: undefined;
67
66
  spans?: undefined;
67
+ error?: undefined;
68
68
  ok: boolean;
69
69
  }>>;
@@ -4,6 +4,7 @@ import { orgAdminAudit } from "../../audit/org-admin.js";
4
4
  import { getDbExec } from "../../db/client.js";
5
5
  import { requireOrgMember } from "../actions.js";
6
6
  export default defineAction({
7
+ mcpTool: true,
7
8
  description: "Invite one person to the active organization as a member and email them the invitation. Every call needs the user's approval. Only organization owners and admins may invite. Admin invitations are sent from Settings > Members.",
8
9
  schema: z.object({ email: z.string().trim().email() }),
9
10
  // The invitation email reaches a real person, so a saved "always allow"
@@ -4,6 +4,7 @@ import { getDbExec } from "../../db/client.js";
4
4
  import { requireOrgMember } from "../actions.js";
5
5
  const MAX_PENDING_INVITATIONS = 100;
6
6
  export default defineAction({
7
+ mcpTool: true,
7
8
  description: "List members of the active organization with their roles, sorted by email. Owners and admins also get pending invitations.",
8
9
  http: { method: "GET" },
9
10
  schema: z.object({
@@ -162,9 +162,9 @@ export declare const getMyOrgHandler: import("h3").EventHandlerWithFetch<import(
162
162
  soloDeploymentAdmin: boolean;
163
163
  }>>;
164
164
  export declare const createOrgHandler: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
165
+ name?: undefined;
165
166
  success: boolean;
166
167
  id?: undefined;
167
- name?: undefined;
168
168
  role?: undefined;
169
169
  } | {
170
170
  success?: undefined;
@@ -2,6 +2,6 @@ export declare function createProgressHandler(): import("h3").EventHandlerWithFe
2
2
  error: string;
3
3
  ok?: undefined;
4
4
  } | {
5
- error?: undefined;
6
5
  ok: boolean;
6
+ error?: undefined;
7
7
  }>>;
@@ -75,10 +75,9 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
75
75
  user: "user";
76
76
  }>>;
77
77
  }, z.core.$strip>>, {
78
- message?: undefined;
78
+ id?: undefined;
79
79
  deleted?: undefined;
80
80
  found?: undefined;
81
- id?: undefined;
82
81
  providers: {
83
82
  id: string;
84
83
  label: string;
@@ -92,18 +91,18 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
92
91
  provider?: undefined;
93
92
  registered?: undefined;
94
93
  label?: undefined;
95
- } | {
96
94
  message?: undefined;
97
- deleted?: undefined;
95
+ } | {
98
96
  id?: undefined;
97
+ deleted?: undefined;
99
98
  providers?: undefined;
100
99
  count?: undefined;
101
100
  found: boolean;
102
101
  provider: import("../custom-registry.js").CustomProviderConfig;
103
102
  registered?: undefined;
104
103
  label?: undefined;
105
- } | {
106
104
  message?: undefined;
105
+ } | {
107
106
  deleted?: undefined;
108
107
  providers?: undefined;
109
108
  count?: undefined;
@@ -112,8 +111,8 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
112
111
  id: string;
113
112
  registered?: undefined;
114
113
  label?: undefined;
115
- } | {
116
114
  message?: undefined;
115
+ } | {
117
116
  found?: undefined;
118
117
  providers?: undefined;
119
118
  count?: undefined;
@@ -122,6 +121,7 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
122
121
  id: string;
123
122
  registered?: undefined;
124
123
  label?: undefined;
124
+ message?: undefined;
125
125
  } | {
126
126
  deleted?: undefined;
127
127
  found?: undefined;
@@ -316,10 +316,10 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
316
316
  notes?: string | undefined;
317
317
  scope?: "org" | "user" | undefined;
318
318
  }, {
319
+ message?: undefined;
320
+ deleted?: undefined;
319
321
  id?: undefined;
320
322
  provider?: undefined;
321
- deleted?: undefined;
322
- message?: undefined;
323
323
  found?: undefined;
324
324
  providers: {
325
325
  id: string;
@@ -334,9 +334,9 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
334
334
  registered?: undefined;
335
335
  label?: undefined;
336
336
  } | {
337
- id?: undefined;
338
- deleted?: undefined;
339
337
  message?: undefined;
338
+ deleted?: undefined;
339
+ id?: undefined;
340
340
  providers?: undefined;
341
341
  count?: undefined;
342
342
  found: boolean;
@@ -344,9 +344,9 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
344
344
  registered?: undefined;
345
345
  label?: undefined;
346
346
  } | {
347
- provider?: undefined;
348
- deleted?: undefined;
349
347
  message?: undefined;
348
+ deleted?: undefined;
349
+ provider?: undefined;
350
350
  providers?: undefined;
351
351
  count?: undefined;
352
352
  found: boolean;
@@ -354,8 +354,8 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
354
354
  registered?: undefined;
355
355
  label?: undefined;
356
356
  } | {
357
- provider?: undefined;
358
357
  message?: undefined;
358
+ provider?: undefined;
359
359
  found?: undefined;
360
360
  providers?: undefined;
361
361
  count?: undefined;
@@ -364,8 +364,8 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
364
364
  registered?: undefined;
365
365
  label?: undefined;
366
366
  } | {
367
- provider?: undefined;
368
367
  deleted?: undefined;
368
+ provider?: undefined;
369
369
  found?: undefined;
370
370
  providers?: undefined;
371
371
  count?: undefined;
@@ -1,7 +1,7 @@
1
1
  import path from "node:path";
2
2
  import { getDatabaseUrl } from "../../db/client.js";
3
3
  import { parseArgs, fail } from "../utils.js";
4
- import { createPostgresScriptClient } from "./postgres-client.js";
4
+ import { createPostgresScriptClient, withPostgresStatementDeadline, } from "./postgres-client.js";
5
5
  import { finalRawDbSql, rawDbInsertTarget, readRawDbWriteStatement, verifyRawDbStatement, } from "./safety.js";
6
6
  import { buildScopingPostgres } from "./scoping.js";
7
7
  function parseSqlArgs(raw, label = "--args") {
@@ -151,9 +151,11 @@ Options:
151
151
  try {
152
152
  const results = [];
153
153
  await client.begin(async (tx) => {
154
- const scoping = await buildScopingPostgres(tx);
154
+ const timedTx = withPostgresStatementDeadline(tx);
155
+ const scoping = await buildScopingPostgres(timedTx);
155
156
  for (const statement of scoping.setup)
156
- await tx.unsafe(statement);
157
+ await timedTx.unsafe(statement);
158
+ let operationFailed = false;
157
159
  try {
158
160
  for (let index = 0; index < statements.length; index++) {
159
161
  const statement = statements[index];
@@ -162,13 +164,13 @@ Options:
162
164
  // final SQL and check it against this transaction's state right
163
165
  // before it runs, after any earlier statement in the batch.
164
166
  const executed = finalRawDbSql(injectOwnership(statement.statement, scoping), "write", label);
165
- await verifyRawDbStatement(tx, executed.statement, {
167
+ await verifyRawDbStatement(timedTx, executed.statement, {
166
168
  columnsByTable: scoping.columnsByTable,
167
169
  label,
168
170
  });
169
171
  const sql = executed.sql;
170
172
  try {
171
- const result = await tx.unsafe(sql, statement.args, {
173
+ const result = await timedTx.unsafe(sql, statement.args, {
172
174
  singleStatement: true,
173
175
  });
174
176
  results.push({
@@ -185,9 +187,19 @@ Options:
185
187
  }
186
188
  }
187
189
  }
190
+ catch (error) {
191
+ operationFailed = true;
192
+ throw error;
193
+ }
188
194
  finally {
189
195
  for (const statement of scoping.teardown) {
190
- await tx.unsafe(statement).catch(() => { });
196
+ try {
197
+ await timedTx.unsafe(statement);
198
+ }
199
+ catch (error) {
200
+ if (!operationFailed)
201
+ throw error;
202
+ }
191
203
  }
192
204
  }
193
205
  });
@@ -1,7 +1,7 @@
1
1
  import path from "node:path";
2
2
  import { getDatabaseUrl } from "../../db/client.js";
3
3
  import { parseArgs, fail } from "../utils.js";
4
- import { createPostgresScriptClient } from "./postgres-client.js";
4
+ import { createPostgresScriptClient, withPostgresStatementDeadline, } from "./postgres-client.js";
5
5
  import { assertNoRawDbAccessControlPatchTarget, assertNoSensitiveFrameworkTables, finalRawDbSql, readRawDbPatchStatement, validateRawDbPatchWhere, verifyRawDbStatement, } from "./safety.js";
6
6
  import { buildScopingPostgres } from "./scoping.js";
7
7
  function isValidIdentifier(value) {
@@ -348,12 +348,14 @@ Options:
348
348
  try {
349
349
  let output;
350
350
  await client.begin(async (tx) => {
351
- const scoping = await buildScopingPostgres(tx);
351
+ const timedTx = withPostgresStatementDeadline(tx);
352
+ const scoping = await buildScopingPostgres(timedTx);
352
353
  for (const statement of scoping.setup)
353
- await tx.unsafe(statement);
354
+ await timedTx.unsafe(statement);
355
+ let operationFailed = false;
354
356
  try {
355
- await verifyRawDbStatement(tx, select.statement);
356
- const selected = await tx.unsafe(select.sql, [], {
357
+ await verifyRawDbStatement(timedTx, select.statement);
358
+ const selected = await timedTx.unsafe(select.sql, [], {
357
359
  singleStatement: true,
358
360
  });
359
361
  if (selected.length === 0) {
@@ -369,8 +371,8 @@ Options:
369
371
  }
370
372
  const edited = applyEditsToValue(original, options);
371
373
  if (edited.applied > 0) {
372
- await verifyRawDbStatement(tx, update.statement);
373
- await tx.unsafe(update.sql, [edited.content], {
374
+ await verifyRawDbStatement(timedTx, update.statement);
375
+ await timedTx.unsafe(update.sql, [edited.content], {
374
376
  singleStatement: true,
375
377
  });
376
378
  }
@@ -384,9 +386,19 @@ Options:
384
386
  results: edited.results,
385
387
  };
386
388
  }
389
+ catch (error) {
390
+ operationFailed = true;
391
+ throw error;
392
+ }
387
393
  finally {
388
394
  for (const statement of scoping.teardown) {
389
- await tx.unsafe(statement).catch(() => { });
395
+ try {
396
+ await timedTx.unsafe(statement);
397
+ }
398
+ catch (error) {
399
+ if (!operationFailed)
400
+ throw error;
401
+ }
390
402
  }
391
403
  }
392
404
  });
@@ -13,4 +13,6 @@ export interface PostgresScriptClient {
13
13
  begin<T>(fn: (tx: PostgresScriptClient) => Promise<T>): Promise<T>;
14
14
  end(): Promise<void>;
15
15
  }
16
+ export declare function setRawDbStatementTimeout(client: Pick<PostgresScriptClient, "unsafe">): Promise<void>;
17
+ export declare function withPostgresStatementDeadline(client: Pick<PostgresScriptClient, "unsafe">, timeoutMs?: number): Pick<PostgresScriptClient, "unsafe">;
16
18
  export declare function createPostgresScriptClient(url: string): Promise<PostgresScriptClient>;
@@ -1,4 +1,20 @@
1
- import { getPgliteClient, isPgliteUrl, toPostgresParams, } from "../../db/client.js";
1
+ import { dbOpTimeoutMs, getPgliteClient, isPgliteUrl, postgresStatementTimeoutMs, toPostgresParams, } from "../../db/client.js";
2
+ export async function setRawDbStatementTimeout(client) {
3
+ await client.unsafe(`SET LOCAL statement_timeout = ${postgresStatementTimeoutMs(dbOpTimeoutMs())}`);
4
+ }
5
+ export function withPostgresStatementDeadline(client, timeoutMs = dbOpTimeoutMs()) {
6
+ const deadline = Date.now() + timeoutMs;
7
+ return {
8
+ async unsafe(sql, args, options) {
9
+ const remainingMs = Math.floor(deadline - Date.now());
10
+ if (remainingMs <= 0) {
11
+ throw new Error(`Database transaction timed out after ${timeoutMs}ms.`);
12
+ }
13
+ await client.unsafe(`SET LOCAL statement_timeout = ${postgresStatementTimeoutMs(remainingMs)}`);
14
+ return client.unsafe(sql, args, options);
15
+ },
16
+ };
17
+ }
2
18
  function rowsResult(rows, affectedRows) {
3
19
  const records = (rows ?? []);
4
20
  const result = records;
@@ -3,7 +3,7 @@ import { assertHostedRuntimeDatabase, getLocalDatabaseUrl, } from "../../db/clie
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
- import { createPostgresScriptClient } from "./postgres-client.js";
6
+ import { createPostgresScriptClient, withPostgresStatementDeadline, } from "./postgres-client.js";
7
7
  import { finalRawDbSql, readRawDbReadStatement, verifyRawDbStatement, } from "./safety.js";
8
8
  import { buildScopingPostgres } from "./scoping.js";
9
9
  function parseSqlArgs(raw) {
@@ -67,14 +67,15 @@ export async function runDbQuery(options) {
67
67
  let rows = [];
68
68
  try {
69
69
  await client.begin(async (tx) => {
70
- const scoping = await buildScopingPostgres(tx);
70
+ const timedTx = withPostgresStatementDeadline(tx);
71
+ const scoping = await buildScopingPostgres(timedTx);
71
72
  for (const setup of scoping.setup)
72
- await tx.unsafe(setup);
73
+ await timedTx.unsafe(setup);
73
74
  // From here on the transaction cannot write, so a data-modifying CTE,
74
75
  // 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, {
76
+ await timedTx.unsafe("SET TRANSACTION READ ONLY");
77
+ await verifyRawDbStatement(timedTx, executed.statement);
78
+ const result = await timedTx.unsafe(executed.sql, sqlArgs, {
78
79
  singleStatement: true,
79
80
  });
80
81
  rows = Array.from(result);
@@ -50,6 +50,7 @@ const SENSITIVE_FRAMEWORK_TABLES = new Set([
50
50
  "users",
51
51
  "session",
52
52
  "sessions",
53
+ "chat_threads",
53
54
  "account",
54
55
  "accounts",
55
56
  "verification",
@@ -83,7 +84,7 @@ function assertNoSensitiveFrameworkTableTokens(tokens, operation) {
83
84
  const name = token.value.toLowerCase();
84
85
  if (!SENSITIVE_FRAMEWORK_TABLES.has(name))
85
86
  continue;
86
- fail(`Sensitive framework table "${name}" is not ${sensitiveVerb(operation)} through raw DB tools. Use the framework auth, secrets, or OAuth APIs instead.`);
87
+ fail(`Sensitive framework table "${name}" is not ${sensitiveVerb(operation)} through raw DB tools. Use the appropriate framework API instead.`);
87
88
  }
88
89
  }
89
90
  export function assertNoSensitiveFrameworkTables(sql, operation) {
@@ -57,39 +57,39 @@ export declare function createWriteSecretHandler(): import("h3").EventHandlerWit
57
57
  } | {
58
58
  error: string;
59
59
  errorCode?: undefined;
60
- ok?: undefined;
61
60
  status?: undefined;
61
+ ok?: undefined;
62
62
  } | {
63
63
  error: string;
64
64
  errorCode: string;
65
- ok?: undefined;
66
65
  status?: undefined;
66
+ ok?: undefined;
67
67
  } | {
68
- error?: undefined;
69
68
  errorCode?: undefined;
70
69
  ok: boolean;
71
70
  status: string;
71
+ error?: undefined;
72
72
  } | {
73
- ok?: undefined;
74
73
  error: string;
75
74
  removed?: undefined;
75
+ ok?: undefined;
76
76
  } | {
77
- error?: undefined;
78
77
  ok: boolean;
79
78
  removed: boolean;
79
+ error?: undefined;
80
80
  }>>;
81
81
  export declare function createTestSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
82
- ok?: undefined;
83
82
  error: string;
84
83
  note?: undefined;
84
+ ok?: undefined;
85
85
  } | {
86
- error?: undefined;
87
86
  ok: boolean;
88
87
  note?: undefined;
89
- } | {
90
88
  error?: undefined;
89
+ } | {
91
90
  ok: boolean;
92
91
  note: string;
92
+ error?: undefined;
93
93
  } | {
94
94
  note?: undefined;
95
95
  ok: boolean;
@@ -117,11 +117,11 @@ export declare function createAdHocSecretHandler(): import("h3").EventHandlerWit
117
117
  } | {
118
118
  error: string;
119
119
  } | {
120
- error?: undefined;
121
120
  ok: boolean;
122
121
  key: string;
123
- } | {
124
122
  error?: undefined;
123
+ } | {
125
124
  ok: boolean;
126
125
  removed: boolean;
126
+ error?: undefined;
127
127
  }>>;
@@ -25,6 +25,7 @@ import { applyProviderModelSelection, providerForEngineName, resolveProviderMode
25
25
  import { startRun } from "../agent/run-manager.js";
26
26
  import { callerHasRunAccess, callerHasThreadAccess, } from "../agent/run-ownership.js";
27
27
  import { readBackgroundRunClaim } from "../agent/run-store.js";
28
+ import { buildAssistantMessageFromRunJournal } from "../agent/run-thread-projector.js";
28
29
  import { buildCurrentTimeUserContext, buildRuntimeContextPrompt, } from "../agent/runtime-context.js";
29
30
  import { buildAssistantMessage, buildUserMessage, applySubmittedUserMessage, containsInlineAttachmentPayload, foldAgentChatRunCompletion, extractThreadMeta, foldUnstartedTurnFailure, mergeThreadDataForClientSave, } from "../agent/thread-data-builder.js";
30
31
  import { appendThreadDebugHistory } from "../agent/thread-debug-history.js";
@@ -2824,21 +2825,29 @@ export function createAgentChatPlugin(options) {
2824
2825
  }
2825
2826
  }
2826
2827
  // Callback to persist agent response when run finishes (even if client disconnected).
2827
- // Reconstructs the assistant message from buffered events and appends to thread_data.
2828
+ // Reconstructs the assistant message from the durable run journal.
2828
2829
  const onRunComplete = async (run, threadId, trackingSource) => {
2829
2830
  const runThreadId = String(run?.threadId ?? threadId ?? "");
2830
2831
  const chatScope = getRequestRunContext()?.chatScope;
2831
- const assistantMsg = buildAssistantMessage(run.events ?? [], run.runId, {
2832
+ const assistantOptions = {
2832
2833
  scope: chatScope,
2833
2834
  suppressInternalContinuation: true,
2834
- turnId: typeof run.turnId === "string" && run.turnId
2835
- ? run.turnId
2836
- : undefined,
2837
- runDurationMs: typeof run.startedAt === "number" &&
2838
- Number.isFinite(run.startedAt)
2835
+ runDurationMs: typeof run.startedAt === "number" && Number.isFinite(run.startedAt)
2839
2836
  ? Math.max(0, Date.now() - run.startedAt)
2840
2837
  : undefined,
2841
- });
2838
+ };
2839
+ const assistantMsg = runThreadId && run.turnId
2840
+ ? await buildAssistantMessageFromRunJournal({
2841
+ threadId: runThreadId,
2842
+ turnId: run.turnId,
2843
+ runId: run.runId,
2844
+ runEvents: run.events ?? [],
2845
+ options: assistantOptions,
2846
+ })
2847
+ : buildAssistantMessage(run.events ?? [], run.runId, {
2848
+ ...assistantOptions,
2849
+ turnId: run.turnId,
2850
+ });
2842
2851
  const runContext = getRequestRunContext();
2843
2852
  const failureCode = run.status === "errored"
2844
2853
  ? [...(run.events ?? [])]