@sema-agent/server 7.105.0 → 7.106.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.
Files changed (174) hide show
  1. package/MIGRATION.md +82 -0
  2. package/USAGE.md +134 -42
  3. package/deploy/sema-up/chart/templates/server-v4pro.yaml +2 -0
  4. package/deploy/sema-up/cluster-up.sh +15 -2
  5. package/deploy/sema-up/kube-up.sh +8 -3
  6. package/deploy/sema-up/lib/flagshape.sh +29 -0
  7. package/deploy/sema-up/lib/parse-machines.py +4 -1
  8. package/deploy/sema-up/sema-up.sh +11 -9
  9. package/deploy/sema-up/stack/sema-stack-cluster.yaml +3 -1
  10. package/dist/approval-card.d.ts +1 -1
  11. package/dist/approval.d.ts +6 -1
  12. package/dist/approval.js +4 -4
  13. package/dist/auth-bridge.d.ts +14 -0
  14. package/dist/auth-bridge.js +7 -0
  15. package/dist/auto-mode-face.d.ts +49 -40
  16. package/dist/auto-mode-face.js +6 -68
  17. package/dist/bench/s1/live-deps.d.ts +9 -2
  18. package/dist/bench/s1/live-deps.js +13 -8
  19. package/dist/boot/budget-tracing.js +3 -2
  20. package/dist/boot/config-center.d.ts +4 -0
  21. package/dist/boot/config-center.js +95 -37
  22. package/dist/boot/coordinators.d.ts +1 -0
  23. package/dist/boot/coordinators.js +3 -1
  24. package/dist/boot/device-lane.d.ts +2 -1
  25. package/dist/boot/execution-env.d.ts +11 -0
  26. package/dist/boot/execution-env.js +30 -6
  27. package/dist/boot/execution-lane.js +2 -3
  28. package/dist/boot/leader.d.ts +2 -1
  29. package/dist/boot/leader.js +2 -0
  30. package/dist/boot/parked-revive-gate.d.ts +10 -7
  31. package/dist/boot/parked-revive-gate.js +4 -11
  32. package/dist/boot/resolve-spec.d.ts +1 -4
  33. package/dist/boot/resolve-spec.js +44 -21
  34. package/dist/boot/runner-deps.d.ts +5 -8
  35. package/dist/boot/runner-deps.js +3 -9
  36. package/dist/boot/side-query-lane.d.ts +2 -2
  37. package/dist/boot/stage-03-store-backend.d.ts +1 -1
  38. package/dist/boot/stage-03-store-backend.js +2 -2
  39. package/dist/boot/stage-04-stores.d.ts +1 -1
  40. package/dist/boot/stage-05-execution-env.d.ts +2 -1
  41. package/dist/boot/stage-05-execution-env.js +2 -2
  42. package/dist/boot/stage-06-runners.d.ts +3 -2
  43. package/dist/boot/stage-06-runners.js +2 -5
  44. package/dist/boot/stage-07-capability-layer.d.ts +2 -2
  45. package/dist/boot/stage-07-capability-layer.js +1 -2
  46. package/dist/boot/stage-08-reapers.d.ts +2 -2
  47. package/dist/boot/stage-09-leader.d.ts +2 -2
  48. package/dist/boot/stage-09-leader.js +2 -2
  49. package/dist/boot/stage-10-http-server.d.ts +2 -2
  50. package/dist/boot/stage-10-http-server.js +11 -7
  51. package/dist/boot/stores.d.ts +1 -1
  52. package/dist/boot/stores.js +6 -4
  53. package/dist/brain.d.ts +24 -2
  54. package/dist/brain.js +29 -10
  55. package/dist/budget.js +3 -0
  56. package/dist/capabilities/center-wiring.d.ts +64 -0
  57. package/dist/capabilities/center-wiring.js +24 -0
  58. package/dist/capabilities/composite-members.d.ts +89 -0
  59. package/dist/capabilities/composite-members.js +20 -0
  60. package/dist/capabilities/execution-lane.d.ts +3 -0
  61. package/dist/capabilities/execution-lane.js +5 -2
  62. package/dist/config-catalog.d.ts +30 -5
  63. package/dist/config-catalog.js +75 -26
  64. package/dist/config-center/apply-effective.d.ts +1 -1
  65. package/dist/config-center/apply-effective.js +7 -8
  66. package/dist/config-center/apply-ledger.d.ts +17 -0
  67. package/dist/config-center/apply-ledger.js +5 -0
  68. package/dist/config-center/center-request.d.ts +17 -0
  69. package/dist/config-center/center-request.js +4 -0
  70. package/dist/config-center/pull-state.d.ts +29 -0
  71. package/dist/config-center/pull-state.js +2 -0
  72. package/dist/config-types.d.ts +41 -14
  73. package/dist/config-types.js +1 -0
  74. package/dist/config.d.ts +35 -0
  75. package/dist/config.js +69 -16
  76. package/dist/cross-session-settings.d.ts +1 -1
  77. package/dist/deployment-governance.d.ts +12 -1
  78. package/dist/deployment-governance.js +2 -0
  79. package/dist/execution-lane-caps.d.ts +30 -2
  80. package/dist/execution-lane-caps.js +12 -0
  81. package/dist/fleet-client.d.ts +18 -13
  82. package/dist/fleet-client.js +32 -22
  83. package/dist/fleet-lease.d.ts +72 -47
  84. package/dist/fleet-lease.js +66 -14
  85. package/dist/governance-http-status.d.ts +0 -1
  86. package/dist/governance-http-status.js +0 -1
  87. package/dist/hosted-posture.d.ts +6 -17
  88. package/dist/hosted-posture.js +11 -6
  89. package/dist/http/admission.js +14 -9
  90. package/dist/http/approval-sweeps.js +13 -1
  91. package/dist/http/dispatch.js +1 -1
  92. package/dist/http/durable-run-leg.js +2 -0
  93. package/dist/http/request-guards.d.ts +25 -0
  94. package/dist/http/request-guards.js +25 -5
  95. package/dist/http/resume-legs.js +19 -11
  96. package/dist/http/route-ctx.d.ts +9 -0
  97. package/dist/http/routes/approvals-assistant.js +0 -1
  98. package/dist/http/routes/capabilities.js +12 -11
  99. package/dist/http/routes/config-catalog.js +2 -1
  100. package/dist/http/routes/diagnostics.js +2 -0
  101. package/dist/http/routes/tasks.js +4 -1
  102. package/dist/http/server.d.ts +20 -4
  103. package/dist/http/session-inherit-env.d.ts +37 -0
  104. package/dist/http/session-inherit-env.js +6 -0
  105. package/dist/http/wire-types.d.ts +19 -28
  106. package/dist/index.d.ts +2 -1
  107. package/dist/index.js +2 -1
  108. package/dist/key-resolver.d.ts +74 -19
  109. package/dist/key-resolver.js +65 -7
  110. package/dist/leader/leader.d.ts +6 -0
  111. package/dist/leader/leader.js +2 -1
  112. package/dist/leader/repair-wire.js +2 -1
  113. package/dist/leader/wire.d.ts +9 -1
  114. package/dist/leader/wire.js +10 -6
  115. package/dist/observability/fail-open.d.ts +5 -1
  116. package/dist/observability/fail-open.js +5 -1
  117. package/dist/observability/metrics.js +2 -0
  118. package/dist/observability/permission-rule-events.d.ts +1 -1
  119. package/dist/observability/permission-rule-events.js +1 -1
  120. package/dist/observability/run-terminal.d.ts +38 -1
  121. package/dist/observability/run-terminal.js +2 -5
  122. package/dist/observability/secret-env-scrub.js +1 -1
  123. package/dist/observability/tool-trace.d.ts +2 -0
  124. package/dist/operator-ask.d.ts +29 -1
  125. package/dist/operator-ask.js +6 -0
  126. package/dist/parked-decide.d.ts +14 -1
  127. package/dist/parked-decide.js +8 -1
  128. package/dist/plugins/session-policy-store-sql.d.ts +38 -7
  129. package/dist/plugins/session-policy-store-sql.js +64 -22
  130. package/dist/plugins/sql-driver.d.ts +1 -1
  131. package/dist/plugins/store-backend.d.ts +12 -3
  132. package/dist/plugins/store-backend.js +16 -5
  133. package/dist/plugins/tidb-pool.js +1 -1
  134. package/dist/read-face-posture.d.ts +4 -4
  135. package/dist/request-key-closure.js +2 -2
  136. package/dist/request-value-closure.d.ts +17 -5
  137. package/dist/request-value-closure.js +12 -0
  138. package/dist/router/route-orchestration.d.ts +11 -0
  139. package/dist/router/route-orchestration.js +7 -6
  140. package/dist/run-local.js +4 -2
  141. package/dist/runs.d.ts +2 -1
  142. package/dist/runs.js +4 -4
  143. package/dist/runtime-caps-resolver.d.ts +4 -10
  144. package/dist/runtime-governance.d.ts +8 -11
  145. package/dist/runtime-governance.js +10 -41
  146. package/dist/sealed-key.d.ts +0 -40
  147. package/dist/sealed-key.js +0 -20
  148. package/dist/server-secret-env.d.ts +11 -12
  149. package/dist/session-policy-wire.d.ts +5 -3
  150. package/dist/session-titler.d.ts +11 -0
  151. package/dist/session-titler.js +6 -2
  152. package/dist/sql-engine-posture.d.ts +2 -2
  153. package/dist/store-posture.d.ts +53 -0
  154. package/dist/store-posture.js +32 -0
  155. package/dist/task-settings.d.ts +34 -45
  156. package/dist/task-settings.js +46 -25
  157. package/dist/tool-approval.d.ts +51 -22
  158. package/dist/tool-approval.js +44 -8
  159. package/dist/tool-declaration-http-status.d.ts +12 -0
  160. package/dist/tool-declaration-http-status.js +12 -0
  161. package/dist/trace/core-keyset-guard.d.ts +7 -7
  162. package/dist/trace/engine-notice-wire.d.ts +1 -1
  163. package/dist/trace/engine-notice-wire.js +5 -0
  164. package/dist/trace/project.d.ts +3 -2
  165. package/dist/trace/project.js +51 -3
  166. package/dist/trace/wire-projection-faces.d.ts +1 -1
  167. package/dist/trace/wire-projection-faces.js +1 -1
  168. package/dist/unattended-permission-mode.d.ts +110 -0
  169. package/dist/unattended-permission-mode.js +65 -0
  170. package/package.json +3 -3
  171. package/dist/boot/session-shell-gate-registry.d.ts +0 -39
  172. package/dist/boot/session-shell-gate-registry.js +0 -21
  173. package/dist/roster-bound-http-status.d.ts +0 -6
  174. package/dist/roster-bound-http-status.js +0 -6
@@ -22,7 +22,7 @@ import { buildSealedKeyPublicFace, ensureSealedKeyStore, reportExecutionPublicKe
22
22
  import { applyEffective, applyCenterReadFace, mutateInPlace, logEffectiveDiff, applyCenterSkills, resolveMcpServers, resolveA2aPeers, restartReasons, modelPlaneChanged, withModelPlaneOf, bootSnapshotOf, fullyLandedForLkg, fetchPromptArtifact } from "../config-center/facade.js";
23
23
  import { createMcpRevocationLedger, enabledMcpEntries } from "../config-center/mcp-revocation.js";
24
24
  import { SHA256_HEX_RE } from "../digest-form.js";
25
- import { createConfigApplyLedger, modelPlaneCommitted, refusedGroups } from "../config-center/apply-ledger.js";
25
+ import { BOOT_REJUDGE_OF_REFUSED_GROUP, createConfigApplyLedger, modelPlaneCommitted, refusedGroups } from "../config-center/apply-ledger.js";
26
26
  function lkgReadNote(scope) {
27
27
  switch (scope) {
28
28
  case "whole":
@@ -38,6 +38,7 @@ export async function createConfigCenterRuntime(ctx) {
38
38
  let latestEffective;
39
39
  let lastRejectedCandidate;
40
40
  const applyLedger = createConfigApplyLedger();
41
+ let lastPull = "none";
41
42
  let generationOrdinal = 0;
42
43
  let lastGenerationVersion;
43
44
  const generationOf = (version) => {
@@ -531,6 +532,8 @@ export async function createConfigCenterRuntime(ctx) {
531
532
  });
532
533
  }
533
534
  }
535
+ if (configProvider.kind === "remote")
536
+ lastPull = "ok";
534
537
  }
535
538
  }
536
539
  catch (err) {
@@ -605,6 +608,12 @@ export async function createConfigCenterRuntime(ctx) {
605
608
  modelRefusalsState: () => modelRefusals,
606
609
  planeDeferredState: () => planeDeferredNoHandoff,
607
610
  configApplyState: () => (configProvider ? applyLedger.view() : undefined),
611
+ centerPullState: () => {
612
+ const view = applyLedger.view();
613
+ const landed = latestEffective;
614
+ const sameGeneration = view.configAppliedOrdinal > 0 && landed !== undefined && landed.version === view.configAppliedVersion;
615
+ return { lastPull, effectiveRevision: sameGeneration && landed !== undefined ? { version: view.configAppliedVersion, updatedAt: landed.updatedAt } : null };
616
+ },
608
617
  centerManagedKeys: () => (configProvider ? centerManagedConfigKeys(latestEffective) : undefined),
609
618
  async refreshNow() {
610
619
  if (!manualRefresh)
@@ -683,12 +692,83 @@ export async function createConfigCenterRuntime(ctx) {
683
692
  },
684
693
  startRefreshLoop(a) {
685
694
  const { pricing, limitSync, swapRunnerDeps, ceilingVerdict, consolidationSeat, degradeRoute, checkpointStorePresent } = a;
695
+ const signalRestart = (c) => {
696
+ const reasons = restartReasons(c.baseline, c.effective, restartCtx(consolidationSeat?.read(), degradeRoute.read()));
697
+ const skillsChangeDetected = reasons.includes("skills");
698
+ if (bootSkillStale && !skillsChangeDetected)
699
+ reasons.push("skills");
700
+ const riderUnproven = !lkgSurvivesRestart && bootSkillStale && !skillsChangeDetected && !lastPrewarmBodiesVerified;
701
+ const stripRider = () => {
702
+ const i = reasons.indexOf("skills");
703
+ if (i < 0)
704
+ return false;
705
+ reasons.splice(i, 1);
706
+ logger.warn("config_boot_skill_debt_withheld", { version: c.effective.version, note: "boot skill debt still unproven on a non-durable deployment (body cache does not verify) — skills restart withheld this tick; re-verified each tick and published once the bodies prove fetchable" });
707
+ return true;
708
+ };
709
+ switch (c.applied) {
710
+ case "complete": {
711
+ const retain = (blocked) => {
712
+ planeDeferredNoHandoff = { version: c.effective.version, since: Date.now(), blocked, candidate: c.effective, ...(c.etag !== undefined ? { candidateEtag: c.etag } : {}) };
713
+ };
714
+ if (lkgSurvivesRestart && !c.lkgPersisted) {
715
+ const blocked = reasons.splice(0, reasons.length);
716
+ retain(blocked);
717
+ logger.warn("restart_candidate_publication_incomplete", { version: c.effective.version, blockedReasons: blocked, note: "durable LKG declared but the candidate's publication is incomplete (skill bodies unverified) — restart signal withheld; retried every tick until the cache completes" });
718
+ }
719
+ else if (riderUnproven && stripRider()) {
720
+ retain(["skills"]);
721
+ }
722
+ break;
723
+ }
724
+ case "partial": {
725
+ const mayRefuseBoot = c.refused.filter((g) => BOOT_REJUDGE_OF_REFUSED_GROUP[g] === "may-refuse-boot");
726
+ if (mayRefuseBoot.length > 0) {
727
+ const revoked = pendingRestart;
728
+ if (reasons.length > 0 || revoked !== undefined)
729
+ logger.warn("restart_signal_withheld_boot_may_refuse", {
730
+ version: c.effective.version,
731
+ groups: mayRefuseBoot,
732
+ reasons: [...reasons],
733
+ ...(revoked !== undefined ? { revoked: { version: revoked.version, reasons: [...revoked.reasons], since: revoked.since } } : {}),
734
+ note: "a partially applied candidate diverged the live config from what this process baked at boot (see reasons), but a restart would re-judge the refused group(s) at boot where this deployment may refuse to start — restart signal withheld, and any signal already published is revoked (see revoked: a restart is unsafe while this candidate is current, whichever tick signed it); fix the refused group or the deployment fact it names, and the next safe tick signals the diff afresh",
735
+ });
736
+ publishRestart(undefined);
737
+ return pendingRestart;
738
+ }
739
+ if (riderUnproven)
740
+ stripRider();
741
+ break;
742
+ }
743
+ default: {
744
+ const unknownApplied = c;
745
+ throw new Error(`signalRestart: unknown apply outcome ${JSON.stringify(unknownApplied)}`);
746
+ }
747
+ }
748
+ if (reasons.length === 0) {
749
+ publishRestart(undefined);
750
+ }
751
+ else if (!pendingRestart || pendingRestart.reasons.join(",") !== reasons.join(",")) {
752
+ publishRestart({ reasons, version: c.effective.version, since: Date.now() });
753
+ }
754
+ else {
755
+ publishRestart({ ...pendingRestart, version: c.effective.version });
756
+ }
757
+ return pendingRestart;
758
+ };
759
+ const restartLogFields = (signal) => signal ? { restartRequired: true, restartReasons: signal.reasons } : {};
686
760
  if (configProvider) {
687
761
  const ccRef = config.configCenter;
688
762
  let refreshInFlight = false;
689
763
  const refreshTick = async (arrival) => {
690
764
  if (refreshInFlight)
691
765
  return "ok";
766
+ const outcome = await refreshTickRan(arrival);
767
+ if (configProvider.kind === "remote")
768
+ lastPull = outcome;
769
+ return outcome;
770
+ };
771
+ const refreshTickRan = async (arrival) => {
692
772
  refreshInFlight = true;
693
773
  try {
694
774
  await retryPendingCatalog("refresh-retry");
@@ -788,39 +868,20 @@ export async function createConfigCenterRuntime(ctx) {
788
868
  groups: refused.map((g) => g.group),
789
869
  note: "a group of this candidate was refused — the live config keeps serving the prior values for it; the LKG and the ETag stay on the last FULLY applied generation (this tree is re-judged next tick)",
790
870
  });
871
+ const partialSignal = signalRestart({ applied: "partial", effective: r.effective, baseline: effective, refused: refused.map((g) => g.group) });
872
+ logger.info("sema_registry_refreshed", {
873
+ version: r.effective.version,
874
+ note: "partially applied — the refused group(s) keep their prior values (see config_candidate_partially_applied); the other groups hot-applied; restart fields as on a complete tick",
875
+ ...restartLogFields(partialSignal),
876
+ });
791
877
  return "ok";
792
878
  }
793
879
  const lkgPersisted = await persistLkgDurable(r.effective, r.etag);
794
- const reasons = restartReasons(effective, r.effective, restartCtx(consolidationSeat?.read(), degradeRoute.read()));
795
- const skillsChangeDetected = reasons.includes("skills");
796
- if (bootSkillStale && !skillsChangeDetected)
797
- reasons.push("skills");
798
- if (lkgSurvivesRestart && !lkgPersisted) {
799
- const blocked = reasons.splice(0, reasons.length);
800
- planeDeferredNoHandoff = { version: r.effective.version, since: Date.now(), blocked, candidate: r.effective, ...(r.etag !== undefined ? { candidateEtag: r.etag } : {}) };
801
- logger.warn("restart_candidate_publication_incomplete", { version: r.effective.version, blockedReasons: blocked, note: "durable LKG declared but the candidate's publication is incomplete (skill bodies unverified) — restart signal withheld; retried every tick until the cache completes" });
802
- }
803
- else if (!lkgSurvivesRestart && bootSkillStale && !skillsChangeDetected && !lastPrewarmBodiesVerified) {
804
- const i = reasons.indexOf("skills");
805
- if (i >= 0) {
806
- reasons.splice(i, 1);
807
- planeDeferredNoHandoff = { version: r.effective.version, since: Date.now(), blocked: ["skills"], candidate: r.effective, ...(r.etag !== undefined ? { candidateEtag: r.etag } : {}) };
808
- logger.warn("config_boot_skill_debt_withheld", { version: r.effective.version, note: "boot skill debt still unproven on a non-durable deployment (body cache does not verify) — skills restart withheld this tick; re-verified each tick and published once the bodies prove fetchable" });
809
- }
810
- }
811
- if (reasons.length === 0) {
812
- publishRestart(undefined);
813
- }
814
- else if (!pendingRestart || pendingRestart.reasons.join(",") !== reasons.join(",")) {
815
- publishRestart({ reasons, version: r.effective.version, since: Date.now() });
816
- }
817
- else {
818
- publishRestart({ ...pendingRestart, version: r.effective.version });
819
- }
880
+ const signal = signalRestart({ applied: "complete", effective: r.effective, ...(r.etag !== undefined ? { etag: r.etag } : {}), baseline: effective, lkgPersisted });
820
881
  logger.info("sema_registry_refreshed", {
821
882
  version: r.effective.version,
822
883
  note: "collab + runtime governance (incl. approvalRequire) + limits + models/roles/roster hot-applied; skills/mcp/a2a/scenarios are restart-to-apply",
823
- ...(pendingRestart ? { restartRequired: true, restartReasons: pendingRestart.reasons } : {}),
884
+ ...restartLogFields(signal),
824
885
  });
825
886
  latestEffective = r.effective;
826
887
  ccEtag = r.etag;
@@ -857,6 +918,8 @@ export async function createConfigCenterRuntime(ctx) {
857
918
  refreshInFlight = true;
858
919
  try {
859
920
  await deferredBootApplyInner(r, useArrivalGeneration(r.effective.version, arrivalGen));
921
+ if (configProvider.kind === "remote")
922
+ lastPull = "ok";
860
923
  }
861
924
  finally {
862
925
  refreshInFlight = false;
@@ -919,21 +982,16 @@ export async function createConfigCenterRuntime(ctx) {
919
982
  groups: refusedLate.map((g) => g.group),
920
983
  note: "late-boot candidate had a refused group — the LKG/ETag stay on the last FULLY applied generation; the refresh cadence re-judges the same tree",
921
984
  });
985
+ const partialSignalLate = signalRestart({ applied: "partial", effective: r.effective, baseline: undefined, refused: refusedLate.map((g) => g.group) });
986
+ logger.info("config_loaded_deferred", { source: configProvider.kind, version: r.effective.version, models: (r.effective.models?.models ?? []).filter((m) => m.enabled !== false).length, ...restartLogFields(partialSignalLate) });
922
987
  return;
923
988
  }
924
989
  effective = r.effective;
925
990
  latestEffective = r.effective;
926
991
  ccEtag = r.etag;
927
992
  const lkgPersistedLate = await persistLkgDurable(r.effective, r.etag);
928
- const reasons = restartReasons(undefined, r.effective, restartCtx(consolidationSeat?.read(), degradeRoute.read()));
929
- if (lkgSurvivesRestart && !lkgPersistedLate) {
930
- const blocked = reasons.splice(0, reasons.length);
931
- planeDeferredNoHandoff = { version: r.effective.version, since: Date.now(), blocked, candidate: r.effective, ...(r.etag !== undefined ? { candidateEtag: r.etag } : {}) };
932
- logger.warn("restart_candidate_publication_incomplete", { version: r.effective.version, blockedReasons: blocked, note: "late-boot candidate publication incomplete on a durable-declared deployment — restart signal withheld; retried every tick" });
933
- }
934
- if (reasons.length > 0)
935
- publishRestart({ reasons, version: r.effective.version, since: Date.now() });
936
- 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 } : {}) });
993
+ const signalLate = signalRestart({ applied: "complete", effective: r.effective, ...(r.etag !== undefined ? { etag: r.etag } : {}), baseline: undefined, lkgPersisted: lkgPersistedLate });
994
+ logger.info("config_loaded_deferred", { source: configProvider.kind, version: r.effective.version, models: (r.effective.models?.models ?? []).filter((m) => m.enabled !== false).length, ...restartLogFields(signalLate) });
937
995
  };
938
996
  if (bootConfigPending) {
939
997
  const arrivalGen = reserveArrivalGeneration();
@@ -40,6 +40,7 @@ export declare function createLiveCoordinators(ctx: LiveCoordinatorsCtx): {
40
40
  durableEnabled: boolean;
41
41
  streamApprovalGate: import("../tool-approval.js").StreamApprovalGate;
42
42
  approvalAskAudit: FileApprovalAskAuditStore | undefined;
43
+ approvalExemptionStore: import("../plugins/approval-exemption-store.js").ApprovalExemptionStore | undefined;
43
44
  sendUserFileEmitter: SendUserFileEmitter | undefined;
44
45
  sendFileLedger: import("../plugins/send-file-ledger.js").SendFileLedger | undefined;
45
46
  sendUserFileToolSpec: import("@sema-agent/core").ToolSpec<import("typebox").TSchema> | undefined;
@@ -55,6 +55,7 @@ export function createLiveCoordinators(ctx) {
55
55
  });
56
56
  }
57
57
  }
58
+ const approvalExemptionStore = backend ? backend.approvalExemption() : undefined;
58
59
  const toolApproval = config.toolApprovalEnabled
59
60
  ? new ToolApprovalCoordinator({
60
61
  ttlMs: config.streamApproval.windowMs,
@@ -69,6 +70,7 @@ export function createLiveCoordinators(ctx) {
69
70
  unattendedPolicy: config.unattendedApprovalPolicy,
70
71
  ...(getParkedRedeem !== undefined ? { parkedRedeem: getParkedRedeem } : {}),
71
72
  ...(approvalAskAudit !== undefined ? { askAudit: approvalAskAudit } : {}),
73
+ ...(approvalExemptionStore !== undefined ? { sessionGrants: approvalExemptionStore } : {}),
72
74
  })
73
75
  : undefined;
74
76
  const sendUserFileEmitter = (() => {
@@ -112,6 +114,6 @@ export function createLiveCoordinators(ctx) {
112
114
  : rawSend;
113
115
  return sendUserFileTool({ send, emitter: sendUserFileEmitter });
114
116
  })();
115
- return { elicitation, question, toolApproval, durableEnabled, streamApprovalGate, approvalAskAudit, sendUserFileEmitter, sendFileLedger, sendUserFileToolSpec };
117
+ return { elicitation, question, toolApproval, durableEnabled, streamApprovalGate, approvalAskAudit, approvalExemptionStore, sendUserFileEmitter, sendFileLedger, sendUserFileToolSpec };
116
118
  }
117
119
  //# sourceMappingURL=coordinators.js.map
@@ -59,7 +59,8 @@ export declare function createDeviceLane(ctx: DeviceLaneCtx): DeviceLane | undef
59
59
  * `boot/leader.ts` 的 `assertLeaderDurableStore`)。
60
60
  *
61
61
  * config 层的门①判的是「运维配没配 SQL 坐标」——那句话回答的是**意图**。它回答不了「这次启动**真的
62
- * 拿到**了店吗」:`openStoreBackendWithFallback` 在 `SESSION_BACKEND=auto`(默认)下,SQL 连不上 /
62
+ * 拿到**了店吗」:`openStoreBackendWithFallback` 在 `SESSION_BACKEND=auto` 下(🔴 S-866 注改真:`auto` **不是**缺省 ——
63
+ * 缺省是 `memory`,`DB_BACKEND` 显式 mysql|pg 时缺省 `mysql`;`auto` 要显式写),SQL 连不上 /
63
64
  * 建表失败 ⇒ `backend=undefined` + 一条 warn 就继续启动。那次启动里 device lane 的四表整个不在,
64
65
  * 而 `boot/execution-env.ts` 的第七臂拿不到三件 ⇒ `executionEnvFactory` 落 `undefined` ⇒ core 拿
65
66
  * `StubExecutionEnv`:**一台自称在员工设备上执行的 worker 实际在自己进程里跑,而且回执同形**。
@@ -8,6 +8,7 @@ import type { DeviceEnrollment } from "../device-enrollment.js";
8
8
  import type { DeviceStore } from "../device-store.js";
9
9
  import type { DeviceWsHub } from "../device-ws-hub.js";
10
10
  import { type TaskAttachmentStore } from "../plugins/task-attachment-store.js";
11
+ import type { TaskInheritEnv } from "../request-value-closure.js";
11
12
  /**
12
13
  * per-session 的 **cwd / shellEnv 登记簿**(TOC cwd seam + R-survey shellEnv seam)+ 它们的**租户围栏**。
13
14
  *
@@ -32,8 +33,17 @@ import { type TaskAttachmentStore } from "../plugins/task-attachment-store.js";
32
33
  export interface SessionScopedRegistries {
33
34
  readonly perSessionCwd: Map<string, string>;
34
35
  readonly perSessionShellEnv: Map<string, Record<string, string>>;
36
+ /** S-895:host shell 的继承策略(`inheritEnv` 请求键)。只读面给 host 工厂包装;写只经 {@link setSessionInheritEnv}。 */
37
+ readonly perSessionInheritEnv: ReadonlyMap<string, TaskInheritEnv>;
35
38
  setSessionCwd(sid: string, cwd: string): void;
36
39
  setSessionShellEnv(sid: string, env: Record<string, string>): void;
40
+ /**
41
+ * S-895 —— 第三只 per-session 绑定(与 cwd / shellEnv 同一只 LRU 权威、同一条围栏、同一条删除)。
42
+ * `undefined` = **清掉**这一格(不是「不动」):`inheritEnv` 是「本条请求要什么」,缺席的请求要的是缺省清洗 ——
43
+ * 与 cwd / shellEnv 的「缺席 = 留着上一条的值」刻意不同(那两位由壳每条请求重发,粘住无害;这一位粘住 = 替一个没说话的
44
+ * 请求放宽凭据面)。
45
+ */
46
+ setSessionInheritEnv(sid: string, policy: TaskInheritEnv | undefined): void;
37
47
  /** 记录/对拍本会话的主人。与上次不同 ⇒ **先清两张表**再记新主(换主即清)。`null` = 匿名会话,
38
48
  * 它同样是一个**稳定**的身份值(不是「未知」),所以匿名重入不会被判成换主。 */
39
49
  fenceSessionOwner(sid: string, owner: string | null): void;
@@ -70,6 +80,7 @@ export declare function createExecutionEnv(ctx: ExecutionEnvCtx): {
70
80
  perSessionCwd: Map<string, string>;
71
81
  setSessionCwd: (sid: string, cwd: string) => void;
72
82
  setSessionShellEnv: (sid: string, env: Record<string, string>) => void;
83
+ setSessionInheritEnv: (sid: string, policy: TaskInheritEnv | undefined) => void;
73
84
  fenceSessionOwner: (sid: string, owner: string | null) => void;
74
85
  dropSessionScoped: (sid: string) => void;
75
86
  noteSessionIncarnation: (sid: string, incarnation: string | undefined) => void;
@@ -19,8 +19,9 @@ import { materializeAttachmentsInto } from "../plugins/task-attachment-store.js"
19
19
  import { reapOrphanWorktrees, withWorktreeIsolation } from "../plugins/worktree-isolation.js";
20
20
  import { customPkgSourceFromEnv, derivePkgSourceEnv } from "../sandbox-pkg-source.js";
21
21
  import { effectiveHostWorkspace } from "../task-cwd.js";
22
+ import { taskInheritEnvRefusal } from "../hosted-posture.js";
22
23
  import { isSandboxPathAdjudicationLane, sandboxPathEnvSlots, withSandboxPathEnvSlot } from "./deferred-sandbox-path-env.js";
23
- import { executionLaneHomeDir } from "../execution-lane-caps.js";
24
+ import { REMOTE_EXEC_PROVIDER_WORDS, executionLaneHomeDir, executionLaneOf, isSingleMachineDefaultLane, laneIsolated } from "../execution-lane-caps.js";
24
25
  function boundedSessionSetter(map, maxEntries) {
25
26
  return (sid, value) => {
26
27
  map.delete(sid);
@@ -51,11 +52,13 @@ const MAX_CWD_SESSIONS = 4096;
51
52
  export function createSessionScopedRegistries(maxEntries = MAX_CWD_SESSIONS) {
52
53
  const perSessionCwd = new Map();
53
54
  const perSessionShellEnv = new Map();
55
+ const perSessionInheritEnv = new Map();
54
56
  const perSessionIncarnation = new Map();
55
57
  const owners = new Map();
56
58
  const dropSession = (sid) => {
57
59
  perSessionCwd.delete(sid);
58
60
  perSessionShellEnv.delete(sid);
61
+ perSessionInheritEnv.delete(sid);
59
62
  perSessionIncarnation.delete(sid);
60
63
  owners.delete(sid);
61
64
  };
@@ -77,10 +80,20 @@ export function createSessionScopedRegistries(maxEntries = MAX_CWD_SESSIONS) {
77
80
  perSessionShellEnv.set(sid, env);
78
81
  touch(sid, undefined);
79
82
  },
83
+ perSessionInheritEnv,
84
+ setSessionInheritEnv: (sid, policy) => {
85
+ if (policy === undefined) {
86
+ perSessionInheritEnv.delete(sid);
87
+ return;
88
+ }
89
+ perSessionInheritEnv.set(sid, policy);
90
+ touch(sid, undefined);
91
+ },
80
92
  fenceSessionOwner: (sid, owner) => {
81
93
  if (owners.has(sid) && owners.get(sid) !== owner) {
82
94
  perSessionCwd.delete(sid);
83
95
  perSessionShellEnv.delete(sid);
96
+ perSessionInheritEnv.delete(sid);
84
97
  perSessionIncarnation.delete(sid);
85
98
  }
86
99
  touch(sid, owner);
@@ -97,6 +110,14 @@ export function createSessionScopedRegistries(maxEntries = MAX_CWD_SESSIONS) {
97
110
  sessionIncarnationOf: (sid) => perSessionIncarnation.get(sid),
98
111
  };
99
112
  }
113
+ function buildRealSystemNote(remoteExec) {
114
+ if (!isSingleMachineDefaultLane(remoteExec))
115
+ return "non-isolated target — actions are permanent; rely on the policy gate + HITL (design/61 §5)";
116
+ const sandboxLanes = REMOTE_EXEC_PROVIDER_WORDS.filter((lane) => laneIsolated(lane)).join("|");
117
+ return ("hands run on this machine with no OS sandbox (single-machine default: REMOTE_EXEC unset, CONFIG_PROVIDER=local) — actions are permanent, " +
118
+ "and the Bash read boundary is a text-level friction floor, not a safety boundary; the safety boundary is a sandbox lane: " +
119
+ `set REMOTE_EXEC=${sandboxLanes} (see docs/DEPLOY-PREREQS.md, section "S-281")`);
120
+ }
100
121
  export function createExecutionEnv(ctx) {
101
122
  const { config, logger, metrics, taskAttachmentStore } = ctx;
102
123
  const laneHomeDir = executionLaneHomeDir(config);
@@ -106,6 +127,7 @@ export function createExecutionEnv(ctx) {
106
127
  const sessionScoped = createSessionScopedRegistries();
107
128
  const { perSessionCwd, setSessionCwd } = sessionScoped;
108
129
  const { perSessionShellEnv, setSessionShellEnv } = sessionScoped;
130
+ const { perSessionInheritEnv, setSessionInheritEnv } = sessionScoped;
109
131
  let executionEnvFactory;
110
132
  let worktreeReap;
111
133
  if (config.remoteExec?.provider === "e2b") {
@@ -207,10 +229,12 @@ export function createExecutionEnv(ctx) {
207
229
  executionEnvFactory = (ctx) => {
208
230
  const cwd = effectiveHostWorkspace(perSessionCwd.get(ctx.sessionId), ctx);
209
231
  const shellEnv = perSessionShellEnv.get(ctx.sessionId);
232
+ const inheritEnv = perSessionInheritEnv.get(ctx.sessionId);
210
233
  const env = hostExecutionEnvFactory({
211
234
  ...hostCfg,
212
235
  ...(cwd ? { workspaceDir: cwd } : {}),
213
236
  ...(shellEnv ? { env: shellEnv } : {}),
237
+ ...(inheritEnv !== undefined ? { inheritEnv } : {}),
214
238
  })(ctx);
215
239
  return lspManager && !cwd ? evictLspOnDestroy(env, lspManager) : env;
216
240
  };
@@ -300,15 +324,15 @@ export function createExecutionEnv(ctx) {
300
324
  executionEnvFactory = withSandboxPathEnvSlot(executionEnvFactory, sandboxPathEnvSlots, logger);
301
325
  }
302
326
  if (executionEnvFactory) {
303
- const isolated = config.remoteExec.provider === "e2b" ||
304
- config.remoteExec.provider === "k8s" ||
305
- config.remoteExec.provider === "local-docker";
327
+ const isolated = laneIsolated(executionLaneOf(config.remoteExec.provider));
306
328
  const suspendable = config.remoteExec.provider === "e2b" ||
307
329
  (config.remoteExec.provider === "k8s" && !!config.remoteExec.s3Snapshot);
308
330
  const sandboxEnvKeys = forwardedSandboxEnvKeys(config.remoteExec);
309
331
  logger.info("remote_exec_enabled", { provider: config.remoteExec.provider, isolated, suspendable, ...(sandboxEnvKeys.length > 0 ? { sandboxEnvKeys } : {}) });
310
332
  if (!isolated)
311
- logger.warn("remote_exec_real_system", { provider: config.remoteExec.provider, note: "non-isolated target — actions are permanent; rely on the policy gate + HITL (design/61 §5)" });
333
+ logger.warn("remote_exec_real_system", { provider: config.remoteExec.provider, note: buildRealSystemNote(config.remoteExec) });
334
+ if (executionLaneOf(config.remoteExec.provider) === "host")
335
+ logger.info("task_inherit_env_honored", { honored: taskInheritEnvRefusal(config) === undefined });
312
336
  }
313
337
  const lspProvider = config.remoteExec?.provider;
314
338
  const lspManager = (lspProvider === "e2b" || lspProvider === "k8s") && config.lspEnabled
@@ -325,7 +349,7 @@ export function createExecutionEnv(ctx) {
325
349
  if (lspManager)
326
350
  logger.info("lsp_enabled", { provider: lspProvider });
327
351
  return {
328
- perTaskImage, sessionEnvSelection, perSessionCwd, setSessionCwd, setSessionShellEnv,
352
+ perTaskImage, sessionEnvSelection, perSessionCwd, setSessionCwd, setSessionShellEnv, setSessionInheritEnv,
329
353
  fenceSessionOwner: sessionScoped.fenceSessionOwner,
330
354
  dropSessionScoped: sessionScoped.dropSession,
331
355
  noteSessionIncarnation: sessionScoped.noteSessionIncarnation,
@@ -1,6 +1,6 @@
1
1
  import { loadRemoteExec } from "@sema-agent/settings-schema/node";
2
2
  import { crossInvariant } from "../config-invariants.js";
3
- import { executionLaneCaps, executionLaneOf } from "../execution-lane-caps.js";
3
+ import { executionLaneOf, laneIsolated } from "../execution-lane-caps.js";
4
4
  export const FILE_SOURCED_LANES = ["host", "local-docker"];
5
5
  export async function composeExecutionLane(config, ctx) {
6
6
  if (ctx.configSource === "local")
@@ -39,11 +39,10 @@ async function applyRemoteExecFile(config, ctx) {
39
39
  if (!fileRx)
40
40
  return;
41
41
  const preemptedLane = executionLaneOf(config.remoteExec?.provider);
42
- const isolatedLane = (lane) => executionLaneCaps(lane).isolation === "supported";
43
42
  const disclosePreemption = (winner) => {
44
43
  if (winner === preemptedLane)
45
44
  return;
46
- const downgrade = isolatedLane(preemptedLane) && !isolatedLane(winner);
45
+ const downgrade = laneIsolated(preemptedLane) && !laneIsolated(winner);
47
46
  logger.warn("remote_exec_lane_preempted_by_file", {
48
47
  preemptedLane,
49
48
  effectiveLane: winner,
@@ -65,7 +65,8 @@ export declare function leaderK8sMinioGap(leaderEnabled: boolean, leaderProvider
65
65
  *
66
66
  * config 层的门②判的是 `DB_BACKEND` 的字面值 —— 那句话回答的是「运维要没要 durable」。它回答不了
67
67
  * 「这次启动**真的拿到**了 durable 吗」:`openStoreBackendWithFallback` 在 `SESSION_BACKEND=auto`
68
- * (默认)下,SQL 连不上 / 建表失败 ⇒ 返回 `backend=undefined` + 一条 warn 就继续启动。那次启动里
68
+ * 下(🔴 S-866 注改真:`auto` **不是**缺省 —— 缺省是 `memory`,`DB_BACKEND` 显式 mysql|pg 时缺省 `mysql`;`auto` 要显式写),SQL 连不上 /
69
+ * 建表失败 ⇒ 返回 `backend=undefined` + 一条 warn 就继续启动。那次启动里
69
70
  * `leaderRun()` 缺席,leader 端点会静默落进单副本内存臂:多副本各自受理同一个 Idempotency-Key、
70
71
  * 跨副本 GET 404、重启失忆 —— 而这条腿的产物是**真 git push**。
71
72
  *
@@ -2,6 +2,7 @@ import { createLeaderEndpoint } from "../leader/endpoint.js";
2
2
  import { createLeaderRunner } from "../leader/wire.js";
3
3
  import { buildDeploymentPostureSeats } from "./runner-deps.js";
4
4
  import { hasApprovalListIntent } from "../approval.js";
5
+ import { unattendedPermissionMode } from "../unattended-permission-mode.js";
5
6
  export function leaderK8sMinioGap(leaderEnabled, leaderProvider, hasS3) {
6
7
  if (!(leaderEnabled && leaderProvider === "k8s" && !hasS3))
7
8
  return null;
@@ -58,6 +59,7 @@ export function createLeaderFace(ctx) {
58
59
  : {}),
59
60
  ...(ctx.promptSource ? { promptSource: ctx.promptSource } : {}),
60
61
  ...(ctx.onError ? { onError: ctx.onError } : {}),
62
+ unattendedPermissionMode: unattendedPermissionMode(config),
61
63
  fanoutEnabled: config.leaderFanoutEnabled,
62
64
  ...((process.env.MODEL_ROUTER_ID || process.env.MODEL_CHEAP_ID)
63
65
  ? { routerModel: (process.env.MODEL_ROUTER_ID || process.env.MODEL_CHEAP_ID) }
@@ -59,13 +59,14 @@ export interface ParkedReviveGateDeps {
59
59
  * 手抄一个等价签名会让 core 改这条 seam 的那天变成静默漂移而不是编译期事件)。
60
60
  */
61
61
  readonly resolveRuntimeCaps?: RunnerDeps["runtimeCapsResolver"];
62
- /** `RunnerDeps.autoMode` 分类器面(信任门半场)在不在场 —— core 7.2.0 的武装条件是
63
- * `runtimeCaps.autoMode === true` **∧** 这一面在场(prepare-task 原式)。⚠️ [ref] / core [ref] 起武装式换成
64
- * `spec.autoModeRequested === true ∧ 席位 ∧ runtimeCaps?.autoMode !== false`——本文件的镜像
65
- * {@link autoModePostureOf} **未随改**(赎回行无父 run 的 auto 意图可读),见该函数头注的未闭环登记。 */
62
+ /** `RunnerDeps.autoMode` 分类器面(信任门半场)在不在场 —— 武装式(core 7.34.0 [ref])=
63
+ * `permissionMode === "auto"` ∧ 这一面在场 ∧ `runtimeCaps?.autoMode !== false`;本文件的镜像见 {@link autoModePostureOf}
64
+ * (赎回行上父 run 的 auto 意图仍无本仓读口,见该函数头注的未闭环登记)。 */
66
65
  readonly autoModeSeatMounted?: boolean;
67
- /** [ref]:父 run 的 auto 意图(`TaskSpec.autoModeRequested` / 链位)——意图武装式下重建 decider 的第三项。⚠️ 今日
68
- * **无持久源**(core [ref] 链位 live-only;RunRecord 无 body),生产装配不递 ⇒ 恒缺席;座留给 core 持久化到货。 */
66
+ /** [ref]:父 run 的 auto 意图(core 7.34.0 起 = 父腿的 `permissionMode === "auto"`,旧键 `autoModeRequested` 已退役)——
67
+ * 意图武装式下重建 decider 的第三项。⚠️ 今日**本仓无读口**(RunRecord 无 body),生产装配不递 ⇒ 恒缺席。
68
+ * S-877 登记:core 7.34.0 起子代 checkpoint 的数据半持久 `inheritedGate.permissionMode`(链在 spawn 时的**生效**模式),
69
+ * 这一座可改读它 —— 接线不在本次提货单内,见 7.106.0 收车档「未闭环」。 */
69
70
  readonly parentAutoModeIntent?: ((row: BackgroundAgentRecord) => boolean) | undefined;
70
71
  readonly logger: {
71
72
  info(event: string, fields?: Record<string, unknown>): void;
@@ -119,7 +120,9 @@ export type RebuiltParentConstraint = NonNullable<RebuiltInheritedGate["parentCo
119
120
  * (core 对这三个都是 seed 兜底)。
120
121
  *
121
122
  * 时点语义(成文进 docs/ASSISTANT-WIRE-CONTRACT.md,免运维当 bug 报):
122
- * · toolPolicy 轴 = **当前** config(与 resume 重折一致,收紧与放松都被兑现——放松是一次显式运维动作);
123
+ * · toolPolicy 轴 = **当前** config(与 resume 重折一致)。S-877(core 7.35.0 #1200)起部署治理层经 `wrapToolPolicy` 携带内层 `projection`
124
+ * ⇒ 父链那一层的**内容**进了 `cpv1` 摘要:挂起期间改了治理 config(收紧或放松)⇒ 重建链摘要对不上 ⇒ core pre-CAS 响亮拒、卡留 pending
125
+ * (此前那一层 opaque,收紧与放松都按当前 config 兑现;特征化钉 test/parked-revive-e2e.test.ts 已随之翻);
123
126
  * · shellGate 轴 = core 取 max(live, park 时 seed)—— 收紧兑现、放松**不**兑现(fail-safe)。
124
127
  * · `autoModeArmed` 轴([ref])= 按**当前** per-principal entitlement 现解(见 {@link autoModePostureOf});
125
128
  * 与 toolPolicy 轴同族:挂起期间权益被撤/被授 ⇒ 摘要对不上 ⇒ core pre-CAS 响亮拒,不静默换姿势跑。
@@ -1,4 +1,3 @@
1
- import { coreArmsAutoOnIntent } from "../auto-mode-face.js";
2
1
  import { join } from "node:path";
3
2
  import { createAutoModeDecider, NodeExecutionEnv } from "@sema-agent/core";
4
3
  import { recordFailOpen } from "../observability/fail-open.js";
@@ -74,16 +73,10 @@ async function resolveCapsFor(deps, identity) {
74
73
  function autoModePostureOf(deps, row, caps) {
75
74
  if (deps.autoModeSeatMounted !== true)
76
75
  return {};
77
- if (!coreArmsAutoOnIntent()) {
78
- if (caps?.autoMode !== true)
79
- return {};
80
- }
81
- else {
82
- if (caps?.autoMode === false)
83
- return {};
84
- if (deps.parentAutoModeIntent?.(row) !== true)
85
- return {};
86
- }
76
+ if (caps?.autoMode === false)
77
+ return {};
78
+ if (deps.parentAutoModeIntent?.(row) !== true)
79
+ return {};
87
80
  return { autoMode: { decider: createUnreachableAncestorClassifier(deps, row) } };
88
81
  }
89
82
  function createUnreachableAncestorClassifier(deps, row) {
@@ -17,7 +17,7 @@ import type { StoreBackend } from "../plugins/store-backend.js";
17
17
  import { type TaskAttachmentStore } from "../plugins/task-attachment-store.js";
18
18
  import { type createPrincipalEntitlementsClient } from "../runtime-caps-resolver.js";
19
19
  import { type OwnerAwareSessionStore } from "../security.js";
20
- import type { RunnerDepsOnAsk } from "./runner-deps.js";
20
+ import { type RunnerDepsOnAsk } from "./runner-deps.js";
21
21
  type PrincipalCaps = ReturnType<typeof createPrincipalEntitlementsClient>;
22
22
  /** `resolveSpec` 原先从 `main()` 闭包里拿到的全部 boot 局部量。 */
23
23
  export interface ResolveSpecCtx {
@@ -47,9 +47,6 @@ export interface ResolveSpecCtx {
47
47
  hookWakeBus: {
48
48
  deliver?: (sessionId: string, text: string) => Promise<boolean>;
49
49
  };
50
- /** [ref] 件⑤:per-session「显式 bypassPermissions」登记簿的写腿(`boot/session-shell-gate-registry.ts`);
51
- * 阶段④ 算出 effMode 的那一行登记,runner-deps 的 config 忠告汇点按 sessionId 回查定级。缺席(桩)= 不登记。 */
52
- noteSessionShellGate?: ((sessionId: string | undefined, explicitOff: boolean) => void) | undefined;
53
50
  resumeAnchorStore: ReturnType<StoreBackend["resumeAnchor"]> | undefined;
54
51
  ownerAware: OwnerAwareSessionStore;
55
52
  taskLimitCaps: {