@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
|
@@ -6,6 +6,7 @@ import { secretKeyNames } from "../../secrets/key-aliases.js";
|
|
|
6
6
|
import { readAppSecrets, } from "../../secrets/storage.js";
|
|
7
7
|
import { readProviderCredentialRejections } from "../../server/credential-provider.js";
|
|
8
8
|
import { isPersonalProviderKeyUseRestricted, readOrgMemberRole, } from "../../server/personal-provider-key-policy.js";
|
|
9
|
+
import { getRequestContext } from "../../server/request-context.js";
|
|
9
10
|
import { readDefaultAgentEngineSettingDetailed, resolveDefaultAgentEngineAuthority, } from "../default-agent-engine.js";
|
|
10
11
|
import { registerBuiltinEngines } from "../engine/builtin.js";
|
|
11
12
|
import { getAgentEngineEntry, isDeploymentEngineUsableForRequest, } from "../engine/registry.js";
|
|
@@ -130,8 +131,20 @@ export default defineAction({
|
|
|
130
131
|
orgId
|
|
131
132
|
? isPersonalProviderKeyUseRestricted({ email, orgId, role })
|
|
132
133
|
: Promise.resolve(false),
|
|
133
|
-
readDefaultAgentEngineSettingDetailed({
|
|
134
|
-
|
|
134
|
+
readDefaultAgentEngineSettingDetailed({
|
|
135
|
+
userEmail: email,
|
|
136
|
+
orgId,
|
|
137
|
+
...(getRequestContext()?.orgScope === "personal"
|
|
138
|
+
? { orgScope: "personal" }
|
|
139
|
+
: {}),
|
|
140
|
+
}),
|
|
141
|
+
resolveDefaultAgentEngineAuthority({
|
|
142
|
+
userEmail: email,
|
|
143
|
+
orgId,
|
|
144
|
+
...(getRequestContext()?.orgScope === "personal"
|
|
145
|
+
? { orgScope: "personal" }
|
|
146
|
+
: {}),
|
|
147
|
+
}),
|
|
135
148
|
Promise.all(AGENT_PROVIDER_CATALOG.map(async (option) => {
|
|
136
149
|
const engine = getAgentEngineEntry(option.engine);
|
|
137
150
|
return [
|
|
@@ -7,6 +7,11 @@ export interface AgentAppModelDefaultSelection {
|
|
|
7
7
|
updatedAt?: number;
|
|
8
8
|
updatedBy?: string;
|
|
9
9
|
}
|
|
10
|
+
export interface AgentAppModelDefaultContext {
|
|
11
|
+
userEmail?: string | null;
|
|
12
|
+
orgId?: string | null;
|
|
13
|
+
orgScope?: "personal";
|
|
14
|
+
}
|
|
10
15
|
export interface AgentAppModelDefaultSettings {
|
|
11
16
|
appId: string;
|
|
12
17
|
engine: string | null;
|
|
@@ -16,21 +21,12 @@ export interface AgentAppModelDefaultSettings {
|
|
|
16
21
|
}
|
|
17
22
|
export declare function normalizeAgentAppModelDefaultAppId(appId: string | null | undefined): string | null;
|
|
18
23
|
export declare function agentAppModelDefaultSettingsKey(appId: string): string;
|
|
19
|
-
export declare function readAgentAppModelDefaultSettings(ctx:
|
|
20
|
-
|
|
21
|
-
orgId?: string | null;
|
|
22
|
-
}, appIdInput: string | null | undefined): Promise<AgentAppModelDefaultSettings>;
|
|
23
|
-
export declare function writeAgentAppModelDefaultSettings(ctx: {
|
|
24
|
-
userEmail?: string | null;
|
|
25
|
-
orgId?: string | null;
|
|
26
|
-
}, appIdInput: string | null | undefined, selection: {
|
|
24
|
+
export declare function readAgentAppModelDefaultSettings(ctx: AgentAppModelDefaultContext, appIdInput: string | null | undefined): Promise<AgentAppModelDefaultSettings>;
|
|
25
|
+
export declare function writeAgentAppModelDefaultSettings(ctx: AgentAppModelDefaultContext, appIdInput: string | null | undefined, selection: {
|
|
27
26
|
engine: string;
|
|
28
27
|
model: string;
|
|
29
28
|
updatedBy?: string | null;
|
|
30
29
|
}): Promise<AgentAppModelDefaultSettings>;
|
|
31
|
-
export declare function resetAgentAppModelDefaultSettings(ctx:
|
|
32
|
-
|
|
33
|
-
orgId?: string | null;
|
|
34
|
-
}, appIdInput: string | null | undefined): Promise<AgentAppModelDefaultSettings>;
|
|
35
|
-
export declare function canUpdateAgentAppModelDefaultSettings(userEmail: string | null | undefined, orgId: string | null | undefined): Promise<boolean>;
|
|
30
|
+
export declare function resetAgentAppModelDefaultSettings(ctx: AgentAppModelDefaultContext, appIdInput: string | null | undefined): Promise<AgentAppModelDefaultSettings>;
|
|
31
|
+
export declare function canUpdateAgentAppModelDefaultSettings(userEmail: string | null | undefined, orgId: string | null | undefined, orgScope?: "personal"): Promise<boolean>;
|
|
36
32
|
export declare function getAgentAppModelDefaultForCurrentRequest(appIdInput: string | null | undefined): Promise<AgentAppModelDefaultSelection | null>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getDbExec } from "../db/client.js";
|
|
2
|
-
import { getRequestOrgId, getRequestUserEmail, } from "../server/request-context.js";
|
|
2
|
+
import { getRequestContext, getRequestOrgId, getRequestUserEmail, } from "../server/request-context.js";
|
|
3
3
|
import { deleteOrgSetting, deleteUserSetting, getOrgSetting, getUserSetting, putOrgSetting, putUserSetting, } from "../settings/index.js";
|
|
4
4
|
export const AGENT_APP_MODEL_DEFAULT_KEY_PREFIX = "agent-app-model-default";
|
|
5
5
|
export function normalizeAgentAppModelDefaultAppId(appId) {
|
|
@@ -50,12 +50,15 @@ export async function readAgentAppModelDefaultSettings(ctx, appIdInput) {
|
|
|
50
50
|
? { appId, ...stored, scope: "org", source: "org" }
|
|
51
51
|
: emptySettings(appId, "org");
|
|
52
52
|
}
|
|
53
|
-
if (ctx.userEmail) {
|
|
53
|
+
if (ctx.userEmail && ctx.orgScope === "personal") {
|
|
54
54
|
const stored = parseSelection(await getUserSetting(ctx.userEmail, key));
|
|
55
55
|
return stored
|
|
56
56
|
? { appId, ...stored, scope: "user", source: "user" }
|
|
57
57
|
: emptySettings(appId, "user");
|
|
58
58
|
}
|
|
59
|
+
if (ctx.userEmail) {
|
|
60
|
+
throw new Error("Organization scope could not be resolved.");
|
|
61
|
+
}
|
|
59
62
|
return emptySettings(appId, "default");
|
|
60
63
|
}
|
|
61
64
|
export async function writeAgentAppModelDefaultSettings(ctx, appIdInput, selection) {
|
|
@@ -83,6 +86,9 @@ export async function writeAgentAppModelDefaultSettings(ctx, appIdInput, selecti
|
|
|
83
86
|
if (!ctx.userEmail) {
|
|
84
87
|
throw new Error("Authentication required to update model defaults.");
|
|
85
88
|
}
|
|
89
|
+
if (ctx.orgScope !== "personal") {
|
|
90
|
+
throw new Error("Organization scope could not be resolved.");
|
|
91
|
+
}
|
|
86
92
|
await putUserSetting(ctx.userEmail, key, value);
|
|
87
93
|
return readAgentAppModelDefaultSettings(ctx, appId);
|
|
88
94
|
}
|
|
@@ -98,14 +104,17 @@ export async function resetAgentAppModelDefaultSettings(ctx, appIdInput) {
|
|
|
98
104
|
if (!ctx.userEmail) {
|
|
99
105
|
throw new Error("Authentication required to reset model defaults.");
|
|
100
106
|
}
|
|
107
|
+
if (ctx.orgScope !== "personal") {
|
|
108
|
+
throw new Error("Organization scope could not be resolved.");
|
|
109
|
+
}
|
|
101
110
|
await deleteUserSetting(ctx.userEmail, key);
|
|
102
111
|
return readAgentAppModelDefaultSettings(ctx, appId);
|
|
103
112
|
}
|
|
104
|
-
export async function canUpdateAgentAppModelDefaultSettings(userEmail, orgId) {
|
|
113
|
+
export async function canUpdateAgentAppModelDefaultSettings(userEmail, orgId, orgScope) {
|
|
105
114
|
if (!userEmail)
|
|
106
115
|
return false;
|
|
107
116
|
if (!orgId)
|
|
108
|
-
return
|
|
117
|
+
return orgScope === "personal";
|
|
109
118
|
try {
|
|
110
119
|
const exec = getDbExec();
|
|
111
120
|
const { rows } = await exec.execute({
|
|
@@ -126,9 +135,18 @@ export async function getAgentAppModelDefaultForCurrentRequest(appIdInput) {
|
|
|
126
135
|
const appId = normalizeAgentAppModelDefaultAppId(appIdInput);
|
|
127
136
|
if (!appId)
|
|
128
137
|
return null;
|
|
138
|
+
const requestContext = getRequestContext();
|
|
129
139
|
const userEmail = getRequestUserEmail();
|
|
130
140
|
const orgId = getRequestOrgId();
|
|
131
|
-
|
|
141
|
+
if (!orgId && requestContext?.orgScope !== "personal")
|
|
142
|
+
return null;
|
|
143
|
+
const settings = await readAgentAppModelDefaultSettings({
|
|
144
|
+
userEmail,
|
|
145
|
+
orgId,
|
|
146
|
+
...(requestContext?.orgScope === "personal"
|
|
147
|
+
? { orgScope: "personal" }
|
|
148
|
+
: {}),
|
|
149
|
+
}, appId).catch(() => null);
|
|
132
150
|
if (!settings?.engine || !settings.model)
|
|
133
151
|
return null;
|
|
134
152
|
return {
|
|
@@ -20,6 +20,8 @@ export type DefaultAgentEngineSource = DefaultAgentEngineScope | "legacy" | "non
|
|
|
20
20
|
export interface DefaultAgentEngineContext {
|
|
21
21
|
userEmail?: string | null;
|
|
22
22
|
orgId?: string | null;
|
|
23
|
+
orgScope?: "personal";
|
|
24
|
+
defaultAgentEngineScope?: "deployment";
|
|
23
25
|
}
|
|
24
26
|
export interface DefaultAgentEngineRead {
|
|
25
27
|
/** The stored `{ engine, model }` row, or null when no default applies. */
|
|
@@ -37,7 +39,7 @@ export type DefaultAgentEngineAuthority = {
|
|
|
37
39
|
userEmail: string;
|
|
38
40
|
} | {
|
|
39
41
|
allowed: false;
|
|
40
|
-
reason: "unauthenticated" | "not-admin";
|
|
42
|
+
reason: "unauthenticated" | "not-admin" | "scope-unavailable";
|
|
41
43
|
message: string;
|
|
42
44
|
};
|
|
43
45
|
export interface DefaultAgentEngineChangeMeta {
|
|
@@ -54,8 +56,8 @@ export interface DefaultAgentEngineSelection {
|
|
|
54
56
|
}
|
|
55
57
|
/**
|
|
56
58
|
* Read the default that applies to `ctx` (the current request when omitted):
|
|
57
|
-
* the organization's row, else
|
|
58
|
-
* deployment-wide row.
|
|
59
|
+
* the organization's row, else an explicitly personal user's own row, else
|
|
60
|
+
* the legacy deployment-wide row. An unresolved scope throws before fallback.
|
|
59
61
|
*
|
|
60
62
|
* A scoped row with no engine is an explicit "cleared" marker. It answers for
|
|
61
63
|
* that scope instead of falling through to the legacy row, so clearing the
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* goes through this module so the scope rules live in one place.
|
|
8
8
|
*/
|
|
9
9
|
import { recordOrgAdminAuditEvent } from "../audit/org-admin.js";
|
|
10
|
-
import { getRequestOrgId, getRequestUserEmail, } from "../server/request-context.js";
|
|
10
|
+
import { getRequestContext, getRequestOrgId, getRequestUserEmail, } from "../server/request-context.js";
|
|
11
11
|
import { getOrgSetting, putOrgSetting } from "../settings/org-settings.js";
|
|
12
12
|
import { getSetting } from "../settings/store.js";
|
|
13
13
|
import { getUserSetting, putUserSetting } from "../settings/user-settings.js";
|
|
@@ -17,7 +17,15 @@ export const DEFAULT_AGENT_ENGINE_SETTING_KEY = "agent-engine";
|
|
|
17
17
|
/** Audit target type for default-model changes and refused attempts. */
|
|
18
18
|
export const DEFAULT_AGENT_ENGINE_AUDIT_TARGET_TYPE = "agent-default-model";
|
|
19
19
|
function requestContext() {
|
|
20
|
-
|
|
20
|
+
const context = getRequestContext();
|
|
21
|
+
return {
|
|
22
|
+
userEmail: getRequestUserEmail(),
|
|
23
|
+
orgId: getRequestOrgId(),
|
|
24
|
+
...(context?.orgScope === "personal" ? { orgScope: "personal" } : {}),
|
|
25
|
+
...(context?.defaultAgentEngineScope === "deployment"
|
|
26
|
+
? { defaultAgentEngineScope: "deployment" }
|
|
27
|
+
: {}),
|
|
28
|
+
};
|
|
21
29
|
}
|
|
22
30
|
function normalizedContext(ctx) {
|
|
23
31
|
return {
|
|
@@ -30,8 +38,8 @@ function hasEngine(value) {
|
|
|
30
38
|
}
|
|
31
39
|
/**
|
|
32
40
|
* Read the default that applies to `ctx` (the current request when omitted):
|
|
33
|
-
* the organization's row, else
|
|
34
|
-
* deployment-wide row.
|
|
41
|
+
* the organization's row, else an explicitly personal user's own row, else
|
|
42
|
+
* the legacy deployment-wide row. An unresolved scope throws before fallback.
|
|
35
43
|
*
|
|
36
44
|
* A scoped row with no engine is an explicit "cleared" marker. It answers for
|
|
37
45
|
* that scope instead of falling through to the legacy row, so clearing the
|
|
@@ -39,13 +47,18 @@ function hasEngine(value) {
|
|
|
39
47
|
*/
|
|
40
48
|
export async function readDefaultAgentEngineSettingDetailed(ctx = requestContext()) {
|
|
41
49
|
const { userEmail, orgId } = normalizedContext(ctx);
|
|
50
|
+
if (!orgId &&
|
|
51
|
+
ctx.orgScope !== "personal" &&
|
|
52
|
+
ctx.defaultAgentEngineScope !== "deployment") {
|
|
53
|
+
throw new Error("Organization scope could not be resolved.");
|
|
54
|
+
}
|
|
42
55
|
if (orgId) {
|
|
43
56
|
const scoped = await getOrgSetting(orgId, DEFAULT_AGENT_ENGINE_SETTING_KEY);
|
|
44
57
|
if (scoped) {
|
|
45
58
|
return { value: hasEngine(scoped) ? scoped : null, source: "org" };
|
|
46
59
|
}
|
|
47
60
|
}
|
|
48
|
-
else if (userEmail) {
|
|
61
|
+
else if (userEmail && ctx.orgScope === "personal") {
|
|
49
62
|
const scoped = await getUserSetting(userEmail, DEFAULT_AGENT_ENGINE_SETTING_KEY);
|
|
50
63
|
if (scoped) {
|
|
51
64
|
return { value: hasEngine(scoped) ? scoped : null, source: "user" };
|
|
@@ -76,9 +89,16 @@ export async function resolveDefaultAgentEngineAuthority(ctx = requestContext())
|
|
|
76
89
|
message: "Sign in to change the default model.",
|
|
77
90
|
};
|
|
78
91
|
}
|
|
92
|
+
if (!orgId && ctx.orgScope !== "personal") {
|
|
93
|
+
return {
|
|
94
|
+
allowed: false,
|
|
95
|
+
reason: "scope-unavailable",
|
|
96
|
+
message: "Organization scope could not be resolved.",
|
|
97
|
+
};
|
|
98
|
+
}
|
|
79
99
|
if (!orgId)
|
|
80
100
|
return { allowed: true, scope: "user", userEmail };
|
|
81
|
-
if (await canUpdateAgentAppModelDefaultSettings(userEmail, orgId)) {
|
|
101
|
+
if (await canUpdateAgentAppModelDefaultSettings(userEmail, orgId, ctx.orgScope)) {
|
|
82
102
|
return { allowed: true, scope: "org", orgId, userEmail };
|
|
83
103
|
}
|
|
84
104
|
return {
|
|
@@ -99,7 +119,12 @@ async function writeScopedRow(authority, value) {
|
|
|
99
119
|
* stores only that scope and records the change.
|
|
100
120
|
*/
|
|
101
121
|
export async function writeDefaultAgentEngineSelection(authority, selection, meta) {
|
|
102
|
-
const previous = await readDefaultAgentEngineSettingDetailed(
|
|
122
|
+
const previous = await readDefaultAgentEngineSettingDetailed({
|
|
123
|
+
userEmail: authority.userEmail,
|
|
124
|
+
...(authority.scope === "org"
|
|
125
|
+
? { orgId: authority.orgId }
|
|
126
|
+
: { orgScope: "personal" }),
|
|
127
|
+
});
|
|
103
128
|
await writeScopedRow(authority, {
|
|
104
129
|
engine: selection.engine,
|
|
105
130
|
model: selection.model,
|
|
@@ -132,7 +157,12 @@ export async function writeDefaultAgentEngineSelection(authority, selection, met
|
|
|
132
157
|
* than deleting the row; see {@link readDefaultAgentEngineSettingDetailed}.
|
|
133
158
|
*/
|
|
134
159
|
export async function clearDefaultAgentEngineSelection(authority, meta) {
|
|
135
|
-
const previous = await readDefaultAgentEngineSettingDetailed(
|
|
160
|
+
const previous = await readDefaultAgentEngineSettingDetailed({
|
|
161
|
+
userEmail: authority.userEmail,
|
|
162
|
+
...(authority.scope === "org"
|
|
163
|
+
? { orgId: authority.orgId }
|
|
164
|
+
: { orgScope: "personal" }),
|
|
165
|
+
});
|
|
136
166
|
await writeScopedRow(authority, {
|
|
137
167
|
cleared: true,
|
|
138
168
|
updatedAt: Date.now(),
|
|
@@ -54,6 +54,8 @@ export declare const AGENT_CHAT_DURABLE_BACKGROUND_ENV = "AGENT_CHAT_DURABLE_BAC
|
|
|
54
54
|
export declare const AGENT_CHAT_BACKGROUND_RUN_FIELD = "__backgroundRun";
|
|
55
55
|
/** Analytics-only source context captured before the run leaves its browser request. */
|
|
56
56
|
export declare const AGENT_CHAT_BROWSER_SESSION_ID_FIELD = "__agentNativeBrowserSessionId";
|
|
57
|
+
/** Captured turn origin; durable workers do not restore loopback trust from this payload. */
|
|
58
|
+
export declare const AGENT_CHAT_REQUEST_CONTEXT_FIELD = "__agentChatRequestContext";
|
|
57
59
|
export declare function isHostedRuntimeForDurableBackground(): boolean;
|
|
58
60
|
export declare function isNetlifyHostedRuntimeForDurableBackground(): boolean;
|
|
59
61
|
export declare function isInBackgroundFunctionRuntime(): boolean;
|
|
@@ -134,6 +134,8 @@ export const AGENT_CHAT_DURABLE_BACKGROUND_ENV = "AGENT_CHAT_DURABLE_BACKGROUND"
|
|
|
134
134
|
export const AGENT_CHAT_BACKGROUND_RUN_FIELD = "__backgroundRun";
|
|
135
135
|
/** Analytics-only source context captured before the run leaves its browser request. */
|
|
136
136
|
export const AGENT_CHAT_BROWSER_SESSION_ID_FIELD = "__agentNativeBrowserSessionId";
|
|
137
|
+
/** Captured turn origin; durable workers do not restore loopback trust from this payload. */
|
|
138
|
+
export const AGENT_CHAT_REQUEST_CONTEXT_FIELD = "__agentChatRequestContext";
|
|
137
139
|
export function isHostedRuntimeForDurableBackground() {
|
|
138
140
|
if (process.env.NETLIFY_LOCAL === "true")
|
|
139
141
|
return false;
|
|
@@ -617,6 +617,8 @@ function identityUserEmail(identity) {
|
|
|
617
617
|
return getRequestUserEmail()?.trim() || undefined;
|
|
618
618
|
}
|
|
619
619
|
async function chatGPTSubscriptionUsableForRequest(identity) {
|
|
620
|
+
if (identity?.orgId || getRequestOrgId())
|
|
621
|
+
return false;
|
|
620
622
|
const email = identityUserEmail(identity);
|
|
621
623
|
if (!email)
|
|
622
624
|
return false;
|
|
@@ -908,6 +910,10 @@ export async function resolveEngine(config) {
|
|
|
908
910
|
if (engineOption &&
|
|
909
911
|
typeof engineOption === "object" &&
|
|
910
912
|
"stream" in engineOption) {
|
|
913
|
+
if (engineOption.name === CHATGPT_SUBSCRIPTION_ENGINE_NAME &&
|
|
914
|
+
!(await chatGPTSubscriptionUsableForRequest(credentialIdentity))) {
|
|
915
|
+
throw new Error("Connect a ChatGPT account with direct model access before using this engine.");
|
|
916
|
+
}
|
|
911
917
|
return engineOption;
|
|
912
918
|
}
|
|
913
919
|
if (engineOption &&
|
|
@@ -58,7 +58,7 @@ import { CONTINUE_UNAVAILABLE_CODE, } from "./auto-continue.js";
|
|
|
58
58
|
import { clipHead } from "./clip-text.js";
|
|
59
59
|
import { connectionRequiredMessage, priorConnectionContextNote, resolvePriorConnectionNote, } from "./connection-required-note.js";
|
|
60
60
|
import { buildSystemManifestSections, readContextXraySystemSections, } from "./context-xray/manifest.js";
|
|
61
|
-
import { AGENT_CHAT_BROWSER_SESSION_ID_FIELD, AGENT_CHAT_BACKGROUND_RUN_FIELD, AGENT_CHAT_PROCESS_RUN_PATH, AGENT_CHAT_RECOVERY_OF_RUN_FIELD, backgroundRuntimeDiagnosticDetail, dispatchPathTargetsNetlifyBackgroundFunction, isAgentChatDurableBackgroundEnabled, isAgentChatForegroundSelfChainEnabled, isInBackgroundFunctionRuntime, resolveAgentChatProcessRunDispatchPath, shouldUseBackgroundFunctionTimeoutForWorker, } from "./durable-background.js";
|
|
61
|
+
import { AGENT_CHAT_BROWSER_SESSION_ID_FIELD, AGENT_CHAT_REQUEST_CONTEXT_FIELD, AGENT_CHAT_BACKGROUND_RUN_FIELD, AGENT_CHAT_PROCESS_RUN_PATH, AGENT_CHAT_RECOVERY_OF_RUN_FIELD, backgroundRuntimeDiagnosticDetail, dispatchPathTargetsNetlifyBackgroundFunction, isAgentChatDurableBackgroundEnabled, isAgentChatForegroundSelfChainEnabled, isInBackgroundFunctionRuntime, resolveAgentChatProcessRunDispatchPath, shouldUseBackgroundFunctionTimeoutForWorker, } from "./durable-background.js";
|
|
62
62
|
import { applyContextXrayTransformForIteration } from "./engine/context-directives-transform.js";
|
|
63
63
|
import { attemptContinuationDispatch } from "./engine/continuation-dispatch-retry.js";
|
|
64
64
|
import { formatLlmCredentialErrorMessage, LLM_MISSING_CREDENTIALS_ERROR_CODE, LLM_MISSING_CREDENTIALS_MESSAGE, userFacingLlmCredentialError, } from "./engine/credential-errors.js";
|
|
@@ -488,6 +488,12 @@ export async function getOwnerActiveApiKey(ownerEmail) {
|
|
|
488
488
|
const engineSetting = await readDefaultAgentEngineSetting({
|
|
489
489
|
userEmail: ownerEmail ?? getRequestUserEmail(),
|
|
490
490
|
orgId: getRequestOrgId(),
|
|
491
|
+
...(getRequestContext()?.orgScope === "personal"
|
|
492
|
+
? { orgScope: "personal" }
|
|
493
|
+
: {}),
|
|
494
|
+
...(getRequestContext()?.defaultAgentEngineScope === "deployment"
|
|
495
|
+
? { defaultAgentEngineScope: "deployment" }
|
|
496
|
+
: {}),
|
|
491
497
|
});
|
|
492
498
|
const activeEngine = engineSetting?.engine ?? "anthropic";
|
|
493
499
|
return (await getOwnerApiKeyForEngine(activeEngine, ownerEmail)).apiKey;
|
|
@@ -523,8 +529,8 @@ export async function resolveOwnerEngineApiKey(input) {
|
|
|
523
529
|
}
|
|
524
530
|
else {
|
|
525
531
|
try {
|
|
526
|
-
const {
|
|
527
|
-
const engineSetting = await
|
|
532
|
+
const { readDefaultAgentEngineSetting } = await import("./default-agent-engine.js");
|
|
533
|
+
const engineSetting = await readDefaultAgentEngineSetting();
|
|
528
534
|
activeEngineSetting = {
|
|
529
535
|
status: "available",
|
|
530
536
|
engine: engineSetting?.engine ?? "anthropic",
|
|
@@ -8101,6 +8107,13 @@ export function createProductionAgentHandler(options) {
|
|
|
8101
8107
|
// A client can submit this private field, so overwrite it at the boundary.
|
|
8102
8108
|
mutableBody[AGENT_CHAT_BROWSER_SESSION_ID_FIELD] =
|
|
8103
8109
|
getRequestContext()?.browserSessionId ?? null;
|
|
8110
|
+
const requestContext = getRequestContext();
|
|
8111
|
+
mutableBody[AGENT_CHAT_REQUEST_CONTEXT_FIELD] = {
|
|
8112
|
+
requestOrigin: requestContext?.requestOrigin ?? null,
|
|
8113
|
+
isLoopbackRequest: !dispatchToBackground && requestContext?.isLoopbackRequest === true,
|
|
8114
|
+
isDirectLoopbackRequest: !dispatchToBackground &&
|
|
8115
|
+
requestContext?.isDirectLoopbackRequest === true,
|
|
8116
|
+
};
|
|
8104
8117
|
delete mutableBody[ANALYTICS_CLIENT_PLATFORM_BODY_FIELD];
|
|
8105
8118
|
}
|
|
8106
8119
|
if (dispatchToBackground) {
|
|
@@ -10286,12 +10299,32 @@ export function createProductionAgentHandler(options) {
|
|
|
10286
10299
|
return;
|
|
10287
10300
|
}
|
|
10288
10301
|
if (exactApprovedToolCall && approvedToolCallsForExecution) {
|
|
10302
|
+
let pausedResult;
|
|
10303
|
+
if (!isBackgroundWorker) {
|
|
10304
|
+
const pausedResults = agentLoopOpts.messages.flatMap((message) => message.content.filter((part) => part.type === "tool-result" &&
|
|
10305
|
+
part.toolCallId === exactApprovedToolCall.callId &&
|
|
10306
|
+
part.approvalPause === true));
|
|
10307
|
+
if (pausedResults.length !== 1) {
|
|
10308
|
+
send({
|
|
10309
|
+
type: "error",
|
|
10310
|
+
error: "The agent run failed.",
|
|
10311
|
+
errorCode: "approved_tool_call_result_missing",
|
|
10312
|
+
recoverable: false,
|
|
10313
|
+
});
|
|
10314
|
+
return;
|
|
10315
|
+
}
|
|
10316
|
+
const [candidate] = pausedResults;
|
|
10317
|
+
if (candidate?.type !== "tool-result") {
|
|
10318
|
+
throw new Error("Approved tool result changed unexpectedly.");
|
|
10319
|
+
}
|
|
10320
|
+
pausedResult = candidate;
|
|
10321
|
+
}
|
|
10289
10322
|
send({
|
|
10290
10323
|
type: "activity",
|
|
10291
10324
|
label: `Running approved ${exactApprovedToolCall.name} action`,
|
|
10292
10325
|
tool: exactApprovedToolCall.name,
|
|
10293
10326
|
});
|
|
10294
|
-
await executeAgentToolCall({
|
|
10327
|
+
const approvedExecution = await executeAgentToolCall({
|
|
10295
10328
|
actions: requestActions,
|
|
10296
10329
|
name: exactApprovedToolCall.name,
|
|
10297
10330
|
input: exactApprovedToolCall.input,
|
|
@@ -10313,7 +10346,27 @@ export function createProductionAgentHandler(options) {
|
|
|
10313
10346
|
isToolAlwaysAllowed: approvalHooks.isToolAlwaysAllowed,
|
|
10314
10347
|
send,
|
|
10315
10348
|
});
|
|
10316
|
-
|
|
10349
|
+
if (approvedExecution.status === "approval_required" ||
|
|
10350
|
+
(approvedExecution.status === "failed" &&
|
|
10351
|
+
approvedExecution.terminalError)) {
|
|
10352
|
+
return;
|
|
10353
|
+
}
|
|
10354
|
+
if (isBackgroundWorker)
|
|
10355
|
+
return;
|
|
10356
|
+
if (!pausedResult) {
|
|
10357
|
+
throw new Error("Approved tool result was not validated.");
|
|
10358
|
+
}
|
|
10359
|
+
pausedResult.content = approvedExecution.output;
|
|
10360
|
+
if (approvedExecution.status === "failed") {
|
|
10361
|
+
pausedResult.isError = true;
|
|
10362
|
+
}
|
|
10363
|
+
else {
|
|
10364
|
+
delete pausedResult.isError;
|
|
10365
|
+
}
|
|
10366
|
+
delete pausedResult.approvalPause;
|
|
10367
|
+
delete pausedResult.approvalAskId;
|
|
10368
|
+
delete agentLoopOpts.approvedToolCalls;
|
|
10369
|
+
delete agentLoopOpts.approvedToolCallAskId;
|
|
10317
10370
|
}
|
|
10318
10371
|
try {
|
|
10319
10372
|
const { getObservabilityConfig, instrumentAgentLoop } = await import("../observability/traces.js");
|
|
@@ -72,6 +72,8 @@ export declare function deleteAutomation(actorInput: AutomationActor, scope: Aut
|
|
|
72
72
|
export interface AutomationExecutionIdentity {
|
|
73
73
|
userEmail: string;
|
|
74
74
|
orgId?: string;
|
|
75
|
+
orgScope?: "personal";
|
|
76
|
+
defaultAgentEngineScope?: "deployment";
|
|
75
77
|
eventOwner: string;
|
|
76
78
|
}
|
|
77
79
|
/**
|
|
@@ -473,7 +473,10 @@ export async function resolveAutomationExecutionIdentity(resourceOwner, meta) {
|
|
|
473
473
|
code: "config_invalid",
|
|
474
474
|
};
|
|
475
475
|
}
|
|
476
|
-
return {
|
|
476
|
+
return {
|
|
477
|
+
ok: true,
|
|
478
|
+
identity: { userEmail, orgScope: "personal", eventOwner: userEmail },
|
|
479
|
+
};
|
|
477
480
|
}
|
|
478
481
|
const userEmail = meta.createdBy?.trim().toLowerCase();
|
|
479
482
|
if (!userEmail) {
|
package/dist/client/analytics.js
CHANGED
|
@@ -19,6 +19,7 @@ import { scheduleAfterPaint } from "./use-after-paint.js";
|
|
|
19
19
|
export { clearAnalyticsSessionId, setAnalyticsSessionId, } from "./analytics-session.js";
|
|
20
20
|
import { fetchAgentEngineStatus, fetchAuthSessionStatus, } from "./client-status-requests.js";
|
|
21
21
|
import { firstPartyHosts, installErrorCapture, } from "./error-capture.js";
|
|
22
|
+
import { buildRecorderReport, recorderSentryEnabled, } from "./recorder.js";
|
|
22
23
|
import { currentRouteTemplate } from "./route-template.js";
|
|
23
24
|
import { scrubUrl } from "./url-scrub.js";
|
|
24
25
|
import { installWebVitals, } from "./web-vitals.js";
|
|
@@ -815,6 +816,51 @@ function shouldDropBrowserSentryNoise(event) {
|
|
|
815
816
|
firstPartyHosts: firstPartyHosts(),
|
|
816
817
|
}).drop;
|
|
817
818
|
}
|
|
819
|
+
// Sentry keeps 100 breadcrumbs by default; the attachment carries the whole report.
|
|
820
|
+
const SENTRY_RECORDER_BREADCRUMBS = 100;
|
|
821
|
+
function recorderBreadcrumb(recorded) {
|
|
822
|
+
const { t: _t, at, type, name, data: _data, ...outcome } = recorded;
|
|
823
|
+
return {
|
|
824
|
+
category: `recorder.${type}`,
|
|
825
|
+
message: [
|
|
826
|
+
name,
|
|
827
|
+
outcome.ok === undefined ? undefined : outcome.ok ? "ok" : "failed",
|
|
828
|
+
outcome.status,
|
|
829
|
+
outcome.errorCode,
|
|
830
|
+
outcome.ms === undefined ? undefined : `${outcome.ms}ms`,
|
|
831
|
+
]
|
|
832
|
+
.filter((part) => part !== undefined)
|
|
833
|
+
.join(" "),
|
|
834
|
+
level: type === "error" || (type === "toast" && name === "error")
|
|
835
|
+
? "error"
|
|
836
|
+
: type === "toast" || outcome.ok === false
|
|
837
|
+
? "warning"
|
|
838
|
+
: "info",
|
|
839
|
+
timestamp: at / 1000,
|
|
840
|
+
data: outcome,
|
|
841
|
+
};
|
|
842
|
+
}
|
|
843
|
+
function addRecorderContext(event, hint) {
|
|
844
|
+
if (!recorderSentryEnabled())
|
|
845
|
+
return;
|
|
846
|
+
const report = buildRecorderReport("error");
|
|
847
|
+
if (report.events.length > 0) {
|
|
848
|
+
event.breadcrumbs = [
|
|
849
|
+
...(event.breadcrumbs ?? []),
|
|
850
|
+
...report.events
|
|
851
|
+
.slice(-SENTRY_RECORDER_BREADCRUMBS)
|
|
852
|
+
.map(recorderBreadcrumb),
|
|
853
|
+
].sort((a, b) => (a.timestamp ?? 0) - (b.timestamp ?? 0));
|
|
854
|
+
}
|
|
855
|
+
hint.attachments = [
|
|
856
|
+
...(hint.attachments ?? []),
|
|
857
|
+
{
|
|
858
|
+
filename: "recorder.json",
|
|
859
|
+
data: JSON.stringify(report),
|
|
860
|
+
contentType: "application/json",
|
|
861
|
+
},
|
|
862
|
+
];
|
|
863
|
+
}
|
|
818
864
|
function firstNonEmpty(...values) {
|
|
819
865
|
for (const value of values) {
|
|
820
866
|
const trimmed = value?.trim();
|
|
@@ -895,7 +941,7 @@ function ensureSentry(loadWithoutDsn = false) {
|
|
|
895
941
|
dsn,
|
|
896
942
|
environment: resolveClientDeploymentEnvironment(),
|
|
897
943
|
release: resolveClientRelease(),
|
|
898
|
-
beforeSend(event) {
|
|
944
|
+
beforeSend(event, hint = {}) {
|
|
899
945
|
if (isSyntheticBrowserTraffic())
|
|
900
946
|
return null;
|
|
901
947
|
event.tags = {
|
|
@@ -924,6 +970,7 @@ function ensureSentry(loadWithoutDsn = false) {
|
|
|
924
970
|
}
|
|
925
971
|
}
|
|
926
972
|
}
|
|
973
|
+
addRecorderContext(event, hint);
|
|
927
974
|
return event;
|
|
928
975
|
},
|
|
929
976
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { agentNativePath } from "./api-path.js";
|
|
2
2
|
import { assertAgentNativeApiEnabled } from "./api-surface.js";
|
|
3
3
|
import { getBrowserTabId } from "./browser-tab-id.js";
|
|
4
|
+
import { recordStateWrite } from "./recorder.js";
|
|
4
5
|
const APP_STATE_KEY_PATTERN = /^[a-zA-Z0-9_:-]+$/;
|
|
5
6
|
const pendingMutations = new Map();
|
|
6
7
|
const mutationRevisions = new Map();
|
|
@@ -200,7 +201,7 @@ export async function readClientAppState(key, options = {}) {
|
|
|
200
201
|
}
|
|
201
202
|
export async function writeClientAppState(key, value, options = {}) {
|
|
202
203
|
assertAgentNativeApiEnabled(`write application state "${key}"`);
|
|
203
|
-
|
|
204
|
+
const write = runClientAppStateMutation(key, async () => {
|
|
204
205
|
const response = await fetch(appStateUrl(key), {
|
|
205
206
|
method: "PUT",
|
|
206
207
|
headers: buildHeaders(options.requestSource),
|
|
@@ -210,6 +211,8 @@ export async function writeClientAppState(key, value, options = {}) {
|
|
|
210
211
|
});
|
|
211
212
|
return parseAppStateResponse(response, `Write application state "${key}"`);
|
|
212
213
|
});
|
|
214
|
+
recordStateWrite(key, value, write, options.signal);
|
|
215
|
+
return write;
|
|
213
216
|
}
|
|
214
217
|
export async function compareAndSetClientAppState(key, expected, next, options = {}) {
|
|
215
218
|
assertAgentNativeApiEnabled(`compare application state \"${key}\"`);
|
|
@@ -230,7 +233,7 @@ export async function compareAndSetClientAppState(key, expected, next, options =
|
|
|
230
233
|
}
|
|
231
234
|
export async function deleteClientAppState(key, options = {}) {
|
|
232
235
|
assertAgentNativeApiEnabled(`delete application state "${key}"`);
|
|
233
|
-
|
|
236
|
+
const deletion = runClientAppStateMutation(key, async () => {
|
|
234
237
|
const response = await fetch(appStateUrl(key), {
|
|
235
238
|
method: "DELETE",
|
|
236
239
|
headers: {
|
|
@@ -245,6 +248,8 @@ export async function deleteClientAppState(key, options = {}) {
|
|
|
245
248
|
});
|
|
246
249
|
return parseAppStateResponse(response, `Delete application state "${key}"`);
|
|
247
250
|
});
|
|
251
|
+
recordStateWrite(key, undefined, deletion, options.signal);
|
|
252
|
+
await deletion;
|
|
248
253
|
}
|
|
249
254
|
export async function setClientAppState(key, value, options = {}) {
|
|
250
255
|
if (value === null || value === undefined) {
|
|
@@ -3232,9 +3232,18 @@ export function createAgentKitProtocolAdapter(runtime, options = {}) {
|
|
|
3232
3232
|
activeMessageCompleted: run.activeMessageCompleted,
|
|
3233
3233
|
pendingWidgets: new Map(run.pendingWidgets),
|
|
3234
3234
|
actions: new Map(run.actions),
|
|
3235
|
-
activeTools: new Map(run.activeTools)
|
|
3236
|
-
|
|
3237
|
-
|
|
3235
|
+
activeTools: new Map([...run.activeTools].map(([id, tool]) => [
|
|
3236
|
+
id,
|
|
3237
|
+
{ ...tool, runId: nextRunId },
|
|
3238
|
+
])),
|
|
3239
|
+
pendingToolMessageAssociations: new Map([...run.pendingToolMessageAssociations].map(([id, tool]) => [
|
|
3240
|
+
id,
|
|
3241
|
+
{ ...tool, runId: nextRunId },
|
|
3242
|
+
])),
|
|
3243
|
+
activeActivities: new Map([...run.activeActivities].map(([id, activity]) => [
|
|
3244
|
+
id,
|
|
3245
|
+
{ ...activity, runId: nextRunId },
|
|
3246
|
+
])),
|
|
3238
3247
|
terminalAppendDepth: 0,
|
|
3239
3248
|
pumpPromise: null,
|
|
3240
3249
|
continuationPromise: null,
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { ensureEmbedAuthFetchInterceptor } from "./embed-auth.js";
|
|
2
|
+
import { startRecorder } from "./recorder.js";
|
|
2
3
|
import { installRouteChunkRecovery } from "./route-chunk-recovery.js";
|
|
3
4
|
export function initializeAgentNativeClient() {
|
|
4
5
|
ensureEmbedAuthFetchInterceptor();
|
|
5
6
|
installRouteChunkRecovery();
|
|
7
|
+
startRecorder();
|
|
6
8
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { RecorderReport } from "./recorder.js";
|
|
2
|
+
export { countRecorderEventsByType, formatProblemReportSummary, formatRecorderEvent, isFailedRecorderEvent, } from "../problem-report/format.js";
|
|
3
|
+
export type ProblemReportUpload = {
|
|
4
|
+
status: "ok";
|
|
5
|
+
ref: string;
|
|
6
|
+
} | {
|
|
7
|
+
status: "failed";
|
|
8
|
+
errorCode?: string;
|
|
9
|
+
message?: string;
|
|
10
|
+
};
|
|
11
|
+
export declare function uploadProblemReport(report: RecorderReport): Promise<ProblemReportUpload>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { actionErrorMessage, callAction } from "./use-action.js";
|
|
2
|
+
export { countRecorderEventsByType, formatProblemReportSummary, formatRecorderEvent, isFailedRecorderEvent, } from "../problem-report/format.js";
|
|
3
|
+
export async function uploadProblemReport(report) {
|
|
4
|
+
try {
|
|
5
|
+
const { ref } = await callAction("save-problem-report", {
|
|
6
|
+
report,
|
|
7
|
+
});
|
|
8
|
+
return { status: "ok", ref };
|
|
9
|
+
}
|
|
10
|
+
catch (error) {
|
|
11
|
+
const errorCode = error.errorCode;
|
|
12
|
+
const message = actionErrorMessage(error);
|
|
13
|
+
return {
|
|
14
|
+
status: "failed",
|
|
15
|
+
...(typeof errorCode === "string" ? { errorCode } : {}),
|
|
16
|
+
...(message ? { message } : {}),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
}
|