clearai-dsh 0.3.0 → 0.4.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 +147 -0
- package/README.md +28 -28
- package/README.zh-CN.md +28 -28
- package/lib/client.js +861 -2555
- package/lib/domain-language.js +552 -48
- package/lib/fold.js +351 -1015
- package/lib/host.js +47 -557
- package/lib/invariant.js +9 -12
- package/lib/knowledge-view.js +363 -226
- package/lib/lang.js +81 -0
- package/package.json +1 -1
- package/presets/clearai/agent.cordis.yml +54 -82
- package/presets/clearai/clearai.patch.yml +54 -82
- package/presets/clearai/plugins/clearai-kernel.js +1590 -5437
- package/presets/clearai/plugins/ontology.js +56 -14
- package/presets/clearai/plugins/prompts.js +73 -363
- package/presets/clearai/skills/clearai-loop/SKILL.md +73 -59
- package/presets/clearai/plugins/brain.js +0 -547
- package/presets/clearai/plugins/commands.js +0 -199
- package/presets/clearai/template/knowledge/README.md +0 -25
- package/presets/clearai/template/memory/README.md +0 -34
- package/presets/clearai/template/project.md +0 -49
- package/presets/clearai/template/skills/README.md +0 -37
- package/presets/clearai/template/skills/chart-diagram-qa/SKILL.md +0 -43
- package/presets/clearai/template/skills/citation-management/SKILL.md +0 -73
- package/presets/clearai/template/skills/citation-management/references/bibtex_formatting.md +0 -908
- package/presets/clearai/template/skills/citation-management/references/citation_validation.md +0 -794
- package/presets/clearai/template/skills/citation-management/references/google_scholar_search.md +0 -725
- package/presets/clearai/template/skills/citation-management/references/metadata_extraction.md +0 -870
- package/presets/clearai/template/skills/citation-management/references/pubmed_search.md +0 -839
- package/presets/clearai/template/skills/citation-management/scripts/doi_to_bibtex.py +0 -204
- package/presets/clearai/template/skills/citation-management/scripts/extract_metadata.py +0 -569
- package/presets/clearai/template/skills/citation-management/scripts/format_bibtex.py +0 -349
- package/presets/clearai/template/skills/citation-management/scripts/generate_schematic.py +0 -139
- package/presets/clearai/template/skills/citation-management/scripts/generate_schematic_ai.py +0 -817
- package/presets/clearai/template/skills/citation-management/scripts/search_google_scholar.py +0 -282
- package/presets/clearai/template/skills/citation-management/scripts/search_pubmed.py +0 -398
- package/presets/clearai/template/skills/citation-management/scripts/validate_citations.py +0 -497
- package/presets/clearai/template/skills/data-analysis/SKILL.md +0 -92
- package/presets/clearai/template/skills/data-analysis/checklists/readiness_check.md +0 -23
- package/presets/clearai/template/skills/data-analysis/templates/analysis_report.md.tpl +0 -63
- package/presets/clearai/template/skills/data-analysis/templates/cleaning_rules_draft.yaml.tpl +0 -32
- package/presets/clearai/template/skills/data-analysis/templates/data_dictionary.md.tpl +0 -12
- package/presets/clearai/template/skills/data-analysis/templates/domain_knowledge_template.md.tpl +0 -316
- package/presets/clearai/template/skills/data-analysis/templates/feature_candidates.json.tpl +0 -20
- package/presets/clearai/template/skills/data-analysis/templates/quality_scorecard.md.tpl +0 -30
- package/presets/clearai/template/skills/data-analysis/workflows/01-data-profiling.md +0 -42
- package/presets/clearai/template/skills/data-analysis/workflows/02-quality-audit.md +0 -36
- package/presets/clearai/template/skills/data-analysis/workflows/03-physical-correlation.md +0 -25
- package/presets/clearai/template/skills/data-analysis/workflows/04-unstructured-mining.md +0 -26
- package/presets/clearai/template/skills/data-qa-analysis/SKILL.md +0 -102
- package/presets/clearai/template/skills/data-qa-analysis/checklists/readiness_check.md +0 -62
- package/presets/clearai/template/skills/data-qa-analysis/templates/best_in_class_report.md.tpl +0 -56
- package/presets/clearai/template/skills/data-qa-analysis/templates/cleaning_rules_draft.yaml.tpl +0 -56
- package/presets/clearai/template/skills/data-qa-analysis/templates/data_dictionary.md.tpl +0 -13
- package/presets/clearai/template/skills/data-qa-analysis/templates/data_source_inventory_and_lineage.md.tpl +0 -146
- package/presets/clearai/template/skills/data-qa-analysis/templates/data_status_report.md.tpl +0 -60
- package/presets/clearai/template/skills/data-qa-analysis/templates/steady_state_rules.yaml.tpl +0 -41
- package/presets/clearai/template/skills/data-qa-analysis/templates/subsystem_registry.md.tpl +0 -101
- package/presets/clearai/template/skills/data-qa-analysis/templates/unified_execution_plan.md.tpl +0 -100
- package/presets/clearai/template/skills/data-qa-analysis/workflows/01-data-source-inventory-and-lineage.md +0 -194
- package/presets/clearai/template/skills/data-qa-analysis/workflows/02-data-alignment-and-tag-semantics.md +0 -122
- package/presets/clearai/template/skills/data-qa-analysis/workflows/03-steady-state-identification.md +0 -126
- package/presets/clearai/template/skills/data-qa-analysis/workflows/04-consumption-analysis.md +0 -152
- package/presets/clearai/template/skills/data-qa-analysis/workflows/05-best-in-class-and-optimization-space.md +0 -78
- package/presets/clearai/template/skills/domain-presearch/SKILL.md +0 -131
- package/presets/clearai/template/skills/domain-presearch/checklists/domain_checklist.md +0 -24
- package/presets/clearai/template/skills/domain-presearch/references/figure_code.md +0 -78
- package/presets/clearai/template/skills/domain-presearch/references/strategic_frameworks.md +0 -38
- package/presets/clearai/template/skills/exploration-loop/SKILL.md +0 -81
- package/presets/clearai/template/skills/exploratory-data-analysis/SKILL.md +0 -77
- package/presets/clearai/template/skills/exploratory-data-analysis/references/bioinformatics_genomics_formats.md +0 -664
- package/presets/clearai/template/skills/exploratory-data-analysis/references/chemistry_molecular_formats.md +0 -664
- package/presets/clearai/template/skills/exploratory-data-analysis/references/general_scientific_formats.md +0 -518
- package/presets/clearai/template/skills/exploratory-data-analysis/references/microscopy_imaging_formats.md +0 -620
- package/presets/clearai/template/skills/exploratory-data-analysis/references/proteomics_metabolomics_formats.md +0 -517
- package/presets/clearai/template/skills/exploratory-data-analysis/references/spectroscopy_analytical_formats.md +0 -633
- package/presets/clearai/template/skills/exploratory-data-analysis/scripts/eda_analyzer.py +0 -547
- package/presets/clearai/template/skills/hypothesis-generation/SKILL.md +0 -73
- package/presets/clearai/template/skills/hypothesis-generation/references/experimental_design_patterns.md +0 -329
- package/presets/clearai/template/skills/hypothesis-generation/references/hypothesis_quality_criteria.md +0 -198
- package/presets/clearai/template/skills/hypothesis-generation/references/literature_search_strategies.md +0 -622
- package/presets/clearai/template/skills/hypothesis-generation/scripts/generate_schematic.py +0 -139
- package/presets/clearai/template/skills/hypothesis-generation/scripts/generate_schematic_ai.py +0 -817
- package/presets/clearai/template/skills/literature-review/SKILL.md +0 -72
- package/presets/clearai/template/skills/literature-review/references/citation_styles.md +0 -166
- package/presets/clearai/template/skills/literature-review/references/database_strategies.md +0 -455
- package/presets/clearai/template/skills/literature-review/scripts/generate_pdf.py +0 -176
- package/presets/clearai/template/skills/literature-review/scripts/generate_schematic.py +0 -139
- package/presets/clearai/template/skills/literature-review/scripts/generate_schematic_ai.py +0 -817
- package/presets/clearai/template/skills/literature-review/scripts/search_databases.py +0 -303
- package/presets/clearai/template/skills/literature-review/scripts/verify_citations.py +0 -221
- package/presets/clearai/template/skills/paper-lookup/SKILL.md +0 -59
- package/presets/clearai/template/skills/paper-lookup/references/arxiv.md +0 -161
- package/presets/clearai/template/skills/paper-lookup/references/biorxiv.md +0 -118
- package/presets/clearai/template/skills/paper-lookup/references/core.md +0 -150
- package/presets/clearai/template/skills/paper-lookup/references/crossref.md +0 -181
- package/presets/clearai/template/skills/paper-lookup/references/medrxiv.md +0 -104
- package/presets/clearai/template/skills/paper-lookup/references/openalex.md +0 -174
- package/presets/clearai/template/skills/paper-lookup/references/pmc.md +0 -152
- package/presets/clearai/template/skills/paper-lookup/references/pubmed.md +0 -124
- package/presets/clearai/template/skills/paper-lookup/references/semantic-scholar.md +0 -203
- package/presets/clearai/template/skills/paper-lookup/references/unpaywall.md +0 -127
- package/presets/clearai/template/skills/process-presearch/SKILL.md +0 -196
- package/presets/clearai/template/skills/process-presearch/checklists/process_checklist.md +0 -18
- package/presets/clearai/template/skills/process-presearch/references/figure_code.md +0 -107
- package/presets/clearai/template/skills/process-presearch/references/source_attribution_example.md +0 -22
- package/presets/clearai/template/skills/process-understanding-extraction/SKILL.md +0 -69
- package/presets/clearai/template/skills/process-understanding-extraction/checklists/readiness_check.md +0 -34
- package/presets/clearai/template/skills/process-understanding-extraction/templates/docx_raw_dump_extractor.py.tpl +0 -132
- package/presets/clearai/template/skills/process-understanding-extraction/templates/entity_map_unit_topology.json.tpl +0 -86
- package/presets/clearai/template/skills/process-understanding-extraction/templates/process_brief.md.tpl +0 -89
- package/presets/clearai/template/skills/process-understanding-extraction/templates/process_brief_builder_from_raw_dump.py.tpl +0 -203
- package/presets/clearai/template/skills/process-understanding-extraction/templates/process_flow_mermaid.md.tpl +0 -41
- package/presets/clearai/template/skills/process-understanding-extraction/templates/unified_execution_plan.md.tpl +0 -53
- package/presets/clearai/template/skills/process-understanding-extraction/workflows/01-process-doc-discovery.md +0 -173
- package/presets/clearai/template/skills/process-understanding-extraction/workflows/02-process-understanding-and-diagramming.md +0 -106
- package/presets/clearai/template/skills/scientific-brainstorming/SKILL.md +0 -64
- package/presets/clearai/template/skills/scientific-brainstorming/references/brainstorming_methods.md +0 -326
- package/presets/clearai/template/skills/scientific-critical-thinking/SKILL.md +0 -72
- package/presets/clearai/template/skills/scientific-critical-thinking/references/common_biases.md +0 -364
- package/presets/clearai/template/skills/scientific-critical-thinking/references/evidence_hierarchy.md +0 -485
- package/presets/clearai/template/skills/scientific-critical-thinking/references/experimental_design.md +0 -496
- package/presets/clearai/template/skills/scientific-critical-thinking/references/logical_fallacies.md +0 -478
- package/presets/clearai/template/skills/scientific-critical-thinking/references/scientific_method.md +0 -169
- package/presets/clearai/template/skills/scientific-critical-thinking/references/statistical_pitfalls.md +0 -506
- package/presets/clearai/template/skills/skill-creator/SKILL.md +0 -109
- package/presets/clearai/template/skills/skill-creator/references/authoring-guide.md +0 -89
- package/presets/clearai/template/skills/statistical-analysis/SKILL.md +0 -79
- package/presets/clearai/template/skills/statistical-analysis/references/assumptions_and_diagnostics.md +0 -369
- package/presets/clearai/template/skills/statistical-analysis/references/bayesian_statistics.md +0 -653
- package/presets/clearai/template/skills/statistical-analysis/references/effect_sizes_and_power.md +0 -578
- package/presets/clearai/template/skills/statistical-analysis/references/reporting_standards.md +0 -469
- package/presets/clearai/template/skills/statistical-analysis/references/test_selection_guide.md +0 -129
- package/presets/clearai/template/skills/statistical-analysis/scripts/assumption_checks.py +0 -538
- package/presets/clearai/template/skills/web-artifact/SKILL.md +0 -165
- package/presets/clearai/template/skills/web-artifact/assets/renderer/renderer.css +0 -229
- package/presets/clearai/template/skills/web-artifact/assets/renderer/renderer.js +0 -373
- package/presets/clearai/template/skills/web-artifact/assets/vendor/elkjs/LICENSE +0 -263
- package/presets/clearai/template/skills/web-artifact/assets/vendor/elkjs/UPSTREAM.md +0 -26
- package/presets/clearai/template/skills/web-artifact/assets/vendor/elkjs/elk.bundled.js +0 -6605
- package/presets/clearai/template/skills/web-artifact/references/when-drawing-a-topology.md +0 -150
- package/presets/clearai/template/skills/web-artifact/references/when-the-page-must-work-offline.md +0 -62
- package/presets/clearai/template/skills/web-artifact/scripts/check_artifact.py +0 -167
- package/presets/clearai/template/skills/web-artifact/scripts/render_topology.js +0 -272
- package/presets/clearai/template/skills/what-if-oracle/LICENSE.txt +0 -5
- package/presets/clearai/template/skills/what-if-oracle/SKILL.md +0 -72
- package/presets/clearai/template/skills/what-if-oracle/references/scenario-templates.md +0 -154
package/lib/fold.js
CHANGED
|
@@ -16,11 +16,9 @@
|
|
|
16
16
|
* 与预设内核的契约:`meta = { kind: 'clearai', v: 1, mutation: { t, ... } }`。
|
|
17
17
|
* 词汇表由本文件的 `applyMutation` 定义;预设侧只负责产出,不负责解释。
|
|
18
18
|
*/
|
|
19
|
-
import { applyLexiconMutation, deriveConflicts, emptyLexicon, formatAssertion, graphProjection, lexiconHealth, normalizeLexicon, objectKey, termUsage, VALUE_FORMS } from './domain-language.js'
|
|
20
|
-
import { knowledgeView } from './knowledge-view.js'
|
|
21
|
-
|
|
22
|
-
/** 世界线分支状态的秩。秩只增;一旦出现 adopted,整个分叉的分支状态冻结。 */
|
|
23
|
-
const BRANCH_RANK = { exploring: 0, evaluated: 1, adopted: 2, pruned: 2 }
|
|
19
|
+
import { applyLexiconMutation, changedDefinitions, materializeOntology, classifyWorkspacePath, deriveConflicts, emptyLexicon, factFromFile, formatAssertion, graphProjection, lexiconHealth, normalizeLexicon, objectKey, termUsage, VALUE_FORMS } from './domain-language.js'
|
|
20
|
+
import { handleOf, knowledgeView, trustOf } from './knowledge-view.js'
|
|
21
|
+
import { bilingual, detectLanguage, messageText, tr, withLanguage } from './lang.js'
|
|
24
22
|
|
|
25
23
|
/** 五个等级,由低到高。等级是「这条证据有多大程度只能靠信任做的人」的刻度(见 docs/verification-loop.md 的等级表)。 */
|
|
26
24
|
const LEVELS = ['L0', 'L1', 'L2', 'L3', 'L4']
|
|
@@ -42,11 +40,29 @@ export const MUTATION_KIND = 'clearai'
|
|
|
42
40
|
* 没有断言的事实照旧可读——形状变了才 +1,不是语义变了才 +1。
|
|
43
41
|
* v10 → v11:**实体账本**独立于目标裁决:`entities`(登记的实例)、`entityAssertions`
|
|
44
42
|
* (登记那一刻就成立的边)、`hostHealth`(宿主读面降级的账),以及
|
|
45
|
-
* `hypotheses[].skips`(
|
|
43
|
+
* `hypotheses[].skips`(跳级理由,v14 删)与 `goal.criteriaHistory`(判据修订)。
|
|
46
44
|
* 旧日志这三块都是空表:实体图仍只从 `facts[].assertions` 长出来,逐字节不变。
|
|
47
|
-
*
|
|
45
|
+
* v11 → v12:**宿主已有的交还宿主**:分叉 / 世界线(`forks`)、侦察(`scouts`)、外脑与技能目录
|
|
46
|
+
* (`brain`、`brainCandidates`、`skillPromotions`、`skillCatalog`、`skillUsage`)、章程读数
|
|
47
|
+
* (`constitution`)与写入计数(`writeCalls`)都从状态里删了。旧日志里的对应事件类型
|
|
48
|
+
* (`fork/*`、`worldline/*`、`scout/*`、`branch/*`、`git/*`、`clearai/brain` 段、相关人门动作)
|
|
49
|
+
* 不认识就原样跳过,其余部分照常折出来。
|
|
50
|
+
* v12 → v13:**步骤完成与结果分开**:步骤的 `tests` 折成 `{hypotheses: id[], level}`(旧账的单条
|
|
51
|
+
* `hypothesis` 照旧读);证据带上它针对的判断(`hypothesis`);`step/advanced` 带上交付本身
|
|
52
|
+
* (`delivery`:谁判的、凭什么、出处),`step.evidence` 变成 id 数组;结算事实带上两项裁决
|
|
53
|
+
* (`holds` / `results`)。
|
|
54
|
+
* v13 → v14:**缺口与关口收窄**:跳级理由整套删除——假设上不再有 `skips`,派生里不再有
|
|
55
|
+
* `untouchedLevels`;旧日志里的 `level/skipped` 不认识就原样跳过。缺口只留三种
|
|
56
|
+
* (`untouched_claims`、`prose_only_claims`、`entities_unlanded`)。
|
|
57
|
+
* v14 → v15:**攒下来的东西住在文件里**:多了 `workspace.files`(工作区里事实文件与本体文件
|
|
58
|
+
* 的内容快照,由 `workspace/synced` 折进来);事实带上 `definitions`(升格那一刻用到的
|
|
59
|
+
* 词条含义指纹)。派生的事实行合并其它会话留下的事实(`foreign`),并给出
|
|
60
|
+
* `definitionsChanged`。本体(词汇、实体、实体关系)改为从本体文件折出来,跨文件问题记在
|
|
61
|
+
* `ontologyProblems`。旧日志里没有这些,折出来就是空的。
|
|
62
|
+
* v15 → v16:多了 `language`(人说话用的语言,从人的消息折出来):系统写给人的话、面板里的读数都跟着它。
|
|
63
|
+
* 投影缓存按版本判定,所以旧缓存会被丢弃、从日志重折一遍。
|
|
48
64
|
*/
|
|
49
|
-
export const STATE_VERSION =
|
|
65
|
+
export const STATE_VERSION = 16
|
|
50
66
|
|
|
51
67
|
/**
|
|
52
68
|
* **只留台账、不折进视图**的变更类型(词汇表的另一半)。
|
|
@@ -56,11 +72,10 @@ export const STATE_VERSION = 11
|
|
|
56
72
|
* · 发行自检按它比对内核实际落的每一类事实 —— 既没折、又不在清单里的,是**真的缺口**;
|
|
57
73
|
* · 读代码的人不必逐条 grep 才知道哪些事实不上界面。
|
|
58
74
|
*
|
|
59
|
-
*
|
|
60
|
-
* (
|
|
61
|
-
* 而不是从投影里再长一份。
|
|
75
|
+
* 它们记「系统做了什么」(准入检查),不改变任何派生量。
|
|
76
|
+
* 旧日志里的 `git/*`(已删除的账本)不再列出:不认识的类型原样返回,本来就会被安静跳过。
|
|
62
77
|
*/
|
|
63
|
-
export const LEDGER_ONLY_MUTATIONS = ['admission/checked'
|
|
78
|
+
export const LEDGER_ONLY_MUTATIONS = ['admission/checked']
|
|
64
79
|
|
|
65
80
|
/** 空状态。`init` 与「日志里还没有任何变更」都必须给出同一个形状。 */
|
|
66
81
|
export function emptyState() {
|
|
@@ -72,45 +87,8 @@ export function emptyState() {
|
|
|
72
87
|
audits: [],
|
|
73
88
|
materials: [],
|
|
74
89
|
facts: [],
|
|
75
|
-
forks: [],
|
|
76
|
-
/** 系统按触发派出去的侦察(子角色不是模型自由委派):结论进资料面,这里留一条记录 */
|
|
77
|
-
scouts: [],
|
|
78
|
-
/** 工作区里等人采纳的候选技能(内核扫描后落的事实)与已采纳的记录 */
|
|
79
|
-
brainCandidates: [],
|
|
80
|
-
skillPromotions: [],
|
|
81
|
-
/**
|
|
82
|
-
* 外脑全景(技能清单 + 记忆索引)。**由内核扫描后随投影下发**——面板在浏览器里读不了盘,
|
|
83
|
-
* 而内核每个 pre-step 都在扫;清单走投影(变了才发),正文才走宿主路由(点开时才读)。
|
|
84
|
-
*/
|
|
85
|
-
brain: null,
|
|
86
|
-
/**
|
|
87
|
-
* 宿主原生的**合并技能目录**(预设自带 / 项目 / 用户 / 我们投影的 `clear/skills` 合并后的表)。
|
|
88
|
-
* 内核每个 pre-step 调一次 `ctx.skills.snapshot`,变了才发——于是面板与模型看的是**同一张表**。
|
|
89
|
-
*/
|
|
90
|
-
skillCatalog: null,
|
|
91
|
-
/**
|
|
92
|
-
* 本会话的技能加载记录。**纯派生,零新账**:
|
|
93
|
-
* 模型加载 = 日志里的 `tool/call name='skill'`;人引用 = 一条含 `/名字` 的用户消息。
|
|
94
|
-
*/
|
|
95
|
-
skillUsage: {},
|
|
96
90
|
blocks: {},
|
|
97
91
|
releases: [],
|
|
98
|
-
/**
|
|
99
|
-
* 运行档(人在场 / 无人值守)。**它是部署预设的初值,不是可切换的开关**:
|
|
100
|
-
* · 人门动词 `set_autonomy` 已摘除,面板上那个开关已经不存在;
|
|
101
|
-
* · `effective` = 内核随投影下发的那一份(只有内核知道组合配置),**只用于展示**;
|
|
102
|
-
* · `override` = 历史日志里可能留下的人门记录。读取侧保留它只是为了旧会话仍然读得通,
|
|
103
|
-
* 当前**没有任何写入者**——不要据此认为现在还能切换档位。
|
|
104
|
-
* · 它也不影响续跑:要不要继续由门状态算出来(见内核 turnDemand)。
|
|
105
|
-
*/
|
|
106
|
-
autonomy: { override: null, effective: null },
|
|
107
|
-
/**
|
|
108
|
-
* 项目章程(`PROJECT.md`)的读数(内核扫描后随投影下发)。
|
|
109
|
-
* 为什么要有它:章程的**读**侧早就接在原生指令文件上了,但**写**侧只有一句提示词——
|
|
110
|
-
* 结果是跑完几轮、交付了产物,章程还是铺工作区那天的模板,谁也没发现——不提醒它就会一直漂着。
|
|
111
|
-
* 事实摆在这里,人与模型都看得见「它还是不是空壳」。
|
|
112
|
-
*/
|
|
113
|
-
constitution: null,
|
|
114
92
|
/**
|
|
115
93
|
* **本体形状**:内核随投影下发的那份声明(对象/状态/边/等级)。
|
|
116
94
|
* 为什么进投影而不是在界面里手抄:面板那一格的页眉要从**声明**生成——
|
|
@@ -149,87 +127,38 @@ export function emptyState() {
|
|
|
149
127
|
*/
|
|
150
128
|
hostHealth: [],
|
|
151
129
|
/**
|
|
152
|
-
*
|
|
153
|
-
* 我收兵了 / 它不在了而那不是我们干的」。形状见 `applyMutation` 的 `continuation/set`。
|
|
130
|
+
* **工作区文件快照**(`workspace/synced`):路径 → `{ digest, data }` 或 `{ digest, error }`。
|
|
154
131
|
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
|
|
132
|
+
* 攒下来的东西(事实、本体)住在项目文件里,跨会话活着;而投影只吃账本。内核每一拍把
|
|
133
|
+
* 这两处的**变化**折成一条变更,于是文件内容也是账本里一条可重放的事实——重放、分叉、
|
|
134
|
+
* 离线复判读到的都是那一刻的文件,而不是此刻盘上的。
|
|
135
|
+
*/
|
|
136
|
+
workspace: { files: {} },
|
|
137
|
+
/**
|
|
138
|
+
* **本体文件的跨文件问题**(第二道校验,只提示):引用断了、类型对不上、单值关系两个取值……
|
|
139
|
+
* 有问题的节点或边不进图,问题列在这里,卡片与本体页都读它。
|
|
140
|
+
*/
|
|
141
|
+
ontologyProblems: [],
|
|
142
|
+
/**
|
|
143
|
+
* **人说话用的语言**('zh' / 'en';还没听到人说话时为 null)。从人的消息里折出来:
|
|
144
|
+
* 工具结果、运行态卡、面板读数都跟着它(见 `lang.js`)。
|
|
159
145
|
*/
|
|
160
|
-
|
|
146
|
+
language: null,
|
|
161
147
|
/** 正在飞的工具调用(来自 tool/call):让「评估者在裁决」成为一条可看见的事实 */
|
|
162
148
|
inFlight: null,
|
|
163
149
|
/** 本会话自己写过的路径(来自 tool/call 的 write/edit):L4 来源分离的判据 */
|
|
164
150
|
written: [],
|
|
165
|
-
/**
|
|
166
|
-
* **会改工作区的工具调用**的一次计数(来自 tool/call 的 write/edit/bash/pwsh)。
|
|
167
|
-
*
|
|
168
|
-
* 为什么要有它:`written` 只认 write/edit,而探索最常走 bash(脚本自己产出文件),
|
|
169
|
-
* 于是「这个会话到底动过工作区没有」用 `written` 答不全。内核在**回合边界**上要问的
|
|
170
|
-
* 正是这一句(是否值得记一次工作区快照),所以这里记一个只增的计数——
|
|
171
|
-
* 它是日志折出来的,可重放,不是第二本账。
|
|
172
|
-
*/
|
|
173
|
-
writeCalls: 0,
|
|
174
151
|
}
|
|
175
152
|
}
|
|
176
153
|
|
|
177
154
|
/**
|
|
178
|
-
*
|
|
155
|
+
* 等裁决的工具:它们的调用在飞时,派生阶段是 `auditing`。`CloseGoal` 是 `Conclude` 的旧名,留着让旧日志照样折。
|
|
179
156
|
* 其余工具不等外部裁决,不该让面板在每次调用时跳一下。
|
|
180
157
|
*/
|
|
181
|
-
const VERDICT_WAITERS = new Set(['AdvancePlan', '
|
|
158
|
+
const VERDICT_WAITERS = new Set(['AdvancePlan', 'Conclude', 'CloseGoal'])
|
|
182
159
|
|
|
183
160
|
/** 记下来的自写路径有上限:它是判据,不是档案(档案在会话日志里)。 */
|
|
184
161
|
const MAX_WRITTEN = 512
|
|
185
|
-
/**
|
|
186
|
-
* 会**改工作区**的工具名。`bash`/`pwsh` 也算:脚本自己产出文件,内核分辨不了只读与写入,
|
|
187
|
-
* 所以宁可偏保守——多记一笔快照,也不漏掉探索产出。
|
|
188
|
-
*/
|
|
189
|
-
const WRITE_CAPABLE_TOOLS = new Set(['write', 'edit', 'bash', 'pwsh'])
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* 技能引用手势:与原生 `dsh-tool-skill` 的 `SKILL_GESTURE` **同一条正则**。
|
|
193
|
-
*
|
|
194
|
-
* 为什么不各写一条:这条正则是「技能正文会不会被注入」的**充要条件**
|
|
195
|
-
* (`dsh-tool-skill/lib/index.js` 的 `invokedSkillNames`:只认 `source.kind === 'user'` 的
|
|
196
|
-
* 文本块)。面板数出来的用法与原生真的做了什么必须是同一件事,否则读数就是编的。
|
|
197
|
-
*/
|
|
198
|
-
const SKILL_GESTURE = /(^|\s)\/([a-z0-9]+(?:-[a-z0-9]+)*)(?=\s|$)/g
|
|
199
|
-
/** 每条技能留最近几次记录(用量是判据不是档案;要全量去会话日志里查)。 */
|
|
200
|
-
const MAX_SKILL_EVENTS = 8
|
|
201
|
-
/** 最多跟踪多少条技能,免得一个失控的会话把投影撑大。 */
|
|
202
|
-
const MAX_SKILLS_TRACKED = 64
|
|
203
|
-
|
|
204
|
-
/** 从一条消息的文本块里取出被引用的技能名(与原生同一条正则、同一套去重)。 */
|
|
205
|
-
function invokedSkillNames(message) {
|
|
206
|
-
const names = []
|
|
207
|
-
for (const block of Array.isArray(message?.content) ? message.content : []) {
|
|
208
|
-
if (block?.type !== 'text' || typeof block.text !== 'string') continue
|
|
209
|
-
for (const match of block.text.matchAll(SKILL_GESTURE)) if (match[2] !== undefined && !names.includes(match[2])) names.push(match[2])
|
|
210
|
-
}
|
|
211
|
-
return names
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/** 这一次加载落在哪一步上(「在第 3 步」这个指针的由来)。 */
|
|
215
|
-
function stepPointer(state) {
|
|
216
|
-
const plan = state.plans.find((item) => item.status === 'active') ?? null
|
|
217
|
-
if (plan === null) return { plan: null, step: null, ordinal: null }
|
|
218
|
-
const step = plan.steps.find((item) => item.status === 'open') ?? null
|
|
219
|
-
return { plan: plan.id, step: step === null ? null : step.id, ordinal: step === null ? null : step.ordinal }
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
function recordSkillUse(state, name, by, at, pointer) {
|
|
223
|
-
const usage = state.skillUsage ?? (state.skillUsage = {})
|
|
224
|
-
if (usage[name] === undefined && Object.keys(usage).length >= MAX_SKILLS_TRACKED) return
|
|
225
|
-
const entry = usage[name] ?? { name, model: 0, human: 0, events: [], lastAt: null }
|
|
226
|
-
if (by === 'model') entry.model += 1
|
|
227
|
-
else entry.human += 1
|
|
228
|
-
entry.events.push({ by, at, plan: pointer.plan, step: pointer.step, ordinal: pointer.ordinal })
|
|
229
|
-
if (entry.events.length > MAX_SKILL_EVENTS) entry.events.splice(0, entry.events.length - MAX_SKILL_EVENTS)
|
|
230
|
-
entry.lastAt = at
|
|
231
|
-
usage[name] = entry
|
|
232
|
-
}
|
|
233
162
|
|
|
234
163
|
/** 深拷贝(状态是纯 JSON,这是唯一需要的工具)。 */
|
|
235
164
|
function clone(value) {
|
|
@@ -257,6 +186,16 @@ function factFromHypothesis(facts, goalId, hypothesisId, claim) {
|
|
|
257
186
|
})
|
|
258
187
|
}
|
|
259
188
|
|
|
189
|
+
/**
|
|
190
|
+
* 步骤检验的判断一律折成 `{hypotheses: id[], level}`:新账写 `hypotheses`(可多条),
|
|
191
|
+
* 旧账只有单条 `hypothesis`。读面只认这一种形状。
|
|
192
|
+
*/
|
|
193
|
+
export function normalizeTests(tests) {
|
|
194
|
+
if (tests === undefined || tests === null || typeof tests !== 'object') return null
|
|
195
|
+
const hypotheses = Array.isArray(tests.hypotheses) ? tests.hypotheses.map(String) : typeof tests.hypothesis === 'string' && tests.hypothesis !== '' ? [tests.hypothesis] : []
|
|
196
|
+
return { hypotheses, level: tests.level ?? null }
|
|
197
|
+
}
|
|
198
|
+
|
|
260
199
|
function appendSteps(plan, rawSteps) {
|
|
261
200
|
for (const raw of rawSteps) {
|
|
262
201
|
plan.steps.push({
|
|
@@ -265,7 +204,7 @@ function appendSteps(plan, rawSteps) {
|
|
|
265
204
|
do: raw.do,
|
|
266
205
|
artifacts: raw.artifacts ?? [],
|
|
267
206
|
done_criteria: raw.done_criteria,
|
|
268
|
-
tests: raw.tests
|
|
207
|
+
tests: normalizeTests(raw.tests),
|
|
269
208
|
status: 'open',
|
|
270
209
|
evidence: null,
|
|
271
210
|
advancedAt: null,
|
|
@@ -357,12 +296,15 @@ export function applyMutation(state, mutation) {
|
|
|
357
296
|
if (String(known.claim ?? '') !== claim) continue
|
|
358
297
|
known.refute_when = hypothesis.refute_when
|
|
359
298
|
known.version = hypothesis.version ?? known.version
|
|
299
|
+
if (typeof hypothesis.name === 'string' && hypothesis.name !== '') known.name = hypothesis.name
|
|
360
300
|
if (Array.isArray(hypothesis.assertions)) known.assertions = clone(hypothesis.assertions)
|
|
361
301
|
continue
|
|
362
302
|
}
|
|
363
303
|
next.hypotheses.push({
|
|
364
304
|
id: hypothesis.id,
|
|
365
305
|
goal: mutation.id,
|
|
306
|
+
/** 短名(第六阶段起,模型自己起);旧日志没有它,读的一侧取主张开头。 */
|
|
307
|
+
...(typeof hypothesis.name === 'string' && hypothesis.name !== '' ? { name: hypothesis.name } : {}),
|
|
366
308
|
claim,
|
|
367
309
|
refute_when: hypothesis.refute_when,
|
|
368
310
|
status: 'proposed',
|
|
@@ -372,12 +314,6 @@ export function applyMutation(state, mutation) {
|
|
|
372
314
|
* 不写断言照旧成立(宽松+校验);写了就是「这条主张用这门语言怎么说」。
|
|
373
315
|
*/
|
|
374
316
|
assertions: Array.isArray(hypothesis.assertions) ? clone(hypothesis.assertions) : null,
|
|
375
|
-
/**
|
|
376
|
-
* **跳级理由**(`level/skipped` 折进来)。跳级本身不违规(首次测量没有廉价路
|
|
377
|
-
* 可走),但「我直接上了 L3、下面几级从没走过」必须能说清为什么不适用——
|
|
378
|
-
* 理由落在这里,`derive()` 会把被理由覆盖的层从 `untouchedLevels` 里减掉。
|
|
379
|
-
*/
|
|
380
|
-
skips: [],
|
|
381
317
|
at,
|
|
382
318
|
})
|
|
383
319
|
}
|
|
@@ -423,31 +359,12 @@ export function applyMutation(state, mutation) {
|
|
|
423
359
|
closedAt: null,
|
|
424
360
|
summary: null,
|
|
425
361
|
blocked: undefined,
|
|
426
|
-
/**
|
|
427
|
-
* 授权记号(ClearAI `plan.confirmed_at` / `confirmed_by`)。
|
|
428
|
-
* 两种取得方式,都可考:**显式动作**(人在原生审阅卡上批准 → `by:'user'`)与
|
|
429
|
-
* **行为**(交付过一步 → `by:'progress'`,见内核 AdvancePlan)。
|
|
430
|
-
* 第三种来源 `by:'autonomy'`(无人值守档在立约时自动确认)**已删除**:
|
|
431
|
-
* 那会让「计划经人确认」这条证据变成系统自己签的。读取侧不需要兼容它,
|
|
432
|
-
* 因为删除发生在写入侧,历史日志里最多出现 `user` 与 `progress`。
|
|
433
|
-
* 注意这是一个**记号**,不是闸门:没确认的计划照样能被交付推起来,那一刻记号按事实补写。
|
|
434
|
-
*/
|
|
435
|
-
confirmed_at: mutation.confirmed_at ?? null,
|
|
436
|
-
confirmed_by: mutation.confirmed_by ?? null,
|
|
437
362
|
steps: [],
|
|
438
363
|
}
|
|
439
364
|
next.plans.push(plan)
|
|
440
365
|
appendSteps(plan, mutation.steps ?? [])
|
|
441
366
|
break
|
|
442
367
|
}
|
|
443
|
-
case 'plan/confirmed': {
|
|
444
|
-
// 第一次授权为准(`stamp_confirmed_by_progress` 同款幂等:已确认就不再改写)。
|
|
445
|
-
const plan = planOf(mutation.plan)
|
|
446
|
-
if (plan === undefined || plan.confirmed_at !== null) break
|
|
447
|
-
plan.confirmed_at = mutation.at ?? new Date(at).toISOString()
|
|
448
|
-
plan.confirmed_by = mutation.by ?? 'user'
|
|
449
|
-
break
|
|
450
|
-
}
|
|
451
368
|
case 'plan/amended': {
|
|
452
369
|
const plan = planOf(mutation.plan)
|
|
453
370
|
if (plan === undefined || plan.status !== 'active') break
|
|
@@ -489,6 +406,8 @@ export function applyMutation(state, mutation) {
|
|
|
489
406
|
}
|
|
490
407
|
case 'block/cleared': {
|
|
491
408
|
delete next.blocks[`${mutation.plan}:${mutation.step}`]
|
|
409
|
+
const plan = planOf(mutation.plan)
|
|
410
|
+
if (plan?.blocked?.step === mutation.step) plan.blocked = undefined
|
|
492
411
|
break
|
|
493
412
|
}
|
|
494
413
|
case 'observation/recorded': {
|
|
@@ -501,7 +420,6 @@ export function applyMutation(state, mutation) {
|
|
|
501
420
|
note: mutation.note ?? null,
|
|
502
421
|
path: mutation.path ?? null,
|
|
503
422
|
step: mutation.step ?? null,
|
|
504
|
-
branch: mutation.branch ?? null,
|
|
505
423
|
at,
|
|
506
424
|
})
|
|
507
425
|
break
|
|
@@ -558,6 +476,9 @@ export function applyMutation(state, mutation) {
|
|
|
558
476
|
const audit = next.audits.find((item) => item.id === mutation.id)
|
|
559
477
|
if (audit !== undefined) {
|
|
560
478
|
audit.verdict = mutation.verdict
|
|
479
|
+
/** 两项裁决:交付成立吗 + 每条判断的结果。旧账没有这两格,`verdict` 那时说的就是交付成立吗。 */
|
|
480
|
+
audit.holds = mutation.holds ?? null
|
|
481
|
+
audit.results = Array.isArray(mutation.results) ? mutation.results : []
|
|
561
482
|
audit.shortfalls = mutation.shortfalls ?? []
|
|
562
483
|
audit.basis = mutation.basis ?? null
|
|
563
484
|
audit.card_path = mutation.card_path ?? null
|
|
@@ -570,7 +491,8 @@ export function applyMutation(state, mutation) {
|
|
|
570
491
|
id: mutation.id,
|
|
571
492
|
step: mutation.step,
|
|
572
493
|
plan: mutation.plan,
|
|
573
|
-
|
|
494
|
+
/** 针对哪条判断;旧账没有这一格(`undefined`),按步骤检验的判断找回来。 */
|
|
495
|
+
hypothesis: mutation.hypothesis,
|
|
574
496
|
verdict: mutation.verdict,
|
|
575
497
|
level: mutation.level,
|
|
576
498
|
evaluator: mutation.evaluator,
|
|
@@ -593,21 +515,22 @@ export function applyMutation(state, mutation) {
|
|
|
593
515
|
const step = stepOf(mutation.plan, mutation.step)
|
|
594
516
|
if (step !== undefined) {
|
|
595
517
|
settle(step, 'advanced')
|
|
596
|
-
|
|
518
|
+
/** 新账是证据 id 的数组(每条判断一份,不检验判断就是空数组);旧账是单个 id。 */
|
|
519
|
+
step.evidence = Array.isArray(mutation.evidence) ? mutation.evidence : mutation.evidence === undefined || mutation.evidence === null ? [] : [mutation.evidence]
|
|
520
|
+
/** 交付本身:谁判的、凭什么、出处。旧账没有,读面退回到证据。 */
|
|
521
|
+
step.delivery =
|
|
522
|
+
mutation.evaluator === undefined
|
|
523
|
+
? null
|
|
524
|
+
: { evaluator: mutation.evaluator, basis: mutation.basis ?? null, refs: Array.isArray(mutation.refs) ? mutation.refs : [], origins: Array.isArray(mutation.origins) ? mutation.origins : [] }
|
|
597
525
|
step.advancedAt = at
|
|
598
526
|
}
|
|
599
527
|
break
|
|
600
528
|
}
|
|
601
529
|
case 'human/released': {
|
|
602
|
-
/**
|
|
603
|
-
* 放行绑在**哪条轴**上要说清:主线绑步骤、世界线绑分支。
|
|
604
|
-
* `via` 记它凭什么算数——现在只有 `approval`(原生审批栈的权威记录),不推断。
|
|
605
|
-
*/
|
|
530
|
+
/** `via` 记它凭什么算数——现在只有 `approval`(原生审批栈的权威记录),不推断。 */
|
|
606
531
|
next.releases.push({
|
|
607
532
|
step: mutation.step ?? null,
|
|
608
533
|
plan: mutation.plan ?? null,
|
|
609
|
-
fork: mutation.fork ?? null,
|
|
610
|
-
branch: mutation.branch ?? null,
|
|
611
534
|
via: mutation.via ?? null,
|
|
612
535
|
at,
|
|
613
536
|
call: mutation.call ?? null,
|
|
@@ -631,10 +554,47 @@ export function applyMutation(state, mutation) {
|
|
|
631
554
|
evidence: mutation.evidence ?? [],
|
|
632
555
|
path: mutation.path ?? null,
|
|
633
556
|
assertions: Array.isArray(mutation.assertions) ? clone(mutation.assertions) : null,
|
|
557
|
+
/** 升格那一刻,断言用到的词条各自的含义指纹:之后定义改了,这条事实要复核。 */
|
|
558
|
+
definitions: mutation.definitions !== null && typeof mutation.definitions === 'object' && !Array.isArray(mutation.definitions) ? clone(mutation.definitions) : null,
|
|
634
559
|
at,
|
|
635
560
|
})
|
|
636
561
|
break
|
|
637
562
|
}
|
|
563
|
+
/**
|
|
564
|
+
* **工作区同步**(`workspace/synced`,内核发):事实文件与本体文件这一拍的变化。
|
|
565
|
+
* 每条 change 是一个文件的新样子(`data` 或读不成时的 `error`),或 `removed`。
|
|
566
|
+
* 按路径覆盖,所以同一批变化折两遍结果一样。
|
|
567
|
+
*/
|
|
568
|
+
case 'workspace/synced': {
|
|
569
|
+
const files = next.workspace !== null && typeof next.workspace === 'object' && next.workspace.files !== null && typeof next.workspace.files === 'object' ? next.workspace.files : {}
|
|
570
|
+
next.workspace = { ...(next.workspace ?? {}), files }
|
|
571
|
+
for (const change of Array.isArray(mutation.changes) ? mutation.changes : []) {
|
|
572
|
+
const path = typeof change?.path === 'string' ? change.path : ''
|
|
573
|
+
if (path === '') continue
|
|
574
|
+
if (change.removed === true) {
|
|
575
|
+
delete files[path]
|
|
576
|
+
continue
|
|
577
|
+
}
|
|
578
|
+
files[path] = change.error !== undefined && change.error !== null ? { digest: change.digest ?? null, error: String(change.error) } : { digest: change.digest ?? null, data: change.data ?? null }
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* **本体从文件折出来**:词汇、实体、实体断言整份按文件重算——文件就是权威,
|
|
582
|
+
* 账本里只记「那一刻文件是什么样」。旧会话里工具写下的本体事件仍折得出来,
|
|
583
|
+
* 但一旦这个会话同步过本体文件,就以文件为准(旧会话不迁移)。
|
|
584
|
+
*/
|
|
585
|
+
const touchedOntology = (mutation.changes ?? []).some((change) => {
|
|
586
|
+
const kind = classifyWorkspacePath(change?.path)?.kind
|
|
587
|
+
return kind !== undefined && kind !== 'fact'
|
|
588
|
+
})
|
|
589
|
+
if (touchedOntology || Object.keys(files).some((path) => classifyWorkspacePath(path)?.kind !== 'fact')) {
|
|
590
|
+
const ontology = materializeOntology(files)
|
|
591
|
+
next.lexicon = ontology.lexicon
|
|
592
|
+
next.entities = ontology.entities
|
|
593
|
+
next.entityAssertions = ontology.entityAssertions
|
|
594
|
+
next.ontologyProblems = ontology.problems
|
|
595
|
+
}
|
|
596
|
+
break
|
|
597
|
+
}
|
|
638
598
|
case 'fact/reviewed': {
|
|
639
599
|
/**
|
|
640
600
|
* **人审查过一条被推翻标记的事实**:撤回它,或判证据不可靠、维持原事实。
|
|
@@ -648,260 +608,6 @@ export function applyMutation(state, mutation) {
|
|
|
648
608
|
fact.review = { decision: mutation.decision === 'retracted' ? 'retracted' : 'kept', reason: mutation.reason ?? null, at, by: mutation.by ?? 'user' }
|
|
649
609
|
break
|
|
650
610
|
}
|
|
651
|
-
case 'worldline/prepared': {
|
|
652
|
-
const fork = next.forks.find((item) => item.id === mutation.fork)
|
|
653
|
-
if (fork === undefined) break
|
|
654
|
-
fork.tier = mutation.tier
|
|
655
|
-
fork.base = mutation.base ?? null
|
|
656
|
-
fork.degradedReason = mutation.reason ?? null
|
|
657
|
-
for (const entry of mutation.branches ?? []) {
|
|
658
|
-
const branch = fork.branches.find((item) => item.id === entry.id)
|
|
659
|
-
if (branch !== undefined) {
|
|
660
|
-
branch.worktree_path = entry.path
|
|
661
|
-
branch.git_branch = entry.branch
|
|
662
|
-
branch.worktree_removed = false
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
break
|
|
666
|
-
}
|
|
667
|
-
case 'fork/merge_skipped': {
|
|
668
|
-
const fork = next.forks.find((item) => item.id === mutation.fork)
|
|
669
|
-
if (fork === undefined) break
|
|
670
|
-
fork.mergeSkipped = { branch: mutation.branch ?? null, reason: mutation.reason ?? null }
|
|
671
|
-
break
|
|
672
|
-
}
|
|
673
|
-
case 'worldline/executing': {
|
|
674
|
-
const fork = next.forks.find((item) => item.id === mutation.fork)
|
|
675
|
-
const branch = fork?.branches.find((item) => item.id === mutation.branch)
|
|
676
|
-
if (branch === undefined) break
|
|
677
|
-
branch.execution = { child: mutation.child ?? null, capability: mutation.capability ?? null, ok: null, conclusion: null }
|
|
678
|
-
break
|
|
679
|
-
}
|
|
680
|
-
case 'worldline/executed': {
|
|
681
|
-
const fork = next.forks.find((item) => item.id === mutation.fork)
|
|
682
|
-
const branch = fork?.branches.find((item) => item.id === mutation.branch)
|
|
683
|
-
if (branch === undefined) break
|
|
684
|
-
branch.execution = { ...(branch.execution ?? { child: mutation.child ?? null, capability: null }), ok: mutation.ok === true, conclusion: mutation.conclusion ?? null, note: mutation.note ?? null }
|
|
685
|
-
break
|
|
686
|
-
}
|
|
687
|
-
case 'worldline/removed': {
|
|
688
|
-
const fork = next.forks.find((item) => item.id === mutation.fork)
|
|
689
|
-
const branch = fork?.branches.find((item) => item.id === mutation.branch)
|
|
690
|
-
if (branch === undefined) break
|
|
691
|
-
branch.worktree_removed = true
|
|
692
|
-
branch.kept_ref = mutation.kept_ref !== false
|
|
693
|
-
break
|
|
694
|
-
}
|
|
695
|
-
case 'fork/merged': {
|
|
696
|
-
const fork = next.forks.find((item) => item.id === mutation.fork)
|
|
697
|
-
if (fork === undefined) break
|
|
698
|
-
fork.merge = {
|
|
699
|
-
branch: mutation.branch,
|
|
700
|
-
commit: mutation.commit ?? null,
|
|
701
|
-
mode: mutation.mode ?? 'merged',
|
|
702
|
-
snapshotCommit: mutation.snapshot_commit ?? null,
|
|
703
|
-
// 临时采纳 = 分差不足以称结论(或由判断而来):它不是结论,是待复核的决定。
|
|
704
|
-
provisional: mutation.provisional === true,
|
|
705
|
-
by: mutation.by ?? 'metric',
|
|
706
|
-
decisionNote: mutation.decision_note ?? null,
|
|
707
|
-
margin: mutation.margin ?? null,
|
|
708
|
-
}
|
|
709
|
-
fork.mergeConflict = null
|
|
710
|
-
break
|
|
711
|
-
}
|
|
712
|
-
case 'fork/merge_conflict': {
|
|
713
|
-
const fork = next.forks.find((item) => item.id === mutation.fork)
|
|
714
|
-
if (fork === undefined) break
|
|
715
|
-
fork.mergeConflict = { branch: mutation.branch, winner: mutation.winner ?? null, margin: mutation.margin ?? null, detail: mutation.detail ?? null }
|
|
716
|
-
break
|
|
717
|
-
}
|
|
718
|
-
case 'scout/dispatched': {
|
|
719
|
-
next.scouts.push({
|
|
720
|
-
id: mutation.id,
|
|
721
|
-
step: mutation.step,
|
|
722
|
-
plan: mutation.plan ?? null,
|
|
723
|
-
trigger: mutation.trigger ?? null,
|
|
724
|
-
child: mutation.child ?? null,
|
|
725
|
-
capability: mutation.capability ?? null,
|
|
726
|
-
// 任务身份:同一个身份已经正常回灌过的,再派时会被复用而不是重跑(幂等派遣)。
|
|
727
|
-
digest: mutation.digest ?? null,
|
|
728
|
-
conclusion: null,
|
|
729
|
-
note: null,
|
|
730
|
-
path: null,
|
|
731
|
-
at,
|
|
732
|
-
})
|
|
733
|
-
break
|
|
734
|
-
}
|
|
735
|
-
case 'scout/settled': {
|
|
736
|
-
const scout = next.scouts.find((item) => item.id === mutation.id)
|
|
737
|
-
if (scout !== undefined) {
|
|
738
|
-
scout.conclusion = mutation.conclusion ?? null
|
|
739
|
-
scout.note = mutation.note ?? null
|
|
740
|
-
scout.path = mutation.path ?? null
|
|
741
|
-
}
|
|
742
|
-
break
|
|
743
|
-
}
|
|
744
|
-
case 'fork/created': {
|
|
745
|
-
if (next.forks.some((fork) => fork.step === mutation.step)) break // 一步只分叉一次
|
|
746
|
-
next.forks.push({
|
|
747
|
-
id: mutation.id,
|
|
748
|
-
step: mutation.step,
|
|
749
|
-
plan: mutation.plan,
|
|
750
|
-
question: mutation.question,
|
|
751
|
-
decide_by: mutation.decide_by,
|
|
752
|
-
settled: false,
|
|
753
|
-
abandoned: false,
|
|
754
|
-
abandonReason: null,
|
|
755
|
-
/** 横评仲裁的判决(`fork/arbitrated`)与它的会话 id(面板可旁观)。 */
|
|
756
|
-
arbitration: null,
|
|
757
|
-
arbitrationSession: null,
|
|
758
|
-
/** 人在面板上的裁决(结构化记录,by:'user');null = 还没裁。 */
|
|
759
|
-
humanDecision: null,
|
|
760
|
-
verdict: null,
|
|
761
|
-
undecidable: null,
|
|
762
|
-
/**
|
|
763
|
-
* 算术算出来的推荐(`fork/recommended`):人裁决那条路上**唯一的输入**。
|
|
764
|
-
* 它是事实(系统算过),不是状态——分支秩不动,收敛仍然只走 ConvergeFork。
|
|
765
|
-
*/
|
|
766
|
-
recommended: null,
|
|
767
|
-
recommendMargin: null,
|
|
768
|
-
recommendProvisional: false,
|
|
769
|
-
recommendBy: null,
|
|
770
|
-
/**
|
|
771
|
-
* 采纳时**没能合并**的原因(`fork/merge_skipped`):面板据此说清「决定登记了,
|
|
772
|
-
* 赢家的产物要靠一次普通交付落位」。不转发它,这句话就永远说不出来。
|
|
773
|
-
*/
|
|
774
|
-
mergeSkipped: null,
|
|
775
|
-
at,
|
|
776
|
-
branches: (mutation.options ?? []).map((option) => ({
|
|
777
|
-
id: option.id,
|
|
778
|
-
label: option.label,
|
|
779
|
-
approach: option.approach,
|
|
780
|
-
done_criteria: option.done_criteria,
|
|
781
|
-
workspace: option.workspace ?? null,
|
|
782
|
-
artifacts: option.artifacts ?? [],
|
|
783
|
-
level: option.level ?? 'L0',
|
|
784
|
-
status: 'exploring',
|
|
785
|
-
card_path: null,
|
|
786
|
-
evaluator_session: null,
|
|
787
|
-
humanDecision: null,
|
|
788
|
-
reading: null,
|
|
789
|
-
validity: null,
|
|
790
|
-
verdict: null,
|
|
791
|
-
basis: null,
|
|
792
|
-
evaluator: null,
|
|
793
|
-
evidence: null,
|
|
794
|
-
at: null,
|
|
795
|
-
})),
|
|
796
|
-
})
|
|
797
|
-
break
|
|
798
|
-
}
|
|
799
|
-
case 'branch/delivered': {
|
|
800
|
-
const fork = next.forks.find((item) => item.id === mutation.fork)
|
|
801
|
-
const branch = fork?.branches.find((item) => item.id === mutation.branch)
|
|
802
|
-
if (branch === undefined) break
|
|
803
|
-
if ((BRANCH_RANK[branch.status] ?? 0) < BRANCH_RANK.evaluated) branch.status = 'evaluated'
|
|
804
|
-
branch.reading = mutation.reading ?? null
|
|
805
|
-
branch.validity = mutation.validity ?? null
|
|
806
|
-
branch.verdict = mutation.verdict ?? null
|
|
807
|
-
branch.basis = mutation.basis ?? null
|
|
808
|
-
branch.evaluator = mutation.evaluator ?? null
|
|
809
|
-
branch.evidence = mutation.evidence ?? null
|
|
810
|
-
// 评估卡与评估者会话:仲裁用卡(注入),面板用会话(旁观入口)。
|
|
811
|
-
branch.card_path = mutation.card_path ?? null
|
|
812
|
-
branch.evaluator_session = mutation.evaluator_session ?? null
|
|
813
|
-
branch.at = at
|
|
814
|
-
break
|
|
815
|
-
}
|
|
816
|
-
case 'fork/recommended': {
|
|
817
|
-
/**
|
|
818
|
-
* 算术算出来的推荐。**只记事实,不动状态**:分支秩不变、`settled` 不变——
|
|
819
|
-
* 采纳永远还是要走 `ConvergeFork`(或人的一次裁决)。它存在的理由只有一个:
|
|
820
|
-
* 人门卡与面板读的是投影里的这个字段,不落账它们就只能写「推荐:无」。
|
|
821
|
-
*/
|
|
822
|
-
const fork = next.forks.find((item) => item.id === mutation.fork)
|
|
823
|
-
if (fork === undefined) break
|
|
824
|
-
fork.recommended = mutation.branch ?? null
|
|
825
|
-
fork.recommendMargin = mutation.margin ?? null
|
|
826
|
-
fork.recommendProvisional = mutation.provisional === true
|
|
827
|
-
fork.recommendBy = mutation.by ?? null
|
|
828
|
-
break
|
|
829
|
-
}
|
|
830
|
-
case 'fork/converged': {
|
|
831
|
-
const fork = next.forks.find((item) => item.id === mutation.fork)
|
|
832
|
-
if (fork === undefined) break
|
|
833
|
-
fork.settled = true
|
|
834
|
-
fork.undecidable = null
|
|
835
|
-
fork.verdict = { winner: mutation.winner, margin: mutation.margin ?? null, tie: mutation.tie === true, metric: mutation.metric ?? null, direction: mutation.direction ?? null }
|
|
836
|
-
for (const branch of fork.branches) {
|
|
837
|
-
if ((BRANCH_RANK[branch.status] ?? 0) >= BRANCH_RANK.adopted) continue // 一旦采纳,状态冻结
|
|
838
|
-
branch.status = branch.id === mutation.winner ? 'adopted' : 'pruned'
|
|
839
|
-
}
|
|
840
|
-
break
|
|
841
|
-
}
|
|
842
|
-
case 'fork/arbitration_dispatched': {
|
|
843
|
-
const fork = next.forks.find((item) => item.id === mutation.fork)
|
|
844
|
-
if (fork === undefined) break
|
|
845
|
-
fork.arbitrationSession = mutation.arbiter_session ?? null
|
|
846
|
-
break
|
|
847
|
-
}
|
|
848
|
-
case 'fork/arbitrated': {
|
|
849
|
-
const fork = next.forks.find((item) => item.id === mutation.fork)
|
|
850
|
-
if (fork === undefined) break
|
|
851
|
-
// 判决写进节点(唯一真相仍是投影):采纳与否由内核重判一次决定,这里只记事实。
|
|
852
|
-
fork.arbitration = {
|
|
853
|
-
winner: mutation.winner ?? null,
|
|
854
|
-
ranking: mutation.ranking ?? [],
|
|
855
|
-
reason: mutation.reason ?? '',
|
|
856
|
-
confidence: mutation.confidence ?? 'low',
|
|
857
|
-
arbiter_session: mutation.arbiter_run_id ?? null,
|
|
858
|
-
}
|
|
859
|
-
break
|
|
860
|
-
}
|
|
861
|
-
case 'fork/undecidable': {
|
|
862
|
-
const fork = next.forks.find((item) => item.id === mutation.fork)
|
|
863
|
-
if (fork === undefined) break
|
|
864
|
-
fork.undecidable = { code: mutation.code, reason: mutation.reason, readings: mutation.readings ?? [] }
|
|
865
|
-
break
|
|
866
|
-
}
|
|
867
|
-
case 'fork/abandoned': {
|
|
868
|
-
const fork = next.forks.find((item) => item.id === mutation.fork)
|
|
869
|
-
if (fork === undefined) break
|
|
870
|
-
fork.abandoned = true
|
|
871
|
-
fork.abandonReason = mutation.reason ?? null
|
|
872
|
-
break
|
|
873
|
-
}
|
|
874
|
-
case 'continuation/set': {
|
|
875
|
-
/**
|
|
876
|
-
* 续跑窗口的账。四种状态就是「我们说过的话」的全部:
|
|
877
|
-
* · `armed` —— 我们布了防(或按策略恢复了它):此刻它在为我们跑。
|
|
878
|
-
* · `paused` —— **我们**按下的暂停,`why` 是真实理由(等裁决 / 等人门 / 阶段边界)。
|
|
879
|
-
* · `stopped` —— 我们收的兵(目标达成 / 如实放弃 / 计划触礁)。
|
|
880
|
-
* · `withdrawn` —— 它不在了,而**不是我们清的**(我们的清除永远与建同拍):
|
|
881
|
-
* 这是平台上唯一说得出口的解释,所以它也只说这些,不说「谁」干的。
|
|
882
|
-
* 一条记录:**窗口在会话里是单数**,所以后一条覆盖前一条,不留历史(历史在会话日志里)。
|
|
883
|
-
*/
|
|
884
|
-
next.continuation = {
|
|
885
|
-
state: typeof mutation.state === 'string' ? mutation.state : 'unknown',
|
|
886
|
-
goal: mutation.goal ?? null,
|
|
887
|
-
target: mutation.target ?? null,
|
|
888
|
-
why: mutation.why ?? null,
|
|
889
|
-
/**
|
|
890
|
-
* 平台对象上那句身份文本的**最后一次观测值**:我们写下它时记我们的,
|
|
891
|
-
* 别人改写之后重记他的。判据是「平台上的文本 === 这条记录」⇒ 还是我们的。
|
|
892
|
-
*/
|
|
893
|
-
label: mutation.label ?? null,
|
|
894
|
-
at,
|
|
895
|
-
}
|
|
896
|
-
break
|
|
897
|
-
}
|
|
898
|
-
/**
|
|
899
|
-
* **领域词汇的六个事件**:接纳 / 版本化修订 / 黏性废止(概念与谓词各三条)。
|
|
900
|
-
*
|
|
901
|
-
* 折法在这里只做解释:把事件折成 `lexicon`。校验(引用是否存在、形状对不对、
|
|
902
|
-
* 语义变化有没有偷偷走修订)全部发生在**落账之前**——预设侧的工具与宿主路由
|
|
903
|
-
* 用的是 `domain-language.js` 的同一份判据;折法不重复判一遍,否则两份判据必然漂。
|
|
904
|
-
*/
|
|
905
611
|
case 'ontology/term_added':
|
|
906
612
|
case 'ontology/predicate_added':
|
|
907
613
|
case 'ontology/term_revised':
|
|
@@ -970,19 +676,6 @@ export function applyMutation(state, mutation) {
|
|
|
970
676
|
})
|
|
971
677
|
break
|
|
972
678
|
}
|
|
973
|
-
/**
|
|
974
|
-
* **跳级理由**(`level/skipped`):这一级为什么在本项目里不适用。
|
|
975
|
-
*
|
|
976
|
-
* 折法只记事实,不改派生:`derive()` 把被理由覆盖的层从 `untouchedLevels` 里减掉,
|
|
977
|
-
* 于是「跳级没理由」这条缺口会自动消失——理由本身就是它的出口。
|
|
978
|
-
*/
|
|
979
|
-
case 'level/skipped': {
|
|
980
|
-
const hypothesis = next.hypotheses.find((item) => item.id === mutation.hypothesis)
|
|
981
|
-
if (hypothesis === undefined) break
|
|
982
|
-
hypothesis.skips = Array.isArray(hypothesis.skips) ? hypothesis.skips : []
|
|
983
|
-
hypothesis.skips.push({ levels: Array.isArray(mutation.levels) ? mutation.levels.slice() : [], reason: mutation.reason ?? null, at })
|
|
984
|
-
break
|
|
985
|
-
}
|
|
986
679
|
/**
|
|
987
680
|
* **判据修订**(`criteria/revised`):改「怎样算完成」这件事本身。
|
|
988
681
|
*
|
|
@@ -1061,6 +754,16 @@ function stampAt(mutations, time) {
|
|
|
1061
754
|
* 宿主读已发布 V3 日志时把旧形状抬升成 `plugin:clearai`;但事件被**直接**喂进这个纯函数时
|
|
1062
755
|
* (测试、旧导出、重放工具)仍带着旧形状,所以两侧都认。
|
|
1063
756
|
*/
|
|
757
|
+
/** 人写的一条消息是哪种语言(插件消息、续跑提示不算)。 */
|
|
758
|
+
function humanLanguage(message) {
|
|
759
|
+
if (message === null || typeof message !== 'object' || message.role !== 'user') return null
|
|
760
|
+
const kind = message.source?.kind
|
|
761
|
+
if (kind !== undefined && kind !== 'user') return null
|
|
762
|
+
const text = messageText(message)
|
|
763
|
+
if (text.startsWith(HUMAN_GATE_MARK)) return null
|
|
764
|
+
return detectLanguage(text)
|
|
765
|
+
}
|
|
766
|
+
|
|
1064
767
|
function isClearaiSource(source) {
|
|
1065
768
|
if (source === null || typeof source !== 'object' || !Array.isArray(source.sections)) return false
|
|
1066
769
|
return source.kind === 'plugin:clearai' || (source.kind === 'plugin' && source.plugin === 'clearai')
|
|
@@ -1076,6 +779,13 @@ function isClearaiSource(source) {
|
|
|
1076
779
|
*/
|
|
1077
780
|
export function applyEvent(state, event) {
|
|
1078
781
|
if (event === null || typeof event !== 'object') return state
|
|
782
|
+
// 人说话用的语言:每一条人写的消息都可能改判(没有信号就不改)。这一拍折出来的文字(本体问题等)也用它。
|
|
783
|
+
const spoken = event.type === 'user/message' ? humanLanguage(event.data) : null
|
|
784
|
+
const base = spoken !== null && spoken !== state?.language ? { ...state, language: spoken } : state
|
|
785
|
+
return withLanguage(base?.language, () => foldEvent(base, event))
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
function foldEvent(state, event) {
|
|
1079
789
|
if (event.type === 'user/message') {
|
|
1080
790
|
// 内核观察到的事实(候选技能、采纳记录、合并目录)走**插件消息的结构化 section**——
|
|
1081
791
|
// 记在会话日志里,所以状态仍然可以从日志重放出来,而不是靠一句散文。
|
|
@@ -1098,8 +808,6 @@ export function applyEvent(state, event) {
|
|
|
1098
808
|
* 原来找到目录就 return,于是同一条消息里的**运行档被吃掉**,
|
|
1099
809
|
* 表现是投影里的 effective 档永远停在「还没定档」,而机制那边早就按新档跑了。
|
|
1100
810
|
*/
|
|
1101
|
-
const brain = source.sections.find((section) => section?.name === 'clearai/brain')
|
|
1102
|
-
const tier = source.sections.find((section) => section?.name === 'clearai/autonomy')
|
|
1103
811
|
/**
|
|
1104
812
|
* 内核在**回合之间**观察到的事实(目前:世界线执行者跑完)。
|
|
1105
813
|
* 与工具结果里的 `meta.mutations` **同形**,所以直接走同一个 `applyMutations`——
|
|
@@ -1120,28 +828,9 @@ export function applyEvent(state, event) {
|
|
|
1120
828
|
}
|
|
1121
829
|
}
|
|
1122
830
|
const facts = source.sections.find((section) => section?.name === 'clearai/mutations')
|
|
1123
|
-
if (
|
|
831
|
+
if (facts !== undefined) {
|
|
1124
832
|
let next = clone(state)
|
|
1125
833
|
let touched = false
|
|
1126
|
-
if (brain !== undefined && typeof brain.text === 'string') {
|
|
1127
|
-
try {
|
|
1128
|
-
const payload = JSON.parse(brain.text)
|
|
1129
|
-
if (Array.isArray(payload?.candidates)) next.brainCandidates = payload.candidates
|
|
1130
|
-
if (payload?.overview !== undefined) next.brain = payload.overview
|
|
1131
|
-
// 章程读数:只有真的变了才会出现在 payload 里(内核那侧按文件系统指纹去重)。
|
|
1132
|
-
if (payload?.constitution !== undefined) next.constitution = payload.constitution
|
|
1133
|
-
// 合并目录:内核从宿主的 skills 服务取的那张表(变了才发)。
|
|
1134
|
-
if (payload?.catalog !== null && typeof payload?.catalog === 'object' && Array.isArray(payload.catalog.entries)) {
|
|
1135
|
-
next.skillCatalog = { complete: payload.catalog.complete === true, entries: payload.catalog.entries, at: typeof event.time === 'number' ? event.time : null }
|
|
1136
|
-
}
|
|
1137
|
-
for (const name of Array.isArray(payload?.promoted) ? payload.promoted : []) {
|
|
1138
|
-
next.skillPromotions = [...(next.skillPromotions ?? []), { name, by: 'user', at: typeof event.time === 'number' ? event.time : Date.now() }]
|
|
1139
|
-
}
|
|
1140
|
-
touched = true
|
|
1141
|
-
} catch {
|
|
1142
|
-
// 坏 payload:如实少一条外脑事实,但不吞掉同一条消息里别的事实。
|
|
1143
|
-
}
|
|
1144
|
-
}
|
|
1145
834
|
if (facts !== undefined && typeof facts.text === 'string') {
|
|
1146
835
|
try {
|
|
1147
836
|
const payload = JSON.parse(facts.text)
|
|
@@ -1153,61 +842,18 @@ export function applyEvent(state, event) {
|
|
|
1153
842
|
// 同上:坏 payload 不当事实,也不影响别的。
|
|
1154
843
|
}
|
|
1155
844
|
}
|
|
1156
|
-
if (tier !== undefined && typeof tier.text === 'string') {
|
|
1157
|
-
try {
|
|
1158
|
-
const payload = JSON.parse(tier.text)
|
|
1159
|
-
const value = String(payload?.value ?? '')
|
|
1160
|
-
if (value === 'attended' || value === 'unattended') {
|
|
1161
|
-
next.autonomy = {
|
|
1162
|
-
...(next.autonomy ?? {}),
|
|
1163
|
-
effective: { value, preset: String(payload?.preset ?? value), source: payload?.source === 'session' ? 'session' : 'preset', at: typeof event.time === 'number' ? event.time : null },
|
|
1164
|
-
}
|
|
1165
|
-
touched = true
|
|
1166
|
-
}
|
|
1167
|
-
} catch {
|
|
1168
|
-
// 同上:坏 payload 不当事实,也不影响别的。
|
|
1169
|
-
}
|
|
1170
|
-
}
|
|
1171
845
|
if (touched) return next
|
|
1172
846
|
}
|
|
1173
847
|
}
|
|
1174
|
-
//
|
|
1175
|
-
// 世界线的裁决记在分叉上,由内核的 ConvergeFork/AbandonFork 落实(合并与清理是内核的活)。
|
|
848
|
+
// 旧日志里人在面板上按下的人门动作 → 照旧落成**事实**(面板已不再发)。
|
|
1176
849
|
const gate = parseHumanGate(event.data)
|
|
1177
|
-
|
|
1178
|
-
// 所以「人用过它」不是我们的推测,而是这条消息本身。插件与注入消息都不算(署名必须是人)。
|
|
1179
|
-
const quoted = source !== null && typeof source === 'object' && source.kind === 'user' ? invokedSkillNames(event.data) : []
|
|
1180
|
-
if (gate === null && quoted.length === 0) return state
|
|
850
|
+
if (gate === null) return state
|
|
1181
851
|
const at = typeof event.time === 'number' ? event.time : Date.now()
|
|
1182
852
|
const next = clone(state)
|
|
1183
|
-
for (const name of quoted) recordSkillUse(next, name, 'human', at, stepPointer(state))
|
|
1184
|
-
if (gate === null) return next
|
|
1185
|
-
// **旧日志容忍分支**(写入者已摘除,只有旧日志可能带着):人切运行档曾经是一条人门动作,旧会话里可能留着。
|
|
1186
|
-
// 读到它就照旧记成一条**人的事实**,让历史会话仍然读得通;当前面板上已经没有这个开关,
|
|
1187
|
-
// 新的会话不会再产生这一条。不要据此认为"现在还能切档"——要删这个分支得先确认没有旧日志。
|
|
1188
|
-
if (gate.action === 'set_autonomy') {
|
|
1189
|
-
const value = String(gate.value ?? '')
|
|
1190
|
-
if (!AUTONOMY_VALUES.includes(value)) return next
|
|
1191
|
-
next.autonomy = { ...(next.autonomy ?? {}), override: { value, at } }
|
|
1192
|
-
return next
|
|
1193
|
-
}
|
|
1194
853
|
/**
|
|
1195
854
|
* 人审查一条事实:标的在 `value`(面板送出的是事实 id),缘由在 `note`。
|
|
1196
855
|
* 已经审过的不再改(第一次决定为准,与计划授权那条同一条纪律)。
|
|
1197
856
|
*/
|
|
1198
|
-
/**
|
|
1199
|
-
* 人认可一次**临时采纳**:它是那道门唯一的机械出口。
|
|
1200
|
-
*
|
|
1201
|
-
* 「临时采纳」的语义是「分差不足以称结论」;原来它只能靠人说话,而门开着会按住续跑
|
|
1202
|
-
* ⇒ 什么都不做的话系统一直等。认可是**人的决定**,落一条 `by:'user'` 的确认留着痕迹;
|
|
1203
|
-
* 「要改判据」那条路仍然在(说一句话,模型重做一条世界线)。
|
|
1204
|
-
*/
|
|
1205
|
-
if (gate.action === 'confirm_provisional') {
|
|
1206
|
-
const provisionalFork = next.forks.find((item) => item.id === (gate.fork ?? null))
|
|
1207
|
-
if (provisionalFork?.merge?.provisional !== true || provisionalFork.merge.confirmed !== undefined) return next
|
|
1208
|
-
provisionalFork.merge = { ...provisionalFork.merge, confirmed: { at, by: 'user' } }
|
|
1209
|
-
return next
|
|
1210
|
-
}
|
|
1211
857
|
if (gate.action === 'retract_fact' || gate.action === 'keep_fact') {
|
|
1212
858
|
const fact = next.facts.find((item) => item.id === (gate.value ?? null))
|
|
1213
859
|
if (fact === undefined || fact.review !== undefined) return next
|
|
@@ -1237,31 +883,12 @@ export function applyEvent(state, event) {
|
|
|
1237
883
|
next.lexicon = applyLexiconMutation(next.lexicon, { t: kind, ...entry, by: 'user' }, at)
|
|
1238
884
|
return next
|
|
1239
885
|
}
|
|
1240
|
-
const fork = next.forks.find((item) => item.id === (gate.fork ?? null))
|
|
1241
|
-
if (fork === undefined) return next
|
|
1242
|
-
fork.humanDecision = {
|
|
1243
|
-
action: gate.action,
|
|
1244
|
-
branch: gate.branch ?? null,
|
|
1245
|
-
note: gate.note ?? null,
|
|
1246
|
-
at,
|
|
1247
|
-
}
|
|
1248
886
|
return next
|
|
1249
887
|
}
|
|
1250
888
|
if (event.type === 'tool/call') {
|
|
1251
889
|
const data = event.data ?? {}
|
|
1252
890
|
if (typeof data.name !== 'string') return state
|
|
1253
891
|
const next = clone(state)
|
|
1254
|
-
if (data.name === 'skill') {
|
|
1255
|
-
// 模型加载技能 = 原生 `skill` 工具的一次调用。工具名与参数形状照原样认,不猜别的写法。
|
|
1256
|
-
let args = null
|
|
1257
|
-
try {
|
|
1258
|
-
args = typeof data.arguments === 'string' ? JSON.parse(data.arguments) : data.arguments
|
|
1259
|
-
} catch {
|
|
1260
|
-
args = null
|
|
1261
|
-
}
|
|
1262
|
-
const name = args === null || typeof args !== 'object' || typeof args.name !== 'string' ? '' : args.name.trim()
|
|
1263
|
-
if (name !== '') recordSkillUse(next, name, 'model', typeof event.time === 'number' ? event.time : 0, stepPointer(state))
|
|
1264
|
-
}
|
|
1265
892
|
if (data.name === 'write' || data.name === 'edit') {
|
|
1266
893
|
let args = null
|
|
1267
894
|
try {
|
|
@@ -1275,11 +902,6 @@ export function applyEvent(state, event) {
|
|
|
1275
902
|
if (next.written.length > MAX_WRITTEN) next.written.splice(0, next.written.length - MAX_WRITTEN)
|
|
1276
903
|
}
|
|
1277
904
|
}
|
|
1278
|
-
/**
|
|
1279
|
-
* 会改工作区的那几件工具都记一笔:`write`/`edit` 是直接的,`bash`/`pwsh` 是间接的
|
|
1280
|
-
* (脚本自己产出文件,内核看不见)。计数只增,判据留给用它的地方。
|
|
1281
|
-
*/
|
|
1282
|
-
if (WRITE_CAPABLE_TOOLS.has(data.name)) next.writeCalls = (next.writeCalls ?? 0) + 1
|
|
1283
905
|
next.inFlight = VERDICT_WAITERS.has(data.name) ? { name: data.name, callId: data.callId ?? null, at: typeof event.time === 'number' ? event.time : 0 } : null
|
|
1284
906
|
return next
|
|
1285
907
|
}
|
|
@@ -1314,10 +936,30 @@ export function applyEvent(state, event) {
|
|
|
1314
936
|
/** 假设的终态:到了这里就不再是「还要继续做的活」。 */
|
|
1315
937
|
const TERMINAL_HYPOTHESIS = new Set(['refuted', 'superseded', 'retracted'])
|
|
1316
938
|
|
|
939
|
+
/**
|
|
940
|
+
* **断言主体落图了吗**:一条判断的断言里,主体还不是实体图节点的那些(去重)。
|
|
941
|
+
*
|
|
942
|
+
* 节点有几种来路,都带出处或独立裁决:实体文件(`clear/ontology/entities/`)里的实体与它们的关系、升格事实里的主体,
|
|
943
|
+
* 以及它们以 instance 形态引出的宾语。`derive()` 把读数挂在每条判断上(`unlanded`),
|
|
944
|
+
* 缺口 ③ 与 `Conclude` 的实体门读的都是它——「卡上说落了」与「门说没落」不会出现两种读数。
|
|
945
|
+
*/
|
|
946
|
+
function subjectsOffGraph(hypothesis, nodes) {
|
|
947
|
+
const seen = new Map()
|
|
948
|
+
for (const assertion of Array.isArray(hypothesis?.assertions) ? hypothesis.assertions : []) {
|
|
949
|
+
const id = String(assertion?.subject?.id ?? '').trim()
|
|
950
|
+
const type = String(assertion?.subject?.type ?? '').trim()
|
|
951
|
+
if (id === '' || type === '') continue
|
|
952
|
+
const key = `${type}|${id}`
|
|
953
|
+
if (seen.has(key) || nodes.has(key)) continue
|
|
954
|
+
seen.set(key, { id, type })
|
|
955
|
+
}
|
|
956
|
+
return [...seen.values()]
|
|
957
|
+
}
|
|
958
|
+
|
|
1317
959
|
/**
|
|
1318
960
|
* **知识模式(分诊)与缺口读数。**
|
|
1319
961
|
*
|
|
1320
|
-
* 判据是**结构的,不是词法的**:这里不猜「这句话像不像研究任务」。立约(`
|
|
962
|
+
* 判据是**结构的,不是词法的**:这里不猜「这句话像不像研究任务」。立约(`Frame`)并用
|
|
1321
963
|
* 相互竞争的假设登记它,是模型自己已经做出的那次承诺——它意味着这件事要跨多轮、要有依据、
|
|
1322
964
|
* 要有可复核的结论。日常问答从不立约,于是从不进这一档。用词面启发式去猜任务类型,
|
|
1323
965
|
* 正是真跑里评估者抓到的那类「硬编码比例」的老路:猜错了没人能复核,而结构判据可以。
|
|
@@ -1342,48 +984,11 @@ export function deriveKnowledge(state, hypotheses, factRows, lexicon) {
|
|
|
1342
984
|
const gaps = []
|
|
1343
985
|
if (mode === 'knowledge') {
|
|
1344
986
|
/**
|
|
1345
|
-
*
|
|
1346
|
-
*
|
|
1347
|
-
*
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
gaps.push({
|
|
1351
|
-
code: 'no_language',
|
|
1352
|
-
count: registered.length,
|
|
1353
|
-
detail: `${registered.length} 条在验命题,但还没有任何概念与谓词:换一轮只能靠重读散文取用它们`,
|
|
1354
|
-
nextAction: '先 RegisterTerm 立词,再 RegisterPredicate 说清它们之间是什么关系',
|
|
1355
|
-
})
|
|
1356
|
-
}
|
|
1357
|
-
/**
|
|
1358
|
-
* ② 只有散文主张的命题:断言是可选的(「加法,不是门槛」),所以这里**不是违规**,
|
|
1359
|
-
* 而是「这条主张还不能被机器比对」。只算非终态的:被推翻/被替代的不再欠这一笔。
|
|
1360
|
-
*/
|
|
1361
|
-
const proseOnly = registered.filter((item) => !Array.isArray(item.assertions) || item.assertions.length === 0)
|
|
1362
|
-
if (proseOnly.length > 0) {
|
|
1363
|
-
gaps.push({
|
|
1364
|
-
code: 'prose_only_claims',
|
|
1365
|
-
count: proseOnly.length,
|
|
1366
|
-
detail: `${proseOnly.length} 条在验命题只有散文主张:两条结论是不是在说同一件事,只能靠重读判断`,
|
|
1367
|
-
nextAction: '用 SetGoal 的修订把这条主张写成断言(主词–谓词–宾语),引用已登记的 id',
|
|
1368
|
-
})
|
|
1369
|
-
}
|
|
1370
|
-
/**
|
|
1371
|
-
* ③ **升格时没带断言**。只算 0.2.0 那条路走出来的事实(`hypothesis` 已关联)——
|
|
1372
|
-
* 更早的事实补不上断言(见已知缺口),把它们算成欠账就是一条永远还不掉的抱怨。
|
|
1373
|
-
*/
|
|
1374
|
-
const unstructured = factRows.filter((fact) => typeof fact.hypothesis === 'string' && fact.hypothesis !== '' && (!Array.isArray(fact.assertions) || fact.assertions.length === 0))
|
|
1375
|
-
if (unstructured.length > 0) {
|
|
1376
|
-
gaps.push({
|
|
1377
|
-
code: 'unstructured_facts',
|
|
1378
|
-
count: unstructured.length,
|
|
1379
|
-
detail: `${unstructured.length} 条已升格事实没有断言:它们进不了实体图,也不能按概念取用`,
|
|
1380
|
-
nextAction: '下次升格时带上 assertions;这条老事实的形态靠新一次升格补',
|
|
1381
|
-
})
|
|
1382
|
-
}
|
|
1383
|
-
/**
|
|
1384
|
-
* ④ **从没被证据碰过的命题**。只在这次会话已经真的跑出过证据之后才报——计划刚立、
|
|
1385
|
-
* 一步都还没走时,所有假设都是「没碰过」,那是正常的起点而不是缺口。
|
|
1386
|
-
* 它和结案时那条 `unjudged` 是同一条先例:**不逼裁决,但不许把「没看过」写成「没问题」**。
|
|
987
|
+
* 缺口只留三种,都是「这一轮的结论还不能被复用」的直接读数,每种都指得到今天就能补的动作。
|
|
988
|
+
*
|
|
989
|
+
* ① **从没被证据碰过的判断**。只在这次会话已经真的跑出过证据之后才报——计划刚立、
|
|
990
|
+
* 一步都还没走时,所有判断都是「没碰过」,那是正常的起点而不是缺口。
|
|
991
|
+
* 不逼裁决,但不许把「没看过」写成「没问题」。
|
|
1387
992
|
*/
|
|
1388
993
|
if (Array.isArray(state?.evidence) && state.evidence.length > 0) {
|
|
1389
994
|
const untouched = registered.filter((item) => item.refutations + item.inconclusive === 0 && item.supportedLevel === null)
|
|
@@ -1391,89 +996,57 @@ export function deriveKnowledge(state, hypotheses, factRows, lexicon) {
|
|
|
1391
996
|
gaps.push({
|
|
1392
997
|
code: 'untouched_claims',
|
|
1393
998
|
count: untouched.length,
|
|
1394
|
-
detail: `${untouched.length}
|
|
1395
|
-
nextAction: '给它派一个带 tests 的步骤并交付:支持 / 推翻 / 无法判定都算碰过',
|
|
999
|
+
detail: tr(`${untouched.length} 条验证中的判断还没有任何证据碰过(支持 / 推翻 / 不确定都算碰过)`, `${untouched.length} judgments under test have not been touched by any evidence (support, refute and inconclusive all count)`),
|
|
1000
|
+
nextAction: tr('给它派一个带 tests 的步骤并交付:支持 / 推翻 / 无法判定都算碰过', 'Give it a step with tests and deliver it: support, refute and inconclusive all count'),
|
|
1396
1001
|
})
|
|
1397
1002
|
}
|
|
1398
1003
|
}
|
|
1399
1004
|
/**
|
|
1400
|
-
*
|
|
1401
|
-
*
|
|
1402
|
-
*
|
|
1403
|
-
* 为什么必须有这一条:实体层从前是升格的副产品,于是「断言齐备、门槛也放行」却
|
|
1404
|
-
* 因为另一条无关判据被卡住时,卡上四项全绿而**实体图是空的**——「看得见的边界」
|
|
1405
|
-
* 在这里漏过一整层。诚实出口是两条:登记并落断言,或如实说清不值得留下形态。
|
|
1406
|
-
*/
|
|
1407
|
-
const assertions = registered.flatMap((item) => (Array.isArray(item.assertions) ? item.assertions : []))
|
|
1408
|
-
const wantedKeys = new Set(assertions.map((assertion) => `${String(assertion?.subject?.type ?? '')}|${String(assertion?.subject?.id ?? '')}`).filter((key) => key !== '|'))
|
|
1409
|
-
/**
|
|
1410
|
-
* **判据是"每一个主体都落地了吗",不是"图上有没有东西"。**
|
|
1411
|
-
*
|
|
1412
|
-
* 弱判据(实体层节点数 === 0)有一个便宜的绕法:随便登记一个**无关**节点,
|
|
1413
|
-
* 缺口就消失,而真正该落地的那几个主体仍然只在命题上。于是这道门形同虚设。
|
|
1414
|
-
* 所以这里逐个主体判它有没有落成实体层节点,`count` 就是**还没落地的个数**。
|
|
1005
|
+
* ② 只有散文主张的判断:断言是可选的,所以这里**不是违规**,也不拦结案,
|
|
1006
|
+
* 而是「这条主张还不能被机器比对、下一轮也按概念取不到」。只算非终态的。
|
|
1415
1007
|
*/
|
|
1008
|
+
const proseOnly = registered.filter((item) => !Array.isArray(item.assertions) || item.assertions.length === 0)
|
|
1009
|
+
if (proseOnly.length > 0) {
|
|
1010
|
+
gaps.push({
|
|
1011
|
+
code: 'prose_only_claims',
|
|
1012
|
+
count: proseOnly.length,
|
|
1013
|
+
detail: tr(`${proseOnly.length} 条验证中的判断只有散文主张:两条结论是不是在说同一件事,只能靠重读判断`, `${proseOnly.length} judgments under test are prose only: whether two conclusions say the same thing can only be judged by rereading them`),
|
|
1014
|
+
nextAction: tr('先在 clear/ontology/ 下写概念、关系与主体的实体文件,再用 Frame 修订把主张写成断言(主词–谓词–宾语)', 'First write concept, relation and subject entity files under clear/ontology/, then revise with Frame to turn the claim into assertions (subject–predicate–object)'),
|
|
1015
|
+
})
|
|
1016
|
+
}
|
|
1416
1017
|
/**
|
|
1417
|
-
*
|
|
1018
|
+
* ③ **断言主体没落图**。判据与 `Conclude` 的实体门是同一条(每条判断上的 `unlanded`):
|
|
1019
|
+
* 断言的主体是不是实体图上带出处的节点。逐个主体判,`count` 就是还没落地的个数——
|
|
1020
|
+
* 随便登记一个无关节点不会让它消失。
|
|
1418
1021
|
*
|
|
1419
|
-
*
|
|
1420
|
-
*
|
|
1421
|
-
* 一条边要么来自带出处的实体断言(`Assert`),要么来自过了独立裁决的升格事实。
|
|
1022
|
+
* 为什么看节点、不看边:边由升格本身落下(事实带着它的断言进图),
|
|
1023
|
+
* 要求在升格之前另在实体文件里把同一句话再写一遍,只是让模型重复劳动。
|
|
1422
1024
|
*/
|
|
1423
|
-
const
|
|
1424
|
-
const landedKeys = new Set(graph.edges.filter((edge) => edge.kind === 'assertion').map((edge) => String(edge.from)))
|
|
1425
|
-
const unlanded = [...wantedKeys].filter((key) => !landedKeys.has(`term:${key}`) && !landedKeys.has(key))
|
|
1025
|
+
const unlanded = [...new Map(registered.flatMap((item) => item.unlanded ?? []).map((item) => [`${item.type}|${item.id}`, item])).values()]
|
|
1426
1026
|
if (unlanded.length > 0) {
|
|
1427
1027
|
gaps.push({
|
|
1428
1028
|
code: 'entities_unlanded',
|
|
1429
1029
|
count: unlanded.length,
|
|
1430
|
-
detail:
|
|
1431
|
-
|
|
1030
|
+
detail: tr(
|
|
1031
|
+
`${unlanded.length} 个断言主体还没有落到实体图(${unlanded.map((item) => `${item.type}|${item.id}`).join('、')}):断言只挂在命题上,不构成「已知」`,
|
|
1032
|
+
`${unlanded.length} assertion subjects are not on the entity graph yet (${unlanded.map((item) => `${item.type}|${item.id}`).join(', ')}): the assertions hang on the proposition only and are not "known"`,
|
|
1033
|
+
),
|
|
1034
|
+
nextAction: tr(
|
|
1035
|
+
'给这些主体各写一个实体文件(clear/ontology/entities/<id>.json,带类型与出处);确实不值得留下形态就把断言从判断上拿掉(Frame 修订)',
|
|
1036
|
+
'Write an entity file for each subject (clear/ontology/entities/<id>.json, with type and source); if one is really not worth keeping, drop the assertion from the judgment (Frame revision)',
|
|
1037
|
+
),
|
|
1432
1038
|
})
|
|
1433
1039
|
}
|
|
1434
|
-
/**
|
|
1435
|
-
* ⑥ **跳级没有理由**。`derive()` 已经把写明理由的层从 `untouchedLevels` 里减掉,
|
|
1436
|
-
* 所以这里数出来的每一个都是真的欠一句解释的层——出口只有一条:
|
|
1437
|
-
* 用 `ExplainLevelSkip` 写清「为什么这一级在本项目里不适用」。
|
|
1438
|
-
*/
|
|
1439
|
-
const skipped = registered
|
|
1440
|
-
.map((item) => ({ id: item.id, levels: Array.isArray(item.untouchedLevels) ? item.untouchedLevels : [] }))
|
|
1441
|
-
.filter((item) => item.levels.length > 0)
|
|
1442
|
-
const skippedCount = skipped.reduce((sum, item) => sum + item.levels.length, 0)
|
|
1443
|
-
if (skippedCount > 0) {
|
|
1444
|
-
gaps.push({
|
|
1445
|
-
code: 'levels_skipped',
|
|
1446
|
-
count: skippedCount,
|
|
1447
|
-
detail: `${skippedCount} 处跳级没有理由(${skipped.map((item) => `命题 ${item.id} 缺 ${item.levels.join('/')}`).join(';')})`,
|
|
1448
|
-
nextAction: '用 ExplainLevelSkip 写明「为什么这一级在本项目里不适用」',
|
|
1449
|
-
})
|
|
1450
|
-
}
|
|
1451
|
-
/**
|
|
1452
|
-
* ⑦ **注册了却没人用的概念**。它和货架上「零引用的概念」那一节读**同一份**引用面
|
|
1453
|
-
* (`termUsage`),所以两处不会出现「货架说没人用、缺口说用了」这种两种读数。
|
|
1454
|
-
*/
|
|
1455
|
-
if (terms.length > 0 && registered.length > 0) {
|
|
1456
|
-
const usage = termUsage(lexicon, factRows, Array.isArray(state?.entityAssertions) ? state.entityAssertions : [])
|
|
1457
|
-
const orphans = terms.filter((term) => term.status !== 'deprecated' && (usage.get(term.id) ?? 0) === 0)
|
|
1458
|
-
if (orphans.length > 0) {
|
|
1459
|
-
gaps.push({
|
|
1460
|
-
code: 'orphan_terms',
|
|
1461
|
-
count: orphans.length,
|
|
1462
|
-
detail: `${orphans.length} 个概念没有任何结论引用它们(它们还只是约定,不是已知)`,
|
|
1463
|
-
nextAction: '要么在断言里用起来,要么在货架上如实标出「未被引用」',
|
|
1464
|
-
})
|
|
1465
|
-
}
|
|
1466
|
-
}
|
|
1467
1040
|
}
|
|
1468
1041
|
return {
|
|
1469
1042
|
mode,
|
|
1470
1043
|
/** 一句人话:为什么进了这一档(或为什么没进)。它进卡,所以不许写成术语。 */
|
|
1471
1044
|
why:
|
|
1472
1045
|
mode === 'knowledge'
|
|
1473
|
-
? `目标还开着,带着 ${registered.length}
|
|
1046
|
+
? tr(`目标还开着,带着 ${registered.length} 条登记过的命题——这是跨轮、要依据、要可复核结论的活`, `The goal is open with ${registered.length} registered propositions: work across turns that needs evidence and checkable conclusions`)
|
|
1474
1047
|
: open
|
|
1475
|
-
? '目标还开着,但没有登记命题:按普通任务推进'
|
|
1476
|
-
: '没有开着的目标:按普通任务推进',
|
|
1048
|
+
? tr('目标还开着,但没有登记命题:按普通任务推进', 'The goal is open but has no registered propositions: proceed as an ordinary task')
|
|
1049
|
+
: tr('没有开着的目标:按普通任务推进', 'No open goal: proceed as an ordinary task'),
|
|
1477
1050
|
gaps,
|
|
1478
1051
|
}
|
|
1479
1052
|
}
|
|
@@ -1481,10 +1054,10 @@ export function deriveKnowledge(state, hypotheses, factRows, lexicon) {
|
|
|
1481
1054
|
/**
|
|
1482
1055
|
* **知识预检(preflight)**:进入知识模式那一刻,把「已知」主动送到模型面前。
|
|
1483
1056
|
*
|
|
1484
|
-
*
|
|
1057
|
+
* 解决的问题:真跑里模型不查就开工——不是因为没有查询的入口,
|
|
1485
1058
|
* 而是因为**没人提醒它此刻该查**。提示词会被读成建议;卡里的读数不会。
|
|
1486
1059
|
* 于是这里把相关性判断做成投影:从当前目标与命题的文本出发,圈出**有界**的一组
|
|
1487
|
-
*
|
|
1060
|
+
* 已有词汇、事实与冲突,随运行态卡注入。模型只在这份摘要不够用时才需要去读全文。
|
|
1488
1061
|
*
|
|
1489
1062
|
* 三条纪律(与缺口读数同一套):
|
|
1490
1063
|
* · **只读**:不产生变更,不新增状态——它就是 \`derive\` 的另一个读面;
|
|
@@ -1705,12 +1278,12 @@ function factChain(context, fact) {
|
|
|
1705
1278
|
*/
|
|
1706
1279
|
function factHistory(fact, evidence) {
|
|
1707
1280
|
const events = []
|
|
1708
|
-
if (fact?.at !== undefined && fact?.at !== null) events.push({ kind: 'fact/promoted', at: fact.at, summary: `升格为事实(支持到 ${fact.level ?? '—'})` })
|
|
1281
|
+
if (fact?.at !== undefined && fact?.at !== null) events.push({ kind: 'fact/promoted', at: fact.at, summary: tr(`升格为事实(支持到 ${fact.level ?? '—'})`, `Promoted to fact (supported to ${fact.level ?? '—'})`) })
|
|
1709
1282
|
if (fact?.review !== undefined && fact?.review !== null) {
|
|
1710
1283
|
events.push({
|
|
1711
1284
|
kind: 'fact/reviewed',
|
|
1712
1285
|
at: fact.review.at ?? null,
|
|
1713
|
-
summary: fact.review.decision === 'retracted' ? '人审查后**撤回**(记录保留)' : '人审查后**维持**(判证据不可靠)',
|
|
1286
|
+
summary: fact.review.decision === 'retracted' ? tr('人审查后**撤回**(记录保留)', 'A person reviewed it and **retracted** it (the record is kept)') : tr('人审查后**维持**(判证据不可靠)', 'A person reviewed it and **kept** it (the evidence was judged unreliable)'),
|
|
1714
1287
|
reason: fact.review.reason ?? null,
|
|
1715
1288
|
})
|
|
1716
1289
|
}
|
|
@@ -1721,12 +1294,12 @@ function factHistory(fact, evidence) {
|
|
|
1721
1294
|
/** 词汇条目的留痕:登记 / 历次修订 / 废止。登记与修订本来就存在词条里(折法保留的)。 */
|
|
1722
1295
|
function entryHistory(entry, label) {
|
|
1723
1296
|
const events = []
|
|
1724
|
-
if (entry?.at !== undefined && entry?.at !== null) events.push({ kind: `${label}_added`, at: entry.at, summary: `登记(依据:${entry.basis ?? '—'})`, by: entry.by ?? null })
|
|
1297
|
+
if (entry?.at !== undefined && entry?.at !== null) events.push({ kind: `${label}_added`, at: entry.at, summary: tr(`登记(依据:${entry.basis ?? '—'})`, `Registered (basis: ${entry.basis ?? '—'})`), by: entry.by ?? null })
|
|
1725
1298
|
for (const revision of Array.isArray(entry?.revisions) ? entry.revisions : []) {
|
|
1726
|
-
events.push({ kind: `${label}_revised`, at: revision.at ?? null, summary: `修订到 v${revision.version ?? '?'}`, reason: revision.reason ?? null, by: revision.by ?? null })
|
|
1299
|
+
events.push({ kind: `${label}_revised`, at: revision.at ?? null, summary: tr(`修订到 v${revision.version ?? '?'}`, `Revised to v${revision.version ?? '?'}`), reason: revision.reason ?? null, by: revision.by ?? null })
|
|
1727
1300
|
}
|
|
1728
1301
|
if (entry?.deprecated !== undefined && entry?.deprecated !== null) {
|
|
1729
|
-
events.push({ kind: `${label}_deprecated`, at: entry.deprecated.at ?? null, summary: '废止(黏性终态,没有复活)', reason: entry.deprecated.reason ?? null, by: entry.deprecated.by ?? null })
|
|
1302
|
+
events.push({ kind: `${label}_deprecated`, at: entry.deprecated.at ?? null, summary: tr('废止(黏性终态,没有复活)', 'Deprecated (final; it does not come back)'), reason: entry.deprecated.reason ?? null, by: entry.deprecated.by ?? null })
|
|
1730
1303
|
}
|
|
1731
1304
|
return events.filter((event) => event.at !== null || event.kind.endsWith('_deprecated')).sort((left, right) => (left.at ?? 0) - (right.at ?? 0))
|
|
1732
1305
|
}
|
|
@@ -1784,7 +1357,7 @@ function inspectConcept(context, termId) {
|
|
|
1784
1357
|
history: entryHistory(term, 'term'),
|
|
1785
1358
|
actions: { canFilter: true, canExpand: true, canEdit: true },
|
|
1786
1359
|
/** 词汇是**约定**,不需要证据等级——把它说清楚,免得读的人以为它「没验」。 */
|
|
1787
|
-
note: '概念是约定,不是主张:它不带证据等级。用这个词写下的句子才需要。',
|
|
1360
|
+
note: tr('概念是约定,不是主张:它不带证据等级。用这个词写下的句子才需要。', 'A concept is a convention, not a claim: it carries no evidence level. Only sentences written with it do.'),
|
|
1788
1361
|
}
|
|
1789
1362
|
}
|
|
1790
1363
|
|
|
@@ -1805,7 +1378,7 @@ function inspectValueType(context, form) {
|
|
|
1805
1378
|
/** 值形态由系统固定,不进领域本体、也没有版本史——如实说空,不编一段。 */
|
|
1806
1379
|
history: [],
|
|
1807
1380
|
actions: { canFilter: true, canExpand: false, canEdit: false },
|
|
1808
|
-
note: '值形态由系统固定(statement / quantity / formula / code / reference),不独立治理,所以没有版本史。',
|
|
1381
|
+
note: tr('值形态由系统固定(statement / quantity / formula / code / reference),不独立治理,所以没有版本史。', 'Value forms are fixed by the system (statement / quantity / formula / code / reference); they are not governed separately, so they have no version history.'),
|
|
1809
1382
|
}
|
|
1810
1383
|
}
|
|
1811
1384
|
|
|
@@ -1849,7 +1422,7 @@ function inspectPredicate(context, predicateId) {
|
|
|
1849
1422
|
.map((conflict) => ({ predicate: conflict.predicate, subject: conflict.subject, sides: conflict.sides.map((side) => ({ fact: side.fact ?? null, value: side.value ?? null })) })),
|
|
1850
1423
|
history: entryHistory(predicate, 'predicate'),
|
|
1851
1424
|
actions: { canFilter: true, canExpand: true, canEdit: true },
|
|
1852
|
-
note: predicate.functional === true ? '单值谓词:同一主词上两条未撤回的确认事实取值不同时,系统给出一对冲突读数(只暴露,不裁决)。' : null,
|
|
1425
|
+
note: predicate.functional === true ? tr('单值谓词:同一主词上两条未撤回的确认事实取值不同时,系统给出一对冲突读数(只暴露,不裁决)。', 'Single-valued predicate: when two unretracted confirmed facts give different values for the same subject, the system reports a conflict pair (surfaced, not resolved).') : null,
|
|
1853
1426
|
}
|
|
1854
1427
|
}
|
|
1855
1428
|
|
|
@@ -1901,7 +1474,7 @@ function inspectInstance(context, key) {
|
|
|
1901
1474
|
.map((conflict) => ({ predicate: conflict.predicate, subject: conflict.subject, sides: conflict.sides.map((side) => ({ fact: side.fact ?? null, value: side.value ?? null })) })),
|
|
1902
1475
|
history: [],
|
|
1903
1476
|
actions: { canFilter: true, canExpand: true, canEdit: false },
|
|
1904
|
-
note: '实例由断言投影出来,不单独注册、也不做实体消解(同名即同节点);它没有自己的版本史。',
|
|
1477
|
+
note: tr('实例由断言投影出来,不单独注册、也不做实体消解(同名即同节点);它没有自己的版本史。', 'Instances are projected from assertions; they are not registered separately and not resolved (same name means same node); they have no version history of their own.'),
|
|
1905
1478
|
}
|
|
1906
1479
|
}
|
|
1907
1480
|
|
|
@@ -1942,7 +1515,7 @@ function inspectLiteral(context, key) {
|
|
|
1942
1515
|
conflicts: [],
|
|
1943
1516
|
history: [],
|
|
1944
1517
|
actions: { canFilter: true, canExpand: true, canEdit: false },
|
|
1945
|
-
note: '字面值是断言里的客体,由事实投影出来;它没有独立生命周期。',
|
|
1518
|
+
note: tr('字面值是断言里的客体,由事实投影出来;它没有独立生命周期。', 'A literal value is the object of an assertion, projected from facts; it has no lifecycle of its own.'),
|
|
1946
1519
|
}
|
|
1947
1520
|
}
|
|
1948
1521
|
|
|
@@ -1978,18 +1551,42 @@ function inspectEdge(context, edgeId) {
|
|
|
1978
1551
|
conflicts: chain.conflicts,
|
|
1979
1552
|
history: chain.history,
|
|
1980
1553
|
actions: { canFilter: true, canExpand: true, canEdit: false },
|
|
1981
|
-
note: predicate?.functional === true ? '这条边落在单值谓词上:同一主词出现第二个不同取值时会产生冲突(只暴露,不裁决)。' : null,
|
|
1554
|
+
note: predicate?.functional === true ? tr('这条边落在单值谓词上:同一主词出现第二个不同取值时会产生冲突(只暴露,不裁决)。', 'This edge uses a single-valued predicate: a second, different value for the same subject produces a conflict (surfaced, not resolved).') : null,
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
|
|
1559
|
+
/**
|
|
1560
|
+
* **可信度怎么变的**:按证据落账的先后重放一遍,每一笔记下「之前 → 之后」。
|
|
1561
|
+
* 面板「点开一条」的第二段读的就是它;分组判据仍只有 `trustOf` 一处,这里只是逐笔调用。
|
|
1562
|
+
* 升格(写进长期知识)和撤回不在这里——它们由读的一侧接在末尾(见 `view`)。
|
|
1563
|
+
*/
|
|
1564
|
+
function trustHistory(hypothesis, rows) {
|
|
1565
|
+
const history = [{ kind: 'proposed', at: hypothesis.at ?? null, to: 'testing' }]
|
|
1566
|
+
let replay = { status: 'proposed', supportedLevel: null, refutations: 0, inconclusive: 0 }
|
|
1567
|
+
const ordered = rows
|
|
1568
|
+
.map((item, index) => ({ item, index }))
|
|
1569
|
+
.sort((left, right) => (left.item.at ?? 0) - (right.item.at ?? 0) || left.index - right.index)
|
|
1570
|
+
.map(({ item }) => item)
|
|
1571
|
+
for (const item of ordered) {
|
|
1572
|
+
const from = trustOf(replay)
|
|
1573
|
+
if (item.verdict === 'refute') replay = { ...replay, refutations: replay.refutations + 1 }
|
|
1574
|
+
else if (item.verdict === 'inconclusive') replay = { ...replay, inconclusive: replay.inconclusive + 1 }
|
|
1575
|
+
else if (item.verdict === 'support' && LEVELS.indexOf(String(item.level ?? '').toUpperCase()) > LEVELS.indexOf(String(replay.supportedLevel ?? '').toUpperCase())) replay = { ...replay, supportedLevel: item.level }
|
|
1576
|
+
history.push({ kind: 'evidence', id: item.id ?? null, at: item.at ?? null, plan: item.plan ?? null, step: item.step ?? null, verdict: item.verdict, level: item.level ?? null, evaluator: item.evaluator ?? null, basis: item.basis ?? null, from, to: trustOf(replay) })
|
|
1982
1577
|
}
|
|
1578
|
+
return history
|
|
1983
1579
|
}
|
|
1984
1580
|
|
|
1985
1581
|
/** 五种值形态的名字与一句话解释(名字取自 `domain-language` 的枚举,这里只加给人读的说明)。 */
|
|
1986
|
-
const
|
|
1987
|
-
statement: '短陈述字符串',
|
|
1988
|
-
quantity: '数值 + 单位',
|
|
1989
|
-
formula: '公式源码(LaTeX;本版不做语义解析)',
|
|
1990
|
-
code: '指向工作区里真有的文件路径',
|
|
1991
|
-
reference: '外部引用(文献 / URL / 编号)',
|
|
1582
|
+
const VALUE_FORM_GLOSS_TEXT = {
|
|
1583
|
+
statement: ['短陈述字符串', 'A short statement string'],
|
|
1584
|
+
quantity: ['数值 + 单位', 'A number plus a unit'],
|
|
1585
|
+
formula: ['公式源码(LaTeX;本版不做语义解析)', 'Formula source (LaTeX; its meaning is not parsed)'],
|
|
1586
|
+
code: ['指向工作区里真有的文件路径', 'A path to a file that exists in the workspace'],
|
|
1587
|
+
reference: ['外部引用(文献 / URL / 编号)', 'An external reference (paper / URL / identifier)'],
|
|
1992
1588
|
}
|
|
1589
|
+
const VALUE_FORM_GLOSS = bilingual(VALUE_FORM_GLOSS_TEXT)
|
|
1993
1590
|
|
|
1994
1591
|
export function derive(state) {
|
|
1995
1592
|
const activePlan = state.plans.find((plan) => plan.status === 'active') ?? null
|
|
@@ -2010,11 +1607,16 @@ export function derive(state) {
|
|
|
2010
1607
|
.map((item) => item.hypothesis)
|
|
2011
1608
|
.filter((id) => typeof id === 'string' && id !== ''),
|
|
2012
1609
|
)
|
|
1610
|
+
const instanceNodes = new Set(graphProjection(state).nodes.filter((node) => node.kind === 'instance').map((node) => String(node.id)))
|
|
2013
1611
|
const hypotheses = state.hypotheses.map((hypothesis) => {
|
|
2014
1612
|
const rows = []
|
|
2015
1613
|
for (const item of state.evidence) {
|
|
1614
|
+
if (item.hypothesis !== undefined) {
|
|
1615
|
+
if (item.hypothesis === hypothesis.id) rows.push(item)
|
|
1616
|
+
continue
|
|
1617
|
+
}
|
|
2016
1618
|
const step = stepOf(item.plan, item.step)
|
|
2017
|
-
if (step?.tests?.
|
|
1619
|
+
if ((normalizeTests(step?.tests)?.hypotheses ?? []).includes(hypothesis.id)) rows.push(item)
|
|
2018
1620
|
}
|
|
2019
1621
|
const refutations = rows.filter((item) => item.verdict === 'refute').length
|
|
2020
1622
|
const inconclusive = rows.filter((item) => item.verdict === 'inconclusive').length
|
|
@@ -2027,40 +1629,13 @@ export function derive(state) {
|
|
|
2027
1629
|
else if (status === 'proposed' && rows.length > 0) status = 'alive'
|
|
2028
1630
|
// 被推翻是黏性终态:「已被替代」不该改写「已被推翻」——两件事。
|
|
2029
1631
|
if (status !== 'superseded' && status !== 'refuted' && status !== 'retracted' && refutations > 0) status = 'refuted'
|
|
2030
|
-
|
|
2031
|
-
* **从没被走过的等级**(派生,零新账)。
|
|
2032
|
-
*
|
|
2033
|
-
* 等级衡量的是「这条结论在多大程度上只能靠信任做的人」,而它逐级上升的补偿是
|
|
2034
|
-
* 独立裁决与人放行。所以「我直接在 L3 上交付、L0/L1/L2 从没走过」本身不是违规
|
|
2035
|
-
* (首次测量没有廉价路可走),但**它必须看得见**——与「假设从没被证据碰过」记成
|
|
2036
|
-
* `unjudged` 是同一条先例:不逼裁决,但不许把「没看过」写成「没问题」。
|
|
2037
|
-
*
|
|
2038
|
-
* 只列**低于已用到过的最高等级**、且一条证据都没有的那些级;没走过任何等级时为空
|
|
2039
|
-
* (还没有声明可谈)。
|
|
2040
|
-
*/
|
|
2041
|
-
const used = new Set(rows.map((item) => String(item.level ?? '').toUpperCase()))
|
|
2042
|
-
const top = [...used].reduce((best, level) => Math.max(best, LEVELS.indexOf(level)), -1)
|
|
2043
|
-
/**
|
|
2044
|
-
* **写明理由的跳级不算「没走过」**:`level/skipped` 记的就是「这一级在本项目里
|
|
2045
|
-
* 为什么不适用」。所以减掉它——于是 `levels_skipped` 缺口与卡上那一行读的都是
|
|
2046
|
-
* 减完的结果:「写明理由」是那条缺口唯一的出口,而且它真的会让缺口消失。
|
|
2047
|
-
*/
|
|
2048
|
-
const skipCovered = new Set(
|
|
2049
|
-
(Array.isArray(hypothesis.skips) ? hypothesis.skips : [])
|
|
2050
|
-
.flatMap((skip) => (Array.isArray(skip?.levels) ? skip.levels : []))
|
|
2051
|
-
.map((level) => String(level).toUpperCase()),
|
|
2052
|
-
)
|
|
2053
|
-
const untouchedLevels = top <= 0 ? [] : LEVELS.slice(0, top).filter((level) => !used.has(level) && !skipCovered.has(level))
|
|
2054
|
-
return { ...hypothesis, status, supportedLevel: supportedLevel < 0 ? null : `L${supportedLevel}`, refutations, inconclusive, untouchedLevels }
|
|
1632
|
+
return { ...hypothesis, status, supportedLevel: supportedLevel < 0 ? null : `L${supportedLevel}`, refutations, inconclusive, unlanded: subjectsOffGraph(hypothesis, instanceNodes), history: trustHistory(hypothesis, rows) }
|
|
2055
1633
|
})
|
|
2056
1634
|
|
|
2057
1635
|
/**
|
|
2058
1636
|
* `plan_is_authorized`:有记号 **或** 已经真的推进过(行为即授权)。
|
|
2059
1637
|
* 第二个分支在 ClearAI 那边是给存量文档用的;这里同样留着——事实与意图冲突时以事实为准。
|
|
2060
1638
|
*/
|
|
2061
|
-
const planIsAuthorized = (plan) =>
|
|
2062
|
-
plan !== null && (plan.confirmed_at !== null || plan.steps.some((step) => step.status !== 'open'))
|
|
2063
|
-
const planConfirmationPending = activePlan !== null && activePlan.status === 'active' && !planIsAuthorized(activePlan)
|
|
2064
1639
|
let phase = null
|
|
2065
1640
|
let progress = null
|
|
2066
1641
|
if (state.goal !== null) {
|
|
@@ -2104,140 +1679,41 @@ export function derive(state) {
|
|
|
2104
1679
|
}
|
|
2105
1680
|
}
|
|
2106
1681
|
|
|
2107
|
-
/**
|
|
2108
|
-
* 世界线的**派生状态**(语义:见 recon 的「四条结束方式」):
|
|
2109
|
-
*
|
|
2110
|
-
* · `failed` —— 执行者没跑成(`worldline/executed{ok:false}`)。**它不是落选**:
|
|
2111
|
-
* 落选意味着它跑完了、被尺子排到了后面;失败是「世界没给它机会」。原来的实现在这两件事上
|
|
2112
|
-
* 共用一个状态(都还是 exploring),于是界面会一直为一条死掉的线呼吸——那是说假话。
|
|
2113
|
-
* · `orphaned` —— 承载它的**步骤被作废**了,而它还没收口。它既不是被人裁掉(没人做过这个决定),
|
|
2114
|
-
* 也不是被算术排掉(没有尺子排过它):它是「随承诺撤回而终止」。这是两个已存在事实的**推论**,
|
|
2115
|
-
* 不是第五种存储状态——零新账,也**不改写历史**(绝不把它写成「已放弃」)。
|
|
2116
|
-
*/
|
|
2117
|
-
const forks = state.forks.map((fork) => {
|
|
2118
|
-
const owner = (activePlan ?? closedPlans[closedPlans.length - 1] ?? null)?.steps.find((step) => step.id === fork.step) ?? null
|
|
2119
|
-
const ownerVoided = owner !== null && owner.status === 'void'
|
|
2120
|
-
return {
|
|
2121
|
-
...fork,
|
|
2122
|
-
ownerStatus: owner === null ? null : owner.status,
|
|
2123
|
-
ownerVoidReason: ownerVoided ? (owner.voidReason ?? null) : null,
|
|
2124
|
-
orphaned: ownerVoided && !fork.settled && !fork.abandoned,
|
|
2125
|
-
phase: fork.abandoned
|
|
2126
|
-
? 'abandoned'
|
|
2127
|
-
: fork.settled
|
|
2128
|
-
? 'settled'
|
|
2129
|
-
: ownerVoided
|
|
2130
|
-
? 'orphaned'
|
|
2131
|
-
: fork.branches.every((branch) => (BRANCH_RANK[branch.status] ?? 0) >= BRANCH_RANK.evaluated)
|
|
2132
|
-
? 'deciding'
|
|
2133
|
-
: 'exploring',
|
|
2134
|
-
branches: fork.branches.map((branch) => ({
|
|
2135
|
-
...branch,
|
|
2136
|
-
// 派生,不落库:执行没跑成,而状态还停在探索中。
|
|
2137
|
-
failed: branch.status === 'exploring' && branch.execution !== null && branch.execution !== undefined && branch.execution.ok === false,
|
|
2138
|
-
orphaned: ownerVoided && !fork.settled && !fork.abandoned && branch.status === 'exploring',
|
|
2139
|
-
/**
|
|
2140
|
-
* **执行者未归**(与「失败」「孤儿」同一族的假话——不说破就会被当成还在跑):
|
|
2141
|
-
*
|
|
2142
|
-
* 执行者是异步派的,所以「它在跑」是一条会过期的状态。分叉一旦收口(收敛或放弃),
|
|
2143
|
-
* 那条世界线就**没有归宿**了——回灌也进不了任何决定。可 `execution.ok` 仍是 `null`,
|
|
2144
|
-
* 于是卡片会永远写「(执行中,结论会自动回灌)」:**一句实现了不了的承诺**。
|
|
2145
|
-
*
|
|
2146
|
-
* 两条已有事实的推论:分叉是终局 + 执行者没报过。零新账,也不改写 `execution` 本身
|
|
2147
|
-
* (它仍然是「没报过」这个已发生的事实)。
|
|
2148
|
-
*/
|
|
2149
|
-
unreturned: (fork.settled === true || fork.abandoned === true) && branch.execution !== null && branch.execution !== undefined && branch.execution.ok === null,
|
|
2150
|
-
})),
|
|
2151
|
-
}
|
|
2152
|
-
})
|
|
2153
|
-
|
|
2154
|
-
/**
|
|
2155
|
-
* 收件箱:每一道门都是**状态锚**(由派生事实算出,不依赖中断记录)——门一解决,条目自然消失,
|
|
2156
|
-
* 不可能残留成僵尸。
|
|
2157
|
-
* 条目只带**分诊信息**(标题/摘要/指向),不带全部正文:收件箱是分诊,不是问诊。
|
|
2158
|
-
*
|
|
2159
|
-
* 这里**只放真门**:不拍板就真的推不动的那种。计划确认不再是条目(它是记号,
|
|
2160
|
-
* 不是闸门;见 HUMAN_GATE_ACTIONS 的收敛记录)——收件箱里的每一条都经得起「等人是必须的吗」。
|
|
2161
|
-
*/
|
|
2162
|
-
const inbox = []
|
|
2163
|
-
for (const fork of forks) {
|
|
2164
|
-
// 人已经裁决过的不再等他(ClearAI 的条目是状态锚:状态一变,条目自然消失)。
|
|
2165
|
-
if (fork.phase !== 'deciding' || (fork.humanDecision ?? null) !== null) continue
|
|
2166
|
-
const label = fork.branches.find((branch) => branch.id === fork.recommended)?.label ?? null
|
|
2167
|
-
inbox.push({
|
|
2168
|
-
kind: 'fork_adopt',
|
|
2169
|
-
title: '世界线裁决',
|
|
2170
|
-
summary: `${fork.question} · ${fork.branches.length} 条世界线探索完毕,待采纳一条${label === null ? '' : ` · 推荐★${label}`}`,
|
|
2171
|
-
plan: fork.plan ?? null,
|
|
2172
|
-
step: fork.step,
|
|
2173
|
-
/** 条目要指得出**是哪一盘分叉**:界面那条「用提问卡决定」的手势按它去取题目与选项。 */
|
|
2174
|
-
fork: fork.id,
|
|
2175
|
-
human_action: 'adopt_branch',
|
|
2176
|
-
/** 门要什么:**点击**(有白名单动词)还是**一句话**(语义判断归模型)。 */
|
|
2177
|
-
needs: 'click',
|
|
2178
|
-
})
|
|
2179
|
-
}
|
|
2180
|
-
for (const fork of forks) {
|
|
2181
|
-
// 人已经认可过就不再等他(状态锚:状态一变,条目自然消失)。
|
|
2182
|
-
if (fork.merge?.provisional !== true || (fork.merge.confirmed ?? null) !== null) continue
|
|
2183
|
-
inbox.push({
|
|
2184
|
-
kind: 'provisional_review',
|
|
2185
|
-
title: '临时采纳待复核',
|
|
2186
|
-
summary: `${fork.question} · 已**临时**采纳「${fork.branches.find((branch) => branch.id === fork.merge.branch)?.label ?? fork.merge.branch}」:${fork.merge.decisionNote ?? '分差不足以称结论'}`,
|
|
2187
|
-
plan: fork.plan ?? null,
|
|
2188
|
-
step: fork.step,
|
|
2189
|
-
/** 标的:认可是对**哪一盘分叉**的认可。 */
|
|
2190
|
-
fork: fork.id,
|
|
2191
|
-
/** 认可是一个动作(一键);「要改判据」那条路仍然靠说一句话,由模型重做一条世界线。 */
|
|
2192
|
-
human_action: 'confirm_provisional',
|
|
2193
|
-
/**
|
|
2194
|
-
* **一键认可**:它曾经是「要一句话」的门,而「认可就什么都不用做」有个洞——
|
|
2195
|
-
* 门开着 ⇒ 续跑停着 ⇒ 什么都不做的话系统一直等,等一个永远不会来的动作。
|
|
2196
|
-
* 认可是一次决定,决定就该有按钮;要改判据那条路仍然在(说一句话,模型重做一条世界线)。
|
|
2197
|
-
*/
|
|
2198
|
-
needs: 'click',
|
|
2199
|
-
ask: '要改判据就重做一条世界线(说一句即可)',
|
|
2200
|
-
})
|
|
2201
|
-
}
|
|
2202
|
-
for (const candidate of state.brainCandidates ?? []) {
|
|
2203
|
-
inbox.push({
|
|
2204
|
-
kind: 'skill_candidate',
|
|
2205
|
-
title: `候选技能:${candidate.name}`,
|
|
2206
|
-
summary: `${String(candidate.description ?? '').slice(0, 120)}——模型自己写的 SOP,采纳后才进它的技能目录。`,
|
|
2207
|
-
plan: null,
|
|
2208
|
-
step: null,
|
|
2209
|
-
human_action: 'promote_skill',
|
|
2210
|
-
skill: candidate.name,
|
|
2211
|
-
needs: 'click',
|
|
2212
|
-
})
|
|
2213
|
-
}
|
|
2214
|
-
if (activePlan !== null && activePlan.blocked !== undefined) {
|
|
2215
|
-
inbox.push({
|
|
2216
|
-
kind: 'plan_blocked',
|
|
2217
|
-
title: '计划被拦',
|
|
2218
|
-
summary: `连续 ${activePlan.blocked.attempts} 次未过观测准入:${activePlan.blocked.reason}`,
|
|
2219
|
-
plan: activePlan.id,
|
|
2220
|
-
step: activePlan.blocked.step ?? null,
|
|
2221
|
-
// 「解除阻塞」不是一次点击能表达的事(要改计划或改判据),所以它没有人门动作:
|
|
2222
|
-
// 人说一句「按 X 改」,语义判断归模型(ClearAI 删掉 NL 白名单的同一条理由)。
|
|
2223
|
-
human_action: null,
|
|
2224
|
-
needs: 'word',
|
|
2225
|
-
ask: '说一句怎么改(改计划 / 补判据),语义判断归模型',
|
|
2226
|
-
})
|
|
2227
|
-
}
|
|
2228
|
-
// `has_open_gate`:有一道门开着——调度侧据此不驱动(ClearAI:两个谓词同集)。
|
|
2229
|
-
const hasOpenGate = inbox.length > 0
|
|
2230
|
-
|
|
2231
1682
|
/**
|
|
2232
1683
|
* 事实那一行的**两个读数**(都派生,不另存):
|
|
2233
1684
|
* · `refuted`——它的假设收到过推翻证据 ⇒ 这条事实要复核;
|
|
2234
1685
|
* · `review` ——人已经审查过(撤回 / 维持),决定连缘由一起留着。
|
|
2235
1686
|
*/
|
|
2236
|
-
const
|
|
1687
|
+
const lexicon = normalizeLexicon(state.lexicon)
|
|
1688
|
+
const ownFacts = (state.facts ?? []).map((fact) => {
|
|
2237
1689
|
const linked = typeof fact.hypothesis === 'string' && fact.hypothesis !== ''
|
|
2238
1690
|
const owner = hypotheses.find((item) => (linked ? item.id === fact.hypothesis : String(item.claim ?? '') === String(fact.text ?? '')))
|
|
2239
|
-
return { ...fact, refuted: (owner?.refutations ?? 0) > 0, review: fact.review ?? null }
|
|
1691
|
+
return { ...fact, refuted: (owner?.refutations ?? 0) > 0, review: fact.review ?? null, foreign: false }
|
|
2240
1692
|
})
|
|
1693
|
+
/**
|
|
1694
|
+
* **别的会话留下的事实**(住在 `clear/knowledge/facts/<id>.json`,经 `workspace/synced` 进账)。
|
|
1695
|
+
* 同一个 id 本会话账上也有就以账上那条为准(它带着推翻读数);文件上的复核结论照样认。
|
|
1696
|
+
* 它们的假设不在本会话,所以 `refuted` 只能是 false——推翻要在这里重新检验一次才算。
|
|
1697
|
+
*/
|
|
1698
|
+
const ownIds = new Set(ownFacts.map((fact) => fact.id))
|
|
1699
|
+
const foreignFacts = []
|
|
1700
|
+
for (const [path, file] of Object.entries(state.workspace?.files ?? {})) {
|
|
1701
|
+
if (classifyWorkspacePath(path)?.kind !== 'fact' || file?.data === undefined) continue
|
|
1702
|
+
const row = factFromFile(file.data, path)
|
|
1703
|
+
if (row === null) continue
|
|
1704
|
+
if (ownIds.has(row.id)) {
|
|
1705
|
+
const own = ownFacts.find((fact) => fact.id === row.id)
|
|
1706
|
+
if (own.review === null && row.review !== null) own.review = row.review
|
|
1707
|
+
continue
|
|
1708
|
+
}
|
|
1709
|
+
foreignFacts.push({ ...row, refuted: false, foreign: true })
|
|
1710
|
+
}
|
|
1711
|
+
foreignFacts.sort((a, b) => (a.at ?? 0) - (b.at ?? 0))
|
|
1712
|
+
/**
|
|
1713
|
+
* `definitionsChanged`:升格那一刻用到的词条,**含义**此后改过或已不在的那些 id。
|
|
1714
|
+
* 只是读数,不撤回事实:改了定义的人知道为什么改,复核交给人与证据。
|
|
1715
|
+
*/
|
|
1716
|
+
const factRows = [...foreignFacts, ...ownFacts].map((fact) => ({ ...fact, definitionsChanged: changedDefinitions(lexicon, fact.definitions) }))
|
|
2241
1717
|
|
|
2242
1718
|
/**
|
|
2243
1719
|
* **领域词汇的派生读数**(两条,都不新存东西):
|
|
@@ -2248,28 +1724,31 @@ export function derive(state) {
|
|
|
2248
1724
|
* 两者都吃 `factRows` 而不是 `state.facts`:事实的复核态与推翻标记是派生的,
|
|
2249
1725
|
* 在这里重算一遍就等于第二份判据。
|
|
2250
1726
|
*/
|
|
2251
|
-
const lexicon = normalizeLexicon(state.lexicon)
|
|
2252
1727
|
const conflicts = deriveConflicts(factRows, lexicon)
|
|
2253
1728
|
const lexiconIssues = lexiconHealth(lexicon, factRows)
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
human_action: 'retract_fact',
|
|
2270
|
-
needs: 'click',
|
|
1729
|
+
/**
|
|
1730
|
+
* **待处理**(第六阶段,取代空了的收件箱):只陈述、不放按钮——决定在对话里说,
|
|
1731
|
+
* 或由开门的那次调用用原生提问卡问。两类:计划连拦停下了;已写进长期知识的结论互相矛盾。
|
|
1732
|
+
* 都是派生读数:计划一解封、矛盾一方被推翻,条目自然消失。
|
|
1733
|
+
*/
|
|
1734
|
+
const needYou = []
|
|
1735
|
+
if (activePlan !== null && activePlan.blocked !== undefined) needYou.push({ kind: 'plan_blocked', text: tr(`计划停下了:${String(activePlan.blocked.reason ?? '连续没过')},怎么改?`, `The plan stopped: ${String(activePlan.blocked.reason ?? 'it kept failing')}. How should it change?`) })
|
|
1736
|
+
for (const conflict of conflicts) {
|
|
1737
|
+
const values = (conflict.sides ?? []).map((side) => String(side.value ?? '?'))
|
|
1738
|
+
needYou.push({
|
|
1739
|
+
kind: 'conflict',
|
|
1740
|
+
text: tr(
|
|
1741
|
+
`${String(conflict.subject ?? '')}的${String(conflict.predicate ?? '')}:${values.map((value) => `「${value}」`).join('和')}矛盾,以哪个为准?`,
|
|
1742
|
+
`${String(conflict.predicate ?? '')} of ${String(conflict.subject ?? '')}: ${values.map((value) => `"${value}"`).join(' and ')} conflict. Which one holds?`,
|
|
1743
|
+
),
|
|
2271
1744
|
})
|
|
2272
1745
|
}
|
|
1746
|
+
for (const fact of factRows) {
|
|
1747
|
+
if (fact.definitionsChanged.length === 0 || fact.review?.decision === 'retracted') continue
|
|
1748
|
+
const head = String(fact.text ?? '').replace(/\s+/g, ' ').trim()
|
|
1749
|
+
needYou.push({ kind: 'definition_changed', fact: fact.id, text: tr(`「${head.length > 40 ? `${head.slice(0, 39)}…` : head}」用到的 ${fact.definitionsChanged.join('、')} 定义已变,这条结论还成立吗?`, `The definitions of ${fact.definitionsChanged.join(', ')} used by "${head.length > 40 ? `${head.slice(0, 39)}…` : head}" changed. Does this conclusion still hold?`) })
|
|
1750
|
+
}
|
|
1751
|
+
|
|
2273
1752
|
|
|
2274
1753
|
const settlement = state.evidence.map((item) => {
|
|
2275
1754
|
const step = stepOf(item.plan, item.step)
|
|
@@ -2291,14 +1770,10 @@ export function derive(state) {
|
|
|
2291
1770
|
activePlan,
|
|
2292
1771
|
closedPlans,
|
|
2293
1772
|
pendingAudit,
|
|
2294
|
-
forks,
|
|
2295
1773
|
factRows,
|
|
2296
1774
|
settlement,
|
|
2297
1775
|
stepOf,
|
|
2298
|
-
|
|
2299
|
-
hasOpenGate,
|
|
2300
|
-
planConfirmationPending,
|
|
2301
|
-
planIsAuthorized,
|
|
1776
|
+
needYou,
|
|
2302
1777
|
/** 领域词汇与它的两条派生读数(见上面那段:冲突与健康度都只是读数)。 */
|
|
2303
1778
|
lexicon,
|
|
2304
1779
|
conflicts,
|
|
@@ -2313,52 +1788,36 @@ export function derive(state) {
|
|
|
2313
1788
|
*
|
|
2314
1789
|
* 人在面板上按的每一个动作,都经宿主平面的路由变成**一条用户消息**进会话日志——
|
|
2315
1790
|
* 内容是一行结构化标记,不是自然语言。为什么要这样:
|
|
2316
|
-
* · **可审计**:谁在什么时候做了什么(
|
|
1791
|
+
* · **可审计**:谁在什么时候做了什么(撤回或维持哪条事实、改了哪个词条),
|
|
2317
1792
|
* 就在日志里(`source.kind==='user'`);
|
|
2318
1793
|
* · **不做 NLU**:ClearAI 把确认短语白名单整体删掉了,理由是「语义判断只归模型,
|
|
2319
1794
|
* harness 只做顺序可判定的题」。标记进得来、自然语言进不来,是同一条纪律;
|
|
2320
1795
|
* · **agent 不可达**:这些动词**没有工具 schema**——模型能调的工具面里不存在它们,
|
|
2321
1796
|
* 它只能看见「人做了什么」这条事实。
|
|
2322
1797
|
*
|
|
2323
|
-
*
|
|
1798
|
+
* 砍掉的动词(奥卡姆:都是**重复**,或者它服务的机制已经删除):
|
|
2324
1799
|
* · `confirm_plan` —— 原生 `dsh-plan-mode` 就是「用户复核的出口」;而我们自己的
|
|
2325
1800
|
* `planIsAuthorized` 本来就承认「交付第一步即授权」。计划确认从来不是闸门(它是记号),
|
|
2326
1801
|
* 少一个假装成闸门的按钮,界面就不再暗示一条不存在的约束。需要人拍板时用原生
|
|
2327
1802
|
* `ask_user_question`(它同样把人的答复留在日志里,署名一样是人)。
|
|
2328
1803
|
* · `invoke_skill` —— 原生 `/` 技能触发器(`dsh-client-ui-skill` 注册 trigger `/`,
|
|
2329
1804
|
* `dsh-client-ui-input-trigger` 出候选菜单)做的正是同一件事,而且带候选菜单。
|
|
2330
|
-
*
|
|
2331
|
-
* · `
|
|
2332
|
-
*
|
|
2333
|
-
* · `promote_skill` —— 候选技能扶正是**只有人能触发**的写动作(模型不能自举)。
|
|
1805
|
+
* · `adopt_branch` / `abandon_fork` / `confirm_provisional` —— 世界线已删除,并行探索交给原生子任务;
|
|
1806
|
+
* · `promote_skill` —— 外脑已删除,技能走原生技能目录。
|
|
1807
|
+
* 旧日志里的这些动作不再折:`parseHumanGate` 按动词表拒收,原样跳过。
|
|
2334
1808
|
*
|
|
2335
|
-
* 已摘除:`set_autonomy
|
|
2336
|
-
*
|
|
2337
|
-
* 折法里仍留一条**只读**容忍分支(见 `applyEvent` 的注释):旧会话日志里可能有一条这样的记录,
|
|
2338
|
-
* 而历史必须继续读得通——但**当前没有任何写入者**,面板上也没有这个开关。
|
|
1809
|
+
* 已摘除:`set_autonomy`(运行档整个删了,旧日志里的这条记录原样跳过);
|
|
1810
|
+
* `retract_fact` / `keep_fact`(这道门改由内核当场问人,见下面的 `LEGACY_GATE_ACTIONS`)。
|
|
2339
1811
|
*/
|
|
2340
1812
|
export const HUMAN_GATE_MARK = '[clearai·人门]'
|
|
2341
|
-
/**
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
/**
|
|
2350
|
-
* **本体四动词(人的通道)**:面板抽屉发的就是它们;模型有同名语义的工具
|
|
2351
|
-
* (`RegisterTerm` 等),但这两个面落的是**同一套判据与同一本账**——判据在宿主半的
|
|
2352
|
-
* `domain` 门里,词汇事件只有一种折法。这个数组在内核那一侧有一份**逐字镜像**
|
|
2353
|
-
* (预设面不能 import 这一层),两边相等由 authority-boundary 套件钉死。
|
|
2354
|
-
*/
|
|
2355
|
-
'register_term',
|
|
2356
|
-
'register_predicate',
|
|
2357
|
-
'revise_term',
|
|
2358
|
-
'deprecate_entry',
|
|
2359
|
-
]
|
|
2360
|
-
/** 运行档的两个取值。**只用于读取旧日志**里的 `set_autonomy` 记录;当前没有写入口。 */
|
|
2361
|
-
export const AUTONOMY_VALUES = ['attended', 'unattended']
|
|
1813
|
+
/**
|
|
1814
|
+
* **只在读旧日志时认**的动词。面板早已没有写入口(第六阶段把 `/api/clearai/gate` 整条拿掉):
|
|
1815
|
+
* 撤回 / 维持事实从第三阶段起由内核在那次交付里当场问人;本体四动词随编辑抽屉一起删,
|
|
1816
|
+
* 要改词汇就在对话里说,模型去改 `clear/ontology/` 下的文件。
|
|
1817
|
+
* 但旧会话里人按过的决定必须照旧折出来,否则重放时一条审过的事实会变回「待复核」、
|
|
1818
|
+
* 人登记的词会凭空消失。
|
|
1819
|
+
*/
|
|
1820
|
+
const LEGACY_GATE_ACTIONS = ['retract_fact', 'keep_fact', 'register_term', 'register_predicate', 'revise_term', 'deprecate_entry']
|
|
2362
1821
|
|
|
2363
1822
|
/** 从一条用户消息里认出人门标记;不是标记就返回 null。 */
|
|
2364
1823
|
export function parseHumanGate(message) {
|
|
@@ -2374,90 +1833,60 @@ export function parseHumanGate(message) {
|
|
|
2374
1833
|
try {
|
|
2375
1834
|
const parsed = JSON.parse(line)
|
|
2376
1835
|
if (parsed === null || typeof parsed !== 'object') return null
|
|
2377
|
-
if (!
|
|
1836
|
+
if (!LEGACY_GATE_ACTIONS.includes(String(parsed.action))) return null
|
|
2378
1837
|
return parsed
|
|
2379
1838
|
} catch {
|
|
2380
1839
|
return null
|
|
2381
1840
|
}
|
|
2382
1841
|
}
|
|
2383
1842
|
|
|
2384
|
-
/**
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
? null
|
|
2407
|
-
: {
|
|
2408
|
-
by: last.by,
|
|
2409
|
-
at: last.at ?? null,
|
|
2410
|
-
plan: last.plan ?? null,
|
|
2411
|
-
step: last.step ?? null,
|
|
2412
|
-
ordinal: last.ordinal ?? null,
|
|
2413
|
-
// 指针落在的这一步现在的结果(现算):advanced / open / void
|
|
2414
|
-
outcome: step === null ? null : step.status,
|
|
2415
|
-
evidence: step === null ? null : (step.evidence ?? null),
|
|
2416
|
-
stepDo: step === null ? null : step.do,
|
|
2417
|
-
},
|
|
2418
|
-
}
|
|
2419
|
-
})
|
|
2420
|
-
.sort((left, right) => String(left.name).localeCompare(String(right.name)))
|
|
1843
|
+
/** 一条判断升格成的事实(按 id 关联;旧账本按文本找回)。 */
|
|
1844
|
+
function factOf(state, hypothesis) {
|
|
1845
|
+
return (state.facts ?? []).find((item) => item.hypothesis === hypothesis.id) ?? (state.facts ?? []).find((item) => item.hypothesis === undefined && String(item.text ?? '') === String(hypothesis.claim ?? '')) ?? null
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1848
|
+
function factRow(state, hypothesis) {
|
|
1849
|
+
const fact = factOf(state, hypothesis)
|
|
1850
|
+
return fact === null ? null : { id: fact.id, text: fact.text ?? null, scope: fact.scope ?? null, level: fact.level ?? null, at: fact.at ?? null, retracted: fact.review?.decision === 'retracted' }
|
|
1851
|
+
}
|
|
1852
|
+
|
|
1853
|
+
/** 面板读的历程:步骤换成「第几步」,末尾接上升格与撤回(它们不是证据,但同样改了可信度)。 */
|
|
1854
|
+
function historyRows(state, hypothesis) {
|
|
1855
|
+
const ordinalOf = (planId, stepId) => {
|
|
1856
|
+
const plan = state.plans.find((item) => item.id === planId)
|
|
1857
|
+
return plan?.steps?.find((step) => step.id === stepId)?.ordinal ?? null
|
|
1858
|
+
}
|
|
1859
|
+
const rows = (hypothesis.history ?? []).map((row) => (row.kind === 'evidence' ? { ...row, ordinal: ordinalOf(row.plan, row.step) } : { ...row }))
|
|
1860
|
+
const fact = factOf(state, hypothesis)
|
|
1861
|
+
const last = () => rows[rows.length - 1]?.to ?? 'testing'
|
|
1862
|
+
if (fact !== null) rows.push({ kind: 'promoted', at: fact.at ?? null, level: fact.level ?? null, from: last(), to: 'credible' })
|
|
1863
|
+
if (fact?.review?.decision === 'retracted') rows.push({ kind: 'retracted', at: fact.review.at ?? null, reason: fact.review.reason ?? null, from: last(), to: 'replaced' })
|
|
1864
|
+
return rows
|
|
2421
1865
|
}
|
|
2422
1866
|
|
|
2423
1867
|
/** 面板契约。浏览器读的就是这个,一字不改。 */
|
|
2424
1868
|
export function view(state, sessionId) {
|
|
2425
1869
|
const derived = derive(state)
|
|
1870
|
+
const promotedIds = new Set((state.facts ?? []).map((fact) => fact?.hypothesis).filter((id) => typeof id === 'string'))
|
|
2426
1871
|
const plan = derived.activePlan ?? derived.closedPlans[derived.closedPlans.length - 1] ?? null
|
|
2427
1872
|
const sid = sessionId === undefined || sessionId === null ? (state.sessionId ?? null) : String(sessionId)
|
|
2428
1873
|
return {
|
|
2429
1874
|
ok: true,
|
|
2430
1875
|
mounted: true,
|
|
2431
1876
|
sessionId: sid,
|
|
2432
|
-
/**
|
|
2433
|
-
|
|
2434
|
-
hasOpenGate: derived.hasOpenGate,
|
|
2435
|
-
/**
|
|
2436
|
-
* 运行档:`value` = 当档(内核下发的那份)、`source` 说明它从哪来(部署预设 / 旧日志里的人门记录)。
|
|
2437
|
-
* 面板与卡片都读这里 —— 投影是唯一真相。
|
|
2438
|
-
* 注意**它不是可切换的开关**:`set_autonomy` 已摘除,现在只有部署预设会下发新值;
|
|
2439
|
-
* `source === 'session'` 只可能来自旧日志。
|
|
2440
|
-
*/
|
|
2441
|
-
/** 项目章程的读数(面板「技能 · 记忆」页签顶部那一行;点开走原生预览)。 */
|
|
2442
|
-
constitution: state.constitution ?? null,
|
|
1877
|
+
/** 待处理:只陈述的几行(计划停下、结论矛盾);面板顶上与输入框旁读同一份。 */
|
|
1878
|
+
needYou: derived.needYou,
|
|
2443
1879
|
/**
|
|
2444
1880
|
* 被闸门裁断过几次(`block/counted`):世界树的**分段通道**画的就是它——
|
|
2445
1881
|
* 这一步磨了几轮、其中几次被驳回。事实在投影里,面板只负责画。
|
|
2446
1882
|
*/
|
|
2447
1883
|
blocks: state.blocks ?? {},
|
|
2448
1884
|
/**
|
|
2449
|
-
* **人放行**(`human/released`)
|
|
2450
|
-
*
|
|
1885
|
+
* **人放行**(`human/released`)的痕迹:每一条都带它绑在哪一步上
|
|
1886
|
+
* 以及凭据(`via:'ask'`:内核那次交付当场问出来的;旧账里是 `approval`)。
|
|
2451
1887
|
* 面板与测试都读这里 —— 推断出来的放行不该有痕迹,所以这份读数本身就是判据。
|
|
2452
1888
|
*/
|
|
2453
|
-
releases: (state.releases ?? []).map((item) => ({ step: item.step ?? null, plan: item.plan ?? null,
|
|
2454
|
-
autonomy: {
|
|
2455
|
-
value: state.autonomy?.effective?.value ?? null,
|
|
2456
|
-
preset: state.autonomy?.effective?.preset ?? null,
|
|
2457
|
-
source: state.autonomy?.effective?.source ?? null,
|
|
2458
|
-
override: state.autonomy?.override ?? null,
|
|
2459
|
-
},
|
|
2460
|
-
brain: state.brain ?? null,
|
|
1889
|
+
releases: (state.releases ?? []).map((item) => ({ step: item.step ?? null, plan: item.plan ?? null, via: item.via ?? null, call: item.call ?? null, at: item.at ?? null })),
|
|
2461
1890
|
/** 本体形状(面板页眉据此生成,不手抄)。 */
|
|
2462
1891
|
ontology: state.ontology ?? null,
|
|
2463
1892
|
/**
|
|
@@ -2472,6 +1901,9 @@ export function view(state, sessionId) {
|
|
|
2472
1901
|
conflicts: derived.conflicts,
|
|
2473
1902
|
health: derived.lexiconIssues,
|
|
2474
1903
|
graph: graphProjection(state),
|
|
1904
|
+
/** 本体文件里的实体(带所在目录的容器)与跨文件问题:本体页的问题列表读它。 */
|
|
1905
|
+
entities: Array.isArray(state.entities) ? state.entities : [],
|
|
1906
|
+
problems: Array.isArray(state.ontologyProblems) ? state.ontologyProblems : [],
|
|
2475
1907
|
},
|
|
2476
1908
|
/**
|
|
2477
1909
|
* **知识模式(分诊)**:面板据此把「这一格是知识主场还是普通进展」说清楚,
|
|
@@ -2494,23 +1926,6 @@ export function view(state, sessionId) {
|
|
|
2494
1926
|
* 面板可以画它,内核的 pre-step 把它送进卡——判据与缺口读数一样,只有这一处。
|
|
2495
1927
|
*/
|
|
2496
1928
|
preflight: knowledgePreflight(state, derived),
|
|
2497
|
-
/**
|
|
2498
|
-
* 续跑窗口的账:面板读它,于是「续跑停着——等裁决」这种**平台说不出来的话**
|
|
2499
|
-
* 有地方说。轮数与相位仍然只在原生 dock 上出现(一个事实只在**一块**面上说),
|
|
2500
|
-
* 这里交出去的只有:状态、它服务的事实对象、以及停下来的真实理由。
|
|
2501
|
-
*/
|
|
2502
|
-
continuation:
|
|
2503
|
-
state.continuation === null || state.continuation === undefined
|
|
2504
|
-
? null
|
|
2505
|
-
: {
|
|
2506
|
-
state: state.continuation.state,
|
|
2507
|
-
goal: state.continuation.goal ?? null,
|
|
2508
|
-
target: state.continuation.target ?? null,
|
|
2509
|
-
why: state.continuation.why ?? null,
|
|
2510
|
-
label: state.continuation.label ?? null,
|
|
2511
|
-
},
|
|
2512
|
-
/** 技能面:合并目录 + 本会话用量(「技能 · 记忆」页签的数据面)。 */
|
|
2513
|
-
skills: { catalog: state.skillCatalog ?? null, usage: skillUsageView(state) },
|
|
2514
1929
|
goal:
|
|
2515
1930
|
state.goal === null
|
|
2516
1931
|
? null
|
|
@@ -2528,15 +1943,20 @@ export function view(state, sessionId) {
|
|
|
2528
1943
|
closeVerdict: state.goal.closeVerdict ?? null,
|
|
2529
1944
|
hypotheses: derived.hypotheses.map((hypothesis) => ({
|
|
2530
1945
|
id: hypothesis.id,
|
|
1946
|
+
name: handleOf(hypothesis),
|
|
1947
|
+
/** 可信度分组(已验证 / 待核验 / 验证中 / 不确定 / 已推翻 / 已替换),面板与卡同一份。 */
|
|
1948
|
+
trust: trustOf(hypothesis, promotedIds.has(hypothesis.id)),
|
|
2531
1949
|
claim: hypothesis.claim,
|
|
2532
1950
|
refuteWhen: hypothesis.refute_when,
|
|
2533
1951
|
status: hypothesis.status,
|
|
2534
1952
|
supportedLevel: hypothesis.supportedLevel,
|
|
2535
1953
|
refutations: hypothesis.refutations,
|
|
2536
1954
|
inconclusive: hypothesis.inconclusive,
|
|
2537
|
-
/** 从没被走过的等级(派生):面板据此说清「这一级是跳上来的」。 */
|
|
2538
|
-
untouchedLevels: hypothesis.untouchedLevels,
|
|
2539
1955
|
version: hypothesis.version,
|
|
1956
|
+
/** 「可信度怎么变的」:逐笔的之前 → 之后,末尾接上写进长期知识 / 被撤回。 */
|
|
1957
|
+
history: historyRows(state, hypothesis),
|
|
1958
|
+
/** 写进长期知识的那条(没升格就是 null):补充段的「范围」读它。 */
|
|
1959
|
+
fact: factRow(state, hypothesis),
|
|
2540
1960
|
/** 断言随假设走(未升格):面板同画芯片(chip 同样在投影侧算好),标注「未升格」。 */
|
|
2541
1961
|
assertions: Array.isArray(hypothesis.assertions)
|
|
2542
1962
|
? hypothesis.assertions.map((assertion) => ({ ...assertion, chip: formatAssertion(state.lexicon, assertion) }))
|
|
@@ -2562,7 +1982,9 @@ export function view(state, sessionId) {
|
|
|
2562
1982
|
doneCriteria: step.done_criteria,
|
|
2563
1983
|
tests: step.tests,
|
|
2564
1984
|
status: step.status,
|
|
2565
|
-
|
|
1985
|
+
evidenceIds: step.evidence ?? [],
|
|
1986
|
+
/** 交付本身(谁判的、凭什么、出处);旧账为 null。 */
|
|
1987
|
+
delivery: step.delivery ?? null,
|
|
2566
1988
|
advancedAt: step.advancedAt,
|
|
2567
1989
|
voidReason: step.voidReason,
|
|
2568
1990
|
})),
|
|
@@ -2576,10 +1998,6 @@ export function view(state, sessionId) {
|
|
|
2576
1998
|
/** 计划自己的一句话(模型建计划时写的)。**给人看的名字**用它,内部 id 退回 tooltip。 */
|
|
2577
1999
|
brief: plan.brief ?? '',
|
|
2578
2000
|
blocked: plan.blocked ?? null,
|
|
2579
|
-
// 授权记号(ClearAI `plan.confirmed_at` / `confirmed_by`):面板据此显示计划门。
|
|
2580
|
-
confirmedAt: plan.confirmed_at ?? null,
|
|
2581
|
-
confirmedBy: plan.confirmed_by ?? null,
|
|
2582
|
-
confirmationPending: derived.planConfirmationPending && plan === derived.activePlan,
|
|
2583
2001
|
steps: plan.steps.map((step) => ({
|
|
2584
2002
|
id: step.id,
|
|
2585
2003
|
ordinal: step.ordinal,
|
|
@@ -2593,7 +2011,9 @@ export function view(state, sessionId) {
|
|
|
2593
2011
|
doneCriteria: step.done_criteria,
|
|
2594
2012
|
tests: step.tests,
|
|
2595
2013
|
status: step.status,
|
|
2596
|
-
|
|
2014
|
+
evidenceIds: step.evidence ?? [],
|
|
2015
|
+
/** 交付本身(谁判的、凭什么、出处);旧账为 null。 */
|
|
2016
|
+
delivery: step.delivery ?? null,
|
|
2597
2017
|
advancedAt: step.advancedAt,
|
|
2598
2018
|
voidReason: step.voidReason,
|
|
2599
2019
|
})),
|
|
@@ -2621,12 +2041,11 @@ export function view(state, sessionId) {
|
|
|
2621
2041
|
stepId: item.step,
|
|
2622
2042
|
planId: item.plan,
|
|
2623
2043
|
/**
|
|
2624
|
-
* `
|
|
2625
|
-
* 独立证据指评估卡(文件)
|
|
2626
|
-
* 「评估卡」那一项永远出不来(成因:世界线证据的评估卡按 `分支` 归属,
|
|
2627
|
-
* 与证据的 `stepId` 不是同一个 id,只看 stepId 会对不上)。
|
|
2044
|
+
* `anchor` **必须交出去**:面板要给每条证据指一个**出处**——
|
|
2045
|
+
* 独立证据指评估卡(文件)、自判证据指它锚定的产物。
|
|
2628
2046
|
*/
|
|
2629
|
-
|
|
2047
|
+
/** 针对哪条判断(旧账为 null:由步骤检验的判断找回来)。 */
|
|
2048
|
+
hypothesis: item.hypothesis ?? null,
|
|
2630
2049
|
anchor: item.anchor ?? 'artifact',
|
|
2631
2050
|
/** 记账时定下的出处。旧日志没有这个字段 ⇒ 客户端走只读回退。 */
|
|
2632
2051
|
origins: item.origins ?? [],
|
|
@@ -2664,6 +2083,8 @@ export function view(state, sessionId) {
|
|
|
2664
2083
|
facts: derived.factRows.map((item) => ({
|
|
2665
2084
|
id: item.id,
|
|
2666
2085
|
text: item.text,
|
|
2086
|
+
/** 产出它的那条判断(旧账本没有,面板把这种事实单独列成一行)。 */
|
|
2087
|
+
hypothesis: item.hypothesis ?? null,
|
|
2667
2088
|
/** 边界(推翻条件)与支持等级:面板与货架都要显示它——「已知」必须带边界。 */
|
|
2668
2089
|
scope: item.scope ?? null,
|
|
2669
2090
|
level: item.level ?? null,
|
|
@@ -2679,91 +2100,6 @@ export function view(state, sessionId) {
|
|
|
2679
2100
|
refuted: item.refuted === true,
|
|
2680
2101
|
review: item.review ?? null,
|
|
2681
2102
|
})),
|
|
2682
|
-
/**
|
|
2683
|
-
* 侦察记录:**结局要能看出来**。
|
|
2684
|
-
*
|
|
2685
|
-
* 失效模式:中断一次跑动之后「状态上全部显示执行完成」,而宿主其实报了失败。
|
|
2686
|
-
* 两处都修了:内核改成按 `stopReason` 落账(aborted/error 不算完成),这里把 `note` 交出去
|
|
2687
|
-
* 并派生成三态——跑着 / 正常回灌 / 没正常结束。面板与资料面据此分诊,而不是一律当"完成"。
|
|
2688
|
-
*/
|
|
2689
|
-
scouts: (state.scouts ?? []).map((item) => ({
|
|
2690
|
-
id: item.id,
|
|
2691
|
-
stepId: item.step,
|
|
2692
|
-
trigger: item.trigger,
|
|
2693
|
-
child: item.child,
|
|
2694
|
-
capability: item.capability,
|
|
2695
|
-
digest: item.digest ?? null,
|
|
2696
|
-
conclusion: item.conclusion,
|
|
2697
|
-
at: item.at,
|
|
2698
|
-
note: item.note ?? null,
|
|
2699
|
-
path: item.path ?? null,
|
|
2700
|
-
status: item.conclusion === null || item.conclusion === undefined ? 'running' : item.note === null || item.note === undefined ? 'settled' : 'failed',
|
|
2701
|
-
})),
|
|
2702
|
-
forks: derived.forks.map((fork) => ({
|
|
2703
|
-
tier: fork.tier ?? null,
|
|
2704
|
-
degradedReason: fork.degradedReason ?? null,
|
|
2705
|
-
merge: fork.merge ?? null,
|
|
2706
|
-
mergeConflict: fork.mergeConflict ?? null,
|
|
2707
|
-
/** 临时采纳:分差不足以称结论(或由判断而来)→ 面板要给「待复核」标记。 */
|
|
2708
|
-
provisional: fork.merge?.provisional === true,
|
|
2709
|
-
decisionNote: fork.merge?.decisionNote ?? null,
|
|
2710
|
-
/** 人在面板上的裁决(结构化记录,by:'user'):内核的工具据此落实它。 */
|
|
2711
|
-
humanDecision: fork.humanDecision ?? null,
|
|
2712
|
-
id: fork.id,
|
|
2713
|
-
stepId: fork.step,
|
|
2714
|
-
question: fork.question,
|
|
2715
|
-
phase: fork.phase,
|
|
2716
|
-
decided: fork.settled,
|
|
2717
|
-
abandonReason: fork.abandonReason,
|
|
2718
|
-
/** 承载它的步骤现在是什么状态(void = 承诺已撤回)——面板据此说「随步骤作废而终止」。 */
|
|
2719
|
-
ownerStatus: fork.ownerStatus ?? null,
|
|
2720
|
-
ownerVoidReason: fork.ownerVoidReason ?? null,
|
|
2721
|
-
/** 派生:步骤作废 + 还没收口。不是新状态,是两个事实的推论(见 derive 里的说明)。 */
|
|
2722
|
-
orphaned: fork.orphaned === true,
|
|
2723
|
-
decideBy: { metric: fork.decide_by?.metric ?? null, direction: fork.decide_by?.direction ?? null },
|
|
2724
|
-
verdict: fork.verdict,
|
|
2725
|
-
undecidable: fork.undecidable,
|
|
2726
|
-
/**
|
|
2727
|
-
* 算术推荐(`fork/recommended`):人门卡与面板的「推荐★」读的就是它。
|
|
2728
|
-
* 它只在分叉**还没收口**时才有意义——收口之后 `verdict.winner` 才是结论。
|
|
2729
|
-
*/
|
|
2730
|
-
recommended: fork.recommended ?? null,
|
|
2731
|
-
recommendMargin: fork.recommendMargin ?? null,
|
|
2732
|
-
recommendProvisional: fork.recommendProvisional === true,
|
|
2733
|
-
recommendBy: fork.recommendBy ?? null,
|
|
2734
|
-
/** 采纳时没能合并(以及为什么):决定登记了,产物靠一次普通交付落位。 */
|
|
2735
|
-
mergeSkipped: fork.mergeSkipped ?? null,
|
|
2736
|
-
arbitration: fork.arbitration ?? null,
|
|
2737
|
-
arbitrationSession: fork.arbitrationSession ?? null,
|
|
2738
|
-
branches: fork.branches.map((branch) => ({
|
|
2739
|
-
id: branch.id,
|
|
2740
|
-
label: branch.label,
|
|
2741
|
-
approach: branch.approach,
|
|
2742
|
-
doneCriteria: branch.done_criteria,
|
|
2743
|
-
workspace: branch.workspace,
|
|
2744
|
-
level: branch.level,
|
|
2745
|
-
status: branch.status,
|
|
2746
|
-
reading: branch.reading,
|
|
2747
|
-
validity: branch.validity,
|
|
2748
|
-
/** 派生:执行没跑成(`execution.ok === false`)。**它不是落选**——面板要分开说。 */
|
|
2749
|
-
failed: branch.failed === true,
|
|
2750
|
-
orphaned: branch.orphaned === true,
|
|
2751
|
-
/** 派生:分叉已收口而执行者没报过 ⇒ 那句「结论会自动回灌」作废(见 derive 里的说明)。 */
|
|
2752
|
-
unreturned: branch.unreturned === true,
|
|
2753
|
-
execution: branch.execution ?? null,
|
|
2754
|
-
verdict: branch.verdict,
|
|
2755
|
-
basis: branch.basis,
|
|
2756
|
-
evaluator: branch.evaluator,
|
|
2757
|
-
execution: branch.execution ?? null,
|
|
2758
|
-
cardPath: branch.card_path ?? null,
|
|
2759
|
-
evaluatorSession: branch.evaluator_session ?? null,
|
|
2760
|
-
gitBranch: branch.git_branch ?? null,
|
|
2761
|
-
worktreePath: branch.worktree_path ?? null,
|
|
2762
|
-
worktreeRemoved: branch.worktree_removed === true,
|
|
2763
|
-
keptRef: branch.kept_ref !== false,
|
|
2764
|
-
at: branch.at,
|
|
2765
|
-
})),
|
|
2766
|
-
})),
|
|
2767
2103
|
}
|
|
2768
2104
|
}
|
|
2769
2105
|
|