chainlesschain 0.166.7 → 0.166.10
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 +21 -0
- package/package.json +4 -1
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AIOps-BlrmQxa2.js → AIOps-CLYBDAGv.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-zyRyLITg.js → ActionButton-CpeSttcu.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-DwGz7OGN.js → Analytics-Ba1TNLG0.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-Dc-nj7Zd.js → AppLayout-xBy8EOeg.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-7Hu3O73p.js → Artifacts-CJ_3TFb8.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-Bnoe3-fR.js → Audit-DUATFCG3.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-BqYFrDKZ.js → BackgroundAgents-BRXYPIGp.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-B3hwW1nz.js → Backup-D8auLx7y.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-XFVxLA4w.js → BaseInput-CBgdslQ3.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-CWRDNw7e.js → Chat-B32LvAiE.js} +5 -5
- package/src/assets/web-panel/assets/ChatBubbleRenderer-CVfvAy-n.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-kFeeBgac.js → Checkbox-BwzBxOpC.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-CCxp8xoI.js → Codegen-0U5vrg1H.js} +1 -1
- package/src/assets/web-panel/assets/{Col-BLKZalle.js → Col-dZ2i4aAj.js} +1 -1
- package/src/assets/web-panel/assets/{Community-DD0eWCez.js → Community-UVvpLr8c.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-CHqHWdy0.js → Compact-BDNEb1Bd.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-Bw54uPiK.js → Compliance-BrzQPxyS.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-DZ13zKNJ.js → Cowork-BbgGsu-9.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-Dlj5Jevg.js → Cron-G321XPhp.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-Nvz0aR0j.js → Crosschain-CgLNUyMn.js} +1 -1
- package/src/assets/web-panel/assets/{DID-Ct89eUyY.js → DID-BAfRj18q.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-BHUH5IAe.js → Dashboard-BNIxA4TK.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-BhZG-PO7.js → Dropdown-BLAUmCAN.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-ae4RVwwJ.js → EmailListRenderer-CsnhTW22.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-AOcxLfLr.js → FamilyGuardDashboard-zcQ7Ip1n.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-BK2PJQOF.js → Federation-KFwxodNU.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-C2n3xO2_.js → FormItemContext-3CYRLEHG.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-DcyUVFZq.js → GenericCardRenderer-D3d9D8L9.js} +1 -1
- package/src/assets/web-panel/assets/{Git-tBae9Lt8.js → Git-BJ202nJY.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-DmNR3AP4.js → Governance-ClnnXVQ-.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-CoseEigi.js → Inference-DNU-PTkc.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-Bdjd1sxr.js → KnowledgeGraph-DJuinbuY.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-CtlkKIcn.js → Logs-D4No-46s.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-Dt8hH_qA.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-BTYb7nEx.js → Marketplace-Ba-KqRN3.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-D0iUdW3P.js → McpTools-DVoxnPgE.js} +2 -2
- package/src/assets/web-panel/assets/{Memory--gwI7w4Y.js → Memory-D_C8gzAZ.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-JmFBi24T.js → MobileBridge-DnzTPcDH.js} +1 -1
- package/src/assets/web-panel/assets/MobileProjects-Jg2dVUc-.js +1 -0
- package/src/assets/web-panel/assets/{Mtc-u7avzyoO.js → Mtc-CvEHmHId.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-gT3z5fo_.js → MtcAudit-VNJmWqmv.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-BIqZTz5n.js → Multisig-BO0x_Vsa.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-DdhVfXYq.js → NLProgramming-CM2gI1UY.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-CBlyUmY3.js → Notes-CMc1HsML.js} +4 -4
- package/src/assets/web-panel/assets/{NotificationSettings-DSyI26N3.js → NotificationSettings-DGNmSKGM.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-B7IEuNz4.js → OrderTableRenderer-Du-Kct3p.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-ozhi4kGS.js → Organization-DRtRZjas.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-CBy57RVM.js → Overflow-Dp4OeWsn.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-CA-ZRv77.js → P2P-DXMJVls8.js} +2 -2
- package/src/assets/web-panel/assets/PdhVaultBrowser-0jYeVmyY.js +7 -0
- package/src/assets/web-panel/assets/{Permissions-DiP632GP.js → Permissions-D1Hbb51o.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-AnU8O-SS.js → PersonalDataHub-BridiDhr.js} +5 -5
- package/src/assets/web-panel/assets/{Pipeline-DDibOUaP.js → Pipeline-B4bUNCm1.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-D421t36T.js → Privacy-BTu_HEqK.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-DR879ZPJ.js → ProjectInit-SVtL2hnt.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-Ded8o6aY.js → ProjectSettings-B2Sh7JZ2.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-BHPxiucB.js → Projects-BwU1xalW.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-Boivuu-E.js → Providers-BWdWQSDw.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-xKD_ntKp.js → QrScannerModal-Brzc-Sxb.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-D3y_A6FV.js → QuickAsk-DIBz7FCe.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-BjKM-YC2.js → Recommend-CZ8RriIJ.js} +1 -1
- package/src/assets/web-panel/assets/RemoteSession-CM0KJQ3t.js +5 -0
- package/src/assets/web-panel/assets/{Reputation-Ci2GjH5p.js → Reputation-D_wo8H0O.js} +1 -1
- package/src/assets/web-panel/assets/{Row-B0-T2BSh.js → Row-D1G1fD7N.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-Bet-ur-r.js → RssFeed-dKCVtA0K.js} +3 -3
- package/src/assets/web-panel/assets/{Search-DWtRAS2R.js → Search-Be1D61xo.js} +1 -1
- package/src/assets/web-panel/assets/{Security-BDfu2Qn6.js → Security-IbNiR6OC.js} +4 -4
- package/src/assets/web-panel/assets/{Services-CvJmvSMM.js → Services-nuHK8y8p.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-CEbrKpdC.js → Skeleton-DnM5jdnC.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-CjVeiETf.js → Skills-DCTdoq_q.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-Dibypxum.js → Sla-DlWun-Ui.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-XflYXp7a.js → SpeechSettings-C19kv2sd.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-ro1Ivx1E.js → SyncSettings-CkQIR_Hu.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-CHq1qFbo.js → Tasks-DdbqOEbs.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-CRnUZxtZ.js → Templates-B7GpIXeo.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-jUX72JCA.js → Tenant-ByEstFhY.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-BBhAs7pt.js → Terminal-BKRVID1a.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-CD5M3RGp.js → TimelineRenderer-CNS3dHNv.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-sah3v0gu.js → Tokens-BykqylCL.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-BNT3Uz3s.js → Trigger-ByNxq51W.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-BytMGPMo.js → Trust-BlrFsdaS.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-BkYVMRQs.js → UkeySign-Dg26A-sd.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-psvb5Q7Y.js → VideoEditing-C1wUxNn0.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-PFQTW0MH.js → Wallet-OphEUzI4.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-DgO-OwRT.js → WebAuthn-_8lL08hF.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-DL2l9VNl.js → WorkflowEditor-BnTuIvnP.js} +1 -1
- package/src/assets/web-panel/assets/{chat-C84akEzN.js → chat-3N3VLQYr.js} +1 -1
- package/src/assets/web-panel/assets/{colors-DzTWuQKP.js → colors-Bo_G96gg.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-CR9LJQFv.js → compact-item-DjubfjI_.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-CTTov5Kn.js → createContext-Cx_HSLCk.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-BX944k9E.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-DALj523o.js → hasIn-D5OjAOlA.js} +1 -1
- package/src/assets/web-panel/assets/{index-D9ScRFxz.js → index-3_CEBEEf.js} +3 -3
- package/src/assets/web-panel/assets/{index-BKNIzw0U.js → index-B905-Mcs.js} +1 -1
- package/src/assets/web-panel/assets/{index-eJcFleIV.js → index-BScGr1BY.js} +1 -1
- package/src/assets/web-panel/assets/{index-CXaCRXN4.js → index-BgPb37tI.js} +1 -1
- package/src/assets/web-panel/assets/{index-B6QOJbSn.js → index-Bj7S5IJw.js} +1 -1
- package/src/assets/web-panel/assets/{index-CNmTDXmf.js → index-BvYo09C0.js} +1 -1
- package/src/assets/web-panel/assets/{index-7ux_XohH.js → index-BxgIE4PD.js} +1 -1
- package/src/assets/web-panel/assets/{index-D_Bg0LdK.js → index-C-4sfa0l.js} +1 -1
- package/src/assets/web-panel/assets/{index-DZER6Lj3.js → index-C-_Cy0k_.js} +1 -1
- package/src/assets/web-panel/assets/{index-CmELknPe.js → index-C4MAnkl5.js} +1 -1
- package/src/assets/web-panel/assets/{index-CXTEpfGe.js → index-C5IwN9q3.js} +1 -1
- package/src/assets/web-panel/assets/{index-W9axN_Ce.js → index-C8dPFFhy.js} +1 -1
- package/src/assets/web-panel/assets/{index-B2qpjU-D.js → index-CC7RnaPP.js} +1 -1
- package/src/assets/web-panel/assets/{index-D0MqUnKO.js → index-CCjq3g32.js} +1 -1
- package/src/assets/web-panel/assets/{index-DtW6Zr_i.js → index-CNmsP0Xf.js} +1 -1
- package/src/assets/web-panel/assets/{index-CqUgXYIQ.js → index-CRKk1JPX.js} +1 -1
- package/src/assets/web-panel/assets/{index-_8vetPbU.js → index-CVPDhmVH.js} +1 -1
- package/src/assets/web-panel/assets/index-C_9Jt_SR.js +1 -0
- package/src/assets/web-panel/assets/{index-Cef0n4k_.js → index-Ce8EWU6r.js} +1 -1
- package/src/assets/web-panel/assets/{index-CnxjXIsK.js → index-CmLwI-Y8.js} +1 -1
- package/src/assets/web-panel/assets/{index-CunGDV0g.js → index-CqZq3zWr.js} +1 -1
- package/src/assets/web-panel/assets/{index-DjE_ew7U.js → index-Cz5tQ5PY.js} +1 -1
- package/src/assets/web-panel/assets/{index-QW0EXx14.js → index-DBvZhdCb.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dah8M4l2.js → index-DD1zSPZH.js} +1 -1
- package/src/assets/web-panel/assets/{index-DBIPnoTj.js → index-DE4XtzMl.js} +1 -1
- package/src/assets/web-panel/assets/index-DL2EFx7m.js +1 -0
- package/src/assets/web-panel/assets/{index-3-XJoYgX.js → index-DXJqa1-I.js} +1 -1
- package/src/assets/web-panel/assets/{index-Be6Os5S8.js → index-DaU60K3A.js} +1 -1
- package/src/assets/web-panel/assets/{index-AXnERsyV.js → index-Dbdr8tVi.js} +1 -1
- package/src/assets/web-panel/assets/{index-hzzTITR9.js → index-DlcbE0sX.js} +1 -1
- package/src/assets/web-panel/assets/{index-s4CFyn-n.js → index-DrVjatPe.js} +1 -1
- package/src/assets/web-panel/assets/{index-CZhhFQi9.js → index-EZr3bvz2.js} +1 -1
- package/src/assets/web-panel/assets/{index-BDyt57zL.js → index-HioDjvUF.js} +1 -1
- package/src/assets/web-panel/assets/{index-DH6OG-ci.js → index-Q_vn0t8d.js} +1 -1
- package/src/assets/web-panel/assets/{index-BNaQt49r.js → index-UnpsQ6A0.js} +1 -1
- package/src/assets/web-panel/assets/{index-C5eeMl_2.js → index-Y-H7j25u.js} +1 -1
- package/src/assets/web-panel/assets/{index-CjkPDSX4.js → index-uAcJUJkx.js} +1 -1
- package/src/assets/web-panel/assets/{index-DxdzSljS.js → index-x7o-2T3S.js} +1 -1
- package/src/assets/web-panel/assets/{index-BHRXKiU1.js → index-ydnEPOzZ.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-JuW8mFj7.js → initDefaultProps-DurpENJY.js} +1 -1
- package/src/assets/web-panel/assets/{motion-Cwb0HBve.js → motion-CIh-z-gR.js} +1 -1
- package/src/assets/web-panel/assets/{move-DHokzMgu.js → move-Uc_yI2NA.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-DX31y6QS.js → mtc-parser-DUevj5wz.js} +1 -1
- package/src/assets/web-panel/assets/{omit-BCIUYDA6.js → omit-85eMYyur.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-DMCj4EoZ.js → pickAttrs-AuxDD20F.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-Dy5K-u1s.js → placementArrow-DOWn8dd1.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-DkGeZu54.js → responsiveObserve-CPxTWodH.js} +1 -1
- package/src/assets/web-panel/assets/{slide-Dc5dyJdg.js → slide-BqvbGPjx.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-DIUBjOaC.js → statusUtils-BzXKkV8d.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-DmyEOxm5.js → styleChecker-B2O7yRk3.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-FRhakk1e.js → useFlexGapSupport-DRWp7px1.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-clXyV1WQ.js → useFs-Dp0V7sjM.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-UF6DG41z.js → usePersonalDataHub-CyCrG3ql.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-CoCt95G7.js → vnode-CXhoFpFo.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-yVWt-ENm.js → zoom-0z3mtwEg.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +1 -1
- package/src/commands/orchestrate.js +61 -9
- package/src/data/changelog.json +47 -1
- package/src/gateways/ws/message-dispatcher.js +4 -0
- package/src/gateways/ws/session-protocol.js +89 -11
- package/src/gateways/ws/ws-agent-handler.js +65 -9
- package/src/gateways/ws/ws-approval-gate.js +181 -16
- package/src/gateways/ws/ws-server.js +16 -6
- package/src/generated/cc-agent-protocol.schema.json +148 -2
- package/src/lib/agent-team/task-lease.js +172 -3
- package/src/lib/agent-team/team-runner.js +60 -1
- package/src/lib/app-server/graph-runtime.js +326 -2
- package/src/lib/app-server/server.js +60 -0
- package/src/lib/approval-grant-ledger.js +69 -0
- package/src/lib/codex-app-server-adapter.js +11 -20
- package/src/lib/graph-kernel/compiler.js +66 -13
- package/src/lib/graph-kernel/cutover-authority-resolver.js +26 -0
- package/src/lib/graph-kernel/cutover-ledger.js +53 -11
- package/src/lib/graph-kernel/index.js +2 -0
- package/src/lib/graph-kernel/production-cutover-evidence.js +969 -0
- package/src/lib/graph-kernel/retirement-evidence.js +575 -0
- package/src/lib/graph-kernel/runtime.js +76 -14
- package/src/lib/interaction-adapter.js +61 -9
- package/src/lib/policy-decision-event.js +149 -0
- package/src/lib/process-execution-broker/platform-sandbox.js +15 -1
- package/src/lib/record-replay/index.js +1 -0
- package/src/lib/record-replay/playwright-ui-driver.js +327 -0
- package/src/lib/webhook-security.js +484 -36
- package/src/runtime/coding-agent-events.cjs +13 -11
- package/src/runtime/headless-runner.js +17 -1
- package/src/runtime/headless-stream.js +66 -3
- package/src/runtime/session-slash-commands.js +57 -6
- package/src/assets/web-panel/assets/ChatBubbleRenderer-Bf30VM8G.js +0 -1
- package/src/assets/web-panel/assets/MarkdownRenderer-CZsTBEQE.js +0 -1
- package/src/assets/web-panel/assets/MobileProjects-BK01cQRx.js +0 -1
- package/src/assets/web-panel/assets/PdhVaultBrowser-DOHc9zm8.js +0 -7
- package/src/assets/web-panel/assets/RemoteSession-CgqKMXG4.js +0 -5
- package/src/assets/web-panel/assets/devWarning-BOhvmlpc.js +0 -1
- package/src/assets/web-panel/assets/index-DDyMJexx.js +0 -1
- package/src/assets/web-panel/assets/index-btiojFYn.js +0 -1
|
@@ -6,6 +6,11 @@ import {
|
|
|
6
6
|
import { createGraphAuthorityBinding } from "../graph-kernel/authority.js";
|
|
7
7
|
import { GraphEventStore } from "../graph-kernel/event-store.js";
|
|
8
8
|
import { GraphKernel } from "../graph-kernel/runtime.js";
|
|
9
|
+
import {
|
|
10
|
+
diffGraphTrace,
|
|
11
|
+
locateBlockedRoot,
|
|
12
|
+
reduceGraphTrace,
|
|
13
|
+
} from "../graph-kernel/trace-reducer.js";
|
|
9
14
|
|
|
10
15
|
const TERMINAL = new Set([
|
|
11
16
|
"succeeded",
|
|
@@ -19,6 +24,10 @@ const TERMINAL = new Set([
|
|
|
19
24
|
"compensation_failed",
|
|
20
25
|
]);
|
|
21
26
|
const DIGEST = /^sha256:[a-f0-9]{64}$/u;
|
|
27
|
+
const DEFAULT_HISTORY_EVENT_LIMIT = 1_000;
|
|
28
|
+
const MAX_HISTORY_EVENT_LIMIT = 2_000;
|
|
29
|
+
const DEFAULT_HISTORY_SNAPSHOT_LIMIT = 200;
|
|
30
|
+
const MAX_HISTORY_SNAPSHOT_LIMIT = 200;
|
|
22
31
|
|
|
23
32
|
function runtimeError(code, message, details = {}) {
|
|
24
33
|
const error = new Error(message);
|
|
@@ -73,6 +82,51 @@ function nodeInput(inputs, nodeId) {
|
|
|
73
82
|
return { prompt: "" };
|
|
74
83
|
}
|
|
75
84
|
|
|
85
|
+
function boundedInteger(value, fallback, { min = 0, max } = {}) {
|
|
86
|
+
if (value === undefined || value === null) return fallback;
|
|
87
|
+
const number = Number(value);
|
|
88
|
+
if (!Number.isSafeInteger(number) || number < min || number > max) {
|
|
89
|
+
throw runtimeError(
|
|
90
|
+
"CC_GRAPH_HISTORY_RANGE_INVALID",
|
|
91
|
+
`Graph history range must be an integer between ${min} and ${max}`,
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
return number;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function debuggerProjection(projection) {
|
|
98
|
+
return Object.freeze({
|
|
99
|
+
...projection,
|
|
100
|
+
timeline: Object.freeze(
|
|
101
|
+
(projection.timeline || []).map((event) =>
|
|
102
|
+
Object.freeze({
|
|
103
|
+
seq: event.seq,
|
|
104
|
+
timestamp: event.timestamp,
|
|
105
|
+
type: event.type,
|
|
106
|
+
hash: event.hash,
|
|
107
|
+
}),
|
|
108
|
+
),
|
|
109
|
+
),
|
|
110
|
+
effects: Object.freeze(
|
|
111
|
+
(projection.effects || []).map((effect) =>
|
|
112
|
+
Object.freeze({
|
|
113
|
+
id: effect.id,
|
|
114
|
+
nodeId: effect.nodeId ?? null,
|
|
115
|
+
attemptId: effect.attemptId ?? null,
|
|
116
|
+
status: effect.status,
|
|
117
|
+
iterationPath: Object.freeze([...(effect.iterationPath || [])]),
|
|
118
|
+
receiptDigest: DIGEST.test(
|
|
119
|
+
String(effect.receipt?.receiptDigest || ""),
|
|
120
|
+
)
|
|
121
|
+
? effect.receipt.receiptDigest
|
|
122
|
+
: null,
|
|
123
|
+
compensationEffectId: effect.compensationEffectId ?? null,
|
|
124
|
+
}),
|
|
125
|
+
),
|
|
126
|
+
),
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
76
130
|
/**
|
|
77
131
|
* Host-owned Graph executor used by fixed App Server capabilities. GraphKernel
|
|
78
132
|
* owns ready/attempt/effect/terminal state; the injected executor owns only a
|
|
@@ -85,6 +139,7 @@ export class AppServerGraphRuntime {
|
|
|
85
139
|
? new GraphEventStore({ rolloutStore })
|
|
86
140
|
: new GraphEventStore(),
|
|
87
141
|
executeNode,
|
|
142
|
+
requestHumanTask = null,
|
|
88
143
|
now = Date.now,
|
|
89
144
|
createId = randomUUID,
|
|
90
145
|
onEvent = null,
|
|
@@ -96,6 +151,8 @@ export class AppServerGraphRuntime {
|
|
|
96
151
|
this.eventStore = eventStore;
|
|
97
152
|
this.rolloutStore = eventStore.rolloutStore;
|
|
98
153
|
this.executeNode = executeNode;
|
|
154
|
+
this.requestHumanTask =
|
|
155
|
+
typeof requestHumanTask === "function" ? requestHumanTask : null;
|
|
99
156
|
this.now = now;
|
|
100
157
|
this.createId = createId;
|
|
101
158
|
this.onEvent = typeof onEvent === "function" ? onEvent : null;
|
|
@@ -161,6 +218,9 @@ export class AppServerGraphRuntime {
|
|
|
161
218
|
persistence: "durable",
|
|
162
219
|
isolated: true,
|
|
163
220
|
terminalEvidence: true,
|
|
221
|
+
durableHumanTasks: true,
|
|
222
|
+
humanTaskQuorum: true,
|
|
223
|
+
humanTaskSeparationOfDuties: true,
|
|
164
224
|
authorityModes: Object.freeze(["shadow", "canonical"]),
|
|
165
225
|
featureGated: true,
|
|
166
226
|
});
|
|
@@ -206,7 +266,8 @@ export class AppServerGraphRuntime {
|
|
|
206
266
|
const compiled = compileGraphDefinition(definition);
|
|
207
267
|
const id = String(runId);
|
|
208
268
|
const request = {
|
|
209
|
-
definition:
|
|
269
|
+
definition:
|
|
270
|
+
compiled.definitionMigration?.backupDefinition || compiled.definition,
|
|
210
271
|
inputs: { ...inputs },
|
|
211
272
|
originSurface,
|
|
212
273
|
agentCapacity: Math.max(1, Number(agentCapacity) || 1),
|
|
@@ -323,11 +384,23 @@ export class AppServerGraphRuntime {
|
|
|
323
384
|
this._emit(runId, "graph/run-settled", { projection: status });
|
|
324
385
|
return status;
|
|
325
386
|
}
|
|
387
|
+
const pendingHumanTask = kernel
|
|
388
|
+
.humanTasks(runId)
|
|
389
|
+
.find((task) => ["open", "claimed"].includes(task.status));
|
|
390
|
+
if (pendingHumanTask) {
|
|
391
|
+
if (!this.requestHumanTask) return status;
|
|
392
|
+
await this._requestHumanTaskDecision(entry, pendingHumanTask);
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
326
395
|
const next = kernel
|
|
327
396
|
.readyNodes(runId)
|
|
328
397
|
.find((candidate) => candidate.dispatch !== "subgraph");
|
|
329
398
|
if (!next) return status;
|
|
330
|
-
|
|
399
|
+
if (entry.compiled.nodes[next.nodeId]?.kind === "human") {
|
|
400
|
+
await this._executeHumanTask(entry, next.nodeId);
|
|
401
|
+
} else {
|
|
402
|
+
await this._executeAttempt(entry, next.nodeId);
|
|
403
|
+
}
|
|
331
404
|
}
|
|
332
405
|
throw runtimeError(
|
|
333
406
|
"CC_GRAPH_DRIVE_LIMIT",
|
|
@@ -335,6 +408,157 @@ export class AppServerGraphRuntime {
|
|
|
335
408
|
);
|
|
336
409
|
}
|
|
337
410
|
|
|
411
|
+
async _executeHumanTask(entry, nodeId) {
|
|
412
|
+
const { kernel } = entry;
|
|
413
|
+
const runId = entry.runId;
|
|
414
|
+
const node = entry.compiled.nodes[nodeId];
|
|
415
|
+
if (node.effectClass !== "none") {
|
|
416
|
+
throw runtimeError(
|
|
417
|
+
"CC_GRAPH_HUMAN_TASK_EFFECT_UNSUPPORTED",
|
|
418
|
+
"HumanTask nodes must use effectClass=none; approved effects belong to a dependent task node",
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
const attempt = kernel.assignNode(runId, nodeId, "app-server-agent", {
|
|
422
|
+
ttlMs: this.writerLeaseTtlMs,
|
|
423
|
+
leaseId: `app-server-human-attempt:${this.createId()}`,
|
|
424
|
+
grant: { executor: "durable_human_task", originSurface: "desktop" },
|
|
425
|
+
});
|
|
426
|
+
const quorum =
|
|
427
|
+
node.join === "quorum" ? Math.max(1, Number(node.quorum) || 1) : 1;
|
|
428
|
+
const task = kernel.createHumanTask(runId, {
|
|
429
|
+
humanTaskId: `app-server-human-task:${this.createId()}`,
|
|
430
|
+
attemptId: attempt.id,
|
|
431
|
+
leaseId: attempt.leaseId,
|
|
432
|
+
fence: attempt.fence,
|
|
433
|
+
operation: nodeInput(entry.inputs, nodeId),
|
|
434
|
+
nonce: `app-server-human-nonce:${this.createId()}`,
|
|
435
|
+
ttlMs: this.writerLeaseTtlMs,
|
|
436
|
+
quorum,
|
|
437
|
+
// Product quorum is intentionally fail-closed: one identity can never
|
|
438
|
+
// contribute more than one vote to a multi-review HumanTask.
|
|
439
|
+
separationOfDuties: quorum > 1,
|
|
440
|
+
});
|
|
441
|
+
this._emit(runId, "graph/human-task-requested", {
|
|
442
|
+
nodeId,
|
|
443
|
+
humanTaskId: task.id,
|
|
444
|
+
quorum: task.quorum,
|
|
445
|
+
separationOfDuties: task.separationOfDuties,
|
|
446
|
+
});
|
|
447
|
+
if (!this.requestHumanTask) {
|
|
448
|
+
return kernel.getRun(runId);
|
|
449
|
+
}
|
|
450
|
+
return this._requestHumanTaskDecision(entry, task);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
async _requestHumanTaskDecision(entry, task) {
|
|
454
|
+
const { kernel, runId } = entry;
|
|
455
|
+
const response = await this.requestHumanTask({ task });
|
|
456
|
+
if (!response || typeof response !== "object" || Array.isArray(response)) {
|
|
457
|
+
throw runtimeError(
|
|
458
|
+
"CC_GRAPH_HUMAN_DECISION_INVALID",
|
|
459
|
+
"HumanTask client returned no structured decision",
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
for (const [field, expected] of [
|
|
463
|
+
["humanTaskId", task.id],
|
|
464
|
+
["runId", runId],
|
|
465
|
+
["revisionDigest", task.revisionDigest],
|
|
466
|
+
["operationDigest", task.operationDigest],
|
|
467
|
+
["nonce", task.nonce],
|
|
468
|
+
]) {
|
|
469
|
+
if (response[field] !== expected) {
|
|
470
|
+
throw runtimeError(
|
|
471
|
+
"CC_GRAPH_HUMAN_TASK_BINDING_MISMATCH",
|
|
472
|
+
`HumanTask client response does not match ${field}`,
|
|
473
|
+
);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
const actorId = String(response.actorId || "").trim();
|
|
477
|
+
if (!actorId) {
|
|
478
|
+
throw runtimeError(
|
|
479
|
+
"CC_GRAPH_HUMAN_DECISION_INVALID",
|
|
480
|
+
"HumanTask client response requires an authenticated actorId",
|
|
481
|
+
);
|
|
482
|
+
}
|
|
483
|
+
if (
|
|
484
|
+
task.separationOfDuties === true &&
|
|
485
|
+
task.decisions.some((entry) => entry.actorId === actorId)
|
|
486
|
+
) {
|
|
487
|
+
throw runtimeError(
|
|
488
|
+
"CC_GRAPH_HUMAN_SEPARATION_OF_DUTIES",
|
|
489
|
+
"HumanTask requires a different authenticated actor",
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
const claim = kernel.claimHumanTask(runId, task.id, actorId, {
|
|
493
|
+
claimLeaseId: `app-server-human-claim:${this.createId()}`,
|
|
494
|
+
ttlMs: Math.min(this.writerLeaseTtlMs, 5 * 60 * 1000),
|
|
495
|
+
});
|
|
496
|
+
const decided = kernel.decideHumanTask(runId, task.id, {
|
|
497
|
+
actorId,
|
|
498
|
+
claimLeaseId: claim.claimLeaseId,
|
|
499
|
+
revisionDigest: response.revisionDigest,
|
|
500
|
+
operationDigest: response.operationDigest,
|
|
501
|
+
nonce: response.nonce,
|
|
502
|
+
decision: response.decision,
|
|
503
|
+
});
|
|
504
|
+
this._emit(runId, "graph/human-task-decided", {
|
|
505
|
+
nodeId: decided.nodeId,
|
|
506
|
+
humanTaskId: decided.id,
|
|
507
|
+
actorId,
|
|
508
|
+
status: decided.status,
|
|
509
|
+
acceptedDecisions: decided.decisions.filter((entry) =>
|
|
510
|
+
["acceptOnce", "acceptForTurn", "acceptForSession"].includes(
|
|
511
|
+
entry.decision.kind,
|
|
512
|
+
),
|
|
513
|
+
).length,
|
|
514
|
+
quorum: decided.quorum,
|
|
515
|
+
});
|
|
516
|
+
if (
|
|
517
|
+
decided.status !== "decided" ||
|
|
518
|
+
["decline", "cancel"].includes(decided.decision?.kind)
|
|
519
|
+
) {
|
|
520
|
+
return kernel.getRun(runId);
|
|
521
|
+
}
|
|
522
|
+
const settlement = kernel.assignNode(
|
|
523
|
+
runId,
|
|
524
|
+
decided.nodeId,
|
|
525
|
+
"app-server-agent",
|
|
526
|
+
{
|
|
527
|
+
ttlMs: this.writerLeaseTtlMs,
|
|
528
|
+
leaseId: `app-server-human-settlement:${this.createId()}`,
|
|
529
|
+
grant: {
|
|
530
|
+
executor: "durable_human_task_decision",
|
|
531
|
+
humanTaskId: decided.id,
|
|
532
|
+
},
|
|
533
|
+
},
|
|
534
|
+
);
|
|
535
|
+
const settled = kernel.settleAttempt(runId, {
|
|
536
|
+
attemptId: settlement.id,
|
|
537
|
+
leaseId: settlement.leaseId,
|
|
538
|
+
fence: settlement.fence,
|
|
539
|
+
outcome: "succeeded",
|
|
540
|
+
evidence: {
|
|
541
|
+
outputDigest: graphDigest(
|
|
542
|
+
{
|
|
543
|
+
humanTaskId: decided.id,
|
|
544
|
+
revisionDigest: decided.revisionDigest,
|
|
545
|
+
operationDigest: decided.operationDigest,
|
|
546
|
+
decisions: decided.decisions,
|
|
547
|
+
},
|
|
548
|
+
"cc.app-server.human-task-settlement/v1",
|
|
549
|
+
),
|
|
550
|
+
},
|
|
551
|
+
usage: { turns: 0, tokens: 0, costUsd: 0, wallMs: 0 },
|
|
552
|
+
});
|
|
553
|
+
this._emit(runId, "graph/node-settled", {
|
|
554
|
+
nodeId: decided.nodeId,
|
|
555
|
+
attemptId: settlement.id,
|
|
556
|
+
outcome: "succeeded",
|
|
557
|
+
humanTaskId: decided.id,
|
|
558
|
+
});
|
|
559
|
+
return settled;
|
|
560
|
+
}
|
|
561
|
+
|
|
338
562
|
async _executeAttempt(entry, nodeId) {
|
|
339
563
|
const { kernel } = entry;
|
|
340
564
|
const runId = entry.runId;
|
|
@@ -451,6 +675,106 @@ export class AppServerGraphRuntime {
|
|
|
451
675
|
return entry.kernel.getRun(id);
|
|
452
676
|
}
|
|
453
677
|
|
|
678
|
+
humanTasks(runId) {
|
|
679
|
+
const id = String(runId);
|
|
680
|
+
const entry = this.runs.get(id) || this._recover(id);
|
|
681
|
+
return entry.kernel.humanTasks(id);
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
history(
|
|
685
|
+
runId,
|
|
686
|
+
{
|
|
687
|
+
afterSeq = 0,
|
|
688
|
+
limit = DEFAULT_HISTORY_EVENT_LIMIT,
|
|
689
|
+
snapshotLimit = DEFAULT_HISTORY_SNAPSHOT_LIMIT,
|
|
690
|
+
} = {},
|
|
691
|
+
) {
|
|
692
|
+
const id = String(runId);
|
|
693
|
+
const cursor = boundedInteger(afterSeq, 0, {
|
|
694
|
+
min: 0,
|
|
695
|
+
max: Number.MAX_SAFE_INTEGER,
|
|
696
|
+
});
|
|
697
|
+
const eventLimit = boundedInteger(limit, DEFAULT_HISTORY_EVENT_LIMIT, {
|
|
698
|
+
min: 1,
|
|
699
|
+
max: MAX_HISTORY_EVENT_LIMIT,
|
|
700
|
+
});
|
|
701
|
+
const projectionLimit = boundedInteger(
|
|
702
|
+
snapshotLimit,
|
|
703
|
+
DEFAULT_HISTORY_SNAPSHOT_LIMIT,
|
|
704
|
+
{ min: 1, max: MAX_HISTORY_SNAPSHOT_LIMIT },
|
|
705
|
+
);
|
|
706
|
+
const head = this.eventStore.head(id);
|
|
707
|
+
const effectiveCursor =
|
|
708
|
+
cursor === 0 && head.seq > eventLimit ? head.seq - eventLimit : cursor;
|
|
709
|
+
const selected = this.eventStore.read(id, {
|
|
710
|
+
afterSeq: effectiveCursor,
|
|
711
|
+
limit: eventLimit + 1,
|
|
712
|
+
});
|
|
713
|
+
const hasMore = selected.length > eventLimit;
|
|
714
|
+
const events = selected.slice(0, eventLimit);
|
|
715
|
+
const stateEvents = events.filter(
|
|
716
|
+
(event) => event.payload?.state?.version === 1,
|
|
717
|
+
);
|
|
718
|
+
if (stateEvents.length === 0) {
|
|
719
|
+
throw runtimeError(
|
|
720
|
+
"CC_GRAPH_TRACE_STATE_MISSING",
|
|
721
|
+
`Graph history has no runtime snapshot after sequence ${effectiveCursor}: ${id}`,
|
|
722
|
+
);
|
|
723
|
+
}
|
|
724
|
+
const snapshotEvents = stateEvents.slice(-projectionLimit);
|
|
725
|
+
const snapshots = snapshotEvents.map((event) => {
|
|
726
|
+
const through = events.filter((candidate) => candidate.seq <= event.seq);
|
|
727
|
+
return Object.freeze({
|
|
728
|
+
seq: event.seq,
|
|
729
|
+
timestamp: event.timestamp,
|
|
730
|
+
type: event.type,
|
|
731
|
+
hash: event.hash,
|
|
732
|
+
projection: debuggerProjection(reduceGraphTrace(through)),
|
|
733
|
+
});
|
|
734
|
+
});
|
|
735
|
+
const diffs = snapshots
|
|
736
|
+
.slice(1)
|
|
737
|
+
.map((snapshot, index) =>
|
|
738
|
+
diffGraphTrace(snapshots[index].projection, snapshot.projection),
|
|
739
|
+
);
|
|
740
|
+
const current = snapshots.at(-1).projection;
|
|
741
|
+
const blockedRoots = Object.freeze(
|
|
742
|
+
current.taskGraph.nodes
|
|
743
|
+
.filter((node) => node.blockedRoot)
|
|
744
|
+
.map((node) =>
|
|
745
|
+
Object.freeze({
|
|
746
|
+
nodeId: node.id,
|
|
747
|
+
...locateBlockedRoot(current, node.id),
|
|
748
|
+
}),
|
|
749
|
+
),
|
|
750
|
+
);
|
|
751
|
+
return Object.freeze({
|
|
752
|
+
schema: "chainlesschain.graph-debug-history/v1",
|
|
753
|
+
runId: id,
|
|
754
|
+
requestedAfterSeq: cursor,
|
|
755
|
+
afterSeq: effectiveCursor,
|
|
756
|
+
nextAfterSeq: events.at(-1)?.seq || effectiveCursor,
|
|
757
|
+
hasMore,
|
|
758
|
+
truncatedBefore: effectiveCursor > cursor,
|
|
759
|
+
truncatedSnapshots: stateEvents.length > projectionLimit,
|
|
760
|
+
eventCount: events.length,
|
|
761
|
+
events: Object.freeze(
|
|
762
|
+
events.map((event) =>
|
|
763
|
+
Object.freeze({
|
|
764
|
+
seq: event.seq,
|
|
765
|
+
timestamp: event.timestamp,
|
|
766
|
+
type: event.type,
|
|
767
|
+
hash: event.hash,
|
|
768
|
+
}),
|
|
769
|
+
),
|
|
770
|
+
),
|
|
771
|
+
snapshots: Object.freeze(snapshots),
|
|
772
|
+
diffs: Object.freeze(diffs),
|
|
773
|
+
blockedRoots,
|
|
774
|
+
current,
|
|
775
|
+
});
|
|
776
|
+
}
|
|
777
|
+
|
|
454
778
|
_recover(runId) {
|
|
455
779
|
let events;
|
|
456
780
|
try {
|
|
@@ -132,6 +132,7 @@ export class CcAppServer {
|
|
|
132
132
|
now,
|
|
133
133
|
createId,
|
|
134
134
|
executeNode: (context) => this._executeGraphNode(context),
|
|
135
|
+
requestHumanTask: (context) => this._requestHumanTask(context),
|
|
135
136
|
onEvent: (event) => {
|
|
136
137
|
void this._notify("graph/event", event).catch(() => {});
|
|
137
138
|
},
|
|
@@ -295,6 +296,7 @@ export class CcAppServer {
|
|
|
295
296
|
"graph/compile": () => this._graphCompile(message.params),
|
|
296
297
|
"graph/run": () => this._graphRun(message.params),
|
|
297
298
|
"graph/status": () => this._graphStatus(message.params),
|
|
299
|
+
"graph/history": () => this._graphHistory(message.params),
|
|
298
300
|
"graph/cancel": () => this._graphCancel(message.params),
|
|
299
301
|
"graph/reconcile": () => this._graphReconcile(message.params),
|
|
300
302
|
};
|
|
@@ -360,6 +362,17 @@ export class CcAppServer {
|
|
|
360
362
|
definitionId: compiled.definitionId,
|
|
361
363
|
revision: compiled.revision,
|
|
362
364
|
revisionDigest: compiled.revisionDigest,
|
|
365
|
+
migratedFrom: compiled.migratedFrom,
|
|
366
|
+
definitionMigration: compiled.definitionMigration
|
|
367
|
+
? {
|
|
368
|
+
schema: compiled.definitionMigration.schema,
|
|
369
|
+
fromVersion: compiled.definitionMigration.fromVersion,
|
|
370
|
+
toVersion: compiled.definitionMigration.toVersion,
|
|
371
|
+
revisionDigest: compiled.definitionMigration.revisionDigest,
|
|
372
|
+
rollbackDigest: compiled.definitionMigration.rollbackDigest,
|
|
373
|
+
backupAvailable: true,
|
|
374
|
+
}
|
|
375
|
+
: null,
|
|
363
376
|
topologicalOrder: [...compiled.topologicalOrder],
|
|
364
377
|
};
|
|
365
378
|
}
|
|
@@ -387,6 +400,15 @@ export class CcAppServer {
|
|
|
387
400
|
return this.graphRuntime.status(requiredString(params.runId, "runId"));
|
|
388
401
|
}
|
|
389
402
|
|
|
403
|
+
_graphHistory(rawParams) {
|
|
404
|
+
const params = requireObject(rawParams);
|
|
405
|
+
return this.graphRuntime.history(requiredString(params.runId, "runId"), {
|
|
406
|
+
afterSeq: params.afterSeq,
|
|
407
|
+
limit: params.limit,
|
|
408
|
+
snapshotLimit: params.snapshotLimit,
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
|
|
390
412
|
_graphCancel(rawParams) {
|
|
391
413
|
const params = requireObject(rawParams);
|
|
392
414
|
return this.graphRuntime.cancel(
|
|
@@ -909,6 +931,44 @@ export class CcAppServer {
|
|
|
909
931
|
return result;
|
|
910
932
|
}
|
|
911
933
|
|
|
934
|
+
async _requestHumanTask({ task }) {
|
|
935
|
+
const result = await this._requestClient("humanTask/decide", { task });
|
|
936
|
+
const response = requireObject(result, "humanTask decision");
|
|
937
|
+
const validation = validateApprovalDecision(response.decision);
|
|
938
|
+
const bindings = [
|
|
939
|
+
["humanTaskId", task.id],
|
|
940
|
+
["runId", task.runId],
|
|
941
|
+
["revisionDigest", task.revisionDigest],
|
|
942
|
+
["operationDigest", task.operationDigest],
|
|
943
|
+
["nonce", task.nonce],
|
|
944
|
+
];
|
|
945
|
+
if (
|
|
946
|
+
!validation.ok ||
|
|
947
|
+
typeof response.actorId !== "string" ||
|
|
948
|
+
!/^[A-Za-z0-9][A-Za-z0-9._:/-]{0,159}$/u.test(response.actorId) ||
|
|
949
|
+
Object.keys(response).some(
|
|
950
|
+
(field) =>
|
|
951
|
+
![
|
|
952
|
+
"humanTaskId",
|
|
953
|
+
"runId",
|
|
954
|
+
"revisionDigest",
|
|
955
|
+
"operationDigest",
|
|
956
|
+
"nonce",
|
|
957
|
+
"actorId",
|
|
958
|
+
"decision",
|
|
959
|
+
].includes(field),
|
|
960
|
+
) ||
|
|
961
|
+
bindings.some(([field, expected]) => response[field] !== expected)
|
|
962
|
+
) {
|
|
963
|
+
throw new JsonRpcError(
|
|
964
|
+
JSON_RPC_ERROR.INVALID_PARAMS,
|
|
965
|
+
"client returned an invalid or stale HumanTask decision",
|
|
966
|
+
{ errors: validation.errors },
|
|
967
|
+
);
|
|
968
|
+
}
|
|
969
|
+
return response;
|
|
970
|
+
}
|
|
971
|
+
|
|
912
972
|
async _completeTurn(turn, status, result) {
|
|
913
973
|
if (TERMINAL_TURN_STATUSES.has(turn.status)) return;
|
|
914
974
|
if (turn.assistantStarted) {
|
|
@@ -46,6 +46,14 @@ function permissionKey(permission) {
|
|
|
46
46
|
return `${permission.capability}\0${permission.scope}`;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
function grantId(sessionId, lifetime, permission) {
|
|
50
|
+
return `grant_${createHash("sha256")
|
|
51
|
+
.update(
|
|
52
|
+
`${String(sessionId)}\0${String(lifetime)}\0${permissionKey(permission)}`,
|
|
53
|
+
)
|
|
54
|
+
.digest("hex")}`;
|
|
55
|
+
}
|
|
56
|
+
|
|
49
57
|
function livePermission(permission, now) {
|
|
50
58
|
const validation = validateAppServerDefinition("PermissionGrant", permission);
|
|
51
59
|
if (!validation.ok) return false;
|
|
@@ -102,6 +110,67 @@ export class ApprovalGrantLedger {
|
|
|
102
110
|
return false;
|
|
103
111
|
}
|
|
104
112
|
|
|
113
|
+
listGrants() {
|
|
114
|
+
const now = this.now();
|
|
115
|
+
const grants = [];
|
|
116
|
+
for (const [lifetime, entries] of [
|
|
117
|
+
["turn", this.turnGrants],
|
|
118
|
+
["session", this.sessionGrants],
|
|
119
|
+
]) {
|
|
120
|
+
for (const [key, entry] of entries) {
|
|
121
|
+
if (!livePermission(entry.permission, now)) {
|
|
122
|
+
entries.delete(key);
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
grants.push(
|
|
126
|
+
Object.freeze({
|
|
127
|
+
grantId: grantId(this.sessionId, lifetime, entry.permission),
|
|
128
|
+
lifetime,
|
|
129
|
+
permission: Object.freeze({ ...entry.permission }),
|
|
130
|
+
binding: entry.binding,
|
|
131
|
+
grantedAt: entry.grantedAt,
|
|
132
|
+
...(lifetime === "turn" ? { turnId: this.turnId } : {}),
|
|
133
|
+
}),
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return Object.freeze(grants);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
revoke(grantIdValue) {
|
|
141
|
+
const requestedId = String(grantIdValue || "");
|
|
142
|
+
for (const [lifetime, entries] of [
|
|
143
|
+
["turn", this.turnGrants],
|
|
144
|
+
["session", this.sessionGrants],
|
|
145
|
+
]) {
|
|
146
|
+
for (const [key, entry] of entries) {
|
|
147
|
+
if (
|
|
148
|
+
grantId(this.sessionId, lifetime, entry.permission) !== requestedId
|
|
149
|
+
) {
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
entries.delete(key);
|
|
153
|
+
if (lifetime === "session") this.revision += 1;
|
|
154
|
+
return Object.freeze({
|
|
155
|
+
revoked: true,
|
|
156
|
+
persistedScope: lifetime === "session",
|
|
157
|
+
grant: Object.freeze({
|
|
158
|
+
grantId: requestedId,
|
|
159
|
+
lifetime,
|
|
160
|
+
permission: Object.freeze({ ...entry.permission }),
|
|
161
|
+
binding: entry.binding,
|
|
162
|
+
grantedAt: entry.grantedAt,
|
|
163
|
+
}),
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return Object.freeze({
|
|
168
|
+
revoked: false,
|
|
169
|
+
persistedScope: false,
|
|
170
|
+
grant: null,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
105
174
|
applyDecision(decision, requiredPermission, binding) {
|
|
106
175
|
if (
|
|
107
176
|
decision?.kind !== "acceptForTurn" &&
|
|
@@ -2,6 +2,11 @@ import { EventEmitter } from "node:events";
|
|
|
2
2
|
|
|
3
3
|
export const CODEX_APP_SERVER_PROTOCOL = "codex-app-server-experimental-v1";
|
|
4
4
|
export const CODEX_APP_SERVER_FEATURE_FLAG = "CC_EXPERIMENTAL_CODEX_APP_SERVER";
|
|
5
|
+
export const CODEX_APP_SERVER_COMPATIBILITY_MATRIX = Object.freeze([
|
|
6
|
+
Object.freeze({ version: "0.149.0" }),
|
|
7
|
+
Object.freeze({ version: "0.150.0" }),
|
|
8
|
+
Object.freeze({ version: "0.150.1" }),
|
|
9
|
+
]);
|
|
5
10
|
|
|
6
11
|
function adapterError(code, message, details = {}) {
|
|
7
12
|
const error = new Error(message);
|
|
@@ -12,30 +17,16 @@ function adapterError(code, message, details = {}) {
|
|
|
12
17
|
}
|
|
13
18
|
|
|
14
19
|
function parseVersion(value) {
|
|
15
|
-
const match = String(value || "")
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
function compareVersion(left, right) {
|
|
20
|
-
for (let index = 0; index < 3; index += 1) {
|
|
21
|
-
if (left[index] !== right[index]) return left[index] - right[index];
|
|
22
|
-
}
|
|
23
|
-
return 0;
|
|
20
|
+
const match = String(value || "")
|
|
21
|
+
.trim()
|
|
22
|
+
.match(/^(?:codex(?:-cli)?\s+)?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/u);
|
|
23
|
+
return match ? match.slice(1).map(Number).join(".") : null;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
export function isCodexAppServerVersionCompatible(version, matrix = []) {
|
|
27
27
|
const parsed = parseVersion(version);
|
|
28
28
|
if (!parsed || !Array.isArray(matrix) || matrix.length === 0) return false;
|
|
29
|
-
return matrix.some((
|
|
30
|
-
const minimum = parseVersion(range.min);
|
|
31
|
-
const maximum = parseVersion(range.maxExclusive);
|
|
32
|
-
return (
|
|
33
|
-
minimum &&
|
|
34
|
-
maximum &&
|
|
35
|
-
compareVersion(parsed, minimum) >= 0 &&
|
|
36
|
-
compareVersion(parsed, maximum) < 0
|
|
37
|
-
);
|
|
38
|
-
});
|
|
29
|
+
return matrix.some((entry) => parseVersion(entry?.version) === parsed);
|
|
39
30
|
}
|
|
40
31
|
|
|
41
32
|
function projectNotification(notification) {
|
|
@@ -103,7 +94,7 @@ export class CodexAppServerAdapter extends EventEmitter {
|
|
|
103
94
|
client,
|
|
104
95
|
fallback,
|
|
105
96
|
upstreamVersion,
|
|
106
|
-
compatibilityMatrix =
|
|
97
|
+
compatibilityMatrix = CODEX_APP_SERVER_COMPATIBILITY_MATRIX,
|
|
107
98
|
enabled = process.env[CODEX_APP_SERVER_FEATURE_FLAG] === "1",
|
|
108
99
|
timeoutMs = 120_000,
|
|
109
100
|
} = {}) {
|