chainlesschain 0.161.2 → 0.161.4
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 +6 -6
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/AIOps-K85peDCg.js +1 -0
- package/src/assets/web-panel/assets/{ActionButton-BvMi4awy.js → ActionButton-DoRABhmk.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-hRk2ziup.js → Analytics-B7p7X_lS.js} +2 -2
- package/src/assets/web-panel/assets/AppLayout-BQb9-BUE.js +1 -0
- package/src/assets/web-panel/assets/AppLayout-P7jhSfLy.css +1 -0
- package/src/assets/web-panel/assets/Audit-CHj8w38X.js +1 -0
- package/src/assets/web-panel/assets/Backup-BsgPajcJ.js +1 -0
- package/src/assets/web-panel/assets/BaseInput-kGeksnCj.js +1 -0
- package/src/assets/web-panel/assets/Chat-2e3EGiwk.js +7 -0
- package/src/assets/web-panel/assets/Chat-D4oAhi11.css +1 -0
- package/src/assets/web-panel/assets/{Checkbox-C9dkWb-7.js → Checkbox-DirndeMM.js} +1 -1
- package/src/assets/web-panel/assets/Codegen-UQuEF6rH.js +1 -0
- package/src/assets/web-panel/assets/{Col-JyQOivHb.js → Col-myVf7h0M.js} +1 -1
- package/src/assets/web-panel/assets/Community-C3Gt4JNP.js +1 -0
- package/src/assets/web-panel/assets/Compact-B4TJcIuz.js +1 -0
- package/src/assets/web-panel/assets/Compliance-DKye9lMe.js +1 -0
- package/src/assets/web-panel/assets/{Cowork-V-tDxtrt.js → Cowork-bD9Z8K9J.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-YgEeQvdV.js → Cron-BrQZ3zwQ.js} +2 -2
- package/src/assets/web-panel/assets/Crosschain-C7m4dIrR.js +1 -0
- package/src/assets/web-panel/assets/{DID-swdBCdMZ.js → DID-CGniRypj.js} +2 -2
- package/src/assets/web-panel/assets/Dashboard-BqQfrJLe.js +3 -0
- package/src/assets/web-panel/assets/{Dropdown-mlITwb7d.js → Dropdown-Csici42Z.js} +1 -1
- package/src/assets/web-panel/assets/Federation-D2boc2WU.js +1 -0
- package/src/assets/web-panel/assets/{FormItemContext-BYmWDwAT.js → FormItemContext-BpXHcM06.js} +1 -1
- package/src/assets/web-panel/assets/{Git-Du1k1iHz.js → Git-C7QHt3Hy.js} +2 -2
- package/src/assets/web-panel/assets/Governance-DPBJNo9u.js +1 -0
- package/src/assets/web-panel/assets/Inference-Ca8VokDu.js +1 -0
- package/src/assets/web-panel/assets/KnowledgeGraph-CrFGz4u2.js +1 -0
- package/src/assets/web-panel/assets/{Logs-CElvIBBJ.js → Logs-DMhNTvH6.js} +2 -2
- package/src/assets/web-panel/assets/Marketplace-DB0lVRRk.js +1 -0
- package/src/assets/web-panel/assets/{McpTools-BpHkrlka.js → McpTools-BmFQp5wQ.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-DeU9ys_m.js → Memory-DDooyF2l.js} +2 -2
- package/src/assets/web-panel/assets/Mtc-Ci6BDiRi.js +6 -0
- package/src/assets/web-panel/assets/Mtc-DHwbdUqA.css +1 -0
- package/src/assets/web-panel/assets/MtcAudit-B0x8gahq.css +1 -0
- package/src/assets/web-panel/assets/MtcAudit-J-DgVOwR.js +19 -0
- package/src/assets/web-panel/assets/NLProgramming-Uv0jicWQ.js +1 -0
- package/src/assets/web-panel/assets/Notes-BHauapxR.css +1 -0
- package/src/assets/web-panel/assets/Notes-SGtdMwfH.js +7 -0
- package/src/assets/web-panel/assets/NotificationSettings-Cg4yXikq.js +1 -0
- package/src/assets/web-panel/assets/NotificationSettings-Co0e0uEO.css +1 -0
- package/src/assets/web-panel/assets/{Organization-B_SHESSc.js → Organization-Bij8W2mU.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-R2SOGT0l.js → Overflow-DNGM4vCD.js} +1 -1
- package/src/assets/web-panel/assets/{OverrideContext-Nubhv68k.js → OverrideContext-DSwW21YS.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-IYYy3cEd.js → P2P-9DlPC_rr.js} +2 -2
- package/src/assets/web-panel/assets/{Permissions-CR1N42yW.js → Permissions-BVJwnBDW.js} +3 -3
- package/src/assets/web-panel/assets/Pipeline-lWCcdQ9k.js +1 -0
- package/src/assets/web-panel/assets/Privacy-BN60IEMG.js +1 -0
- package/src/assets/web-panel/assets/{ProjectSettings-CEDhpgbs.js → ProjectSettings-GJoyBlLy.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-DABi6ylb.js → Projects-Dvs_UqGv.js} +2 -2
- package/src/assets/web-panel/assets/{Providers-HzrcE8ma.js → Providers-A5KukRrJ.js} +2 -2
- package/src/assets/web-panel/assets/QuickAsk-D4K2pIHJ.js +1 -0
- package/src/assets/web-panel/assets/Recommend-DQKR520B.js +1 -0
- package/src/assets/web-panel/assets/Reputation-BVntfhQV.js +1 -0
- package/src/assets/web-panel/assets/Row-Do4ULXpm.js +1 -0
- package/src/assets/web-panel/assets/RssFeed-FAHzJo-E.js +3 -0
- package/src/assets/web-panel/assets/Search-DsOTkKd1.js +1 -0
- package/src/assets/web-panel/assets/{Security-DdW4hu_4.js → Security-hglxmoVZ.js} +3 -3
- package/src/assets/web-panel/assets/{Services-CnzEzGFN.js → Services-C6Rv4i8z.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-D6RevdW2.js → Skeleton-D-dXFspp.js} +2 -2
- package/src/assets/web-panel/assets/Skills-D6sVDuOT.js +1 -0
- package/src/assets/web-panel/assets/Sla-8O-JYjQq.js +1 -0
- package/src/assets/web-panel/assets/SpeechSettings-DSOYsk6R.js +1 -0
- package/src/assets/web-panel/assets/SyncSettings-C5nhT_6b.js +1 -0
- package/src/assets/web-panel/assets/SyncSettings-C6cDrwDR.css +1 -0
- package/src/assets/web-panel/assets/Tasks-NrpJCUFD.js +1 -0
- package/src/assets/web-panel/assets/Templates-Ca2HO-Ff.js +1 -0
- package/src/assets/web-panel/assets/Tenant-BWXC1pLX.js +1 -0
- package/src/assets/web-panel/assets/Tokens-DUtMwWxZ.js +1 -0
- package/src/assets/web-panel/assets/{Trigger-DkSZjOlY.js → Trigger-TyynxipZ.js} +1 -1
- package/src/assets/web-panel/assets/Trust-b4KDouFb.js +1 -0
- package/src/assets/web-panel/assets/UkeySign-CJpVhu8c.js +1 -0
- package/src/assets/web-panel/assets/VideoEditing-Bpn0V8T2.js +1 -0
- package/src/assets/web-panel/assets/{Wallet-DHbi5dHt.js → Wallet-CzBLjCnl.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-BkGDI33-.js → WebAuthn-HnRCEu5Q.js} +5 -5
- package/src/assets/web-panel/assets/WorkflowEditor-CMWDod4n.js +1 -0
- package/src/assets/web-panel/assets/{_getTag-CWA3v-Ds.js → _getTag-BVc6NQ_K.js} +1 -1
- package/src/assets/web-panel/assets/chat-D7QE42qx.js +1 -0
- package/src/assets/web-panel/assets/{collapseMotion-BIjDVXtT.js → collapseMotion-6-_FbiKl.js} +1 -1
- package/src/assets/web-panel/assets/{colors-D2tTvuDI.js → colors-BO5nQJrH.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-CqCEUZiy.js → compact-item-By0t_ZDX.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-C6HFlAQP.js → createContext-CHZw2WQy.js} +1 -1
- package/src/assets/web-panel/assets/{debounce-Q0BOJsYd.js → debounce-D9xTw4Sx.js} +1 -1
- package/src/assets/web-panel/assets/{echarts-DmBLM6YO.js → echarts-BrEnqXDv.js} +1 -1
- package/src/assets/web-panel/assets/{hasIn-yp2CbhYc.js → hasIn-CHScXGSD.js} +1 -1
- package/src/assets/web-panel/assets/icons-B2G69bhT.js +57 -0
- package/src/assets/web-panel/assets/index-B-VML4Pz.js +1 -0
- package/src/assets/web-panel/assets/index-B6FaCH6K.js +55 -0
- package/src/assets/web-panel/assets/index-BCoEssNV.js +1 -0
- package/src/assets/web-panel/assets/index-BFMpMM1y.js +21 -0
- package/src/assets/web-panel/assets/index-BIoymtmG.js +1 -0
- package/src/assets/web-panel/assets/{index-B2qFUwGb.js → index-BVL4FbJ3.js} +2 -2
- package/src/assets/web-panel/assets/{index-B0jkl2Zb.js → index-BXXqtwrw.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cpfx7-LN.js → index-BY-BnqWj.js} +2 -2
- package/src/assets/web-panel/assets/{index-DnehXcB-.js → index-Bbf4Smm-.js} +1 -1
- package/src/assets/web-panel/assets/{index-w1xShUDf.js → index-BdOG1g8i.js} +1 -1
- package/src/assets/web-panel/assets/{index-DlgMVieO.js → index-BgAPyeU5.js} +1 -1
- package/src/assets/web-panel/assets/index-Bn_0bupT.js +69 -0
- package/src/assets/web-panel/assets/index-BnaKc6VJ.js +1 -0
- package/src/assets/web-panel/assets/index-BqMhh2pY.js +1 -0
- package/src/assets/web-panel/assets/{index-CucxAdwN.js → index-BuzilAPw.js} +2 -2
- package/src/assets/web-panel/assets/{index-BTL2yIvT.js → index-By3TOq5w.js} +1 -1
- package/src/assets/web-panel/assets/index-C1ssAFdz.js +1 -0
- package/src/assets/web-panel/assets/{index-DEKuiAPQ.js → index-CAPiTJT4.js} +2 -2
- package/src/assets/web-panel/assets/{index-BlVnFOFL.js → index-CCOGrBpN.js} +1 -1
- package/src/assets/web-panel/assets/{index-JszcDpsT.js → index-COVlQxSz.js} +2 -2
- package/src/assets/web-panel/assets/{index-Dj4P0iWm.js → index-CYCwDDoS.js} +1 -1
- package/src/assets/web-panel/assets/{index-D34iabcS.js → index-C_EIwaP7.js} +8 -8
- package/src/assets/web-panel/assets/{index-CXf0zL5i.js → index-CfZqQ0JR.js} +3 -3
- package/src/assets/web-panel/assets/{index-CcwZodUl.js → index-CpHzrw8g.js} +1 -1
- package/src/assets/web-panel/assets/index-CszhN_r3.js +1 -0
- package/src/assets/web-panel/assets/index-Czk89HQF.js +1 -0
- package/src/assets/web-panel/assets/{index-BxSsO6Sm.js → index-D3WrGn-J.js} +2 -2
- package/src/assets/web-panel/assets/index-DS0o68gc.js +3 -0
- package/src/assets/web-panel/assets/{index-NNymVAza.js → index-DZ4t_1f-.js} +2 -2
- package/src/assets/web-panel/assets/{index-B8Qxu0q2.js → index-D_QlAaj5.js} +1 -1
- package/src/assets/web-panel/assets/index-DgWJBqLh.js +13 -0
- package/src/assets/web-panel/assets/{index-CFoFkVUt.js → index-DiHryOr_.js} +4 -4
- package/src/assets/web-panel/assets/index-DqPwMq7i.js +1 -0
- package/src/assets/web-panel/assets/{index-Blq49aTW.js → index-Qm6Kn3iq.js} +1 -1
- package/src/assets/web-panel/assets/index-U1O0AXXH.js +1 -0
- package/src/assets/web-panel/assets/index-jRQNL6Yj.js +12 -0
- package/src/assets/web-panel/assets/index-sgVQrC2c.js +1 -0
- package/src/assets/web-panel/assets/{index-CrEEL63u.js → index-yLdzB8i-.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-DfdVxwz6.js → initDefaultProps-CMFUJGsa.js} +1 -1
- package/src/assets/web-panel/assets/{isPlainObject-DSTnI585.js → isPlainObject-_gz-soRW.js} +1 -1
- package/src/assets/web-panel/assets/isSymbol-DAy2hlR5.js +1 -0
- package/src/assets/web-panel/assets/{motion-CL0bdvJg.js → motion-CTtMugIF.js} +2 -2
- package/src/assets/web-panel/assets/{move-Bo9Fgzv7.js → move-DHjaLqhq.js} +1 -1
- package/src/assets/web-panel/assets/mtc-parser-Cc3pB9Xm.js +1 -0
- package/src/assets/web-panel/assets/{omit-C-cc6wHr.js → omit-COm0v71g.js} +1 -1
- package/src/assets/web-panel/assets/operationUnit-vCu2co3e.js +1 -0
- package/src/assets/web-panel/assets/{pickAttrs-CjLp5RN-.js → pickAttrs-C3Ftf-sl.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-CJa8gsqa.js → placementArrow-IJhTwCmF.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-CcDj3P-p.js → responsiveObserve-Cmu2mrKT.js} +1 -1
- package/src/assets/web-panel/assets/{slide-CpvbHO26.js → slide-CnWCDeu_.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-BK69kP1U.js → statusUtils-CSOJeZ-L.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-CDvBRzsG.js → styleChecker-94VBD7Xs.js} +1 -1
- package/src/assets/web-panel/assets/useFlexGapSupport-CBjbWuc9.js +1 -0
- package/src/assets/web-panel/assets/useFs-DfXp51Mk.js +1 -0
- package/src/assets/web-panel/assets/{useMergedState-TP9VIF2K.js → useMergedState-n0f81Aew.js} +1 -1
- package/src/assets/web-panel/assets/{useRefs-BhIz_lC3.js → useRefs-CTh0C9xI.js} +1 -1
- package/src/assets/web-panel/assets/{useState-CpKsyozn.js → useState-4T1Ntfpx.js} +1 -1
- package/src/assets/web-panel/assets/{vendor-B6ToihkA.js → vendor-Ci2pTZ_t.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-Bzp-FsbB.js → vnode-CGY3YRLi.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-BHqpWXJV.js → zoom-CZllDiMO.js} +1 -1
- package/src/assets/web-panel/index.html +3 -3
- package/src/commands/mtc.js +2 -0
- package/src/gateways/ws/chat-intent-protocol.js +192 -0
- package/src/gateways/ws/message-dispatcher.js +6 -0
- package/src/gateways/ws/ws-server.js +20 -0
- package/src/lib/chat-intent-service.js +530 -0
- package/src/lib/config-manager.js +3 -1
- package/src/lib/governance-v2-helpers.js +1 -1
- package/src/lib/sync-manager.js +119 -25
- package/src/assets/web-panel/assets/AIOps-CoZ9bIqF.js +0 -1
- package/src/assets/web-panel/assets/AppLayout-CtGprHSx.css +0 -1
- package/src/assets/web-panel/assets/AppLayout-_JR3Gko8.js +0 -1
- package/src/assets/web-panel/assets/Audit-D8WmaHdX.js +0 -1
- package/src/assets/web-panel/assets/Backup-CogYVeiE.js +0 -1
- package/src/assets/web-panel/assets/BaseInput-NAp5_OPY.js +0 -1
- package/src/assets/web-panel/assets/Chat-ByiYUboW.css +0 -1
- package/src/assets/web-panel/assets/Chat-DkQnhjfk.js +0 -2
- package/src/assets/web-panel/assets/Codegen-BHyJ3j-p.js +0 -1
- package/src/assets/web-panel/assets/Community-UMq5QuBA.js +0 -1
- package/src/assets/web-panel/assets/Compact-DGlwooBJ.js +0 -1
- package/src/assets/web-panel/assets/Compliance-2rWGO55k.js +0 -1
- package/src/assets/web-panel/assets/Crosschain-Cgd5cRKn.js +0 -1
- package/src/assets/web-panel/assets/Dashboard-ClnWtxsT.js +0 -3
- package/src/assets/web-panel/assets/Federation-DtUN3wQa.js +0 -1
- package/src/assets/web-panel/assets/Governance-B2TFaWsf.js +0 -1
- package/src/assets/web-panel/assets/Inference-Cm_hmXla.js +0 -1
- package/src/assets/web-panel/assets/KnowledgeGraph-DLaLMo4r.js +0 -1
- package/src/assets/web-panel/assets/Marketplace-BlR3RCDV.js +0 -1
- package/src/assets/web-panel/assets/Mtc-CCJZpnJo.js +0 -6
- package/src/assets/web-panel/assets/Mtc-Cc8OJxe_.css +0 -1
- package/src/assets/web-panel/assets/NLProgramming-B_Tie6j1.js +0 -1
- package/src/assets/web-panel/assets/Notes-BcpuirPj.js +0 -7
- package/src/assets/web-panel/assets/Notes-DKkPfXlY.css +0 -1
- package/src/assets/web-panel/assets/Pipeline-nwFpKsU_.js +0 -1
- package/src/assets/web-panel/assets/Privacy-BGpz72PX.js +0 -1
- package/src/assets/web-panel/assets/QuickAsk-6FgX9DC6.js +0 -1
- package/src/assets/web-panel/assets/Recommend-BPhQwye7.js +0 -1
- package/src/assets/web-panel/assets/Reputation-BL6hTN1s.js +0 -1
- package/src/assets/web-panel/assets/Row-2akLU3YS.js +0 -1
- package/src/assets/web-panel/assets/RssFeed-D6qNq6Ht.js +0 -3
- package/src/assets/web-panel/assets/Search-R_b-u9oL.js +0 -1
- package/src/assets/web-panel/assets/Skills-DD5ReHH7.js +0 -1
- package/src/assets/web-panel/assets/Sla-CaQOOsjD.js +0 -1
- package/src/assets/web-panel/assets/SpeechSettings-D-pGIn9Z.js +0 -1
- package/src/assets/web-panel/assets/Tasks-BbdO_i4Q.js +0 -1
- package/src/assets/web-panel/assets/Templates-CupAugDn.js +0 -1
- package/src/assets/web-panel/assets/Tenant-rseAzHcY.js +0 -1
- package/src/assets/web-panel/assets/Tokens-DXMokNbR.js +0 -1
- package/src/assets/web-panel/assets/Trust-CKb7QDH1.js +0 -1
- package/src/assets/web-panel/assets/UkeySign-Cux8_Ib_.js +0 -1
- package/src/assets/web-panel/assets/VideoEditing-CSOjdBZg.js +0 -1
- package/src/assets/web-panel/assets/WorkflowEditor-BFZ3RYva.js +0 -1
- package/src/assets/web-panel/assets/chat-BQ-Nk2XY.js +0 -1
- package/src/assets/web-panel/assets/icons-DvZE-RKs.js +0 -57
- package/src/assets/web-panel/assets/index-4cn1LmJ9.js +0 -1
- package/src/assets/web-panel/assets/index-B74gWYqD.js +0 -1
- package/src/assets/web-panel/assets/index-B9b_mz4I.js +0 -55
- package/src/assets/web-panel/assets/index-BAA1SFp1.js +0 -1
- package/src/assets/web-panel/assets/index-BJeE7n_I.js +0 -12
- package/src/assets/web-panel/assets/index-BJoK7MkB.js +0 -13
- package/src/assets/web-panel/assets/index-CA3g3EpL.js +0 -1
- package/src/assets/web-panel/assets/index-CWhXxdyo.js +0 -1
- package/src/assets/web-panel/assets/index-Cd6m6ynF.js +0 -1
- package/src/assets/web-panel/assets/index-Cfy9l115.js +0 -1
- package/src/assets/web-panel/assets/index-CijiVpfO.js +0 -1
- package/src/assets/web-panel/assets/index-CvuBD5TK.js +0 -3
- package/src/assets/web-panel/assets/index-Cw4v7ezB.js +0 -65
- package/src/assets/web-panel/assets/index-DjeNNVwu.js +0 -1
- package/src/assets/web-panel/assets/index-KdFFI-p3.js +0 -1
- package/src/assets/web-panel/assets/index-aw2DwKj-.js +0 -21
- package/src/assets/web-panel/assets/index-nzDx0JAR.js +0 -1
- package/src/assets/web-panel/assets/isSymbol-CsP9Ob42.js +0 -1
- package/src/assets/web-panel/assets/mtc-parser-pGMSt10g.js +0 -1
- package/src/assets/web-panel/assets/useFlexGapSupport-CRN_hzJt.js +0 -1
- package/src/assets/web-panel/assets/useFs-BD-YRwbU.js +0 -1
- package/src/assets/web-panel/assets/ws-D_5-FRIb.js +0 -1
|
@@ -0,0 +1,530 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chat Intent Service — ports the V5 desktop intent understanding and
|
|
3
|
+
* follow-up intent classifier flows to the CLI/WS layer so the web-shell
|
|
4
|
+
* can drive the same UX over WebSocket topics.
|
|
5
|
+
*
|
|
6
|
+
* understandIntent({ userInput, contextMode, llmOptions })
|
|
7
|
+
* → { correctedInput, intent, keyPoints }
|
|
8
|
+
*
|
|
9
|
+
* classifyFollowupIntent({ input, context, llmOptions })
|
|
10
|
+
* → { intent, confidence, reason, extractedInfo, method, latency }
|
|
11
|
+
*
|
|
12
|
+
* Intent categories match V5 (FollowupIntentClassifier):
|
|
13
|
+
* CONTINUE_EXECUTION | MODIFY_REQUIREMENT | CLARIFICATION | CANCEL_TASK
|
|
14
|
+
*
|
|
15
|
+
* The rule-based first-pass mirrors the V5 keyword + regex sets so behaviour
|
|
16
|
+
* is identical when the LLM is offline. LLM fallback uses the same low-temp
|
|
17
|
+
* JSON-output prompt as V5.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { chatWithStreaming, chatStream } from "./chat-core.js";
|
|
21
|
+
|
|
22
|
+
const DEFAULT_INTENT_TIMEOUT_MS = 15000;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Build the V5 understandIntent system + user prompt pair.
|
|
26
|
+
*
|
|
27
|
+
* `history` is optional — when provided, the last few exchanges are
|
|
28
|
+
* embedded into the user prompt so the model can resolve anaphora ("再来
|
|
29
|
+
* 一次"、"和上次一样") instead of treating each input in isolation.
|
|
30
|
+
*/
|
|
31
|
+
function buildUnderstandPrompts(userInput, contextMode, history) {
|
|
32
|
+
const systemPrompt = `你是一个智能的意图理解助手。你的任务是:
|
|
33
|
+
|
|
34
|
+
1. **纠错处理**:识别并纠正用户输入中的打字错误、拼写错误、语法错误等问题
|
|
35
|
+
2. **意图识别**:理解用户的真实意图和需求(如有对话历史,请结合上下文消解指代)
|
|
36
|
+
3. **要点提取**:提取用户需求的关键要点
|
|
37
|
+
|
|
38
|
+
请以JSON格式返回结果,格式如下:
|
|
39
|
+
\`\`\`json
|
|
40
|
+
{
|
|
41
|
+
"correctedInput": "纠错后的输入(如果没有错误,则与原输入相同)",
|
|
42
|
+
"intent": "用户的意图描述(简短的一句话)",
|
|
43
|
+
"keyPoints": ["关键要点1", "关键要点2", "关键要点3"]
|
|
44
|
+
}
|
|
45
|
+
\`\`\`
|
|
46
|
+
|
|
47
|
+
**注意事项:**
|
|
48
|
+
- 如果输入没有错误,correctedInput应该与原输入完全相同
|
|
49
|
+
- intent应该简洁明了,不超过30个字
|
|
50
|
+
- keyPoints应该提取3-5个核心要点
|
|
51
|
+
- 必须返回有效的JSON格式`;
|
|
52
|
+
|
|
53
|
+
const historyBlock =
|
|
54
|
+
Array.isArray(history) && history.length > 0
|
|
55
|
+
? `\n\n对话历史(最近 ${history.length} 条):\n${history
|
|
56
|
+
.map((m) => `- ${m.role}: ${String(m.content || "").slice(0, 200)}`)
|
|
57
|
+
.join("\n")}`
|
|
58
|
+
: "";
|
|
59
|
+
|
|
60
|
+
const userPrompt = `请理解以下用户输入:
|
|
61
|
+
|
|
62
|
+
用户输入:${userInput}
|
|
63
|
+
|
|
64
|
+
上下文模式:${contextMode || "global"}${historyBlock}`;
|
|
65
|
+
|
|
66
|
+
return { systemPrompt, userPrompt };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Extract the JSON body from an LLM response (handles ```json fences and
|
|
71
|
+
* bare JSON braces). Returns null if no JSON found.
|
|
72
|
+
*/
|
|
73
|
+
function extractJson(content) {
|
|
74
|
+
if (!content || typeof content !== "string") return null;
|
|
75
|
+
const fenced = content.match(/```json\s*([\s\S]*?)```/i);
|
|
76
|
+
if (fenced) return fenced[1].trim();
|
|
77
|
+
const generic = content.match(/```\s*([\s\S]*?)```/);
|
|
78
|
+
if (generic) return generic[1].trim();
|
|
79
|
+
const bare = content.match(/\{[\s\S]*\}/);
|
|
80
|
+
return bare ? bare[0] : null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Understand a user input — runs the V5 prompt through the active LLM and
|
|
85
|
+
* parses the response. Returns a normalised understanding object plus a
|
|
86
|
+
* `success` flag; on parse / LLM failure, falls back to passing the input
|
|
87
|
+
* through verbatim with `intent='general'` so the caller never crashes.
|
|
88
|
+
*
|
|
89
|
+
* @param {object} args
|
|
90
|
+
* @param {string} args.userInput
|
|
91
|
+
* @param {string} [args.contextMode='global']
|
|
92
|
+
* @param {object} args.llmOptions - { provider, model, baseUrl, apiKey }
|
|
93
|
+
* @returns {Promise<{success: boolean, correctedInput: string, intent: string,
|
|
94
|
+
* keyPoints: string[], error?: string}>}
|
|
95
|
+
*/
|
|
96
|
+
export async function understandIntent({
|
|
97
|
+
userInput,
|
|
98
|
+
contextMode = "global",
|
|
99
|
+
history,
|
|
100
|
+
llmOptions,
|
|
101
|
+
}) {
|
|
102
|
+
if (!userInput || !userInput.trim()) {
|
|
103
|
+
throw new Error("userInput required");
|
|
104
|
+
}
|
|
105
|
+
if (!llmOptions || !llmOptions.provider) {
|
|
106
|
+
// Caller chose not to provide LLM creds — degrade gracefully so the UI
|
|
107
|
+
// can still show the confirmation card.
|
|
108
|
+
return {
|
|
109
|
+
success: false,
|
|
110
|
+
correctedInput: userInput,
|
|
111
|
+
intent: "general",
|
|
112
|
+
keyPoints: [],
|
|
113
|
+
error: "LLM not configured",
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const { systemPrompt, userPrompt } = buildUnderstandPrompts(
|
|
118
|
+
userInput,
|
|
119
|
+
contextMode,
|
|
120
|
+
history,
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
const fullContent = await chatWithStreaming(
|
|
125
|
+
[
|
|
126
|
+
{ role: "system", content: systemPrompt },
|
|
127
|
+
{ role: "user", content: userPrompt },
|
|
128
|
+
],
|
|
129
|
+
{
|
|
130
|
+
...llmOptions,
|
|
131
|
+
// Lower temperature → more deterministic JSON output.
|
|
132
|
+
temperature: 0.3,
|
|
133
|
+
maxTokens: 500,
|
|
134
|
+
},
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
const jsonText = extractJson(fullContent);
|
|
138
|
+
if (!jsonText) {
|
|
139
|
+
throw new Error("LLM response did not contain JSON");
|
|
140
|
+
}
|
|
141
|
+
const parsed = JSON.parse(jsonText);
|
|
142
|
+
return {
|
|
143
|
+
success: true,
|
|
144
|
+
correctedInput: parsed.correctedInput || userInput,
|
|
145
|
+
intent: parsed.intent || "general",
|
|
146
|
+
keyPoints: Array.isArray(parsed.keyPoints) ? parsed.keyPoints : [],
|
|
147
|
+
};
|
|
148
|
+
} catch (err) {
|
|
149
|
+
return {
|
|
150
|
+
success: false,
|
|
151
|
+
correctedInput: userInput,
|
|
152
|
+
intent: "general",
|
|
153
|
+
keyPoints: [],
|
|
154
|
+
error: err?.message || String(err),
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Streaming variant of {@link understandIntent}.
|
|
161
|
+
*
|
|
162
|
+
* Yields:
|
|
163
|
+
* { type: 'token', token } — every LLM delta token (raw, may be partial JSON)
|
|
164
|
+
* { type: 'final', success, correctedInput, intent, keyPoints, error? }
|
|
165
|
+
*
|
|
166
|
+
* The UI uses tokens as a "thinking…" indicator and only renders the
|
|
167
|
+
* confirmation card on the `final` payload. Yielding raw tokens (rather
|
|
168
|
+
* than incrementally parsed JSON) keeps this layer simple — partial JSON
|
|
169
|
+
* isn't reliably parseable mid-stream and would just produce noise.
|
|
170
|
+
*
|
|
171
|
+
* @param {object} args
|
|
172
|
+
* @param {string} args.userInput
|
|
173
|
+
* @param {string} [args.contextMode='global']
|
|
174
|
+
* @param {Array<{role:string,content:string}>} [args.history]
|
|
175
|
+
* @param {object} [args.llmOptions]
|
|
176
|
+
*/
|
|
177
|
+
export async function* understandIntentStream({
|
|
178
|
+
userInput,
|
|
179
|
+
contextMode = "global",
|
|
180
|
+
history,
|
|
181
|
+
llmOptions,
|
|
182
|
+
}) {
|
|
183
|
+
if (!userInput || !userInput.trim()) {
|
|
184
|
+
throw new Error("userInput required");
|
|
185
|
+
}
|
|
186
|
+
if (!llmOptions || !llmOptions.provider) {
|
|
187
|
+
yield {
|
|
188
|
+
type: "final",
|
|
189
|
+
success: false,
|
|
190
|
+
correctedInput: userInput,
|
|
191
|
+
intent: "general",
|
|
192
|
+
keyPoints: [],
|
|
193
|
+
error: "LLM not configured",
|
|
194
|
+
};
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const { systemPrompt, userPrompt } = buildUnderstandPrompts(
|
|
199
|
+
userInput,
|
|
200
|
+
contextMode,
|
|
201
|
+
history,
|
|
202
|
+
);
|
|
203
|
+
let buffer = "";
|
|
204
|
+
try {
|
|
205
|
+
for await (const event of chatStream(
|
|
206
|
+
[
|
|
207
|
+
{ role: "system", content: systemPrompt },
|
|
208
|
+
{ role: "user", content: userPrompt },
|
|
209
|
+
],
|
|
210
|
+
{ ...llmOptions, temperature: 0.3, maxTokens: 500 },
|
|
211
|
+
)) {
|
|
212
|
+
if (event.type === "response-token") {
|
|
213
|
+
buffer += event.token;
|
|
214
|
+
yield { type: "token", token: event.token };
|
|
215
|
+
} else if (event.type === "response-complete") {
|
|
216
|
+
// chatStream's terminal event carries the full collected text;
|
|
217
|
+
// prefer it over the accumulated buffer in case provider semantics
|
|
218
|
+
// differ.
|
|
219
|
+
buffer = event.content || buffer;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
const jsonText = extractJson(buffer);
|
|
223
|
+
if (!jsonText) {
|
|
224
|
+
yield {
|
|
225
|
+
type: "final",
|
|
226
|
+
success: false,
|
|
227
|
+
correctedInput: userInput,
|
|
228
|
+
intent: "general",
|
|
229
|
+
keyPoints: [],
|
|
230
|
+
error: "LLM response did not contain JSON",
|
|
231
|
+
};
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
const parsed = JSON.parse(jsonText);
|
|
235
|
+
yield {
|
|
236
|
+
type: "final",
|
|
237
|
+
success: true,
|
|
238
|
+
correctedInput: parsed.correctedInput || userInput,
|
|
239
|
+
intent: parsed.intent || "general",
|
|
240
|
+
keyPoints: Array.isArray(parsed.keyPoints) ? parsed.keyPoints : [],
|
|
241
|
+
};
|
|
242
|
+
} catch (err) {
|
|
243
|
+
yield {
|
|
244
|
+
type: "final",
|
|
245
|
+
success: false,
|
|
246
|
+
correctedInput: userInput,
|
|
247
|
+
intent: "general",
|
|
248
|
+
keyPoints: [],
|
|
249
|
+
error: err?.message || String(err),
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// =====================================================================
|
|
255
|
+
// Follow-up intent classifier — rule-first, LLM-fallback
|
|
256
|
+
// =====================================================================
|
|
257
|
+
|
|
258
|
+
const FOLLOWUP_RULES = {
|
|
259
|
+
CONTINUE_EXECUTION: {
|
|
260
|
+
keywords: [
|
|
261
|
+
"继续",
|
|
262
|
+
"开始",
|
|
263
|
+
"好的",
|
|
264
|
+
"好",
|
|
265
|
+
"嗯",
|
|
266
|
+
"行",
|
|
267
|
+
"ok",
|
|
268
|
+
"OK",
|
|
269
|
+
"快点",
|
|
270
|
+
"去吧",
|
|
271
|
+
"执行",
|
|
272
|
+
],
|
|
273
|
+
patterns: [
|
|
274
|
+
/^(继续|好的?|嗯|行|OK|ok)$/i,
|
|
275
|
+
/^快点|赶紧|马上/,
|
|
276
|
+
/^开始(吧|执行)/,
|
|
277
|
+
],
|
|
278
|
+
},
|
|
279
|
+
MODIFY_REQUIREMENT: {
|
|
280
|
+
keywords: [
|
|
281
|
+
"改",
|
|
282
|
+
"修改",
|
|
283
|
+
"换成",
|
|
284
|
+
"换",
|
|
285
|
+
"换个",
|
|
286
|
+
"不要",
|
|
287
|
+
"去掉",
|
|
288
|
+
"删除",
|
|
289
|
+
"加上",
|
|
290
|
+
"增加",
|
|
291
|
+
"还要",
|
|
292
|
+
"另外",
|
|
293
|
+
],
|
|
294
|
+
patterns: [
|
|
295
|
+
/(改|换)(成|个)/,
|
|
296
|
+
/(加|增加|还要|另外).+(功能|页面|按钮|模块)/,
|
|
297
|
+
/不要|去掉|删除/,
|
|
298
|
+
/等等|等一下|先别/,
|
|
299
|
+
/还要.*(修改|改)/,
|
|
300
|
+
],
|
|
301
|
+
},
|
|
302
|
+
CLARIFICATION: {
|
|
303
|
+
keywords: [
|
|
304
|
+
"用",
|
|
305
|
+
"采用",
|
|
306
|
+
"使用",
|
|
307
|
+
"应该是",
|
|
308
|
+
"具体是",
|
|
309
|
+
"颜色",
|
|
310
|
+
"字体",
|
|
311
|
+
"大小",
|
|
312
|
+
"位置",
|
|
313
|
+
"标题",
|
|
314
|
+
],
|
|
315
|
+
patterns: [
|
|
316
|
+
/^(用|采用|使用)/,
|
|
317
|
+
/(颜色|字体|大小|位置|标题).*(是|用|为)/,
|
|
318
|
+
/^.{1,20}(应该|具体)(是|为)/,
|
|
319
|
+
/^数据(来源|是)/,
|
|
320
|
+
/.*(用|采用).*(字体|颜色|大小)/,
|
|
321
|
+
],
|
|
322
|
+
},
|
|
323
|
+
CANCEL_TASK: {
|
|
324
|
+
keywords: ["算了", "不用", "停止", "取消", "暂停", "先不"],
|
|
325
|
+
patterns: [
|
|
326
|
+
/^(算了|不用|停止|取消|暂停)/,
|
|
327
|
+
/(算了|不用了|停止|取消|暂停)/,
|
|
328
|
+
/^先不.*(了|吧)/,
|
|
329
|
+
/不做了|别做了/,
|
|
330
|
+
/先不做/,
|
|
331
|
+
],
|
|
332
|
+
},
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
function ruleBasedClassify(userInput) {
|
|
336
|
+
const input = (userInput || "").trim();
|
|
337
|
+
|
|
338
|
+
if (input.length === 0) {
|
|
339
|
+
return {
|
|
340
|
+
intent: "CONTINUE_EXECUTION",
|
|
341
|
+
confidence: 0.9,
|
|
342
|
+
reason: "Empty input → continue",
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const scores = {
|
|
347
|
+
CONTINUE_EXECUTION: 0,
|
|
348
|
+
MODIFY_REQUIREMENT: 0,
|
|
349
|
+
CLARIFICATION: 0,
|
|
350
|
+
CANCEL_TASK: 0,
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
for (const [intent, config] of Object.entries(FOLLOWUP_RULES)) {
|
|
354
|
+
const weightMultiplier = intent === "CANCEL_TASK" ? 1.5 : 1.0;
|
|
355
|
+
for (const keyword of config.keywords) {
|
|
356
|
+
if (input.includes(keyword)) scores[intent] += 0.3 * weightMultiplier;
|
|
357
|
+
}
|
|
358
|
+
for (const pattern of config.patterns) {
|
|
359
|
+
if (pattern.test(input)) scores[intent] += 0.5 * weightMultiplier;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (/(算了|不用了?|停止|取消|暂停|先不做)/.test(input)) {
|
|
364
|
+
scores.CANCEL_TASK = Math.max(scores.CANCEL_TASK, 1.0);
|
|
365
|
+
}
|
|
366
|
+
if (/^(好的?|嗯|行|OK|ok|继续)$/i.test(input)) {
|
|
367
|
+
scores.CONTINUE_EXECUTION = 1.0;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const maxIntent = Object.keys(scores).reduce((a, b) =>
|
|
371
|
+
scores[a] > scores[b] ? a : b,
|
|
372
|
+
);
|
|
373
|
+
const maxScore = scores[maxIntent];
|
|
374
|
+
|
|
375
|
+
return {
|
|
376
|
+
intent: maxIntent,
|
|
377
|
+
confidence: Math.min(maxScore, 1.0),
|
|
378
|
+
reason: `rule-based scores: ${JSON.stringify(scores)}`,
|
|
379
|
+
scores,
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function buildFollowupPrompts(userInput, context = {}) {
|
|
384
|
+
const { currentTask, conversationHistory, taskPlan } = context;
|
|
385
|
+
|
|
386
|
+
const systemPrompt = `你是一个专业的意图分类器,负责分析用户在任务执行过程中的后续输入意图。
|
|
387
|
+
|
|
388
|
+
# 你的任务
|
|
389
|
+
判断用户输入属于以下哪一种意图类型:
|
|
390
|
+
|
|
391
|
+
1. **CONTINUE_EXECUTION (继续执行)**
|
|
392
|
+
- 用户在催促、确认、同意继续当前任务
|
|
393
|
+
- 示例: "继续"、"好的"、"快点"、"开始吧"、"行"
|
|
394
|
+
|
|
395
|
+
2. **MODIFY_REQUIREMENT (修改需求)**
|
|
396
|
+
- 用户想要修改、追加、删除需求
|
|
397
|
+
- 示例: "改成红色"、"还要加一个登录页"、"去掉导航栏"、"换个字体"
|
|
398
|
+
|
|
399
|
+
3. **CLARIFICATION (补充说明)**
|
|
400
|
+
- 用户提供额外的细节信息或参数
|
|
401
|
+
- 示例: "标题用宋体"、"数据来源是 users.csv"、"颜色用 #FF5733"
|
|
402
|
+
|
|
403
|
+
4. **CANCEL_TASK (取消任务)**
|
|
404
|
+
- 用户想要停止、取消当前任务
|
|
405
|
+
- 示例: "算了"、"不用了"、"停止"、"先不做了"
|
|
406
|
+
|
|
407
|
+
# 输出格式
|
|
408
|
+
严格返回 JSON 格式:
|
|
409
|
+
{
|
|
410
|
+
"intent": "CONTINUE_EXECUTION | MODIFY_REQUIREMENT | CLARIFICATION | CANCEL_TASK",
|
|
411
|
+
"confidence": 0.0-1.0,
|
|
412
|
+
"reason": "判断理由(1-2句话)",
|
|
413
|
+
"extractedInfo": "如果是 MODIFY_REQUIREMENT 或 CLARIFICATION,提取关键信息"
|
|
414
|
+
}`;
|
|
415
|
+
|
|
416
|
+
const userPrompt = `
|
|
417
|
+
# 上下文信息
|
|
418
|
+
${currentTask ? `**当前任务**: ${JSON.stringify(currentTask, null, 2)}` : ""}
|
|
419
|
+
|
|
420
|
+
${taskPlan ? `**任务计划**: ${JSON.stringify(taskPlan, null, 2)}` : ""}
|
|
421
|
+
|
|
422
|
+
${
|
|
423
|
+
Array.isArray(conversationHistory) && conversationHistory.length > 0
|
|
424
|
+
? `**对话历史**:\n${conversationHistory
|
|
425
|
+
.slice(-5)
|
|
426
|
+
.map((m) => `- ${m.role}: ${m.content}`)
|
|
427
|
+
.join("\n")}`
|
|
428
|
+
: ""
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
# 用户输入
|
|
432
|
+
"${userInput}"
|
|
433
|
+
|
|
434
|
+
# 请分析并返回 JSON 结果`;
|
|
435
|
+
|
|
436
|
+
return { systemPrompt, userPrompt };
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
async function llmBasedClassify(userInput, context, llmOptions) {
|
|
440
|
+
const { systemPrompt, userPrompt } = buildFollowupPrompts(userInput, context);
|
|
441
|
+
const fullContent = await chatWithStreaming(
|
|
442
|
+
[
|
|
443
|
+
{ role: "system", content: systemPrompt },
|
|
444
|
+
{ role: "user", content: userPrompt },
|
|
445
|
+
],
|
|
446
|
+
{
|
|
447
|
+
...llmOptions,
|
|
448
|
+
temperature: 0.1,
|
|
449
|
+
maxTokens: 300,
|
|
450
|
+
},
|
|
451
|
+
);
|
|
452
|
+
const jsonText = extractJson(fullContent);
|
|
453
|
+
if (!jsonText) throw new Error("LLM response missing JSON");
|
|
454
|
+
const parsed = JSON.parse(jsonText);
|
|
455
|
+
return {
|
|
456
|
+
intent: parsed.intent || "CLARIFICATION",
|
|
457
|
+
confidence: typeof parsed.confidence === "number" ? parsed.confidence : 0.5,
|
|
458
|
+
reason: parsed.reason || "",
|
|
459
|
+
extractedInfo: parsed.extractedInfo || null,
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Classify a follow-up user input — rule-first (covers ~80% of cases),
|
|
465
|
+
* falls back to LLM for ambiguous inputs. On LLM error, returns the rule
|
|
466
|
+
* result if it had any signal, otherwise CLARIFICATION default.
|
|
467
|
+
*
|
|
468
|
+
* @param {object} args
|
|
469
|
+
* @param {string} args.input
|
|
470
|
+
* @param {object} [args.context] - { currentTask, conversationHistory, taskPlan }
|
|
471
|
+
* @param {object} [args.llmOptions] - LLM credentials; if omitted, rule-only
|
|
472
|
+
* @returns {Promise<{intent: string, confidence: number, reason: string,
|
|
473
|
+
* extractedInfo?: any, method: string, latency: number}>}
|
|
474
|
+
*/
|
|
475
|
+
export async function classifyFollowupIntent({
|
|
476
|
+
input,
|
|
477
|
+
context = {},
|
|
478
|
+
llmOptions,
|
|
479
|
+
}) {
|
|
480
|
+
const startTime = Date.now();
|
|
481
|
+
const ruleResult = ruleBasedClassify(input);
|
|
482
|
+
|
|
483
|
+
if (ruleResult.confidence > 0.8) {
|
|
484
|
+
return {
|
|
485
|
+
...ruleResult,
|
|
486
|
+
method: "rule",
|
|
487
|
+
latency: Date.now() - startTime,
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
if (!llmOptions || !llmOptions.provider) {
|
|
492
|
+
return {
|
|
493
|
+
...ruleResult,
|
|
494
|
+
method: "rule_no_llm",
|
|
495
|
+
latency: Date.now() - startTime,
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
try {
|
|
500
|
+
const llmResult = await llmBasedClassify(input, context, llmOptions);
|
|
501
|
+
return {
|
|
502
|
+
...llmResult,
|
|
503
|
+
method: "llm",
|
|
504
|
+
latency: Date.now() - startTime,
|
|
505
|
+
};
|
|
506
|
+
} catch (_err) {
|
|
507
|
+
return ruleResult.confidence > 0
|
|
508
|
+
? {
|
|
509
|
+
...ruleResult,
|
|
510
|
+
method: "rule_fallback",
|
|
511
|
+
latency: Date.now() - startTime,
|
|
512
|
+
}
|
|
513
|
+
: {
|
|
514
|
+
intent: "CLARIFICATION",
|
|
515
|
+
confidence: 0.5,
|
|
516
|
+
reason: "Unable to classify, defaulting to CLARIFICATION",
|
|
517
|
+
method: "default",
|
|
518
|
+
latency: Date.now() - startTime,
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// Exposed for test suites — internal helpers.
|
|
524
|
+
export const _internal = {
|
|
525
|
+
buildUnderstandPrompts,
|
|
526
|
+
buildFollowupPrompts,
|
|
527
|
+
extractJson,
|
|
528
|
+
ruleBasedClassify,
|
|
529
|
+
DEFAULT_INTENT_TIMEOUT_MS,
|
|
530
|
+
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync, existsSync } from "node:fs";
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
|
|
2
|
+
import { dirname } from "node:path";
|
|
2
3
|
import { getConfigPath } from "./paths.js";
|
|
3
4
|
import { DEFAULT_CONFIG } from "../constants.js";
|
|
4
5
|
|
|
@@ -18,6 +19,7 @@ export function loadConfig() {
|
|
|
18
19
|
|
|
19
20
|
export function saveConfig(config) {
|
|
20
21
|
const configPath = getConfigPath();
|
|
22
|
+
mkdirSync(dirname(configPath), { recursive: true });
|
|
21
23
|
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
22
24
|
}
|
|
23
25
|
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* The shapes vary only in names and defaults, so these helpers let new
|
|
14
14
|
* (and migrated) modules replace ~50 lines of boilerplate with ~10.
|
|
15
15
|
*
|
|
16
|
-
* Migration guide: see GOV_V2_MIGRATION.md
|
|
16
|
+
* Migration guide: see docs/migrations/GOV_V2_MIGRATION.md.
|
|
17
17
|
* `packages/cli` is an ESM package, so this helper exports ESM bindings.
|
|
18
18
|
*/
|
|
19
19
|
|