chore(store): remove unused Zustand stores and slim down package
After the chat refactoring moved state management to @multica/hooks, the Zustand stores (useConnectionStore, useMessagesStore, useAutoConnect) are no longer imported by any application code. This removes them along with their unused dependencies (zustand, uuid, react). - Delete connection-store.ts, messages.ts, use-auto-connect.ts - Extract Message/ToolStatus types into types.ts (preserves UI imports) - Remove saveConnection/loadConnection/clearConnection from connection.ts - Drop zustand, uuid, react deps from package.json Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
c2b5ada2ef
commit
1fe27a59d0
8 changed files with 27 additions and 624 deletions
|
|
@ -8,13 +8,9 @@
|
|||
"./*": "./src/*.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@multica/sdk": "workspace:*",
|
||||
"react": "catalog:",
|
||||
"uuid": "^13.0.0",
|
||||
"zustand": "catalog:"
|
||||
"@multica/sdk": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,324 +0,0 @@
|
|||
/**
|
||||
* Connection Store - manages WebSocket connection lifecycle
|
||||
*
|
||||
* Responsibilities:
|
||||
* 1. Persist deviceId (auto-generated on first run, restored from localStorage)
|
||||
* 2. Establish WebSocket connection to Gateway using connection code (from QR/paste)
|
||||
* 3. Maintain connection state (disconnected → connecting → connected → registered)
|
||||
* 4. Route incoming stream messages from Hub to MessagesStore
|
||||
* 5. Provide send() for MessagesStore to send messages
|
||||
*
|
||||
* Data flow:
|
||||
* connection code → connect() → GatewayClient(Socket.io) → Gateway server
|
||||
* ↓
|
||||
* onMessage callback → MessagesStore
|
||||
*/
|
||||
import { create } from "zustand"
|
||||
import { persist } from "zustand/middleware"
|
||||
import { v7 as uuidv7 } from "uuid"
|
||||
import {
|
||||
GatewayClient,
|
||||
StreamAction,
|
||||
type ConnectionState,
|
||||
type StreamPayload,
|
||||
type AgentEvent,
|
||||
type CompactionEndEvent,
|
||||
type GetAgentMessagesResult,
|
||||
type ContentBlock,
|
||||
} from "@multica/sdk"
|
||||
import { useMessagesStore, type Message } from "./messages"
|
||||
import { clearConnection, type ConnectionInfo } from "./connection"
|
||||
|
||||
interface ConnectionStoreState {
|
||||
deviceId: string
|
||||
gatewayUrl: string | null
|
||||
hubId: string | null
|
||||
agentId: string | null
|
||||
connectionState: ConnectionState
|
||||
lastError: { code: string; message: string } | null
|
||||
/** Whether the current connection required Owner approval (new device) */
|
||||
isNewDevice: boolean | null
|
||||
}
|
||||
|
||||
interface ConnectionStoreActions {
|
||||
connect: (code: ConnectionInfo) => void
|
||||
disconnect: () => void
|
||||
send: (to: string, action: string, payload: unknown) => void
|
||||
}
|
||||
|
||||
export type ConnectionStore = ConnectionStoreState & ConnectionStoreActions
|
||||
|
||||
// Module-level singleton — only one WebSocket connection per app
|
||||
let client: GatewayClient | null = null
|
||||
|
||||
/**
|
||||
* Create a GatewayClient and bind message-handling callbacks.
|
||||
*
|
||||
* GatewayClient is defined in packages/sdk/src/client.ts
|
||||
* It wraps Socket.io and exposes:
|
||||
* - connect() establish WebSocket connection
|
||||
* - disconnect() tear down connection
|
||||
* - send(to, action, payload) send message to a specific device
|
||||
* - request(to, method, params) send RPC request and await response
|
||||
* - onStateChange(cb) listen for connection state changes
|
||||
* - onMessage(cb) listen for incoming messages
|
||||
* - onSendError(cb) listen for send failures
|
||||
* - isRegistered / isConnected connection state checks
|
||||
*
|
||||
* Connection requires two params:
|
||||
* - url: Gateway server address (from connection code's gateway field)
|
||||
* - deviceId: unique device identifier (persisted in this store)
|
||||
*
|
||||
* Sending messages requires two routing params:
|
||||
* - hubId: which Hub to send to (from connection code)
|
||||
* - agentId: which Agent within the Hub (from connection code)
|
||||
*/
|
||||
function createClient(
|
||||
url: string,
|
||||
deviceId: string,
|
||||
hubId: string,
|
||||
token: string,
|
||||
set: (s: Partial<ConnectionStoreState>) => void,
|
||||
getState: () => ConnectionStoreState,
|
||||
): GatewayClient {
|
||||
return new GatewayClient({
|
||||
url,
|
||||
deviceId,
|
||||
deviceType: "client",
|
||||
hubId,
|
||||
token,
|
||||
})
|
||||
// Sync connection state changes to the store
|
||||
.onStateChange((connectionState) => {
|
||||
set({ connectionState })
|
||||
// Fetch message history after successful registration
|
||||
if (connectionState === "registered") {
|
||||
void fetchHistory(getState())
|
||||
}
|
||||
})
|
||||
// Route incoming messages to MessagesStore
|
||||
.onMessage((msg) => {
|
||||
// Streaming messages: Agent replies arrive in chunks
|
||||
if (msg.action === StreamAction) {
|
||||
const payload = msg.payload as StreamPayload
|
||||
const store = useMessagesStore.getState()
|
||||
const { event } = payload
|
||||
|
||||
switch (event.type) {
|
||||
case "message_start": {
|
||||
store.startStream(payload.streamId, payload.agentId)
|
||||
const content = extractContent(event)
|
||||
if (content.length) store.appendStream(payload.streamId, content)
|
||||
break
|
||||
}
|
||||
case "message_update": {
|
||||
const content = extractContent(event)
|
||||
store.appendStream(payload.streamId, content)
|
||||
break
|
||||
}
|
||||
case "message_end": {
|
||||
const content = extractContent(event)
|
||||
const stopReason = "message" in event
|
||||
? (event.message as { stopReason?: string })?.stopReason
|
||||
: undefined
|
||||
store.endStream(payload.streamId, content, stopReason)
|
||||
break
|
||||
}
|
||||
case "tool_execution_start": {
|
||||
store.startToolExecution(
|
||||
payload.agentId,
|
||||
event.toolCallId,
|
||||
event.toolName,
|
||||
event.args,
|
||||
)
|
||||
break
|
||||
}
|
||||
case "tool_execution_end": {
|
||||
store.endToolExecution(
|
||||
event.toolCallId,
|
||||
event.result,
|
||||
event.isError,
|
||||
)
|
||||
break
|
||||
}
|
||||
case "tool_execution_update":
|
||||
// Partial results — not rendered yet, ignored for now
|
||||
break
|
||||
case "compaction_start": {
|
||||
store.startCompaction()
|
||||
break
|
||||
}
|
||||
case "compaction_end": {
|
||||
const evt = event as CompactionEndEvent
|
||||
store.endCompaction({
|
||||
removed: evt.removed,
|
||||
kept: evt.kept,
|
||||
tokensRemoved: evt.tokensRemoved,
|
||||
tokensKept: evt.tokensKept,
|
||||
reason: evt.reason,
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Handle error messages from Hub (e.g. UNAUTHORIZED)
|
||||
if (msg.action === "error") {
|
||||
const payload = msg.payload as { code: string; message: string }
|
||||
set({ lastError: { code: payload.code, message: payload.message } })
|
||||
return
|
||||
}
|
||||
|
||||
// Handle direct (non-streaming) messages
|
||||
const payload = msg.payload as { agentId?: string; content?: string }
|
||||
if (payload?.agentId && payload?.content) {
|
||||
useMessagesStore.getState().addAssistantMessage(payload.content, payload.agentId)
|
||||
}
|
||||
})
|
||||
.onVerified((result) => set({ isNewDevice: result.isNewDevice ?? false }))
|
||||
.onError((error) => set({ lastError: { code: "VERIFY_ERROR", message: error.message } }))
|
||||
.onSendError((error) => set({ lastError: { code: error.code, message: error.error } }))
|
||||
}
|
||||
|
||||
/** Fetch message history from Hub via RPC after connection is established */
|
||||
async function fetchHistory(state: ConnectionStoreState): Promise<void> {
|
||||
const { hubId, agentId } = state
|
||||
if (!client || !hubId || !agentId) return
|
||||
|
||||
try {
|
||||
const result = await client.request<GetAgentMessagesResult>(
|
||||
hubId, "getAgentMessages", { agentId, limit: 200 },
|
||||
)
|
||||
|
||||
// Build a lookup map: toolCallId → { name, arguments } from assistant ToolCall blocks
|
||||
const toolCallArgsMap = new Map<string, { name: string; args: Record<string, unknown> }>()
|
||||
for (const m of result.messages) {
|
||||
if (m.role === "assistant") {
|
||||
for (const block of m.content) {
|
||||
if (block.type === "toolCall") {
|
||||
toolCallArgsMap.set(block.id, { name: block.name, args: block.arguments })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mirror the backend message array directly
|
||||
const messages: Message[] = []
|
||||
for (const m of result.messages) {
|
||||
if (m.role === "user") {
|
||||
messages.push({
|
||||
id: uuidv7(),
|
||||
role: "user",
|
||||
content: toContentBlocks(m.content),
|
||||
agentId,
|
||||
})
|
||||
} else if (m.role === "assistant") {
|
||||
messages.push({
|
||||
id: uuidv7(),
|
||||
role: "assistant",
|
||||
content: toContentBlocks(m.content),
|
||||
agentId,
|
||||
stopReason: m.stopReason,
|
||||
})
|
||||
} else if (m.role === "toolResult") {
|
||||
const callInfo = toolCallArgsMap.get(m.toolCallId)
|
||||
messages.push({
|
||||
id: uuidv7(),
|
||||
role: "toolResult",
|
||||
content: toContentBlocks(m.content),
|
||||
agentId,
|
||||
toolCallId: m.toolCallId,
|
||||
toolName: m.toolName,
|
||||
toolArgs: callInfo?.args,
|
||||
toolStatus: m.isError ? "error" : "success",
|
||||
isError: m.isError,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (messages.length > 0) {
|
||||
useMessagesStore.getState().loadMessages(messages)
|
||||
}
|
||||
} catch {
|
||||
// History fetch is best-effort — connection still works without it
|
||||
}
|
||||
}
|
||||
|
||||
/** Convert raw backend content (string or block array) to ContentBlock[] */
|
||||
function toContentBlocks(content: string | ContentBlock[]): ContentBlock[] {
|
||||
if (typeof content === "string") {
|
||||
return content ? [{ type: "text", text: content }] : []
|
||||
}
|
||||
if (Array.isArray(content)) return content
|
||||
return []
|
||||
}
|
||||
|
||||
/** Extract content blocks from an AgentEvent that carries a message */
|
||||
function extractContent(event: AgentEvent): ContentBlock[] {
|
||||
if (!("message" in event)) return []
|
||||
const msg = event.message
|
||||
if (!msg || !("content" in msg)) return []
|
||||
const content = msg.content
|
||||
return Array.isArray(content) ? content as ContentBlock[] : []
|
||||
}
|
||||
|
||||
export const useConnectionStore = create<ConnectionStore>()(
|
||||
persist(
|
||||
(set, get) => ({
|
||||
deviceId: uuidv7(),
|
||||
gatewayUrl: null,
|
||||
hubId: null,
|
||||
agentId: null,
|
||||
connectionState: "disconnected",
|
||||
lastError: null,
|
||||
isNewDevice: null,
|
||||
|
||||
// Connect using a connection code (disconnect existing connection first)
|
||||
connect: (code) => {
|
||||
if (client) {
|
||||
client.disconnect()
|
||||
client = null
|
||||
}
|
||||
|
||||
set({
|
||||
gatewayUrl: code.gateway,
|
||||
hubId: code.hubId,
|
||||
agentId: code.agentId,
|
||||
})
|
||||
|
||||
client = createClient(code.gateway, get().deviceId, code.hubId, code.token, set, get)
|
||||
client.connect()
|
||||
},
|
||||
|
||||
// Disconnect and clear all state (messages + saved connection code)
|
||||
disconnect: () => {
|
||||
if (client) {
|
||||
client.disconnect()
|
||||
client = null
|
||||
}
|
||||
useMessagesStore.getState().clearMessages()
|
||||
clearConnection()
|
||||
set({
|
||||
connectionState: "disconnected",
|
||||
gatewayUrl: null,
|
||||
hubId: null,
|
||||
agentId: null,
|
||||
lastError: null,
|
||||
isNewDevice: null,
|
||||
})
|
||||
},
|
||||
|
||||
// Send a message to a target device (called by MessagesStore.sendMessage)
|
||||
send: (to, action, payload) => {
|
||||
if (!client?.isRegistered) return
|
||||
client.send(to, action, payload)
|
||||
},
|
||||
}),
|
||||
{
|
||||
name: "multica-device",
|
||||
// Only persist deviceId — other fields are runtime state
|
||||
partialize: (state) => ({ deviceId: state.deviceId }),
|
||||
},
|
||||
),
|
||||
)
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
const STORAGE_KEY = "multica-connection"
|
||||
|
||||
export interface ConnectionInfo {
|
||||
type: "multica-connect"
|
||||
gateway: string
|
||||
|
|
@ -88,29 +86,3 @@ export function parseConnectionCode(input: string): ConnectionInfo {
|
|||
|
||||
return parsed
|
||||
}
|
||||
|
||||
export function saveConnection(info: ConnectionInfo): void {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(info))
|
||||
}
|
||||
|
||||
export function loadConnection(): ConnectionInfo | null {
|
||||
const raw = localStorage.getItem(STORAGE_KEY)
|
||||
if (!raw) return null
|
||||
|
||||
try {
|
||||
const info = JSON.parse(raw)
|
||||
if (!isConnectionInfo(info)) return null
|
||||
if (isExpired(info.expires)) {
|
||||
localStorage.removeItem(STORAGE_KEY)
|
||||
return null
|
||||
}
|
||||
return info
|
||||
} catch {
|
||||
localStorage.removeItem(STORAGE_KEY)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export function clearConnection(): void {
|
||||
localStorage.removeItem(STORAGE_KEY)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,3 @@
|
|||
export { useConnectionStore } from "./connection-store"
|
||||
export type { ConnectionStore } from "./connection-store"
|
||||
export { useAutoConnect } from "./use-auto-connect"
|
||||
export { useMessagesStore } from "./messages"
|
||||
export type { Message, MessagesStore, SendContext, ToolStatus, CompactionStats } from "./messages"
|
||||
export { parseConnectionCode, saveConnection, loadConnection, clearConnection } from "./connection"
|
||||
export type { Message, ToolStatus } from "./types"
|
||||
export { parseConnectionCode } from "./connection"
|
||||
export type { ConnectionInfo } from "./connection"
|
||||
|
|
|
|||
|
|
@ -1,208 +0,0 @@
|
|||
/**
|
||||
* Messages Store - manages chat messages and streaming state
|
||||
*
|
||||
* Data model mirrors the backend (pi-ai / pi-agent-core) exactly:
|
||||
* - UserMessage: { role: "user", content: ContentBlock[] }
|
||||
* - AssistantMessage: { role: "assistant", content: ContentBlock[] }
|
||||
* - ToolResultMessage: { role: "toolResult", toolCallId, toolName, content, isError }
|
||||
*
|
||||
* Streaming simply updates the content of the current assistant message in-place.
|
||||
* Tool execution events (start/end) create / update toolResult messages.
|
||||
*/
|
||||
import { create } from "zustand"
|
||||
import { v7 as uuidv7 } from "uuid"
|
||||
import type { ContentBlock } from "@multica/sdk"
|
||||
|
||||
export type ToolStatus = "running" | "success" | "error" | "interrupted"
|
||||
|
||||
export interface CompactionStats {
|
||||
removed: number
|
||||
kept: number
|
||||
tokensRemoved?: number
|
||||
tokensKept?: number
|
||||
reason: string
|
||||
}
|
||||
|
||||
export interface Message {
|
||||
id: string
|
||||
role: "user" | "assistant" | "toolResult"
|
||||
content: ContentBlock[]
|
||||
agentId: string
|
||||
// AssistantMessage metadata
|
||||
stopReason?: string
|
||||
// ToolResult fields (only when role === "toolResult")
|
||||
toolCallId?: string
|
||||
toolName?: string
|
||||
toolArgs?: Record<string, unknown>
|
||||
toolStatus?: ToolStatus
|
||||
isError?: boolean
|
||||
}
|
||||
|
||||
/** Parameters needed to route a message through the gateway */
|
||||
export interface SendContext {
|
||||
hubId: string
|
||||
agentId: string
|
||||
send: (to: string, action: string, payload: unknown) => void
|
||||
}
|
||||
|
||||
interface MessagesState {
|
||||
messages: Message[]
|
||||
streamingIds: Set<string>
|
||||
compacting: boolean
|
||||
lastCompaction: CompactionStats | null
|
||||
}
|
||||
|
||||
interface MessagesActions {
|
||||
sendMessage: (text: string, ctx: SendContext) => void
|
||||
addUserMessage: (content: string, agentId: string) => void
|
||||
addAssistantMessage: (content: string, agentId: string) => void
|
||||
updateMessage: (id: string, content: ContentBlock[]) => void
|
||||
loadMessages: (msgs: Message[]) => void
|
||||
clearMessages: () => void
|
||||
// Streaming
|
||||
startStream: (streamId: string, agentId: string) => void
|
||||
appendStream: (streamId: string, content: ContentBlock[]) => void
|
||||
endStream: (streamId: string, content: ContentBlock[], stopReason?: string) => void
|
||||
// Tool execution lifecycle
|
||||
startToolExecution: (agentId: string, toolCallId: string, toolName: string, args?: unknown) => void
|
||||
endToolExecution: (toolCallId: string, result?: unknown, isError?: boolean) => void
|
||||
// Compaction lifecycle
|
||||
startCompaction: () => void
|
||||
endCompaction: (stats: CompactionStats) => void
|
||||
}
|
||||
|
||||
export type MessagesStore = MessagesState & MessagesActions
|
||||
|
||||
export const useMessagesStore = create<MessagesStore>()((set, get) => ({
|
||||
messages: [],
|
||||
streamingIds: new Set<string>(),
|
||||
compacting: false,
|
||||
lastCompaction: null,
|
||||
|
||||
sendMessage: (text, ctx) => {
|
||||
get().addUserMessage(text, ctx.agentId)
|
||||
ctx.send(ctx.hubId, "message", { agentId: ctx.agentId, content: text })
|
||||
},
|
||||
|
||||
addUserMessage: (content, agentId) => {
|
||||
set((s) => ({
|
||||
messages: [...s.messages, {
|
||||
id: uuidv7(),
|
||||
role: "user",
|
||||
content: [{ type: "text" as const, text: content }],
|
||||
agentId,
|
||||
}],
|
||||
}))
|
||||
},
|
||||
|
||||
addAssistantMessage: (content, agentId) => {
|
||||
set((s) => ({
|
||||
messages: [...s.messages, {
|
||||
id: uuidv7(),
|
||||
role: "assistant",
|
||||
content: [{ type: "text" as const, text: content }],
|
||||
agentId,
|
||||
}],
|
||||
}))
|
||||
},
|
||||
|
||||
updateMessage: (id, content) => {
|
||||
set((s) => ({
|
||||
messages: s.messages.map((m) => (m.id === id ? { ...m, content } : m)),
|
||||
}))
|
||||
},
|
||||
|
||||
loadMessages: (msgs) => {
|
||||
set({ messages: msgs })
|
||||
},
|
||||
|
||||
clearMessages: () => {
|
||||
set({ messages: [], streamingIds: new Set(), compacting: false, lastCompaction: null })
|
||||
},
|
||||
|
||||
// --- Streaming: build assistant message incrementally ---
|
||||
|
||||
startStream: (streamId, agentId) => {
|
||||
set((s) => {
|
||||
const ids = new Set(s.streamingIds)
|
||||
ids.add(streamId)
|
||||
return {
|
||||
messages: [...s.messages, { id: streamId, role: "assistant" as const, content: [], agentId }],
|
||||
streamingIds: ids,
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// Replace the entire content array with the latest partial snapshot
|
||||
appendStream: (streamId, content) => {
|
||||
set((s) => ({
|
||||
messages: s.messages.map((m) => (m.id === streamId ? { ...m, content } : m)),
|
||||
}))
|
||||
},
|
||||
|
||||
endStream: (streamId, content, stopReason) => {
|
||||
set((s) => {
|
||||
const ids = new Set(s.streamingIds)
|
||||
ids.delete(streamId)
|
||||
// Find the agentId of the stream being ended to scope tool interruption
|
||||
const streamMsg = s.messages.find((m) => m.id === streamId)
|
||||
const streamAgentId = streamMsg?.agentId
|
||||
return {
|
||||
messages: s.messages.map((m) => {
|
||||
if (m.id === streamId) return { ...m, content, stopReason }
|
||||
// Interrupt running tool executions belonging to the same agent
|
||||
if (m.role === "toolResult" && m.toolStatus === "running" && m.agentId === streamAgentId) {
|
||||
return { ...m, toolStatus: "interrupted" as ToolStatus }
|
||||
}
|
||||
return m
|
||||
}),
|
||||
streamingIds: ids,
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// --- Tool execution: create / update toolResult messages ---
|
||||
|
||||
startToolExecution: (agentId, toolCallId, toolName, args) => {
|
||||
set((s) => ({
|
||||
messages: [...s.messages, {
|
||||
id: uuidv7(),
|
||||
role: "toolResult" as const,
|
||||
content: [],
|
||||
agentId,
|
||||
toolCallId,
|
||||
toolName,
|
||||
toolArgs: args as Record<string, unknown> | undefined,
|
||||
toolStatus: "running" as ToolStatus,
|
||||
isError: false,
|
||||
}],
|
||||
}))
|
||||
},
|
||||
|
||||
endToolExecution: (toolCallId, result, isError) => {
|
||||
set((s) => ({
|
||||
messages: s.messages.map((m) =>
|
||||
m.role === "toolResult" && m.toolCallId === toolCallId
|
||||
? {
|
||||
...m,
|
||||
toolStatus: (isError ? "error" : "success") as ToolStatus,
|
||||
isError: isError ?? false,
|
||||
content: result != null
|
||||
? [{ type: "text" as const, text: typeof result === "string" ? result : JSON.stringify(result) }]
|
||||
: [],
|
||||
}
|
||||
: m
|
||||
),
|
||||
}))
|
||||
},
|
||||
|
||||
// --- Compaction lifecycle ---
|
||||
|
||||
startCompaction: () => {
|
||||
set({ compacting: true })
|
||||
},
|
||||
|
||||
endCompaction: (stats) => {
|
||||
set({ compacting: false, lastCompaction: stats })
|
||||
},
|
||||
}))
|
||||
16
packages/store/src/types.ts
Normal file
16
packages/store/src/types.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import type { ContentBlock } from "@multica/sdk"
|
||||
|
||||
export type ToolStatus = "running" | "success" | "error" | "interrupted"
|
||||
|
||||
export interface Message {
|
||||
id: string
|
||||
role: "user" | "assistant" | "toolResult"
|
||||
content: ContentBlock[]
|
||||
agentId: string
|
||||
stopReason?: string
|
||||
toolCallId?: string
|
||||
toolName?: string
|
||||
toolArgs?: Record<string, unknown>
|
||||
toolStatus?: ToolStatus
|
||||
isError?: boolean
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
"use client"
|
||||
|
||||
import { useState, useEffect } from "react"
|
||||
import { useConnectionStore } from "./connection-store"
|
||||
import { loadConnection } from "./connection"
|
||||
|
||||
/** Auto-connect from saved connection code on mount, skip if already connected */
|
||||
export function useAutoConnect(): { loading: boolean } {
|
||||
const connectionState = useConnectionStore((s) => s.connectionState)
|
||||
const [loading, setLoading] = useState(true)
|
||||
|
||||
useEffect(() => {
|
||||
const state = useConnectionStore.getState()
|
||||
if (state.connectionState !== "disconnected") {
|
||||
setLoading(false)
|
||||
return
|
||||
}
|
||||
const saved = loadConnection()
|
||||
if (saved) {
|
||||
state.connect(saved)
|
||||
} else {
|
||||
setLoading(false)
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (connectionState !== "disconnected") {
|
||||
setLoading(false)
|
||||
}
|
||||
}, [connectionState])
|
||||
|
||||
return { loading }
|
||||
}
|
||||
28
pnpm-lock.yaml
generated
28
pnpm-lock.yaml
generated
|
|
@ -34,13 +34,13 @@ importers:
|
|||
dependencies:
|
||||
'@mariozechner/pi-agent-core':
|
||||
specifier: ^0.50.3
|
||||
version: 0.50.3(@modelcontextprotocol/sdk@1.25.3(hono@4.11.7)(zod@4.3.6))(ws@8.18.3)(zod@4.3.6)
|
||||
version: 0.50.3(@modelcontextprotocol/sdk@1.25.3(hono@4.11.7)(zod@3.25.76))(ws@8.18.3)(zod@3.25.76)
|
||||
'@mariozechner/pi-ai':
|
||||
specifier: ^0.50.3
|
||||
version: 0.50.3(@modelcontextprotocol/sdk@1.25.3(hono@4.11.7)(zod@4.3.6))(ws@8.18.3)(zod@4.3.6)
|
||||
version: 0.50.3(@modelcontextprotocol/sdk@1.25.3(hono@4.11.7)(zod@3.25.76))(ws@8.18.3)(zod@3.25.76)
|
||||
'@mariozechner/pi-coding-agent':
|
||||
specifier: ^0.50.3
|
||||
version: 0.50.3(@modelcontextprotocol/sdk@1.25.3(hono@4.11.7)(zod@4.3.6))(ws@8.18.3)(zod@4.3.6)
|
||||
version: 0.50.3(@modelcontextprotocol/sdk@1.25.3(hono@4.11.7)(zod@3.25.76))(ws@8.18.3)(zod@3.25.76)
|
||||
'@mozilla/readability':
|
||||
specifier: ^0.6.0
|
||||
version: 0.6.0
|
||||
|
|
@ -457,10 +457,10 @@ importers:
|
|||
devDependencies:
|
||||
'@mariozechner/pi-agent-core':
|
||||
specifier: ^0.50.3
|
||||
version: 0.50.3(@modelcontextprotocol/sdk@1.25.3(hono@4.11.7)(zod@3.25.76))(ws@8.18.3)(zod@3.25.76)
|
||||
version: 0.50.3(@modelcontextprotocol/sdk@1.25.3(hono@4.11.7)(zod@4.3.6))(ws@8.18.3)(zod@4.3.6)
|
||||
'@mariozechner/pi-ai':
|
||||
specifier: ^0.50.3
|
||||
version: 0.50.3(@modelcontextprotocol/sdk@1.25.3(hono@4.11.7)(zod@3.25.76))(ws@8.18.3)(zod@3.25.76)
|
||||
version: 0.50.3(@modelcontextprotocol/sdk@1.25.3(hono@4.11.7)(zod@4.3.6))(ws@8.18.3)(zod@4.3.6)
|
||||
'@types/uuid':
|
||||
specifier: ^11.0.0
|
||||
version: 11.0.0
|
||||
|
|
@ -473,19 +473,7 @@ importers:
|
|||
'@multica/sdk':
|
||||
specifier: workspace:*
|
||||
version: link:../sdk
|
||||
react:
|
||||
specifier: 'catalog:'
|
||||
version: 19.2.3
|
||||
uuid:
|
||||
specifier: ^13.0.0
|
||||
version: 13.0.0
|
||||
zustand:
|
||||
specifier: 'catalog:'
|
||||
version: 5.0.10(@types/react@19.1.17)(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3))
|
||||
devDependencies:
|
||||
'@types/react':
|
||||
specifier: 'catalog:'
|
||||
version: 19.1.17
|
||||
typescript:
|
||||
specifier: 'catalog:'
|
||||
version: 5.9.3
|
||||
|
|
@ -11596,12 +11584,12 @@ snapshots:
|
|||
- ws
|
||||
- zod
|
||||
|
||||
'@mariozechner/pi-coding-agent@0.50.3(@modelcontextprotocol/sdk@1.25.3(hono@4.11.7)(zod@4.3.6))(ws@8.18.3)(zod@4.3.6)':
|
||||
'@mariozechner/pi-coding-agent@0.50.3(@modelcontextprotocol/sdk@1.25.3(hono@4.11.7)(zod@3.25.76))(ws@8.18.3)(zod@3.25.76)':
|
||||
dependencies:
|
||||
'@mariozechner/clipboard': 0.3.0
|
||||
'@mariozechner/jiti': 2.6.5
|
||||
'@mariozechner/pi-agent-core': 0.50.3(@modelcontextprotocol/sdk@1.25.3(hono@4.11.7)(zod@4.3.6))(ws@8.18.3)(zod@4.3.6)
|
||||
'@mariozechner/pi-ai': 0.50.3(@modelcontextprotocol/sdk@1.25.3(hono@4.11.7)(zod@4.3.6))(ws@8.18.3)(zod@4.3.6)
|
||||
'@mariozechner/pi-agent-core': 0.50.3(@modelcontextprotocol/sdk@1.25.3(hono@4.11.7)(zod@3.25.76))(ws@8.18.3)(zod@3.25.76)
|
||||
'@mariozechner/pi-ai': 0.50.3(@modelcontextprotocol/sdk@1.25.3(hono@4.11.7)(zod@3.25.76))(ws@8.18.3)(zod@3.25.76)
|
||||
'@mariozechner/pi-tui': 0.50.3
|
||||
'@silvia-odwyer/photon-node': 0.3.4
|
||||
chalk: 5.6.2
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue