@musnows/scriverse 0.7.4 → 0.7.6

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
@@ -664,13 +664,7 @@ async function sendEpub(response, archive, title, fallbackStem) {
664
664
  response.type(EPUB_MIME_TYPE);
665
665
  response.setHeader("Content-Disposition", epubContentDisposition(title, fallbackStem));
666
666
  response.setHeader("Cache-Control", "private, no-store");
667
- await pipeline(archive.generateNodeStream({
668
- type: "nodebuffer",
669
- // 逐条目压缩后再写入,确保首个 mimetype 本地头包含确定长度且没有额外字段。
670
- streamFiles: false,
671
- compression: "DEFLATE",
672
- compressionOptions: { level: 6 }
673
- }), response);
667
+ await pipeline(archive, response);
674
668
  }
675
669
  function parse(schema, value) {
676
670
  return schema.parse(value);
@@ -1386,7 +1380,7 @@ export function createRuntime(options) {
1386
1380
  });
1387
1381
  app.delete("/api/works/:workId", async (request, response) => {
1388
1382
  const input = parse(z.object({ expectedVersionNo: expectedVersionNoSchema }).strict(), request.body ?? {});
1389
- store.deleteWork(request.params.workId, input.expectedVersionNo);
1383
+ ai.deleteWork(request.params.workId, input.expectedVersionNo);
1390
1384
  noContent(response);
1391
1385
  });
1392
1386
  app.get("/api/works/:workId/cover", (request, response) => {
@@ -1584,7 +1578,21 @@ export function createRuntime(options) {
1584
1578
  data(response, pagination ? store.listChapterOutlinesPage(request.params.workId, pagination) : store.listChapterOutlines(request.params.workId));
1585
1579
  });
1586
1580
  app.get("/api/works/:workId/outline-board", (request, response) => {
1587
- data(response, store.getChapterOutlineBoard(request.params.workId));
1581
+ const query = parse(z.object({
1582
+ q: z.string().trim().max(200).default(""),
1583
+ volumeId: z.string().trim().max(200).default(""),
1584
+ outlineStatus: z.enum(["all", "empty", "draft", "ready", "completed"]).default("all"),
1585
+ foreshadowStatus: z.enum(["all", "none", "unresolved", "resolved", "abandoned"]).default("all"),
1586
+ sort: z.enum(["tree", "status", "foreshadows", "title"]).default("tree")
1587
+ }), request.query);
1588
+ const pagination = parsePagination(request.query) ?? { page: 1, limit: 30, offset: 0 };
1589
+ data(response, store.getChapterOutlineBoard(request.params.workId, {
1590
+ query: query.q,
1591
+ volumeId: query.volumeId,
1592
+ outlineStatus: query.outlineStatus,
1593
+ foreshadowStatus: query.foreshadowStatus,
1594
+ sort: query.sort
1595
+ }, pagination));
1588
1596
  });
1589
1597
  app.get("/api/chapters/:chapterId/outline", (request, response) => data(response, store.getChapterOutline(request.params.chapterId)));
1590
1598
  app.put("/api/chapters/:chapterId/outline", (request, response) => {
@@ -2367,8 +2375,9 @@ export function createRuntime(options) {
2367
2375
  modelId: identifier.optional(),
2368
2376
  scope: contextSchema,
2369
2377
  instruction: z.string().max(100_000).default(""),
2370
- citations: aiCitationsSchema.optional()
2371
- }), request.body ?? {});
2378
+ citations: aiCitationsSchema.optional(),
2379
+ ignoreContextWarning: z.boolean().optional()
2380
+ }).strict(), request.body ?? {});
2372
2381
  const conversation = store.getAiConversation(request.params.conversationId);
2373
2382
  data(response, await ai.prepareConversationContext({
2374
2383
  conversationId: request.params.conversationId,
@@ -2376,7 +2385,7 @@ export function createRuntime(options) {
2376
2385
  modelId: input.modelId,
2377
2386
  scope: input.scope,
2378
2387
  instruction: instructionWithCitations(input.instruction, input.citations ?? [])
2379
- }));
2388
+ }, { ignoreWarning: input.ignoreContextWarning === true }));
2380
2389
  });
2381
2390
  app.post("/api/ai-conversations/:conversationId/compact", async (request, response) => {
2382
2391
  const input = parse(z.object({ modelId: identifier.optional(), scope: contextSchema }), request.body);
@@ -2484,8 +2493,9 @@ export function createRuntime(options) {
2484
2493
  parameters: jsonObject.optional(),
2485
2494
  citations: aiCitationsSchema.optional(),
2486
2495
  conversationId: identifier.optional(),
2487
- currentMessageId: identifier.optional()
2488
- }), request.body);
2496
+ currentMessageId: identifier.optional(),
2497
+ ignoreContextWarning: z.boolean().optional()
2498
+ }).strict(), request.body);
2489
2499
  const providedIdempotencyKey = request.get("Idempotency-Key");
2490
2500
  const idempotencyKey = providedIdempotencyKey
2491
2501
  ? parse(idempotencyKeySchema, providedIdempotencyKey)
@@ -2520,6 +2530,8 @@ export function createRuntime(options) {
2520
2530
  let streamRequestId = null;
2521
2531
  let streamRequestFinished = false;
2522
2532
  let lastStreamLeaseTouchAt = Date.now();
2533
+ let preparedContext = null;
2534
+ let preparedConversation = null;
2523
2535
  const startStream = () => {
2524
2536
  if (response.headersSent)
2525
2537
  return;
@@ -2541,31 +2553,27 @@ export function createRuntime(options) {
2541
2553
  try {
2542
2554
  if (!existingRequest) {
2543
2555
  store.assertAiConversationStreamAvailable(conversationId);
2544
- const inspection = ai.inspectConversationContext({
2556
+ preparedContext = await ai.prepareConversationContext({
2545
2557
  conversationId,
2546
2558
  workId: request.params.workId,
2547
2559
  modelId: input.modelId,
2548
2560
  scope: input.scope,
2549
2561
  instruction: resolvedInstruction,
2550
2562
  excludeConversationMessageId: input.currentMessageId
2551
- });
2552
- if (inspection.action === "warn") {
2553
- const prepared = await ai.prepareConversationContext({
2554
- conversationId,
2555
- workId: request.params.workId,
2556
- modelId: input.modelId,
2557
- scope: input.scope,
2558
- instruction: resolvedInstruction,
2559
- excludeConversationMessageId: input.currentMessageId
2560
- });
2563
+ }, { ignoreWarning: input.ignoreContextWarning === true });
2564
+ preparedConversation = redactAiConversation(store.getAiConversationSummary(conversationId), permissions);
2565
+ if (preparedContext.action === "warn") {
2561
2566
  startStream();
2562
2567
  sendEvent("ready", { streaming: true, idempotencyKey });
2563
2568
  sendEvent("context", {
2564
- ...prepared,
2565
- conversation: redactAiConversation({
2566
- ...store.getAiConversationSummary(conversationId),
2567
- contextWarningPending: true
2568
- }, permissions)
2569
+ ...preparedContext,
2570
+ conversation: preparedConversation
2571
+ });
2572
+ sendEvent("complete", {
2573
+ warningOnly: true,
2574
+ conversationId,
2575
+ conversationTitle: conversation.title,
2576
+ contextUsage: preparedContext.usage
2569
2577
  });
2570
2578
  return;
2571
2579
  }
@@ -2630,24 +2638,15 @@ export function createRuntime(options) {
2630
2638
  }
2631
2639
  return;
2632
2640
  }
2641
+ if (preparedContext) {
2642
+ sendEvent("context", {
2643
+ ...preparedContext,
2644
+ conversation: preparedConversation
2645
+ });
2646
+ }
2633
2647
  const currentMessageId = String(begun.userMessage?.id ?? input.currentMessageId ?? "");
2634
2648
  if (begun.userMessage)
2635
2649
  sendEvent("user_message", { message: redactAiConversationMessage(begun.userMessage, permissions) });
2636
- const prepared = await ai.prepareConversationContext({
2637
- conversationId,
2638
- workId: request.params.workId,
2639
- modelId: input.modelId,
2640
- scope: input.scope,
2641
- instruction: resolvedInstruction,
2642
- excludeConversationMessageId: currentMessageId
2643
- }, { skipWarning: true });
2644
- sendEvent("context", {
2645
- ...prepared,
2646
- conversation: redactAiConversation({
2647
- ...store.getAiConversationSummary(conversationId),
2648
- contextWarningPending: prepared.action === "warn"
2649
- }, permissions)
2650
- });
2651
2650
  const suggestion = await ai.createStreamingChat({
2652
2651
  workId: request.params.workId,
2653
2652
  instruction: resolvedInstruction,