@rubytech/create-maxy 1.0.886 → 1.0.887

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/create-maxy",
3
- "version": "1.0.886",
3
+ "version": "1.0.887",
4
4
  "description": "Install Maxy — AI for Productive People",
5
5
  "bin": {
6
6
  "create-maxy": "./dist/index.js"
@@ -52,7 +52,7 @@ Tools are available via the `admin` MCP server.
52
52
 
53
53
  `logs-read { type: "agent-stream" }` is the canonical name for the per-conversation tool-use/tool-result archive previously called `system`; both names work and the legacy alias is preserved.
54
54
 
55
- **Stream log is the primary diagnostic surface for an in-progress session.** Every stream log is named by sessionKey and exists from the first token. The parent-process console fan-out tee in [`platform/ui/app/lib/claude-agent/logging.ts`](../../ui/app/lib/claude-agent/logging.ts) appends every `[<tag>]`-prefixed `console.error` / `console.log` line to every active session's stream log alongside `server.log`. For diagnosing an in-session issue (WhatsApp inbound, Cloudflare action, persist write, baileys error), call `logs-read { sessionKey: "<…>" }` first — the stream log carries both the agent lifecycle AND the parent-process events that occurred during the session window. The `conversationId` parameter remains a legacy alias resolving to the same sessionKey-named file. `logs-read { type: "server" }` is the cross-session escape hatch for events outside any single session window.
55
+ **Stream log is the primary diagnostic surface for an in-progress session.** Every stream log is named by sessionKey and the stream-log file exists from the first token. The first-token invariant is bound by `platform/scripts/__tests__/first-token-creates-stream-log.test.sh`: one operator turn, one token, `claude-agent-stream-<sessionKey>.log` exists and contains the token bytes after the byte returns to the operator. The parent-process console fan-out tee in [`platform/ui/app/lib/claude-agent/logging.ts`](../../ui/app/lib/claude-agent/logging.ts) appends every `[<tag>]`-prefixed `console.error` / `console.log` line to every active session's stream log alongside `server.log`. For diagnosing an in-session issue (WhatsApp inbound, Cloudflare action, persist write, baileys error), call `logs-read { sessionKey: "<…>" }` first — the stream log carries both the agent lifecycle AND the parent-process events that occurred during the session window. The `conversationId` parameter remains a legacy alias resolving to the same sessionKey-named file. `logs-read { type: "server" }` is the cross-session escape hatch for events outside any single session window.
56
56
 
57
57
  ## Skills
58
58
 
@@ -1,5 +1,13 @@
1
1
  # Troubleshooting
2
2
 
3
+ ## Stream-log file for a fresh session is absent or empty
4
+
5
+ **Symptom:** Operator opens a new admin session, sends one turn, sees the agent reply, then `logs-read sessionKey=<…>` returns `file-not-found` or zero bytes.
6
+
7
+ **Invariant:** For every new session, the stream-log file exists on disk and contains the token bytes from the moment the first token returns to the operator. The first-token invariant is bound by `platform/scripts/__tests__/first-token-creates-stream-log.test.sh`: one operator turn, one token, `claude-agent-stream-<sessionKey>.log` exists and contains the token bytes — pass iff file present and bytes present.
8
+
9
+ **Diagnose if it ever recurs:** run `bash platform/scripts/__tests__/first-token-creates-stream-log.test.sh` from the install. Pass = invariant holds; any other exit = the writer-side existence contract is broken and one `[log-tee] missing-on-resolve sessionKey=<8> surface=<…>` line on `server.log` is the operator-visible signal (P0).
10
+
3
11
  ## Browser navigation to a local file (`file://`) used to time out for two minutes
4
12
 
5
13
  **Symptom:** Older versions of the platform's admin agent would attempt `browser_navigate file:///path/to.html`, hit Playwright's silent two-minute timeout, then guess fixed ports (8080 / 3000 / 8000 / 9000) and report `ERR_CONNECTION_REFUSED` for each before someone manually started a local HTTP server.
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env bash
2
+ # Task 1011 — the binding test for the first-token invariant.
3
+ #
4
+ # Invariant: for every new session, at the moment the first token is
5
+ # emitted, the stream-log file for that session exists on disk and contains
6
+ # that token's bytes. Tasks 1006/1008/1010 each shipped a partial slice;
7
+ # this test fixes the invariant itself to one assertion.
8
+ #
9
+ # The .test.sh entrypoint is the operator-facing surface — runnable from a
10
+ # bash prompt, from boot smoke, and from any CI runner that has `node` on
11
+ # PATH. The contract assertions live in the vitest spec at:
12
+ # platform/ui/server/routes/admin/__tests__/first-token-creates-stream-log.test.ts
13
+ #
14
+ # That spec exercises the live chat-route handler, the live appendFileSync
15
+ # writer, and a live filesystem (mkdtempSync tmpdir). The only seams
16
+ # substituted are upstream of the writer: the agent SDK invocation, the
17
+ # session-store identity lookups, and the Neo4j createConversation
18
+ # roundtrip. None of those are the writer the invariant binds.
19
+ #
20
+ # Exit codes:
21
+ # 0 test passed — file present, token bytes present
22
+ # 1 test failed — file absent OR token bytes absent
23
+ # 2 vitest binary not resolvable from platform/ui
24
+
25
+ set -euo pipefail
26
+
27
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
28
+ UI_DIR="$(cd "$SCRIPT_DIR/../../ui" && pwd)"
29
+ SPEC_REL="server/routes/admin/__tests__/first-token-creates-stream-log.test.ts"
30
+
31
+ if [[ ! -f "$UI_DIR/$SPEC_REL" ]]; then
32
+ echo "FATAL: vitest spec missing at $UI_DIR/$SPEC_REL" >&2
33
+ exit 2
34
+ fi
35
+
36
+ cd "$UI_DIR"
37
+ exec npx --no-install vitest run "$SPEC_REL" --reporter=verbose
@@ -172,12 +172,63 @@ case_every_per_session_type() {
172
172
  return 0
173
173
  }
174
174
 
175
+ # Task 1010 — sk_<hex16> writer-side format from Task 1008 must resolve via
176
+ # the underscore-bearing regex `^[a-zA-Z0-9_-]+$`. Pre-1010 the regex
177
+ # rejected `_`, leaving every post-1008 stream log unretrievable.
178
+ case_sk_underscore_prefix_resolves() {
179
+ local root="/tmp/maxy-prefix-test-7-$$"
180
+ local script
181
+ script=$(setup_install_tree "$root")
182
+ local logdir="$root/data/accounts/acct-test/logs"
183
+ local sk="sk_16e0eeb035cf52ef"
184
+ echo "sentinel-sk" > "$logdir/claude-agent-stream-${sk}.log"
185
+
186
+ local rc=0
187
+ local stdout
188
+ stdout=$("$script" "sk_16e0e" agent-stream 2>/dev/null) || rc=$?
189
+ cleanup_install_tree "$root"
190
+
191
+ [[ $rc -eq 0 ]] || { echo " expected exit 0, got $rc"; return 1; }
192
+ [[ "$stdout" == *"sentinel-sk"* ]] || { echo " missing sentinel"; return 1; }
193
+ return 0
194
+ }
195
+
196
+ # Task 1010 — shell metacharacters must still be rejected. The regex widen
197
+ # adds `_` only; `;`, `*`, `$`, `?`, `[` etc. must still hit the reject path
198
+ # and exit 2, otherwise user input would escape literal matching in the glob.
199
+ # Also asserts the `[logs-read] regex-rejected` observability line lands in
200
+ # server.log so the adherence runner can see writer/reader divergence.
201
+ case_shell_metachar_still_rejected() {
202
+ local root="/tmp/maxy-prefix-test-8-$$"
203
+ local script
204
+ script=$(setup_install_tree "$root")
205
+ local server_log="$HOME/.$(basename "$root")/logs/server.log"
206
+ : > "$server_log"
207
+
208
+ local rc=0
209
+ "$script" "abc;rm" agent-stream >/dev/null 2>/tmp/prefix-stderr-8-$$ || rc=$?
210
+ local stderr
211
+ stderr=$(cat /tmp/prefix-stderr-8-$$)
212
+ rm -f /tmp/prefix-stderr-8-$$
213
+ local server_log_contents
214
+ server_log_contents=$(cat "$server_log" 2>/dev/null || echo "")
215
+ cleanup_install_tree "$root"
216
+
217
+ [[ $rc -eq 2 ]] || { echo " expected exit 2, got $rc"; return 1; }
218
+ [[ "$stderr" == *"invalid characters"* ]] || { echo " stderr missing 'invalid characters': $stderr"; return 1; }
219
+ [[ "$server_log_contents" == *"[logs-read] regex-rejected"* ]] || { echo " server.log missing regex-rejected line: $server_log_contents"; return 1; }
220
+ [[ "$server_log_contents" == *"sample=;"* ]] || { echo " server.log missing sample=;: $server_log_contents"; return 1; }
221
+ return 0
222
+ }
223
+
175
224
  run_case "8-char prefix resolves full sessionKey file" case_8char_prefix_resolves_full
176
225
  run_case "ambiguous prefix refuses to pick" case_ambiguous_prefix_refuses
177
226
  run_case "more-specific prefix disambiguates" case_more_specific_prefix_resolves
178
227
  run_case "zero matches → file-not-found" case_zero_match_miss
179
228
  run_case "full 36-char sessionKey resolves" case_full_sessionkey_resolves
180
229
  run_case "every per-session type resolves" case_every_per_session_type
230
+ run_case "Task 1010: sk_ underscore prefix resolves" case_sk_underscore_prefix_resolves
231
+ run_case "Task 1010: shell metacharacter still rejected" case_shell_metachar_still_rejected
181
232
 
182
233
  echo ""
183
234
  echo "================================================"
@@ -118,6 +118,16 @@ is_per_conversation_type() {
118
118
  SEARCH_TYPES="agent-stream error session public server mcp vnc"
119
119
  VALID_TYPES="agent-stream, system, session, error, heartbeat, public, server, mcp, vnc"
120
120
 
121
+ # Authoritative sessionKey character set.
122
+ # Writer-side (Task 1008) emits `sk_<hex16>` filenames; the underscore must
123
+ # stay in this regex or every post-1008 file becomes unretrievable
124
+ # (Task 1010). UUID v4 sessionKeys (legacy and current alternate format)
125
+ # also pass. Widening past this set is unsafe: session_key is concatenated
126
+ # into a glob below (`${prefix}${session_key}*.log`), so any character bash
127
+ # treats as a metacharacter (`*`, `?`, `[`, `;`, `$`, etc.) would escape
128
+ # literal matching.
129
+ SESSIONKEY_REGEX='^[a-zA-Z0-9_-]+$'
130
+
121
131
  usage() {
122
132
  cat >&2 <<EOF
123
133
  Usage:
@@ -172,10 +182,20 @@ per_conversation_mode() {
172
182
  fi
173
183
 
174
184
  # Reject shell metacharacters in session_key — the prefix-match glob below
175
- # would otherwise turn user input into a shell pattern. SessionKeys only
176
- # contain [a-zA-Z0-9-].
177
- if [[ ! "$session_key" =~ ^[a-zA-Z0-9-]+$ ]]; then
178
- echo "Error: sessionKey contains invalid characters (allowed: a-z, A-Z, 0-9, -)" >&2
185
+ # would otherwise turn user input into a shell pattern. SessionKeys are
186
+ # `sk_<hex16>` (Task 1008) or UUID v4; both fit $SESSIONKEY_REGEX defined
187
+ # near the top of the script. Task 1010 widened this from `[a-zA-Z0-9-]`
188
+ # after the Task 1008 underscore-bearing format made every post-1008
189
+ # session unretrievable.
190
+ if [[ ! "$session_key" =~ $SESSIONKEY_REGEX ]]; then
191
+ # Observability: surface the rejection before the operator-visible error
192
+ # so the adherence runner can prove writer/reader divergence.
193
+ # Best-effort append; mirrors the missing-on-resolve idiom below.
194
+ local rej_ts bad_char
195
+ rej_ts="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
196
+ bad_char=$(printf '%s' "$session_key" | tr -d 'a-zA-Z0-9_-' | head -c 1)
197
+ echo "${rej_ts} [logs-read] regex-rejected sessionKey-prefix=${session_key:0:12} regex=${SESSIONKEY_REGEX} sample=${bad_char}" >> "$SERVER_LOG" 2>/dev/null || true
198
+ echo "Error: sessionKey contains invalid characters (allowed: a-z, A-Z, 0-9, _, -)" >&2
179
199
  exit 2
180
200
  fi
181
201