Changeset 0.32.0 (#206)

This commit is contained in:
2026-03-19 18:50:27 +00:00
parent 6668e546fe
commit b1266b0d7c
283 changed files with 2187 additions and 1055 deletions

View File

@@ -5,8 +5,8 @@ import (
"github.com/gin-gonic/gin"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/models"
"chat-switchboard/store"
)
type Mode string

View File

@@ -9,8 +9,8 @@ import (
"github.com/gin-gonic/gin"
"golang.org/x/crypto/bcrypt"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/models"
"chat-switchboard/store"
)
const bcryptCost = 12

View File

@@ -8,8 +8,8 @@ import (
"github.com/gin-gonic/gin"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/models"
"chat-switchboard/store"
)
// MTLSConfig holds mTLS-specific configuration.

View File

@@ -3,7 +3,7 @@ package auth
import (
"testing"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/store"
)
func TestParseDN(t *testing.T) {

View File

@@ -18,8 +18,8 @@ import (
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v5"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/models"
"chat-switchboard/store"
)
// OIDCConfig holds OIDC-specific configuration from env vars.

View File

@@ -3,7 +3,7 @@ package auth_test
import (
"testing"
"git.gobha.me/xcaliber/chat-switchboard/auth"
"chat-switchboard/auth"
)
func TestOIDCProvider_Discovery(t *testing.T) {

View File

@@ -4,7 +4,7 @@ import (
"context"
"time"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/store"
)
// EveryoneGroupID is the stable ID of the implicit "Everyone" group seeded in

View File

@@ -9,9 +9,9 @@ import (
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"git.gobha.me/xcaliber/chat-switchboard/config"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/config"
"chat-switchboard/models"
"chat-switchboard/store"
)
const sessionCookieName = "sb_session"

View File

@@ -3,7 +3,7 @@ package capabilities
import (
"testing"
"git.gobha.me/xcaliber/chat-switchboard/models"
"chat-switchboard/models"
)
func TestLookupKnownModel_AlwaysFalse(t *testing.T) {

View File

@@ -4,7 +4,7 @@ import (
"regexp"
"strings"
"git.gobha.me/xcaliber/chat-switchboard/models"
"chat-switchboard/models"
)
// ── Known Model Defaults ────────────────────

View File

@@ -4,8 +4,8 @@ import (
"context"
"log"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/models"
"chat-switchboard/store"
)
// ModelsForUser returns all models and Personas visible to a user.

View File

@@ -8,11 +8,11 @@ import (
"strings"
"time"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/providers"
"git.gobha.me/xcaliber/chat-switchboard/roles"
"git.gobha.me/xcaliber/chat-switchboard/store"
"git.gobha.me/xcaliber/chat-switchboard/treepath"
"chat-switchboard/models"
"chat-switchboard/providers"
"chat-switchboard/roles"
"chat-switchboard/store"
"chat-switchboard/treepath"
)
// ── Request / Result ────────────────────────

View File

@@ -6,9 +6,9 @@ import (
"testing"
"time"
"git.gobha.me/xcaliber/chat-switchboard/database"
"git.gobha.me/xcaliber/chat-switchboard/models"
postgres "git.gobha.me/xcaliber/chat-switchboard/store/postgres"
"chat-switchboard/database"
"chat-switchboard/models"
postgres "chat-switchboard/store/postgres"
)
// ── Helpers ─────────────────────────────────

View File

@@ -1,7 +1,7 @@
package compaction
import (
"git.gobha.me/xcaliber/chat-switchboard/treepath"
"chat-switchboard/treepath"
)
// ── Token Estimation ────────────────────────

View File

@@ -3,7 +3,7 @@ package compaction
import (
"testing"
"git.gobha.me/xcaliber/chat-switchboard/treepath"
"chat-switchboard/treepath"
)
func TestEstimateTokens(t *testing.T) {

View File

@@ -6,10 +6,10 @@ import (
"sync"
"time"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/roles"
"git.gobha.me/xcaliber/chat-switchboard/store"
"git.gobha.me/xcaliber/chat-switchboard/treepath"
"chat-switchboard/models"
"chat-switchboard/roles"
"chat-switchboard/store"
"chat-switchboard/treepath"
)
// ── Defaults ────────────────────────────────

View File

@@ -4,7 +4,7 @@ import (
"os"
"testing"
"git.gobha.me/xcaliber/chat-switchboard/database"
"chat-switchboard/database"
)
func TestMain(m *testing.M) {

View File

@@ -12,7 +12,7 @@ import (
_ "github.com/lib/pq"
_ "modernc.org/sqlite"
"git.gobha.me/xcaliber/chat-switchboard/config"
"chat-switchboard/config"
)
// DB is the application-wide database connection pool.

View File

@@ -13,7 +13,6 @@
CREATE TABLE IF NOT EXISTS teams (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(200) NOT NULL UNIQUE,
slug VARCHAR(200) NOT NULL DEFAULT '' UNIQUE,
description TEXT DEFAULT '',
created_by UUID NOT NULL REFERENCES users(id) ON DELETE RESTRICT,
is_active BOOLEAN DEFAULT true,

View File

@@ -0,0 +1,37 @@
-- ==========================================
-- Chat Switchboard — 020 Multi-Replica HA
-- ==========================================
-- Shared state tables for multi-replica operation.
-- v0.32.0: ws_tickets, rate_limit_counters.
-- ==========================================
-- =========================================
-- WEBSOCKET TICKETS (replaces in-memory sync.Map)
-- =========================================
-- Short-lived, single-use tokens for WebSocket authentication.
-- Ticket issued on pod-1 must validate on pod-2.
CREATE TABLE IF NOT EXISTS ws_tickets (
id TEXT PRIMARY KEY,
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
expires_at TIMESTAMPTZ NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_ws_tickets_expires
ON ws_tickets (expires_at);
-- =========================================
-- RATE LIMIT COUNTERS (replaces in-memory token bucket)
-- =========================================
-- Fixed-window counters keyed by scope:identifier and time bucket.
-- Key format: "{scope}:{identifier}" e.g. "auth:192.168.1.1"
CREATE TABLE IF NOT EXISTS rate_limit_counters (
key TEXT NOT NULL,
window_start TIMESTAMPTZ NOT NULL,
tokens REAL NOT NULL DEFAULT 0,
PRIMARY KEY (key, window_start)
);
CREATE INDEX IF NOT EXISTS idx_rate_limit_counters_window
ON rate_limit_counters (window_start);

View File

@@ -4,7 +4,6 @@
CREATE TABLE IF NOT EXISTS teams (
id TEXT PRIMARY KEY,
name TEXT NOT NULL UNIQUE,
slug TEXT NOT NULL DEFAULT '' UNIQUE,
description TEXT DEFAULT '',
created_by TEXT NOT NULL REFERENCES users(id) ON DELETE RESTRICT,
is_active INTEGER DEFAULT 1,

View File

@@ -0,0 +1,34 @@
-- ==========================================
-- Chat Switchboard — 020 Multi-Replica HA
-- ==========================================
-- Shared state tables for multi-replica operation.
-- v0.32.0: ws_tickets, rate_limit_counters.
-- SQLite parity — functional for single-process test coverage.
-- ==========================================
-- =========================================
-- WEBSOCKET TICKETS
-- =========================================
CREATE TABLE IF NOT EXISTS ws_tickets (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
expires_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_ws_tickets_expires
ON ws_tickets (expires_at);
-- =========================================
-- RATE LIMIT COUNTERS
-- =========================================
CREATE TABLE IF NOT EXISTS rate_limit_counters (
key TEXT NOT NULL,
window_start TEXT NOT NULL,
tokens REAL NOT NULL DEFAULT 0,
PRIMARY KEY (key, window_start)
);
CREATE INDEX IF NOT EXISTS idx_rate_limit_counters_window
ON rate_limit_counters (window_start);

View File

@@ -115,7 +115,7 @@ func TestRouteFor(t *testing.T) {
{"pong", DirToClient},
// Tool bridge routes
{"tool.call.abc123", DirToClient},
{"tool.result.abc123", DirFromClient},
{"tool.result.abc123", DirBoth}, // v0.32.0: DirBoth for cross-pod WaitFor
// Extension lifecycle
{"extension.loaded", DirLocal},
{"extension.error", DirLocal},
@@ -201,11 +201,14 @@ func TestToolCallRouteToClient(t *testing.T) {
}
}
func TestToolResultRouteFromClient(t *testing.T) {
func TestToolResultRouteBoth(t *testing.T) {
// v0.32.0: tool.result is DirBoth so results cross pods for WaitFor.
// The WS subscriber explicitly filters out tool.result events to
// prevent re-sending to clients (see subscribeToBus in ws.go).
if !ShouldAcceptFromClient("tool.result.abc123") {
t.Error("tool.result.* should be accepted from client")
}
if ShouldSendToClient("tool.result.abc123") {
t.Error("tool.result.* should NOT be sent to client")
if !ShouldSendToClient("tool.result.abc123") {
t.Error("tool.result.* should route DirBoth (filtered by WS subscriber, not routing table)")
}
}

View File

@@ -23,7 +23,7 @@ import (
"github.com/lib/pq"
"git.gobha.me/xcaliber/chat-switchboard/database"
"chat-switchboard/database"
)
const pgNotifyChannel = "switchboard_events"

View File

@@ -0,0 +1,25 @@
package events
// v0.32.0: TicketValidatorAdapter bridges the context-aware store.TicketStore
// to the middleware.TicketValidator interface (which has no context parameter).
// Replaces the in-memory TicketStore that lived in this file previously.
import (
"context"
"time"
"chat-switchboard/store"
)
// TicketValidatorAdapter satisfies middleware.TicketValidator using a
// store.TicketStore backend (PG or SQLite).
type TicketValidatorAdapter struct {
Store store.TicketStore
}
// Validate atomically consumes a ticket. Satisfies middleware.TicketValidator.
func (a *TicketValidatorAdapter) Validate(ticketID string) (string, bool) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
return a.Store.Validate(ctx, ticketID)
}

View File

@@ -1,118 +0,0 @@
package events
import (
"crypto/rand"
"encoding/hex"
"log"
"sync"
"time"
)
const (
// ticketTTL is how long a ticket remains valid after issuance.
ticketTTL = 30 * time.Second
// ticketReapInterval is how often the background reaper runs.
ticketReapInterval = 15 * time.Second
)
// ticket is a single-use opaque token that maps to a user ID.
type ticket struct {
userID string
expiresAt time.Time
}
// TicketStore manages short-lived, single-use WebSocket authentication tickets.
//
// Flow:
// 1. Client POSTs to /api/v1/ws/ticket (authenticated via JWT).
// 2. Server returns {"ticket": "<opaque>"}.
// 3. Client connects WebSocket with ?ticket=<opaque>.
// 4. Server validates and deletes the ticket atomically (single-use).
//
// This replaces passing the JWT as ?token= in the WebSocket URL, which
// exposed the token in server logs, proxy logs, and browser history.
type TicketStore struct {
mu sync.Mutex
tickets map[string]ticket
stopCh chan struct{}
}
// NewTicketStore creates a store and starts the background reaper.
func NewTicketStore() *TicketStore {
ts := &TicketStore{
tickets: make(map[string]ticket),
stopCh: make(chan struct{}),
}
go ts.reaper()
return ts
}
// Issue creates a new single-use ticket for the given user.
// Returns the opaque ticket string.
func (ts *TicketStore) Issue(userID string) (string, error) {
b := make([]byte, 16) // 128-bit random
if _, err := rand.Read(b); err != nil {
return "", err
}
id := hex.EncodeToString(b)
ts.mu.Lock()
ts.tickets[id] = ticket{
userID: userID,
expiresAt: time.Now().Add(ticketTTL),
}
ts.mu.Unlock()
return id, nil
}
// Validate checks a ticket, deletes it (single-use), and returns the user ID.
// Returns ("", false) if the ticket is invalid, expired, or already consumed.
func (ts *TicketStore) Validate(ticketID string) (string, bool) {
ts.mu.Lock()
defer ts.mu.Unlock()
t, ok := ts.tickets[ticketID]
if !ok {
return "", false
}
delete(ts.tickets, ticketID) // single-use: delete immediately
if time.Now().After(t.expiresAt) {
return "", false
}
return t.userID, true
}
// Stop shuts down the background reaper.
func (ts *TicketStore) Stop() {
close(ts.stopCh)
}
// reaper periodically removes expired tickets that were never validated.
func (ts *TicketStore) reaper() {
ticker := time.NewTicker(ticketReapInterval)
defer ticker.Stop()
for {
select {
case <-ticker.C:
ts.mu.Lock()
now := time.Now()
reaped := 0
for id, t := range ts.tickets {
if now.After(t.expiresAt) {
delete(ts.tickets, id)
reaped++
}
}
ts.mu.Unlock()
if reaped > 0 {
log.Printf("[ws-tickets] reaped %d expired tickets", reaped)
}
case <-ts.stopCh:
return
}
}
}

View File

@@ -9,6 +9,11 @@ type Event struct {
Payload json.RawMessage `json:"payload"`
Ts int64 `json:"ts"`
// v0.32.0: Cross-pod targeted delivery. When set, only connections
// belonging to this user receive the event. Serialized for pg_broadcast
// (harmless if clients see it — they ignore unknown fields).
TargetUserID string `json:"target_user_id,omitempty"`
// Server-side metadata (not serialized to clients)
SenderID string `json:"-"` // user ID of sender, empty for system events
ConnID string `json:"-"` // WebSocket connection ID, empty for server-origin
@@ -60,7 +65,7 @@ var routeTable = map[string]Direction{
"role.fallback": DirToClient,
// Notifications (v0.20.0)
"notification.new": DirToClient, // targeted via Hub.SendToUser, not room-based
"notification.new": DirToClient, // targeted via Hub.PublishToUser, not room-based
"notification.read": DirToClient, // badge sync across tabs
// Workspace (v0.21.5)
@@ -79,7 +84,7 @@ var routeTable = map[string]Direction{
// Tool execution (browser tools bridge)
"tool.call.": DirToClient, // Server → specific client (browser tool invocation)
"tool.result.": DirFromClient, // Client → server (browser tool result)
"tool.result.": DirBoth, // v0.32.0: DirBoth — result must cross pods for WaitFor
// Extension lifecycle
"extension.loaded": DirLocal, // Client-only

View File

@@ -170,9 +170,17 @@ func (h *Hub) IsConnected(userID string) bool {
return false
}
// SendToUser sends an event directly to all WebSocket connections for a user,
// bypassing room filtering. Used for targeted tool.call delivery.
func (h *Hub) SendToUser(userID string, event Event) {
// PublishToUser sends an event to a specific user via the bus.
// v0.32.0: Cross-pod safe — the bus broadcast hook fans out via pg_notify.
// All replicas receive the event; only the one with the user's connection delivers it.
func (h *Hub) PublishToUser(userID string, event Event) {
event.TargetUserID = userID
h.bus.Publish(event)
}
// sendToUserLocal sends an event directly to local WebSocket connections for a user.
// Unexported — callers should use PublishToUser for cross-pod delivery.
func (h *Hub) sendToUserLocal(userID string, event Event) {
data, err := json.Marshal(event)
if err != nil {
return
@@ -226,6 +234,18 @@ func (c *Conn) subscribeToBus() {
return
}
// v0.32.0: Targeted delivery — only deliver to the intended user.
// Targeted events skip room filtering (user-scoped, not room-scoped).
if e.TargetUserID != "" && e.TargetUserID != c.userID {
return
}
// v0.32.0: tool.result travels cross-pod for WaitFor (DirBoth) but
// must not be forwarded to WebSocket clients — they sent it.
if strings.HasPrefix(e.Label, "tool.result.") {
return
}
// Don't echo typing events back to the sender
if e.Label == "chat.typing" || e.ConnID == c.id {
if e.SenderID == c.userID && e.ConnID == c.id {

View File

@@ -9,9 +9,9 @@ import (
"context"
"log"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/sandbox"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/models"
"chat-switchboard/sandbox"
"chat-switchboard/store"
)
// DiscoverStarlarkFilters scans the package registry for active starlark

View File

@@ -13,7 +13,7 @@ import (
"fmt"
"log"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/store"
)
// KBInjectFilter injects a system prompt listing active knowledge bases

View File

@@ -24,8 +24,8 @@ import (
"go.starlark.net/starlark"
"git.gobha.me/xcaliber/chat-switchboard/sandbox"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/sandbox"
"chat-switchboard/store"
)
// StarlarkFilter runs a Starlark package's on_pre_completion entry point.

View File

@@ -1,4 +1,4 @@
module git.gobha.me/xcaliber/chat-switchboard
module chat-switchboard
go 1.22

View File

@@ -12,14 +12,14 @@ import (
"github.com/gin-gonic/gin"
"golang.org/x/crypto/bcrypt"
"git.gobha.me/xcaliber/chat-switchboard/auth"
"git.gobha.me/xcaliber/chat-switchboard/crypto"
"git.gobha.me/xcaliber/chat-switchboard/database"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/providers"
"git.gobha.me/xcaliber/chat-switchboard/storage"
"git.gobha.me/xcaliber/chat-switchboard/store"
"git.gobha.me/xcaliber/chat-switchboard/tools/search"
"chat-switchboard/auth"
"chat-switchboard/crypto"
"chat-switchboard/database"
"chat-switchboard/models"
"chat-switchboard/providers"
"chat-switchboard/storage"
"chat-switchboard/store"
"chat-switchboard/tools/search"
)
type AdminHandler struct {

View File

@@ -6,8 +6,8 @@ import (
"github.com/gin-gonic/gin"
"git.gobha.me/xcaliber/chat-switchboard/notifications"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/notifications"
"chat-switchboard/store"
)
// AdminEmailHandler handles admin SMTP configuration and test endpoints.

View File

@@ -7,9 +7,9 @@ import (
"github.com/gin-gonic/gin"
"git.gobha.me/xcaliber/chat-switchboard/crypto"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/crypto"
"chat-switchboard/models"
"chat-switchboard/store"
)
// ProviderConfigHandler handles user-facing provider config endpoints.

View File

@@ -8,8 +8,8 @@ import (
"github.com/gin-gonic/gin"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/models"
"chat-switchboard/store"
)
// AuditLog writes an audit entry via the store interface.

View File

@@ -17,11 +17,11 @@ import (
"github.com/google/uuid"
"golang.org/x/crypto/bcrypt"
"git.gobha.me/xcaliber/chat-switchboard/auth"
"git.gobha.me/xcaliber/chat-switchboard/config"
"git.gobha.me/xcaliber/chat-switchboard/crypto"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/auth"
"chat-switchboard/config"
"chat-switchboard/crypto"
"chat-switchboard/models"
"chat-switchboard/store"
)
// Claims represents the JWT payload.

View File

@@ -14,9 +14,9 @@ import (
"github.com/golang-jwt/jwt/v5"
"golang.org/x/crypto/bcrypt"
"git.gobha.me/xcaliber/chat-switchboard/auth"
"git.gobha.me/xcaliber/chat-switchboard/config"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/auth"
"chat-switchboard/config"
"chat-switchboard/store"
)
func testConfig() *config.Config {

View File

@@ -15,8 +15,8 @@ import (
"github.com/gin-gonic/gin"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/models"
"chat-switchboard/store"
)
const avatarSize = 128

View File

@@ -8,11 +8,11 @@ import (
"github.com/gin-gonic/gin"
capspkg "git.gobha.me/xcaliber/chat-switchboard/capabilities"
"git.gobha.me/xcaliber/chat-switchboard/auth"
"git.gobha.me/xcaliber/chat-switchboard/health"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/store"
capspkg "chat-switchboard/capabilities"
"chat-switchboard/auth"
"chat-switchboard/health"
"chat-switchboard/models"
"chat-switchboard/store"
)
// ModelHandler provides the unified models endpoint.

View File

@@ -6,8 +6,8 @@ import (
"github.com/gin-gonic/gin"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/models"
"chat-switchboard/store"
)
// ── Channel Models Handler ──────────────────

View File

@@ -9,8 +9,8 @@ import (
"github.com/gin-gonic/gin"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/models"
"chat-switchboard/store"
)
// ── Request / Response types ────────────────

View File

@@ -13,22 +13,22 @@ import (
"github.com/gin-gonic/gin"
"git.gobha.me/xcaliber/chat-switchboard/auth"
capspkg "git.gobha.me/xcaliber/chat-switchboard/capabilities"
"git.gobha.me/xcaliber/chat-switchboard/crypto"
"git.gobha.me/xcaliber/chat-switchboard/database"
"git.gobha.me/xcaliber/chat-switchboard/events"
"git.gobha.me/xcaliber/chat-switchboard/filters"
"git.gobha.me/xcaliber/chat-switchboard/health"
"git.gobha.me/xcaliber/chat-switchboard/knowledge"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/notifications"
"git.gobha.me/xcaliber/chat-switchboard/providers"
"git.gobha.me/xcaliber/chat-switchboard/routing"
"git.gobha.me/xcaliber/chat-switchboard/sandbox"
"git.gobha.me/xcaliber/chat-switchboard/storage"
"git.gobha.me/xcaliber/chat-switchboard/store"
"git.gobha.me/xcaliber/chat-switchboard/tools"
"chat-switchboard/auth"
capspkg "chat-switchboard/capabilities"
"chat-switchboard/crypto"
"chat-switchboard/database"
"chat-switchboard/events"
"chat-switchboard/filters"
"chat-switchboard/health"
"chat-switchboard/knowledge"
"chat-switchboard/models"
"chat-switchboard/notifications"
"chat-switchboard/providers"
"chat-switchboard/routing"
"chat-switchboard/sandbox"
"chat-switchboard/storage"
"chat-switchboard/store"
"chat-switchboard/tools"
)
// ── Request Types ───────────────────────────
@@ -280,7 +280,7 @@ func (h *CompletionHandler) Complete(c *gin.Context) {
Ts: time.Now().UnixMilli(),
}
// Send to sender
h.hub.SendToUser(userID, evt)
h.hub.PublishToUser(userID, evt)
// Send to other user participants in the channel
pRows, pErr := database.DB.QueryContext(c.Request.Context(), database.Q(`
SELECT participant_id FROM channel_participants
@@ -291,7 +291,7 @@ func (h *CompletionHandler) Complete(c *gin.Context) {
for pRows.Next() {
var pid string
if pRows.Scan(&pid) == nil {
h.hub.SendToUser(pid, evt)
h.hub.PublishToUser(pid, evt)
}
}
}
@@ -598,7 +598,7 @@ func (h *CompletionHandler) Complete(c *gin.Context) {
"from_user": userID,
"content": truncateContent(req.Content, 120),
})
h.hub.SendToUser(mentionedUserID, events.Event{
h.hub.PublishToUser(mentionedUserID, events.Event{
Label: "user.mentioned",
Payload: payload,
Ts: time.Now().UnixMilli(),

View File

@@ -7,11 +7,11 @@ import (
"log"
"time"
"git.gobha.me/xcaliber/chat-switchboard/events"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/providers"
"chat-switchboard/events"
"chat-switchboard/models"
"chat-switchboard/providers"
capspkg "git.gobha.me/xcaliber/chat-switchboard/capabilities"
capspkg "chat-switchboard/capabilities"
)
// maxChainDepth limits AI-to-AI chaining to prevent runaway loops.
@@ -175,7 +175,7 @@ func (h *CompletionHandler) chainIfMentioned(
"tokens_used": resp.InputTokens + resp.OutputTokens,
"chain_depth": depth + 1,
})
h.hub.SendToUser(userID, events.Event{
h.hub.PublishToUser(userID, events.Event{
Label: "message.created",
Payload: payload,
Ts: time.Now().UnixMilli(),
@@ -314,7 +314,7 @@ func (h *CompletionHandler) chainToPersona(channelID, userID, personaID, trigger
"tokens_used": resp.InputTokens + resp.OutputTokens,
"chain_depth": depth,
})
h.hub.SendToUser(userID, events.Event{
h.hub.PublishToUser(userID, events.Event{
Label: "message.created",
Payload: payload,
Ts: time.Now().UnixMilli(),
@@ -350,7 +350,7 @@ func (h *CompletionHandler) emitTyping(userID, channelID, participantID, display
"participant_type": "persona",
"display_name": displayName,
})
h.hub.SendToUser(userID, events.Event{
h.hub.PublishToUser(userID, events.Event{
Label: eventType,
Payload: payload,
Ts: time.Now().UnixMilli(),

View File

@@ -42,9 +42,9 @@ import (
"github.com/gin-gonic/gin"
"go.starlark.net/starlark"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/sandbox"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/models"
"chat-switchboard/sandbox"
"chat-switchboard/store"
)
// ExtAPIHandler serves extension API routes via Starlark.

View File

@@ -15,7 +15,7 @@ import (
"regexp"
"strings"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/store"
)
// validSchemaIdentifier matches safe SQL identifiers for table and column names.

View File

@@ -8,7 +8,7 @@ import (
_ "modernc.org/sqlite"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/store"
)
// ── mock ExtDataStore ────────────────────────────────────────────────────────

View File

@@ -5,8 +5,8 @@ import (
"github.com/gin-gonic/gin"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/models"
"chat-switchboard/store"
)
// ExtPermHandler serves extension permission management endpoints.

View File

@@ -5,8 +5,8 @@ import (
"github.com/gin-gonic/gin"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/models"
"chat-switchboard/store"
)
// ExtSecretsHandler serves extension secret management endpoints.

View File

@@ -11,13 +11,13 @@ import (
"github.com/gin-gonic/gin"
authpkg "git.gobha.me/xcaliber/chat-switchboard/auth"
"git.gobha.me/xcaliber/chat-switchboard/config"
"git.gobha.me/xcaliber/chat-switchboard/database"
"git.gobha.me/xcaliber/chat-switchboard/middleware"
"git.gobha.me/xcaliber/chat-switchboard/store"
postgres "git.gobha.me/xcaliber/chat-switchboard/store/postgres"
sqlite "git.gobha.me/xcaliber/chat-switchboard/store/sqlite"
authpkg "chat-switchboard/auth"
"chat-switchboard/config"
"chat-switchboard/database"
"chat-switchboard/middleware"
"chat-switchboard/store"
postgres "chat-switchboard/store/postgres"
sqlite "chat-switchboard/store/sqlite"
)
// ── Extension Test Harness ──────────────────

View File

@@ -7,9 +7,9 @@ import (
"github.com/gin-gonic/gin"
"git.gobha.me/xcaliber/chat-switchboard/database"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/database"
"chat-switchboard/models"
"chat-switchboard/store"
)
// ExtensionHandler serves extension management endpoints.

View File

@@ -12,10 +12,10 @@ import (
"github.com/gin-gonic/gin"
"git.gobha.me/xcaliber/chat-switchboard/extraction"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/storage"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/extraction"
"chat-switchboard/models"
"chat-switchboard/storage"
"chat-switchboard/store"
)
// ── Default Limits ─────────────────────────

View File

@@ -10,8 +10,8 @@ import (
"github.com/gin-gonic/gin"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/models"
"chat-switchboard/store"
)
type FolderHandler struct {

View File

@@ -11,10 +11,10 @@ import (
"golang.org/x/crypto/ssh"
"git.gobha.me/xcaliber/chat-switchboard/crypto"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/store"
"git.gobha.me/xcaliber/chat-switchboard/workspace"
"chat-switchboard/crypto"
"chat-switchboard/models"
"chat-switchboard/store"
"chat-switchboard/workspace"
"github.com/gin-gonic/gin"
)

View File

@@ -7,13 +7,13 @@ import (
"github.com/gin-gonic/gin"
"git.gobha.me/xcaliber/chat-switchboard/config"
"git.gobha.me/xcaliber/chat-switchboard/crypto"
"git.gobha.me/xcaliber/chat-switchboard/database"
"git.gobha.me/xcaliber/chat-switchboard/middleware"
"git.gobha.me/xcaliber/chat-switchboard/store"
postgres "git.gobha.me/xcaliber/chat-switchboard/store/postgres"
sqlite "git.gobha.me/xcaliber/chat-switchboard/store/sqlite"
"chat-switchboard/config"
"chat-switchboard/crypto"
"chat-switchboard/database"
"chat-switchboard/middleware"
"chat-switchboard/store"
postgres "chat-switchboard/store/postgres"
sqlite "chat-switchboard/store/sqlite"
)
// ── Git Credentials Test Harness ──────────

View File

@@ -7,11 +7,11 @@ import (
"github.com/gin-gonic/gin"
"git.gobha.me/xcaliber/chat-switchboard/auth"
"git.gobha.me/xcaliber/chat-switchboard/database"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/notifications"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/auth"
"chat-switchboard/database"
"chat-switchboard/models"
"chat-switchboard/notifications"
"chat-switchboard/store"
)
// ── Request types ───────────────────────────

View File

@@ -6,11 +6,11 @@ import (
"github.com/gin-gonic/gin"
capspkg "git.gobha.me/xcaliber/chat-switchboard/capabilities"
"git.gobha.me/xcaliber/chat-switchboard/health"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/providers"
"git.gobha.me/xcaliber/chat-switchboard/store"
capspkg "chat-switchboard/capabilities"
"chat-switchboard/health"
"chat-switchboard/models"
"chat-switchboard/providers"
"chat-switchboard/store"
)
// ── Health Admin Handler ────────────────────

View File

@@ -15,16 +15,16 @@ import (
"github.com/golang-jwt/jwt/v5"
"github.com/google/uuid"
"git.gobha.me/xcaliber/chat-switchboard/config"
authpkg "git.gobha.me/xcaliber/chat-switchboard/auth"
"git.gobha.me/xcaliber/chat-switchboard/database"
"git.gobha.me/xcaliber/chat-switchboard/middleware"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/roles"
"git.gobha.me/xcaliber/chat-switchboard/store"
postgres "git.gobha.me/xcaliber/chat-switchboard/store/postgres"
sqlite "git.gobha.me/xcaliber/chat-switchboard/store/sqlite"
"git.gobha.me/xcaliber/chat-switchboard/treepath"
"chat-switchboard/config"
authpkg "chat-switchboard/auth"
"chat-switchboard/database"
"chat-switchboard/middleware"
"chat-switchboard/models"
"chat-switchboard/roles"
"chat-switchboard/store"
postgres "chat-switchboard/store/postgres"
sqlite "chat-switchboard/store/sqlite"
"chat-switchboard/treepath"
)
// ── Test Harness ────────────────────────────

View File

@@ -11,10 +11,10 @@ import (
"github.com/gin-gonic/gin"
"git.gobha.me/xcaliber/chat-switchboard/knowledge"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/storage"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/knowledge"
"chat-switchboard/models"
"chat-switchboard/storage"
"chat-switchboard/store"
)
// ── Limits ───────────────────────────────────

View File

@@ -7,14 +7,14 @@ import (
"strings"
"testing"
"git.gobha.me/xcaliber/chat-switchboard/compaction"
"git.gobha.me/xcaliber/chat-switchboard/database"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/roles"
"git.gobha.me/xcaliber/chat-switchboard/store"
postgres "git.gobha.me/xcaliber/chat-switchboard/store/postgres"
sqlite "git.gobha.me/xcaliber/chat-switchboard/store/sqlite"
"git.gobha.me/xcaliber/chat-switchboard/treepath"
"chat-switchboard/compaction"
"chat-switchboard/database"
"chat-switchboard/models"
"chat-switchboard/roles"
"chat-switchboard/store"
postgres "chat-switchboard/store/postgres"
sqlite "chat-switchboard/store/sqlite"
"chat-switchboard/treepath"
)
// ═══════════════════════════════════════════

View File

@@ -10,8 +10,8 @@ import (
"testing"
"time"
"git.gobha.me/xcaliber/chat-switchboard/database"
"git.gobha.me/xcaliber/chat-switchboard/providers"
"chat-switchboard/database"
"chat-switchboard/providers"
)
// ═══════════════════════════════════════════

View File

@@ -5,9 +5,9 @@ import (
"github.com/gin-gonic/gin"
"git.gobha.me/xcaliber/chat-switchboard/memory"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/memory"
"chat-switchboard/models"
"chat-switchboard/store"
)
// MemoryHandler provides REST endpoints for memory management.

View File

@@ -6,9 +6,9 @@ import (
"log"
"strings"
"git.gobha.me/xcaliber/chat-switchboard/knowledge"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/knowledge"
"chat-switchboard/models"
"chat-switchboard/store"
)
// maxMemoryChars is the approximate character budget for injected memories.

View File

@@ -4,8 +4,8 @@ import (
"net/http"
"testing"
"git.gobha.me/xcaliber/chat-switchboard/database"
"git.gobha.me/xcaliber/chat-switchboard/models"
"chat-switchboard/database"
"chat-switchboard/models"
)
// seedMemory inserts a memory directly into the DB for testing.

View File

@@ -10,15 +10,15 @@ import (
"github.com/gin-gonic/gin"
capspkg "git.gobha.me/xcaliber/chat-switchboard/capabilities"
"git.gobha.me/xcaliber/chat-switchboard/crypto"
"git.gobha.me/xcaliber/chat-switchboard/events"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/providers"
"git.gobha.me/xcaliber/chat-switchboard/storage"
"git.gobha.me/xcaliber/chat-switchboard/store"
"git.gobha.me/xcaliber/chat-switchboard/tools"
"git.gobha.me/xcaliber/chat-switchboard/treepath"
capspkg "chat-switchboard/capabilities"
"chat-switchboard/crypto"
"chat-switchboard/events"
"chat-switchboard/models"
"chat-switchboard/providers"
"chat-switchboard/storage"
"chat-switchboard/store"
"chat-switchboard/tools"
"chat-switchboard/treepath"
)
// ── Request / Response types ────────────────

View File

@@ -5,8 +5,8 @@ import (
"github.com/gin-gonic/gin"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/models"
"chat-switchboard/store"
)
// ModelPrefsHandler handles user model preference endpoints.

View File

@@ -8,12 +8,12 @@ import (
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"git.gobha.me/xcaliber/chat-switchboard/config"
"git.gobha.me/xcaliber/chat-switchboard/database"
"git.gobha.me/xcaliber/chat-switchboard/middleware"
"git.gobha.me/xcaliber/chat-switchboard/store"
postgres "git.gobha.me/xcaliber/chat-switchboard/store/postgres"
sqlite "git.gobha.me/xcaliber/chat-switchboard/store/sqlite"
"chat-switchboard/config"
"chat-switchboard/database"
"chat-switchboard/middleware"
"chat-switchboard/store"
postgres "chat-switchboard/store/postgres"
sqlite "chat-switchboard/store/sqlite"
)
// ── Model Prefs Test Harness ──────────────

View File

@@ -10,9 +10,9 @@ import (
"strconv"
"time"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/providers"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/models"
"chat-switchboard/providers"
"chat-switchboard/store"
)
// providerSyncTimeout is the maximum time allowed for an outbound provider

View File

@@ -7,9 +7,9 @@ import (
"github.com/gin-gonic/gin"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/notelinks"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/models"
"chat-switchboard/notelinks"
"chat-switchboard/store"
)
// ── Request / Response Types ────────────────

View File

@@ -8,14 +8,14 @@ import (
"github.com/gin-gonic/gin"
"git.gobha.me/xcaliber/chat-switchboard/config"
"git.gobha.me/xcaliber/chat-switchboard/database"
"git.gobha.me/xcaliber/chat-switchboard/middleware"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/notifications"
"git.gobha.me/xcaliber/chat-switchboard/store"
postgres "git.gobha.me/xcaliber/chat-switchboard/store/postgres"
sqlite "git.gobha.me/xcaliber/chat-switchboard/store/sqlite"
"chat-switchboard/config"
"chat-switchboard/database"
"chat-switchboard/middleware"
"chat-switchboard/models"
"chat-switchboard/notifications"
"chat-switchboard/store"
postgres "chat-switchboard/store/postgres"
sqlite "chat-switchboard/store/sqlite"
)
// ── Notification Test Harness ──────────────

View File

@@ -10,10 +10,10 @@ import (
"github.com/gin-gonic/gin"
"git.gobha.me/xcaliber/chat-switchboard/events"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/notifications"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/events"
"chat-switchboard/models"
"chat-switchboard/notifications"
"chat-switchboard/store"
)
// ── Handler ─────────────────────────────────
@@ -111,7 +111,7 @@ func (h *NotificationHandler) MarkRead(c *gin.Context) {
// Sync badge across tabs
if h.hub != nil {
payload, _ := json.Marshal(map[string]string{"id": id})
h.hub.SendToUser(userID, events.Event{
h.hub.PublishToUser(userID, events.Event{
Label: "notification.read",
Payload: payload,
Ts: time.Now().UnixMilli(),
@@ -138,7 +138,7 @@ func (h *NotificationHandler) MarkAllRead(c *gin.Context) {
// Sync badge across tabs
if h.hub != nil {
payload, _ := json.Marshal(map[string]string{"action": "mark_all_read"})
h.hub.SendToUser(userID, events.Event{
h.hub.PublishToUser(userID, events.Event{
Label: "notification.read",
Payload: payload,
Ts: time.Now().UnixMilli(),
@@ -341,7 +341,7 @@ func (h *NotificationHandler) Broadcast(c *gin.Context) {
"level": level,
})
for _, uid := range userIDs {
h.hub.SendToUser(uid, events.Event{
h.hub.PublishToUser(uid, events.Event{
Label: "system.broadcast",
Payload: payload,
Ts: time.Now().UnixMilli(),

View File

@@ -18,7 +18,7 @@ import (
"github.com/gin-gonic/gin"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/store"
)
// PackageExportHandler handles package export operations.

View File

@@ -28,8 +28,8 @@ import (
"go.starlark.net/starlark"
"git.gobha.me/xcaliber/chat-switchboard/sandbox"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/sandbox"
"chat-switchboard/store"
)
// ParseSchemaVersion extracts the "schema_version" integer from a manifest.

View File

@@ -29,7 +29,7 @@ import (
"github.com/gin-gonic/gin"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/store"
)
// RegistryEntry represents a single package in the registry.

View File

@@ -14,9 +14,9 @@ import (
"github.com/gin-gonic/gin"
"git.gobha.me/xcaliber/chat-switchboard/database"
"git.gobha.me/xcaliber/chat-switchboard/sandbox"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/database"
"chat-switchboard/sandbox"
"chat-switchboard/store"
)
// validPackageID matches lowercase alphanumeric slugs with optional hyphens.

View File

@@ -6,8 +6,8 @@ import (
"github.com/gin-gonic/gin"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/models"
"chat-switchboard/store"
)
// ── Channel Participants Handler ──────────────

View File

@@ -10,8 +10,8 @@ import (
"github.com/gin-gonic/gin"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/models"
"chat-switchboard/store"
)
type PersonaGroupHandler struct {

View File

@@ -6,8 +6,8 @@ import (
"github.com/gin-gonic/gin"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/models"
"chat-switchboard/store"
)
// PersonaHandler handles persona endpoints.

View File

@@ -15,7 +15,7 @@ import (
"github.com/gin-gonic/gin"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/store"
)
const presenceOnlineThreshold = 90 * time.Second

View File

@@ -8,12 +8,12 @@ import (
"github.com/gin-gonic/gin"
"golang.org/x/crypto/bcrypt"
"git.gobha.me/xcaliber/chat-switchboard/config"
"git.gobha.me/xcaliber/chat-switchboard/database"
"git.gobha.me/xcaliber/chat-switchboard/middleware"
"git.gobha.me/xcaliber/chat-switchboard/store"
postgres "git.gobha.me/xcaliber/chat-switchboard/store/postgres"
sqlite "git.gobha.me/xcaliber/chat-switchboard/store/sqlite"
"chat-switchboard/config"
"chat-switchboard/database"
"chat-switchboard/middleware"
"chat-switchboard/store"
postgres "chat-switchboard/store/postgres"
sqlite "chat-switchboard/store/sqlite"
)
// ── Profile Test Harness ──────────────────

View File

@@ -6,8 +6,8 @@ import (
"github.com/gin-gonic/gin"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/models"
"chat-switchboard/store"
)
// ── Request / Response Types ────────────────

View File

@@ -8,13 +8,13 @@ import (
"github.com/gin-gonic/gin"
"git.gobha.me/xcaliber/chat-switchboard/config"
"git.gobha.me/xcaliber/chat-switchboard/database"
"git.gobha.me/xcaliber/chat-switchboard/middleware"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/store"
postgres "git.gobha.me/xcaliber/chat-switchboard/store/postgres"
sqlite "git.gobha.me/xcaliber/chat-switchboard/store/sqlite"
"chat-switchboard/config"
"chat-switchboard/database"
"chat-switchboard/middleware"
"chat-switchboard/models"
"chat-switchboard/store"
postgres "chat-switchboard/store/postgres"
sqlite "chat-switchboard/store/sqlite"
)
// ── Project Test Harness ──────────────────

View File

@@ -10,10 +10,10 @@ import (
"context"
"fmt"
"git.gobha.me/xcaliber/chat-switchboard/crypto"
"git.gobha.me/xcaliber/chat-switchboard/providers"
"git.gobha.me/xcaliber/chat-switchboard/sandbox"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/crypto"
"chat-switchboard/providers"
"chat-switchboard/sandbox"
"chat-switchboard/store"
)
// ProviderResolverAdapter implements sandbox.ProviderResolver using

View File

@@ -17,10 +17,10 @@ import (
"fmt"
"log"
"git.gobha.me/xcaliber/chat-switchboard/crypto"
"git.gobha.me/xcaliber/chat-switchboard/providers"
"git.gobha.me/xcaliber/chat-switchboard/store"
"git.gobha.me/xcaliber/chat-switchboard/tools"
"chat-switchboard/crypto"
"chat-switchboard/providers"
"chat-switchboard/store"
"chat-switchboard/tools"
)
// ── Provider Resolution ────────────────────────

View File

@@ -5,10 +5,10 @@ import (
"github.com/gin-gonic/gin"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/providers"
"git.gobha.me/xcaliber/chat-switchboard/roles"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/models"
"chat-switchboard/providers"
"chat-switchboard/roles"
"chat-switchboard/store"
)
// RolesHandler manages model role configuration.

View File

@@ -7,14 +7,14 @@ import (
"github.com/gin-gonic/gin"
"git.gobha.me/xcaliber/chat-switchboard/config"
authpkg "git.gobha.me/xcaliber/chat-switchboard/auth"
"git.gobha.me/xcaliber/chat-switchboard/database"
"git.gobha.me/xcaliber/chat-switchboard/middleware"
"git.gobha.me/xcaliber/chat-switchboard/pages"
"git.gobha.me/xcaliber/chat-switchboard/store"
postgres "git.gobha.me/xcaliber/chat-switchboard/store/postgres"
sqlite "git.gobha.me/xcaliber/chat-switchboard/store/sqlite"
"chat-switchboard/config"
authpkg "chat-switchboard/auth"
"chat-switchboard/database"
"chat-switchboard/middleware"
"chat-switchboard/pages"
"chat-switchboard/store"
postgres "chat-switchboard/store/postgres"
sqlite "chat-switchboard/store/sqlite"
)
// TestRouteRegistration verifies that ALL production routes can be

View File

@@ -6,10 +6,10 @@ import (
"github.com/gin-gonic/gin"
"git.gobha.me/xcaliber/chat-switchboard/health"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/routing"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/health"
"chat-switchboard/models"
"chat-switchboard/routing"
"chat-switchboard/store"
)
// ── Routing Admin Handler ───────────────────

View File

@@ -8,7 +8,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/lib/pq"
"git.gobha.me/xcaliber/chat-switchboard/database"
"chat-switchboard/database"
)
// ── SafeJSON ────────────────────────────────

View File

@@ -7,7 +7,7 @@ import (
"os"
"path/filepath"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/store"
)
// builtinManifest is the on-disk manifest.json shape.

View File

@@ -5,10 +5,10 @@ import (
"log"
"strings"
"git.gobha.me/xcaliber/chat-switchboard/config"
"git.gobha.me/xcaliber/chat-switchboard/crypto"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/config"
"chat-switchboard/crypto"
"chat-switchboard/models"
"chat-switchboard/store"
)
// Known provider default endpoints for seeding.

View File

@@ -15,8 +15,8 @@ import (
"github.com/gin-gonic/gin"
"golang.org/x/crypto/bcrypt"
"git.gobha.me/xcaliber/chat-switchboard/crypto"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/crypto"
"chat-switchboard/store"
)
// ── Request Types ───────────────────────────

View File

@@ -5,7 +5,7 @@ import (
"github.com/gin-gonic/gin"
"git.gobha.me/xcaliber/chat-switchboard/storage"
"chat-switchboard/storage"
)
// storageConfigured is a package-level flag set during init.

View File

@@ -11,11 +11,11 @@ import (
"github.com/gin-gonic/gin"
"git.gobha.me/xcaliber/chat-switchboard/events"
"git.gobha.me/xcaliber/chat-switchboard/providers"
"git.gobha.me/xcaliber/chat-switchboard/sandbox"
"git.gobha.me/xcaliber/chat-switchboard/store"
"git.gobha.me/xcaliber/chat-switchboard/tools"
"chat-switchboard/events"
"chat-switchboard/providers"
"chat-switchboard/sandbox"
"chat-switchboard/store"
"chat-switchboard/tools"
)
// recordHealthFn records provider health from standalone streaming functions.
@@ -141,7 +141,7 @@ func executeBrowserTool(hub *events.Hub, userID string, call tools.ToolCall) too
"arguments": json.RawMessage(call.Arguments),
})
hub.SendToUser(userID, events.Event{
hub.PublishToUser(userID, events.Event{
Label: "tool.call." + callID,
Payload: payload,
Ts: time.Now().UnixMilli(),

View File

@@ -9,9 +9,9 @@ import (
"github.com/gin-gonic/gin"
"git.gobha.me/xcaliber/chat-switchboard/compaction"
"git.gobha.me/xcaliber/chat-switchboard/roles"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/compaction"
"chat-switchboard/roles"
"chat-switchboard/store"
)
// SummarizeHandler handles manual conversation summarization via HTTP.

View File

@@ -7,7 +7,7 @@ import (
"strings"
"testing"
"git.gobha.me/xcaliber/chat-switchboard/database"
"chat-switchboard/database"
)
// ═══════════════════════════════════════════════

View File

@@ -7,12 +7,12 @@ import (
"github.com/gin-gonic/gin"
"git.gobha.me/xcaliber/chat-switchboard/auth"
"git.gobha.me/xcaliber/chat-switchboard/middleware"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/store"
"git.gobha.me/xcaliber/chat-switchboard/taskutil"
"git.gobha.me/xcaliber/chat-switchboard/webhook"
"chat-switchboard/auth"
"chat-switchboard/middleware"
"chat-switchboard/models"
"chat-switchboard/store"
"chat-switchboard/taskutil"
"chat-switchboard/webhook"
)
// TaskHandler manages task CRUD and manual execution.

View File

@@ -8,10 +8,10 @@ import (
"github.com/gin-gonic/gin"
capspkg "git.gobha.me/xcaliber/chat-switchboard/capabilities"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/providers"
"git.gobha.me/xcaliber/chat-switchboard/store"
capspkg "chat-switchboard/capabilities"
"chat-switchboard/models"
"chat-switchboard/providers"
"chat-switchboard/store"
)
// ── Team Provider Handlers ──────────────────

View File

@@ -11,11 +11,11 @@ import (
"github.com/gin-gonic/gin"
"git.gobha.me/xcaliber/chat-switchboard/crypto"
"git.gobha.me/xcaliber/chat-switchboard/database"
"git.gobha.me/xcaliber/chat-switchboard/models"
"git.gobha.me/xcaliber/chat-switchboard/providers"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/crypto"
"chat-switchboard/database"
"chat-switchboard/models"
"chat-switchboard/providers"
"chat-switchboard/store"
)
// ── Request types ───────────────────────────
@@ -105,7 +105,6 @@ func (h *TeamHandler) CreateTeam(c *gin.Context) {
team := &models.Team{
Name: req.Name,
Slug: slugify(req.Name),
Description: req.Description,
CreatedBy: adminID,
IsActive: true,
@@ -119,7 +118,7 @@ func (h *TeamHandler) CreateTeam(c *gin.Context) {
return
}
c.JSON(http.StatusCreated, gin.H{"id": team.ID, "name": team.Name, "slug": team.Slug})
c.JSON(http.StatusCreated, gin.H{"id": team.ID, "name": team.Name})
AuditLog(h.stores.Audit, c, "team.create", "team", team.ID, map[string]interface{}{"name": team.Name})
}
@@ -156,7 +155,6 @@ func (h *TeamHandler) UpdateTeam(c *gin.Context) {
fields := map[string]interface{}{}
if req.Name != nil {
fields["name"] = *req.Name
fields["slug"] = slugify(*req.Name)
}
if req.Description != nil {
fields["description"] = *req.Description

View File

@@ -6,8 +6,8 @@ import (
"github.com/gin-gonic/gin"
"git.gobha.me/xcaliber/chat-switchboard/database"
"git.gobha.me/xcaliber/chat-switchboard/providers"
"chat-switchboard/database"
"chat-switchboard/providers"
_ "modernc.org/sqlite" // register sqlite driver for DB_DRIVER=sqlite
)

View File

@@ -9,9 +9,9 @@ import (
"github.com/gin-gonic/gin"
"git.gobha.me/xcaliber/chat-switchboard/providers"
"git.gobha.me/xcaliber/chat-switchboard/roles"
"git.gobha.me/xcaliber/chat-switchboard/store"
"chat-switchboard/providers"
"chat-switchboard/roles"
"chat-switchboard/store"
)
// TitleHandler generates chat titles using the utility role.

Some files were not shown because too many files have changed in this diff Show More