chainlesschain 0.166.86 → 0.166.88
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 +26 -15
- package/package.json +1 -1
- package/src/assets/linux-subreaper/linux-arm64/manifest.json +1 -1
- package/src/assets/linux-subreaper/linux-x64/manifest.json +1 -1
- package/src/assets/web-panel/assets/{AIOps-DZwiXAIh.js → AIOps-QzeGep9J.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-aNzXoIJP.js → ActionButton-Dfc5bx7S.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-ETQk4ZXt.js → Analytics-tn-_ZG2E.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-D5oEbeEC.js → AppLayout-Deg3WAK8.js} +3 -3
- package/src/assets/web-panel/assets/{Artifacts-ja48FAne.js → Artifacts-KU05t0a6.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-DlN0YnDt.js → Audit-CkSZf54T.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-CzvyNsRg.js → BackgroundAgents-i7ivfXF0.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-DXgEC2MQ.js → Backup-QcVUKtf0.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-D9XqejLx.js → BaseInput-B3h5ZJ0g.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-BTwuYUlN.js → Chat-D7AVz0xi.js} +5 -5
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-0anIC2Wn.js → ChatBubbleRenderer-B0-euPW7.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-CLuaQbDt.js → Checkbox-H0p0TMlD.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-KwQUZs8A.js → Codegen-B6HOmAY3.js} +1 -1
- package/src/assets/web-panel/assets/{Col-BCmROkzc.js → Col-n_64Fog2.js} +1 -1
- package/src/assets/web-panel/assets/{Community-CXlUEHR4.js → Community-CX0JGgj0.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-Dzx-lWI2.js → Compact-C4JWUZlx.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-CfXW82FF.js → Compliance-DKyTZakB.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-DIISTRm2.js → Cowork-C0joBMci.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-nyJ-KcFE.js → Cron-D6VkYpxK.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-Dtv1E4cV.js → Crosschain-7b6Uu8jc.js} +1 -1
- package/src/assets/web-panel/assets/{DID-BCTpektS.js → DID-C8YPKqzY.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-C1Q_m0So.js → Dashboard-DJsOiEry.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-BrQ5_PPL.js → Dropdown-CyStFY-4.js} +1 -1
- package/src/assets/web-panel/assets/EmailListRenderer-COrIx2Rg.js +1 -0
- package/src/assets/web-panel/assets/{EvolutionSettings-DaGaE6bX.js → EvolutionSettings-DpxORusH.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-nJDWBECt.js → FamilyGuardDashboard-5BYZdMG0.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-DhMUY_wG.js → Federation-s1kS0ekU.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-CbONJPuc.js → FormItemContext-CmgoH9HV.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-CDdX4dX-.js → GenericCardRenderer-DSRWrvcL.js} +1 -1
- package/src/assets/web-panel/assets/{Git-DXbDVJdR.js → Git-Cz3EgnS1.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-DbAfAvs1.js → Governance-De4atTe7.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-Dy-fGea2.js → Inference-DoitBBxJ.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DAxRpGUF.js → KnowledgeGraph-CIdmJ1pH.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-5L1UCR8X.js → Logs-Coa_qNhU.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-u0FPafvT.js → MarkdownRenderer-CeT_RocE.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-DUk_McDG.js → Marketplace-C6kbAQoM.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-B6PNsZ_p.js → McpTools-C2TZn8D-.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-BUUs8j5F.js → Memory-DYdEKDuB.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-pXRsgyuO.js → MobileBridge-pKLeVviZ.js} +1 -1
- package/src/assets/web-panel/assets/MobileProjects-D4bWcHA_.js +1 -0
- package/src/assets/web-panel/assets/{Mtc-Deaj4faB.js → Mtc-IEoVRV29.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-wUV0VUH1.js → MtcAudit-Cov-yYTY.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-C67IIkts.js → Multisig--Jr7OCmp.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-BCA_CWrW.js → NLProgramming-D2gXnYAA.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-D9eu14hQ.js → Notes-hk8pMyve.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-Cnd-J4LM.js → NotificationSettings-D26Nc7MU.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-C4pHUuyZ.js → OrderTableRenderer-DmzHMEOa.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-4YExAbnp.js → Organization-Cn-o6mHT.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-BF-wFuZz.js → Overflow-DP6xpDak.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-CdiajqCT.js → P2P-C9WAjG-I.js} +2 -2
- package/src/assets/web-panel/assets/PdhVaultBrowser-DxZb0Ux8.js +7 -0
- package/src/assets/web-panel/assets/{Permissions-DrZ4D78_.js → Permissions-BFBN4-IL.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-lDc6OXjz.js → PersonalDataHub-BEDvoAiz.js} +6 -6
- package/src/assets/web-panel/assets/{Pipeline-DqDS9ON1.js → Pipeline-Co2Ze_rR.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-BMekZRZu.js → Privacy-BOTn1EId.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-LYS7NFvJ.js → ProjectInit-CObyrZYm.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-CcJNHk9A.js → ProjectSettings-DVTN31rd.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-DrfAHgdF.js → Projects-DQSEaVjE.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-aqsly9SR.js → Providers-BRIKWIdQ.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-ZWvR3zqg.js → QrScannerModal-3nxaEXxN.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-BT5CgsvW.js → QuickAsk-iwZJEE9z.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-Cz8Dz2RN.js → Recommend-D2_EEbvc.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-BlxPVbtQ.js → RemoteSession-S4w2mUfq.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-B3VGBE-1.js → Reputation-CstUvfTC.js} +1 -1
- package/src/assets/web-panel/assets/{Row-Bwv7vSEA.js → Row-DEGHFpxz.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-DHI085r_.js → RssFeed-DGdZtJQl.js} +2 -2
- package/src/assets/web-panel/assets/{Search-DBler5T0.js → Search-DhfXi99U.js} +1 -1
- package/src/assets/web-panel/assets/{Security-BZdw43NW.js → Security-BbzISrgX.js} +4 -4
- package/src/assets/web-panel/assets/{Services-Cslk5lUj.js → Services-CFjP2sQ3.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-Bur-C-25.js → Skeleton-CFn5wbvm.js} +1 -1
- package/src/assets/web-panel/assets/Skills-fVbT8RcW.js +1 -0
- package/src/assets/web-panel/assets/{Sla-B1OWgQ51.js → Sla-D6OtC0MT.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-CaBcEaV4.js → SpeechSettings-D0kteBjU.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-CBbG8L6J.js → SyncSettings-BGEWCtPh.js} +2 -2
- package/src/assets/web-panel/assets/Tasks-B1xZUG4D.js +1 -0
- package/src/assets/web-panel/assets/{Templates-VBBOZ1H5.js → Templates-COlorjqb.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-CtQTj7IF.js → Tenant-yLkwfuo3.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-DXVTQjnl.js → Terminal-CAmkq1wf.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-BRIno0aN.js → TimelineRenderer-CcKYq1Ue.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-uiiAfkYN.js → Tokens-9hIqn_LE.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-CPApN8o6.js → Trigger-Dsdkb74C.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-T2jKVwCE.js → Trust-DC4GuE3C.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-C2U_gbzP.js → UkeySign-CSlh00wf.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-BCdlr3Kj.js → VideoEditing-QEWLo3QY.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-CWnjoSc1.js → Wallet-BkPNQgmg.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-CqSbYqHq.js → WebAuthn-7---jCBl.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-Dr_I3jcr.js → WorkflowEditor-Dq6Yiu8E.js} +1 -1
- package/src/assets/web-panel/assets/{chat-Cg21FKdE.js → chat-CTYIMpWD.js} +1 -1
- package/src/assets/web-panel/assets/{colors-CK85tY9e.js → colors-C8JtYlDN.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-m-XYPC7W.js → compact-item-spXpnsNC.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-BmiEeNvs.js → createContext-D04ewPuL.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-3-s4adfp.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-CCw4KAoc.js → hasIn-DZblOKXx.js} +1 -1
- package/src/assets/web-panel/assets/{index-Br9t_I1X.js → index-749P1pkM.js} +1 -1
- package/src/assets/web-panel/assets/{index-BvjC3t35.js → index-B-cKXztT.js} +1 -1
- package/src/assets/web-panel/assets/{index-BiVPFsat.js → index-B57W1RB6.js} +1 -1
- package/src/assets/web-panel/assets/{index-8KzhlvJ9.js → index-B7RA974g.js} +1 -1
- package/src/assets/web-panel/assets/{index-OIbfsi-o.js → index-BKfR2eSQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BGb1j2z9.js → index-BLv-KSGc.js} +1 -1
- package/src/assets/web-panel/assets/{index-yYmjkf38.js → index-BOuUUW2U.js} +3 -3
- package/src/assets/web-panel/assets/{index-J81uvUMN.js → index-BSO33_O2.js} +1 -1
- package/src/assets/web-panel/assets/{index-DI9GJFcn.js → index-B_65aY6c.js} +1 -1
- package/src/assets/web-panel/assets/{index-BHsiSoGY.js → index-BjMY3Y_L.js} +1 -1
- package/src/assets/web-panel/assets/{index-6jXf-Lln.js → index-Bm0z1M6s.js} +1 -1
- package/src/assets/web-panel/assets/{index-BGUYgc96.js → index-Bw-3CtK2.js} +1 -1
- package/src/assets/web-panel/assets/{index-sWUmvT8H.js → index-CE3TYhl0.js} +1 -1
- package/src/assets/web-panel/assets/{index-CN2xbLGz.js → index-CHtwoTKX.js} +1 -1
- package/src/assets/web-panel/assets/{index-D7gS_TJl.js → index-CLkua5sJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BStyjmLG.js → index-COvZnoIg.js} +1 -1
- package/src/assets/web-panel/assets/{index-DmSgfXuz.js → index-CZmBlh5P.js} +1 -1
- package/src/assets/web-panel/assets/{index-lhoexBDH.js → index-Cfk4o9eI.js} +1 -1
- package/src/assets/web-panel/assets/{index-DMbrl43d.js → index-Cgztj1JO.js} +1 -1
- package/src/assets/web-panel/assets/{index-DxRhnsEC.js → index-Cz0gkRz_.js} +1 -1
- package/src/assets/web-panel/assets/{index-DAlGVix-.js → index-DHOV24VR.js} +1 -1
- package/src/assets/web-panel/assets/{index-DIlGxS2W.js → index-DLUHbPl3.js} +1 -1
- package/src/assets/web-panel/assets/{index-CYEzWoyZ.js → index-DSoyEHi2.js} +1 -1
- package/src/assets/web-panel/assets/{index-CBcgIWOe.js → index-D_Md0Jhp.js} +1 -1
- package/src/assets/web-panel/assets/{index-D1sU5-XA.js → index-Dcd-qZ68.js} +1 -1
- package/src/assets/web-panel/assets/{index-B8IC9ZeZ.js → index-DcjouIsi.js} +1 -1
- package/src/assets/web-panel/assets/{index-ByVvAXdl.js → index-DdC0OsNh.js} +1 -1
- package/src/assets/web-panel/assets/{index-BtvVupUL.js → index-DruABY-g.js} +1 -1
- package/src/assets/web-panel/assets/{index-IskAKXhx.js → index-Dssnz_1x.js} +1 -1
- package/src/assets/web-panel/assets/{index-DpIy2HzE.js → index-DtSzmzFF.js} +1 -1
- package/src/assets/web-panel/assets/{index-BIOTRzaF.js → index-DwIEfKo7.js} +1 -1
- package/src/assets/web-panel/assets/{index-B7xJkhtJ.js → index-EM4_wLPi.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bv90WEWa.js → index-IFEbaISP.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cf5arJie.js → index-KIoJ2eCK.js} +1 -1
- package/src/assets/web-panel/assets/{index-BUYyhYP6.js → index-Mxj-qi1G.js} +1 -1
- package/src/assets/web-panel/assets/index-ZMf_BGlO.js +1 -0
- package/src/assets/web-panel/assets/index-jvp8bJBF.js +1 -0
- package/src/assets/web-panel/assets/{index-Br0ZNdu1.js → index-rU6_An6N.js} +1 -1
- package/src/assets/web-panel/assets/{index-BcfVHmFK.js → index-rVEqWa0_.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-Cc1XOmNe.js → initDefaultProps-Dl0wiMxn.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BehsX88u.js → motion-BFmZYrGO.js} +1 -1
- package/src/assets/web-panel/assets/{move-BPx2YsqY.js → move-Cjs64yWq.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-BSFJgFQA.js → mtc-parser-NeQsnBuP.js} +1 -1
- package/src/assets/web-panel/assets/{omit-DBN5KUeo.js → omit-4Rs-rfs9.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-BDK1yZ-9.js → pickAttrs-CUhx2rDY.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-C4rMmKNJ.js → placementArrow-DbIM_OB2.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-BoeoOnxX.js → responsiveObserve-B_TGx9AN.js} +1 -1
- package/src/assets/web-panel/assets/{slide-BKlr1bl9.js → slide-C46cEP5t.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-Ky1apWPI.js → statusUtils-DqeksICX.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-DxxR7uXk.js → styleChecker-2rrypo5u.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-DmsUqVe9.js → useFlexGapSupport-RRe1Edsg.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-CwkG7sOg.js → useFs-CB0oxU1U.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-CddgeEfF.js → usePersonalDataHub-B98WdlUE.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-C_-H8i4Y.js → vnode-CY8AoD95.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-aAsHUMNE.js → zoom-BnT-0MbP.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +1 -1
- package/src/commands/eval.js +116 -67
- package/src/commands/memory.js +34 -15
- package/src/commands/sandbox.js +9 -0
- package/src/data/changelog.json +26 -0
- package/src/lib/causal-observability.js +6 -45
- package/src/lib/context-memory-kernel/durable-memory-port.js +49 -10
- package/src/lib/context-memory-kernel/memory-query-index.js +278 -0
- package/src/lib/context-memory-kernel/memory-service.js +17 -2
- package/src/lib/context-memory-kernel/segmented-memory-port.js +277 -9
- package/src/lib/eval/outcomes.js +20 -10
- package/src/lib/eval/runner.js +49 -5
- package/src/lib/eval/stream-terminal.js +51 -0
- package/src/lib/eval/tasks.js +8 -3
- package/src/lib/eval/verify01-contracts.js +349 -0
- package/src/lib/eval/verify01-execution.js +611 -0
- package/src/lib/eval/verify01-host-evidence.js +604 -0
- package/src/lib/execution-support.js +58 -0
- package/src/lib/llm-pricing.js +98 -11
- package/src/lib/model-catalog-review.js +116 -0
- package/src/lib/model-context-catalog.js +23 -1
- package/src/lib/openai-responses.js +39 -0
- package/src/lib/process-execution-broker/index.js +8 -0
- package/src/lib/process-execution-broker/linux-subreaper-process.js +24 -2
- package/src/lib/process-execution-broker/process-ownership-journal.js +168 -7
- package/src/lib/process-execution-broker/process-ownership-quarantine.js +56 -1
- package/src/lib/process-execution-broker/process-recovery-cgroup.js +272 -0
- package/src/runtime/agent-core.js +9 -24
- package/src/runtime/permission-authority-host.js +14 -0
- package/src/assets/web-panel/assets/EmailListRenderer-DjoNlONP.js +0 -1
- package/src/assets/web-panel/assets/MobileProjects-D7v-2Hwm.js +0 -1
- package/src/assets/web-panel/assets/PdhVaultBrowser-BkjfS6Th.js +0 -7
- package/src/assets/web-panel/assets/Skills-v0zgLy0X.js +0 -1
- package/src/assets/web-panel/assets/Tasks-C7EjXrnN.js +0 -1
- package/src/assets/web-panel/assets/devWarning-CkMEODFC.js +0 -1
- package/src/assets/web-panel/assets/index-BjE5qaKn.js +0 -1
- package/src/assets/web-panel/assets/index-CsFH-2Zk.js +0 -1
|
@@ -4,11 +4,17 @@ import { randomUUID } from "node:crypto";
|
|
|
4
4
|
import { withFileLock } from "../with-file-lock.js";
|
|
5
5
|
import { writeSecurityStore } from "../durable-security-store.js";
|
|
6
6
|
import { ensurePrivateDirectory } from "../secure-fs.js";
|
|
7
|
+
import { performance } from "node:perf_hooks";
|
|
8
|
+
import {
|
|
9
|
+
openRecoveryCgroup,
|
|
10
|
+
validateRecoveryCgroup,
|
|
11
|
+
} from "./process-recovery-cgroup.js";
|
|
7
12
|
|
|
8
13
|
export const PROCESS_OWNERSHIP_PENDING = "BROKER_PROCESS_OWNERSHIP_PENDING";
|
|
9
14
|
export const PROCESS_OWNERSHIP_JOURNAL_UNAVAILABLE =
|
|
10
15
|
"BROKER_PROCESS_OWNERSHIP_JOURNAL_UNAVAILABLE";
|
|
11
16
|
const SCHEMA = "chainlesschain.process-ownership-journal/v1";
|
|
17
|
+
const RECOVERY_SCHEMA = "chainlesschain.process-ownership-journal/v2";
|
|
12
18
|
const MAX_BYTES = 1024 * 1024;
|
|
13
19
|
const MAX_PENDING = 1024;
|
|
14
20
|
const UUID = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/;
|
|
@@ -27,8 +33,12 @@ function unavailable(cause) {
|
|
|
27
33
|
function validate(state) {
|
|
28
34
|
if (
|
|
29
35
|
!state ||
|
|
30
|
-
|
|
31
|
-
|
|
36
|
+
!(
|
|
37
|
+
(state.schema === SCHEMA &&
|
|
38
|
+
Object.keys(state).sort().join() === "pending,schema") ||
|
|
39
|
+
(state.schema === RECOVERY_SCHEMA &&
|
|
40
|
+
Object.keys(state).sort().join() === "pending,recoveries,schema")
|
|
41
|
+
) ||
|
|
32
42
|
!Array.isArray(state.pending) ||
|
|
33
43
|
state.pending.length > MAX_PENDING
|
|
34
44
|
)
|
|
@@ -37,7 +47,12 @@ function validate(state) {
|
|
|
37
47
|
for (const entry of state.pending) {
|
|
38
48
|
if (
|
|
39
49
|
!entry ||
|
|
40
|
-
|
|
50
|
+
![
|
|
51
|
+
"executionId,ownerPid,token",
|
|
52
|
+
...(state.schema === RECOVERY_SCHEMA
|
|
53
|
+
? ["executionId,ownerPid,recovery,token"]
|
|
54
|
+
: []),
|
|
55
|
+
].includes(Object.keys(entry).sort().join()) ||
|
|
41
56
|
!UUID.test(entry.executionId) ||
|
|
42
57
|
!UUID.test(entry.token) ||
|
|
43
58
|
!Number.isSafeInteger(entry.ownerPid) ||
|
|
@@ -45,8 +60,31 @@ function validate(state) {
|
|
|
45
60
|
ids.has(entry.executionId)
|
|
46
61
|
)
|
|
47
62
|
throw new Error("Invalid ownership journal entry");
|
|
63
|
+
if (Object.hasOwn(entry, "recovery"))
|
|
64
|
+
validateRecoveryCgroup(entry.recovery, entry.executionId);
|
|
48
65
|
ids.add(entry.executionId);
|
|
49
66
|
}
|
|
67
|
+
if (state.schema === RECOVERY_SCHEMA) {
|
|
68
|
+
if (
|
|
69
|
+
!Array.isArray(state.recoveries) ||
|
|
70
|
+
state.recoveries.length > MAX_PENDING
|
|
71
|
+
)
|
|
72
|
+
throw new Error("Invalid recovery audit inventory");
|
|
73
|
+
for (const receipt of state.recoveries) {
|
|
74
|
+
if (
|
|
75
|
+
!receipt ||
|
|
76
|
+
Object.keys(receipt).sort().join() !==
|
|
77
|
+
"completedAt,executionId,group,killIssued,populated,token" ||
|
|
78
|
+
!UUID.test(receipt.executionId) ||
|
|
79
|
+
!UUID.test(receipt.token) ||
|
|
80
|
+
receipt.killIssued !== true ||
|
|
81
|
+
receipt.populated !== false ||
|
|
82
|
+
!Number.isFinite(Date.parse(receipt.completedAt))
|
|
83
|
+
)
|
|
84
|
+
throw new Error("Invalid recovery audit receipt");
|
|
85
|
+
validateRecoveryCgroup(receipt.group, receipt.executionId);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
50
88
|
return state;
|
|
51
89
|
}
|
|
52
90
|
|
|
@@ -211,6 +249,13 @@ export class ProcessOwnershipJournal {
|
|
|
211
249
|
}
|
|
212
250
|
}
|
|
213
251
|
|
|
252
|
+
#write(file, state, label = "Process ownership") {
|
|
253
|
+
validate(state);
|
|
254
|
+
if (Buffer.byteLength(`${JSON.stringify(state, null, 2)}\n`) > MAX_BYTES)
|
|
255
|
+
throw new Error("Ownership journal byte capacity is exhausted");
|
|
256
|
+
writeSecurityStore(file, label, state);
|
|
257
|
+
}
|
|
258
|
+
|
|
214
259
|
assertAvailable() {
|
|
215
260
|
if (this.#fault) throw this.#fault;
|
|
216
261
|
this.#withDirectory(false, (file) => this.#assertState(this.#read(file)));
|
|
@@ -222,6 +267,9 @@ export class ProcessOwnershipJournal {
|
|
|
222
267
|
const state = this.#read(file);
|
|
223
268
|
return {
|
|
224
269
|
pendingExecutionIds: state.pending.map((entry) => entry.executionId),
|
|
270
|
+
recoverableExecutionIds: state.pending
|
|
271
|
+
.filter((entry) => entry.recovery)
|
|
272
|
+
.map((entry) => entry.executionId),
|
|
225
273
|
blocked: state.pending.some(
|
|
226
274
|
(entry) => this.#local.get(entry.executionId)?.token !== entry.token,
|
|
227
275
|
),
|
|
@@ -229,24 +277,34 @@ export class ProcessOwnershipJournal {
|
|
|
229
277
|
});
|
|
230
278
|
}
|
|
231
279
|
|
|
232
|
-
prepare(executionId) {
|
|
280
|
+
prepare(executionId, recovery = null) {
|
|
233
281
|
if (!UUID.test(executionId))
|
|
234
282
|
throw new TypeError("Invalid ownership execution id");
|
|
235
283
|
if (this.#fault) throw this.#fault;
|
|
236
|
-
|
|
284
|
+
if (recovery) validateRecoveryCgroup(recovery, executionId);
|
|
285
|
+
const entry = {
|
|
286
|
+
executionId,
|
|
287
|
+
ownerPid: process.pid,
|
|
288
|
+
token: randomUUID(),
|
|
289
|
+
...(recovery ? { recovery: { ...recovery } } : {}),
|
|
290
|
+
};
|
|
237
291
|
this.#withDirectory(true, (file) =>
|
|
238
292
|
withFileLock(
|
|
239
293
|
file,
|
|
240
294
|
() => {
|
|
241
295
|
const state = this.#read(file);
|
|
242
296
|
this.#assertState(state);
|
|
297
|
+
if (recovery && state.schema === SCHEMA) {
|
|
298
|
+
state.schema = RECOVERY_SCHEMA;
|
|
299
|
+
state.recoveries = [];
|
|
300
|
+
}
|
|
243
301
|
if (
|
|
244
302
|
state.pending.length >= MAX_PENDING ||
|
|
245
303
|
state.pending.some((item) => item.executionId === executionId)
|
|
246
304
|
)
|
|
247
305
|
throw new Error("Ownership journal admission limit");
|
|
248
306
|
state.pending.push(entry);
|
|
249
|
-
|
|
307
|
+
this.#write(file, state);
|
|
250
308
|
this.#initialized = true;
|
|
251
309
|
this.#local.set(executionId, entry);
|
|
252
310
|
},
|
|
@@ -284,7 +342,7 @@ export class ProcessOwnershipJournal {
|
|
|
284
342
|
state.pending = state.pending.filter(
|
|
285
343
|
(item) => item.executionId !== executionId,
|
|
286
344
|
);
|
|
287
|
-
|
|
345
|
+
this.#write(file, state);
|
|
288
346
|
this.#local.delete(executionId);
|
|
289
347
|
settled = true;
|
|
290
348
|
},
|
|
@@ -294,4 +352,107 @@ export class ProcessOwnershipJournal {
|
|
|
294
352
|
},
|
|
295
353
|
});
|
|
296
354
|
}
|
|
355
|
+
|
|
356
|
+
/** Explicitly cancel an orphaned, identity-bound cgroup. Old PID-only records
|
|
357
|
+
* remain quarantined. Neither process death nor age is a cleanup authority.
|
|
358
|
+
*/
|
|
359
|
+
async recover(executionId, { timeoutMs = 5000, pollMs = 10 } = {}) {
|
|
360
|
+
if (
|
|
361
|
+
!UUID.test(executionId) ||
|
|
362
|
+
!Number.isSafeInteger(timeoutMs) ||
|
|
363
|
+
timeoutMs < 1 ||
|
|
364
|
+
timeoutMs > 30000 ||
|
|
365
|
+
!Number.isSafeInteger(pollMs) ||
|
|
366
|
+
pollMs < 1 ||
|
|
367
|
+
pollMs > 1000
|
|
368
|
+
)
|
|
369
|
+
throw new TypeError("Invalid process recovery request");
|
|
370
|
+
if (this.#fault) throw this.#fault;
|
|
371
|
+
if (this.#local.has(executionId))
|
|
372
|
+
throw unavailable(
|
|
373
|
+
new Error(
|
|
374
|
+
"A locally owned live launch must use its normal cancellation fence",
|
|
375
|
+
),
|
|
376
|
+
);
|
|
377
|
+
const entry = this.#withDirectory(false, (file) => {
|
|
378
|
+
const state = this.#read(file);
|
|
379
|
+
const found = state.pending.find(
|
|
380
|
+
(item) => item.executionId === executionId,
|
|
381
|
+
);
|
|
382
|
+
if (!found?.recovery) {
|
|
383
|
+
const error = new Error(
|
|
384
|
+
"This pending execution has no recoverable kernel group identity",
|
|
385
|
+
);
|
|
386
|
+
error.code = PROCESS_OWNERSHIP_PENDING;
|
|
387
|
+
error.recoveryRequired = true;
|
|
388
|
+
throw error;
|
|
389
|
+
}
|
|
390
|
+
if (state.recoveries.length >= MAX_PENDING)
|
|
391
|
+
throw new Error("Recovery audit capacity is exhausted");
|
|
392
|
+
return { ...found, recovery: { ...found.recovery } };
|
|
393
|
+
});
|
|
394
|
+
const group = openRecoveryCgroup(entry.recovery, executionId);
|
|
395
|
+
let settled = false;
|
|
396
|
+
try {
|
|
397
|
+
group.kill();
|
|
398
|
+
const deadline = performance.now() + timeoutMs;
|
|
399
|
+
while (group.populated()) {
|
|
400
|
+
if (performance.now() >= deadline) {
|
|
401
|
+
const error = new Error(
|
|
402
|
+
"Recovered process group has not reached its empty kernel fence",
|
|
403
|
+
);
|
|
404
|
+
error.code = PROCESS_OWNERSHIP_PENDING;
|
|
405
|
+
error.recoveryRequired = true;
|
|
406
|
+
throw error;
|
|
407
|
+
}
|
|
408
|
+
await new Promise((resolve) => setTimeout(resolve, pollMs));
|
|
409
|
+
}
|
|
410
|
+
const receipt = {
|
|
411
|
+
executionId,
|
|
412
|
+
token: entry.token,
|
|
413
|
+
group: entry.recovery,
|
|
414
|
+
killIssued: true,
|
|
415
|
+
populated: false,
|
|
416
|
+
completedAt: new Date().toISOString(),
|
|
417
|
+
};
|
|
418
|
+
this.#withDirectory(false, (file) =>
|
|
419
|
+
withFileLock(
|
|
420
|
+
file,
|
|
421
|
+
() => {
|
|
422
|
+
const state = this.#read(file);
|
|
423
|
+
const current = state.pending.find(
|
|
424
|
+
(item) => item.executionId === executionId,
|
|
425
|
+
);
|
|
426
|
+
if (
|
|
427
|
+
!current ||
|
|
428
|
+
current.token !== entry.token ||
|
|
429
|
+
JSON.stringify(current.recovery) !==
|
|
430
|
+
JSON.stringify(entry.recovery)
|
|
431
|
+
)
|
|
432
|
+
throw new Error("Ownership recovery lost its original record");
|
|
433
|
+
if (state.recoveries.length >= MAX_PENDING)
|
|
434
|
+
throw new Error("Recovery audit capacity is exhausted");
|
|
435
|
+
// Check the kernel fence again while committing the durable receipt.
|
|
436
|
+
group.confirmEmpty();
|
|
437
|
+
state.pending = state.pending.filter(
|
|
438
|
+
(item) => item.executionId !== executionId,
|
|
439
|
+
);
|
|
440
|
+
state.recoveries.push(receipt);
|
|
441
|
+
this.#write(file, state, "Process ownership recovery");
|
|
442
|
+
settled = true;
|
|
443
|
+
},
|
|
444
|
+
{ failIfUnavailable: true, timeoutMs: 2000 },
|
|
445
|
+
),
|
|
446
|
+
);
|
|
447
|
+
return { ...receipt, cleanupConfirmed: true, executionResumed: false };
|
|
448
|
+
} finally {
|
|
449
|
+
// Publish settlement before removing the empty group. If journal IO
|
|
450
|
+
// fails, its kernel identity remains available for a safe retry.
|
|
451
|
+
try {
|
|
452
|
+
group.close({ remove: settled });
|
|
453
|
+
} catch {
|
|
454
|
+
group.close(); /* Empty directory leftovers never certify a run. */
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
297
458
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { getMachineSecurityAnchorDir } from "../paths.js";
|
|
3
3
|
import { ProcessOwnershipJournal } from "./process-ownership-journal.js";
|
|
4
|
+
import { prepareRecoveryCgroup } from "./process-recovery-cgroup.js";
|
|
4
5
|
|
|
5
6
|
// Shared by every Broker in this JS runtime. A new Broker, different cwd or
|
|
6
7
|
// cleared audit history cannot turn a lost process owner into cleanup evidence.
|
|
@@ -18,7 +19,60 @@ function durableJournal() {
|
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
export function prepareProcessOwnership(executionId) {
|
|
21
|
-
|
|
22
|
+
const authority = durableJournal();
|
|
23
|
+
if (!authority) return null;
|
|
24
|
+
const group = prepareRecoveryCgroup(
|
|
25
|
+
executionId,
|
|
26
|
+
process.env.CHAINLESSCHAIN_PROCESS_RECOVERY_CGROUP_ROOT,
|
|
27
|
+
);
|
|
28
|
+
if (!group) return authority.prepare(executionId);
|
|
29
|
+
let lease;
|
|
30
|
+
try {
|
|
31
|
+
lease = authority.prepare(executionId, group.identity);
|
|
32
|
+
} catch (error) {
|
|
33
|
+
group.close({ remove: true });
|
|
34
|
+
throw error;
|
|
35
|
+
}
|
|
36
|
+
let settled = false,
|
|
37
|
+
retained = false;
|
|
38
|
+
return Object.freeze({
|
|
39
|
+
beforeLaunch: (proc) => group.attachBeforeLaunch(proc),
|
|
40
|
+
settle() {
|
|
41
|
+
if (settled) return;
|
|
42
|
+
if (retained)
|
|
43
|
+
throw new Error("Retained recovery group requires explicit recovery");
|
|
44
|
+
try {
|
|
45
|
+
group.confirmEmpty();
|
|
46
|
+
lease.settle();
|
|
47
|
+
} catch (error) {
|
|
48
|
+
retained = true;
|
|
49
|
+
lease.retain();
|
|
50
|
+
group.close();
|
|
51
|
+
throw error;
|
|
52
|
+
}
|
|
53
|
+
settled = true;
|
|
54
|
+
try {
|
|
55
|
+
group.close({ remove: true });
|
|
56
|
+
} catch {
|
|
57
|
+
group.close();
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
retain() {
|
|
61
|
+
if (settled || retained) return;
|
|
62
|
+
retained = true;
|
|
63
|
+
lease.retain();
|
|
64
|
+
group.close();
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export async function recoverProcessOwnership(executionId, options) {
|
|
70
|
+
const authority = durableJournal();
|
|
71
|
+
if (!authority)
|
|
72
|
+
throw new Error("Kernel process ownership recovery requires Linux");
|
|
73
|
+
const receipt = await authority.recover(executionId, options);
|
|
74
|
+
unresolvedOwners.delete(executionId);
|
|
75
|
+
return receipt;
|
|
22
76
|
}
|
|
23
77
|
|
|
24
78
|
export const PROCESS_OWNERSHIP_UNCONFIRMED =
|
|
@@ -73,6 +127,7 @@ export function getProcessOwnershipStatus() {
|
|
|
73
127
|
!!durable &&
|
|
74
128
|
[...unresolvedOwners.values()].every((owner) => owner.durable),
|
|
75
129
|
restartSafe: false,
|
|
130
|
+
recoverableExecutionIds: durable?.recoverableExecutionIds || [],
|
|
76
131
|
};
|
|
77
132
|
}
|
|
78
133
|
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
export const PROCESS_RECOVERY_CGROUP_SCHEMA =
|
|
5
|
+
"chainlesschain.process-recovery-cgroup/v1";
|
|
6
|
+
const UUID = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/u;
|
|
7
|
+
const CGROUP2_MAGIC = 0x63677270n;
|
|
8
|
+
const BOOT_ID = "/proc/sys/kernel/random/boot_id";
|
|
9
|
+
|
|
10
|
+
function denied(message, cause) {
|
|
11
|
+
return Object.assign(
|
|
12
|
+
new Error(
|
|
13
|
+
`Process recovery cgroup is unavailable: ${message}`,
|
|
14
|
+
cause ? { cause } : undefined,
|
|
15
|
+
),
|
|
16
|
+
{
|
|
17
|
+
code: "BROKER_PROCESS_RECOVERY_UNAVAILABLE",
|
|
18
|
+
recoveryRequired: true,
|
|
19
|
+
},
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
function requireValue(value, reason) {
|
|
23
|
+
if (!value) throw denied(reason);
|
|
24
|
+
}
|
|
25
|
+
function identity(stat) {
|
|
26
|
+
return { dev: String(stat.dev), ino: String(stat.ino) };
|
|
27
|
+
}
|
|
28
|
+
function same(stat, expected, prefix = "") {
|
|
29
|
+
return (
|
|
30
|
+
String(stat.dev) === expected[`${prefix}dev`] &&
|
|
31
|
+
String(stat.ino) === expected[`${prefix}ino`]
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
function rootPath(root) {
|
|
35
|
+
requireValue(
|
|
36
|
+
typeof root === "string" &&
|
|
37
|
+
path.posix.isAbsolute(root) &&
|
|
38
|
+
root !== "/" &&
|
|
39
|
+
!root.includes("\0") &&
|
|
40
|
+
!root.split("/").includes(".."),
|
|
41
|
+
"invalid delegated root",
|
|
42
|
+
);
|
|
43
|
+
return path.posix.normalize(root).replace(/\/+$/u, "");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function validateRecoveryCgroup(value, executionId) {
|
|
47
|
+
requireValue(
|
|
48
|
+
value &&
|
|
49
|
+
Object.keys(value).sort().join() ===
|
|
50
|
+
"bootId,dev,ino,name,root,rootdev,rootino,schema" &&
|
|
51
|
+
value.schema === PROCESS_RECOVERY_CGROUP_SCHEMA &&
|
|
52
|
+
UUID.test(value.bootId) &&
|
|
53
|
+
UUID.test(executionId) &&
|
|
54
|
+
value.name === `cc-owner-${executionId}` &&
|
|
55
|
+
rootPath(value.root) === value.root &&
|
|
56
|
+
[value.dev, value.ino, value.rootdev, value.rootino].every(
|
|
57
|
+
(part) => typeof part === "string" && /^\d{1,30}$/u.test(part),
|
|
58
|
+
),
|
|
59
|
+
"invalid persisted group identity",
|
|
60
|
+
);
|
|
61
|
+
return value;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function openDirectory(io, directory) {
|
|
65
|
+
const before = io.lstatSync(directory, { bigint: true });
|
|
66
|
+
requireValue(
|
|
67
|
+
before.isDirectory() && !before.isSymbolicLink(),
|
|
68
|
+
"cgroup path is not a directory",
|
|
69
|
+
);
|
|
70
|
+
requireValue(
|
|
71
|
+
io.realpathSync(directory) === directory,
|
|
72
|
+
"cgroup path traverses a link",
|
|
73
|
+
);
|
|
74
|
+
const fd = io.openSync(
|
|
75
|
+
directory,
|
|
76
|
+
io.constants.O_RDONLY | io.constants.O_DIRECTORY | io.constants.O_NOFOLLOW,
|
|
77
|
+
);
|
|
78
|
+
try {
|
|
79
|
+
const opened = io.fstatSync(fd, { bigint: true });
|
|
80
|
+
requireValue(
|
|
81
|
+
same(opened, identity(before)),
|
|
82
|
+
"cgroup directory changed while opening",
|
|
83
|
+
);
|
|
84
|
+
requireValue(
|
|
85
|
+
BigInt(io.statfsSync(`/proc/self/fd/${fd}`, { bigint: true }).type) ===
|
|
86
|
+
CGROUP2_MAGIC,
|
|
87
|
+
"not a kernel cgroup v2 filesystem",
|
|
88
|
+
);
|
|
89
|
+
return { fd, stat: opened, pinned: `/proc/self/fd/${fd}` };
|
|
90
|
+
} catch (error) {
|
|
91
|
+
io.closeSync(fd);
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Kernel object identities, not PIDs, establish the recovery target. The root
|
|
97
|
+
* must be explicitly delegated by the host. This is cooperative lifecycle
|
|
98
|
+
* recovery, not confinement against same-UID code that can move cgroup members.
|
|
99
|
+
*/
|
|
100
|
+
export function openRecoveryCgroup(value, executionId, runtime = {}) {
|
|
101
|
+
const io = runtime.fs || fs;
|
|
102
|
+
requireValue(
|
|
103
|
+
(runtime.platform || process.platform) === "linux",
|
|
104
|
+
"Linux cgroup v2 is required",
|
|
105
|
+
);
|
|
106
|
+
validateRecoveryCgroup(value, executionId);
|
|
107
|
+
requireValue(
|
|
108
|
+
String(io.readFileSync(BOOT_ID, "utf8")).trim() === value.bootId,
|
|
109
|
+
"boot identity changed",
|
|
110
|
+
);
|
|
111
|
+
const root = openDirectory(io, value.root);
|
|
112
|
+
let group;
|
|
113
|
+
try {
|
|
114
|
+
requireValue(
|
|
115
|
+
same(root.stat, value, "root"),
|
|
116
|
+
"delegated root identity changed",
|
|
117
|
+
);
|
|
118
|
+
group = openDirectory(io, path.posix.join(value.root, value.name));
|
|
119
|
+
requireValue(same(group.stat, value), "recovery group identity changed");
|
|
120
|
+
} catch (error) {
|
|
121
|
+
if (group) io.closeSync(group.fd);
|
|
122
|
+
io.closeSync(root.fd);
|
|
123
|
+
throw error;
|
|
124
|
+
}
|
|
125
|
+
let closed = false;
|
|
126
|
+
const assertIdentity = () => {
|
|
127
|
+
requireValue(!closed, "group lease was closed");
|
|
128
|
+
requireValue(
|
|
129
|
+
String(io.readFileSync(BOOT_ID, "utf8")).trim() === value.bootId,
|
|
130
|
+
"boot identity changed",
|
|
131
|
+
);
|
|
132
|
+
const currentRoot = io.lstatSync(value.root, { bigint: true });
|
|
133
|
+
const currentGroup = io.lstatSync(path.posix.join(value.root, value.name), {
|
|
134
|
+
bigint: true,
|
|
135
|
+
});
|
|
136
|
+
requireValue(
|
|
137
|
+
currentRoot.isDirectory() &&
|
|
138
|
+
!currentRoot.isSymbolicLink() &&
|
|
139
|
+
same(currentRoot, value, "root") &&
|
|
140
|
+
currentGroup.isDirectory() &&
|
|
141
|
+
!currentGroup.isSymbolicLink() &&
|
|
142
|
+
same(currentGroup, value),
|
|
143
|
+
"cgroup authority was replaced",
|
|
144
|
+
);
|
|
145
|
+
};
|
|
146
|
+
const populated = () => {
|
|
147
|
+
assertIdentity();
|
|
148
|
+
const bytes = io.readFileSync(`${group.pinned}/cgroup.events`, "utf8");
|
|
149
|
+
requireValue(
|
|
150
|
+
typeof bytes === "string" && bytes.length <= 4096,
|
|
151
|
+
"invalid cgroup event payload",
|
|
152
|
+
);
|
|
153
|
+
const lines = bytes
|
|
154
|
+
.trim()
|
|
155
|
+
.split(/\n/u)
|
|
156
|
+
.map((line) => line.trim().split(/\s+/u));
|
|
157
|
+
const entries = lines.filter(([key]) => key === "populated");
|
|
158
|
+
requireValue(
|
|
159
|
+
entries.length === 1 &&
|
|
160
|
+
entries[0].length === 2 &&
|
|
161
|
+
["0", "1"].includes(entries[0][1]),
|
|
162
|
+
"missing or invalid populated fence",
|
|
163
|
+
);
|
|
164
|
+
return entries[0][1] === "1";
|
|
165
|
+
};
|
|
166
|
+
return Object.freeze({
|
|
167
|
+
identity: Object.freeze({ ...value }),
|
|
168
|
+
attachBeforeLaunch(proc) {
|
|
169
|
+
assertIdentity();
|
|
170
|
+
requireValue(
|
|
171
|
+
Number.isSafeInteger(proc?.pid) && proc.pid > 0,
|
|
172
|
+
"supervisor PID is unavailable",
|
|
173
|
+
);
|
|
174
|
+
requireValue(!populated(), "new recovery group was already populated");
|
|
175
|
+
// The native supervisor is still blocked on its private launch socket.
|
|
176
|
+
// No target can fork before this write and membership check complete.
|
|
177
|
+
io.writeFileSync(`${group.pinned}/cgroup.procs`, `${proc.pid}\n`, "utf8");
|
|
178
|
+
const members = String(
|
|
179
|
+
io.readFileSync(`${group.pinned}/cgroup.procs`, "utf8"),
|
|
180
|
+
)
|
|
181
|
+
.trim()
|
|
182
|
+
.split(/\s+/u);
|
|
183
|
+
requireValue(
|
|
184
|
+
members.includes(String(proc.pid)),
|
|
185
|
+
"supervisor membership was not observed",
|
|
186
|
+
);
|
|
187
|
+
},
|
|
188
|
+
kill() {
|
|
189
|
+
assertIdentity();
|
|
190
|
+
io.writeFileSync(`${group.pinned}/cgroup.kill`, "1\n", "utf8");
|
|
191
|
+
},
|
|
192
|
+
populated,
|
|
193
|
+
confirmEmpty() {
|
|
194
|
+
requireValue(!populated(), "recovery group remains populated");
|
|
195
|
+
return true;
|
|
196
|
+
},
|
|
197
|
+
close({ remove = false } = {}) {
|
|
198
|
+
if (closed) return;
|
|
199
|
+
try {
|
|
200
|
+
if (remove) {
|
|
201
|
+
requireValue(
|
|
202
|
+
!populated(),
|
|
203
|
+
"cannot remove a populated recovery group",
|
|
204
|
+
);
|
|
205
|
+
io.rmdirSync(`${root.pinned}/${value.name}`);
|
|
206
|
+
}
|
|
207
|
+
} finally {
|
|
208
|
+
closed = true;
|
|
209
|
+
io.closeSync(group.fd);
|
|
210
|
+
io.closeSync(root.fd);
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export function prepareRecoveryCgroup(executionId, rootInput, runtime = {}) {
|
|
217
|
+
if (!rootInput) return null;
|
|
218
|
+
const io = runtime.fs || fs;
|
|
219
|
+
requireValue(
|
|
220
|
+
(runtime.platform || process.platform) === "linux",
|
|
221
|
+
"Linux cgroup v2 is required",
|
|
222
|
+
);
|
|
223
|
+
requireValue(UUID.test(executionId), "invalid execution identity");
|
|
224
|
+
const root = rootPath(rootInput);
|
|
225
|
+
const opened = openDirectory(io, root);
|
|
226
|
+
const name = `cc-owner-${executionId}`;
|
|
227
|
+
let created = false;
|
|
228
|
+
try {
|
|
229
|
+
const bootId = String(io.readFileSync(BOOT_ID, "utf8")).trim();
|
|
230
|
+
requireValue(UUID.test(bootId), "boot identity is unavailable");
|
|
231
|
+
// The unique group is created through the pinned delegated root.
|
|
232
|
+
io.mkdirSync(`${opened.pinned}/${name}`, { mode: 0o700 });
|
|
233
|
+
created = true;
|
|
234
|
+
const directory = path.posix.join(root, name);
|
|
235
|
+
const stat = io.lstatSync(directory, { bigint: true });
|
|
236
|
+
const value = {
|
|
237
|
+
schema: PROCESS_RECOVERY_CGROUP_SCHEMA,
|
|
238
|
+
root,
|
|
239
|
+
name,
|
|
240
|
+
bootId,
|
|
241
|
+
rootdev: String(opened.stat.dev),
|
|
242
|
+
rootino: String(opened.stat.ino),
|
|
243
|
+
...identity(stat),
|
|
244
|
+
};
|
|
245
|
+
const group = openRecoveryCgroup(value, executionId, runtime);
|
|
246
|
+
try {
|
|
247
|
+
group.confirmEmpty();
|
|
248
|
+
// Require the real kernel primitive before a recoverable launch is armed.
|
|
249
|
+
const killFd = io.openSync(
|
|
250
|
+
`${directory}/cgroup.kill`,
|
|
251
|
+
io.constants.O_WRONLY | io.constants.O_NOFOLLOW,
|
|
252
|
+
);
|
|
253
|
+
io.closeSync(killFd);
|
|
254
|
+
return group;
|
|
255
|
+
} catch (error) {
|
|
256
|
+
group.close({ remove: true });
|
|
257
|
+
created = false;
|
|
258
|
+
throw error;
|
|
259
|
+
}
|
|
260
|
+
} catch (error) {
|
|
261
|
+
if (created) {
|
|
262
|
+
try {
|
|
263
|
+
io.rmdirSync(`${opened.pinned}/${name}`);
|
|
264
|
+
} catch {
|
|
265
|
+
/* No process was launched into this group. */
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
throw denied("preparation failed", error);
|
|
269
|
+
} finally {
|
|
270
|
+
io.closeSync(opened.fd);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
@@ -88,6 +88,7 @@ import {
|
|
|
88
88
|
} from "../lib/model-capabilities.js";
|
|
89
89
|
import {
|
|
90
90
|
createOpenAIResponsesBody,
|
|
91
|
+
createOpenAIResponsesReasoning,
|
|
91
92
|
createOpenAIResponsesStreamState,
|
|
92
93
|
finalizeOpenAIResponsesStream,
|
|
93
94
|
normalizeOpenAIResponsesResponse,
|
|
@@ -12120,9 +12121,10 @@ export async function chatWithTools(rawMessages, options) {
|
|
|
12120
12121
|
model: effectiveModel,
|
|
12121
12122
|
messages,
|
|
12122
12123
|
tools,
|
|
12123
|
-
maxOutputTokens:
|
|
12124
|
-
reasoning: _openAIResponsesReasoningParams(options),
|
|
12124
|
+
maxOutputTokens: modelProfile.requestMaxOutputTokens,
|
|
12125
|
+
reasoning: _openAIResponsesReasoningParams(options, modelProfile),
|
|
12125
12126
|
stream: typeof options.onToken === "function",
|
|
12127
|
+
modelProfile,
|
|
12126
12128
|
});
|
|
12127
12129
|
if (typeof options.onToken === "function") {
|
|
12128
12130
|
return await _retryStreamingChat(
|
|
@@ -13039,28 +13041,11 @@ function _intensityToEffort(want) {
|
|
|
13039
13041
|
}
|
|
13040
13042
|
|
|
13041
13043
|
/** Build an opt-in Responses reasoning request without changing default cost. */
|
|
13042
|
-
export function _openAIResponsesReasoningParams(
|
|
13043
|
-
|
|
13044
|
-
|
|
13045
|
-
|
|
13046
|
-
|
|
13047
|
-
"minimal",
|
|
13048
|
-
"low",
|
|
13049
|
-
"medium",
|
|
13050
|
-
"high",
|
|
13051
|
-
"xhigh",
|
|
13052
|
-
]);
|
|
13053
|
-
const requested =
|
|
13054
|
-
typeof options.thinkingEffort === "string"
|
|
13055
|
-
? options.thinkingEffort.toLowerCase()
|
|
13056
|
-
: null;
|
|
13057
|
-
return {
|
|
13058
|
-
effort:
|
|
13059
|
-
requested && allowed.has(requested)
|
|
13060
|
-
? requested
|
|
13061
|
-
: _intensityToEffort(want),
|
|
13062
|
-
summary: "auto",
|
|
13063
|
-
};
|
|
13044
|
+
export function _openAIResponsesReasoningParams(
|
|
13045
|
+
options = {},
|
|
13046
|
+
modelProfile = null,
|
|
13047
|
+
) {
|
|
13048
|
+
return createOpenAIResponsesReasoning(options, modelProfile);
|
|
13064
13049
|
}
|
|
13065
13050
|
|
|
13066
13051
|
/**
|
|
@@ -3,6 +3,8 @@ import path from "node:path";
|
|
|
3
3
|
import persistentSettings from "../lib/settings-permission-authority.cjs";
|
|
4
4
|
import settingsLoader from "../lib/settings-loader.cjs";
|
|
5
5
|
import { ScopedPermissionStore } from "../lib/scoped-permission-store.js";
|
|
6
|
+
import { assessAgentSandboxCapabilities } from "../lib/agent-sandbox.js";
|
|
7
|
+
import { describeExecutionSupport } from "../lib/execution-support.js";
|
|
6
8
|
import {
|
|
7
9
|
createPermissionRulesProvider,
|
|
8
10
|
permissionRulesProviderAuthority,
|
|
@@ -81,6 +83,18 @@ export function openPermissionAuthorityHost({
|
|
|
81
83
|
};
|
|
82
84
|
}
|
|
83
85
|
return Object.freeze({
|
|
86
|
+
capabilities(sandbox, options = {}) {
|
|
87
|
+
assertOpen();
|
|
88
|
+
runtimeOptions({ sandbox });
|
|
89
|
+
const report = assessAgentSandboxCapabilities(sandbox, options);
|
|
90
|
+
return {
|
|
91
|
+
...report,
|
|
92
|
+
support: describeExecutionSupport({
|
|
93
|
+
capabilityReport: report,
|
|
94
|
+
permissionRulesProvider: provider,
|
|
95
|
+
}),
|
|
96
|
+
};
|
|
97
|
+
},
|
|
84
98
|
exportLaunch() {
|
|
85
99
|
assertOpen();
|
|
86
100
|
return persistentSettings.exportSettingsPermissionAuthority(binding);
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{I as C,P as c,J as f,U as r,c as h,_ as k,R as n,V as l,K as d,Q as u,S as g,b as t}from"./vendor-Bn1JzzAn.js";import{_ as w}from"./index-yYmjkf38.js";import{aH as j}from"./icons-DG5UD-6K.js";const E={__name:"EmailListRenderer",props:{event:{type:Object,required:!0}},setup(_,{expose:m}){m();const a=_,e=t(()=>a.event.content||{}),i=t(()=>a.event.extra||{}),p=t(()=>e.value.from||e.value.sender||e.value.fromName||a.event.actor||"(未知发件人)"),s=t(()=>e.value.subject||e.value.title||"(无主题)"),y=t(()=>e.value.snippet||e.value.preview||e.value.body?.slice(0,100)||null),b=t(()=>Array.isArray(e.value.attachments)?e.value.attachments.length:typeof e.value.attachmentCount=="number"?e.value.attachmentCount:0),x=t(()=>i.value.category||e.value.category),S=t(()=>e.value.unread===!0),T=t(()=>{if(!a.event.occurredAt)return"";const o=new Date(a.event.occurredAt);return`${o.getFullYear()}-${String(o.getMonth()+1).padStart(2,"0")}-${String(o.getDate()).padStart(2,"0")} ${String(o.getHours()).padStart(2,"0")}:${String(o.getMinutes()).padStart(2,"0")}`}),v={props:a,c:e,e:i,fromText:p,subjectText:s,snippetText:y,attachmentCount:b,category:x,isUnread:S,formattedTime:T,computed:t,get MailOutlined(){return j}};return Object.defineProperty(v,"__isScriptSetup",{enumerable:!1,value:!0}),v}},M={class:"email-row"},N={class:"left"},O={class:"body"},V={class:"head"},A={class:"from"},B={class:"time"},R={class:"subject"},D={key:0,class:"snippet"},L={class:"meta"};function U(_,m,a,e,i,p){const s=C("a-tag");return c(),f("div",M,[r("div",N,[h(e.MailOutlined,{class:k(["icon",{unread:e.isUnread}])},null,8,["class"])]),r("div",O,[r("div",V,[r("span",A,n(e.fromText),1),r("span",B,n(e.formattedTime),1)]),r("div",R,n(e.subjectText),1),e.snippetText?(c(),f("div",D,n(e.snippetText),1)):l("v-if",!0),r("div",L,[h(s,{color:"blue",class:"src"},{default:d(()=>[u(n(a.event.source.adapter),1)]),_:1}),e.attachmentCount>0?(c(),g(s,{key:0,color:"orange"},{default:d(()=>[u(" 📎 "+n(e.attachmentCount),1)]),_:1})):l("v-if",!0),e.category?(c(),g(s,{key:1},{default:d(()=>[u(n(e.category),1)]),_:1})):l("v-if",!0)])])])}const Y=w(E,[["render",U],["__scopeId","data-v-e4ddf632"],["__file","/tmp/cc-web-panel-EYas9w/repo/packages/web-panel/src/components/pdh/renderers/EmailListRenderer.vue"]]);export{Y as default};
|