@agent-native/core 0.7.19 → 0.7.21
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.
- package/README.md +1 -1
- package/dist/agent/engine/builder-engine.d.ts.map +1 -1
- package/dist/agent/engine/builder-engine.js +45 -2
- package/dist/agent/engine/builder-engine.js.map +1 -1
- package/dist/agent/loop-settings.d.ts +37 -0
- package/dist/agent/loop-settings.d.ts.map +1 -0
- package/dist/agent/loop-settings.js +127 -0
- package/dist/agent/loop-settings.js.map +1 -0
- package/dist/agent/production-agent.d.ts +8 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +268 -29
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +76 -3
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/run-store.d.ts +1 -1
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +65 -2
- package/dist/agent/run-store.js.map +1 -1
- package/dist/agent/thread-data-builder.d.ts +3 -0
- package/dist/agent/thread-data-builder.d.ts.map +1 -1
- package/dist/agent/thread-data-builder.js +52 -10
- package/dist/agent/thread-data-builder.js.map +1 -1
- package/dist/agent/tool-search.d.ts +37 -0
- package/dist/agent/tool-search.d.ts.map +1 -0
- package/dist/agent/tool-search.js +201 -0
- package/dist/agent/tool-search.js.map +1 -0
- package/dist/agent/types.d.ts +8 -1
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/cli/create.d.ts.map +1 -1
- package/dist/cli/create.js +44 -9
- package/dist/cli/create.js.map +1 -1
- package/dist/cli/workspacify.d.ts +2 -0
- package/dist/cli/workspacify.d.ts.map +1 -1
- package/dist/cli/workspacify.js +34 -1
- package/dist/cli/workspacify.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +277 -18
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/ConnectBuilderCard.d.ts.map +1 -1
- package/dist/client/ConnectBuilderCard.js +1 -1
- package/dist/client/ConnectBuilderCard.js.map +1 -1
- package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +14 -6
- package/dist/client/MultiTabAssistantChat.js.map +1 -1
- package/dist/client/NewWorkspaceAppFlow.d.ts +14 -0
- package/dist/client/NewWorkspaceAppFlow.d.ts.map +1 -0
- package/dist/client/NewWorkspaceAppFlow.js +198 -0
- package/dist/client/NewWorkspaceAppFlow.js.map +1 -0
- package/dist/client/PoweredByBadge.d.ts +10 -1
- package/dist/client/PoweredByBadge.d.ts.map +1 -1
- package/dist/client/PoweredByBadge.js +120 -8
- package/dist/client/PoweredByBadge.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts +3 -5
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +26 -19
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/agent-chat.d.ts.map +1 -1
- package/dist/client/agent-chat.js +15 -3
- package/dist/client/agent-chat.js.map +1 -1
- package/dist/client/analytics.d.ts +1 -1
- package/dist/client/analytics.d.ts.map +1 -1
- package/dist/client/analytics.js +141 -1
- package/dist/client/analytics.js.map +1 -1
- package/dist/client/builder-frame.d.ts +10 -0
- package/dist/client/builder-frame.d.ts.map +1 -0
- package/dist/client/builder-frame.js +94 -0
- package/dist/client/builder-frame.js.map +1 -0
- package/dist/client/composer/MentionPopover.d.ts.map +1 -1
- package/dist/client/composer/MentionPopover.js +5 -1
- package/dist/client/composer/MentionPopover.js.map +1 -1
- package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/client/composer/TiptapComposer.js +11 -6
- package/dist/client/composer/TiptapComposer.js.map +1 -1
- package/dist/client/error-format.d.ts +20 -1
- package/dist/client/error-format.d.ts.map +1 -1
- package/dist/client/error-format.js +53 -5
- package/dist/client/error-format.js.map +1 -1
- package/dist/client/index.d.ts +3 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +3 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/notifications/NotificationsBell.d.ts.map +1 -1
- package/dist/client/notifications/NotificationsBell.js +28 -1
- package/dist/client/notifications/NotificationsBell.js.map +1 -1
- package/dist/client/onboarding/OnboardingPanel.d.ts.map +1 -1
- package/dist/client/onboarding/OnboardingPanel.js +88 -6
- package/dist/client/onboarding/OnboardingPanel.js.map +1 -1
- package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
- package/dist/client/settings/SettingsPanel.js +145 -9
- package/dist/client/settings/SettingsPanel.js.map +1 -1
- package/dist/client/settings/useBuilderStatus.d.ts +13 -0
- package/dist/client/settings/useBuilderStatus.d.ts.map +1 -1
- package/dist/client/settings/useBuilderStatus.js +50 -9
- package/dist/client/settings/useBuilderStatus.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts +3 -0
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +88 -7
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/client/tools/ToolsListPage.d.ts.map +1 -1
- package/dist/client/tools/ToolsListPage.js +16 -1
- package/dist/client/tools/ToolsListPage.js.map +1 -1
- package/dist/client/tools/ToolsSidebarSection.d.ts.map +1 -1
- package/dist/client/tools/ToolsSidebarSection.js +63 -8
- package/dist/client/tools/ToolsSidebarSection.js.map +1 -1
- package/dist/client/tools/tool-order.d.ts +7 -0
- package/dist/client/tools/tool-order.d.ts.map +1 -0
- package/dist/client/tools/tool-order.js +47 -0
- package/dist/client/tools/tool-order.js.map +1 -0
- package/dist/client/transcription/BuilderTranscriptionCta.d.ts.map +1 -1
- package/dist/client/transcription/BuilderTranscriptionCta.js +71 -6
- package/dist/client/transcription/BuilderTranscriptionCta.js.map +1 -1
- package/dist/client/use-send-to-agent-chat.d.ts.map +1 -1
- package/dist/client/use-send-to-agent-chat.js +11 -3
- package/dist/client/use-send-to-agent-chat.js.map +1 -1
- package/dist/client/useProductionAgent.d.ts.map +1 -1
- package/dist/client/useProductionAgent.js +1 -1
- package/dist/client/useProductionAgent.js.map +1 -1
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +5 -1
- package/dist/db/client.js.map +1 -1
- package/dist/deploy/build.d.ts +1 -0
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +4 -1
- package/dist/deploy/build.js.map +1 -1
- package/dist/oauth-tokens/index.d.ts +1 -1
- package/dist/oauth-tokens/index.d.ts.map +1 -1
- package/dist/oauth-tokens/index.js +1 -1
- package/dist/oauth-tokens/index.js.map +1 -1
- package/dist/oauth-tokens/store.d.ts.map +1 -1
- package/dist/oauth-tokens/store.js +6 -0
- package/dist/oauth-tokens/store.js.map +1 -1
- package/dist/observability/store.d.ts.map +1 -1
- package/dist/observability/store.js +19 -19
- package/dist/observability/store.js.map +1 -1
- package/dist/onboarding/default-steps.d.ts.map +1 -1
- package/dist/onboarding/default-steps.js +95 -61
- package/dist/onboarding/default-steps.js.map +1 -1
- package/dist/onboarding/plugin.d.ts.map +1 -1
- package/dist/onboarding/plugin.js +17 -8
- package/dist/onboarding/plugin.js.map +1 -1
- package/dist/org/migrations.js +2 -2
- package/dist/org/migrations.js.map +1 -1
- package/dist/scripts/agent-engines/list-agent-engines.d.ts.map +1 -1
- package/dist/scripts/agent-engines/list-agent-engines.js +2 -3
- package/dist/scripts/agent-engines/list-agent-engines.js.map +1 -1
- package/dist/scripts/db/exec.d.ts +2 -1
- package/dist/scripts/db/exec.d.ts.map +1 -1
- package/dist/scripts/db/exec.js +264 -61
- package/dist/scripts/db/exec.js.map +1 -1
- package/dist/scripts/db/schema.d.ts.map +1 -1
- package/dist/scripts/db/schema.js +16 -4
- package/dist/scripts/db/schema.js.map +1 -1
- package/dist/scripts/dev/index.d.ts.map +1 -1
- package/dist/scripts/dev/index.js +36 -11
- package/dist/scripts/dev/index.js.map +1 -1
- package/dist/scripts/manage-agent-loop-settings.d.ts +7 -0
- package/dist/scripts/manage-agent-loop-settings.d.ts.map +1 -0
- package/dist/scripts/manage-agent-loop-settings.js +63 -0
- package/dist/scripts/manage-agent-loop-settings.js.map +1 -0
- package/dist/scripts/runner.d.ts.map +1 -1
- package/dist/scripts/runner.js +11 -0
- package/dist/scripts/runner.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +60 -18
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/app-url.d.ts +5 -4
- package/dist/server/app-url.d.ts.map +1 -1
- package/dist/server/app-url.js +8 -4
- package/dist/server/app-url.js.map +1 -1
- package/dist/server/auth.d.ts +8 -0
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +82 -29
- package/dist/server/auth.js.map +1 -1
- package/dist/server/better-auth-instance.d.ts.map +1 -1
- package/dist/server/better-auth-instance.js +16 -5
- package/dist/server/better-auth-instance.js.map +1 -1
- package/dist/server/builder-browser.d.ts +12 -0
- package/dist/server/builder-browser.d.ts.map +1 -1
- package/dist/server/builder-browser.js +36 -4
- package/dist/server/builder-browser.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +350 -53
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/credential-provider.d.ts +21 -3
- package/dist/server/credential-provider.d.ts.map +1 -1
- package/dist/server/credential-provider.js +51 -21
- package/dist/server/credential-provider.js.map +1 -1
- package/dist/server/google-oauth.d.ts +3 -0
- package/dist/server/google-oauth.d.ts.map +1 -1
- package/dist/server/google-oauth.js +27 -3
- package/dist/server/google-oauth.js.map +1 -1
- package/dist/server/index.d.ts +4 -3
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +4 -3
- package/dist/server/index.js.map +1 -1
- package/dist/server/onboarding-html.js +2 -2
- package/dist/server/onboarding-html.js.map +1 -1
- package/dist/server/schema-prompt.d.ts.map +1 -1
- package/dist/server/schema-prompt.js +2 -1
- package/dist/server/schema-prompt.js.map +1 -1
- package/dist/server/security-headers.d.ts +3 -0
- package/dist/server/security-headers.d.ts.map +1 -1
- package/dist/server/security-headers.js +7 -1
- package/dist/server/security-headers.js.map +1 -1
- package/dist/server/ssr-handler.d.ts.map +1 -1
- package/dist/server/ssr-handler.js +31 -6
- package/dist/server/ssr-handler.js.map +1 -1
- package/dist/templates/default/_gitignore +5 -1
- package/dist/templates/default/app/root.tsx +1 -0
- package/dist/templates/default/public/favicon.svg +3 -3
- package/dist/templates/default/public/icon-180.svg +3 -3
- package/dist/templates/default/public/icon-192.svg +3 -3
- package/dist/templates/default/public/icon-512.svg +3 -3
- package/dist/templates/workspace-core/AGENTS.md +23 -7
- package/dist/templates/workspace-core/package.json +2 -1
- package/dist/templates/workspace-core/src/credentials.ts +22 -11
- package/dist/templates/workspace-root/.env.example +7 -0
- package/dist/templates/workspace-root/README.md +6 -3
- package/dist/templates/workspace-root/_gitignore +3 -0
- package/dist/templates/workspace-root/package.json +3 -1
- package/dist/templates/workspace-root/scripts/workspace-dev.ts +375 -0
- package/dist/tools/actions.d.ts.map +1 -1
- package/dist/tools/actions.js +2 -0
- package/dist/tools/actions.js.map +1 -1
- package/dist/tools/html-shell.d.ts.map +1 -1
- package/dist/tools/html-shell.js +13 -1
- package/dist/tools/html-shell.js.map +1 -1
- package/dist/tools/store.d.ts.map +1 -1
- package/dist/tools/store.js +10 -10
- package/dist/tools/store.js.map +1 -1
- package/dist/tracking/providers.d.ts +1 -0
- package/dist/tracking/providers.d.ts.map +1 -1
- package/dist/tracking/providers.js +72 -0
- package/dist/tracking/providers.js.map +1 -1
- package/dist/vite/action-types-plugin.d.ts.map +1 -1
- package/dist/vite/action-types-plugin.js +106 -9
- package/dist/vite/action-types-plugin.js.map +1 -1
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +62 -1
- package/dist/vite/client.js.map +1 -1
- package/docs/content/authentication.md +17 -13
- package/docs/content/deployment.md +11 -11
- package/docs/content/mcp-clients.md +2 -2
- package/docs/content/onboarding.md +32 -30
- package/docs/content/security.md +1 -1
- package/docs/content/tools.md +4 -0
- package/package.json +2 -2
- package/src/templates/default/_gitignore +5 -1
- package/src/templates/default/app/root.tsx +1 -0
- package/src/templates/default/public/favicon.svg +3 -3
- package/src/templates/default/public/icon-180.svg +3 -3
- package/src/templates/default/public/icon-192.svg +3 -3
- package/src/templates/default/public/icon-512.svg +3 -3
- package/src/templates/workspace-core/AGENTS.md +23 -7
- package/src/templates/workspace-core/package.json +2 -1
- package/src/templates/workspace-core/src/credentials.ts +22 -11
- package/src/templates/workspace-root/.env.example +7 -0
- package/src/templates/workspace-root/README.md +6 -3
- package/src/templates/workspace-root/_gitignore +3 -0
- package/src/templates/workspace-root/package.json +3 -1
- package/src/templates/workspace-root/scripts/workspace-dev.ts +375 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { getH3App, awaitBootstrap } from "./framework-request-handler.js";
|
|
2
|
-
import { defineEventHandler, setResponseStatus, setResponseHeader, getMethod, } from "h3";
|
|
2
|
+
import { defineEventHandler, setResponseStatus, setResponseHeader, getMethod, getHeader, } from "h3";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { createPollHandler } from "./poll.js";
|
|
5
5
|
import { createSSEHandler } from "./sse.js";
|
|
6
6
|
import { upsertEnvFile } from "./create-server.js";
|
|
7
7
|
import { readBody } from "./h3-helpers.js";
|
|
8
|
-
import { BUILDER_ENV_KEYS,
|
|
8
|
+
import { BUILDER_ENV_KEYS, buildBuilderCliAuthUrl, createBuilderBrowserCallbackErrorPage, createBuilderBrowserCallbackPage, getBuilderBranchProjectId, getBuilderBrowserStatusForEvent, isBuilderBranchingEnabled, resolveSafePreviewUrl, runBuilderAgent, } from "./builder-browser.js";
|
|
9
9
|
import { getState, putState, deleteState, listComposeDrafts, getComposeDraft, putComposeDraft, deleteComposeDraft, deleteAllComposeDrafts, } from "../application-state/handlers.js";
|
|
10
10
|
import { getSetting, putSetting, deleteSetting } from "../settings/store.js";
|
|
11
11
|
import { getUserSetting, putUserSetting, deleteUserSetting, } from "../settings/user-settings.js";
|
|
@@ -19,6 +19,7 @@ import { readMultipartFormData } from "h3";
|
|
|
19
19
|
import { createListSecretsHandler, createWriteSecretHandler, createTestSecretHandler, createAdHocSecretHandler, } from "../secrets/routes.js";
|
|
20
20
|
import { registerFrameworkSecrets } from "../secrets/register-framework-secrets.js";
|
|
21
21
|
import { registerBuiltinProviders } from "../tracking/providers.js";
|
|
22
|
+
import { track } from "../tracking/index.js";
|
|
22
23
|
import { registerBuiltinNotificationChannels } from "../notifications/channels.js";
|
|
23
24
|
import { createNotificationsHandler } from "../notifications/routes.js";
|
|
24
25
|
import { createProgressHandler } from "../progress/routes.js";
|
|
@@ -26,7 +27,9 @@ import { createTranscribeVoiceHandler } from "./transcribe-voice.js";
|
|
|
26
27
|
import { runWithRequestContext } from "./request-context.js";
|
|
27
28
|
import { createVoiceProvidersStatusHandler } from "./voice-providers-status.js";
|
|
28
29
|
import { PROVIDER_ENV_META } from "../agent/engine/provider-env-vars.js";
|
|
30
|
+
import { canUpdateAgentLoopSettings, readAgentLoopSettings, resetAgentLoopSettings, validateMaxIterationsInput, writeAgentLoopSettings, } from "../agent/loop-settings.js";
|
|
29
31
|
import { isAgentEngineSettingConfigured, getAgentEngineEntry, detectEngineFromEnv, detectEngineFromUserSecrets, isStoredEngineUsable, } from "../agent/engine/registry.js";
|
|
32
|
+
import { getOrgContext } from "../org/context.js";
|
|
30
33
|
/**
|
|
31
34
|
* The base path prefix for all framework-level routes.
|
|
32
35
|
* All agent-native core routes live under this namespace to avoid
|
|
@@ -53,6 +56,14 @@ function isEnvVarWriteAllowed() {
|
|
|
53
56
|
return true;
|
|
54
57
|
return isDevEnvironment() && isLocalDatabase();
|
|
55
58
|
}
|
|
59
|
+
function trackBuilderLifecycle(name, userEmail, properties = {}) {
|
|
60
|
+
if (!userEmail)
|
|
61
|
+
return;
|
|
62
|
+
track(name, {
|
|
63
|
+
feature: "builder",
|
|
64
|
+
...properties,
|
|
65
|
+
}, { userId: userEmail });
|
|
66
|
+
}
|
|
56
67
|
function normalizeAppBasePath(value) {
|
|
57
68
|
if (!value || value === "/")
|
|
58
69
|
return "";
|
|
@@ -295,15 +306,17 @@ export function createCoreRoutesPlugin(options = {}) {
|
|
|
295
306
|
}
|
|
296
307
|
getH3App(nitroApp).use(`${P}/builder/status`, defineEventHandler(async (event) => {
|
|
297
308
|
const envStatus = getBuilderBrowserStatusForEvent(event);
|
|
298
|
-
// Read session once so we can establish per-user request context for
|
|
299
|
-
// credential resolution. Without this, resolveBuilderCredentials()
|
|
300
|
-
// calls getRequestUserEmail() on an empty AsyncLocalStorage store and
|
|
301
|
-
// falls through to process.env — causing the connection state to
|
|
302
|
-
// flicker between requests depending on stale env values.
|
|
303
309
|
const session = await getSession(event).catch(() => null);
|
|
304
310
|
const userEmail = session?.email;
|
|
311
|
+
// Env-managed mode: BUILDER_PRIVATE_KEY is set at the deployment
|
|
312
|
+
// level, so every user shares the operator's Builder identity.
|
|
313
|
+
// Skip per-user lookups entirely — the env key is authoritative
|
|
314
|
+
// and the UI must hide the connect/disconnect controls.
|
|
315
|
+
if (envStatus.envManaged) {
|
|
316
|
+
return envStatus;
|
|
317
|
+
}
|
|
305
318
|
return runWithRequestContext({ userEmail }, async () => {
|
|
306
|
-
//
|
|
319
|
+
// Per-user OAuth mode: read the user's app_secrets-stored creds.
|
|
307
320
|
try {
|
|
308
321
|
const { resolveBuilderCredentials } = await import("./credential-provider.js");
|
|
309
322
|
const creds = await resolveBuilderCredentials();
|
|
@@ -352,7 +365,7 @@ export function createCoreRoutesPlugin(options = {}) {
|
|
|
352
365
|
}
|
|
353
366
|
}
|
|
354
367
|
catch {
|
|
355
|
-
// settings store unavailable — fall through
|
|
368
|
+
// settings store unavailable — fall through
|
|
356
369
|
}
|
|
357
370
|
// Honor legacy disconnect flag for existing deployments.
|
|
358
371
|
try {
|
|
@@ -370,49 +383,160 @@ export function createCoreRoutesPlugin(options = {}) {
|
|
|
370
383
|
}
|
|
371
384
|
}
|
|
372
385
|
catch {
|
|
373
|
-
// DB not reachable
|
|
386
|
+
// DB not reachable
|
|
374
387
|
}
|
|
375
|
-
//
|
|
376
|
-
//
|
|
377
|
-
//
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
orgName: undefined,
|
|
388
|
-
orgKind: undefined,
|
|
389
|
-
};
|
|
390
|
-
}
|
|
391
|
-
return envStatus;
|
|
388
|
+
// No env, no per-user creds → not configured. Both authenticated
|
|
389
|
+
// and unauthenticated callers see "not connected" so they can
|
|
390
|
+
// run through the OAuth flow.
|
|
391
|
+
return {
|
|
392
|
+
...envStatus,
|
|
393
|
+
configured: false,
|
|
394
|
+
privateKeyConfigured: false,
|
|
395
|
+
publicKeyConfigured: false,
|
|
396
|
+
userId: undefined,
|
|
397
|
+
orgName: undefined,
|
|
398
|
+
orgKind: undefined,
|
|
399
|
+
};
|
|
392
400
|
});
|
|
393
401
|
}));
|
|
402
|
+
// How long a pending-connect row is valid. Must be long enough for
|
|
403
|
+
// the user to complete the Builder CLI-auth flow, but short enough
|
|
404
|
+
// that a stale row from an abandoned attempt doesn't accept a new
|
|
405
|
+
// callback minutes later.
|
|
406
|
+
const BUILDER_CONNECT_PENDING_TTL_MS = 10 * 60 * 1000; // 10 min
|
|
407
|
+
// Decide whether a /builder/connect navigation originated from this
|
|
408
|
+
// app's own UI (allowed) or from a foreign origin (cross-site CSRF
|
|
409
|
+
// attempt — rejected). Sec-Fetch-Site is the modern signal:
|
|
410
|
+
// - "same-origin": user clicked Connect from our own pages — allow
|
|
411
|
+
// - "none": typed in URL bar / bookmark / browser extension — allow
|
|
412
|
+
// - "same-site" / "cross-site" / missing-but-with-foreign-Origin
|
|
413
|
+
// all map to reject.
|
|
414
|
+
// For older browsers without Sec-Fetch-* we fall back to Origin and
|
|
415
|
+
// then Referer, comparing against the request's resolved origin.
|
|
416
|
+
function isSameOriginConnect(event) {
|
|
417
|
+
const fetchSite = getHeader(event, "sec-fetch-site");
|
|
418
|
+
if (fetchSite === "same-origin" || fetchSite === "none")
|
|
419
|
+
return true;
|
|
420
|
+
if (fetchSite)
|
|
421
|
+
return false; // browser told us it's cross-site/same-site
|
|
422
|
+
const expected = getOrigin(event).replace(/\/+$/, "");
|
|
423
|
+
const origin = getHeader(event, "origin");
|
|
424
|
+
if (origin)
|
|
425
|
+
return origin.replace(/\/+$/, "") === expected;
|
|
426
|
+
const referer = getHeader(event, "referer");
|
|
427
|
+
if (referer) {
|
|
428
|
+
try {
|
|
429
|
+
return new URL(referer).origin === expected;
|
|
430
|
+
}
|
|
431
|
+
catch {
|
|
432
|
+
return false;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
// No Sec-Fetch-Site, no Origin, no Referer — pre-2020 browser
|
|
436
|
+
// making a top-level navigation. Allow; cookies are still
|
|
437
|
+
// session-bound so the worst case degrades to the prior behavior.
|
|
438
|
+
return true;
|
|
439
|
+
}
|
|
394
440
|
// Lightweight 302 to the Builder CLI-auth URL. Lets clients do
|
|
395
441
|
// `window.open('/_agent-native/builder/connect', '_blank')` synchronously
|
|
396
442
|
// inside a click handler, avoiding the popup-blocker downgrade that
|
|
397
|
-
// happens when an await sits before window.open.
|
|
398
|
-
//
|
|
399
|
-
//
|
|
400
|
-
//
|
|
443
|
+
// happens when an await sits before window.open.
|
|
444
|
+
//
|
|
445
|
+
// CSRF protection here is two-layer because session cookies are
|
|
446
|
+
// SameSite=None;Secure (so the editor iframe can ride along) — that
|
|
447
|
+
// means a session cookie alone does NOT prevent cross-origin
|
|
448
|
+
// window.open from initiating a connect flow on the victim's behalf:
|
|
449
|
+
// 1. Sec-Fetch-Site header — modern browsers stamp every request
|
|
450
|
+
// with the navigation context. We only allow `same-origin` or
|
|
451
|
+
// `none` (typed/bookmark/extension); cross-site / same-site are
|
|
452
|
+
// rejected. The previous "URL-embedded signed state" was dropped
|
|
453
|
+
// because Builder's /cli-auth strips arbitrary query params; this
|
|
454
|
+
// replaces that defense with a browser-native one.
|
|
455
|
+
// 2. Pending row keyed by session email + bound nonce — the callback
|
|
456
|
+
// requires both a valid session and a one-time row that this
|
|
457
|
+
// handler wrote during the same flow. Without the same-origin
|
|
458
|
+
// check above, an attacker could prime the row from cross-site
|
|
459
|
+
// and then trick the victim into hitting a callback URL with
|
|
460
|
+
// attacker-controlled p-key/api-key, hijacking the victim's
|
|
461
|
+
// account. With the check, the attacker can't prime the row.
|
|
401
462
|
getH3App(nitroApp).use(`${P}/builder/connect`, defineEventHandler(async (event) => {
|
|
402
463
|
const session = await getSession(event).catch(() => null);
|
|
403
464
|
if (!session?.email) {
|
|
404
465
|
setResponseStatus(event, 401);
|
|
405
466
|
return { error: "Authentication required" };
|
|
406
467
|
}
|
|
407
|
-
|
|
408
|
-
|
|
468
|
+
// Env-managed mode: per-user OAuth is disabled because the operator
|
|
469
|
+
// already provided a deploy-level Builder identity. Reject the
|
|
470
|
+
// connect attempt — any per-user keys we wrote would be ignored
|
|
471
|
+
// by the resolver, so completing the OAuth flow would be a no-op
|
|
472
|
+
// that misleads the user about the resulting connection state.
|
|
473
|
+
const { isBuilderEnvManaged } = await import("./credential-provider.js");
|
|
474
|
+
if (isBuilderEnvManaged()) {
|
|
475
|
+
setResponseStatus(event, 409);
|
|
476
|
+
return {
|
|
477
|
+
error: "Builder is managed by the deployment (BUILDER_PRIVATE_KEY is set). Per-user connect is disabled.",
|
|
478
|
+
envManaged: true,
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
// Same-origin gate. Sec-Fetch-Site is the primary signal; fall
|
|
482
|
+
// back to Origin/Referer for older browsers. Reject any context
|
|
483
|
+
// that isn't this exact app's origin — including same-site
|
|
484
|
+
// subdomains, since a compromised subdomain shouldn't be able
|
|
485
|
+
// to mint Builder credential writes for users of the main app.
|
|
486
|
+
if (!isSameOriginConnect(event)) {
|
|
487
|
+
trackBuilderLifecycle("builder connect failed", session.email, {
|
|
488
|
+
reason: "cross_origin",
|
|
489
|
+
stage: "connect",
|
|
490
|
+
});
|
|
491
|
+
setResponseStatus(event, 403);
|
|
492
|
+
return { error: "Cross-origin connect requests are not allowed" };
|
|
493
|
+
}
|
|
494
|
+
// Clear any prior failure row from a previous attempt — otherwise
|
|
495
|
+
// useBuilderStatus polling sees the stale error and aborts the
|
|
496
|
+
// new attempt before it can complete.
|
|
497
|
+
try {
|
|
498
|
+
await deleteSetting(`builder-connect-error:${session.email}`);
|
|
499
|
+
}
|
|
500
|
+
catch {
|
|
501
|
+
// No prior error row — fine
|
|
502
|
+
}
|
|
503
|
+
// Store a short-lived pending row. If the DB is unavailable we
|
|
504
|
+
// surface a popup-renderable error page that signals the parent
|
|
505
|
+
// via BroadcastChannel, rather than letting the popup show raw
|
|
506
|
+
// JSON and the parent poll for 5 minutes.
|
|
507
|
+
try {
|
|
508
|
+
await putSetting(`builder-pending-connect:${session.email}`, {
|
|
509
|
+
expiresAt: Date.now() + BUILDER_CONNECT_PENDING_TTL_MS,
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
catch (err) {
|
|
513
|
+
trackBuilderLifecycle("builder connect failed", session.email, {
|
|
514
|
+
reason: "pending_storage_unavailable",
|
|
515
|
+
stage: "connect",
|
|
516
|
+
});
|
|
517
|
+
const msg = "Could not initiate Builder connect — storage unavailable. Try again.";
|
|
518
|
+
console.error("[builder] Could not store pending-connect state:", err?.message ?? err);
|
|
519
|
+
// Best-effort: also write the error row so the parent's
|
|
520
|
+
// /builder/status poll picks it up if BroadcastChannel doesn't.
|
|
521
|
+
await putSetting(`builder-connect-error:${session.email}`, {
|
|
522
|
+
message: msg,
|
|
523
|
+
at: Date.now(),
|
|
524
|
+
}).catch(() => { });
|
|
525
|
+
setResponseStatus(event, 503);
|
|
526
|
+
setResponseHeader(event, "Content-Type", "text/html; charset=utf-8");
|
|
527
|
+
return createBuilderBrowserCallbackErrorPage(msg);
|
|
528
|
+
}
|
|
529
|
+
trackBuilderLifecycle("builder connect started", session.email, {
|
|
530
|
+
stage: "connect",
|
|
531
|
+
});
|
|
532
|
+
// Build the cli-auth URL without embedding state in redirect_url:
|
|
533
|
+
// Builder's /cli-auth appends params directly to redirect_url and
|
|
534
|
+
// does not preserve any pre-existing query string we put there.
|
|
535
|
+
const cliAuthUrl = buildBuilderCliAuthUrl(getOrigin(event), null);
|
|
409
536
|
setResponseStatus(event, 302);
|
|
410
537
|
setResponseHeader(event, "Location", cliAuthUrl);
|
|
411
538
|
return "";
|
|
412
539
|
}));
|
|
413
|
-
// Hardcoded for the early preview — later this will come from workspace/org
|
|
414
|
-
// config so each team can point at its own Builder project.
|
|
415
|
-
const DEFAULT_BUILDER_PROJECT_ID = "274d28fec94b48f2b2d68f2274d390eb";
|
|
416
540
|
getH3App(nitroApp).use(`${P}/builder/run`, defineEventHandler(async (event) => {
|
|
417
541
|
if (getMethod(event) !== "POST") {
|
|
418
542
|
setResponseStatus(event, 405);
|
|
@@ -442,6 +566,12 @@ export function createCoreRoutesPlugin(options = {}) {
|
|
|
442
566
|
return { error: "A signed-in user is required to run Builder" };
|
|
443
567
|
}
|
|
444
568
|
const userEmail = session.email;
|
|
569
|
+
if (!isBuilderBranchingEnabled()) {
|
|
570
|
+
setResponseStatus(event, 403);
|
|
571
|
+
return {
|
|
572
|
+
error: "Builder branch creation is not enabled for this deployment. Set ENABLE_BUILDER=true or BUILDER_BRANCH_PROJECT_ID.",
|
|
573
|
+
};
|
|
574
|
+
}
|
|
445
575
|
// Wrap in runWithRequestContext so resolveBuilderCredential() inside
|
|
446
576
|
// runBuilderAgent() resolves per-user app_secrets rather than falling
|
|
447
577
|
// through to process.env — the same pattern the /builder/status endpoint
|
|
@@ -458,7 +588,7 @@ export function createCoreRoutesPlugin(options = {}) {
|
|
|
458
588
|
try {
|
|
459
589
|
const result = await runBuilderAgent({
|
|
460
590
|
prompt,
|
|
461
|
-
projectId:
|
|
591
|
+
projectId: getBuilderBranchProjectId(),
|
|
462
592
|
branchName: typeof body?.branchName === "string"
|
|
463
593
|
? body.branchName
|
|
464
594
|
: undefined,
|
|
@@ -480,27 +610,105 @@ export function createCoreRoutesPlugin(options = {}) {
|
|
|
480
610
|
setResponseStatus(event, 405);
|
|
481
611
|
return { error: "Method not allowed" };
|
|
482
612
|
}
|
|
483
|
-
// Session blocks anonymous callers; the
|
|
484
|
-
//
|
|
485
|
-
//
|
|
613
|
+
// Session blocks anonymous callers; the pending-row check below
|
|
614
|
+
// (combined with the same-origin gate on /builder/connect) blocks
|
|
615
|
+
// CSRF. Session cookies are SameSite=None;Secure for the iframe
|
|
616
|
+
// editor, so they ride along on attacker-crafted top-level links —
|
|
617
|
+
// the SameSite=None concession is exactly why the connect flow
|
|
618
|
+
// can't rely on session-cookie identity alone.
|
|
486
619
|
const session = await getSession(event).catch(() => null);
|
|
487
620
|
if (!session?.email) {
|
|
488
621
|
setResponseStatus(event, 401);
|
|
489
622
|
return { error: "Authentication required" };
|
|
490
623
|
}
|
|
491
624
|
const requestUrl = new URL(`${event.url?.pathname || "/"}${event.url?.search || ""}`, getOrigin(event));
|
|
492
|
-
|
|
493
|
-
|
|
625
|
+
// Verify and consume the server-side pending-connect row that the
|
|
626
|
+
// /builder/connect route stored. This replaces the old URL-embedded
|
|
627
|
+
// signed CSRF state (_an_state) which Builder's /cli-auth page was
|
|
628
|
+
// stripping from the redirect_url query string.
|
|
629
|
+
//
|
|
630
|
+
// The delete must succeed before we proceed — otherwise a DB blip
|
|
631
|
+
// leaves the row in place and the same callback URL can be
|
|
632
|
+
// replayed against the same session for up to 10 minutes (the
|
|
633
|
+
// TTL window). Treat a delete failure as a hard failure: the
|
|
634
|
+
// user retries, the next /builder/connect call rewrites the
|
|
635
|
+
// pending row.
|
|
636
|
+
let pendingValid = false;
|
|
637
|
+
let pendingError = null;
|
|
638
|
+
try {
|
|
639
|
+
const pending = (await getSetting(`builder-pending-connect:${session.email}`));
|
|
640
|
+
if (pending &&
|
|
641
|
+
typeof pending.expiresAt === "number" &&
|
|
642
|
+
Date.now() < pending.expiresAt) {
|
|
643
|
+
try {
|
|
644
|
+
await deleteSetting(`builder-pending-connect:${session.email}`);
|
|
645
|
+
pendingValid = true;
|
|
646
|
+
}
|
|
647
|
+
catch (err) {
|
|
648
|
+
pendingError =
|
|
649
|
+
"Could not consume pending-connect token (storage error). Please retry.";
|
|
650
|
+
console.error("[builder] deleteSetting failed for pending-connect — refusing to proceed (replay risk):", err?.message ?? err);
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
catch {
|
|
655
|
+
// DB temporarily unavailable — treat as missing.
|
|
656
|
+
}
|
|
657
|
+
if (pendingError) {
|
|
658
|
+
trackBuilderLifecycle("builder connect failed", session.email, {
|
|
659
|
+
reason: "pending_consume_storage_error",
|
|
660
|
+
stage: "callback",
|
|
661
|
+
});
|
|
662
|
+
// Best-effort signal to the parent's poll loop, then render the
|
|
663
|
+
// popup-friendly error page so the BroadcastChannel notify fires.
|
|
664
|
+
await putSetting(`builder-connect-error:${session.email}`, {
|
|
665
|
+
message: pendingError,
|
|
666
|
+
at: Date.now(),
|
|
667
|
+
}).catch(() => { });
|
|
668
|
+
setResponseStatus(event, 503);
|
|
669
|
+
setResponseHeader(event, "Content-Type", "text/html; charset=utf-8");
|
|
670
|
+
return createBuilderBrowserCallbackErrorPage(pendingError);
|
|
671
|
+
}
|
|
672
|
+
if (!pendingValid) {
|
|
673
|
+
trackBuilderLifecycle("builder connect failed", session.email, {
|
|
674
|
+
reason: "missing_pending_connect",
|
|
675
|
+
stage: "callback",
|
|
676
|
+
});
|
|
677
|
+
const msg = "No active connect flow found. Restart the Builder connect flow from Settings.";
|
|
678
|
+
// Write an error signal so the polling loop in the parent tab
|
|
679
|
+
// terminates quickly instead of waiting 5 minutes for the timeout.
|
|
680
|
+
try {
|
|
681
|
+
await putSetting(`builder-connect-error:${session.email}`, {
|
|
682
|
+
message: msg,
|
|
683
|
+
at: Date.now(),
|
|
684
|
+
});
|
|
685
|
+
}
|
|
686
|
+
catch {
|
|
687
|
+
// DB unavailable — parent will time out naturally.
|
|
688
|
+
}
|
|
494
689
|
setResponseStatus(event, 403);
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
};
|
|
690
|
+
setResponseHeader(event, "Content-Type", "text/html; charset=utf-8");
|
|
691
|
+
return createBuilderBrowserCallbackErrorPage(msg);
|
|
498
692
|
}
|
|
499
693
|
const privateKey = requestUrl.searchParams.get("p-key");
|
|
500
694
|
const publicKey = requestUrl.searchParams.get("api-key");
|
|
501
695
|
if (!privateKey || !publicKey) {
|
|
696
|
+
trackBuilderLifecycle("builder connect failed", session.email, {
|
|
697
|
+
reason: "missing_credentials",
|
|
698
|
+
stage: "callback",
|
|
699
|
+
});
|
|
700
|
+
// Render the popup-friendly error page (and write a status row)
|
|
701
|
+
// instead of bare JSON, so the parent tab's poll loop terminates
|
|
702
|
+
// immediately via BroadcastChannel rather than hanging until the
|
|
703
|
+
// 5-minute timeout.
|
|
704
|
+
const msg = "Builder didn't return credentials. Restart the connect flow from settings.";
|
|
705
|
+
await putSetting(`builder-connect-error:${session.email}`, {
|
|
706
|
+
message: msg,
|
|
707
|
+
at: Date.now(),
|
|
708
|
+
}).catch(() => { });
|
|
502
709
|
setResponseStatus(event, 400);
|
|
503
|
-
|
|
710
|
+
setResponseHeader(event, "Content-Type", "text/html; charset=utf-8");
|
|
711
|
+
return createBuilderBrowserCallbackErrorPage(msg);
|
|
504
712
|
}
|
|
505
713
|
const userId = requestUrl.searchParams.get("user-id");
|
|
506
714
|
const orgName = requestUrl.searchParams.get("org-name");
|
|
@@ -532,6 +740,10 @@ export function createCoreRoutesPlugin(options = {}) {
|
|
|
532
740
|
console.error("[builder] Failed to persist per-user credentials:", writeError);
|
|
533
741
|
}
|
|
534
742
|
if (writeError) {
|
|
743
|
+
trackBuilderLifecycle("builder connect failed", session.email, {
|
|
744
|
+
reason: "credential_write_failed",
|
|
745
|
+
stage: "callback",
|
|
746
|
+
});
|
|
535
747
|
// Best-effort signal to /builder/status. If putSetting also fails
|
|
536
748
|
// (entire DB unreachable) the popup's postMessage still notifies
|
|
537
749
|
// the parent. If both fail the parent times out at 5min as today.
|
|
@@ -563,15 +775,19 @@ export function createCoreRoutesPlugin(options = {}) {
|
|
|
563
775
|
// No prior error row — fine
|
|
564
776
|
}
|
|
565
777
|
const previewUrl = resolveSafePreviewUrl(requestUrl.searchParams.get("preview-url"), event);
|
|
778
|
+
trackBuilderLifecycle("builder connect succeeded", session.email, {
|
|
779
|
+
stage: "callback",
|
|
780
|
+
has_preview_url: Boolean(previewUrl),
|
|
781
|
+
org_kind: orgKind || undefined,
|
|
782
|
+
});
|
|
566
783
|
setResponseHeader(event, "Content-Type", "text/html; charset=utf-8");
|
|
567
784
|
return createBuilderBrowserCallbackPage(previewUrl);
|
|
568
785
|
}));
|
|
569
|
-
// POST /_agent-native/builder/disconnect — revoke the
|
|
570
|
-
// credentials
|
|
571
|
-
//
|
|
572
|
-
//
|
|
573
|
-
//
|
|
574
|
-
// avoid a "still connected after disconnect" state on restart.
|
|
786
|
+
// POST /_agent-native/builder/disconnect — revoke the user's per-user
|
|
787
|
+
// Builder credentials in app_secrets. In env-managed mode (deploy-level
|
|
788
|
+
// BUILDER_PRIVATE_KEY set) disconnection is operator-controlled — this
|
|
789
|
+
// endpoint refuses with 409 so a stale UI button can't pretend to
|
|
790
|
+
// disconnect a deploy-level identity it doesn't own.
|
|
575
791
|
getH3App(nitroApp).use(`${P}/builder/disconnect`, defineEventHandler(async (event) => {
|
|
576
792
|
if (getMethod(event) !== "POST") {
|
|
577
793
|
setResponseStatus(event, 405);
|
|
@@ -582,12 +798,23 @@ export function createCoreRoutesPlugin(options = {}) {
|
|
|
582
798
|
setResponseStatus(event, 401);
|
|
583
799
|
return { error: "unauthorized" };
|
|
584
800
|
}
|
|
801
|
+
const { isBuilderEnvManaged, deleteBuilderCredentials } = await import("./credential-provider.js");
|
|
802
|
+
if (isBuilderEnvManaged()) {
|
|
803
|
+
setResponseStatus(event, 409);
|
|
804
|
+
return {
|
|
805
|
+
ok: false,
|
|
806
|
+
error: "Builder is managed by deploy-level BUILDER_PRIVATE_KEY. To disconnect, the operator must remove the env var.",
|
|
807
|
+
envManaged: true,
|
|
808
|
+
};
|
|
809
|
+
}
|
|
585
810
|
// Delete per-user Builder credentials from app_secrets.
|
|
586
811
|
try {
|
|
587
|
-
const { deleteBuilderCredentials } = await import("./credential-provider.js");
|
|
588
812
|
await deleteBuilderCredentials(session.email);
|
|
589
813
|
}
|
|
590
814
|
catch (err) {
|
|
815
|
+
trackBuilderLifecycle("builder disconnect failed", session.email, {
|
|
816
|
+
reason: "credential_delete_failed",
|
|
817
|
+
});
|
|
591
818
|
setResponseStatus(event, 500);
|
|
592
819
|
return {
|
|
593
820
|
ok: false,
|
|
@@ -595,6 +822,7 @@ export function createCoreRoutesPlugin(options = {}) {
|
|
|
595
822
|
cause: err instanceof Error ? err.message : String(err),
|
|
596
823
|
};
|
|
597
824
|
}
|
|
825
|
+
trackBuilderLifecycle("builder disconnect succeeded", session.email);
|
|
598
826
|
return { ok: true };
|
|
599
827
|
}));
|
|
600
828
|
// Proxy to Builder's agents-run API for background code changes.
|
|
@@ -847,6 +1075,75 @@ export function createCoreRoutesPlugin(options = {}) {
|
|
|
847
1075
|
};
|
|
848
1076
|
}
|
|
849
1077
|
}));
|
|
1078
|
+
// GET/PUT/DELETE /_agent-native/agent-loop-settings — org/user-scoped
|
|
1079
|
+
// ceiling for tool-calling loop iterations before the agent asks whether
|
|
1080
|
+
// it should keep going.
|
|
1081
|
+
getH3App(nitroApp).use(`${P}/agent-loop-settings`, defineEventHandler(async (event) => {
|
|
1082
|
+
const session = await getSession(event).catch(() => null);
|
|
1083
|
+
if (!session?.email) {
|
|
1084
|
+
setResponseStatus(event, 401);
|
|
1085
|
+
return { error: "unauthorized" };
|
|
1086
|
+
}
|
|
1087
|
+
const orgCtx = await getOrgContext(event).catch(() => null);
|
|
1088
|
+
const orgId = orgCtx?.orgId ?? session.orgId ?? null;
|
|
1089
|
+
const ctx = { userEmail: session.email, orgId };
|
|
1090
|
+
const canUpdate = await canUpdateAgentLoopSettings(session.email, orgId);
|
|
1091
|
+
const withContext = async () => ({
|
|
1092
|
+
...(await readAgentLoopSettings(ctx)),
|
|
1093
|
+
canUpdate,
|
|
1094
|
+
orgId,
|
|
1095
|
+
orgName: orgCtx?.orgName ?? null,
|
|
1096
|
+
role: orgCtx?.role ?? null,
|
|
1097
|
+
});
|
|
1098
|
+
const method = getMethod(event);
|
|
1099
|
+
if (method === "GET") {
|
|
1100
|
+
return withContext();
|
|
1101
|
+
}
|
|
1102
|
+
if (method === "PUT") {
|
|
1103
|
+
if (!canUpdate) {
|
|
1104
|
+
setResponseStatus(event, 403);
|
|
1105
|
+
return {
|
|
1106
|
+
error: orgId
|
|
1107
|
+
? "Only organization owners and admins can change the agent step limit."
|
|
1108
|
+
: "You cannot change the agent step limit.",
|
|
1109
|
+
};
|
|
1110
|
+
}
|
|
1111
|
+
const body = await readBody(event).catch(() => ({}));
|
|
1112
|
+
const validation = validateMaxIterationsInput(body?.maxIterations);
|
|
1113
|
+
if (validation.ok === false) {
|
|
1114
|
+
setResponseStatus(event, 400);
|
|
1115
|
+
return { error: validation.error };
|
|
1116
|
+
}
|
|
1117
|
+
const updated = await writeAgentLoopSettings(ctx, validation.value);
|
|
1118
|
+
return {
|
|
1119
|
+
...updated,
|
|
1120
|
+
canUpdate,
|
|
1121
|
+
orgId,
|
|
1122
|
+
orgName: orgCtx?.orgName ?? null,
|
|
1123
|
+
role: orgCtx?.role ?? null,
|
|
1124
|
+
};
|
|
1125
|
+
}
|
|
1126
|
+
if (method === "DELETE") {
|
|
1127
|
+
if (!canUpdate) {
|
|
1128
|
+
setResponseStatus(event, 403);
|
|
1129
|
+
return {
|
|
1130
|
+
error: orgId
|
|
1131
|
+
? "Only organization owners and admins can reset the agent step limit."
|
|
1132
|
+
: "You cannot reset the agent step limit.",
|
|
1133
|
+
};
|
|
1134
|
+
}
|
|
1135
|
+
const updated = await resetAgentLoopSettings(ctx);
|
|
1136
|
+
return {
|
|
1137
|
+
...updated,
|
|
1138
|
+
canUpdate,
|
|
1139
|
+
orgId,
|
|
1140
|
+
orgName: orgCtx?.orgName ?? null,
|
|
1141
|
+
role: orgCtx?.role ?? null,
|
|
1142
|
+
};
|
|
1143
|
+
}
|
|
1144
|
+
setResponseStatus(event, 405);
|
|
1145
|
+
return { error: "Method not allowed" };
|
|
1146
|
+
}));
|
|
850
1147
|
// ─── Usage & cost summary ────────────────────────────────────────
|
|
851
1148
|
// GET /_agent-native/usage?sinceDays=30
|
|
852
1149
|
// Returns spend broken down by label, model, app, and day for the
|