@agent-native/core 0.163.3 → 0.164.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (202) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/app/hooks/use-dashboard-chat-context.ts +29 -0
  3. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +1 -2
  4. package/corpus/templates/analytics/netlify.toml +2 -0
  5. package/corpus/templates/assets/netlify.toml +2 -0
  6. package/corpus/templates/brain/netlify.toml +2 -0
  7. package/corpus/templates/calendar/app/components/calendar/DayView.tsx +2 -1
  8. package/corpus/templates/calendar/changelog/2026-08-19-calendar-page-loads-stay-fast-after-idle-periods.md +6 -0
  9. package/corpus/templates/calendar/netlify.toml +2 -0
  10. package/corpus/templates/chat/netlify.toml +2 -0
  11. package/corpus/templates/clips/app/components/player/player-controls.tsx +3 -3
  12. package/corpus/templates/clips/app/components/player/reaction-emojis.ts +12 -0
  13. package/corpus/templates/clips/app/components/player/reactions-tray.tsx +90 -9
  14. package/corpus/templates/clips/app/components/player/scrubber.tsx +43 -10
  15. package/corpus/templates/clips/app/components/player/video-player.tsx +105 -15
  16. package/corpus/templates/clips/app/routes/_app.settings._index.tsx +1 -2
  17. package/corpus/templates/clips/app/routes/_index.tsx +10 -2
  18. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +13 -8
  19. package/corpus/templates/clips/app/routes/share.$shareId.tsx +15 -8
  20. package/corpus/templates/clips/netlify.toml +2 -0
  21. package/corpus/templates/clips/server/routes/[...page].head.ts +5 -0
  22. package/corpus/templates/content/netlify.toml +2 -0
  23. package/corpus/templates/design/.agents/skills/design-editor-architecture/SKILL.md +103 -0
  24. package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +2 -1
  25. package/corpus/templates/design/AGENTS.md +3 -1
  26. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +3 -2
  27. package/corpus/templates/design/app/components/design/editor/AgentNativeMenuMark.tsx +25 -0
  28. package/corpus/templates/design/app/components/design/editor/DesignBottomToolbar.tsx +411 -0
  29. package/corpus/templates/design/app/components/design/editor/DesignCollaborators.tsx +153 -0
  30. package/corpus/templates/design/app/components/design/editor/DesignWorkspaceRail.tsx +231 -0
  31. package/corpus/templates/design/app/components/design/editor/MakeRealDialog.tsx +223 -0
  32. package/corpus/templates/design/app/components/design/editor/PendingScreenDeletionDialog.tsx +58 -0
  33. package/corpus/templates/design/app/components/design/editor/PendingVisualStyleWarningDialog.tsx +56 -0
  34. package/corpus/templates/design/app/components/design/editor/ReadOnlyEditorPanel.tsx +23 -0
  35. package/corpus/templates/design/app/components/design/editor/SaveTemplateDialog.tsx +161 -0
  36. package/corpus/templates/design/app/components/design/editor/toolbar-controls.tsx +185 -0
  37. package/corpus/templates/design/app/pages/DesignEditor.tsx +20913 -0
  38. package/corpus/templates/design/app/pages/design-editor/command-types.ts +120 -0
  39. package/corpus/templates/design/app/pages/design-editor/commands/README.md +193 -0
  40. package/corpus/templates/design/app/pages/design-editor/commands/add-auto-layout.ts +416 -0
  41. package/corpus/templates/design/app/pages/design-editor/commands/add-screen.ts +112 -0
  42. package/corpus/templates/design/app/pages/design-editor/commands/align-selection.ts +183 -0
  43. package/corpus/templates/design/app/pages/design-editor/commands/apply-design-editor-command.ts +243 -0
  44. package/corpus/templates/design/app/pages/design-editor/commands/apply-file-content-update.ts +236 -0
  45. package/corpus/templates/design/app/pages/design-editor/commands/apply-local-content-update.ts +304 -0
  46. package/corpus/templates/design/app/pages/design-editor/commands/apply-pending-visual-styles-with-agent.ts +376 -0
  47. package/corpus/templates/design/app/pages/design-editor/commands/apply-to-source.ts +132 -0
  48. package/corpus/templates/design/app/pages/design-editor/commands/can-move-layer.ts +117 -0
  49. package/corpus/templates/design/app/pages/design-editor/commands/change-selected-z-index.ts +212 -0
  50. package/corpus/templates/design/app/pages/design-editor/commands/commit-relative-style-delta-to-selected-layers.ts +176 -0
  51. package/corpus/templates/design/app/pages/design-editor/commands/commit-styles-to-selected-layers.ts +175 -0
  52. package/corpus/templates/design/app/pages/design-editor/commands/commit-visual-styles.ts +710 -0
  53. package/corpus/templates/design/app/pages/design-editor/commands/confirm-make-real.ts +99 -0
  54. package/corpus/templates/design/app/pages/design-editor/commands/copy-as-figma-svg.ts +95 -0
  55. package/corpus/templates/design/app/pages/design-editor/commands/copy-selection.ts +156 -0
  56. package/corpus/templates/design/app/pages/design-editor/commands/create-primitive.ts +291 -0
  57. package/corpus/templates/design/app/pages/design-editor/commands/create-screen-frame.ts +131 -0
  58. package/corpus/templates/design/app/pages/design-editor/commands/cross-screen-element-drop.ts +577 -0
  59. package/corpus/templates/design/app/pages/design-editor/commands/delete-files.ts +364 -0
  60. package/corpus/templates/design/app/pages/design-editor/commands/delete-selection.ts +521 -0
  61. package/corpus/templates/design/app/pages/design-editor/commands/detach-instance-menu-action.ts +88 -0
  62. package/corpus/templates/design/app/pages/design-editor/commands/distribute-selection.ts +114 -0
  63. package/corpus/templates/design/app/pages/design-editor/commands/download-all-screens-pdf.ts +116 -0
  64. package/corpus/templates/design/app/pages/design-editor/commands/download-pdf.ts +103 -0
  65. package/corpus/templates/design/app/pages/design-editor/commands/download-svg.ts +159 -0
  66. package/corpus/templates/design/app/pages/design-editor/commands/duplicate-screen.ts +201 -0
  67. package/corpus/templates/design/app/pages/design-editor/commands/duplicate-selection.ts +279 -0
  68. package/corpus/templates/design/app/pages/design-editor/commands/editor-paste.ts +81 -0
  69. package/corpus/templates/design/app/pages/design-editor/commands/enter-hotkey.ts +155 -0
  70. package/corpus/templates/design/app/pages/design-editor/commands/enter-single-screen.ts +168 -0
  71. package/corpus/templates/design/app/pages/design-editor/commands/escape-hotkey.ts +212 -0
  72. package/corpus/templates/design/app/pages/design-editor/commands/frame-selection.ts +119 -0
  73. package/corpus/templates/design/app/pages/design-editor/commands/geometry-commit.ts +169 -0
  74. package/corpus/templates/design/app/pages/design-editor/commands/get-selected-layer-snapshots.ts +177 -0
  75. package/corpus/templates/design/app/pages/design-editor/commands/group-selection.ts +127 -0
  76. package/corpus/templates/design/app/pages/design-editor/commands/iframe-context-menu.ts +121 -0
  77. package/corpus/templates/design/app/pages/design-editor/commands/import-figma-clipboard-into-design.ts +119 -0
  78. package/corpus/templates/design/app/pages/design-editor/commands/layer-marquee-selection-change.ts +163 -0
  79. package/corpus/templates/design/app/pages/design-editor/commands/layer-move-to-screen.ts +331 -0
  80. package/corpus/templates/design/app/pages/design-editor/commands/layer-move.ts +517 -0
  81. package/corpus/templates/design/app/pages/design-editor/commands/layer-rename.ts +247 -0
  82. package/corpus/templates/design/app/pages/design-editor/commands/layer-selection-change.ts +161 -0
  83. package/corpus/templates/design/app/pages/design-editor/commands/mode-change.ts +122 -0
  84. package/corpus/templates/design/app/pages/design-editor/commands/nudge-selection.ts +213 -0
  85. package/corpus/templates/design/app/pages/design-editor/commands/overview-primitive-reparent.ts +318 -0
  86. package/corpus/templates/design/app/pages/design-editor/commands/paste-copied-screens.ts +114 -0
  87. package/corpus/templates/design/app/pages/design-editor/commands/paste-over-selection.ts +70 -0
  88. package/corpus/templates/design/app/pages/design-editor/commands/paste-selection.ts +436 -0
  89. package/corpus/templates/design/app/pages/design-editor/commands/paste-to-replace.ts +135 -0
  90. package/corpus/templates/design/app/pages/design-editor/commands/pasted-image-files.ts +209 -0
  91. package/corpus/templates/design/app/pages/design-editor/commands/persist-frame-geometry-save.ts +113 -0
  92. package/corpus/templates/design/app/pages/design-editor/commands/persist-tweak-save.ts +160 -0
  93. package/corpus/templates/design/app/pages/design-editor/commands/primitive-created.ts +168 -0
  94. package/corpus/templates/design/app/pages/design-editor/commands/record-pending-live-layer-state-edit.ts +149 -0
  95. package/corpus/templates/design/app/pages/design-editor/commands/record-pending-live-structure-edit.ts +200 -0
  96. package/corpus/templates/design/app/pages/design-editor/commands/record-pending-live-text-edit.ts +152 -0
  97. package/corpus/templates/design/app/pages/design-editor/commands/record-pending-visual-style-edit.ts +247 -0
  98. package/corpus/templates/design/app/pages/design-editor/commands/redo.ts +1086 -0
  99. package/corpus/templates/design/app/pages/design-editor/commands/render-png-blob.ts +200 -0
  100. package/corpus/templates/design/app/pages/design-editor/commands/save-file-content.ts +207 -0
  101. package/corpus/templates/design/app/pages/design-editor/commands/screen-element-select.ts +262 -0
  102. package/corpus/templates/design/app/pages/design-editor/commands/screen-text-content-change.ts +216 -0
  103. package/corpus/templates/design/app/pages/design-editor/commands/screen-visual-duplicate-change.ts +108 -0
  104. package/corpus/templates/design/app/pages/design-editor/commands/screen-visual-structure-change.ts +280 -0
  105. package/corpus/templates/design/app/pages/design-editor/commands/screen-visual-style-change.ts +151 -0
  106. package/corpus/templates/design/app/pages/design-editor/commands/send-overview-annotations.ts +88 -0
  107. package/corpus/templates/design/app/pages/design-editor/commands/send-runtime-layer-move-semantic-handoff.ts +122 -0
  108. package/corpus/templates/design/app/pages/design-editor/commands/send-runtime-layer-semantic-handoff.ts +143 -0
  109. package/corpus/templates/design/app/pages/design-editor/commands/send-runtime-layer-state-semantic-handoff.ts +115 -0
  110. package/corpus/templates/design/app/pages/design-editor/commands/start-retry-generation.ts +152 -0
  111. package/corpus/templates/design/app/pages/design-editor/commands/start-sidebar-resize.ts +99 -0
  112. package/corpus/templates/design/app/pages/design-editor/commands/style-change.ts +152 -0
  113. package/corpus/templates/design/app/pages/design-editor/commands/styles-change.ts +155 -0
  114. package/corpus/templates/design/app/pages/design-editor/commands/suggest-auto-layout.ts +125 -0
  115. package/corpus/templates/design/app/pages/design-editor/commands/text-content-change.ts +201 -0
  116. package/corpus/templates/design/app/pages/design-editor/commands/tidy-up.ts +123 -0
  117. package/corpus/templates/design/app/pages/design-editor/commands/toggle-layer-hidden.ts +167 -0
  118. package/corpus/templates/design/app/pages/design-editor/commands/toggle-layer-locked.ts +175 -0
  119. package/corpus/templates/design/app/pages/design-editor/commands/toggle-motion-keyframe.ts +99 -0
  120. package/corpus/templates/design/app/pages/design-editor/commands/tweak-prompt-submit.ts +84 -0
  121. package/corpus/templates/design/app/pages/design-editor/commands/undo.ts +906 -0
  122. package/corpus/templates/design/app/pages/design-editor/commands/ungroup-selection.ts +170 -0
  123. package/corpus/templates/design/app/pages/design-editor/commands/visual-duplicate-change.ts +119 -0
  124. package/corpus/templates/design/app/pages/design-editor/commands/visual-structure-change.ts +274 -0
  125. package/corpus/templates/design/app/pages/design-editor/commands/write-frame-geometry-snapshot.ts +99 -0
  126. package/corpus/templates/design/app/pages/design-editor/derive/design-breakpoints.ts +56 -0
  127. package/corpus/templates/design/app/pages/design-editor/derive/overview-screens.ts +146 -0
  128. package/corpus/templates/design/app/pages/design-editor/domains/use-tweaks.ts +326 -0
  129. package/corpus/templates/design/app/pages/design-editor/editor-constants.ts +54 -0
  130. package/corpus/templates/design/app/pages/design-editor/editor-helpers.ts +183 -0
  131. package/corpus/templates/design/app/pages/design-editor/effects/adopt-db-file-content.ts +270 -0
  132. package/corpus/templates/design/app/pages/design-editor/effects/mirror-selection-to-agent-chat.ts +134 -0
  133. package/corpus/templates/design/app/pages/design-editor/effects/motion-autosave.ts +335 -0
  134. package/corpus/templates/design/app/pages/design-editor/effects/observe-collab-text.ts +183 -0
  135. package/corpus/templates/design/app/pages/design-editor/effects/publish-agent-selection-context.ts +252 -0
  136. package/corpus/templates/design/app/pages/design-editor/effects/resume-pending-generation.ts +178 -0
  137. package/corpus/templates/design/app/pages/design-editor/effects/seed-collab-content.ts +157 -0
  138. package/corpus/templates/design/app/pages/design-editor/preview-html.ts +19 -0
  139. package/corpus/templates/design/netlify.toml +2 -0
  140. package/corpus/templates/design/shared/board-file.ts +2 -1
  141. package/corpus/templates/dispatch/netlify.toml +2 -0
  142. package/corpus/templates/forms/netlify.toml +2 -0
  143. package/corpus/templates/mail/netlify.toml +2 -0
  144. package/corpus/templates/plan/netlify.toml +2 -0
  145. package/corpus/templates/slides/netlify.toml +2 -0
  146. package/dist/agent/engine/builder-engine.d.ts +6 -4
  147. package/dist/agent/engine/builder-engine.js +91 -26
  148. package/dist/agent/engine/registry.js +16 -0
  149. package/dist/cli/code-agent-agent-tools.d.ts +2 -0
  150. package/dist/cli/code-agent-agent-tools.js +188 -0
  151. package/dist/cli/code-agent-collaboration.d.ts +34 -0
  152. package/dist/cli/code-agent-collaboration.js +104 -0
  153. package/dist/cli/code-agent-executor.js +6 -0
  154. package/dist/cli/code-agent-schedules.d.ts +54 -0
  155. package/dist/cli/code-agent-schedules.js +198 -0
  156. package/dist/client/AgentPanel.js +1 -1
  157. package/dist/client/ConnectBuilderCard.js +5 -2
  158. package/dist/client/EnvironmentBadge.js +6 -5
  159. package/dist/client/chat/run-recovery.js +3 -2
  160. package/dist/client/chat/tool-call-display.js +2 -3
  161. package/dist/client/components/CodeRequiredDialog.js +4 -2
  162. package/dist/client/i18n.js +5 -4
  163. package/dist/client/settings/SettingsPanel.js +1 -1
  164. package/dist/client/settings/VoiceTranscriptionSection.js +1 -1
  165. package/dist/client/settings/useBuilderStatus.d.ts +6 -1
  166. package/dist/client/settings/useBuilderStatus.js +22 -33
  167. package/dist/client/transcription/BuilderTranscriptionCta.js +1 -1
  168. package/dist/code-agents/index.d.ts +2 -0
  169. package/dist/code-agents/index.js +2 -0
  170. package/dist/collab/routes.d.ts +1 -1
  171. package/dist/localization/shared.d.ts +2 -0
  172. package/dist/localization/shared.js +5 -0
  173. package/dist/mcp-client/oauth-client.d.ts +1 -0
  174. package/dist/mcp-client/oauth-client.js +50 -1
  175. package/dist/observability/routes.d.ts +1 -1
  176. package/dist/provider-api/actions/provider-api.d.ts +2 -2
  177. package/dist/resources/handlers.d.ts +1 -1
  178. package/dist/server/auth-marketing-locales.d.ts +7 -0
  179. package/dist/server/auth-marketing-locales.js +1322 -0
  180. package/dist/server/auth-marketing.d.ts +1 -0
  181. package/dist/server/auth-marketing.js +35 -6
  182. package/dist/server/auth.js +4 -17
  183. package/dist/server/builder-browser.d.ts +22 -0
  184. package/dist/server/builder-browser.js +77 -1
  185. package/dist/server/builder-oauth.d.ts +50 -0
  186. package/dist/server/builder-oauth.js +263 -0
  187. package/dist/server/core-routes-plugin.d.ts +11 -0
  188. package/dist/server/core-routes-plugin.js +291 -415
  189. package/dist/server/credential-provider.d.ts +1 -1
  190. package/dist/server/credential-provider.js +1 -1
  191. package/dist/server/csrf.js +2 -2
  192. package/dist/server/onboarding-html.js +52 -120
  193. package/dist/server/realtime-token.d.ts +1 -1
  194. package/dist/server/transcribe-voice.d.ts +1 -1
  195. package/dist/settings/store.d.ts +9 -0
  196. package/dist/settings/store.js +19 -0
  197. package/dist/templates/chat/netlify.toml +2 -0
  198. package/dist/vite/client.js +35 -37
  199. package/docs/content/deployment.mdx +2 -0
  200. package/docs/content/multi-app-workspace.mdx +1 -1
  201. package/package.json +2 -2
  202. package/src/templates/chat/netlify.toml +2 -0
@@ -8,10 +8,12 @@
8
8
  * concurrency) into structured stop events that carry an upgrade URL when
9
9
  * the chat UI needs to prompt the user to upgrade.
10
10
  *
11
- * Credentials come from the gateway lane (`resolveBuilderGatewayCredentials`):
12
- * the user's own Builder connection when they have one, otherwise the
13
- * deployment's Builder-credits pair. Base URL is overridable via
14
- * BUILDER_GATEWAY_BASE_URL.
11
+ * Interactive users authenticate with Builder OAuth. Existing connections may
12
+ * keep using BUILDER_PRIVATE_KEY + BUILDER_PUBLIC_KEY until they reconnect.
13
+ * When neither is present, credentials come from the gateway lane
14
+ * (`resolveBuilderGatewayCredentials`): the user's own Builder connection,
15
+ * otherwise the deployment's Builder-credits pair. Base URL is overridable
16
+ * via BUILDER_GATEWAY_BASE_URL.
15
17
  */
16
18
  import { type BuilderGatewayLane } from "../../server/credential-provider.js";
17
19
  import type { AgentEngine, EngineCapabilities } from "./types.js";
@@ -8,13 +8,17 @@
8
8
  * concurrency) into structured stop events that carry an upgrade URL when
9
9
  * the chat UI needs to prompt the user to upgrade.
10
10
  *
11
- * Credentials come from the gateway lane (`resolveBuilderGatewayCredentials`):
12
- * the user's own Builder connection when they have one, otherwise the
13
- * deployment's Builder-credits pair. Base URL is overridable via
14
- * BUILDER_GATEWAY_BASE_URL.
11
+ * Interactive users authenticate with Builder OAuth. Existing connections may
12
+ * keep using BUILDER_PRIVATE_KEY + BUILDER_PUBLIC_KEY until they reconnect.
13
+ * When neither is present, credentials come from the gateway lane
14
+ * (`resolveBuilderGatewayCredentials`): the user's own Builder connection,
15
+ * otherwise the deployment's Builder-credits pair. Base URL is overridable
16
+ * via BUILDER_GATEWAY_BASE_URL.
15
17
  */
18
+ import { BUILDER_OAUTH_SCOPE, hasBuilderOAuthSession, markBuilderOAuthReconnectRequired, resolveBuilderOAuthRequestAccess, } from "../../server/builder-oauth.js";
16
19
  import { captureError } from "../../server/capture-error.js";
17
20
  import { clearBuilderGatewayAuthFailure, isBuilderGatewayDeployConfigured, resolveBuilderGatewayCredentialsDetailed, getBuilderGatewayBaseUrl, recordBuilderGatewayAuthFailure, } from "../../server/credential-provider.js";
21
+ import { getRequestUserEmail } from "../../server/request-context.js";
18
22
  import { applyBuilderUtmTrackingParams } from "../../shared/builder-link-tracking.js";
19
23
  import { isGPTReasoningModel, normalizeReasoningEffortForModel, } from "../../shared/reasoning-effort.js";
20
24
  import { isInBackgroundFunctionRuntime } from "../durable-background.js";
@@ -127,11 +131,35 @@ class BuilderEngine {
127
131
  async *stream(opts) {
128
132
  const creds = this.configuredCredentials ??
129
133
  (await resolveBuilderGatewayCredentialsDetailed());
130
- const creditsLane = isBuilderCreditsLane(creds);
131
- const authHeader = creds.privateKey ? `Bearer ${creds.privateKey}` : null;
134
+ const ownerEmail = getRequestUserEmail();
135
+ let oauthAccess = null;
136
+ let hasStoredOAuth = false;
137
+ if (ownerEmail) {
138
+ hasStoredOAuth = await hasBuilderOAuthSession(ownerEmail);
139
+ if (hasStoredOAuth) {
140
+ try {
141
+ oauthAccess = await resolveBuilderOAuthRequestAccess({
142
+ ownerEmail,
143
+ requiredScope: BUILDER_OAUTH_SCOPE,
144
+ });
145
+ }
146
+ catch {
147
+ // coercion-ok: unusable OAuth custody must not fall back to legacy keys.
148
+ oauthAccess = null;
149
+ }
150
+ }
151
+ }
152
+ // Prefer OAuth when present. If OAuth custody exists but is unusable,
153
+ // do not silently fall back to a legacy private key for the same user.
154
+ const authHeader = oauthAccess
155
+ ? `Bearer ${oauthAccess.accessToken}`
156
+ : !hasStoredOAuth && creds.privateKey
157
+ ? `Bearer ${creds.privateKey}`
158
+ : null;
132
159
  const spaceId = creds.publicKey;
133
- const builderUserId = creds.userId;
134
- if (!authHeader || !spaceId) {
160
+ const builderUserId = oauthAccess ? undefined : creds.userId;
161
+ const creditsLane = oauthAccess ? false : isBuilderCreditsLane(creds);
162
+ if (!authHeader || (!oauthAccess && !spaceId)) {
135
163
  yield gatewayErrorStop({
136
164
  error: LLM_MISSING_CREDENTIALS_MESSAGE,
137
165
  errorCode: LLM_MISSING_CREDENTIALS_ERROR_CODE,
@@ -246,7 +274,10 @@ class BuilderEngine {
246
274
  };
247
275
  const gatewayBaseUrl = getBuilderGatewayBaseUrl();
248
276
  const gatewayUrl = new URL("messages", gatewayBaseUrl.endsWith("/") ? gatewayBaseUrl : `${gatewayBaseUrl}/`);
249
- gatewayUrl.searchParams.set("apiKey", spaceId);
277
+ // OAuth tokens carry Space identity in the JWT `org` claim. Sending a
278
+ // client-supplied apiKey/public key is legacy private-key behavior only.
279
+ if (spaceId && !oauthAccess)
280
+ gatewayUrl.searchParams.set("apiKey", spaceId);
250
281
  const orgLabel = creds.orgName || "unknown-org";
251
282
  const tStart = Date.now();
252
283
  console.log(`[builder-engine] → POST ${gatewayUrl.origin}${gatewayUrl.pathname} model=${model} tools=${tools.length} org=${orgLabel}`);
@@ -260,7 +291,9 @@ class BuilderEngine {
260
291
  headers: {
261
292
  "Content-Type": "application/json",
262
293
  Authorization: authHeader,
263
- "x-builder-api-key": spaceId,
294
+ ...(spaceId && !oauthAccess
295
+ ? { "x-builder-api-key": spaceId }
296
+ : {}),
264
297
  ...getBuilderGatewayRequestHeaders(),
265
298
  ...(builderUserId ? { "x-builder-user-id": builderUserId } : {}),
266
299
  },
@@ -288,25 +321,32 @@ class BuilderEngine {
288
321
  }
289
322
  console.log(`[builder-engine] ← ${response.status} ${response.statusText} in ${Date.now() - tStart}ms`);
290
323
  if (!response.ok) {
291
- yield* emitHttpError(response, { creditsLane, requestShape });
324
+ yield* emitHttpError(response, {
325
+ creditsLane,
326
+ requestShape,
327
+ recordLegacyCredentialFailure: !oauthAccess,
328
+ });
292
329
  return;
293
330
  }
294
331
  // A successful gateway call proves the connected credentials are valid
295
332
  // again. Clear any prior auth-failure marker so status / chat-card
296
333
  // surfaces stop flagging the connection as broken. This is the only
297
334
  // self-healing path for workspace/env-managed credentials, which never
298
- // flow through writeBuilderCredentials.
299
- try {
300
- const creds = this.configuredCredentials ??
301
- (await resolveBuilderGatewayCredentialsDetailed());
302
- await clearBuilderGatewayAuthFailure({
303
- privateKey: creds.privateKey,
304
- publicKey: creds.publicKey,
305
- });
306
- }
307
- catch {
308
- // Marker clearing is best-effort; a stale marker just means the user
309
- // sees "reconnect Builder" until the next successful call clears it.
335
+ // flow through writeBuilderCredentials. OAuth failures are not tracked
336
+ // with the legacy private-key fingerprint marker.
337
+ if (!oauthAccess) {
338
+ try {
339
+ const legacyCreds = this.configuredCredentials ??
340
+ (await resolveBuilderGatewayCredentialsDetailed());
341
+ await clearBuilderGatewayAuthFailure({
342
+ privateKey: legacyCreds.privateKey,
343
+ publicKey: legacyCreds.publicKey,
344
+ });
345
+ }
346
+ catch {
347
+ // coercion-ok: clearing the legacy auth-failure marker is best-effort;
348
+ // a stale marker only keeps the reconnect CTA until the next success.
349
+ }
310
350
  }
311
351
  const contentType = response.headers.get("content-type") ?? "";
312
352
  if (contentType.includes("text/html")) {
@@ -341,6 +381,7 @@ class BuilderEngine {
341
381
  gatewayUrl,
342
382
  requestStartedAt: tStart,
343
383
  requestShape,
384
+ recordLegacyCredentialFailure: !oauthAccess,
344
385
  });
345
386
  }
346
387
  finally {
@@ -398,6 +439,19 @@ function gatewayErrorStop(details, creditsLane, requestShape) {
398
439
  ...retry,
399
440
  };
400
441
  }
442
+ async function recordAuthFailureForCurrentLane(opts) {
443
+ if (opts.recordLegacyCredentialFailure !== false) {
444
+ await recordBuilderGatewayAuthFailure({
445
+ status: opts.status,
446
+ code: opts.code,
447
+ message: opts.message,
448
+ });
449
+ return;
450
+ }
451
+ const ownerEmail = getRequestUserEmail();
452
+ if (ownerEmail)
453
+ await markBuilderOAuthReconnectRequired(ownerEmail);
454
+ }
401
455
  async function* emitHttpError(response, opts) {
402
456
  const status = response.status;
403
457
  // Read the body once as text and then try to parse — calling `.json()`
@@ -432,7 +486,12 @@ async function* emitHttpError(response, opts) {
432
486
  return;
433
487
  }
434
488
  if (status === 401 || code === "unauthorized") {
435
- await recordBuilderGatewayAuthFailure({ status, code, message });
489
+ await recordAuthFailureForCurrentLane({
490
+ recordLegacyCredentialFailure: opts.recordLegacyCredentialFailure,
491
+ status,
492
+ code,
493
+ message,
494
+ });
436
495
  yield stop({
437
496
  error: "Builder authentication failed. Reconnect Builder (free tier available) via Settings.",
438
497
  errorCode: "builder_auth_error",
@@ -440,7 +499,12 @@ async function* emitHttpError(response, opts) {
440
499
  return;
441
500
  }
442
501
  if (status === 403 && isBuilderCredentialAuthError(message)) {
443
- await recordBuilderGatewayAuthFailure({ status, code, message });
502
+ await recordAuthFailureForCurrentLane({
503
+ recordLegacyCredentialFailure: opts.recordLegacyCredentialFailure,
504
+ status,
505
+ code,
506
+ message,
507
+ });
444
508
  yield stop({
445
509
  error: "Builder authentication failed. Reconnect Builder (free tier available) via Settings.",
446
510
  errorCode: "builder_auth_error",
@@ -709,7 +773,8 @@ async function* parseJsonlStream(reader, model, captureContext = {}) {
709
773
  classifyTerminalErrorCode(String(errMsg))));
710
774
  console.error(`[builder-engine] stop reason=error model=${model} code=${errCode ?? "(none)"} requestId=${gatewayRequestId ?? "(none)"} error=${errMsg}`);
711
775
  if (isCredentialAuthError) {
712
- await recordBuilderGatewayAuthFailure({
776
+ await recordAuthFailureForCurrentLane({
777
+ recordLegacyCredentialFailure: captureContext.recordLegacyCredentialFailure,
713
778
  code: typeof gatewayErrCode === "string" ? gatewayErrCode : errCode,
714
779
  message: String(errMsg),
715
780
  });
@@ -9,7 +9,9 @@
9
9
  */
10
10
  import { createRequire } from "node:module";
11
11
  import { getAppConfig } from "../../app-config/index.js";
12
+ import { BUILDER_OAUTH_SCOPE, hasBuilderOAuthSession, resolveBuilderOAuthRequestAccess, } from "../../server/builder-oauth.js";
12
13
  import { assertCredentialStoreReadable, canUseDeployCredentialFallbackForRequest, getBuilderCredentialAuthFailure, getProviderCredentialAuthFailure, readDeployCredentialEnv, resolveBuilderCredentialsDetailed, resolveBuilderGatewayCredentialsDetailed, resolveSecret, } from "../../server/credential-provider.js";
14
+ import { getRequestUserEmail } from "../../server/request-context.js";
13
15
  import { getSetting } from "../../settings/store.js";
14
16
  import { getAgentAppModelDefaultForCurrentRequest } from "../app-model-defaults.js";
15
17
  import { OLLAMA_BASE_URL_ENV_VAR, OPENAI_BASE_URL_ENV_VAR, } from "./openai-compatible-endpoint.js";
@@ -556,6 +558,20 @@ async function resolveProviderBaseUrl(envVar) {
556
558
  * whose org-shared keys exist but were unreadable.
557
559
  */
558
560
  async function hasUsableBuilderConnection(identity) {
561
+ const ownerEmail = identity?.userEmail?.trim().toLowerCase() || getRequestUserEmail();
562
+ if (ownerEmail && (await hasBuilderOAuthSession(ownerEmail))) {
563
+ try {
564
+ return Boolean(await resolveBuilderOAuthRequestAccess({
565
+ ownerEmail,
566
+ requiredScope: BUILDER_OAUTH_SCOPE,
567
+ }));
568
+ }
569
+ catch {
570
+ // coercion-ok: custody present but unusable is "not usable", not absent;
571
+ // reconnect UX is owned by /builder/status, not this boolean probe.
572
+ return false;
573
+ }
574
+ }
559
575
  const creds = await resolveBuilderCredentialsDetailed(identity);
560
576
  assertCredentialStoreReadable(creds);
561
577
  return Boolean(creds.privateKey && creds.publicKey);
@@ -0,0 +1,2 @@
1
+ import type { ActionEntry } from "../agent/production-agent.js";
2
+ export declare function createCodeAgentAgentTools(runId: string, cwd: string, runTitle?: string): Record<string, ActionEntry>;
@@ -0,0 +1,188 @@
1
+ import { createCodeAgentThread, listCodeAgentThreadSummaries, messageCodeAgentThread, } from "./code-agent-collaboration.js";
2
+ import { createCodeAgentSchedule, deleteCodeAgentSchedule, listCodeAgentSchedules, updateCodeAgentSchedule, } from "./code-agent-schedules.js";
3
+ export function createCodeAgentAgentTools(runId, cwd, runTitle) {
4
+ return {
5
+ "manage-schedules": {
6
+ tool: {
7
+ description: "Create and manage durable local desktop schedules. A global schedule starts a new Agent-Native Code thread on every interval. A thread schedule queues a message into an existing thread. Use intervalMinutes for intervals such as 360 for every 6 hours.",
8
+ parameters: {
9
+ type: "object",
10
+ properties: {
11
+ action: {
12
+ type: "string",
13
+ enum: ["create", "list", "update", "delete"],
14
+ description: "The schedule operation.",
15
+ },
16
+ scheduleId: {
17
+ type: "string",
18
+ description: "Required for update or delete.",
19
+ },
20
+ name: {
21
+ type: "string",
22
+ description: "Human-readable schedule name.",
23
+ },
24
+ prompt: {
25
+ type: "string",
26
+ description: "Instructions sent to the new or existing thread.",
27
+ },
28
+ scope: {
29
+ type: "string",
30
+ enum: ["global", "thread"],
31
+ description: "Whether to start a new thread or message an existing one.",
32
+ },
33
+ targetRunId: {
34
+ type: "string",
35
+ description: "Existing thread id when scope is thread.",
36
+ },
37
+ intervalMinutes: {
38
+ type: "number",
39
+ description: "Whole minutes between runs, for example 360 is every 6 hours.",
40
+ },
41
+ enabled: {
42
+ type: "boolean",
43
+ description: "Whether the schedule is active.",
44
+ },
45
+ },
46
+ required: ["action"],
47
+ },
48
+ },
49
+ run: async (args) => {
50
+ try {
51
+ switch (args.action) {
52
+ case "list":
53
+ return JSON.stringify({
54
+ ok: true,
55
+ schedules: listCodeAgentSchedules(),
56
+ });
57
+ case "create": {
58
+ const schedule = createCodeAgentSchedule({
59
+ name: String(args.name ?? ""),
60
+ prompt: String(args.prompt ?? ""),
61
+ scope: args.scope === "thread" ? "thread" : "global",
62
+ targetRunId: typeof args.targetRunId === "string"
63
+ ? args.targetRunId
64
+ : undefined,
65
+ intervalMinutes: Number(args.intervalMinutes),
66
+ enabled: args.enabled !== false,
67
+ createdByRunId: runId,
68
+ });
69
+ return JSON.stringify({ ok: true, schedule });
70
+ }
71
+ case "update": {
72
+ const scheduleId = String(args.scheduleId ?? "").trim();
73
+ if (!scheduleId)
74
+ return "Error: scheduleId is required for update.";
75
+ const schedule = updateCodeAgentSchedule(scheduleId, {
76
+ ...(typeof args.name === "string" ? { name: args.name } : {}),
77
+ ...(typeof args.prompt === "string"
78
+ ? { prompt: args.prompt }
79
+ : {}),
80
+ ...(args.scope === "global" || args.scope === "thread"
81
+ ? { scope: args.scope }
82
+ : {}),
83
+ ...(typeof args.targetRunId === "string"
84
+ ? { targetRunId: args.targetRunId }
85
+ : {}),
86
+ ...(args.intervalMinutes !== undefined
87
+ ? { intervalMinutes: Number(args.intervalMinutes) }
88
+ : {}),
89
+ ...(typeof args.enabled === "boolean"
90
+ ? { enabled: args.enabled }
91
+ : {}),
92
+ });
93
+ return schedule
94
+ ? JSON.stringify({ ok: true, schedule })
95
+ : `Error: schedule not found: ${scheduleId}`;
96
+ }
97
+ case "delete": {
98
+ const scheduleId = String(args.scheduleId ?? "").trim();
99
+ if (!scheduleId)
100
+ return "Error: scheduleId is required for delete.";
101
+ return JSON.stringify({
102
+ ok: deleteCodeAgentSchedule(scheduleId),
103
+ scheduleId,
104
+ });
105
+ }
106
+ default:
107
+ return "Error: action must be create, list, update, or delete.";
108
+ }
109
+ }
110
+ catch (error) {
111
+ return `Error: ${error instanceof Error ? error.message : String(error)}`;
112
+ }
113
+ },
114
+ },
115
+ "manage-agent-threads": {
116
+ tool: {
117
+ description: "List threads, create a new Agent-Native Code thread, or queue a message to another thread. Messages are durable and include a source-agent marker in the recipient transcript.",
118
+ parameters: {
119
+ type: "object",
120
+ properties: {
121
+ action: {
122
+ type: "string",
123
+ enum: ["list", "create", "message"],
124
+ description: "The thread operation.",
125
+ },
126
+ query: {
127
+ type: "string",
128
+ description: "Optional title/id filter for list.",
129
+ },
130
+ title: { type: "string", description: "Title for a new thread." },
131
+ prompt: {
132
+ type: "string",
133
+ description: "Initial prompt or message.",
134
+ },
135
+ targetRunId: {
136
+ type: "string",
137
+ description: "Recipient thread id when action is message.",
138
+ },
139
+ },
140
+ required: ["action"],
141
+ },
142
+ },
143
+ run: async (args) => {
144
+ try {
145
+ switch (args.action) {
146
+ case "list":
147
+ return JSON.stringify({
148
+ ok: true,
149
+ threads: listCodeAgentThreadSummaries(typeof args.query === "string" ? args.query : undefined),
150
+ });
151
+ case "create": {
152
+ const result = createCodeAgentThread({
153
+ title: typeof args.title === "string" ? args.title : undefined,
154
+ prompt: String(args.prompt ?? ""),
155
+ cwd,
156
+ sourceRunId: runId,
157
+ sourceRunTitle: runTitle,
158
+ });
159
+ return JSON.stringify({
160
+ ok: true,
161
+ thread: result.run,
162
+ event: result.event,
163
+ });
164
+ }
165
+ case "message": {
166
+ const result = messageCodeAgentThread({
167
+ targetRunId: String(args.targetRunId ?? ""),
168
+ prompt: String(args.prompt ?? ""),
169
+ sourceRunId: runId,
170
+ sourceRunTitle: runTitle,
171
+ });
172
+ return JSON.stringify({
173
+ ok: true,
174
+ thread: result.run,
175
+ event: result.event,
176
+ });
177
+ }
178
+ default:
179
+ return "Error: action must be list, create, or message.";
180
+ }
181
+ }
182
+ catch (error) {
183
+ return `Error: ${error instanceof Error ? error.message : String(error)}`;
184
+ }
185
+ },
186
+ },
187
+ };
188
+ }
@@ -0,0 +1,34 @@
1
+ import { type CodeAgentPermissionMode, type CodeAgentRunRecord, type CodeAgentTranscriptEvent } from "./code-agent-runs.js";
2
+ export interface CodeAgentThreadSummary {
3
+ id: string;
4
+ goalId: string;
5
+ title: string;
6
+ status: CodeAgentRunRecord["status"];
7
+ updatedAt: string;
8
+ source?: string;
9
+ }
10
+ export interface CreateCodeAgentThreadInput {
11
+ title?: string;
12
+ prompt: string;
13
+ cwd: string;
14
+ permissionMode?: CodeAgentPermissionMode;
15
+ sourceRunId: string;
16
+ sourceRunTitle?: string;
17
+ }
18
+ export interface CreateCodeAgentThreadResult {
19
+ run: CodeAgentRunRecord;
20
+ event: CodeAgentTranscriptEvent;
21
+ }
22
+ export interface MessageCodeAgentThreadInput {
23
+ targetRunId: string;
24
+ prompt: string;
25
+ sourceRunId: string;
26
+ sourceRunTitle?: string;
27
+ }
28
+ export interface MessageCodeAgentThreadResult {
29
+ run: CodeAgentRunRecord;
30
+ event: CodeAgentTranscriptEvent;
31
+ }
32
+ export declare function listCodeAgentThreadSummaries(query?: string): CodeAgentThreadSummary[];
33
+ export declare function createCodeAgentThread(input: CreateCodeAgentThreadInput): CreateCodeAgentThreadResult;
34
+ export declare function messageCodeAgentThread(input: MessageCodeAgentThreadInput): MessageCodeAgentThreadResult;
@@ -0,0 +1,104 @@
1
+ import { appendCodeAgentTranscriptEvent, createCodeAgentRunRecord, getCodeAgentRunRecord, listCodeAgentRunRecords, queueCodeAgentFollowUp, updateCodeAgentRunRecord, } from "./code-agent-runs.js";
2
+ export function listCodeAgentThreadSummaries(query) {
3
+ const normalizedQuery = query?.trim().toLowerCase();
4
+ return listCodeAgentRunRecords()
5
+ .filter((run) => {
6
+ if (!normalizedQuery)
7
+ return true;
8
+ return `${run.title} ${run.id}`.toLowerCase().includes(normalizedQuery);
9
+ })
10
+ .map((run) => ({
11
+ id: run.id,
12
+ goalId: run.goalId,
13
+ title: run.title,
14
+ status: run.status,
15
+ updatedAt: run.updatedAt,
16
+ source: typeof run.metadata?.source === "string"
17
+ ? run.metadata.source
18
+ : undefined,
19
+ }));
20
+ }
21
+ export function createCodeAgentThread(input) {
22
+ const prompt = requiredText(input.prompt, "Thread prompt");
23
+ const title = requiredText(input.title ?? titleFromPrompt(prompt), "Thread title");
24
+ const now = new Date().toISOString();
25
+ const run = createCodeAgentRunRecord({
26
+ goalId: "task",
27
+ title,
28
+ subtitle: "Created by another agent",
29
+ status: "queued",
30
+ phase: "queued",
31
+ permissionMode: input.permissionMode ?? "full-auto",
32
+ cwd: input.cwd,
33
+ metadata: {
34
+ source: "agent",
35
+ sourceRunId: input.sourceRunId,
36
+ ...(input.sourceRunTitle ? { sourceRunTitle: input.sourceRunTitle } : {}),
37
+ createdByAgent: true,
38
+ startRequested: true,
39
+ createdAtByAgent: now,
40
+ initialPrompt: prompt,
41
+ },
42
+ });
43
+ const event = appendCodeAgentTranscriptEvent({
44
+ runId: run.id,
45
+ kind: "user",
46
+ message: prompt,
47
+ metadata: {
48
+ source: "agent",
49
+ sourceRunId: input.sourceRunId,
50
+ ...(input.sourceRunTitle ? { sourceRunTitle: input.sourceRunTitle } : {}),
51
+ threadCreated: true,
52
+ },
53
+ });
54
+ return { run, event };
55
+ }
56
+ export function messageCodeAgentThread(input) {
57
+ const prompt = requiredText(input.prompt, "Thread message");
58
+ if (!input.targetRunId || input.targetRunId === input.sourceRunId) {
59
+ throw new Error("Choose a different target thread.");
60
+ }
61
+ const target = getCodeAgentRunRecord(input.targetRunId);
62
+ if (!target)
63
+ throw new Error(`Thread not found: ${input.targetRunId}`);
64
+ const event = appendCodeAgentTranscriptEvent({
65
+ runId: target.id,
66
+ kind: "user",
67
+ message: prompt,
68
+ metadata: {
69
+ source: "agent",
70
+ sourceRunId: input.sourceRunId,
71
+ ...(input.sourceRunTitle ? { sourceRunTitle: input.sourceRunTitle } : {}),
72
+ },
73
+ });
74
+ const followUp = queueCodeAgentFollowUp({
75
+ runId: target.id,
76
+ prompt,
77
+ mode: "queued",
78
+ eventId: event.id,
79
+ source: "agent",
80
+ });
81
+ if (!followUp)
82
+ throw new Error(`Could not queue a message for ${target.id}`);
83
+ const run = updateCodeAgentRunRecord(target.id, {
84
+ metadata: {
85
+ startRequested: true,
86
+ lastMessageSource: "agent",
87
+ lastMessageSourceRunId: input.sourceRunId,
88
+ },
89
+ }) ?? target;
90
+ return { run, event };
91
+ }
92
+ function requiredText(value, label) {
93
+ const text = typeof value === "string" ? value.trim() : "";
94
+ if (!text)
95
+ throw new Error(`${label} is required.`);
96
+ return text.slice(0, 8_000);
97
+ }
98
+ function titleFromPrompt(prompt) {
99
+ const firstLine = prompt.split(/\r?\n/, 1)[0]?.trim() ?? "";
100
+ const normalized = firstLine.replace(/^\/+[a-z0-9-]+\s*/i, "").trim();
101
+ if (!normalized)
102
+ return "Agent-created thread";
103
+ return normalized.length > 72 ? `${normalized.slice(0, 69)}...` : normalized;
104
+ }
@@ -14,6 +14,7 @@ import { buildMergedConfig, McpClientManager, mcpToolsToActionEntries, } from ".
14
14
  import { readAgentsBundleFromFs, generateDevelopmentSkillsPromptBlock, } from "../server/agents-bundle.js";
15
15
  import { getAmbientOrgId, getAmbientUserEmail, runWithRequestContext, } from "../server/request-context.js";
16
16
  import { isReasoningEffort, } from "../shared/reasoning-effort.js";
17
+ import { createCodeAgentAgentTools } from "./code-agent-agent-tools.js";
17
18
  import { createAgentNativeConfigContext, loadResolvedAgentNativeConfig, } from "../vite/agent-native-config-loader.js";
18
19
  import { runClaudeCodeParticipant, } from "./claude-code-participant.js";
19
20
  import { codexMcpConfigArgs, mergeCodeAgentMcpConfig, restrictCodeAgentMcpConfig, } from "./code-agent-mcp-config.js";
@@ -2201,6 +2202,10 @@ Current run mode: ${mode} mode (${permissionMode}).
2201
2202
  - In an Agent-Native app or workspace, also run \`pnpm agent-native:doctor\` (or \`pnpm doctor\`) after source changes. Treat every finding as a fix-required security issue; do not disable a guard without a reviewer-readable reason.
2202
2203
  - Do not claim a change works, tests pass, or a build succeeds unless you actually ran it and saw the result. If you could not verify something, say exactly what is unverified and why.
2203
2204
 
2205
+ # Schedules and cooperating threads
2206
+
2207
+ In Auto mode, use \`manage-schedules\` for durable local desktop schedules. A global schedule starts a fresh thread on each interval; a thread schedule queues a message into an existing thread. Use \`intervalMinutes: 360\` for every 6 hours. Use \`manage-agent-threads\` to list threads, create a peer thread, or message another thread. Messages include source-agent metadata so the recipient can link back to the sender.
2208
+
2204
2209
  # Tools beyond the basics
2205
2210
 
2206
2211
  - Use \`tool-search\` when you need a capability that may come from MCP, including browser automation or computer control.
@@ -2278,6 +2283,7 @@ function createLocalCodeAgentActions(cwd, permissionMode, runId, onToolMetadata,
2278
2283
  read: actions.read,
2279
2284
  };
2280
2285
  }
2286
+ Object.assign(actions, createCodeAgentAgentTools(runId, cwd));
2281
2287
  return actions;
2282
2288
  }
2283
2289
  function resolveCodeAgentToolProfile(value) {
@@ -0,0 +1,54 @@
1
+ export type CodeAgentScheduleScope = "global" | "thread";
2
+ export type CodeAgentScheduleStatus = "queued" | "completed" | "errored";
3
+ export interface CodeAgentScheduleRecord {
4
+ schemaVersion: 1;
5
+ id: string;
6
+ name: string;
7
+ prompt: string;
8
+ scope: CodeAgentScheduleScope;
9
+ targetRunId?: string;
10
+ intervalMinutes: number;
11
+ enabled: boolean;
12
+ createdAt: string;
13
+ updatedAt: string;
14
+ nextRunAt: string;
15
+ lastRunAt?: string;
16
+ lastStatus?: CodeAgentScheduleStatus;
17
+ lastError?: string;
18
+ lastTriggeredRunId?: string;
19
+ createdByRunId?: string;
20
+ }
21
+ export interface CreateCodeAgentScheduleInput {
22
+ name: string;
23
+ prompt: string;
24
+ scope?: CodeAgentScheduleScope;
25
+ targetRunId?: string;
26
+ intervalMinutes: number;
27
+ enabled?: boolean;
28
+ createdByRunId?: string;
29
+ now?: Date;
30
+ }
31
+ export interface UpdateCodeAgentScheduleInput {
32
+ name?: string;
33
+ prompt?: string;
34
+ scope?: CodeAgentScheduleScope;
35
+ targetRunId?: string | null;
36
+ intervalMinutes?: number;
37
+ enabled?: boolean;
38
+ }
39
+ export interface CodeAgentScheduleRunPatch {
40
+ lastRunAt: string;
41
+ lastStatus: CodeAgentScheduleStatus;
42
+ nextRunAt?: string;
43
+ lastError?: string | null;
44
+ lastTriggeredRunId?: string;
45
+ }
46
+ export declare function codeAgentSchedulesPath(): string;
47
+ export declare function listCodeAgentSchedules(): CodeAgentScheduleRecord[];
48
+ export declare function getCodeAgentSchedule(scheduleId: string): CodeAgentScheduleRecord | null;
49
+ export declare function createCodeAgentSchedule(input: CreateCodeAgentScheduleInput): CodeAgentScheduleRecord;
50
+ export declare function updateCodeAgentSchedule(scheduleId: string, input: UpdateCodeAgentScheduleInput): CodeAgentScheduleRecord | null;
51
+ export declare function deleteCodeAgentSchedule(scheduleId: string): boolean;
52
+ export declare function markCodeAgentScheduleRun(scheduleId: string, patch: CodeAgentScheduleRunPatch): CodeAgentScheduleRecord | null;
53
+ export declare function isCodeAgentScheduleDue(schedule: CodeAgentScheduleRecord, now?: Date): boolean;
54
+ export declare function nextCodeAgentScheduleRunAt(schedule: CodeAgentScheduleRecord, now?: Date): string;