@rubytech/create-maxy-code 0.1.159 → 0.1.161

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 (146) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/lib/graph-search/src/__tests__/fulltext-coverage.test.ts +48 -17
  3. package/payload/platform/neo4j/schema.cypher +59 -36
  4. package/payload/platform/plugins/.claude-plugin/marketplace.json +5 -0
  5. package/payload/platform/plugins/admin/hooks/archive-ingest-surface-gate.sh +2 -2
  6. package/payload/platform/plugins/admin/skills/business-profile/SKILL.md +1 -1
  7. package/payload/platform/plugins/docs/references/plugins-guide.md +3 -2
  8. package/payload/platform/plugins/email/.claude-plugin/plugin.json +1 -1
  9. package/payload/platform/plugins/email/PLUGIN.md +5 -1
  10. package/payload/platform/plugins/email/mcp/dist/index.js +25 -0
  11. package/payload/platform/plugins/email/mcp/dist/index.js.map +1 -1
  12. package/payload/platform/plugins/email/mcp/dist/lib/conversation-archive-dispatch.d.ts +8 -0
  13. package/payload/platform/plugins/email/mcp/dist/lib/conversation-archive-dispatch.d.ts.map +1 -0
  14. package/payload/platform/plugins/email/mcp/dist/lib/conversation-archive-dispatch.js +177 -0
  15. package/payload/platform/plugins/email/mcp/dist/lib/conversation-archive-dispatch.js.map +1 -0
  16. package/payload/platform/plugins/email/mcp/dist/lib/graph.d.ts +6 -54
  17. package/payload/platform/plugins/email/mcp/dist/lib/graph.d.ts.map +1 -1
  18. package/payload/platform/plugins/email/mcp/dist/lib/graph.js +12 -300
  19. package/payload/platform/plugins/email/mcp/dist/lib/graph.js.map +1 -1
  20. package/payload/platform/plugins/email/mcp/dist/tools/email-classify.d.ts +6 -0
  21. package/payload/platform/plugins/email/mcp/dist/tools/email-classify.d.ts.map +1 -0
  22. package/payload/platform/plugins/email/mcp/dist/tools/email-classify.js +89 -0
  23. package/payload/platform/plugins/email/mcp/dist/tools/email-classify.js.map +1 -0
  24. package/payload/platform/plugins/email/mcp/dist/tools/email-ingest.d.ts.map +1 -1
  25. package/payload/platform/plugins/email/mcp/dist/tools/email-ingest.js +11 -6
  26. package/payload/platform/plugins/email/mcp/dist/tools/email-ingest.js.map +1 -1
  27. package/payload/platform/plugins/linkedin-import/skills/linkedin-import/SKILL.md +10 -37
  28. package/payload/platform/plugins/linkedin-import/skills/linkedin-import/references/connections.md +1 -0
  29. package/payload/platform/plugins/memory/.claude-plugin/plugin.json +1 -1
  30. package/payload/platform/plugins/memory/PLUGIN.md +10 -1
  31. package/payload/platform/plugins/memory/bin/conversation-archive-ingest.mjs +139 -439
  32. package/payload/platform/plugins/memory/bin/conversation-archive-ingest.sh +15 -47
  33. package/payload/platform/plugins/memory/mcp/dist/index.js +168 -29
  34. package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
  35. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/compiled-truth-revision.test.js +43 -27
  36. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/compiled-truth-revision.test.js.map +1 -1
  37. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/label-delete-gate.test.d.ts +2 -0
  38. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/label-delete-gate.test.d.ts.map +1 -0
  39. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/label-delete-gate.test.js +62 -0
  40. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/label-delete-gate.test.js.map +1 -0
  41. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-cypher-drift.test.js +5 -0
  42. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-cypher-drift.test.js.map +1 -1
  43. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/typed-edge-schema.test.js +1 -1
  44. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/typed-edge-schema.test.js.map +1 -1
  45. package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-revision.d.ts +42 -25
  46. package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-revision.d.ts.map +1 -1
  47. package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-revision.js +23 -17
  48. package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-revision.js.map +1 -1
  49. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/email.d.ts +3 -0
  50. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/email.d.ts.map +1 -0
  51. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/email.js +61 -0
  52. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/email.js.map +1 -0
  53. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/index.d.ts +1 -0
  54. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/index.d.ts.map +1 -1
  55. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/index.js +3 -0
  56. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/index.js.map +1 -1
  57. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/types.d.ts +1 -1
  58. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/types.d.ts.map +1 -1
  59. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/types.js +1 -0
  60. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/types.js.map +1 -1
  61. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/derive-keys.d.ts +2 -0
  62. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/derive-keys.d.ts.map +1 -1
  63. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/derive-keys.js +9 -3
  64. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/derive-keys.js.map +1 -1
  65. package/payload/platform/plugins/memory/mcp/dist/lib/kd-classify-gate.d.ts +41 -0
  66. package/payload/platform/plugins/memory/mcp/dist/lib/kd-classify-gate.d.ts.map +1 -0
  67. package/payload/platform/plugins/memory/mcp/dist/lib/kd-classify-gate.js +71 -0
  68. package/payload/platform/plugins/memory/mcp/dist/lib/kd-classify-gate.js.map +1 -0
  69. package/payload/platform/plugins/memory/mcp/dist/lib/label-delete-gate.d.ts +18 -0
  70. package/payload/platform/plugins/memory/mcp/dist/lib/label-delete-gate.d.ts.map +1 -0
  71. package/payload/platform/plugins/memory/mcp/dist/lib/label-delete-gate.js +31 -0
  72. package/payload/platform/plugins/memory/mcp/dist/lib/label-delete-gate.js.map +1 -0
  73. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-derive-insights.test.d.ts +5 -0
  74. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-derive-insights.test.d.ts.map +1 -1
  75. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-derive-insights.test.js +162 -78
  76. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-derive-insights.test.js.map +1 -1
  77. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-enrich-rejection.test.js +1 -1
  78. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-enrich-rejection.test.js.map +1 -1
  79. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-source-agnosticism.test.js +2 -0
  80. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-source-agnosticism.test.js.map +1 -1
  81. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-compiled-truth-history.test.js +2 -0
  82. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-compiled-truth-history.test.js.map +1 -1
  83. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-delete-emit.test.js +6 -0
  84. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-delete-emit.test.js.map +1 -1
  85. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-delete-reserved-label.test.d.ts +2 -0
  86. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-delete-reserved-label.test.d.ts.map +1 -0
  87. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-delete-reserved-label.test.js +141 -0
  88. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-delete-reserved-label.test.js.map +1 -0
  89. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-empty-trash-reserved-label.test.d.ts +2 -0
  90. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-empty-trash-reserved-label.test.d.ts.map +1 -0
  91. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-empty-trash-reserved-label.test.js +86 -0
  92. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-empty-trash-reserved-label.test.js.map +1 -0
  93. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.d.ts +33 -34
  94. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.d.ts.map +1 -1
  95. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.js +94 -273
  96. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.js.map +1 -1
  97. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-list-chunks.d.ts +43 -0
  98. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-list-chunks.d.ts.map +1 -0
  99. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-list-chunks.js +96 -0
  100. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-list-chunks.js.map +1 -0
  101. package/payload/platform/plugins/memory/mcp/dist/tools/kd-classify.d.ts +6 -0
  102. package/payload/platform/plugins/memory/mcp/dist/tools/kd-classify.d.ts.map +1 -0
  103. package/payload/platform/plugins/memory/mcp/dist/tools/kd-classify.js +73 -0
  104. package/payload/platform/plugins/memory/mcp/dist/tools/kd-classify.js.map +1 -0
  105. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.d.ts.map +1 -1
  106. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.js +0 -1
  107. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.js.map +1 -1
  108. package/payload/platform/plugins/memory/mcp/dist/tools/memory-compiled-truth-history.d.ts +6 -0
  109. package/payload/platform/plugins/memory/mcp/dist/tools/memory-compiled-truth-history.d.ts.map +1 -1
  110. package/payload/platform/plugins/memory/mcp/dist/tools/memory-compiled-truth-history.js +2 -0
  111. package/payload/platform/plugins/memory/mcp/dist/tools/memory-compiled-truth-history.js.map +1 -1
  112. package/payload/platform/plugins/memory/mcp/dist/tools/memory-delete.d.ts +8 -0
  113. package/payload/platform/plugins/memory/mcp/dist/tools/memory-delete.d.ts.map +1 -1
  114. package/payload/platform/plugins/memory/mcp/dist/tools/memory-delete.js +40 -5
  115. package/payload/platform/plugins/memory/mcp/dist/tools/memory-delete.js.map +1 -1
  116. package/payload/platform/plugins/memory/mcp/dist/tools/memory-empty-trash.d.ts +7 -0
  117. package/payload/platform/plugins/memory/mcp/dist/tools/memory-empty-trash.d.ts.map +1 -1
  118. package/payload/platform/plugins/memory/mcp/dist/tools/memory-empty-trash.js +17 -1
  119. package/payload/platform/plugins/memory/mcp/dist/tools/memory-empty-trash.js.map +1 -1
  120. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js +11 -0
  121. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js.map +1 -1
  122. package/payload/platform/plugins/memory/mcp/dist/tools/memory-node-exists.d.ts +28 -0
  123. package/payload/platform/plugins/memory/mcp/dist/tools/memory-node-exists.d.ts.map +1 -0
  124. package/payload/platform/plugins/memory/mcp/dist/tools/memory-node-exists.js +46 -0
  125. package/payload/platform/plugins/memory/mcp/dist/tools/memory-node-exists.js.map +1 -0
  126. package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.d.ts.map +1 -1
  127. package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.js +26 -0
  128. package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.js.map +1 -1
  129. package/payload/platform/plugins/memory/mcp/vitest.config.ts +5 -4
  130. package/payload/platform/plugins/memory/references/schema-base.md +4 -6
  131. package/payload/platform/plugins/memory/skills/conversation-archive/SKILL.md +100 -116
  132. package/payload/platform/plugins/memory/skills/conversation-archive-enrich/SKILL.md +74 -24
  133. package/payload/platform/plugins/notion-import/skills/notion-import/SKILL.md +1 -1
  134. package/payload/platform/plugins/obsidian-import/skills/obsidian-import/SKILL.md +1 -1
  135. package/payload/platform/plugins/substack-import/.claude-plugin/plugin.json +8 -0
  136. package/payload/platform/plugins/substack-import/PLUGIN.md +34 -0
  137. package/payload/platform/plugins/substack-import/skills/substack-import/SKILL.md +182 -0
  138. package/payload/platform/plugins/substack-import/skills/substack-import/references/archive-shape.md +68 -0
  139. package/payload/platform/plugins/substack-import/skills/substack-import/references/attachments.md +72 -0
  140. package/payload/platform/plugins/substack-import/skills/substack-import/references/engagement.md +61 -0
  141. package/payload/platform/plugins/substack-import/skills/substack-import/references/posts.md +80 -0
  142. package/payload/platform/plugins/substack-import/skills/substack-import/references/subscribers.md +74 -0
  143. package/payload/platform/plugins/x-import/skills/x-import/SKILL.md +1 -1
  144. package/payload/platform/templates/specialists/agents/archive-ingest-operator.md +45 -0
  145. package/payload/platform/templates/specialists/agents/database-operator.md +5 -17
  146. package/payload/platform/templates/specialists/agents/librarian.md +1 -1
@@ -1,43 +1,60 @@
1
1
  /**
2
- * Task 391 compiled-truth revision history.
2
+ * Compiled-truth revision history (Task 391, restored under Task 436).
3
3
  *
4
- * Single shared primitive that writes a new `:CompiledTruthRevision`
5
- * row and the `:HAS_REVISION` edge in the same transaction as the
6
- * caller's `compiledTruth` SET. Callers compose this Cypher block
7
- * into their own write statement so the SET and the revision MERGE
8
- * commit atomically — if either fails, neither lands.
4
+ * Single shared primitive that writes `:CompiledTruthRevision` rows
5
+ * and `:HAS_REVISION` edges in the same transaction as the caller's
6
+ * `compiledTruth` / `compiledTruthPublic` SET. Callers concatenate
7
+ * this fragment into their own write statement so the SET and the
8
+ * revision CREATEs commit atomically — if either fails, neither
9
+ * lands.
9
10
  *
10
- * Two source values are sanctioned: 'rewriter' (rewriter library
11
- * wrote it) and 'operator' (sanctioned operator-edit route — not yet
12
- * implemented; see follow-up Task 393).
11
+ * `twin` discriminates which property the row is the revision of —
12
+ * `"compiledTruth"` (private/admin twin) or `"compiledTruthPublic"`
13
+ * (public twin). Task 391 only had the private twin; Task 424 moved
14
+ * compiled-truth writes into a specialist that handles both twins,
15
+ * so the row needs a discriminator for history queries.
13
16
  *
14
- * Pruning is the dream-cycle's job (phase 5). This file never
15
- * deletes.
17
+ * `source` enum: `'rewriter'` (rewriter library wrote the truth) and
18
+ * `'operator'` (sanctioned operator-edit route — passed by the
19
+ * specialist when the operator supplied a hint).
20
+ *
21
+ * UNWIND-on-empty is a Cypher no-op, but production callers gate on
22
+ * `revisions.length > 0` anyway so the standard (non-rewriter)
23
+ * update path emits no extra clauses.
24
+ *
25
+ * Pruning is the dream-cycle's job (phase 5). This file never deletes.
16
26
  */
17
27
  import type { Session } from "neo4j-driver";
18
28
  export type CompiledTruthRevisionSource = "rewriter" | "operator";
29
+ export type CompiledTruthRevisionTwin = "compiledTruth" | "compiledTruthPublic";
30
+ export interface CompiledTruthRevisionRow {
31
+ truth: string;
32
+ twin: CompiledTruthRevisionTwin;
33
+ source: CompiledTruthRevisionSource;
34
+ }
19
35
  /**
20
- * Cypher fragment that creates one `:CompiledTruthRevision` row and
21
- * links it from the entity bound as `n` in the caller's MATCH. The
22
- * caller is responsible for binding `n` (the entity) and providing
23
- * `$accountId`, `$truth`, and `$source` parameters.
36
+ * Cypher fragment that UNWINDs the caller's `$revisions` array and
37
+ * creates one `:CompiledTruthRevision` row per element, linked from
38
+ * the entity bound as `n` in the caller's MATCH. The caller is
39
+ * responsible for binding `n` (the entity) and providing
40
+ * `$accountId` and `$revisions` parameters.
24
41
  *
25
- * Designed to be concatenated after a `compiledTruth` SET so both
26
- * mutations sit in the same `session.run` call (single TX).
42
+ * Designed to be concatenated after a `compiledTruth` /
43
+ * `compiledTruthPublic` SET so both mutations sit in the same
44
+ * `session.run` call (single TX).
27
45
  */
28
- export declare const REVISION_CYPHER_FRAGMENT = "\nWITH n\nCREATE (r:CompiledTruthRevision {\n revisionId: randomUUID(),\n accountId: $accountId,\n truth: $truth,\n writtenAt: datetime(),\n source: $source\n})\nCREATE (n)-[:HAS_REVISION]->(r)\n";
46
+ export declare const REVISION_CYPHER_FRAGMENT_UNWIND = "\nUNWIND $revisions AS rev\nCREATE (r:CompiledTruthRevision {\n revisionId: randomUUID(),\n accountId: $accountId,\n truth: rev.truth,\n twin: rev.twin,\n writtenAt: datetime(),\n source: rev.source\n})\nCREATE (n)-[:HAS_REVISION]->(r)\n";
29
47
  /**
30
48
  * Convenience runner for tests and ad-hoc callers that want to write
31
- * a revision in isolation (no concurrent `compiledTruth` SET).
32
- * Production writers compose `REVISION_CYPHER_FRAGMENT` into their
33
- * own statement; this helper exists so test code does not have to
34
- * duplicate the fragment.
49
+ * revisions in isolation (no concurrent `compiledTruth` SET).
50
+ * Production writers compose `REVISION_CYPHER_FRAGMENT_UNWIND` into
51
+ * their own statement; this helper exists so test code does not have
52
+ * to duplicate the fragment.
35
53
  */
36
- export declare function writeRevisionStandalone(args: {
54
+ export declare function writeRevisionsStandalone(args: {
37
55
  session: Session;
38
56
  nodeId: string;
39
57
  accountId: string;
40
- truth: string;
41
- source: CompiledTruthRevisionSource;
58
+ revisions: CompiledTruthRevisionRow[];
42
59
  }): Promise<void>;
43
60
  //# sourceMappingURL=compiled-truth-revision.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"compiled-truth-revision.d.ts","sourceRoot":"","sources":["../../src/lib/compiled-truth-revision.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C,MAAM,MAAM,2BAA2B,GAAG,UAAU,GAAG,UAAU,CAAC;AAElE;;;;;;;;GAQG;AACH,eAAO,MAAM,wBAAwB,6MAUpC,CAAC;AAEF;;;;;;GAMG;AACH,wBAAsB,uBAAuB,CAAC,IAAI,EAAE;IAClD,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,2BAA2B,CAAC;CACrC,GAAG,OAAO,CAAC,IAAI,CAAC,CAOhB"}
1
+ {"version":3,"file":"compiled-truth-revision.d.ts","sourceRoot":"","sources":["../../src/lib/compiled-truth-revision.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C,MAAM,MAAM,2BAA2B,GAAG,UAAU,GAAG,UAAU,CAAC;AAClE,MAAM,MAAM,yBAAyB,GAAG,eAAe,GAAG,qBAAqB,CAAC;AAEhF,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,yBAAyB,CAAC;IAChC,MAAM,EAAE,2BAA2B,CAAC;CACrC;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,+BAA+B,wPAW3C,CAAC;AAEF;;;;;;GAMG;AACH,wBAAsB,wBAAwB,CAAC,IAAI,EAAE;IACnD,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,wBAAwB,EAAE,CAAC;CACvC,GAAG,OAAO,CAAC,IAAI,CAAC,CAShB"}
@@ -1,33 +1,39 @@
1
1
  /**
2
- * Cypher fragment that creates one `:CompiledTruthRevision` row and
3
- * links it from the entity bound as `n` in the caller's MATCH. The
4
- * caller is responsible for binding `n` (the entity) and providing
5
- * `$accountId`, `$truth`, and `$source` parameters.
2
+ * Cypher fragment that UNWINDs the caller's `$revisions` array and
3
+ * creates one `:CompiledTruthRevision` row per element, linked from
4
+ * the entity bound as `n` in the caller's MATCH. The caller is
5
+ * responsible for binding `n` (the entity) and providing
6
+ * `$accountId` and `$revisions` parameters.
6
7
  *
7
- * Designed to be concatenated after a `compiledTruth` SET so both
8
- * mutations sit in the same `session.run` call (single TX).
8
+ * Designed to be concatenated after a `compiledTruth` /
9
+ * `compiledTruthPublic` SET so both mutations sit in the same
10
+ * `session.run` call (single TX).
9
11
  */
10
- export const REVISION_CYPHER_FRAGMENT = `
11
- WITH n
12
+ export const REVISION_CYPHER_FRAGMENT_UNWIND = `
13
+ UNWIND $revisions AS rev
12
14
  CREATE (r:CompiledTruthRevision {
13
15
  revisionId: randomUUID(),
14
16
  accountId: $accountId,
15
- truth: $truth,
17
+ truth: rev.truth,
18
+ twin: rev.twin,
16
19
  writtenAt: datetime(),
17
- source: $source
20
+ source: rev.source
18
21
  })
19
22
  CREATE (n)-[:HAS_REVISION]->(r)
20
23
  `;
21
24
  /**
22
25
  * Convenience runner for tests and ad-hoc callers that want to write
23
- * a revision in isolation (no concurrent `compiledTruth` SET).
24
- * Production writers compose `REVISION_CYPHER_FRAGMENT` into their
25
- * own statement; this helper exists so test code does not have to
26
- * duplicate the fragment.
26
+ * revisions in isolation (no concurrent `compiledTruth` SET).
27
+ * Production writers compose `REVISION_CYPHER_FRAGMENT_UNWIND` into
28
+ * their own statement; this helper exists so test code does not have
29
+ * to duplicate the fragment.
27
30
  */
28
- export async function writeRevisionStandalone(args) {
29
- const { session, nodeId, accountId, truth, source } = args;
31
+ export async function writeRevisionsStandalone(args) {
32
+ const { session, nodeId, accountId, revisions } = args;
33
+ if (revisions.length === 0)
34
+ return;
30
35
  await session.run(`MATCH (n) WHERE elementId(n) = $nodeId AND n.accountId = $accountId
31
- ${REVISION_CYPHER_FRAGMENT}`, { nodeId, accountId, truth, source });
36
+ WITH n
37
+ ${REVISION_CYPHER_FRAGMENT_UNWIND}`, { nodeId, accountId, revisions });
32
38
  }
33
39
  //# sourceMappingURL=compiled-truth-revision.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"compiled-truth-revision.js","sourceRoot":"","sources":["../../src/lib/compiled-truth-revision.ts"],"names":[],"mappings":"AAoBA;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG;;;;;;;;;;CAUvC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,IAM7C;IACC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAC3D,MAAM,OAAO,CAAC,GAAG,CACf;OACG,wBAAwB,EAAE,EAC7B,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,CACrC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"compiled-truth-revision.js","sourceRoot":"","sources":["../../src/lib/compiled-truth-revision.ts"],"names":[],"mappings":"AAqCA;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAG;;;;;;;;;;;CAW9C,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,IAK9C;IACC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC;IACvD,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IACnC,MAAM,OAAO,CAAC,GAAG,CACf;;OAEG,+BAA+B,EAAE,EACpC,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,CACjC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { NormaliserInput, NormaliserResult } from "./types.js";
2
+ export declare function emailNormaliser(input: NormaliserInput): NormaliserResult;
3
+ //# sourceMappingURL=email.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"email.d.ts","sourceRoot":"","sources":["../../../src/lib/conversation-normalisers/email.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAc,MAAM,YAAY,CAAC;AAgChF,wBAAgB,eAAe,CAAC,KAAK,EAAE,eAAe,GAAG,gBAAgB,CAwDxE"}
@@ -0,0 +1,61 @@
1
+ import { createHash } from "node:crypto";
2
+ import { readFileSync } from "node:fs";
3
+ import { basename } from "node:path";
4
+ export function emailNormaliser(input) {
5
+ const { filePath, accountId } = input;
6
+ if (!accountId || !accountId.trim()) {
7
+ throw new Error("email: accountId is required.");
8
+ }
9
+ const rawBytes = readFileSync(filePath);
10
+ const archiveSha256 = createHash("sha256").update(rawBytes).digest("hex");
11
+ let raw;
12
+ try {
13
+ raw = JSON.parse(rawBytes.toString("utf-8"));
14
+ }
15
+ catch (err) {
16
+ throw new Error(`email: JSON parse failed — ${err instanceof Error ? err.message : String(err)}. file=${filePath}`);
17
+ }
18
+ if (!Array.isArray(raw)) {
19
+ throw new Error(`email: expected top-level JSON array of message envelopes. file=${filePath}`);
20
+ }
21
+ let parseErrors = 0;
22
+ const records = [];
23
+ for (const m of raw) {
24
+ const iso = parseDateHeader(m?.date);
25
+ const email = m?.from?.email?.trim();
26
+ const name = m?.from?.name?.trim() || email || "";
27
+ const bodyText = m?.bodyText ?? "";
28
+ if (!iso || !email) {
29
+ parseErrors += 1;
30
+ continue;
31
+ }
32
+ const senderPrefix = `From: ${name} <${email}>`;
33
+ records.push({ iso, body: `${senderPrefix}\n${bodyText}` });
34
+ }
35
+ records.sort((a, b) => (a.iso < b.iso ? -1 : a.iso > b.iso ? 1 : 0));
36
+ const parsedLines = records.map((r, i) => ({
37
+ dateSent: r.iso,
38
+ body: r.body,
39
+ sequenceIndex: i,
40
+ }));
41
+ return {
42
+ parsedLines,
43
+ archiveSha256,
44
+ archiveSourceFile: basename(filePath),
45
+ counters: {
46
+ parsed: parsedLines.length,
47
+ systemSkipped: 0,
48
+ mediaSkipped: 0,
49
+ parseErrors,
50
+ },
51
+ };
52
+ }
53
+ function parseDateHeader(value) {
54
+ if (!value || !value.trim())
55
+ return null;
56
+ const ms = Date.parse(value);
57
+ if (!Number.isFinite(ms))
58
+ return null;
59
+ return new Date(ms).toISOString();
60
+ }
61
+ //# sourceMappingURL=email.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"email.js","sourceRoot":"","sources":["../../../src/lib/conversation-normalisers/email.ts"],"names":[],"mappings":"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"}
@@ -2,5 +2,6 @@ import type { ConversationNormaliser, ConversationSource } from "./types.js";
2
2
  export * from "./types.js";
3
3
  export { whatsappTextNormaliser } from "./whatsapp-text.js";
4
4
  export { xDmNormaliser } from "./x-dm.js";
5
+ export { emailNormaliser } from "./email.js";
5
6
  export declare function getNormaliser(source: ConversationSource): ConversationNormaliser;
6
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/conversation-normalisers/index.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAI7E,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAe1C,wBAAgB,aAAa,CAAC,MAAM,EAAE,kBAAkB,GAAG,sBAAsB,CAShF"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/conversation-normalisers/index.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAK7E,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAgB7C,wBAAgB,aAAa,CAAC,MAAM,EAAE,kBAAkB,GAAG,sBAAsB,CAShF"}
@@ -7,9 +7,11 @@
7
7
  // in `types.ts`. No new skill, no new MCP tool, no new plugin.
8
8
  import { whatsappTextNormaliser } from "./whatsapp-text.js";
9
9
  import { xDmNormaliser } from "./x-dm.js";
10
+ import { emailNormaliser } from "./email.js";
10
11
  export * from "./types.js";
11
12
  export { whatsappTextNormaliser } from "./whatsapp-text.js";
12
13
  export { xDmNormaliser } from "./x-dm.js";
14
+ export { emailNormaliser } from "./email.js";
13
15
  /**
14
16
  * Closed registry from source enum → normaliser. Phase 0 ships only
15
17
  * `whatsapp`; future sources land here as they are implemented.
@@ -21,6 +23,7 @@ export { xDmNormaliser } from "./x-dm.js";
21
23
  const NORMALISERS_BY_SOURCE = {
22
24
  whatsapp: whatsappTextNormaliser,
23
25
  "x-dm": xDmNormaliser,
26
+ email: emailNormaliser,
24
27
  };
25
28
  export function getNormaliser(source) {
26
29
  const normaliser = NORMALISERS_BY_SOURCE[source];
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/conversation-normalisers/index.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,EAAE;AACF,2EAA2E;AAC3E,2EAA2E;AAC3E,6EAA6E;AAC7E,4EAA4E;AAC5E,+DAA+D;AAG/D,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE1C,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE1C;;;;;;;GAOG;AACH,MAAM,qBAAqB,GAAgE;IACzF,QAAQ,EAAE,sBAAsB;IAChC,MAAM,EAAE,aAAa;CACtB,CAAC;AAEF,MAAM,UAAU,aAAa,CAAC,MAA0B;IACtD,MAAM,UAAU,GAAG,qBAAqB,CAAC,MAAM,CAAC,CAAC;IACjD,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClE,MAAM,IAAI,KAAK,CACb,+DAA+D,MAAM,mBAAmB,WAAW,6FAA6F,CACjM,CAAC;IACJ,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/conversation-normalisers/index.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,EAAE;AACF,2EAA2E;AAC3E,2EAA2E;AAC3E,6EAA6E;AAC7E,4EAA4E;AAC5E,+DAA+D;AAG/D,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C;;;;;;;GAOG;AACH,MAAM,qBAAqB,GAAgE;IACzF,QAAQ,EAAE,sBAAsB;IAChC,MAAM,EAAE,aAAa;IACrB,KAAK,EAAE,eAAe;CACvB,CAAC;AAEF,MAAM,UAAU,aAAa,CAAC,MAA0B;IACtD,MAAM,UAAU,GAAG,qBAAqB,CAAC,MAAM,CAAC,CAAC;IACjD,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClE,MAAM,IAAI,KAAK,CACb,+DAA+D,MAAM,mBAAmB,WAAW,6FAA6F,CACjM,CAAC;IACJ,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC"}
@@ -1,4 +1,4 @@
1
- export declare const CONVERSATION_SOURCES: readonly ["whatsapp", "telegram", "signal", "linkedin-messages", "zoom-transcript", "meeting-minutes", "imessage", "slack", "x-dm", "other"];
1
+ export declare const CONVERSATION_SOURCES: readonly ["whatsapp", "telegram", "signal", "linkedin-messages", "zoom-transcript", "meeting-minutes", "imessage", "slack", "x-dm", "email", "other"];
2
2
  export type ConversationSource = (typeof CONVERSATION_SOURCES)[number];
3
3
  export declare function isConversationSource(s: unknown): s is ConversationSource;
4
4
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/lib/conversation-normalisers/types.ts"],"names":[],"mappings":"AAsBA,eAAO,MAAM,oBAAoB,8IAWvB,CAAC;AAEX,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEvE,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,kBAAkB,CAExE;AAED;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,gFAAgF;IAChF,QAAQ,EAAE,MAAM,CAAC;IACjB,qFAAqF;IACrF,IAAI,EAAE,MAAM,CAAC;IACb,oDAAoD;IACpD,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,eAAe;IAC9B,0EAA0E;IAC1E,QAAQ,EAAE,MAAM,CAAC;IACjB,oDAAoD;IACpD,SAAS,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,QAAQ,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B;qDACiD;IACjD,aAAa,EAAE,MAAM,CAAC;IACtB,+DAA+D;IAC/D,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,kBAAkB,CAAC;CAC9B;AAED,MAAM,MAAM,sBAAsB,GAAG,CACnC,KAAK,EAAE,eAAe,KACnB,OAAO,CAAC,gBAAgB,CAAC,GAAG,gBAAgB,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/lib/conversation-normalisers/types.ts"],"names":[],"mappings":"AAsBA,eAAO,MAAM,oBAAoB,uJAYvB,CAAC;AAEX,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEvE,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,kBAAkB,CAExE;AAED;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,gFAAgF;IAChF,QAAQ,EAAE,MAAM,CAAC;IACjB,qFAAqF;IACrF,IAAI,EAAE,MAAM,CAAC;IACb,oDAAoD;IACpD,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,eAAe;IAC9B,0EAA0E;IAC1E,QAAQ,EAAE,MAAM,CAAC;IACjB,oDAAoD;IACpD,SAAS,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,QAAQ,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B;qDACiD;IACjD,aAAa,EAAE,MAAM,CAAC;IACtB,+DAA+D;IAC/D,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,kBAAkB,CAAC;CAC9B;AAED,MAAM,MAAM,sBAAsB,GAAG,CACnC,KAAK,EAAE,eAAe,KACnB,OAAO,CAAC,gBAAgB,CAAC,GAAG,gBAAgB,CAAC"}
@@ -29,6 +29,7 @@ export const CONVERSATION_SOURCES = [
29
29
  "imessage",
30
30
  "slack",
31
31
  "x-dm",
32
+ "email",
32
33
  "other",
33
34
  ];
34
35
  export function isConversationSource(s) {
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/lib/conversation-normalisers/types.ts"],"names":[],"mappings":"AAAA,0CAA0C;AAC1C,EAAE;AACF,2EAA2E;AAC3E,yEAAyE;AACzE,sEAAsE;AACtE,wEAAwE;AACxE,0EAA0E;AAC1E,oBAAoB;AACpB,EAAE;AACF,yDAAyD;AACzD,uEAAuE;AACvE,wEAAwE;AACxE,0EAA0E;AAC1E,qEAAqE;AACrE,6EAA6E;AAC7E,4CAA4C;AAC5C,EAAE;AACF,yEAAyE;AACzE,0EAA0E;AAC1E,0EAA0E;AAC1E,aAAa;AAEb,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,UAAU;IACV,UAAU;IACV,QAAQ;IACR,mBAAmB;IACnB,iBAAiB;IACjB,iBAAiB;IACjB,UAAU;IACV,OAAO;IACP,MAAM;IACN,OAAO;CACC,CAAC;AAIX,MAAM,UAAU,oBAAoB,CAAC,CAAU;IAC7C,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAK,oBAA0C,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;AAC1F,CAAC"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/lib/conversation-normalisers/types.ts"],"names":[],"mappings":"AAAA,0CAA0C;AAC1C,EAAE;AACF,2EAA2E;AAC3E,yEAAyE;AACzE,sEAAsE;AACtE,wEAAwE;AACxE,0EAA0E;AAC1E,oBAAoB;AACpB,EAAE;AACF,yDAAyD;AACzD,uEAAuE;AACvE,wEAAwE;AACxE,0EAA0E;AAC1E,qEAAqE;AACrE,6EAA6E;AAC7E,4CAA4C;AAC5C,EAAE;AACF,yEAAyE;AACzE,0EAA0E;AAC1E,0EAA0E;AAC1E,aAAa;AAEb,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,UAAU;IACV,UAAU;IACV,QAAQ;IACR,mBAAmB;IACnB,iBAAiB;IACjB,iBAAiB;IACjB,UAAU;IACV,OAAO;IACP,MAAM;IACN,OAAO;IACP,OAAO;CACC,CAAC;AAIX,MAAM,UAAU,oBAAoB,CAAC,CAAU;IAC7C,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAK,oBAA0C,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;AAC1F,CAAC"}
@@ -4,6 +4,8 @@ export interface DeriveConversationIdentityInput {
4
4
  /** Element IDs of every confirmed participant (owner + others). Order is
5
5
  * not significant; the function sorts internally. */
6
6
  participantElementIds: readonly string[];
7
+ /** ConversationSource enum value (whatsapp, email, telegram, …). */
8
+ source: string;
7
9
  }
8
10
  export declare function deriveConversationIdentity(input: DeriveConversationIdentityInput): string;
9
11
  export interface DeriveMessageContentHashInput {
@@ -1 +1 @@
1
- {"version":3,"file":"derive-keys.d.ts","sourceRoot":"","sources":["../../../src/lib/conversation-pipeline/derive-keys.ts"],"names":[],"mappings":"AAsBA,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,MAAM,WAAW,+BAA+B;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB;0DACsD;IACtD,qBAAqB,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1C;AAED,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,+BAA+B,GACrC,MAAM,CASR;AAED,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,6BAA6B,GACnC,MAAM,CAER"}
1
+ {"version":3,"file":"derive-keys.d.ts","sourceRoot":"","sources":["../../../src/lib/conversation-pipeline/derive-keys.ts"],"names":[],"mappings":"AAyBA,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,MAAM,WAAW,+BAA+B;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB;0DACsD;IACtD,qBAAqB,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,oEAAoE;IACpE,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,+BAA+B,GACrC,MAAM,CAYR;AAED,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,6BAA6B,GACnC,MAAM,CAER"}
@@ -6,11 +6,14 @@ import { createHash } from "node:crypto";
6
6
  // archive collapse to the same identity regardless of release-level drift in
7
7
  // chunk indices, hash widths, or arbitrary tiebreakers.
8
8
  //
9
- // conversationIdentity = sha256(accountId + ":" + sortedParticipantElementIds.join(","))
9
+ // conversationIdentity = sha256(accountId + ":" + sortedParticipantElementIds.join(",") + ":" + source)
10
10
  // messageContentHash = sha256(dateSent + "|" + body)
11
11
  //
12
12
  // `conversationIdentity` is stable across re-exports — same operator + same
13
- // participant set → same identity, regardless of file bytes.
13
+ // participant set + same source → same identity, regardless of file bytes.
14
+ // Source is part of the hash because a 1:1 conversation with the same person
15
+ // on WhatsApp and by email is two conversations, not one — their `:Person`
16
+ // elementIds match, so without source-scoping the parent rows collide.
14
17
  //
15
18
  // `messageContentHash`: hashes `dateSent + body`. Per-line
16
19
  // senderName is gone (the scanner is body-opaque); the body string still
@@ -28,8 +31,11 @@ export function deriveConversationIdentity(input) {
28
31
  if (input.participantElementIds.length === 0) {
29
32
  throw new Error("deriveConversationIdentity: participantElementIds must be non-empty");
30
33
  }
34
+ if (!input.source || !input.source.trim()) {
35
+ throw new Error("deriveConversationIdentity: source is required");
36
+ }
31
37
  const sorted = [...input.participantElementIds].sort();
32
- return sha256Hex(`${input.accountId}:${sorted.join(",")}`);
38
+ return sha256Hex(`${input.accountId}:${sorted.join(",")}:${input.source}`);
33
39
  }
34
40
  export function deriveMessageContentHash(input) {
35
41
  return sha256Hex(`${input.dateSent}|${input.body}`);
@@ -1 +1 @@
1
- {"version":3,"file":"derive-keys.js","sourceRoot":"","sources":["../../../src/lib/conversation-pipeline/derive-keys.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,8EAA8E;AAC9E,0EAA0E;AAC1E,EAAE;AACF,yEAAyE;AACzE,6EAA6E;AAC7E,wDAAwD;AACxD,EAAE;AACF,2FAA2F;AAC3F,yDAAyD;AACzD,EAAE;AACF,4EAA4E;AAC5E,6DAA6D;AAC7D,EAAE;AACF,2DAA2D;AAC3D,yEAAyE;AACzE,0EAA0E;AAC1E,0EAA0E;AAC1E,0EAA0E;AAC1E,8EAA8E;AAE9E,MAAM,UAAU,SAAS,CAAC,KAAa;IACrC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC1D,CAAC;AASD,MAAM,UAAU,0BAA0B,CACxC,KAAsC;IAEtC,IAAI,CAAC,KAAK,CAAC,SAAS,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,CAAC;QAChD,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;IACvE,CAAC;IACD,IAAI,KAAK,CAAC,qBAAqB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,KAAK,CAAC,qEAAqE,CAAC,CAAC;IACzF,CAAC;IACD,MAAM,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC,qBAAqB,CAAC,CAAC,IAAI,EAAE,CAAC;IACvD,OAAO,SAAS,CAAC,GAAG,KAAK,CAAC,SAAS,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAC7D,CAAC;AAOD,MAAM,UAAU,wBAAwB,CACtC,KAAoC;IAEpC,OAAO,SAAS,CAAC,GAAG,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;AACtD,CAAC"}
1
+ {"version":3,"file":"derive-keys.js","sourceRoot":"","sources":["../../../src/lib/conversation-pipeline/derive-keys.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,8EAA8E;AAC9E,0EAA0E;AAC1E,EAAE;AACF,yEAAyE;AACzE,6EAA6E;AAC7E,wDAAwD;AACxD,EAAE;AACF,0GAA0G;AAC1G,yDAAyD;AACzD,EAAE;AACF,4EAA4E;AAC5E,2EAA2E;AAC3E,6EAA6E;AAC7E,2EAA2E;AAC3E,uEAAuE;AACvE,EAAE;AACF,2DAA2D;AAC3D,yEAAyE;AACzE,0EAA0E;AAC1E,0EAA0E;AAC1E,0EAA0E;AAC1E,8EAA8E;AAE9E,MAAM,UAAU,SAAS,CAAC,KAAa;IACrC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC1D,CAAC;AAWD,MAAM,UAAU,0BAA0B,CACxC,KAAsC;IAEtC,IAAI,CAAC,KAAK,CAAC,SAAS,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,CAAC;QAChD,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;IACvE,CAAC;IACD,IAAI,KAAK,CAAC,qBAAqB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,KAAK,CAAC,qEAAqE,CAAC,CAAC;IACzF,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;IACpE,CAAC;IACD,MAAM,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC,qBAAqB,CAAC,CAAC,IAAI,EAAE,CAAC;IACvD,OAAO,SAAS,CAAC,GAAG,KAAK,CAAC,SAAS,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;AAC7E,CAAC;AAOD,MAAM,UAAU,wBAAwB,CACtC,KAAoC;IAEpC,OAAO,SAAS,CAAC,GAAG,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;AACtD,CAAC"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Body-growth threshold above which a reclassify is allowed. Below this the
3
+ * gate returns "skip" because section topology is unlikely to have shifted.
4
+ * Picked to fire on roughly 2-3 new messages in a 10-message thread.
5
+ */
6
+ export declare const KD_CLASSIFY_GROWTH_THRESHOLD = 0.25;
7
+ export interface GateInputs {
8
+ bodyLen: number;
9
+ /** Body length at the previous successful classify; 0 / null means never classified. */
10
+ lastClassifiedBodyLength: number;
11
+ }
12
+ export interface GateDecision {
13
+ action: "skip" | "proceed";
14
+ growthPct: number;
15
+ /** Human-readable skip reason when action === "skip". */
16
+ reason: string | null;
17
+ }
18
+ /** Decide whether a classify call should proceed based on body-length delta. */
19
+ export declare function decideGate({ bodyLen, lastClassifiedBodyLength }: GateInputs): GateDecision;
20
+ /**
21
+ * Write the KD's body to a deterministic temp-file path so the librarian can
22
+ * feed it through memory-ingest-extract → memory-ingest unchanged. Deterministic
23
+ * path = idempotent under repeat calls; the file is overwritten if it exists.
24
+ */
25
+ export declare function writeClassifyTempFile(accountDir: string, attachmentId: string, body: string): Promise<string>;
26
+ /**
27
+ * Build the dispatch envelope the operator-visible tool returns. Admin reads
28
+ * the envelope and uses the Task tool to dispatch the librarian specialist;
29
+ * librarian runs the document-ingest skill against `tempFilePath` with the
30
+ * named `attachmentId` so the resulting :Section children land under the
31
+ * pre-existing :KnowledgeDocument (memory-ingest is keyed on attachmentId).
32
+ */
33
+ export declare function buildDispatchEnvelope(opts: {
34
+ attachmentId: string;
35
+ source: string;
36
+ tempFilePath: string;
37
+ bodyLen: number;
38
+ lastClassifiedBodyLength: number;
39
+ growthPct: number;
40
+ }): string;
41
+ //# sourceMappingURL=kd-classify-gate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kd-classify-gate.d.ts","sourceRoot":"","sources":["../../src/lib/kd-classify-gate.ts"],"names":[],"mappings":"AAeA;;;;GAIG;AACH,eAAO,MAAM,4BAA4B,OAAO,CAAC;AAEjD,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,wFAAwF;IACxF,wBAAwB,EAAE,MAAM,CAAC;CAClC;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,yDAAyD;IACzD,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,gFAAgF;AAChF,wBAAgB,UAAU,CAAC,EAAE,OAAO,EAAE,wBAAwB,EAAE,EAAE,UAAU,GAAG,YAAY,CAe1F;AAED;;;;GAIG;AACH,wBAAsB,qBAAqB,CACzC,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,CAAC,CAKjB;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE;IAC1C,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,wBAAwB,EAAE,MAAM,CAAC;IACjC,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,MAAM,CAeT"}
@@ -0,0 +1,71 @@
1
+ import { mkdir, writeFile } from "node:fs/promises";
2
+ import { dirname } from "node:path";
3
+ // ---------------------------------------------------------------------------
4
+ // Reclassify gate for :KnowledgeDocument operator-solicited classification.
5
+ //
6
+ // Two consumers today: kd-classify (memory plugin, generic) and email-classify
7
+ // (email plugin, derives attachmentId from rootMessageId then delegates here-
8
+ // equivalent logic). Email duplicates this file's small surface inline rather
9
+ // than importing across plugins; the threshold and envelope shape are the
10
+ // shared contract. Keep both copies aligned when the gate changes.
11
+ //
12
+ // Task 401.
13
+ // ---------------------------------------------------------------------------
14
+ /**
15
+ * Body-growth threshold above which a reclassify is allowed. Below this the
16
+ * gate returns "skip" because section topology is unlikely to have shifted.
17
+ * Picked to fire on roughly 2-3 new messages in a 10-message thread.
18
+ */
19
+ export const KD_CLASSIFY_GROWTH_THRESHOLD = 0.25;
20
+ /** Decide whether a classify call should proceed based on body-length delta. */
21
+ export function decideGate({ bodyLen, lastClassifiedBodyLength }) {
22
+ if (lastClassifiedBodyLength <= 0) {
23
+ // First-time classify — no prior stamp.
24
+ return { action: "proceed", growthPct: 1, reason: null };
25
+ }
26
+ const growth = (bodyLen - lastClassifiedBodyLength) / lastClassifiedBodyLength;
27
+ if (growth >= KD_CLASSIFY_GROWTH_THRESHOLD) {
28
+ return { action: "proceed", growthPct: growth, reason: null };
29
+ }
30
+ const pctRounded = Math.round(growth * 100);
31
+ return {
32
+ action: "skip",
33
+ growthPct: growth,
34
+ reason: `no classification needed (body grew ${pctRounded}% since last ingest; threshold ${Math.round(KD_CLASSIFY_GROWTH_THRESHOLD * 100)}%)`,
35
+ };
36
+ }
37
+ /**
38
+ * Write the KD's body to a deterministic temp-file path so the librarian can
39
+ * feed it through memory-ingest-extract → memory-ingest unchanged. Deterministic
40
+ * path = idempotent under repeat calls; the file is overwritten if it exists.
41
+ */
42
+ export async function writeClassifyTempFile(accountDir, attachmentId, body) {
43
+ const path = `${accountDir}/tmp/kd-classify-${attachmentId}.txt`;
44
+ await mkdir(dirname(path), { recursive: true });
45
+ await writeFile(path, body, "utf-8");
46
+ return path;
47
+ }
48
+ /**
49
+ * Build the dispatch envelope the operator-visible tool returns. Admin reads
50
+ * the envelope and uses the Task tool to dispatch the librarian specialist;
51
+ * librarian runs the document-ingest skill against `tempFilePath` with the
52
+ * named `attachmentId` so the resulting :Section children land under the
53
+ * pre-existing :KnowledgeDocument (memory-ingest is keyed on attachmentId).
54
+ */
55
+ export function buildDispatchEnvelope(opts) {
56
+ const growthLabel = opts.lastClassifiedBodyLength > 0
57
+ ? `${Math.round(opts.growthPct * 100)}% growth since last ingest`
58
+ : "first classify";
59
+ return [
60
+ `Ready to classify ${opts.source} :KnowledgeDocument into sections.`,
61
+ `attachmentId: ${opts.attachmentId}`,
62
+ `bodyLen: ${opts.bodyLen} chars (${growthLabel})`,
63
+ `tempFilePath: ${opts.tempFilePath}`,
64
+ ``,
65
+ `Next: dispatch the \`librarian\` specialist via the Task tool with this brief —`,
66
+ ` "Ingest the document at ${opts.tempFilePath} using mode=document, attachmentId=${opts.attachmentId},`,
67
+ ` anchor=LocalBusiness. The :KnowledgeDocument already exists and has source=${opts.source};`,
68
+ ` memory-ingest will MERGE by attachmentId and write :Section children, replacing any prior."`,
69
+ ].join("\n");
70
+ }
71
+ //# sourceMappingURL=kd-classify-gate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kd-classify-gate.js","sourceRoot":"","sources":["../../src/lib/kd-classify-gate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,8EAA8E;AAC9E,4EAA4E;AAC5E,EAAE;AACF,+EAA+E;AAC/E,8EAA8E;AAC9E,8EAA8E;AAC9E,0EAA0E;AAC1E,mEAAmE;AACnE,EAAE;AACF,YAAY;AACZ,8EAA8E;AAE9E;;;;GAIG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,IAAI,CAAC;AAejD,gFAAgF;AAChF,MAAM,UAAU,UAAU,CAAC,EAAE,OAAO,EAAE,wBAAwB,EAAc;IAC1E,IAAI,wBAAwB,IAAI,CAAC,EAAE,CAAC;QAClC,wCAAwC;QACxC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAC3D,CAAC;IACD,MAAM,MAAM,GAAG,CAAC,OAAO,GAAG,wBAAwB,CAAC,GAAG,wBAAwB,CAAC;IAC/E,IAAI,MAAM,IAAI,4BAA4B,EAAE,CAAC;QAC3C,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAChE,CAAC;IACD,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC;IAC5C,OAAO;QACL,MAAM,EAAE,MAAM;QACd,SAAS,EAAE,MAAM;QACjB,MAAM,EAAE,uCAAuC,UAAU,kCAAkC,IAAI,CAAC,KAAK,CAAC,4BAA4B,GAAG,GAAG,CAAC,IAAI;KAC9I,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,UAAkB,EAClB,YAAoB,EACpB,IAAY;IAEZ,MAAM,IAAI,GAAG,GAAG,UAAU,oBAAoB,YAAY,MAAM,CAAC;IACjE,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,MAAM,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IACrC,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAOrC;IACC,MAAM,WAAW,GAAG,IAAI,CAAC,wBAAwB,GAAG,CAAC;QACnD,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,GAAG,GAAG,CAAC,4BAA4B;QACjE,CAAC,CAAC,gBAAgB,CAAC;IACrB,OAAO;QACL,qBAAqB,IAAI,CAAC,MAAM,oCAAoC;QACpE,iBAAiB,IAAI,CAAC,YAAY,EAAE;QACpC,YAAY,IAAI,CAAC,OAAO,WAAW,WAAW,GAAG;QACjD,iBAAiB,IAAI,CAAC,YAAY,EAAE;QACpC,EAAE;QACF,iFAAiF;QACjF,6BAA6B,IAAI,CAAC,YAAY,sCAAsC,IAAI,CAAC,YAAY,GAAG;QACxG,iFAAiF,IAAI,CAAC,MAAM,GAAG;QAC/F,gGAAgG;KACjG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}
@@ -0,0 +1,18 @@
1
+ export declare const RESERVED_LABEL_DELETE_REFUSAL: string;
2
+ interface DeleteGateArgs {
3
+ labels: string[];
4
+ origin?: string;
5
+ accountId?: string;
6
+ elementId?: string;
7
+ tool: "memory-delete" | "memory-empty-trash";
8
+ }
9
+ export type DeleteGateResult = {
10
+ ok: true;
11
+ } | {
12
+ ok: false;
13
+ reason: "reserved-label";
14
+ message: string;
15
+ };
16
+ export declare function checkLabelDeleteGate(args: DeleteGateArgs): DeleteGateResult;
17
+ export {};
18
+ //# sourceMappingURL=label-delete-gate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"label-delete-gate.d.ts","sourceRoot":"","sources":["../../src/lib/label-delete-gate.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,6BAA6B,QAGgB,CAAC;AAE3D,UAAU,cAAc;IACtB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,eAAe,GAAG,oBAAoB,CAAC;CAC9C;AAED,MAAM,MAAM,gBAAgB,GACxB;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GACZ;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7D,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,cAAc,GAAG,gBAAgB,CAqB3E"}
@@ -0,0 +1,31 @@
1
+ // Task 443 — Refuse agent-origin deletes targeting the per-account root
2
+ // `:LocalBusiness`. Mirrors the write-side gate in `label-origin-gate.ts`
3
+ // (Task 350): the installer's seed-neo4j.sh writes the single root via raw
4
+ // cypher (system origin); resets that need to remove + re-seed call the
5
+ // memory-delete primitive with `origin: "system"`. Without this gate an
6
+ // agent or specialist can soft-delete the immutable per-account root via
7
+ // `memory-delete`, and a subsequent `memory-empty-trash` will DETACH DELETE
8
+ // it from the graph.
9
+ const RESERVED_LABEL = "LocalBusiness";
10
+ const SYSTEM_ORIGIN = "system";
11
+ export const RESERVED_LABEL_DELETE_REFUSAL = `Delete blocked (reserved-label): ${RESERVED_LABEL} is the per-account root ` +
12
+ `seeded by the installer and held immutable. ` +
13
+ `See platform/plugins/memory/references/schema-base.md.`;
14
+ export function checkLabelDeleteGate(args) {
15
+ const { labels, origin, accountId, elementId, tool } = args;
16
+ if (!labels.includes(RESERVED_LABEL)) {
17
+ return { ok: true };
18
+ }
19
+ if ((origin ?? "") === SYSTEM_ORIGIN) {
20
+ return { ok: true };
21
+ }
22
+ const acctPrefix = accountId ? accountId.slice(0, 8) : "<none>";
23
+ const eidPrefix = elementId ? elementId.slice(0, 16) : "<none>";
24
+ process.stderr.write(`[label-delete-gate] reject accountId=${acctPrefix} elementId=${eidPrefix} label=${RESERVED_LABEL} tool=${tool}\n`);
25
+ return {
26
+ ok: false,
27
+ reason: "reserved-label",
28
+ message: RESERVED_LABEL_DELETE_REFUSAL,
29
+ };
30
+ }
31
+ //# sourceMappingURL=label-delete-gate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"label-delete-gate.js","sourceRoot":"","sources":["../../src/lib/label-delete-gate.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,0EAA0E;AAC1E,2EAA2E;AAC3E,wEAAwE;AACxE,wEAAwE;AACxE,yEAAyE;AACzE,4EAA4E;AAC5E,qBAAqB;AAErB,MAAM,cAAc,GAAG,eAAe,CAAC;AACvC,MAAM,aAAa,GAAG,QAAQ,CAAC;AAE/B,MAAM,CAAC,MAAM,6BAA6B,GACxC,oCAAoC,cAAc,2BAA2B;IAC7E,8CAA8C;IAC9C,wDAAwD,CAAC;AAc3D,MAAM,UAAU,oBAAoB,CAAC,IAAoB;IACvD,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC;IAE5D,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;QACrC,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;IACtB,CAAC;IACD,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,KAAK,aAAa,EAAE,CAAC;QACrC,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;IACtB,CAAC;IAED,MAAM,UAAU,GAAG,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;IAChE,MAAM,SAAS,GAAG,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;IAChE,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,wCAAwC,UAAU,cAAc,SAAS,UAAU,cAAc,SAAS,IAAI,IAAI,CACnH,CAAC;IAEF,OAAO;QACL,EAAE,EAAE,KAAK;QACT,MAAM,EAAE,gBAAgB;QACxB,OAAO,EAAE,6BAA6B;KACvC,CAAC;AACJ,CAAC"}
@@ -1,2 +1,7 @@
1
+ /**
2
+ * Task 433 — the per-chunk derive step accepts agent-produced claims
3
+ * and emits cypher proposals; it no longer calls Haiku, no longer paginates.
4
+ * `conversation-archive-list-chunks` covers the page-walk side.
5
+ */
1
6
  export {};
2
7
  //# sourceMappingURL=conversation-archive-derive-insights.test.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"conversation-archive-derive-insights.test.d.ts","sourceRoot":"","sources":["../../../src/tools/__tests__/conversation-archive-derive-insights.test.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"conversation-archive-derive-insights.test.d.ts","sourceRoot":"","sources":["../../../src/tools/__tests__/conversation-archive-derive-insights.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}