weflow-cli 1.7.0 → 1.8.0
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/ARCHITECTURE.md +3 -2
- package/CHANGELOG.md +1949 -1
- package/OPERATIONS.md +774 -1
- package/README.en.md +93 -7
- package/README.md +31 -6
- package/bin/weflow-cli.ts +934 -27
- package/dist/bin/weflow-cli.js +952 -27
- package/dist/bin/weflow-cli.js.map +1 -1
- package/dist/mcp-server/index.js +39 -13
- package/dist/mcp-server/index.js.map +1 -1
- package/dist/src/core/appMsgFormat.d.ts +37 -0
- package/dist/src/core/appMsgFormat.d.ts.map +1 -0
- package/dist/src/core/appMsgFormat.js +106 -0
- package/dist/src/core/appMsgFormat.js.map +1 -0
- package/dist/src/core/ntCore.d.ts +19 -2
- package/dist/src/core/ntCore.d.ts.map +1 -1
- package/dist/src/core/ntCore.js +11 -1
- package/dist/src/core/ntCore.js.map +1 -1
- package/dist/src/core/sqlcipherCore.d.ts +0 -12
- package/dist/src/core/sqlcipherCore.d.ts.map +1 -1
- package/dist/src/core/sqlcipherCore.js +5 -64
- package/dist/src/core/sqlcipherCore.js.map +1 -1
- package/dist/src/core/wcdbCore.d.ts +1 -1
- package/dist/src/core/wcdbCore.d.ts.map +1 -1
- package/dist/src/core/wcdbCore.js +5 -1
- package/dist/src/core/wcdbCore.js.map +1 -1
- package/dist/src/panel/client.d.ts +25 -0
- package/dist/src/panel/client.d.ts.map +1 -0
- package/dist/src/panel/client.js +34 -0
- package/dist/src/panel/client.js.map +1 -0
- package/dist/src/panel/endpoint.d.ts +45 -0
- package/dist/src/panel/endpoint.d.ts.map +1 -0
- package/dist/src/panel/endpoint.js +132 -0
- package/dist/src/panel/endpoint.js.map +1 -0
- package/dist/src/panel/launch.d.ts +50 -0
- package/dist/src/panel/launch.d.ts.map +1 -0
- package/dist/src/panel/launch.js +74 -0
- package/dist/src/panel/launch.js.map +1 -0
- package/dist/src/panel/server.d.ts +23 -0
- package/dist/src/panel/server.d.ts.map +1 -0
- package/dist/src/panel/server.js +413 -0
- package/dist/src/panel/server.js.map +1 -0
- package/dist/src/panel/userId.d.ts +37 -0
- package/dist/src/panel/userId.d.ts.map +1 -0
- package/dist/src/panel/userId.js +56 -0
- package/dist/src/panel/userId.js.map +1 -0
- package/dist/src/services/assistantDaemon.d.ts +20 -3
- package/dist/src/services/assistantDaemon.d.ts.map +1 -1
- package/dist/src/services/assistantDaemon.js +46 -9
- package/dist/src/services/assistantDaemon.js.map +1 -1
- package/dist/src/services/assistantEval.d.ts +141 -0
- package/dist/src/services/assistantEval.d.ts.map +1 -0
- package/dist/src/services/assistantEval.js +497 -0
- package/dist/src/services/assistantEval.js.map +1 -0
- package/dist/src/services/assistantMemory.d.ts +106 -4
- package/dist/src/services/assistantMemory.d.ts.map +1 -1
- package/dist/src/services/assistantMemory.js +396 -61
- package/dist/src/services/assistantMemory.js.map +1 -1
- package/dist/src/services/assistantPrivacy.d.ts +15 -2
- package/dist/src/services/assistantPrivacy.d.ts.map +1 -1
- package/dist/src/services/assistantPrivacy.js +18 -4
- package/dist/src/services/assistantPrivacy.js.map +1 -1
- package/dist/src/services/assistantRouter.d.ts +41 -0
- package/dist/src/services/assistantRouter.d.ts.map +1 -0
- package/dist/src/services/assistantRouter.js +151 -0
- package/dist/src/services/assistantRouter.js.map +1 -0
- package/dist/src/services/assistantService.d.ts +209 -3
- package/dist/src/services/assistantService.d.ts.map +1 -1
- package/dist/src/services/assistantService.js +590 -75
- package/dist/src/services/assistantService.js.map +1 -1
- package/dist/src/services/assistantTools.d.ts +205 -1
- package/dist/src/services/assistantTools.d.ts.map +1 -1
- package/dist/src/services/assistantTools.js +1089 -58
- package/dist/src/services/assistantTools.js.map +1 -1
- package/dist/src/services/assistantTrace.d.ts +47 -0
- package/dist/src/services/assistantTrace.d.ts.map +1 -0
- package/dist/src/services/assistantTrace.js +150 -0
- package/dist/src/services/assistantTrace.js.map +1 -0
- package/dist/src/services/chatService.d.ts +1 -1
- package/dist/src/services/chatService.d.ts.map +1 -1
- package/dist/src/services/chatService.js +5 -2
- package/dist/src/services/chatService.js.map +1 -1
- package/dist/src/services/configService.d.ts +9 -0
- package/dist/src/services/configService.d.ts.map +1 -1
- package/dist/src/services/configService.js +15 -8
- package/dist/src/services/configService.js.map +1 -1
- package/dist/src/services/pythonBridge.d.ts +41 -0
- package/dist/src/services/pythonBridge.d.ts.map +1 -0
- package/dist/src/services/pythonBridge.js +119 -0
- package/dist/src/services/pythonBridge.js.map +1 -0
- package/dist/src/services/syncService.d.ts +8 -2
- package/dist/src/services/syncService.d.ts.map +1 -1
- package/dist/src/services/syncService.js +26 -7
- package/dist/src/services/syncService.js.map +1 -1
- package/dist/src/services/wechatMessageService.d.ts +25 -0
- package/dist/src/services/wechatMessageService.d.ts.map +1 -1
- package/dist/src/services/wechatMessageService.js +69 -7
- package/dist/src/services/wechatMessageService.js.map +1 -1
- package/dist/src/utils/atomicWrite.d.ts +4 -0
- package/dist/src/utils/atomicWrite.d.ts.map +1 -0
- package/dist/src/utils/atomicWrite.js +60 -0
- package/dist/src/utils/atomicWrite.js.map +1 -0
- package/dist/src/utils/dateRange.d.ts +15 -0
- package/dist/src/utils/dateRange.d.ts.map +1 -1
- package/dist/src/utils/dateRange.js +47 -0
- package/dist/src/utils/dateRange.js.map +1 -1
- package/dist/src/utils/text.d.ts +18 -0
- package/dist/src/utils/text.d.ts.map +1 -0
- package/dist/src/utils/text.js +20 -0
- package/dist/src/utils/text.js.map +1 -0
- package/docs/AI_INTERFACE.md +2 -0
- package/docs/DECISIONS.md +1424 -0
- package/docs/EXTENDING.md +187 -0
- package/docs/JEV/345/272/224/347/224/250/346/226/275/345/267/245/346/226/207/346/241/243.md +310 -0
- package/docs/MCP.md +23 -4
- package/docs/PROJECT_STATE.md +299 -98
- package/docs/SETUP.md +22 -0
- package/docs/SYNC_CONTRACT.md +31 -3
- package/mcp-server/index.ts +681 -657
- package/package.json +84 -83
- package/resources/panel/ball-position.cjs +153 -0
- package/resources/panel/index.html +46 -0
- package/resources/panel/main.cjs +447 -0
- package/resources/panel/mascot-focus.png +0 -0
- package/resources/panel/mascot-happy.png +0 -0
- package/resources/panel/mascot-sorry.png +0 -0
- package/resources/panel/mascot-tired.png +0 -0
- package/resources/panel/mascot.png +0 -0
- package/resources/panel/package.json +7 -0
- package/resources/panel/panel.css +407 -0
- package/resources/panel/preload.cjs +72 -0
- package/resources/panel/quick-menu.cjs +197 -0
- package/resources/panel/renderer.js +441 -0
- package/resources/panel/tray-menu.cjs +40 -0
- package/resources/panel/tray.png +0 -0
- package/scripts/_utils.py +403 -54
- package/scripts/annual_report.py +4 -2
- package/scripts/article_notes.py +423 -0
- package/scripts/backfill_articles.py +438 -0
- package/scripts/biz_daily.py +590 -105
- package/scripts/chat_notes.py +470 -0
- package/scripts/classify_daily.py +10 -18
- package/scripts/compile_wiki.py +1059 -46
- package/scripts/create_reading_notes.py +199 -14
- package/scripts/decide.py +253 -0
- package/scripts/draft_reply.py +671 -0
- package/scripts/export_chat_html.py +35 -33
- package/scripts/extract_todos.py +31 -5
- package/scripts/fav_notes.py +318 -0
- package/scripts/fix_topics.py +2 -1
- package/scripts/generate_ai_report.py +214 -9
- package/scripts/generate_html.py +18 -3
- package/scripts/generate_review.py +2 -2
- package/scripts/jev_client.py +294 -0
- package/scripts/jev_probe.py +440 -0
- package/scripts/nt_common.py +82 -0
- package/scripts/nt_decrypt.py +1755 -1460
- package/scripts/pipeline.py +27 -2
- package/scripts/promote_ideas.py +19 -4
- package/scripts/quality_eval.py +707 -0
- package/scripts/rag_chat.py +4 -4
- package/scripts/read_image.py +187 -0
- package/scripts/reply_debt.py +488 -0
- package/scripts/route_cards.py +453 -0
- package/scripts/semantic_search.py +208 -49
- package/scripts/user_notes.py +315 -0
- package/scripts/vault_rag.py +13 -7
- package/scripts/vault_search.py +21 -4
- package/scripts/wiki_lint.py +397 -0
- package/src/core/appMsgFormat.ts +117 -0
- package/src/core/ntCore.ts +27 -2
- package/src/core/sqlcipherCore.ts +5 -61
- package/src/core/wcdbCore.ts +6 -2
- package/src/panel/client.ts +49 -0
- package/src/panel/endpoint.ts +144 -0
- package/src/panel/launch.ts +101 -0
- package/src/panel/server.ts +424 -0
- package/src/panel/userId.ts +72 -0
- package/src/services/assistantDaemon.ts +57 -9
- package/src/services/assistantEval.ts +611 -0
- package/src/services/assistantMemory.ts +413 -57
- package/src/services/assistantPrivacy.ts +18 -4
- package/src/services/assistantRouter.ts +191 -0
- package/src/services/assistantService.ts +671 -74
- package/src/services/assistantTools.ts +1118 -57
- package/src/services/assistantTrace.ts +175 -0
- package/src/services/chatService.ts +6 -2
- package/src/services/configService.ts +403 -385
- package/src/services/pythonBridge.ts +139 -0
- package/src/services/syncService.ts +37 -9
- package/src/services/wechatMessageService.ts +73 -7
- package/src/utils/atomicWrite.ts +56 -0
- package/src/utils/dateRange.ts +46 -0
- package/src/utils/text.ts +20 -0
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 助手一轮的**决策轨迹**:它判断了什么、调了哪个工具、调了几轮、为什么停下。
|
|
3
|
+
*
|
|
4
|
+
* 为什么要有:助手此前对外只吐一句答复,中间过程全在审计里散成一行行事件
|
|
5
|
+
* (`CLOUD_CALL` / `TOOL:x` / `TURN_DONE tools=N`)——`TURN_DONE tools=5` 只告诉你有 5 次
|
|
6
|
+
* 工具调用,不告诉你是哪 5 次、按什么顺序、每次带什么参数。排查"它为什么答成这样"时,
|
|
7
|
+
* 缺的正是这一层。
|
|
8
|
+
*
|
|
9
|
+
* ## 与审计的分工(别混)
|
|
10
|
+
*
|
|
11
|
+
* - **审计**(`assistant_audit.log`):只记事件与字节量,**绝不记内容**,且是出境记录。
|
|
12
|
+
* - **轨迹**(`assistant_trace.jsonl`):记这一步是**怎么走的**,包括工具参数的摘要——
|
|
13
|
+
* 所以它是本地排查用的,不是给第三方看的。参数摘要**过一遍 `privacyGate.redact`**,
|
|
14
|
+
* 值截断,和别处的出境纪律一致。
|
|
15
|
+
*
|
|
16
|
+
* ## 思维链
|
|
17
|
+
*
|
|
18
|
+
* 轨迹里的 `reasoning` 是模型**真返回**的推理内容(OpenAI 兼容接口的 `reasoning_content`)。
|
|
19
|
+
* 当前默认模型 `deepseek-chat` 不返回它(那是 `deepseek-reasoner` 一类模型的行为),
|
|
20
|
+
* 所以这个字段平时是空的——**空就是空,不假装有**。`reasoningChars` 会如实记 0。
|
|
21
|
+
*/
|
|
22
|
+
import { appendFileSync, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from 'node:fs'
|
|
23
|
+
import { join } from 'node:path'
|
|
24
|
+
import { homedir } from 'node:os'
|
|
25
|
+
import { privacyGate } from './assistantPrivacy.js'
|
|
26
|
+
|
|
27
|
+
const TRACE_FILE = join(homedir(), '.weflow-cli', 'assistant_trace.jsonl')
|
|
28
|
+
/** 单条记录里推理内容最多留多少字(它是模型的独白,不是给用户看的东西) */
|
|
29
|
+
const REASONING_KEEP = 800
|
|
30
|
+
/** 单个参数值最多留多少字 */
|
|
31
|
+
const ARG_VALUE_KEEP = 40
|
|
32
|
+
/** 文件超过这个大小就裁到最近 `TRACE_KEEP` 条 */
|
|
33
|
+
const TRACE_MAX_BYTES = 512 * 1024
|
|
34
|
+
const TRACE_KEEP = 200
|
|
35
|
+
|
|
36
|
+
export type StopReason = 'answered' | 'rounds-exhausted' | 'llm-error' | 'builtin' | 'denied'
|
|
37
|
+
|
|
38
|
+
export interface ToolStep {
|
|
39
|
+
kind: 'tool'
|
|
40
|
+
name: string
|
|
41
|
+
/** 参数摘要(已脱敏、已截断) */
|
|
42
|
+
args: string
|
|
43
|
+
bytes: number
|
|
44
|
+
/** 这次**有没有给出内容**。判据与边界见 `assistantTools.producedContent` */
|
|
45
|
+
produced: boolean
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface NoteStep {
|
|
49
|
+
kind: 'route' | 'round' | 'note'
|
|
50
|
+
detail: string
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export type TraceStep = ToolStep | NoteStep
|
|
54
|
+
|
|
55
|
+
export interface TurnTrace {
|
|
56
|
+
at: string
|
|
57
|
+
/** 会话/用户 id。**只留在本地文件里**,微信里那条 `轨迹` 不打印它 */
|
|
58
|
+
userId: string
|
|
59
|
+
questionChars: number
|
|
60
|
+
steps: TraceStep[]
|
|
61
|
+
rounds: number
|
|
62
|
+
toolCalls: number
|
|
63
|
+
/** 模型真返回的推理内容;当前默认模型不返回,所以通常是空串 */
|
|
64
|
+
reasoning: string
|
|
65
|
+
reasoningChars: number
|
|
66
|
+
answerChars: number
|
|
67
|
+
stop: StopReason
|
|
68
|
+
elapsedMs: number
|
|
69
|
+
error?: string
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** 工具参数的摘要:`contact=甲, limit=20`。过脱敏、值截断。 */
|
|
73
|
+
export function summarizeArgs(args: Record<string, unknown>): string {
|
|
74
|
+
const parts: string[] = []
|
|
75
|
+
for (const [key, value] of Object.entries(args ?? {})) {
|
|
76
|
+
let text: string
|
|
77
|
+
if (value === null || value === undefined) text = String(value)
|
|
78
|
+
else if (typeof value === 'object') text = Array.isArray(value) ? `[${value.length} 项]` : '{…}'
|
|
79
|
+
else text = String(value)
|
|
80
|
+
if (text.length > ARG_VALUE_KEEP) text = text.slice(0, ARG_VALUE_KEEP) + '…'
|
|
81
|
+
parts.push(`${key}=${text}`)
|
|
82
|
+
}
|
|
83
|
+
const joined = parts.join(', ')
|
|
84
|
+
return privacyGate.redact(joined).safe
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function trimIfLarge(): void {
|
|
88
|
+
try {
|
|
89
|
+
if (!existsSync(TRACE_FILE) || statSync(TRACE_FILE).size <= TRACE_MAX_BYTES) return
|
|
90
|
+
const lines = readFileSync(TRACE_FILE, 'utf8').split('\n').filter(Boolean)
|
|
91
|
+
writeFileSync(TRACE_FILE, lines.slice(-TRACE_KEEP).join('\n') + '\n', 'utf8')
|
|
92
|
+
} catch { /* 裁剪失败不该影响记录 */ }
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** 记一轮。失败不抛——排查用的东西不该把对话弄挂。 */
|
|
96
|
+
export function recordTurn(trace: TurnTrace): void {
|
|
97
|
+
try {
|
|
98
|
+
// 目录要先建:新机器上 `~/.weflow-cli` 可能还不存在,而 `appendFileSync` 会抛 ENOENT——
|
|
99
|
+
// 被下面那个 catch 吞掉,就成了"轨迹静默地永远不落盘"(审计那边一开始就有这一步,
|
|
100
|
+
// 轨迹漏了;写这条测试时被自己抓到)。
|
|
101
|
+
const dir = join(homedir(), '.weflow-cli')
|
|
102
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true })
|
|
103
|
+
trimIfLarge()
|
|
104
|
+
appendFileSync(TRACE_FILE, JSON.stringify(trace, null, 0) + '\n', 'utf8')
|
|
105
|
+
} catch { /* 记录失败不阻断 */ }
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** 读最近 `n` 轮(新→旧)。读不出来就给空数组,不抛。 */
|
|
109
|
+
export function readTurns(n = 5): TurnTrace[] {
|
|
110
|
+
try {
|
|
111
|
+
const lines = readFileSync(TRACE_FILE, 'utf8').split('\n').filter(Boolean)
|
|
112
|
+
return lines.slice(-n).map(line => {
|
|
113
|
+
try { return JSON.parse(line) as TurnTrace } catch { return null }
|
|
114
|
+
}).filter(Boolean).reverse() as TurnTrace[]
|
|
115
|
+
} catch {
|
|
116
|
+
return []
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function traceFile(): string {
|
|
121
|
+
return TRACE_FILE
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** 一轮的推理内容该留多少。截断时留个记号,别让"截断"读起来像"说完了"。 */
|
|
125
|
+
export function clipReasoning(text: string): string {
|
|
126
|
+
const trimmed = (text ?? '').trim()
|
|
127
|
+
return trimmed.length <= REASONING_KEEP ? trimmed : trimmed.slice(0, REASONING_KEEP) + '…'
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const STEP_LABEL: Record<string, string> = {
|
|
131
|
+
route: '判断层', round: '模型往返', note: '说明', tool: '工具',
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** 给终端看的多行描述。纯函数,好测。 */
|
|
135
|
+
export function describeTurn(trace: TurnTrace): string[] {
|
|
136
|
+
const lines: string[] = []
|
|
137
|
+
const stamp = (trace.at || '').replace('T', ' ').slice(0, 19)
|
|
138
|
+
lines.push(`${stamp} ${trace.elapsedMs}ms 往返 ${trace.rounds} 次 · 工具 ${trace.toolCalls} 次 · 结束于「${trace.stop}」`)
|
|
139
|
+
for (const step of trace.steps) {
|
|
140
|
+
if (step.kind === 'tool') {
|
|
141
|
+
const mark = step.produced ? '' : ' (无内容)'
|
|
142
|
+
lines.push(` · ${STEP_LABEL.tool} ${step.name}(${step.args}) → ${step.bytes} 字节${mark}`)
|
|
143
|
+
} else {
|
|
144
|
+
lines.push(` · ${STEP_LABEL[step.kind] ?? step.kind}: ${step.detail}`)
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (trace.reasoningChars) {
|
|
148
|
+
lines.push(` · 推理 ${trace.reasoningChars} 字${trace.reasoning ? ':' : '(模型这次没有返回内容)'}`)
|
|
149
|
+
for (const line of (trace.reasoning || '').split('\n')) lines.push(` ${line}`)
|
|
150
|
+
} else {
|
|
151
|
+
lines.push(' · 推理: 无(当前模型不返回 reasoning_content)')
|
|
152
|
+
}
|
|
153
|
+
if (trace.error) lines.push(` · 出错: ${trace.error}`)
|
|
154
|
+
return lines
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* 微信里 `轨迹` 那条要短。**不打印 userId**——它是账号标识,没必要出现在聊天里。
|
|
159
|
+
*/
|
|
160
|
+
export function describeForChat(trace: TurnTrace | undefined): string {
|
|
161
|
+
if (!trace) return '还没有可看的轨迹:这条之前的记录不在本机(或这是第一条消息)。'
|
|
162
|
+
const lines: string[] = [`上一轮(${(trace.at || '').slice(11, 19)},${trace.elapsedMs}ms,往返 ${trace.rounds} 次)`]
|
|
163
|
+
for (const step of trace.steps) {
|
|
164
|
+
if (step.kind === 'tool') {
|
|
165
|
+
lines.push(`· ${step.name}(${step.args}) → ${step.bytes} 字节${step.produced ? '' : '(无内容)'}`)
|
|
166
|
+
} else {
|
|
167
|
+
lines.push(`· ${step.detail}`)
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
lines.push(`· 结束: ${trace.stop}`)
|
|
171
|
+
lines.push(trace.reasoningChars
|
|
172
|
+
? `· 推理: ${trace.reasoningChars} 字(当前模型${trace.reasoning ? '有返回' : '只报了长度'})`
|
|
173
|
+
: '· 推理: 无(当前模型不返回 reasoning_content)')
|
|
174
|
+
return lines.join('\n')
|
|
175
|
+
}
|
|
@@ -251,17 +251,21 @@ export class ChatService {
|
|
|
251
251
|
async getMessagesWithShards(
|
|
252
252
|
talker: string,
|
|
253
253
|
limit = 0,
|
|
254
|
-
offset = 0
|
|
254
|
+
offset = 0,
|
|
255
|
+
fromTime?: number | null
|
|
255
256
|
): Promise<{ messages: Message[]; shards?: ShardReport }> {
|
|
256
257
|
const conn = await this.connect()
|
|
257
258
|
if (!conn.success) return { messages: [] }
|
|
258
259
|
|
|
259
260
|
if (this.ntCore) {
|
|
260
|
-
const result = await this.ntCore.getMessagesWithShards(talker, limit, offset)
|
|
261
|
+
const result = await this.ntCore.getMessagesWithShards(talker, limit, offset, fromTime)
|
|
261
262
|
if (!result.success || !result.messages) return { messages: [] }
|
|
262
263
|
return { messages: result.messages, shards: result.shards }
|
|
263
264
|
}
|
|
264
265
|
|
|
266
|
+
// The other backends have no shard report and no range pushdown. They
|
|
267
|
+
// return everything and the caller's own window filter does the rest,
|
|
268
|
+
// which is why a sync over them reports `coverage: 'unverified'`.
|
|
265
269
|
return { messages: await this.getMessages(talker, limit, offset) }
|
|
266
270
|
}
|
|
267
271
|
|