/** * Workflow super-set unlock — map the per-task `body.selfOrchestration` intent onto * `TaskSpec.selfOrchestration`, the gate that mounts core's LLM-authored `run_workflow` engine (the workflow.ts * agent/parallel/pipeline orchestrator). Same shape as the rank-1 roster gap: the engine (core run_workflow + * workflow.ts) is BUILT, but nothing mapped a body field to the per-task spec flag, so it was unreachable over HTTP * (capabilities advertised `workflows:true` but the LLM could never call run_workflow). * * Gating: * - DEPLOYMENT: `selfOrchestrationEnabled` must be on (the workflowScriptRunner + stores are wired). Otherwise a set * flag is a harmless no-op (core fail-closes run_workflow without the sandbox runner), so it's dropped for clarity. * - MULTI-TENANT: honored ONLY when a per-principal entitlement RESOLVER is wired (`hasEntitlementResolver`). 🔴 core's * `allowWorkflows` (RuntimeCaps) is TIGHTEN-ONLY — a resolver can only DENY; `undefined` = NO restriction, so the * deployment capability governs. Therefore WITHOUT a resolver the engine would mount run_workflow UNGATED for every * tenant. So we fail-closed here (defense-in-depth: a misconfigured multi-tenant deployment with no entitlement * source must NOT leak the LLM-authored workflow engine). WITH a resolver, core enforces `allowWorkflows` * per-principal (deny → fail-closed). Single-user (owns its own worker) honors directly. Three-gate: engine-can ∧ * center-may(allowWorkflows) ∧ shell-show. * * 🔴 [ref](2026-09-03):fail-closed 的**方向**从来是对的,错的是它**没人说**。多租户 + 无 entitlement 源 * 的部署上,这条腿把 `body.selfOrchestration:true`(以及 `settings.ultracode` 展开出来的同一个意图)静默 * 折成 `undefined`,而 `/v1/capabilities` 照报 `workflows:true` ⇒ 「壳显示支持、引擎按普通 run 跑」。修法 * 是把部署级半场抽成下面的 {@link selfOrchestrationDenial}(能力位与准入**同一只**谓词),提交面在拒因 * 在场时**响亮**拒(`routes` 的 501 `capability.self_orchestration_required`),不再静默折。 */ export declare function selfOrchestrationFromBody(body: { selfOrchestration?: unknown; }, config: SelfOrchestrationGateConfig, hasEntitlementResolver: boolean): true | undefined; /** 本模块读的部署面(`selfOrchestrationEnabled` 恒在真 config 上;遗留双重断言 stub 可能真缺,故一律 `!== true` 读)。 */ export interface SelfOrchestrationGateConfig { selfOrchestrationEnabled: boolean; requirePrincipal?: boolean; } /** * [ref](DEBTS,P1)—— 自编排准入的**部署级拒因**闭集。`null` = 本部署这一层不拒(是否真跑还看 engine-can * 与 core 的 per-principal `allowWorkflows`)。 * * 唯一成员 `"entitlement_resolver_absent"`:多租户(`requirePrincipal === true`)+ 运维**显式开了** * `SELF_ORCHESTRATION_ENABLED` + 没有任何 per-principal entitlement 源(`hasEntitlementResolver` —— 判据由 * 调用方从 `centerEntitlementSourceWired(config)` / 装配好的 caps 客户端取)。这一形下上面的准入腿对 * **每一个** principal 都 fail-closed(理由见函数上方注:core 的 `allowWorkflows` 是 tighten-only, * resolver 缺席 = 无限制,所以本层必须自己关死),于是这台机器上的自编排是**结构性死的**。 * * 🔴 为什么这不是布尔而是**闭集拒因**:能力面要能对壳说清「为什么暗」——「这份二进制/这台机器没有工作流 * 引擎」与「引擎在、但本部署配不出 per-principal 授权」是两件事,壳对它们要做的事不同(前者别给这一格; * 后者该提示运维接 center)。合成一个布尔就把这两件事打成同一个 false —— 与 `permissionModeAuto` 那一位 * 「三项不合成一个布尔」同判据。 * * 🔴 为什么 `selfOrchestrationEnabled` 关着时返回 `null` 而不是另一个拒因:那是**运维刻意的姿态** * (多租户缺省就是关),能力面的 engine-can 半场自己就已经诚实为假;而本拒因说的是**半配置** * (阀门开着而配不出来),两者的处置不同——后者才需要点名。这也正是本件的补偿旋钮:把 * `SELF_ORCHESTRATION_ENABLED` 关掉(对这台机器而言是唯一诚实的姿态)即刻回到姿态形。 */ export type SelfOrchestrationDenial = "entitlement_resolver_absent"; export declare function selfOrchestrationDenial(config: SelfOrchestrationGateConfig, hasEntitlementResolver: boolean): SelfOrchestrationDenial | null; /** * §4 (Fork) — map the per-task `body.enableFork` intent onto `TaskSpec.enableFork`. core 1.257 ([ref] BREAKING) * RETIRED the standalone `Fork` tool; a fork is now `Agent(subagent_type:"fork")`, and `enableFork` is one of the two * governance axes (`forkGovernanceDenial(spec.enableFork, runtimeCaps.allowFork)`) that gate it. * * 🔴 CONSUME-CRITICAL — the default FLIPPED to opt-OUT: core now treats `enableFork === undefined` as **available** * (only an EXPLICIT `enableFork:false` / `allowFork:false` denies — CC's default posture). So where the SERVICE intends * to DENY, it must return `false` EXPLICITLY — the old "return undefined = not granted" is now fail-OPEN. * - DEPLOYMENT posture OFF (`!forkEnabled`, e.g. multi-tenant default): `false` (explicit deny — preserve opt-in posture). * - PER-TASK OPT-OUT (`body.enableFork === false`): `false` (any tenancy). * - SINGLE-USER TURNKEY: `true` — DEFAULT ON for the LLM (clay 2026-07-01). (core still only lets it fork where the * session store is fork-capable and never inside a fork / read-only band — graceful degrade, no capability lie.) * - MULTI-TENANT: `true` ONLY with a per-principal entitlement RESOLVER wired (core then enforces `allowFork` * tighten-only per-principal); WITHOUT a resolver `false` (fail-closed — `allowFork` is tighten-only so an absent * resolver = `undefined` = ungated, and now `enableFork:undefined` also auto-grants, so the service must hard-deny). */ export declare function enableForkFromBody(body: { enableFork?: unknown; }, config: { forkEnabled: boolean; requirePrincipal?: boolean; }, hasEntitlementResolver: boolean): boolean; /** * The workflow-script MODEL allowlist (`agent({model})` picks). core is * fail-closed: NO allowlist configured ⇒ `WorkflowModelNotAllowedError` on ANY model pick, which on a TOC * single-user deployment broke clay's own dual-model scripts (决策 v4-pro + 执行 v4-flash) out of the box — * the operator configured every catalog entry themselves, so there is nothing to protect them from. * - Explicit `SELF_ORCHESTRATION_MODELS` always wins (any tenancy — the operator's deliberate narrowing). * - SINGLE-USER: default to the deployment's OWN model catalog (`Object.keys(models)`). * - MULTI-TENANT: keep core's fail-closed empty default (a model pick is a cost/capability grant the * operator must make explicitly; a tenant script must not self-select the expensive rung). * Returns undefined when no allowlist should be set (core then fail-closes picks; the script's `agent()` * without a model still runs on the workflow's default role — only PICKING is gated). */ export declare function workflowModelAllowlistFor(config: { selfOrchestrationModels: string[]; requirePrincipal?: boolean; models: Record; }): string[] | undefined; /** [ref] caps for a caller-supplied `retainSubagentSessions` object (core defaults 30min/16; the ledger * is run-scoped and fully released at parent end, so these only bound the WITHIN-RUN memory pin). */ export declare const RETAIN_SUBAGENT_TTL_MS_CAP: number; export declare const RETAIN_SUBAGENT_MAX_CAP = 64; /** * [ref] (core 1.225): normalize an untrusted `body.retainSubagentSessions` onto the TaskSpec field. * `true`/`false` pass through (core's own defaults apply); an object keeps only NUMERIC ttlMs/max, each * clamped to a positive integer under the cap (retention pins settled child sessions in memory for the * parent run's lifetime — the knob must not become a caller-controlled resource hold). Anything malformed → * undefined = OFF (core default). Defensive, never throws: RESUME re-runs resolveSpec WITHOUT the HTTP-layer * validation, so this normalizer is the single guard on every path. */ export declare function normalizeRetainSubagentSessions(raw: unknown): boolean | { ttlMs?: number; max?: number; } | undefined; /** * Per-task wall clock by TENANCY POSTURE (clay 2026-07-04, make-real 教训①:600s 默认墙钟截停了健康推进的 * 长任务/chaos 恢复 run): * - SINGLE-USER turnkey(`requirePrincipal !== true`)= the user IS the superadmin → **NO wall clock** * (undefined; core arms none — its separate wedge safety-net still catches a stuck tool call, so "no wall" * never means "hangs forever"). CC has no whole-task decapitation either. * - MULTI-TENANT: base 2400s (big tasks — council/debate/discuss — 3600s). (PAIR-REVIEW F-4 附带修:此行 * 曾写 600s/1500s 与代码漂移——真值以下方 base 为准。) * - Explicit `TASK_TIMEOUT_SEC` wins in ANY tenancy (the operator's deliberate wall) and keeps its documented * raise-only floor (`Math.max` with the base, so a misconfigured low value cannot shrink a council budget). */ export declare function taskWallClockSec(envTimeoutSec: number, requirePrincipal: boolean | undefined, big: boolean): number | undefined; //# sourceMappingURL=task-workflow.d.ts.map