@p-dsh-market/conversation-knowledge-map 0.1.0 → 0.1.2
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/README.md +2 -0
- package/cordis.patch.yml +1 -1
- package/lib/client.js +38 -4
- package/lib/generation-orchestrator.js +157 -39
- package/lib/index.js +64 -5
- package/package.json +1 -1
- package/skills/conversation-knowledge-map/SKILL.md +1 -0
package/README.md
CHANGED
|
@@ -9,6 +9,8 @@ DSH 的“知识视图”插件,把同一工作路径下用户明确选择的
|
|
|
9
9
|
|
|
10
10
|
生成流程必须经过菜单栏配置和应用内确认。Host 会再次校验锚点 Session、`cwd`、选中 Session、revision 和固定保存目录;模型输出先通过图数据 Schema 校验,失败或取消不会替换旧结果。
|
|
11
11
|
|
|
12
|
+
配置面板会列出运行时可用的 Provider / Model,并默认带入 DSH 默认模型;本次选择会绑定到确认令牌、Agent 调用和 `manifest.json`,不会因为默认模型变化而被静默替换。提交任务后配置对话框立即关闭,失败原因会显示在知识视图页的任务条中。
|
|
13
|
+
|
|
12
14
|
节点“继续对话”只形成一个可编辑的后续问题。确认导航后,插件使用公开的 Session 导航入口;如果当前 Runtime 没有向该槽位暴露草稿镜像,则提供“打开并复制问题”的安全降级,不自动发送消息。
|
|
13
15
|
|
|
14
16
|
## 本地验证
|
package/cordis.patch.yml
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
- insert:
|
|
2
2
|
- id: p-dsh-market-conversation-knowledge-map
|
|
3
3
|
name: '@p-dsh-market/conversation-knowledge-map'
|
|
4
|
-
inject: [agentDefaultModel, agents, sessionQuery, sessions, skills, webServer]
|
|
4
|
+
inject: [agentDefaultModel, agents, llm, sessionQuery, sessions, skills, webServer]
|
package/lib/client.js
CHANGED
|
@@ -92,6 +92,7 @@ window.__ModuleLoader__.load({
|
|
|
92
92
|
context: null,
|
|
93
93
|
state: null,
|
|
94
94
|
sessions: [],
|
|
95
|
+
modelCatalog: null,
|
|
95
96
|
generation: null,
|
|
96
97
|
loading: false,
|
|
97
98
|
loaded: false,
|
|
@@ -450,6 +451,15 @@ window.__ModuleLoader__.load({
|
|
|
450
451
|
var includePair = React.useState(false)
|
|
451
452
|
var includeSubagents = includePair[0]
|
|
452
453
|
var setIncludeSubagents = includePair[1]
|
|
454
|
+
var modelCatalogPair = React.useState(null)
|
|
455
|
+
var modelCatalog = modelCatalogPair[0]
|
|
456
|
+
var setModelCatalog = modelCatalogPair[1]
|
|
457
|
+
var providerPair = React.useState('')
|
|
458
|
+
var modelProvider = providerPair[0]
|
|
459
|
+
var setModelProvider = providerPair[1]
|
|
460
|
+
var modelIdPair = React.useState('')
|
|
461
|
+
var modelId = modelIdPair[0]
|
|
462
|
+
var setModelId = modelIdPair[1]
|
|
453
463
|
var confirmationPair = React.useState(null)
|
|
454
464
|
var confirmation = confirmationPair[0]
|
|
455
465
|
var setConfirmation = confirmationPair[1]
|
|
@@ -466,17 +476,30 @@ window.__ModuleLoader__.load({
|
|
|
466
476
|
setSelected(function (old) { return old.length ? old.filter(function (id) { return (body.sessions || []).some(function (item) { return item.id === id }) }) : [store.sessionId] })
|
|
467
477
|
}).catch(function (error) { setMessage(error.message || String(error)) })
|
|
468
478
|
}, [store.context && store.context.cwd, includeSubagents])
|
|
479
|
+
React.useEffect(function () {
|
|
480
|
+
if (!store.context || !store.context.ready) return
|
|
481
|
+
request('/models').then(function (body) {
|
|
482
|
+
var catalog = body.catalog || { default: null, groups: [] }
|
|
483
|
+
setModelCatalog(catalog)
|
|
484
|
+
var defaultModel = catalog.default || {}
|
|
485
|
+
var firstGroup = catalog.groups && catalog.groups[0] ? catalog.groups[0] : {}
|
|
486
|
+
var firstModel = firstGroup.models && firstGroup.models[0] ? firstGroup.models[0].id : ''
|
|
487
|
+
setModelProvider(function (old) { return old || defaultModel.provider || firstGroup.id || '' })
|
|
488
|
+
setModelId(function (old) { return old || defaultModel.model || firstModel || '' })
|
|
489
|
+
}).catch(function (error) { setMessage(error.message || String(error)) })
|
|
490
|
+
}, [store.context && store.context.cwd])
|
|
469
491
|
function toggle(id) { setSelected(function (old) { return old.indexOf(id) >= 0 ? old.filter(function (item) { return item !== id }) : old.concat([id]) }) }
|
|
470
492
|
function confirm() {
|
|
471
493
|
if (!selected.length) { setMessage('至少选择一个对话。'); return }
|
|
472
494
|
setBusy(true)
|
|
473
|
-
request('/confirm', { method: 'POST', body: { anchorSessionId: store.sessionId, selectedSessionIds: selected, outputMode: mode, prompt: prompt, strict: strict, includeSubagents: includeSubagents, expectedRevision: store.state ? store.state.revision : 0 } }).then(function (body) { setConfirmation(body.confirmation); setMessage(''); setBusy(false) }).catch(function (error) { setMessage(error.message || String(error)); setBusy(false) })
|
|
495
|
+
request('/confirm', { method: 'POST', body: { anchorSessionId: store.sessionId, selectedSessionIds: selected, outputMode: mode, prompt: prompt, strict: strict, includeSubagents: includeSubagents, model: { provider: modelProvider, model: modelId }, expectedRevision: store.state ? store.state.revision : 0 } }).then(function (body) { setConfirmation(body.confirmation); setMessage(''); setBusy(false) }).catch(function (error) { setMessage(error.message || String(error)); setBusy(false) })
|
|
474
496
|
}
|
|
475
497
|
function start() {
|
|
476
498
|
setBusy(true)
|
|
477
|
-
request('/generations', { method: 'POST', body: { token: confirmation.token, anchorSessionId: store.sessionId, selectedSessionIds: selected, outputMode: mode, prompt: prompt, strict: strict, includeSubagents: includeSubagents, expectedRevision: confirmation.revision } }).then(function (body) {
|
|
499
|
+
request('/generations', { method: 'POST', body: { token: confirmation.token, anchorSessionId: store.sessionId, selectedSessionIds: selected, outputMode: mode, prompt: prompt, strict: strict, includeSubagents: includeSubagents, model: confirmation.model, expectedRevision: confirmation.revision } }).then(function (body) {
|
|
478
500
|
setConfirmation(null)
|
|
479
501
|
setBusy(false)
|
|
502
|
+
setOverlayOpen(false)
|
|
480
503
|
applyGeneration(store, body.generation)
|
|
481
504
|
openGenerationStream(store, body.generation.id)
|
|
482
505
|
}).catch(function (error) { setMessage(error.message || String(error)); setBusy(false) })
|
|
@@ -484,7 +507,7 @@ window.__ModuleLoader__.load({
|
|
|
484
507
|
if (!store.context || !store.context.ready) return React.createElement('div', { className: 'ckm-modal-backdrop' }, React.createElement('div', { className: 'ckm-modal' }, [React.createElement('h3', { key: 'title' }, '知识视图'), React.createElement('p', { key: 'copy' }, '请先打开一个有明确工作路径的已有对话。'), Button({ key: 'close', className: 'ckm-primary', onClick: function () { setOverlayOpen(false) } }, '关闭')]))
|
|
485
508
|
return React.createElement('div', { className: 'ckm-modal-backdrop' }, React.createElement('div', { className: 'ckm-modal ckm-config-modal', role: 'dialog', 'aria-modal': 'true' }, confirmation ? [
|
|
486
509
|
React.createElement('h3', { key: 'title' }, '确认生成知识视图?'),
|
|
487
|
-
React.createElement('dl', { key: 'summary', className: 'ckm-confirm-summary' }, [React.createElement('dt', { key: 'cwd-label' }, '工作路径'), React.createElement('dd', { key: 'cwd' }, store.context.cwd), React.createElement('dt', { key: 'source-label' }, '来源'), React.createElement('dd', { key: 'sources' }, confirmation.selectedSessions.length + ' 个已选择对话'), React.createElement('dt', { key: 'output-label' }, '生成'), React.createElement('dd', { key: 'output' }, mode === 'both' ? '思维导图 + 知识图谱' : mode), React.createElement('dt', { key: 'strict-label' }, '约束'), React.createElement('dd', { key: 'strict' }, strict ? '严格约束已开启' : '普通约束'), React.createElement('dt', { key: 'save-label' }, '保存'), React.createElement('dd', { key: 'save' }, '.g-dsh-market-knowledge' + (confirmation.overwrite ? '(将替换已有结果)' : ''))]),
|
|
510
|
+
React.createElement('dl', { key: 'summary', className: 'ckm-confirm-summary' }, [React.createElement('dt', { key: 'cwd-label' }, '工作路径'), React.createElement('dd', { key: 'cwd' }, store.context.cwd), React.createElement('dt', { key: 'source-label' }, '来源'), React.createElement('dd', { key: 'sources' }, confirmation.selectedSessions.length + ' 个已选择对话'), React.createElement('dt', { key: 'output-label' }, '生成'), React.createElement('dd', { key: 'output' }, mode === 'both' ? '思维导图 + 知识图谱' : mode), React.createElement('dt', { key: 'model-label' }, '模型'), React.createElement('dd', { key: 'model' }, confirmation.model.provider + ' / ' + confirmation.model.model), React.createElement('dt', { key: 'strict-label' }, '约束'), React.createElement('dd', { key: 'strict' }, strict ? '严格约束已开启' : '普通约束'), React.createElement('dt', { key: 'save-label' }, '保存'), React.createElement('dd', { key: 'save' }, '.g-dsh-market-knowledge' + (confirmation.overwrite ? '(将替换已有结果)' : ''))]),
|
|
488
511
|
React.createElement('p', { key: 'note', className: 'ckm-warning' }, '确认后才会读取所选对话正文、调用模型并写入工作区;不会自动发送消息。'),
|
|
489
512
|
React.createElement('div', { key: 'actions', className: 'ckm-modal-actions' }, [Button({ key: 'back', className: 'ckm-secondary', onClick: function () { setConfirmation(null) }, disabled: busy }, '返回修改'), Button({ key: 'ok', className: 'ckm-primary', onClick: start, disabled: busy }, busy ? '生成中…' : '确认并生成')])
|
|
490
513
|
] : [
|
|
@@ -507,6 +530,17 @@ window.__ModuleLoader__.load({
|
|
|
507
530
|
])
|
|
508
531
|
]),
|
|
509
532
|
React.createElement('label', { key: 'mode', className: 'ckm-field' }, ['生成内容', React.createElement('select', { key: 'select', value: mode, onChange: function (event) { setMode(event.target.value) } }, [React.createElement('option', { key: 'both', value: 'both' }, '思维导图 + 知识图谱'), React.createElement('option', { key: 'mind', value: 'mind-map' }, '仅思维导图'), React.createElement('option', { key: 'graph', value: 'knowledge-graph' }, '仅知识图谱')])]),
|
|
533
|
+
React.createElement('label', { key: 'model', className: 'ckm-field' }, [
|
|
534
|
+
'生成模型',
|
|
535
|
+
React.createElement('span', { key: 'hint', className: 'ckm-panel-hint' }, '默认带入 DSH 默认模型;可以为本次知识视图单独选择 Provider / Model。'),
|
|
536
|
+
modelCatalog && modelCatalog.groups && modelCatalog.groups.length ? React.createElement('div', { key: 'selectors', className: 'ckm-model-selectors' }, [
|
|
537
|
+
React.createElement('select', { key: 'provider', value: modelProvider, onChange: function (event) { var next = event.target.value; var group = modelCatalog.groups.filter(function (item) { return item.id === next })[0]; setModelProvider(next); setModelId(group && group.models && group.models[0] ? group.models[0].id : '') } }, modelCatalog.groups.map(function (group) { return React.createElement('option', { key: group.id, value: group.id }, group.name + '(' + group.id + ')') })),
|
|
538
|
+
React.createElement('select', { key: 'model', value: modelId, onChange: function (event) { setModelId(event.target.value) } }, ((modelCatalog.groups.filter(function (item) { return item.id === modelProvider })[0] || {}).models || []).map(function (item) { return React.createElement('option', { key: item.id, value: item.id }, item.name + '(' + item.id + ')') }))
|
|
539
|
+
]) : React.createElement('div', { key: 'inputs', className: 'ckm-model-selectors' }, [
|
|
540
|
+
React.createElement('input', { key: 'provider', value: modelProvider, placeholder: 'Provider', onChange: function (event) { setModelProvider(event.target.value) } }),
|
|
541
|
+
React.createElement('input', { key: 'model', value: modelId, placeholder: 'Model', onChange: function (event) { setModelId(event.target.value) } })
|
|
542
|
+
])
|
|
543
|
+
]),
|
|
510
544
|
React.createElement('label', { key: 'prompt', className: 'ckm-field' }, ['额外要求', React.createElement('textarea', { key: 'textarea', rows: 4, maxLength: 4000, value: prompt, placeholder: '请输入形成思维导图或知识图谱时需要遵守的 Prompt…', onChange: function (event) { setPrompt(event.target.value) } })]),
|
|
511
545
|
React.createElement('label', { key: 'strict', className: 'ckm-inline-field' }, [React.createElement('input', { key: 'check', type: 'checkbox', checked: strict, onChange: function (event) { setStrict(event.target.checked) } }), '严格约束模式(来源、路径、工具和写入均由 Host 校验)']),
|
|
512
546
|
message ? React.createElement('p', { key: 'message', className: 'ckm-error' }, message) : null,
|
|
@@ -531,7 +565,7 @@ window.__ModuleLoader__.load({
|
|
|
531
565
|
'.ckm-empty{display:flex;flex:1;min-height:260px;flex-direction:column;align-items:center;justify-content:center;padding:28px;text-align:center}.ckm-empty-icon{display:grid;place-items:center;width:44px;height:44px;margin-bottom:12px;border:1px solid var(--dsw-alias-border-l2);border-radius:14px;color:var(--dsw-alias-state-business-primary);font-size:24px}.ckm-empty h2{margin:0 0 8px;font-size:17px}.ckm-empty p{max-width:520px;margin:0;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.7}' +
|
|
532
566
|
'.ckm-generation-strip{display:flex;align-items:center;gap:12px;padding:9px 20px;border-bottom:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-state-business-tertiary);font-size:12px}.ckm-generation-strip span{color:var(--dsw-alias-label-secondary)}.ckm-generation-strip .ckm-danger{margin-left:auto}' +
|
|
533
567
|
'.ckm-workspace{display:grid;grid-template-columns:minmax(0,1fr) 330px;min-height:0;flex:1}.ckm-mind-canvas,.ckm-graph-canvas{min-width:0;min-height:0;overflow:auto;padding:20px;background:var(--dsw-alias-bg-base)}.ckm-detail{min-width:0;min-height:0;overflow:auto;padding:20px;border-left:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1)}.ckm-panel-hint{margin:0 0 14px;color:var(--dsw-alias-label-secondary);font-size:11px;line-height:1.6}.ckm-tree{display:flex;flex-direction:column;gap:8px;max-width:900px;margin:0 auto}.ckm-tree-node{display:flex;flex-direction:column;align-items:flex-start;gap:5px;width:calc(100% - 0px);padding:12px;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);text-align:left;cursor:pointer}.ckm-tree-node[data-active=true]{border-color:var(--dsw-alias-state-business-primary);background:var(--dsw-alias-state-business-tertiary)}.ckm-tree-node:hover{background:var(--dsw-alias-interactive-bg-hover)}.ckm-tree-node strong{font-size:13px}.ckm-tree-node span:last-child{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.65}.ckm-node-type{display:inline-block;color:var(--dsw-alias-state-business-primary);font-size:10px;letter-spacing:.04em;text-transform:uppercase}.ckm-detail-head{display:flex;flex-direction:column;gap:5px}.ckm-detail h3{margin:0;font-size:16px;line-height:1.45}.ckm-narrative{font-size:13px;line-height:1.8}.ckm-source-box{margin:16px 0;padding:12px;border:1px solid var(--dsw-alias-border-l2);border-radius:9px;background:var(--dsw-alias-bg-base);font-size:11px;line-height:1.6}.ckm-source-box strong{display:block;margin-bottom:5px}.ckm-source-box ul{margin:0;padding-left:18px;color:var(--dsw-alias-label-secondary)}.ckm-detail-empty{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.7}' +
|
|
534
|
-
'.ckm-graph-layout{grid-template-columns:minmax(0,1fr) 330px}.ckm-graph-toolbar{display:flex;gap:8px}.ckm-graph-toolbar input,.ckm-graph-toolbar select,.ckm-field textarea,.ckm-field select,.ckm-field input,.ckm-modal textarea,.ckm-modal select{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:8px 9px;background:var(--dsw-specific-input-major);color:var(--dsw-alias-label-primary);font:inherit;font-size:12px}.ckm-graph-toolbar input{flex:1}.ckm-graph-svg{display:block;width:100%;min-height:420px;margin-top:8px;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-layer-1)}.ckm-edge{stroke:var(--dsw-alias-border-l2);stroke-width:1.5}.ckm-graph-node{cursor:pointer}.ckm-graph-node circle{fill:var(--dsw-alias-state-business-tertiary);stroke:var(--dsw-alias-state-business-primary);stroke-width:1.5}.ckm-graph-node[data-active=true] circle{fill:var(--dsw-alias-button-info-fill);stroke:var(--dsw-alias-label-primary)}.ckm-graph-node text{fill:var(--dsw-alias-label-primary);font-size:11px}' +
|
|
568
|
+
'.ckm-graph-layout{grid-template-columns:minmax(0,1fr) 330px}.ckm-graph-toolbar{display:flex;gap:8px}.ckm-model-selectors{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.4fr);gap:8px}.ckm-graph-toolbar input,.ckm-graph-toolbar select,.ckm-field textarea,.ckm-field select,.ckm-field input,.ckm-modal textarea,.ckm-modal select{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:8px 9px;background:var(--dsw-specific-input-major);color:var(--dsw-alias-label-primary);font:inherit;font-size:12px}.ckm-graph-toolbar input{flex:1}.ckm-graph-svg{display:block;width:100%;min-height:420px;margin-top:8px;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-layer-1)}.ckm-edge{stroke:var(--dsw-alias-border-l2);stroke-width:1.5}.ckm-graph-node{cursor:pointer}.ckm-graph-node circle{fill:var(--dsw-alias-state-business-tertiary);stroke:var(--dsw-alias-state-business-primary);stroke-width:1.5}.ckm-graph-node[data-active=true] circle{fill:var(--dsw-alias-button-info-fill);stroke:var(--dsw-alias-label-primary)}.ckm-graph-node text{fill:var(--dsw-alias-label-primary);font-size:11px}' +
|
|
535
569
|
'.ckm-modal-backdrop{position:fixed;inset:0;z-index:120;display:flex;align-items:center;justify-content:center;padding:20px;background:rgba(0,0,0,.46)}.ckm-modal{width:min(680px,calc(100vw - 40px));max-height:min(760px,calc(100vh - 40px));overflow:auto;padding:22px;border:1px solid var(--dsw-alias-border-l2);border-radius:14px;box-shadow:0 18px 56px #0008}.ckm-modal h3{margin:0;font-size:17px}.ckm-modal p{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.65}.ckm-modal-head{display:flex;align-items:center;justify-content:space-between}.ckm-icon-close{border:0;background:transparent;color:var(--dsw-alias-label-secondary);font-size:20px;cursor:pointer}.ckm-workspace-label{padding:9px;border-radius:8px;background:var(--dsw-alias-bg-layer-1);word-break:break-all}.ckm-field{display:flex;flex-direction:column;gap:7px;margin:14px 0;color:var(--dsw-alias-label-secondary);font-size:12px}.ckm-field textarea{resize:vertical}.ckm-session-list{display:flex;max-height:220px;flex-direction:column;gap:5px;overflow:auto}.ckm-session-option{display:flex;align-items:flex-start;gap:8px;padding:8px;border:1px solid transparent;border-radius:8px;background:var(--dsw-alias-bg-layer-1);cursor:pointer}.ckm-session-option:hover{border-color:var(--dsw-alias-border-l2)}.ckm-session-option input,.ckm-inline-field input{margin-top:3px}.ckm-session-option span{display:flex;flex-direction:column;gap:3px}.ckm-session-option small{color:var(--dsw-alias-label-secondary);font-size:10px}.ckm-inline-field{display:flex;align-items:flex-start;gap:7px;margin:10px 0;color:var(--dsw-alias-label-secondary);font-size:11px;line-height:1.5}.ckm-confirm-summary{display:grid;grid-template-columns:100px 1fr;gap:7px 12px;margin:18px 0;font-size:12px}.ckm-confirm-summary dt{color:var(--dsw-alias-label-secondary)}.ckm-confirm-summary dd{margin:0;word-break:break-all}.ckm-warning{padding:10px;border-radius:8px;background:#d29c2518;color:var(--dsw-alias-label-secondary)}.ckm-error{color:#ff9898!important}.ckm-modal-actions{justify-content:flex-end;margin-top:18px}.ckm-header-pending{border-color:var(--dsw-alias-state-business-primary);background:var(--dsw-alias-state-business-tertiary)}' +
|
|
536
570
|
'@media(max-width:800px){.ckm-workspace,.ckm-graph-layout{display:flex;flex-direction:column}.ckm-detail{border-top:1px solid var(--dsw-alias-border-l2);border-left:0}.ckm-page-header{align-items:flex-start;flex-direction:column}.ckm-page-actions{width:100%;flex-wrap:wrap}}'
|
|
537
571
|
|
|
@@ -23,26 +23,89 @@ function phaseMessage(status) {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
function asObject(value) {
|
|
26
|
-
if (value && typeof value === 'object') {
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
27
|
+
const wrapperKeys = ['result', 'value', 'output', 'data', 'message', 'content', 'text']
|
|
28
|
+
for (const key of wrapperKeys) {
|
|
29
|
+
if (value[key] && typeof value[key] === 'object' && !Array.isArray(value[key])) return asObject(value[key])
|
|
30
|
+
}
|
|
31
|
+
if (wrapperKeys.some((key) => typeof value[key] === 'string')) return null
|
|
29
32
|
return value
|
|
30
33
|
}
|
|
31
34
|
return null
|
|
32
35
|
}
|
|
33
36
|
|
|
37
|
+
function textFromContent(value) {
|
|
38
|
+
if (typeof value === 'string') return value
|
|
39
|
+
if (Array.isArray(value)) {
|
|
40
|
+
return value.map((block) => {
|
|
41
|
+
if (!block || typeof block !== 'object' || block.type === 'reasoning') return ''
|
|
42
|
+
if (typeof block.text === 'string') return block.text
|
|
43
|
+
if (typeof block.value === 'string') return block.value
|
|
44
|
+
if (block.type === 'tool-result') return textFromContent(block.content)
|
|
45
|
+
return ''
|
|
46
|
+
}).filter(Boolean).join('\n\n')
|
|
47
|
+
}
|
|
48
|
+
if (value && typeof value === 'object') {
|
|
49
|
+
if (typeof value.text === 'string') return value.text
|
|
50
|
+
if (typeof value.value === 'string') return value.value
|
|
51
|
+
for (const key of ['content', 'result', 'output', 'data', 'message']) {
|
|
52
|
+
if (value[key] !== undefined) return textFromContent(value[key])
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return ''
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function findJsonObject(text) {
|
|
59
|
+
const candidates = []
|
|
60
|
+
const fenced = String(text || '').match(/```(?:json)?\s*([\s\S]*?)```/i)
|
|
61
|
+
if (fenced) candidates.push(fenced[1])
|
|
62
|
+
candidates.push(String(text || ''))
|
|
63
|
+
for (const candidate of candidates) {
|
|
64
|
+
for (let start = 0; start < candidate.length; start += 1) {
|
|
65
|
+
if (candidate[start] !== '{') continue
|
|
66
|
+
let depth = 0
|
|
67
|
+
let inString = false
|
|
68
|
+
let escaped = false
|
|
69
|
+
for (let index = start; index < candidate.length; index += 1) {
|
|
70
|
+
const char = candidate[index]
|
|
71
|
+
if (inString) {
|
|
72
|
+
if (escaped) escaped = false
|
|
73
|
+
else if (char === '\\') escaped = true
|
|
74
|
+
else if (char === '"') inString = false
|
|
75
|
+
continue
|
|
76
|
+
}
|
|
77
|
+
if (char === '"') {
|
|
78
|
+
inString = true
|
|
79
|
+
continue
|
|
80
|
+
}
|
|
81
|
+
if (char === '{') depth += 1
|
|
82
|
+
else if (char === '}') {
|
|
83
|
+
depth -= 1
|
|
84
|
+
if (depth === 0) {
|
|
85
|
+
const fragment = candidate.slice(start, index + 1)
|
|
86
|
+
try {
|
|
87
|
+
const parsed = JSON.parse(fragment)
|
|
88
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) return parsed
|
|
89
|
+
} catch {
|
|
90
|
+
// Try the next opening brace in case prose contained an example.
|
|
91
|
+
}
|
|
92
|
+
break
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return null
|
|
99
|
+
}
|
|
100
|
+
|
|
34
101
|
export function parseStructuredOutput(value) {
|
|
35
102
|
const object = asObject(value)
|
|
36
103
|
if (object) return object
|
|
37
|
-
const text =
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
if (
|
|
41
|
-
|
|
42
|
-
return JSON.parse(text.slice(start, end + 1))
|
|
43
|
-
} catch (error) {
|
|
44
|
-
throw new Error(`模型 JSON 无法解析:${error.message}`)
|
|
45
|
-
}
|
|
104
|
+
const text = textFromContent(value).replace(/^\uFEFF/, '').trim()
|
|
105
|
+
const parsed = findJsonObject(text)
|
|
106
|
+
if (parsed) return parsed
|
|
107
|
+
if (!text) throw new Error('模型没有返回 JSON 对象。')
|
|
108
|
+
throw new Error('模型返回了文本,但其中没有可解析的 JSON 对象。')
|
|
46
109
|
}
|
|
47
110
|
|
|
48
111
|
function sourceRefsFromChunk(source, chunk) {
|
|
@@ -139,20 +202,54 @@ function followUpPrompt(node, source, targetSessionId, strict) {
|
|
|
139
202
|
].join('\n\n')
|
|
140
203
|
}
|
|
141
204
|
|
|
205
|
+
function messageText(message) {
|
|
206
|
+
if (!message || typeof message !== 'object') return ''
|
|
207
|
+
return textFromContent(message.content) || textFromContent(message.text) || textFromContent(message.value) || textFromContent(message.message)
|
|
208
|
+
}
|
|
209
|
+
|
|
142
210
|
function extractAgentText(surface) {
|
|
143
211
|
const events = Array.isArray(surface?.events) ? surface.events : []
|
|
144
212
|
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
145
213
|
const event = events[index]
|
|
146
214
|
if (event?.type === 'assistant/message') {
|
|
147
|
-
const
|
|
148
|
-
const
|
|
149
|
-
if (
|
|
150
|
-
if (typeof content === 'string') return content
|
|
215
|
+
const data = event.data && typeof event.data === 'object' ? event.data : {}
|
|
216
|
+
const text = messageText(data.message || data) || messageText(data)
|
|
217
|
+
if (text) return text
|
|
151
218
|
}
|
|
152
219
|
}
|
|
220
|
+
const chunks = events.filter((event) => event?.type === 'assistant/chunk').map((event) => {
|
|
221
|
+
const data = event.data && typeof event.data === 'object' ? event.data : {}
|
|
222
|
+
return textFromContent(data.chunk || data.text || data.content)
|
|
223
|
+
}).filter(Boolean)
|
|
224
|
+
return chunks.join('')
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
async function readAgentText(sessionQuery, sessionId, signal) {
|
|
228
|
+
for (let attempt = 0; attempt < 6; attempt += 1) {
|
|
229
|
+
if (signal?.aborted) throw signal.reason || new Error('生成已取消。')
|
|
230
|
+
const surface = await sessionQuery?.readSurface?.(sessionId)
|
|
231
|
+
const text = extractAgentText(surface)
|
|
232
|
+
if (text.trim()) return text
|
|
233
|
+
if (attempt < 5) await new Promise((resolve) => setTimeout(resolve, 40 * (attempt + 1)))
|
|
234
|
+
}
|
|
235
|
+
if (typeof sessionQuery?.readSession === 'function') {
|
|
236
|
+
const log = await sessionQuery.readSession(sessionId)
|
|
237
|
+
const text = extractAgentText(log)
|
|
238
|
+
if (text.trim()) return text
|
|
239
|
+
}
|
|
153
240
|
return ''
|
|
154
241
|
}
|
|
155
242
|
|
|
243
|
+
function normalizeModelSelection(value) {
|
|
244
|
+
if (value === undefined || value === null || value === '') return null
|
|
245
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error('模型选择必须是包含 Provider 和 Model 的对象。')
|
|
246
|
+
const provider = String(value.provider || '').trim()
|
|
247
|
+
const model = String(value.model || '').trim()
|
|
248
|
+
if (!provider && !model) return null
|
|
249
|
+
if (!provider || !model) throw new Error('Provider 和 Model 必须同时填写。')
|
|
250
|
+
return { provider, model }
|
|
251
|
+
}
|
|
252
|
+
|
|
156
253
|
export class KnowledgeGenerationOrchestrator {
|
|
157
254
|
constructor({
|
|
158
255
|
sessionQuery,
|
|
@@ -161,6 +258,7 @@ export class KnowledgeGenerationOrchestrator {
|
|
|
161
258
|
agentDefaultModel,
|
|
162
259
|
storage,
|
|
163
260
|
modelRunner,
|
|
261
|
+
sessionEventSource,
|
|
164
262
|
sourceReader = readSelectedSurfaces,
|
|
165
263
|
now = () => Date.now(),
|
|
166
264
|
idFactory = () => randomUUID()
|
|
@@ -171,6 +269,7 @@ export class KnowledgeGenerationOrchestrator {
|
|
|
171
269
|
this.agentDefaultModel = agentDefaultModel
|
|
172
270
|
this.storage = storage
|
|
173
271
|
this.modelRunner = modelRunner
|
|
272
|
+
this.sessionEventSource = sessionEventSource
|
|
174
273
|
this.sourceReader = sourceReader
|
|
175
274
|
this.now = now
|
|
176
275
|
this.idFactory = idFactory
|
|
@@ -278,6 +377,7 @@ export class KnowledgeGenerationOrchestrator {
|
|
|
278
377
|
cwd: request.cwd,
|
|
279
378
|
strict: request.strict === true,
|
|
280
379
|
selectedSessionIds: request.selectedSessionIds,
|
|
380
|
+
model: request.model,
|
|
281
381
|
signal: task.controller.signal
|
|
282
382
|
})
|
|
283
383
|
summaries.push(normalizeSummary(value, source, chunk))
|
|
@@ -294,6 +394,7 @@ export class KnowledgeGenerationOrchestrator {
|
|
|
294
394
|
cwd: request.cwd,
|
|
295
395
|
strict: request.strict === true,
|
|
296
396
|
selectedSessionIds: request.selectedSessionIds,
|
|
397
|
+
model: request.model,
|
|
297
398
|
signal: task.controller.signal
|
|
298
399
|
})
|
|
299
400
|
mindMap = validateMindMap(value, { selectedSessionIds: request.selectedSessionIds, strict: request.strict === true })
|
|
@@ -306,6 +407,7 @@ export class KnowledgeGenerationOrchestrator {
|
|
|
306
407
|
cwd: request.cwd,
|
|
307
408
|
strict: request.strict === true,
|
|
308
409
|
selectedSessionIds: request.selectedSessionIds,
|
|
410
|
+
model: request.model,
|
|
309
411
|
signal: task.controller.signal
|
|
310
412
|
})
|
|
311
413
|
knowledgeGraph = validateKnowledgeGraph(value, { selectedSessionIds: request.selectedSessionIds, strict: request.strict === true })
|
|
@@ -342,39 +444,54 @@ export class KnowledgeGenerationOrchestrator {
|
|
|
342
444
|
async runModel(input) {
|
|
343
445
|
if (typeof this.modelRunner === 'function') return parseStructuredOutput(await this.modelRunner(input))
|
|
344
446
|
if (!this.agents?.create) throw new Error('当前 DSH Runtime 未提供 agents.create,无法生成知识视图。')
|
|
345
|
-
let selection =
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
447
|
+
let selection = normalizeModelSelection(input.model)
|
|
448
|
+
if (!selection) {
|
|
449
|
+
try { selection = normalizeModelSelection(this.agentDefaultModel?.currentSelection?.()) } catch { selection = null }
|
|
450
|
+
}
|
|
451
|
+
const provider = String(selection?.provider || '').trim()
|
|
452
|
+
const model = String(selection?.model || '').trim()
|
|
349
453
|
if (!provider || !model) throw new Error('当前没有可用的默认 Provider/Model。')
|
|
350
454
|
const sessionId = `knowledge-map-${this.idFactory()}`
|
|
351
|
-
const
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
name: 'knowledge-map:protocol',
|
|
359
|
-
order: 0,
|
|
360
|
-
text: '你是 DSH 知识视图生成器。只输出调用方要求的 JSON;不要调用外部网络、文件写入或其他 Agent 工具。'
|
|
455
|
+
const liveEvents = []
|
|
456
|
+
let unsubscribe
|
|
457
|
+
if (typeof this.sessionEventSource === 'function') {
|
|
458
|
+
try {
|
|
459
|
+
unsubscribe = this.sessionEventSource((session, event) => {
|
|
460
|
+
const eventSessionId = String(session?.id || session?.header?.id || '')
|
|
461
|
+
if (eventSessionId === sessionId && event) liveEvents.push(event)
|
|
361
462
|
})
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
}
|
|
366
|
-
})
|
|
463
|
+
} catch { /* event subscription is an optimization; surface reads remain the fallback */ }
|
|
464
|
+
}
|
|
465
|
+
let handle
|
|
367
466
|
try {
|
|
467
|
+
handle = await this.agents.create({
|
|
468
|
+
sessionId,
|
|
469
|
+
meta: { cwd: input.cwd, origin: 'subagent' },
|
|
470
|
+
agentOptions: { provider, model, maxTokens: input.kind === 'summary' ? 2500 : 6000 },
|
|
471
|
+
signal: input.signal,
|
|
472
|
+
setup: async (agentCtx) => {
|
|
473
|
+
agentCtx?.systemPrompt?.section?.({
|
|
474
|
+
name: 'knowledge-map:protocol',
|
|
475
|
+
order: 0,
|
|
476
|
+
text: '你是 DSH 知识视图生成器。只输出调用方要求的 JSON;不要调用外部网络、文件写入或其他 Agent 工具。'
|
|
477
|
+
})
|
|
478
|
+
try {
|
|
479
|
+
agentCtx?.tools?.restrict?.({ deny: ['multi_agent_discuss', 'shell', 'filesystem', 'web_search', 'browser'] })
|
|
480
|
+
} catch { /* older runtimes may not expose tool restriction */ }
|
|
481
|
+
}
|
|
482
|
+
})
|
|
368
483
|
handle.agent.followup(makeUserMessage(input.prompt, `${sessionId}-${input.kind}`))
|
|
369
484
|
await handle.agent.whenIdle()
|
|
370
|
-
const
|
|
371
|
-
return parseStructuredOutput(
|
|
485
|
+
const liveText = extractAgentText({ events: liveEvents })
|
|
486
|
+
if (liveText.trim()) return parseStructuredOutput(liveText)
|
|
487
|
+
return parseStructuredOutput(await readAgentText(this.sessionQuery, sessionId, input.signal))
|
|
372
488
|
} finally {
|
|
373
|
-
await
|
|
489
|
+
await unsubscribe?.()
|
|
490
|
+
await handle?.dispose?.()
|
|
374
491
|
}
|
|
375
492
|
}
|
|
376
493
|
|
|
377
|
-
async formFollowUp({ cwd, node, targetSessionId, strict = true, signal }) {
|
|
494
|
+
async formFollowUp({ cwd, node, targetSessionId, strict = true, model, signal }) {
|
|
378
495
|
const sources = await this.sourceReader({ sessionQuery: this.sessionQuery, sessions: this.sessions }, {
|
|
379
496
|
cwd,
|
|
380
497
|
sessionIds: [targetSessionId],
|
|
@@ -386,6 +503,7 @@ export class KnowledgeGenerationOrchestrator {
|
|
|
386
503
|
cwd,
|
|
387
504
|
strict,
|
|
388
505
|
selectedSessionIds: [targetSessionId],
|
|
506
|
+
model,
|
|
389
507
|
signal
|
|
390
508
|
})
|
|
391
509
|
const result = parseStructuredOutput(value)
|
package/lib/index.js
CHANGED
|
@@ -41,6 +41,25 @@ function normalizeOutputMode(value) {
|
|
|
41
41
|
return mode
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
function normalizeModelSelection(value) {
|
|
45
|
+
if (value === undefined || value === null || value === '') return null
|
|
46
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error('模型选择必须是包含 Provider 和 Model 的对象。')
|
|
47
|
+
const provider = String(value.provider || '').trim()
|
|
48
|
+
const model = String(value.model || '').trim()
|
|
49
|
+
if (!provider && !model) return null
|
|
50
|
+
if (!provider || !model) throw new Error('Provider 和 Model 必须同时填写。')
|
|
51
|
+
if (provider.length > 160 || model.length > 240) throw new Error('Provider 或 Model 名称过长。')
|
|
52
|
+
return { provider, model }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function currentModelSelection(service) {
|
|
56
|
+
try {
|
|
57
|
+
return normalizeModelSelection(service?.currentSelection?.())
|
|
58
|
+
} catch {
|
|
59
|
+
return null
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
44
63
|
function normalizeGenerationInput(body = {}) {
|
|
45
64
|
const selectedSessionIds = [...new Set((Array.isArray(body.selectedSessionIds) ? body.selectedSessionIds : []).map((id) => String(id || '').trim()).filter(Boolean))]
|
|
46
65
|
if (!selectedSessionIds.length) throw new Error('至少选择一个对话。')
|
|
@@ -54,6 +73,7 @@ function normalizeGenerationInput(body = {}) {
|
|
|
54
73
|
prompt,
|
|
55
74
|
strict: body.strict !== false,
|
|
56
75
|
includeSubagents: body.includeSubagents === true,
|
|
76
|
+
model: normalizeModelSelection(body.model),
|
|
57
77
|
expectedRevision: Number.isInteger(expectedRevision) && expectedRevision >= 0 ? expectedRevision : 0
|
|
58
78
|
}
|
|
59
79
|
}
|
|
@@ -67,6 +87,7 @@ function canonicalPayload(value) {
|
|
|
67
87
|
prompt: value.prompt,
|
|
68
88
|
strict: value.strict === true,
|
|
69
89
|
includeSubagents: value.includeSubagents === true,
|
|
90
|
+
model: value.model ? { provider: value.model.provider, model: value.model.model } : null,
|
|
70
91
|
expectedRevision: Number(value.expectedRevision || 0)
|
|
71
92
|
})
|
|
72
93
|
}
|
|
@@ -79,23 +100,26 @@ function statusForError(error) {
|
|
|
79
100
|
|
|
80
101
|
export function createHost(options = {}) {
|
|
81
102
|
const host = {
|
|
82
|
-
inject: ['agentDefaultModel', 'agents', 'sessionQuery', 'sessions', 'skills', 'webServer'],
|
|
103
|
+
inject: ['agentDefaultModel', 'agents', 'llm', 'sessionQuery', 'sessions', 'skills', 'webServer'],
|
|
83
104
|
|
|
84
105
|
apply(ctx) {
|
|
85
106
|
const sessionQuery = options.sessionQuery || getService(ctx, 'sessionQuery')
|
|
86
107
|
const sessions = options.sessions || getService(ctx, 'sessions')
|
|
87
108
|
const agents = options.agents || getService(ctx, 'agents')
|
|
88
109
|
const agentDefaultModel = options.agentDefaultModel || getService(ctx, 'agentDefaultModel')
|
|
110
|
+
const llm = options.llm || getService(ctx, 'llm')
|
|
89
111
|
const skills = options.skills || getService(ctx, 'skills')
|
|
90
112
|
const webServer = options.webServer || getService(ctx, 'webServer')
|
|
91
113
|
const storage = options.storage || new WorkspaceStorage(options.storageOptions)
|
|
114
|
+
const sessionEventSource = options.sessionEventSource || (typeof ctx?.on === 'function' ? (listener) => ctx.on('session/event', listener) : null)
|
|
92
115
|
const orchestrator = options.orchestrator || new KnowledgeGenerationOrchestrator({
|
|
93
116
|
sessionQuery,
|
|
94
117
|
sessions,
|
|
95
118
|
agents,
|
|
96
119
|
agentDefaultModel,
|
|
97
120
|
storage,
|
|
98
|
-
modelRunner: options.modelRunner
|
|
121
|
+
modelRunner: options.modelRunner,
|
|
122
|
+
sessionEventSource
|
|
99
123
|
})
|
|
100
124
|
const confirmations = new Map()
|
|
101
125
|
const sseClients = new Set()
|
|
@@ -109,6 +133,35 @@ export function createHost(options = {}) {
|
|
|
109
133
|
return { ready: true, state: 'ready', sessionId: String(header.id), cwd, origin: String(header.origin || '') }
|
|
110
134
|
}
|
|
111
135
|
|
|
136
|
+
async function modelCatalog() {
|
|
137
|
+
const defaultModel = currentModelSelection(agentDefaultModel)
|
|
138
|
+
const providers = typeof llm?.listProviders === 'function' ? llm.listProviders() : []
|
|
139
|
+
const groups = (await Promise.all(providers.map(async (provider) => {
|
|
140
|
+
const providerId = String(provider?.id || '').trim()
|
|
141
|
+
if (!providerId || typeof llm?.listModels !== 'function') return null
|
|
142
|
+
try {
|
|
143
|
+
const models = await llm.listModels(providerId)
|
|
144
|
+
const entries = (Array.isArray(models) ? models : []).map((item) => ({
|
|
145
|
+
id: String(item?.id || '').trim(),
|
|
146
|
+
name: String(item?.name || item?.id || '').trim()
|
|
147
|
+
})).filter((item) => item.id)
|
|
148
|
+
return entries.length ? {
|
|
149
|
+
id: providerId,
|
|
150
|
+
name: String(provider?.name || providerId).trim(),
|
|
151
|
+
models: entries
|
|
152
|
+
} : null
|
|
153
|
+
} catch {
|
|
154
|
+
return null
|
|
155
|
+
}
|
|
156
|
+
}))).filter(Boolean)
|
|
157
|
+
if (defaultModel) {
|
|
158
|
+
const group = groups.find((item) => item.id === defaultModel.provider)
|
|
159
|
+
if (!group) groups.unshift({ id: defaultModel.provider, name: defaultModel.provider, models: [{ id: defaultModel.model, name: defaultModel.model }] })
|
|
160
|
+
else if (!group.models.some((item) => item.id === defaultModel.model)) group.models.unshift({ id: defaultModel.model, name: defaultModel.model })
|
|
161
|
+
}
|
|
162
|
+
return { default: defaultModel, groups }
|
|
163
|
+
}
|
|
164
|
+
|
|
112
165
|
async function sessionsFor(body) {
|
|
113
166
|
const context = await contextFor(body.anchorSessionId)
|
|
114
167
|
if (!context.ready) throw new Error('请先打开一个有明确工作路径的已有对话。')
|
|
@@ -122,7 +175,9 @@ export function createHost(options = {}) {
|
|
|
122
175
|
for (const id of input.selectedSessionIds) if (!allowed.has(id)) throw new Error(`所选对话不属于当前工作路径或已不可用:${id}`)
|
|
123
176
|
const state = await storage.readState(context.cwd)
|
|
124
177
|
if (input.expectedRevision !== state.revision) throw new WorkspaceRevisionError(input.expectedRevision, state.revision)
|
|
125
|
-
const
|
|
178
|
+
const model = input.model || currentModelSelection(agentDefaultModel)
|
|
179
|
+
if (!model) throw new Error('当前没有可用的 Provider/Model,请先在模型设置中配置默认模型。')
|
|
180
|
+
const payload = { ...input, model, anchorSessionId: context.sessionId, cwd: context.cwd, expectedRevision: state.revision }
|
|
126
181
|
const token = randomUUID()
|
|
127
182
|
confirmations.set(token, { payload, expiresAt: Date.now() + CONFIRMATION_TTL, used: false })
|
|
128
183
|
return {
|
|
@@ -133,6 +188,7 @@ export function createHost(options = {}) {
|
|
|
133
188
|
selectedSessions: available.filter((item) => input.selectedSessionIds.includes(item.id)),
|
|
134
189
|
outputMode: input.outputMode,
|
|
135
190
|
strict: input.strict,
|
|
191
|
+
model,
|
|
136
192
|
promptSummary: shortText(input.prompt, 300),
|
|
137
193
|
overwrite: state.exists
|
|
138
194
|
}
|
|
@@ -141,7 +197,7 @@ export function createHost(options = {}) {
|
|
|
141
197
|
function consumeConfirmation(token, body) {
|
|
142
198
|
const value = confirmations.get(String(token || ''))
|
|
143
199
|
if (!value || value.used || value.expiresAt < Date.now()) throw new Error('生成确认已过期,请返回配置重新确认。')
|
|
144
|
-
const supplied = normalizeGenerationInput({ ...body, anchorSessionId: body.anchorSessionId || value.payload.anchorSessionId, selectedSessionIds: body.selectedSessionIds || value.payload.selectedSessionIds, outputMode: body.outputMode || value.payload.outputMode, prompt: body.prompt ?? value.payload.prompt, strict: body.strict ?? value.payload.strict, includeSubagents: body.includeSubagents ?? value.payload.includeSubagents, expectedRevision: body.expectedRevision ?? value.payload.expectedRevision })
|
|
200
|
+
const supplied = normalizeGenerationInput({ ...body, anchorSessionId: body.anchorSessionId || value.payload.anchorSessionId, selectedSessionIds: body.selectedSessionIds || value.payload.selectedSessionIds, outputMode: body.outputMode || value.payload.outputMode, prompt: body.prompt ?? value.payload.prompt, strict: body.strict ?? value.payload.strict, includeSubagents: body.includeSubagents ?? value.payload.includeSubagents, model: body.model ?? value.payload.model, expectedRevision: body.expectedRevision ?? value.payload.expectedRevision })
|
|
145
201
|
const expected = canonicalPayload(value.payload)
|
|
146
202
|
const actual = canonicalPayload({ ...supplied, cwd: value.payload.cwd })
|
|
147
203
|
if (expected !== actual) throw new Error('确认内容已变化,请返回配置重新确认。')
|
|
@@ -164,6 +220,9 @@ export function createHost(options = {}) {
|
|
|
164
220
|
const sessionId = parseUrl(req).searchParams.get('sessionId') || ''
|
|
165
221
|
return jsonResponse(res, 200, { ok: true, context: await contextFor(sessionId) })
|
|
166
222
|
}
|
|
223
|
+
if (path[0] === 'models' && method === 'GET') {
|
|
224
|
+
return jsonResponse(res, 200, { ok: true, catalog: await modelCatalog() })
|
|
225
|
+
}
|
|
167
226
|
if (path[0] === 'sessions' && method === 'GET') {
|
|
168
227
|
const anchorSessionId = parseUrl(req).searchParams.get('anchorSessionId') || ''
|
|
169
228
|
const includeSubagents = parseUrl(req).searchParams.get('includeSubagents') === 'true'
|
|
@@ -204,7 +263,7 @@ export function createHost(options = {}) {
|
|
|
204
263
|
if (!node) throw new Error('思维导图节点不存在。')
|
|
205
264
|
const targetSessionId = safeId(body.targetSessionId || node.primarySourceSessionId || node.sourceRefs?.[0]?.sessionId, '目标 Session ID')
|
|
206
265
|
if (!state.manifest?.sourceSessionIds?.includes(targetSessionId)) throw new Error('目标对话不是本次生成的来源对话。')
|
|
207
|
-
const result = await orchestrator.formFollowUp({ cwd: context.cwd, node, targetSessionId, strict: state.manifest.strict !== false })
|
|
266
|
+
const result = await orchestrator.formFollowUp({ cwd: context.cwd, node, targetSessionId, strict: state.manifest.strict !== false, model: state.manifest.model })
|
|
208
267
|
return jsonResponse(res, 200, { ok: true, followUp: result, context, revision: state.revision })
|
|
209
268
|
}
|
|
210
269
|
if (path[0] === 'navigation' && path[1] === 'confirm' && method === 'POST') {
|
package/package.json
CHANGED
|
@@ -8,6 +8,7 @@ description: 在用户明确要求整理多个同工作路径对话、生成思
|
|
|
8
8
|
- 只有用户明确点击确认后,才能读取所选对话正文、调用模型或写入 `.g-dsh-market-knowledge`。
|
|
9
9
|
- 只使用锚点 Session 的 `header.cwd` 和用户从同一 `cwd` 选择的 Session;默认排除 `origin: subagent`。
|
|
10
10
|
- 默认读取 `sessionQuery.readSurface()`,不要读取或复制完整原始 JSONL。
|
|
11
|
+
- 生成模型由用户在配置面板中选择;确认时固定 Provider / Model,并将其传给每一次摘要、图谱和追问调用。
|
|
11
12
|
- 思维导图节点必须包含标题、阶段性说明和来源引用;知识图谱关系必须包含证据和置信度。
|
|
12
13
|
- 知识图谱是静态结果,不自动更新、不在图上编辑、不从节点发散。
|
|
13
14
|
- 思维导图节点只生成后续问题;导航确认后不自动发送。
|