@rubytech/create-maxy-code 0.1.147 → 0.1.150
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/dist/index.d.ts +34 -11
- package/payload/platform/lib/graph-search/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/graph-search/dist/index.js +42 -13
- package/payload/platform/lib/graph-search/dist/index.js.map +1 -1
- package/payload/platform/lib/graph-search/src/__tests__/fulltext-coverage.test.ts +75 -40
- package/payload/platform/lib/graph-search/src/index.ts +45 -12
- package/payload/platform/lib/graph-style/dist/index.d.ts +5 -5
- package/payload/platform/lib/graph-style/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/graph-style/dist/index.js +11 -9
- package/payload/platform/lib/graph-style/dist/index.js.map +1 -1
- package/payload/platform/lib/graph-style/src/index.ts +11 -9
- package/payload/platform/lib/graph-trash/dist/index.js +6 -6
- package/payload/platform/lib/graph-trash/dist/index.js.map +1 -1
- package/payload/platform/lib/graph-trash/src/index.ts +6 -6
- package/payload/platform/neo4j/edge-annotations.json +4 -4
- package/payload/platform/neo4j/schema.cypher +143 -58
- package/payload/platform/plugins/admin/PLUGIN.md +1 -2
- package/payload/platform/plugins/admin/hooks/__tests__/pre-turn-graph-pass.test.sh +544 -0
- package/payload/platform/plugins/admin/hooks/__tests__/session-end-retrospective.test.sh +2 -2
- package/payload/platform/plugins/admin/hooks/archive-ingest-surface-gate.sh +2 -2
- package/payload/platform/plugins/admin/hooks/{post-turn-graph-pass.sh → pre-turn-graph-pass.sh} +83 -53
- package/payload/platform/plugins/admin/hooks/session-end-retrospective.sh +6 -3
- package/payload/platform/plugins/docs/references/linkedin-extension.md +1 -1
- package/payload/platform/plugins/docs/references/platform.md +1 -1
- package/payload/platform/plugins/docs/references/plugins-guide.md +2 -2
- package/payload/platform/plugins/linkedin-import/skills/linkedin-import/SKILL.md +5 -5
- package/payload/platform/plugins/memory/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/memory/PLUGIN.md +4 -4
- package/payload/platform/plugins/memory/bin/conversation-archive-ingest.mjs +5 -5
- package/payload/platform/plugins/memory/bin/conversation-archive-ingest.sh +1 -1
- package/payload/platform/plugins/memory/mcp/dist/index.js +35 -33
- 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 +4 -1
- 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__/compiled-truth-rewriter-operator-hint.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/compiled-truth-rewriter-operator-hint.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/compiled-truth-rewriter-operator-hint.test.js +90 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/compiled-truth-rewriter-operator-hint.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-rewriter.d.ts +14 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-rewriter.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-rewriter.js +27 -4
- package/payload/platform/plugins/memory/mcp/dist/lib/compiled-truth-rewriter.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-citation-audit.d.ts +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-citation-audit.js +2 -2
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-orphans.d.ts +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-orphans.js +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.d.ts +2 -2
- 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 +3 -4
- package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-schema.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-schema.js +6 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-schema.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-derive-insights.test.js +2 -2
- 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__/memory-ingest.test.js +17 -17
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-search-fields.test.js +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-search-fields.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-operator-hint.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-operator-hint.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-operator-hint.test.js +329 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-operator-hint.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-write-compiled-truth-rejection.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-write-compiled-truth-rejection.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-write-compiled-truth-rejection.test.js +39 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-write-compiled-truth-rejection.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.d.ts +3 -3
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.js +6 -6
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-delete.d.ts +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-delete.js +9 -3
- 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 +2 -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.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.d.ts +6 -5
- 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 +30 -29
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-restore.d.ts +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-restore.js +4 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-restore.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.js +1 -11
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.d.ts +27 -18
- 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 +83 -70
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js +27 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/vitest.config.ts +2 -0
- package/payload/platform/plugins/memory/references/schema-base.md +7 -6
- package/payload/platform/plugins/memory/skills/conversation-archive/SKILL.md +12 -12
- package/payload/platform/plugins/scheduling/skills/daily-prep/SKILL.md +2 -2
- package/payload/platform/plugins/whatsapp/PLUGIN.md +1 -1
- package/payload/platform/plugins/x-import/PLUGIN.md +3 -3
- package/payload/platform/plugins/x-import/skills/x-import/SKILL.md +7 -7
- package/payload/platform/scripts/seed-neo4j.sh +3 -3
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +0 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
- package/payload/platform/templates/agents/admin/IDENTITY.md +7 -7
- package/payload/platform/templates/specialists/agents/database-operator.md +6 -6
- package/payload/platform/templates/specialists/agents/librarian.md +2 -2
- package/payload/server/public/assets/{admin-_pm0RRTE.js → admin-CQMFJD81.js} +1 -1
- package/payload/server/public/assets/{data-b6q64Ot4.js → data-k-sUX-rP.js} +1 -1
- package/payload/server/public/assets/{graph-D1SVlBLb.js → graph-By7i71WM.js} +1 -1
- package/payload/server/public/assets/{graph-labels-BiDGzVs2.js → graph-labels-DemDBXA3.js} +1 -1
- package/payload/server/public/assets/{page-DwFigpyR.js → page-CHBpFW3X.js} +1 -1
- package/payload/server/public/assets/{page-BV6E4J2q.js → page-jqA-w9gF.js} +1 -1
- package/payload/server/public/data.html +3 -3
- package/payload/server/public/graph.html +3 -3
- package/payload/server/public/index.html +4 -4
- package/payload/server/server.js +29 -20
- package/payload/platform/plugins/admin/skills/commitment-followthrough/SKILL.md +0 -60
|
@@ -0,0 +1,544 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Task 425 — pre-turn-graph-pass UserPromptSubmit hook test suite.
|
|
3
|
+
#
|
|
4
|
+
# Supersedes the Task 419 Stop-hook test suite. A local Python HTTP server
|
|
5
|
+
# stands in for the admin server:
|
|
6
|
+
# POST /api/admin/log-ingest → records every line emit_log sent.
|
|
7
|
+
# GET /api/admin/post-turn-context → returns the contents of $CTX_FILE,
|
|
8
|
+
# or 500 if $CTX_FILE is missing/empty.
|
|
9
|
+
# Both kinds of request are appended to $REQ_LOG so assertions can see
|
|
10
|
+
# every POST body AND whether the GET was attempted at all.
|
|
11
|
+
#
|
|
12
|
+
# Contract under test:
|
|
13
|
+
# - Role/specialist gates: exit 0, single trigger-skipped log line, empty
|
|
14
|
+
# stdout (no additionalContext).
|
|
15
|
+
# - MAXY_UI_INTERNAL_PORT unset: exit 0, empty stdout. The hook emits its
|
|
16
|
+
# missing-env diagnostic on its own stderr channel because
|
|
17
|
+
# /api/admin/log-ingest is unreachable; loud-fail is acceptable.
|
|
18
|
+
# - Missing-transcript: exit 0, single trigger-skipped log line, empty
|
|
19
|
+
# stdout.
|
|
20
|
+
# - Dispatch path (admin + populated JSONL): exit 0, stdout is a JSON
|
|
21
|
+
# envelope `{"hookSpecificOutput":{"hookEventName":"UserPromptSubmit",
|
|
22
|
+
# "additionalContext":"..."}}` whose additionalContext begins with
|
|
23
|
+
# `[system: pre-turn-graph-pass-hook]` and contains `<conversation>` +
|
|
24
|
+
# `<prior-writes>` blocks, with conversationId interpolated from
|
|
25
|
+
# CONVERSATION_NODE_ID.
|
|
26
|
+
# - Prior-writes fetch fails (context endpoint 500): exit 0, dispatch
|
|
27
|
+
# still fires, `<prior-writes>` block resolves to `(none)`.
|
|
28
|
+
# - CONVERSATION_NODE_ID unset: exit 0, additionalContext names
|
|
29
|
+
# `conversationId: <unset>`, and no GET to /api/admin/post-turn-context
|
|
30
|
+
# is attempted.
|
|
31
|
+
# - Multi-block assistant turn: conversation walk concatenates `text`
|
|
32
|
+
# blocks; `tool_use` blocks dropped.
|
|
33
|
+
# - User turn whose only content is `tool_result` blocks: skipped in
|
|
34
|
+
# the conversation walk.
|
|
35
|
+
# - UserPromptSubmit stdin-shape regression: a synthetic UserPromptSubmit
|
|
36
|
+
# payload produces the documented stdout envelope; a stale Stop-shaped
|
|
37
|
+
# payload (no hook_event_name, no prompt) also produces the
|
|
38
|
+
# UserPromptSubmit envelope because the hook keys on
|
|
39
|
+
# session_id + transcript_path, not on input shape sniffing.
|
|
40
|
+
|
|
41
|
+
set -u
|
|
42
|
+
|
|
43
|
+
HOOK="$(cd "$(dirname "$0")/.." && pwd)/pre-turn-graph-pass.sh"
|
|
44
|
+
if [[ ! -x "$HOOK" ]]; then
|
|
45
|
+
echo "FAIL: $HOOK not executable" >&2
|
|
46
|
+
exit 1
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
OP_ID='aaaaaaaa-1111-2222-3333-bbbbbbbbbbbb'
|
|
50
|
+
CONV_ID='conv-deadbeef-0001'
|
|
51
|
+
|
|
52
|
+
TMPFILES=()
|
|
53
|
+
LISTENER_PIDS=()
|
|
54
|
+
cleanup_test_state() {
|
|
55
|
+
for pid in "${LISTENER_PIDS[@]:-}"; do
|
|
56
|
+
if [[ -n "$pid" ]]; then
|
|
57
|
+
kill "$pid" 2>/dev/null || true
|
|
58
|
+
wait "$pid" 2>/dev/null || true
|
|
59
|
+
fi
|
|
60
|
+
done
|
|
61
|
+
for f in "${TMPFILES[@]:-}"; do
|
|
62
|
+
[[ -n "$f" ]] && rm -f "$f" 2>/dev/null || true
|
|
63
|
+
done
|
|
64
|
+
}
|
|
65
|
+
trap cleanup_test_state EXIT
|
|
66
|
+
|
|
67
|
+
PASS=0
|
|
68
|
+
FAIL=0
|
|
69
|
+
pass() { echo "PASS: $1"; PASS=$((PASS + 1)); }
|
|
70
|
+
fail() { echo "FAIL: $1" >&2; FAIL=$((FAIL + 1)); }
|
|
71
|
+
|
|
72
|
+
start_listener() {
|
|
73
|
+
REQ_LOG=$(mktemp); TMPFILES+=("$REQ_LOG")
|
|
74
|
+
CTX_FILE=$(mktemp); TMPFILES+=("$CTX_FILE")
|
|
75
|
+
: > "$CTX_FILE"
|
|
76
|
+
LISTENER_PORT=$((39700 + RANDOM % 100))
|
|
77
|
+
python3 - "$LISTENER_PORT" "$REQ_LOG" "$CTX_FILE" <<'PY' &
|
|
78
|
+
import sys, http.server, json
|
|
79
|
+
port = int(sys.argv[1])
|
|
80
|
+
log_path = sys.argv[2]
|
|
81
|
+
ctx_path = sys.argv[3]
|
|
82
|
+
|
|
83
|
+
class H(http.server.BaseHTTPRequestHandler):
|
|
84
|
+
def log_message(self, *a, **k): pass
|
|
85
|
+
def do_POST(self):
|
|
86
|
+
n = int(self.headers.get('Content-Length','0') or 0)
|
|
87
|
+
body = self.rfile.read(n).decode('utf-8','replace')
|
|
88
|
+
with open(log_path, 'a', encoding='utf-8') as f:
|
|
89
|
+
f.write('POST\t' + self.path + '\t' + body + '\n')
|
|
90
|
+
self.send_response(200)
|
|
91
|
+
self.send_header('Content-Type','application/json')
|
|
92
|
+
self.end_headers()
|
|
93
|
+
self.wfile.write(json.dumps({"ok": True}).encode('utf-8'))
|
|
94
|
+
def do_GET(self):
|
|
95
|
+
with open(log_path, 'a', encoding='utf-8') as f:
|
|
96
|
+
f.write('GET\t' + self.path + '\t\n')
|
|
97
|
+
if self.path.startswith('/api/admin/post-turn-context'):
|
|
98
|
+
try:
|
|
99
|
+
with open(ctx_path, 'r', encoding='utf-8') as f:
|
|
100
|
+
body = f.read()
|
|
101
|
+
except FileNotFoundError:
|
|
102
|
+
body = ''
|
|
103
|
+
if not body.strip():
|
|
104
|
+
self.send_response(500)
|
|
105
|
+
self.end_headers()
|
|
106
|
+
return
|
|
107
|
+
self.send_response(200)
|
|
108
|
+
self.send_header('Content-Type','application/json')
|
|
109
|
+
self.end_headers()
|
|
110
|
+
self.wfile.write(body.encode('utf-8'))
|
|
111
|
+
else:
|
|
112
|
+
self.send_response(404)
|
|
113
|
+
self.end_headers()
|
|
114
|
+
|
|
115
|
+
http.server.HTTPServer(('127.0.0.1', port), H).serve_forever()
|
|
116
|
+
PY
|
|
117
|
+
LISTENER_PIDS+=("$!")
|
|
118
|
+
for _ in $(seq 1 20); do
|
|
119
|
+
if curl -sS --max-time 1 -X POST "http://127.0.0.1:${LISTENER_PORT}/ping" -d '{}' >/dev/null 2>&1; then
|
|
120
|
+
break
|
|
121
|
+
fi
|
|
122
|
+
sleep 0.1
|
|
123
|
+
done
|
|
124
|
+
: > "$REQ_LOG"
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
run_hook() {
|
|
128
|
+
# Args: role, specialist, ui_port, conversation_node_id, stdin_json
|
|
129
|
+
local role="$1"; local specialist="$2"; local ui_port="$3"
|
|
130
|
+
local conv="$4"; local stdin_json="$5"
|
|
131
|
+
local stderr_file; stderr_file=$(mktemp); TMPFILES+=("$stderr_file")
|
|
132
|
+
local stdout_file; stdout_file=$(mktemp); TMPFILES+=("$stdout_file")
|
|
133
|
+
local env_cmd=(env)
|
|
134
|
+
env_cmd+=("MAXY_SESSION_ROLE=${role}")
|
|
135
|
+
env_cmd+=("MAXY_SPECIALIST=${specialist}")
|
|
136
|
+
if [[ -n "$ui_port" ]]; then
|
|
137
|
+
env_cmd+=("MAXY_UI_INTERNAL_PORT=${ui_port}")
|
|
138
|
+
fi
|
|
139
|
+
if [[ -n "$conv" ]]; then
|
|
140
|
+
env_cmd+=("CONVERSATION_NODE_ID=${conv}")
|
|
141
|
+
fi
|
|
142
|
+
env_cmd+=("ACCOUNT_ID=acct-test-0001")
|
|
143
|
+
printf '%s' "$stdin_json" | "${env_cmd[@]}" bash "$HOOK" >"$stdout_file" 2>"$stderr_file"
|
|
144
|
+
HOOK_RC=$?
|
|
145
|
+
HOOK_STDERR=$(cat "$stderr_file")
|
|
146
|
+
HOOK_STDOUT=$(cat "$stdout_file")
|
|
147
|
+
sleep 0.1
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
ingest_lines() {
|
|
151
|
+
grep -E '^POST\s+/api/admin/log-ingest\s+' "$REQ_LOG" 2>/dev/null | python3 -c '
|
|
152
|
+
import sys, json
|
|
153
|
+
for raw in sys.stdin:
|
|
154
|
+
try:
|
|
155
|
+
parts = raw.rstrip("\n").split("\t", 2)
|
|
156
|
+
if len(parts) < 3: continue
|
|
157
|
+
body = parts[2]
|
|
158
|
+
d = json.loads(body)
|
|
159
|
+
if isinstance(d, dict) and isinstance(d.get("line"), str):
|
|
160
|
+
print(d["line"])
|
|
161
|
+
except Exception:
|
|
162
|
+
pass
|
|
163
|
+
' || true
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
context_get_count() {
|
|
167
|
+
grep -E '^GET\s+/api/admin/post-turn-context' "$REQ_LOG" 2>/dev/null | wc -l | tr -d ' '
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
additional_context() {
|
|
171
|
+
printf '%s' "$HOOK_STDOUT" | python3 -c '
|
|
172
|
+
import sys, json
|
|
173
|
+
try:
|
|
174
|
+
d = json.load(sys.stdin)
|
|
175
|
+
out = (d.get("hookSpecificOutput") or {}).get("additionalContext", "")
|
|
176
|
+
sys.stdout.write(out)
|
|
177
|
+
except Exception:
|
|
178
|
+
pass
|
|
179
|
+
'
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
envelope_for() {
|
|
183
|
+
python3 -c '
|
|
184
|
+
import json, sys
|
|
185
|
+
print(json.dumps({
|
|
186
|
+
"session_id": sys.argv[1],
|
|
187
|
+
"transcript_path": sys.argv[2],
|
|
188
|
+
"cwd": "/tmp",
|
|
189
|
+
"hook_event_name": "UserPromptSubmit",
|
|
190
|
+
"prompt": "test prompt body",
|
|
191
|
+
}))
|
|
192
|
+
' "$1" "$2"
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
write_simple_transcript() {
|
|
196
|
+
local out="$1"; shift
|
|
197
|
+
python3 - "$out" "$@" <<'PY'
|
|
198
|
+
import sys, json
|
|
199
|
+
out = sys.argv[1]
|
|
200
|
+
records = []
|
|
201
|
+
i = 2
|
|
202
|
+
while i + 1 < len(sys.argv):
|
|
203
|
+
u, a = sys.argv[i], sys.argv[i+1]
|
|
204
|
+
records.append({
|
|
205
|
+
"type": "user",
|
|
206
|
+
"timestamp": "2026-05-26T10:00:00.000Z",
|
|
207
|
+
"message": {"role": "user", "content": u},
|
|
208
|
+
})
|
|
209
|
+
records.append({
|
|
210
|
+
"type": "assistant",
|
|
211
|
+
"timestamp": "2026-05-26T10:00:01.000Z",
|
|
212
|
+
"message": {
|
|
213
|
+
"id": "msg_a",
|
|
214
|
+
"role": "assistant",
|
|
215
|
+
"content": [{"type": "text", "text": a}],
|
|
216
|
+
},
|
|
217
|
+
})
|
|
218
|
+
i += 2
|
|
219
|
+
with open(out, "w", encoding="utf-8") as f:
|
|
220
|
+
for r in records:
|
|
221
|
+
f.write(json.dumps(r) + "\n")
|
|
222
|
+
PY
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
DIRECTIVE_TOKEN='[system: pre-turn-graph-pass-hook]'
|
|
226
|
+
|
|
227
|
+
# ---------------------------------------------------------------------------
|
|
228
|
+
# Case 1: role != admin → trigger-skipped reason=role-not-admin
|
|
229
|
+
# ---------------------------------------------------------------------------
|
|
230
|
+
start_listener
|
|
231
|
+
TRANSCRIPT_BASIC=$(mktemp); TMPFILES+=("$TRANSCRIPT_BASIC")
|
|
232
|
+
write_simple_transcript "$TRANSCRIPT_BASIC" "hello" "hi there"
|
|
233
|
+
ENV_BASIC=$(envelope_for "$OP_ID" "$TRANSCRIPT_BASIC")
|
|
234
|
+
: > "$REQ_LOG"
|
|
235
|
+
run_hook "public" "" "$LISTENER_PORT" "$CONV_ID" "$ENV_BASIC"
|
|
236
|
+
[[ "$HOOK_RC" -eq 0 ]] || fail "case-1 rc=$HOOK_RC"
|
|
237
|
+
if [[ -n "$HOOK_STDOUT" ]]; then
|
|
238
|
+
fail "case-1 stdout must be empty on role-not-admin, got: $HOOK_STDOUT"
|
|
239
|
+
fi
|
|
240
|
+
if ingest_lines | grep -qE "^trigger-skipped sessionId=${OP_ID} reason=role-not-admin$"; then
|
|
241
|
+
pass "case-1 role=public → trigger-skipped reason=role-not-admin"
|
|
242
|
+
else
|
|
243
|
+
fail "case-1 expected role-not-admin, got: $(ingest_lines)"
|
|
244
|
+
fi
|
|
245
|
+
|
|
246
|
+
# ---------------------------------------------------------------------------
|
|
247
|
+
# Case 2: MAXY_SPECIALIST set → trigger-skipped reason=is-specialist
|
|
248
|
+
# ---------------------------------------------------------------------------
|
|
249
|
+
: > "$REQ_LOG"
|
|
250
|
+
run_hook "admin" "database-operator" "$LISTENER_PORT" "$CONV_ID" "$ENV_BASIC"
|
|
251
|
+
[[ "$HOOK_RC" -eq 0 ]] || fail "case-2 rc=$HOOK_RC"
|
|
252
|
+
if [[ -n "$HOOK_STDOUT" ]]; then
|
|
253
|
+
fail "case-2 stdout must be empty on is-specialist, got: $HOOK_STDOUT"
|
|
254
|
+
fi
|
|
255
|
+
if ingest_lines | grep -qE "^trigger-skipped sessionId=${OP_ID} reason=is-specialist specialist=database-operator$"; then
|
|
256
|
+
pass "case-2 specialist=database-operator → trigger-skipped reason=is-specialist"
|
|
257
|
+
else
|
|
258
|
+
fail "case-2 expected is-specialist, got: $(ingest_lines)"
|
|
259
|
+
fi
|
|
260
|
+
|
|
261
|
+
# ---------------------------------------------------------------------------
|
|
262
|
+
# Case 3: MAXY_UI_INTERNAL_PORT unset → exit 0, empty stdout, loud stderr.
|
|
263
|
+
# ---------------------------------------------------------------------------
|
|
264
|
+
: > "$REQ_LOG"
|
|
265
|
+
run_hook "admin" "" "" "$CONV_ID" "$ENV_BASIC"
|
|
266
|
+
[[ "$HOOK_RC" -eq 0 ]] || fail "case-3 rc=$HOOK_RC"
|
|
267
|
+
if [[ -n "$HOOK_STDOUT" ]]; then
|
|
268
|
+
fail "case-3 stdout must be empty when MAXY_UI_INTERNAL_PORT unset"
|
|
269
|
+
fi
|
|
270
|
+
if [[ "$(ingest_lines | wc -l | tr -d ' ')" == "0" ]]; then
|
|
271
|
+
pass "case-3 missing MAXY_UI_INTERNAL_PORT → exit 0, no dispatch"
|
|
272
|
+
else
|
|
273
|
+
fail "case-3 expected no ingest lines (port unset), got: $(ingest_lines)"
|
|
274
|
+
fi
|
|
275
|
+
|
|
276
|
+
# ---------------------------------------------------------------------------
|
|
277
|
+
# Case 4: transcript_path missing on disk → trigger-skipped reason=missing-transcript
|
|
278
|
+
# ---------------------------------------------------------------------------
|
|
279
|
+
MISSING_PATH="/tmp/pre-turn-graph-pass-does-not-exist-$$.jsonl"
|
|
280
|
+
rm -f "$MISSING_PATH"
|
|
281
|
+
ENV_MISSING=$(envelope_for "$OP_ID" "$MISSING_PATH")
|
|
282
|
+
: > "$REQ_LOG"
|
|
283
|
+
run_hook "admin" "" "$LISTENER_PORT" "$CONV_ID" "$ENV_MISSING"
|
|
284
|
+
[[ "$HOOK_RC" -eq 0 ]] || fail "case-4 rc=$HOOK_RC"
|
|
285
|
+
if [[ -n "$HOOK_STDOUT" ]]; then
|
|
286
|
+
fail "case-4 stdout must be empty on missing-transcript"
|
|
287
|
+
fi
|
|
288
|
+
if ingest_lines | grep -qE "^trigger-skipped sessionId=${OP_ID} reason=missing-transcript$"; then
|
|
289
|
+
pass "case-4 missing transcript_path → trigger-skipped reason=missing-transcript"
|
|
290
|
+
else
|
|
291
|
+
fail "case-4 expected missing-transcript, got: $(ingest_lines)"
|
|
292
|
+
fi
|
|
293
|
+
|
|
294
|
+
# ---------------------------------------------------------------------------
|
|
295
|
+
# Case 5: Dispatch path → exit 0, UserPromptSubmit JSON envelope, blocks
|
|
296
|
+
# interpolated, dispatch log emitted.
|
|
297
|
+
# ---------------------------------------------------------------------------
|
|
298
|
+
cat > "$CTX_FILE" <<'JSON'
|
|
299
|
+
{"writes":[{"elementId":"4:abc:1","labels":["KnowledgeDocument"],"properties":{"title":"prior note"}}]}
|
|
300
|
+
JSON
|
|
301
|
+
: > "$REQ_LOG"
|
|
302
|
+
run_hook "admin" "" "$LISTENER_PORT" "$CONV_ID" "$ENV_BASIC"
|
|
303
|
+
[[ "$HOOK_RC" -eq 0 ]] || fail "case-5 expected rc=0, got $HOOK_RC"
|
|
304
|
+
STDOUT_OK=$(printf '%s' "$HOOK_STDOUT" | python3 -c '
|
|
305
|
+
import sys, json
|
|
306
|
+
try:
|
|
307
|
+
d = json.load(sys.stdin)
|
|
308
|
+
out = d["hookSpecificOutput"]
|
|
309
|
+
assert out["hookEventName"] == "UserPromptSubmit"
|
|
310
|
+
assert isinstance(out["additionalContext"], str)
|
|
311
|
+
print("OK")
|
|
312
|
+
except Exception as e:
|
|
313
|
+
print(f"BAD: {e}")
|
|
314
|
+
')
|
|
315
|
+
if [[ "$STDOUT_OK" != "OK" ]]; then
|
|
316
|
+
fail "case-5 stdout envelope malformed: $STDOUT_OK"
|
|
317
|
+
fi
|
|
318
|
+
CTX_BODY=$(additional_context)
|
|
319
|
+
if ! printf '%s' "$CTX_BODY" | head -n 1 | grep -qF "$DIRECTIVE_TOKEN"; then
|
|
320
|
+
fail "case-5 additionalContext must begin with $DIRECTIVE_TOKEN"
|
|
321
|
+
fi
|
|
322
|
+
if ! printf '%s' "$CTX_BODY" | grep -q '<conversation>'; then
|
|
323
|
+
fail "case-5 additionalContext missing <conversation> block"
|
|
324
|
+
fi
|
|
325
|
+
if ! printf '%s' "$CTX_BODY" | grep -q '<prior-writes>'; then
|
|
326
|
+
fail "case-5 additionalContext missing <prior-writes> block"
|
|
327
|
+
fi
|
|
328
|
+
if ! printf '%s' "$CTX_BODY" | grep -q "conversationId: ${CONV_ID}"; then
|
|
329
|
+
fail "case-5 additionalContext must interpolate conversationId=${CONV_ID}"
|
|
330
|
+
fi
|
|
331
|
+
if ! printf '%s' "$CTX_BODY" | grep -q '\[user: hello\]'; then
|
|
332
|
+
fail "case-5 additionalContext must carry user turn '[user: hello]'"
|
|
333
|
+
fi
|
|
334
|
+
if ! printf '%s' "$CTX_BODY" | grep -q '\[assistant: hi there\]'; then
|
|
335
|
+
fail "case-5 additionalContext must carry assistant turn '[assistant: hi there]'"
|
|
336
|
+
fi
|
|
337
|
+
if ! printf '%s' "$CTX_BODY" | grep -q 'KnowledgeDocument'; then
|
|
338
|
+
fail "case-5 additionalContext <prior-writes> must include fetched labels"
|
|
339
|
+
fi
|
|
340
|
+
if ingest_lines | grep -qE "^dispatch sessionId=${OP_ID} conversationId=${CONV_ID} "; then
|
|
341
|
+
pass "case-5 dispatch path → exit 0 + JSON envelope with conversation, prior-writes, conversationId"
|
|
342
|
+
else
|
|
343
|
+
fail "case-5 expected dispatch log line, got: $(ingest_lines)"
|
|
344
|
+
fi
|
|
345
|
+
|
|
346
|
+
# ---------------------------------------------------------------------------
|
|
347
|
+
# Case 6: Prior-writes fetch fails (500) → exit 0, prior-writes=(none).
|
|
348
|
+
# ---------------------------------------------------------------------------
|
|
349
|
+
: > "$CTX_FILE"
|
|
350
|
+
: > "$REQ_LOG"
|
|
351
|
+
run_hook "admin" "" "$LISTENER_PORT" "$CONV_ID" "$ENV_BASIC"
|
|
352
|
+
[[ "$HOOK_RC" -eq 0 ]] || fail "case-6 expected rc=0, got $HOOK_RC"
|
|
353
|
+
CTX_BODY=$(additional_context)
|
|
354
|
+
PRIOR_BODY=$(printf '%s' "$CTX_BODY" | awk '/<prior-writes>/{flag=1;next}/<\/prior-writes>/{flag=0}flag' | tr -d '[:space:]')
|
|
355
|
+
if [[ "$PRIOR_BODY" == "(none)" ]]; then
|
|
356
|
+
pass "case-6 context endpoint 500 → dispatch still fires, prior-writes = (none)"
|
|
357
|
+
else
|
|
358
|
+
fail "case-6 expected prior-writes=(none), got [$PRIOR_BODY]"
|
|
359
|
+
fi
|
|
360
|
+
|
|
361
|
+
# ---------------------------------------------------------------------------
|
|
362
|
+
# Case 7: CONVERSATION_NODE_ID unset → conversationId:<unset>, no context GET.
|
|
363
|
+
# ---------------------------------------------------------------------------
|
|
364
|
+
cat > "$CTX_FILE" <<'JSON'
|
|
365
|
+
{"writes":[]}
|
|
366
|
+
JSON
|
|
367
|
+
: > "$REQ_LOG"
|
|
368
|
+
run_hook "admin" "" "$LISTENER_PORT" "" "$ENV_BASIC"
|
|
369
|
+
[[ "$HOOK_RC" -eq 0 ]] || fail "case-7 expected rc=0, got $HOOK_RC"
|
|
370
|
+
CTX_BODY=$(additional_context)
|
|
371
|
+
if ! printf '%s' "$CTX_BODY" | grep -q 'conversationId: <unset>'; then
|
|
372
|
+
fail "case-7 additionalContext must contain 'conversationId: <unset>'"
|
|
373
|
+
fi
|
|
374
|
+
GET_COUNT=$(context_get_count)
|
|
375
|
+
if [[ "$GET_COUNT" == "0" ]]; then
|
|
376
|
+
pass "case-7 CONVERSATION_NODE_ID unset → no GET /api/admin/post-turn-context attempted"
|
|
377
|
+
else
|
|
378
|
+
fail "case-7 expected zero context GETs, got: $GET_COUNT"
|
|
379
|
+
fi
|
|
380
|
+
|
|
381
|
+
# ---------------------------------------------------------------------------
|
|
382
|
+
# Case 8: Multi-block assistant turn → text concatenated, tool_use dropped.
|
|
383
|
+
# ---------------------------------------------------------------------------
|
|
384
|
+
T_MULTI=$(mktemp); TMPFILES+=("$T_MULTI")
|
|
385
|
+
python3 - "$T_MULTI" <<'PY'
|
|
386
|
+
import sys, json
|
|
387
|
+
out = sys.argv[1]
|
|
388
|
+
records = [
|
|
389
|
+
{
|
|
390
|
+
"type": "user",
|
|
391
|
+
"timestamp": "2026-05-26T10:00:00.000Z",
|
|
392
|
+
"message": {"role": "user", "content": "go"},
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
"type": "assistant",
|
|
396
|
+
"timestamp": "2026-05-26T10:00:01.000Z",
|
|
397
|
+
"message": {
|
|
398
|
+
"id": "msg_a",
|
|
399
|
+
"role": "assistant",
|
|
400
|
+
"content": [
|
|
401
|
+
{"type": "text", "text": "part-one "},
|
|
402
|
+
{"type": "tool_use", "id": "tu_1", "name": "Read", "input": {}},
|
|
403
|
+
{"type": "text", "text": "part-two"},
|
|
404
|
+
],
|
|
405
|
+
},
|
|
406
|
+
},
|
|
407
|
+
]
|
|
408
|
+
with open(out, "w", encoding="utf-8") as f:
|
|
409
|
+
for r in records:
|
|
410
|
+
f.write(json.dumps(r) + "\n")
|
|
411
|
+
PY
|
|
412
|
+
ENV_MULTI=$(envelope_for "$OP_ID" "$T_MULTI")
|
|
413
|
+
cat > "$CTX_FILE" <<'JSON'
|
|
414
|
+
{"writes":[]}
|
|
415
|
+
JSON
|
|
416
|
+
: > "$REQ_LOG"
|
|
417
|
+
run_hook "admin" "" "$LISTENER_PORT" "$CONV_ID" "$ENV_MULTI"
|
|
418
|
+
[[ "$HOOK_RC" -eq 0 ]] || fail "case-8 expected rc=0, got $HOOK_RC"
|
|
419
|
+
CTX_BODY=$(additional_context)
|
|
420
|
+
if ! printf '%s' "$CTX_BODY" | grep -q '\[assistant: part-one part-two\]'; then
|
|
421
|
+
fail "case-8 expected concatenated assistant text '[assistant: part-one part-two]'"
|
|
422
|
+
fi
|
|
423
|
+
if printf '%s' "$CTX_BODY" | grep -q '"tool_use"'; then
|
|
424
|
+
fail "case-8 tool_use block must not appear in additionalContext"
|
|
425
|
+
fi
|
|
426
|
+
if printf '%s' "$CTX_BODY" | grep -q '\[assistant: part-one part-two\]'; then
|
|
427
|
+
pass "case-8 multi-block assistant turn → text concatenated, tool_use dropped"
|
|
428
|
+
fi
|
|
429
|
+
|
|
430
|
+
# ---------------------------------------------------------------------------
|
|
431
|
+
# Case 9: User turn with only tool_result blocks → skipped in conversation walk.
|
|
432
|
+
# ---------------------------------------------------------------------------
|
|
433
|
+
T_TRONLY=$(mktemp); TMPFILES+=("$T_TRONLY")
|
|
434
|
+
python3 - "$T_TRONLY" <<'PY'
|
|
435
|
+
import sys, json
|
|
436
|
+
out = sys.argv[1]
|
|
437
|
+
records = [
|
|
438
|
+
{
|
|
439
|
+
"type": "user",
|
|
440
|
+
"timestamp": "2026-05-26T10:00:00.000Z",
|
|
441
|
+
"message": {"role": "user", "content": "real user turn"},
|
|
442
|
+
},
|
|
443
|
+
{
|
|
444
|
+
"type": "assistant",
|
|
445
|
+
"timestamp": "2026-05-26T10:00:01.000Z",
|
|
446
|
+
"message": {
|
|
447
|
+
"id": "msg_a",
|
|
448
|
+
"role": "assistant",
|
|
449
|
+
"content": [{"type": "text", "text": "assistant reply"}],
|
|
450
|
+
},
|
|
451
|
+
},
|
|
452
|
+
{
|
|
453
|
+
"type": "user",
|
|
454
|
+
"timestamp": "2026-05-26T10:00:02.000Z",
|
|
455
|
+
"message": {
|
|
456
|
+
"role": "user",
|
|
457
|
+
"content": [
|
|
458
|
+
{"type": "tool_result", "tool_use_id": "tu_x", "content": "tool plumbing payload"},
|
|
459
|
+
],
|
|
460
|
+
},
|
|
461
|
+
},
|
|
462
|
+
]
|
|
463
|
+
with open(out, "w", encoding="utf-8") as f:
|
|
464
|
+
for r in records:
|
|
465
|
+
f.write(json.dumps(r) + "\n")
|
|
466
|
+
PY
|
|
467
|
+
ENV_TRONLY=$(envelope_for "$OP_ID" "$T_TRONLY")
|
|
468
|
+
: > "$REQ_LOG"
|
|
469
|
+
run_hook "admin" "" "$LISTENER_PORT" "$CONV_ID" "$ENV_TRONLY"
|
|
470
|
+
[[ "$HOOK_RC" -eq 0 ]] || fail "case-9 expected rc=0, got $HOOK_RC"
|
|
471
|
+
CTX_BODY=$(additional_context)
|
|
472
|
+
CONV_BLOCK=$(printf '%s' "$CTX_BODY" | awk '/<conversation>/{flag=1;next}/<\/conversation>/{flag=0}flag')
|
|
473
|
+
if ! printf '%s' "$CONV_BLOCK" | grep -q '\[user: real user turn\]'; then
|
|
474
|
+
fail "case-9 conversation block must include real user turn"
|
|
475
|
+
fi
|
|
476
|
+
if printf '%s' "$CONV_BLOCK" | grep -q 'tool plumbing payload'; then
|
|
477
|
+
fail "case-9 tool_result-only user record must not appear in conversation walk"
|
|
478
|
+
fi
|
|
479
|
+
TR_LINES=$(printf '%s\n' "$CONV_BLOCK" | grep -cE '\[user:' || true)
|
|
480
|
+
if [[ "$TR_LINES" == "1" ]]; then
|
|
481
|
+
pass "case-9 tool_result-only user record skipped in conversation walk"
|
|
482
|
+
else
|
|
483
|
+
fail "case-9 expected exactly one [user: ...] line, got $TR_LINES"
|
|
484
|
+
fi
|
|
485
|
+
|
|
486
|
+
# ---------------------------------------------------------------------------
|
|
487
|
+
# Case 10: UserPromptSubmit shape — directive on stdout only, never stderr;
|
|
488
|
+
# stdout envelope has hookEventName="UserPromptSubmit".
|
|
489
|
+
# ---------------------------------------------------------------------------
|
|
490
|
+
cat > "$CTX_FILE" <<'JSON'
|
|
491
|
+
{"writes":[]}
|
|
492
|
+
JSON
|
|
493
|
+
: > "$REQ_LOG"
|
|
494
|
+
run_hook "admin" "" "$LISTENER_PORT" "$CONV_ID" "$ENV_BASIC"
|
|
495
|
+
[[ "$HOOK_RC" -eq 0 ]] || fail "case-10 expected rc=0, got $HOOK_RC"
|
|
496
|
+
if printf '%s' "$HOOK_STDERR" | grep -qF "$DIRECTIVE_TOKEN"; then
|
|
497
|
+
fail "case-10 directive must not appear on stderr (UserPromptSubmit contract is stdout JSON)"
|
|
498
|
+
fi
|
|
499
|
+
HEN=$(printf '%s' "$HOOK_STDOUT" | python3 -c '
|
|
500
|
+
import sys, json
|
|
501
|
+
try:
|
|
502
|
+
d = json.load(sys.stdin)
|
|
503
|
+
sys.stdout.write(d["hookSpecificOutput"]["hookEventName"])
|
|
504
|
+
except Exception:
|
|
505
|
+
pass
|
|
506
|
+
')
|
|
507
|
+
if [[ "$HEN" == "UserPromptSubmit" ]]; then
|
|
508
|
+
pass "case-10 stdout envelope is well-formed JSON with hookEventName=UserPromptSubmit"
|
|
509
|
+
else
|
|
510
|
+
fail "case-10 expected hookEventName=UserPromptSubmit, got [$HEN]"
|
|
511
|
+
fi
|
|
512
|
+
|
|
513
|
+
# ---------------------------------------------------------------------------
|
|
514
|
+
# Case 11: Stop-shaped stdin regression — a CC Stop-hook payload carries
|
|
515
|
+
# hook_event_name="Stop". The hook must not silently emit a
|
|
516
|
+
# UserPromptSubmit envelope on a Stop fire. Expect exit 0 with
|
|
517
|
+
# empty stdout and a `wrong-hook-event` skip line.
|
|
518
|
+
# ---------------------------------------------------------------------------
|
|
519
|
+
ENV_STOP_SHAPED=$(python3 -c '
|
|
520
|
+
import json, sys
|
|
521
|
+
print(json.dumps({
|
|
522
|
+
"session_id": sys.argv[1],
|
|
523
|
+
"transcript_path": sys.argv[2],
|
|
524
|
+
"hook_event_name": "Stop",
|
|
525
|
+
}))
|
|
526
|
+
' "$OP_ID" "$TRANSCRIPT_BASIC")
|
|
527
|
+
: > "$REQ_LOG"
|
|
528
|
+
run_hook "admin" "" "$LISTENER_PORT" "$CONV_ID" "$ENV_STOP_SHAPED"
|
|
529
|
+
[[ "$HOOK_RC" -eq 0 ]] || fail "case-11 expected rc=0, got $HOOK_RC"
|
|
530
|
+
if [[ -n "$HOOK_STDOUT" ]]; then
|
|
531
|
+
fail "case-11 stdout must be empty on wrong-hook-event Stop, got: $HOOK_STDOUT"
|
|
532
|
+
fi
|
|
533
|
+
if ingest_lines | grep -qE "^trigger-skipped sessionId=${OP_ID} reason=wrong-hook-event event=Stop$"; then
|
|
534
|
+
pass "case-11 Stop-shaped stdin rejected with wrong-hook-event skip"
|
|
535
|
+
else
|
|
536
|
+
fail "case-11 expected wrong-hook-event skip, got: $(ingest_lines)"
|
|
537
|
+
fi
|
|
538
|
+
|
|
539
|
+
# ---------------------------------------------------------------------------
|
|
540
|
+
# Summary
|
|
541
|
+
# ---------------------------------------------------------------------------
|
|
542
|
+
echo
|
|
543
|
+
echo "pre-turn-graph-pass tests: $PASS passed, $FAIL failed"
|
|
544
|
+
[[ "$FAIL" -eq 0 ]]
|
|
@@ -294,7 +294,7 @@ write_transcript "$T_RELEASE" \
|
|
|
294
294
|
"assistant|hello|" \
|
|
295
295
|
"user|/end|" \
|
|
296
296
|
"assistant|running retrospective|" \
|
|
297
|
-
"assistant||tool_use:
|
|
297
|
+
"assistant||tool_use:mcp__admin__session-retrospective-mark-complete"
|
|
298
298
|
ENV_REL=$(envelope_for "$OP_ID" "$T_RELEASE")
|
|
299
299
|
: > "$REQ_LOG"
|
|
300
300
|
run_hook "admin" "" "$ENV_REL"
|
|
@@ -325,7 +325,7 @@ fi
|
|
|
325
325
|
T_OTHER=$(mktemp); TMPFILES+=("$T_OTHER")
|
|
326
326
|
write_transcript "$T_OTHER" \
|
|
327
327
|
"user|/end|" \
|
|
328
|
-
"assistant||tool_use:
|
|
328
|
+
"assistant||tool_use:mcp__admin__session-retrospective-mark-complete"
|
|
329
329
|
# Active gate inspects only T_ACTIVE (no sentinel inside).
|
|
330
330
|
T_ACTIVE=$(mktemp); TMPFILES+=("$T_ACTIVE")
|
|
331
331
|
write_transcript "$T_ACTIVE" "user|/end|"
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
# blocking the legacy MCP deviation tools mechanically. The retired
|
|
9
9
|
# `whatsapp-export-insight-pass` tool is replaced by Phase 2:
|
|
10
10
|
# `mcp__memory__conversation-archive-derive-insights` — a read-only tool that
|
|
11
|
-
# walks :Section chunks of one named
|
|
11
|
+
# walks :Section chunks of one named :ConversationArchive in
|
|
12
12
|
# pages and emits per-row proposals. The new tool is NOT in any BLOCK list
|
|
13
13
|
# (the gate is allow-by-default for unrecognised tools) — its writes go
|
|
14
14
|
# through the existing graph-cypher-write surface, gated by the operator per
|
|
@@ -154,7 +154,7 @@ fi
|
|
|
154
154
|
case "$TOOL_NAME" in
|
|
155
155
|
mcp__memory__whatsapp-export-parse|mcp__memory__whatsapp-export-insight-write|mcp__memory__whatsapp-export-insight-pass|mcp__memory__whatsapp-export-preview)
|
|
156
156
|
emit_decision "block" "denied-mcp-legacy" \
|
|
157
|
-
"Blocked: ${TOOL_NAME} is a retired path. generalised conversation-archive ingest — invoke 'bash platform/plugins/memory/bin/conversation-archive-ingest.sh <archive> --source whatsapp --owner-element-id <id> --participant-person-ids <csv> --scope <admin|public>' once. Normalise, sessionize, classify (mode=chat), and memory-ingest (writes :
|
|
157
|
+
"Blocked: ${TOOL_NAME} is a retired path. generalised conversation-archive ingest — invoke 'bash platform/plugins/memory/bin/conversation-archive-ingest.sh <archive> --source whatsapp --owner-element-id <id> --participant-person-ids <csv> --scope <admin|public>' once. Normalise, sessionize, classify (mode=chat), and memory-ingest (writes :ConversationArchive keyed on conversationIdentity, source=<enum>) all run in-process."
|
|
158
158
|
;;
|
|
159
159
|
esac
|
|
160
160
|
|