@thispointon/kondi-chat 0.1.2
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/LICENSE +21 -0
- package/README.md +556 -0
- package/bin/kondi-chat +56 -0
- package/bin/kondi-chat.js +72 -0
- package/package.json +55 -0
- package/scripts/demo.tape +49 -0
- package/scripts/postinstall.cjs +103 -0
- package/src/audit/analytics.ts +261 -0
- package/src/audit/ledger.ts +253 -0
- package/src/audit/telemetry.ts +165 -0
- package/src/cli/backend.ts +675 -0
- package/src/cli/commands.ts +419 -0
- package/src/cli/help.ts +182 -0
- package/src/cli/submit-helpers.ts +159 -0
- package/src/cli/submit.ts +539 -0
- package/src/cli/wizard.ts +121 -0
- package/src/context/bootstrap.ts +138 -0
- package/src/context/budget.ts +100 -0
- package/src/context/manager.ts +666 -0
- package/src/context/memory.ts +160 -0
- package/src/context/preflight.ts +176 -0
- package/src/context/project-brain.ts +101 -0
- package/src/context/receipts.ts +108 -0
- package/src/context/skills.ts +154 -0
- package/src/context/symbol-index.ts +240 -0
- package/src/council/profiles.ts +137 -0
- package/src/council/tool.ts +138 -0
- package/src/council-engine/cli/council-artifacts.ts +230 -0
- package/src/council-engine/cli/council-config.ts +178 -0
- package/src/council-engine/cli/council-session-export.ts +116 -0
- package/src/council-engine/cli/kondi.ts +98 -0
- package/src/council-engine/cli/llm-caller.ts +229 -0
- package/src/council-engine/cli/localStorage-shim.ts +119 -0
- package/src/council-engine/cli/node-platform.ts +68 -0
- package/src/council-engine/cli/run-council.ts +481 -0
- package/src/council-engine/cli/run-pipeline.ts +772 -0
- package/src/council-engine/cli/session-export.ts +153 -0
- package/src/council-engine/configs/councils/analysis.json +101 -0
- package/src/council-engine/configs/councils/code-planning.json +86 -0
- package/src/council-engine/configs/councils/coding.json +89 -0
- package/src/council-engine/configs/councils/debate.json +97 -0
- package/src/council-engine/configs/councils/solo-claude.json +34 -0
- package/src/council-engine/configs/councils/solo-gpt.json +34 -0
- package/src/council-engine/council/coding-orchestrator.ts +1205 -0
- package/src/council-engine/council/context-bootstrap.ts +147 -0
- package/src/council-engine/council/context-inspection.ts +42 -0
- package/src/council-engine/council/context-store.ts +763 -0
- package/src/council-engine/council/deliberation-orchestrator.ts +2762 -0
- package/src/council-engine/council/factory.ts +164 -0
- package/src/council-engine/council/index.ts +201 -0
- package/src/council-engine/council/ledger-store.ts +438 -0
- package/src/council-engine/council/prompts.ts +1689 -0
- package/src/council-engine/council/storage-cleanup.ts +164 -0
- package/src/council-engine/council/store.ts +1110 -0
- package/src/council-engine/council/synthesis.ts +291 -0
- package/src/council-engine/council/types.ts +845 -0
- package/src/council-engine/council/validation.ts +613 -0
- package/src/council-engine/pipeline/build-detect.ts +73 -0
- package/src/council-engine/pipeline/executor.ts +1048 -0
- package/src/council-engine/pipeline/index.ts +9 -0
- package/src/council-engine/pipeline/install-detect.ts +84 -0
- package/src/council-engine/pipeline/memory-store.ts +182 -0
- package/src/council-engine/pipeline/output-parsers.ts +146 -0
- package/src/council-engine/pipeline/run-output.ts +149 -0
- package/src/council-engine/pipeline/session-import.ts +177 -0
- package/src/council-engine/pipeline/store.ts +753 -0
- package/src/council-engine/pipeline/test-detect.ts +82 -0
- package/src/council-engine/pipeline/types.ts +401 -0
- package/src/council-engine/services/deliberationSummary.ts +114 -0
- package/src/council-engine/tsconfig.json +16 -0
- package/src/council-engine/types/mcp.ts +122 -0
- package/src/council-engine/utils/filterTools.ts +73 -0
- package/src/engine/apply.ts +238 -0
- package/src/engine/checkpoints.ts +237 -0
- package/src/engine/consultants.ts +347 -0
- package/src/engine/diff.ts +171 -0
- package/src/engine/errors.ts +102 -0
- package/src/engine/git-tools.ts +246 -0
- package/src/engine/hooks.ts +181 -0
- package/src/engine/loop-guard.ts +155 -0
- package/src/engine/permissions.ts +293 -0
- package/src/engine/pipeline.ts +376 -0
- package/src/engine/sub-agents.ts +133 -0
- package/src/engine/task-card.ts +185 -0
- package/src/engine/task-router.ts +256 -0
- package/src/engine/task-store.ts +86 -0
- package/src/engine/tools.ts +783 -0
- package/src/engine/verify.ts +111 -0
- package/src/mcp/client.ts +225 -0
- package/src/mcp/config.ts +120 -0
- package/src/mcp/tool-manager.ts +192 -0
- package/src/mcp/types.ts +61 -0
- package/src/providers/llm-caller.ts +943 -0
- package/src/providers/rate-limiter.ts +238 -0
- package/src/router/NOTES.md +28 -0
- package/src/router/collector.ts +474 -0
- package/src/router/embeddings.ts +286 -0
- package/src/router/index.ts +299 -0
- package/src/router/intent-router.ts +225 -0
- package/src/router/nn-router.ts +205 -0
- package/src/router/profiles.ts +309 -0
- package/src/router/registry.ts +565 -0
- package/src/router/rules.ts +274 -0
- package/src/router/train.py +408 -0
- package/src/session/store.ts +211 -0
- package/src/test-utils/mock-llm.ts +39 -0
- package/src/types.ts +322 -0
- package/src/web/manager.ts +311 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test Command Auto-Detection
|
|
3
|
+
* Scans project files to determine the appropriate test command.
|
|
4
|
+
* Accepts an optional readFile callback to abstract file I/O (Tauri / Node.js).
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export type ReadFileFn = (path: string) => Promise<string | null>;
|
|
8
|
+
|
|
9
|
+
export interface DetectedTest {
|
|
10
|
+
command: string;
|
|
11
|
+
framework: string;
|
|
12
|
+
confidence: 'high' | 'medium' | 'low';
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Detect the appropriate test command for a project directory.
|
|
17
|
+
* Checks common project files in priority order.
|
|
18
|
+
*
|
|
19
|
+
* @param workingDir Absolute path to the project root
|
|
20
|
+
* @param readFile Optional callback that reads a file and returns its content
|
|
21
|
+
* (or null if not found). When omitted, detection is skipped.
|
|
22
|
+
*/
|
|
23
|
+
export async function detectTestCommand(
|
|
24
|
+
workingDir: string,
|
|
25
|
+
readFile?: ReadFileFn,
|
|
26
|
+
): Promise<DetectedTest | null> {
|
|
27
|
+
if (!readFile) return null;
|
|
28
|
+
|
|
29
|
+
const fileExists = async (path: string): Promise<string | null> => {
|
|
30
|
+
try {
|
|
31
|
+
return await readFile(path);
|
|
32
|
+
} catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// 1. package.json — Node.js projects
|
|
38
|
+
const packageJson = await fileExists(`${workingDir}/package.json`);
|
|
39
|
+
if (packageJson) {
|
|
40
|
+
try {
|
|
41
|
+
const pkg = JSON.parse(packageJson);
|
|
42
|
+
if (pkg.scripts?.test && pkg.scripts.test !== 'echo "Error: no test specified" && exit 1') {
|
|
43
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
44
|
+
let framework = 'npm test';
|
|
45
|
+
if (deps?.vitest) framework = 'vitest';
|
|
46
|
+
else if (deps?.jest) framework = 'jest';
|
|
47
|
+
else if (deps?.mocha) framework = 'mocha';
|
|
48
|
+
return { command: 'npm test', framework, confidence: 'high' };
|
|
49
|
+
}
|
|
50
|
+
} catch { /* invalid JSON, skip */ }
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// 2. Cargo.toml — Rust projects
|
|
54
|
+
const cargoToml = await fileExists(`${workingDir}/Cargo.toml`);
|
|
55
|
+
if (cargoToml) {
|
|
56
|
+
return { command: 'cargo test', framework: 'cargo', confidence: 'high' };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// 3. go.mod — Go projects
|
|
60
|
+
const goMod = await fileExists(`${workingDir}/go.mod`);
|
|
61
|
+
if (goMod) {
|
|
62
|
+
return { command: 'go test ./...', framework: 'go test', confidence: 'high' };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// 4. Python — pytest or unittest
|
|
66
|
+
const pytestIni = await fileExists(`${workingDir}/pytest.ini`);
|
|
67
|
+
if (pytestIni) {
|
|
68
|
+
return { command: 'pytest', framework: 'pytest', confidence: 'high' };
|
|
69
|
+
}
|
|
70
|
+
const pyprojectToml = await fileExists(`${workingDir}/pyproject.toml`);
|
|
71
|
+
if (pyprojectToml && pyprojectToml.includes('[tool.pytest')) {
|
|
72
|
+
return { command: 'pytest', framework: 'pytest', confidence: 'high' };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// 5. Makefile with test target
|
|
76
|
+
const makefile = await fileExists(`${workingDir}/Makefile`);
|
|
77
|
+
if (makefile && /^test\s*:/m.test(makefile)) {
|
|
78
|
+
return { command: 'make test', framework: 'make', confidence: 'medium' };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pipeline System: Type Definitions
|
|
3
|
+
* Councils as Workflow Steps — ordered stages with parallel steps
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// Step & Pipeline Status
|
|
8
|
+
// ============================================================================
|
|
9
|
+
|
|
10
|
+
/** All pipeline step types. Gate, script, and condition are non-council types. */
|
|
11
|
+
export const PIPELINE_STEP_TYPES = ['council', 'code_planning', 'analysis', 'agent', 'coding', 'review', 'enrich', 'gate', 'script', 'condition'] as const;
|
|
12
|
+
export type PipelineStepType = (typeof PIPELINE_STEP_TYPES)[number];
|
|
13
|
+
|
|
14
|
+
/** Council-based step types (excludes gate, script, condition). */
|
|
15
|
+
export const COUNCIL_STEP_TYPES = ['council', 'code_planning', 'analysis', 'agent', 'coding', 'review', 'enrich'] as const;
|
|
16
|
+
export type CouncilStepType = (typeof COUNCIL_STEP_TYPES)[number];
|
|
17
|
+
|
|
18
|
+
/** Human-readable labels for each step type */
|
|
19
|
+
export const STEP_TYPE_LABELS: Record<PipelineStepType, string> = {
|
|
20
|
+
council: 'Council',
|
|
21
|
+
code_planning: 'Code Planning',
|
|
22
|
+
analysis: 'Analysis',
|
|
23
|
+
agent: 'Agent',
|
|
24
|
+
coding: 'Coding',
|
|
25
|
+
review: 'Review',
|
|
26
|
+
enrich: 'Enrich',
|
|
27
|
+
gate: 'Gate',
|
|
28
|
+
script: 'Script',
|
|
29
|
+
condition: 'Condition',
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/** What kind of data a step produces for downstream consumption */
|
|
33
|
+
export type OutputType = 'string' | 'file' | 'directory' | 'json';
|
|
34
|
+
|
|
35
|
+
export type PipelineStepStatus =
|
|
36
|
+
| 'pending'
|
|
37
|
+
| 'running'
|
|
38
|
+
| 'completed'
|
|
39
|
+
| 'failed'
|
|
40
|
+
| 'skipped'
|
|
41
|
+
| 'waiting_approval';
|
|
42
|
+
|
|
43
|
+
export type PipelineStatus =
|
|
44
|
+
| 'draft'
|
|
45
|
+
| 'ready'
|
|
46
|
+
| 'running'
|
|
47
|
+
| 'completed'
|
|
48
|
+
| 'failed'
|
|
49
|
+
| 'paused';
|
|
50
|
+
|
|
51
|
+
// ============================================================================
|
|
52
|
+
// Step Artifacts (output produced by a completed step)
|
|
53
|
+
// ============================================================================
|
|
54
|
+
|
|
55
|
+
export interface StepArtifact {
|
|
56
|
+
stepId: string;
|
|
57
|
+
content: string;
|
|
58
|
+
artifactType: 'decision' | 'output' | 'llm_response' | 'approval';
|
|
59
|
+
metadata?: {
|
|
60
|
+
councilId?: string;
|
|
61
|
+
decisionId?: string;
|
|
62
|
+
outputId?: string;
|
|
63
|
+
model?: string;
|
|
64
|
+
tokensUsed?: number;
|
|
65
|
+
outputPath?: string; // file path where output was saved (planning/coding steps)
|
|
66
|
+
outputType?: OutputType; // what kind of data this artifact represents
|
|
67
|
+
stepName?: string; // human-readable name of the producing step
|
|
68
|
+
stepType?: string; // PipelineStepType value
|
|
69
|
+
};
|
|
70
|
+
createdAt: string;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ============================================================================
|
|
74
|
+
// Memory Entry (persistent across scheduled runs)
|
|
75
|
+
// ============================================================================
|
|
76
|
+
|
|
77
|
+
export interface MemoryEntry {
|
|
78
|
+
runNumber: number;
|
|
79
|
+
runDate: string;
|
|
80
|
+
/** Captured artifacts from designated steps, keyed by sanitized step name */
|
|
81
|
+
captures: Record<string, string>;
|
|
82
|
+
/** True if this entry is a compressed summary of multiple older entries */
|
|
83
|
+
compressed?: boolean;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// ============================================================================
|
|
87
|
+
// Output Isolation Config
|
|
88
|
+
// ============================================================================
|
|
89
|
+
|
|
90
|
+
export interface PipelineOutputConfig {
|
|
91
|
+
/** Enable structured output directories (default: true for new pipelines) */
|
|
92
|
+
enabled: boolean;
|
|
93
|
+
/** Max runs to keep on disk before pruning oldest (default: 50, 0 = unlimited) */
|
|
94
|
+
maxRetainedRuns?: number;
|
|
95
|
+
/** What to save per step */
|
|
96
|
+
stepOutput: 'artifact_only' | 'artifact_and_deliberation';
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Run manifest written to _manifest.json */
|
|
100
|
+
export interface RunManifest {
|
|
101
|
+
runNumber: number;
|
|
102
|
+
pipelineId: string;
|
|
103
|
+
pipelineName: string;
|
|
104
|
+
startedAt: string;
|
|
105
|
+
completedAt: string;
|
|
106
|
+
status: 'completed' | 'failed';
|
|
107
|
+
initialInput: string;
|
|
108
|
+
stageCount: number;
|
|
109
|
+
stepCount: number;
|
|
110
|
+
totalTokens: number;
|
|
111
|
+
totalDurationMs: number;
|
|
112
|
+
/** Whether memory was updated from this run */
|
|
113
|
+
memoryUpdated: boolean;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Step metadata written to _meta.json */
|
|
117
|
+
export interface StepMeta {
|
|
118
|
+
stepId: string;
|
|
119
|
+
stepName: string;
|
|
120
|
+
stepType: string;
|
|
121
|
+
stageIndex: number;
|
|
122
|
+
stepIndex: number;
|
|
123
|
+
startedAt?: string;
|
|
124
|
+
completedAt?: string;
|
|
125
|
+
status: PipelineStepStatus;
|
|
126
|
+
outputType: OutputType;
|
|
127
|
+
councilId?: string;
|
|
128
|
+
model?: string;
|
|
129
|
+
tokensUsed?: number;
|
|
130
|
+
durationMs?: number;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// ============================================================================
|
|
134
|
+
// Pipeline Persona (full-featured, matches council persona capabilities)
|
|
135
|
+
// ============================================================================
|
|
136
|
+
|
|
137
|
+
export interface PipelinePersona {
|
|
138
|
+
templateId?: string;
|
|
139
|
+
name: string;
|
|
140
|
+
role: 'manager' | 'consultant' | 'worker' | 'reviewer';
|
|
141
|
+
model: string;
|
|
142
|
+
provider: string;
|
|
143
|
+
avatar?: string;
|
|
144
|
+
color?: string;
|
|
145
|
+
systemPrompt?: string;
|
|
146
|
+
stance?: 'advocate' | 'critic' | 'neutral' | 'wildcard';
|
|
147
|
+
traits?: string[];
|
|
148
|
+
interactionStyle?: 'debate' | 'build' | 'question' | 'synthesize' | 'review';
|
|
149
|
+
domain?: string;
|
|
150
|
+
temperature?: number;
|
|
151
|
+
verbosity?: 'concise' | 'balanced' | 'thorough';
|
|
152
|
+
focusArea?: string;
|
|
153
|
+
startingStance?: string;
|
|
154
|
+
suppressPersona?: boolean;
|
|
155
|
+
/** Worker-only: save the worker's output to the working directory (default: true) */
|
|
156
|
+
saveOutput?: boolean;
|
|
157
|
+
/** MCP servers this persona can access (undefined = all servers) */
|
|
158
|
+
allowedServerIds?: string[];
|
|
159
|
+
/** Override default tool behavior: 'full' = enable all tools, 'none' = disable tools */
|
|
160
|
+
toolAccess?: 'full' | 'none';
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// ============================================================================
|
|
164
|
+
// Step Configs
|
|
165
|
+
// ============================================================================
|
|
166
|
+
|
|
167
|
+
export interface CouncilStepConfig {
|
|
168
|
+
type: CouncilStepType;
|
|
169
|
+
councilSetup: {
|
|
170
|
+
name: string;
|
|
171
|
+
personas: PipelinePersona[];
|
|
172
|
+
maxRounds?: number;
|
|
173
|
+
maxRevisions?: number;
|
|
174
|
+
expectedOutput?: string;
|
|
175
|
+
decisionCriteria?: string[];
|
|
176
|
+
workingDirectory?: string;
|
|
177
|
+
directoryConstrained?: boolean;
|
|
178
|
+
// Coding orchestrator config
|
|
179
|
+
testCommand?: string;
|
|
180
|
+
maxDebugCycles?: number;
|
|
181
|
+
maxReviewCycles?: number;
|
|
182
|
+
/** MCP servers this step can access (undefined = all servers) */
|
|
183
|
+
allowedServerIds?: string[];
|
|
184
|
+
};
|
|
185
|
+
/** Standing instructions — what this step should DO (supplemental to input context) */
|
|
186
|
+
task?: string;
|
|
187
|
+
/** Template that renders previous step outputs as input context */
|
|
188
|
+
inputTemplate: string;
|
|
189
|
+
/** What kind of data this step produces (default: 'string') */
|
|
190
|
+
outputType?: OutputType;
|
|
191
|
+
/** Include pipeline's initial input as additional context, regardless of stage */
|
|
192
|
+
includePipelineInput?: boolean;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export interface LlmStepConfig {
|
|
196
|
+
type: 'analysis' | 'agent';
|
|
197
|
+
model: string;
|
|
198
|
+
provider: string;
|
|
199
|
+
systemPrompt: string;
|
|
200
|
+
inputTemplate: string;
|
|
201
|
+
workingDirectory?: string;
|
|
202
|
+
directoryConstrained?: boolean;
|
|
203
|
+
/** MCP servers this step can access (undefined = all servers) */
|
|
204
|
+
allowedServerIds?: string[];
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export interface GateStepConfig {
|
|
208
|
+
type: 'gate';
|
|
209
|
+
approvalPrompt: string;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export interface ScriptStepConfig {
|
|
213
|
+
type: 'script';
|
|
214
|
+
/** Shell command to execute. Previous step output available as $KONDI_INPUT env var. */
|
|
215
|
+
command: string;
|
|
216
|
+
/** Template that renders previous step outputs into the $KONDI_INPUT env var */
|
|
217
|
+
inputTemplate: string;
|
|
218
|
+
/** What kind of data this step produces (default: 'string') */
|
|
219
|
+
outputType?: OutputType;
|
|
220
|
+
/** Include pipeline's initial input as additional context, regardless of stage */
|
|
221
|
+
includePipelineInput?: boolean;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export type ConditionMode = 'contains' | 'regex' | 'equals';
|
|
225
|
+
export type ConditionAction = 'continue' | 'skip_next_stage' | 'stop';
|
|
226
|
+
|
|
227
|
+
export interface ConditionStepConfig {
|
|
228
|
+
type: 'condition';
|
|
229
|
+
/** The expression to match against (string literal, regex pattern, or exact match) */
|
|
230
|
+
expression: string;
|
|
231
|
+
/** How to evaluate the expression against input */
|
|
232
|
+
mode: ConditionMode;
|
|
233
|
+
/** Template that renders previous step outputs as input to evaluate */
|
|
234
|
+
inputTemplate: string;
|
|
235
|
+
/** Action when expression matches */
|
|
236
|
+
trueAction: ConditionAction;
|
|
237
|
+
/** Action when expression does not match */
|
|
238
|
+
falseAction: ConditionAction;
|
|
239
|
+
/** Include pipeline's initial input as additional context, regardless of stage */
|
|
240
|
+
includePipelineInput?: boolean;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export type StepConfig = CouncilStepConfig | LlmStepConfig | GateStepConfig | ScriptStepConfig | ConditionStepConfig;
|
|
244
|
+
|
|
245
|
+
/** Helper: is this a council-based step type? Excludes gate, script, and condition. */
|
|
246
|
+
export function isCouncilType(type: PipelineStepType): boolean {
|
|
247
|
+
return type !== 'gate' && type !== 'script' && type !== 'condition';
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/** Helper: is this a lightweight council (single-agent, 0-round)? */
|
|
251
|
+
export function isLightweightCouncilType(type: PipelineStepType): boolean {
|
|
252
|
+
return type === 'analysis' || type === 'agent';
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* @deprecated Use isLightweightCouncilType instead.
|
|
257
|
+
* Kept for backwards compatibility with existing code.
|
|
258
|
+
*/
|
|
259
|
+
export function isLlmType(type: PipelineStepType): boolean {
|
|
260
|
+
return type === 'analysis' || type === 'agent';
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Migrate legacy LlmStepConfig to CouncilStepConfig.
|
|
265
|
+
* Old pipelines may have { type: 'analysis'|'agent', model, provider, systemPrompt, ... }
|
|
266
|
+
* without a councilSetup. This creates one from the flat fields.
|
|
267
|
+
*/
|
|
268
|
+
export function migrateLlmConfig(config: LlmStepConfig): CouncilStepConfig {
|
|
269
|
+
const isAnalysis = config.type === 'analysis';
|
|
270
|
+
return {
|
|
271
|
+
type: config.type,
|
|
272
|
+
councilSetup: {
|
|
273
|
+
name: isAnalysis ? 'Analysis' : 'Agent',
|
|
274
|
+
personas: [{
|
|
275
|
+
name: isAnalysis ? 'Analyst' : 'Executor',
|
|
276
|
+
role: isAnalysis ? 'manager' : 'worker',
|
|
277
|
+
model: config.model,
|
|
278
|
+
provider: config.provider,
|
|
279
|
+
systemPrompt: config.systemPrompt,
|
|
280
|
+
suppressPersona: true,
|
|
281
|
+
}],
|
|
282
|
+
maxRounds: 0,
|
|
283
|
+
maxRevisions: 0,
|
|
284
|
+
workingDirectory: config.workingDirectory,
|
|
285
|
+
directoryConstrained: config.directoryConstrained,
|
|
286
|
+
allowedServerIds: config.allowedServerIds,
|
|
287
|
+
},
|
|
288
|
+
inputTemplate: config.inputTemplate,
|
|
289
|
+
outputType: 'string',
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// ============================================================================
|
|
294
|
+
// Pipeline Step
|
|
295
|
+
// ============================================================================
|
|
296
|
+
|
|
297
|
+
export interface PipelineStep {
|
|
298
|
+
id: string;
|
|
299
|
+
name: string;
|
|
300
|
+
description?: string;
|
|
301
|
+
config: StepConfig;
|
|
302
|
+
status: PipelineStepStatus;
|
|
303
|
+
artifact?: StepArtifact;
|
|
304
|
+
error?: string;
|
|
305
|
+
startedAt?: string;
|
|
306
|
+
completedAt?: string;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// ============================================================================
|
|
310
|
+
// Pipeline Stage
|
|
311
|
+
// ============================================================================
|
|
312
|
+
|
|
313
|
+
export interface PipelineStage {
|
|
314
|
+
id: string;
|
|
315
|
+
name: string;
|
|
316
|
+
steps: PipelineStep[];
|
|
317
|
+
/** How steps in this stage are executed (default: 'sequential') */
|
|
318
|
+
executionMode?: 'sequential' | 'parallel';
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// ============================================================================
|
|
322
|
+
// Pipeline
|
|
323
|
+
// ============================================================================
|
|
324
|
+
|
|
325
|
+
// ============================================================================
|
|
326
|
+
// Schedule Config
|
|
327
|
+
// ============================================================================
|
|
328
|
+
|
|
329
|
+
export interface PipelineSchedule {
|
|
330
|
+
enabled: boolean;
|
|
331
|
+
/** Time of day in HH:MM (24-hour) format */
|
|
332
|
+
time: string;
|
|
333
|
+
/** Recurrence mode */
|
|
334
|
+
mode: 'once' | 'daily' | 'weekly';
|
|
335
|
+
/** For 'once' mode: ISO date string (YYYY-MM-DD) */
|
|
336
|
+
date?: string;
|
|
337
|
+
/** For 'weekly' mode: 0 = Sunday, 1 = Monday, ... 6 = Saturday */
|
|
338
|
+
dayOfWeek?: number;
|
|
339
|
+
/** ISO timestamp of the last scheduled run (to avoid double-fires) */
|
|
340
|
+
lastRunAt?: string;
|
|
341
|
+
/** Maintain memory across scheduled runs */
|
|
342
|
+
maintainMemory?: boolean;
|
|
343
|
+
/** Max detailed entries before older ones get compressed (default: 30) */
|
|
344
|
+
maxDetailedEntries?: number;
|
|
345
|
+
/** Which steps' artifacts to capture into memory (step IDs).
|
|
346
|
+
* If empty/undefined, captures only the last completed step. */
|
|
347
|
+
captureStepIds?: string[];
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
export interface Pipeline {
|
|
351
|
+
id: string;
|
|
352
|
+
name: string;
|
|
353
|
+
description?: string;
|
|
354
|
+
initialInput: string;
|
|
355
|
+
stages: PipelineStage[];
|
|
356
|
+
settings: {
|
|
357
|
+
workingDirectory?: string;
|
|
358
|
+
directoryConstrained?: boolean;
|
|
359
|
+
failurePolicy: 'stop' | 'skip_step';
|
|
360
|
+
schedule?: PipelineSchedule;
|
|
361
|
+
outputConfig?: PipelineOutputConfig;
|
|
362
|
+
};
|
|
363
|
+
status: PipelineStatus;
|
|
364
|
+
currentStageIndex: number;
|
|
365
|
+
createdAt: string;
|
|
366
|
+
updatedAt: string;
|
|
367
|
+
/** Where this pipeline was executed — 'cli' for CLI-imported sessions */
|
|
368
|
+
source?: 'cli' | 'gui';
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// ============================================================================
|
|
372
|
+
// CLI ↔ GUI Session Export/Import
|
|
373
|
+
// ============================================================================
|
|
374
|
+
|
|
375
|
+
export interface KondiSessionCouncilData {
|
|
376
|
+
ledgerIndex: any;
|
|
377
|
+
ledgerChunks: Record<number, any[]>;
|
|
378
|
+
context: any | null;
|
|
379
|
+
contextHistory: any[];
|
|
380
|
+
contextPatches: any[];
|
|
381
|
+
decision: any | null;
|
|
382
|
+
plan: any | null;
|
|
383
|
+
directive: any | null;
|
|
384
|
+
outputs: any[];
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
export interface KondiSession {
|
|
388
|
+
version: 1;
|
|
389
|
+
exportedAt: string;
|
|
390
|
+
source: 'cli';
|
|
391
|
+
pipeline: Pipeline;
|
|
392
|
+
councils: any[];
|
|
393
|
+
councilData: Record<string, KondiSessionCouncilData>;
|
|
394
|
+
execution: {
|
|
395
|
+
status: 'completed' | 'failed';
|
|
396
|
+
startedAt: string;
|
|
397
|
+
completedAt: string;
|
|
398
|
+
durationMs: number;
|
|
399
|
+
workingDirectory: string;
|
|
400
|
+
};
|
|
401
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deliberation Summary Builder (Tauri-free)
|
|
3
|
+
* Extracted from deliberationSaveService.ts so it can be used by the pipeline
|
|
4
|
+
* executor without Tauri dependencies.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { Council } from '../council/types';
|
|
8
|
+
import { getAllEntries, buildMechanicalSummary } from '../council/ledger-store';
|
|
9
|
+
import { getDecision, getLatestOutput } from '../council/context-store';
|
|
10
|
+
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// Full Deliberation Builder
|
|
13
|
+
// ============================================================================
|
|
14
|
+
|
|
15
|
+
export function buildFullDeliberation(council: Council): string {
|
|
16
|
+
const entries = getAllEntries(council.id);
|
|
17
|
+
if (entries.length === 0) return '# Deliberation\n\nNo entries recorded.';
|
|
18
|
+
|
|
19
|
+
// Group entries by round
|
|
20
|
+
const rounds = new Map<number | 'none', typeof entries>();
|
|
21
|
+
for (const entry of entries) {
|
|
22
|
+
const key = entry.roundNumber ?? 'none';
|
|
23
|
+
if (!rounds.has(key)) rounds.set(key, []);
|
|
24
|
+
rounds.get(key)!.push(entry);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const getPersonaName = (personaId: string): string => {
|
|
28
|
+
const persona = council.personas.find((p) => p.id === personaId);
|
|
29
|
+
return persona?.name || personaId;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const getRoleName = (personaId: string): string => {
|
|
33
|
+
const assignment = council.deliberation?.roleAssignments?.find(
|
|
34
|
+
(r) => r.personaId === personaId
|
|
35
|
+
);
|
|
36
|
+
return assignment?.role || 'unknown';
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
let md = `# Deliberation: ${council.name}\n\n`;
|
|
40
|
+
md += `**Topic:** ${council.topic}\n`;
|
|
41
|
+
md += `**Date:** ${new Date().toISOString().split('T')[0]}\n`;
|
|
42
|
+
md += `**Total Entries:** ${entries.length}\n\n---\n\n`;
|
|
43
|
+
|
|
44
|
+
// Non-round entries first
|
|
45
|
+
const noRound = rounds.get('none');
|
|
46
|
+
if (noRound && noRound.length > 0) {
|
|
47
|
+
for (const entry of noRound) {
|
|
48
|
+
md += `### ${getPersonaName(entry.authorPersonaId)} (${getRoleName(entry.authorPersonaId)}) — ${entry.entryType}\n`;
|
|
49
|
+
md += `*${new Date(entry.timestamp).toLocaleString()}*\n\n`;
|
|
50
|
+
md += `${entry.content}\n\n---\n\n`;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Round entries
|
|
55
|
+
const sortedRounds = Array.from(rounds.keys())
|
|
56
|
+
.filter((k) => k !== 'none')
|
|
57
|
+
.sort((a, b) => (a as number) - (b as number));
|
|
58
|
+
|
|
59
|
+
for (const round of sortedRounds) {
|
|
60
|
+
md += `## Round ${round}\n\n`;
|
|
61
|
+
const roundEntries = rounds.get(round)!;
|
|
62
|
+
for (const entry of roundEntries) {
|
|
63
|
+
md += `### ${getPersonaName(entry.authorPersonaId)} (${getRoleName(entry.authorPersonaId)}) — ${entry.entryType}\n`;
|
|
64
|
+
md += `*${new Date(entry.timestamp).toLocaleString()}*\n\n`;
|
|
65
|
+
md += `${entry.content}\n\n---\n\n`;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return md;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ============================================================================
|
|
73
|
+
// Abbreviated Summary Builder
|
|
74
|
+
// ============================================================================
|
|
75
|
+
|
|
76
|
+
export function buildAbbreviatedSummary(council: Council): string {
|
|
77
|
+
const entries = getAllEntries(council.id);
|
|
78
|
+
if (entries.length === 0) return 'No deliberation entries.';
|
|
79
|
+
|
|
80
|
+
let summary = `=== Deliberation Summary: ${council.name} ===\n\n`;
|
|
81
|
+
|
|
82
|
+
// Consultant highlights (mechanical summary)
|
|
83
|
+
const mechanicalSummary = buildMechanicalSummary(entries);
|
|
84
|
+
if (mechanicalSummary) {
|
|
85
|
+
// Replace persona IDs with names in the mechanical summary
|
|
86
|
+
let namedSummary = mechanicalSummary;
|
|
87
|
+
for (const p of council.personas) {
|
|
88
|
+
namedSummary = namedSummary.replace(new RegExp(p.id, 'g'), p.name);
|
|
89
|
+
}
|
|
90
|
+
summary += `--- Consultant Highlights ---\n${namedSummary}\n\n`;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Decision
|
|
94
|
+
const decision = getDecision(council.id);
|
|
95
|
+
if (decision) {
|
|
96
|
+
summary += `--- Decision ---\n${decision.content}\n`;
|
|
97
|
+
if (decision.acceptanceCriteria) {
|
|
98
|
+
summary += `\nAcceptance Criteria: ${decision.acceptanceCriteria}\n`;
|
|
99
|
+
}
|
|
100
|
+
summary += '\n';
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Output (truncated)
|
|
104
|
+
const output = getLatestOutput(council.id);
|
|
105
|
+
if (output) {
|
|
106
|
+
const maxLen = 2000;
|
|
107
|
+
const truncated = output.content.length > maxLen
|
|
108
|
+
? output.content.slice(0, maxLen) + '\n\n[... truncated ...]'
|
|
109
|
+
: output.content;
|
|
110
|
+
summary += `--- Output (v${output.version}) ---\n${truncated}\n`;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return summary;
|
|
114
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"allowImportingTsExtensions": true,
|
|
7
|
+
"noEmit": true,
|
|
8
|
+
"strict": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"resolveJsonModule": true,
|
|
12
|
+
"isolatedModules": true,
|
|
13
|
+
"lib": ["ES2022"]
|
|
14
|
+
},
|
|
15
|
+
"include": ["."]
|
|
16
|
+
}
|