@actalk/inkos-core 0.3.4 → 0.3.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.
Files changed (99) hide show
  1. package/LICENSE +21 -0
  2. package/dist/__tests__/ai-tells.test.d.ts +2 -0
  3. package/dist/__tests__/ai-tells.test.d.ts.map +1 -0
  4. package/dist/__tests__/ai-tells.test.js +78 -0
  5. package/dist/__tests__/ai-tells.test.js.map +1 -0
  6. package/dist/__tests__/detection-insights.test.d.ts +2 -0
  7. package/dist/__tests__/detection-insights.test.d.ts.map +1 -0
  8. package/dist/__tests__/detection-insights.test.js +50 -0
  9. package/dist/__tests__/detection-insights.test.js.map +1 -0
  10. package/dist/__tests__/detector.test.d.ts +2 -0
  11. package/dist/__tests__/detector.test.d.ts.map +1 -0
  12. package/dist/__tests__/detector.test.js +71 -0
  13. package/dist/__tests__/detector.test.js.map +1 -0
  14. package/dist/__tests__/style-analyzer.test.d.ts +2 -0
  15. package/dist/__tests__/style-analyzer.test.d.ts.map +1 -0
  16. package/dist/__tests__/style-analyzer.test.js +53 -0
  17. package/dist/__tests__/style-analyzer.test.js.map +1 -0
  18. package/dist/__tests__/webhook.test.d.ts +2 -0
  19. package/dist/__tests__/webhook.test.d.ts.map +1 -0
  20. package/dist/__tests__/webhook.test.js +62 -0
  21. package/dist/__tests__/webhook.test.js.map +1 -0
  22. package/dist/agents/ai-tells.d.ts +24 -0
  23. package/dist/agents/ai-tells.d.ts.map +1 -0
  24. package/dist/agents/ai-tells.js +114 -0
  25. package/dist/agents/ai-tells.js.map +1 -0
  26. package/dist/agents/architect.d.ts.map +1 -1
  27. package/dist/agents/architect.js +2 -0
  28. package/dist/agents/architect.js.map +1 -1
  29. package/dist/agents/continuity.d.ts.map +1 -1
  30. package/dist/agents/continuity.js +34 -2
  31. package/dist/agents/continuity.js.map +1 -1
  32. package/dist/agents/detection-insights.d.ts +9 -0
  33. package/dist/agents/detection-insights.d.ts.map +1 -0
  34. package/dist/agents/detection-insights.js +54 -0
  35. package/dist/agents/detection-insights.js.map +1 -0
  36. package/dist/agents/detector.d.ts +17 -0
  37. package/dist/agents/detector.d.ts.map +1 -0
  38. package/dist/agents/detector.js +77 -0
  39. package/dist/agents/detector.js.map +1 -0
  40. package/dist/agents/reviser.d.ts +1 -1
  41. package/dist/agents/reviser.d.ts.map +1 -1
  42. package/dist/agents/reviser.js +1 -0
  43. package/dist/agents/reviser.js.map +1 -1
  44. package/dist/agents/style-analyzer.d.ts +11 -0
  45. package/dist/agents/style-analyzer.d.ts.map +1 -0
  46. package/dist/agents/style-analyzer.js +81 -0
  47. package/dist/agents/style-analyzer.js.map +1 -0
  48. package/dist/agents/writer-prompts.d.ts +1 -1
  49. package/dist/agents/writer-prompts.d.ts.map +1 -1
  50. package/dist/agents/writer-prompts.js +42 -2
  51. package/dist/agents/writer-prompts.js.map +1 -1
  52. package/dist/agents/writer.d.ts +9 -0
  53. package/dist/agents/writer.d.ts.map +1 -1
  54. package/dist/agents/writer.js +98 -5
  55. package/dist/agents/writer.js.map +1 -1
  56. package/dist/index.d.ts +10 -2
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +8 -2
  59. package/dist/index.js.map +1 -1
  60. package/dist/models/chapter.d.ts +9 -0
  61. package/dist/models/chapter.d.ts.map +1 -1
  62. package/dist/models/chapter.js +3 -0
  63. package/dist/models/chapter.js.map +1 -1
  64. package/dist/models/detection.d.ts +25 -0
  65. package/dist/models/detection.d.ts.map +1 -0
  66. package/dist/models/detection.js +2 -0
  67. package/dist/models/detection.js.map +1 -0
  68. package/dist/models/project.d.ts +156 -0
  69. package/dist/models/project.d.ts.map +1 -1
  70. package/dist/models/project.js +31 -0
  71. package/dist/models/project.js.map +1 -1
  72. package/dist/models/style-profile.d.ts +16 -0
  73. package/dist/models/style-profile.d.ts.map +1 -0
  74. package/dist/models/style-profile.js +2 -0
  75. package/dist/models/style-profile.js.map +1 -0
  76. package/dist/notify/dispatcher.d.ts +3 -0
  77. package/dist/notify/dispatcher.d.ts.map +1 -1
  78. package/dist/notify/dispatcher.js +28 -0
  79. package/dist/notify/dispatcher.js.map +1 -1
  80. package/dist/notify/webhook.d.ts +15 -0
  81. package/dist/notify/webhook.d.ts.map +1 -0
  82. package/dist/notify/webhook.js +28 -0
  83. package/dist/notify/webhook.js.map +1 -0
  84. package/dist/pipeline/detection-runner.d.ts +31 -0
  85. package/dist/pipeline/detection-runner.d.ts.map +1 -0
  86. package/dist/pipeline/detection-runner.js +109 -0
  87. package/dist/pipeline/detection-runner.js.map +1 -0
  88. package/dist/pipeline/runner.d.ts +3 -2
  89. package/dist/pipeline/runner.d.ts.map +1 -1
  90. package/dist/pipeline/runner.js +63 -8
  91. package/dist/pipeline/runner.js.map +1 -1
  92. package/dist/pipeline/scheduler.d.ts +11 -0
  93. package/dist/pipeline/scheduler.d.ts.map +1 -1
  94. package/dist/pipeline/scheduler.js +60 -0
  95. package/dist/pipeline/scheduler.js.map +1 -1
  96. package/dist/state/manager.d.ts.map +1 -1
  97. package/dist/state/manager.js +21 -3
  98. package/dist/state/manager.js.map +1 -1
  99. package/package.json +8 -8
@@ -23,12 +23,33 @@ export const NotifyChannelSchema = z.discriminatedUnion("type", [
23
23
  type: z.literal("feishu"),
24
24
  webhookUrl: z.string().url(),
25
25
  }),
26
+ z.object({
27
+ type: z.literal("webhook"),
28
+ url: z.string().url(),
29
+ secret: z.string().optional(),
30
+ events: z.array(z.string()).default([]),
31
+ }),
26
32
  ]);
33
+ export const DetectionConfigSchema = z.object({
34
+ provider: z.enum(["gptzero", "originality", "custom"]).default("custom"),
35
+ apiUrl: z.string().url(),
36
+ apiKeyEnv: z.string().min(1),
37
+ threshold: z.number().min(0).max(1).default(0.5),
38
+ enabled: z.boolean().default(false),
39
+ autoRewrite: z.boolean().default(false),
40
+ maxRetries: z.number().int().min(1).max(10).default(3),
41
+ });
42
+ export const QualityGatesSchema = z.object({
43
+ maxAuditRetries: z.number().int().min(0).max(10).default(2),
44
+ pauseAfterConsecutiveFailures: z.number().int().min(1).default(3),
45
+ retryTemperatureStep: z.number().min(0).max(0.5).default(0.1),
46
+ });
27
47
  export const ProjectConfigSchema = z.object({
28
48
  name: z.string().min(1),
29
49
  version: z.literal("0.1.0"),
30
50
  llm: LLMConfigSchema,
31
51
  notify: z.array(NotifyChannelSchema).default([]),
52
+ detection: DetectionConfigSchema.optional(),
32
53
  daemon: z.object({
33
54
  schedule: z.object({
34
55
  radarCron: z.string().default("0 9 * * *"),
@@ -36,6 +57,11 @@ export const ProjectConfigSchema = z.object({
36
57
  auditCron: z.string().default("0 17 * * *"),
37
58
  }),
38
59
  maxConcurrentBooks: z.number().int().min(1).default(3),
60
+ qualityGates: QualityGatesSchema.default({
61
+ maxAuditRetries: 2,
62
+ pauseAfterConsecutiveFailures: 3,
63
+ retryTemperatureStep: 0.1,
64
+ }),
39
65
  }).default({
40
66
  schedule: {
41
67
  radarCron: "0 9 * * *",
@@ -43,6 +69,11 @@ export const ProjectConfigSchema = z.object({
43
69
  auditCron: "0 17 * * *",
44
70
  },
45
71
  maxConcurrentBooks: 3,
72
+ qualityGates: {
73
+ maxAuditRetries: 2,
74
+ pauseAfterConsecutiveFailures: 3,
75
+ retryTemperatureStep: 0.1,
76
+ },
46
77
  }),
47
78
  });
48
79
  //# sourceMappingURL=project.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"project.js","sourceRoot":"","sources":["../../src/models/project.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"project.js","sourceRoot":"","sources":["../../src/models/project.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /** Style fingerprint profile extracted from reference text. */
2
+ export interface StyleProfile {
3
+ readonly avgSentenceLength: number;
4
+ readonly sentenceLengthStdDev: number;
5
+ readonly avgParagraphLength: number;
6
+ readonly paragraphLengthRange: {
7
+ readonly min: number;
8
+ readonly max: number;
9
+ };
10
+ readonly vocabularyDiversity: number;
11
+ readonly topPatterns: ReadonlyArray<string>;
12
+ readonly rhetoricalFeatures: ReadonlyArray<string>;
13
+ readonly sourceName?: string;
14
+ readonly analyzedAt?: string;
15
+ }
16
+ //# sourceMappingURL=style-profile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"style-profile.d.ts","sourceRoot":"","sources":["../../src/models/style-profile.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACtC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,oBAAoB,EAAE;QAC7B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,WAAW,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC5C,QAAQ,CAAC,kBAAkB,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACnD,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=style-profile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"style-profile.js","sourceRoot":"","sources":["../../src/models/style-profile.ts"],"names":[],"mappings":""}
@@ -1,7 +1,10 @@
1
1
  import type { NotifyChannel } from "../models/project.js";
2
+ import { type WebhookPayload } from "./webhook.js";
2
3
  export interface NotifyMessage {
3
4
  readonly title: string;
4
5
  readonly body: string;
5
6
  }
6
7
  export declare function dispatchNotification(channels: ReadonlyArray<NotifyChannel>, message: NotifyMessage): Promise<void>;
8
+ /** Dispatch a structured webhook event to all webhook channels. */
9
+ export declare function dispatchWebhookEvent(channels: ReadonlyArray<NotifyChannel>, payload: WebhookPayload): Promise<void>;
7
10
  //# sourceMappingURL=dispatcher.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../../src/notify/dispatcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAK1D,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,aAAa,CAAC,aAAa,CAAC,EACtC,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,IAAI,CAAC,CAmCf"}
1
+ {"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../../src/notify/dispatcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAI1D,OAAO,EAAe,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAEhE,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,aAAa,CAAC,aAAa,CAAC,EACtC,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,IAAI,CAAC,CAgDf;AAED,mEAAmE;AACnE,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,aAAa,CAAC,aAAa,CAAC,EACtC,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,IAAI,CAAC,CAiBf"}
@@ -1,6 +1,7 @@
1
1
  import { sendTelegram } from "./telegram.js";
2
2
  import { sendFeishu } from "./feishu.js";
3
3
  import { sendWechatWork } from "./wechat-work.js";
4
+ import { sendWebhook } from "./webhook.js";
4
5
  export async function dispatchNotification(channels, message) {
5
6
  const fullText = `**${message.title}**\n\n${message.body}`;
6
7
  const tasks = channels.map(async (channel) => {
@@ -15,6 +16,16 @@ export async function dispatchNotification(channels, message) {
15
16
  case "wechat-work":
16
17
  await sendWechatWork({ webhookUrl: channel.webhookUrl }, fullText);
17
18
  break;
19
+ case "webhook":
20
+ // Webhook channels are handled by dispatchWebhookEvent for structured events.
21
+ // For generic text notifications, send as a pipeline-complete event.
22
+ await sendWebhook({ url: channel.url, secret: channel.secret, events: channel.events }, {
23
+ event: "pipeline-complete",
24
+ bookId: "",
25
+ timestamp: new Date().toISOString(),
26
+ data: { title: message.title, body: message.body },
27
+ });
28
+ break;
18
29
  }
19
30
  }
20
31
  catch (e) {
@@ -24,4 +35,21 @@ export async function dispatchNotification(channels, message) {
24
35
  });
25
36
  await Promise.all(tasks);
26
37
  }
38
+ /** Dispatch a structured webhook event to all webhook channels. */
39
+ export async function dispatchWebhookEvent(channels, payload) {
40
+ const webhookChannels = channels.filter((ch) => ch.type === "webhook");
41
+ if (webhookChannels.length === 0)
42
+ return;
43
+ const tasks = webhookChannels.map(async (channel) => {
44
+ if (channel.type !== "webhook")
45
+ return;
46
+ try {
47
+ await sendWebhook({ url: channel.url, secret: channel.secret, events: channel.events }, payload);
48
+ }
49
+ catch (e) {
50
+ process.stderr.write(`[webhook] ${channel.url} failed: ${e}\n`);
51
+ }
52
+ });
53
+ await Promise.all(tasks);
54
+ }
27
55
  //# sourceMappingURL=dispatcher.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"dispatcher.js","sourceRoot":"","sources":["../../src/notify/dispatcher.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAOlD,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,QAAsC,EACtC,OAAsB;IAEtB,MAAM,QAAQ,GAAG,KAAK,OAAO,CAAC,KAAK,SAAS,OAAO,CAAC,IAAI,EAAE,CAAC;IAE3D,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;QAC3C,IAAI,CAAC;YACH,QAAQ,OAAO,CAAC,IAAI,EAAE,CAAC;gBACrB,KAAK,UAAU;oBACb,MAAM,YAAY,CAChB,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,EACtD,QAAQ,CACT,CAAC;oBACF,MAAM;gBACR,KAAK,QAAQ;oBACX,MAAM,UAAU,CACd,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,EAClC,OAAO,CAAC,KAAK,EACb,OAAO,CAAC,IAAI,CACb,CAAC;oBACF,MAAM;gBACR,KAAK,aAAa;oBAChB,MAAM,cAAc,CAClB,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,EAClC,QAAQ,CACT,CAAC;oBACF,MAAM;YACV,CAAC;QACH,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,sEAAsE;YACtE,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,YAAY,OAAO,CAAC,IAAI,YAAY,CAAC,IAAI,CAC1C,CAAC;QACJ,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AAC3B,CAAC"}
1
+ {"version":3,"file":"dispatcher.js","sourceRoot":"","sources":["../../src/notify/dispatcher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ export interface WebhookConfig {
2
+ readonly url: string;
3
+ readonly secret?: string;
4
+ readonly events?: ReadonlyArray<string>;
5
+ }
6
+ export type WebhookEvent = "chapter-complete" | "audit-passed" | "audit-failed" | "revision-complete" | "pipeline-complete" | "pipeline-error";
7
+ export interface WebhookPayload {
8
+ readonly event: WebhookEvent;
9
+ readonly bookId: string;
10
+ readonly chapterNumber?: number;
11
+ readonly timestamp: string;
12
+ readonly data?: Record<string, unknown>;
13
+ }
14
+ export declare function sendWebhook(config: WebhookConfig, payload: WebhookPayload): Promise<void>;
15
+ //# sourceMappingURL=webhook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhook.d.ts","sourceRoot":"","sources":["../../src/notify/webhook.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CACzC;AAED,MAAM,MAAM,YAAY,GACpB,kBAAkB,GAClB,cAAc,GACd,cAAc,GACd,mBAAmB,GACnB,mBAAmB,GACnB,gBAAgB,CAAC;AAErB,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACzC;AAED,wBAAsB,WAAW,CAC/B,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,IAAI,CAAC,CA6Bf"}
@@ -0,0 +1,28 @@
1
+ import { createHmac } from "node:crypto";
2
+ export async function sendWebhook(config, payload) {
3
+ // Filter by subscribed events
4
+ if (config.events && config.events.length > 0 && !config.events.includes(payload.event)) {
5
+ return;
6
+ }
7
+ const body = JSON.stringify(payload);
8
+ const headers = {
9
+ "Content-Type": "application/json",
10
+ };
11
+ // HMAC-SHA256 signature if secret is configured
12
+ if (config.secret) {
13
+ const signature = createHmac("sha256", config.secret)
14
+ .update(body)
15
+ .digest("hex");
16
+ headers["X-InkOS-Signature"] = `sha256=${signature}`;
17
+ }
18
+ const response = await fetch(config.url, {
19
+ method: "POST",
20
+ headers,
21
+ body,
22
+ });
23
+ if (!response.ok) {
24
+ const responseBody = await response.text();
25
+ throw new Error(`Webhook POST to ${config.url} failed: ${response.status} ${responseBody}`);
26
+ }
27
+ }
28
+ //# sourceMappingURL=webhook.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhook.js","sourceRoot":"","sources":["../../src/notify/webhook.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAwBzC,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,MAAqB,EACrB,OAAuB;IAEvB,8BAA8B;IAC9B,IAAI,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACxF,OAAO;IACT,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IACrC,MAAM,OAAO,GAA2B;QACtC,cAAc,EAAE,kBAAkB;KACnC,CAAC;IAEF,gDAAgD;IAChD,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAClB,MAAM,SAAS,GAAG,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC;aAClD,MAAM,CAAC,IAAI,CAAC;aACZ,MAAM,CAAC,KAAK,CAAC,CAAC;QACjB,OAAO,CAAC,mBAAmB,CAAC,GAAG,UAAU,SAAS,EAAE,CAAC;IACvD,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE;QACvC,MAAM,EAAE,MAAM;QACd,OAAO;QACP,IAAI;KACL,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,YAAY,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QAC3C,MAAM,IAAI,KAAK,CAAC,mBAAmB,MAAM,CAAC,GAAG,YAAY,QAAQ,CAAC,MAAM,IAAI,YAAY,EAAE,CAAC,CAAC;IAC9F,CAAC;AACH,CAAC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Detection pipeline runner — handles detection, auto-rewrite loop, and history tracking.
3
+ * Extracted from runner.ts to keep runner under 800 lines.
4
+ */
5
+ import type { DetectionConfig } from "../models/project.js";
6
+ import type { DetectionHistoryEntry } from "../models/detection.js";
7
+ import type { AgentContext } from "../agents/base.js";
8
+ import { type DetectionResult } from "../agents/detector.js";
9
+ export interface DetectChapterResult {
10
+ readonly chapterNumber: number;
11
+ readonly detection: DetectionResult;
12
+ readonly passed: boolean;
13
+ }
14
+ export interface DetectAndRewriteResult {
15
+ readonly chapterNumber: number;
16
+ readonly originalScore: number;
17
+ readonly finalScore: number;
18
+ readonly attempts: number;
19
+ readonly passed: boolean;
20
+ readonly finalContent: string;
21
+ }
22
+ /** Run detection on a single chapter's content. */
23
+ export declare function detectChapter(config: DetectionConfig, content: string, chapterNumber: number): Promise<DetectChapterResult>;
24
+ /**
25
+ * Detect-and-rewrite loop: detect → revise in anti-detect mode → re-detect,
26
+ * until score passes threshold or max retries reached.
27
+ */
28
+ export declare function detectAndRewrite(config: DetectionConfig, ctx: AgentContext, bookDir: string, content: string, chapterNumber: number, genre?: string): Promise<DetectAndRewriteResult>;
29
+ /** Load detection history from disk. */
30
+ export declare function loadDetectionHistory(bookDir: string): Promise<ReadonlyArray<DetectionHistoryEntry>>;
31
+ //# sourceMappingURL=detection-runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detection-runner.d.ts","sourceRoot":"","sources":["../../src/pipeline/detection-runner.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AACpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAK9E,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED,mDAAmD;AACnD,wBAAsB,aAAa,CACjC,MAAM,EAAE,eAAe,EACvB,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,mBAAmB,CAAC,CAO9B;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,eAAe,EACvB,GAAG,EAAE,YAAY,EACjB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,MAAM,EACrB,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,sBAAsB,CAAC,CA2EjC;AAuBD,wCAAwC;AACxC,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,aAAa,CAAC,qBAAqB,CAAC,CAAC,CAQ/C"}
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Detection pipeline runner — handles detection, auto-rewrite loop, and history tracking.
3
+ * Extracted from runner.ts to keep runner under 800 lines.
4
+ */
5
+ import { detectAIContent } from "../agents/detector.js";
6
+ import { ReviserAgent } from "../agents/reviser.js";
7
+ import { readFile, writeFile, mkdir } from "node:fs/promises";
8
+ import { join } from "node:path";
9
+ /** Run detection on a single chapter's content. */
10
+ export async function detectChapter(config, content, chapterNumber) {
11
+ const detection = await detectAIContent(config, content);
12
+ return {
13
+ chapterNumber,
14
+ detection,
15
+ passed: detection.score <= config.threshold,
16
+ };
17
+ }
18
+ /**
19
+ * Detect-and-rewrite loop: detect → revise in anti-detect mode → re-detect,
20
+ * until score passes threshold or max retries reached.
21
+ */
22
+ export async function detectAndRewrite(config, ctx, bookDir, content, chapterNumber, genre) {
23
+ const maxRetries = config.maxRetries;
24
+ let currentContent = content;
25
+ const firstDetection = await detectAIContent(config, currentContent);
26
+ const originalScore = firstDetection.score;
27
+ if (firstDetection.score <= config.threshold) {
28
+ await recordHistory(bookDir, {
29
+ chapterNumber,
30
+ timestamp: firstDetection.detectedAt,
31
+ provider: firstDetection.provider,
32
+ score: firstDetection.score,
33
+ action: "detect",
34
+ attempt: 0,
35
+ });
36
+ return {
37
+ chapterNumber,
38
+ originalScore,
39
+ finalScore: firstDetection.score,
40
+ attempts: 0,
41
+ passed: true,
42
+ finalContent: currentContent,
43
+ };
44
+ }
45
+ let finalScore = firstDetection.score;
46
+ let attempts = 0;
47
+ for (let i = 0; i < maxRetries; i++) {
48
+ attempts = i + 1;
49
+ // Rewrite in anti-detect mode
50
+ const reviser = new ReviserAgent(ctx);
51
+ const reviseOutput = await reviser.reviseChapter(bookDir, currentContent, chapterNumber, [{
52
+ severity: "warning",
53
+ category: "AIGC检测",
54
+ description: `AI检测分数 ${finalScore.toFixed(2)} 超过阈值 ${config.threshold}`,
55
+ suggestion: "降低AI生成痕迹:增加段落长度差异、减少套话、用口语化表达替代书面语",
56
+ }], "anti-detect", genre);
57
+ if (reviseOutput.revisedContent.length === 0)
58
+ break;
59
+ currentContent = reviseOutput.revisedContent;
60
+ // Re-detect
61
+ const reDetection = await detectAIContent(config, currentContent);
62
+ finalScore = reDetection.score;
63
+ await recordHistory(bookDir, {
64
+ chapterNumber,
65
+ timestamp: reDetection.detectedAt,
66
+ provider: reDetection.provider,
67
+ score: reDetection.score,
68
+ action: "rewrite",
69
+ attempt: attempts,
70
+ });
71
+ if (finalScore <= config.threshold)
72
+ break;
73
+ }
74
+ return {
75
+ chapterNumber,
76
+ originalScore,
77
+ finalScore,
78
+ attempts,
79
+ passed: finalScore <= config.threshold,
80
+ finalContent: currentContent,
81
+ };
82
+ }
83
+ /** Append an entry to detection_history.json. */
84
+ async function recordHistory(bookDir, entry) {
85
+ const historyPath = join(bookDir, "story", "detection_history.json");
86
+ let history = [];
87
+ try {
88
+ const raw = await readFile(historyPath, "utf-8");
89
+ history = JSON.parse(raw);
90
+ }
91
+ catch {
92
+ // File doesn't exist yet
93
+ }
94
+ history.push(entry);
95
+ await mkdir(join(bookDir, "story"), { recursive: true });
96
+ await writeFile(historyPath, JSON.stringify(history, null, 2), "utf-8");
97
+ }
98
+ /** Load detection history from disk. */
99
+ export async function loadDetectionHistory(bookDir) {
100
+ const historyPath = join(bookDir, "story", "detection_history.json");
101
+ try {
102
+ const raw = await readFile(historyPath, "utf-8");
103
+ return JSON.parse(raw);
104
+ }
105
+ catch {
106
+ return [];
107
+ }
108
+ }
109
+ //# sourceMappingURL=detection-runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detection-runner.js","sourceRoot":"","sources":["../../src/pipeline/detection-runner.ts"],"names":[],"mappings":"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"}
@@ -61,7 +61,7 @@ export declare class PipelineRunner {
61
61
  runRadar(): Promise<RadarResult>;
62
62
  initBook(book: BookConfig): Promise<void>;
63
63
  /** Write a single draft chapter. Saves chapter file + truth files + index + snapshot. */
64
- writeDraft(bookId: string, context?: string): Promise<DraftResult>;
64
+ writeDraft(bookId: string, context?: string, wordCount?: number): Promise<DraftResult>;
65
65
  /** Audit the latest (or specified) chapter. Read-only, no lock needed. */
66
66
  auditDraft(bookId: string, chapterNumber?: number): Promise<AuditResult & {
67
67
  readonly chapterNumber: number;
@@ -72,8 +72,9 @@ export declare class PipelineRunner {
72
72
  readTruthFiles(bookId: string): Promise<TruthFiles>;
73
73
  /** Get book status overview. */
74
74
  getBookStatus(bookId: string): Promise<BookStatusInfo>;
75
- writeNextChapter(bookId: string): Promise<ChapterPipelineResult>;
75
+ writeNextChapter(bookId: string, wordCount?: number): Promise<ChapterPipelineResult>;
76
76
  private _writeNextChapterLocked;
77
+ private emitWebhook;
77
78
  private readChapterContent;
78
79
  }
79
80
  //# sourceMappingURL=runner.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/pipeline/runner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAK1D,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAErE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAK7D,OAAO,KAAK,EAAE,WAAW,EAAc,MAAM,yBAAyB,CAAC;AACvE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAItD,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,cAAc,CAAC,EAAE,aAAa,CAAC,aAAa,CAAC,CAAC;IACvD,QAAQ,CAAC,YAAY,CAAC,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC;IACnD,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;CACnC;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,cAAc,CAAC;CAC9C;AAGD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CAC7C;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC;CAC/C;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IACrC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiB;gBAE5B,MAAM,EAAE,cAAc;IAKlC,OAAO,CAAC,QAAQ;YASF,gBAAgB;IASxB,QAAQ,IAAI,OAAO,CAAC,WAAW,CAAC;IAKhC,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAY/C,yFAAyF;IACnF,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAoDxE,0EAA0E;IACpE,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG;QAAE,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC;IA6BnH,sEAAsE;IAChE,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,EAAE,IAAI,GAAE,UAAsB,GAAG,OAAO,CAAC,YAAY,CAAC;IAwF9G,uCAAuC;IACjC,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAwBzD,gCAAgC;IAC1B,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAuBtD,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC;YASxD,uBAAuB;YA4IvB,kBAAkB;CAcjC"}
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/pipeline/runner.ts"],"names":[],"mappings":"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"}
@@ -4,8 +4,9 @@ import { ContinuityAuditor } from "../agents/continuity.js";
4
4
  import { ReviserAgent } from "../agents/reviser.js";
5
5
  import { RadarAgent } from "../agents/radar.js";
6
6
  import { readGenreProfile } from "../agents/rules-reader.js";
7
+ import { analyzeAITells } from "../agents/ai-tells.js";
7
8
  import { StateManager } from "../state/manager.js";
8
- import { dispatchNotification } from "../notify/dispatcher.js";
9
+ import { dispatchNotification, dispatchWebhookEvent } from "../notify/dispatcher.js";
9
10
  import { readFile, readdir, writeFile } from "node:fs/promises";
10
11
  import { join } from "node:path";
11
12
  export class PipelineRunner {
@@ -44,7 +45,7 @@ export class PipelineRunner {
44
45
  await this.state.saveChapterIndex(book.id, []);
45
46
  }
46
47
  /** Write a single draft chapter. Saves chapter file + truth files + index + snapshot. */
47
- async writeDraft(bookId, context) {
48
+ async writeDraft(bookId, context, wordCount) {
48
49
  const releaseLock = await this.state.acquireBookLock(bookId);
49
50
  try {
50
51
  const book = await this.state.loadBookConfig(bookId);
@@ -57,6 +58,7 @@ export class PipelineRunner {
57
58
  bookDir,
58
59
  chapterNumber,
59
60
  externalContext: context ?? this.config.externalContext,
61
+ ...(wordCount ? { wordCountOverride: wordCount } : {}),
60
62
  });
61
63
  // Save chapter file
62
64
  const chaptersDir = join(bookDir, "chapters");
@@ -67,6 +69,7 @@ export class PipelineRunner {
67
69
  await writeFile(filePath, `# 第${chapterNumber}章 ${output.title}\n\n${output.content}`, "utf-8");
68
70
  // Save truth files
69
71
  await writer.saveChapter(bookDir, output, gp.numericalSystem);
72
+ await writer.saveNewTruthFiles(bookDir, output);
70
73
  // Update index
71
74
  const existingIndex = await this.state.loadChapterIndex(bookId);
72
75
  const now = new Date().toISOString();
@@ -82,6 +85,10 @@ export class PipelineRunner {
82
85
  await this.state.saveChapterIndex(bookId, [...existingIndex, newEntry]);
83
86
  // Snapshot
84
87
  await this.state.snapshotState(bookId, chapterNumber);
88
+ await this.emitWebhook("chapter-complete", bookId, chapterNumber, {
89
+ title: output.title,
90
+ wordCount: output.wordCount,
91
+ });
85
92
  return { chapterNumber, title: output.title, wordCount: output.wordCount, filePath };
86
93
  }
87
94
  finally {
@@ -98,7 +105,18 @@ export class PipelineRunner {
98
105
  }
99
106
  const content = await this.readChapterContent(bookDir, targetChapter);
100
107
  const auditor = new ContinuityAuditor(this.agentCtx(bookId));
101
- const result = await auditor.auditChapter(bookDir, content, targetChapter, book.genre);
108
+ const llmResult = await auditor.auditChapter(bookDir, content, targetChapter, book.genre);
109
+ // Merge rule-based AI-tell detection
110
+ const aiTells = analyzeAITells(content);
111
+ const mergedIssues = [
112
+ ...llmResult.issues,
113
+ ...aiTells.issues,
114
+ ];
115
+ const result = {
116
+ passed: llmResult.passed,
117
+ issues: mergedIssues,
118
+ summary: llmResult.summary,
119
+ };
102
120
  // Update index with audit result
103
121
  const index = await this.state.loadChapterIndex(bookId);
104
122
  const updated = index.map((ch) => ch.number === targetChapter
@@ -110,6 +128,7 @@ export class PipelineRunner {
110
128
  }
111
129
  : ch);
112
130
  await this.state.saveChapterIndex(bookId, updated);
131
+ await this.emitWebhook(result.passed ? "audit-passed" : "audit-failed", bookId, targetChapter, { summary: result.summary, issueCount: result.issues.length });
113
132
  return { ...result, chapterNumber: targetChapter };
114
133
  }
115
134
  /** Revise the latest (or specified) chapter based on audit issues. */
@@ -172,6 +191,10 @@ export class PipelineRunner {
172
191
  await this.state.saveChapterIndex(bookId, updatedIndex);
173
192
  // Re-snapshot
174
193
  await this.state.snapshotState(bookId, targetChapter);
194
+ await this.emitWebhook("revision-complete", bookId, targetChapter, {
195
+ wordCount: reviseOutput.wordCount,
196
+ fixedCount: reviseOutput.fixedIssues.length,
197
+ });
175
198
  return {
176
199
  chapterNumber: targetChapter,
177
200
  wordCount: reviseOutput.wordCount,
@@ -225,16 +248,16 @@ export class PipelineRunner {
225
248
  // ---------------------------------------------------------------------------
226
249
  // Full pipeline (convenience — runs draft + audit + revise in one shot)
227
250
  // ---------------------------------------------------------------------------
228
- async writeNextChapter(bookId) {
251
+ async writeNextChapter(bookId, wordCount) {
229
252
  const releaseLock = await this.state.acquireBookLock(bookId);
230
253
  try {
231
- return await this._writeNextChapterLocked(bookId);
254
+ return await this._writeNextChapterLocked(bookId, wordCount);
232
255
  }
233
256
  finally {
234
257
  await releaseLock();
235
258
  }
236
259
  }
237
- async _writeNextChapterLocked(bookId) {
260
+ async _writeNextChapterLocked(bookId, wordCount) {
238
261
  const book = await this.state.loadBookConfig(bookId);
239
262
  const bookDir = this.state.bookDir(bookId);
240
263
  const chapterNumber = await this.state.getNextChapterNumber(bookId);
@@ -246,10 +269,17 @@ export class PipelineRunner {
246
269
  bookDir,
247
270
  chapterNumber,
248
271
  externalContext: this.config.externalContext,
272
+ ...(wordCount ? { wordCountOverride: wordCount } : {}),
249
273
  });
250
274
  // 2. Audit chapter
251
275
  const auditor = new ContinuityAuditor(this.agentCtx(bookId));
252
- let auditResult = await auditor.auditChapter(bookDir, output.content, chapterNumber, book.genre);
276
+ const llmAudit = await auditor.auditChapter(bookDir, output.content, chapterNumber, book.genre);
277
+ const aiTellsResult = analyzeAITells(output.content);
278
+ let auditResult = {
279
+ passed: llmAudit.passed,
280
+ issues: [...llmAudit.issues, ...aiTellsResult.issues],
281
+ summary: llmAudit.summary,
282
+ };
253
283
  let finalContent = output.content;
254
284
  let finalWordCount = output.wordCount;
255
285
  let revised = false;
@@ -264,7 +294,13 @@ export class PipelineRunner {
264
294
  finalWordCount = reviseOutput.wordCount;
265
295
  revised = true;
266
296
  // Re-audit the revised content
267
- auditResult = await auditor.auditChapter(bookDir, finalContent, chapterNumber, book.genre);
297
+ const reAudit = await auditor.auditChapter(bookDir, finalContent, chapterNumber, book.genre);
298
+ const reAITells = analyzeAITells(finalContent);
299
+ auditResult = {
300
+ passed: reAudit.passed,
301
+ issues: [...reAudit.issues, ...reAITells.issues],
302
+ summary: reAudit.summary,
303
+ };
268
304
  // Update state files from revision
269
305
  const storyDir = join(bookDir, "story");
270
306
  if (reviseOutput.updatedState !== "(状态卡未更新)") {
@@ -289,6 +325,8 @@ export class PipelineRunner {
289
325
  if (!revised) {
290
326
  await writer.saveChapter(bookDir, output, gp.numericalSystem);
291
327
  }
328
+ // Save new truth files (summaries, subplots, emotional arcs, character matrix)
329
+ await writer.saveNewTruthFiles(bookDir, output);
292
330
  // 5. Update chapter index
293
331
  const existingIndex = await this.state.loadChapterIndex(bookId);
294
332
  const now = new Date().toISOString();
@@ -321,6 +359,12 @@ export class PipelineRunner {
321
359
  .join("\n"),
322
360
  });
323
361
  }
362
+ await this.emitWebhook("pipeline-complete", bookId, chapterNumber, {
363
+ title: output.title,
364
+ wordCount: finalWordCount,
365
+ passed: auditResult.passed,
366
+ revised,
367
+ });
324
368
  return {
325
369
  chapterNumber,
326
370
  title: output.title,
@@ -333,6 +377,17 @@ export class PipelineRunner {
333
377
  // ---------------------------------------------------------------------------
334
378
  // Helpers
335
379
  // ---------------------------------------------------------------------------
380
+ async emitWebhook(event, bookId, chapterNumber, data) {
381
+ if (!this.config.notifyChannels || this.config.notifyChannels.length === 0)
382
+ return;
383
+ await dispatchWebhookEvent(this.config.notifyChannels, {
384
+ event,
385
+ bookId,
386
+ chapterNumber,
387
+ timestamp: new Date().toISOString(),
388
+ data,
389
+ });
390
+ }
336
391
  async readChapterContent(bookDir, chapterNumber) {
337
392
  const chaptersDir = join(bookDir, "chapters");
338
393
  const files = await readdir(chaptersDir);