praana 0.5.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 (204) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +124 -0
  3. package/bin/praana.js +17 -0
  4. package/bin/pran.js +17 -0
  5. package/dist/app-banner.d.ts +11 -0
  6. package/dist/app-banner.js +161 -0
  7. package/dist/app-controller.d.ts +44 -0
  8. package/dist/app-controller.js +143 -0
  9. package/dist/app-identity.d.ts +18 -0
  10. package/dist/app-identity.js +52 -0
  11. package/dist/auto-compact.d.ts +16 -0
  12. package/dist/auto-compact.js +101 -0
  13. package/dist/cli-args.d.ts +14 -0
  14. package/dist/cli-args.js +69 -0
  15. package/dist/compile-classic.d.ts +21 -0
  16. package/dist/compile-classic.js +106 -0
  17. package/dist/compiler.d.ts +75 -0
  18. package/dist/compiler.js +406 -0
  19. package/dist/config.d.ts +3 -0
  20. package/dist/config.js +433 -0
  21. package/dist/context-engine/activity-log.d.ts +9 -0
  22. package/dist/context-engine/activity-log.js +109 -0
  23. package/dist/context-engine/artifact-store.d.ts +32 -0
  24. package/dist/context-engine/artifact-store.js +272 -0
  25. package/dist/context-engine/bm25.d.ts +3 -0
  26. package/dist/context-engine/bm25.js +32 -0
  27. package/dist/context-engine/checkpoint.d.ts +34 -0
  28. package/dist/context-engine/checkpoint.js +430 -0
  29. package/dist/context-engine/classify.d.ts +3 -0
  30. package/dist/context-engine/classify.js +60 -0
  31. package/dist/context-engine/db.d.ts +73 -0
  32. package/dist/context-engine/db.js +505 -0
  33. package/dist/context-engine/distiller.d.ts +30 -0
  34. package/dist/context-engine/distiller.js +67 -0
  35. package/dist/context-engine/engine-compiler.d.ts +23 -0
  36. package/dist/context-engine/engine-compiler.js +297 -0
  37. package/dist/context-engine/error-tracker.d.ts +21 -0
  38. package/dist/context-engine/error-tracker.js +74 -0
  39. package/dist/context-engine/event-lineage.d.ts +26 -0
  40. package/dist/context-engine/event-lineage.js +120 -0
  41. package/dist/context-engine/extraction.d.ts +26 -0
  42. package/dist/context-engine/extraction.js +83 -0
  43. package/dist/context-engine/index.d.ts +82 -0
  44. package/dist/context-engine/index.js +238 -0
  45. package/dist/context-engine/scoring.d.ts +13 -0
  46. package/dist/context-engine/scoring.js +47 -0
  47. package/dist/context-engine/state-snapshot.d.ts +8 -0
  48. package/dist/context-engine/state-snapshot.js +50 -0
  49. package/dist/context-engine/summarize.d.ts +6 -0
  50. package/dist/context-engine/summarize.js +32 -0
  51. package/dist/context-engine/telemetry.d.ts +25 -0
  52. package/dist/context-engine/telemetry.js +64 -0
  53. package/dist/context-engine/turn-digest.d.ts +50 -0
  54. package/dist/context-engine/turn-digest.js +250 -0
  55. package/dist/context-engine/turn-ledger.d.ts +18 -0
  56. package/dist/context-engine/turn-ledger.js +184 -0
  57. package/dist/context-engine/turn-recorder.d.ts +24 -0
  58. package/dist/context-engine/turn-recorder.js +88 -0
  59. package/dist/context-engine/types.d.ts +201 -0
  60. package/dist/context-engine/types.js +4 -0
  61. package/dist/context-pressure.d.ts +19 -0
  62. package/dist/context-pressure.js +36 -0
  63. package/dist/distillers/generic.d.ts +14 -0
  64. package/dist/distillers/generic.js +93 -0
  65. package/dist/distillers/git-diff.d.ts +8 -0
  66. package/dist/distillers/git-diff.js +119 -0
  67. package/dist/distillers/index.d.ts +2 -0
  68. package/dist/distillers/index.js +16 -0
  69. package/dist/distillers/npm-test.d.ts +8 -0
  70. package/dist/distillers/npm-test.js +50 -0
  71. package/dist/distillers/rg-results.d.ts +8 -0
  72. package/dist/distillers/rg-results.js +28 -0
  73. package/dist/distillers/tsc-errors.d.ts +8 -0
  74. package/dist/distillers/tsc-errors.js +52 -0
  75. package/dist/event-log.d.ts +56 -0
  76. package/dist/event-log.js +214 -0
  77. package/dist/llm.d.ts +29 -0
  78. package/dist/llm.js +155 -0
  79. package/dist/logger.d.ts +94 -0
  80. package/dist/logger.js +287 -0
  81. package/dist/main.d.ts +1 -0
  82. package/dist/main.js +54 -0
  83. package/dist/memory/confidence.d.ts +7 -0
  84. package/dist/memory/confidence.js +37 -0
  85. package/dist/memory/consolidation.d.ts +26 -0
  86. package/dist/memory/consolidation.js +166 -0
  87. package/dist/memory/db.d.ts +40 -0
  88. package/dist/memory/db.js +283 -0
  89. package/dist/memory/dedup.d.ts +6 -0
  90. package/dist/memory/dedup.js +50 -0
  91. package/dist/memory/embedder-factory.d.ts +3 -0
  92. package/dist/memory/embedder-factory.js +81 -0
  93. package/dist/memory/embeddings.d.ts +15 -0
  94. package/dist/memory/embeddings.js +67 -0
  95. package/dist/memory/index.d.ts +9 -0
  96. package/dist/memory/index.js +11 -0
  97. package/dist/memory/ollama-summarizer.d.ts +19 -0
  98. package/dist/memory/ollama-summarizer.js +72 -0
  99. package/dist/memory/openai-summarizer.d.ts +21 -0
  100. package/dist/memory/openai-summarizer.js +51 -0
  101. package/dist/memory/store.d.ts +61 -0
  102. package/dist/memory/store.js +502 -0
  103. package/dist/memory/summarizer-factory.d.ts +3 -0
  104. package/dist/memory/summarizer-factory.js +69 -0
  105. package/dist/memory/summarizer.d.ts +4 -0
  106. package/dist/memory/summarizer.js +112 -0
  107. package/dist/memory/types.d.ts +87 -0
  108. package/dist/memory/types.js +17 -0
  109. package/dist/model-context.d.ts +15 -0
  110. package/dist/model-context.js +212 -0
  111. package/dist/project-detector.d.ts +37 -0
  112. package/dist/project-detector.js +604 -0
  113. package/dist/render.d.ts +15 -0
  114. package/dist/render.js +46 -0
  115. package/dist/session.d.ts +118 -0
  116. package/dist/session.js +809 -0
  117. package/dist/skills/index.d.ts +69 -0
  118. package/dist/skills/index.js +885 -0
  119. package/dist/skills/types.d.ts +93 -0
  120. package/dist/skills/types.js +8 -0
  121. package/dist/slash-commands.d.ts +14 -0
  122. package/dist/slash-commands.js +301 -0
  123. package/dist/state-graph.d.ts +38 -0
  124. package/dist/state-graph.js +255 -0
  125. package/dist/status-bar.d.ts +54 -0
  126. package/dist/status-bar.js +184 -0
  127. package/dist/thinking-display.d.ts +21 -0
  128. package/dist/thinking-display.js +37 -0
  129. package/dist/tool-summary.d.ts +4 -0
  130. package/dist/tool-summary.js +67 -0
  131. package/dist/tools/index.d.ts +925 -0
  132. package/dist/tools/index.js +86 -0
  133. package/dist/tools/knowledge.d.ts +140 -0
  134. package/dist/tools/knowledge.js +260 -0
  135. package/dist/tools/memory.d.ts +39 -0
  136. package/dist/tools/memory.js +300 -0
  137. package/dist/tools/search-code.d.ts +134 -0
  138. package/dist/tools/search-code.js +390 -0
  139. package/dist/tools/system.d.ts +16 -0
  140. package/dist/tools/system.js +499 -0
  141. package/dist/tools/tool-def.d.ts +6 -0
  142. package/dist/tools/tool-def.js +3 -0
  143. package/dist/turn-control.d.ts +51 -0
  144. package/dist/turn-control.js +210 -0
  145. package/dist/turn.d.ts +20 -0
  146. package/dist/turn.js +624 -0
  147. package/dist/types.d.ts +233 -0
  148. package/dist/types.js +4 -0
  149. package/dist/ui/readline-ui.d.ts +2 -0
  150. package/dist/ui/readline-ui.js +176 -0
  151. package/dist/ui/tui/app.d.ts +13 -0
  152. package/dist/ui/tui/app.js +270 -0
  153. package/dist/ui/tui/busy-indicator.d.ts +2 -0
  154. package/dist/ui/tui/busy-indicator.js +13 -0
  155. package/dist/ui/tui/components/gutter-rule.d.ts +5 -0
  156. package/dist/ui/tui/components/gutter-rule.js +9 -0
  157. package/dist/ui/tui/components/inline-tool-row.d.ts +10 -0
  158. package/dist/ui/tui/components/inline-tool-row.js +8 -0
  159. package/dist/ui/tui/components/prompt-input.d.ts +20 -0
  160. package/dist/ui/tui/components/prompt-input.js +120 -0
  161. package/dist/ui/tui/components/system-line.d.ts +5 -0
  162. package/dist/ui/tui/components/system-line.js +6 -0
  163. package/dist/ui/tui/components/thinking-block.d.ts +11 -0
  164. package/dist/ui/tui/components/thinking-block.js +31 -0
  165. package/dist/ui/tui/components/toast-line.d.ts +4 -0
  166. package/dist/ui/tui/components/toast-line.js +8 -0
  167. package/dist/ui/tui/components/tool-result-line.d.ts +5 -0
  168. package/dist/ui/tui/components/tool-result-line.js +6 -0
  169. package/dist/ui/tui/components/turn-footer.d.ts +5 -0
  170. package/dist/ui/tui/components/turn-footer.js +7 -0
  171. package/dist/ui/tui/components/user-block.d.ts +6 -0
  172. package/dist/ui/tui/components/user-block.js +6 -0
  173. package/dist/ui/tui/logo-banner.d.ts +5 -0
  174. package/dist/ui/tui/logo-banner.js +8 -0
  175. package/dist/ui/tui/markdown-render.d.ts +16 -0
  176. package/dist/ui/tui/markdown-render.js +218 -0
  177. package/dist/ui/tui/palette.d.ts +12 -0
  178. package/dist/ui/tui/palette.js +13 -0
  179. package/dist/ui/tui/reasoning-summary.d.ts +12 -0
  180. package/dist/ui/tui/reasoning-summary.js +27 -0
  181. package/dist/ui/tui/reducer.d.ts +92 -0
  182. package/dist/ui/tui/reducer.js +260 -0
  183. package/dist/ui/tui/run.d.ts +3 -0
  184. package/dist/ui/tui/run.js +40 -0
  185. package/dist/ui/tui/sink.d.ts +4 -0
  186. package/dist/ui/tui/sink.js +89 -0
  187. package/dist/ui/tui/status-bar-view.d.ts +5 -0
  188. package/dist/ui/tui/status-bar-view.js +44 -0
  189. package/dist/ui/tui/terminal-height.d.ts +12 -0
  190. package/dist/ui/tui/terminal-height.js +20 -0
  191. package/dist/ui/tui/terminal-width.d.ts +2 -0
  192. package/dist/ui/tui/terminal-width.js +5 -0
  193. package/dist/ui/tui/tool-display.d.ts +23 -0
  194. package/dist/ui/tui/tool-display.js +217 -0
  195. package/dist/ui/tui/transcript-line.d.ts +12 -0
  196. package/dist/ui/tui/transcript-line.js +43 -0
  197. package/dist/ui/tui/transcript-replay.d.ts +12 -0
  198. package/dist/ui/tui/transcript-replay.js +117 -0
  199. package/dist/ui-events.d.ts +39 -0
  200. package/dist/ui-events.js +33 -0
  201. package/dist/ui.d.ts +77 -0
  202. package/dist/ui.js +179 -0
  203. package/package.json +73 -0
  204. package/praana.config.example.toml +231 -0
@@ -0,0 +1,56 @@
1
+ import type { Event, EventActor, EventKind } from "./types.js";
2
+ export interface EventSearchOptions {
3
+ kinds?: EventKind[];
4
+ limit?: number;
5
+ }
6
+ export interface EventSearchMatch {
7
+ event: Event;
8
+ excerpt: string;
9
+ }
10
+ export declare const EVENT_LOG_FILENAME = "events.jsonl";
11
+ export declare const LEGACY_EVENT_LOG_FILENAME = "events.log";
12
+ export declare function getEventLogPath(sessionDir: string): string;
13
+ export declare function getLegacyEventLogPath(sessionDir: string): string;
14
+ /** Rename or merge legacy events.log into events.jsonl when opening a session. */
15
+ export declare function migrateLegacyEventLog(sessionDir: string): void;
16
+ export declare class EventLog {
17
+ private fd;
18
+ private logPath;
19
+ private checkpointPath;
20
+ private sessionId;
21
+ private eventCount;
22
+ private closed;
23
+ private compressedIds;
24
+ constructor(sessionId: string, logDir: string);
25
+ private loadCompressionCheckpoint;
26
+ append(event: {
27
+ kind: EventKind;
28
+ actor: EventActor;
29
+ payload: Record<string, unknown>;
30
+ event_id?: string;
31
+ timestamp?: number;
32
+ }): void;
33
+ readLast(n: number): Event[];
34
+ readAll(): Event[];
35
+ /** All events excluding those marked compressed for prompt assembly. */
36
+ readAllUncompressed(): Event[];
37
+ replayContextActions(): Event[];
38
+ getLogPath(): string;
39
+ /**
40
+ * Search all events in this session. Terms are ANDed (case-insensitive).
41
+ * Use pipe (|) in query for OR alternatives, e.g. "issue|review".
42
+ */
43
+ search(query: string, options?: EventSearchOptions): EventSearchMatch[];
44
+ private internalRead;
45
+ getSessionId(): string;
46
+ /** Mark event IDs as compressed — they will be excluded from readLastUncompressed. */
47
+ markEventsAsCompressed(eventIds: string[]): void;
48
+ /** Read last n events, excluding compressed ones. */
49
+ readLastUncompressed(n: number): Event[];
50
+ /** Get the number of compressed events. */
51
+ getCompressedCount(): number;
52
+ close(): void;
53
+ }
54
+ import type { SessionMeta } from "./types.js";
55
+ export declare function writeSessionMeta(logDir: string, meta: SessionMeta): void;
56
+ export declare function readSessionMeta(logDir: string, sessionId: string): SessionMeta | null;
@@ -0,0 +1,214 @@
1
+ import { mkdirSync, openSync, writeSync, fsyncSync, closeSync, readFileSync, existsSync, renameSync, statSync, unlinkSync, appendFileSync, } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { ulid } from "ulid";
4
+ export const EVENT_LOG_FILENAME = "events.jsonl";
5
+ export const LEGACY_EVENT_LOG_FILENAME = "events.log";
6
+ export function getEventLogPath(sessionDir) {
7
+ return join(sessionDir, EVENT_LOG_FILENAME);
8
+ }
9
+ export function getLegacyEventLogPath(sessionDir) {
10
+ return join(sessionDir, LEGACY_EVENT_LOG_FILENAME);
11
+ }
12
+ /** Rename or merge legacy events.log into events.jsonl when opening a session. */
13
+ export function migrateLegacyEventLog(sessionDir) {
14
+ const jsonlPath = getEventLogPath(sessionDir);
15
+ const legacyPath = getLegacyEventLogPath(sessionDir);
16
+ if (!existsSync(legacyPath))
17
+ return;
18
+ if (!existsSync(jsonlPath)) {
19
+ renameSync(legacyPath, jsonlPath);
20
+ return;
21
+ }
22
+ const legacySize = statSync(legacyPath).size;
23
+ if (legacySize === 0) {
24
+ unlinkSync(legacyPath);
25
+ return;
26
+ }
27
+ const jsonlSize = statSync(jsonlPath).size;
28
+ if (jsonlSize === 0) {
29
+ unlinkSync(jsonlPath);
30
+ renameSync(legacyPath, jsonlPath);
31
+ return;
32
+ }
33
+ const legacyContent = readFileSync(legacyPath, "utf-8");
34
+ if (legacyContent.trim()) {
35
+ // Ensure events.jsonl ends with newline before appending
36
+ const jsonlContent = readFileSync(jsonlPath, "utf-8");
37
+ const prefix = jsonlContent.endsWith("\n") ? "" : "\n";
38
+ const suffix = legacyContent.endsWith("\n") ? legacyContent : legacyContent + "\n";
39
+ appendFileSync(jsonlPath, prefix + suffix);
40
+ }
41
+ unlinkSync(legacyPath);
42
+ }
43
+ export class EventLog {
44
+ fd;
45
+ logPath;
46
+ checkpointPath;
47
+ sessionId;
48
+ eventCount = 0;
49
+ closed = false;
50
+ compressedIds = new Set();
51
+ constructor(sessionId, logDir) {
52
+ this.sessionId = sessionId;
53
+ const sessionDir = join(logDir, sessionId);
54
+ mkdirSync(sessionDir, { recursive: true });
55
+ migrateLegacyEventLog(sessionDir);
56
+ this.logPath = getEventLogPath(sessionDir);
57
+ this.checkpointPath = join(sessionDir, "compression_checkpoint.json");
58
+ this.fd = openSync(this.logPath, "a");
59
+ this.loadCompressionCheckpoint();
60
+ }
61
+ loadCompressionCheckpoint() {
62
+ try {
63
+ if (existsSync(this.checkpointPath)) {
64
+ const data = JSON.parse(readFileSync(this.checkpointPath, "utf-8"));
65
+ if (Array.isArray(data.compressed_ids)) {
66
+ this.compressedIds = new Set(data.compressed_ids);
67
+ }
68
+ }
69
+ }
70
+ catch { /* ignore corrupted checkpoint */ }
71
+ }
72
+ append(event) {
73
+ const fullEvent = {
74
+ event_id: event.event_id ?? ulid(),
75
+ session_id: this.sessionId,
76
+ timestamp: event.timestamp ?? Date.now(),
77
+ kind: event.kind,
78
+ actor: event.actor,
79
+ payload: event.payload,
80
+ };
81
+ const line = JSON.stringify(fullEvent) + "\n";
82
+ writeSync(this.fd, line, undefined, "utf-8");
83
+ fsyncSync(this.fd);
84
+ this.eventCount++;
85
+ }
86
+ readLast(n) {
87
+ return this.internalRead().slice(-n);
88
+ }
89
+ readAll() {
90
+ return this.internalRead();
91
+ }
92
+ /** All events excluding those marked compressed for prompt assembly. */
93
+ readAllUncompressed() {
94
+ const all = this.internalRead();
95
+ if (this.compressedIds.size === 0)
96
+ return all;
97
+ return all.filter((e) => !this.compressedIds.has(e.event_id));
98
+ }
99
+ replayContextActions() {
100
+ return this.internalRead().filter((e) => e.kind === "context_action");
101
+ }
102
+ getLogPath() {
103
+ return this.logPath;
104
+ }
105
+ /**
106
+ * Search all events in this session. Terms are ANDed (case-insensitive).
107
+ * Use pipe (|) in query for OR alternatives, e.g. "issue|review".
108
+ */
109
+ search(query, options = {}) {
110
+ const trimmed = query.trim();
111
+ if (!trimmed)
112
+ return [];
113
+ const terms = trimmed.includes("|")
114
+ ? trimmed.split("|").map((t) => t.trim().toLowerCase()).filter(Boolean)
115
+ : trimmed.toLowerCase().split(/\s+/).filter(Boolean);
116
+ const kindSet = options.kinds ? new Set(options.kinds) : null;
117
+ const limit = options.limit ?? 20;
118
+ const matches = [];
119
+ for (const event of this.internalRead()) {
120
+ if (kindSet && !kindSet.has(event.kind))
121
+ continue;
122
+ const text = eventSearchText(event).toLowerCase();
123
+ const matched = trimmed.includes("|")
124
+ ? terms.some((term) => text.includes(term))
125
+ : terms.every((term) => text.includes(term));
126
+ if (!matched)
127
+ continue;
128
+ matches.push({ event, excerpt: buildExcerpt(event, 400) });
129
+ if (matches.length >= limit)
130
+ break;
131
+ }
132
+ return matches;
133
+ }
134
+ internalRead() {
135
+ try {
136
+ const content = readFileSync(this.logPath, "utf-8");
137
+ return content.trim().split("\n").filter(Boolean).map((line) => JSON.parse(line));
138
+ }
139
+ catch {
140
+ return [];
141
+ }
142
+ }
143
+ getSessionId() {
144
+ return this.sessionId;
145
+ }
146
+ /** Mark event IDs as compressed — they will be excluded from readLastUncompressed. */
147
+ markEventsAsCompressed(eventIds) {
148
+ for (const id of eventIds) {
149
+ this.compressedIds.add(id);
150
+ }
151
+ const data = { compressed_ids: Array.from(this.compressedIds), timestamp: Date.now() };
152
+ writeFileSync(this.checkpointPath, JSON.stringify(data) + "\n", "utf-8");
153
+ }
154
+ /** Read last n events, excluding compressed ones. */
155
+ readLastUncompressed(n) {
156
+ const all = this.internalRead();
157
+ if (this.compressedIds.size === 0)
158
+ return all.slice(-n);
159
+ const uncompressed = all.filter((e) => !this.compressedIds.has(e.event_id));
160
+ return uncompressed.slice(-n);
161
+ }
162
+ /** Get the number of compressed events. */
163
+ getCompressedCount() {
164
+ return this.compressedIds.size;
165
+ }
166
+ close() {
167
+ if (this.closed)
168
+ return;
169
+ this.closed = true;
170
+ closeSync(this.fd);
171
+ }
172
+ }
173
+ // ---- Session meta helpers ----
174
+ import { writeFileSync } from "node:fs";
175
+ import { join as pathJoin } from "node:path";
176
+ export function writeSessionMeta(logDir, meta) {
177
+ const sessionDir = pathJoin(logDir, meta.session_id);
178
+ mkdirSync(sessionDir, { recursive: true });
179
+ writeFileSync(pathJoin(sessionDir, "meta.json"), JSON.stringify(meta, null, 2) + "\n");
180
+ }
181
+ function eventSearchText(event) {
182
+ const p = event.payload;
183
+ switch (event.kind) {
184
+ case "user_message":
185
+ case "agent_message":
186
+ return String(p.text ?? "");
187
+ case "tool_call":
188
+ return `${String(p.tool ?? "")} ${JSON.stringify(p.args ?? {})}`;
189
+ case "tool_result":
190
+ return `${String(p.tool ?? "")} ${JSON.stringify(p.result ?? {})}`;
191
+ case "context_action":
192
+ return JSON.stringify(p);
193
+ case "system_note":
194
+ return JSON.stringify(p);
195
+ default:
196
+ return JSON.stringify(p);
197
+ }
198
+ }
199
+ function buildExcerpt(event, maxLen) {
200
+ const text = eventSearchText(event).replace(/\s+/g, " ").trim();
201
+ if (text.length <= maxLen)
202
+ return text;
203
+ return text.slice(0, maxLen - 3) + "...";
204
+ }
205
+ export function readSessionMeta(logDir, sessionId) {
206
+ try {
207
+ const metaPath = pathJoin(logDir, sessionId, "meta.json");
208
+ const content = readFileSync(metaPath, "utf-8");
209
+ return JSON.parse(content);
210
+ }
211
+ catch {
212
+ return null;
213
+ }
214
+ }
package/dist/llm.d.ts ADDED
@@ -0,0 +1,29 @@
1
+ import type { PraanaConfig } from "./types.js";
2
+ export { resolveContextWindowSync, fetchAndCacheContextWindow, DEFAULT_MODEL_CONTEXT_WINDOW, } from "./model-context.js";
3
+ interface ProviderConfig {
4
+ /** pi-ai API type identifier */
5
+ api: string;
6
+ /** pi-ai provider identifier */
7
+ provider: string;
8
+ /** Env var to check for API key, or null if none needed */
9
+ envKey: string | null;
10
+ /** Default base URL for this provider's API */
11
+ baseUrl: string;
12
+ /** Optional HTTP headers sent with every request */
13
+ headers?: Record<string, string>;
14
+ }
15
+ /** Lookup a provider config. Falls back to openrouter for unknown values. */
16
+ export declare function getProviderConfig(provider: string): ProviderConfig;
17
+ /** Return all known provider IDs (for docs / help text). */
18
+ export declare function listKnownProviders(): string[];
19
+ /** Return the env var name required by a provider, or null. */
20
+ export declare function getProviderEnvKey(provider: string): string | null;
21
+ /** Check whether the provider's API key is available in the environment. */
22
+ export declare function isProviderAvailable(provider: string): boolean;
23
+ /** Human-readable message explaining which env var is missing. */
24
+ export declare function getMissingKeyMessage(provider: string): string | null;
25
+ type RuntimeModel = Record<string, unknown> & {
26
+ __piOptions?: Record<string, unknown>;
27
+ };
28
+ export declare function createProvider(config: PraanaConfig["llm"], contextWindow?: number): (modelId: string) => RuntimeModel;
29
+ export declare function resolveModel(modelString: string): string;
package/dist/llm.js ADDED
@@ -0,0 +1,155 @@
1
+ import { resolveContextWindowSync } from "./model-context.js";
2
+ import { getAppLogger } from "./logger.js";
3
+ export { resolveContextWindowSync, fetchAndCacheContextWindow, DEFAULT_MODEL_CONTEXT_WINDOW, } from "./model-context.js";
4
+ const PROVIDER_REGISTRY = {
5
+ // ── OpenAI-compatible (use "openai-completions" API) ──
6
+ openrouter: {
7
+ api: "openai-completions",
8
+ provider: "openrouter",
9
+ envKey: "OPENROUTER_API_KEY",
10
+ baseUrl: "https://openrouter.ai/api/v1",
11
+ headers: {
12
+ "HTTP-Referer": "https://github.com/aria",
13
+ "X-Title": "PRAANA",
14
+ },
15
+ },
16
+ openai: {
17
+ api: "openai-completions",
18
+ provider: "openai",
19
+ envKey: "OPENAI_API_KEY",
20
+ baseUrl: "https://api.openai.com/v1",
21
+ },
22
+ deepseek: {
23
+ api: "openai-completions",
24
+ provider: "deepseek",
25
+ envKey: "DEEPSEEK_API_KEY",
26
+ baseUrl: "https://api.deepseek.com/v1",
27
+ },
28
+ groq: {
29
+ api: "openai-completions",
30
+ provider: "groq",
31
+ envKey: "GROQ_API_KEY",
32
+ baseUrl: "https://api.groq.com/openai/v1",
33
+ },
34
+ xai: {
35
+ api: "openai-completions",
36
+ provider: "xai",
37
+ envKey: "XAI_API_KEY",
38
+ baseUrl: "https://api.x.ai/v1",
39
+ },
40
+ fireworks: {
41
+ api: "openai-completions",
42
+ provider: "fireworks",
43
+ envKey: "FIREWORKS_API_KEY",
44
+ baseUrl: "https://api.fireworks.ai/inference/v1",
45
+ },
46
+ opencode: {
47
+ api: "openai-completions",
48
+ provider: "opencode",
49
+ envKey: "OPENCODE_API_KEY",
50
+ baseUrl: "https://opencode.ai/zen/v1",
51
+ },
52
+ together: {
53
+ api: "openai-completions",
54
+ provider: "together",
55
+ envKey: "TOGETHER_API_KEY",
56
+ baseUrl: "https://api.together.xyz/v1",
57
+ },
58
+ ollama: {
59
+ api: "openai-completions",
60
+ provider: "openai",
61
+ envKey: null, // local — no key needed
62
+ baseUrl: "http://127.0.0.1:11434/v1",
63
+ },
64
+ // ── Native API (different wire protocol) ──
65
+ anthropic: {
66
+ api: "anthropic-messages",
67
+ provider: "anthropic",
68
+ envKey: "ANTHROPIC_API_KEY",
69
+ baseUrl: "https://api.anthropic.com",
70
+ },
71
+ google: {
72
+ api: "google-generative-ai",
73
+ provider: "google",
74
+ envKey: "GOOGLE_GENERATIVE_AI_API_KEY",
75
+ baseUrl: "https://generativelanguage.googleapis.com/v1beta",
76
+ },
77
+ mistral: {
78
+ api: "mistral-conversations",
79
+ provider: "mistral",
80
+ envKey: "MISTRAL_API_KEY",
81
+ baseUrl: "https://api.mistral.ai/v1",
82
+ },
83
+ "amazon-bedrock": {
84
+ api: "bedrock-converse-stream",
85
+ provider: "amazon-bedrock",
86
+ envKey: null, // uses AWS credentials (env / IAM / profile)
87
+ baseUrl: "",
88
+ },
89
+ };
90
+ // ── Exported helpers ───────────────────────────────────────────
91
+ /** Lookup a provider config. Falls back to openrouter for unknown values. */
92
+ export function getProviderConfig(provider) {
93
+ const entry = PROVIDER_REGISTRY[provider];
94
+ if (!entry) {
95
+ getAppLogger().child("llm").warn(`Unknown provider "${provider}", falling back to openrouter. Known providers: ${listKnownProviders().join(", ")}`);
96
+ return PROVIDER_REGISTRY["openrouter"];
97
+ }
98
+ return entry;
99
+ }
100
+ /** Return all known provider IDs (for docs / help text). */
101
+ export function listKnownProviders() {
102
+ return Object.keys(PROVIDER_REGISTRY).sort();
103
+ }
104
+ /** Return the env var name required by a provider, or null. */
105
+ export function getProviderEnvKey(provider) {
106
+ return getProviderConfig(provider).envKey;
107
+ }
108
+ /** Check whether the provider's API key is available in the environment. */
109
+ export function isProviderAvailable(provider) {
110
+ const envKey = getProviderEnvKey(provider);
111
+ if (envKey === null)
112
+ return true; // no key needed (ollama, bedrock)
113
+ return !!process.env[envKey];
114
+ }
115
+ /** Human-readable message explaining which env var is missing. */
116
+ export function getMissingKeyMessage(provider) {
117
+ const envKey = getProviderEnvKey(provider);
118
+ if (envKey === null)
119
+ return null;
120
+ if (process.env[envKey])
121
+ return null;
122
+ return `Missing required environment variable: ${envKey}`;
123
+ }
124
+ function buildModel(config, modelId, contextWindow) {
125
+ const pc = getProviderConfig(config.provider);
126
+ const baseUrl = config.base_url ?? pc.baseUrl;
127
+ const apiKey = pc.envKey ? (process.env[pc.envKey] ?? "") : "no-key";
128
+ const model = {
129
+ id: modelId,
130
+ name: modelId,
131
+ provider: pc.provider,
132
+ api: pc.api,
133
+ baseUrl,
134
+ input: ["text"],
135
+ reasoning: true,
136
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
137
+ contextWindow: contextWindow ??
138
+ resolveContextWindowSync(config.provider, modelId, config.context_window),
139
+ maxTokens: 8192,
140
+ };
141
+ if (pc.headers) {
142
+ model.headers = { ...pc.headers };
143
+ }
144
+ model.__piOptions = {
145
+ apiKey,
146
+ headers: pc.headers ? { ...pc.headers } : undefined,
147
+ };
148
+ return model;
149
+ }
150
+ export function createProvider(config, contextWindow) {
151
+ return (modelId) => buildModel(config, modelId, contextWindow);
152
+ }
153
+ export function resolveModel(modelString) {
154
+ return modelString;
155
+ }
@@ -0,0 +1,94 @@
1
+ import { type DestinationStream } from "pino";
2
+ export type LogLevel = "error" | "warn" | "info" | "debug";
3
+ export type LogDomain = "app" | "compiler" | "llm" | "session" | "memory" | "config" | "tool" | "skills" | "context_engine";
4
+ export type ErrorCode = "LLM_STREAM_ERROR" | "LLM_EMPTY_RESPONSE" | "LLM_ABORTED" | "TURN_FAILED" | "SESSION_START_FAILED" | "MEMORY_INIT_FAILED" | "TOOL_EXECUTION_FAILED" | "CONFIG_INVALID" | "UNKNOWN";
5
+ export interface LogEntry {
6
+ level: LogLevel;
7
+ domain: LogDomain;
8
+ message: string;
9
+ code?: ErrorCode;
10
+ details?: Record<string, unknown>;
11
+ cause?: Error;
12
+ }
13
+ export interface LoggerOptions {
14
+ domain?: LogDomain;
15
+ debug?: boolean;
16
+ sessionId?: string;
17
+ /** Base session directory from config (e.g. ~/.praana/sessions). */
18
+ sessionLogDir?: string;
19
+ /** Pre-built rolling file destination for ~/.praana/logs. */
20
+ appFileStream?: DestinationStream;
21
+ /** Pre-built rolling file destination for session system.log. */
22
+ sessionFileStream?: DestinationStream;
23
+ /** Test hook — capture formatted log lines instead of stderr/files. */
24
+ writeLine?: (line: string) => void;
25
+ /** TUI boot — capture notice lines instead of writing to stderr. */
26
+ captureNotice?: (line: string) => void;
27
+ }
28
+ /** Daily rotated logs; symlink at `current.log` points to today's file. */
29
+ export declare const LOG_SYMLINK_FILENAME = "current.log";
30
+ export declare const LOG_RETENTION_DAYS = 15;
31
+ /** pino-roll retains `count` rotated files plus the active file. */
32
+ export declare const LOG_RETENTION_COUNT: number;
33
+ export declare function getAppLogDir(): string;
34
+ /** Base path for pino-roll (extension added by rotator). */
35
+ export declare function getAppLogBase(): string;
36
+ /** Stable symlink path for tailing the active app log. */
37
+ export declare function getAppLogPath(): string;
38
+ export declare function getSessionLogBase(sessionLogDir: string, sessionId: string): string;
39
+ /** Stable symlink path for tailing the active session system log. */
40
+ export declare function getSessionSystemLogPath(sessionLogDir: string, sessionId: string): string;
41
+ export declare class PraanaLogger {
42
+ private readonly pino;
43
+ private readonly options;
44
+ constructor(options?: LoggerOptions);
45
+ child(domain: LogDomain): PraanaLogger;
46
+ log(entry: LogEntry): void;
47
+ error(message: string, opts?: {
48
+ code?: ErrorCode;
49
+ details?: Record<string, unknown>;
50
+ cause?: Error;
51
+ domain?: LogDomain;
52
+ }): void;
53
+ warn(message: string, opts?: {
54
+ code?: ErrorCode;
55
+ details?: Record<string, unknown>;
56
+ cause?: Error;
57
+ domain?: LogDomain;
58
+ }): void;
59
+ info(message: string, opts?: {
60
+ details?: Record<string, unknown>;
61
+ domain?: LogDomain;
62
+ }): void;
63
+ debug(message: string, opts?: {
64
+ details?: Record<string, unknown>;
65
+ domain?: LogDomain;
66
+ }): void;
67
+ /** User-visible status — always on stderr; also written to system log files at info level. */
68
+ notice(message: string, opts?: {
69
+ domain?: LogDomain;
70
+ details?: Record<string, unknown>;
71
+ }): void;
72
+ }
73
+ export declare function getAppLogger(): PraanaLogger;
74
+ export declare function setAppLogger(logger: PraanaLogger): void;
75
+ /** Initialise daily-rotating app log under ~/.praana/logs (no-op in tests). */
76
+ export declare function initAppLogFile(): Promise<void>;
77
+ export declare function createSessionLogger(opts: {
78
+ sessionId: string;
79
+ sessionLogDir: string;
80
+ debug?: boolean;
81
+ captureNotice?: (line: string) => void;
82
+ }): Promise<PraanaLogger>;
83
+ export declare function createTestLogger(writeLine: (line: string) => void, opts?: {
84
+ debug?: boolean;
85
+ }): PraanaLogger;
86
+ /** Extract a human-readable message from a pi-ai assistant/error message object. */
87
+ export declare function extractLlmErrorMessage(message: unknown): string | undefined;
88
+ export declare function formatUserFacingLlmError(opts: {
89
+ reason: string;
90
+ llmMessage?: string;
91
+ model: string;
92
+ provider: string;
93
+ }): string;
94
+ export declare function isLogLevel(value: string): value is LogLevel;