@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.
Files changed (108) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +556 -0
  3. package/bin/kondi-chat +56 -0
  4. package/bin/kondi-chat.js +72 -0
  5. package/package.json +55 -0
  6. package/scripts/demo.tape +49 -0
  7. package/scripts/postinstall.cjs +103 -0
  8. package/src/audit/analytics.ts +261 -0
  9. package/src/audit/ledger.ts +253 -0
  10. package/src/audit/telemetry.ts +165 -0
  11. package/src/cli/backend.ts +675 -0
  12. package/src/cli/commands.ts +419 -0
  13. package/src/cli/help.ts +182 -0
  14. package/src/cli/submit-helpers.ts +159 -0
  15. package/src/cli/submit.ts +539 -0
  16. package/src/cli/wizard.ts +121 -0
  17. package/src/context/bootstrap.ts +138 -0
  18. package/src/context/budget.ts +100 -0
  19. package/src/context/manager.ts +666 -0
  20. package/src/context/memory.ts +160 -0
  21. package/src/context/preflight.ts +176 -0
  22. package/src/context/project-brain.ts +101 -0
  23. package/src/context/receipts.ts +108 -0
  24. package/src/context/skills.ts +154 -0
  25. package/src/context/symbol-index.ts +240 -0
  26. package/src/council/profiles.ts +137 -0
  27. package/src/council/tool.ts +138 -0
  28. package/src/council-engine/cli/council-artifacts.ts +230 -0
  29. package/src/council-engine/cli/council-config.ts +178 -0
  30. package/src/council-engine/cli/council-session-export.ts +116 -0
  31. package/src/council-engine/cli/kondi.ts +98 -0
  32. package/src/council-engine/cli/llm-caller.ts +229 -0
  33. package/src/council-engine/cli/localStorage-shim.ts +119 -0
  34. package/src/council-engine/cli/node-platform.ts +68 -0
  35. package/src/council-engine/cli/run-council.ts +481 -0
  36. package/src/council-engine/cli/run-pipeline.ts +772 -0
  37. package/src/council-engine/cli/session-export.ts +153 -0
  38. package/src/council-engine/configs/councils/analysis.json +101 -0
  39. package/src/council-engine/configs/councils/code-planning.json +86 -0
  40. package/src/council-engine/configs/councils/coding.json +89 -0
  41. package/src/council-engine/configs/councils/debate.json +97 -0
  42. package/src/council-engine/configs/councils/solo-claude.json +34 -0
  43. package/src/council-engine/configs/councils/solo-gpt.json +34 -0
  44. package/src/council-engine/council/coding-orchestrator.ts +1205 -0
  45. package/src/council-engine/council/context-bootstrap.ts +147 -0
  46. package/src/council-engine/council/context-inspection.ts +42 -0
  47. package/src/council-engine/council/context-store.ts +763 -0
  48. package/src/council-engine/council/deliberation-orchestrator.ts +2762 -0
  49. package/src/council-engine/council/factory.ts +164 -0
  50. package/src/council-engine/council/index.ts +201 -0
  51. package/src/council-engine/council/ledger-store.ts +438 -0
  52. package/src/council-engine/council/prompts.ts +1689 -0
  53. package/src/council-engine/council/storage-cleanup.ts +164 -0
  54. package/src/council-engine/council/store.ts +1110 -0
  55. package/src/council-engine/council/synthesis.ts +291 -0
  56. package/src/council-engine/council/types.ts +845 -0
  57. package/src/council-engine/council/validation.ts +613 -0
  58. package/src/council-engine/pipeline/build-detect.ts +73 -0
  59. package/src/council-engine/pipeline/executor.ts +1048 -0
  60. package/src/council-engine/pipeline/index.ts +9 -0
  61. package/src/council-engine/pipeline/install-detect.ts +84 -0
  62. package/src/council-engine/pipeline/memory-store.ts +182 -0
  63. package/src/council-engine/pipeline/output-parsers.ts +146 -0
  64. package/src/council-engine/pipeline/run-output.ts +149 -0
  65. package/src/council-engine/pipeline/session-import.ts +177 -0
  66. package/src/council-engine/pipeline/store.ts +753 -0
  67. package/src/council-engine/pipeline/test-detect.ts +82 -0
  68. package/src/council-engine/pipeline/types.ts +401 -0
  69. package/src/council-engine/services/deliberationSummary.ts +114 -0
  70. package/src/council-engine/tsconfig.json +16 -0
  71. package/src/council-engine/types/mcp.ts +122 -0
  72. package/src/council-engine/utils/filterTools.ts +73 -0
  73. package/src/engine/apply.ts +238 -0
  74. package/src/engine/checkpoints.ts +237 -0
  75. package/src/engine/consultants.ts +347 -0
  76. package/src/engine/diff.ts +171 -0
  77. package/src/engine/errors.ts +102 -0
  78. package/src/engine/git-tools.ts +246 -0
  79. package/src/engine/hooks.ts +181 -0
  80. package/src/engine/loop-guard.ts +155 -0
  81. package/src/engine/permissions.ts +293 -0
  82. package/src/engine/pipeline.ts +376 -0
  83. package/src/engine/sub-agents.ts +133 -0
  84. package/src/engine/task-card.ts +185 -0
  85. package/src/engine/task-router.ts +256 -0
  86. package/src/engine/task-store.ts +86 -0
  87. package/src/engine/tools.ts +783 -0
  88. package/src/engine/verify.ts +111 -0
  89. package/src/mcp/client.ts +225 -0
  90. package/src/mcp/config.ts +120 -0
  91. package/src/mcp/tool-manager.ts +192 -0
  92. package/src/mcp/types.ts +61 -0
  93. package/src/providers/llm-caller.ts +943 -0
  94. package/src/providers/rate-limiter.ts +238 -0
  95. package/src/router/NOTES.md +28 -0
  96. package/src/router/collector.ts +474 -0
  97. package/src/router/embeddings.ts +286 -0
  98. package/src/router/index.ts +299 -0
  99. package/src/router/intent-router.ts +225 -0
  100. package/src/router/nn-router.ts +205 -0
  101. package/src/router/profiles.ts +309 -0
  102. package/src/router/registry.ts +565 -0
  103. package/src/router/rules.ts +274 -0
  104. package/src/router/train.py +408 -0
  105. package/src/session/store.ts +211 -0
  106. package/src/test-utils/mock-llm.ts +39 -0
  107. package/src/types.ts +322 -0
  108. package/src/web/manager.ts +311 -0
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Pure helpers used by the agent submit path.
3
+ *
4
+ * Everything in this file is deliberately side-effect-free and takes its
5
+ * dependencies as parameters. No startup wiring, no global state, no
6
+ * callers-injected emit functions. The rest of backend.ts is free to do
7
+ * the noisy orchestration; the decisions encoded here can be unit-tested
8
+ * in isolation.
9
+ */
10
+
11
+ import type { LLMMessage, ProviderId } from '../types.ts';
12
+ import type { ModelRegistry, ModelEntry } from '../router/registry.ts';
13
+ import type { BudgetProfile } from '../router/profiles.ts';
14
+
15
+ // ---------------------------------------------------------------------------
16
+ // In-loop context compaction
17
+ // ---------------------------------------------------------------------------
18
+
19
+ /**
20
+ * Stub tool results older than `keepLatest` iterations in place so they stop
21
+ * costing input tokens on every subsequent LLM call inside an agent loop.
22
+ *
23
+ * Rationale: the ledger showed one user turn going 10k → 23k input tokens as
24
+ * `read_file` / `search_code` results piled up in the messages array. Most
25
+ * of that content is no longer load-bearing two iterations later — the model
26
+ * has already read whatever mattered and moved on. We keep the last
27
+ * `keepLatest` tool turns verbatim and collapse older ones into one-line
28
+ * placeholders. Errors are never stubbed. `messages` is mutated in place;
29
+ * it's a local buffer to handleSubmit, so there's no cross-turn leakage.
30
+ */
31
+ export function collapseOldToolResults(
32
+ messages: LLMMessage[],
33
+ keepLatest = 2,
34
+ minLen = 300,
35
+ ): number {
36
+ let saved = 0;
37
+ let keptToolTurns = 0;
38
+ for (let i = messages.length - 1; i >= 0; i--) {
39
+ const m = messages[i];
40
+ if (m.role !== 'tool' || !m.toolResults) continue;
41
+ if (keptToolTurns < keepLatest) {
42
+ keptToolTurns++;
43
+ continue;
44
+ }
45
+ for (const tr of m.toolResults) {
46
+ if (tr.isError) continue;
47
+ const origLen = (tr.content || '').length;
48
+ if (origLen < minLen) continue;
49
+ const stub = `[collapsed: ${origLen} chars from earlier iteration — content pruned to save context]`;
50
+ saved += origLen - stub.length;
51
+ tr.content = stub;
52
+ }
53
+ }
54
+ return saved;
55
+ }
56
+
57
+ /** Cheap estimate of total message tokens (4 chars ≈ 1 token). */
58
+ export function estimateMessagesTokens(messages: LLMMessage[]): number {
59
+ let chars = 0;
60
+ for (const m of messages) {
61
+ if (m.content) chars += m.content.length;
62
+ if (m.toolCalls) chars += JSON.stringify(m.toolCalls).length;
63
+ if (m.toolResults) for (const tr of m.toolResults) chars += (tr.content || '').length;
64
+ }
65
+ return Math.ceil(chars / 4);
66
+ }
67
+
68
+ /**
69
+ * Adaptive in-loop compaction. Escalates aggressiveness until the token
70
+ * estimate is under the profile's contextBudget:
71
+ *
72
+ * pass 1: keep 2 tool turns, stub anything ≥ 300 chars
73
+ * pass 2: keep 1 tool turn, stub anything ≥ 100 chars
74
+ * pass 3: keep 1 tool turn, stub anything ≥ 50 chars
75
+ *
76
+ * No LLM calls — pure local string manipulation. Returns before/after
77
+ * token estimates and total bytes saved so the caller can emit it as an
78
+ * activity line.
79
+ */
80
+ export function compactInLoop(
81
+ messages: LLMMessage[],
82
+ budget: number,
83
+ ): { before: number; after: number; savedBytes: number } {
84
+ const before = estimateMessagesTokens(messages);
85
+ if (before <= budget) return { before, after: before, savedBytes: 0 };
86
+
87
+ let savedBytes = collapseOldToolResults(messages, 2, 300);
88
+ if (estimateMessagesTokens(messages) > budget) {
89
+ savedBytes += collapseOldToolResults(messages, 1, 100);
90
+ }
91
+ if (estimateMessagesTokens(messages) > budget) {
92
+ savedBytes += collapseOldToolResults(messages, 1, 50);
93
+ }
94
+ return { before, after: estimateMessagesTokens(messages), savedBytes };
95
+ }
96
+
97
+ // ---------------------------------------------------------------------------
98
+ // Model selection helpers
99
+ // ---------------------------------------------------------------------------
100
+
101
+ /**
102
+ * Pick the cheapest enabled model for compaction-style LLM calls.
103
+ * Honors the profile's `allowedProviders` if set, else falls back to
104
+ * deriving scope from `rolePinning` — so a zai-only profile compacts
105
+ * with glm-4.5-flash (free), not claude-haiku.
106
+ * Returns undefined if nothing suitable is enabled.
107
+ */
108
+ export function pickCompressionModel(
109
+ registry: ModelRegistry,
110
+ profile: BudgetProfile,
111
+ ): { provider: ProviderId; id: string } | undefined {
112
+ const candidates: ModelEntry[] = registry.getAvailable();
113
+ const providers = new Set<string>();
114
+ if (profile.allowedProviders && profile.allowedProviders.length > 0) {
115
+ for (const p of profile.allowedProviders) providers.add(p);
116
+ } else if (profile.rolePinning) {
117
+ for (const modelId of Object.values(profile.rolePinning)) {
118
+ const m = registry.getById(modelId);
119
+ if (m) providers.add(m.provider);
120
+ }
121
+ }
122
+ const inScope: ModelEntry[] = providers.size > 0
123
+ ? candidates.filter(m => providers.has(m.provider))
124
+ : candidates;
125
+ const withSummarization = inScope.filter(m => m.capabilities.includes('summarization'));
126
+ const pool = withSummarization.length > 0 ? withSummarization : inScope;
127
+ if (pool.length === 0) return undefined;
128
+ pool.sort((a, b) => a.inputCostPer1M - b.inputCostPer1M);
129
+ return { provider: pool[0].provider, id: pool[0].id };
130
+ }
131
+
132
+ // ---------------------------------------------------------------------------
133
+ // Phase classification
134
+ // ---------------------------------------------------------------------------
135
+
136
+ /**
137
+ * Coarse classification of a user message as a coding-execution task vs. a
138
+ * discussion. Used to pick an initial phase for `router.select()`. The
139
+ * intent router runs on every phase, so a misclassification here is
140
+ * recoverable — the intent tier will still pick a role-appropriate model.
141
+ *
142
+ * Heuristics target the common patterns:
143
+ * - Strong coding verb + code-y noun ("implement a parser", "save this to disk")
144
+ * - Language keyword ("in Python", "in Rust")
145
+ * - File extension mention (".py", ".ts", ".md", …)
146
+ * - File-oriented imperative ("save X to disk", "dump Y to file")
147
+ *
148
+ * Everything else defaults to `discuss`.
149
+ */
150
+ export function classifyPhase(input: string): 'execute' | 'discuss' {
151
+ const s = input.toLowerCase();
152
+ // Strong coding-intent verbs paired with code-y nouns / file extensions / language names.
153
+ if (/\b(write|make|create|build|implement|generate|add|fix|debug|refactor|optimize|update|change|modify|edit|remove|rewrite|port|translate|save|store|persist|dump|export|append)\b[^\n]{0,80}\b(code|script|function|class|method|file|test|module|app|component|endpoint|api|page|cli|tool|server|client|parser|wrapper|helper|util|util(?:s|ity)|service|model|database|schema|migration|response|answer|reply|output|log|notes?|disk|report|review|summary|transcript)\b/.test(s)) return 'execute';
154
+ if (/\bin\s+(python|javascript|typescript|rust|go(lang)?|java|c\+\+|c#|ruby|php|swift|kotlin|bash|shell|sql)\b/.test(s)) return 'execute';
155
+ if (/\.(py|js|ts|tsx|jsx|rs|go|java|cpp|cc|h|hpp|cs|rb|php|swift|kt|sh|sql|html|css|scss|json|yml|yaml|toml|md|txt)\b/.test(s)) return 'execute';
156
+ if (/\b(write|make|create|build|implement)\s+(a|an|the)?\s*(python|js|ts|rust|go|bash|shell|sql)\b/.test(s)) return 'execute';
157
+ if (/\b(save|write|store|dump|export|persist|append)\b[^\n]{0,40}\b(to|as|into|in)\b[^\n]{0,80}\b(disk|file|folder|directory|path)\b/.test(s)) return 'execute';
158
+ return 'discuss';
159
+ }