chainlesschain 0.166.14 → 0.166.16
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/package.json +1 -1
- package/src/assets/web-panel/assets/{AIOps-C8SyFhOo.js → AIOps-CTxa2Wbq.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-Dup5gFK0.js → ActionButton-DHrQyYhy.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-8FyOWA7V.js → Analytics-DQiUqcNl.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-DBzdU9dg.js → AppLayout-_Iz4r6wt.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-C_0Z8gNy.js → Artifacts-BNs63YHo.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-Cf2a-58m.js → Audit-BgHIqRLn.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-M7w-2zgB.js → BackgroundAgents-DUQ5axBB.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-CCTHQXoL.js → Backup-CJ51E9Vw.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-DjXpeFwJ.js → BaseInput-o-5toA_R.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-cAvchsJU.js → Chat-Cu7a-x1L.js} +6 -6
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-DvEvxlZv.js → ChatBubbleRenderer-BUyYkAXr.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-DKs6qvOn.js → Checkbox-DwdmZ71L.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-DlkA7b9X.js → Codegen-BrH2eYiP.js} +1 -1
- package/src/assets/web-panel/assets/{Col-CkfdKhyJ.js → Col-BVJQEP2j.js} +1 -1
- package/src/assets/web-panel/assets/{Community-iNXMHzj6.js → Community-FGO3vZPq.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-DQxPEd09.js → Compact-DYGAFVnT.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-CSKvMCjs.js → Compliance-CRvc8Bdt.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-CkWqazpD.js → Cowork-D6Ah2m7A.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-k6W8IzhB.js → Cron-AXbU2ifR.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-C8MS1OuY.js → Crosschain-C5DXgEFd.js} +1 -1
- package/src/assets/web-panel/assets/{DID-DJuCcgiQ.js → DID-Bxh9QV9v.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-LeThKzp3.js → Dashboard-B-ZHEta-.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-BvxwgpE4.js → Dropdown-66Ik1jqa.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-n3e_a1M3.js → EmailListRenderer-CCWk1BIM.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-509W6W6v.js → FamilyGuardDashboard--9faIULK.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-xgZReQm0.js → Federation-CejW9CgX.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-jZOhKcQx.js → FormItemContext-acHgcXki.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-Jo0ULzHv.js → GenericCardRenderer-D2ghd_K4.js} +1 -1
- package/src/assets/web-panel/assets/{Git-BsRcyWQP.js → Git-BOe1YJrV.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-JlpMYTAk.js → Governance-BjgzJsof.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-CUEz872-.js → Inference-CXCD5rnP.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DgrvvEDU.js → KnowledgeGraph-CHA8QgJT.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-Df-KY4U3.js → Logs-DORPMcL6.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-CPPHz01z.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-kPZDTOeI.js → Marketplace-BCFlo1Yx.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-CRRzPqNX.js → McpTools-Dl_uZAKh.js} +2 -2
- package/src/assets/web-panel/assets/{Memory-D6pNSkg9.js → Memory-DupHamwz.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-g-pvtwRC.js → MobileBridge-BRSMPJxW.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-Bzayfrfe.js → MobileProjects-BHpuzsDl.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-B7S3W-XL.js → Mtc-Ch0jOQfZ.js} +2 -2
- package/src/assets/web-panel/assets/{MtcAudit-B8OMEWVb.js → MtcAudit-qzwQrDi7.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-CowRp7GH.js → Multisig-B5wHzUcm.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-BkQNyi4c.js → NLProgramming-Hy9uAiA0.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-Cm7gtkKz.js → Notes-DkuchMC7.js} +4 -4
- package/src/assets/web-panel/assets/{NotificationSettings-CbPTVh9m.js → NotificationSettings-CYZhD7Pi.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-Dswi26mJ.js → OrderTableRenderer-DKOoqajt.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-BkjQ9yy7.js → Organization-D9AXn6j8.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-DmRFXP7Y.js → Overflow-Bg15J6hX.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-DQE1toAu.js → P2P--B54HoQn.js} +2 -2
- package/src/assets/web-panel/assets/PdhVaultBrowser-Cz9Kk2Nb.js +7 -0
- package/src/assets/web-panel/assets/{Permissions-xSjfOaiU.js → Permissions-DHx38eS1.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-B9ICLqVP.js → PersonalDataHub-Dw8i2hAs.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-CUNwyN9E.js → Pipeline-B2o6Ur2G.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-SOoCricX.js → Privacy-8g-h2fnu.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-CSaFul0F.js → ProjectInit-DuVTcINx.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-CueiB_oe.js → ProjectSettings-C8njvdZX.js} +2 -2
- package/src/assets/web-panel/assets/Projects-Ca9BAfu7.js +1 -0
- package/src/assets/web-panel/assets/{Providers-CTqf_31h.js → Providers-wFqgod_W.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-C8ZP31E9.js → QrScannerModal-9NCXrpmA.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-CPgn4713.js → QuickAsk-WUTNvFkY.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-8_e0zw0i.js → Recommend-xT4nYmju.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-iT7lbGqw.js → RemoteSession-fak1PBGC.js} +3 -3
- package/src/assets/web-panel/assets/{Reputation-B4q9SUOz.js → Reputation-D1AkQvD-.js} +1 -1
- package/src/assets/web-panel/assets/{Row-CA_nASyL.js → Row-DVfm6xGt.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-Cwb0E3yv.js → RssFeed-BDunoLSE.js} +3 -3
- package/src/assets/web-panel/assets/{Search-D9iVsOxJ.js → Search-OPycRoU3.js} +1 -1
- package/src/assets/web-panel/assets/{Security-DPLbYchG.js → Security-C8-Fm3Dt.js} +3 -3
- package/src/assets/web-panel/assets/{Services-DGMVL_LF.js → Services-qM_sMpMl.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-B_6kym0D.js → Skeleton-D3HBJjVF.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-Ds6r0dXB.js → Skills-jr67X-tP.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-BLHAbBhb.js → Sla-CS3ggyCg.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-CZuddd14.js → SpeechSettings-CTOCYxOi.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-CdBq7oSl.js → SyncSettings-D3s3cLT5.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-DfT60Nyl.js → Tasks-CdfoJ25m.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-RLfsKy2V.js → Templates-Drb4AI8X.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-Cw8xCNLE.js → Tenant-DzwR3Qkz.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-BOUkNu4f.js → Terminal-D-ESq7xp.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-CgGZWaRm.js → TimelineRenderer-BK-AUuws.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-CpwjVyir.js → Tokens-xGTouuNp.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-DL21H-eb.js → Trigger-BvZj-v2H.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-B3ffFclW.js → Trust-BLC9CGmc.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-CkBeb5aI.js → UkeySign-BSuCXpmE.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-U2iVaE3r.js → VideoEditing-DT3A-_Ql.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-CQk2wzYd.js → Wallet-c1OXao85.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-DYIuIYzT.js → WebAuthn-CuA-F7KJ.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-tg1c2mZQ.js → WorkflowEditor-MANZrf82.js} +1 -1
- package/src/assets/web-panel/assets/{chat-CtL61spq.js → chat-D4Y4Ey0O.js} +1 -1
- package/src/assets/web-panel/assets/{colors-DKpbeo8s.js → colors-DDH06hKO.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-gPZsrswt.js → compact-item-Cym756Xj.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-CfHlwzVr.js → createContext-By4Nb1k-.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-KO3pAGeO.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-MV47g_6X.js → hasIn-Ch7OY258.js} +1 -1
- package/src/assets/web-panel/assets/{index-DMMvlLp9.js → index-6-2eYBXr.js} +1 -1
- package/src/assets/web-panel/assets/{index-DsH-i_3d.js → index-6CpOV3Pn.js} +1 -1
- package/src/assets/web-panel/assets/{index-CB1HnbNK.js → index-ArMrnx0v.js} +1 -1
- package/src/assets/web-panel/assets/{index-DU4rZpiK.js → index-B51otVoZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-CRv_uMLV.js → index-BBBUn5v7.js} +1 -1
- package/src/assets/web-panel/assets/{index-srBc9U_s.js → index-BFydNU6a.js} +1 -1
- package/src/assets/web-panel/assets/{index-DnZ_zS1M.js → index-BIrRX-ra.js} +1 -1
- package/src/assets/web-panel/assets/{index-DGtEBodr.js → index-BZB9q-Lp.js} +1 -1
- package/src/assets/web-panel/assets/index-Bh4Rgkg2.js +1 -0
- package/src/assets/web-panel/assets/{index-BjlPD7GO.js → index-Bo20BfdS.js} +1 -1
- package/src/assets/web-panel/assets/{index-C_B9LiKs.js → index-CF3Lhyns.js} +1 -1
- package/src/assets/web-panel/assets/{index-Rb7CRMw9.js → index-CGsvEbbq.js} +1 -1
- package/src/assets/web-panel/assets/{index-DIeJmlAA.js → index-CJPks33Y.js} +1 -1
- package/src/assets/web-panel/assets/{index-xUlgG0Xe.js → index-CT-3FYwd.js} +1 -1
- package/src/assets/web-panel/assets/{index-6G0ETFTW.js → index-CUSUzaB8.js} +1 -1
- package/src/assets/web-panel/assets/{index-CzfxTSPW.js → index-CV1UC4zo.js} +1 -1
- package/src/assets/web-panel/assets/{index-CmPJpLoD.js → index-CV8qXTvb.js} +1 -1
- package/src/assets/web-panel/assets/{index-fYcS7NS0.js → index-CZn6dmZe.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cjv0kKCU.js → index-CjNUTBwe.js} +1 -1
- package/src/assets/web-panel/assets/{index-Df9sWKpl.js → index-CpudmDhf.js} +1 -1
- package/src/assets/web-panel/assets/{index-C9RF4ieu.js → index-CykROEJA.js} +1 -1
- package/src/assets/web-panel/assets/{index-Zvkt9xqi.js → index-D5sADPGY.js} +1 -1
- package/src/assets/web-panel/assets/{index-yxxXlqBA.js → index-D9le0-dc.js} +1 -1
- package/src/assets/web-panel/assets/{index-BBw52sEl.js → index-DAkk1M7x.js} +1 -1
- package/src/assets/web-panel/assets/{index-ExAGPAnX.js → index-DNEJzc0r.js} +1 -1
- package/src/assets/web-panel/assets/{index-BQp5M0s1.js → index-DNlxihs8.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ddcpj1El.js → index-DiewptTL.js} +1 -1
- package/src/assets/web-panel/assets/{index-BOyGUuBE.js → index-DjwgJ5Jd.js} +1 -1
- package/src/assets/web-panel/assets/index-Dox_coTY.js +1 -0
- package/src/assets/web-panel/assets/{index-CmM9jqwe.js → index-DtGValNi.js} +1 -1
- package/src/assets/web-panel/assets/{index-DfLyHCWi.js → index-DvnywwUj.js} +1 -1
- package/src/assets/web-panel/assets/{index-BN-wxSQ8.js → index-OU8DZ9BL.js} +1 -1
- package/src/assets/web-panel/assets/{index-D1XB5-t5.js → index-XGeQMvWK.js} +1 -1
- package/src/assets/web-panel/assets/{index-C4lF-f_z.js → index-ZY2Qs_Sn.js} +1 -1
- package/src/assets/web-panel/assets/{index-C9gtY4nG.js → index-jx7kr6mB.js} +3 -3
- package/src/assets/web-panel/assets/{index-C6nC_DyY.js → index-mL3nOcH6.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dd19vRwy.js → index-oBsfUbyH.js} +1 -1
- package/src/assets/web-panel/assets/{index-bSFrAszw.js → index-q-s04OKn.js} +1 -1
- package/src/assets/web-panel/assets/{index-D_Pl1GnW.js → index-tZ5BUdad.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-BeOZoSZ4.js → initDefaultProps-DGcX9iPP.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BXE8VMKx.js → motion-2SeL1D8A.js} +1 -1
- package/src/assets/web-panel/assets/{move-I9sdvPGX.js → move-3cYbQCpS.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-d25zrJd0.js → mtc-parser-IDdxfw0z.js} +1 -1
- package/src/assets/web-panel/assets/{omit-D71V_T4e.js → omit-BNswdKO6.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-BNZKpIAR.js → pickAttrs-BwCnem2I.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-jOcgxTTz.js → placementArrow-B9PtZ5rq.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-pta_O8AU.js → responsiveObserve-CPB6iSzV.js} +1 -1
- package/src/assets/web-panel/assets/{slide-Dlq6om7z.js → slide-DZgyblZa.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-BnkO_yRv.js → statusUtils-D82IBdsx.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-p8NfWmcG.js → styleChecker-qSHtosM-.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-KcjZZ5U7.js → useFlexGapSupport-CmWO7h0I.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-gqonDMWB.js → useFs-yLrs_ub4.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-jIIfE4go.js → usePersonalDataHub-CJ1VW6rh.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-Dyo7fBrM.js → vnode-CzY_moQ4.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-6l8BMg9W.js → zoom-DAa7rEY5.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/agent.js +62 -11
- package/src/commands/learning.js +71 -1
- package/src/commands/team.js +100 -3
- package/src/data/changelog.json +29 -3
- package/src/harness/worktree-isolator.js +74 -9
- package/src/lib/agent-team/task-lease.js +12 -1
- package/src/lib/agent-team/team-graph-runtime-adapter.js +6 -0
- package/src/lib/agent-team/team-runner.js +4 -0
- package/src/lib/agent-team/team-worktree.js +4 -0
- package/src/lib/capability-manifest.js +212 -1
- package/src/lib/evolution/evolution-artifact-ports.js +3277 -0
- package/src/lib/evolution/evolution-eval-gate.js +6442 -0
- package/src/lib/evolution/evolution-evidence-projector.js +3786 -0
- package/src/lib/evolution/evolution-ledger-ports.js +2689 -0
- package/src/lib/evolution/evolution-ledger.js +4280 -0
- package/src/lib/evolution/skill-candidate-registry.js +2258 -0
- package/src/lib/evolution/skill-execution-manifest.js +942 -0
- package/src/lib/evolution/skill-mutation-authority.js +1677 -0
- package/src/lib/evolution/skill-promotion-controller.js +506 -0
- package/src/lib/evolution/skill-release-registry.js +3277 -0
- package/src/lib/evolution/skill-target-matrix-eval.js +3233 -0
- package/src/lib/evolution/skill-writer-inventory-manifest.js +998 -0
- package/src/lib/evolution/skill-writer-inventory-validator.js +816 -0
- package/src/lib/formal-quality-eval-runtime.js +192 -0
- package/src/lib/graph-kernel/cutover-contract.js +7 -0
- package/src/lib/graph-kernel/cutover-ledger.js +8 -7
- package/src/lib/graph-kernel/evidence-digest.js +47 -0
- package/src/lib/graph-kernel/graph-runtime-surfaces.json +8 -1
- package/src/lib/graph-kernel/production-cutover-evidence.js +342 -15
- package/src/lib/graph-kernel/production-source-evidence.js +1599 -0
- package/src/lib/graph-kernel/retirement-evidence.js +15 -5
- package/src/lib/graph-kernel/runtime-surface-manifest.js +67 -2
- package/src/lib/headless-manifest.js +3 -1
- package/src/lib/learning/skill-improver.js +311 -43
- package/src/lib/learning/skill-synthesizer.js +236 -16
- package/src/lib/process-execution-broker/index.js +32 -0
- package/src/lib/process-execution-broker/platform-sandbox.js +5 -4
- package/src/lib/secure-fs.js +45 -2
- package/src/runtime/agent-core.js +17 -5
- package/src/runtime/headless-runner.js +16 -9
- package/src/assets/web-panel/assets/MarkdownRenderer-BEPDcSwz.js +0 -1
- package/src/assets/web-panel/assets/PdhVaultBrowser-BujyoDRG.js +0 -7
- package/src/assets/web-panel/assets/Projects-BOeXvU7y.js +0 -1
- package/src/assets/web-panel/assets/devWarning-DdqnMhzi.js +0 -1
- package/src/assets/web-panel/assets/index-BcK8o4ke.js +0 -1
- package/src/assets/web-panel/assets/index-DvbAjyYI.js +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { graphDigest } from "./
|
|
1
|
+
import { graphEvidenceDigest as graphDigest } from "./evidence-digest.js";
|
|
2
2
|
|
|
3
3
|
export const GRAPH_RETIREMENT_EVIDENCE_SCHEMA =
|
|
4
4
|
"chainlesschain.graph-retirement-qualification/v1";
|
|
@@ -215,7 +215,7 @@ function evidenceIdentity(
|
|
|
215
215
|
return normalized;
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
-
function observationWindow(input, { notBefore } = {}) {
|
|
218
|
+
function observationWindow(input, { notBefore, minimumDurationMs = 1 } = {}) {
|
|
219
219
|
const started = timestamp(input.startedAt, "startedAt");
|
|
220
220
|
const ended = timestamp(input.endedAt, "endedAt");
|
|
221
221
|
if (ended.milliseconds <= started.milliseconds) {
|
|
@@ -231,10 +231,18 @@ function observationWindow(input, { notBefore } = {}) {
|
|
|
231
231
|
{ notBefore, startedAt: started.value },
|
|
232
232
|
);
|
|
233
233
|
}
|
|
234
|
+
const durationMs = ended.milliseconds - started.milliseconds;
|
|
235
|
+
if (durationMs < minimumDurationMs || input?.durationMs !== durationMs) {
|
|
236
|
+
throw evidenceError(
|
|
237
|
+
"CC_GRAPH_RETIREMENT_OBSERVATION_TOO_SHORT",
|
|
238
|
+
`durationMs must equal the timestamp window and be at least ${minimumDurationMs}`,
|
|
239
|
+
{ durationMs, minimumDurationMs },
|
|
240
|
+
);
|
|
241
|
+
}
|
|
234
242
|
return {
|
|
235
243
|
startedAt: started.value,
|
|
236
244
|
endedAt: ended.value,
|
|
237
|
-
durationMs
|
|
245
|
+
durationMs,
|
|
238
246
|
observationSampleCount: positive(
|
|
239
247
|
input.observationSampleCount,
|
|
240
248
|
"observationSampleCount",
|
|
@@ -454,6 +462,7 @@ export function normalizeGraphRetirementEvidence(
|
|
|
454
462
|
commitSha,
|
|
455
463
|
contract: contractInput,
|
|
456
464
|
requiredPlatforms,
|
|
465
|
+
minimumDurationMs = 1,
|
|
457
466
|
},
|
|
458
467
|
) {
|
|
459
468
|
const contract = normalizeGraphRetirementContract(contractInput);
|
|
@@ -470,7 +479,7 @@ export function normalizeGraphRetirementEvidence(
|
|
|
470
479
|
commitSha,
|
|
471
480
|
contract,
|
|
472
481
|
}),
|
|
473
|
-
...observationWindow(input),
|
|
482
|
+
...observationWindow(input, { minimumDurationMs }),
|
|
474
483
|
activeLegacyRunCount: zero(
|
|
475
484
|
input?.activeLegacyRunCount,
|
|
476
485
|
"activeLegacyRunCount",
|
|
@@ -508,6 +517,7 @@ export function normalizeGraphLegacyWriterObservation(
|
|
|
508
517
|
commitSha,
|
|
509
518
|
contract: contractInput,
|
|
510
519
|
notBefore,
|
|
520
|
+
minimumDurationMs = 1,
|
|
511
521
|
},
|
|
512
522
|
) {
|
|
513
523
|
const contract = normalizeGraphRetirementContract(contractInput);
|
|
@@ -554,7 +564,7 @@ export function normalizeGraphLegacyWriterObservation(
|
|
|
554
564
|
commitSha,
|
|
555
565
|
contract,
|
|
556
566
|
}),
|
|
557
|
-
...observationWindow(input, { notBefore }),
|
|
567
|
+
...observationWindow(input, { notBefore, minimumDurationMs }),
|
|
558
568
|
activeLegacyRunCount: zero(
|
|
559
569
|
input?.activeLegacyRunCount,
|
|
560
570
|
"activeLegacyRunCount",
|
|
@@ -9,8 +9,8 @@ import {
|
|
|
9
9
|
import {
|
|
10
10
|
GRAPH_CUTOVER_LEDGER_SCHEMA,
|
|
11
11
|
GRAPH_CUTOVER_REQUIRED_PLATFORMS,
|
|
12
|
-
} from "./cutover-
|
|
13
|
-
import { graphDigest } from "./
|
|
12
|
+
} from "./cutover-contract.js";
|
|
13
|
+
import { graphEvidenceDigest as graphDigest } from "./evidence-digest.js";
|
|
14
14
|
|
|
15
15
|
export const GRAPH_RUNTIME_SURFACE_MANIFEST_SCHEMA =
|
|
16
16
|
"chainlesschain.graph-runtime-surfaces/v1";
|
|
@@ -111,6 +111,71 @@ export function graphRuntimeEntryManifestDigest(
|
|
|
111
111
|
);
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
export function graphRuntimeSurfaceManifestDigest(manifest) {
|
|
115
|
+
return graphDigest(manifest, "cc.graph.runtime-surface-manifest/v1");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function assertGraphProductionRuntimeSurfaceManifest(manifest) {
|
|
119
|
+
const surfaces = Array.isArray(manifest?.surfaces) ? manifest.surfaces : [];
|
|
120
|
+
const entries = surfaces.flatMap((surface) => surface.entries || []);
|
|
121
|
+
const counts = {
|
|
122
|
+
migrate: entries.filter((entry) => entry.cutoverStrategy === "migrate")
|
|
123
|
+
.length,
|
|
124
|
+
retire: entries.filter((entry) => entry.cutoverStrategy === "retire")
|
|
125
|
+
.length,
|
|
126
|
+
disabled: entries.filter((entry) => entry.cutoverStrategy === "disabled")
|
|
127
|
+
.length,
|
|
128
|
+
};
|
|
129
|
+
const surfaceNames = surfaces.map((surface) => surface.originSurface).sort();
|
|
130
|
+
const expectedSurfaceNames = [
|
|
131
|
+
"browser",
|
|
132
|
+
"cli_team",
|
|
133
|
+
"cowork",
|
|
134
|
+
"desktop",
|
|
135
|
+
"scheduler",
|
|
136
|
+
];
|
|
137
|
+
const expectedLegacyMutationErrorCodes = {
|
|
138
|
+
browser: "CC_CLI_LEGACY_RUNTIME_READ_ONLY",
|
|
139
|
+
cli_team: "CC_CLI_LEGACY_RUNTIME_READ_ONLY",
|
|
140
|
+
cowork: "CC_CLI_LEGACY_RUNTIME_READ_ONLY",
|
|
141
|
+
desktop: "CC_DESKTOP_LEGACY_RUNTIME_READ_ONLY",
|
|
142
|
+
scheduler: "CC_CLI_LEGACY_RUNTIME_READ_ONLY",
|
|
143
|
+
};
|
|
144
|
+
if (
|
|
145
|
+
surfaces.length !== 5 ||
|
|
146
|
+
JSON.stringify(surfaceNames) !== JSON.stringify(expectedSurfaceNames) ||
|
|
147
|
+
entries.length !== 23 ||
|
|
148
|
+
counts.migrate !== 7 ||
|
|
149
|
+
counts.retire !== 13 ||
|
|
150
|
+
counts.disabled !== 3 ||
|
|
151
|
+
JSON.stringify(manifest?.cutoverPolicy?.legacyMutationErrorCodes) !==
|
|
152
|
+
JSON.stringify(expectedLegacyMutationErrorCodes)
|
|
153
|
+
) {
|
|
154
|
+
const error = new Error(
|
|
155
|
+
"production Graph runtime manifest must freeze exactly 5 surfaces, 23 entries, 20 durable, 7 migrate, 13 retire, and 3 disabled",
|
|
156
|
+
);
|
|
157
|
+
error.name = "GraphRuntimeSurfaceManifestError";
|
|
158
|
+
error.code = "CC_GRAPH_PRODUCTION_MANIFEST_CONTRACT_MISMATCH";
|
|
159
|
+
error.actual = {
|
|
160
|
+
surfaceCount: surfaces.length,
|
|
161
|
+
surfaceNames,
|
|
162
|
+
entryCount: entries.length,
|
|
163
|
+
durableEntryCount: counts.migrate + counts.retire,
|
|
164
|
+
...counts,
|
|
165
|
+
};
|
|
166
|
+
throw error;
|
|
167
|
+
}
|
|
168
|
+
return Object.freeze({
|
|
169
|
+
surfaceCount: 5,
|
|
170
|
+
entryCount: 23,
|
|
171
|
+
durableEntryCount: 20,
|
|
172
|
+
migratableEntryCount: 7,
|
|
173
|
+
retirementEntryCount: 13,
|
|
174
|
+
disabledEntryCount: 3,
|
|
175
|
+
manifestDigest: graphRuntimeSurfaceManifestDigest(manifest),
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
|
|
114
179
|
function manifestFiles(manifest) {
|
|
115
180
|
return new Set([
|
|
116
181
|
...manifest.surfaces.flatMap((surface) =>
|
|
@@ -19,6 +19,7 @@ import { createRequire } from "node:module";
|
|
|
19
19
|
import { PROTOCOL_MIN_VERSION } from "./capability-negotiation.js";
|
|
20
20
|
import {
|
|
21
21
|
CAPABILITY_MANIFEST,
|
|
22
|
+
buildEvolutionStatus,
|
|
22
23
|
toAgentFeatureFlags,
|
|
23
24
|
} from "./capability-manifest.js";
|
|
24
25
|
|
|
@@ -129,7 +130,7 @@ export function buildLoadedSources({
|
|
|
129
130
|
* (protocol version, tool names, permission modes, IO formats, exit codes,
|
|
130
131
|
* feature flags) without parsing --help text.
|
|
131
132
|
*/
|
|
132
|
-
export function buildAgentCapabilities() {
|
|
133
|
+
export function buildAgentCapabilities({ evolutionEvidence = {} } = {}) {
|
|
133
134
|
let version = null;
|
|
134
135
|
try {
|
|
135
136
|
version = _require("../../package.json").version || null;
|
|
@@ -158,5 +159,6 @@ export function buildAgentCapabilities() {
|
|
|
158
159
|
input_formats: [...CAPABILITY_MANIFEST.inputFormats],
|
|
159
160
|
exit_codes: HEADLESS_EXIT_CODES,
|
|
160
161
|
features: toAgentFeatureFlags(),
|
|
162
|
+
evolution_status: buildEvolutionStatus({ evidence: evolutionEvidence }),
|
|
161
163
|
};
|
|
162
164
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* SkillImprover —
|
|
3
|
-
* based on execution feedback and better trajectories.
|
|
2
|
+
* SkillImprover — Proposes candidate revisions for auto-synthesized SKILL.md
|
|
3
|
+
* files based on execution feedback and better trajectories.
|
|
4
4
|
*
|
|
5
5
|
* Three improvement triggers:
|
|
6
6
|
* 1. repairFromError — skill execution failed, patch the procedure
|
|
@@ -69,6 +69,41 @@ export function rebuildSkillMd(meta, body) {
|
|
|
69
69
|
return `---\n${lines.join("\n")}\n---\n${body}`;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
/**
|
|
73
|
+
* A skill name is used as a path segment in both the source and candidate
|
|
74
|
+
* trees. Reject traversal and ambiguous path-like names before any I/O.
|
|
75
|
+
* @param {string} skillName
|
|
76
|
+
* @returns {boolean}
|
|
77
|
+
*/
|
|
78
|
+
export function isSafeSkillName(skillName) {
|
|
79
|
+
return (
|
|
80
|
+
typeof skillName === "string" &&
|
|
81
|
+
/^[\p{L}\p{N}][\p{L}\p{N}._-]{0,127}$/u.test(skillName) &&
|
|
82
|
+
skillName !== "." &&
|
|
83
|
+
skillName !== ".." &&
|
|
84
|
+
!skillName.includes("..")
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function rootContains(parentRoot, childRoot) {
|
|
89
|
+
if (!parentRoot || !childRoot) return false;
|
|
90
|
+
const parent = path.resolve(parentRoot);
|
|
91
|
+
const child = path.resolve(childRoot);
|
|
92
|
+
const relative = path.relative(parent, child);
|
|
93
|
+
return (
|
|
94
|
+
relative === "" ||
|
|
95
|
+
(relative !== ".." &&
|
|
96
|
+
!relative.startsWith(`..${path.sep}`) &&
|
|
97
|
+
!path.isAbsolute(relative))
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function rootsOverlap(firstRoot, secondRoot) {
|
|
102
|
+
return (
|
|
103
|
+
rootContains(firstRoot, secondRoot) || rootContains(secondRoot, firstRoot)
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
72
107
|
// ── LLM prompt builders ────────────────────────────────
|
|
73
108
|
|
|
74
109
|
/**
|
|
@@ -188,16 +223,68 @@ export class SkillImprover {
|
|
|
188
223
|
* @param {import("better-sqlite3").Database} db
|
|
189
224
|
* @param {function} llmChat — async (messages) => string
|
|
190
225
|
* @param {import("./trajectory-store.js").TrajectoryStore} trajectoryStore
|
|
191
|
-
* @param {{skillsDir?:string, limits?:object}} [config]
|
|
226
|
+
* @param {{skillsDir?:string, candidateOutputDir?:string, diffOnly?:boolean, limits?:object}} [config]
|
|
192
227
|
*/
|
|
193
228
|
constructor(db, llmChat, trajectoryStore, config = {}) {
|
|
194
229
|
this.db = db;
|
|
195
230
|
this.llmChat = llmChat;
|
|
196
231
|
this.trajectoryStore = trajectoryStore;
|
|
197
232
|
this.skillsDir = config.skillsDir || null;
|
|
233
|
+
this.candidateOutputDir = config.candidateOutputDir || null;
|
|
234
|
+
this.diffOnly = config.diffOnly === true;
|
|
198
235
|
this.skillLimits = resolveSkillLimits(config.limits);
|
|
199
236
|
}
|
|
200
237
|
|
|
238
|
+
/**
|
|
239
|
+
* Return the fail-closed mutation boundary for this instance. `skillsDir` is
|
|
240
|
+
* always read-only; proposals must use an isolated candidate root or
|
|
241
|
+
* explicitly opt into diff-only output.
|
|
242
|
+
* @returns {{available:boolean, reason?:string}}
|
|
243
|
+
*/
|
|
244
|
+
getAvailability() {
|
|
245
|
+
if (!this.skillsDir) {
|
|
246
|
+
return { available: false, reason: "source skill registry unavailable" };
|
|
247
|
+
}
|
|
248
|
+
if (typeof this.llmChat !== "function") {
|
|
249
|
+
return { available: false, reason: "LLM unavailable" };
|
|
250
|
+
}
|
|
251
|
+
if (this.diffOnly && this.candidateOutputDir) {
|
|
252
|
+
return {
|
|
253
|
+
available: false,
|
|
254
|
+
reason: "configure either candidateOutputDir or diffOnly, not both",
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
if (!this.diffOnly && !this.candidateOutputDir) {
|
|
258
|
+
return {
|
|
259
|
+
available: false,
|
|
260
|
+
reason:
|
|
261
|
+
"candidate output unavailable; configure candidateOutputDir or diffOnly",
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
if (
|
|
265
|
+
this.candidateOutputDir &&
|
|
266
|
+
rootsOverlap(this.skillsDir, this.candidateOutputDir)
|
|
267
|
+
) {
|
|
268
|
+
return {
|
|
269
|
+
available: false,
|
|
270
|
+
reason: "candidate output must be isolated from the active skill tree",
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
return { available: true };
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
_unavailableResult() {
|
|
277
|
+
const availability = this.getAvailability();
|
|
278
|
+
return availability.available
|
|
279
|
+
? null
|
|
280
|
+
: {
|
|
281
|
+
improved: false,
|
|
282
|
+
candidateCreated: false,
|
|
283
|
+
status: "unavailable",
|
|
284
|
+
reason: availability.reason,
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
|
|
201
288
|
/**
|
|
202
289
|
* Repair a skill after an execution error.
|
|
203
290
|
* @param {string} skillName
|
|
@@ -205,6 +292,16 @@ export class SkillImprover {
|
|
|
205
292
|
* @returns {Promise<{improved:boolean, reason:string}>}
|
|
206
293
|
*/
|
|
207
294
|
async repairFromError(skillName, errorContext) {
|
|
295
|
+
const unavailable = this._unavailableResult();
|
|
296
|
+
if (unavailable) return unavailable;
|
|
297
|
+
if (!isSafeSkillName(skillName)) {
|
|
298
|
+
return {
|
|
299
|
+
improved: false,
|
|
300
|
+
candidateCreated: false,
|
|
301
|
+
status: "error",
|
|
302
|
+
reason: "invalid skill name",
|
|
303
|
+
};
|
|
304
|
+
}
|
|
208
305
|
const skillContent = await this._readSkill(skillName);
|
|
209
306
|
if (!skillContent) {
|
|
210
307
|
return { improved: false, reason: "skill not found" };
|
|
@@ -225,10 +322,14 @@ export class SkillImprover {
|
|
|
225
322
|
meta.version = bumpVersion(meta.version);
|
|
226
323
|
|
|
227
324
|
const newContent = rebuildSkillMd(meta, newBody);
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
325
|
+
return this._finalizeCandidate({
|
|
326
|
+
skillName,
|
|
327
|
+
content: newContent,
|
|
328
|
+
version: meta.version,
|
|
329
|
+
triggerType: "error_repair",
|
|
330
|
+
detail: suggestion.diagnosis || "",
|
|
331
|
+
reason: suggestion.diagnosis || "repair candidate generated",
|
|
332
|
+
});
|
|
232
333
|
}
|
|
233
334
|
|
|
234
335
|
/**
|
|
@@ -238,6 +339,16 @@ export class SkillImprover {
|
|
|
238
339
|
* @returns {Promise<{improved:boolean, reason:string}>}
|
|
239
340
|
*/
|
|
240
341
|
async updateFromCorrection(skillName, correctionContext) {
|
|
342
|
+
const unavailable = this._unavailableResult();
|
|
343
|
+
if (unavailable) return unavailable;
|
|
344
|
+
if (!isSafeSkillName(skillName)) {
|
|
345
|
+
return {
|
|
346
|
+
improved: false,
|
|
347
|
+
candidateCreated: false,
|
|
348
|
+
status: "error",
|
|
349
|
+
reason: "invalid skill name",
|
|
350
|
+
};
|
|
351
|
+
}
|
|
241
352
|
const skillContent = await this._readSkill(skillName);
|
|
242
353
|
if (!skillContent) {
|
|
243
354
|
return { improved: false, reason: "skill not found" };
|
|
@@ -258,14 +369,14 @@ export class SkillImprover {
|
|
|
258
369
|
meta.version = bumpVersion(meta.version);
|
|
259
370
|
|
|
260
371
|
const newContent = rebuildSkillMd(meta, newBody);
|
|
261
|
-
|
|
262
|
-
this._logImprovement(
|
|
372
|
+
return this._finalizeCandidate({
|
|
263
373
|
skillName,
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
374
|
+
content: newContent,
|
|
375
|
+
version: meta.version,
|
|
376
|
+
triggerType: "user_correction",
|
|
377
|
+
detail: suggestion.whatChanged || "",
|
|
378
|
+
reason: suggestion.whatChanged || "correction candidate generated",
|
|
379
|
+
});
|
|
269
380
|
}
|
|
270
381
|
|
|
271
382
|
/**
|
|
@@ -275,6 +386,16 @@ export class SkillImprover {
|
|
|
275
386
|
* @returns {Promise<{improved:boolean, reason:string}>}
|
|
276
387
|
*/
|
|
277
388
|
async improveFromBetterTrajectory(skillName, betterTrajectory) {
|
|
389
|
+
const unavailable = this._unavailableResult();
|
|
390
|
+
if (unavailable) return unavailable;
|
|
391
|
+
if (!isSafeSkillName(skillName)) {
|
|
392
|
+
return {
|
|
393
|
+
improved: false,
|
|
394
|
+
candidateCreated: false,
|
|
395
|
+
status: "error",
|
|
396
|
+
reason: "invalid skill name",
|
|
397
|
+
};
|
|
398
|
+
}
|
|
278
399
|
const skillContent = await this._readSkill(skillName);
|
|
279
400
|
if (!skillContent) {
|
|
280
401
|
return { improved: false, reason: "skill not found" };
|
|
@@ -295,22 +416,34 @@ export class SkillImprover {
|
|
|
295
416
|
meta.version = bumpVersion(meta.version);
|
|
296
417
|
|
|
297
418
|
const newContent = rebuildSkillMd(meta, newBody);
|
|
298
|
-
|
|
299
|
-
this._logImprovement(
|
|
419
|
+
return this._finalizeCandidate({
|
|
300
420
|
skillName,
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
421
|
+
content: newContent,
|
|
422
|
+
version: meta.version,
|
|
423
|
+
triggerType: "better_trajectory",
|
|
424
|
+
detail: suggestion.improvements || "",
|
|
425
|
+
reason: suggestion.improvements || "improvement candidate generated",
|
|
426
|
+
});
|
|
306
427
|
}
|
|
307
428
|
|
|
308
429
|
/**
|
|
309
430
|
* Scan for skills that can be improved from recent high-score trajectories.
|
|
310
|
-
* @returns {Promise<{improved: string[], skipped: string[]}>}
|
|
431
|
+
* @returns {Promise<{status:string, improved: string[], candidates: object[], skipped: string[], reason?:string}>}
|
|
311
432
|
*/
|
|
312
433
|
async scanForImprovements() {
|
|
434
|
+
const unavailable = this._unavailableResult();
|
|
435
|
+
if (unavailable) {
|
|
436
|
+
return {
|
|
437
|
+
status: unavailable.status,
|
|
438
|
+
improved: [],
|
|
439
|
+
candidates: [],
|
|
440
|
+
skipped: [],
|
|
441
|
+
reason: unavailable.reason,
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
|
|
313
445
|
const improved = [];
|
|
446
|
+
const candidates = [];
|
|
314
447
|
const skipped = [];
|
|
315
448
|
|
|
316
449
|
// Find synthesized trajectories that have higher-scoring siblings
|
|
@@ -350,8 +483,12 @@ export class SkillImprover {
|
|
|
350
483
|
row.synthesized_skill,
|
|
351
484
|
better,
|
|
352
485
|
);
|
|
353
|
-
if (result.
|
|
354
|
-
|
|
486
|
+
if (result.candidateCreated || result.status === "diff-only") {
|
|
487
|
+
candidates.push({
|
|
488
|
+
skillName: row.synthesized_skill,
|
|
489
|
+
status: result.status,
|
|
490
|
+
candidatePath: result.candidatePath || null,
|
|
491
|
+
});
|
|
355
492
|
} else {
|
|
356
493
|
skipped.push(`${row.synthesized_skill}: ${result.reason}`);
|
|
357
494
|
}
|
|
@@ -363,7 +500,7 @@ export class SkillImprover {
|
|
|
363
500
|
}
|
|
364
501
|
}
|
|
365
502
|
|
|
366
|
-
return { improved, skipped };
|
|
503
|
+
return { status: "completed", improved, candidates, skipped };
|
|
367
504
|
}
|
|
368
505
|
|
|
369
506
|
// ── Internal ────────────────────────────────────────
|
|
@@ -391,7 +528,7 @@ export class SkillImprover {
|
|
|
391
528
|
* @returns {Promise<string|null>}
|
|
392
529
|
*/
|
|
393
530
|
async _readSkill(skillName) {
|
|
394
|
-
if (!this.skillsDir) return null;
|
|
531
|
+
if (!this.skillsDir || !isSafeSkillName(skillName)) return null;
|
|
395
532
|
const skillFile = _deps.path.join(this.skillsDir, skillName, "SKILL.md");
|
|
396
533
|
try {
|
|
397
534
|
if (typeof _deps.fs.promises.lstat === "function") {
|
|
@@ -413,21 +550,156 @@ export class SkillImprover {
|
|
|
413
550
|
}
|
|
414
551
|
|
|
415
552
|
/**
|
|
416
|
-
*
|
|
553
|
+
* Persist or return a candidate without mutating the source skill tree.
|
|
554
|
+
* The audit write is required: callers never receive a successful candidate
|
|
555
|
+
* result when persistence or logging fails.
|
|
556
|
+
* @param {{skillName:string, content:string, version:string, triggerType:string, detail:string, reason:string}} candidate
|
|
557
|
+
* @returns {Promise<object>}
|
|
558
|
+
*/
|
|
559
|
+
async _finalizeCandidate(candidate) {
|
|
560
|
+
if (this.diffOnly) {
|
|
561
|
+
this._logImprovement(
|
|
562
|
+
candidate.skillName,
|
|
563
|
+
candidate.triggerType,
|
|
564
|
+
candidate.detail,
|
|
565
|
+
);
|
|
566
|
+
return {
|
|
567
|
+
improved: false,
|
|
568
|
+
candidateCreated: false,
|
|
569
|
+
candidateGenerated: true,
|
|
570
|
+
status: "diff-only",
|
|
571
|
+
reason: candidate.reason,
|
|
572
|
+
candidate: {
|
|
573
|
+
skillName: candidate.skillName,
|
|
574
|
+
version: candidate.version,
|
|
575
|
+
content: candidate.content,
|
|
576
|
+
},
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
const persisted = await this._writeCandidate(
|
|
581
|
+
candidate.skillName,
|
|
582
|
+
candidate.version,
|
|
583
|
+
candidate.content,
|
|
584
|
+
);
|
|
585
|
+
this._logImprovement(
|
|
586
|
+
candidate.skillName,
|
|
587
|
+
candidate.triggerType,
|
|
588
|
+
candidate.detail,
|
|
589
|
+
);
|
|
590
|
+
return {
|
|
591
|
+
improved: false,
|
|
592
|
+
candidateCreated: true,
|
|
593
|
+
candidateGenerated: true,
|
|
594
|
+
status: "candidate",
|
|
595
|
+
reason: candidate.reason,
|
|
596
|
+
candidatePath: persisted.skillFile,
|
|
597
|
+
candidate: {
|
|
598
|
+
skillName: candidate.skillName,
|
|
599
|
+
version: candidate.version,
|
|
600
|
+
},
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* Write a versioned candidate to the isolated candidate registry.
|
|
417
606
|
* @param {string} skillName
|
|
607
|
+
* @param {string} version
|
|
418
608
|
* @param {string} content
|
|
609
|
+
* @returns {Promise<{skillDir:string, skillFile:string}>}
|
|
419
610
|
*/
|
|
420
|
-
async
|
|
421
|
-
|
|
611
|
+
async _writeCandidate(skillName, version, content) {
|
|
612
|
+
const availability = this.getAvailability();
|
|
613
|
+
if (!availability.available || this.diffOnly) {
|
|
614
|
+
throw new Error(
|
|
615
|
+
availability.reason || "candidate persistence unavailable",
|
|
616
|
+
);
|
|
617
|
+
}
|
|
618
|
+
if (!isSafeSkillName(skillName) || !/^\d+\.\d+\.\d+$/.test(version)) {
|
|
619
|
+
throw new Error("invalid candidate identity");
|
|
620
|
+
}
|
|
422
621
|
assertSkillFileSize(
|
|
423
622
|
"SKILL.md",
|
|
424
623
|
Buffer.byteLength(String(content), "utf8"),
|
|
425
624
|
this.skillLimits,
|
|
426
625
|
);
|
|
427
|
-
|
|
428
|
-
const
|
|
429
|
-
|
|
430
|
-
|
|
626
|
+
this._assertCandidateFilesystemSupport();
|
|
627
|
+
const candidateRoot = await this._ensurePrivateDirectory(
|
|
628
|
+
this.candidateOutputDir,
|
|
629
|
+
{ recursive: true },
|
|
630
|
+
);
|
|
631
|
+
await this._assertRealpathIsolation(candidateRoot);
|
|
632
|
+
|
|
633
|
+
const skillNamespacePath = _deps.path.join(
|
|
634
|
+
this.candidateOutputDir,
|
|
635
|
+
skillName,
|
|
636
|
+
);
|
|
637
|
+
const skillNamespace =
|
|
638
|
+
await this._ensurePrivateDirectory(skillNamespacePath);
|
|
639
|
+
this._assertPathWithinCandidateRoot(candidateRoot, skillNamespace);
|
|
640
|
+
|
|
641
|
+
const skillDir = _deps.path.join(skillNamespace, version);
|
|
642
|
+
const canonicalSkillDir = await this._ensurePrivateDirectory(skillDir);
|
|
643
|
+
this._assertPathWithinCandidateRoot(candidateRoot, canonicalSkillDir);
|
|
644
|
+
|
|
645
|
+
const skillFile = _deps.path.join(canonicalSkillDir, "SKILL.md");
|
|
646
|
+
await _deps.fs.promises.writeFile(skillFile, content, {
|
|
647
|
+
encoding: "utf-8",
|
|
648
|
+
flag: "wx",
|
|
649
|
+
});
|
|
650
|
+
return { skillDir: canonicalSkillDir, skillFile };
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
_assertCandidateFilesystemSupport() {
|
|
654
|
+
if (
|
|
655
|
+
typeof _deps.fs.promises.realpath !== "function" ||
|
|
656
|
+
typeof _deps.fs.promises.lstat !== "function"
|
|
657
|
+
) {
|
|
658
|
+
throw new Error(
|
|
659
|
+
"candidate filesystem identity support unavailable; refusing write",
|
|
660
|
+
);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
async _ensurePrivateDirectory(directory, options = {}) {
|
|
665
|
+
let stat;
|
|
666
|
+
try {
|
|
667
|
+
stat = await _deps.fs.promises.lstat(directory);
|
|
668
|
+
} catch (error) {
|
|
669
|
+
if (error?.code !== "ENOENT") throw error;
|
|
670
|
+
try {
|
|
671
|
+
await _deps.fs.promises.mkdir(directory, {
|
|
672
|
+
recursive: options.recursive === true,
|
|
673
|
+
mode: 0o700,
|
|
674
|
+
});
|
|
675
|
+
} catch (mkdirError) {
|
|
676
|
+
if (mkdirError?.code !== "EEXIST") throw mkdirError;
|
|
677
|
+
}
|
|
678
|
+
stat = await _deps.fs.promises.lstat(directory);
|
|
679
|
+
}
|
|
680
|
+
if (stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
681
|
+
throw new Error(
|
|
682
|
+
`candidate directory is not a trusted directory: ${directory}`,
|
|
683
|
+
);
|
|
684
|
+
}
|
|
685
|
+
return _deps.fs.promises.realpath(directory);
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
async _assertRealpathIsolation(candidateRoot) {
|
|
689
|
+
const sourceRoot = await _deps.fs.promises.realpath(this.skillsDir);
|
|
690
|
+
if (rootsOverlap(sourceRoot, candidateRoot)) {
|
|
691
|
+
throw new Error(
|
|
692
|
+
"candidate output resolves into the active skill tree; refusing write",
|
|
693
|
+
);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
_assertPathWithinCandidateRoot(candidateRoot, candidatePath) {
|
|
698
|
+
if (!rootContains(candidateRoot, candidatePath)) {
|
|
699
|
+
throw new Error(
|
|
700
|
+
"candidate parent resolves outside the candidate root; refusing write",
|
|
701
|
+
);
|
|
702
|
+
}
|
|
431
703
|
}
|
|
432
704
|
|
|
433
705
|
/**
|
|
@@ -437,16 +709,12 @@ export class SkillImprover {
|
|
|
437
709
|
* @param {string} detail
|
|
438
710
|
*/
|
|
439
711
|
_logImprovement(skillName, triggerType, detail) {
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
.run(skillName, triggerType, (detail || "").slice(0, 500));
|
|
447
|
-
} catch {
|
|
448
|
-
// Non-critical, don't break the flow
|
|
449
|
-
}
|
|
712
|
+
this.db
|
|
713
|
+
.prepare(
|
|
714
|
+
`INSERT INTO skill_improvement_log (skill_name, trigger_type, detail)
|
|
715
|
+
VALUES (?, ?, ?)`,
|
|
716
|
+
)
|
|
717
|
+
.run(skillName, triggerType, (detail || "").slice(0, 500));
|
|
450
718
|
}
|
|
451
719
|
|
|
452
720
|
/**
|