@sema-agent/core
Version:
Stateless, task-oriented AI agent core
123 lines (122 loc) • 5.78 kB
JavaScript
import { RETAIN_DEFAULT_TTL_MS, RETAIN_DEFAULT_MAX, SESSION_DEFAULT_TTL_DAYS } from "./defaults.js";
export const CONFIG_CATALOG_VERSION = "sema-config@1";
export function describeConfigCatalog() {
const knobs = [
{
key: "limits.maxTurns",
recommended: null,
unit: "turns",
kind: "budget",
summary: "Per-slice turn ceiling. Unset = unbounded (design/164 retired the engine-side net): a task that needs a turn bound states one. 0 = the explicit unbounded sentinel.",
precedence: "spec.limits.maxTurns > unset",
},
{
key: "limits.maxWalltimeMs",
recommended: null,
unit: "ms",
kind: "budget",
summary: "Per-slice ACTIVE wall-clock window for the task (suspended time does not count; a resume opens a fresh window). Unset = unbounded, which is the recommendation — bound work by tokens, not by time.",
precedence: "spec.limits.maxWalltimeMs > unset",
},
{
key: "limits.maxOutputTokens",
recommended: null,
unit: "tokens",
kind: "budget",
summary: "Per-REQUEST output cap (provider max_tokens). Unset = model default. Distinct from the cumulative budget maxTokens.",
precedence: "spec.limits.maxOutputTokens > model.maxTokens > provider default",
},
{
key: "limits.maxCostUsd",
recommended: null,
unit: "usd",
kind: "budget",
summary: "Per-slice spend ceiling (hard-cancels the in-flight stream by default when crossed). Unset = unbounded.",
precedence: "spec.limits.maxCostUsd > unset",
},
{
key: "limits.maxTokens",
recommended: null,
unit: "tokens",
kind: "budget",
summary: "Per-slice token budget — the PRIMARY axis. Unset = unbounded.",
precedence: "spec.limits.maxTokens > unset",
},
{
key: "limits.approachNotice",
recommended: null,
kind: "behavior",
summary: "Advisory limit-approach reminders (one at ~80% of the tightest armed axis, one at ~95%). Unset = ON whenever an axis is armed; false = off; { at } overrides the thresholds.",
precedence: "spec.limits.approachNotice > on-when-any-axis-armed",
},
{
key: "budget.totalTokens",
recommended: null,
unit: "tokens",
kind: "budget",
summary: "Cross-slice token allocation for resource-suspend tasks. First slice only; later slices read the frozen ledger. Unset = per-slice windows only.",
precedence: "spec.resourceSuspend.totalTokens (first slice) > unset",
},
{
key: "delegation.retainTtlMs",
recommended: RETAIN_DEFAULT_TTL_MS,
unit: "ms",
kind: "retention",
summary: "Retained child session TTL (settled-at based) within the parent run's retain ledger.",
precedence: "TaskSpec.retainSubagentSessions.ttlMs > default",
},
{
key: "delegation.retainMax",
recommended: RETAIN_DEFAULT_MAX,
unit: "count",
kind: "retention",
summary: "Max retained children per parent run (oldest settled idle evicted beyond it).",
precedence: "TaskSpec.retainSubagentSessions.max > default",
},
{
key: "session.idleTtlDays",
recommended: SESSION_DEFAULT_TTL_DAYS,
unit: "days",
kind: "retention",
summary: "Idle TTL before a cached session is evicted from the session store.",
precedence: "TtlSessionStoreOptions.defaultTtlDays > default",
},
];
return { version: CONFIG_CATALOG_VERSION, knobs };
}
export const MAX_OVERRIDE_DECLARATIONS = 32;
const MAX_DECLARATION_CHARS = 200;
const sanitizeDeclarationText = (s) => s.replace(/[\u0000-\u0008\u000b-\u001f\u007f]/g, "").slice(0, MAX_DECLARATION_CHARS);
export function resolveEffectiveConfig(spec, ctx) {
const l = spec.limits;
const pick = (key, specVal, dflt) => specVal !== undefined ? { key, value: specVal, provenance: "spec" } : { key, value: dflt, provenance: "default" };
const maxOutputTokens = l?.maxOutputTokens !== undefined
? { key: "limits.maxOutputTokens", value: l.maxOutputTokens, provenance: "spec" }
: ctx?.modelMaxTokens !== undefined
? { key: "limits.maxOutputTokens", value: ctx.modelMaxTokens, provenance: "derived" }
: { key: "limits.maxOutputTokens", value: null, provenance: "default" };
const fields = [
pick("limits.maxTurns", l?.maxTurns, null),
pick("limits.maxWalltimeMs", l?.maxWalltimeMs, null),
maxOutputTokens,
pick("limits.maxCostUsd", l?.maxCostUsd, null),
pick("limits.maxTokens", l?.maxTokens, null),
pick("budget.totalTokens", spec.resourceSuspend?.totalTokens, null),
pick("budget.totalBudgetUsd", spec.resourceSuspend?.totalBudgetUsd, null),
];
for (const d of (spec.configOverrides ?? []).slice(0, MAX_OVERRIDE_DECLARATIONS)) {
fields.push({
key: sanitizeDeclarationText(d.key),
value: typeof d.value === "string" ? sanitizeDeclarationText(d.value) : d.value,
provenance: "host-declared",
});
}
return fields;
}
export function declarationReasons(declarations) {
const reasons = {};
for (const d of (declarations ?? []).slice(0, MAX_OVERRIDE_DECLARATIONS)) {
reasons[sanitizeDeclarationText(d.key)] = sanitizeDeclarationText(d.reason);
}
return reasons;
}