@roll-agent/relay-client 0.2.2 → 0.3.0
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 +4 -0
- package/dist/client.d.ts +14 -12
- package/dist/client.js +1 -1
- package/dist/schemas.d.ts +208 -4
- package/dist/schemas.js +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -103,3 +103,7 @@ error.
|
|
|
103
103
|
`@roll-agent/relay-client/testing` exports `createRelayClientForTesting()` and the minimal WebSocket
|
|
104
104
|
and scheduler interfaces. Inject a deterministic transport without adding Node APIs to production
|
|
105
105
|
Browser code.
|
|
106
|
+
|
|
107
|
+
### Context recovery and Wire 1.3
|
|
108
|
+
|
|
109
|
+
`RelayThreadView.contextRecovery` carries the latest bounded recovery gate state from snapshots and `context.recovery.changed` events. It is `null` for older peers. The default session advertisement remains `["1.2", "1.1"]` for existing Relay services with strict version allowlists. After upgrading the Relay service and Companion together, pass `relayProtocolVersions: ["1.3", "1.2", "1.1"]` to `createRelayClient`. Companion's bridge accepts `protocolVersion: "1.3"`; the Core host session factory accepts `relayProtocolVersion: "1.3"`. The deployed Relay service must support 1.3 ticket negotiation and routing before enabling either option.
|
package/dist/client.d.ts
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { relayRequestMethodSchemasV13, type RelayInteractionRequestV11, type RelayRequestParamsForVersion, type RelayRequestResultForVersion } from "@roll-agent/relay-protocol";
|
|
2
2
|
import { z } from "zod/v4";
|
|
3
3
|
import { type RelayClientErrorDetails, type RelayConnectionState, type RelayThreadView } from "./schemas.ts";
|
|
4
4
|
import { type RelayClientRuntimeDependencies } from "./transport.ts";
|
|
5
|
-
type ThreadListWireInput = z.input<(typeof
|
|
6
|
-
type ThreadCreateWireInput = z.input<(typeof
|
|
7
|
-
type ThreadListWireResult = RelayRequestResultForVersion<"1.
|
|
8
|
-
type ThreadOpenWireResult = RelayRequestResultForVersion<"1.2", "thread.open">;
|
|
9
|
-
type ThreadSnapshotWireResult = RelayRequestResultForVersion<"1.2", "thread.snapshot">;
|
|
10
|
-
type TurnStartWireResult = RelayRequestResultForVersion<"1.
|
|
11
|
-
type TurnCancelWireResult = RelayRequestResultForVersion<"1.
|
|
12
|
-
type InteractionCandidateWireResult = RelayRequestResultForVersion<"1.
|
|
13
|
-
export type RelayOperationResult = RelayRequestResultForVersion<"1.
|
|
14
|
-
export type RelayThreadCapabilities = RelayRequestResultForVersion<"1.
|
|
5
|
+
type ThreadListWireInput = z.input<(typeof relayRequestMethodSchemasV13)["thread.list"]["params"]>;
|
|
6
|
+
type ThreadCreateWireInput = z.input<(typeof relayRequestMethodSchemasV13)["thread.create"]["params"]>;
|
|
7
|
+
type ThreadListWireResult = RelayRequestResultForVersion<"1.3", "thread.list">;
|
|
8
|
+
type ThreadOpenWireResult = RelayRequestResultForVersion<"1.3", "thread.open"> | RelayRequestResultForVersion<"1.2", "thread.open">;
|
|
9
|
+
type ThreadSnapshotWireResult = RelayRequestResultForVersion<"1.3", "thread.snapshot"> | RelayRequestResultForVersion<"1.2", "thread.snapshot">;
|
|
10
|
+
type TurnStartWireResult = RelayRequestResultForVersion<"1.3", "turn.start">;
|
|
11
|
+
type TurnCancelWireResult = RelayRequestResultForVersion<"1.3", "turn.cancel">;
|
|
12
|
+
type InteractionCandidateWireResult = RelayRequestResultForVersion<"1.3", "interaction.candidate">;
|
|
13
|
+
export type RelayOperationResult = RelayRequestResultForVersion<"1.3", "operation.result.get">;
|
|
14
|
+
export type RelayThreadCapabilities = RelayRequestResultForVersion<"1.3", "thread.capabilities">;
|
|
15
15
|
export type RelayThreadId = ThreadOpenWireResult["thread"]["id"];
|
|
16
16
|
export type RelayTurnId = RelayRequestParamsForVersion<"1.2", "turn.cancel">["turnId"];
|
|
17
17
|
export type RelayInteractionId = RelayInteractionRequestV11["interactionId"];
|
|
@@ -27,7 +27,7 @@ export type RelayTurnCancelResult = TurnCancelWireResult;
|
|
|
27
27
|
export type RelayInteractionCandidateResult = InteractionCandidateWireResult;
|
|
28
28
|
export interface RelaySessionProviderInput {
|
|
29
29
|
readonly signal: AbortSignal;
|
|
30
|
-
readonly supportedRelayProtocolVersions: readonly
|
|
30
|
+
readonly supportedRelayProtocolVersions: readonly ("1.3" | "1.2" | "1.1")[];
|
|
31
31
|
}
|
|
32
32
|
export type RelaySessionProvider = (input: RelaySessionProviderInput) => Promise<unknown>;
|
|
33
33
|
export interface RelayRequestOptions {
|
|
@@ -36,6 +36,8 @@ export interface RelayRequestOptions {
|
|
|
36
36
|
}
|
|
37
37
|
export interface CreateRelayClientOptions {
|
|
38
38
|
readonly getSession: RelaySessionProvider;
|
|
39
|
+
/** Opt into 1.3 only after the Relay service and Companion support it. */
|
|
40
|
+
readonly relayProtocolVersions?: readonly ("1.3" | "1.2" | "1.1")[];
|
|
39
41
|
readonly requestTimeoutMs?: number;
|
|
40
42
|
}
|
|
41
43
|
export type RelayConnectionListener = (state: RelayConnectionState) => void;
|
package/dist/client.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{RELAY_ERROR_CODES_V11 as e,RELAY_MESSAGE_TYPES_V11 as t,RELAY_REQUEST_METHODS_V11 as s,RELAY_REQUEST_METHOD_DISPOSITIONS_V12 as n,parseRelayInteractionCandidateForRequestV11 as i,parseRelayRequestParamsForVersion as r,parseRelayRequestResultForVersion as o,relayAckSchemaV11 as a,relayMessageSchemaV12 as c,relayRequestIdSchema as l,relayRequestMethodSchemasV12 as d,relayRuntimeRequestSchemaV12 as h}from"@roll-agent/relay-protocol";import{parseRelayBrowserFirstControlFrameAny as u,relayBrowserControlMessageSchemaAny as p,relaySessionDescriptorSchema as y}from"@roll-agent/relay-protocol/control";import{z as m}from"zod/v4";import{RELAY_CLIENT_TRANSPORT_ERROR_CODES as f,getPendingInteractionRequest as g,relayClientErrorDetailsSchema as v,relayConnectionStateSchema as w,relayThreadViewSchema as R}from"./schemas.js";import{browserRelayRuntimeDependencies as S}from"./transport.js";export class RelayClientError extends Error{details;constructor(e){super(e.message),this.name="RelayClientError",this.details=v.parse(e)}}const b=3e4,I=1,q=-1,k={normal:1e3,invalidFrame:4002,sessionFatal:4008,sessionRetryable:4012},C=new Set(["completed","cancelled","failed"]);function T(e,t,s){return new RelayClientError({kind:"transport",code:e,message:t,retryable:s})}function E(e){return e instanceof RelayClientError?e:T(f.transportError,e instanceof Error?e.message:"Relay transport failed",!0)}function P(e,t){try{e(t)}catch(e){if("function"==typeof globalThis.reportError)try{globalThis.reportError(e)}catch{}}}function M(e){if("string"!=typeof e)throw T(f.protocolError,"Relay sent a non-text frame",!1);try{return JSON.parse(e)}catch{throw T(f.protocolError,"Relay sent invalid JSON",!1)}}function A(e){try{return u(e)}catch{throw T(f.protocolError,"Relay first frame must be Control 1.0 session.ready for Wire 1.1",!1)}}function B(e){const t=e.activeTurn;return void 0===t?{status:"idle"}:"cancelling"===t.status?{status:"cancelling",turnId:t.id}:{status:"running",turnId:t.id}}function O(e,t){const s=e.findIndex(e=>e.streamId===t.streamId);return-1===s?[...e,t]:e.map((e,n)=>n===s?t:e)}function x(e,t){return{status:e,interactionId:t.interactionId,threadId:t.threadId,turnId:t.turnId,method:t.method}}class RelayThreadImpl{id;#e;#t=new Set;#s;constructor(e,t){this.#e=e,this.id=t,this.#s=R.parse({status:"loading",threadId:t,snapshot:null,liveAssistantMessages:[],interactions:[],turn:{status:"idle"}})}getSnapshot(){return this.#s}subscribe(e){return this.#t.add(e),P(e,this.#s),()=>{this.#t.delete(e)}}async send(e,t){const s=await this.#e.startTurn(this.id,e,t);return this.#n({status:"running",turnId:s.turnId}),s}async cancel(e,t){const s=await this.#e.cancelTurn(this.id,e,t);return s.cancelling&&this.#n({status:"cancelling",turnId:e}),s}async respond(e,t,s){const n=this.#s.interactions.find(t=>{const s=g(t);return s?.interactionId===e}),i=void 0===n?void 0:g(n);if(void 0===i)throw T(f.connectionUnavailable,"Interaction is no longer pending in this Browser session",!1);this.#i(e,"responding");try{return await this.#e.respondToInteraction(i,t,s)}catch(t){throw this.#i(e,"pending"),t}}getResult(e,t){return this.#e.getOperationResult(this.id,e,t)}capabilities(e){return this.#e.getThreadCapabilities(this.id,e)}async refresh(e){try{const t=await this.#e.getThreadSnapshot(this.id,e);return this.applySnapshot(t),t}catch(e){throw this.applyError(E(e).details),e}}applySnapshot(e){this.#r({status:"ready",threadId:this.id,snapshot:e,liveAssistantMessages:[],interactions:this.#s.interactions,turn:B(e)})}applyError(e){this.#r({status:"error",threadId:this.id,snapshot:this.#s.snapshot,liveAssistantMessages:this.#s.liveAssistantMessages,interactions:this.#s.interactions,turn:this.#s.turn,error:e})}applyRuntimeEvent(e){const t=e.event,s=t.event,n=t.turnId;let i=this.#s.liveAssistantMessages,r=this.#s.turn;if("message.started"===s.type)i=O(i,{status:"streaming",streamId:s.streamId,text:""});else if("message.delta"===s.type){const e=i.find(e=>e.streamId===s.streamId);i=O(i,{status:"streaming",streamId:s.streamId,text:`${e?.text??""}${s.delta}`})}else"message.completed"===s.type?i=O(i,{status:"completed",streamId:s.streamId,text:s.text}):"turn.started"===s.type&&void 0!==n?r={status:"running",turnId:n}:"turn.completed"===s.type&&void 0!==n?r={status:"completed",turnId:n}:"turn.cancelled"===s.type&&void 0!==n?r={status:"cancelled",turnId:n}:"turn.failed"===s.type&&void 0!==n&&(r={status:"failed",turnId:n,stage:s.stage});this.#o({liveAssistantMessages:i,turn:r}),"turn.completed"!==s.type&&"turn.cancelled"!==s.type&&"turn.failed"!==s.type||this.refresh().catch(()=>{})}applyInteractionRequest(e){const t=this.#s.interactions.findIndex(t=>{const s=g(t);return s?.interactionId===e.interactionId}),s={status:"pending",request:e},n=-1===t?[...this.#s.interactions,s]:this.#s.interactions.map((e,n)=>n===t?s:e);this.#o({interactions:n})}applyInteractionTerminal(e){const s=x(e.type===t.interactionResolved?"resolved":"cancelled",e),n=this.#s.interactions.findIndex(t=>{const s=g(t);return s?.interactionId===e.interactionId}),i=-1===n?[...this.#s.interactions,s]:this.#s.interactions.map((e,t)=>t===n?s:e);this.#o({interactions:i})}#n(e){const t=this.#s.turn;"idle"!==t.status&&t.turnId===e.turnId&&C.has(t.status)||this.#o({turn:e})}#i(e,t){const s=this.#s.interactions.map(s=>{const n=g(s);return n?.interactionId===e?{status:t,request:n}:s});this.#o({interactions:s})}#o(e){const t={threadId:this.id,snapshot:this.#s.snapshot,liveAssistantMessages:e.liveAssistantMessages??this.#s.liveAssistantMessages,interactions:e.interactions??this.#s.interactions,turn:e.turn??this.#s.turn};this.#r(null===this.#s.snapshot?{status:"loading",...t,snapshot:null}:{status:"ready",...t,snapshot:this.#s.snapshot})}#r(e){this.#s=R.parse(e);for(const e of this.#t)P(e,this.#s)}}export class RelayClientImpl{#a;#c;#l=new Set;#d=new Map;#h=new Map;#u=new Map;#p={status:"idle"};#y;#m;#f="1.1";#g=new Map;#v;#w;#R;#S=0;#b=!1;#I=!1;#q=q;#k=!1;#C=0;#T=!1;#E;constructor(e,t){if(this.#a=e,this.#c=t,this.#E=e.requestTimeoutMs??b,!Number.isFinite(this.#E)||this.#E<=0)throw new TypeError("requestTimeoutMs must be a positive finite number")}async connect(e){if("connected"===this.#p.status)return;if(this.#b)throw T(f.closed,"Relay client is closed",!1);this.#P();const t=this.#v;return void 0!==t?t:(this.#I=!1,this.#S=0,this.#M({status:"connecting",attempt:1}),this.#A(e?.signal,e?.timeoutMs).catch(e=>{const t=E(e);throw this.#b||this.#I||this.#M({status:"closed",reason:"session"===t.details.kind?"session":"transport"}),t}))}close(){this.#b||(this.#b=!0,this.#I=!0,this.#w?.abort(),this.#P(),this.#B("client closed"),this.#O(!0),this.#M({status:"closed",reason:"client"}))}getConnectionState(){return this.#p}subscribeConnection(e){return this.#l.add(e),P(e,this.#p),()=>{this.#l.delete(e)}}async listThreads(e={},t){return this.#x(s.threadList,e,t)}async createThread(e={},t){const n=await this.#x(s.threadCreate,{...e,requestId:this.#c.createUuid()},t),i=this.#F(n.thread.id);return await i.refresh(t).catch(()=>{}),i}async openThread(e,t){const n=await this.#x(s.threadOpen,{threadId:e},t),i=this.#F(e);return i.applySnapshot(n),i}async startTurn(e,t,n){return this.#x(s.turnStart,{requestId:this.#c.createUuid(),threadId:e,turnId:this.#c.createUuid(),input:{text:t}},n)}async cancelTurn(e,t,n){return this.#x(s.turnCancel,{requestId:this.#c.createUuid(),threadId:e,turnId:t},n)}async respondToInteraction(e,t,n){const r=i(e,t);return this.#x(s.interactionCandidate,r,n)}async getThreadCapabilities(e,t){return this.#x("thread.capabilities",{threadId:e},t)}getOperationResult(e,t,s){if("1.2"!==this.#f)return Promise.reject(T(f.protocolError,"App results require Relay Wire 1.2",!1));const n=`${this.#C}:${this.#m}:${e}:${t}`;if(void 0===s){const e=this.#g.get(n);if(e)return e}const i=this.#x("operation.result.get",{threadId:e,operationId:t},s);return void 0===s&&(this.#g.set(n,i),i.finally(()=>{this.#g.get(n)===i&&this.#g.delete(n)}).catch(()=>{})),i}async getThreadSnapshot(e,t){return this.#x(s.threadSnapshot,{threadId:e,limit:100},t)}async#W(e,t){const s=t??this.#E;if(!Number.isFinite(s)||s<=0)throw new TypeError("timeoutMs must be a positive finite number");const n=new AbortController;this.#w=n;let i=!1;const r=()=>{n.abort()};e?.addEventListener("abort",r,{once:!0}),!0===e?.aborted&&n.abort();const o=this.#c.scheduler.setTimer(()=>{i=!0,n.abort()},s),a=new Promise((e,t)=>{const s=()=>{t(T(i?f.requestTimeout:f.aborted,i?"Relay session connection timed out":"Relay session request was aborted",i)),this.#y?.close(k.normal,"session attempt cancelled")};n.signal.aborted?s():n.signal.addEventListener("abort",s,{once:!0})});try{await Promise.race([this.#j(n.signal),a])}finally{this.#c.scheduler.clearTimer(o),e?.removeEventListener("abort",r),this.#w===n&&(this.#w=void 0)}}async#j(e){try{let t;try{t=await this.#a.getSession({signal:e,supportedRelayProtocolVersions:["1.2","1.1"]})}catch(t){if(e.aborted)throw T(f.aborted,"Relay session request was aborted",!1);throw E(t)}if(e.aborted)throw T(f.aborted,"Relay session request was aborted",!1);const s=y.safeParse(t);if(!s.success)throw T(f.invalidSession,"Session endpoint returned an invalid Relay session",!1);if(Date.parse(s.data.expiresAt)<=this.#c.scheduler.now())throw T(f.invalidSession,"Session endpoint returned an expired Relay session",!1);this.#B("superseded relay session");const n=this.#c.createWebSocket(s.data.connectUrl);this.#y=n,await this.#L(n)}catch(e){throw E(e)}}#L(e){return new Promise((t,s)=>{const n={ready:!1,settled:!1},i=e=>{n.settled||(n.settled=!0,s(e))};e.setHandlers({onOpen:()=>{},onMessage:s=>{if(!n.ready||this.#y===e)try{const e=M(s);if(!n.ready){const s=A(e);if(void 0!==this.#m&&this.#m!==s.workspaceId)throw T(f.protocolError,"Relay session changed the bound Workspace",!1);return this.#m=s.workspaceId,this.#f=s.relayProtocolVersion,this.#g.clear(),n.ready=!0,n.settled=!0,this.#S=0,this.#T=!0,this.#M({status:"connected",workspaceStatus:s.workspaceStatus}),this.#U(),"online"===s.workspaceStatus&&this.#z(),void t()}this.#$(e)}catch(t){const s=E(t);n.ready?this.#V(s):i(s),e.close(k.invalidFrame,"invalid relay frame")}},onClose:()=>{const t=this.#y===e;t&&(this.#y=void 0),n.ready?t&&this.#N():i(T(f.transportError,"Relay connection closed before session.ready",!0))},onError:()=>{n.ready||i(T(f.transportError,"Relay WebSocket failed before session.ready",!0))}})})}#$(e){const t=p.safeParse(e);if(t.success)return void this.#G(t.data);const s=c.safeParse(e);if(!s.success)throw T(f.protocolError,"Relay sent an invalid Control 1.0 or Wire 1.1 frame",!1);this.#H(s.data)}#G(e){if("session.ready"===e.type)throw T(f.protocolError,"session.ready may only be the first frame",!1);if("workspace.status"===e.type){this.#D(e.workspaceId);const t=this.#p,s="connected"===t.status&&"offline"===t.workspaceStatus&&"online"===e.status;return this.#M({status:"connected",workspaceStatus:e.status}),s&&(this.#T=!0,this.#J()),void("online"===e.status&&this.#z())}const t=new RelayClientError({kind:"session",code:e.code,message:`Relay session failed: ${e.code}`,retryable:e.retryable});if(e.retryable)this.#y?.close(k.sessionRetryable,"retryable relay session error");else{this.#I=!0,this.#O(!0);for(const e of this.#d.values())e.applyError(t.details);this.#M({status:"closed",reason:"session"}),this.#y?.close(k.sessionFatal,"non-retryable relay session error")}}#H(e){if(e.type!==t.runtimeResponse){if(e.type===t.runtimeGap)return this.#D(e.workspaceId),this.#Q(e.throughRelaySequence),void this.#K(e.throughRelaySequence);if(e.type!==t.runtimeEvent&&e.type!==t.interactionRequest&&e.type!==t.interactionResolved&&e.type!==t.interactionCancelled)throw T(f.protocolError,`Relay sent disallowed Browser inbound frame: ${e.type}`,!1);this.#X({relaySequence:e.relaySequence,message:e})}else this.#Y(e)}#Y(t){this.#D(t.workspaceId);const s=this.#h.get(t.requestId);if(void 0!==s){if(void 0!==t.error){const n=t.error.code===e.remoteRequestDenied?"Remote request denied":t.error.message;return void this.#Z(s,new RelayClientError({kind:"remote",code:t.error.code,message:n,retryable:t.error.retryable}))}if(void 0!==t.result)try{const e=o(this.#f,s.method,t.result);this.#Z(s,void 0,e)}catch{this.#Z(s,T(f.protocolError,`Relay returned an invalid ${s.method} result`,!1))}else this.#Z(s,T(f.protocolError,"runtime.response contains neither result nor error",!1))}}#X(e){if(this.#D(e.message.workspaceId),this.#Q(e.relaySequence),e.relaySequence<=this.#q)this.#U();else if(this.#k)this.#u.set(e.relaySequence,e);else{if(e.relaySequence!==this.#q+1)return this.#u.set(e.relaySequence,e),void this.#K(e.relaySequence-1);this.#_(e.message),this.#q=e.relaySequence,this.#U()}}#_(e){const s=e.type===t.runtimeEvent?e.event.threadId:e.threadId,n=this.#F(s);e.type===t.runtimeEvent?n.applyRuntimeEvent(e):e.type===t.interactionRequest?n.applyInteractionRequest(e):n.applyInteractionTerminal(e)}#K(e){if(e<=this.#q)return void this.#U();if(this.#k)return;this.#k=!0;const t=e,s=this.#C,n=[...this.#d.values()].map(async e=>e.refresh());Promise.all(n).then(()=>{if(this.#C===s){this.#q=t;for(const e of[...this.#u.keys()])e<=t&&this.#u.delete(e);this.#k=!1,this.#U(),this.#ee()}}).catch(()=>{this.#C===s&&(this.#k=!1,this.#y?.close(k.sessionRetryable,"snapshot recovery failed"))})}#Q(e){this.#T&&(this.#T=!1,e>this.#q||(this.#C+=1,this.#q=q,this.#u.clear(),this.#k=!1,this.#J()))}#J(){for(const e of this.#d.values())e.refresh().catch(()=>{})}#ee(){for(;!this.#k;){const e=this.#q+1,t=this.#u.get(e);if(void 0===t){const t=[...this.#u.keys()].sort((e,t)=>e-t)[0];return void(void 0!==t&&t>e&&this.#K(t-1))}this.#u.delete(e),this.#_(t.message),this.#q=e,this.#U()}}#U(){const e=this.#m,s=this.#y;if(void 0===e||1!==s?.readyState)return;const n=a.parse({type:t.runtimeAck,workspaceId:e,throughRelaySequence:this.#q});s.send(JSON.stringify(n))}#N(){this.#b||this.#I||(this.#te(),this.#se())}#se(){if(void 0!==this.#R||this.#b||this.#I)return;this.#S+=1;const e=this.#c.reconnectDelayMs(this.#S),t=new Date(this.#c.scheduler.now()+e).toISOString();this.#M({status:"reconnecting",attempt:this.#S,retryAt:t}),this.#R=this.#c.scheduler.setTimer(()=>{this.#R=void 0,this.#b||this.#I||void 0!==this.#v||"connected"===this.#p.status||this.#A().catch(e=>{if(this.#b)return;if(!E(e).details.retryable)return this.#I=!0,this.#O(!0),void this.#M({status:"closed",reason:"session"});this.#se()})},e)}#A(e,t){const s=this.#W(e,t).finally(()=>{this.#v===s&&(this.#v=void 0)});return this.#v=s,s}#B(e){const t=this.#y;void 0!==t&&(this.#y=void 0,t.close(k.normal,e))}#P(){void 0!==this.#R&&(this.#c.scheduler.clearTimer(this.#R),this.#R=void 0)}#z(){for(const e of this.#h.values())e.mutation&&this.#ne(e)}#x(e,s,i){if("connected"!==this.#p.status||"online"!==this.#p.workspaceStatus||void 0===this.#m){const e="connected"===this.#p.status&&"offline"===this.#p.workspaceStatus;return Promise.reject(T(e?f.workspaceOffline:f.connectionUnavailable,e?"The local Workspace is offline":"Relay is not connected",!0))}if(!0===i?.signal?.aborted)return Promise.reject(T(f.aborted,"Relay request was aborted",!1));const o=r("1.2",e,s),a=l.parse(this.#c.createUuid()),c=h.parse({type:t.runtimeRequest,requestId:a,workspaceId:this.#m,method:e,params:o}),d=i?.timeoutMs??this.#E;if(!Number.isFinite(d)||d<=0)return Promise.reject(new TypeError("timeoutMs must be a positive finite number"));const u="mutation"===n[e];return new Promise((t,s)=>{const n=i?.signal?()=>{const e=this.#h.get(a);if(void 0===e)return;const t=e.mutation&&e.sent;this.#Z(e,T(t?f.outcomeUnknown:f.aborted,t?"Mutation outcome is unknown after Browser cancellation":"Relay request was aborted",!1))}:void 0,r={requestId:a,method:e,frame:JSON.stringify(c),mutation:u,sent:!1,resolve:e=>{t(e)},reject:s,signal:i?.signal,abortListener:n,timeoutHandle:void 0};r.timeoutHandle=this.#c.scheduler.setTimer(()=>{const e=this.#h.get(a);if(void 0===e)return;const t=e.mutation&&e.sent;this.#Z(e,T(t?f.outcomeUnknown:f.requestTimeout,t?"Mutation outcome is unknown after Relay timeout":"Relay request timed out",!t))},d),i?.signal?.addEventListener("abort",n??(()=>{}),{once:!0}),this.#h.set(a,r),this.#ne(r)})}#ne(e){const t=this.#y;1===t?.readyState&&"connected"===this.#p.status&&"online"===this.#p.workspaceStatus&&(t.send(e.frame),e.sent=!0)}#Z(e,t,s){this.#h.delete(e.requestId)&&(void 0!==e.timeoutHandle&&this.#c.scheduler.clearTimer(e.timeoutHandle),void 0!==e.signal&&void 0!==e.abortListener&&e.signal.removeEventListener("abort",e.abortListener),void 0!==t?e.reject(t):e.resolve(s))}#te(){for(const e of[...this.#h.values()])e.mutation||this.#Z(e,T(f.transportError,"Relay disconnected before the query completed",!0))}#O(e){for(const t of[...this.#h.values()]){const s=e&&t.mutation&&t.sent;this.#Z(t,T(s?f.outcomeUnknown:f.closed,s?"Mutation outcome is unknown after Relay closed":"Relay client is closed",!1))}}#D(e){if(this.#m!==e)throw T(f.protocolError,"Relay frame does not match the bound Workspace",!1)}#F(e){const t=this.#d.get(e);if(void 0!==t)return t;const s=new RelayThreadImpl(this,e);return this.#d.set(e,s),s}#M(e){this.#p=w.parse(e);for(const e of this.#l)P(e,this.#p)}#V(e){this.#I=!0,this.#O(!0);for(const t of this.#d.values())t.applyError(e.details);this.#M({status:"closed",reason:"protocol"})}}export function createRelayClientWithRuntime(e,t){return new RelayClientImpl(e,t)}export function createRelayClient(e){return createRelayClientWithRuntime(e,S)}
|
|
1
|
+
import{RELAY_ERROR_CODES_V11 as e,RELAY_MESSAGE_TYPES_V11 as t,RELAY_REQUEST_METHODS_V11 as s,RELAY_REQUEST_METHOD_DISPOSITIONS_V12 as n,parseRelayInteractionCandidateForRequestV11 as i,parseRelayRequestParamsForVersion as r,parseRelayRequestResultForVersion as o,relayAckSchemaV11 as a,relayMessageSchemaV13 as c,relayRequestIdSchema as l,relayRequestMethodSchemasV13 as d,relayRuntimeRequestSchemaV12 as h}from"@roll-agent/relay-protocol";import{parseRelayBrowserFirstControlFrameAny as u,relayBrowserControlMessageSchemaAny as p,relaySessionDescriptorSchema as y}from"@roll-agent/relay-protocol/control";import{z as m}from"zod/v4";import{RELAY_CLIENT_TRANSPORT_ERROR_CODES as f,getPendingInteractionRequest as v,relayClientErrorDetailsSchema as g,relayConnectionStateSchema as w,relayThreadViewSchema as R}from"./schemas.js";import{browserRelayRuntimeDependencies as S}from"./transport.js";export class RelayClientError extends Error{details;constructor(e){super(e.message),this.name="RelayClientError",this.details=g.parse(e)}}const b=3e4,I=1,q=-1,k={normal:1e3,invalidFrame:4002,sessionFatal:4008,sessionRetryable:4012},C=new Set(["completed","cancelled","failed"]);function T(e,t,s){return new RelayClientError({kind:"transport",code:e,message:t,retryable:s})}function E(e){return e instanceof RelayClientError?e:T(f.transportError,e instanceof Error?e.message:"Relay transport failed",!0)}function P(e,t){try{e(t)}catch(e){if("function"==typeof globalThis.reportError)try{globalThis.reportError(e)}catch{}}}function M(e){if("string"!=typeof e)throw T(f.protocolError,"Relay sent a non-text frame",!1);try{return JSON.parse(e)}catch{throw T(f.protocolError,"Relay sent invalid JSON",!1)}}function A(e){try{return u(e)}catch{throw T(f.protocolError,"Relay first frame must be Control 1.0 session.ready for Wire 1.1",!1)}}function x(e){const t=e.activeTurn;return void 0===t?{status:"idle"}:"cancelling"===t.status?{status:"cancelling",turnId:t.id}:{status:"running",turnId:t.id}}function B(e,t){const s=e.findIndex(e=>e.streamId===t.streamId);return-1===s?[...e,t]:e.map((e,n)=>n===s?t:e)}function O(e,t){return{status:e,interactionId:t.interactionId,threadId:t.threadId,turnId:t.turnId,method:t.method}}class RelayThreadImpl{id;#e;#t=new Set;#s;constructor(e,t){this.#e=e,this.id=t,this.#s=R.parse({status:"loading",threadId:t,snapshot:null,liveAssistantMessages:[],interactions:[],turn:{status:"idle"}})}getSnapshot(){return this.#s}subscribe(e){return this.#t.add(e),P(e,this.#s),()=>{this.#t.delete(e)}}async send(e,t){const s=await this.#e.startTurn(this.id,e,t);return this.#n({status:"running",turnId:s.turnId}),s}async cancel(e,t){const s=await this.#e.cancelTurn(this.id,e,t);return s.cancelling&&this.#n({status:"cancelling",turnId:e}),s}async respond(e,t,s){const n=this.#s.interactions.find(t=>{const s=v(t);return s?.interactionId===e}),i=void 0===n?void 0:v(n);if(void 0===i)throw T(f.connectionUnavailable,"Interaction is no longer pending in this Browser session",!1);this.#i(e,"responding");try{return await this.#e.respondToInteraction(i,t,s)}catch(t){throw this.#i(e,"pending"),t}}getResult(e,t){return this.#e.getOperationResult(this.id,e,t)}capabilities(e){return this.#e.getThreadCapabilities(this.id,e)}async refresh(e){try{const t=await this.#e.getThreadSnapshot(this.id,e);return this.applySnapshot(t),t}catch(e){throw this.applyError(E(e).details),e}}applySnapshot(e){this.#r({status:"ready",threadId:this.id,snapshot:e,liveAssistantMessages:[],interactions:this.#s.interactions,turn:x(e),contextRecovery:"contextRecovery"in e?e.contextRecovery:null})}applyError(e){this.#r({status:"error",threadId:this.id,snapshot:this.#s.snapshot,liveAssistantMessages:this.#s.liveAssistantMessages,interactions:this.#s.interactions,turn:this.#s.turn,contextRecovery:this.#s.contextRecovery,error:e})}applyRuntimeEvent(e){const t=e.event,s=t.event,n=t.turnId;let i=this.#s.liveAssistantMessages,r=this.#s.turn;if("message.started"===s.type)i=B(i,{status:"streaming",streamId:s.streamId,text:""});else if("message.delta"===s.type){const e=i.find(e=>e.streamId===s.streamId);i=B(i,{status:"streaming",streamId:s.streamId,text:`${e?.text??""}${s.delta}`})}else"message.completed"===s.type?i=B(i,{status:"completed",streamId:s.streamId,text:s.text}):"turn.started"===s.type&&void 0!==n?r={status:"running",turnId:n}:"turn.completed"===s.type&&void 0!==n?r={status:"completed",turnId:n}:"turn.cancelled"===s.type&&void 0!==n?r={status:"cancelled",turnId:n}:"turn.failed"===s.type&&void 0!==n&&(r={status:"failed",turnId:n,stage:s.stage});this.#o({liveAssistantMessages:i,turn:r,..."context.recovery.changed"===s.type?{contextRecovery:s.contextRecovery}:{}}),"turn.completed"!==s.type&&"turn.cancelled"!==s.type&&"turn.failed"!==s.type||this.refresh().catch(()=>{})}applyInteractionRequest(e){const t=this.#s.interactions.findIndex(t=>{const s=v(t);return s?.interactionId===e.interactionId}),s={status:"pending",request:e},n=-1===t?[...this.#s.interactions,s]:this.#s.interactions.map((e,n)=>n===t?s:e);this.#o({interactions:n})}applyInteractionTerminal(e){const s=O(e.type===t.interactionResolved?"resolved":"cancelled",e),n=this.#s.interactions.findIndex(t=>{const s=v(t);return s?.interactionId===e.interactionId}),i=-1===n?[...this.#s.interactions,s]:this.#s.interactions.map((e,t)=>t===n?s:e);this.#o({interactions:i})}#n(e){const t=this.#s.turn;"idle"!==t.status&&t.turnId===e.turnId&&C.has(t.status)||this.#o({turn:e})}#i(e,t){const s=this.#s.interactions.map(s=>{const n=v(s);return n?.interactionId===e?{status:t,request:n}:s});this.#o({interactions:s})}#o(e){const t={threadId:this.id,snapshot:this.#s.snapshot,liveAssistantMessages:e.liveAssistantMessages??this.#s.liveAssistantMessages,interactions:e.interactions??this.#s.interactions,turn:e.turn??this.#s.turn,contextRecovery:void 0===e.contextRecovery?this.#s.contextRecovery:e.contextRecovery};this.#r(null===this.#s.snapshot?{status:"loading",...t,snapshot:null}:{status:"ready",...t,snapshot:this.#s.snapshot})}#r(e){this.#s=R.parse(e);for(const e of this.#t)P(e,this.#s)}}export class RelayClientImpl{#a;#c;#l=new Set;#d=new Map;#h=new Map;#u=new Map;#p={status:"idle"};#y;#m;#f="1.1";#v=new Map;#g;#w;#R;#S=0;#b=!1;#I=!1;#q=q;#k=!1;#C=0;#T=!1;#E;constructor(e,t){if(this.#a=e,this.#c=t,this.#E=e.requestTimeoutMs??b,!Number.isFinite(this.#E)||this.#E<=0)throw new TypeError("requestTimeoutMs must be a positive finite number")}async connect(e){if("connected"===this.#p.status)return;if(this.#b)throw T(f.closed,"Relay client is closed",!1);this.#P();const t=this.#g;return void 0!==t?t:(this.#I=!1,this.#S=0,this.#M({status:"connecting",attempt:1}),this.#A(e?.signal,e?.timeoutMs).catch(e=>{const t=E(e);throw this.#b||this.#I||this.#M({status:"closed",reason:"session"===t.details.kind?"session":"transport"}),t}))}close(){this.#b||(this.#b=!0,this.#I=!0,this.#w?.abort(),this.#P(),this.#x("client closed"),this.#B(!0),this.#M({status:"closed",reason:"client"}))}getConnectionState(){return this.#p}subscribeConnection(e){return this.#l.add(e),P(e,this.#p),()=>{this.#l.delete(e)}}async listThreads(e={},t){return this.#O(s.threadList,e,t)}async createThread(e={},t){const n=await this.#O(s.threadCreate,{...e,requestId:this.#c.createUuid()},t),i=this.#F(n.thread.id);return await i.refresh(t).catch(()=>{}),i}async openThread(e,t){const n=await this.#O(s.threadOpen,{threadId:e},t),i=this.#F(e);return i.applySnapshot(n),i}async startTurn(e,t,n){return this.#O(s.turnStart,{requestId:this.#c.createUuid(),threadId:e,turnId:this.#c.createUuid(),input:{text:t}},n)}async cancelTurn(e,t,n){return this.#O(s.turnCancel,{requestId:this.#c.createUuid(),threadId:e,turnId:t},n)}async respondToInteraction(e,t,n){const r=i(e,t);return this.#O(s.interactionCandidate,r,n)}async getThreadCapabilities(e,t){return this.#O("thread.capabilities",{threadId:e},t)}getOperationResult(e,t,s){if("1.1"===this.#f)return Promise.reject(T(f.protocolError,"App results require Relay Wire 1.2",!1));const n=`${this.#C}:${this.#m}:${e}:${t}`;if(void 0===s){const e=this.#v.get(n);if(e)return e}const i=this.#O("operation.result.get",{threadId:e,operationId:t},s);return void 0===s&&(this.#v.set(n,i),i.finally(()=>{this.#v.get(n)===i&&this.#v.delete(n)}).catch(()=>{})),i}async getThreadSnapshot(e,t){return this.#O(s.threadSnapshot,{threadId:e,limit:100},t)}async#W(e,t){const s=t??this.#E;if(!Number.isFinite(s)||s<=0)throw new TypeError("timeoutMs must be a positive finite number");const n=new AbortController;this.#w=n;let i=!1;const r=()=>{n.abort()};e?.addEventListener("abort",r,{once:!0}),!0===e?.aborted&&n.abort();const o=this.#c.scheduler.setTimer(()=>{i=!0,n.abort()},s),a=new Promise((e,t)=>{const s=()=>{t(T(i?f.requestTimeout:f.aborted,i?"Relay session connection timed out":"Relay session request was aborted",i)),this.#y?.close(k.normal,"session attempt cancelled")};n.signal.aborted?s():n.signal.addEventListener("abort",s,{once:!0})});try{await Promise.race([this.#j(n.signal),a])}finally{this.#c.scheduler.clearTimer(o),e?.removeEventListener("abort",r),this.#w===n&&(this.#w=void 0)}}async#j(e){try{let t;try{t=await this.#a.getSession({signal:e,supportedRelayProtocolVersions:this.#a.relayProtocolVersions??["1.2","1.1"]})}catch(t){if(e.aborted)throw T(f.aborted,"Relay session request was aborted",!1);throw E(t)}if(e.aborted)throw T(f.aborted,"Relay session request was aborted",!1);const s=y.safeParse(t);if(!s.success)throw T(f.invalidSession,"Session endpoint returned an invalid Relay session",!1);if(Date.parse(s.data.expiresAt)<=this.#c.scheduler.now())throw T(f.invalidSession,"Session endpoint returned an expired Relay session",!1);this.#x("superseded relay session");const n=this.#c.createWebSocket(s.data.connectUrl);this.#y=n,await this.#L(n)}catch(e){throw E(e)}}#L(e){return new Promise((t,s)=>{const n={ready:!1,settled:!1},i=e=>{n.settled||(n.settled=!0,s(e))};e.setHandlers({onOpen:()=>{},onMessage:s=>{if(!n.ready||this.#y===e)try{const e=M(s);if(!n.ready){const s=A(e);if(void 0!==this.#m&&this.#m!==s.workspaceId)throw T(f.protocolError,"Relay session changed the bound Workspace",!1);return this.#m=s.workspaceId,this.#f=s.relayProtocolVersion,this.#v.clear(),n.ready=!0,n.settled=!0,this.#S=0,this.#T=!0,this.#M({status:"connected",workspaceStatus:s.workspaceStatus}),this.#U(),"online"===s.workspaceStatus&&this.#z(),void t()}this.#V(e)}catch(t){const s=E(t);n.ready?this.#$(s):i(s),e.close(k.invalidFrame,"invalid relay frame")}},onClose:()=>{const t=this.#y===e;t&&(this.#y=void 0),n.ready?t&&this.#N():i(T(f.transportError,"Relay connection closed before session.ready",!0))},onError:()=>{n.ready||i(T(f.transportError,"Relay WebSocket failed before session.ready",!0))}})})}#V(e){const t=p.safeParse(e);if(t.success)return void this.#G(t.data);const s=c.safeParse(e);if(!s.success)throw T(f.protocolError,"Relay sent an invalid Control 1.0 or Wire 1.1 frame",!1);this.#H(s.data)}#G(e){if("session.ready"===e.type)throw T(f.protocolError,"session.ready may only be the first frame",!1);if("workspace.status"===e.type){this.#D(e.workspaceId);const t=this.#p,s="connected"===t.status&&"offline"===t.workspaceStatus&&"online"===e.status;return this.#M({status:"connected",workspaceStatus:e.status}),s&&(this.#T=!0,this.#J()),void("online"===e.status&&this.#z())}const t=new RelayClientError({kind:"session",code:e.code,message:`Relay session failed: ${e.code}`,retryable:e.retryable});if(e.retryable)this.#y?.close(k.sessionRetryable,"retryable relay session error");else{this.#I=!0,this.#B(!0);for(const e of this.#d.values())e.applyError(t.details);this.#M({status:"closed",reason:"session"}),this.#y?.close(k.sessionFatal,"non-retryable relay session error")}}#H(e){if(e.type!==t.runtimeResponse){if(e.type===t.runtimeGap)return this.#D(e.workspaceId),this.#Q(e.throughRelaySequence),void this.#K(e.throughRelaySequence);if(e.type!==t.runtimeEvent&&e.type!==t.interactionRequest&&e.type!==t.interactionResolved&&e.type!==t.interactionCancelled)throw T(f.protocolError,`Relay sent disallowed Browser inbound frame: ${e.type}`,!1);this.#X({relaySequence:e.relaySequence,message:e})}else this.#Y(e)}#Y(t){this.#D(t.workspaceId);const s=this.#h.get(t.requestId);if(void 0!==s){if(void 0!==t.error){const n=t.error.code===e.remoteRequestDenied?"Remote request denied":t.error.message;return void this.#Z(s,new RelayClientError({kind:"remote",code:t.error.code,message:n,retryable:t.error.retryable}))}if(void 0!==t.result)try{const e=o(this.#f,s.method,t.result);this.#Z(s,void 0,e)}catch{this.#Z(s,T(f.protocolError,`Relay returned an invalid ${s.method} result`,!1))}else this.#Z(s,T(f.protocolError,"runtime.response contains neither result nor error",!1))}}#X(e){if(this.#D(e.message.workspaceId),this.#Q(e.relaySequence),e.relaySequence<=this.#q)this.#U();else if(this.#k)this.#u.set(e.relaySequence,e);else{if(e.relaySequence!==this.#q+1)return this.#u.set(e.relaySequence,e),void this.#K(e.relaySequence-1);this.#_(e.message),this.#q=e.relaySequence,this.#U()}}#_(e){const s=e.type===t.runtimeEvent?e.event.threadId:e.threadId,n=this.#F(s);e.type===t.runtimeEvent?n.applyRuntimeEvent(e):e.type===t.interactionRequest?n.applyInteractionRequest(e):n.applyInteractionTerminal(e)}#K(e){if(e<=this.#q)return void this.#U();if(this.#k)return;this.#k=!0;const t=e,s=this.#C,n=[...this.#d.values()].map(async e=>e.refresh());Promise.all(n).then(()=>{if(this.#C===s){this.#q=t;for(const e of[...this.#u.keys()])e<=t&&this.#u.delete(e);this.#k=!1,this.#U(),this.#ee()}}).catch(()=>{this.#C===s&&(this.#k=!1,this.#y?.close(k.sessionRetryable,"snapshot recovery failed"))})}#Q(e){this.#T&&(this.#T=!1,e>this.#q||(this.#C+=1,this.#q=q,this.#u.clear(),this.#k=!1,this.#J()))}#J(){for(const e of this.#d.values())e.refresh().catch(()=>{})}#ee(){for(;!this.#k;){const e=this.#q+1,t=this.#u.get(e);if(void 0===t){const t=[...this.#u.keys()].sort((e,t)=>e-t)[0];return void(void 0!==t&&t>e&&this.#K(t-1))}this.#u.delete(e),this.#_(t.message),this.#q=e,this.#U()}}#U(){const e=this.#m,s=this.#y;if(void 0===e||1!==s?.readyState)return;const n=a.parse({type:t.runtimeAck,workspaceId:e,throughRelaySequence:this.#q});s.send(JSON.stringify(n))}#N(){this.#b||this.#I||(this.#te(),this.#se())}#se(){if(void 0!==this.#R||this.#b||this.#I)return;this.#S+=1;const e=this.#c.reconnectDelayMs(this.#S),t=new Date(this.#c.scheduler.now()+e).toISOString();this.#M({status:"reconnecting",attempt:this.#S,retryAt:t}),this.#R=this.#c.scheduler.setTimer(()=>{this.#R=void 0,this.#b||this.#I||void 0!==this.#g||"connected"===this.#p.status||this.#A().catch(e=>{if(this.#b)return;if(!E(e).details.retryable)return this.#I=!0,this.#B(!0),void this.#M({status:"closed",reason:"session"});this.#se()})},e)}#A(e,t){const s=this.#W(e,t).finally(()=>{this.#g===s&&(this.#g=void 0)});return this.#g=s,s}#x(e){const t=this.#y;void 0!==t&&(this.#y=void 0,t.close(k.normal,e))}#P(){void 0!==this.#R&&(this.#c.scheduler.clearTimer(this.#R),this.#R=void 0)}#z(){for(const e of this.#h.values())e.mutation&&this.#ne(e)}#O(e,s,i){if("connected"!==this.#p.status||"online"!==this.#p.workspaceStatus||void 0===this.#m){const e="connected"===this.#p.status&&"offline"===this.#p.workspaceStatus;return Promise.reject(T(e?f.workspaceOffline:f.connectionUnavailable,e?"The local Workspace is offline":"Relay is not connected",!0))}if(!0===i?.signal?.aborted)return Promise.reject(T(f.aborted,"Relay request was aborted",!1));const o=r("1.2",e,s),a=l.parse(this.#c.createUuid()),c=h.parse({type:t.runtimeRequest,requestId:a,workspaceId:this.#m,method:e,params:o}),d=i?.timeoutMs??this.#E;if(!Number.isFinite(d)||d<=0)return Promise.reject(new TypeError("timeoutMs must be a positive finite number"));const u="mutation"===n[e];return new Promise((t,s)=>{const n=i?.signal?()=>{const e=this.#h.get(a);if(void 0===e)return;const t=e.mutation&&e.sent;this.#Z(e,T(t?f.outcomeUnknown:f.aborted,t?"Mutation outcome is unknown after Browser cancellation":"Relay request was aborted",!1))}:void 0,r={requestId:a,method:e,frame:JSON.stringify(c),mutation:u,sent:!1,resolve:e=>{t(e)},reject:s,signal:i?.signal,abortListener:n,timeoutHandle:void 0};r.timeoutHandle=this.#c.scheduler.setTimer(()=>{const e=this.#h.get(a);if(void 0===e)return;const t=e.mutation&&e.sent;this.#Z(e,T(t?f.outcomeUnknown:f.requestTimeout,t?"Mutation outcome is unknown after Relay timeout":"Relay request timed out",!t))},d),i?.signal?.addEventListener("abort",n??(()=>{}),{once:!0}),this.#h.set(a,r),this.#ne(r)})}#ne(e){const t=this.#y;1===t?.readyState&&"connected"===this.#p.status&&"online"===this.#p.workspaceStatus&&(t.send(e.frame),e.sent=!0)}#Z(e,t,s){this.#h.delete(e.requestId)&&(void 0!==e.timeoutHandle&&this.#c.scheduler.clearTimer(e.timeoutHandle),void 0!==e.signal&&void 0!==e.abortListener&&e.signal.removeEventListener("abort",e.abortListener),void 0!==t?e.reject(t):e.resolve(s))}#te(){for(const e of[...this.#h.values()])e.mutation||this.#Z(e,T(f.transportError,"Relay disconnected before the query completed",!0))}#B(e){for(const t of[...this.#h.values()]){const s=e&&t.mutation&&t.sent;this.#Z(t,T(s?f.outcomeUnknown:f.closed,s?"Mutation outcome is unknown after Relay closed":"Relay client is closed",!1))}}#D(e){if(this.#m!==e)throw T(f.protocolError,"Relay frame does not match the bound Workspace",!1)}#F(e){const t=this.#d.get(e);if(void 0!==t)return t;const s=new RelayThreadImpl(this,e);return this.#d.set(e,s),s}#M(e){this.#p=w.parse(e);for(const e of this.#l)P(e,this.#p)}#$(e){this.#I=!0,this.#B(!0);for(const t of this.#d.values())t.applyError(e.details);this.#M({status:"closed",reason:"protocol"})}}export function createRelayClientWithRuntime(e,t){return new RelayClientImpl(e,t)}export function createRelayClient(e){return createRelayClientWithRuntime(e,S)}
|
package/dist/schemas.d.ts
CHANGED
|
@@ -291,6 +291,12 @@ export declare const relayTurnStateSchema: z.ZodDiscriminatedUnion<[z.ZodReadonl
|
|
|
291
291
|
}, z.core.$strict>>]>;
|
|
292
292
|
export declare const relayThreadViewSchema: z.ZodDiscriminatedUnion<[z.ZodReadonly<z.ZodObject<{
|
|
293
293
|
snapshot: z.ZodNull;
|
|
294
|
+
contextRecovery: z.ZodDefault<z.ZodNullable<z.ZodReadonly<z.ZodObject<{
|
|
295
|
+
checkpointId: z.ZodString;
|
|
296
|
+
required: z.ZodBoolean;
|
|
297
|
+
unextracted: z.ZodBoolean;
|
|
298
|
+
requiredItemCount: z.ZodNumber;
|
|
299
|
+
}, z.core.$strict>>>>;
|
|
294
300
|
threadId: z.core.$ZodBranded<z.ZodString, "ThreadId">;
|
|
295
301
|
liveAssistantMessages: z.ZodReadonly<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodReadonly<z.ZodObject<{
|
|
296
302
|
status: z.ZodLiteral<"streaming">;
|
|
@@ -497,7 +503,7 @@ export declare const relayThreadViewSchema: z.ZodDiscriminatedUnion<[z.ZodReadon
|
|
|
497
503
|
}, z.core.$strict>>]>;
|
|
498
504
|
status: z.ZodLiteral<"loading">;
|
|
499
505
|
}, z.core.$strict>>, z.ZodReadonly<z.ZodObject<{
|
|
500
|
-
snapshot: z.ZodReadonly<z.ZodObject<{
|
|
506
|
+
snapshot: z.ZodUnion<readonly [z.ZodReadonly<z.ZodObject<{
|
|
501
507
|
thread: z.ZodReadonly<z.ZodObject<{
|
|
502
508
|
id: z.core.$ZodBranded<z.ZodString, "ThreadId">;
|
|
503
509
|
title: z.ZodOptional<z.ZodString>;
|
|
@@ -584,7 +590,106 @@ export declare const relayThreadViewSchema: z.ZodDiscriminatedUnion<[z.ZodReadon
|
|
|
584
590
|
}, z.core.$strict>>>;
|
|
585
591
|
nextBeforeSequence: z.ZodNullable<z.ZodNumber>;
|
|
586
592
|
}, z.core.$strict>>;
|
|
587
|
-
|
|
593
|
+
contextRecovery: z.ZodNullable<z.ZodReadonly<z.ZodObject<{
|
|
594
|
+
checkpointId: z.ZodString;
|
|
595
|
+
required: z.ZodBoolean;
|
|
596
|
+
unextracted: z.ZodBoolean;
|
|
597
|
+
requiredItemCount: z.ZodNumber;
|
|
598
|
+
}, z.core.$strict>>>;
|
|
599
|
+
}, z.core.$strict>>, z.ZodReadonly<z.ZodObject<{
|
|
600
|
+
thread: z.ZodReadonly<z.ZodObject<{
|
|
601
|
+
id: z.core.$ZodBranded<z.ZodString, "ThreadId">;
|
|
602
|
+
title: z.ZodOptional<z.ZodString>;
|
|
603
|
+
model: z.ZodOptional<z.ZodString>;
|
|
604
|
+
createdAt: z.ZodString;
|
|
605
|
+
updatedAt: z.ZodString;
|
|
606
|
+
messageCount: z.ZodNumber;
|
|
607
|
+
}, z.core.$strict>>;
|
|
608
|
+
messages: z.ZodReadonly<z.ZodObject<{
|
|
609
|
+
items: z.ZodArray<z.ZodReadonly<z.ZodObject<{
|
|
610
|
+
sequence: z.ZodNumber;
|
|
611
|
+
role: z.ZodEnum<{
|
|
612
|
+
user: "user";
|
|
613
|
+
assistant: "assistant";
|
|
614
|
+
}>;
|
|
615
|
+
createdAt: z.ZodString;
|
|
616
|
+
parts: z.ZodArray<z.ZodReadonly<z.ZodObject<{
|
|
617
|
+
type: z.ZodLiteral<"text">;
|
|
618
|
+
text: z.ZodString;
|
|
619
|
+
}, z.core.$strict>>>;
|
|
620
|
+
}, z.core.$strict>>>;
|
|
621
|
+
nextBeforeSequence: z.ZodNullable<z.ZodNumber>;
|
|
622
|
+
}, z.core.$strict>>;
|
|
623
|
+
activeTurn: z.ZodOptional<z.ZodReadonly<z.ZodObject<{
|
|
624
|
+
id: z.core.$ZodBranded<z.ZodString, "TurnId">;
|
|
625
|
+
status: z.ZodEnum<{
|
|
626
|
+
running: "running";
|
|
627
|
+
cancelling: "cancelling";
|
|
628
|
+
}>;
|
|
629
|
+
startedAt: z.ZodString;
|
|
630
|
+
}, z.core.$strict>>>;
|
|
631
|
+
pendingApprovals: z.ZodArray<z.ZodReadonly<z.ZodObject<{
|
|
632
|
+
id: z.core.$ZodBranded<z.ZodString, "ApprovalId">;
|
|
633
|
+
turnId: z.core.$ZodBranded<z.ZodString, "TurnId">;
|
|
634
|
+
agentName: z.ZodString;
|
|
635
|
+
toolName: z.ZodString;
|
|
636
|
+
preview: z.ZodJSONSchema;
|
|
637
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
638
|
+
}, z.core.$strict>>>;
|
|
639
|
+
transcriptCompleteness: z.ZodEnum<{
|
|
640
|
+
complete: "complete";
|
|
641
|
+
legacy_snapshot: "legacy_snapshot";
|
|
642
|
+
}>;
|
|
643
|
+
operations: z.ZodReadonly<z.ZodObject<{
|
|
644
|
+
items: z.ZodArray<z.ZodReadonly<z.ZodObject<{
|
|
645
|
+
id: z.core.$ZodBranded<z.ZodString, "OperationId">;
|
|
646
|
+
sequence: z.ZodNumber;
|
|
647
|
+
toolCallId: z.ZodString;
|
|
648
|
+
agentName: z.ZodString;
|
|
649
|
+
toolName: z.ZodString;
|
|
650
|
+
createdAt: z.ZodString;
|
|
651
|
+
outcome: z.ZodReadonly<z.ZodObject<{
|
|
652
|
+
kind: z.ZodEnum<{
|
|
653
|
+
success: "success";
|
|
654
|
+
user_rejected: "user_rejected";
|
|
655
|
+
policy_denied: "policy_denied";
|
|
656
|
+
invalid_input: "invalid_input";
|
|
657
|
+
cancelled: "cancelled";
|
|
658
|
+
tool_failed: "tool_failed";
|
|
659
|
+
}>;
|
|
660
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
661
|
+
}, z.core.$strict>>;
|
|
662
|
+
display: z.ZodJSONSchema;
|
|
663
|
+
appOutput: z.ZodOptional<z.ZodReadonly<z.ZodDiscriminatedUnion<[z.ZodReadonly<z.ZodObject<{
|
|
664
|
+
status: z.ZodLiteral<"rejected">;
|
|
665
|
+
reason: z.ZodEnum<{
|
|
666
|
+
credential_field: "credential_field";
|
|
667
|
+
credential_value: "credential_value";
|
|
668
|
+
}>;
|
|
669
|
+
field: z.ZodOptional<z.ZodString>;
|
|
670
|
+
}, z.core.$strict>>, z.ZodObject<{
|
|
671
|
+
status: z.ZodLiteral<"available">;
|
|
672
|
+
schemaId: z.ZodString;
|
|
673
|
+
schemaVersion: z.ZodNumber;
|
|
674
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
675
|
+
status: z.ZodEnum<{
|
|
676
|
+
not_provided: "not_provided";
|
|
677
|
+
invalid: "invalid";
|
|
678
|
+
too_large: "too_large";
|
|
679
|
+
expired: "expired";
|
|
680
|
+
denied: "denied";
|
|
681
|
+
}>;
|
|
682
|
+
}, z.core.$strict>]>>>;
|
|
683
|
+
}, z.core.$strict>>>;
|
|
684
|
+
nextBeforeSequence: z.ZodNullable<z.ZodNumber>;
|
|
685
|
+
}, z.core.$strict>>;
|
|
686
|
+
}, z.core.$strict>>]>;
|
|
687
|
+
contextRecovery: z.ZodDefault<z.ZodNullable<z.ZodReadonly<z.ZodObject<{
|
|
688
|
+
checkpointId: z.ZodString;
|
|
689
|
+
required: z.ZodBoolean;
|
|
690
|
+
unextracted: z.ZodBoolean;
|
|
691
|
+
requiredItemCount: z.ZodNumber;
|
|
692
|
+
}, z.core.$strict>>>>;
|
|
588
693
|
threadId: z.core.$ZodBranded<z.ZodString, "ThreadId">;
|
|
589
694
|
liveAssistantMessages: z.ZodReadonly<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodReadonly<z.ZodObject<{
|
|
590
695
|
status: z.ZodLiteral<"streaming">;
|
|
@@ -791,7 +896,100 @@ export declare const relayThreadViewSchema: z.ZodDiscriminatedUnion<[z.ZodReadon
|
|
|
791
896
|
}, z.core.$strict>>]>;
|
|
792
897
|
status: z.ZodLiteral<"ready">;
|
|
793
898
|
}, z.core.$strict>>, z.ZodReadonly<z.ZodObject<{
|
|
794
|
-
snapshot: z.ZodNullable<z.ZodReadonly<z.ZodObject<{
|
|
899
|
+
snapshot: z.ZodNullable<z.ZodUnion<readonly [z.ZodReadonly<z.ZodObject<{
|
|
900
|
+
thread: z.ZodReadonly<z.ZodObject<{
|
|
901
|
+
id: z.core.$ZodBranded<z.ZodString, "ThreadId">;
|
|
902
|
+
title: z.ZodOptional<z.ZodString>;
|
|
903
|
+
model: z.ZodOptional<z.ZodString>;
|
|
904
|
+
createdAt: z.ZodString;
|
|
905
|
+
updatedAt: z.ZodString;
|
|
906
|
+
messageCount: z.ZodNumber;
|
|
907
|
+
}, z.core.$strict>>;
|
|
908
|
+
messages: z.ZodReadonly<z.ZodObject<{
|
|
909
|
+
items: z.ZodArray<z.ZodReadonly<z.ZodObject<{
|
|
910
|
+
sequence: z.ZodNumber;
|
|
911
|
+
role: z.ZodEnum<{
|
|
912
|
+
user: "user";
|
|
913
|
+
assistant: "assistant";
|
|
914
|
+
}>;
|
|
915
|
+
createdAt: z.ZodString;
|
|
916
|
+
parts: z.ZodArray<z.ZodReadonly<z.ZodObject<{
|
|
917
|
+
type: z.ZodLiteral<"text">;
|
|
918
|
+
text: z.ZodString;
|
|
919
|
+
}, z.core.$strict>>>;
|
|
920
|
+
}, z.core.$strict>>>;
|
|
921
|
+
nextBeforeSequence: z.ZodNullable<z.ZodNumber>;
|
|
922
|
+
}, z.core.$strict>>;
|
|
923
|
+
activeTurn: z.ZodOptional<z.ZodReadonly<z.ZodObject<{
|
|
924
|
+
id: z.core.$ZodBranded<z.ZodString, "TurnId">;
|
|
925
|
+
status: z.ZodEnum<{
|
|
926
|
+
running: "running";
|
|
927
|
+
cancelling: "cancelling";
|
|
928
|
+
}>;
|
|
929
|
+
startedAt: z.ZodString;
|
|
930
|
+
}, z.core.$strict>>>;
|
|
931
|
+
pendingApprovals: z.ZodArray<z.ZodReadonly<z.ZodObject<{
|
|
932
|
+
id: z.core.$ZodBranded<z.ZodString, "ApprovalId">;
|
|
933
|
+
turnId: z.core.$ZodBranded<z.ZodString, "TurnId">;
|
|
934
|
+
agentName: z.ZodString;
|
|
935
|
+
toolName: z.ZodString;
|
|
936
|
+
preview: z.ZodJSONSchema;
|
|
937
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
938
|
+
}, z.core.$strict>>>;
|
|
939
|
+
transcriptCompleteness: z.ZodEnum<{
|
|
940
|
+
complete: "complete";
|
|
941
|
+
legacy_snapshot: "legacy_snapshot";
|
|
942
|
+
}>;
|
|
943
|
+
operations: z.ZodReadonly<z.ZodObject<{
|
|
944
|
+
items: z.ZodArray<z.ZodReadonly<z.ZodObject<{
|
|
945
|
+
id: z.core.$ZodBranded<z.ZodString, "OperationId">;
|
|
946
|
+
sequence: z.ZodNumber;
|
|
947
|
+
toolCallId: z.ZodString;
|
|
948
|
+
agentName: z.ZodString;
|
|
949
|
+
toolName: z.ZodString;
|
|
950
|
+
createdAt: z.ZodString;
|
|
951
|
+
outcome: z.ZodReadonly<z.ZodObject<{
|
|
952
|
+
kind: z.ZodEnum<{
|
|
953
|
+
success: "success";
|
|
954
|
+
user_rejected: "user_rejected";
|
|
955
|
+
policy_denied: "policy_denied";
|
|
956
|
+
invalid_input: "invalid_input";
|
|
957
|
+
cancelled: "cancelled";
|
|
958
|
+
tool_failed: "tool_failed";
|
|
959
|
+
}>;
|
|
960
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
961
|
+
}, z.core.$strict>>;
|
|
962
|
+
display: z.ZodJSONSchema;
|
|
963
|
+
appOutput: z.ZodOptional<z.ZodReadonly<z.ZodDiscriminatedUnion<[z.ZodReadonly<z.ZodObject<{
|
|
964
|
+
status: z.ZodLiteral<"rejected">;
|
|
965
|
+
reason: z.ZodEnum<{
|
|
966
|
+
credential_field: "credential_field";
|
|
967
|
+
credential_value: "credential_value";
|
|
968
|
+
}>;
|
|
969
|
+
field: z.ZodOptional<z.ZodString>;
|
|
970
|
+
}, z.core.$strict>>, z.ZodObject<{
|
|
971
|
+
status: z.ZodLiteral<"available">;
|
|
972
|
+
schemaId: z.ZodString;
|
|
973
|
+
schemaVersion: z.ZodNumber;
|
|
974
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
975
|
+
status: z.ZodEnum<{
|
|
976
|
+
not_provided: "not_provided";
|
|
977
|
+
invalid: "invalid";
|
|
978
|
+
too_large: "too_large";
|
|
979
|
+
expired: "expired";
|
|
980
|
+
denied: "denied";
|
|
981
|
+
}>;
|
|
982
|
+
}, z.core.$strict>]>>>;
|
|
983
|
+
}, z.core.$strict>>>;
|
|
984
|
+
nextBeforeSequence: z.ZodNullable<z.ZodNumber>;
|
|
985
|
+
}, z.core.$strict>>;
|
|
986
|
+
contextRecovery: z.ZodNullable<z.ZodReadonly<z.ZodObject<{
|
|
987
|
+
checkpointId: z.ZodString;
|
|
988
|
+
required: z.ZodBoolean;
|
|
989
|
+
unextracted: z.ZodBoolean;
|
|
990
|
+
requiredItemCount: z.ZodNumber;
|
|
991
|
+
}, z.core.$strict>>>;
|
|
992
|
+
}, z.core.$strict>>, z.ZodReadonly<z.ZodObject<{
|
|
795
993
|
thread: z.ZodReadonly<z.ZodObject<{
|
|
796
994
|
id: z.core.$ZodBranded<z.ZodString, "ThreadId">;
|
|
797
995
|
title: z.ZodOptional<z.ZodString>;
|
|
@@ -878,7 +1076,7 @@ export declare const relayThreadViewSchema: z.ZodDiscriminatedUnion<[z.ZodReadon
|
|
|
878
1076
|
}, z.core.$strict>>>;
|
|
879
1077
|
nextBeforeSequence: z.ZodNullable<z.ZodNumber>;
|
|
880
1078
|
}, z.core.$strict>>;
|
|
881
|
-
}, z.core.$strict
|
|
1079
|
+
}, z.core.$strict>>]>>;
|
|
882
1080
|
error: z.ZodDiscriminatedUnion<[z.ZodReadonly<z.ZodObject<{
|
|
883
1081
|
code: z.ZodEnum<{
|
|
884
1082
|
readonly aborted: "ABORTED";
|
|
@@ -912,6 +1110,12 @@ export declare const relayThreadViewSchema: z.ZodDiscriminatedUnion<[z.ZodReadon
|
|
|
912
1110
|
retryable: z.ZodBoolean;
|
|
913
1111
|
kind: z.ZodLiteral<"remote">;
|
|
914
1112
|
}, z.core.$strict>>]>;
|
|
1113
|
+
contextRecovery: z.ZodDefault<z.ZodNullable<z.ZodReadonly<z.ZodObject<{
|
|
1114
|
+
checkpointId: z.ZodString;
|
|
1115
|
+
required: z.ZodBoolean;
|
|
1116
|
+
unextracted: z.ZodBoolean;
|
|
1117
|
+
requiredItemCount: z.ZodNumber;
|
|
1118
|
+
}, z.core.$strict>>>>;
|
|
915
1119
|
threadId: z.core.$ZodBranded<z.ZodString, "ThreadId">;
|
|
916
1120
|
liveAssistantMessages: z.ZodReadonly<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodReadonly<z.ZodObject<{
|
|
917
1121
|
status: z.ZodLiteral<"streaming">;
|
package/dist/schemas.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{relayInteractionRequestSchemaV11 as t,relayRequestMethodSchemasV12 as e}from"@roll-agent/relay-protocol";import{relayClientErrorCodeSchema as
|
|
1
|
+
import{relayInteractionRequestSchemaV11 as t,relayRequestMethodSchemasV12 as e,relayThreadSnapshotSchemaV13 as r}from"@roll-agent/relay-protocol";import{relayClientErrorCodeSchema as n}from"@roll-agent/relay-protocol/control";import{z as o}from"zod/v4";const a=e["thread.open"].params.def.innerType.shape.threadId,s=e["turn.cancel"].params.def.innerType.shape.turnId,i=o.union([r,e["thread.snapshot"].result]);export const RELAY_CONNECTION_STATUSES={idle:"idle",connecting:"connecting",connected:"connected",reconnecting:"reconnecting",closed:"closed"};export const relayConnectionStateSchema=o.discriminatedUnion("status",[o.object({status:o.literal(RELAY_CONNECTION_STATUSES.idle)}).strict().readonly(),o.object({status:o.literal(RELAY_CONNECTION_STATUSES.connecting),attempt:o.number().int().positive()}).strict().readonly(),o.object({status:o.literal(RELAY_CONNECTION_STATUSES.connected),workspaceStatus:o.enum(["online","offline"])}).strict().readonly(),o.object({status:o.literal(RELAY_CONNECTION_STATUSES.reconnecting),attempt:o.number().int().positive(),retryAt:o.string().datetime({offset:!0})}).strict().readonly(),o.object({status:o.literal(RELAY_CONNECTION_STATUSES.closed),reason:o.enum(["client","session","transport","protocol"])}).strict().readonly()]);export const RELAY_CLIENT_TRANSPORT_ERROR_CODES={aborted:"ABORTED",closed:"CLIENT_CLOSED",connectionUnavailable:"CONNECTION_UNAVAILABLE",invalidSession:"INVALID_SESSION",outcomeUnknown:"OUTCOME_UNKNOWN",protocolError:"PROTOCOL_ERROR",requestTimeout:"REQUEST_TIMEOUT",transportError:"TRANSPORT_ERROR",workspaceOffline:"WORKSPACE_OFFLINE"};export const relayClientTransportErrorCodeSchema=o.enum(RELAY_CLIENT_TRANSPORT_ERROR_CODES);const l={message:o.string(),retryable:o.boolean()};export const relayClientErrorDetailsSchema=o.discriminatedUnion("kind",[o.object({kind:o.literal("transport"),...l,code:relayClientTransportErrorCodeSchema}).strict().readonly(),o.object({kind:o.literal("session"),...l,code:n}).strict().readonly(),o.object({kind:o.literal("remote"),...l,code:o.string().min(1)}).strict().readonly()]);export const relayLiveAssistantMessageSchema=o.discriminatedUnion("status",[o.object({status:o.literal("streaming"),streamId:o.string().uuid(),text:o.string()}).strict().readonly(),o.object({status:o.literal("completed"),streamId:o.string().uuid(),text:o.string()}).strict().readonly()]);const c={interactionId:t.options[0].def.innerType.shape.interactionId,threadId:a,turnId:s,method:t.options[0].def.innerType.shape.method.or(t.options[1].def.innerType.shape.method)};export const relayInteractionStateSchema=o.discriminatedUnion("status",[o.object({status:o.literal("pending"),request:t}).strict().readonly(),o.object({status:o.literal("responding"),request:t}).strict().readonly(),o.object({status:o.literal("resolved"),...c}).strict().readonly(),o.object({status:o.literal("cancelled"),...c}).strict().readonly()]);export const relayTurnStateSchema=o.discriminatedUnion("status",[o.object({status:o.literal("idle")}).strict().readonly(),o.object({status:o.literal("running"),turnId:s}).strict().readonly(),o.object({status:o.literal("cancelling"),turnId:s}).strict().readonly(),o.object({status:o.literal("completed"),turnId:s}).strict().readonly(),o.object({status:o.literal("cancelled"),turnId:s}).strict().readonly(),o.object({status:o.literal("failed"),turnId:s,stage:o.enum(["bootstrap","plan","execute"])}).strict().readonly()]);const d={contextRecovery:r.def.innerType.shape.contextRecovery.default(null),threadId:a,liveAssistantMessages:o.array(relayLiveAssistantMessageSchema).readonly(),interactions:o.array(relayInteractionStateSchema).readonly(),turn:relayTurnStateSchema};export const relayThreadViewSchema=o.discriminatedUnion("status",[o.object({status:o.literal("loading"),...d,snapshot:o.null()}).strict().readonly(),o.object({status:o.literal("ready"),...d,snapshot:i}).strict().readonly(),o.object({status:o.literal("error"),...d,snapshot:i.nullable(),error:relayClientErrorDetailsSchema}).strict().readonly()]);export function getPendingInteractionRequest(t){return"pending"===t.status||"responding"===t.status?t.request:void 0}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@roll-agent/relay-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"repository": {
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"node": ">=22.6.0"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@roll-agent/relay-protocol": "0.
|
|
34
|
+
"@roll-agent/relay-protocol": "0.5.0",
|
|
35
35
|
"zod": "3.25.76"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|