dsh-realtime 0.1.1 → 0.2.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/lib/error.d.ts +48 -2
- package/lib/error.d.ts.map +1 -1
- package/lib/error.js +56 -1
- package/lib/error.js.map +1 -1
- package/lib/types.d.ts +15 -3
- package/lib/types.d.ts.map +1 -1
- package/lib/types.js.map +1 -1
- package/package.json +1 -1
- package/src/error.ts +85 -2
- package/src/types.ts +15 -3
package/lib/error.d.ts
CHANGED
|
@@ -26,9 +26,51 @@ export declare const REALTIME_ERROR_CODES: Readonly<{
|
|
|
26
26
|
PROVIDER_ERROR: "PROVIDER_ERROR";
|
|
27
27
|
/** A recorded session could not be read as a recording. */
|
|
28
28
|
INVALID_RECORDING: "INVALID_RECORDING";
|
|
29
|
+
/**
|
|
30
|
+
* The capability is not configured yet — an expected state on a fresh install, not a fault.
|
|
31
|
+
*
|
|
32
|
+
* Distinct from {@link REALTIME_ERROR_CODES.CREDENTIAL_REJECTED}: absent is fixed by supplying a
|
|
33
|
+
* value, unusable by replacing one.
|
|
34
|
+
*/
|
|
35
|
+
NOT_CONFIGURED: "NOT_CONFIGURED";
|
|
36
|
+
/** A credential is present and the provider refused it. Replace it; retrying cannot help. */
|
|
37
|
+
CREDENTIAL_REJECTED: "CREDENTIAL_REJECTED";
|
|
38
|
+
/** The credential is valid but the account cannot use what was requested (model, region, scope). */
|
|
39
|
+
NOT_ENTITLED: "NOT_ENTITLED";
|
|
40
|
+
/** The account cannot pay for the request — an exhausted balance or a hard billing limit. */
|
|
41
|
+
INSUFFICIENT_CREDIT: "INSUFFICIENT_CREDIT";
|
|
42
|
+
/** The provider throttled the request. Repeating later may succeed. */
|
|
43
|
+
RATE_LIMITED: "RATE_LIMITED";
|
|
44
|
+
/** The provider was asked something and did not answer within the bound. */
|
|
45
|
+
PROVIDER_TIMEOUT: "PROVIDER_TIMEOUT";
|
|
46
|
+
/** The transport failed — a socket error, not a decision the provider made. */
|
|
47
|
+
NETWORK: "NETWORK";
|
|
29
48
|
}>;
|
|
30
49
|
/** One of {@link REALTIME_ERROR_CODES}. */
|
|
31
50
|
export type RealtimeErrorCode = (typeof REALTIME_ERROR_CODES)[keyof typeof REALTIME_ERROR_CODES];
|
|
51
|
+
/**
|
|
52
|
+
* What a caller can act on, carried as structure rather than prose.
|
|
53
|
+
*
|
|
54
|
+
* A failure a user cannot act on is a failure report that wasted their time. Every field here exists
|
|
55
|
+
* so the *class* of a failure survives translation: an agent can relay `remedy` verbatim, a settings
|
|
56
|
+
* surface can highlight `setting`, a retry loop can consult `retryable`, and a human reading a log
|
|
57
|
+
* can see the provider's own `providerCode` instead of a paraphrase of it.
|
|
58
|
+
*
|
|
59
|
+
* Never carries secret material. `providerCode` is the provider's own error code, never a credential.
|
|
60
|
+
*/
|
|
61
|
+
export interface RealtimeFailureDetail {
|
|
62
|
+
/**
|
|
63
|
+
* What to do about it, written to be relayed **verbatim** to whoever is trying to use the feature.
|
|
64
|
+
* One sentence, imperative, no jargon.
|
|
65
|
+
*/
|
|
66
|
+
remedy?: string;
|
|
67
|
+
/** The configuration key that must be supplied, when the failure is a configuration state. */
|
|
68
|
+
setting?: string;
|
|
69
|
+
/** Whether repeating the identical call could succeed without any change. */
|
|
70
|
+
retryable?: boolean;
|
|
71
|
+
/** The provider's own error code, verbatim. Never a translated or inferred value. */
|
|
72
|
+
providerCode?: string;
|
|
73
|
+
}
|
|
32
74
|
/**
|
|
33
75
|
* A typed seam failure.
|
|
34
76
|
*
|
|
@@ -38,11 +80,15 @@ export type RealtimeErrorCode = (typeof REALTIME_ERROR_CODES)[keyof typeof REALT
|
|
|
38
80
|
export declare class RealtimeError extends Error {
|
|
39
81
|
/** Stable machine code. Branch on this, never on `message`. */
|
|
40
82
|
readonly code: RealtimeErrorCode;
|
|
83
|
+
/** What a caller can act on, when the failure is one they can act on. */
|
|
84
|
+
readonly detail: RealtimeFailureDetail | undefined;
|
|
41
85
|
/**
|
|
42
86
|
* @param message - non-empty human-readable summary. Must not contain secret material.
|
|
43
87
|
* @param code - one of {@link REALTIME_ERROR_CODES}.
|
|
44
|
-
* @param options - optional `cause`.
|
|
88
|
+
* @param options - optional `cause`, and an optional structured `detail`.
|
|
45
89
|
*/
|
|
46
|
-
constructor(message: string, code: RealtimeErrorCode, options?: ErrorOptions
|
|
90
|
+
constructor(message: string, code: RealtimeErrorCode, options?: ErrorOptions & {
|
|
91
|
+
detail?: RealtimeFailureDetail;
|
|
92
|
+
});
|
|
47
93
|
}
|
|
48
94
|
//# sourceMappingURL=error.d.ts.map
|
package/lib/error.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../src/error.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,gDAAgD;AAChD,eAAO,MAAM,oBAAoB;IAC/B,iEAAiE;;IAEjE,yEAAyE;;IAEzE,yEAAyE;;IAEzE,wDAAwD;;IAExD,mEAAmE;;IAEnE,2CAA2C;;IAE3C,iGAAiG;;IAEjG,iGAAiG;;IAEjG,2DAA2D;;
|
|
1
|
+
{"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../src/error.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,gDAAgD;AAChD,eAAO,MAAM,oBAAoB;IAC/B,iEAAiE;;IAEjE,yEAAyE;;IAEzE,yEAAyE;;IAEzE,wDAAwD;;IAExD,mEAAmE;;IAEnE,2CAA2C;;IAE3C,iGAAiG;;IAEjG,iGAAiG;;IAEjG,2DAA2D;;IAS3D;;;;;OAKG;;IAEH,6FAA6F;;IAE7F,oGAAoG;;IAEpG,6FAA6F;;IAE7F,uEAAuE;;IAEvE,4EAA4E;;IAE5E,+EAA+E;;EAE/E,CAAA;AAEF,2CAA2C;AAC3C,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,OAAO,oBAAoB,CAAC,CAAA;AAEhG;;;;;;;;;GASG;AACH,MAAM,WAAW,qBAAqB;IACpC;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,8FAA8F;IAC9F,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,6EAA6E;IAC7E,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,qFAAqF;IACrF,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AA4BD;;;;;GAKG;AACH,qBAAa,aAAc,SAAQ,KAAK;IACtC,+DAA+D;IAC/D,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAA;IAEhC,yEAAyE;IACzE,QAAQ,CAAC,MAAM,EAAE,qBAAqB,GAAG,SAAS,CAAA;IAElD;;;;OAIG;IACH,YAAY,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG;QAAE,MAAM,CAAC,EAAE,qBAAqB,CAAA;KAAE,EAchH;CACF"}
|
package/lib/error.js
CHANGED
|
@@ -26,7 +26,56 @@ export const REALTIME_ERROR_CODES = Object.freeze({
|
|
|
26
26
|
PROVIDER_ERROR: 'PROVIDER_ERROR',
|
|
27
27
|
/** A recorded session could not be read as a recording. */
|
|
28
28
|
INVALID_RECORDING: 'INVALID_RECORDING',
|
|
29
|
+
// ---------------------------------------------------------------------------------------------
|
|
30
|
+
// Failure taxonomy. These exist so a consumer can branch on the *class* of a failure rather than
|
|
31
|
+
// parse prose: a setup state, a rejected credential, an account that cannot pay, and a throttle
|
|
32
|
+
// need four different responses, and collapsing them is what makes onboarding feel unfinished.
|
|
33
|
+
// ---------------------------------------------------------------------------------------------
|
|
34
|
+
/**
|
|
35
|
+
* The capability is not configured yet — an expected state on a fresh install, not a fault.
|
|
36
|
+
*
|
|
37
|
+
* Distinct from {@link REALTIME_ERROR_CODES.CREDENTIAL_REJECTED}: absent is fixed by supplying a
|
|
38
|
+
* value, unusable by replacing one.
|
|
39
|
+
*/
|
|
40
|
+
NOT_CONFIGURED: 'NOT_CONFIGURED',
|
|
41
|
+
/** A credential is present and the provider refused it. Replace it; retrying cannot help. */
|
|
42
|
+
CREDENTIAL_REJECTED: 'CREDENTIAL_REJECTED',
|
|
43
|
+
/** The credential is valid but the account cannot use what was requested (model, region, scope). */
|
|
44
|
+
NOT_ENTITLED: 'NOT_ENTITLED',
|
|
45
|
+
/** The account cannot pay for the request — an exhausted balance or a hard billing limit. */
|
|
46
|
+
INSUFFICIENT_CREDIT: 'INSUFFICIENT_CREDIT',
|
|
47
|
+
/** The provider throttled the request. Repeating later may succeed. */
|
|
48
|
+
RATE_LIMITED: 'RATE_LIMITED',
|
|
49
|
+
/** The provider was asked something and did not answer within the bound. */
|
|
50
|
+
PROVIDER_TIMEOUT: 'PROVIDER_TIMEOUT',
|
|
51
|
+
/** The transport failed — a socket error, not a decision the provider made. */
|
|
52
|
+
NETWORK: 'NETWORK',
|
|
53
|
+
});
|
|
54
|
+
/** Expected `typeof` for each optional detail field. Data, so the check costs one loop and not a branch each. */
|
|
55
|
+
const DETAIL_FIELD_TYPES = Object.freeze({
|
|
56
|
+
remedy: 'string',
|
|
57
|
+
setting: 'string',
|
|
58
|
+
providerCode: 'string',
|
|
59
|
+
retryable: 'boolean',
|
|
29
60
|
});
|
|
61
|
+
/**
|
|
62
|
+
* Reject a malformed detail before it is attached to a failure.
|
|
63
|
+
* @param detail - candidate detail.
|
|
64
|
+
* @throws TypeError naming the offending field.
|
|
65
|
+
*/
|
|
66
|
+
function assertDetail(detail) {
|
|
67
|
+
for (const [field, expected] of Object.entries(DETAIL_FIELD_TYPES)) {
|
|
68
|
+
const value = detail[field];
|
|
69
|
+
if (value === undefined)
|
|
70
|
+
continue;
|
|
71
|
+
if (typeof value !== expected) {
|
|
72
|
+
throw new TypeError(`RealtimeError detail.${field} must be a ${expected}`);
|
|
73
|
+
}
|
|
74
|
+
if (expected === 'string' && value === '') {
|
|
75
|
+
throw new TypeError(`RealtimeError detail.${field} must be non-empty when supplied`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
30
79
|
/**
|
|
31
80
|
* A typed seam failure.
|
|
32
81
|
*
|
|
@@ -36,10 +85,12 @@ export const REALTIME_ERROR_CODES = Object.freeze({
|
|
|
36
85
|
export class RealtimeError extends Error {
|
|
37
86
|
/** Stable machine code. Branch on this, never on `message`. */
|
|
38
87
|
code;
|
|
88
|
+
/** What a caller can act on, when the failure is one they can act on. */
|
|
89
|
+
detail;
|
|
39
90
|
/**
|
|
40
91
|
* @param message - non-empty human-readable summary. Must not contain secret material.
|
|
41
92
|
* @param code - one of {@link REALTIME_ERROR_CODES}.
|
|
42
|
-
* @param options - optional `cause`.
|
|
93
|
+
* @param options - optional `cause`, and an optional structured `detail`.
|
|
43
94
|
*/
|
|
44
95
|
constructor(message, code, options) {
|
|
45
96
|
if (typeof message !== 'string' || message.length === 0) {
|
|
@@ -48,9 +99,13 @@ export class RealtimeError extends Error {
|
|
|
48
99
|
if (typeof code !== 'string' || code.length === 0) {
|
|
49
100
|
throw new TypeError('RealtimeError code must be a non-empty string');
|
|
50
101
|
}
|
|
102
|
+
if (options?.detail !== undefined) {
|
|
103
|
+
assertDetail(options.detail);
|
|
104
|
+
}
|
|
51
105
|
super(message, options);
|
|
52
106
|
this.name = 'RealtimeError';
|
|
53
107
|
this.code = code;
|
|
108
|
+
this.detail = options?.detail;
|
|
54
109
|
}
|
|
55
110
|
}
|
|
56
111
|
//# sourceMappingURL=error.js.map
|
package/lib/error.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error.js","sourceRoot":"","sources":["../src/error.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,gDAAgD;AAChD,MAAM,CAAC,MAAM,oBAAoB,GAAG,MAAM,CAAC,MAAM,CAAC;IAChD,iEAAiE;IACjE,gBAAgB,EAAE,kBAAkB;IACpC,yEAAyE;IACzE,kBAAkB,EAAE,oBAAoB;IACxC,yEAAyE;IACzE,qBAAqB,EAAE,uBAAuB;IAC9C,wDAAwD;IACxD,UAAU,EAAE,YAAY;IACxB,mEAAmE;IACnE,cAAc,EAAE,gBAAgB;IAChC,2CAA2C;IAC3C,cAAc,EAAE,gBAAgB;IAChC,iGAAiG;IACjG,kBAAkB,EAAE,oBAAoB;IACxC,iGAAiG;IACjG,cAAc,EAAE,gBAAgB;IAChC,2DAA2D;IAC3D,iBAAiB,EAAE,mBAAmB;
|
|
1
|
+
{"version":3,"file":"error.js","sourceRoot":"","sources":["../src/error.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,gDAAgD;AAChD,MAAM,CAAC,MAAM,oBAAoB,GAAG,MAAM,CAAC,MAAM,CAAC;IAChD,iEAAiE;IACjE,gBAAgB,EAAE,kBAAkB;IACpC,yEAAyE;IACzE,kBAAkB,EAAE,oBAAoB;IACxC,yEAAyE;IACzE,qBAAqB,EAAE,uBAAuB;IAC9C,wDAAwD;IACxD,UAAU,EAAE,YAAY;IACxB,mEAAmE;IACnE,cAAc,EAAE,gBAAgB;IAChC,2CAA2C;IAC3C,cAAc,EAAE,gBAAgB;IAChC,iGAAiG;IACjG,kBAAkB,EAAE,oBAAoB;IACxC,iGAAiG;IACjG,cAAc,EAAE,gBAAgB;IAChC,2DAA2D;IAC3D,iBAAiB,EAAE,mBAAmB;IAEtC,gGAAgG;IAChG,iGAAiG;IACjG,gGAAgG;IAChG,+FAA+F;IAC/F,gGAAgG;IAEhG;;;;;OAKG;IACH,cAAc,EAAE,gBAAgB;IAChC,6FAA6F;IAC7F,mBAAmB,EAAE,qBAAqB;IAC1C,oGAAoG;IACpG,YAAY,EAAE,cAAc;IAC5B,6FAA6F;IAC7F,mBAAmB,EAAE,qBAAqB;IAC1C,uEAAuE;IACvE,YAAY,EAAE,cAAc;IAC5B,4EAA4E;IAC5E,gBAAgB,EAAE,kBAAkB;IACpC,+EAA+E;IAC/E,OAAO,EAAE,SAAS;CACnB,CAAC,CAAA;AA6BF,iHAAiH;AACjH,MAAM,kBAAkB,GAAG,MAAM,CAAC,MAAM,CAAC;IACvC,MAAM,EAAE,QAAQ;IAChB,OAAO,EAAE,QAAQ;IACjB,YAAY,EAAE,QAAQ;IACtB,SAAS,EAAE,SAAS;CACrB,CAAC,CAAA;AAEF;;;;GAIG;AACH,SAAS,YAAY,CAAC,MAA6B;IACjD,KAAK,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,kBAAkB,CAAC,EAAE,CAAC;QACnE,MAAM,KAAK,GAAI,MAAkC,CAAC,KAAK,CAAC,CAAA;QACxD,IAAI,KAAK,KAAK,SAAS;YAAE,SAAQ;QACjC,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC9B,MAAM,IAAI,SAAS,CAAC,wBAAwB,KAAK,cAAc,QAAQ,EAAE,CAAC,CAAA;QAC5E,CAAC;QACD,IAAI,QAAQ,KAAK,QAAQ,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;YAC1C,MAAM,IAAI,SAAS,CAAC,wBAAwB,KAAK,kCAAkC,CAAC,CAAA;QACtF,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,OAAO,aAAc,SAAQ,KAAK;IACtC,+DAA+D;IACtD,IAAI,CAAmB;IAEhC,yEAAyE;IAChE,MAAM,CAAmC;IAElD;;;;OAIG;IACH,YAAY,OAAe,EAAE,IAAuB,EAAE,OAA2D;QAC/G,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxD,MAAM,IAAI,SAAS,CAAC,kDAAkD,CAAC,CAAA;QACzE,CAAC;QACD,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAClD,MAAM,IAAI,SAAS,CAAC,+CAA+C,CAAC,CAAA;QACtE,CAAC;QACD,IAAI,OAAO,EAAE,MAAM,KAAK,SAAS,EAAE,CAAC;YAClC,YAAY,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;QAC9B,CAAC;QACD,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;QACvB,IAAI,CAAC,IAAI,GAAG,eAAe,CAAA;QAC3B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAChB,IAAI,CAAC,MAAM,GAAG,OAAO,EAAE,MAAM,CAAA;IAC/B,CAAC;CACF"}
|
package/lib/types.d.ts
CHANGED
|
@@ -144,20 +144,32 @@ export interface RealtimeSession {
|
|
|
144
144
|
unmuteInput(): void;
|
|
145
145
|
/**
|
|
146
146
|
* Return a delegated result for the model to **speak aloud**.
|
|
147
|
+
*
|
|
148
|
+
* Resolves on the provider's **acknowledgement** when it answers a delegation. With no
|
|
149
|
+
* `delegationId` there is no acknowledgement to wait for: the provider accepts a session-wide append
|
|
150
|
+
* in silence, because it does not begin applying context at a bare `session.start`. That form
|
|
151
|
+
* therefore resolves on the write and is **best-effort** — it is applied once audio has flowed, and
|
|
152
|
+
* this seam cannot say when. It is not a way to speak unprompted.
|
|
147
153
|
* @param content - plain text, non-empty and within {@link MAX_APPEND_CHARS}.
|
|
148
|
-
* @param delegationId - the delegation this answers, or `undefined` for
|
|
154
|
+
* @param delegationId - the delegation this answers, or `undefined` for best-effort session context.
|
|
149
155
|
*/
|
|
150
156
|
appendCommentary(content: string, delegationId?: string): Promise<void>;
|
|
151
157
|
/**
|
|
152
158
|
* Add context the model may use **without speaking it** — progress, facts, intermediate state.
|
|
159
|
+
*
|
|
160
|
+
* With no `delegationId` this resolves on the write and is best-effort, for the reason recorded on
|
|
161
|
+
* {@link appendCommentary}: the provider does not acknowledge a session-wide append.
|
|
153
162
|
* @param content - plain text, non-empty and within {@link MAX_APPEND_CHARS}.
|
|
154
|
-
* @param delegationId - the delegation this relates to, or `undefined` for
|
|
163
|
+
* @param delegationId - the delegation this relates to, or `undefined` for best-effort session context.
|
|
155
164
|
*/
|
|
156
165
|
appendThinking(content: string, delegationId?: string): Promise<void>;
|
|
157
166
|
/**
|
|
158
167
|
* Steer the live conversation's behaviour (tone, brevity, policy) without speaking anything.
|
|
168
|
+
*
|
|
169
|
+
* With no `delegationId` this resolves on the write and is best-effort, for the reason recorded on
|
|
170
|
+
* {@link appendCommentary}.
|
|
159
171
|
* @param content - plain text, non-empty and within {@link MAX_APPEND_CHARS}.
|
|
160
|
-
* @param delegationId - `undefined` for session-wide steering.
|
|
172
|
+
* @param delegationId - `undefined` for best-effort session-wide steering.
|
|
161
173
|
*/
|
|
162
174
|
appendInstructions(content: string, delegationId?: string): Promise<void>;
|
|
163
175
|
/**
|
package/lib/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,kDAAkD;AAClD,MAAM,WAAW,oBAAoB;IACnC,sGAAsG;IACtG,EAAE,EAAE,MAAM,CAAA;IACV,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAA;IACZ,+DAA+D;IAC/D,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,0GAA0G;AAC1G,MAAM,WAAW,iBAAiB;IAChC,+DAA+D;IAC/D,EAAE,EAAE,MAAM,CAAA;IACV,4BAA4B;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,iEAAiE;IACjE,eAAe,CAAC,EAAE,SAAS,gBAAgB,EAAE,CAAA;IAC7C,qDAAqD;IACrD,gBAAgB,CAAC,EAAE,SAAS,gBAAgB,EAAE,CAAA;CAC/C;AAED,oDAAoD;AACpD,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,CAAA;AAEzD,+CAA+C;AAC/C,MAAM,WAAW,mBAAmB;IAClC,iDAAiD;IACjD,UAAU,EAAE,MAAM,CAAA;IAClB,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAA;IAChB,+DAA+D;IAC/D,QAAQ,EAAE,OAAO,CAAA;CAClB;AAED,6CAA6C;AAC7C,MAAM,WAAW,sBAAsB;IACrC,oDAAoD;IACpD,QAAQ,EAAE,MAAM,CAAA;IAChB,sBAAsB;IACtB,KAAK,EAAE,MAAM,CAAA;IACb;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,mFAAmF;IACnF,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,oGAAoG;IACpG,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,qDAAqD;IACrD,QAAQ,CAAC,EAAE,uBAAuB,CAAA;CACnC;AAED,6EAA6E;AAC7E,MAAM,WAAW,kBAAkB;IACjC,0FAA0F;IAC1F,EAAE,EAAE,MAAM,CAAA;IACV,uEAAuE;IACvE,MAAM,EAAE,QAAQ,GAAG,WAAW,CAAA;IAC9B;;;;;OAKG;IACH,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,kEAAkE;AAClE,MAAM,WAAW,kBAAkB;IACjC,wBAAwB;IACxB,IAAI,EAAE,OAAO,GAAG,QAAQ,CAAA;IACxB,8DAA8D;IAC9D,IAAI,EAAE,MAAM,CAAA;IACZ,kDAAkD;IAClD,KAAK,EAAE,OAAO,CAAA;CACf;AAED,wFAAwF;AACxF,MAAM,WAAW,aAAa;IAC5B,wDAAwD;IACxD,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,gEAAgE;AAChE,MAAM,WAAW,uBAAuB;IACtC,uDAAuD;IACvD,OAAO,CAAC,CAAC,IAAI,EAAE,sBAAsB,GAAG,IAAI,CAAA;IAC5C,qCAAqC;IACrC,YAAY,CAAC,CAAC,UAAU,EAAE,kBAAkB,GAAG,IAAI,CAAA;IACnD,oDAAoD;IACpD,YAAY,CAAC,CAAC,UAAU,EAAE,kBAAkB,GAAG,IAAI,CAAA;IACnD,oCAAoC;IACpC,OAAO,CAAC,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAAA;IACpC,gEAAgE;IAChE,OAAO,CAAC,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAA;IACjC,uEAAuE;IACvE,QAAQ,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAChC;;;;;OAKG;IACH,OAAO,CAAC,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;CAC7B;AAED,0CAA0C;AAC1C,MAAM,WAAW,sBAAsB;IACrC,gCAAgC;IAChC,QAAQ,EAAE,MAAM,CAAA;IAChB,wGAAwG;IACxG,KAAK,EAAE,MAAM,CAAA;IACb,8CAA8C;IAC9C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,oDAAoD;IACpD,UAAU,EAAE,mBAAmB,CAAA;IAC/B,wCAAwC;IACxC,WAAW,EAAE,mBAAmB,CAAA;CACjC;AAED;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,qEAAqE;IACrE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,8CAA8C;IAC9C,QAAQ,CAAC,OAAO,EAAE,sBAAsB,CAAA;IAExC;;;;;;OAMG;IACH,SAAS,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAA;IAElC,gFAAgF;IAChF,SAAS,IAAI,IAAI,CAAA;IAEjB,6DAA6D;IAC7D,WAAW,IAAI,IAAI,CAAA;IAEnB
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,kDAAkD;AAClD,MAAM,WAAW,oBAAoB;IACnC,sGAAsG;IACtG,EAAE,EAAE,MAAM,CAAA;IACV,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAA;IACZ,+DAA+D;IAC/D,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,0GAA0G;AAC1G,MAAM,WAAW,iBAAiB;IAChC,+DAA+D;IAC/D,EAAE,EAAE,MAAM,CAAA;IACV,4BAA4B;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,iEAAiE;IACjE,eAAe,CAAC,EAAE,SAAS,gBAAgB,EAAE,CAAA;IAC7C,qDAAqD;IACrD,gBAAgB,CAAC,EAAE,SAAS,gBAAgB,EAAE,CAAA;CAC/C;AAED,oDAAoD;AACpD,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,CAAA;AAEzD,+CAA+C;AAC/C,MAAM,WAAW,mBAAmB;IAClC,iDAAiD;IACjD,UAAU,EAAE,MAAM,CAAA;IAClB,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAA;IAChB,+DAA+D;IAC/D,QAAQ,EAAE,OAAO,CAAA;CAClB;AAED,6CAA6C;AAC7C,MAAM,WAAW,sBAAsB;IACrC,oDAAoD;IACpD,QAAQ,EAAE,MAAM,CAAA;IAChB,sBAAsB;IACtB,KAAK,EAAE,MAAM,CAAA;IACb;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,mFAAmF;IACnF,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,oGAAoG;IACpG,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,qDAAqD;IACrD,QAAQ,CAAC,EAAE,uBAAuB,CAAA;CACnC;AAED,6EAA6E;AAC7E,MAAM,WAAW,kBAAkB;IACjC,0FAA0F;IAC1F,EAAE,EAAE,MAAM,CAAA;IACV,uEAAuE;IACvE,MAAM,EAAE,QAAQ,GAAG,WAAW,CAAA;IAC9B;;;;;OAKG;IACH,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,kEAAkE;AAClE,MAAM,WAAW,kBAAkB;IACjC,wBAAwB;IACxB,IAAI,EAAE,OAAO,GAAG,QAAQ,CAAA;IACxB,8DAA8D;IAC9D,IAAI,EAAE,MAAM,CAAA;IACZ,kDAAkD;IAClD,KAAK,EAAE,OAAO,CAAA;CACf;AAED,wFAAwF;AACxF,MAAM,WAAW,aAAa;IAC5B,wDAAwD;IACxD,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,gEAAgE;AAChE,MAAM,WAAW,uBAAuB;IACtC,uDAAuD;IACvD,OAAO,CAAC,CAAC,IAAI,EAAE,sBAAsB,GAAG,IAAI,CAAA;IAC5C,qCAAqC;IACrC,YAAY,CAAC,CAAC,UAAU,EAAE,kBAAkB,GAAG,IAAI,CAAA;IACnD,oDAAoD;IACpD,YAAY,CAAC,CAAC,UAAU,EAAE,kBAAkB,GAAG,IAAI,CAAA;IACnD,oCAAoC;IACpC,OAAO,CAAC,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAAA;IACpC,gEAAgE;IAChE,OAAO,CAAC,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAA;IACjC,uEAAuE;IACvE,QAAQ,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAChC;;;;;OAKG;IACH,OAAO,CAAC,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;CAC7B;AAED,0CAA0C;AAC1C,MAAM,WAAW,sBAAsB;IACrC,gCAAgC;IAChC,QAAQ,EAAE,MAAM,CAAA;IAChB,wGAAwG;IACxG,KAAK,EAAE,MAAM,CAAA;IACb,8CAA8C;IAC9C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,oDAAoD;IACpD,UAAU,EAAE,mBAAmB,CAAA;IAC/B,wCAAwC;IACxC,WAAW,EAAE,mBAAmB,CAAA;CACjC;AAED;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,qEAAqE;IACrE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,8CAA8C;IAC9C,QAAQ,CAAC,OAAO,EAAE,sBAAsB,CAAA;IAExC;;;;;;OAMG;IACH,SAAS,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAA;IAElC,gFAAgF;IAChF,SAAS,IAAI,IAAI,CAAA;IAEjB,6DAA6D;IAC7D,WAAW,IAAI,IAAI,CAAA;IAEnB;;;;;;;;;;OAUG;IACH,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAEvE;;;;;;;OAOG;IACH,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAErE;;;;;;;OAOG;IACH,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAEzE;;;OAGG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACvB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,OAAO,CAAA"}
|
package/lib/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAkMH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,CAAA"}
|
package/package.json
CHANGED
package/src/error.ts
CHANGED
|
@@ -27,11 +27,87 @@ export const REALTIME_ERROR_CODES = Object.freeze({
|
|
|
27
27
|
PROVIDER_ERROR: 'PROVIDER_ERROR',
|
|
28
28
|
/** A recorded session could not be read as a recording. */
|
|
29
29
|
INVALID_RECORDING: 'INVALID_RECORDING',
|
|
30
|
+
|
|
31
|
+
// ---------------------------------------------------------------------------------------------
|
|
32
|
+
// Failure taxonomy. These exist so a consumer can branch on the *class* of a failure rather than
|
|
33
|
+
// parse prose: a setup state, a rejected credential, an account that cannot pay, and a throttle
|
|
34
|
+
// need four different responses, and collapsing them is what makes onboarding feel unfinished.
|
|
35
|
+
// ---------------------------------------------------------------------------------------------
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The capability is not configured yet — an expected state on a fresh install, not a fault.
|
|
39
|
+
*
|
|
40
|
+
* Distinct from {@link REALTIME_ERROR_CODES.CREDENTIAL_REJECTED}: absent is fixed by supplying a
|
|
41
|
+
* value, unusable by replacing one.
|
|
42
|
+
*/
|
|
43
|
+
NOT_CONFIGURED: 'NOT_CONFIGURED',
|
|
44
|
+
/** A credential is present and the provider refused it. Replace it; retrying cannot help. */
|
|
45
|
+
CREDENTIAL_REJECTED: 'CREDENTIAL_REJECTED',
|
|
46
|
+
/** The credential is valid but the account cannot use what was requested (model, region, scope). */
|
|
47
|
+
NOT_ENTITLED: 'NOT_ENTITLED',
|
|
48
|
+
/** The account cannot pay for the request — an exhausted balance or a hard billing limit. */
|
|
49
|
+
INSUFFICIENT_CREDIT: 'INSUFFICIENT_CREDIT',
|
|
50
|
+
/** The provider throttled the request. Repeating later may succeed. */
|
|
51
|
+
RATE_LIMITED: 'RATE_LIMITED',
|
|
52
|
+
/** The provider was asked something and did not answer within the bound. */
|
|
53
|
+
PROVIDER_TIMEOUT: 'PROVIDER_TIMEOUT',
|
|
54
|
+
/** The transport failed — a socket error, not a decision the provider made. */
|
|
55
|
+
NETWORK: 'NETWORK',
|
|
30
56
|
})
|
|
31
57
|
|
|
32
58
|
/** One of {@link REALTIME_ERROR_CODES}. */
|
|
33
59
|
export type RealtimeErrorCode = (typeof REALTIME_ERROR_CODES)[keyof typeof REALTIME_ERROR_CODES]
|
|
34
60
|
|
|
61
|
+
/**
|
|
62
|
+
* What a caller can act on, carried as structure rather than prose.
|
|
63
|
+
*
|
|
64
|
+
* A failure a user cannot act on is a failure report that wasted their time. Every field here exists
|
|
65
|
+
* so the *class* of a failure survives translation: an agent can relay `remedy` verbatim, a settings
|
|
66
|
+
* surface can highlight `setting`, a retry loop can consult `retryable`, and a human reading a log
|
|
67
|
+
* can see the provider's own `providerCode` instead of a paraphrase of it.
|
|
68
|
+
*
|
|
69
|
+
* Never carries secret material. `providerCode` is the provider's own error code, never a credential.
|
|
70
|
+
*/
|
|
71
|
+
export interface RealtimeFailureDetail {
|
|
72
|
+
/**
|
|
73
|
+
* What to do about it, written to be relayed **verbatim** to whoever is trying to use the feature.
|
|
74
|
+
* One sentence, imperative, no jargon.
|
|
75
|
+
*/
|
|
76
|
+
remedy?: string
|
|
77
|
+
/** The configuration key that must be supplied, when the failure is a configuration state. */
|
|
78
|
+
setting?: string
|
|
79
|
+
/** Whether repeating the identical call could succeed without any change. */
|
|
80
|
+
retryable?: boolean
|
|
81
|
+
/** The provider's own error code, verbatim. Never a translated or inferred value. */
|
|
82
|
+
providerCode?: string
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Expected `typeof` for each optional detail field. Data, so the check costs one loop and not a branch each. */
|
|
86
|
+
const DETAIL_FIELD_TYPES = Object.freeze({
|
|
87
|
+
remedy: 'string',
|
|
88
|
+
setting: 'string',
|
|
89
|
+
providerCode: 'string',
|
|
90
|
+
retryable: 'boolean',
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Reject a malformed detail before it is attached to a failure.
|
|
95
|
+
* @param detail - candidate detail.
|
|
96
|
+
* @throws TypeError naming the offending field.
|
|
97
|
+
*/
|
|
98
|
+
function assertDetail(detail: RealtimeFailureDetail): void {
|
|
99
|
+
for (const [field, expected] of Object.entries(DETAIL_FIELD_TYPES)) {
|
|
100
|
+
const value = (detail as Record<string, unknown>)[field]
|
|
101
|
+
if (value === undefined) continue
|
|
102
|
+
if (typeof value !== expected) {
|
|
103
|
+
throw new TypeError(`RealtimeError detail.${field} must be a ${expected}`)
|
|
104
|
+
}
|
|
105
|
+
if (expected === 'string' && value === '') {
|
|
106
|
+
throw new TypeError(`RealtimeError detail.${field} must be non-empty when supplied`)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
35
111
|
/**
|
|
36
112
|
* A typed seam failure.
|
|
37
113
|
*
|
|
@@ -42,20 +118,27 @@ export class RealtimeError extends Error {
|
|
|
42
118
|
/** Stable machine code. Branch on this, never on `message`. */
|
|
43
119
|
readonly code: RealtimeErrorCode
|
|
44
120
|
|
|
121
|
+
/** What a caller can act on, when the failure is one they can act on. */
|
|
122
|
+
readonly detail: RealtimeFailureDetail | undefined
|
|
123
|
+
|
|
45
124
|
/**
|
|
46
125
|
* @param message - non-empty human-readable summary. Must not contain secret material.
|
|
47
126
|
* @param code - one of {@link REALTIME_ERROR_CODES}.
|
|
48
|
-
* @param options - optional `cause`.
|
|
127
|
+
* @param options - optional `cause`, and an optional structured `detail`.
|
|
49
128
|
*/
|
|
50
|
-
constructor(message: string, code: RealtimeErrorCode, options?: ErrorOptions) {
|
|
129
|
+
constructor(message: string, code: RealtimeErrorCode, options?: ErrorOptions & { detail?: RealtimeFailureDetail }) {
|
|
51
130
|
if (typeof message !== 'string' || message.length === 0) {
|
|
52
131
|
throw new TypeError('RealtimeError message must be a non-empty string')
|
|
53
132
|
}
|
|
54
133
|
if (typeof code !== 'string' || code.length === 0) {
|
|
55
134
|
throw new TypeError('RealtimeError code must be a non-empty string')
|
|
56
135
|
}
|
|
136
|
+
if (options?.detail !== undefined) {
|
|
137
|
+
assertDetail(options.detail)
|
|
138
|
+
}
|
|
57
139
|
super(message, options)
|
|
58
140
|
this.name = 'RealtimeError'
|
|
59
141
|
this.code = code
|
|
142
|
+
this.detail = options?.detail
|
|
60
143
|
}
|
|
61
144
|
}
|
package/src/types.ts
CHANGED
|
@@ -159,22 +159,34 @@ export interface RealtimeSession {
|
|
|
159
159
|
|
|
160
160
|
/**
|
|
161
161
|
* Return a delegated result for the model to **speak aloud**.
|
|
162
|
+
*
|
|
163
|
+
* Resolves on the provider's **acknowledgement** when it answers a delegation. With no
|
|
164
|
+
* `delegationId` there is no acknowledgement to wait for: the provider accepts a session-wide append
|
|
165
|
+
* in silence, because it does not begin applying context at a bare `session.start`. That form
|
|
166
|
+
* therefore resolves on the write and is **best-effort** — it is applied once audio has flowed, and
|
|
167
|
+
* this seam cannot say when. It is not a way to speak unprompted.
|
|
162
168
|
* @param content - plain text, non-empty and within {@link MAX_APPEND_CHARS}.
|
|
163
|
-
* @param delegationId - the delegation this answers, or `undefined` for
|
|
169
|
+
* @param delegationId - the delegation this answers, or `undefined` for best-effort session context.
|
|
164
170
|
*/
|
|
165
171
|
appendCommentary(content: string, delegationId?: string): Promise<void>
|
|
166
172
|
|
|
167
173
|
/**
|
|
168
174
|
* Add context the model may use **without speaking it** — progress, facts, intermediate state.
|
|
175
|
+
*
|
|
176
|
+
* With no `delegationId` this resolves on the write and is best-effort, for the reason recorded on
|
|
177
|
+
* {@link appendCommentary}: the provider does not acknowledge a session-wide append.
|
|
169
178
|
* @param content - plain text, non-empty and within {@link MAX_APPEND_CHARS}.
|
|
170
|
-
* @param delegationId - the delegation this relates to, or `undefined` for
|
|
179
|
+
* @param delegationId - the delegation this relates to, or `undefined` for best-effort session context.
|
|
171
180
|
*/
|
|
172
181
|
appendThinking(content: string, delegationId?: string): Promise<void>
|
|
173
182
|
|
|
174
183
|
/**
|
|
175
184
|
* Steer the live conversation's behaviour (tone, brevity, policy) without speaking anything.
|
|
185
|
+
*
|
|
186
|
+
* With no `delegationId` this resolves on the write and is best-effort, for the reason recorded on
|
|
187
|
+
* {@link appendCommentary}.
|
|
176
188
|
* @param content - plain text, non-empty and within {@link MAX_APPEND_CHARS}.
|
|
177
|
-
* @param delegationId - `undefined` for session-wide steering.
|
|
189
|
+
* @param delegationId - `undefined` for best-effort session-wide steering.
|
|
178
190
|
*/
|
|
179
191
|
appendInstructions(content: string, delegationId?: string): Promise<void>
|
|
180
192
|
|