@rubytech/create-maxy 1.0.881 → 1.0.884
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-mcp/dist/index.js +45 -0
- package/payload/platform/lib/graph-mcp/dist/index.js.map +1 -1
- package/payload/platform/lib/graph-mcp/src/index.ts +47 -0
- package/payload/platform/lib/graph-write/dist/__tests__/action-provenance-gate.test.js +57 -9
- package/payload/platform/lib/graph-write/dist/__tests__/action-provenance-gate.test.js.map +1 -1
- package/payload/platform/lib/graph-write/dist/conversation-provenance.d.ts +26 -0
- package/payload/platform/lib/graph-write/dist/conversation-provenance.d.ts.map +1 -0
- package/payload/platform/lib/graph-write/dist/conversation-provenance.js +81 -0
- package/payload/platform/lib/graph-write/dist/conversation-provenance.js.map +1 -0
- package/payload/platform/lib/graph-write/dist/index.d.ts +38 -16
- package/payload/platform/lib/graph-write/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/graph-write/dist/index.js +75 -35
- package/payload/platform/lib/graph-write/dist/index.js.map +1 -1
- package/payload/platform/lib/graph-write/src/__tests__/action-provenance-gate.test.ts +59 -9
- package/payload/platform/lib/graph-write/src/conversation-provenance.ts +140 -0
- package/payload/platform/lib/graph-write/src/index.ts +76 -35
- package/payload/platform/lib/mcp-eager/dist/index.d.ts +61 -0
- package/payload/platform/lib/mcp-eager/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/mcp-eager/dist/index.js +49 -0
- package/payload/platform/lib/mcp-eager/dist/index.js.map +1 -0
- package/payload/platform/lib/mcp-eager/src/index.ts +78 -0
- package/payload/platform/lib/mcp-eager/tsconfig.json +8 -0
- package/payload/platform/package.json +2 -2
- package/payload/platform/plugins/admin/mcp/dist/__tests__/plugin-read-skill-resolution.test.js +26 -2
- package/payload/platform/plugins/admin/mcp/dist/__tests__/plugin-read-skill-resolution.test.js.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/index.js +36 -33
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/skill-resolution.d.ts +5 -1
- package/payload/platform/plugins/admin/mcp/dist/skill-resolution.d.ts.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/skill-resolution.js +24 -8
- package/payload/platform/plugins/admin/mcp/dist/skill-resolution.js.map +1 -1
- package/payload/platform/plugins/contacts/PLUGIN.md +8 -0
- package/payload/platform/plugins/contacts/mcp/dist/index.js +10 -9
- package/payload/platform/plugins/contacts/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.d.ts.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.js +17 -2
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.js.map +1 -1
- package/payload/platform/plugins/docs/references/internals.md +15 -2
- package/payload/platform/plugins/docs/references/plugins-guide.md +2 -0
- package/payload/platform/plugins/email/mcp/dist/index.js +10 -9
- package/payload/platform/plugins/email/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/memory/PLUGIN.md +5 -3
- package/payload/platform/plugins/memory/mcp/dist/index.js +10 -9
- package/payload/platform/plugins/memory/mcp/dist/index.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 +18 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/index.js +9 -8
- package/payload/platform/plugins/scheduling/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/tasks/mcp/dist/index.js +15 -14
- package/payload/platform/plugins/tasks/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/index.js +4 -3
- package/payload/platform/plugins/telegram/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/workflows/mcp/dist/index.js +9 -8
- package/payload/platform/plugins/workflows/mcp/dist/index.js.map +1 -1
- package/payload/platform/scripts/__tests__/logs-read-prefix.sh +341 -0
- package/payload/platform/scripts/logs-read.sh +108 -41
- package/payload/platform/scripts/logs-read.test.sh +6 -2
- package/payload/platform/templates/agents/admin/IDENTITY.md +1 -1
- package/payload/premium-plugins/real-agency/BUNDLE.md +1 -1
- package/payload/server/chunk-5PQU2HW2.js +11672 -0
- package/payload/server/chunk-ECAQVMRA.js +759 -0
- package/payload/server/chunk-K7S5T4VG.js +11534 -0
- package/payload/server/cloudflare-task-tracker-JNZXLW32.js +22 -0
- package/payload/server/maxy-edge.js +2 -2
- package/payload/server/server.js +38 -6
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Task 998 — bash harness for logs-read.sh prefix-match resolution.
|
|
3
|
+
#
|
|
4
|
+
# Covers the contract from the task brief:
|
|
5
|
+
# 1. 8-char prefix → resolves full-UUID file (the original bug)
|
|
6
|
+
# 2. Multiple matches → reason=ambiguous-prefix, exit 1, never picks
|
|
7
|
+
# 3. More-specific prefix disambiguates
|
|
8
|
+
# 4. Zero matches → reason=file-not-found-in-either-shape with glob patterns
|
|
9
|
+
# 5. Full 36-char UUID still resolves (regression boundary)
|
|
10
|
+
# 6. Preflush prefix match
|
|
11
|
+
# 7. Preflush ambiguity (Pass 1 zero, Pass 2 multiple)
|
|
12
|
+
# 8. Every per-conversation type (agent-stream, session, error, public)
|
|
13
|
+
# resolves under prefix-match
|
|
14
|
+
#
|
|
15
|
+
# Companion to platform/scripts/logs-read.test.sh (Task 671's two-shape
|
|
16
|
+
# resolution harness).
|
|
17
|
+
set -euo pipefail
|
|
18
|
+
|
|
19
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
20
|
+
LOGS_READ="$SCRIPT_DIR/../logs-read.sh"
|
|
21
|
+
if [[ ! -x "$LOGS_READ" ]]; then
|
|
22
|
+
echo "FATAL: $LOGS_READ not executable" >&2
|
|
23
|
+
exit 1
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
PASS=0
|
|
27
|
+
FAIL=0
|
|
28
|
+
|
|
29
|
+
# logs-read.sh resolves PLATFORM_ROOT from its own location; mirror the
|
|
30
|
+
# logs-read.test.sh convention by symlinking the script into an ephemeral
|
|
31
|
+
# install tree per case.
|
|
32
|
+
setup_install_tree() {
|
|
33
|
+
local root="$1"
|
|
34
|
+
mkdir -p "$root/platform/scripts"
|
|
35
|
+
mkdir -p "$root/data/accounts/acct-test/logs"
|
|
36
|
+
mkdir -p "$HOME/.$(basename "$root")/logs"
|
|
37
|
+
ln -sf "$LOGS_READ" "$root/platform/scripts/logs-read.sh"
|
|
38
|
+
echo "$root/platform/scripts/logs-read.sh"
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
cleanup_install_tree() {
|
|
42
|
+
local root="$1"
|
|
43
|
+
rm -rf "$root"
|
|
44
|
+
rm -rf "$HOME/.$(basename "$root")"
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
run_case() {
|
|
48
|
+
local name="$1"
|
|
49
|
+
shift
|
|
50
|
+
echo ""
|
|
51
|
+
echo "=== CASE: $name ==="
|
|
52
|
+
if "$@"; then
|
|
53
|
+
PASS=$((PASS + 1))
|
|
54
|
+
echo "PASS: $name"
|
|
55
|
+
else
|
|
56
|
+
FAIL=$((FAIL + 1))
|
|
57
|
+
echo "FAIL: $name"
|
|
58
|
+
fi
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
# --- Case 1: 8-char prefix resolves a full-UUID file (original bug) ---
|
|
62
|
+
case_prefix_resolves_full() {
|
|
63
|
+
local root="/tmp/maxy-logs-read-prefix-1-$$"
|
|
64
|
+
local script
|
|
65
|
+
script=$(setup_install_tree "$root")
|
|
66
|
+
local logdir="$root/data/accounts/acct-test/logs"
|
|
67
|
+
local full_uuid="3483269d-c793-4a07-98cc-556d936f2f4d"
|
|
68
|
+
local filename="claude-agent-stream-${full_uuid}.log"
|
|
69
|
+
echo "prefix-match-sentinel" > "$logdir/$filename"
|
|
70
|
+
|
|
71
|
+
local stdout stderr rc=0
|
|
72
|
+
stdout=$("$script" "3483269d" system 2>/tmp/logs-read-prefix-stderr-1-$$) || rc=$?
|
|
73
|
+
stderr=$(cat /tmp/logs-read-prefix-stderr-1-$$)
|
|
74
|
+
rm -f /tmp/logs-read-prefix-stderr-1-$$
|
|
75
|
+
|
|
76
|
+
cleanup_install_tree "$root"
|
|
77
|
+
|
|
78
|
+
if [[ $rc -ne 0 ]]; then echo " expected exit 0, got $rc"; return 1; fi
|
|
79
|
+
if [[ "$stdout" != *"prefix-match-sentinel"* ]]; then
|
|
80
|
+
echo " stdout missing sentinel: $stdout"; return 1
|
|
81
|
+
fi
|
|
82
|
+
if [[ "$stderr" != *"matched_shape=full"* ]]; then
|
|
83
|
+
echo " stderr missing matched_shape=full: $stderr"; return 1
|
|
84
|
+
fi
|
|
85
|
+
if [[ "$stdout" != *"$filename"* ]]; then
|
|
86
|
+
echo " header missing matched filename: $stdout"; return 1
|
|
87
|
+
fi
|
|
88
|
+
return 0
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
# --- Case 2: Multiple matches on Pass 1 → ambiguous-prefix, exit 1 ---
|
|
92
|
+
case_ambiguous_prefix() {
|
|
93
|
+
local root="/tmp/maxy-logs-read-prefix-2-$$"
|
|
94
|
+
local script
|
|
95
|
+
script=$(setup_install_tree "$root")
|
|
96
|
+
local logdir="$root/data/accounts/acct-test/logs"
|
|
97
|
+
echo "A" > "$logdir/claude-agent-stream-aaaaaaaa-1111-A.log"
|
|
98
|
+
echo "B" > "$logdir/claude-agent-stream-aaaaaaaa-2222-B.log"
|
|
99
|
+
|
|
100
|
+
local stdout stderr rc=0
|
|
101
|
+
stdout=$("$script" "aaaaaaaa" system 2>/tmp/logs-read-prefix-stderr-2-$$) || rc=$?
|
|
102
|
+
stderr=$(cat /tmp/logs-read-prefix-stderr-2-$$)
|
|
103
|
+
rm -f /tmp/logs-read-prefix-stderr-2-$$
|
|
104
|
+
|
|
105
|
+
cleanup_install_tree "$root"
|
|
106
|
+
|
|
107
|
+
if [[ $rc -ne 1 ]]; then echo " expected exit 1, got $rc"; return 1; fi
|
|
108
|
+
if [[ "$stderr" != *"reason=ambiguous-prefix"* ]]; then
|
|
109
|
+
echo " stderr missing reason=ambiguous-prefix: $stderr"; return 1
|
|
110
|
+
fi
|
|
111
|
+
if [[ "$stderr" != *"matches=2"* ]]; then
|
|
112
|
+
echo " stderr missing matches=2: $stderr"; return 1
|
|
113
|
+
fi
|
|
114
|
+
if [[ "$stderr" != *"claude-agent-stream-aaaaaaaa-1111-A.log"* ]]; then
|
|
115
|
+
echo " stderr missing candidate A: $stderr"; return 1
|
|
116
|
+
fi
|
|
117
|
+
if [[ "$stderr" != *"claude-agent-stream-aaaaaaaa-2222-B.log"* ]]; then
|
|
118
|
+
echo " stderr missing candidate B: $stderr"; return 1
|
|
119
|
+
fi
|
|
120
|
+
# Body must NOT contain either sentinel — refusal means no body emitted.
|
|
121
|
+
if [[ -n "$stdout" ]]; then
|
|
122
|
+
echo " stdout non-empty on ambiguous refusal: $stdout"; return 1
|
|
123
|
+
fi
|
|
124
|
+
return 0
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
# --- Case 3: more-specific prefix disambiguates ---
|
|
128
|
+
case_more_specific_disambiguates() {
|
|
129
|
+
local root="/tmp/maxy-logs-read-prefix-3-$$"
|
|
130
|
+
local script
|
|
131
|
+
script=$(setup_install_tree "$root")
|
|
132
|
+
local logdir="$root/data/accounts/acct-test/logs"
|
|
133
|
+
echo "ONE" > "$logdir/claude-agent-stream-aaaaaaaa-1111-A.log"
|
|
134
|
+
echo "TWO" > "$logdir/claude-agent-stream-aaaaaaaa-2222-B.log"
|
|
135
|
+
|
|
136
|
+
local stdout stderr rc=0
|
|
137
|
+
stdout=$("$script" "aaaaaaaa-1111" system 2>/tmp/logs-read-prefix-stderr-3-$$) || rc=$?
|
|
138
|
+
stderr=$(cat /tmp/logs-read-prefix-stderr-3-$$)
|
|
139
|
+
rm -f /tmp/logs-read-prefix-stderr-3-$$
|
|
140
|
+
|
|
141
|
+
cleanup_install_tree "$root"
|
|
142
|
+
|
|
143
|
+
if [[ $rc -ne 0 ]]; then echo " expected exit 0, got $rc"; return 1; fi
|
|
144
|
+
if [[ "$stdout" != *"ONE"* ]]; then echo " stdout missing ONE: $stdout"; return 1; fi
|
|
145
|
+
if [[ "$stdout" == *"TWO"* ]]; then echo " stdout contained TWO: $stdout"; return 1; fi
|
|
146
|
+
if [[ "$stderr" != *"matched_shape=full"* ]]; then
|
|
147
|
+
echo " stderr missing matched_shape=full: $stderr"; return 1
|
|
148
|
+
fi
|
|
149
|
+
return 0
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
# --- Case 4: zero matches → tried=[<glob>, <glob>] file-not-found ---
|
|
153
|
+
case_zero_matches() {
|
|
154
|
+
local root="/tmp/maxy-logs-read-prefix-4-$$"
|
|
155
|
+
local script
|
|
156
|
+
script=$(setup_install_tree "$root")
|
|
157
|
+
|
|
158
|
+
local stdout stderr rc=0
|
|
159
|
+
stdout=$("$script" "xxxxxxxx" system 2>/tmp/logs-read-prefix-stderr-4-$$) || rc=$?
|
|
160
|
+
stderr=$(cat /tmp/logs-read-prefix-stderr-4-$$)
|
|
161
|
+
rm -f /tmp/logs-read-prefix-stderr-4-$$
|
|
162
|
+
|
|
163
|
+
cleanup_install_tree "$root"
|
|
164
|
+
|
|
165
|
+
if [[ $rc -ne 1 ]]; then echo " expected exit 1, got $rc"; return 1; fi
|
|
166
|
+
if [[ "$stderr" != *"reason=file-not-found-in-either-shape"* ]]; then
|
|
167
|
+
echo " stderr missing reason=file-not-found-in-either-shape: $stderr"; return 1
|
|
168
|
+
fi
|
|
169
|
+
if [[ "$stderr" != *"tried=[claude-agent-stream-xxxxxxxx*.log, claude-agent-stream-preflush-xxxxxxxx*.log]"* ]]; then
|
|
170
|
+
echo " stderr missing tried=[<glob>, <glob>]: $stderr"; return 1
|
|
171
|
+
fi
|
|
172
|
+
return 0
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
# --- Case 5: full 36-char UUID still resolves (regression boundary) ---
|
|
176
|
+
case_full_uuid_regression() {
|
|
177
|
+
local root="/tmp/maxy-logs-read-prefix-5-$$"
|
|
178
|
+
local script
|
|
179
|
+
script=$(setup_install_tree "$root")
|
|
180
|
+
local logdir="$root/data/accounts/acct-test/logs"
|
|
181
|
+
local full_uuid="3483269d-c793-4a07-98cc-556d936f2f4d"
|
|
182
|
+
local filename="claude-agent-stream-${full_uuid}.log"
|
|
183
|
+
echo "regression-sentinel" > "$logdir/$filename"
|
|
184
|
+
|
|
185
|
+
local stdout stderr rc=0
|
|
186
|
+
stdout=$("$script" "$full_uuid" system 2>/tmp/logs-read-prefix-stderr-5-$$) || rc=$?
|
|
187
|
+
stderr=$(cat /tmp/logs-read-prefix-stderr-5-$$)
|
|
188
|
+
rm -f /tmp/logs-read-prefix-stderr-5-$$
|
|
189
|
+
|
|
190
|
+
cleanup_install_tree "$root"
|
|
191
|
+
|
|
192
|
+
if [[ $rc -ne 0 ]]; then echo " expected exit 0, got $rc"; return 1; fi
|
|
193
|
+
if [[ "$stdout" != *"regression-sentinel"* ]]; then
|
|
194
|
+
echo " stdout missing sentinel: $stdout"; return 1
|
|
195
|
+
fi
|
|
196
|
+
if [[ "$stderr" != *"matched_shape=full"* ]]; then
|
|
197
|
+
echo " stderr missing matched_shape=full: $stderr"; return 1
|
|
198
|
+
fi
|
|
199
|
+
return 0
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
# --- Case 6: preflush-only file, prefix-match (Pass 2) ---
|
|
203
|
+
case_preflush_prefix_match() {
|
|
204
|
+
local root="/tmp/maxy-logs-read-prefix-6-$$"
|
|
205
|
+
local script
|
|
206
|
+
script=$(setup_install_tree "$root")
|
|
207
|
+
local logdir="$root/data/accounts/acct-test/logs"
|
|
208
|
+
# Preflush filename uses sessionKey:0:12; mimic with a 12-char slice.
|
|
209
|
+
echo "preflush-prefix-sentinel" > "$logdir/claude-agent-stream-preflush-bbbbbbbb-ccc.log"
|
|
210
|
+
|
|
211
|
+
local stdout stderr rc=0
|
|
212
|
+
stdout=$("$script" "bbbbbbbb" system 2>/tmp/logs-read-prefix-stderr-6-$$) || rc=$?
|
|
213
|
+
stderr=$(cat /tmp/logs-read-prefix-stderr-6-$$)
|
|
214
|
+
rm -f /tmp/logs-read-prefix-stderr-6-$$
|
|
215
|
+
|
|
216
|
+
cleanup_install_tree "$root"
|
|
217
|
+
|
|
218
|
+
if [[ $rc -ne 0 ]]; then echo " expected exit 0, got $rc"; return 1; fi
|
|
219
|
+
if [[ "$stdout" != *"preflush-prefix-sentinel"* ]]; then
|
|
220
|
+
echo " stdout missing sentinel: $stdout"; return 1
|
|
221
|
+
fi
|
|
222
|
+
if [[ "$stderr" != *"matched_shape=preflush"* ]]; then
|
|
223
|
+
echo " stderr missing matched_shape=preflush: $stderr"; return 1
|
|
224
|
+
fi
|
|
225
|
+
return 0
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
# --- Case 7: preflush ambiguity (Pass 1 zero, Pass 2 multiple) ---
|
|
229
|
+
case_preflush_ambiguity() {
|
|
230
|
+
local root="/tmp/maxy-logs-read-prefix-7-$$"
|
|
231
|
+
local script
|
|
232
|
+
script=$(setup_install_tree "$root")
|
|
233
|
+
local logdir="$root/data/accounts/acct-test/logs"
|
|
234
|
+
echo "P1" > "$logdir/claude-agent-stream-preflush-cccccccc-111.log"
|
|
235
|
+
echo "P2" > "$logdir/claude-agent-stream-preflush-cccccccc-222.log"
|
|
236
|
+
|
|
237
|
+
local stdout stderr rc=0
|
|
238
|
+
stdout=$("$script" "cccccccc" system 2>/tmp/logs-read-prefix-stderr-7-$$) || rc=$?
|
|
239
|
+
stderr=$(cat /tmp/logs-read-prefix-stderr-7-$$)
|
|
240
|
+
rm -f /tmp/logs-read-prefix-stderr-7-$$
|
|
241
|
+
|
|
242
|
+
cleanup_install_tree "$root"
|
|
243
|
+
|
|
244
|
+
if [[ $rc -ne 1 ]]; then echo " expected exit 1, got $rc"; return 1; fi
|
|
245
|
+
if [[ "$stderr" != *"reason=ambiguous-prefix"* ]]; then
|
|
246
|
+
echo " stderr missing reason=ambiguous-prefix: $stderr"; return 1
|
|
247
|
+
fi
|
|
248
|
+
if [[ "$stderr" != *"matches=2"* ]]; then
|
|
249
|
+
echo " stderr missing matches=2: $stderr"; return 1
|
|
250
|
+
fi
|
|
251
|
+
if [[ "$stderr" != *"claude-agent-stream-preflush-cccccccc-111.log"* ]]; then
|
|
252
|
+
echo " stderr missing preflush candidate 1: $stderr"; return 1
|
|
253
|
+
fi
|
|
254
|
+
if [[ "$stderr" != *"claude-agent-stream-preflush-cccccccc-222.log"* ]]; then
|
|
255
|
+
echo " stderr missing preflush candidate 2: $stderr"; return 1
|
|
256
|
+
fi
|
|
257
|
+
return 0
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
# --- Case 8: every per-conversation type resolves under prefix-match ---
|
|
261
|
+
# Parameterised over the four prefix_for_type values:
|
|
262
|
+
# agent-stream → claude-agent-stream-
|
|
263
|
+
# session → sse-events-
|
|
264
|
+
# error → claude-agent-stderr-
|
|
265
|
+
# public → public-agent-stream-
|
|
266
|
+
case_all_types_prefix_match() {
|
|
267
|
+
local root="/tmp/maxy-logs-read-prefix-8-$$"
|
|
268
|
+
local script
|
|
269
|
+
script=$(setup_install_tree "$root")
|
|
270
|
+
local logdir="$root/data/accounts/acct-test/logs"
|
|
271
|
+
local full_uuid="7d49ef21-1234-4567-89ab-cdef01234567"
|
|
272
|
+
|
|
273
|
+
echo "sentinel-agent-stream" > "$logdir/claude-agent-stream-${full_uuid}.log"
|
|
274
|
+
echo "sentinel-session" > "$logdir/sse-events-${full_uuid}.log"
|
|
275
|
+
echo "sentinel-error" > "$logdir/claude-agent-stderr-${full_uuid}.log"
|
|
276
|
+
echo "sentinel-public" > "$logdir/public-agent-stream-${full_uuid}.log"
|
|
277
|
+
|
|
278
|
+
local pairs=(
|
|
279
|
+
"agent-stream|sentinel-agent-stream"
|
|
280
|
+
"session|sentinel-session"
|
|
281
|
+
"error|sentinel-error"
|
|
282
|
+
"public|sentinel-public"
|
|
283
|
+
)
|
|
284
|
+
local fail=0
|
|
285
|
+
local pair t expected_sentinel
|
|
286
|
+
for pair in "${pairs[@]}"; do
|
|
287
|
+
t="${pair%%|*}"
|
|
288
|
+
expected_sentinel="${pair##*|}"
|
|
289
|
+
local stdout stderr rc=0
|
|
290
|
+
stdout=$("$script" "7d49ef21" "$t" 2>/tmp/logs-read-prefix-stderr-8-$$) || rc=$?
|
|
291
|
+
stderr=$(cat /tmp/logs-read-prefix-stderr-8-$$)
|
|
292
|
+
rm -f /tmp/logs-read-prefix-stderr-8-$$
|
|
293
|
+
if [[ $rc -ne 0 ]]; then echo " [$t] expected exit 0, got $rc — stderr: $stderr"; fail=1; continue; fi
|
|
294
|
+
if [[ "$stdout" != *"$expected_sentinel"* ]]; then
|
|
295
|
+
echo " [$t] missing sentinel '$expected_sentinel': $stdout"; fail=1
|
|
296
|
+
fi
|
|
297
|
+
if [[ "$stderr" != *"matched_shape=full"* ]]; then
|
|
298
|
+
echo " [$t] stderr missing matched_shape=full: $stderr"; fail=1
|
|
299
|
+
fi
|
|
300
|
+
done
|
|
301
|
+
|
|
302
|
+
cleanup_install_tree "$root"
|
|
303
|
+
[[ $fail -eq 0 ]]
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
# --- Case 9: conv_id with shell metacharacters is rejected ---
|
|
307
|
+
case_metacharacter_rejected() {
|
|
308
|
+
local root="/tmp/maxy-logs-read-prefix-9-$$"
|
|
309
|
+
local script
|
|
310
|
+
script=$(setup_install_tree "$root")
|
|
311
|
+
|
|
312
|
+
local stdout stderr rc=0
|
|
313
|
+
# Use a backslash-escaped wildcard to feed it as a literal argument.
|
|
314
|
+
stdout=$("$script" "ab*cd" system 2>/tmp/logs-read-prefix-stderr-9-$$) || rc=$?
|
|
315
|
+
stderr=$(cat /tmp/logs-read-prefix-stderr-9-$$)
|
|
316
|
+
rm -f /tmp/logs-read-prefix-stderr-9-$$
|
|
317
|
+
|
|
318
|
+
cleanup_install_tree "$root"
|
|
319
|
+
|
|
320
|
+
if [[ $rc -ne 2 ]]; then echo " expected exit 2 (usage), got $rc"; return 1; fi
|
|
321
|
+
if [[ "$stderr" != *"invalid characters"* ]]; then
|
|
322
|
+
echo " stderr missing invalid-characters guard: $stderr"; return 1
|
|
323
|
+
fi
|
|
324
|
+
return 0
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
run_case "8-char prefix resolves full-UUID file" case_prefix_resolves_full
|
|
328
|
+
run_case "ambiguous prefix → refused, candidates listed" case_ambiguous_prefix
|
|
329
|
+
run_case "more-specific prefix disambiguates" case_more_specific_disambiguates
|
|
330
|
+
run_case "zero matches → file-not-found-in-either-shape" case_zero_matches
|
|
331
|
+
run_case "full 36-char UUID still resolves" case_full_uuid_regression
|
|
332
|
+
run_case "preflush file resolves by prefix" case_preflush_prefix_match
|
|
333
|
+
run_case "preflush ambiguity refused" case_preflush_ambiguity
|
|
334
|
+
run_case "every per-conversation type prefix-matches" case_all_types_prefix_match
|
|
335
|
+
run_case "shell metacharacters rejected" case_metacharacter_rejected
|
|
336
|
+
|
|
337
|
+
echo ""
|
|
338
|
+
echo "================================================"
|
|
339
|
+
echo " Passed: $PASS / Failed: $FAIL"
|
|
340
|
+
echo "================================================"
|
|
341
|
+
[[ $FAIL -eq 0 ]]
|
|
@@ -144,25 +144,42 @@ validate_type() {
|
|
|
144
144
|
esac
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
-
# --- Per-conversation mode:
|
|
147
|
+
# --- Per-conversation mode: prefix-match {prefix}{convId-prefix}*.log ---
|
|
148
148
|
#
|
|
149
|
-
# Task 671
|
|
149
|
+
# Task 671 — two filename shapes the writer emits:
|
|
150
150
|
# FULL: {prefix}{conversationId}.log (post 1→2-user-turn flush)
|
|
151
151
|
# PREFLUSH: {prefix}preflush-{id:0:12}.log (pre-flush, first turn; the
|
|
152
152
|
# slice is of the sessionKey)
|
|
153
|
-
# This mode tries FULL across every account dir first; if zero FULL hits,
|
|
154
|
-
# falls back to PREFLUSH across every dir. If BOTH exist in the same dir,
|
|
155
|
-
# FULL wins and the preflush sibling is logged to server.log as stale
|
|
156
|
-
# (promotion-race housekeeping signal).
|
|
157
153
|
#
|
|
158
|
-
#
|
|
154
|
+
# Task 998 — operator surface accepts any unambiguous prefix of the id.
|
|
155
|
+
# The writer's filename is canonical (full 36-char UUID) but every agent
|
|
156
|
+
# line surfaces the 8-char id (`conversationId=3483269d…`). Pass 1 and
|
|
157
|
+
# Pass 2 each glob `${prefix}${conv_id}*.log` so an 8-char operator input
|
|
158
|
+
# resolves a full-UUID file. Three trailer outcomes:
|
|
159
|
+
# single hit → exit 0 + matched_shape=full|preflush (existing success)
|
|
160
|
+
# zero hits → exit 1 + reason=file-not-found-in-either-shape (existing miss)
|
|
161
|
+
# 2+ hits → exit 1 + reason=ambiguous-prefix candidates=[...]; never picks
|
|
162
|
+
#
|
|
163
|
+
# Stale-preflush housekeeping (Task 671): on a single FULL hit, glob the
|
|
164
|
+
# preflush pattern in the same dir and emit `[logs-read] stale-preflush-detected`
|
|
165
|
+
# to server.log for each sibling. Best-effort — retrieval doesn't fail if
|
|
166
|
+
# server.log is unwritable.
|
|
167
|
+
#
|
|
168
|
+
# Identity note: the preflush fallback exists for the abrupt-exit case where
|
|
159
169
|
# the operator passes the sessionKey (no conversationId was ever assigned).
|
|
160
|
-
# For post-flush retrievals the operator passes the conversationId
|
|
161
|
-
# FULL file is expected to exist.
|
|
170
|
+
# For post-flush retrievals the operator passes the conversationId (or any
|
|
171
|
+
# unambiguous prefix of it) and the FULL file is expected to exist.
|
|
162
172
|
#
|
|
163
|
-
# MIRROR
|
|
164
|
-
# - platform/ui/app/lib/logs-read-resolve.ts (canonical TS helper
|
|
165
|
-
#
|
|
173
|
+
# MIRROR — intentional divergence (Task 998):
|
|
174
|
+
# - platform/ui/app/lib/logs-read-resolve.ts (canonical TS helper) —
|
|
175
|
+
# EXACT-MATCH on `${prefix}${conversationId}.log`.
|
|
176
|
+
# - platform/plugins/admin/mcp/src/index.ts (conversationId branch) —
|
|
177
|
+
# EXACT-MATCH, same shape.
|
|
178
|
+
# Both serve agents that always hold the full conversationId. This shell
|
|
179
|
+
# counterpart is the operator surface; operators read 8-char ids from agent
|
|
180
|
+
# lines and type them at the SSH prompt. Do not "fix" the divergence by
|
|
181
|
+
# adding prefix-match to TS/MCP — it would mask agent bugs that pass
|
|
182
|
+
# partial ids.
|
|
166
183
|
per_conversation_mode() {
|
|
167
184
|
local conv_id="$1"
|
|
168
185
|
local filter_type="${2:-agent-stream}"
|
|
@@ -172,6 +189,14 @@ per_conversation_mode() {
|
|
|
172
189
|
exit 2
|
|
173
190
|
fi
|
|
174
191
|
|
|
192
|
+
# Reject shell metacharacters in conv_id — the prefix-match glob below
|
|
193
|
+
# would otherwise turn user input into a shell pattern. UUIDs and
|
|
194
|
+
# sessionKeys only contain [a-zA-Z0-9-].
|
|
195
|
+
if [[ ! "$conv_id" =~ ^[a-zA-Z0-9-]+$ ]]; then
|
|
196
|
+
echo "Error: conversationId contains invalid characters (allowed: a-z, A-Z, 0-9, -)" >&2
|
|
197
|
+
exit 2
|
|
198
|
+
fi
|
|
199
|
+
|
|
175
200
|
validate_type "$filter_type"
|
|
176
201
|
|
|
177
202
|
# Platform-scoped types shortcut to their fixed files regardless of convId.
|
|
@@ -190,56 +215,98 @@ per_conversation_mode() {
|
|
|
190
215
|
exit 2
|
|
191
216
|
fi
|
|
192
217
|
|
|
193
|
-
#
|
|
194
|
-
local
|
|
195
|
-
local
|
|
218
|
+
# Glob patterns the two-shape contract expects (Task 998: prefix match).
|
|
219
|
+
local full_glob="${prefix}${conv_id}*.log"
|
|
220
|
+
local preflush_glob="${prefix}preflush-${conv_id:0:12}*.log"
|
|
196
221
|
|
|
197
|
-
local found=0
|
|
198
222
|
local searched=0
|
|
199
223
|
local matched_shape=""
|
|
200
224
|
|
|
201
|
-
# Pass 1: FULL across every log dir
|
|
225
|
+
# Pass 1: FULL prefix glob across every log dir; aggregate hits.
|
|
226
|
+
local -a full_hits=()
|
|
227
|
+
local -a full_dirs=()
|
|
228
|
+
shopt -s nullglob
|
|
202
229
|
for log_dir in "${ACCOUNT_LOG_DIRS[@]}"; do
|
|
203
|
-
local full_path="$log_dir/$full_name"
|
|
204
230
|
searched=$((searched + 1))
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
231
|
+
for f in "$log_dir"/${prefix}${conv_id}*.log; do
|
|
232
|
+
full_hits+=("$f")
|
|
233
|
+
full_dirs+=("$log_dir")
|
|
234
|
+
done
|
|
235
|
+
done
|
|
236
|
+
shopt -u nullglob
|
|
237
|
+
|
|
238
|
+
# Pass 1 ambiguous → refuse, never silently pick.
|
|
239
|
+
if [[ ${#full_hits[@]} -gt 1 ]]; then
|
|
240
|
+
local -a candidate_names=()
|
|
241
|
+
local cf
|
|
242
|
+
for cf in "${full_hits[@]}"; do
|
|
243
|
+
candidate_names+=("$(basename "$cf")")
|
|
244
|
+
done
|
|
245
|
+
local joined
|
|
246
|
+
joined=$(IFS=','; printf '%s' "${candidate_names[*]}")
|
|
247
|
+
echo "-- trailer: conversationId=$conv_id type=$filter_type searched=$searched matches=${#full_hits[@]} reason=ambiguous-prefix candidates=[${joined}]" >&2
|
|
248
|
+
exit 1
|
|
249
|
+
fi
|
|
250
|
+
|
|
251
|
+
local found=0
|
|
252
|
+
|
|
253
|
+
# Pass 1 single hit → emit body, run stale-preflush detection in matched dir.
|
|
254
|
+
if [[ ${#full_hits[@]} -eq 1 ]]; then
|
|
255
|
+
local full_path="${full_hits[0]}"
|
|
256
|
+
local match_dir="${full_dirs[0]}"
|
|
257
|
+
echo "## $(basename "$full_path") ($filter_type)$(account_suffix "$match_dir")"
|
|
210
258
|
cat "$full_path"
|
|
211
|
-
found
|
|
259
|
+
found=1
|
|
212
260
|
matched_shape="full"
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
if [[ -f "$stale_path" ]]; then
|
|
261
|
+
|
|
262
|
+
shopt -s nullglob
|
|
263
|
+
local stale_path
|
|
264
|
+
for stale_path in "$match_dir"/${prefix}preflush-${conv_id:0:12}*.log; do
|
|
218
265
|
local stale_ts
|
|
219
266
|
stale_ts="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
220
267
|
echo "${stale_ts} [logs-read] stale-preflush-detected path=${stale_path}" >> "$SERVER_LOG" 2>/dev/null || true
|
|
221
|
-
|
|
222
|
-
|
|
268
|
+
done
|
|
269
|
+
shopt -u nullglob
|
|
270
|
+
fi
|
|
223
271
|
|
|
224
|
-
# Pass 2: PREFLUSH
|
|
272
|
+
# Pass 2: PREFLUSH prefix glob, only if Pass 1 had zero hits.
|
|
225
273
|
if [[ $found -eq 0 ]]; then
|
|
274
|
+
local -a preflush_hits=()
|
|
275
|
+
local -a preflush_dirs=()
|
|
276
|
+
shopt -s nullglob
|
|
226
277
|
for log_dir in "${ACCOUNT_LOG_DIRS[@]}"; do
|
|
227
|
-
local preflush_path="$log_dir/$preflush_name"
|
|
228
278
|
searched=$((searched + 1))
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
279
|
+
for f in "$log_dir"/${prefix}preflush-${conv_id:0:12}*.log; do
|
|
280
|
+
preflush_hits+=("$f")
|
|
281
|
+
preflush_dirs+=("$log_dir")
|
|
282
|
+
done
|
|
283
|
+
done
|
|
284
|
+
shopt -u nullglob
|
|
285
|
+
|
|
286
|
+
if [[ ${#preflush_hits[@]} -gt 1 ]]; then
|
|
287
|
+
local -a candidate_names=()
|
|
288
|
+
local pf
|
|
289
|
+
for pf in "${preflush_hits[@]}"; do
|
|
290
|
+
candidate_names+=("$(basename "$pf")")
|
|
291
|
+
done
|
|
292
|
+
local joined
|
|
293
|
+
joined=$(IFS=','; printf '%s' "${candidate_names[*]}")
|
|
294
|
+
echo "-- trailer: conversationId=$conv_id type=$filter_type searched=$searched matches=${#preflush_hits[@]} reason=ambiguous-prefix candidates=[${joined}]" >&2
|
|
295
|
+
exit 1
|
|
296
|
+
fi
|
|
297
|
+
|
|
298
|
+
if [[ ${#preflush_hits[@]} -eq 1 ]]; then
|
|
299
|
+
local preflush_path="${preflush_hits[0]}"
|
|
300
|
+
echo "## $(basename "$preflush_path") ($filter_type)$(account_suffix "${preflush_dirs[0]}")"
|
|
234
301
|
cat "$preflush_path"
|
|
235
|
-
found
|
|
302
|
+
found=1
|
|
236
303
|
matched_shape="preflush"
|
|
237
|
-
|
|
304
|
+
fi
|
|
238
305
|
fi
|
|
239
306
|
|
|
240
307
|
# Trailer: empty output never leaves the reader guessing why.
|
|
241
308
|
if [[ $found -eq 0 ]]; then
|
|
242
|
-
echo "-- trailer: conversationId=$conv_id type=$filter_type searched=$searched found=0 tried=[${
|
|
309
|
+
echo "-- trailer: conversationId=$conv_id type=$filter_type searched=$searched found=0 tried=[${full_glob}, ${preflush_glob}] reason=file-not-found-in-either-shape" >&2
|
|
243
310
|
exit 1
|
|
244
311
|
fi
|
|
245
312
|
echo "" >&2
|
|
@@ -22,6 +22,10 @@ CONV_ID="8c264cfb-441f-48bf-9811-9fa3ad3b51dc"
|
|
|
22
22
|
PREFLUSH_SLICE="8c264cfb-441"
|
|
23
23
|
FULL_NAME="claude-agent-stream-${CONV_ID}.log"
|
|
24
24
|
PREFLUSH_NAME="claude-agent-stream-preflush-${PREFLUSH_SLICE}.log"
|
|
25
|
+
# Task 998 — miss trailer now lists glob patterns, not literal filenames,
|
|
26
|
+
# because Pass 1 / Pass 2 prefix-glob `${prefix}${conv_id}*.log`.
|
|
27
|
+
FULL_GLOB="claude-agent-stream-${CONV_ID}*.log"
|
|
28
|
+
PREFLUSH_GLOB="claude-agent-stream-preflush-${PREFLUSH_SLICE}*.log"
|
|
25
29
|
|
|
26
30
|
PASS=0
|
|
27
31
|
FAIL=0
|
|
@@ -141,8 +145,8 @@ case_neither() {
|
|
|
141
145
|
echo " expected exit 1, got $rc"
|
|
142
146
|
return 1
|
|
143
147
|
fi
|
|
144
|
-
if [[ "$stderr" != *"tried=[${
|
|
145
|
-
echo " stderr missing tried=[…,…]: $stderr"
|
|
148
|
+
if [[ "$stderr" != *"tried=[${FULL_GLOB}, ${PREFLUSH_GLOB}]"* ]]; then
|
|
149
|
+
echo " stderr missing tried=[…,…] glob patterns: $stderr"
|
|
146
150
|
return 1
|
|
147
151
|
fi
|
|
148
152
|
if [[ "$stderr" != *"reason=file-not-found-in-either-shape"* ]]; then
|
|
@@ -55,7 +55,7 @@ Your personalisation is in `agents/admin/SOUL.md`. Read it and apply it. SOUL.md
|
|
|
55
55
|
- Professional, direct, action-oriented. Concise and precise. British English unless SOUL.md specifies otherwise.
|
|
56
56
|
- Do not use emoji characters in any response. Plain text and punctuation only.
|
|
57
57
|
- Every URL in a response must be a clickable markdown link — never raw text. Use `[visible label](url)` format. The label should be the meaningful part of the URL (hostname, path, or a short description), not the full raw URL repeated.
|
|
58
|
-
- **Internal scaffolding never appears in user-facing replies.** Tool descriptions, error strings, skill prompts, and graph schemas may name internal contracts (e.g. `producedByTaskId`, `:PRODUCED`, `Write blocked (no-admin-user)`, `missing-
|
|
58
|
+
- **Internal scaffolding never appears in user-facing replies.** Tool descriptions, error strings, skill prompts, and graph schemas may name internal contracts (e.g. `producedByTaskId`, `:PRODUCED`, `Write blocked (no-admin-user)`, `missing-provenance`, `archiveSha256`, `lastIngestedMessageHash`) so you know how to act. Translate them into plain English for the user. Forbidden in user-facing replies: literal tool names with `mcp__` prefix, `Task NNN` references, doctrine names ("process-provenance", "write doctrine", "graph-write gate"), MCP-tool-description quotes, raw error discriminators. Forbidden phrasings include: "the LocalBusiness write requires a producedByTaskId per Task 885", "the write gate is blocking", "I'll call ToolSearch". Permitted equivalents: "I need to set up the business profile first before recording that — let me run that now", "the platform requires me to anchor this to an action record — creating one now". Internal language stays in your reasoning, not your prose to the operator.
|
|
59
59
|
|
|
60
60
|
## Information Sourcing
|
|
61
61
|
|
|
@@ -17,7 +17,7 @@ plugins:
|
|
|
17
17
|
|
|
18
18
|
# Real Agency
|
|
19
19
|
|
|
20
|
-
Premium plugin bundle for UK estate agency professionals. Purchasing this bundle grants access to all 11 sub-plugins, each independently activatable via `enabledPlugins`. The bundle also delivers three specialist roles — negotiator, valuer, and compliance — that embed domain knowledge from the sub-plugins and operate autonomously with Loop CRM tools.
|
|
20
|
+
Premium plugin bundle for UK estate agency professionals. Purchasing this bundle grants access to all 11 sub-plugins, each independently activatable via `enabledPlugins`. When the bundle is auto-delivered at admin session startup (or explicitly via `premium-deliver`), every sub-plugin in the `plugins:` list above is stamped into `account.json` `enabledPlugins` in one idempotent set-union — delivery implies enablement, so `mcp__loop__*` tools and the other sub-plugin surfaces are visible to the agent from the first turn after install. Disable individual sub-plugins with `plugin-toggle-enabled`. The bundle also delivers three specialist roles — negotiator, valuer, and compliance — that embed domain knowledge from the sub-plugins and operate autonomously with Loop CRM tools.
|
|
21
21
|
|
|
22
22
|
## Specialist Roles
|
|
23
23
|
|