nanocodex 0.6.5 → 0.6.7
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/README.md +311 -6
- package/THIRD-PARTY-LICENSES +23 -0
- package/browser/Agent.d.mts +5 -4
- package/browser/Agent.mjs +4 -0
- package/browser/Claude.d.mts +4 -0
- package/browser/Claude.mjs +11 -0
- package/browser/InlineAgent.mjs +19 -1
- package/browser/Voice.mjs +11 -4
- package/browser/VoiceSession.mjs +20 -6
- package/browser/host.d.mts +54 -1
- package/browser/host.mjs +65 -16
- package/browser/hostManagedWebSocket.mjs +11 -2
- package/browser/index.d.mts +3 -10
- package/browser/index.mjs +2 -5
- package/browser/socket-observations.mjs +203 -0
- package/cloud/Decorator.d.mts +2 -2
- package/cloud/Decorator.mjs +1 -0
- package/cloud/Dialog.d.mts +13 -0
- package/cloud/Dialog.mjs +52 -6
- package/cloud/Transport.d.mts +0 -11
- package/cloud/Transport.mjs +1 -386
- package/cloud/actions/account.d.mts +12 -0
- package/cloud/actions/account.mjs +11 -0
- package/cloud/actions/agent.mjs +3 -0
- package/cloud/actions/connection.d.mts +3 -1
- package/cloud/actions/connection.mjs +19 -4
- package/cloud/index.d.mts +1 -1
- package/cloud/index.mjs +1 -1
- package/cloud/internal.mjs +19 -4
- package/cloud/mercator.mjs +1 -1
- package/cloud/mock.d.mts +17 -0
- package/cloud/mock.mjs +392 -0
- package/cloud/types.d.mts +4 -1
- package/cloudflare/Agent.d.mts +6 -0
- package/cloudflare/Agent.mjs +190 -10
- package/cloudflare/agent-settings.d.mts +1 -1
- package/cloudflare/agent-settings.mjs +12 -3
- package/cloudflare/egress.mjs +9 -6
- package/cloudflare/gateway-binding-responses.mjs +3 -3
- package/cloudflare/gateway-responses.d.mts +1 -0
- package/cloudflare/gateway-responses.mjs +7 -7
- package/cloudflare/managed-auth.d.mts +4 -4
- package/cloudflare/managed-auth.mjs +20 -2
- package/cloudflare/managed-live.d.mts +6 -0
- package/cloudflare/managed-live.mjs +56 -1
- package/cloudflare/tracing.d.mts +16 -0
- package/cloudflare/tracing.mjs +47 -0
- package/cloudflare/workers-ai-responses.d.mts +3 -2
- package/cloudflare/workers-ai-responses.mjs +83 -8
- package/host/Agent.d.mts +6 -2
- package/host/Claude.d.mts +3 -0
- package/host/Claude.mjs +2 -0
- package/host/index.d.mts +6 -10
- package/host/index.mjs +2 -5
- package/index.d.mts +11 -10
- package/index.mjs +0 -5
- package/internal.mjs +33 -3
- package/managed/Agent.d.mts +10 -3
- package/managed/Agent.mjs +60 -10
- package/managed/ManagedError.d.mts +3 -0
- package/managed/Voice.mjs +3 -1
- package/managed/internal.mjs +2 -1
- package/node/Agent.d.mts +6 -2
- package/node/Agent.mjs +14 -1
- package/node/Claude.d.mts +4 -0
- package/node/Claude.mjs +24 -0
- package/node/host.mjs +18 -9
- package/node/index.d.mts +6 -10
- package/node/index.mjs +2 -5
- package/package.json +45 -9
- package/pkg-node/nanocodex.d.ts +123 -2
- package/pkg-node/nanocodex.js +879 -89
- package/pkg-node/nanocodex_bg.wasm.d.ts +49 -3
- package/pkg-web/.nanocodex-bindgen-stamp +4 -4
- package/pkg-web/nanocodex-build.json +1 -1
- package/pkg-web/nanocodex.d.ts +172 -5
- package/pkg-web/nanocodex.js +872 -88
- package/pkg-web/nanocodex_bg.js +872 -88
- package/pkg-web/nanocodex_bg.wasm +0 -0
- package/pkg-web/nanocodex_bg.wasm.d.ts +49 -3
- package/pkg-web/nanocodex_worker.js +1 -1
- package/runtime/claude-host.mjs +308 -0
- package/runtime/claude-subscription.mjs +79 -0
- package/runtime/claude.d.mts +112 -0
- package/runtime/claude.mjs +221 -0
- package/runtime/code-effect-identity.mjs +123 -0
- package/runtime/harnesses.mjs +51 -0
- package/runtime/mcp-payment.mjs +8 -0
- package/runtime/mcp-runtime.d.mts +10 -0
- package/runtime/mcp-runtime.mjs +242 -55
- package/runtime/mercator.mjs +2 -0
- package/runtime/quickjs-evaluator.mjs +23 -11
- package/runtime/response-controls.mjs +16 -10
- package/runtime/responses-http.mjs +4 -2
- package/runtime/retry-after.d.mts +10 -0
- package/runtime/retry-after.mjs +86 -0
- package/runtime/steer-receipt.mjs +8 -1
- package/runtime/subagent-routing.d.mts +4 -2
- package/runtime/subagent-routing.mjs +9 -3
- package/runtime/subagents.d.mts +8 -3
- package/services/account.d.mts +76 -0
- package/services/account.mjs +151 -0
- package/services/hosted.mjs +72 -0
- package/services/index.d.mts +95 -0
- package/services/index.mjs +192 -0
- package/services/scope.mjs +36 -0
- package/{runtime/tempo-provider.d.mts → tempo/index.d.mts} +20 -1
- package/{runtime/tempo-provider.mjs → tempo/index.mjs} +39 -3
- package/tools/browser/browserShell.mjs +18 -8
- package/tools/browser/python.worker.mjs +17 -1
- package/tools/browser/threadGit.mjs +12 -2
- package/tools/environment.d.mts +17 -0
- package/tools/environment.mjs +24 -1
- package/tools/index.d.mts +6 -3
- package/types.d.mts +107 -6
- package/worker/Claude.d.mts +4 -0
- package/worker/Claude.mjs +11 -0
- package/worker/ClaudeSubscription.d.mts +24 -0
- package/worker/ClaudeSubscription.mjs +9 -0
- package/worker/index.d.mts +9 -0
- package/worker/index.mjs +4 -0
package/browser/VoiceSession.mjs
CHANGED
|
@@ -90,6 +90,7 @@ export class BrowserVoiceSession {
|
|
|
90
90
|
#elevenLabs;
|
|
91
91
|
#muted = false;
|
|
92
92
|
#inputGeneration = 0;
|
|
93
|
+
#playbackRevision = 0;
|
|
93
94
|
#meterTimer;
|
|
94
95
|
#backendReady;
|
|
95
96
|
#resolveBackendReady;
|
|
@@ -267,7 +268,7 @@ export class BrowserVoiceSession {
|
|
|
267
268
|
const channel = this.#channel;
|
|
268
269
|
channel.addEventListener("message", (event) => {
|
|
269
270
|
if (this.#directControl && !this.#closed && this.#channel === channel) {
|
|
270
|
-
this.#receiveControl(event.data, () => this.#channel === channel);
|
|
271
|
+
this.#receiveControl(event.data, () => this.#channel === channel && channel.readyState === "open");
|
|
271
272
|
}
|
|
272
273
|
});
|
|
273
274
|
channel.addEventListener("close", () => {
|
|
@@ -311,6 +312,7 @@ export class BrowserVoiceSession {
|
|
|
311
312
|
}
|
|
312
313
|
|
|
313
314
|
noteTypedInput() {
|
|
315
|
+
this.#playbackRevision++;
|
|
314
316
|
this.#playbackEnabled = false;
|
|
315
317
|
this.#elevenLabs?.interrupt();
|
|
316
318
|
this.#speaker?.setEnabled(false);
|
|
@@ -429,14 +431,19 @@ export class BrowserVoiceSession {
|
|
|
429
431
|
for (const text of effects.undelivered_answers ?? []) this.#options.onUndeliveredAnswer?.(text);
|
|
430
432
|
if (effects.input_generation !== undefined) {
|
|
431
433
|
if (effects.input_generation < this.#inputGeneration) return;
|
|
432
|
-
if (effects.input_generation > this.#inputGeneration)
|
|
434
|
+
if (effects.input_generation > this.#inputGeneration) {
|
|
435
|
+
this.#playbackRevision++;
|
|
436
|
+
this.#elevenLabs?.interrupt();
|
|
437
|
+
}
|
|
433
438
|
this.#inputGeneration = effects.input_generation;
|
|
434
439
|
}
|
|
435
440
|
if (effects.playback_enabled === false) {
|
|
441
|
+
this.#playbackRevision++;
|
|
436
442
|
this.#playbackEnabled = false;
|
|
437
443
|
this.#elevenLabs?.interrupt();
|
|
438
444
|
this.#speaker?.setEnabled(false);
|
|
439
445
|
}
|
|
446
|
+
const playbackRevision = this.#playbackRevision;
|
|
440
447
|
let sent = 0;
|
|
441
448
|
for (const frame of effects.frames ?? []) {
|
|
442
449
|
if (this.#directControl && this.#channel?.readyState === "open") {
|
|
@@ -448,12 +455,15 @@ export class BrowserVoiceSession {
|
|
|
448
455
|
}
|
|
449
456
|
}
|
|
450
457
|
if (effects.acknowledge_frames && sent > 0) await this.#core?.framesSent(sent);
|
|
451
|
-
|
|
458
|
+
// A live interruption can overtake asynchronous frame acknowledgement.
|
|
459
|
+
// Keep its speaker gate authoritative while retaining accepted captions.
|
|
460
|
+
const currentPlayback = playbackRevision === this.#playbackRevision;
|
|
461
|
+
if (currentPlayback && !this.#closed && effects.playback_enabled === true && sent === (effects.frames?.length ?? 0)) {
|
|
452
462
|
this.#playbackEnabled = true;
|
|
453
463
|
this.#speaker?.setEnabled(!this.#elevenLabs);
|
|
454
464
|
}
|
|
455
465
|
for (const entry of effects.transcripts ?? []) {
|
|
456
|
-
if (this.#playbackEnabled && !this.#closed) this.#elevenLabs?.transcript(entry);
|
|
466
|
+
if (currentPlayback && this.#playbackEnabled && !this.#closed) this.#elevenLabs?.transcript(entry);
|
|
457
467
|
this.#options.onTranscript(entry.speaker, entry.text, entry);
|
|
458
468
|
}
|
|
459
469
|
if (effects.status) this.#status(effects.status);
|
|
@@ -490,7 +500,7 @@ export class BrowserVoiceSession {
|
|
|
490
500
|
let opened = false;
|
|
491
501
|
sideband.addEventListener("message", (event) => {
|
|
492
502
|
if (!this.#closed && generation === this.#sidebandGeneration) {
|
|
493
|
-
this.#receiveControl(event.data, () => generation === this.#sidebandGeneration);
|
|
503
|
+
this.#receiveControl(event.data, () => generation === this.#sidebandGeneration && sideband.readyState === WebSocket.OPEN);
|
|
494
504
|
}
|
|
495
505
|
});
|
|
496
506
|
sideband.addEventListener("close", () => {
|
|
@@ -516,7 +526,11 @@ export class BrowserVoiceSession {
|
|
|
516
526
|
this.#applyLive(async () => {
|
|
517
527
|
if (!this.#directControl) await this.#admission;
|
|
518
528
|
if (this.#closed || !isCurrent()) return;
|
|
519
|
-
|
|
529
|
+
const requiresAdmission = await this.#core.requiresAgentAdmission(payload);
|
|
530
|
+
// Classification can yield while this transport closes or is replaced.
|
|
531
|
+
// Only requests accepted on the current connection may enter the queue.
|
|
532
|
+
if (this.#closed || !isCurrent()) return;
|
|
533
|
+
if (requiresAdmission) {
|
|
520
534
|
// Retain accepted requests on close, but keep captions and interruptions
|
|
521
535
|
// independent of the durable route and its ordered task queue.
|
|
522
536
|
void this.#enqueue(async () => {
|
package/browser/host.d.mts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
CodeEvaluator,
|
|
3
|
+
CodeEffectJournal,
|
|
3
4
|
McpServers,
|
|
4
5
|
MppSession,
|
|
5
6
|
SubagentToolContext,
|
|
@@ -51,12 +52,60 @@ export type BrowserWebSocketConnection = {
|
|
|
51
52
|
socket: WebSocket;
|
|
52
53
|
status?: number | undefined;
|
|
53
54
|
requestId?: string | undefined;
|
|
55
|
+
/** @internal Correlation with the separately deployed credential egress. */
|
|
56
|
+
egressRequestId?: string | undefined;
|
|
54
57
|
serverModel?: string | undefined;
|
|
55
58
|
reasoningIncluded?: boolean | undefined;
|
|
56
59
|
turnState?: string | undefined;
|
|
57
60
|
};
|
|
58
61
|
|
|
62
|
+
/** @internal Passive lifecycle metadata; never provider frames or request content. */
|
|
63
|
+
export type BrowserSocketObservation = {
|
|
64
|
+
event: "socket.connecting" | "socket.connect_waiting" | "socket.opened" | "socket.closed" | "socket.error"
|
|
65
|
+
| "request.send_started" | "request.send_waiting" | "request.sent" | "request.waiting" | "request.first_message"
|
|
66
|
+
| "request.first_output" | "request.first_reasoning_delta" | "request.first_answer_delta" | "request.first_tool_delta" | "request.finished" | "provider.timing";
|
|
67
|
+
socket_id: string;
|
|
68
|
+
request_id: string;
|
|
69
|
+
egress_request_id?: string;
|
|
70
|
+
provider_request_id?: string;
|
|
71
|
+
response_id?: string;
|
|
72
|
+
socket_request_index?: number;
|
|
73
|
+
model_call_index?: number;
|
|
74
|
+
phase?: "generation" | "compaction" | "warmup";
|
|
75
|
+
outcome?: "completed" | "failed" | "send_failed" | "superseded";
|
|
76
|
+
elapsed_ms?: number;
|
|
77
|
+
send_wait_ms?: number;
|
|
78
|
+
first_message_ms?: number;
|
|
79
|
+
first_output_ms?: number;
|
|
80
|
+
first_reasoning_delta_ms?: number;
|
|
81
|
+
first_answer_delta_ms?: number;
|
|
82
|
+
first_tool_delta_ms?: number;
|
|
83
|
+
/** Fixed protocol name, or unclassified for oversized/unknown envelopes. */
|
|
84
|
+
provider_event_type?: string;
|
|
85
|
+
output_kind?: "item" | "reasoning" | "answer" | "tool";
|
|
86
|
+
last_message_age_ms?: number;
|
|
87
|
+
received_message_count?: number;
|
|
88
|
+
queued_message_count?: number;
|
|
89
|
+
socket_delivered_message_count?: number;
|
|
90
|
+
socket_queue_residence_max_ms?: number;
|
|
91
|
+
buffered_send_bytes?: number;
|
|
92
|
+
close_code?: number;
|
|
93
|
+
close_clean?: boolean;
|
|
94
|
+
intentional?: boolean;
|
|
95
|
+
pre_inference_ms?: number;
|
|
96
|
+
engine_queue_max_ms?: number;
|
|
97
|
+
engine_service_ttft_total_ms?: number;
|
|
98
|
+
};
|
|
99
|
+
|
|
59
100
|
export function createBrowserHost(options?: {
|
|
101
|
+
/** @internal Trusted runtime configuration carried by nanocodex.browser.internalRuntime. */
|
|
102
|
+
[key: symbol]: {
|
|
103
|
+
traceTool?: <T>(
|
|
104
|
+
name: string,
|
|
105
|
+
context: { sessionId: string; callId: string; parentCallId?: string; turnId?: string },
|
|
106
|
+
run: () => Promise<T>,
|
|
107
|
+
) => Promise<T>;
|
|
108
|
+
} | undefined;
|
|
60
109
|
WebSocketImpl?: typeof WebSocket;
|
|
61
110
|
hostAuth?: boolean;
|
|
62
111
|
hostManagedProtocol?: boolean;
|
|
@@ -74,7 +123,9 @@ export function createBrowserHost(options?: {
|
|
|
74
123
|
/** Remote MCP servers exposed through native and Code Mode tool_search plus deferred tools. */
|
|
75
124
|
mcp?: McpServers;
|
|
76
125
|
codeEvaluator?: CodeEvaluator;
|
|
77
|
-
|
|
126
|
+
/** @internal Trusted durable effect receipts, not available inside guest code. */
|
|
127
|
+
codeEffectJournal?: CodeEffectJournal;
|
|
128
|
+
toolMode?: "code" | "code-only" | "direct";
|
|
78
129
|
/** @internal Live host lifecycle for ephemeral Rust-owned subagents. */
|
|
79
130
|
subagentRouting?: Pick<import('../runtime/subagent-routing.mjs').SubagentRouting, 'resolve' | 'bind'>;
|
|
80
131
|
subagentSessions?: {
|
|
@@ -97,6 +148,8 @@ export function createBrowserHost(options?: {
|
|
|
97
148
|
engine_service_ttft_total_ms?: number;
|
|
98
149
|
}>;
|
|
99
150
|
}) => void;
|
|
151
|
+
/** @internal Live observations while an owned request waits; does not cancel it. */
|
|
152
|
+
onSocketEvent?: (event: BrowserSocketObservation) => void;
|
|
100
153
|
maxQueuedMessages?: number;
|
|
101
154
|
maxQueuedBytes?: number;
|
|
102
155
|
maxBufferedSendBytes?: number;
|
package/browser/host.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { createCodeEffectIdentity } from "../runtime/code-effect-identity.mjs";
|
|
1
2
|
import { createBeforeCompaction } from "../runtime/before-compaction.mjs";
|
|
2
3
|
import { createResponsesHttp, responsesHttpHeaders } from "../runtime/responses-http.mjs";
|
|
3
|
-
import { createCodeRuntime, toolResult } from "../runtime/code-runtime.mjs";
|
|
4
|
+
import { createCodeRuntime, toolResult, traceToolInvocation } from "../runtime/code-runtime.mjs";
|
|
4
5
|
import {
|
|
5
6
|
toolRouterBrand,
|
|
6
7
|
toolRouterRuntime,
|
|
@@ -9,6 +10,7 @@ import {
|
|
|
9
10
|
import { utf8ByteLength } from "../runtime/utf8.mjs";
|
|
10
11
|
import { createWorkerEvaluator } from "../runtime/worker-evaluator.mjs";
|
|
11
12
|
import { openHostManagedWebSocket } from "./hostManagedWebSocket.mjs";
|
|
13
|
+
import { createSocketObservations } from "./socket-observations.mjs";
|
|
12
14
|
|
|
13
15
|
const DEFAULT_MAX_QUEUED_MESSAGES = 4_096;
|
|
14
16
|
const DEFAULT_MAX_QUEUED_BYTES = 32 * 1024 * 1024;
|
|
@@ -19,14 +21,24 @@ const MPP_CLIENT_PROTOCOL_ERROR_CLOSE_CODE = 3008;
|
|
|
19
21
|
const WEBSOCKET_OPEN = 1;
|
|
20
22
|
|
|
21
23
|
export function createBrowserHost(options = {}) {
|
|
24
|
+
const internalRuntime = options[Symbol.for("nanocodex.browser.internalRuntime")];
|
|
25
|
+
if (internalRuntime !== undefined
|
|
26
|
+
&& (!internalRuntime || typeof internalRuntime !== "object" || Array.isArray(internalRuntime))) {
|
|
27
|
+
throw new TypeError("browser host internal runtime options must be an object");
|
|
28
|
+
}
|
|
29
|
+
const traceTool = internalRuntime?.traceTool;
|
|
30
|
+
if (traceTool !== undefined && typeof traceTool !== "function") {
|
|
31
|
+
throw new TypeError("browser host tool tracing hook must be a function");
|
|
32
|
+
}
|
|
22
33
|
const onSocketTiming = options.onSocketTiming;
|
|
23
34
|
if (onSocketTiming !== undefined && typeof onSocketTiming !== "function") {
|
|
24
35
|
throw new TypeError("host socket timing hook must be a function");
|
|
25
36
|
}
|
|
37
|
+
const socketObservations = createSocketObservations(options.onSocketEvent);
|
|
26
38
|
const preservation = createBeforeCompaction(options.beforeCompaction);
|
|
27
39
|
const toolMode = options.toolMode ?? "code";
|
|
28
|
-
if (toolMode !== "code" && toolMode !== "direct") {
|
|
29
|
-
throw new TypeError("toolMode must be code or direct");
|
|
40
|
+
if (toolMode !== "code" && toolMode !== "code-only" && toolMode !== "direct") {
|
|
41
|
+
throw new TypeError("toolMode must be code, code-only or direct");
|
|
30
42
|
}
|
|
31
43
|
const toolsRouter = options.tools?.[toolRouterBrand]
|
|
32
44
|
? options.tools[toolRouterRuntime]
|
|
@@ -38,7 +50,7 @@ export function createBrowserHost(options = {}) {
|
|
|
38
50
|
if (toolsMcp && options.mcp) {
|
|
39
51
|
throw new TypeError("MCP is already configured in Tools");
|
|
40
52
|
}
|
|
41
|
-
if ((toolsMcp || options.mcp) && toolMode
|
|
53
|
+
if ((toolsMcp || options.mcp) && toolMode === "direct") {
|
|
42
54
|
throw new TypeError("remote MCP requires Code Mode");
|
|
43
55
|
}
|
|
44
56
|
const toolsLifecycle = options.tools?.[toolRuntimeLifecycle];
|
|
@@ -76,8 +88,12 @@ export function createBrowserHost(options = {}) {
|
|
|
76
88
|
: () => Promise.reject(new Error(
|
|
77
89
|
"browser Code Mode requires a child Worker or an explicit codeEvaluator",
|
|
78
90
|
)));
|
|
91
|
+
const effectIdentity = createCodeEffectIdentity(options.codeEffectJournal);
|
|
79
92
|
const code = createCodeRuntime(options.tools, {
|
|
93
|
+
traceTool,
|
|
80
94
|
evaluate: codeEvaluator,
|
|
95
|
+
effectJournal: options.codeEffectJournal,
|
|
96
|
+
effectIdentity: options.codeEffectJournal ? effectIdentity.resolve : undefined,
|
|
81
97
|
subagentSessions: options.subagentSessions,
|
|
82
98
|
});
|
|
83
99
|
const toolProviders = options.toolProviders ?? [];
|
|
@@ -192,12 +208,22 @@ export function createBrowserHost(options = {}) {
|
|
|
192
208
|
if (preconnected !== undefined) await closePreconnected();
|
|
193
209
|
ownership = openOwned(() => createWebSocket(endpoint, sessionId, request));
|
|
194
210
|
}
|
|
195
|
-
|
|
196
|
-
const
|
|
211
|
+
let connection;
|
|
212
|
+
const observation = socketObservations?.connect(sessionId, () => ({
|
|
213
|
+
queued_message_count: connection?.queue.length ?? 0,
|
|
214
|
+
socket_delivered_message_count: connection?.timing?.metrics.delivered_message_count ?? 0,
|
|
215
|
+
socket_queue_residence_max_ms: connection?.timing?.metrics.queue_residence_max_ms ?? 0,
|
|
216
|
+
buffered_send_bytes: connection?.socket.bufferedAmount ?? 0,
|
|
217
|
+
}));
|
|
218
|
+
let normalized;
|
|
219
|
+
try { normalized = normalizeWebSocketConnection(await ownership.promise); }
|
|
220
|
+
catch (error) { observation?.error(); observation?.close(undefined, false); throw error; }
|
|
221
|
+
const { socket, ...handshake } = normalized;
|
|
197
222
|
return new Promise((resolve, reject) => {
|
|
198
223
|
let settled = false;
|
|
199
|
-
|
|
224
|
+
connection = {
|
|
200
225
|
socket,
|
|
226
|
+
observation,
|
|
201
227
|
timing: socketTiming(),
|
|
202
228
|
queue: [],
|
|
203
229
|
queuedBytes: 0,
|
|
@@ -209,6 +235,7 @@ export function createBrowserHost(options = {}) {
|
|
|
209
235
|
if (settled) return;
|
|
210
236
|
settled = true;
|
|
211
237
|
connectingConnections.delete(connection);
|
|
238
|
+
observation?.close(undefined, false);
|
|
212
239
|
finishSocketTiming(connection);
|
|
213
240
|
reject(error);
|
|
214
241
|
};
|
|
@@ -228,6 +255,7 @@ export function createBrowserHost(options = {}) {
|
|
|
228
255
|
delete connection.reject;
|
|
229
256
|
const handle = nextHandle++;
|
|
230
257
|
connections.set(handle, connection);
|
|
258
|
+
observation?.connected(handshake);
|
|
231
259
|
resolve(JSON.stringify({
|
|
232
260
|
handle,
|
|
233
261
|
status: handshake.status ?? 101,
|
|
@@ -244,6 +272,7 @@ export function createBrowserHost(options = {}) {
|
|
|
244
272
|
: { kind: "binary" });
|
|
245
273
|
});
|
|
246
274
|
socket.addEventListener("close", (event) => {
|
|
275
|
+
observation?.close(event, connection.intentionallyClosed);
|
|
247
276
|
connection.wakeSend?.();
|
|
248
277
|
if (!settled) {
|
|
249
278
|
rejectConnection(new Error(`WebSocket closed during connection with code ${event.code}`));
|
|
@@ -252,6 +281,7 @@ export function createBrowserHost(options = {}) {
|
|
|
252
281
|
}
|
|
253
282
|
});
|
|
254
283
|
socket.addEventListener("error", () => {
|
|
284
|
+
observation?.error();
|
|
255
285
|
if (!settled) {
|
|
256
286
|
rejectConnection(new Error("WebSocket connection failed"));
|
|
257
287
|
} else {
|
|
@@ -329,6 +359,8 @@ export function createBrowserHost(options = {}) {
|
|
|
329
359
|
error: "concurrent WebSocket sends are unsupported" });
|
|
330
360
|
}
|
|
331
361
|
connection.sending = true;
|
|
362
|
+
let sent = false;
|
|
363
|
+
connection.observation?.sendStarted();
|
|
332
364
|
try {
|
|
333
365
|
if (connection.managed) {
|
|
334
366
|
connection.socket.send(JSON.stringify({ mpp: "message", data: message }));
|
|
@@ -363,6 +395,8 @@ export function createBrowserHost(options = {}) {
|
|
|
363
395
|
}
|
|
364
396
|
if (closed()) return closedResult();
|
|
365
397
|
connection.socket.send(message);
|
|
398
|
+
sent = true;
|
|
399
|
+
connection.observation?.sent();
|
|
366
400
|
return JSON.stringify({ ok: true });
|
|
367
401
|
} catch (error) {
|
|
368
402
|
return JSON.stringify({
|
|
@@ -371,6 +405,8 @@ export function createBrowserHost(options = {}) {
|
|
|
371
405
|
error: error instanceof Error ? error.message : String(error),
|
|
372
406
|
});
|
|
373
407
|
} finally {
|
|
408
|
+
// A send that did not reach socket.send must not leave a waiting timer.
|
|
409
|
+
if (!sent) connection.observation?.sendFailed();
|
|
374
410
|
connection.sending = false;
|
|
375
411
|
}
|
|
376
412
|
}
|
|
@@ -406,6 +442,7 @@ export function createBrowserHost(options = {}) {
|
|
|
406
442
|
if (!connection) return;
|
|
407
443
|
connections.delete(handle);
|
|
408
444
|
connection.intentionallyClosed = true;
|
|
445
|
+
connection.observation?.close(undefined, true);
|
|
409
446
|
connection.wakeSend?.();
|
|
410
447
|
connection.waiter?.({ kind: "closed", detail: "by the WASM runtime" });
|
|
411
448
|
finishSocketTiming(connection);
|
|
@@ -422,11 +459,13 @@ export function createBrowserHost(options = {}) {
|
|
|
422
459
|
if (timing && dataMessage) {
|
|
423
460
|
timing.metrics.message_count += 1;
|
|
424
461
|
// Only the exact metadata event can produce diagnostics; deltas stay opaque.
|
|
425
|
-
|
|
462
|
+
connection.observation?.message(message.kind === "text" ? message.text : undefined);
|
|
463
|
+
if (message.kind === "text" && (timing.provider.size < 32 || connection.observation)) {
|
|
426
464
|
const metadata = providerSocketTiming(message.text);
|
|
427
465
|
if (metadata) {
|
|
466
|
+
connection.observation?.provider(metadata);
|
|
428
467
|
const key = metadata.response_id ?? timing.provider.size;
|
|
429
|
-
if (!timing.provider.has(key)) timing.provider.set(key, metadata);
|
|
468
|
+
if (timing.provider.size < 32 && !timing.provider.has(key)) timing.provider.set(key, metadata);
|
|
430
469
|
}
|
|
431
470
|
}
|
|
432
471
|
}
|
|
@@ -461,7 +500,7 @@ export function createBrowserHost(options = {}) {
|
|
|
461
500
|
|
|
462
501
|
// No counters, timestamps, metadata parsing or observations without the internal hook.
|
|
463
502
|
function socketTiming() {
|
|
464
|
-
return onSocketTiming === undefined ? undefined : {
|
|
503
|
+
return onSocketTiming === undefined && socketObservations === undefined ? undefined : {
|
|
465
504
|
metrics: {
|
|
466
505
|
message_count: 0,
|
|
467
506
|
delivered_message_count: 0,
|
|
@@ -479,6 +518,7 @@ export function createBrowserHost(options = {}) {
|
|
|
479
518
|
// Finalize owned consumption, not the remote close event: buffered frames may
|
|
480
519
|
// still be drained after that event. Clear first for reentrant close/dispose.
|
|
481
520
|
connection.timing = undefined;
|
|
521
|
+
if (onSocketTiming === undefined) return;
|
|
482
522
|
const metrics = timing.metrics;
|
|
483
523
|
try {
|
|
484
524
|
const result = onSocketTiming({
|
|
@@ -513,7 +553,7 @@ export function createBrowserHost(options = {}) {
|
|
|
513
553
|
}
|
|
514
554
|
for (const handle of [...connections.keys()]) cleanup(() => close(handle));
|
|
515
555
|
cleanup(() => closePreconnected(disposalError));
|
|
516
|
-
cleanup(() => code.reset());
|
|
556
|
+
cleanup(() => { effectIdentity.reset(); return code.reset(); });
|
|
517
557
|
cleanup(() => mcpInstalled?.then(() => {}));
|
|
518
558
|
cleanup(() => toolsLifecycle?.close());
|
|
519
559
|
cleanup(() => options.onDispose?.());
|
|
@@ -612,10 +652,19 @@ export function createBrowserHost(options = {}) {
|
|
|
612
652
|
next,
|
|
613
653
|
close,
|
|
614
654
|
sleep: (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)),
|
|
615
|
-
executeCode: code.executeCodeObserved
|
|
616
|
-
|
|
655
|
+
executeCode: traceTool === undefined ? code.executeCodeObserved
|
|
656
|
+
: (source, sessionId = "default", callId = "exec", model = "unknown", turnId, localDefinitions, executeLocalTool) =>
|
|
657
|
+
traceToolInvocation(traceTool, "exec", {
|
|
658
|
+
sessionId, callId, ...(turnId == null ? {} : { turnId }),
|
|
659
|
+
}, () => code.executeCodeObserved(source, sessionId, callId, model, turnId, localDefinitions, executeLocalTool)),
|
|
660
|
+
waitCode: traceTool === undefined ? code.waitCodeObserved
|
|
661
|
+
: (input, sessionId = "default", callId = "wait") =>
|
|
662
|
+
traceToolInvocation(traceTool, "wait", { sessionId, callId },
|
|
663
|
+
() => code.waitCodeObserved(input, sessionId, callId)),
|
|
617
664
|
beginCodeTurn: code.beginTurn,
|
|
618
665
|
cancelCodeTurn: code.cancelTurn,
|
|
666
|
+
preemptCode: code.preempt,
|
|
667
|
+
preemptCodeTurn: code.preemptTurn,
|
|
619
668
|
nextCodeUpdate: code.nextCodeUpdate,
|
|
620
669
|
executeTool: code.executeTool,
|
|
621
670
|
routeSubagent: (request) => {
|
|
@@ -653,9 +702,9 @@ export function createBrowserHost(options = {}) {
|
|
|
653
702
|
},
|
|
654
703
|
toolMode: () => toolMode,
|
|
655
704
|
toolDefinitions: code.toolDefinitions,
|
|
656
|
-
releaseSession: code.releaseSession,
|
|
657
|
-
emitEvent: onEvent,
|
|
658
|
-
reset: code.reset,
|
|
705
|
+
releaseSession: (sessionId) => { effectIdentity.release(sessionId); socketObservations?.release(sessionId); return code.releaseSession(sessionId); },
|
|
706
|
+
emitEvent: (event, ...args) => { code.observeEvent(event); effectIdentity.observe(event); socketObservations?.runtime(event); return onEvent(event, ...args); },
|
|
707
|
+
reset: () => { effectIdentity.reset(); return code.reset(); },
|
|
659
708
|
dispose,
|
|
660
709
|
});
|
|
661
710
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { retryAfterAdvice } from "../runtime/retry-after.mjs";
|
|
2
|
+
|
|
1
3
|
const DEFAULT_TIMEOUT_MS = 10_000;
|
|
2
4
|
|
|
3
5
|
/** Open Nanocodex's same-origin Responses proxy and consume its setup frame. */
|
|
@@ -74,13 +76,20 @@ function waitForProxyHandshake(socket, timeoutMs) {
|
|
|
74
76
|
&& message.status <= 599) {
|
|
75
77
|
const status = message.status;
|
|
76
78
|
const body = typeof message.error === "string" ? message.error : `HTTP ${status}`;
|
|
77
|
-
const
|
|
79
|
+
const advice = retryAfterAdvice(
|
|
80
|
+
typeof message.retryAfter === "number" ? String(message.retryAfter) : message.retryAfter,
|
|
81
|
+
);
|
|
82
|
+
// The proxy may already have captured advice before draining its rejection body.
|
|
83
|
+
if (Number.isSafeInteger(message.retry_after_deadline_ms) && message.retry_after_deadline_ms >= 0) {
|
|
84
|
+
advice.retry_after_deadline_ms = message.retry_after_deadline_ms;
|
|
85
|
+
}
|
|
78
86
|
fail(Object.assign(
|
|
79
87
|
new Error(`Agent connection rejected with HTTP ${status}: ${body}`),
|
|
80
88
|
{
|
|
81
89
|
status,
|
|
82
90
|
body,
|
|
83
|
-
...
|
|
91
|
+
...advice,
|
|
92
|
+
...(advice.retry_after !== undefined ? { retryAfter: advice.retry_after } : {}),
|
|
84
93
|
},
|
|
85
94
|
));
|
|
86
95
|
return;
|
package/browser/index.d.mts
CHANGED
|
@@ -4,16 +4,6 @@ export {
|
|
|
4
4
|
subscriptionRevision,
|
|
5
5
|
} from "../runtime/subscription-store.mjs";
|
|
6
6
|
export { createQuickJsEvaluator } from "../runtime/quickjs-evaluator.mjs";
|
|
7
|
-
export {
|
|
8
|
-
createTempoProvider,
|
|
9
|
-
createTempoProviderFromAccounts,
|
|
10
|
-
DEFAULT_MERCATOR_MCP_URL,
|
|
11
|
-
} from "../runtime/tempo-provider.mjs";
|
|
12
|
-
export type {
|
|
13
|
-
AccountsTempoProviderOptions,
|
|
14
|
-
AccountsWallet,
|
|
15
|
-
TempoProvider,
|
|
16
|
-
} from "../runtime/tempo-provider.mjs";
|
|
17
7
|
export type {
|
|
18
8
|
AgentEvent,
|
|
19
9
|
AgentLifecycle,
|
|
@@ -46,6 +36,7 @@ export type {
|
|
|
46
36
|
TurnResult,
|
|
47
37
|
TurnUsage,
|
|
48
38
|
McpPayment,
|
|
39
|
+
PaidMcpPayment,
|
|
49
40
|
McpServer,
|
|
50
41
|
McpServers,
|
|
51
42
|
MemoryChatGptSubscriptionStore,
|
|
@@ -79,3 +70,5 @@ export type {
|
|
|
79
70
|
BrowserWebSocketConnection,
|
|
80
71
|
BrowserWebSocketRequest,
|
|
81
72
|
} from "./host.mjs";
|
|
73
|
+
|
|
74
|
+
export * as Claude from "./Claude.mjs";
|
package/browser/index.mjs
CHANGED
|
@@ -4,11 +4,6 @@ export {
|
|
|
4
4
|
subscriptionRevision,
|
|
5
5
|
} from "../runtime/subscription-store.mjs";
|
|
6
6
|
export { createQuickJsEvaluator } from "../runtime/quickjs-evaluator.mjs";
|
|
7
|
-
export {
|
|
8
|
-
createTempoProvider,
|
|
9
|
-
createTempoProviderFromAccounts,
|
|
10
|
-
DEFAULT_MERCATOR_MCP_URL,
|
|
11
|
-
} from "../runtime/tempo-provider.mjs";
|
|
12
7
|
export * as Agent from "./Agent.mjs";
|
|
13
8
|
export * as ChatGptSubscription from "./ChatGptSubscription.mjs";
|
|
14
9
|
export * as Subagents from "../runtime/subagents.mjs";
|
|
@@ -21,3 +16,5 @@ export {
|
|
|
21
16
|
defaultHostManagedWebSocketUrl,
|
|
22
17
|
openHostManagedWebSocket,
|
|
23
18
|
} from "./hostManagedWebSocket.mjs";
|
|
19
|
+
|
|
20
|
+
export * as Claude from "./Claude.mjs";
|