@rubytech/create-realagent 1.0.826 → 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.
- package/package.json +1 -1
- package/payload/platform/neo4j/schema.cypher +35 -2
- package/payload/platform/package.json +2 -2
- package/payload/platform/plugins/admin/hooks/__tests__/archive-ingest-surface-gate.test.sh +39 -54
- package/payload/platform/plugins/admin/hooks/archive-ingest-surface-gate.sh +26 -52
- package/payload/platform/plugins/admin/skills/onboarding/SKILL.md +7 -7
- package/payload/platform/plugins/docs/references/cloudflare.md +1 -1
- package/payload/platform/plugins/docs/references/plugins-guide.md +1 -1
- package/payload/platform/plugins/docs/references/troubleshooting.md +1 -0
- package/payload/platform/plugins/memory/PLUGIN.md +5 -5
- package/payload/platform/plugins/memory/mcp/dist/index.js +18 -253
- package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/llm-classifier.test.js +51 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/llm-classifier.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-validator.test.js +103 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-validator.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.d.ts +19 -4
- package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.js +149 -56
- package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/schema-validator.d.ts +16 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/schema-validator.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/schema-validator.js +12 -3
- package/payload/platform/plugins/memory/mcp/dist/lib/schema-validator.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-write.test.js +2 -138
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-write.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest.test.js +66 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-personfields-open.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-personfields-open.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-personfields-open.test.js +148 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-personfields-open.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.d.ts +1 -64
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.js +6 -336
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.d.ts +30 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js +231 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.d.ts +21 -17
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js +77 -37
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js.map +1 -1
- package/payload/platform/plugins/memory/references/schema-base.md +7 -2
- package/payload/platform/plugins/memory/skills/document-ingest/SKILL.md +54 -4
- package/payload/platform/plugins/whatsapp/PLUGIN.md +1 -1
- package/payload/platform/plugins/whatsapp-import/lib/dist/delta-cursor.d.ts +18 -0
- package/payload/platform/plugins/whatsapp-import/lib/dist/delta-cursor.d.ts.map +1 -0
- package/payload/platform/plugins/whatsapp-import/lib/dist/delta-cursor.js +31 -0
- package/payload/platform/plugins/whatsapp-import/lib/dist/delta-cursor.js.map +1 -0
- package/payload/platform/plugins/whatsapp-import/lib/dist/derive-keys.d.ts +27 -12
- package/payload/platform/plugins/whatsapp-import/lib/dist/derive-keys.d.ts.map +1 -1
- package/payload/platform/plugins/whatsapp-import/lib/dist/derive-keys.js +40 -20
- package/payload/platform/plugins/whatsapp-import/lib/dist/derive-keys.js.map +1 -1
- package/payload/platform/plugins/whatsapp-import/lib/dist/index.d.ts +7 -4
- package/payload/platform/plugins/whatsapp-import/lib/dist/index.d.ts.map +1 -1
- package/payload/platform/plugins/whatsapp-import/lib/dist/index.js +9 -6
- package/payload/platform/plugins/whatsapp-import/lib/dist/index.js.map +1 -1
- package/payload/platform/plugins/whatsapp-import/lib/dist/sessionize.d.ts +25 -0
- package/payload/platform/plugins/whatsapp-import/lib/dist/sessionize.d.ts.map +1 -0
- package/payload/platform/plugins/whatsapp-import/lib/dist/sessionize.js +48 -0
- package/payload/platform/plugins/whatsapp-import/lib/dist/sessionize.js.map +1 -0
- package/payload/platform/plugins/whatsapp-import/lib/dist/to-classifier-input.d.ts +3 -0
- package/payload/platform/plugins/whatsapp-import/lib/dist/to-classifier-input.d.ts.map +1 -0
- package/payload/platform/plugins/whatsapp-import/lib/dist/to-classifier-input.js +47 -0
- package/payload/platform/plugins/whatsapp-import/lib/dist/to-classifier-input.js.map +1 -0
- package/payload/platform/scripts/seed-neo4j.sh +15 -14
- package/payload/platform/templates/specialists/agents/database-operator.md +10 -17
- package/payload/server/chunk-CUSH3UXP.js +2305 -0
- package/payload/server/chunk-IWNDVGKT.js +10077 -0
- package/payload/server/chunk-KC7NUABI.js +654 -0
- package/payload/server/chunk-T2OPNP3L.js +654 -0
- package/payload/server/chunk-WUVXPZIV.js +1116 -0
- package/payload/server/client-pool-3TM3SRIA.js +32 -0
- package/payload/server/cloudflare-task-tracker-4NIODMGL.js +19 -0
- package/payload/server/cloudflare-task-tracker-CR6TL4VL.js +19 -0
- package/payload/server/maxy-edge.js +3 -3
- package/payload/server/neo4j-migrations-XTQ4WEV6.js +428 -0
- package/payload/server/public/assets/{admin-DOkUspG1.js → admin-BNwPsMhJ.js} +2 -2
- package/payload/server/public/assets/{graph-LLMJa4Ch.js → graph-N_Bw-8oT.js} +1 -1
- package/payload/server/public/assets/{page-DoaF3DB0.js → page-BKLGP-th.js} +1 -1
- package/payload/server/public/graph.html +2 -2
- package/payload/server/public/index.html +2 -2
- package/payload/server/server.js +281 -168
- package/payload/platform/plugins/whatsapp-import/PLUGIN.md +0 -46
- package/payload/platform/plugins/whatsapp-import/bin/ingest.mjs +0 -670
- package/payload/platform/plugins/whatsapp-import/bin/whatsapp-ingest.sh +0 -131
- package/payload/platform/plugins/whatsapp-import/lib/src/__tests__/filter-gate.test.ts +0 -172
- package/payload/platform/plugins/whatsapp-import/lib/src/__tests__/ingest-idempotence.test.ts +0 -141
- package/payload/platform/plugins/whatsapp-import/lib/src/__tests__/parse-export-lrm.test.ts +0 -83
- package/payload/platform/plugins/whatsapp-import/lib/src/__tests__/parse-export.test.ts +0 -678
- package/payload/platform/plugins/whatsapp-import/lib/src/derive-keys.ts +0 -59
- package/payload/platform/plugins/whatsapp-import/lib/src/filter.ts +0 -136
- package/payload/platform/plugins/whatsapp-import/lib/src/index.ts +0 -19
- package/payload/platform/plugins/whatsapp-import/lib/src/parse-export.ts +0 -471
- package/payload/platform/plugins/whatsapp-import/lib/tsconfig.json +0 -9
- package/payload/platform/plugins/whatsapp-import/lib/vitest.config.ts +0 -9
- package/payload/platform/plugins/whatsapp-import/skills/whatsapp-import/SKILL.md +0 -131
- package/payload/platform/plugins/whatsapp-import/skills/whatsapp-import/references/export-parse.md +0 -109
- package/payload/platform/plugins/whatsapp-import/skills/whatsapp-import-enrich/SKILL.md +0 -333
package/package.json
CHANGED
|
@@ -258,6 +258,39 @@ OPTIONS {
|
|
|
258
258
|
}
|
|
259
259
|
};
|
|
260
260
|
|
|
261
|
+
// ----------------------------------------------------------
|
|
262
|
+
// ConversationArchive — chunked WhatsApp/messaging archive (Task 891).
|
|
263
|
+
//
|
|
264
|
+
// :ConversationArchive parent + :Section:Conversation chunks (HAS_SECTION + NEXT chain).
|
|
265
|
+
// MERGE-keyed on conversationIdentity = sha256(accountId + ":" + sortedParticipantElementIds).
|
|
266
|
+
// :Section base label is shared with KnowledgeDocument's children — chunks
|
|
267
|
+
// reuse the section_embedding vector index above and the universal fulltext
|
|
268
|
+
// index below.
|
|
269
|
+
//
|
|
270
|
+
// :PARTICIPANT_IN edges from :Person/:AdminUser to :ConversationArchive are
|
|
271
|
+
// MERGEd on every ingest (idempotent); the operator confirms each participant
|
|
272
|
+
// up front via the document-ingest skill's chat-mode participant flow
|
|
273
|
+
// (Task 894 — supersedes the prior whatsapp-import skill).
|
|
274
|
+
// ----------------------------------------------------------
|
|
275
|
+
|
|
276
|
+
CREATE CONSTRAINT conversation_archive_identity_unique IF NOT EXISTS
|
|
277
|
+
FOR (a:ConversationArchive) REQUIRE a.conversationIdentity IS UNIQUE;
|
|
278
|
+
|
|
279
|
+
CREATE INDEX conversation_archive_account IF NOT EXISTS
|
|
280
|
+
FOR (a:ConversationArchive) ON (a.accountId);
|
|
281
|
+
|
|
282
|
+
CREATE INDEX conversation_archive_session IF NOT EXISTS
|
|
283
|
+
FOR (a:ConversationArchive) ON (a.createdBySession);
|
|
284
|
+
|
|
285
|
+
CREATE VECTOR INDEX conversation_archive_embedding IF NOT EXISTS
|
|
286
|
+
FOR (a:ConversationArchive) ON (a.embedding)
|
|
287
|
+
OPTIONS {
|
|
288
|
+
indexConfig: {
|
|
289
|
+
`vector.dimensions`: 768,
|
|
290
|
+
`vector.similarity_function`: 'cosine'
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
|
|
261
294
|
// Universal full-text BM25 index for hybrid keyword search (Task 748).
|
|
262
295
|
//
|
|
263
296
|
// Every operator-meaningful label written by the platform is in the index union;
|
|
@@ -276,7 +309,7 @@ OPTIONS {
|
|
|
276
309
|
// Label union — every operator-meaningful label:
|
|
277
310
|
// - Business identity: LocalBusiness, Service, PriceSpecification, OpeningHoursSpecification, Organization
|
|
278
311
|
// - People: Person, UserProfile, Preference, AdminUser, AccessGrant
|
|
279
|
-
// - Knowledge: KnowledgeDocument, Section, Chunk (legacy), DigitalDocument, CreativeWork,
|
|
312
|
+
// - Knowledge: KnowledgeDocument, ConversationArchive (Task 891), Section, Chunk (legacy), DigitalDocument, CreativeWork,
|
|
280
313
|
// Question, FAQPage, DefinedTerm, Review, ImageObject
|
|
281
314
|
// - Conversational: Conversation, AdminConversation, PublicConversation, Message,
|
|
282
315
|
// UserMessage, AssistantMessage, ToolCall
|
|
@@ -309,7 +342,7 @@ OPTIONS {
|
|
|
309
342
|
CREATE FULLTEXT INDEX entity_search IF NOT EXISTS
|
|
310
343
|
FOR (n:LocalBusiness|Service|PriceSpecification|OpeningHoursSpecification|Organization
|
|
311
344
|
|Person|UserProfile|Preference|AdminUser|AccessGrant
|
|
312
|
-
|KnowledgeDocument|Section|Chunk|DigitalDocument|CreativeWork|Question|FAQPage|DefinedTerm|Review|ImageObject
|
|
345
|
+
|KnowledgeDocument|ConversationArchive|Section|Chunk|DigitalDocument|CreativeWork|Question|FAQPage|DefinedTerm|Review|ImageObject
|
|
313
346
|
|Conversation|AdminConversation|PublicConversation|Message|UserMessage|AssistantMessage|ToolCall
|
|
314
347
|
|Task|Project|Event
|
|
315
348
|
|Workflow|WorkflowStep|WorkflowRun|StepResult
|
|
@@ -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 &&
|
|
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
|
|
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
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
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-connections → ALLOWED
|
|
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
|
-
#
|
|
61
|
+
# Plugin-source-edit block ------------------------------------------------
|
|
55
62
|
|
|
56
|
-
run_case "Edit on platform/plugins/
|
|
57
|
-
'{"hook_event_name":"PreToolUse","tool_name":"Edit","tool_input":{"file_path":"/Users/x/repo/platform/plugins/
|
|
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
|
|
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
|
-
#
|
|
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/
|
|
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":"
|
|
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":"
|
|
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,22 +1,24 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# Archive-ingest surface gate (Task 855
|
|
2
|
+
# Archive-ingest surface gate (Task 855, updated by Task 891, trimmed by Task 894).
|
|
3
3
|
#
|
|
4
|
-
#
|
|
5
|
-
# Task 855
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
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.
|
|
9
14
|
#
|
|
10
|
-
# 1.
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
# `whatsapp-export`; LinkedIn
|
|
18
|
-
# and other archiveTypes pass
|
|
19
|
-
# 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.
|
|
20
22
|
#
|
|
21
23
|
# 2. PreToolUse Edit/Write/NotebookEdit: deny writes under
|
|
22
24
|
# `*platform/plugins/*/lib/*` (parser/CSV-shape source for any *-import or
|
|
@@ -26,18 +28,10 @@
|
|
|
26
28
|
# 3. PreToolUse Bash: deny commands invoking JavaScript test runners
|
|
27
29
|
# (vitest|bun test|npm test|npx jest|node .*vitest). Preserved from Task 846.
|
|
28
30
|
#
|
|
29
|
-
# 4.
|
|
30
|
-
# `mcp__*__*-import-parse` tool whose `tool_response.isError == true`
|
|
31
|
-
# writes a flag file. Subsequent PreToolUse on ANY tool blocks until
|
|
32
|
-
# UserPromptSubmit clears the flag. A 600s TTL is the cross-session safety
|
|
33
|
-
# net. Preserved from Task 846 because LinkedIn and future per-source archive
|
|
34
|
-
# parsers still use the legacy MCP path until they migrate to their own
|
|
35
|
-
# deterministic Bash entries.
|
|
36
|
-
#
|
|
37
|
-
# 5. Logging: every PreToolUse decision emits one line in the format
|
|
31
|
+
# 4. Logging: every PreToolUse decision emits one line in the format
|
|
38
32
|
# [archive-ingest-gate] decision=<allow|block> tool=<name> reason=<r> ...
|
|
39
33
|
# so the operator can grep the full decision trail for one ingest from
|
|
40
|
-
# server.log
|
|
34
|
+
# server.log.
|
|
41
35
|
#
|
|
42
36
|
# Exit codes follow Claude Code hook protocol: 0 = allow, 2 = block (stderr
|
|
43
37
|
# message shown to the agent). Fail-closed on terminal stdin to match
|
|
@@ -134,21 +128,12 @@ if [ -f "$FLAG_FILE" ]; then
|
|
|
134
128
|
fi
|
|
135
129
|
fi
|
|
136
130
|
|
|
137
|
-
# --- Block 2: legacy WhatsApp MCP tools — denied unconditionally ----------
|
|
138
|
-
case "$TOOL_NAME" in
|
|
139
|
-
mcp__memory__whatsapp-export-parse|mcp__memory__whatsapp-export-insight-write)
|
|
140
|
-
emit_decision "block" "denied-mcp-legacy" \
|
|
141
|
-
"Blocked: ${TOOL_NAME} is the Task 804 legacy path. Task 855 ships the deterministic Bash entry — invoke 'bash platform/plugins/whatsapp-import/bin/whatsapp-ingest.sh <archive> --owner-element-id <id> --subject-person-id <id> --scope <admin|public> --filter <all|senders=<csv>|date-range=<from>..<to>>' once. Parse, archive-write, and insight all run in-process; do not call legacy MCP tools."
|
|
142
|
-
;;
|
|
143
|
-
esac
|
|
144
|
-
|
|
145
131
|
# Helper: extract a top-level field from `tool_input` via python3 — never via
|
|
146
132
|
# grep+sed against the raw JSON, which would pick the first textual occurrence
|
|
147
|
-
# including nested-object matches (`rows[0].archiveType`,
|
|
148
|
-
#
|
|
149
|
-
#
|
|
150
|
-
#
|
|
151
|
-
# 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.
|
|
152
137
|
extract_tool_input_field() {
|
|
153
138
|
local field="$1"
|
|
154
139
|
printf '%s' "$INPUT" | python3 -c "
|
|
@@ -161,18 +146,7 @@ except Exception:
|
|
|
161
146
|
" 2>/dev/null || echo ""
|
|
162
147
|
}
|
|
163
148
|
|
|
164
|
-
# --- Block
|
|
165
|
-
# LinkedIn and future archiveTypes flow unchanged through memory-archive-write.
|
|
166
|
-
# Only `archiveType=whatsapp-export` is now restricted to the script path.
|
|
167
|
-
if [ "$TOOL_NAME" = "mcp__memory__memory-archive-write" ]; then
|
|
168
|
-
ARCHIVE_TYPE=$(extract_tool_input_field archiveType)
|
|
169
|
-
if [ "$ARCHIVE_TYPE" = "whatsapp-export" ]; then
|
|
170
|
-
emit_decision "block" "denied-mcp-legacy archiveType=whatsapp-export" \
|
|
171
|
-
"Blocked: memory-archive-write with archiveType='whatsapp-export' is the Task 804 legacy path. Task 855 ships the deterministic Bash entry — invoke 'bash platform/plugins/whatsapp-import/bin/whatsapp-ingest.sh <archive> --owner-element-id <id> --subject-person-id <id> --scope <admin|public> --filter <all|senders=<csv>|date-range=<from>..<to>>' once. Other archiveTypes (linkedin-connections, …) flow through memory-archive-write unchanged."
|
|
172
|
-
fi
|
|
173
|
-
fi
|
|
174
|
-
|
|
175
|
-
# --- Block 4: plugin-source path block (Edit/Write/NotebookEdit) -----------
|
|
149
|
+
# --- Block 2: plugin-source path block (Edit/Write/NotebookEdit) -----------
|
|
176
150
|
case "$TOOL_NAME" in
|
|
177
151
|
Edit|Write|NotebookEdit)
|
|
178
152
|
FILE_PATH=$(extract_tool_input_field file_path)
|
|
@@ -185,7 +159,7 @@ case "$TOOL_NAME" in
|
|
|
185
159
|
;;
|
|
186
160
|
esac
|
|
187
161
|
|
|
188
|
-
# --- Block
|
|
162
|
+
# --- Block 3: shell test-runner block (Bash) -------------------------------
|
|
189
163
|
COMMAND=""
|
|
190
164
|
if [ "$TOOL_NAME" = "Bash" ]; then
|
|
191
165
|
COMMAND=$(extract_tool_input_field command)
|
|
@@ -210,28 +210,28 @@ Pin the operator's persona and bootstrap the graph nodes that satisfy the graph-
|
|
|
210
210
|
- `status`: `"running"`
|
|
211
211
|
- `kind`: `"onboarding-establish-owner"`
|
|
212
212
|
- `inputsProvided`: the keys you actually pass on `inputs` (e.g. `["mode"]`); records the call shape
|
|
213
|
-
- `inputs`: the form payload
|
|
213
|
+
- `inputs`: the form payload known at creation time — `{ mode: "personal" | "business-owner" }`
|
|
214
214
|
- `inputSchema`: `{ secretFields: [] }` — the step-9 mode select carries no secrets; declare the empty list explicitly so the contract is visible at the call site
|
|
215
215
|
|
|
216
216
|
The returned `taskId` is the action-provenance handle for this step — every subsequent `memory-write` for an action-provenance-gated label (`Person`, `UserProfile`, `AdminUser`, `Organization`, `LocalBusiness`) MUST pass it as `producedByTaskId` so the inbound `:PRODUCED` edge from the Task is composed into the write. The Task is auto-linked to the current `AdminConversation` via `RAISED_DURING` (this is what makes `MATCH (c:AdminConversation)<-[:RAISED_DURING]-(t:Task)-[:PRODUCED]->(entity)` traversable from the conversation that initiated onboarding).
|
|
217
217
|
|
|
218
|
-
The `
|
|
218
|
+
**Recording what you collect.** The `onboarding-establish-owner` Task is the audit record for step 9. Record every operator-meaningful fact you collect using the existing surfaces — `description` for the running summary, `note` (via `task-update`) for facts as they land (email collected, phone declined, resolved Person/UserProfile elementIds), `appendStep` for phase markers. Agent's judgement decides which slot. Contract: by `task-complete`, a reader of the Task properties can reconstruct what was collected, what was declined, and which entities were touched without consulting any other source. No secrets in any property regardless of slot — `task-create`'s `inputs` is centrally redacted via `inputSchema.secretFields`; the post-creation slots carry no schema, so the agent's responsibility is to never write secret material into them.
|
|
219
219
|
|
|
220
220
|
Then branch on the mode.
|
|
221
221
|
|
|
222
222
|
### `business-owner`
|
|
223
223
|
|
|
224
|
-
Invoke the `business-profile` skill, passing the `taskId` so the skill can thread it as `producedByTaskId` into every `memory-write` it issues. The skill follows its first-run path: create the `AdminUser` node, create the `LocalBusiness` node, create the `Organization` node, collect identity + address + whichever additional domains (hours, services, FAQs, brand assets) the user provides. When `business-profile` reports that the required nodes exist in the graph, call `task-update
|
|
224
|
+
Invoke the `business-profile` skill, passing the `taskId` so the skill can thread it as `producedByTaskId` into every `memory-write` it issues. The skill follows its first-run path: create the `AdminUser` node, create the `LocalBusiness` node, create the `Organization` node, collect identity + address + whichever additional domains (hours, services, FAQs, brand assets) the user provides. When `business-profile` reports that the required nodes exist in the graph, call `task-update` with both `appendStep:"business-profile-complete"` AND `note:"Business profile complete — AdminUser=<elementId>, LocalBusiness=<elementId>, Organization=<elementId>"` (the resolved elementIds from the skill's writes; one call carries both the phase marker and the audit content). Then write the `HAS_PROFILE` edge from the personal-profile `Person` to the operator's `UserProfile` via `memory-update` (one `memory-search` to resolve both elementIds; the `UserProfile` already exists from step 6 onwards via the lazy-create in `loadUserProfile`). Then call `task-complete(taskId)` and `onboarding-complete-step` with step 9. Do not mark step 9 complete before the required nodes + the HAS_PROFILE edge exist — the gate's precondition must be real, not just recorded.
|
|
225
225
|
|
|
226
226
|
### `personal`
|
|
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. **
|
|
231
|
-
2. **
|
|
232
|
-
3. **Append the step.** Call `task-update
|
|
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
|
+
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
|
-
5. **Close the action record.** Call `task-update
|
|
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)`.
|
|
235
235
|
6. **Mark step 9 complete.** Call `onboarding-complete-step` with step 9.
|
|
236
236
|
|
|
237
237
|
After step 9 completes in personal mode, tell the user that {{productName}} is configured for personal use — their employer (if any) is not registered here. If they later become the operator for a business of their own, they can ask {{productName}} to set up a business profile, which invokes the `business-profile` skill directly.
|
|
@@ -22,7 +22,7 @@ Ask the agent to set up Cloudflare. The agent first confirms the domain is alrea
|
|
|
22
22
|
- **Proxy apex** — optional bare-domain hostname (e.g. `yourdomain.com`) that should also serve the public agent.
|
|
23
23
|
- **Admin password** — the password used to gate remote access to the admin surface.
|
|
24
24
|
|
|
25
|
-
When you submit, the `/api/admin/cloudflare/setup` endpoint runs — in strict order — `setRemotePassword`, launches a `cloudflare-setup` action (earlier platform fixes: `systemd-run --user` transient unit wrapping `setup-tunnel.sh <brand> <port> <hostname...>`), and registers a post-exit handler
|
|
25
|
+
When you submit, the `/api/admin/cloudflare/setup` endpoint runs — in strict order — `setRemotePassword`, launches a `cloudflare-setup` action (earlier platform fixes: `systemd-run --user` transient unit wrapping `setup-tunnel.sh <brand> <port> <hostname...>`), and registers a post-exit handler that writes alias-domains for every non-`public.*` public or apex hostname (so e.g. `chat.yourdomain.com` is classified as public by `isPublicHost`) and closes the audit Task. When the post-exit handler observes the systemd-run unit gone before its terminal status was sampled (a known systemd-run cgroup race), it consults `tunnel.state` on disk and verifies the tunnel identity matches the form you submitted; if so, the audit Task closes `completed` despite the missed observation. Otherwise it closes `failed` with `endpoint-died-pre-reconcile` and the boot reconciler may still recover the Task at next restart. The script runs end-to-end:
|
|
26
26
|
|
|
27
27
|
- `cloudflared tunnel login` — OAuth browser sign-in. The VNC browser opens the Cloudflare authorize page; pick the account that owns your domain, click Authorize. `cert.pem` lands.
|
|
28
28
|
- Tunnel resolution from operator-supplied identity (operator-selected-tunnel fix). The form populates a tunnel-select dropdown from `GET /api/admin/cloudflare/tunnels` (which calls `cloudflared tunnel list --output json` on your logged-in account). You either pick an existing tunnel from the list or type a name to create a new one. The form posts EXACTLY ONE of `{tunnelId, tunnelName}`; the script enforces the same constraint as defence in depth. Pre-fix the script derived `${BRAND}-$(hostname -s)` locally; that broke the operator-state-is-authoritative doctrine and silently created orphan tunnels whenever the device hostname changed. Stream log emits `step=tunnel-resolve source=operator-selected|operator-created tunnel_id=… tunnel_name=…` once the UUID is known.
|
|
@@ -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
|
-
|
|
|
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
|
|
|
@@ -207,6 +207,7 @@ Failure modes:
|
|
|
207
207
|
- `[client-event] kind=post-restart-resume phase=start` absent: form's CustomEvent never reached the chat hook (regression in form `onExit` or chat-listener mount).
|
|
208
208
|
- `[client-event] phase=start` present, `[admin-resume] reason=post-restart` absent: `waitForRestartCycle` exhausted its bound (brand never restarted) or `/resume` rejected — check `phase=health-timeout` / `phase=resume-rejected`.
|
|
209
209
|
- All four present except `[persist] role=user … Cloudflare setup completed`: marker chat POST failed — check the chat surface for an inline error or a `phase=resume-error` line.
|
|
210
|
+
- Refresh after a successful Cloudflare setup shows a visible `Cloudflare setup completed (actionId: …)` user bubble: the resume mappers' synthetic-marker suppression broke or the marker shape drifted. Server-side `[admin-resume] syntheticHidden=<n>` field on the same line counts user rows the client should hide (`_componentDone` envelopes, `_lifecycle` envelopes, and the `Cloudflare setup completed (actionId: …)` literal) — a count of 0 against a refresh that should have hidden one means the helper at `platform/ui/app/lib/synthetic-marker.ts` no longer recognises the producing literal (check `CloudflareSetupForm.tsx` and `useAdminChat.ts` for shape drift).
|
|
210
211
|
|
|
211
212
|
---
|
|
212
213
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: memory
|
|
3
|
-
description: "Graph memory plugin. Provides memory-search, memory-rank, memory-write, and memory-update tools for reading from, writing to, and updating the Neo4j knowledge graph. Includes conversational memory — organic preference learning, evidence-backed recall, and transparent 'what do you know about me?' responses."
|
|
3
|
+
description: "Graph memory plugin. Provides memory-search, memory-rank, memory-write, and memory-update tools for reading from, writing to, and updating the Neo4j knowledge graph. Includes conversational memory — organic preference learning, evidence-backed recall, and transparent 'what do you know about me?' responses. Document ingestion (memory-classify + memory-ingest) supports two modes: `document` (default) for unstructured PDF/web content → KnowledgeDocument + Section, and `chat` for chat archives → ConversationArchive + Section:Conversation chunks (the chunked-archive contract)."
|
|
4
4
|
tools:
|
|
5
5
|
- memory-search
|
|
6
6
|
- memory-rank
|
|
@@ -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
|
|
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.
|