@agent-native/core 0.203.1-nightly-20261007154213 → 0.203.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/default-agent-engine.d.ts +1 -1
- package/dist/agent/default-agent-engine.js +1 -1
- package/dist/agent/engine-status.d.ts +49 -0
- package/dist/agent/engine-status.js +112 -0
- package/dist/client/client-status-requests.js +8 -0
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/routes.d.ts +1 -1
- package/dist/jobs/actions/manage-recurring-job.d.ts +1 -1
- package/dist/jobs/frontmatter-loss.d.ts +17 -0
- package/dist/jobs/frontmatter-loss.js +62 -0
- package/dist/jobs/frontmatter.d.ts +7 -0
- package/dist/jobs/frontmatter.js +39 -0
- package/dist/jobs/tools.js +16 -3
- package/dist/localization/default-model-messages.d.ts +11 -0
- package/dist/localization/default-model-messages.js +88 -0
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +2 -2
- package/dist/org/index.d.ts +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +4 -4
- package/dist/provider-api/actions/provider-api.d.ts +8 -8
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/resources/store.js +40 -2
- package/dist/scripts/agent-engines/list-agent-engines.js +8 -5
- package/dist/scripts/agent-engines/manage-agent-engine.js +22 -4
- package/dist/scripts/agent-engines/set-agent-engine.d.ts +25 -0
- package/dist/scripts/agent-engines/set-agent-engine.js +94 -8
- package/dist/secrets/routes.d.ts +6 -6
- package/dist/server/agent-engine-default-model-route.d.ts +1 -1
- package/dist/server/agent-engine-ollama-models-route.d.ts +1 -1
- package/dist/server/core-routes-plugin.d.ts +1 -43
- package/dist/server/core-routes-plugin.js +15 -92
- package/dist/server/email-auth-links.js +98 -2
- package/dist/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +26 -39
- package/dist/templates/workspace-core/.agents/skills/recurring-jobs/SKILL.md +16 -1
- package/dist/triggers/actions/manage-automation.d.ts +7 -7
- package/package.json +4 -4
- package/src/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +26 -39
- package/src/templates/workspace-core/.agents/skills/recurring-jobs/SKILL.md +16 -1
|
@@ -311,8 +311,8 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
|
|
|
311
311
|
notes?: string | undefined;
|
|
312
312
|
scope?: "org" | "user" | undefined;
|
|
313
313
|
}, {
|
|
314
|
+
provider?: undefined;
|
|
314
315
|
id?: undefined;
|
|
315
|
-
deleted?: undefined;
|
|
316
316
|
found?: undefined;
|
|
317
317
|
providers: {
|
|
318
318
|
id: string;
|
|
@@ -324,46 +324,46 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
|
|
|
324
324
|
updatedAt: number;
|
|
325
325
|
}[];
|
|
326
326
|
count: number;
|
|
327
|
-
|
|
327
|
+
deleted?: undefined;
|
|
328
328
|
registered?: undefined;
|
|
329
329
|
label?: undefined;
|
|
330
330
|
message?: undefined;
|
|
331
331
|
} | {
|
|
332
332
|
id?: undefined;
|
|
333
|
-
deleted?: undefined;
|
|
334
333
|
count?: undefined;
|
|
335
334
|
providers?: undefined;
|
|
336
335
|
found: boolean;
|
|
337
336
|
provider: import("../custom-registry.js").CustomProviderConfig;
|
|
337
|
+
deleted?: undefined;
|
|
338
338
|
registered?: undefined;
|
|
339
339
|
label?: undefined;
|
|
340
340
|
message?: undefined;
|
|
341
341
|
} | {
|
|
342
|
-
|
|
342
|
+
provider?: undefined;
|
|
343
343
|
count?: undefined;
|
|
344
344
|
providers?: undefined;
|
|
345
|
-
provider?: undefined;
|
|
346
345
|
found: boolean;
|
|
347
346
|
id: string;
|
|
347
|
+
deleted?: undefined;
|
|
348
348
|
registered?: undefined;
|
|
349
349
|
label?: undefined;
|
|
350
350
|
message?: undefined;
|
|
351
351
|
} | {
|
|
352
|
+
provider?: undefined;
|
|
352
353
|
count?: undefined;
|
|
353
354
|
found?: undefined;
|
|
354
355
|
providers?: undefined;
|
|
355
|
-
provider?: undefined;
|
|
356
356
|
deleted: boolean;
|
|
357
357
|
id: string;
|
|
358
358
|
registered?: undefined;
|
|
359
359
|
label?: undefined;
|
|
360
360
|
message?: undefined;
|
|
361
361
|
} | {
|
|
362
|
-
|
|
362
|
+
provider?: undefined;
|
|
363
363
|
count?: undefined;
|
|
364
364
|
found?: undefined;
|
|
365
365
|
providers?: undefined;
|
|
366
|
-
|
|
366
|
+
deleted?: undefined;
|
|
367
367
|
registered: boolean;
|
|
368
368
|
id: string;
|
|
369
369
|
label: string;
|
|
@@ -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
|
-
ok?: undefined;
|
|
43
42
|
error: string;
|
|
43
|
+
ok?: undefined;
|
|
44
44
|
} | {
|
|
45
45
|
error?: undefined;
|
|
46
46
|
ok: boolean;
|
package/dist/resources/store.js
CHANGED
|
@@ -2,6 +2,7 @@ import crypto from "crypto";
|
|
|
2
2
|
import { getDbExec } from "../db/client.js";
|
|
3
3
|
import { ensureColumnExists, ensureIndexExists, ensureTableExists, } from "../db/ddl-guard.js";
|
|
4
4
|
import { widenIntColumnsToBigInt } from "../db/widen-columns.js";
|
|
5
|
+
import { noteJobFrontmatterWrite } from "../jobs/frontmatter-loss.js";
|
|
5
6
|
import { canUseLocalWorkspaceResourcePath, deleteLocalWorkspaceResource, deleteLocalWorkspaceResourceIfCurrent, isLocalWorkspaceResourceId, isLocalWorkspaceResourcesEnabled, listLocalWorkspaceResources, localWorkspaceResourcePathFromId, readLocalWorkspaceResource, writeLocalWorkspaceResourceIfAbsentAtPath, writeLocalWorkspaceResourceIfCurrent, writeLocalWorkspaceResource, } from "../local-artifacts/index.js";
|
|
6
7
|
import { getRequestOrgId, getRequestUserEmail, } from "../server/request-context.js";
|
|
7
8
|
import { getSetting, putSetting, } from "../settings/store.js";
|
|
@@ -1164,8 +1165,9 @@ export async function resourcePut(owner, path, content, mimeType, options) {
|
|
|
1164
1165
|
const now = Date.now();
|
|
1165
1166
|
const size = Buffer.byteLength(content, "utf8");
|
|
1166
1167
|
const mime = mimeType || "text/markdown";
|
|
1168
|
+
const isJobFile = path.startsWith("jobs/");
|
|
1167
1169
|
const { rows: existing } = await client.execute({
|
|
1168
|
-
sql: `SELECT id, created_at, created_by, visibility, thread_id, run_id, expires_at, metadata FROM resources WHERE owner = ? AND path = ?`,
|
|
1170
|
+
sql: `SELECT id, created_at, created_by, visibility, thread_id, run_id, expires_at, metadata${isJobFile ? ", content" : ""} FROM resources WHERE owner = ? AND path = ?`,
|
|
1169
1171
|
args: [owner, path],
|
|
1170
1172
|
});
|
|
1171
1173
|
const existingRow = existing[0];
|
|
@@ -1215,6 +1217,16 @@ export async function resourcePut(owner, path, content, mimeType, options) {
|
|
|
1215
1217
|
metadata,
|
|
1216
1218
|
],
|
|
1217
1219
|
});
|
|
1220
|
+
if (typeof existingRow?.content === "string") {
|
|
1221
|
+
await noteJobFrontmatterWrite({
|
|
1222
|
+
owner,
|
|
1223
|
+
orgId: organizationIdFromResourceOwner(owner),
|
|
1224
|
+
path,
|
|
1225
|
+
before: existingRow.content,
|
|
1226
|
+
after: content,
|
|
1227
|
+
writer: "resourcePut",
|
|
1228
|
+
});
|
|
1229
|
+
}
|
|
1218
1230
|
emitResourceChange(id, path, owner, options?.requestSource);
|
|
1219
1231
|
return {
|
|
1220
1232
|
id,
|
|
@@ -1344,6 +1356,14 @@ export async function resourcePutIfCurrent(input) {
|
|
|
1344
1356
|
if (rows.length === 0)
|
|
1345
1357
|
return null;
|
|
1346
1358
|
const resource = rowToResource(rows[0]);
|
|
1359
|
+
await noteJobFrontmatterWrite({
|
|
1360
|
+
owner: resource.owner,
|
|
1361
|
+
orgId: organizationIdFromResourceOwner(resource.owner),
|
|
1362
|
+
path: resource.path,
|
|
1363
|
+
before: input.expectedContent,
|
|
1364
|
+
after: resource.content,
|
|
1365
|
+
writer: "resourcePutIfCurrent",
|
|
1366
|
+
});
|
|
1347
1367
|
emitResourceChange(resource.id, resource.path, resource.owner);
|
|
1348
1368
|
return resource;
|
|
1349
1369
|
}
|
|
@@ -1453,6 +1473,14 @@ async function resourcePutIfSnapshotInternal(input, emitChange, clientOverride)
|
|
|
1453
1473
|
return null;
|
|
1454
1474
|
const resource = rowToResource(rows[0]);
|
|
1455
1475
|
if (emitChange) {
|
|
1476
|
+
await noteJobFrontmatterWrite({
|
|
1477
|
+
owner: resource.owner,
|
|
1478
|
+
orgId: organizationIdFromResourceOwner(resource.owner),
|
|
1479
|
+
path: resource.path,
|
|
1480
|
+
before: previous.content,
|
|
1481
|
+
after: resource.content,
|
|
1482
|
+
writer: "resourcePutIfSnapshot",
|
|
1483
|
+
});
|
|
1456
1484
|
emitResourceChange(resource.id, resource.path, resource.owner, input.options?.requestSource);
|
|
1457
1485
|
}
|
|
1458
1486
|
return { before: previous, resource };
|
|
@@ -1490,7 +1518,17 @@ export async function resourcePutSnapshotBatchIfCurrent(writes, options) {
|
|
|
1490
1518
|
return null;
|
|
1491
1519
|
throw error;
|
|
1492
1520
|
}
|
|
1493
|
-
for (const [index, { resource }] of result.entries()) {
|
|
1521
|
+
for (const [index, { before, resource }] of result.entries()) {
|
|
1522
|
+
if (before) {
|
|
1523
|
+
await noteJobFrontmatterWrite({
|
|
1524
|
+
owner: resource.owner,
|
|
1525
|
+
orgId: organizationIdFromResourceOwner(resource.owner),
|
|
1526
|
+
path: resource.path,
|
|
1527
|
+
before: before.content,
|
|
1528
|
+
after: resource.content,
|
|
1529
|
+
writer: "resourcePutSnapshotBatchIfCurrent",
|
|
1530
|
+
});
|
|
1531
|
+
}
|
|
1494
1532
|
emitResourceChange(resource.id, resource.path, resource.owner, writes[index]?.options?.requestSource);
|
|
1495
1533
|
}
|
|
1496
1534
|
return result;
|
|
@@ -134,11 +134,14 @@ export async function run(args = {}) {
|
|
|
134
134
|
detectedFromUser ??
|
|
135
135
|
detectedFromEnv ??
|
|
136
136
|
getAgentEngineEntry("anthropic"));
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
137
|
+
const configuredModel = getAppConfig().agent.model;
|
|
138
|
+
const currentModelCandidate = configuredModel && configuredModel !== "auto"
|
|
139
|
+
? configuredModel
|
|
140
|
+
: appDefaultUsable && currentEntry?.name === appDefault?.engine
|
|
141
|
+
? appDefault?.model
|
|
142
|
+
: storedUsable && currentEntry?.name === current?.engine
|
|
143
|
+
? current?.model
|
|
144
|
+
: undefined;
|
|
142
145
|
const acceptsCustomModels = currentEntry
|
|
143
146
|
? await resolveEngineAcceptsCustomModels(currentEntry)
|
|
144
147
|
: false;
|
|
@@ -2,12 +2,13 @@ import { canUpdateAgentAppModelDefaultSettings, normalizeAgentAppModelDefaultApp
|
|
|
2
2
|
import { CHATGPT_SUBSCRIPTION_ENGINE_NAME } from "../../agent/chatgpt-subscription-contract.js";
|
|
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
|
+
import { defaultModelMessagesForUser } from "../../localization/default-model-messages.js";
|
|
5
6
|
import { getRequestOrgId, getRequestUserEmail, } from "../../server/request-context.js";
|
|
6
7
|
import { run as runList } from "./list-agent-engines.js";
|
|
7
|
-
import { run as runSet } from "./set-agent-engine.js";
|
|
8
|
+
import { run as runSet, resolveDefaultModelEffective, effectiveConfigurationMessage, } from "./set-agent-engine.js";
|
|
8
9
|
import { run as runTest } from "./test-agent-engine.js";
|
|
9
10
|
export const tool = {
|
|
10
|
-
description: 'Manage AI
|
|
11
|
+
description: 'Manage AI engines and scoped defaults. Clarify organization/personal versus app when the user says "change my default" without a scope. action="set" changes only the organization default (owners/admins) or a no-organization user\'s personal default, preserving ALL app overrides. action="set-app-default" changes only the named app default, preserving explicit chat/automation models. Only an explicit request to inherit uses action="reset-app-default". action="list" shows available options, effective selection and canUpdateDefault; action="get-app-default" inspects the app default; action="test" checks connectivity.',
|
|
11
12
|
parameters: {
|
|
12
13
|
type: "object",
|
|
13
14
|
properties: {
|
|
@@ -113,6 +114,10 @@ async function runSetAppDefault(args) {
|
|
|
113
114
|
? "Error: Only organization owners and admins can change app model defaults."
|
|
114
115
|
: "Error: Authentication required to change app model defaults.";
|
|
115
116
|
}
|
|
117
|
+
const effective = await resolveDefaultModelEffective(ctx, appId, {
|
|
118
|
+
appDefault: { engine, model: normalizedModel },
|
|
119
|
+
});
|
|
120
|
+
const messages = await defaultModelMessagesForUser(ctx.userEmail);
|
|
116
121
|
const settings = await writeAgentAppModelDefaultSettings(ctx, appId, {
|
|
117
122
|
engine,
|
|
118
123
|
model: normalizedModel,
|
|
@@ -123,8 +128,12 @@ async function runSetAppDefault(args) {
|
|
|
123
128
|
: ` Requested model "${model}" is no longer supported, so "${normalizedModel}" was saved instead.`;
|
|
124
129
|
return JSON.stringify({
|
|
125
130
|
ok: true,
|
|
131
|
+
requestedScope: "app",
|
|
126
132
|
...settings,
|
|
127
|
-
|
|
133
|
+
effective,
|
|
134
|
+
preservedOverrides: ["chat-models", "automation-models"],
|
|
135
|
+
message: `Default model for ${appId} set to ${normalizedModel} via ${entry.label}.${normalizedNote}` +
|
|
136
|
+
effectiveConfigurationMessage(effective, messages),
|
|
128
137
|
}, null, 2);
|
|
129
138
|
}
|
|
130
139
|
async function runResetAppDefault(args) {
|
|
@@ -138,11 +147,20 @@ async function runResetAppDefault(args) {
|
|
|
138
147
|
? "Error: Only organization owners and admins can reset app model defaults."
|
|
139
148
|
: "Error: Authentication required to reset app model defaults.";
|
|
140
149
|
}
|
|
150
|
+
registerBuiltinEngines();
|
|
151
|
+
const effective = await resolveDefaultModelEffective(ctx, appId, {
|
|
152
|
+
appDefault: null,
|
|
153
|
+
});
|
|
154
|
+
const messages = await defaultModelMessagesForUser(ctx.userEmail);
|
|
141
155
|
const settings = await resetAgentAppModelDefaultSettings(ctx, appId);
|
|
142
156
|
return JSON.stringify({
|
|
143
157
|
ok: true,
|
|
158
|
+
requestedScope: "app",
|
|
144
159
|
...settings,
|
|
145
|
-
|
|
160
|
+
effective,
|
|
161
|
+
preservedOverrides: ["chat-models", "automation-models"],
|
|
162
|
+
message: `Default model for ${appId} reset to the global LLM default.` +
|
|
163
|
+
effectiveConfigurationMessage(effective, messages),
|
|
146
164
|
}, null, 2);
|
|
147
165
|
}
|
|
148
166
|
export async function run(args, context) {
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { type ActionRunContext } from "../../action.js";
|
|
6
6
|
import { type DefaultAgentEngineChangeMeta, type DefaultAgentEngineContext } from "../../agent/default-agent-engine.js";
|
|
7
|
+
import { type AgentEngineStatusResult } from "../../agent/engine-status.js";
|
|
7
8
|
import type { ActionTool } from "../../agent/types.js";
|
|
8
9
|
export declare const tool: ActionTool;
|
|
9
10
|
export declare const DEFAULT_MODEL_ADMIN_REQUIRED_ERROR_CODE = "default_model_admin_required";
|
|
@@ -13,6 +14,11 @@ export type SelectDefaultAgentEngineResult = {
|
|
|
13
14
|
model: string;
|
|
14
15
|
requestedModel: string;
|
|
15
16
|
label: string;
|
|
17
|
+
scope: "org" | "user";
|
|
18
|
+
appId?: string;
|
|
19
|
+
effective: Pick<AgentEngineStatusResult, "configured" | "engine" | "model"> & {
|
|
20
|
+
source?: "configuration" | "app-default" | "org" | "user" | "legacy" | "app_secrets" | "env";
|
|
21
|
+
};
|
|
16
22
|
} | {
|
|
17
23
|
status: "refused";
|
|
18
24
|
message: string;
|
|
@@ -26,6 +32,24 @@ export type SelectDefaultAgentEngineResult = {
|
|
|
26
32
|
status: "unavailable";
|
|
27
33
|
message: string;
|
|
28
34
|
};
|
|
35
|
+
export declare function resolveDefaultModelEffective(ctx: DefaultAgentEngineContext, appId: string | null, planned?: {
|
|
36
|
+
sharedDefault?: {
|
|
37
|
+
engine: string;
|
|
38
|
+
model: string;
|
|
39
|
+
};
|
|
40
|
+
appDefault?: {
|
|
41
|
+
engine: string;
|
|
42
|
+
model: string;
|
|
43
|
+
} | null;
|
|
44
|
+
}): Promise<Extract<SelectDefaultAgentEngineResult, {
|
|
45
|
+
status: "selected";
|
|
46
|
+
}>["effective"]>;
|
|
47
|
+
export declare function effectiveConfigurationMessage(effective: Extract<SelectDefaultAgentEngineResult, {
|
|
48
|
+
status: "selected";
|
|
49
|
+
}>["effective"], messages: {
|
|
50
|
+
configurationOverride: string;
|
|
51
|
+
configurationUnavailable: string;
|
|
52
|
+
}): string;
|
|
29
53
|
/**
|
|
30
54
|
* Validate an engine/model pair and save it as the default for the caller's
|
|
31
55
|
* scope. Shared by the `set` action and the provider-key save route so both
|
|
@@ -34,5 +58,6 @@ export type SelectDefaultAgentEngineResult = {
|
|
|
34
58
|
export declare function selectDefaultAgentEngine(input: {
|
|
35
59
|
engine?: string;
|
|
36
60
|
model?: string;
|
|
61
|
+
appId?: string;
|
|
37
62
|
}, meta: DefaultAgentEngineChangeMeta, ctx?: DefaultAgentEngineContext): Promise<SelectDefaultAgentEngineResult>;
|
|
38
63
|
export declare function run(args: Record<string, string>, context?: ActionRunContext): Promise<string>;
|
|
@@ -3,15 +3,19 @@
|
|
|
3
3
|
* `agent-engine` setting) for the caller's organization.
|
|
4
4
|
*/
|
|
5
5
|
import { ActionContractError } from "../../action.js";
|
|
6
|
+
import { normalizeAgentAppModelDefaultAppId, readAgentAppModelDefaultSettings, } from "../../agent/app-model-defaults.js";
|
|
6
7
|
import { CHATGPT_SUBSCRIPTION_ENGINE_NAME } from "../../agent/chatgpt-subscription-contract.js";
|
|
7
|
-
import { recordDefaultAgentEngineRefusal, resolveDefaultAgentEngineAuthority, writeDefaultAgentEngineSelection, } from "../../agent/default-agent-engine.js";
|
|
8
|
+
import { recordDefaultAgentEngineRefusal, readDefaultAgentEngineSettingDetailed, resolveDefaultAgentEngineAuthority, writeDefaultAgentEngineSelection, } from "../../agent/default-agent-engine.js";
|
|
9
|
+
import { resolveAgentEngineStatus, } from "../../agent/engine-status.js";
|
|
8
10
|
import { listChatGPTSubscriptionModels } from "../../agent/engine/chatgpt-subscription-engine.js";
|
|
9
|
-
import { listAgentEngines, getAgentEngineEntry, isAgentEnginePackageInstalled, isStoredEngineUsableForRequest, normalizeModelForEngine, resolveEngineAcceptsCustomModels, resolveEnginePreservesCustomModels, registerBuiltinEngines, } from "../../agent/engine/index.js";
|
|
11
|
+
import { listAgentEngines, detectEngineFromEnv, detectEngineFromUserSecrets, getAgentEngineEntry, isAgentEnginePackageInstalled, isStoredEngineUsableForRequest, normalizeModelForEngine, resolveEngineAcceptsCustomModels, resolveEnginePreservesCustomModels, registerBuiltinEngines, } from "../../agent/engine/index.js";
|
|
12
|
+
import { getAppConfig } from "../../app-config/index.js";
|
|
10
13
|
import { CHATGPT_SUBSCRIPTION_LAB } from "../../labs/core-labs.js";
|
|
11
14
|
import { getUserLabEnabled } from "../../labs/store.js";
|
|
15
|
+
import { defaultModelMessagesForUser } from "../../localization/default-model-messages.js";
|
|
12
16
|
import { getRequestOrgId, getRequestUserEmail, } from "../../server/request-context.js";
|
|
13
17
|
export const tool = {
|
|
14
|
-
description: 'Set the organization\'s default AI engine and model
|
|
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.',
|
|
15
19
|
parameters: {
|
|
16
20
|
type: "object",
|
|
17
21
|
properties: {
|
|
@@ -28,6 +32,58 @@ export const tool = {
|
|
|
28
32
|
},
|
|
29
33
|
};
|
|
30
34
|
export const DEFAULT_MODEL_ADMIN_REQUIRED_ERROR_CODE = "default_model_admin_required";
|
|
35
|
+
export async function resolveDefaultModelEffective(ctx, appId, planned = {}) {
|
|
36
|
+
const credentialIdentity = ctx.userEmail
|
|
37
|
+
? { userEmail: ctx.userEmail, orgId: ctx.orgId }
|
|
38
|
+
: undefined;
|
|
39
|
+
const shared = {
|
|
40
|
+
source: ctx.orgId ? "org" : "user",
|
|
41
|
+
};
|
|
42
|
+
const status = await resolveAgentEngineStatus({
|
|
43
|
+
lookupEntry: getAgentEngineEntry,
|
|
44
|
+
readStoredEngine: async () => {
|
|
45
|
+
if (planned.sharedDefault)
|
|
46
|
+
return planned.sharedDefault;
|
|
47
|
+
const stored = await readDefaultAgentEngineSettingDetailed(ctx);
|
|
48
|
+
shared.source = stored.source;
|
|
49
|
+
return stored.value;
|
|
50
|
+
},
|
|
51
|
+
readAppDefault: async () => {
|
|
52
|
+
if (planned.appDefault !== undefined)
|
|
53
|
+
return planned.appDefault;
|
|
54
|
+
if (!appId)
|
|
55
|
+
return null;
|
|
56
|
+
const stored = await readAgentAppModelDefaultSettings(ctx, appId);
|
|
57
|
+
return stored.engine && stored.model
|
|
58
|
+
? { engine: stored.engine, model: stored.model }
|
|
59
|
+
: null;
|
|
60
|
+
},
|
|
61
|
+
readOpenAiBaseUrlConfigured: () => false,
|
|
62
|
+
isStoredEngineUsable: (stored, entry) => isStoredEngineUsableForRequest(stored, entry, { credentialIdentity }),
|
|
63
|
+
detectFromUserSecrets: () => detectEngineFromUserSecrets(credentialIdentity),
|
|
64
|
+
detectFromEnv: detectEngineFromEnv,
|
|
65
|
+
});
|
|
66
|
+
return {
|
|
67
|
+
configured: status.configured,
|
|
68
|
+
engine: status.engine,
|
|
69
|
+
model: status.model,
|
|
70
|
+
source: status.selectionSource === "shared-default"
|
|
71
|
+
? shared.source === "none"
|
|
72
|
+
? undefined
|
|
73
|
+
: shared.source
|
|
74
|
+
: status.selectionSource,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
export function effectiveConfigurationMessage(effective, messages) {
|
|
78
|
+
if (effective.source !== "configuration")
|
|
79
|
+
return "";
|
|
80
|
+
return (" " +
|
|
81
|
+
(effective.configured
|
|
82
|
+
? messages.configurationOverride
|
|
83
|
+
.replace("{{model}}", effective.model)
|
|
84
|
+
.replace("{{engine}}", effective.engine)
|
|
85
|
+
: messages.configurationUnavailable));
|
|
86
|
+
}
|
|
31
87
|
/**
|
|
32
88
|
* Validate an engine/model pair and save it as the default for the caller's
|
|
33
89
|
* scope. Shared by the `set` action and the provider-key save route so both
|
|
@@ -135,6 +191,15 @@ export async function selectDefaultAgentEngine(input, meta, ctx = {
|
|
|
135
191
|
acceptsCustomModels,
|
|
136
192
|
preserveCustomModels,
|
|
137
193
|
});
|
|
194
|
+
const config = getAppConfig();
|
|
195
|
+
const appIdInput = input.appId ?? config.app.id ?? config.app.template ?? config.app.slug;
|
|
196
|
+
const appId = normalizeAgentAppModelDefaultAppId(appIdInput);
|
|
197
|
+
if (appIdInput && !appId) {
|
|
198
|
+
return { status: "invalid", message: "A valid appId is required." };
|
|
199
|
+
}
|
|
200
|
+
const effective = await resolveDefaultModelEffective(ctx, appId, {
|
|
201
|
+
sharedDefault: { engine: engineName, model: resolvedModel },
|
|
202
|
+
});
|
|
138
203
|
await writeDefaultAgentEngineSelection(authority, { engine: engineName, model: resolvedModel }, meta);
|
|
139
204
|
return {
|
|
140
205
|
status: "selected",
|
|
@@ -142,10 +207,14 @@ export async function selectDefaultAgentEngine(input, meta, ctx = {
|
|
|
142
207
|
model: resolvedModel,
|
|
143
208
|
requestedModel,
|
|
144
209
|
label: entry.label,
|
|
210
|
+
scope: authority.scope,
|
|
211
|
+
effective,
|
|
212
|
+
...(appId ? { appId } : {}),
|
|
145
213
|
};
|
|
146
214
|
}
|
|
147
215
|
export async function run(args, context) {
|
|
148
|
-
const
|
|
216
|
+
const messages = await defaultModelMessagesForUser(getRequestUserEmail());
|
|
217
|
+
const result = await selectDefaultAgentEngine({ ...args, appId: context?.appId ?? args.appId }, {
|
|
149
218
|
actionName: context?.actionName ?? "manage-agent-engine",
|
|
150
219
|
caller: context?.caller,
|
|
151
220
|
threadId: context?.threadId,
|
|
@@ -166,13 +235,30 @@ export async function run(args, context) {
|
|
|
166
235
|
if (result.status === "unavailable") {
|
|
167
236
|
return `Error: ${result.message}`;
|
|
168
237
|
}
|
|
169
|
-
const normalizedNote = result.model === result.requestedModel
|
|
170
|
-
? ""
|
|
171
|
-
: ` Requested model "${result.requestedModel}" is no longer supported, so "${result.model}" was saved instead.`;
|
|
172
238
|
return JSON.stringify({
|
|
173
239
|
ok: true,
|
|
174
240
|
engine: result.engine,
|
|
175
241
|
model: result.model,
|
|
176
|
-
|
|
242
|
+
requestedModel: result.requestedModel,
|
|
243
|
+
scope: result.scope,
|
|
244
|
+
requestedScope: result.scope,
|
|
245
|
+
appId: result.appId,
|
|
246
|
+
appDefaultReset: false,
|
|
247
|
+
preservedOverrides: ["app-models", "chat-models", "automation-models"],
|
|
248
|
+
effective: result.effective,
|
|
249
|
+
message: (result.scope === "org"
|
|
250
|
+
? messages.organizationSelected
|
|
251
|
+
: messages.userSelected)
|
|
252
|
+
.replace("{{model}}", result.model)
|
|
253
|
+
.replace("{{engine}}", result.label) +
|
|
254
|
+
(result.effective.source === "configuration"
|
|
255
|
+
? effectiveConfigurationMessage(result.effective, messages)
|
|
256
|
+
: result.appId && result.effective.source === "app-default"
|
|
257
|
+
? " " +
|
|
258
|
+
messages.appOverridePreserved
|
|
259
|
+
.replace("{{appId}}", result.appId)
|
|
260
|
+
.replace("{{model}}", result.effective.model)
|
|
261
|
+
.replace("{{engine}}", result.effective.engine)
|
|
262
|
+
: ""),
|
|
177
263
|
});
|
|
178
264
|
}
|
package/dist/secrets/routes.d.ts
CHANGED
|
@@ -73,32 +73,32 @@ export declare function createWriteSecretHandler(): import("h3").EventHandlerWit
|
|
|
73
73
|
ok?: undefined;
|
|
74
74
|
status?: undefined;
|
|
75
75
|
} | {
|
|
76
|
+
error?: undefined;
|
|
76
77
|
errorCode?: undefined;
|
|
77
78
|
retryable?: undefined;
|
|
78
79
|
ok: boolean;
|
|
79
80
|
status: string;
|
|
80
|
-
error?: undefined;
|
|
81
81
|
} | {
|
|
82
82
|
ok?: undefined;
|
|
83
83
|
error: string;
|
|
84
84
|
removed?: undefined;
|
|
85
85
|
} | {
|
|
86
|
+
error?: undefined;
|
|
86
87
|
ok: boolean;
|
|
87
88
|
removed: boolean;
|
|
88
|
-
error?: undefined;
|
|
89
89
|
}>>;
|
|
90
90
|
export declare function createTestSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
91
91
|
ok?: undefined;
|
|
92
92
|
error: string;
|
|
93
93
|
note?: undefined;
|
|
94
94
|
} | {
|
|
95
|
+
error?: undefined;
|
|
95
96
|
ok: boolean;
|
|
96
97
|
note?: undefined;
|
|
97
|
-
error?: undefined;
|
|
98
98
|
} | {
|
|
99
|
+
error?: undefined;
|
|
99
100
|
ok: boolean;
|
|
100
101
|
note: string;
|
|
101
|
-
error?: undefined;
|
|
102
102
|
} | {
|
|
103
103
|
note?: undefined;
|
|
104
104
|
ok: boolean;
|
|
@@ -126,11 +126,11 @@ export declare function createAdHocSecretHandler(): import("h3").EventHandlerWit
|
|
|
126
126
|
} | {
|
|
127
127
|
error: string;
|
|
128
128
|
} | {
|
|
129
|
+
error?: undefined;
|
|
129
130
|
ok: boolean;
|
|
130
131
|
key: string;
|
|
131
|
-
error?: undefined;
|
|
132
132
|
} | {
|
|
133
|
+
error?: undefined;
|
|
133
134
|
ok: boolean;
|
|
134
135
|
removed: boolean;
|
|
135
|
-
error?: undefined;
|
|
136
136
|
}>>;
|
|
@@ -9,8 +9,8 @@ export declare const AGENT_ENGINE_API_KEY_ACTION_NAME = "agent-engine-api-key";
|
|
|
9
9
|
*/
|
|
10
10
|
export declare function createAgentEngineDisconnectHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
11
11
|
error: string;
|
|
12
|
-
ok?: undefined;
|
|
13
12
|
scope?: undefined;
|
|
13
|
+
ok?: undefined;
|
|
14
14
|
} | {
|
|
15
15
|
ok: boolean;
|
|
16
16
|
error: any;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { H3Event } from "h3";
|
|
2
2
|
import type { CredentialFixer } from "../agent/engine/credential-state.js";
|
|
3
|
+
export { normalizeAgentEngineStatusModel, resolveAgentEngineStatus, type AgentEngineStatusResult, type AgentEngineStatusResponse, type AgentEngineStatusDeps, } from "../agent/engine-status.js";
|
|
3
4
|
import { type AppStateAnonymousOwnerResolver } from "../application-state/handlers.js";
|
|
4
5
|
import { type DatabaseSchemaHealthResult } from "../db/runtime-diagnostics.js";
|
|
5
6
|
import { ensureS3FileUploadProvider } from "../file-upload/s3.js";
|
|
@@ -23,49 +24,6 @@ import { type H3AppShim } from "./framework-request-handler.js";
|
|
|
23
24
|
export declare const FRAMEWORK_ROUTE_PREFIX = "/_agent-native";
|
|
24
25
|
export declare const FRAMEWORK_EVENTS_ROUTE = "/_agent-native/events";
|
|
25
26
|
export declare const LEGACY_FRAMEWORK_EVENTS_ROUTE = "/_agent-native/poll-events";
|
|
26
|
-
export declare function normalizeAgentEngineStatusModel(entry: {
|
|
27
|
-
name: string;
|
|
28
|
-
defaultModel: string;
|
|
29
|
-
supportedModels: readonly string[];
|
|
30
|
-
} | undefined, model: string | null | undefined): string;
|
|
31
|
-
type AgentEngineStatusEntry = {
|
|
32
|
-
name: string;
|
|
33
|
-
defaultModel: string;
|
|
34
|
-
supportedModels: readonly string[];
|
|
35
|
-
requiredEnvVars: readonly string[];
|
|
36
|
-
};
|
|
37
|
-
export interface AgentEngineStatusResult {
|
|
38
|
-
configured: boolean;
|
|
39
|
-
engine?: string;
|
|
40
|
-
model?: string;
|
|
41
|
-
source?: "settings" | "env" | "app_secrets";
|
|
42
|
-
envVar?: string;
|
|
43
|
-
openAiBaseUrlConfigured?: boolean;
|
|
44
|
-
}
|
|
45
|
-
export interface AgentEngineStatusResponse extends AgentEngineStatusResult {
|
|
46
|
-
/** Strict chat-only eligibility; distinct from broad engine `configured`. */
|
|
47
|
-
chatEligible: boolean;
|
|
48
|
-
}
|
|
49
|
-
export interface AgentEngineStatusDeps<E extends AgentEngineStatusEntry = AgentEngineStatusEntry> {
|
|
50
|
-
readStoredEngine: () => Promise<{
|
|
51
|
-
engine?: string;
|
|
52
|
-
model?: string;
|
|
53
|
-
} | null>;
|
|
54
|
-
readOpenAiBaseUrlConfigured: () => boolean | Promise<boolean>;
|
|
55
|
-
isStoredEngineUsable: (stored: unknown, entry: E) => boolean | Promise<boolean>;
|
|
56
|
-
detectFromUserSecrets: () => Promise<E | null>;
|
|
57
|
-
detectFromEnv: () => E | null | Promise<E | null>;
|
|
58
|
-
lookupEntry?: (engine: string) => E | undefined;
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* Resolve "does this request have a usable AI provider" for one identity.
|
|
62
|
-
*
|
|
63
|
-
* Every call site pays for these lookups on a user-visible path (the agent
|
|
64
|
-
* composer blocks on the status probe), so the two identity-independent reads
|
|
65
|
-
* start together and the expensive `app_secrets` sweep only runs when the
|
|
66
|
-
* cheaper sources have not already answered.
|
|
67
|
-
*/
|
|
68
|
-
export declare function resolveAgentEngineStatus<E extends AgentEngineStatusEntry>(deps: AgentEngineStatusDeps<E>): Promise<AgentEngineStatusResult>;
|
|
69
27
|
/**
|
|
70
28
|
* Resolve the identity the status answer depends on. Both lookups memoize per
|
|
71
29
|
* request inside their own helpers, so repeating them here stays cheap.
|