@agent-native/core 0.148.0 → 0.149.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 (165) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/changelog/2026-08-10-wide-tables-in-analytics-chat-now-scroll-horizontally-within.md +6 -0
  3. package/corpus/templates/calendar/app/components/calendar/DayView.tsx +13 -53
  4. package/corpus/templates/calendar/app/components/calendar/WeekView.tsx +11 -70
  5. package/corpus/templates/calendar/app/lib/event-layout.ts +122 -0
  6. package/corpus/templates/calendar/app/routes/_app.availability.tsx +1 -1
  7. package/corpus/templates/calendar/changelog/2026-08-10-opening-availability-settings-from-the-agent-lands-on-the-av.md +6 -0
  8. package/corpus/templates/calendar/vitest.config.ts +6 -1
  9. package/corpus/templates/clips/actions/lib/audio-only-transcription.ts +3 -1
  10. package/corpus/templates/clips/app/components/player/video-player.tsx +44 -4
  11. package/corpus/templates/clips/changelog/2026-08-10-fixed-transcription-failures-for-recordings-without-captured.md +6 -0
  12. package/corpus/templates/clips/changelog/2026-08-10-mobile-clips-can-now-enter-fullscreen-on-iphone-safari.md +6 -0
  13. package/corpus/templates/content/actions/_property-utils.ts +11 -1
  14. package/corpus/templates/content/actions/view-screen.ts +62 -12
  15. package/corpus/templates/content/server/plugins/agent-chat.ts +2 -0
  16. package/corpus/templates/design/.agents/skills/design-templates/SKILL.md +22 -0
  17. package/corpus/templates/design/AGENTS.md +3 -0
  18. package/corpus/templates/design/actions/create-design-from-template.ts +19 -0
  19. package/corpus/templates/design/actions/get-design-snapshot.ts +18 -0
  20. package/corpus/templates/design/actions/get-design-template.ts +178 -0
  21. package/corpus/templates/design/actions/list-designs.ts +3 -1
  22. package/corpus/templates/design/actions/view-screen.ts +31 -0
  23. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +225 -214
  24. package/corpus/templates/design/app/components/design/multi-screen/types.ts +3 -0
  25. package/corpus/templates/design/app/i18n-data.ts +78 -0
  26. package/corpus/templates/design/app/lib/design-authors.ts +64 -0
  27. package/corpus/templates/design/app/pages/Index.tsx +114 -4
  28. package/corpus/templates/design/app/pages/design-editor/generation-prompt-directives.ts +1 -0
  29. package/corpus/templates/design/changelog/2026-08-07-alt-dragging-an-element-to-copy-it-now-keeps-the-copy-visibl.md +6 -0
  30. package/corpus/templates/design/changelog/2026-08-07-design-cards-now-show-who-created-each-design-with-a-filter-.md +6 -0
  31. package/corpus/templates/design/changelog/2026-08-07-drawing-a-shape-on-the-overview-canvas-now-keeps-the-shape-s.md +6 -0
  32. package/corpus/templates/design/changelog/2026-08-07-hold-option-alt-while-dragging-a-frame-on-the-overview-canva.md +6 -0
  33. package/corpus/templates/design/server/lib/design-template-data.ts +143 -0
  34. package/corpus/templates/design/server/plugins/agent-chat.ts +1 -0
  35. package/corpus/templates/forms/AGENTS.md +2 -2
  36. package/corpus/templates/forms/actions/create-form.ts +1 -1
  37. package/corpus/templates/forms/actions/update-form.ts +5 -5
  38. package/corpus/templates/forms/changelog/2026-08-10-forms-now-keep-agent-applied-field-changes-visible-after-an-.md +6 -0
  39. package/corpus/templates/forms/changelog/2026-08-10-published-form-links-always-use-the-working-public-form-url.md +6 -0
  40. package/corpus/templates/forms/server/plugins/agent-chat.ts +2 -2
  41. package/corpus/templates/slides/AGENTS.md +4 -3
  42. package/corpus/templates/slides/actions/import-google-slides-reference.ts +56 -10
  43. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +12 -74
  44. package/corpus/templates/slides/app/components/editor/GeneratingSlidePreview.tsx +5 -1
  45. package/corpus/templates/slides/app/components/editor/GoogleDriveConnectionCta.tsx +15 -4
  46. package/corpus/templates/slides/app/components/editor/NewDeckReferenceStep.tsx +116 -55
  47. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +1 -0
  48. package/corpus/templates/slides/app/components/onboarding/FirstDeckOnboardingFlow.tsx +143 -18
  49. package/corpus/templates/slides/app/i18n/en-US.ts +6 -0
  50. package/corpus/templates/slides/app/lib/generation-state.ts +7 -1
  51. package/corpus/templates/slides/app/pages/DeckEditor.tsx +32 -14
  52. package/corpus/templates/slides/app/pages/Index.tsx +110 -128
  53. package/corpus/templates/slides/changelog/2026-08-10-imported-reference-decks-now-show-clear-success-and-stay-sel.md +6 -0
  54. package/corpus/templates/slides/changelog/2026-08-10-pasted-google-slides-links-import-after-reconnecting-google-.md +6 -0
  55. package/corpus/templates/slides/server/handlers/google-docs-auth.ts +4 -0
  56. package/corpus/templates/slides/server/lib/google-docs-oauth.ts +13 -0
  57. package/corpus/templates/slides/server/plugins/agent-chat.ts +1 -1
  58. package/dist/agent/engine/registry.d.ts +8 -2
  59. package/dist/agent/engine/registry.js +63 -29
  60. package/dist/agent/production-agent.js +7 -0
  61. package/dist/agent/run-manager.d.ts +5 -4
  62. package/dist/agent/run-manager.js +6 -5
  63. package/dist/cli/claude-code-participant.d.ts +2 -1
  64. package/dist/cli/claude-code-participant.js +19 -0
  65. package/dist/cli/code-agent-executor.js +223 -0
  66. package/dist/cli/create-workspace.js +3 -3
  67. package/dist/cli/create.d.ts +3 -2
  68. package/dist/cli/create.js +20 -24
  69. package/dist/client/AgentPanel.js +8 -4
  70. package/dist/client/AssistantChat.js +34 -1
  71. package/dist/client/agent-chat-adapter.d.ts +1 -0
  72. package/dist/client/agent-chat-adapter.js +10 -2
  73. package/dist/client/blocks/library/diagram.js +1 -1
  74. package/dist/client/blocks/library/sanitize-html.js +66 -2
  75. package/dist/client/blocks/library/wireframe-kit.js +69 -0
  76. package/dist/client/chat/markdown-renderer.d.ts +1 -0
  77. package/dist/client/chat/markdown-renderer.js +4 -0
  78. package/dist/client/chat/message-components.d.ts +1 -0
  79. package/dist/client/chat/message-components.js +9 -1
  80. package/dist/client/chat/tool-call-display.d.ts +8 -0
  81. package/dist/client/chat/tool-call-display.js +30 -21
  82. package/dist/client/chat-first/app-pane.js +1 -1
  83. package/dist/client/chat-first/apps-rail.d.ts +1 -1
  84. package/dist/client/chat-first/apps-rail.js +12 -12
  85. package/dist/client/chat-first/chat-history.d.ts +1 -1
  86. package/dist/client/chat-first/chat-history.js +2 -0
  87. package/dist/client/chat-first/copy.js +1 -0
  88. package/dist/client/chat-first/primary-nav.js +1 -1
  89. package/dist/client/chat-first/types.d.ts +1 -0
  90. package/dist/client/resources/McpConnectionSuggestion.js +2 -2
  91. package/dist/client/settings/SettingsTabsPage.d.ts +3 -1
  92. package/dist/client/settings/SettingsTabsPage.js +2 -2
  93. package/dist/client/sse-event-processor.d.ts +8 -0
  94. package/dist/client/sse-event-processor.js +13 -6
  95. package/dist/collab/struct-routes.d.ts +1 -1
  96. package/dist/deploy/build.d.ts +5 -7
  97. package/dist/deploy/build.js +19 -12
  98. package/dist/localization/default-messages.js +1 -1
  99. package/dist/observability/routes.d.ts +3 -3
  100. package/dist/provider-api/actions/provider-api.d.ts +2 -2
  101. package/dist/scripts/call-agent.js +3 -3
  102. package/dist/scripts/describe-workspace-apps.js +1 -1
  103. package/dist/secrets/routes.d.ts +6 -6
  104. package/dist/server/agent-capabilities.js +0 -0
  105. package/dist/server/agent-chat/context-tools.js +1 -1
  106. package/dist/server/agent-chat/prompt-resources.js +1 -1
  107. package/dist/server/agent-chat-plugin.js +1 -1
  108. package/dist/server/auth.js +190 -42
  109. package/dist/server/better-auth-migrations.js +53 -0
  110. package/dist/server/http-response-telemetry.js +7 -1
  111. package/dist/server/onboarding-html.js +119 -33
  112. package/dist/server/transcribe-voice.d.ts +1 -1
  113. package/dist/shared/password-policy.d.ts +2 -1
  114. package/dist/shared/password-policy.js +2 -1
  115. package/dist/styles/agent-conversation.css +20 -3
  116. package/dist/styles/agent-native.css +14 -2
  117. package/dist/styles/blocks.css +132 -0
  118. package/dist/templates/default/app/i18n/ar-SA.ts +1 -1
  119. package/dist/templates/default/app/i18n/de-DE.ts +1 -1
  120. package/dist/templates/default/app/i18n/en-US.ts +1 -1
  121. package/dist/templates/default/app/i18n/es-ES.ts +1 -1
  122. package/dist/templates/default/app/i18n/fr-FR.ts +1 -1
  123. package/dist/templates/default/app/i18n/hi-IN.ts +1 -1
  124. package/dist/templates/default/app/i18n/ja-JP.ts +1 -1
  125. package/dist/templates/default/app/i18n/ko-KR.ts +1 -1
  126. package/dist/templates/default/app/i18n/pt-BR.ts +1 -1
  127. package/dist/templates/default/app/i18n/zh-CN.ts +1 -1
  128. package/dist/templates/default/app/i18n/zh-TW.ts +1 -1
  129. package/dist/templates/default/pnpm-workspace.yaml +1 -0
  130. package/dist/templates/workspace-root/pnpm-workspace.yaml +2 -0
  131. package/docs/content/actions.mdx +4 -1
  132. package/docs/content/agent-surfaces.mdx +8 -6
  133. package/docs/content/automations.mdx +1 -1
  134. package/docs/content/cloneable-saas.mdx +6 -6
  135. package/docs/content/database.mdx +6 -1
  136. package/docs/content/deployment.mdx +10 -8
  137. package/docs/content/docs-components.mdx +22 -14
  138. package/docs/content/drop-in-agent.mdx +1 -1
  139. package/docs/content/environment-variables.mdx +4 -3
  140. package/docs/content/getting-started-actions.mdx +5 -4
  141. package/docs/content/getting-started-pages.mdx +7 -6
  142. package/docs/content/getting-started.mdx +4 -3
  143. package/docs/content/notifications.mdx +6 -6
  144. package/docs/content/progress.mdx +5 -5
  145. package/docs/content/routing.mdx +3 -1
  146. package/docs/content/server-database.mdx +13 -4
  147. package/docs/content/server-overview.mdx +9 -9
  148. package/docs/content/server-plugins.mdx +10 -4
  149. package/docs/content/server-routes.mdx +7 -2
  150. package/docs/content/tracking.mdx +4 -4
  151. package/docs/content/what-is-agent-native.mdx +4 -4
  152. package/package.json +2 -2
  153. package/src/templates/default/app/i18n/ar-SA.ts +1 -1
  154. package/src/templates/default/app/i18n/de-DE.ts +1 -1
  155. package/src/templates/default/app/i18n/en-US.ts +1 -1
  156. package/src/templates/default/app/i18n/es-ES.ts +1 -1
  157. package/src/templates/default/app/i18n/fr-FR.ts +1 -1
  158. package/src/templates/default/app/i18n/hi-IN.ts +1 -1
  159. package/src/templates/default/app/i18n/ja-JP.ts +1 -1
  160. package/src/templates/default/app/i18n/ko-KR.ts +1 -1
  161. package/src/templates/default/app/i18n/pt-BR.ts +1 -1
  162. package/src/templates/default/app/i18n/zh-CN.ts +1 -1
  163. package/src/templates/default/app/i18n/zh-TW.ts +1 -1
  164. package/src/templates/default/pnpm-workspace.yaml +1 -0
  165. package/src/templates/workspace-root/pnpm-workspace.yaml +2 -0
@@ -401,20 +401,26 @@ function shouldTraceEngineDetection() {
401
401
  * `/builder/status` resolves them via the same request org context, and the
402
402
  * chat engine picker must not disagree with that card.
403
403
  */
404
- export async function detectEngineFromUserSecrets() {
404
+ export async function detectEngineFromUserSecrets(identity) {
405
405
  const traceLookup = shouldTraceEngineDetection();
406
- let email;
407
- let orgId;
406
+ let email = identity?.userEmail?.trim() || undefined;
407
+ let orgId = identity?.orgId;
408
408
  try {
409
409
  const { getRequestUserEmail, getRequestOrgId } = await import("../../server/request-context.js");
410
- email = getRequestUserEmail();
411
- orgId = getRequestOrgId();
410
+ email ??= getRequestUserEmail();
411
+ if (orgId === undefined)
412
+ orgId = getRequestOrgId();
412
413
  }
413
414
  catch {
415
+ if (!email) {
416
+ if (traceLookup) {
417
+ console.log(`[engine-detect] result=null reason=no-request-context email=(unknown) orgId=(unknown)`);
418
+ }
419
+ return null;
420
+ }
414
421
  if (traceLookup) {
415
- console.log(`[engine-detect] result=null reason=no-request-context email=(unknown) orgId=(unknown)`);
422
+ console.log(`[engine-detect] request context unavailable; using explicit identity email=${email} orgId=${orgId ?? "(none)"}`);
416
423
  }
417
- return null;
418
424
  }
419
425
  if (!email) {
420
426
  if (traceLookup) {
@@ -427,8 +433,9 @@ export async function detectEngineFromUserSecrets() {
427
433
  return false;
428
434
  if (entry.requiredEnvVars.length === 0)
429
435
  return false;
430
- if (entry.name === "builder")
431
- return hasUsableBuilderConnection();
436
+ if (entry.name === "builder") {
437
+ return hasUsableBuilderConnection(identity);
438
+ }
432
439
  for (const key of entry.requiredEnvVars) {
433
440
  // A throw here means the credential store could not be read. Let it
434
441
  // propagate: swallowing it reports "no provider connected" to a user
@@ -520,8 +527,8 @@ async function resolveProviderBaseUrl(envVar) {
520
527
  * keeps that distinction instead of reporting "connect a provider" to a user
521
528
  * whose org-shared keys exist but were unreadable.
522
529
  */
523
- async function hasUsableBuilderConnection() {
524
- const creds = await resolveBuilderCredentialsDetailed();
530
+ async function hasUsableBuilderConnection(identity) {
531
+ const creds = await resolveBuilderCredentialsDetailed(identity);
525
532
  assertCredentialStoreReadable(creds);
526
533
  return Boolean(creds.privateKey && creds.publicKey);
527
534
  }
@@ -567,7 +574,7 @@ async function apiKeyBelongsToDifferentProvider(apiKey, selectedEntry) {
567
574
  }
568
575
  return false;
569
576
  }
570
- async function engineCreateConfigForEntry(entry, apiKey, extra, preferResolvedCredential = false, apiKeyEnvVar) {
577
+ async function engineCreateConfigForEntry(entry, apiKey, extra, preferResolvedCredential = false, apiKeyEnvVar, credentialIdentity) {
571
578
  const safeExtra = { ...(extra ?? {}) };
572
579
  let matchingApiKey = apiKey;
573
580
  // A declared provenance settles the question without inspecting values: a
@@ -632,6 +639,25 @@ async function engineCreateConfigForEntry(entry, apiKey, extra, preferResolvedCr
632
639
  safeExtra.baseUrl = baseUrl;
633
640
  }
634
641
  }
642
+ if (entry.name === "builder" &&
643
+ (credentialIdentity !== undefined || safeExtra.credentials == null)) {
644
+ // Builder authentication is a private/public key pair, not the single
645
+ // provider key carried by ResolveEngineConfig. Capture the scoped pair
646
+ // while the verified request identity is available so a later stream or
647
+ // detached run cannot resolve credentials from the wrong ambient context.
648
+ const creds = await resolveBuilderCredentialsDetailed(credentialIdentity);
649
+ assertCredentialStoreReadable(creds);
650
+ if (credentialIdentity !== undefined ||
651
+ creds.source !== null ||
652
+ creds.lookupFailed) {
653
+ safeExtra.credentials = {
654
+ privateKey: creds.privateKey,
655
+ publicKey: creds.publicKey,
656
+ userId: creds.userId,
657
+ orgName: creds.orgName,
658
+ };
659
+ }
660
+ }
635
661
  return engineCreateConfig(entry, matchingApiKey, safeExtra);
636
662
  }
637
663
  /**
@@ -661,15 +687,16 @@ export function isStoredEngineUsable(stored, entry) {
661
687
  * deploy env vars; this async helper is what request-time routes should use
662
688
  * when deciding whether a stored engine can actually run for the current user.
663
689
  */
664
- export async function isStoredEngineUsableForRequest(stored, entry) {
690
+ export async function isStoredEngineUsableForRequest(stored, entry, options = {}) {
665
691
  if (!isAgentEnginePackageInstalled(entry))
666
692
  return false;
667
693
  if (isAgentEngineSettingConfigured(stored))
668
694
  return true;
669
695
  if (entry.requiredEnvVars.length === 0)
670
696
  return true;
671
- if (entry.name === "builder")
672
- return hasUsableBuilderConnection();
697
+ if (entry.name === "builder") {
698
+ return hasUsableBuilderConnection(options.credentialIdentity);
699
+ }
673
700
  for (const key of entry.requiredEnvVars) {
674
701
  if (!(await resolveUsableProviderSecret(key)))
675
702
  return false;
@@ -692,8 +719,9 @@ export async function isResolvedEngineUsableForRequest(engine, options = {}) {
692
719
  return false;
693
720
  if (entry.requiredEnvVars.length === 0)
694
721
  return true;
695
- if (entry.name === "builder")
696
- return hasUsableBuilderConnection();
722
+ if (entry.name === "builder") {
723
+ return hasUsableBuilderConnection(options.credentialIdentity);
724
+ }
697
725
  if (options.apiKey?.trim()) {
698
726
  const key = entry.requiredEnvVars[0];
699
727
  if (!key)
@@ -776,7 +804,7 @@ export async function getConfiguredEngineNameForRequest(options = {}) {
776
804
  * 7. Default "anthropic" (requires ANTHROPIC_API_KEY)
777
805
  */
778
806
  export async function resolveEngine(config) {
779
- const { engineOption, apiKey, apiKeyEnvVar, model: _model, appId } = config;
807
+ const { engineOption, apiKey, apiKeyEnvVar, model: _model, appId, credentialIdentity, } = config;
780
808
  // 1. Explicit instance passed directly
781
809
  if (engineOption &&
782
810
  typeof engineOption === "object" &&
@@ -792,7 +820,7 @@ export async function resolveEngine(config) {
792
820
  if (!entry)
793
821
  throw new Error(`[agent-engine] Unknown engine: "${name}". Registered: ${[..._registry.keys()].join(", ")}`);
794
822
  assertAgentEnginePackageInstalled(entry);
795
- return entry.create(await engineCreateConfigForEntry(entry, apiKey, engineConfig, false, apiKeyEnvVar));
823
+ return entry.create(await engineCreateConfigForEntry(entry, apiKey, engineConfig, false, apiKeyEnvVar, credentialIdentity));
796
824
  }
797
825
  // 3. Explicit string name from options
798
826
  if (typeof engineOption === "string") {
@@ -800,7 +828,7 @@ export async function resolveEngine(config) {
800
828
  if (!entry)
801
829
  throw new Error(`[agent-engine] Unknown engine: "${engineOption}". Registered: ${[..._registry.keys()].join(", ")}`);
802
830
  assertAgentEnginePackageInstalled(entry);
803
- return entry.create(await engineCreateConfigForEntry(entry, apiKey, undefined, false, apiKeyEnvVar));
831
+ return entry.create(await engineCreateConfigForEntry(entry, apiKey, undefined, false, apiKeyEnvVar, credentialIdentity));
804
832
  }
805
833
  // 4. Env var — explicit engine name override
806
834
  const envEngine = process.env.AGENT_ENGINE;
@@ -808,14 +836,17 @@ export async function resolveEngine(config) {
808
836
  const entry = _registry.get(envEngine);
809
837
  if (entry) {
810
838
  assertAgentEnginePackageInstalled(entry);
811
- return entry.create(await engineCreateConfigForEntry(entry, apiKey, undefined, true, apiKeyEnvVar));
839
+ return entry.create(await engineCreateConfigForEntry(entry, apiKey, undefined, true, apiKeyEnvVar, credentialIdentity));
812
840
  }
813
841
  }
814
842
  const appDefault = await getAgentAppModelDefaultForCurrentRequest(appId);
815
843
  if (appDefault?.engine) {
816
844
  const entry = _registry.get(appDefault.engine);
817
- if (entry && (await isStoredEngineUsableForRequest(appDefault, entry))) {
818
- return entry.create(await engineCreateConfigForEntry(entry, apiKey, undefined, true, apiKeyEnvVar));
845
+ if (entry &&
846
+ (await isStoredEngineUsableForRequest(appDefault, entry, {
847
+ credentialIdentity,
848
+ }))) {
849
+ return entry.create(await engineCreateConfigForEntry(entry, apiKey, undefined, true, apiKeyEnvVar, credentialIdentity));
819
850
  }
820
851
  }
821
852
  let stored = null;
@@ -829,7 +860,7 @@ export async function resolveEngine(config) {
829
860
  // (Builder OAuth callback + "paste your own key" settings flow write here,
830
861
  // not env). Stored/app defaults are checked first so an explicit provider
831
862
  // selection can override a connected Builder account.
832
- const detectedFromUser = await detectEngineFromUserSecrets();
863
+ const detectedFromUser = await detectEngineFromUserSecrets(credentialIdentity);
833
864
  // 6. Settings store — only when the stored row's API key is reachable.
834
865
  // This explicit selection beats automatic Builder detection so users can
835
866
  // switch away from Builder credits by saving/applying their own provider key.
@@ -838,26 +869,29 @@ export async function resolveEngine(config) {
838
869
  const storedConfig = storedRaw?.config;
839
870
  if (storedRaw && typeof storedEngine === "string") {
840
871
  const entry = _registry.get(storedEngine);
841
- if (entry && (await isStoredEngineUsableForRequest(storedRaw, entry))) {
842
- return entry.create(await engineCreateConfigForEntry(entry, apiKey, stripInlineApiKeyConfig(storedConfig), true, apiKeyEnvVar));
872
+ if (entry &&
873
+ (await isStoredEngineUsableForRequest(storedRaw, entry, {
874
+ credentialIdentity,
875
+ }))) {
876
+ return entry.create(await engineCreateConfigForEntry(entry, apiKey, stripInlineApiKeyConfig(storedConfig), true, apiKeyEnvVar, credentialIdentity));
843
877
  }
844
878
  }
845
879
  if (detectedFromUser) {
846
- return detectedFromUser.create(await engineCreateConfigForEntry(detectedFromUser, apiKey, undefined, true, apiKeyEnvVar));
880
+ return detectedFromUser.create(await engineCreateConfigForEntry(detectedFromUser, apiKey, undefined, true, apiKeyEnvVar, credentialIdentity));
847
881
  }
848
882
  // 8. Auto-detect from any provider env var — so just dropping a key in
849
883
  // .env works without also setting AGENT_ENGINE. Skip keys with active
850
884
  // auth-failure markers so a rejected deploy key cannot permanently win.
851
885
  const detected = await detectEngineFromEnvForRequest();
852
886
  if (detected) {
853
- return detected.create(await engineCreateConfigForEntry(detected, apiKey, undefined, true, apiKeyEnvVar));
887
+ return detected.create(await engineCreateConfigForEntry(detected, apiKey, undefined, true, apiKeyEnvVar, credentialIdentity));
854
888
  }
855
889
  // 9. Default: anthropic
856
890
  const anthropicEntry = _registry.get("anthropic");
857
891
  if (!anthropicEntry) {
858
892
  throw new Error("[agent-engine] Default Anthropic engine is not registered. Did builtin.ts fail to load?");
859
893
  }
860
- return anthropicEntry.create(await engineCreateConfigForEntry(anthropicEntry, apiKey, undefined, true, apiKeyEnvVar));
894
+ return anthropicEntry.create(await engineCreateConfigForEntry(anthropicEntry, apiKey, undefined, true, apiKeyEnvVar, credentialIdentity));
861
895
  }
862
896
  /**
863
897
  * Read the user-selected model for an engine from the `agent-engine` setting.
@@ -5978,6 +5978,10 @@ export function createProductionAgentHandler(options) {
5978
5978
  // DIAGNOSTIC-ONLY: bracket engine resolution (Builder credential / app-default
5979
5979
  // settings reads inside resolveEngine).
5980
5980
  workerStep("engine_start");
5981
+ const credentialIdentity = {
5982
+ userEmail: ownerEmail,
5983
+ orgId: getRequestOrgId(),
5984
+ };
5981
5985
  let engine;
5982
5986
  try {
5983
5987
  engine = await resolveEngine({
@@ -5986,6 +5990,7 @@ export function createProductionAgentHandler(options) {
5986
5990
  apiKeyEnvVar: effectiveApiKeyEnvVar,
5987
5991
  model: configuredModel,
5988
5992
  appId: options.appId,
5993
+ credentialIdentity,
5989
5994
  });
5990
5995
  }
5991
5996
  catch {
@@ -5993,6 +5998,7 @@ export function createProductionAgentHandler(options) {
5993
5998
  apiKey: effectiveApiKey,
5994
5999
  apiKeyEnvVar: effectiveApiKeyEnvVar,
5995
6000
  appId: options.appId,
6001
+ credentialIdentity,
5996
6002
  });
5997
6003
  }
5998
6004
  // DIAGNOSTIC-ONLY: engine resolution finished.
@@ -6056,6 +6062,7 @@ export function createProductionAgentHandler(options) {
6056
6062
  console.log(`[agent-chat] resolved engine=${engine.name} model=${effectiveModel} requestModel=${requestModel ?? "(none)"} requestEngine=${requestEngine ?? "(none)"} modelSource=${modelSelectionSource} turnId=${requestTurnId ?? "(none)"}`);
6057
6063
  if (!(await isResolvedEngineUsableForRequest(engine, {
6058
6064
  apiKey: effectiveApiKey,
6065
+ credentialIdentity,
6059
6066
  }))) {
6060
6067
  setResponseHeader(event, "Content-Type", "text/event-stream");
6061
6068
  setResponseHeader(event, "Cache-Control", "no-cache");
@@ -164,10 +164,11 @@ export declare const DEFAULT_COMPLETED_RUN_RETENTION_MS: number;
164
164
  */
165
165
  export declare const DEFAULT_ERRORED_RUN_RETENTION_MS: number;
166
166
  /**
167
- * How recently a terminal run must have started for `/runs/active` to surface
168
- * it. Reconnect after this window won't replay the run — typical real-world
169
- * disconnects resolve in seconds, so 10 minutes is generous while keeping us
170
- * from resurrecting ancient turns when the user reopens an old thread.
167
+ * How recently a terminal run must have completed for `/runs/active` to surface
168
+ * it. This must outlast the durable browser watchdog (13 minutes) and the
169
+ * in-flight reader watchdog (15 minutes) so a quiet event stream can detach
170
+ * and still replay a run that completed while the browser was waiting.
171
+ * Reconnect after this window won't replay the run.
171
172
  */
172
173
  export declare const TERMINAL_RUN_RECONNECT_WINDOW_MS: number;
173
174
  /** Fast poll cadence while a SQL-backed SSE subscription is actively receiving rows. */
@@ -180,12 +180,13 @@ export const DEFAULT_COMPLETED_RUN_RETENTION_MS = 24 * 60 * 60 * 1000;
180
180
  */
181
181
  export const DEFAULT_ERRORED_RUN_RETENTION_MS = 7 * 24 * 60 * 60 * 1000;
182
182
  /**
183
- * How recently a terminal run must have started for `/runs/active` to surface
184
- * it. Reconnect after this window won't replay the run — typical real-world
185
- * disconnects resolve in seconds, so 10 minutes is generous while keeping us
186
- * from resurrecting ancient turns when the user reopens an old thread.
183
+ * How recently a terminal run must have completed for `/runs/active` to surface
184
+ * it. This must outlast the durable browser watchdog (13 minutes) and the
185
+ * in-flight reader watchdog (15 minutes) so a quiet event stream can detach
186
+ * and still replay a run that completed while the browser was waiting.
187
+ * Reconnect after this window won't replay the run.
187
188
  */
188
- export const TERMINAL_RUN_RECONNECT_WINDOW_MS = 10 * 60 * 1000;
189
+ export const TERMINAL_RUN_RECONNECT_WINDOW_MS = 20 * 60 * 1000;
189
190
  /** Fast poll cadence while a SQL-backed SSE subscription is actively receiving rows. */
190
191
  export const SQL_SUBSCRIPTION_ACTIVE_POLL_MS = 125;
191
192
  /** Baseline SQL-backed SSE poll cadence when the run is idle. */
@@ -42,6 +42,7 @@ export interface RunClaudeCodeParticipantOptions {
42
42
  prompt: string;
43
43
  cwd: string;
44
44
  model?: string;
45
+ effort?: string;
45
46
  session?: ClaudeCodeParticipantSession;
46
47
  /** Either the fixed CLI name or an absolute executable path for packaged apps. */
47
48
  command?: string;
@@ -71,6 +72,6 @@ export interface ClaudeCodeSubscriptionStatusOptions {
71
72
  }
72
73
  export declare function readClaudeCodeSubscriptionStatus(options?: ClaudeCodeSubscriptionStatusOptions): Promise<ClaudeCodeSubscriptionStatus>;
73
74
  export declare function runClaudeCodeParticipant(options: RunClaudeCodeParticipantOptions): Promise<ClaudeCodeParticipantResult>;
74
- export declare function buildClaudeCodeParticipantArgs(options: Pick<RunClaudeCodeParticipantOptions, "role" | "model" | "session">): string[];
75
+ export declare function buildClaudeCodeParticipantArgs(options: Pick<RunClaudeCodeParticipantOptions, "role" | "model" | "effort" | "session">): string[];
75
76
  export declare const CLAUDE_CODE_PARTICIPANT_TESTED_VERSION = "2.1.208";
76
77
  export {};
@@ -102,6 +102,9 @@ export function buildClaudeCodeParticipantArgs(options) {
102
102
  const model = readString(options.model);
103
103
  if (model)
104
104
  args.push("--model", model);
105
+ const effort = normalizeClaudeEffort(options.effort);
106
+ if (effort)
107
+ args.push("--effort", effort);
105
108
  const sessionId = readString(options.session?.sessionId);
106
109
  const resumeSessionId = readString(options.session?.resumeSessionId);
107
110
  if (sessionId)
@@ -112,6 +115,22 @@ export function buildClaudeCodeParticipantArgs(options) {
112
115
  args.push("--no-session-persistence");
113
116
  return args;
114
117
  }
118
+ function normalizeClaudeEffort(effort) {
119
+ switch (effort) {
120
+ case "low":
121
+ case "medium":
122
+ case "high":
123
+ case "max":
124
+ return effort;
125
+ case "minimal":
126
+ case "none":
127
+ return "low";
128
+ case "xhigh":
129
+ return "high";
130
+ default:
131
+ return undefined;
132
+ }
133
+ }
115
134
  function collectClaudeCodeParticipantResult(child, options) {
116
135
  const maxEvents = boundedLimit(options.maxEvents, DEFAULT_MAX_EVENTS);
117
136
  const maxStreamBytes = boundedLimit(options.maxStreamBytes, DEFAULT_MAX_STREAM_BYTES);
@@ -14,6 +14,7 @@ import { readAgentsBundleFromFs, generateDevelopmentSkillsPromptBlock, } from ".
14
14
  import { getAmbientOrgId, getAmbientUserEmail, runWithRequestContext, } from "../server/request-context.js";
15
15
  import { isReasoningEffort, } from "../shared/reasoning-effort.js";
16
16
  import { createAgentNativeConfigContext, loadResolvedAgentNativeConfig, } from "../vite/agent-native-config-loader.js";
17
+ import { runClaudeCodeParticipant, } from "./claude-code-participant.js";
17
18
  import { codexMcpConfigArgs, mergeCodeAgentMcpConfig, restrictCodeAgentMcpConfig, } from "./code-agent-mcp-config.js";
18
19
  import { createCodeAgentOutputSmoother } from "./code-agent-output-smoother.js";
19
20
  import { addCodeAgentCommandToAllowlist, appendCodeAgentTranscriptEvent, dequeueCodeAgentFollowUp, getCodeAgentRunRecord, isCodeAgentCommandAllowed, listCodeAgentTranscriptEvents, updateCodeAgentRunRecord, } from "./code-agent-runs.js";
@@ -21,6 +22,8 @@ const DEFAULT_COMMAND_TIMEOUT_MS = 120_000;
21
22
  const MAX_TOOL_OUTPUT_CHARS = 50_000;
22
23
  const MAX_FILE_READ_CHARS = 120_000;
23
24
  const CODEX_CLI_ENGINE_NAME = "codex-cli";
25
+ const CLAUDE_CLI_ENGINE_NAME = "claude-cli";
26
+ const CLAUDE_CLI_DEFAULT_MODEL = "claude-sonnet-5";
24
27
  const RECAP_SOURCE_TOOL_PROFILE = "recap-source";
25
28
  const RECAP_SOURCE_OUTPUT_FILE = "recap-source.json";
26
29
  /**
@@ -110,6 +113,18 @@ export async function executeCodeAgentRun(options) {
110
113
  signal: options.signal,
111
114
  });
112
115
  }
116
+ if (requestedEngine === CLAUDE_CLI_ENGINE_NAME) {
117
+ return executeClaudeCliRun({
118
+ run: existing,
119
+ prompt: executionPrompt,
120
+ model: options.model ?? metadataString(existing, "model"),
121
+ reasoningEffort: options.reasoningEffort ?? metadataReasoningEffort(existing),
122
+ permissionMode: existing.permissionMode ?? "full-auto",
123
+ stdout: options.stdout,
124
+ streamToolOutputToStdout,
125
+ signal: options.signal,
126
+ });
127
+ }
113
128
  const engine = options.engine ?? (await resolveExecutorEngine(requestedEngine));
114
129
  if (!engine) {
115
130
  const message = "No LLM provider key was found. Set ANTHROPIC_API_KEY, OPENAI_API_KEY, GOOGLE_GENERATIVE_AI_API_KEY, another supported provider key, or run `codex login` to use Codex CLI.";
@@ -437,6 +452,214 @@ export function codeAgentMcpInvocationPolicy(permissionMode) {
437
452
  mode: permissionMode === "read-only" ? "read-only" : "allow-all",
438
453
  };
439
454
  }
455
+ async function executeClaudeCliRun(options) {
456
+ const cwd = options.run.cwd || process.cwd();
457
+ const model = normalizeClaudeCliModel(options.model);
458
+ const reasoningEffort = normalizeClaudeCliEffort(options.reasoningEffort);
459
+ const streamToolOutputToStdout = options.streamToolOutputToStdout ??
460
+ process.env.AGENT_NATIVE_CODE_AGENT_STRUCTURED_STDOUT !== "1";
461
+ const assistantText = [];
462
+ appendCodeAgentTranscriptEvent({
463
+ runId: options.run.id,
464
+ kind: "status",
465
+ message: "Starting Claude Code with local Claude authentication.",
466
+ metadata: {
467
+ status: "running",
468
+ phase: "executing",
469
+ engine: CLAUDE_CLI_ENGINE_NAME,
470
+ },
471
+ });
472
+ try {
473
+ const result = await runClaudeCodeParticipant({
474
+ role: options.permissionMode === "full-auto" ? "driver" : "watchdog",
475
+ prompt: buildClaudeCliPrompt(options.run, options.prompt),
476
+ cwd,
477
+ model,
478
+ effort: reasoningEffort,
479
+ signal: options.signal,
480
+ onEvent: (event) => {
481
+ const text = readClaudeParticipantAssistantText(event);
482
+ if (!text)
483
+ return;
484
+ assistantText.push(text);
485
+ if (streamToolOutputToStdout)
486
+ options.stdout?.write(text);
487
+ },
488
+ });
489
+ const finalMessage = readClaudeParticipantResultText(result.events) ??
490
+ (assistantText.join("\n\n").trim() || "Claude Code run completed.");
491
+ appendCodeAgentTranscriptEvent({
492
+ runId: options.run.id,
493
+ kind: "system",
494
+ message: finalMessage,
495
+ metadata: {
496
+ role: "assistant",
497
+ engine: CLAUDE_CLI_ENGINE_NAME,
498
+ model,
499
+ ...(reasoningEffort ? { reasoningEffort } : {}),
500
+ },
501
+ });
502
+ const pendingFollowUp = dequeueCodeAgentFollowUp(options.run.id);
503
+ if (pendingFollowUp) {
504
+ const message = pendingFollowUp.mode === "queued"
505
+ ? "Claude Code run completed; running queued follow-up."
506
+ : "Claude Code run completed; applying steering follow-up.";
507
+ appendCodeAgentTranscriptEvent({
508
+ runId: options.run.id,
509
+ kind: "status",
510
+ message,
511
+ metadata: {
512
+ status: "running",
513
+ phase: "follow-up",
514
+ followUpId: pendingFollowUp.id,
515
+ followUpMode: pendingFollowUp.mode,
516
+ engine: CLAUDE_CLI_ENGINE_NAME,
517
+ },
518
+ });
519
+ if (pendingFollowUp.permissionMode) {
520
+ updateCodeAgentRunRecord(options.run.id, {
521
+ permissionMode: pendingFollowUp.permissionMode,
522
+ });
523
+ }
524
+ return executeCodeAgentRun({
525
+ runId: options.run.id,
526
+ prompt: pendingFollowUp.prompt,
527
+ attachments: pendingFollowUp.attachments ??
528
+ userPromptAttachmentsForEvent(options.run.id, pendingFollowUp.eventId),
529
+ appendUserEvent: false,
530
+ stdout: options.stdout,
531
+ streamToolOutputToStdout: options.streamToolOutputToStdout,
532
+ signal: options.signal,
533
+ });
534
+ }
535
+ appendCodeAgentTranscriptEvent({
536
+ runId: options.run.id,
537
+ kind: "status",
538
+ message: "Claude Code run completed.",
539
+ metadata: {
540
+ status: "completed",
541
+ phase: "complete",
542
+ engine: CLAUDE_CLI_ENGINE_NAME,
543
+ },
544
+ });
545
+ return updateCodeAgentRunRecord(options.run.id, {
546
+ status: "completed",
547
+ phase: "complete",
548
+ needsApproval: false,
549
+ progress: {
550
+ label: "Complete",
551
+ completed: 1,
552
+ total: 1,
553
+ percent: 100,
554
+ },
555
+ metadata: {
556
+ executionCompletedAt: new Date().toISOString(),
557
+ engine: CLAUDE_CLI_ENGINE_NAME,
558
+ model,
559
+ ...(reasoningEffort ? { reasoningEffort } : {}),
560
+ permissionMode: options.permissionMode,
561
+ },
562
+ });
563
+ }
564
+ catch (error) {
565
+ const interrupted = options.signal?.aborted === true;
566
+ const message = error instanceof Error ? error.message : String(error);
567
+ const summary = interrupted
568
+ ? "Claude Code run paused."
569
+ : `Claude Code run failed: ${message}`;
570
+ if (streamToolOutputToStdout)
571
+ options.stdout?.write(`\n${summary}\n`);
572
+ appendCodeAgentTranscriptEvent({
573
+ runId: options.run.id,
574
+ kind: "status",
575
+ message: summary,
576
+ metadata: {
577
+ status: interrupted ? "paused" : "errored",
578
+ phase: interrupted ? "paused" : "error",
579
+ engine: CLAUDE_CLI_ENGINE_NAME,
580
+ },
581
+ });
582
+ return updateCodeAgentRunRecord(options.run.id, {
583
+ status: interrupted ? "paused" : "errored",
584
+ phase: interrupted ? "paused" : "error",
585
+ progress: {
586
+ label: interrupted ? "Paused" : "Error",
587
+ completed: 0,
588
+ total: 1,
589
+ failed: interrupted ? 0 : 1,
590
+ percent: 0,
591
+ },
592
+ metadata: {
593
+ ...(interrupted
594
+ ? { executionPausedAt: new Date().toISOString() }
595
+ : {
596
+ executionError: message,
597
+ executionErroredAt: new Date().toISOString(),
598
+ }),
599
+ engine: CLAUDE_CLI_ENGINE_NAME,
600
+ model,
601
+ },
602
+ });
603
+ }
604
+ }
605
+ function buildClaudeCliPrompt(run, prompt) {
606
+ const permissionMode = run.permissionMode ?? "full-auto";
607
+ const mode = permissionMode === "full-auto" ? "Auto" : "Plan";
608
+ const modeInstruction = mode === "Plan"
609
+ ? "Inspect and explain only. Do not edit files or run mutating commands."
610
+ : "Edit and verify as needed. Do not create, switch, reset, rebase, or stash git branches.";
611
+ const additionalSkillsRoot = process.env.AGENT_NATIVE_CODE_AGENT_SKILLS_ROOT?.trim();
612
+ return [
613
+ `You are running from Agent-Native Code in ${run.cwd || process.cwd()}.`,
614
+ "Follow the repository AGENTS.md and any relevant skill instructions.",
615
+ ...(additionalSkillsRoot
616
+ ? [
617
+ `Additional installed Agent Plugin skills are available under ${additionalSkillsRoot}. Read any relevant SKILL.md files before acting.`,
618
+ ]
619
+ : []),
620
+ `Run mode: ${mode} (${permissionMode}). ${modeInstruction}`,
621
+ "",
622
+ "# User request",
623
+ prompt,
624
+ ].join("\n");
625
+ }
626
+ function normalizeClaudeCliModel(model) {
627
+ const trimmed = model?.trim();
628
+ return !trimmed || trimmed === "auto" || trimmed === CLAUDE_CLI_ENGINE_NAME
629
+ ? CLAUDE_CLI_DEFAULT_MODEL
630
+ : trimmed;
631
+ }
632
+ function normalizeClaudeCliEffort(effort) {
633
+ return effort && effort !== "auto" ? effort : undefined;
634
+ }
635
+ function readClaudeParticipantAssistantText(event) {
636
+ if (event.type !== "assistant")
637
+ return null;
638
+ const message = asRecordValue(event.message);
639
+ const content = message?.content;
640
+ if (!Array.isArray(content))
641
+ return null;
642
+ const text = content
643
+ .filter((part) => isRecordValue(part))
644
+ .map((part) => (typeof part.text === "string" ? part.text : ""))
645
+ .filter(Boolean)
646
+ .join("");
647
+ return text.trim() || null;
648
+ }
649
+ function readClaudeParticipantResultText(events) {
650
+ for (let index = events.length - 1; index >= 0; index -= 1) {
651
+ const result = events[index]?.result;
652
+ if (typeof result === "string" && result.trim())
653
+ return result.trim();
654
+ }
655
+ return null;
656
+ }
657
+ function isRecordValue(value) {
658
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
659
+ }
660
+ function asRecordValue(value) {
661
+ return isRecordValue(value) ? value : null;
662
+ }
440
663
  async function executeCodexCliRun(options) {
441
664
  const cwd = options.run.cwd || process.cwd();
442
665
  const outputDir = fs.mkdtempSync(path.join(os.tmpdir(), "agent-native-code-codex-"));
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * @deprecated `create-workspace` is now an alias for `create`. In current
3
- * versions, `agent-native create <name>` defaults to scaffolding a workspace
4
- * with a multi-select template picker. Use `--standalone` for a single-app
5
- * standalone scaffold.
3
+ * versions, `agent-native create <name>` asks for a starting shape and its
4
+ * Chat/first-party template paths scaffold a workspace. Use `--standalone`
5
+ * for a single-app standalone scaffold.
6
6
  *
7
7
  * This module is kept for backwards compatibility with older docs and
8
8
  * scripts that still invoke `agent-native create-workspace`.
@@ -32,8 +32,9 @@ export interface CreateAppOptions {
32
32
  /**
33
33
  * Main entry for `agent-native create [name]`.
34
34
  *
35
- * Default behavior: scaffold a workspace at <name>/ with a multi-select
36
- * template picker. Use --standalone for the single-app standalone flow.
35
+ * Default behavior: ask for a starting shape, with Chat and first-party
36
+ * templates creating a workspace at <name>/. Use --standalone for the
37
+ * single-app standalone flow.
37
38
  *
38
39
  * If run *inside* an existing workspace, falls through to the add-app
39
40
  * flow that scaffolds one new app under apps/<name>/.