@rubytech/create-maxy-code 0.1.159 → 0.1.161
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/payload/platform/lib/graph-search/src/__tests__/fulltext-coverage.test.ts +48 -17
- package/payload/platform/neo4j/schema.cypher +59 -36
- package/payload/platform/plugins/.claude-plugin/marketplace.json +5 -0
- package/payload/platform/plugins/admin/hooks/archive-ingest-surface-gate.sh +2 -2
- package/payload/platform/plugins/admin/skills/business-profile/SKILL.md +1 -1
- package/payload/platform/plugins/docs/references/plugins-guide.md +3 -2
- package/payload/platform/plugins/email/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/email/PLUGIN.md +5 -1
- package/payload/platform/plugins/email/mcp/dist/index.js +25 -0
- package/payload/platform/plugins/email/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/conversation-archive-dispatch.d.ts +8 -0
- package/payload/platform/plugins/email/mcp/dist/lib/conversation-archive-dispatch.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/conversation-archive-dispatch.js +177 -0
- package/payload/platform/plugins/email/mcp/dist/lib/conversation-archive-dispatch.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/graph.d.ts +6 -54
- package/payload/platform/plugins/email/mcp/dist/lib/graph.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/graph.js +12 -300
- package/payload/platform/plugins/email/mcp/dist/lib/graph.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-classify.d.ts +6 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-classify.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-classify.js +89 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-classify.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-ingest.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-ingest.js +11 -6
- package/payload/platform/plugins/email/mcp/dist/tools/email-ingest.js.map +1 -1
- package/payload/platform/plugins/linkedin-import/skills/linkedin-import/SKILL.md +10 -37
- package/payload/platform/plugins/linkedin-import/skills/linkedin-import/references/connections.md +1 -0
- package/payload/platform/plugins/memory/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/memory/PLUGIN.md +10 -1
- package/payload/platform/plugins/memory/bin/conversation-archive-ingest.mjs +139 -439
- package/payload/platform/plugins/memory/bin/conversation-archive-ingest.sh +15 -47
- package/payload/platform/plugins/memory/mcp/dist/index.js +168 -29
- package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/compiled-truth-revision.test.js +43 -27
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/compiled-truth-revision.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/label-delete-gate.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/label-delete-gate.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/label-delete-gate.test.js +62 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/label-delete-gate.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-cypher-drift.test.js +5 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-cypher-drift.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/typed-edge-schema.test.js +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/typed-edge-schema.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-revision.d.ts +42 -25
- package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-revision.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-revision.js +23 -17
- package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-revision.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/email.d.ts +3 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/email.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/email.js +61 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/email.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/index.d.ts +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/index.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/index.js +3 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/index.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/types.d.ts +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/types.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/types.js +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/types.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/derive-keys.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/derive-keys.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/derive-keys.js +9 -3
- package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/derive-keys.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/kd-classify-gate.d.ts +41 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/kd-classify-gate.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/kd-classify-gate.js +71 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/kd-classify-gate.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/label-delete-gate.d.ts +18 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/label-delete-gate.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/label-delete-gate.js +31 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/label-delete-gate.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-derive-insights.test.d.ts +5 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-derive-insights.test.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-derive-insights.test.js +162 -78
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-derive-insights.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-enrich-rejection.test.js +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-enrich-rejection.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-source-agnosticism.test.js +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-source-agnosticism.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-compiled-truth-history.test.js +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-compiled-truth-history.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-delete-emit.test.js +6 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-delete-emit.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-delete-reserved-label.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-delete-reserved-label.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-delete-reserved-label.test.js +141 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-delete-reserved-label.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-empty-trash-reserved-label.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-empty-trash-reserved-label.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-empty-trash-reserved-label.test.js +86 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-empty-trash-reserved-label.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.d.ts +33 -34
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.js +94 -273
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-list-chunks.d.ts +43 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-list-chunks.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-list-chunks.js +96 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-list-chunks.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/kd-classify.d.ts +6 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/kd-classify.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/kd-classify.js +73 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/kd-classify.js.map +1 -0
- 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 +0 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-compiled-truth-history.d.ts +6 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-compiled-truth-history.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-compiled-truth-history.js +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-compiled-truth-history.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-delete.d.ts +8 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-delete.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-delete.js +40 -5
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-delete.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-empty-trash.d.ts +7 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-empty-trash.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-empty-trash.js +17 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-empty-trash.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js +11 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-node-exists.d.ts +28 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-node-exists.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-node-exists.js +46 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-node-exists.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.js +26 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/vitest.config.ts +5 -4
- package/payload/platform/plugins/memory/references/schema-base.md +4 -6
- package/payload/platform/plugins/memory/skills/conversation-archive/SKILL.md +100 -116
- package/payload/platform/plugins/memory/skills/conversation-archive-enrich/SKILL.md +74 -24
- package/payload/platform/plugins/notion-import/skills/notion-import/SKILL.md +1 -1
- package/payload/platform/plugins/obsidian-import/skills/obsidian-import/SKILL.md +1 -1
- package/payload/platform/plugins/substack-import/.claude-plugin/plugin.json +8 -0
- package/payload/platform/plugins/substack-import/PLUGIN.md +34 -0
- package/payload/platform/plugins/substack-import/skills/substack-import/SKILL.md +182 -0
- package/payload/platform/plugins/substack-import/skills/substack-import/references/archive-shape.md +68 -0
- package/payload/platform/plugins/substack-import/skills/substack-import/references/attachments.md +72 -0
- package/payload/platform/plugins/substack-import/skills/substack-import/references/engagement.md +61 -0
- package/payload/platform/plugins/substack-import/skills/substack-import/references/posts.md +80 -0
- package/payload/platform/plugins/substack-import/skills/substack-import/references/subscribers.md +74 -0
- package/payload/platform/plugins/x-import/skills/x-import/SKILL.md +1 -1
- package/payload/platform/templates/specialists/agents/archive-ingest-operator.md +45 -0
- package/payload/platform/templates/specialists/agents/database-operator.md +5 -17
- package/payload/platform/templates/specialists/agents/librarian.md +1 -1
|
@@ -1,43 +1,29 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# =============================================================================
|
|
3
|
-
# conversation-archive-ingest.sh —
|
|
4
|
-
# conversation-archive
|
|
5
|
-
# env-only identity, progress-file path on stdout).
|
|
3
|
+
# conversation-archive-ingest.sh — Bash wrapper around the
|
|
4
|
+
# conversation-archive prepare helper (Task 435).
|
|
6
5
|
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
# cursor) → sessionize delta at gap-hours boundary → for each session: classify
|
|
14
|
-
# via Haiku (mode='chat') + memory-ingest immediately (per-session checkpoint).
|
|
6
|
+
# Post-Task-435 contract: the wrapper validates env (ACCOUNT_ID, USER_ID,
|
|
7
|
+
# NEO4J_URI, NEO4J_PASSWORD) and argv, then exec's the .mjs which runs the
|
|
8
|
+
# deterministic pipeline (normalise → verify participants → derive
|
|
9
|
+
# conversationIdentity → look up prior archive cursor → slice delta →
|
|
10
|
+
# sessionize → emit JSON). Classify + memory-ingest move into the dispatched
|
|
11
|
+
# specialist's per-turn work (driven by SKILL.md).
|
|
15
12
|
#
|
|
16
13
|
# Usage:
|
|
17
14
|
# bash conversation-archive-ingest.sh <archive-path>
|
|
18
|
-
# --source <whatsapp|telegram|signal|linkedin-messages|zoom-transcript|meeting-minutes|imessage|slack|other>
|
|
15
|
+
# --source <whatsapp|telegram|signal|linkedin-messages|zoom-transcript|meeting-minutes|imessage|slack|x-dm|other>
|
|
19
16
|
# --participant-person-ids <csv>
|
|
20
17
|
# --scope <admin|public>
|
|
21
|
-
# [--rebuild]
|
|
18
|
+
# [--rebuild]
|
|
22
19
|
# [--timezone <iana-zone>]
|
|
23
|
-
# [--date-format <DD/MM/YY|MM/DD/YY|DD/MM/YYYY|MM/DD/YYYY>]
|
|
24
|
-
# [--session-id <id>]
|
|
25
|
-
#
|
|
26
|
-
# Sessions split deterministically at 8h gap (fixed code constant).
|
|
27
|
-
# `--session-gap-hours` is REMOVED; passing it FAILs at phase=argv.
|
|
20
|
+
# [--date-format <DD/MM/YY|MM/DD/YY|DD/MM/YYYY|MM/DD/YYYY>]
|
|
28
21
|
#
|
|
29
|
-
# Identity:
|
|
30
|
-
#
|
|
31
|
-
# elementId is derived from (accountId, userId) inside the bin via Cypher.
|
|
32
|
-
# `--account-id` and `--owner-element-id` flags are GONE — env-only identity
|
|
33
|
-
# is doctrine. `--participant-person-ids` carries the operator-confirmed
|
|
34
|
-
# non-owner set.
|
|
22
|
+
# Identity: ACCOUNT_ID + USER_ID come from the caller's env (plumbed by
|
|
23
|
+
# spawn-env.ts). Owner :AdminUser elementId is derived inside the .mjs.
|
|
35
24
|
#
|
|
36
|
-
# Exit 0
|
|
25
|
+
# Exit 0 + one JSON line on stdout on success.
|
|
37
26
|
# Exit !0 + one [conversation-archive] FAIL line on stderr on failure.
|
|
38
|
-
# Stdout (always, before exec node): `[conversation-archive] progress-file=<absolute-path>`
|
|
39
|
-
# Mid-run progress: line-by-line writes to that progress-file (fsync per line);
|
|
40
|
-
# the agent's heartbeat polls it via `tail -n <N> <path>` every 30 s.
|
|
41
27
|
# =============================================================================
|
|
42
28
|
|
|
43
29
|
set -euo pipefail
|
|
@@ -56,10 +42,6 @@ if [ ! -f "$INGEST_MJS" ]; then
|
|
|
56
42
|
arg_fail "conversation-archive-ingest.mjs not found at $INGEST_MJS — run from a built install"
|
|
57
43
|
fi
|
|
58
44
|
|
|
59
|
-
# Identity is env-only. The mjs LOUD-FAILs on missing/malformed
|
|
60
|
-
# values, but we mirror the cheap accountId presence check here so the
|
|
61
|
-
# progress-file path can be computed and emitted before exec node — without
|
|
62
|
-
# accountId, the path is undefined.
|
|
63
45
|
if [ -z "${ACCOUNT_ID:-}" ]; then
|
|
64
46
|
arg_fail "ACCOUNT_ID env missing — bin must be invoked under an authenticated agent context (set by spawn-env.ts)"
|
|
65
47
|
fi
|
|
@@ -74,7 +56,6 @@ HAS_PARTICIPANTS=0
|
|
|
74
56
|
PARTICIPANTS_VAL=""
|
|
75
57
|
HAS_SCOPE=0
|
|
76
58
|
SCOPE_VAL=""
|
|
77
|
-
SESSION_ID_VAL=""
|
|
78
59
|
|
|
79
60
|
ARGS=("$@")
|
|
80
61
|
i=0
|
|
@@ -84,7 +65,6 @@ while [ $i -lt ${#ARGS[@]} ]; do
|
|
|
84
65
|
--source) HAS_SOURCE=1; SOURCE_VAL="${ARGS[$((i + 1))]:-}"; i=$((i + 2)); continue ;;
|
|
85
66
|
--participant-person-ids) HAS_PARTICIPANTS=1; PARTICIPANTS_VAL="${ARGS[$((i + 1))]:-}"; i=$((i + 2)); continue ;;
|
|
86
67
|
--scope) HAS_SCOPE=1; SCOPE_VAL="${ARGS[$((i + 1))]:-}"; i=$((i + 2)); continue ;;
|
|
87
|
-
--session-id) SESSION_ID_VAL="${ARGS[$((i + 1))]:-}"; i=$((i + 2)); continue ;;
|
|
88
68
|
--rebuild) i=$((i + 1)); continue ;;
|
|
89
69
|
--session-gap-hours) arg_fail "--session-gap-hours is no longer supported; gap is fixed at 8h. Use --rebuild on the prior export bytes if you need to re-classify." ;;
|
|
90
70
|
--timezone|--date-format) i=$((i + 2)); continue ;;
|
|
@@ -98,7 +78,7 @@ while [ $i -lt ${#ARGS[@]} ]; do
|
|
|
98
78
|
done
|
|
99
79
|
|
|
100
80
|
[ -n "$ARCHIVE" ] || arg_fail "missing positional <archive>"
|
|
101
|
-
[ "$HAS_SOURCE" -eq 1 ] && [ -n "$SOURCE_VAL" ] || arg_fail "missing --source (whatsapp|telegram|signal|linkedin-messages|zoom-transcript|meeting-minutes|imessage|slack|other)"
|
|
81
|
+
[ "$HAS_SOURCE" -eq 1 ] && [ -n "$SOURCE_VAL" ] || arg_fail "missing --source (whatsapp|telegram|signal|linkedin-messages|zoom-transcript|meeting-minutes|imessage|slack|x-dm|other)"
|
|
102
82
|
[ "$HAS_PARTICIPANTS" -eq 1 ] && [ -n "$PARTICIPANTS_VAL" ] || arg_fail "missing --participant-person-ids (csv of operator-confirmed :Person/:AdminUser elementIds, owner excluded)"
|
|
103
83
|
[ "$HAS_SCOPE" -eq 1 ] && [ -n "$SCOPE_VAL" ] || arg_fail "missing --scope (or empty value)"
|
|
104
84
|
case "$SCOPE_VAL" in
|
|
@@ -123,16 +103,4 @@ fi
|
|
|
123
103
|
export NEO4J_USER="${NEO4J_USER:-neo4j}"
|
|
124
104
|
export MAXY_PLATFORM_ROOT="$PLATFORM_ROOT"
|
|
125
105
|
|
|
126
|
-
# Progress-file path must match the path the mjs opens (deterministic from
|
|
127
|
-
# (ACCOUNT_ID, sessionId)). When the operator omits --session-id, the mjs
|
|
128
|
-
# mints one — the wrapper passes through the same value via env so both
|
|
129
|
-
# sides converge on one path.
|
|
130
|
-
if [ -z "$SESSION_ID_VAL" ]; then
|
|
131
|
-
SESSION_ID_VAL="conversation-archive:${SOURCE_VAL}:$(date +%s%N)"
|
|
132
|
-
ARGS+=(--session-id "$SESSION_ID_VAL")
|
|
133
|
-
fi
|
|
134
|
-
INSTALL_DIR="$(cd "$PLATFORM_ROOT/.." && pwd)"
|
|
135
|
-
PROGRESS_FILE="$INSTALL_DIR/data/accounts/$ACCOUNT_ID/logs/conversation-archive-$SESSION_ID_VAL.log"
|
|
136
|
-
echo "[conversation-archive] progress-file=$PROGRESS_FILE"
|
|
137
|
-
|
|
138
106
|
exec node "$INGEST_MJS" "${ARGS[@]}"
|
|
@@ -12,6 +12,8 @@ import { buildLiveSchemaSource, defaultSchemaCypherPath, } from "./lib/live-sche
|
|
|
12
12
|
import { memoryReindex } from "./tools/memory-reindex.js";
|
|
13
13
|
import { memoryIngestExtract } from "./tools/memory-ingest-extract.js";
|
|
14
14
|
import { memoryIngest } from "./tools/memory-ingest.js";
|
|
15
|
+
import { CONVERSATION_SOURCES } from "./lib/conversation-normalisers/types.js";
|
|
16
|
+
import { kdClassify } from "./tools/kd-classify.js";
|
|
15
17
|
import { memoryArchiveWrite } from "./tools/memory-archive-write.js";
|
|
16
18
|
import { obsidianVaultImport, } from "./tools/obsidian-vault-import.js";
|
|
17
19
|
// whatsapp-export-{parse,preview,insight-write} deleted. Conversation
|
|
@@ -25,6 +27,7 @@ import { memoryIngestWeb } from "./tools/memory-ingest-web.js";
|
|
|
25
27
|
import { memoryUpdate } from "./tools/memory-update.js";
|
|
26
28
|
import { memoryDelete } from "./tools/memory-delete.js";
|
|
27
29
|
import { memoryFindCandidates } from "./tools/memory-find-candidates.js";
|
|
30
|
+
import { memoryNodeExists, ALLOWED_LABELS as MEMORY_NODE_EXISTS_LABELS } from "./tools/memory-node-exists.js";
|
|
28
31
|
import { memoryRestore } from "./tools/memory-restore.js";
|
|
29
32
|
import { memoryEmptyTrash } from "./tools/memory-empty-trash.js";
|
|
30
33
|
import { memoryListAttachments } from "./tools/memory-list-attachments.js";
|
|
@@ -39,7 +42,8 @@ import { graphPruneDenylistAdd } from "./tools/graph-prune-denylist-add.js";
|
|
|
39
42
|
import { graphPruneDenylistList } from "./tools/graph-prune-denylist-list.js";
|
|
40
43
|
import { graphPruneDenylistRemove } from "./tools/graph-prune-denylist-remove.js";
|
|
41
44
|
import { conversationMemoryExpunge } from "./tools/conversation-memory-expunge.js";
|
|
42
|
-
import { conversationArchiveDeriveInsights, } from "./tools/conversation-archive-derive-insights.js";
|
|
45
|
+
import { conversationArchiveDeriveInsights, PROPOSAL_KINDS, } from "./tools/conversation-archive-derive-insights.js";
|
|
46
|
+
import { conversationArchiveListChunks } from "./tools/conversation-archive-list-chunks.js";
|
|
43
47
|
import { conversationArchiveEnrichRejection } from "./tools/conversation-archive-enrich-rejection.js";
|
|
44
48
|
import { fetchImageAsContent } from "./tools/image-fetch.js";
|
|
45
49
|
import { memoryTypedEdgePass } from "./tools/memory-typed-edge-pass.js";
|
|
@@ -295,16 +299,12 @@ eagerTool(server, "memory-search", "Search the knowledge graph using semantic ve
|
|
|
295
299
|
}
|
|
296
300
|
});
|
|
297
301
|
// ---------------------------------------------------------------------------
|
|
298
|
-
// conversation-archive Phase 2
|
|
299
|
-
//
|
|
300
|
-
// archive
|
|
301
|
-
//
|
|
302
|
-
// Returns operator-facing proposals with the cypher needed to wire them;
|
|
303
|
-
// the conversation-archive-enrich skill drives the per-row operator gate.
|
|
304
|
-
// The tool itself NEVER writes; idempotency lives on the MERGE-key shape
|
|
305
|
-
// (chunkElementId, kind, contentHash) encoded in each proposal's mergeCypher.
|
|
302
|
+
// conversation-archive Phase 2 — chunk page reader (Task 433).
|
|
303
|
+
// Returns one page of :Section chunk bodies for the named :ConversationArchive
|
|
304
|
+
// so the conversation-archive-enrich skill can drive the dispatched
|
|
305
|
+
// specialist to read each chunk and emit claims in-turn. Read-only.
|
|
306
306
|
// ---------------------------------------------------------------------------
|
|
307
|
-
server.tool("conversation-archive-
|
|
307
|
+
server.tool("conversation-archive-list-chunks", "Page through the :Section chunks of one named :ConversationArchive and return each chunk's elementId, ordered index, summary, and full body. The conversation-archive-enrich skill calls this per page, then prompts the dispatched specialist to read each chunk's body in-turn and emit claims; the skill hands those claims back to `conversation-archive-derive-insights` for per-kind cypher emission. Read-only.", {
|
|
308
308
|
archiveElementId: z
|
|
309
309
|
.string()
|
|
310
310
|
.describe("elementId of the :ConversationArchive parent (Task 397 — keyed on conversationIdentity) the operator named (required)."),
|
|
@@ -313,23 +313,100 @@ server.tool("conversation-archive-derive-insights", "Walk the :Section chunks of
|
|
|
313
313
|
.int()
|
|
314
314
|
.nonnegative()
|
|
315
315
|
.optional()
|
|
316
|
-
.describe("Zero-based chunk offset. Default 0.
|
|
316
|
+
.describe("Zero-based chunk offset. Default 0. Advance by chunkLimit per page."),
|
|
317
317
|
chunkLimit: z
|
|
318
318
|
.number()
|
|
319
319
|
.int()
|
|
320
320
|
.positive()
|
|
321
321
|
.max(20)
|
|
322
322
|
.optional()
|
|
323
|
-
.describe("Chunks per page (1..20). Default 5
|
|
323
|
+
.describe("Chunks per page (1..20). Default 5."),
|
|
324
324
|
}, async ({ archiveElementId, chunkOffset, chunkLimit }) => {
|
|
325
325
|
if (!accountId)
|
|
326
|
-
return refuseNoAccount("conversation-archive-
|
|
326
|
+
return refuseNoAccount("conversation-archive-list-chunks");
|
|
327
327
|
try {
|
|
328
|
-
const result = await
|
|
328
|
+
const result = await conversationArchiveListChunks({
|
|
329
329
|
accountId,
|
|
330
330
|
archiveElementId,
|
|
331
331
|
chunkOffset,
|
|
332
332
|
chunkLimit,
|
|
333
|
+
});
|
|
334
|
+
const header = `archiveElementId=${result.archiveElementId} title=${JSON.stringify(result.archiveTitle ?? "")} totalChunks=${result.totalChunks} walked=${result.walkedFrom}..${result.walkedTo} chunks=${result.chunks.length} chunksRemaining=${result.chunksRemaining}`;
|
|
335
|
+
const chunkBlocks = result.chunks
|
|
336
|
+
.map((c) => [
|
|
337
|
+
`Chunk ${c.index + 1}/${result.totalChunks}:`,
|
|
338
|
+
` elementId=${c.elementId}`,
|
|
339
|
+
` summary=${JSON.stringify(c.summary)}`,
|
|
340
|
+
` body=${JSON.stringify(c.body)}`,
|
|
341
|
+
].join("\n"))
|
|
342
|
+
.join("\n\n");
|
|
343
|
+
return {
|
|
344
|
+
content: [
|
|
345
|
+
{
|
|
346
|
+
type: "text",
|
|
347
|
+
text: result.chunks.length === 0
|
|
348
|
+
? `${header}\n(no chunks at this offset)`
|
|
349
|
+
: `${header}\n\n${chunkBlocks}`,
|
|
350
|
+
},
|
|
351
|
+
],
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
catch (err) {
|
|
355
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
356
|
+
return {
|
|
357
|
+
content: [{ type: "text", text: msg }],
|
|
358
|
+
isError: true,
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
process.stderr.write("[memory-mcp] registered tool=conversation-archive-list-chunks\n");
|
|
363
|
+
// ---------------------------------------------------------------------------
|
|
364
|
+
// conversation-archive Phase 2: per-chunk claim → cypher proposal emitter.
|
|
365
|
+
// Task 433: accepts the agent-produced `claims` array for one chunk (read
|
|
366
|
+
// from `conversation-archive-list-chunks`) and returns operator-facing
|
|
367
|
+
// proposals carrying the per-kind mergeCypher. Rejection sidecar filter is
|
|
368
|
+
// applied here. The tool itself NEVER writes; idempotency lives on the
|
|
369
|
+
// MERGE-key (chunkElementId, kind, contentHash) baked into each mergeCypher.
|
|
370
|
+
// ---------------------------------------------------------------------------
|
|
371
|
+
const claimSchema = z.object({
|
|
372
|
+
kind: z.enum([...PROPOSAL_KINDS]).describe("One of: mention, task, preference, observed-relationship."),
|
|
373
|
+
evidenceSnippet: z
|
|
374
|
+
.string()
|
|
375
|
+
.describe("Near-verbatim quote from the chunk body (≤80 chars enforced server-side)."),
|
|
376
|
+
subject: z
|
|
377
|
+
.string()
|
|
378
|
+
.nullable()
|
|
379
|
+
.optional()
|
|
380
|
+
.describe("Display name of the primary entity. Required for mention + observed-relationship."),
|
|
381
|
+
object: z
|
|
382
|
+
.string()
|
|
383
|
+
.nullable()
|
|
384
|
+
.optional()
|
|
385
|
+
.describe("Display name of the related entity. Required for observed-relationship only."),
|
|
386
|
+
payload: z
|
|
387
|
+
.record(z.string(), z.unknown())
|
|
388
|
+
.optional()
|
|
389
|
+
.describe("Kind-specific structured fields (taskTitle/taskDueHint for task; preferenceCategory/preferenceKey/preferenceValue for preference; relationshipType for observed-relationship)."),
|
|
390
|
+
});
|
|
391
|
+
server.tool("conversation-archive-derive-insights", "Convert one chunk's agent-produced claims into operator-facing claim proposals (mention, task, preference, observed-relationship). The conversation-archive-enrich skill reads chunk bodies via `conversation-archive-list-chunks`, drives the dispatched specialist to emit claims in-turn, and passes them here for per-kind cypher emission. Applies the durable rejection sidecar so already-triaged claims do not re-surface. Read-only: the per-row operator gate runs in the skill, this tool never writes.", {
|
|
392
|
+
archiveElementId: z
|
|
393
|
+
.string()
|
|
394
|
+
.describe("elementId of the :ConversationArchive parent (Task 397 — keyed on conversationIdentity) the operator named (required)."),
|
|
395
|
+
chunkElementId: z
|
|
396
|
+
.string()
|
|
397
|
+
.describe("elementId of the single :Section chunk the agent just read (required)."),
|
|
398
|
+
claims: z
|
|
399
|
+
.array(claimSchema)
|
|
400
|
+
.describe("Claims the dispatched specialist emitted in-turn from the chunk body. Zero is valid — pass [] to record an empty-chunk pass."),
|
|
401
|
+
}, async ({ archiveElementId, chunkElementId, claims }) => {
|
|
402
|
+
if (!accountId)
|
|
403
|
+
return refuseNoAccount("conversation-archive-derive-insights");
|
|
404
|
+
try {
|
|
405
|
+
const result = await conversationArchiveDeriveInsights({
|
|
406
|
+
accountId,
|
|
407
|
+
archiveElementId,
|
|
408
|
+
chunkElementId,
|
|
409
|
+
claims: claims,
|
|
333
410
|
sessionId: resolveSessionId(),
|
|
334
411
|
});
|
|
335
412
|
const proposalLines = result.proposals
|
|
@@ -349,13 +426,13 @@ server.tool("conversation-archive-derive-insights", "Walk the :Section chunks of
|
|
|
349
426
|
].join("\n");
|
|
350
427
|
})
|
|
351
428
|
.join("\n\n");
|
|
352
|
-
const header = `archiveElementId=${result.archiveElementId}
|
|
429
|
+
const header = `archiveElementId=${result.archiveElementId} chunkElementId=${result.chunkElementId} proposals=${result.proposals.length} rejectionsFiltered=${result.rejectionsFiltered} invalidClaims=${result.invalidClaims}`;
|
|
353
430
|
return {
|
|
354
431
|
content: [
|
|
355
432
|
{
|
|
356
433
|
type: "text",
|
|
357
434
|
text: result.proposals.length === 0
|
|
358
|
-
? `${header}\n(no
|
|
435
|
+
? `${header}\n(no proposals from this chunk's claims)`
|
|
359
436
|
: `${header}\n\n${proposalLines}`,
|
|
360
437
|
},
|
|
361
438
|
],
|
|
@@ -888,20 +965,29 @@ if (!readOnly) {
|
|
|
888
965
|
};
|
|
889
966
|
}
|
|
890
967
|
});
|
|
891
|
-
server.tool("memory-ingest", "Write a classified document into the graph as typed nodes anchored to
|
|
892
|
-
"
|
|
968
|
+
server.tool("memory-ingest", "Write a classified document or conversation-archive session into the graph as typed nodes anchored to a subject. " +
|
|
969
|
+
"Two mutually-exclusive parent paths — exactly one MUST be set: " +
|
|
970
|
+
"(1) **Document path** — set `attachmentId` (requires a prior memory-ingest-extract call for the text cache). MERGEs under `:KnowledgeDocument` keyed on attachmentId. The typed-section JSON in `sections` is produced by the dispatched specialist in-turn from the loaded ontology. " +
|
|
971
|
+
"(2) **Conversation-archive path** — set `conversationIdentity` along with the conversation-document fields (`source`, `archiveSha256`, `archiveSourceFile`, `participantElementIds`, `lastIngestedMessageHash`, `lastIngestedMessageAt`, optionally `archiveTitle` and `cleanupPriorChunks`). MERGEs under `:ConversationArchive` keyed on conversationIdentity. The specialist invokes this once per session prepared by `conversation-archive-ingest.sh`, advancing the cursor atomically with each session's chunks. " +
|
|
893
972
|
"Each section becomes either a typed graph node (Position, Service, Credential, etc.) anchored to the named " +
|
|
894
|
-
"subject via the natural ontology edge plus a (
|
|
895
|
-
"UNMAPPED sections — a generic :Section node hanging off the
|
|
896
|
-
"
|
|
897
|
-
"
|
|
973
|
+
"subject via the natural ontology edge plus a (parent)-[:REFERENCES]->(typed) link, or — for " +
|
|
974
|
+
"UNMAPPED sections — a generic :Section node hanging off the parent via HAS_SECTION (legacy fallback). " +
|
|
975
|
+
"Document re-ingest replaces the document's typed and untyped children (same attachmentId). Conversation-archive re-ingest is delta-append: prior chunks are never touched unless `cleanupPriorChunks=true` is set (only legitimate on the FIRST session of a `--rebuild` run). " +
|
|
976
|
+
"Shared related entities (Organizations, Persons referenced by typed nodes) " +
|
|
898
977
|
"are MERGEd by identifying property and never deleted on re-ingest.", {
|
|
899
|
-
attachmentId: z.string().describe("UUID of the file attachment — must match a prior memory-ingest-extract call"),
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
978
|
+
attachmentId: z.string().optional().describe("UUID of the file attachment — must match a prior memory-ingest-extract call. Document path only — mutually exclusive with conversationIdentity."),
|
|
979
|
+
conversationIdentity: z.string().optional().describe("sha256(accountId + ':' + sortedParticipantElementIds.join(',')) — produced by `conversation-archive-ingest.sh`. Conversation-archive path only — mutually exclusive with attachmentId."),
|
|
980
|
+
source: z.enum(CONVERSATION_SOURCES).optional().describe("Conversation source enum. Required on the conversation-archive path."),
|
|
981
|
+
archiveSha256: z.string().optional().describe("sha256 of the export bytes. Required on the conversation-archive path; stamped on every :Section chunk so cleanup-by-archiveSha256 is exact."),
|
|
982
|
+
archiveSourceFile: z.string().optional().describe("Basename of the source file (e.g. `_chat.txt`). Required on the conversation-archive path."),
|
|
983
|
+
participantElementIds: z.array(z.string()).optional().describe("Owner + operator-confirmed participants (elementIds). Required on the conversation-archive path. Owner is included first; :PARTICIPANT_IN edges land for every entry. Order is irrelevant for conversationIdentity, which is derived from a sorted set."),
|
|
984
|
+
lastIngestedMessageHash: z.string().optional().describe("sha256 of the last message in THIS session. Required on the conversation-archive path; written to :ConversationArchive.lastIngestedMessageHash atomically with this session's chunks for delta-append on the next ingest."),
|
|
985
|
+
lastIngestedMessageAt: z.string().optional().describe("ISO timestamp of the last message in THIS session. Required on the conversation-archive path; written to :ConversationArchive.lastIngestedMessageAt."),
|
|
986
|
+
archiveTitle: z.string().optional().describe("Stable archive title (`<source> · <owner> ↔ <others> · <YYYY-MM-DD>→<YYYY-MM-DD>`) computed by `conversation-archive-ingest.sh`. Written ON CREATE and COALESCE-on-MATCH so the UI label is stable across re-ingests. Conversation-archive path only."),
|
|
987
|
+
cleanupPriorChunks: z.boolean().optional().describe("When true, the writer deletes prior chunks for the matching archiveSha256 before MERGE. Set this ONLY on the FIRST session of a `--rebuild` run; subsequent sessions in the same run MUST set it false or the writer drops chunks the earlier sessions just wrote. Defaults to false."),
|
|
988
|
+
documentSummary: z.string().describe("Classifier-produced summary (1-3 sentences) for the document path. On the conversation-archive path, pass the stable `archiveTitle` so the parent's summary matches the title."),
|
|
989
|
+
anchorNodeId: z.string().describe("Element ID of the anchor node. Document path: the operator-confirmed document subject (UserProfile/LocalBusiness/Person/Organization). Conversation-archive path: the owner :AdminUser elementId (or :Person for a third-party archive)."),
|
|
990
|
+
anchorLabel: z.string().describe("Primary label of the anchor node (e.g. 'UserProfile', 'LocalBusiness', 'AdminUser'). Used in the per-section MATCH for anchor-edge creation."),
|
|
905
991
|
sections: z.array(z.object({
|
|
906
992
|
kind: z.string().describe("Section kind from the closed enumeration (Position/Education/Chapter/Parties/.../Other), or a standalone-node kind like 'Project'."),
|
|
907
993
|
title: z.string(),
|
|
@@ -945,13 +1031,22 @@ if (!readOnly) {
|
|
|
945
1031
|
documentKeywords: z.array(z.string()).optional().describe("Classifier-extracted topic keywords."),
|
|
946
1032
|
userKeywords: z.array(z.string()).optional().describe("Keywords explicitly provided by the user. Merged additively with documentKeywords."),
|
|
947
1033
|
sessionId: z.string().optional().describe("Session UUID for provenance stamping. Optional override; the server falls back to the SESSION_ID env var set by the admin agent at MCP spawn. The agent only needs to pass this when calling outside the normal admin-session context."),
|
|
948
|
-
}, async ({ attachmentId, documentSummary, anchorNodeId, anchorLabel, sections, documentEdges, orphanCandidates, scope, sourceUrl, sourceType, documentKeywords, userKeywords, sessionId: sessionIdOverride }) => {
|
|
1034
|
+
}, async ({ attachmentId, conversationIdentity, source, archiveSha256, archiveSourceFile, participantElementIds, lastIngestedMessageHash, lastIngestedMessageAt, archiveTitle, cleanupPriorChunks, documentSummary, anchorNodeId, anchorLabel, sections, documentEdges, orphanCandidates, scope, sourceUrl, sourceType, documentKeywords, userKeywords, sessionId: sessionIdOverride }) => {
|
|
949
1035
|
if (!accountId)
|
|
950
1036
|
return refuseNoAccount("memory-ingest");
|
|
951
1037
|
try {
|
|
952
1038
|
const result = await memoryIngest({
|
|
953
1039
|
accountId,
|
|
954
1040
|
attachmentId,
|
|
1041
|
+
conversationIdentity,
|
|
1042
|
+
source,
|
|
1043
|
+
archiveSha256,
|
|
1044
|
+
archiveSourceFile,
|
|
1045
|
+
participantElementIds,
|
|
1046
|
+
lastIngestedMessageHash,
|
|
1047
|
+
lastIngestedMessageAt,
|
|
1048
|
+
archiveTitle,
|
|
1049
|
+
cleanupPriorChunks,
|
|
955
1050
|
documentSummary,
|
|
956
1051
|
anchorNodeId,
|
|
957
1052
|
anchorLabel,
|
|
@@ -978,6 +1073,7 @@ if (!readOnly) {
|
|
|
978
1073
|
orphanCandidates: result.orphanCandidates,
|
|
979
1074
|
documentSummary: result.documentSummary,
|
|
980
1075
|
keywords: result.keywords,
|
|
1076
|
+
cleanedPriorChunks: result.cleanedPriorChunks,
|
|
981
1077
|
}),
|
|
982
1078
|
}],
|
|
983
1079
|
};
|
|
@@ -992,6 +1088,25 @@ if (!readOnly) {
|
|
|
992
1088
|
};
|
|
993
1089
|
}
|
|
994
1090
|
});
|
|
1091
|
+
eagerTool(server, "kd-classify", "Operator-solicited reclassify for any :KnowledgeDocument that carries a `body` property (today: email-thread KDs; future: any thread-shaped source). Reads the KD by attachmentId, decides via a body-growth gate (proceed iff body grew ≥25% since the last classify, or first-ever classify), stages the body to a temp file under $ACCOUNT_DIR/tmp/, and returns a dispatch envelope the admin agent hands to the `librarian` specialist. Librarian runs the document-ingest skill against the temp file; memory-ingest MERGEs by attachmentId, replacing any prior :Section children, and stamps lastClassifiedAt + lastClassifiedBodyLength on the KD so the next gate call has a baseline. Bodyless attachment KDs (PDFs etc.) are not supported here — they have no growth signal.", {
|
|
1092
|
+
attachmentId: z.string().describe("attachmentId of the :KnowledgeDocument to classify. For email threads, this is sha256(`email:<accountId>:<rootMessageId>`); use the email-classify tool when you only have the root Message-ID."),
|
|
1093
|
+
}, async ({ attachmentId }) => {
|
|
1094
|
+
if (!accountId)
|
|
1095
|
+
return refuseNoAccount("kd-classify");
|
|
1096
|
+
try {
|
|
1097
|
+
const text = await kdClassify({ accountId, attachmentId });
|
|
1098
|
+
return { content: [{ type: "text", text }] };
|
|
1099
|
+
}
|
|
1100
|
+
catch (err) {
|
|
1101
|
+
return {
|
|
1102
|
+
content: [{
|
|
1103
|
+
type: "text",
|
|
1104
|
+
text: `kd-classify failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
1105
|
+
}],
|
|
1106
|
+
isError: true,
|
|
1107
|
+
};
|
|
1108
|
+
}
|
|
1109
|
+
});
|
|
995
1110
|
eagerTool(server, "memory-archive-write", "Bulk-archive write surface for flat datasets. " +
|
|
996
1111
|
"Writes typed entities + natural edges in 500-row UNWIND batches. The Cypher body is fixed " +
|
|
997
1112
|
"server-side per `archiveType`; the agent supplies parsed rows + the discriminant, never " +
|
|
@@ -1223,6 +1338,30 @@ if (!readOnly) {
|
|
|
1223
1338
|
};
|
|
1224
1339
|
}
|
|
1225
1340
|
});
|
|
1341
|
+
server.tool("memory-node-exists", "Deterministic label-existence check for installer-seeded singleton roots. " +
|
|
1342
|
+
"Returns { exists, elementId, labels } for a single label/accountId lookup, " +
|
|
1343
|
+
"bypassing the memory-search ranking stack so an installer-seeded node with " +
|
|
1344
|
+
"empty content properties (e.g. a freshly seeded :LocalBusiness with no name yet) " +
|
|
1345
|
+
"is still reported as exists=true. Allow-list of labels is fixed: LocalBusiness, " +
|
|
1346
|
+
"AdminUser, UserProfile, Person. Refuses any other label — use memory-search for " +
|
|
1347
|
+
"ranked discovery on general labels. Use this before any branch that claims a " +
|
|
1348
|
+
"singleton root is missing.", {
|
|
1349
|
+
label: z.enum(MEMORY_NODE_EXISTS_LABELS).describe("Installer-seeded singleton label. One of: LocalBusiness, AdminUser, UserProfile, Person."),
|
|
1350
|
+
}, async ({ label }) => {
|
|
1351
|
+
if (!accountId)
|
|
1352
|
+
return refuseNoAccount("memory-node-exists");
|
|
1353
|
+
try {
|
|
1354
|
+
const result = await memoryNodeExists({ accountId, label });
|
|
1355
|
+
const payload = JSON.stringify(result);
|
|
1356
|
+
return { content: [{ type: "text", text: payload }] };
|
|
1357
|
+
}
|
|
1358
|
+
catch (err) {
|
|
1359
|
+
return {
|
|
1360
|
+
content: [{ type: "text", text: `memory-node-exists failed: ${err instanceof Error ? err.message : String(err)}` }],
|
|
1361
|
+
isError: true,
|
|
1362
|
+
};
|
|
1363
|
+
}
|
|
1364
|
+
});
|
|
1226
1365
|
server.tool("memory-delete", "Soft-delete a single graph node by elementId. Adds the :Trashed label, preserves relationships, " +
|
|
1227
1366
|
"and excludes the node from memory-search and any read filtered via notTrashed. For KnowledgeDocument, " +
|
|
1228
1367
|
"cascades to linked Section and Chunk children. For Conversation, cascades to attached :PART_OF Messages " +
|