@kal-elsam/kairo-runtime 0.16.0 → 0.17.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 (81) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/package.json +2 -1
  3. package/scripts/cockpit-smoke.mjs +1 -1
  4. package/scripts/ux-smoke-test.sh +3 -3
  5. package/src/cli.js +96 -11
  6. package/src/global/agent-capabilities/create-capability-adapter.js +2 -2
  7. package/src/global/architect/architect-cli.js +76 -0
  8. package/src/global/architect/architect-codex.js +146 -0
  9. package/src/global/architect/architect-manager.js +125 -0
  10. package/src/global/architect/architect-store.js +377 -0
  11. package/src/global/architect/architect-types.js +47 -0
  12. package/src/global/cli-help.js +10 -1
  13. package/src/global/cockpit/app.js +475 -0
  14. package/src/global/cockpit/card.js +111 -0
  15. package/src/global/cockpit/cli.js +33 -0
  16. package/src/global/cockpit/gauge.js +31 -0
  17. package/src/global/cockpit/project-overlay.js +683 -0
  18. package/src/global/cockpit/rows.js +148 -0
  19. package/src/global/cockpit/theme.js +118 -0
  20. package/src/global/cockpit/view.js +1263 -0
  21. package/src/global/conversation/bootstrap-analyzer-adapters.js +251 -0
  22. package/src/global/conversation/cli.js +53 -0
  23. package/src/global/conversation/codex-sandbox.js +230 -0
  24. package/src/global/conversation/cursor-sandbox.js +215 -0
  25. package/src/global/conversation/project-analysis.js +204 -0
  26. package/src/global/conversation/project-profile.js +178 -0
  27. package/src/global/conversation/project-router.js +149 -0
  28. package/src/global/conversation/project-strategy-store.js +64 -0
  29. package/src/global/conversation/project-strategy.js +514 -0
  30. package/src/global/conversation/sanitized-snapshot.js +169 -0
  31. package/src/global/conversation/secret-scanner.js +71 -0
  32. package/src/global/conversation/service.js +1063 -0
  33. package/src/global/conversation/session-store.js +75 -0
  34. package/src/global/conversation/transcript-store.js +79 -0
  35. package/src/global/conversation/ui.js +195 -0
  36. package/src/global/intelligence/capability-scoring.js +480 -0
  37. package/src/global/intelligence/execution-router.js +444 -0
  38. package/src/global/intelligence/kairo-telemetry-source.js +59 -0
  39. package/src/global/intelligence/kairobench-runner.js +85 -0
  40. package/src/global/intelligence/kairobench-source.js +34 -0
  41. package/src/global/intelligence/kairobench-tasks.js +47 -0
  42. package/src/global/intelligence/model-candidate-catalog.js +456 -0
  43. package/src/global/intelligence/model-capability-registry-sources.js +145 -0
  44. package/src/global/intelligence/model-capability-registry.js +125 -0
  45. package/src/global/intelligence/model-intelligence.js +1646 -0
  46. package/src/global/intelligence/official-benchmark-snapshots.js +162 -0
  47. package/src/global/intelligence/quick-ask.js +149 -0
  48. package/src/global/intelligence/role-profiles.js +251 -0
  49. package/src/global/intelligence/skill-catalog.js +67 -0
  50. package/src/global/intelligence/subscription-pressure-source.js +41 -0
  51. package/src/global/mcp/kairo-mcp.js +51 -18
  52. package/src/global/mcp/work-snapshot-rule.js +4 -2
  53. package/src/global/mcp/workspace-binding.js +88 -0
  54. package/src/global/mcp/workspace-mcp-entry.js +74 -0
  55. package/src/global/mcp-install.js +8 -1
  56. package/src/global/observability/artificial-analysis-models.js +118 -0
  57. package/src/global/observability/claude-models.js +31 -0
  58. package/src/global/observability/claude-usage.js +112 -0
  59. package/src/global/observability/codex-models.js +96 -0
  60. package/src/global/observability/codex-usage.js +160 -0
  61. package/src/global/observability/cursor-auth.js +88 -0
  62. package/src/global/observability/cursor-models.js +101 -0
  63. package/src/global/observability/huggingface-leaderboard.js +97 -0
  64. package/src/global/observability/opencode-models.js +101 -0
  65. package/src/global/observability/opencode-usage.js +162 -0
  66. package/src/global/paths.js +49 -2
  67. package/src/global/profile.js +23 -1
  68. package/src/global/runtime/execution-adapters/claude.js +63 -30
  69. package/src/global/runtime/execution-adapters/codex.js +9 -2
  70. package/src/global/runtime/execution-adapters/create-execution-adapter.js +6 -1
  71. package/src/global/runtime/execution-adapters/opencode.js +83 -18
  72. package/src/global/runtime/execution-worktree-manager.js +924 -0
  73. package/src/global/runtime/execution-worktree-orchestrator.js +194 -0
  74. package/src/global/runtime/execution-worktree-store.js +83 -0
  75. package/src/global/runtime/execution-worktree-types.js +45 -0
  76. package/src/global/runtime/run-events.js +38 -0
  77. package/src/global/runtime/run-manager.js +22 -6
  78. package/src/global/runtime/run-supervisor.js +41 -12
  79. package/src/global/runtime/usage-manager.js +96 -0
  80. package/src/global/runtime/usage-store.js +69 -0
  81. package/src/global/runtime/usage-types.js +62 -0
@@ -0,0 +1,69 @@
1
+ import { existsSync } from "node:fs";
2
+ import { mkdir, readdir, readFile } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { harnessHomePaths } from "../paths.js";
5
+ import { EXECUTION_ADAPTER_IDS } from "./execution-adapters/index.js";
6
+ import { writeAtomicJson } from "./write-atomic-json.js";
7
+
8
+ const writeLocks = new Map();
9
+
10
+ export function getUsageDir(homeDir) {
11
+ return harnessHomePaths(homeDir).usageDir;
12
+ }
13
+
14
+ /**
15
+ * The one real boundary a provider id gets validated at before it's ever
16
+ * used to build a path — the same defense-in-depth role
17
+ * assertWorktreeId/assertTaskId already play elsewhere. Provider ids are a
18
+ * real, closed list (the same one execution-adapters/index.js resolves
19
+ * against), never a free-form string, so an unknown one is always a bug
20
+ * to surface loudly rather than a path to silently sanitize.
21
+ */
22
+ function usagePath(homeDir, provider) {
23
+ if (!EXECUTION_ADAPTER_IDS.includes(provider)) {
24
+ throw new Error(`Unknown provider "${provider}" for usage tracking. Use ${EXECUTION_ADAPTER_IDS.join(", ")}.`);
25
+ }
26
+ return join(getUsageDir(homeDir), `${provider}.json`);
27
+ }
28
+
29
+ export async function readProviderUsage(homeDir, provider) {
30
+ const statePath = usagePath(homeDir, provider);
31
+ if (!existsSync(statePath)) return null;
32
+
33
+ try {
34
+ return JSON.parse(await readFile(statePath, "utf8"));
35
+ } catch (error) {
36
+ throw new Error(`Invalid provider usage state at ${statePath}: ${error.message}`);
37
+ }
38
+ }
39
+
40
+ /** Read-modify-write is serialized per provider — same real race protection run-store.js's writeRunState already relies on. */
41
+ export async function writeProviderUsage(homeDir, provider, record) {
42
+ const key = provider;
43
+ const previous = writeLocks.get(key) ?? Promise.resolve();
44
+ const next = previous.then(async () => {
45
+ const usageDir = getUsageDir(homeDir);
46
+ await mkdir(usageDir, { recursive: true });
47
+ await writeAtomicJson(usagePath(homeDir, provider), record);
48
+ return record;
49
+ });
50
+ writeLocks.set(key, next.catch(() => {}));
51
+ return next;
52
+ }
53
+
54
+ export async function listProviderUsage(homeDir) {
55
+ const usageDir = getUsageDir(homeDir);
56
+ if (!existsSync(usageDir)) return [];
57
+
58
+ const entries = await readdir(usageDir, { withFileTypes: true });
59
+ const records = [];
60
+ for (const entry of entries) {
61
+ if (!entry.isFile() || !entry.name.endsWith(".json")) continue;
62
+ const provider = entry.name.slice(0, -".json".length);
63
+ if (!EXECUTION_ADAPTER_IDS.includes(provider)) continue;
64
+ const record = await readProviderUsage(homeDir, provider);
65
+ if (record) records.push(record);
66
+ }
67
+ records.sort((left, right) => left.provider.localeCompare(right.provider));
68
+ return records;
69
+ }
@@ -0,0 +1,62 @@
1
+ export const USAGE_SCHEMA = "kairo.provider-usage/v1";
2
+
3
+ // HEALTHY: well under budget. MODERATE/CONSERVE: real, rising signal — no
4
+ // enforcement yet. CRITICAL: the last tier before the gate. EXHAUSTED: the
5
+ // real, enforced stop — assertProviderNotExhausted (usage-manager.js)
6
+ // refuses to start a new run for a provider in this tier.
7
+ export const USAGE_TIERS = Object.freeze({
8
+ HEALTHY: "healthy",
9
+ MODERATE: "moderate",
10
+ CONSERVE: "conserve",
11
+ CRITICAL: "critical",
12
+ EXHAUSTED: "exhausted"
13
+ });
14
+
15
+ // Fractions of the configured budget. Ratios strictly below MODERATE stay
16
+ // HEALTHY; at/above 1.0 (100% of budget) is always EXHAUSTED, regardless
17
+ // of where CRITICAL is set.
18
+ export const USAGE_TIER_THRESHOLDS = Object.freeze({
19
+ MODERATE: 0.5,
20
+ CONSERVE: 0.75,
21
+ CRITICAL: 0.9
22
+ });
23
+
24
+ /**
25
+ * Classifies real, cumulative consumption against a real, configured
26
+ * budget — never against a single run's own usage. No budget configured
27
+ * (null) means no gate exists for this provider yet: always HEALTHY,
28
+ * exactly mirroring how a null profile.tokenBudget already means "no
29
+ * per-request gate" elsewhere in this codebase (see router.js).
30
+ */
31
+ export function classifyUsageTier(consumedTokens, budgetTokens) {
32
+ if (budgetTokens == null || !Number.isFinite(budgetTokens) || budgetTokens <= 0) {
33
+ return USAGE_TIERS.HEALTHY;
34
+ }
35
+ const consumed = Number.isFinite(consumedTokens) ? Math.max(0, consumedTokens) : 0;
36
+ const ratio = consumed / budgetTokens;
37
+
38
+ if (ratio >= 1) return USAGE_TIERS.EXHAUSTED;
39
+ if (ratio >= USAGE_TIER_THRESHOLDS.CRITICAL) return USAGE_TIERS.CRITICAL;
40
+ if (ratio >= USAGE_TIER_THRESHOLDS.CONSERVE) return USAGE_TIERS.CONSERVE;
41
+ if (ratio >= USAGE_TIER_THRESHOLDS.MODERATE) return USAGE_TIERS.MODERATE;
42
+ return USAGE_TIERS.HEALTHY;
43
+ }
44
+
45
+ export function isProviderExhausted(tier) {
46
+ return tier === USAGE_TIERS.EXHAUSTED;
47
+ }
48
+
49
+ export function createProviderUsageRecord(provider) {
50
+ const now = new Date().toISOString();
51
+ return {
52
+ schema: USAGE_SCHEMA,
53
+ provider,
54
+ totalInputTokens: 0,
55
+ totalOutputTokens: 0,
56
+ totalTokens: 0,
57
+ totalCost: 0,
58
+ runCount: 0,
59
+ createdAt: now,
60
+ updatedAt: now
61
+ };
62
+ }