@musnows/scriverse 1.0.12 → 1.1.1

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/dist/app.js CHANGED
@@ -14,6 +14,7 @@ import { AI_PROVIDER_PROTOCOL_OPTIONS, AI_PROVIDER_PROTOCOLS, AI_THINKING_TYPES,
14
14
  import { MAX_AI_ANALYSIS_TIMEOUT_SECONDS, MIN_AI_ANALYSIS_TIMEOUT_SECONDS } from "./ai-analysis-timeout.js";
15
15
  import { aiConversationExportContentDisposition, exportAiConversationMarkdown } from "./ai-conversation-export.js";
16
16
  import { DEFAULT_AI_CHAT_TAB_LIMIT } from "./ai-chat-tab-limit.js";
17
+ import { DEFAULT_CHAPTER_ANNOTATION_NOTE_MAX_LENGTH } from "./chapter-annotation-note.js";
17
18
  import { MAX_AI_STREAM_IDLE_TIMEOUT_SECONDS, MIN_AI_STREAM_IDLE_TIMEOUT_SECONDS, normalizeAiStreamIdleTimeoutSeconds } from "./ai-stream-timeout.js";
18
19
  import { AttachmentStorage } from "./attachment-storage.js";
19
20
  import { attachmentDownloadFileName, inlineContentDisposition } from "./attachment-download.js";
@@ -22,6 +23,7 @@ import { LiteLlmPriceCache } from "./ai-model-pricing.js";
22
23
  import { MIN_AGENT_TOOL_CALL_LIMIT, resolveMaxAgentToolCallLimit } from "./ai-tool-results.js";
23
24
  import { SEMANTIC_SOURCE_TYPES } from "./semantic-search.js";
24
25
  import { CHARACTER_EXTRACTION_MAX_ALIASES, CHARACTER_EXTRACTION_MAX_CANDIDATES, CHARACTER_EXTRACTION_MAX_IDENTITY_LENGTH, CHARACTER_EXTRACTION_MAX_NAME_LENGTH, CHARACTER_EXTRACTION_MAX_SPECIES_LENGTH } from "./character-extraction.js";
26
+ import { characterAttributesInputSchema, characterStateInputSchema } from "./character-structured-fields.js";
25
27
  import { AiWritePlanManager, AI_USER_QUESTION_STATUSES, AI_WRITE_PLAN_STATUSES, aiWriteToolDescriptions, aiWriteToolLabels, aiWriteToolsUpdateSchema, answerAiUserQuestionSchema, resolveAiWritePlanMaxOperations } from "./ai-write-plans.js";
26
28
  import { CredentialVault } from "./credential-vault.js";
27
29
  import { Database } from "./database.js";
@@ -314,9 +316,9 @@ const characterSchema = z.object({
314
316
  aliases: z.array(z.string().trim().min(1).max(200)).max(100).optional(),
315
317
  raceId: identifier.nullable().optional(),
316
318
  organizationIds: z.array(identifier).max(100).optional(),
317
- attributes: jsonObject.optional(),
319
+ attributes: characterAttributesInputSchema.optional(),
318
320
  profile: jsonObject.optional(),
319
- currentState: jsonObject.optional(),
321
+ currentState: characterStateInputSchema.optional(),
320
322
  lockedFields: optionalStrings,
321
323
  firstChapterId: identifier.nullable().optional()
322
324
  }).strict();
@@ -1281,6 +1283,7 @@ function redactVersionSnapshots(value, mapper) {
1281
1283
  export function createRuntime(options) {
1282
1284
  const uploadLimits = options.uploadLimits ?? DEFAULT_IMAGE_UPLOAD_LIMITS;
1283
1285
  const aiChatTabLimit = options.aiChatTabLimit ?? DEFAULT_AI_CHAT_TAB_LIMIT;
1286
+ const chapterAnnotationNoteMaxLength = options.chapterAnnotationNoteMaxLength ?? DEFAULT_CHAPTER_ANNOTATION_NOTE_MAX_LENGTH;
1284
1287
  const requestedAiStreamHeartbeatIntervalMs = Number(options.aiStreamHeartbeatIntervalMs);
1285
1288
  const aiStreamHeartbeatIntervalMs = Number.isFinite(requestedAiStreamHeartbeatIntervalMs)
1286
1289
  ? Math.max(1, Math.trunc(requestedAiStreamHeartbeatIntervalMs))
@@ -1452,7 +1455,7 @@ export function createRuntime(options) {
1452
1455
  auth,
1453
1456
  resolveAnalysisTask: (workId, input) => ai.resolveTaskInput(workId, input),
1454
1457
  startAnalysisTask: (workId, input) => store.createTask(workId, input)
1455
- });
1458
+ }, { chapterAnnotationNoteMaxLength });
1456
1459
  ai.attachWritePlanManager(aiWritePlanManager);
1457
1460
  const app = express();
1458
1461
  enforceCaseInsensitiveRouting(app);
@@ -1502,6 +1505,7 @@ export function createRuntime(options) {
1502
1505
  protocols: [...AI_PROVIDER_PROTOCOLS],
1503
1506
  development: options.developmentServer === true,
1504
1507
  aiChatTabLimit,
1508
+ chapterAnnotationNoteMaxLength,
1505
1509
  uploadLimits
1506
1510
  });
1507
1511
  });
@@ -2500,12 +2504,12 @@ export function createRuntime(options) {
2500
2504
  kind: z.enum(["note", "todo"]),
2501
2505
  startLine: z.number().int().positive(),
2502
2506
  endLine: z.number().int().positive(),
2503
- note: z.string().trim().min(1).max(2000)
2507
+ note: z.string().trim().min(1).max(chapterAnnotationNoteMaxLength)
2504
2508
  }).strict().refine((value) => value.endLine >= value.startLine, { message: "结束行不能早于开始行", path: ["endLine"] }), request.body);
2505
2509
  data(response, store.createChapterAnnotation(request.params.chapterId, input), 201);
2506
2510
  });
2507
2511
  app.patch("/api/chapter-annotations/:annotationId", (request, response) => {
2508
- const input = parse(z.object({ note: z.string().trim().min(1).max(2000).optional(), status: z.enum(["open", "resolved"]).optional(), expectedVersionNo: expectedVersionNoSchema }).strict().refine((value) => value.note !== undefined || value.status !== undefined, { message: "至少需要修改一项" }), request.body);
2512
+ const input = parse(z.object({ note: z.string().trim().min(1).max(chapterAnnotationNoteMaxLength).optional(), status: z.enum(["open", "resolved"]).optional(), expectedVersionNo: expectedVersionNoSchema }).strict().refine((value) => value.note !== undefined || value.status !== undefined, { message: "至少需要修改一项" }), request.body);
2509
2513
  const { expectedVersionNo, ...update } = input;
2510
2514
  data(response, store.updateChapterAnnotation(request.params.annotationId, update, expectedVersionNo));
2511
2515
  });
@@ -3571,6 +3575,19 @@ export function createRuntime(options) {
3571
3575
  const permissions = requestPermissions(request, String(conversation.workId));
3572
3576
  data(response, redactAiConversationMessage(message, permissions), 201);
3573
3577
  });
3578
+ app.post("/api/ai-conversations/:conversationId/steer", (request, response) => {
3579
+ const input = parse(z.object({
3580
+ content: nonEmpty.max(100_000)
3581
+ }).strict(), request.body ?? {});
3582
+ const conversation = store.getAiConversationSummary(request.params.conversationId);
3583
+ const workId = String(conversation.workId);
3584
+ const permissions = requestPermissions(request, workId);
3585
+ if (!canWriteWorkModule(permissions, "ai-chat")) {
3586
+ throw new AppError(403, "WORK_MODULE_WRITE_DENIED", "你没有使用创作助手的权限");
3587
+ }
3588
+ const entry = ai.enqueueSteer(workId, request.params.conversationId, input.content);
3589
+ data(response, { ...entry, status: "pending" }, 202);
3590
+ });
3574
3591
  app.post("/api/ai-conversations/:conversationId/context/prepare", async (request, response) => {
3575
3592
  const input = parse(z.object({
3576
3593
  modelId: identifier.optional(),
@@ -4068,6 +4085,7 @@ export function createRuntime(options) {
4068
4085
  if (!response.writableEnded && !response.destroyed)
4069
4086
  response.write(`event: ${event}\ndata: ${JSON.stringify(payload)}\n\n`);
4070
4087
  };
4088
+ let releaseSteer = () => undefined;
4071
4089
  try {
4072
4090
  if (!existingRequest) {
4073
4091
  preparedChatImageAttachments = await ai.prepareChatImageAttachments(request.params.workId, modelId, input.imageAttachmentIds ?? [], permissions);
@@ -4188,6 +4206,7 @@ export function createRuntime(options) {
4188
4206
  questionId: pendingQuestion.id
4189
4207
  });
4190
4208
  }
4209
+ releaseSteer = ai.steerMailbox.begin(conversationId);
4191
4210
  const result = await ai.createStreamingChat({
4192
4211
  workId: request.params.workId,
4193
4212
  instruction: resolvedInstruction,
@@ -4199,6 +4218,10 @@ export function createRuntime(options) {
4199
4218
  onToolCall: (toolCall, round) => sendEvent("tool_call", { ...toolCall, round }),
4200
4219
  onProcessStep: (step) => sendEvent("process_step", step),
4201
4220
  onContextCompacted: (event) => sendEvent("context_compacted", event),
4221
+ onSteer: (event) => sendEvent("steer", {
4222
+ message: redactAiConversationMessage(event.message, permissions),
4223
+ applied: true
4224
+ }),
4202
4225
  conversationId,
4203
4226
  excludeConversationMessageId: currentMessageId,
4204
4227
  ...(currentMessageId ? { assistantMessageRequestId: `assistant:${currentMessageId}` } : {}),
@@ -4255,6 +4278,7 @@ export function createRuntime(options) {
4255
4278
  }
4256
4279
  }
4257
4280
  finally {
4281
+ releaseSteer();
4258
4282
  stopStreamHeartbeat();
4259
4283
  if (streamRequestId && !streamRequestFinished && !stopping) {
4260
4284
  store.finishAiConversationStreamRequest(streamRequestId, "cancelled", "stream_closed");