connectonion 0.0.18 → 0.0.21
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/dist/connect/auth.d.ts +17 -0
- package/dist/connect/auth.d.ts.map +1 -0
- package/dist/connect/auth.js +90 -0
- package/dist/connect/chat-item-mapper.d.ts +12 -0
- package/dist/connect/chat-item-mapper.d.ts.map +1 -0
- package/dist/connect/chat-item-mapper.js +169 -0
- package/dist/connect/endpoint.d.ts +9 -4
- package/dist/connect/endpoint.d.ts.map +1 -1
- package/dist/connect/endpoint.js +9 -25
- package/dist/connect/index.d.ts +7 -0
- package/dist/connect/index.d.ts.map +1 -1
- package/dist/connect/remote-agent.d.ts +55 -73
- package/dist/connect/remote-agent.d.ts.map +1 -1
- package/dist/connect/remote-agent.js +476 -736
- package/dist/connect/types.d.ts +8 -0
- package/dist/connect/types.d.ts.map +1 -1
- package/dist/react/index.d.ts +5 -95
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +8 -230
- package/dist/react/store.d.ts +44 -0
- package/dist/react/store.d.ts.map +1 -0
- package/dist/react/store.js +61 -0
- package/dist/react/use-agent-for-human.d.ts +145 -0
- package/dist/react/use-agent-for-human.d.ts.map +1 -0
- package/dist/react/use-agent-for-human.js +203 -0
- package/package.json +1 -1
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isEventType = void 0;
|
|
4
|
+
exports.useAgentForHuman = useAgentForHuman;
|
|
5
|
+
exports.isChatItemType = isChatItemType;
|
|
6
|
+
const react_1 = require("react");
|
|
7
|
+
const connect_1 = require("../connect");
|
|
8
|
+
const store_1 = require("./store");
|
|
9
|
+
/**
|
|
10
|
+
* React hook for a human user to interact with a remote AI agent.
|
|
11
|
+
*
|
|
12
|
+
* This is the primary hook for building chat UIs where a human drives the
|
|
13
|
+
* conversation. It handles approval gates, ULW pauses, onboarding flows,
|
|
14
|
+
* and session persistence — all concerns specific to human interaction.
|
|
15
|
+
* For agent-to-agent communication, use `connect()` directly instead.
|
|
16
|
+
*
|
|
17
|
+
* Wraps a `RemoteAgent` instance with Zustand-backed localStorage persistence
|
|
18
|
+
* so chat history and session state survive page refreshes. One store is created
|
|
19
|
+
* per `(address, sessionId)` pair and cached for the lifetime of the module.
|
|
20
|
+
*
|
|
21
|
+
* **Lifecycle**
|
|
22
|
+
* 1. On mount (or when `sessionId` changes), any persisted session is restored
|
|
23
|
+
* into the `RemoteAgent` so the server can resume from the correct context.
|
|
24
|
+
* 2. `agent.onMessage` is registered in an effect to receive every streaming
|
|
25
|
+
* event from the agent — UI items, status, connection state, and session
|
|
26
|
+
* snapshots are all synced here without a polling interval.
|
|
27
|
+
* 3. `input()` is fire-and-forget: it merges the session and dispatches the
|
|
28
|
+
* prompt; all reactive updates come back through `onMessage`.
|
|
29
|
+
*
|
|
30
|
+
* **Session ID ownership**
|
|
31
|
+
* The caller is responsible for generating and managing the session UUID.
|
|
32
|
+
* A stable ID (e.g. persisted in a URL parameter or parent component state)
|
|
33
|
+
* lets users resume interrupted sessions across browser refreshes.
|
|
34
|
+
*
|
|
35
|
+
* @param address - Agent's 0x-prefixed public address on the relay network
|
|
36
|
+
* @param sessionId - UUID identifying this conversation session
|
|
37
|
+
* @returns Reactive state and methods for driving a chat UI
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```tsx
|
|
41
|
+
* const { status, ui, input, isProcessing } = useAgentForHuman(agentAddress, sessionId);
|
|
42
|
+
*
|
|
43
|
+
* return (
|
|
44
|
+
* <button disabled={isProcessing} onClick={() => input('Hello')}>
|
|
45
|
+
* Send
|
|
46
|
+
* </button>
|
|
47
|
+
* );
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
function useAgentForHuman(address, sessionId) {
|
|
51
|
+
const useStore = (0, store_1.getStore)(address, sessionId);
|
|
52
|
+
// State from store
|
|
53
|
+
const messages = useStore((s) => s.messages);
|
|
54
|
+
const ui = useStore((s) => s.ui);
|
|
55
|
+
const session = useStore((s) => s.session);
|
|
56
|
+
const status = useStore((s) => s.status);
|
|
57
|
+
const error = useStore((s) => s.error);
|
|
58
|
+
// Actions from store
|
|
59
|
+
const setStatus = useStore((s) => s.setStatus);
|
|
60
|
+
const setUI = useStore((s) => s.setUI);
|
|
61
|
+
const setSession = useStore((s) => s.setSession);
|
|
62
|
+
const setError = useStore((s) => s.setError);
|
|
63
|
+
const updateMessages = useStore((s) => s.updateMessages);
|
|
64
|
+
const resetStore = useStore((s) => s.reset);
|
|
65
|
+
// RemoteAgent instance (keyed by address + sessionId)
|
|
66
|
+
const agentRef = (0, react_1.useRef)(null);
|
|
67
|
+
const keyRef = (0, react_1.useRef)(`${address}:${sessionId}`);
|
|
68
|
+
// Tear down the cached agent when the caller switches to a different address or session
|
|
69
|
+
// so the next render creates a fresh RemoteAgent pointing at the correct endpoint.
|
|
70
|
+
if (keyRef.current !== `${address}:${sessionId}`) {
|
|
71
|
+
agentRef.current = null;
|
|
72
|
+
keyRef.current = `${address}:${sessionId}`;
|
|
73
|
+
}
|
|
74
|
+
if (!agentRef.current) {
|
|
75
|
+
agentRef.current = (0, connect_1.connect)(address);
|
|
76
|
+
}
|
|
77
|
+
const agent = agentRef.current;
|
|
78
|
+
// connectionState is initialized from the agent and then kept in sync via onMessage.
|
|
79
|
+
const [connectionState, setConnectionState] = (0, react_1.useState)(agent.connectionState);
|
|
80
|
+
// Register a single onMessage callback for the lifetime of this agent instance.
|
|
81
|
+
// This replaces a polling interval: every streaming event from the server triggers
|
|
82
|
+
// one synchronous flush of all derived state into React/Zustand.
|
|
83
|
+
(0, react_1.useEffect)(() => {
|
|
84
|
+
agent.onMessage = () => {
|
|
85
|
+
setUI([...agent.ui]);
|
|
86
|
+
setStatus(agent.status);
|
|
87
|
+
setConnectionState(agent.connectionState);
|
|
88
|
+
if (agent.error)
|
|
89
|
+
setError(agent.error);
|
|
90
|
+
if (agent.currentSession) {
|
|
91
|
+
setSession(agent.currentSession);
|
|
92
|
+
if (agent.currentSession.messages) {
|
|
93
|
+
updateMessages(agent.currentSession.messages);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
return () => { agent.onMessage = null; };
|
|
98
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
99
|
+
}, [agent]);
|
|
100
|
+
// Restore persisted session into the RemoteAgent on mount or when sessionId changes.
|
|
101
|
+
// Then auto-reconnect to sync with server (get newer data, resume executing agent, etc.)
|
|
102
|
+
(0, react_1.useEffect)(() => {
|
|
103
|
+
if (session) {
|
|
104
|
+
agent._currentSession = { ...session, session_id: sessionId };
|
|
105
|
+
agent._chatItems = [...ui];
|
|
106
|
+
}
|
|
107
|
+
else if (messages.length > 0) {
|
|
108
|
+
agent._currentSession = { session_id: sessionId, messages };
|
|
109
|
+
agent._chatItems = [...ui];
|
|
110
|
+
}
|
|
111
|
+
// No auto-reconnect on mount. Show cached conversation from localStorage.
|
|
112
|
+
// When user sends next message, input() → _ensureConnected() → CONNECT
|
|
113
|
+
// will sync with server (session merge, server_newer, etc.).
|
|
114
|
+
}, [sessionId]);
|
|
115
|
+
const input = (prompt, options) => {
|
|
116
|
+
setError(null);
|
|
117
|
+
// Merge session before dispatching: the agent may have received a mode change via
|
|
118
|
+
// setMode() (stored only on _currentSession) that isn't reflected in the Zustand
|
|
119
|
+
// store yet. We preserve those in-flight agent properties while ensuring the server
|
|
120
|
+
// receives the canonical message history and the correct session ID.
|
|
121
|
+
const agentSession = agent._currentSession || {};
|
|
122
|
+
agent._currentSession = {
|
|
123
|
+
...agentSession, // Preserve mode set by setMode()
|
|
124
|
+
...(session || {}), // Overlay with store session
|
|
125
|
+
session_id: sessionId, // Ensure correct session ID
|
|
126
|
+
messages: session?.messages || messages,
|
|
127
|
+
};
|
|
128
|
+
// Restore chat items if agent is empty but store has data
|
|
129
|
+
// (Zustand hydration is async — the mount-time restore effect may have
|
|
130
|
+
// run before localStorage was hydrated, leaving _chatItems empty)
|
|
131
|
+
if (agent._chatItems.length === 0 && ui.length > 0) {
|
|
132
|
+
agent._chatItems = [...ui];
|
|
133
|
+
}
|
|
134
|
+
agent.input(prompt, options); // non-blocking — updates come via onMessage
|
|
135
|
+
};
|
|
136
|
+
const reconnect = () => {
|
|
137
|
+
// Ensure session is set on agent before reconnecting
|
|
138
|
+
if (!agent._currentSession?.session_id) {
|
|
139
|
+
agent._currentSession = { ...(session || {}), session_id: sessionId };
|
|
140
|
+
}
|
|
141
|
+
if (agent._chatItems.length === 0 && ui.length > 0) {
|
|
142
|
+
agent._chatItems = [...ui];
|
|
143
|
+
}
|
|
144
|
+
agent.reconnect(sessionId); // non-blocking — updates come via onMessage
|
|
145
|
+
};
|
|
146
|
+
const reset = () => {
|
|
147
|
+
agent.reset();
|
|
148
|
+
resetStore();
|
|
149
|
+
};
|
|
150
|
+
const sendMessage = (message) => {
|
|
151
|
+
agent.send(message);
|
|
152
|
+
};
|
|
153
|
+
const setMode = (newMode, options) => {
|
|
154
|
+
agent.setMode(newMode, options);
|
|
155
|
+
// Mirror the mode change into the Zustand store immediately so the UI reflects it
|
|
156
|
+
// before the next server-synced session arrives. ULW counters are also seeded here
|
|
157
|
+
// so consumers can render a turn budget without waiting for the first response.
|
|
158
|
+
const updates = { mode: newMode };
|
|
159
|
+
if (newMode === 'ulw') {
|
|
160
|
+
updates.ulw_turns = options?.turns || 100;
|
|
161
|
+
updates.ulw_turns_used = 0;
|
|
162
|
+
}
|
|
163
|
+
setSession(session
|
|
164
|
+
? { ...session, ...updates }
|
|
165
|
+
: { session_id: sessionId, ...updates });
|
|
166
|
+
};
|
|
167
|
+
return {
|
|
168
|
+
status,
|
|
169
|
+
connectionState,
|
|
170
|
+
ui,
|
|
171
|
+
sessionId,
|
|
172
|
+
isProcessing: status !== 'idle',
|
|
173
|
+
error,
|
|
174
|
+
checkSessionStatus: (sid) => agent.checkSessionStatus(sid),
|
|
175
|
+
mode: session?.mode || 'safe',
|
|
176
|
+
ulwTurns: session?.ulw_turns ?? null,
|
|
177
|
+
ulwTurnsUsed: session?.ulw_turns_used ?? null,
|
|
178
|
+
input,
|
|
179
|
+
sendMessage,
|
|
180
|
+
signOnboard: (options) => agent.signOnboard(options),
|
|
181
|
+
setMode,
|
|
182
|
+
reconnect,
|
|
183
|
+
reset,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Type guard that narrows a `ChatItem` to the specific variant identified by `type`.
|
|
188
|
+
*
|
|
189
|
+
* Prefer this over a raw `item.type === 'tool_call'` comparison in render code
|
|
190
|
+
* because TypeScript will fully narrow the variant's unique fields inside the branch.
|
|
191
|
+
*
|
|
192
|
+
* @example
|
|
193
|
+
* ```ts
|
|
194
|
+
* if (isChatItemType(item, 'tool_call')) {
|
|
195
|
+
* console.log(item.name, item.timing_ms); // fully typed
|
|
196
|
+
* }
|
|
197
|
+
* ```
|
|
198
|
+
*/
|
|
199
|
+
function isChatItemType(item, type) {
|
|
200
|
+
return item.type === type;
|
|
201
|
+
}
|
|
202
|
+
/** @deprecated Use isChatItemType instead */
|
|
203
|
+
exports.isEventType = isChatItemType;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "connectonion",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.21",
|
|
4
4
|
"description": "Connect to Python AI agents from TypeScript apps - Use powerful Python agents in your React, Next.js, Node.js, and Electron applications",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|