@ykurilov/agent-chat-core 0.70.0 → 0.71.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @ykurilov/agent-chat-core
2
2
 
3
+ ## 0.71.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 8b8c651: Три вида поля Одуванчик: сжатое, обычное и расширенное. Настройки модели/провайдера/рассуждений из каталога, цена и метки, инструменты приложения, шторка телефона. Сфера и токены из опубликованных ds-react/ds-tokens. Необязательный договор скина v29, сохранение уровня ответа для подписи в истории.
8
+
9
+ ## 0.70.1
10
+
11
+ ### Patch Changes
12
+
13
+ - ba86a4a: Стандартный DeepSeek 4.1 Flash обновлён до 1017 у OpenRouter, Together и Fireworks. У OpenRouter обновлены цены и расписание пикового тарифа; id профиля сохранён.
14
+
3
15
  ## 0.70.0
4
16
 
5
17
  ### Minor Changes
@@ -5625,6 +5625,22 @@ function parseNestedJson(text, schema, tally) {
5625
5625
  return coerceBySchema(parsed.value, schema, tally);
5626
5626
  }
5627
5627
 
5628
+ // src/conversation/answer-reasoning.ts
5629
+ var ANSWER_REASONING_PART = "data-answer-reasoning";
5630
+ function answerReasoningOf(parts) {
5631
+ const part = parts.find((item) => item.type === ANSWER_REASONING_PART);
5632
+ if (!part || !("data" in part) || typeof part.data !== "object" || part.data === null) return void 0;
5633
+ const level = part.data.level;
5634
+ return typeof level === "string" ? level : void 0;
5635
+ }
5636
+ function withoutAnswerReasoning(parts) {
5637
+ return parts.some((part) => part.type === ANSWER_REASONING_PART) ? parts.filter((part) => part.type !== ANSWER_REASONING_PART) : parts;
5638
+ }
5639
+ function withAnswerReasoning(parts, level) {
5640
+ if (level === void 0) return parts;
5641
+ return [...withoutAnswerReasoning(parts), { type: ANSWER_REASONING_PART, data: { level } }];
5642
+ }
5643
+
5628
5644
  // src/agent-run/run-agent-turn.ts
5629
5645
  import { stepCountIs, streamText } from "ai";
5630
5646
 
@@ -7209,7 +7225,7 @@ async function runTurn(turnInput, emitRaw) {
7209
7225
  providerId = fallbackProviderIds[0];
7210
7226
  logError(`[\u0437\u0430\u043F\u0430\u0441\u043D\u043E\u0439 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440] \u0447\u0430\u0442=${input.chatId} \u043C\u043E\u0434\u0435\u043B\u044C=${modelId} \u0441=${input.providerId} \u043D\u0430=${providerId} \u043F\u0440\u0438\u0447\u0438\u043D\u0430=${primaryPaused} \u043E\u0441\u043D\u043E\u0432\u043D\u043E\u0439 \u043D\u0430 \u043F\u0430\u0443\u0437\u0435`);
7211
7227
  }
7212
- emit({ type: "start", messageId, messageMetadata: { modelId, providerId: input.providerId } });
7228
+ emit({ type: "start", messageId, messageMetadata: { modelId, providerId: input.providerId, ...input.reasoning !== void 0 && { reasoningLevel: input.reasoning } } });
7213
7229
  if (input.liveDemos && chatHasExternalContent(await storage.listMessages(input.chatId)) && externalContent.announce()) emitRaw(externalContentChunk());
7214
7230
  const silenceSetting = resolveSilenceSwitch(input.providerFallback);
7215
7231
  if (silenceSetting.onSilence === "restart-step-on-next" && silenceSetting.maxSwitches > 0) {
@@ -7485,7 +7501,7 @@ async function runTurn(turnInput, emitRaw) {
7485
7501
  }
7486
7502
  }
7487
7503
  for (const chunk of seam.flush()) emitNow(chunk);
7488
- if (!started) emit({ type: "start", messageId, messageMetadata: { modelId, providerId: input.providerId } });
7504
+ if (!started) emit({ type: "start", messageId, messageMetadata: { modelId, providerId: input.providerId, ...input.reasoning !== void 0 && { reasoningLevel: input.reasoning } } });
7489
7505
  if (input.waitingWidgets && !input.abortSignal.aborted && errors.length === 0) {
7490
7506
  for (const toolCallId of extraWidgetCallIds(await saver.snapshot(), input.waitingWidgets)) {
7491
7507
  emitNow({ type: "tool-output-error", toolCallId, errorText: ONE_QUESTION_AT_A_TIME });
@@ -7564,7 +7580,7 @@ async function runTurn(turnInput, emitRaw) {
7564
7580
  finished = await finishWithRetries(
7565
7581
  storage,
7566
7582
  messageId,
7567
- { status, parts, usage, reasoningDurationsMs, error, ...answeredByBackup, ...input.afterTurn && { afterTurn: "pending" } },
7583
+ { status, parts: withAnswerReasoning(parts, input.reasoning), usage, reasoningDurationsMs, error, ...answeredByBackup, ...input.afterTurn && { afterTurn: "pending" } },
7568
7584
  logError
7569
7585
  );
7570
7586
  } catch (failure) {
@@ -7622,7 +7638,7 @@ async function runTurn(turnInput, emitRaw) {
7622
7638
  if (input.onFinished) runFinishedHook(input, status, answeredProviderId);
7623
7639
  emitToClient({
7624
7640
  type: "message-metadata",
7625
- messageMetadata: { status, reasoningDurationsMs, ...titlesMetadata(parts), ...answeredByBackup, ...usage && { usage }, ...error && { error } }
7641
+ messageMetadata: { status, reasoningDurationsMs, ...input.reasoning !== void 0 && { reasoningLevel: input.reasoning }, ...titlesMetadata(parts), ...answeredByBackup, ...usage && { usage }, ...error && { error } }
7626
7642
  });
7627
7643
  if (status === "error") for (const chunk of errors) emitToClient(chunk);
7628
7644
  if (terminal.length === 0) terminal.push(status === "stopped" ? { type: "abort" } : { type: "finish" });
@@ -7721,7 +7737,7 @@ function readAnswerAttempt(input, attempt, opened, classify) {
7721
7737
  // Только id сообщения: SDK дописывает куски в ответ с этим id.
7722
7738
  ...originalMessage && { originalMessages: [originalMessage] },
7723
7739
  sendReasoning: true,
7724
- messageMetadata: ({ part }) => part.type === "start" ? { modelId: input.modelId, providerId: found.provider.id } : void 0,
7740
+ messageMetadata: ({ part }) => part.type === "start" ? { modelId: input.modelId, providerId: found.provider.id, ...input.reasoning !== void 0 && { reasoningLevel: input.reasoning } } : void 0,
7725
7741
  onError
7726
7742
  }).pipeThrough(
7727
7743
  new TransformStream({
@@ -9446,10 +9462,10 @@ var deepseek41FlashProfile = {
9446
9462
  displayName: "DeepSeek 4.1 Flash",
9447
9463
  contextWindowTokens: 1e6,
9448
9464
  acceptsImages: true,
9449
- // Картинку в результате инструмента видит у самого DeepSeek (проверено вживую 2026-10-05), у Together и Fireworks
9450
- // (вживую 2026-10-15: картинка в ответе инструмента и в сообщении человека — цвет назван верно; запись каталога
9451
- // Together «только текст» устарела) и через OpenRouter у всех хозяев, кроме запрещённых ниже: одни её не видят,
9452
- // другие отвергают запрос (замер всех хозяев модели в OpenRouter 2026-10-06, specs/research-openrouter-tool-images.md).
9465
+ // 1017: картинка в результате инструмента проверена у DeepSeek, OpenRouter и Fireworks 2026-10-22.
9466
+ // Together: новый id в каталоге; живой вызов вернул 402, отметка сохранена по замеру предыдущей версии.
9467
+ // Запрет хозяев ниже сохранён по ранним замерам (specs/research-openrouter-tool-images.md);
9468
+ // проверка обычного маршрута 1017 не заменяет отдельного замера каждого хозяина.
9453
9469
  toolResultImageProviders: ["deepseek", "together", "fireworks", "openrouter"],
9454
9470
  reasoning: { replayPastTurns: true, levels: ["off", "on"], defaultLevel: "on" },
9455
9471
  // Запрет стоит всегда, а не только при картинке: запрос одинаков в каждом ходе, кэш и выбранный хозяин не теряются.
@@ -9460,13 +9476,14 @@ var deepseek41FlashProfile = {
9460
9476
  // прогон 35 учеников «Мастера понятий» — каши 0 из 1036 ответов (было 2 случая). Уходит к Together и OpenRouter;
9461
9477
  // DeepSeek напрямую обрезки не принимает.
9462
9478
  sampling: { minP: 0.05 },
9479
+ // 1017: прямой DeepSeek обновляет alias сам; остальные провайдеры используют датированный id.
9463
9480
  providerModelIds: {
9464
9481
  deepseek: "deepseek-flash",
9465
- fireworks: "accounts/fireworks/models/deepseek-v4p1-flash",
9466
- together: "deepseek-ai/DeepSeek-V4.1-Flash",
9467
- openrouter: "deepseek/deepseek-v4.1-flash"
9482
+ fireworks: "accounts/fireworks/models/deepseek-v4p1-flash-1017",
9483
+ together: "deepseek-ai/DeepSeek-V4.1-Flash-1017",
9484
+ openrouter: "deepseek/deepseek-v4.1-flash-1017"
9468
9485
  },
9469
- // Цены: прайс-листы провайдеров на 2026-09-30, доллары за миллион токенов (specs/012-spending-limits/research.md).
9486
+ // Цены за миллион токенов: каталоги провайдеров 2026-10-22.
9470
9487
  pricing: {
9471
9488
  // DeepSeek: цена вне пика, в часы пик (будни 01–04 и 06–10 UTC, api-docs.deepseek.com, проверено 2026-10-05) всё
9472
9489
  // вдвое, включая попадания в кэш. Праздники Китая (тоже вне пика) не заданы: расход в них чуть завышается.
@@ -9485,9 +9502,23 @@ var deepseek41FlashProfile = {
9485
9502
  },
9486
9503
  fireworks: { inputPerMillion: 0.3, outputPerMillion: 1.2, cachedInputPerMillion: 6e-3 },
9487
9504
  together: { inputPerMillion: 0.3, outputPerMillion: 1.2, cachedInputPerMillion: 6e-3 },
9488
- // Платной записи в кэш у хозяев OpenRouter нет (ни `input_cache_write`, ни `input_cache_write_1h`, 2026-10-12): кэш
9505
+ // Платной записи в кэш у хозяев OpenRouter нет (ни `input_cache_write`, ни `input_cache_write_1h`, 2026-10-22): кэш
9489
9506
  // сам, записанное идёт по цене входа. Часовая цена — тоже цена входа: срок метки модель не читает.
9490
- openrouter: { inputPerMillion: 0.3, outputPerMillion: 1.2, cachedInputPerMillion: 6e-3, cacheWrite1hPerMillion: 0.3 }
9507
+ openrouter: {
9508
+ inputPerMillion: 0.15,
9509
+ outputPerMillion: 0.6,
9510
+ cachedInputPerMillion: 3e-3,
9511
+ cacheWrite1hPerMillion: 0.15,
9512
+ // Каталог OpenRouter pricing.overrides: то же расписание пика, что у прямого DeepSeek.
9513
+ peakHours: {
9514
+ timeZone: "UTC",
9515
+ multiplier: 2,
9516
+ windows: [
9517
+ { days: [1, 2, 3, 4, 5], from: "01:00", to: "04:00" },
9518
+ { days: [1, 2, 3, 4, 5], from: "06:00", to: "10:00" }
9519
+ ]
9520
+ }
9521
+ }
9491
9522
  }
9492
9523
  };
9493
9524
 
@@ -10818,6 +10849,10 @@ function buildModelMenu(catalog, env, defaultChoice) {
10818
10849
  models: catalog.models.filter((model) => model.providerModelIds[provider.id]).map((model) => ({
10819
10850
  id: model.id,
10820
10851
  displayName: model.displayName,
10852
+ ...model.menu?.group !== void 0 && { group: model.menu.group },
10853
+ ...model.menu?.description !== void 0 && { description: model.menu.description },
10854
+ ...model.menu?.tags !== void 0 && { tags: [...model.menu.tags] },
10855
+ ...model.pricing?.[provider.id] && { price: { inputPerMillion: model.pricing[provider.id].inputPerMillion, outputPerMillion: model.pricing[provider.id].outputPerMillion } },
10821
10856
  acceptsImages: modelAcceptsImages(model, provider.id),
10822
10857
  reasoning: {
10823
10858
  levels: [...model.reasoning.levels ?? []],
@@ -11413,13 +11448,15 @@ function withRetries(model, context) {
11413
11448
  function toAgentChatMessage(message) {
11414
11449
  if (message.role === "user") return { id: message.id, role: "user", parts: message.parts };
11415
11450
  const { pendingSteps: _steps, ...usage } = message.usage ?? {};
11451
+ const reasoningLevel = answerReasoningOf(message.parts);
11416
11452
  const thoughtTitles = thoughtTitlesOfParts(message.parts);
11417
11453
  return {
11418
11454
  id: message.id,
11419
11455
  role: "assistant",
11420
- parts: message.parts,
11456
+ parts: withoutAnswerReasoning(message.parts),
11421
11457
  metadata: {
11422
11458
  status: message.status,
11459
+ ...reasoningLevel !== void 0 && { reasoningLevel },
11423
11460
  reasoningDurationsMs: message.reasoningDurationsMs,
11424
11461
  ...Object.keys(thoughtTitles).length > 0 && { thoughtTitles },
11425
11462
  ...message.modelId && { modelId: message.modelId },
@@ -12224,7 +12261,7 @@ var SHOWS_EVERYTHING = {
12224
12261
  visibleChunks: (stream) => stream,
12225
12262
  visibleCompactions: (markers) => markers
12226
12263
  };
12227
- var MODEL_METADATA_KEYS = ["modelId", "providerId", "usage"];
12264
+ var MODEL_METADATA_KEYS = ["modelId", "providerId", "reasoningLevel", "usage"];
12228
12265
  var MODEL_CHAT_KEYS = ["ownerId", "modelId", "providerId", "reasoning"];
12229
12266
  var withoutErrorDetails = (metadata) => {
12230
12267
  const error = metadata.error;
@@ -12290,6 +12327,7 @@ function clientVisibilityOf(capabilities, sdkScreenTool, { thoughtTitles = false
12290
12327
  const shownTitles = {};
12291
12328
  let serverOrdinal = 0;
12292
12329
  const kept = message.parts.flatMap((part) => {
12330
+ if (part.type === ANSWER_REASONING_PART) return [];
12293
12331
  if (part.type === "reasoning" && placesThoughts) {
12294
12332
  const title = serverTitles[String(serverOrdinal++)];
12295
12333
  if (title === void 0) return [];
@@ -15111,6 +15149,7 @@ export {
15111
15149
  IMPORT_MAX_IMAGES,
15112
15150
  IMPORT_MAX_IMAGE_BYTES,
15113
15151
  InvalidModelChoiceError,
15152
+ buildModelMenu,
15114
15153
  BASIC_WIDGET_NAMES,
15115
15154
  SUGGESTIONS_SHOWN,
15116
15155
  defineWidget,
@@ -15128,4 +15167,4 @@ export {
15128
15167
  InvalidWidgetAnswerError,
15129
15168
  createChatService
15130
15169
  };
15131
- //# sourceMappingURL=chunk-EW3572B5.js.map
15170
+ //# sourceMappingURL=chunk-YMUK7P4S.js.map