Skip to content

Commit 0f4e3f7

Browse files
john-zhhclaude
andcommitted
feat: v3.0 platform features — bot adapters, chat UI, session bridge, sidebar beta badges
- Add Discord/Lark/Slack adapters with real message handling - Add bot memory (persona/instructions) with file persistence - Add bot-proxy bridge for session visibility in system prompt - Add ChatTab with SSE streaming and sessionStorage persistence - Add sidebar beta badges for Bot and Middleware nav items - Register CLI sessions with daemon for bot awareness - Improve sync settings UI with timezone select dropdown Co-Authored-By: Claude (claude-opus-4-6) <noreply@anthropic.com>
1 parent ee36515 commit 0f4e3f7

33 files changed

Lines changed: 2867 additions & 225 deletions

CLAUDE.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,25 @@ go build ./...
3333
go test ./...
3434
```
3535

36+
## Dev Environment
37+
38+
Dev daemon runs on separate ports to avoid interfering with production:
39+
40+
- **Dev Web UI**: `http://localhost:29840`
41+
- **Dev Proxy**: `http://localhost:29841`
42+
- **Dev Config**: `~/.zen-dev/zen.json`
43+
- **Production ports (19840/19841)**: NEVER touch during development
44+
45+
```sh
46+
./scripts/dev.sh # Start dev daemon (builds + starts)
47+
./scripts/dev.sh stop # Stop dev daemon
48+
./scripts/dev.sh restart # Rebuild and restart dev daemon
49+
./scripts/dev.sh status # Check dev daemon status
50+
./scripts/dev.sh web # Start frontend dev server (Vite)
51+
```
52+
53+
After modifying Go code, always run `./scripts/dev.sh restart` to rebuild and restart the dev daemon.
54+
3655
## Release Process
3756

3857
Push a git tag to trigger GitHub Actions release workflow:

cmd/root.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@ package cmd
22

33
import (
44
"bufio"
5+
"bytes"
56
"context"
67
"crypto/rand"
78
"encoding/hex"
9+
"encoding/json"
810
"fmt"
911
"io"
1012
"log"
13+
"net/http"
1114
"net/url"
1215
"os"
1316
"os/exec"
@@ -217,6 +220,9 @@ func startViaDaemon(profile, client string, providerNames []string, args []strin
217220
// Set X-Zen-Client header via env var (proxy strips it)
218221
os.Setenv("X_ZEN_CLIENT", clientBin)
219222

223+
// Register session with daemon for bot visibility
224+
registerSession(proxyPort, profile, sessionID, clientBin)
225+
220226
// Find client binary
221227
cliPath, err := exec.LookPath(clientBin)
222228
if err != nil {
@@ -258,6 +264,23 @@ func startViaDaemon(profile, client string, providerNames []string, args []strin
258264
return nil
259265
}
260266

267+
// registerSession registers a session with the daemon for bot visibility.
268+
func registerSession(proxyPort int, profile, sessionID, clientType string) {
269+
url := fmt.Sprintf("http://127.0.0.1:%d/api/v1/daemon/sessions", proxyPort)
270+
body, _ := json.Marshal(map[string]string{
271+
"session_id": sessionID,
272+
"profile": profile,
273+
"client_type": clientType,
274+
})
275+
276+
client := &http.Client{Timeout: 2 * time.Second}
277+
resp, err := client.Post(url, "application/json", bytes.NewReader(body))
278+
if err != nil {
279+
return // Silently ignore - daemon might not support this yet
280+
}
281+
resp.Body.Close()
282+
}
283+
261284
// ensureDaemonRunning checks if zend is running and starts it if not.
262285
func ensureDaemonRunning() error {
263286
if _, running := daemon.IsDaemonRunning(); running {

go.mod

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@ require (
66
github.com/charmbracelet/bubbles v1.0.0
77
github.com/charmbracelet/bubbletea v1.3.10
88
github.com/charmbracelet/lipgloss v1.1.0
9+
github.com/gorilla/websocket v1.5.3
10+
github.com/minio/minio-go/v7 v7.0.98
911
github.com/pkoukk/tiktoken-go v0.1.8
1012
github.com/spf13/cobra v1.10.2
13+
golang.org/x/crypto v0.48.0
1114
modernc.org/sqlite v1.45.0
1215
)
1316

@@ -36,7 +39,6 @@ require (
3639
github.com/mattn/go-runewidth v0.0.19 // indirect
3740
github.com/minio/crc64nvme v1.1.1 // indirect
3841
github.com/minio/md5-simd v1.1.2 // indirect
39-
github.com/minio/minio-go/v7 v7.0.98 // indirect
4042
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
4143
github.com/muesli/cancelreader v0.2.2 // indirect
4244
github.com/muesli/termenv v0.16.0 // indirect
@@ -49,7 +51,6 @@ require (
4951
github.com/tinylib/msgp v1.6.1 // indirect
5052
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
5153
go.yaml.in/yaml/v3 v3.0.4 // indirect
52-
golang.org/x/crypto v0.48.0 // indirect
5354
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 // indirect
5455
golang.org/x/net v0.49.0 // indirect
5556
golang.org/x/sys v0.41.0 // indirect

go.sum

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17k
3737
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
3838
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
3939
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
40+
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
41+
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
4042
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
4143
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
4244
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
@@ -87,8 +89,8 @@ github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
8789
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
8890
github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
8991
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
90-
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
91-
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
92+
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
93+
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
9294
github.com/tinylib/msgp v1.6.1 h1:ESRv8eL3u+DNHUoSAAQRE50Hm162zqAnBoGv9PzScPY=
9395
github.com/tinylib/msgp v1.6.1/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA=
9496
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
@@ -99,26 +101,21 @@ golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
99101
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
100102
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 h1:mgKeJMpvi0yx/sU5GsxQ7p6s2wtOnGAHZWCHUM4KGzY=
101103
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70=
102-
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
103-
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
104104
golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c=
105+
golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU=
105106
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
106107
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
107-
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
108-
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
109108
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
109+
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
110110
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
111111
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
112-
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
113-
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
114112
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
115113
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
116-
golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
117-
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
118114
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
119115
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
120-
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
121-
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
116+
golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc=
117+
golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg=
118+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
122119
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
123120
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
124121
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

internal/bot/adapters/adapter.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,19 @@ type DiscordConfig struct {
139139
AllowedGuilds []string `json:"allowed_guilds,omitempty"`
140140
}
141141

142+
// IsGuildAllowed checks if a guild/server is in the allowed list.
143+
func (c *DiscordConfig) IsGuildAllowed(guildID string) bool {
144+
if len(c.AllowedGuilds) == 0 {
145+
return true
146+
}
147+
for _, g := range c.AllowedGuilds {
148+
if g == guildID {
149+
return true
150+
}
151+
}
152+
return false
153+
}
154+
142155
// SlackConfig is the configuration for Slack adapter.
143156
type SlackConfig struct {
144157
AdapterConfig

0 commit comments

Comments
 (0)