ei-tui 0.5.0 → 0.5.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.
- package/package.json +1 -1
- package/src/core/AGENTS.md +2 -2
- package/src/core/context-utils.ts +3 -4
- package/src/core/handlers/human-matching.ts +33 -21
- package/src/core/llm-client.ts +119 -39
- package/src/core/orchestrators/human-extraction.ts +0 -5
- package/src/core/processor.ts +5 -0
- package/src/core/queue-manager.ts +4 -0
- package/src/core/queue-processor.ts +1 -0
- package/src/core/state/queue.ts +7 -0
- package/src/core/state-manager.ts +233 -4
- package/src/core/tools/index.ts +1 -1
- package/src/core/types/data-items.ts +3 -1
- package/src/core/types/entities.ts +21 -4
- package/src/integrations/claude-code/importer.ts +0 -1
- package/src/integrations/claude-code/types.ts +0 -1
- package/src/integrations/opencode/importer.ts +0 -1
- package/src/storage/merge.ts +47 -2
- package/tui/src/commands/dlq.ts +12 -4
- package/tui/src/commands/provider.tsx +110 -90
- package/tui/src/commands/queue.ts +11 -3
- package/tui/src/commands/settings.tsx +9 -17
- package/tui/src/components/ModelListOverlay.tsx +203 -0
- package/tui/src/components/PromptInput.tsx +0 -2
- package/tui/src/context/ei.tsx +7 -0
- package/tui/src/util/persona-editor.tsx +15 -12
- package/tui/src/util/provider-editor.tsx +23 -6
- package/tui/src/util/yaml-serializers.ts +255 -73
- package/src/core/model-context-windows.ts +0 -49
- package/tui/src/commands/model.ts +0 -47
package/package.json
CHANGED
package/src/core/AGENTS.md
CHANGED
|
@@ -53,10 +53,10 @@ Priority queue for LLM requests:
|
|
|
53
53
|
|
|
54
54
|
Multi-provider LLM abstraction layer:
|
|
55
55
|
- Handles requests to Anthropic, OpenAI, Bedrock, local models
|
|
56
|
-
- **Sets `max_tokens:
|
|
56
|
+
- **Sets `max_tokens: 8000`** by default (safe for most providers; users can configure higher per-model)
|
|
57
57
|
- Prevents unbounded generation (test showed timeout after 2min without limit)
|
|
58
58
|
- Local models silently clamp to their configured maximums
|
|
59
|
-
- Anthropic Opus 4 accepts
|
|
59
|
+
- Anthropic Opus 4 accepts up to 64K output (configure `max_output_tokens` on the model to unlock)
|
|
60
60
|
|
|
61
61
|
**JSON Response Parsing** (`parseJSONResponse()`):
|
|
62
62
|
- **Strategy 1**: Extract from markdown code blocks (```json)
|
|
@@ -23,11 +23,10 @@ export function filterMessagesForContext(
|
|
|
23
23
|
|
|
24
24
|
const msgMs = new Date(msg.timestamp).getTime();
|
|
25
25
|
|
|
26
|
-
if (
|
|
27
|
-
|
|
28
|
-
}
|
|
26
|
+
if (msgMs < windowStartMs) return false;
|
|
27
|
+
if (contextBoundary && msgMs < boundaryMs) return false;
|
|
29
28
|
|
|
30
|
-
return
|
|
29
|
+
return true;
|
|
31
30
|
});
|
|
32
31
|
}
|
|
33
32
|
|
|
@@ -282,8 +282,7 @@ function normalizeText(text: string): string {
|
|
|
282
282
|
.replace(/[\u2018\u2019\u0060\u00B4]/g, "'") // curly single, backtick, acute accent
|
|
283
283
|
.replace(/[\u2014\u2013\u2012]/g, '-') // em-dash, en-dash, figure dash
|
|
284
284
|
.replace(/\u00A0/g, ' ') // non-breaking space
|
|
285
|
-
.replace(/[\u2000-\u200F]/g, ' ') // unicode space variants
|
|
286
|
-
.replace(/\u2026|\.\.\./g, '\u2026'); // normalize both ellipsis forms → unicode ellipsis (1:1)
|
|
285
|
+
.replace(/[\u2000-\u200F]/g, ' '); // unicode space variants
|
|
287
286
|
}
|
|
288
287
|
|
|
289
288
|
function stripPunctuation(text: string): string {
|
|
@@ -297,31 +296,46 @@ function stripPunctuation(text: string): string {
|
|
|
297
296
|
.toLowerCase();
|
|
298
297
|
}
|
|
299
298
|
|
|
300
|
-
interface WordBoundaryMatch {
|
|
299
|
+
export interface WordBoundaryMatch {
|
|
301
300
|
start: number;
|
|
302
301
|
end: number;
|
|
303
302
|
text: string;
|
|
304
303
|
}
|
|
305
304
|
|
|
306
|
-
function
|
|
305
|
+
export function expandToWordBoundaries(text: string, start: number, end: number): WordBoundaryMatch {
|
|
306
|
+
// Only walk backward if start is mid-word (not already at a word boundary)
|
|
307
|
+
if (start > 0 && !/\s/.test(text[start]))
|
|
308
|
+
while (start > 0 && !/\s/.test(text[start - 1])) start--;
|
|
309
|
+
// Only walk forward if end is mid-word
|
|
310
|
+
if (end > 0 && !/\s/.test(text[end - 1]))
|
|
311
|
+
while (end < text.length && !/\s/.test(text[end])) end++;
|
|
312
|
+
return { start, end, text: text.slice(start, end) };
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
export function findQuoteByWords(quoteText: string, msgText: string): WordBoundaryMatch | null {
|
|
307
316
|
const strippedQuote = stripPunctuation(quoteText);
|
|
308
317
|
const quoteWords = strippedQuote.split(' ').filter(w => w.length > 0);
|
|
309
318
|
|
|
310
|
-
if (quoteWords.length <
|
|
319
|
+
if (quoteWords.length < 2) return null; // Too short to trust — require at least 2 words
|
|
311
320
|
|
|
312
|
-
// Build word token list from original message with original positions
|
|
321
|
+
// Build word token list from original message with original positions.
|
|
322
|
+
// Each \S+ token is re-split into sub-tokens (sharing the parent's start/end)
|
|
323
|
+
// so that contractions stripped by stripPunctuation (e.g. don't → "don t")
|
|
324
|
+
// align correctly with quoteWords which is also split on spaces.
|
|
313
325
|
const wordTokens: Array<{ word: string; start: number; end: number }> = [];
|
|
314
326
|
const wordRegex = /\S+/g;
|
|
315
327
|
let match: RegExpExecArray | null;
|
|
316
328
|
while ((match = wordRegex.exec(msgText)) !== null) {
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
329
|
+
const tokenStart = match.index;
|
|
330
|
+
const tokenEnd = match.index + match[0].length;
|
|
331
|
+
const stripped = stripPunctuation(match[0]);
|
|
332
|
+
const subWords = stripped.split(' ').filter(w => w.length > 0);
|
|
333
|
+
for (const sub of subWords) {
|
|
334
|
+
wordTokens.push({ word: sub, start: tokenStart, end: tokenEnd });
|
|
335
|
+
}
|
|
322
336
|
}
|
|
323
337
|
|
|
324
|
-
// Find contiguous sequence of
|
|
338
|
+
// Find contiguous sequence of word tokens matching the quote words
|
|
325
339
|
for (let i = 0; i <= wordTokens.length - quoteWords.length; i++) {
|
|
326
340
|
let allMatch = true;
|
|
327
341
|
for (let j = 0; j < quoteWords.length; j++) {
|
|
@@ -333,11 +347,7 @@ function findQuoteByWords(quoteText: string, msgText: string): WordBoundaryMatch
|
|
|
333
347
|
if (allMatch) {
|
|
334
348
|
const startToken = wordTokens[i];
|
|
335
349
|
const endToken = wordTokens[i + quoteWords.length - 1];
|
|
336
|
-
return
|
|
337
|
-
start: startToken.start,
|
|
338
|
-
end: endToken.end,
|
|
339
|
-
text: msgText.slice(startToken.start, endToken.end),
|
|
340
|
-
};
|
|
350
|
+
return expandToWordBoundaries(msgText, startToken.start, endToken.end);
|
|
341
351
|
}
|
|
342
352
|
}
|
|
343
353
|
|
|
@@ -370,9 +380,10 @@ async function validateAndStoreQuotes(
|
|
|
370
380
|
let matchLevel: string;
|
|
371
381
|
|
|
372
382
|
if (start !== -1) {
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
383
|
+
const expanded = expandToWordBoundaries(msgText, start, start + candidate.text.length);
|
|
384
|
+
matchStart = expanded.start;
|
|
385
|
+
matchEnd = expanded.end;
|
|
386
|
+
matchText = expanded.text;
|
|
376
387
|
matchLevel = "exact";
|
|
377
388
|
} else {
|
|
378
389
|
// Level 2: word-boundary fallback
|
|
@@ -440,7 +451,8 @@ async function validateAndStoreQuotes(
|
|
|
440
451
|
data_item_ids: [dataItemId],
|
|
441
452
|
persona_groups: [personaGroup || "General"],
|
|
442
453
|
text: matchText,
|
|
443
|
-
speaker: message.role === "human" ? "human" : personaName,
|
|
454
|
+
speaker: message.role === "human" ? "human" : (message.speaker_name ?? personaName),
|
|
455
|
+
channel: personaName,
|
|
444
456
|
timestamp: message.timestamp,
|
|
445
457
|
start: matchStart,
|
|
446
458
|
end: matchEnd,
|
package/src/core/llm-client.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type { ChatMessage, ProviderAccount } from "./types.js";
|
|
2
|
-
|
|
1
|
+
import type { ChatMessage, ProviderAccount, ModelConfig } from "./types.js";
|
|
2
|
+
const DEFAULT_TOKEN_LIMIT = 8192;
|
|
3
|
+
const DEFAULT_MAX_OUTPUT_TOKENS = 8000;
|
|
3
4
|
|
|
4
5
|
export interface ProviderConfig {
|
|
5
6
|
baseURL: string;
|
|
@@ -9,7 +10,7 @@ export interface ProviderConfig {
|
|
|
9
10
|
|
|
10
11
|
export interface ResolvedModel {
|
|
11
12
|
provider: string;
|
|
12
|
-
model: string;
|
|
13
|
+
model: string | undefined;
|
|
13
14
|
config: ProviderConfig;
|
|
14
15
|
extraHeaders?: Record<string, string>;
|
|
15
16
|
}
|
|
@@ -19,6 +20,8 @@ export interface LLMCallOptions {
|
|
|
19
20
|
temperature?: number;
|
|
20
21
|
/** OpenAI-compatible tools array. When present and non-empty, sent with tool_choice: "auto". */
|
|
21
22
|
tools?: Record<string, unknown>[];
|
|
23
|
+
/** Fire-and-forget callback invoked after a successful response to increment usage counters. */
|
|
24
|
+
onUsageUpdate?: (modelId: string, usage: { calls: number; tokens_in: number; tokens_out: number }) => void;
|
|
22
25
|
}
|
|
23
26
|
|
|
24
27
|
export interface LLMRawResponse {
|
|
@@ -43,27 +46,90 @@ let llmCallCount = 0;
|
|
|
43
46
|
|
|
44
47
|
|
|
45
48
|
|
|
49
|
+
function isGuid(str: string): boolean {
|
|
50
|
+
return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(str);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function buildResolvedModel(account: ProviderAccount, model: ModelConfig): ResolvedModel {
|
|
54
|
+
return {
|
|
55
|
+
provider: account.name,
|
|
56
|
+
model: model.name === "(default)" ? undefined : model.name,
|
|
57
|
+
config: {
|
|
58
|
+
name: account.name,
|
|
59
|
+
baseURL: account.url,
|
|
60
|
+
apiKey: account.api_key || "",
|
|
61
|
+
},
|
|
62
|
+
extraHeaders: account.extra_headers,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function resolveModelById(
|
|
67
|
+
modelId: string,
|
|
68
|
+
accounts: ProviderAccount[]
|
|
69
|
+
): { account: ProviderAccount; model: ModelConfig } | undefined {
|
|
70
|
+
for (const account of accounts) {
|
|
71
|
+
if (!account.enabled || account.type !== "llm") continue;
|
|
72
|
+
const model = account.models?.find((m) => m.id === modelId);
|
|
73
|
+
if (model) return { account, model };
|
|
74
|
+
}
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function getDisplayName(account: ProviderAccount, model: ModelConfig): string {
|
|
79
|
+
return `${account.name}:${model.name}`;
|
|
80
|
+
}
|
|
81
|
+
|
|
46
82
|
export function resolveModel(modelSpec?: string, accounts?: ProviderAccount[]): ResolvedModel {
|
|
47
83
|
if (!modelSpec) {
|
|
48
84
|
throw new Error("No model specified. Set a provider on this persona with /provider, or set a default_model in settings.");
|
|
49
85
|
}
|
|
86
|
+
|
|
87
|
+
if (accounts && isGuid(modelSpec)) {
|
|
88
|
+
const result = resolveModelById(modelSpec, accounts);
|
|
89
|
+
if (result) {
|
|
90
|
+
return buildResolvedModel(result.account, result.model);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const fallbackAccount = accounts.find((acc) => acc.enabled && acc.type === "llm" && acc.default_model);
|
|
94
|
+
if (fallbackAccount?.default_model) {
|
|
95
|
+
const fallbackResult = resolveModelById(fallbackAccount.default_model, accounts);
|
|
96
|
+
if (fallbackResult) {
|
|
97
|
+
return buildResolvedModel(fallbackResult.account, fallbackResult.model);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
throw new Error(
|
|
102
|
+
`Model "${modelSpec}" not found. It may have been deleted. Update this persona's model in settings.`
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
50
106
|
let provider = "";
|
|
51
107
|
let model = modelSpec;
|
|
52
|
-
|
|
108
|
+
|
|
53
109
|
if (modelSpec.includes(":")) {
|
|
54
110
|
const [p, ...rest] = modelSpec.split(":");
|
|
55
111
|
provider = p;
|
|
56
112
|
model = rest.join(":");
|
|
57
113
|
}
|
|
58
|
-
|
|
59
|
-
// Check both "provider:model" format AND bare account names
|
|
114
|
+
|
|
60
115
|
if (accounts) {
|
|
61
|
-
const searchName = provider || modelSpec;
|
|
116
|
+
const searchName = provider || modelSpec;
|
|
62
117
|
const matchingAccount = accounts.find(
|
|
63
118
|
(acc) => acc.name.toLowerCase() === searchName.toLowerCase() && acc.enabled && acc.type === "llm"
|
|
64
119
|
);
|
|
65
120
|
if (matchingAccount) {
|
|
66
|
-
|
|
121
|
+
const matchingModel = matchingAccount.models?.find((m) => m.name === model);
|
|
122
|
+
if (matchingModel) {
|
|
123
|
+
return buildResolvedModel(matchingAccount, matchingModel);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (!provider && matchingAccount.default_model && matchingAccount.models) {
|
|
127
|
+
const defaultModel = matchingAccount.models.find((m) => m.id === matchingAccount.default_model);
|
|
128
|
+
if (defaultModel) {
|
|
129
|
+
return buildResolvedModel(matchingAccount, defaultModel);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
67
133
|
const resolvedModel = provider ? model : (matchingAccount.default_model || model);
|
|
68
134
|
return {
|
|
69
135
|
provider: matchingAccount.name,
|
|
@@ -77,7 +143,7 @@ export function resolveModel(modelSpec?: string, accounts?: ProviderAccount[]):
|
|
|
77
143
|
};
|
|
78
144
|
}
|
|
79
145
|
}
|
|
80
|
-
|
|
146
|
+
|
|
81
147
|
throw new Error(
|
|
82
148
|
`No provider "${provider || modelSpec}" found. Create one with /provider new, or check that it's enabled.`
|
|
83
149
|
);
|
|
@@ -85,44 +151,48 @@ export function resolveModel(modelSpec?: string, accounts?: ProviderAccount[]):
|
|
|
85
151
|
|
|
86
152
|
const tokenLimitLoggedModels = new Set<string>();
|
|
87
153
|
|
|
154
|
+
function findModelAndAccount(
|
|
155
|
+
spec: string,
|
|
156
|
+
accounts: ProviderAccount[]
|
|
157
|
+
): { model: ModelConfig | undefined; account: ProviderAccount | undefined } {
|
|
158
|
+
if (spec.includes(":")) {
|
|
159
|
+
const [providerName, ...rest] = spec.split(":");
|
|
160
|
+
const modelName = rest.join(":");
|
|
161
|
+
const account = accounts.find(
|
|
162
|
+
(a) => a.name.toLowerCase() === providerName.toLowerCase() && a.enabled
|
|
163
|
+
);
|
|
164
|
+
const model = account?.models?.find((m) => m.name === modelName);
|
|
165
|
+
return { model, account };
|
|
166
|
+
}
|
|
167
|
+
for (const account of accounts) {
|
|
168
|
+
const model = account.models?.find((m) => m.id === spec);
|
|
169
|
+
if (model) return { model, account };
|
|
170
|
+
}
|
|
171
|
+
return { model: undefined, account: undefined };
|
|
172
|
+
}
|
|
173
|
+
|
|
88
174
|
export function resolveTokenLimit(
|
|
89
175
|
modelSpec?: string,
|
|
90
176
|
accounts?: ProviderAccount[]
|
|
91
177
|
): number {
|
|
92
178
|
const spec = modelSpec || "";
|
|
93
179
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if (spec.includes(":")) {
|
|
97
|
-
const [p, ...rest] = spec.split(":");
|
|
98
|
-
provider = p;
|
|
99
|
-
model = rest.join(":");
|
|
100
|
-
}
|
|
180
|
+
if (accounts && spec) {
|
|
181
|
+
const { model, account } = findModelAndAccount(spec, accounts);
|
|
101
182
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const matchingAccount = accounts.find(
|
|
106
|
-
(acc) => acc.name.toLowerCase() === searchName.toLowerCase() && acc.enabled
|
|
107
|
-
);
|
|
108
|
-
if (matchingAccount?.token_limit) {
|
|
109
|
-
logTokenLimit(model, "user-override", matchingAccount.token_limit);
|
|
110
|
-
return matchingAccount.token_limit;
|
|
183
|
+
if (model?.token_limit) {
|
|
184
|
+
logTokenLimit(spec, "model-config", model.token_limit);
|
|
185
|
+
return model.token_limit;
|
|
111
186
|
}
|
|
112
|
-
if (matchingAccount && !provider) {
|
|
113
|
-
model = matchingAccount.default_model || model;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
187
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
188
|
+
if (account?.token_limit) {
|
|
189
|
+
const displayName = spec.includes(":") ? spec.split(":").slice(1).join(":") : spec;
|
|
190
|
+
logTokenLimit(displayName, "user-override", account.token_limit);
|
|
191
|
+
return account.token_limit;
|
|
192
|
+
}
|
|
122
193
|
}
|
|
123
194
|
|
|
124
|
-
|
|
125
|
-
logTokenLimit(model, "default", DEFAULT_TOKEN_LIMIT);
|
|
195
|
+
logTokenLimit(spec, "default", DEFAULT_TOKEN_LIMIT);
|
|
126
196
|
return DEFAULT_TOKEN_LIMIT;
|
|
127
197
|
}
|
|
128
198
|
|
|
@@ -148,13 +218,16 @@ export async function callLLMRaw(
|
|
|
148
218
|
): Promise<LLMRawResponse> {
|
|
149
219
|
llmCallCount++;
|
|
150
220
|
|
|
151
|
-
const { signal, temperature = 0.7 } = options;
|
|
221
|
+
const { signal, temperature = 0.7, onUsageUpdate } = options;
|
|
152
222
|
|
|
153
223
|
if (signal?.aborted) {
|
|
154
224
|
throw new Error("LLM call aborted");
|
|
155
225
|
}
|
|
156
226
|
|
|
157
227
|
const { model, config, extraHeaders } = resolveModel(modelSpec, accounts);
|
|
228
|
+
const { model: modelConfig } = (accounts && modelSpec)
|
|
229
|
+
? findModelAndAccount(modelSpec, accounts)
|
|
230
|
+
: { model: undefined };
|
|
158
231
|
|
|
159
232
|
const chatMessages: ChatMessage[] = [
|
|
160
233
|
{ role: "system", content: systemPrompt },
|
|
@@ -186,10 +259,10 @@ export async function callLLMRaw(
|
|
|
186
259
|
}
|
|
187
260
|
|
|
188
261
|
const requestBody: Record<string, unknown> = {
|
|
189
|
-
model,
|
|
262
|
+
...(model !== undefined && { model }),
|
|
190
263
|
messages: finalMessages,
|
|
191
264
|
temperature,
|
|
192
|
-
max_tokens:
|
|
265
|
+
max_tokens: modelConfig?.max_output_tokens ?? DEFAULT_MAX_OUTPUT_TOKENS,
|
|
193
266
|
};
|
|
194
267
|
|
|
195
268
|
if (options.tools && options.tools.length > 0) {
|
|
@@ -210,6 +283,13 @@ export async function callLLMRaw(
|
|
|
210
283
|
}
|
|
211
284
|
|
|
212
285
|
const data = await response.json();
|
|
286
|
+
|
|
287
|
+
if (onUsageUpdate && modelConfig) {
|
|
288
|
+
const tokensIn = data.usage?.prompt_tokens ?? data.usage?.input_tokens ?? 0;
|
|
289
|
+
const tokensOut = data.usage?.completion_tokens ?? data.usage?.output_tokens ?? 0;
|
|
290
|
+
onUsageUpdate(modelConfig.id, { calls: 1, tokens_in: tokensIn, tokens_out: tokensOut });
|
|
291
|
+
}
|
|
292
|
+
|
|
213
293
|
const choice = data.choices?.[0];
|
|
214
294
|
|
|
215
295
|
const assistantMessage = choice?.message as Record<string, unknown> | undefined;
|
|
@@ -66,8 +66,6 @@ export interface ExtractionOptions {
|
|
|
66
66
|
ceremony_progress?: number;
|
|
67
67
|
/** Override model for extraction LLM calls */
|
|
68
68
|
extraction_model?: string;
|
|
69
|
-
/** Override token budget for chunking */
|
|
70
|
-
extraction_token_limit?: number;
|
|
71
69
|
/**
|
|
72
70
|
* Controls whether external (integration-imported) messages are included.
|
|
73
71
|
* - "exclude": skip messages where external === true
|
|
@@ -88,9 +86,6 @@ const EXTRACTION_BUDGET_RATIO = 0.75;
|
|
|
88
86
|
const MIN_EXTRACTION_TOKENS = 10000;
|
|
89
87
|
|
|
90
88
|
function getExtractionMaxTokens(state: StateManager, options?: ExtractionOptions): number {
|
|
91
|
-
if (options?.extraction_token_limit) {
|
|
92
|
-
return Math.max(MIN_EXTRACTION_TOKENS, Math.floor(options.extraction_token_limit * EXTRACTION_BUDGET_RATIO));
|
|
93
|
-
}
|
|
94
89
|
const human = state.getHuman();
|
|
95
90
|
const modelForTokenLimit = options?.extraction_model ?? human.settings?.default_model;
|
|
96
91
|
const tokenLimit = resolveTokenLimit(modelForTokenLimit, human.settings?.accounts);
|
package/src/core/processor.ts
CHANGED
|
@@ -107,6 +107,7 @@ import {
|
|
|
107
107
|
getQueueActiveItems,
|
|
108
108
|
getDLQItems,
|
|
109
109
|
updateQueueItem,
|
|
110
|
+
deleteQueueItems,
|
|
110
111
|
clearQueue,
|
|
111
112
|
submitOneShot,
|
|
112
113
|
} from "./queue-manager.js";
|
|
@@ -1931,6 +1932,10 @@ const toolNextSteps = new Set([
|
|
|
1931
1932
|
return updateQueueItem(this.stateManager, id, updates);
|
|
1932
1933
|
}
|
|
1933
1934
|
|
|
1935
|
+
deleteQueueItems(ids: string[]): number {
|
|
1936
|
+
return deleteQueueItems(this.stateManager, ids);
|
|
1937
|
+
}
|
|
1938
|
+
|
|
1934
1939
|
async clearQueue(): Promise<number> {
|
|
1935
1940
|
return clearQueue(this.stateManager, this.queueProcessor);
|
|
1936
1941
|
}
|
|
@@ -51,6 +51,10 @@ export function updateQueueItem(
|
|
|
51
51
|
return sm.queue_updateItem(id, updates);
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
export function deleteQueueItems(sm: StateManager, ids: string[]): number {
|
|
55
|
+
return sm.queue_deleteItems(ids);
|
|
56
|
+
}
|
|
57
|
+
|
|
54
58
|
export async function clearQueue(sm: StateManager, qp: QueueProcessor): Promise<number> {
|
|
55
59
|
qp.abort();
|
|
56
60
|
return sm.queue_clear();
|
|
@@ -542,6 +542,7 @@ export class QueueProcessor {
|
|
|
542
542
|
`be parsed as valid JSON. Please reformat it as the JSON object described in your ` +
|
|
543
543
|
`system instructions. Respond with ONLY the JSON object, or \`{}\` if no changes ` +
|
|
544
544
|
`are needed.\n\n---\n${malformedContent}\n---` +
|
|
545
|
+
`\n\nThe user does NOT know there was a problem - This request is from Ei to you to try to fix it for them.` +
|
|
545
546
|
`\n\n**CRITICAL INSTRUCTION** - DO NOT OMIT ANY DATA. You are this agent's last hope!`;
|
|
546
547
|
|
|
547
548
|
try {
|
package/src/core/state/queue.ts
CHANGED
|
@@ -158,6 +158,13 @@ export class QueueState {
|
|
|
158
158
|
return true;
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
+
deleteItems(ids: string[]): number {
|
|
162
|
+
const idSet = new Set(ids);
|
|
163
|
+
const before = this.queue.length;
|
|
164
|
+
this.queue = this.queue.filter(r => !idSet.has(r.id));
|
|
165
|
+
return before - this.queue.length;
|
|
166
|
+
}
|
|
167
|
+
|
|
161
168
|
trimDLQ(): number {
|
|
162
169
|
const dlqItems = this.queue.filter(r => r.state === "dlq");
|
|
163
170
|
const cutoff = new Date();
|