@agent-native/core 0.209.1 → 0.209.2-nightly-20261016005836
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/dist/agent/actions/list-model-providers.js +15 -2
- package/dist/agent/app-model-defaults.d.ts +9 -13
- package/dist/agent/app-model-defaults.js +23 -5
- package/dist/agent/default-agent-engine.d.ts +5 -3
- package/dist/agent/default-agent-engine.js +38 -8
- package/dist/agent/durable-background.d.ts +2 -0
- package/dist/agent/durable-background.js +2 -0
- package/dist/agent/engine/registry.js +6 -0
- package/dist/agent/production-agent.js +58 -5
- package/dist/app-config/clips.d.ts +43 -0
- package/dist/app-config/clips.js +91 -0
- package/dist/app-config/index.d.ts +1 -0
- package/dist/app-config/index.js +1 -0
- package/dist/app-config/schema.d.ts +17 -0
- package/dist/app-config/schema.js +2 -0
- package/dist/automations/service.d.ts +2 -0
- package/dist/automations/service.js +4 -1
- package/dist/client/analytics.js +48 -1
- package/dist/client/application-state.js +7 -2
- package/dist/client/chat/agentkit-protocol.js +12 -3
- package/dist/client/client-bootstrap.js +2 -0
- package/dist/client/problem-report.d.ts +11 -0
- package/dist/client/problem-report.js +19 -0
- package/dist/client/recorder.d.ts +88 -0
- package/dist/client/recorder.js +585 -0
- package/dist/client/route-chunk-recovery.js +4 -1
- package/dist/client/sign-out.js +2 -0
- package/dist/client/use-action.js +3 -0
- package/dist/deploy/workspace-deploy.js +77 -3
- package/dist/file-upload/actions/upload-image.d.ts +2 -2
- package/dist/integrations/google-docs-poller.js +12 -5
- package/dist/integrations/plugin.js +86 -19
- package/dist/integrations/request-scope.d.ts +16 -0
- package/dist/integrations/request-scope.js +27 -0
- package/dist/integrations/webhook-handler.d.ts +2 -0
- package/dist/integrations/webhook-handler.js +38 -18
- package/dist/jobs/background-automation-runner.d.ts +2 -0
- package/dist/jobs/background-automation-runner.js +25 -2
- package/dist/jobs/scheduler.js +36 -22
- package/dist/mcp/screen-memory-stdio.d.ts +7 -7
- package/dist/notifications/routes.d.ts +2 -2
- package/dist/observability/routes.d.ts +1 -1
- package/dist/org/context.d.ts +12 -0
- package/dist/org/context.js +26 -11
- package/dist/org/index.d.ts +1 -1
- package/dist/problem-report/actions/read-problem-report.d.ts +27 -0
- package/dist/problem-report/actions/read-problem-report.js +103 -0
- package/dist/problem-report/actions/save-problem-report.d.ts +35 -0
- package/dist/problem-report/actions/save-problem-report.js +36 -0
- package/dist/problem-report/format.d.ts +7 -0
- package/dist/problem-report/format.js +110 -0
- package/dist/problem-report/schema.d.ts +41 -0
- package/dist/problem-report/schema.js +34 -0
- package/dist/provider-api/actions/custom-provider-registration.d.ts +8 -8
- package/dist/provider-api/actions/provider-api.d.ts +14 -14
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/scripts/agent-engines/manage-agent-engine.js +9 -4
- package/dist/scripts/agent-engines/set-agent-engine.js +7 -1
- package/dist/server/action-discovery.js +11 -0
- package/dist/server/action-routes.js +2 -0
- package/dist/server/agent-chat-ai-setup.d.ts +2 -3
- package/dist/server/agent-chat-ai-setup.js +18 -4
- package/dist/server/agent-chat-plugin.js +97 -22
- package/dist/server/agent-chat-stream.d.ts +17 -5
- package/dist/server/agent-chat-stream.js +15 -0
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/agent-engine-default-model-route.d.ts +2 -2
- package/dist/server/agent-engine-default-model-route.js +39 -6
- package/dist/server/agent-engine-status-cache.d.ts +4 -0
- package/dist/server/agent-engine-status-cache.js +2 -0
- package/dist/server/agent-run-context.d.ts +9 -0
- package/dist/server/agent-run-context.js +82 -17
- package/dist/server/auth.js +6 -1
- package/dist/server/builder-browser.js +18 -3
- package/dist/server/chatgpt-subscription-oauth.js +12 -17
- package/dist/server/core-routes-plugin.d.ts +1 -0
- package/dist/server/core-routes-plugin.js +46 -9
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/loopback.d.ts +1 -0
- package/dist/server/loopback.js +30 -0
- package/dist/server/origin-allowlist.d.ts +2 -0
- package/dist/server/origin-allowlist.js +13 -1
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/request-context.d.ts +4 -0
- package/dist/templates/chat/app/components/layout/Sidebar.tsx +11 -1
- package/dist/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +2 -2
- package/dist/templates/factory/.agents/skills/review-latest-feedback/references/channel-inventory.md +31 -0
- package/dist/triggers/dispatcher.js +117 -31
- package/docs/content/recorder.mdx +259 -0
- package/docs/content/template-clips-developers.mdx +9 -8
- package/docs/content/template-clips-features.mdx +8 -7
- package/package.json +7 -4
- package/src/templates/chat/app/components/layout/Sidebar.tsx +11 -1
- package/src/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +2 -2
- package/src/templates/factory/.agents/skills/review-latest-feedback/references/channel-inventory.md +31 -0
- package/third-party-licenses/TABLER-ICONS.txt +21 -0
|
@@ -59,7 +59,7 @@ import { AGENT_CHAT_STREAM_PATH, AGENT_CHAT_STREAM_TOKEN_SUFFIX, AGENT_CHAT_STRE
|
|
|
59
59
|
import { handleSharedThreadRequest, } from "./agent-chat/shared-thread.js";
|
|
60
60
|
import { sortResourceSkills } from "./agent-chat/skill-frontmatter.js";
|
|
61
61
|
import { discoverAgents } from "./agent-discovery.js";
|
|
62
|
-
import { resolveAgentRunOrgId, resolveAgentRunOwnerContext, runWithAgentRunContext, seedAgentRunOwnerContext, seedBackgroundAgentRunOwnerContext, } from "./agent-run-context.js";
|
|
62
|
+
import { resolveAgentRunOrgId, resolveAgentRunOrgScope, resolveAgentRunOwnerContext, runWithAgentRunContext, seedAgentRunOwnerContext, seedBackgroundAgentRunOwnerContext, } from "./agent-run-context.js";
|
|
63
63
|
import { AGENT_TEAM_PROCESS_RUN_PATH, getCurrentDelegationDepth, processAgentTeamRun, reconcileStaleAgentTeamRuns, reconcileAgentTeamRunsForOwner, } from "./agent-teams.js";
|
|
64
64
|
import { getSession, isLoopbackRequest, registerAuthPublicPaths, } from "./auth.js";
|
|
65
65
|
import { captureError } from "./capture-error.js";
|
|
@@ -71,9 +71,11 @@ import { getOrigin } from "./google-oauth.js";
|
|
|
71
71
|
import { readBody } from "./h3-helpers.js";
|
|
72
72
|
import { loadHostedHarnessConfig } from "./hosted-harness-policy.js";
|
|
73
73
|
import { startIntervalJob } from "./interval-job.js";
|
|
74
|
+
import { isDirectLoopbackRequest } from "./loopback.js";
|
|
74
75
|
import { getModelFamilyOverlay } from "./prompts/index.js";
|
|
75
76
|
import { mountRealtimeVoiceRoutes } from "./realtime-voice.js";
|
|
76
77
|
import { runWithRequestContext, getRequestContext, getRequestOrgId, getRequestUserEmail, getRequestRunContext, ensureRequestRunContext, } from "./request-context.js";
|
|
78
|
+
import { getForwardedRequestOrigin } from "./request-origin.js";
|
|
77
79
|
export { handleSharedThreadRequest };
|
|
78
80
|
export function trackAgentChatRunLifecycle(event, threadId, attemptId, userId, properties = {}, appId, trackingSource) {
|
|
79
81
|
if (!threadId?.trim() ||
|
|
@@ -3389,10 +3391,12 @@ export function createAgentChatPlugin(options) {
|
|
|
3389
3391
|
const owner = await getOwnerFromEvent(event);
|
|
3390
3392
|
const orgId = await getOrgIdFromEvent(event);
|
|
3391
3393
|
const { resolveOwnerEngineApiKey } = await import("../agent/production-agent.js");
|
|
3392
|
-
const userApiKey =
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3394
|
+
const userApiKey = owner
|
|
3395
|
+
? await resolveOwnerEngineApiKey({
|
|
3396
|
+
engineOption: options?.engine,
|
|
3397
|
+
ownerEmail: owner,
|
|
3398
|
+
})
|
|
3399
|
+
: { apiKey: undefined, apiKeyEnvVar: undefined };
|
|
3396
3400
|
const runCtx = ensureRequestRunContext();
|
|
3397
3401
|
if (runCtx) {
|
|
3398
3402
|
runCtx.requestOrigin = getOrigin(event);
|
|
@@ -4150,14 +4154,27 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
4150
4154
|
catch {
|
|
4151
4155
|
orgCtx = null;
|
|
4152
4156
|
}
|
|
4153
|
-
const
|
|
4154
|
-
|
|
4155
|
-
:
|
|
4156
|
-
|
|
4157
|
+
const orgScope = await resolveAgentRunOrgScope({
|
|
4158
|
+
event,
|
|
4159
|
+
ownerContext: { owner: session.email, anonymous: false },
|
|
4160
|
+
resolveOrgId: options?.resolveOrgId,
|
|
4161
|
+
});
|
|
4162
|
+
if (!orgScope.orgId && orgScope.orgScope !== "personal") {
|
|
4163
|
+
return {
|
|
4164
|
+
ok: false,
|
|
4165
|
+
status: 503,
|
|
4166
|
+
error: "Organization scope is temporarily unavailable.",
|
|
4167
|
+
};
|
|
4168
|
+
}
|
|
4169
|
+
const orgId = orgScope.orgId ?? null;
|
|
4170
|
+
const canUpdate = await canUpdateAgentAppModelDefaultSettings(session.email, orgId, orgScope.orgScope);
|
|
4157
4171
|
return {
|
|
4158
4172
|
ok: true,
|
|
4159
4173
|
userEmail: session.email,
|
|
4160
4174
|
orgId,
|
|
4175
|
+
...(orgScope.orgScope === "personal"
|
|
4176
|
+
? { orgScope: "personal" }
|
|
4177
|
+
: {}),
|
|
4161
4178
|
orgName: orgCtx?.orgId === orgId ? (orgCtx.orgName ?? null) : null,
|
|
4162
4179
|
role: orgCtx?.orgId === orgId ? (orgCtx.role ?? null) : null,
|
|
4163
4180
|
canUpdate,
|
|
@@ -4166,12 +4183,15 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
4166
4183
|
const listModelDefaultEngineOptions = async (ctx, event) => {
|
|
4167
4184
|
registerBuiltinEngines();
|
|
4168
4185
|
const availableEngines = listAgentEngines();
|
|
4169
|
-
const
|
|
4186
|
+
const directLoopbackRequest = isDirectLoopbackRequest(event);
|
|
4187
|
+
const chatGPTEnabled = directLoopbackRequest &&
|
|
4188
|
+
ctx.orgScope === "personal" &&
|
|
4189
|
+
!ctx.orgId &&
|
|
4170
4190
|
!!ctx.userEmail &&
|
|
4171
4191
|
availableEngines.some((entry) => entry.name === CHATGPT_SUBSCRIPTION_ENGINE_NAME) &&
|
|
4172
4192
|
(await getUserLabEnabled(ctx.userEmail, CHATGPT_SUBSCRIPTION_LAB));
|
|
4173
4193
|
// Provider model selections follow the setting scope. ChatGPT catalogs
|
|
4174
|
-
// are user-scoped, so
|
|
4194
|
+
// are user-scoped, so organization defaults cannot use them.
|
|
4175
4195
|
const selectionScope = ctx.orgId ? "org" : "user";
|
|
4176
4196
|
const selections = new Map();
|
|
4177
4197
|
const modelsFor = async (entry) => {
|
|
@@ -4192,8 +4212,12 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
4192
4212
|
return runWithRequestContext({
|
|
4193
4213
|
userEmail: ctx.userEmail,
|
|
4194
4214
|
orgId: ctx.orgId ?? undefined,
|
|
4195
|
-
|
|
4215
|
+
...(ctx.orgScope === "personal"
|
|
4216
|
+
? { orgScope: "personal" }
|
|
4217
|
+
: {}),
|
|
4218
|
+
requestOrigin: getForwardedRequestOrigin(event),
|
|
4196
4219
|
isLoopbackRequest: isLoopbackRequest(event),
|
|
4220
|
+
isDirectLoopbackRequest: isDirectLoopbackRequest(event),
|
|
4197
4221
|
}, async () => {
|
|
4198
4222
|
const chatGPTCatalog = chatGPTEnabled && ctx.userEmail
|
|
4199
4223
|
? await listChatGPTSubscriptionModels(ctx.userEmail)
|
|
@@ -4224,7 +4248,11 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
4224
4248
|
const ctx = await resolveModelDefaultsContext(event);
|
|
4225
4249
|
if (!ctx.ok)
|
|
4226
4250
|
return ctx;
|
|
4227
|
-
const scope = {
|
|
4251
|
+
const scope = {
|
|
4252
|
+
userEmail: ctx.userEmail,
|
|
4253
|
+
orgId: ctx.orgId,
|
|
4254
|
+
orgScope: ctx.orgScope,
|
|
4255
|
+
};
|
|
4228
4256
|
const [settings, orgDefault] = await Promise.all([
|
|
4229
4257
|
readAgentAppModelDefaultSettings(scope, appId),
|
|
4230
4258
|
readDefaultAgentEngineSetting(scope),
|
|
@@ -4283,7 +4311,11 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
4283
4311
|
};
|
|
4284
4312
|
}
|
|
4285
4313
|
if (method === "DELETE") {
|
|
4286
|
-
await resetAgentAppModelDefaultSettings({
|
|
4314
|
+
await resetAgentAppModelDefaultSettings({
|
|
4315
|
+
userEmail: ctx.userEmail,
|
|
4316
|
+
orgId: ctx.orgId,
|
|
4317
|
+
orgScope: ctx.orgScope,
|
|
4318
|
+
}, appId);
|
|
4287
4319
|
return buildModelDefaultsPayload(event, appId);
|
|
4288
4320
|
}
|
|
4289
4321
|
const body = await readBody(event).catch(() => ({}));
|
|
@@ -4322,8 +4354,12 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
4322
4354
|
const catalog = await runWithRequestContext({
|
|
4323
4355
|
userEmail: ctx.userEmail,
|
|
4324
4356
|
orgId: ctx.orgId ?? undefined,
|
|
4325
|
-
|
|
4357
|
+
...(ctx.orgScope === "personal"
|
|
4358
|
+
? { orgScope: "personal" }
|
|
4359
|
+
: {}),
|
|
4360
|
+
requestOrigin: getForwardedRequestOrigin(event),
|
|
4326
4361
|
isLoopbackRequest: isLoopbackRequest(event),
|
|
4362
|
+
isDirectLoopbackRequest: isDirectLoopbackRequest(event),
|
|
4327
4363
|
}, () => listChatGPTSubscriptionModels(ctx.userEmail));
|
|
4328
4364
|
if (!catalog.models.includes(model)) {
|
|
4329
4365
|
setResponseStatus(event, 400);
|
|
@@ -4339,7 +4375,11 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
4339
4375
|
error: `Model "${model}" is not supported by Builder. Choose one of: ${entry.supportedModels.join(", ")}`,
|
|
4340
4376
|
};
|
|
4341
4377
|
}
|
|
4342
|
-
await writeAgentAppModelDefaultSettings({
|
|
4378
|
+
await writeAgentAppModelDefaultSettings({
|
|
4379
|
+
userEmail: ctx.userEmail,
|
|
4380
|
+
orgId: ctx.orgId,
|
|
4381
|
+
orgScope: ctx.orgScope,
|
|
4382
|
+
}, targetAppId, { engine, model, updatedBy: ctx.userEmail });
|
|
4343
4383
|
return buildModelDefaultsPayload(event, targetAppId);
|
|
4344
4384
|
}));
|
|
4345
4385
|
// Mount save-key BEFORE the prefix handler so it isn't shadowed.
|
|
@@ -4894,10 +4934,21 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
4894
4934
|
setResponseStatus(event, 400);
|
|
4895
4935
|
return { error: "message is required" };
|
|
4896
4936
|
}
|
|
4897
|
-
const
|
|
4898
|
-
|
|
4937
|
+
const orgScope = await resolveAgentRunOrgScope({
|
|
4938
|
+
event,
|
|
4939
|
+
ownerContext: titleOwnerContext,
|
|
4940
|
+
resolveOrgId: options?.resolveOrgId,
|
|
4941
|
+
});
|
|
4942
|
+
const titleRequestContext = {
|
|
4943
|
+
userEmail: ownerEmail,
|
|
4944
|
+
...orgScope,
|
|
4945
|
+
requestOrigin: getForwardedRequestOrigin(event),
|
|
4946
|
+
isLoopbackRequest: isLoopbackRequest(event),
|
|
4947
|
+
isDirectLoopbackRequest: isDirectLoopbackRequest(event),
|
|
4948
|
+
};
|
|
4949
|
+
await runWithRequestContext(titleRequestContext, () => requireAgentChatAiSetup());
|
|
4899
4950
|
try {
|
|
4900
|
-
const title = await runWithRequestContext(
|
|
4951
|
+
const title = await runWithRequestContext(titleRequestContext, () => generateChatTitle({ ...request, appId: options?.appId }));
|
|
4901
4952
|
return { title };
|
|
4902
4953
|
}
|
|
4903
4954
|
catch (error) {
|
|
@@ -5410,11 +5461,12 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
5410
5461
|
const threadRouteHandler = withTransientDatabaseFallback(`${routePath}/threads`, async (event) => {
|
|
5411
5462
|
const ownerContext = await resolveOwnerContext(event);
|
|
5412
5463
|
const owner = ownerContext.owner;
|
|
5413
|
-
const
|
|
5464
|
+
const orgScope = await resolveAgentRunOrgScope({
|
|
5414
5465
|
event,
|
|
5415
5466
|
ownerContext,
|
|
5416
5467
|
resolveOrgId: options?.resolveOrgId,
|
|
5417
5468
|
});
|
|
5469
|
+
const orgId = orgScope.orgId;
|
|
5418
5470
|
const method = getMethod(event);
|
|
5419
5471
|
const { threadId, tail: threadTail } = parseThreadRoute(event);
|
|
5420
5472
|
const isThreadSubroute = (subroute) => threadTail[0] === subroute;
|
|
@@ -5672,7 +5724,13 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
5672
5724
|
(mutation.type === "append" ||
|
|
5673
5725
|
mutation.type === "moveToTop" ||
|
|
5674
5726
|
mutation.type === "claim")) {
|
|
5675
|
-
await runWithRequestContext({
|
|
5727
|
+
await runWithRequestContext({
|
|
5728
|
+
userEmail: owner,
|
|
5729
|
+
...orgScope,
|
|
5730
|
+
requestOrigin: getForwardedRequestOrigin(event),
|
|
5731
|
+
isLoopbackRequest: isLoopbackRequest(event),
|
|
5732
|
+
isDirectLoopbackRequest: isDirectLoopbackRequest(event),
|
|
5733
|
+
}, () => requireAgentChatAiSetup());
|
|
5676
5734
|
}
|
|
5677
5735
|
let result;
|
|
5678
5736
|
try {
|
|
@@ -6042,10 +6100,24 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
6042
6100
|
return { error: "Authentication required" };
|
|
6043
6101
|
}
|
|
6044
6102
|
try {
|
|
6103
|
+
const orgScope = await resolveAgentRunOrgScope({
|
|
6104
|
+
event,
|
|
6105
|
+
ownerContext: { owner: session.email, anonymous: false },
|
|
6106
|
+
resolveOrgId: options?.resolveOrgId,
|
|
6107
|
+
});
|
|
6108
|
+
if (!orgScope.orgId && orgScope.orgScope !== "personal") {
|
|
6109
|
+
setResponseStatus(event, 503);
|
|
6110
|
+
return {
|
|
6111
|
+
error: "Organization scope is temporarily unavailable.",
|
|
6112
|
+
};
|
|
6113
|
+
}
|
|
6114
|
+
const scope = orgScope.orgId
|
|
6115
|
+
? { orgId: orgScope.orgId }
|
|
6116
|
+
: { orgId: null, orgScope: "personal" };
|
|
6045
6117
|
return {
|
|
6046
6118
|
token: await createAgentChatStreamToken({
|
|
6047
6119
|
ownerEmail: session.email,
|
|
6048
|
-
|
|
6120
|
+
...scope,
|
|
6049
6121
|
authUserId: session.authUserId,
|
|
6050
6122
|
}),
|
|
6051
6123
|
ttlSeconds: AGENT_CHAT_STREAM_TOKEN_TTL_SECONDS,
|
|
@@ -6078,6 +6150,9 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
6078
6150
|
owner: principal.ownerEmail,
|
|
6079
6151
|
anonymous: false,
|
|
6080
6152
|
orgId: principal.orgId,
|
|
6153
|
+
...(principal.orgScope === "personal"
|
|
6154
|
+
? { orgScope: "personal" }
|
|
6155
|
+
: {}),
|
|
6081
6156
|
...(principal.authUserId
|
|
6082
6157
|
? { authUserId: principal.authUserId }
|
|
6083
6158
|
: {}),
|
|
@@ -1,16 +1,28 @@
|
|
|
1
1
|
export declare const AGENT_CHAT_STREAM_PATH = "/_agent-native/agent-chat-stream";
|
|
2
2
|
export declare const AGENT_CHAT_STREAM_TOKEN_SUFFIX = "/stream-token";
|
|
3
3
|
export declare const AGENT_CHAT_STREAM_TOKEN_TTL_SECONDS: number;
|
|
4
|
-
export
|
|
4
|
+
export type AgentChatStreamPrincipal = {
|
|
5
5
|
ownerEmail: string;
|
|
6
|
-
orgId: string | null;
|
|
7
6
|
authUserId?: string;
|
|
8
|
-
}
|
|
7
|
+
} & ({
|
|
8
|
+
orgId: string;
|
|
9
|
+
orgScope?: never;
|
|
10
|
+
} | {
|
|
11
|
+
orgId: null;
|
|
12
|
+
orgScope: "personal";
|
|
13
|
+
});
|
|
14
|
+
type AgentChatStreamTokenScope = {
|
|
15
|
+
orgId: string;
|
|
16
|
+
orgScope?: never;
|
|
17
|
+
} | {
|
|
18
|
+
orgId: null;
|
|
19
|
+
orgScope: "personal";
|
|
20
|
+
};
|
|
9
21
|
export declare function isAgentChatStreamingRuntime(): boolean;
|
|
10
22
|
export declare function readAgentChatStreamBearerToken(authorization: string | undefined): string | null;
|
|
11
23
|
export declare function createAgentChatStreamToken(input: {
|
|
12
24
|
ownerEmail: string;
|
|
13
|
-
orgId?: string | null;
|
|
14
25
|
authUserId?: string;
|
|
15
|
-
}): Promise<string>;
|
|
26
|
+
} & AgentChatStreamTokenScope): Promise<string>;
|
|
16
27
|
export declare function verifyAgentChatStreamToken(token: string): Promise<AgentChatStreamPrincipal | null>;
|
|
28
|
+
export {};
|
|
@@ -41,6 +41,13 @@ export function readAgentChatStreamBearerToken(authorization) {
|
|
|
41
41
|
}
|
|
42
42
|
export async function createAgentChatStreamToken(input) {
|
|
43
43
|
const ownerEmail = validateOwnerEmail(input.ownerEmail);
|
|
44
|
+
if (input.orgScope === "personal" && input.orgId !== null) {
|
|
45
|
+
throw new Error("Personal agent-chat stream scope cannot include an org.");
|
|
46
|
+
}
|
|
47
|
+
if (input.orgScope !== "personal" &&
|
|
48
|
+
(typeof input.orgId !== "string" || !input.orgId.trim())) {
|
|
49
|
+
throw new Error("Agent-chat stream tokens require a resolved organization or personal scope.");
|
|
50
|
+
}
|
|
44
51
|
const authUserId = input.authUserId?.trim();
|
|
45
52
|
if (input.authUserId !== undefined &&
|
|
46
53
|
(!authUserId ||
|
|
@@ -53,6 +60,7 @@ export async function createAgentChatStreamToken(input) {
|
|
|
53
60
|
token_type: AGENT_CHAT_STREAM_TOKEN_TYPE,
|
|
54
61
|
sub: ownerEmail,
|
|
55
62
|
org_id: input.orgId ?? null,
|
|
63
|
+
...(input.orgScope === "personal" ? { org_scope: "personal" } : {}),
|
|
56
64
|
...(authUserId ? { auth_user_id: authUserId } : {}),
|
|
57
65
|
})
|
|
58
66
|
.setProtectedHeader({ alg: "HS256", typ: "JWT" })
|
|
@@ -80,6 +88,12 @@ export async function verifyAgentChatStreamToken(token) {
|
|
|
80
88
|
const orgId = payload.org_id;
|
|
81
89
|
if (orgId !== null && typeof orgId !== "string")
|
|
82
90
|
return null;
|
|
91
|
+
const orgScope = payload.org_scope;
|
|
92
|
+
if ((orgScope !== undefined && orgScope !== "personal") ||
|
|
93
|
+
(orgScope === "personal" && orgId !== null) ||
|
|
94
|
+
(orgId === null && orgScope !== "personal")) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
83
97
|
const authUserId = payload.auth_user_id;
|
|
84
98
|
if (authUserId !== undefined &&
|
|
85
99
|
(typeof authUserId !== "string" ||
|
|
@@ -91,6 +105,7 @@ export async function verifyAgentChatStreamToken(token) {
|
|
|
91
105
|
return {
|
|
92
106
|
ownerEmail: validateOwnerEmail(payload.sub),
|
|
93
107
|
orgId,
|
|
108
|
+
...(orgScope === "personal" ? { orgScope: "personal" } : {}),
|
|
94
109
|
...(authUserId ? { authUserId } : {}),
|
|
95
110
|
};
|
|
96
111
|
// coercion-ok: signature, issuer, audience, and expiry failures mean invalid authorization.
|
|
@@ -67,12 +67,12 @@ export declare function createAgentEngineApiKeyHandler(): import("h3").EventHand
|
|
|
67
67
|
code: any;
|
|
68
68
|
} | {
|
|
69
69
|
errorCode?: undefined;
|
|
70
|
+
error?: undefined;
|
|
70
71
|
code?: undefined;
|
|
71
72
|
ok: boolean;
|
|
72
73
|
key: string;
|
|
73
74
|
baseUrlKey?: string;
|
|
74
75
|
scope: AgentEngineApiKeyScope;
|
|
75
76
|
defaultModel?: import("./agent-engine-default-model-route.js").SavedKeyDefaultModelOutcome;
|
|
76
|
-
error?: undefined;
|
|
77
77
|
}>>;
|
|
78
78
|
export {};
|
|
@@ -8,17 +8,17 @@ export declare const AGENT_ENGINE_API_KEY_ACTION_NAME = "agent-engine-api-key";
|
|
|
8
8
|
* resolveEngine's credential detection.
|
|
9
9
|
*/
|
|
10
10
|
export declare function createAgentEngineDisconnectHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
11
|
-
error: string;
|
|
12
11
|
ok?: undefined;
|
|
12
|
+
error: string;
|
|
13
13
|
scope?: undefined;
|
|
14
14
|
} | {
|
|
15
15
|
ok: boolean;
|
|
16
16
|
error: any;
|
|
17
17
|
scope?: undefined;
|
|
18
18
|
} | {
|
|
19
|
-
error?: undefined;
|
|
20
19
|
ok: boolean;
|
|
21
20
|
scope: "org" | "user";
|
|
21
|
+
error?: undefined;
|
|
22
22
|
}>>;
|
|
23
23
|
export interface DefaultModelSelectionRequest {
|
|
24
24
|
engine: string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { defineEventHandler, getMethod, setResponseStatus, } from "h3";
|
|
2
2
|
import { clearDefaultAgentEngineSelection, recordDefaultAgentEngineRefusal, resolveDefaultAgentEngineAuthority, } from "../agent/default-agent-engine.js";
|
|
3
|
-
import {
|
|
3
|
+
import { resolveAgentRunOrgScope } from "./agent-run-context.js";
|
|
4
4
|
import { getSession } from "./auth.js";
|
|
5
5
|
import { runWithRequestContext } from "./request-context.js";
|
|
6
6
|
export const AGENT_ENGINE_DISCONNECT_ACTION_NAME = "agent-engine-disconnect";
|
|
@@ -26,8 +26,24 @@ export function createAgentEngineDisconnectHandler() {
|
|
|
26
26
|
// Not caught separately: an unreadable org context must not downgrade an
|
|
27
27
|
// org member's clear to a personal one that answers "ok" and changes
|
|
28
28
|
// nothing.
|
|
29
|
-
const
|
|
30
|
-
|
|
29
|
+
const orgScope = await resolveAgentRunOrgScope({
|
|
30
|
+
event,
|
|
31
|
+
ownerContext: { owner: session.email, anonymous: false },
|
|
32
|
+
});
|
|
33
|
+
if (!orgScope.orgId && orgScope.orgScope !== "personal") {
|
|
34
|
+
setResponseStatus(event, 503);
|
|
35
|
+
return {
|
|
36
|
+
ok: false,
|
|
37
|
+
error: "Organization scope is temporarily unavailable.",
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
const ctx = {
|
|
41
|
+
userEmail: session.email,
|
|
42
|
+
orgId: orgScope.orgId,
|
|
43
|
+
...(orgScope.orgScope === "personal"
|
|
44
|
+
? { orgScope: "personal" }
|
|
45
|
+
: {}),
|
|
46
|
+
};
|
|
31
47
|
const meta = {
|
|
32
48
|
actionName: AGENT_ENGINE_DISCONNECT_ACTION_NAME,
|
|
33
49
|
caller: "http",
|
|
@@ -88,19 +104,36 @@ export async function selectDefaultModelForSavedKey(event, input) {
|
|
|
88
104
|
if (!userEmail)
|
|
89
105
|
return { status: "skipped", reason: "not-allowed" };
|
|
90
106
|
let orgId;
|
|
107
|
+
let orgScope;
|
|
91
108
|
if (input.keyScope === "org") {
|
|
92
109
|
orgId = input.keyScopeId;
|
|
93
110
|
}
|
|
94
111
|
else {
|
|
95
|
-
|
|
112
|
+
const resolved = await resolveAgentRunOrgScope({
|
|
113
|
+
event,
|
|
114
|
+
ownerContext: { owner: userEmail, anonymous: false },
|
|
115
|
+
});
|
|
116
|
+
orgId = resolved.orgId;
|
|
117
|
+
orgScope = resolved.orgScope;
|
|
118
|
+
if (!orgId && orgScope !== "personal") {
|
|
119
|
+
return { status: "skipped", reason: "not-allowed" };
|
|
120
|
+
}
|
|
96
121
|
if (orgId)
|
|
97
122
|
return { status: "skipped", reason: "personal-key" };
|
|
98
123
|
}
|
|
99
|
-
const ctx = {
|
|
124
|
+
const ctx = {
|
|
125
|
+
userEmail,
|
|
126
|
+
orgId: orgId ?? null,
|
|
127
|
+
...(orgScope ? { orgScope } : {}),
|
|
128
|
+
};
|
|
100
129
|
// Loaded here so the key-save route doesn't pull the engine registry into
|
|
101
130
|
// its module graph for saves that don't touch the default.
|
|
102
131
|
const { selectDefaultAgentEngine } = await import("../scripts/agent-engines/set-agent-engine.js");
|
|
103
|
-
const result = await runWithRequestContext({
|
|
132
|
+
const result = await runWithRequestContext({
|
|
133
|
+
userEmail,
|
|
134
|
+
...(orgId ? { orgId } : {}),
|
|
135
|
+
...(orgScope ? { orgScope } : {}),
|
|
136
|
+
}, () => selectDefaultAgentEngine(input.request, { actionName: AGENT_ENGINE_API_KEY_ACTION_NAME, caller: "http" }, ctx));
|
|
104
137
|
if (result.status === "selected") {
|
|
105
138
|
return { status: "selected", engine: result.engine, model: result.model };
|
|
106
139
|
}
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
export declare function memoizeAgentEngineStatus<T>(identity: {
|
|
2
2
|
userEmail?: string | null;
|
|
3
3
|
orgId?: string | null;
|
|
4
|
+
orgScope?: "personal" | null;
|
|
5
|
+
isDirectLoopbackRequest?: boolean;
|
|
4
6
|
}, load: () => Promise<T>): Promise<T>;
|
|
5
7
|
export declare function getMemoizedAgentEngineStatus<T>(identity: {
|
|
6
8
|
userEmail?: string | null;
|
|
7
9
|
orgId?: string | null;
|
|
10
|
+
orgScope?: "personal" | null;
|
|
11
|
+
isDirectLoopbackRequest?: boolean;
|
|
8
12
|
}): Promise<T> | undefined;
|
|
9
13
|
export declare function invalidateAgentEngineStatusCache(): void;
|
|
10
14
|
/** @internal Test assertion for bounded status-cache retention. */
|
|
@@ -20,6 +20,8 @@ function statusCacheKey(identity) {
|
|
|
20
20
|
return JSON.stringify([
|
|
21
21
|
identity.userEmail?.trim().toLowerCase() ?? null,
|
|
22
22
|
identity.orgId?.trim() || null,
|
|
23
|
+
identity.orgScope === "personal",
|
|
24
|
+
identity.isDirectLoopbackRequest === true,
|
|
23
25
|
]);
|
|
24
26
|
}
|
|
25
27
|
export function memoizeAgentEngineStatus(identity, load) {
|
|
@@ -22,11 +22,20 @@ export declare function seedBackgroundAgentRunOwnerContext(event: H3Event, runId
|
|
|
22
22
|
export declare function resolveAgentRunOwnerContext(event: H3Event, options?: {
|
|
23
23
|
anonymousOwner?: AnonymousOwnerResolver;
|
|
24
24
|
}): Promise<AgentRunOwnerContext>;
|
|
25
|
+
export type AgentRunOrgScope = {
|
|
26
|
+
orgId?: string;
|
|
27
|
+
orgScope?: "personal";
|
|
28
|
+
};
|
|
25
29
|
export declare function resolveAgentRunOrgId(options: {
|
|
26
30
|
event: H3Event;
|
|
27
31
|
ownerContext: AgentRunOwnerContext;
|
|
28
32
|
resolveOrgId?: OrgIdResolver;
|
|
29
33
|
}): Promise<string | undefined>;
|
|
34
|
+
export declare function resolveAgentRunOrgScope(options: {
|
|
35
|
+
event: H3Event;
|
|
36
|
+
ownerContext: AgentRunOwnerContext;
|
|
37
|
+
resolveOrgId?: OrgIdResolver;
|
|
38
|
+
}): Promise<AgentRunOrgScope>;
|
|
30
39
|
export declare function resolveAgentRunRequestContext(options: {
|
|
31
40
|
event: H3Event;
|
|
32
41
|
ownerContext: AgentRunOwnerContext;
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import { createError, getHeader } from "h3";
|
|
2
|
-
import { AGENT_CHAT_BROWSER_SESSION_ID_FIELD } from "../agent/durable-background.js";
|
|
2
|
+
import { AGENT_CHAT_BROWSER_SESSION_ID_FIELD, AGENT_CHAT_REQUEST_CONTEXT_FIELD, } from "../agent/durable-background.js";
|
|
3
3
|
import { ANALYTICS_CLIENT_PLATFORM_BODY_FIELD, ANALYTICS_CLIENT_PLATFORM_HEADER, normalizeAnalyticsClientPlatform, } from "../shared/analytics-platform.js";
|
|
4
4
|
import { SYNTHETIC_TRAFFIC_HEADER, isSyntheticTrafficValue, } from "../shared/test-traffic.js";
|
|
5
5
|
import { CREDENTIAL_MEMBERSHIP_UNAVAILABLE_MESSAGE, isCredentialMembershipUnavailable, } from "./credential-membership-unavailable.js";
|
|
6
|
-
import {
|
|
7
|
-
|
|
6
|
+
import { isDirectLoopbackRequest, isLoopbackRequest } from "./loopback.js";
|
|
7
|
+
import { getRequestContext, getRequestIdentityAuthenticatedAtMs, getRequestIdentitySessionToken, hasExplicitPersonalOrgScope, runWithRequestContext, } from "./request-context.js";
|
|
8
|
+
import { getForwardedRequestOrigin } from "./request-origin.js";
|
|
9
|
+
const resolveOrgScopeForEmail = (...args) => import("../org/context.js").then(({ resolveOrgScopeForEmail }) => resolveOrgScopeForEmail(...args));
|
|
10
|
+
async function hasOrgScopeResolutionUnavailable(event) {
|
|
11
|
+
const { hasOrgScopeResolutionUnavailable } = await import("../org/context.js");
|
|
12
|
+
return hasOrgScopeResolutionUnavailable(event);
|
|
13
|
+
}
|
|
8
14
|
const getOrgContext = (...args) => import("../org/context.js").then(({ getOrgContext }) => getOrgContext(...args));
|
|
9
15
|
export const AGENT_RUN_OWNER_CONTEXT_KEY = "__agentNativeOwnerContext";
|
|
10
16
|
function eventContext(event) {
|
|
@@ -153,10 +159,23 @@ export async function resolveAgentRunOwnerContext(event, options = {}) {
|
|
|
153
159
|
});
|
|
154
160
|
}
|
|
155
161
|
export async function resolveAgentRunOrgId(options) {
|
|
156
|
-
|
|
157
|
-
|
|
162
|
+
return (await resolveAgentRunOrgScope(options)).orgId;
|
|
163
|
+
}
|
|
164
|
+
export async function resolveAgentRunOrgScope(options) {
|
|
165
|
+
const { ownerContext } = options;
|
|
166
|
+
const hasOwnerOrgId = Object.prototype.hasOwnProperty.call(ownerContext, "orgId");
|
|
167
|
+
if (hasOwnerOrgId) {
|
|
168
|
+
const orgId = normalizeId(ownerContext.orgId);
|
|
169
|
+
if (orgId)
|
|
170
|
+
return { orgId };
|
|
171
|
+
return ownerContext.orgScope === "personal" ? { orgScope: "personal" } : {};
|
|
172
|
+
}
|
|
173
|
+
if (ownerContext.orgScope === "personal" ||
|
|
174
|
+
hasExplicitPersonalOrgScope(options.event)) {
|
|
175
|
+
return { orgScope: "personal" };
|
|
158
176
|
}
|
|
159
177
|
let resolvedOrgId;
|
|
178
|
+
let orgLookupFailed = false;
|
|
160
179
|
if (options.resolveOrgId) {
|
|
161
180
|
resolvedOrgId = normalizeId(await options.resolveOrgId(options.event));
|
|
162
181
|
}
|
|
@@ -167,32 +186,57 @@ export async function resolveAgentRunOrgId(options) {
|
|
|
167
186
|
resolvedOrgId = normalizeId(session?.orgId);
|
|
168
187
|
}
|
|
169
188
|
catch {
|
|
170
|
-
|
|
189
|
+
orgLookupFailed = true;
|
|
171
190
|
}
|
|
172
191
|
if (!resolvedOrgId) {
|
|
173
192
|
try {
|
|
174
193
|
const orgContext = await getOrgContext(options.event);
|
|
175
194
|
resolvedOrgId = normalizeId(orgContext.orgId);
|
|
195
|
+
const scopeUnavailable = await hasOrgScopeResolutionUnavailable(options.event);
|
|
196
|
+
if (!resolvedOrgId && scopeUnavailable)
|
|
197
|
+
orgLookupFailed = true;
|
|
198
|
+
if (!resolvedOrgId &&
|
|
199
|
+
!scopeUnavailable &&
|
|
200
|
+
!orgLookupFailed &&
|
|
201
|
+
ownerContext.owner &&
|
|
202
|
+
!ownerContext.anonymous &&
|
|
203
|
+
normalizeId(orgContext.email)?.toLowerCase() ===
|
|
204
|
+
ownerContext.owner.trim().toLowerCase()) {
|
|
205
|
+
return { orgScope: "personal" };
|
|
206
|
+
}
|
|
176
207
|
}
|
|
177
208
|
catch {
|
|
178
|
-
|
|
209
|
+
orgLookupFailed = true;
|
|
179
210
|
}
|
|
180
211
|
}
|
|
181
212
|
}
|
|
182
|
-
if (!resolvedOrgId &&
|
|
183
|
-
options.ownerContext.owner &&
|
|
184
|
-
!options.ownerContext.anonymous) {
|
|
213
|
+
if (!resolvedOrgId && ownerContext.owner && !ownerContext.anonymous) {
|
|
185
214
|
try {
|
|
186
|
-
|
|
215
|
+
const scope = await resolveOrgScopeForEmail(ownerContext.owner);
|
|
216
|
+
if (scope.status === "organization") {
|
|
217
|
+
resolvedOrgId = normalizeId(scope.orgId);
|
|
218
|
+
}
|
|
219
|
+
else if (scope.status === "unknown") {
|
|
220
|
+
orgLookupFailed = true;
|
|
221
|
+
}
|
|
222
|
+
else if (!orgLookupFailed) {
|
|
223
|
+
return { orgScope: "personal" };
|
|
224
|
+
}
|
|
187
225
|
}
|
|
188
226
|
catch {
|
|
189
|
-
|
|
227
|
+
orgLookupFailed = true;
|
|
190
228
|
}
|
|
191
229
|
}
|
|
192
|
-
|
|
230
|
+
if (resolvedOrgId)
|
|
231
|
+
return { orgId: resolvedOrgId };
|
|
232
|
+
// An absent org is personal only when every available membership lookup succeeded.
|
|
233
|
+
if (!orgLookupFailed && ownerContext.owner && !ownerContext.anonymous) {
|
|
234
|
+
return { orgScope: "personal" };
|
|
235
|
+
}
|
|
236
|
+
return {};
|
|
193
237
|
}
|
|
194
238
|
export async function resolveAgentRunRequestContext(options) {
|
|
195
|
-
const orgId = await
|
|
239
|
+
const { orgId, orgScope } = await resolveAgentRunOrgScope(options);
|
|
196
240
|
const timezone = readAgentRunTimezone(options.event);
|
|
197
241
|
const browserSessionId = options.isBackgroundWorker
|
|
198
242
|
? readDurableBrowserSessionId(options.event)
|
|
@@ -202,6 +246,17 @@ export async function resolveAgentRunRequestContext(options) {
|
|
|
202
246
|
const clientPlatform = readAnalyticsClientPlatformHeader(options.event);
|
|
203
247
|
const isSyntheticTraffic = readSyntheticTrafficHeader(options.event);
|
|
204
248
|
const waitUntil = requestWaitUntil(options.event);
|
|
249
|
+
const durableBody = options.isBackgroundWorker
|
|
250
|
+
? eventContext(options.event)["__agentChatBackgroundBody"]
|
|
251
|
+
: undefined;
|
|
252
|
+
const durableRequestContext = durableBody?.[AGENT_CHAT_REQUEST_CONTEXT_FIELD];
|
|
253
|
+
const durableOrigin = durableRequestContext &&
|
|
254
|
+
typeof durableRequestContext === "object" &&
|
|
255
|
+
!Array.isArray(durableRequestContext) &&
|
|
256
|
+
typeof durableRequestContext.requestOrigin ===
|
|
257
|
+
"string"
|
|
258
|
+
? durableRequestContext.requestOrigin
|
|
259
|
+
: undefined;
|
|
205
260
|
const run = {
|
|
206
261
|
...(options.isBackgroundWorker ? { isBackgroundWorker: true } : {}),
|
|
207
262
|
...(waitUntil ? { waitUntil } : {}),
|
|
@@ -211,10 +266,11 @@ export async function resolveAgentRunRequestContext(options) {
|
|
|
211
266
|
...(options.ownerContext.authUserId
|
|
212
267
|
? { authUserId: options.ownerContext.authUserId }
|
|
213
268
|
: {}),
|
|
214
|
-
...(
|
|
215
|
-
? { orgScope: "personal" }
|
|
216
|
-
: {}),
|
|
269
|
+
...(orgScope === "personal" ? { orgScope: "personal" } : {}),
|
|
217
270
|
...(options.ownerContext.anonymous ? { agentRunAnonymous: true } : {}),
|
|
271
|
+
...(options.ownerContext.anonymous && !orgId
|
|
272
|
+
? { defaultAgentEngineScope: "deployment" }
|
|
273
|
+
: {}),
|
|
218
274
|
...(options.ownerContext.identityAuthenticatedAtMs !== undefined
|
|
219
275
|
? {
|
|
220
276
|
identityAuthenticatedAtMs: options.ownerContext.identityAuthenticatedAtMs,
|
|
@@ -227,6 +283,15 @@ export async function resolveAgentRunRequestContext(options) {
|
|
|
227
283
|
...(browserSessionId ? { browserSessionId } : {}),
|
|
228
284
|
...(clientPlatform ? { clientPlatform } : {}),
|
|
229
285
|
...(isSyntheticTraffic ? { isSyntheticTraffic: true } : {}),
|
|
286
|
+
requestOrigin: options.isBackgroundWorker
|
|
287
|
+
? durableOrigin
|
|
288
|
+
: getForwardedRequestOrigin(options.event),
|
|
289
|
+
isLoopbackRequest: options.isBackgroundWorker
|
|
290
|
+
? false
|
|
291
|
+
: isLoopbackRequest(options.event),
|
|
292
|
+
isDirectLoopbackRequest: options.isBackgroundWorker
|
|
293
|
+
? false
|
|
294
|
+
: isDirectLoopbackRequest(options.event),
|
|
230
295
|
...(browserTabId ? { run: { ...run, browserTabId } } : {}),
|
|
231
296
|
...(!browserTabId && Object.keys(run).length > 0 ? { run } : {}),
|
|
232
297
|
};
|
package/dist/server/auth.js
CHANGED
|
@@ -271,10 +271,15 @@ async function enrichLegacySessionIdentity(session, canonicalUser) {
|
|
|
271
271
|
// coercion-ok: preserve the valid legacy session when optional profile enrichment is unreadable.
|
|
272
272
|
.catch(() => null);
|
|
273
273
|
}
|
|
274
|
-
if (!existing
|
|
274
|
+
if (!existing ||
|
|
275
|
+
existing.user.email.trim().toLowerCase() !==
|
|
276
|
+
session.email.trim().toLowerCase()) {
|
|
275
277
|
return session;
|
|
278
|
+
}
|
|
276
279
|
return {
|
|
277
280
|
...session,
|
|
281
|
+
authUserId: session.authUserId ?? existing.user.id,
|
|
282
|
+
userId: session.userId ?? existing.user.id,
|
|
278
283
|
...(!session.name?.trim() && existing.user.name?.trim()
|
|
279
284
|
? { name: existing.user.name.trim() }
|
|
280
285
|
: {}),
|