clearai-dsh 0.1.7 → 0.2.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/CHANGELOG.md +32 -0
- package/README.md +66 -77
- package/README.zh-CN.md +60 -71
- package/lib/client.js +498 -69
- package/lib/domain-language.js +740 -0
- package/lib/fold.js +185 -7
- package/lib/host.js +71 -1
- package/package.json +1 -1
- package/presets/clearai/agent.cordis.yml +3 -3
- package/presets/clearai/plugins/clearai-kernel.js +430 -6
- package/presets/clearai/plugins/prompts.js +32 -0
|
@@ -401,7 +401,22 @@ export const HUMAN_GATE_MARK = '[clearai·人门]'
|
|
|
401
401
|
* 两侧白名单一旦各自维护,摘动词时只改一侧 ⇒ 内核仍认、宿主不认 ⇒ **等价性用例立刻红** ✓。
|
|
402
402
|
* 这正是那条用例存在的意义:两份实现漂移不许静默。
|
|
403
403
|
*/
|
|
404
|
-
|
|
404
|
+
/**
|
|
405
|
+
* **逐字镜像 `ui/lib/fold.js` 的同名清单**(预设面不能 import 宿主半,于是只能两份;
|
|
406
|
+
* 两份相等由 `test/authority-boundary.test.mjs` 钉死——它红的时候,是清单漂了,不是测试坏了)。
|
|
407
|
+
*/
|
|
408
|
+
export const HUMAN_GATE_ACTIONS = [
|
|
409
|
+
'adopt_branch',
|
|
410
|
+
'abandon_fork',
|
|
411
|
+
'promote_skill',
|
|
412
|
+
'retract_fact',
|
|
413
|
+
'keep_fact',
|
|
414
|
+
'confirm_provisional',
|
|
415
|
+
'register_term',
|
|
416
|
+
'register_predicate',
|
|
417
|
+
'revise_term',
|
|
418
|
+
'deprecate_entry',
|
|
419
|
+
]
|
|
405
420
|
export function parseHumanGateMessage(message) {
|
|
406
421
|
if (message === null || typeof message !== 'object') return null
|
|
407
422
|
// 署名必须是人:插件与模型来源的同名标记不算人门动作(与宿主半同一条纪律)。
|
|
@@ -478,6 +493,8 @@ export const MECHANISM_TOOLS = {
|
|
|
478
493
|
brain: ['SaveSkill', 'WriteMemory'],
|
|
479
494
|
/** 账本两件:它长在 git 上(A 层用工作区自己的仓库,B 层用数据区的旁路账本,与世界线共用一本)。 */
|
|
480
495
|
ledger: ['FileHistory', 'RestoreFile'],
|
|
496
|
+
/** 领域语言:七个动词 = 词汇的全部写入口(注册/修订/废止) + 一个读入口(按概念取已知)。 */
|
|
497
|
+
ontology: ['RegisterTerm', 'RegisterPredicate', 'ReviseTerm', 'RevisePredicate', 'DeprecateTerm', 'DeprecatePredicate', 'QueryKnowledge'],
|
|
481
498
|
}
|
|
482
499
|
const TOOL_CATALOG = new Set(Object.values(MECHANISM_TOOLS).flat())
|
|
483
500
|
|
|
@@ -2557,7 +2574,7 @@ export function apply(ctx, config = {}) {
|
|
|
2557
2574
|
const facts = Array.isArray(state?.facts) ? state.facts : []
|
|
2558
2575
|
if (facts.length === 0) return null
|
|
2559
2576
|
const lines = [
|
|
2560
|
-
'#
|
|
2577
|
+
'# 本体内容(已确立条目,可作为「已知」引用;未结构化的旧条目照旧在架)',
|
|
2561
2578
|
'',
|
|
2562
2579
|
'> 每条都带**边界**(推翻条件)与**支持等级**:引用它之前先看边界还在不在。',
|
|
2563
2580
|
'> 这一份由系统维护(做的人不能写 `clear/knowledge/facts`);新的在前。',
|
|
@@ -2605,6 +2622,59 @@ export function apply(ctx, config = {}) {
|
|
|
2605
2622
|
}
|
|
2606
2623
|
}
|
|
2607
2624
|
|
|
2625
|
+
/**
|
|
2626
|
+
* **领域词汇货架**:把折出来的词汇落成 `clear/ontology/domain.md`。
|
|
2627
|
+
*
|
|
2628
|
+
* 与过程本体货架同一条纪律(读面 + 幂等),但**触发时机不同**:过程本体随发布版本,
|
|
2629
|
+
* 一个会话铺一次就够;词汇每一步都可能变,所以每个本体动词落账之后都要重铺一遍——
|
|
2630
|
+
* 内容没变就不重写(文件时间戳是给人的读数,不是噪声)。
|
|
2631
|
+
*
|
|
2632
|
+
* 带 `mutations` 时按**这一步之后**的样子渲染:工具返回前货架就已更新,读的人不必等下一回合。
|
|
2633
|
+
*/
|
|
2634
|
+
function ensureDomainShelf(hostService, sessionId, mutations = []) {
|
|
2635
|
+
if (hostService?.domain?.renderShelf === undefined) return ''
|
|
2636
|
+
try {
|
|
2637
|
+
const body = hostService.domain.renderShelf(sessionId, Array.isArray(mutations) ? mutations : [])
|
|
2638
|
+
const file = join(sessionCwd(sessionId), 'clear', 'ontology', 'domain.md')
|
|
2639
|
+
if (existsSync(file) && readFileSync(file, 'utf8') === body) return ''
|
|
2640
|
+
writeTextFile(file, body)
|
|
2641
|
+
return `\n词汇货架已更新:${join('clear', 'ontology', 'domain.md')}(概念 / 谓词 / 图 / 引用)。**它是读面,不是权威**——要改词汇就调注册 / 修订 / 废止动词。`
|
|
2642
|
+
} catch (error) {
|
|
2643
|
+
ctx.logger?.warn?.(`clearai domain shelf: 写入失败 ${String(error?.message ?? error).slice(0, 160)}`)
|
|
2644
|
+
return ''
|
|
2645
|
+
}
|
|
2646
|
+
}
|
|
2647
|
+
|
|
2648
|
+
/**
|
|
2649
|
+
* 领域判据的宿主入口。**拿不到就明确拒,不抛**:预设与宿主半同包同版本,
|
|
2650
|
+
* 但一个缺了这道门的宿主(旧包、裁剪过的部署、测试桩)不该让工具在 `undefined` 上崩——
|
|
2651
|
+
* 崩掉会让模型以为是自己的参数错了,而真实原因是这一层没接上。
|
|
2652
|
+
*/
|
|
2653
|
+
function domainJudge(hostService) {
|
|
2654
|
+
return hostService?.domain ?? null
|
|
2655
|
+
}
|
|
2656
|
+
|
|
2657
|
+
/** 在折出来的词汇里找一个条目(概念或谓词)。判据与折法同源:读的就是 `state.lexicon`。 */
|
|
2658
|
+
function findLexiconEntry(state, id) {
|
|
2659
|
+
const lexicon = state?.lexicon ?? {}
|
|
2660
|
+
const wanted = String(id ?? '').trim()
|
|
2661
|
+
if (wanted === '') return null
|
|
2662
|
+
const term = (Array.isArray(lexicon.terms) ? lexicon.terms : []).find((item) => item.id === wanted)
|
|
2663
|
+
if (term !== undefined) return { kind: 'term', entry: term }
|
|
2664
|
+
const predicate = (Array.isArray(lexicon.predicates) ? lexicon.predicates : []).find((item) => item.id === wanted)
|
|
2665
|
+
if (predicate !== undefined) return { kind: 'predicate', entry: predicate }
|
|
2666
|
+
return null
|
|
2667
|
+
}
|
|
2668
|
+
|
|
2669
|
+
/** 查询条件的一行人话:让「查不到」也说得清查的是什么。 */
|
|
2670
|
+
function describeFilter(filter) {
|
|
2671
|
+
const parts = []
|
|
2672
|
+
if (filter.term !== '') parts.push(`概念「${filter.term}」`)
|
|
2673
|
+
if (filter.predicate !== '') parts.push(`谓词「${filter.predicate}」`)
|
|
2674
|
+
if (filter.subject !== '') parts.push(`主体「${filter.subject}」`)
|
|
2675
|
+
return `查询:${parts.join(' · ')}`
|
|
2676
|
+
}
|
|
2677
|
+
|
|
2608
2678
|
/**
|
|
2609
2679
|
* **本体货架**:把已装的那份本体落成 `clear/ontology/<id>.md`。
|
|
2610
2680
|
*
|
|
@@ -2685,10 +2755,34 @@ export function apply(ctx, config = {}) {
|
|
|
2685
2755
|
promote_at_level: { type: 'string', enum: LEVELS, description: '升格门槛(默认 L3)' },
|
|
2686
2756
|
hypotheses: {
|
|
2687
2757
|
type: 'array',
|
|
2688
|
-
description:
|
|
2758
|
+
description:
|
|
2759
|
+
'候选假设:每条一句话主张 + 一句推翻条件;可带**类型化断言**(可选,提供即严校:谓词与概念必须已登记、宾语形态要合值域、同一事实里不许自相矛盾)。不写断言照旧成立——断言是加法,不是门槛。',
|
|
2689
2760
|
items: {
|
|
2690
2761
|
type: 'object',
|
|
2691
|
-
properties: {
|
|
2762
|
+
properties: {
|
|
2763
|
+
claim: { type: 'string' },
|
|
2764
|
+
refute_when: { type: 'string' },
|
|
2765
|
+
assertions: {
|
|
2766
|
+
type: 'array',
|
|
2767
|
+
description: '断言:主词–谓词–宾语(引用领域词汇里的 id)',
|
|
2768
|
+
items: {
|
|
2769
|
+
type: 'object',
|
|
2770
|
+
properties: {
|
|
2771
|
+
predicate: { type: 'string' },
|
|
2772
|
+
subject: { type: 'object', properties: { id: { type: 'string' }, type: { type: 'string' } }, required: ['id'], additionalProperties: false },
|
|
2773
|
+
object: {
|
|
2774
|
+
type: 'object',
|
|
2775
|
+
properties: { kind: { type: 'string', enum: ['statement', 'quantity', 'formula', 'code', 'reference', 'instance'] }, value: {}, unit: { type: 'string' }, type: { type: 'string' } },
|
|
2776
|
+
required: ['kind'],
|
|
2777
|
+
additionalProperties: false,
|
|
2778
|
+
},
|
|
2779
|
+
qualifiers: { type: 'object' },
|
|
2780
|
+
},
|
|
2781
|
+
required: ['predicate', 'subject', 'object'],
|
|
2782
|
+
additionalProperties: false,
|
|
2783
|
+
},
|
|
2784
|
+
},
|
|
2785
|
+
},
|
|
2692
2786
|
required: ['claim', 'refute_when'],
|
|
2693
2787
|
additionalProperties: false,
|
|
2694
2788
|
},
|
|
@@ -2714,6 +2808,19 @@ export function apply(ctx, config = {}) {
|
|
|
2714
2808
|
for (const hypothesis of hypotheses) {
|
|
2715
2809
|
if (typeof hypothesis?.claim !== 'string' || hypothesis.claim.trim() === '') return fail('hypothesis_claim_required', '每条假设要有一句话主张。')
|
|
2716
2810
|
if (typeof hypothesis?.refute_when !== 'string' || hypothesis.refute_when.trim() === '') return fail('hypothesis_refute_required', '每条假设必须写清「什么结果会推翻它」——没有推翻条件的假设无法被检验。')
|
|
2811
|
+
/**
|
|
2812
|
+
* **宽松+校验**:不写断言放行(断言是加法),写了就在**落账之前**严校——
|
|
2813
|
+
* 引用不存在的谓词 / 概念、宾语形态不合值域、同一事实自相矛盾,一律当场拒。
|
|
2814
|
+
* 判据来自宿主半(`hostService.domain`),与折法和读面同源。
|
|
2815
|
+
*/
|
|
2816
|
+
if (hypothesis.assertions !== undefined && hypothesis.assertions !== null) {
|
|
2817
|
+
const judge = domainJudge(hostService)
|
|
2818
|
+
if (judge === null) return fail('domain_unavailable', '这一层的宿主没有提供领域判据(domain facade):无法校验断言。请检查宿主半与预设是否同版本。')
|
|
2819
|
+
const problems = judge.validateAssertions(sessionId, hypothesis.assertions)
|
|
2820
|
+
if (problems.length > 0) {
|
|
2821
|
+
return fail('assertions_rejected', `这条假设的断言不能成立(先注册词汇,或改断言):\n${problems.map((item) => `- ${item}`).join('\n')}`)
|
|
2822
|
+
}
|
|
2823
|
+
}
|
|
2717
2824
|
}
|
|
2718
2825
|
// 假设数量下限:首次立目标就得带够候选——0 条一样拦(候选对比是检验的前提,
|
|
2719
2826
|
// 只有一个猜想时「验证」容易退化成找证据支持自己)。修订不受此限。
|
|
@@ -2739,6 +2846,8 @@ export function apply(ctx, config = {}) {
|
|
|
2739
2846
|
id: `h-${Math.random().toString(36).slice(2, 8)}`,
|
|
2740
2847
|
claim: hypothesis.claim.trim(),
|
|
2741
2848
|
refute_when: hypothesis.refute_when.trim(),
|
|
2849
|
+
/** 断言随假设落账;没写就是 null(加法,不是门槛)。 */
|
|
2850
|
+
assertions: Array.isArray(hypothesis.assertions) ? hypothesis.assertions : null,
|
|
2742
2851
|
version: index + 1,
|
|
2743
2852
|
})),
|
|
2744
2853
|
})
|
|
@@ -2909,10 +3018,17 @@ export function apply(ctx, config = {}) {
|
|
|
2909
3018
|
t: 'fact/promoted',
|
|
2910
3019
|
id: factId,
|
|
2911
3020
|
goal: goal.id,
|
|
3021
|
+
/**
|
|
3022
|
+
* **身份与内容一起定型**:`hypothesis` 是产出它的那条假设(按 id 关联,
|
|
3023
|
+
* 措辞改了也认得出),`assertions` 是这条事实的类型化内容(没写就是 null)。
|
|
3024
|
+
* 断言只在**升格这一刻**落地——旧事实不会被回溯改写。
|
|
3025
|
+
*/
|
|
3026
|
+
hypothesis: hypothesis.id,
|
|
2912
3027
|
text: hypothesis.claim,
|
|
2913
3028
|
scope: hypothesis.refute_when ?? null,
|
|
2914
3029
|
level: hypothesis.supportedLevel ?? null,
|
|
2915
3030
|
evidence: state.evidence.filter((item) => item.verdict === 'support').map((item) => item.id),
|
|
3031
|
+
assertions: Array.isArray(hypothesis.assertions) ? hypothesis.assertions : null,
|
|
2916
3032
|
path,
|
|
2917
3033
|
})
|
|
2918
3034
|
promoted.push(hypothesis.claim)
|
|
@@ -3007,6 +3123,301 @@ export function apply(ctx, config = {}) {
|
|
|
3007
3123
|
additionalProperties: false,
|
|
3008
3124
|
}
|
|
3009
3125
|
|
|
3126
|
+
|
|
3127
|
+
// ── 领域语言(本体动词)─────────────────────────────────────────────────
|
|
3128
|
+
/**
|
|
3129
|
+
* 七个动词 = 领域词汇的**全部写入口**(注册 / 修订 / 废止,概念与谓词各一套)+ 一个读入口。
|
|
3130
|
+
*
|
|
3131
|
+
* 它们只做一件事:把「这个领域有哪些概念、哪些谓词、关系取什么值形态」写进账本事件,
|
|
3132
|
+
* 由折法折成 `state.lexicon`;再由同一份折法长出本体图、冲突读数与货架。
|
|
3133
|
+
*
|
|
3134
|
+
* 四条贯穿所有动词的纪律:
|
|
3135
|
+
* · **判据只有一份**——校验经 `hostService.domain.*`(与折法、读面同源),预设侧不复制规则;
|
|
3136
|
+
* · **依据必填**——约定可以自愿,不能无来由;
|
|
3137
|
+
* · **没有删除**——修订留版本,废止留缘由且是黏性终态;
|
|
3138
|
+
* · **语义变化必须换 id**——改展示信息走修订,改含义/主词域/值域走「废止 + 新注册」。
|
|
3139
|
+
*/
|
|
3140
|
+
defineTool({
|
|
3141
|
+
name: 'RegisterTerm',
|
|
3142
|
+
description:
|
|
3143
|
+
'登记一个领域概念(本体图上的节点)。id 用小写 slug;`gloss` 一句话说清它指什么;`basis` 必填——哪份材料、哪条事实或人说的哪句话让这个词成立。`parent` 可选(is_a,只能连概念)。登记是**约定**不是主张:它不需要证据等级,但从此可以出现在断言里。要改展示信息用 ReviseTerm,要作废用 DeprecateTerm(记录不会删)。',
|
|
3144
|
+
parameters: {
|
|
3145
|
+
type: 'object',
|
|
3146
|
+
properties: {
|
|
3147
|
+
id: { type: 'string', description: '小写字母开头的 slug(字母/数字/下划线,≤40)' },
|
|
3148
|
+
label: { type: 'string', description: '给人看的名字' },
|
|
3149
|
+
gloss: { type: 'string', description: '一句话释义' },
|
|
3150
|
+
aliases: { type: 'array', items: { type: 'string' }, description: '别名(可选)' },
|
|
3151
|
+
parent: { type: 'string', description: '父概念 id(可选,is_a)' },
|
|
3152
|
+
basis: { type: 'string', description: '依据:哪份材料 / 哪条事实 / 谁说的' },
|
|
3153
|
+
},
|
|
3154
|
+
required: ['id', 'label', 'gloss', 'basis'],
|
|
3155
|
+
additionalProperties: false,
|
|
3156
|
+
},
|
|
3157
|
+
output: CARD_OUTPUT,
|
|
3158
|
+
async execute(args, exec) {
|
|
3159
|
+
const call = open(exec)
|
|
3160
|
+
if (call.ok !== true) return call.response
|
|
3161
|
+
const { hostService, sessionId, mutations } = call
|
|
3162
|
+
const done = finish(hostService, sessionId, mutations)
|
|
3163
|
+
const judge = domainJudge(hostService)
|
|
3164
|
+
if (judge === null) return fail('domain_unavailable', '这一层的宿主没有提供领域判据(domain facade):无法校验词汇。请检查宿主半与预设是否同版本。')
|
|
3165
|
+
const problems = judge.validateTerm(sessionId, args)
|
|
3166
|
+
if (problems.length > 0) return fail('term_rejected', `这个词不能登记:\n${problems.map((item) => `- ${item}`).join('\n')}`)
|
|
3167
|
+
mutations.push({
|
|
3168
|
+
t: 'ontology/term_added',
|
|
3169
|
+
id: String(args.id).trim(),
|
|
3170
|
+
label: String(args.label).trim(),
|
|
3171
|
+
gloss: String(args.gloss).trim(),
|
|
3172
|
+
aliases: Array.isArray(args.aliases) ? args.aliases.map((alias) => String(alias)) : [],
|
|
3173
|
+
parent: args.parent === undefined ? null : String(args.parent).trim(),
|
|
3174
|
+
basis: String(args.basis).trim(),
|
|
3175
|
+
})
|
|
3176
|
+
const note = ensureDomainShelf(hostService, sessionId, mutations)
|
|
3177
|
+
return done({
|
|
3178
|
+
ok: true,
|
|
3179
|
+
code: 'term_registered',
|
|
3180
|
+
message: `概念 ${String(args.id).trim()} 已登记。它现在是本体图上的一个节点:新断言可以引用它。${note}`,
|
|
3181
|
+
})
|
|
3182
|
+
},
|
|
3183
|
+
})
|
|
3184
|
+
|
|
3185
|
+
defineTool({
|
|
3186
|
+
name: 'RegisterPredicate',
|
|
3187
|
+
description:
|
|
3188
|
+
'登记一个领域谓词(本体图上的边)。值域二选一:`range={form:"quantity"|"statement"|"formula"|"code"|"reference",unit?}` 说宾语是一个**字面值**,或 `range={term:"<概念 id>"}` 说宾语是**另一个概念的实例**。`domain` 可选(主词域,声明了它,断言的主体就必须写明类型)。`functional=true` 表示单值:同一主体出现两个不同取值时,投影会给出一对**冲突**(只暴露,不裁决)。',
|
|
3189
|
+
parameters: {
|
|
3190
|
+
type: 'object',
|
|
3191
|
+
properties: {
|
|
3192
|
+
id: { type: 'string', description: '小写字母开头的 slug' },
|
|
3193
|
+
label: { type: 'string', description: '给人看的名字' },
|
|
3194
|
+
gloss: { type: 'string', description: '一句话释义(可选)' },
|
|
3195
|
+
domain: { type: 'string', description: '主词域:概念 id(可选)' },
|
|
3196
|
+
range: {
|
|
3197
|
+
type: 'object',
|
|
3198
|
+
description: '值域:{form,unit?} 或 {term}',
|
|
3199
|
+
properties: {
|
|
3200
|
+
form: { type: 'string', enum: ['statement', 'quantity', 'formula', 'code', 'reference'] },
|
|
3201
|
+
unit: { type: 'string' },
|
|
3202
|
+
term: { type: 'string' },
|
|
3203
|
+
},
|
|
3204
|
+
additionalProperties: false,
|
|
3205
|
+
},
|
|
3206
|
+
functional: { type: 'boolean', description: '是否单值(默认否)' },
|
|
3207
|
+
basis: { type: 'string', description: '依据(必填)' },
|
|
3208
|
+
},
|
|
3209
|
+
required: ['id', 'label', 'range', 'basis'],
|
|
3210
|
+
additionalProperties: false,
|
|
3211
|
+
},
|
|
3212
|
+
output: CARD_OUTPUT,
|
|
3213
|
+
async execute(args, exec) {
|
|
3214
|
+
const call = open(exec)
|
|
3215
|
+
if (call.ok !== true) return call.response
|
|
3216
|
+
const { hostService, sessionId, mutations } = call
|
|
3217
|
+
const done = finish(hostService, sessionId, mutations)
|
|
3218
|
+
const judge = domainJudge(hostService)
|
|
3219
|
+
if (judge === null) return fail('domain_unavailable', '这一层的宿主没有提供领域判据(domain facade):无法校验词汇。请检查宿主半与预设是否同版本。')
|
|
3220
|
+
const problems = judge.validatePredicate(sessionId, args)
|
|
3221
|
+
if (problems.length > 0) return fail('predicate_rejected', `这个谓词不能登记:\n${problems.map((item) => `- ${item}`).join('\n')}`)
|
|
3222
|
+
mutations.push({
|
|
3223
|
+
t: 'ontology/predicate_added',
|
|
3224
|
+
id: String(args.id).trim(),
|
|
3225
|
+
label: String(args.label).trim(),
|
|
3226
|
+
gloss: args.gloss === undefined ? '' : String(args.gloss).trim(),
|
|
3227
|
+
domain: args.domain === undefined ? null : String(args.domain).trim(),
|
|
3228
|
+
range: args.range,
|
|
3229
|
+
functional: args.functional === true,
|
|
3230
|
+
basis: String(args.basis).trim(),
|
|
3231
|
+
})
|
|
3232
|
+
const note = ensureDomainShelf(hostService, sessionId, mutations)
|
|
3233
|
+
return done({
|
|
3234
|
+
ok: true,
|
|
3235
|
+
code: 'predicate_registered',
|
|
3236
|
+
message: `谓词 ${String(args.id).trim()} 已登记(${args.range?.term ? `宾语是概念 ${args.range.term} 的实例` : `宾语取 ${args.range?.form} 形态`}${args.functional === true ? ' · 单值' : ''})。下一步:在 SetGoal 的假设里带上断言,引用它。${note}`,
|
|
3237
|
+
})
|
|
3238
|
+
},
|
|
3239
|
+
})
|
|
3240
|
+
|
|
3241
|
+
defineTool({
|
|
3242
|
+
name: 'ReviseTerm',
|
|
3243
|
+
description:
|
|
3244
|
+
'修订一个概念的**展示信息**(名字 / 释义 / 别名):版本 +1,旧值留在账上,id 不变。**语义变化不许走这条路**——含义、父概念变了就废止旧条目、注册新条目;稳定 id 的含义在历史上悄悄改变,等于拿今天的释义重写所有旧事实。',
|
|
3245
|
+
parameters: {
|
|
3246
|
+
type: 'object',
|
|
3247
|
+
properties: {
|
|
3248
|
+
id: { type: 'string' },
|
|
3249
|
+
label: { type: 'string', description: '新名字(可选)' },
|
|
3250
|
+
gloss: { type: 'string', description: '新释义(可选)' },
|
|
3251
|
+
aliases: { type: 'array', items: { type: 'string' } },
|
|
3252
|
+
reason: { type: 'string', description: '为什么改(必填)' },
|
|
3253
|
+
},
|
|
3254
|
+
required: ['id', 'reason'],
|
|
3255
|
+
additionalProperties: false,
|
|
3256
|
+
},
|
|
3257
|
+
output: CARD_OUTPUT,
|
|
3258
|
+
async execute(args, exec) {
|
|
3259
|
+
const call = open(exec)
|
|
3260
|
+
if (call.ok !== true) return call.response
|
|
3261
|
+
const { hostService, sessionId, state, mutations } = call
|
|
3262
|
+
const done = finish(hostService, sessionId, mutations)
|
|
3263
|
+
const entry = findLexiconEntry(state, args.id)
|
|
3264
|
+
if (entry === null || entry.kind !== 'term') return fail('unknown_term', `词汇里没有这个概念:${String(args.id)}`)
|
|
3265
|
+
if (entry.entry.status === 'deprecated') return fail('term_deprecated', `概念 ${String(args.id)} 已废止,不能修订(要恢复语义就注册新条目)。`)
|
|
3266
|
+
if (typeof args.reason !== 'string' || args.reason.trim() === '') return fail('reason_required', '修订要写一句原因:改了什么、为什么改。')
|
|
3267
|
+
if (args.label === undefined && args.gloss === undefined && args.aliases === undefined) return fail('nothing_to_revise', '没有要改的字段:label / gloss / aliases 至少给一个。')
|
|
3268
|
+
mutations.push({ t: 'ontology/term_revised', id: String(args.id), label: args.label === undefined ? undefined : String(args.label).trim(), gloss: args.gloss === undefined ? undefined : String(args.gloss).trim(), aliases: Array.isArray(args.aliases) ? args.aliases.map((alias) => String(alias)) : undefined, reason: String(args.reason).trim() })
|
|
3269
|
+
const note = ensureDomainShelf(hostService, sessionId, mutations)
|
|
3270
|
+
return done({ ok: true, code: 'term_revised', message: `概念 ${String(args.id)} 已修订(旧值留在账上)。${note}` })
|
|
3271
|
+
},
|
|
3272
|
+
})
|
|
3273
|
+
|
|
3274
|
+
defineTool({
|
|
3275
|
+
name: 'RevisePredicate',
|
|
3276
|
+
description: '修订一个谓词的**展示信息**(名字 / 释义)。值域、主词域与单值性是语义——那三样变了要废止旧谓词、注册新的。',
|
|
3277
|
+
parameters: {
|
|
3278
|
+
type: 'object',
|
|
3279
|
+
properties: { id: { type: 'string' }, label: { type: 'string' }, gloss: { type: 'string' }, reason: { type: 'string' } },
|
|
3280
|
+
required: ['id', 'reason'],
|
|
3281
|
+
additionalProperties: false,
|
|
3282
|
+
},
|
|
3283
|
+
output: CARD_OUTPUT,
|
|
3284
|
+
async execute(args, exec) {
|
|
3285
|
+
const call = open(exec)
|
|
3286
|
+
if (call.ok !== true) return call.response
|
|
3287
|
+
const { hostService, sessionId, state, mutations } = call
|
|
3288
|
+
const done = finish(hostService, sessionId, mutations)
|
|
3289
|
+
const entry = findLexiconEntry(state, args.id)
|
|
3290
|
+
if (entry === null || entry.kind !== 'predicate') return fail('unknown_predicate', `词汇里没有这个谓词:${String(args.id)}`)
|
|
3291
|
+
if (entry.entry.status === 'deprecated') return fail('predicate_deprecated', `谓词 ${String(args.id)} 已废止,不能修订。`)
|
|
3292
|
+
if (typeof args.reason !== 'string' || args.reason.trim() === '') return fail('reason_required', '修订要写一句原因。')
|
|
3293
|
+
mutations.push({ t: 'ontology/predicate_revised', id: String(args.id), label: args.label === undefined ? undefined : String(args.label).trim(), gloss: args.gloss === undefined ? undefined : String(args.gloss).trim(), reason: String(args.reason).trim() })
|
|
3294
|
+
const note = ensureDomainShelf(hostService, sessionId, mutations)
|
|
3295
|
+
return done({ ok: true, code: 'predicate_revised', message: `谓词 ${String(args.id)} 已修订(版本 +1,旧值留着)。${note}` })
|
|
3296
|
+
},
|
|
3297
|
+
})
|
|
3298
|
+
|
|
3299
|
+
/**
|
|
3300
|
+
* **废止是黏性终态**:没有复活这条路。存量事实照旧可读(历史留着),
|
|
3301
|
+
* 引用它的**新**断言会被拒,并在货架上标明「所用术语已废止」。
|
|
3302
|
+
*/
|
|
3303
|
+
defineTool({
|
|
3304
|
+
name: 'DeprecateTerm',
|
|
3305
|
+
description:
|
|
3306
|
+
'废止一个概念(或谓词):缘由必填。**没有删除**——条目、旧版本与引用过它的事实全部留着;新断言不许再引用它,存量事实在货架上标明「所用术语已废止」。要恢复语义就注册一个新条目(那是覆盖,不是复活)。',
|
|
3307
|
+
parameters: {
|
|
3308
|
+
type: 'object',
|
|
3309
|
+
properties: { id: { type: 'string', description: '概念或谓词的 id' }, reason: { type: 'string', description: '为什么废止(必填)' } },
|
|
3310
|
+
required: ['id', 'reason'],
|
|
3311
|
+
additionalProperties: false,
|
|
3312
|
+
},
|
|
3313
|
+
output: CARD_OUTPUT,
|
|
3314
|
+
async execute(args, exec) {
|
|
3315
|
+
const call = open(exec)
|
|
3316
|
+
if (call.ok !== true) return call.response
|
|
3317
|
+
const { hostService, sessionId, state, mutations } = call
|
|
3318
|
+
const done = finish(hostService, sessionId, mutations)
|
|
3319
|
+
const entry = findLexiconEntry(state, args.id)
|
|
3320
|
+
if (entry === null) return fail('unknown_entry', `词汇里没有这个条目:${String(args.id)}`)
|
|
3321
|
+
if (entry.entry.status === 'deprecated') return fail('already_deprecated', `${String(args.id)} 已经是废止状态(这一步不会重复记账)。`)
|
|
3322
|
+
if (typeof args.reason !== 'string' || args.reason.trim() === '') return fail('reason_required', '废止要写缘由:为什么这个词不再用。')
|
|
3323
|
+
mutations.push({ t: entry.kind === 'term' ? 'ontology/term_deprecated' : 'ontology/predicate_deprecated', id: String(args.id), reason: String(args.reason).trim() })
|
|
3324
|
+
const note = ensureDomainShelf(hostService, sessionId, mutations)
|
|
3325
|
+
return done({
|
|
3326
|
+
ok: true,
|
|
3327
|
+
code: 'entry_deprecated',
|
|
3328
|
+
message: `${entry.kind === 'term' ? '概念' : '谓词'} ${String(args.id)} 已废止(记录保留,新断言不许再引用;引用过它的事实照旧可读)。${note}`,
|
|
3329
|
+
})
|
|
3330
|
+
},
|
|
3331
|
+
})
|
|
3332
|
+
|
|
3333
|
+
defineTool({
|
|
3334
|
+
name: 'DeprecatePredicate',
|
|
3335
|
+
description: '废止一个谓词:缘由必填,记录保留,新断言不许再引用它(与 DeprecateTerm 同一条纪律,单列出来是为了让参数与语义各自说清)。',
|
|
3336
|
+
parameters: {
|
|
3337
|
+
type: 'object',
|
|
3338
|
+
properties: { id: { type: 'string' }, reason: { type: 'string' } },
|
|
3339
|
+
required: ['id', 'reason'],
|
|
3340
|
+
additionalProperties: false,
|
|
3341
|
+
},
|
|
3342
|
+
output: CARD_OUTPUT,
|
|
3343
|
+
async execute(args, exec) {
|
|
3344
|
+
const call = open(exec)
|
|
3345
|
+
if (call.ok !== true) return call.response
|
|
3346
|
+
const { hostService, sessionId, state, mutations } = call
|
|
3347
|
+
const done = finish(hostService, sessionId, mutations)
|
|
3348
|
+
const entry = findLexiconEntry(state, args.id)
|
|
3349
|
+
if (entry === null || entry.kind !== 'predicate') return fail('unknown_predicate', `词汇里没有这个谓词:${String(args.id)}`)
|
|
3350
|
+
if (entry.entry.status === 'deprecated') return fail('already_deprecated', `${String(args.id)} 已经是废止状态。`)
|
|
3351
|
+
if (typeof args.reason !== 'string' || args.reason.trim() === '') return fail('reason_required', '废止要写缘由。')
|
|
3352
|
+
mutations.push({ t: 'ontology/predicate_deprecated', id: String(args.id), reason: String(args.reason).trim() })
|
|
3353
|
+
const note = ensureDomainShelf(hostService, sessionId, mutations)
|
|
3354
|
+
return done({ ok: true, code: 'entry_deprecated', message: `谓词 ${String(args.id)} 已废止(记录保留,新断言不许再引用)。${note}` })
|
|
3355
|
+
},
|
|
3356
|
+
})
|
|
3357
|
+
|
|
3358
|
+
/**
|
|
3359
|
+
* **螺旋上半圈的入口**:按概念 / 谓词 / 主体取「已知」。
|
|
3360
|
+
* 读数全部来自投影(事实与流转中的假设),不新建任何存储;它也不改任何东西。
|
|
3361
|
+
*/
|
|
3362
|
+
defineTool({
|
|
3363
|
+
name: 'QueryKnowledge',
|
|
3364
|
+
description:
|
|
3365
|
+
'按概念 / 谓词 / 主体查「已知」:返回带类型化断言的**已升格事实**与**还在流转的命题**。要复用前面的结论就用它,而不是把事实库重读一遍。只读:它不改任何东西。',
|
|
3366
|
+
parameters: {
|
|
3367
|
+
type: 'object',
|
|
3368
|
+
properties: {
|
|
3369
|
+
term: { type: 'string', description: '概念 id:匹配断言的主体类型 / 宾语概念 / 词条本身' },
|
|
3370
|
+
predicate: { type: 'string', description: '谓词 id' },
|
|
3371
|
+
subject: { type: 'string', description: '主体名称(实例)' },
|
|
3372
|
+
},
|
|
3373
|
+
additionalProperties: false,
|
|
3374
|
+
},
|
|
3375
|
+
output: CARD_OUTPUT,
|
|
3376
|
+
async execute(args, exec) {
|
|
3377
|
+
const call = open(exec)
|
|
3378
|
+
if (call.ok !== true) return call.response
|
|
3379
|
+
const { hostService, sessionId, state, mutations } = call
|
|
3380
|
+
const done = finish(hostService, sessionId, mutations)
|
|
3381
|
+
const derived = hostService.derive(sessionId)
|
|
3382
|
+
const filter = { term: args.term === undefined ? '' : String(args.term).trim(), predicate: args.predicate === undefined ? '' : String(args.predicate).trim(), subject: args.subject === undefined ? '' : String(args.subject).trim() }
|
|
3383
|
+
if (filter.term === '' && filter.predicate === '' && filter.subject === '') return fail('query_empty', '至少给一个条件:term / predicate / subject。')
|
|
3384
|
+
const match = (assertion) => {
|
|
3385
|
+
const subject = assertion?.subject ?? {}
|
|
3386
|
+
const object = assertion?.object ?? {}
|
|
3387
|
+
if (filter.predicate !== '' && String(assertion?.predicate ?? '') !== filter.predicate) return false
|
|
3388
|
+
if (filter.subject !== '' && String(subject.id ?? '') !== filter.subject) return false
|
|
3389
|
+
if (filter.term !== '') {
|
|
3390
|
+
const hits = [String(subject.type ?? ''), String(subject.id ?? ''), String(object.kind) === 'instance' ? String(object.type ?? '') : '', String(object.kind) === 'instance' ? String(object.value ?? '') : '', String(assertion?.predicate ?? '')]
|
|
3391
|
+
if (!hits.includes(filter.term)) return false
|
|
3392
|
+
}
|
|
3393
|
+
return true
|
|
3394
|
+
}
|
|
3395
|
+
const facts = derived.factRows.filter((fact) => (Array.isArray(fact.assertions) ? fact.assertions : []).some(match))
|
|
3396
|
+
const inFlight = derived.hypotheses.filter((hypothesis) => (Array.isArray(hypothesis.assertions) ? hypothesis.assertions : []).some(match))
|
|
3397
|
+
if (facts.length === 0 && inFlight.length === 0) {
|
|
3398
|
+
return done({ ok: true, code: 'knowledge_empty', message: `这个词汇条件下还没有任何东西:${describeFilter(filter)}。要么先登记/验证,要么换个条件——**不要**把「查不到」写成「不存在」。` })
|
|
3399
|
+
}
|
|
3400
|
+
const lines = []
|
|
3401
|
+
if (facts.length > 0) {
|
|
3402
|
+
lines.push(`已知(已升格,可作为已知引用):${facts.length} 条`)
|
|
3403
|
+
for (const fact of facts.slice(0, 12)) {
|
|
3404
|
+
lines.push(`- ${fact.id} · ${clip(String(fact.text ?? ''), 120)}(支持到 ${fact.level ?? '—'}${fact.review?.decision === 'retracted' ? ' · **已撤回**' : fact.refuted === true ? ' · 有推翻证据等人决定' : ''})`)
|
|
3405
|
+
lines.push(` 边界:${clip(String(fact.scope ?? '(未写)'), 120)}`)
|
|
3406
|
+
for (const assertion of fact.assertions ?? []) if (match(assertion)) lines.push(` 断言:${hostService.domain.format(sessionId, assertion)}`)
|
|
3407
|
+
}
|
|
3408
|
+
if (facts.length > 12) lines.push(` (还有 ${facts.length - 12} 条,见 clear/knowledge/facts/INDEX.md)`)
|
|
3409
|
+
}
|
|
3410
|
+
if (inFlight.length > 0) {
|
|
3411
|
+
lines.push(`还在流转的命题(未升格):${inFlight.length} 条`)
|
|
3412
|
+
for (const hypothesis of inFlight.slice(0, 8)) {
|
|
3413
|
+
lines.push(`- ${hypothesis.id} [${hypothesis.status}] ${clip(String(hypothesis.claim ?? ''), 100)}${hypothesis.supportedLevel === null ? '' : `(支持到 ${hypothesis.supportedLevel})`}`)
|
|
3414
|
+
for (const assertion of hypothesis.assertions ?? []) if (match(assertion)) lines.push(` 断言:${hostService.domain.format(sessionId, assertion)}`)
|
|
3415
|
+
}
|
|
3416
|
+
}
|
|
3417
|
+
return done({ ok: true, code: 'knowledge_found', message: `${describeFilter(filter)}\n${lines.join('\n')}` })
|
|
3418
|
+
},
|
|
3419
|
+
})
|
|
3420
|
+
|
|
3010
3421
|
defineTool({
|
|
3011
3422
|
name: 'CreatePlan',
|
|
3012
3423
|
description:
|
|
@@ -5707,7 +6118,12 @@ export function apply(ctx, config = {}) {
|
|
|
5707
6118
|
|
|
5708
6119
|
function protectedRoots(sessionId) {
|
|
5709
6120
|
const cwd = sessionCwd(sessionId)
|
|
5710
|
-
|
|
6121
|
+
/**
|
|
6122
|
+
* 系统所有的四格:`evidence` / `facts` / `goals`,加上 `ontology`(词汇货架)。
|
|
6123
|
+
* 为什么词汇也在此列:货架是**渲染**,词条的权威在账本事件里——
|
|
6124
|
+
* 谁直接改那份 markdown,谁就制造了一份谁也不认的词汇表(面板读的是折法)。
|
|
6125
|
+
*/
|
|
6126
|
+
return [join(cwd, 'clear', 'evidence'), join(cwd, 'clear', 'knowledge', 'facts'), join(cwd, 'clear', 'goals'), join(cwd, 'clear', 'ontology')]
|
|
5711
6127
|
}
|
|
5712
6128
|
|
|
5713
6129
|
function touchesProtected(sessionId, value) {
|
|
@@ -5844,7 +6260,7 @@ export function apply(ctx, config = {}) {
|
|
|
5844
6260
|
// 「评估卡与事实只能由系统写」:做的人写不进证据面(执行者同样适用)
|
|
5845
6261
|
const suspect = touchesProtected(sessionId, args.file_path) ?? touchesProtected(sessionId, args.path) ?? (exec.name === 'bash' ? touchesProtected(sessionId, args.command) : null)
|
|
5846
6262
|
if (suspect !== null) {
|
|
5847
|
-
return { kind: 'deny', reason: `clear/evidence、clear/knowledge/facts、clear/goals 由系统所有,做的人不能写:${suspect}
|
|
6263
|
+
return { kind: 'deny', reason: `clear/evidence、clear/knowledge/facts、clear/goals、clear/ontology 由系统所有,做的人不能写:${suspect}。事实、评估卡与词汇货架只能由系统落盘(词汇要改就调注册/修订/废止动词)。` }
|
|
5848
6264
|
}
|
|
5849
6265
|
// L4 人放行:ClearAI 的设计目标未实现,这里用宿主的审批瀑布实现。
|
|
5850
6266
|
// 门挂在**等级**上:主线看步骤等级,世界线看分支等级——两条路同一道门。
|
|
@@ -6017,6 +6433,14 @@ export function apply(ctx, config = {}) {
|
|
|
6017
6433
|
ontologyShelved.add(sessionId)
|
|
6018
6434
|
if (shelf !== null) ontologyNote = `\n- 本体已就位(${CONTRIB.ontology.objects.length} 个对象 · ${CONTRIB.ontology.levels.length} 级):${join('clear', 'ontology', `${CONTRIB.ontology.id}.md`)}——交付前对照它(哪些状态合法、每一级谁来判)。`
|
|
6019
6435
|
}
|
|
6436
|
+
/**
|
|
6437
|
+
* **领域词汇货架**每一拍都重铺一次(幂等:内容没变就不重写)。
|
|
6438
|
+
*
|
|
6439
|
+
* 为什么不像过程本体那样只铺一次:那一份随**发布版本**,这一份随**会话**——
|
|
6440
|
+
* 一个项目今天没用词汇、明天开始用,货架必须自己长出来,而不是等人记得去建。
|
|
6441
|
+
* 它也不进卡:货架的位置在提示词里说一次就够,每拍重复就是往上下文里灌水。
|
|
6442
|
+
*/
|
|
6443
|
+
ensureDomainShelf(host(), sessionId)
|
|
6020
6444
|
let brainNote = ''
|
|
6021
6445
|
/** 事实货架那句话说一次就够(事实很少变)。 */
|
|
6022
6446
|
let factsNote = ''
|
|
@@ -246,6 +246,38 @@ SOP 里的「确认 / 经确认才进入下一 workflow」要求的是一次**
|
|
|
246
246
|
- 普通工具错误:同回合返回 ok=false 与原因,可继续,自己纠。
|
|
247
247
|
- 效果不确定时**先观察**:越过派发边界后结局未知的操作,语义只有一条——先看当前事实,再谈重试。重试的安全前提是「知道上次到底做没做」,而这个前提在结局未知时恰恰不成立。
|
|
248
248
|
- 供应商失败被归一化成类型化事实并退避;终不可用时暂停,一句「继续」原地续跑。不可信输入不许升格为整个运行的死亡。`,
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
name: 'clearai/domain-language',
|
|
252
|
+
class: 'hard',
|
|
253
|
+
order: 425,
|
|
254
|
+
text: `# 领域语言(词汇 / 断言 / 图)
|
|
255
|
+
|
|
256
|
+
这一节讲**知识用什么语言写**。认识论循环管「凭什么信」,领域本体管「用什么语言说」——两层分开,不许互相代替。
|
|
257
|
+
|
|
258
|
+
## 什么时候值得先立词
|
|
259
|
+
- 同一件事你会反复写(某个炉次、某个速率、某个指标),而且**两条结论要能互相比对**时:先 \`RegisterTerm\` 立概念,再 \`RegisterPredicate\` 立关系(写清主词域、值域、是否单值)。
|
|
260
|
+
- 词条是**约定**,不是主张:它不需要证据等级,但**依据必填**——哪份材料、哪条事实、谁说的。
|
|
261
|
+
- 反过来:只在这一条结论里出现一次的说法,不必造词——写进 claim 就够了。
|
|
262
|
+
|
|
263
|
+
## 断言是加法,不是门槛
|
|
264
|
+
- \`SetGoal\` 的假设可以带 \`assertions\`(主词–谓词–宾语)。**提供即严校**:引用不存在的谓词或概念、宾语形态不合值域、同一事实里自相矛盾,一律在**落账之前**被拒。
|
|
265
|
+
- 不写断言照旧升格,只是显示为「未结构化」。
|
|
266
|
+
- 值形态五种:\`statement\` / \`quantity\`(数值 + 单位) / \`formula\` / \`code\`(指向**工作区里真有的文件**,相对路径) / \`reference\`;关系谓词的宾语是另一个概念的 \`instance\`。
|
|
267
|
+
- 断言在**升格那一刻**随事实落地(\`fact/promoted\`),之后不回溯改写旧事实。
|
|
268
|
+
|
|
269
|
+
## 冲突只暴露,不裁决
|
|
270
|
+
两条**未撤回**的已确认事实落在同一单值谓词、同一主体、而客体不同时,系统给出一对冲突读数。它**不撤回任何一侧、也不判断哪条为真**——处置走人门(\`retract_fact\` / \`keep_fact\`)。别把冲突读成「系统说这条错了」。
|
|
271
|
+
|
|
272
|
+
## 复用已知用 QueryKnowledge
|
|
273
|
+
按概念 / 谓词 / 主体取「已知」:它返回带断言的已升格事实(连同边界与等级)与还在流转的命题。**查不到 ≠ 不存在**——如实说查了什么条件,不要用一次空查询断言世上没有。
|
|
274
|
+
|
|
275
|
+
## 改词有两条路,别走错
|
|
276
|
+
- 展示信息(名字 / 释义 / 别名)→ \`ReviseTerm\` / \`RevisePredicate\`:版本 +1,旧值留着。
|
|
277
|
+
- 语义(含义 / 父概念 / 主词域 / 值域 / 单值性)→ **废止 + 注册新 id**:稳定 id 的含义不许在历史上悄悄改变。
|
|
278
|
+
- 废止是黏性终态,没有复活:记录留着,新断言不许再引用它,存量事实照旧可读。
|
|
279
|
+
|
|
280
|
+
货架在 \`clear/ontology/domain.md\`(概念 / 谓词 / 图 / 引用统计)——**它是读面,不是权威**;\`clear/ontology/\` 由系统所有,你不能直接写。`,
|
|
249
281
|
},
|
|
250
282
|
{
|
|
251
283
|
name: 'clearai/coding-standards',
|