chainlesschain 0.162.32 → 0.162.33
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-Cg_uWAVl.js → AIOps-3TazCYWE.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-DSFtQ1c2.js → ActionButton-DUPN0PST.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-BMxpkw8y.js → Analytics-CemvhkzD.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-tgVxlmsx.js → AppLayout-BL_tAU3M.js} +3 -3
- package/src/assets/web-panel/assets/{Audit-DwzGllcp.js → Audit-Dl9l-cxF.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-BG28Y2MV.js → Backup-BKDDX75m.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-TXthbazl.js → BaseInput-CDYePvMI.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-D096SxaD.js → Chat-CGtR0sg3.js} +4 -4
- package/src/assets/web-panel/assets/ChatBubbleRenderer-DZjc9uKn.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-Czttw1JS.js → Checkbox-CwYIHOOo.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-DZtMgv4q.js → Codegen-CIF5tbtd.js} +1 -1
- package/src/assets/web-panel/assets/{Col-D3DnfExY.js → Col-z7d4kxeP.js} +1 -1
- package/src/assets/web-panel/assets/{Community-Bj5AdwqY.js → Community-DUlDrqF7.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-BQ8Zszub.js → Compact-CJ1o8QQR.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-DXacb34n.js → Compliance-D3i9d_uO.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-BgMUBTkw.js → Cowork-Wm7JTkfB.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-fqBWOqlN.js → Cron-B0QnHhZx.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-E4oa1MWy.js → Crosschain-3yPrnNgd.js} +1 -1
- package/src/assets/web-panel/assets/{DID-pwgfYZaV.js → DID-cfdkiDWF.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-n8mdLFIR.js → Dashboard-DFkgM4gT.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown--6DYqxk7.js → Dropdown-YYWE81DL.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-CkjQluz3.js → EmailListRenderer-BXfHK1Bn.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-u-QTQ-OC.js → FamilyGuardDashboard-DInUxJ2G.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-D219M5Qc.js → Federation-DNUYeFsv.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-BBU_aopC.js → FormItemContext-Cr7eVEBB.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-pTMCIHcM.js → GenericCardRenderer-_gF4cmDa.js} +1 -1
- package/src/assets/web-panel/assets/{Git-ClcCARWt.js → Git-BqldmUbO.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-CvUi3I93.js → Governance-BF59ZiQ8.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-DT-a4pVg.js → Inference-Cy7y1eb9.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DHMs2LY8.js → KnowledgeGraph-B3fVocTO.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-D2s4eV1N.js → Logs-BDirsUVk.js} +2 -2
- package/src/assets/web-panel/assets/{Marketplace-YC5-fx-6.js → Marketplace-GhXpZgp2.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-7JHTEC4T.js → McpTools-0VvfIhKx.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-BudotVLD.js → Memory-CJLBgAUT.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-CAiRyLVU.js → MobileBridge-BMedY9Yg.js} +2 -2
- package/src/assets/web-panel/assets/MobileProjects-mdohgRlL.js +1 -0
- package/src/assets/web-panel/assets/{Mtc-d0iY0CeK.js → Mtc-CgEuUg0g.js} +2 -2
- package/src/assets/web-panel/assets/{MtcAudit-aI2cG1UP.js → MtcAudit-1pWNe_xi.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-4bF70khG.js → Multisig-DPIQ7oZL.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-CwLib1S7.js → NLProgramming-W__P_P4Z.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-Wt7AuFRU.js → Notes-C_MCDhFk.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-D081vV_7.js → NotificationSettings-CDFotapL.js} +1 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-Dtht0cEs.js +1 -0
- package/src/assets/web-panel/assets/{Organization-BNEsUNdP.js → Organization-D6lMumhD.js} +2 -2
- package/src/assets/web-panel/assets/{Overflow-B_1iUXDD.js → Overflow-BMOvUMW6.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-Dbc-kNwJ.js → P2P-DsQTEw1t.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-D8Xh289k.js → PdhVaultBrowser-CncRtN1Z.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-C77mM6-n.js → Permissions-DDC-DkUl.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-Dj0J3r_K.js → PersonalDataHub-DVKY_NnT.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-B6F0WQ2C.js → Pipeline-C7oDVTl-.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-eDKOkyyq.js → Privacy-DReGvTEJ.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-DAWwhr5_.js → ProjectInit-C-j2dzxJ.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-DwdK8k6I.js → ProjectSettings-DcUsvFnc.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-Cb3p5QAP.js → Projects-jSjWnmr6.js} +1 -1
- package/src/assets/web-panel/assets/{Providers--DcYxQfN.js → Providers-DIpohWG5.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-DU268niT.js → QuickAsk-DdvLtpEU.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-ChnflhV1.js → Recommend-DPAi2zo3.js} +1 -1
- package/src/assets/web-panel/assets/{Reputation-DSsY3bQG.js → Reputation-DJD7qXSI.js} +1 -1
- package/src/assets/web-panel/assets/{Row-Zb-EjmgQ.js → Row-XERdPDHk.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-CGLiixZB.js → RssFeed-Cl_VlCLg.js} +3 -3
- package/src/assets/web-panel/assets/{Search-Dhr_po-U.js → Search-C-poG9P5.js} +1 -1
- package/src/assets/web-panel/assets/{Security-GMYNhGsR.js → Security-DjjCrw8v.js} +2 -2
- package/src/assets/web-panel/assets/{Services-DiOpnVY0.js → Services-BuWeB4YJ.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-DG3ez6ME.js → Skeleton-VZXOKwC_.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-DZGptytP.js → Skills-B76ONTfP.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-CtGpE3xA.js → Sla-DIj1KREq.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-DQFw6Cf9.js → SpeechSettings-BrAp3Yk3.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-C8X78RpX.js → SyncSettings--mJcpccF.js} +2 -2
- package/src/assets/web-panel/assets/Tasks-DM8cMr83.js +1 -0
- package/src/assets/web-panel/assets/{Templates-SF9_ZWsV.js → Templates-kOBK6m1Z.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-BbIQSVZz.js → Tenant-BjSzYPzn.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-DKr5zDwu.js → Terminal-DwpY-Ay7.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-BtLaNaWr.js → TimelineRenderer-aoI0DazM.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-CfYbk2NG.js → Tokens-YwE0LqSZ.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-BLX_XDP0.js → Trigger-CwSKzvlX.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-BWxUv9PR.js → Trust-B__Jqdzn.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-DRwTyQD4.js → UkeySign-mty0jwmx.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-BsC4VOSo.js → VideoEditing-Ddsx_OQ6.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-CSsO1NJU.js → Wallet-D4Q8yXZm.js} +2 -2
- package/src/assets/web-panel/assets/{WebAuthn-z1MxiFzS.js → WebAuthn-CLUaKUr5.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-B1vV7uuJ.js → WorkflowEditor-Di5pOaeC.js} +1 -1
- package/src/assets/web-panel/assets/{chat-C0NJRaL2.js → chat-CELatHkT.js} +1 -1
- package/src/assets/web-panel/assets/{colors-CHRiteWF.js → colors-CawDLjXV.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-2XmBBKPD.js → compact-item-DeMp-K0j.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-DkedHC38.js → createContext-zY9kXivd.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-zLjV7g6r.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-Bpn9Xrlw.js → hasIn-VEBMW8E4.js} +1 -1
- package/src/assets/web-panel/assets/{index-De49R7TX.js → index-8kqE_cVD.js} +1 -1
- package/src/assets/web-panel/assets/{index-i4W_EAuh.js → index-B8AZpx7d.js} +1 -1
- package/src/assets/web-panel/assets/{index-DryKGM_t.js → index-BFc0vBN9.js} +1 -1
- package/src/assets/web-panel/assets/{index-uHGxyZtQ.js → index-BVkrfyuk.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dk7P-q3n.js → index-BfGGKoo8.js} +1 -1
- package/src/assets/web-panel/assets/{index-CyJpmSHZ.js → index-BjctklSd.js} +1 -1
- package/src/assets/web-panel/assets/{index-NuBsCRaR.js → index-BqJ2r12F.js} +1 -1
- package/src/assets/web-panel/assets/{index-BOsIgPge.js → index-C0GhuYLk.js} +1 -1
- package/src/assets/web-panel/assets/index-CDtUWCtX.js +1 -0
- package/src/assets/web-panel/assets/{index-CdR7RfRP.js → index-CHqvj9uz.js} +1 -1
- package/src/assets/web-panel/assets/{index-CJOoo72F.js → index-CHxHLv2b.js} +1 -1
- package/src/assets/web-panel/assets/{index-DM9JrnYi.js → index-CWbbB1MI.js} +1 -1
- package/src/assets/web-panel/assets/{index-DUBsq_1G.js → index-CbJZzK9B.js} +1 -1
- package/src/assets/web-panel/assets/{index-B5NGWgHp.js → index-CfZV3FXN.js} +1 -1
- package/src/assets/web-panel/assets/{index-BveL_4n3.js → index-Cr7lnIeI.js} +1 -1
- package/src/assets/web-panel/assets/{index-CWgWrrWs.js → index-CtLZammH.js} +1 -1
- package/src/assets/web-panel/assets/{index-Sk3-3tKa.js → index-CtoauqWt.js} +1 -1
- package/src/assets/web-panel/assets/{index-CxvA72CP.js → index-CyeYs7SG.js} +1 -1
- package/src/assets/web-panel/assets/{index-BN068mCR.js → index-DALuVdhu.js} +1 -1
- package/src/assets/web-panel/assets/{index-DAFLFMXQ.js → index-DClGYjBM.js} +1 -1
- package/src/assets/web-panel/assets/{index-DMbF-Euw.js → index-DPHe9NYG.js} +1 -1
- package/src/assets/web-panel/assets/{index-BYmwEaIk.js → index-DSiL_W2n.js} +1 -1
- package/src/assets/web-panel/assets/{index-D9mNfpxi.js → index-DXNe_zIP.js} +1 -1
- package/src/assets/web-panel/assets/{index-DDy_RDjs.js → index-DhsfyHcg.js} +1 -1
- package/src/assets/web-panel/assets/{index-DE5Qm9UI.js → index-Dna2psGz.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cljnfuxu.js → index-GRNVdvoA.js} +1 -1
- package/src/assets/web-panel/assets/{index-DAeHmElB.js → index-JseP3-5X.js} +1 -1
- package/src/assets/web-panel/assets/{index-De5vOO9V.js → index-KcOEkUCM.js} +1 -1
- package/src/assets/web-panel/assets/{index-CCg6ZY4t.js → index-S9JZDSaa.js} +1 -1
- package/src/assets/web-panel/assets/{index-CToQxpWz.js → index-SrQIPYq8.js} +1 -1
- package/src/assets/web-panel/assets/{index-cfSUlOfY.js → index-TfXODan7.js} +1 -1
- package/src/assets/web-panel/assets/{index-BfY9U3X5.js → index-V3K9gvKR.js} +1 -1
- package/src/assets/web-panel/assets/{index-BYUd69vM.js → index-VJnHvkv2.js} +1 -1
- package/src/assets/web-panel/assets/{index-BItcSqan.js → index-XFyv3Sg_.js} +3 -3
- package/src/assets/web-panel/assets/{index-BZ1gOoiG.js → index-b3ZuAreb.js} +1 -1
- package/src/assets/web-panel/assets/index-d_RPqH7u.js +1 -0
- package/src/assets/web-panel/assets/{index-DtU4qZRF.js → index-fBNVDEf2.js} +1 -1
- package/src/assets/web-panel/assets/{index-alGjpoM1.js → index-u_1aiNTA.js} +1 -1
- package/src/assets/web-panel/assets/{index-D7U411hK.js → index-vaD1iHg5.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-DlDE-QgI.js → initDefaultProps-Sd7Eayz4.js} +1 -1
- package/src/assets/web-panel/assets/{motion-CodUbIRF.js → motion-DlToY72q.js} +1 -1
- package/src/assets/web-panel/assets/{move-DaLwsHeR.js → move-DvS7EmAP.js} +1 -1
- package/src/assets/web-panel/assets/{omit-DdVg-3rL.js → omit-CzLq4QKW.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-KLR1EVCo.js → pickAttrs-BcM75Jx_.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-ChV7HvNw.js → placementArrow-B7xXXiwd.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-BB_A8dBt.js → responsiveObserve-CrYPRB-g.js} +1 -1
- package/src/assets/web-panel/assets/{slide-Bc1tQnIK.js → slide-CSYTtsRt.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-CgrveSb0.js → statusUtils-CeSuOVT_.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-vXAYhhjz.js → styleChecker-KiQethca.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-BCIMPfq9.js → useFlexGapSupport-CSQnQdiv.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-DMZGdr6G.js → useFs-Br8Kr1pr.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-118tWI_Z.js → usePersonalDataHub-DGJtDcMm.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-Z7O2Y7JP.js → vnode-C-jVtGka.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-BXym6zmD.js → zoom-CeWySTPF.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/agent.js +20 -0
- package/src/lib/goal-assess.js +228 -0
- package/src/lib/goal-store.js +33 -0
- package/src/runtime/agent-core.js +22 -0
- package/src/runtime/headless-runner.js +122 -0
- package/src/runtime/headless-stream.js +21 -0
- package/src/runtime/mcp-config.js +139 -0
- package/src/assets/web-panel/assets/ChatBubbleRenderer-PIx0Eu9I.js +0 -1
- package/src/assets/web-panel/assets/MobileProjects-CrJJOCFw.js +0 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-DCPei1L9.js +0 -1
- package/src/assets/web-panel/assets/Tasks-DtVkhWCV.js +0 -1
- package/src/assets/web-panel/assets/devWarning-DmNpkOdC.js +0 -1
- package/src/assets/web-panel/assets/index-7nAysteg.js +0 -1
- package/src/assets/web-panel/assets/index-BKWSQilQ.js +0 -1
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* goal-assess — run-end LLM self-assessment of goal progress (cc goal Phase 2).
|
|
3
|
+
*
|
|
4
|
+
* After an agent run bound to a goal (--goal-assess), an LLM is asked to judge,
|
|
5
|
+
* from the run transcript, whether the goal advanced — and to propose key-result
|
|
6
|
+
* updates, a one-line progress note, and any concerns. The proposal is then
|
|
7
|
+
* persisted through goal-store (progress / key results / agent note / drift).
|
|
8
|
+
*
|
|
9
|
+
* This is OPT-IN because it spends extra tokens (one bonus completion per run).
|
|
10
|
+
* The LLM call is injected as `chat(promptString) => Promise<string>` so the
|
|
11
|
+
* orchestration is deterministically unit-testable without a provider.
|
|
12
|
+
*
|
|
13
|
+
* Parsing is deliberately tolerant: models wrap JSON in prose / code fences, so
|
|
14
|
+
* we extract the first balanced `{...}` block and validate the fields we use.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
recordProgress,
|
|
19
|
+
setKeyResult,
|
|
20
|
+
addDriftFlags,
|
|
21
|
+
getGoal,
|
|
22
|
+
} from "./goal-store.js";
|
|
23
|
+
|
|
24
|
+
/** Bound the transcript we feed the judge so the assessment stays cheap. */
|
|
25
|
+
const MAX_FINAL_TEXT = 2000;
|
|
26
|
+
const MAX_TOOLS = 40;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Build the assessment prompt. Asks for a strict JSON object describing whether
|
|
30
|
+
* the goal advanced + proposed updates.
|
|
31
|
+
* @param {object} goal
|
|
32
|
+
* @param {object} transcript { prompt, finalText, toolCalls:[{tool,args}] }
|
|
33
|
+
*/
|
|
34
|
+
export function buildAssessPrompt(goal, transcript = {}) {
|
|
35
|
+
const krLines = (goal.keyResults || [])
|
|
36
|
+
.map(
|
|
37
|
+
(k) =>
|
|
38
|
+
` - id=${k.id} | "${k.text}"` +
|
|
39
|
+
(k.target != null ? ` | ${k.current ?? 0}/${k.target}` : "") +
|
|
40
|
+
(k.done ? " | DONE" : ""),
|
|
41
|
+
)
|
|
42
|
+
.join("\n");
|
|
43
|
+
|
|
44
|
+
const tools = (transcript.toolCalls || [])
|
|
45
|
+
.slice(0, MAX_TOOLS)
|
|
46
|
+
.map((t) => t.tool)
|
|
47
|
+
.join(", ");
|
|
48
|
+
|
|
49
|
+
const finalText = String(transcript.finalText || "").slice(0, MAX_FINAL_TEXT);
|
|
50
|
+
|
|
51
|
+
return [
|
|
52
|
+
"You are assessing whether a single agent run advanced a long-running goal.",
|
|
53
|
+
"",
|
|
54
|
+
`GOAL: ${goal.objective}`,
|
|
55
|
+
`CURRENT PROGRESS: ${goal.progress ?? 0}%`,
|
|
56
|
+
goal.keyResults?.length
|
|
57
|
+
? `KEY RESULTS:\n${krLines}`
|
|
58
|
+
: "KEY RESULTS: (none)",
|
|
59
|
+
"",
|
|
60
|
+
`RUN TASK: ${String(transcript.prompt || "").slice(0, 500)}`,
|
|
61
|
+
tools ? `TOOLS USED: ${tools}` : "TOOLS USED: (none)",
|
|
62
|
+
`RUN RESULT:\n${finalText || "(no final text)"}`,
|
|
63
|
+
"",
|
|
64
|
+
"Reply with ONLY a JSON object (no prose, no code fence) of this shape:",
|
|
65
|
+
"{",
|
|
66
|
+
' "advanced": true|false, // did this run move the goal forward?',
|
|
67
|
+
' "progress": <0-100 or null>, // your estimate of new overall progress, or null to leave unchanged',
|
|
68
|
+
' "keyResults": [ // updates for specific key results (omit if none)',
|
|
69
|
+
' {"id": "<kr-id>", "current": <number or null>, "done": true|false}',
|
|
70
|
+
" ],",
|
|
71
|
+
' "note": "<one short sentence summarizing what changed>",',
|
|
72
|
+
' "concerns": ["<short concern>", ...] // anything blocking/at-risk (omit if none)',
|
|
73
|
+
"}",
|
|
74
|
+
].join("\n");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Tolerantly parse the judge's reply into a normalized assessment, or null.
|
|
79
|
+
* @param {string} text
|
|
80
|
+
* @returns {{advanced:boolean, progress:number|null, keyResults:object[], note:string, concerns:string[]}|null}
|
|
81
|
+
*/
|
|
82
|
+
export function parseAssessment(text) {
|
|
83
|
+
if (!text || typeof text !== "string") return null;
|
|
84
|
+
const json = extractFirstJsonObject(text);
|
|
85
|
+
if (!json) return null;
|
|
86
|
+
let obj;
|
|
87
|
+
try {
|
|
88
|
+
obj = JSON.parse(json);
|
|
89
|
+
} catch {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
if (!obj || typeof obj !== "object") return null;
|
|
93
|
+
|
|
94
|
+
const progress =
|
|
95
|
+
obj.progress == null || obj.progress === "" ? null : clampPct(obj.progress);
|
|
96
|
+
|
|
97
|
+
const keyResults = Array.isArray(obj.keyResults)
|
|
98
|
+
? obj.keyResults
|
|
99
|
+
.filter((k) => k && k.id)
|
|
100
|
+
.map((k) => ({
|
|
101
|
+
id: String(k.id),
|
|
102
|
+
current:
|
|
103
|
+
k.current == null || k.current === "" ? null : Number(k.current),
|
|
104
|
+
done: k.done === true,
|
|
105
|
+
}))
|
|
106
|
+
: [];
|
|
107
|
+
|
|
108
|
+
const concerns = Array.isArray(obj.concerns)
|
|
109
|
+
? obj.concerns.map((c) => String(c)).filter(Boolean)
|
|
110
|
+
: [];
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
advanced: obj.advanced === true,
|
|
114
|
+
progress,
|
|
115
|
+
keyResults,
|
|
116
|
+
note: obj.note ? String(obj.note) : "",
|
|
117
|
+
concerns,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function clampPct(v) {
|
|
122
|
+
const n = Math.round(Number(v));
|
|
123
|
+
if (!Number.isFinite(n)) return null;
|
|
124
|
+
return Math.max(0, Math.min(100, n));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** Extract the first balanced top-level {...} block from text (fence-tolerant). */
|
|
128
|
+
function extractFirstJsonObject(text) {
|
|
129
|
+
const start = text.indexOf("{");
|
|
130
|
+
if (start < 0) return null;
|
|
131
|
+
let depth = 0;
|
|
132
|
+
let inStr = false;
|
|
133
|
+
let esc = false;
|
|
134
|
+
for (let i = start; i < text.length; i++) {
|
|
135
|
+
const ch = text[i];
|
|
136
|
+
if (inStr) {
|
|
137
|
+
if (esc) esc = false;
|
|
138
|
+
else if (ch === "\\") esc = true;
|
|
139
|
+
else if (ch === '"') inStr = false;
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (ch === '"') inStr = true;
|
|
143
|
+
else if (ch === "{") depth++;
|
|
144
|
+
else if (ch === "}") {
|
|
145
|
+
depth--;
|
|
146
|
+
if (depth === 0) return text.slice(start, i + 1);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Persist a parsed assessment to the goal via goal-store. Each sub-update is
|
|
154
|
+
* isolated (a bad key-result id can't void the progress note). Returns the
|
|
155
|
+
* final goal state.
|
|
156
|
+
* @param {string} goalId
|
|
157
|
+
* @param {object} a output of parseAssessment
|
|
158
|
+
* @param {object} [opts] { root }
|
|
159
|
+
*/
|
|
160
|
+
export function applyAssessment(goalId, a, opts = {}) {
|
|
161
|
+
if (!a) return getGoal(goalId, opts);
|
|
162
|
+
|
|
163
|
+
// Key-result updates first (they may drive derived progress).
|
|
164
|
+
for (const kr of a.keyResults || []) {
|
|
165
|
+
try {
|
|
166
|
+
setKeyResult(goalId, kr.id, { current: kr.current, done: kr.done }, opts);
|
|
167
|
+
} catch {
|
|
168
|
+
/* unknown / stale key-result id — skip, don't void the rest */
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Explicit progress and/or the agent note.
|
|
173
|
+
if (a.progress != null || a.note) {
|
|
174
|
+
try {
|
|
175
|
+
recordProgress(
|
|
176
|
+
goalId,
|
|
177
|
+
{ pct: a.progress, note: a.note, by: "agent" },
|
|
178
|
+
opts,
|
|
179
|
+
);
|
|
180
|
+
} catch {
|
|
181
|
+
/* best-effort */
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Drift: a no-advance run, plus any explicit concerns.
|
|
186
|
+
const flags = [];
|
|
187
|
+
if (!a.advanced) {
|
|
188
|
+
flags.push({ kind: "no-progress", detail: "run did not advance the goal" });
|
|
189
|
+
}
|
|
190
|
+
for (const c of a.concerns || []) flags.push({ kind: "concern", detail: c });
|
|
191
|
+
if (flags.length) {
|
|
192
|
+
try {
|
|
193
|
+
addDriftFlags(goalId, flags, opts);
|
|
194
|
+
} catch {
|
|
195
|
+
/* best-effort */
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return getGoal(goalId, opts);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Full orchestration: prompt → chat → parse → apply. Returns
|
|
204
|
+
* `{ assessment, goal }` (assessment is null when the judge gave no usable JSON).
|
|
205
|
+
* @param {object} params { goal, transcript, chat, opts }
|
|
206
|
+
* chat: (promptString) => Promise<string>
|
|
207
|
+
*/
|
|
208
|
+
export async function assessGoalProgress({
|
|
209
|
+
goal,
|
|
210
|
+
transcript,
|
|
211
|
+
chat,
|
|
212
|
+
opts = {},
|
|
213
|
+
}) {
|
|
214
|
+
if (!goal || typeof chat !== "function") {
|
|
215
|
+
return { assessment: null, goal: goal || null };
|
|
216
|
+
}
|
|
217
|
+
const prompt = buildAssessPrompt(goal, transcript);
|
|
218
|
+
let reply;
|
|
219
|
+
try {
|
|
220
|
+
reply = await chat(prompt);
|
|
221
|
+
} catch {
|
|
222
|
+
return { assessment: null, goal };
|
|
223
|
+
}
|
|
224
|
+
const assessment = parseAssessment(reply);
|
|
225
|
+
if (!assessment) return { assessment: null, goal };
|
|
226
|
+
const updated = applyAssessment(goal.id, assessment, opts);
|
|
227
|
+
return { assessment, goal: updated };
|
|
228
|
+
}
|
package/src/lib/goal-store.js
CHANGED
|
@@ -270,6 +270,39 @@ export function setStatus(id, status, opts = {}) {
|
|
|
270
270
|
);
|
|
271
271
|
}
|
|
272
272
|
|
|
273
|
+
/**
|
|
274
|
+
* Append drift flags to a goal's `drift.flags` list. Each flag is normalized to
|
|
275
|
+
* `{ at, kind, detail }`. Used by the run-end assessment (cc goal Phase 2) to
|
|
276
|
+
* record "no progress this run" / concern signals. Capped at 20 (newest kept).
|
|
277
|
+
* @param {string} id
|
|
278
|
+
* @param {Array<string|object>} flags
|
|
279
|
+
*/
|
|
280
|
+
export function addDriftFlags(id, flags, opts = {}) {
|
|
281
|
+
const list = (Array.isArray(flags) ? flags : [flags]).filter(Boolean);
|
|
282
|
+
if (list.length === 0) return getGoal(id, opts);
|
|
283
|
+
return mutate(
|
|
284
|
+
id,
|
|
285
|
+
(g) => {
|
|
286
|
+
for (const f of list) {
|
|
287
|
+
const flag =
|
|
288
|
+
typeof f === "string"
|
|
289
|
+
? { at: nowIso(), kind: "concern", detail: f }
|
|
290
|
+
: {
|
|
291
|
+
at: nowIso(),
|
|
292
|
+
kind: f.kind || "concern",
|
|
293
|
+
detail: f.detail || "",
|
|
294
|
+
};
|
|
295
|
+
g.drift.flags.push(flag);
|
|
296
|
+
}
|
|
297
|
+
// Keep only the most recent 20 to bound the file size.
|
|
298
|
+
if (g.drift.flags.length > 20) {
|
|
299
|
+
g.drift.flags = g.drift.flags.slice(-20);
|
|
300
|
+
}
|
|
301
|
+
},
|
|
302
|
+
opts,
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
|
|
273
306
|
/** Delete a goal. Returns true if it existed. */
|
|
274
307
|
export function deleteGoal(id, opts = {}) {
|
|
275
308
|
const root = opts.root || defaultRoot();
|
|
@@ -2198,12 +2198,34 @@ export async function* agentLoop(messages, options) {
|
|
|
2198
2198
|
);
|
|
2199
2199
|
if (stats.saved > 0 && compacted.length < messages.length) {
|
|
2200
2200
|
messages.splice(0, messages.length, ...compacted);
|
|
2201
|
+
// Persist the compaction so a later --resume rebuilds from the
|
|
2202
|
+
// shortened history. An explicit `onCompaction` hook (if a caller
|
|
2203
|
+
// provides one) takes precedence; otherwise self-persist a `compact`
|
|
2204
|
+
// event — but only when the session is already being persisted to
|
|
2205
|
+
// disk (the JSONL file exists), so a one-shot `cc agent -p` (which
|
|
2206
|
+
// never creates a session file) writes nothing. Opt out with
|
|
2207
|
+
// `persistCompaction: false`. Best-effort throughout.
|
|
2201
2208
|
if (typeof options.onCompaction === "function") {
|
|
2202
2209
|
try {
|
|
2203
2210
|
options.onCompaction(stats, compacted);
|
|
2204
2211
|
} catch {
|
|
2205
2212
|
// persistence is best-effort
|
|
2206
2213
|
}
|
|
2214
|
+
} else if (
|
|
2215
|
+
options.sessionId &&
|
|
2216
|
+
options.persistCompaction !== false
|
|
2217
|
+
) {
|
|
2218
|
+
try {
|
|
2219
|
+
const store = await import("../harness/jsonl-session-store.js");
|
|
2220
|
+
if (store.sessionExists(options.sessionId)) {
|
|
2221
|
+
store.appendCompactEvent(options.sessionId, {
|
|
2222
|
+
...stats,
|
|
2223
|
+
messages: compacted,
|
|
2224
|
+
});
|
|
2225
|
+
}
|
|
2226
|
+
} catch {
|
|
2227
|
+
// self-persist is best-effort
|
|
2228
|
+
}
|
|
2207
2229
|
}
|
|
2208
2230
|
yield { type: "compaction", stats, runId };
|
|
2209
2231
|
}
|
|
@@ -26,12 +26,14 @@ import {
|
|
|
26
26
|
agentLoop as coreAgentLoop,
|
|
27
27
|
formatToolArgs,
|
|
28
28
|
} from "./agent-core.js";
|
|
29
|
+
import { loadMcpConfig } from "./mcp-config.js";
|
|
29
30
|
import { IterationBudget } from "../lib/iteration-budget.js";
|
|
30
31
|
import {
|
|
31
32
|
startSession as jsonlStartSession,
|
|
32
33
|
appendUserMessage as jsonlAppendUserMessage,
|
|
33
34
|
appendAssistantMessage as jsonlAppendAssistantMessage,
|
|
34
35
|
appendTokenUsage as jsonlAppendTokenUsage,
|
|
36
|
+
appendCompactEvent as jsonlAppendCompactEvent,
|
|
35
37
|
rebuildMessages as jsonlRebuildMessages,
|
|
36
38
|
sessionExists as jsonlSessionExists,
|
|
37
39
|
getLastSessionId as jsonlGetLastSessionId,
|
|
@@ -245,6 +247,7 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
245
247
|
appendAssistantMessage:
|
|
246
248
|
deps.appendAssistantMessage || jsonlAppendAssistantMessage,
|
|
247
249
|
appendTokenUsage: deps.appendTokenUsage || jsonlAppendTokenUsage,
|
|
250
|
+
appendCompactEvent: deps.appendCompactEvent || jsonlAppendCompactEvent,
|
|
248
251
|
getLastSessionId: deps.getLastSessionId || jsonlGetLastSessionId,
|
|
249
252
|
};
|
|
250
253
|
const isStream = outputFormat === "stream-json";
|
|
@@ -367,6 +370,26 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
367
370
|
}
|
|
368
371
|
}
|
|
369
372
|
|
|
373
|
+
// --mcp-config: connect ad-hoc MCP servers for this run and expose their
|
|
374
|
+
// tools to the LLM (Claude-Code parity). Connection is best-effort — a server
|
|
375
|
+
// that fails to connect is logged to stderr and contributes no tools; a
|
|
376
|
+
// missing/empty config file fails fast (the user explicitly asked for MCP).
|
|
377
|
+
let mcp = null;
|
|
378
|
+
if (options.mcpConfig) {
|
|
379
|
+
const doLoadMcp = deps.loadMcpConfig || loadMcpConfig;
|
|
380
|
+
try {
|
|
381
|
+
mcp = await doLoadMcp(options.mcpConfig, { writeErr });
|
|
382
|
+
if (isText) {
|
|
383
|
+
for (const c of mcp.connected) {
|
|
384
|
+
writeErr(` mcp: ${c.server} (${c.tools} tools)\n`);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
} catch (err) {
|
|
388
|
+
writeErr(`Error: ${err.message}\n`);
|
|
389
|
+
return { exitCode: 1, result: err.message, isError: true };
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
370
393
|
const loopOptions = {
|
|
371
394
|
model,
|
|
372
395
|
provider,
|
|
@@ -382,11 +405,48 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
382
405
|
enabledToolNames,
|
|
383
406
|
disabledTools,
|
|
384
407
|
iterationBudget: budget,
|
|
408
|
+
// --mcp-config wiring: tool defs for the LLM + dispatch map + live client.
|
|
409
|
+
mcpClient: mcp?.mcpClient || null,
|
|
410
|
+
extraToolDefinitions: mcp?.extraToolDefinitions || undefined,
|
|
411
|
+
externalToolExecutors: mcp?.externalToolExecutors || undefined,
|
|
412
|
+
externalToolDescriptors: mcp?.externalToolDescriptors || undefined,
|
|
385
413
|
// chatFn passthrough lets tests drive the loop deterministically.
|
|
386
414
|
chatFn: deps.chatFn || options.chatFn || undefined,
|
|
387
415
|
signal: options.signal || undefined,
|
|
388
416
|
};
|
|
389
417
|
|
|
418
|
+
// Goal binding (cc goal, Phase 1). `--goal <id>` binds explicitly; `--goal`
|
|
419
|
+
// with no value (options.goal === true) auto-resolves from active/session.
|
|
420
|
+
// When omitted, headless stays goal-free (no behavior change). Best-effort:
|
|
421
|
+
// a failure here must never fail the run.
|
|
422
|
+
let boundGoalId = null;
|
|
423
|
+
if (options.goal !== undefined && options.goal !== false) {
|
|
424
|
+
try {
|
|
425
|
+
const explicitId = typeof options.goal === "string" ? options.goal : null;
|
|
426
|
+
const { resolveActiveGoal, linkSession } =
|
|
427
|
+
await import("../lib/goal-store.js");
|
|
428
|
+
const goal = (deps.resolveActiveGoal || resolveActiveGoal)({
|
|
429
|
+
explicitId,
|
|
430
|
+
sessionId,
|
|
431
|
+
});
|
|
432
|
+
if (goal) {
|
|
433
|
+
const { goalPrepareCall } = await import("../lib/goal-context.js");
|
|
434
|
+
loopOptions.prepareCall = goalPrepareCall(goal);
|
|
435
|
+
boundGoalId = goal.id;
|
|
436
|
+
// Link the session so a later `--continue`/`--resume` keeps this goal.
|
|
437
|
+
if (explicitId && persist !== false) {
|
|
438
|
+
try {
|
|
439
|
+
linkSession(goal.id, sessionId);
|
|
440
|
+
} catch {
|
|
441
|
+
/* linking is optional polish — never fatal */
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
} catch {
|
|
446
|
+
/* goal binding is best-effort — proceed without it */
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
390
450
|
const startedAt = deps.now ? deps.now() : Date.now();
|
|
391
451
|
const toolCalls = [];
|
|
392
452
|
const usage = { input_tokens: 0, output_tokens: 0 };
|
|
@@ -424,6 +484,7 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
424
484
|
resumed_from: resumeId,
|
|
425
485
|
history_messages: history.length,
|
|
426
486
|
additional_directories: additionalDirectories,
|
|
487
|
+
goal_id: boundGoalId,
|
|
427
488
|
});
|
|
428
489
|
|
|
429
490
|
try {
|
|
@@ -522,6 +583,17 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
522
583
|
writeErr(`Error: ${message}\n`);
|
|
523
584
|
}
|
|
524
585
|
return { exitCode: 1, result: message, isError: true };
|
|
586
|
+
} finally {
|
|
587
|
+
// Tear down ad-hoc MCP servers (--mcp-config) before returning, whether the
|
|
588
|
+
// loop completed or threw. Best-effort: a failed disconnect never masks the
|
|
589
|
+
// run's own outcome.
|
|
590
|
+
if (mcp?.mcpClient) {
|
|
591
|
+
try {
|
|
592
|
+
await mcp.mcpClient.disconnectAll();
|
|
593
|
+
} catch {
|
|
594
|
+
// ignore — disconnect is best-effort
|
|
595
|
+
}
|
|
596
|
+
}
|
|
525
597
|
}
|
|
526
598
|
|
|
527
599
|
// coreAgentLoop emits run-ended reason "budget-exhausted" when the iteration
|
|
@@ -543,6 +615,56 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
543
615
|
}
|
|
544
616
|
}
|
|
545
617
|
|
|
618
|
+
// Run-end goal self-assessment (cc goal Phase 2, opt-in via --goal-assess).
|
|
619
|
+
// Spends one extra completion to judge whether the run advanced the bound
|
|
620
|
+
// goal, then persists progress / key-result / drift updates. Best-effort: it
|
|
621
|
+
// must never change the run's own outcome.
|
|
622
|
+
if (options.goalAssess && boundGoalId && !isError) {
|
|
623
|
+
try {
|
|
624
|
+
const { getGoal } = await import("../lib/goal-store.js");
|
|
625
|
+
const goal = (deps.getGoal || getGoal)(boundGoalId);
|
|
626
|
+
if (goal) {
|
|
627
|
+
const { assessGoalProgress } = await import("../lib/goal-assess.js");
|
|
628
|
+
const doAssess = deps.assessGoalProgress || assessGoalProgress;
|
|
629
|
+
const assessChat =
|
|
630
|
+
deps.assessChat ||
|
|
631
|
+
(async (assessPrompt) => {
|
|
632
|
+
const { chatWithTools } = await import("./agent-core.js");
|
|
633
|
+
const r = await chatWithTools(
|
|
634
|
+
[{ role: "user", content: assessPrompt }],
|
|
635
|
+
{ model, provider, baseUrl, apiKey, enabledToolNames: [] },
|
|
636
|
+
);
|
|
637
|
+
return r?.message?.content || "";
|
|
638
|
+
});
|
|
639
|
+
const { assessment } = await doAssess({
|
|
640
|
+
goal,
|
|
641
|
+
transcript: { prompt: options.prompt, finalText, toolCalls },
|
|
642
|
+
chat: assessChat,
|
|
643
|
+
});
|
|
644
|
+
if (assessment) {
|
|
645
|
+
if (isText) {
|
|
646
|
+
writeErr(
|
|
647
|
+
` ◎ goal ${boundGoalId}: ${assessment.advanced ? "advanced" : "no progress"}` +
|
|
648
|
+
(assessment.progress != null
|
|
649
|
+
? ` (${assessment.progress}%)`
|
|
650
|
+
: "") +
|
|
651
|
+
"\n",
|
|
652
|
+
);
|
|
653
|
+
}
|
|
654
|
+
emitStream({
|
|
655
|
+
type: "goal_assessment",
|
|
656
|
+
goal_id: boundGoalId,
|
|
657
|
+
advanced: assessment.advanced,
|
|
658
|
+
progress: assessment.progress,
|
|
659
|
+
note: assessment.note,
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
} catch {
|
|
664
|
+
/* assessment is best-effort — never affect the run outcome */
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
|
|
546
668
|
if (isStream) {
|
|
547
669
|
emitStream({
|
|
548
670
|
type: "result",
|
|
@@ -207,6 +207,26 @@ export async function runAgentHeadlessStream(options = {}, deps = {}) {
|
|
|
207
207
|
additional_directories: additionalDirectories,
|
|
208
208
|
});
|
|
209
209
|
|
|
210
|
+
// Goal binding (cc goal, Phase 1) — resolved once and injected on every turn.
|
|
211
|
+
// `--goal <id>` binds explicitly; `--goal` with no value auto-resolves.
|
|
212
|
+
let goalPrepareCallFn;
|
|
213
|
+
if (options.goal !== undefined && options.goal !== false) {
|
|
214
|
+
try {
|
|
215
|
+
const explicitId = typeof options.goal === "string" ? options.goal : null;
|
|
216
|
+
const { resolveActiveGoal } = await import("../lib/goal-store.js");
|
|
217
|
+
const goal = (deps.resolveActiveGoal || resolveActiveGoal)({
|
|
218
|
+
explicitId,
|
|
219
|
+
sessionId,
|
|
220
|
+
});
|
|
221
|
+
if (goal) {
|
|
222
|
+
const { goalPrepareCall } = await import("../lib/goal-context.js");
|
|
223
|
+
goalPrepareCallFn = goalPrepareCall(goal);
|
|
224
|
+
}
|
|
225
|
+
} catch {
|
|
226
|
+
/* goal binding is best-effort — proceed without it */
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
210
230
|
const loopOptionsBase = {
|
|
211
231
|
model,
|
|
212
232
|
provider,
|
|
@@ -219,6 +239,7 @@ export async function runAgentHeadlessStream(options = {}, deps = {}) {
|
|
|
219
239
|
approvalGate,
|
|
220
240
|
enabledToolNames,
|
|
221
241
|
disabledTools,
|
|
242
|
+
prepareCall: goalPrepareCallFn,
|
|
222
243
|
chatFn: deps.chatFn || options.chatFn || undefined,
|
|
223
244
|
signal: options.signal || undefined,
|
|
224
245
|
// --include-partial-messages: stream live assistant-text deltas as
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ad-hoc MCP config loader for headless agent runs (`cc agent --mcp-config`).
|
|
3
|
+
*
|
|
4
|
+
* Claude-Code parity: load a JSON file describing MCP servers, connect them for
|
|
5
|
+
* the duration of one headless run, and expose each server's tools to the LLM.
|
|
6
|
+
*
|
|
7
|
+
* The agent loop already knows how to *call* an MCP tool — it just needs three
|
|
8
|
+
* things wired into its options (see agent-core.js):
|
|
9
|
+
* - extraToolDefinitions → the LLM sees the tools (OpenAI function shape)
|
|
10
|
+
* - externalToolExecutors → name → {kind:"mcp", serverName, toolName}
|
|
11
|
+
* - mcpClient → executes mcpClient.callTool(server, tool, args)
|
|
12
|
+
*
|
|
13
|
+
* Tools are namespaced `mcp__<server>__<tool>` (Claude-Code convention) so they
|
|
14
|
+
* never collide with built-in agent tools or across servers.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import fs from "fs";
|
|
18
|
+
import { MCPClient } from "../harness/mcp-client.js";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Normalize a parsed config object into a `{ name: serverConfig }` map.
|
|
22
|
+
* Accepts the Claude-Code shape `{ "mcpServers": { ... } }`, the bundle shape
|
|
23
|
+
* `{ "servers": { ... } }`, or a bare `{ name: cfg }` map.
|
|
24
|
+
*/
|
|
25
|
+
export function parseMcpServers(raw) {
|
|
26
|
+
const block =
|
|
27
|
+
(raw && typeof raw === "object" && (raw.mcpServers || raw.servers)) ||
|
|
28
|
+
raw ||
|
|
29
|
+
{};
|
|
30
|
+
const out = {};
|
|
31
|
+
for (const [name, cfg] of Object.entries(block)) {
|
|
32
|
+
if (!cfg || typeof cfg !== "object") continue;
|
|
33
|
+
out[name] = {
|
|
34
|
+
command: cfg.command,
|
|
35
|
+
args: Array.isArray(cfg.args) ? cfg.args : [],
|
|
36
|
+
env: cfg.env && typeof cfg.env === "object" ? cfg.env : {},
|
|
37
|
+
url: cfg.url,
|
|
38
|
+
transport: cfg.transport,
|
|
39
|
+
headers: cfg.headers && typeof cfg.headers === "object" ? cfg.headers : {},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
return out;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Namespaced tool name exposed to the LLM. */
|
|
46
|
+
export function mcpToolName(server, tool) {
|
|
47
|
+
return `mcp__${server}__${tool}`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Connect the given servers and build the agent-loop wiring. Connection
|
|
52
|
+
* failures are reported (writeErr) but never abort the run — a broken server
|
|
53
|
+
* just contributes no tools.
|
|
54
|
+
*
|
|
55
|
+
* @param {object} servers `{ name: { command|url, args, env, transport, headers } }`
|
|
56
|
+
* @param {object} [deps] { writeErr, createClient }
|
|
57
|
+
* @returns {Promise<{mcpClient, extraToolDefinitions, externalToolExecutors,
|
|
58
|
+
* externalToolDescriptors, connected}>}
|
|
59
|
+
*/
|
|
60
|
+
export async function setupMcpFromConfig(servers, deps = {}) {
|
|
61
|
+
const writeErr = deps.writeErr || (() => {});
|
|
62
|
+
const createClient = deps.createClient || (() => new MCPClient());
|
|
63
|
+
|
|
64
|
+
const mcpClient = createClient();
|
|
65
|
+
const extraToolDefinitions = [];
|
|
66
|
+
const externalToolExecutors = {};
|
|
67
|
+
const externalToolDescriptors = {};
|
|
68
|
+
const connected = [];
|
|
69
|
+
|
|
70
|
+
for (const [name, cfg] of Object.entries(servers)) {
|
|
71
|
+
let result;
|
|
72
|
+
try {
|
|
73
|
+
result = await mcpClient.connect(name, cfg);
|
|
74
|
+
} catch (err) {
|
|
75
|
+
writeErr(` mcp: failed to connect "${name}": ${err.message}\n`);
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
const tools = Array.isArray(result?.tools) ? result.tools : [];
|
|
79
|
+
connected.push({ server: name, tools: tools.length });
|
|
80
|
+
for (const t of tools) {
|
|
81
|
+
if (!t || !t.name) continue;
|
|
82
|
+
const full = mcpToolName(name, t.name);
|
|
83
|
+
extraToolDefinitions.push({
|
|
84
|
+
type: "function",
|
|
85
|
+
function: {
|
|
86
|
+
name: full,
|
|
87
|
+
description: t.description || `MCP tool "${t.name}" on server "${name}"`,
|
|
88
|
+
parameters: t.inputSchema || { type: "object", properties: {} },
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
externalToolExecutors[full] = {
|
|
92
|
+
kind: "mcp",
|
|
93
|
+
serverName: name,
|
|
94
|
+
toolName: t.name,
|
|
95
|
+
};
|
|
96
|
+
externalToolDescriptors[full] = {
|
|
97
|
+
name: full,
|
|
98
|
+
kind: "mcp",
|
|
99
|
+
category: "mcp",
|
|
100
|
+
source: name,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
mcpClient,
|
|
107
|
+
extraToolDefinitions,
|
|
108
|
+
externalToolExecutors,
|
|
109
|
+
externalToolDescriptors,
|
|
110
|
+
connected,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Read + parse a `--mcp-config` file and connect its servers.
|
|
116
|
+
* Throws on an unreadable/empty config (fail fast — the user asked for MCP).
|
|
117
|
+
*
|
|
118
|
+
* @param {string} filePath
|
|
119
|
+
* @param {object} [deps] { writeErr, createClient, readFile }
|
|
120
|
+
*/
|
|
121
|
+
export async function loadMcpConfig(filePath, deps = {}) {
|
|
122
|
+
const readFile = deps.readFile || ((p) => fs.readFileSync(p, "utf-8"));
|
|
123
|
+
let raw;
|
|
124
|
+
try {
|
|
125
|
+
raw = JSON.parse(readFile(filePath));
|
|
126
|
+
} catch (err) {
|
|
127
|
+
throw new Error(
|
|
128
|
+
`--mcp-config: cannot read/parse "${filePath}": ${err.message}`,
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
const servers = parseMcpServers(raw);
|
|
132
|
+
if (Object.keys(servers).length === 0) {
|
|
133
|
+
throw new Error(
|
|
134
|
+
`--mcp-config: no servers found in "${filePath}" ` +
|
|
135
|
+
`(expected {"mcpServers": {"name": {"command": "..."}}}).`,
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
return setupMcpFromConfig(servers, deps);
|
|
139
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{I as v,P as u,J as S,U as s,R as a,S as y,K as C,Q as x,V as w,_ as B,b as n}from"./vendor-BvqAck49.js";import{_ as k}from"./index-BItcSqan.js";import"./icons-DP3uiYxy.js";const M={__name:"ChatBubbleRenderer",props:{event:{type:Object,required:!0}},setup(c,{expose:d}){d();const t=c,r=n(()=>{const e=t.event.content||{};return e.text||e.body||e.message||e.title||JSON.stringify(e).slice(0,200)}),i=n(()=>{const e=t.event.content||{};return e.from||e.sender||e.senderName||t.event.actor||"(unknown)"}),l=n(()=>{const e=(t.event.actor||"").toLowerCase();return e.includes("self")||e==="me"||e.endsWith("_self")}),o=n(()=>{const e=t.event.source.adapter||"";return e.startsWith("messaging-qq")?"magenta":e==="wechat"?"green":"blue"}),m=n(()=>{if(!t.event.occurredAt)return"";try{const e=new Date(t.event.occurredAt),p=e.getFullYear(),f=String(e.getMonth()+1).padStart(2,"0"),g=String(e.getDate()).padStart(2,"0"),b=String(e.getHours()).padStart(2,"0"),h=String(e.getMinutes()).padStart(2,"0");return`${p}-${f}-${g} ${b}:${h}`}catch{return""}}),_={props:t,messageText:r,actorLabel:i,isMine:l,adapterColor:o,formattedTime:m,computed:n};return Object.defineProperty(_,"__isScriptSetup",{enumerable:!1,value:!0}),_}},N={class:"bubble"},T={class:"meta"},R={class:"actor"},V={class:"time"},q={class:"body"};function D(c,d,t,r,i,l){const o=v("a-tag");return u(),S("div",{class:B(["chat-row",{mine:r.isMine}])},[s("div",N,[s("div",T,[s("span",R,a(r.actorLabel),1),s("span",V,a(r.formattedTime),1)]),s("div",q,a(r.messageText),1),t.event.source.adapter?(u(),y(o,{key:0,class:"src",color:r.adapterColor},{default:C(()=>[x(a(t.event.source.adapter),1)]),_:1},8,["color"])):w("v-if",!0)])],2)}const A=k(M,[["render",D],["__scopeId","data-v-49238629"],["__file","/tmp/cc-web-panel-v2hnMt/repo/packages/web-panel/src/components/pdh/renderers/ChatBubbleRenderer.vue"]]);export{A as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{I as n,J as g,c as l,K as r,N as u,P as h,U as o,R as t,Q as b}from"./vendor-BvqAck49.js";import{_ as v,b as m}from"./index-BItcSqan.js";import{a7 as M,M as y}from"./icons-DP3uiYxy.js";const B={__name:"MobileProjects",setup(p,{expose:i}){i();const c=u(),{t:e}=m();function a(){c.push("/projects")}function _(){c.push("/mobile-bridge")}const s={router:c,t:e,goToProjects:a,goToMobileBridge:_,get useRouter(){return u},get useI18n(){return m},get MobileOutlined(){return y},get FolderOutlined(){return M}};return Object.defineProperty(s,"__isScriptSetup",{enumerable:!1,value:!0}),s}},x={class:"mobile-projects-placeholder"},O={class:"title"},k={class:"subtitle"},w={class:"explainer"};function T(p,i,c,e,a,_){const s=n("a-alert"),d=n("a-button"),f=n("a-space"),P=n("a-empty"),j=n("a-card");return h(),g("div",x,[l(j,null,{default:r(()=>[l(P,{description:!1},{image:r(()=>[l(e.MobileOutlined,{style:{fontSize:"64px",color:"#bfbfbf"}})]),default:r(()=>[o("h2",O,t(e.t("mobileProjects.title")),1),o("p",k,t(e.t("mobileProjects.subtitle")),1),l(s,{message:e.t("mobileProjects.v02Banner"),type:"info","show-icon":"",class:"banner"},null,8,["message"]),o("div",w,[o("h3",null,t(e.t("mobileProjects.currentDirection")),1),o("p",null,t(e.t("mobileProjects.currentDirectionBody")),1),o("ul",null,[o("li",null,t(e.t("mobileProjects.stepPhone")),1),o("li",null,t(e.t("mobileProjects.stepTap")),1),o("li",null,t(e.t("mobileProjects.stepPull")),1)]),o("h3",null,t(e.t("mobileProjects.reverseDirection")),1),o("p",null,t(e.t("mobileProjects.reverseDirectionBody")),1)]),l(f,{class:"actions"},{default:r(()=>[l(d,{type:"primary",onClick:e.goToProjects},{default:r(()=>[l(e.FolderOutlined),b(" "+t(e.t("mobileProjects.viewLocalProjects")),1)]),_:1}),l(d,{onClick:e.goToMobileBridge},{default:r(()=>[l(e.MobileOutlined),b(" "+t(e.t("mobileProjects.checkBridge")),1)]),_:1})]),_:1})]),_:1})]),_:1})])}const N=v(B,[["render",T],["__scopeId","data-v-9262cc45"],["__file","/tmp/cc-web-panel-v2hnMt/repo/packages/web-panel/src/views/MobileProjects.vue"]]);export{N as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{I as w,P as l,J as u,U as s,R as n,c as i,K as v,Q as _,V as g,b as a}from"./vendor-BvqAck49.js";import{_ as S}from"./index-BItcSqan.js";import"./icons-DP3uiYxy.js";const k={__name:"OrderTableRenderer",props:{event:{type:Object,required:!0}},setup(p,{expose:r}){r();const o=p,e=a(()=>o.event.content||{}),d=a(()=>o.event.extra||{}),m=a(()=>d.value.merchant||e.value.merchant||e.value.counterparty||"—"),c=a(()=>e.value.title||e.value.name||e.value.itemName||e.value.text||"—"),y=a(()=>{const t=e.value.amount??e.value.price??e.value.total;return t==null?"—":`${e.value.currency||"¥"} ${typeof t=="number"?t.toFixed(2):t}`}),T=a(()=>d.value.orderNo||e.value.orderNo||e.value.orderId),f=a(()=>e.value.status||e.value.state),h=a(()=>{const t=(f.value||"").toLowerCase();return t.includes("成功")||t.includes("succe")||t.includes("paid")?"green":t.includes("退")||t.includes("refund")?"orange":t.includes("失败")||t.includes("fail")?"red":"default"}),b=a(()=>{if(!o.event.occurredAt)return"";const t=new Date(o.event.occurredAt);return`${t.getFullYear()}-${String(t.getMonth()+1).padStart(2,"0")}-${String(t.getDate()).padStart(2,"0")} ${String(t.getHours()).padStart(2,"0")}:${String(t.getMinutes()).padStart(2,"0")}`}),x={props:o,c:e,e:d,merchantText:m,itemText:c,amountText:y,orderNo:T,statusText:f,statusColor:h,formattedTime:b,computed:a};return Object.defineProperty(x,"__isScriptSetup",{enumerable:!1,value:!0}),x}},N={class:"order-card"},C={class:"head"},O={class:"time"},V={class:"row"},R={class:"val"},B={class:"row"},D={class:"val"},I={class:"row amount-row"},M={class:"val amount"},j={key:0,class:"row"},A={class:"val mono"},F={key:1,class:"row"};function P(p,r,o,e,d,m){const c=w("a-tag");return l(),u("div",N,[s("div",C,[s("span",O,n(e.formattedTime),1),i(c,{color:"gold"},{default:v(()=>[_(n(o.event.source.adapter),1)]),_:1}),i(c,null,{default:v(()=>[_(n(o.event.subtype),1)]),_:1})]),s("div",V,[r[0]||(r[0]=s("span",{class:"key"},"商户",-1)),s("span",R,n(e.merchantText),1)]),s("div",B,[r[1]||(r[1]=s("span",{class:"key"},"商品/项目",-1)),s("span",D,n(e.itemText),1)]),s("div",I,[r[2]||(r[2]=s("span",{class:"key"},"金额",-1)),s("span",M,n(e.amountText),1)]),e.orderNo?(l(),u("div",j,[r[3]||(r[3]=s("span",{class:"key"},"单号",-1)),s("span",A,n(e.orderNo),1)])):g("v-if",!0),e.statusText?(l(),u("div",F,[r[4]||(r[4]=s("span",{class:"key"},"状态",-1)),i(c,{color:e.statusColor},{default:v(()=>[_(n(e.statusText),1)]),_:1},8,["color"])])):g("v-if",!0)])}const K=S(k,[["render",P],["__scopeId","data-v-5ed5524d"],["__file","/tmp/cc-web-panel-v2hnMt/repo/packages/web-panel/src/components/pdh/renderers/OrderTableRenderer.vue"]]);export{K as default};
|