chainlesschain 0.162.123 → 0.162.124
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-C5CSd8pY.js → AIOps-BsDJlD_l.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-BugJtiJc.js → ActionButton-COjZZKWG.js} +1 -1
- package/src/assets/web-panel/assets/Analytics-4ZDZE7lc.js +3 -0
- package/src/assets/web-panel/assets/{AppLayout-Cb0eHetM.js → AppLayout-CYH5k2Rp.js} +5 -5
- package/src/assets/web-panel/assets/{Audit-qjq04wh8.js → Audit-BKlFbLdl.js} +1 -1
- package/src/assets/web-panel/assets/Backup-CiX61Qc2.js +1 -0
- package/src/assets/web-panel/assets/{BaseInput-CZ0c7QHk.js → BaseInput-CDxIQokd.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-CaKDT3wV.js → Chat-CaKgdRab.js} +6 -6
- package/src/assets/web-panel/assets/ChatBubbleRenderer-DgvpwU5o.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-C6-Iu3bG.js → Checkbox-CjiWfu4s.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-TGLFk6P1.js → Codegen-tKWGzajw.js} +1 -1
- package/src/assets/web-panel/assets/{Col-DVBrO7AN.js → Col-ZKWREyNs.js} +1 -1
- package/src/assets/web-panel/assets/{Community-tg6-Rik1.js → Community-CmLuPBUU.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-DM4ZgFSE.js → Compact-DqknM98n.js} +1 -1
- package/src/assets/web-panel/assets/Compliance-PZw0aBLI.js +1 -0
- package/src/assets/web-panel/assets/{Cowork-CJwiWkUY.js → Cowork-Dp29kHsC.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-dsdXHvrO.js → Cron-zmEJAetz.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-p7_5Gq-7.js → Crosschain-Sb-uM7Ty.js} +1 -1
- package/src/assets/web-panel/assets/{DID-BB_LBLYT.js → DID-D_UFNzJ5.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-Bh_HkNSD.js → Dashboard-Btag698v.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-dTFtVnKk.js → Dropdown-CH7l3KCs.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-yqdttzXV.js → EmailListRenderer-Bud2M3XX.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-DfX3YpUU.js → FamilyGuardDashboard-Bd2_8uME.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-BJApfPV4.js → Federation-1jhstF5P.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-DNMwIh7X.js → FormItemContext-CpSH464Y.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-C-T8UQf0.js → GenericCardRenderer-CANo8O-y.js} +1 -1
- package/src/assets/web-panel/assets/{Git-SURFhkLi.js → Git-hvuZVoD3.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-B4u_KsfE.js → Governance-BQrWksKt.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-2kr_rKQd.js → Inference-jEBTp12v.js} +1 -1
- package/src/assets/web-panel/assets/KnowledgeGraph-tDiV2taf.js +1 -0
- package/src/assets/web-panel/assets/Logs-DDkTnedu.js +2 -0
- package/src/assets/web-panel/assets/{Marketplace-W9Iz8b2m.js → Marketplace-9Yi32avt.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-D0waMomb.js → McpTools-Qx78XyOT.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-Ci768GQ3.js → Memory-CXYDO1oT.js} +2 -2
- package/src/assets/web-panel/assets/MobileBridge-MfUfkHA0.js +1 -0
- package/src/assets/web-panel/assets/{MobileProjects-RpFuAiNz.js → MobileProjects-DEDbWNIk.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-DJdw8btT.js → Mtc-Dwa_vlR8.js} +2 -2
- package/src/assets/web-panel/assets/{MtcAudit-Dc596XgH.js → MtcAudit-DAVkxhJv.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig--Qs7eGsH.js → Multisig-C3upmgPN.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-D3y71FDX.js → NLProgramming-DO3CZ0_z.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-uMxjj66_.js → Notes-B1Oy3FbC.js} +4 -4
- package/src/assets/web-panel/assets/{NotificationSettings-C4ikBOsm.js → NotificationSettings-Bs4-Fc6b.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-BpmdunAz.js → OrderTableRenderer-CEy1pIeq.js} +1 -1
- package/src/assets/web-panel/assets/Organization-D4cJ1UNo.js +4 -0
- package/src/assets/web-panel/assets/{Overflow-B-4_IpbB.js → Overflow-fdzvlF7x.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-DdOf9BZs.js → P2P-Y13PwXBA.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-DDU2Zc9Q.js → PdhVaultBrowser-Ck1zCLe6.js} +3 -3
- package/src/assets/web-panel/assets/Permissions-CvEXP6LC.js +4 -0
- package/src/assets/web-panel/assets/{PersonalDataHub-B3WcvKni.js → PersonalDataHub-JeP7IWMW.js} +3 -3
- package/src/assets/web-panel/assets/{Pipeline-DASuPDc3.js → Pipeline-BZ7wRzG1.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-DnixwmhJ.js → Privacy-BJ6qj9Hv.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-BQjMDkML.js → ProjectInit-DeWd9UcS.js} +2 -2
- package/src/assets/web-panel/assets/ProjectSettings-DUpVuU9F.js +2 -0
- package/src/assets/web-panel/assets/{Projects-BGWEJIVT.js → Projects-y1WbI337.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-Bl0dw6cI.js → Providers-DECW00ek.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-BDrk7l3t.js → QuickAsk-Dl-pK9Io.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-BPKof_CA.js → Recommend-Bju04wTd.js} +1 -1
- package/src/assets/web-panel/assets/{Reputation-6MNJLklK.js → Reputation-BUPa3nEk.js} +1 -1
- package/src/assets/web-panel/assets/{Row-DmGJwpfd.js → Row-WYqqaq_5.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-9-Fxh3_w.js → RssFeed-iyQT5q9l.js} +3 -3
- package/src/assets/web-panel/assets/{Search-CCHjey_D.js → Search-CrfwJF0R.js} +1 -1
- package/src/assets/web-panel/assets/{Security-D6CySV7r.js → Security-c4Jxf5Ih.js} +4 -4
- package/src/assets/web-panel/assets/{Services-Co8Mk8qE.js → Services-CRuisolj.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-BdON8M-1.js → Skeleton-CCnzJkb-.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-CEJE1Go1.js → Skills-CZURCwZg.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-BSxFvFX9.js → Sla-CrMzaEi2.js} +1 -1
- package/src/assets/web-panel/assets/SpeechSettings-jTDOM5eY.js +1 -0
- package/src/assets/web-panel/assets/{SyncSettings-52NXsfyj.js → SyncSettings-CJWVtd87.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-He7CweW6.js → Tasks-61lrQ_lS.js} +1 -1
- package/src/assets/web-panel/assets/Templates-Cg-iF2g1.js +1 -0
- package/src/assets/web-panel/assets/{Tenant-Dd5XNbYF.js → Tenant-DrDv1FFc.js} +1 -1
- package/src/assets/web-panel/assets/Terminal-CP15hcNS.js +3 -0
- package/src/assets/web-panel/assets/{TimelineRenderer-BdfqBQmM.js → TimelineRenderer-Sl5uXrkN.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-CsgNsrdQ.js → Tokens-yRIZTVsR.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-Dt_N_rvo.js → Trigger-BLCQEOF2.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-B1lJzsPn.js → Trust-D5xq8z6s.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-CW8YdFxg.js → UkeySign-Dwp0zXQ6.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-CU6o7qvp.js → VideoEditing-CIHA55Sx.js} +1 -1
- package/src/assets/web-panel/assets/Wallet-Hjajvnto.js +4 -0
- package/src/assets/web-panel/assets/WebAuthn-DqSURUvI.js +5 -0
- package/src/assets/web-panel/assets/{WorkflowEditor-BcCA2U8H.js → WorkflowEditor-B5bHRwUG.js} +1 -1
- package/src/assets/web-panel/assets/{chat-Cxl5DGsc.js → chat-DsheLKkG.js} +1 -1
- package/src/assets/web-panel/assets/{colors-FS_7Xggs.js → colors-CST2UkgL.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-BzXjd3wJ.js → compact-item-yqEoy1L7.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-DhF9bi_u.js → createContext-Jwp78HFY.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-C8to6gQk.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-Y1a0aV9N.js → hasIn-CVS5mFEP.js} +1 -1
- package/src/assets/web-panel/assets/{index-DfaSKEVD.js → index-AnW25bEq.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dmjxpu0L.js → index-BCHAUdel.js} +1 -1
- package/src/assets/web-panel/assets/{index-B48PZ-xM.js → index-BLTUVd0V.js} +1 -1
- package/src/assets/web-panel/assets/{index-CRSkKj9M.js → index-BeblNJDH.js} +1 -1
- package/src/assets/web-panel/assets/{index-uLoKviUT.js → index-BuI27WSx.js} +1 -1
- package/src/assets/web-panel/assets/{index-BTltEtoG.js → index-BzetOasL.js} +1 -1
- package/src/assets/web-panel/assets/{index-CETvk0NN.js → index-C0FZScMe.js} +1 -1
- package/src/assets/web-panel/assets/{index-DZJMsVI1.js → index-C130LLPq.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cgo1J_Kf.js → index-C8rq85gu.js} +1 -1
- package/src/assets/web-panel/assets/{index-BBtULjKY.js → index-CDAPnZUs.js} +1 -1
- package/src/assets/web-panel/assets/{index-BepInmSi.js → index-CIE2n8k_.js} +1 -1
- package/src/assets/web-panel/assets/{index-D3IVAG9l.js → index-CRBbVS43.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ci7PSoQn.js → index-CSBPc-sI.js} +1 -1
- package/src/assets/web-panel/assets/{index-CxYHV8nb.js → index-CTd3lDxp.js} +1 -1
- package/src/assets/web-panel/assets/index-CUWj5L2s.js +1 -0
- package/src/assets/web-panel/assets/{index-CzlCP4jP.js → index-ChRL6rgA.js} +1 -1
- package/src/assets/web-panel/assets/{index-DVTex5M8.js → index-CjfN2CpJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-1iacQgo5.js → index-Cjig5i3a.js} +3 -3
- package/src/assets/web-panel/assets/{index-JUukFpxi.js → index-Crk4KWLW.js} +1 -1
- package/src/assets/web-panel/assets/{index-BllceSdr.js → index-CrlRa054.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cn9lbqaS.js → index-CtyEOGuj.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bt_0ygjA.js → index-Cu6Ql64n.js} +1 -1
- package/src/assets/web-panel/assets/{index-B_Z0PHC0.js → index-Cx_t5rWw.js} +1 -1
- package/src/assets/web-panel/assets/{index-CELVZPNt.js → index-Cy0dNzkp.js} +1 -1
- package/src/assets/web-panel/assets/{index-GCd5hs58.js → index-CzsKK0tQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BnvaUocg.js → index-DDAigsel.js} +1 -1
- package/src/assets/web-panel/assets/index-DZfnYE6e.js +1 -0
- package/src/assets/web-panel/assets/{index-C2S8McM1.js → index-D_1b7uh6.js} +1 -1
- package/src/assets/web-panel/assets/{index--fcP34eT.js → index-D_e9gwfn.js} +1 -1
- package/src/assets/web-panel/assets/{index-DgHWZVjv.js → index-DbxAlpPC.js} +1 -1
- package/src/assets/web-panel/assets/{index-BNmWkPqm.js → index-De600Jjm.js} +1 -1
- package/src/assets/web-panel/assets/{index-DxGVbuqr.js → index-Dgyn8-wN.js} +1 -1
- package/src/assets/web-panel/assets/{index-BzQkdgQW.js → index-DiK4vSZa.js} +1 -1
- package/src/assets/web-panel/assets/{index-CuTKOay7.js → index-DpYn5v2k.js} +1 -1
- package/src/assets/web-panel/assets/{index-Sl-sLj3T.js → index-DxaU_lza.js} +1 -1
- package/src/assets/web-panel/assets/{index-Na733kBa.js → index-Dxljh9Fu.js} +1 -1
- package/src/assets/web-panel/assets/{index-DLyIxnjc.js → index-R-VGFpi6.js} +1 -1
- package/src/assets/web-panel/assets/{index-DUcVF8e6.js → index-SjxCCf5h.js} +1 -1
- package/src/assets/web-panel/assets/{index-1khPb4ZS.js → index-diWkx2Wq.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-D6QmlnQ-.js → initDefaultProps-ClAjrsQ-.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BHrTGY1_.js → motion-BalXv_60.js} +1 -1
- package/src/assets/web-panel/assets/{move-Bpph6B_N.js → move-DMelzIW-.js} +1 -1
- package/src/assets/web-panel/assets/{omit-Dx0YMbWn.js → omit-oxecfU3b.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-D-a8Fm6C.js → pickAttrs-DJ5F5M6x.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-DXFSVgxv.js → placementArrow-DdbKsant.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-CxhQ1EaK.js → responsiveObserve-BUuM5cmS.js} +1 -1
- package/src/assets/web-panel/assets/{slide-IlBI0XU8.js → slide-DSV0ccvR.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-COoXVzje.js → statusUtils-B-6oLAXf.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-DCuq89M_.js → styleChecker-DdCAHtsZ.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-tfNE1ALO.js → useFlexGapSupport-ZORkcoZd.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-DD4bzBkU.js → useFs-BFp46LoP.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-DVkH1hbT.js → usePersonalDataHub-H7rxgbdW.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-D1ZmXyxM.js → vnode-eIq4Tk0J.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-DblBUHfi.js → zoom-CTNrv8-H.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/ask.js +24 -1
- package/src/commands/init.js +5 -4
- package/src/commands/instinct.js +18 -5
- package/src/commands/mcp.js +30 -3
- package/src/commands/memory.js +18 -5
- package/src/gateways/http/envelope-http-server.js +17 -2
- package/src/gateways/terminal/PtyManager.js +15 -2
- package/src/gateways/ws/ws-server.js +20 -0
- package/src/gateways/ws/ws-session-gateway.js +16 -0
- package/src/harness/mcp-client.js +57 -0
- package/src/harness/worktree-isolator.js +20 -9
- package/src/lib/agent-core.js +3 -0
- package/src/lib/autonomous-agent.js +7 -6
- package/src/lib/chat-intent-service.js +4 -2
- package/src/lib/checkpoint-store.js +23 -0
- package/src/lib/credential-guard.js +7 -0
- package/src/lib/git-integration.js +57 -1
- package/src/lib/hook-runner.cjs +11 -1
- package/src/lib/interactive-planner.js +4 -3
- package/src/lib/json-schema-output.js +47 -0
- package/src/lib/learning/reflection-engine.js +4 -3
- package/src/lib/learning/skill-improver.js +4 -3
- package/src/lib/learning/skill-synthesizer.js +4 -3
- package/src/lib/mcp-oauth.js +208 -20
- package/src/lib/mcp-serve.js +56 -6
- package/src/lib/orchestrator.js +4 -2
- package/src/lib/process-manager.js +31 -3
- package/src/lib/repl-completer.js +29 -0
- package/src/lib/slot-filler.js +4 -3
- package/src/lib/sub-agent-context.js +6 -0
- package/src/lib/web-fetch.js +54 -3
- package/src/lib/workflow-engine.js +35 -11
- package/src/runtime/agent-core.js +248 -26
- package/src/runtime/headless-stream.js +124 -17
- package/src/assets/web-panel/assets/Analytics-CzxpcV9E.js +0 -3
- package/src/assets/web-panel/assets/Backup-C2Y_XNBA.js +0 -1
- package/src/assets/web-panel/assets/ChatBubbleRenderer-TeKnMwPj.js +0 -1
- package/src/assets/web-panel/assets/Compliance-DFeWryxt.js +0 -1
- package/src/assets/web-panel/assets/KnowledgeGraph-h-nL7J6x.js +0 -1
- package/src/assets/web-panel/assets/Logs-DjuWREBJ.js +0 -2
- package/src/assets/web-panel/assets/MobileBridge-4jAehfcz.js +0 -3
- package/src/assets/web-panel/assets/Organization-BBEmFNGM.js +0 -4
- package/src/assets/web-panel/assets/Permissions-B1ebgevF.js +0 -4
- package/src/assets/web-panel/assets/ProjectSettings-dHyZ3SxW.js +0 -2
- package/src/assets/web-panel/assets/SpeechSettings-C3rShYlV.js +0 -1
- package/src/assets/web-panel/assets/Templates-UtjBlJIO.js +0 -1
- package/src/assets/web-panel/assets/Terminal-pFyh42cu.js +0 -3
- package/src/assets/web-panel/assets/Wallet-DPxjEhbN.js +0 -4
- package/src/assets/web-panel/assets/WebAuthn-CuJ29YAF.js +0 -5
- package/src/assets/web-panel/assets/devWarning-B1j78JeH.js +0 -1
- package/src/assets/web-panel/assets/index-BaN6wKWx.js +0 -1
- package/src/assets/web-panel/assets/index-N7JKRLCC.js +0 -1
|
@@ -13,7 +13,9 @@ import { resolve } from "node:path";
|
|
|
13
13
|
import {
|
|
14
14
|
isGitRepo,
|
|
15
15
|
gitExec,
|
|
16
|
+
gitExecArgs,
|
|
16
17
|
assertSafeGitRef,
|
|
18
|
+
assertSafeGitPath,
|
|
17
19
|
} from "../lib/git-integration.js";
|
|
18
20
|
|
|
19
21
|
const WORKTREE_DIR = ".worktrees";
|
|
@@ -167,6 +169,7 @@ export function diffWorktree(repoDir, branchName, options = {}) {
|
|
|
167
169
|
if (options.baseBranch) assertSafeGitRef(options.baseBranch, "base branch");
|
|
168
170
|
const base = options.baseBranch || "HEAD";
|
|
169
171
|
const filePath = options.filePath || null;
|
|
172
|
+
if (filePath) assertSafeGitPath(filePath, "file path");
|
|
170
173
|
const fileArg = filePath ? ` -- "${filePath}"` : "";
|
|
171
174
|
const statOutput = gitExec(
|
|
172
175
|
`diff ${base}...${branchName} --stat --numstat${fileArg}`,
|
|
@@ -223,14 +226,13 @@ export function mergeWorktree(repoDir, branchName, options = {}) {
|
|
|
223
226
|
const msg =
|
|
224
227
|
options.message ||
|
|
225
228
|
`feat: merge agent work from ${branchName} (squashed)`;
|
|
226
|
-
|
|
229
|
+
// Free-text message → argv (no shell) so `$()`/backticks/quotes in it
|
|
230
|
+
// can't inject a command (the old `-m "…"` only escaped `"`).
|
|
231
|
+
gitExecArgs(["commit", "-m", msg], repoDir);
|
|
227
232
|
} else {
|
|
228
233
|
const msg =
|
|
229
234
|
options.message || `Merge branch '${branchName}' (agent task)`;
|
|
230
|
-
|
|
231
|
-
`merge ${branchName} --no-edit -m "${msg.replace(/"/g, '\\"')}"`,
|
|
232
|
-
repoDir,
|
|
233
|
-
);
|
|
235
|
+
gitExecArgs(["merge", branchName, "--no-edit", "-m", msg], repoDir);
|
|
234
236
|
}
|
|
235
237
|
|
|
236
238
|
if (deleteBranch) {
|
|
@@ -393,6 +395,7 @@ export function applyWorktreeAutomationCandidate(
|
|
|
393
395
|
const conflictType = options.conflictType || null;
|
|
394
396
|
const baseBranch = _resolveBaseBranchForMerge(repoDir, options.baseBranch);
|
|
395
397
|
|
|
398
|
+
if (filePath) assertSafeGitPath(filePath, "file path");
|
|
396
399
|
if (!filePath) {
|
|
397
400
|
throw new Error("filePath is required");
|
|
398
401
|
}
|
|
@@ -514,8 +517,10 @@ export function worktreeLog(repoDir, branchName, baseBranch = "HEAD") {
|
|
|
514
517
|
|
|
515
518
|
function _fileStatus(repoDir, base, branch, filePath) {
|
|
516
519
|
try {
|
|
517
|
-
|
|
518
|
-
|
|
520
|
+
// filePath here comes from git's conflict output (a tracked file name);
|
|
521
|
+
// pass it via argv so an exotic / maliciously-named file can't inject.
|
|
522
|
+
const output = gitExecArgs(
|
|
523
|
+
["diff", `${base}...${branch}`, "--name-status", "--", filePath],
|
|
519
524
|
repoDir,
|
|
520
525
|
);
|
|
521
526
|
const status = output.trim().charAt(0);
|
|
@@ -603,7 +608,10 @@ function _buildConflictSummary(repoDir, filePath, branchName) {
|
|
|
603
608
|
|
|
604
609
|
function _conflictStatusCode(repoDir, filePath) {
|
|
605
610
|
try {
|
|
606
|
-
const output =
|
|
611
|
+
const output = gitExecArgs(
|
|
612
|
+
["status", "--porcelain", "--", filePath],
|
|
613
|
+
repoDir,
|
|
614
|
+
);
|
|
607
615
|
const firstLine = output.split("\n").find((line) => line.trim());
|
|
608
616
|
return firstLine ? firstLine.slice(0, 2) : "UU";
|
|
609
617
|
} catch (_e) {
|
|
@@ -779,7 +787,10 @@ function _buildDiffPreview(repoDir, filePath, branchName) {
|
|
|
779
787
|
};
|
|
780
788
|
|
|
781
789
|
try {
|
|
782
|
-
const diff =
|
|
790
|
+
const diff = gitExecArgs(
|
|
791
|
+
["diff", `HEAD...${branchName}`, "--", filePath],
|
|
792
|
+
repoDir,
|
|
793
|
+
);
|
|
783
794
|
preview.snippet =
|
|
784
795
|
diff.length > 2000
|
|
785
796
|
? `${diff.slice(0, 2000)}\n... [diff truncated]`
|
package/src/lib/agent-core.js
CHANGED
|
@@ -3,8 +3,11 @@ export {
|
|
|
3
3
|
AGENT_TOOLS,
|
|
4
4
|
AGENT_TOOL_REGISTRY,
|
|
5
5
|
MAX_SUB_AGENT_DEPTH,
|
|
6
|
+
MAX_SUB_AGENTS_PER_RUN,
|
|
6
7
|
MAX_TOOL_RESULT_CHARS,
|
|
7
8
|
capToolResultString,
|
|
9
|
+
safeStringifyToolResult,
|
|
10
|
+
tokenizeShellWords,
|
|
8
11
|
reloadSkills,
|
|
9
12
|
getAgentToolDefinitions,
|
|
10
13
|
getAgentToolDescriptors,
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { EventEmitter } from "events";
|
|
11
|
+
import { firstBalancedJson } from "./json-schema-output.js";
|
|
11
12
|
|
|
12
13
|
// Exported for test injection
|
|
13
14
|
export const _deps = {
|
|
@@ -471,10 +472,10 @@ Reply with a JSON object: { "action": "retry|add_step|skip", "newParams": {...},
|
|
|
471
472
|
|
|
472
473
|
_parseSteps(text) {
|
|
473
474
|
try {
|
|
474
|
-
// Extract JSON array from response
|
|
475
|
-
const
|
|
476
|
-
if (
|
|
477
|
-
return JSON.parse(
|
|
475
|
+
// Extract the first balanced JSON array from the response
|
|
476
|
+
const jsonText = firstBalancedJson(text, "[");
|
|
477
|
+
if (jsonText) {
|
|
478
|
+
return JSON.parse(jsonText);
|
|
478
479
|
}
|
|
479
480
|
} catch (_err) {
|
|
480
481
|
// Parse failure
|
|
@@ -484,8 +485,8 @@ Reply with a JSON object: { "action": "retry|add_step|skip", "newParams": {...},
|
|
|
484
485
|
|
|
485
486
|
_parseJSON(text) {
|
|
486
487
|
try {
|
|
487
|
-
const
|
|
488
|
-
if (
|
|
488
|
+
const jsonText = firstBalancedJson(text, "{");
|
|
489
|
+
if (jsonText) return JSON.parse(jsonText);
|
|
489
490
|
} catch (_err) {
|
|
490
491
|
// Parse failure
|
|
491
492
|
}
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
import { chatWithStreaming, chatStream } from "./chat-core.js";
|
|
21
|
+
import { firstBalancedJson } from "./json-schema-output.js";
|
|
21
22
|
|
|
22
23
|
const DEFAULT_INTENT_TIMEOUT_MS = 15000;
|
|
23
24
|
|
|
@@ -76,8 +77,9 @@ function extractJson(content) {
|
|
|
76
77
|
if (fenced) return fenced[1].trim();
|
|
77
78
|
const generic = content.match(/```\s*([\s\S]*?)```/);
|
|
78
79
|
if (generic) return generic[1].trim();
|
|
79
|
-
|
|
80
|
-
|
|
80
|
+
// Balanced extraction stops at the first complete object instead of the old
|
|
81
|
+
// greedy /\{[\s\S]*\}/, which over-captured trailing prose with a stray }.
|
|
82
|
+
return firstBalancedJson(content, "{");
|
|
81
83
|
}
|
|
82
84
|
|
|
83
85
|
/**
|
|
@@ -256,6 +256,29 @@ export function createCheckpoint(cwd = process.cwd(), opts = {}) {
|
|
|
256
256
|
/* non-critical */
|
|
257
257
|
}
|
|
258
258
|
|
|
259
|
+
// Bound the session's checkpoint history when asked. Auto-checkpointing
|
|
260
|
+
// (one ref per mutating tool) passes a cap so a long agentic run can't
|
|
261
|
+
// accumulate unbounded refs — which also keeps nextId's per-create
|
|
262
|
+
// for-each-ref scan bounded. Best-effort: pruning never fails the
|
|
263
|
+
// checkpoint, and the dropped commit objects stay reachable via newer
|
|
264
|
+
// checkpoints' parent chain (only addressability by id is lost for the
|
|
265
|
+
// oldest entries). Manual `cc checkpoint create` omits the cap → unbounded.
|
|
266
|
+
if (Number.isFinite(opts.maxPerSession) && opts.maxPerSession > 0) {
|
|
267
|
+
try {
|
|
268
|
+
const rows = listRefs(root, session); // oldest-first (creatordate)
|
|
269
|
+
const excess = rows.length - opts.maxPerSession;
|
|
270
|
+
for (let i = 0; i < excess; i++) {
|
|
271
|
+
try {
|
|
272
|
+
git(["update-ref", "-d", rows[i].ref], { cwd: root });
|
|
273
|
+
} catch {
|
|
274
|
+
/* best-effort — a failed prune never affects the new checkpoint */
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
} catch {
|
|
278
|
+
/* pruning is entirely best-effort */
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
259
282
|
return { id, ref, commit, tree, parent, label, session, createdAt, files };
|
|
260
283
|
} finally {
|
|
261
284
|
rmQuiet(tmpIndex);
|
|
@@ -51,6 +51,12 @@ const CREDENTIAL_BASENAMES = new Set([
|
|
|
51
51
|
"id_dsa",
|
|
52
52
|
"id_ecdsa",
|
|
53
53
|
"id_ed25519", // private SSH keys (the `.pub` siblings have a different basename)
|
|
54
|
+
"id_ecdsa_sk",
|
|
55
|
+
"id_ed25519_sk", // FIDO/security-key SSH keys
|
|
56
|
+
".htpasswd", // web-server basic-auth hashes
|
|
57
|
+
"kubeconfig", // bare KUBECONFIG file (the ~/.kube/config form is matched below)
|
|
58
|
+
".terraformrc", // Terraform registry/API tokens
|
|
59
|
+
"terraform.rc",
|
|
54
60
|
]);
|
|
55
61
|
|
|
56
62
|
// Extensions whose BYTES are the secret (private keys / cert bundles / keyrings).
|
|
@@ -67,6 +73,7 @@ const CREDENTIAL_PATH_RE = [
|
|
|
67
73
|
/[\\/]\.docker[\\/]config\.json$/i,
|
|
68
74
|
/[\\/]\.config[\\/]gh[\\/]hosts\.yml$/i, // GitHub CLI OAuth token
|
|
69
75
|
/[\\/]\.gnupg[\\/]/i, // GPG keyring directory
|
|
76
|
+
/service[-_]account[^\\/]*\.json$/i, // GCP service-account key JSON
|
|
70
77
|
];
|
|
71
78
|
|
|
72
79
|
// secret(s).{json,yml,yaml,env,txt}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Git integration — wraps system git commands for knowledge base versioning.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { execSync } from "child_process";
|
|
5
|
+
import { execSync, spawnSync } from "child_process";
|
|
6
6
|
import { existsSync, writeFileSync, chmodSync } from "fs";
|
|
7
7
|
import { join } from "path";
|
|
8
8
|
|
|
@@ -41,6 +41,62 @@ export function assertSafeGitRef(ref, label = "git ref") {
|
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Validate a repo-relative file path before it is interpolated into a shell git
|
|
46
|
+
* command (e.g. `diff … -- "${filePath}"`). Double-quoting does NOT neutralize
|
|
47
|
+
* `$()` / backticks / a closing `"`, so an unsanitized path from a WS frame is a
|
|
48
|
+
* shell-injection vector. Allow only ordinary path characters (no shell
|
|
49
|
+
* metacharacters), reject absolute paths, a leading `-` (flag injection), and
|
|
50
|
+
* `..` (traversal). Exported for reuse + tests.
|
|
51
|
+
*
|
|
52
|
+
* @param {string} p
|
|
53
|
+
* @param {string} [label="file path"]
|
|
54
|
+
*/
|
|
55
|
+
export function assertSafeGitPath(p, label = "file path") {
|
|
56
|
+
if (typeof p !== "string" || p.length === 0) {
|
|
57
|
+
throw new Error(`Invalid ${label}: expected a non-empty string`);
|
|
58
|
+
}
|
|
59
|
+
if (
|
|
60
|
+
!/^[A-Za-z0-9._/+@=, -]+$/.test(p) || // safe path chars only — no shell metachars
|
|
61
|
+
p.startsWith("-") || // no flag injection (e.g. `-- --output=…`)
|
|
62
|
+
p.startsWith("/") || // no absolute path (posix)
|
|
63
|
+
/^[A-Za-z]:/.test(p) || // no absolute path (windows drive)
|
|
64
|
+
p.includes("..") // no traversal / range tricks
|
|
65
|
+
) {
|
|
66
|
+
throw new Error(`Unsafe ${label}: ${JSON.stringify(p)}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Run git with an ARGV array (no shell) — for commands that carry free-text the
|
|
72
|
+
* caller can't allowlist (a commit / merge message). spawnSync passes each arg
|
|
73
|
+
* verbatim, so `$()`, backticks, quotes, and `;` in the message are inert
|
|
74
|
+
* instead of being interpreted by a shell as they would be through `gitExec`.
|
|
75
|
+
* Throws git's stderr on failure, mirroring `gitExec`'s contract.
|
|
76
|
+
*
|
|
77
|
+
* @param {string[]} args
|
|
78
|
+
* @param {string} cwd
|
|
79
|
+
* @param {object} [opts] { input }
|
|
80
|
+
* @returns {string} trimmed stdout
|
|
81
|
+
*/
|
|
82
|
+
export function gitExecArgs(args, cwd, opts = {}) {
|
|
83
|
+
const res = spawnSync("git", args, {
|
|
84
|
+
cwd,
|
|
85
|
+
encoding: "utf-8",
|
|
86
|
+
windowsHide: true,
|
|
87
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
88
|
+
input: opts.input,
|
|
89
|
+
});
|
|
90
|
+
if (res.error) throw res.error;
|
|
91
|
+
if (res.status !== 0) {
|
|
92
|
+
const stderr = (res.stderr || res.stdout || "").toString().trim();
|
|
93
|
+
throw new Error(
|
|
94
|
+
stderr || `git ${args.join(" ")} failed (exit ${res.status})`,
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
return (res.stdout || "").toString().trim();
|
|
98
|
+
}
|
|
99
|
+
|
|
44
100
|
/**
|
|
45
101
|
* Run a git command and return stdout.
|
|
46
102
|
*/
|
package/src/lib/hook-runner.cjs
CHANGED
|
@@ -107,7 +107,17 @@ function tryParseDecision(stdout) {
|
|
|
107
107
|
* nonBlockingError?:boolean, error?:string }}
|
|
108
108
|
*/
|
|
109
109
|
function runCommandHook(command, input = {}, opts = {}) {
|
|
110
|
-
const {
|
|
110
|
+
const { cwd, event } = opts;
|
|
111
|
+
// spawnSync is SYNCHRONOUS — it blocks the whole agent until the hook returns
|
|
112
|
+
// or its timeout fires. Node treats a 0/undefined timeout as UNLIMITED, so a
|
|
113
|
+
// missing, zero, NaN, or negative value (e.g. a malformed settings.json
|
|
114
|
+
// `timeout`) would let a hung hook freeze the agent forever. Guarantee a
|
|
115
|
+
// positive, bounded timeout regardless of the caller's value (already in ms).
|
|
116
|
+
const _rawTimeout = Number(opts.timeout);
|
|
117
|
+
const timeout =
|
|
118
|
+
Number.isFinite(_rawTimeout) && _rawTimeout > 0
|
|
119
|
+
? Math.min(_rawTimeout, 600000)
|
|
120
|
+
: 60000;
|
|
111
121
|
if (!command) {
|
|
112
122
|
return { decision: HOOK_DECISIONS.CONTINUE, reason: null, exitCode: 0 };
|
|
113
123
|
}
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import { EventEmitter } from "events";
|
|
11
11
|
import { createHash } from "crypto";
|
|
12
|
+
import { firstBalancedJson } from "./json-schema-output.js";
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Plan session statuses
|
|
@@ -345,12 +346,12 @@ Keep plans concise (3-8 steps). Use appropriate tools for each step.`;
|
|
|
345
346
|
]);
|
|
346
347
|
|
|
347
348
|
const content = response?.message?.content || response?.content || "";
|
|
348
|
-
const
|
|
349
|
-
if (!
|
|
349
|
+
const jsonText = firstBalancedJson(content, "{");
|
|
350
|
+
if (!jsonText) {
|
|
350
351
|
throw new Error("Failed to parse plan from LLM response");
|
|
351
352
|
}
|
|
352
353
|
|
|
353
|
-
return JSON.parse(
|
|
354
|
+
return JSON.parse(jsonText);
|
|
354
355
|
}
|
|
355
356
|
|
|
356
357
|
/**
|
|
@@ -80,6 +80,48 @@ export function validateAgainstSchema(value, schema, path = "$") {
|
|
|
80
80
|
return errors;
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
/**
|
|
84
|
+
* Return the first *balanced* JSON object/array substring in `text`, honoring
|
|
85
|
+
* string literals so braces inside strings don't throw off the depth count.
|
|
86
|
+
*
|
|
87
|
+
* Unlike a greedy `/\{[\s\S]*\}/` match (which spans to the LAST bracket and
|
|
88
|
+
* over-captures trailing prose or a second object — making JSON.parse throw),
|
|
89
|
+
* this stops at the first complete top-level value.
|
|
90
|
+
*
|
|
91
|
+
* @param {string} text
|
|
92
|
+
* @param {"{"|"["} [only] restrict to object- or array-openers when set
|
|
93
|
+
* @returns {string|null} the balanced substring, or null if none is found
|
|
94
|
+
*/
|
|
95
|
+
export function firstBalancedJson(text, only) {
|
|
96
|
+
const s = String(text || "");
|
|
97
|
+
for (let i = 0; i < s.length; i++) {
|
|
98
|
+
const open = s[i];
|
|
99
|
+
if (open !== "{" && open !== "[") continue;
|
|
100
|
+
if (only && open !== only) continue;
|
|
101
|
+
const close = open === "{" ? "}" : "]";
|
|
102
|
+
let depth = 0;
|
|
103
|
+
let inStr = false;
|
|
104
|
+
let esc = false;
|
|
105
|
+
for (let j = i; j < s.length; j++) {
|
|
106
|
+
const c = s[j];
|
|
107
|
+
if (inStr) {
|
|
108
|
+
if (esc) esc = false;
|
|
109
|
+
else if (c === "\\") esc = true;
|
|
110
|
+
else if (c === '"') inStr = false;
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (c === '"') inStr = true;
|
|
114
|
+
else if (c === open) depth++;
|
|
115
|
+
else if (c === close) {
|
|
116
|
+
depth--;
|
|
117
|
+
if (depth === 0) return s.slice(i, j + 1);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// opener at i never closed — try the next opener
|
|
121
|
+
}
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
|
|
83
125
|
/** Pull a JSON payload out of an LLM reply (bare, fenced, or embedded). */
|
|
84
126
|
export function extractJsonPayload(text) {
|
|
85
127
|
const raw = String(text || "").trim();
|
|
@@ -87,6 +129,11 @@ export function extractJsonPayload(text) {
|
|
|
87
129
|
tries.push(raw);
|
|
88
130
|
const fence = /```(?:json)?\s*([\s\S]*?)```/i.exec(raw);
|
|
89
131
|
if (fence) tries.push(fence[1].trim());
|
|
132
|
+
// Prefer a balanced run (stops at the first complete value) over the greedy
|
|
133
|
+
// first-bracket..last-bracket slice below, which over-captures on trailing
|
|
134
|
+
// prose or a second object.
|
|
135
|
+
const balanced = firstBalancedJson(raw);
|
|
136
|
+
if (balanced) tries.push(balanced);
|
|
90
137
|
const firstObj = raw.indexOf("{");
|
|
91
138
|
const lastObj = raw.lastIndexOf("}");
|
|
92
139
|
if (firstObj !== -1 && lastObj > firstObj)
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import { extractToolNames } from "./skill-synthesizer.js";
|
|
15
|
+
import { firstBalancedJson } from "../json-schema-output.js";
|
|
15
16
|
|
|
16
17
|
// ── _deps for test injection ────────────────────────
|
|
17
18
|
const _deps = {
|
|
@@ -268,9 +269,9 @@ export class ReflectionEngine {
|
|
|
268
269
|
try {
|
|
269
270
|
const messages = buildReflectionPrompt(reportData);
|
|
270
271
|
const response = await this.llmChat(messages);
|
|
271
|
-
const
|
|
272
|
-
if (!
|
|
273
|
-
return JSON.parse(
|
|
272
|
+
const jsonText = firstBalancedJson(response, "{");
|
|
273
|
+
if (!jsonText) return null;
|
|
274
|
+
return JSON.parse(jsonText);
|
|
274
275
|
} catch {
|
|
275
276
|
return null;
|
|
276
277
|
}
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import fs from "fs";
|
|
15
|
+
import { firstBalancedJson } from "../json-schema-output.js";
|
|
15
16
|
import path from "path";
|
|
16
17
|
|
|
17
18
|
// ── _deps for test injection ────────────────────────────
|
|
@@ -374,9 +375,9 @@ export class SkillImprover {
|
|
|
374
375
|
if (!this.llmChat) return null;
|
|
375
376
|
try {
|
|
376
377
|
const response = await this.llmChat(messages);
|
|
377
|
-
const
|
|
378
|
-
if (!
|
|
379
|
-
return JSON.parse(
|
|
378
|
+
const jsonText = firstBalancedJson(response, "{");
|
|
379
|
+
if (!jsonText) return null;
|
|
380
|
+
return JSON.parse(jsonText);
|
|
380
381
|
} catch {
|
|
381
382
|
return null;
|
|
382
383
|
}
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import fs from "fs";
|
|
20
|
+
import { firstBalancedJson } from "../json-schema-output.js";
|
|
20
21
|
import path from "path";
|
|
21
22
|
|
|
22
23
|
// ── _deps for test injection ────────────────────────────
|
|
@@ -258,9 +259,9 @@ export class SkillSynthesizer {
|
|
|
258
259
|
|
|
259
260
|
try {
|
|
260
261
|
// Try to parse JSON from response
|
|
261
|
-
const
|
|
262
|
-
if (!
|
|
263
|
-
return JSON.parse(
|
|
262
|
+
const jsonText = firstBalancedJson(response, "{");
|
|
263
|
+
if (!jsonText) return null;
|
|
264
|
+
return JSON.parse(jsonText);
|
|
264
265
|
} catch {
|
|
265
266
|
return null;
|
|
266
267
|
}
|