@twinsearth/dsh-master 3.2.9 → 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.
Files changed (102) hide show
  1. package/README.md +39 -6
  2. package/client.js +51 -1
  3. package/dist-host/cost/attribution.js +183 -0
  4. package/dist-host/cost/audit.js +163 -0
  5. package/dist-host/cost/budget.js +211 -0
  6. package/dist-host/cost/bus.js +95 -0
  7. package/dist-host/cost/cert-chain.js +100 -0
  8. package/dist-host/cost/experiment.js +132 -0
  9. package/dist-host/cost/gate.js +189 -0
  10. package/dist-host/cost/host-surface.js +80 -0
  11. package/dist-host/cost/index.js +312 -0
  12. package/dist-host/cost/ledger-report.js +193 -0
  13. package/dist-host/cost/ledger.js +401 -0
  14. package/dist-host/cost/metrics.js +198 -0
  15. package/dist-host/cost/milestones.js +171 -0
  16. package/dist-host/cost/modules/index.js +5 -0
  17. package/dist-host/cost/modules/manifests.js +130 -0
  18. package/dist-host/cost/policy.js +276 -0
  19. package/dist-host/cost/report.js +156 -0
  20. package/dist-host/cost/reuse-report.js +128 -0
  21. package/dist-host/cost/rsi.js +367 -0
  22. package/dist-host/cost/toolface.js +147 -0
  23. package/dist-host/cost/types.js +31 -0
  24. package/dist-host/kernel.js +38 -11
  25. package/dist-host/plugins/cost-optimizer.js +277 -27
  26. package/dist-host/plugins/superagent-router.js +89 -2
  27. package/dist-host/plugins/superagent-worker.js +1 -1
  28. package/dist-host/superagent/router.js +1 -1
  29. package/dist-host/superagent/types.js +8 -0
  30. package/dist-worker/agent-loop.js +449 -7
  31. package/dist-worker/baseline-fit.js +66 -0
  32. package/dist-worker/budget-guard.js +93 -0
  33. package/dist-worker/ccs.js +99 -0
  34. package/dist-worker/cost-hooks.js +115 -0
  35. package/dist-worker/cost-modules.js +330 -0
  36. package/dist-worker/cost-spi.js +3 -0
  37. package/dist-worker/edg.js +372 -0
  38. package/dist-worker/gdr.js +159 -0
  39. package/dist-worker/index.js +80 -2
  40. package/dist-worker/llm-http.js +20 -0
  41. package/dist-worker/sink-http.js +1 -0
  42. package/dist-worker/spill.js +115 -0
  43. package/dist-worker/tool-batch.js +66 -0
  44. package/dist-worker/tool-compact.js +275 -0
  45. package/dist-worker/tool-disclosure.js +90 -0
  46. package/dist-worker/tools.js +16 -0
  47. package/docs/ARCHITECTURE.md +32 -11
  48. package/docs/CHANGELOG.md +60 -0
  49. package/docs/DESIGN-v3.3.0-cost-optimization.md +1053 -0
  50. package/docs/EXPERIMENT-M5-real-ab.md +82 -0
  51. package/docs/RELEASE-v3.2.10.md +32 -0
  52. package/docs/ROADMAP.md +3 -3
  53. package/docs/SECURITY.md +9 -9
  54. package/docs/SUPERAGENT.md +5 -5
  55. package/docs/THIRD-PARTY.md +87 -0
  56. package/docs/WORKER-AGENT-LOOP.md +1 -1
  57. package/package.json +15 -3
  58. package/src/cost/attribution.ts +186 -0
  59. package/src/cost/audit.ts +170 -0
  60. package/src/cost/budget.ts +226 -0
  61. package/src/cost/bus.ts +97 -0
  62. package/src/cost/cert-chain.ts +102 -0
  63. package/src/cost/experiment.ts +136 -0
  64. package/src/cost/gate.ts +191 -0
  65. package/src/cost/host-surface.ts +97 -0
  66. package/src/cost/index.ts +316 -0
  67. package/src/cost/ledger-report.ts +186 -0
  68. package/src/cost/ledger.ts +385 -0
  69. package/src/cost/metrics.ts +193 -0
  70. package/src/cost/milestones.ts +170 -0
  71. package/src/cost/modules/index.ts +13 -0
  72. package/src/cost/modules/manifests.ts +129 -0
  73. package/src/cost/policy.ts +276 -0
  74. package/src/cost/report.ts +158 -0
  75. package/src/cost/reuse-report.ts +131 -0
  76. package/src/cost/rsi.ts +364 -0
  77. package/src/cost/toolface.ts +151 -0
  78. package/src/cost/types.ts +298 -0
  79. package/src/kernel.ts +37 -11
  80. package/src/plugins/cost-optimizer.ts +271 -26
  81. package/src/plugins/superagent-router.ts +83 -2
  82. package/src/plugins/superagent-worker.ts +1 -1
  83. package/src/superagent/router.ts +1 -1
  84. package/src/superagent/types.ts +70 -0
  85. package/worker/agent-loop.ts +444 -7
  86. package/worker/baseline-fit.ts +80 -0
  87. package/worker/budget-guard.ts +156 -0
  88. package/worker/ccs.ts +202 -0
  89. package/worker/cost-hooks.ts +133 -0
  90. package/worker/cost-modules.ts +356 -0
  91. package/worker/cost-spi.ts +283 -0
  92. package/worker/edg.ts +424 -0
  93. package/worker/gdr.ts +233 -0
  94. package/worker/index.ts +84 -3
  95. package/worker/llm-http.ts +18 -0
  96. package/worker/sink-http.ts +5 -2
  97. package/worker/spill.ts +133 -0
  98. package/worker/tool-batch.ts +124 -0
  99. package/worker/tool-compact.ts +317 -0
  100. package/worker/tool-disclosure.ts +131 -0
  101. package/worker/tools.ts +553 -531
  102. package/worker/types.ts +191 -0
@@ -0,0 +1,151 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // Copyright (c) 2026 dsh-Master contributors — see LICENSE-CORE.
3
+ // @ts-nocheck — host JS→TS 迁移阶段:运行时行为优先,类型注解逐步补齐(worker/ 仍严格检查)。
4
+
5
+ /**
6
+ * 3.3.0 · M2 交付物:**工具面 token 下降 + 归因**。
7
+ *
8
+ * 两件事必须分开算,因为它们属于两本不同的账:
9
+ *
10
+ * * **A1 紧凑输出**:回执变短 ⇒ 每次交互更便宜 ⇒ **execution 节省**。
11
+ * 默认 `shadow` 时只能报「本可节省」,不许算成真节省。
12
+ * * **A2 渐进纰漏**:常驻工具面收敛 ⇒ 每轮 schema 更少;但 `load_tools` 的装载
13
+ * 是**作者/选择成本** ⇒ **governance 账**(与 §5.1 一致),永不混进节省。
14
+ *
15
+ * 关闭 A2 时必须 `reductionRatio === 0`(fail-open 回全量工具面),这是可测的。
16
+ */
17
+
18
+ const num = (value) => {
19
+ const n = Number(value)
20
+ return Number.isFinite(n) && n >= 0 ? n : 0
21
+ }
22
+ const tokensOf = (text) => (String(text ?? '').length > 0 ? Math.ceil(String(text).length / 4) : 0)
23
+
24
+ /**
25
+ * 计算工具面:全量(基线)vs 可见(常驻 + 已加载)。
26
+ * @param {{
27
+ * schemas: Array<{name: string, description?: string, parameters?: unknown}>,
28
+ * enabled?: boolean,
29
+ * resident?: string[],
30
+ * loaded?: string[],
31
+ * }} input
32
+ */
33
+ export function planToolFace(input = {}) {
34
+ const schemas = Array.isArray(input.schemas) ? input.schemas : []
35
+ const enabled = input.enabled === true
36
+ const resident = new Set(Array.isArray(input.resident) ? input.resident.map(String) : [])
37
+ const loaded = new Set(Array.isArray(input.loaded) ? input.loaded.map(String) : [])
38
+
39
+ const tokenOfSchema = (schema) => tokensOf(JSON.stringify({ name: schema?.name, description: schema?.description, parameters: schema?.parameters }))
40
+ const baselineTokens = schemas.reduce((sum, schema) => sum + tokenOfSchema(schema), 0)
41
+ const visible = enabled ? schemas.filter((schema) => resident.has(String(schema?.name)) || loaded.has(String(schema?.name))) : schemas
42
+ const residentTokens = visible.reduce((sum, schema) => sum + tokenOfSchema(schema), 0)
43
+ const savedTokens = Math.max(0, baselineTokens - residentTokens)
44
+ return Object.freeze({
45
+ enabled,
46
+ baseline: Object.freeze({ count: schemas.length, tokens: baselineTokens }),
47
+ resident: Object.freeze({ count: visible.length, tokens: residentTokens, names: visible.map((schema) => String(schema?.name)) }),
48
+ savedTokens,
49
+ reductionRatio: baselineTokens > 0 ? savedTokens / baselineTokens : 0,
50
+ /** 关闭时必须是 0(fail-open 回全量),调用方可直接断言。 */
51
+ perTurn: Object.freeze({ tokensBefore: baselineTokens, tokensAfter: residentTokens }),
52
+ })
53
+ }
54
+
55
+ /**
56
+ * M2 报表:工具面下降(A2) + 回执压缩(A1) + 归因分列。
57
+ *
58
+ * @param {{
59
+ * schemas?: object[],
60
+ * disclosure?: {enabled?: boolean, resident?: string[], loaded?: string[]},
61
+ * compactions?: Array<{toolName?: string, applied?: boolean, evaluated?: boolean, estimated?: boolean, savedTokens?: number, savedChars?: number}>,
62
+ * loads?: Array<{toolName?: string, tokens?: number}>,
63
+ * mode?: 'off' | 'shadow' | 'enforce',
64
+ * }} input
65
+ */
66
+ export function buildToolFaceReport(input = {}) {
67
+ const disclosure = input.disclosure ?? {}
68
+ const face = planToolFace({ schemas: input.schemas, enabled: disclosure.enabled === true, resident: disclosure.resident, loaded: disclosure.loaded })
69
+ const mode = input.mode === 'off' || input.mode === 'enforce' ? input.mode : 'shadow'
70
+
71
+ const byTool = {}
72
+ let evaluated = 0
73
+ let applied = 0
74
+ let appliedSaved = 0
75
+ let wouldSave = 0
76
+ let estimatedSaved = 0
77
+ for (const event of Array.isArray(input.compactions) ? input.compactions : []) {
78
+ if (event?.evaluated === false) continue
79
+ evaluated += 1
80
+ const saved = num(event?.savedTokens ?? (num(event?.savedChars) / 4))
81
+ if (event?.applied === true) {
82
+ applied += 1
83
+ appliedSaved += saved
84
+ if (event?.estimated === true) estimatedSaved += saved
85
+ } else {
86
+ wouldSave += saved
87
+ }
88
+ const toolName = String(event?.toolName ?? 'unknown')
89
+ if (!byTool[toolName]) byTool[toolName] = { evaluated: 0, applied: 0, savedTokens: 0 }
90
+ byTool[toolName].evaluated += 1
91
+ if (event?.applied === true) {
92
+ byTool[toolName].applied += 1
93
+ byTool[toolName].savedTokens += saved
94
+ }
95
+ }
96
+
97
+ // A2 的装载成本:进 governance,不进节省
98
+ const loadEvents = Array.isArray(input.loads) ? input.loads.length : 0
99
+ const governanceTokens = (Array.isArray(input.loads) ? input.loads : []).reduce((sum, event) => sum + num(event?.tokens), 0)
100
+
101
+ // A1 的节省口径:只有 enforce 且真的替换了才算执行节省
102
+ const a1ExecutionSaved = mode === 'enforce' ? appliedSaved : 0
103
+ const a1WouldSaveTokens = mode === 'enforce' ? 0 : wouldSave + appliedSaved
104
+ const executionSaved = a1ExecutionSaved
105
+ return Object.freeze({
106
+ ok: true,
107
+ mode,
108
+ toolFace: face,
109
+ a1: Object.freeze({
110
+ module: 'a1-compact',
111
+ evaluated,
112
+ applied,
113
+ /** enforce 且替换成功时才算的节省。 */
114
+ executionSavedTokens: a1ExecutionSaved,
115
+ /** shadow 下「本可节省」——不是节省,报表必须显式标出。 */
116
+ wouldSaveTokens: a1WouldSaveTokens,
117
+ estimatedSavedTokens: estimatedSaved,
118
+ byTool: Object.freeze(byTool),
119
+ }),
120
+ a2: Object.freeze({
121
+ module: 'a2-disclosure',
122
+ enabled: face.enabled,
123
+ loadEvents,
124
+ /** 装载成本属于作者/选择成本 ⇒ governance(永不并入节省)。 */
125
+ governanceTokens,
126
+ perTurnSavedTokens: face.savedTokens,
127
+ }),
128
+ attribution: Object.freeze({
129
+ /** 工具面下降算 A2 的贡献,但**只有在 enabled 时才非 0**。 */
130
+ 'a2-disclosure': face.savedTokens,
131
+ 'a1-compact': a1ExecutionSaved,
132
+ governance: governanceTokens,
133
+ }),
134
+ totals: Object.freeze({
135
+ executionSaved,
136
+ governanceTokens,
137
+ /** 净收益 = 执行节省(A1,enforce 才有) + 工具面下降(A2,per-turn) − 装载治理成本。 */
138
+ netBenefit: executionSaved + face.savedTokens - governanceTokens,
139
+ /** 两项分列(图省事合并会导致 §8 的重复计风险)。 */
140
+ byKind: Object.freeze({
141
+ toolFaceSavedTokens: face.savedTokens,
142
+ receiptSavedTokens: a1ExecutionSaved,
143
+ }),
144
+ }),
145
+ discipline: Object.freeze({
146
+ shadowNeverCountedAsSavings: mode !== 'enforce',
147
+ loadCostIsGovernance: true,
148
+ disabledDisclosureReducesNothing: face.enabled !== true || face.savedTokens >= 0,
149
+ }),
150
+ })
151
+ }
@@ -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.2.9'
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,16 +66,17 @@ export const ROW = Object.freeze({
41
66
  export const ROLE = Object.freeze({ mother: 'mother', essence: 'essence', fork: 'fork' })
42
67
 
43
68
  /**
44
- * 角色矩阵(v3.2.3):
45
- * * Router 常驻原身(essence):只评估/建票/派发,不执行代码;母体不接纳。
46
- * * Worker 是研究原型,只在 fork:编译后的 Worker preset 行 master-superagent-loop 也只 fork。
47
- * * 成本优化 fork;双主管 mother/essence。
69
+ * 角色矩阵:
70
+ * * Router 在 essence 与 mother 激活:只评估/建票/派发,不执行代码;fork 不注册。
71
+ * * Worker / Worker Agent Loop 在 fork 与 essence 激活:原身可直接执行工单,
72
+ * 分身仍是默认执行体;mother 不执行。
73
+ * * 成本优化仍只 fork;双主管 mother/essence。
48
74
  */
49
75
  export const ROLE_MATRIX = Object.freeze({
50
76
  'master-core': Object.freeze(Object.values(ROLE)),
51
- 'master-superagent-router': Object.freeze([ROLE.essence]),
52
- 'master-superagent-worker': Object.freeze([ROLE.fork]),
53
- 'master-superagent-loop': Object.freeze([ROLE.fork]),
77
+ 'master-superagent-router': Object.freeze([ROLE.essence, ROLE.mother]),
78
+ 'master-superagent-worker': Object.freeze([ROLE.fork, ROLE.essence]),
79
+ 'master-superagent-loop': Object.freeze([ROLE.fork, ROLE.essence]),
54
80
  'master-cost-optimizer': Object.freeze([ROLE.fork]),
55
81
  'master-dual-manager': Object.freeze([ROLE.mother, ROLE.essence]),
56
82
  })
@@ -59,17 +85,17 @@ export const SUBPLUGIN_CATALOG = Object.freeze([
59
85
  Object.freeze({
60
86
  id: 'superagent-router', row: ROW.superagentRouter, name: '超级智能体 Router', nameEn: 'SuperAgent Router',
61
87
  roles: ROLE_MATRIX[ROW.superagentRouter], roadmap: '3.2.1',
62
- description: '常驻原身:Webhook → 评估 → Ticket/Mandate → 派发;不执行代码改动。',
88
+ description: '常驻原身/母体:Webhook → 评估 → Ticket/Mandate → 派发;不执行代码改动。',
63
89
  }),
64
90
  Object.freeze({
65
91
  id: 'superagent-worker', row: ROW.superagentWorker, name: '超级智能体 Worker', nameEn: 'SuperAgent Worker',
66
92
  roles: ROLE_MATRIX[ROW.superagentWorker], roadmap: '3.2.1',
67
- description: '每个工单一个隔离分身:在 Mandate 作用域内执行;研究原型,仅 fork。',
93
+ description: '每个工单一个隔离执行体:在 Mandate 作用域内执行;fork 与 essence 可用。',
68
94
  }),
69
95
  Object.freeze({
70
96
  id: 'superagent-loop', row: ROW.superagentLoop, name: 'Worker Agent Loop', nameEn: 'SuperAgent Worker Loop',
71
97
  roles: ROLE_MATRIX[ROW.superagentLoop], roadmap: '3.2.3',
72
- description: '编译后的 Worker preset:有 Ticket 的 fork 自动跑多轮工具循环并流式回传。',
98
+ description: '编译后的 Worker preset:有 Ticket 的在 fork/essence 自动跑多轮工具循环并流式回传。',
73
99
  }),
74
100
  Object.freeze({
75
101
  id: 'cost-optimizer', row: ROW.cost, name: '成本优化', nameEn: 'HarnessMM',