@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.
- package/package.json +1 -1
- package/payload/platform/lib/mcp-stderr-tee/package.json +7 -0
- package/payload/platform/plugins/admin/hooks/turn-completed-graph-write.sh +142 -52
- package/payload/platform/plugins/docs/references/platform.md +18 -4
- package/payload/platform/plugins/docs/references/plugins-guide.md +2 -0
- package/payload/platform/services/claude-session-manager/dist/config.d.ts +17 -0
- package/payload/platform/services/claude-session-manager/dist/config.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/config.js +31 -0
- package/payload/platform/services/claude-session-manager/dist/config.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/fs-watcher.d.ts +54 -0
- package/payload/platform/services/claude-session-manager/dist/fs-watcher.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/fs-watcher.js +516 -0
- package/payload/platform/services/claude-session-manager/dist/fs-watcher.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts +3 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js +364 -204
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/index.js +38 -11
- package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/jsonl-path.d.ts +2 -0
- package/payload/platform/services/claude-session-manager/dist/jsonl-path.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/jsonl-path.js +11 -6
- package/payload/platform/services/claude-session-manager/dist/jsonl-path.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +37 -44
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +136 -159
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/reaper.d.ts +24 -0
- package/payload/platform/services/claude-session-manager/dist/reaper.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/reaper.js +114 -0
- package/payload/platform/services/claude-session-manager/dist/reaper.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/session-store.d.ts +16 -41
- package/payload/platform/services/claude-session-manager/dist/session-store.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/session-store.js +29 -68
- package/payload/platform/services/claude-session-manager/dist/session-store.js.map +1 -1
- package/payload/premium-plugins/real-agent/lib/mcp-stderr-tee/dist/index.d.ts +51 -0
- package/payload/premium-plugins/real-agent/lib/mcp-stderr-tee/dist/index.d.ts.map +1 -0
- package/payload/premium-plugins/real-agent/lib/mcp-stderr-tee/dist/index.js +196 -0
- package/payload/premium-plugins/real-agent/lib/mcp-stderr-tee/dist/index.js.map +1 -0
- package/payload/premium-plugins/real-agent/lib/mcp-stderr-tee/package.json +7 -0
- package/payload/server/public/assets/{Checkbox-6lLDKzMP.js → Checkbox-blxv2XPY.js} +1 -1
- package/payload/server/public/assets/{admin-DO9W2D9g.js → admin-C3EvHpLN.js} +1 -1
- package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-BbLtk_k6.js → architectureDiagram-Q4EWVU46-bETsVzGS.js} +1 -1
- package/payload/server/public/assets/{blockDiagram-DXYQGD6D-B9IgcgeJ.js → blockDiagram-DXYQGD6D-5y0GBnfQ.js} +1 -1
- package/payload/server/public/assets/{brand-DQrOw38P.css → brand-DQlLKLDH.css} +1 -1
- package/payload/server/public/assets/{c4Diagram-AHTNJAMY-BrvPS09f.js → c4Diagram-AHTNJAMY-C1znpVGu.js} +1 -1
- package/payload/server/public/assets/channel-BWqTaXzb.js +1 -0
- package/payload/server/public/assets/{chunk-336JU56O-DXOr0ZnC.js → chunk-336JU56O-DlCVUGzQ.js} +2 -2
- package/payload/server/public/assets/{chunk-426QAEUC-BFW4zwrV.js → chunk-426QAEUC-bGYkmA4z.js} +1 -1
- package/payload/server/public/assets/{chunk-4TB4RGXK-D7_xc8xf.js → chunk-4TB4RGXK-6Sb0Undm.js} +1 -1
- package/payload/server/public/assets/{chunk-5FUZZQ4R-CdCSpb8j.js → chunk-5FUZZQ4R-CAT_Pq2T.js} +1 -1
- package/payload/server/public/assets/{chunk-5PVQY5BW-BVgnD9QR.js → chunk-5PVQY5BW-j231ESse.js} +1 -1
- package/payload/server/public/assets/{chunk-EDXVE4YY-CYrT1Tom.js → chunk-EDXVE4YY-BAlS4QL7.js} +1 -1
- package/payload/server/public/assets/{chunk-ENJZ2VHE-B70d0jN7.js → chunk-ENJZ2VHE-D3Ku_BMP.js} +1 -1
- package/payload/server/public/assets/{chunk-ICPOFSXX-BcLouRbd.js → chunk-ICPOFSXX-AR86A5WB.js} +1 -1
- package/payload/server/public/assets/{chunk-OYMX7WX6-C2ja2fPj.js → chunk-OYMX7WX6-DxGhapkW.js} +1 -1
- package/payload/server/public/assets/{chunk-U2HBQHQK-CCjoTpL6.js → chunk-U2HBQHQK-DAlqsDo_.js} +1 -1
- package/payload/server/public/assets/{chunk-X2U36JSP-QIVkp-XR.js → chunk-X2U36JSP-lZdkkdyB.js} +1 -1
- package/payload/server/public/assets/{chunk-YZCP3GAM-qD9rebhx.js → chunk-YZCP3GAM-CCjtXOFD.js} +1 -1
- package/payload/server/public/assets/{chunk-ZZ45TVLE-C0J_f05H.js → chunk-ZZ45TVLE-ComtdKny.js} +1 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-B9hC6fOR.js +1 -0
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-afl_ydIG.js +1 -0
- package/payload/server/public/assets/clone-Lj83OaZ3.js +1 -0
- package/payload/server/public/assets/{dagre-B3cA7jId.js → dagre-DX8QJeVt.js} +1 -1
- package/payload/server/public/assets/{dagre-KV5264BT-C8lVFO5U.js → dagre-KV5264BT-BcRbiZ1q.js} +1 -1
- package/payload/server/public/assets/data-CS_R5VWQ.js +1 -0
- package/payload/server/public/assets/{device-url-actions-qu2-XiHc.js → device-url-actions-Cag9IJ7n.js} +1 -1
- package/payload/server/public/assets/{diagram-5BDNPKRD-BYkcTelK.js → diagram-5BDNPKRD-Bw47VwHO.js} +1 -1
- package/payload/server/public/assets/{diagram-G4DWMVQ6-B6f_WR4j.js → diagram-G4DWMVQ6-B-aVfhiD.js} +1 -1
- package/payload/server/public/assets/{diagram-MMDJMWI5-ALaKcN6w.js → diagram-MMDJMWI5-DERz2dKo.js} +1 -1
- package/payload/server/public/assets/{diagram-TYMM5635-CkVg3crF.js → diagram-TYMM5635-B7eEX9Ta.js} +1 -1
- package/payload/server/public/assets/{erDiagram-SMLLAGMA-DszlTGIQ.js → erDiagram-SMLLAGMA-G_JJq3zB.js} +1 -1
- package/payload/server/public/assets/{flowDiagram-DWJPFMVM-C9-FqSlk.js → flowDiagram-DWJPFMVM-BeY9QaO0.js} +1 -1
- package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-faJU-5Zy.js → ganttDiagram-T4ZO3ILL-IXviGYnx.js} +1 -1
- package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-Cck8_tiG.js → gitGraphDiagram-UUTBAWPF-GlY6pZQ1.js} +1 -1
- package/payload/server/public/assets/graph-DtGkQBFr.js +1 -0
- package/payload/server/public/assets/graph-labels-Dwu1ON5v.js +1 -0
- package/payload/server/public/assets/{graphlib-CYRxeBXj.js → graphlib-BWgAjZSb.js} +1 -1
- package/payload/server/public/assets/{infoDiagram-42DDH7IO-CLjEjhJX.js → infoDiagram-42DDH7IO-D4YZM0gD.js} +1 -1
- package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-nSIaVXdw.js → ishikawaDiagram-UXIWVN3A-fR71GDOp.js} +1 -1
- package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-DuKq7d3g.js → journeyDiagram-VCZTEJTY-C1TcY_6m.js} +1 -1
- package/payload/server/public/assets/{kanban-definition-6JOO6SKY-Bt5xqHfY.js → kanban-definition-6JOO6SKY-IPZKpnnG.js} +1 -1
- package/payload/server/public/assets/{line-BgV8qCP_.js → line-B2JtEL1B.js} +1 -1
- package/payload/server/public/assets/{mermaid-parser.core-DQzl__pY.js → mermaid-parser.core-BKCLXqUh.js} +1 -1
- package/payload/server/public/assets/{mermaid.core-BAPEOjwh.js → mermaid.core-CmNpCLB-.js} +3 -3
- package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-CmMBrI79.js → mindmap-definition-QFDTVHPH-BS0E2FPD.js} +1 -1
- package/payload/server/public/assets/{page-BoDdtGsH.js → page-BdI3GCun.js} +1 -1
- package/payload/server/public/assets/{page-D_NCNi5j.js → page-zIjAkU2y.js} +1 -1
- package/payload/server/public/assets/{pieDiagram-DEJITSTG-BhsR9k6j.js → pieDiagram-DEJITSTG-J61JDyk8.js} +1 -1
- package/payload/server/public/assets/{public-DCcGKIMW.js → public-C5vaQnC_.js} +3 -3
- package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-DSKV6d_A.js → quadrantDiagram-34T5L4WZ-CLI03OtR.js} +1 -1
- package/payload/server/public/assets/{requirementDiagram-MS252O5E-BccM48AI.js → requirementDiagram-MS252O5E-C8MCt5NO.js} +1 -1
- package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-C0pxiQfn.js → sankeyDiagram-XADWPNL6-DQuugruj.js} +1 -1
- package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-BVsUIJsW.js → sequenceDiagram-FGHM5R23-CoYitwhO.js} +1 -1
- package/payload/server/public/assets/{stateDiagram-FHFEXIEX-lqYQZ-F1.js → stateDiagram-FHFEXIEX-G68cHHoI.js} +1 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-Dtgpgr_D.js +1 -0
- package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-D-LxYOHq.js → timeline-definition-GMOUNBTQ-CelLeJVS.js} +1 -1
- package/payload/server/public/assets/{vennDiagram-DHZGUBPP-BNHzvNba.js → vennDiagram-DHZGUBPP-BVgFPkJq.js} +1 -1
- package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-BfXR9l5s.js → wardleyDiagram-NUSXRM2D-CzD_8hl2.js} +1 -1
- package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-CL7mVEbT.js → xychartDiagram-5P7HB3ND-BuTxWdX5.js} +1 -1
- package/payload/server/public/data.html +5 -5
- package/payload/server/public/graph.html +6 -6
- package/payload/server/public/index.html +8 -8
- package/payload/server/public/public.html +5 -5
- package/payload/server/server.js +94 -4
- package/payload/server/public/assets/channel-Bmc1qlCD.js +0 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-RVZorqSk.js +0 -1
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-BEZem5bl.js +0 -1
- package/payload/server/public/assets/clone-B6eXIhiR.js +0 -1
- package/payload/server/public/assets/data-BaSmBQ7U.js +0 -1
- package/payload/server/public/assets/graph-labels-COhLbJD0.js +0 -1
- package/payload/server/public/assets/graph-qg6fkRyC.js +0 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-D2eHThhC.js +0 -1
- /package/payload/server/public/assets/{brand-Bky-hMVw.js → brand-D6mXeyxV.js} +0 -0
package/package.json
CHANGED
|
@@ -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
|
|
4
|
-
#
|
|
5
|
-
#
|
|
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"
|
|
9
|
-
# - MAXY_HIDDEN_SPAWN must equal "0" (skip recursive fires
|
|
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
|
-
#
|
|
17
|
-
#
|
|
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
|
-
|
|
87
|
+
ADMIN_SESSION_ID="${PARSED%% *}"
|
|
56
88
|
TRANSCRIPT_PATH="${PARSED#* }"
|
|
57
89
|
|
|
58
|
-
if [ -z "$
|
|
90
|
+
if [ -z "$ADMIN_SESSION_ID" ] || [ -z "$TRANSCRIPT_PATH" ] || [ ! -f "$TRANSCRIPT_PATH" ]; then
|
|
59
91
|
exit 0
|
|
60
92
|
fi
|
|
61
93
|
|
|
62
|
-
#
|
|
63
|
-
#
|
|
64
|
-
#
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
-
#
|
|
75
|
-
#
|
|
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
|
|
80
|
-
sid = sys.argv[1]
|
|
81
|
-
tail = sys.argv[2]
|
|
141
|
+
import json, sys
|
|
82
142
|
body = {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
'
|
|
150
|
+
')
|
|
96
151
|
|
|
97
|
-
|
|
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
|
-
#
|
|
102
|
-
|
|
103
|
-
|
|
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 "$
|
|
108
|
-
"
|
|
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=${
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
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;
|
|
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":"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;
|
|
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"}
|