@sema-agent/server 1.313.0 → 1.315.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/approval-hmac.d.ts +17 -0
- package/dist/approval-hmac.js +27 -0
- package/dist/auth-bridge.d.ts +3 -2
- package/dist/bench/s1/live-deps.js +2 -2
- package/dist/budget.d.ts +4 -76
- package/dist/budget.js +4 -83
- package/dist/capabilities/sandbox-file-send.d.ts +2 -1
- package/dist/config-center/apply-effective.d.ts +22 -0
- package/dist/config-center/apply-effective.js +283 -0
- package/dist/config-center/http-client.d.ts +23 -0
- package/dist/config-center/http-client.js +109 -0
- package/dist/config-center/restart-signal.d.ts +12 -0
- package/dist/config-center/restart-signal.js +70 -0
- package/dist/config-center/skills-mcp.d.ts +13 -0
- package/dist/config-center/skills-mcp.js +113 -0
- package/dist/config-center/types.d.ts +143 -0
- package/dist/config-center/types.js +2 -0
- package/dist/config-types.d.ts +1 -1
- package/dist/elicitation.d.ts +5 -3
- package/dist/elicitation.js +3 -2
- package/dist/fleet/fleet-bus.js +92 -83
- package/dist/fleet-lease.d.ts +1 -1
- package/dist/fleet-lease.js +1 -1
- package/dist/hooks/hook-llm.d.ts +5 -4
- package/dist/hooks/hook-runner.d.ts +1 -1
- package/dist/hooks/hook-runner.js +19 -10
- package/dist/http/server.d.ts +95 -69
- package/dist/http/server.js +75 -86
- package/dist/index.d.ts +1 -1
- package/dist/key-resolver.d.ts +1 -1
- package/dist/key-resolver.js +1 -1
- package/dist/leader/grader-env-factory.d.ts +1 -1
- package/dist/leader/grader-env-factory.js +2 -2
- package/dist/leader/leader.js +5 -2
- package/dist/leader/wire.d.ts +1 -1
- package/dist/leader/wire.js +170 -166
- package/dist/main.js +487 -476
- package/dist/memory-scope.d.ts +18 -0
- package/dist/memory-scope.js +38 -0
- package/dist/observability/cost-taxonomy.d.ts +34 -0
- package/dist/observability/cost-taxonomy.js +26 -0
- package/dist/observability/prompt-manifest.d.ts +75 -0
- package/dist/observability/prompt-manifest.js +82 -0
- package/dist/plugins/checkpoint-store-sql.d.ts +67 -0
- package/dist/plugins/checkpoint-store-sql.js +224 -0
- package/dist/plugins/image-bake-store-sql.d.ts +53 -0
- package/dist/plugins/image-bake-store-sql.js +463 -0
- package/dist/plugins/k8s-bg-scripts.d.ts +19 -0
- package/dist/plugins/k8s-bg-scripts.js +129 -0
- package/dist/plugins/k8s-exec-protocol.d.ts +20 -0
- package/dist/plugins/k8s-exec-protocol.js +87 -0
- package/dist/plugins/pg-checkpoint-store.d.ts +1 -33
- package/dist/plugins/pg-checkpoint-store.js +1 -189
- package/dist/plugins/pg-cost-quota.js +3 -143
- package/dist/plugins/pg-image-bake.d.ts +1 -40
- package/dist/plugins/pg-image-bake.js +1 -420
- package/dist/plugins/pg-rate-limiter.d.ts +2 -32
- package/dist/plugins/pg-rate-limiter.js +4 -147
- package/dist/plugins/remote-env-k8s.d.ts +5 -38
- package/dist/plugins/remote-env-k8s.js +7 -213
- package/dist/plugins/sql-driver.d.ts +25 -0
- package/dist/plugins/sql-driver.js +59 -0
- package/dist/plugins/tidb-checkpoint-store.d.ts +1 -49
- package/dist/plugins/tidb-checkpoint-store.js +1 -191
- package/dist/plugins/tidb-image-bake.d.ts +1 -38
- package/dist/plugins/tidb-image-bake.js +1 -348
- package/dist/plugins/web-search.d.ts +4 -3
- package/dist/plugins/write-behind-counter.d.ts +14 -3
- package/dist/plugins/write-behind-counter.js +39 -12
- package/dist/principal-jwt.d.ts +44 -0
- package/dist/principal-jwt.js +95 -0
- package/dist/question.d.ts +2 -1
- package/dist/question.js +3 -2
- package/dist/run-local.js +2 -2
- package/dist/runtime-caps-resolver.d.ts +7 -7
- package/dist/runtime-caps-resolver.js +3 -3
- package/dist/security.d.ts +4 -74
- package/dist/security.js +6 -155
- package/dist/sema-registry.d.ts +5 -200
- package/dist/sema-registry.js +4 -567
- package/dist/tool-approval.d.ts +2 -1
- package/dist/tool-approval.js +3 -2
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -58,15 +58,15 @@ import { createRegistryJwtVerifier } from "./auth-bridge.js";
|
|
|
58
58
|
import { createMetrics } from "./observability/metrics.js";
|
|
59
59
|
import { setRedactionObserver, redactSecrets } from "./trace/redact.js";
|
|
60
60
|
import { RateLimiter } from "./observability/rate-limit.js";
|
|
61
|
-
import { createHttpServer, explicitOperator } from "./http/server.js";
|
|
61
|
+
import { createHttpServer, explicitOperator, } from "./http/server.js";
|
|
62
62
|
import { exportMemoryScope } from "./memory-export.js";
|
|
63
63
|
import { performMemorySync } from "./memory-sync.js";
|
|
64
64
|
import { createMemorySyncRunner, createMemorySyncTransport } from "./memory-sync-client.js";
|
|
65
65
|
import { createLeaderEndpoint } from "./leader/endpoint.js";
|
|
66
|
-
import {
|
|
66
|
+
import { createLeaderRunner } from "./leader/wire.js";
|
|
67
67
|
import { WorkflowNotifyGate, JournalingWorkflowRunStore, FileWorkflowNotifyJournalStore, } from "./orchestration/workflow-notify-journal.js";
|
|
68
68
|
import { FileWorkflowCompletionInbox, InMemoryWorkflowCompletionInbox, resolveCompletionRoute, resolveServedSession, } from "./orchestration/workflow-completion-inbox.js";
|
|
69
|
-
import { buildPricing,
|
|
69
|
+
import { buildPricing, createTracer, createSideQueryAccountant, cappedCeiling, ModelUsageTracker, PromptManifestTracker } from "./budget.js";
|
|
70
70
|
import { CostQuota } from "./observability/cost-quota.js";
|
|
71
71
|
import { startOtlpExporter } from "./observability/otel-exporter.js";
|
|
72
72
|
import { HEARTBEAT_MS, backgroundAgentOutput, taskHandleOutput, taskHandleStop } from "./runs.js";
|
|
@@ -84,14 +84,14 @@ import { LocalTaskAttachmentStore } from "./plugins/local-task-attachment-store.
|
|
|
84
84
|
import { MinioBlobBackend, SQL_BLOB_DEFAULT_MAX_BYTES } from "./plugins/blob-backend.js";
|
|
85
85
|
import { TiDBBackgroundAgentStore, PgBackgroundAgentStore, ensureTiDBBackgroundAgentSchema, ensurePgBackgroundAgentSchema } from "./plugins/background-agent-store-sql.js";
|
|
86
86
|
import { TiDBMailboxStore, PgMailboxStore, ensureTiDBMailboxSchema, ensurePgMailboxSchema } from "./plugins/mailbox-store-sql.js";
|
|
87
|
-
import {
|
|
87
|
+
import { createPrincipalEntitlementsClient, gateExecutionLane, scopedTokenNeedsWorker, applyObserverEnvOptIn } from "./runtime-caps-resolver.js";
|
|
88
88
|
import { applyRuntimeGovernance, stripDelegationTools } from "./runtime-governance.js";
|
|
89
89
|
import { parseTaskSettings, applyTaskSettings, coercePermissionMode, withPermissionMode, effectiveThinking, acceptAppendSystemPrompt, hasConstitutionAnchors, providerDropsAppend, MAX_SETTINGS_OUTPUT_STYLE_CHARS } from "./task-settings.js";
|
|
90
|
-
import {
|
|
90
|
+
import { createTaskHooks, composeHooks } from "./hooks/hook-runner.js";
|
|
91
91
|
import { createHookLlm } from "./hooks/hook-llm.js";
|
|
92
92
|
import { selfOrchestrationFromBody, enableForkFromBody, workflowModelAllowlistFor, normalizeRetainSubagentSessions } from "./task-workflow.js";
|
|
93
93
|
import { FleetUsageAccumulator, startFleetClientFromEnv } from "./fleet-client.js";
|
|
94
|
-
import {
|
|
94
|
+
import { createFleetLeaseFromEnv } from "./fleet-lease.js";
|
|
95
95
|
import { reclaimOrphansAtBoot } from "./boot-reclaim.js";
|
|
96
96
|
import { evictLspOnDestroy } from "./lsp-evict.js";
|
|
97
97
|
import { makeLoadProjectMemory, makeProbeInstructionSources } from "./project-memory.js";
|
|
@@ -104,7 +104,7 @@ import { createWorkerHardenedVmRunner } from "./orchestration/hardened-vm-worker
|
|
|
104
104
|
import { WorkflowAgentRegistry } from "./orchestration/workflow-agent-steer.js";
|
|
105
105
|
import { SubagentSteerRegistry } from "./orchestration/subagent-steer.js";
|
|
106
106
|
import { FleetEventBus, fleetBackgroundChildPublisher } from "./fleet/fleet-bus.js";
|
|
107
|
-
import {
|
|
107
|
+
import { createKeyResolver } from "./key-resolver.js";
|
|
108
108
|
function serviceVersion() {
|
|
109
109
|
try {
|
|
110
110
|
const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
@@ -668,9 +668,9 @@ async function main() {
|
|
|
668
668
|
const modelUsageTracker = new ModelUsageTracker();
|
|
669
669
|
const promptManifestTracker = new PromptManifestTracker();
|
|
670
670
|
const fleetUsage = config.configCenter && process.env.FLEET_ADVERTISE_ADDRESS ? new FleetUsageAccumulator() : undefined;
|
|
671
|
-
const fleetLease =
|
|
672
|
-
const tracer =
|
|
673
|
-
const sideQueryAccounting =
|
|
671
|
+
const fleetLease = createFleetLeaseFromEnv(config, { logger, metrics });
|
|
672
|
+
const tracer = createTracer(metrics, costQuota, modelUsageTracker, fleetUsage, fleetLease, (m) => config.modelQuotaWeights[m] ?? 1, promptManifestTracker);
|
|
673
|
+
const sideQueryAccounting = createSideQueryAccountant(metrics, costQuota, fleetUsage, fleetLease, (m) => config.modelQuotaWeights[m] ?? 1);
|
|
674
674
|
const toolResultStore = backend?.toolResult ? backend.toolResult() : undefined;
|
|
675
675
|
const sessionPolicyStore = backend ? backend.sessionPolicy() : undefined;
|
|
676
676
|
const fileSnapshotStore = backend ? backend.fileSnapshot() : undefined;
|
|
@@ -975,7 +975,7 @@ async function main() {
|
|
|
975
975
|
});
|
|
976
976
|
}
|
|
977
977
|
const principalCaps = config.configCenter && !config.configCenter.dryRun
|
|
978
|
-
?
|
|
978
|
+
? createPrincipalEntitlementsClient({
|
|
979
979
|
baseUrl: config.configCenter.baseUrl,
|
|
980
980
|
token: config.configCenter.token,
|
|
981
981
|
...(config.configCenter.worker ? { worker: config.configCenter.worker } : {}),
|
|
@@ -1190,19 +1190,19 @@ async function main() {
|
|
|
1190
1190
|
tracer,
|
|
1191
1191
|
promptSource,
|
|
1192
1192
|
sessionStore: subRunnerSessions,
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1193
|
+
executionEnvFactory: executionEnvFactory ? executionEnvFactory : undefined,
|
|
1194
|
+
lspManager: lspManager ? lspManager : undefined,
|
|
1195
|
+
backgroundAgentStore: backgroundAgentStore ? backgroundAgentStore : undefined,
|
|
1196
|
+
mailboxStore: mailboxStore ? mailboxStore : undefined,
|
|
1197
|
+
rosterStore: rosterStore ? rosterStore : undefined,
|
|
1198
|
+
onBackgroundChildEvent: runnerDeps.onBackgroundChildEvent ? runnerDeps.onBackgroundChildEvent : undefined,
|
|
1199
|
+
loadProjectMemory: runnerDeps.loadProjectMemory ? runnerDeps.loadProjectMemory : undefined,
|
|
1200
|
+
probeInstructionSources: runnerDeps.probeInstructionSources ? runnerDeps.probeInstructionSources : undefined,
|
|
1201
1201
|
hooks: deploymentHooks,
|
|
1202
|
-
|
|
1202
|
+
onError: runnerDeps.onError ? runnerDeps.onError : undefined,
|
|
1203
1203
|
toolResultStore,
|
|
1204
|
-
|
|
1205
|
-
|
|
1204
|
+
sessionPolicyStore: sessionPolicyStore ? sessionPolicyStore : undefined,
|
|
1205
|
+
checkpointStore: checkpointStore ? checkpointStore : undefined,
|
|
1206
1206
|
});
|
|
1207
1207
|
const authorize = createAuthorizer(config, sessionStore);
|
|
1208
1208
|
const runStore = backend ? backend.run() : undefined;
|
|
@@ -1575,7 +1575,7 @@ async function main() {
|
|
|
1575
1575
|
if (!planeDeferred)
|
|
1576
1576
|
markRosterLanded(r.effective);
|
|
1577
1577
|
mutateInPlace(pricing, buildPricing(config.models));
|
|
1578
|
-
keyResolver =
|
|
1578
|
+
keyResolver = createKeyResolver(config.modelApiKeyEnv, process.env, config.modelApiKeys);
|
|
1579
1579
|
const lkgPersisted = await persistLkgDurable(r.effective, r.etag);
|
|
1580
1580
|
const reasons = restartReasons(effective, r.effective);
|
|
1581
1581
|
const skillsChangeDetected = reasons.includes("skills");
|
|
@@ -1675,7 +1675,7 @@ async function main() {
|
|
|
1675
1675
|
if (!planeDeferredLate)
|
|
1676
1676
|
markRosterLanded(r.effective);
|
|
1677
1677
|
mutateInPlace(pricing, buildPricing(config.models));
|
|
1678
|
-
keyResolver =
|
|
1678
|
+
keyResolver = createKeyResolver(config.modelApiKeyEnv, process.env, config.modelApiKeys);
|
|
1679
1679
|
effective = r.effective;
|
|
1680
1680
|
latestEffective = r.effective;
|
|
1681
1681
|
ccEtag = r.etag;
|
|
@@ -1705,7 +1705,7 @@ async function main() {
|
|
|
1705
1705
|
if (bootConfigPending)
|
|
1706
1706
|
void bootConfigPending.then((r) => (lkgBooted ? refreshTick(r) : deferredBootApply(r)), () => { });
|
|
1707
1707
|
}
|
|
1708
|
-
let keyResolver =
|
|
1708
|
+
let keyResolver = createKeyResolver(config.modelApiKeyEnv, process.env, config.modelApiKeys);
|
|
1709
1709
|
if (process.env.ANTHROPIC_AUTH_TOKEN && !process.env.ANTHROPIC_API_KEY && (Object.keys(config.modelApiKeys).length > 0 || Object.keys(config.modelApiKeyEnv).length > 0)) {
|
|
1710
1710
|
logger.warn("per_model_key_bearer_gateway_combo", {
|
|
1711
1711
|
perModelKeys: Object.keys(config.modelApiKeys).length + Object.keys(config.modelApiKeyEnv).length,
|
|
@@ -1789,7 +1789,7 @@ async function main() {
|
|
|
1789
1789
|
(leaderProvider === "e2b" ||
|
|
1790
1790
|
(leaderProvider === "k8s" && "s3" in leaderMinio && executionEnvFactory) ||
|
|
1791
1791
|
(leaderProvider === "host" && executionEnvFactory))
|
|
1792
|
-
? createLeaderEndpoint(
|
|
1792
|
+
? createLeaderEndpoint(createLeaderRunner({
|
|
1793
1793
|
brain, models: config.models, roles: config.roles, pricing, logger,
|
|
1794
1794
|
fanoutEnabled: config.leaderFanoutEnabled,
|
|
1795
1795
|
...((process.env.MODEL_ROUTER_ID || process.env.MODEL_CHEAP_ID)
|
|
@@ -1834,73 +1834,77 @@ async function main() {
|
|
|
1834
1834
|
if (registryJwtVerifier)
|
|
1835
1835
|
logger.info("auth_bridge_enabled", { issuer: authBridgeIssuer });
|
|
1836
1836
|
const hookWakeBus = {};
|
|
1837
|
-
const
|
|
1838
|
-
runner,
|
|
1839
|
-
config,
|
|
1840
|
-
authorize,
|
|
1841
|
-
sessionStoreLabel: config.sessionBackend === "tidb" && backend ? `durable(${backend.kind})` : config.sessionBackend,
|
|
1842
|
-
taskAttachmentStore: taskAttachmentStore ? taskAttachmentStore : undefined,
|
|
1843
|
-
snapshotBlobSqlCapBytes: backend && backend.kind !== "local" && !config.snapshotBlobStore
|
|
1844
|
-
? (config.snapshotBlobSqlMaxBytes ?? (backend.kind === "mysql" ? SQL_BLOB_DEFAULT_MAX_BYTES : undefined))
|
|
1845
|
-
: undefined,
|
|
1846
|
-
modelReady: () => modelReadyState.ready,
|
|
1847
|
-
scenarioDetails,
|
|
1848
|
-
registryJwtVerifier: registryJwtVerifier ? registryJwtVerifier : undefined,
|
|
1849
|
-
hookWakeBus,
|
|
1837
|
+
const stores = {
|
|
1850
1838
|
runStore,
|
|
1851
|
-
fleetBus,
|
|
1852
|
-
workflowsCapable,
|
|
1853
1839
|
resumeAnchorStore: resumeAnchorStore ? resumeAnchorStore : undefined,
|
|
1840
|
+
approvalStore,
|
|
1854
1841
|
approvalExemptionStore: approvalExemptionStore ? approvalExemptionStore : undefined,
|
|
1855
|
-
|
|
1842
|
+
checkpointStore,
|
|
1856
1843
|
sessionPolicyStore: sessionPolicyStore ? sessionPolicyStore : undefined,
|
|
1857
1844
|
fileSnapshotStore: fileSnapshotStore ? fileSnapshotStore : undefined,
|
|
1858
|
-
|
|
1859
|
-
sessionMirrorRuling: principalCaps
|
|
1860
|
-
? async (p) => (await principalCaps.executionRuling(p))?.sessionMirror
|
|
1861
|
-
: undefined,
|
|
1862
|
-
approvalStore,
|
|
1863
|
-
checkpointStore,
|
|
1845
|
+
taskAttachmentStore: taskAttachmentStore ? taskAttachmentStore : undefined,
|
|
1864
1846
|
backgroundAgentStore: backgroundAgentStore ? backgroundAgentStore : undefined,
|
|
1865
|
-
|
|
1866
|
-
parkedReviveInheritedGate: parkedReviveInheritedGate ? parkedReviveInheritedGate : undefined,
|
|
1867
|
-
imageIndex,
|
|
1868
|
-
imageBakes,
|
|
1869
|
-
leaderEndpoint,
|
|
1847
|
+
sessionStorage: ownerAware,
|
|
1870
1848
|
workflowRunStore: workflowRunStore ? workflowRunStore : undefined,
|
|
1871
1849
|
workflowJournalStore: workflowJournalStore ? workflowJournalStore : undefined,
|
|
1850
|
+
imageIndex,
|
|
1851
|
+
imageBakes,
|
|
1852
|
+
outcomeSink: outcomeSink ? outcomeSink : undefined,
|
|
1853
|
+
sendFileLedger: sendFileLedger ? sendFileLedger : undefined,
|
|
1854
|
+
backend: backend ? backend : undefined,
|
|
1855
|
+
};
|
|
1856
|
+
const coordinators = {
|
|
1857
|
+
elicitation: elicitation ? elicitation : undefined,
|
|
1858
|
+
question: question ? question : undefined,
|
|
1859
|
+
toolApproval: toolApproval ? toolApproval : undefined,
|
|
1872
1860
|
workflowAgentRegistry: workflowAgentRegistry ? workflowAgentRegistry : undefined,
|
|
1873
1861
|
subagentSteerRegistry,
|
|
1874
|
-
subagentTaskOutput: (handle, access) => backgroundAgentOutput(defaultTaskRegistry, handle, access, backgroundAgentStore),
|
|
1875
|
-
taskHandleOutput: (handle, access) => taskHandleOutput(defaultTaskRegistry, handle, access, backgroundAgentStore),
|
|
1876
|
-
taskHandleStop: (handle, access) => taskHandleStop(defaultTaskRegistry, handle, access, backgroundAgentStore),
|
|
1877
1862
|
workflowCompletionInbox: workflowCompletionInbox ? workflowCompletionInbox : undefined,
|
|
1878
|
-
|
|
1879
|
-
sessionStorage: ownerAware,
|
|
1863
|
+
fleetBus,
|
|
1880
1864
|
sessionWatch: sessionWatchRegistry ? sessionWatchRegistry : undefined,
|
|
1881
|
-
|
|
1865
|
+
hookWakeBus,
|
|
1866
|
+
sendUserFile: sendUserFileEmitter ? sendUserFileEmitter : undefined,
|
|
1867
|
+
leaderEndpoint,
|
|
1868
|
+
sessionTitler: sessionTitler ? sessionTitler : undefined,
|
|
1869
|
+
};
|
|
1870
|
+
const seams = {
|
|
1871
|
+
sessionAudit,
|
|
1882
1872
|
purgeSession: purgeSession ? purgeSession : undefined,
|
|
1873
|
+
subagentTaskOutput: (handle, access) => backgroundAgentOutput(defaultTaskRegistry, handle, access, backgroundAgentStore),
|
|
1874
|
+
taskHandleOutput: (handle, access) => taskHandleOutput(defaultTaskRegistry, handle, access, backgroundAgentStore),
|
|
1875
|
+
taskHandleStop: (handle, access) => taskHandleStop(defaultTaskRegistry, handle, access, backgroundAgentStore),
|
|
1876
|
+
memoryExport: memoryExportBackend ? (scope) => exportMemoryScope(memoryExportBackend, scope) : undefined,
|
|
1877
|
+
memorySync: memoryExportBackend && memorySyncCursors
|
|
1878
|
+
? (scope, syncReq) => performMemorySync(memoryExportBackend, memorySyncCursors, scope, syncReq)
|
|
1879
|
+
: undefined,
|
|
1880
|
+
sessionMirrorRuling: principalCaps
|
|
1881
|
+
? async (p) => (await principalCaps.executionRuling(p))?.sessionMirror
|
|
1882
|
+
: undefined,
|
|
1883
1883
|
instrumentDegenerate,
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
toolApproval: toolApproval ? toolApproval : undefined,
|
|
1890
|
-
sendUserFile: sendUserFileEmitter ? sendUserFileEmitter : undefined,
|
|
1891
|
-
sendFileLedger: sendFileLedger ? sendFileLedger : undefined,
|
|
1892
|
-
instanceId,
|
|
1884
|
+
sideQueryAccounting,
|
|
1885
|
+
parkedReviveTool: parkedReviveTool ? parkedReviveTool : undefined,
|
|
1886
|
+
parkedReviveInheritedGate: parkedReviveInheritedGate ? parkedReviveInheritedGate : undefined,
|
|
1887
|
+
};
|
|
1888
|
+
const observability = {
|
|
1893
1889
|
logger,
|
|
1894
1890
|
metrics,
|
|
1891
|
+
modelUsage: modelUsageTracker,
|
|
1892
|
+
promptManifests: promptManifestTracker,
|
|
1893
|
+
planCacheProbe,
|
|
1894
|
+
};
|
|
1895
|
+
const governance = {
|
|
1896
|
+
authorize,
|
|
1897
|
+
registryJwtVerifier: registryJwtVerifier ? registryJwtVerifier : undefined,
|
|
1895
1898
|
rateLimiter,
|
|
1896
1899
|
costQuota,
|
|
1897
1900
|
fleetLease: fleetLease ? fleetLease : undefined,
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1901
|
+
};
|
|
1902
|
+
const deployment = {
|
|
1903
|
+
sessionStoreLabel: config.sessionBackend === "tidb" && backend ? `durable(${backend.kind})` : config.sessionBackend,
|
|
1904
|
+
modelReady: () => modelReadyState.ready,
|
|
1905
|
+
scenarioDetails,
|
|
1906
|
+
workflowsCapable,
|
|
1907
|
+
instanceId,
|
|
1904
1908
|
capabilities: {
|
|
1905
1909
|
version: serviceVersion(),
|
|
1906
1910
|
scenarios: Object.keys(scenarios),
|
|
@@ -1910,431 +1914,438 @@ async function main() {
|
|
|
1910
1914
|
planeDeferredState: () => planeDeferredNoHandoff,
|
|
1911
1915
|
drainState,
|
|
1912
1916
|
storeDegraded: storeBackendDegraded,
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1917
|
+
};
|
|
1918
|
+
const knobs = {
|
|
1919
|
+
snapshotBlobSqlCapBytes: backend && backend.kind !== "local" && !config.snapshotBlobStore
|
|
1920
|
+
? (config.snapshotBlobSqlMaxBytes ?? (backend.kind === "mysql" ? SQL_BLOB_DEFAULT_MAX_BYTES : undefined))
|
|
1921
|
+
: undefined,
|
|
1922
|
+
sessionEventsMaxConnections: sessionWatchRegistry ? posIntEnv(process.env.SESSION_EVENTS_MAX_CONNS, 256, 100_000) : undefined,
|
|
1923
|
+
};
|
|
1924
|
+
const resolveSpec = async (body, _req, auth, opts) => {
|
|
1925
|
+
const requested = gateScenarioRequest(await principalCaps?.scenarioRuling(auth?.principal), body.scenario, config.defaultScenario);
|
|
1926
|
+
gateExecutionLane(await principalCaps?.executionRuling(auth?.principal), config.remoteExec?.provider ?? "in-process");
|
|
1927
|
+
const scenarioName = scenarios[requested] ? requested : "default";
|
|
1928
|
+
if (scenarioName === "autonomous")
|
|
1929
|
+
logger.warn("scenario_autonomous_deprecated", { alias: "code", note: "renamed by [891]; alias keeps finalVerification pinned — pass scenario:'code' (+ explicit finalVerification if wanted)" });
|
|
1930
|
+
const cap = selectScenario(scenarios, scenarioName)(body, auth?.principal);
|
|
1931
|
+
const centerDecls = centerPrompts?.declarations;
|
|
1932
|
+
const appendLessPack = centerDecls
|
|
1933
|
+
? centerIdentityAssembled(centerDecls, scenarioName, hasConstitutionAnchors)
|
|
1934
|
+
: providerDropsAppend(cap.promptProvider, typeof body.systemPrompt === "string" ? body.systemPrompt : undefined);
|
|
1935
|
+
const acceptedAppend = acceptAppendSystemPrompt(body.appendSystemPrompt, (detail) => logger.warn("append_system_prompt_dropped", { detail, sessionId: auth?.sessionId ?? null }), appendLessPack);
|
|
1936
|
+
const objective = typeof body.objective === "string" ? body.objective : "";
|
|
1937
|
+
const parsedSettings = parseTaskSettings(body.settings);
|
|
1938
|
+
if (opts?.leg === "fresh" && appendLessPack) {
|
|
1939
|
+
const riderPresent = typeof body.appendSystemPrompt === "string" && body.appendSystemPrompt.length > 0;
|
|
1940
|
+
if (riderPresent || parsedSettings.settings?.outputStyle) {
|
|
1941
|
+
throw new HttpError(400, `${riderPresent ? "appendSystemPrompt" : "settings.outputStyle"} is not supported when the effective prompt is already assembled (constitution anchors in systemPrompt, or a center assembled-identity pack for this scenario): the assembler's pass-through path cannot mount the append slot — fold it into the assembled prompt instead`);
|
|
1935
1942
|
}
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1943
|
+
}
|
|
1944
|
+
if (parsedSettings.deferred.length > 0) {
|
|
1945
|
+
logger.warn("task_settings_deferred", { fields: parsedSettings.deferred, sessionId: auth?.sessionId ?? null });
|
|
1946
|
+
}
|
|
1947
|
+
let attachmentNotice;
|
|
1948
|
+
{
|
|
1949
|
+
const reqAtt = body.attachmentIds;
|
|
1950
|
+
if (reqAtt !== undefined) {
|
|
1951
|
+
if (!Array.isArray(reqAtt) || !reqAtt.every((x) => typeof x === "string" && x.length > 0 && x.length <= 64)) {
|
|
1952
|
+
throw new HttpError(400, "`attachmentIds` must be an array of attachment ids (strings)");
|
|
1953
|
+
}
|
|
1954
|
+
if (reqAtt.length > 16)
|
|
1955
|
+
throw new HttpError(400, "`attachmentIds` exceeds the per-task limit (16)");
|
|
1956
|
+
if (reqAtt.length > 0) {
|
|
1957
|
+
if (!taskAttachmentStore)
|
|
1958
|
+
throw new HttpError(501, "attachmentIds require a store backend (DB_BACKEND=tidb|pg|local)");
|
|
1959
|
+
if (!auth?.sessionId)
|
|
1960
|
+
throw new HttpError(400, "`attachmentIds` requires a session-resolving deployment (no authorizer session)");
|
|
1961
|
+
const sid = auth.sessionId;
|
|
1962
|
+
const r = await bindAttachmentsForTask({
|
|
1963
|
+
store: taskAttachmentStore,
|
|
1964
|
+
owner: auth?.principal ?? "default",
|
|
1965
|
+
ids: reqAtt,
|
|
1966
|
+
sessionId: sid,
|
|
1967
|
+
leg: opts?.leg === "fresh" ? "fresh" : "resume",
|
|
1968
|
+
nowMs: Date.now(),
|
|
1969
|
+
onMissing: (id) => {
|
|
1970
|
+
if (opts?.leg === "fresh")
|
|
1971
|
+
throw new HttpError(400, `attachment not found: ${id}`);
|
|
1972
|
+
logger.warn("attachment_missing_on_resume", { sessionId: sid, id });
|
|
1973
|
+
},
|
|
1974
|
+
});
|
|
1975
|
+
attachmentNotice = r.notice;
|
|
1966
1976
|
}
|
|
1967
1977
|
}
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1978
|
+
}
|
|
1979
|
+
const execLane = config.remoteExec?.provider ?? "in-process";
|
|
1980
|
+
if (typeof body.cwd === "string" && body.cwd.length > 0 && auth?.sessionId) {
|
|
1981
|
+
if (cwdHonored(config) && isValidCwd(body.cwd))
|
|
1982
|
+
setSessionCwd(auth.sessionId, body.cwd);
|
|
1983
|
+
else if (inProcessSingleUserLane(config) && isValidCwd(body.cwd) && satisfiedByProcessCwd(body.cwd, realpathSync))
|
|
1984
|
+
logger.debug("task_cwd_inherited", { lane: execLane, sessionId: auth.sessionId });
|
|
1985
|
+
else
|
|
1986
|
+
logger.warn("task_cwd_ignored", { honored: cwdHonored(config), lane: execLane, sessionId: auth.sessionId });
|
|
1987
|
+
}
|
|
1988
|
+
if (parsedSettings.settings?.shellEnv && auth?.sessionId) {
|
|
1989
|
+
if (cwdHonored(config))
|
|
1990
|
+
setSessionShellEnv(auth.sessionId, parsedSettings.settings.shellEnv);
|
|
1991
|
+
else if (inProcessSingleUserLane(config)) {
|
|
1992
|
+
const requested = parsedSettings.settings.shellEnv;
|
|
1993
|
+
const mismatched = shellEnvMismatchCount(requested, process.env);
|
|
1994
|
+
if (mismatched === 0)
|
|
1995
|
+
logger.debug("task_shell_env_inherited", { lane: execLane, keys: Object.keys(requested).length, sessionId: auth.sessionId });
|
|
1974
1996
|
else
|
|
1975
|
-
logger.warn("
|
|
1997
|
+
logger.warn("task_shell_env_ignored", { honored: false, lane: execLane, mismatchedKeys: mismatched, sessionId: auth.sessionId });
|
|
1976
1998
|
}
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
}
|
|
1988
|
-
else
|
|
1989
|
-
logger.warn("task_shell_env_ignored", { honored: false, lane: execLane, sessionId: auth.sessionId });
|
|
1999
|
+
else
|
|
2000
|
+
logger.warn("task_shell_env_ignored", { honored: false, lane: execLane, sessionId: auth.sessionId });
|
|
2001
|
+
}
|
|
2002
|
+
const taskHooks = (() => {
|
|
2003
|
+
const hc = parsedSettings.settings?.hooks;
|
|
2004
|
+
if (!hc)
|
|
2005
|
+
return undefined;
|
|
2006
|
+
if (config.requirePrincipal === true) {
|
|
2007
|
+
logger.warn("task_hooks_ignored", { honored: false, sessionId: auth?.sessionId ?? null });
|
|
2008
|
+
return undefined;
|
|
1990
2009
|
}
|
|
1991
|
-
const
|
|
1992
|
-
|
|
1993
|
-
|
|
2010
|
+
const effMode = coercePermissionMode(body.permissionMode) ?? parsedSettings.settings?.permissions?.defaultMode;
|
|
2011
|
+
return createTaskHooks(hc, {
|
|
2012
|
+
logger,
|
|
2013
|
+
sessionId: auth?.sessionId ?? "",
|
|
2014
|
+
cwd: (auth?.sessionId ? perSessionCwd.get(auth.sessionId) : undefined) ?? process.cwd(),
|
|
2015
|
+
...(effMode ? { permissionMode: effMode } : {}),
|
|
2016
|
+
...(parsedSettings.settings?.shellEnv ? { shellEnv: parsedSettings.settings.shellEnv } : {}),
|
|
2017
|
+
hookLlm,
|
|
2018
|
+
hookAgent,
|
|
2019
|
+
onHookNotice: (n) => fleetBus.publishHookNotice({
|
|
2020
|
+
...n,
|
|
2021
|
+
...(auth?.principal ? { ownerScope: auth.principal } : {}),
|
|
2022
|
+
...(auth?.sessionId ? { ownerSessionId: auth.sessionId } : {}),
|
|
2023
|
+
}),
|
|
2024
|
+
...(auth?.sessionId
|
|
2025
|
+
? {
|
|
2026
|
+
wake: async (text) => hookWakeBus.deliver
|
|
2027
|
+
?
|
|
2028
|
+
hookWakeBus.deliver(auth.sessionId, `[hook asyncRewake] ${redactSecrets(text)}`)
|
|
2029
|
+
: false,
|
|
2030
|
+
}
|
|
2031
|
+
: {}),
|
|
2032
|
+
});
|
|
2033
|
+
})();
|
|
2034
|
+
const rawAddDirs = body.additionalDirectories;
|
|
2035
|
+
const additionalDirectories = cwdHonored(config) ? parseAdditionalDirectories(rawAddDirs) : undefined;
|
|
2036
|
+
if (rawAddDirs !== undefined && !cwdHonored(config)) {
|
|
2037
|
+
logger.warn("task_additional_directories_ignored", { honored: false, sessionId: auth?.sessionId ?? null });
|
|
2038
|
+
}
|
|
2039
|
+
const wireCatalog = expandTiers(config.models, config.tiers) ?? config.models;
|
|
2040
|
+
const picked = resolveTaskModel(body.model ?? parsedSettings.settings?.model, objective, wireCatalog);
|
|
2041
|
+
if (picked.unknownExplicit !== undefined) {
|
|
2042
|
+
metrics.inc("task_model_unknown_fallback_total");
|
|
2043
|
+
logger.warn("task_model_unknown_fallback", { requested: picked.unknownExplicit.slice(0, 120), fallback: picked.model, sessionId: auth?.sessionId ?? null });
|
|
2044
|
+
}
|
|
2045
|
+
if (Array.isArray(body.images) && body.images.length > 0 && !modelSupportsImages(wireCatalog[picked.model])) {
|
|
2046
|
+
metrics.inc("images_omitted_total", { model: picked.model });
|
|
2047
|
+
logger.warn("images_omitted_no_vision_model", { model: picked.model, count: body.images.length });
|
|
2048
|
+
}
|
|
2049
|
+
let resumeAtEntryId;
|
|
2050
|
+
if (typeof body.resumeAt === "string" && body.resumeAt.length > 0) {
|
|
2051
|
+
if (!auth?.sessionId)
|
|
2052
|
+
throw new HttpError(422, "resumeAt requires a session to branch (resume_at.no_session)");
|
|
2053
|
+
if (!resumeAnchorStore || !ownerAware.getLeafId)
|
|
2054
|
+
throw new HttpError(501, "resume-at is not available on this worker (no session-store backend for the anchor map)");
|
|
2055
|
+
resumeAtEntryId = await resumeAnchorStore.resolve(auth.sessionId, body.resumeAt, auth.principal ?? null);
|
|
2056
|
+
if (resumeAtEntryId === undefined)
|
|
2057
|
+
throw new HttpError(404, "resumeAt: no such message in this session (resume_at.unknown_event)");
|
|
2058
|
+
}
|
|
2059
|
+
let rewindFilesToEntryId;
|
|
2060
|
+
if (resumeAtEntryId === undefined && typeof body.rewindFilesTo === "string" && body.rewindFilesTo.length > 0) {
|
|
2061
|
+
if (!auth?.sessionId)
|
|
2062
|
+
throw new HttpError(422, "rewindFilesTo requires a session (rewind_files_to.no_session)");
|
|
2063
|
+
if (!resumeAnchorStore || !ownerAware.getLeafId)
|
|
2064
|
+
throw new HttpError(501, "rewind-files-to is not available on this worker (no session-store backend for the anchor map)");
|
|
2065
|
+
rewindFilesToEntryId = await resumeAnchorStore.resolve(auth.sessionId, body.rewindFilesTo, auth.principal ?? null);
|
|
2066
|
+
if (rewindFilesToEntryId === undefined)
|
|
2067
|
+
throw new HttpError(404, "rewindFilesTo: no such message in this session (rewind_files_to.unknown_event)");
|
|
2068
|
+
}
|
|
2069
|
+
const s4ProjectId = auth?.resolvedProjectId ?? (typeof body.projectId === "string" && body.projectId ? body.projectId : undefined);
|
|
2070
|
+
const s4DefaultScopes = s4ProjectId ? config.projects[s4ProjectId]?.defaultScopes : undefined;
|
|
2071
|
+
const spec = {
|
|
2072
|
+
objective: attachmentNotice ? `${picked.cleanedObjective}\n\n${attachmentNotice}` : picked.cleanedObjective,
|
|
2073
|
+
systemPrompt: typeof body.systemPrompt === "string" ? body.systemPrompt : undefined,
|
|
2074
|
+
appendSystemPrompt: acceptedAppend,
|
|
2075
|
+
sessionId: auth?.sessionId,
|
|
2076
|
+
principal: auth?.principal,
|
|
2077
|
+
images: body.images,
|
|
2078
|
+
...(body.clientContext ? { clientContext: { timeZone: body.clientContext.timeZone, userEmail: body.clientContext.userEmail } } : {}),
|
|
2079
|
+
...(() => {
|
|
2080
|
+
const ct = body.compaction?.clampTolerance;
|
|
2081
|
+
return typeof ct === "number" && Number.isFinite(ct) && ct >= 0 && ct <= 1 ? { compaction: { clampTolerance: ct } } : {};
|
|
2082
|
+
})(),
|
|
2083
|
+
outputSchema: body.outputSchema,
|
|
2084
|
+
...(typeof body.outputRetries === "number" && Number.isFinite(body.outputRetries) && body.outputRetries >= 1
|
|
2085
|
+
? { outputRetries: Math.min(10, Math.floor(body.outputRetries)) }
|
|
2086
|
+
: {}),
|
|
2087
|
+
resumeAt: resumeAtEntryId,
|
|
2088
|
+
resumeAtMode: normalizeResumeAtMode(body.resumeAtMode, resumeAtEntryId !== undefined),
|
|
2089
|
+
requireExistingSession: body.requireExistingSession === true ? true : undefined,
|
|
2090
|
+
enableFork: enableForkFromBody(body, config, Boolean(centerRuntimeCapsResolver)),
|
|
2091
|
+
suggestNextPrompts: normalizeSuggestNextPrompts(body.suggestNextPrompts),
|
|
2092
|
+
rewindFiles: body.rewindFiles === true ? true : undefined,
|
|
2093
|
+
rewindFilesTo: rewindFilesToEntryId,
|
|
2094
|
+
additionalDirectories,
|
|
2095
|
+
enablePlanMode: config.planModeEnabled ? true : undefined,
|
|
2096
|
+
selfOrchestration: selfOrchestrationFromBody({ selfOrchestration: body.selfOrchestration === true || parsedSettings.settings?.ultracode === true }, config, Boolean(centerRuntimeCapsResolver)),
|
|
2097
|
+
forwardSubagentEvents: body.forwardSubagentEvents === true ? true : undefined,
|
|
2098
|
+
retainSubagentSessions: normalizeRetainSubagentSessions(body.retainSubagentSessions),
|
|
2099
|
+
...taskAgentsSpecFragment(body.agents, config.requirePrincipal, (event, fields) => logger.warn(event, { ...fields, sessionId: auth?.sessionId ?? null })),
|
|
2100
|
+
...(retainBackgroundProcessesFromBody(body.retainBackgroundProcesses, config.requirePrincipal, (event, fields) => logger.warn(event, { ...fields, sessionId: auth?.sessionId ?? null })) === true
|
|
2101
|
+
? { retainBackgroundProcesses: true }
|
|
2102
|
+
: {}),
|
|
2103
|
+
...(() => { const v = toolNameListFromBody(body.excludeTools); return v ? { excludeTools: v } : {}; })(),
|
|
2104
|
+
...(() => { const v = toolNameListFromBody(body.deferTools); return v ? { deferTools: v } : {}; })(),
|
|
2105
|
+
...(() => { const v = promptProfileFromBody(body.promptProfile); return v ? { promptProfile: v } : {}; })(),
|
|
2106
|
+
...(typeof body.interactiveTools === "boolean"
|
|
2107
|
+
? { interactiveTools: body.interactiveTools }
|
|
2108
|
+
: {}),
|
|
2109
|
+
resilience: normalizeResilience(body.resilience, explicitOperator(auth?.principal, config.operatorPrincipals)),
|
|
2110
|
+
finalVerification: cap.finalVerification === true || body.finalVerification === true ? true : undefined,
|
|
2111
|
+
attachments: normalizeAttachments(body.attachments),
|
|
2112
|
+
...(taskHooks ? { hooks: composeHooks(deploymentHooks, taskHooks) } : {}),
|
|
2113
|
+
model: picked.model,
|
|
2114
|
+
...((() => {
|
|
2115
|
+
if (typeof body.compactionModel !== "string" || body.compactionModel.length === 0)
|
|
2116
|
+
return {};
|
|
2117
|
+
const cm = matchCatalogModel(body.compactionModel, wireCatalog);
|
|
2118
|
+
if (cm === undefined) {
|
|
2119
|
+
if (opts?.leg === "fresh") {
|
|
2120
|
+
throw new HttpError(400, `unknown compactionModel "${body.compactionModel.slice(0, 120)}" — not in the configured catalog (name, tier word, or id; a catalog refresh may have removed it mid-request)`);
|
|
2121
|
+
}
|
|
2122
|
+
metrics.inc("task_model_unknown_fallback_total");
|
|
2123
|
+
logger.warn("compaction_model_unknown_fallback", { requested: body.compactionModel.slice(0, 120), sessionId: auth?.sessionId ?? null });
|
|
2124
|
+
return {};
|
|
2125
|
+
}
|
|
2126
|
+
return { compactionModel: cm };
|
|
2127
|
+
})()),
|
|
2128
|
+
thinking: effectiveThinking(body.reasoningEffort, parsedSettings.settings?.ultracode === true),
|
|
2129
|
+
getApiKeyAndHeaders: keyResolver,
|
|
2130
|
+
...(() => {
|
|
2131
|
+
const limits = resolveTaskLimits(body.limits, taskLimitCaps, taskTimeoutSec, config.requirePrincipal, body.council === true || body.debate === true || scenarioName === "team");
|
|
2132
|
+
const declarations = [];
|
|
2133
|
+
if (taskLimitCaps.timeoutSec !== undefined)
|
|
2134
|
+
declarations.push({ key: "server.limits.timeoutSecCap", value: String(taskLimitCaps.timeoutSec), reason: "env TASK_TIMEOUT_MAX_SEC (operator ceiling on caller limits.timeoutSec)" });
|
|
2135
|
+
if (taskLimitCaps.maxOutputTokens !== undefined)
|
|
2136
|
+
declarations.push({ key: "server.limits.maxOutputTokensCap", value: String(taskLimitCaps.maxOutputTokens), reason: "env TASK_MAX_OUTPUT_TOKENS_MAX (operator ceiling)" });
|
|
2137
|
+
if (taskLimitCaps.maxTurns !== undefined)
|
|
2138
|
+
declarations.push({ key: "server.limits.maxTurnsCap", value: String(taskLimitCaps.maxTurns), reason: "env TASK_MAX_TURNS_MAX (operator ceiling)" });
|
|
2139
|
+
if (config.maxTaskCostUsd > 0)
|
|
2140
|
+
declarations.push({ key: "server.budget.maxCostUsdCap", value: String(config.maxTaskCostUsd), reason: "env MAX_TASK_COST_USD (operator ceiling; becomes the effective value when the caller sends none)" });
|
|
2141
|
+
if (config.maxTaskTokens > 0)
|
|
2142
|
+
declarations.push({ key: "server.budget.maxTokensCap", value: String(config.maxTaskTokens), reason: "env MAX_TASK_TOKENS_MAX (operator ceiling; becomes the effective value when the caller sends none)" });
|
|
2143
|
+
if (config.requirePrincipal === true)
|
|
2144
|
+
declarations.push({ key: "server.limits.wallClockBaseSec", value: "2400/3600", reason: "tenancy wall-clock base (multi-tenant; big tasks 3600) — TASK_TIMEOUT_SEC raises, never shrinks" });
|
|
2145
|
+
return { ...(limits !== undefined ? { limits } : {}), ...(declarations.length > 0 ? { configOverrides: declarations } : {}) };
|
|
2146
|
+
})(),
|
|
2147
|
+
...(config.requirePrincipal !== true ? { backgroundScope: "session" } : {}),
|
|
2148
|
+
maxCostUsd: cappedCeiling(body.maxCostUsd, config.maxTaskCostUsd),
|
|
2149
|
+
maxTokens: cappedCeiling(body.maxTokens, config.maxTaskTokens),
|
|
2150
|
+
degrade: (() => {
|
|
2151
|
+
if (!config.degrade || cappedCeiling(body.maxCostUsd, config.maxTaskCostUsd) === undefined)
|
|
1994
2152
|
return undefined;
|
|
1995
|
-
if (
|
|
1996
|
-
|
|
2153
|
+
if (Array.isArray(body.images) && body.images.length > 0 && !config.degrade.toSupportsImages) {
|
|
2154
|
+
metrics.inc("degrade_dropped_total", { reason: "vision_target" });
|
|
2155
|
+
logger.warn("degrade_dropped", { reason: "vision_target", model: picked.model, images: body.images.length });
|
|
1997
2156
|
return undefined;
|
|
1998
2157
|
}
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
...(auth?.sessionId
|
|
2158
|
+
return { to: config.degrade.to, atCostFraction: config.degrade.atCostFraction };
|
|
2159
|
+
})(),
|
|
2160
|
+
memory: memoryEngine ? memorySpecForRequest(auth?.memoryScope, body.memoryWrite, s4DefaultScopes) : undefined,
|
|
2161
|
+
tools: ((base) => {
|
|
2162
|
+
const extra = [...(selectEnvTool ? [selectEnvTool] : []), ...(sendUserFileToolSpec ? [sendUserFileToolSpec] : [])];
|
|
2163
|
+
const merged = extra.length > 0 ? [...(base ?? []), ...extra] : base;
|
|
2164
|
+
return merged && config.toolDeferLongtail ? applyLongtailDefer(merged, true) : merged;
|
|
2165
|
+
})(cap.tools),
|
|
2166
|
+
skills: mergeUserSkills(cap.skills, body.skills, logger),
|
|
2167
|
+
mcp: resolveRequestMcp(mcpForScenario(config.mcpServers, scenarioName), body.mcpServers, config, logger),
|
|
2168
|
+
promptProvider: centerDecls ? centerPromptProvider(centerDecls, scenarioName) : cap.promptProvider,
|
|
2169
|
+
toolPolicy: durableEnabled
|
|
2170
|
+
? combinePolicies(createDurableQuestionPolicy(), createDurableAskPolicy({
|
|
2171
|
+
requireApproval: config.approvalRequire, deny: config.approvalDeny, autoBudget: config.approvalAutoBudget, neverAuto: config.approvalNeverAuto,
|
|
2172
|
+
...(approvalExemptionStore && auth?.sessionId
|
|
2014
2173
|
? {
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
hookWakeBus.deliver(auth.sessionId, `[hook asyncRewake] ${redactSecrets(text)}`)
|
|
2018
|
-
: false,
|
|
2174
|
+
exempt: (toolName) => approvalExemptionStore.has(auth.sessionId, toolName),
|
|
2175
|
+
onExempted: (toolName, rawToolName) => logger.info("approval_exempted", { sessionId: auth.sessionId, toolName, rawToolName }),
|
|
2019
2176
|
}
|
|
2020
2177
|
: {}),
|
|
2021
|
-
})
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
metrics.inc("task_model_unknown_fallback_total");
|
|
2032
|
-
logger.warn("task_model_unknown_fallback", { requested: picked.unknownExplicit.slice(0, 120), fallback: picked.model, sessionId: auth?.sessionId ?? null });
|
|
2033
|
-
}
|
|
2034
|
-
if (Array.isArray(body.images) && body.images.length > 0 && !modelSupportsImages(wireCatalog[picked.model])) {
|
|
2035
|
-
metrics.inc("images_omitted_total", { model: picked.model });
|
|
2036
|
-
logger.warn("images_omitted_no_vision_model", { model: picked.model, count: body.images.length });
|
|
2037
|
-
}
|
|
2038
|
-
let resumeAtEntryId;
|
|
2039
|
-
if (typeof body.resumeAt === "string" && body.resumeAt.length > 0) {
|
|
2040
|
-
if (!auth?.sessionId)
|
|
2041
|
-
throw new HttpError(422, "resumeAt requires a session to branch (resume_at.no_session)");
|
|
2042
|
-
if (!resumeAnchorStore || !ownerAware.getLeafId)
|
|
2043
|
-
throw new HttpError(501, "resume-at is not available on this worker (no session-store backend for the anchor map)");
|
|
2044
|
-
resumeAtEntryId = await resumeAnchorStore.resolve(auth.sessionId, body.resumeAt, auth.principal ?? null);
|
|
2045
|
-
if (resumeAtEntryId === undefined)
|
|
2046
|
-
throw new HttpError(404, "resumeAt: no such message in this session (resume_at.unknown_event)");
|
|
2047
|
-
}
|
|
2048
|
-
let rewindFilesToEntryId;
|
|
2049
|
-
if (resumeAtEntryId === undefined && typeof body.rewindFilesTo === "string" && body.rewindFilesTo.length > 0) {
|
|
2050
|
-
if (!auth?.sessionId)
|
|
2051
|
-
throw new HttpError(422, "rewindFilesTo requires a session (rewind_files_to.no_session)");
|
|
2052
|
-
if (!resumeAnchorStore || !ownerAware.getLeafId)
|
|
2053
|
-
throw new HttpError(501, "rewind-files-to is not available on this worker (no session-store backend for the anchor map)");
|
|
2054
|
-
rewindFilesToEntryId = await resumeAnchorStore.resolve(auth.sessionId, body.rewindFilesTo, auth.principal ?? null);
|
|
2055
|
-
if (rewindFilesToEntryId === undefined)
|
|
2056
|
-
throw new HttpError(404, "rewindFilesTo: no such message in this session (rewind_files_to.unknown_event)");
|
|
2057
|
-
}
|
|
2058
|
-
const s4ProjectId = auth?.resolvedProjectId ?? (typeof body.projectId === "string" && body.projectId ? body.projectId : undefined);
|
|
2059
|
-
const s4DefaultScopes = s4ProjectId ? config.projects[s4ProjectId]?.defaultScopes : undefined;
|
|
2060
|
-
const spec = {
|
|
2061
|
-
objective: attachmentNotice ? `${picked.cleanedObjective}\n\n${attachmentNotice}` : picked.cleanedObjective,
|
|
2062
|
-
systemPrompt: typeof body.systemPrompt === "string" ? body.systemPrompt : undefined,
|
|
2063
|
-
appendSystemPrompt: acceptedAppend,
|
|
2064
|
-
sessionId: auth?.sessionId,
|
|
2065
|
-
principal: auth?.principal,
|
|
2066
|
-
images: body.images,
|
|
2067
|
-
...(body.clientContext ? { clientContext: { timeZone: body.clientContext.timeZone, userEmail: body.clientContext.userEmail } } : {}),
|
|
2068
|
-
...(() => {
|
|
2069
|
-
const ct = body.compaction?.clampTolerance;
|
|
2070
|
-
return typeof ct === "number" && Number.isFinite(ct) && ct >= 0 && ct <= 1 ? { compaction: { clampTolerance: ct } } : {};
|
|
2071
|
-
})(),
|
|
2072
|
-
outputSchema: body.outputSchema,
|
|
2073
|
-
...(typeof body.outputRetries === "number" && Number.isFinite(body.outputRetries) && body.outputRetries >= 1
|
|
2074
|
-
? { outputRetries: Math.min(10, Math.floor(body.outputRetries)) }
|
|
2075
|
-
: {}),
|
|
2076
|
-
resumeAt: resumeAtEntryId,
|
|
2077
|
-
resumeAtMode: normalizeResumeAtMode(body.resumeAtMode, resumeAtEntryId !== undefined),
|
|
2078
|
-
requireExistingSession: body.requireExistingSession === true ? true : undefined,
|
|
2079
|
-
enableFork: enableForkFromBody(body, config, Boolean(centerRuntimeCapsResolver)),
|
|
2080
|
-
suggestNextPrompts: normalizeSuggestNextPrompts(body.suggestNextPrompts),
|
|
2081
|
-
rewindFiles: body.rewindFiles === true ? true : undefined,
|
|
2082
|
-
rewindFilesTo: rewindFilesToEntryId,
|
|
2083
|
-
additionalDirectories,
|
|
2084
|
-
enablePlanMode: config.planModeEnabled ? true : undefined,
|
|
2085
|
-
selfOrchestration: selfOrchestrationFromBody({ selfOrchestration: body.selfOrchestration === true || parsedSettings.settings?.ultracode === true }, config, Boolean(centerRuntimeCapsResolver)),
|
|
2086
|
-
forwardSubagentEvents: body.forwardSubagentEvents === true ? true : undefined,
|
|
2087
|
-
retainSubagentSessions: normalizeRetainSubagentSessions(body.retainSubagentSessions),
|
|
2088
|
-
...taskAgentsSpecFragment(body.agents, config.requirePrincipal, (event, fields) => logger.warn(event, { ...fields, sessionId: auth?.sessionId ?? null })),
|
|
2089
|
-
...(retainBackgroundProcessesFromBody(body.retainBackgroundProcesses, config.requirePrincipal, (event, fields) => logger.warn(event, { ...fields, sessionId: auth?.sessionId ?? null })) === true
|
|
2090
|
-
? { retainBackgroundProcesses: true }
|
|
2091
|
-
: {}),
|
|
2092
|
-
...(() => { const v = toolNameListFromBody(body.excludeTools); return v ? { excludeTools: v } : {}; })(),
|
|
2093
|
-
...(() => { const v = toolNameListFromBody(body.deferTools); return v ? { deferTools: v } : {}; })(),
|
|
2094
|
-
...(() => { const v = promptProfileFromBody(body.promptProfile); return v ? { promptProfile: v } : {}; })(),
|
|
2095
|
-
...(typeof body.interactiveTools === "boolean"
|
|
2096
|
-
? { interactiveTools: body.interactiveTools }
|
|
2097
|
-
: {}),
|
|
2098
|
-
resilience: normalizeResilience(body.resilience, explicitOperator(auth?.principal, config.operatorPrincipals)),
|
|
2099
|
-
finalVerification: cap.finalVerification === true || body.finalVerification === true ? true : undefined,
|
|
2100
|
-
attachments: normalizeAttachments(body.attachments),
|
|
2101
|
-
...(taskHooks ? { hooks: composeHooks(deploymentHooks, taskHooks) } : {}),
|
|
2102
|
-
model: picked.model,
|
|
2103
|
-
...((() => {
|
|
2104
|
-
if (typeof body.compactionModel !== "string" || body.compactionModel.length === 0)
|
|
2105
|
-
return {};
|
|
2106
|
-
const cm = matchCatalogModel(body.compactionModel, wireCatalog);
|
|
2107
|
-
if (cm === undefined) {
|
|
2108
|
-
if (opts?.leg === "fresh") {
|
|
2109
|
-
throw new HttpError(400, `unknown compactionModel "${body.compactionModel.slice(0, 120)}" — not in the configured catalog (name, tier word, or id; a catalog refresh may have removed it mid-request)`);
|
|
2110
|
-
}
|
|
2111
|
-
metrics.inc("task_model_unknown_fallback_total");
|
|
2112
|
-
logger.warn("compaction_model_unknown_fallback", { requested: body.compactionModel.slice(0, 120), sessionId: auth?.sessionId ?? null });
|
|
2113
|
-
return {};
|
|
2114
|
-
}
|
|
2115
|
-
return { compactionModel: cm };
|
|
2116
|
-
})()),
|
|
2117
|
-
thinking: effectiveThinking(body.reasoningEffort, parsedSettings.settings?.ultracode === true),
|
|
2118
|
-
getApiKeyAndHeaders: keyResolver,
|
|
2119
|
-
...(() => {
|
|
2120
|
-
const limits = resolveTaskLimits(body.limits, taskLimitCaps, taskTimeoutSec, config.requirePrincipal, body.council === true || body.debate === true || scenarioName === "team");
|
|
2121
|
-
const declarations = [];
|
|
2122
|
-
if (taskLimitCaps.timeoutSec !== undefined)
|
|
2123
|
-
declarations.push({ key: "server.limits.timeoutSecCap", value: String(taskLimitCaps.timeoutSec), reason: "env TASK_TIMEOUT_MAX_SEC (operator ceiling on caller limits.timeoutSec)" });
|
|
2124
|
-
if (taskLimitCaps.maxOutputTokens !== undefined)
|
|
2125
|
-
declarations.push({ key: "server.limits.maxOutputTokensCap", value: String(taskLimitCaps.maxOutputTokens), reason: "env TASK_MAX_OUTPUT_TOKENS_MAX (operator ceiling)" });
|
|
2126
|
-
if (taskLimitCaps.maxTurns !== undefined)
|
|
2127
|
-
declarations.push({ key: "server.limits.maxTurnsCap", value: String(taskLimitCaps.maxTurns), reason: "env TASK_MAX_TURNS_MAX (operator ceiling)" });
|
|
2128
|
-
if (config.maxTaskCostUsd > 0)
|
|
2129
|
-
declarations.push({ key: "server.budget.maxCostUsdCap", value: String(config.maxTaskCostUsd), reason: "env MAX_TASK_COST_USD (operator ceiling; becomes the effective value when the caller sends none)" });
|
|
2130
|
-
if (config.maxTaskTokens > 0)
|
|
2131
|
-
declarations.push({ key: "server.budget.maxTokensCap", value: String(config.maxTaskTokens), reason: "env MAX_TASK_TOKENS_MAX (operator ceiling; becomes the effective value when the caller sends none)" });
|
|
2132
|
-
if (config.requirePrincipal === true)
|
|
2133
|
-
declarations.push({ key: "server.limits.wallClockBaseSec", value: "2400/3600", reason: "tenancy wall-clock base (multi-tenant; big tasks 3600) — TASK_TIMEOUT_SEC raises, never shrinks" });
|
|
2134
|
-
return { ...(limits !== undefined ? { limits } : {}), ...(declarations.length > 0 ? { configOverrides: declarations } : {}) };
|
|
2135
|
-
})(),
|
|
2136
|
-
...(config.requirePrincipal !== true ? { backgroundScope: "session" } : {}),
|
|
2137
|
-
maxCostUsd: cappedCeiling(body.maxCostUsd, config.maxTaskCostUsd),
|
|
2138
|
-
maxTokens: cappedCeiling(body.maxTokens, config.maxTaskTokens),
|
|
2139
|
-
degrade: (() => {
|
|
2140
|
-
if (!config.degrade || cappedCeiling(body.maxCostUsd, config.maxTaskCostUsd) === undefined)
|
|
2141
|
-
return undefined;
|
|
2142
|
-
if (Array.isArray(body.images) && body.images.length > 0 && !config.degrade.toSupportsImages) {
|
|
2143
|
-
metrics.inc("degrade_dropped_total", { reason: "vision_target" });
|
|
2144
|
-
logger.warn("degrade_dropped", { reason: "vision_target", model: picked.model, images: body.images.length });
|
|
2145
|
-
return undefined;
|
|
2146
|
-
}
|
|
2147
|
-
return { to: config.degrade.to, atCostFraction: config.degrade.atCostFraction };
|
|
2148
|
-
})(),
|
|
2149
|
-
memory: memoryEngine ? memorySpecForRequest(auth?.memoryScope, body.memoryWrite, s4DefaultScopes) : undefined,
|
|
2150
|
-
tools: ((base) => {
|
|
2151
|
-
const extra = [...(selectEnvTool ? [selectEnvTool] : []), ...(sendUserFileToolSpec ? [sendUserFileToolSpec] : [])];
|
|
2152
|
-
const merged = extra.length > 0 ? [...(base ?? []), ...extra] : base;
|
|
2153
|
-
return merged && config.toolDeferLongtail ? applyLongtailDefer(merged, true) : merged;
|
|
2154
|
-
})(cap.tools),
|
|
2155
|
-
skills: mergeUserSkills(cap.skills, body.skills, logger),
|
|
2156
|
-
mcp: resolveRequestMcp(mcpForScenario(config.mcpServers, scenarioName), body.mcpServers, config, logger),
|
|
2157
|
-
promptProvider: centerDecls ? centerPromptProvider(centerDecls, scenarioName) : cap.promptProvider,
|
|
2158
|
-
toolPolicy: durableEnabled
|
|
2159
|
-
? combinePolicies(createDurableQuestionPolicy(), createDurableAskPolicy({
|
|
2160
|
-
requireApproval: config.approvalRequire, deny: config.approvalDeny, autoBudget: config.approvalAutoBudget, neverAuto: config.approvalNeverAuto,
|
|
2161
|
-
...(approvalExemptionStore && auth?.sessionId
|
|
2178
|
+
}))
|
|
2179
|
+
: approvalEnabled && approvalStore
|
|
2180
|
+
? createOaApprovalPolicy({
|
|
2181
|
+
store: approvalStore,
|
|
2182
|
+
requireApproval: config.approvalRequire,
|
|
2183
|
+
deny: config.approvalDeny,
|
|
2184
|
+
pollMs: config.approvalPollMs,
|
|
2185
|
+
context: () => ({ sessionId: auth?.sessionId ?? null, owner: auth?.principal ?? null }),
|
|
2186
|
+
neverAuto: config.approvalNeverAuto,
|
|
2187
|
+
...(approvalExemptionStore
|
|
2162
2188
|
? {
|
|
2163
|
-
exempt: (toolName) => approvalExemptionStore.has(
|
|
2164
|
-
onExempted: (
|
|
2189
|
+
exempt: (sessionId, toolName) => approvalExemptionStore.has(sessionId, toolName),
|
|
2190
|
+
onExempted: (sessionId, toolName) => logger.info("approval_exempted", { sessionId, toolName }),
|
|
2165
2191
|
}
|
|
2166
2192
|
: {}),
|
|
2167
|
-
})
|
|
2168
|
-
:
|
|
2169
|
-
?
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
:
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
...(durableEnabled
|
|
2187
|
-
? {
|
|
2188
|
-
checkpointStore,
|
|
2189
|
-
durableApproval: {
|
|
2190
|
-
scope: auth?.principal ?? "_",
|
|
2191
|
-
...(config.approvalTimeoutSec > 0 ? { ttlMs: config.approvalTimeoutSec * 1000 } : {}),
|
|
2192
|
-
},
|
|
2193
|
-
...((rs) => (rs ? { resourceSuspend: rs } : {}))(resourceSuspendOptIn({
|
|
2194
|
-
enabled: config.resourceSuspend,
|
|
2195
|
-
ttlSec: config.resourceSuspendTtlSec,
|
|
2196
|
-
isVerify: body.verify === true,
|
|
2197
|
-
isCascade: body.cascade === true,
|
|
2198
|
-
scope: auth?.principal ?? "_",
|
|
2199
|
-
})),
|
|
2200
|
-
onQuestion: QUESTION_AWAITS_RESUME,
|
|
2201
|
-
}
|
|
2202
|
-
: {}),
|
|
2203
|
-
};
|
|
2204
|
-
const governedBase = applyRuntimeGovernance(spec, { autonomy: config.autonomy, commandPolicy: config.commandPolicy });
|
|
2205
|
-
let governed = governedBase;
|
|
2206
|
-
const bodyMode = coercePermissionMode(body.permissionMode);
|
|
2207
|
-
let effectiveSettings = bodyMode ? withPermissionMode(parsedSettings.settings, bodyMode) : parsedSettings.settings;
|
|
2208
|
-
if (appendLessPack && effectiveSettings?.outputStyle) {
|
|
2209
|
-
logger.warn("output_style_dropped", { detail: "the effective prompt pack cannot mount the append slot (already-assembled systemPrompt or center assembled-identity declaration)", sessionId: auth?.sessionId ?? null });
|
|
2210
|
-
const { outputStyle: _dropped, ...rest } = effectiveSettings;
|
|
2211
|
-
effectiveSettings = Object.keys(rest).length > 0 ? rest : undefined;
|
|
2212
|
-
}
|
|
2213
|
-
if (effectiveSettings?.outputStyle && acceptedAppend && acceptedAppend.length + 2 + effectiveSettings.outputStyle.length > MAX_SETTINGS_OUTPUT_STYLE_CHARS) {
|
|
2214
|
-
logger.warn("output_style_dropped", { detail: `combined append carriers exceed the ${MAX_SETTINGS_OUTPUT_STYLE_CHARS} cap (rider ${acceptedAppend.length} + style ${effectiveSettings.outputStyle.length}) — style dropped (pre-cap stored body on a resume leg?)`, sessionId: auth?.sessionId ?? null });
|
|
2215
|
-
const { outputStyle: _dropped2, ...rest2 } = effectiveSettings;
|
|
2216
|
-
effectiveSettings = Object.keys(rest2).length > 0 ? rest2 : undefined;
|
|
2217
|
-
}
|
|
2218
|
-
const hostSemanticsLane = config.remoteExec === undefined || config.remoteExec.provider === "host";
|
|
2219
|
-
const shellScratchpad = await acceptShellScratchpadDir(body.scratchpadDir, {
|
|
2220
|
-
requirePrincipal: config.requirePrincipal,
|
|
2221
|
-
hostSemanticsLane,
|
|
2222
|
-
warn: (msg, meta) => logger.warn(msg, { ...(meta ?? {}), sessionId: auth?.sessionId ?? null }),
|
|
2223
|
-
});
|
|
2224
|
-
const scratchpadDir = shellScratchpad ??
|
|
2225
|
-
(hostSemanticsLane && auth?.sessionId ? await ensureScratchpadDir(config.localDataRoot ?? localRoot, auth.sessionId) : undefined);
|
|
2226
|
-
if (effectiveSettings) {
|
|
2227
|
-
const fsWriteGate = hostSemanticsLane
|
|
2228
|
-
? (() => {
|
|
2229
|
-
const sessionCwd = auth?.sessionId ? effectiveHostWorkspace(perSessionCwd.get(auth.sessionId), {}) : undefined;
|
|
2230
|
-
const gateCwd = sessionCwd ??
|
|
2231
|
-
(config.remoteExec === undefined
|
|
2232
|
-
? process.cwd()
|
|
2233
|
-
: join(config.localDataRoot ?? localRoot, "fs-write-gate-unrooted"));
|
|
2234
|
-
return {
|
|
2235
|
-
env: new NodeExecutionEnv({ cwd: gateCwd }),
|
|
2236
|
-
cwd: gateCwd,
|
|
2237
|
-
...(scratchpadDir ? { scratchpadDir } : {}),
|
|
2238
|
-
...(config.sensitiveWritePatterns.length > 0 ? { sensitivePatterns: config.sensitiveWritePatterns } : {}),
|
|
2239
|
-
...(config.manualModeShellGate ? { shellGate: config.manualModeShellGate } : {}),
|
|
2240
|
-
...(approvalExemptionStore && auth?.sessionId
|
|
2241
|
-
? {
|
|
2242
|
-
isExempt: async (toolName) => {
|
|
2243
|
-
const hit = await approvalExemptionStore.has(auth.sessionId, toolName);
|
|
2244
|
-
if (hit)
|
|
2245
|
-
logger.info("fs_write_gate_exempted", { toolName, sessionId: auth.sessionId });
|
|
2246
|
-
return hit;
|
|
2247
|
-
},
|
|
2248
|
-
}
|
|
2249
|
-
: {}),
|
|
2250
|
-
};
|
|
2251
|
-
})()
|
|
2252
|
-
: undefined;
|
|
2253
|
-
const workflowGate = approvalExemptionStore && auth?.sessionId
|
|
2254
|
-
? {
|
|
2255
|
-
isExempt: async (toolName) => {
|
|
2256
|
-
const hit = await approvalExemptionStore.has(auth.sessionId, toolName);
|
|
2257
|
-
if (hit)
|
|
2258
|
-
logger.info("workflow_gate_exempted", { toolName, sessionId: auth.sessionId });
|
|
2259
|
-
return hit;
|
|
2260
|
-
},
|
|
2261
|
-
}
|
|
2262
|
-
: undefined;
|
|
2263
|
-
try {
|
|
2264
|
-
governed = applyTaskSettings(governedBase, effectiveSettings, fsWriteGate, workflowGate);
|
|
2193
|
+
})
|
|
2194
|
+
: singleUserAutoAcceptBaseline
|
|
2195
|
+
? createAllowDenyPolicy({})
|
|
2196
|
+
: undefined,
|
|
2197
|
+
...(durableEnabled
|
|
2198
|
+
? {
|
|
2199
|
+
checkpointStore,
|
|
2200
|
+
durableApproval: {
|
|
2201
|
+
scope: auth?.principal ?? "_",
|
|
2202
|
+
...(config.approvalTimeoutSec > 0 ? { ttlMs: config.approvalTimeoutSec * 1000 } : {}),
|
|
2203
|
+
},
|
|
2204
|
+
...((rs) => (rs ? { resourceSuspend: rs } : {}))(resourceSuspendOptIn({
|
|
2205
|
+
enabled: config.resourceSuspend,
|
|
2206
|
+
ttlSec: config.resourceSuspendTtlSec,
|
|
2207
|
+
isVerify: body.verify === true,
|
|
2208
|
+
isCascade: body.cascade === true,
|
|
2209
|
+
scope: auth?.principal ?? "_",
|
|
2210
|
+
})),
|
|
2211
|
+
onQuestion: QUESTION_AWAITS_RESUME,
|
|
2265
2212
|
}
|
|
2266
|
-
|
|
2267
|
-
|
|
2213
|
+
: {}),
|
|
2214
|
+
};
|
|
2215
|
+
const governedBase = applyRuntimeGovernance(spec, { autonomy: config.autonomy, commandPolicy: config.commandPolicy });
|
|
2216
|
+
let governed = governedBase;
|
|
2217
|
+
const bodyMode = coercePermissionMode(body.permissionMode);
|
|
2218
|
+
let effectiveSettings = bodyMode ? withPermissionMode(parsedSettings.settings, bodyMode) : parsedSettings.settings;
|
|
2219
|
+
if (appendLessPack && effectiveSettings?.outputStyle) {
|
|
2220
|
+
logger.warn("output_style_dropped", { detail: "the effective prompt pack cannot mount the append slot (already-assembled systemPrompt or center assembled-identity declaration)", sessionId: auth?.sessionId ?? null });
|
|
2221
|
+
const { outputStyle: _dropped, ...rest } = effectiveSettings;
|
|
2222
|
+
effectiveSettings = Object.keys(rest).length > 0 ? rest : undefined;
|
|
2223
|
+
}
|
|
2224
|
+
if (effectiveSettings?.outputStyle && acceptedAppend && acceptedAppend.length + 2 + effectiveSettings.outputStyle.length > MAX_SETTINGS_OUTPUT_STYLE_CHARS) {
|
|
2225
|
+
logger.warn("output_style_dropped", { detail: `combined append carriers exceed the ${MAX_SETTINGS_OUTPUT_STYLE_CHARS} cap (rider ${acceptedAppend.length} + style ${effectiveSettings.outputStyle.length}) — style dropped (pre-cap stored body on a resume leg?)`, sessionId: auth?.sessionId ?? null });
|
|
2226
|
+
const { outputStyle: _dropped2, ...rest2 } = effectiveSettings;
|
|
2227
|
+
effectiveSettings = Object.keys(rest2).length > 0 ? rest2 : undefined;
|
|
2228
|
+
}
|
|
2229
|
+
const hostSemanticsLane = config.remoteExec === undefined || config.remoteExec.provider === "host";
|
|
2230
|
+
const shellScratchpad = await acceptShellScratchpadDir(body.scratchpadDir, {
|
|
2231
|
+
requirePrincipal: config.requirePrincipal,
|
|
2232
|
+
hostSemanticsLane,
|
|
2233
|
+
warn: (msg, meta) => logger.warn(msg, { ...(meta ?? {}), sessionId: auth?.sessionId ?? null }),
|
|
2234
|
+
});
|
|
2235
|
+
const scratchpadDir = shellScratchpad ??
|
|
2236
|
+
(hostSemanticsLane && auth?.sessionId ? await ensureScratchpadDir(config.localDataRoot ?? localRoot, auth.sessionId) : undefined);
|
|
2237
|
+
if (effectiveSettings) {
|
|
2238
|
+
const fsWriteGate = hostSemanticsLane
|
|
2239
|
+
? (() => {
|
|
2240
|
+
const sessionCwd = auth?.sessionId ? effectiveHostWorkspace(perSessionCwd.get(auth.sessionId), {}) : undefined;
|
|
2241
|
+
const gateCwd = sessionCwd ??
|
|
2242
|
+
(config.remoteExec === undefined
|
|
2243
|
+
? process.cwd()
|
|
2244
|
+
: join(config.localDataRoot ?? localRoot, "fs-write-gate-unrooted"));
|
|
2245
|
+
return {
|
|
2246
|
+
env: new NodeExecutionEnv({ cwd: gateCwd }),
|
|
2247
|
+
cwd: gateCwd,
|
|
2248
|
+
...(scratchpadDir ? { scratchpadDir } : {}),
|
|
2249
|
+
...(config.sensitiveWritePatterns.length > 0 ? { sensitivePatterns: config.sensitiveWritePatterns } : {}),
|
|
2250
|
+
...(config.manualModeShellGate ? { shellGate: config.manualModeShellGate } : {}),
|
|
2251
|
+
...(approvalExemptionStore && auth?.sessionId
|
|
2252
|
+
? {
|
|
2253
|
+
isExempt: async (toolName) => {
|
|
2254
|
+
const hit = await approvalExemptionStore.has(auth.sessionId, toolName);
|
|
2255
|
+
if (hit)
|
|
2256
|
+
logger.info("fs_write_gate_exempted", { toolName, sessionId: auth.sessionId });
|
|
2257
|
+
return hit;
|
|
2258
|
+
},
|
|
2259
|
+
}
|
|
2260
|
+
: {}),
|
|
2261
|
+
};
|
|
2262
|
+
})()
|
|
2263
|
+
: undefined;
|
|
2264
|
+
const workflowGate = approvalExemptionStore && auth?.sessionId
|
|
2265
|
+
? {
|
|
2266
|
+
isExempt: async (toolName) => {
|
|
2267
|
+
const hit = await approvalExemptionStore.has(auth.sessionId, toolName);
|
|
2268
|
+
if (hit)
|
|
2269
|
+
logger.info("workflow_gate_exempted", { toolName, sessionId: auth.sessionId });
|
|
2270
|
+
return hit;
|
|
2271
|
+
},
|
|
2268
2272
|
}
|
|
2273
|
+
: undefined;
|
|
2274
|
+
try {
|
|
2275
|
+
governed = applyTaskSettings(governedBase, effectiveSettings, fsWriteGate, workflowGate);
|
|
2269
2276
|
}
|
|
2270
|
-
|
|
2271
|
-
|
|
2277
|
+
catch (e) {
|
|
2278
|
+
throw new HttpError(422, `settings are tighten-only and cannot loosen the deployment policy: ${e.message}`);
|
|
2272
2279
|
}
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
if (!auth?.sessionId) {
|
|
2284
|
-
throw new HttpError(400, "sandboxImageProfile requires a resolved session");
|
|
2285
|
-
}
|
|
2286
|
-
const capsNeeded = Array.isArray(body.capabilitiesNeeded)
|
|
2287
|
-
? body.capabilitiesNeeded.filter((c) => typeof c === "string")
|
|
2288
|
-
: undefined;
|
|
2289
|
-
const resolved = await resolveSandboxImageRef({
|
|
2290
|
-
profile: taskImageProfile,
|
|
2291
|
-
...(capsNeeded && capsNeeded.length > 0 ? { capabilitiesNeeded: capsNeeded } : {}),
|
|
2292
|
-
viewer: { operator: explicitOperator(auth.principal, config.operatorPrincipals), tenantId: auth.principal ?? null },
|
|
2293
|
-
index: imageIndex,
|
|
2294
|
-
});
|
|
2295
|
-
if (!resolved.ok)
|
|
2296
|
-
throw new HttpError(resolved.status, resolved.message);
|
|
2297
|
-
perTaskImage.set(auth.sessionId, resolved.ref);
|
|
2298
|
-
resolvedImageCaps = resolved.capabilities;
|
|
2280
|
+
}
|
|
2281
|
+
if (governed.handsReadOnly === true && Array.isArray(governed.tools)) {
|
|
2282
|
+
governed = { ...governed, tools: stripDelegationTools(governed.tools) };
|
|
2283
|
+
}
|
|
2284
|
+
const bodyProfile = typeof body.sandboxImageProfile === "string" && body.sandboxImageProfile.length > 0 ? body.sandboxImageProfile : undefined;
|
|
2285
|
+
const taskImageProfile = bodyProfile ?? (body.cascade === true || body.verify === true ? undefined : sessionEnvSelection.get(auth?.sessionId));
|
|
2286
|
+
let resolvedImageCaps;
|
|
2287
|
+
if (taskImageProfile !== undefined) {
|
|
2288
|
+
if (config.remoteExec?.provider !== "k8s") {
|
|
2289
|
+
throw new HttpError(400, "sandboxImageProfile is only supported on the k8s sandbox backend");
|
|
2299
2290
|
}
|
|
2300
|
-
if (
|
|
2301
|
-
|
|
2302
|
-
const remoteScratchpadDir = !hostSemanticsLane && isRemoteScratchpadLane(config.remoteExec?.provider)
|
|
2303
|
-
? remoteScratchpadDirFor(auth?.sessionId)
|
|
2304
|
-
: undefined;
|
|
2305
|
-
const factScratchpadDir = scratchpadDir ?? remoteScratchpadDir;
|
|
2306
|
-
const facts = buildEnvFacts({
|
|
2307
|
-
profile: taskImageProfile,
|
|
2308
|
-
capabilities: resolvedImageCaps,
|
|
2309
|
-
...(pkgSourceLane ? { pkgSource: config.sandboxPkgSource } : {}),
|
|
2310
|
-
egress: egressForRemoteExec(config.remoteExec),
|
|
2311
|
-
...(factScratchpadDir ? { scratchpadDir: factScratchpadDir } : {}),
|
|
2312
|
-
resumeFacts: resumeFactsForLane(config.remoteExec?.provider, config.remoteExec?.provider === "k8s" ? { k8sSnapshot: Boolean(config.remoteExec.s3Snapshot) } : undefined),
|
|
2313
|
-
});
|
|
2314
|
-
if (facts)
|
|
2315
|
-
governed = { ...governed, envFacts: facts };
|
|
2291
|
+
if (!imageIndex) {
|
|
2292
|
+
throw new HttpError(400, "sandboxImageProfile requires the image index (sema-registry backend not configured)");
|
|
2316
2293
|
}
|
|
2317
|
-
if (
|
|
2318
|
-
|
|
2319
|
-
if (!dirs.includes(scratchpadDir))
|
|
2320
|
-
governed = { ...governed, additionalDirectories: [...dirs, scratchpadDir] };
|
|
2294
|
+
if (!auth?.sessionId) {
|
|
2295
|
+
throw new HttpError(400, "sandboxImageProfile requires a resolved session");
|
|
2321
2296
|
}
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2297
|
+
const capsNeeded = Array.isArray(body.capabilitiesNeeded)
|
|
2298
|
+
? body.capabilitiesNeeded.filter((c) => typeof c === "string")
|
|
2299
|
+
: undefined;
|
|
2300
|
+
const resolved = await resolveSandboxImageRef({
|
|
2301
|
+
profile: taskImageProfile,
|
|
2302
|
+
...(capsNeeded && capsNeeded.length > 0 ? { capabilitiesNeeded: capsNeeded } : {}),
|
|
2303
|
+
viewer: { operator: explicitOperator(auth.principal, config.operatorPrincipals), tenantId: auth.principal ?? null },
|
|
2304
|
+
index: imageIndex,
|
|
2305
|
+
});
|
|
2306
|
+
if (!resolved.ok)
|
|
2307
|
+
throw new HttpError(resolved.status, resolved.message);
|
|
2308
|
+
perTaskImage.set(auth.sessionId, resolved.ref);
|
|
2309
|
+
resolvedImageCaps = resolved.capabilities;
|
|
2310
|
+
}
|
|
2311
|
+
if (config.envFactsEnabled) {
|
|
2312
|
+
const pkgSourceLane = config.remoteExec?.provider === "e2b" || config.remoteExec?.provider === "k8s";
|
|
2313
|
+
const remoteScratchpadDir = !hostSemanticsLane && isRemoteScratchpadLane(config.remoteExec?.provider)
|
|
2314
|
+
? remoteScratchpadDirFor(auth?.sessionId)
|
|
2315
|
+
: undefined;
|
|
2316
|
+
const factScratchpadDir = scratchpadDir ?? remoteScratchpadDir;
|
|
2317
|
+
const facts = buildEnvFacts({
|
|
2318
|
+
profile: taskImageProfile,
|
|
2319
|
+
capabilities: resolvedImageCaps,
|
|
2320
|
+
...(pkgSourceLane ? { pkgSource: config.sandboxPkgSource } : {}),
|
|
2321
|
+
egress: egressForRemoteExec(config.remoteExec),
|
|
2322
|
+
...(factScratchpadDir ? { scratchpadDir: factScratchpadDir } : {}),
|
|
2323
|
+
resumeFacts: resumeFactsForLane(config.remoteExec?.provider, config.remoteExec?.provider === "k8s" ? { k8sSnapshot: Boolean(config.remoteExec.s3Snapshot) } : undefined),
|
|
2324
|
+
});
|
|
2325
|
+
if (facts)
|
|
2326
|
+
governed = { ...governed, envFacts: facts };
|
|
2327
|
+
}
|
|
2328
|
+
if (scratchpadDir !== undefined) {
|
|
2329
|
+
const dirs = governed.additionalDirectories ?? [];
|
|
2330
|
+
if (!dirs.includes(scratchpadDir))
|
|
2331
|
+
governed = { ...governed, additionalDirectories: [...dirs, scratchpadDir] };
|
|
2332
|
+
}
|
|
2333
|
+
if (config.routerEnabled) {
|
|
2334
|
+
const isolatedExecEnv = isIsolatedExecEnv(config.remoteExec?.provider, config.remoteExec?.provider === "k8s" ? config.remoteExec.runtimeClass : undefined);
|
|
2335
|
+
const decision = routeServiceTask({
|
|
2336
|
+
isolatedExecEnv,
|
|
2337
|
+
hasExecutionEnv: config.remoteExec != null,
|
|
2338
|
+
...(config.autonomy ? { autonomy: config.autonomy } : {}),
|
|
2339
|
+
explicitTeam: body.council === true || body.debate === true || scenarioName === "team",
|
|
2340
|
+
});
|
|
2341
|
+
logger.info("orchestration_routed", { mode: decision.mode, reason: decision.reason, sessionId: auth?.sessionId ?? null });
|
|
2342
|
+
if (decision.mode === "supervisor") {
|
|
2343
|
+
return tightenTaskSpec(governed, supPostureOverrides());
|
|
2334
2344
|
}
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
}
|
|
2345
|
+
}
|
|
2346
|
+
return governed;
|
|
2347
|
+
};
|
|
2348
|
+
const server = createHttpServer({ runner, config, resolveSpec, stores, coordinators, seams, observability, governance, deployment, knobs });
|
|
2338
2349
|
runDenySweep = server.denyExpiredApprovals;
|
|
2339
2350
|
const bindHost = resolveBindHost(config);
|
|
2340
2351
|
await new Promise((resolve) => (bindHost ? server.listen(config.port, bindHost, resolve) : server.listen(config.port, resolve)));
|