@sema-agent/server 1.220.0 → 1.222.0
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/budget.d.ts +2 -0
- package/dist/budget.d.ts.map +1 -1
- package/dist/budget.js +1 -1
- package/dist/budget.js.map +1 -1
- package/dist/capabilities/center-prompts.d.ts +16 -0
- package/dist/capabilities/center-prompts.d.ts.map +1 -0
- package/dist/capabilities/center-prompts.js +64 -0
- package/dist/capabilities/center-prompts.js.map +1 -0
- package/dist/capabilities/scenarios.d.ts +0 -1
- package/dist/capabilities/scenarios.d.ts.map +1 -1
- package/dist/capabilities/scenarios.js +1 -10
- package/dist/capabilities/scenarios.js.map +1 -1
- package/dist/config-provider.d.ts +2 -0
- package/dist/config-provider.d.ts.map +1 -1
- package/dist/config-provider.js +11 -1
- package/dist/config-provider.js.map +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/http/server.d.ts +2 -0
- package/dist/http/server.d.ts.map +1 -1
- package/dist/http/server.js +7 -0
- package/dist/http/server.js.map +1 -1
- package/dist/main.js +137 -59
- package/dist/main.js.map +1 -1
- package/dist/sema-registry.d.ts +1 -2
- package/dist/sema-registry.d.ts.map +1 -1
- package/dist/sema-registry.js +3 -1
- package/dist/sema-registry.js.map +1 -1
- package/dist/spec-fields.d.ts +1 -0
- package/dist/spec-fields.d.ts.map +1 -1
- package/dist/spec-fields.js +6 -0
- package/dist/spec-fields.js.map +1 -1
- package/package.json +3 -3
package/dist/main.js
CHANGED
|
@@ -18,7 +18,7 @@ import { withLedgerRecording } from "./plugins/send-file-ledger.js";
|
|
|
18
18
|
import { basename, resolve } from "node:path";
|
|
19
19
|
import { stat as fsStat, readFile as fsReadFile } from "node:fs/promises";
|
|
20
20
|
import { buildEnvFacts, egressForRemoteExec, ensureScratchpadDir, purgeScratchpadDir, sweepStaleScratchpads } from "./env-facts.js";
|
|
21
|
-
import { normalizeSuggestNextPrompts, normalizeResilience, normalizeAttachments, normalizeResumeAtMode, resolveTaskLimits, taskAgentsSpecFragment, retainBackgroundProcessesFromBody } from "./spec-fields.js";
|
|
21
|
+
import { normalizeSuggestNextPrompts, normalizeResilience, normalizeAttachments, normalizeResumeAtMode, resolveTaskLimits, taskAgentsSpecFragment, retainBackgroundProcessesFromBody, toolNameListFromBody } from "./spec-fields.js";
|
|
22
22
|
import { createBrain, brainSummary } from "./brain.js";
|
|
23
23
|
import { loadConfig, logConfigDiagnostics } from "./config.js";
|
|
24
24
|
import { resourceSuspendOptIn } from "./resource-suspend.js";
|
|
@@ -69,7 +69,8 @@ import { QuestionCoordinator } from "./question.js";
|
|
|
69
69
|
import { ToolApprovalCoordinator } from "./tool-approval.js";
|
|
70
70
|
import { applyEffective, mutateInPlace, logEffectiveDiff, applyCenterSkills, resolveMcpServers, mcpForScenario, restartReasons } from "./sema-registry.js";
|
|
71
71
|
import { ensureSealedKeyStore, reportExecutionPublicKey } from "./sealed-key.js";
|
|
72
|
-
import { createConfigProvider } from "./config-provider.js";
|
|
72
|
+
import { createConfigProvider, raceBootFetch, BOOT_FETCH_DEFERRED } from "./config-provider.js";
|
|
73
|
+
import { validateCenterPrompts, centerPromptProvider } from "./capabilities/center-prompts.js";
|
|
73
74
|
import { createPrincipalCapsClient, gateExecutionLane, scopedTokenNeedsWorker, applyObserverEnvOptIn } from "./runtime-caps-resolver.js";
|
|
74
75
|
import { applyRuntimeGovernance, stripDelegationTools } from "./runtime-governance.js";
|
|
75
76
|
import { parseTaskSettings, applyTaskSettings, coercePermissionMode, withPermissionMode, effectiveThinking } from "./task-settings.js";
|
|
@@ -154,12 +155,45 @@ async function main() {
|
|
|
154
155
|
logger.info("model_roster_landed", { note: "ready gate open — billable submissions accepted" });
|
|
155
156
|
}
|
|
156
157
|
};
|
|
158
|
+
let centerPrompts;
|
|
159
|
+
const adoptCenterPrompts = (eff, phase) => {
|
|
160
|
+
const raw = eff?.prompts;
|
|
161
|
+
if (raw === undefined) {
|
|
162
|
+
if (centerPrompts)
|
|
163
|
+
logger.info("center_prompts_cleared", { phase, note: "effective carries no prompts key (publish gate off) — built-in providers resume" });
|
|
164
|
+
centerPrompts = undefined;
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
const v = validateCenterPrompts(raw);
|
|
168
|
+
if (!v.ok) {
|
|
169
|
+
logger.warn("center_prompts_invalid", { phase, error: v.error, kept: centerPrompts?.packId ?? "(builtin)", note: "malformed prompts face rejected (core assemble would THROW per task) — keeping the previous pack/built-ins; fix the center payload" });
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
if (centerPrompts?.contentDigest !== v.value.contentDigest) {
|
|
173
|
+
logger.info("center_prompts_adopted", { phase, packId: v.value.packId, contentDigest: v.value.contentDigest, sections: v.value.sections.length, scenarioOverrides: Object.keys(v.value.scenarioOverrides ?? {}).length, note: "new tasks assemble the new pack (epoch re-pins at the next task boundary); in-flight sessions stay pinned" });
|
|
174
|
+
}
|
|
175
|
+
centerPrompts = v.value;
|
|
176
|
+
};
|
|
177
|
+
let bootConfigPending;
|
|
157
178
|
if (configProvider) {
|
|
158
179
|
const dryRun = cc?.dryRun ?? false;
|
|
159
180
|
try {
|
|
160
|
-
const
|
|
181
|
+
const bootFetch = configProvider.fetchEffective(undefined);
|
|
182
|
+
const raced = await raceBootFetch(bootFetch, config.configBootFetchBudgetMs);
|
|
183
|
+
if (raced === BOOT_FETCH_DEFERRED) {
|
|
184
|
+
bootConfigPending = bootFetch;
|
|
185
|
+
bootFetch.catch(() => { });
|
|
186
|
+
logger.warn("config_boot_fetch_deferred", {
|
|
187
|
+
source: configProvider.kind,
|
|
188
|
+
budgetMs: config.configBootFetchBudgetMs,
|
|
189
|
+
note: "center slow/unreachable — serving on the env fallback now; the pull continues in the background and hot-applies on arrival (roster gate opens then). Raise CONFIG_BOOT_FETCH_BUDGET_MS to block boot instead.",
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
const r = raced === BOOT_FETCH_DEFERRED ? null : raced;
|
|
161
193
|
if (r) {
|
|
162
|
-
const
|
|
194
|
+
const bootPromptsRaw = r.effective.prompts;
|
|
195
|
+
const bootPromptsOk = bootPromptsRaw === undefined || validateCenterPrompts(bootPromptsRaw).ok;
|
|
196
|
+
const bootClean = (r.domainErrors ?? []).length === 0 && bootPromptsOk;
|
|
163
197
|
if (bootClean)
|
|
164
198
|
ccEtag = r.etag;
|
|
165
199
|
for (const de of r.domainErrors ?? [])
|
|
@@ -169,6 +203,7 @@ async function main() {
|
|
|
169
203
|
}
|
|
170
204
|
else {
|
|
171
205
|
applyEffective(config, r.effective, logger, { sealedKeys });
|
|
206
|
+
adoptCenterPrompts(r.effective, "boot");
|
|
172
207
|
markRosterLanded(r.effective);
|
|
173
208
|
effective = r.effective;
|
|
174
209
|
if (bootClean)
|
|
@@ -1042,67 +1077,108 @@ async function main() {
|
|
|
1042
1077
|
if (configProvider) {
|
|
1043
1078
|
const ccRef = config.configCenter;
|
|
1044
1079
|
let refreshInFlight = false;
|
|
1045
|
-
const
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1080
|
+
const refreshTick = async () => {
|
|
1081
|
+
if (refreshInFlight)
|
|
1082
|
+
return;
|
|
1083
|
+
refreshInFlight = true;
|
|
1084
|
+
try {
|
|
1085
|
+
const r = await configProvider.fetchEffective(ccEtag);
|
|
1086
|
+
if (r) {
|
|
1087
|
+
const promptsRaw = r.effective.prompts;
|
|
1088
|
+
const promptsGate = promptsRaw !== undefined ? validateCenterPrompts(promptsRaw) : { ok: true };
|
|
1089
|
+
const badDomains = [
|
|
1090
|
+
...new Set([...(r.domainErrors ?? []).map((de) => de.domain), ...(promptsGate.ok ? [] : ["prompts"])]),
|
|
1091
|
+
]
|
|
1092
|
+
.sort()
|
|
1093
|
+
.join(",");
|
|
1094
|
+
if (badDomains) {
|
|
1095
|
+
if (lastRejectedCandidate?.version !== r.effective.version || lastRejectedCandidate.domains !== badDomains) {
|
|
1096
|
+
lastRejectedCandidate = { version: r.effective.version, domains: badDomains, at: Date.now() };
|
|
1097
|
+
logger.warn("config_candidate_rejected", {
|
|
1098
|
+
version: r.effective.version,
|
|
1099
|
+
domains: badDomains,
|
|
1100
|
+
errors: [
|
|
1101
|
+
...(r.domainErrors ?? []).map((de) => ({ domain: de.domain, error: de.error })),
|
|
1102
|
+
...(promptsGate.ok ? [] : [{ domain: "prompts", error: promptsGate.error }]),
|
|
1103
|
+
],
|
|
1104
|
+
lkgVersion: latestEffective?.version ?? "(env)",
|
|
1105
|
+
note: "candidate rejected whole (§9.5-5) — live config keeps the last-known-good; fix the named files to unblock",
|
|
1106
|
+
});
|
|
1066
1107
|
}
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1108
|
+
return;
|
|
1109
|
+
}
|
|
1110
|
+
lastRejectedCandidate = undefined;
|
|
1111
|
+
if (ccRef?.dryRun) {
|
|
1112
|
+
logEffectiveDiff(config, r.effective, logger);
|
|
1113
|
+
ccEtag = r.etag;
|
|
1114
|
+
}
|
|
1115
|
+
else {
|
|
1116
|
+
applyEffective(config, r.effective, logger, { teamsOnly: true, sealedKeys });
|
|
1117
|
+
markRosterLanded(r.effective);
|
|
1118
|
+
mutateInPlace(pricing, buildPricing(config.models));
|
|
1119
|
+
keyResolver = buildKeyResolver(config.modelApiKeyEnv, process.env, config.modelApiKeys);
|
|
1120
|
+
const reasons = restartReasons(effective, r.effective);
|
|
1121
|
+
if (reasons.length === 0) {
|
|
1122
|
+
pendingRestart = undefined;
|
|
1123
|
+
}
|
|
1124
|
+
else if (!pendingRestart || pendingRestart.reasons.join(",") !== reasons.join(",")) {
|
|
1125
|
+
pendingRestart = { restartRequired: true, reasons, version: r.effective.version, since: Date.now() };
|
|
1071
1126
|
}
|
|
1072
1127
|
else {
|
|
1073
|
-
|
|
1074
|
-
markRosterLanded(r.effective);
|
|
1075
|
-
mutateInPlace(pricing, buildPricing(config.models));
|
|
1076
|
-
keyResolver = buildKeyResolver(config.modelApiKeyEnv, process.env, config.modelApiKeys);
|
|
1077
|
-
const reasons = restartReasons(effective, r.effective);
|
|
1078
|
-
if (reasons.length === 0) {
|
|
1079
|
-
pendingRestart = undefined;
|
|
1080
|
-
}
|
|
1081
|
-
else if (!pendingRestart || pendingRestart.reasons.join(",") !== reasons.join(",")) {
|
|
1082
|
-
pendingRestart = { restartRequired: true, reasons, version: r.effective.version, since: Date.now() };
|
|
1083
|
-
}
|
|
1084
|
-
else {
|
|
1085
|
-
pendingRestart = { ...pendingRestart, version: r.effective.version };
|
|
1086
|
-
}
|
|
1087
|
-
logger.info("sema_registry_refreshed", {
|
|
1088
|
-
version: r.effective.version,
|
|
1089
|
-
note: "teams + runtime governance + models/roles/roster hot-applied; skills/mcp/runtime-gates/scenarios are restart-to-apply",
|
|
1090
|
-
...(pendingRestart ? { restartRequired: true, restartReasons: pendingRestart.reasons } : {}),
|
|
1091
|
-
});
|
|
1092
|
-
latestEffective = r.effective;
|
|
1093
|
-
ccEtag = r.etag;
|
|
1128
|
+
pendingRestart = { ...pendingRestart, version: r.effective.version };
|
|
1094
1129
|
}
|
|
1130
|
+
logger.info("sema_registry_refreshed", {
|
|
1131
|
+
version: r.effective.version,
|
|
1132
|
+
note: "teams + runtime governance + models/roles/roster hot-applied; skills/mcp/runtime-gates/scenarios are restart-to-apply",
|
|
1133
|
+
...(pendingRestart ? { restartRequired: true, restartReasons: pendingRestart.reasons } : {}),
|
|
1134
|
+
});
|
|
1135
|
+
latestEffective = r.effective;
|
|
1136
|
+
ccEtag = r.etag;
|
|
1095
1137
|
}
|
|
1096
1138
|
}
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
}
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
}
|
|
1104
|
-
}
|
|
1139
|
+
}
|
|
1140
|
+
catch (err) {
|
|
1141
|
+
logger.warn("sema_registry_refresh_failed", { err: String(err), note: "etag NOT advanced — the same candidate is re-fetched and re-applied next poll" });
|
|
1142
|
+
}
|
|
1143
|
+
finally {
|
|
1144
|
+
refreshInFlight = false;
|
|
1145
|
+
}
|
|
1146
|
+
};
|
|
1147
|
+
const ccTimer = setInterval(() => void refreshTick(), 60_000);
|
|
1105
1148
|
ccTimer.unref?.();
|
|
1149
|
+
const deferredBootApply = (r) => {
|
|
1150
|
+
if (!r)
|
|
1151
|
+
return;
|
|
1152
|
+
const promptsRaw = r.effective.prompts;
|
|
1153
|
+
const promptsGate = promptsRaw !== undefined ? validateCenterPrompts(promptsRaw) : { ok: true };
|
|
1154
|
+
if ((r.domainErrors ?? []).length > 0 || !promptsGate.ok) {
|
|
1155
|
+
logger.warn("config_boot_deferred_candidate_rejected", {
|
|
1156
|
+
version: r.effective.version,
|
|
1157
|
+
errors: [...(r.domainErrors ?? []).map((de) => ({ domain: de.domain, error: de.error })), ...(promptsGate.ok ? [] : [{ domain: "prompts", error: promptsGate.error }])],
|
|
1158
|
+
note: "late boot pull carried an invalid candidate — env fallback keeps serving; the refresh cadence re-judges the same tree (etag not advanced)",
|
|
1159
|
+
});
|
|
1160
|
+
return;
|
|
1161
|
+
}
|
|
1162
|
+
if (ccRef?.dryRun) {
|
|
1163
|
+
logEffectiveDiff(config, r.effective, logger);
|
|
1164
|
+
ccEtag = r.etag;
|
|
1165
|
+
return;
|
|
1166
|
+
}
|
|
1167
|
+
applyEffective(config, r.effective, logger, { teamsOnly: true, sealedKeys });
|
|
1168
|
+
adoptCenterPrompts(r.effective, "boot-deferred");
|
|
1169
|
+
markRosterLanded(r.effective);
|
|
1170
|
+
mutateInPlace(pricing, buildPricing(config.models));
|
|
1171
|
+
keyResolver = buildKeyResolver(config.modelApiKeyEnv, process.env, config.modelApiKeys);
|
|
1172
|
+
effective = r.effective;
|
|
1173
|
+
latestEffective = r.effective;
|
|
1174
|
+
ccEtag = r.etag;
|
|
1175
|
+
const reasons = restartReasons(undefined, r.effective).filter((sl) => sl !== "prompts");
|
|
1176
|
+
if (reasons.length > 0)
|
|
1177
|
+
pendingRestart = { restartRequired: true, reasons, version: r.effective.version, since: Date.now() };
|
|
1178
|
+
logger.info("config_loaded_deferred", { source: configProvider.kind, version: r.effective.version, models: (r.effective.models?.models ?? []).filter((m) => m.enabled !== false).length, ...(reasons.length > 0 ? { restartRequired: true, restartReasons: reasons } : {}) });
|
|
1179
|
+
};
|
|
1180
|
+
if (bootConfigPending)
|
|
1181
|
+
void bootConfigPending.then((r) => deferredBootApply(r), () => { });
|
|
1106
1182
|
}
|
|
1107
1183
|
let keyResolver = buildKeyResolver(config.modelApiKeyEnv, process.env, config.modelApiKeys);
|
|
1108
1184
|
const mysqlPool = backend?.mysqlPool();
|
|
@@ -1397,6 +1473,8 @@ async function main() {
|
|
|
1397
1473
|
...(retainBackgroundProcessesFromBody(body.retainBackgroundProcesses, config.requirePrincipal, (event, fields) => logger.warn(event, { ...fields, sessionId: auth?.sessionId ?? null })) === true
|
|
1398
1474
|
? { retainBackgroundProcesses: true }
|
|
1399
1475
|
: {}),
|
|
1476
|
+
...(() => { const v = toolNameListFromBody(body.excludeTools); return v ? { excludeTools: v } : {}; })(),
|
|
1477
|
+
...(() => { const v = toolNameListFromBody(body.deferTools); return v ? { deferTools: v } : {}; })(),
|
|
1400
1478
|
...(typeof body.interactiveTools === "boolean"
|
|
1401
1479
|
? { interactiveTools: body.interactiveTools }
|
|
1402
1480
|
: {}),
|
|
@@ -1432,7 +1510,7 @@ async function main() {
|
|
|
1432
1510
|
})(cap.tools),
|
|
1433
1511
|
skills: mergeUserSkills(cap.skills, body.skills, logger),
|
|
1434
1512
|
mcp: resolveRequestMcp(mcpForScenario(config.mcpServers, scenarioName), body.mcpServers, config, logger),
|
|
1435
|
-
promptProvider: cap.promptProvider,
|
|
1513
|
+
promptProvider: centerPrompts ? centerPromptProvider(centerPrompts, scenarioName) : cap.promptProvider,
|
|
1436
1514
|
toolPolicy: durableEnabled
|
|
1437
1515
|
? combinePolicies(createDurableQuestionPolicy(), createDurableAskPolicy({
|
|
1438
1516
|
requireApproval: config.approvalRequire, deny: config.approvalDeny, autoBudget: config.approvalAutoBudget, neverAuto: config.approvalNeverAuto,
|