@agent-native/core 0.157.8 → 0.157.10

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 (154) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/actions/purge-first-party-analytics-postgres.ts +26 -8
  3. package/corpus/templates/analytics/scripts/first-party-analytics-postgres-retention-dry-run.sql +261 -0
  4. package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +3 -1
  5. package/corpus/templates/analytics/server/lib/first-party-analytics-purge.ts +75 -31
  6. package/corpus/templates/analytics/server/lib/workspace-feature-flags.ts +4 -1
  7. package/corpus/templates/analytics/server/plugins/db.ts +139 -0
  8. package/corpus/templates/slides/AGENTS.md +14 -10
  9. package/corpus/templates/slides/actions/export-pptx.ts +1311 -121
  10. package/corpus/templates/slides/actions/import-file.ts +28 -1
  11. package/corpus/templates/slides/actions/import-pptx.ts +69 -42
  12. package/corpus/templates/slides/app/components/deck/SlideRenderer.tsx +222 -29
  13. package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +71 -0
  14. package/corpus/templates/slides/app/global.css +19 -1
  15. package/corpus/templates/slides/app/lib/export-google-slides-client.ts +44 -5
  16. package/corpus/templates/slides/app/lib/export-pdf-client.ts +13 -6
  17. package/corpus/templates/slides/app/lib/export-pptx-client.ts +601 -30
  18. package/corpus/templates/slides/app/pages/DeckEditor.tsx +18 -1
  19. package/corpus/templates/slides/server/handlers/import/html-converter.ts +1240 -22
  20. package/corpus/templates/slides/server/handlers/import/pdf-fidelity-parser.ts +329 -64
  21. package/corpus/templates/slides/server/handlers/import/pptx-assets.ts +24 -0
  22. package/corpus/templates/slides/server/lib/source-import.ts +10 -1
  23. package/corpus/templates/slides/shared/aspect-ratios.ts +13 -1
  24. package/dist/agent/harness/migrations.d.ts +4 -0
  25. package/dist/agent/harness/migrations.js +60 -0
  26. package/dist/agent/production-agent.d.ts +2 -0
  27. package/dist/agent/production-agent.js +15 -0
  28. package/dist/agent/run-loop-with-resume.d.ts +1 -0
  29. package/dist/agent/run-loop-with-resume.js +44 -12
  30. package/dist/agent/run-migrations.d.ts +8 -0
  31. package/dist/agent/run-migrations.js +61 -0
  32. package/dist/automations/service.d.ts +6 -0
  33. package/dist/automations/service.js +35 -0
  34. package/dist/chat-threads/migrations.d.ts +1 -0
  35. package/dist/chat-threads/migrations.js +1 -0
  36. package/dist/chat-threads/schema-migrations.d.ts +8 -0
  37. package/dist/chat-threads/schema-migrations.js +60 -0
  38. package/dist/cli/code-agent-connector.d.ts +4 -1
  39. package/dist/cli/code-agent-connector.js +154 -23
  40. package/dist/cli/code-agent-executor.js +255 -13
  41. package/dist/cli/code-agent-runs.d.ts +1 -0
  42. package/dist/cli/code-agent-runs.js +2 -1
  43. package/dist/cli/portal-workspace.d.ts +49 -0
  44. package/dist/cli/portal-workspace.js +320 -0
  45. package/dist/client/AssistantChat.d.ts +1 -1
  46. package/dist/client/AssistantChat.js +43 -61
  47. package/dist/client/ConnectBuilderCard.js +33 -5
  48. package/dist/client/agent-chat-adapter.js +4 -0
  49. package/dist/client/analytics-platform.d.ts +2 -0
  50. package/dist/client/analytics-platform.js +12 -0
  51. package/dist/client/analytics.d.ts +3 -0
  52. package/dist/client/analytics.js +10 -1
  53. package/dist/client/chat/index.d.ts +1 -0
  54. package/dist/client/chat/index.js +1 -0
  55. package/dist/client/chat/message-components.d.ts +2 -0
  56. package/dist/client/chat/message-components.js +11 -1
  57. package/dist/client/chat/run-recovery.d.ts +2 -1
  58. package/dist/client/chat/run-recovery.js +2 -2
  59. package/dist/client/chat-first/apps-rail.d.ts +1 -1
  60. package/dist/client/chat-first/apps-rail.js +11 -11
  61. package/dist/client/chat-first/surface-tabs.d.ts +1 -1
  62. package/dist/client/chat-first/surface-tabs.js +38 -36
  63. package/dist/client/chat-first/types.d.ts +5 -0
  64. package/dist/client/chat-first.d.ts +1 -1
  65. package/dist/client/chat-first.js +4 -4
  66. package/dist/client/desktop-local-code-change.d.ts +10 -0
  67. package/dist/client/desktop-local-code-change.js +16 -0
  68. package/dist/client/index.d.ts +1 -0
  69. package/dist/client/index.js +1 -0
  70. package/dist/client/observability/ThumbsFeedback.js +53 -26
  71. package/dist/client/observability/useObservability.d.ts +1 -1
  72. package/dist/client/observability/useObservability.js +6 -3
  73. package/dist/client/terminal/AgentTerminal.d.ts +9 -1
  74. package/dist/client/terminal/AgentTerminal.js +38 -3
  75. package/dist/client/terminal/index.d.ts +1 -1
  76. package/dist/client/terminal/index.js +1 -1
  77. package/dist/client/track.js +9 -1
  78. package/dist/client/use-action.js +3 -0
  79. package/dist/collab/awareness.d.ts +2 -2
  80. package/dist/deploy/build.js +1 -1
  81. package/dist/feature-flags/a2a-action-route.js +4 -2
  82. package/dist/feature-flags/permissions.d.ts +2 -0
  83. package/dist/feature-flags/permissions.js +6 -1
  84. package/dist/ingestion/pptx.d.ts +92 -1
  85. package/dist/ingestion/pptx.js +1180 -115
  86. package/dist/integrations/index.d.ts +3 -3
  87. package/dist/integrations/index.js +2 -2
  88. package/dist/integrations/plugin.js +95 -6
  89. package/dist/integrations/remote-commands-store.d.ts +5 -0
  90. package/dist/integrations/remote-commands-store.js +63 -22
  91. package/dist/integrations/remote-devices-store.d.ts +2 -1
  92. package/dist/integrations/remote-devices-store.js +46 -0
  93. package/dist/integrations/remote-types.d.ts +20 -0
  94. package/dist/jobs/actions/list-recurring-jobs.d.ts +3 -0
  95. package/dist/jobs/actions/list-recurring-jobs.js +5 -1
  96. package/dist/jobs/frontmatter.d.ts +16 -0
  97. package/dist/jobs/frontmatter.js +53 -0
  98. package/dist/jobs/remote-execution.d.ts +42 -0
  99. package/dist/jobs/remote-execution.js +287 -0
  100. package/dist/jobs/scheduler.js +102 -3
  101. package/dist/jobs/tools.js +50 -3
  102. package/dist/localization/core-messages/ar-SA.js +4 -0
  103. package/dist/localization/core-messages/de-DE.js +4 -0
  104. package/dist/localization/core-messages/en-US.d.ts +4 -0
  105. package/dist/localization/core-messages/en-US.js +4 -0
  106. package/dist/localization/core-messages/es-ES.js +4 -0
  107. package/dist/localization/core-messages/fr-FR.js +4 -0
  108. package/dist/localization/core-messages/hi-IN.js +4 -0
  109. package/dist/localization/core-messages/ja-JP.js +4 -0
  110. package/dist/localization/core-messages/ko-KR.js +4 -0
  111. package/dist/localization/core-messages/pt-BR.js +4 -0
  112. package/dist/localization/core-messages/zh-CN.js +4 -0
  113. package/dist/localization/core-messages/zh-TW.js +4 -0
  114. package/dist/localization/core-messages.d.ts +4 -0
  115. package/dist/notifications/routes.d.ts +3 -3
  116. package/dist/observability/feedback.js +40 -16
  117. package/dist/observability/routes.d.ts +6 -6
  118. package/dist/observability/routes.js +15 -3
  119. package/dist/observability/store.js +2 -0
  120. package/dist/observability/traces.js +5 -1
  121. package/dist/provider-api/actions/custom-provider-registration.d.ts +16 -16
  122. package/dist/provider-api/actions/provider-api.d.ts +13 -13
  123. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  124. package/dist/resources/handlers.d.ts +1 -1
  125. package/dist/secrets/routes.d.ts +9 -9
  126. package/dist/server/action-routes.d.ts +7 -5
  127. package/dist/server/action-routes.js +13 -6
  128. package/dist/server/agent-chat-plugin.js +6 -0
  129. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  130. package/dist/server/agent-run-context.d.ts +1 -0
  131. package/dist/server/agent-run-context.js +9 -0
  132. package/dist/server/better-auth-instance.js +4 -0
  133. package/dist/server/core-routes-plugin.js +8 -1
  134. package/dist/server/realtime-voice.js +2 -1
  135. package/dist/server/release-migrations.js +16 -0
  136. package/dist/server/request-context.d.ts +2 -0
  137. package/dist/server/transcribe-voice.d.ts +1 -1
  138. package/dist/shared/analytics-platform.d.ts +6 -0
  139. package/dist/shared/analytics-platform.js +14 -0
  140. package/dist/shared/mcp-embed-headers.d.ts +1 -1
  141. package/dist/shared/mcp-embed-headers.js +1 -1
  142. package/dist/styles/agent-native.css +83 -103
  143. package/dist/terminal/cli-registry.js +5 -0
  144. package/dist/tracking/registry.js +9 -1
  145. package/dist/triggers/actions/list-automations.d.ts +3 -0
  146. package/dist/triggers/actions/list-automations.js +3 -0
  147. package/dist/triggers/actions/manage-automation.d.ts +9 -0
  148. package/dist/triggers/actions/manage-automation.js +14 -2
  149. package/dist/triggers/actions.js +82 -7
  150. package/dist/usage/migrations.d.ts +4 -0
  151. package/dist/usage/migrations.js +45 -0
  152. package/docs/content/template-content-developers.mdx +48 -0
  153. package/docs/content/toolkit-ui.mdx +57 -0
  154. package/package.json +3 -3
@@ -263,10 +263,14 @@ const messages = {
263
263
  "errorMessages.startNewChat": "새 채팅 시작",
264
264
  "errorMessages.upgradeAtBuilder": "Builder.io에서 업그레이드",
265
265
  "feedback.inaccurate": "부정확함",
266
+ "feedback.keyboardHint": "보내려면 {{shortcut}} Enter",
266
267
  "feedback.notHelpful": "도움이 되지 않음",
268
+ "feedback.placeholder": "무엇이 문제였는지 알려주세요…",
269
+ "feedback.submit": "제출",
267
270
  "feedback.thumbsDown": "싫어요",
268
271
  "feedback.thumbsUp": "좋아요",
269
272
  "feedback.tooSlow": "너무 느림",
273
+ "feedback.whatWentWrong": "무엇이 잘못되었나요?",
270
274
  "feedback.wrongTool": "잘못된 도구",
271
275
  "header.switchToCli": "CLI로 전환",
272
276
  "history.active": "활성",
@@ -408,10 +408,14 @@ const messages = {
408
408
  "errorMessages.startNewChat": "Iniciar novo chat",
409
409
  "errorMessages.upgradeAtBuilder": "Fazer upgrade no Builder.io",
410
410
  "feedback.inaccurate": "Impreciso",
411
+ "feedback.keyboardHint": "{{shortcut}} Enter para enviar",
411
412
  "feedback.notHelpful": "Pouco útil",
413
+ "feedback.placeholder": "Conte-nos o que deu errado...",
414
+ "feedback.submit": "Enviar",
412
415
  "feedback.thumbsDown": "Não gostei",
413
416
  "feedback.thumbsUp": "Gostei",
414
417
  "feedback.tooSlow": "Muito lento",
418
+ "feedback.whatWentWrong": "O que deu errado?",
415
419
  "feedback.wrongTool": "Ferramenta errada",
416
420
  "contextMeter.ariaLabel": "Contexto {{percent}}%, {{totalTokens}}{{breakdown}}. Abrir a análise de contexto.",
417
421
  "contextMeter.breakdown": " no total: {{systemTokens}} do sistema + {{conversationTokens}} da conversa",
@@ -263,10 +263,14 @@ const messages = {
263
263
  "errorMessages.startNewChat": "开始新聊天",
264
264
  "errorMessages.upgradeAtBuilder": "在 Builder.io 升级",
265
265
  "feedback.inaccurate": "不准确",
266
+ "feedback.keyboardHint": "{{shortcut}} Enter 发送",
266
267
  "feedback.notHelpful": "没有帮助",
268
+ "feedback.placeholder": "告诉我们哪里出了问题……",
269
+ "feedback.submit": "提交",
267
270
  "feedback.thumbsDown": "不喜欢",
268
271
  "feedback.thumbsUp": "喜欢",
269
272
  "feedback.tooSlow": "太慢",
273
+ "feedback.whatWentWrong": "哪里出了问题?",
270
274
  "feedback.wrongTool": "工具错误",
271
275
  "header.switchToCli": "切换到 CLI",
272
276
  "history.active": "活跃",
@@ -263,10 +263,14 @@ const messages = {
263
263
  "errorMessages.startNewChat": "開始新聊天",
264
264
  "errorMessages.upgradeAtBuilder": "在 Builder.io 升級",
265
265
  "feedback.inaccurate": "不準確",
266
+ "feedback.keyboardHint": "{{shortcut}} Enter 送出",
266
267
  "feedback.notHelpful": "沒有幫助",
268
+ "feedback.placeholder": "告訴我們哪裡出了問題……",
269
+ "feedback.submit": "送出",
267
270
  "feedback.thumbsDown": "不喜歡",
268
271
  "feedback.thumbsUp": "喜歡",
269
272
  "feedback.tooSlow": "太慢",
273
+ "feedback.whatWentWrong": "哪裡出了問題?",
270
274
  "feedback.wrongTool": "工具錯誤",
271
275
  "header.switchToCli": "切換到 CLI",
272
276
  "history.active": "使用中",
@@ -267,10 +267,14 @@ export declare const englishAgentChatMessages: {
267
267
  readonly "errorMessages.startNewChat": "Start new chat";
268
268
  readonly "errorMessages.upgradeAtBuilder": "Upgrade at Builder.io";
269
269
  readonly "feedback.inaccurate": "Inaccurate";
270
+ readonly "feedback.keyboardHint": "{{shortcut}} Enter to send";
270
271
  readonly "feedback.notHelpful": "Not helpful";
272
+ readonly "feedback.placeholder": "Tell us what went wrong...";
273
+ readonly "feedback.submit": "Submit";
271
274
  readonly "feedback.thumbsDown": "Thumbs down";
272
275
  readonly "feedback.thumbsUp": "Thumbs up";
273
276
  readonly "feedback.tooSlow": "Too slow";
277
+ readonly "feedback.whatWentWrong": "What went wrong?";
274
278
  readonly "feedback.wrongTool": "Wrong tool";
275
279
  readonly "header.switchToCli": "Switch to CLI";
276
280
  readonly "history.active": "Active";
@@ -16,18 +16,18 @@ export declare function createNotificationsHandler(): import("h3").EventHandlerW
16
16
  error?: undefined;
17
17
  ok?: undefined;
18
18
  } | {
19
- count?: undefined;
20
19
  updated: number;
21
20
  error?: undefined;
22
21
  ok?: undefined;
23
- } | {
24
22
  count?: undefined;
23
+ } | {
25
24
  updated?: undefined;
26
25
  error: string;
27
26
  ok?: undefined;
28
- } | {
29
27
  count?: undefined;
28
+ } | {
30
29
  updated?: undefined;
31
30
  error?: undefined;
32
31
  ok: boolean;
32
+ count?: undefined;
33
33
  }>>;
@@ -30,6 +30,16 @@ export async function submitFeedback(opts) {
30
30
  await insertFeedback(entry);
31
31
  return entry;
32
32
  }
33
+ function unwrapPersistedThreadMessage(value) {
34
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
35
+ return null;
36
+ }
37
+ const record = value;
38
+ const nested = record.message;
39
+ return nested && typeof nested === "object" && !Array.isArray(nested)
40
+ ? nested
41
+ : record;
42
+ }
33
43
  async function getThreadMessages(threadId) {
34
44
  await ensureObservabilityTables();
35
45
  const client = getDbExec();
@@ -47,22 +57,36 @@ async function getThreadMessages(threadId) {
47
57
  const messages = data.messages ?? data;
48
58
  if (!Array.isArray(messages))
49
59
  return [];
50
- return messages
51
- .filter((m) => m &&
52
- typeof m.role === "string" &&
53
- (typeof m.content === "string" ||
54
- (Array.isArray(m.content) &&
55
- m.content.some((p) => p.type === "text"))))
56
- .map((m) => ({
57
- role: m.role,
58
- content: typeof m.content === "string"
59
- ? m.content
60
- : m.content
61
- .filter((p) => p.type === "text")
62
- .map((p) => p.text ?? "")
63
- .join(""),
64
- createdAt: m.createdAt ? Number(m.createdAt) : undefined,
65
- }));
60
+ return messages.flatMap((value) => {
61
+ const message = unwrapPersistedThreadMessage(value);
62
+ if (!message)
63
+ return [];
64
+ const content = message.content;
65
+ if ((message.role !== "user" && message.role !== "assistant") ||
66
+ (typeof content !== "string" && !Array.isArray(content))) {
67
+ return [];
68
+ }
69
+ const text = typeof content === "string"
70
+ ? content
71
+ : content
72
+ .filter((part) => Boolean(part) &&
73
+ typeof part === "object" &&
74
+ !Array.isArray(part) &&
75
+ part.type === "text")
76
+ .map((part) => (typeof part.text === "string" ? part.text : ""))
77
+ .join("");
78
+ if (!text)
79
+ return [];
80
+ return [
81
+ {
82
+ role: message.role,
83
+ content: text,
84
+ createdAt: typeof message.createdAt === "number"
85
+ ? message.createdAt
86
+ : undefined,
87
+ },
88
+ ];
89
+ });
66
90
  }
67
91
  catch {
68
92
  return [];
@@ -8,7 +8,7 @@
8
8
  * GET /traces/:runId — get trace detail (spans + summary)
9
9
  * GET /traces/:runId/evals — get evals for a run
10
10
  * POST /feedback — submit feedback
11
- * GET /feedback?since=N&limit=N — list feedback entries
11
+ * GET /feedback?since=N&limit=N&feedbackType=text — list feedback entries
12
12
  * GET /feedback/stats?since=N — feedback aggregation stats
13
13
  * GET /satisfaction?since=N — satisfaction scores
14
14
  * GET /evals/stats?since=N — eval stats
@@ -41,27 +41,27 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
41
41
  thumbsUpRate: number;
42
42
  avgEvalScore: number;
43
43
  } | {
44
+ error?: undefined;
45
+ ok?: undefined;
44
46
  summary: import("./types.js").TraceSummary;
45
47
  spans: import("./types.js").TraceSpan[];
46
48
  id?: undefined;
49
+ } | {
47
50
  error?: undefined;
48
51
  ok?: undefined;
49
- } | {
50
52
  summary?: undefined;
51
53
  spans?: undefined;
52
54
  id: string;
53
- error?: undefined;
54
- ok?: undefined;
55
55
  } | {
56
+ ok?: undefined;
56
57
  summary?: undefined;
57
58
  spans?: undefined;
58
59
  id?: undefined;
59
60
  error: any;
60
- ok?: undefined;
61
61
  } | {
62
+ error?: undefined;
62
63
  summary?: undefined;
63
64
  spans?: undefined;
64
65
  id?: undefined;
65
- error?: undefined;
66
66
  ok: boolean;
67
67
  }>>;
@@ -8,7 +8,7 @@
8
8
  * GET /traces/:runId — get trace detail (spans + summary)
9
9
  * GET /traces/:runId/evals — get evals for a run
10
10
  * POST /feedback — submit feedback
11
- * GET /feedback?since=N&limit=N — list feedback entries
11
+ * GET /feedback?since=N&limit=N&feedbackType=text — list feedback entries
12
12
  * GET /feedback/stats?since=N — feedback aggregation stats
13
13
  * GET /satisfaction?since=N — satisfaction scores
14
14
  * GET /evals/stats?since=N — eval stats
@@ -27,6 +27,16 @@ import { track } from "../tracking/registry.js";
27
27
  import { emitAiFeedbackSurveyEvent } from "./posthog-ai.js";
28
28
  import { getObservabilityOverview, getTraceSummaries, getTraceSummary, getTraceSpansForRun, getEvalsForRun, insertFeedback, getFeedback, getFeedbackStats, getSatisfactionScores, getEvalStats, listExperiments, insertExperiment, getExperiment, updateExperiment, getExperimentResults, } from "./store.js";
29
29
  import { trackingIdentityProperties } from "./tracking-identity.js";
30
+ const FEEDBACK_TYPES = [
31
+ "thumbs_up",
32
+ "thumbs_down",
33
+ "category",
34
+ "text",
35
+ ];
36
+ function isFeedbackType(value) {
37
+ return (typeof value === "string" &&
38
+ FEEDBACK_TYPES.includes(value));
39
+ }
30
40
  function nanoid(size = 21) {
31
41
  const alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
32
42
  let id = "";
@@ -143,8 +153,7 @@ export function createObservabilityHandler() {
143
153
  return { error: "Invalid JSON body" };
144
154
  }
145
155
  const feedbackType = body?.feedbackType;
146
- if (!feedbackType ||
147
- !["thumbs_up", "thumbs_down", "category", "text"].includes(feedbackType)) {
156
+ if (!isFeedbackType(feedbackType)) {
148
157
  setResponseStatus(event, 400);
149
158
  return { error: "feedbackType is required" };
150
159
  }
@@ -229,6 +238,9 @@ export function createObservabilityHandler() {
229
238
  return getFeedback({
230
239
  sinceMs: parseSince(q),
231
240
  limit: parseLimit(q),
241
+ feedbackType: isFeedbackType(q.feedbackType)
242
+ ? q.feedbackType
243
+ : undefined,
232
244
  userId: owner,
233
245
  });
234
246
  }
@@ -197,6 +197,7 @@ export async function ensureObservabilityTables() {
197
197
  await ensureIndexExists("idx_feedback_thread", `CREATE INDEX IF NOT EXISTS idx_feedback_thread ON agent_feedback (thread_id)`);
198
198
  await ensureIndexExists("idx_feedback_created", `CREATE INDEX IF NOT EXISTS idx_feedback_created ON agent_feedback (created_at)`);
199
199
  await ensureIndexExists("idx_feedback_user", `CREATE INDEX IF NOT EXISTS idx_feedback_user ON agent_feedback (user_id, created_at)`);
200
+ await ensureIndexExists("idx_feedback_type_created", `CREATE INDEX IF NOT EXISTS idx_feedback_type_created ON agent_feedback (feedback_type, created_at)`);
200
201
  await ensureIndexExists("idx_satisfaction_thread", `CREATE INDEX IF NOT EXISTS idx_satisfaction_thread ON agent_satisfaction_scores (thread_id)`);
201
202
  await ensureIndexExists("idx_satisfaction_user", `CREATE INDEX IF NOT EXISTS idx_satisfaction_user ON agent_satisfaction_scores (user_id, computed_at)`);
202
203
  await ensureIndexExists("idx_evals_run", `CREATE INDEX IF NOT EXISTS idx_evals_run ON agent_evals (run_id)`);
@@ -252,6 +253,7 @@ export async function ensureObservabilityTables() {
252
253
  `CREATE INDEX IF NOT EXISTS idx_feedback_thread ON agent_feedback (thread_id)`,
253
254
  `CREATE INDEX IF NOT EXISTS idx_feedback_created ON agent_feedback (created_at)`,
254
255
  `CREATE INDEX IF NOT EXISTS idx_feedback_user ON agent_feedback (user_id, created_at)`,
256
+ `CREATE INDEX IF NOT EXISTS idx_feedback_type_created ON agent_feedback (feedback_type, created_at)`,
255
257
  `CREATE INDEX IF NOT EXISTS idx_satisfaction_thread ON agent_satisfaction_scores (thread_id)`,
256
258
  `CREATE INDEX IF NOT EXISTS idx_satisfaction_user ON agent_satisfaction_scores (user_id, computed_at)`,
257
259
  `CREATE INDEX IF NOT EXISTS idx_evals_run ON agent_evals (run_id)`,
@@ -637,7 +637,11 @@ export async function instrumentAgentLoop(opts) {
637
637
  catch { }
638
638
  let llmCallCount = 0;
639
639
  if (usage || runStatus === "error") {
640
- llmCallCount = 1;
640
+ llmCallCount =
641
+ usage?.llmCalls ??
642
+ // Compatibility for custom loop implementations that predate the
643
+ // attempt counter: a measured run still counts as one call.
644
+ 1;
641
645
  const generationUsage = usage ?? {
642
646
  inputTokens: 0,
643
647
  outputTokens: 0,
@@ -75,9 +75,7 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
75
75
  user: "user";
76
76
  }>>;
77
77
  }, z.core.$strip>>, {
78
- message?: undefined;
79
- id?: undefined;
80
- provider?: undefined;
78
+ found?: undefined;
81
79
  deleted?: undefined;
82
80
  providers: {
83
81
  id: string;
@@ -89,45 +87,47 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
89
87
  updatedAt: number;
90
88
  }[];
91
89
  count: number;
92
- found?: undefined;
90
+ provider?: undefined;
93
91
  registered?: undefined;
92
+ id?: undefined;
94
93
  label?: undefined;
95
- } | {
96
94
  message?: undefined;
97
- count?: undefined;
98
- id?: undefined;
95
+ } | {
99
96
  deleted?: undefined;
100
97
  providers?: undefined;
98
+ count?: undefined;
101
99
  found: boolean;
102
100
  provider: import("../custom-registry.js").CustomProviderConfig;
103
101
  registered?: undefined;
102
+ id?: undefined;
104
103
  label?: undefined;
105
- } | {
106
104
  message?: undefined;
107
- count?: undefined;
108
- provider?: undefined;
105
+ } | {
109
106
  deleted?: undefined;
110
107
  providers?: undefined;
108
+ count?: undefined;
109
+ provider?: undefined;
111
110
  found: boolean;
112
111
  id: string;
113
112
  registered?: undefined;
114
113
  label?: undefined;
115
- } | {
116
114
  message?: undefined;
115
+ } | {
116
+ found?: undefined;
117
+ providers?: undefined;
117
118
  count?: undefined;
118
119
  provider?: undefined;
119
- providers?: undefined;
120
- found?: undefined;
121
120
  deleted: boolean;
122
121
  id: string;
123
122
  registered?: undefined;
124
123
  label?: undefined;
124
+ message?: undefined;
125
125
  } | {
126
- count?: undefined;
127
- provider?: undefined;
126
+ found?: undefined;
128
127
  deleted?: undefined;
129
128
  providers?: undefined;
130
- found?: undefined;
129
+ count?: undefined;
130
+ provider?: undefined;
131
131
  registered: boolean;
132
132
  id: string;
133
133
  label: string;
@@ -311,8 +311,11 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
311
311
  notes?: string | undefined;
312
312
  scope?: "org" | "user" | undefined;
313
313
  }, {
314
- deleted?: undefined;
315
314
  id?: undefined;
315
+ provider?: undefined;
316
+ deleted?: undefined;
317
+ found?: undefined;
318
+ message?: undefined;
316
319
  providers: {
317
320
  id: string;
318
321
  label: string;
@@ -323,47 +326,44 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
323
326
  updatedAt: number;
324
327
  }[];
325
328
  count: number;
326
- provider?: undefined;
327
- found?: undefined;
328
329
  registered?: undefined;
329
330
  label?: undefined;
330
- message?: undefined;
331
331
  } | {
332
- deleted?: undefined;
333
332
  id?: undefined;
333
+ deleted?: undefined;
334
+ message?: undefined;
334
335
  providers?: undefined;
335
336
  count?: undefined;
336
337
  found: boolean;
337
338
  provider: import("../custom-registry.js").CustomProviderConfig;
338
339
  registered?: undefined;
339
340
  label?: undefined;
340
- message?: undefined;
341
341
  } | {
342
+ provider?: undefined;
342
343
  deleted?: undefined;
344
+ message?: undefined;
343
345
  providers?: undefined;
344
346
  count?: undefined;
345
- provider?: undefined;
346
347
  found: boolean;
347
348
  id: string;
348
349
  registered?: undefined;
349
350
  label?: undefined;
350
- message?: undefined;
351
351
  } | {
352
- providers?: undefined;
353
- count?: undefined;
354
352
  provider?: undefined;
355
353
  found?: undefined;
354
+ message?: undefined;
355
+ providers?: undefined;
356
+ count?: undefined;
356
357
  deleted: boolean;
357
358
  id: string;
358
359
  registered?: undefined;
359
360
  label?: undefined;
360
- message?: undefined;
361
361
  } | {
362
+ provider?: undefined;
362
363
  deleted?: undefined;
364
+ found?: undefined;
363
365
  providers?: undefined;
364
366
  count?: undefined;
365
- provider?: undefined;
366
- found?: undefined;
367
367
  registered: boolean;
368
368
  id: string;
369
369
  label: string;
@@ -73,7 +73,6 @@ export declare function createProviderCorpusJobAction(options: CreateProviderCor
73
73
  offset?: unknown;
74
74
  limit?: unknown;
75
75
  }, {
76
- deleted?: undefined;
77
76
  jobs: {
78
77
  id: string;
79
78
  name: string;
@@ -84,6 +83,7 @@ export declare function createProviderCorpusJobAction(options: CreateProviderCor
84
83
  updatedAt: string;
85
84
  }[];
86
85
  total: number;
86
+ deleted?: undefined;
87
87
  jobId?: undefined;
88
88
  } | {
89
89
  jobs?: undefined;
@@ -91,9 +91,9 @@ export declare function createProviderCorpusJobAction(options: CreateProviderCor
91
91
  deleted: boolean;
92
92
  jobId: string;
93
93
  } | {
94
- deleted?: undefined;
95
94
  jobs?: undefined;
96
95
  total?: undefined;
96
+ deleted?: undefined;
97
97
  jobId?: undefined;
98
98
  hits: Record<string, unknown>[];
99
99
  offset: number;
@@ -48,8 +48,8 @@ export declare function handleUpdateResource(event: any): Promise<import("./stor
48
48
  }>;
49
49
  /** DELETE /_agent-native/resources/:id — delete a resource */
50
50
  export declare function handleDeleteResource(event: any): Promise<{
51
- ok?: undefined;
52
51
  error: string;
52
+ ok?: undefined;
53
53
  } | {
54
54
  error?: undefined;
55
55
  ok: boolean;
@@ -34,37 +34,37 @@ export declare function createListSecretsHandler(): import("h3").EventHandlerWit
34
34
  /** POST /_agent-native/secrets/:key — write a secret. */
35
35
  export declare function createWriteSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
36
36
  error: string;
37
- status?: undefined;
38
37
  ok?: undefined;
38
+ status?: undefined;
39
39
  } | {
40
+ error?: undefined;
40
41
  ok: boolean;
41
42
  status: string;
42
- error?: undefined;
43
43
  } | {
44
+ ok?: undefined;
44
45
  error: string;
45
46
  removed?: undefined;
46
- ok?: undefined;
47
47
  } | {
48
+ error?: undefined;
48
49
  ok: boolean;
49
50
  removed: boolean;
50
- error?: undefined;
51
51
  }>>;
52
52
  /**
53
53
  * POST /_agent-native/secrets/:key/test — validate an optional candidate value
54
54
  * or the current stored value without changing anything.
55
55
  */
56
56
  export declare function createTestSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
57
+ ok?: undefined;
57
58
  error: string;
58
59
  note?: undefined;
59
- ok?: undefined;
60
60
  } | {
61
+ error?: undefined;
61
62
  ok: boolean;
62
63
  note?: undefined;
63
- error?: undefined;
64
64
  } | {
65
+ error?: undefined;
65
66
  ok: boolean;
66
67
  note: string;
67
- error?: undefined;
68
68
  } | {
69
69
  note?: undefined;
70
70
  ok: boolean;
@@ -95,11 +95,11 @@ export interface AdHocSecretPayload {
95
95
  export declare function createAdHocSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<AdHocSecretPayload[] | {
96
96
  error: string;
97
97
  } | {
98
+ error?: undefined;
98
99
  ok: boolean;
99
100
  key: string;
100
- error?: undefined;
101
101
  } | {
102
+ error?: undefined;
102
103
  ok: boolean;
103
104
  removed: boolean;
104
- error?: undefined;
105
105
  }>>;
@@ -14,12 +14,14 @@ export type ActionRouteResolvedCaller = AgentRunOwnerContext & {
14
14
  * Org to scope the request to, verified from the same credential as the
15
15
  * caller identity (e.g. the A2A token's org claim). When omitted, the org
16
16
  * is derived from the verified owner email via the framework's owner→org
17
- * membership lookup. The ambient session/org state on the request is never
18
- * consulted for adapter-resolved callers: a request can carry both a valid
19
- * A2A bearer and an unrelated browser cookie, and the cookie user's org
20
- * must not leak into the token caller's request context.
17
+ * membership lookup. An explicit `null` means the verified caller has no
18
+ * org and must not fall back to another membership. The ambient session/org
19
+ * state on the request is never consulted for adapter-resolved callers: a
20
+ * request can carry both a valid A2A bearer and an unrelated browser cookie,
21
+ * and the cookie user's org must not leak into the token caller's request
22
+ * context.
21
23
  */
22
- orgId?: string;
24
+ orgId?: string | null;
23
25
  /** Verified A2A correlation and issuer metadata for the audit row. */
24
26
  delegationJti?: string;
25
27
  delegationIssuer?: string;
@@ -3,12 +3,13 @@ import { verifyA2ATokenWithClaims } from "../a2a-claims.js";
3
3
  import { isActionContractError, isAgentActionStopError } from "../action.js";
4
4
  import { isTransientDatabaseError } from "../db/client.js";
5
5
  import { declaresFeatureFlagDelegation } from "../feature-flags/a2a-action-route.js";
6
+ import { isFeatureFlagAdminEmail } from "../feature-flags/permissions.js";
6
7
  import { resolveOrgByDomain, resolveOrgIdForEmail } from "../org/context.js";
7
8
  import { readBody } from "../server/h3-helpers.js";
8
9
  import { EMBED_TARGET_HEADER } from "../shared/embed-auth.js";
9
10
  import { isMcpEmbedCorsOrigin, MCP_EMBED_CORS_ALLOW_HEADERS, shouldAllowMcpEmbedCredentials, } from "../shared/mcp-embed-headers.js";
10
11
  import { actionCallIsReadOnly, notifyActionChange } from "./action-change.js";
11
- import { readBrowserSessionIdHeader, seedAgentRunOwnerContext, } from "./agent-run-context.js";
12
+ import { readBrowserSessionIdHeader, readAnalyticsClientPlatformHeader, seedAgentRunOwnerContext, } from "./agent-run-context.js";
12
13
  import { captureError } from "./capture-error.js";
13
14
  import { getAllowedCorsOrigin as resolveAllowedCorsOrigin, readCorsAllowedOrigins, } from "./cors-origins.js";
14
15
  import { resolveEmbedSessionFromRequest, resolvedEmbedCapabilityScope, } from "./embed-session.js";
@@ -55,7 +56,7 @@ async function resolveFeatureFlagA2ACaller(event, actionName) {
55
56
  if (!claims || !claims.scope.includes(required))
56
57
  throw new Error("Invalid feature flag delegation");
57
58
  const localOrg = await resolveOrgByDomain(claims.orgDomain);
58
- if (!localOrg)
59
+ if (!localOrg && !isFeatureFlagAdminEmail(claims.email))
59
60
  throw new Error("Invalid feature flag delegation");
60
61
  if (actionName === "set-feature-flag" &&
61
62
  (await consumeOneTimeJti(claims.jti))) {
@@ -63,7 +64,7 @@ async function resolveFeatureFlagA2ACaller(event, actionName) {
63
64
  }
64
65
  return {
65
66
  owner: claims.email,
66
- orgId: localOrg.orgId,
67
+ orgId: localOrg?.orgId ?? null,
67
68
  anonymous: false,
68
69
  delegationJti: claims.jti,
69
70
  delegationIssuer: claims.issuer,
@@ -167,7 +168,7 @@ function handleOptionsRequest(event) {
167
168
  }
168
169
  setResponseHeader(event, "Access-Control-Allow-Methods", "GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS");
169
170
  setResponseHeader(event, "Access-Control-Allow-Headers", cors.credentials
170
- ? `Content-Type,Authorization,X-Requested-With,X-Request-Source,X-Agent-Native-CSRF,X-User-Timezone,X-Agent-Native-Session-Id,X-Agent-Native-Tool-Bridge,X-Agent-Native-Tool-Id,X-Agent-Native-Frontend,X-Agent-Native-Client-Compatibility,X-Agent-Native-Build-Id,${EMBED_TARGET_HEADER}`
171
+ ? `Content-Type,Authorization,X-Requested-With,X-Request-Source,X-Agent-Native-CSRF,X-User-Timezone,X-Agent-Native-Session-Id,X-Agent-Native-Client-Platform,X-Agent-Native-Tool-Bridge,X-Agent-Native-Tool-Id,X-Agent-Native-Frontend,X-Agent-Native-Client-Compatibility,X-Agent-Native-Build-Id,${EMBED_TARGET_HEADER}`
171
172
  : `${MCP_EMBED_CORS_ALLOW_HEADERS},X-Agent-Native-Tool-Bridge,X-Agent-Native-Tool-Id,X-Agent-Native-Frontend,X-Agent-Native-Client-Compatibility,X-Agent-Native-Build-Id`);
172
173
  }
173
174
  setResponseStatus(event, 204);
@@ -362,7 +363,8 @@ export function mountActionRoutes(nitroApp, actions, options) {
362
363
  }
363
364
  // Org scoping. For adapter-resolved callers the org must come
364
365
  // exclusively from the verified credential: the adapter-asserted
365
- // orgId when present, otherwise the owner-email membership lookup.
366
+ // orgId when present, explicit null when the caller has no org,
367
+ // otherwise the owner-email membership lookup.
366
368
  // The request's ambient session/org state (`resolveOrgId`, usually
367
369
  // getSession-backed) is deliberately NOT consulted — a request can
368
370
  // carry both a valid A2A bearer and an unrelated same-origin browser
@@ -372,7 +374,10 @@ export function mountActionRoutes(nitroApp, actions, options) {
372
374
  let orgId;
373
375
  if (resolvedCaller) {
374
376
  orgId = normalizeOrgId(resolvedCaller.orgId);
375
- if (!orgId && resolvedCaller.owner && !resolvedCaller.anonymous) {
377
+ if (resolvedCaller.orgId !== null &&
378
+ !orgId &&
379
+ resolvedCaller.owner &&
380
+ !resolvedCaller.anonymous) {
376
381
  orgId = await storedActiveOrgId(resolvedCaller.owner);
377
382
  }
378
383
  }
@@ -385,6 +390,7 @@ export function mountActionRoutes(nitroApp, actions, options) {
385
390
  }
386
391
  const timezone = readTimezoneHeader(event);
387
392
  const browserSessionId = readBrowserSessionIdHeader(event);
393
+ const clientPlatform = readAnalyticsClientPlatformHeader(event);
388
394
  return runWithRequestContext({
389
395
  userEmail,
390
396
  userName,
@@ -392,6 +398,7 @@ export function mountActionRoutes(nitroApp, actions, options) {
392
398
  authCapability,
393
399
  timezone,
394
400
  browserSessionId,
401
+ clientPlatform,
395
402
  requestOrigin: getRequestURL(event).origin,
396
403
  // Captured here because this is the last layer that still holds
397
404
  // the h3 event; everything below reads it off the request store.
@@ -35,6 +35,7 @@ import { getSkillNameFromPath } from "../resources/metadata.js";
35
35
  import { resourceList, resourceListAccessible, resourceGet, ensurePersonalDefaults, SHARED_OWNER, WORKSPACE_OWNER, } from "../resources/store.js";
36
36
  import { normalizeDatabaseToolsMode } from "../scripts/db/tool-mode.js";
37
37
  import { getSetting, putSetting } from "../settings/store.js";
38
+ import { ANALYTICS_CLIENT_PLATFORM_BODY_FIELD, normalizeAnalyticsClientPlatform, } from "../shared/analytics-platform.js";
38
39
  import { docsUrl } from "../shared/docs-url.js";
39
40
  import { handleSharedThreadRequest, } from "./agent-chat/shared-thread.js";
40
41
  import { discoverAgents } from "./agent-discovery.js";
@@ -4909,6 +4910,11 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
4909
4910
  // is already consumed, so the handler reads this instead.
4910
4911
  event.context = event.context ?? {};
4911
4912
  event.context.__agentChatBackgroundBody = workerBody;
4913
+ const persistedClientPlatform = normalizeAnalyticsClientPlatform(workerBody[ANALYTICS_CLIENT_PLATFORM_BODY_FIELD]);
4914
+ if (persistedClientPlatform) {
4915
+ event.context[ANALYTICS_CLIENT_PLATFORM_BODY_FIELD] =
4916
+ persistedClientPlatform;
4917
+ }
4912
4918
  // Durable owner context: this self-dispatch is cookieless (HMAC-only).
4913
4919
  // Resolve the owner from the persisted run row, never the request
4914
4920
  // body, then invoke the normal handler. The shared agent-run context