dsh-plugin-prompt-tool 3.0.1 → 3.1.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 +17 -1
- package/engine/actions/catalog.mjs +2 -1
- package/engine/actions/index.mjs +5 -3
- package/engine/actions/shared.mjs +26 -12
- package/engine/actions/tool-visibility.mjs +31 -14
- package/engine/compositions/source/local/context-provenance.yml +9 -0
- package/engine/compositions/source/local/generation-recovery.yml +10 -0
- package/engine/compositions/source/local/retrieval.yml +7 -0
- package/engine/compositions/source/local/soul-ledger.yml +3 -0
- package/engine/compositions/source/local/soul-maintenance.yml +9 -0
- package/engine/conditions/examples.mjs +1 -0
- package/engine/conditions/index.mjs +7 -0
- package/engine/conditions/manifest.mjs +28 -0
- package/engine/context-idle-compaction.mjs +58 -0
- package/engine/context-provenance.d.mts +7 -0
- package/engine/context-provenance.mjs +156 -0
- package/engine/context-pruner.mjs +64 -0
- package/engine/context-routing.mjs +45 -0
- package/engine/context-summary.mjs +80 -0
- package/engine/fillers.mjs +37 -4
- package/engine/generation-recovery-coordinator.mjs +40 -0
- package/engine/generation-recovery-maintenance.mjs +18 -0
- package/engine/generation-recovery.mjs +153 -0
- package/engine/host-package.mjs +13 -0
- package/engine/layers.d.mts +4 -0
- package/engine/layers.mjs +57 -20
- package/engine/present-as.mjs +2 -2
- package/engine/retrieval-reference.d.mts +5 -0
- package/engine/retrieval-reference.mjs +26 -0
- package/engine/retrieval.mjs +17 -0
- package/engine/rule-engine.mjs +1 -0
- package/engine/rule-runtime.d.mts +8 -1
- package/engine/rule-runtime.mjs +18 -10
- package/engine/rule-spec.mjs +4 -2
- package/engine/schema.mjs +1 -1
- package/engine/service-names.d.mts +5 -0
- package/engine/service-names.mjs +5 -0
- package/engine/soul-ledger.mjs +8 -0
- package/engine/soul-maintenance.mjs +20 -0
- package/lib/client.js +16 -6
- package/lib/client.js.map +1 -1
- package/lib/index.mjs +1953 -246
- package/modules/soul/assets/ledger.md +4 -2
- package/modules/soul/assets/manifest.yml +2 -0
- package/modules/soul/assets/reference/00-layers.md +27 -27
- package/modules/soul/assets/reference/02-echo.md +161 -161
- package/modules/soul/assets/reference/05-doctor.md +67 -67
- package/modules/soul/assets/reference/10-echo-anatomy.md +109 -109
- package/modules/soul/assets/reference/ECHO-/346/235/203/345/250/201/345/217/243/345/276/204.md +202 -202
- package/modules/soul/assets/reference/ECHO/345/205/255/345/261/202/344/270/216mnemon/345/215/217/345/220/214/350/257/264/346/230/216/344/271/246.md +229 -229
- package/modules/soul/assets/templates/agents-template.md +3 -0
- package/modules/soul/assets/templates/self-template.md +19 -0
- package/modules/soul/module.yml +71 -13
- package/modules/tool-surface/module.yml +8 -5
- package/package.json +1 -1
- package/skills/dsh-module/reference.md +2 -2
- package/templates/80-tool-surface.yml +8 -7
package/engine/layers.mjs
CHANGED
|
@@ -183,7 +183,7 @@ function textLayerGroups(configs) {
|
|
|
183
183
|
}
|
|
184
184
|
|
|
185
185
|
/** system-section:注册静态 system prompt 段(支持官方 {{variable}} 渲染与 merged 拼接)。 */
|
|
186
|
-
function wireSystemSections(ctx, configs, registry, warnOnce, keep, ordered) {
|
|
186
|
+
function wireSystemSections(ctx, configs, registry, warnOnce, keep, ordered, sourceError) {
|
|
187
187
|
const systemPrompt = getService(ctx, 'systemPrompt')
|
|
188
188
|
if (systemPrompt === undefined || typeof systemPrompt.section !== 'function') {
|
|
189
189
|
if (configs.length > 0) warnOnce(`${name}: systemPrompt service unavailable — system-section configs skipped`)
|
|
@@ -218,6 +218,7 @@ function wireSystemSections(ctx, configs, registry, warnOnce, keep, ordered) {
|
|
|
218
218
|
keep(systemPrompt.suppressRuntimeContext(), `${name}: suppressRuntimeContext ${base.id}`)
|
|
219
219
|
}
|
|
220
220
|
} catch (error) {
|
|
221
|
+
if (sourceError !== undefined) throw sourceError(error, base.sourceModuleId)
|
|
221
222
|
warnOnce(`${name}: system-section config ${base.id} failed: ${String(error?.message ?? error)}`)
|
|
222
223
|
}
|
|
223
224
|
}
|
|
@@ -240,7 +241,7 @@ async function resolvedContextText(ctx, config, context, registry, warnOnce) {
|
|
|
240
241
|
}
|
|
241
242
|
|
|
242
243
|
/** 条件化文本保留官方排序占位;仅在本次真实 assembly 填充,不缓存 context 身份。 */
|
|
243
|
-
function wireRuleTextContributions(ctx, configs, registry, warnOnce, keep, on, ordered) {
|
|
244
|
+
function wireRuleTextContributions(ctx, configs, registry, warnOnce, keep, on, ordered, sourceError) {
|
|
244
245
|
if (!configs.length) return []
|
|
245
246
|
const systemPrompt = getService(ctx, 'systemPrompt')
|
|
246
247
|
if (systemPrompt === undefined) { warnOnce(`${name}: systemPrompt unavailable`); return [] }
|
|
@@ -252,8 +253,26 @@ function wireRuleTextContributions(ctx, configs, registry, warnOnce, keep, on, o
|
|
|
252
253
|
const section = layer === 'system-section'
|
|
253
254
|
const base = group[0]
|
|
254
255
|
const entryName = textRegistrationName(base, section ? 'sectionName' : 'contextName')
|
|
255
|
-
|
|
256
|
-
|
|
256
|
+
// runtime-context 在官方 refreshContext 里按注册 provider 重算:provider 必须返回当前文本,
|
|
257
|
+
// 否则 assemble 钩子的填充会在请求前被 marker 覆盖。system-section 不重算,仍用 marker 占位。
|
|
258
|
+
// 文本槽按 Agent 分区:同一挂载服务多个 Agent,交错装配时不能互相读到对方的正文。
|
|
259
|
+
const holders = section ? undefined : new WeakMap()
|
|
260
|
+
const sharedHolder = section ? undefined : { text: '' }
|
|
261
|
+
const holderOf = section ? undefined : (context) => {
|
|
262
|
+
const key = context?.agent
|
|
263
|
+
if (key === undefined || key === null) return sharedHolder
|
|
264
|
+
let holder = holders.get(key)
|
|
265
|
+
if (holder === undefined) { holder = { text: '' }; holders.set(key, holder) }
|
|
266
|
+
return holder
|
|
267
|
+
}
|
|
268
|
+
try {
|
|
269
|
+
keep(systemPrompt[section ? 'section' : 'context']({
|
|
270
|
+
name: entryName,
|
|
271
|
+
order: base.order,
|
|
272
|
+
text: section ? marker : (context) => holderOf(context).text,
|
|
273
|
+
}), `${name}: conditional ${entryName}`)
|
|
274
|
+
} catch (error) { throw sourceError === undefined ? error : sourceError(error, base.sourceModuleId) }
|
|
275
|
+
bindings.push({ group, name: entryName, collection: section ? 'sections' : 'contexts', holderOf })
|
|
257
276
|
ordered[section ? 'sections' : 'contexts'].push(orderedEntry(entryName, base))
|
|
258
277
|
}
|
|
259
278
|
let active = true
|
|
@@ -261,9 +280,13 @@ function wireRuleTextContributions(ctx, configs, registry, warnOnce, keep, on, o
|
|
|
261
280
|
return [on('system-prompt/assemble', async (assembly, context, next, invocation) => {
|
|
262
281
|
const frame = invocation ?? ruleFrame('system-prompt/assemble', [assembly, context], warnOnce)
|
|
263
282
|
for (const binding of bindings) {
|
|
264
|
-
const entry =
|
|
283
|
+
const entry = binding.collection === 'sections'
|
|
284
|
+
? assembly.sections.find(item => item.name === binding.name && item.text === marker)
|
|
285
|
+
: assembly.contexts.find(item => item.name === binding.name)
|
|
265
286
|
if (entry === undefined) continue
|
|
287
|
+
const holder = binding.holderOf === undefined ? undefined : binding.holderOf(context)
|
|
266
288
|
entry.text = ''
|
|
289
|
+
if (holder !== undefined) holder.text = ''
|
|
267
290
|
const blocks = []
|
|
268
291
|
for (const config of binding.group) {
|
|
269
292
|
if (!active || context.signal?.aborted || !actionMatches(config, frame) || !matchesAgentScope(config, context.agent)) continue
|
|
@@ -274,7 +297,10 @@ function wireRuleTextContributions(ctx, configs, registry, warnOnce, keep, on, o
|
|
|
274
297
|
if (text.length) blocks.push(text)
|
|
275
298
|
} catch (error) { warnOnce(`${name}: rule text ${config.id} failed: ${String(error?.message ?? error)}`) }
|
|
276
299
|
}
|
|
277
|
-
if (active && !context.signal?.aborted)
|
|
300
|
+
if (active && !context.signal?.aborted) {
|
|
301
|
+
entry.text = blocks.join('\n\n')
|
|
302
|
+
if (holder !== undefined) holder.text = entry.text
|
|
303
|
+
}
|
|
278
304
|
}
|
|
279
305
|
return next()
|
|
280
306
|
})]
|
|
@@ -282,7 +308,7 @@ function wireRuleTextContributions(ctx, configs, registry, warnOnce, keep, on, o
|
|
|
282
308
|
}
|
|
283
309
|
|
|
284
310
|
/** runtime-context:注册运行时上下文;静态文本支持 merged,动态策略在官方 assembly waterfall 中填充。 */
|
|
285
|
-
function wireRuntimeContexts(ctx, configs, registry, warnOnce, keep, on, ordered) {
|
|
311
|
+
function wireRuntimeContexts(ctx, configs, registry, warnOnce, keep, on, ordered, sourceError) {
|
|
286
312
|
const disposers = []
|
|
287
313
|
const systemPrompt = getService(ctx, 'systemPrompt')
|
|
288
314
|
if (systemPrompt === undefined || typeof systemPrompt.context !== 'function') {
|
|
@@ -309,44 +335,55 @@ function wireRuntimeContexts(ctx, configs, registry, warnOnce, keep, on, ordered
|
|
|
309
335
|
}), `${name}: context ${base.id}`)
|
|
310
336
|
ordered.contexts.push(orderedEntry(entryName, base))
|
|
311
337
|
} catch (error) {
|
|
338
|
+
if (sourceError !== undefined) throw sourceError(error, base.sourceModuleId)
|
|
312
339
|
if (base.linkedDelivery) throw error
|
|
313
340
|
warnOnce(`${name}: runtime-context config ${base.id} failed: ${String(error?.message ?? error)}`)
|
|
314
341
|
}
|
|
315
342
|
}
|
|
316
|
-
// 官方 provider
|
|
317
|
-
//
|
|
343
|
+
// 官方 provider 必须同步:注册函数 provider 读取 holder,再在 waterfall 中填充。
|
|
344
|
+
// 官方 refreshContext 会用注册 provider 重算 contexts;返回 holder 当前值,填充才不会被 reset。
|
|
318
345
|
const placeholders = configs.filter(needsResolver)
|
|
319
346
|
.sort(compareTextPlacement)
|
|
320
347
|
if (placeholders.length === 0) return disposers
|
|
321
|
-
const slotVariable = `pt_runtime_${newMessageId('context').replace(/[^a-z0-9_]/gi, '_').toLowerCase()}`
|
|
322
|
-
const slotText = `{{${slotVariable}}}`
|
|
323
348
|
const registered = new Map()
|
|
324
349
|
let active = true
|
|
325
350
|
keep(() => { active = false }, `${name}: runtime-context lifecycle`)
|
|
326
|
-
keep(systemPrompt.variable(slotVariable, () => ''), `${name}: runtime-context placeholder`)
|
|
327
351
|
for (const config of placeholders) {
|
|
328
352
|
try {
|
|
329
353
|
const contextName = textRegistrationName(config, 'contextName')
|
|
354
|
+
// 文本槽按 Agent 分区:交错装配时 A 的 refresh 不得读到 B 的正文。
|
|
355
|
+
const holders = new WeakMap()
|
|
356
|
+
const sharedHolder = { text: '' }
|
|
357
|
+
const holderOf = (context) => {
|
|
358
|
+
const key = context?.agent
|
|
359
|
+
if (key === undefined || key === null) return sharedHolder
|
|
360
|
+
let holder = holders.get(key)
|
|
361
|
+
if (holder === undefined) { holder = { text: '' }; holders.set(key, holder) }
|
|
362
|
+
return holder
|
|
363
|
+
}
|
|
330
364
|
keep(systemPrompt.context({
|
|
331
365
|
name: contextName,
|
|
332
366
|
order: config.order,
|
|
333
|
-
text:
|
|
367
|
+
text: (context) => holderOf(context).text,
|
|
334
368
|
}), `${name}: context ${config.id}`)
|
|
335
|
-
registered.set(contextName, config)
|
|
369
|
+
registered.set(contextName, { config, holderOf })
|
|
336
370
|
ordered.contexts.push(orderedEntry(contextName, config))
|
|
337
371
|
} catch (error) {
|
|
372
|
+
if (sourceError !== undefined) throw sourceError(error, config.sourceModuleId)
|
|
338
373
|
warnOnce(`${name}: runtime-context placeholder ${config.id} failed: ${String(error?.message ?? error)}`)
|
|
339
374
|
}
|
|
340
375
|
}
|
|
341
376
|
disposers.push(on('system-prompt/assemble', async (assembly, context, next) => {
|
|
342
|
-
const entries = assembly.contexts.filter(entry => registered.has(entry.name)
|
|
377
|
+
const entries = assembly.contexts.filter(entry => registered.has(entry.name))
|
|
343
378
|
for (const entry of entries) {
|
|
344
|
-
const config = registered.get(entry.name)
|
|
379
|
+
const { config, holderOf } = registered.get(entry.name)
|
|
380
|
+
const holder = holderOf(context)
|
|
345
381
|
entry.text = ''
|
|
382
|
+
holder.text = ''
|
|
346
383
|
if (!active || context.signal?.aborted) continue
|
|
347
384
|
try {
|
|
348
385
|
const text = await resolvedContextText(ctx, config, context, registry.get(config), warnOnce)
|
|
349
|
-
if (active && !context.signal?.aborted) entry.text = text
|
|
386
|
+
if (active && !context.signal?.aborted) { entry.text = text; holder.text = text }
|
|
350
387
|
} catch (error) {
|
|
351
388
|
warnOnce(`${name}: runtime-context ${config.id} resolve failed: ${String(error?.message ?? error)}`)
|
|
352
389
|
}
|
|
@@ -681,9 +718,9 @@ export function wireLayers(ctx, configs, warnOnce, options = {}) {
|
|
|
681
718
|
&& (typeof config.rule?.when === 'function' || typeof config.actionWhen === 'function'
|
|
682
719
|
|| (config.rule !== undefined || config.params?.templateEvaluation === true) && config.params?.complete !== true && config.params?.suppressRuntimeContext !== true))
|
|
683
720
|
const regular = configs.filter(config => !conditional.includes(config))
|
|
684
|
-
wireSystemSections(ctx, regular.filter((config) => config.layer === 'system-section'), registry, warnOnce, keep, ordered)
|
|
685
|
-
wireRuntimeContexts(ctx, regular.filter((config) => config.layer === 'runtime-context'), registry, warnOnce, keep, on, ordered)
|
|
686
|
-
wireRuleTextContributions(ctx, conditional, registry, warnOnce, keep, on, ordered)
|
|
721
|
+
wireSystemSections(ctx, regular.filter((config) => config.layer === 'system-section'), registry, warnOnce, keep, ordered, options.sourceError)
|
|
722
|
+
wireRuntimeContexts(ctx, regular.filter((config) => config.layer === 'runtime-context'), registry, warnOnce, keep, on, ordered, options.sourceError)
|
|
723
|
+
wireRuleTextContributions(ctx, conditional, registry, warnOnce, keep, on, ordered, options.sourceError)
|
|
687
724
|
wireTurnStops(ctx, configs.filter((config) => config.layer === 'turn-stop'), warnOnce, on, options.turnStopBudgets)
|
|
688
725
|
wireSubagentEvents(ctx, configs.filter((config) => config.layer === 'subagent-start' || config.layer === 'subagent-end'), warnOnce, on)
|
|
689
726
|
// 官方 order 只提供初始槽位;configOrder 覆盖段/上下文的先后(平台段与其他插件段不动)。
|
package/engine/present-as.mjs
CHANGED
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
* declared for this scope`)——本行不捕获、不降级,让它响亮失败(装配失败优于静默换形态)。
|
|
14
14
|
*
|
|
15
15
|
* `ptc` 需要 `ctx.ptcRuntime`(宿主 base bundle 的 `ptc-runtime` 行提供):用 `ctx.inject`
|
|
16
|
-
* 等它就绪再声明;等不到本行停在 pending
|
|
17
|
-
*
|
|
16
|
+
* 等它就绪再声明;等不到本行停在 pending。受管装配观察初始化结果,声明失败时按来源
|
|
17
|
+
* 回退或撤销模块;本行不自行改成 native。`native` 不需要运行时,直接声明。
|
|
18
18
|
*
|
|
19
19
|
* disposer:`presentAs` 本身就是 effect(它用调用方的 ctx 注册、并把那个 disposer 还回来),
|
|
20
20
|
* 本行不再包一层,随 fiber 释放即撤销。
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export type RetrievalEvent = { readonly seq: number; readonly type: string }
|
|
2
|
+
export type SessionReference = { kind: 'session'; moduleId: string; sessionId: string; seqs: number[]; revision: string; start: number; end: number }
|
|
3
|
+
export const MAX_REFERENCE_CHARS: number
|
|
4
|
+
export function sessionReferenceText(events: readonly RetrievalEvent[], seqs: readonly number[]): string
|
|
5
|
+
export function makeSessionReference(moduleId: string, sessionId: string, events: readonly RetrievalEvent[], seqs: readonly number[], range?: { start?: number; end?: number }): SessionReference
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto'
|
|
2
|
+
|
|
3
|
+
export const MAX_REFERENCE_CHARS = 8192
|
|
4
|
+
|
|
5
|
+
/** seq 是持久日志身份;不排序为数值区间,也不以相同正文替代。 */
|
|
6
|
+
export function sessionReferenceText(events, seqs) {
|
|
7
|
+
if (!Array.isArray(seqs) || seqs.length === 0 || seqs.length > 2048 || new Set(seqs).size !== seqs.length || seqs.some(seq => !Number.isSafeInteger(seq) || seq < 0)) throw new Error('检索引用事件身份无效')
|
|
8
|
+
const bySeq = new Map(events.map(event => [event.seq, event]))
|
|
9
|
+
let bytes = 0
|
|
10
|
+
return seqs.map(seq => {
|
|
11
|
+
const event = bySeq.get(seq)
|
|
12
|
+
if (!event) throw new Error('检索引用已失效')
|
|
13
|
+
const text = JSON.stringify(event)
|
|
14
|
+
bytes += Buffer.byteLength(text)
|
|
15
|
+
if (bytes > 64 * 1024 * 1024) throw new Error('检索引用来源超过64 MiB上限')
|
|
16
|
+
return text
|
|
17
|
+
}).join('\n')
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function makeSessionReference(moduleId, sessionId, events, seqs, range = {}) {
|
|
21
|
+
if (typeof moduleId !== 'string' || !moduleId || typeof sessionId !== 'string' || !sessionId) throw new Error('检索引用缺少真实来源身份')
|
|
22
|
+
const text = sessionReferenceText(events, seqs)
|
|
23
|
+
const start = range.start ?? 0, end = range.end ?? Math.min(text.length, start + MAX_REFERENCE_CHARS)
|
|
24
|
+
if (!Number.isSafeInteger(start) || !Number.isSafeInteger(end) || start < 0 || end <= start || end > text.length || end - start > MAX_REFERENCE_CHARS) throw new Error('检索引用范围无效')
|
|
25
|
+
return { kind: 'session', moduleId, sessionId, seqs: [...seqs], revision: createHash('sha256').update(text).digest('hex'), start, end }
|
|
26
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { bool, defineConfig, stringList } from './fields.mjs'
|
|
2
|
+
import { toolServiceName } from './service-names.mjs'
|
|
3
|
+
|
|
4
|
+
export const name = 'retrieval'
|
|
5
|
+
export const inject = ['tools']
|
|
6
|
+
export const configContract = defineConfig({
|
|
7
|
+
enabled: bool({ default: false }),
|
|
8
|
+
paths: stringList({ default: () => [], allowEmpty: true, dedupe: true }),
|
|
9
|
+
sessions: stringList({ default: () => [], allowEmpty: true, dedupe: true }),
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
export function apply(ctx, raw, options) {
|
|
13
|
+
const config = configContract.parse(raw, name)
|
|
14
|
+
if (!config.enabled) return
|
|
15
|
+
const fiber = ctx.inject([toolServiceName(name)], scopeCtx => scopeCtx.effect(() => scopeCtx.get(toolServiceName(name)).mount(scopeCtx, config, options)))
|
|
16
|
+
return () => { void fiber.dispose() }
|
|
17
|
+
}
|
package/engine/rule-engine.mjs
CHANGED
|
@@ -37,5 +37,6 @@ export async function apply(ctx, config) {
|
|
|
37
37
|
const moduleId = typeof packet.moduleId === 'string' ? packet.moduleId : basename(dirname(file))
|
|
38
38
|
const rules = compileRules(packet.rules, { moduleId, variables: packet.variables, variablesEnabled: packet.variablesEnabled, configOrder: packet.configOrder, promptConfigOptions: { templateBaseUrl, templateModuleRoot, strategyDir } })
|
|
39
39
|
// 条件在挂载缝绑定(preset 需要挂载 scope 的 ctx);官方模块导出只作可选兜底。
|
|
40
|
+
// 独立装配路径没有插件侧服务,manifest 条件在这个入口恒不可用(见 docs/engine-reuse.md)。
|
|
40
41
|
return mountRuleSources(ctx, [{ moduleId, rules }], { standingMountFor: await loadStandingMountFor() })
|
|
41
42
|
}
|
|
@@ -11,6 +11,11 @@ export interface RuleVariableStore {
|
|
|
11
11
|
publish(moduleId: string): void
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
/** 清单检查事实通道(宿主读取器);引擎只调用 read,undefined 表示不可用。 */
|
|
15
|
+
export interface RuleManifestCheck {
|
|
16
|
+
read(moduleId: string): boolean | undefined
|
|
17
|
+
}
|
|
18
|
+
|
|
14
19
|
/** 判定上报载荷;仅覆盖插件依赖面(宿主按 `RuleOutcomeReport` 消费它)。 */
|
|
15
20
|
export interface RuleOutcomeReport {
|
|
16
21
|
moduleId?: string
|
|
@@ -30,6 +35,8 @@ export interface MountRuleSourcesOptions {
|
|
|
30
35
|
plugin?: string
|
|
31
36
|
warnOnce?: WarnOnce
|
|
32
37
|
variableStore?: RuleVariableStore
|
|
38
|
+
/** 清单 checks 事实;缺省=manifest 条件恒不可用。 */
|
|
39
|
+
manifestCheck?: RuleManifestCheck
|
|
33
40
|
/** 官方预设兜底;缺省=没有兜底(预设条件恒不命中)。 */
|
|
34
41
|
standingMountFor?: StandingMountFor
|
|
35
42
|
/** 注入的晋升相位实例对;缺省从协调器或自建池取。 */
|
|
@@ -38,7 +45,7 @@ export interface MountRuleSourcesOptions {
|
|
|
38
45
|
onOutcome?(report: RuleOutcomeReport): void
|
|
39
46
|
}
|
|
40
47
|
|
|
41
|
-
/**
|
|
48
|
+
/** 所有规则来源的唯一装配入口;返回 disposer。可归因挂载错误携带 sourceModuleId,原错误保留在 cause。 */
|
|
42
49
|
export function mountRuleSources(
|
|
43
50
|
ctx: unknown,
|
|
44
51
|
sources: ReadonlyArray<RuleSourceLike>,
|
package/engine/rule-runtime.mjs
CHANGED
|
@@ -16,6 +16,8 @@ const noNext = new Set(['agent/inbox/inserted', 'agent/turn-stopping', 'subagent
|
|
|
16
16
|
const compare = (a, b) => a.action.channelOrder - b.action.channelOrder
|
|
17
17
|
|| a.moduleId.localeCompare(b.moduleId) || a.ruleIndex - b.ruleIndex || a.action.actionIndex - b.action.actionIndex
|
|
18
18
|
|
|
19
|
+
const sourceError = (error, moduleId) => Object.assign(new Error(String(error?.message ?? error), { cause: error }), { sourceModuleId: moduleId })
|
|
20
|
+
|
|
19
21
|
/** 所有来源只进入这个装配入口;受众限定固定效果要求 ctx 属于 options.agent 的 scope。 */
|
|
20
22
|
export function mountRuleSources(ctx, sources, options = {}) {
|
|
21
23
|
validateDeliverySources(sources)
|
|
@@ -32,7 +34,6 @@ export function mountRuleSources(ctx, sources, options = {}) {
|
|
|
32
34
|
// 条件绑定**在这里**发生:`compileRules` 编译期没有挂载 scope 的 ctx(preset 条件在那边恒
|
|
33
35
|
// UNAVAILABLE 且静默)。必须早于事件订阅与动作注册——动作级 `actionWhen.observe` 是订阅通道,
|
|
34
36
|
// 绑晚了那一类谓词会漏事件。`standingMountFor` 是可选兜底(官方包缺席=没有兜底,不是错误)。
|
|
35
|
-
rebindConditions(sources, { ctx, standingMountFor: options.standingMountFor, promotionPool: pool, variableStore: options.variableStore })
|
|
36
37
|
const warnOnce = options.warnOnce ?? createWarnOnce(ctx, options.plugin ?? 'rule-engine')
|
|
37
38
|
// 非 pre-step 动作仍共享一次 ruleFrame;规则实例没有来源字段(RuleDefinition 不暴露 moduleId),
|
|
38
39
|
// 因此保留一份局部 rule→module 映射。它不是 pre-step 来源事实的重建,不参与 pre-step 的 source sink。
|
|
@@ -50,19 +51,23 @@ export function mountRuleSources(ctx, sources, options = {}) {
|
|
|
50
51
|
// 因此按 moduleId 分档;来源 id 缺失时落空串兜底键(与 wireTurnStops 同源)。
|
|
51
52
|
const turnStopBudgets = new Map()
|
|
52
53
|
const injections = new Map(sources.map(source => [source.moduleId, { moduleId: source.moduleId, sourceId: moduleSourceId(source.moduleId), configs: [], ruleActions: [] }]))
|
|
53
|
-
const rules = sources.flatMap(source => source.rules.filter(rule => rule.enabled !== false))
|
|
54
54
|
// 动作级分支条件(actionWhen)也要喂 session/event:否则 else / 嵌套 if 里的 phase / count
|
|
55
55
|
// 谓词永远收不到事件,状态停在冷扫那一刻(规则级 when 已由 rules 覆盖,动作级单独补齐)。
|
|
56
|
-
const observer = wireTriggerObservers(ctx, [
|
|
57
|
-
...rules,
|
|
58
|
-
...rules.flatMap(rule => (rule.actions ?? [])
|
|
59
|
-
.filter(action => typeof action.actionWhen?.observe === 'function')
|
|
60
|
-
.map(action => ({ id: action.id, when: action.actionWhen }))),
|
|
61
|
-
], { plugin: options.plugin ?? 'rule-engine', warnOnce })
|
|
62
|
-
if (observer) releases.push(observer)
|
|
63
56
|
let active = true
|
|
64
57
|
const bind = (item, on) => prepareAction(item.action, { plugin: options.plugin, warnOnce, promptConfigOptions: item.rule.promptConfigOptions, on })(ctx)
|
|
65
58
|
try {
|
|
59
|
+
for (const source of sources) {
|
|
60
|
+
try { rebindConditions([source], { ctx, standingMountFor: options.standingMountFor, promotionPool: pool, variableStore: options.variableStore, manifestCheck: options.manifestCheck }) }
|
|
61
|
+
catch (error) { throw sourceError(error, source.moduleId) }
|
|
62
|
+
}
|
|
63
|
+
const rules = sources.flatMap(source => source.rules.filter(rule => rule.enabled !== false))
|
|
64
|
+
const observer = wireTriggerObservers(ctx, [
|
|
65
|
+
...rules,
|
|
66
|
+
...rules.flatMap(rule => (rule.actions ?? [])
|
|
67
|
+
.filter(action => typeof action.actionWhen?.observe === 'function')
|
|
68
|
+
.map(action => ({ id: action.id, when: action.actionWhen }))),
|
|
69
|
+
], { plugin: options.plugin ?? 'rule-engine', warnOnce })
|
|
70
|
+
if (observer) releases.push(observer)
|
|
66
71
|
for (const source of sources) for (const [ruleIndex, rule] of source.rules.entries()) {
|
|
67
72
|
if (rule.enabled === false) continue
|
|
68
73
|
for (const action of rule.actions) {
|
|
@@ -82,6 +87,7 @@ export function mountRuleSources(ctx, sources, options = {}) {
|
|
|
82
87
|
releases.push(wireLayers(ctx, textConfigs, warnOnce, {
|
|
83
88
|
agent: options.agent,
|
|
84
89
|
turnStopBudgets,
|
|
90
|
+
sourceError,
|
|
85
91
|
on: (channel, handler) => {
|
|
86
92
|
if (channel !== point.channel) throw new TypeError(`rule action registered outside ${point.channel}: ${channel}`)
|
|
87
93
|
handlers.push({ handler }); return () => {}
|
|
@@ -109,7 +115,9 @@ export function mountRuleSources(ctx, sources, options = {}) {
|
|
|
109
115
|
// 这里再返回一个真 disposer 会与它重复释放同一批 handler。
|
|
110
116
|
return () => {}
|
|
111
117
|
}
|
|
112
|
-
|
|
118
|
+
let dispose
|
|
119
|
+
try { dispose = bind(item, on) }
|
|
120
|
+
catch (error) { throw sourceError(error, moduleId) }
|
|
113
121
|
// 未命中通知:条件为假时动作不会进入 handler,撤销型动作(tool-visibility 的执行闸)
|
|
114
122
|
// 只能靠这条回调收手——否则第一轮为真后永久留闸。
|
|
115
123
|
if (typeof dispose?.miss === 'function') {
|
package/engine/rule-spec.mjs
CHANGED
|
@@ -231,8 +231,8 @@ export function compileRules(specs, options = {}) {
|
|
|
231
231
|
if (!Array.isArray(specs)) throw new TypeError('rules must be an array')
|
|
232
232
|
// 条件的绑定**不在编译期**:`preset` 谓词的取法要挂载 scope 的 ctx(`conditions/preset.mjs`),
|
|
233
233
|
// 准备期拿不到。这里显式剥掉挂载期选项——传了也不生效,免得调用方以为已经绑好。
|
|
234
|
-
// variableStore 与 ctx/standingMountFor 同理:绑定发生在挂载期,准备期传了也不生效。
|
|
235
|
-
const { ctx: _ctx, standingMountFor: _standingMountFor, variableStore: _variableStore, ...conditionOptions } = options
|
|
234
|
+
// variableStore / manifestCheck 与 ctx/standingMountFor 同理:绑定发生在挂载期,准备期传了也不生效。
|
|
235
|
+
const { ctx: _ctx, standingMountFor: _standingMountFor, variableStore: _variableStore, manifestCheck: _manifestCheck, ...conditionOptions } = options
|
|
236
236
|
const ids = new Set()
|
|
237
237
|
const pendingConfigs = []
|
|
238
238
|
const moduleId = options.moduleId ?? options.sourceModuleId
|
|
@@ -411,6 +411,8 @@ function needsMountContext(node) {
|
|
|
411
411
|
if (Object.hasOwn(node, 'preset')) return true
|
|
412
412
|
// variable 要读挂载 scope 的变量适配器(准备期没有)。
|
|
413
413
|
if (Object.hasOwn(node, 'variable')) return true
|
|
414
|
+
// manifest 要读挂载 scope 的清单检查服务(准备期没有)。
|
|
415
|
+
if (Object.hasOwn(node, 'manifest')) return true
|
|
414
416
|
if (Object.hasOwn(node, 'phase') && node.phase?.subscribe !== false) return true
|
|
415
417
|
if (node.anchor?.after !== undefined) return true
|
|
416
418
|
return Object.values(node).some(needsMountContext)
|
package/engine/schema.mjs
CHANGED
|
@@ -198,7 +198,7 @@ export const KNOWN_MODEL_SCOPES = new Set(['all', 'pro', 'flash'])
|
|
|
198
198
|
* 自定义策略或模板 patch 的角色仍由 executor 在出口校验,避免写出非法事件。
|
|
199
199
|
*/
|
|
200
200
|
export const KNOWN_ROLES = new Set(['user'])
|
|
201
|
-
export const KNOWN_FILLS = new Set(['instruction-hint', 'env-facts', 'skill-catalog', 'tool-catalog'])
|
|
201
|
+
export const KNOWN_FILLS = new Set(['instruction-hint', 'env-facts', 'skill-catalog', 'tool-catalog', 'soul-self'])
|
|
202
202
|
|
|
203
203
|
/** 层能力矩阵:每个字段只在对应注入层生效。客户端表单据此动态渲染。 */
|
|
204
204
|
export const LAYER_FIELD_POLICIES = Object.fromEntries(
|
|
@@ -5,6 +5,11 @@ export const PRE_STEP_COORDINATOR_SERVICE: string
|
|
|
5
5
|
|
|
6
6
|
/** 发现引擎注册表服务名:宿主 root provide,adapter 行按 agent 注册,动作按 agent 解析。 */
|
|
7
7
|
export const DISCOVERY_SERVICE: string
|
|
8
|
+
export const SOUL_SELF_SERVICE: string
|
|
9
|
+
export const SOUL_MAINTENANCE_SERVICE: string
|
|
10
|
+
|
|
11
|
+
/** 模块清单 checks 事实:宿主提供,宿主装配读取后经挂载 options 传给谓词。 */
|
|
12
|
+
export const MANIFEST_CHECK_SERVICE: 'promptToolManifestCheck'
|
|
8
13
|
|
|
9
14
|
/** 四个模块工具外壳的服务名:`pt-<引擎模块 id>`。前缀是本函数的内部细节。 */
|
|
10
15
|
export function toolServiceName(engineModuleId: string): string
|
package/engine/service-names.mjs
CHANGED
|
@@ -3,6 +3,11 @@ export const PRE_STEP_COORDINATOR_SERVICE = 'promptToolPreStep'
|
|
|
3
3
|
|
|
4
4
|
/** 发现引擎注册表:宿主 root provide,adapter 行按 agent 注册,动作按 agent 解析。 */
|
|
5
5
|
export const DISCOVERY_SERVICE = 'promptToolDiscovery'
|
|
6
|
+
export const SOUL_SELF_SERVICE = 'promptToolSoulSelf'
|
|
7
|
+
export const SOUL_MAINTENANCE_SERVICE = 'promptToolSoulMaintenance'
|
|
8
|
+
|
|
9
|
+
/** 模块清单 checks 事实:宿主提供,宿主装配读取后经挂载 options 传给谓词。 */
|
|
10
|
+
export const MANIFEST_CHECK_SERVICE = 'promptToolManifestCheck'
|
|
6
11
|
|
|
7
12
|
/** 五个模块工具外壳的服务名:`pt-<引擎模块 id>`。前缀是本函数的内部细节。 */
|
|
8
13
|
export function toolServiceName(engineModuleId) {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { toolServiceName } from './service-names.mjs'
|
|
2
|
+
|
|
3
|
+
export const name = 'soul-ledger'
|
|
4
|
+
export function apply(ctx) {
|
|
5
|
+
ctx.inject([toolServiceName(name)], scopeCtx => {
|
|
6
|
+
scopeCtx.effect(() => scopeCtx.get(toolServiceName(name)).mount(scopeCtx), name + ': mount')
|
|
7
|
+
})
|
|
8
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { bool, defineConfig, enumOf, passthrough } from './fields.mjs'
|
|
2
|
+
import { SOUL_MAINTENANCE_SERVICE } from './service-names.mjs'
|
|
3
|
+
|
|
4
|
+
export const name = 'soul-maintenance'
|
|
5
|
+
const bounded = (fallback, min, max) => passthrough((value = fallback, plugin, field) => {
|
|
6
|
+
if (!Number.isSafeInteger(value) || value < min || value > max) throw new TypeError(`${plugin}: ${field} must be an integer from ${min} to ${max}`)
|
|
7
|
+
return value
|
|
8
|
+
})
|
|
9
|
+
export const configContract = defineConfig({
|
|
10
|
+
enabled: bool({ default: false }), intervalMs: bounded(60000, 1000, 2147483647),
|
|
11
|
+
wake: bool({ default: false }), audience: enumOf(['all', 'main', 'subagent'], { default: 'all' }),
|
|
12
|
+
budgetBytes: bounded(12288, 1024, 65536),
|
|
13
|
+
})
|
|
14
|
+
export function apply(ctx, raw, options = {}) {
|
|
15
|
+
const config = configContract.parse(raw, name)
|
|
16
|
+
if (!config.enabled) return
|
|
17
|
+
ctx.inject([SOUL_MAINTENANCE_SERVICE], scopeCtx => {
|
|
18
|
+
scopeCtx.effect(() => scopeCtx.get(SOUL_MAINTENANCE_SERVICE).mount(scopeCtx, config, options), name + ': mount')
|
|
19
|
+
})
|
|
20
|
+
}
|
package/lib/client.js
CHANGED
|
@@ -742,7 +742,10 @@ window.__ModuleLoader__.load({
|
|
|
742
742
|
fileRead: "module_file_read",
|
|
743
743
|
fileAppend: "module_file_append",
|
|
744
744
|
manifest: "module_manifest",
|
|
745
|
-
variables: "module_variables"
|
|
745
|
+
variables: "module_variables",
|
|
746
|
+
soulLedger: "soul_ledger",
|
|
747
|
+
search: "module_search",
|
|
748
|
+
open: "module_open"
|
|
746
749
|
};
|
|
747
750
|
const MODULE_TOOL_CAPABILITIES = {
|
|
748
751
|
[MODULE_TOOL_NAMES.import]: "module-import-tools",
|
|
@@ -751,7 +754,10 @@ window.__ModuleLoader__.load({
|
|
|
751
754
|
[MODULE_TOOL_NAMES.fileRead]: "module-file-tools",
|
|
752
755
|
[MODULE_TOOL_NAMES.fileAppend]: "module-file-tools",
|
|
753
756
|
[MODULE_TOOL_NAMES.manifest]: "module-manifest-tools",
|
|
754
|
-
[MODULE_TOOL_NAMES.variables]: "module-variable-tools"
|
|
757
|
+
[MODULE_TOOL_NAMES.variables]: "module-variable-tools",
|
|
758
|
+
[MODULE_TOOL_NAMES.soulLedger]: "soul-ledger",
|
|
759
|
+
[MODULE_TOOL_NAMES.search]: "retrieval",
|
|
760
|
+
[MODULE_TOOL_NAMES.open]: "retrieval"
|
|
755
761
|
};
|
|
756
762
|
/** 旧公开类型名只作输入兼容。 */
|
|
757
763
|
/**
|
|
@@ -10469,6 +10475,7 @@ window.__ModuleLoader__.load({
|
|
|
10469
10475
|
"triggers.label.variable": "变量状态",
|
|
10470
10476
|
"triggers.label.op": "比较方式",
|
|
10471
10477
|
"triggers.label.value": "比较值",
|
|
10478
|
+
"triggers.label.manifest": "清单检查",
|
|
10472
10479
|
"triggers.label.after": "限定在动作投递之后",
|
|
10473
10480
|
"triggers.label.afterAction": "起点动作标识",
|
|
10474
10481
|
"triggers.option.userMessage": "用户消息",
|
|
@@ -10494,6 +10501,7 @@ window.__ModuleLoader__.load({
|
|
|
10494
10501
|
"triggers.backToSettings": "返回本层参数",
|
|
10495
10502
|
"triggers.label.text": "文本",
|
|
10496
10503
|
"triggers.label.phase": "阶段",
|
|
10504
|
+
"triggers.label.ok": "检查通过",
|
|
10497
10505
|
"triggers.label.source": "消息来源",
|
|
10498
10506
|
"triggers.label.count": "事件计数",
|
|
10499
10507
|
"triggers.label.names": "名称名单",
|
|
@@ -10549,7 +10557,7 @@ window.__ModuleLoader__.load({
|
|
|
10549
10557
|
"triggers.label.allowFrom": "按需解锁",
|
|
10550
10558
|
"triggers.label.tool": "发现工具",
|
|
10551
10559
|
"triggers.label.key": "键名",
|
|
10552
|
-
"triggers.label.requireMatch": "
|
|
10560
|
+
"triggers.label.requireMatch": "名单工具全部缺失时保留完整目录",
|
|
10553
10561
|
"triggers.label.keep": "仅保留",
|
|
10554
10562
|
"triggers.label.remove": "移除",
|
|
10555
10563
|
"triggers.label.clear": "清空全部",
|
|
@@ -10693,6 +10701,7 @@ window.__ModuleLoader__.load({
|
|
|
10693
10701
|
"triggers.label.variable": "Variable state",
|
|
10694
10702
|
"triggers.label.op": "Comparison",
|
|
10695
10703
|
"triggers.label.value": "Comparison value",
|
|
10704
|
+
"triggers.label.manifest": "Manifest check",
|
|
10696
10705
|
"triggers.label.after": "Observe after action delivery",
|
|
10697
10706
|
"triggers.label.afterAction": "Starting action ID",
|
|
10698
10707
|
"triggers.option.userMessage": "User message",
|
|
@@ -10718,6 +10727,7 @@ window.__ModuleLoader__.load({
|
|
|
10718
10727
|
"triggers.backToSettings": "Back to layer parameters",
|
|
10719
10728
|
"triggers.label.text": "Text",
|
|
10720
10729
|
"triggers.label.phase": "Phase",
|
|
10730
|
+
"triggers.label.ok": "Checks passed",
|
|
10721
10731
|
"triggers.label.source": "Message source",
|
|
10722
10732
|
"triggers.label.count": "Event count",
|
|
10723
10733
|
"triggers.label.names": "Name list",
|
|
@@ -10773,7 +10783,7 @@ window.__ModuleLoader__.load({
|
|
|
10773
10783
|
"triggers.label.allowFrom": "On-demand unlocking",
|
|
10774
10784
|
"triggers.label.tool": "Discovery tool",
|
|
10775
10785
|
"triggers.label.key": "Key",
|
|
10776
|
-
"triggers.label.requireMatch": "Keep the full catalog if
|
|
10786
|
+
"triggers.label.requireMatch": "Keep the full catalog only if no listed tool exists",
|
|
10777
10787
|
"triggers.label.keep": "Keep only",
|
|
10778
10788
|
"triggers.label.remove": "Remove",
|
|
10779
10789
|
"triggers.label.clear": "Clear all",
|
|
@@ -18457,7 +18467,7 @@ window.__ModuleLoader__.load({
|
|
|
18457
18467
|
"rules.surface.dynamic": "按需解锁",
|
|
18458
18468
|
"rules.surface.discoveryTool": "发现工具",
|
|
18459
18469
|
"rules.surface.argumentKey": "名单参数",
|
|
18460
|
-
"rules.surface.requireMatch": "
|
|
18470
|
+
"rules.surface.requireMatch": "工具全部缺失时保留全部",
|
|
18461
18471
|
"rules.surface.textMode": "过滤方式",
|
|
18462
18472
|
"rules.surface.unrestricted": "不过滤",
|
|
18463
18473
|
"rules.surface.keep": "仅保留",
|
|
@@ -18582,7 +18592,7 @@ window.__ModuleLoader__.load({
|
|
|
18582
18592
|
"rules.surface.dynamic": "Unlock on demand",
|
|
18583
18593
|
"rules.surface.discoveryTool": "Discovery tool",
|
|
18584
18594
|
"rules.surface.argumentKey": "List argument",
|
|
18585
|
-
"rules.surface.requireMatch": "Keep all if
|
|
18595
|
+
"rules.surface.requireMatch": "Keep all only if no tool exists",
|
|
18586
18596
|
"rules.surface.textMode": "Filter",
|
|
18587
18597
|
"rules.surface.unrestricted": "No filter",
|
|
18588
18598
|
"rules.surface.keep": "Keep only",
|