@wangzhizhi/remi 0.0.1-alpha

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 (55) hide show
  1. package/README.md +9 -0
  2. package/dist/doctor.js +108 -0
  3. package/dist/git.js +41 -0
  4. package/dist/help.js +27 -0
  5. package/dist/i18n.js +422 -0
  6. package/dist/index.js +97 -0
  7. package/dist/initPrompt.js +17 -0
  8. package/dist/model.js +116 -0
  9. package/dist/modelSelection.js +34 -0
  10. package/dist/permissionDisplay.js +46 -0
  11. package/dist/permissions.js +206 -0
  12. package/dist/repl.js +346 -0
  13. package/dist/resume.js +3 -0
  14. package/dist/setup.js +62 -0
  15. package/dist/statusline.js +59 -0
  16. package/dist/style.js +48 -0
  17. package/dist/syntaxTheme.js +39 -0
  18. package/dist/tui/RemiApp.js +1756 -0
  19. package/dist/tui/commands.js +427 -0
  20. package/dist/tui/index.js +42 -0
  21. package/dist/tui/renderers/Header.js +28 -0
  22. package/dist/tui/renderers/MessageList.js +1176 -0
  23. package/dist/tui/renderers/PromptBox.js +118 -0
  24. package/dist/tui/renderers/StatusLine.js +124 -0
  25. package/dist/tui/renderers/WorkingIndicator.js +70 -0
  26. package/dist/tui/slashCommandHighlight.js +8 -0
  27. package/dist/tui/theme.js +13 -0
  28. package/dist/tui/types.js +1 -0
  29. package/dist/usage.js +66 -0
  30. package/dist/version.js +5 -0
  31. package/node_modules/@remi/compact/dist/index.js +389 -0
  32. package/node_modules/@remi/compact/package.json +8 -0
  33. package/node_modules/@remi/config/dist/index.js +426 -0
  34. package/node_modules/@remi/config/package.json +8 -0
  35. package/node_modules/@remi/core/dist/contextBuilder.js +344 -0
  36. package/node_modules/@remi/core/dist/directoryOverview.js +359 -0
  37. package/node_modules/@remi/core/dist/index.js +2843 -0
  38. package/node_modules/@remi/core/dist/projectInstructions.js +123 -0
  39. package/node_modules/@remi/core/dist/responseStyles.js +98 -0
  40. package/node_modules/@remi/core/package.json +8 -0
  41. package/node_modules/@remi/llm/dist/index.js +804 -0
  42. package/node_modules/@remi/llm/package.json +8 -0
  43. package/node_modules/@remi/memory/dist/index.js +312 -0
  44. package/node_modules/@remi/memory/package.json +8 -0
  45. package/node_modules/@remi/permissions/dist/index.js +90 -0
  46. package/node_modules/@remi/permissions/package.json +8 -0
  47. package/node_modules/@remi/sessions/dist/index.js +370 -0
  48. package/node_modules/@remi/sessions/package.json +8 -0
  49. package/node_modules/@remi/skills/dist/index.js +273 -0
  50. package/node_modules/@remi/skills/package.json +8 -0
  51. package/node_modules/@remi/terminal-markdown/dist/index.js +1412 -0
  52. package/node_modules/@remi/terminal-markdown/package.json +8 -0
  53. package/node_modules/@remi/tools/dist/index.js +3875 -0
  54. package/node_modules/@remi/tools/package.json +8 -0
  55. package/package.json +48 -0
@@ -0,0 +1,344 @@
1
+ import { createHeuristicTokenEstimator } from '@remi/llm';
2
+ const defaultTokenEstimator = createHeuristicTokenEstimator();
3
+ export function buildProviderContext(options) {
4
+ const tokenEstimator = options.tokenEstimator ?? defaultTokenEstimator;
5
+ const tokenEstimateProfile = options.tokenEstimateProfile ?? 'default';
6
+ const messages = [];
7
+ const layers = [];
8
+ addMessageLayer({
9
+ id: 'system',
10
+ source: 'prompt/base-system.md + response style',
11
+ messages,
12
+ layers,
13
+ layerMessages: [
14
+ {
15
+ role: 'system',
16
+ content: applyLayerBudget(options.systemPrompt, options.budgets?.system, options.tokenBudgets?.system, tokenEstimator, tokenEstimateProfile),
17
+ },
18
+ ],
19
+ budgetChars: options.budgets?.system,
20
+ budgetTokens: options.tokenBudgets?.system,
21
+ tokenEstimator,
22
+ tokenEstimateProfile,
23
+ });
24
+ if (options.toolPrompt && options.toolPrompt.trim().length > 0) {
25
+ addMessageLayer({
26
+ id: 'tool',
27
+ source: 'prompt/tool-use-system.md',
28
+ messages,
29
+ layers,
30
+ layerMessages: [
31
+ {
32
+ role: 'system',
33
+ content: applyLayerBudget(options.toolPrompt, options.budgets?.tool, options.tokenBudgets?.tool, tokenEstimator, tokenEstimateProfile),
34
+ },
35
+ ],
36
+ budgetChars: options.budgets?.tool,
37
+ budgetTokens: options.tokenBudgets?.tool,
38
+ tokenEstimator,
39
+ tokenEstimateProfile,
40
+ });
41
+ }
42
+ else {
43
+ addTrace(layers, {
44
+ id: 'tool',
45
+ source: 'tool prompt disabled for this turn',
46
+ included: false,
47
+ messageCount: 0,
48
+ charCount: 0,
49
+ tokenCount: 0,
50
+ }, options.budgets?.tool, options.tokenBudgets?.tool);
51
+ }
52
+ if (options.projectInstructions && options.projectInstructions.trim().length > 0) {
53
+ addMessageLayer({
54
+ id: 'project',
55
+ source: options.projectInstructionsSource ?? 'project instructions',
56
+ messages,
57
+ layers,
58
+ layerMessages: [
59
+ {
60
+ role: 'system',
61
+ content: applyLayerBudget(options.projectInstructions, options.budgets?.project, options.tokenBudgets?.project, tokenEstimator, tokenEstimateProfile),
62
+ },
63
+ ],
64
+ budgetChars: options.budgets?.project,
65
+ budgetTokens: options.tokenBudgets?.project,
66
+ tokenEstimator,
67
+ tokenEstimateProfile,
68
+ });
69
+ }
70
+ else {
71
+ addTrace(layers, {
72
+ id: 'project',
73
+ source: options.projectInstructionsSource ?? 'project instructions unavailable',
74
+ included: false,
75
+ messageCount: 0,
76
+ charCount: 0,
77
+ tokenCount: 0,
78
+ }, options.budgets?.project, options.tokenBudgets?.project);
79
+ }
80
+ const memoryPrompt = formatMemoryContext(options.memories ?? [], options.budgets?.memory, options.tokenBudgets?.memory, tokenEstimator, tokenEstimateProfile);
81
+ if (memoryPrompt) {
82
+ addMessageLayer({
83
+ id: 'memory',
84
+ source: '.agent/memory recall',
85
+ messages,
86
+ layers,
87
+ layerMessages: [{ role: 'system', content: memoryPrompt }],
88
+ budgetChars: options.budgets?.memory,
89
+ budgetTokens: options.tokenBudgets?.memory,
90
+ tokenEstimator,
91
+ tokenEstimateProfile,
92
+ });
93
+ }
94
+ else {
95
+ addTrace(layers, {
96
+ id: 'memory',
97
+ source: '.agent/memory recall',
98
+ included: false,
99
+ messageCount: 0,
100
+ charCount: 0,
101
+ tokenCount: 0,
102
+ }, options.budgets?.memory, options.tokenBudgets?.memory);
103
+ }
104
+ if (options.sessionMemorySummary && options.sessionMemorySummary.trim().length > 0) {
105
+ addMessageLayer({
106
+ id: 'session',
107
+ source: 'latest session memory summary',
108
+ messages,
109
+ layers,
110
+ layerMessages: [
111
+ {
112
+ role: 'system',
113
+ content: applyLayerBudget(options.sessionMemorySummary, options.budgets?.session, options.tokenBudgets?.session, tokenEstimator, tokenEstimateProfile),
114
+ },
115
+ ],
116
+ budgetChars: options.budgets?.session,
117
+ budgetTokens: options.tokenBudgets?.session,
118
+ tokenEstimator,
119
+ tokenEstimateProfile,
120
+ });
121
+ }
122
+ else {
123
+ addTrace(layers, {
124
+ id: 'session',
125
+ source: 'session memory unavailable',
126
+ included: false,
127
+ messageCount: 0,
128
+ charCount: 0,
129
+ tokenCount: 0,
130
+ }, options.budgets?.session, options.tokenBudgets?.session);
131
+ }
132
+ if (options.compactSummary && options.compactSummary.trim().length > 0) {
133
+ addMessageLayer({
134
+ id: 'compact',
135
+ source: 'latest compact summary',
136
+ messages,
137
+ layers,
138
+ layerMessages: [
139
+ {
140
+ role: 'system',
141
+ content: applyLayerBudget(options.compactSummary, options.budgets?.compact, options.tokenBudgets?.compact, tokenEstimator, tokenEstimateProfile),
142
+ },
143
+ ],
144
+ budgetChars: options.budgets?.compact,
145
+ budgetTokens: options.tokenBudgets?.compact,
146
+ tokenEstimator,
147
+ tokenEstimateProfile,
148
+ });
149
+ }
150
+ else {
151
+ addTrace(layers, {
152
+ id: 'compact',
153
+ source: 'compact summary unavailable',
154
+ included: false,
155
+ messageCount: 0,
156
+ charCount: 0,
157
+ tokenCount: 0,
158
+ }, options.budgets?.compact, options.tokenBudgets?.compact);
159
+ }
160
+ addMessageLayer({
161
+ id: 'recent',
162
+ source: '.agent/sessions recent user/assistant transcript',
163
+ messages,
164
+ layers,
165
+ layerMessages: applyMessagesBudget(options.recentMessages ?? [], options.budgets?.recent, options.tokenBudgets?.recent, tokenEstimator, tokenEstimateProfile),
166
+ budgetChars: options.budgets?.recent,
167
+ budgetTokens: options.tokenBudgets?.recent,
168
+ tokenEstimator,
169
+ tokenEstimateProfile,
170
+ });
171
+ addMessageLayer({
172
+ id: 'current',
173
+ source: 'current user input',
174
+ messages,
175
+ layers,
176
+ layerMessages: [
177
+ {
178
+ role: 'user',
179
+ content: applyLayerBudget(options.currentUserMessage, options.budgets?.current, options.tokenBudgets?.current, tokenEstimator, tokenEstimateProfile),
180
+ },
181
+ ],
182
+ budgetChars: options.budgets?.current,
183
+ budgetTokens: options.tokenBudgets?.current,
184
+ tokenEstimator,
185
+ tokenEstimateProfile,
186
+ });
187
+ const totalChars = messages.reduce((sum, message) => sum + message.content.length, 0);
188
+ const totalTokens = estimateMessagesTokensWith(messages, tokenEstimator, tokenEstimateProfile);
189
+ const budgetTokens = options.effectiveInputBudgetTokens ?? options.contextWindowTokens;
190
+ const usedPercent = budgetTokens && budgetTokens > 0
191
+ ? totalTokens > 0
192
+ ? Math.max(1, Math.min(100, Math.ceil((totalTokens / budgetTokens) * 100)))
193
+ : 0
194
+ : undefined;
195
+ return {
196
+ messages,
197
+ trace: {
198
+ layers,
199
+ totalMessages: messages.length,
200
+ totalChars,
201
+ totalTokens,
202
+ ...(options.contextWindowTokens !== undefined ? { contextWindowTokens: options.contextWindowTokens } : {}),
203
+ ...(options.effectiveInputBudgetTokens !== undefined ? { effectiveInputBudgetTokens: options.effectiveInputBudgetTokens } : {}),
204
+ ...(options.autoCompactThresholdTokens !== undefined ? { autoCompactThresholdTokens: options.autoCompactThresholdTokens } : {}),
205
+ tokenEstimatorKind: options.tokenEstimatorKind ?? tokenEstimator.kind,
206
+ tokenEstimatorRequestedKind: options.tokenEstimatorRequestedKind ?? options.tokenEstimatorKind ?? tokenEstimator.kind,
207
+ tokenEstimateProfile,
208
+ ...(options.tokenEstimatorFallback !== undefined ? { tokenEstimatorFallback: options.tokenEstimatorFallback } : {}),
209
+ ...(options.tokenEstimatorFallbackReason !== undefined ? { tokenEstimatorFallbackReason: options.tokenEstimatorFallbackReason } : {}),
210
+ ...(usedPercent !== undefined ? { usedPercent, remainingPercent: 100 - usedPercent } : {}),
211
+ truncated: layers.some(layer => layer.budgetChars !== undefined && layer.charCount >= layer.budgetChars) ||
212
+ layers.some(layer => layer.budgetTokens !== undefined && layer.tokenCount >= layer.budgetTokens),
213
+ },
214
+ };
215
+ }
216
+ function addMessageLayer(options) {
217
+ const included = options.layerMessages.length > 0;
218
+ options.messages.push(...options.layerMessages);
219
+ const content = options.layerMessages.map(message => message.content).join('\n');
220
+ addTrace(options.layers, {
221
+ id: options.id,
222
+ source: options.source,
223
+ included,
224
+ messageCount: options.layerMessages.length,
225
+ charCount: content.length,
226
+ tokenCount: estimateTextTokens(content, options.tokenEstimator, options.tokenEstimateProfile),
227
+ }, options.budgetChars, options.budgetTokens);
228
+ }
229
+ function addTrace(layers, trace, budgetChars, budgetTokens) {
230
+ layers.push({
231
+ ...trace,
232
+ ...(budgetChars === undefined ? {} : { budgetChars }),
233
+ ...(budgetTokens === undefined ? {} : { budgetTokens }),
234
+ });
235
+ }
236
+ function formatMemoryContext(memories, budgetChars, budgetTokens, tokenEstimator, tokenEstimateProfile) {
237
+ if (memories.length === 0) {
238
+ return undefined;
239
+ }
240
+ const body = [
241
+ '## Relevant Long-Term Memory',
242
+ '',
243
+ 'Use these memories only when they are relevant to the current task. Explicit user instructions override memory.',
244
+ 'Memory records are point-in-time observations. Verify file paths, function names, flags, branch state, and other current-state claims against the live repo before acting on them.',
245
+ '',
246
+ ...memories.map(memory => [
247
+ `### ${memory.id} (${memory.type})`,
248
+ `Description: ${memory.description}`,
249
+ `Updated: ${memory.updatedAt}`,
250
+ `Reason: ${memory.reason}`,
251
+ '',
252
+ memory.body.trim(),
253
+ ].join('\n')),
254
+ ].join('\n\n');
255
+ return applyLayerBudget(body, budgetChars, budgetTokens, tokenEstimator, tokenEstimateProfile);
256
+ }
257
+ function applyLayerBudget(text, budgetChars, budgetTokens, tokenEstimator, tokenEstimateProfile) {
258
+ let result = text;
259
+ if (budgetChars !== undefined && result.length > budgetChars) {
260
+ result = truncateText(result, budgetChars, '[Context layer truncated.]');
261
+ }
262
+ if (budgetTokens !== undefined && estimateTextTokens(result, tokenEstimator, tokenEstimateProfile) > budgetTokens) {
263
+ result = truncateTextToTokenBudget(result, budgetTokens, tokenEstimator, tokenEstimateProfile);
264
+ }
265
+ return result;
266
+ }
267
+ function applyMessagesBudget(messages, budgetChars, budgetTokens, tokenEstimator, tokenEstimateProfile) {
268
+ if (messages.length === 0 || (budgetChars === undefined && budgetTokens === undefined)) {
269
+ return messages;
270
+ }
271
+ const selected = [];
272
+ let chars = 0;
273
+ let tokens = 0;
274
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
275
+ const message = messages[index];
276
+ if (!message) {
277
+ continue;
278
+ }
279
+ const messageChars = message.content.length;
280
+ const messageTokens = estimateMessageTokens(message, tokenEstimator, tokenEstimateProfile);
281
+ const wouldExceedChars = budgetChars !== undefined && chars + messageChars > budgetChars;
282
+ const wouldExceedTokens = budgetTokens !== undefined && tokens + messageTokens > budgetTokens;
283
+ if (wouldExceedChars || wouldExceedTokens) {
284
+ const remainingChars = budgetChars === undefined ? undefined : Math.max(0, budgetChars - chars);
285
+ const remainingTokens = budgetTokens === undefined ? undefined : Math.max(0, budgetTokens - tokens);
286
+ if (selected.length === 0 && (remainingChars === undefined || remainingChars > 64) && (remainingTokens === undefined || remainingTokens > 16)) {
287
+ selected.unshift({
288
+ ...message,
289
+ content: applyLayerBudget(message.content, remainingChars, remainingTokens, tokenEstimator, tokenEstimateProfile),
290
+ });
291
+ }
292
+ break;
293
+ }
294
+ selected.unshift(message);
295
+ chars += messageChars;
296
+ tokens += messageTokens;
297
+ }
298
+ return selected;
299
+ }
300
+ export function estimateMessagesTokens(messages) {
301
+ return estimateMessagesTokensWith(messages, defaultTokenEstimator, 'default');
302
+ }
303
+ export function estimateMessagesTokensWith(messages, tokenEstimator, profile) {
304
+ return messages.reduce((sum, message) => sum + estimateMessageTokens(message, tokenEstimator, profile), 0);
305
+ }
306
+ function estimateMessageTokens(message, tokenEstimator = defaultTokenEstimator, profile = 'default') {
307
+ return estimateTextTokens(`${message.role}\n${message.content}`, tokenEstimator, profile);
308
+ }
309
+ function estimateTextTokens(text, tokenEstimator = defaultTokenEstimator, profile = 'default') {
310
+ return tokenEstimator.estimateText({ text, profile }).tokens;
311
+ }
312
+ function truncateTextToTokenBudget(text, budgetTokens, tokenEstimator = defaultTokenEstimator, profile = 'default') {
313
+ if (budgetTokens <= 0) {
314
+ return '';
315
+ }
316
+ if (estimateTextTokens(text, tokenEstimator, profile) <= budgetTokens) {
317
+ return text;
318
+ }
319
+ let low = 0;
320
+ let high = text.length;
321
+ while (low < high) {
322
+ const mid = Math.floor((low + high + 1) / 2);
323
+ const candidate = truncateText(text, mid, '[Context layer truncated.]');
324
+ if (estimateTextTokens(candidate, tokenEstimator, profile) <= budgetTokens) {
325
+ low = mid;
326
+ }
327
+ else {
328
+ high = mid - 1;
329
+ }
330
+ }
331
+ return truncateText(text, low, '[Context layer truncated.]');
332
+ }
333
+ function truncateText(text, maxLength, marker) {
334
+ if (maxLength <= 0) {
335
+ return '';
336
+ }
337
+ if (text.length <= maxLength) {
338
+ return text;
339
+ }
340
+ if (maxLength <= marker.length + 1) {
341
+ return text.slice(0, maxLength);
342
+ }
343
+ return `${text.slice(0, maxLength - marker.length - 1)}\n${marker}`;
344
+ }