chainlesschain 0.162.197 → 0.162.200
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/bin/chainlesschain.js +14 -6
- package/package.json +4 -1
- package/scripts/command-lifecycle-report.mjs +94 -0
- package/src/assets/web-panel/assets/{AIOps-D3Gu1a5d.js → AIOps-DcQ0bWKy.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-DrwAoKaz.js → ActionButton-GecwfFPt.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-C7Q-gd_3.js → Analytics-CD6iSt9v.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-DgBtae-n.js → AppLayout-CDEtuEYf.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-pQ6LcA-W.js → Artifacts-DS3jAjeW.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-DK61tocB.js → Audit-CcSJA048.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-BOKgDFRh.js → BackgroundAgents-DRo9Morb.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-Bo9G4vTP.js → Backup-Vw5MrWft.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-Do_dE9_v.js → BaseInput-DNHIu823.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-COVvZzOL.js → Chat-DvAsO8-b.js} +4 -4
- package/src/assets/web-panel/assets/ChatBubbleRenderer-9aDuObay.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-Z9MJMolt.js → Checkbox-CKCa7tvn.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-BVzWKVbf.js → Codegen-CIFvZdaT.js} +1 -1
- package/src/assets/web-panel/assets/{Col-C8MnYcbT.js → Col-C79yz7bK.js} +1 -1
- package/src/assets/web-panel/assets/{Community-DQxqw15l.js → Community-DVvZskLJ.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-BAUmrh3y.js → Compact-oaew61hV.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-jPAkQ3zK.js → Compliance-DqA7wmRx.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-BBQyxbh8.js → Cowork-B-qGadxa.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-W5koq8Z5.js → Cron-CUlCl4sS.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-BQhnu5hS.js → Crosschain-CNOlr9DU.js} +1 -1
- package/src/assets/web-panel/assets/{DID-DD7PlI18.js → DID-DJqFCT_3.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-w_ILuwKI.js → Dashboard-CznGpphf.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-qiDUAyAo.js → Dropdown-_cJw218E.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-PDeBcI5H.js → EmailListRenderer-Dr-1BZ9C.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-SuArscRM.js → FamilyGuardDashboard-Dxf4xtSk.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-BCovZaUn.js → Federation-DOiG3i_1.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-DjlFdVFk.js → FormItemContext-BzbMcFB9.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-DoEZ7fcS.js → GenericCardRenderer-VbBrYttP.js} +1 -1
- package/src/assets/web-panel/assets/{Git-CV0-6GPI.js → Git-CLG8E6ED.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-B_SFN50w.js → Governance-LGJQfk_q.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-heI0Eo6k.js → Inference-DlQKx9lF.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-Cz5INS5p.js → KnowledgeGraph-DU8vEouV.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-BSFxiHkg.js → Logs-FZIQOdln.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-BJNWIrbw.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-0TWTpE7k.js → Marketplace-C_xd-zZI.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-CV4-wBLm.js → McpTools-q-YktphS.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-x5bH5rw5.js → Memory-Cq9OhDin.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-DaBmUu9G.js → MobileBridge-B03_R98T.js} +1 -1
- package/src/assets/web-panel/assets/MobileProjects-DZrk-qmF.js +1 -0
- package/src/assets/web-panel/assets/{Mtc-B_zKEBif.js → Mtc-BidCKW28.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-BCfKD2yg.js → MtcAudit-r-iaMVM7.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-DgYfj6c_.js → Multisig-CgTUFvff.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-Dz09lFUm.js → NLProgramming-B7NvzfAV.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-Cuj6ugBE.js → Notes-80CWNqJI.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-DK2WXjhg.js → NotificationSettings-0H6xdiY3.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-D1IAOQoo.js → OrderTableRenderer-CKP4AFWy.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-9fqnPvTw.js → Organization-B8sxCV_h.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-n5xHkhow.js → Overflow-CW9JBBhQ.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-B5AUWwH-.js → P2P-bdSQV7oZ.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-CoUDV6dy.js → PdhVaultBrowser-DQad42nW.js} +4 -4
- package/src/assets/web-panel/assets/{Permissions-DPRmQf_H.js → Permissions-CueexxTR.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-B37bsbEN.js → PersonalDataHub-fqxflhR8.js} +6 -6
- package/src/assets/web-panel/assets/{Pipeline-CCAuPqu9.js → Pipeline-KFwwnIMC.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-B81msZdv.js → Privacy-ClnD9buY.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-B-Py2NbF.js → ProjectInit-CZEz6sm3.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-CYsneiRB.js → ProjectSettings-B6NzpmOI.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-Y975Mh6R.js → Projects-DYyuwPCs.js} +1 -1
- package/src/assets/web-panel/assets/Providers-C3tz_jdh.js +1 -0
- package/src/assets/web-panel/assets/{QrScannerModal-Bj_6gJ0l.js → QrScannerModal-BzuOBbqB.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-BgpBCMPE.js → QuickAsk-CmNO9qSj.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-DhUW3YUA.js → Recommend-VJNj3_NF.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-DdeyfbkA.js → RemoteSession-CrbdbSBF.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-fxW421xd.js → Reputation-DTyMd45j.js} +1 -1
- package/src/assets/web-panel/assets/{Row-De73w1og.js → Row-DHrQ-Icz.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-3US_Q_lj.js → RssFeed-DqWxY9c5.js} +2 -2
- package/src/assets/web-panel/assets/{Search-DbZ_DU74.js → Search-ctF1Ks3J.js} +1 -1
- package/src/assets/web-panel/assets/{Security-DoIU1vMF.js → Security-BrQz8uo5.js} +4 -4
- package/src/assets/web-panel/assets/{Services-DB_RGqpc.js → Services-D4scVFar.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-DnzLaayC.js → Skeleton-oUScaOjv.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-VzVXHENF.js → Skills-CL-y0eLt.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-DMfvjCAI.js → Sla-DQExhwXj.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-DQQOYWbF.js → SpeechSettings-iNp_I7kN.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-Bmx3FG1P.js → SyncSettings-CTDlMo0A.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-DOQyfb-2.js → Tasks-4kb8l3sc.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-D6hTRT7c.js → Templates-BXcL9oXW.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-CTPQZAzs.js → Tenant-DLE8eS0D.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-B7vII3oH.js → Terminal-DGpUZPPK.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-BkiFJeXV.js → TimelineRenderer-C1pWXGPC.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-BPPdQMNo.js → Tokens-BYfbfqhZ.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-BxG8JAEI.js → Trigger-K9YfLOCw.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-DHD7nMpM.js → Trust-Ctkq77Hy.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-B4BJpU47.js → UkeySign-C6_CSe9J.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-CRiGpFx2.js → VideoEditing-Be3rpOWs.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-BSDSNiem.js → Wallet-D0UIplxk.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-DLTUh5Zv.js → WebAuthn-CF9bM9I3.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-tW2MwjhD.js → WorkflowEditor-D0H1XVPY.js} +1 -1
- package/src/assets/web-panel/assets/{chat-SgsLQ3FC.js → chat-Cgzzbe0o.js} +1 -1
- package/src/assets/web-panel/assets/{colors-BHjjLIKO.js → colors-BwNxEVS0.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-VceErdKq.js → compact-item-D60Z4Hhs.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-BZ332MYl.js → createContext-MFk58XB0.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-BJiw369i.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-CBTjJOjn.js → hasIn-CRj2SvGO.js} +1 -1
- package/src/assets/web-panel/assets/{index-DaCCy6lq.js → index--s45_1cb.js} +1 -1
- package/src/assets/web-panel/assets/{index-CMVvxIS8.js → index--tquQ2ZG.js} +1 -1
- package/src/assets/web-panel/assets/{index-BhXZEUAc.js → index-1T1rhokn.js} +1 -1
- package/src/assets/web-panel/assets/{index-BQSmZGgs.js → index-B-M82_rx.js} +1 -1
- package/src/assets/web-panel/assets/{index-BeiqkxJG.js → index-BE_el1Zs.js} +1 -1
- package/src/assets/web-panel/assets/{index-29wVzi-k.js → index-BhRkR7IO.js} +1 -1
- package/src/assets/web-panel/assets/{index-iYQNU9za.js → index-Bo0y96-K.js} +1 -1
- package/src/assets/web-panel/assets/{index-BRWhLbWF.js → index-CJgu5JUK.js} +1 -1
- package/src/assets/web-panel/assets/{index-BPXFLYdW.js → index-CLWwdebO.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cr4rjlW5.js → index-CNAK5QG_.js} +1 -1
- package/src/assets/web-panel/assets/{index-CaIS_TNX.js → index-CQjJjPRD.js} +1 -1
- package/src/assets/web-panel/assets/{index-1n7xjKDU.js → index-CTDbAfae.js} +1 -1
- package/src/assets/web-panel/assets/{index-BETEjuNr.js → index-CVI_9sRK.js} +1 -1
- package/src/assets/web-panel/assets/{index-By5jBtkV.js → index-CY14oMQK.js} +1 -1
- package/src/assets/web-panel/assets/{index-DgPvuIn2.js → index-CbiTJkZf.js} +1 -1
- package/src/assets/web-panel/assets/index-CiMouosn.js +1 -0
- package/src/assets/web-panel/assets/{index-CawpdGTW.js → index-CqWpxMqx.js} +1 -1
- package/src/assets/web-panel/assets/{index-_PHrIO4O.js → index-CvB8lYUR.js} +1 -1
- package/src/assets/web-panel/assets/{index-BOtvPdHt.js → index-D51XwtCY.js} +1 -1
- package/src/assets/web-panel/assets/{index-CrUgDQkf.js → index-D6HvK_1O.js} +1 -1
- package/src/assets/web-panel/assets/{index-BY1wexYG.js → index-D9jVIwLJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-C_B9rFQE.js → index-DA4tE98u.js} +1 -1
- package/src/assets/web-panel/assets/{index-B5KAGxNo.js → index-DBwOtXw0.js} +1 -1
- package/src/assets/web-panel/assets/{index-BC1xuQgX.js → index-DKdNzTIb.js} +1 -1
- package/src/assets/web-panel/assets/{index-B2u26AS3.js → index-DRySdmvN.js} +1 -1
- package/src/assets/web-panel/assets/{index-DePOeMDM.js → index-DWzyTev7.js} +1 -1
- package/src/assets/web-panel/assets/index-DXcKlwy7.js +1 -0
- package/src/assets/web-panel/assets/{index-BTlc-Bve.js → index-DYjs7jAP.js} +1 -1
- package/src/assets/web-panel/assets/{index-CLc19alH.js → index-DbROzMBp.js} +1 -1
- package/src/assets/web-panel/assets/{index-fU7wNHDB.js → index-DhFtk5PR.js} +1 -1
- package/src/assets/web-panel/assets/{index-D_SePDWj.js → index-Di2AbMLg.js} +1 -1
- package/src/assets/web-panel/assets/{index-vIXOpcjD.js → index-DvmocQsM.js} +1 -1
- package/src/assets/web-panel/assets/{index-BcUenmCX.js → index-Dw-o0zEF.js} +1 -1
- package/src/assets/web-panel/assets/{index-DUkVN794.js → index-DysPoP38.js} +1 -1
- package/src/assets/web-panel/assets/{index-CtjLcCNU.js → index-LX9pJxm_.js} +1 -1
- package/src/assets/web-panel/assets/{index-B40dQn0G.js → index-OI_fXWx0.js} +3 -3
- package/src/assets/web-panel/assets/{index-FJ3zC9LK.js → index-eDTOX8Xj.js} +1 -1
- package/src/assets/web-panel/assets/{index-DdefYBSw.js → index-gWKDsTzF.js} +1 -1
- package/src/assets/web-panel/assets/{index-CpumS98o.js → index-q_GDUVAA.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-D1oZNz0D.js → initDefaultProps-C61kZqEd.js} +1 -1
- package/src/assets/web-panel/assets/{motion-C8k6doMO.js → motion-DZnT5U1L.js} +1 -1
- package/src/assets/web-panel/assets/{move-DJ4qSbYt.js → move-L8wzwbel.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-CdpJD85j.js → mtc-parser-DLdYSejc.js} +1 -1
- package/src/assets/web-panel/assets/{omit-B0V4slKA.js → omit-D53cbS59.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-Di5NiW1F.js → pickAttrs-Bbc5HPtu.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-B5OMHgEM.js → placementArrow-BkvmO860.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-C6U9IWql.js → responsiveObserve-u2y1ryS1.js} +1 -1
- package/src/assets/web-panel/assets/{slide-BMyTH3rN.js → slide-BBqj7svU.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-C7d6BG7Z.js → statusUtils-GN0mqu2d.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-CR50QnQy.js → styleChecker-BvwPKoBT.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-DBEeoMb8.js → useFlexGapSupport-CCt8tnfY.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-3r099cjv.js → useFs-DXAxl2SV.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-C5IYNdTK.js → usePersonalDataHub-DihlR2dj.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-DmEOLRHf.js → vnode-D7BOX2gi.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-DnpUiVCB.js → zoom-CVbO-nL7.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +4 -4
- package/src/commands/agent.js +3 -3
- package/src/commands/background-session.js +145 -0
- package/src/commands/mcp.js +244 -7
- package/src/commands/serve.js +1 -1
- package/src/commands/session-show.js +129 -0
- package/src/commands/session.js +97 -145
- package/src/commands/skill.js +87 -50
- package/src/data/changelog.json +40 -1
- package/src/gateways/ws/session-protocol.js +14 -4
- package/src/gateways/ws/ws-agent-handler.js +39 -1
- package/src/harness/jsonl-session-store.js +1547 -127
- package/src/harness/mcp-client.js +2690 -409
- package/src/harness/session-index.js +80 -80
- package/src/harness/session-list-index.js +210 -7
- package/src/lazy-dispatch.js +76 -8
- package/src/lib/abort-utils.js +68 -0
- package/src/lib/agent-core.js +1 -59
- package/src/lib/background-interaction-journal.js +2 -2
- package/src/lib/chat-core.js +24 -6
- package/src/lib/command-lifecycle-report.js +516 -0
- package/src/lib/cowork-mcp-tools.js +47 -2
- package/src/lib/cowork-task-runner.js +11 -3
- package/src/lib/doctor-checkup.js +9 -7
- package/src/lib/learning/skill-improver.js +21 -2
- package/src/lib/mcp-call-ledger-store.js +116 -3
- package/src/lib/mcp-call-ledger.js +59 -14
- package/src/lib/mcp-client.js +1 -0
- package/src/lib/mcp-stdio-environment.js +48 -0
- package/src/lib/mcp-stdio-executable-identity.js +1133 -0
- package/src/lib/mcp-stdio-execution-authority.js +363 -0
- package/src/lib/mcp-stdio-package-materialization.js +1424 -0
- package/src/lib/mcp-tool-metadata.js +436 -0
- package/src/lib/mcp-tool-result.js +367 -0
- package/src/lib/observability/index.js +71 -0
- package/src/lib/packer/native-update-state.js +979 -1
- package/src/lib/packer/pack-update-applier.js +1050 -125
- package/src/lib/packer/pkg-config-generator.js +1 -0
- package/src/lib/paths.js +53 -0
- package/src/lib/pr-link-ledger.js +8 -27
- package/src/lib/pr-link-store.js +26 -0
- package/src/lib/process-execution-broker/index.js +192 -35
- package/src/lib/process-execution-broker/platform-sandbox.js +291 -24
- package/src/lib/process-tree-termination.js +253 -0
- package/src/lib/recent-session.js +17 -17
- package/src/lib/secure-file-identity.js +47 -0
- package/src/lib/secure-fs.js +21 -2
- package/src/lib/session-anti-rollback-anchor.js +766 -0
- package/src/lib/session-host-lease.js +492 -0
- package/src/lib/session-host-snapshot.js +9 -5
- package/src/lib/session-persistence-failure.js +109 -0
- package/src/lib/session-picker.js +26 -3
- package/src/lib/session-projection.js +5 -1
- package/src/lib/session-search.js +25 -11
- package/src/lib/session-tail.js +31 -1
- package/src/lib/side-effect-ledger-store.js +2 -2
- package/src/lib/skill-budget.js +222 -0
- package/src/lib/skill-execution-authority.js +385 -0
- package/src/lib/skill-execution-identity.js +39 -2
- package/src/lib/skill-loader.js +322 -86
- package/src/lib/skill-mcp.js +43 -1
- package/src/lib/turn-binding-store.js +2 -2
- package/src/repl/agent-repl.js +483 -77
- package/src/repl/bg-dashboard.js +81 -28
- package/src/repl/chat-repl.js +77 -12
- package/src/runtime/agent-core.js +428 -113
- package/src/runtime/agent-runtime.js +26 -3
- package/src/runtime/cleanup-deadline.js +143 -0
- package/src/runtime/headless-runner.js +494 -149
- package/src/runtime/headless-stream.js +504 -75
- package/src/runtime/mcp-config.js +157 -10
- package/src/runtime/mcp-tool-search.js +0 -0
- package/src/runtime/output-backpressure.js +419 -0
- package/src/runtime/pipe-safety.js +56 -21
- package/src/assets/web-panel/assets/ChatBubbleRenderer-BUUyJ5QO.js +0 -1
- package/src/assets/web-panel/assets/MarkdownRenderer-1mNozZ8v.js +0 -1
- package/src/assets/web-panel/assets/MobileProjects-DqnoFPgR.js +0 -1
- package/src/assets/web-panel/assets/Providers-uO-10aP1.js +0 -1
- package/src/assets/web-panel/assets/devWarning-DosMaL-k.js +0 -1
- package/src/assets/web-panel/assets/index-0O5ePnUq.js +0 -1
- package/src/assets/web-panel/assets/index-ChAzPigF.js +0 -1
- package/src/lib/skill-process-broker.js +0 -85
|
@@ -0,0 +1,516 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Privacy-preserving command lifecycle telemetry aggregation.
|
|
3
|
+
*
|
|
4
|
+
* The runtime emits one content-free OTLP metric for each migrated command
|
|
5
|
+
* invocation. This module consumes those OTLP payloads and combines them with
|
|
6
|
+
* an explicit collector coverage statement. Alias removal is fail-closed:
|
|
7
|
+
* missing release cycles, weak coverage, undocumented sampling bias or invalid
|
|
8
|
+
* metric points can only produce `insufficient-data`.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export const COMMAND_LIFECYCLE_REPORT_SCHEMA =
|
|
12
|
+
"chainlesschain.command-lifecycle-report.v1";
|
|
13
|
+
export const COMMAND_LIFECYCLE_COVERAGE_SCHEMA =
|
|
14
|
+
"chainlesschain.command-lifecycle-coverage.v1";
|
|
15
|
+
export const COMMAND_LIFECYCLE_INVOCATION_METRIC =
|
|
16
|
+
"chainlesschain.cli.command.lifecycle.invocations";
|
|
17
|
+
|
|
18
|
+
export const COMMAND_ALIAS_DECISION_POLICY = Object.freeze({
|
|
19
|
+
minimumObservedMinorCycles: 2,
|
|
20
|
+
minimumEligibleInstallations: 50,
|
|
21
|
+
minimumReportingInstallations: 30,
|
|
22
|
+
minimumCollectorCoverageRatio: 0.5,
|
|
23
|
+
minimumSampleRate: 0.1,
|
|
24
|
+
minimumCommandInvocations: 200,
|
|
25
|
+
minimumReplacementInvocations: 100,
|
|
26
|
+
maximumLegacyShareForRemoval: 0.01,
|
|
27
|
+
requiredPlatforms: Object.freeze(["linux", "macos", "windows"]),
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const REQUIRED_ATTRIBUTES = Object.freeze([
|
|
31
|
+
"command.name",
|
|
32
|
+
"command.route",
|
|
33
|
+
"command.outcome",
|
|
34
|
+
"cli.version",
|
|
35
|
+
"command.deprecated_since",
|
|
36
|
+
"command.removal_not_before",
|
|
37
|
+
]);
|
|
38
|
+
const ALLOWED_ATTRIBUTES = new Set(REQUIRED_ATTRIBUTES);
|
|
39
|
+
const EXACT_RELEASE = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/;
|
|
40
|
+
const COMMAND_NAME = /^[a-z0-9][a-z0-9-]{0,63}$/;
|
|
41
|
+
|
|
42
|
+
function parseRelease(value) {
|
|
43
|
+
if (typeof value !== "string") return null;
|
|
44
|
+
const match = EXACT_RELEASE.exec(value);
|
|
45
|
+
if (!match) return null;
|
|
46
|
+
return {
|
|
47
|
+
value,
|
|
48
|
+
major: Number(match[1]),
|
|
49
|
+
minor: Number(match[2]),
|
|
50
|
+
patch: Number(match[3]),
|
|
51
|
+
prerelease: match[4] || null,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function compareReleases(left, right) {
|
|
56
|
+
const a = typeof left === "string" ? parseRelease(left) : left;
|
|
57
|
+
const b = typeof right === "string" ? parseRelease(right) : right;
|
|
58
|
+
if (!a || !b) return null;
|
|
59
|
+
for (const key of ["major", "minor", "patch"]) {
|
|
60
|
+
if (a[key] !== b[key]) return a[key] < b[key] ? -1 : 1;
|
|
61
|
+
}
|
|
62
|
+
if (a.prerelease === b.prerelease) return 0;
|
|
63
|
+
if (a.prerelease === null) return 1;
|
|
64
|
+
if (b.prerelease === null) return -1;
|
|
65
|
+
return a.prerelease.localeCompare(b.prerelease);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function otlpValue(value) {
|
|
69
|
+
if (!value || typeof value !== "object") return undefined;
|
|
70
|
+
for (const key of ["stringValue", "intValue", "doubleValue", "boolValue"]) {
|
|
71
|
+
if (Object.hasOwn(value, key)) return value[key];
|
|
72
|
+
}
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function attributesFor(point) {
|
|
77
|
+
if (!Array.isArray(point?.attributes)) return null;
|
|
78
|
+
const attributes = {};
|
|
79
|
+
for (const item of point.attributes) {
|
|
80
|
+
if (
|
|
81
|
+
!item ||
|
|
82
|
+
typeof item.key !== "string" ||
|
|
83
|
+
!ALLOWED_ATTRIBUTES.has(item.key) ||
|
|
84
|
+
Object.hasOwn(attributes, item.key)
|
|
85
|
+
) {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
attributes[item.key] = otlpValue(item.value);
|
|
89
|
+
}
|
|
90
|
+
return attributes;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function pointCount(point) {
|
|
94
|
+
const raw = Object.hasOwn(point || {}, "asInt")
|
|
95
|
+
? point.asInt
|
|
96
|
+
: point?.asDouble;
|
|
97
|
+
const value = Number(raw);
|
|
98
|
+
return Number.isSafeInteger(value) && value > 0 ? value : null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function metricPayloads(document) {
|
|
102
|
+
if (Array.isArray(document)) {
|
|
103
|
+
return document.flatMap((entry) => metricPayloads(entry));
|
|
104
|
+
}
|
|
105
|
+
if (!document || typeof document !== "object") return [];
|
|
106
|
+
if (document.body) return metricPayloads(document.body);
|
|
107
|
+
if (Array.isArray(document.requests)) {
|
|
108
|
+
return metricPayloads(document.requests);
|
|
109
|
+
}
|
|
110
|
+
return Array.isArray(document.resourceMetrics) ? [document] : [];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function lifecycleEntries(manifest) {
|
|
114
|
+
return (manifest?.commands || [])
|
|
115
|
+
.filter(
|
|
116
|
+
(entry) =>
|
|
117
|
+
entry?.lifecycle?.state === "deprecated" &&
|
|
118
|
+
typeof entry.replacement === "string",
|
|
119
|
+
)
|
|
120
|
+
.sort((left, right) => left.name.localeCompare(right.name));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function blankCommand(entry) {
|
|
124
|
+
return {
|
|
125
|
+
command: entry.name,
|
|
126
|
+
replacement: entry.replacement,
|
|
127
|
+
deprecatedSince: entry.lifecycle.deprecatedSince,
|
|
128
|
+
removalNotBefore: entry.lifecycle.removalNotBefore,
|
|
129
|
+
minimumReleaseCycles: entry.lifecycle.minimumReleaseCycles,
|
|
130
|
+
legacy: { invocations: 0, completed: 0, error: 0 },
|
|
131
|
+
replacementRoute: { invocations: 0, completed: 0, error: 0 },
|
|
132
|
+
versions: new Set(),
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function validateCoverage(coverage, policy) {
|
|
137
|
+
const blockers = [];
|
|
138
|
+
if (coverage?.schema !== COMMAND_LIFECYCLE_COVERAGE_SCHEMA) {
|
|
139
|
+
blockers.push("coverage-schema-invalid");
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const decisionVersion = parseRelease(coverage?.decisionVersion);
|
|
143
|
+
if (!decisionVersion) blockers.push("decision-version-invalid");
|
|
144
|
+
|
|
145
|
+
const observedReleases = Array.isArray(coverage?.observedReleases)
|
|
146
|
+
? coverage.observedReleases.map(parseRelease)
|
|
147
|
+
: [];
|
|
148
|
+
if (
|
|
149
|
+
observedReleases.length === 0 ||
|
|
150
|
+
observedReleases.some((release) => !release)
|
|
151
|
+
) {
|
|
152
|
+
blockers.push("observed-releases-invalid");
|
|
153
|
+
}
|
|
154
|
+
const observedMinorCycles = new Set(
|
|
155
|
+
observedReleases
|
|
156
|
+
.filter(Boolean)
|
|
157
|
+
.map((release) => `${release.major}.${release.minor}`),
|
|
158
|
+
);
|
|
159
|
+
if (observedMinorCycles.size < policy.minimumObservedMinorCycles) {
|
|
160
|
+
blockers.push("release-observation-window-incomplete");
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const startMs = Date.parse(coverage?.window?.startedAt || "");
|
|
164
|
+
const endMs = Date.parse(coverage?.window?.endedAt || "");
|
|
165
|
+
if (
|
|
166
|
+
!Number.isFinite(startMs) ||
|
|
167
|
+
!Number.isFinite(endMs) ||
|
|
168
|
+
endMs <= startMs
|
|
169
|
+
) {
|
|
170
|
+
blockers.push("observation-window-invalid");
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const collector = coverage?.collector || {};
|
|
174
|
+
const eligibleInstallations = Number(collector.eligibleInstallations);
|
|
175
|
+
const reportingInstallations = Number(collector.reportingInstallations);
|
|
176
|
+
const sampleRate = Number(collector.sampleRate);
|
|
177
|
+
const coverageRatio =
|
|
178
|
+
Number.isFinite(eligibleInstallations) && eligibleInstallations > 0
|
|
179
|
+
? reportingInstallations / eligibleInstallations
|
|
180
|
+
: 0;
|
|
181
|
+
if (
|
|
182
|
+
!Number.isSafeInteger(eligibleInstallations) ||
|
|
183
|
+
eligibleInstallations < policy.minimumEligibleInstallations
|
|
184
|
+
) {
|
|
185
|
+
blockers.push("eligible-installation-cohort-too-small");
|
|
186
|
+
}
|
|
187
|
+
if (
|
|
188
|
+
!Number.isSafeInteger(reportingInstallations) ||
|
|
189
|
+
reportingInstallations < policy.minimumReportingInstallations ||
|
|
190
|
+
reportingInstallations > eligibleInstallations
|
|
191
|
+
) {
|
|
192
|
+
blockers.push("reporting-installation-cohort-too-small");
|
|
193
|
+
}
|
|
194
|
+
if (coverageRatio < policy.minimumCollectorCoverageRatio) {
|
|
195
|
+
blockers.push("collector-coverage-too-low");
|
|
196
|
+
}
|
|
197
|
+
if (
|
|
198
|
+
!Number.isFinite(sampleRate) ||
|
|
199
|
+
sampleRate < policy.minimumSampleRate ||
|
|
200
|
+
sampleRate > 1
|
|
201
|
+
) {
|
|
202
|
+
blockers.push("sample-rate-too-low-or-invalid");
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const platforms = collector.platforms || {};
|
|
206
|
+
for (const platform of policy.requiredPlatforms) {
|
|
207
|
+
if (
|
|
208
|
+
!Number.isSafeInteger(platforms[platform]) ||
|
|
209
|
+
platforms[platform] <= 0
|
|
210
|
+
) {
|
|
211
|
+
blockers.push(`platform-coverage-missing:${platform}`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const knownBiases = Array.isArray(collector.knownBiases)
|
|
216
|
+
? collector.knownBiases
|
|
217
|
+
: [];
|
|
218
|
+
if (knownBiases.length === 0) {
|
|
219
|
+
blockers.push("collector-bias-not-documented");
|
|
220
|
+
}
|
|
221
|
+
for (const bias of knownBiases) {
|
|
222
|
+
if (
|
|
223
|
+
!bias ||
|
|
224
|
+
typeof bias.id !== "string" ||
|
|
225
|
+
typeof bias.description !== "string"
|
|
226
|
+
) {
|
|
227
|
+
blockers.push("collector-bias-entry-invalid");
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
if (bias.blocking === true) {
|
|
231
|
+
blockers.push(`collector-bias-blocking:${bias.id}`);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return {
|
|
236
|
+
ready: blockers.length === 0,
|
|
237
|
+
blockers: [...new Set(blockers)].sort(),
|
|
238
|
+
decisionVersion: decisionVersion?.value || null,
|
|
239
|
+
observedReleases: observedReleases
|
|
240
|
+
.filter(Boolean)
|
|
241
|
+
.map((release) => release.value),
|
|
242
|
+
observedMinorCycles: [...observedMinorCycles].sort(),
|
|
243
|
+
window: {
|
|
244
|
+
startedAt: Number.isFinite(startMs)
|
|
245
|
+
? new Date(startMs).toISOString()
|
|
246
|
+
: null,
|
|
247
|
+
endedAt: Number.isFinite(endMs) ? new Date(endMs).toISOString() : null,
|
|
248
|
+
},
|
|
249
|
+
collector: {
|
|
250
|
+
eligibleInstallations:
|
|
251
|
+
Number.isSafeInteger(eligibleInstallations) &&
|
|
252
|
+
eligibleInstallations >= 0
|
|
253
|
+
? eligibleInstallations
|
|
254
|
+
: null,
|
|
255
|
+
reportingInstallations:
|
|
256
|
+
Number.isSafeInteger(reportingInstallations) &&
|
|
257
|
+
reportingInstallations >= 0
|
|
258
|
+
? reportingInstallations
|
|
259
|
+
: null,
|
|
260
|
+
coverageRatio,
|
|
261
|
+
sampleRate: Number.isFinite(sampleRate) ? sampleRate : null,
|
|
262
|
+
platforms: Object.fromEntries(
|
|
263
|
+
policy.requiredPlatforms.map((platform) => [
|
|
264
|
+
platform,
|
|
265
|
+
Number.isSafeInteger(platforms[platform]) && platforms[platform] >= 0
|
|
266
|
+
? platforms[platform]
|
|
267
|
+
: 0,
|
|
268
|
+
]),
|
|
269
|
+
),
|
|
270
|
+
knownBiases: knownBiases
|
|
271
|
+
.filter(
|
|
272
|
+
(bias) =>
|
|
273
|
+
bias &&
|
|
274
|
+
typeof bias.id === "string" &&
|
|
275
|
+
typeof bias.description === "string",
|
|
276
|
+
)
|
|
277
|
+
.map((bias) => ({
|
|
278
|
+
id: bias.id,
|
|
279
|
+
description: bias.description,
|
|
280
|
+
blocking: bias.blocking === true,
|
|
281
|
+
})),
|
|
282
|
+
},
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function decisionFor(command, coverage, ingestionReady, policy) {
|
|
287
|
+
const total =
|
|
288
|
+
command.legacy.invocations + command.replacementRoute.invocations;
|
|
289
|
+
const legacyShare = total > 0 ? command.legacy.invocations / total : null;
|
|
290
|
+
const reasons = [];
|
|
291
|
+
const removalComparison = compareReleases(
|
|
292
|
+
coverage.decisionVersion,
|
|
293
|
+
command.removalNotBefore,
|
|
294
|
+
);
|
|
295
|
+
if (!coverage.ready) reasons.push(...coverage.blockers);
|
|
296
|
+
if (!ingestionReady) reasons.push("telemetry-input-invalid");
|
|
297
|
+
if (removalComparison === null || removalComparison < 0) {
|
|
298
|
+
reasons.push("removal-version-not-reached");
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const observedForCommand = coverage.observedReleases.filter((version) => {
|
|
302
|
+
const afterDeprecation = compareReleases(version, command.deprecatedSince);
|
|
303
|
+
const beforeDecision = compareReleases(version, coverage.decisionVersion);
|
|
304
|
+
return (
|
|
305
|
+
afterDeprecation !== null && afterDeprecation >= 0 && beforeDecision <= 0
|
|
306
|
+
);
|
|
307
|
+
});
|
|
308
|
+
const observedCycles = new Set(
|
|
309
|
+
observedForCommand.map((version) => {
|
|
310
|
+
const parsed = parseRelease(version);
|
|
311
|
+
return `${parsed.major}.${parsed.minor}`;
|
|
312
|
+
}),
|
|
313
|
+
);
|
|
314
|
+
if (observedCycles.size < command.minimumReleaseCycles) {
|
|
315
|
+
reasons.push("command-release-window-incomplete");
|
|
316
|
+
}
|
|
317
|
+
if (total < policy.minimumCommandInvocations) {
|
|
318
|
+
reasons.push("command-sample-too-small");
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (reasons.length > 0) {
|
|
322
|
+
return {
|
|
323
|
+
decision: "insufficient-data",
|
|
324
|
+
reasons: [...new Set(reasons)].sort(),
|
|
325
|
+
totalInvocations: total,
|
|
326
|
+
legacyShare,
|
|
327
|
+
observedMinorCycles: [...observedCycles].sort(),
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if (legacyShare > policy.maximumLegacyShareForRemoval) {
|
|
332
|
+
return {
|
|
333
|
+
decision: "retain",
|
|
334
|
+
reasons: ["legacy-usage-above-removal-threshold"],
|
|
335
|
+
totalInvocations: total,
|
|
336
|
+
legacyShare,
|
|
337
|
+
observedMinorCycles: [...observedCycles].sort(),
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
if (
|
|
341
|
+
command.replacementRoute.invocations < policy.minimumReplacementInvocations
|
|
342
|
+
) {
|
|
343
|
+
return {
|
|
344
|
+
decision: "insufficient-data",
|
|
345
|
+
reasons: ["replacement-sample-too-small"],
|
|
346
|
+
totalInvocations: total,
|
|
347
|
+
legacyShare,
|
|
348
|
+
observedMinorCycles: [...observedCycles].sort(),
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
return {
|
|
352
|
+
decision: "remove",
|
|
353
|
+
reasons: ["compatibility-window-and-usage-thresholds-satisfied"],
|
|
354
|
+
totalInvocations: total,
|
|
355
|
+
legacyShare,
|
|
356
|
+
observedMinorCycles: [...observedCycles].sort(),
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
export function buildCommandLifecycleReport({
|
|
361
|
+
documents = [],
|
|
362
|
+
manifest,
|
|
363
|
+
coverage,
|
|
364
|
+
generatedAt = new Date().toISOString(),
|
|
365
|
+
policy = COMMAND_ALIAS_DECISION_POLICY,
|
|
366
|
+
} = {}) {
|
|
367
|
+
const entries = lifecycleEntries(manifest);
|
|
368
|
+
if (entries.length === 0) {
|
|
369
|
+
throw new Error("command manifest has no deprecated compatibility entries");
|
|
370
|
+
}
|
|
371
|
+
const byCommand = new Map(
|
|
372
|
+
entries.map((entry) => [entry.name, blankCommand(entry)]),
|
|
373
|
+
);
|
|
374
|
+
const violations = [];
|
|
375
|
+
let acceptedPoints = 0;
|
|
376
|
+
let rejectedPoints = 0;
|
|
377
|
+
let payloadCount = 0;
|
|
378
|
+
|
|
379
|
+
for (const payload of metricPayloads(documents)) {
|
|
380
|
+
payloadCount += 1;
|
|
381
|
+
for (const resource of payload.resourceMetrics || []) {
|
|
382
|
+
for (const scope of resource.scopeMetrics || []) {
|
|
383
|
+
for (const metric of scope.metrics || []) {
|
|
384
|
+
if (metric?.name !== COMMAND_LIFECYCLE_INVOCATION_METRIC) continue;
|
|
385
|
+
for (const point of metric?.sum?.dataPoints || []) {
|
|
386
|
+
const attributes = attributesFor(point);
|
|
387
|
+
const count = pointCount(point);
|
|
388
|
+
const command = attributes
|
|
389
|
+
? byCommand.get(attributes["command.name"])
|
|
390
|
+
: null;
|
|
391
|
+
const valid =
|
|
392
|
+
attributes &&
|
|
393
|
+
REQUIRED_ATTRIBUTES.every((key) =>
|
|
394
|
+
Object.hasOwn(attributes, key),
|
|
395
|
+
) &&
|
|
396
|
+
Object.keys(attributes).length === REQUIRED_ATTRIBUTES.length &&
|
|
397
|
+
count !== null &&
|
|
398
|
+
command &&
|
|
399
|
+
COMMAND_NAME.test(attributes["command.name"]) &&
|
|
400
|
+
["legacy", "replacement"].includes(attributes["command.route"]) &&
|
|
401
|
+
["completed", "error"].includes(attributes["command.outcome"]) &&
|
|
402
|
+
parseRelease(attributes["cli.version"]) &&
|
|
403
|
+
attributes["command.deprecated_since"] ===
|
|
404
|
+
command?.deprecatedSince &&
|
|
405
|
+
attributes["command.removal_not_before"] ===
|
|
406
|
+
command?.removalNotBefore;
|
|
407
|
+
if (!valid) {
|
|
408
|
+
rejectedPoints += 1;
|
|
409
|
+
violations.push("invalid-lifecycle-metric-point");
|
|
410
|
+
continue;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
acceptedPoints += 1;
|
|
414
|
+
const bucket =
|
|
415
|
+
attributes["command.route"] === "legacy"
|
|
416
|
+
? command.legacy
|
|
417
|
+
: command.replacementRoute;
|
|
418
|
+
bucket.invocations += count;
|
|
419
|
+
bucket[attributes["command.outcome"]] += count;
|
|
420
|
+
command.versions.add(attributes["cli.version"]);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
const coverageResult = validateCoverage(coverage, policy);
|
|
428
|
+
const ingestionReady = rejectedPoints === 0 && acceptedPoints > 0;
|
|
429
|
+
if (acceptedPoints === 0) violations.push("no-lifecycle-metric-points");
|
|
430
|
+
|
|
431
|
+
const decisions = [...byCommand.values()].map((command) => {
|
|
432
|
+
const recommendation = decisionFor(
|
|
433
|
+
command,
|
|
434
|
+
coverageResult,
|
|
435
|
+
ingestionReady,
|
|
436
|
+
policy,
|
|
437
|
+
);
|
|
438
|
+
return {
|
|
439
|
+
command: command.command,
|
|
440
|
+
replacement: command.replacement,
|
|
441
|
+
deprecatedSince: command.deprecatedSince,
|
|
442
|
+
removalNotBefore: command.removalNotBefore,
|
|
443
|
+
legacy: command.legacy,
|
|
444
|
+
replacementRoute: command.replacementRoute,
|
|
445
|
+
observedVersions: [...command.versions].sort((left, right) =>
|
|
446
|
+
compareReleases(left, right),
|
|
447
|
+
),
|
|
448
|
+
...recommendation,
|
|
449
|
+
};
|
|
450
|
+
});
|
|
451
|
+
const summary = decisions.reduce(
|
|
452
|
+
(result, item) => {
|
|
453
|
+
result[item.decision] += 1;
|
|
454
|
+
return result;
|
|
455
|
+
},
|
|
456
|
+
{ retain: 0, remove: 0, "insufficient-data": 0 },
|
|
457
|
+
);
|
|
458
|
+
|
|
459
|
+
return {
|
|
460
|
+
schema: COMMAND_LIFECYCLE_REPORT_SCHEMA,
|
|
461
|
+
generatedAt: new Date(generatedAt).toISOString(),
|
|
462
|
+
manifestSchema: manifest?.schema || null,
|
|
463
|
+
policy: {
|
|
464
|
+
...policy,
|
|
465
|
+
requiredPlatforms: [...policy.requiredPlatforms],
|
|
466
|
+
},
|
|
467
|
+
coverage: coverageResult,
|
|
468
|
+
ingestion: {
|
|
469
|
+
payloadCount,
|
|
470
|
+
acceptedPoints,
|
|
471
|
+
rejectedPoints,
|
|
472
|
+
ready: ingestionReady,
|
|
473
|
+
violations: [...new Set(violations)].sort(),
|
|
474
|
+
},
|
|
475
|
+
summary,
|
|
476
|
+
decisions,
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
export function renderCommandLifecycleReportMarkdown(report) {
|
|
481
|
+
const lines = [
|
|
482
|
+
"# CLI command lifecycle decision report",
|
|
483
|
+
"",
|
|
484
|
+
`Generated: ${report.generatedAt}`,
|
|
485
|
+
"",
|
|
486
|
+
`Coverage ready: **${report.coverage.ready ? "yes" : "no"}**`,
|
|
487
|
+
`Telemetry input ready: **${report.ingestion.ready ? "yes" : "no"}**`,
|
|
488
|
+
`Decisions: ${report.summary.remove} remove, ${report.summary.retain} retain, ${report.summary["insufficient-data"]} insufficient-data.`,
|
|
489
|
+
"",
|
|
490
|
+
];
|
|
491
|
+
if (report.coverage.blockers.length > 0) {
|
|
492
|
+
lines.push(
|
|
493
|
+
"Coverage blockers: " +
|
|
494
|
+
report.coverage.blockers.map((item) => `\`${item}\``).join(", "),
|
|
495
|
+
"",
|
|
496
|
+
);
|
|
497
|
+
}
|
|
498
|
+
if (report.ingestion.violations.length > 0) {
|
|
499
|
+
lines.push(
|
|
500
|
+
"Input violations: " +
|
|
501
|
+
report.ingestion.violations.map((item) => `\`${item}\``).join(", "),
|
|
502
|
+
"",
|
|
503
|
+
);
|
|
504
|
+
}
|
|
505
|
+
lines.push(
|
|
506
|
+
"| Legacy entry | Replacement | Legacy | Replacement | Legacy share | Decision | Reasons |",
|
|
507
|
+
"| --- | --- | ---: | ---: | ---: | --- | --- |",
|
|
508
|
+
);
|
|
509
|
+
for (const item of report.decisions) {
|
|
510
|
+
lines.push(
|
|
511
|
+
`| \`${item.command}\` | \`${item.replacement}\` | ${item.legacy.invocations} | ${item.replacementRoute.invocations} | ${item.legacyShare === null ? "n/a" : `${(item.legacyShare * 100).toFixed(2)}%`} | **${item.decision}** | ${item.reasons.join(", ")} |`,
|
|
512
|
+
);
|
|
513
|
+
}
|
|
514
|
+
lines.push("");
|
|
515
|
+
return lines.join("\n");
|
|
516
|
+
}
|
|
@@ -21,6 +21,10 @@
|
|
|
21
21
|
*/
|
|
22
22
|
import { validateMcpServerConfig } from "./skill-mcp.js";
|
|
23
23
|
import { mcpEffectDescriptorFields } from "./mcp-effect-contract.js";
|
|
24
|
+
import {
|
|
25
|
+
MCP_STDIO_LOCAL_CODE_TRUST_REQUIRED_CODE,
|
|
26
|
+
issueMcpStdioExecutionAuthority,
|
|
27
|
+
} from "./mcp-stdio-execution-authority.js";
|
|
24
28
|
|
|
25
29
|
export const _deps = {
|
|
26
30
|
importMcpClient: async () => {
|
|
@@ -90,6 +94,7 @@ export function toAgentTool(serverName, tool) {
|
|
|
90
94
|
* @param {{ mcpServers?: Array<object> }} template
|
|
91
95
|
* @param {object} [opts]
|
|
92
96
|
* @param {(msg: string, err?: Error) => void} [opts.onWarn]
|
|
97
|
+
* @param {(request: object) => boolean|Promise<boolean>} [opts.approveLocalCodeExecution]
|
|
93
98
|
* @returns {Promise<{
|
|
94
99
|
* mcpClient: object|null,
|
|
95
100
|
* mounted: string[],
|
|
@@ -121,15 +126,55 @@ export async function mountTemplateMcpTools(template, opts = {}) {
|
|
|
121
126
|
.filter(Boolean);
|
|
122
127
|
if (validated.length === 0) return empty;
|
|
123
128
|
|
|
129
|
+
const skipped = [];
|
|
130
|
+
const authorized = [];
|
|
131
|
+
for (const server of validated) {
|
|
132
|
+
let approved = false;
|
|
133
|
+
try {
|
|
134
|
+
approved =
|
|
135
|
+
typeof opts.approveLocalCodeExecution === "function" &&
|
|
136
|
+
(await opts.approveLocalCodeExecution({
|
|
137
|
+
templateId: template?.id || null,
|
|
138
|
+
serverName: server.name,
|
|
139
|
+
command: server.command,
|
|
140
|
+
args: [...server.args],
|
|
141
|
+
cwd: server.cwd || null,
|
|
142
|
+
envKeys: Object.keys(server.env || {}).sort(),
|
|
143
|
+
})) === true;
|
|
144
|
+
} catch (error) {
|
|
145
|
+
const message = error?.message || String(error);
|
|
146
|
+
skipped.push({ name: server.name, error: message });
|
|
147
|
+
opts.onWarn?.(
|
|
148
|
+
`[cowork-mcp] Local-code approval failed for "${server.name}": ${message}`,
|
|
149
|
+
error,
|
|
150
|
+
);
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
if (!approved) {
|
|
154
|
+
const message = `${MCP_STDIO_LOCAL_CODE_TRUST_REQUIRED_CODE}: template MCP server requires explicit local-code approval`;
|
|
155
|
+
skipped.push({ name: server.name, error: message });
|
|
156
|
+
opts.onWarn?.(`[cowork-mcp] Skipped "${server.name}": ${message}`);
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
const config = { ...server };
|
|
160
|
+
config.mcpStdioExecutionAuthority = issueMcpStdioExecutionAuthority({
|
|
161
|
+
serverName: server.name,
|
|
162
|
+
config,
|
|
163
|
+
approvalKind: "explicit-config",
|
|
164
|
+
approvalSource: `cowork-template:${template?.id || "unknown"}:${server.name}`,
|
|
165
|
+
});
|
|
166
|
+
authorized.push(config);
|
|
167
|
+
}
|
|
168
|
+
if (authorized.length === 0) return { ...empty, skipped };
|
|
169
|
+
|
|
124
170
|
const MCPClient = await _deps.importMcpClient();
|
|
125
171
|
const mcpClient = new MCPClient();
|
|
126
172
|
const mounted = [];
|
|
127
|
-
const skipped = [];
|
|
128
173
|
const extraToolDefinitions = [];
|
|
129
174
|
const externalToolDescriptors = {};
|
|
130
175
|
const externalToolExecutors = {};
|
|
131
176
|
|
|
132
|
-
for (const server of
|
|
177
|
+
for (const server of authorized) {
|
|
133
178
|
try {
|
|
134
179
|
await mcpClient.connect(server.name, server);
|
|
135
180
|
mounted.push(server.name);
|
|
@@ -313,6 +313,7 @@ export function prepareCoworkMcpRuntime(mcp, options = {}) {
|
|
|
313
313
|
}
|
|
314
314
|
let recoveryNotice = null;
|
|
315
315
|
let recoveryState = null;
|
|
316
|
+
let recoveryAuthority = null;
|
|
316
317
|
let bindingAllowed = false;
|
|
317
318
|
let expectedHeadHash = null;
|
|
318
319
|
try {
|
|
@@ -321,6 +322,7 @@ export function prepareCoworkMcpRuntime(mcp, options = {}) {
|
|
|
321
322
|
String(options.templateId || "free"),
|
|
322
323
|
);
|
|
323
324
|
const recovery = projected.recovery;
|
|
325
|
+
recoveryAuthority = recovery;
|
|
324
326
|
expectedHeadHash = projected.expectedHeadHash;
|
|
325
327
|
bindingAllowed = projected.bindingAllowed;
|
|
326
328
|
recoveryNotice = formatMcpLedgerRecoveryNotice(recovery);
|
|
@@ -361,9 +363,12 @@ export function prepareCoworkMcpRuntime(mcp, options = {}) {
|
|
|
361
363
|
}
|
|
362
364
|
}
|
|
363
365
|
|
|
364
|
-
const sink = createSessionMcpLedgerSink(
|
|
365
|
-
|
|
366
|
-
|
|
366
|
+
const sink = createSessionMcpLedgerSink(
|
|
367
|
+
sessionId,
|
|
368
|
+
_deps.appendSessionEvent === appendSessionEvent
|
|
369
|
+
? { recovery: recoveryAuthority }
|
|
370
|
+
: { appendEvent: _deps.appendSessionEvent },
|
|
371
|
+
);
|
|
367
372
|
const recoveryController =
|
|
368
373
|
createMcpRecoveryAdmissionController(recoveryState);
|
|
369
374
|
return {
|
|
@@ -395,6 +400,7 @@ export function prepareCoworkMcpRuntime(mcp, options = {}) {
|
|
|
395
400
|
* @param {number} [options.maxIterations] - Override iteration limit
|
|
396
401
|
* @param {number} [options.tokenBudget] - Override token budget
|
|
397
402
|
* @param {string} [options.mcpSessionId] - Stable MCP ledger session for resume
|
|
403
|
+
* @param {(request: object) => boolean|Promise<boolean>} [options.approveMcpLocalCodeExecution]
|
|
398
404
|
* @returns {Promise<{ taskId: string, status: string, result: object }>}
|
|
399
405
|
*/
|
|
400
406
|
export async function runCoworkTask(options = {}) {
|
|
@@ -410,6 +416,7 @@ export async function runCoworkTask(options = {}) {
|
|
|
410
416
|
onProgress = null,
|
|
411
417
|
signal = null,
|
|
412
418
|
mcpSessionId = null,
|
|
419
|
+
approveMcpLocalCodeExecution = null,
|
|
413
420
|
} = options;
|
|
414
421
|
|
|
415
422
|
if (!userMessage || typeof userMessage !== "string") {
|
|
@@ -458,6 +465,7 @@ export async function runCoworkTask(options = {}) {
|
|
|
458
465
|
|
|
459
466
|
// Mount template-declared MCP servers (best-effort, failures are tolerated)
|
|
460
467
|
const mcp = await mountTemplateMcpTools(template, {
|
|
468
|
+
approveLocalCodeExecution: approveMcpLocalCodeExecution,
|
|
461
469
|
onWarn: (msg) => {
|
|
462
470
|
if (onProgress) onProgress({ type: "mcp-warning", message: msg });
|
|
463
471
|
},
|
|
@@ -604,22 +604,24 @@ async function transcriptSection(_opts, _deps2) {
|
|
|
604
604
|
const { verifyAllSessions } =
|
|
605
605
|
await import("../harness/jsonl-session-store.js");
|
|
606
606
|
const results = verifyAllSessions({ limit: 200 });
|
|
607
|
-
const
|
|
608
|
-
|
|
607
|
+
const integrityFailures = results.filter((result) =>
|
|
608
|
+
["tampered", "missing", "conflict"].includes(result.status),
|
|
609
|
+
);
|
|
610
|
+
if (integrityFailures.length > 0) {
|
|
609
611
|
checks.push(
|
|
610
612
|
check(
|
|
611
613
|
"transcript-tamper",
|
|
612
614
|
"Transcript integrity",
|
|
613
615
|
CHECK_LEVELS.ERR,
|
|
614
|
-
`${
|
|
616
|
+
`${integrityFailures.length} transcript integrity failure(s): ${integrityFailures
|
|
615
617
|
.slice(0, 5)
|
|
616
|
-
.map((
|
|
617
|
-
.join(", ")}${
|
|
618
|
+
.map((result) => `${result.sessionId} [${result.status}]`)
|
|
619
|
+
.join(", ")}${integrityFailures.length > 5 ? ", …" : ""}`,
|
|
618
620
|
{
|
|
619
621
|
id: "transcript-tamper",
|
|
620
622
|
safe: false,
|
|
621
623
|
description: "Inspect the flagged transcripts",
|
|
622
|
-
command: `cc session verify ${
|
|
624
|
+
command: `cc session verify ${integrityFailures[0].sessionId}`,
|
|
623
625
|
},
|
|
624
626
|
),
|
|
625
627
|
);
|
|
@@ -629,7 +631,7 @@ async function transcriptSection(_opts, _deps2) {
|
|
|
629
631
|
"transcript-tamper",
|
|
630
632
|
"Transcript integrity",
|
|
631
633
|
CHECK_LEVELS.OK,
|
|
632
|
-
`${results.length} session(s) checked, no
|
|
634
|
+
`${results.length} session(s) checked, no integrity failures`,
|
|
633
635
|
),
|
|
634
636
|
);
|
|
635
637
|
}
|