@musnows/scriverse 0.8.3 → 0.8.5

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.
@@ -1,16 +1,19 @@
1
1
  export const AI_STREAM_IDLE_TIMEOUT_SECONDS_ENV = "SCRIVERSE_AI_STREAM_IDLE_TIMEOUT_SECONDS";
2
- export const DEFAULT_AI_STREAM_IDLE_TIMEOUT_SECONDS = 30;
3
- export const MIN_AI_STREAM_IDLE_TIMEOUT_SECONDS = 10;
4
- export const MAX_AI_STREAM_IDLE_TIMEOUT_SECONDS = 120;
2
+ export const DEFAULT_AI_STREAM_IDLE_TIMEOUT_SECONDS = 90;
3
+ export const MIN_AI_STREAM_IDLE_TIMEOUT_SECONDS = 30;
4
+ export const MAX_AI_STREAM_IDLE_TIMEOUT_SECONDS = 600;
5
5
  export const DEFAULT_AI_STREAM_IDLE_TIMEOUT_MS = DEFAULT_AI_STREAM_IDLE_TIMEOUT_SECONDS * 1_000;
6
+ export function normalizeAiStreamIdleTimeoutSeconds(value) {
7
+ if (!Number.isSafeInteger(value))
8
+ return DEFAULT_AI_STREAM_IDLE_TIMEOUT_SECONDS;
9
+ return Math.min(MAX_AI_STREAM_IDLE_TIMEOUT_SECONDS, Math.max(MIN_AI_STREAM_IDLE_TIMEOUT_SECONDS, value));
10
+ }
6
11
  export function resolveAiStreamIdleTimeoutSeconds(environment = process.env) {
7
12
  const raw = environment[AI_STREAM_IDLE_TIMEOUT_SECONDS_ENV]?.trim() ?? "";
8
13
  if (!/^\d+$/u.test(raw))
9
14
  return DEFAULT_AI_STREAM_IDLE_TIMEOUT_SECONDS;
10
15
  const configured = Number(raw);
11
- if (!Number.isSafeInteger(configured))
12
- return DEFAULT_AI_STREAM_IDLE_TIMEOUT_SECONDS;
13
- return Math.min(MAX_AI_STREAM_IDLE_TIMEOUT_SECONDS, Math.max(MIN_AI_STREAM_IDLE_TIMEOUT_SECONDS, configured));
16
+ return normalizeAiStreamIdleTimeoutSeconds(configured);
14
17
  }
15
18
  export function resolveAiStreamIdleTimeoutMs(environment = process.env) {
16
19
  return resolveAiStreamIdleTimeoutSeconds(environment) * 1_000;
@@ -1 +1 @@
1
- {"version":3,"file":"ai-stream-timeout.js","sourceRoot":"","sources":["../src/ai-stream-timeout.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,kCAAkC,GAAG,0CAA0C,CAAC;AAC7F,MAAM,CAAC,MAAM,sCAAsC,GAAG,EAAE,CAAC;AACzD,MAAM,CAAC,MAAM,kCAAkC,GAAG,EAAE,CAAC;AACrD,MAAM,CAAC,MAAM,kCAAkC,GAAG,GAAG,CAAC;AACtD,MAAM,CAAC,MAAM,iCAAiC,GAAG,sCAAsC,GAAG,KAAK,CAAC;AAEhG,MAAM,UAAU,iCAAiC,CAAC,cAAiC,OAAO,CAAC,GAAG;IAC5F,MAAM,GAAG,GAAG,WAAW,CAAC,kCAAkC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IAC1E,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,sCAAsC,CAAC;IACvE,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IAC/B,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,UAAU,CAAC;QAAE,OAAO,sCAAsC,CAAC;IACrF,OAAO,IAAI,CAAC,GAAG,CACb,kCAAkC,EAClC,IAAI,CAAC,GAAG,CAAC,kCAAkC,EAAE,UAAU,CAAC,CACzD,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,4BAA4B,CAAC,cAAiC,OAAO,CAAC,GAAG;IACvF,OAAO,iCAAiC,CAAC,WAAW,CAAC,GAAG,KAAK,CAAC;AAChE,CAAC"}
1
+ {"version":3,"file":"ai-stream-timeout.js","sourceRoot":"","sources":["../src/ai-stream-timeout.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,kCAAkC,GAAG,0CAA0C,CAAC;AAC7F,MAAM,CAAC,MAAM,sCAAsC,GAAG,EAAE,CAAC;AACzD,MAAM,CAAC,MAAM,kCAAkC,GAAG,EAAE,CAAC;AACrD,MAAM,CAAC,MAAM,kCAAkC,GAAG,GAAG,CAAC;AACtD,MAAM,CAAC,MAAM,iCAAiC,GAAG,sCAAsC,GAAG,KAAK,CAAC;AAEhG,MAAM,UAAU,mCAAmC,CAAC,KAAa;IAC/D,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC;QAAE,OAAO,sCAAsC,CAAC;IAChF,OAAO,IAAI,CAAC,GAAG,CACb,kCAAkC,EAClC,IAAI,CAAC,GAAG,CAAC,kCAAkC,EAAE,KAAK,CAAC,CACpD,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,iCAAiC,CAAC,cAAiC,OAAO,CAAC,GAAG;IAC5F,MAAM,GAAG,GAAG,WAAW,CAAC,kCAAkC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IAC1E,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,sCAAsC,CAAC;IACvE,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IAC/B,OAAO,mCAAmC,CAAC,UAAU,CAAC,CAAC;AACzD,CAAC;AAED,MAAM,UAAU,4BAA4B,CAAC,cAAiC,OAAO,CAAC,GAAG;IACvF,OAAO,iCAAiC,CAAC,WAAW,CAAC,GAAG,KAAK,CAAC;AAChE,CAAC"}
package/dist/ai.js CHANGED
@@ -3,7 +3,7 @@ import { buildCompletionRequestBody, isAiProviderProtocol, normalizeProviderBase
3
3
  import { AGENT_TOOL_RESULT_MAX_CHARS, DEFAULT_AGENT_TOOL_CALL_GLOBAL_MULTIPLIER, MIN_AGENT_TOOL_CALL_LIMIT, agentToolCallGlobalLimit, agentToolCallQuotaNoticeBudgetChars, agentToolCallQuotaUsedAfterCompact, agentToolCallSoftWarningThreshold, clampAgentToolCallGlobalMultiplier, paginateToolResultRecords, resolveMaxAgentToolCallLimit, shouldRejectAgentToolCalls, shouldRejectGlobalToolCalls, structuralToolResultRecords, withAgentToolCallQuotaNotice } from "./ai-tool-results.js";
4
4
  import { AiConnectivityTestGate, hashAiConnectivityConfiguration } from "./ai-connectivity-test.js";
5
5
  import { aiHttpRetryCount, aiHttpRetryDelayMs, normalizeAiRetryPolicy } from "./ai-retry.js";
6
- import { DEFAULT_AI_STREAM_IDLE_TIMEOUT_MS } from "./ai-stream-timeout.js";
6
+ import { DEFAULT_AI_STREAM_IDLE_TIMEOUT_MS, normalizeAiStreamIdleTimeoutSeconds } from "./ai-stream-timeout.js";
7
7
  import { characterExtractionHash, characterExtractionSelectionFingerprint, editableCharacterExtractionCandidate, normalizeCharacterExtractionCandidate, parseStoredCharacterExtractionCandidates } from "./character-extraction.js";
8
8
  import { PLATFORM_AI_WORK_ID } from "./database.js";
9
9
  import { AppError, notFound } from "./errors.js";
@@ -284,7 +284,7 @@ function thinkingParameters(provider, model) {
284
284
  return { thinking: { type: thinkingEnabled ? "enabled" : "disabled" }, ...effortParameters };
285
285
  }
286
286
  const CONFIGURED_AGENT_TOOL_IDS = ["story_index", "read_chapters", "grep", "search_story_entities", "read_character_sections", "search_drafts", "image", "calculate_time"];
287
- const AGENT_TOOL_IDS = [...CONFIGURED_AGENT_TOOL_IDS, "recall_self", "recall_relationship"];
287
+ const AGENT_TOOL_IDS = [...CONFIGURED_AGENT_TOOL_IDS, "recall_self", "recall_relationship", "recall_story"];
288
288
  const AGENT_TOOL_READ_MODULES = {
289
289
  story_index: ["prose"],
290
290
  read_chapters: ["prose"],
@@ -595,6 +595,14 @@ const AGENT_TOOL_DEFINITIONS = {
595
595
  parameters: { type: "object", properties: { characters: { type: "array", items: { type: "string", minLength: 1, maxLength: 200 }, maxItems: 20, default: [], description: "可选的对方角色姓名、别名或角色 ID 列表;留空时只列出有关系的角色。" }, cursor: agentToolCursorParameter }, additionalProperties: false }
596
596
  }
597
597
  },
598
+ recall_story: {
599
+ type: "function",
600
+ function: {
601
+ name: "recall_story",
602
+ description: "查询当前作品已保存正文中的关键词,返回匹配的完整段落及章节标题和 ID。用于回忆最近发生的故事情节、场景或原文措辞;只能读取当前正文,不会读取设定库或作者想法。",
603
+ parameters: { type: "object", properties: { keyword: { type: "string", minLength: 1, maxLength: 200 }, limit: { type: "integer", minimum: 1, maximum: 100, default: 20 }, cursor: agentToolCursorParameter }, required: ["keyword"], additionalProperties: false }
604
+ }
605
+ },
598
606
  calculate_time: {
599
607
  type: "function",
600
608
  function: {
@@ -1695,6 +1703,12 @@ export class AiManager {
1695
1703
  backoffRetryCount: this.retryPolicy.backoffRetryCount
1696
1704
  });
1697
1705
  }
1706
+ setInteractiveStreamIdleTimeoutSeconds(seconds) {
1707
+ this.interactiveStreamIdleTimeoutMs = normalizeAiStreamIdleTimeoutSeconds(seconds) * 1_000;
1708
+ logger.info("ai.manager.stream_idle_timeout_updated", {
1709
+ interactiveStreamIdleTimeoutMs: this.interactiveStreamIdleTimeoutMs
1710
+ });
1711
+ }
1698
1712
  getPlatformTokenUsage(timezoneOffset) {
1699
1713
  return this.getTokenUsage(null, timezoneOffset, true);
1700
1714
  }
@@ -2830,7 +2844,13 @@ export class AiManager {
2830
2844
  return this.getModel(modelId);
2831
2845
  }
2832
2846
  deleteModel(modelId) {
2833
- this.getModelRow(modelId);
2847
+ const model = this.getModelRow(modelId);
2848
+ const providerId = stringValue(model, "provider_id");
2849
+ this.store.audit(PLATFORM_AI_WORK_ID, "model.deleted", "model", modelId, {
2850
+ providerId,
2851
+ modelId: stringValue(model, "model_id"),
2852
+ displayName: stringValue(model, "display_name")
2853
+ });
2834
2854
  this.store.db.transaction(() => {
2835
2855
  this.clearImageToolModelReferences(modelId);
2836
2856
  this.store.db.run("DELETE FROM models WHERE id = ?", modelId);
@@ -4387,6 +4407,7 @@ export class AiManager {
4387
4407
  ...(enabledToolIds.includes("calculate_time") ? ["涉及日期差值或从日期推算目标日期时,使用 calculate_time;不要凭记忆估算日期。"] : []),
4388
4408
  "当回应涉及角色自身的身份、经历、所见所闻或记忆,而角色卡与对话历史不足以确定时,使用 recall_self 回忆;它不能指定或查询其他角色。",
4389
4409
  ...(enabledToolIds.includes("recall_relationship") ? ["当回应涉及当前角色与其他角色的关系、关系类型、状态或相处经历,而角色卡与对话历史不足以确定时,使用 recall_relationship;先不传 characters 获取有关系的角色列表,再传入 characters 数组获取一个或多个指定角色的关系详情。它只能查询当前角色参与的关系,不能查询两个其他角色之间的关系。"] : []),
4410
+ ...(enabledToolIds.includes("recall_story") ? ["当回应涉及已经写入故事的近期情节、场景或具体措辞,而角色自身记忆与对话历史不足以确定时,使用 recall_story 按关键词查询当前正文。"] : []),
4390
4411
  "把返回内容自然地当作角色自己的记忆、认知或感受来表达。没有返回的信息就以符合角色的方式表现为不知道、没见过、记不清或不确定,不得补用全知信息。"
4391
4412
  ].join("\n")
4392
4413
  : enabledToolIds.length > 0
@@ -4668,6 +4689,9 @@ export class AiManager {
4668
4689
  if (canReadWorkModule(permissions, "relationships") && (!requested || requested.has("recall_relationship"))) {
4669
4690
  roleplayTools.push("recall_relationship");
4670
4691
  }
4692
+ if (canReadWorkModule(permissions, "prose") && (!requested || requested.has("recall_story"))) {
4693
+ roleplayTools.push("recall_story");
4694
+ }
4671
4695
  roleplayTools.push("calculate_time");
4672
4696
  return roleplayTools;
4673
4697
  }
@@ -4843,8 +4867,9 @@ export class AiManager {
4843
4867
  : name === "image" ? imageArguments
4844
4868
  : name === "recall_self" ? recallSelfArguments
4845
4869
  : name === "recall_relationship" ? recallRelationshipArguments
4846
- : name === "calculate_time" ? calculateTimeArguments
4847
- : null;
4870
+ : name === "recall_story" ? grepArguments
4871
+ : name === "calculate_time" ? calculateTimeArguments
4872
+ : null;
4848
4873
  const toolId = AGENT_TOOL_IDS.includes(name) ? name : null;
4849
4874
  const enabledTools = allowedToolIds ?? new Set(this.store.getWorkAiSettings(workId).agentTools
4850
4875
  .filter((item) => typeof item === "string" && AGENT_TOOL_IDS.includes(item)));
@@ -4856,6 +4881,7 @@ export class AiManager {
4856
4881
  ? (toolId === "calculate_time" && enabledTools.has(toolId))
4857
4882
  || (toolId === "recall_self" && enabledTools.has(toolId) && canReadWorkModule(permissions, "characters"))
4858
4883
  || (toolId === "recall_relationship" && enabledTools.has(toolId) && canReadWorkModule(permissions, "characters") && canReadWorkModule(permissions, "relationships"))
4884
+ || (toolId === "recall_story" && enabledTools.has(toolId) && canReadWorkModule(permissions, "prose"))
4859
4885
  : Boolean(configuredToolId && enabledTools.has(configuredToolId) && this.canReadWithAgentTool(permissions, configuredToolId));
4860
4886
  if (!schema || !toolId || !toolAvailable) {
4861
4887
  return {
@@ -5209,7 +5235,7 @@ export class AiManager {
5209
5235
  }), maximumResultChars);
5210
5236
  return { id: toolCall.id, name, calledAt, arguments: { chapterIds, include, ...(cursor > 0 ? { cursor } : {}) }, status: "completed", result };
5211
5237
  }
5212
- if (name === "grep") {
5238
+ if (name === "grep" || name === "recall_story") {
5213
5239
  const { keyword, limit, cursor } = args;
5214
5240
  const matches = this.store.searchChapterParagraphs(workId, keyword, limit, { excludeAuthorNotes: true })
5215
5241
  .filter((match) => !scopedChapterIds || scopedChapterIds.has(String(match.chapterId)));