@rubytech/create-maxy-code 0.1.40 → 0.1.41

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.
Files changed (114) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/lib/mcp-stderr-tee/package.json +7 -0
  3. package/payload/platform/plugins/admin/hooks/turn-completed-graph-write.sh +142 -52
  4. package/payload/platform/plugins/docs/references/platform.md +18 -4
  5. package/payload/platform/plugins/docs/references/plugins-guide.md +2 -0
  6. package/payload/platform/services/claude-session-manager/dist/config.d.ts +17 -0
  7. package/payload/platform/services/claude-session-manager/dist/config.d.ts.map +1 -1
  8. package/payload/platform/services/claude-session-manager/dist/config.js +31 -0
  9. package/payload/platform/services/claude-session-manager/dist/config.js.map +1 -1
  10. package/payload/platform/services/claude-session-manager/dist/fs-watcher.d.ts +54 -0
  11. package/payload/platform/services/claude-session-manager/dist/fs-watcher.d.ts.map +1 -0
  12. package/payload/platform/services/claude-session-manager/dist/fs-watcher.js +516 -0
  13. package/payload/platform/services/claude-session-manager/dist/fs-watcher.js.map +1 -0
  14. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts +3 -1
  15. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  16. package/payload/platform/services/claude-session-manager/dist/http-server.js +364 -204
  17. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  18. package/payload/platform/services/claude-session-manager/dist/index.js +38 -11
  19. package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
  20. package/payload/platform/services/claude-session-manager/dist/jsonl-path.d.ts +2 -0
  21. package/payload/platform/services/claude-session-manager/dist/jsonl-path.d.ts.map +1 -1
  22. package/payload/platform/services/claude-session-manager/dist/jsonl-path.js +11 -6
  23. package/payload/platform/services/claude-session-manager/dist/jsonl-path.js.map +1 -1
  24. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +37 -44
  25. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
  26. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +136 -159
  27. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
  28. package/payload/platform/services/claude-session-manager/dist/reaper.d.ts +24 -0
  29. package/payload/platform/services/claude-session-manager/dist/reaper.d.ts.map +1 -0
  30. package/payload/platform/services/claude-session-manager/dist/reaper.js +114 -0
  31. package/payload/platform/services/claude-session-manager/dist/reaper.js.map +1 -0
  32. package/payload/platform/services/claude-session-manager/dist/session-store.d.ts +16 -41
  33. package/payload/platform/services/claude-session-manager/dist/session-store.d.ts.map +1 -1
  34. package/payload/platform/services/claude-session-manager/dist/session-store.js +29 -68
  35. package/payload/platform/services/claude-session-manager/dist/session-store.js.map +1 -1
  36. package/payload/premium-plugins/real-agent/lib/mcp-stderr-tee/dist/index.d.ts +51 -0
  37. package/payload/premium-plugins/real-agent/lib/mcp-stderr-tee/dist/index.d.ts.map +1 -0
  38. package/payload/premium-plugins/real-agent/lib/mcp-stderr-tee/dist/index.js +196 -0
  39. package/payload/premium-plugins/real-agent/lib/mcp-stderr-tee/dist/index.js.map +1 -0
  40. package/payload/premium-plugins/real-agent/lib/mcp-stderr-tee/package.json +7 -0
  41. package/payload/server/public/assets/{Checkbox-6lLDKzMP.js → Checkbox-blxv2XPY.js} +1 -1
  42. package/payload/server/public/assets/{admin-DO9W2D9g.js → admin-C3EvHpLN.js} +1 -1
  43. package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-BbLtk_k6.js → architectureDiagram-Q4EWVU46-bETsVzGS.js} +1 -1
  44. package/payload/server/public/assets/{blockDiagram-DXYQGD6D-B9IgcgeJ.js → blockDiagram-DXYQGD6D-5y0GBnfQ.js} +1 -1
  45. package/payload/server/public/assets/{brand-DQrOw38P.css → brand-DQlLKLDH.css} +1 -1
  46. package/payload/server/public/assets/{c4Diagram-AHTNJAMY-BrvPS09f.js → c4Diagram-AHTNJAMY-C1znpVGu.js} +1 -1
  47. package/payload/server/public/assets/channel-BWqTaXzb.js +1 -0
  48. package/payload/server/public/assets/{chunk-336JU56O-DXOr0ZnC.js → chunk-336JU56O-DlCVUGzQ.js} +2 -2
  49. package/payload/server/public/assets/{chunk-426QAEUC-BFW4zwrV.js → chunk-426QAEUC-bGYkmA4z.js} +1 -1
  50. package/payload/server/public/assets/{chunk-4TB4RGXK-D7_xc8xf.js → chunk-4TB4RGXK-6Sb0Undm.js} +1 -1
  51. package/payload/server/public/assets/{chunk-5FUZZQ4R-CdCSpb8j.js → chunk-5FUZZQ4R-CAT_Pq2T.js} +1 -1
  52. package/payload/server/public/assets/{chunk-5PVQY5BW-BVgnD9QR.js → chunk-5PVQY5BW-j231ESse.js} +1 -1
  53. package/payload/server/public/assets/{chunk-EDXVE4YY-CYrT1Tom.js → chunk-EDXVE4YY-BAlS4QL7.js} +1 -1
  54. package/payload/server/public/assets/{chunk-ENJZ2VHE-B70d0jN7.js → chunk-ENJZ2VHE-D3Ku_BMP.js} +1 -1
  55. package/payload/server/public/assets/{chunk-ICPOFSXX-BcLouRbd.js → chunk-ICPOFSXX-AR86A5WB.js} +1 -1
  56. package/payload/server/public/assets/{chunk-OYMX7WX6-C2ja2fPj.js → chunk-OYMX7WX6-DxGhapkW.js} +1 -1
  57. package/payload/server/public/assets/{chunk-U2HBQHQK-CCjoTpL6.js → chunk-U2HBQHQK-DAlqsDo_.js} +1 -1
  58. package/payload/server/public/assets/{chunk-X2U36JSP-QIVkp-XR.js → chunk-X2U36JSP-lZdkkdyB.js} +1 -1
  59. package/payload/server/public/assets/{chunk-YZCP3GAM-qD9rebhx.js → chunk-YZCP3GAM-CCjtXOFD.js} +1 -1
  60. package/payload/server/public/assets/{chunk-ZZ45TVLE-C0J_f05H.js → chunk-ZZ45TVLE-ComtdKny.js} +1 -1
  61. package/payload/server/public/assets/classDiagram-6PBFFD2Q-B9hC6fOR.js +1 -0
  62. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-afl_ydIG.js +1 -0
  63. package/payload/server/public/assets/clone-Lj83OaZ3.js +1 -0
  64. package/payload/server/public/assets/{dagre-B3cA7jId.js → dagre-DX8QJeVt.js} +1 -1
  65. package/payload/server/public/assets/{dagre-KV5264BT-C8lVFO5U.js → dagre-KV5264BT-BcRbiZ1q.js} +1 -1
  66. package/payload/server/public/assets/data-CS_R5VWQ.js +1 -0
  67. package/payload/server/public/assets/{device-url-actions-qu2-XiHc.js → device-url-actions-Cag9IJ7n.js} +1 -1
  68. package/payload/server/public/assets/{diagram-5BDNPKRD-BYkcTelK.js → diagram-5BDNPKRD-Bw47VwHO.js} +1 -1
  69. package/payload/server/public/assets/{diagram-G4DWMVQ6-B6f_WR4j.js → diagram-G4DWMVQ6-B-aVfhiD.js} +1 -1
  70. package/payload/server/public/assets/{diagram-MMDJMWI5-ALaKcN6w.js → diagram-MMDJMWI5-DERz2dKo.js} +1 -1
  71. package/payload/server/public/assets/{diagram-TYMM5635-CkVg3crF.js → diagram-TYMM5635-B7eEX9Ta.js} +1 -1
  72. package/payload/server/public/assets/{erDiagram-SMLLAGMA-DszlTGIQ.js → erDiagram-SMLLAGMA-G_JJq3zB.js} +1 -1
  73. package/payload/server/public/assets/{flowDiagram-DWJPFMVM-C9-FqSlk.js → flowDiagram-DWJPFMVM-BeY9QaO0.js} +1 -1
  74. package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-faJU-5Zy.js → ganttDiagram-T4ZO3ILL-IXviGYnx.js} +1 -1
  75. package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-Cck8_tiG.js → gitGraphDiagram-UUTBAWPF-GlY6pZQ1.js} +1 -1
  76. package/payload/server/public/assets/graph-DtGkQBFr.js +1 -0
  77. package/payload/server/public/assets/graph-labels-Dwu1ON5v.js +1 -0
  78. package/payload/server/public/assets/{graphlib-CYRxeBXj.js → graphlib-BWgAjZSb.js} +1 -1
  79. package/payload/server/public/assets/{infoDiagram-42DDH7IO-CLjEjhJX.js → infoDiagram-42DDH7IO-D4YZM0gD.js} +1 -1
  80. package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-nSIaVXdw.js → ishikawaDiagram-UXIWVN3A-fR71GDOp.js} +1 -1
  81. package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-DuKq7d3g.js → journeyDiagram-VCZTEJTY-C1TcY_6m.js} +1 -1
  82. package/payload/server/public/assets/{kanban-definition-6JOO6SKY-Bt5xqHfY.js → kanban-definition-6JOO6SKY-IPZKpnnG.js} +1 -1
  83. package/payload/server/public/assets/{line-BgV8qCP_.js → line-B2JtEL1B.js} +1 -1
  84. package/payload/server/public/assets/{mermaid-parser.core-DQzl__pY.js → mermaid-parser.core-BKCLXqUh.js} +1 -1
  85. package/payload/server/public/assets/{mermaid.core-BAPEOjwh.js → mermaid.core-CmNpCLB-.js} +3 -3
  86. package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-CmMBrI79.js → mindmap-definition-QFDTVHPH-BS0E2FPD.js} +1 -1
  87. package/payload/server/public/assets/{page-BoDdtGsH.js → page-BdI3GCun.js} +1 -1
  88. package/payload/server/public/assets/{page-D_NCNi5j.js → page-zIjAkU2y.js} +1 -1
  89. package/payload/server/public/assets/{pieDiagram-DEJITSTG-BhsR9k6j.js → pieDiagram-DEJITSTG-J61JDyk8.js} +1 -1
  90. package/payload/server/public/assets/{public-DCcGKIMW.js → public-C5vaQnC_.js} +3 -3
  91. package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-DSKV6d_A.js → quadrantDiagram-34T5L4WZ-CLI03OtR.js} +1 -1
  92. package/payload/server/public/assets/{requirementDiagram-MS252O5E-BccM48AI.js → requirementDiagram-MS252O5E-C8MCt5NO.js} +1 -1
  93. package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-C0pxiQfn.js → sankeyDiagram-XADWPNL6-DQuugruj.js} +1 -1
  94. package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-BVsUIJsW.js → sequenceDiagram-FGHM5R23-CoYitwhO.js} +1 -1
  95. package/payload/server/public/assets/{stateDiagram-FHFEXIEX-lqYQZ-F1.js → stateDiagram-FHFEXIEX-G68cHHoI.js} +1 -1
  96. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-Dtgpgr_D.js +1 -0
  97. package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-D-LxYOHq.js → timeline-definition-GMOUNBTQ-CelLeJVS.js} +1 -1
  98. package/payload/server/public/assets/{vennDiagram-DHZGUBPP-BNHzvNba.js → vennDiagram-DHZGUBPP-BVgFPkJq.js} +1 -1
  99. package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-BfXR9l5s.js → wardleyDiagram-NUSXRM2D-CzD_8hl2.js} +1 -1
  100. package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-CL7mVEbT.js → xychartDiagram-5P7HB3ND-BuTxWdX5.js} +1 -1
  101. package/payload/server/public/data.html +5 -5
  102. package/payload/server/public/graph.html +6 -6
  103. package/payload/server/public/index.html +8 -8
  104. package/payload/server/public/public.html +5 -5
  105. package/payload/server/server.js +94 -4
  106. package/payload/server/public/assets/channel-Bmc1qlCD.js +0 -1
  107. package/payload/server/public/assets/classDiagram-6PBFFD2Q-RVZorqSk.js +0 -1
  108. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-BEZem5bl.js +0 -1
  109. package/payload/server/public/assets/clone-B6eXIhiR.js +0 -1
  110. package/payload/server/public/assets/data-BaSmBQ7U.js +0 -1
  111. package/payload/server/public/assets/graph-labels-COhLbJD0.js +0 -1
  112. package/payload/server/public/assets/graph-qg6fkRyC.js +0 -1
  113. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-D2eHThhC.js +0 -1
  114. /package/payload/server/public/assets/{brand-Bky-hMVw.js → brand-D6mXeyxV.js} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/create-maxy-code",
3
- "version": "0.1.40",
3
+ "version": "0.1.41",
4
4
  "description": "Install Maxy — AI for Productive People",
5
5
  "bin": {
6
6
  "create-maxy-code": "./dist/index.js"
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "@maxy/mcp-stderr-tee",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "commonjs",
6
+ "main": "dist/index.js"
7
+ }
@@ -1,37 +1,71 @@
1
1
  #!/usr/bin/env bash
2
2
  # Stop hook — fires on every completed admin-agent turn and dispatches one
3
- # background PTY against `specialists:database-operator` with the just-
4
- # completed turn payload as stdin. The hidden PTY is the only writer to the
5
- # Neo4j graph; the admin agent stays focused on the operator's request.
3
+ # background PTY against `specialists:database-operator`. The hidden PTY is
4
+ # the only writer to the Neo4j graph; the admin agent stays focused on the
5
+ # operator's request.
6
+ #
7
+ # Pipeline (Task 094 redesign):
8
+ # 1. /spawn (no stdinPayload) — manager awaits its watcher's PID-file
9
+ # event before returning. Response carries the recorder's intrinsic
10
+ # sessionId.
11
+ # 2. POST /<recorder-sessionId>/input — body is the database-operator
12
+ # prompt followed by the filtered JSONL tail of the just-completed
13
+ # turn (user / assistant / tool-call records only; queue-operation,
14
+ # auto_mode, deferred_tools_delta, hook_*, last-prompt, skill_listing,
15
+ # mcp_instructions_delta, permission-mode are dropped).
16
+ # 3. When the recorder's Stop hook fires, this hook runs again INSIDE
17
+ # the recorder PTY; the MAXY_HIDDEN_SPAWN gate exits early so no
18
+ # recursion. The recorder's PTY exits naturally; its Stop is then
19
+ # driven by POST /<sessionId>/stop from the same hook invocation
20
+ # that posted /input.
6
21
  #
7
22
  # Gating:
8
- # - MAXY_SESSION_ROLE must equal "admin" (skip public PTYs)
9
- # - MAXY_HIDDEN_SPAWN must equal "0" (skip recursive fires from the
10
- # hidden database-operator PTY itself; without this gate every
11
- # recorder turn would spawn another recorder forever)
23
+ # - MAXY_SESSION_ROLE must equal "admin"
24
+ # - MAXY_HIDDEN_SPAWN must equal "0" (skip recursive fires)
12
25
  #
13
26
  # Input: Claude Code's Stop hook stdin shape
14
27
  # { "session_id": "<intrinsic>", "transcript_path": "<jsonl path>", ... }
15
28
  #
16
- # Output: one log line per fire on the user-facing session's stderr
17
- # `turn-completed-graph-write fired conversationId=<8> tail-bytes=<n> ms=<n>`
18
- #
19
- # Failure modes are silent (exit 0) — this hook is observability + write
20
- # orchestration; a failure here must never break the operator's session.
29
+ # Failure modes are silent (exit 0) a failure here must never break the
30
+ # operator's session.
21
31
 
22
32
  set -uo pipefail
23
33
 
24
34
  START_MS=$(python3 -c 'import time; print(int(time.time()*1000))')
25
35
 
26
- # Gate 1: only fire on admin foreground PTYs.
27
36
  if [ "${MAXY_SESSION_ROLE:-}" != "admin" ]; then
28
37
  exit 0
29
38
  fi
30
39
  if [ "${MAXY_HIDDEN_SPAWN:-0}" = "1" ]; then
40
+ # Recorder PTY's own Stop hook. Read its session_id and ask the manager
41
+ # to SIGTERM the recorder so it does not idle in the active recents list
42
+ # after its single turn completes. Keep the JSONL — it's the audit trail.
43
+ if [ -t 0 ]; then
44
+ exit 0
45
+ fi
46
+ REC_INPUT=$(cat)
47
+ if [ -z "$REC_INPUT" ]; then
48
+ exit 0
49
+ fi
50
+ REC_SESSION_ID=$(printf '%s' "$REC_INPUT" | python3 -c '
51
+ import sys, json
52
+ try:
53
+ d = json.load(sys.stdin)
54
+ print(d.get("session_id", "") or "")
55
+ except Exception:
56
+ print("")
57
+ ' 2>/dev/null)
58
+ if [ -z "$REC_SESSION_ID" ]; then
59
+ exit 0
60
+ fi
61
+ REC_PORT="${CLAUDE_SESSION_MANAGER_PORT:-19400}"
62
+ curl -sS -X POST \
63
+ --max-time 5 \
64
+ "http://127.0.0.1:${REC_PORT}/${REC_SESSION_ID}/stop" >/dev/null 2>&1
65
+ echo "[recorder] self-stop sessionId=${REC_SESSION_ID:0:8}" >&2
31
66
  exit 0
32
67
  fi
33
68
 
34
- # Gate 2: stdin must be a JSON envelope. Fail-open on parse failure.
35
69
  if [ -t 0 ]; then
36
70
  exit 0
37
71
  fi
@@ -40,8 +74,6 @@ if [ -z "$INPUT" ]; then
40
74
  exit 0
41
75
  fi
42
76
 
43
- # Extract session_id and transcript_path via python3 (grep on JSON is
44
- # fragile when values carry escaped quotes).
45
77
  PARSED=$(printf '%s' "$INPUT" | python3 -c '
46
78
  import sys, json
47
79
  try:
@@ -52,63 +84,121 @@ try:
52
84
  except Exception:
53
85
  print("\t")
54
86
  ' 2>/dev/null)
55
- SESSION_ID="${PARSED%% *}"
87
+ ADMIN_SESSION_ID="${PARSED%% *}"
56
88
  TRANSCRIPT_PATH="${PARSED#* }"
57
89
 
58
- if [ -z "$SESSION_ID" ] || [ -z "$TRANSCRIPT_PATH" ] || [ ! -f "$TRANSCRIPT_PATH" ]; then
90
+ if [ -z "$ADMIN_SESSION_ID" ] || [ -z "$TRANSCRIPT_PATH" ] || [ ! -f "$TRANSCRIPT_PATH" ]; then
59
91
  exit 0
60
92
  fi
61
93
 
62
- # Read last 64 KiB of the transcript enough for one completed turn
63
- # including a moderate tool transcript, bounded so the POST body never
64
- # balloons. tail -c is portable across BSD and GNU.
65
- TAIL_BYTES=$(wc -c < "$TRANSCRIPT_PATH" 2>/dev/null | tr -d ' ')
66
- TAIL_BYTES="${TAIL_BYTES:-0}"
67
- TURN_TAIL=$(tail -c 65536 "$TRANSCRIPT_PATH" 2>/dev/null)
94
+ # Filter the JSONL tail down to records the database-operator cares about:
95
+ # user prompts, assistant messages, and tool calls. Drop ephemeral
96
+ # admin-state records that bloat the prompt without graph value.
97
+ FILTERED_TAIL=$(python3 - "$TRANSCRIPT_PATH" <<'PY'
98
+ import sys, json
99
+ DROP = {
100
+ 'queue-operation', 'auto_mode', 'deferred_tools_delta',
101
+ 'last-prompt', 'skill_listing', 'mcp_instructions_delta',
102
+ 'permission-mode',
103
+ }
104
+ def keep(rec):
105
+ t = rec.get('type') if isinstance(rec, dict) else None
106
+ if not isinstance(t, str): return False
107
+ if t in DROP: return False
108
+ if t.startswith('hook_'): return False
109
+ if t in ('user', 'assistant', 'message'): return True
110
+ if 'tool' in t.lower(): return True
111
+ return False
112
+
113
+ path = sys.argv[1]
114
+ # Read whole file; admin transcripts are bounded by Claude Code's own
115
+ # compaction so the read cost is reasonable. Take the last 200 records
116
+ # from the kept set so a long transcript doesn't blow the prompt budget.
117
+ out = []
118
+ try:
119
+ with open(path, 'r', encoding='utf-8') as f:
120
+ for line in f:
121
+ line = line.strip()
122
+ if not line: continue
123
+ try: rec = json.loads(line)
124
+ except Exception: continue
125
+ if keep(rec): out.append(rec)
126
+ except Exception:
127
+ pass
128
+ out = out[-200:]
129
+ print(json.dumps(out, separators=(',', ':')))
130
+ PY
131
+ )
132
+
133
+ FILTERED_COUNT=$(printf '%s' "$FILTERED_TAIL" | python3 -c 'import sys,json; d=json.load(sys.stdin); print(len(d) if isinstance(d,list) else 0)' 2>/dev/null || echo 0)
68
134
 
69
- # Manager port — environment override or compile-time default. The
70
- # installer's systemd unit stamps CLAUDE_SESSION_MANAGER_PORT so this
71
- # resolves to the brand's loopback port at runtime.
72
135
  MANAGER_PORT="${CLAUDE_SESSION_MANAGER_PORT:-19400}"
136
+ MANAGER_BASE="http://127.0.0.1:${MANAGER_PORT}"
73
137
 
74
- # Build the /spawn body. The recorder gets the raw transcript tail in
75
- # stdinPayload; the recorder agent parses the JSONL records itself
76
- # (admin-agent JSONL is more authoritative than any post-hoc summary the
77
- # hook could synthesise).
138
+ # Spawn the recorder PTY. Manager blocks on its watcher's PID-file event
139
+ # and returns the recorder's intrinsic sessionId in the response body.
78
140
  SPAWN_BODY=$(python3 -c '
79
- import sys, json
80
- sid = sys.argv[1]
81
- tail = sys.argv[2]
141
+ import json, sys
82
142
  body = {
83
- "senderId": "turn-recorder",
84
- "role": "admin",
85
- "channel": "browser",
86
- "hidden": True,
87
- "specialist": "database-operator",
88
- "stdinPayload": json.dumps({
89
- "conversationId": sid,
90
- "occurredAt": __import__("datetime").datetime.utcnow().isoformat() + "Z",
91
- "transcriptTail": tail,
92
- }),
143
+ "senderId": "turn-recorder",
144
+ "role": "admin",
145
+ "channel": "browser",
146
+ "hidden": True,
147
+ "specialist": "database-operator",
93
148
  }
94
149
  print(json.dumps(body))
95
- ' "$SESSION_ID" "$TURN_TAIL" 2>/dev/null)
150
+ ')
96
151
 
97
- if [ -z "$SPAWN_BODY" ]; then
152
+ SPAWN_RES=$(curl -sS -X POST \
153
+ -H 'Content-Type: application/json' \
154
+ --max-time 10 \
155
+ --data "$SPAWN_BODY" \
156
+ "${MANAGER_BASE}/spawn" 2>/dev/null)
157
+
158
+ RECORDER_SESSION_ID=$(printf '%s' "$SPAWN_RES" | python3 -c '
159
+ import sys, json
160
+ try:
161
+ d = json.load(sys.stdin)
162
+ print(d.get("sessionId", "") or "")
163
+ except Exception:
164
+ print("")
165
+ ' 2>/dev/null)
166
+
167
+ if [ -z "$RECORDER_SESSION_ID" ]; then
168
+ echo "turn-completed-graph-write spawn-failed conversationId=${ADMIN_SESSION_ID:0:8}" >&2
98
169
  exit 0
99
170
  fi
100
171
 
101
- # Fire-and-forget POST. The manager returns 200 on URL capture (~2 s);
102
- # we do not wait for it. curl is preferred (universally present on
103
- # Linux/macOS) — failure is silent.
172
+ # Compose the input message: prompt prefix + filtered transcript JSON.
173
+ INPUT_BODY=$(python3 -c '
174
+ import sys, json
175
+ sid = sys.argv[1]
176
+ tail_json = sys.argv[2]
177
+ prompt = (
178
+ "You are the database-operator specialist. Record the following completed turn "
179
+ "into the Neo4j graph. The transcript is a JSON array of filtered records "
180
+ "from the admin agent (user prompts, assistant responses, tool calls). "
181
+ f"Conversation id: {sid}.\n\nTranscript:\n{tail_json}\n"
182
+ )
183
+ print(json.dumps({"text": prompt}))
184
+ ' "$ADMIN_SESSION_ID" "$FILTERED_TAIL" 2>/dev/null)
185
+
186
+ if [ -z "$INPUT_BODY" ]; then
187
+ exit 0
188
+ fi
189
+
190
+ INPUT_BYTES=$(printf '%s' "$INPUT_BODY" | wc -c | tr -d ' ')
191
+
104
192
  curl -sS -X POST \
105
193
  -H 'Content-Type: application/json' \
106
194
  --max-time 5 \
107
- --data "$SPAWN_BODY" \
108
- "http://127.0.0.1:${MANAGER_PORT}/spawn" >/dev/null 2>&1 &
195
+ --data "$INPUT_BODY" \
196
+ "${MANAGER_BASE}/${RECORDER_SESSION_ID}/input" >/dev/null 2>&1
197
+
198
+ echo "[recorder] input-posted sessionId=${RECORDER_SESSION_ID:0:8} bytes=${INPUT_BYTES} filtered-from-records=${FILTERED_COUNT}" >&2
109
199
 
110
200
  END_MS=$(python3 -c 'import time; print(int(time.time()*1000))')
111
201
  ELAPSED=$((END_MS - START_MS))
112
- echo "turn-completed-graph-write fired conversationId=${SESSION_ID:0:8} tail-bytes=${TAIL_BYTES} ms=${ELAPSED}" >&2
202
+ echo "turn-completed-graph-write fired conversationId=${ADMIN_SESSION_ID:0:8} recorderSessionId=${RECORDER_SESSION_ID:0:8} filtered=${FILTERED_COUNT} ms=${ELAPSED}" >&2
113
203
 
114
204
  exit 0
@@ -70,15 +70,17 @@ There is no dashboard, no settings panel, no menus. Everything is done through c
70
70
 
71
71
  The chat input auto-grows as you type — it expands to fit your message and shrinks back when you delete text. You can also drag the resize handle above the input to set a custom height.
72
72
 
73
- The admin interface is a three-pane layout: a sidebar on the left with navigation (Sessions, People, Agents, Projects, Tasks, Artefacts) and your recent conversations; the chat in the middle; and an artefact pane on the right that opens when you select a document, click a project, or open Browser, Data, or Graph from the menu, holding the surface side-by-side with the conversation so the chat stays live while you work in it. At the very top of the sidebar — above the nav rows — a borderless row holds two controls: a "+ New session" button on the left that spawns a fresh Claude Code session, and a Mode trigger on the right showing the current permission mode (Ask, Accept edits, Plan, or Auto). The sidebar's vertical order is: new-session strip first, then the nav (Sessions, People, Agents, Projects, Tasks, Artefacts), then the sessions list, then the footer. Both controls render as plain text-plus-icon affordances with no surrounding rectangle. The "+ New session" button is a text-width hit target — its clickable area is exactly the icon plus label, not the whole row — and shows no hover fill; the only hover feedback is the pointer cursor. The Mode trigger is pushed flush to the right edge of the row. Clicking the Mode trigger opens a popover downward from the row whose header reads "Mode" and lists the four permission modes with the current selection check-marked. The sidebar's nav rows swap the list view in place: Sessions shows recent conversations, Projects shows your active work projects, and Artefacts lists every KnowledgeDocument plus this account's agent templates (your admin agent's IDENTITY, SOUL, and KNOWLEDGE files plus one entry per enabled specialist). Each recent session row carries a small status dot (filled sage for alive, hollow circle for ended) so you can read the live state at a glance without parsing the subtitle. The People, Agents, and Tasks rows are graph shortcuts: clicking each opens the artefact-pane Graph filtered to every Person, every public Agent, or every Task in your account respectively, with no side-list, because the graph itself is the result. Public agents become first-class graph entities the moment you create them, with edges to their IDENTITY/SOUL/KNOWLEDGE files, edges to every knowledge document they have access to, and edges from every conversation they have handled, so a single Agents click reveals the whole shape of who knows what and who has been talking to whom. Click an artefact row to open the document. KnowledgeDocuments and your admin agent's templates are editable: type in the document and changes save automatically; specialist agent templates are read-only because they ship with Maxy and your edits would be overwritten on the next install. PDF artefacts render inline so you can read them without leaving the pane. If your browser doesn't have a built-in PDF viewer, a Download button appears instead. Artefacts that have no readable file backing them (orphan rows, files removed from disk, unsupported content types) show a one-line banner explaining the skip instead of opening to a blank pane. Click a project row to open the Graph view focused on that project's neighbourhood; clicking a second project swaps the focus rather than stacking on top. The chat and artefact divider is drag-resizable: drag the line between the columns to make either side wider; double-click it to reset to half of the available width (viewport minus sidebar), clamped to the chat and artefact min-width floors. Your chosen width is remembered across reloads. On wider screens (>1280px) all three panes are visible. The sidebar narrows at 1280px, the artefact pane hides at 1080px (Browser, Data, and Graph then open as full-window pages instead), and the sidebar collapses to a 56px icon rail at 820px. On every viewport the chat header reads left to right as a triptych: a dedicated sidebar toggle (the panel-right icon, which swaps to panel-right-open when the sidebar is showing), the brand mark next to the title in the centre, and the menu burger on the right. This header toggle is the sole sidebar-toggle button; the sidebar itself no longer carries a duplicate. Tap the sidebar toggle to show or hide the sidebar: on phones (<720px) it slides the drawer in or out, on wider screens it collapses or expands the sidebar column. The brand mark in the centre is decorative; clicks go through the dedicated toggle so the affordance is unambiguous. The drawer animation only fires on tap (220ms slide in or out); resizing your window across the 720px boundary snaps the layout without animation, so you never see a half-open flash. At ≤640px the session metadata pane stacks each label above its value instead of the desktop two-column grid, and the row of action buttons (Open in new tab / Open JSONL / Rename / Pin / Archive / End or Purge) collapses behind a single Actions trigger that opens a popover upward from the foot of the pane. Breakpoint summary: >1280px = full sidebar + chat + artefact pane (drag-resizable divider); 1280px→1080px = sidebar narrows; 1080px→820px = artefact pane hides (Browser/Data/Graph open as full-window pages instead); 820px→720px = sidebar collapses to 56px icon rail; ≤720px = sidebar becomes off-canvas drawer (vertical stack of nav, recents list, foot, the same shape as the desktop sidebar, just on top of the chat instead of beside it).
73
+ The admin interface is a three-pane layout: a sidebar on the left with navigation (Sessions, People, Agents, Projects, Tasks, Artefacts) and your recent conversations; the chat in the middle; and an artefact pane on the right that opens when you select a document, click a project, or open Browser, Data, or Graph from the menu, holding the surface side-by-side with the conversation so the chat stays live while you work in it. At the very top of the sidebar — above the nav rows — a borderless row holds two controls: a "+ New session" button on the left that spawns a fresh Claude Code session, and a Mode trigger on the right showing the current permission mode (Ask, Accept edits, Plan, or Auto). The sidebar's vertical order is: new-session strip first, then the nav (Sessions, People, Agents, Projects, Tasks, Artefacts), then the sessions list, then the footer. Both controls render as plain text-plus-icon affordances with no surrounding rectangle. The "+ New session" button is a text-width hit target — its clickable area is exactly the icon plus label, not the whole row — and shows no hover fill; the only hover feedback is the pointer cursor. The Mode trigger is pushed flush to the right edge of the row. Clicking the Mode trigger opens a popover downward from the row whose header reads "Mode" and lists the four permission modes with the current selection check-marked. The sidebar's nav rows swap the list view in place: Sessions shows recent conversations, Projects shows your active work projects, and Artefacts lists every KnowledgeDocument plus this account's agent templates (your admin agent's IDENTITY, SOUL, and KNOWLEDGE files plus one entry per enabled specialist). Each recent session row carries a three-state indicator: three pulsing dots when the session is busy (currently processing a turn), a solid sage dot when it is idle (live PTY waiting for input), and a hollow ring when it is archived (PTY exited, JSONL on disk for audit). The list itself splits into three views via a segmented control above the rows: **Active** shows every live session, **Archived** shows every JSONL on disk whose PTY has exited, and **All** shows both. The view choice persists across reloads. An "Include background" toggle inside the Active view surfaces specialist spawns (the database-operator recorder, premium-plugin agents, anything spawned with a `--agent` flag) which are hidden by default so the list reflects what you started directly. Each row also carries a small uppercase badge — `admin` for operator-driven sessions, the specialist name (for example `db-op`) for background work — so the source of any row is unambiguous at a glance. The People, Agents, and Tasks rows are graph shortcuts: clicking each opens the artefact-pane Graph filtered to every Person, every public Agent, or every Task in your account respectively, with no side-list, because the graph itself is the result. Public agents become first-class graph entities the moment you create them, with edges to their IDENTITY/SOUL/KNOWLEDGE files, edges to every knowledge document they have access to, and edges from every conversation they have handled, so a single Agents click reveals the whole shape of who knows what and who has been talking to whom. Click an artefact row to open the document. KnowledgeDocuments and your admin agent's templates are editable: type in the document and changes save automatically; specialist agent templates are read-only because they ship with Maxy and your edits would be overwritten on the next install. PDF artefacts render inline so you can read them without leaving the pane. If your browser doesn't have a built-in PDF viewer, a Download button appears instead. Artefacts that have no readable file backing them (orphan rows, files removed from disk, unsupported content types) show a one-line banner explaining the skip instead of opening to a blank pane. Click a project row to open the Graph view focused on that project's neighbourhood; clicking a second project swaps the focus rather than stacking on top. The chat and artefact divider is drag-resizable: drag the line between the columns to make either side wider; double-click it to reset to half of the available width (viewport minus sidebar), clamped to the chat and artefact min-width floors. Your chosen width is remembered across reloads. On wider screens (>1280px) all three panes are visible. The sidebar narrows at 1280px, the artefact pane hides at 1080px (Browser, Data, and Graph then open as full-window pages instead), and the sidebar collapses to a 56px icon rail at 820px. On every viewport the chat header reads left to right as a triptych: a dedicated sidebar toggle (the panel-right icon, which swaps to panel-right-open when the sidebar is showing), the brand mark next to the title in the centre, and the menu burger on the right. This header toggle is the sole sidebar-toggle button; the sidebar itself no longer carries a duplicate. Tap the sidebar toggle to show or hide the sidebar: on phones (<720px) it slides the drawer in or out, on wider screens it collapses or expands the sidebar column. The brand mark in the centre is decorative; clicks go through the dedicated toggle so the affordance is unambiguous. The drawer animation only fires on tap (220ms slide in or out); resizing your window across the 720px boundary snaps the layout without animation, so you never see a half-open flash. At ≤640px the session metadata pane stacks each label above its value instead of the desktop two-column grid, and the row of action buttons (Open in new tab / Open JSONL / Rename / Pin / Archive / End or Purge) collapses behind a single Actions trigger that opens a popover upward from the foot of the pane. Breakpoint summary: >1280px = full sidebar + chat + artefact pane (drag-resizable divider); 1280px→1080px = sidebar narrows; 1080px→820px = artefact pane hides (Browser/Data/Graph open as full-window pages instead); 820px→720px = sidebar collapses to 56px icon rail; ≤720px = sidebar becomes off-canvas drawer (vertical stack of nav, recents list, foot, the same shape as the desktop sidebar, just on top of the chat instead of beside it).
74
74
 
75
75
  Page titles are brand-aware: the browser tab shows your product name (e.g. `Real Agent` instead of `Maxy`) on every shell — chat, graph, and data — so a non-default brand never leaks the default name in tab strips or browser history.
76
76
 
77
- **Session lifecycle and reconcile model.** The sidebar Sessions list and the ConversationsModal share one fetch loop (`useAdminSessions`). Every fetch is operator-triggered: first load on mount (`ensureLoaded`), the refresh button on the sessions group head, and a single `refetch` appended to every mutation in `session-actions` (resume, end, purge, archive, unarchive). There is no idle ticker; the UI does not call `/api/admin/claude-sessions` between operator actions, so the DevTools console stays quiet while you are not clicking anything. Each fetch emits exactly one line: `[admin-ui] sessions-poll outcome=<unchanged|changed|error> count=<n> cacheKey=<8>`. The format is held from the prior polling shape so existing log greps still match; the rate just drops to one per user action. Byte-identical responses skip the `setSessions` commit (projection-equality gate) so AdminShell does not re-render when the list has not changed.
77
+ **Session lifecycle and reconcile model.** The sidebar Sessions list is driven by a single Server-Sent Events feed at `/api/admin/claude-sessions/events`. The session manager watches the two directories Claude Code writes (`${CLAUDE_CONFIG_DIR}/sessions/<pid>.json` for live state, `${CLAUDE_CONFIG_DIR}/projects/<slug>/<sid>.jsonl` for transcripts) and emits `row-created`, `row-updated`, `row-archived`, or `row-removed` deltas to every connected browser tab. On connect the manager replays the current row index so a freshly-opened tab catches up without polling, then streams deltas as files change on disk. Two open tabs see the same list within ~300ms of any spawn, status flip, or exit; no refresh button required for state to be current. The legacy `/list` fetch and `useAdminSessions` hook stay mounted to serve the ConversationsModal and the post-action reconcile path in `session-actions`, but the sidebar's visible rows come from the row store, not from `/list`. Each EventSource open emits `[admin-events] client-connected ip=<…> seeded-rows=<n>` server-side and `[admin-ui] session-row-store connected events-received=<n>` in the browser console; transport drops log `[admin-ui] session-row-store reconnect attempt=<n>` until the EventSource reattaches.
78
78
 
79
- The trade-off is staleness for PTY exits that happen outside this UI: a Claude Code crash, an `ssh` kill of the brand process, or a sibling tab spawning a new session stays invisible in the current tab until you click the refresh button or perform another mutation. Two open tabs can show different lists until both refresh. The accepted view is that every lifecycle event in this dashboard originates from a click in this dashboard, so a missing ticker cannot deliver information the operator did not already cause.
79
+ The trade-off is a longer-lived connection per tab: the manager's per-process subscriber count rises with open tabs, and the SSE channel must survive proxy idle timeouts. The manager emits a 25-second keep-alive comment line on every connection (ignored by EventSource consumers, refreshes the proxy clock) and the browser-side store force-closes-and-reconnects on transport errors with exponential backoff capped at 30s.
80
80
 
81
- **Spawn lifecycle: optimistic, then reconcile.** Clicking "+ New session" returns a usable row inside a couple of seconds, even when you don't immediately type. The manager spawns the PTY, captures the `/remote-control` URL, registers the row under the URL's bridge suffix, and returns 200; the row is visible in the sidebar at that point with `claudeSessionId: null`. Claude Code writes its per-session JSONL transcript only when you submit the first turn (true on 2.1.143 and 2.1.128), so the intrinsic session id (the JSONL basename) is null until then. When you type, the manager observes the JSONL appearing on disk and promotes the row to the intrinsic id without changing its sidebar position; subsequent route calls accept either the bridge suffix or the intrinsic id and resolve to the same row. If you close an idle session without typing, the row leaves the list cleanly and no JSONL is created. There is no 5-second timeout that kills an otherwise-healthy claude operator typing latency is unbounded by design.
81
+ **Spawn lifecycle: PID-file driven.** Clicking "+ New session" spawns the PTY and waits for Claude Code's PID file to appear at `${CLAUDE_CONFIG_DIR}/sessions/<pid>.json`. The PID file lands at process init (for `entrypoint: cli` spawns) and carries the intrinsic `sessionId`, `bridgeSessionId`, `agent`, and `status` directly. The manager's filesystem watcher reports the create event; the spawn response includes the canonical `sessionId` from that file. URL capture still runs in parallel to populate the operator-facing iframe URL, but it no longer gates readiness. The JSONL transcript is written on the first operator turn (true on 2.1.143 and 2.1.128); the watcher fires a separate event for that, and `/list`, `/meta`, `/log` resolve any of four ids `sessionId`, `bridgeSessionId`, `bridgeSuffix`, or numeric `pid`to the same row.
82
+
83
+ **Stop vs. delete.** `POST /<id>/stop` sends SIGTERM, leaves the JSONL on disk for audit, and is idempotent against an already-dead row. `DELETE /<id>` removes the JSONL + sidecar + per-session subdir and returns 409 if the PTY is still alive (stop first). Any unknown id returns 404; nothing returns a silent 204 against an id the manager does not know.
82
84
 
83
85
  The metadata pane subscribes to the same /list projection. When an operator clicks End on an alive row, the DELETE returns 200 and the post-mutation refetch decides what happens next: a session that wrote a JSONL surfaces as a dehydrated `status: 'ended'` row (the pane swaps `End session` for `Purge JSONL` plus `Resume`), and a session that never wrote a JSONL (`Turns: 0`) leaves the list entirely (the pane shows a `Session ended without a transcript. Close this pane.` banner with a Close button and no destructive action). The manager's `/list` and `/meta` are the only authorities on post-End state; the client does not pre-empt either response with an optimistic mutation.
84
86
 
@@ -122,3 +124,15 @@ When your public agent sends a message to someone — via email, WhatsApp, Teleg
122
124
  - **SMS:** a brief suffix is appended when the message is AI-composed
123
125
 
124
126
  Messages you write yourself (e.g. typing directly in WhatsApp) are not marked — the disclosure applies only to content composed by the AI agent.
127
+
128
+ ## Session Slot Safeguards
129
+
130
+ {{productName}} runs each chat — yours and every visitor's — as a separate `claude` process on your Pi. Three safeguards keep these processes from piling up:
131
+
132
+ - **Specialist cap.** Background specialists (`database-operator`, `content-producer`, etc.) are limited to three running at once. If you ask for a fourth while three are still working, the oldest idle one is shut down first. If all three are actively running, the request is rejected with `specialist-cap-reached`.
133
+ - **Operator reserve.** Two slots are always held back for *you* — your own chats and one-off tasks. Specialist work that would consume the last reserved slot is rejected with `operator-slots-reserved`. Your interactive chats are never blocked.
134
+ - **Idle reaper.** Every 30 seconds the platform looks for specialist processes that started, then went silent without producing any output. After two minutes of silence the platform shuts them down.
135
+
136
+ All three are tunable via env vars (`CLAUDE_SESSION_MANAGER_SPECIALIST_CAP`, `CLAUDE_SESSION_MANAGER_OPERATOR_RESERVE`, `CLAUDE_SESSION_MANAGER_TOTAL_PTY_CAP`, `CLAUDE_SESSION_MANAGER_RECORDER_IDLE_TTL_MS`); developer details in `.docs/platform.md` § "Claude Session Manager — PTY Slot Safeguards".
137
+
138
+ If you suspect background processes are piling up, run `grep '\[reaper\]' ~/.{brand}/logs/server.log | tail -50` — each tick logs how many rows it scanned and reaped.
@@ -141,6 +141,8 @@ initStderrTee("your-plugin-name");
141
141
 
142
142
  After this, every `console.error("[your-tool]...")` from any tool in the plugin appears as `[<iso-ts>] [mcp:your-plugin-name] [your-tool]...` in the per-conversation stream log `claude-agent-stream-{conversationId}.log`, alongside the usual agent events. The raw per-server file `mcp-your-plugin-name-stderr-{date}.log` is still produced for deep-dive grep.
143
143
 
144
+ **Premium plugins.** Source lives at `premium-plugins/<bundle>/plugins/<name>/mcp/src/` — deeper than platform plugins, so the source-relative import to `platform/lib/mcp-stderr-tee/dist/index.js` uses more `../` segments. The bundler rewrites the compiled output to the canonical `../../../../lib/mcp-stderr-tee/dist/index.js` at staging time and ships `platform/lib/mcp-stderr-tee/{dist,package.json}` into `premium-plugins/<bundle>/lib/mcp-stderr-tee/` so the import resolves at deployed depth. The bundler fails loudly if `platform/lib/mcp-stderr-tee/package.json` is missing (it must pin `type` so install-location parent walks cannot mis-classify the dist file) or if any lib referenced by a rewritten import has no source dist.
145
+
144
146
  **How the tee decides which file to write to:** the platform sets `STREAM_LOG_PATH` as an environment variable on every MCP server spawn, pointing to the conversation-scoped stream log. The MCP server does not know about conversations — it just trusts `STREAM_LOG_PATH`. Multiple concurrent conversations produce multiple concurrent MCP server processes, each teeing to its own file; no cross-conversation leakage.
145
147
 
146
148
  **`STREAM_LOG_PATH` reaches every Claude Code child.** The platform now sets `STREAM_LOG_PATH` on the parent `claude` spawn env itself (not only on MCP server envs), so the bundled Bun runtime inherits it and every Bash-tool subprocess the CLI spawns sees it too. Opt-in shell scripts — currently `setup-tunnel.sh`, `reset-tunnel.sh`, and `list-cf-domains.sh` under `platform/plugins/cloudflare/scripts/` — read the variable, guard against a missing value with a loud exit, and tee subprocess output line-by-line into the same per-conversation file. Each spawn writes one `[spawn-env] STREAM_LOG_PATH=set pid=… conversationId=… site=…` line so the env-propagation is auditable per session. The chat UI tails the same file for lines matching `^\[([^\]]+)\] \[([a-z][a-z0-9-]*)((?::[a-z0-9:_-]+)?)\] ` — any lowercase scope shape participates on first write (earlier platform fixes generalised from the pre-592 enum `setup-tunnel|reset-tunnel`) — and emits them as `script_stream` SSE events; see `.docs/web-chat.md` for the contract. Inner-layer helpers that a.sh wrapper spawns (e.g. `list-cf-domains.ts` via `node --experimental-strip-types`) must write phase lines directly to `STREAM_LOG_PATH` rather than relying on stderr propagation; the build-gate `platform/ui/scripts/check-stream-log-contract.mjs` enforces this and is the definitive reference for the three allowed patterns (tee-wrapped, direct-write, or explicit stderr-only marker).
@@ -14,6 +14,23 @@ export interface ManagerConfig {
14
14
  urlCaptureTimeoutMs: number;
15
15
  killGraceMs: number;
16
16
  jsonlReadMaxBytes: number;
17
+ /** Per-account concurrent specialist (`--agent`) PTYs. (N+1)th attempt
18
+ * evicts the oldest idle specialist; rejects 503 if none are idle.
19
+ * Env override: `CLAUDE_SESSION_MANAGER_SPECIALIST_CAP`. Default 3. */
20
+ specialistCap: number;
21
+ /** Slots reserved for operator (non-specialist) spawns. Specialist
22
+ * spawns are rejected with 503 `operator-slots-reserved` once
23
+ * `currentTotalLive >= totalPtyCap - operatorReserve`. Env:
24
+ * `CLAUDE_SESSION_MANAGER_OPERATOR_RESERVE`. Default 2. */
25
+ operatorReserve: number;
26
+ /** Hard ceiling on concurrent PTYs the manager will hold open. Taken
27
+ * as the min of `/proc/sys/kernel/pty/max` (Linux only; absent on
28
+ * Darwin) and `CLAUDE_SESSION_MANAGER_TOTAL_PTY_CAP` (default 64). */
29
+ totalPtyCap: number;
30
+ /** Reaper TTL: a specialist row with `status: idle` and no JSONL whose
31
+ * `updatedAt` is older than this is SIGTERMed. Env:
32
+ * `CLAUDE_SESSION_MANAGER_RECORDER_IDLE_TTL_MS`. Default 120 000. */
33
+ recorderIdleTtlMs: number;
17
34
  host: HostResolution;
18
35
  /** per-account directory resolved at boot. Mirrors the
19
36
  * installer's `resolveInstallAccountId` walk: the first sub-directory of
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AACrD,OAAO,EAGL,KAAK,iBAAiB,EACvB,MAAM,yBAAyB,CAAA;AAGhC,MAAM,WAAW,cAAe,SAAQ,WAAW;IACjD;;4DAEwD;IACxD,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;CACjC;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,WAAW,EAAE,MAAM,CAAA;IACnB,iBAAiB,EAAE,MAAM,CAAA;IACzB,IAAI,EAAE,cAAc,CAAA;IACpB;;;;;;;8BAO0B;IAC1B,UAAU,EAAE,MAAM,CAAA;IAClB;;;qDAGiD;IACjD,iBAAiB,EAAE,iBAAiB,CAAA;CACrC;AAsJD,wBAAgB,UAAU,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,aAAa,CAkE9E"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AACrD,OAAO,EAGL,KAAK,iBAAiB,EACvB,MAAM,yBAAyB,CAAA;AAGhC,MAAM,WAAW,cAAe,SAAQ,WAAW;IACjD;;4DAEwD;IACxD,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;CACjC;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,WAAW,EAAE,MAAM,CAAA;IACnB,iBAAiB,EAAE,MAAM,CAAA;IACzB;;4EAEwE;IACxE,aAAa,EAAE,MAAM,CAAA;IACrB;;;gEAG4D;IAC5D,eAAe,EAAE,MAAM,CAAA;IACvB;;2EAEuE;IACvE,WAAW,EAAE,MAAM,CAAA;IACnB;;0EAEsE;IACtE,iBAAiB,EAAE,MAAM,CAAA;IACzB,IAAI,EAAE,cAAc,CAAA;IACpB;;;;;;;8BAO0B;IAC1B,UAAU,EAAE,MAAM,CAAA;IAClB;;;qDAGiD;IACjD,iBAAiB,EAAE,iBAAiB,CAAA;CACrC;AA4KD,wBAAgB,UAAU,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,aAAa,CA4E9E"}
@@ -7,6 +7,28 @@ const DEFAULT_URL_TIMEOUT_MS = 30_000;
7
7
  const DEFAULT_KILL_GRACE_MS = 5_000;
8
8
  const DEFAULT_JSONL_READ_MAX_BYTES = 1 * 1024 * 1024;
9
9
  const DEFAULT_BRAND_PORT = 19200;
10
+ const DEFAULT_SPECIALIST_CAP = 3;
11
+ const DEFAULT_OPERATOR_RESERVE = 2;
12
+ const DEFAULT_TOTAL_PTY_CAP = 64;
13
+ const DEFAULT_RECORDER_IDLE_TTL_MS = 120_000;
14
+ const PTY_MAX_PATH = '/proc/sys/kernel/pty/max';
15
+ function parsePositiveInt(raw, fallback) {
16
+ if (raw === undefined)
17
+ return fallback;
18
+ const n = Number(raw);
19
+ return Number.isInteger(n) && n > 0 ? n : fallback;
20
+ }
21
+ function readKernelPtyMax() {
22
+ try {
23
+ const raw = readFileSync(PTY_MAX_PATH, 'utf-8').trim();
24
+ const n = Number(raw);
25
+ return Number.isInteger(n) && n > 0 ? n : Number.POSITIVE_INFINITY;
26
+ }
27
+ catch {
28
+ // Darwin (dev) / sysctl absent → no kernel-derived ceiling. Env cap holds.
29
+ return Number.POSITIVE_INFINITY;
30
+ }
31
+ }
10
32
  const CHANNELS_FOR_LIMITS = [
11
33
  'browser',
12
34
  'whatsapp',
@@ -183,6 +205,11 @@ export function loadConfig(env = process.env) {
183
205
  // the manager renders `tunnel-url: none` rather than fabricating an FQDN
184
206
  // for a tunnel that may not exist.
185
207
  const tunnelUrl = null;
208
+ const specialistCap = parsePositiveInt(env.CLAUDE_SESSION_MANAGER_SPECIALIST_CAP, DEFAULT_SPECIALIST_CAP);
209
+ const operatorReserve = parsePositiveInt(env.CLAUDE_SESSION_MANAGER_OPERATOR_RESERVE, DEFAULT_OPERATOR_RESERVE);
210
+ const envTotalCap = parsePositiveInt(env.CLAUDE_SESSION_MANAGER_TOTAL_PTY_CAP, DEFAULT_TOTAL_PTY_CAP);
211
+ const totalPtyCap = Math.min(readKernelPtyMax(), envTotalCap);
212
+ const recorderIdleTtlMs = parsePositiveInt(env.CLAUDE_SESSION_MANAGER_RECORDER_IDLE_TTL_MS, DEFAULT_RECORDER_IDLE_TTL_MS);
186
213
  return {
187
214
  port,
188
215
  persistDir,
@@ -191,6 +218,10 @@ export function loadConfig(env = process.env) {
191
218
  urlCaptureTimeoutMs: DEFAULT_URL_TIMEOUT_MS,
192
219
  killGraceMs: DEFAULT_KILL_GRACE_MS,
193
220
  jsonlReadMaxBytes: DEFAULT_JSONL_READ_MAX_BYTES,
221
+ specialistCap,
222
+ operatorReserve,
223
+ totalPtyCap,
224
+ recorderIdleTtlMs,
194
225
  host: {
195
226
  hostname: osHost,
196
227
  lanIPv4: lan.ip,
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAC/D,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAC9D,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAEzC,OAAO,EACL,2BAA2B,GAG5B,MAAM,yBAAyB,CAAA;AAoDhC,MAAM,YAAY,GAAG,KAAK,CAAA;AAC1B,MAAM,sBAAsB,GAAG,MAAM,CAAA;AACrC,MAAM,qBAAqB,GAAG,KAAK,CAAA;AACnC,MAAM,4BAA4B,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAA;AACpD,MAAM,kBAAkB,GAAG,KAAK,CAAA;AAChC,MAAM,sBAAsB,GAAG,CAAC,CAAA;AAChC,MAAM,wBAAwB,GAAG,CAAC,CAAA;AAClC,MAAM,qBAAqB,GAAG,EAAE,CAAA;AAChC,MAAM,4BAA4B,GAAG,OAAO,CAAA;AAC5C,MAAM,YAAY,GAAG,0BAA0B,CAAA;AAE/C,SAAS,gBAAgB,CAAC,GAAuB,EAAE,QAAgB;IACjE,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,QAAQ,CAAA;IACtC,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAA;IACrB,OAAO,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAA;AACpD,CAAC;AAED,SAAS,gBAAgB;IACvB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAA;QACtD,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAA;QACrB,OAAO,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,iBAAiB,CAAA;IACpE,CAAC;IAAC,MAAM,CAAC;QACP,2EAA2E;QAC3E,OAAO,MAAM,CAAC,iBAAiB,CAAA;IACjC,CAAC;AACH,CAAC;AAQD,MAAM,mBAAmB,GAAuB;IAC9C,SAAS;IACT,UAAU;IACV,UAAU;IACV,SAAS;IACT,OAAO;CACR,CAAA;AAED,SAAS,WAAW,CAAC,GAAY,EAAE,QAAqB;IACtD,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,QAAQ,CAAA;IACpD,MAAM,CAAC,GAAG,GAA4C,CAAA;IACtD,MAAM,GAAG,GAAG,OAAO,CAAC,CAAC,GAAG,KAAK,QAAQ,IAAI,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAA;IACpG,MAAM,QAAQ,GACZ,OAAO,CAAC,CAAC,QAAQ,KAAK,QAAQ,IAAI,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,QAAQ,GAAG,CAAC;QAC9E,CAAC,CAAC,CAAC,CAAC,QAAQ;QACZ,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAA;IACvB,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAA;AAC1B,CAAC;AAED,SAAS,sBAAsB,CAAC,GAAY;IAC1C,MAAM,QAAQ,GAAG,2BAA2B,CAAA;IAC5C,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,QAAQ,CAAA;IACpD,MAAM,CAAC,GAAG,GAAiD,CAAA;IAC3D,MAAM,aAAa,GAAG,CAAC,CAAC,CAAC,UAAU,IAAI,OAAO,CAAC,CAAC,UAAU,KAAK,QAAQ;QACrE,CAAC,CAAE,CAAC,CAAC,UAAsC;QAC3C,CAAC,CAAC,EAAE,CAAC,CAAA;IACP,MAAM,UAAU,GAAG,EAAkC,CAAA;IACrD,KAAK,MAAM,EAAE,IAAI,mBAAmB,EAAE,CAAC;QACrC,UAAU,CAAC,EAAE,CAAC,GAAG,WAAW,CAAC,aAAa,CAAC,EAAE,CAAC,EAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAA;IAC1E,CAAC;IACD,OAAO;QACL,UAAU;QACV,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC;KAC/C,CAAA;AACH,CAAC;AAED,SAAS,gBAAgB,CAAC,MAAyB,EAAE,GAAsB;IACzE,MAAM,MAAM,GAAG,GAAG,CAAC,qBAAqB,CAAA;IACxC,MAAM,MAAM,GAAG,GAAG,CAAC,2BAA2B,CAAA;IAC9C,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IAC3C,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IAC3C,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,MAAM,CAAA;IACjD,MAAM,IAAI,GAAG,EAAE,UAAU,EAAE,EAAkC,EAAE,MAAM,EAAE,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,EAAE,CAAA;IAC7F,MAAM,WAAW,GAAG,IAAI,KAAK,IAAI,IAAI,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAA;IACrF,MAAM,cAAc,GAAG,IAAI,KAAK,IAAI,IAAI,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAA;IACxF,KAAK,MAAM,EAAE,IAAI,mBAAmB,EAAE,CAAC;QACrC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,GAAG;YACpB,GAAG,EAAE,WAAW,IAAI,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,GAAG;YAC7C,QAAQ,EAAE,cAAc,IAAI,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,QAAQ;SAC3D,CAAA;IACH,CAAC;IACD,IAAI,WAAW,KAAK,IAAI;QAAE,IAAI,CAAC,MAAM,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,WAAW,GAAG,CAAC,CAAC,CAAA;IACtF,IAAI,cAAc,KAAK,IAAI;QAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,GAAG,cAAc,CAAA;IAClE,OAAO,IAAI,CAAA;AACb,CAAC;AAED,SAAS,SAAS,CAAC,GAAsB;IACvC,MAAM,YAAY,GAAG,GAAG,CAAC,aAAa,IAAI,GAAG,CAAC,kBAAkB,CAAA;IAChE,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CACb,yGAAyG;YACvG,kEAAkE,CACrE,CAAA;IACH,CAAC;IACD,MAAM,SAAS,GAAG,IAAI,CAAC,YAAY,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAA;IAC5D,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CACb,kDAAkD,SAAS,yEAAyE,CACrI,CAAA;IACH,CAAC;IACD,IAAI,KAA8B,CAAA;IAClC,IAAI,CAAC;QACH,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,SAAS,EAAE,OAAO,CAAC,CAA4B,CAAA;IACjF,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,MAAM,GAAG,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;QAC/E,MAAM,IAAI,KAAK,CAAC,0CAA0C,SAAS,kBAAkB,MAAM,EAAE,CAAC,CAAA;IAChG,CAAC;IACD,MAAM,EAAE,GAAG,OAAO,KAAK,CAAC,QAAQ,KAAK,QAAQ,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAA;IAClG,IAAI,CAAC,EAAE,EAAE,CAAC;QACR,MAAM,IAAI,KAAK,CACb,0CAA0C,SAAS,mCAAmC,CACvF,CAAA;IACH,CAAC;IACD,MAAM,MAAM,GACV,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAA;IACnF,MAAM,iBAAiB,GAAG,sBAAsB,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAA;IACzE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,iBAAiB,EAAE,EAAE,CAAA;AAC3E,CAAC;AAED,SAAS,cAAc;IACrB,MAAM,MAAM,GAAG,iBAAiB,EAAE,CAAA;IAClC,KAAK,MAAM,CAAC,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAC/C,IAAI,CAAC,KAAK;YAAE,SAAQ;QACpB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,IAAI,CAAC,MAAM,KAAK,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAC7C,OAAO,EAAE,EAAE,EAAE,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,CAAA;YAC/C,CAAC;QACH,CAAC;IACH,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,0BAA0B,CAAA;IACzE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,2CAA2C,KAAK,EAAE,EAAE,CAAA;AACrF,CAAC;AAED;;;;;;;uEAOuE;AACvE,SAAS,iBAAiB,CAAC,YAAoB;IAC7C,MAAM,UAAU,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;IACxC,MAAM,YAAY,GAAG,IAAI,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,CAAC,CAAA;IACzD,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC;QAAE,OAAO,IAAI,CAAA;IAC1C,IAAI,OAAiB,CAAA;IACrB,IAAI,CAAC;QACH,OAAO,GAAG,WAAW,CAAC,YAAY,CAAC,CAAA;IACrC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,SAAS,GAAG,IAAI,CAAC,YAAY,EAAE,KAAK,CAAC,CAAA;QAC3C,IAAI,UAAU,CAAC,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;YAAE,OAAO,SAAS,CAAA;IACnE,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED,SAAS,gBAAgB,CAAC,GAAsB;IAC9C,MAAM,GAAG,GAAG,GAAG,CAAC,eAAe,IAAI,GAAG,CAAC,IAAI,CAAA;IAC3C,IAAI,CAAC,GAAG;QAAE,OAAO,kBAAkB,CAAA;IACnC,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAA;IACrB,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,KAAK;QAAE,OAAO,kBAAkB,CAAA;IACzE,OAAO,CAAC,CAAA;AACV,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAyB,OAAO,CAAC,GAAG;IAC7D,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,2BAA2B,IAAI,YAAY,CAAC,CAAA;IACpE,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,KAAK,EAAE,CAAC;QACxD,MAAM,IAAI,KAAK,CAAC,wCAAwC,GAAG,CAAC,2BAA2B,EAAE,CAAC,CAAA;IAC5F,CAAC;IACD,MAAM,UAAU,GAAG,GAAG,CAAC,kCAAkC,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,CAAC,CAAA;IACrF,MAAM,SAAS,GAAG,GAAG,CAAC,UAAU,IAAI,QAAQ,CAAA;IAE5C,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,CAAA;IAC5B,MAAM,YAAY,GAAG,GAAG,CAAC,aAAa,IAAI,GAAG,CAAC,kBAAkB,CAAA;IAChE,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,yEAAyE;QACzE,oEAAoE;QACpE,MAAM,IAAI,KAAK,CAAC,oFAAoF,CAAC,CAAA;IACvG,CAAC;IACD,MAAM,UAAU,GAAG,iBAAiB,CAAC,YAAY,CAAC,CAAA;IAClD,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CACb,4GAA4G,OAAO,CAAC,YAAY,CAAC,kBAAkB;YACjJ,qFAAqF,CACxF,CAAA;IACH,CAAC;IACD,8EAA8E;IAC9E,2EAA2E;IAC3E,0EAA0E;IAC1E,gFAAgF;IAChF,qEAAqE;IACrE,MAAM,QAAQ,GAAG,GAAG,CAAC,gCAAgC,IAAI,UAAU,CAAA;IACnE,MAAM,GAAG,GAAG,cAAc,EAAE,CAAA;IAC5B,MAAM,SAAS,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAA;IACvC,MAAM,MAAM,GAAG,QAAQ,EAAE,CAAA;IACzB,yEAAyE;IACzE,2EAA2E;IAC3E,mEAAmE;IACnE,uEAAuE;IACvE,qEAAqE;IACrE,uCAAuC;IACvC,MAAM,QAAQ,GAAG,GAAG,CAAC,EAAE;QACrB,CAAC,CAAC,UAAU,GAAG,CAAC,EAAE,IAAI,SAAS,EAAE;QACjC,CAAC,CAAC,UAAU,KAAK,CAAC,MAAM,CAAC,QAAQ,UAAU,SAAS,EAAE,CAAA;IACxD,uEAAuE;IACvE,wEAAwE;IACxE,wEAAwE;IACxE,sEAAsE;IACtE,yEAAyE;IACzE,mCAAmC;IACnC,MAAM,SAAS,GAAkB,IAAI,CAAA;IAErC,MAAM,aAAa,GAAG,gBAAgB,CAAC,GAAG,CAAC,qCAAqC,EAAE,sBAAsB,CAAC,CAAA;IACzG,MAAM,eAAe,GAAG,gBAAgB,CAAC,GAAG,CAAC,uCAAuC,EAAE,wBAAwB,CAAC,CAAA;IAC/G,MAAM,WAAW,GAAG,gBAAgB,CAAC,GAAG,CAAC,oCAAoC,EAAE,qBAAqB,CAAC,CAAA;IACrG,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,gBAAgB,EAAE,EAAE,WAAW,CAAC,CAAA;IAC7D,MAAM,iBAAiB,GAAG,gBAAgB,CAAC,GAAG,CAAC,2CAA2C,EAAE,4BAA4B,CAAC,CAAA;IAEzH,OAAO;QACL,IAAI;QACJ,UAAU;QACV,SAAS;QACT,QAAQ;QACR,mBAAmB,EAAE,sBAAsB;QAC3C,WAAW,EAAE,qBAAqB;QAClC,iBAAiB,EAAE,4BAA4B;QAC/C,aAAa;QACb,eAAe;QACf,WAAW;QACX,iBAAiB;QACjB,IAAI,EAAE;YACJ,QAAQ,EAAE,MAAM;YAChB,OAAO,EAAE,GAAG,CAAC,EAAE;YACf,QAAQ;YACR,SAAS;YACT,iBAAiB,EAAE,GAAG,CAAC,UAAU;SAClC;QACD,UAAU;QACV,iBAAiB,EAAE,gBAAgB,CAAC,KAAK,CAAC,MAAM,CAAC,iBAAiB,EAAE,GAAG,CAAC;KACzE,CAAA;AACH,CAAC"}
@@ -0,0 +1,54 @@
1
+ import type { Logger } from './types.js';
2
+ export type RowState = 'live' | 'archived';
3
+ export type RowStatus = 'idle' | 'busy' | 'gone';
4
+ export interface SessionRow {
5
+ /** Intrinsic Claude session id (UUID). Canonical key. */
6
+ sessionId: string;
7
+ /** Full bridge session id ("session_<suffix>") from PID file, or null. */
8
+ bridgeSessionId: string | null;
9
+ /** Suffix only (no "session_" prefix). Derived from bridgeSessionId. */
10
+ bridgeSuffix: string | null;
11
+ /** Specialist agent name from PID file (`--agent <name>`), or null.
12
+ * Always null on archived rows — PID file is gone by then. */
13
+ agent: string | null;
14
+ /** Specialist agent name cached from the first JSONL `agent-setting`
15
+ * record (read once at row-archive transition or boot-seed of an
16
+ * archived row). Null when the JSONL has no `agent-setting` record
17
+ * (operator-driven session). UI's resolveAgentLabel falls back to
18
+ * this when `agent` is null. */
19
+ cachedAgentSetting: string | null;
20
+ cwd: string | null;
21
+ pid: number | null;
22
+ jsonlPath: string | null;
23
+ pidFilePath: string | null;
24
+ state: RowState;
25
+ status: RowStatus;
26
+ /** ms epoch from PID file `updatedAt`/`startedAt`, or 0 if unknown. */
27
+ updatedAt: number;
28
+ }
29
+ export interface WatcherEvent {
30
+ kind: 'create' | 'modify' | 'delete';
31
+ source: 'pid' | 'jsonl';
32
+ row: SessionRow | null;
33
+ sessionId: string;
34
+ }
35
+ export type Subscriber = (event: WatcherEvent) => void;
36
+ export interface FsWatcher {
37
+ bootSeed(): void;
38
+ arm(): void;
39
+ subscribe(sub: Subscriber): () => void;
40
+ getBySessionId(id: string): SessionRow | undefined;
41
+ getByBridgeSessionId(id: string): SessionRow | undefined;
42
+ getByBridgeSuffix(suffix: string): SessionRow | undefined;
43
+ getByPid(pid: number): SessionRow | undefined;
44
+ all(): SessionRow[];
45
+ /** Resolve once a PID-file event lands for `pid`. Rejects on `timeoutMs`. */
46
+ waitForPid(pid: number, timeoutMs: number): Promise<SessionRow>;
47
+ close(): void;
48
+ }
49
+ export declare function createFsWatcher(opts: {
50
+ sessionsDir: string;
51
+ projectsDir: string;
52
+ logger: Logger;
53
+ }): FsWatcher;
54
+ //# sourceMappingURL=fs-watcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fs-watcher.d.ts","sourceRoot":"","sources":["../src/fs-watcher.ts"],"names":[],"mappings":"AA2CA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAsCxC,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAA;AAC1C,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAA;AAEhD,MAAM,WAAW,UAAU;IACzB,yDAAyD;IACzD,SAAS,EAAE,MAAM,CAAA;IACjB,0EAA0E;IAC1E,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,wEAAwE;IACxE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B;mEAC+D;IAC/D,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB;;;;qCAIiC;IACjC,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAA;IACjC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAClB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,KAAK,EAAE,QAAQ,CAAA;IACf,MAAM,EAAE,SAAS,CAAA;IACjB,uEAAuE;IACvE,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAA;IACpC,MAAM,EAAE,KAAK,GAAG,OAAO,CAAA;IACvB,GAAG,EAAE,UAAU,GAAG,IAAI,CAAA;IACtB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,MAAM,UAAU,GAAG,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAA;AAEtD,MAAM,WAAW,SAAS;IACxB,QAAQ,IAAI,IAAI,CAAA;IAChB,GAAG,IAAI,IAAI,CAAA;IACX,SAAS,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM,IAAI,CAAA;IACtC,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAAA;IAClD,oBAAoB,CAAC,EAAE,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAAA;IACxD,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAAA;IACzD,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAAA;IAC7C,GAAG,IAAI,UAAU,EAAE,CAAA;IACnB,6EAA6E;IAC7E,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAA;IAC/D,KAAK,IAAI,IAAI,CAAA;CACd;AA0DD,wBAAgB,eAAe,CAAC,IAAI,EAAE;IACpC,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;CACf,GAAG,SAAS,CAyVZ"}