@ziggs-ai/api-client 0.33.1 → 0.34.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/capabilities/chat.d.ts +1 -0
- package/dist/capabilities/chat.js +5 -1
- package/dist/http/ChatClient.d.ts +7 -2
- package/dist/http/ContextReadClient.d.ts +2 -0
- package/dist/http/ContextReadClient.js +6 -0
- package/dist/shared/apiError.d.ts +3 -1
- package/dist/shared/apiError.js +22 -2
- package/dist/shared/rateLimit.js +2 -2
- package/package.json +1 -1
|
@@ -38,10 +38,14 @@ export function presentSendResult(result, env) {
|
|
|
38
38
|
// What counted it, in the server's words: a stranger's free messages with a
|
|
39
39
|
// listed agent run out, and the person writing needs to know before they do.
|
|
40
40
|
const counted = result.coverage?.message ? ` ${result.coverage.message}` : '';
|
|
41
|
+
const routing = delivery.reachable
|
|
42
|
+
? `Assigned to ${delivery.assignee ?? 'nobody'}; reachable: ${delivery.reachable}${delivery.reason ? ` (${delivery.reason})` : ''}.${delivery.lastRead ? ` Last inbox read: ${delivery.lastRead}.` : ''}`
|
|
43
|
+
: 'Mailbox routing is unconfirmed; a person may be covered by an assistant.';
|
|
41
44
|
return {
|
|
42
45
|
...base,
|
|
43
|
-
delivered: `Message ${result.messageId} was accepted into chat ${result.chatId}, addressed to ${destination}.
|
|
46
|
+
delivered: `Message ${result.messageId} was accepted into chat ${result.chatId}, addressed to ${destination}. ${routing} Accepted is not read and does not confirm a wake or reply.${counted}`,
|
|
44
47
|
to: delivery.to,
|
|
48
|
+
delivery,
|
|
45
49
|
};
|
|
46
50
|
}
|
|
47
51
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ChatReadDto } from '@ziggs-ai/contracts';
|
|
2
|
-
import type { MessageDelivery, SendChatMessageResult as WireSendChatMessageResult } from '@ziggs-ai/contracts';
|
|
2
|
+
import type { MessageDelivery as WireMessageDelivery, SendChatMessageResult as WireSendChatMessageResult } from '@ziggs-ai/contracts';
|
|
3
3
|
import { type Creds } from '../types.js';
|
|
4
4
|
/**
|
|
5
5
|
* One room from `GET /chats/mine`, as the server sends it.
|
|
@@ -85,7 +85,12 @@ export interface SendChatMessageInput {
|
|
|
85
85
|
* `wakesAgent` field that once rode along was removed for exactly that reason
|
|
86
86
|
* and is not coming back.
|
|
87
87
|
*/
|
|
88
|
-
export type
|
|
88
|
+
export type MessageDelivery = WireMessageDelivery & {
|
|
89
|
+
assignee?: string | null;
|
|
90
|
+
reachable?: 'yes' | 'no';
|
|
91
|
+
reason?: string;
|
|
92
|
+
lastRead?: string | null;
|
|
93
|
+
};
|
|
89
94
|
/**
|
|
90
95
|
* The send result as a caller here holds it.
|
|
91
96
|
*
|
|
@@ -105,6 +105,8 @@ export declare class ContextReadClient {
|
|
|
105
105
|
* intended. Send it on every read made while acting on a wake.
|
|
106
106
|
*/
|
|
107
107
|
constructor(operatorKey: string, agentId?: string, baseUrl?: string, laneId?: string);
|
|
108
|
+
/** The same reader acting on one wake's lane. */
|
|
109
|
+
withLane(laneId: string | undefined): ContextReadClient;
|
|
108
110
|
read<T = unknown>(type: ContextReadType, query: ContextReadQuery): Promise<ContextReadEnvelope<T>>;
|
|
109
111
|
/**
|
|
110
112
|
* Aggregated chat snapshot — `GET /context/snapshot?via=chat:<id>`. The
|
|
@@ -74,6 +74,12 @@ export class ContextReadClient {
|
|
|
74
74
|
this.baseUrl = baseUrl || getBackendUrl();
|
|
75
75
|
this.laneId = laneId;
|
|
76
76
|
}
|
|
77
|
+
/** The same reader acting on one wake's lane. */
|
|
78
|
+
withLane(laneId) {
|
|
79
|
+
if (!laneId || laneId === this.laneId)
|
|
80
|
+
return this;
|
|
81
|
+
return new ContextReadClient(this.operatorKey, this.agentId, this.baseUrl, laneId);
|
|
82
|
+
}
|
|
77
83
|
async read(type, query) {
|
|
78
84
|
if (!query.via?.trim()) {
|
|
79
85
|
throw new Error('ContextReadClient.read: via is required');
|
|
@@ -31,9 +31,11 @@ export declare function parseErrorCode(responseBody: string): string | null;
|
|
|
31
31
|
export declare function parseRetryAfterMs(headers: {
|
|
32
32
|
get(name: string): string | null;
|
|
33
33
|
}): number | null;
|
|
34
|
+
export declare function isCoverageRefusal(code: string | null | undefined): boolean;
|
|
34
35
|
/**
|
|
35
36
|
* Throw the parsed failure as an {@link ApiError} (or {@link RateLimitedError}
|
|
36
|
-
* for 429), preserving status, body, machine
|
|
37
|
+
* for a 429 that is not a coverage refusal), preserving status, body, machine
|
|
38
|
+
* code, and Retry-After.
|
|
37
39
|
*/
|
|
38
40
|
export declare function throwApiError(response: {
|
|
39
41
|
status: number;
|
package/dist/shared/apiError.js
CHANGED
|
@@ -100,14 +100,34 @@ function clampRetryMs(ms) {
|
|
|
100
100
|
return 1_000;
|
|
101
101
|
return Math.min(MAX_RETRY_AFTER_MS, Math.max(1_000, Math.round(ms)));
|
|
102
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* Coverage refusals name themselves. A 429 with one of these codes is not a
|
|
105
|
+
* wait-and-retry: the writer has to hire, or someone has to set the agreement
|
|
106
|
+
* up. The backend answers them as 403; this keeps a stale 429 from becoming a
|
|
107
|
+
* rate limit too.
|
|
108
|
+
*/
|
|
109
|
+
const COVERAGE_REFUSALS = new Set([
|
|
110
|
+
'CONVERSATION_ALLOWANCE_EXHAUSTED',
|
|
111
|
+
'CONTACT_POLICY_REFUSED',
|
|
112
|
+
'LISTING_PAUSED',
|
|
113
|
+
'CONVERSATION_COVERAGE_REQUIRED',
|
|
114
|
+
'CONVERSATION_COVERAGE_ENDED',
|
|
115
|
+
'CONVERSATION_NOT_FUNDED',
|
|
116
|
+
'CONVERSATION_SPONSORSHIP_REQUIRED',
|
|
117
|
+
'TASK_ONLY_AGENT',
|
|
118
|
+
]);
|
|
119
|
+
export function isCoverageRefusal(code) {
|
|
120
|
+
return !!code && COVERAGE_REFUSALS.has(code);
|
|
121
|
+
}
|
|
103
122
|
/**
|
|
104
123
|
* Throw the parsed failure as an {@link ApiError} (or {@link RateLimitedError}
|
|
105
|
-
* for 429), preserving status, body, machine
|
|
124
|
+
* for a 429 that is not a coverage refusal), preserving status, body, machine
|
|
125
|
+
* code, and Retry-After.
|
|
106
126
|
*/
|
|
107
127
|
export function throwApiError(response, responseBody, defaultMessage) {
|
|
108
128
|
const message = parseErrorMessage(responseBody, defaultMessage);
|
|
109
129
|
const code = parseErrorCode(responseBody);
|
|
110
|
-
if (response.status === 429) {
|
|
130
|
+
if (response.status === 429 && !isCoverageRefusal(code)) {
|
|
111
131
|
throw new RateLimitedError(message, response.headers ? parseRetryAfterMs(response.headers) : null, responseBody, code);
|
|
112
132
|
}
|
|
113
133
|
throw new ApiError(message, response.status, responseBody, code);
|
package/dist/shared/rateLimit.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ApiError, RateLimitedError } from '../types.js';
|
|
2
|
-
import { parseErrorCode, parseErrorMessage, parseRetryAfterMs, } from './apiError.js';
|
|
2
|
+
import { isCoverageRefusal, parseErrorCode, parseErrorMessage, parseRetryAfterMs, } from './apiError.js';
|
|
3
3
|
/**
|
|
4
4
|
* a 429 is not a generic failure, it is an instruction with a
|
|
5
5
|
* deadline attached.
|
|
@@ -33,7 +33,7 @@ export function isRateLimited(err) {
|
|
|
33
33
|
export function pollSurfaceError(label, res, body) {
|
|
34
34
|
const message = parseErrorMessage(body, `${label} failed: ${res.status}`);
|
|
35
35
|
const code = parseErrorCode(body);
|
|
36
|
-
if (res.status === 429) {
|
|
36
|
+
if (res.status === 429 && !isCoverageRefusal(code)) {
|
|
37
37
|
return new RateLimitedError(message, parseRetryAfterMs(res.headers), body, code);
|
|
38
38
|
}
|
|
39
39
|
return new ApiError(message, res.status, body, code);
|