@mrace07/kairo 0.1.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.
Files changed (78) hide show
  1. package/README.md +141 -0
  2. package/dist/application/coding-agent.d.ts +85 -0
  3. package/dist/application/coding-agent.js +765 -0
  4. package/dist/application/context-manager.d.ts +22 -0
  5. package/dist/application/context-manager.js +174 -0
  6. package/dist/application/context-selector.d.ts +11 -0
  7. package/dist/application/context-selector.js +74 -0
  8. package/dist/application/evaluated-agent.d.ts +7 -0
  9. package/dist/application/evaluated-agent.js +16 -0
  10. package/dist/application/evaluation-comparison.d.ts +34 -0
  11. package/dist/application/evaluation-comparison.js +91 -0
  12. package/dist/application/evaluation-harness.d.ts +19 -0
  13. package/dist/application/evaluation-harness.js +217 -0
  14. package/dist/application/failure-analyzer.d.ts +5 -0
  15. package/dist/application/failure-analyzer.js +37 -0
  16. package/dist/application/interaction-routing.d.ts +9 -0
  17. package/dist/application/interaction-routing.js +20 -0
  18. package/dist/application/live-evaluation.d.ts +8 -0
  19. package/dist/application/live-evaluation.js +185 -0
  20. package/dist/application/model-routing.d.ts +12 -0
  21. package/dist/application/model-routing.js +40 -0
  22. package/dist/application/model-system-instruction.d.ts +4 -0
  23. package/dist/application/model-system-instruction.js +4 -0
  24. package/dist/application/self-evaluation.d.ts +26 -0
  25. package/dist/application/self-evaluation.js +394 -0
  26. package/dist/application/task-metrics.d.ts +31 -0
  27. package/dist/application/task-metrics.js +42 -0
  28. package/dist/application/verification-planner.d.ts +12 -0
  29. package/dist/application/verification-planner.js +97 -0
  30. package/dist/domain/models.d.ts +247 -0
  31. package/dist/domain/models.js +1 -0
  32. package/dist/domain/ports.d.ts +87 -0
  33. package/dist/domain/ports.js +1 -0
  34. package/dist/domain/provider-error.d.ts +18 -0
  35. package/dist/domain/provider-error.js +17 -0
  36. package/dist/infrastructure/configuration/config.d.ts +24 -0
  37. package/dist/infrastructure/configuration/config.js +79 -0
  38. package/dist/infrastructure/filesystem/platform-paths.d.ts +8 -0
  39. package/dist/infrastructure/filesystem/platform-paths.js +18 -0
  40. package/dist/infrastructure/persistence/sqlite-session-store.d.ts +82 -0
  41. package/dist/infrastructure/persistence/sqlite-session-store.js +447 -0
  42. package/dist/infrastructure/providers/gemini-provider.d.ts +14 -0
  43. package/dist/infrastructure/providers/gemini-provider.js +90 -0
  44. package/dist/infrastructure/providers/groq-provider.d.ts +16 -0
  45. package/dist/infrastructure/providers/groq-provider.js +101 -0
  46. package/dist/infrastructure/providers/jev-safety-advisor.d.ts +18 -0
  47. package/dist/infrastructure/providers/jev-safety-advisor.js +95 -0
  48. package/dist/infrastructure/providers/mistral-provider.d.ts +15 -0
  49. package/dist/infrastructure/providers/mistral-provider.js +137 -0
  50. package/dist/infrastructure/providers/openrouter-provider.d.ts +15 -0
  51. package/dist/infrastructure/providers/openrouter-provider.js +104 -0
  52. package/dist/infrastructure/providers/provider-recovery.d.ts +10 -0
  53. package/dist/infrastructure/providers/provider-recovery.js +108 -0
  54. package/dist/infrastructure/providers/provider-registry.d.ts +22 -0
  55. package/dist/infrastructure/providers/provider-registry.js +67 -0
  56. package/dist/infrastructure/repository/repository-awareness.d.ts +12 -0
  57. package/dist/infrastructure/repository/repository-awareness.js +25 -0
  58. package/dist/infrastructure/repository/repository-profiler.d.ts +35 -0
  59. package/dist/infrastructure/repository/repository-profiler.js +498 -0
  60. package/dist/infrastructure/security/macos-keychain-store.d.ts +17 -0
  61. package/dist/infrastructure/security/macos-keychain-store.js +73 -0
  62. package/dist/infrastructure/tools/workspace-tools.d.ts +30 -0
  63. package/dist/infrastructure/tools/workspace-tools.js +321 -0
  64. package/dist/interface/cli/evaluation-comparison-report.d.ts +6 -0
  65. package/dist/interface/cli/evaluation-comparison-report.js +46 -0
  66. package/dist/interface/cli/evaluation-report.d.ts +14 -0
  67. package/dist/interface/cli/evaluation-report.js +122 -0
  68. package/dist/interface/cli/index.d.ts +2 -0
  69. package/dist/interface/cli/index.js +238 -0
  70. package/dist/interface/cli/provider-setup.d.ts +16 -0
  71. package/dist/interface/cli/provider-setup.js +86 -0
  72. package/dist/interface/cli/repl.d.ts +7 -0
  73. package/dist/interface/cli/repl.js +19 -0
  74. package/dist/interface/cli/task-trace.d.ts +7 -0
  75. package/dist/interface/cli/task-trace.js +48 -0
  76. package/dist/interface/cli/tui.d.ts +147 -0
  77. package/dist/interface/cli/tui.js +910 -0
  78. package/package.json +61 -0
@@ -0,0 +1,5 @@
1
+ import type { FailureEvidence } from "../domain/models.js";
2
+ export declare class FailureAnalyzer {
3
+ /** Extracts bounded paths and useful error lines from a failed verification command. */
4
+ analyze(command: string, output: string): FailureEvidence;
5
+ }
@@ -0,0 +1,37 @@
1
+ const MAX_EXCERPTS = 8;
2
+ const MAX_OUTPUT = 8_000;
3
+ export class FailureAnalyzer {
4
+ /** Extracts bounded paths and useful error lines from a failed verification command. */
5
+ analyze(command, output) {
6
+ // Keep persisted repair context small even when a test runner emits a large stack trace.
7
+ const lines = output
8
+ .slice(0, MAX_OUTPUT)
9
+ .split("\n")
10
+ .map((line) => line.trim());
11
+ const fileLocations = new Map();
12
+ for (const line of lines) {
13
+ // Covers the common JavaScript/TypeScript `path:line[:column]` stack-trace form.
14
+ for (const match of line.matchAll(/([\w@./-]+\.[cm]?[jt]sx?):(\d+)(?::(\d+))?/g)) {
15
+ const path = match[1];
16
+ fileLocations.set(`${path}:${match[2]}:${match[3] ?? ""}`, {
17
+ path,
18
+ line: Number(match[2]),
19
+ column: match[3] ? Number(match[3]) : undefined,
20
+ });
21
+ }
22
+ // Test runners often name the failing file without a source location.
23
+ const testFile = /(?:FAIL|✖|×)\s+([\w@./-]+\.[cm]?[jt]sx?)/.exec(line)?.[1];
24
+ if (testFile)
25
+ fileLocations.set(testFile, { path: testFile });
26
+ }
27
+ const excerpts = lines
28
+ .filter((line) => /(?:error|fail|expect|assert|exception|✖|×)/i.test(line))
29
+ .filter((line, index, all) => Boolean(line) && all.indexOf(line) === index)
30
+ .slice(0, MAX_EXCERPTS);
31
+ return {
32
+ summary: excerpts[0] || `Verification command failed: ${command}`,
33
+ fileLocations: [...fileLocations.values()].slice(0, MAX_EXCERPTS),
34
+ excerpts,
35
+ };
36
+ }
37
+ }
@@ -0,0 +1,9 @@
1
+ /** The bounded interaction classes used before Kairo creates a coding task. */
2
+ export type InteractionIntent = "conversation" | "answer" | "repository_task";
3
+ export type AutoInteractionMode = "plan" | "build";
4
+ /** Keeps obvious social messages local so they spend neither Jev nor coding-model capacity. */
5
+ export declare function greetingResponse(input: string): string | undefined;
6
+ /** Builds minimal, credential-free context for Jev's pre-loop intent decision. */
7
+ export declare function interactionIntentState(input: string): string;
8
+ /** AUTO keeps conversational inputs read-only and enables BUILD only for repository work. */
9
+ export declare function autoInteractionMode(intent: InteractionIntent): AutoInteractionMode;
@@ -0,0 +1,20 @@
1
+ const simpleGreeting = /^(?:hi|hello|hey|yo|good\s+(?:morning|afternoon|evening)|thanks|thank\s+you)[!.\s]*$/i;
2
+ /** Keeps obvious social messages local so they spend neither Jev nor coding-model capacity. */
3
+ export function greetingResponse(input) {
4
+ if (!simpleGreeting.test(input.trim()))
5
+ return undefined;
6
+ if (/thank/i.test(input))
7
+ return "You're welcome. What would you like to work on?";
8
+ return "Hello! What would you like to inspect, plan, change, or verify?";
9
+ }
10
+ /** Builds minimal, credential-free context for Jev's pre-loop intent decision. */
11
+ export function interactionIntentState(input) {
12
+ return [
13
+ `User message: ${input.replace(/(?:sk|gsk|or|AIza)[-_a-zA-Z0-9]{12,}/g, "[redacted]").slice(0, 1_500)}`,
14
+ "Classify the interaction before any repository context or tools are exposed.",
15
+ ].join("\n");
16
+ }
17
+ /** AUTO keeps conversational inputs read-only and enables BUILD only for repository work. */
18
+ export function autoInteractionMode(intent) {
19
+ return intent === "repository_task" ? "build" : "plan";
20
+ }
@@ -0,0 +1,8 @@
1
+ import type { LiveEvaluationResult } from "../domain/models.js";
2
+ export type LiveEvaluationOptions = {
3
+ onProgress?: (text: string) => void;
4
+ apiKey: string;
5
+ model: string;
6
+ };
7
+ /** Evaluates live Gemini runs in disposable fixtures; DeepEval scores behavior, not the fixture assertion. */
8
+ export declare function runLiveEvaluationSuite(options: LiveEvaluationOptions): Promise<LiveEvaluationResult[]>;
@@ -0,0 +1,185 @@
1
+ import { cp, mkdtemp, rm } from "node:fs/promises";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { EvaluationDataset, Golden } from "deepeval/dataset";
5
+ import { TaskCompletionMetric } from "deepeval/metrics";
6
+ import { GeminiModel } from "deepeval/models";
7
+ import { SpanType, observe, updateCurrentSpan } from "deepeval/tracing";
8
+ import { SqliteSessionStore } from "../infrastructure/persistence/sqlite-session-store.js";
9
+ import { GeminiProvider } from "../infrastructure/providers/gemini-provider.js";
10
+ import { RepositoryProfiler } from "../infrastructure/repository/repository-profiler.js";
11
+ import { WorkspaceTools, definitions } from "../infrastructure/tools/workspace-tools.js";
12
+ import { CodingAgent } from "./coding-agent.js";
13
+ import { evaluationScenarios } from "./evaluation-harness.js";
14
+ import { taskMetrics } from "./task-metrics.js";
15
+ import { runEvaluatedAgent } from "./evaluated-agent.js";
16
+ const fixtures = join(process.cwd(), "evals", "fixtures");
17
+ /** Evaluates live Gemini runs in disposable fixtures; DeepEval scores behavior, not the fixture assertion. */
18
+ export async function runLiveEvaluationSuite(options) {
19
+ const dataset = new EvaluationDataset({
20
+ goldens: evaluationScenarios.map((scenario) => new Golden({
21
+ name: scenario.id,
22
+ input: scenario.prompt,
23
+ expectedOutput: expectedOutcome(scenario),
24
+ })),
25
+ });
26
+ const completion = new TaskCompletionMetric({
27
+ model: new GeminiModel({ apiKey: options.apiKey, model: options.model }),
28
+ threshold: 0.7,
29
+ includeReason: true,
30
+ });
31
+ const results = [];
32
+ let scenarioIndex = 0;
33
+ for await (const golden of dataset.evalsIterator({
34
+ metrics: [completion],
35
+ errorConfig: { ignoreErrors: true },
36
+ displayConfig: { printResults: false, showIndicator: false },
37
+ identifier: "kairo-live",
38
+ })) {
39
+ const scenario = evaluationScenarios[scenarioIndex++];
40
+ if (!(golden instanceof Golden))
41
+ throw new Error("Live coding evaluations require single-turn goldens.");
42
+ const deterministic = await runObservedScenario(scenario, golden.input, options);
43
+ results.push(deterministic);
44
+ }
45
+ return results.map((result, index) => {
46
+ const metric = dataset.evalResults[index]?.metricsData?.find((item) => item.name === "Task Completion");
47
+ const judge = {
48
+ passed: metric?.success === true,
49
+ score: metric?.score,
50
+ reason: metric?.reason,
51
+ error: metric?.error,
52
+ };
53
+ return { ...result, passed: result.passed && judge.passed, judge };
54
+ });
55
+ }
56
+ /** Runs one actual agent task under a DeepEval agent span, keeping raw workspace data out of the trace. */
57
+ async function runObservedScenario(scenario, prompt, options) {
58
+ const root = await mkdtemp(join(tmpdir(), `kairo-live-eval-${scenario.id}-`));
59
+ try {
60
+ await cp(join(fixtures, scenario.id), root, { recursive: true });
61
+ const store = await SqliteSessionStore.open(":memory:");
62
+ try {
63
+ const session = store.create(root);
64
+ const tools = await WorkspaceTools.create(root);
65
+ store.saveRepositorySnapshot(session.id, await new RepositoryProfiler().profile(root));
66
+ const agent = new CodingAgent(new GeminiProvider(options.apiKey, options.model, definitions), store, tools, new FixtureApproval(), definitions, { provider: "gemini", model: options.model });
67
+ let failure = {};
68
+ const runAgent = observe({
69
+ type: SpanType.AGENT,
70
+ name: "kairo-coding-agent",
71
+ availableTools: definitions.map((definition) => definition.name),
72
+ fn: async (input) => {
73
+ failure = await runEvaluatedAgent(agent, session.id, input, options.onProgress);
74
+ const task = agent.status(session.id);
75
+ const output = finalResponse(store.messages(session.id), task.error);
76
+ const events = store.taskEvents(task.id);
77
+ updateCurrentSpan({
78
+ input,
79
+ output,
80
+ expectedOutput: expectedOutcome(scenario),
81
+ toolsCalled: toolCalls(events),
82
+ metadata: {
83
+ taskStatus: task.status,
84
+ changedFileCount: task.changedFiles.length,
85
+ verificationPassed: task.verificationPassed === true,
86
+ repairAttempts: store.repairAttempts(task.id).length,
87
+ },
88
+ });
89
+ return output;
90
+ },
91
+ });
92
+ await runAgent(prompt);
93
+ const task = agent.status(session.id);
94
+ const expectationPassed = failure.error ? false : await scenario.expect(root);
95
+ const metrics = taskMetrics(store.taskEvents(task.id));
96
+ const verified = task.verificationPassed === true;
97
+ return {
98
+ id: scenario.id,
99
+ passed: task.status === "completed" && verified && expectationPassed,
100
+ taskStatus: task.status,
101
+ verified,
102
+ expectationPassed,
103
+ error: failure.error ?? task.error,
104
+ failureCategory: failure.category,
105
+ judge: { passed: false },
106
+ metrics: {
107
+ providerRetries: metrics.providerRetries,
108
+ providerWaitMs: metrics.providerWaitMs,
109
+ modelTurns: metrics.modelTurns,
110
+ toolExecutions: metrics.toolExecutions,
111
+ toolFailures: metrics.toolFailures,
112
+ approvals: metrics.approvals,
113
+ repairs: metrics.repairs,
114
+ verificationPasses: metrics.verificationPasses,
115
+ verificationFailures: metrics.verificationFailures,
116
+ verificationSelections: metrics.verificationSelections,
117
+ focusedVerifications: metrics.focusedVerifications,
118
+ broadVerifications: metrics.broadVerifications,
119
+ repairConverged: metrics.repairConverged,
120
+ modelMs: metrics.modelMs,
121
+ toolMs: metrics.toolMs,
122
+ },
123
+ };
124
+ }
125
+ finally {
126
+ store.close();
127
+ }
128
+ }
129
+ catch (error) {
130
+ return {
131
+ id: scenario.id,
132
+ passed: false,
133
+ taskStatus: "failed",
134
+ verified: false,
135
+ expectationPassed: false,
136
+ error: error.message,
137
+ judge: { passed: false },
138
+ metrics: emptyMetrics(),
139
+ };
140
+ }
141
+ finally {
142
+ await rm(root, { recursive: true, force: true });
143
+ }
144
+ }
145
+ /** Eval fixture mutations are intentionally pre-approved, unlike an interactive user workspace. */
146
+ class FixtureApproval {
147
+ async approve(_call, _description) {
148
+ return true;
149
+ }
150
+ }
151
+ /** The final natural-language model message is useful judge context without exposing tool output. */
152
+ function finalResponse(messages, error) {
153
+ return ([...messages].reverse().find((message) => message.role === "model" && !message.toolCallId)
154
+ ?.content ??
155
+ error ??
156
+ "Agent finished without a final response.");
157
+ }
158
+ /** Tool names retain the agent trajectory shape while withholding paths, arguments, and command output. */
159
+ function toolCalls(events) {
160
+ return events
161
+ .filter((event) => event.kind === "tool_requested" && event.name)
162
+ .map((event) => ({ name: event.name }));
163
+ }
164
+ function expectedOutcome(scenario) {
165
+ return scenario.id === "create-file"
166
+ ? "Create result.txt containing done and run the fixture test successfully."
167
+ : "Repair message.txt so the fixture test passes after the initial failure.";
168
+ }
169
+ function emptyMetrics() {
170
+ return {
171
+ modelTurns: 0,
172
+ toolExecutions: 0,
173
+ toolFailures: 0,
174
+ approvals: 0,
175
+ repairs: 0,
176
+ verificationPasses: 0,
177
+ verificationFailures: 0,
178
+ verificationSelections: 0,
179
+ focusedVerifications: 0,
180
+ broadVerifications: 0,
181
+ repairConverged: false,
182
+ modelMs: 0,
183
+ toolMs: 0,
184
+ };
185
+ }
@@ -0,0 +1,12 @@
1
+ import type { ModelSelection } from "../domain/models.js";
2
+ import type { JevModelTier } from "../domain/ports.js";
3
+ export type AvailableModel = ModelSelection & {
4
+ apiKey: string;
5
+ tier: JevModelTier;
6
+ };
7
+ /** Selects only from locally credentialed models; the manual selection is the deterministic fallback. */
8
+ export declare function selectAutoModel(available: AvailableModel[], manual: ModelSelection, tier: JevModelTier): AvailableModel | undefined;
9
+ /** Orders replacement models after quota exhaustion, preferring a different provider first. */
10
+ export declare function quotaFallbackModels(available: AvailableModel[], selected: ModelSelection, manual: ModelSelection, tier: JevModelTier): AvailableModel[];
11
+ /** Builds bounded, credential-free metadata for the TypeSafe decision request. */
12
+ export declare function modelRoutingState(request: string, candidates: AvailableModel[]): string;
@@ -0,0 +1,40 @@
1
+ /** Selects only from locally credentialed models; the manual selection is the deterministic fallback. */
2
+ export function selectAutoModel(available, manual, tier) {
3
+ const preferred = available.filter((candidate) => candidate.tier === tier);
4
+ return (preferred.find((candidate) => candidate.provider === manual.provider && candidate.model === manual.model) ??
5
+ preferred[0] ??
6
+ available.find((candidate) => candidate.provider === manual.provider && candidate.model === manual.model) ??
7
+ available[0]);
8
+ }
9
+ /** Orders replacement models after quota exhaustion, preferring a different provider first. */
10
+ export function quotaFallbackModels(available, selected, manual, tier) {
11
+ const remaining = available.filter((candidate) => candidate.provider !== selected.provider || candidate.model !== selected.model);
12
+ const rank = (candidate) => {
13
+ const sameProvider = candidate.provider === selected.provider;
14
+ const sameTier = candidate.tier === tier;
15
+ const manualModel = candidate.provider === manual.provider && candidate.model === manual.model;
16
+ if (!sameProvider && sameTier)
17
+ return 0;
18
+ if (!sameProvider && manualModel)
19
+ return 1;
20
+ if (!sameProvider)
21
+ return 2;
22
+ if (sameTier)
23
+ return 3;
24
+ if (manualModel)
25
+ return 4;
26
+ return 5;
27
+ };
28
+ return remaining.sort((left, right) => rank(left) - rank(right));
29
+ }
30
+ /** Builds bounded, credential-free metadata for the TypeSafe decision request. */
31
+ export function modelRoutingState(request, candidates) {
32
+ return [
33
+ `Task request: ${request.replace(/(?:sk|gsk|or|AIza)[-_a-zA-Z0-9]{12,}/g, "[redacted]").slice(0, 1_500)}`,
34
+ `Available model tiers: ${candidates
35
+ .map((candidate) => `${candidate.provider}/${candidate.model}=${candidate.tier}`)
36
+ .join(", ")
37
+ .slice(0, 1_500)}`,
38
+ "Choose the coding-model tier only. Credentials, source files, and tool output are not included.",
39
+ ].join("\n");
40
+ }
@@ -0,0 +1,4 @@
1
+ /** Shared behavior contract applied consistently by every model provider. */
2
+ export declare const modelSystemInstruction = "You are Kairo, a careful coding agent. Work only through the provided tools. Inspect relevant files before changing code. After any edit, run an appropriate verification command before declaring success. When a tool fails, inspect its error and try a materially different repair; do not repeat the same call. Keep tool use focused because outputs may be truncated and execution is bounded. Explain the completed work, verification evidence, and remaining limitations concisely.";
3
+ /** Keeps non-repository answers conversational and prevents accidental tool-oriented behavior. */
4
+ export declare const conversationSystemInstruction = "You are Kairo. Answer the user's general question directly and concisely. Do not claim to inspect, test, change, or know anything about a repository. Tools are unavailable for this response; ask the user to explicitly request repository work if they need it.";
@@ -0,0 +1,4 @@
1
+ /** Shared behavior contract applied consistently by every model provider. */
2
+ export const modelSystemInstruction = "You are Kairo, a careful coding agent. Work only through the provided tools. Inspect relevant files before changing code. After any edit, run an appropriate verification command before declaring success. When a tool fails, inspect its error and try a materially different repair; do not repeat the same call. Keep tool use focused because outputs may be truncated and execution is bounded. Explain the completed work, verification evidence, and remaining limitations concisely.";
3
+ /** Keeps non-repository answers conversational and prevents accidental tool-oriented behavior. */
4
+ export const conversationSystemInstruction = "You are Kairo. Answer the user's general question directly and concisely. Do not claim to inspect, test, change, or know anything about a repository. Tools are unavailable for this response; ask the user to explicitly request repository work if they need it.";
@@ -0,0 +1,26 @@
1
+ import type { EvaluationRun, SelfEvaluationResult, ProviderId } from "../domain/models.js";
2
+ import type { EvaluationStore } from "../domain/ports.js";
3
+ export type SelfEvaluationScenario = {
4
+ id: string;
5
+ prompt: string;
6
+ seed(workspace: string): Promise<void>;
7
+ /** Hidden behavioral check, separate from the agent's own verification command. */
8
+ verify(workspace: string): Promise<void>;
9
+ };
10
+ export type SelfEvaluationOptions = {
11
+ apiKey: string;
12
+ provider: ProviderId;
13
+ model: string;
14
+ evaluationStore: EvaluationStore;
15
+ trials?: number;
16
+ sourceRoot?: string;
17
+ onProgress?: (text: string) => void;
18
+ };
19
+ export type SelfEvaluationRun = {
20
+ run: EvaluationRun;
21
+ results: SelfEvaluationResult[];
22
+ };
23
+ /** Seven real Kairo safeguards, deliberately removed from isolated source copies. */
24
+ export declare const selfEvaluationScenarios: SelfEvaluationScenario[];
25
+ /** Runs real provider-backed tasks against clean Git-free snapshots and persists metadata-only evidence. */
26
+ export declare function runSelfEvaluationSuite(options: SelfEvaluationOptions): Promise<SelfEvaluationRun>;