@rubytech/create-maxy 1.0.884 → 1.0.886

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.
@@ -0,0 +1,151 @@
1
+ .docs/agents.md
2
+ .docs/conversation-archive.md
3
+ .docs/deployment.md
4
+ .docs/mcp-servers.md
5
+ .docs/neo4j.md
6
+ .docs/observability.md
7
+ .docs/platform.md
8
+ .docs/web-chat.md
9
+ .docs/whatsapp.md
10
+ platform/lib/graph-mcp/src/index.ts
11
+ platform/lib/graph-trash/src/index.ts
12
+ platform/lib/graph-write/src/__tests__/audit.test.ts
13
+ platform/lib/graph-write/src/audit.ts
14
+ platform/neo4j/schema.cypher
15
+ platform/plugins/admin/PLUGIN.md
16
+ platform/plugins/admin/hooks/__tests__/archive-ingest-surface-gate.test.sh
17
+ platform/plugins/admin/mcp/src/index.ts
18
+ platform/plugins/admin/skills/onboarding/SKILL.md
19
+ platform/plugins/admin/skills/stream-log-review/SKILL.md
20
+ platform/plugins/admin/skills/stream-log-review/references/analysis-patterns.md
21
+ platform/plugins/business-assistant/references/profiling.md
22
+ platform/plugins/contacts/mcp/src/index.ts
23
+ platform/plugins/contacts/mcp/src/tools/contact-erase.ts
24
+ platform/plugins/contacts/mcp/src/tools/contact-export.ts
25
+ platform/plugins/contacts/mcp/src/tools/group-create.ts
26
+ platform/plugins/contacts/mcp/src/tools/group-manage.ts
27
+ platform/plugins/docs/references/admin-session.md
28
+ platform/plugins/docs/references/contacts-guide.md
29
+ platform/plugins/docs/references/internals.md
30
+ platform/plugins/docs/references/platform.md
31
+ platform/plugins/docs/references/plugins-guide.md
32
+ platform/plugins/docs/references/troubleshooting.md
33
+ platform/plugins/linkedin-import/skills/linkedin-import/SKILL.md
34
+ platform/plugins/linkedin-import/skills/linkedin-import/references/connections.md
35
+ platform/plugins/memory/bin/conversation-archive-ingest.mjs
36
+ platform/plugins/memory/bin/conversation-archive-ingest.sh
37
+ platform/plugins/memory/mcp/scripts/boot-smoke.sh
38
+ platform/plugins/memory/mcp/scripts/graph/fixture.cypher
39
+ platform/plugins/memory/mcp/src/index.ts
40
+ platform/plugins/memory/mcp/src/lib/graph-prune.ts
41
+ platform/plugins/memory/mcp/src/tools/conversation-archive-derive-insights.ts
42
+ platform/plugins/memory/mcp/src/tools/conversation-archive-enrich-rejection.ts
43
+ platform/plugins/memory/mcp/src/tools/conversation-memory-expunge.ts
44
+ platform/plugins/memory/mcp/src/tools/memory-archive-write.ts
45
+ platform/plugins/memory/mcp/src/tools/memory-find-candidates.ts
46
+ platform/plugins/memory/mcp/src/tools/memory-ingest.ts
47
+ platform/plugins/memory/mcp/src/tools/profile-update.ts
48
+ platform/plugins/memory/references/graph-primitives.md
49
+ platform/plugins/memory/references/schema-base.md
50
+ platform/plugins/memory/skills/conversation-archive/SKILL.md
51
+ platform/plugins/memory/skills/conversational-memory/SKILL.md
52
+ platform/plugins/memory/skills/document-ingest/SKILL.md
53
+ platform/plugins/scheduling/mcp/src/index.ts
54
+ platform/plugins/tasks/.mcp.json
55
+ platform/plugins/tasks/mcp/src/index.ts
56
+ platform/plugins/tasks/mcp/src/tools/project-create.ts
57
+ platform/plugins/tasks/mcp/src/tools/session-list.ts
58
+ platform/plugins/waitlist/mcp/src/index.ts
59
+ platform/plugins/waitlist/mcp/src/tools/__tests__/waitlist-persist.test.ts
60
+ platform/plugins/waitlist/mcp/src/tools/waitlist-heal.ts
61
+ platform/plugins/waitlist/mcp/src/tools/waitlist-list.ts
62
+ platform/plugins/waitlist/mcp/src/tools/waitlist-persist.ts
63
+ platform/plugins/waitlist/mcp/src/tools/waitlist-scan.ts
64
+ platform/plugins/waitlist/mcp/src/tools/waitlist-setup.ts
65
+ platform/plugins/whatsapp/PLUGIN.md
66
+ platform/plugins/whatsapp/mcp/src/index.ts
67
+ platform/plugins/workflows/mcp/src/index.ts
68
+ platform/scripts/__tests__/admin-persist-audit.test.ts
69
+ platform/scripts/admin-conversation-recover.mjs
70
+ platform/scripts/admin-persist-audit.ts
71
+ platform/scripts/check-no-conversation-id-leaks.mjs
72
+ platform/scripts/check-sdk-oauth.mjs
73
+ platform/scripts/component-knowledgedoc-backfill.ts
74
+ platform/scripts/seed-neo4j.sh
75
+ platform/templates/agents/admin/IDENTITY.md
76
+ platform/templates/specialists/agents/database-operator.md
77
+ platform/ui/__tests__/form-spawn-tailer.test.ts
78
+ platform/ui/app/AdminModals.tsx
79
+ platform/ui/app/ChatActionsMenu.tsx
80
+ platform/ui/app/ChatFooter.tsx
81
+ platform/ui/app/ConversationRowActions.tsx
82
+ platform/ui/app/Sidebar.tsx
83
+ platform/ui/app/__tests__/buildSessionLogsUrl.test.ts
84
+ platform/ui/app/admin/components/CloudflareSetupForm.tsx
85
+ platform/ui/app/graph/__tests__/display-helpers.test.ts
86
+ platform/ui/app/graph/__tests__/zoom-tier-colour-stability.test.ts
87
+ platform/ui/app/graph/display-helpers.ts
88
+ platform/ui/app/lib/__tests__/admin-conversation-create.test.ts
89
+ platform/ui/app/lib/__tests__/admin-persist-observability.test.ts
90
+ platform/ui/app/lib/__tests__/claude-agent-loop-stop.test.ts
91
+ platform/ui/app/lib/__tests__/conversation-deferred-persistence.test.ts
92
+ platform/ui/app/lib/__tests__/ensure-conversation-handled-by.test.ts
93
+ platform/ui/app/lib/__tests__/graph-trash-conversation-cascade.test.ts
94
+ platform/ui/app/lib/__tests__/jsonl-replay.test.ts
95
+ platform/ui/app/lib/__tests__/load-user-profile-sparse.test.ts
96
+ platform/ui/app/lib/__tests__/neo4j-store-autolabel.test.ts
97
+ platform/ui/app/lib/__tests__/neo4j-store-persist-message.test.ts
98
+ platform/ui/app/lib/admin-sse-registry.ts
99
+ platform/ui/app/lib/attachments.ts
100
+ platform/ui/app/lib/claude-agent/admin-agent.ts
101
+ platform/ui/app/lib/claude-agent/compaction.ts
102
+ platform/ui/app/lib/claude-agent/dispatcher.ts
103
+ platform/ui/app/lib/claude-agent/jsonl-replay.ts
104
+ platform/ui/app/lib/claude-agent/logging.ts
105
+ platform/ui/app/lib/claude-agent/memory-context.ts
106
+ platform/ui/app/lib/claude-agent/public-agent.ts
107
+ platform/ui/app/lib/claude-agent/session-store.ts
108
+ platform/ui/app/lib/claude-agent/spawn-env.ts
109
+ platform/ui/app/lib/claude-agent/stream-parser.ts
110
+ platform/ui/app/lib/client-event.ts
111
+ platform/ui/app/lib/cloudflare-setup-types.ts
112
+ platform/ui/app/lib/cloudflare-task-tracker.ts
113
+ platform/ui/app/lib/logs-read-resolve.ts
114
+ platform/ui/app/lib/neo4j-store.ts
115
+ platform/ui/app/lib/paths.ts
116
+ platform/ui/app/lib/useConversationRename.ts
117
+ platform/ui/app/lib/useLogsPopover.ts
118
+ platform/ui/app/lib/vnc.ts
119
+ platform/ui/app/lib/whatsapp/__tests__/ensure-conversation.test.ts
120
+ platform/ui/app/lib/whatsapp/__tests__/persist-message.test.ts
121
+ platform/ui/app/lib/whatsapp/ensure-conversation.ts
122
+ platform/ui/app/lib/whatsapp/manager.ts
123
+ platform/ui/app/lib/whatsapp/persist-message.ts
124
+ platform/ui/app/page.tsx
125
+ platform/ui/app/public/MessageList.tsx
126
+ platform/ui/app/public/page.tsx
127
+ platform/ui/app/public/types.ts
128
+ platform/ui/app/public/useSession.ts
129
+ platform/ui/app/useAdminAuth.ts
130
+ platform/ui/app/useAdminChat.ts
131
+ platform/ui/server/index.ts
132
+ platform/ui/server/routes/__tests__/whatsapp-conversation-graph-state.test.ts
133
+ platform/ui/server/routes/_helpers.ts
134
+ platform/ui/server/routes/admin/__tests__/browser-iframe-route.test.ts
135
+ platform/ui/server/routes/admin/__tests__/chat-stream-log-path.test.ts
136
+ platform/ui/server/routes/admin/__tests__/cloudflare-tunnels-zero.test.ts
137
+ platform/ui/server/routes/admin/__tests__/graph-search.test.ts
138
+ platform/ui/server/routes/admin/__tests__/graph-subgraph.test.ts
139
+ platform/ui/server/routes/admin/__tests__/logs.test.ts
140
+ platform/ui/server/routes/admin/__tests__/sessions.test.ts
141
+ platform/ui/server/routes/admin/browser-iframe.ts
142
+ platform/ui/server/routes/admin/chat-failure.ts
143
+ platform/ui/server/routes/admin/chat.ts
144
+ platform/ui/server/routes/admin/cloudflare.ts
145
+ platform/ui/server/routes/admin/graph-subgraph.ts
146
+ platform/ui/server/routes/admin/logs.ts
147
+ platform/ui/server/routes/admin/session.ts
148
+ platform/ui/server/routes/admin/sessions.ts
149
+ platform/ui/server/routes/group.ts
150
+ platform/ui/server/routes/session.ts
151
+ platform/ui/server/routes/whatsapp.ts
@@ -0,0 +1,100 @@
1
+ #!/usr/bin/env bash
2
+ # log-adherence-check.sh — Task 1006 existence contract diagnostic.
3
+ #
4
+ # Reads every sessionKey under the active install's per-account logs and
5
+ # asserts that a resolvable claude-agent-stream-<sessionKey>.log exists for
6
+ # each. Runs hourly on-device in addition to the in-process timer wired
7
+ # from platform/ui/app/lib/claude-agent/logging.ts; both are intentionally
8
+ # redundant — one runs inside the server process, this one runs out of band
9
+ # so a stalled server still leaves a fresh adherence record.
10
+ #
11
+ # Emits one `[log-tee] adherence-check window=24h sessions=N misses=M ts=...`
12
+ # line to the platform server log on every run. Per-miss lines surface as
13
+ # `[log-tee] missing-on-resolve sessionKey=<8> surface=adherence-script
14
+ # reason=file-not-on-disk`. `misses=0` is the steady state.
15
+ #
16
+ # Exit codes:
17
+ # 0 No misses (steady state).
18
+ # 1 At least one miss recorded.
19
+ # 2 Usage / environment error.
20
+ set -euo pipefail
21
+
22
+ # --- Resolve install dir, the same way logs-read.sh does. ---
23
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
24
+ PLATFORM_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
25
+ INSTALL_DIR="$(dirname "$PLATFORM_ROOT")"
26
+ INSTALL_DIR_NAME=$(basename "$INSTALL_DIR")
27
+ CONFIG_DIR="$HOME/.$INSTALL_DIR_NAME"
28
+ SERVER_LOG="$CONFIG_DIR/logs/server.log"
29
+
30
+ ACCOUNTS_DIR="$INSTALL_DIR/data/accounts"
31
+ if [[ ! -d "$ACCOUNTS_DIR" ]]; then
32
+ echo "[log-adherence-check] accounts directory missing: $ACCOUNTS_DIR" >&2
33
+ exit 2
34
+ fi
35
+
36
+ # Window: only count session keys whose stream-log file was modified in the
37
+ # last 24h. This avoids fingerprinting long-archived sessions that have aged
38
+ # out via the 7-day retention purge.
39
+ WINDOW_HOURS=24
40
+
41
+ # Collect every basename-derived sessionKey from claude-agent-stream-*.log
42
+ # files across all account log directories, filtered to the active window.
43
+ declare -a KEYS=()
44
+ shopt -s nullglob
45
+ for log_dir in "$ACCOUNTS_DIR"/*/logs; do
46
+ for f in "$log_dir"/claude-agent-stream-*.log; do
47
+ if [[ -f "$f" ]]; then
48
+ # find -mmin needs minutes; 24h = 1440 min.
49
+ if find "$f" -mmin "-$((WINDOW_HOURS * 60))" -print -quit | grep -q .; then
50
+ base=$(basename "$f")
51
+ key="${base#claude-agent-stream-}"
52
+ key="${key%.log}"
53
+ KEYS+=("$key")
54
+ fi
55
+ fi
56
+ done
57
+ done
58
+ shopt -u nullglob
59
+
60
+ # De-duplicate sessionKeys (one session may emit multiple .log files via
61
+ # sibling prefixes; the existence contract is per-sessionKey).
62
+ declare -A SEEN=()
63
+ declare -a UNIQUE=()
64
+ for k in "${KEYS[@]:-}"; do
65
+ if [[ -z "${SEEN[$k]:-}" ]]; then
66
+ SEEN[$k]=1
67
+ UNIQUE+=("$k")
68
+ fi
69
+ done
70
+
71
+ SESSIONS=${#UNIQUE[@]}
72
+ MISSES=0
73
+
74
+ # For each sessionKey, verify the agent-stream file is present. Misses
75
+ # surface as a missing-on-resolve line. The file we just listed must
76
+ # exist by definition; this loop catches the case where a sessionKey was
77
+ # referenced in tee/register emissions on server.log but the file itself
78
+ # was unlinked between the scan and the check (very rare, but visible).
79
+ TS=$(date -u +%Y-%m-%dT%H:%M:%SZ)
80
+ for k in "${UNIQUE[@]:-}"; do
81
+ found=0
82
+ for log_dir in "$ACCOUNTS_DIR"/*/logs; do
83
+ if [[ -f "$log_dir/claude-agent-stream-${k}.log" ]]; then
84
+ found=1
85
+ break
86
+ fi
87
+ done
88
+ if [[ $found -eq 0 ]]; then
89
+ MISSES=$((MISSES + 1))
90
+ echo "${TS} [log-tee] missing-on-resolve sessionKey=${k:0:8} surface=adherence-script reason=file-not-on-disk" >> "$SERVER_LOG" 2>/dev/null || true
91
+ fi
92
+ done
93
+
94
+ echo "${TS} [log-tee] adherence-check window=${WINDOW_HOURS}h sessions=${SESSIONS} misses=${MISSES} surface=script ts=${TS}" >> "$SERVER_LOG" 2>/dev/null || true
95
+ echo "[log-adherence-check] sessions=${SESSIONS} misses=${MISSES} window=${WINDOW_HOURS}h"
96
+
97
+ if [[ $MISSES -gt 0 ]]; then
98
+ exit 1
99
+ fi
100
+ exit 0
@@ -1,31 +1,30 @@
1
1
  #!/usr/bin/env bash
2
2
  # logs-read.sh — Shell counterpart to the logs-read MCP tool.
3
3
  #
4
- # Task 532: stream logs are per-conversation, not per-day. The primary mode
5
- # now reads a single {prefix}-{conversationId}.log file — the reader gets a
6
- # continuous, conversation-scoped timeline from first [spawn] to final
7
- # [process-exit] without needing to filter.
8
- #
9
- # Task 671: first-turn sessions (pre 1→2-user-turn flush per Task 650) land
10
- # at {prefix}preflush-{sessionKey:0:12}.log. The per-conversation mode
11
- # resolves either shape from the same identifier — full first, preflush as
12
- # fallback.
4
+ # Task 1006: stream logs are per-session, not per-day or per-conversation.
5
+ # The primary mode reads a single {prefix}-{sessionKey}.log file — the
6
+ # operator types a sessionKey prefix and the surface returns the bytes from
7
+ # first [spawn] to final [process-exit] without filtering. sessionKey is
8
+ # the single identifier on disk; the earlier full vs preflush split is gone.
13
9
  #
14
10
  # Modes:
15
- # logs-read.sh <conversationId> [type] Read {prefix}-{convId}.log
16
- # logs-read.sh --tail [type] [lines] Tail most recent file of type
17
- # logs-read.sh --grep <sessionKey> [type] Legacy grep across log files
11
+ # logs-read.sh <sessionKey-or-prefix> [type] Read {prefix}-{sessionKey}.log
12
+ # logs-read.sh --tail [type] [lines] Tail most recent file of type
13
+ # logs-read.sh --grep <sessionKey> [type] Legacy grep across log files
18
14
  #
19
- # Types: agent-stream (canonical, per-conversation tool-use/tool-result archive;
15
+ # Types: agent-stream (canonical, per-session tool-use/tool-result archive;
20
16
  # `system` is kept as a backwards-compatible alias), session, error,
21
17
  # heartbeat, public, server, mcp, vnc
22
- # For per-conversation types (agent-stream/system/session/error/public): conversationId is
23
- # required in the first mode. For platform-scoped types (server/vnc/heartbeat):
24
- # conversationId is ignored.
18
+ # For per-session types (agent-stream/system/session/error/public): sessionKey
19
+ # is required in the first mode. For platform-scoped types (server/vnc/
20
+ # heartbeat): the id is ignored.
25
21
  #
26
22
  # Every invocation writes a one-line trailer describing files searched,
27
23
  # matches returned, and files rejected — empty output never leaves a reader
28
- # guessing why.
24
+ # guessing why. On a miss the script appends a one-line
25
+ # `[log-tee] missing-on-resolve sessionKey=<8> surface=logs-read-sh
26
+ # reason=...` entry to server.log so the writer-side existence contract is
27
+ # the authoritative signal.
29
28
  #
30
29
  # Exit codes:
31
30
  # 0 Success (output produced)
@@ -122,16 +121,16 @@ VALID_TYPES="agent-stream, system, session, error, heartbeat, public, server, mc
122
121
  usage() {
123
122
  cat >&2 <<EOF
124
123
  Usage:
125
- logs-read.sh <conversationId> [type] Read {prefix}-{convId}.log
126
- logs-read.sh --tail [type] [lines] Tail most recent log of type
127
- logs-read.sh --grep <sessionKey> [type] Legacy grep across log files
124
+ logs-read.sh <sessionKey-or-prefix> [type] Read {prefix}-{sessionKey}.log
125
+ logs-read.sh --tail [type] [lines] Tail most recent log of type
126
+ logs-read.sh --grep <sessionKey> [type] Legacy grep across log files
128
127
 
129
128
  Types: $VALID_TYPES
130
129
  Default type: agent-stream | Default lines: 50
131
130
 
132
- Per-conversation types (agent-stream, system, session, error, public) require conversationId
133
- in the first mode — the log file is named {prefix}-{conversationId}.log.
134
- Platform-scoped types (server, vnc, heartbeat) ignore conversationId.
131
+ Per-session types (agent-stream, system, session, error, public) require
132
+ sessionKey in the first mode — the log file is named {prefix}-{sessionKey}.log.
133
+ Platform-scoped types (server, vnc, heartbeat) ignore the id.
135
134
  EOF
136
135
  exit 2
137
136
  }
@@ -144,65 +143,48 @@ validate_type() {
144
143
  esac
145
144
  }
146
145
 
147
- # --- Per-conversation mode: prefix-match {prefix}{convId-prefix}*.log ---
146
+ # --- Per-session mode: prefix-match {prefix}{sessionKey-prefix}*.log ---
148
147
  #
149
- # Task 671two filename shapes the writer emits:
150
- # FULL: {prefix}{conversationId}.log (post 1→2-user-turn flush)
151
- # PREFLUSH: {prefix}preflush-{id:0:12}.log (pre-flush, first turn; the
152
- # slice is of the sessionKey)
148
+ # Task 1006one filename shape per session: {prefix}{sessionKey}.log.
149
+ # The operator types an 8-char-or-greater sessionKey prefix; we glob
150
+ # `${prefix}${session_key}*.log` across every account log dir. The earlier
151
+ # full vs preflush two-shape contract is gone: sessionKey is the single
152
+ # identifier on disk, mounted from turn 1.
153
153
  #
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)
154
+ # Three trailer outcomes:
155
+ # single hit → exit 0 (success)
156
+ # zero hits → exit 1 + appends `[log-tee] missing-on-resolve sessionKey=<8>
157
+ # surface=logs-read-sh reason=...` to server.log so
158
+ # the writer-side existence contract is the
159
+ # authoritative signal.
161
160
  # 2+ hits → exit 1 + reason=ambiguous-prefix candidates=[...]; never picks
162
161
  #
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
169
- # the operator passes the sessionKey (no conversationId was ever assigned).
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.
172
- #
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.
162
+ # MIRROR keep these three sites in lockstep:
163
+ # - platform/ui/app/lib/logs-read-resolve.ts (TS helper, exact-match)
164
+ # - platform/plugins/admin/mcp/src/index.ts (MCP tool, exact-match)
183
165
  per_conversation_mode() {
184
- local conv_id="$1"
166
+ local session_key="$1"
185
167
  local filter_type="${2:-agent-stream}"
186
168
 
187
- if [[ -z "$conv_id" ]]; then
188
- echo "Error: conversationId cannot be empty" >&2
169
+ if [[ -z "$session_key" ]]; then
170
+ echo "Error: sessionKey cannot be empty" >&2
189
171
  exit 2
190
172
  fi
191
173
 
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
174
+ # 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
197
179
  exit 2
198
180
  fi
199
181
 
200
182
  validate_type "$filter_type"
201
183
 
202
- # Platform-scoped types shortcut to their fixed files regardless of convId.
184
+ # Platform-scoped types shortcut to their fixed files regardless of id.
203
185
  case "$filter_type" in
204
186
  server|vnc|heartbeat|mcp)
205
- echo "# note: type=$filter_type is platform-scoped; conversationId ignored — showing fixed file" >&2
187
+ echo "# note: type=$filter_type is platform-scoped; sessionKey ignored — showing fixed file" >&2
206
188
  tail_mode "$filter_type" "50"
207
189
  return
208
190
  ;;
@@ -215,103 +197,51 @@ per_conversation_mode() {
215
197
  exit 2
216
198
  fi
217
199
 
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"
221
-
200
+ local glob="${prefix}${session_key}*.log"
222
201
  local searched=0
223
- local matched_shape=""
224
202
 
225
- # Pass 1: FULL prefix glob across every log dir; aggregate hits.
226
- local -a full_hits=()
227
- local -a full_dirs=()
203
+ local -a hits=()
204
+ local -a hit_dirs=()
228
205
  shopt -s nullglob
229
206
  for log_dir in "${ACCOUNT_LOG_DIRS[@]}"; do
230
207
  searched=$((searched + 1))
231
- for f in "$log_dir"/${prefix}${conv_id}*.log; do
232
- full_hits+=("$f")
233
- full_dirs+=("$log_dir")
208
+ for f in "$log_dir"/${prefix}${session_key}*.log; do
209
+ hits+=("$f")
210
+ hit_dirs+=("$log_dir")
234
211
  done
235
212
  done
236
213
  shopt -u nullglob
237
214
 
238
- # Pass 1 ambiguous → refuse, never silently pick.
239
- if [[ ${#full_hits[@]} -gt 1 ]]; then
215
+ if [[ ${#hits[@]} -gt 1 ]]; then
240
216
  local -a candidate_names=()
241
217
  local cf
242
- for cf in "${full_hits[@]}"; do
218
+ for cf in "${hits[@]}"; do
243
219
  candidate_names+=("$(basename "$cf")")
244
220
  done
245
221
  local joined
246
222
  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
223
+ echo "-- trailer: sessionKey=$session_key type=$filter_type searched=$searched matches=${#hits[@]} reason=ambiguous-prefix candidates=[${joined}]" >&2
248
224
  exit 1
249
225
  fi
250
226
 
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")"
258
- cat "$full_path"
259
- found=1
260
- matched_shape="full"
261
-
262
- shopt -s nullglob
263
- local stale_path
264
- for stale_path in "$match_dir"/${prefix}preflush-${conv_id:0:12}*.log; do
265
- local stale_ts
266
- stale_ts="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
267
- echo "${stale_ts} [logs-read] stale-preflush-detected path=${stale_path}" >> "$SERVER_LOG" 2>/dev/null || true
268
- done
269
- shopt -u nullglob
227
+ if [[ ${#hits[@]} -eq 1 ]]; then
228
+ local hit_path="${hits[0]}"
229
+ local match_dir="${hit_dirs[0]}"
230
+ echo "## $(basename "$hit_path") ($filter_type)$(account_suffix "$match_dir")"
231
+ cat "$hit_path"
232
+ echo "" >&2
233
+ echo "-- trailer: sessionKey=$session_key type=$filter_type searched=$searched matched=1" >&2
234
+ exit 0
270
235
  fi
271
236
 
272
- # Pass 2: PREFLUSH prefix glob, only if Pass 1 had zero hits.
273
- if [[ $found -eq 0 ]]; then
274
- local -a preflush_hits=()
275
- local -a preflush_dirs=()
276
- shopt -s nullglob
277
- for log_dir in "${ACCOUNT_LOG_DIRS[@]}"; do
278
- searched=$((searched + 1))
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
237
+ # Task 1006 missing-on-resolve emission. Best-effort; never fails the
238
+ # invocation when server.log is unwritable.
239
+ local miss_ts
240
+ miss_ts="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
241
+ echo "${miss_ts} [log-tee] missing-on-resolve sessionKey=${session_key:0:8} surface=logs-read-sh reason=\"file-not-found type=${filter_type}\"" >> "$SERVER_LOG" 2>/dev/null || true
297
242
 
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]}")"
301
- cat "$preflush_path"
302
- found=1
303
- matched_shape="preflush"
304
- fi
305
- fi
306
-
307
- # Trailer: empty output never leaves the reader guessing why.
308
- if [[ $found -eq 0 ]]; then
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
310
- exit 1
311
- fi
312
- echo "" >&2
313
- echo "-- trailer: conversationId=$conv_id type=$filter_type searched=$searched matched=$found matched_shape=$matched_shape" >&2
314
- exit 0
243
+ echo "-- trailer: sessionKey=$session_key type=$filter_type searched=$searched found=0 tried=[${glob}] reason=file-not-found" >&2
244
+ exit 1
315
245
  }
316
246
 
317
247
  # --- Legacy grep mode (backward compatibility with sessionKey-tagged lines) ---