@rubytech/create-realagent 1.0.828 → 1.0.829

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 (72) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/neo4j/schema.cypher +2 -1
  3. package/payload/platform/package.json +2 -2
  4. package/payload/platform/plugins/admin/hooks/__tests__/archive-ingest-surface-gate.test.sh +39 -54
  5. package/payload/platform/plugins/admin/hooks/archive-ingest-surface-gate.sh +26 -58
  6. package/payload/platform/plugins/admin/skills/onboarding/SKILL.md +2 -2
  7. package/payload/platform/plugins/docs/references/plugins-guide.md +1 -1
  8. package/payload/platform/plugins/memory/PLUGIN.md +4 -4
  9. package/payload/platform/plugins/memory/mcp/dist/index.js +18 -218
  10. package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
  11. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-validator.test.js +103 -0
  12. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-validator.test.js.map +1 -1
  13. package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.d.ts.map +1 -1
  14. package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.js +30 -20
  15. package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.js.map +1 -1
  16. package/payload/platform/plugins/memory/mcp/dist/lib/schema-validator.d.ts +16 -1
  17. package/payload/platform/plugins/memory/mcp/dist/lib/schema-validator.d.ts.map +1 -1
  18. package/payload/platform/plugins/memory/mcp/dist/lib/schema-validator.js +12 -3
  19. package/payload/platform/plugins/memory/mcp/dist/lib/schema-validator.js.map +1 -1
  20. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-write.test.js +2 -138
  21. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-write.test.js.map +1 -1
  22. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest.test.js +10 -5
  23. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest.test.js.map +1 -1
  24. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-personfields-open.test.d.ts +2 -0
  25. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-personfields-open.test.d.ts.map +1 -0
  26. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-personfields-open.test.js +148 -0
  27. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-personfields-open.test.js.map +1 -0
  28. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.d.ts +1 -64
  29. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.d.ts.map +1 -1
  30. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.js +6 -336
  31. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.js.map +1 -1
  32. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.d.ts +7 -11
  33. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.d.ts.map +1 -1
  34. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js +1 -11
  35. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js.map +1 -1
  36. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.d.ts +21 -17
  37. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.d.ts.map +1 -1
  38. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js +77 -37
  39. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js.map +1 -1
  40. package/payload/platform/plugins/memory/references/schema-base.md +2 -0
  41. package/payload/platform/plugins/memory/skills/document-ingest/SKILL.md +54 -4
  42. package/payload/platform/plugins/whatsapp/PLUGIN.md +1 -1
  43. package/payload/platform/scripts/seed-neo4j.sh +15 -14
  44. package/payload/platform/templates/specialists/agents/database-operator.md +9 -15
  45. package/payload/server/chunk-CUSH3UXP.js +2305 -0
  46. package/payload/server/chunk-IWNDVGKT.js +10077 -0
  47. package/payload/server/chunk-KC7NUABI.js +654 -0
  48. package/payload/server/chunk-WUVXPZIV.js +1116 -0
  49. package/payload/server/client-pool-3TM3SRIA.js +32 -0
  50. package/payload/server/cloudflare-task-tracker-4NIODMGL.js +19 -0
  51. package/payload/server/maxy-edge.js +3 -3
  52. package/payload/server/neo4j-migrations-XTQ4WEV6.js +428 -0
  53. package/payload/server/server.js +6 -6
  54. package/payload/platform/plugins/whatsapp-import/PLUGIN.md +0 -48
  55. package/payload/platform/plugins/whatsapp-import/bin/ingest.mjs +0 -617
  56. package/payload/platform/plugins/whatsapp-import/bin/whatsapp-ingest.sh +0 -98
  57. package/payload/platform/plugins/whatsapp-import/lib/src/__tests__/delta-append.test.ts +0 -163
  58. package/payload/platform/plugins/whatsapp-import/lib/src/__tests__/parse-export-lrm.test.ts +0 -83
  59. package/payload/platform/plugins/whatsapp-import/lib/src/__tests__/parse-export.test.ts +0 -678
  60. package/payload/platform/plugins/whatsapp-import/lib/src/__tests__/sessionize.test.ts +0 -91
  61. package/payload/platform/plugins/whatsapp-import/lib/src/__tests__/to-classifier-input.test.ts +0 -59
  62. package/payload/platform/plugins/whatsapp-import/lib/src/delta-cursor.ts +0 -54
  63. package/payload/platform/plugins/whatsapp-import/lib/src/derive-keys.ts +0 -82
  64. package/payload/platform/plugins/whatsapp-import/lib/src/index.ts +0 -22
  65. package/payload/platform/plugins/whatsapp-import/lib/src/parse-export.ts +0 -471
  66. package/payload/platform/plugins/whatsapp-import/lib/src/sessionize.ts +0 -81
  67. package/payload/platform/plugins/whatsapp-import/lib/src/to-classifier-input.ts +0 -48
  68. package/payload/platform/plugins/whatsapp-import/lib/tsconfig.json +0 -9
  69. package/payload/platform/plugins/whatsapp-import/lib/vitest.config.ts +0 -9
  70. package/payload/platform/plugins/whatsapp-import/skills/whatsapp-import/SKILL.md +0 -124
  71. package/payload/platform/plugins/whatsapp-import/skills/whatsapp-import/references/conversation-archive-shape.md +0 -143
  72. package/payload/platform/plugins/whatsapp-import/skills/whatsapp-import/references/export-parse.md +0 -109
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/create-realagent",
3
- "version": "1.0.828",
3
+ "version": "1.0.829",
4
4
  "description": "Install Real Agent — Built for agents. By agents.",
5
5
  "bin": {
6
6
  "create-realagent": "./dist/index.js"
@@ -269,7 +269,8 @@ OPTIONS {
269
269
  //
270
270
  // :PARTICIPANT_IN edges from :Person/:AdminUser to :ConversationArchive are
271
271
  // MERGEd on every ingest (idempotent); the operator confirms each participant
272
- // up front via the whatsapp-import skill flow.
272
+ // up front via the document-ingest skill's chat-mode participant flow
273
+ // (Task 894 — supersedes the prior whatsapp-import skill).
273
274
  // ----------------------------------------------------------
274
275
 
275
276
  CREATE CONSTRAINT conversation_archive_identity_unique IF NOT EXISTS
@@ -6,8 +6,8 @@
6
6
  "plugins/*/mcp"
7
7
  ],
8
8
  "scripts": {
9
- "build": "tsc -p lib/models/tsconfig.json && tsc -p lib/anthropic-key/tsconfig.json && tsc -p lib/oauth-llm/tsconfig.json && tsc -p lib/mcp-stderr-tee/tsconfig.json && tsc -p lib/mcp-spawn-tee/tsconfig.json && tsc -p lib/graph-write/tsconfig.json && tsc -p lib/graph-mcp/tsconfig.json && tsc -p lib/graph-trash/tsconfig.json && tsc -p lib/graph-search/tsconfig.json && tsc -p lib/screening-patterns/tsconfig.json && tsc -p lib/device-url/tsconfig.json && tsc -p lib/brand-templating/tsconfig.json && tsc -p lib/entitlement/tsconfig.json && tsc -p lib/task-secrets/tsconfig.json && tsc -p plugins/whatsapp-import/lib/tsconfig.json && NODE_OPTIONS='--max-old-space-size=8192' tsc -b plugins/*/mcp/tsconfig.json",
10
- "build:lib": "tsc -p lib/models/tsconfig.json && tsc -p lib/anthropic-key/tsconfig.json && tsc -p lib/oauth-llm/tsconfig.json && tsc -p lib/mcp-stderr-tee/tsconfig.json && tsc -p lib/mcp-spawn-tee/tsconfig.json && tsc -p lib/graph-write/tsconfig.json && tsc -p lib/graph-mcp/tsconfig.json && tsc -p lib/graph-trash/tsconfig.json && tsc -p lib/graph-search/tsconfig.json && tsc -p lib/screening-patterns/tsconfig.json && tsc -p lib/device-url/tsconfig.json && tsc -p lib/brand-templating/tsconfig.json && tsc -p lib/entitlement/tsconfig.json && tsc -p lib/task-secrets/tsconfig.json && tsc -p plugins/whatsapp-import/lib/tsconfig.json",
9
+ "build": "tsc -p lib/models/tsconfig.json && tsc -p lib/anthropic-key/tsconfig.json && tsc -p lib/oauth-llm/tsconfig.json && tsc -p lib/mcp-stderr-tee/tsconfig.json && tsc -p lib/mcp-spawn-tee/tsconfig.json && tsc -p lib/graph-write/tsconfig.json && tsc -p lib/graph-mcp/tsconfig.json && tsc -p lib/graph-trash/tsconfig.json && tsc -p lib/graph-search/tsconfig.json && tsc -p lib/screening-patterns/tsconfig.json && tsc -p lib/device-url/tsconfig.json && tsc -p lib/brand-templating/tsconfig.json && tsc -p lib/entitlement/tsconfig.json && tsc -p lib/task-secrets/tsconfig.json && NODE_OPTIONS='--max-old-space-size=8192' tsc -b plugins/*/mcp/tsconfig.json",
10
+ "build:lib": "tsc -p lib/models/tsconfig.json && tsc -p lib/anthropic-key/tsconfig.json && tsc -p lib/oauth-llm/tsconfig.json && tsc -p lib/mcp-stderr-tee/tsconfig.json && tsc -p lib/mcp-spawn-tee/tsconfig.json && tsc -p lib/graph-write/tsconfig.json && tsc -p lib/graph-mcp/tsconfig.json && tsc -p lib/graph-trash/tsconfig.json && tsc -p lib/graph-search/tsconfig.json && tsc -p lib/screening-patterns/tsconfig.json && tsc -p lib/device-url/tsconfig.json && tsc -p lib/brand-templating/tsconfig.json && tsc -p lib/entitlement/tsconfig.json && tsc -p lib/task-secrets/tsconfig.json",
11
11
  "build:memory": "tsc -p plugins/memory/mcp/tsconfig.json",
12
12
  "build:contacts": "tsc -p plugins/contacts/mcp/tsconfig.json",
13
13
  "build:telegram": "tsc -p plugins/telegram/mcp/tsconfig.json",
@@ -1,23 +1,30 @@
1
1
  #!/usr/bin/env bash
2
- # Regression test for archive-ingest-surface-gate.sh (Task 855).
2
+ # Regression test for archive-ingest-surface-gate.sh (Task 855, trimmed by Task 894).
3
3
  #
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 whatsapp-ingest.sh → ALLOWED
20
- # F. Default-allow emits a [archive-ingest-gate] decision=allow log line
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.
21
28
 
22
29
  set -u
23
30
 
@@ -51,18 +58,20 @@ run_case() {
51
58
  fi
52
59
  }
53
60
 
54
- # Preserved cases ---------------------------------------------------------
61
+ # Plugin-source-edit block ------------------------------------------------
55
62
 
56
- run_case "Edit on platform/plugins/whatsapp-import/lib/src/parse-export.ts → BLOCKED" \
57
- '{"hook_event_name":"PreToolUse","tool_name":"Edit","tool_input":{"file_path":"/Users/x/repo/platform/plugins/whatsapp-import/lib/src/parse-export.ts","old_string":"a","new_string":"b"}}' \
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"}}' \
58
65
  2
59
66
 
60
67
  run_case "Edit on README.md → ALLOWED" \
61
68
  '{"hook_event_name":"PreToolUse","tool_name":"Edit","tool_input":{"file_path":"/Users/x/repo/README.md","old_string":"a","new_string":"b"}}' \
62
69
  0
63
70
 
71
+ # Test-runner block (Bash) ------------------------------------------------
72
+
64
73
  run_case "Bash 'npx vitest run' → BLOCKED" \
65
- '{"hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"npx vitest run parse-export.test.ts"}}' \
74
+ '{"hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"npx vitest run x.test.ts"}}' \
66
75
  2
67
76
 
68
77
  run_case "Bash 'ls -la' → ALLOWED" \
@@ -77,45 +86,18 @@ run_case "Bash 'npm test' → BLOCKED" \
77
86
  '{"hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"npm test"}}' \
78
87
  2
79
88
 
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
89
+ # memory-archive-write LinkedIn passes through unchanged ----------------
93
90
 
94
91
  run_case "PreToolUse memory-archive-write w/ archiveType=linkedin-connections → ALLOWED" \
95
92
  '{"hook_event_name":"PreToolUse","tool_name":"mcp__memory__memory-archive-write","tool_input":{"archiveType":"linkedin-connections","ownerNodeId":"x","accountId":"a","rows":[]}}' \
96
93
  0
97
94
 
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
-
109
95
  # Plugin-source-edit path block must read tool_input.file_path top-level,
110
96
  # not a nested file_path in old_string/new_string.
111
97
  run_case "BYPASS: nested file_path in old_string + top-level file_path in lib/* → BLOCKED" \
112
- '{"hook_event_name":"PreToolUse","tool_name":"Edit","tool_input":{"file_path":"/repo/platform/plugins/whatsapp-import/lib/src/parse-export.ts","old_string":"file_path:/safe/path","new_string":"x"}}' \
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"}}' \
113
99
  2
114
100
 
115
- run_case "PreToolUse Bash invoking whatsapp-ingest.sh → ALLOWED" \
116
- '{"hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"bash platform/plugins/whatsapp-import/bin/whatsapp-ingest.sh /tmp/chat.zip --owner-element-id 4:abc:1 --subject-person-id 4:abc:2 --scope admin --filter all"}}' \
117
- 0
118
-
119
101
  # Default-allow log-line check
120
102
  LOG=$(printf '%s' '{"hook_event_name":"PreToolUse","tool_name":"Read","tool_input":{"file_path":"/tmp/foo"}}' | bash "$HOOK" 2>&1 1>/dev/null)
121
103
  if printf '%s' "$LOG" | grep -q '\[archive-ingest-gate\] decision=allow tool=Read reason=default'; then
@@ -136,10 +118,13 @@ else
136
118
  FAIL=$((FAIL + 1))
137
119
  fi
138
120
 
139
- # Parse-error flag lifecycle (preserved)
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).
140
125
  rm -f "$FLAG_FILE"
141
126
  run_case "PostToolUse parse-error sets flag (exit 0)" \
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"}]}}' \
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"}]}}' \
143
128
  0
144
129
  [[ -f "$FLAG_FILE" ]] && { echo "PASS: parse-error flag created"; PASS=$((PASS+1)); } \
145
130
  || { echo "FAIL: parse-error flag NOT created" >&2; FAIL=$((FAIL+1)); }
@@ -168,7 +153,7 @@ run_case "Stale flag auto-clears, PreToolUse Read → ALLOWED" \
168
153
  # PostToolUse parse-success leaves flag absent
169
154
  rm -f "$FLAG_FILE"
170
155
  run_case "PostToolUse parse-success (isError:false) does NOT set flag" \
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\":[]}"}]}}' \
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\":[]}"}]}}' \
172
157
  0
173
158
  [[ ! -f "$FLAG_FILE" ]] && { echo "PASS: parse-success leaves flag absent"; PASS=$((PASS+1)); } \
174
159
  || { echo "FAIL: parse-success incorrectly created flag" >&2; FAIL=$((FAIL+1)); }
@@ -1,28 +1,24 @@
1
1
  #!/usr/bin/env bash
2
- # Archive-ingest surface gate (Task 855, updated by Task 891).
2
+ # Archive-ingest surface gate (Task 855, updated by Task 891, trimmed by Task 894).
3
3
  #
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
- # (`whatsapp-import/bin/whatsapp-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.
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.
13
14
  #
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.)
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.
26
22
  #
27
23
  # 2. PreToolUse Edit/Write/NotebookEdit: deny writes under
28
24
  # `*platform/plugins/*/lib/*` (parser/CSV-shape source for any *-import or
@@ -32,18 +28,10 @@
32
28
  # 3. PreToolUse Bash: deny commands invoking JavaScript test runners
33
29
  # (vitest|bun test|npm test|npx jest|node .*vitest). Preserved from Task 846.
34
30
  #
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
31
+ # 4. Logging: every PreToolUse decision emits one line in the format
44
32
  # [archive-ingest-gate] decision=<allow|block> tool=<name> reason=<r> ...
45
33
  # so the operator can grep the full decision trail for one ingest from
46
- # server.log alongside the [whatsapp-ingest] script lines.
34
+ # server.log.
47
35
  #
48
36
  # Exit codes follow Claude Code hook protocol: 0 = allow, 2 = block (stderr
49
37
  # message shown to the agent). Fail-closed on terminal stdin to match
@@ -140,21 +128,12 @@ if [ -f "$FLAG_FILE" ]; then
140
128
  fi
141
129
  fi
142
130
 
143
- # --- Block 2: legacy WhatsApp MCP tools — denied unconditionally ----------
144
- case "$TOOL_NAME" in
145
- mcp__memory__whatsapp-export-parse|mcp__memory__whatsapp-export-insight-write|mcp__memory__whatsapp-export-insight-pass)
146
- emit_decision "block" "denied-mcp-legacy" \
147
- "Blocked: ${TOOL_NAME} is a retired path. Task 891 ships the chunked-archive contract — invoke 'bash platform/plugins/whatsapp-import/bin/whatsapp-ingest.sh <archive> --owner-element-id <id> --participant-person-ids <csv> --scope <admin|public>' once. Parse, sessionize, classify (mode=chat), and memory-ingest (parentLabel=ConversationArchive) all run in-process; do not call legacy MCP tools. Phase 2 insight derivation is deferred to its own follow-up task."
148
- ;;
149
- esac
150
-
151
131
  # Helper: extract a top-level field from `tool_input` via python3 — never via
152
132
  # grep+sed against the raw JSON, which would pick the first textual occurrence
153
- # including nested-object matches (`rows[0].archiveType`,
154
- # `conversation.archiveType`, etc.) and let a malicious payload bypass the
155
- # block. python3 is the project standard for JSON-aware hook parsing
156
- # (mirrors lane-gate.sh:31-46). On parse failure return empty string
157
- # downstream block conditions skip cleanly.
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.
158
137
  extract_tool_input_field() {
159
138
  local field="$1"
160
139
  printf '%s' "$INPUT" | python3 -c "
@@ -167,18 +146,7 @@ except Exception:
167
146
  " 2>/dev/null || echo ""
168
147
  }
169
148
 
170
- # --- Block 3: memory-archive-write conditional on whatsapp-export -----------
171
- # LinkedIn and future archiveTypes flow unchanged through memory-archive-write.
172
- # Only `archiveType=whatsapp-export` is now restricted to the script path.
173
- if [ "$TOOL_NAME" = "mcp__memory__memory-archive-write" ]; then
174
- ARCHIVE_TYPE=$(extract_tool_input_field archiveType)
175
- if [ "$ARCHIVE_TYPE" = "whatsapp-export" ]; then
176
- emit_decision "block" "denied-mcp-legacy archiveType=whatsapp-export" \
177
- "Blocked: memory-archive-write with archiveType='whatsapp-export' is a retired path. Task 891 ships the chunked-archive contract — invoke 'bash platform/plugins/whatsapp-import/bin/whatsapp-ingest.sh <archive> --owner-element-id <id> --participant-person-ids <csv> --scope <admin|public>' once. Other archiveTypes (linkedin-connections, …) flow through memory-archive-write unchanged."
178
- fi
179
- fi
180
-
181
- # --- Block 4: plugin-source path block (Edit/Write/NotebookEdit) -----------
149
+ # --- Block 2: plugin-source path block (Edit/Write/NotebookEdit) -----------
182
150
  case "$TOOL_NAME" in
183
151
  Edit|Write|NotebookEdit)
184
152
  FILE_PATH=$(extract_tool_input_field file_path)
@@ -191,7 +159,7 @@ case "$TOOL_NAME" in
191
159
  ;;
192
160
  esac
193
161
 
194
- # --- Block 5: shell test-runner block (Bash) -------------------------------
162
+ # --- Block 3: shell test-runner block (Bash) -------------------------------
195
163
  COMMAND=""
196
164
  if [ "$TOOL_NAME" = "Bash" ]; then
197
165
  COMMAND=$(extract_tool_input_field command)
@@ -227,8 +227,8 @@ Invoke the `business-profile` skill, passing the `taskId` so the skill can threa
227
227
 
228
228
  Personal mode does not register a `LocalBusiness`. The `AdminUser` and personal-profile `Person` nodes were written deterministically at PIN setup time (Task 830 — `writeAdminUserAndPerson`, run as `createdBy.agent === 'system'` and therefore exempt from the action-provenance gate), so this step only enriches the existing Person with operator-identity fields and links it to the `UserProfile`:
229
229
 
230
- 1. **Ask the user for their email AND phone number** in one short conversational message {{productName}} stores both on the personal-profile Person for downstream features (notifications, contact-method matching, identity-coverage signal that the agent uses to detect missing identity in future turns). The user may decline either; record what they provide. (Operator-identity fix: the system-prompt's "Identity coverage" block surfaces missing identity fields on every turn, so a partial answer becomes a follow-up prompt rather than a silent gap.)
231
- 2. **Attach the identity to Person.** Call `profile-update` with `personFields: { email: "<value>", telephone: "<value>" }` (omit either key the user declined). The tool resolves the personal-profile Person via `(au:AdminUser {userId:$you})-[:OWNS]->(p:Person)` server-side and writes the canonical `email`/`telephone` fields. Use canonical `telephone` — `phone` is the schema synonym, not the canonical name; `profile-update` rejects `phone` rather than silently rewriting it. The tool throws if the OWNS edge is missing rather than silently no-oping (the PIN-setup path is the only place that edge is created — a missing edge means PIN setup never ran or was rolled back).
230
+ 1. **Elicit Person properties that help {{productName}} serve this operator.** Open by default identity (email, phone, names), context (where they live, languages they speak, what they do, who they work for), or anything else the operator volunteers that makes future assistance more useful. The personal-profile Person is comprehensive, not enumerated: ask in one short conversational message for what feels natural to volunteer at first contact, accept whatever the operator offers, do not chase a fixed list. The user may decline any field; record what they provide.
231
+ 2. **Persist via `profile-update.personFields`.** Pass a single object whose keys are the Person properties the operator volunteered, in the operator's volunteered phrasing — the central schema validator handles synonyms (e.g. `phone` rejects with "use telephone") and Forbidden Properties (e.g. `name` rejects with "use givenName + familyName"); the agent does not pre-rewrite. The tool resolves the personal-profile Person via `(au:AdminUser {userId:$you})-[:OWNS]->(p:Person)` server-side and throws loudly if the OWNS edge is missing rather than silently no-oping (the PIN-setup path is the only place that edge is created — a missing edge means PIN setup never ran or was rolled back).
232
232
  3. **Append the step + record the facts.** Call `task-update` with both `appendStep:"identity-attached"` AND `note:"Identity attached — email=<value-or-declined>, telephone=<value-or-declined>"` (use the actual values the operator supplied; for declines write the literal `declined`). One call, both fields. The note carries the operator-meaningful audit content; the step is the phase marker.
233
233
  4. **Link the personal-profile `Person` to the `UserProfile`.** Call `memory-search` to resolve the `UserProfile` elementId for the operator (the lazy `loadUserProfile` write created it on the first admin session). Then call `memory-update` on the Person to add the `HAS_PROFILE` edge to the UserProfile. (`HAS_PROFILE` from `:Person` is a sibling pattern to the existing `AdminUser→HAS_PROFILE→UserProfile`; both are valid sources for the same edge type. See [schema-base.md Relationship Patterns](../../../memory/references/schema-base.md).)
234
234
  5. **Close the action record.** Call `task-update` with both `appendStep:"profile-linked"` AND `note:"Profile linked — Person=<elementId>, UserProfile=<elementId>"` (the resolved elementIds from steps 2 and 4). Then call `task-complete(taskId)`.
@@ -40,7 +40,7 @@ These are enabled during onboarding and can be added or removed at any time. Som
40
40
  | `waitlist` | Waitlist lifecycle — extract sign-ups from conversations, review | — |
41
41
  | `replicate` | Image generation — three models for photorealistic, design, and fast draft images | Content producer, Research assistant |
42
42
  | `linkedin-import` | Import a LinkedIn Basic Data Export — Profile and Connections today, more CSVs as references land | Database operator |
43
- | `whatsapp-import` | Import a WhatsApp `_chat.txt` export as a chunked `:ConversationArchive` shape (the chunked-archive contract — supersedes the prior two-phase load+enrich contract). Single Bash entry — `bash platform/plugins/whatsapp-import/bin/whatsapp-ingest.sh <archive> --owner-element-id <id> --participant-person-ids <csv> --scope <admin\|public>` — runs parse → operator-confirms owner + every distinct sender → sessionize at gap-hours boundaries (default 12h) → classify each session via Haiku (`memory-classify` with `mode='chat'`) into topic-bounded `:Section:Conversation` chunks → memory-ingest with `parentLabel='ConversationArchive'`. The parent MERGEs on `conversationIdentity = sha256(accountId + ":" + sortedParticipantElementIds)` — stable across re-exports, identical for DM and group. Re-imports are delta-append: prior chunks never touched, only messages after `lastIngestedMessageHash` flow through. Auto-creating participants is forbidden — any parsed senderName outside the operator-confirmed closed set LOUD-FAILs with `parser-miss`. Phase 2 insight derivation (`:Observation` / `:Task` / `:Preference` / `:MENTIONS` against chunks) is deferred to a separate follow-up task. Distinct from the live `whatsapp` plugin which is a Baileys QR-pairing channel. | Database operator |
43
+ | WhatsApp `_chat.txt` archive | (No dedicated plugin.) Chat archives are document-shaped narrative content and route through the unified `document-ingest` skill with `mode='chat'` and `parentLabel='ConversationArchive'`. The skill confirms participants up front (owner + others, no auto-creation), computes `archiveSha256` for cleanup-on-re-ingest discipline, and lets `memory-classify`'s chat prompt produce `:Section:Conversation` chunks bounded by topic transitions. Distinct from the live `whatsapp` plugin (Baileys QR-pairing channel). | Database operator |
44
44
 
45
45
  ### Claude Official (marketplace)
46
46
 
@@ -20,8 +20,6 @@ tools:
20
20
  - memory-edit-attachment
21
21
  - memory-rename-attachment
22
22
  - memory-archive-write
23
- - whatsapp-export-parse
24
- - whatsapp-export-insight-write
25
23
  - conversation-list
26
24
  - conversation-search
27
25
  - profile-read
@@ -85,9 +83,9 @@ Graph hygiene is **agent-directed, case by case** — no autonomous rule engine,
85
83
 
86
84
  The owner's profile and preferences accumulate organically from conversation — never from questionnaires. Load the conversational memory skill via `plugin-read` for full guidance on when to observe preferences, how to handle remember/forget requests, and how to answer "what do you know about me?" transparently with confidence scores and evidence trail.
87
85
 
88
- ## UserProfile Field Management
86
+ ## UserProfile + Person Field Management
89
87
 
90
- `profile-update` serves two purposes: managing Preference nodes (category/key/value) and setting top-level UserProfile node properties via the `profileFields` parameter.
88
+ `profile-update` serves three purposes: managing Preference nodes (category/key/value), setting top-level UserProfile node properties via `profileFields`, and setting Person properties on the operator's personal-profile Person via `personFields`.
91
89
 
92
90
  Settable UserProfile fields: `timezone` (IANA format, e.g. `Europe/London`), `locale`, `givenName`, `role`, `expertise`. These are account metadata stored directly on the UserProfile node — distinct from Preference nodes, which represent learned behavioural patterns.
93
91
 
@@ -98,6 +96,8 @@ Example — setting a user's timezone:
98
96
 
99
97
  Restricted fields (`accountId`, `embedding`, `profileVersion`) cannot be set via `profileFields`.
100
98
 
99
+ **Personal-profile Person via `personFields`.** The personal-profile Person is open by default — the agent decides which Person properties best help it serve this operator (identity, contact, context); the central schema validator enforces synonym + Forbidden Properties rejection (per `references/schema-base.md`). Writes route to the personal-profile Person via the OWNS edge from the AdminUser. Validation runs in `mode: "update"` so the required-property loop is skipped on partial SET (givenName/familyName were established at PIN setup). Pass any Person properties the operator volunteers; the validator surfaces synonym (e.g. `phone`) and forbidden (e.g. `name`) errors with descriptive messages. No allow-list, no enumeration — see the schema-base.md Person row notes for the doctrine.
100
+
101
101
  ## Schema References
102
102
 
103
103
  Before any structured write, load `references/schema-base.md` via `plugin-read`. This defines property naming rules, required-property groups for documented types, forbidden-property rules, and relationship patterns. If the `LocalBusiness` node has a `businessType` property, also load the matching vertical schema (`references/schema-{businessType}.md`) — it extends the base with vertical-specific types. Confirm which schemas were consulted before writing.