@rubytech/create-maxy-code 0.1.175 → 0.1.179

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 (60) hide show
  1. package/dist/index.js +13 -4
  2. package/package.json +1 -1
  3. package/payload/platform/plugins/email/skills/email-composition/SKILL.md +1 -1
  4. package/payload/platform/plugins/memory/bin/conversation-archive-ingest.mjs +38 -3
  5. package/payload/platform/plugins/memory/mcp/dist/index.js +17 -7
  6. package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
  7. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/turn-text-cache-path.d.ts +8 -0
  8. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/turn-text-cache-path.d.ts.map +1 -0
  9. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/turn-text-cache-path.js +27 -0
  10. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/turn-text-cache-path.js.map +1 -0
  11. package/payload/platform/plugins/memory/mcp/dist/lib/section-types.d.ts +15 -2
  12. package/payload/platform/plugins/memory/mcp/dist/lib/section-types.d.ts.map +1 -1
  13. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-body-server-sliced.test.d.ts +2 -0
  14. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-body-server-sliced.test.d.ts.map +1 -0
  15. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-body-server-sliced.test.js +95 -0
  16. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-body-server-sliced.test.js.map +1 -0
  17. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-chat-body-server-sliced.test.d.ts +2 -0
  18. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-chat-body-server-sliced.test.d.ts.map +1 -0
  19. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-chat-body-server-sliced.test.js +124 -0
  20. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-chat-body-server-sliced.test.js.map +1 -0
  21. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-section-properties-strip.test.d.ts +2 -0
  22. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-section-properties-strip.test.d.ts.map +1 -0
  23. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-section-properties-strip.test.js +104 -0
  24. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-section-properties-strip.test.js.map +1 -0
  25. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-web.d.ts +12 -0
  26. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-web.d.ts.map +1 -1
  27. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-web.js +38 -13
  28. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-web.js.map +1 -1
  29. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.d.ts +23 -0
  30. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.d.ts.map +1 -1
  31. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js +234 -20
  32. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js.map +1 -1
  33. package/payload/platform/plugins/memory/skills/conversation-archive/SKILL.md +7 -3
  34. package/payload/platform/plugins/memory/skills/document-ingest/SKILL.md +12 -3
  35. package/payload/platform/plugins/substack-import/skills/substack-import/SKILL.md +1 -0
  36. package/payload/premium-plugins/venture-studio/skills/investor-data-room/SKILL.md +1 -1
  37. package/payload/premium-plugins/writer-craft/PLUGIN.md +2 -2
  38. package/payload/premium-plugins/writer-craft/mcp/dist/index.js +3 -3
  39. package/payload/premium-plugins/writer-craft/mcp/dist/index.js.map +1 -1
  40. package/payload/premium-plugins/writer-craft/mcp/dist/lib/voice-corpus.d.ts +4 -22
  41. package/payload/premium-plugins/writer-craft/mcp/dist/lib/voice-corpus.d.ts.map +1 -1
  42. package/payload/premium-plugins/writer-craft/mcp/dist/lib/voice-corpus.js +4 -36
  43. package/payload/premium-plugins/writer-craft/mcp/dist/lib/voice-corpus.js.map +1 -1
  44. package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-distil-profile.d.ts.map +1 -1
  45. package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-distil-profile.js +34 -2
  46. package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-distil-profile.js.map +1 -1
  47. package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-retrieve-conditioning.d.ts.map +1 -1
  48. package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-retrieve-conditioning.js +24 -5
  49. package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-retrieve-conditioning.js.map +1 -1
  50. package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-tag-content.d.ts +2 -2
  51. package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-tag-content.d.ts.map +1 -1
  52. package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-tag-content.js +14 -28
  53. package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-tag-content.js.map +1 -1
  54. package/payload/premium-plugins/writer-craft/mcp/scripts/smoke.mjs +21 -41
  55. package/payload/premium-plugins/writer-craft/mcp/src/index.ts +5 -5
  56. package/payload/premium-plugins/writer-craft/mcp/src/lib/voice-corpus.ts +4 -41
  57. package/payload/premium-plugins/writer-craft/mcp/src/tools/voice-distil-profile.ts +32 -2
  58. package/payload/premium-plugins/writer-craft/mcp/src/tools/voice-retrieve-conditioning.ts +25 -5
  59. package/payload/premium-plugins/writer-craft/mcp/src/tools/voice-tag-content.ts +16 -31
  60. package/payload/premium-plugins/writer-craft/skills/voice-mirror/SKILL.md +16 -19
@@ -0,0 +1,27 @@
1
+ import { tmpdir } from "node:os";
2
+ import { join } from "node:path";
3
+ // Single source of truth for the per-session turnText tempfile path the bin
4
+ // (`conversation-archive-ingest.mjs`) writes and the writer
5
+ // (`memory-ingest.ts` → `ingestConversationDocument`) reads. Both sides
6
+ // reconstruct the path from the same identity inputs so neither process
7
+ // trusts a path string carried across the LLM boundary.
8
+ //
9
+ // Task 468: the chat-archive path mirrors the document path's `extractCache`
10
+ // contract — the writer slices `:Section.body` from a non-LLM source of
11
+ // turnText. The bin is a separate Node process so the cache is a tempfile
12
+ // instead of an in-process Map.
13
+ export const TURN_TEXT_CACHE_DIR_PREFIX = "maxy-conversation-archive";
14
+ export function turnTextCachePath(inputs) {
15
+ const { conversationIdentity, archiveSha256, sessionIndex } = inputs;
16
+ if (!conversationIdentity || typeof conversationIdentity !== "string") {
17
+ throw new Error("turnTextCachePath: conversationIdentity required");
18
+ }
19
+ if (!archiveSha256 || typeof archiveSha256 !== "string") {
20
+ throw new Error("turnTextCachePath: archiveSha256 required");
21
+ }
22
+ if (!Number.isInteger(sessionIndex) || sessionIndex < 1) {
23
+ throw new Error(`turnTextCachePath: sessionIndex must be integer >= 1 (got ${sessionIndex})`);
24
+ }
25
+ return join(tmpdir(), TURN_TEXT_CACHE_DIR_PREFIX, `${conversationIdentity}-${archiveSha256}`, `session-${sessionIndex}.txt`);
26
+ }
27
+ //# sourceMappingURL=turn-text-cache-path.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"turn-text-cache-path.js","sourceRoot":"","sources":["../../../src/lib/conversation-pipeline/turn-text-cache-path.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,4EAA4E;AAC5E,4DAA4D;AAC5D,wEAAwE;AACxE,wEAAwE;AACxE,wDAAwD;AACxD,EAAE;AACF,6EAA6E;AAC7E,wEAAwE;AACxE,0EAA0E;AAC1E,gCAAgC;AAEhC,MAAM,CAAC,MAAM,0BAA0B,GAAG,2BAA2B,CAAC;AAQtE,MAAM,UAAU,iBAAiB,CAAC,MAA+B;IAC/D,MAAM,EAAE,oBAAoB,EAAE,aAAa,EAAE,YAAY,EAAE,GAAG,MAAM,CAAC;IACrE,IAAI,CAAC,oBAAoB,IAAI,OAAO,oBAAoB,KAAK,QAAQ,EAAE,CAAC;QACtE,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;IACtE,CAAC;IACD,IAAI,CAAC,aAAa,IAAI,OAAO,aAAa,KAAK,QAAQ,EAAE,CAAC;QACxD,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;IAC/D,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,YAAY,CAAC,IAAI,YAAY,GAAG,CAAC,EAAE,CAAC;QACxD,MAAM,IAAI,KAAK,CACb,6DAA6D,YAAY,GAAG,CAC7E,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,CACT,MAAM,EAAE,EACR,0BAA0B,EAC1B,GAAG,oBAAoB,IAAI,aAAa,EAAE,EAC1C,WAAW,YAAY,MAAM,CAC9B,CAAC;AACJ,CAAC"}
@@ -37,8 +37,21 @@ export interface ClassifiedSection {
37
37
  kind: string;
38
38
  /** Short human-readable title for the section. */
39
39
  title: string;
40
- /** The section's body text — server-reconstructed via `documentText.slice(sourceStart, sourceEnd)`. */
41
- body: string;
40
+ /**
41
+ * Section body text — omit on BOTH paths. The writer derives body
42
+ * server-side via `cachedText.slice(sourceStart, sourceEnd)`. Any value
43
+ * supplied here is logged and ignored; it must never be written to Neo4j.
44
+ *
45
+ * - Document path: source text is `extractCache.get(attachmentId).text`,
46
+ * populated by `memory-ingest-extract` from the file on disk.
47
+ * - Conversation-archive path: source text is the per-session tempfile
48
+ * written by `conversation-archive-ingest.mjs` at
49
+ * `turnTextCachePath(conversationIdentity, archiveSha256, sessionIndex)`.
50
+ *
51
+ * Tasks 465 (document path) and 468 (chat path) closed the LLM-supplied
52
+ * body vector on both sides — the classifier emits offsets only.
53
+ */
54
+ body?: string;
42
55
  /** 1-3 sentence summary, ≤500 chars; stored as `:Section.summary`. */
43
56
  summary: string;
44
57
  /** Whole-document character offsets — inclusive start, exclusive end. */
@@ -1 +1 @@
1
- {"version":3,"file":"section-types.d.ts","sourceRoot":"","sources":["../../src/lib/section-types.ts"],"names":[],"mappings":"AAQA,8EAA8E;AAC9E,eAAO,MAAM,sBAAsB,wEAMzB,CAAC;AAEX,kFAAkF;AAClF,eAAO,MAAM,wBAAwB,yJAW3B,CAAC;AAEX,0GAA0G;AAC1G,eAAO,MAAM,sBAAsB,4SAqBzB,CAAC;AAEX,gFAAgF;AAChF,eAAO,MAAM,qBAAqB,sBAAuB,CAAC;AAE1D,sFAAsF;AACtF,eAAO,MAAM,kBAAkB,EAAG,OAAgB,CAAC;AAEnD,+DAA+D;AAC/D,MAAM,MAAM,mBAAmB,GAAG,aAAa,GAAG,WAAW,CAAC;AAE9D,mEAAmE;AACnE,MAAM,MAAM,oBAAoB,GAAG,UAAU,GAAG,UAAU,CAAC;AAE3D,kFAAkF;AAClF,MAAM,WAAW,iBAAiB;IAChC,8DAA8D;IAC9D,IAAI,EAAE,MAAM,CAAC;IACb,sCAAsC;IACtC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,oDAAoD;IACpD,IAAI,EAAE;QACJ,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,EAAE,oBAAoB,CAAC;QAChC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACtC,CAAC;IACF;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,0FAA0F;AAC1F,MAAM,WAAW,iBAAiB;IAChC,yFAAyF;IACzF,IAAI,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,KAAK,EAAE,MAAM,CAAC;IACd,uGAAuG;IACvG,IAAI,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,OAAO,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,iFAAiF;IACjF,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC;;;;OAIG;IACH,UAAU,EAAE;QACV,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,EAAE,mBAAmB,CAAC;QAC/B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACtC,GAAG,IAAI,CAAC;IACT,oFAAoF;IACpF,OAAO,CAAC,EAAE,iBAAiB,EAAE,CAAC;IAC9B,kFAAkF;IAClF,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,6EAA6E;IAC7E,KAAK,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,UAAU,GAAG,UAAU,CAAC;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC1C,wEAAwE;IACxE,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,kFAAkF;AAClF,MAAM,WAAW,gBAAgB;IAC/B,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,gBAAgB,EAAE,eAAe,EAAE,CAAC;IACpC,aAAa,CAAC,EAAE,sBAAsB,EAAE,CAAC;IACzC,iFAAiF;IACjF,mBAAmB,EAAE,MAAM,CAAC;CAC7B"}
1
+ {"version":3,"file":"section-types.d.ts","sourceRoot":"","sources":["../../src/lib/section-types.ts"],"names":[],"mappings":"AAQA,8EAA8E;AAC9E,eAAO,MAAM,sBAAsB,wEAMzB,CAAC;AAEX,kFAAkF;AAClF,eAAO,MAAM,wBAAwB,yJAW3B,CAAC;AAEX,0GAA0G;AAC1G,eAAO,MAAM,sBAAsB,4SAqBzB,CAAC;AAEX,gFAAgF;AAChF,eAAO,MAAM,qBAAqB,sBAAuB,CAAC;AAE1D,sFAAsF;AACtF,eAAO,MAAM,kBAAkB,EAAG,OAAgB,CAAC;AAEnD,+DAA+D;AAC/D,MAAM,MAAM,mBAAmB,GAAG,aAAa,GAAG,WAAW,CAAC;AAE9D,mEAAmE;AACnE,MAAM,MAAM,oBAAoB,GAAG,UAAU,GAAG,UAAU,CAAC;AAE3D,kFAAkF;AAClF,MAAM,WAAW,iBAAiB;IAChC,8DAA8D;IAC9D,IAAI,EAAE,MAAM,CAAC;IACb,sCAAsC;IACtC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,oDAAoD;IACpD,IAAI,EAAE;QACJ,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,EAAE,oBAAoB,CAAC;QAChC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACtC,CAAC;IACF;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,0FAA0F;AAC1F,MAAM,WAAW,iBAAiB;IAChC,yFAAyF;IACzF,IAAI,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,KAAK,EAAE,MAAM,CAAC;IACd;;;;;;;;;;;;;OAaG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sEAAsE;IACtE,OAAO,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,iFAAiF;IACjF,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC;;;;OAIG;IACH,UAAU,EAAE;QACV,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,EAAE,mBAAmB,CAAC;QAC/B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACtC,GAAG,IAAI,CAAC;IACT,oFAAoF;IACpF,OAAO,CAAC,EAAE,iBAAiB,EAAE,CAAC;IAC9B,kFAAkF;IAClF,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,6EAA6E;IAC7E,KAAK,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,UAAU,GAAG,UAAU,CAAC;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC1C,wEAAwE;IACxE,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,kFAAkF;AAClF,MAAM,WAAW,gBAAgB;IAC/B,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,gBAAgB,EAAE,eAAe,EAAE,CAAC;IACpC,aAAa,CAAC,EAAE,sBAAsB,EAAE,CAAC;IACzC,iFAAiF;IACjF,mBAAmB,EAAE,MAAM,CAAC;CAC7B"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=memory-ingest-body-server-sliced.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-ingest-body-server-sliced.test.d.ts","sourceRoot":"","sources":["../../../src/tools/__tests__/memory-ingest-body-server-sliced.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,95 @@
1
+ // Task 465 — :Section.body is server-derived from offsets, not agent-supplied.
2
+ //
3
+ // The classifier emits (sourceStart, sourceEnd) into the cached text. The
4
+ // server slices that text to produce :Section.body so a hallucinated /
5
+ // paraphrased / summarised body cannot reach Neo4j.
6
+ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
7
+ import { deriveSectionBodies } from "../memory-ingest.js";
8
+ describe("deriveSectionBodies", () => {
9
+ let logSpy;
10
+ beforeEach(() => {
11
+ logSpy = vi.spyOn(console, "error").mockImplementation(() => { });
12
+ });
13
+ afterEach(() => {
14
+ logSpy.mockRestore();
15
+ });
16
+ it("(a) returns server-derived slices on the happy path (multi-section)", () => {
17
+ const cachedText = "Section One body.\nSection Two body.\nSection Three body.";
18
+ const sections = [
19
+ { sourceStart: 0, sourceEnd: 17 },
20
+ { sourceStart: 18, sourceEnd: 35 },
21
+ { sourceStart: 36, sourceEnd: 55 },
22
+ ];
23
+ const result = deriveSectionBodies(sections, cachedText, "att-happy");
24
+ expect(result).toEqual([
25
+ "Section One body.",
26
+ "Section Two body.",
27
+ "Section Three body.",
28
+ ]);
29
+ expect(logSpy).not.toHaveBeenCalled();
30
+ });
31
+ it("(b) returns empty string and warns on a zero-width section", () => {
32
+ const cachedText = "Some text.";
33
+ const sections = [{ sourceStart: 5, sourceEnd: 5 }];
34
+ const result = deriveSectionBodies(sections, cachedText, "att-zero");
35
+ expect(result).toEqual([""]);
36
+ expect(logSpy).toHaveBeenCalledTimes(1);
37
+ expect(logSpy.mock.calls[0][0]).toContain("zero-width section");
38
+ expect(logSpy.mock.calls[0][0]).toContain("sectionIndex=0");
39
+ expect(logSpy.mock.calls[0][0]).toContain("att-zero");
40
+ });
41
+ it("(c) throws on reversed offsets (sourceStart > sourceEnd)", () => {
42
+ const cachedText = "Some text.";
43
+ const sections = [{ sourceStart: 7, sourceEnd: 3 }];
44
+ expect(() => deriveSectionBodies(sections, cachedText, "att-rev")).toThrow(/reversed offsets.*sectionIndex=0.*sourceStart=7.*sourceEnd=3.*att-rev/);
45
+ });
46
+ it("(d) throws on out-of-bounds sourceEnd", () => {
47
+ const cachedText = "Short text."; // length 11
48
+ const sections = [{ sourceStart: 0, sourceEnd: 100 }];
49
+ expect(() => deriveSectionBodies(sections, cachedText, "att-oob")).toThrow(/out-of-bounds sourceEnd.*sectionIndex=0.*sourceEnd=100.*textLength=11/);
50
+ });
51
+ it("(d2) throws on negative sourceStart", () => {
52
+ const cachedText = "Some text.";
53
+ const sections = [{ sourceStart: -1, sourceEnd: 5 }];
54
+ expect(() => deriveSectionBodies(sections, cachedText, "att-neg")).toThrow(/invalid sourceStart.*sourceStart=-1.*att-neg/);
55
+ });
56
+ it("(e) logs and ignores an agent-supplied body — returns slice, not agent text", () => {
57
+ const cachedText = "Real document content here.";
58
+ const agentBody = "HALLUCINATED SUMMARY THAT MUST NOT BE STORED";
59
+ const sections = [{ sourceStart: 0, sourceEnd: 27, body: agentBody }];
60
+ const result = deriveSectionBodies(sections, cachedText, "att-hall");
61
+ expect(result).toEqual(["Real document content here."]);
62
+ expect(result[0]).not.toContain("HALLUCINATED");
63
+ expect(logSpy).toHaveBeenCalledTimes(1);
64
+ expect(logSpy.mock.calls[0][0]).toContain("agent body suppressed");
65
+ expect(logSpy.mock.calls[0][0]).toContain(`agentBodyLength=${agentBody.length}`);
66
+ expect(logSpy.mock.calls[0][0]).toContain("agentBodyPreview=");
67
+ expect(logSpy.mock.calls[0][0]).toContain("HALLUCINATED");
68
+ expect(logSpy.mock.calls[0][0]).toContain("att-hall");
69
+ });
70
+ it("(f) correctly slices each section in a multi-section document with gaps", () => {
71
+ const cachedText = "AAA\n\nBBB\n\nCCC";
72
+ const sections = [
73
+ { sourceStart: 0, sourceEnd: 3 },
74
+ { sourceStart: 5, sourceEnd: 8 },
75
+ { sourceStart: 10, sourceEnd: 13 },
76
+ ];
77
+ const result = deriveSectionBodies(sections, cachedText, "att-multi");
78
+ expect(result).toEqual(["AAA", "BBB", "CCC"]);
79
+ });
80
+ it("includes the failing sectionIndex when one of several sections is invalid", () => {
81
+ const cachedText = "Hello world.";
82
+ const sections = [
83
+ { sourceStart: 0, sourceEnd: 5 },
84
+ { sourceStart: 10, sourceEnd: 6 }, // reversed
85
+ ];
86
+ expect(() => deriveSectionBodies(sections, cachedText, "att-mix")).toThrow(/sectionIndex=1/);
87
+ });
88
+ it("uses default sourceKey='unknown' when omitted (still produces a grep-able log)", () => {
89
+ const cachedText = "abc";
90
+ const sections = [{ sourceStart: 0, sourceEnd: 0 }];
91
+ deriveSectionBodies(sections, cachedText);
92
+ expect(logSpy.mock.calls[0][0]).toContain("sourceKey=unknown");
93
+ });
94
+ });
95
+ //# sourceMappingURL=memory-ingest-body-server-sliced.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-ingest-body-server-sliced.test.js","sourceRoot":"","sources":["../../../src/tools/__tests__/memory-ingest-body-server-sliced.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=memory-ingest-chat-body-server-sliced.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-ingest-chat-body-server-sliced.test.d.ts","sourceRoot":"","sources":["../../../src/tools/__tests__/memory-ingest-chat-body-server-sliced.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,124 @@
1
+ // Task 468 — conversation-archive path slices :Section.body server-side from
2
+ // a non-LLM source of turnText (the bin's per-session tempfile). Mirrors the
3
+ // document path's Task 465 contract on the chat side.
4
+ //
5
+ // These tests cover the cross-process plumbing: the cache-path helper that
6
+ // both bin and writer use to construct the deterministic tempfile path, plus
7
+ // the slicer's behaviour when keyed by `conversationIdentity`.
8
+ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
9
+ import { tmpdir } from "node:os";
10
+ import { sep } from "node:path";
11
+ import { turnTextCachePath, TURN_TEXT_CACHE_DIR_PREFIX, } from "../../lib/conversation-pipeline/turn-text-cache-path.js";
12
+ import { deriveSectionBodies } from "../memory-ingest.js";
13
+ describe("turnTextCachePath", () => {
14
+ it("constructs a deterministic path under os.tmpdir() keyed by (cid, sha, sessionIndex)", () => {
15
+ const path = turnTextCachePath({
16
+ conversationIdentity: "cid123",
17
+ archiveSha256: "sha456",
18
+ sessionIndex: 1,
19
+ });
20
+ expect(path).toBe([tmpdir(), TURN_TEXT_CACHE_DIR_PREFIX, "cid123-sha456", "session-1.txt"].join(sep));
21
+ });
22
+ it("produces distinct paths for distinct session indices on the same archive", () => {
23
+ const a = turnTextCachePath({
24
+ conversationIdentity: "cid",
25
+ archiveSha256: "sha",
26
+ sessionIndex: 1,
27
+ });
28
+ const b = turnTextCachePath({
29
+ conversationIdentity: "cid",
30
+ archiveSha256: "sha",
31
+ sessionIndex: 2,
32
+ });
33
+ expect(a).not.toBe(b);
34
+ });
35
+ it("produces distinct dirs for distinct archives on the same conversation", () => {
36
+ const a = turnTextCachePath({
37
+ conversationIdentity: "cid",
38
+ archiveSha256: "sha-old",
39
+ sessionIndex: 1,
40
+ });
41
+ const b = turnTextCachePath({
42
+ conversationIdentity: "cid",
43
+ archiveSha256: "sha-new",
44
+ sessionIndex: 1,
45
+ });
46
+ // Differ in the per-archive directory segment, not the file basename.
47
+ expect(a).not.toBe(b);
48
+ expect(a.endsWith("session-1.txt")).toBe(true);
49
+ expect(b.endsWith("session-1.txt")).toBe(true);
50
+ });
51
+ it("throws on missing conversationIdentity", () => {
52
+ expect(() => turnTextCachePath({
53
+ conversationIdentity: "",
54
+ archiveSha256: "sha",
55
+ sessionIndex: 1,
56
+ })).toThrow(/conversationIdentity/);
57
+ });
58
+ it("throws on missing archiveSha256", () => {
59
+ expect(() => turnTextCachePath({
60
+ conversationIdentity: "cid",
61
+ archiveSha256: "",
62
+ sessionIndex: 1,
63
+ })).toThrow(/archiveSha256/);
64
+ });
65
+ it("throws on non-integer sessionIndex", () => {
66
+ expect(() => turnTextCachePath({
67
+ conversationIdentity: "cid",
68
+ archiveSha256: "sha",
69
+ sessionIndex: 1.5,
70
+ })).toThrow(/sessionIndex/);
71
+ });
72
+ it("throws on sessionIndex < 1", () => {
73
+ expect(() => turnTextCachePath({
74
+ conversationIdentity: "cid",
75
+ archiveSha256: "sha",
76
+ sessionIndex: 0,
77
+ })).toThrow(/sessionIndex/);
78
+ });
79
+ });
80
+ describe("deriveSectionBodies on the conversation-archive path", () => {
81
+ let logSpy;
82
+ beforeEach(() => {
83
+ logSpy = vi.spyOn(console, "error").mockImplementation(() => { });
84
+ });
85
+ afterEach(() => {
86
+ logSpy.mockRestore();
87
+ });
88
+ // Mirror of Task 465 test (e) on the chat side — the agent-supplied `body`
89
+ // is the LLM-paraphrase vector this task exists to close. The slicer is
90
+ // shared between paths but the chat-path identity flows through as
91
+ // `sourceKey` so logs stay grep-able by conversationIdentity.
92
+ it("suppresses an agent-supplied body and returns the verbatim turnText slice", () => {
93
+ const turnText = "[2024-01-15T09:30:00+00:00] Joel: Morning\n[2024-01-15T09:31:00+00:00] Adam: hey";
94
+ const cid = "cid-abc";
95
+ const sections = [
96
+ {
97
+ sourceStart: 0,
98
+ sourceEnd: 42,
99
+ body: "PARAPHRASED SUMMARY — Joel greets Adam in the morning",
100
+ },
101
+ ];
102
+ const result = deriveSectionBodies(sections, turnText, cid);
103
+ expect(result).toEqual([turnText.slice(0, 42)]);
104
+ expect(result[0]).not.toContain("PARAPHRASED");
105
+ expect(logSpy.mock.calls[0][0]).toContain("agent body suppressed");
106
+ expect(logSpy.mock.calls[0][0]).toContain(`sourceKey=${cid}`);
107
+ });
108
+ it("slices multiple sections of a chat turnText into verbatim bodies", () => {
109
+ const turnText = "[2024-01-15T09:30:00+00:00] Joel: Morning\n[2024-01-15T09:31:00+00:00] Adam: hey";
110
+ const sections = [
111
+ { sourceStart: 0, sourceEnd: 41 },
112
+ { sourceStart: 42, sourceEnd: turnText.length },
113
+ ];
114
+ const result = deriveSectionBodies(sections, turnText, "cid-multi");
115
+ expect(result[0]).toBe(turnText.slice(0, 41));
116
+ expect(result[1]).toBe(turnText.slice(42, turnText.length));
117
+ });
118
+ it("throws with conversationIdentity in the error when offsets exceed turnText length", () => {
119
+ const turnText = "short";
120
+ const sections = [{ sourceStart: 0, sourceEnd: 1000 }];
121
+ expect(() => deriveSectionBodies(sections, turnText, "cid-oob")).toThrow(/out-of-bounds sourceEnd.*sourceEnd=1000.*textLength=5.*cid-oob/);
122
+ });
123
+ });
124
+ //# sourceMappingURL=memory-ingest-chat-body-server-sliced.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-ingest-chat-body-server-sliced.test.js","sourceRoot":"","sources":["../../../src/tools/__tests__/memory-ingest-chat-body-server-sliced.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=memory-ingest-section-properties-strip.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-ingest-section-properties-strip.test.d.ts","sourceRoot":"","sources":["../../../src/tools/__tests__/memory-ingest-section-properties-strip.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,104 @@
1
+ // Task 469 — section.properties reserved-key strip.
2
+ //
3
+ // `section.properties` is an unconstrained Zod record. The writer spreads it
4
+ // onto every node it creates, with `baseProps` stamped on top. Any writer-
5
+ // owned key the agent puts into `properties` would land verbatim on the node
6
+ // UNLESS `baseProps` also carries that key. The most attractive smuggle slot
7
+ // is `summary` — `:Section.summary` is the operator-visible compressed view.
8
+ //
9
+ // These tests cover the strip helper directly (mirrors the Task 465 pattern
10
+ // where deriveDocumentBodies is exported and tested in isolation).
11
+ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
12
+ import { stripReservedSectionProperties, RESERVED_SECTION_PROPERTY_KEYS, } from "../memory-ingest.js";
13
+ describe("stripReservedSectionProperties", () => {
14
+ let logSpy;
15
+ beforeEach(() => {
16
+ logSpy = vi.spyOn(console, "error").mockImplementation(() => { });
17
+ });
18
+ afterEach(() => {
19
+ logSpy.mockRestore();
20
+ });
21
+ it("strips properties.summary and logs the smuggle attempt", () => {
22
+ const result = stripReservedSectionProperties({ summary: "SMUGGLED PARAPHRASE", customTag: "keep" }, { kind: "Chapter", identity: "attachmentId=att-1" });
23
+ expect(result).toEqual({ customTag: "keep" });
24
+ expect(result.summary).toBeUndefined();
25
+ expect(logSpy).toHaveBeenCalledTimes(1);
26
+ expect(logSpy.mock.calls[0][0]).toContain("reserved-property-keys-stripped");
27
+ expect(logSpy.mock.calls[0][0]).toContain("kind=Chapter");
28
+ expect(logSpy.mock.calls[0][0]).toContain("keys=summary");
29
+ expect(logSpy.mock.calls[0][0]).toContain("attachmentId=att-1");
30
+ });
31
+ it("passes non-reserved keys through and emits no log when nothing is stripped", () => {
32
+ const result = stripReservedSectionProperties({ startedAt: "2026-01-01", endedAt: "2026-05-01", role: "Engineer" }, { kind: "Position", identity: "attachmentId=att-2" });
33
+ expect(result).toEqual({
34
+ startedAt: "2026-01-01",
35
+ endedAt: "2026-05-01",
36
+ role: "Engineer",
37
+ });
38
+ expect(logSpy).not.toHaveBeenCalled();
39
+ });
40
+ it("strips every reserved key in one pass and lists them all in the log", () => {
41
+ const smuggle = {
42
+ summary: "x",
43
+ title: "x",
44
+ body: "x",
45
+ embedding: [0.1],
46
+ accountId: "x",
47
+ source: "x",
48
+ keep: "yes",
49
+ };
50
+ const result = stripReservedSectionProperties(smuggle, {
51
+ kind: "Other",
52
+ identity: "conversationIdentity=conv-1 archiveSha256=abc",
53
+ });
54
+ expect(result).toEqual({ keep: "yes" });
55
+ expect(logSpy).toHaveBeenCalledTimes(1);
56
+ const logLine = logSpy.mock.calls[0][0];
57
+ for (const k of ["summary", "title", "body", "embedding", "accountId", "source"]) {
58
+ expect(logLine).toContain(k);
59
+ }
60
+ expect(logLine).toContain("conversationIdentity=conv-1");
61
+ expect(logLine).toContain("archiveSha256=abc");
62
+ });
63
+ it("does not mutate the input object", () => {
64
+ const input = { summary: "smuggle", keep: "yes" };
65
+ stripReservedSectionProperties(input, {
66
+ kind: "Chapter",
67
+ identity: "attachmentId=att-3",
68
+ });
69
+ expect(input).toEqual({ summary: "smuggle", keep: "yes" });
70
+ });
71
+ it("handles an empty properties bag", () => {
72
+ const result = stripReservedSectionProperties({}, { kind: "Chapter", identity: "attachmentId=att-4" });
73
+ expect(result).toEqual({});
74
+ expect(logSpy).not.toHaveBeenCalled();
75
+ });
76
+ it("reserved set covers every writer-owned key named in the Task 469 brief", () => {
77
+ // The brief enumerates the 17 keys baseProps stamps across both paths.
78
+ // If a key is added to baseProps later and not added here, that key
79
+ // becomes a new smuggle channel.
80
+ const brief = [
81
+ "accountId",
82
+ "title",
83
+ "body",
84
+ "bodyPreview",
85
+ "summary",
86
+ "embedding",
87
+ "position",
88
+ "scope",
89
+ "createdAt",
90
+ "updatedAt",
91
+ "createdByAgent",
92
+ "createdBySource",
93
+ "createdBySession",
94
+ "source",
95
+ "sourceDocumentId",
96
+ "archiveSha256",
97
+ "archiveSourceFile",
98
+ ];
99
+ for (const k of brief) {
100
+ expect(RESERVED_SECTION_PROPERTY_KEYS.has(k)).toBe(true);
101
+ }
102
+ });
103
+ });
104
+ //# sourceMappingURL=memory-ingest-section-properties-strip.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-ingest-section-properties-strip.test.js","sourceRoot":"","sources":["../../../src/tools/__tests__/memory-ingest-section-properties-strip.test.ts"],"names":[],"mappings":"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"}
@@ -4,10 +4,18 @@ export interface IngestWebParams {
4
4
  url: string;
5
5
  content: string;
6
6
  scope?: string;
7
+ /**
8
+ * Caller-supplied editorial title (parsed from HTML `<title>` / `og:title`
9
+ * by the dispatching librarian). When present, used verbatim (length-capped
10
+ * to 200 chars). Falls through to H1 → URL-slug → URL when absent or empty.
11
+ */
12
+ title?: string;
7
13
  }
14
+ export type TitleSource = "caller" | "h1" | "url-slug" | "url";
8
15
  export interface IngestWebResult {
9
16
  attachmentId: string;
10
17
  title: string;
18
+ titleSource: TitleSource;
11
19
  extract: ExtractResult;
12
20
  existingDocument?: {
13
21
  nodeId: string;
@@ -16,5 +24,9 @@ export interface IngestWebResult {
16
24
  ingestedAt: string;
17
25
  };
18
26
  }
27
+ export declare function extractTitle(content: string, url: string, callerTitle?: string): {
28
+ title: string;
29
+ source: TitleSource;
30
+ };
19
31
  export declare function memoryIngestWeb(params: IngestWebParams): Promise<IngestWebResult>;
20
32
  //# sourceMappingURL=memory-ingest-web.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"memory-ingest-web.d.ts","sourceRoot":"","sources":["../../src/tools/memory-ingest-web.ts"],"names":[],"mappings":"AAKA,OAAO,EAAuB,KAAK,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAoBrF,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,aAAa,CAAC;IACvB,gBAAgB,CAAC,EAAE;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AA6CD,wBAAsB,eAAe,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC,CAMvF"}
1
+ {"version":3,"file":"memory-ingest-web.d.ts","sourceRoot":"","sources":["../../src/tools/memory-ingest-web.ts"],"names":[],"mappings":"AAKA,OAAO,EAAuB,KAAK,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAqBrF,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,IAAI,GAAG,UAAU,GAAG,KAAK,CAAC;AAE/D,MAAM,WAAW,eAAe;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,WAAW,CAAC;IACzB,OAAO,EAAE,aAAa,CAAC;IACvB,gBAAgB,CAAC,EAAE;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AAQD,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,WAAW,CAAC,EAAE,MAAM,GACnB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,CAgBxC;AAgDD,wBAAsB,eAAe,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC,CAMvF"}
@@ -5,19 +5,44 @@ import { tmpdir } from "node:os";
5
5
  import { getSession } from "../lib/neo4j.js";
6
6
  import { memoryIngestExtract } from "./memory-ingest-extract.js";
7
7
  import { withWriteEvent } from "../lib/log-ingest.js";
8
- function extractTitle(content, url) {
8
+ const TITLE_MAX = 200;
9
+ function cap(s) {
10
+ return s.length > TITLE_MAX ? s.slice(0, TITLE_MAX) : s;
11
+ }
12
+ export function extractTitle(content, url, callerTitle) {
13
+ if (callerTitle) {
14
+ const trimmed = callerTitle.trim();
15
+ if (trimmed.length > 0)
16
+ return { title: cap(trimmed), source: "caller" };
17
+ }
9
18
  const h1Match = content.match(/^#\s+(.+)$/m);
10
19
  if (h1Match) {
11
- const title = h1Match[1].trim();
12
- return title.length > 200 ? title.slice(0, 200) : title;
20
+ const t = h1Match[1].trim();
21
+ if (t.length > 0)
22
+ return { title: cap(t), source: "h1" };
23
+ }
24
+ const slug = urlSlug(url);
25
+ if (slug)
26
+ return { title: cap(slug), source: "url-slug" };
27
+ return { title: url, source: "url" };
28
+ }
29
+ function urlSlug(url) {
30
+ let pathname;
31
+ try {
32
+ pathname = new URL(url).pathname;
13
33
  }
14
- const firstLine = content.split("\n").find((line) => line.trim().length > 0);
15
- if (firstLine) {
16
- const cleaned = firstLine.replace(/^[#*_>\-\s]+/, "").trim();
17
- if (cleaned.length > 0)
18
- return cleaned.length > 200 ? cleaned.slice(0, 200) : cleaned;
34
+ catch {
35
+ return null;
19
36
  }
20
- return url;
37
+ const segments = pathname.split("/").filter((s) => s.length > 0);
38
+ if (segments.length === 0)
39
+ return null;
40
+ const last = segments[segments.length - 1];
41
+ const cleaned = last
42
+ .replace(/\.[a-zA-Z0-9]{1,5}$/, "")
43
+ .replace(/[-_]+/g, " ")
44
+ .trim();
45
+ return cleaned.length > 0 ? cleaned : null;
21
46
  }
22
47
  async function checkExistingDocument(url, accountId) {
23
48
  const session = getSession();
@@ -48,7 +73,7 @@ export async function memoryIngestWeb(params) {
48
73
  return withWriteEvent({ tool: "memory-ingest-web", accountId: params.accountId, name: params.url }, () => memoryIngestWebImpl(params), (r) => ({ elementId: r.attachmentId, name: r.title }));
49
74
  }
50
75
  async function memoryIngestWebImpl(params) {
51
- const { accountId, url, content, scope } = params;
76
+ const { accountId, url, content, scope, title: callerTitle } = params;
52
77
  const t0 = Date.now();
53
78
  const log = (stage, detail) => console.error(`[memory-ingest-web] [${url.slice(0, 60)}] ${stage}${detail ? ` — ${detail}` : ""} (${Date.now() - t0}ms)`);
54
79
  log("start", `${content.length} chars, scope=${scope ?? "public"}`);
@@ -62,8 +87,8 @@ async function memoryIngestWebImpl(params) {
62
87
  if (existingDocument) {
63
88
  log("dedup-found", `existing: ${existingDocument.nodeId.slice(0, 12)}, ingested ${existingDocument.ingestedAt}`);
64
89
  }
65
- const title = extractTitle(trimmed, url);
66
- log("title", title.slice(0, 80));
90
+ const { title, source: titleSource } = extractTitle(trimmed, url, callerTitle);
91
+ log("title", `${title.slice(0, 80)} (title-source=${titleSource})`);
67
92
  const attachmentId = randomUUID();
68
93
  const tempPath = join(tmpdir(), `maxy-web-${attachmentId}.md`);
69
94
  try {
@@ -77,7 +102,7 @@ async function memoryIngestWebImpl(params) {
77
102
  attachmentId,
78
103
  });
79
104
  log("extract-done", `${extract.textLength} chars cached`);
80
- return { attachmentId, title, extract, existingDocument };
105
+ return { attachmentId, title, titleSource, extract, existingDocument };
81
106
  }
82
107
  finally {
83
108
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"memory-ingest-web.js","sourceRoot":"","sources":["../../src/tools/memory-ingest-web.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"memory-ingest-web.js","sourceRoot":"","sources":["../../src/tools/memory-ingest-web.ts"],"names":[],"mappings":"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"}