@agent-native/core 0.209.1 → 0.209.2-nightly-20261015213030
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/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/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- 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/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/progress/routes.d.ts +1 -1
- 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/resources/handlers.d.ts +1 -1
- 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-ollama-models-route.d.ts +2 -2
- 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/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/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 +5 -3
- 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
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export const RECORDER_EVENT_TYPES = [
|
|
3
|
+
"action",
|
|
4
|
+
"state",
|
|
5
|
+
"nav",
|
|
6
|
+
"error",
|
|
7
|
+
"toast",
|
|
8
|
+
"log",
|
|
9
|
+
];
|
|
10
|
+
// Loose objects: a field the recorder adds later is stored, not stripped.
|
|
11
|
+
const recorderEventSchema = z.looseObject({
|
|
12
|
+
t: z.number(),
|
|
13
|
+
at: z.number(),
|
|
14
|
+
type: z.enum(RECORDER_EVENT_TYPES),
|
|
15
|
+
name: z.string().optional(),
|
|
16
|
+
ok: z.boolean().optional(),
|
|
17
|
+
status: z.number().optional(),
|
|
18
|
+
errorCode: z.string().optional(),
|
|
19
|
+
ms: z.number().optional(),
|
|
20
|
+
requestId: z.string().optional(),
|
|
21
|
+
data: z.record(z.string(), z.unknown()).optional(),
|
|
22
|
+
});
|
|
23
|
+
export const recorderReportSchema = z.looseObject({
|
|
24
|
+
app: z.string().max(200),
|
|
25
|
+
build: z.string(),
|
|
26
|
+
url: z.string(),
|
|
27
|
+
at: z.string(),
|
|
28
|
+
reason: z.enum(["user", "error", "test", "manual"]),
|
|
29
|
+
windowSeconds: z.number(),
|
|
30
|
+
events: z.array(recorderEventSchema),
|
|
31
|
+
watched: z.record(z.string(), z.unknown()),
|
|
32
|
+
pending: z.array(z.looseObject({ type: z.string(), name: z.string(), ageMs: z.number() })),
|
|
33
|
+
dropped: z.number(),
|
|
34
|
+
});
|
|
@@ -76,7 +76,6 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
|
|
|
76
76
|
}>>;
|
|
77
77
|
}, z.core.$strip>>, {
|
|
78
78
|
id?: undefined;
|
|
79
|
-
provider?: undefined;
|
|
80
79
|
deleted?: undefined;
|
|
81
80
|
message?: undefined;
|
|
82
81
|
providers: {
|
|
@@ -89,44 +88,45 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
|
|
|
89
88
|
updatedAt: number;
|
|
90
89
|
}[];
|
|
91
90
|
count: number;
|
|
91
|
+
provider?: undefined;
|
|
92
92
|
found?: undefined;
|
|
93
93
|
registered?: undefined;
|
|
94
94
|
label?: undefined;
|
|
95
95
|
} | {
|
|
96
96
|
id?: undefined;
|
|
97
97
|
deleted?: undefined;
|
|
98
|
+
count?: undefined;
|
|
98
99
|
message?: undefined;
|
|
99
100
|
providers?: undefined;
|
|
100
|
-
count?: undefined;
|
|
101
101
|
found: boolean;
|
|
102
102
|
provider: import("../custom-registry.js").CustomProviderConfig;
|
|
103
103
|
registered?: undefined;
|
|
104
104
|
label?: undefined;
|
|
105
105
|
} | {
|
|
106
|
-
provider?: undefined;
|
|
107
106
|
deleted?: undefined;
|
|
107
|
+
count?: undefined;
|
|
108
108
|
message?: undefined;
|
|
109
109
|
providers?: undefined;
|
|
110
|
-
|
|
110
|
+
provider?: undefined;
|
|
111
111
|
found: boolean;
|
|
112
112
|
id: string;
|
|
113
113
|
registered?: undefined;
|
|
114
114
|
label?: undefined;
|
|
115
115
|
} | {
|
|
116
|
-
|
|
116
|
+
count?: undefined;
|
|
117
117
|
message?: undefined;
|
|
118
118
|
providers?: undefined;
|
|
119
|
-
|
|
119
|
+
provider?: undefined;
|
|
120
120
|
found?: undefined;
|
|
121
121
|
deleted: boolean;
|
|
122
122
|
id: string;
|
|
123
123
|
registered?: undefined;
|
|
124
124
|
label?: undefined;
|
|
125
125
|
} | {
|
|
126
|
-
provider?: undefined;
|
|
127
126
|
deleted?: undefined;
|
|
128
|
-
providers?: undefined;
|
|
129
127
|
count?: undefined;
|
|
128
|
+
providers?: undefined;
|
|
129
|
+
provider?: undefined;
|
|
130
130
|
found?: undefined;
|
|
131
131
|
registered: boolean;
|
|
132
132
|
id: string;
|
|
@@ -317,9 +317,6 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
|
|
|
317
317
|
scope?: "org" | "user" | undefined;
|
|
318
318
|
}, {
|
|
319
319
|
message?: undefined;
|
|
320
|
-
id?: undefined;
|
|
321
|
-
deleted?: undefined;
|
|
322
|
-
provider?: undefined;
|
|
323
320
|
providers: {
|
|
324
321
|
id: string;
|
|
325
322
|
label: string;
|
|
@@ -330,45 +327,48 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
|
|
|
330
327
|
updatedAt: number;
|
|
331
328
|
}[];
|
|
332
329
|
count: number;
|
|
330
|
+
provider?: undefined;
|
|
333
331
|
found?: undefined;
|
|
332
|
+
deleted?: undefined;
|
|
334
333
|
registered?: undefined;
|
|
334
|
+
id?: undefined;
|
|
335
335
|
label?: undefined;
|
|
336
336
|
} | {
|
|
337
|
+
count?: undefined;
|
|
337
338
|
message?: undefined;
|
|
338
|
-
id?: undefined;
|
|
339
|
-
deleted?: undefined;
|
|
340
339
|
providers?: undefined;
|
|
341
|
-
count?: undefined;
|
|
342
340
|
found: boolean;
|
|
343
341
|
provider: import("../custom-registry.js").CustomProviderConfig;
|
|
342
|
+
deleted?: undefined;
|
|
344
343
|
registered?: undefined;
|
|
344
|
+
id?: undefined;
|
|
345
345
|
label?: undefined;
|
|
346
346
|
} | {
|
|
347
|
+
count?: undefined;
|
|
347
348
|
message?: undefined;
|
|
348
|
-
deleted?: undefined;
|
|
349
|
-
provider?: undefined;
|
|
350
349
|
providers?: undefined;
|
|
351
|
-
|
|
350
|
+
provider?: undefined;
|
|
352
351
|
found: boolean;
|
|
353
352
|
id: string;
|
|
353
|
+
deleted?: undefined;
|
|
354
354
|
registered?: undefined;
|
|
355
355
|
label?: undefined;
|
|
356
356
|
} | {
|
|
357
|
+
count?: undefined;
|
|
357
358
|
message?: undefined;
|
|
358
|
-
provider?: undefined;
|
|
359
359
|
providers?: undefined;
|
|
360
|
-
|
|
360
|
+
provider?: undefined;
|
|
361
361
|
found?: undefined;
|
|
362
362
|
deleted: boolean;
|
|
363
363
|
id: string;
|
|
364
364
|
registered?: undefined;
|
|
365
365
|
label?: undefined;
|
|
366
366
|
} | {
|
|
367
|
-
deleted?: undefined;
|
|
368
|
-
provider?: undefined;
|
|
369
|
-
providers?: undefined;
|
|
370
367
|
count?: undefined;
|
|
368
|
+
providers?: undefined;
|
|
369
|
+
provider?: undefined;
|
|
371
370
|
found?: undefined;
|
|
371
|
+
deleted?: undefined;
|
|
372
372
|
registered: boolean;
|
|
373
373
|
id: string;
|
|
374
374
|
label: string;
|
|
@@ -73,6 +73,7 @@ export declare function createProviderCorpusJobAction(options: CreateProviderCor
|
|
|
73
73
|
offset?: unknown;
|
|
74
74
|
limit?: unknown;
|
|
75
75
|
}, {
|
|
76
|
+
deleted?: undefined;
|
|
76
77
|
jobs: {
|
|
77
78
|
id: string;
|
|
78
79
|
name: string;
|
|
@@ -83,7 +84,6 @@ export declare function createProviderCorpusJobAction(options: CreateProviderCor
|
|
|
83
84
|
updatedAt: string;
|
|
84
85
|
}[];
|
|
85
86
|
total: number;
|
|
86
|
-
deleted?: undefined;
|
|
87
87
|
jobId?: undefined;
|
|
88
88
|
} | {
|
|
89
89
|
jobs?: undefined;
|
|
@@ -91,9 +91,9 @@ export declare function createProviderCorpusJobAction(options: CreateProviderCor
|
|
|
91
91
|
deleted: boolean;
|
|
92
92
|
jobId: string;
|
|
93
93
|
} | {
|
|
94
|
+
deleted?: undefined;
|
|
94
95
|
jobs?: undefined;
|
|
95
96
|
total?: undefined;
|
|
96
|
-
deleted?: undefined;
|
|
97
97
|
jobId?: undefined;
|
|
98
98
|
hits: Record<string, unknown>[];
|
|
99
99
|
offset: number;
|
|
@@ -39,8 +39,8 @@ export declare function handleUpdateResource(event: any): Promise<import("./stor
|
|
|
39
39
|
error: string;
|
|
40
40
|
}>;
|
|
41
41
|
export declare function handleDeleteResource(event: any): Promise<{
|
|
42
|
-
error: string;
|
|
43
42
|
ok?: undefined;
|
|
43
|
+
error: string;
|
|
44
44
|
} | {
|
|
45
45
|
error?: undefined;
|
|
46
46
|
ok: boolean;
|
|
@@ -3,7 +3,7 @@ import { CHATGPT_SUBSCRIPTION_ENGINE_NAME } from "../../agent/chatgpt-subscripti
|
|
|
3
3
|
import { listChatGPTSubscriptionModels } from "../../agent/engine/chatgpt-subscription-engine.js";
|
|
4
4
|
import { getAgentEngineEntry, isAgentEnginePackageInstalled, normalizeModelForEngine, resolveEngineAcceptsCustomModels, resolveEnginePreservesCustomModels, registerBuiltinEngines, } from "../../agent/engine/index.js";
|
|
5
5
|
import { defaultModelMessagesForUser } from "../../localization/default-model-messages.js";
|
|
6
|
-
import { getRequestOrgId, getRequestUserEmail, } from "../../server/request-context.js";
|
|
6
|
+
import { getRequestContext, getRequestOrgId, getRequestUserEmail, } from "../../server/request-context.js";
|
|
7
7
|
import { run as runList } from "./list-agent-engines.js";
|
|
8
8
|
import { run as runSet, resolveDefaultModelEffective, effectiveConfigurationMessage, } from "./set-agent-engine.js";
|
|
9
9
|
import { run as runTest } from "./test-agent-engine.js";
|
|
@@ -46,9 +46,11 @@ export const tool = {
|
|
|
46
46
|
};
|
|
47
47
|
function currentContext() {
|
|
48
48
|
try {
|
|
49
|
+
const context = getRequestContext();
|
|
49
50
|
return {
|
|
50
51
|
userEmail: getRequestUserEmail(),
|
|
51
52
|
orgId: getRequestOrgId(),
|
|
53
|
+
...(context?.orgScope === "personal" ? { orgScope: "personal" } : {}),
|
|
52
54
|
};
|
|
53
55
|
}
|
|
54
56
|
catch {
|
|
@@ -64,7 +66,7 @@ async function runGetAppDefault(args) {
|
|
|
64
66
|
return "Error: appId is required";
|
|
65
67
|
const ctx = currentContext();
|
|
66
68
|
const settings = await readAgentAppModelDefaultSettings(ctx, appId);
|
|
67
|
-
const canUpdate = await canUpdateAgentAppModelDefaultSettings(ctx.userEmail, ctx.orgId);
|
|
69
|
+
const canUpdate = await canUpdateAgentAppModelDefaultSettings(ctx.userEmail, ctx.orgId, ctx.orgScope);
|
|
68
70
|
return JSON.stringify({ ok: true, ...settings, canUpdate }, null, 2);
|
|
69
71
|
}
|
|
70
72
|
async function runSetAppDefault(args) {
|
|
@@ -79,6 +81,9 @@ async function runSetAppDefault(args) {
|
|
|
79
81
|
if (!model)
|
|
80
82
|
return "Error: model is required";
|
|
81
83
|
const ctx = currentContext();
|
|
84
|
+
if (!ctx.orgId && ctx.orgScope !== "personal") {
|
|
85
|
+
return "Error: Organization scope could not be resolved.";
|
|
86
|
+
}
|
|
82
87
|
if (engine === CHATGPT_SUBSCRIPTION_ENGINE_NAME) {
|
|
83
88
|
if (ctx.orgId) {
|
|
84
89
|
return "Error: ChatGPT plan access is personal and cannot be selected as an organization default.";
|
|
@@ -108,7 +113,7 @@ async function runSetAppDefault(args) {
|
|
|
108
113
|
acceptsCustomModels,
|
|
109
114
|
preserveCustomModels,
|
|
110
115
|
});
|
|
111
|
-
const canUpdate = await canUpdateAgentAppModelDefaultSettings(ctx.userEmail, ctx.orgId);
|
|
116
|
+
const canUpdate = await canUpdateAgentAppModelDefaultSettings(ctx.userEmail, ctx.orgId, ctx.orgScope);
|
|
112
117
|
if (!canUpdate) {
|
|
113
118
|
return ctx.orgId
|
|
114
119
|
? "Error: Only organization owners and admins can change app model defaults."
|
|
@@ -141,7 +146,7 @@ async function runResetAppDefault(args) {
|
|
|
141
146
|
if (!appId)
|
|
142
147
|
return "Error: appId is required";
|
|
143
148
|
const ctx = currentContext();
|
|
144
|
-
const canUpdate = await canUpdateAgentAppModelDefaultSettings(ctx.userEmail, ctx.orgId);
|
|
149
|
+
const canUpdate = await canUpdateAgentAppModelDefaultSettings(ctx.userEmail, ctx.orgId, ctx.orgScope);
|
|
145
150
|
if (!canUpdate) {
|
|
146
151
|
return ctx.orgId
|
|
147
152
|
? "Error: Only organization owners and admins can reset app model defaults."
|
|
@@ -13,7 +13,7 @@ import { getAppConfig } from "../../app-config/index.js";
|
|
|
13
13
|
import { CHATGPT_SUBSCRIPTION_LAB } from "../../labs/core-labs.js";
|
|
14
14
|
import { getUserLabEnabled } from "../../labs/store.js";
|
|
15
15
|
import { defaultModelMessagesForUser } from "../../localization/default-model-messages.js";
|
|
16
|
-
import { getRequestOrgId, getRequestUserEmail, } from "../../server/request-context.js";
|
|
16
|
+
import { getRequestContext, getRequestOrgId, getRequestUserEmail, } from "../../server/request-context.js";
|
|
17
17
|
export const tool = {
|
|
18
18
|
description: 'Set only the organization\'s default AI engine and model (or a no-organization user\'s personal default). Preserve every app override and explicit chat/automation model. For an app default, use manage-agent-engine with action="set-app-default"; inheritance requires action="reset-app-default". If the user\'s requested scope is ambiguous, clarify organization/personal versus app before changing anything. The result separates the saved default from the current app\'s effective model. Only organization owners and admins can change it. Use manage-agent-engine with action="list" first to see options and canUpdateDefault.',
|
|
19
19
|
parameters: {
|
|
@@ -33,6 +33,9 @@ export const tool = {
|
|
|
33
33
|
};
|
|
34
34
|
export const DEFAULT_MODEL_ADMIN_REQUIRED_ERROR_CODE = "default_model_admin_required";
|
|
35
35
|
export async function resolveDefaultModelEffective(ctx, appId, planned = {}) {
|
|
36
|
+
if (!ctx.orgId && ctx.orgScope !== "personal") {
|
|
37
|
+
throw new Error("Organization scope could not be resolved.");
|
|
38
|
+
}
|
|
36
39
|
const credentialIdentity = ctx.userEmail
|
|
37
40
|
? { userEmail: ctx.userEmail, orgId: ctx.orgId }
|
|
38
41
|
: undefined;
|
|
@@ -92,6 +95,9 @@ export function effectiveConfigurationMessage(effective, messages) {
|
|
|
92
95
|
export async function selectDefaultAgentEngine(input, meta, ctx = {
|
|
93
96
|
userEmail: getRequestUserEmail(),
|
|
94
97
|
orgId: getRequestOrgId(),
|
|
98
|
+
...(getRequestContext()?.orgScope === "personal"
|
|
99
|
+
? { orgScope: "personal" }
|
|
100
|
+
: {}),
|
|
95
101
|
}) {
|
|
96
102
|
registerBuiltinEngines();
|
|
97
103
|
const engineName = input.engine?.trim();
|
|
@@ -386,6 +386,9 @@ export const ALWAYS_ON_CORE_ACTIONS = new Set([
|
|
|
386
386
|
"set-tool-approval-policy",
|
|
387
387
|
// The client's same-tick GET batching depends on it, so it cannot be gated.
|
|
388
388
|
"get-actions-batch",
|
|
389
|
+
// The shared Report a problem entry calls these in every app.
|
|
390
|
+
"save-problem-report",
|
|
391
|
+
"read-problem-report",
|
|
389
392
|
]);
|
|
390
393
|
export async function mergeCoreSharingActions(registry) {
|
|
391
394
|
const entries = [
|
|
@@ -469,6 +472,14 @@ export async function mergeCoreSharingActions(registry) {
|
|
|
469
472
|
() => import("../agent/actions/get-agentkit-capabilities.js"),
|
|
470
473
|
],
|
|
471
474
|
["upload-image", () => import("../file-upload/actions/upload-image.js")],
|
|
475
|
+
[
|
|
476
|
+
"save-problem-report",
|
|
477
|
+
() => import("../problem-report/actions/save-problem-report.js"),
|
|
478
|
+
],
|
|
479
|
+
[
|
|
480
|
+
"read-problem-report",
|
|
481
|
+
() => import("../problem-report/actions/read-problem-report.js"),
|
|
482
|
+
],
|
|
472
483
|
[
|
|
473
484
|
"get-file-storage",
|
|
474
485
|
() => import("../file-upload/actions/get-file-storage.js"),
|
|
@@ -48,6 +48,7 @@ function currentBuildId() {
|
|
|
48
48
|
}
|
|
49
49
|
import { isLoopbackRequest, registerAuthPublicPaths } from "./auth.js";
|
|
50
50
|
import { getH3App } from "./framework-request-handler.js";
|
|
51
|
+
import { isDirectLoopbackRequest } from "./loopback.js";
|
|
51
52
|
import { getVerifiedServiceIdentityFromEvent, getRequestIdentityAuthenticatedAtMs, getRequestIdentitySessionToken, hasExplicitPersonalOrgScope, markExplicitPersonalOrgScope, runWithRequestContext, } from "./request-context.js";
|
|
52
53
|
const ROUTE_PREFIX = ACTION_ROUTE_PREFIX;
|
|
53
54
|
const WEBMCP_ACTION_ROUTE_PREFIX = "/_agent-native/webmcp/actions";
|
|
@@ -815,6 +816,7 @@ function mountActionRoutesInternal(nitroApp, actions, options) {
|
|
|
815
816
|
requestOrigin: getForwardedRequestOrigin(event),
|
|
816
817
|
federationMembershipValidated: isFederationMembershipValidatedForEvent(event, userEmail, orgId),
|
|
817
818
|
isLoopbackRequest: isLoopbackRequest(event),
|
|
819
|
+
isDirectLoopbackRequest: isDirectLoopbackRequest(event),
|
|
818
820
|
}, async () => {
|
|
819
821
|
if (typeof entry.maxBodyBytes === "number" && method !== "GET") {
|
|
820
822
|
const clRaw = getHeader(event, "content-length");
|
|
@@ -9,9 +9,8 @@ export declare function isBuilderChatSetupReady(input: {
|
|
|
9
9
|
}): boolean;
|
|
10
10
|
/**
|
|
11
11
|
* Chat accepts the Builder gateway/OAuth lane, a usable BYOK API key, or a
|
|
12
|
-
* configured OpenAI-compatible/Ollama endpoint
|
|
13
|
-
*
|
|
14
|
-
* qualify.
|
|
12
|
+
* configured OpenAI-compatible/Ollama endpoint, or a usable personal ChatGPT
|
|
13
|
+
* subscription account. Engine selection by itself is not readiness.
|
|
15
14
|
*/
|
|
16
15
|
export declare function isAgentChatAiSetupReady(input?: {
|
|
17
16
|
status?: AgentEngineStatusResult;
|
|
@@ -3,8 +3,9 @@ import { CHATGPT_SUBSCRIPTION_ENGINE_NAME } from "../agent/chatgpt-subscription-
|
|
|
3
3
|
import { OLLAMA_BASE_URL_ENV_VAR, OPENAI_BASE_URL_ENV_VAR, PROVIDER_ENV_VARS, } from "../agent/engine/provider-env-vars.js";
|
|
4
4
|
import { getAgentEngineEntry } from "../agent/engine/registry.js";
|
|
5
5
|
import { hasUsableBuilderOAuthSessionForReadiness } from "./builder-oauth.js";
|
|
6
|
+
import { hasChatGPTSubscriptionCredential } from "./chatgpt-subscription-oauth.js";
|
|
6
7
|
import { assertCredentialStoreReadable, getProviderCredentialAuthFailure, prefetchSecrets, resolveBuilderGatewayCredentialsDetailed, resolveBuilderPrivateKey, resolveSecretDetailed, } from "./credential-provider.js";
|
|
7
|
-
import { getRequestOrgId, getRequestUserEmail } from "./request-context.js";
|
|
8
|
+
import { getRequestContext, getRequestOrgId, getRequestUserEmail, } from "./request-context.js";
|
|
8
9
|
export const AGENT_CHAT_AI_SETUP_REQUIRED_CODE = "AGENT_CHAT_AI_SETUP_REQUIRED";
|
|
9
10
|
export function isBuilderChatSetupReady(input) {
|
|
10
11
|
return Boolean(input.oauthSessionUsable ||
|
|
@@ -13,9 +14,8 @@ export function isBuilderChatSetupReady(input) {
|
|
|
13
14
|
}
|
|
14
15
|
/**
|
|
15
16
|
* Chat accepts the Builder gateway/OAuth lane, a usable BYOK API key, or a
|
|
16
|
-
* configured OpenAI-compatible/Ollama endpoint
|
|
17
|
-
*
|
|
18
|
-
* qualify.
|
|
17
|
+
* configured OpenAI-compatible/Ollama endpoint, or a usable personal ChatGPT
|
|
18
|
+
* subscription account. Engine selection by itself is not readiness.
|
|
19
19
|
*/
|
|
20
20
|
export async function isAgentChatAiSetupReady(input) {
|
|
21
21
|
if (input?.status && input.detectFromUserSecrets) {
|
|
@@ -42,6 +42,10 @@ export async function isAgentChatAiSetupReady(input) {
|
|
|
42
42
|
return true;
|
|
43
43
|
unreadable ||= readiness.unreadable;
|
|
44
44
|
}
|
|
45
|
+
const ownerEmail = getRequestUserEmail();
|
|
46
|
+
if (ownerEmail && (await hasEligibleChatGPTSubscription(ownerEmail))) {
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
45
49
|
if (unreadable)
|
|
46
50
|
throwAgentChatSetupReadUnavailable();
|
|
47
51
|
return false;
|
|
@@ -103,8 +107,18 @@ export async function isAgentChatAiSetupReady(input) {
|
|
|
103
107
|
assertCredentialStoreReadable(unreadable);
|
|
104
108
|
throwAgentChatSetupReadUnavailable();
|
|
105
109
|
}
|
|
110
|
+
if (ownerEmail && (await hasEligibleChatGPTSubscription(ownerEmail))) {
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
106
113
|
return false;
|
|
107
114
|
}
|
|
115
|
+
async function hasEligibleChatGPTSubscription(ownerEmail) {
|
|
116
|
+
const requestContext = getRequestContext();
|
|
117
|
+
if (getRequestOrgId() || requestContext?.orgScope !== "personal") {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
return hasChatGPTSubscriptionCredential(ownerEmail);
|
|
121
|
+
}
|
|
108
122
|
async function checkProviderEntryReadiness(entry) {
|
|
109
123
|
if (!entry || !isChatSetupProviderEntry(entry)) {
|
|
110
124
|
return { usable: false, unreadable: false };
|
|
@@ -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
|
: {}),
|