@luckydraw/cumulus 1.0.38 → 1.0.40
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/CHANGELOG.md +22 -0
- package/dist/gateway/adapters/webchat.d.ts +3 -0
- package/dist/gateway/adapters/webchat.d.ts.map +1 -1
- package/dist/gateway/adapters/webchat.js +84 -1
- package/dist/gateway/adapters/webchat.js.map +1 -1
- package/dist/gateway/config.d.ts +16 -2
- package/dist/gateway/config.d.ts.map +1 -1
- package/dist/gateway/config.js +12 -6
- package/dist/gateway/config.js.map +1 -1
- package/dist/gateway/daemon.d.ts +1 -0
- package/dist/gateway/daemon.d.ts.map +1 -1
- package/dist/gateway/daemon.js +4 -0
- package/dist/gateway/daemon.js.map +1 -1
- package/dist/gateway/one-shot-model.d.ts +92 -0
- package/dist/gateway/one-shot-model.d.ts.map +1 -0
- package/dist/gateway/one-shot-model.js +191 -0
- package/dist/gateway/one-shot-model.js.map +1 -0
- package/dist/gateway/server.d.ts +3 -0
- package/dist/gateway/server.d.ts.map +1 -1
- package/dist/gateway/server.js +4 -0
- package/dist/gateway/server.js.map +1 -1
- package/dist/gateway/stall-detection.d.ts +19 -50
- package/dist/gateway/stall-detection.d.ts.map +1 -1
- package/dist/gateway/stall-detection.js +27 -190
- package/dist/gateway/stall-detection.js.map +1 -1
- package/dist/gateway/static/widget.js +342 -35
- package/dist/gateway/tasks-refresh.d.ts +89 -0
- package/dist/gateway/tasks-refresh.d.ts.map +1 -0
- package/dist/gateway/tasks-refresh.js +224 -0
- package/dist/gateway/tasks-refresh.js.map +1 -0
- package/dist/gateway/tasks.d.ts +21 -1
- package/dist/gateway/tasks.d.ts.map +1 -1
- package/dist/gateway/tasks.js +62 -10
- package/dist/gateway/tasks.js.map +1 -1
- package/dist/gateway/worker.d.ts +86 -0
- package/dist/gateway/worker.d.ts.map +1 -0
- package/dist/gateway/worker.js +0 -0
- package/dist/gateway/worker.js.map +1 -0
- package/dist/lib/tasks-file.d.ts +23 -0
- package/dist/lib/tasks-file.d.ts.map +1 -1
- package/dist/lib/tasks-file.js +49 -0
- package/dist/lib/tasks-file.js.map +1 -1
- package/dist/lib/tasks-format.d.ts.map +1 -1
- package/dist/lib/tasks-format.js +12 -5
- package/dist/lib/tasks-format.js.map +1 -1
- package/package.json +1 -1
|
@@ -16,57 +16,17 @@
|
|
|
16
16
|
* the worse failure (Karl, 2026-08-30).
|
|
17
17
|
*
|
|
18
18
|
* The classifier runs on WHATEVER model the operator configured
|
|
19
|
-
* (`stallClassifierModel`), not just a Claude sub-model (task 165)
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* one-shot HTTP `chat()` through the provider the resolver picks. The prompt
|
|
24
|
-
* and the fail-closed contract are identical on both branches.
|
|
19
|
+
* (`stallClassifierModel`), not just a Claude sub-model (task 165), via
|
|
20
|
+
* `runOneShotModel` — the one way the gateway itself calls a model (task 179).
|
|
21
|
+
* This module owns the PROMPT and the fail-closed reading of the answer; which
|
|
22
|
+
* provider runs it, and how, lives in `one-shot-model.ts`.
|
|
25
23
|
*/
|
|
24
|
+
import { type OneShotModelContext } from './one-shot-model.js';
|
|
26
25
|
export declare function hasUncheckedTodo(text: string): boolean;
|
|
27
26
|
/** Last `n` sentences of a message — the classifier only needs the tail, not the whole thing. */
|
|
28
27
|
export declare function lastSentences(text: string, n?: number): string;
|
|
29
28
|
/** The single dangling sentence to quote back in the nudge message. */
|
|
30
29
|
export declare function extractDanglingSentence(text: string): string;
|
|
31
|
-
/**
|
|
32
|
-
* Everything the classifier needs to decide WHICH provider runs the configured
|
|
33
|
-
* model and HOW to reach it. Mirrors the fields `server.ts` already threads into
|
|
34
|
-
* every turn's `sendMessage`, so the classifier resolves its model exactly the way
|
|
35
|
-
* a turn does — no second notion of "how do I run a non-Claude model."
|
|
36
|
-
*/
|
|
37
|
-
export interface StallClassifierContext {
|
|
38
|
-
/** Direct-provider catalog (HF / OpenAI / custom entries). */
|
|
39
|
-
models?: Array<{
|
|
40
|
-
id: string;
|
|
41
|
-
provider?: string;
|
|
42
|
-
contextWindow?: number;
|
|
43
|
-
}>;
|
|
44
|
-
/** Claude CLI sub-model catalog (Opus / Sonnet / Haiku variants). */
|
|
45
|
-
claudeModels?: Array<{
|
|
46
|
-
id: string;
|
|
47
|
-
}>;
|
|
48
|
-
hfApiKey?: string;
|
|
49
|
-
openaiApiKey?: string;
|
|
50
|
-
customProviders?: Array<{
|
|
51
|
-
id: string;
|
|
52
|
-
baseUrl: string;
|
|
53
|
-
apiKey?: string;
|
|
54
|
-
}>;
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Resolve the configured stall-classifier model id against BOTH catalogs.
|
|
58
|
-
*
|
|
59
|
-
* An id naming a `claudeModels[]` entry runs as a Claude CLI sub-model; an id
|
|
60
|
-
* naming a `models[]` entry runs on that entry's direct provider (task 165). An
|
|
61
|
-
* id that names nothing in either (absent, blank, or stale after a catalog edit)
|
|
62
|
-
* falls back to `"haiku"` rather than failing closed, since the classifier is a
|
|
63
|
-
* best-effort fallback signal, not a correctness gate.
|
|
64
|
-
*/
|
|
65
|
-
export declare function resolveStallClassifierModel(stallClassifierModel: string | undefined, claudeModels?: Array<{
|
|
66
|
-
id: string;
|
|
67
|
-
}>, models?: Array<{
|
|
68
|
-
id: string;
|
|
69
|
-
}>): string;
|
|
70
30
|
export interface ClassifyStallOptions {
|
|
71
31
|
timeoutMs?: number;
|
|
72
32
|
/**
|
|
@@ -80,15 +40,24 @@ export interface ClassifyStallOptions {
|
|
|
80
40
|
* routed through `resolveModelProvider` and run on whatever provider that
|
|
81
41
|
* names — a Claude CLI sub-model or a direct-provider HTTP model (task 165).
|
|
82
42
|
*/
|
|
83
|
-
context?:
|
|
43
|
+
context?: OneShotModelContext;
|
|
84
44
|
}
|
|
85
45
|
/**
|
|
86
46
|
* Cheap-model classification for the "trails off with no `<todo>` at all" case.
|
|
87
47
|
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
48
|
+
* The provider branch lives in `runOneShotModel` (task 179); what stays here is
|
|
49
|
+
* the part that is the classifier's own — the prompt, the tail-of-message snippet
|
|
50
|
+
* it runs on, and the fail-closed reading of the answer.
|
|
51
|
+
*
|
|
52
|
+
* `reasoningEffort: 'low'` (task 166) because the classification is a trivial
|
|
53
|
+
* binary read of a few sentences and must not deliberate; `CLASSIFIER_MAX_TOKENS`
|
|
54
|
+
* (task 167) because on both HTTP providers that budget bounds REASONING +
|
|
55
|
+
* CONTENT together, so it has to hold the whole low-effort thinking pass before
|
|
56
|
+
* the one-word verdict can be emitted at all.
|
|
57
|
+
*
|
|
58
|
+
* Fails closed on every branch: a `null` answer (spawn failure, non-zero exit,
|
|
59
|
+
* timeout, provider throw) and a non-"YES" answer are both "no nudge", because
|
|
60
|
+
* prodding a thread that correctly finished is the worse failure.
|
|
92
61
|
*/
|
|
93
62
|
export declare function classifyStall(text: string, opts?: ClassifyStallOptions): Promise<boolean>;
|
|
94
63
|
/** Is the last message from the thread's own `<todo>`/classifier signals a stall? */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stall-detection.d.ts","sourceRoot":"","sources":["../../src/gateway/stall-detection.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"stall-detection.d.ts","sourceRoot":"","sources":["../../src/gateway/stall-detection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAmB,KAAK,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAMhF,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAUtD;AAkCD,iGAAiG;AACjG,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,SAAI,GAAG,MAAM,CAEzD;AAED,uEAAuE;AACvE,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAG5D;AA+BD,MAAM,WAAW,oBAAoB;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,OAAO,CAAC,EAAE,mBAAmB,CAAC;CAC/B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,aAAa,CACjC,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,oBAAyB,GAC9B,OAAO,CAAC,OAAO,CAAC,CAalB;AAED,qFAAqF;AACrF,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,oBAAyB,GAAG,OAAO,CAAC,OAAO,CAAC,CAG/F;AAID;;;;;;;;;;GAUG;AACH,MAAM,WAAW,aAAa;IAC5B,0EAA0E;IAC1E,QAAQ,EAAE,OAAO,CAAC;IAClB,oDAAoD;IACpD,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAMD;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC,GAAG,aAAa,CAKhB;AAID,6FAA6F;AAC7F,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED,qEAAqE;AACrE,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,cAAc,EAAE,GAAG,SAAS,EAClC,SAAS,EAAE,mBAAmB,EAAE,GAAG,SAAS,EAC5C,GAAG,GAAE,MAAmB,GACvB,MAAM,GAAG,IAAI,CAgBf"}
|
|
@@ -16,17 +16,12 @@
|
|
|
16
16
|
* the worse failure (Karl, 2026-08-30).
|
|
17
17
|
*
|
|
18
18
|
* The classifier runs on WHATEVER model the operator configured
|
|
19
|
-
* (`stallClassifierModel`), not just a Claude sub-model (task 165)
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* one-shot HTTP `chat()` through the provider the resolver picks. The prompt
|
|
24
|
-
* and the fail-closed contract are identical on both branches.
|
|
19
|
+
* (`stallClassifierModel`), not just a Claude sub-model (task 165), via
|
|
20
|
+
* `runOneShotModel` — the one way the gateway itself calls a model (task 179).
|
|
21
|
+
* This module owns the PROMPT and the fail-closed reading of the answer; which
|
|
22
|
+
* provider runs it, and how, lives in `one-shot-model.ts`.
|
|
25
23
|
*/
|
|
26
|
-
import {
|
|
27
|
-
import { resolveModelProvider } from '../lib/gateway.js';
|
|
28
|
-
import { HuggingFaceProvider } from '../lib/huggingface-provider.js';
|
|
29
|
-
import { OpenAIResponsesProvider } from '../lib/openai-responses-provider.js';
|
|
24
|
+
import { runOneShotModel } from './one-shot-model.js';
|
|
30
25
|
/** Last (authoritative, full-replacement) `<todo>` block still has an open `[ ]` item. */
|
|
31
26
|
const TODO_BLOCK_RE = /<todo>([\s\S]*?)<\/todo>/g;
|
|
32
27
|
const UNCHECKED_ITEM_RE = /\[\s\]/;
|
|
@@ -95,25 +90,6 @@ const STALL_CLASSIFIER_PROMPT = (snippet) => `You are checking whether an AI ass
|
|
|
95
90
|
`message reads as a completed thought, a finished answer, a question back to the user, an ` +
|
|
96
91
|
`offer awaiting the user's decision, or anything you are not certain about, answer NO.\n\n` +
|
|
97
92
|
`Respond with exactly one word: YES or NO.`;
|
|
98
|
-
/**
|
|
99
|
-
* Resolve the configured stall-classifier model id against BOTH catalogs.
|
|
100
|
-
*
|
|
101
|
-
* An id naming a `claudeModels[]` entry runs as a Claude CLI sub-model; an id
|
|
102
|
-
* naming a `models[]` entry runs on that entry's direct provider (task 165). An
|
|
103
|
-
* id that names nothing in either (absent, blank, or stale after a catalog edit)
|
|
104
|
-
* falls back to `"haiku"` rather than failing closed, since the classifier is a
|
|
105
|
-
* best-effort fallback signal, not a correctness gate.
|
|
106
|
-
*/
|
|
107
|
-
export function resolveStallClassifierModel(stallClassifierModel, claudeModels, models) {
|
|
108
|
-
const id = stallClassifierModel?.trim();
|
|
109
|
-
if (!id)
|
|
110
|
-
return 'haiku';
|
|
111
|
-
if (claudeModels?.some(entry => entry.id === id))
|
|
112
|
-
return id;
|
|
113
|
-
if (models?.some(entry => entry.id === id))
|
|
114
|
-
return id;
|
|
115
|
-
return 'haiku';
|
|
116
|
-
}
|
|
117
93
|
// ─── Classifier execution ───────────────────────────────────────────────────────
|
|
118
94
|
/**
|
|
119
95
|
* Output budget for the HTTP classifier call (task 167).
|
|
@@ -131,175 +107,36 @@ export function resolveStallClassifierModel(stallClassifierModel, claudeModels,
|
|
|
131
107
|
*/
|
|
132
108
|
const CLASSIFIER_MAX_TOKENS = 512;
|
|
133
109
|
/**
|
|
134
|
-
*
|
|
135
|
-
* full agentic turn. Fails closed: any error, non-zero exit, timeout, or
|
|
136
|
-
* non-"YES" response resolves to `false` (no nudge).
|
|
110
|
+
* Cheap-model classification for the "trails off with no `<todo>` at all" case.
|
|
137
111
|
*
|
|
138
|
-
* The
|
|
139
|
-
*
|
|
140
|
-
* it
|
|
141
|
-
* requires output-format=stream-json.`), so the classifier would fail closed on every
|
|
142
|
-
* call and the fallback signal would be dead. Plain text on stdin is the form that
|
|
143
|
-
* actually works and sidesteps ARG_MAX for long prompts.
|
|
144
|
-
*/
|
|
145
|
-
async function classifyViaClaudeCli(text, model, timeoutMs) {
|
|
146
|
-
const snippet = lastSentences(text, 6);
|
|
147
|
-
if (!snippet)
|
|
148
|
-
return false;
|
|
149
|
-
return new Promise(resolve => {
|
|
150
|
-
const args = ['--print', '--model', model];
|
|
151
|
-
const cleanEnv = Object.fromEntries(Object.entries(process.env).filter(([key]) => !key.startsWith('CLAUDE') && key !== 'CLAUDECODE'));
|
|
152
|
-
let claude;
|
|
153
|
-
try {
|
|
154
|
-
claude = spawn('claude', args, {
|
|
155
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
156
|
-
env: cleanEnv,
|
|
157
|
-
shell: process.platform === 'win32',
|
|
158
|
-
windowsHide: true,
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
catch {
|
|
162
|
-
resolve(false);
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
// Plain text on stdin (see doc comment) — no JSON envelope, no --input-format.
|
|
166
|
-
claude.stdin?.write(STALL_CLASSIFIER_PROMPT(snippet));
|
|
167
|
-
claude.stdin?.end();
|
|
168
|
-
let stdout = '';
|
|
169
|
-
let settled = false;
|
|
170
|
-
const finish = (result) => {
|
|
171
|
-
if (settled)
|
|
172
|
-
return;
|
|
173
|
-
settled = true;
|
|
174
|
-
clearTimeout(timer);
|
|
175
|
-
resolve(result);
|
|
176
|
-
};
|
|
177
|
-
const timer = setTimeout(() => {
|
|
178
|
-
claude.kill();
|
|
179
|
-
finish(false);
|
|
180
|
-
}, timeoutMs);
|
|
181
|
-
timer.unref?.();
|
|
182
|
-
claude.stdout?.on('data', (data) => {
|
|
183
|
-
stdout += data.toString();
|
|
184
|
-
});
|
|
185
|
-
claude.on('close', code => {
|
|
186
|
-
if (code !== 0) {
|
|
187
|
-
finish(false);
|
|
188
|
-
return;
|
|
189
|
-
}
|
|
190
|
-
finish(/^YES\b/i.test(stdout.trim()));
|
|
191
|
-
});
|
|
192
|
-
claude.on('error', () => finish(false));
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
/**
|
|
196
|
-
* The direct-provider branch (task 165): a one-shot HTTP `chat()` through the
|
|
197
|
-
* provider `resolveModelProvider` picked for the configured model. No tools, no
|
|
198
|
-
* system prompt beyond the classifier prompt. The answer is one word, but the
|
|
199
|
-
* `maxTokens` budget must be large enough to hold a reasoning model's thinking pass
|
|
200
|
-
* first (task 167). Fails closed exactly like the CLI branch: any throw, timeout, or
|
|
201
|
-
* non-"YES" response resolves to `false`.
|
|
112
|
+
* The provider branch lives in `runOneShotModel` (task 179); what stays here is
|
|
113
|
+
* the part that is the classifier's own — the prompt, the tail-of-message snippet
|
|
114
|
+
* it runs on, and the fail-closed reading of the answer.
|
|
202
115
|
*
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
*
|
|
206
|
-
*
|
|
207
|
-
*
|
|
208
|
-
* detector was structurally dead for reasoning models.
|
|
209
|
-
* - The verdict is parsed from the streamed `text` events, not `response.content`.
|
|
210
|
-
* Both HTTP providers emit `text` events for CONTENT only — reasoning is
|
|
211
|
-
* accumulated separately and never streamed as a `text` event. But
|
|
212
|
-
* `response.content` folds reasoning in as a last-resort text block
|
|
213
|
-
* (`fullText || reasoningText`), so reading the blocks can hand the parser the
|
|
214
|
-
* model's private scratch paper. The `text` events are the channel that is
|
|
215
|
-
* guaranteed to be the actual answer; if none ever arrive, the answer is empty
|
|
216
|
-
* and we fail closed, exactly as before.
|
|
116
|
+
* `reasoningEffort: 'low'` (task 166) because the classification is a trivial
|
|
117
|
+
* binary read of a few sentences and must not deliberate; `CLASSIFIER_MAX_TOKENS`
|
|
118
|
+
* (task 167) because on both HTTP providers that budget bounds REASONING +
|
|
119
|
+
* CONTENT together, so it has to hold the whole low-effort thinking pass before
|
|
120
|
+
* the one-word verdict can be emitted at all.
|
|
217
121
|
*
|
|
218
|
-
*
|
|
219
|
-
*
|
|
220
|
-
*
|
|
221
|
-
* model still ran out of budget mid-reasoning and emitted no `text` event. The budget
|
|
222
|
-
* had to grow to `CLASSIFIER_MAX_TOKENS` (512) to hold the thinking pass AND the
|
|
223
|
-
* verdict. (The 166 comment originally claimed capping effort at `low` was what let
|
|
224
|
-
* the model "finish thinking inside `maxTokens`" — it did not; the budget was the
|
|
225
|
-
* binding constraint all along.)
|
|
122
|
+
* Fails closed on every branch: a `null` answer (spawn failure, non-zero exit,
|
|
123
|
+
* timeout, provider throw) and a non-"YES" answer are both "no nudge", because
|
|
124
|
+
* prodding a thread that correctly finished is the worse failure.
|
|
226
125
|
*/
|
|
227
|
-
async function
|
|
126
|
+
export async function classifyStall(text, opts = {}) {
|
|
228
127
|
const snippet = lastSentences(text, 6);
|
|
229
128
|
if (!snippet)
|
|
230
129
|
return false;
|
|
231
|
-
const
|
|
232
|
-
|
|
233
|
-
:
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
let answer = '';
|
|
238
|
-
try {
|
|
239
|
-
await provider.chat({
|
|
240
|
-
model: modelId,
|
|
241
|
-
system: [],
|
|
242
|
-
messages: [{ role: 'user', content: STALL_CLASSIFIER_PROMPT(snippet) }],
|
|
243
|
-
tools: [],
|
|
244
|
-
maxTokens: CLASSIFIER_MAX_TOKENS,
|
|
245
|
-
reasoningEffort: 'low',
|
|
246
|
-
signal: controller.signal,
|
|
247
|
-
onEvent: event => {
|
|
248
|
-
if (event.type === 'text' && event.text)
|
|
249
|
-
answer += event.text;
|
|
250
|
-
},
|
|
251
|
-
});
|
|
252
|
-
return /^YES\b/i.test(answer.trim());
|
|
253
|
-
}
|
|
254
|
-
catch {
|
|
255
|
-
// Any provider error, abort, or malformed response fails closed to "no nudge".
|
|
256
|
-
return false;
|
|
257
|
-
}
|
|
258
|
-
finally {
|
|
259
|
-
clearTimeout(timer);
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
/**
|
|
263
|
-
* Cheap-model classification for the "trails off with no `<todo>` at all" case.
|
|
264
|
-
*
|
|
265
|
-
* Routes the configured model through `resolveModelProvider` and runs it on
|
|
266
|
-
* whatever provider that names: a Claude CLI sub-model as a one-off
|
|
267
|
-
* `claude --print --model <id>` call, any other catalog model as a one-shot
|
|
268
|
-
* HTTP `chat()` (task 165). Fails closed on every branch.
|
|
269
|
-
*/
|
|
270
|
-
export async function classifyStall(text, opts = {}) {
|
|
271
|
-
const timeoutMs = opts.timeoutMs ?? 10000;
|
|
272
|
-
const ctx = opts.context;
|
|
273
|
-
// No context: preserve the task-164 behavior — the configured id (or "haiku")
|
|
274
|
-
// runs directly as a Claude CLI sub-model. This is what unit tests and any
|
|
275
|
-
// caller without a catalog rely on.
|
|
276
|
-
if (!ctx) {
|
|
277
|
-
const model = opts.model?.trim() || 'haiku';
|
|
278
|
-
return classifyViaClaudeCli(text, model, timeoutMs);
|
|
279
|
-
}
|
|
280
|
-
const modelId = resolveStallClassifierModel(opts.model, ctx.claudeModels, ctx.models);
|
|
281
|
-
// A Claude CLI sub-model runs on the CLI — detected by membership in claudeModels[],
|
|
282
|
-
// plus the "haiku" fallback (a valid CLI alias). These are NEVER passed to
|
|
283
|
-
// resolveModelProvider: that resolver's contract is "claude" or a direct-provider id,
|
|
284
|
-
// and feeding it a Claude sub-model id would fall through to the HF branch and
|
|
285
|
-
// misroute a Claude model over HTTP when an HF key is present.
|
|
286
|
-
const isClaudeSub = modelId === 'haiku' || ctx.claudeModels?.some(entry => entry.id === modelId);
|
|
287
|
-
if (isClaudeSub) {
|
|
288
|
-
return classifyViaClaudeCli(text, modelId, timeoutMs);
|
|
289
|
-
}
|
|
290
|
-
// A direct-provider model: route through the same single source of truth every turn
|
|
291
|
-
// uses (task 118/147). No second notion of "how do I run a non-Claude model."
|
|
292
|
-
const resolved = resolveModelProvider(modelId, ctx.models, {
|
|
293
|
-
hfApiKey: ctx.hfApiKey,
|
|
294
|
-
openaiApiKey: ctx.openaiApiKey,
|
|
295
|
-
customProviders: ctx.customProviders,
|
|
130
|
+
const answer = await runOneShotModel(STALL_CLASSIFIER_PROMPT(snippet), {
|
|
131
|
+
model: opts.model,
|
|
132
|
+
context: opts.context,
|
|
133
|
+
timeoutMs: opts.timeoutMs ?? 10000,
|
|
134
|
+
maxTokens: CLASSIFIER_MAX_TOKENS,
|
|
135
|
+
reasoningEffort: 'low',
|
|
296
136
|
});
|
|
297
|
-
if (
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
return classifyViaClaudeCli(text, 'haiku', timeoutMs);
|
|
301
|
-
}
|
|
302
|
-
return classifyViaHttp(text, modelId, resolved, timeoutMs);
|
|
137
|
+
if (answer === null)
|
|
138
|
+
return false;
|
|
139
|
+
return /^YES\b/i.test(answer.trim());
|
|
303
140
|
}
|
|
304
141
|
/** Is the last message from the thread's own `<todo>`/classifier signals a stall? */
|
|
305
142
|
export async function isStalled(text, opts = {}) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stall-detection.js","sourceRoot":"","sources":["../../src/gateway/stall-detection.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"stall-detection.js","sourceRoot":"","sources":["../../src/gateway/stall-detection.ts"],"names":[],"mappings":"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"}
|