chainlesschain 0.166.47 → 0.166.56

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 (180) hide show
  1. package/README.md +22 -0
  2. package/package.json +2 -2
  3. package/src/assets/web-panel/.build-hash +1 -1
  4. package/src/assets/web-panel/assets/{AIOps-CDpT0WXV.js → AIOps-CBFnwYRX.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-BNfUljWx.js → ActionButton-Sl4eXWpR.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-C9323DEx.js → Analytics-DB0lD9Kb.js} +3 -3
  7. package/src/assets/web-panel/assets/{AppLayout-CRrjb2db.js → AppLayout-j77EmkRK.js} +3 -3
  8. package/src/assets/web-panel/assets/{Artifacts-Db-DGqJS.js → Artifacts-CU3izCW9.js} +1 -1
  9. package/src/assets/web-panel/assets/{Audit-CJdHRJ0W.js → Audit-C44fSibb.js} +1 -1
  10. package/src/assets/web-panel/assets/{BackgroundAgents-_RSCnTYs.js → BackgroundAgents-pM_NnJW3.js} +1 -1
  11. package/src/assets/web-panel/assets/{Backup-8GFQi-dM.js → Backup-AOHXqhxN.js} +1 -1
  12. package/src/assets/web-panel/assets/{BaseInput-C87NtgUZ.js → BaseInput-DaYc8oA_.js} +1 -1
  13. package/src/assets/web-panel/assets/{Chat-DcTMWS8G.js → Chat-CmlCAXxW.js} +5 -5
  14. package/src/assets/web-panel/assets/{ChatBubbleRenderer-5ZDPTW_H.js → ChatBubbleRenderer-CkCSkJDJ.js} +1 -1
  15. package/src/assets/web-panel/assets/{Checkbox-DM9EmYee.js → Checkbox-DISI7iaA.js} +1 -1
  16. package/src/assets/web-panel/assets/{Codegen-M2YqDRNN.js → Codegen-BYpeKUpf.js} +1 -1
  17. package/src/assets/web-panel/assets/{Col-oB1LLxyf.js → Col-Dk6VwWGq.js} +1 -1
  18. package/src/assets/web-panel/assets/{Community-TcVZK5tF.js → Community-BDyTedMu.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compact-XKmq-3K_.js → Compact-CTNlGM9X.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compliance-C6CBiDRy.js → Compliance-C_tw3IdT.js} +1 -1
  21. package/src/assets/web-panel/assets/{Cowork-Dz9CGqXH.js → Cowork-cduHWEmo.js} +4 -4
  22. package/src/assets/web-panel/assets/{Cron-CxJDgDVL.js → Cron-Dqotjv-s.js} +2 -2
  23. package/src/assets/web-panel/assets/{Crosschain-DMP0JsaQ.js → Crosschain-ByW7RsJQ.js} +1 -1
  24. package/src/assets/web-panel/assets/{DID-I5IQeogb.js → DID-BI-lafPj.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dashboard-BfLW2MXj.js → Dashboard-8m50TRZL.js} +2 -2
  26. package/src/assets/web-panel/assets/{Dropdown-BK8EjAlh.js → Dropdown-CN3hmRTc.js} +1 -1
  27. package/src/assets/web-panel/assets/{EmailListRenderer-C3iqSv5I.js → EmailListRenderer-t0lzxsDT.js} +1 -1
  28. package/src/assets/web-panel/assets/EvolutionSettings-DabpA3cQ.js +1 -0
  29. package/src/assets/web-panel/assets/{FamilyGuardDashboard-T19rnqm-.js → FamilyGuardDashboard-BYN4UshY.js} +1 -1
  30. package/src/assets/web-panel/assets/{Federation-CozrbPMy.js → Federation-n44OtIXp.js} +1 -1
  31. package/src/assets/web-panel/assets/{FormItemContext-k1dCLNNJ.js → FormItemContext-B-WXDu_I.js} +1 -1
  32. package/src/assets/web-panel/assets/{GenericCardRenderer-DbaW_Jvt.js → GenericCardRenderer-Bm7v-mTf.js} +1 -1
  33. package/src/assets/web-panel/assets/{Git-CQHRyEIt.js → Git-BMIS6sbo.js} +2 -2
  34. package/src/assets/web-panel/assets/{Governance-BMJYZgMy.js → Governance-B1EhHobE.js} +1 -1
  35. package/src/assets/web-panel/assets/{Inference-B7mpNU7V.js → Inference-Dy_HJWHo.js} +1 -1
  36. package/src/assets/web-panel/assets/{KnowledgeGraph-Br0Blg6B.js → KnowledgeGraph-BVdfUbZy.js} +1 -1
  37. package/src/assets/web-panel/assets/{Logs-HIdk0S6M.js → Logs-Dph_oLQk.js} +2 -2
  38. package/src/assets/web-panel/assets/MarkdownRenderer-CNHYqcxm.js +1 -0
  39. package/src/assets/web-panel/assets/{Marketplace-Bkfq69UP.js → Marketplace-Bw4HWORj.js} +1 -1
  40. package/src/assets/web-panel/assets/{McpTools-dtmx0bHO.js → McpTools-Be6DPfA1.js} +3 -3
  41. package/src/assets/web-panel/assets/{Memory-CrRlOuEf.js → Memory-DlvLafC9.js} +2 -2
  42. package/src/assets/web-panel/assets/{MobileBridge-D4ZIZiAH.js → MobileBridge-Upc_Jwlt.js} +1 -1
  43. package/src/assets/web-panel/assets/{MobileProjects-C90Rw7GE.js → MobileProjects-I17jrUbY.js} +1 -1
  44. package/src/assets/web-panel/assets/{Mtc-CC6k_uh4.js → Mtc-DyBzY0GH.js} +4 -4
  45. package/src/assets/web-panel/assets/{MtcAudit-T_CUIYUy.js → MtcAudit-2-7xAwEo.js} +2 -2
  46. package/src/assets/web-panel/assets/{Multisig-N_e0RRWk.js → Multisig-DtUZpFmZ.js} +3 -3
  47. package/src/assets/web-panel/assets/{NLProgramming-Iq3VzEv0.js → NLProgramming-iInH7jBr.js} +1 -1
  48. package/src/assets/web-panel/assets/{Notes-B5sKJUJt.js → Notes-BO3Z3vaS.js} +4 -4
  49. package/src/assets/web-panel/assets/{NotificationSettings-K8UVp8qQ.js → NotificationSettings-ClX_0jFE.js} +1 -1
  50. package/src/assets/web-panel/assets/{OrderTableRenderer-BWNyM-6m.js → OrderTableRenderer-Czw742w4.js} +1 -1
  51. package/src/assets/web-panel/assets/{Organization-DMO1NJxI.js → Organization-BATEx24i.js} +4 -4
  52. package/src/assets/web-panel/assets/{Overflow-BBY6b31Z.js → Overflow-B_PXMNia.js} +1 -1
  53. package/src/assets/web-panel/assets/{P2P-DmsL0y0v.js → P2P-BSiE8PqT.js} +2 -2
  54. package/src/assets/web-panel/assets/{PdhVaultBrowser-C65AX_RY.js → PdhVaultBrowser-BbklUgZY.js} +3 -3
  55. package/src/assets/web-panel/assets/{Permissions-CWcpRtWa.js → Permissions-B7bpflT_.js} +4 -4
  56. package/src/assets/web-panel/assets/{PersonalDataHub--ZNDIdsn.js → PersonalDataHub-BWZS4BLy.js} +3 -3
  57. package/src/assets/web-panel/assets/{Pipeline-DCdj5NGb.js → Pipeline-DP2YSs6H.js} +1 -1
  58. package/src/assets/web-panel/assets/{Privacy-BRKu7Q3E.js → Privacy-DB9ptoH0.js} +1 -1
  59. package/src/assets/web-panel/assets/{ProjectInit-BJ7ozIAb.js → ProjectInit-BAfePyYk.js} +2 -2
  60. package/src/assets/web-panel/assets/{ProjectSettings-B2cVLiXW.js → ProjectSettings-BEXn4Cm3.js} +2 -2
  61. package/src/assets/web-panel/assets/{Projects-Ce9r_W-p.js → Projects-BzSs0rzn.js} +1 -1
  62. package/src/assets/web-panel/assets/{Providers-BDespvGZ.js → Providers-CRX9TdRO.js} +1 -1
  63. package/src/assets/web-panel/assets/{QrScannerModal-BUNO4fDU.js → QrScannerModal-DUWcSY5L.js} +1 -1
  64. package/src/assets/web-panel/assets/{QuickAsk-D2TUqeBV.js → QuickAsk-Cgo3Z_hs.js} +1 -1
  65. package/src/assets/web-panel/assets/{Recommend-BNNQPHBH.js → Recommend-Dcinx0IW.js} +1 -1
  66. package/src/assets/web-panel/assets/{RemoteSession-D2t1V_Cm.js → RemoteSession-BMZzTnAN.js} +2 -2
  67. package/src/assets/web-panel/assets/{Reputation-DVK5hEqI.js → Reputation-BdZzpOp9.js} +1 -1
  68. package/src/assets/web-panel/assets/{Row-BB9RTwmE.js → Row-DqM8GVcx.js} +1 -1
  69. package/src/assets/web-panel/assets/{RssFeed-DmEO7liZ.js → RssFeed-Soj_QBK3.js} +2 -2
  70. package/src/assets/web-panel/assets/{Search-UwEYjnDB.js → Search-Cn2es0-v.js} +1 -1
  71. package/src/assets/web-panel/assets/{Security-DebqSsMp.js → Security-t7C4hz3n.js} +3 -3
  72. package/src/assets/web-panel/assets/{Services-CuuPts3C.js → Services-ChF-e_Uf.js} +2 -2
  73. package/src/assets/web-panel/assets/{Skeleton-DbWpmbgf.js → Skeleton-BiPIyK-2.js} +1 -1
  74. package/src/assets/web-panel/assets/{Skills-BaK7SxzT.js → Skills-HgkKJenJ.js} +1 -1
  75. package/src/assets/web-panel/assets/{Sla-BFHv51Jb.js → Sla-BSfluv_k.js} +1 -1
  76. package/src/assets/web-panel/assets/{SpeechSettings-B9b1R6uK.js → SpeechSettings-Sm0Gz9tM.js} +1 -1
  77. package/src/assets/web-panel/assets/{SyncSettings-BnO0CN02.js → SyncSettings-DlhBMnfU.js} +2 -2
  78. package/src/assets/web-panel/assets/{Tasks-Dy01Vq49.js → Tasks-C7OUvsnp.js} +1 -1
  79. package/src/assets/web-panel/assets/{Templates-BCy2AMHc.js → Templates-CrMtw87y.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tenant-4Tx9y_Y1.js → Tenant-DSNSB6j5.js} +1 -1
  81. package/src/assets/web-panel/assets/{Terminal-CPOoUH8J.js → Terminal-Cmh78FQg.js} +2 -2
  82. package/src/assets/web-panel/assets/{TimelineRenderer-C85GlJzu.js → TimelineRenderer-BE8b8lHR.js} +1 -1
  83. package/src/assets/web-panel/assets/{Tokens-_xneFBGF.js → Tokens-BrtgdIvJ.js} +1 -1
  84. package/src/assets/web-panel/assets/{Trigger-BizG4rTr.js → Trigger-HCmaoKDp.js} +1 -1
  85. package/src/assets/web-panel/assets/{Trust-Dh8EIn3n.js → Trust-GASQ4F-2.js} +1 -1
  86. package/src/assets/web-panel/assets/{UkeySign-C5NWoHWQ.js → UkeySign-B8kSzWGr.js} +1 -1
  87. package/src/assets/web-panel/assets/{VideoEditing-0YfrySL6.js → VideoEditing-Nn-clhv5.js} +1 -1
  88. package/src/assets/web-panel/assets/{Wallet-BZVH8Ags.js → Wallet-DdYMKTYk.js} +4 -4
  89. package/src/assets/web-panel/assets/{WebAuthn-kNofa6vm.js → WebAuthn-xJXUr_eK.js} +3 -3
  90. package/src/assets/web-panel/assets/{WorkflowEditor-BNO34_j0.js → WorkflowEditor-CbSa87av.js} +1 -1
  91. package/src/assets/web-panel/assets/{chat-ifQ2FlwD.js → chat-CVrNLGUV.js} +1 -1
  92. package/src/assets/web-panel/assets/{colors-1TL8H6EH.js → colors-e2veLfIA.js} +1 -1
  93. package/src/assets/web-panel/assets/{compact-item-DBfRjs_T.js → compact-item-CNd7nhUq.js} +1 -1
  94. package/src/assets/web-panel/assets/{createContext-Bs6V4iBf.js → createContext-DEudTVqv.js} +1 -1
  95. package/src/assets/web-panel/assets/devWarning-DW7he-SA.js +1 -0
  96. package/src/assets/web-panel/assets/{hasIn-Bz6LMY_0.js → hasIn-BlSAFZa3.js} +1 -1
  97. package/src/assets/web-panel/assets/{index--WvT2Il3.js → index-0y7tHZdT.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-DMVktlxG.js → index-4ZT0vIJt.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-B6RClMZl.js → index-7-7rr4Mf.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-BB1Af7Fv.js → index-B1C1wS5c.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-lUL3bD2z.js → index-BG-VNepa.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-CtB45Hok.js → index-BMIhGZUR.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-DOww5Qny.js → index-BRNCKg4q.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-BiZ7Nz5-.js → index-B_UNX_xq.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-C9QYxq61.js → index-BmVFeSEq.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-BoCTDcn6.js → index-Bp4Gcn9u.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-CXtiLagi.js → index-BpH6T1gA.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-B5SJCUZ7.js → index-BptI9bLu.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-Cn7EIWmI.js → index-Bqeb4jnC.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-DvM2xBUA.js → index-Bwtt-0CQ.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-EK3XtMxA.js → index-C4h_Bvd2.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-DDhy4eCS.js → index-C6zGNZ5H.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-rGhS02f8.js → index-CHMrAZiR.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-BQL_tldd.js → index-CK6Rb66R.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-C7d6QlfR.js → index-CU4Y4VVN.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-Cykt9BbN.js → index-CWuv47z0.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-BXJXMaZe.js → index-CYNoXhxl.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-f_7L9GvP.js → index-CgVwz2jD.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-vHtnQsnV.js → index-ClROBPWd.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-BhPDhmvN.js → index-D7yJceTZ.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-Cu_V6ZJH.js → index-DFv-nsIT.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-CNKHoXWI.js → index-DFzvcZ3Q.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-vb1XIxEB.js → index-DJ5gsewX.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-DWPp-d07.js → index-DVORJydf.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-pOUFfRdN.js → index-D_D7lcnQ.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-cNwsev6n.js → index-DtrqgNBl.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-CBh48_Mh.js → index-DwjWRUTf.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-BxqC3LiO.js → index-DyFZ7gDd.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-XJIV7HEw.js → index-Ec6VBiV-.js} +1 -1
  130. package/src/assets/web-panel/assets/index-IkoHuWRe.js +1 -0
  131. package/src/assets/web-panel/assets/{index-CXVBQ82k.js → index-Jk99aZIr.js} +3 -3
  132. package/src/assets/web-panel/assets/{index-84mi5rsv.js → index-Lm9pm1zm.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-C6c0j-Ki.js → index-MuTb62K6.js} +1 -1
  134. package/src/assets/web-panel/assets/index-MwqsDbb1.js +1 -0
  135. package/src/assets/web-panel/assets/{index-DEPPzM8I.js → index-hzUfIn4u.js} +1 -1
  136. package/src/assets/web-panel/assets/{initDefaultProps-DpRIjwTt.js → initDefaultProps-C6fpNcA5.js} +1 -1
  137. package/src/assets/web-panel/assets/{motion-CG4_9o-Z.js → motion-Bjh9Come.js} +1 -1
  138. package/src/assets/web-panel/assets/{move-Dku1fXtf.js → move-DFa4ul-_.js} +1 -1
  139. package/src/assets/web-panel/assets/{mtc-parser-BvAVeX-3.js → mtc-parser-KoYqA_e5.js} +1 -1
  140. package/src/assets/web-panel/assets/{omit-DFu0x78D.js → omit-ByTjP-wG.js} +1 -1
  141. package/src/assets/web-panel/assets/{pickAttrs-CmI-FNcK.js → pickAttrs-CG_rmTxx.js} +1 -1
  142. package/src/assets/web-panel/assets/{placementArrow-CGERYewB.js → placementArrow-LDePZ8V9.js} +1 -1
  143. package/src/assets/web-panel/assets/{responsiveObserve-D7G8bF5J.js → responsiveObserve-DZFzH4RR.js} +1 -1
  144. package/src/assets/web-panel/assets/{slide-B2sXwWXy.js → slide-DfXQyTvD.js} +1 -1
  145. package/src/assets/web-panel/assets/{statusUtils-DI5yh0n3.js → statusUtils-CuhqsJfO.js} +1 -1
  146. package/src/assets/web-panel/assets/{styleChecker-deGJ5dp_.js → styleChecker-qqVjJw5u.js} +1 -1
  147. package/src/assets/web-panel/assets/{useFlexGapSupport-OqHBXEmi.js → useFlexGapSupport-BPxnCxcb.js} +1 -1
  148. package/src/assets/web-panel/assets/{useFs-Dqgx0Qv_.js → useFs-D11NLCjV.js} +1 -1
  149. package/src/assets/web-panel/assets/{usePersonalDataHub-Be-lveh2.js → usePersonalDataHub-HbZVcaCX.js} +1 -1
  150. package/src/assets/web-panel/assets/{vnode-CFxB876W.js → vnode-CZHQlfTu.js} +1 -1
  151. package/src/assets/web-panel/assets/{zoom-BFOYrl4e.js → zoom-CV3rUknJ.js} +1 -1
  152. package/src/assets/web-panel/index.html +1 -1
  153. package/src/commands/context.js +31 -11
  154. package/src/commands/evolution-deployment.js +70 -0
  155. package/src/commands/plugin.js +36 -5
  156. package/src/data/changelog.json +78 -0
  157. package/src/gateways/ws/evolution-deployment-topic-handlers.js +20 -0
  158. package/src/generated/cc-agent-protocol.schema.json +44 -1
  159. package/src/harness/prompt-compressor.js +12 -4
  160. package/src/lib/background-agent-list-index.js +222 -0
  161. package/src/lib/background-agent-supervisor.js +38 -0
  162. package/src/lib/codex-app-server-adapter.js +1 -0
  163. package/src/lib/context-memory-kernel/provider-context.js +69 -4
  164. package/src/lib/deferred-question-context.js +160 -4
  165. package/src/lib/eval/plugin-suite.js +286 -22
  166. package/src/lib/evolution/evolution-deployment-config.js +16 -1
  167. package/src/lib/evolution/evolution-deployment-profile.js +102 -58
  168. package/src/lib/evolution/evolution-test-deployment.js +386 -0
  169. package/src/lib/model-context-window.js +3 -2
  170. package/src/lib/runtime-usage-ledger.js +5 -0
  171. package/src/lib/session-host-snapshot.js +16 -0
  172. package/src/repl/agent-repl.js +19 -4
  173. package/src/runtime/agent-core.js +13 -0
  174. package/src/runtime/headless-runner.js +4 -0
  175. package/src/runtime/headless-stream.js +171 -14
  176. package/src/assets/web-panel/assets/EvolutionSettings-BJmBzpW6.js +0 -1
  177. package/src/assets/web-panel/assets/MarkdownRenderer-HxeocBHy.js +0 -1
  178. package/src/assets/web-panel/assets/devWarning-BHnbnuYL.js +0 -1
  179. package/src/assets/web-panel/assets/index-CsRz9-ZH.js +0 -1
  180. package/src/assets/web-panel/assets/index-DnjMFf3m.js +0 -1
@@ -1445,12 +1445,24 @@ export function registerPluginCommand(program) {
1445
1445
  "--suite <file>",
1446
1446
  "Suite JSON inside the plugin root (default: evals/suite.json)",
1447
1447
  )
1448
+ .option(
1449
+ "--holdout <file>",
1450
+ "Reviewer-owned holdout suite outside the plugin payload",
1451
+ )
1448
1452
  .option("--provider <provider>", "Fixed provider for both eval arms")
1449
1453
  .option("--model <model>", "Fixed model for both eval arms")
1450
1454
  .option(
1451
1455
  "--min-pass-rate-delta <pct>",
1452
1456
  "Override the suite's minimum candidate gain in percentage points",
1453
1457
  )
1458
+ .option(
1459
+ "--samples <n>",
1460
+ "Run each control/candidate task 1-20 times (default: suite or 1)",
1461
+ )
1462
+ .option(
1463
+ "--arm-order <order>",
1464
+ "balanced, control-first, or candidate-first (default: balanced)",
1465
+ )
1454
1466
  .option("--json", "Output the bound report as JSON")
1455
1467
  .option("--html <file>", "Write a local HTML report")
1456
1468
  .option("--keep", "Keep per-task temporary workspaces")
@@ -1479,15 +1491,30 @@ export function registerPluginCommand(program) {
1479
1491
  }
1480
1492
  minPassRateDelta = percentage / 100;
1481
1493
  }
1494
+ let sampleRuns;
1495
+ if (options.samples !== undefined) {
1496
+ sampleRuns = Number(options.samples);
1497
+ if (
1498
+ !Number.isSafeInteger(sampleRuns) ||
1499
+ sampleRuns < 1 ||
1500
+ sampleRuns > 20
1501
+ ) {
1502
+ throw new Error("--samples must be an integer between 1 and 20");
1503
+ }
1504
+ }
1482
1505
  const {
1506
+ attachPluginEvalHoldout,
1483
1507
  loadPluginEvalDefinition,
1484
1508
  renderPluginEvalHtml,
1485
1509
  runPluginEval,
1486
1510
  } = await import("../lib/eval/plugin-suite.js");
1487
1511
  const { makeHeadlessRunAgent } = await import("./eval.js");
1488
- const definition = loadPluginEvalDefinition(dir, {
1489
- suiteFile: options.suite,
1490
- });
1512
+ const definition = attachPluginEvalHoldout(
1513
+ loadPluginEvalDefinition(dir, {
1514
+ suiteFile: options.suite,
1515
+ }),
1516
+ options.holdout,
1517
+ );
1491
1518
  const dryRunAgent = async () => ({
1492
1519
  ok: true,
1493
1520
  output: `${JSON.stringify({
@@ -1526,11 +1553,15 @@ export function registerPluginCommand(program) {
1526
1553
  dryRun: options.dryRun === true,
1527
1554
  keepWorkspaces: options.keep === true,
1528
1555
  ...(minPassRateDelta === undefined ? {} : { minPassRateDelta }),
1556
+ ...(sampleRuns === undefined ? {} : { sampleRuns }),
1557
+ ...(options.armOrder === undefined
1558
+ ? {}
1559
+ : { armOrder: options.armOrder }),
1529
1560
  onResult: options.json
1530
1561
  ? undefined
1531
- : ({ arm, result }) =>
1562
+ : ({ arm, sample, result }) =>
1532
1563
  logger.info(
1533
- ` ${arm.padEnd(9)} ${result.pass ? "✔" : "✗"} ${result.id} (${result.ms}ms)`,
1564
+ ` ${arm.padEnd(9)} #${sample} ${result.pass ? "✔" : "✗"} ${result.id} (${result.ms}ms)`,
1534
1565
  ),
1535
1566
  });
1536
1567
  if (options.html) {
@@ -1,6 +1,84 @@
1
1
  {
2
2
  "source": "CHANGELOG.md",
3
3
  "releases": [
4
+ {
5
+ "productVersion": null,
6
+ "cliVersion": "0.166.56",
7
+ "cliVersions": ["0.166.56"],
8
+ "date": null,
9
+ "title": "",
10
+ "sections": [
11
+ {
12
+ "heading": "Fixed - cc CLI 0.166.56: stabilize the OIDC release gates",
13
+ "body": "- Keep the root lockfile's CLI package version aligned with the publishable CLI\n package so the cross-platform release contract remains valid.\n- Allow the REPL host's module-isolation failure test enough time on saturated\n hosted runners without relaxing any failure-path assertion."
14
+ }
15
+ ]
16
+ },
17
+ {
18
+ "productVersion": null,
19
+ "cliVersion": "0.166.54",
20
+ "cliVersions": ["0.166.54"],
21
+ "date": null,
22
+ "title": "",
23
+ "sections": [
24
+ {
25
+ "heading": "Fixed - cc CLI 0.166.54: canonicalize TEST deployment paths on macOS",
26
+ "body": "- Resolve generated credential directories and saved credential files to their\n physical paths so macOS `/var` and `/private/var` aliases do not break TEST\n deployment refresh or formal-certificate replacement.\n- Keep deployment profile path assertions portable while preserving real-path\n verification before any private key is reused."
27
+ }
28
+ ]
29
+ },
30
+ {
31
+ "productVersion": null,
32
+ "cliVersion": "0.166.53",
33
+ "cliVersions": ["0.166.53"],
34
+ "date": null,
35
+ "title": "",
36
+ "sections": [
37
+ {
38
+ "heading": "Added - cc CLI 0.166.53: one-click TEST deployment for governed Skill evolution",
39
+ "body": "- Add `cc evolution deployment init-test` to generate a local Ed25519 test key,\n sign the deployment descriptor, and enable it through the normal descriptor\n verification and profile write path, without OpenSSL or bypassing governance.\n- Add `cc evolution deployment replace-test` to rotate a test deployment into the\n formal descriptor and trust root using a constrained root-rotation proof.\n- Keep `deploymentMode: test` and the red TEST badge visible in status and UI;\n `init-test` refuses to overwrite an existing managed profile or to run while\n `CHAINLESSCHAIN_EVOLUTION_DEPLOYMENT_*` environment overrides are present.\n- Surface the same one-click generate/refresh and replace flows in the VS Code\n and JetBrains Configure Skill Evolution views and in `cc ui` settings."
40
+ }
41
+ ]
42
+ },
43
+ {
44
+ "productVersion": null,
45
+ "cliVersion": "0.166.52",
46
+ "cliVersions": ["0.166.52"],
47
+ "date": null,
48
+ "title": "",
49
+ "sections": [
50
+ {
51
+ "heading": "Fixed - cc CLI 0.166.52: wait for authoritative release gates",
52
+ "body": "- Give the exact-SHA npm release gate enough time for the full Linux, Windows,\n and macOS CLI matrix to finish when hosted runners are queued.\n- Prevent IDE release tags from scheduling a redundant CLI CI matrix for the\n same commit, preserving runner capacity for the authoritative release gate."
53
+ }
54
+ ]
55
+ },
56
+ {
57
+ "productVersion": null,
58
+ "cliVersion": "0.166.51",
59
+ "cliVersions": ["0.166.51"],
60
+ "date": null,
61
+ "title": "",
62
+ "sections": [
63
+ {
64
+ "heading": "Fixed - cc CLI 0.166.51: report the active model context accurately",
65
+ "body": "- Resolve the context window from the exact provider, endpoint, and explicit\n override used for a request, so the planner, auto-compactor, session report,\n REPL, and IDE panel share one capacity value.\n- Keep large-window conversations under a token budget rather than compacting\n solely after 50 short messages; retain the count guard for smaller windows.\n- Carry the main model request's context window and cache counters through the\n stream and session ledger, excluding sub-agent and compaction requests from\n the primary context indicator.\n- Label transcript-derived context figures as estimates and retain cache-read\n and cache-write usage in the completed chat status.\n- Keep semantic-compaction boundary fixtures explicit about their bounded\n synthetic request window, so they continue to verify compaction settlement\n rather than inheriting a provider default."
66
+ }
67
+ ]
68
+ },
69
+ {
70
+ "productVersion": null,
71
+ "cliVersion": "0.166.48",
72
+ "cliVersions": ["0.166.48"],
73
+ "date": null,
74
+ "title": "",
75
+ "sections": [
76
+ {
77
+ "heading": "Added - cc CLI 0.166.48: make evaluation, recovery, and governed recall durable",
78
+ "body": "- Add repeated balanced sampling, confidence intervals, and reviewer-owned\n holdout suites to `cc plugin eval` through `--samples`, `--arm-order`, and\n `--holdout`.\n- Persist standalone Headless deferred questions across process restarts, with\n binding checks and one-time answer consumption.\n- Add a file-identity-bound read-only index for paged background Agent status\n queries, with authoritative full-scan fallback on cache misses or races.\n- Add host-supplied, revision/digest-bound semantic recall candidates while\n preserving canonical lifecycle, scope, expiry, and sink enforcement.\n- Publish Context Memory Kernel `0.1.3` before the CLI so public installs\n resolve the new governed semantic-recall contract."
79
+ }
80
+ ]
81
+ },
4
82
  {
5
83
  "productVersion": null,
6
84
  "cliVersion": "0.166.47",
@@ -3,6 +3,10 @@ import {
3
3
  getEvolutionDeploymentStatus,
4
4
  setEvolutionDeploymentEnabled,
5
5
  } from "../../lib/evolution/evolution-deployment-config.js";
6
+ import {
7
+ initializeEvolutionTestDeployment,
8
+ replaceEvolutionTestDeployment,
9
+ } from "../../lib/evolution/evolution-test-deployment.js";
6
10
 
7
11
  function assertSecureControl(context) {
8
12
  const server = context?.server;
@@ -30,6 +34,8 @@ export function createEvolutionDeploymentTopicHandlers(options = {}) {
30
34
  const getStatus = options.getStatus || getEvolutionDeploymentStatus;
31
35
  const configure = options.configure || configureEvolutionDeployment;
32
36
  const setEnabled = options.setEnabled || setEvolutionDeploymentEnabled;
37
+ const initTest = options.initTest || initializeEvolutionTestDeployment;
38
+ const replaceTest = options.replaceTest || replaceEvolutionTestDeployment;
33
39
  return {
34
40
  "evolution.deployment.status": async (_frame, context) => {
35
41
  assertSecureControl(context);
@@ -43,6 +49,20 @@ export function createEvolutionDeploymentTopicHandlers(options = {}) {
43
49
  enabled: true,
44
50
  });
45
51
  },
52
+ "evolution.deployment.init-test": async (frame, context) => {
53
+ assertSecureControl(context);
54
+ return initTest({
55
+ modulePath: requiredPath(frame?.modulePath, "modulePath"),
56
+ enabled: true,
57
+ });
58
+ },
59
+ "evolution.deployment.replace-test": async (frame, context) => {
60
+ assertSecureControl(context);
61
+ return replaceTest({
62
+ descriptorPath: requiredPath(frame?.descriptorPath, "descriptorPath"),
63
+ trustRootPath: requiredPath(frame?.trustRootPath, "trustRootPath"),
64
+ });
65
+ },
46
66
  "evolution.deployment.set-enabled": async (frame, context) => {
47
67
  assertSecureControl(context);
48
68
  if (typeof frame?.enabled !== "boolean")
@@ -24,7 +24,7 @@
24
24
  "contextMemory": {
25
25
  "schemaId": "https://chainlesschain.com/schemas/context-memory-kernel/v1",
26
26
  "schemaVersion": 1,
27
- "sourceDigest": "sha256:9dabfbccf4315d511bb09d13decc743e1b43b90ad81eaa5da62df44ba55934cb",
27
+ "sourceDigest": "sha256:6599168b8fa9ace2d1697c44c2ac10141aa20bd7e9be29e9ea814ff032f87d68",
28
28
  "methods": {
29
29
  "context/plan": {
30
30
  "request": "ContextPlanRequest",
@@ -4808,6 +4808,13 @@
4808
4808
  "$ref": "#/$defs/ContextScopeAdmission"
4809
4809
  }
4810
4810
  },
4811
+ "semanticCandidates": {
4812
+ "type": "array",
4813
+ "maxItems": 1000,
4814
+ "items": {
4815
+ "$ref": "#/$defs/MemorySemanticCandidate"
4816
+ }
4817
+ },
4811
4818
  "limit": {
4812
4819
  "type": "integer",
4813
4820
  "minimum": 1,
@@ -4854,6 +4861,15 @@
4854
4861
  "type": "integer",
4855
4862
  "minimum": 0
4856
4863
  },
4864
+ "retrievalMode": {
4865
+ "enum": [
4866
+ "governed_hybrid"
4867
+ ]
4868
+ },
4869
+ "semanticEvidenceCount": {
4870
+ "type": "integer",
4871
+ "minimum": 0
4872
+ },
4857
4873
  "results": {
4858
4874
  "type": "array",
4859
4875
  "maxItems": 1000,
@@ -5768,6 +5784,33 @@
5768
5784
  "$ref": "#/$defs/MemoryRecallResult"
5769
5785
  }
5770
5786
  }
5787
+ },
5788
+ "MemorySemanticCandidate": {
5789
+ "type": "object",
5790
+ "additionalProperties": false,
5791
+ "required": [
5792
+ "memoryId",
5793
+ "revision",
5794
+ "recordDigest",
5795
+ "score"
5796
+ ],
5797
+ "properties": {
5798
+ "memoryId": {
5799
+ "$ref": "#/$defs/ContextMemoryIdentifier"
5800
+ },
5801
+ "revision": {
5802
+ "type": "integer",
5803
+ "minimum": 1
5804
+ },
5805
+ "recordDigest": {
5806
+ "$ref": "#/$defs/ContextMemoryDigest"
5807
+ },
5808
+ "score": {
5809
+ "type": "number",
5810
+ "minimum": 0,
5811
+ "maximum": 1
5812
+ }
5813
+ }
5771
5814
  }
5772
5815
  }
5773
5816
  }
@@ -315,7 +315,11 @@ export class PromptCompressor {
315
315
  !options.maxMessages &&
316
316
  !options.maxTokens
317
317
  ) {
318
- const ctxWindow = getContextWindow(options.model, options.provider);
318
+ const ctxWindow = getContextWindow(
319
+ options.model,
320
+ options.provider,
321
+ options,
322
+ );
319
323
  const adaptive = adaptiveThresholds(ctxWindow);
320
324
  this.maxMessages = adaptive.maxMessages;
321
325
  this.maxTokens = adaptive.maxTokens;
@@ -337,8 +341,8 @@ export class PromptCompressor {
337
341
  );
338
342
  }
339
343
 
340
- adaptToModel(model, provider) {
341
- const ctxWindow = getContextWindow(model, provider);
344
+ adaptToModel(model, provider, options = {}) {
345
+ const ctxWindow = getContextWindow(model, provider, options);
342
346
  const adaptive = adaptiveThresholds(ctxWindow);
343
347
  this.maxMessages = adaptive.maxMessages;
344
348
  this.maxTokens = adaptive.maxTokens;
@@ -483,8 +487,12 @@ export class PromptCompressor {
483
487
  }
484
488
 
485
489
  shouldAutoCompact(messages) {
490
+ // Large adaptive windows use token pressure, not an arbitrary 50-message
491
+ // trigger. Small local models and explicit fixed limits retain their
492
+ // count-based policy; maxMessages remains a compaction target in all modes.
486
493
  return (
487
- messages.length > this.maxMessages ||
494
+ ((!this._adaptive || this._contextWindow < 128000) &&
495
+ messages.length > this.maxMessages) ||
488
496
  estimateMessagesTokens(messages) > this.maxTokens
489
497
  );
490
498
  }
@@ -0,0 +1,222 @@
1
+ import {
2
+ chmodSync,
3
+ lstatSync,
4
+ openSync,
5
+ closeSync,
6
+ readFileSync,
7
+ readdirSync,
8
+ renameSync,
9
+ rmSync,
10
+ statSync,
11
+ writeFileSync,
12
+ } from "node:fs";
13
+ import { createHash, randomBytes } from "node:crypto";
14
+ import { join } from "node:path";
15
+
16
+ export const BACKGROUND_AGENT_LIST_INDEX_SCHEMA =
17
+ "chainlesschain.background-agent-list-index/v1";
18
+ export const BACKGROUND_AGENT_LIST_INDEX_FILE =
19
+ ".background-agent-list-index-v1";
20
+
21
+ const STATE_FILE = /^(bg-[A-Za-z0-9-]+)\.json$/u;
22
+ const MAX_INDEX_BYTES = 64 * 1024 * 1024;
23
+ const MAX_INDEX_ENTRIES = 1_000_000;
24
+
25
+ function sha256(value, domain) {
26
+ return `sha256:${createHash("sha256")
27
+ .update(domain)
28
+ .update("\0")
29
+ .update(value)
30
+ .digest("hex")}`;
31
+ }
32
+
33
+ function stateFileSignature(directory, name, id) {
34
+ const target = join(directory, name);
35
+ const link = lstatSync(target);
36
+ if (!link.isFile() || link.isSymbolicLink()) {
37
+ throw new TypeError(`background agent state is not a regular file: ${id}`);
38
+ }
39
+ const stat = statSync(target, { bigint: true });
40
+ return {
41
+ id,
42
+ name,
43
+ size: String(stat.size),
44
+ mtimeNs: String(stat.mtimeNs),
45
+ ctimeNs: String(stat.ctimeNs),
46
+ dev: String(stat.dev),
47
+ ino: String(stat.ino),
48
+ };
49
+ }
50
+
51
+ function collectInventory(directory) {
52
+ const files = readdirSync(directory, { withFileTypes: true })
53
+ .map((entry) => {
54
+ const match = STATE_FILE.exec(entry.name);
55
+ if (!match || entry.name.includes(".job.")) return null;
56
+ if (!entry.isFile()) {
57
+ throw new TypeError(
58
+ `background agent state is not a regular file: ${match[1]}`,
59
+ );
60
+ }
61
+ return stateFileSignature(directory, entry.name, match[1]);
62
+ })
63
+ .filter(Boolean)
64
+ .sort((left, right) => left.name.localeCompare(right.name, "en"));
65
+ if (files.length > MAX_INDEX_ENTRIES) {
66
+ throw new RangeError(
67
+ `background agent list index exceeds ${MAX_INDEX_ENTRIES} entries`,
68
+ );
69
+ }
70
+ const encoded = JSON.stringify(files);
71
+ return {
72
+ files,
73
+ digest: sha256(encoded, "cc.background-agent-list-inventory/v1"),
74
+ };
75
+ }
76
+
77
+ function normalizeEntry(value, inventoryIds) {
78
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
79
+ if (!inventoryIds.has(value.id)) return null;
80
+ const startedAt = Number(value.startedAt);
81
+ if (!Number.isFinite(startedAt) || startedAt < 0) return null;
82
+ if (
83
+ typeof value.status !== "string" ||
84
+ value.status.length === 0 ||
85
+ value.status.length > 64
86
+ ) {
87
+ return null;
88
+ }
89
+ return { id: value.id, startedAt, status: value.status };
90
+ }
91
+
92
+ function readCachedIndex(directory, inventory) {
93
+ const target = join(directory, BACKGROUND_AGENT_LIST_INDEX_FILE);
94
+ try {
95
+ const stat = lstatSync(target);
96
+ if (
97
+ !stat.isFile() ||
98
+ stat.isSymbolicLink() ||
99
+ stat.size <= 0 ||
100
+ stat.size > MAX_INDEX_BYTES
101
+ ) {
102
+ return null;
103
+ }
104
+ const parsed = JSON.parse(readFileSync(target, "utf8"));
105
+ if (
106
+ !parsed ||
107
+ typeof parsed !== "object" ||
108
+ Array.isArray(parsed) ||
109
+ parsed.schema !== BACKGROUND_AGENT_LIST_INDEX_SCHEMA ||
110
+ parsed.inventoryDigest !== inventory.digest ||
111
+ parsed.fileCount !== inventory.files.length ||
112
+ !Array.isArray(parsed.entries) ||
113
+ parsed.entries.length > inventory.files.length
114
+ ) {
115
+ return null;
116
+ }
117
+ const inventoryIds = new Set(inventory.files.map((file) => file.id));
118
+ const entries = parsed.entries.map((entry) =>
119
+ normalizeEntry(entry, inventoryIds),
120
+ );
121
+ if (entries.some((entry) => entry === null)) return null;
122
+ if (new Set(entries.map((entry) => entry.id)).size !== entries.length) {
123
+ return null;
124
+ }
125
+ const contentDigest = sha256(
126
+ JSON.stringify({
127
+ schema: parsed.schema,
128
+ inventoryDigest: parsed.inventoryDigest,
129
+ fileCount: parsed.fileCount,
130
+ entries,
131
+ }),
132
+ "cc.background-agent-list-index/v1",
133
+ );
134
+ if (parsed.digest !== contentDigest) return null;
135
+ return entries;
136
+ } catch {
137
+ return null;
138
+ }
139
+ }
140
+
141
+ function writeCachedIndex(directory, inventory, entries) {
142
+ const target = join(directory, BACKGROUND_AGENT_LIST_INDEX_FILE);
143
+ const value = {
144
+ schema: BACKGROUND_AGENT_LIST_INDEX_SCHEMA,
145
+ inventoryDigest: inventory.digest,
146
+ fileCount: inventory.files.length,
147
+ entries,
148
+ };
149
+ value.digest = sha256(
150
+ JSON.stringify(value),
151
+ "cc.background-agent-list-index/v1",
152
+ );
153
+ const temporary = `${target}.${process.pid}.${randomBytes(4).toString("hex")}.tmp`;
154
+ try {
155
+ const descriptor = openSync(temporary, "wx", 0o600);
156
+ try {
157
+ writeFileSync(descriptor, `${JSON.stringify(value)}\n`, "utf8");
158
+ } finally {
159
+ closeSync(descriptor);
160
+ }
161
+ if (process.platform !== "win32") chmodSync(temporary, 0o600);
162
+ renameSync(temporary, target);
163
+ } catch {
164
+ rmSync(temporary, { force: true });
165
+ }
166
+ }
167
+
168
+ function projectState(state, id) {
169
+ if (!state || typeof state !== "object" || String(state.id) !== id) {
170
+ return null;
171
+ }
172
+ const startedAt = Number(state.startedAt || 0);
173
+ if (!Number.isFinite(startedAt) || startedAt < 0) return null;
174
+ if (
175
+ typeof state.status !== "string" ||
176
+ state.status.length === 0 ||
177
+ state.status.length > 64
178
+ ) {
179
+ return null;
180
+ }
181
+ return { id, startedAt, status: state.status };
182
+ }
183
+
184
+ /**
185
+ * Load a content-free projection when it exactly matches every authority-file
186
+ * identity. A miss rebuilds from authority and retries the inventory fence;
187
+ * callers must fall back to their authoritative scan if a concurrent writer
188
+ * prevents a stable rebuild.
189
+ */
190
+ export function loadBackgroundAgentListIndex({ directory, readState }) {
191
+ if (typeof directory !== "string" || !directory) {
192
+ throw new TypeError("background agent list index directory is required");
193
+ }
194
+ if (typeof readState !== "function") {
195
+ throw new TypeError("background agent list index readState is required");
196
+ }
197
+ const inventory = collectInventory(directory);
198
+ const cached = readCachedIndex(directory, inventory);
199
+ if (cached) {
200
+ return Object.freeze({
201
+ source: "index",
202
+ entries: Object.freeze(cached.map((entry) => Object.freeze(entry))),
203
+ });
204
+ }
205
+
206
+ const entries = inventory.files
207
+ .map((file) => projectState(readState(file.id), file.id))
208
+ .filter(Boolean);
209
+ const verifiedInventory = collectInventory(directory);
210
+ if (verifiedInventory.digest !== inventory.digest) {
211
+ const error = new Error(
212
+ "background agent authority changed while rebuilding the list index",
213
+ );
214
+ error.code = "BACKGROUND_AGENT_LIST_INDEX_RACE";
215
+ throw error;
216
+ }
217
+ writeCachedIndex(directory, inventory, entries);
218
+ return Object.freeze({
219
+ source: "rebuilt",
220
+ entries: Object.freeze(entries.map((entry) => Object.freeze(entry))),
221
+ });
222
+ }
@@ -60,6 +60,7 @@ import {
60
60
  cleanupBackgroundAgentKeeperPipeDirectory,
61
61
  prepareBackgroundAgentKeeperPipePath,
62
62
  } from "./background-agent-keeper-protocol.js";
63
+ import { loadBackgroundAgentListIndex } from "./background-agent-list-index.js";
63
64
 
64
65
  export const DEFAULT_HEARTBEAT_INTERVAL_MS = 5000;
65
66
  export const DEFAULT_HEARTBEAT_STALE_MS = 120000;
@@ -2181,6 +2182,43 @@ export function listBackgroundAgentsPage(options = {}) {
2181
2182
  "background agent page cursor does not match the requested filter",
2182
2183
  );
2183
2184
  }
2185
+ let indexed = null;
2186
+ try {
2187
+ indexed = loadBackgroundAgentListIndex({
2188
+ directory: backgroundAgentsDir(),
2189
+ readState: readBackgroundAgentState,
2190
+ });
2191
+ } catch {
2192
+ // The index is a disposable, content-free projection. Authority races,
2193
+ // corruption, or an unwritable cache must retain the full-scan behavior.
2194
+ }
2195
+ if (indexed) {
2196
+ const projected = [];
2197
+ const summaries = [...indexed.entries]
2198
+ .filter((state) => options.all || state.status === "running")
2199
+ .sort(compareBackgroundAgentListEntries);
2200
+ for (const summary of summaries) {
2201
+ if (cursor && !isBackgroundAgentAfterCursor(summary, cursor)) continue;
2202
+ const state = readBackgroundAgentState(summary.id);
2203
+ if (!state) continue;
2204
+ const effective = effectiveBackgroundAgentState(state, options);
2205
+ if (!effective || (!options.all && effective.status !== "running")) {
2206
+ continue;
2207
+ }
2208
+ if (cursor && !isBackgroundAgentAfterCursor(effective, cursor)) continue;
2209
+ projected.push(withLifecycleState(effective));
2210
+ if (projected.length > limit) break;
2211
+ }
2212
+ const sessions = projected.slice(0, limit);
2213
+ const nextCursor =
2214
+ projected.length > sessions.length
2215
+ ? encodeBackgroundAgentPageCursor(sessions.at(-1), options.all)
2216
+ : null;
2217
+ return Object.freeze({
2218
+ sessions: Object.freeze(sessions),
2219
+ nextCursor,
2220
+ });
2221
+ }
2184
2222
  const eligible = cursor
2185
2223
  ? listBackgroundAgentEntries(options, { stableTieBreak: true }).filter(
2186
2224
  (state) => isBackgroundAgentAfterCursor(state, cursor),
@@ -6,6 +6,7 @@ export const CODEX_APP_SERVER_COMPATIBILITY_MATRIX = Object.freeze([
6
6
  Object.freeze({ version: "0.149.0" }),
7
7
  Object.freeze({ version: "0.150.0" }),
8
8
  Object.freeze({ version: "0.150.1" }),
9
+ Object.freeze({ version: "0.154.0" }),
9
10
  ]);
10
11
 
11
12
  function adapterError(code, message, details = {}) {