soulbridge 0.0.26 → 0.0.100
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/dist/packages/core/src/config/dimensions.d.ts +18 -0
- package/dist/packages/core/src/config/dimensions.js +33 -0
- package/dist/packages/core/src/config/domains.d.ts +70 -0
- package/dist/packages/core/src/config/domains.js +70 -0
- package/dist/packages/core/src/config/labels.d.ts +22 -0
- package/dist/packages/core/src/config/labels.js +21 -0
- package/dist/packages/core/src/config/status.d.ts +51 -0
- package/dist/packages/core/src/config/status.js +47 -0
- package/dist/packages/core/src/consolidate.d.ts +170 -0
- package/dist/packages/core/src/consolidate.js +311 -0
- package/dist/packages/core/src/context.d.ts +2 -0
- package/dist/packages/core/src/context.js +48 -17
- package/dist/packages/core/src/dimension-text.d.ts +6 -0
- package/dist/packages/core/src/dimension-text.js +24 -0
- package/dist/packages/core/src/extract.d.ts +140 -0
- package/dist/packages/core/src/extract.js +611 -0
- package/dist/packages/core/src/index.d.ts +362 -10
- package/dist/packages/core/src/index.js +983 -127
- package/dist/packages/core/src/inject-text.d.ts +16 -0
- package/dist/packages/core/src/inject-text.js +24 -0
- package/dist/packages/core/src/profile-prompt.d.ts +18 -0
- package/dist/packages/core/src/profile-prompt.js +103 -0
- package/dist/packages/core/src/prompts.d.ts +122 -0
- package/dist/packages/core/src/prompts.generated.d.ts +9 -0
- package/dist/packages/core/src/prompts.generated.js +9 -0
- package/dist/packages/core/src/prompts.js +293 -0
- package/dist/packages/core/src/reflect.d.ts +7 -0
- package/dist/packages/core/src/reflect.js +25 -27
- package/dist/packages/core/src/retrieval.d.ts +39 -0
- package/dist/packages/core/src/retrieval.js +105 -0
- package/dist/packages/core/src/roles.d.ts +18 -0
- package/dist/packages/core/src/roles.js +17 -0
- package/dist/packages/core/src/rules.d.ts +69 -0
- package/dist/packages/core/src/rules.js +152 -0
- package/dist/packages/core/src/sqlite.d.ts +26 -5
- package/dist/packages/core/src/sqlite.js +944 -33
- package/dist/packages/core/src/store.d.ts +92 -3
- package/dist/packages/core/src/types.d.ts +442 -1
- package/dist/packages/core/src/types.js +1 -0
- package/package.json +5 -3
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The profile dimension vocabulary, declared once.
|
|
3
|
+
*
|
|
4
|
+
* A dimension is a noun we ship to two model stages and to the merge parser: the
|
|
5
|
+
* classifier decides which lens a lesson belongs to, the merge decides which lens a
|
|
6
|
+
* line belongs to, and the parser rejects anything else. Those three must agree, so
|
|
7
|
+
* the names AND their admission criteria live here and nowhere else — prompts render
|
|
8
|
+
* this, they do not retype it.
|
|
9
|
+
*
|
|
10
|
+
* The definitions are borrowed from measurement traditions on purpose:
|
|
11
|
+
* 价值观 ← Schwartz; 人际关系 ← interpersonal circumplex (warmth × dominance);
|
|
12
|
+
* 生活兴趣 ← RIASEC; 情绪 ← core affect. 边界 has no taxonomy — it quotes the words.
|
|
13
|
+
* These are admission criteria for a sentence, not a personality theory.
|
|
14
|
+
*/
|
|
15
|
+
export declare const PROFILE_DIMENSIONS: readonly ['价值观', '工作方式', '沟通偏好', '情绪', '人际关系', '生活兴趣', '边界'];
|
|
16
|
+
export type ProfileDimension = (typeof PROFILE_DIMENSIONS)[number];
|
|
17
|
+
/** What counts as a line in each dimension, and what does not. */
|
|
18
|
+
export declare const PROFILE_DIMENSION_DEFINITIONS: Record<ProfileDimension, string>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The profile dimension vocabulary, declared once.
|
|
3
|
+
*
|
|
4
|
+
* A dimension is a noun we ship to two model stages and to the merge parser: the
|
|
5
|
+
* classifier decides which lens a lesson belongs to, the merge decides which lens a
|
|
6
|
+
* line belongs to, and the parser rejects anything else. Those three must agree, so
|
|
7
|
+
* the names AND their admission criteria live here and nowhere else — prompts render
|
|
8
|
+
* this, they do not retype it.
|
|
9
|
+
*
|
|
10
|
+
* The definitions are borrowed from measurement traditions on purpose:
|
|
11
|
+
* 价值观 ← Schwartz; 人际关系 ← interpersonal circumplex (warmth × dominance);
|
|
12
|
+
* 生活兴趣 ← RIASEC; 情绪 ← core affect. 边界 has no taxonomy — it quotes the words.
|
|
13
|
+
* These are admission criteria for a sentence, not a personality theory.
|
|
14
|
+
*/
|
|
15
|
+
export const PROFILE_DIMENSIONS = [
|
|
16
|
+
'价值观',
|
|
17
|
+
'工作方式',
|
|
18
|
+
'沟通偏好',
|
|
19
|
+
'情绪',
|
|
20
|
+
'人际关系',
|
|
21
|
+
'生活兴趣',
|
|
22
|
+
'边界',
|
|
23
|
+
];
|
|
24
|
+
/** What counts as a line in each dimension, and what does not. */
|
|
25
|
+
export const PROFILE_DIMENSION_DEFINITIONS = {
|
|
26
|
+
价值观: '超越具体情境、值得追求的目标(Schwartz)。"想把事做对"算;"这个接口用 POST"不算。',
|
|
27
|
+
工作方式: '面对任务时的稳定做法。"先验证再动手"算;"这次改了 A 文件"不算。',
|
|
28
|
+
沟通偏好: '希望信息以什么形式、什么节奏出现。"整轮对话当整体"算。',
|
|
29
|
+
情绪: '对具体事件的感受与强度。"被反复确认会烦"算;"今天天气"不算。',
|
|
30
|
+
人际关系: '与特定人的位置和距离(温暖 × 支配)。"她最近压力大"算。',
|
|
31
|
+
生活兴趣: '自愿投入时间的事。"喜欢爬山"算。',
|
|
32
|
+
边界: '明确说过的"不要"。没有公认分类学,必须贴着原话,不许概括。',
|
|
33
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Value domains that both the code and the model must agree on — data only.
|
|
3
|
+
*
|
|
4
|
+
* ADR 0027 splits work by *who decides*: the model decides meaning, the code decides
|
|
5
|
+
* shape. The failures that motivated this file were all shape decisions the code made
|
|
6
|
+
* on the model's behalf: a slot value that "looks like a name" became an entity, an
|
|
7
|
+
* entity without a type defaulted to `person`, and the same holder column carried both
|
|
8
|
+
* keywords and entity ids.
|
|
9
|
+
*
|
|
10
|
+
* Rules that come from this file (checked by health-check §12):
|
|
11
|
+
* 1. A domain's values are listed here once; other modules import them.
|
|
12
|
+
* 2. No semantic field may have a default value in code — a default *is* a decision,
|
|
13
|
+
* and decisions belong to the model or to the caller.
|
|
14
|
+
* 3. A slot value becomes an entity reference only when it is explicitly marked with
|
|
15
|
+
* SLOT_ENTITY_MARKER. Syntax never implies semantics.
|
|
16
|
+
*
|
|
17
|
+
* Keep this module free of functions: it is rendered into prompts and compared against
|
|
18
|
+
* live data, so it must stay inert.
|
|
19
|
+
*/
|
|
20
|
+
/** What an entity can be. The same set the store accepts. */
|
|
21
|
+
export declare const ENTITY_TYPES: readonly ['person', 'thing', 'place', 'self'];
|
|
22
|
+
/** `holder` either names a layer or points at the entity whose mind holds the fact. */
|
|
23
|
+
export declare const HOLDER_WORDS: readonly ['world', 'self'];
|
|
24
|
+
/** A slot value is text unless it starts with this marker. */
|
|
25
|
+
export declare const SLOT_ENTITY_MARKER = "@";
|
|
26
|
+
/**
|
|
27
|
+
* What a fact is *for* — chosen by the model from a closed set.
|
|
28
|
+
*
|
|
29
|
+
* `type_name` stays free text ("裁员", "BOM 教训"); this field is the structural answer to
|
|
30
|
+
* "which consumer reads it", so code no longer routes by matching a Chinese label:
|
|
31
|
+
* observation → retrieval
|
|
32
|
+
* judgment → retrieval + profile input
|
|
33
|
+
* rule → injected when its trigger fires
|
|
34
|
+
* profile → injected every turn (after merge)
|
|
35
|
+
* promise → commitment tracking
|
|
36
|
+
* lesson → rule candidate / anchor
|
|
37
|
+
*/
|
|
38
|
+
export declare const FACT_ROLES: readonly ['observation', 'judgment', 'rule', 'profile', 'promise', 'lesson'];
|
|
39
|
+
/**
|
|
40
|
+
* Where a fact is in its life — written by code, never by the model.
|
|
41
|
+
*
|
|
42
|
+
* `raw` is the exchange itself (turns), `candidate` is held but not injected, `active` is in
|
|
43
|
+
* use, `retracted` is kept for the record and read by nothing. `superseded` was the old name
|
|
44
|
+
* for `retracted`; the migration maps it.
|
|
45
|
+
*/
|
|
46
|
+
export declare const FACT_STATES: readonly ['raw', 'candidate', 'active', 'retracted'];
|
|
47
|
+
/** Old status values → new ones. Kept here so the migration and the reader agree. */
|
|
48
|
+
export declare const FACT_STATE_MIGRATION: {
|
|
49
|
+
readonly none: 'active';
|
|
50
|
+
readonly open: 'active';
|
|
51
|
+
readonly kept: 'active';
|
|
52
|
+
readonly broken: 'retracted';
|
|
53
|
+
readonly superseded: 'retracted';
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* The old Chinese type labels mapped to structural roles.
|
|
57
|
+
*
|
|
58
|
+
* `type_name` used to be the only place that said what a fact was, and rows written before the
|
|
59
|
+
* role column still carry it. One table, used by the writer (new rows get a role), by the reader
|
|
60
|
+
* fallback, and by the migration script — so the three cannot disagree.
|
|
61
|
+
*/
|
|
62
|
+
export declare const LEGACY_ROLE_BY_TYPE: {
|
|
63
|
+
readonly 规则: 'rule';
|
|
64
|
+
readonly 画像: 'profile';
|
|
65
|
+
readonly 结论: 'judgment';
|
|
66
|
+
readonly 判断: 'judgment';
|
|
67
|
+
readonly 决定: 'judgment';
|
|
68
|
+
readonly 承诺: 'promise';
|
|
69
|
+
readonly 教训: 'lesson';
|
|
70
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Value domains that both the code and the model must agree on — data only.
|
|
3
|
+
*
|
|
4
|
+
* ADR 0027 splits work by *who decides*: the model decides meaning, the code decides
|
|
5
|
+
* shape. The failures that motivated this file were all shape decisions the code made
|
|
6
|
+
* on the model's behalf: a slot value that "looks like a name" became an entity, an
|
|
7
|
+
* entity without a type defaulted to `person`, and the same holder column carried both
|
|
8
|
+
* keywords and entity ids.
|
|
9
|
+
*
|
|
10
|
+
* Rules that come from this file (checked by health-check §12):
|
|
11
|
+
* 1. A domain's values are listed here once; other modules import them.
|
|
12
|
+
* 2. No semantic field may have a default value in code — a default *is* a decision,
|
|
13
|
+
* and decisions belong to the model or to the caller.
|
|
14
|
+
* 3. A slot value becomes an entity reference only when it is explicitly marked with
|
|
15
|
+
* SLOT_ENTITY_MARKER. Syntax never implies semantics.
|
|
16
|
+
*
|
|
17
|
+
* Keep this module free of functions: it is rendered into prompts and compared against
|
|
18
|
+
* live data, so it must stay inert.
|
|
19
|
+
*/
|
|
20
|
+
/** What an entity can be. The same set the store accepts. */
|
|
21
|
+
export const ENTITY_TYPES = ['person', 'thing', 'place', 'self'];
|
|
22
|
+
/** `holder` either names a layer or points at the entity whose mind holds the fact. */
|
|
23
|
+
export const HOLDER_WORDS = ['world', 'self'];
|
|
24
|
+
/** A slot value is text unless it starts with this marker. */
|
|
25
|
+
export const SLOT_ENTITY_MARKER = '@';
|
|
26
|
+
/**
|
|
27
|
+
* What a fact is *for* — chosen by the model from a closed set.
|
|
28
|
+
*
|
|
29
|
+
* `type_name` stays free text ("裁员", "BOM 教训"); this field is the structural answer to
|
|
30
|
+
* "which consumer reads it", so code no longer routes by matching a Chinese label:
|
|
31
|
+
* observation → retrieval
|
|
32
|
+
* judgment → retrieval + profile input
|
|
33
|
+
* rule → injected when its trigger fires
|
|
34
|
+
* profile → injected every turn (after merge)
|
|
35
|
+
* promise → commitment tracking
|
|
36
|
+
* lesson → rule candidate / anchor
|
|
37
|
+
*/
|
|
38
|
+
export const FACT_ROLES = ['observation', 'judgment', 'rule', 'profile', 'promise', 'lesson'];
|
|
39
|
+
/**
|
|
40
|
+
* Where a fact is in its life — written by code, never by the model.
|
|
41
|
+
*
|
|
42
|
+
* `raw` is the exchange itself (turns), `candidate` is held but not injected, `active` is in
|
|
43
|
+
* use, `retracted` is kept for the record and read by nothing. `superseded` was the old name
|
|
44
|
+
* for `retracted`; the migration maps it.
|
|
45
|
+
*/
|
|
46
|
+
export const FACT_STATES = ['raw', 'candidate', 'active', 'retracted'];
|
|
47
|
+
/** Old status values → new ones. Kept here so the migration and the reader agree. */
|
|
48
|
+
export const FACT_STATE_MIGRATION = {
|
|
49
|
+
none: 'active',
|
|
50
|
+
open: 'active',
|
|
51
|
+
kept: 'active',
|
|
52
|
+
broken: 'retracted',
|
|
53
|
+
superseded: 'retracted',
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* The old Chinese type labels mapped to structural roles.
|
|
57
|
+
*
|
|
58
|
+
* `type_name` used to be the only place that said what a fact was, and rows written before the
|
|
59
|
+
* role column still carry it. One table, used by the writer (new rows get a role), by the reader
|
|
60
|
+
* fallback, and by the migration script — so the three cannot disagree.
|
|
61
|
+
*/
|
|
62
|
+
export const LEGACY_ROLE_BY_TYPE = {
|
|
63
|
+
规则: 'rule',
|
|
64
|
+
画像: 'profile',
|
|
65
|
+
结论: 'judgment',
|
|
66
|
+
判断: 'judgment',
|
|
67
|
+
决定: 'judgment',
|
|
68
|
+
承诺: 'promise',
|
|
69
|
+
教训: 'lesson',
|
|
70
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The layer names a lesson can be classified into — declared once.
|
|
3
|
+
*
|
|
4
|
+
* These labels travel three ways: the classifier is told to emit them, the parser
|
|
5
|
+
* matches them, and the store writes them as `type_name`. A copy anywhere else means
|
|
6
|
+
* a rename turns into "one stage writes a label the next stage does not read".
|
|
7
|
+
*/
|
|
8
|
+
export declare const CLASSIFY_LABELS: {
|
|
9
|
+
readonly rule: '规则';
|
|
10
|
+
readonly profile: '画像';
|
|
11
|
+
readonly pending: '待修';
|
|
12
|
+
};
|
|
13
|
+
export type ClassifyLabel = (typeof CLASSIFY_LABELS)[keyof typeof CLASSIFY_LABELS];
|
|
14
|
+
/** The judgment a conclusion is stored as. */
|
|
15
|
+
export declare const CONCLUSION_TYPE = "\u7ED3\u8BBA";
|
|
16
|
+
/**
|
|
17
|
+
* The fact type names that are injected as always-on anchors.
|
|
18
|
+
*
|
|
19
|
+
* Kept here because the set is shared by the context builder and by profile/rule
|
|
20
|
+
* readers; retyping it in a second filter is how a layer silently drops out.
|
|
21
|
+
*/
|
|
22
|
+
export declare const ANCHOR_TYPES: readonly string[];
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The layer names a lesson can be classified into — declared once.
|
|
3
|
+
*
|
|
4
|
+
* These labels travel three ways: the classifier is told to emit them, the parser
|
|
5
|
+
* matches them, and the store writes them as `type_name`. A copy anywhere else means
|
|
6
|
+
* a rename turns into "one stage writes a label the next stage does not read".
|
|
7
|
+
*/
|
|
8
|
+
export const CLASSIFY_LABELS = {
|
|
9
|
+
rule: '规则',
|
|
10
|
+
profile: '画像',
|
|
11
|
+
pending: '待修',
|
|
12
|
+
};
|
|
13
|
+
/** The judgment a conclusion is stored as. */
|
|
14
|
+
export const CONCLUSION_TYPE = '结论';
|
|
15
|
+
/**
|
|
16
|
+
* The fact type names that are injected as always-on anchors.
|
|
17
|
+
*
|
|
18
|
+
* Kept here because the set is shared by the context builder and by profile/rule
|
|
19
|
+
* readers; retyping it in a second filter is how a layer silently drops out.
|
|
20
|
+
*/
|
|
21
|
+
export const ANCHOR_TYPES = ['结论', '教训', '原则', '性格'];
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fact lifecycle vocabulary — declared once, imported everywhere.
|
|
3
|
+
*
|
|
4
|
+
* "superseded" used to be typed out in three files, so renaming it would have left
|
|
5
|
+
* one filter silently admitting retired facts. Config modules hold data only; the
|
|
6
|
+
* decoding (SQL binding, filters, prompts) lives in the code that imports this.
|
|
7
|
+
*
|
|
8
|
+
* Values match the column defaults in sqlite.ts; changing one is a schema migration,
|
|
9
|
+
* not a rename.
|
|
10
|
+
*/
|
|
11
|
+
export declare const FACT_STATUS: {
|
|
12
|
+
/** Written, no lifecycle decision yet. */
|
|
13
|
+
readonly none: 'none';
|
|
14
|
+
/** Being worked on (promises, plans). */
|
|
15
|
+
readonly open: 'open';
|
|
16
|
+
/** Held. */
|
|
17
|
+
readonly kept: 'kept';
|
|
18
|
+
/** Broken (a promise that did not happen). */
|
|
19
|
+
readonly broken: 'broken';
|
|
20
|
+
/** Replaced by another fact; kept for the record, never injected. */
|
|
21
|
+
readonly superseded: 'superseded';
|
|
22
|
+
};
|
|
23
|
+
export type FactStatus = (typeof FACT_STATUS)[keyof typeof FACT_STATUS];
|
|
24
|
+
/** Where a fact came from, in the trust sense (not a type). */
|
|
25
|
+
export declare const FACT_SOURCE: {
|
|
26
|
+
readonly experienced: 'experienced';
|
|
27
|
+
readonly told: 'told';
|
|
28
|
+
readonly inferred: 'inferred';
|
|
29
|
+
};
|
|
30
|
+
export type FactSource = (typeof FACT_SOURCE)[keyof typeof FACT_SOURCE];
|
|
31
|
+
/** How precise a time is. `unknown` is an honest answer, not a failure. */
|
|
32
|
+
export declare const AT_PRECISION: {
|
|
33
|
+
readonly day: 'day';
|
|
34
|
+
readonly range: 'range';
|
|
35
|
+
readonly relative: 'relative';
|
|
36
|
+
readonly unknown: 'unknown';
|
|
37
|
+
};
|
|
38
|
+
export type AtPrecision = (typeof AT_PRECISION)[keyof typeof AT_PRECISION];
|
|
39
|
+
/**
|
|
40
|
+
* The legacy `memories` layer's lifecycle.
|
|
41
|
+
*
|
|
42
|
+
* It shares the word "superseded" with facts on purpose: one lifecycle concept, one
|
|
43
|
+
* set of values, one declaration. The table retires (see retirements.json), the
|
|
44
|
+
* vocabulary does not fork before it does.
|
|
45
|
+
*/
|
|
46
|
+
export declare const MEMORY_STATUS: {
|
|
47
|
+
readonly active: 'active';
|
|
48
|
+
readonly superseded: 'superseded';
|
|
49
|
+
readonly archived: 'archived';
|
|
50
|
+
};
|
|
51
|
+
export type MemoryStatus = (typeof MEMORY_STATUS)[keyof typeof MEMORY_STATUS];
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fact lifecycle vocabulary — declared once, imported everywhere.
|
|
3
|
+
*
|
|
4
|
+
* "superseded" used to be typed out in three files, so renaming it would have left
|
|
5
|
+
* one filter silently admitting retired facts. Config modules hold data only; the
|
|
6
|
+
* decoding (SQL binding, filters, prompts) lives in the code that imports this.
|
|
7
|
+
*
|
|
8
|
+
* Values match the column defaults in sqlite.ts; changing one is a schema migration,
|
|
9
|
+
* not a rename.
|
|
10
|
+
*/
|
|
11
|
+
export const FACT_STATUS = {
|
|
12
|
+
/** Written, no lifecycle decision yet. */
|
|
13
|
+
none: 'none',
|
|
14
|
+
/** Being worked on (promises, plans). */
|
|
15
|
+
open: 'open',
|
|
16
|
+
/** Held. */
|
|
17
|
+
kept: 'kept',
|
|
18
|
+
/** Broken (a promise that did not happen). */
|
|
19
|
+
broken: 'broken',
|
|
20
|
+
/** Replaced by another fact; kept for the record, never injected. */
|
|
21
|
+
superseded: 'superseded',
|
|
22
|
+
};
|
|
23
|
+
/** Where a fact came from, in the trust sense (not a type). */
|
|
24
|
+
export const FACT_SOURCE = {
|
|
25
|
+
experienced: 'experienced',
|
|
26
|
+
told: 'told',
|
|
27
|
+
inferred: 'inferred',
|
|
28
|
+
};
|
|
29
|
+
/** How precise a time is. `unknown` is an honest answer, not a failure. */
|
|
30
|
+
export const AT_PRECISION = {
|
|
31
|
+
day: 'day',
|
|
32
|
+
range: 'range',
|
|
33
|
+
relative: 'relative',
|
|
34
|
+
unknown: 'unknown',
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* The legacy `memories` layer's lifecycle.
|
|
38
|
+
*
|
|
39
|
+
* It shares the word "superseded" with facts on purpose: one lifecycle concept, one
|
|
40
|
+
* set of values, one declaration. The table retires (see retirements.json), the
|
|
41
|
+
* vocabulary does not fork before it does.
|
|
42
|
+
*/
|
|
43
|
+
export const MEMORY_STATUS = {
|
|
44
|
+
active: 'active',
|
|
45
|
+
superseded: 'superseded',
|
|
46
|
+
archived: 'archived',
|
|
47
|
+
};
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Consolidation: several facts become one conclusion, with the evidence kept.
|
|
3
|
+
*
|
|
4
|
+
* Two ideas are borrowed rather than invented here:
|
|
5
|
+
*
|
|
6
|
+
* - AIS / attribution (Rashkin et al. 2021): every sentence must be
|
|
7
|
+
* attributable to identified sources. A conclusion cites fact ids, and one
|
|
8
|
+
* citation is not enough — that is a fact, not a conclusion.
|
|
9
|
+
* - Chain-of-Verification (Dhuliawala et al. 2023): verifying a draft against
|
|
10
|
+
* its sources works better when the check is independent of the draft. The
|
|
11
|
+
* model writes conclusions in one call and answers "do these facts directly
|
|
12
|
+
* support it?" in a second, without the first answer in view.
|
|
13
|
+
*
|
|
14
|
+
* The gate below is deliberately deterministic: evidence count, evidence
|
|
15
|
+
* existence, statement shape, and over-generalisation are all checkable without
|
|
16
|
+
* a model. The model only does what code cannot — compress meaning.
|
|
17
|
+
*/
|
|
18
|
+
import type { ConclusionDraft, Fact, SlotValue } from './types.ts';
|
|
19
|
+
export interface ConclusionGateInput {
|
|
20
|
+
draft: ConclusionDraft;
|
|
21
|
+
/** Facts the model was shown; evidence must come from here. */
|
|
22
|
+
allowed: Set<string>;
|
|
23
|
+
/** Existing conclusion the draft claims to replace, if any. */
|
|
24
|
+
existing?: Fact | null;
|
|
25
|
+
}
|
|
26
|
+
export type ConclusionGateResult = {
|
|
27
|
+
ok: true;
|
|
28
|
+
statement: string;
|
|
29
|
+
evidence: string[];
|
|
30
|
+
confidence: number;
|
|
31
|
+
} | {
|
|
32
|
+
ok: false;
|
|
33
|
+
reason: string;
|
|
34
|
+
};
|
|
35
|
+
export declare function gateConclusion(input: ConclusionGateInput): ConclusionGateResult;
|
|
36
|
+
/**
|
|
37
|
+
* The fact a conclusion becomes.
|
|
38
|
+
*
|
|
39
|
+
* No new table: a conclusion is a fact whose type is 结论, whose holder is the
|
|
40
|
+
* soul itself, and whose evidence is a `fact` slot per citation. That keeps
|
|
41
|
+
* retrieval, export and supersede working unchanged.
|
|
42
|
+
*
|
|
43
|
+
* A conclusion also carries the entities its evidence mentions, under `关于`.
|
|
44
|
+
* Without that it cannot be recalled when the conversation turns to that person —
|
|
45
|
+
* the one question the whole layer exists to answer.
|
|
46
|
+
*/
|
|
47
|
+
export declare function conclusionSlots(evidence: string[], entities?: string[]): {
|
|
48
|
+
slot: string;
|
|
49
|
+
value: SlotValue;
|
|
50
|
+
}[];
|
|
51
|
+
/**
|
|
52
|
+
* Parse the draft answer: one conclusion per line.
|
|
53
|
+
*
|
|
54
|
+
* 结论: 用户讨厌同一件事反复交代 | 证据=fc_a1,fc_a2 | 置信=0.8 | 取代=fc_c1
|
|
55
|
+
* 无
|
|
56
|
+
*/
|
|
57
|
+
export declare function parseConclusionDraft(raw: string): ConclusionDraft | null;
|
|
58
|
+
export interface VerificationDraft {
|
|
59
|
+
verdict: 'supported' | 'partial' | 'unsupported';
|
|
60
|
+
reason?: string;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Parse the verification answer:
|
|
64
|
+
*
|
|
65
|
+
* 判定: 支持 | 依据=fc_a1,fc_a2 | 理由=两条事实都提到"反复交代"
|
|
66
|
+
* 判定: 不支持 | 理由=只有 fc_a1 提到,fc_a2 说的是工具切换
|
|
67
|
+
*/
|
|
68
|
+
export declare function parseVerificationDraft(raw: string): VerificationDraft | null;
|
|
69
|
+
/**
|
|
70
|
+
* Normalise a statement for the one check a rule can make safely.
|
|
71
|
+
*
|
|
72
|
+
* This is deliberately narrow: it only catches text that is *the same text*, so a
|
|
73
|
+
* hit cannot be wrong. Everything about meaning is the model's call, because
|
|
74
|
+
* "same topic, different words" is exactly where string matching lies.
|
|
75
|
+
*/
|
|
76
|
+
export declare function normalizeStatement(statement: string): string;
|
|
77
|
+
export interface ConclusionRelation {
|
|
78
|
+
kind: 'duplicate' | 'refine' | 'conflict' | 'unrelated';
|
|
79
|
+
target?: string;
|
|
80
|
+
reason?: string;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Parse the relation check:
|
|
84
|
+
*
|
|
85
|
+
* 关系: 重复 fc_abc | 理由=说的是同一件事,只是措辞不同
|
|
86
|
+
* 关系: 细化 fc_abc | 理由=新结论多了"只在紧急时"
|
|
87
|
+
* 关系: 矛盾 fc_abc | 理由=一个说喜欢重复讲,一个说讨厌
|
|
88
|
+
* 关系: 无关
|
|
89
|
+
*
|
|
90
|
+
* A named id that we do not hold is not an error — the caller treats it as
|
|
91
|
+
* unrelated, which only ever writes the new conclusion and never deletes one.
|
|
92
|
+
*/
|
|
93
|
+
export declare function parseRelationDraft(raw: string): ConclusionRelation | null;
|
|
94
|
+
/**
|
|
95
|
+
* Read a verifier's answer, keeping "we could not read it" separate from "no".
|
|
96
|
+
*
|
|
97
|
+
* An empty or unparseable reply is not evidence against a conclusion — it is the
|
|
98
|
+
* documented stream-gone failure. Collapsing it to `unsupported` silently discards
|
|
99
|
+
* a draft that was never actually checked (and before this, it also lost the
|
|
100
|
+
* thread). Only a parsed verdict may drop a conclusion.
|
|
101
|
+
*/
|
|
102
|
+
export declare function readVerification(raw: string): {
|
|
103
|
+
kind: 'verdict';
|
|
104
|
+
verdict: VerificationDraft;
|
|
105
|
+
} | {
|
|
106
|
+
kind: 'retry';
|
|
107
|
+
reason: string;
|
|
108
|
+
};
|
|
109
|
+
export interface ProfileLine {
|
|
110
|
+
statement: string;
|
|
111
|
+
sources: string[];
|
|
112
|
+
dimension: ProfileDimension;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* The dimensions a profile line can belong to.
|
|
116
|
+
*
|
|
117
|
+
* Declared in dimensions.ts so prompts render it instead of retyping the names —
|
|
118
|
+
* a second copy is how "add an eighth dimension" becomes a silent drift.
|
|
119
|
+
*/
|
|
120
|
+
export { PROFILE_DIMENSIONS } from './config/dimensions.ts';
|
|
121
|
+
export type { ProfileDimension } from './config/dimensions.ts';
|
|
122
|
+
import type { ProfileDimension } from './config/dimensions.ts';
|
|
123
|
+
/**
|
|
124
|
+
* Parse the merged profile.
|
|
125
|
+
*
|
|
126
|
+
* Three things are rejected by code rather than hoped for: a line with no source is
|
|
127
|
+
* an assumption, a hedged or absolute line is a failed merge, and a fragment is not
|
|
128
|
+
* a judgment. Near-duplicates are folded, because "the same thing said twice" is the
|
|
129
|
+
* exact failure the merge exists to fix.
|
|
130
|
+
*
|
|
131
|
+
* What code cannot check is direction ("先" becoming "后"). That needs the separate
|
|
132
|
+
* verification pass, and it is why this parser is not the last gate.
|
|
133
|
+
*/
|
|
134
|
+
export declare function parseProfileMerge(raw: string, allowed: Set<string>, expected?: string): ProfileLine[];
|
|
135
|
+
/**
|
|
136
|
+
* A merged line is derived: it exists because other lines do.
|
|
137
|
+
*
|
|
138
|
+
* Derivedness used to be a slot written by exactly one code path. Any line created
|
|
139
|
+
* another way was therefore treated as an original and could be merged again, and
|
|
140
|
+
* re-compressing a compression produces lines thinner than what they came from.
|
|
141
|
+
* Citing a source is what makes a line derived; the marker is only a cache of that,
|
|
142
|
+
* so both are read — a line that names its sources can never be merge input.
|
|
143
|
+
*/
|
|
144
|
+
export declare function isDerivedProfileLine(fact: {
|
|
145
|
+
slots?: {
|
|
146
|
+
slot: string;
|
|
147
|
+
}[];
|
|
148
|
+
}): boolean;
|
|
149
|
+
export interface ProfileCandidate {
|
|
150
|
+
id: string;
|
|
151
|
+
statement: string;
|
|
152
|
+
dimension?: string;
|
|
153
|
+
}
|
|
154
|
+
export interface ProfileGroup {
|
|
155
|
+
/** Empty for candidates that have no dimension yet; they are not merge input. */
|
|
156
|
+
dimension: string;
|
|
157
|
+
items: ProfileCandidate[];
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Cut the merge input along dimensions before any model sees it.
|
|
161
|
+
*
|
|
162
|
+
* A single call across every dimension welded unrelated judgments into one sentence
|
|
163
|
+
* ("以目的实现为准,大胆假设、小心求证"). Which judgment belongs to which lens is a
|
|
164
|
+
* semantic call, but *keeping batches apart* is structure, so code does it: the
|
|
165
|
+
* model only ever compresses lines that are already about the same thing.
|
|
166
|
+
*
|
|
167
|
+
* Unlabelled candidates are their own group. Guessing a dimension to fill a batch
|
|
168
|
+
* would be invention, and a wrong dimension is worse than a missing line.
|
|
169
|
+
*/
|
|
170
|
+
export declare function groupProfileCandidates(items: ProfileCandidate[]): ProfileGroup[];
|