@agent-native/core 0.207.0-nightly-20261011124220 → 0.207.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/dist/agent/production-agent.js +23 -6
  2. package/dist/agent/thread-data-builder.js +7 -4
  3. package/dist/cli/eval.js +20 -4
  4. package/dist/client/analytics/index.d.ts +1 -1
  5. package/dist/client/analytics.d.ts +12 -3
  6. package/dist/client/analytics.js +51 -17
  7. package/dist/client/onboarding/use-onboarding.d.ts +1 -0
  8. package/dist/client/onboarding/use-onboarding.js +19 -6
  9. package/dist/collab/struct-routes.d.ts +1 -1
  10. package/dist/eval/agent-runner.js +18 -8
  11. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  12. package/dist/mcp/connect-route.js +9 -4
  13. package/dist/observability/routes.d.ts +7 -7
  14. package/dist/onboarding/plugin.js +15 -45
  15. package/dist/progress/routes.d.ts +1 -1
  16. package/dist/resources/store.js +25 -30
  17. package/dist/server/agent-discovery.js +47 -53
  18. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  19. package/dist/server/agent-engine-ollama-models-route.d.ts +2 -2
  20. package/dist/server/builtin-agents.d.ts +2 -0
  21. package/dist/server/builtin-agents.js +50 -3
  22. package/dist/server/credential-provider.d.ts +6 -9
  23. package/dist/server/credential-provider.js +33 -21
  24. package/dist/templates/builder-code-starter/.agents/skills/adding-a-feature/SKILL.md.patch +1 -1
  25. package/dist/templates/builder-code-starter/AGENTS.md.patch +3 -3
  26. package/dist/templates/builder-code-starter/app/components/layout/Layout.tsx.patch +6 -7
  27. package/dist/templates/builder-code-starter/app/hooks/use-navigation-state.ts.patch +21 -28
  28. package/dist/templates/builder-code-starter/app/root.tsx.patch +16 -5
  29. package/dist/templates/builder-code-starter/template-layer.json +4 -0
  30. package/dist/templates/chat/.agents/skills/secrets/SKILL.md +6 -2
  31. package/dist/templates/chat/AGENTS.md +3 -3
  32. package/dist/templates/chat/CHANGELOG.md +6 -0
  33. package/dist/templates/chat/app/components/chat/ChatHomeRedirect.tsx +25 -0
  34. package/dist/templates/chat/app/components/chat/ChatRouteContent.tsx +828 -15
  35. package/dist/templates/chat/app/components/home/HomePage.tsx +332 -0
  36. package/dist/templates/chat/app/components/layout/AgentInspector.tsx +2 -4
  37. package/dist/templates/chat/app/components/layout/Header.tsx +1 -1
  38. package/dist/templates/chat/app/components/layout/Layout.tsx +5 -6
  39. package/dist/templates/chat/app/components/layout/Sidebar.tsx +35 -11
  40. package/dist/templates/chat/app/components/ui/item.tsx +1 -0
  41. package/dist/templates/chat/app/components/ui/separator.tsx +1 -0
  42. package/dist/templates/chat/app/components/ui/skeleton.tsx +1 -0
  43. package/dist/templates/chat/app/hooks/use-navigation-state.ts +16 -22
  44. package/dist/templates/chat/app/i18n/ar-SA.ts +25 -0
  45. package/dist/templates/chat/app/i18n/de-DE.ts +26 -0
  46. package/dist/templates/chat/app/i18n/en-US.ts +25 -0
  47. package/dist/templates/chat/app/i18n/es-ES.ts +26 -0
  48. package/dist/templates/chat/app/i18n/fr-FR.ts +26 -0
  49. package/dist/templates/chat/app/i18n/hi-IN.ts +26 -0
  50. package/dist/templates/chat/app/i18n/ja-JP.ts +26 -0
  51. package/dist/templates/chat/app/i18n/ko-KR.ts +26 -0
  52. package/dist/templates/chat/app/i18n/pt-BR.ts +26 -0
  53. package/dist/templates/chat/app/i18n/zh-CN.ts +25 -0
  54. package/dist/templates/chat/app/i18n/zh-TW.ts +25 -0
  55. package/dist/templates/chat/app/lib/chat-paths.ts +479 -0
  56. package/dist/templates/chat/app/root.tsx +14 -3
  57. package/dist/templates/chat/app/routes/chat._index.tsx +5 -0
  58. package/dist/templates/chat/app/routes/home.tsx +3 -22
  59. package/dist/templates/chat/changelog/2026-10-10-home-handoff-recovery-capacity.md +6 -0
  60. package/dist/templates/default/.agents/skills/secrets/SKILL.md +6 -2
  61. package/dist/templates/headless/.agents/skills/secrets/SKILL.md +6 -2
  62. package/dist/templates/workspace-core/.agents/skills/secrets/SKILL.md +6 -2
  63. package/dist/triggers/actions/manage-automation.d.ts +2 -2
  64. package/docs/content/deployment-environment-variables.mdx +10 -10
  65. package/docs/content/environment-variables.mdx +38 -6
  66. package/docs/content/getting-started.mdx +7 -7
  67. package/docs/content/multi-app-workspace.mdx +7 -3
  68. package/docs/content/template-chat-first-edits.mdx +46 -33
  69. package/package.json +4 -4
  70. package/src/templates/builder-code-starter/.agents/skills/adding-a-feature/SKILL.md.patch +1 -1
  71. package/src/templates/builder-code-starter/AGENTS.md.patch +3 -3
  72. package/src/templates/builder-code-starter/app/components/layout/Layout.tsx.patch +6 -7
  73. package/src/templates/builder-code-starter/app/hooks/use-navigation-state.ts.patch +21 -28
  74. package/src/templates/builder-code-starter/app/root.tsx.patch +16 -5
  75. package/src/templates/builder-code-starter/template-layer.json +4 -0
  76. package/src/templates/chat/.agents/skills/secrets/SKILL.md +6 -2
  77. package/src/templates/chat/AGENTS.md +3 -3
  78. package/src/templates/chat/CHANGELOG.md +6 -0
  79. package/src/templates/chat/app/components/chat/ChatHomeRedirect.tsx +25 -0
  80. package/src/templates/chat/app/components/chat/ChatRouteContent.tsx +828 -15
  81. package/src/templates/chat/app/components/home/HomePage.tsx +332 -0
  82. package/src/templates/chat/app/components/layout/AgentInspector.tsx +2 -4
  83. package/src/templates/chat/app/components/layout/Header.tsx +1 -1
  84. package/src/templates/chat/app/components/layout/Layout.tsx +5 -6
  85. package/src/templates/chat/app/components/layout/Sidebar.tsx +35 -11
  86. package/src/templates/chat/app/components/ui/item.tsx +1 -0
  87. package/src/templates/chat/app/components/ui/separator.tsx +1 -0
  88. package/src/templates/chat/app/components/ui/skeleton.tsx +1 -0
  89. package/src/templates/chat/app/hooks/use-navigation-state.ts +16 -22
  90. package/src/templates/chat/app/i18n/ar-SA.ts +25 -0
  91. package/src/templates/chat/app/i18n/de-DE.ts +26 -0
  92. package/src/templates/chat/app/i18n/en-US.ts +25 -0
  93. package/src/templates/chat/app/i18n/es-ES.ts +26 -0
  94. package/src/templates/chat/app/i18n/fr-FR.ts +26 -0
  95. package/src/templates/chat/app/i18n/hi-IN.ts +26 -0
  96. package/src/templates/chat/app/i18n/ja-JP.ts +26 -0
  97. package/src/templates/chat/app/i18n/ko-KR.ts +26 -0
  98. package/src/templates/chat/app/i18n/pt-BR.ts +26 -0
  99. package/src/templates/chat/app/i18n/zh-CN.ts +25 -0
  100. package/src/templates/chat/app/i18n/zh-TW.ts +25 -0
  101. package/src/templates/chat/app/lib/chat-paths.ts +479 -0
  102. package/src/templates/chat/app/root.tsx +14 -3
  103. package/src/templates/chat/app/routes/chat._index.tsx +5 -0
  104. package/src/templates/chat/app/routes/home.tsx +3 -22
  105. package/src/templates/chat/changelog/2026-10-10-home-handoff-recovery-capacity.md +6 -0
  106. package/src/templates/default/.agents/skills/secrets/SKILL.md +6 -2
  107. package/src/templates/headless/.agents/skills/secrets/SKILL.md +6 -2
  108. package/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +6 -2
@@ -8846,6 +8846,16 @@ export function createProductionAgentHandler(options) {
8846
8846
  const isReaperSuccessor = isBackgroundWorker &&
8847
8847
  typeof mutableBody[AGENT_CHAT_RECOVERY_OF_RUN_FIELD] === "string" &&
8848
8848
  Boolean(mutableBody[AGENT_CHAT_RECOVERY_OF_RUN_FIELD]);
8849
+ const isClientTurnContinuation = !isBackgroundWorker &&
8850
+ internalContinuation === true &&
8851
+ !continueOf &&
8852
+ !requestedApprovedToolCalls?.length &&
8853
+ typeof message === "string" &&
8854
+ message.startsWith(AGENT_INTERNAL_CONTINUE_PROMPT) &&
8855
+ typeof threadId === "string" &&
8856
+ threadId.trim().length > 0 &&
8857
+ typeof requestTurnId === "string" &&
8858
+ requestTurnId.trim().length > 0;
8849
8859
  const runId = backgroundRunMarker?.runId ?? generateRunId();
8850
8860
  const effectiveThreadId = threadId ?? runId;
8851
8861
  const effectiveTurnId = typeof backgroundRunMarker?.turnId === "string" &&
@@ -9008,7 +9018,10 @@ export function createProductionAgentHandler(options) {
9008
9018
  // A server successor and a continuation, automatic or chosen, resume the
9009
9019
  // same turn the same way: the thread's tool calls and results, plus the turn's
9010
9020
  // journal of finished steps, so nothing already done is sent again.
9011
- if ((isChainedBackgroundContinuation || isReaperSuccessor || continueOf) &&
9021
+ if ((isChainedBackgroundContinuation ||
9022
+ isReaperSuccessor ||
9023
+ continueOf ||
9024
+ isClientTurnContinuation) &&
9012
9025
  effectiveThreadId) {
9013
9026
  try {
9014
9027
  const { getThread } = await import("../chat-threads/store.js");
@@ -9022,12 +9035,14 @@ export function createProductionAgentHandler(options) {
9022
9035
  // A continuation always follows a stopped run, so a thread without
9023
9036
  // that turn's prompt means its history was lost, not that there was
9024
9037
  // none. A successor stays best-effort and resumes from what is there.
9025
- if (continueOf && !foundTurnPrompt) {
9038
+ if ((continueOf || isClientTurnContinuation) && !foundTurnPrompt) {
9026
9039
  throw new Error(`thread ${effectiveThreadId} has no turn prompt`);
9027
9040
  }
9028
9041
  // Resuming takes the thread's newest prompt, so a newer one, even one
9029
9042
  // whose run never started, would run under this turn's journal.
9030
- const promptTurnId = continueOf && threadData ? latestPromptTurnId(threadData) : undefined;
9043
+ const promptTurnId = (continueOf || isClientTurnContinuation) && threadData
9044
+ ? latestPromptTurnId(threadData)
9045
+ : undefined;
9031
9046
  if (promptTurnId && promptTurnId !== effectiveTurnId) {
9032
9047
  if (await updateRunStatusIfRunning(runId, "errored")) {
9033
9048
  await setRunTerminalReason(runId, CONTINUE_UNAVAILABLE_CODE);
@@ -9044,9 +9059,11 @@ export function createProductionAgentHandler(options) {
9044
9059
  ? backgroundRunMarker.continuationReason
9045
9060
  : isReaperSuccessor
9046
9061
  ? "network_interrupted"
9047
- : "run_timeout";
9062
+ : isClientTurnContinuation
9063
+ ? "stream_ended"
9064
+ : "run_timeout";
9048
9065
  const journalRead = await loadPriorTurnToolCallJournal(effectiveThreadId, effectiveTurnId);
9049
- if ((continueOf || isReaperSuccessor) &&
9066
+ if ((continueOf || isReaperSuccessor || isClientTurnContinuation) &&
9050
9067
  journalRead.status === "unreadable") {
9051
9068
  throw new Error(journalRead.error);
9052
9069
  }
@@ -9073,7 +9090,7 @@ export function createProductionAgentHandler(options) {
9073
9090
  }
9074
9091
  }
9075
9092
  catch (error) {
9076
- if (continueOf || isReaperSuccessor) {
9093
+ if (continueOf || isReaperSuccessor || isClientTurnContinuation) {
9077
9094
  // The browser's history lacks the stopped run's tool results, so
9078
9095
  // continuing from it could repeat a step that already finished.
9079
9096
  console.warn(`[agent-chat] auto-continue history unreadable for thread ${effectiveThreadId}:`, error);
@@ -1587,7 +1587,8 @@ export function foldThreadRunSuggestions(repo, run) {
1587
1587
  oldRun.lastSequence >= 0
1588
1588
  ? oldRun.lastSequence
1589
1589
  : 0;
1590
- const lastSequence = run.events.reduce((maximum, { seq }) => Number.isSafeInteger(seq) && seq >= 0 ? Math.max(maximum, seq) : maximum, previousSequence);
1590
+ // Runtime event sequence numbers and AgentKit protocol cursors are separate.
1591
+ const lastSequence = previousSequence;
1591
1592
  const snapshot = {
1592
1593
  ...oldRun,
1593
1594
  id: run.runId,
@@ -2287,9 +2288,11 @@ function mergeAgentKitHistory(existing, incoming, promptRunIds, onAnnotationConf
2287
2288
  if (Array.isArray(merged.suggestions)) {
2288
2289
  merged.suggestions = merged.suggestions.filter((suggestion) => suggestion?.runId === latest?.id);
2289
2290
  }
2290
- if (Array.isArray(merged.activeRunIds)) {
2291
- merged.activeRunIds = merged.activeRunIds.filter((id) => !["completed", "failed", "cancelled"].includes(runs.get(id)?.status ?? ""));
2292
- }
2291
+ const activeRunIds = new Set([
2292
+ ...(Array.isArray(previous.activeRunIds) ? previous.activeRunIds : []),
2293
+ ...(Array.isArray(next.activeRunIds) ? next.activeRunIds : []),
2294
+ ]);
2295
+ merged.activeRunIds = [...activeRunIds].filter((id) => !["completed", "failed", "cancelled"].includes(runs.get(id)?.status ?? ""));
2293
2296
  }
2294
2297
  const existingMessageRunIds = snapshotMessageRunIds(previous);
2295
2298
  const incomingMessageRunIds = snapshotMessageRunIds(next);
package/dist/cli/eval.js CHANGED
@@ -382,11 +382,27 @@ async function runPromote(args) {
382
382
  process.exit(0);
383
383
  }
384
384
  async function readReviewFile(filePath) {
385
- const contents = await fs.readFile(path.resolve(filePath), "utf8");
386
- if (Buffer.byteLength(contents, "utf8") > MAX_REVIEW_FILE_BYTES) {
387
- throw new Error("Reviewed input file exceeds the 64 KiB limit.");
385
+ const handle = await fs.open(path.resolve(filePath), "r");
386
+ try {
387
+ if ((await handle.stat()).size > MAX_REVIEW_FILE_BYTES) {
388
+ throw new Error("Reviewed input file exceeds the 64 KiB limit.");
389
+ }
390
+ const buffer = Buffer.alloc(MAX_REVIEW_FILE_BYTES + 1);
391
+ let bytesRead = 0;
392
+ while (bytesRead < buffer.length) {
393
+ const result = await handle.read(buffer, bytesRead, buffer.length - bytesRead, bytesRead);
394
+ if (result.bytesRead === 0)
395
+ break;
396
+ bytesRead += result.bytesRead;
397
+ }
398
+ if (bytesRead > MAX_REVIEW_FILE_BYTES) {
399
+ throw new Error("Reviewed input file exceeds the 64 KiB limit.");
400
+ }
401
+ return buffer.subarray(0, bytesRead).toString("utf8");
402
+ }
403
+ finally {
404
+ await handle.close();
388
405
  }
389
- return contents;
390
406
  }
391
407
  async function readReviewedPromptFile(filePath, maxLength) {
392
408
  const prompt = await readReviewFile(filePath);
@@ -1,2 +1,2 @@
1
- export { AGENT_NATIVE_EXCEPTION_EVENT_NAME, addErrorBreadcrumb, captureAttribution, captureClientException, captureError, captureException, captureMessage, clearAnalyticsSessionId, configureTracking, getAnalyticsAnonymousId, getAnalyticsSessionId, getFirstTouchAttribution, getLastTouchAttribution, getSessionReplayContext, getSessionReplayUrl, isErrorCaptureInstalled, maybeStartSessionReplay, setAnalyticsSessionId, setSentryUser, setTrackingContentCaptureEnabled, setTrackingIdentity, startSessionReplay, stopSessionReplay, trackAgentChatLifecycle, trackAnonymousEvent, trackEvent, trackLifecycleEvent, trackSessionStatus, type AgentChatLifecycleEvent, type CaptureExceptionContext, type CapturedExceptionEvent, type ClientCaptureContext, type ConfigureTrackingOptions, type ErrorCaptureConfigOptions, type ExceptionBreadcrumb, type ExceptionLevel, type FirstTouchAttribution, type LastTouchAttribution, type SessionReplayContext, type SessionReplayLinkOptions, type SessionReplayOptions, type SessionReplayStartResult, type SessionReplayUrlMatcher, type TrackingIdentityUser, } from "../analytics.js";
1
+ export { AGENT_NATIVE_EXCEPTION_EVENT_NAME, addErrorBreadcrumb, captureAttribution, captureClientException, captureError, captureException, captureMessage, clearAnalyticsSessionId, configureTracking, getAnalyticsAnonymousId, getAnalyticsSessionId, getFirstTouchAttribution, getLastTouchAttribution, getSessionReplayContext, getSessionReplayUrl, isErrorCaptureInstalled, maybeStartSessionReplay, setAnalyticsSessionId, setSentryUser, setTrackingContentCaptureEnabled, setTrackingIdentity, startSessionReplay, stopSessionReplay, trackAgentChatLifecycle, trackAnonymousEvent, trackEvent, trackLifecycleEvent, trackSessionStatus, type AgentChatLifecycleEvent, type CaptureAttributionOptions, type CaptureExceptionContext, type CapturedExceptionEvent, type ClientCaptureContext, type ConfigureTrackingOptions, type ErrorCaptureConfigOptions, type ExceptionBreadcrumb, type ExceptionLevel, type FirstTouchAttribution, type LastTouchAttribution, type SessionReplayContext, type SessionReplayLinkOptions, type SessionReplayOptions, type SessionReplayStartResult, type SessionReplayUrlMatcher, type TrackingIdentityUser, } from "../analytics.js";
2
2
  export { track } from "../track.js";
@@ -113,12 +113,21 @@ export declare function getAnalyticsAnonymousId(): string | undefined;
113
113
  export declare function getAnalyticsSessionId(): string | undefined;
114
114
  export declare function getFirstTouchAttribution(): FirstTouchAttribution | null;
115
115
  export declare function getLastTouchAttribution(): LastTouchAttribution | null;
116
+ export interface CaptureAttributionOptions {
117
+ /**
118
+ * The app path this visit entered, when the page isn't it. The sign-in page
119
+ * stands in front of the path the visitor asked for, and `landing_path`
120
+ * must name that path, not the sign-in page.
121
+ */
122
+ landingPath?: string;
123
+ }
116
124
  /**
117
125
  * Store the visitor's first and last touch now instead of in
118
- * `configureTracking()`, for a page that reads them before tracking starts.
119
- * It runs once per page load, so tracking's own capture is then a no-op.
126
+ * `configureTracking()`, for a page that reads them before tracking starts,
127
+ * or that never starts tracking, like the sign-in page. It runs once per page
128
+ * load, so tracking's own capture is then a no-op.
120
129
  */
121
- export declare function captureAttribution(): void;
130
+ export declare function captureAttribution(options?: CaptureAttributionOptions): void;
122
131
  export declare function setSentryUser(user: TrackingIdentityUser | null, orgId?: string | null): void;
123
132
  export declare function setTrackingIdentity(user: TrackingIdentityUser | null, orgId?: string | null): void;
124
133
  export interface ClientCaptureContext {
@@ -1,7 +1,7 @@
1
1
  import { recordTrackingEvent } from "../observability/tracing.js";
2
2
  import { AGENT_NATIVE_LIFECYCLE_EVENTS, AGENT_SIGNALS_PAGEVIEW_PROPERTY, AGENT_SIGNALS_VERSION, canonicalTrackingEvent, legacyLifecycleEvent, normalizeTrackingDimension, PAGE_LOAD_PAGEVIEW_PROPERTY, isWaitedActionResponse, SLOW_ACTION_RESPONSE_MS, TRACKING_EVENT_ALIAS_ID_PROPERTY, withCanonicalTrackingProperties, } from "../shared/analytics-events.js";
3
3
  import { ANALYTICS_CLIENT_PLATFORM_PROPERTY, } from "../shared/analytics-platform.js";
4
- import { hasAttributionSource } from "../shared/attribution-source.js";
4
+ import { hasAttributionSource, } from "../shared/attribution-source.js";
5
5
  import { resolveLaneEndpoint } from "../shared/environment-lanes.js";
6
6
  import { classifyErrorNoise, } from "../shared/error-noise.js";
7
7
  import { llmConnectionTrackingProperties, } from "../shared/llm-connection.js";
@@ -444,7 +444,7 @@ function scrubReferrerHost(referrer) {
444
444
  return "";
445
445
  }
446
446
  }
447
- function buildFirstTouchAttribution() {
447
+ function buildFirstTouchAttribution(landingPathname) {
448
448
  const attribution = {};
449
449
  let params = null;
450
450
  try {
@@ -460,7 +460,7 @@ function buildFirstTouchAttribution() {
460
460
  attribution[field] = value;
461
461
  }
462
462
  }
463
- const landingPath = truncateFirstTouchField(window.location.pathname);
463
+ const landingPath = truncateFirstTouchField(landingPathname);
464
464
  if (landingPath)
465
465
  attribution.landing_path = landingPath;
466
466
  const landingReferrer = typeof document !== "undefined" ? scrubReferrerHost(document.referrer) : "";
@@ -574,14 +574,14 @@ function backfillAttributionCookie(storageKey, spec) {
574
574
  * from replaces it. Without that, an untagged first visit would hide every
575
575
  * tagged visit after it.
576
576
  */
577
- function captureFirstTouchAttribution() {
577
+ function captureFirstTouchAttribution(options = {}) {
578
578
  if (_firstTouchCaptured)
579
579
  return;
580
580
  _firstTouchCaptured = true;
581
581
  if (typeof window === "undefined")
582
582
  return;
583
583
  try {
584
- const current = buildFirstTouchAttribution();
584
+ const current = buildFirstTouchAttribution(options.landingPath ?? window.location.pathname);
585
585
  const existing = getFirstTouchAttribution();
586
586
  if (existing &&
587
587
  (hasAttributionSource(existing) || !hasAttributionSource(current))) {
@@ -650,38 +650,72 @@ function readForwardedLastTouch() {
650
650
  }
651
651
  return hasAttributionSource(forwarded) ? forwarded : null;
652
652
  }
653
- function readStoredAttribution(storageKey) {
654
- if (typeof window === "undefined")
653
+ function parseAttribution(raw) {
654
+ if (!raw)
655
655
  return null;
656
656
  try {
657
- const raw = safeStorageGet(storageKey);
658
- if (!raw)
659
- return null;
660
657
  const parsed = JSON.parse(raw);
661
658
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
662
659
  return null;
663
660
  }
664
- return parsed;
661
+ // Every field is a string; a corrupted non-string one would make source
662
+ // detection throw, so it is dropped like any unreadable value.
663
+ const touch = {};
664
+ for (const [field, value] of Object.entries(parsed)) {
665
+ if (typeof value === "string")
666
+ touch[field] = value;
667
+ }
668
+ return touch;
669
+ }
670
+ catch {
671
+ // coercion-ok: an unparseable value holds no touch; the caller tries the
672
+ // next source.
673
+ return null;
674
+ }
675
+ }
676
+ function readAttributionCookieValue(cookieName) {
677
+ const cookie = readAttributionCookie(cookieName);
678
+ if (!cookie)
679
+ return null;
680
+ try {
681
+ return parseAttribution(decodeURIComponent(cookie));
665
682
  }
666
683
  catch {
684
+ // coercion-ok: a malformed cookie encoding holds no touch.
667
685
  return null;
668
686
  }
669
687
  }
688
+ /**
689
+ * The stored touch, unless only its cookie says where the visitor came from:
690
+ * storage can be blocked, cleared, or hold an unreadable or empty value. The
691
+ * cookie is what signup reads, so a source that survives only there still
692
+ * counts, and capture must not overwrite it as if this were a first visit.
693
+ */
694
+ function readStoredAttribution(storageKey, cookieName) {
695
+ if (typeof window === "undefined")
696
+ return null;
697
+ const stored = parseAttribution(safeStorageGet(storageKey));
698
+ if (hasAttributionSource(stored))
699
+ return stored;
700
+ const cookie = readAttributionCookieValue(cookieName);
701
+ return hasAttributionSource(cookie) ? cookie : (stored ?? cookie);
702
+ }
670
703
  export function getFirstTouchAttribution() {
671
- return readStoredAttribution(FIRST_TOUCH_STORAGE_KEY);
704
+ return readStoredAttribution(FIRST_TOUCH_STORAGE_KEY, FIRST_TOUCH_COOKIE_NAME);
672
705
  }
673
706
  export function getLastTouchAttribution() {
674
- return readStoredAttribution(LAST_TOUCH_STORAGE_KEY);
707
+ return readStoredAttribution(LAST_TOUCH_STORAGE_KEY, LAST_TOUCH_COOKIE_NAME);
675
708
  }
676
709
  /**
677
710
  * Store the visitor's first and last touch now instead of in
678
- * `configureTracking()`, for a page that reads them before tracking starts.
679
- * It runs once per page load, so tracking's own capture is then a no-op.
711
+ * `configureTracking()`, for a page that reads them before tracking starts,
712
+ * or that never starts tracking, like the sign-in page. It runs once per page
713
+ * load, so tracking's own capture is then a no-op.
680
714
  */
681
- export function captureAttribution() {
715
+ export function captureAttribution(options = {}) {
682
716
  if (isSyntheticBrowserTraffic())
683
717
  return;
684
- captureFirstTouchAttribution();
718
+ captureFirstTouchAttribution(options);
685
719
  }
686
720
  function isLocalAnalyticsHostname(hostname) {
687
721
  const h = (hostname || "").toLowerCase();
@@ -18,6 +18,7 @@ export interface UseOnboardingResult {
18
18
  profile: OnboardingAppProfile | null;
19
19
  loading: boolean;
20
20
  error: string | null;
21
+ summaryKnown: boolean;
21
22
  currentStepId: string | null;
22
23
  completeCount: number;
23
24
  totalCount: number;
@@ -583,6 +583,7 @@ export function useOnboarding(options = {}) {
583
583
  const [profile, setProfile] = useState(null);
584
584
  const [loading, setLoading] = useState(true);
585
585
  const [error, setError] = useState(null);
586
+ const [summaryKnown, setSummaryKnown] = useState(false);
586
587
  const [dismissed, setDismissed] = useState(false);
587
588
  const [firstRun, setFirstRun] = useState(preview || initialFirstRun);
588
589
  const [completeFirstRunError, setCompleteFirstRunError] = useState(null);
@@ -606,10 +607,8 @@ export function useOnboarding(options = {}) {
606
607
  : initialFirstRun
607
608
  ? Promise.resolve(true)
608
609
  : fetchFirstRunOnboardingStatus();
609
- const [summary, firstRunRes] = await Promise.all([
610
- readOnboardingSummary(summaryUrl, reuseSharedRead !== true),
611
- firstRunPromise,
612
- ]);
610
+ const firstRunResultPromise = firstRunPromise.then((value) => ({ status: "fulfilled", value }), (reason) => ({ status: "rejected", reason }));
611
+ const summary = await readOnboardingSummary(summaryUrl, reuseSharedRead !== true);
613
612
  if (!mountedRef.current ||
614
613
  fetchGeneration !== fetchGenerationRef.current) {
615
614
  return;
@@ -630,13 +629,23 @@ export function useOnboarding(options = {}) {
630
629
  stepsRef.current = summary.steps;
631
630
  setSteps(summary.steps);
632
631
  setProfile(summary.profile);
632
+ setDismissed(!!summary.dismissed);
633
+ setSummaryKnown(true);
634
+ setError(null);
635
+ const firstRunResult = await firstRunResultPromise;
636
+ if (!mountedRef.current ||
637
+ fetchGeneration !== fetchGenerationRef.current) {
638
+ return;
639
+ }
640
+ if (firstRunResult.status === "rejected") {
641
+ throw firstRunResult.reason;
642
+ }
633
643
  if (preview) {
634
644
  setFirstRun(true);
635
645
  }
636
646
  else if (!initialFirstRun) {
637
- setFirstRun(firstRunRes === true);
647
+ setFirstRun(firstRunResult.value === true);
638
648
  }
639
- setDismissed(!!summary.dismissed);
640
649
  setError(null);
641
650
  }
642
651
  catch (e) {
@@ -710,6 +719,9 @@ export function useOnboarding(options = {}) {
710
719
  flow: "checklist",
711
720
  step_id: id,
712
721
  });
722
+ const updatedSteps = stepsRef.current.map((step) => step.id === id ? { ...step, complete: true } : step);
723
+ stepsRef.current = updatedSteps;
724
+ setSteps(updatedSteps);
713
725
  await fetchAll();
714
726
  }, [fetchAll]);
715
727
  const dismiss = useCallback(async () => {
@@ -802,6 +814,7 @@ export function useOnboarding(options = {}) {
802
814
  profile,
803
815
  loading,
804
816
  error,
817
+ summaryKnown,
805
818
  currentStepId,
806
819
  completeCount,
807
820
  totalCount,
@@ -1,6 +1,6 @@
1
1
  export declare const postCollabJson: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
2
- error: string;
3
2
  ok?: undefined;
3
+ error: string;
4
4
  } | {
5
5
  error?: undefined;
6
6
  ok: boolean;
@@ -41,11 +41,24 @@ export async function createAgentRunner(config) {
41
41
  Object.values(actions).some((action) => action.readOnly !== true))) {
42
42
  throw new Error("Production chat evals require the real final response guard and a read-only adapter action registry.");
43
43
  }
44
- const engine = config.engine ?? (await resolveEngine({ engineOption: undefined }));
45
- const modelCandidate = config.model ??
46
- (await getStoredModelForEngine(engine)) ??
47
- engine.defaultModel;
48
- const model = normalizeModelForEngine(engine, modelCandidate);
44
+ const resolveRuntimeModel = async () => {
45
+ const engine = config.engine ??
46
+ (await resolveEngine({ appId: productionContext?.appId }));
47
+ const modelCandidate = config.model ??
48
+ (await getStoredModelForEngine(engine, {
49
+ appId: productionContext?.appId,
50
+ })) ??
51
+ engine.defaultModel;
52
+ return { engine, model: normalizeModelForEngine(engine, modelCandidate) };
53
+ };
54
+ const runtimeModel = productionContext
55
+ ? await runWithRequestContext({
56
+ userEmail: productionContext.ownerEmail,
57
+ orgId: productionContext.orgId ?? undefined,
58
+ isSyntheticTraffic: true,
59
+ }, resolveRuntimeModel)
60
+ : await resolveRuntimeModel();
61
+ const { engine, model } = runtimeModel;
49
62
  const timeoutMs = config.timeoutMs ?? DEFAULT_AGENT_TIMEOUT_MS;
50
63
  const initialToolNames = productionContext?.initialToolNames
51
64
  ? new Set(productionContext.initialToolNames)
@@ -359,9 +372,6 @@ function validateProductionPathRun(result, context, actionAllowlist) {
359
372
  !["ok", "empty", "timed_out", "failed"].includes(receipt.prefetchStatus)) {
360
373
  return "Production chat eval adapter returned an incomplete setup receipt.";
361
374
  }
362
- if (receipt.prefetchStatus !== "ok" && receipt.prefetchStatus !== "empty") {
363
- return `Production chat eval adapter reported unsuccessful prefetch status "${receipt.prefetchStatus}".`;
364
- }
365
375
  if (receipt.ownerEmail.trim().toLowerCase() !==
366
376
  context.ownerEmail.trim().toLowerCase() ||
367
377
  receipt.orgId !== context.orgId) {
@@ -42,8 +42,8 @@ declare const _default: import("../../action.js").ActionDefinition<{
42
42
  url: string;
43
43
  id: string;
44
44
  provider: string;
45
- configured?: undefined;
46
45
  error?: undefined;
46
+ configured?: undefined;
47
47
  } | {
48
48
  url?: undefined;
49
49
  id?: undefined;
@@ -78,13 +78,18 @@ function joinAppPath(basePath, path) {
78
78
  return basePath;
79
79
  return `${basePath}${path.startsWith("/") ? path : `/${path}`}`;
80
80
  }
81
- function appLabel(origin, options) {
81
+ function appLabel(origin, options, environment) {
82
82
  const app = getAppConfig().app;
83
83
  const declared = options.appId ?? app.id ?? app.template ?? app.slug;
84
84
  if (declared)
85
85
  return declared;
86
+ // Deployed functions run without npm's `npm_package_name`, so first-party
87
+ // apps usually land here. A beta lane is `beta.<app>.agent-native.com`: its
88
+ // leading label names the lane, and every beta app would share it. Only a
89
+ // beta deployment drops it, so no production name changes.
86
90
  try {
87
- const h = new URL(origin).hostname;
91
+ const host = new URL(origin).hostname;
92
+ const h = environment === "beta" ? host.replace(/^beta\./, "") : host;
88
93
  return h.split(".")[0] || h;
89
94
  }
90
95
  catch {
@@ -106,10 +111,10 @@ function connectEnvironment() {
106
111
  export function resolveMcpConnectIdentity(appUrl, options) {
107
112
  const environment = connectEnvironment();
108
113
  const baseName = options.serverName?.trim() ||
109
- derivedMcpServerBaseName(appLabel(appUrl, options), environment);
114
+ derivedMcpServerBaseName(appLabel(appUrl, options, environment), environment);
110
115
  return {
111
116
  serverName: mcpConnectServerName(baseName, environment),
112
- appName: options.appName || appLabel(appUrl, options),
117
+ appName: options.appName || appLabel(appUrl, options, environment),
113
118
  appUrl,
114
119
  mcpUrl: mcpResourceUrl(appUrl),
115
120
  environment,
@@ -27,43 +27,43 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
27
27
  thumbsUpRate: number;
28
28
  avgEvalScore: number;
29
29
  } | {
30
+ ok?: undefined;
30
31
  error: string;
31
32
  message: string;
32
33
  summary?: undefined;
33
34
  spans?: undefined;
34
- ok?: undefined;
35
35
  } | {
36
+ error?: undefined;
37
+ ok?: undefined;
36
38
  message?: undefined;
37
39
  summary: import("./types.js").TraceSummary;
38
40
  spans: import("./types.js").TraceSpan[];
41
+ } | {
39
42
  error?: undefined;
40
43
  ok?: undefined;
41
- } | {
42
44
  message?: undefined;
43
45
  summary?: undefined;
44
46
  spans?: undefined;
45
47
  traceMissing: true;
46
48
  id: string;
49
+ } | {
47
50
  error?: undefined;
48
51
  ok?: undefined;
49
- } | {
50
52
  message?: undefined;
51
53
  summary?: undefined;
52
54
  spans?: undefined;
53
55
  traceMissing?: undefined;
54
56
  id: string;
55
- error?: undefined;
56
- ok?: undefined;
57
57
  } | {
58
+ ok?: undefined;
58
59
  message?: undefined;
59
60
  summary?: undefined;
60
61
  spans?: undefined;
61
62
  error: any;
62
- ok?: undefined;
63
63
  } | {
64
+ error?: undefined;
64
65
  message?: undefined;
65
66
  summary?: undefined;
66
67
  spans?: undefined;
67
- error?: undefined;
68
68
  ok: boolean;
69
69
  }>>;
@@ -3,7 +3,6 @@ import { getAppConfig } from "../app-config/index.js";
3
3
  import { appStateGet, appStatePut } from "../application-state/store.js";
4
4
  import { getOrgContext } from "../org/context.js";
5
5
  import { readBrowserSessionIdHeader } from "../server/agent-run-context.js";
6
- import { CredentialStoreUnavailableError } from "../server/credential-provider.js";
7
6
  import { awaitBootstrap, getH3App, markDefaultPluginProvided, } from "../server/framework-request-handler.js";
8
7
  import { runWithRequestContext } from "../server/request-context.js";
9
8
  import { FIRST_RUN_ONBOARDING_COMPLETED_KEY, FIRST_RUN_ONBOARDING_COOKIE, FIRST_RUN_ONBOARDING_ELIGIBLE_KEY, } from "../shared/first-run-onboarding.js";
@@ -29,13 +28,8 @@ async function resolveOnboardingContext(event) {
29
28
  };
30
29
  }
31
30
  async function hasOverride(sessionId, stepId) {
32
- try {
33
- const val = await appStateGet(sessionId, `${OVERRIDE_KEY_PREFIX}${stepId}`);
34
- return !!(val && val.complete);
35
- }
36
- catch {
37
- return false;
38
- }
31
+ const val = await appStateGet(sessionId, `${OVERRIDE_KEY_PREFIX}${stepId}`);
32
+ return !!(val && val.complete);
39
33
  }
40
34
  async function serializeSteps(context, options = {}) {
41
35
  const steps = listOnboardingSteps();
@@ -46,12 +40,7 @@ async function serializeSteps(context, options = {}) {
46
40
  }
47
41
  let complete = false;
48
42
  if (!options.preview) {
49
- try {
50
- complete = (await step.isComplete(context)) === true;
51
- }
52
- catch {
53
- complete = false;
54
- }
43
+ complete = (await step.isComplete(context)) === true;
55
44
  if (!complete) {
56
45
  complete = await hasOverride(context.sessionId, step.id);
57
46
  }
@@ -78,19 +67,8 @@ function allRequiredComplete(statuses) {
78
67
  return statuses.filter((s) => s.required).every((s) => s.complete);
79
68
  }
80
69
  async function readDismissedFlag(sessionId) {
81
- // The dismissed flag is optional UX state; a transient DB failure reading it
82
- // must not take down a read whose steps and profile are still usable (the
83
- // pre-summary client already assumed "not dismissed" when this read
84
- // failed). A credential-store outage is not transient, so it still throws.
85
- try {
86
- const value = await appStateGet(sessionId, DISMISSED_KEY);
87
- return !!(value && value.dismissed);
88
- }
89
- catch (error) {
90
- if (error instanceof CredentialStoreUnavailableError)
91
- throw error;
92
- return false;
93
- }
70
+ const value = await appStateGet(sessionId, DISMISSED_KEY);
71
+ return !!(value && value.dismissed);
94
72
  }
95
73
  async function resolveSharedCompletionEnabled() {
96
74
  if (!getAppConfig().onboarding.sharedCompletion.enabled)
@@ -164,24 +142,16 @@ export function createOnboardingPlugin(options = {}) {
164
142
  return { error: "Method not allowed" };
165
143
  }
166
144
  const context = await resolveOnboardingContext(event);
167
- try {
168
- return await withOnboardingRequestContext(context, async () => {
169
- const [value, statuses] = await Promise.all([
170
- appStateGet(context.sessionId, DISMISSED_KEY),
171
- serializeSteps(context),
172
- ]);
173
- const dismissed = !!(value && value.dismissed);
174
- return {
175
- dismissed,
176
- allComplete: allRequiredComplete(statuses),
177
- };
178
- });
179
- }
180
- catch (error) {
181
- if (error instanceof CredentialStoreUnavailableError)
182
- throw error;
183
- return { dismissed: false, allComplete: false };
184
- }
145
+ return withOnboardingRequestContext(context, async () => {
146
+ const [statuses, value] = await Promise.all([
147
+ serializeSteps(context),
148
+ appStateGet(context.sessionId, DISMISSED_KEY),
149
+ ]);
150
+ return {
151
+ dismissed: !!(value && value.dismissed),
152
+ allComplete: allRequiredComplete(statuses),
153
+ };
154
+ });
185
155
  }));
186
156
  getH3App(nitroApp).use(`${ONBOARDING_PREFIX}/profile`, defineEventHandler(async (event) => {
187
157
  if (getMethod(event) !== "GET") {
@@ -2,6 +2,6 @@ export declare function createProgressHandler(): import("h3").EventHandlerWithFe
2
2
  error: string;
3
3
  ok?: undefined;
4
4
  } | {
5
- ok: boolean;
6
5
  error?: undefined;
6
+ ok: boolean;
7
7
  }>>;