@ziggs-ai/api-client 0.3.1 → 0.5.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/dist/ConnectionManager.d.ts +13 -1
- package/dist/ConnectionManager.js +52 -9
- package/dist/capabilities/artifacts.d.ts +3 -0
- package/dist/capabilities/artifacts.js +94 -0
- package/dist/capabilities/chat.d.ts +11 -0
- package/dist/capabilities/chat.js +38 -0
- package/dist/capabilities/connections.d.ts +4 -0
- package/dist/capabilities/connections.js +112 -0
- package/dist/capabilities/context.d.ts +23 -0
- package/dist/capabilities/context.js +220 -0
- package/dist/capabilities/discovery.d.ts +4 -0
- package/dist/capabilities/discovery.js +77 -0
- package/dist/capabilities/grants.d.ts +9 -0
- package/dist/capabilities/grants.js +77 -0
- package/dist/capabilities/index.d.ts +9 -0
- package/dist/capabilities/index.js +9 -0
- package/dist/capabilities/links.d.ts +17 -0
- package/dist/capabilities/links.js +219 -0
- package/dist/capabilities/payments.d.ts +11 -0
- package/dist/capabilities/payments.js +404 -0
- package/dist/capabilities/types.d.ts +88 -0
- package/dist/capabilities/types.js +24 -0
- package/dist/http/AgreementClient.d.ts +6 -0
- package/dist/http/ConnectionsClient.d.ts +27 -1
- package/dist/http/ConnectionsClient.js +29 -0
- package/dist/http/ContextReadClient.js +7 -1
- package/dist/http/GrantsClient.d.ts +16 -0
- package/dist/http/GrantsClient.js +3 -0
- package/dist/http/InboxClient.d.ts +95 -65
- package/dist/http/InboxClient.js +42 -14
- package/dist/http/MarketplaceClient.d.ts +8 -0
- package/dist/http/OrgsClient.d.ts +36 -0
- package/dist/http/OrgsClient.js +61 -0
- package/dist/http/PaymentsClient.d.ts +75 -10
- package/dist/http/PaymentsClient.js +26 -14
- package/dist/http/index.d.ts +6 -6
- package/dist/http/index.js +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/package.json +1 -1
- package/dist/http/grantRails.d.ts +0 -20
- package/dist/http/grantRails.js +0 -50
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ZIG-956 — one shared definition per SDK/MCP tool capability.
|
|
3
|
+
*
|
|
4
|
+
* The tool-surface-parity epic (ZIG-893→903) unified the HTTP layer here in
|
|
5
|
+
* api-client but left ~25 shared capabilities hand-written twice: once in the
|
|
6
|
+
* agent-sdk's defineTool DSL, once in ziggs-mcp zod. Each capability below is
|
|
7
|
+
* the single source for the tool's schema, validation, response shaping, and
|
|
8
|
+
* guidance; the two surfaces register it through thin adapters
|
|
9
|
+
* (agent-sdk `toolFromCapability`, ziggs-mcp `registerCapability`).
|
|
10
|
+
*
|
|
11
|
+
* Params are a neutral JSON-schema-flavoured DSL rather than zod because the
|
|
12
|
+
* two surfaces cannot share one zod: agent-sdk is on zod v4, ziggs-mcp on
|
|
13
|
+
* zod v3 (pinned by the MCP SDK's ZodRawShape), and api-client deliberately
|
|
14
|
+
* has no zod dependency. The SDK adapter feeds params straight into
|
|
15
|
+
* defineTool's converter; the MCP adapter lowers them to zod v3.
|
|
16
|
+
*/
|
|
17
|
+
import type { Creds } from '../types.js';
|
|
18
|
+
export type CapabilitySurface = 'sdk' | 'mcp';
|
|
19
|
+
/** read-only / write / destructive — lowered to MCP tool annotations. */
|
|
20
|
+
export type CapabilityAnnotation = 'read-only' | 'write' | 'destructive';
|
|
21
|
+
export interface CapabilityParam {
|
|
22
|
+
type: 'string' | 'number' | 'boolean' | 'object' | 'array';
|
|
23
|
+
required?: boolean;
|
|
24
|
+
description?: string;
|
|
25
|
+
/** Only for type 'string'. */
|
|
26
|
+
enum?: readonly string[];
|
|
27
|
+
/** Only for type 'array'. */
|
|
28
|
+
items?: {
|
|
29
|
+
type: 'string';
|
|
30
|
+
enum?: readonly string[];
|
|
31
|
+
} | {
|
|
32
|
+
type: 'object';
|
|
33
|
+
properties?: Record<string, unknown>;
|
|
34
|
+
required?: string[];
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Runtime context a surface adapter hands the shared handler. `creds.agentId`
|
|
39
|
+
* may be absent for agent-scoped operator keys (payments rail); capabilities
|
|
40
|
+
* that impersonate set `needsAgentId` so the adapter fails early instead.
|
|
41
|
+
*/
|
|
42
|
+
export interface CapabilityEnv {
|
|
43
|
+
creds: {
|
|
44
|
+
operatorKey: string;
|
|
45
|
+
agentId?: string;
|
|
46
|
+
};
|
|
47
|
+
/** SDK runner base-URL override (BACKEND_URL / ZIGGS_BACKEND_URL). */
|
|
48
|
+
baseUrl?: string;
|
|
49
|
+
/** Web-app origin for human-facing URLs (claim links etc.). */
|
|
50
|
+
webUrl?: string;
|
|
51
|
+
surface: CapabilitySurface;
|
|
52
|
+
}
|
|
53
|
+
export interface CapabilityDefinition {
|
|
54
|
+
/** Stable capability key (the parity-table base name), e.g. 'payment_transfer'. */
|
|
55
|
+
key: string;
|
|
56
|
+
/** Registered tool name per surface — the ZIG-903 parity table is the authority. */
|
|
57
|
+
names: {
|
|
58
|
+
sdk: string;
|
|
59
|
+
mcp: string;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Tool description per surface. Kept side by side deliberately: wording may
|
|
63
|
+
* reference surface-local tool names and MCP delegate-protocol guidance, but
|
|
64
|
+
* schema + handler can no longer drift.
|
|
65
|
+
*/
|
|
66
|
+
descriptions: {
|
|
67
|
+
sdk: string;
|
|
68
|
+
mcp: string;
|
|
69
|
+
};
|
|
70
|
+
annotation: CapabilityAnnotation;
|
|
71
|
+
params: Record<string, CapabilityParam>;
|
|
72
|
+
/** True when the handler impersonates an agent (X-Agent-Id required). */
|
|
73
|
+
needsAgentId?: boolean;
|
|
74
|
+
handler: (args: Record<string, unknown>, env: CapabilityEnv) => Promise<unknown>;
|
|
75
|
+
/** Pass-through flags for the SDK's defineTool options. */
|
|
76
|
+
sdkOptions?: {
|
|
77
|
+
isAgreementCreation?: boolean;
|
|
78
|
+
isGenericFallback?: boolean;
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/** Creds for impersonated calls; adapters guarantee agentId when needsAgentId. */
|
|
82
|
+
export declare function fullCreds(env: CapabilityEnv): Creds;
|
|
83
|
+
/**
|
|
84
|
+
* Re-throw a client error with a capability-level prefix, preserving the HTTP
|
|
85
|
+
* status/body the clients attach (the SDK runtime and toolError classification
|
|
86
|
+
* both read them).
|
|
87
|
+
*/
|
|
88
|
+
export declare function rethrowWithContext(error: unknown, prefix: string): never;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** Creds for impersonated calls; adapters guarantee agentId when needsAgentId. */
|
|
2
|
+
export function fullCreds(env) {
|
|
3
|
+
const { operatorKey, agentId } = env.creds;
|
|
4
|
+
if (!operatorKey)
|
|
5
|
+
throw new Error('operatorKey missing from tool context');
|
|
6
|
+
if (!agentId)
|
|
7
|
+
throw new Error('agentId missing from tool context');
|
|
8
|
+
return { operatorKey, agentId };
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Re-throw a client error with a capability-level prefix, preserving the HTTP
|
|
12
|
+
* status/body the clients attach (the SDK runtime and toolError classification
|
|
13
|
+
* both read them).
|
|
14
|
+
*/
|
|
15
|
+
export function rethrowWithContext(error, prefix) {
|
|
16
|
+
const e = error;
|
|
17
|
+
const wrapped = new Error(`${prefix}: ${e.message}`);
|
|
18
|
+
if (e.status !== undefined)
|
|
19
|
+
wrapped.status = e.status;
|
|
20
|
+
if (e.body !== undefined)
|
|
21
|
+
wrapped.body = e.body;
|
|
22
|
+
wrapped['cause'] = error;
|
|
23
|
+
throw wrapped;
|
|
24
|
+
}
|
|
@@ -14,6 +14,12 @@ export interface ProposeTerms {
|
|
|
14
14
|
lifecycle?: string;
|
|
15
15
|
expiresAt?: string;
|
|
16
16
|
maxExecutions?: number;
|
|
17
|
+
/**
|
|
18
|
+
* How `price` reads. `total` (default) escrows one price for the whole
|
|
19
|
+
* engagement and pays at fulfillment; `per_task` is a RATE settled as each
|
|
20
|
+
* task completes (standing/open agreements only, and the default for a hire).
|
|
21
|
+
*/
|
|
22
|
+
billing?: 'total' | 'per_task';
|
|
17
23
|
agreementDescription?: string;
|
|
18
24
|
parentAgreementId?: string;
|
|
19
25
|
parentTaskId?: string;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import 'dotenv/config';
|
|
2
|
+
import type { GrantView } from './grants.js';
|
|
2
3
|
export declare function assertNoLeakedConnectionSecret(serialized: string): void;
|
|
3
4
|
/** Thrown by ConnectionsClient with the HTTP status and raw body attached. */
|
|
4
5
|
export interface ConnectionsError extends Error {
|
|
@@ -11,6 +12,23 @@ export interface ConnectionProxyParams {
|
|
|
11
12
|
action: string;
|
|
12
13
|
payload?: unknown;
|
|
13
14
|
}
|
|
15
|
+
/** A grant row from the per-connection lister (GET /connections/:id/grants). */
|
|
16
|
+
export interface ConnectionGrant {
|
|
17
|
+
grantId?: string;
|
|
18
|
+
holderId?: string;
|
|
19
|
+
caveats?: unknown[];
|
|
20
|
+
[key: string]: unknown;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* ZIG-641 / ZIG-648 — every connection grant the acting agent holds, grouped by
|
|
24
|
+
* connection, via the unified GET /grants. `provider` comes from the grant's
|
|
25
|
+
* resolved scope label.
|
|
26
|
+
*/
|
|
27
|
+
export interface ConnectionWithGrants {
|
|
28
|
+
connectionId: string;
|
|
29
|
+
provider: string | null;
|
|
30
|
+
grants: GrantView[];
|
|
31
|
+
}
|
|
14
32
|
export interface McpConnectionRequestParams {
|
|
15
33
|
serverUrl: string;
|
|
16
34
|
tools: string[];
|
|
@@ -53,7 +71,15 @@ export declare class ConnectionsClient {
|
|
|
53
71
|
*/
|
|
54
72
|
listGrants({ connectionId }: {
|
|
55
73
|
connectionId: string;
|
|
56
|
-
}): Promise<
|
|
74
|
+
}): Promise<ConnectionGrant[]>;
|
|
75
|
+
/**
|
|
76
|
+
* ZIG-641 / ZIG-956 — cross-connection discovery over the unified GET /grants:
|
|
77
|
+
* every live connection grant this agent holds, grouped by connection, so a
|
|
78
|
+
* proxy caller's connectionId/grantId no longer has to arrive out of band.
|
|
79
|
+
* Moved here from ziggs-mcp's inline helper. The response is scanned
|
|
80
|
+
* defensively for leaked secrets, as `proxy` does.
|
|
81
|
+
*/
|
|
82
|
+
listForHolder(): Promise<ConnectionWithGrants[]>;
|
|
57
83
|
/** Issue a connection grant to an agent holder (connection owner side). */
|
|
58
84
|
issueGrant({ connectionId, holderId, caveats, }: {
|
|
59
85
|
connectionId: string;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import 'dotenv/config';
|
|
2
2
|
import { getBackendUrl } from '../utils/urlUtils.js';
|
|
3
3
|
import { buildOperatorHeaders } from './operatorHeaders.js';
|
|
4
|
+
import { GrantsClient } from './GrantsClient.js';
|
|
4
5
|
// ZIG-569 — defense-in-depth mirror of the backend leak-guard
|
|
5
6
|
// (assertProxyResponseDoesNotLeakTokens). The backend strips the *specific*
|
|
6
7
|
// vault token from the response; clients never see that token, so this layer
|
|
@@ -82,6 +83,34 @@ export class ConnectionsClient {
|
|
|
82
83
|
const grants = res['grants'] || [];
|
|
83
84
|
return grants.filter((g) => g['holderId'] === this.agentId);
|
|
84
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* ZIG-641 / ZIG-956 — cross-connection discovery over the unified GET /grants:
|
|
88
|
+
* every live connection grant this agent holds, grouped by connection, so a
|
|
89
|
+
* proxy caller's connectionId/grantId no longer has to arrive out of band.
|
|
90
|
+
* Moved here from ziggs-mcp's inline helper. The response is scanned
|
|
91
|
+
* defensively for leaked secrets, as `proxy` does.
|
|
92
|
+
*/
|
|
93
|
+
async listForHolder() {
|
|
94
|
+
const grantsClient = new GrantsClient(this.operatorKey, this.agentId, this.baseUrl);
|
|
95
|
+
// All pages of the agent's live connection grants (not just the first page).
|
|
96
|
+
const items = await grantsClient.listAllGrants({
|
|
97
|
+
scopeKind: 'connection',
|
|
98
|
+
health: 'active',
|
|
99
|
+
});
|
|
100
|
+
const byConnection = new Map();
|
|
101
|
+
for (const g of items) {
|
|
102
|
+
const connectionId = g.scope.id;
|
|
103
|
+
let group = byConnection.get(connectionId);
|
|
104
|
+
if (!group) {
|
|
105
|
+
group = { connectionId, provider: g.scope.label ?? null, grants: [] };
|
|
106
|
+
byConnection.set(connectionId, group);
|
|
107
|
+
}
|
|
108
|
+
group.grants.push(g);
|
|
109
|
+
}
|
|
110
|
+
const result = [...byConnection.values()];
|
|
111
|
+
assertNoLeakedConnectionSecret(JSON.stringify(result));
|
|
112
|
+
return result;
|
|
113
|
+
}
|
|
85
114
|
/** Issue a connection grant to an agent holder (connection owner side). */
|
|
86
115
|
async issueGrant({ connectionId, holderId, caveats, }) {
|
|
87
116
|
if (!connectionId)
|
|
@@ -49,7 +49,13 @@ export class ContextReadClient {
|
|
|
49
49
|
const res = await fetch(url.toString(), { headers });
|
|
50
50
|
const body = await res.text().catch(() => '');
|
|
51
51
|
if (!res.ok) {
|
|
52
|
-
|
|
52
|
+
// Carry the status like `snapshot()` does, so callers can branch on it.
|
|
53
|
+
// A 403 here is a legitimate outcome, not a transport failure: addressing
|
|
54
|
+
// and authorisation are separate, so an agent can be told about mail it
|
|
55
|
+
// is not (or is no longer) allowed to open.
|
|
56
|
+
const err = new Error(`ContextReadClient.read ${type} ${res.status} ${body.slice(0, 200)}`);
|
|
57
|
+
err.status = res.status;
|
|
58
|
+
throw err;
|
|
53
59
|
}
|
|
54
60
|
return JSON.parse(body);
|
|
55
61
|
}
|
|
@@ -16,10 +16,26 @@ export interface ListGrantsQuery {
|
|
|
16
16
|
cursor?: string;
|
|
17
17
|
limit?: number;
|
|
18
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* ZIG-893 / ZIG-956 — a grant rail the caller's operator key cannot read, named
|
|
21
|
+
* by the backend on `GET /grants` (it silently drops those rails from `items`,
|
|
22
|
+
* so the unified list tool names what it isn't entitled to instead of
|
|
23
|
+
* presenting a short list as if it were complete). Replaces the client-side
|
|
24
|
+
* mirror of the backend scope table + JWT decode (the retired grantRails.ts).
|
|
25
|
+
*/
|
|
26
|
+
export interface UnreadableRail {
|
|
27
|
+
rail: 'context' | 'connection' | 'wallet';
|
|
28
|
+
requiredScope: string;
|
|
29
|
+
}
|
|
19
30
|
export interface ListGrantsResult {
|
|
20
31
|
items: GrantView[];
|
|
21
32
|
nextCursor: string | null;
|
|
22
33
|
hasMore: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Rails the caller can't read, per the backend. Absent when the backend
|
|
36
|
+
* predates ZIG-956 or every requested rail was readable.
|
|
37
|
+
*/
|
|
38
|
+
unreadableRails?: UnreadableRail[];
|
|
23
39
|
}
|
|
24
40
|
/**
|
|
25
41
|
* ZIG-648 — unified grant listing across every rail. `GET /grants` returns the
|
|
@@ -1,28 +1,24 @@
|
|
|
1
1
|
import 'dotenv/config';
|
|
2
|
-
export type InboxScopeKind = 'chat' | 'agreement' | 'org';
|
|
3
2
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* One thing addressed to this agent. A reference, never content — following it
|
|
4
|
+
* (a chat read, a task read) is where this agent's grants are enforced.
|
|
6
5
|
*/
|
|
7
|
-
export interface
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
export interface InboxDeliveryRef {
|
|
7
|
+
/** 'message' | 'artifact' | 'task-state' | 'agreement'. */
|
|
8
|
+
kind: string;
|
|
9
|
+
resourceId: string;
|
|
10
|
+
chatId: string | null;
|
|
11
|
+
agreementId: string | null;
|
|
12
|
+
taskId: string | null;
|
|
13
|
+
/** Who wrote it. Never this agent — you are not woken by your own writes. */
|
|
14
|
+
actorId: string | null;
|
|
15
|
+
ts: string;
|
|
12
16
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
newMessages: number;
|
|
19
|
-
newArtifacts: number;
|
|
20
|
-
latestAt: string | null;
|
|
21
|
-
since: string;
|
|
22
|
-
/** Per-chat breakdown for org / agreement scopes (ZIG-543). Absent for a chat scope. */
|
|
23
|
-
chats?: InboxScopeChat[];
|
|
24
|
-
/** Chats with news beyond the per-scope cap, not listed in `chats`. */
|
|
25
|
-
truncatedChats?: number;
|
|
17
|
+
/** Message/artifact deliveries folded by chat, so you can open chats directly. */
|
|
18
|
+
export interface InboxChatNews {
|
|
19
|
+
chatId: string;
|
|
20
|
+
count: number;
|
|
21
|
+
latestAt: string;
|
|
26
22
|
}
|
|
27
23
|
export interface InboxProposalRef {
|
|
28
24
|
agreementId: string;
|
|
@@ -35,54 +31,59 @@ export interface InboxConnectionRequestRef {
|
|
|
35
31
|
message: string | null;
|
|
36
32
|
requestedAt: string | null;
|
|
37
33
|
}
|
|
38
|
-
/** Agent asking its principal to connect an MCP server + grant tools (ZIG-686). */
|
|
39
|
-
export interface InboxMcpServerRequestRef {
|
|
40
|
-
requestId: string;
|
|
41
|
-
requesterAgentId: string;
|
|
42
|
-
serverUrl: string;
|
|
43
|
-
tools: string[];
|
|
44
|
-
reason: string | null;
|
|
45
|
-
requestedAt: string | null;
|
|
46
|
-
}
|
|
47
34
|
/** Pull-only MCP: prompt the human when proposals need a decision (ZIG-482 / ZIG-481). */
|
|
48
35
|
export interface InboxHumanAttention {
|
|
49
36
|
required: true;
|
|
50
|
-
reason: 'proposals_awaiting_me' | 'connection_requests_awaiting_me' | '
|
|
37
|
+
reason: 'proposals_awaiting_me' | 'connection_requests_awaiting_me' | 'multiple';
|
|
51
38
|
proposalCount: number;
|
|
52
39
|
truncatedProposals: number;
|
|
53
40
|
connectionRequestCount: number;
|
|
54
41
|
truncatedConnectionRequests: number;
|
|
55
|
-
mcpServerRequestCount: number;
|
|
56
|
-
truncatedMcpServerRequests: number;
|
|
57
42
|
promptUser: string;
|
|
58
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* An open task assigned to this agent (ZIG-973). References only — read the
|
|
46
|
+
* task for its description/plan/inputs.
|
|
47
|
+
*
|
|
48
|
+
* Tasks ride their own channel because assignment IS their delivery: before
|
|
49
|
+
* this, a task wake was a synthetic chat row, so work under a chat-less
|
|
50
|
+
* agreement (or self-assigned) reached nobody.
|
|
51
|
+
*/
|
|
52
|
+
export interface InboxTaskRef {
|
|
53
|
+
taskId: string;
|
|
54
|
+
agreementId: string | null;
|
|
55
|
+
title: string;
|
|
56
|
+
state: string;
|
|
57
|
+
updatedAt: string | null;
|
|
58
|
+
}
|
|
59
59
|
export interface InboxEnvelope {
|
|
60
60
|
asOf: string;
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Unacked deliveries addressed to this agent, newest first. This IS the
|
|
63
|
+
* inbox — read straight out of the delivery log, not derived from grants.
|
|
64
|
+
*/
|
|
65
|
+
deliveries: InboxDeliveryRef[];
|
|
66
|
+
/** True when there was more than one envelope's worth; the rest stay unacked. */
|
|
67
|
+
deliveriesCapped: boolean;
|
|
68
|
+
/** The chat-bearing deliveries above, folded by chat. */
|
|
69
|
+
chats: InboxChatNews[];
|
|
70
|
+
/**
|
|
71
|
+
* Pass to `ack()` after acting. Null when there is nothing to ack. Ack after
|
|
72
|
+
* acting, not after reading: a crash in between redelivers.
|
|
73
|
+
*/
|
|
74
|
+
ackTo: string | null;
|
|
75
|
+
/** Open tasks assigned to this agent — the work channel (ZIG-973). */
|
|
76
|
+
tasksAwaitingMe: InboxTaskRef[];
|
|
77
|
+
truncatedTasks: number;
|
|
64
78
|
proposalsAwaitingMe: InboxProposalRef[];
|
|
65
79
|
truncatedProposals: number;
|
|
66
80
|
connectionRequestsAwaitingMe: InboxConnectionRequestRef[];
|
|
67
81
|
truncatedConnectionRequests: number;
|
|
68
|
-
/** Agent requests to connect an MCP server awaiting the user (ZIG-686). */
|
|
69
|
-
mcpServerRequestsAwaitingMe: InboxMcpServerRequestRef[];
|
|
70
|
-
truncatedMcpServerRequests: number;
|
|
71
82
|
humanAttention?: InboxHumanAttention;
|
|
72
83
|
}
|
|
73
|
-
export interface InboxAck {
|
|
74
|
-
kind: InboxScopeKind;
|
|
75
|
-
id: string;
|
|
76
|
-
upTo: string;
|
|
77
|
-
}
|
|
78
84
|
export interface InboxAckResult {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
kind: InboxScopeKind;
|
|
82
|
-
id: string;
|
|
83
|
-
};
|
|
84
|
-
ackedUpTo: string;
|
|
85
|
-
}>;
|
|
85
|
+
/** Where the watermark now sits. Monotonic — a rewind is a no-op, not an error. */
|
|
86
|
+
ackedUpTo: string | null;
|
|
86
87
|
}
|
|
87
88
|
/**
|
|
88
89
|
* Long-poll option shared by the inbox reads. The server holds the request up
|
|
@@ -93,27 +94,50 @@ export interface InboxAckResult {
|
|
|
93
94
|
*/
|
|
94
95
|
export interface InboxReadOptions {
|
|
95
96
|
waitSeconds?: number;
|
|
97
|
+
/**
|
|
98
|
+
* Operator read only (ZIG-965): restrict the sweep to this roster. The
|
|
99
|
+
* server intersects it with the agents the key owner runs — it narrows,
|
|
100
|
+
* never widens. A launcher hosting a subset should always pass the agents
|
|
101
|
+
* it actually registered, or it pays for a sweep of the owner's whole
|
|
102
|
+
* seeded fleet.
|
|
103
|
+
*/
|
|
104
|
+
agents?: string[];
|
|
96
105
|
}
|
|
97
|
-
/**
|
|
106
|
+
/**
|
|
107
|
+
* One agent's line in the operator sweep: enough to decide whether to start a
|
|
108
|
+
* host, and nothing more.
|
|
109
|
+
*
|
|
110
|
+
* Deliberately NOT that agent's envelope. The launcher only chooses who to
|
|
111
|
+
* run; the host it starts reads its own inbox on its own credentials a moment
|
|
112
|
+
* later, so building N envelopes here was work thrown away.
|
|
113
|
+
*/
|
|
98
114
|
export interface InboxOperatorAgentEntry {
|
|
99
115
|
agentId: string;
|
|
100
|
-
/**
|
|
101
|
-
|
|
116
|
+
/** Newest delivery addressed to this agent, or null if it never had one. */
|
|
117
|
+
deliveredUpTo: string | null;
|
|
118
|
+
/** How far it has acked. Mail exists when `deliveredUpTo > ackedUpTo`. */
|
|
119
|
+
ackedUpTo: string | null;
|
|
120
|
+
/**
|
|
121
|
+
* True when this agent holds open assigned work. Independent of the
|
|
122
|
+
* watermark: a host that died mid-task already acked past the task's
|
|
123
|
+
* delivery, and the open task is the only durable trace it needs restarting.
|
|
124
|
+
*/
|
|
125
|
+
hasOpenTasks: boolean;
|
|
102
126
|
}
|
|
103
|
-
/** GET /inbox/operator:
|
|
127
|
+
/** GET /inbox/operator: which of the key owner's agents have mail or open work. */
|
|
104
128
|
export interface InboxOperatorEnvelope {
|
|
105
129
|
asOf: string;
|
|
106
|
-
/** Agents with
|
|
130
|
+
/** Agents with mail or open work. */
|
|
107
131
|
agents: InboxOperatorAgentEntry[];
|
|
108
|
-
/** Agents examined
|
|
132
|
+
/** Agents examined that had neither. */
|
|
109
133
|
idleAgents: number;
|
|
110
134
|
/** Owned agents beyond the server's per-pass cap — not examined. */
|
|
111
135
|
truncatedAgents: number;
|
|
112
136
|
}
|
|
113
137
|
/**
|
|
114
|
-
* The doorbell, not the door (ZIG-434): references
|
|
115
|
-
*
|
|
116
|
-
* Wraps `GET /inbox` and `POST /inbox/ack`.
|
|
138
|
+
* The doorbell, not the door (ZIG-434): references addressed to this agent
|
|
139
|
+
* since its last ack — never content. Flow: inbox → read → act → ack
|
|
140
|
+
* (ZIG-446). Wraps `GET /inbox` and `POST /inbox/ack`.
|
|
117
141
|
*/
|
|
118
142
|
export declare class InboxClient {
|
|
119
143
|
private readonly operatorKey;
|
|
@@ -128,11 +152,17 @@ export declare class InboxClient {
|
|
|
128
152
|
private inboxUrl;
|
|
129
153
|
getInbox(opts?: InboxReadOptions): Promise<InboxEnvelope>;
|
|
130
154
|
/**
|
|
131
|
-
* Operator-level multiplexed read:
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
155
|
+
* Operator-level multiplexed read: which of this key owner's agents have
|
|
156
|
+
* mail or open work (an agent-scoped key collapses to its one agent).
|
|
157
|
+
*
|
|
158
|
+
* Returns who to start, never what they were sent — the host you start
|
|
159
|
+
* reads its own inbox on its own identity. Ack stays per agent.
|
|
135
160
|
*/
|
|
136
161
|
getOperatorInbox(opts?: InboxReadOptions): Promise<InboxOperatorEnvelope>;
|
|
137
|
-
|
|
162
|
+
/**
|
|
163
|
+
* Advance this agent's watermark — pass the envelope's `ackTo`. Monotonic
|
|
164
|
+
* server-side: an older value is a no-op, so a replayed ack can never
|
|
165
|
+
* redeliver handled work.
|
|
166
|
+
*/
|
|
167
|
+
ack(upTo: string): Promise<InboxAckResult>;
|
|
138
168
|
}
|
package/dist/http/InboxClient.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import 'dotenv/config';
|
|
2
2
|
import { getBackendUrl } from '../utils/urlUtils.js';
|
|
3
3
|
/**
|
|
4
|
-
* The doorbell, not the door (ZIG-434): references
|
|
5
|
-
*
|
|
6
|
-
* Wraps `GET /inbox` and `POST /inbox/ack`.
|
|
4
|
+
* The doorbell, not the door (ZIG-434): references addressed to this agent
|
|
5
|
+
* since its last ack — never content. Flow: inbox → read → act → ack
|
|
6
|
+
* (ZIG-446). Wraps `GET /inbox` and `POST /inbox/ack`.
|
|
7
7
|
*/
|
|
8
8
|
export class InboxClient {
|
|
9
9
|
operatorKey;
|
|
@@ -34,6 +34,9 @@ export class InboxClient {
|
|
|
34
34
|
if (opts.waitSeconds != null && opts.waitSeconds > 0) {
|
|
35
35
|
url.searchParams.set('wait', String(opts.waitSeconds));
|
|
36
36
|
}
|
|
37
|
+
if (opts.agents?.length) {
|
|
38
|
+
url.searchParams.set('agents', opts.agents.join(','));
|
|
39
|
+
}
|
|
37
40
|
return url.toString();
|
|
38
41
|
}
|
|
39
42
|
async getInbox(opts = {}) {
|
|
@@ -47,28 +50,53 @@ export class InboxClient {
|
|
|
47
50
|
return JSON.parse(body);
|
|
48
51
|
}
|
|
49
52
|
/**
|
|
50
|
-
* Operator-level multiplexed read:
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
53
|
+
* Operator-level multiplexed read: which of this key owner's agents have
|
|
54
|
+
* mail or open work (an agent-scoped key collapses to its one agent).
|
|
55
|
+
*
|
|
56
|
+
* Returns who to start, never what they were sent — the host you start
|
|
57
|
+
* reads its own inbox on its own identity. Ack stays per agent.
|
|
54
58
|
*/
|
|
55
59
|
async getOperatorInbox(opts = {}) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
60
|
+
// Bounded wait (ZIG-965): the server holds at most `waitSeconds` (+ sweep
|
|
61
|
+
// time); a poll that outlives that by a wide margin is a dead sweep, and
|
|
62
|
+
// without a timeout it blocked the launcher's whole poll loop — lazy wake
|
|
63
|
+
// simply stopped. Abort and let the caller's retry loop take over.
|
|
64
|
+
const timeoutMs = ((opts.waitSeconds ?? 0) + 30) * 1000;
|
|
65
|
+
const ac = new AbortController();
|
|
66
|
+
const timer = setTimeout(() => ac.abort(), timeoutMs);
|
|
67
|
+
let res;
|
|
68
|
+
try {
|
|
69
|
+
res = await fetch(this.inboxUrl('/inbox/operator', opts), {
|
|
70
|
+
headers: this.headers(),
|
|
71
|
+
signal: ac.signal,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
throw ac.signal.aborted
|
|
76
|
+
? new Error(`InboxClient.getOperatorInbox timed out after ${timeoutMs}ms`)
|
|
77
|
+
: err;
|
|
78
|
+
}
|
|
79
|
+
finally {
|
|
80
|
+
clearTimeout(timer);
|
|
81
|
+
}
|
|
59
82
|
const body = await res.text().catch(() => '');
|
|
60
83
|
if (!res.ok) {
|
|
61
84
|
throw new Error(`InboxClient.getOperatorInbox ${res.status} ${body.slice(0, 200)}`);
|
|
62
85
|
}
|
|
63
86
|
return JSON.parse(body);
|
|
64
87
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
88
|
+
/**
|
|
89
|
+
* Advance this agent's watermark — pass the envelope's `ackTo`. Monotonic
|
|
90
|
+
* server-side: an older value is a no-op, so a replayed ack can never
|
|
91
|
+
* redeliver handled work.
|
|
92
|
+
*/
|
|
93
|
+
async ack(upTo) {
|
|
94
|
+
if (!upTo)
|
|
95
|
+
throw new Error('InboxClient.ack: upTo is required');
|
|
68
96
|
const res = await fetch(`${this.baseUrl}/inbox/ack`, {
|
|
69
97
|
method: 'POST',
|
|
70
98
|
headers: this.headers(),
|
|
71
|
-
body: JSON.stringify({
|
|
99
|
+
body: JSON.stringify({ upTo }),
|
|
72
100
|
});
|
|
73
101
|
const body = await res.text().catch(() => '');
|
|
74
102
|
if (!res.ok) {
|
|
@@ -8,6 +8,12 @@ export interface PublishOfferPayload {
|
|
|
8
8
|
maxExecutions?: number;
|
|
9
9
|
/** `hire` = claimer becomes the provider's principal on claim. Defaults to `service` server-side. */
|
|
10
10
|
engagementKind?: 'hire' | 'service';
|
|
11
|
+
/**
|
|
12
|
+
* How `price` reads. `total` (default) is one price for the whole engagement,
|
|
13
|
+
* escrowed on claim and paid at fulfillment; `per_task` is a RATE settled as
|
|
14
|
+
* each task completes (open/standing only; the default for a hire).
|
|
15
|
+
*/
|
|
16
|
+
billing?: 'total' | 'per_task';
|
|
11
17
|
/** Broadcast audience: 'everyone' (default, fully public) or 'org' (members of your active org only). */
|
|
12
18
|
audience?: BroadcastAudience;
|
|
13
19
|
metadata?: Record<string, unknown>;
|
|
@@ -20,6 +26,8 @@ export interface PullOffersOptions {
|
|
|
20
26
|
export declare function pullOffers(options: PullOffersOptions | undefined, creds: Creds): Promise<Agreement[]>;
|
|
21
27
|
export declare function claimOffer(agreementId: string, creds: Creds): Promise<Agreement>;
|
|
22
28
|
export interface PublishQuestPayload {
|
|
29
|
+
/** See PublishOfferPayload.billing. */
|
|
30
|
+
billing?: 'total' | 'per_task';
|
|
23
31
|
description: string;
|
|
24
32
|
chatId?: string;
|
|
25
33
|
payerId?: string;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import 'dotenv/config';
|
|
2
|
+
import type { Creds } from '../types.js';
|
|
3
|
+
export interface MyOrg {
|
|
4
|
+
orgId: string;
|
|
5
|
+
name: string;
|
|
6
|
+
kind: string;
|
|
7
|
+
role?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* ZIG-739 / ZIG-956 — the operator's full org membership (not just granted
|
|
11
|
+
* scopes, which is all the grant listers see). Lets a delegate resolve an org
|
|
12
|
+
* name to an id and offer a pick-list instead of demanding a pasted org_... id.
|
|
13
|
+
* Moved here from ziggs-mcp so every surface rides the one client (ZIG-894).
|
|
14
|
+
*/
|
|
15
|
+
export declare function fetchMyOrgs(creds: Creds, baseUrl?: string): Promise<MyOrg[]>;
|
|
16
|
+
export type OrgResolution = {
|
|
17
|
+
status: 'ok';
|
|
18
|
+
orgId: string;
|
|
19
|
+
} | {
|
|
20
|
+
status: 'ambiguous';
|
|
21
|
+
matches: MyOrg[];
|
|
22
|
+
} | {
|
|
23
|
+
status: 'not-found';
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* ZIG-739 — resolve an org selector (exact org_... id OR a name/handle) against
|
|
27
|
+
* the operator's memberships. Exact id wins; otherwise case-insensitive name
|
|
28
|
+
* match. Ambiguous names return the candidates rather than guessing.
|
|
29
|
+
*/
|
|
30
|
+
export declare function resolveOrgSelector(orgs: MyOrg[], selector: string): OrgResolution;
|
|
31
|
+
/**
|
|
32
|
+
* ZIG-640 / ZIG-956 — runtime acting org from the server (self-hire / agent
|
|
33
|
+
* row): GET /agents/claude-delegate/access. Moved here from ziggs-mcp's inline
|
|
34
|
+
* fetch (ZIG-894 "one client for every surface").
|
|
35
|
+
*/
|
|
36
|
+
export declare function fetchDelegateAccess(creds: Creds, baseUrl?: string): Promise<Record<string, unknown>>;
|