@agent-native/core 0.203.1-nightly-20261007183952 → 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/routes.d.ts +2 -2
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/file-upload/actions/upload-image.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 +4 -4
- package/dist/org/handlers.d.ts +2 -2
- package/dist/org/index.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/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/agent-engine-default-model-route.d.ts +1 -1
- package/dist/server/agent-engine-ollama-models-route.d.ts +2 -2
- package/dist/server/core-routes-plugin.d.ts +1 -43
- package/dist/server/core-routes-plugin.js +15 -92
- package/dist/templates/workspace-core/.agents/skills/recurring-jobs/SKILL.md +16 -1
- package/package.json +4 -4
- package/src/templates/workspace-core/.agents/skills/recurring-jobs/SKILL.md +16 -1
|
@@ -71,7 +71,7 @@ export declare function readDefaultAgentEngineSetting(ctx?: DefaultAgentEngineCo
|
|
|
71
71
|
export declare function resolveDefaultAgentEngineAuthority(ctx?: DefaultAgentEngineContext): Promise<DefaultAgentEngineAuthority>;
|
|
72
72
|
/**
|
|
73
73
|
* Save the default for the authority's scope. Validate the engine first; this
|
|
74
|
-
* only
|
|
74
|
+
* stores only that scope and records the change.
|
|
75
75
|
*/
|
|
76
76
|
export declare function writeDefaultAgentEngineSelection(authority: Extract<DefaultAgentEngineAuthority, {
|
|
77
77
|
allowed: true;
|
|
@@ -95,7 +95,7 @@ async function writeScopedRow(authority, value) {
|
|
|
95
95
|
}
|
|
96
96
|
/**
|
|
97
97
|
* Save the default for the authority's scope. Validate the engine first; this
|
|
98
|
-
* only
|
|
98
|
+
* stores only that scope and records the change.
|
|
99
99
|
*/
|
|
100
100
|
export async function writeDefaultAgentEngineSelection(authority, selection, meta) {
|
|
101
101
|
await writeScopedRow(authority, {
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export declare function normalizeAgentEngineStatusModel(entry: {
|
|
2
|
+
name: string;
|
|
3
|
+
defaultModel: string;
|
|
4
|
+
supportedModels: readonly string[];
|
|
5
|
+
} | undefined, model: string | null | undefined): string;
|
|
6
|
+
type AgentEngineStatusEntry = {
|
|
7
|
+
name: string;
|
|
8
|
+
defaultModel: string;
|
|
9
|
+
supportedModels: readonly string[];
|
|
10
|
+
requiredEnvVars: readonly string[];
|
|
11
|
+
};
|
|
12
|
+
export interface AgentEngineStatusResult {
|
|
13
|
+
configured: boolean;
|
|
14
|
+
engine?: string;
|
|
15
|
+
model?: string;
|
|
16
|
+
source?: "settings" | "env" | "app_secrets" | "app-default";
|
|
17
|
+
selectionSource?: "configuration" | "app-default" | "shared-default" | "app_secrets" | "env";
|
|
18
|
+
envVar?: string;
|
|
19
|
+
openAiBaseUrlConfigured?: boolean;
|
|
20
|
+
}
|
|
21
|
+
export interface AgentEngineStatusResponse extends AgentEngineStatusResult {
|
|
22
|
+
/** Strict chat-only eligibility; distinct from broad engine `configured`. */
|
|
23
|
+
chatEligible: boolean;
|
|
24
|
+
}
|
|
25
|
+
export interface AgentEngineStatusDeps<E extends AgentEngineStatusEntry = AgentEngineStatusEntry> {
|
|
26
|
+
readStoredEngine: () => Promise<{
|
|
27
|
+
engine?: string;
|
|
28
|
+
model?: string;
|
|
29
|
+
} | null>;
|
|
30
|
+
readAppDefault?: () => Promise<{
|
|
31
|
+
engine: string;
|
|
32
|
+
model: string;
|
|
33
|
+
} | null>;
|
|
34
|
+
readOpenAiBaseUrlConfigured: () => boolean | Promise<boolean>;
|
|
35
|
+
isStoredEngineUsable: (stored: unknown, entry: E) => boolean | Promise<boolean>;
|
|
36
|
+
detectFromUserSecrets: () => Promise<E | null>;
|
|
37
|
+
detectFromEnv: () => E | null | Promise<E | null>;
|
|
38
|
+
lookupEntry?: (engine: string) => E | undefined;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Resolve "does this request have a usable AI provider" for one identity.
|
|
42
|
+
*
|
|
43
|
+
* Every call site pays for these lookups on a user-visible path (the agent
|
|
44
|
+
* composer blocks on the status probe), so the two identity-independent reads
|
|
45
|
+
* start together and the expensive `app_secrets` sweep only runs when the
|
|
46
|
+
* cheaper sources have not already answered.
|
|
47
|
+
*/
|
|
48
|
+
export declare function resolveAgentEngineStatus<E extends AgentEngineStatusEntry>(deps: AgentEngineStatusDeps<E>): Promise<AgentEngineStatusResult>;
|
|
49
|
+
export {};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { getAppConfig } from "../app-config/index.js";
|
|
2
|
+
import { getRequestContext } from "../server/request-context.js";
|
|
3
|
+
import { DEFAULT_MODEL } from "./default-model.js";
|
|
4
|
+
import { getAgentEngineEntry, isAgentEngineSettingConfigured, normalizeModelForEngine, } from "./engine/registry.js";
|
|
5
|
+
export function normalizeAgentEngineStatusModel(entry, model) {
|
|
6
|
+
if (!entry)
|
|
7
|
+
return model ?? DEFAULT_MODEL;
|
|
8
|
+
return normalizeModelForEngine(entry, model ?? entry.defaultModel);
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Resolve "does this request have a usable AI provider" for one identity.
|
|
12
|
+
*
|
|
13
|
+
* Every call site pays for these lookups on a user-visible path (the agent
|
|
14
|
+
* composer blocks on the status probe), so the two identity-independent reads
|
|
15
|
+
* start together and the expensive `app_secrets` sweep only runs when the
|
|
16
|
+
* cheaper sources have not already answered.
|
|
17
|
+
*/
|
|
18
|
+
export async function resolveAgentEngineStatus(deps) {
|
|
19
|
+
const lookupEntry = (deps.lookupEntry ?? getAgentEngineEntry);
|
|
20
|
+
const [stored, openAiBaseUrlConfigured, appDefault] = await Promise.all([
|
|
21
|
+
deps.readStoredEngine(),
|
|
22
|
+
deps.readOpenAiBaseUrlConfigured(),
|
|
23
|
+
deps.readAppDefault?.(),
|
|
24
|
+
]);
|
|
25
|
+
const configuredEngine = getAppConfig().agent.engine;
|
|
26
|
+
const configuredModel = getAppConfig().agent.model === "auto"
|
|
27
|
+
? undefined
|
|
28
|
+
: getAppConfig().agent.model;
|
|
29
|
+
const envEntry = configuredEngine ? lookupEntry(configuredEngine) : undefined;
|
|
30
|
+
if (envEntry) {
|
|
31
|
+
if (await deps.isStoredEngineUsable({ engine: envEntry.name }, envEntry)) {
|
|
32
|
+
return {
|
|
33
|
+
configured: true,
|
|
34
|
+
engine: envEntry.name,
|
|
35
|
+
model: normalizeAgentEngineStatusModel(envEntry, configuredModel ??
|
|
36
|
+
(appDefault?.engine === envEntry.name
|
|
37
|
+
? appDefault.model
|
|
38
|
+
: stored?.engine === envEntry.name
|
|
39
|
+
? stored.model
|
|
40
|
+
: undefined)),
|
|
41
|
+
source: "env",
|
|
42
|
+
selectionSource: "configuration",
|
|
43
|
+
envVar: "AGENT_ENGINE",
|
|
44
|
+
openAiBaseUrlConfigured,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
if (getRequestContext()?.isSyntheticTraffic !== true) {
|
|
48
|
+
return {
|
|
49
|
+
configured: false,
|
|
50
|
+
selectionSource: "configuration",
|
|
51
|
+
openAiBaseUrlConfigured,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (appDefault) {
|
|
56
|
+
const entry = lookupEntry(appDefault.engine);
|
|
57
|
+
if (entry && (await deps.isStoredEngineUsable(appDefault, entry))) {
|
|
58
|
+
return {
|
|
59
|
+
configured: true,
|
|
60
|
+
engine: appDefault.engine,
|
|
61
|
+
model: normalizeAgentEngineStatusModel(entry, configuredModel ?? appDefault.model),
|
|
62
|
+
source: "app-default",
|
|
63
|
+
selectionSource: configuredModel ? "configuration" : "app-default",
|
|
64
|
+
openAiBaseUrlConfigured,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// Stored provider selections win over an existing Builder connection, so
|
|
69
|
+
// this is checked before the app_secrets sweep — and the sweep is skipped
|
|
70
|
+
// entirely when it answers.
|
|
71
|
+
if (stored && typeof stored.engine === "string") {
|
|
72
|
+
const entry = lookupEntry(stored.engine);
|
|
73
|
+
if (entry && (await deps.isStoredEngineUsable(stored, entry))) {
|
|
74
|
+
return {
|
|
75
|
+
configured: true,
|
|
76
|
+
engine: stored.engine,
|
|
77
|
+
model: normalizeAgentEngineStatusModel(entry, configuredModel ?? stored.model),
|
|
78
|
+
source: isAgentEngineSettingConfigured(stored) ? "settings" : "env",
|
|
79
|
+
selectionSource: configuredModel ? "configuration" : "shared-default",
|
|
80
|
+
envVar: entry.requiredEnvVars[0],
|
|
81
|
+
openAiBaseUrlConfigured,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Per-user app_secrets — a user who connected Builder (or pasted their own
|
|
86
|
+
// provider key) may not have any deploy-level env vars set.
|
|
87
|
+
const detectedFromUser = await deps.detectFromUserSecrets();
|
|
88
|
+
if (detectedFromUser) {
|
|
89
|
+
return {
|
|
90
|
+
configured: true,
|
|
91
|
+
engine: detectedFromUser.name,
|
|
92
|
+
model: normalizeAgentEngineStatusModel(detectedFromUser, configuredModel),
|
|
93
|
+
source: "app_secrets",
|
|
94
|
+
selectionSource: configuredModel ? "configuration" : "app_secrets",
|
|
95
|
+
envVar: detectedFromUser.requiredEnvVars[0],
|
|
96
|
+
openAiBaseUrlConfigured,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
const detected = await deps.detectFromEnv();
|
|
100
|
+
if (detected) {
|
|
101
|
+
return {
|
|
102
|
+
configured: true,
|
|
103
|
+
engine: detected.name,
|
|
104
|
+
model: normalizeAgentEngineStatusModel(detected, configuredModel),
|
|
105
|
+
source: "env",
|
|
106
|
+
selectionSource: configuredModel ? "configuration" : "env",
|
|
107
|
+
envVar: detected.requiredEnvVars[0],
|
|
108
|
+
openAiBaseUrlConfigured,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
return { configured: false, openAiBaseUrlConfigured };
|
|
112
|
+
}
|
|
@@ -47,6 +47,14 @@ function installInvalidationListeners() {
|
|
|
47
47
|
if (typeof window.addEventListener === "function") {
|
|
48
48
|
window.addEventListener("focus", expireClientStatusCache);
|
|
49
49
|
window.addEventListener("agent-engine:configured-changed", invalidateClientStatusRequests);
|
|
50
|
+
window.addEventListener("agent-native:tool-done", (event) => {
|
|
51
|
+
const detail = event.detail;
|
|
52
|
+
if (detail?.tool === "manage-agent-engine" &&
|
|
53
|
+
detail.completedSideEffect === true &&
|
|
54
|
+
detail.isError !== true) {
|
|
55
|
+
window.dispatchEvent(new Event("agent-engine:configured-changed"));
|
|
56
|
+
}
|
|
57
|
+
});
|
|
50
58
|
}
|
|
51
59
|
if (typeof document.addEventListener === "function") {
|
|
52
60
|
document.addEventListener("visibilitychange", () => {
|
package/dist/collab/routes.d.ts
CHANGED
|
@@ -8,15 +8,15 @@ export declare const getCollabState: import("h3").EventHandlerWithFetch<import("
|
|
|
8
8
|
state: string;
|
|
9
9
|
}>>;
|
|
10
10
|
export declare const postCollabUpdate: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
11
|
-
ok?: undefined;
|
|
12
11
|
error: string;
|
|
12
|
+
ok?: undefined;
|
|
13
13
|
} | {
|
|
14
14
|
error?: undefined;
|
|
15
15
|
ok: boolean;
|
|
16
16
|
}>>;
|
|
17
17
|
export declare const postCollabText: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
18
|
-
ok?: undefined;
|
|
19
18
|
text?: undefined;
|
|
19
|
+
ok?: undefined;
|
|
20
20
|
error: string;
|
|
21
21
|
} | {
|
|
22
22
|
error?: undefined;
|
|
@@ -39,11 +39,11 @@ declare const _default: import("../../action.js").ActionDefinition<{
|
|
|
39
39
|
provider?: undefined;
|
|
40
40
|
configured?: undefined;
|
|
41
41
|
} | {
|
|
42
|
-
error?: undefined;
|
|
43
42
|
url: string;
|
|
44
43
|
id: string;
|
|
45
44
|
provider: string;
|
|
46
45
|
configured?: undefined;
|
|
46
|
+
error?: undefined;
|
|
47
47
|
} | {
|
|
48
48
|
url?: undefined;
|
|
49
49
|
id?: undefined;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface JobFileWrite {
|
|
2
|
+
owner: string;
|
|
3
|
+
orgId: string | null;
|
|
4
|
+
path: string;
|
|
5
|
+
before: string;
|
|
6
|
+
after: string;
|
|
7
|
+
writer: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* A write that removes editor-owned frontmatter fields from a job file is
|
|
11
|
+
* recorded with who wrote it. Several callers have rebuilt a whole job file
|
|
12
|
+
* from the fields they know and silently dropped the rest, and the job keeps
|
|
13
|
+
* running, so the loss shows up days later as a missing channel or name.
|
|
14
|
+
* Never throws: this runs on the scheduler's write path, and a recording
|
|
15
|
+
* failure must not fail the write that triggered it.
|
|
16
|
+
*/
|
|
17
|
+
export declare function noteJobFrontmatterWrite(write: JobFileWrite): Promise<void>;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { droppedJobFrontmatterKeys } from "./frontmatter.js";
|
|
2
|
+
const CALLER_FRAMES = 6;
|
|
3
|
+
const SUMMARY_KEYS = 12;
|
|
4
|
+
// Only the function name and `file:line` of each frame: enough to tell
|
|
5
|
+
// `runCreate` from the scheduler without recording server directory layout.
|
|
6
|
+
function callerFrames() {
|
|
7
|
+
const frames = new Error().stack?.split("\n").slice(1) ?? [];
|
|
8
|
+
return frames
|
|
9
|
+
.map((frame) => frame.trim().replace(/^at\s+/, ""))
|
|
10
|
+
.filter((frame) => !frame.includes("frontmatter-loss") &&
|
|
11
|
+
!frame.includes("resources/store") &&
|
|
12
|
+
!frame.includes("node:internal"))
|
|
13
|
+
.slice(0, CALLER_FRAMES)
|
|
14
|
+
.map((frame) => frame.replace(/\(?[^\s()]*[\\/]([^\\/\s():]+:\d+):\d+\)?/, "($1)"));
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* A write that removes editor-owned frontmatter fields from a job file is
|
|
18
|
+
* recorded with who wrote it. Several callers have rebuilt a whole job file
|
|
19
|
+
* from the fields they know and silently dropped the rest, and the job keeps
|
|
20
|
+
* running, so the loss shows up days later as a missing channel or name.
|
|
21
|
+
* Never throws: this runs on the scheduler's write path, and a recording
|
|
22
|
+
* failure must not fail the write that triggered it.
|
|
23
|
+
*/
|
|
24
|
+
export async function noteJobFrontmatterWrite(write) {
|
|
25
|
+
try {
|
|
26
|
+
if (!write.path.startsWith("jobs/"))
|
|
27
|
+
return;
|
|
28
|
+
const dropped = droppedJobFrontmatterKeys(write.before, write.after);
|
|
29
|
+
if (dropped.length === 0)
|
|
30
|
+
return;
|
|
31
|
+
const callers = callerFrames();
|
|
32
|
+
console.warn(`[recurring-jobs] "${write.path}" lost frontmatter fields (${dropped.join(", ")}) in ${write.writer}`, callers);
|
|
33
|
+
const [{ recordOrgAdminAuditEvent }, requestContext] = await Promise.all([
|
|
34
|
+
import("../audit/org-admin.js"),
|
|
35
|
+
import("../server/request-context.js"),
|
|
36
|
+
]);
|
|
37
|
+
const run = requestContext.getRequestRunContext();
|
|
38
|
+
const userEmail = requestContext.getRequestUserEmail();
|
|
39
|
+
const listed = dropped.slice(0, SUMMARY_KEYS).join(", ");
|
|
40
|
+
await recordOrgAdminAuditEvent({
|
|
41
|
+
action: "job-fields-dropped",
|
|
42
|
+
targetType: "job-file",
|
|
43
|
+
targetId: write.path,
|
|
44
|
+
summary: `Job file ${write.path} lost ${dropped.length} frontmatter field(s): ${listed}${dropped.length > SUMMARY_KEYS ? ", ..." : ""}`,
|
|
45
|
+
userEmail,
|
|
46
|
+
orgId: write.orgId,
|
|
47
|
+
caller: run?.threadId ? "tool" : userEmail ? "http" : "automation",
|
|
48
|
+
threadId: run?.threadId,
|
|
49
|
+
runId: run?.runId,
|
|
50
|
+
args: {
|
|
51
|
+
path: write.path,
|
|
52
|
+
owner: write.owner,
|
|
53
|
+
writer: write.writer,
|
|
54
|
+
droppedKeys: dropped,
|
|
55
|
+
callers,
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
console.error(`[recurring-jobs] could not check or record lost fields for "${write.path}":`, error);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -95,6 +95,13 @@ export type JobExecutionFrontmatterPatch = {
|
|
|
95
95
|
remoteAutomationRunId?: string;
|
|
96
96
|
remoteAdvanceSchedule?: boolean;
|
|
97
97
|
};
|
|
98
|
+
export declare const JOB_EXECUTION_FRONTMATTER_KEYS: ReadonlySet<string>;
|
|
99
|
+
/**
|
|
100
|
+
* Frontmatter keys present in `before` and gone from `after`, ignoring the
|
|
101
|
+
* execution bookkeeping a run adds and clears. A job whose file loses its
|
|
102
|
+
* editor-owned fields still schedules and runs, so nothing else notices.
|
|
103
|
+
*/
|
|
104
|
+
export declare function droppedJobFrontmatterKeys(before: string, after: string): string[];
|
|
98
105
|
export type JobFrontmatterPatchValue = string | number | boolean | readonly string[] | undefined;
|
|
99
106
|
export type JobFrontmatterPatch = {
|
|
100
107
|
[key: string]: JobFrontmatterPatchValue;
|
package/dist/jobs/frontmatter.js
CHANGED
|
@@ -436,6 +436,45 @@ export function buildJobResourceContent(meta, body) {
|
|
|
436
436
|
lines.push("---", "", body);
|
|
437
437
|
return lines.join("\n");
|
|
438
438
|
}
|
|
439
|
+
export const JOB_EXECUTION_FRONTMATTER_KEYS = new Set([
|
|
440
|
+
"lastRun",
|
|
441
|
+
"lastCheck",
|
|
442
|
+
"lastStatus",
|
|
443
|
+
"lastError",
|
|
444
|
+
"lastErrorCode",
|
|
445
|
+
"consecutiveFailures",
|
|
446
|
+
"lastFailedEventId",
|
|
447
|
+
"pausedReason",
|
|
448
|
+
"pausedAt",
|
|
449
|
+
"nextRun",
|
|
450
|
+
"remoteRequestId",
|
|
451
|
+
"remoteCommandId",
|
|
452
|
+
"remoteRunId",
|
|
453
|
+
"remoteAutomationRunId",
|
|
454
|
+
"remoteAdvanceSchedule",
|
|
455
|
+
]);
|
|
456
|
+
const FRONTMATTER_KEY_RE = /^([A-Za-z][A-Za-z0-9_]*):/;
|
|
457
|
+
function frontmatterKeys(content) {
|
|
458
|
+
const keys = new Set();
|
|
459
|
+
const match = content.match(FRONTMATTER_RE);
|
|
460
|
+
if (!match)
|
|
461
|
+
return keys;
|
|
462
|
+
for (const line of match[1].split(/\r?\n/)) {
|
|
463
|
+
const key = FRONTMATTER_KEY_RE.exec(line)?.[1];
|
|
464
|
+
if (key)
|
|
465
|
+
keys.add(key);
|
|
466
|
+
}
|
|
467
|
+
return keys;
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Frontmatter keys present in `before` and gone from `after`, ignoring the
|
|
471
|
+
* execution bookkeeping a run adds and clears. A job whose file loses its
|
|
472
|
+
* editor-owned fields still schedules and runs, so nothing else notices.
|
|
473
|
+
*/
|
|
474
|
+
export function droppedJobFrontmatterKeys(before, after) {
|
|
475
|
+
const kept = frontmatterKeys(after);
|
|
476
|
+
return [...frontmatterKeys(before)].filter((key) => !kept.has(key) && !JOB_EXECUTION_FRONTMATTER_KEYS.has(key));
|
|
477
|
+
}
|
|
439
478
|
function jobFrontmatterBounds(content) {
|
|
440
479
|
const newline = content.startsWith("---\r\n")
|
|
441
480
|
? "\r\n"
|
package/dist/jobs/tools.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { DEFAULT_AUTOMATION_SCHEDULE } from "../automations/service.js";
|
|
2
2
|
import { getDbExec } from "../db/client.js";
|
|
3
3
|
import { resolveUserSchedulingTimezone } from "../localization/user-timezone.js";
|
|
4
|
-
import { resourcePut, resourceGetByPath, resourceList, resourceDelete, organizationIdFromResourceOwner, sharedResourceOwner, SHARED_OWNER, } from "../resources/store.js";
|
|
4
|
+
import { resourcePut, resourcePutIfAbsent, resourceGetByPath, resourceList, resourceDelete, organizationIdFromResourceOwner, sharedResourceOwner, SHARED_OWNER, } from "../resources/store.js";
|
|
5
5
|
import { getRequestUserEmail, getRequestOrgId, getIntegrationRequestContext, } from "../server/request-context.js";
|
|
6
6
|
import { isReasoningEffort, REASONING_EFFORTS, } from "../shared/reasoning-effort.js";
|
|
7
7
|
import { RESUME_AUTOMATION_PATCH } from "./automation-outcome.js";
|
|
@@ -150,7 +150,20 @@ async function runCreate(args, appId) {
|
|
|
150
150
|
...(mcpTools?.length ? { mcpTools } : {}),
|
|
151
151
|
};
|
|
152
152
|
const content = buildJobContent(meta, instructions);
|
|
153
|
-
await
|
|
153
|
+
const created = await resourcePutIfAbsent(owner, path, content);
|
|
154
|
+
if (!created) {
|
|
155
|
+
const existing = await resourceGetByPath(owner, path);
|
|
156
|
+
if (!existing) {
|
|
157
|
+
return JSON.stringify({
|
|
158
|
+
error: `Job "${name}" was not created: the write was not applied.`,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
return JSON.stringify({
|
|
162
|
+
error: classifyJobResource(existing.content).kind === "automation"
|
|
163
|
+
? `"${name}" already exists and is an automation. Use manage-automations to change it.`
|
|
164
|
+
: `Job "${name}" already exists. Use manage-jobs with action 'update' to change it, or pick a different name.`,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
154
167
|
return JSON.stringify({
|
|
155
168
|
created: true,
|
|
156
169
|
name,
|
|
@@ -386,7 +399,7 @@ export function createJobTools(appId) {
|
|
|
386
399
|
description: `Manage recurring jobs that run on a cron schedule.
|
|
387
400
|
|
|
388
401
|
Actions:
|
|
389
|
-
- "create": Create a new recurring job. Requires name and instructions; an omitted schedule defaults to once per hour.
|
|
402
|
+
- "create": Create a new recurring job. Requires name and instructions; an omitted schedule defaults to once per hour. Fails if a job with that name already exists; use "update" to change an existing job.
|
|
390
403
|
- "list": List all recurring jobs and their status (schedule, enabled, last run, next run).
|
|
391
404
|
- "update": Update a job's schedule, instructions, or enabled state. Requires name.
|
|
392
405
|
- "delete": Delete a recurring job. Requires name. Always confirm with the user first.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type BuiltinLocaleCode } from "./shared.js";
|
|
2
|
+
interface DefaultModelMessages {
|
|
3
|
+
configurationOverride: string;
|
|
4
|
+
configurationUnavailable: string;
|
|
5
|
+
organizationSelected: string;
|
|
6
|
+
userSelected: string;
|
|
7
|
+
appOverridePreserved: string;
|
|
8
|
+
}
|
|
9
|
+
export declare const DEFAULT_MODEL_MESSAGES: Record<BuiltinLocaleCode, DefaultModelMessages>;
|
|
10
|
+
export declare function defaultModelMessagesForUser(userEmail?: string | null): Promise<DefaultModelMessages>;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { getUserSetting } from "../settings/user-settings.js";
|
|
2
|
+
import { DEFAULT_LOCALE, LOCALIZATION_SETTING_KEY, normalizeLocalizationPreference, } from "./shared.js";
|
|
3
|
+
export const DEFAULT_MODEL_MESSAGES = {
|
|
4
|
+
"en-US": {
|
|
5
|
+
configurationOverride: "App configuration currently uses {{model}} via {{engine}}.",
|
|
6
|
+
configurationUnavailable: "App configuration currently has no usable model; it remains unchanged.",
|
|
7
|
+
organizationSelected: "Organization default set to {{model}} via {{engine}}. All app overrides and explicit chat and automation models are unchanged.",
|
|
8
|
+
userSelected: "Personal default set to {{model}} via {{engine}}. All app overrides and explicit chat and automation models are unchanged.",
|
|
9
|
+
appOverridePreserved: "{{appId}} currently uses {{model}} via {{engine}}. Its app override is preserved.",
|
|
10
|
+
},
|
|
11
|
+
"es-ES": {
|
|
12
|
+
configurationOverride: "La configuración de la aplicación usa actualmente {{model}} mediante {{engine}}.",
|
|
13
|
+
configurationUnavailable: "La configuración de la aplicación no tiene un modelo utilizable actualmente; no cambia.",
|
|
14
|
+
organizationSelected: "Modelo predeterminado de la organización establecido en {{model}} mediante {{engine}}. Todos los valores propios de las aplicaciones y los modelos explícitos de chats y automatizaciones no cambian.",
|
|
15
|
+
userSelected: "Modelo predeterminado personal establecido en {{model}} mediante {{engine}}. Todos los valores propios de las aplicaciones y los modelos explícitos de chats y automatizaciones no cambian.",
|
|
16
|
+
appOverridePreserved: "{{appId}} usa actualmente {{model}} mediante {{engine}}. Se conserva su valor propio de aplicación.",
|
|
17
|
+
},
|
|
18
|
+
"fr-FR": {
|
|
19
|
+
configurationOverride: "La configuration de l’application utilise actuellement {{model}} via {{engine}}.",
|
|
20
|
+
configurationUnavailable: "La configuration de l’application ne dispose actuellement d’aucun modèle utilisable ; elle reste inchangée.",
|
|
21
|
+
organizationSelected: "Modèle par défaut de l’organisation défini sur {{model}} via {{engine}}. Tous les réglages propres aux applications et les modèles explicites des conversations et automatisations restent inchangés.",
|
|
22
|
+
userSelected: "Modèle par défaut personnel défini sur {{model}} via {{engine}}. Tous les réglages propres aux applications et les modèles explicites des conversations et automatisations restent inchangés.",
|
|
23
|
+
appOverridePreserved: "{{appId}} utilise actuellement {{model}} via {{engine}}. Son réglage propre est conservé.",
|
|
24
|
+
},
|
|
25
|
+
"de-DE": {
|
|
26
|
+
configurationOverride: "Die App-Konfiguration verwendet derzeit {{model}} über {{engine}}.",
|
|
27
|
+
configurationUnavailable: "Die App-Konfiguration hat derzeit kein verwendbares Modell; sie bleibt unverändert.",
|
|
28
|
+
organizationSelected: "Organisationsstandard auf {{model}} über {{engine}} gesetzt. Alle App-Einstellungen und expliziten Chat- und Automationsmodelle bleiben unverändert.",
|
|
29
|
+
userSelected: "Persönlicher Standard auf {{model}} über {{engine}} gesetzt. Alle App-Einstellungen und expliziten Chat- und Automationsmodelle bleiben unverändert.",
|
|
30
|
+
appOverridePreserved: "{{appId}} verwendet derzeit {{model}} über {{engine}}. Die eigene App-Einstellung bleibt erhalten.",
|
|
31
|
+
},
|
|
32
|
+
"pt-BR": {
|
|
33
|
+
configurationOverride: "A configuração do aplicativo usa atualmente {{model}} via {{engine}}.",
|
|
34
|
+
configurationUnavailable: "A configuração do aplicativo não tem um modelo utilizável no momento; ela permanece inalterada.",
|
|
35
|
+
organizationSelected: "Modelo padrão da organização definido como {{model}} via {{engine}}. Todos os padrões específicos de aplicativos e modelos explícitos de chats e automações permanecem inalterados.",
|
|
36
|
+
userSelected: "Modelo padrão pessoal definido como {{model}} via {{engine}}. Todos os padrões específicos de aplicativos e modelos explícitos de chats e automações permanecem inalterados.",
|
|
37
|
+
appOverridePreserved: "{{appId}} usa atualmente {{model}} via {{engine}}. Seu padrão específico é preservado.",
|
|
38
|
+
},
|
|
39
|
+
"zh-CN": {
|
|
40
|
+
configurationOverride: "应用配置当前通过 {{engine}} 使用 {{model}}。",
|
|
41
|
+
configurationUnavailable: "应用配置当前没有可用模型;其设置保持不变。",
|
|
42
|
+
organizationSelected: "组织默认模型已通过 {{engine}} 设置为 {{model}}。所有应用的独立设置以及聊天和自动化中明确指定的模型保持不变。",
|
|
43
|
+
userSelected: "个人默认模型已通过 {{engine}} 设置为 {{model}}。所有应用的独立设置以及聊天和自动化中明确指定的模型保持不变。",
|
|
44
|
+
appOverridePreserved: "{{appId}} 当前通过 {{engine}} 使用 {{model}}。其应用独立设置已保留。",
|
|
45
|
+
},
|
|
46
|
+
"zh-TW": {
|
|
47
|
+
configurationOverride: "應用程式設定目前透過 {{engine}} 使用 {{model}}。",
|
|
48
|
+
configurationUnavailable: "應用程式設定目前沒有可用模型;其設定保持不變。",
|
|
49
|
+
organizationSelected: "組織預設模型已透過 {{engine}} 設為 {{model}}。所有應用程式的獨立設定以及聊天和自動化中明確指定的模型保持不變。",
|
|
50
|
+
userSelected: "個人預設模型已透過 {{engine}} 設為 {{model}}。所有應用程式的獨立設定以及聊天和自動化中明確指定的模型保持不變。",
|
|
51
|
+
appOverridePreserved: "{{appId}} 目前透過 {{engine}} 使用 {{model}}。其應用程式獨立設定已保留。",
|
|
52
|
+
},
|
|
53
|
+
"ja-JP": {
|
|
54
|
+
configurationOverride: "アプリの設定は現在 {{engine}} 経由で {{model}} を使用しています。",
|
|
55
|
+
configurationUnavailable: "アプリの設定には現在利用可能なモデルがありません。設定は変更されません。",
|
|
56
|
+
organizationSelected: "組織の既定モデルを {{engine}} 経由で {{model}} に設定しました。すべてのアプリ固有設定と、チャットや自動化で明示的に指定したモデルは変更されません。",
|
|
57
|
+
userSelected: "個人の既定モデルを {{engine}} 経由で {{model}} に設定しました。すべてのアプリ固有設定と、チャットや自動化で明示的に指定したモデルは変更されません。",
|
|
58
|
+
appOverridePreserved: "{{appId}} は現在 {{engine}} 経由で {{model}} を使用しています。アプリ固有設定は保持されます。",
|
|
59
|
+
},
|
|
60
|
+
"ko-KR": {
|
|
61
|
+
configurationOverride: "앱 설정은 현재 {{engine}}을 통해 {{model}}을 사용합니다.",
|
|
62
|
+
configurationUnavailable: "앱 설정에는 현재 사용 가능한 모델이 없습니다. 설정은 변경되지 않습니다.",
|
|
63
|
+
organizationSelected: "{{engine}}을 통해 조직 기본 모델을 {{model}}로 설정했습니다. 모든 앱별 설정과 채팅 및 자동화에서 명시적으로 지정한 모델은 변경되지 않습니다.",
|
|
64
|
+
userSelected: "{{engine}}을 통해 개인 기본 모델을 {{model}}로 설정했습니다. 모든 앱별 설정과 채팅 및 자동화에서 명시적으로 지정한 모델은 변경되지 않습니다.",
|
|
65
|
+
appOverridePreserved: "{{appId}}은(는) 현재 {{engine}}을 통해 {{model}}을 사용합니다. 앱별 설정은 유지됩니다.",
|
|
66
|
+
},
|
|
67
|
+
"hi-IN": {
|
|
68
|
+
configurationOverride: "ऐप का कॉन्फ़िगरेशन अभी {{engine}} के माध्यम से {{model}} इस्तेमाल करता है।",
|
|
69
|
+
configurationUnavailable: "ऐप के कॉन्फ़िगरेशन में अभी कोई उपयोग योग्य मॉडल नहीं है; वह नहीं बदला है।",
|
|
70
|
+
organizationSelected: "{{engine}} के माध्यम से संगठन का डिफ़ॉल्ट मॉडल {{model}} सेट किया गया। सभी ऐप के अपने सेटिंग और चैट तथा ऑटोमेशन के स्पष्ट मॉडल नहीं बदले हैं।",
|
|
71
|
+
userSelected: "{{engine}} के माध्यम से व्यक्तिगत डिफ़ॉल्ट मॉडल {{model}} सेट किया गया। सभी ऐप के अपने सेटिंग और चैट तथा ऑटोमेशन के स्पष्ट मॉडल नहीं बदले हैं।",
|
|
72
|
+
appOverridePreserved: "{{appId}} अभी {{engine}} के माध्यम से {{model}} इस्तेमाल करता है। उसकी अपनी ऐप सेटिंग सुरक्षित है।",
|
|
73
|
+
},
|
|
74
|
+
"ar-SA": {
|
|
75
|
+
configurationOverride: "تستخدم إعدادات التطبيق حاليًا {{model}} عبر {{engine}}.",
|
|
76
|
+
configurationUnavailable: "لا تحتوي إعدادات التطبيق حاليًا على نموذج قابل للاستخدام؛ وهي لم تتغير.",
|
|
77
|
+
organizationSelected: "تم تعيين النموذج الافتراضي للمؤسسة إلى {{model}} عبر {{engine}}. لم تتغير أي إعدادات خاصة بالتطبيقات ولا النماذج المحددة صراحةً للمحادثات والأتمتة.",
|
|
78
|
+
userSelected: "تم تعيين النموذج الافتراضي الشخصي إلى {{model}} عبر {{engine}}. لم تتغير أي إعدادات خاصة بالتطبيقات ولا النماذج المحددة صراحةً للمحادثات والأتمتة.",
|
|
79
|
+
appOverridePreserved: "يستخدم {{appId}} حاليًا {{model}} عبر {{engine}}. تم الحفاظ على إعداده الخاص بالتطبيق.",
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
export async function defaultModelMessagesForUser(userEmail) {
|
|
83
|
+
const preference = userEmail
|
|
84
|
+
? normalizeLocalizationPreference(await getUserSetting(userEmail, LOCALIZATION_SETTING_KEY))
|
|
85
|
+
: { locale: DEFAULT_LOCALE };
|
|
86
|
+
return (DEFAULT_MODEL_MESSAGES[preference.locale] ??
|
|
87
|
+
DEFAULT_MODEL_MESSAGES[DEFAULT_LOCALE]);
|
|
88
|
+
}
|
|
@@ -27,19 +27,19 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
|
|
|
27
27
|
thumbsUpRate: number;
|
|
28
28
|
avgEvalScore: number;
|
|
29
29
|
} | {
|
|
30
|
+
id?: undefined;
|
|
30
31
|
ok?: undefined;
|
|
31
32
|
error: string;
|
|
32
33
|
message: string;
|
|
33
34
|
summary?: undefined;
|
|
34
35
|
spans?: undefined;
|
|
35
|
-
id?: undefined;
|
|
36
36
|
} | {
|
|
37
|
+
id?: undefined;
|
|
37
38
|
error?: undefined;
|
|
38
39
|
ok?: undefined;
|
|
39
40
|
message?: undefined;
|
|
40
41
|
summary: import("./types.js").TraceSummary;
|
|
41
42
|
spans: import("./types.js").TraceSpan[];
|
|
42
|
-
id?: undefined;
|
|
43
43
|
} | {
|
|
44
44
|
error?: undefined;
|
|
45
45
|
ok?: undefined;
|
|
@@ -48,17 +48,17 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
|
|
|
48
48
|
spans?: undefined;
|
|
49
49
|
id: string;
|
|
50
50
|
} | {
|
|
51
|
+
id?: undefined;
|
|
51
52
|
ok?: undefined;
|
|
52
53
|
message?: undefined;
|
|
53
54
|
summary?: undefined;
|
|
54
55
|
spans?: undefined;
|
|
55
|
-
id?: undefined;
|
|
56
56
|
error: any;
|
|
57
57
|
} | {
|
|
58
|
+
id?: undefined;
|
|
58
59
|
error?: undefined;
|
|
59
60
|
message?: undefined;
|
|
60
61
|
summary?: undefined;
|
|
61
62
|
spans?: undefined;
|
|
62
|
-
id?: undefined;
|
|
63
63
|
ok: boolean;
|
|
64
64
|
}>>;
|
package/dist/org/handlers.d.ts
CHANGED
|
@@ -71,10 +71,10 @@ export declare const setWorkspaceAppDefaultVisibilityHandler: import("h3").Event
|
|
|
71
71
|
visibility: WorkspaceAppDefaultVisibility;
|
|
72
72
|
}>>;
|
|
73
73
|
export declare const createOrgHandler: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
74
|
-
name?: undefined;
|
|
75
74
|
success: boolean;
|
|
76
|
-
role?: undefined;
|
|
77
75
|
id?: undefined;
|
|
76
|
+
name?: undefined;
|
|
77
|
+
role?: undefined;
|
|
78
78
|
} | {
|
|
79
79
|
success?: undefined;
|
|
80
80
|
id: string;
|
package/dist/org/index.d.ts
CHANGED
|
@@ -179,8 +179,8 @@ export declare const getMyOrgHandler: import("h3").EventHandlerWithFetch<import(
|
|
|
179
179
|
export declare const createOrgHandler: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
180
180
|
name?: undefined;
|
|
181
181
|
success: boolean;
|
|
182
|
-
id?: undefined;
|
|
183
182
|
role?: undefined;
|
|
183
|
+
id?: undefined;
|
|
184
184
|
} | {
|
|
185
185
|
success?: undefined;
|
|
186
186
|
id: string;
|
|
@@ -94,39 +94,39 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
|
|
|
94
94
|
label?: undefined;
|
|
95
95
|
} | {
|
|
96
96
|
id?: undefined;
|
|
97
|
-
count?: undefined;
|
|
98
97
|
message?: undefined;
|
|
99
98
|
deleted?: undefined;
|
|
100
99
|
providers?: undefined;
|
|
100
|
+
count?: undefined;
|
|
101
101
|
found: boolean;
|
|
102
102
|
provider: import("../custom-registry.js").CustomProviderConfig;
|
|
103
103
|
registered?: undefined;
|
|
104
104
|
label?: undefined;
|
|
105
105
|
} | {
|
|
106
|
-
count?: undefined;
|
|
107
106
|
message?: undefined;
|
|
108
107
|
deleted?: undefined;
|
|
109
108
|
providers?: undefined;
|
|
109
|
+
count?: undefined;
|
|
110
110
|
provider?: undefined;
|
|
111
111
|
found: boolean;
|
|
112
112
|
id: string;
|
|
113
113
|
registered?: undefined;
|
|
114
114
|
label?: undefined;
|
|
115
115
|
} | {
|
|
116
|
-
count?: undefined;
|
|
117
116
|
message?: undefined;
|
|
118
117
|
found?: undefined;
|
|
119
118
|
providers?: undefined;
|
|
119
|
+
count?: undefined;
|
|
120
120
|
provider?: undefined;
|
|
121
121
|
deleted: boolean;
|
|
122
122
|
id: string;
|
|
123
123
|
registered?: undefined;
|
|
124
124
|
label?: undefined;
|
|
125
125
|
} | {
|
|
126
|
-
count?: undefined;
|
|
127
126
|
deleted?: undefined;
|
|
128
127
|
found?: undefined;
|
|
129
128
|
providers?: undefined;
|
|
129
|
+
count?: undefined;
|
|
130
130
|
provider?: undefined;
|
|
131
131
|
registered: boolean;
|
|
132
132
|
id: string;
|