chainlesschain 0.162.101 → 0.162.102
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/assets/{AIOps-NeD6mA1O.js → AIOps-C1k00Gsz.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-Bhp7A7vO.js → ActionButton-BinI6ZDq.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-DgdI84Jy.js → Analytics-Cg0jo_cd.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-Qw29caa7.js → AppLayout-CClMs_AA.js} +5 -5
- package/src/assets/web-panel/assets/{Audit-DgkzhBjz.js → Audit-BtQgvFdM.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-CiWEQoW5.js → Backup-43FRi7ZI.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-BWNbkeDF.js → BaseInput-Cfza-FZU.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-CUcj4Vyd.js → Chat-7AjHSiSC.js} +6 -6
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-bAMNCgEj.js → ChatBubbleRenderer-Bh1YNdtx.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-B0uZTW8f.js → Checkbox-DgjfqlWW.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-BPk7b-Xa.js → Codegen-CkwOBThj.js} +1 -1
- package/src/assets/web-panel/assets/{Col-C2cV_dw4.js → Col-Cohk5WYM.js} +1 -1
- package/src/assets/web-panel/assets/{Community-CDN6Yggb.js → Community-D3LMGh3F.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-C4WPtW_V.js → Compact-DqoMJmh0.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-BGUfeH-3.js → Compliance-BVjWZlEj.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-CsnVgF-o.js → Cowork-C0lqQPqu.js} +2 -2
- package/src/assets/web-panel/assets/{Cron--rGanASk.js → Cron-DHd_PJZS.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-D_CLrd-0.js → Crosschain-BphK2Q9u.js} +1 -1
- package/src/assets/web-panel/assets/{DID-iYkbVc8W.js → DID-CWIw0aG4.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-CuFMU98t.js → Dashboard-CKgDU9U6.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-BPSjv_Ye.js → Dropdown-DkIlDUXF.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-DBmk7czO.js → EmailListRenderer-5sXA9hwZ.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-BaCArN11.js → FamilyGuardDashboard-CgcdJLH7.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-DsTV-8Ca.js → Federation-DPOcJISA.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-C6fRr_6F.js → FormItemContext-DxOutBMJ.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-AfuZCfyv.js → GenericCardRenderer-BYVNtkcd.js} +1 -1
- package/src/assets/web-panel/assets/{Git-CAFIrGhb.js → Git-B37LKxOu.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-C-NaxDs4.js → Governance-CnKcx5un.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-BPfeNZP7.js → Inference-CbRAWN3_.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DDUBte_m.js → KnowledgeGraph-D0w6RfE_.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-DWysA6JP.js → Logs-DxBDKpzq.js} +2 -2
- package/src/assets/web-panel/assets/{Marketplace-CAK68hXc.js → Marketplace-DNLi6O_3.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-CNmmoDAk.js → McpTools-s0sBCPXw.js} +4 -4
- package/src/assets/web-panel/assets/{Memory-D_KmFoui.js → Memory-BdSEgpmP.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-CaY-6kjj.js → MobileBridge-Dz7nz4K9.js} +2 -2
- package/src/assets/web-panel/assets/MobileProjects-DakdA2PC.js +1 -0
- package/src/assets/web-panel/assets/{Mtc-YnLrtuXT.js → Mtc-Be9OBypk.js} +6 -6
- package/src/assets/web-panel/assets/{MtcAudit-D38v836u.js → MtcAudit-KI2ih4Tl.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-Dv3ad9S4.js → Multisig-Cg3jHFjL.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-MYTh2eWC.js → NLProgramming-BepHx8fU.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-DYO-0Hva.js → Notes-2f05HyqN.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-D39gOb3h.js → NotificationSettings-Blwio_G5.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-CXdO0z3I.js → OrderTableRenderer-D7HJfvo_.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-DOYTwQwt.js → Organization-Uihlz1cI.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-Cm-go0yU.js → Overflow-gZx8kADL.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-CakGtorw.js → P2P-DqEEGqba.js} +2 -2
- package/src/assets/web-panel/assets/PdhVaultBrowser-BHUi1nb5.js +7 -0
- package/src/assets/web-panel/assets/{Permissions-C_podo04.js → Permissions-ERF2ZQ3G.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-nBbehIKp.js → PersonalDataHub-CzDy9CFB.js} +3 -3
- package/src/assets/web-panel/assets/{Pipeline-CGzQUoKv.js → Pipeline-Dy4x9Ejw.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-BuD-Dd9m.js → Privacy-C6UJys52.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-CLcFmGXU.js → ProjectInit-Bmi027fa.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-BxXTR0dH.js → ProjectSettings-NinSZmGA.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-DkpBjAfd.js → Projects-DeW-p-hT.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-DwHK42ht.js → Providers-4hxrBTLo.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-idJPKErk.js → QuickAsk-B5inZlW8.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-BvJj0Nu2.js → Recommend-7lpBm00-.js} +1 -1
- package/src/assets/web-panel/assets/{Reputation-BK9l4Rcf.js → Reputation-QfdGZxYO.js} +1 -1
- package/src/assets/web-panel/assets/{Row-rLxg7xk5.js → Row-DqHC7BCT.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-Cw6ZcHBY.js → RssFeed-B3F1aGP6.js} +3 -3
- package/src/assets/web-panel/assets/{Search-D8mKp506.js → Search-BXtjuoYO.js} +1 -1
- package/src/assets/web-panel/assets/{Security-CXunojmQ.js → Security-Iy0MOV-A.js} +4 -4
- package/src/assets/web-panel/assets/{Services-BE5DgmPl.js → Services-C_puMp_6.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-CssL5wmE.js → Skeleton-Bg6mTVZU.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-zyS_1YrT.js → Skills-D4wXhanR.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-DIVn2Roa.js → Sla-Cgr2B9R6.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-CjNdZWdj.js → SpeechSettings-ctYqZEY5.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-BCbu5WQT.js → SyncSettings-C3qWh9y4.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-BjFrgXge.js → Tasks-D3AAgUx1.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-Cl4Y3lKN.js → Templates-Bm_Dyz8T.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-k1T0F_eH.js → Tenant-DOSrOw9X.js} +1 -1
- package/src/assets/web-panel/assets/Terminal-DrG0bEOS.js +3 -0
- package/src/assets/web-panel/assets/TimelineRenderer-BB8Qq6Cx.js +1 -0
- package/src/assets/web-panel/assets/{Tokens-BUWEiyab.js → Tokens-DqyF_Flh.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-9srMXwpo.js → Trigger-ClwQC2hP.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-hmGor_R2.js → Trust-DGQSLPQ0.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-Dst7K10a.js → UkeySign-BAkeYsUO.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-D_utUv1u.js → VideoEditing-WyUxS65Y.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-BpYTTseI.js → Wallet-Br7GdjEg.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-CEIw8QrF.js → WebAuthn-bKs1dahj.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-Bkb4bUZ_.js → WorkflowEditor-CVqAALYe.js} +1 -1
- package/src/assets/web-panel/assets/{chat-CTqDgGRH.js → chat-BDGDRbab.js} +1 -1
- package/src/assets/web-panel/assets/{colors-CnJHVFLk.js → colors-DIUxZwT8.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-D4-v0ahl.js → compact-item-C2iM4GmN.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-CYDbnHZX.js → createContext-D27NeHwO.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-Daz0PHhS.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-CbYwlEz5.js → hasIn-D1lXdsFj.js} +1 -1
- package/src/assets/web-panel/assets/{index-BR4facv0.js → index-B5ZB5lsV.js} +1 -1
- package/src/assets/web-panel/assets/{index-DMKNVIqJ.js → index-B6kc4-nD.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cl7-mbsb.js → index-B8POWlbO.js} +1 -1
- package/src/assets/web-panel/assets/{index-CVc_5aBF.js → index-BE9bNvdn.js} +1 -1
- package/src/assets/web-panel/assets/{index-BCQ4kcL3.js → index-BIVYPlpl.js} +1 -1
- package/src/assets/web-panel/assets/{index-B8uhiRML.js → index-BTUFhfHs.js} +1 -1
- package/src/assets/web-panel/assets/{index-qNwkSspX.js → index-BTedVe_v.js} +1 -1
- package/src/assets/web-panel/assets/{index-OqmUv5QO.js → index-Bi8u5GsP.js} +1 -1
- package/src/assets/web-panel/assets/{index-CP-UKwu8.js → index-Bil9t91t.js} +1 -1
- package/src/assets/web-panel/assets/{index-CfshMjFN.js → index-BqbjsJcS.js} +1 -1
- package/src/assets/web-panel/assets/{index-tmNsJzQU.js → index-CAnMUhHO.js} +1 -1
- package/src/assets/web-panel/assets/{index-BOUSaCsn.js → index-CDzSUTpa.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dl_NdY39.js → index-CEO9vhv7.js} +3 -3
- package/src/assets/web-panel/assets/{index-DxvFrsxl.js → index-CFEwtE3q.js} +1 -1
- package/src/assets/web-panel/assets/{index-C2fICr5d.js → index-CFY1KjB1.js} +1 -1
- package/src/assets/web-panel/assets/{index-5NXdAIZK.js → index-CKMDgjfh.js} +1 -1
- package/src/assets/web-panel/assets/{index-BR0FfrCL.js → index-CNk98bb1.js} +1 -1
- package/src/assets/web-panel/assets/{index-DaCCLe7i.js → index-Ca8n97h9.js} +1 -1
- package/src/assets/web-panel/assets/{index-BGUOVPjO.js → index-Cb9u9MMo.js} +1 -1
- package/src/assets/web-panel/assets/index-Cbe_eJAR.js +1 -0
- package/src/assets/web-panel/assets/{index-CxKdSB-p.js → index-Cbqr8kW6.js} +1 -1
- package/src/assets/web-panel/assets/{index-CY971z_9.js → index-CfR3Pvsu.js} +1 -1
- package/src/assets/web-panel/assets/{index-DVKfa-z4.js → index-Cgf3zMwq.js} +1 -1
- package/src/assets/web-panel/assets/{index-4767GEls.js → index-CiA-L5ld.js} +1 -1
- package/src/assets/web-panel/assets/{index-CK8nROWH.js → index-CoygRreE.js} +1 -1
- package/src/assets/web-panel/assets/{index-B-hiXmVK.js → index-CsIw-YQF.js} +1 -1
- package/src/assets/web-panel/assets/{index-wxz2s6uW.js → index-DFhdJm7M.js} +1 -1
- package/src/assets/web-panel/assets/{index-8cmtr1jC.js → index-DK0jgmJg.js} +1 -1
- package/src/assets/web-panel/assets/{index-BygyTsjr.js → index-Dc4k4V1u.js} +1 -1
- package/src/assets/web-panel/assets/{index-D8MYVsVB.js → index-DfSav-yQ.js} +1 -1
- package/src/assets/web-panel/assets/index-DgcPjW1c.js +1 -0
- package/src/assets/web-panel/assets/{index-xDe5BkjU.js → index-DkOu8er7.js} +1 -1
- package/src/assets/web-panel/assets/{index-C4chfOjw.js → index-DnCVI295.js} +1 -1
- package/src/assets/web-panel/assets/{index-BNz3yht2.js → index-Do13fQ5W.js} +1 -1
- package/src/assets/web-panel/assets/{index-DdRtpQFq.js → index-DrS-Mrm-.js} +1 -1
- package/src/assets/web-panel/assets/{index-B78d_-pR.js → index-ET5LYk9B.js} +1 -1
- package/src/assets/web-panel/assets/{index-YuO8gxKb.js → index-HZGu1woY.js} +1 -1
- package/src/assets/web-panel/assets/{index-DjFQuno4.js → index-Y1rkRas5.js} +1 -1
- package/src/assets/web-panel/assets/{index-dKpwhR5x.js → index-yIM-KxVm.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-qCQgpAty.js → initDefaultProps-C0Yrc6yY.js} +1 -1
- package/src/assets/web-panel/assets/{motion-SkVSGdwu.js → motion-DnOI85ek.js} +1 -1
- package/src/assets/web-panel/assets/{move-BpWxaW_x.js → move-DT9zQPZp.js} +1 -1
- package/src/assets/web-panel/assets/{omit-DlZWM2H4.js → omit-DFtDi5Wd.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-DobtUnVe.js → pickAttrs-Cobkb_rd.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-CD6mYRZi.js → placementArrow-DWQDu4i-.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-DvIIMTD_.js → responsiveObserve-daoQcWcX.js} +1 -1
- package/src/assets/web-panel/assets/{slide-CVgyUfaA.js → slide-BLkzd2OV.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-DrUDnjy_.js → statusUtils-Uyxi81M0.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-BNYQS60m.js → styleChecker-Dtw5lUcs.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-DYv7Tvk0.js → useFlexGapSupport-_JpMgpdt.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-DmAD2atJ.js → useFs-huwAlPD-.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-DbLVKA3I.js → usePersonalDataHub-7cafj8_f.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-C3WEt6_P.js → vnode-DYUSfRt7.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-CRoSX_oZ.js → zoom-B-tjvmer.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/a2a.js +2 -1
- package/src/commands/bi.js +4 -3
- package/src/commands/browse.js +3 -2
- package/src/commands/codegen.js +7 -6
- package/src/commands/collab.js +2 -1
- package/src/commands/crosschain.js +5 -4
- package/src/commands/dao.js +10 -10
- package/src/commands/dbevo.js +3 -2
- package/src/commands/economy.js +8 -6
- package/src/commands/evolution.js +10 -8
- package/src/commands/export.js +2 -1
- package/src/commands/federation.js +8 -7
- package/src/commands/fusion.js +4 -3
- package/src/commands/git.js +2 -1
- package/src/commands/governance.js +6 -5
- package/src/commands/hardening.js +3 -2
- package/src/commands/hmemory.js +2 -1
- package/src/commands/inference.js +9 -8
- package/src/commands/infra.js +9 -8
- package/src/commands/instinct.js +66 -0
- package/src/commands/learning.js +66 -0
- package/src/commands/marketplace.js +5 -4
- package/src/commands/matrix.js +2 -1
- package/src/commands/memory.js +66 -0
- package/src/commands/nlprog.js +4 -3
- package/src/commands/nostr.js +4 -3
- package/src/commands/note.js +2 -1
- package/src/commands/ops.js +3 -2
- package/src/commands/perception.js +6 -5
- package/src/commands/privacy.js +13 -12
- package/src/commands/quantization.js +3 -2
- package/src/commands/recommend.js +8 -7
- package/src/commands/reputation.js +4 -3
- package/src/commands/runtime.js +6 -5
- package/src/commands/sandbox.js +5 -4
- package/src/commands/session.js +2 -1
- package/src/commands/sla.js +6 -5
- package/src/commands/social.js +6 -5
- package/src/commands/stress.js +7 -6
- package/src/commands/sync.js +2 -1
- package/src/commands/tenant.js +3 -2
- package/src/commands/trust.js +7 -6
- package/src/commands/wallet.js +2 -1
- package/src/lib/__tests__/agents.test.js +225 -0
- package/src/lib/__tests__/cowork-observe-html.test.js +141 -0
- package/src/lib/__tests__/logger.test.js +110 -0
- package/src/lib/__tests__/project-inventory.test.js +280 -0
- package/src/lib/__tests__/project-root.test.js +96 -0
- package/src/lib/__tests__/provider-stream.test.js +190 -0
- package/src/lib/cli-arg.js +41 -0
- package/src/lib/cli-numeric.js +12 -3
- package/src/lib/instinct-manager.js +64 -0
- package/src/lib/learning/learning-tables.js +83 -0
- package/src/lib/mcp-oauth.js +10 -2
- package/src/lib/memory-manager.js +67 -0
- package/src/lib/plan-mode.js +3 -2
- package/src/repl/__tests__/mcp-prompt.test.js +155 -0
- package/src/repl/__tests__/slash-macro.test.js +81 -0
- package/src/runtime/__tests__/coding-agent-contract-shared.test.js +136 -0
- package/src/runtime/__tests__/coding-agent-events.test.js +263 -0
- package/src/assets/web-panel/assets/MobileProjects-DhDxrWze.js +0 -1
- package/src/assets/web-panel/assets/PdhVaultBrowser-BlmbHJnb.js +0 -7
- package/src/assets/web-panel/assets/Terminal-i0tHF8fy.js +0 -3
- package/src/assets/web-panel/assets/TimelineRenderer-FmPaNyqK.js +0 -1
- package/src/assets/web-panel/assets/devWarning-OhxeajH6.js +0 -1
- package/src/assets/web-panel/assets/index-Bbtoi_47.js +0 -1
- package/src/assets/web-panel/assets/index-Cb8RWwaB.js +0 -1
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* agents tests — user-defined subagent discovery (.claude/agents/*.md), the
|
|
5
|
+
* Claude-Code parity layer, previously untested. Covers normalizeTools,
|
|
6
|
+
* parseAgentFile (frontmatter + body=systemPrompt, defaults, bad YAML),
|
|
7
|
+
* discoverAgents (path-derived names with `:`, frontmatter name override,
|
|
8
|
+
* project-shadows-personal precedence, sorting) and getAgent (colon/slash/
|
|
9
|
+
* leading-slash lookup). In-memory fake fs over POSIX paths via the deps seam;
|
|
10
|
+
* no `.git` present so projectRootBase resolves to null (cwd+home dirs only).
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { describe, it, expect } from "vitest";
|
|
14
|
+
import pathDefault from "path";
|
|
15
|
+
import {
|
|
16
|
+
normalizeTools,
|
|
17
|
+
parseAgentFile,
|
|
18
|
+
discoverAgents,
|
|
19
|
+
getAgent,
|
|
20
|
+
} from "../agents.js";
|
|
21
|
+
|
|
22
|
+
const P = pathDefault.posix;
|
|
23
|
+
|
|
24
|
+
function makeFs(files) {
|
|
25
|
+
const contents = new Map(Object.entries(files));
|
|
26
|
+
const fileSet = new Set(contents.keys());
|
|
27
|
+
const dirSet = new Set(["/"]);
|
|
28
|
+
for (const f of fileSet) {
|
|
29
|
+
let d = P.dirname(f);
|
|
30
|
+
while (d && !dirSet.has(d)) {
|
|
31
|
+
dirSet.add(d);
|
|
32
|
+
d = P.dirname(d);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const childrenOf = (dir) => {
|
|
36
|
+
const out = new Map();
|
|
37
|
+
for (const f of fileSet)
|
|
38
|
+
if (P.dirname(f) === dir) out.set(P.basename(f), false);
|
|
39
|
+
for (const d of dirSet)
|
|
40
|
+
if (d !== "/" && P.dirname(d) === dir) out.set(P.basename(d), true);
|
|
41
|
+
return out;
|
|
42
|
+
};
|
|
43
|
+
return {
|
|
44
|
+
readFileSync(p) {
|
|
45
|
+
if (!fileSet.has(p)) {
|
|
46
|
+
const e = new Error("ENOENT");
|
|
47
|
+
e.code = "ENOENT";
|
|
48
|
+
throw e;
|
|
49
|
+
}
|
|
50
|
+
return contents.get(p);
|
|
51
|
+
},
|
|
52
|
+
existsSync(p) {
|
|
53
|
+
return fileSet.has(p) || dirSet.has(p);
|
|
54
|
+
},
|
|
55
|
+
statSync(p) {
|
|
56
|
+
if (!fileSet.has(p) && !dirSet.has(p)) {
|
|
57
|
+
const e = new Error("ENOENT");
|
|
58
|
+
e.code = "ENOENT";
|
|
59
|
+
throw e;
|
|
60
|
+
}
|
|
61
|
+
const isDir = dirSet.has(p);
|
|
62
|
+
return { isDirectory: () => isDir, isFile: () => !isDir };
|
|
63
|
+
},
|
|
64
|
+
readdirSync(p, opts) {
|
|
65
|
+
if (!dirSet.has(p)) {
|
|
66
|
+
const e = new Error("ENOENT");
|
|
67
|
+
e.code = "ENOENT";
|
|
68
|
+
throw e;
|
|
69
|
+
}
|
|
70
|
+
const kids = childrenOf(p);
|
|
71
|
+
if (opts && opts.withFileTypes) {
|
|
72
|
+
return [...kids].map(([name, isDir]) => ({
|
|
73
|
+
name,
|
|
74
|
+
isDirectory: () => isDir,
|
|
75
|
+
isFile: () => !isDir,
|
|
76
|
+
}));
|
|
77
|
+
}
|
|
78
|
+
return [...kids.keys()];
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const discover = (files, home = "/home/u") =>
|
|
84
|
+
discoverAgents("/proj", { deps: { fs: makeFs(files), path: P }, home });
|
|
85
|
+
|
|
86
|
+
// --------------------------------------------------------------------------- //
|
|
87
|
+
// normalizeTools
|
|
88
|
+
// --------------------------------------------------------------------------- //
|
|
89
|
+
describe("normalizeTools", () => {
|
|
90
|
+
it("returns null for null/undefined", () => {
|
|
91
|
+
expect(normalizeTools(null)).toBe(null);
|
|
92
|
+
expect(normalizeTools(undefined)).toBe(null);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("splits a comma/space string into a trimmed list", () => {
|
|
96
|
+
expect(normalizeTools("read, write, run")).toEqual([
|
|
97
|
+
"read",
|
|
98
|
+
"write",
|
|
99
|
+
"run",
|
|
100
|
+
]);
|
|
101
|
+
expect(normalizeTools("a b c")).toEqual(["a", "b", "c"]);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("normalizes an array, dropping empties", () => {
|
|
105
|
+
expect(normalizeTools(["read", "", " write "])).toEqual(["read", "write"]);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("returns null when nothing remains", () => {
|
|
109
|
+
expect(normalizeTools("")).toBe(null);
|
|
110
|
+
expect(normalizeTools([" ", ""])).toBe(null);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// --------------------------------------------------------------------------- //
|
|
115
|
+
// parseAgentFile
|
|
116
|
+
// --------------------------------------------------------------------------- //
|
|
117
|
+
describe("parseAgentFile", () => {
|
|
118
|
+
const parse = (content, opts) =>
|
|
119
|
+
parseAgentFile("/a.md", "project", {
|
|
120
|
+
deps: { fs: makeFs({ "/a.md": content }) },
|
|
121
|
+
...opts,
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it("returns null when the file cannot be read", () => {
|
|
125
|
+
expect(
|
|
126
|
+
parseAgentFile("/missing.md", "project", { deps: { fs: makeFs({}) } }),
|
|
127
|
+
).toBe(null);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it("uses the whole body as systemPrompt when there is no frontmatter", () => {
|
|
131
|
+
const a = parse("You are a helpful agent.");
|
|
132
|
+
expect(a.systemPrompt).toBe("You are a helpful agent.");
|
|
133
|
+
expect(a.name).toBe(null);
|
|
134
|
+
expect(a.description).toBe("");
|
|
135
|
+
expect(a.tools).toBe(null);
|
|
136
|
+
expect(a.model).toBe(null);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("parses frontmatter metadata and trims the body", () => {
|
|
140
|
+
const a = parse(
|
|
141
|
+
"---\nname: sec\ndescription: security review\ntools: read, grep\nmodel: opus\n---\n\nReview for vulns.\n",
|
|
142
|
+
);
|
|
143
|
+
expect(a.name).toBe("sec");
|
|
144
|
+
expect(a.description).toBe("security review");
|
|
145
|
+
expect(a.tools).toEqual(["read", "grep"]);
|
|
146
|
+
expect(a.model).toBe("opus");
|
|
147
|
+
expect(a.systemPrompt).toBe("Review for vulns.");
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it("accepts a YAML array for tools", () => {
|
|
151
|
+
const a = parse("---\ntools:\n - read\n - write\n---\nbody");
|
|
152
|
+
expect(a.tools).toEqual(["read", "write"]);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it("falls back to defaults on invalid YAML frontmatter", () => {
|
|
156
|
+
const a = parse("---\n: : bad : yaml :\n---\nthe body");
|
|
157
|
+
expect(a.name).toBe(null);
|
|
158
|
+
expect(a.systemPrompt).toBe("the body");
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// --------------------------------------------------------------------------- //
|
|
163
|
+
// discoverAgents
|
|
164
|
+
// --------------------------------------------------------------------------- //
|
|
165
|
+
describe("discoverAgents", () => {
|
|
166
|
+
it("derives names from the path with ':' for nesting", () => {
|
|
167
|
+
const agents = discover({
|
|
168
|
+
"/proj/.claude/agents/reviewer.md": "Review.",
|
|
169
|
+
"/proj/.claude/agents/sec/audit.md": "Audit.",
|
|
170
|
+
});
|
|
171
|
+
const names = agents.map((a) => a.name);
|
|
172
|
+
expect(names).toEqual(["reviewer", "sec:audit"]); // sorted
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it("lets frontmatter name override the path-derived name", () => {
|
|
176
|
+
const agents = discover({
|
|
177
|
+
"/proj/.claude/agents/x.md": "---\nname: custom\n---\nbody",
|
|
178
|
+
});
|
|
179
|
+
expect(agents[0].name).toBe("custom");
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it("project scope shadows personal on a name clash", () => {
|
|
183
|
+
const agents = discover({
|
|
184
|
+
"/proj/.claude/agents/reviewer.md": "PROJECT version.",
|
|
185
|
+
"/home/u/.claude/agents/reviewer.md": "PERSONAL version.",
|
|
186
|
+
"/home/u/.claude/agents/helper.md": "Helper.",
|
|
187
|
+
});
|
|
188
|
+
const reviewer = agents.find((a) => a.name === "reviewer");
|
|
189
|
+
expect(reviewer.scope).toBe("project");
|
|
190
|
+
expect(reviewer.systemPrompt).toBe("PROJECT version.");
|
|
191
|
+
// personal-only agent still discovered
|
|
192
|
+
expect(agents.find((a) => a.name === "helper").scope).toBe("personal");
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it("returns [] when no agent dirs exist", () => {
|
|
196
|
+
expect(discover({ "/proj/readme.md": "x" })).toEqual([]);
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// --------------------------------------------------------------------------- //
|
|
201
|
+
// getAgent
|
|
202
|
+
// --------------------------------------------------------------------------- //
|
|
203
|
+
describe("getAgent", () => {
|
|
204
|
+
const files = {
|
|
205
|
+
"/proj/.claude/agents/sec/audit.md": "---\ndescription: d\n---\nAudit.",
|
|
206
|
+
};
|
|
207
|
+
const get = (name) =>
|
|
208
|
+
getAgent(name, "/proj", {
|
|
209
|
+
deps: { fs: makeFs(files), path: P },
|
|
210
|
+
home: "/home/u",
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it("looks up by colon name", () => {
|
|
214
|
+
expect(get("sec:audit")?.description).toBe("d");
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it("accepts slash and leading-slash forms", () => {
|
|
218
|
+
expect(get("sec/audit")?.name).toBe("sec:audit");
|
|
219
|
+
expect(get("/sec/audit")?.name).toBe("sec:audit");
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it("returns null for an unknown agent", () => {
|
|
223
|
+
expect(get("nope")).toBe(null);
|
|
224
|
+
});
|
|
225
|
+
});
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* cowork-observe-html buildHtml tests (previously untested).
|
|
5
|
+
*
|
|
6
|
+
* buildHtml renders a static dashboard from an aggregate snapshot and embeds
|
|
7
|
+
* user-controlled data (template names, failure summaries, schedule fields) into
|
|
8
|
+
* HTML and into an inline <script> JSON blob. The escaping is the security
|
|
9
|
+
* boundary — these tests pin XSS safety (HTML entity escaping + JSON
|
|
10
|
+
* script-breakout escaping) plus the structural rendering / empty states.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { describe, it, expect } from "vitest";
|
|
14
|
+
import { buildHtml } from "../cowork-observe-html.js";
|
|
15
|
+
|
|
16
|
+
describe("buildHtml — document shape", () => {
|
|
17
|
+
it("emits a full HTML document with UTF-8 charset", () => {
|
|
18
|
+
const html = buildHtml({});
|
|
19
|
+
expect(html.startsWith("<!DOCTYPE html>")).toBe(true);
|
|
20
|
+
expect(html).toContain('<meta charset="UTF-8">');
|
|
21
|
+
expect(html).toContain("Cowork Observe");
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("does not throw on null/undefined data", () => {
|
|
25
|
+
expect(() => buildHtml(null)).not.toThrow();
|
|
26
|
+
expect(() => buildHtml(undefined)).not.toThrow();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("renders empty-state messages when there is no data", () => {
|
|
30
|
+
const html = buildHtml({});
|
|
31
|
+
expect(html).toContain("No template runs in window.");
|
|
32
|
+
expect(html).toContain("No failures in window");
|
|
33
|
+
expect(html).toContain("No upcoming triggers.");
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("renders summary stat cards", () => {
|
|
37
|
+
const html = buildHtml({
|
|
38
|
+
tasks: { total: 42, successRate: 0.876, failed: 5, avgTokens: 1200 },
|
|
39
|
+
schedules: { active: 3 },
|
|
40
|
+
});
|
|
41
|
+
expect(html).toContain(">42<"); // total
|
|
42
|
+
expect(html).toContain(">88%<"); // successRate rounded
|
|
43
|
+
expect(html).toContain(">3<"); // active schedules
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
describe("buildHtml — table rendering", () => {
|
|
48
|
+
it("renders template rows with success-rate percentage", () => {
|
|
49
|
+
const html = buildHtml({
|
|
50
|
+
templates: [
|
|
51
|
+
{ templateName: "build", runs: 9, successRate: 0.5, avgTokens: 100 },
|
|
52
|
+
],
|
|
53
|
+
});
|
|
54
|
+
expect(html).toContain("<td>build</td>");
|
|
55
|
+
expect(html).toContain("<td>50%</td>");
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("caps template rows at 10", () => {
|
|
59
|
+
const templates = Array.from({ length: 15 }, (_, i) => ({
|
|
60
|
+
templateName: `t${i}`,
|
|
61
|
+
runs: i,
|
|
62
|
+
successRate: 1,
|
|
63
|
+
avgTokens: 0,
|
|
64
|
+
}));
|
|
65
|
+
const html = buildHtml({ templates });
|
|
66
|
+
expect(html).toContain("<td>t9</td>");
|
|
67
|
+
expect(html).not.toContain("<td>t10</td>"); // 11th dropped
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("truncates failure summary to 80 chars", () => {
|
|
71
|
+
const long = "x".repeat(200);
|
|
72
|
+
const html = buildHtml({
|
|
73
|
+
failures: [
|
|
74
|
+
{
|
|
75
|
+
templateId: "f",
|
|
76
|
+
failureCount: 2,
|
|
77
|
+
commonSummaries: [{ summary: long }],
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
});
|
|
81
|
+
// the visible table cell is truncated to 80 (the raw 200-char string still
|
|
82
|
+
// lives untruncated inside the embedded JSON blob, so scope to the <td>).
|
|
83
|
+
expect(html).toContain("<td>" + "x".repeat(80) + "</td>");
|
|
84
|
+
expect(html).not.toContain("<td>" + "x".repeat(81));
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describe("buildHtml — XSS safety", () => {
|
|
89
|
+
it("HTML-escapes user-controlled template names", () => {
|
|
90
|
+
const html = buildHtml({
|
|
91
|
+
templates: [
|
|
92
|
+
{
|
|
93
|
+
templateName: "<script>alert(1)</script>",
|
|
94
|
+
runs: 1,
|
|
95
|
+
successRate: 1,
|
|
96
|
+
avgTokens: 0,
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
});
|
|
100
|
+
expect(html).toContain("<script>alert(1)</script>");
|
|
101
|
+
// the raw payload must not appear inside a table cell
|
|
102
|
+
expect(html).not.toContain("<td><script>alert(1)</script></td>");
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it("HTML-escapes failure summaries and schedule fields", () => {
|
|
106
|
+
const html = buildHtml({
|
|
107
|
+
failures: [
|
|
108
|
+
{
|
|
109
|
+
templateName: "<b>x</b>",
|
|
110
|
+
failureCount: 1,
|
|
111
|
+
commonSummaries: [{ summary: "a&b\"c'" }],
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
schedules: {
|
|
115
|
+
nextTriggers: [
|
|
116
|
+
{ at: "<i>t</i>", cron: "* * * * *", scheduleId: "s<1>" },
|
|
117
|
+
],
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
expect(html).toContain("<b>x</b>");
|
|
121
|
+
expect(html).toContain("a&b"c'");
|
|
122
|
+
expect(html).toContain("<i>t</i>");
|
|
123
|
+
expect(html).toContain("s<1>");
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it("escapes a </script> breakout in the embedded JSON blob", () => {
|
|
127
|
+
const html = buildHtml({
|
|
128
|
+
tasks: { note: "</script><img src=x onerror=alert(1)>" },
|
|
129
|
+
});
|
|
130
|
+
// the inline JSON must not contain a raw closing script tag
|
|
131
|
+
expect(html).toContain("\\u003c/script");
|
|
132
|
+
expect(html).not.toContain("</script><img");
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it("escapes <, > and & in the embedded JSON blob", () => {
|
|
136
|
+
const html = buildHtml({ x: "<a> & <b>" });
|
|
137
|
+
expect(html).toContain("\\u003ca\\u003e \\u0026 \\u003cb\\u003e");
|
|
138
|
+
// the script assignment line itself carries the escaped form
|
|
139
|
+
expect(html).toContain("window.__COWORK_OBSERVE__ =");
|
|
140
|
+
});
|
|
141
|
+
});
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* logger tests (previously untested) — the cc CLI's console logger. Small but
|
|
5
|
+
* the gating semantics matter: quiet suppresses info/success/log/newline but
|
|
6
|
+
* must NEVER suppress warn/error (otherwise failures would be hidden), and
|
|
7
|
+
* verbose only emits when enabled. console is spied; module-level verbose/quiet
|
|
8
|
+
* flags are reset between tests.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
|
12
|
+
import {
|
|
13
|
+
setVerbose,
|
|
14
|
+
setQuiet,
|
|
15
|
+
info,
|
|
16
|
+
success,
|
|
17
|
+
warn,
|
|
18
|
+
error,
|
|
19
|
+
verbose,
|
|
20
|
+
log,
|
|
21
|
+
newline,
|
|
22
|
+
logger,
|
|
23
|
+
} from "../logger.js";
|
|
24
|
+
|
|
25
|
+
let logSpy;
|
|
26
|
+
let errSpy;
|
|
27
|
+
|
|
28
|
+
beforeEach(() => {
|
|
29
|
+
logSpy = vi.spyOn(console, "log").mockImplementation(() => {});
|
|
30
|
+
errSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
31
|
+
setQuiet(false);
|
|
32
|
+
setVerbose(false);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
afterEach(() => {
|
|
36
|
+
setQuiet(false);
|
|
37
|
+
setVerbose(false);
|
|
38
|
+
vi.restoreAllMocks();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
describe("default (not quiet, not verbose)", () => {
|
|
42
|
+
it("info/success/log/newline write to console.log", () => {
|
|
43
|
+
info("i");
|
|
44
|
+
success("s");
|
|
45
|
+
log("l");
|
|
46
|
+
newline();
|
|
47
|
+
expect(logSpy).toHaveBeenCalledTimes(4);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("verbose is suppressed unless enabled", () => {
|
|
51
|
+
verbose("v");
|
|
52
|
+
expect(logSpy).not.toHaveBeenCalled();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("error writes to console.error, not console.log", () => {
|
|
56
|
+
error("boom");
|
|
57
|
+
expect(errSpy).toHaveBeenCalledTimes(1);
|
|
58
|
+
expect(logSpy).not.toHaveBeenCalled();
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
describe("quiet mode", () => {
|
|
63
|
+
beforeEach(() => setQuiet(true));
|
|
64
|
+
|
|
65
|
+
it("suppresses info/success/log/newline", () => {
|
|
66
|
+
info("i");
|
|
67
|
+
success("s");
|
|
68
|
+
log("l");
|
|
69
|
+
newline();
|
|
70
|
+
expect(logSpy).not.toHaveBeenCalled();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("does NOT suppress warn (still console.log)", () => {
|
|
74
|
+
warn("careful");
|
|
75
|
+
expect(logSpy).toHaveBeenCalledTimes(1);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("does NOT suppress error", () => {
|
|
79
|
+
error("boom");
|
|
80
|
+
expect(errSpy).toHaveBeenCalledTimes(1);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe("verbose mode", () => {
|
|
85
|
+
it("emits verbose only after setVerbose(true)", () => {
|
|
86
|
+
verbose("before");
|
|
87
|
+
expect(logSpy).not.toHaveBeenCalled();
|
|
88
|
+
setVerbose(true);
|
|
89
|
+
verbose("after");
|
|
90
|
+
expect(logSpy).toHaveBeenCalledTimes(1);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe("logger object", () => {
|
|
95
|
+
it("exposes the full method surface", () => {
|
|
96
|
+
for (const m of [
|
|
97
|
+
"info",
|
|
98
|
+
"success",
|
|
99
|
+
"warn",
|
|
100
|
+
"error",
|
|
101
|
+
"verbose",
|
|
102
|
+
"log",
|
|
103
|
+
"newline",
|
|
104
|
+
"setVerbose",
|
|
105
|
+
"setQuiet",
|
|
106
|
+
]) {
|
|
107
|
+
expect(typeof logger[m]).toBe("function");
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
});
|