chainlesschain 0.166.59 → 0.166.63
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/README.md +38 -8
- package/package.json +4 -1
- package/src/assets/web-panel/assets/{AIOps-B9i3kk5f.js → AIOps-CfPrpABR.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-DGMs1wBm.js → ActionButton-Bkv1erki.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-D-JGeynM.js → Analytics-fY_7epoU.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-DLECnXlP.js → AppLayout-DegaN-aa.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-CwaTiXN4.js → Artifacts-90-UNTVE.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-BCb7s5Ky.js → Audit-BRQ1F4sA.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-D4IOzvKs.js → BackgroundAgents-Cb85uP7O.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-Cknunk10.js → Backup-CrLBSWQK.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-DnU8pbUh.js → BaseInput-DditUW5B.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-DOouYIhz.js → Chat-0XozleDQ.js} +5 -5
- package/src/assets/web-panel/assets/ChatBubbleRenderer-8QXBJvgy.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-BrumNw6V.js → Checkbox-C2Ck_3ju.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-B4E_uI_N.js → Codegen-Chjw-MS7.js} +1 -1
- package/src/assets/web-panel/assets/{Col-C7H2sdaL.js → Col-B_w8_Oka.js} +1 -1
- package/src/assets/web-panel/assets/{Community-BFdzcMSl.js → Community-XAqxPnXq.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-BzVXohrg.js → Compact-BX_3GJ3m.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-B_4QcuSH.js → Compliance-DE3kIC9z.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-DjuQzJx8.js → Cowork-DPBYp1ey.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-Bf2iY-1-.js → Cron-D9cknqzn.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-CY9QW7IT.js → Crosschain-BzHrGWY9.js} +1 -1
- package/src/assets/web-panel/assets/{DID-kDZBvaKr.js → DID-BuD8TkZl.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-DoYboS3g.js → Dashboard-BbxMuZVK.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-LLEzI6l1.js → Dropdown-jq0ki-tE.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-D2kmSvkg.js → EmailListRenderer-RiMvINxT.js} +1 -1
- package/src/assets/web-panel/assets/{EvolutionSettings-DHj2yUGZ.js → EvolutionSettings-DE-URi9i.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-B7Hg7fR0.js → FamilyGuardDashboard-DV3ao62-.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-CUdwU6aH.js → Federation-BF6BmBPg.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-D1KrwjwY.js → FormItemContext-CKTX59j2.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-CXGLoHfw.js → GenericCardRenderer-DZkH-CTs.js} +1 -1
- package/src/assets/web-panel/assets/{Git-TG3Q9JWG.js → Git-B5JYb-w-.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-BJD7h_fv.js → Governance-D3WfDZ6W.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-Dky4l_bZ.js → Inference-DoSvfDUk.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-CCx9aXBV.js → KnowledgeGraph-DUDIzN97.js} +1 -1
- package/src/assets/web-panel/assets/Logs-JfJerfFD.js +2 -0
- package/src/assets/web-panel/assets/MarkdownRenderer-DXgA7cZm.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-BzL6KJMX.js → Marketplace-BpVrOM_V.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-BReSRzEU.js → McpTools-CFnQdiSv.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-BhmkkCxd.js → Memory-Dxv81cNl.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-BeQMHr_n.js → MobileBridge-C92eX7PB.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-CphEwj_l.js → MobileProjects-BUHw7Wox.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-CFk1ibuN.js → Mtc-D-G9H4A1.js} +2 -2
- package/src/assets/web-panel/assets/{MtcAudit-CU9qrmbH.js → MtcAudit-CAEaEtAz.js} +5 -5
- package/src/assets/web-panel/assets/{Multisig-CKR-Gh0Y.js → Multisig-Cx_-X9HD.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-D1qWY6Mx.js → NLProgramming-DRIKG4Xy.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-B4CM0C65.js → Notes-CmL0vKUv.js} +4 -4
- package/src/assets/web-panel/assets/{NotificationSettings-BTQYWveR.js → NotificationSettings-D2cQ5aQn.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-DxN6fAn8.js → OrderTableRenderer-CPfd7PNy.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-scgboNfd.js → Organization-EKqO46hz.js} +2 -2
- package/src/assets/web-panel/assets/{Overflow-BP4aQhid.js → Overflow-BOHjBwJo.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-65jIo6MY.js → P2P-Dx4LdCE0.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-Bfi4-tDQ.js → PdhVaultBrowser-CgCt2xdT.js} +5 -5
- package/src/assets/web-panel/assets/{Permissions-DWtxT7bE.js → Permissions-DWjDeO7O.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-Bilmr8BR.js → PersonalDataHub-Bx4Od8pG.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-IPIMM-1y.js → Pipeline-oKxws_Ch.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-DFB8RiVK.js → Privacy-Df1HVRKR.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-DxZksvu1.js → ProjectInit-BraLwZT4.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-DsjPsnYN.js → ProjectSettings-CKru54M0.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-CC5FBLLq.js → Projects-BEr7cJcS.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-Dk3ViPuF.js → Providers-C8v30DU3.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-CJ8R6-bF.js → QrScannerModal-CPyNV-O5.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-DJfCPORc.js → QuickAsk-BLIew7A5.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-DjDygXeb.js → Recommend-DVjWmsPs.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-BJiJ1YHw.js → RemoteSession-B4PNTaEf.js} +3 -3
- package/src/assets/web-panel/assets/{Reputation-fYE8jPcc.js → Reputation-D5HOV6oI.js} +1 -1
- package/src/assets/web-panel/assets/{Row-vcvsjMm4.js → Row-DgCQom2M.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-CZnKliwn.js → RssFeed-zvIcGHgz.js} +3 -3
- package/src/assets/web-panel/assets/{Search-DEStiFGI.js → Search-C-KbJP_z.js} +1 -1
- package/src/assets/web-panel/assets/{Security-BPb_szxS.js → Security-CasnI-Ld.js} +3 -3
- package/src/assets/web-panel/assets/{Services-TR0xZhSF.js → Services-CR-pSlFv.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-BSPu9boC.js → Skeleton-BB5On1b5.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-CXEj9pC6.js → Skills-CC-wdQi-.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-jlOlDGMA.js → Sla-CPX5QKEu.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-C8oPE_HQ.js → SpeechSettings-Bifkl6Zz.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-DZNUx8zt.js → SyncSettings-CdUVeB-S.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-B4T6aoOl.js → Tasks-BttwN3fs.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-DeRgtwZ5.js → Templates-CBHNbpQ7.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-DDVoXGxa.js → Tenant-B90lEwSQ.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-DGAUoJMz.js → Terminal-u8KqitsA.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-Co4Nkpjm.js → TimelineRenderer-CnEJMkJo.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-DaMLa0Fm.js → Tokens-CTM2OOJ_.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-C7iLr-Ik.js → Trigger-Ca8NmmgU.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-CxX_tQqi.js → Trust-CI9GpUj2.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-ClrdckEx.js → UkeySign-BlMLGqKK.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-DgTVhaoK.js → VideoEditing-C8yCZrH0.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-SIbwES0O.js → Wallet-BpuYrz8o.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-YNqVnhZF.js → WebAuthn-DFpNfLG_.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-BvynT9mj.js → WorkflowEditor-BWhLoeU4.js} +1 -1
- package/src/assets/web-panel/assets/{chat-X79b2tVy.js → chat-Dwo4LC7E.js} +1 -1
- package/src/assets/web-panel/assets/{colors-DioWDCbu.js → colors-DkdGvKBf.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-DpAXXaqu.js → compact-item-1gI0lfs3.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-BYq8bCQX.js → createContext-O9iHirtr.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-axBqv361.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-CDJNMJ_n.js → hasIn-DY1jo9BQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-DUe-hFGb.js → index-1AnBwAoM.js} +1 -1
- package/src/assets/web-panel/assets/{index-CS3QdYDW.js → index-B-kbagPi.js} +1 -1
- package/src/assets/web-panel/assets/{index-DZysaS5T.js → index-B0TX-bLV.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cx0_WiSe.js → index-B5l7eb6E.js} +1 -1
- package/src/assets/web-panel/assets/{index-CJEFd0QJ.js → index-BDwV_V5g.js} +1 -1
- package/src/assets/web-panel/assets/{index-D88MWTOx.js → index-BgHG4f2K.js} +1 -1
- package/src/assets/web-panel/assets/{index-BK53HyEP.js → index-BgVO1aXv.js} +1 -1
- package/src/assets/web-panel/assets/{index-COqkDiRU.js → index-Bl6GfsQZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-CUbuCFWb.js → index-BmAK4o9I.js} +1 -1
- package/src/assets/web-panel/assets/{index-C7cXBJcM.js → index-BsQkwBu2.js} +1 -1
- package/src/assets/web-panel/assets/{index-Byt1ny7l.js → index-BumRAO-7.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bh9la-GL.js → index-Bx4MNNMY.js} +1 -1
- package/src/assets/web-panel/assets/{index-B8_grEFP.js → index-C4c0XaiT.js} +1 -1
- package/src/assets/web-panel/assets/index-CAsOttsq.js +1 -0
- package/src/assets/web-panel/assets/{index-YviHYMA2.js → index-CBqSTwY9.js} +1 -1
- package/src/assets/web-panel/assets/{index-00kVj4me.js → index-D8fqsTHW.js} +1 -1
- package/src/assets/web-panel/assets/{index-Wl55a61D.js → index-DCqPIE3G.js} +1 -1
- package/src/assets/web-panel/assets/{index-CLSt7mOy.js → index-DNnaEzC5.js} +1 -1
- package/src/assets/web-panel/assets/{index-C4xggKQp.js → index-DTppQua6.js} +1 -1
- package/src/assets/web-panel/assets/{index-CbxqdIzD.js → index-DfFzjnA0.js} +1 -1
- package/src/assets/web-panel/assets/{index-BwHEGjR7.js → index-DskEFn8M.js} +1 -1
- package/src/assets/web-panel/assets/{index-D3J9FKFr.js → index-DvRvwzc-.js} +1 -1
- package/src/assets/web-panel/assets/{index-BGEXGr9g.js → index-DwBcFhcc.js} +1 -1
- package/src/assets/web-panel/assets/{index-B6wxVEIZ.js → index-DwJBbK3Z.js} +1 -1
- package/src/assets/web-panel/assets/{index-mHAPEcPi.js → index-DwhJqZqM.js} +1 -1
- package/src/assets/web-panel/assets/{index-C1-NqghD.js → index-Dx51Am-0.js} +1 -1
- package/src/assets/web-panel/assets/{index-DP9ZLjra.js → index-E5Ar6dNV.js} +1 -1
- package/src/assets/web-panel/assets/{index-DSFkmRvK.js → index-ETHEhWvI.js} +1 -1
- package/src/assets/web-panel/assets/{index-DmNspbVU.js → index-KUrBWzIM.js} +1 -1
- package/src/assets/web-panel/assets/index-MgEKheCh.js +1 -0
- package/src/assets/web-panel/assets/{index-TqLyy6IR.js → index-YnGvCu0P.js} +3 -3
- package/src/assets/web-panel/assets/{index-Q3WQ5Zhr.js → index-hRtVittm.js} +1 -1
- package/src/assets/web-panel/assets/{index-DAnXSg0r.js → index-nXoV4OCZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BBvA7Hxj.js → index-qUbrqzvO.js} +1 -1
- package/src/assets/web-panel/assets/{index-BIO8EVHt.js → index-rIJqtLz-.js} +1 -1
- package/src/assets/web-panel/assets/{index-B8ChecWd.js → index-rkkMl4Lb.js} +1 -1
- package/src/assets/web-panel/assets/{index-DwG9khVs.js → index-tXjOuLZm.js} +1 -1
- package/src/assets/web-panel/assets/{index-V6l3ia83.js → index-zLOzCcxM.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bt0zcD5R.js → index-zPpssHXA.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-18rmGkik.js → initDefaultProps-Du5HHTVt.js} +1 -1
- package/src/assets/web-panel/assets/{motion-3kvGzSgV.js → motion-D9mAVPKP.js} +1 -1
- package/src/assets/web-panel/assets/{move-Dx3DiVHc.js → move-DPO_sFtt.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-VX1dFJEI.js → mtc-parser-CnezVcXi.js} +1 -1
- package/src/assets/web-panel/assets/{omit-CniSeCOA.js → omit-DNpYMbet.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-RPggmcOd.js → pickAttrs-BUFm_slL.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-B45YBykr.js → placementArrow-DvwJtnzY.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-D0nciAEn.js → responsiveObserve-CUWOh_KI.js} +1 -1
- package/src/assets/web-panel/assets/{slide-UxLNWqjI.js → slide-bVHCUsdj.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-CukpV_uI.js → statusUtils-FlOzOrxf.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-BIcEZk6Y.js → styleChecker-C6CFXutA.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-Zah-xvV3.js → useFlexGapSupport-BNGqO_E0.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-BA7ihKFN.js → useFs-DyKOrA27.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-C64CsyY3.js → usePersonalDataHub-DZAqeMAJ.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-Datcekgj.js → vnode-Do7fVasi.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-CaFQv1KF.js → zoom-DYkEmOT2.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/data/changelog.json +52 -0
- package/src/lib/background-agent-supervisor.js +10 -1
- package/src/lib/evolution/evidence-backed-wiki-maintainer.js +119 -12
- package/src/lib/evolution/evolution-artifact-ports.js +6 -0
- package/src/lib/evolution/evolution-deployment-loader.js +139 -0
- package/src/lib/evolution/evolution-eval-gate.js +5 -0
- package/src/lib/evolution/evolution-ledger-ports.js +41 -17
- package/src/lib/evolution/pm-exploration-benchmark.js +253 -0
- package/src/lib/evolution/pm-exploration-budget-executor.js +346 -0
- package/src/lib/evolution/pm-exploration-business-grader.js +581 -0
- package/src/lib/evolution/pm-exploration-evidence-bundle.js +613 -0
- package/src/lib/evolution/pm-exploration-execution-host.js +946 -0
- package/src/lib/evolution/pm-exploration-ledger-adapter.js +860 -0
- package/src/lib/evolution/pm-exploration-provider-settlement-adapter.js +420 -0
- package/src/lib/evolution/pm-exploration-receipts.js +626 -0
- package/src/lib/evolution/pm-exploration-recovery-snapshot-store.js +521 -0
- package/src/lib/evolution/pm-exploration-rounds.js +842 -0
- package/src/lib/evolution/pm-exploration-transition-committer.js +541 -0
- package/src/lib/evolution/pm-exploration-volcengine-provider.js +724 -0
- package/src/lib/evolution/pm-result-grader.cjs +287 -0
- package/src/lib/read-file-loop-guard.js +36 -5
- package/src/lib/secure-fs.js +51 -4
- package/src/lib/task-progress-tracker.js +29 -7
- package/src/lib/text-file-search.js +1 -0
- package/src/runtime/agent-core.js +46 -4
- package/src/assets/web-panel/assets/ChatBubbleRenderer-C4tdSms8.js +0 -1
- package/src/assets/web-panel/assets/Logs-BtesccPB.js +0 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-DoN7oE2J.js +0 -1
- package/src/assets/web-panel/assets/devWarning-ONeWC5dH.js +0 -1
- package/src/assets/web-panel/assets/index--QLnQuO9.js +0 -1
- package/src/assets/web-panel/assets/index-CkgeII0e.js +0 -1
|
@@ -0,0 +1,842 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { isProxy } from "node:util/types";
|
|
3
|
+
|
|
4
|
+
export const PM_EXPLORATION_PLAN_SCHEMA =
|
|
5
|
+
"chainlesschain.pm-exploration-plan/v1";
|
|
6
|
+
export const PM_EXPLORATION_CHECKPOINT_SCHEMA =
|
|
7
|
+
"chainlesschain.pm-exploration-checkpoint/v1";
|
|
8
|
+
export const PM_EXPLORATION_MERGE_SCHEMA =
|
|
9
|
+
"chainlesschain.pm-exploration-merge/v1";
|
|
10
|
+
export const PM_EXPLORATION_FROZEN_MEMORY_SCHEMA =
|
|
11
|
+
"chainlesschain.pm-exploration-frozen-memory/v1";
|
|
12
|
+
export const PM_EXPLORATION_RECOVERY_SNAPSHOT_SCHEMA =
|
|
13
|
+
"chainlesschain.pm-exploration-recovery-snapshot/v1";
|
|
14
|
+
|
|
15
|
+
const DIGEST = /^sha256:[a-f0-9]{64}$/u;
|
|
16
|
+
const ID = /^[a-z][a-z0-9]*(?:[._:-][a-z0-9]+)*$/u;
|
|
17
|
+
const JOURNALS = new WeakMap();
|
|
18
|
+
const ROUND_TOKENS = new WeakMap();
|
|
19
|
+
|
|
20
|
+
function canonical(value) {
|
|
21
|
+
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
22
|
+
if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
|
|
23
|
+
return `{${Object.keys(value)
|
|
24
|
+
.sort()
|
|
25
|
+
.map((key) => `${JSON.stringify(key)}:${canonical(value[key])}`)
|
|
26
|
+
.join(",")}}`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function hash(domain, value) {
|
|
30
|
+
return `sha256:${createHash("sha256")
|
|
31
|
+
.update(domain)
|
|
32
|
+
.update("\0")
|
|
33
|
+
.update(canonical(value))
|
|
34
|
+
.digest("hex")}`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function exact(value, keys, label) {
|
|
38
|
+
if (
|
|
39
|
+
!value ||
|
|
40
|
+
typeof value !== "object" ||
|
|
41
|
+
Array.isArray(value) ||
|
|
42
|
+
isProxy(value) ||
|
|
43
|
+
Object.getPrototypeOf(value) !== Object.prototype
|
|
44
|
+
) {
|
|
45
|
+
throw new TypeError(`${label} must be a plain object`);
|
|
46
|
+
}
|
|
47
|
+
const actual = Reflect.ownKeys(value);
|
|
48
|
+
if (
|
|
49
|
+
actual.length !== keys.length ||
|
|
50
|
+
actual.some((key) => {
|
|
51
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
52
|
+
return (
|
|
53
|
+
typeof key !== "string" ||
|
|
54
|
+
!keys.includes(key) ||
|
|
55
|
+
!descriptor ||
|
|
56
|
+
!descriptor.enumerable ||
|
|
57
|
+
!("value" in descriptor)
|
|
58
|
+
);
|
|
59
|
+
})
|
|
60
|
+
) {
|
|
61
|
+
throw new TypeError(`${label} has unexpected or accessor fields`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function identifier(value, label) {
|
|
66
|
+
if (typeof value !== "string" || value.length > 256 || !ID.test(value))
|
|
67
|
+
throw new TypeError(`${label} is invalid`);
|
|
68
|
+
return value;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function identifierList(value, label, minimum, maximum) {
|
|
72
|
+
if (
|
|
73
|
+
!Array.isArray(value) ||
|
|
74
|
+
isProxy(value) ||
|
|
75
|
+
Object.getPrototypeOf(value) !== Array.prototype ||
|
|
76
|
+
value.length < minimum ||
|
|
77
|
+
value.length > maximum
|
|
78
|
+
) {
|
|
79
|
+
throw new TypeError(
|
|
80
|
+
`${label} must contain from ${minimum} to ${maximum} identifiers`,
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
const ownKeys = Reflect.ownKeys(value);
|
|
84
|
+
if (
|
|
85
|
+
ownKeys.length !== value.length + 1 ||
|
|
86
|
+
ownKeys.some((key) => {
|
|
87
|
+
if (key === "length") {
|
|
88
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
89
|
+
return !descriptor || !("value" in descriptor);
|
|
90
|
+
}
|
|
91
|
+
if (typeof key !== "string" || !/^(?:0|[1-9][0-9]*)$/u.test(key))
|
|
92
|
+
return true;
|
|
93
|
+
const index = Number(key);
|
|
94
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
95
|
+
return (
|
|
96
|
+
index >= value.length ||
|
|
97
|
+
!descriptor ||
|
|
98
|
+
!descriptor.enumerable ||
|
|
99
|
+
!("value" in descriptor)
|
|
100
|
+
);
|
|
101
|
+
})
|
|
102
|
+
) {
|
|
103
|
+
throw new TypeError(`${label} cannot contain holes or accessor fields`);
|
|
104
|
+
}
|
|
105
|
+
const result = Array.from({ length: value.length }, (_, index) =>
|
|
106
|
+
identifier(
|
|
107
|
+
Object.getOwnPropertyDescriptor(value, String(index)).value,
|
|
108
|
+
`${label} entry`,
|
|
109
|
+
),
|
|
110
|
+
);
|
|
111
|
+
if (new Set(result).size !== result.length)
|
|
112
|
+
throw new TypeError(`${label} must be unique`);
|
|
113
|
+
return result;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function digest(value, label) {
|
|
117
|
+
if (typeof value !== "string" || !DIGEST.test(value))
|
|
118
|
+
throw new TypeError(`${label} must be a sha256 digest`);
|
|
119
|
+
return value;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function integer(value, label, minimum = 0, maximum = Number.MAX_SAFE_INTEGER) {
|
|
123
|
+
if (!Number.isSafeInteger(value) || value < minimum || value > maximum)
|
|
124
|
+
throw new TypeError(`${label} is outside its allowed range`);
|
|
125
|
+
return value;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function deepFreeze(value, seen = new WeakSet()) {
|
|
129
|
+
if (!value || typeof value !== "object" || seen.has(value)) return value;
|
|
130
|
+
seen.add(value);
|
|
131
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
132
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
133
|
+
if (descriptor && "value" in descriptor) deepFreeze(descriptor.value, seen);
|
|
134
|
+
}
|
|
135
|
+
return Object.freeze(value);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function copyPlainData(
|
|
139
|
+
value,
|
|
140
|
+
label,
|
|
141
|
+
context = { seen: new WeakSet(), nodes: 0 },
|
|
142
|
+
) {
|
|
143
|
+
if (
|
|
144
|
+
value === null ||
|
|
145
|
+
typeof value === "string" ||
|
|
146
|
+
typeof value === "boolean"
|
|
147
|
+
) {
|
|
148
|
+
return value;
|
|
149
|
+
}
|
|
150
|
+
if (typeof value === "number" && Number.isSafeInteger(value)) return value;
|
|
151
|
+
if (
|
|
152
|
+
!value ||
|
|
153
|
+
typeof value !== "object" ||
|
|
154
|
+
isProxy(value) ||
|
|
155
|
+
context.seen.has(value) ||
|
|
156
|
+
context.nodes >= 20_000
|
|
157
|
+
) {
|
|
158
|
+
throw new TypeError(`${label} must be bounded acyclic plain data`);
|
|
159
|
+
}
|
|
160
|
+
context.seen.add(value);
|
|
161
|
+
context.nodes += 1;
|
|
162
|
+
if (Array.isArray(value)) {
|
|
163
|
+
if (Object.getPrototypeOf(value) !== Array.prototype)
|
|
164
|
+
throw new TypeError(`${label} must use the default array prototype`);
|
|
165
|
+
const keys = Reflect.ownKeys(value);
|
|
166
|
+
if (keys.length !== value.length + 1)
|
|
167
|
+
throw new TypeError(`${label} cannot contain holes or extra fields`);
|
|
168
|
+
const result = [];
|
|
169
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
170
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
|
|
171
|
+
if (!descriptor || !descriptor.enumerable || !("value" in descriptor))
|
|
172
|
+
throw new TypeError(`${label} cannot contain holes or accessor fields`);
|
|
173
|
+
result.push(copyPlainData(descriptor.value, label, context));
|
|
174
|
+
}
|
|
175
|
+
context.seen.delete(value);
|
|
176
|
+
return result;
|
|
177
|
+
}
|
|
178
|
+
if (Object.getPrototypeOf(value) !== Object.prototype)
|
|
179
|
+
throw new TypeError(`${label} must use the default object prototype`);
|
|
180
|
+
const result = {};
|
|
181
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
182
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
183
|
+
if (
|
|
184
|
+
typeof key !== "string" ||
|
|
185
|
+
!descriptor ||
|
|
186
|
+
!descriptor.enumerable ||
|
|
187
|
+
!("value" in descriptor)
|
|
188
|
+
) {
|
|
189
|
+
throw new TypeError(`${label} cannot contain symbol or accessor fields`);
|
|
190
|
+
}
|
|
191
|
+
Object.defineProperty(result, key, {
|
|
192
|
+
value: copyPlainData(descriptor.value, label, context),
|
|
193
|
+
enumerable: true,
|
|
194
|
+
writable: true,
|
|
195
|
+
configurable: true,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
context.seen.delete(value);
|
|
199
|
+
return result;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function stateFor(journal) {
|
|
203
|
+
const state = JOURNALS.get(journal);
|
|
204
|
+
if (!state)
|
|
205
|
+
throw new TypeError("a branded PM exploration journal is required");
|
|
206
|
+
return state;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export function createPmExplorationPlan(input) {
|
|
210
|
+
exact(
|
|
211
|
+
input,
|
|
212
|
+
[
|
|
213
|
+
"planId",
|
|
214
|
+
"suiteDigest",
|
|
215
|
+
"trainingPartitionDigest",
|
|
216
|
+
"trainingTaskIds",
|
|
217
|
+
"environmentDigest",
|
|
218
|
+
"initialMemoryDigest",
|
|
219
|
+
"broadBranchIds",
|
|
220
|
+
"maxRounds",
|
|
221
|
+
"maxTokens",
|
|
222
|
+
"maxToolCalls",
|
|
223
|
+
"maxWallClockMs",
|
|
224
|
+
"maxConsecutiveNoGain",
|
|
225
|
+
],
|
|
226
|
+
"PM exploration plan",
|
|
227
|
+
);
|
|
228
|
+
const trainingTaskIds = identifierList(
|
|
229
|
+
input.trainingTaskIds,
|
|
230
|
+
"trainingTaskIds",
|
|
231
|
+
1,
|
|
232
|
+
10_000,
|
|
233
|
+
);
|
|
234
|
+
const broadBranchIds = identifierList(
|
|
235
|
+
input.broadBranchIds,
|
|
236
|
+
"broadBranchIds",
|
|
237
|
+
1,
|
|
238
|
+
8,
|
|
239
|
+
);
|
|
240
|
+
const maxRounds = integer(input.maxRounds, "maxRounds", 1, 64);
|
|
241
|
+
const maxConsecutiveNoGain = integer(
|
|
242
|
+
input.maxConsecutiveNoGain,
|
|
243
|
+
"maxConsecutiveNoGain",
|
|
244
|
+
1,
|
|
245
|
+
maxRounds,
|
|
246
|
+
);
|
|
247
|
+
const core = {
|
|
248
|
+
schema: PM_EXPLORATION_PLAN_SCHEMA,
|
|
249
|
+
planId: identifier(input.planId, "planId"),
|
|
250
|
+
suiteDigest: digest(input.suiteDigest, "suiteDigest"),
|
|
251
|
+
trainingPartitionDigest: digest(
|
|
252
|
+
input.trainingPartitionDigest,
|
|
253
|
+
"trainingPartitionDigest",
|
|
254
|
+
),
|
|
255
|
+
trainingTaskIds,
|
|
256
|
+
environmentDigest: digest(input.environmentDigest, "environmentDigest"),
|
|
257
|
+
initialMemoryDigest: digest(
|
|
258
|
+
input.initialMemoryDigest,
|
|
259
|
+
"initialMemoryDigest",
|
|
260
|
+
),
|
|
261
|
+
broadBranchIds,
|
|
262
|
+
maxRounds,
|
|
263
|
+
maxTokens: integer(input.maxTokens, "maxTokens", 1),
|
|
264
|
+
maxToolCalls: integer(input.maxToolCalls, "maxToolCalls", 1),
|
|
265
|
+
maxWallClockMs: integer(input.maxWallClockMs, "maxWallClockMs", 1),
|
|
266
|
+
maxConsecutiveNoGain,
|
|
267
|
+
};
|
|
268
|
+
return deepFreeze({
|
|
269
|
+
...core,
|
|
270
|
+
planDigest: hash(PM_EXPLORATION_PLAN_SCHEMA, core),
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function validatedPmExplorationPlan(plan) {
|
|
275
|
+
exact(
|
|
276
|
+
plan,
|
|
277
|
+
[
|
|
278
|
+
"schema",
|
|
279
|
+
"planId",
|
|
280
|
+
"suiteDigest",
|
|
281
|
+
"trainingPartitionDigest",
|
|
282
|
+
"trainingTaskIds",
|
|
283
|
+
"environmentDigest",
|
|
284
|
+
"initialMemoryDigest",
|
|
285
|
+
"broadBranchIds",
|
|
286
|
+
"maxRounds",
|
|
287
|
+
"maxTokens",
|
|
288
|
+
"maxToolCalls",
|
|
289
|
+
"maxWallClockMs",
|
|
290
|
+
"maxConsecutiveNoGain",
|
|
291
|
+
"planDigest",
|
|
292
|
+
],
|
|
293
|
+
"PM exploration plan envelope",
|
|
294
|
+
);
|
|
295
|
+
if (plan.schema !== PM_EXPLORATION_PLAN_SCHEMA)
|
|
296
|
+
throw new TypeError("PM exploration plan schema is unsupported");
|
|
297
|
+
const verified = createPmExplorationPlan({
|
|
298
|
+
planId: plan.planId,
|
|
299
|
+
suiteDigest: plan.suiteDigest,
|
|
300
|
+
trainingPartitionDigest: plan.trainingPartitionDigest,
|
|
301
|
+
trainingTaskIds: plan.trainingTaskIds,
|
|
302
|
+
environmentDigest: plan.environmentDigest,
|
|
303
|
+
initialMemoryDigest: plan.initialMemoryDigest,
|
|
304
|
+
broadBranchIds: plan.broadBranchIds,
|
|
305
|
+
maxRounds: plan.maxRounds,
|
|
306
|
+
maxTokens: plan.maxTokens,
|
|
307
|
+
maxToolCalls: plan.maxToolCalls,
|
|
308
|
+
maxWallClockMs: plan.maxWallClockMs,
|
|
309
|
+
maxConsecutiveNoGain: plan.maxConsecutiveNoGain,
|
|
310
|
+
});
|
|
311
|
+
if (verified.planDigest !== plan.planDigest)
|
|
312
|
+
throw new TypeError("PM exploration plan digest mismatch");
|
|
313
|
+
return verified;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
export function verifyPmExplorationPlan(plan) {
|
|
317
|
+
validatedPmExplorationPlan(plan);
|
|
318
|
+
return true;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
export function createPmExplorationJournal(plan) {
|
|
322
|
+
const verifiedPlan = validatedPmExplorationPlan(plan);
|
|
323
|
+
const journal = Object.freeze({});
|
|
324
|
+
JOURNALS.set(journal, {
|
|
325
|
+
plan: verifiedPlan,
|
|
326
|
+
stage: "broad",
|
|
327
|
+
branchHeads: new Map(
|
|
328
|
+
verifiedPlan.broadBranchIds.map((branchId) => [
|
|
329
|
+
branchId,
|
|
330
|
+
verifiedPlan.initialMemoryDigest,
|
|
331
|
+
]),
|
|
332
|
+
),
|
|
333
|
+
branchCheckpointDigests: new Map(
|
|
334
|
+
verifiedPlan.broadBranchIds.map((branchId) => [branchId, []]),
|
|
335
|
+
),
|
|
336
|
+
activeBranches: new Set(),
|
|
337
|
+
activeRoundIds: new Set(),
|
|
338
|
+
usedRoundIds: new Set(),
|
|
339
|
+
checkpoints: [],
|
|
340
|
+
merge: null,
|
|
341
|
+
deepHead: null,
|
|
342
|
+
totals: { tokens: 0, toolCalls: 0, wallClockMs: 0 },
|
|
343
|
+
consecutiveNoGain: 0,
|
|
344
|
+
stopReason: null,
|
|
345
|
+
frozen: null,
|
|
346
|
+
});
|
|
347
|
+
return journal;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
export function startPmExplorationRound(journal, input) {
|
|
351
|
+
const state = stateFor(journal);
|
|
352
|
+
exact(
|
|
353
|
+
input,
|
|
354
|
+
["roundId", "stage", "branchId", "taskId", "inputMemoryDigest"],
|
|
355
|
+
"PM exploration round",
|
|
356
|
+
);
|
|
357
|
+
if (state.frozen) throw new Error("PM exploration memory is already frozen");
|
|
358
|
+
if (state.stopReason)
|
|
359
|
+
throw new Error(`PM exploration stopped: ${state.stopReason}`);
|
|
360
|
+
if (state.checkpoints.length >= state.plan.maxRounds)
|
|
361
|
+
throw new Error("PM exploration round budget is exhausted");
|
|
362
|
+
const roundId = identifier(input.roundId, "roundId");
|
|
363
|
+
if (state.usedRoundIds.has(roundId) || state.activeRoundIds.has(roundId))
|
|
364
|
+
throw new Error("PM exploration round ID has already been used");
|
|
365
|
+
const taskId = identifier(input.taskId, "taskId");
|
|
366
|
+
if (!state.plan.trainingTaskIds.includes(taskId))
|
|
367
|
+
throw new Error("round task is outside the bound training partition");
|
|
368
|
+
const inputMemoryDigest = digest(
|
|
369
|
+
input.inputMemoryDigest,
|
|
370
|
+
"inputMemoryDigest",
|
|
371
|
+
);
|
|
372
|
+
let branchId = null;
|
|
373
|
+
let expectedHead;
|
|
374
|
+
if (input.stage === "broad") {
|
|
375
|
+
if (state.stage !== "broad") throw new Error("Broad exploration is closed");
|
|
376
|
+
branchId = identifier(input.branchId, "branchId");
|
|
377
|
+
if (!state.branchHeads.has(branchId))
|
|
378
|
+
throw new Error("Unknown broad branch");
|
|
379
|
+
if (state.activeBranches.has(branchId))
|
|
380
|
+
throw new Error("Broad branch already has an active round");
|
|
381
|
+
expectedHead = state.branchHeads.get(branchId);
|
|
382
|
+
state.activeBranches.add(branchId);
|
|
383
|
+
} else if (input.stage === "deep") {
|
|
384
|
+
if (input.branchId !== null)
|
|
385
|
+
throw new TypeError("Deep rounds cannot name a branch");
|
|
386
|
+
if (state.stage !== "deep")
|
|
387
|
+
throw new Error("Deep exploration is not active");
|
|
388
|
+
if (state.activeRoundIds.size > 0)
|
|
389
|
+
throw new Error("Deep exploration must be sequential");
|
|
390
|
+
expectedHead = state.deepHead;
|
|
391
|
+
} else {
|
|
392
|
+
throw new TypeError("round stage must be broad or deep");
|
|
393
|
+
}
|
|
394
|
+
if (inputMemoryDigest !== expectedHead) {
|
|
395
|
+
if (branchId !== null) state.activeBranches.delete(branchId);
|
|
396
|
+
throw new Error("round input memory does not match its current checkpoint");
|
|
397
|
+
}
|
|
398
|
+
const round = Object.freeze({});
|
|
399
|
+
state.activeRoundIds.add(roundId);
|
|
400
|
+
ROUND_TOKENS.set(round, {
|
|
401
|
+
journal,
|
|
402
|
+
roundId,
|
|
403
|
+
stage: input.stage,
|
|
404
|
+
branchId,
|
|
405
|
+
taskId,
|
|
406
|
+
inputMemoryDigest,
|
|
407
|
+
consumed: false,
|
|
408
|
+
});
|
|
409
|
+
return round;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
function metrics(value) {
|
|
413
|
+
exact(value, ["tokens", "toolCalls", "wallClockMs"], "round metrics");
|
|
414
|
+
return {
|
|
415
|
+
tokens: integer(value.tokens, "tokens"),
|
|
416
|
+
toolCalls: integer(value.toolCalls, "toolCalls"),
|
|
417
|
+
wallClockMs: integer(value.wallClockMs, "wallClockMs"),
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
function updateStopReason(state, budgetExceeded) {
|
|
422
|
+
if (
|
|
423
|
+
budgetExceeded ||
|
|
424
|
+
state.totals.tokens >= state.plan.maxTokens ||
|
|
425
|
+
state.totals.toolCalls >= state.plan.maxToolCalls ||
|
|
426
|
+
state.totals.wallClockMs >= state.plan.maxWallClockMs
|
|
427
|
+
) {
|
|
428
|
+
state.stopReason = "budget-exhausted";
|
|
429
|
+
} else if (state.checkpoints.length >= state.plan.maxRounds)
|
|
430
|
+
state.stopReason = "max-rounds";
|
|
431
|
+
else if (state.consecutiveNoGain >= state.plan.maxConsecutiveNoGain)
|
|
432
|
+
state.stopReason = "consecutive-no-gain";
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
export function completePmExplorationRound(journal, round, input) {
|
|
436
|
+
const state = stateFor(journal);
|
|
437
|
+
const binding = ROUND_TOKENS.get(round);
|
|
438
|
+
if (!binding || binding.journal !== journal)
|
|
439
|
+
throw new TypeError("round capability does not belong to this journal");
|
|
440
|
+
if (binding.consumed) throw new Error("round capability has already settled");
|
|
441
|
+
exact(
|
|
442
|
+
input,
|
|
443
|
+
[
|
|
444
|
+
"executionReceiptDigest",
|
|
445
|
+
"graderReceiptDigest",
|
|
446
|
+
"outputMemoryDigest",
|
|
447
|
+
"decision",
|
|
448
|
+
"metrics",
|
|
449
|
+
],
|
|
450
|
+
"PM round completion",
|
|
451
|
+
);
|
|
452
|
+
if (!["accept", "reject", "unsafe"].includes(input.decision))
|
|
453
|
+
throw new TypeError("round decision is invalid");
|
|
454
|
+
const executionReceiptDigest = digest(
|
|
455
|
+
input.executionReceiptDigest,
|
|
456
|
+
"executionReceiptDigest",
|
|
457
|
+
);
|
|
458
|
+
const graderReceiptDigest = digest(
|
|
459
|
+
input.graderReceiptDigest,
|
|
460
|
+
"graderReceiptDigest",
|
|
461
|
+
);
|
|
462
|
+
const outputMemoryDigest = digest(
|
|
463
|
+
input.outputMemoryDigest,
|
|
464
|
+
"outputMemoryDigest",
|
|
465
|
+
);
|
|
466
|
+
const consumed = metrics(input.metrics);
|
|
467
|
+
const nextTotals = {
|
|
468
|
+
tokens: state.totals.tokens + consumed.tokens,
|
|
469
|
+
toolCalls: state.totals.toolCalls + consumed.toolCalls,
|
|
470
|
+
wallClockMs: state.totals.wallClockMs + consumed.wallClockMs,
|
|
471
|
+
};
|
|
472
|
+
let budgetExceeded = false;
|
|
473
|
+
for (const [key, maximum] of [
|
|
474
|
+
["tokens", state.plan.maxTokens],
|
|
475
|
+
["toolCalls", state.plan.maxToolCalls],
|
|
476
|
+
["wallClockMs", state.plan.maxWallClockMs],
|
|
477
|
+
]) {
|
|
478
|
+
if (!Number.isSafeInteger(nextTotals[key]))
|
|
479
|
+
throw new TypeError(`aggregate ${key} exceeds the safe integer range`);
|
|
480
|
+
if (nextTotals[key] > maximum) budgetExceeded = true;
|
|
481
|
+
}
|
|
482
|
+
if (
|
|
483
|
+
input.decision === "accept" &&
|
|
484
|
+
outputMemoryDigest === binding.inputMemoryDigest
|
|
485
|
+
) {
|
|
486
|
+
throw new Error("an accepted round must produce a new memory digest");
|
|
487
|
+
}
|
|
488
|
+
const accepted = input.decision === "accept" && !budgetExceeded;
|
|
489
|
+
const effectiveMemoryDigest = accepted
|
|
490
|
+
? outputMemoryDigest
|
|
491
|
+
: binding.inputMemoryDigest;
|
|
492
|
+
const core = {
|
|
493
|
+
schema: PM_EXPLORATION_CHECKPOINT_SCHEMA,
|
|
494
|
+
planDigest: state.plan.planDigest,
|
|
495
|
+
suiteDigest: state.plan.suiteDigest,
|
|
496
|
+
trainingPartitionDigest: state.plan.trainingPartitionDigest,
|
|
497
|
+
environmentDigest: state.plan.environmentDigest,
|
|
498
|
+
sequence: state.checkpoints.length + 1,
|
|
499
|
+
roundId: binding.roundId,
|
|
500
|
+
stage: binding.stage,
|
|
501
|
+
branchId: binding.branchId,
|
|
502
|
+
taskId: binding.taskId,
|
|
503
|
+
inputMemoryDigest: binding.inputMemoryDigest,
|
|
504
|
+
outputMemoryDigest,
|
|
505
|
+
effectiveMemoryDigest,
|
|
506
|
+
executionReceiptDigest,
|
|
507
|
+
graderReceiptDigest,
|
|
508
|
+
decision: input.decision,
|
|
509
|
+
accepted,
|
|
510
|
+
disposition: budgetExceeded ? "rejected-budget" : input.decision,
|
|
511
|
+
metrics: consumed,
|
|
512
|
+
aggregateMetrics: nextTotals,
|
|
513
|
+
};
|
|
514
|
+
const checkpoint = deepFreeze({
|
|
515
|
+
...core,
|
|
516
|
+
checkpointDigest: hash(PM_EXPLORATION_CHECKPOINT_SCHEMA, core),
|
|
517
|
+
authenticated: false,
|
|
518
|
+
qualifiesForPromotion: false,
|
|
519
|
+
});
|
|
520
|
+
binding.consumed = true;
|
|
521
|
+
state.activeRoundIds.delete(binding.roundId);
|
|
522
|
+
state.usedRoundIds.add(binding.roundId);
|
|
523
|
+
if (binding.branchId !== null) {
|
|
524
|
+
state.activeBranches.delete(binding.branchId);
|
|
525
|
+
state.branchHeads.set(binding.branchId, effectiveMemoryDigest);
|
|
526
|
+
state.branchCheckpointDigests
|
|
527
|
+
.get(binding.branchId)
|
|
528
|
+
.push(checkpoint.checkpointDigest);
|
|
529
|
+
} else {
|
|
530
|
+
state.deepHead = effectiveMemoryDigest;
|
|
531
|
+
}
|
|
532
|
+
state.checkpoints.push(checkpoint);
|
|
533
|
+
state.totals = nextTotals;
|
|
534
|
+
state.consecutiveNoGain = accepted ? 0 : state.consecutiveNoGain + 1;
|
|
535
|
+
updateStopReason(state, budgetExceeded);
|
|
536
|
+
return checkpoint;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
export function mergePmExplorationBroadBranches(journal, input) {
|
|
540
|
+
const state = stateFor(journal);
|
|
541
|
+
exact(
|
|
542
|
+
input,
|
|
543
|
+
["mergeId", "outputMemoryDigest", "conflictResolutionReceiptDigest"],
|
|
544
|
+
"PM broad merge",
|
|
545
|
+
);
|
|
546
|
+
if (state.stage !== "broad" || state.merge)
|
|
547
|
+
throw new Error("Broad exploration cannot be merged in this state");
|
|
548
|
+
if (state.activeRoundIds.size > 0)
|
|
549
|
+
throw new Error("Active rounds must settle before the broad merge");
|
|
550
|
+
const branchCheckpointDigests = state.plan.broadBranchIds.map((branchId) => {
|
|
551
|
+
const checkpoints = state.branchCheckpointDigests.get(branchId);
|
|
552
|
+
if (checkpoints.length === 0)
|
|
553
|
+
throw new Error(`Broad branch ${branchId} has no completed checkpoint`);
|
|
554
|
+
return Object.freeze({
|
|
555
|
+
branchId,
|
|
556
|
+
checkpointDigest: checkpoints.at(-1),
|
|
557
|
+
memoryDigest: state.branchHeads.get(branchId),
|
|
558
|
+
});
|
|
559
|
+
});
|
|
560
|
+
const core = {
|
|
561
|
+
schema: PM_EXPLORATION_MERGE_SCHEMA,
|
|
562
|
+
planDigest: state.plan.planDigest,
|
|
563
|
+
mergeId: identifier(input.mergeId, "mergeId"),
|
|
564
|
+
baseMemoryDigest: state.plan.initialMemoryDigest,
|
|
565
|
+
branchCheckpoints: branchCheckpointDigests,
|
|
566
|
+
outputMemoryDigest: digest(input.outputMemoryDigest, "outputMemoryDigest"),
|
|
567
|
+
conflictResolutionReceiptDigest: digest(
|
|
568
|
+
input.conflictResolutionReceiptDigest,
|
|
569
|
+
"conflictResolutionReceiptDigest",
|
|
570
|
+
),
|
|
571
|
+
};
|
|
572
|
+
const merge = deepFreeze({
|
|
573
|
+
...core,
|
|
574
|
+
mergeDigest: hash(PM_EXPLORATION_MERGE_SCHEMA, core),
|
|
575
|
+
authenticated: false,
|
|
576
|
+
qualifiesForPromotion: false,
|
|
577
|
+
});
|
|
578
|
+
state.merge = merge;
|
|
579
|
+
state.deepHead = core.outputMemoryDigest;
|
|
580
|
+
state.stage = "broad-complete";
|
|
581
|
+
return merge;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
export function enterPmExplorationDeepStage(journal, mergeDigest) {
|
|
585
|
+
const state = stateFor(journal);
|
|
586
|
+
if (state.stage !== "broad-complete" || !state.merge)
|
|
587
|
+
throw new Error("Broad exploration must be merged before Deep exploration");
|
|
588
|
+
if (digest(mergeDigest, "mergeDigest") !== state.merge.mergeDigest)
|
|
589
|
+
throw new Error("Deep exploration merge binding mismatch");
|
|
590
|
+
if (state.stopReason)
|
|
591
|
+
throw new Error(`PM exploration stopped: ${state.stopReason}`);
|
|
592
|
+
state.stage = "deep";
|
|
593
|
+
state.consecutiveNoGain = 0;
|
|
594
|
+
return Object.freeze({
|
|
595
|
+
stage: state.stage,
|
|
596
|
+
inputMemoryDigest: state.deepHead,
|
|
597
|
+
mergeDigest: state.merge.mergeDigest,
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
export function freezePmExplorationMemory(journal, input) {
|
|
602
|
+
const state = stateFor(journal);
|
|
603
|
+
exact(
|
|
604
|
+
input,
|
|
605
|
+
["finalMemoryDigest", "evaluatorReceiptDigest"],
|
|
606
|
+
"PM memory freeze",
|
|
607
|
+
);
|
|
608
|
+
if (state.frozen) throw new Error("PM exploration memory is already frozen");
|
|
609
|
+
if (state.stage !== "deep")
|
|
610
|
+
throw new Error("Deep exploration must run before memory freeze");
|
|
611
|
+
if (state.activeRoundIds.size > 0)
|
|
612
|
+
throw new Error("Active rounds must settle before memory freeze");
|
|
613
|
+
if (!state.checkpoints.some((checkpoint) => checkpoint.stage === "deep"))
|
|
614
|
+
throw new Error("At least one Deep checkpoint is required before freeze");
|
|
615
|
+
const finalMemoryDigest = digest(
|
|
616
|
+
input.finalMemoryDigest,
|
|
617
|
+
"finalMemoryDigest",
|
|
618
|
+
);
|
|
619
|
+
if (finalMemoryDigest !== state.deepHead)
|
|
620
|
+
throw new Error("final memory does not match the current Deep checkpoint");
|
|
621
|
+
const core = {
|
|
622
|
+
schema: PM_EXPLORATION_FROZEN_MEMORY_SCHEMA,
|
|
623
|
+
planDigest: state.plan.planDigest,
|
|
624
|
+
suiteDigest: state.plan.suiteDigest,
|
|
625
|
+
trainingPartitionDigest: state.plan.trainingPartitionDigest,
|
|
626
|
+
environmentDigest: state.plan.environmentDigest,
|
|
627
|
+
mergeDigest: state.merge.mergeDigest,
|
|
628
|
+
finalCheckpointDigest: state.checkpoints.at(-1).checkpointDigest,
|
|
629
|
+
finalMemoryDigest,
|
|
630
|
+
evaluatorReceiptDigest: digest(
|
|
631
|
+
input.evaluatorReceiptDigest,
|
|
632
|
+
"evaluatorReceiptDigest",
|
|
633
|
+
),
|
|
634
|
+
totalRounds: state.checkpoints.length,
|
|
635
|
+
aggregateMetrics: state.totals,
|
|
636
|
+
stopReason: state.stopReason,
|
|
637
|
+
};
|
|
638
|
+
state.frozen = deepFreeze({
|
|
639
|
+
...core,
|
|
640
|
+
frozenMemoryDigest: hash(PM_EXPLORATION_FROZEN_MEMORY_SCHEMA, core),
|
|
641
|
+
authenticated: false,
|
|
642
|
+
qualifiesForPromotion: false,
|
|
643
|
+
});
|
|
644
|
+
state.stage = "frozen";
|
|
645
|
+
return state.frozen;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
export function exportPmExplorationRecoverySnapshot(journal) {
|
|
649
|
+
const state = stateFor(journal);
|
|
650
|
+
if (state.activeRoundIds.size > 0)
|
|
651
|
+
throw new Error(
|
|
652
|
+
"Active rounds must settle before recovery snapshot export",
|
|
653
|
+
);
|
|
654
|
+
const core = {
|
|
655
|
+
schema: PM_EXPLORATION_RECOVERY_SNAPSHOT_SCHEMA,
|
|
656
|
+
planDigest: state.plan.planDigest,
|
|
657
|
+
stage: state.stage,
|
|
658
|
+
checkpoints: [...state.checkpoints],
|
|
659
|
+
merge: state.merge,
|
|
660
|
+
frozen: state.frozen,
|
|
661
|
+
};
|
|
662
|
+
return deepFreeze({
|
|
663
|
+
...core,
|
|
664
|
+
snapshotDigest: hash(PM_EXPLORATION_RECOVERY_SNAPSHOT_SCHEMA, core),
|
|
665
|
+
authenticated: false,
|
|
666
|
+
qualifiesForPromotion: false,
|
|
667
|
+
});
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
function validatedRecoverySnapshot(value, plan) {
|
|
671
|
+
exact(
|
|
672
|
+
value,
|
|
673
|
+
[
|
|
674
|
+
"schema",
|
|
675
|
+
"planDigest",
|
|
676
|
+
"stage",
|
|
677
|
+
"checkpoints",
|
|
678
|
+
"merge",
|
|
679
|
+
"frozen",
|
|
680
|
+
"snapshotDigest",
|
|
681
|
+
"authenticated",
|
|
682
|
+
"qualifiesForPromotion",
|
|
683
|
+
],
|
|
684
|
+
"PM exploration recovery snapshot",
|
|
685
|
+
);
|
|
686
|
+
if (
|
|
687
|
+
value.schema !== PM_EXPLORATION_RECOVERY_SNAPSHOT_SCHEMA ||
|
|
688
|
+
value.authenticated !== false ||
|
|
689
|
+
value.qualifiesForPromotion !== false
|
|
690
|
+
) {
|
|
691
|
+
throw new TypeError("PM exploration recovery snapshot flags are invalid");
|
|
692
|
+
}
|
|
693
|
+
if (value.planDigest !== plan.planDigest)
|
|
694
|
+
throw new Error("PM recovery snapshot plan binding mismatch");
|
|
695
|
+
const core = copyPlainData(
|
|
696
|
+
{
|
|
697
|
+
schema: value.schema,
|
|
698
|
+
planDigest: value.planDigest,
|
|
699
|
+
stage: value.stage,
|
|
700
|
+
checkpoints: value.checkpoints,
|
|
701
|
+
merge: value.merge,
|
|
702
|
+
frozen: value.frozen,
|
|
703
|
+
},
|
|
704
|
+
"PM exploration recovery snapshot payload",
|
|
705
|
+
);
|
|
706
|
+
if (
|
|
707
|
+
!Array.isArray(core.checkpoints) ||
|
|
708
|
+
core.checkpoints.length > plan.maxRounds
|
|
709
|
+
) {
|
|
710
|
+
throw new TypeError("PM recovery checkpoint count is invalid");
|
|
711
|
+
}
|
|
712
|
+
const snapshotDigest = digest(value.snapshotDigest, "snapshotDigest");
|
|
713
|
+
if (snapshotDigest !== hash(PM_EXPLORATION_RECOVERY_SNAPSHOT_SCHEMA, core)) {
|
|
714
|
+
throw new Error("PM recovery snapshot digest mismatch");
|
|
715
|
+
}
|
|
716
|
+
return { core, snapshotDigest };
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
function replayCheckpoint(journal, expected) {
|
|
720
|
+
const round = startPmExplorationRound(journal, {
|
|
721
|
+
roundId: expected.roundId,
|
|
722
|
+
stage: expected.stage,
|
|
723
|
+
branchId: expected.branchId,
|
|
724
|
+
taskId: expected.taskId,
|
|
725
|
+
inputMemoryDigest: expected.inputMemoryDigest,
|
|
726
|
+
});
|
|
727
|
+
const actual = completePmExplorationRound(journal, round, {
|
|
728
|
+
executionReceiptDigest: expected.executionReceiptDigest,
|
|
729
|
+
graderReceiptDigest: expected.graderReceiptDigest,
|
|
730
|
+
outputMemoryDigest: expected.outputMemoryDigest,
|
|
731
|
+
decision: expected.decision,
|
|
732
|
+
metrics: expected.metrics,
|
|
733
|
+
});
|
|
734
|
+
if (canonical(actual) !== canonical(expected))
|
|
735
|
+
throw new Error("PM recovery checkpoint replay mismatch");
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
/**
|
|
739
|
+
* Restores only internally consistent, quiescent state. The snapshot remains
|
|
740
|
+
* unauthenticated and cannot authorize execution or promotion.
|
|
741
|
+
*/
|
|
742
|
+
export function restorePmExplorationJournal(plan, snapshot) {
|
|
743
|
+
const verifiedPlan = validatedPmExplorationPlan(plan);
|
|
744
|
+
const { core, snapshotDigest } = validatedRecoverySnapshot(
|
|
745
|
+
snapshot,
|
|
746
|
+
verifiedPlan,
|
|
747
|
+
);
|
|
748
|
+
if (!["broad", "broad-complete", "deep", "frozen"].includes(core.stage)) {
|
|
749
|
+
throw new TypeError("PM recovery snapshot stage is invalid");
|
|
750
|
+
}
|
|
751
|
+
let deepReached = false;
|
|
752
|
+
for (const checkpoint of core.checkpoints) {
|
|
753
|
+
if (checkpoint.stage === "deep") deepReached = true;
|
|
754
|
+
else if (checkpoint.stage !== "broad" || deepReached)
|
|
755
|
+
throw new Error("PM recovery checkpoint stage order is invalid");
|
|
756
|
+
}
|
|
757
|
+
if (
|
|
758
|
+
(core.stage === "broad" &&
|
|
759
|
+
(core.merge !== null || core.frozen !== null || deepReached)) ||
|
|
760
|
+
(core.stage === "broad-complete" &&
|
|
761
|
+
(core.merge === null || core.frozen !== null || deepReached)) ||
|
|
762
|
+
(core.stage === "deep" && (core.merge === null || core.frozen !== null)) ||
|
|
763
|
+
(core.stage === "frozen" &&
|
|
764
|
+
(core.merge === null || core.frozen === null || !deepReached))
|
|
765
|
+
) {
|
|
766
|
+
throw new Error("PM recovery snapshot stage payload is inconsistent");
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
const journal = createPmExplorationJournal(verifiedPlan);
|
|
770
|
+
for (const checkpoint of core.checkpoints.filter(
|
|
771
|
+
(entry) => entry.stage === "broad",
|
|
772
|
+
)) {
|
|
773
|
+
replayCheckpoint(journal, checkpoint);
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
if (core.merge !== null) {
|
|
777
|
+
const actualMerge = mergePmExplorationBroadBranches(journal, {
|
|
778
|
+
mergeId: core.merge.mergeId,
|
|
779
|
+
outputMemoryDigest: core.merge.outputMemoryDigest,
|
|
780
|
+
conflictResolutionReceiptDigest:
|
|
781
|
+
core.merge.conflictResolutionReceiptDigest,
|
|
782
|
+
});
|
|
783
|
+
if (canonical(actualMerge) !== canonical(core.merge))
|
|
784
|
+
throw new Error("PM recovery merge replay mismatch");
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
if (core.stage === "deep" || core.stage === "frozen") {
|
|
788
|
+
enterPmExplorationDeepStage(journal, core.merge.mergeDigest);
|
|
789
|
+
for (const checkpoint of core.checkpoints.filter(
|
|
790
|
+
(entry) => entry.stage === "deep",
|
|
791
|
+
)) {
|
|
792
|
+
replayCheckpoint(journal, checkpoint);
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
if (core.stage === "frozen") {
|
|
797
|
+
const actualFrozen = freezePmExplorationMemory(journal, {
|
|
798
|
+
finalMemoryDigest: core.frozen.finalMemoryDigest,
|
|
799
|
+
evaluatorReceiptDigest: core.frozen.evaluatorReceiptDigest,
|
|
800
|
+
});
|
|
801
|
+
if (canonical(actualFrozen) !== canonical(core.frozen))
|
|
802
|
+
throw new Error("PM recovery frozen memory replay mismatch");
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
if (
|
|
806
|
+
exportPmExplorationRecoverySnapshot(journal).snapshotDigest !==
|
|
807
|
+
snapshotDigest
|
|
808
|
+
) {
|
|
809
|
+
throw new Error("PM recovery snapshot replay did not reproduce its digest");
|
|
810
|
+
}
|
|
811
|
+
return journal;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
export function inspectPmExplorationJournal(journal) {
|
|
815
|
+
const state = stateFor(journal);
|
|
816
|
+
return deepFreeze({
|
|
817
|
+
schema: "chainlesschain.pm-exploration-journal-projection/v1",
|
|
818
|
+
planDigest: state.plan.planDigest,
|
|
819
|
+
stage: state.stage,
|
|
820
|
+
branchHeads: state.plan.broadBranchIds.map((branchId) => ({
|
|
821
|
+
branchId,
|
|
822
|
+
memoryDigest: state.branchHeads.get(branchId),
|
|
823
|
+
checkpointCount: state.branchCheckpointDigests.get(branchId).length,
|
|
824
|
+
checkpointDigest:
|
|
825
|
+
state.branchCheckpointDigests.get(branchId).at(-1) ?? null,
|
|
826
|
+
})),
|
|
827
|
+
deepHead: state.deepHead,
|
|
828
|
+
checkpointDigests: state.checkpoints.map(
|
|
829
|
+
(checkpoint) => checkpoint.checkpointDigest,
|
|
830
|
+
),
|
|
831
|
+
mergeDigest: state.merge?.mergeDigest ?? null,
|
|
832
|
+
mergeReceiptDigest: state.merge?.conflictResolutionReceiptDigest ?? null,
|
|
833
|
+
finalCheckpointDigest: state.checkpoints.at(-1)?.checkpointDigest ?? null,
|
|
834
|
+
frozenMemoryDigest: state.frozen?.frozenMemoryDigest ?? null,
|
|
835
|
+
aggregateMetrics: state.totals,
|
|
836
|
+
consecutiveNoGain: state.consecutiveNoGain,
|
|
837
|
+
stopReason: state.stopReason,
|
|
838
|
+
activeRoundCount: state.activeRoundIds.size,
|
|
839
|
+
authenticated: false,
|
|
840
|
+
qualifiesForPromotion: false,
|
|
841
|
+
});
|
|
842
|
+
}
|