@aikdna/kdna-studio-cli 0.11.0 → 0.13.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/src/ai/filter.js DELETED
@@ -1,70 +0,0 @@
1
- // Sensitive Content Filter — blocks 6 domains in English and Chinese
2
- // Matches behavior of Studio App's SensitiveInferenceFilter
3
- const SENSITIVE_PATTERNS = {
4
- identity: {
5
- en: /\b(passport\s*(no|number|#)|id\s*(card|number|#)|social\s*security|ssn|driver'?s?\s*license|national\s*id|身份证|护照号)\b/i,
6
- zh: /(身份证号|护照号码|社保号|驾驶证号|军官证)/
7
- },
8
- health: {
9
- en: /\b(medical\s*record|patient\s*(id|number)|diagnosis\s*(code|report)|HIV|AIDS|cancer\s*diagnosis|病历|诊断)\b/i,
10
- zh: /(病历|诊断报告|体检报告|HIV|艾滋病|癌症诊断)/
11
- },
12
- political: {
13
- en: /\b(political\s*(party|affiliation|view)|voting\s*record|party\s*membership|political|政治)\b/i,
14
- zh: /(政治面貌|党派|政治立场|政见)/
15
- },
16
- religious: {
17
- en: /\b(religious\s*(belief|affiliation|view)|religion|faith\s*(group|community)|宗教)\b/i,
18
- zh: /(宗教信仰|宗教派别|教派)/
19
- },
20
- financial: {
21
- en: /\b(bank\s*account|credit\s*card\s*number|financial\s*(record|history)|income\s*(level|bracket)|salary|银行|金融)\b/i,
22
- zh: /(银行卡号|信用卡号|银行账户|收入水平|财务状况|工资)/
23
- },
24
- intimate: {
25
- en: /\b(sexual\s*(orientation|preference|identity)|gender\s*identity|sex\s*(life|history)|marital\s*status|性|性别|婚姻)\b/i,
26
- zh: /(性取向|性别认同|性生活|婚姻状况|性偏好)/
27
- },
28
- };
29
-
30
- function check(text, options = {}) {
31
- if (!text || typeof text !== 'string') return { safe: true, flagged: [], matches: {} };
32
-
33
- const flagged = [];
34
- const matches = {};
35
-
36
- for (const [domain, patterns] of Object.entries(SENSITIVE_PATTERNS)) {
37
- const activePatterns = [];
38
- if (options.lang !== 'zh' && patterns.en) activePatterns.push(patterns.en);
39
- if (options.lang !== 'en' && patterns.zh) activePatterns.push(patterns.zh);
40
-
41
- for (const pattern of activePatterns) {
42
- const match = text.match(pattern);
43
- if (match) {
44
- flagged.push(domain);
45
- matches[domain] = match[0];
46
- break;
47
- }
48
- }
49
- }
50
-
51
- return { safe: flagged.length === 0, flagged, matches };
52
- }
53
-
54
- function checkEvidence(evidenceList, options = {}) {
55
- const results = [];
56
- for (const evidence of evidenceList) {
57
- const content = evidence.raw_text || evidence.content || '';
58
- const result = check(content, options);
59
- if (!result.safe) {
60
- results.push({
61
- filename: evidence.filename || evidence.name || 'unknown',
62
- flagged: result.flagged,
63
- matches: result.matches,
64
- });
65
- }
66
- }
67
- return results;
68
- }
69
-
70
- module.exports = { check, checkEvidence, SENSITIVE_PATTERNS };
package/src/ai/index.js DELETED
@@ -1,4 +0,0 @@
1
- const distill = require('./distill');
2
- const interview = require('./interview');
3
-
4
- module.exports = { distill, interview };
@@ -1,170 +0,0 @@
1
- // AI Interview — 4-stage guided judgment authoring
2
- const llm = require('../llm');
3
-
4
- const STAGES = {
5
- distillJudgment: {
6
- label: 'Extract Judgment',
7
- system: `You are a domain judgment expert. Your task is to help the user distill their raw knowledge into clear, actionable judgment principles (axioms).
8
-
9
- For each axiom, ask the user:
10
- 1. What is the core judgment? (one sentence)
11
- 2. When does this apply? (specific triggers)
12
- 3. When does it NOT apply? (boundaries, exceptions)
13
- 4. What happens if they get this wrong? (failure risk)
14
-
15
- Be conversational but focused. After the user provides their thoughts, synthesize them into a draft axiom with the format:
16
- - one_sentence: "..."
17
- - full_statement: "..."
18
- - applies_when: [...]
19
- - does_not_apply_when: [...]
20
- - failure_risk: "..."
21
-
22
- Then ask if they want to refine it or move to the next principle.`,
23
- },
24
- clarifyBoundaries: {
25
- label: 'Clarify Boundaries',
26
- system: `You are a boundary clarification expert. Review the axioms drafted so far and help the user identify gaps:
27
-
28
- 1. Where might these axioms conflict with each other?
29
- 2. What edge cases are not covered?
30
- 3. Are the "does not apply" conditions specific enough, or are they vague?
31
- 4. Would a reasonable person disagree with any of these judgments?
32
-
33
- Present one gap at a time and ask the user to clarify. Be direct but constructive.`,
34
- },
35
- correctMisreadings: {
36
- label: 'Correct Misreadings',
37
- system: `You are a misunderstanding detector. For each axiom, imagine how an AI agent might MISINTERPRET it:
38
-
39
- 1. Literal vs intended meaning
40
- 2. Over-application (applying too broadly)
41
- 3. Under-application (not applying when it should)
42
- 4. Cultural or context-specific assumptions
43
-
44
- For each potential misunderstanding, ask the user: "Is this a valid concern? How would you correct it?"
45
-
46
- The user's corrections will become misunderstanding cards that guide the agent toward correct interpretation.`,
47
- },
48
- replayScenario: {
49
- label: 'Replay Scenario',
50
- system: `You are a scenario simulation expert. For the axioms and misunderstandings defined so far, create realistic test scenarios:
51
-
52
- 1. A clear-cut case where the judgment applies perfectly
53
- 2. An ambiguous case that tests the boundaries
54
- 3. A case where the judgment should NOT apply (tempting but wrong)
55
-
56
- Present each scenario to the user and ask:
57
- - "What would the correct judgment be here?"
58
- - "What makes this tricky?"
59
- - "Does this reveal any gaps in the axioms?"
60
-
61
- The user's responses become the basis for eval cases and scenario cards.`,
62
- },
63
- };
64
-
65
- const STAGE_ALIASES = Object.freeze({
66
- distill: 'distillJudgment',
67
- clarify: 'clarifyBoundaries',
68
- correct: 'correctMisreadings',
69
- replay: 'replayScenario',
70
- });
71
-
72
- function normalizeInterviewStage(stage) {
73
- const normalized = STAGE_ALIASES[stage] || stage;
74
- if (!STAGES[normalized]) {
75
- throw new Error(
76
- `Unknown interview stage: ${stage}. Valid: ${[
77
- ...Object.keys(STAGE_ALIASES),
78
- ...Object.keys(STAGES),
79
- ].join(', ')}`,
80
- );
81
- }
82
- return normalized;
83
- }
84
-
85
- async function runInterview(config, project, stage, context, options = {}) {
86
- stage = normalizeInterviewStage(stage);
87
- const stageConfig = STAGES[stage];
88
-
89
- const systemPrompt = stageConfig.system;
90
- const userPrompt = [
91
- `## Project: ${project.name || 'Untitled'}`,
92
- project.description ? `## Description: ${project.description}` : '',
93
- ``,
94
- `## Current State`,
95
- `Cards: ${(project.cards || []).length} (${(project.cards || []).filter(c => c.locked).length} locked)`,
96
- project.distillation_candidates ? `Candidates: ${project.distillation_candidates.length}` : '',
97
- ``,
98
- `## Context`,
99
- context || '(no additional context)',
100
- ``,
101
- `Begin ${stageConfig.label} stage.`,
102
- ].filter(Boolean).join('\n');
103
-
104
- const result = await llm.chat([
105
- { role: 'system', content: systemPrompt },
106
- { role: 'user', content: userPrompt },
107
- ], options);
108
-
109
- return { stage, label: stageConfig.label, content: result.content };
110
- }
111
-
112
- async function runInterviewInteractive(config, project, options = {}) {
113
- const readline = require('readline');
114
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
115
- const ask = (q) => new Promise(resolve => rl.question(q, resolve));
116
-
117
- console.log(`\n=== KDNA Studio — AI Interview ===`);
118
- console.log(`Project: ${project.name || 'Untitled'}`);
119
- console.log(`4 stages: distill → clarify → correct → replay\n`);
120
-
121
- const conversation = [];
122
- let results = [];
123
-
124
- for (const [stageId, stageConfig] of Object.entries(STAGES)) {
125
- console.log(`\n--- ${stageConfig.label} ---`);
126
-
127
- const contextParts = [`Project: ${project.name || 'Untitled'}`];
128
- if (project.description) contextParts.push(`Description: ${project.description}`);
129
- const cardsSummary = (project.cards || []).map(c => `[${c.type}] ${c.one_sentence || c.concept_name || c.wrong_interpretation || ''}`).join('\n');
130
- if (cardsSummary) contextParts.push(`Existing cards:\n${cardsSummary}`);
131
-
132
- const initialResponse = await llm.chat([
133
- { role: 'system', content: stageConfig.system },
134
- { role: 'user', content: `Start the ${stageConfig.label} stage.\n\nContext:\n${contextParts.join('\n')}` },
135
- ], options);
136
- console.log(`\nAI: ${initialResponse.content}\n`);
137
-
138
- let keepGoing = true;
139
- while (keepGoing) {
140
- const answer = await ask('You (type "next" to proceed, "done" to finish this stage): ');
141
- if (answer.toLowerCase() === 'next' || answer.toLowerCase() === 'done') {
142
- keepGoing = false;
143
- if (answer.toLowerCase() === 'done') break;
144
- continue;
145
- }
146
- if (!answer.trim()) continue;
147
-
148
- conversation.push({ stage: stageId, role: 'user', content: answer });
149
- const response = await llm.chat([
150
- { role: 'system', content: stageConfig.system },
151
- ...conversation.filter(m => m.stage === stageId).map(m => ({ role: m.role, content: m.content })),
152
- ], options);
153
- conversation.push({ stage: stageId, role: 'assistant', content: response.content });
154
- console.log(`\nAI: ${response.content}\n`);
155
- }
156
-
157
- results.push({ stage: stageId, label: stageConfig.label, conversation: conversation.filter(m => m.stage === stageId) });
158
- }
159
-
160
- rl.close();
161
- return results;
162
- }
163
-
164
- module.exports = {
165
- STAGES,
166
- STAGE_ALIASES,
167
- normalizeInterviewStage,
168
- runInterview,
169
- runInterviewInteractive,
170
- };