@yeaft/webchat-agent 0.1.508 → 0.1.509

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yeaft/webchat-agent",
3
- "version": "0.1.508",
3
+ "version": "0.1.509",
4
4
  "description": "Remote agent for Yeaft WebChat — connects worker machines to the central server",
5
5
  "main": "index.js",
6
6
  "type": "module",
package/unify/engine.js CHANGED
@@ -26,6 +26,7 @@ import { buildMemoryInjection } from './memory/layout.js';
26
26
  import { runStopHooks } from './stop-hooks.js';
27
27
  import { getThreadStore, MAIN_THREAD_ID } from './threads/store.js';
28
28
  import { pickEffort, parseEffortPrefix } from './effort.js';
29
+ import { normalizeEffort } from './models.js';
29
30
 
30
31
  /**
31
32
  * task-324 — Turn cap removed.
@@ -446,9 +447,12 @@ export class Engine {
446
447
 
447
448
  // task-327b: `/max` / `/high` / `/medium` / `/low` prefix override.
448
449
  // Explicit caller-supplied userEffort wins over the prefix.
450
+ // task-327c nit: defensively normalize caller-supplied userEffort BEFORE
451
+ // the merge, so an invalid caller value (e.g. 'ULTRA') does not shadow a
452
+ // valid prompt prefix.
449
453
  const parsed = parseEffortPrefix(prompt);
450
454
  const effectivePrompt = parsed.cleanedPrompt;
451
- const effectiveUserEffort = userEffort || parsed.effort || null;
455
+ const effectiveUserEffort = normalizeEffort(userEffort) || parsed.effort || null;
452
456
 
453
457
  // ─── task-325a: engine-owned AbortController ─────────────
454
458
  // We create our own controller for this query run so `engine.abort()`
@@ -297,8 +297,13 @@ export class AnthropicAdapter extends LLMAdapter {
297
297
 
298
298
  /**
299
299
  * Non-streaming call for side queries.
300
+ *
301
+ * task-327c: accepts `effort` for internal scenario-tagged calls
302
+ * (consolidate/dream/recall/light). Guards mirror stream() — unsupported
303
+ * models silently drop the param. max_tokens auto-widens to budget+1024
304
+ * when needed.
300
305
  */
301
- async call({ model, system, messages, maxTokens = 4096, signal }) {
306
+ async call({ model, system, messages, maxTokens = 4096, effort, signal }) {
302
307
  if (signal?.aborted) throw new LLMAbortError();
303
308
 
304
309
  const body = {
@@ -308,6 +313,20 @@ export class AnthropicAdapter extends LLMAdapter {
308
313
  messages: this.#translateMessages(messages),
309
314
  };
310
315
 
316
+ // task-327c: mirror stream()'s thinking injection for side queries.
317
+ const normEffort = normalizeEffort(effort);
318
+ if (thinkingV1Enabled() && normEffort) {
319
+ const cap = getThinkingCapability(model);
320
+ if (cap.supportsThinking && cap.thinkingProtocol === 'anthropic') {
321
+ const budget = thinkingBudgetForEffort(model, normEffort);
322
+ if (budget && budget > 0) {
323
+ const minMax = budget + 1024;
324
+ if (body.max_tokens < minMax) body.max_tokens = minMax;
325
+ body.thinking = { type: 'enabled', budget_tokens: budget };
326
+ }
327
+ }
328
+ }
329
+
311
330
  const response = await fetch(`${this.#baseUrl}/v1/messages`, {
312
331
  method: 'POST',
313
332
  headers: {
@@ -350,8 +350,12 @@ export class ChatCompletionsAdapter extends LLMAdapter {
350
350
 
351
351
  /**
352
352
  * Non-streaming call for side queries.
353
+ *
354
+ * task-327c: accepts `effort` for internal scenario-tagged calls
355
+ * (consolidate/dream/recall/light). Feature-flag + capability guards
356
+ * mirror stream() exactly; unsupported models silently drop the param.
353
357
  */
354
- async call({ model, system, messages, maxTokens = 4096, extraBody, signal }) {
358
+ async call({ model, system, messages, maxTokens = 4096, effort, extraBody, signal }) {
355
359
  if (signal?.aborted) throw new LLMAbortError();
356
360
 
357
361
  const body = {
@@ -360,6 +364,18 @@ export class ChatCompletionsAdapter extends LLMAdapter {
360
364
  ...this.#maxTokensBody(model, maxTokens),
361
365
  };
362
366
 
367
+ // task-327c: mirror stream()'s reasoning.effort injection for side queries.
368
+ const normEffort = normalizeEffort(effort);
369
+ if (thinkingV1Enabled() && normEffort) {
370
+ const cap = getThinkingCapability(model);
371
+ if (cap.supportsThinking && cap.thinkingProtocol === 'openai-reasoning') {
372
+ const reasoningEffort = mapEffortToOpenAIReasoning(normEffort);
373
+ if (reasoningEffort) {
374
+ body.reasoning = { effort: reasoningEffort };
375
+ }
376
+ }
377
+ }
378
+
363
379
  // extraBody allows callers to pass through any additional/override parameters
364
380
  if (extraBody) Object.assign(body, extraBody);
365
381
 
@@ -15,6 +15,7 @@
15
15
  */
16
16
 
17
17
  import { extractMemories } from './extract.js';
18
+ import { pickEffort } from '../effort.js';
18
19
 
19
20
  // ─── Constants ──────────────────────────────────────────────────
20
21
 
@@ -103,6 +104,11 @@ async function generateSummary(messages, adapter, config) {
103
104
  system,
104
105
  messages: [{ role: 'user', content: `Summarize this conversation:\n\n${conversation}` }],
105
106
  maxTokens: 1024,
107
+ // task-327c: consolidate is a high-complexity side-query; flag as
108
+ // 'max' effort so supported models use extended thinking / reasoning.
109
+ // Router/adapter silently drops the param for models that don't
110
+ // support thinking, or when UNIFY_THINKING_V1 is off.
111
+ effort: pickEffort({ scenario: 'consolidate' }),
106
112
  });
107
113
  return result.text.trim();
108
114
  } catch {
@@ -16,6 +16,7 @@ import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, unlink
16
16
  import { join } from 'path';
17
17
  import { scanEntries, findStaleEntries, findDuplicateGroups, summarizeScan } from './scan.js';
18
18
  import { MAX_ENTRIES } from './store.js';
19
+ import { pickEffort } from '../effort.js';
19
20
  import {
20
21
  ensureLayout,
21
22
  renderIndex,
@@ -399,6 +400,9 @@ async function llmCall(adapter, config, system, prompt) {
399
400
  system,
400
401
  messages: [{ role: 'user', content: prompt }],
401
402
  maxTokens: 4096,
403
+ // task-327c: dream is self-reflective memory maintenance — flag 'max'
404
+ // so supported models use the full thinking budget.
405
+ effort: pickEffort({ scenario: 'dream' }),
402
406
  });
403
407
 
404
408
  const text = result.text.trim();
@@ -761,6 +765,9 @@ Write the narrative as Markdown with:
761
765
  system,
762
766
  messages: [{ role: 'user', content: prompt }],
763
767
  maxTokens: 2048,
768
+ // task-327c: dream narrative synthesis — same 'max' tier as the
769
+ // dream phase above; both pass through dream's self-reflection loop.
770
+ effort: pickEffort({ scenario: 'dream' }),
764
771
  });
765
772
  const text = (result?.text || '').trim();
766
773
  if (!text) return null;
@@ -9,6 +9,7 @@
9
9
  */
10
10
 
11
11
  import { MEMORY_KINDS } from './store.js';
12
+ import { pickEffort } from '../effort.js';
12
13
 
13
14
  /**
14
15
  * Build the extraction prompt.
@@ -69,6 +70,9 @@ export async function extractMemories({ messages, adapter, config }) {
69
70
  system,
70
71
  messages: [{ role: 'user', content: extractionPrompt }],
71
72
  maxTokens: 2048,
73
+ // task-327c: extract runs inside the consolidate pipeline — the
74
+ // JSON-structured output benefits from the same 'max' thinking tier.
75
+ effort: pickEffort({ scenario: 'consolidate' }),
72
76
  });
73
77
 
74
78
  const text = result.text.trim();
@@ -14,6 +14,7 @@
14
14
  */
15
15
 
16
16
  import { createHash } from 'crypto';
17
+ import { pickEffort } from '../effort.js';
17
18
 
18
19
  // ─── Constants ──────────────────────────────────────────────────
19
20
 
@@ -148,6 +149,9 @@ Select the ${MAX_RECALL_RESULTS} most relevant entries. Return a JSON array of e
148
149
  system,
149
150
  messages,
150
151
  maxTokens: 512,
152
+ // task-327c: recall step-3 is a cheap classifier pass (pick N out of
153
+ // 15 candidates). Flag 'low' so supported models skip deep reasoning.
154
+ effort: pickEffort({ scenario: 'recall' }),
151
155
  });
152
156
 
153
157
  // Parse the JSON array from the response