@twinsearth/dsh-master 3.2.10 → 3.3.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/README.md +3 -0
- package/client.js +50 -0
- package/dist-host/cost/attribution.js +183 -0
- package/dist-host/cost/audit.js +163 -0
- package/dist-host/cost/budget.js +211 -0
- package/dist-host/cost/bus.js +95 -0
- package/dist-host/cost/cert-chain.js +100 -0
- package/dist-host/cost/experiment.js +132 -0
- package/dist-host/cost/gate.js +189 -0
- package/dist-host/cost/host-surface.js +80 -0
- package/dist-host/cost/index.js +312 -0
- package/dist-host/cost/ledger-report.js +193 -0
- package/dist-host/cost/ledger.js +401 -0
- package/dist-host/cost/metrics.js +198 -0
- package/dist-host/cost/milestones.js +171 -0
- package/dist-host/cost/modules/index.js +5 -0
- package/dist-host/cost/modules/manifests.js +130 -0
- package/dist-host/cost/policy.js +276 -0
- package/dist-host/cost/report.js +156 -0
- package/dist-host/cost/reuse-report.js +128 -0
- package/dist-host/cost/rsi.js +367 -0
- package/dist-host/cost/toolface.js +147 -0
- package/dist-host/cost/types.js +31 -0
- package/dist-host/kernel.js +28 -2
- package/dist-host/plugins/cost-optimizer.js +277 -27
- package/dist-host/plugins/superagent-router.js +88 -1
- package/dist-host/superagent/types.js +8 -0
- package/dist-worker/agent-loop.js +449 -7
- package/dist-worker/baseline-fit.js +66 -0
- package/dist-worker/budget-guard.js +93 -0
- package/dist-worker/ccs.js +99 -0
- package/dist-worker/cost-hooks.js +115 -0
- package/dist-worker/cost-modules.js +330 -0
- package/dist-worker/cost-spi.js +3 -0
- package/dist-worker/edg.js +372 -0
- package/dist-worker/gdr.js +159 -0
- package/dist-worker/index.js +80 -2
- package/dist-worker/llm-http.js +20 -0
- package/dist-worker/sink-http.js +1 -0
- package/dist-worker/spill.js +115 -0
- package/dist-worker/tool-batch.js +66 -0
- package/dist-worker/tool-compact.js +275 -0
- package/dist-worker/tool-disclosure.js +90 -0
- package/dist-worker/tools.js +16 -0
- package/docs/ARCHITECTURE.md +21 -0
- package/docs/CHANGELOG.md +50 -0
- package/docs/DESIGN-v3.3.0-cost-optimization.md +1053 -0
- package/docs/EXPERIMENT-M5-real-ab.md +82 -0
- package/docs/THIRD-PARTY.md +87 -0
- package/package.json +1 -1
- package/src/cost/attribution.ts +186 -0
- package/src/cost/audit.ts +170 -0
- package/src/cost/budget.ts +226 -0
- package/src/cost/bus.ts +97 -0
- package/src/cost/cert-chain.ts +102 -0
- package/src/cost/experiment.ts +136 -0
- package/src/cost/gate.ts +191 -0
- package/src/cost/host-surface.ts +97 -0
- package/src/cost/index.ts +316 -0
- package/src/cost/ledger-report.ts +186 -0
- package/src/cost/ledger.ts +385 -0
- package/src/cost/metrics.ts +193 -0
- package/src/cost/milestones.ts +170 -0
- package/src/cost/modules/index.ts +13 -0
- package/src/cost/modules/manifests.ts +129 -0
- package/src/cost/policy.ts +276 -0
- package/src/cost/report.ts +158 -0
- package/src/cost/reuse-report.ts +131 -0
- package/src/cost/rsi.ts +364 -0
- package/src/cost/toolface.ts +151 -0
- package/src/cost/types.ts +298 -0
- package/src/kernel.ts +27 -2
- package/src/plugins/cost-optimizer.ts +271 -26
- package/src/plugins/superagent-router.ts +82 -1
- package/src/superagent/types.ts +70 -0
- package/worker/agent-loop.ts +444 -7
- package/worker/baseline-fit.ts +80 -0
- package/worker/budget-guard.ts +156 -0
- package/worker/ccs.ts +202 -0
- package/worker/cost-hooks.ts +133 -0
- package/worker/cost-modules.ts +356 -0
- package/worker/cost-spi.ts +283 -0
- package/worker/edg.ts +424 -0
- package/worker/gdr.ts +233 -0
- package/worker/index.ts +84 -3
- package/worker/llm-http.ts +18 -0
- package/worker/sink-http.ts +5 -2
- package/worker/spill.ts +133 -0
- package/worker/tool-batch.ts +124 -0
- package/worker/tool-compact.ts +317 -0
- package/worker/tool-disclosure.ts +131 -0
- package/worker/tools.ts +553 -531
- package/worker/types.ts +191 -0
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// Copyright (c) 2026 dsh-Master contributors — see LICENSE-CORE.
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 3.3.0 成本优化系统层的共享类型(M1)。
|
|
6
|
+
*
|
|
7
|
+
* 只放类型与常量,不含运行时逻辑;host 侧 `src/cost/*` 与插件编排器共用。
|
|
8
|
+
* 对齐 `docs/DESIGN-v3.3.0-cost-optimization.md` §5(系统层)与 §9(安全不变量)。
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/** 两本账:执行节省 vs 治理成本(HarnessMM 论文要求分开核算)。 */
|
|
12
|
+
export type CostAccount = 'execution' | 'governance'
|
|
13
|
+
|
|
14
|
+
/** 归因模块 id;a* = 工具面(dsh-ops 方向),b* = 决策复用(HarnessMM 方向)。 */
|
|
15
|
+
export type CostModuleId =
|
|
16
|
+
| 'a1-compact'
|
|
17
|
+
| 'a2-disclosure'
|
|
18
|
+
| 'a3-batch'
|
|
19
|
+
| 'b1-edg'
|
|
20
|
+
| 'b2-gdr'
|
|
21
|
+
| 'b3-ccs'
|
|
22
|
+
| 'b4-rsi'
|
|
23
|
+
| 'unattributed'
|
|
24
|
+
|
|
25
|
+
export type CostSource = 'model' | 'tool' | 'substitution' | 'retry' | 'authoring' | 'offline'
|
|
26
|
+
|
|
27
|
+
/** 事件相位(设计 §5.1):prompt / completion / tool / authoring / substitution。 */
|
|
28
|
+
export type CostPhase = 'prompt' | 'completion' | 'tool' | 'authoring' | 'substitution'
|
|
29
|
+
|
|
30
|
+
export const COST_PHASES: readonly CostPhase[] = Object.freeze(['prompt', 'completion', 'tool', 'authoring', 'substitution'])
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* 作用面(设计 §3.3):宿主会话工具面与 master Worker 工具面是**两个不同面板**,
|
|
34
|
+
* 节省必须分开记账,否则会把「装了别人的宿主插件」算成自己的收益。
|
|
35
|
+
* * `worker` —— 本项目 Worker 自带工具面(`worker/tools.ts`),A1–A3 在这里生效;
|
|
36
|
+
* * `host` —— 宿主会话工具面(dsh-ops 一类宿主插件生效的地方),本项目只做
|
|
37
|
+
* **可选适配器 + 归因**,不重写、不冒领。
|
|
38
|
+
*/
|
|
39
|
+
export type CostSurface = 'worker' | 'host'
|
|
40
|
+
|
|
41
|
+
export interface CostTokens {
|
|
42
|
+
input: number
|
|
43
|
+
output: number
|
|
44
|
+
total: number
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface CostEntryInput {
|
|
48
|
+
account: CostAccount
|
|
49
|
+
module?: CostModuleId
|
|
50
|
+
source?: CostSource
|
|
51
|
+
/** 相位;缺省由 source 推导(见 ledger.ts PHASE_OF_SOURCE)。 */
|
|
52
|
+
phase?: CostPhase
|
|
53
|
+
/** 作用面;缺省为 worker(本项目的自研模块都在这面)。 */
|
|
54
|
+
surface?: CostSurface
|
|
55
|
+
/** 外部适配器上报(如 dsh-ops 在宿主面的收益):只展示,永不计入本项目 executionSaved。 */
|
|
56
|
+
external?: boolean
|
|
57
|
+
taskId?: string
|
|
58
|
+
forkId?: string
|
|
59
|
+
sessionId?: string
|
|
60
|
+
/** 会话内轮次与循环步号(可回放定位)。 */
|
|
61
|
+
turn?: number
|
|
62
|
+
step?: number
|
|
63
|
+
provider?: string
|
|
64
|
+
model?: string
|
|
65
|
+
toolName?: string
|
|
66
|
+
ticketId?: string
|
|
67
|
+
inputTokens?: number
|
|
68
|
+
outputTokens?: number
|
|
69
|
+
cachedTokens?: number
|
|
70
|
+
totalTokens?: number
|
|
71
|
+
/** 对照组(observe)同任务基线;给出时才计算 saved。 */
|
|
72
|
+
baselineTokens?: number
|
|
73
|
+
/** 显式节省归因;给出时优先于 baseline 推导。 */
|
|
74
|
+
savedTokens?: number
|
|
75
|
+
/** provider 未返回 usage 时的兜底估算:报表与实测分开列,不混算。 */
|
|
76
|
+
estimated?: boolean
|
|
77
|
+
meta?: Record<string, unknown>
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface CostEntry extends CostTokens {
|
|
81
|
+
id: string
|
|
82
|
+
at: number
|
|
83
|
+
account: CostAccount
|
|
84
|
+
module: CostModuleId
|
|
85
|
+
source: CostSource
|
|
86
|
+
phase: CostPhase
|
|
87
|
+
surface: CostSurface
|
|
88
|
+
external: boolean
|
|
89
|
+
taskId: string | null
|
|
90
|
+
forkId: string | null
|
|
91
|
+
sessionId: string | null
|
|
92
|
+
turn: number
|
|
93
|
+
step: number
|
|
94
|
+
provider: string | null
|
|
95
|
+
model: string | null
|
|
96
|
+
toolName: string | null
|
|
97
|
+
ticketId: string | null
|
|
98
|
+
cached: number
|
|
99
|
+
baselineTokens: number | null
|
|
100
|
+
savedTokens: number
|
|
101
|
+
estimated: boolean
|
|
102
|
+
meta: Record<string, unknown>
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export interface CostBlock {
|
|
106
|
+
input: number
|
|
107
|
+
output: number
|
|
108
|
+
total: number
|
|
109
|
+
saved: number
|
|
110
|
+
entries: number
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export interface CostSurfaceRow extends CostBlock {
|
|
114
|
+
surface: CostSurface
|
|
115
|
+
external: boolean
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export interface CostTaskRow extends CostBlock {
|
|
119
|
+
key: string
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export interface CostModuleRow extends CostBlock {
|
|
123
|
+
module: CostModuleId
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export interface CostForkRow extends CostBlock {
|
|
127
|
+
forkId: string
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** 估算与实测必须分开列(设计 §5.1):总量各自累计,绝不相加。 */
|
|
131
|
+
export interface CostEstimateSplit {
|
|
132
|
+
measuredTokens: number
|
|
133
|
+
measuredEntries: number
|
|
134
|
+
estimatedTokens: number
|
|
135
|
+
estimatedEntries: number
|
|
136
|
+
/** 估算值占全部 token 的比例(0..1),用于报表标注可信度。 */
|
|
137
|
+
estimatedRatio: number
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export interface CostLedgerSnapshot {
|
|
141
|
+
ok: true
|
|
142
|
+
count: number
|
|
143
|
+
dropped: number
|
|
144
|
+
execution: CostBlock
|
|
145
|
+
governance: CostBlock
|
|
146
|
+
netSaved: number
|
|
147
|
+
ownExecutionSaved: number
|
|
148
|
+
externalSaved: number
|
|
149
|
+
/** 按作用面拆分:本项目(worker)与外部适配器(host)永不相加。 */
|
|
150
|
+
bySurface: CostSurfaceRow[]
|
|
151
|
+
byModule: CostModuleRow[]
|
|
152
|
+
byTask: CostTaskRow[]
|
|
153
|
+
byFork: CostForkRow[]
|
|
154
|
+
/** 实测 vs 估算(不混算)。 */
|
|
155
|
+
tokens: CostEstimateSplit
|
|
156
|
+
persistence: { enabled: boolean; file: string | null; appended: number; failed: number }
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export interface CostQuery {
|
|
160
|
+
taskId?: string
|
|
161
|
+
forkId?: string
|
|
162
|
+
sessionId?: string
|
|
163
|
+
ticketId?: string
|
|
164
|
+
module?: string
|
|
165
|
+
account?: CostAccount
|
|
166
|
+
phase?: CostPhase
|
|
167
|
+
surface?: CostSurface
|
|
168
|
+
external?: boolean
|
|
169
|
+
estimated?: boolean
|
|
170
|
+
/** 时间窗(含边界)。 */
|
|
171
|
+
from?: number
|
|
172
|
+
to?: number
|
|
173
|
+
limit?: number
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export type BudgetUnit = 'task' | 'fork' | 'day'
|
|
177
|
+
export type BudgetExhaustAction = 'hard-stop' | 'degrade'
|
|
178
|
+
|
|
179
|
+
export interface BudgetRule {
|
|
180
|
+
unit: BudgetUnit
|
|
181
|
+
/** token 上限(> 0)。与 limitCost 至少给一个,否则规则无效(fail-closed)。 */
|
|
182
|
+
limitTokens?: number
|
|
183
|
+
/** 成本上限(同一货币单位;需要调用方在 authorize 时给 cost 才可判定)。 */
|
|
184
|
+
limitCost?: number
|
|
185
|
+
/** 超限策略;默认 hard-stop。 */
|
|
186
|
+
onExhaust?: BudgetExhaustAction
|
|
187
|
+
/** degrade 档目标模型;必须在 degradeModels 白名单内,否则自动退化为 hard-stop。 */
|
|
188
|
+
degradeTo?: string
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/** @deprecated 使用 BudgetRule(保留别名以兼容早期调用)。 */
|
|
192
|
+
export type BudgetLimit = BudgetRule
|
|
193
|
+
|
|
194
|
+
export interface BudgetStateRow {
|
|
195
|
+
unit: BudgetUnit
|
|
196
|
+
key: string
|
|
197
|
+
consumedTokens: number
|
|
198
|
+
consumedCost: number
|
|
199
|
+
limitTokens: number | null
|
|
200
|
+
limitCost: number | null
|
|
201
|
+
remainingTokens: number | null
|
|
202
|
+
remainingCost: number | null
|
|
203
|
+
onExhaust: BudgetExhaustAction
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export type PolicyMode = 'observe' | 'shadow' | 'enforce'
|
|
207
|
+
|
|
208
|
+
export interface EnforceEvidence {
|
|
209
|
+
/** shadow 期守卫准确率(0..1)。 */
|
|
210
|
+
accuracy?: number
|
|
211
|
+
/** shadow 期样本数。 */
|
|
212
|
+
samples?: number
|
|
213
|
+
/** shadow 期破坏性工具命中次数(必须为 0)。 */
|
|
214
|
+
destructiveHits?: number
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export interface PolicyDescription {
|
|
218
|
+
mode: PolicyMode
|
|
219
|
+
requestedMode: PolicyMode
|
|
220
|
+
allowlist: string[]
|
|
221
|
+
gate: { minAccuracy: number; minSamples: number; allowlistRequired: true }
|
|
222
|
+
lastRefusal: { at: number; next: PolicyMode; reasons: string[] } | null
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export interface AuditRecord {
|
|
226
|
+
seq: number
|
|
227
|
+
at: number
|
|
228
|
+
type: string
|
|
229
|
+
actor: string
|
|
230
|
+
payload: Record<string, unknown>
|
|
231
|
+
prevHash: string
|
|
232
|
+
hash: string
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* 替换证书(设计 §5.5):每一次「用已解决决策替换慢模型回调」都必须留下这张凭证。
|
|
237
|
+
* 与 ledger 事件用同一 `taskId + step` 关联,因此可回放「这一省是怎么来的」。
|
|
238
|
+
*/
|
|
239
|
+
export interface SubstitutionCert {
|
|
240
|
+
/** 可执行决策图(B1)中的边标识。 */
|
|
241
|
+
edgeId: string
|
|
242
|
+
/** 被替换回调原本要调用的工具。 */
|
|
243
|
+
toolName: string
|
|
244
|
+
/** 工具 schema 的哈希:schema 变了,旧决策不得复用。 */
|
|
245
|
+
schemaHash: string
|
|
246
|
+
/** 守卫表达式(B2 判据),保留原文以便复核。 */
|
|
247
|
+
guardExpr: string
|
|
248
|
+
/** 命中时工具回执的摘要(B2 依据它判断守卫)。 */
|
|
249
|
+
receiptDigest: string
|
|
250
|
+
/** 被替换掉的模型回调标识(慢模型那一次)。 */
|
|
251
|
+
replacedCallbackId: string
|
|
252
|
+
/** 必须经 DSH 原生派发器执行:恒为 true(false 一律拒绝)。 */
|
|
253
|
+
viaNativeDispatcher: true
|
|
254
|
+
/** 关联键(与 ledger 事件一致)。 */
|
|
255
|
+
taskId: string
|
|
256
|
+
step: number
|
|
257
|
+
/** 本次替换的归因节省(token)。 */
|
|
258
|
+
savedTokens?: number
|
|
259
|
+
moduleId?: string
|
|
260
|
+
forkId?: string
|
|
261
|
+
sessionId?: string
|
|
262
|
+
ticketId?: string
|
|
263
|
+
at?: number
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export const COST_MODULES: readonly CostModuleId[] = Object.freeze([
|
|
267
|
+
'a1-compact',
|
|
268
|
+
'a2-disclosure',
|
|
269
|
+
'a3-batch',
|
|
270
|
+
'b1-edg',
|
|
271
|
+
'b2-gdr',
|
|
272
|
+
'b3-ccs',
|
|
273
|
+
'b4-rsi',
|
|
274
|
+
'unattributed',
|
|
275
|
+
])
|
|
276
|
+
|
|
277
|
+
export const COST_SOURCES: readonly CostSource[] = Object.freeze([
|
|
278
|
+
'model',
|
|
279
|
+
'tool',
|
|
280
|
+
'substitution',
|
|
281
|
+
'retry',
|
|
282
|
+
'authoring',
|
|
283
|
+
'offline',
|
|
284
|
+
])
|
|
285
|
+
|
|
286
|
+
export const POLICY_MODES: readonly PolicyMode[] = Object.freeze(['observe', 'shadow', 'enforce'])
|
|
287
|
+
|
|
288
|
+
/** 两个作用面(设计 §3.3):不可合并统计。 */
|
|
289
|
+
export const COST_SURFACES: readonly CostSurface[] = Object.freeze(['worker', 'host'])
|
|
290
|
+
|
|
291
|
+
/** 默认预算:与 Worker 现有 maxTokens 对齐(task=50k),fork=500k。 */
|
|
292
|
+
export const DEFAULT_BUDGET_LIMITS: readonly BudgetLimit[] = Object.freeze([
|
|
293
|
+
Object.freeze({ unit: 'task' as BudgetUnit, limitTokens: 50_000, onExhaust: 'hard-stop' as BudgetExhaustAction }),
|
|
294
|
+
Object.freeze({ unit: 'fork' as BudgetUnit, limitTokens: 500_000, onExhaust: 'hard-stop' as BudgetExhaustAction }),
|
|
295
|
+
])
|
|
296
|
+
|
|
297
|
+
/** enforce 门禁(设计 §13):准确率 ≥0.98、样本 ≥20、零破坏性命中、非空白名单。 */
|
|
298
|
+
export const ENFORCE_GATE = Object.freeze({ minAccuracy: 0.98, minSamples: 20 })
|
package/src/kernel.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
import crypto from 'node:crypto'
|
|
12
12
|
|
|
13
|
-
export const MASTER_VERSION = '3.
|
|
13
|
+
export const MASTER_VERSION = '3.3.0'
|
|
14
14
|
export const FANG_CONTRACT_MIN = '3.1.14'
|
|
15
15
|
|
|
16
16
|
export const SERVICE = Object.freeze({
|
|
@@ -21,6 +21,31 @@ export const SERVICE = Object.freeze({
|
|
|
21
21
|
dual: 'dshMasterDual',
|
|
22
22
|
})
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* 3.3.0 §11:成本系统的**可选子服务**。
|
|
26
|
+
*
|
|
27
|
+
* 主服务 `SERVICE.cost` 的形态不变(向后兼容);子服务由 cost-optimizer 在能力允许时
|
|
28
|
+
* 额外 `provide`,消费方必须用 `probeOptionalServices(ctx)` **探测**而不是假定存在 ——
|
|
29
|
+
* 缺了就是缺了,不能假装拿到空壳对象。
|
|
30
|
+
*/
|
|
31
|
+
export const OPTIONAL_SERVICE = Object.freeze({
|
|
32
|
+
costLedger: 'dshMasterCostLedger',
|
|
33
|
+
costBudget: 'dshMasterCostBudget',
|
|
34
|
+
costPolicy: 'dshMasterCostPolicy',
|
|
35
|
+
costAudit: 'dshMasterCostAudit',
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
/** 探测可选子服务是否就位(只读、无副作用)。 */
|
|
39
|
+
export function probeOptionalServices(ctx) {
|
|
40
|
+
const present = {}
|
|
41
|
+
for (const [key, name] of Object.entries(OPTIONAL_SERVICE)) {
|
|
42
|
+
try {
|
|
43
|
+
present[key] = typeof ctx?.get === 'function' ? Boolean(ctx.get(name)) : false
|
|
44
|
+
} catch { present[key] = false }
|
|
45
|
+
}
|
|
46
|
+
return present
|
|
47
|
+
}
|
|
48
|
+
|
|
24
49
|
export const FANG_SERVICE = Object.freeze({
|
|
25
50
|
orchestrator: 'fang',
|
|
26
51
|
forker: 'fangForker',
|
|
@@ -41,7 +66,7 @@ export const ROW = Object.freeze({
|
|
|
41
66
|
export const ROLE = Object.freeze({ mother: 'mother', essence: 'essence', fork: 'fork' })
|
|
42
67
|
|
|
43
68
|
/**
|
|
44
|
-
*
|
|
69
|
+
* 角色矩阵:
|
|
45
70
|
* * Router 在 essence 与 mother 激活:只评估/建票/派发,不执行代码;fork 不注册。
|
|
46
71
|
* * Worker / Worker Agent Loop 在 fork 与 essence 激活:原身可直接执行工单,
|
|
47
72
|
* 分身仍是默认执行体;mother 不执行。
|