@agent-native/core 0.168.13 → 0.169.1

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 (96) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/actions/bigquery-table-info.ts +3 -2
  3. package/corpus/templates/analytics/actions/search-bigquery-schema.ts +130 -4
  4. package/corpus/templates/analytics/agent-native.config.ts +1 -0
  5. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +36 -23
  6. package/corpus/templates/analytics/app/lib/dashboard-visibility.ts +41 -0
  7. package/corpus/templates/analytics/app/pages/DashboardOverview.tsx +34 -9
  8. package/corpus/templates/analytics/changelog/2026-08-21-dashboard-filters-keep-other-users-dashboards-out-of-mine.md +6 -0
  9. package/corpus/templates/analytics/server/lib/real-data-actions.ts +14 -3
  10. package/corpus/templates/analytics/server/plugins/agent-chat.ts +33 -3
  11. package/corpus/templates/brain/server/lib/brain.ts +27 -2
  12. package/corpus/templates/clips/actions/stop-meeting-recording.ts +87 -6
  13. package/corpus/templates/clips/app/components/library/search-bar.tsx +1 -1
  14. package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +3 -4
  15. package/corpus/templates/design/app/components/design/EditPanel.tsx +15 -47
  16. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +3 -3
  17. package/corpus/templates/design/app/components/design/keyboard-shortcuts.ts +0 -7
  18. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +2 -4
  19. package/corpus/templates/design/app/i18n-data.ts +10 -10
  20. package/corpus/templates/design/app/pages/DesignEditor.tsx +22 -22
  21. package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +39 -8
  22. package/corpus/templates/design/app/pages/DesignSystems.tsx +1 -1
  23. package/corpus/templates/design/app/pages/Index.tsx +1 -1
  24. package/corpus/templates/design/app/pages/design-editor/preview-navigation.ts +25 -0
  25. package/corpus/templates/mail/app/hooks/use-emails.ts +13 -3
  26. package/dist/agent/engine/credential-errors.d.ts +1 -1
  27. package/dist/agent/engine/credential-errors.js +2 -2
  28. package/dist/agent/engine/first-event-timeout.d.ts +8 -0
  29. package/dist/agent/engine/first-event-timeout.js +8 -0
  30. package/dist/agent/production-agent.d.ts +0 -30
  31. package/dist/agent/production-agent.js +17 -38
  32. package/dist/agent/run-loop-with-resume.d.ts +38 -25
  33. package/dist/agent/run-loop-with-resume.js +140 -55
  34. package/dist/agent/run-manager.d.ts +83 -68
  35. package/dist/agent/run-manager.js +280 -94
  36. package/dist/agent/run-store.d.ts +31 -0
  37. package/dist/agent/run-store.js +42 -12
  38. package/dist/app-config/agent.d.ts +2 -0
  39. package/dist/app-config/agent.js +33 -0
  40. package/dist/app-config/run-lifecycle-invariants.d.ts +248 -0
  41. package/dist/app-config/run-lifecycle-invariants.js +342 -0
  42. package/dist/app-config/schema.d.ts +2 -0
  43. package/dist/app-config/store.js +9 -1
  44. package/dist/client/AssistantChat.js +47 -15
  45. package/dist/client/ConnectBuilderCard.d.ts +3 -1
  46. package/dist/client/ConnectBuilderCard.js +6 -3
  47. package/dist/client/agent-chat-adapter.d.ts +0 -2
  48. package/dist/client/agent-chat-adapter.js +7 -23
  49. package/dist/client/chat/message-components.d.ts +7 -0
  50. package/dist/client/chat/message-components.js +1 -1
  51. package/dist/client/chat/run-recovery.js +5 -2
  52. package/dist/client/chat/tool-call-display.js +9 -3
  53. package/dist/client/error-format.js +12 -0
  54. package/dist/client/navigation/index.d.ts +1 -0
  55. package/dist/client/navigation/index.js +1 -0
  56. package/dist/client/route-state.js +6 -0
  57. package/dist/client/workspace-app-navigation.d.ts +10 -0
  58. package/dist/client/workspace-app-navigation.js +38 -0
  59. package/dist/collab/struct-routes.d.ts +1 -1
  60. package/dist/deploy/build.d.ts +13 -0
  61. package/dist/deploy/build.js +51 -0
  62. package/dist/jobs/background-automation-runner.d.ts +25 -0
  63. package/dist/jobs/background-automation-runner.js +104 -21
  64. package/dist/jobs/run-history.d.ts +7 -1
  65. package/dist/jobs/run-history.js +57 -14
  66. package/dist/localization/core-messages/ar-SA.js +1 -1
  67. package/dist/localization/core-messages/de-DE.js +1 -1
  68. package/dist/localization/core-messages/en-US.d.ts +1 -1
  69. package/dist/localization/core-messages/en-US.js +1 -1
  70. package/dist/localization/core-messages/es-ES.js +1 -1
  71. package/dist/localization/core-messages/fr-FR.js +1 -1
  72. package/dist/localization/core-messages/hi-IN.js +1 -1
  73. package/dist/localization/core-messages/ja-JP.js +1 -1
  74. package/dist/localization/core-messages/ko-KR.js +1 -1
  75. package/dist/localization/core-messages/pt-BR.js +1 -1
  76. package/dist/localization/core-messages/zh-CN.js +1 -1
  77. package/dist/localization/core-messages/zh-TW.js +1 -1
  78. package/dist/localization/core-messages.d.ts +1 -1
  79. package/dist/notifications/routes.d.ts +1 -1
  80. package/dist/observability/routes.d.ts +3 -3
  81. package/dist/observability/traces.d.ts +13 -0
  82. package/dist/observability/traces.js +369 -317
  83. package/dist/progress/routes.d.ts +1 -1
  84. package/dist/provider-api/actions/custom-provider-registration.d.ts +12 -12
  85. package/dist/provider-api/actions/provider-api.d.ts +13 -13
  86. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  87. package/dist/resources/handlers.d.ts +1 -1
  88. package/dist/scripts/db/check-scoping.js +9 -6
  89. package/dist/secrets/routes.d.ts +6 -6
  90. package/dist/server/agent-chat-plugin.js +2 -4
  91. package/dist/server/builder-browser.d.ts +4 -0
  92. package/dist/server/builder-browser.js +23 -1
  93. package/dist/server/core-routes-plugin.js +13 -1
  94. package/dist/server/transcribe-voice.d.ts +1 -1
  95. package/package.json +3 -3
  96. /package/corpus/templates/dispatch/app/routes/{apps.$appId.tsx → apps.$appId.$.tsx} +0 -0
@@ -2,7 +2,7 @@ import { collectFinalResponseTextFromAgentEvents } from "../a2a/response-text.js
2
2
  import { getStoredModelForEngine, normalizeModelForEngine, resolveEngine, } from "../agent/engine/index.js";
3
3
  import { actionsToEngineTools, filterInitialEngineTools, getOwnerActiveApiKey, } from "../agent/production-agent.js";
4
4
  import { runAgentLoopDirectWithSoftTimeout } from "../agent/run-loop-with-resume.js";
5
- import { resolveRunSoftTimeoutMs, startRun, } from "../agent/run-manager.js";
5
+ import { abortRun, resolveBackgroundAutomationSoftTimeoutMs, resolveBackgroundRunHardTimeoutMs, startRun, } from "../agent/run-manager.js";
6
6
  import { claimBackgroundRun, insertRun } from "../agent/run-store.js";
7
7
  import { buildAssistantMessage, buildUserMessage, extractThreadMeta, foldAssistantTurn, upsertUserMessage, } from "../agent/thread-data-builder.js";
8
8
  import { attachToolSearch } from "../agent/tool-search.js";
@@ -13,8 +13,27 @@ import { organizationIdFromResourceOwner, organizationResourceOwner, } from "../
13
13
  import { captureError } from "../server/capture-error.js";
14
14
  import { runWithRequestContext, } from "../server/request-context.js";
15
15
  import { attachAutomationRunThread, finishAutomationRun, startAutomationRun, } from "./run-history.js";
16
- const BACKGROUND_RUN_STUCK_MS = 10 * 60_000;
16
+ /**
17
+ * Default hard abort for one in-process automation run. Read through
18
+ * `resolveBackgroundRunHardTimeoutMs()` at the use site — this is the host's
19
+ * real function budget for scheduled work, and it differs by deployment.
20
+ */
17
21
  export const BACKGROUND_RUN_HARD_TIMEOUT_MS = 10 * 60_000;
22
+ /**
23
+ * Terminal failure of a background automation, carrying the machine-readable
24
+ * code the failure taxonomy already computes.
25
+ *
26
+ * The code used to be produced and then dropped, so "how often are runs cut
27
+ * off?" was a `LIKE '%no_progress%'` over an English sentence.
28
+ */
29
+ export class BackgroundAutomationRunError extends Error {
30
+ errorCode;
31
+ constructor(message, errorCode) {
32
+ super(message);
33
+ this.name = "BackgroundAutomationRunError";
34
+ this.errorCode = errorCode;
35
+ }
36
+ }
18
37
  /**
19
38
  * A persisted background run must not outlive its execution identity.
20
39
  * Organization runs fail closed when membership state cannot be read. A
@@ -112,8 +131,11 @@ export function isBackgroundAutomationRunActive(meta, now = new Date()) {
112
131
  if (!meta.lastRun)
113
132
  return false;
114
133
  const startedAt = new Date(meta.lastRun).getTime();
134
+ // Tracks the hard abort: past it no run of this automation is still alive, so
135
+ // a deployment that raises the abort must not have its live runs treated as
136
+ // stuck and re-dispatched underneath themselves.
115
137
  return (Number.isFinite(startedAt) &&
116
- now.getTime() - startedAt < BACKGROUND_RUN_STUCK_MS);
138
+ now.getTime() - startedAt < resolveBackgroundRunHardTimeoutMs());
117
139
  }
118
140
  /**
119
141
  * A soft-timeout/no-progress checkpoint is a continuation boundary, not a
@@ -186,6 +208,9 @@ export async function runBackgroundAutomation(options, deps) {
186
208
  }
187
209
  catch (err) {
188
210
  const message = err instanceof Error ? err.message : String(err);
211
+ const errorCode = err instanceof BackgroundAutomationRunError
212
+ ? err.errorCode
213
+ : "background_automation_failed";
189
214
  // Both callers (recurring-jobs scheduler, trigger dispatcher) record this
190
215
  // onto the automation's own metadata and console.error it, and neither
191
216
  // reports it. A failure visible only in a resource field and stdout is not
@@ -204,7 +229,7 @@ export async function runBackgroundAutomation(options, deps) {
204
229
  },
205
230
  ...(runIdRef.current ? { aiTraceId: runIdRef.current } : {}),
206
231
  });
207
- await recordRunOutcome(historyId, "error", `${message}. No delivery was confirmed.`);
232
+ await recordRunOutcome(historyId, "error", `${message}. No delivery was confirmed.`, errorCode);
208
233
  throw err;
209
234
  }
210
235
  // Outside the try: history is bookkeeping about the run, so a failure to
@@ -229,8 +254,9 @@ async function recordRunThread(historyId, threadId, runId) {
229
254
  }
230
255
  function backgroundAutomationPersistFailure(input) {
231
256
  if (input.hardTimedOut) {
257
+ const minutes = Math.round((input.hardTimeoutMs ?? BACKGROUND_RUN_HARD_TIMEOUT_MS) / 60_000);
232
258
  return {
233
- message: `Background automation timed out after ${BACKGROUND_RUN_HARD_TIMEOUT_MS / 60_000} minutes`,
259
+ message: `Background automation timed out after ${minutes} minutes`,
234
260
  errorCode: "background_automation_hard_timeout",
235
261
  };
236
262
  }
@@ -301,11 +327,11 @@ async function persistBackgroundAutomationTurn(input) {
301
327
  await updateThreadData(input.threadId, JSON.stringify(repo), input.threadTitle || row.title, meta.preview || row.preview, Array.isArray(messages) ? messages.length : 0);
302
328
  });
303
329
  }
304
- async function recordRunOutcome(historyId, status, error) {
330
+ async function recordRunOutcome(historyId, status, error, errorCode) {
305
331
  if (!historyId)
306
332
  return;
307
333
  try {
308
- await finishAutomationRun(historyId, status, error);
334
+ await finishAutomationRun(historyId, status, error, errorCode);
309
335
  }
310
336
  catch (err) {
311
337
  console.error(`[automations] Could not record run ${historyId} as ${status}:`, err);
@@ -372,12 +398,15 @@ async function executeBackgroundAutomation(options, deps, historyId, runIdRef) {
372
398
  // Hardcoded rather than `isInBackgroundFunctionRuntime()` (what
373
399
  // webhook-handler.ts uses): a webhook can arrive on either runtime, but
374
400
  // a scheduler tick never serves a synchronous request, so the
375
- // interactive clamp never applies to it. The wider soft ceiling stays
376
- // bounded by this runner's own BACKGROUND_RUN_HARD_TIMEOUT_MS abort.
377
- const softTimeoutMs = resolveRunSoftTimeoutMs(undefined, {
378
- useHostedDefault: true,
379
- backgroundFunction: true,
380
- });
401
+ // interactive clamp never applies to it.
402
+ //
403
+ // Derived from this runner's OWN hard abort, not from the durable-chat
404
+ // background ceiling: that ceiling is 13 minutes and this process is
405
+ // killed at 10, so taking it left the recoverable soft-timeout boundary
406
+ // as dead code and the terminal no-progress backstop as the only
407
+ // boundary an automation could reach.
408
+ const hardTimeoutMs = resolveBackgroundRunHardTimeoutMs();
409
+ const softTimeoutMs = resolveBackgroundAutomationSoftTimeoutMs();
381
410
  const usageRef = { current: null };
382
411
  let responseText = "";
383
412
  let hardAbortTimer = null;
@@ -400,8 +429,8 @@ async function executeBackgroundAutomation(options, deps, historyId, runIdRef) {
400
429
  throw new Error(`Background automation "${automation.name}" (run "${runId}") could not claim its own freshly-inserted run row`);
401
430
  }
402
431
  await new Promise((resolve, reject) => {
403
- const activeRun = startRun(runId, thread.id, async (send, signal) => {
404
- usageRef.current = await runAgentLoopDirectWithSoftTimeout({
432
+ const activeRun = startRun(runId, thread.id, async (send, signal, control) => {
433
+ const loopOpts = {
405
434
  engine,
406
435
  model,
407
436
  systemPrompt,
@@ -425,7 +454,45 @@ async function executeBackgroundAutomation(options, deps, historyId, runIdRef) {
425
454
  runId,
426
455
  maxIterations: automation.meta.maxIterations,
427
456
  maxRunInputTokens: automation.meta.maxRunInputTokens,
428
- }, softTimeoutMs, { backgroundFunction: true });
457
+ };
458
+ // Same adapter A2A uses: bridge this runner's multi-argument shape
459
+ // to the single-argument `runAgentLoop` `instrumentAgentLoop`
460
+ // expects. `control` is what lets a chunk boundary be recovered
461
+ // here instead of ending the turn.
462
+ const execute = (o = loopOpts) => runAgentLoopDirectWithSoftTimeout(o, softTimeoutMs, { backgroundFunction: true }, control);
463
+ let instrumented = false;
464
+ try {
465
+ const { getObservabilityConfig, instrumentAgentLoop } = await import("../observability/traces.js");
466
+ const config = await getObservabilityConfig();
467
+ if (config.enabled) {
468
+ instrumented = true;
469
+ usageRef.current = await instrumentAgentLoop({
470
+ runAgentLoop: (o) => execute(o),
471
+ loopOpts,
472
+ runId,
473
+ threadId: thread.id,
474
+ // A scheduled run is NOT anonymous. Passing the owner is what
475
+ // makes it visible to per-user observability reads.
476
+ userId: ownerEmail,
477
+ config,
478
+ spanName: "background_automation_run",
479
+ metadata: {
480
+ automation: automation.name,
481
+ trigger: "background_automation",
482
+ scope: orgId ? "organization" : "personal",
483
+ },
484
+ });
485
+ return;
486
+ }
487
+ }
488
+ catch (error) {
489
+ // Match A2A and interactive chat: a setup failure falls through
490
+ // to an uninstrumented run, but a failure from INSIDE the
491
+ // instrumented loop is the real run failure and must rethrow.
492
+ if (instrumented)
493
+ throw error;
494
+ }
495
+ usageRef.current = await execute();
429
496
  }, async (run) => {
430
497
  if (hardAbortTimer) {
431
498
  clearTimeout(hardAbortTimer);
@@ -434,6 +501,7 @@ async function executeBackgroundAutomation(options, deps, historyId, runIdRef) {
434
501
  const persistFailure = backgroundAutomationPersistFailure({
435
502
  run,
436
503
  hardTimedOut,
504
+ hardTimeoutMs,
437
505
  });
438
506
  try {
439
507
  await persistBackgroundAutomationTurn({
@@ -453,11 +521,11 @@ async function executeBackgroundAutomation(options, deps, historyId, runIdRef) {
453
521
  if (hardTimedOut)
454
522
  return;
455
523
  if (persistFailure) {
456
- reject(new Error(persistFailure.message));
524
+ reject(new BackgroundAutomationRunError(persistFailure.message, persistFailure.errorCode));
457
525
  return;
458
526
  }
459
527
  if (run.status !== "completed") {
460
- reject(new Error(`Background automation ended with status: ${run.status}`));
528
+ reject(new BackgroundAutomationRunError(`Background automation ended with status: ${run.status}`, `background_automation_${run.status}`));
461
529
  return;
462
530
  }
463
531
  responseText = collectFinalResponseTextFromAgentEvents((run.events ?? []).map((entry) => entry.event));
@@ -465,22 +533,37 @@ async function executeBackgroundAutomation(options, deps, historyId, runIdRef) {
465
533
  }, {
466
534
  softTimeoutMs,
467
535
  backgroundFunction: true,
536
+ // This runner owns continuation in-process: there is no HTTP body
537
+ // to re-POST and no `chainServerDrivenContinuation` behind it, so a
538
+ // checkpoint must end the CHUNK and let the loop above recover it.
539
+ recoverChunkBoundaries: true,
540
+ // Matches the `dispatch_mode` this runner already writes onto the
541
+ // run row at insert. Without it the terminal and boundary analytics
542
+ // events reported every scheduled run as foreground.
543
+ dispatchMode: "background",
544
+ noProgressTimeoutMs: options.noProgressTimeoutMs,
545
+ backgroundNoProgressTimeoutMs: options.backgroundNoProgressTimeoutMs,
468
546
  model,
469
547
  engineName: engine.name,
548
+ userId: ownerEmail,
470
549
  });
471
550
  hardAbortTimer = setTimeout(() => {
472
551
  hardAbortTimer = null;
473
552
  if (activeRun.status !== "running")
474
553
  return;
475
554
  hardTimedOut = true;
476
- activeRun.abort.abort("background_automation_hard_timeout");
477
- const timeoutError = new Error(`Background automation timed out after ${BACKGROUND_RUN_HARD_TIMEOUT_MS / 60_000} minutes`);
555
+ // `abortRun`, not `activeRun.abort.abort`: the controller alone
556
+ // carries no reason the run manager can see, so finalization fell
557
+ // through to `aborted:user` and a hard timeout was recorded as a
558
+ // person pressing Stop.
559
+ abortRun(runId, "background_automation_hard_timeout");
560
+ const timeoutError = new BackgroundAutomationRunError(`Background automation timed out after ${Math.round(hardTimeoutMs / 60_000)} minutes`, "background_automation_hard_timeout");
478
561
  void activeRun.finalized
479
562
  .catch(() => { })
480
563
  .then(() => {
481
564
  reject(timeoutError);
482
565
  });
483
- }, BACKGROUND_RUN_HARD_TIMEOUT_MS);
566
+ }, hardTimeoutMs);
484
567
  }).finally(() => {
485
568
  if (hardAbortTimer) {
486
569
  clearTimeout(hardAbortTimer);
@@ -20,6 +20,12 @@ export interface AutomationRun {
20
20
  startedAt: number;
21
21
  finishedAt: number | null;
22
22
  error: string | null;
23
+ /**
24
+ * Machine-readable failure code, so "how often are runs cut off?" is a
25
+ * GROUP BY instead of a LIKE over an English sentence. Null on success and
26
+ * on rows written before the column existed.
27
+ */
28
+ errorCode: string | null;
23
29
  }
24
30
  export interface StartAutomationRunInput {
25
31
  owner: string;
@@ -56,7 +62,7 @@ export declare function listUnclaimedAutomationRuns(options?: {
56
62
  olderThanMs?: number;
57
63
  limit?: number;
58
64
  }): Promise<AutomationRun[]>;
59
- export declare function finishAutomationRun(id: string, status: Exclude<AutomationRunStatus, "running">, error?: string): Promise<void>;
65
+ export declare function finishAutomationRun(id: string, status: Exclude<AutomationRunStatus, "running">, error?: string, errorCode?: string): Promise<void>;
60
66
  /**
61
67
  * Attach the agent thread once it exists. The thread is created after the run
62
68
  * row so the history survives a crash between the two.
@@ -1,5 +1,6 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { z } from "zod";
3
+ import { resolveBackgroundRunHardTimeoutMs } from "../agent/run-manager.js";
3
4
  import { getDbExec, intType, isPostgres } from "../db/client.js";
4
5
  import { ensureColumnExists, ensureIndexExists, ensureTableExists, } from "../db/ddl-guard.js";
5
6
  import { runMigrations } from "../db/migrations.js";
@@ -17,20 +18,38 @@ registerEvent({
17
18
  threadId: z.string().nullable(),
18
19
  status: z.enum(["success", "error", "interrupted"]),
19
20
  error: z.string().nullable(),
21
+ errorCode: z.string().nullable(),
22
+ /** Wall-clock from `started_at` to now. Null when the row predates the
23
+ * start timestamp being readable, so "not measured" stays distinct from
24
+ * "took no time". */
25
+ durationMs: z.number().nullable(),
20
26
  }),
21
27
  });
22
28
  const TABLE = "automation_runs";
23
29
  const MAX_ERROR_LENGTH = 500;
30
+ const MAX_ERROR_CODE_LENGTH = 100;
24
31
  /**
25
- * Generous multiple of the runner's own 10 minute hard abort
26
- * (BACKGROUND_RUN_HARD_TIMEOUT_MS). Past this, no run is still alive.
32
+ * Past this, no run of this automation is still alive.
33
+ *
34
+ * DERIVED from the runner's own hard abort rather than pinned, because that
35
+ * abort became configurable: a fixed 15 minutes against a longer configured
36
+ * timeout would report a still-executing run as `interrupted` and expire its
37
+ * claim lease, redispatching it on top of itself. Half again the abort leaves
38
+ * room for wind-down and the terminal write without ever preceding them.
27
39
  */
28
- const RUN_LIVENESS_CEILING_MS = 15 * 60_000;
40
+ function resolveRunLivenessCeilingMs() {
41
+ return Math.ceil(resolveBackgroundRunHardTimeoutMs() * 1.5);
42
+ }
29
43
  const INTERRUPTED_RUN_MESSAGE = "Worker stopped before a terminal result was recorded. The serverless worker may have timed out or been recycled. No delivery was confirmed.";
30
- // The background worker has a shorter hard timeout than this lease. A worker
31
- // that dies after claiming can therefore be redelivered without overlapping a
32
- // still-live execution under normal runtime limits.
33
- const CLAIM_LEASE_MS = RUN_LIVENESS_CEILING_MS;
44
+ /**
45
+ * Derived at read time alongside `INTERRUPTED_RUN_MESSAGE`: a process killed
46
+ * mid-run cannot write its own code any more than it can write its own message.
47
+ */
48
+ const INTERRUPTED_RUN_ERROR_CODE = "background_automation_interrupted";
49
+ // The background worker's hard timeout is always shorter than this lease, by
50
+ // construction above. A worker that dies after claiming can therefore be
51
+ // redelivered without overlapping a still-live execution.
52
+ const claimLeaseMs = () => resolveRunLivenessCeilingMs();
34
53
  /** Rows kept per automation, so a per-minute schedule cannot grow forever. */
35
54
  const RUNS_RETAINED_PER_AUTOMATION = 50;
36
55
  /** Authoritative release-time schema for durable automation history. */
@@ -76,6 +95,11 @@ export const AUTOMATION_RUN_MIGRATIONS = [
76
95
  name: "automation-runs-app-id",
77
96
  sql: `ALTER TABLE ${TABLE} ADD COLUMN IF NOT EXISTS app_id TEXT`,
78
97
  },
98
+ {
99
+ version: 5,
100
+ name: "automation-runs-error-code",
101
+ sql: `ALTER TABLE ${TABLE} ADD COLUMN IF NOT EXISTS error_code TEXT`,
102
+ },
79
103
  ];
80
104
  export async function runAutomationRunMigrations(nitroApp) {
81
105
  await runMigrations(AUTOMATION_RUN_MIGRATIONS, {
@@ -102,6 +126,7 @@ export async function ensureTable() {
102
126
  started_at ${intType()} NOT NULL,
103
127
  finished_at ${intType()},
104
128
  error TEXT,
129
+ error_code TEXT,
105
130
  claimed_at ${intType()},
106
131
  dispatch_pending ${intType()} NOT NULL DEFAULT 0
107
132
  )
@@ -111,6 +136,7 @@ export async function ensureTable() {
111
136
  await ensureTableExists(TABLE, createSql);
112
137
  await ensureColumnExists(TABLE, "claimed_at", `ALTER TABLE ${TABLE} ADD COLUMN IF NOT EXISTS claimed_at ${intType()}`);
113
138
  await ensureColumnExists(TABLE, "dispatch_pending", `ALTER TABLE ${TABLE} ADD COLUMN IF NOT EXISTS dispatch_pending ${intType()} NOT NULL DEFAULT 0`);
139
+ await ensureColumnExists(TABLE, "error_code", `ALTER TABLE ${TABLE} ADD COLUMN IF NOT EXISTS error_code TEXT`);
114
140
  await ensureIndexExists(`idx_${TABLE}_owner_automation`, indexSql);
115
141
  return;
116
142
  }
@@ -121,6 +147,7 @@ export async function ensureTable() {
121
147
  ["claimed_at", `${intType()}`],
122
148
  ["dispatch_pending", `${intType()} NOT NULL DEFAULT 0`],
123
149
  ["app_id", "TEXT"],
150
+ ["error_code", "TEXT"],
124
151
  ]) {
125
152
  if (columns.has(name))
126
153
  continue;
@@ -146,7 +173,7 @@ export async function ensureTable() {
146
173
  function toRun(row, now) {
147
174
  const stored = String(row.status);
148
175
  const startedAt = Number(row.started_at);
149
- const status = stored === "running" && now - startedAt > RUN_LIVENESS_CEILING_MS
176
+ const status = stored === "running" && now - startedAt > resolveRunLivenessCeilingMs()
150
177
  ? "interrupted"
151
178
  : stored;
152
179
  return {
@@ -167,6 +194,11 @@ function toRun(row, now) {
167
194
  : row.error == null
168
195
  ? null
169
196
  : String(row.error),
197
+ errorCode: row.error_code == null && status === "interrupted"
198
+ ? INTERRUPTED_RUN_ERROR_CODE
199
+ : row.error_code == null
200
+ ? null
201
+ : String(row.error_code),
170
202
  };
171
203
  }
172
204
  /**
@@ -212,7 +244,7 @@ export async function claimAutomationRun(id) {
212
244
  const now = Date.now();
213
245
  const result = await getDbExec().execute({
214
246
  sql: `UPDATE ${TABLE} SET claimed_at = ? WHERE id = ? AND dispatch_pending = 1 AND (claimed_at IS NULL OR claimed_at <= ?) AND status = 'running'`,
215
- args: [now, id, now - CLAIM_LEASE_MS],
247
+ args: [now, id, now - claimLeaseMs()],
216
248
  });
217
249
  return Number(result.rowsAffected ?? 0) > 0;
218
250
  }
@@ -234,7 +266,7 @@ export async function listUnclaimedAutomationRuns(options) {
234
266
  AND started_at <= ?
235
267
  ORDER BY started_at ASC LIMIT ${limit}`,
236
268
  args: [
237
- Date.now() - CLAIM_LEASE_MS,
269
+ Date.now() - claimLeaseMs(),
238
270
  ...(appId ? [appId] : []),
239
271
  Date.now() - olderThanMs,
240
272
  ],
@@ -263,19 +295,28 @@ async function pruneAutomationRuns(owner, automation) {
263
295
  args: [owner, automation, owner, automation],
264
296
  });
265
297
  }
266
- export async function finishAutomationRun(id, status, error) {
298
+ export async function finishAutomationRun(id, status, error, errorCode) {
267
299
  await ensureTable();
268
300
  const existing = await getDbExec().execute({
269
- sql: `SELECT owner, automation, path, org_id, run_id, thread_id FROM ${TABLE} WHERE id = ? LIMIT 1`,
301
+ sql: `SELECT owner, automation, path, org_id, run_id, thread_id, started_at FROM ${TABLE} WHERE id = ? LIMIT 1`,
270
302
  args: [id],
271
303
  });
272
304
  const row = existing.rows?.[0];
305
+ const finishedAt = Date.now();
273
306
  await getDbExec().execute({
274
- sql: `UPDATE ${TABLE} SET status = ?, finished_at = ?, error = ? WHERE id = ?`,
275
- args: [status, Date.now(), error?.slice(0, MAX_ERROR_LENGTH) ?? null, id],
307
+ sql: `UPDATE ${TABLE} SET status = ?, finished_at = ?, error = ?, error_code = ? WHERE id = ?`,
308
+ args: [
309
+ status,
310
+ finishedAt,
311
+ error?.slice(0, MAX_ERROR_LENGTH) ?? null,
312
+ errorCode?.slice(0, MAX_ERROR_CODE_LENGTH) ?? null,
313
+ id,
314
+ ],
276
315
  });
277
316
  if (!row)
278
317
  return;
318
+ const rawStartedAt = Number(row.started_at);
319
+ const startedAt = Number.isFinite(rawStartedAt) ? rawStartedAt : null;
279
320
  try {
280
321
  emitBusEvent("automation.run.finished", {
281
322
  automationRunId: id,
@@ -287,6 +328,8 @@ export async function finishAutomationRun(id, status, error) {
287
328
  threadId: row.thread_id == null ? null : String(row.thread_id),
288
329
  status,
289
330
  error: error?.slice(0, MAX_ERROR_LENGTH) ?? null,
331
+ errorCode: errorCode?.slice(0, MAX_ERROR_CODE_LENGTH) ?? null,
332
+ durationMs: startedAt === null ? null : Math.max(0, finishedAt - startedAt),
290
333
  }, { owner: String(row.owner) });
291
334
  }
292
335
  catch (eventError) {
@@ -270,7 +270,7 @@ const messages = {
270
270
  "errorMessages.gatewayNoDetails": "لم تُرجع بوابة النموذج أي تفاصيل عن الخطأ وتعذّر على المحادثة الاسترداد. انتظر قليلًا ثم أعد المحاولة، أو ابدأ محادثة جديدة إذا استمرت المشكلة.",
271
271
  "errorMessages.inactivityTimeout": "انتهت مهلة اتصال الوكيل قبل أن يتمكن من الإكمال. يمكنك المتابعة من العمل الجزئي أو إعادة المحاولة.",
272
272
  "errorMessages.invalidToolSchema": "كان مخطط إحدى الأدوات غير صالح، لذلك رفض النموذج الطلب قبل بدئه. يمكن تخطي الأداة غير الصالحة وإعادة محاولة الطلب.",
273
- "errorMessages.noProviderConnected": "لا يوجد مزوّد LLM متصل. افتح «إدارة الوكيل» «LLM» لهذا التطبيق، ثم اربط Builder.io أو أضف مفتاح مزوّد.",
273
+ "errorMessages.noProviderConnected": "لا يوجد مزوّد LLM متصل. افتح الإعدادات > الوكيل > مزوّدو الذكاء الاصطناعي، ثم اربط Builder.io (تتوفر خطة مجانية) أو أضف مفتاح مزوّد.",
274
274
  "errorMessages.openBuilderSpaceSettings": "فتح إعدادات مساحة Builder",
275
275
  "errorMessages.providerAuthentication": "رفض مزوّد النموذج مفتاح API المحفوظ. حدّث المفتاح من الإعدادات ← عمليات التكامل ← مفاتيح API، ثم أعد المحاولة.",
276
276
  "errorMessages.providerConfiguration": "لا يمكن لهذا النموذج استخدام الأدوات بالإعدادات الحالية. بدّل النموذج من الإعدادات، ثم أعد المحاولة.",
@@ -399,7 +399,7 @@ const messages = {
399
399
  "errorMessages.gatewayNoDetails": "Das Modell-Gateway hat keine Fehlerdetails zurückgegeben und der Chat konnte nicht fortgesetzt werden. Warte einen Moment und versuche es erneut. Falls das Problem bestehen bleibt, starte einen neuen Chat.",
400
400
  "errorMessages.inactivityTimeout": "Die Verbindung zum Agenten wurde wegen Zeitüberschreitung beendet, bevor er fertig war. Du kannst mit dem Teilergebnis fortfahren oder es erneut versuchen.",
401
401
  "errorMessages.invalidToolSchema": "Ein Tool-Schema war ungültig. Deshalb hat das Modell die Anfrage abgelehnt, bevor sie gestartet wurde. Das ungültige Tool kann übersprungen und die Anfrage erneut gesendet werden.",
402
- "errorMessages.noProviderConnected": "Es ist kein LLM-Anbieter verbunden. Öffne für diese App „Agent verwalten“ „LLM“ und verbinde anschließend Builder.io oder füge einen Anbieterschlüssel hinzu.",
402
+ "errorMessages.noProviderConnected": "Es ist kein LLM-Anbieter verbunden. Öffne Einstellungen > Agent > KI-Anbieter und verbinde anschließend Builder.io (kostenloser Tarif verfügbar) oder füge einen Anbieterschlüssel hinzu.",
403
403
  "errorMessages.openBuilderSpaceSettings": "Builder-Space-Einstellungen öffnen",
404
404
  "errorMessages.providerAuthentication": "Der Modellanbieter hat den gespeicherten API-Schlüssel abgelehnt. Aktualisiere ihn unter Einstellungen → Integrationen → API-Schlüssel und versuche es erneut.",
405
405
  "errorMessages.providerConfiguration": "Dieses Modell kann mit den aktuellen Einstellungen keine Tools verwenden. Wechsle in den Einstellungen das Modell und versuche es erneut.",
@@ -262,7 +262,7 @@ declare const messages: {
262
262
  readonly "errorMessages.gatewayNoDetails": "The model gateway returned no error details and the chat couldn't recover. Wait a moment and retry, or start a new chat if it keeps happening.";
263
263
  readonly "errorMessages.inactivityTimeout": "The agent connection timed out before it could finish. You can continue from the partial work or retry.";
264
264
  readonly "errorMessages.invalidToolSchema": "A tool schema was invalid, so the model rejected the request before it started. The invalid tool can be skipped and the request retried.";
265
- readonly "errorMessages.noProviderConnected": "No LLM provider is connected. Open this app's Manage agent > LLM, then connect Builder.io or add a provider key.";
265
+ readonly "errorMessages.noProviderConnected": "No LLM provider is connected. Open Settings > Agent > AI providers, then connect Builder.io (free tier available) or add a provider key.";
266
266
  readonly "errorMessages.openBuilderSpaceSettings": "Open Builder space settings";
267
267
  readonly "errorMessages.providerAuthentication": "The model provider rejected the saved API key. Update the key in Settings → Integrations → API keys, then retry.";
268
268
  readonly "errorMessages.providerConfiguration": "This model can't use tools with the current settings. Switch models in Settings, then retry.";
@@ -262,7 +262,7 @@ const messages = {
262
262
  "errorMessages.gatewayNoDetails": "The model gateway returned no error details and the chat couldn't recover. Wait a moment and retry, or start a new chat if it keeps happening.",
263
263
  "errorMessages.inactivityTimeout": "The agent connection timed out before it could finish. You can continue from the partial work or retry.",
264
264
  "errorMessages.invalidToolSchema": "A tool schema was invalid, so the model rejected the request before it started. The invalid tool can be skipped and the request retried.",
265
- "errorMessages.noProviderConnected": "No LLM provider is connected. Open this app's Manage agent > LLM, then connect Builder.io or add a provider key.",
265
+ "errorMessages.noProviderConnected": "No LLM provider is connected. Open Settings > Agent > AI providers, then connect Builder.io (free tier available) or add a provider key.",
266
266
  "errorMessages.openBuilderSpaceSettings": "Open Builder space settings",
267
267
  "errorMessages.providerAuthentication": "The model provider rejected the saved API key. Update the key in Settings → Integrations → API keys, then retry.",
268
268
  "errorMessages.providerConfiguration": "This model can't use tools with the current settings. Switch models in Settings, then retry.",
@@ -406,7 +406,7 @@ const messages = {
406
406
  "errorMessages.gatewayNoDetails": "La pasarela del modelo no devolvió detalles del error y el chat no pudo recuperarse. Espera un momento y vuelve a intentarlo. Si el problema continúa, inicia un chat nuevo.",
407
407
  "errorMessages.inactivityTimeout": "La conexión del agente expiró antes de que pudiera finalizar. Puedes continuar desde el trabajo parcial o volver a intentarlo.",
408
408
  "errorMessages.invalidToolSchema": "El esquema de una herramienta no era válido, así que el modelo rechazó la solicitud antes de iniciarla. Puedes omitir la herramienta no válida y volver a intentarlo.",
409
- "errorMessages.noProviderConnected": "No hay ningún proveedor de LLM conectado. Abre «Administrar agente» «LLM» para esta aplicación y, a continuación, conecta Builder.io o añade una clave de proveedor.",
409
+ "errorMessages.noProviderConnected": "No hay ningún proveedor de LLM conectado. Abre Configuración > Agente > Proveedores de IA y, a continuación, conecta Builder.io (nivel gratuito disponible) o añade una clave de proveedor.",
410
410
  "errorMessages.openBuilderSpaceSettings": "Abrir los ajustes del espacio de Builder",
411
411
  "errorMessages.providerAuthentication": "El proveedor del modelo rechazó la clave de API guardada. Actualízala en Ajustes → Integraciones → Claves de API y vuelve a intentarlo.",
412
412
  "errorMessages.providerConfiguration": "Este modelo no puede usar herramientas con los ajustes actuales. Cambia de modelo en Ajustes y vuelve a intentarlo.",
@@ -406,7 +406,7 @@ const messages = {
406
406
  "errorMessages.gatewayNoDetails": "La passerelle du modèle n’a fourni aucun détail sur l’erreur et la discussion n’a pas pu reprendre. Patientez un instant et réessayez. Si le problème persiste, démarrez une nouvelle discussion.",
407
407
  "errorMessages.inactivityTimeout": "La connexion à l’agent a expiré avant la fin. Vous pouvez poursuivre à partir du travail partiel ou réessayer.",
408
408
  "errorMessages.invalidToolSchema": "Le schéma d’un outil n’était pas valide. Le modèle a donc rejeté la demande avant son démarrage. Vous pouvez ignorer cet outil et réessayer.",
409
- "errorMessages.noProviderConnected": "Aucun fournisseur de LLM n’est connecté. Ouvrez « Gérer l’agent » « LLM » pour cette application, puis connectez Builder.io ou ajoutez une clé de fournisseur.",
409
+ "errorMessages.noProviderConnected": "Aucun fournisseur de LLM n’est connecté. Ouvrez Paramètres > Agent > Fournisseurs d’IA, puis connectez Builder.io (offre gratuite disponible) ou ajoutez une clé de fournisseur.",
410
410
  "errorMessages.openBuilderSpaceSettings": "Ouvrir les paramètres de l’espace Builder",
411
411
  "errorMessages.providerAuthentication": "Le fournisseur du modèle a rejeté la clé API enregistrée. Mettez-la à jour dans Paramètres → Intégrations → Clés API, puis réessayez.",
412
412
  "errorMessages.providerConfiguration": "Ce modèle ne peut pas utiliser d’outils avec les paramètres actuels. Changez de modèle dans les paramètres, puis réessayez.",
@@ -262,7 +262,7 @@ const messages = {
262
262
  "errorMessages.gatewayNoDetails": "मॉडल गेटवे ने त्रुटि का कोई विवरण नहीं दिया और चैट रिकवर नहीं हो सकी। कुछ देर रुककर फिर से प्रयास करें या समस्या बनी रहने पर नई चैट शुरू करें।",
263
263
  "errorMessages.inactivityTimeout": "एजेंट का कनेक्शन काम पूरा होने से पहले समय सीमा पर पहुँच गया। आप आंशिक काम से जारी रख सकते हैं या फिर से प्रयास कर सकते हैं।",
264
264
  "errorMessages.invalidToolSchema": "एक टूल स्कीमा अमान्य था, इसलिए मॉडल ने अनुरोध शुरू होने से पहले ही अस्वीकार कर दिया। अमान्य टूल को छोड़कर अनुरोध दोबारा किया जा सकता है।",
265
- "errorMessages.noProviderConnected": "कोई LLM प्रदाता कनेक्ट नहीं है। इस ऐप में “एजेंट प्रबंधित करें” → “LLM” खोलें, फिर Builder.io कनेक्ट करें या प्रदाता कुंजी जोड़ें।",
265
+ "errorMessages.noProviderConnected": "कोई LLM प्रदाता कनेक्ट नहीं है। सेटिंग्स > एजेंट > AI प्रदाता खोलें, फिर Builder.io कनेक्ट करें (मुफ़्त स्तर उपलब्ध है) या प्रदाता कुंजी जोड़ें।",
266
266
  "errorMessages.openBuilderSpaceSettings": "Builder स्पेस सेटिंग्स खोलें",
267
267
  "errorMessages.providerAuthentication": "मॉडल प्रदाता ने सहेजी गई API कुंजी अस्वीकार कर दी। सेटिंग्स → इंटीग्रेशन → API कुंजियाँ में कुंजी अपडेट करें, फिर से प्रयास करें।",
268
268
  "errorMessages.providerConfiguration": "यह मॉडल मौजूदा सेटिंग्स के साथ टूल का उपयोग नहीं कर सकता। सेटिंग्स में मॉडल बदलें, फिर से प्रयास करें।",
@@ -260,7 +260,7 @@ const messages = {
260
260
  "errorMessages.gatewayNoDetails": "モデルゲートウェイからエラーの詳細が返されず、チャットを復旧できませんでした。少し待ってから再試行し、繰り返し発生する場合は新しいチャットを開始してください。",
261
261
  "errorMessages.inactivityTimeout": "エージェントとの接続が完了前にタイムアウトしました。途中までの作業から続行するか、再試行できます。",
262
262
  "errorMessages.invalidToolSchema": "ツールのスキーマが無効だったため、モデルは開始前にリクエストを拒否しました。無効なツールをスキップして再試行できます。",
263
- "errorMessages.noProviderConnected": "LLM プロバイダーが接続されていません。このアプリの「エージェントを管理」→「LLM」を開き、Builder.io を接続するか、プロバイダーキーを追加してください。",
263
+ "errorMessages.noProviderConnected": "LLM プロバイダーが接続されていません。設定 > エージェント > AI プロバイダーを開き、Builder.io(無料プランあり)に接続するか、プロバイダーキーを追加してください。",
264
264
  "errorMessages.openBuilderSpaceSettings": "Builder スペース設定を開く",
265
265
  "errorMessages.providerAuthentication": "モデルプロバイダーが保存済みの API キーを拒否しました。設定 → 連携 → API キーでキーを更新してから再試行してください。",
266
266
  "errorMessages.providerConfiguration": "現在の設定では、このモデルはツールを使用できません。設定でモデルを切り替えてから再試行してください。",
@@ -260,7 +260,7 @@ const messages = {
260
260
  "errorMessages.gatewayNoDetails": "모델 게이트웨이가 오류 세부 정보를 반환하지 않아 채팅을 복구할 수 없습니다. 잠시 후 다시 시도하고, 문제가 계속되면 새 채팅을 시작하세요.",
261
261
  "errorMessages.inactivityTimeout": "에이전트 연결이 완료 전에 시간 초과되었습니다. 부분적으로 완료된 작업에서 계속하거나 다시 시도할 수 있습니다.",
262
262
  "errorMessages.invalidToolSchema": "도구 스키마가 올바르지 않아 모델이 요청 시작 전에 거부했습니다. 올바르지 않은 도구를 건너뛰고 요청을 다시 시도할 수 있습니다.",
263
- "errorMessages.noProviderConnected": "연결된 LLM 제공업체가 없습니다. 앱의 ‘에이전트 관리’ ‘LLM’을 연 다음 Builder.io를 연결하거나 제공업체 키를 추가하세요.",
263
+ "errorMessages.noProviderConnected": "연결된 LLM 제공업체가 없습니다. 설정 > 에이전트 > AI 제공업체를 연 다음 Builder.io(무료 요금제 제공)를 연결하거나 제공업체 키를 추가하세요.",
264
264
  "errorMessages.openBuilderSpaceSettings": "Builder 스페이스 설정 열기",
265
265
  "errorMessages.providerAuthentication": "모델 제공업체가 저장된 API 키를 거부했습니다. 설정 → 연동 → API 키에서 키를 업데이트한 후 다시 시도하세요.",
266
266
  "errorMessages.providerConfiguration": "현재 설정에서는 이 모델이 도구를 사용할 수 없습니다. 설정에서 모델을 변경한 후 다시 시도하세요.",
@@ -406,7 +406,7 @@ const messages = {
406
406
  "errorMessages.gatewayNoDetails": "O gateway do modelo não retornou detalhes do erro, e o chat não pôde ser recuperado. Aguarde um momento e tente novamente. Se o problema persistir, inicie um novo chat.",
407
407
  "errorMessages.inactivityTimeout": "A conexão com o agente expirou antes da conclusão. Você pode continuar a partir do trabalho parcial ou tentar novamente.",
408
408
  "errorMessages.invalidToolSchema": "O esquema de uma ferramenta era inválido, então o modelo rejeitou a solicitação antes de iniciá-la. Você pode ignorar a ferramenta inválida e tentar novamente.",
409
- "errorMessages.noProviderConnected": "Nenhum provedor de LLM está conectado. Abra “Gerenciar agente” “LLM” para este app e conecte o Builder.io ou adicione uma chave de provedor.",
409
+ "errorMessages.noProviderConnected": "Nenhum provedor de LLM está conectado. Abra Configurações > Agente > Provedores de IA e conecte o Builder.io (nível gratuito disponível) ou adicione uma chave de provedor.",
410
410
  "errorMessages.openBuilderSpaceSettings": "Abrir as configurações do espaço do Builder",
411
411
  "errorMessages.providerAuthentication": "O provedor do modelo rejeitou a chave de API salva. Atualize-a em Configurações → Integrações → Chaves de API e tente novamente.",
412
412
  "errorMessages.providerConfiguration": "Este modelo não pode usar ferramentas com as configurações atuais. Troque de modelo em Configurações e tente novamente.",
@@ -260,7 +260,7 @@ const messages = {
260
260
  "errorMessages.gatewayNoDetails": "模型网关未返回错误详情,聊天无法恢复。请稍等片刻后重试;如果问题持续出现,请开始新聊天。",
261
261
  "errorMessages.inactivityTimeout": "智能体连接在完成前超时。您可以从已完成的部分继续,也可以重试。",
262
262
  "errorMessages.invalidToolSchema": "工具架构无效,因此模型在请求开始前拒绝了该请求。可以跳过无效工具并重试请求。",
263
- "errorMessages.noProviderConnected": "尚未连接任何 LLM 提供商。打开此应用的“管理智能体”→“LLM”,然后连接 Builder.io 或添加提供商密钥。",
263
+ "errorMessages.noProviderConnected": "尚未连接任何 LLM 提供商。打开设置 > 智能体 > AI 提供商,然后连接 Builder.io(提供免费套餐)或添加提供商密钥。",
264
264
  "errorMessages.openBuilderSpaceSettings": "打开 Builder 空间设置",
265
265
  "errorMessages.providerAuthentication": "模型提供商拒绝了已保存的 API 密钥。请在设置 → 集成 → API 密钥中更新密钥,然后重试。",
266
266
  "errorMessages.providerConfiguration": "此模型无法在当前设置下使用工具。请在设置中切换模型,然后重试。",
@@ -260,7 +260,7 @@ const messages = {
260
260
  "errorMessages.gatewayNoDetails": "模型閘道未傳回錯誤詳細資訊,聊天無法復原。請稍候再試;如果問題持續發生,請開始新聊天。",
261
261
  "errorMessages.inactivityTimeout": "代理連線在完成前逾時。您可以從已完成的部分繼續,也可以重試。",
262
262
  "errorMessages.invalidToolSchema": "工具結構描述無效,因此模型在要求開始前便拒絕了要求。您可以略過無效工具並重試要求。",
263
- "errorMessages.noProviderConnected": "尚未連接任何 LLM 提供商。開啟此應用程式的「管理代理程式」→「LLM」,然後連接 Builder.io 或新增提供商金鑰。",
263
+ "errorMessages.noProviderConnected": "尚未連接任何 LLM 提供商。開啟設定 > 代理程式 > AI 提供商,然後連接 Builder.io(提供免費方案)或新增提供商金鑰。",
264
264
  "errorMessages.openBuilderSpaceSettings": "開啟 Builder 空間設定",
265
265
  "errorMessages.providerAuthentication": "模型供應商拒絕了已儲存的 API 金鑰。請在設定 → 整合 → API 金鑰中更新金鑰,然後重試。",
266
266
  "errorMessages.providerConfiguration": "這個模型無法在目前設定下使用工具。請在設定中切換模型,然後重試。",
@@ -264,7 +264,7 @@ export declare const englishAgentChatMessages: {
264
264
  readonly "errorMessages.gatewayNoDetails": "The model gateway returned no error details and the chat couldn't recover. Wait a moment and retry, or start a new chat if it keeps happening.";
265
265
  readonly "errorMessages.inactivityTimeout": "The agent connection timed out before it could finish. You can continue from the partial work or retry.";
266
266
  readonly "errorMessages.invalidToolSchema": "A tool schema was invalid, so the model rejected the request before it started. The invalid tool can be skipped and the request retried.";
267
- readonly "errorMessages.noProviderConnected": "No LLM provider is connected. Open this app's Manage agent > LLM, then connect Builder.io or add a provider key.";
267
+ readonly "errorMessages.noProviderConnected": "No LLM provider is connected. Open Settings > Agent > AI providers, then connect Builder.io (free tier available) or add a provider key.";
268
268
  readonly "errorMessages.openBuilderSpaceSettings": "Open Builder space settings";
269
269
  readonly "errorMessages.providerAuthentication": "The model provider rejected the saved API key. Update the key in Settings → Integrations → API keys, then retry.";
270
270
  readonly "errorMessages.providerConfiguration": "This model can't use tools with the current settings. Switch models in Settings, then retry.";
@@ -28,6 +28,6 @@ export declare function createNotificationsHandler(): import("h3").EventHandlerW
28
28
  } | {
29
29
  count?: undefined;
30
30
  updated?: undefined;
31
- error?: undefined;
32
31
  ok: boolean;
32
+ error?: undefined;
33
33
  }>>;
@@ -41,16 +41,16 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
41
41
  thumbsUpRate: number;
42
42
  avgEvalScore: number;
43
43
  } | {
44
- error?: undefined;
45
44
  summary: import("./types.js").TraceSummary;
46
45
  spans: import("./types.js").TraceSpan[];
47
46
  id?: undefined;
47
+ error?: undefined;
48
48
  ok?: undefined;
49
49
  } | {
50
- error?: undefined;
51
50
  summary?: undefined;
52
51
  spans?: undefined;
53
52
  id: string;
53
+ error?: undefined;
54
54
  ok?: undefined;
55
55
  } | {
56
56
  summary?: undefined;
@@ -59,9 +59,9 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
59
59
  error: any;
60
60
  ok?: undefined;
61
61
  } | {
62
- error?: undefined;
63
62
  summary?: undefined;
64
63
  spans?: undefined;
65
64
  id?: undefined;
65
+ error?: undefined;
66
66
  ok: boolean;
67
67
  }>>;