chainlesschain 0.166.47 → 0.166.48
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 +2 -2
- package/src/assets/web-panel/assets/{AIOps-CDpT0WXV.js → AIOps-BLGEffkh.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-BNfUljWx.js → ActionButton-Cz1z2VGD.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-C9323DEx.js → Analytics-C3j9cf6q.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-CRrjb2db.js → AppLayout-BR9ERuCA.js} +3 -3
- package/src/assets/web-panel/assets/{Artifacts-Db-DGqJS.js → Artifacts-CozJCm4y.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-CJdHRJ0W.js → Audit-DC5g9Ra2.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-_RSCnTYs.js → BackgroundAgents-B1zrQ0dW.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-8GFQi-dM.js → Backup-ChVnfjpm.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-C87NtgUZ.js → BaseInput-8_vLQsLf.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-DcTMWS8G.js → Chat-Dct9bm6L.js} +5 -5
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-5ZDPTW_H.js → ChatBubbleRenderer-BvoSsCKh.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-DM9EmYee.js → Checkbox-DZQIsCIC.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-M2YqDRNN.js → Codegen-CdzHxmnq.js} +1 -1
- package/src/assets/web-panel/assets/{Col-oB1LLxyf.js → Col-DdAN8LeW.js} +1 -1
- package/src/assets/web-panel/assets/{Community-TcVZK5tF.js → Community-CNgjT9MA.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-XKmq-3K_.js → Compact-BtQiX6G-.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-C6CBiDRy.js → Compliance-OGX5ma3j.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-Dz9CGqXH.js → Cowork-CgugQnGM.js} +4 -4
- package/src/assets/web-panel/assets/{Cron-CxJDgDVL.js → Cron-Bpg7cPFD.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-DMP0JsaQ.js → Crosschain-D1hsuswf.js} +1 -1
- package/src/assets/web-panel/assets/{DID-I5IQeogb.js → DID-miDRsMBw.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-BfLW2MXj.js → Dashboard-CGcl8htP.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-BK8EjAlh.js → Dropdown-BFaxTdVE.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-C3iqSv5I.js → EmailListRenderer-BQdGLZIO.js} +1 -1
- package/src/assets/web-panel/assets/EvolutionSettings-bMvgBk6r.js +1 -0
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-T19rnqm-.js → FamilyGuardDashboard-c1WVC_--.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-CozrbPMy.js → Federation-DyAcFrQY.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-k1dCLNNJ.js → FormItemContext-CfwpfD2B.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-DbaW_Jvt.js → GenericCardRenderer-DkJcbBpS.js} +1 -1
- package/src/assets/web-panel/assets/{Git-CQHRyEIt.js → Git-DgYIrPJh.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-BMJYZgMy.js → Governance-BF1tYPJ6.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-B7mpNU7V.js → Inference-BFkwRIrU.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-Br0Blg6B.js → KnowledgeGraph-DpCoqhBa.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-HIdk0S6M.js → Logs-DU2qAJmD.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-HxeocBHy.js → MarkdownRenderer-QWv7UJLq.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-Bkfq69UP.js → Marketplace-Dz9NUrfo.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-dtmx0bHO.js → McpTools-B67I3d5v.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-CrRlOuEf.js → Memory-crkElJSy.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-D4ZIZiAH.js → MobileBridge-BO20VbdX.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-C90Rw7GE.js → MobileProjects-Ba-3gZZd.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-CC6k_uh4.js → Mtc-BAHZM084.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-T_CUIYUy.js → MtcAudit-CL3DzajL.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-N_e0RRWk.js → Multisig-BSWWzvWH.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-Iq3VzEv0.js → NLProgramming-fnxqXM_3.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-B5sKJUJt.js → Notes-qUZPIQXT.js} +4 -4
- package/src/assets/web-panel/assets/{NotificationSettings-K8UVp8qQ.js → NotificationSettings-x9Xrixqu.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-BWNyM-6m.js → OrderTableRenderer-BtMtQUt7.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-DMO1NJxI.js → Organization-C946Hzna.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-BBY6b31Z.js → Overflow-BYf8KIts.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-DmsL0y0v.js → P2P-Cbqf5dnv.js} +2 -2
- package/src/assets/web-panel/assets/PdhVaultBrowser-rmxp0cuF.js +7 -0
- package/src/assets/web-panel/assets/{Permissions-CWcpRtWa.js → Permissions-CZBw45EW.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub--ZNDIdsn.js → PersonalDataHub-BKwUAt43.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-DCdj5NGb.js → Pipeline-DmFD2QW4.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-BRKu7Q3E.js → Privacy-KpuwWvt5.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-BJ7ozIAb.js → ProjectInit-Bt8UheX6.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-B2cVLiXW.js → ProjectSettings-2ZeAn9CP.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-Ce9r_W-p.js → Projects-zBuet8Zs.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-BDespvGZ.js → Providers-DcEAnw6L.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-BUNO4fDU.js → QrScannerModal-qF7liYG9.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-D2TUqeBV.js → QuickAsk-qiESlVBJ.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-BNNQPHBH.js → Recommend-Bk-cQyi9.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-D2t1V_Cm.js → RemoteSession-CwrekErS.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-DVK5hEqI.js → Reputation-Bmw_UQtW.js} +1 -1
- package/src/assets/web-panel/assets/{Row-BB9RTwmE.js → Row-CPdqZIUX.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-DmEO7liZ.js → RssFeed-D_W-xxvS.js} +2 -2
- package/src/assets/web-panel/assets/{Search-UwEYjnDB.js → Search-Hu0oDFj_.js} +1 -1
- package/src/assets/web-panel/assets/{Security-DebqSsMp.js → Security-DeRxUwmq.js} +4 -4
- package/src/assets/web-panel/assets/{Services-CuuPts3C.js → Services-CsNmHFkf.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-DbWpmbgf.js → Skeleton-gv_Vz0tN.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-BaK7SxzT.js → Skills-Dgg-_rCB.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-BFHv51Jb.js → Sla-Bpu-bsEJ.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-B9b1R6uK.js → SpeechSettings-CrhleHuF.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-BnO0CN02.js → SyncSettings-JHjUN6I9.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-Dy01Vq49.js → Tasks-DKRWKkl9.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-BCy2AMHc.js → Templates-leiWntLq.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-4Tx9y_Y1.js → Tenant--1bYulvn.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-CPOoUH8J.js → Terminal-C0y6FvSS.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-C85GlJzu.js → TimelineRenderer-CQK4PVv4.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-_xneFBGF.js → Tokens-DbIvF-DV.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-BizG4rTr.js → Trigger-CkXJ2vuq.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-Dh8EIn3n.js → Trust-B02WXJPw.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-C5NWoHWQ.js → UkeySign-VPMkfzmw.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-0YfrySL6.js → VideoEditing-C5ezUnah.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-BZVH8Ags.js → Wallet-CYfvYRrY.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-kNofa6vm.js → WebAuthn-DtKl-kpr.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-BNO34_j0.js → WorkflowEditor-DACITF-u.js} +1 -1
- package/src/assets/web-panel/assets/{chat-ifQ2FlwD.js → chat-qnygfnLi.js} +1 -1
- package/src/assets/web-panel/assets/{colors-1TL8H6EH.js → colors-D4mISLBD.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-DBfRjs_T.js → compact-item-BBPfxYGL.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-Bs6V4iBf.js → createContext-Byx4ndHp.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-DBy6gsuI.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-Bz6LMY_0.js → hasIn-CGjLkkn3.js} +1 -1
- package/src/assets/web-panel/assets/{index--WvT2Il3.js → index-B8UpadHC.js} +1 -1
- package/src/assets/web-panel/assets/{index-DMVktlxG.js → index-B9jH-h4X.js} +1 -1
- package/src/assets/web-panel/assets/{index-BiZ7Nz5-.js → index-BN7_TSgU.js} +1 -1
- package/src/assets/web-panel/assets/{index-CtB45Hok.js → index-BNe2lCk6.js} +1 -1
- package/src/assets/web-panel/assets/{index-CNKHoXWI.js → index-B_fHPZ8E.js} +1 -1
- package/src/assets/web-panel/assets/index-BfEDXk_E.js +1 -0
- package/src/assets/web-panel/assets/{index-B5SJCUZ7.js → index-BklGH3YI.js} +1 -1
- package/src/assets/web-panel/assets/{index-pOUFfRdN.js → index-BmKYaaGP.js} +1 -1
- package/src/assets/web-panel/assets/{index-BB1Af7Fv.js → index-BxQF1wCU.js} +1 -1
- package/src/assets/web-panel/assets/{index-EK3XtMxA.js → index-ByA1NyFP.js} +1 -1
- package/src/assets/web-panel/assets/{index-cNwsev6n.js → index-BzeBBAl5.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cykt9BbN.js → index-C3abGsSW.js} +1 -1
- package/src/assets/web-panel/assets/{index-f_7L9GvP.js → index-C9tcHs46.js} +1 -1
- package/src/assets/web-panel/assets/{index-BxqC3LiO.js → index-CD5Btq6p.js} +1 -1
- package/src/assets/web-panel/assets/{index-DOww5Qny.js → index-CFMWjuav.js} +1 -1
- package/src/assets/web-panel/assets/{index-rGhS02f8.js → index-CMj8qmpX.js} +1 -1
- package/src/assets/web-panel/assets/{index-BQL_tldd.js → index-CVUJjQAT.js} +1 -1
- package/src/assets/web-panel/assets/{index-C7d6QlfR.js → index-CWhI8G14.js} +1 -1
- package/src/assets/web-panel/assets/{index-CXtiLagi.js → index-CZjNamSU.js} +1 -1
- package/src/assets/web-panel/assets/{index-C6c0j-Ki.js → index-CmHE-lY_.js} +1 -1
- package/src/assets/web-panel/assets/{index-DWPp-d07.js → index-CoA8WsVr.js} +1 -1
- package/src/assets/web-panel/assets/{index-DEPPzM8I.js → index-Cpg3AWRy.js} +1 -1
- package/src/assets/web-panel/assets/index-CrxUwNM8.js +1 -0
- package/src/assets/web-panel/assets/{index-DDhy4eCS.js → index-CsVuQsY1.js} +1 -1
- package/src/assets/web-panel/assets/{index-vb1XIxEB.js → index-CydHMjwc.js} +1 -1
- package/src/assets/web-panel/assets/{index-CBh48_Mh.js → index-DH6iC5b2.js} +1 -1
- package/src/assets/web-panel/assets/{index-84mi5rsv.js → index-DZXhKAk9.js} +1 -1
- package/src/assets/web-panel/assets/{index-BoCTDcn6.js → index-D_g9mA1N.js} +1 -1
- package/src/assets/web-panel/assets/{index-B6RClMZl.js → index-DboTeuPF.js} +1 -1
- package/src/assets/web-panel/assets/{index-XJIV7HEw.js → index-DfgpKd64.js} +1 -1
- package/src/assets/web-panel/assets/{index-C9QYxq61.js → index-Dmfe0L2Z.js} +1 -1
- package/src/assets/web-panel/assets/{index-lUL3bD2z.js → index-Dzni_5Qw.js} +1 -1
- package/src/assets/web-panel/assets/{index-BXJXMaZe.js → index-J5JkStvY.js} +1 -1
- package/src/assets/web-panel/assets/{index-CXVBQ82k.js → index-Ne8UGllV.js} +3 -3
- package/src/assets/web-panel/assets/{index-BhPDhmvN.js → index-O09jXbir.js} +1 -1
- package/src/assets/web-panel/assets/{index-DvM2xBUA.js → index-Y7el_3WS.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cu_V6ZJH.js → index-c_tBkIlU.js} +1 -1
- package/src/assets/web-panel/assets/{index-vHtnQsnV.js → index-lKJWAmd4.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cn7EIWmI.js → index-xzRKTy0R.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-DpRIjwTt.js → initDefaultProps-XuhfEIhB.js} +1 -1
- package/src/assets/web-panel/assets/{motion-CG4_9o-Z.js → motion-Cbxh6AxO.js} +1 -1
- package/src/assets/web-panel/assets/{move-Dku1fXtf.js → move-C7IEnFly.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-BvAVeX-3.js → mtc-parser-lLADx4LL.js} +1 -1
- package/src/assets/web-panel/assets/{omit-DFu0x78D.js → omit-mIbic_k-.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-CmI-FNcK.js → pickAttrs-_xIYEqu3.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-CGERYewB.js → placementArrow-ClB-E0ny.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-D7G8bF5J.js → responsiveObserve-D36Bg2ut.js} +1 -1
- package/src/assets/web-panel/assets/{slide-B2sXwWXy.js → slide-CUuO08Bl.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-DI5yh0n3.js → statusUtils-BKahwLbv.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-deGJ5dp_.js → styleChecker-2pT-T8vy.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-OqHBXEmi.js → useFlexGapSupport-DyH5g7Rg.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-Dqgx0Qv_.js → useFs-CTOauB3X.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-Be-lveh2.js → usePersonalDataHub-B02JYIMe.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-CFxB876W.js → vnode-Do22wQrc.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-BFOYrl4e.js → zoom-BfnL7rit.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/plugin.js +36 -5
- package/src/data/changelog.json +13 -0
- package/src/generated/cc-agent-protocol.schema.json +44 -1
- package/src/lib/background-agent-list-index.js +222 -0
- package/src/lib/background-agent-supervisor.js +38 -0
- package/src/lib/codex-app-server-adapter.js +1 -0
- package/src/lib/context-memory-kernel/provider-context.js +69 -4
- package/src/lib/deferred-question-context.js +160 -4
- package/src/lib/eval/plugin-suite.js +286 -22
- package/src/lib/session-host-snapshot.js +16 -0
- package/src/runtime/headless-stream.js +162 -13
- package/src/assets/web-panel/assets/EvolutionSettings-BJmBzpW6.js +0 -1
- package/src/assets/web-panel/assets/PdhVaultBrowser-C65AX_RY.js +0 -7
- package/src/assets/web-panel/assets/devWarning-BHnbnuYL.js +0 -1
- package/src/assets/web-panel/assets/index-CsRz9-ZH.js +0 -1
- package/src/assets/web-panel/assets/index-DnjMFf3m.js +0 -1
|
@@ -1,6 +1,17 @@
|
|
|
1
|
+
import { normalizeInteractionBinding } from "./interaction-binding.js";
|
|
2
|
+
|
|
1
3
|
const MAX_DEFERRED_ANSWERS_PER_CALL = 8;
|
|
2
4
|
const MAX_DEFERRED_ANSWER_CHARS = 16_384;
|
|
3
5
|
const MAX_DEFERRED_QUESTION_CHARS = 2_048;
|
|
6
|
+
const MAX_DEFERRED_QUESTIONS = 32;
|
|
7
|
+
const MAX_DEFERRED_OPTIONS = 32;
|
|
8
|
+
|
|
9
|
+
export const DEFERRED_QUESTION_EVENTS = Object.freeze({
|
|
10
|
+
REQUESTED: "deferred_question_requested",
|
|
11
|
+
RESOLVED: "deferred_question_resolved",
|
|
12
|
+
CONSUMED: "deferred_question_consumed",
|
|
13
|
+
EXPIRED: "deferred_question_expired",
|
|
14
|
+
});
|
|
4
15
|
|
|
5
16
|
function boundedText(value, maxChars) {
|
|
6
17
|
let text;
|
|
@@ -18,6 +29,145 @@ function boundedText(value, maxChars) {
|
|
|
18
29
|
return `${text.slice(0, maxChars)}\n[truncated]`;
|
|
19
30
|
}
|
|
20
31
|
|
|
32
|
+
function cloneJson(value, fallback = null) {
|
|
33
|
+
try {
|
|
34
|
+
return JSON.parse(JSON.stringify(value));
|
|
35
|
+
} catch {
|
|
36
|
+
return fallback;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function normalizeBinding(value, sessionId = null) {
|
|
41
|
+
const binding = normalizeInteractionBinding(value);
|
|
42
|
+
if (
|
|
43
|
+
!Number.isSafeInteger(binding.sequence) ||
|
|
44
|
+
binding.sequence <= 0 ||
|
|
45
|
+
binding.sessionId == null ||
|
|
46
|
+
(sessionId != null && String(binding.sessionId) !== String(sessionId))
|
|
47
|
+
) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
return binding;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function sameBinding(left, right) {
|
|
54
|
+
return JSON.stringify(left) === JSON.stringify(right);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function normalizeDeferredQuestionRequest(value, sessionId = null) {
|
|
58
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
59
|
+
const questionId = String(value.questionId || value.requestId || "").trim();
|
|
60
|
+
const binding = normalizeBinding(value.binding, sessionId);
|
|
61
|
+
if (!questionId || questionId.length > 160 || !binding) return null;
|
|
62
|
+
return Object.freeze({
|
|
63
|
+
questionId,
|
|
64
|
+
question: boundedText(value.question || "", MAX_DEFERRED_QUESTION_CHARS),
|
|
65
|
+
options: Array.isArray(value.options)
|
|
66
|
+
? cloneJson(value.options.slice(0, MAX_DEFERRED_OPTIONS), [])
|
|
67
|
+
: null,
|
|
68
|
+
multiSelect: value.multiSelect === true,
|
|
69
|
+
purpose: value.purpose === "preference" ? "preference" : "information",
|
|
70
|
+
binding,
|
|
71
|
+
requestedRevision: Number.isSafeInteger(
|
|
72
|
+
value.requestedRevision ?? value.contextRevision,
|
|
73
|
+
)
|
|
74
|
+
? (value.requestedRevision ?? value.contextRevision)
|
|
75
|
+
: null,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function normalizeDeferredQuestionAnswer(value, sessionId = null) {
|
|
80
|
+
const request = normalizeDeferredQuestionRequest(value, sessionId);
|
|
81
|
+
if (!request) return null;
|
|
82
|
+
return Object.freeze({
|
|
83
|
+
...request,
|
|
84
|
+
answer: boundedText(value.answer, MAX_DEFERRED_ANSWER_CHARS),
|
|
85
|
+
resolvedRevision: Number.isSafeInteger(value.resolvedRevision)
|
|
86
|
+
? value.resolvedRevision
|
|
87
|
+
: null,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function upsertBounded(map, key, value) {
|
|
92
|
+
map.delete(key);
|
|
93
|
+
map.set(key, value);
|
|
94
|
+
while (map.size > MAX_DEFERRED_QUESTIONS) {
|
|
95
|
+
map.delete(map.keys().next().value);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function createDeferredQuestionEventReducer({ sessionId = null } = {}) {
|
|
100
|
+
const pending = new Map();
|
|
101
|
+
const answers = new Map();
|
|
102
|
+
let maxSequence = 0;
|
|
103
|
+
return {
|
|
104
|
+
accept(event) {
|
|
105
|
+
const data = event?.data || {};
|
|
106
|
+
if (event?.type === DEFERRED_QUESTION_EVENTS.REQUESTED) {
|
|
107
|
+
const record = normalizeDeferredQuestionRequest(data, sessionId);
|
|
108
|
+
if (
|
|
109
|
+
!record ||
|
|
110
|
+
pending.has(record.questionId) ||
|
|
111
|
+
answers.has(record.questionId) ||
|
|
112
|
+
record.binding.sequence <= maxSequence
|
|
113
|
+
) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
maxSequence = Math.max(maxSequence, record.binding.sequence);
|
|
117
|
+
upsertBounded(pending, record.questionId, record);
|
|
118
|
+
answers.delete(record.questionId);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if (event?.type === DEFERRED_QUESTION_EVENTS.RESOLVED) {
|
|
122
|
+
const record = normalizeDeferredQuestionAnswer(data, sessionId);
|
|
123
|
+
const requested = record && pending.get(record.questionId);
|
|
124
|
+
if (
|
|
125
|
+
!record ||
|
|
126
|
+
!requested ||
|
|
127
|
+
!sameBinding(requested.binding, record.binding)
|
|
128
|
+
) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
pending.delete(record.questionId);
|
|
132
|
+
upsertBounded(answers, record.questionId, record);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
if (
|
|
136
|
+
event?.type === DEFERRED_QUESTION_EVENTS.CONSUMED ||
|
|
137
|
+
event?.type === DEFERRED_QUESTION_EVENTS.EXPIRED
|
|
138
|
+
) {
|
|
139
|
+
const ids = Array.isArray(data.questionIds)
|
|
140
|
+
? data.questionIds
|
|
141
|
+
: [data.questionId || data.requestId];
|
|
142
|
+
for (const value of ids) {
|
|
143
|
+
const questionId = String(value || "").trim();
|
|
144
|
+
if (!questionId) continue;
|
|
145
|
+
pending.delete(questionId);
|
|
146
|
+
answers.delete(questionId);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
finish() {
|
|
151
|
+
return Object.freeze({
|
|
152
|
+
pendingQuestions: Object.freeze(
|
|
153
|
+
[...pending.values()].map((entry) => Object.freeze({ ...entry })),
|
|
154
|
+
),
|
|
155
|
+
answers: Object.freeze(
|
|
156
|
+
[...answers.values()].map((entry) => Object.freeze({ ...entry })),
|
|
157
|
+
),
|
|
158
|
+
maxSequence,
|
|
159
|
+
});
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export function reduceDeferredQuestionEvents(events, options = {}) {
|
|
165
|
+
const reducer = createDeferredQuestionEventReducer(options);
|
|
166
|
+
for (const event of Array.isArray(events) ? events : [])
|
|
167
|
+
reducer.accept(event);
|
|
168
|
+
return reducer.finish();
|
|
169
|
+
}
|
|
170
|
+
|
|
21
171
|
/**
|
|
22
172
|
* Session-scoped inbox for answers to non-blocking, informational questions.
|
|
23
173
|
* Answers are consumed once as transient user context; they are never persisted
|
|
@@ -75,15 +225,21 @@ export class DeferredQuestionContext {
|
|
|
75
225
|
|
|
76
226
|
prepareCall({ currentRevision = null } = {}) {
|
|
77
227
|
if (this._answers.length === 0) return null;
|
|
78
|
-
const answers = this._answers.
|
|
228
|
+
const answers = this._answers.slice(0, MAX_DEFERRED_ANSWERS_PER_CALL);
|
|
79
229
|
if (this._onConsumed) {
|
|
80
230
|
try {
|
|
81
|
-
|
|
231
|
+
if (
|
|
232
|
+
this._onConsumed(answers.map((entry) => entry.questionId)) === false
|
|
233
|
+
) {
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
82
236
|
} catch {
|
|
83
|
-
//
|
|
84
|
-
//
|
|
237
|
+
// Do not inject context unless its one-shot consumption is durably
|
|
238
|
+
// acknowledged. A later call can retry without duplicating the answer.
|
|
239
|
+
return null;
|
|
85
240
|
}
|
|
86
241
|
}
|
|
242
|
+
this._answers.splice(0, answers.length);
|
|
87
243
|
const revision = Number.isSafeInteger(currentRevision)
|
|
88
244
|
? currentRevision
|
|
89
245
|
: null;
|
|
@@ -7,6 +7,8 @@ import { encodeName } from "../plugin-runtime/scopes.js";
|
|
|
7
7
|
|
|
8
8
|
export const PLUGIN_EVAL_SUITE_SCHEMA = "chainlesschain.plugin-eval-suite/v1";
|
|
9
9
|
export const PLUGIN_EVAL_REPORT_SCHEMA = "chainlesschain.plugin-eval-report/v1";
|
|
10
|
+
export const PLUGIN_EVAL_HOLDOUT_SCHEMA =
|
|
11
|
+
"chainlesschain.plugin-eval-holdout/v1";
|
|
10
12
|
|
|
11
13
|
const DEFAULT_SUITE_PATH = "evals/suite.json";
|
|
12
14
|
const MAX_SUITE_BYTES = 1024 * 1024;
|
|
@@ -22,6 +24,7 @@ const MAX_ASSERTION_FILE_BYTES = 4 * 1024 * 1024;
|
|
|
22
24
|
const MAX_RUNTIME_OUTPUT_BYTES = 16 * 1024 * 1024;
|
|
23
25
|
const MAX_RUNTIME_TOOL_CALLS = 10_000;
|
|
24
26
|
const MAX_TRIGGER_SIGNALS = 256;
|
|
27
|
+
const MAX_SAMPLE_RUNS = 20;
|
|
25
28
|
const TASK_ID = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$/u;
|
|
26
29
|
const EXPECTATIONS = new Set([
|
|
27
30
|
"should_trigger",
|
|
@@ -35,6 +38,7 @@ const ASSERTION_TYPES = new Set([
|
|
|
35
38
|
"file_contains",
|
|
36
39
|
"json_equals",
|
|
37
40
|
]);
|
|
41
|
+
const ARM_ORDERS = new Set(["balanced", "control-first", "candidate-first"]);
|
|
38
42
|
|
|
39
43
|
function digest(bytes, domain) {
|
|
40
44
|
return `sha256:${crypto
|
|
@@ -122,6 +126,25 @@ function suiteRelativePath(root, suiteFile) {
|
|
|
122
126
|
return relation.split(path.sep).join("/");
|
|
123
127
|
}
|
|
124
128
|
|
|
129
|
+
function readBoundedJsonFile(file, label) {
|
|
130
|
+
const target = path.resolve(file);
|
|
131
|
+
const stat = fs.lstatSync(target);
|
|
132
|
+
if (!stat.isFile() || stat.isSymbolicLink()) {
|
|
133
|
+
throw new TypeError(`${label} must be a regular file, not a symlink`);
|
|
134
|
+
}
|
|
135
|
+
if (stat.size === 0 || stat.size > MAX_SUITE_BYTES) {
|
|
136
|
+
throw new RangeError(`${label} must be under ${MAX_SUITE_BYTES} bytes`);
|
|
137
|
+
}
|
|
138
|
+
const bytes = fs.readFileSync(target);
|
|
139
|
+
let value;
|
|
140
|
+
try {
|
|
141
|
+
value = JSON.parse(bytes.toString("utf8"));
|
|
142
|
+
} catch {
|
|
143
|
+
throw new TypeError(`${label} is not valid JSON`);
|
|
144
|
+
}
|
|
145
|
+
return { target, bytes, value: plainObject(value, label) };
|
|
146
|
+
}
|
|
147
|
+
|
|
125
148
|
function collectPluginSnapshot(root) {
|
|
126
149
|
const rootStat = fs.lstatSync(root);
|
|
127
150
|
if (!rootStat.isDirectory() || rootStat.isSymbolicLink()) {
|
|
@@ -214,6 +237,24 @@ function parseThreshold(value, label, { min = 0, max = 1 } = {}) {
|
|
|
214
237
|
return value;
|
|
215
238
|
}
|
|
216
239
|
|
|
240
|
+
function parseSampleRuns(value, label = "sampling.runs") {
|
|
241
|
+
if (!Number.isSafeInteger(value) || value < 1 || value > MAX_SAMPLE_RUNS) {
|
|
242
|
+
throw new RangeError(
|
|
243
|
+
`${label} must be an integer between 1 and ${MAX_SAMPLE_RUNS}`,
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
return value;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function parseArmOrder(value, label = "sampling.armOrder") {
|
|
250
|
+
if (!ARM_ORDERS.has(value)) {
|
|
251
|
+
throw new TypeError(
|
|
252
|
+
`${label} must be balanced, control-first, or candidate-first`,
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
return value;
|
|
256
|
+
}
|
|
257
|
+
|
|
217
258
|
function normalizeAssertion(value, taskId, index) {
|
|
218
259
|
const assertion = plainObject(value, `task ${taskId} assertion ${index}`);
|
|
219
260
|
if (!ASSERTION_TYPES.has(assertion.type)) {
|
|
@@ -414,6 +455,8 @@ export function loadPluginEvalDefinition(pluginRoot, { suiteFile } = {}) {
|
|
|
414
455
|
}
|
|
415
456
|
const thresholds = raw.thresholds || {};
|
|
416
457
|
plainObject(thresholds, "plugin eval suite.thresholds");
|
|
458
|
+
const sampling = raw.sampling || {};
|
|
459
|
+
plainObject(sampling, "plugin eval suite.sampling");
|
|
417
460
|
const maxCandidateCostUsd = parseThreshold(
|
|
418
461
|
thresholds.maxCandidateCostUsd,
|
|
419
462
|
"thresholds.maxCandidateCostUsd",
|
|
@@ -421,6 +464,7 @@ export function loadPluginEvalDefinition(pluginRoot, { suiteFile } = {}) {
|
|
|
421
464
|
);
|
|
422
465
|
return {
|
|
423
466
|
schema: PLUGIN_EVAL_SUITE_SCHEMA,
|
|
467
|
+
pluginRoot: fs.realpathSync(root),
|
|
424
468
|
identity: {
|
|
425
469
|
name: manifest.metadata.name,
|
|
426
470
|
version: manifest.metadata.version,
|
|
@@ -433,6 +477,10 @@ export function loadPluginEvalDefinition(pluginRoot, { suiteFile } = {}) {
|
|
|
433
477
|
),
|
|
434
478
|
payload: snapshot,
|
|
435
479
|
tasks,
|
|
480
|
+
sampling: {
|
|
481
|
+
runs: parseSampleRuns(sampling.runs ?? 1),
|
|
482
|
+
armOrder: parseArmOrder(sampling.armOrder || "balanced"),
|
|
483
|
+
},
|
|
436
484
|
thresholds: {
|
|
437
485
|
minPassRateDelta:
|
|
438
486
|
parseThreshold(
|
|
@@ -450,6 +498,87 @@ export function loadPluginEvalDefinition(pluginRoot, { suiteFile } = {}) {
|
|
|
450
498
|
};
|
|
451
499
|
}
|
|
452
500
|
|
|
501
|
+
/**
|
|
502
|
+
* Attach reviewer-owned tasks from a file outside the plugin payload. The
|
|
503
|
+
* digest is reported separately so an author-controlled payload cannot mutate
|
|
504
|
+
* or silently absorb the holdout corpus.
|
|
505
|
+
*/
|
|
506
|
+
export function attachPluginEvalHoldout(definition, holdoutFile) {
|
|
507
|
+
if (!holdoutFile) return definition;
|
|
508
|
+
const { target, bytes, value } = readBoundedJsonFile(
|
|
509
|
+
holdoutFile,
|
|
510
|
+
"plugin eval holdout",
|
|
511
|
+
);
|
|
512
|
+
const holdoutReal = fs.realpathSync(target);
|
|
513
|
+
const relation = path.relative(definition.pluginRoot, holdoutReal);
|
|
514
|
+
if (
|
|
515
|
+
relation === "" ||
|
|
516
|
+
(!relation.startsWith(`..${path.sep}`) &&
|
|
517
|
+
relation !== ".." &&
|
|
518
|
+
!path.isAbsolute(relation))
|
|
519
|
+
) {
|
|
520
|
+
throw new TypeError(
|
|
521
|
+
"plugin eval holdout must be outside the plugin payload root",
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
if (value.schema !== PLUGIN_EVAL_HOLDOUT_SCHEMA) {
|
|
525
|
+
throw new TypeError(
|
|
526
|
+
`plugin eval holdout must use ${PLUGIN_EVAL_HOLDOUT_SCHEMA}`,
|
|
527
|
+
);
|
|
528
|
+
}
|
|
529
|
+
const identity = plainObject(value.plugin, "plugin eval holdout.plugin");
|
|
530
|
+
if (
|
|
531
|
+
identity.name !== definition.identity.name ||
|
|
532
|
+
identity.version !== definition.identity.version
|
|
533
|
+
) {
|
|
534
|
+
throw new TypeError(
|
|
535
|
+
"plugin eval holdout identity must match the parsed plugin manifest",
|
|
536
|
+
);
|
|
537
|
+
}
|
|
538
|
+
if (
|
|
539
|
+
!Array.isArray(value.tasks) ||
|
|
540
|
+
value.tasks.length === 0 ||
|
|
541
|
+
value.tasks.length > MAX_TASKS
|
|
542
|
+
) {
|
|
543
|
+
throw new RangeError(`plugin eval holdout requires 1-${MAX_TASKS} tasks`);
|
|
544
|
+
}
|
|
545
|
+
const holdoutTasks = value.tasks.map((task, index) => ({
|
|
546
|
+
...normalizeTask(task, index),
|
|
547
|
+
source: "holdout",
|
|
548
|
+
}));
|
|
549
|
+
if (definition.tasks.length + holdoutTasks.length > MAX_TASKS) {
|
|
550
|
+
throw new RangeError(
|
|
551
|
+
`plugin eval author and holdout tasks may not exceed ${MAX_TASKS}`,
|
|
552
|
+
);
|
|
553
|
+
}
|
|
554
|
+
const ids = new Set(definition.tasks.map((task) => task.id));
|
|
555
|
+
for (const task of holdoutTasks) {
|
|
556
|
+
if (ids.has(task.id)) {
|
|
557
|
+
throw new TypeError(`duplicate plugin eval task id: ${task.id}`);
|
|
558
|
+
}
|
|
559
|
+
ids.add(task.id);
|
|
560
|
+
}
|
|
561
|
+
return {
|
|
562
|
+
...definition,
|
|
563
|
+
tasks: [
|
|
564
|
+
...definition.tasks.map((task) => ({
|
|
565
|
+
...task,
|
|
566
|
+
source: task.source || "author",
|
|
567
|
+
})),
|
|
568
|
+
...holdoutTasks,
|
|
569
|
+
],
|
|
570
|
+
holdout: {
|
|
571
|
+
schema: PLUGIN_EVAL_HOLDOUT_SCHEMA,
|
|
572
|
+
// Reports may be shared as CI artifacts; do not disclose the reviewer's
|
|
573
|
+
// absolute workstation path. The digest is the authoritative binding.
|
|
574
|
+
path: path.basename(target),
|
|
575
|
+
digest: digest(bytes, "chainlesschain.plugin-eval-holdout-bytes/v1"),
|
|
576
|
+
tasks: holdoutTasks.length,
|
|
577
|
+
independentFromPayload: true,
|
|
578
|
+
},
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
|
|
453
582
|
function materializePlugin(definition, workspace) {
|
|
454
583
|
const nameDir = path.join(
|
|
455
584
|
workspace,
|
|
@@ -555,6 +684,7 @@ function buildTasks(definition, { candidate }) {
|
|
|
555
684
|
return definition.tasks.map((task) => ({
|
|
556
685
|
id: task.id,
|
|
557
686
|
description: task.description,
|
|
687
|
+
source: task.source || "author",
|
|
558
688
|
prompt: task.prompt,
|
|
559
689
|
expectedFiles: task.expectedFiles,
|
|
560
690
|
...(task.timeoutMs === undefined ? {} : { timeoutMs: task.timeoutMs }),
|
|
@@ -708,6 +838,7 @@ function projectArm(summary, definition, { candidate }) {
|
|
|
708
838
|
metrics.pluginTriggered === false));
|
|
709
839
|
return {
|
|
710
840
|
...result,
|
|
841
|
+
source: task.source || "author",
|
|
711
842
|
expectation,
|
|
712
843
|
pluginTriggered: metrics.pluginTriggered,
|
|
713
844
|
activationEvidenceAvailable: metrics.available === true,
|
|
@@ -753,10 +884,110 @@ function reportArm(arm) {
|
|
|
753
884
|
usage: arm.usage,
|
|
754
885
|
totalCostUsd: arm.totalCostUsd,
|
|
755
886
|
ms: arm.ms,
|
|
887
|
+
sampleRuns: arm.sampleRuns,
|
|
756
888
|
results: arm.results,
|
|
757
889
|
};
|
|
758
890
|
}
|
|
759
891
|
|
|
892
|
+
function aggregateArms(arms) {
|
|
893
|
+
const results = arms.flatMap((arm, sampleIndex) =>
|
|
894
|
+
arm.results.map((result) => ({ ...result, sample: sampleIndex + 1 })),
|
|
895
|
+
);
|
|
896
|
+
const total = results.length;
|
|
897
|
+
const measured = results.filter((result) =>
|
|
898
|
+
Array.isArray(result.unrelatedChanges),
|
|
899
|
+
);
|
|
900
|
+
const tasksWithUnrelatedChanges = measured.filter(
|
|
901
|
+
(result) => result.unrelatedChanges.length > 0,
|
|
902
|
+
).length;
|
|
903
|
+
const passed = results.filter((result) => result.pass).length;
|
|
904
|
+
const effectivePassed = results.filter(
|
|
905
|
+
(result) => result.effectivePass,
|
|
906
|
+
).length;
|
|
907
|
+
const triggered = results.filter(
|
|
908
|
+
(result) => result.pluginTriggered === true,
|
|
909
|
+
).length;
|
|
910
|
+
return {
|
|
911
|
+
results,
|
|
912
|
+
passed,
|
|
913
|
+
failed: total - passed,
|
|
914
|
+
total,
|
|
915
|
+
passRate: total > 0 ? passed / total : 0,
|
|
916
|
+
effectivePassed,
|
|
917
|
+
effectiveFailed: total - effectivePassed,
|
|
918
|
+
effectivePassRate: total > 0 ? effectivePassed / total : 0,
|
|
919
|
+
artifactChecksPassed: results.filter((result) => result.artifactCheckPassed)
|
|
920
|
+
.length,
|
|
921
|
+
executionsSucceeded: results.filter((result) => result.executionSucceeded)
|
|
922
|
+
.length,
|
|
923
|
+
tasksWithUnrelatedChanges,
|
|
924
|
+
unrelatedChangeRate: measured.length
|
|
925
|
+
? tasksWithUnrelatedChanges / measured.length
|
|
926
|
+
: 0,
|
|
927
|
+
triggerRate: total > 0 ? triggered / total : 0,
|
|
928
|
+
activationEvidenceComplete: results.every(
|
|
929
|
+
(result) => result.activationEvidenceAvailable,
|
|
930
|
+
),
|
|
931
|
+
...sumUsage(results),
|
|
932
|
+
ms: arms.reduce((totalMs, arm) => totalMs + arm.ms, 0),
|
|
933
|
+
sampleRuns: arms.length,
|
|
934
|
+
};
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
// Wilson score intervals stay well behaved for the small and extreme samples
|
|
938
|
+
// common in plugin author evals (unlike p +/- z*sqrt(p(1-p)/n)).
|
|
939
|
+
function wilson95(successes, total) {
|
|
940
|
+
if (!Number.isSafeInteger(total) || total <= 0) return null;
|
|
941
|
+
const z = 1.959963984540054;
|
|
942
|
+
const z2 = z * z;
|
|
943
|
+
const p = successes / total;
|
|
944
|
+
const denominator = 1 + z2 / total;
|
|
945
|
+
const center = (p + z2 / (2 * total)) / denominator;
|
|
946
|
+
const radius =
|
|
947
|
+
(z * Math.sqrt((p * (1 - p) + z2 / (4 * total)) / total)) / denominator;
|
|
948
|
+
return {
|
|
949
|
+
lower: Math.max(0, center - radius),
|
|
950
|
+
upper: Math.min(1, center + radius),
|
|
951
|
+
};
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
function confidenceComparison(control, candidate) {
|
|
955
|
+
const controlInterval = wilson95(control.effectivePassed, control.total);
|
|
956
|
+
const candidateInterval = wilson95(
|
|
957
|
+
candidate.effectivePassed,
|
|
958
|
+
candidate.total,
|
|
959
|
+
);
|
|
960
|
+
if (!controlInterval || !candidateInterval) return null;
|
|
961
|
+
return {
|
|
962
|
+
method: "wilson-95-newcombe-difference",
|
|
963
|
+
control: controlInterval,
|
|
964
|
+
candidate: candidateInterval,
|
|
965
|
+
passRateDelta: {
|
|
966
|
+
lower: candidateInterval.lower - controlInterval.upper,
|
|
967
|
+
upper: candidateInterval.upper - controlInterval.lower,
|
|
968
|
+
},
|
|
969
|
+
};
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
function sampleArmSequence(sampleRuns, armOrder, suiteDigest) {
|
|
973
|
+
if (armOrder === "control-first") {
|
|
974
|
+
return Array.from({ length: sampleRuns }, () => ["control", "candidate"]);
|
|
975
|
+
}
|
|
976
|
+
if (armOrder === "candidate-first") {
|
|
977
|
+
return Array.from({ length: sampleRuns }, () => ["candidate", "control"]);
|
|
978
|
+
}
|
|
979
|
+
// Bind the first arm to the suite digest, then alternate. This balances
|
|
980
|
+
// provider drift without introducing an unrecorded random source.
|
|
981
|
+
const startsWithCandidate =
|
|
982
|
+
Number.parseInt(String(suiteDigest).slice(-2), 16) % 2 === 1;
|
|
983
|
+
return Array.from({ length: sampleRuns }, (_, index) => {
|
|
984
|
+
const candidateFirst = startsWithCandidate
|
|
985
|
+
? index % 2 === 0
|
|
986
|
+
: index % 2 === 1;
|
|
987
|
+
return candidateFirst ? ["candidate", "control"] : ["control", "candidate"];
|
|
988
|
+
});
|
|
989
|
+
}
|
|
990
|
+
|
|
760
991
|
export async function runPluginEval(
|
|
761
992
|
definition,
|
|
762
993
|
{
|
|
@@ -768,6 +999,8 @@ export async function runPluginEval(
|
|
|
768
999
|
model = null,
|
|
769
1000
|
dryRun = false,
|
|
770
1001
|
minPassRateDelta,
|
|
1002
|
+
sampleRuns,
|
|
1003
|
+
armOrder,
|
|
771
1004
|
onResult,
|
|
772
1005
|
} = {},
|
|
773
1006
|
) {
|
|
@@ -777,30 +1010,49 @@ export async function runPluginEval(
|
|
|
777
1010
|
) {
|
|
778
1011
|
throw new TypeError("controlRunAgent and candidateRunAgent are required");
|
|
779
1012
|
}
|
|
780
|
-
const
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
keepWorkspaces,
|
|
784
|
-
onResult: onResult ? (result) => onResult({ arm, result }) : undefined,
|
|
785
|
-
});
|
|
786
|
-
const control = projectArm(
|
|
787
|
-
await runArm(
|
|
788
|
-
"control",
|
|
789
|
-
buildTasks(definition, { candidate: false }),
|
|
790
|
-
controlRunAgent,
|
|
791
|
-
),
|
|
792
|
-
definition,
|
|
793
|
-
{ candidate: false },
|
|
1013
|
+
const resolvedSampleRuns = parseSampleRuns(
|
|
1014
|
+
sampleRuns === undefined ? definition.sampling?.runs || 1 : sampleRuns,
|
|
1015
|
+
"sampleRuns",
|
|
794
1016
|
);
|
|
795
|
-
const
|
|
796
|
-
|
|
797
|
-
"
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
1017
|
+
const resolvedArmOrder = parseArmOrder(
|
|
1018
|
+
armOrder === undefined
|
|
1019
|
+
? definition.sampling?.armOrder || "balanced"
|
|
1020
|
+
: armOrder,
|
|
1021
|
+
"armOrder",
|
|
1022
|
+
);
|
|
1023
|
+
const runArm = async (arm, tasks, runAgent, sample) =>
|
|
1024
|
+
projectArm(
|
|
1025
|
+
await runSuite(tasks, {
|
|
1026
|
+
runAgent: instrumentRunAgent(runAgent, definition.identity),
|
|
1027
|
+
keepWorkspaces,
|
|
1028
|
+
onResult: onResult
|
|
1029
|
+
? (result) => onResult({ arm, sample, result })
|
|
1030
|
+
: undefined,
|
|
1031
|
+
}),
|
|
1032
|
+
definition,
|
|
1033
|
+
{ candidate: arm === "candidate" },
|
|
1034
|
+
);
|
|
1035
|
+
const armRuns = { control: [], candidate: [] };
|
|
1036
|
+
const sequence = sampleArmSequence(
|
|
1037
|
+
resolvedSampleRuns,
|
|
1038
|
+
resolvedArmOrder,
|
|
1039
|
+
definition.suiteDigest,
|
|
803
1040
|
);
|
|
1041
|
+
for (const [sampleIndex, orderedArms] of sequence.entries()) {
|
|
1042
|
+
for (const arm of orderedArms) {
|
|
1043
|
+
const candidate = arm === "candidate";
|
|
1044
|
+
armRuns[arm].push(
|
|
1045
|
+
await runArm(
|
|
1046
|
+
arm,
|
|
1047
|
+
buildTasks(definition, { candidate }),
|
|
1048
|
+
candidate ? candidateRunAgent : controlRunAgent,
|
|
1049
|
+
sampleIndex + 1,
|
|
1050
|
+
),
|
|
1051
|
+
);
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
const control = aggregateArms(armRuns.control);
|
|
1055
|
+
const candidate = aggregateArms(armRuns.candidate);
|
|
804
1056
|
const requiredDelta =
|
|
805
1057
|
minPassRateDelta === undefined
|
|
806
1058
|
? definition.thresholds.minPassRateDelta
|
|
@@ -859,10 +1111,19 @@ export async function runPluginEval(
|
|
|
859
1111
|
digest: definition.suiteDigest,
|
|
860
1112
|
tasks: definition.tasks.length,
|
|
861
1113
|
},
|
|
1114
|
+
holdout: definition.holdout || null,
|
|
862
1115
|
selection: {
|
|
863
1116
|
provider,
|
|
864
1117
|
model,
|
|
865
1118
|
dryRun: dryRun === true,
|
|
1119
|
+
sampleRuns: resolvedSampleRuns,
|
|
1120
|
+
armOrder: resolvedArmOrder,
|
|
1121
|
+
},
|
|
1122
|
+
sampling: {
|
|
1123
|
+
runs: resolvedSampleRuns,
|
|
1124
|
+
armOrder: resolvedArmOrder,
|
|
1125
|
+
sequence,
|
|
1126
|
+
deterministic: true,
|
|
866
1127
|
},
|
|
867
1128
|
thresholds: {
|
|
868
1129
|
...definition.thresholds,
|
|
@@ -877,6 +1138,9 @@ export async function runPluginEval(
|
|
|
877
1138
|
? "regression"
|
|
878
1139
|
: "neutral",
|
|
879
1140
|
},
|
|
1141
|
+
statistics: {
|
|
1142
|
+
confidence95: confidenceComparison(control, candidate),
|
|
1143
|
+
},
|
|
880
1144
|
arms: {
|
|
881
1145
|
control: reportArm(control),
|
|
882
1146
|
candidate: reportArm(candidate),
|
|
@@ -28,6 +28,10 @@ import {
|
|
|
28
28
|
} from "./session-message-provenance.js";
|
|
29
29
|
import { createSessionTranscriptStructureProjection } from "./session-transcript-structure.js";
|
|
30
30
|
import { normalizeSessionBudgetRootConfig } from "./session-budget-production-root.js";
|
|
31
|
+
import {
|
|
32
|
+
createDeferredQuestionEventReducer,
|
|
33
|
+
reduceDeferredQuestionEvents,
|
|
34
|
+
} from "./deferred-question-context.js";
|
|
31
35
|
|
|
32
36
|
export const SESSION_HOST_SNAPSHOT_SCHEMA =
|
|
33
37
|
"chainlesschain.session-host-snapshot/v1";
|
|
@@ -292,6 +296,7 @@ export function projectVerifiedSessionHostSnapshot(sessionId, events) {
|
|
|
292
296
|
sessionId,
|
|
293
297
|
verified: true,
|
|
294
298
|
});
|
|
299
|
+
const deferred = reduceDeferredQuestionEvents(safeEvents, { sessionId });
|
|
295
300
|
return Object.freeze({
|
|
296
301
|
snapshot: projectSessionHostObservation({
|
|
297
302
|
sessionId,
|
|
@@ -301,6 +306,9 @@ export function projectVerifiedSessionHostSnapshot(sessionId, events) {
|
|
|
301
306
|
}),
|
|
302
307
|
messages,
|
|
303
308
|
recovery,
|
|
309
|
+
deferredQuestions: deferred.pendingQuestions,
|
|
310
|
+
deferredAnswers: deferred.answers,
|
|
311
|
+
deferredQuestionSequence: deferred.maxSequence,
|
|
304
312
|
sessionBudgetRoot: sessionBudgetRootFromEvents(safeEvents),
|
|
305
313
|
});
|
|
306
314
|
}
|
|
@@ -316,6 +324,9 @@ function createStreamingSessionHostProjection(sessionId) {
|
|
|
316
324
|
sessionId,
|
|
317
325
|
verified: true,
|
|
318
326
|
});
|
|
327
|
+
const deferredQuestionReducer = createDeferredQuestionEventReducer({
|
|
328
|
+
sessionId,
|
|
329
|
+
});
|
|
319
330
|
|
|
320
331
|
return {
|
|
321
332
|
accept(event) {
|
|
@@ -337,6 +348,7 @@ function createStreamingSessionHostProjection(sessionId) {
|
|
|
337
348
|
lastEventType =
|
|
338
349
|
typeof safeEvent.type === "string" ? safeEvent.type : null;
|
|
339
350
|
mcpReducer.accept(safeEvent);
|
|
351
|
+
deferredQuestionReducer.accept(safeEvent);
|
|
340
352
|
},
|
|
341
353
|
finish({ headHash, eventCount, readMessages }) {
|
|
342
354
|
structure.finish();
|
|
@@ -351,6 +363,7 @@ function createStreamingSessionHostProjection(sessionId) {
|
|
|
351
363
|
projectCanonicalResumeMessages(recoveredMessages, { strict: true }),
|
|
352
364
|
);
|
|
353
365
|
const recovery = mcpReducer.finish();
|
|
366
|
+
const deferred = deferredQuestionReducer.finish();
|
|
354
367
|
return Object.freeze({
|
|
355
368
|
snapshot: projectSessionHostProjection({
|
|
356
369
|
sessionId,
|
|
@@ -363,6 +376,9 @@ function createStreamingSessionHostProjection(sessionId) {
|
|
|
363
376
|
}),
|
|
364
377
|
messages,
|
|
365
378
|
recovery,
|
|
379
|
+
deferredQuestions: deferred.pendingQuestions,
|
|
380
|
+
deferredAnswers: deferred.answers,
|
|
381
|
+
deferredQuestionSequence: deferred.maxSequence,
|
|
366
382
|
sessionBudgetRoot,
|
|
367
383
|
});
|
|
368
384
|
},
|