@ziggs-ai/api-client 0.8.0 → 0.9.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/README.md +10 -0
- package/dist/ConnectionManager.d.ts +21 -57
- package/dist/ConnectionManager.js +34 -163
- package/dist/capabilities/agreements.js +2 -2
- package/dist/capabilities/artifacts.js +11 -10
- package/dist/capabilities/connections.js +1 -1
- package/dist/capabilities/context.js +29 -11
- package/dist/capabilities/grants.d.ts +7 -0
- package/dist/capabilities/grants.js +9 -2
- package/dist/capabilities/index.d.ts +1 -1
- package/dist/capabilities/index.js +1 -1
- package/dist/capabilities/links.d.ts +0 -8
- package/dist/capabilities/links.js +3 -25
- package/dist/capabilities/types.d.ts +1 -0
- package/dist/capabilities/types.js +7 -2
- package/dist/http/AgreementClient.d.ts +67 -16
- package/dist/http/AgreementClient.js +161 -29
- package/dist/http/ArtifactsClient.d.ts +5 -1
- package/dist/http/ArtifactsClient.js +17 -5
- package/dist/http/ChatClient.js +5 -2
- package/dist/http/ConnectionsClient.js +4 -4
- package/dist/http/ContextDiscoveryClient.js +2 -1
- package/dist/http/ContextReadClient.d.ts +30 -3
- package/dist/http/ContextReadClient.js +63 -17
- package/dist/http/GrantsClient.js +2 -1
- package/dist/http/InboxClient.d.ts +32 -50
- package/dist/http/InboxClient.js +0 -39
- package/dist/http/MarketplaceClient.d.ts +0 -1
- package/dist/http/MarketplaceClient.js +8 -3
- package/dist/http/MessagesClient.js +3 -5
- package/dist/http/OrgsClient.js +3 -2
- package/dist/http/PaymentsClient.js +3 -5
- package/dist/http/TaskClient.d.ts +8 -0
- package/dist/http/TaskClient.js +3 -0
- package/dist/http/agreementFlows.d.ts +13 -5
- package/dist/http/agreementFlows.js +18 -24
- package/dist/http/index.d.ts +3 -3
- package/dist/http/index.js +1 -1
- package/dist/http/operatorHeaders.d.ts +7 -1
- package/dist/http/operatorHeaders.js +8 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.js +4 -3
- package/dist/shared/apiError.d.ts +22 -1
- package/dist/shared/apiError.js +60 -3
- package/dist/shared/rateLimit.d.ts +12 -22
- package/dist/shared/rateLimit.js +18 -51
- package/dist/types.d.ts +70 -1
- package/dist/types.js +39 -1
- package/package.json +1 -1
package/dist/http/ChatClient.js
CHANGED
|
@@ -7,6 +7,9 @@ function buildHeaders(creds) {
|
|
|
7
7
|
'content-type': 'application/json',
|
|
8
8
|
Authorization: `Bearer ${creds.operatorKey}`,
|
|
9
9
|
'X-Agent-Id': creds.agentId,
|
|
10
|
+
// ZIG-1092 — the wake's lane, so the backend can fence this call to the
|
|
11
|
+
// engagement it belongs to rather than the agent's whole authority.
|
|
12
|
+
...(creds.laneId ? { 'X-Ziggs-Lane': creds.laneId } : {}),
|
|
10
13
|
};
|
|
11
14
|
}
|
|
12
15
|
function assertCreds(creds, op) {
|
|
@@ -101,7 +104,7 @@ export async function listMyChats(creds) {
|
|
|
101
104
|
assertCreds(creds, 'list my chats');
|
|
102
105
|
// ZIG-699 — empty array only for a genuine empty 200; any failure (non-2xx /
|
|
103
106
|
// network) throws so ziggs_chat_list reports a real error instead of "you
|
|
104
|
-
// have no chats".
|
|
107
|
+
// have no chats". ZIG-1124 — HTTP failures are ApiError (status/body/code).
|
|
105
108
|
let res;
|
|
106
109
|
try {
|
|
107
110
|
res = await fetch(`${getBackendUrl()}/chats/mine`, {
|
|
@@ -116,7 +119,7 @@ export async function listMyChats(creds) {
|
|
|
116
119
|
if (!res.ok) {
|
|
117
120
|
const body = await res.text().catch(() => '');
|
|
118
121
|
runtimeLog.warn('ChatClient', `⚠️ listMyChats failed: ${res.status} ${res.statusText} ${body?.slice(0, 200)}`);
|
|
119
|
-
|
|
122
|
+
throwApiError(res, body, `GET /chats/mine failed: ${res.status}`);
|
|
120
123
|
}
|
|
121
124
|
const data = await res.json().catch(() => null);
|
|
122
125
|
return Array.isArray(data?.['chats']) ? data['chats'] : [];
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import 'dotenv/config';
|
|
2
2
|
import { getBackendUrl } from '../utils/urlUtils.js';
|
|
3
|
+
import { throwApiError } from '../shared/apiError.js';
|
|
3
4
|
import { buildOperatorHeaders } from './operatorHeaders.js';
|
|
4
5
|
import { GrantsClient } from './GrantsClient.js';
|
|
5
6
|
// ZIG-569 — defense-in-depth mirror of the backend leak-guard
|
|
@@ -169,10 +170,9 @@ export class ConnectionsClient {
|
|
|
169
170
|
const response = await fetch(`${this.baseUrl}${path}`, init);
|
|
170
171
|
const text = await response.text().catch(() => '');
|
|
171
172
|
if (!response.ok) {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
throw err;
|
|
173
|
+
// ZIG-1124 — ApiError (status/body/code); ConnectionsError remains the
|
|
174
|
+
// documented duck type for callers that branch on `.status`.
|
|
175
|
+
throwApiError(response, text, `${method} ${path} failed: ${response.status}`);
|
|
176
176
|
}
|
|
177
177
|
return text;
|
|
178
178
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import 'dotenv/config';
|
|
2
2
|
import { getBackendUrl } from '../utils/urlUtils.js';
|
|
3
|
+
import { throwApiError } from '../shared/apiError.js';
|
|
3
4
|
import { buildOperatorHeaders } from './operatorHeaders.js';
|
|
4
5
|
/**
|
|
5
6
|
* P4 discovery — labels-only pointers to context the agent could REQUEST but
|
|
@@ -34,7 +35,7 @@ export class ContextDiscoveryClient {
|
|
|
34
35
|
});
|
|
35
36
|
const body = await res.text().catch(() => '');
|
|
36
37
|
if (!res.ok) {
|
|
37
|
-
|
|
38
|
+
throwApiError(res, body, `ContextDiscoveryClient.discoverGrantable failed: ${res.status}`);
|
|
38
39
|
}
|
|
39
40
|
const parsed = JSON.parse(body);
|
|
40
41
|
return (parsed.items ?? []).map((i) => ({
|
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
import 'dotenv/config';
|
|
2
|
-
export
|
|
2
|
+
export declare const CONTEXT_READ_TYPES: readonly ["messages", "artifacts", "agreements", "tasks"];
|
|
3
|
+
export type ContextReadType = (typeof CONTEXT_READ_TYPES)[number];
|
|
4
|
+
/**
|
|
5
|
+
* Entry points a read can name as `via=<kind>:<id>` — the server's `parseVia`
|
|
6
|
+
* grammar. `counterparty` parses but reads nothing: it resolves a scope graph,
|
|
7
|
+
* not content, which is why {@link CONTEXT_READ_VIA} admits it for no type.
|
|
8
|
+
*/
|
|
9
|
+
export declare const VIA_KINDS: readonly ["chat", "agreement", "task", "counterparty", "artifact"];
|
|
10
|
+
export type ViaKind = (typeof VIA_KINDS)[number];
|
|
11
|
+
/**
|
|
12
|
+
* Which entry points each read type actually accepts, mirroring the server's
|
|
13
|
+
* `CONTEXT_READ_VIA`. Stated once here so the tool descriptions, the argument
|
|
14
|
+
* check, and this type all read off the same list instead of three prose
|
|
15
|
+
* copies that drift — and so a wrong pairing is refused with the reason rather
|
|
16
|
+
* than as a bare 400 from a round-trip away.
|
|
17
|
+
*/
|
|
18
|
+
export declare const CONTEXT_READ_VIA: Record<ContextReadType, readonly ViaKind[]>;
|
|
19
|
+
/** `chat:<id>, agreement:<id>` — the accepted entries for one read type, for humans. */
|
|
20
|
+
export declare function viaHint(type: ContextReadType): string;
|
|
21
|
+
/** Split `chat:abc` into its parts, or null when it is not a `via` at all. */
|
|
22
|
+
export declare function parseVia(via: string): {
|
|
23
|
+
kind: ViaKind;
|
|
24
|
+
id: string;
|
|
25
|
+
} | null;
|
|
3
26
|
export interface ContextReadQuery {
|
|
4
27
|
via: string;
|
|
5
28
|
cursor?: string;
|
|
@@ -12,7 +35,7 @@ export interface ContextReadQuery {
|
|
|
12
35
|
export interface ContextReadEnvelope<T = unknown> {
|
|
13
36
|
type: ContextReadType;
|
|
14
37
|
via: {
|
|
15
|
-
kind:
|
|
38
|
+
kind: ViaKind;
|
|
16
39
|
id: string;
|
|
17
40
|
};
|
|
18
41
|
items: T[];
|
|
@@ -44,11 +67,15 @@ export declare class ContextReadClient {
|
|
|
44
67
|
private readonly operatorKey;
|
|
45
68
|
private readonly agentId?;
|
|
46
69
|
private readonly baseUrl;
|
|
70
|
+
private readonly laneId?;
|
|
47
71
|
/**
|
|
48
72
|
* @param operatorKey Agent-scoped or fleet operator key.
|
|
49
73
|
* @param agentId Required for fleet keys (sent as X-Agent-Id). Omit for agent-scoped keys.
|
|
74
|
+
* @param laneId ZIG-1092 — the wake's lane, sent as X-Ziggs-Lane. This is the
|
|
75
|
+
* path the dogfood leak ran through: `via=artifact:<other customer's spec>`
|
|
76
|
+
* was authorised purely because the same agent had authored it.
|
|
50
77
|
*/
|
|
51
|
-
constructor(operatorKey: string, agentId?: string, baseUrl?: string);
|
|
78
|
+
constructor(operatorKey: string, agentId?: string, baseUrl?: string, laneId?: string);
|
|
52
79
|
read<T = unknown>(type: ContextReadType, query: ContextReadQuery): Promise<ContextReadEnvelope<T>>;
|
|
53
80
|
/**
|
|
54
81
|
* Aggregated chat snapshot — `GET /context/snapshot?via=chat:<id>`. The
|
|
@@ -1,6 +1,54 @@
|
|
|
1
1
|
import 'dotenv/config';
|
|
2
2
|
import { getBackendUrl } from '../utils/urlUtils.js';
|
|
3
3
|
import { pollSurfaceError } from '../shared/rateLimit.js';
|
|
4
|
+
export const CONTEXT_READ_TYPES = [
|
|
5
|
+
'messages',
|
|
6
|
+
'artifacts',
|
|
7
|
+
'agreements',
|
|
8
|
+
'tasks',
|
|
9
|
+
];
|
|
10
|
+
/**
|
|
11
|
+
* Entry points a read can name as `via=<kind>:<id>` — the server's `parseVia`
|
|
12
|
+
* grammar. `counterparty` parses but reads nothing: it resolves a scope graph,
|
|
13
|
+
* not content, which is why {@link CONTEXT_READ_VIA} admits it for no type.
|
|
14
|
+
*/
|
|
15
|
+
export const VIA_KINDS = [
|
|
16
|
+
'chat',
|
|
17
|
+
'agreement',
|
|
18
|
+
'task',
|
|
19
|
+
'counterparty',
|
|
20
|
+
'artifact',
|
|
21
|
+
];
|
|
22
|
+
/**
|
|
23
|
+
* Which entry points each read type actually accepts, mirroring the server's
|
|
24
|
+
* `CONTEXT_READ_VIA`. Stated once here so the tool descriptions, the argument
|
|
25
|
+
* check, and this type all read off the same list instead of three prose
|
|
26
|
+
* copies that drift — and so a wrong pairing is refused with the reason rather
|
|
27
|
+
* than as a bare 400 from a round-trip away.
|
|
28
|
+
*/
|
|
29
|
+
export const CONTEXT_READ_VIA = {
|
|
30
|
+
messages: ['chat'],
|
|
31
|
+
artifacts: ['chat', 'agreement', 'task', 'artifact'],
|
|
32
|
+
agreements: ['chat', 'agreement'],
|
|
33
|
+
tasks: ['agreement', 'task'],
|
|
34
|
+
};
|
|
35
|
+
/** `chat:<id>, agreement:<id>` — the accepted entries for one read type, for humans. */
|
|
36
|
+
export function viaHint(type) {
|
|
37
|
+
return CONTEXT_READ_VIA[type].map((k) => `${k}:<id>`).join(', ');
|
|
38
|
+
}
|
|
39
|
+
/** Split `chat:abc` into its parts, or null when it is not a `via` at all. */
|
|
40
|
+
export function parseVia(via) {
|
|
41
|
+
const at = via.indexOf(':');
|
|
42
|
+
if (at <= 0)
|
|
43
|
+
return null;
|
|
44
|
+
const kind = via.slice(0, at);
|
|
45
|
+
const id = via.slice(at + 1);
|
|
46
|
+
if (!id)
|
|
47
|
+
return null;
|
|
48
|
+
return VIA_KINDS.includes(kind)
|
|
49
|
+
? { kind: kind, id }
|
|
50
|
+
: null;
|
|
51
|
+
}
|
|
4
52
|
/**
|
|
5
53
|
* Protocol-first uniform context reads (ZIG-427).
|
|
6
54
|
* Wraps `GET /context/read/:type` — one client, one envelope, four types.
|
|
@@ -9,16 +57,21 @@ export class ContextReadClient {
|
|
|
9
57
|
operatorKey;
|
|
10
58
|
agentId;
|
|
11
59
|
baseUrl;
|
|
60
|
+
laneId;
|
|
12
61
|
/**
|
|
13
62
|
* @param operatorKey Agent-scoped or fleet operator key.
|
|
14
63
|
* @param agentId Required for fleet keys (sent as X-Agent-Id). Omit for agent-scoped keys.
|
|
64
|
+
* @param laneId ZIG-1092 — the wake's lane, sent as X-Ziggs-Lane. This is the
|
|
65
|
+
* path the dogfood leak ran through: `via=artifact:<other customer's spec>`
|
|
66
|
+
* was authorised purely because the same agent had authored it.
|
|
15
67
|
*/
|
|
16
|
-
constructor(operatorKey, agentId, baseUrl) {
|
|
68
|
+
constructor(operatorKey, agentId, baseUrl, laneId) {
|
|
17
69
|
if (!operatorKey)
|
|
18
70
|
throw new Error('ContextReadClient: operatorKey is required');
|
|
19
71
|
this.operatorKey = operatorKey;
|
|
20
72
|
this.agentId = agentId;
|
|
21
73
|
this.baseUrl = baseUrl || getBackendUrl();
|
|
74
|
+
this.laneId = laneId;
|
|
22
75
|
}
|
|
23
76
|
async read(type, query) {
|
|
24
77
|
if (!query.via?.trim()) {
|
|
@@ -44,24 +97,19 @@ export class ContextReadClient {
|
|
|
44
97
|
};
|
|
45
98
|
if (this.agentId)
|
|
46
99
|
headers['X-Agent-Id'] = this.agentId;
|
|
100
|
+
if (this.laneId)
|
|
101
|
+
headers['X-Ziggs-Lane'] = this.laneId;
|
|
47
102
|
if (query.contextGrantId) {
|
|
48
103
|
headers['X-Context-Grant-Id'] = query.contextGrantId;
|
|
49
104
|
}
|
|
50
105
|
const res = await fetch(url.toString(), { headers });
|
|
51
106
|
const body = await res.text().catch(() => '');
|
|
52
107
|
if (!res.ok) {
|
|
53
|
-
// Carry the status like `snapshot()` does, so callers can branch on it.
|
|
54
108
|
// A 403 here is a legitimate outcome, not a transport failure: addressing
|
|
55
109
|
// and authorisation are separate, so an agent can be told about mail it
|
|
56
|
-
// is not (or is no longer) allowed to open.
|
|
57
|
-
//
|
|
58
|
-
|
|
59
|
-
if (res.status === 429) {
|
|
60
|
-
throw pollSurfaceError(`ContextReadClient.read ${type}`, res, body);
|
|
61
|
-
}
|
|
62
|
-
const err = new Error(`ContextReadClient.read ${type} ${res.status} ${body.slice(0, 200)}`);
|
|
63
|
-
err.status = res.status;
|
|
64
|
-
throw err;
|
|
110
|
+
// is not (or is no longer) allowed to open. ZIG-1124 — one ApiError shape
|
|
111
|
+
// (429 → RateLimitedError with Retry-After).
|
|
112
|
+
throw pollSurfaceError(`ContextReadClient.read ${type}`, res, body);
|
|
65
113
|
}
|
|
66
114
|
return JSON.parse(body);
|
|
67
115
|
}
|
|
@@ -88,18 +136,16 @@ export class ContextReadClient {
|
|
|
88
136
|
};
|
|
89
137
|
if (this.agentId)
|
|
90
138
|
headers['X-Agent-Id'] = this.agentId;
|
|
139
|
+
if (this.laneId)
|
|
140
|
+
headers['X-Ziggs-Lane'] = this.laneId;
|
|
91
141
|
if (opts.contextGrantId) {
|
|
92
142
|
headers['X-Context-Grant-Id'] = opts.contextGrantId;
|
|
93
143
|
}
|
|
94
144
|
const res = await fetch(url.toString(), { headers });
|
|
95
145
|
const body = await res.text().catch(() => '');
|
|
96
146
|
if (!res.ok) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
const err = new Error(`ContextReadClient.snapshot ${res.status} ${body.slice(0, 200)}`);
|
|
101
|
-
err.status = res.status;
|
|
102
|
-
throw err;
|
|
147
|
+
// ZIG-1124 — ApiError for every non-OK (429 → RateLimitedError).
|
|
148
|
+
throw pollSurfaceError('ContextReadClient.snapshot', res, body);
|
|
103
149
|
}
|
|
104
150
|
return JSON.parse(body);
|
|
105
151
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import 'dotenv/config';
|
|
2
2
|
import { getBackendUrl } from '../utils/urlUtils.js';
|
|
3
|
+
import { throwApiError } from '../shared/apiError.js';
|
|
3
4
|
import { buildOperatorHeaders } from './operatorHeaders.js';
|
|
4
5
|
/**
|
|
5
6
|
* ZIG-648 — unified grant listing across every rail. `GET /grants` returns the
|
|
@@ -41,7 +42,7 @@ export class GrantsClient {
|
|
|
41
42
|
});
|
|
42
43
|
const body = await res.text().catch(() => '');
|
|
43
44
|
if (!res.ok) {
|
|
44
|
-
|
|
45
|
+
throwApiError(res, body, `GrantsClient.listGrants failed: ${res.status}`);
|
|
45
46
|
}
|
|
46
47
|
const parsed = JSON.parse(body);
|
|
47
48
|
return {
|
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
import 'dotenv/config';
|
|
2
|
+
/**
|
|
3
|
+
* What a delivery can be about. A closed union, not a comment: a consumer that
|
|
4
|
+
* dispatches on `kind` (the MCP read plan) is only safe if the compiler can tell
|
|
5
|
+
* it a case is missing. The task-only deliverable that was acked unread got
|
|
6
|
+
* through precisely because this was `string`.
|
|
7
|
+
*
|
|
8
|
+
* A type and not a value list, unlike the backend's `RESOURCE_KINDS` — nothing
|
|
9
|
+
* on this side validates a delivery kind at runtime (the server does that on the
|
|
10
|
+
* way in), and exhaustiveness checking is purely type-level.
|
|
11
|
+
*/
|
|
12
|
+
export type InboxDeliveryKind = 'message' | 'artifact' | 'task-state' | 'agreement';
|
|
2
13
|
/**
|
|
3
14
|
* One thing addressed to this agent. A reference, never content — following it
|
|
4
15
|
* (a chat read, a task read) is where this agent's grants are enforced.
|
|
5
16
|
*/
|
|
6
17
|
export interface InboxDeliveryRef {
|
|
7
|
-
|
|
8
|
-
kind: string;
|
|
18
|
+
kind: InboxDeliveryKind;
|
|
9
19
|
resourceId: string;
|
|
10
20
|
chatId: string | null;
|
|
11
21
|
agreementId: string | null;
|
|
@@ -24,16 +34,35 @@ export interface InboxProposalRef {
|
|
|
24
34
|
agreementId: string;
|
|
25
35
|
title: string;
|
|
26
36
|
proposedAt: string | null;
|
|
37
|
+
/**
|
|
38
|
+
* ZIG-1087 — party ids still owing a decision, and the named responder slot.
|
|
39
|
+
* The inbox lists proposals awaiting the agent OR its human, and only the
|
|
40
|
+
* agent's own slot is one it can submit; these say which is which.
|
|
41
|
+
*
|
|
42
|
+
* Optional because a backend deployed before ZIG-1087 omits them, and this
|
|
43
|
+
* client is installed independently of the server it talks to. Absent reads
|
|
44
|
+
* as "no slot of mine", which routes the decision to the human — the safe
|
|
45
|
+
* direction: it withholds a call, it never invents authority.
|
|
46
|
+
*/
|
|
47
|
+
pendingApprovalPartyIds?: string[];
|
|
48
|
+
proposedTo?: string | null;
|
|
27
49
|
}
|
|
28
50
|
export interface InboxConnectionRequestRef {
|
|
29
51
|
requestId: string;
|
|
30
|
-
|
|
52
|
+
/**
|
|
53
|
+
* Non-addressable persona reference for the requester (`psn_*`).
|
|
54
|
+
* Never use as an account id for lookup / wake / pay (ZIG-1137).
|
|
55
|
+
*/
|
|
56
|
+
requesterRef: string;
|
|
31
57
|
/** ZIG-1039 — human-readable name for consent cards. */
|
|
32
58
|
requesterDisplayName?: string | null;
|
|
33
59
|
/** ZIG-1039 — org label for consent cards. */
|
|
34
60
|
requesterOrgName?: string | null;
|
|
35
61
|
message: string | null;
|
|
36
62
|
requestedAt: string | null;
|
|
63
|
+
/** ZIG-1087 — see InboxProposalRef; a link request uses the same gate. */
|
|
64
|
+
pendingApprovalPartyIds?: string[];
|
|
65
|
+
proposedTo?: string | null;
|
|
37
66
|
}
|
|
38
67
|
/** Pull-only MCP: prompt the human when proposals need a decision (ZIG-482 / ZIG-481). */
|
|
39
68
|
export interface InboxHumanAttention {
|
|
@@ -98,45 +127,6 @@ export interface InboxAckResult {
|
|
|
98
127
|
*/
|
|
99
128
|
export interface InboxReadOptions {
|
|
100
129
|
waitSeconds?: number;
|
|
101
|
-
/**
|
|
102
|
-
* Operator read only (ZIG-965): restrict the sweep to this roster. The
|
|
103
|
-
* server intersects it with the agents the key owner runs — it narrows,
|
|
104
|
-
* never widens. A launcher hosting a subset should always pass the agents
|
|
105
|
-
* it actually registered, or it pays for a sweep of the owner's whole
|
|
106
|
-
* seeded fleet.
|
|
107
|
-
*/
|
|
108
|
-
agents?: string[];
|
|
109
|
-
}
|
|
110
|
-
/**
|
|
111
|
-
* One agent's line in the operator sweep: enough to decide whether to start a
|
|
112
|
-
* host, and nothing more.
|
|
113
|
-
*
|
|
114
|
-
* Deliberately NOT that agent's envelope. The launcher only chooses who to
|
|
115
|
-
* run; the host it starts reads its own inbox on its own credentials a moment
|
|
116
|
-
* later, so building N envelopes here was work thrown away.
|
|
117
|
-
*/
|
|
118
|
-
export interface InboxOperatorAgentEntry {
|
|
119
|
-
agentId: string;
|
|
120
|
-
/** Newest delivery addressed to this agent, or null if it never had one. */
|
|
121
|
-
deliveredUpTo: string | null;
|
|
122
|
-
/** How far it has acked. Mail exists when `deliveredUpTo > ackedUpTo`. */
|
|
123
|
-
ackedUpTo: string | null;
|
|
124
|
-
/**
|
|
125
|
-
* True when this agent holds open assigned work. Independent of the
|
|
126
|
-
* watermark: a host that died mid-task already acked past the task's
|
|
127
|
-
* delivery, and the open task is the only durable trace it needs restarting.
|
|
128
|
-
*/
|
|
129
|
-
hasOpenTasks: boolean;
|
|
130
|
-
}
|
|
131
|
-
/** GET /inbox/operator: which of the key owner's agents have mail or open work. */
|
|
132
|
-
export interface InboxOperatorEnvelope {
|
|
133
|
-
asOf: string;
|
|
134
|
-
/** Agents with mail or open work. */
|
|
135
|
-
agents: InboxOperatorAgentEntry[];
|
|
136
|
-
/** Agents examined that had neither. */
|
|
137
|
-
idleAgents: number;
|
|
138
|
-
/** Owned agents beyond the server's per-pass cap — not examined. */
|
|
139
|
-
truncatedAgents: number;
|
|
140
130
|
}
|
|
141
131
|
/**
|
|
142
132
|
* The doorbell, not the door (ZIG-434): references addressed to this agent
|
|
@@ -155,14 +145,6 @@ export declare class InboxClient {
|
|
|
155
145
|
private headers;
|
|
156
146
|
private inboxUrl;
|
|
157
147
|
getInbox(opts?: InboxReadOptions): Promise<InboxEnvelope>;
|
|
158
|
-
/**
|
|
159
|
-
* Operator-level multiplexed read: which of this key owner's agents have
|
|
160
|
-
* mail or open work (an agent-scoped key collapses to its one agent).
|
|
161
|
-
*
|
|
162
|
-
* Returns who to start, never what they were sent — the host you start
|
|
163
|
-
* reads its own inbox on its own identity. Ack stays per agent.
|
|
164
|
-
*/
|
|
165
|
-
getOperatorInbox(opts?: InboxReadOptions): Promise<InboxOperatorEnvelope>;
|
|
166
148
|
/**
|
|
167
149
|
* Advance this agent's watermark — pass the envelope's `ackTo`. Monotonic
|
|
168
150
|
* server-side: an older value is a no-op, so a replayed ack can never
|
package/dist/http/InboxClient.js
CHANGED
|
@@ -35,9 +35,6 @@ export class InboxClient {
|
|
|
35
35
|
if (opts.waitSeconds != null && opts.waitSeconds > 0) {
|
|
36
36
|
url.searchParams.set('wait', String(opts.waitSeconds));
|
|
37
37
|
}
|
|
38
|
-
if (opts.agents?.length) {
|
|
39
|
-
url.searchParams.set('agents', opts.agents.join(','));
|
|
40
|
-
}
|
|
41
38
|
return url.toString();
|
|
42
39
|
}
|
|
43
40
|
async getInbox(opts = {}) {
|
|
@@ -50,42 +47,6 @@ export class InboxClient {
|
|
|
50
47
|
}
|
|
51
48
|
return JSON.parse(body);
|
|
52
49
|
}
|
|
53
|
-
/**
|
|
54
|
-
* Operator-level multiplexed read: which of this key owner's agents have
|
|
55
|
-
* mail or open work (an agent-scoped key collapses to its one agent).
|
|
56
|
-
*
|
|
57
|
-
* Returns who to start, never what they were sent — the host you start
|
|
58
|
-
* reads its own inbox on its own identity. Ack stays per agent.
|
|
59
|
-
*/
|
|
60
|
-
async getOperatorInbox(opts = {}) {
|
|
61
|
-
// Bounded wait (ZIG-965): the server holds at most `waitSeconds` (+ sweep
|
|
62
|
-
// time); a poll that outlives that by a wide margin is a dead sweep, and
|
|
63
|
-
// without a timeout it blocked the launcher's whole poll loop — lazy wake
|
|
64
|
-
// simply stopped. Abort and let the caller's retry loop take over.
|
|
65
|
-
const timeoutMs = ((opts.waitSeconds ?? 0) + 30) * 1000;
|
|
66
|
-
const ac = new AbortController();
|
|
67
|
-
const timer = setTimeout(() => ac.abort(), timeoutMs);
|
|
68
|
-
let res;
|
|
69
|
-
try {
|
|
70
|
-
res = await fetch(this.inboxUrl('/inbox/operator', opts), {
|
|
71
|
-
headers: this.headers(),
|
|
72
|
-
signal: ac.signal,
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
catch (err) {
|
|
76
|
-
throw ac.signal.aborted
|
|
77
|
-
? new Error(`InboxClient.getOperatorInbox timed out after ${timeoutMs}ms`)
|
|
78
|
-
: err;
|
|
79
|
-
}
|
|
80
|
-
finally {
|
|
81
|
-
clearTimeout(timer);
|
|
82
|
-
}
|
|
83
|
-
const body = await res.text().catch(() => '');
|
|
84
|
-
if (!res.ok) {
|
|
85
|
-
throw pollSurfaceError('InboxClient.getOperatorInbox', res, body);
|
|
86
|
-
}
|
|
87
|
-
return JSON.parse(body);
|
|
88
|
-
}
|
|
89
50
|
/**
|
|
90
51
|
* Advance this agent's watermark — pass the envelope's `ackTo`. Monotonic
|
|
91
52
|
* server-side: an older value is a no-op, so a replayed ack can never
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import 'dotenv/config';
|
|
2
|
+
// ZIG-1111: one shaping rule for every agreement this package parses.
|
|
3
|
+
import { shapeAgreement } from './AgreementClient.js';
|
|
2
4
|
import { getBackendUrl } from '../utils/urlUtils.js';
|
|
3
5
|
import { throwApiError } from '../shared/apiError.js';
|
|
4
6
|
function getMarketplaceBaseUrl() { return `${getBackendUrl()}/marketplace`; }
|
|
@@ -7,6 +9,9 @@ function buildHeaders(creds) {
|
|
|
7
9
|
'content-type': 'application/json',
|
|
8
10
|
Authorization: `Bearer ${creds.operatorKey}`,
|
|
9
11
|
'X-Agent-Id': creds.agentId,
|
|
12
|
+
// ZIG-1092 — the wake's lane, so the backend can fence this call to the
|
|
13
|
+
// engagement it belongs to rather than the agent's whole authority.
|
|
14
|
+
...(creds.laneId ? { 'X-Ziggs-Lane': creds.laneId } : {}),
|
|
10
15
|
};
|
|
11
16
|
}
|
|
12
17
|
function assertCreds(creds, op) {
|
|
@@ -27,7 +32,7 @@ export async function publishOffer(payload, creds) {
|
|
|
27
32
|
throwApiError(res, body, `Marketplace offer publish failed: ${res.status}`);
|
|
28
33
|
}
|
|
29
34
|
const data = await res.json().catch(() => null);
|
|
30
|
-
return (data?.['offer'] ?? data);
|
|
35
|
+
return shapeAgreement((data?.['offer'] ?? data));
|
|
31
36
|
}
|
|
32
37
|
export async function pullOffers(options, creds) {
|
|
33
38
|
assertCreds(creds, 'marketplace offers pull');
|
|
@@ -64,7 +69,7 @@ export async function claimOffer(agreementId, creds) {
|
|
|
64
69
|
const data = await res.json().catch(() => null);
|
|
65
70
|
if (!data?.['ok'])
|
|
66
71
|
throw new Error(data?.['error'] || 'Claim failed');
|
|
67
|
-
return data['offer'];
|
|
72
|
+
return shapeAgreement(data['offer']);
|
|
68
73
|
}
|
|
69
74
|
export async function publishQuest(payload, creds) {
|
|
70
75
|
assertCreds(creds, 'quest publish');
|
|
@@ -80,7 +85,7 @@ export async function publishQuest(payload, creds) {
|
|
|
80
85
|
const data = await res.json().catch(() => null);
|
|
81
86
|
if (!data?.['agreement'])
|
|
82
87
|
throw new Error('Quest publish returned no agreement');
|
|
83
|
-
return data['agreement'];
|
|
88
|
+
return shapeAgreement(data['agreement']);
|
|
84
89
|
}
|
|
85
90
|
export async function pullQuests(options, creds) {
|
|
86
91
|
assertCreds(creds, 'quest pull');
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import 'dotenv/config';
|
|
2
2
|
import { getBackendUrl } from '../utils/urlUtils.js';
|
|
3
|
+
import { throwApiError } from '../shared/apiError.js';
|
|
3
4
|
/**
|
|
4
5
|
* Read-side client for forward-delta message reads.
|
|
5
6
|
*
|
|
@@ -35,11 +36,8 @@ export class MessagesClient {
|
|
|
35
36
|
const res = await fetch(url.toString(), { headers: this._headers() });
|
|
36
37
|
if (!res.ok) {
|
|
37
38
|
const body = await res.text().catch(() => '');
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
// without parsing the message string.
|
|
41
|
-
err.status = res.status;
|
|
42
|
-
throw err;
|
|
39
|
+
// Callers branch on ApiError.status (404 = chat deleted/not visible).
|
|
40
|
+
throwApiError(res, body, `MessagesClient.list failed: ${res.status}`);
|
|
43
41
|
}
|
|
44
42
|
return (await res.json());
|
|
45
43
|
}
|
package/dist/http/OrgsClient.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import 'dotenv/config';
|
|
2
2
|
import { getBackendUrl } from '../utils/urlUtils.js';
|
|
3
|
+
import { throwApiError } from '../shared/apiError.js';
|
|
3
4
|
import { buildOperatorHeaders } from './operatorHeaders.js';
|
|
4
5
|
/**
|
|
5
6
|
* ZIG-739 / ZIG-956 — the operator's full org membership (not just granted
|
|
@@ -15,7 +16,7 @@ export async function fetchMyOrgs(creds, baseUrl) {
|
|
|
15
16
|
});
|
|
16
17
|
const body = await res.text().catch(() => '');
|
|
17
18
|
if (!res.ok) {
|
|
18
|
-
|
|
19
|
+
throwApiError(res, body, `GET /orgs/me failed: ${res.status}`);
|
|
19
20
|
}
|
|
20
21
|
const parsed = body ? JSON.parse(body) : {};
|
|
21
22
|
return (parsed.orgs ?? []).map((o) => ({
|
|
@@ -55,7 +56,7 @@ export async function fetchDelegateAccess(creds, baseUrl) {
|
|
|
55
56
|
});
|
|
56
57
|
const body = await res.text().catch(() => '');
|
|
57
58
|
if (!res.ok) {
|
|
58
|
-
|
|
59
|
+
throwApiError(res, body, `GET /agents/claude-delegate/access failed: ${res.status}`);
|
|
59
60
|
}
|
|
60
61
|
return body ? JSON.parse(body) : {};
|
|
61
62
|
}
|
|
@@ -2,7 +2,7 @@ import 'dotenv/config';
|
|
|
2
2
|
import { getBackendUrl } from '../utils/urlUtils.js';
|
|
3
3
|
import { buildOperatorHeaders } from './operatorHeaders.js';
|
|
4
4
|
import { GrantsClient } from './GrantsClient.js';
|
|
5
|
-
import {
|
|
5
|
+
import { throwApiError } from '../shared/apiError.js';
|
|
6
6
|
function randomIdempotencyKey(prefix = 'op') {
|
|
7
7
|
return `${prefix}_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
8
8
|
}
|
|
@@ -217,10 +217,8 @@ export class PaymentsClient {
|
|
|
217
217
|
const response = await fetch(`${this.baseUrl}${path}`, init);
|
|
218
218
|
const text = await response.text();
|
|
219
219
|
if (!response.ok) {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
err.body = text;
|
|
223
|
-
throw err;
|
|
220
|
+
// ZIG-1124 — ApiError; PaymentsError remains the duck type for `.status`.
|
|
221
|
+
throwApiError(response, text, `HTTP ${response.status}`);
|
|
224
222
|
}
|
|
225
223
|
return text ? JSON.parse(text) : null;
|
|
226
224
|
}
|
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import 'dotenv/config';
|
|
2
2
|
import { type Creds, type Task, type TaskState } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* When the buyer reviews a task's plan. Task-rail only — an agreement has no
|
|
5
|
+
* plan to review, which is why ZIG-1095 cut this from the propose/counter/
|
|
6
|
+
* subcontract inputs rather than teaching those routes to keep one.
|
|
7
|
+
*/
|
|
8
|
+
export type PlanReviewTiming = 'with_proposal' | 'before_execution';
|
|
3
9
|
export interface CreateTaskData {
|
|
4
10
|
description: string;
|
|
5
11
|
agreementId: string;
|
|
6
12
|
parentTaskId?: string;
|
|
7
13
|
plan?: unknown;
|
|
14
|
+
planReviewTiming?: PlanReviewTiming;
|
|
15
|
+
requireMidWorkPlanAck?: boolean;
|
|
8
16
|
idempotencyKey?: string;
|
|
9
17
|
/** Explicit delegation target (ZIG-586) — must be a party to the agreement; validated server-side. */
|
|
10
18
|
assigneeId?: string;
|
package/dist/http/TaskClient.js
CHANGED
|
@@ -7,6 +7,9 @@ function buildHeaders(creds) {
|
|
|
7
7
|
'content-type': 'application/json',
|
|
8
8
|
Authorization: `Bearer ${creds.operatorKey}`,
|
|
9
9
|
'X-Agent-Id': creds.agentId,
|
|
10
|
+
// ZIG-1092 — the wake's lane, so the backend can fence this call to the
|
|
11
|
+
// engagement it belongs to rather than the agent's whole authority.
|
|
12
|
+
...(creds.laneId ? { 'X-Ziggs-Lane': creds.laneId } : {}),
|
|
10
13
|
};
|
|
11
14
|
}
|
|
12
15
|
function assertCreds(creds, op) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ProposeTerms } from './AgreementClient.js';
|
|
1
|
+
import { type ClaimedKind, type ProposeTerms } from './AgreementClient.js';
|
|
2
2
|
import { type Agreement, type Creds, type EngagementKind } from '../types.js';
|
|
3
3
|
/**
|
|
4
4
|
* ZIG-1022 — one propose grammar. Direct, broadcast (quest and standing
|
|
@@ -22,11 +22,19 @@ export declare function proposeUnified(input: UnifiedProposeInput, creds: Creds)
|
|
|
22
22
|
agreement: Agreement;
|
|
23
23
|
shape: ProposeShape;
|
|
24
24
|
}>;
|
|
25
|
-
export type ClaimedKind = 'link' | 'offer' | 'quest' | 'hand-off';
|
|
26
25
|
/**
|
|
27
|
-
* ZIG-1021 — one claim verb for any open broadcast
|
|
28
|
-
*
|
|
29
|
-
*
|
|
26
|
+
* ZIG-1021 — one claim verb for any open broadcast: link invite, quest,
|
|
27
|
+
* hand-off, or standing offer.
|
|
28
|
+
*
|
|
29
|
+
* One request. This used to read the agreement first to decide which endpoint to
|
|
30
|
+
* post to, and `GET /agreements/:id` is party-scoped — a claimer is by definition
|
|
31
|
+
* not yet a party to the broadcast it is claiming, so the routing read 404'd and
|
|
32
|
+
* every standing offer in the store failed with "Agreement not found" before
|
|
33
|
+
* either claim endpoint was called (ZIG-1155). The backend routes it now, where
|
|
34
|
+
* the row is readable without being a party to it.
|
|
35
|
+
*
|
|
36
|
+
* `kind` arrives on the claim response — the route that did the routing reports
|
|
37
|
+
* which broadcast kind this turned out to be.
|
|
30
38
|
*/
|
|
31
39
|
export declare function claimOpenAgreement(agreementId: string, creds: Creds): Promise<{
|
|
32
40
|
agreement: Agreement;
|