connectonion 0.0.9 → 0.0.11
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/address.d.ts.map +1 -1
- package/dist/address.js +8 -4
- package/dist/connect.d.ts +48 -34
- package/dist/connect.d.ts.map +1 -1
- package/dist/connect.js +122 -130
- package/dist/react/index.d.ts +22 -4
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +48 -18
- package/package.json +1 -1
package/dist/address.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"address.d.ts","sourceRoot":"","sources":["../src/address.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAgBH,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,UAAU,CAAC;IAC/B,UAAU,EAAE,MAAM,GAAG,UAAU,CAAC;CACjC;AAwCD;;GAEG;AACH,wBAAgB,QAAQ,IAAI,WAAW,CAoBtC;AAED;;GAEG;AACH,wBAAgB,IAAI,CAAC,KAAK,GAAE,MAAc,GAAG,WAAW,GAAG,IAAI,CAuC9D;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"address.d.ts","sourceRoot":"","sources":["../src/address.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAgBH,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,UAAU,CAAC;IAC/B,UAAU,EAAE,MAAM,GAAG,UAAU,CAAC;CACjC;AAwCD;;GAEG;AACH,wBAAgB,QAAQ,IAAI,WAAW,CAoBtC;AAED;;GAEG;AACH,wBAAgB,IAAI,CAAC,KAAK,GAAE,MAAc,GAAG,WAAW,GAAG,IAAI,CAuC9D;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,CAenD;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,WAAW,GAAG,IAAI,CAgChD;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,WAAW,CAgB7C;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAQ7E;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,GAChB;IACD,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB,CAgBA;AAED;;GAEG;AACH,wBAAgB,IAAI,CAAC,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAmB/E;AAED;;GAEG;AACH,wBAAgB,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CA4B5F;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,GAChB;IACD,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB,CAiBA"}
|
package/dist/address.js
CHANGED
|
@@ -132,11 +132,12 @@ function saveBrowser(keys) {
|
|
|
132
132
|
if (!storage) {
|
|
133
133
|
throw new Error('saveBrowser() requires browser environment');
|
|
134
134
|
}
|
|
135
|
+
// Convert Uint8Array to hex string (browser-safe, no Buffer dependency)
|
|
135
136
|
const data = {
|
|
136
137
|
address: keys.address,
|
|
137
138
|
shortAddress: keys.shortAddress,
|
|
138
|
-
publicKey:
|
|
139
|
-
privateKey:
|
|
139
|
+
publicKey: Array.from(keys.publicKey).map(b => b.toString(16).padStart(2, '0')).join(''),
|
|
140
|
+
privateKey: Array.from(keys.privateKey).map(b => b.toString(16).padStart(2, '0')).join(''),
|
|
140
141
|
};
|
|
141
142
|
storage.setItem('connectonion_keys', JSON.stringify(data));
|
|
142
143
|
}
|
|
@@ -154,11 +155,14 @@ function loadBrowser() {
|
|
|
154
155
|
}
|
|
155
156
|
try {
|
|
156
157
|
const data = JSON.parse(stored);
|
|
158
|
+
// Convert hex strings to Uint8Array (browser-safe, no Buffer dependency)
|
|
159
|
+
const publicKey = new Uint8Array(data.publicKey.match(/.{2}/g).map(b => parseInt(b, 16)));
|
|
160
|
+
const privateKey = new Uint8Array(data.privateKey.match(/.{2}/g).map(b => parseInt(b, 16)));
|
|
157
161
|
return {
|
|
158
162
|
address: data.address,
|
|
159
163
|
shortAddress: data.shortAddress,
|
|
160
|
-
publicKey
|
|
161
|
-
privateKey
|
|
164
|
+
publicKey,
|
|
165
|
+
privateKey,
|
|
162
166
|
};
|
|
163
167
|
}
|
|
164
168
|
catch {
|
package/dist/connect.d.ts
CHANGED
|
@@ -46,8 +46,8 @@
|
|
|
46
46
|
*
|
|
47
47
|
* @llm-note
|
|
48
48
|
* Dependencies: imports from [src/address.ts (AddressData, generate, load, sign, etc.), src/types.ts (SessionStatus)] | imported by [src/index.ts, src/react/index.ts, examples/connect-example.ts, tests/connect.test.ts] | tested by [tests/connect.test.ts]
|
|
49
|
-
* Data flow: connect(address, options) → creates RemoteAgent → agent.input(prompt) → generates session_id (UUID) →
|
|
50
|
-
* State/Effects: opens WebSocket connections | sends signed/unsigned messages | updates internal UI events array | syncs session from server | generates/loads Ed25519 keys | saves keys to localStorage (browser) or .co/keys/ (Node.js) |
|
|
49
|
+
* Data flow: connect(address, options) → creates RemoteAgent → agent.input(prompt) → generates session_id (UUID) → opens WebSocket → sends INPUT{type, input_id, to, prompt, session{session_id, messages}, signature?} → receives PING every 30s, responds with PONG → receives stream events (tool_call, tool_result, thinking, assistant) with session state → receives OUTPUT{result, session_id, session} → returns Response{text, done} | On disconnect/timeout: polls GET /sessions/{session_id} every 10s until result ready
|
|
50
|
+
* State/Effects: opens WebSocket connections | sends signed/unsigned messages | updates internal UI events array | syncs session from server events (in-memory) | generates/loads Ed25519 keys | saves keys to localStorage (browser) or .co/keys/ (Node.js) | polls server via HTTP fetch on connection failure | health check interval detects dead connections
|
|
51
51
|
* Integration: exposes connect(address, options), RemoteAgent class, Response, ChatItem types, AgentStatus, ConnectOptions{enablePolling, pollIntervalMs, maxPollAttempts} | supports relay mode (wss://oo.openonion.ai) and direct mode (deployed agent URL) | Ed25519 signing for authentication | session persistence across calls | automatic recovery from network failures, timeouts, page refreshes
|
|
52
52
|
* Performance: 600s timeout default (10 min) | real-time WebSocket streaming | PING/PONG keep-alive every 30s | health check every 10s | polling fallback on failures | results persist 24h server-side
|
|
53
53
|
* Errors: throws on timeout (600s default) after polling exhausted | throws on WebSocket errors after polling attempt | throws on connection close after polling attempt | throws on ERROR messages from server | throws if session expired (404) during polling | graceful fallback to polling prevents data loss
|
|
@@ -65,16 +65,18 @@ export interface Response {
|
|
|
65
65
|
done: boolean;
|
|
66
66
|
}
|
|
67
67
|
/** Chat item type */
|
|
68
|
-
export type ChatItemType = 'user' | 'agent' | 'thinking' | 'tool_call' | 'ask_user' | 'approval_needed' | 'onboard_required' | 'onboard_success' | 'intent' | 'eval' | 'compact' | 'tool_blocked';
|
|
68
|
+
export type ChatItemType = 'user' | 'agent' | 'thinking' | 'tool_call' | 'ask_user' | 'approval_needed' | 'onboard_required' | 'onboard_success' | 'intent' | 'eval' | 'compact' | 'tool_blocked' | 'ulw_turns_reached';
|
|
69
69
|
/** Chat item - data for rendering one element in chat UI */
|
|
70
70
|
export type ChatItem = {
|
|
71
71
|
id: string;
|
|
72
72
|
type: 'user';
|
|
73
73
|
content: string;
|
|
74
|
+
images?: string[];
|
|
74
75
|
} | {
|
|
75
76
|
id: string;
|
|
76
77
|
type: 'agent';
|
|
77
78
|
content: string;
|
|
79
|
+
images?: string[];
|
|
78
80
|
} | {
|
|
79
81
|
id: string;
|
|
80
82
|
type: 'thinking';
|
|
@@ -155,6 +157,11 @@ export type ChatItem = {
|
|
|
155
157
|
tool: string;
|
|
156
158
|
reason: string;
|
|
157
159
|
message: string;
|
|
160
|
+
} | {
|
|
161
|
+
id: string;
|
|
162
|
+
type: 'ulw_turns_reached';
|
|
163
|
+
turns_used: number;
|
|
164
|
+
max_turns: number;
|
|
158
165
|
};
|
|
159
166
|
type WebSocketLike = {
|
|
160
167
|
onopen: ((ev?: unknown) => unknown) | null;
|
|
@@ -231,8 +238,14 @@ export interface SessionState {
|
|
|
231
238
|
}>;
|
|
232
239
|
trace?: unknown[];
|
|
233
240
|
turn?: number;
|
|
234
|
-
|
|
241
|
+
mode?: 'safe' | 'plan' | 'accept_edits' | 'ulw';
|
|
242
|
+
/** ULW mode: max turns before pausing */
|
|
243
|
+
ulw_turns?: number;
|
|
244
|
+
/** ULW mode: turns used so far */
|
|
245
|
+
ulw_turns_used?: number;
|
|
235
246
|
}
|
|
247
|
+
/** Valid approval modes */
|
|
248
|
+
export type ApprovalMode = 'safe' | 'plan' | 'accept_edits' | 'ulw';
|
|
236
249
|
/** Agent status */
|
|
237
250
|
export type AgentStatus = 'idle' | 'working' | 'waiting';
|
|
238
251
|
/**
|
|
@@ -297,6 +310,17 @@ export declare class RemoteAgent {
|
|
|
297
310
|
get currentSession(): SessionState | null;
|
|
298
311
|
/** Chat items for rendering. One type = one component. */
|
|
299
312
|
get ui(): ChatItem[];
|
|
313
|
+
/** Current approval mode: 'safe' | 'plan' | 'accept_edits' */
|
|
314
|
+
get mode(): ApprovalMode;
|
|
315
|
+
/**
|
|
316
|
+
* Change the approval mode.
|
|
317
|
+
* Sends mode_change message to server if WebSocket is active.
|
|
318
|
+
*
|
|
319
|
+
* @param mode - New mode: 'safe' | 'plan' | 'accept_edits'
|
|
320
|
+
*/
|
|
321
|
+
setMode(mode: ApprovalMode, options?: {
|
|
322
|
+
turns?: number;
|
|
323
|
+
}): void;
|
|
300
324
|
/** Clear session and UI state, start fresh */
|
|
301
325
|
reset(): void;
|
|
302
326
|
/** Alias for reset (backwards compatibility) */
|
|
@@ -306,14 +330,20 @@ export declare class RemoteAgent {
|
|
|
306
330
|
* Returns Response with text and done flag.
|
|
307
331
|
*
|
|
308
332
|
* @param prompt - User's input
|
|
309
|
-
* @param
|
|
333
|
+
* @param options - Optional settings: images (base64 data URLs), timeoutMs
|
|
310
334
|
* @returns Response with text and done flag
|
|
311
335
|
*/
|
|
312
|
-
input(prompt: string,
|
|
336
|
+
input(prompt: string, options?: {
|
|
337
|
+
images?: string[];
|
|
338
|
+
timeoutMs?: number;
|
|
339
|
+
}): Promise<Response>;
|
|
313
340
|
/**
|
|
314
341
|
* Async version of input (same as input, for API consistency with Python).
|
|
315
342
|
*/
|
|
316
|
-
inputAsync(prompt: string,
|
|
343
|
+
inputAsync(prompt: string, options?: {
|
|
344
|
+
images?: string[];
|
|
345
|
+
timeoutMs?: number;
|
|
346
|
+
}): Promise<Response>;
|
|
317
347
|
/**
|
|
318
348
|
* Respond to ask_user event.
|
|
319
349
|
* Only valid when status === 'waiting' after an ask_user event.
|
|
@@ -341,6 +371,17 @@ export declare class RemoteAgent {
|
|
|
341
371
|
inviteCode?: string;
|
|
342
372
|
payment?: number;
|
|
343
373
|
}): void;
|
|
374
|
+
/**
|
|
375
|
+
* Respond to ULW turns reached event.
|
|
376
|
+
* Only valid when status === 'waiting' after an ulw_turns_reached event.
|
|
377
|
+
*
|
|
378
|
+
* @param action - 'continue' to extend turns, or 'switch_mode' to change mode
|
|
379
|
+
* @param options - turns to extend (for continue) or mode to switch to
|
|
380
|
+
*/
|
|
381
|
+
respondToUlwTurnsReached(action: 'continue' | 'switch_mode', options?: {
|
|
382
|
+
turns?: number;
|
|
383
|
+
mode?: ApprovalMode;
|
|
384
|
+
}): void;
|
|
344
385
|
/**
|
|
345
386
|
* Ensure keys are loaded or generated (lazy initialization).
|
|
346
387
|
* Only runs once, on first connection attempt.
|
|
@@ -351,33 +392,6 @@ export declare class RemoteAgent {
|
|
|
351
392
|
* Repeatedly checks GET /sessions/{session_id} until result is ready.
|
|
352
393
|
*/
|
|
353
394
|
private _pollForResult;
|
|
354
|
-
/**
|
|
355
|
-
* Save session ID to localStorage (browser only).
|
|
356
|
-
*/
|
|
357
|
-
private _saveSessionId;
|
|
358
|
-
/**
|
|
359
|
-
* Clear session ID from localStorage (browser only).
|
|
360
|
-
* Also clears the full session data.
|
|
361
|
-
*/
|
|
362
|
-
private _clearSessionId;
|
|
363
|
-
/**
|
|
364
|
-
* Save full session state to localStorage (browser only).
|
|
365
|
-
* Enables conversation recovery after browser refresh.
|
|
366
|
-
*/
|
|
367
|
-
private _saveSession;
|
|
368
|
-
/**
|
|
369
|
-
* Load session state from localStorage (browser only).
|
|
370
|
-
* Returns null if session doesn't exist.
|
|
371
|
-
*/
|
|
372
|
-
private _loadSession;
|
|
373
|
-
/**
|
|
374
|
-
* Load active session ID from localStorage (browser only).
|
|
375
|
-
*/
|
|
376
|
-
private _loadActiveSessionId;
|
|
377
|
-
/**
|
|
378
|
-
* Clear specific session from localStorage (browser only).
|
|
379
|
-
*/
|
|
380
|
-
private _clearSession;
|
|
381
395
|
/**
|
|
382
396
|
* Start health check interval to detect dead connections.
|
|
383
397
|
* Checks if PING received within 60 seconds, attempts reconnect if not.
|
package/dist/connect.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connect.d.ts","sourceRoot":"","sources":["../src/connect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AAEH,OAAO,KAAK,OAAO,MAAM,WAAW,CAAC;AAErC,YAAY,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAM7C;;;;;GAKG;AACH,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;CACf;AAMD,qBAAqB;AACrB,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,OAAO,GAAG,UAAU,GAAG,WAAW,GAAG,UAAU,GAAG,iBAAiB,GAAG,kBAAkB,GAAG,iBAAiB,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,GAAG,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"connect.d.ts","sourceRoot":"","sources":["../src/connect.ts"],"names":[],"mappings":"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"}
|
package/dist/connect.js
CHANGED
|
@@ -47,8 +47,8 @@
|
|
|
47
47
|
*
|
|
48
48
|
* @llm-note
|
|
49
49
|
* Dependencies: imports from [src/address.ts (AddressData, generate, load, sign, etc.), src/types.ts (SessionStatus)] | imported by [src/index.ts, src/react/index.ts, examples/connect-example.ts, tests/connect.test.ts] | tested by [tests/connect.test.ts]
|
|
50
|
-
* Data flow: connect(address, options) → creates RemoteAgent → agent.input(prompt) → generates session_id (UUID) →
|
|
51
|
-
* State/Effects: opens WebSocket connections | sends signed/unsigned messages | updates internal UI events array | syncs session from server | generates/loads Ed25519 keys | saves keys to localStorage (browser) or .co/keys/ (Node.js) |
|
|
50
|
+
* Data flow: connect(address, options) → creates RemoteAgent → agent.input(prompt) → generates session_id (UUID) → opens WebSocket → sends INPUT{type, input_id, to, prompt, session{session_id, messages}, signature?} → receives PING every 30s, responds with PONG → receives stream events (tool_call, tool_result, thinking, assistant) with session state → receives OUTPUT{result, session_id, session} → returns Response{text, done} | On disconnect/timeout: polls GET /sessions/{session_id} every 10s until result ready
|
|
51
|
+
* State/Effects: opens WebSocket connections | sends signed/unsigned messages | updates internal UI events array | syncs session from server events (in-memory) | generates/loads Ed25519 keys | saves keys to localStorage (browser) or .co/keys/ (Node.js) | polls server via HTTP fetch on connection failure | health check interval detects dead connections
|
|
52
52
|
* Integration: exposes connect(address, options), RemoteAgent class, Response, ChatItem types, AgentStatus, ConnectOptions{enablePolling, pollIntervalMs, maxPollAttempts} | supports relay mode (wss://oo.openonion.ai) and direct mode (deployed agent URL) | Ed25519 signing for authentication | session persistence across calls | automatic recovery from network failures, timeouts, page refreshes
|
|
53
53
|
* Performance: 600s timeout default (10 min) | real-time WebSocket streaming | PING/PONG keep-alive every 30s | health check every 10s | polling fallback on failures | results persist 24h server-side
|
|
54
54
|
* Errors: throws on timeout (600s default) after polling exhausted | throws on WebSocket errors after polling attempt | throws on connection close after polling attempt | throws on ERROR messages from server | throws if session expired (404) during polling | graceful fallback to polling prevents data loss
|
|
@@ -359,9 +359,41 @@ class RemoteAgent {
|
|
|
359
359
|
get ui() {
|
|
360
360
|
return this._chatItems;
|
|
361
361
|
}
|
|
362
|
+
/** Current approval mode: 'safe' | 'plan' | 'accept_edits' */
|
|
363
|
+
get mode() {
|
|
364
|
+
return this._currentSession?.mode || 'safe';
|
|
365
|
+
}
|
|
362
366
|
// ==========================================================================
|
|
363
367
|
// Public Methods
|
|
364
368
|
// ==========================================================================
|
|
369
|
+
/**
|
|
370
|
+
* Change the approval mode.
|
|
371
|
+
* Sends mode_change message to server if WebSocket is active.
|
|
372
|
+
*
|
|
373
|
+
* @param mode - New mode: 'safe' | 'plan' | 'accept_edits'
|
|
374
|
+
*/
|
|
375
|
+
setMode(mode, options) {
|
|
376
|
+
// Update local state immediately
|
|
377
|
+
if (!this._currentSession) {
|
|
378
|
+
this._currentSession = { mode };
|
|
379
|
+
}
|
|
380
|
+
else {
|
|
381
|
+
this._currentSession.mode = mode;
|
|
382
|
+
}
|
|
383
|
+
// For ULW mode, initialize turn tracking
|
|
384
|
+
if (mode === 'ulw') {
|
|
385
|
+
this._currentSession.ulw_turns = options?.turns || 100;
|
|
386
|
+
this._currentSession.ulw_turns_used = 0;
|
|
387
|
+
}
|
|
388
|
+
// Send to server if connected
|
|
389
|
+
if (this._activeWs) {
|
|
390
|
+
const msg = { type: 'mode_change', mode };
|
|
391
|
+
if (mode === 'ulw' && options?.turns) {
|
|
392
|
+
msg.turns = options.turns;
|
|
393
|
+
}
|
|
394
|
+
this._activeWs.send(JSON.stringify(msg));
|
|
395
|
+
}
|
|
396
|
+
}
|
|
365
397
|
/** Clear session and UI state, start fresh */
|
|
366
398
|
reset() {
|
|
367
399
|
// Close active WebSocket if any
|
|
@@ -372,11 +404,6 @@ class RemoteAgent {
|
|
|
372
404
|
catch { /* ignore */ }
|
|
373
405
|
this._activeWs = null;
|
|
374
406
|
}
|
|
375
|
-
// Clear session from localStorage
|
|
376
|
-
if (this._currentSession?.session_id) {
|
|
377
|
-
this._clearSession(this._currentSession.session_id);
|
|
378
|
-
}
|
|
379
|
-
this._clearSessionId();
|
|
380
407
|
this._currentSession = null;
|
|
381
408
|
this._chatItems = [];
|
|
382
409
|
this._status = 'idle';
|
|
@@ -391,17 +418,19 @@ class RemoteAgent {
|
|
|
391
418
|
* Returns Response with text and done flag.
|
|
392
419
|
*
|
|
393
420
|
* @param prompt - User's input
|
|
394
|
-
* @param
|
|
421
|
+
* @param options - Optional settings: images (base64 data URLs), timeoutMs
|
|
395
422
|
* @returns Response with text and done flag
|
|
396
423
|
*/
|
|
397
|
-
async input(prompt,
|
|
398
|
-
|
|
424
|
+
async input(prompt, options) {
|
|
425
|
+
const timeoutMs = options?.timeoutMs ?? 600000;
|
|
426
|
+
const images = options?.images;
|
|
427
|
+
return this._streamInput(prompt, timeoutMs, images);
|
|
399
428
|
}
|
|
400
429
|
/**
|
|
401
430
|
* Async version of input (same as input, for API consistency with Python).
|
|
402
431
|
*/
|
|
403
|
-
async inputAsync(prompt,
|
|
404
|
-
return this.input(prompt,
|
|
432
|
+
async inputAsync(prompt, options) {
|
|
433
|
+
return this.input(prompt, options);
|
|
405
434
|
}
|
|
406
435
|
/**
|
|
407
436
|
* Respond to ask_user event.
|
|
@@ -463,6 +492,24 @@ class RemoteAgent {
|
|
|
463
492
|
...signed,
|
|
464
493
|
}));
|
|
465
494
|
}
|
|
495
|
+
/**
|
|
496
|
+
* Respond to ULW turns reached event.
|
|
497
|
+
* Only valid when status === 'waiting' after an ulw_turns_reached event.
|
|
498
|
+
*
|
|
499
|
+
* @param action - 'continue' to extend turns, or 'switch_mode' to change mode
|
|
500
|
+
* @param options - turns to extend (for continue) or mode to switch to
|
|
501
|
+
*/
|
|
502
|
+
respondToUlwTurnsReached(action, options) {
|
|
503
|
+
if (!this._activeWs) {
|
|
504
|
+
throw new Error('No active connection to respond to ULW');
|
|
505
|
+
}
|
|
506
|
+
this._activeWs.send(JSON.stringify({
|
|
507
|
+
type: 'ULW_RESPONSE',
|
|
508
|
+
action,
|
|
509
|
+
...(action === 'continue' && options?.turns && { turns: options.turns }),
|
|
510
|
+
...(action === 'switch_mode' && options?.mode && { mode: options.mode }),
|
|
511
|
+
}));
|
|
512
|
+
}
|
|
466
513
|
// ==========================================================================
|
|
467
514
|
// Private Methods - Streaming
|
|
468
515
|
// ==========================================================================
|
|
@@ -524,107 +571,6 @@ class RemoteAgent {
|
|
|
524
571
|
}
|
|
525
572
|
throw new Error('Polling timeout: Result not ready after maximum attempts');
|
|
526
573
|
}
|
|
527
|
-
/**
|
|
528
|
-
* Save session ID to localStorage (browser only).
|
|
529
|
-
*/
|
|
530
|
-
_saveSessionId(sessionId) {
|
|
531
|
-
if (isBrowserEnv() && typeof globalThis !== 'undefined' && 'localStorage' in globalThis) {
|
|
532
|
-
try {
|
|
533
|
-
globalThis.localStorage.setItem('connectonion_active_session', sessionId);
|
|
534
|
-
}
|
|
535
|
-
catch {
|
|
536
|
-
// Ignore localStorage errors
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
/**
|
|
541
|
-
* Clear session ID from localStorage (browser only).
|
|
542
|
-
* Also clears the full session data.
|
|
543
|
-
*/
|
|
544
|
-
_clearSessionId() {
|
|
545
|
-
if (isBrowserEnv() && typeof globalThis !== 'undefined' && 'localStorage' in globalThis) {
|
|
546
|
-
try {
|
|
547
|
-
// Get active session_id and clear its data
|
|
548
|
-
const activeId = globalThis.localStorage.getItem('connectonion_active_session');
|
|
549
|
-
if (activeId) {
|
|
550
|
-
this._clearSession(activeId);
|
|
551
|
-
}
|
|
552
|
-
globalThis.localStorage.removeItem('connectonion_active_session');
|
|
553
|
-
}
|
|
554
|
-
catch {
|
|
555
|
-
// Ignore localStorage errors
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
/**
|
|
560
|
-
* Save full session state to localStorage (browser only).
|
|
561
|
-
* Enables conversation recovery after browser refresh.
|
|
562
|
-
*/
|
|
563
|
-
_saveSession(session) {
|
|
564
|
-
if (isBrowserEnv() && typeof globalThis !== 'undefined' && 'localStorage' in globalThis && session.session_id) {
|
|
565
|
-
try {
|
|
566
|
-
const sessionWithTimestamp = {
|
|
567
|
-
...session,
|
|
568
|
-
_savedAt: Date.now()
|
|
569
|
-
};
|
|
570
|
-
const key = `connectonion_session_${session.session_id}`;
|
|
571
|
-
globalThis.localStorage.setItem(key, JSON.stringify(sessionWithTimestamp));
|
|
572
|
-
}
|
|
573
|
-
catch {
|
|
574
|
-
// Ignore localStorage errors (quota exceeded, etc.)
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
/**
|
|
579
|
-
* Load session state from localStorage (browser only).
|
|
580
|
-
* Returns null if session doesn't exist.
|
|
581
|
-
*/
|
|
582
|
-
_loadSession(sessionId) {
|
|
583
|
-
if (isBrowserEnv() && typeof globalThis !== 'undefined' && 'localStorage' in globalThis) {
|
|
584
|
-
try {
|
|
585
|
-
const key = `connectonion_session_${sessionId}`;
|
|
586
|
-
const stored = globalThis.localStorage.getItem(key);
|
|
587
|
-
if (stored) {
|
|
588
|
-
const data = JSON.parse(stored);
|
|
589
|
-
// Remove timestamp before returning
|
|
590
|
-
delete data._savedAt;
|
|
591
|
-
return data;
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
catch {
|
|
595
|
-
// Ignore localStorage errors
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
return null;
|
|
599
|
-
}
|
|
600
|
-
/**
|
|
601
|
-
* Load active session ID from localStorage (browser only).
|
|
602
|
-
*/
|
|
603
|
-
_loadActiveSessionId() {
|
|
604
|
-
if (isBrowserEnv() && typeof globalThis !== 'undefined' && 'localStorage' in globalThis) {
|
|
605
|
-
try {
|
|
606
|
-
return globalThis.localStorage.getItem('connectonion_active_session');
|
|
607
|
-
}
|
|
608
|
-
catch {
|
|
609
|
-
return null;
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
return null;
|
|
613
|
-
}
|
|
614
|
-
/**
|
|
615
|
-
* Clear specific session from localStorage (browser only).
|
|
616
|
-
*/
|
|
617
|
-
_clearSession(sessionId) {
|
|
618
|
-
if (isBrowserEnv() && typeof globalThis !== 'undefined' && 'localStorage' in globalThis) {
|
|
619
|
-
try {
|
|
620
|
-
const key = `connectonion_session_${sessionId}`;
|
|
621
|
-
globalThis.localStorage.removeItem(key);
|
|
622
|
-
}
|
|
623
|
-
catch {
|
|
624
|
-
// Ignore localStorage errors
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
574
|
/**
|
|
629
575
|
* Start health check interval to detect dead connections.
|
|
630
576
|
* Checks if PING received within 60 seconds, attempts reconnect if not.
|
|
@@ -644,16 +590,13 @@ class RemoteAgent {
|
|
|
644
590
|
if (this._enablePolling) {
|
|
645
591
|
this._pollForResult(sessionId)
|
|
646
592
|
.then((result) => {
|
|
647
|
-
this._clearSessionId();
|
|
648
593
|
resolve({ text: result, done: true });
|
|
649
594
|
})
|
|
650
595
|
.catch((pollError) => {
|
|
651
|
-
this._clearSessionId();
|
|
652
596
|
reject(new Error(`Connection health check failed and polling failed: ${pollError}`));
|
|
653
597
|
});
|
|
654
598
|
}
|
|
655
599
|
else {
|
|
656
|
-
this._clearSessionId();
|
|
657
600
|
reject(new Error('Connection health check failed: No PING received for 60 seconds'));
|
|
658
601
|
}
|
|
659
602
|
}
|
|
@@ -690,29 +633,20 @@ class RemoteAgent {
|
|
|
690
633
|
* Stream input to agent with real-time UI events.
|
|
691
634
|
* Uses directUrl if provided, otherwise tries auto-discovery, then falls back to relay.
|
|
692
635
|
*/
|
|
693
|
-
async _streamInput(prompt, timeoutMs) {
|
|
636
|
+
async _streamInput(prompt, timeoutMs, images) {
|
|
694
637
|
// Lazy-load keys on first use
|
|
695
638
|
this._ensureKeys();
|
|
696
639
|
// Try endpoint resolution (once, cached)
|
|
697
640
|
await this._tryResolveEndpoint();
|
|
698
641
|
// Add user event to UI
|
|
699
|
-
this._addChatItem({ type: 'user', content: prompt });
|
|
642
|
+
this._addChatItem({ type: 'user', content: prompt, images });
|
|
700
643
|
// Add optimistic thinking (shows immediately, removed when real events arrive)
|
|
701
644
|
this._addChatItem({ type: 'thinking', id: '__optimistic__', status: 'running' });
|
|
702
645
|
// Set status to working
|
|
703
646
|
this._status = 'working';
|
|
704
647
|
const inputId = generateUUID();
|
|
705
|
-
//
|
|
706
|
-
const activeSessionId = this._loadActiveSessionId();
|
|
707
|
-
if (activeSessionId && !this._currentSession) {
|
|
708
|
-
const loaded = this._loadSession(activeSessionId);
|
|
709
|
-
if (loaded) {
|
|
710
|
-
this._currentSession = loaded;
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
// Generate or reuse session_id for tracking and polling fallback
|
|
648
|
+
// Reuse existing session_id or generate new one for tracking and polling fallback
|
|
714
649
|
const sessionId = this._currentSession?.session_id || generateUUID();
|
|
715
|
-
this._saveSessionId(sessionId);
|
|
716
650
|
// Choose connection mode: direct (explicit or resolved) or via relay
|
|
717
651
|
let wsUrl;
|
|
718
652
|
let isDirect = false;
|
|
@@ -781,6 +715,10 @@ class RemoteAgent {
|
|
|
781
715
|
prompt,
|
|
782
716
|
...signed,
|
|
783
717
|
};
|
|
718
|
+
// Add images if provided
|
|
719
|
+
if (images && images.length > 0) {
|
|
720
|
+
inputMessage.images = images;
|
|
721
|
+
}
|
|
784
722
|
// Only include 'to' for relay mode
|
|
785
723
|
if (!isDirect) {
|
|
786
724
|
inputMessage.to = this.address;
|
|
@@ -827,6 +765,31 @@ class RemoteAgent {
|
|
|
827
765
|
}
|
|
828
766
|
return;
|
|
829
767
|
}
|
|
768
|
+
// Handle session_sync (separate message to avoid circular ref on server)
|
|
769
|
+
if (data?.type === 'session_sync' && data.session) {
|
|
770
|
+
this._currentSession = data.session;
|
|
771
|
+
}
|
|
772
|
+
// Handle mode_changed (agent or user changed approval mode)
|
|
773
|
+
if (data?.type === 'mode_changed' && data.mode) {
|
|
774
|
+
if (!this._currentSession) {
|
|
775
|
+
this._currentSession = { mode: data.mode };
|
|
776
|
+
}
|
|
777
|
+
else {
|
|
778
|
+
this._currentSession.mode = data.mode;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
// Handle ulw_turns_reached (ULW mode hit max turns)
|
|
782
|
+
if (data?.type === 'ulw_turns_reached') {
|
|
783
|
+
this._status = 'waiting';
|
|
784
|
+
if (this._currentSession) {
|
|
785
|
+
this._currentSession.ulw_turns_used = data.turns_used;
|
|
786
|
+
}
|
|
787
|
+
this._addChatItem({
|
|
788
|
+
type: 'ulw_turns_reached',
|
|
789
|
+
turns_used: data.turns_used,
|
|
790
|
+
max_turns: data.max_turns,
|
|
791
|
+
});
|
|
792
|
+
}
|
|
830
793
|
// Handle streaming events
|
|
831
794
|
if (data?.type === 'llm_call' || data?.type === 'llm_result' ||
|
|
832
795
|
data?.type === 'tool_call' || data?.type === 'tool_result' ||
|
|
@@ -834,6 +797,10 @@ class RemoteAgent {
|
|
|
834
797
|
data?.type === 'intent' || data?.type === 'eval' || data?.type === 'compact' ||
|
|
835
798
|
data?.type === 'tool_blocked') {
|
|
836
799
|
this._handleStreamEvent(data);
|
|
800
|
+
// Sync session state from each event (legacy, kept for backwards compat)
|
|
801
|
+
if (data.session) {
|
|
802
|
+
this._currentSession = data.session;
|
|
803
|
+
}
|
|
837
804
|
}
|
|
838
805
|
// Handle ask_user event (agent needs more input)
|
|
839
806
|
// Keep WebSocket OPEN - user will respond via respond() method
|
|
@@ -921,11 +888,11 @@ class RemoteAgent {
|
|
|
921
888
|
settled = true;
|
|
922
889
|
clearTimeout(timer);
|
|
923
890
|
this._stopHealthCheck();
|
|
891
|
+
this._removeOptimisticThinking();
|
|
924
892
|
this._status = 'idle';
|
|
925
|
-
// Sync session from server
|
|
893
|
+
// Sync session from server
|
|
926
894
|
if (data.session) {
|
|
927
895
|
this._currentSession = data.session;
|
|
928
|
-
this._saveSession(data.session); // Save for next message
|
|
929
896
|
}
|
|
930
897
|
// Add agent response to UI (skip if already added via 'assistant' event)
|
|
931
898
|
const result = data.result || '';
|
|
@@ -1121,6 +1088,31 @@ class RemoteAgent {
|
|
|
1121
1088
|
}
|
|
1122
1089
|
break;
|
|
1123
1090
|
}
|
|
1091
|
+
case 'agent_image': {
|
|
1092
|
+
// Agent sent an image to display
|
|
1093
|
+
const imageData = event.image;
|
|
1094
|
+
if (imageData) {
|
|
1095
|
+
// Find the last agent message and add the image to it
|
|
1096
|
+
const lastAgent = this._chatItems.filter((e) => e.type === 'agent').pop();
|
|
1097
|
+
if (lastAgent) {
|
|
1098
|
+
// Add image to existing agent message
|
|
1099
|
+
if (!lastAgent.images) {
|
|
1100
|
+
lastAgent.images = [];
|
|
1101
|
+
}
|
|
1102
|
+
lastAgent.images.push(imageData);
|
|
1103
|
+
}
|
|
1104
|
+
else {
|
|
1105
|
+
// No agent message yet - create one with just the image
|
|
1106
|
+
this._addChatItem({
|
|
1107
|
+
type: 'agent',
|
|
1108
|
+
id: event.id != null ? String(event.id) : undefined,
|
|
1109
|
+
content: '',
|
|
1110
|
+
images: [imageData],
|
|
1111
|
+
});
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
break;
|
|
1115
|
+
}
|
|
1124
1116
|
case 'intent': {
|
|
1125
1117
|
// Intent analysis from system_reminder plugin
|
|
1126
1118
|
const intentId = event.id;
|
package/dist/react/index.d.ts
CHANGED
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
* }
|
|
18
18
|
* ```
|
|
19
19
|
*/
|
|
20
|
-
import { Response, ChatItem, AgentStatus, ConnectOptions } from '../connect';
|
|
21
|
-
export type { Response, ChatItem, ChatItemType, AgentStatus, ConnectOptions, AgentInfo, } from '../connect';
|
|
20
|
+
import { Response, ChatItem, AgentStatus, ConnectOptions, ApprovalMode } from '../connect';
|
|
21
|
+
export type { Response, ChatItem, ChatItemType, AgentStatus, ConnectOptions, AgentInfo, ApprovalMode, } from '../connect';
|
|
22
22
|
export { fetchAgentInfo } from '../connect';
|
|
23
23
|
/** @deprecated Use ChatItem instead */
|
|
24
24
|
export type UIEvent = ChatItem;
|
|
@@ -26,8 +26,9 @@ export type UIEvent = ChatItem;
|
|
|
26
26
|
* localStorage structure:
|
|
27
27
|
*
|
|
28
28
|
* co:agent:{address}:session:{sessionId}
|
|
29
|
-
* → { messages, ui, createdAt, updatedAt }
|
|
29
|
+
* → { messages, ui, session, createdAt, updatedAt }
|
|
30
30
|
* → Each session stored separately by sessionId
|
|
31
|
+
* → session contains full SessionState (messages, trace, turn) synced from server
|
|
31
32
|
*
|
|
32
33
|
* Future: useSessions hook for session management
|
|
33
34
|
*/
|
|
@@ -48,13 +49,30 @@ export interface UseAgentReturn {
|
|
|
48
49
|
sessionId: string;
|
|
49
50
|
isProcessing: boolean;
|
|
50
51
|
error: Error | null;
|
|
51
|
-
|
|
52
|
+
/** Current approval mode: 'safe' | 'plan' | 'accept_edits' | 'ulw' */
|
|
53
|
+
mode: ApprovalMode;
|
|
54
|
+
/** ULW mode: max turns before pausing */
|
|
55
|
+
ulwTurns: number | null;
|
|
56
|
+
/** ULW mode: turns used so far */
|
|
57
|
+
ulwTurnsUsed: number | null;
|
|
58
|
+
input: (prompt: string, options?: {
|
|
59
|
+
images?: string[];
|
|
60
|
+
timeoutMs?: number;
|
|
61
|
+
}) => Promise<Response>;
|
|
52
62
|
respond: (answer: string | string[]) => void;
|
|
53
63
|
respondToApproval: (approved: boolean, scope?: 'once' | 'session', mode?: 'reject_soft' | 'reject_hard' | 'reject_explain', feedback?: string) => void;
|
|
64
|
+
respondToUlwTurnsReached: (action: 'continue' | 'switch_mode', options?: {
|
|
65
|
+
turns?: number;
|
|
66
|
+
mode?: ApprovalMode;
|
|
67
|
+
}) => void;
|
|
54
68
|
submitOnboard: (options: {
|
|
55
69
|
inviteCode?: string;
|
|
56
70
|
payment?: number;
|
|
57
71
|
}) => void;
|
|
72
|
+
/** Change approval mode: 'safe' | 'plan' | 'accept_edits' | 'ulw' */
|
|
73
|
+
setMode: (mode: ApprovalMode, options?: {
|
|
74
|
+
turns?: number;
|
|
75
|
+
}) => void;
|
|
58
76
|
reset: () => void;
|
|
59
77
|
}
|
|
60
78
|
export interface UseAgentOptions extends ConnectOptions {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAKH,OAAO,EAGL,QAAQ,EACR,QAAQ,EACR,WAAW,EACX,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"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"}
|
package/dist/react/index.js
CHANGED
|
@@ -36,6 +36,7 @@ function createInitialState() {
|
|
|
36
36
|
return {
|
|
37
37
|
messages: [],
|
|
38
38
|
ui: [],
|
|
39
|
+
session: null,
|
|
39
40
|
status: 'idle',
|
|
40
41
|
error: null,
|
|
41
42
|
createdAt: now,
|
|
@@ -51,6 +52,7 @@ function createAgentStore(address, sessionId) {
|
|
|
51
52
|
...createInitialState(),
|
|
52
53
|
setStatus: (status) => set({ status }),
|
|
53
54
|
setUI: (ui) => set({ ui, updatedAt: Date.now() }),
|
|
55
|
+
setSession: (session) => set({ session, updatedAt: Date.now() }),
|
|
54
56
|
setError: (error) => set({ error }),
|
|
55
57
|
updateMessages: (messages) => set({ messages, updatedAt: Date.now() }),
|
|
56
58
|
reset: () => set(createInitialState()),
|
|
@@ -61,6 +63,7 @@ function createAgentStore(address, sessionId) {
|
|
|
61
63
|
partialize: (state) => ({
|
|
62
64
|
messages: state.messages,
|
|
63
65
|
ui: state.ui,
|
|
66
|
+
session: state.session,
|
|
64
67
|
createdAt: state.createdAt,
|
|
65
68
|
updatedAt: state.updatedAt,
|
|
66
69
|
}),
|
|
@@ -88,11 +91,13 @@ function useAgent(address, options) {
|
|
|
88
91
|
// State from store
|
|
89
92
|
const messages = useStore((s) => s.messages);
|
|
90
93
|
const ui = useStore((s) => s.ui);
|
|
94
|
+
const session = useStore((s) => s.session);
|
|
91
95
|
const status = useStore((s) => s.status);
|
|
92
96
|
const error = useStore((s) => s.error);
|
|
93
97
|
// Actions from store
|
|
94
98
|
const setStatus = useStore((s) => s.setStatus);
|
|
95
99
|
const setUI = useStore((s) => s.setUI);
|
|
100
|
+
const setSession = useStore((s) => s.setSession);
|
|
96
101
|
const setError = useStore((s) => s.setError);
|
|
97
102
|
const updateMessages = useStore((s) => s.updateMessages);
|
|
98
103
|
const resetStore = useStore((s) => s.reset);
|
|
@@ -110,35 +115,40 @@ function useAgent(address, options) {
|
|
|
110
115
|
const agent = agentRef.current;
|
|
111
116
|
// Restore session to agent on mount or when sessionId changes
|
|
112
117
|
(0, react_1.useEffect)(() => {
|
|
113
|
-
if (
|
|
114
|
-
agent._currentSession = {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
118
|
+
if (session) {
|
|
119
|
+
agent._currentSession = { ...session, session_id: sessionId };
|
|
120
|
+
agent._chatItems = [...ui];
|
|
121
|
+
}
|
|
122
|
+
else if (messages.length > 0) {
|
|
123
|
+
agent._currentSession = { session_id: sessionId, messages };
|
|
119
124
|
agent._chatItems = [...ui];
|
|
120
125
|
}
|
|
121
126
|
}, [sessionId]);
|
|
122
|
-
const input = async (prompt,
|
|
127
|
+
const input = async (prompt, options) => {
|
|
123
128
|
setError(null);
|
|
124
129
|
setStatus('working');
|
|
125
|
-
// Set session before request
|
|
126
|
-
agent._currentSession =
|
|
127
|
-
session_id: sessionId
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
// Poll for UI updates
|
|
130
|
+
// Set session before request (restore from store for server to continue conversation)
|
|
131
|
+
agent._currentSession = session
|
|
132
|
+
? { ...session, session_id: sessionId }
|
|
133
|
+
: { session_id: sessionId, messages };
|
|
134
|
+
// Poll for UI updates and session state
|
|
131
135
|
const pollInterval = setInterval(() => {
|
|
132
136
|
setUI([...agent.ui]);
|
|
133
137
|
setStatus(agent.status);
|
|
138
|
+
// Sync session from agent on every tick (server sends session with each trace event)
|
|
139
|
+
if (agent.currentSession) {
|
|
140
|
+
setSession(agent.currentSession);
|
|
141
|
+
}
|
|
134
142
|
}, 50);
|
|
135
143
|
try {
|
|
136
|
-
const response = await agent.input(prompt,
|
|
137
|
-
// Persist messages
|
|
138
|
-
if (agent.currentSession?.messages) {
|
|
139
|
-
updateMessages(agent.currentSession.messages);
|
|
140
|
-
}
|
|
144
|
+
const response = await agent.input(prompt, options);
|
|
141
145
|
// Final sync
|
|
146
|
+
if (agent.currentSession) {
|
|
147
|
+
setSession(agent.currentSession);
|
|
148
|
+
if (agent.currentSession.messages) {
|
|
149
|
+
updateMessages(agent.currentSession.messages);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
142
152
|
setUI([...agent.ui]);
|
|
143
153
|
setStatus(agent.status);
|
|
144
154
|
return response;
|
|
@@ -166,16 +176,36 @@ function useAgent(address, options) {
|
|
|
166
176
|
const submitOnboard = (options) => {
|
|
167
177
|
agent.submitOnboard(options);
|
|
168
178
|
};
|
|
179
|
+
const respondToUlwTurnsReached = (action, options) => {
|
|
180
|
+
agent.respondToUlwTurnsReached(action, options);
|
|
181
|
+
};
|
|
182
|
+
const setMode = (mode, options) => {
|
|
183
|
+
agent.setMode(mode, options);
|
|
184
|
+
// Update local session state to reflect mode change immediately
|
|
185
|
+
if (session) {
|
|
186
|
+
const updates = { mode };
|
|
187
|
+
if (mode === 'ulw') {
|
|
188
|
+
updates.ulw_turns = options?.turns || 100;
|
|
189
|
+
updates.ulw_turns_used = 0;
|
|
190
|
+
}
|
|
191
|
+
setSession({ ...session, ...updates });
|
|
192
|
+
}
|
|
193
|
+
};
|
|
169
194
|
return {
|
|
170
195
|
status,
|
|
171
196
|
ui,
|
|
172
197
|
sessionId,
|
|
173
198
|
isProcessing: status !== 'idle',
|
|
174
199
|
error,
|
|
200
|
+
mode: session?.mode || 'safe',
|
|
201
|
+
ulwTurns: session?.ulw_turns ?? null,
|
|
202
|
+
ulwTurnsUsed: session?.ulw_turns_used ?? null,
|
|
175
203
|
input,
|
|
176
204
|
respond,
|
|
177
205
|
respondToApproval,
|
|
206
|
+
respondToUlwTurnsReached,
|
|
178
207
|
submitOnboard,
|
|
208
|
+
setMode,
|
|
179
209
|
reset,
|
|
180
210
|
};
|
|
181
211
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "connectonion",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.11",
|
|
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",
|