chainlesschain 0.166.2 → 0.166.4
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/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AIOps-BnZ24WRk.js → AIOps-HcOh0ZbH.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-ivXY7MGb.js → ActionButton-DWjDqBhd.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-BQS4OD-k.js → Analytics-C6FS-y3W.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-wOBuOKTO.js → AppLayout-DCN5cldF.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-BYuC4nw-.js → Artifacts-Crd-KE8P.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-BIeVfBt6.js → Audit-BexuTQZc.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-hIuyhmJ7.js → BackgroundAgents-Dydkh-b5.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-B6ohdgPQ.js → Backup-BYq21wMP.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-BzgYCw6h.js → BaseInput-CmNZP6yM.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-CwahvZU1.js → Chat-J6bD0zCl.js} +6 -6
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-B-1IZ_k8.js → ChatBubbleRenderer-MEoaygnL.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-26EDP2x0.js → Checkbox-Ce-9vNmK.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-DYLw1Zvd.js → Codegen-zf7-Y-Eh.js} +1 -1
- package/src/assets/web-panel/assets/{Col-DH_zAvD-.js → Col-D-TkY0FD.js} +1 -1
- package/src/assets/web-panel/assets/{Community-DURrTXJu.js → Community-l300NA-1.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-CQyBqN0p.js → Compact-D-9Q_hxc.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-a0DfgKHD.js → Compliance-DAJ8lLiQ.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-C6fY_5Ke.js → Cowork-xmzZ967S.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-DOwmpbTe.js → Cron-CeGqO5W4.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-DEPOIEUs.js → Crosschain-DXN0AE-4.js} +1 -1
- package/src/assets/web-panel/assets/{DID-DaKc-CEV.js → DID-CtIKASPr.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-C-0KLXq5.js → Dashboard-DqRwT8NX.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-CnsDukzr.js → Dropdown-ddIVljO1.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-Bi_ACD4N.js → EmailListRenderer-CcsFzV4y.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-BZASpnx3.js → FamilyGuardDashboard-PnlAUGcG.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-43oTtzyp.js → Federation-BlmLuQrL.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-oyVadnkZ.js → FormItemContext-dDovBj1Y.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-Cwvfd6TZ.js → GenericCardRenderer-DTvGshFr.js} +1 -1
- package/src/assets/web-panel/assets/{Git-Cgi2wvU0.js → Git-DuFR2xgA.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-l8TWDKZ0.js → Governance-9EaNcJuq.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-Dc8j6TaO.js → Inference-B-iYWH_r.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DgcO-Fwz.js → KnowledgeGraph-BGBaKiLX.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-DjiZpiwa.js → Logs-G3wmXzfa.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-B5MFCi0N.js → MarkdownRenderer-hk0YwjTR.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-BeDn1hFc.js → Marketplace-BNARBeAv.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-PGdVfWK6.js → McpTools-DST2cSSK.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-e76lY3a1.js → Memory-piwDLsVH.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-DKlQqot_.js → MobileBridge-D-W8zHdm.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-C4g6WOEr.js → MobileProjects-CwaYHlVP.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-1j3NpRQq.js → Mtc-C2dxnN7Q.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-DgwiVd1C.js → MtcAudit-BwPZsV3N.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-Dzg5lZZY.js → Multisig-CUt8w2al.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-D7GYhst-.js → NLProgramming-DwxErx27.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-Blcfvaic.js → Notes-B-mXAZ84.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-Dc4Y6vfr.js → NotificationSettings-Bd1fBAzd.js} +1 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-CCInnavw.js +1 -0
- package/src/assets/web-panel/assets/{Organization-DBXq024L.js → Organization-DTTONYZT.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-CFPbdx1Y.js → Overflow-H6lCZ1rE.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-w7z61NR5.js → P2P-DyBZzeYW.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-B7eGGZOa.js → PdhVaultBrowser-jj7LhT04.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-Df5Cl50D.js → Permissions-B5nSQwsr.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-C-We7RF6.js → PersonalDataHub-CACaQtlY.js} +5 -5
- package/src/assets/web-panel/assets/{Pipeline-B6fHPnKy.js → Pipeline-5y-DR_Bw.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-B85BdiIc.js → Privacy-BMSA3Ehd.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-CcOyazSe.js → ProjectInit-VCOj1dTa.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-BE53KfuU.js → ProjectSettings-CJaXO_iE.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-DOvMOoe_.js → Projects-C21-MQ3H.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-5l7sT9lq.js → Providers-DZ1FNKUR.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-DE37RXy4.js → QrScannerModal-6DKc9UBX.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-BGmGCo7j.js → QuickAsk-Dyd0Ckrk.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-BFdx8Nrm.js → Recommend-CYW4-GJH.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-C1w-j_hB.js → RemoteSession-DLrfPy4F.js} +3 -3
- package/src/assets/web-panel/assets/{Reputation-BOvn-10y.js → Reputation-DfqPo1FI.js} +1 -1
- package/src/assets/web-panel/assets/{Row-db-a1YO9.js → Row-CR89H2x5.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-DeS43ZGM.js → RssFeed-B9QY7R-F.js} +3 -3
- package/src/assets/web-panel/assets/{Search-BaQYNfeb.js → Search-CUDnzu4w.js} +1 -1
- package/src/assets/web-panel/assets/{Security-yt1ESHDg.js → Security-CJ0CtM_Z.js} +3 -3
- package/src/assets/web-panel/assets/{Services-BcNqPUsS.js → Services-CyrQ6F5U.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-X3ceUQWf.js → Skeleton-DcwnKatQ.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-DmYeax6C.js → Skills-BhKkYF3_.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-C_fdfbuJ.js → Sla-CWh-awh7.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-CDqohXXQ.js → SpeechSettings-BaUMNzx7.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-B25maYEY.js → SyncSettings-D1Ijwwxl.js} +2 -2
- package/src/assets/web-panel/assets/Tasks-DFqjHi3T.js +1 -0
- package/src/assets/web-panel/assets/{Templates-DUH0iuc4.js → Templates-Ct-9_ki3.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-DrvhHf9T.js → Tenant-zBkgyilh.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-C2lJVYHA.js → Terminal-VVO2Hjfi.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-C1Hf_jYn.js → TimelineRenderer-CX9pIkx3.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-DniS1NpS.js → Tokens-DHaD6e_m.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-4jrcb8oA.js → Trigger-CFq9j3WV.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-DiBte55w.js → Trust-DuF7gIYX.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-CZRtc4VB.js → UkeySign-DbuidXJX.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-BtYGJDmg.js → VideoEditing-B8IA9_qE.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-D1nrTm7H.js → Wallet-DdCglSIK.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-DI91acpx.js → WebAuthn-CeteRsA8.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-BtQjI50d.js → WorkflowEditor-DJhIbG__.js} +1 -1
- package/src/assets/web-panel/assets/{chat-I4A-f4FC.js → chat-srOi_Wef.js} +1 -1
- package/src/assets/web-panel/assets/{colors-QZkDkFVt.js → colors-HpVHsNGK.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item--FtCdzpr.js → compact-item-DsLhrqWe.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-CaIKIKxP.js → createContext-aOasIUuE.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-B_acr36n.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-DLYUII6-.js → hasIn-D5BVMimS.js} +1 -1
- package/src/assets/web-panel/assets/{index-DGLHSCHM.js → index-B615KD2o.js} +1 -1
- package/src/assets/web-panel/assets/{index-3rVtgqa2.js → index-BCp70IlO.js} +1 -1
- package/src/assets/web-panel/assets/{index-Deg2S9ZJ.js → index-BD64r0Tx.js} +1 -1
- package/src/assets/web-panel/assets/{index-yayaayEA.js → index-BGBvkNN3.js} +1 -1
- package/src/assets/web-panel/assets/{index-B65QxJaG.js → index-BNbsjeEc.js} +1 -1
- package/src/assets/web-panel/assets/{index-BxPoPofq.js → index-BTwDhhvl.js} +1 -1
- package/src/assets/web-panel/assets/{index-C3PnIEOH.js → index-BigtYchO.js} +1 -1
- package/src/assets/web-panel/assets/index-BnLYpdox.js +1 -0
- package/src/assets/web-panel/assets/{index-BLGJmgmx.js → index-Bu6TVdgQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-CL4c5D6G.js → index-C4tPtHU0.js} +1 -1
- package/src/assets/web-panel/assets/{index-Du2wUavy.js → index-CGVHW0nF.js} +1 -1
- package/src/assets/web-panel/assets/{index-DA_TPdzX.js → index-CQEoGqMG.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ch0IFpGy.js → index-CQXp_uNd.js} +1 -1
- package/src/assets/web-panel/assets/{index-CBtII-nl.js → index-CSxJ4oRd.js} +1 -1
- package/src/assets/web-panel/assets/{index-21I79XSf.js → index-CUUs12L9.js} +1 -1
- package/src/assets/web-panel/assets/{index-DAuf8r5o.js → index-CVp_dHCs.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bs0qPPFb.js → index-ChGUPEwS.js} +1 -1
- package/src/assets/web-panel/assets/{index-DVitI3UQ.js → index-CvhVCHk-.js} +1 -1
- package/src/assets/web-panel/assets/{index-DK2r6Bv7.js → index-D1xZMQrN.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ooo_5jHo.js → index-D2XRXkZU.js} +1 -1
- package/src/assets/web-panel/assets/{index-DJrd_CR3.js → index-DQ5j1VKg.js} +1 -1
- package/src/assets/web-panel/assets/{index-CXaeZCRP.js → index-DRCIPsdm.js} +1 -1
- package/src/assets/web-panel/assets/{index-CQ7OBHka.js → index-DV4y5rUj.js} +1 -1
- package/src/assets/web-panel/assets/{index-eTqL91aV.js → index-DcLBzIiI.js} +1 -1
- package/src/assets/web-panel/assets/{index-CisYjy88.js → index-DiXsis7m.js} +1 -1
- package/src/assets/web-panel/assets/{index-D4CXFU-w.js → index-Dpj-rEoV.js} +1 -1
- package/src/assets/web-panel/assets/{index-BebTT7iR.js → index-Dum_r_oY.js} +1 -1
- package/src/assets/web-panel/assets/index-Dzmwsl_v.js +1 -0
- package/src/assets/web-panel/assets/{index-vfiDxdN2.js → index-LSz0ZOB8.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ygi2rYL1.js → index-Ow3SRXRX.js} +1 -1
- package/src/assets/web-panel/assets/{index-hfiZ-i2b.js → index-P6IMLmLT.js} +1 -1
- package/src/assets/web-panel/assets/{index-CgMm85BY.js → index-V0gpVTbS.js} +1 -1
- package/src/assets/web-panel/assets/{index-0DYBQ-6e.js → index-b0Y0hDhX.js} +1 -1
- package/src/assets/web-panel/assets/{index-BGWSmE3G.js → index-dPFzlII1.js} +1 -1
- package/src/assets/web-panel/assets/{index-OQPIS_am.js → index-fZnlPk7H.js} +1 -1
- package/src/assets/web-panel/assets/{index-BkJgWW_L.js → index-kYqi6zYp.js} +1 -1
- package/src/assets/web-panel/assets/{index-BvdwNnHC.js → index-lFOfvZDn.js} +3 -3
- package/src/assets/web-panel/assets/{index-BcgtvJbC.js → index-q--LtRh9.js} +1 -1
- package/src/assets/web-panel/assets/{index-D2xETCkj.js → index-vQ6jy8Ew.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-DDkTvw8k.js → initDefaultProps-1NU9yNpr.js} +1 -1
- package/src/assets/web-panel/assets/{motion-fkr96tN5.js → motion-BZk2hQ6c.js} +1 -1
- package/src/assets/web-panel/assets/{move-CGBV0SV2.js → move-BNvublME.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-DAsCliFt.js → mtc-parser-DWk5BUoL.js} +1 -1
- package/src/assets/web-panel/assets/{omit-BM8PMmKN.js → omit-DztYxX3x.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-C6SdWoTK.js → pickAttrs-DURL8mtg.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-BG0EVu-d.js → placementArrow-B820R56z.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-L10IvfYC.js → responsiveObserve-DUAkGJL1.js} +1 -1
- package/src/assets/web-panel/assets/{slide-TddIkbrn.js → slide-uZQeNSo6.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-FlveF2Yn.js → statusUtils-CGR1wFVq.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-BrXRyebg.js → styleChecker-BIIPOMsQ.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-B7_oiDXZ.js → useFlexGapSupport-rIB9c0g2.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-CQYeRYWb.js → useFs-CrJe1BJi.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-DspIkhVi.js → usePersonalDataHub-DyEoD3Qm.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-BCwmwMAW.js → vnode-CjohPxI0.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-B0cEGluV.js → zoom-C5POL1_6.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/team.js +165 -53
- package/src/data/changelog.json +26 -0
- package/src/gateways/ws/remote-session-protocol.js +20 -15
- package/src/generated/cc-agent-protocol.schema.json +67 -1
- package/src/lib/agent-team/task-lease.js +718 -6
- package/src/lib/agent-team/team-graph-projection.js +303 -0
- package/src/lib/agent-team/team-message-bridge.js +83 -5
- package/src/lib/agent-team/team-message-tools.js +34 -1
- package/src/lib/agent-team/team-runner.js +1131 -9
- package/src/lib/agent-team/team-session-message-adapter.js +720 -0
- package/src/lib/app-server/cli-agent-kernel-adapter.js +1 -0
- package/src/lib/app-server/protocol.js +44 -8
- package/src/lib/app-server/server.js +8 -11
- package/src/lib/approval-grant-ledger.js +187 -0
- package/src/lib/remote-approval-bridge.js +8 -12
- package/src/lib/remote-approval-decision.js +87 -0
- package/src/lib/session-message-fabric.js +265 -3
- package/src/runtime/headless-stream.js +184 -19
- package/src/assets/web-panel/assets/OrderTableRenderer-dgM87XIn.js +0 -1
- package/src/assets/web-panel/assets/Tasks-3ILwauYw.js +0 -1
- package/src/assets/web-panel/assets/devWarning-D7qVrkrO.js +0 -1
- package/src/assets/web-panel/assets/index-D9Y_ZwZn.js +0 -1
- package/src/assets/web-panel/assets/index-D_cPes4G.js +0 -1
|
@@ -1,13 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import schema from "../../generated/cc-agent-protocol.schema.json" with { type: "json" };
|
|
2
2
|
import { validate } from "../json-schema-validate.js";
|
|
3
3
|
|
|
4
|
-
const schema = JSON.parse(
|
|
5
|
-
readFileSync(
|
|
6
|
-
new URL("../../generated/cc-agent-protocol.schema.json", import.meta.url),
|
|
7
|
-
"utf8",
|
|
8
|
-
),
|
|
9
|
-
);
|
|
10
|
-
|
|
11
4
|
export const APP_SERVER_PROTOCOL_VERSION = schema["x-cc-protocol"].version;
|
|
12
5
|
export const APP_SERVER_MIN_PROTOCOL_VERSION =
|
|
13
6
|
schema["x-cc-protocol"].minimumCompatibleVersion;
|
|
@@ -53,6 +46,49 @@ export function validateAppServerMessage(value) {
|
|
|
53
46
|
});
|
|
54
47
|
}
|
|
55
48
|
|
|
49
|
+
const definitionSchemas = new Map();
|
|
50
|
+
|
|
51
|
+
/** Validate one canonical $defs value without maintaining a second hand-written union. */
|
|
52
|
+
export function validateAppServerDefinition(name, value) {
|
|
53
|
+
const definition = APP_SERVER_SCHEMA.$defs?.[name];
|
|
54
|
+
if (!definition) {
|
|
55
|
+
return Object.freeze({
|
|
56
|
+
ok: false,
|
|
57
|
+
errors: Object.freeze([
|
|
58
|
+
Object.freeze({
|
|
59
|
+
path: "#",
|
|
60
|
+
message: `unknown protocol definition ${String(name)}`,
|
|
61
|
+
}),
|
|
62
|
+
]),
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
let definitionSchema = definitionSchemas.get(name);
|
|
66
|
+
if (!definitionSchema) {
|
|
67
|
+
definitionSchema = {
|
|
68
|
+
$schema: APP_SERVER_SCHEMA.$schema,
|
|
69
|
+
$ref: `#/$defs/${name}`,
|
|
70
|
+
$defs: APP_SERVER_SCHEMA.$defs,
|
|
71
|
+
};
|
|
72
|
+
definitionSchemas.set(name, definitionSchema);
|
|
73
|
+
}
|
|
74
|
+
const result = validate(value, definitionSchema);
|
|
75
|
+
return Object.freeze({
|
|
76
|
+
ok: result.valid,
|
|
77
|
+
errors: Object.freeze(
|
|
78
|
+
result.errors.map((error) =>
|
|
79
|
+
Object.freeze({
|
|
80
|
+
path: error.instancePath || error.schemaPath || "#",
|
|
81
|
+
message: error.message || error.code || "invalid value",
|
|
82
|
+
}),
|
|
83
|
+
),
|
|
84
|
+
),
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function validateApprovalDecision(value) {
|
|
89
|
+
return validateAppServerDefinition("ApprovalDecision", value);
|
|
90
|
+
}
|
|
91
|
+
|
|
56
92
|
export function rpcResult(id, result) {
|
|
57
93
|
return { jsonrpc: "2.0", id, result: result ?? null };
|
|
58
94
|
}
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
rpcError,
|
|
13
13
|
rpcNotification,
|
|
14
14
|
rpcResult,
|
|
15
|
+
validateApprovalDecision,
|
|
15
16
|
validateAppServerMessage,
|
|
16
17
|
} from "./protocol.js";
|
|
17
18
|
|
|
@@ -746,28 +747,24 @@ export class CcAppServer {
|
|
|
746
747
|
? event.risk
|
|
747
748
|
: "high",
|
|
748
749
|
reason: event.reason || "The Agent Kernel requires approval",
|
|
749
|
-
requestedPermissions:
|
|
750
|
+
requestedPermissions: Array.isArray(event.requested_permissions)
|
|
751
|
+
? event.requested_permissions
|
|
752
|
+
: [],
|
|
750
753
|
};
|
|
751
754
|
}
|
|
752
755
|
|
|
753
756
|
async _requestApproval(turn, event) {
|
|
754
757
|
const request = this._approvalRequest(turn, event);
|
|
755
758
|
const result = await this._requestClient("approval/decide", { request });
|
|
756
|
-
const
|
|
757
|
-
|
|
758
|
-
"acceptOnce",
|
|
759
|
-
"acceptForTurn",
|
|
760
|
-
"acceptForSession",
|
|
761
|
-
"decline",
|
|
762
|
-
"cancel",
|
|
763
|
-
]);
|
|
764
|
-
if (!allowed.has(decision.kind)) {
|
|
759
|
+
const validation = validateApprovalDecision(result);
|
|
760
|
+
if (!validation.ok) {
|
|
765
761
|
throw new JsonRpcError(
|
|
766
762
|
JSON_RPC_ERROR.INVALID_PARAMS,
|
|
767
763
|
"client returned an invalid approval decision",
|
|
764
|
+
{ errors: validation.errors },
|
|
768
765
|
);
|
|
769
766
|
}
|
|
770
|
-
return
|
|
767
|
+
return result;
|
|
771
768
|
}
|
|
772
769
|
|
|
773
770
|
async _completeTurn(turn, status, result) {
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { validateAppServerDefinition } from "./app-server/protocol.js";
|
|
3
|
+
|
|
4
|
+
export const APPROVAL_GRANTS_EVENT = "approval_grants";
|
|
5
|
+
export const APPROVAL_GRANTS_SCHEMA = "chainlesschain.approval-grants/v1";
|
|
6
|
+
|
|
7
|
+
function stableValue(value) {
|
|
8
|
+
if (Array.isArray(value)) return value.map(stableValue);
|
|
9
|
+
if (!value || typeof value !== "object") return value;
|
|
10
|
+
return Object.fromEntries(
|
|
11
|
+
Object.keys(value)
|
|
12
|
+
.filter((key) => value[key] !== undefined)
|
|
13
|
+
.sort()
|
|
14
|
+
.map((key) => [key, stableValue(value[key])]),
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function stableJson(value) {
|
|
19
|
+
return JSON.stringify(stableValue(value));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function boundedScope(value) {
|
|
23
|
+
const serialized = stableJson(value);
|
|
24
|
+
if (serialized.length <= 1024) return serialized;
|
|
25
|
+
return `sha256:${createHash("sha256").update(serialized).digest("hex")}`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function boundedCapability(tool) {
|
|
29
|
+
const candidate = `tool:${String(tool || "unknown")}`;
|
|
30
|
+
if (candidate.length <= 128) return candidate;
|
|
31
|
+
return `tool:sha256:${createHash("sha256").update(candidate).digest("hex")}`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function approvalPermissionForContext(ctx = {}, { cwd = null } = {}) {
|
|
35
|
+
const tool = ctx.tool || ctx.toolName || ctx.name || "unknown";
|
|
36
|
+
const args =
|
|
37
|
+
ctx.args ?? (ctx.command != null ? { command: ctx.command } : null);
|
|
38
|
+
const policy = ctx.rule || ctx.riskLevel || ctx.risk || null;
|
|
39
|
+
return Object.freeze({
|
|
40
|
+
capability: boundedCapability(tool),
|
|
41
|
+
scope: boundedScope({ cwd: cwd || ctx.cwd || null, args, policy }),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function permissionKey(permission) {
|
|
46
|
+
return `${permission.capability}\0${permission.scope}`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function livePermission(permission, now) {
|
|
50
|
+
const validation = validateAppServerDefinition("PermissionGrant", permission);
|
|
51
|
+
if (!validation.ok) return false;
|
|
52
|
+
return !permission.expiresAt || Date.parse(permission.expiresAt) > now;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function samePermission(left, right) {
|
|
56
|
+
return permissionKey(left) === permissionKey(right);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function normalizePersistedEntry(entry, now) {
|
|
60
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) return null;
|
|
61
|
+
if (!livePermission(entry.permission, now)) return null;
|
|
62
|
+
if (
|
|
63
|
+
typeof entry.binding !== "string" ||
|
|
64
|
+
!entry.binding ||
|
|
65
|
+
typeof entry.grantedAt !== "string" ||
|
|
66
|
+
Number.isNaN(Date.parse(entry.grantedAt))
|
|
67
|
+
) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
return Object.freeze({
|
|
71
|
+
permission: Object.freeze({ ...entry.permission }),
|
|
72
|
+
binding: entry.binding,
|
|
73
|
+
grantedAt: entry.grantedAt,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export class ApprovalGrantLedger {
|
|
78
|
+
constructor({ sessionId, now = () => Date.now() } = {}) {
|
|
79
|
+
if (!sessionId) throw new TypeError("approval grant sessionId is required");
|
|
80
|
+
this.sessionId = String(sessionId);
|
|
81
|
+
this.now = now;
|
|
82
|
+
this.turnId = null;
|
|
83
|
+
this.turnGrants = new Map();
|
|
84
|
+
this.sessionGrants = new Map();
|
|
85
|
+
this.revision = 0;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
beginTurn(turnId) {
|
|
89
|
+
this.turnId = String(turnId);
|
|
90
|
+
this.turnGrants.clear();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
allows(permission) {
|
|
94
|
+
const now = this.now();
|
|
95
|
+
const key = permissionKey(permission);
|
|
96
|
+
for (const grants of [this.turnGrants, this.sessionGrants]) {
|
|
97
|
+
const entry = grants.get(key);
|
|
98
|
+
if (!entry) continue;
|
|
99
|
+
if (livePermission(entry.permission, now)) return true;
|
|
100
|
+
grants.delete(key);
|
|
101
|
+
}
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
applyDecision(decision, requiredPermission, binding) {
|
|
106
|
+
if (
|
|
107
|
+
decision?.kind !== "acceptForTurn" &&
|
|
108
|
+
decision?.kind !== "acceptForSession"
|
|
109
|
+
) {
|
|
110
|
+
return Object.freeze({
|
|
111
|
+
decision,
|
|
112
|
+
granted: Object.freeze([]),
|
|
113
|
+
persistedScope: false,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
const candidates =
|
|
117
|
+
decision.permissions === undefined
|
|
118
|
+
? [requiredPermission]
|
|
119
|
+
: decision.permissions;
|
|
120
|
+
const target =
|
|
121
|
+
decision.kind === "acceptForSession"
|
|
122
|
+
? this.sessionGrants
|
|
123
|
+
: this.turnGrants;
|
|
124
|
+
const granted = candidates
|
|
125
|
+
.filter(
|
|
126
|
+
(permission) =>
|
|
127
|
+
livePermission(permission, this.now()) &&
|
|
128
|
+
samePermission(permission, requiredPermission) &&
|
|
129
|
+
(target.has(permissionKey(permission)) || target.size < 64),
|
|
130
|
+
)
|
|
131
|
+
.slice(0, 1)
|
|
132
|
+
.map((permission) => Object.freeze({ ...permission }));
|
|
133
|
+
const grantedAt = new Date(this.now()).toISOString();
|
|
134
|
+
for (const permission of granted) {
|
|
135
|
+
target.set(
|
|
136
|
+
permissionKey(permission),
|
|
137
|
+
Object.freeze({ permission, binding, grantedAt }),
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
if (granted.length) this.revision += 1;
|
|
141
|
+
return Object.freeze({
|
|
142
|
+
decision: Object.freeze({ ...decision, permissions: granted }),
|
|
143
|
+
granted: Object.freeze(granted),
|
|
144
|
+
persistedScope:
|
|
145
|
+
decision.kind === "acceptForSession" && granted.length > 0,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
toJSON() {
|
|
150
|
+
return {
|
|
151
|
+
schema: APPROVAL_GRANTS_SCHEMA,
|
|
152
|
+
sessionId: this.sessionId,
|
|
153
|
+
revision: this.revision,
|
|
154
|
+
grants: [...this.sessionGrants.values()].map((entry) => ({
|
|
155
|
+
permission: { ...entry.permission },
|
|
156
|
+
binding: entry.binding,
|
|
157
|
+
grantedAt: entry.grantedAt,
|
|
158
|
+
})),
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
static fromJSON(value, { sessionId, now = () => Date.now() } = {}) {
|
|
163
|
+
const ledger = new ApprovalGrantLedger({ sessionId, now });
|
|
164
|
+
if (
|
|
165
|
+
!value ||
|
|
166
|
+
typeof value !== "object" ||
|
|
167
|
+
Array.isArray(value) ||
|
|
168
|
+
value.schema !== APPROVAL_GRANTS_SCHEMA ||
|
|
169
|
+
value.sessionId !== ledger.sessionId ||
|
|
170
|
+
!Number.isSafeInteger(value.revision) ||
|
|
171
|
+
value.revision < 0 ||
|
|
172
|
+
!Array.isArray(value.grants) ||
|
|
173
|
+
value.grants.length > 64
|
|
174
|
+
) {
|
|
175
|
+
throw new TypeError("invalid persisted approval grant ledger");
|
|
176
|
+
}
|
|
177
|
+
for (const candidate of value.grants) {
|
|
178
|
+
const entry = normalizePersistedEntry(candidate, now());
|
|
179
|
+
if (!entry) {
|
|
180
|
+
throw new TypeError("invalid persisted approval grant entry");
|
|
181
|
+
}
|
|
182
|
+
ledger.sessionGrants.set(permissionKey(entry.permission), entry);
|
|
183
|
+
}
|
|
184
|
+
ledger.revision = value.revision;
|
|
185
|
+
return ledger;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
@@ -52,6 +52,7 @@ import {
|
|
|
52
52
|
resolveRemoteControlWsUrl,
|
|
53
53
|
} from "./remote-control.js";
|
|
54
54
|
import { logger } from "./logger.js";
|
|
55
|
+
import { normalizeRemoteApprovalDecision } from "./remote-approval-decision.js";
|
|
55
56
|
|
|
56
57
|
const DEFAULT_DECISION_TIMEOUT_MS = 5 * 60 * 1000;
|
|
57
58
|
const REMOTE_LEASE_AUTHORIZATION_KIND =
|
|
@@ -411,24 +412,19 @@ export class RemoteApprovalBridge {
|
|
|
411
412
|
// The device must echo the complete durable capability tuple. Missing,
|
|
412
413
|
// mismatched, stale, duplicate, or expired resolutions never settle an
|
|
413
414
|
// approval; only the store's successful CAS can return approved=true.
|
|
414
|
-
const
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
rawAnswer === "false" ||
|
|
420
|
-
rawAnswer === "yes" ||
|
|
421
|
-
rawAnswer === "no";
|
|
422
|
-
if (!hasDecision) {
|
|
415
|
+
const remoteDecision = normalizeRemoteApprovalDecision(message.event, {
|
|
416
|
+
requireCanonical:
|
|
417
|
+
message.memberCapabilities?.includes("approval-decision-v1") === true,
|
|
418
|
+
});
|
|
419
|
+
if (!remoteDecision.ok) {
|
|
423
420
|
this._recordSecurityError({
|
|
424
421
|
action: "approval.resolve",
|
|
425
422
|
requestId,
|
|
426
|
-
reason:
|
|
423
|
+
reason: remoteDecision.reason,
|
|
427
424
|
});
|
|
428
425
|
return;
|
|
429
426
|
}
|
|
430
|
-
const decision =
|
|
431
|
-
rawAnswer === true || rawAnswer === "true" || rawAnswer === "yes";
|
|
427
|
+
const decision = remoteDecision.approved;
|
|
432
428
|
let ackedLease = null;
|
|
433
429
|
// The server creates the lease before forwarding the resolve frame. Keep a
|
|
434
430
|
// strictly request-bound cancellation handle even when durable adoption of
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { validateApprovalDecision } from "./app-server/protocol.js";
|
|
2
|
+
|
|
3
|
+
export const REMOTE_APPROVAL_DECISION_CAPABILITY = "approval-decision-v1";
|
|
4
|
+
|
|
5
|
+
const LEGACY_TRUE = new Set([true, "true", "yes"]);
|
|
6
|
+
const LEGACY_FALSE = new Set([false, "false", "no"]);
|
|
7
|
+
|
|
8
|
+
function parseLegacyValue(value) {
|
|
9
|
+
if (LEGACY_TRUE.has(value)) return true;
|
|
10
|
+
if (LEGACY_FALSE.has(value)) return false;
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function invalid(reason) {
|
|
15
|
+
return Object.freeze({ ok: false, reason });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Normalize a remote approval response at the transport boundary.
|
|
20
|
+
*
|
|
21
|
+
* New clients send a canonical ApprovalDecision plus the N-1 boolean
|
|
22
|
+
* projection. Legacy clients may still send only answer/approved. Remote
|
|
23
|
+
* binary approval UIs cannot safely review scoped grants, so only acceptOnce
|
|
24
|
+
* and decline are admitted here.
|
|
25
|
+
*/
|
|
26
|
+
export function normalizeRemoteApprovalDecision(
|
|
27
|
+
event,
|
|
28
|
+
{ requireCanonical = false } = {},
|
|
29
|
+
) {
|
|
30
|
+
if (!event || typeof event !== "object" || Array.isArray(event)) {
|
|
31
|
+
return invalid("decision-required");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const hasCanonical = Object.prototype.hasOwnProperty.call(event, "decision");
|
|
35
|
+
let canonicalApproved = null;
|
|
36
|
+
let canonicalKind = null;
|
|
37
|
+
if (hasCanonical) {
|
|
38
|
+
const validation = validateApprovalDecision(event.decision);
|
|
39
|
+
if (!validation.ok) return invalid("canonical-decision-invalid");
|
|
40
|
+
canonicalKind = event.decision.kind;
|
|
41
|
+
if (canonicalKind === "acceptOnce") canonicalApproved = true;
|
|
42
|
+
else if (canonicalKind === "decline") canonicalApproved = false;
|
|
43
|
+
else return invalid("remote-decision-kind-not-supported");
|
|
44
|
+
} else if (requireCanonical) {
|
|
45
|
+
return invalid("canonical-decision-required");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const legacyValues = [];
|
|
49
|
+
for (const field of ["answer", "approved"]) {
|
|
50
|
+
const value = event[field];
|
|
51
|
+
// Preserve the old `answer ?? approved` behavior for a null answer.
|
|
52
|
+
if (value === null || value === undefined) continue;
|
|
53
|
+
const parsed = parseLegacyValue(value);
|
|
54
|
+
if (parsed === null) return invalid("legacy-decision-invalid");
|
|
55
|
+
legacyValues.push(parsed);
|
|
56
|
+
}
|
|
57
|
+
if (legacyValues.some((value) => value !== legacyValues[0])) {
|
|
58
|
+
return invalid("legacy-decision-conflict");
|
|
59
|
+
}
|
|
60
|
+
const legacyApproved = legacyValues.length > 0 ? legacyValues[0] : null;
|
|
61
|
+
|
|
62
|
+
if (canonicalApproved === null && legacyApproved === null) {
|
|
63
|
+
return invalid("decision-required");
|
|
64
|
+
}
|
|
65
|
+
if (
|
|
66
|
+
canonicalApproved !== null &&
|
|
67
|
+
legacyApproved !== null &&
|
|
68
|
+
canonicalApproved !== legacyApproved
|
|
69
|
+
) {
|
|
70
|
+
return invalid("canonical-legacy-decision-conflict");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return Object.freeze({
|
|
74
|
+
ok: true,
|
|
75
|
+
approved: canonicalApproved ?? legacyApproved,
|
|
76
|
+
kind: canonicalKind || (legacyApproved === true ? "acceptOnce" : "decline"),
|
|
77
|
+
canonical: hasCanonical,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function requireRemoteApprovalDecision(event, options) {
|
|
82
|
+
const result = normalizeRemoteApprovalDecision(event, options);
|
|
83
|
+
if (!result.ok) {
|
|
84
|
+
throw new TypeError(`Invalid remote approval decision: ${result.reason}`);
|
|
85
|
+
}
|
|
86
|
+
return result;
|
|
87
|
+
}
|