@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.
Files changed (39) hide show
  1. package/dist/agent/default-agent-engine.d.ts +1 -1
  2. package/dist/agent/default-agent-engine.js +1 -1
  3. package/dist/agent/engine-status.d.ts +49 -0
  4. package/dist/agent/engine-status.js +112 -0
  5. package/dist/client/client-status-requests.js +8 -0
  6. package/dist/collab/awareness.d.ts +2 -2
  7. package/dist/collab/routes.d.ts +1 -1
  8. package/dist/jobs/actions/manage-recurring-job.d.ts +1 -1
  9. package/dist/jobs/frontmatter-loss.d.ts +17 -0
  10. package/dist/jobs/frontmatter-loss.js +62 -0
  11. package/dist/jobs/frontmatter.d.ts +7 -0
  12. package/dist/jobs/frontmatter.js +39 -0
  13. package/dist/jobs/tools.js +16 -3
  14. package/dist/localization/default-model-messages.d.ts +11 -0
  15. package/dist/localization/default-model-messages.js +88 -0
  16. package/dist/notifications/routes.d.ts +1 -1
  17. package/dist/observability/routes.d.ts +2 -2
  18. package/dist/org/index.d.ts +1 -1
  19. package/dist/progress/routes.d.ts +1 -1
  20. package/dist/provider-api/actions/custom-provider-registration.d.ts +4 -4
  21. package/dist/provider-api/actions/provider-api.d.ts +8 -8
  22. package/dist/resources/handlers.d.ts +1 -1
  23. package/dist/resources/store.js +40 -2
  24. package/dist/scripts/agent-engines/list-agent-engines.js +8 -5
  25. package/dist/scripts/agent-engines/manage-agent-engine.js +22 -4
  26. package/dist/scripts/agent-engines/set-agent-engine.d.ts +25 -0
  27. package/dist/scripts/agent-engines/set-agent-engine.js +94 -8
  28. package/dist/secrets/routes.d.ts +6 -6
  29. package/dist/server/agent-engine-default-model-route.d.ts +1 -1
  30. package/dist/server/agent-engine-ollama-models-route.d.ts +1 -1
  31. package/dist/server/core-routes-plugin.d.ts +1 -43
  32. package/dist/server/core-routes-plugin.js +15 -92
  33. package/dist/server/email-auth-links.js +98 -2
  34. package/dist/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +26 -39
  35. package/dist/templates/workspace-core/.agents/skills/recurring-jobs/SKILL.md +16 -1
  36. package/dist/triggers/actions/manage-automation.d.ts +7 -7
  37. package/package.json +4 -4
  38. package/src/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +26 -39
  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
- provider?: undefined;
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
- deleted?: undefined;
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
- deleted?: undefined;
362
+ provider?: undefined;
363
363
  count?: undefined;
364
364
  found?: undefined;
365
365
  providers?: undefined;
366
- provider?: undefined;
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;
@@ -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 currentModelCandidate = appDefaultUsable && currentEntry?.name === appDefault?.engine
138
- ? appDefault?.model
139
- : storedUsable && currentEntry?.name === current?.engine
140
- ? current?.model
141
- : undefined;
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 agent engines: list available engines, set the organization default engine/model, test an engine, or manage the current app/template default model. Pass action="list" to see options, action="set" to change the organization default (owners and admins only; list reports canUpdateDefault), action="test" to verify connectivity, action="get-app-default" to inspect this app default, action="set-app-default" to set this app default, or action="reset-app-default" to clear it.',
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
- message: `Default model for ${appId} set to ${normalizedModel} via ${entry.label}.${normalizedNote}`,
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
- message: `Default model for ${appId} reset to the global LLM default.`,
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. Only organization owners and admins can change it; a user with no organization sets their own. Changes take effect on the next conversation. Use manage-agent-engine with action="list" first to see available options and whether you can change the default (canUpdateDefault).',
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 result = await selectDefaultAgentEngine(args, {
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
- message: `Default model set to ${result.label} with model ${result.model}. Takes effect on the next conversation.${normalizedNote}`,
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
  }
@@ -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,6 +1,6 @@
1
1
  export declare function createAgentEngineOllamaModelsHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
2
- error: string;
3
2
  ok?: undefined;
3
+ error: string;
4
4
  models?: undefined;
5
5
  } | {
6
6
  error?: undefined;
@@ -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.