@rubytech/create-realagent 1.0.829 → 1.0.831

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 (121) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/config/brand.json +1 -1
  3. package/payload/platform/lib/oauth-llm/dist/index.d.ts +9 -2
  4. package/payload/platform/lib/oauth-llm/dist/index.d.ts.map +1 -1
  5. package/payload/platform/lib/oauth-llm/dist/index.js +26 -1
  6. package/payload/platform/lib/oauth-llm/dist/index.js.map +1 -1
  7. package/payload/platform/lib/oauth-llm/src/index.ts +43 -4
  8. package/payload/platform/neo4j/migrations/007-conversation-archive-source.ts +116 -0
  9. package/payload/platform/neo4j/migrations/008-adminuser-accountid-backfill.ts +85 -0
  10. package/payload/platform/neo4j/schema.cypher +12 -3
  11. package/payload/platform/plugins/admin/hooks/__tests__/archive-ingest-surface-gate.test.sh +54 -39
  12. package/payload/platform/plugins/admin/hooks/archive-ingest-surface-gate.sh +64 -26
  13. package/payload/platform/plugins/admin/mcp/dist/index.js +25 -3
  14. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  15. package/payload/platform/plugins/contacts/mcp/dist/index.js +5 -5
  16. package/payload/platform/plugins/contacts/mcp/dist/index.js.map +1 -1
  17. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.d.ts +1 -1
  18. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.d.ts.map +1 -1
  19. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.js +29 -23
  20. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.js.map +1 -1
  21. package/payload/platform/plugins/docs/references/internals.md +1 -1
  22. package/payload/platform/plugins/docs/references/plugins-guide.md +1 -1
  23. package/payload/platform/plugins/memory/PLUGIN.md +2 -1
  24. package/payload/platform/plugins/memory/bin/conversation-archive-ingest.mjs +564 -0
  25. package/payload/platform/plugins/memory/bin/conversation-archive-ingest.sh +106 -0
  26. package/payload/platform/plugins/memory/mcp/dist/index.js +30 -16
  27. package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
  28. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/llm-classifier.test.js +4 -3
  29. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/llm-classifier.test.js.map +1 -1
  30. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-loader.test.js +11 -6
  31. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-loader.test.js.map +1 -1
  32. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/index.d.ts +5 -0
  33. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/index.d.ts.map +1 -0
  34. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/index.js +30 -0
  35. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/index.js.map +1 -0
  36. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/timestamp-scanner.d.ts +49 -0
  37. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/timestamp-scanner.d.ts.map +1 -0
  38. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/timestamp-scanner.js +35 -0
  39. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/timestamp-scanner.js.map +1 -0
  40. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/types.d.ts +47 -0
  41. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/types.d.ts.map +1 -0
  42. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/types.js +31 -0
  43. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/types.js.map +1 -0
  44. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/whatsapp-text.d.ts +3 -0
  45. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/whatsapp-text.d.ts.map +1 -0
  46. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/whatsapp-text.js +155 -0
  47. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/whatsapp-text.js.map +1 -0
  48. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/delta-cursor.d.ts +11 -0
  49. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/delta-cursor.d.ts.map +1 -0
  50. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/delta-cursor.js +20 -0
  51. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/delta-cursor.js.map +1 -0
  52. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/derive-keys.d.ts +14 -0
  53. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/derive-keys.d.ts.map +1 -0
  54. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/derive-keys.js +38 -0
  55. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/derive-keys.js.map +1 -0
  56. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/sender-bind.d.ts +16 -0
  57. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/sender-bind.d.ts.map +1 -0
  58. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/sender-bind.js +59 -0
  59. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/sender-bind.js.map +1 -0
  60. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/sessionize.d.ts +9 -0
  61. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/sessionize.d.ts.map +1 -0
  62. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/sessionize.js +32 -0
  63. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/sessionize.js.map +1 -0
  64. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/to-turn-text.d.ts +3 -0
  65. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/to-turn-text.d.ts.map +1 -0
  66. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/to-turn-text.js +29 -0
  67. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/to-turn-text.js.map +1 -0
  68. package/payload/platform/plugins/memory/mcp/dist/lib/document-chunker.d.ts +45 -0
  69. package/payload/platform/plugins/memory/mcp/dist/lib/document-chunker.d.ts.map +1 -0
  70. package/payload/platform/plugins/memory/mcp/dist/lib/document-chunker.js +125 -0
  71. package/payload/platform/plugins/memory/mcp/dist/lib/document-chunker.js.map +1 -0
  72. package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.d.ts +34 -9
  73. package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.d.ts.map +1 -1
  74. package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.js +360 -35
  75. package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.js.map +1 -1
  76. package/payload/platform/plugins/memory/mcp/dist/lib/llm-ranker.d.ts +3 -2
  77. package/payload/platform/plugins/memory/mcp/dist/lib/llm-ranker.d.ts.map +1 -1
  78. package/payload/platform/plugins/memory/mcp/dist/lib/llm-ranker.js +46 -17
  79. package/payload/platform/plugins/memory/mcp/dist/lib/llm-ranker.js.map +1 -1
  80. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-source-agnosticism.test.d.ts +2 -0
  81. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-source-agnosticism.test.d.ts.map +1 -0
  82. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-source-agnosticism.test.js +73 -0
  83. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-source-agnosticism.test.js.map +1 -0
  84. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-whatsapp-text.test.d.ts +2 -0
  85. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-whatsapp-text.test.d.ts.map +1 -0
  86. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-whatsapp-text.test.js +109 -0
  87. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-whatsapp-text.test.js.map +1 -0
  88. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest.test.js +34 -3
  89. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest.test.js.map +1 -1
  90. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.d.ts +17 -0
  91. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.d.ts.map +1 -1
  92. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.js +34 -13
  93. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.js.map +1 -1
  94. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.d.ts +18 -7
  95. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.d.ts.map +1 -1
  96. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js +24 -8
  97. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js.map +1 -1
  98. package/payload/platform/plugins/memory/mcp/dist/tools/memory-rank.js +2 -2
  99. package/payload/platform/plugins/memory/mcp/dist/tools/memory-rank.js.map +1 -1
  100. package/payload/platform/plugins/memory/references/schema-base.md +2 -2
  101. package/payload/platform/plugins/memory/skills/conversation-archive/SKILL.md +133 -0
  102. package/payload/platform/plugins/memory/skills/document-ingest/SKILL.md +5 -2
  103. package/payload/platform/plugins/whatsapp/PLUGIN.md +1 -1
  104. package/payload/platform/scripts/seed-neo4j.sh +15 -15
  105. package/payload/platform/templates/specialists/agents/database-operator.md +8 -9
  106. package/payload/server/chunk-7BO5HDJC.js +10093 -0
  107. package/payload/server/chunk-BCFM2UPH.js +2305 -0
  108. package/payload/server/chunk-CV3HPX46.js +10097 -0
  109. package/payload/server/chunk-EL4DZ56X.js +1116 -0
  110. package/payload/server/chunk-J6YWEJBN.js +1116 -0
  111. package/payload/server/chunk-OCPJGZ6S.js +654 -0
  112. package/payload/server/chunk-QOJ2D26Z.js +654 -0
  113. package/payload/server/chunk-RC46ZYGT.js +2305 -0
  114. package/payload/server/client-pool-7NTEFNVQ.js +32 -0
  115. package/payload/server/client-pool-ZNGN66GN.js +32 -0
  116. package/payload/server/cloudflare-task-tracker-MHALDN54.js +19 -0
  117. package/payload/server/cloudflare-task-tracker-WE77WXSI.js +19 -0
  118. package/payload/server/maxy-edge.js +3 -3
  119. package/payload/server/neo4j-migrations-4XPNJNM6.js +490 -0
  120. package/payload/server/neo4j-migrations-6RW423E2.js +530 -0
  121. package/payload/server/server.js +30 -19
@@ -1,30 +1,23 @@
1
1
  #!/usr/bin/env bash
2
- # Regression test for archive-ingest-surface-gate.sh (Task 855, trimmed by Task 894).
2
+ # Regression test for archive-ingest-surface-gate.sh (Task 855).
3
3
  #
4
- # Covers (post-Task-894):
5
- # 1. Edit on /platform/plugins/<x>/lib/* → BLOCKED
6
- # 2. Edit on benign path ALLOWED
7
- # 3. Bash with `npx vitest`/`bun test`/`npm test` BLOCKED
8
- # 4. PreToolUse memory-archive-write w/ archiveType=linkedin-connectionsALLOWED
9
- # 5. Default-allow emits a [archive-ingest-gate] decision=allow log line
10
- # 6. Parse-error flag lifecycle: PostToolUse on any *-export-parse / *-import-parse
11
- # with isError:true sets flag; subsequent PreToolUse blocks; UserPromptSubmit
12
- # clears; stale flag (>600s) auto-clears; parse-success leaves flag absent.
13
- # 7. Bypass attempts: nested file_path in old_string + top-level file_path in
14
- # lib/* BLOCKED.
15
- # 8. Fail-closed terminal check.
16
- #
17
- # Removed by Task 894:
18
- # - WhatsApp-specific MCP-tool blocks (the three legacy whatsapp-export tools
19
- # no longer exist; chat archives flow through memory-classify mode='chat'
20
- # and memory-ingest parentLabel='ConversationArchive' via document-ingest).
21
- # - memory-archive-write archiveType='whatsapp-export' block (the enum value
22
- # was dropped from memory-archive-write).
23
- #
24
- # A generic LinkedIn-shaped fixture stands in for the parse-error lifecycle —
25
- # the gate's PostToolUse pattern matches `mcp__*__*-export-parse` /
26
- # `mcp__*__*-import-parse`, so any plausible parser tool name exercises the
27
- # flag-set / flag-clear path.
4
+ # Covers:
5
+ # Preserved-from-Task-846:
6
+ # 1. Edit on /platform/plugins/<x>/lib/*BLOCKED
7
+ # 2. Edit on benign pathALLOWED
8
+ # 3. Bash with `npx vitest`/`bun test`/`npm test` BLOCKED
9
+ # 4. PostToolUse on whatsapp-export-parse with isError:true sets flag
10
+ # 5. Subsequent PreToolUse on ANY tool BLOCKED
11
+ # 6. UserPromptSubmit clears flag normal allow resumes
12
+ # 7. PostToolUse with isError:false flag absent
13
+ # 8. Stale flag (>600s) auto-clears
14
+ # New (Task 855):
15
+ # A. PreToolUse mcp__memory__whatsapp-export-parse BLOCKED
16
+ # B. PreToolUse mcp__memory__whatsapp-export-insight-write → BLOCKED
17
+ # C. PreToolUse mcp__memory__memory-archive-write w/ archiveType=whatsapp-export → BLOCKED
18
+ # D. PreToolUse mcp__memory__memory-archive-write w/ archiveType=linkedin-connections ALLOWED
19
+ # E. PreToolUse Bash invoking conversation-archive-ingest.sh ALLOWED
20
+ # F. Default-allow emits a [archive-ingest-gate] decision=allow log line
28
21
 
29
22
  set -u
30
23
 
@@ -58,20 +51,18 @@ run_case() {
58
51
  fi
59
52
  }
60
53
 
61
- # Plugin-source-edit block ------------------------------------------------
54
+ # Preserved cases ---------------------------------------------------------
62
55
 
63
- run_case "Edit on platform/plugins/linkedin-import/lib/src/parse.ts → BLOCKED" \
64
- '{"hook_event_name":"PreToolUse","tool_name":"Edit","tool_input":{"file_path":"/Users/x/repo/platform/plugins/linkedin-import/lib/src/parse.ts","old_string":"a","new_string":"b"}}' \
56
+ run_case "Edit on platform/plugins/memory/mcp/src/lib/conversation-normalisers/whatsapp-text.ts → BLOCKED" \
57
+ '{"hook_event_name":"PreToolUse","tool_name":"Edit","tool_input":{"file_path":"/Users/x/repo/platform/plugins/memory/mcp/src/lib/conversation-normalisers/whatsapp-text.ts","old_string":"a","new_string":"b"}}' \
65
58
  2
66
59
 
67
60
  run_case "Edit on README.md → ALLOWED" \
68
61
  '{"hook_event_name":"PreToolUse","tool_name":"Edit","tool_input":{"file_path":"/Users/x/repo/README.md","old_string":"a","new_string":"b"}}' \
69
62
  0
70
63
 
71
- # Test-runner block (Bash) ------------------------------------------------
72
-
73
64
  run_case "Bash 'npx vitest run' → BLOCKED" \
74
- '{"hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"npx vitest run x.test.ts"}}' \
65
+ '{"hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"npx vitest run parse-export.test.ts"}}' \
75
66
  2
76
67
 
77
68
  run_case "Bash 'ls -la' → ALLOWED" \
@@ -86,18 +77,45 @@ run_case "Bash 'npm test' → BLOCKED" \
86
77
  '{"hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"npm test"}}' \
87
78
  2
88
79
 
89
- # memory-archive-write LinkedIn passes through unchanged ----------------
80
+ # New (Task 855) cases ----------------------------------------------------
81
+
82
+ run_case "PreToolUse mcp__memory__whatsapp-export-parse → BLOCKED" \
83
+ '{"hook_event_name":"PreToolUse","tool_name":"mcp__memory__whatsapp-export-parse","tool_input":{"filePath":"/tmp/_chat.txt","accountId":"acct1","timezone":"Europe/London"}}' \
84
+ 2
85
+
86
+ run_case "PreToolUse mcp__memory__whatsapp-export-insight-write → BLOCKED" \
87
+ '{"hook_event_name":"PreToolUse","tool_name":"mcp__memory__whatsapp-export-insight-write","tool_input":{"kind":"MENTIONS","name":"Joel"}}' \
88
+ 2
89
+
90
+ run_case "PreToolUse memory-archive-write w/ archiveType=whatsapp-export → BLOCKED" \
91
+ '{"hook_event_name":"PreToolUse","tool_name":"mcp__memory__memory-archive-write","tool_input":{"archiveType":"whatsapp-export","ownerNodeId":"x","accountId":"a","rows":[]}}' \
92
+ 2
90
93
 
91
94
  run_case "PreToolUse memory-archive-write w/ archiveType=linkedin-connections → ALLOWED" \
92
95
  '{"hook_event_name":"PreToolUse","tool_name":"mcp__memory__memory-archive-write","tool_input":{"archiveType":"linkedin-connections","ownerNodeId":"x","accountId":"a","rows":[]}}' \
93
96
  0
94
97
 
98
+ # Bypass attempts (Task 855 code-review C1): nested archiveType in rows[0]
99
+ # or conversation must NOT defeat the block. The gate must read the
100
+ # top-level tool_input.archiveType, not the first textual occurrence.
101
+ run_case "BYPASS: nested rows[0].archiveType=linkedin + top-level archiveType=whatsapp-export → BLOCKED" \
102
+ '{"hook_event_name":"PreToolUse","tool_name":"mcp__memory__memory-archive-write","tool_input":{"rows":[{"archiveType":"linkedin-connections"}],"archiveType":"whatsapp-export","ownerNodeId":"x","accountId":"a"}}' \
103
+ 2
104
+
105
+ run_case "BYPASS: conversation.archiveType=linkedin + top-level archiveType=whatsapp-export → BLOCKED" \
106
+ '{"hook_event_name":"PreToolUse","tool_name":"mcp__memory__memory-archive-write","tool_input":{"conversation":{"archiveType":"linkedin-connections","conversationId":"x"},"archiveType":"whatsapp-export","ownerNodeId":"x","accountId":"a","rows":[]}}' \
107
+ 2
108
+
95
109
  # Plugin-source-edit path block must read tool_input.file_path top-level,
96
110
  # not a nested file_path in old_string/new_string.
97
111
  run_case "BYPASS: nested file_path in old_string + top-level file_path in lib/* → BLOCKED" \
98
- '{"hook_event_name":"PreToolUse","tool_name":"Edit","tool_input":{"file_path":"/repo/platform/plugins/linkedin-import/lib/src/parse.ts","old_string":"file_path:/safe/path","new_string":"x"}}' \
112
+ '{"hook_event_name":"PreToolUse","tool_name":"Edit","tool_input":{"file_path":"/repo/platform/plugins/memory/mcp/src/lib/conversation-normalisers/whatsapp-text.ts","old_string":"file_path:/safe/path","new_string":"x"}}' \
99
113
  2
100
114
 
115
+ run_case "PreToolUse Bash invoking conversation-archive-ingest.sh → ALLOWED" \
116
+ '{"hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"bash platform/plugins/memory/bin/conversation-archive-ingest.sh /tmp/chat.zip --source whatsapp --owner-element-id 4:abc:1 --participant-person-ids 4:abc:2 --scope admin"}}' \
117
+ 0
118
+
101
119
  # Default-allow log-line check
102
120
  LOG=$(printf '%s' '{"hook_event_name":"PreToolUse","tool_name":"Read","tool_input":{"file_path":"/tmp/foo"}}' | bash "$HOOK" 2>&1 1>/dev/null)
103
121
  if printf '%s' "$LOG" | grep -q '\[archive-ingest-gate\] decision=allow tool=Read reason=default'; then
@@ -118,13 +136,10 @@ else
118
136
  FAIL=$((FAIL + 1))
119
137
  fi
120
138
 
121
- # Parse-error flag lifecycle (preserved). The gate's PostToolUse pattern is
122
- # `mcp__*__*-export-parse` / `mcp__*__*-import-parse` — exercise via the
123
- # linkedin-import-parse name (synthetic — the linkedin importer currently
124
- # uses memory-archive-write, but the gate matches by tool name shape).
139
+ # Parse-error flag lifecycle (preserved)
125
140
  rm -f "$FLAG_FILE"
126
141
  run_case "PostToolUse parse-error sets flag (exit 0)" \
127
- '{"hook_event_name":"PostToolUse","tool_name":"mcp__memory__linkedin-import-parse","tool_input":{"filePath":"connections.csv"},"tool_response":{"isError":true,"content":[{"type":"text","text":"parse-error file=connections.csv line=1 reason=missing-header"}]}}' \
142
+ '{"hook_event_name":"PostToolUse","tool_name":"mcp__memory__whatsapp-export-parse","tool_input":{"filePath":"_chat.txt"},"tool_response":{"isError":true,"content":[{"type":"text","text":"parse-error file=_chat.txt line=1 reason=not-a-_chat.txt"}]}}' \
128
143
  0
129
144
  [[ -f "$FLAG_FILE" ]] && { echo "PASS: parse-error flag created"; PASS=$((PASS+1)); } \
130
145
  || { echo "FAIL: parse-error flag NOT created" >&2; FAIL=$((FAIL+1)); }
@@ -153,7 +168,7 @@ run_case "Stale flag auto-clears, PreToolUse Read → ALLOWED" \
153
168
  # PostToolUse parse-success leaves flag absent
154
169
  rm -f "$FLAG_FILE"
155
170
  run_case "PostToolUse parse-success (isError:false) does NOT set flag" \
156
- '{"hook_event_name":"PostToolUse","tool_name":"mcp__memory__linkedin-import-parse","tool_input":{"filePath":"connections.csv"},"tool_response":{"isError":false,"content":[{"type":"text","text":"{\"parsedLines\":[]}"}]}}' \
171
+ '{"hook_event_name":"PostToolUse","tool_name":"mcp__memory__whatsapp-export-parse","tool_input":{"filePath":"_chat.txt"},"tool_response":{"isError":false,"content":[{"type":"text","text":"{\"parsedLines\":[]}"}]}}' \
157
172
  0
158
173
  [[ ! -f "$FLAG_FILE" ]] && { echo "PASS: parse-success leaves flag absent"; PASS=$((PASS+1)); } \
159
174
  || { echo "FAIL: parse-success incorrectly created flag" >&2; FAIL=$((FAIL+1)); }
@@ -1,24 +1,28 @@
1
1
  #!/usr/bin/env bash
2
- # Archive-ingest surface gate (Task 855, updated by Task 891, trimmed by Task 894).
2
+ # Archive-ingest surface gate (Task 855, updated by Task 891).
3
3
  #
4
- # Three enforcements, one script — phase decided by `hook_event_name` on stdin.
5
- # Task 855 narrowed the database-operator subagent's effective surface during
6
- # archive ingestion. Task 894 retired the WhatsApp `_chat.txt` deterministic
7
- # parser and its three legacy MCP tools entirely — chat archives now flow
8
- # through the unified `document-ingest` pipeline (`memory-classify` mode='chat'
9
- # + `memory-ingest` parentLabel='ConversationArchive'). The WhatsApp-specific
10
- # block lists were removed because the tools they referenced no longer exist
11
- # and the `archiveType=whatsapp-export` enum value was dropped from
12
- # `memory-archive-write`. The remaining blocks still cover LinkedIn and any
13
- # future flat-dataset archive types that ship per-source parsers.
4
+ # Five enforcements, one script — phase decided by `hook_event_name` on stdin.
5
+ # Task 855 narrows the database-operator subagent's effective surface during
6
+ # WhatsApp archive ingestion to exactly one Bash entry
7
+ # (`memory/bin/conversation-archive-ingest.sh`) plus read-only neighbours, by
8
+ # blocking the legacy MCP deviation tools mechanically. Task 891 retired the
9
+ # `whatsapp-export-insight-pass` tool entirely (Phase 2 enrichment moved to a
10
+ # separate follow-up task that will operate on :Section:Conversation chunks);
11
+ # the tool name is added to the BLOCK list so any agent that still references
12
+ # it from a stale skill or runbook gets a loud denial instead of MCP-not-found.
14
13
  #
15
- # 1. Parse-error gate: PostToolUse on any `mcp__*__*-export-parse` /
16
- # `mcp__*__*-import-parse` tool whose `tool_response.isError == true`
17
- # writes a flag file. Subsequent PreToolUse on ANY tool blocks until
18
- # UserPromptSubmit clears the flag. A 600s TTL is the cross-session safety
19
- # net. Preserved from Task 846 because LinkedIn and future per-source archive
20
- # parsers still use the legacy MCP path until they migrate to their own
21
- # deterministic Bash entries.
14
+ # 1. PreToolUse on the four legacy WhatsApp MCP tools — BLOCK unconditionally.
15
+ # The single deterministic Bash entry is the only supported path for
16
+ # `archiveType=whatsapp-export`. Tool source for #1-#3 remains until cleanup;
17
+ # `whatsapp-export-insight-pass` source was deleted by Task 891 and the
18
+ # block here catches stale references.
19
+ # mcp__memory__whatsapp-export-parse
20
+ # mcp__memory__whatsapp-export-insight-write
21
+ # mcp__memory__whatsapp-export-insight-pass (Task 891 — deleted, retired)
22
+ # mcp__memory__memory-archive-write (only when `archiveType` is
23
+ # `whatsapp-export`; LinkedIn
24
+ # and other archiveTypes pass
25
+ # through unchanged.)
22
26
  #
23
27
  # 2. PreToolUse Edit/Write/NotebookEdit: deny writes under
24
28
  # `*platform/plugins/*/lib/*` (parser/CSV-shape source for any *-import or
@@ -28,10 +32,18 @@
28
32
  # 3. PreToolUse Bash: deny commands invoking JavaScript test runners
29
33
  # (vitest|bun test|npm test|npx jest|node .*vitest). Preserved from Task 846.
30
34
  #
31
- # 4. Logging: every PreToolUse decision emits one line in the format
35
+ # 4. Parse-error gate: PostToolUse on any `mcp__*__*-export-parse` /
36
+ # `mcp__*__*-import-parse` tool whose `tool_response.isError == true`
37
+ # writes a flag file. Subsequent PreToolUse on ANY tool blocks until
38
+ # UserPromptSubmit clears the flag. A 600s TTL is the cross-session safety
39
+ # net. Preserved from Task 846 because LinkedIn and future per-source archive
40
+ # parsers still use the legacy MCP path until they migrate to their own
41
+ # deterministic Bash entries.
42
+ #
43
+ # 5. Logging: every PreToolUse decision emits one line in the format
32
44
  # [archive-ingest-gate] decision=<allow|block> tool=<name> reason=<r> ...
33
45
  # so the operator can grep the full decision trail for one ingest from
34
- # server.log.
46
+ # server.log alongside the [whatsapp-ingest] script lines.
35
47
  #
36
48
  # Exit codes follow Claude Code hook protocol: 0 = allow, 2 = block (stderr
37
49
  # message shown to the agent). Fail-closed on terminal stdin to match
@@ -128,12 +140,25 @@ if [ -f "$FLAG_FILE" ]; then
128
140
  fi
129
141
  fi
130
142
 
143
+ # --- Block 2: legacy WhatsApp MCP tools — defensive denial ----------------
144
+ # These tool sources were deleted by Task 894; the block stays as a stale-
145
+ # reference catch (skill markdown checked into older installs may still name
146
+ # them). The harness will return tool-not-found anyway, but a named block
147
+ # message guides the operator to the new entry.
148
+ case "$TOOL_NAME" in
149
+ mcp__memory__whatsapp-export-parse|mcp__memory__whatsapp-export-insight-write|mcp__memory__whatsapp-export-insight-pass|mcp__memory__whatsapp-export-preview)
150
+ emit_decision "block" "denied-mcp-legacy" \
151
+ "Blocked: ${TOOL_NAME} is a retired path. Task 894 generalised conversation-archive ingest — invoke 'bash platform/plugins/memory/bin/conversation-archive-ingest.sh <archive> --source whatsapp --owner-element-id <id> --participant-person-ids <csv> --scope <admin|public>' once. Normalise, sessionize, classify (mode=chat), and memory-ingest (parentLabel=ConversationArchive, source=<enum>) all run in-process."
152
+ ;;
153
+ esac
154
+
131
155
  # Helper: extract a top-level field from `tool_input` via python3 — never via
132
156
  # grep+sed against the raw JSON, which would pick the first textual occurrence
133
- # including nested-object matches (`rows[0].archiveType`, etc.) and let a
134
- # malicious payload bypass the block. python3 is the project standard for
135
- # JSON-aware hook parsing (mirrors lane-gate.sh:31-46). On parse failure
136
- # return empty string downstream block conditions skip cleanly.
157
+ # including nested-object matches (`rows[0].archiveType`,
158
+ # `conversation.archiveType`, etc.) and let a malicious payload bypass the
159
+ # block. python3 is the project standard for JSON-aware hook parsing
160
+ # (mirrors lane-gate.sh:31-46). On parse failure return empty string
161
+ # downstream block conditions skip cleanly.
137
162
  extract_tool_input_field() {
138
163
  local field="$1"
139
164
  printf '%s' "$INPUT" | python3 -c "
@@ -146,7 +171,20 @@ except Exception:
146
171
  " 2>/dev/null || echo ""
147
172
  }
148
173
 
149
- # --- Block 2: plugin-source path block (Edit/Write/NotebookEdit) -----------
174
+ # --- Block 3: memory-archive-write conditional on conversation-shaped types
175
+ # LinkedIn-connections and future flat-dataset archiveTypes flow unchanged.
176
+ # `whatsapp-export` was the only conversation-shaped archiveType; Task 894
177
+ # deleted that handler entirely, so the type itself is now invalid input —
178
+ # the block stays defensive against operator-edited skills that still name it.
179
+ if [ "$TOOL_NAME" = "mcp__memory__memory-archive-write" ]; then
180
+ ARCHIVE_TYPE=$(extract_tool_input_field archiveType)
181
+ if [ "$ARCHIVE_TYPE" = "whatsapp-export" ]; then
182
+ emit_decision "block" "denied-mcp-legacy archiveType=whatsapp-export" \
183
+ "Blocked: memory-archive-write with archiveType='whatsapp-export' is a retired path. Task 894 routes conversation transcripts through 'bash platform/plugins/memory/bin/conversation-archive-ingest.sh <archive> --source whatsapp --owner-element-id <id> --participant-person-ids <csv> --scope <admin|public>'. Flat-dataset archiveTypes (linkedin-connections, …) flow through memory-archive-write unchanged."
184
+ fi
185
+ fi
186
+
187
+ # --- Block 4: plugin-source path block (Edit/Write/NotebookEdit) -----------
150
188
  case "$TOOL_NAME" in
151
189
  Edit|Write|NotebookEdit)
152
190
  FILE_PATH=$(extract_tool_input_field file_path)
@@ -159,7 +197,7 @@ case "$TOOL_NAME" in
159
197
  ;;
160
198
  esac
161
199
 
162
- # --- Block 3: shell test-runner block (Bash) -------------------------------
200
+ # --- Block 5: shell test-runner block (Bash) -------------------------------
163
201
  COMMAND=""
164
202
  if [ "$TOOL_NAME" = "Bash" ]; then
165
203
  COMMAND=$(extract_tool_input_field command)
@@ -737,9 +737,19 @@ server.tool("admin-add", "Add a new admin user to this account. Creates a device
737
737
  const firstSpace = trimmedName.search(/\s/);
738
738
  const givenName = firstSpace === -1 ? trimmedName : trimmedName.slice(0, firstSpace).trim();
739
739
  const familyName = firstSpace === -1 ? null : (trimmedName.slice(firstSpace + 1).trim() || null);
740
+ // Task 897: stamp `accountId` on every AdminUser at MERGE time, both
741
+ // ON CREATE and ON MATCH (the latter via COALESCE so a pre-existing
742
+ // value isn't overwritten if it differs — which would itself be a
743
+ // graph-invariant violation worth surfacing). Pre-897 the missing
744
+ // accountId fingerprint produced :AdminUser nodes that migration 004
745
+ // pruned silently, costing the admin pin during onboarding.
740
746
  const result = await session.run(`MERGE (au:AdminUser {userId: $userId})
741
- ON CREATE SET au.name = $name, au.createdAt = $createdAt
742
- ON MATCH SET au.name = $name, au.updatedAt = $createdAt
747
+ ON CREATE SET au.accountId = $accountId,
748
+ au.name = $name,
749
+ au.createdAt = $createdAt
750
+ ON MATCH SET au.accountId = COALESCE(au.accountId, $accountId),
751
+ au.name = $name,
752
+ au.updatedAt = $createdAt
743
753
  WITH au
744
754
  MATCH (b:LocalBusiness {accountId: $accountId})
745
755
  MERGE (au)-[r:ADMIN_OF]->(b)
@@ -772,11 +782,23 @@ server.tool("admin-add", "Add a new admin user to this account. Creates a device
772
782
  if (result.records.length > 0) {
773
783
  personReused = result.records[0].get("reused");
774
784
  }
785
+ // Task 897 — post-write assertion mirroring Task 785 in neo4j-store.ts.
786
+ // MATCH back the AdminUser we just wrote and verify every required
787
+ // field landed. Loud-fail the tool call if accountId or name is null
788
+ // so a Cypher regression that drops the field is grep-detectable.
789
+ const verify = await session.run(`MATCH (au:AdminUser {userId: $userId})
790
+ RETURN coalesce(au.accountId, '') AS accountId,
791
+ coalesce(au.name, '') AS name`, { userId });
792
+ const verifiedAccountId = verify.records[0]?.get("accountId") || "";
793
+ const verifiedName = verify.records[0]?.get("name") || "";
794
+ if (!verifiedAccountId || !verifiedName) {
795
+ throw new Error(`post-write assertion failed: AdminUser userId=${userIdShort} accountId=${verifiedAccountId || "(null)"} name=${verifiedName || "(null)"} — required fields missing after MERGE`);
796
+ }
775
797
  }
776
798
  finally {
777
799
  await session.close();
778
800
  }
779
- console.error(`[admin-auth-store] action=add userId=${userIdShort} result=ok store=neo4j personReused=${personReused}`);
801
+ console.error(`[admin] admin-add success userId=${userIdShort} accountId=${ACCOUNT_ID} name=${name.trim()} required-fields=ok personReused=${personReused}`);
780
802
  }
781
803
  catch (err) {
782
804
  const errMsg = err instanceof Error ? err.message : String(err);