@ziggs-ai/api-client 0.37.27 → 0.38.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/CHANGELOG.md +10 -0
- package/dist/capabilities/artifacts.js +9 -9
- package/dist/capabilities/connections.js +3 -8
- package/dist/capabilities/context.js +15 -4
- package/dist/capabilities/grants.js +1 -4
- package/dist/capabilities/introductions.js +13 -12
- package/dist/capabilities/mcpBriefs.js +10 -1
- package/dist/capabilities/payments.js +2 -7
- package/dist/http/AgreementClient.d.ts +10 -2
- package/dist/http/AgreementClient.js +6 -4
- package/dist/http/ArtifactsClient.d.ts +2 -9
- package/dist/http/ArtifactsClient.js +3 -11
- package/dist/http/AssistantConnectionClient.d.ts +1 -1
- package/dist/http/AssistantConnectionClient.js +2 -2
- package/dist/http/ConnectionsClient.d.ts +2 -12
- package/dist/http/ConnectionsClient.js +4 -15
- package/dist/http/ContextGrantsClient.d.ts +1 -0
- package/dist/http/ContextGrantsClient.js +1 -1
- package/dist/http/ContextOpenClient.d.ts +2 -3
- package/dist/http/ContextOpenClient.js +2 -4
- package/dist/http/ContextReadClient.d.ts +5 -13
- package/dist/http/ContextReadClient.js +4 -19
- package/dist/http/ExecutionContextClient.d.ts +3 -0
- package/dist/http/ExecutionContextClient.js +17 -0
- package/dist/http/GrantsClient.d.ts +2 -10
- package/dist/http/GrantsClient.js +3 -12
- package/dist/http/InboxClient.d.ts +2 -2
- package/dist/http/InboxClient.js +1 -3
- package/dist/http/IntroductionsClient.d.ts +21 -1
- package/dist/http/PaymentsClient.d.ts +2 -15
- package/dist/http/PaymentsClient.js +4 -18
- package/dist/http/SessionsClient.d.ts +34 -0
- package/dist/http/SessionsClient.js +72 -0
- package/dist/http/TelemetryClient.js +3 -3
- package/dist/http/ToolPickClient.js +2 -2
- package/dist/http/index.d.ts +3 -1
- package/dist/http/index.js +2 -1
- package/dist/http/operatorHeaders.d.ts +14 -36
- package/dist/http/operatorHeaders.js +26 -50
- package/dist/inboxAck.d.ts +14 -46
- package/dist/inboxAck.js +12 -102
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/types.d.ts +15 -16
- package/package.json +2 -2
|
@@ -57,28 +57,13 @@ export class ContextReadClient {
|
|
|
57
57
|
operatorKey;
|
|
58
58
|
agentId;
|
|
59
59
|
baseUrl;
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
* @param operatorKey Agent-scoped or fleet operator key.
|
|
63
|
-
* @param agentId Required for fleet keys (sent as X-Agent-Id). Omit for agent-scoped keys.
|
|
64
|
-
* @param laneId the wake's lane, sent as X-Ziggs-Lane. The server fences a
|
|
65
|
-
* read to the lane's parties; without it, reach falls back to a wider
|
|
66
|
-
* test and a read can be authorised on a weaker basis than the caller
|
|
67
|
-
* intended. Send it on every read made while acting on a wake.
|
|
68
|
-
*/
|
|
69
|
-
constructor(operatorKey, agentId, baseUrl, laneId) {
|
|
60
|
+
/** Authority is supplied by withWorkExecution / withWorkExecutionId. */
|
|
61
|
+
constructor(operatorKey, agentId, baseUrl) {
|
|
70
62
|
if (!operatorKey)
|
|
71
63
|
throw new Error('ContextReadClient: operatorKey is required');
|
|
72
64
|
this.operatorKey = operatorKey;
|
|
73
65
|
this.agentId = agentId;
|
|
74
66
|
this.baseUrl = baseUrl || getBackendUrl();
|
|
75
|
-
this.laneId = laneId;
|
|
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
67
|
}
|
|
83
68
|
async read(type, query) {
|
|
84
69
|
if (!query.via?.trim()) {
|
|
@@ -101,7 +86,7 @@ export class ContextReadClient {
|
|
|
101
86
|
if (query.contextGrantId) {
|
|
102
87
|
url.searchParams.set('contextGrantId', query.contextGrantId);
|
|
103
88
|
}
|
|
104
|
-
const headers = buildOperatorHeaders(this.operatorKey, this.agentId, undefined
|
|
89
|
+
const headers = buildOperatorHeaders(this.operatorKey, this.agentId, undefined);
|
|
105
90
|
if (query.contextGrantId) {
|
|
106
91
|
headers['X-Context-Grant-Id'] = query.contextGrantId;
|
|
107
92
|
}
|
|
@@ -138,7 +123,7 @@ export class ContextReadClient {
|
|
|
138
123
|
if (opts.contextGrantId) {
|
|
139
124
|
url.searchParams.set('contextGrantId', opts.contextGrantId);
|
|
140
125
|
}
|
|
141
|
-
const headers = buildOperatorHeaders(this.operatorKey, this.agentId, undefined
|
|
126
|
+
const headers = buildOperatorHeaders(this.operatorKey, this.agentId, undefined);
|
|
142
127
|
if (opts.contextGrantId) {
|
|
143
128
|
headers['X-Context-Grant-Id'] = opts.contextGrantId;
|
|
144
129
|
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { WorkExecutionContext } from '@ziggs-ai/contracts';
|
|
2
|
+
/** Fail closed before resuming model state, including timers and recorded wakes. */
|
|
3
|
+
export declare function validateWorkExecution(context: WorkExecutionContext, operatorKey: string, baseUrl?: string): Promise<void>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { getBackendUrl } from '../utils/urlUtils.js';
|
|
2
|
+
import { buildOperatorHeaders, withWorkExecution } from './operatorHeaders.js';
|
|
3
|
+
import { throwApiError } from '../shared/apiError.js';
|
|
4
|
+
/** Fail closed before resuming model state, including timers and recorded wakes. */
|
|
5
|
+
export async function validateWorkExecution(context, operatorKey, baseUrl = getBackendUrl()) {
|
|
6
|
+
await withWorkExecution(context, async () => {
|
|
7
|
+
const response = await fetch(`${baseUrl}/execution/context`, {
|
|
8
|
+
headers: buildOperatorHeaders(operatorKey, context.actorId),
|
|
9
|
+
});
|
|
10
|
+
const text = await response.text();
|
|
11
|
+
if (!response.ok)
|
|
12
|
+
throwApiError(response, text, 'Execution context is no longer valid');
|
|
13
|
+
const body = JSON.parse(text);
|
|
14
|
+
if (body.contextId !== context.contextId)
|
|
15
|
+
throw new Error('Execution preflight returned a different context');
|
|
16
|
+
});
|
|
17
|
+
}
|
|
@@ -56,16 +56,8 @@ export declare class GrantsClient {
|
|
|
56
56
|
private readonly operatorKey;
|
|
57
57
|
private readonly agentId?;
|
|
58
58
|
private readonly baseUrl;
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
* @param laneId the wake's lane (a chat id, or `agrn-<agreementId>`), sent as
|
|
62
|
-
* X-Ziggs-Lane. The server reads off it who the agent is acting for, and
|
|
63
|
-
* answers the spendable rails with only the grants this wake may spend —
|
|
64
|
-
* so an agent serving several customers is never handed a list it has to
|
|
65
|
-
* choose between. Without it the list is everything the agent holds, and
|
|
66
|
-
* the grant it picks may then be refused with nothing saying why.
|
|
67
|
-
*/
|
|
68
|
-
constructor(operatorKey: string, agentId?: string, baseUrl?: string, laneId?: string);
|
|
59
|
+
/** Authority is supplied by withWorkExecution / withWorkExecutionId. */
|
|
60
|
+
constructor(operatorKey: string, agentId?: string, baseUrl?: string);
|
|
69
61
|
listGrants(query?: ListGrantsQuery): Promise<ListGrantsResult>;
|
|
70
62
|
/**
|
|
71
63
|
* Every grant matching `query`, following the cursor to completion. Use when a
|
|
@@ -12,22 +12,13 @@ export class GrantsClient {
|
|
|
12
12
|
operatorKey;
|
|
13
13
|
agentId;
|
|
14
14
|
baseUrl;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
* @param laneId the wake's lane (a chat id, or `agrn-<agreementId>`), sent as
|
|
18
|
-
* X-Ziggs-Lane. The server reads off it who the agent is acting for, and
|
|
19
|
-
* answers the spendable rails with only the grants this wake may spend —
|
|
20
|
-
* so an agent serving several customers is never handed a list it has to
|
|
21
|
-
* choose between. Without it the list is everything the agent holds, and
|
|
22
|
-
* the grant it picks may then be refused with nothing saying why.
|
|
23
|
-
*/
|
|
24
|
-
constructor(operatorKey, agentId, baseUrl, laneId) {
|
|
15
|
+
/** Authority is supplied by withWorkExecution / withWorkExecutionId. */
|
|
16
|
+
constructor(operatorKey, agentId, baseUrl) {
|
|
25
17
|
if (!operatorKey)
|
|
26
18
|
throw new Error('GrantsClient: operatorKey is required');
|
|
27
19
|
this.operatorKey = operatorKey;
|
|
28
20
|
this.agentId = agentId;
|
|
29
21
|
this.baseUrl = baseUrl || getBackendUrl();
|
|
30
|
-
this.laneId = laneId;
|
|
31
22
|
}
|
|
32
23
|
async listGrants(query = {}) {
|
|
33
24
|
const url = new URL(`${this.baseUrl}/grants`);
|
|
@@ -51,7 +42,7 @@ export class GrantsClient {
|
|
|
51
42
|
if (query.limit != null)
|
|
52
43
|
url.searchParams.set('limit', String(query.limit));
|
|
53
44
|
const res = await fetch(url.toString(), {
|
|
54
|
-
headers: buildOperatorHeaders(this.operatorKey, this.agentId, undefined
|
|
45
|
+
headers: buildOperatorHeaders(this.operatorKey, this.agentId, undefined),
|
|
55
46
|
});
|
|
56
47
|
const body = await res.text().catch(() => '');
|
|
57
48
|
if (!res.ok) {
|
|
@@ -49,7 +49,7 @@ export declare class InboxClient {
|
|
|
49
49
|
* refused.
|
|
50
50
|
*/
|
|
51
51
|
ack(upTo: string, opts?: {
|
|
52
|
-
|
|
52
|
+
handledDeliveryIds?: string[];
|
|
53
53
|
}): Promise<InboxAckResult>;
|
|
54
54
|
/** The person said to leave this proposal pending. Does not approve or reject. */
|
|
55
55
|
park(agreementId: string): Promise<{
|
|
@@ -62,7 +62,7 @@ export declare class InboxClient {
|
|
|
62
62
|
* A second host-conflict is a live competitor and is rethrown unchanged.
|
|
63
63
|
*/
|
|
64
64
|
ackOrTakeHost(upTo: string, opts?: {
|
|
65
|
-
|
|
65
|
+
handledDeliveryIds?: string[];
|
|
66
66
|
}): Promise<InboxAckResult>;
|
|
67
67
|
/**
|
|
68
68
|
* Drop this process's inbox host lease so another client can take
|
package/dist/http/InboxClient.js
CHANGED
|
@@ -119,9 +119,7 @@ export class InboxClient {
|
|
|
119
119
|
headers: this.headers(),
|
|
120
120
|
body: JSON.stringify({
|
|
121
121
|
upTo,
|
|
122
|
-
...(opts.
|
|
123
|
-
? { handledResourceIds: opts.handledResourceIds }
|
|
124
|
-
: {}),
|
|
122
|
+
...(opts.handledDeliveryIds ? { handledDeliveryIds: opts.handledDeliveryIds } : {}),
|
|
125
123
|
}),
|
|
126
124
|
});
|
|
127
125
|
const body = await res.text().catch(() => '');
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import type { WriteSource } from '../capabilities/sources.js';
|
|
2
2
|
/** What became of one hello. `expired` is derived by the server, not stored. */
|
|
3
3
|
export type IntroductionStatus = 'open' | 'redeemed' | 'declined' | 'revoked' | 'expired';
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Where a redeem ended. `joined`: your agent is in the thing the token names
|
|
6
|
+
* (your own token, a teammate's, or someone you are already linked to).
|
|
7
|
+
* `claimed` / `bid` / `accepted`: the listing, request or proposal it names.
|
|
8
|
+
* `link_pending`: a link both people still approve.
|
|
9
|
+
*/
|
|
10
|
+
export type IntroductionOutcome = 'joined' | 'claimed' | 'bid' | 'accepted' | 'link_pending';
|
|
5
11
|
export interface IntroductionFrom {
|
|
6
12
|
orgId: string;
|
|
7
13
|
agentId: string | null;
|
|
@@ -26,11 +32,25 @@ export interface IntroductionView {
|
|
|
26
32
|
venue: string | null;
|
|
27
33
|
venueRef: string | null;
|
|
28
34
|
note: string | null;
|
|
35
|
+
/** What the token is about. The public view gives kind and title only. */
|
|
36
|
+
about?: {
|
|
37
|
+
kind: 'agreement' | 'task' | 'chat' | 'workspace';
|
|
38
|
+
id?: string;
|
|
39
|
+
title: string | null;
|
|
40
|
+
shape: 'listing' | 'request' | 'proposal' | null;
|
|
41
|
+
} | null;
|
|
42
|
+
/** How many people the token serves, and how many are left. */
|
|
43
|
+
redeems?: {
|
|
44
|
+
allowed: number;
|
|
45
|
+
left: number;
|
|
46
|
+
};
|
|
29
47
|
agreementId: string | null;
|
|
30
48
|
redeemedBy: {
|
|
31
49
|
agentId: string | null;
|
|
32
50
|
orgId: string | null;
|
|
33
51
|
} | null;
|
|
52
|
+
/** Returned to a redeemer only: the room its agent is now in. */
|
|
53
|
+
chatId?: string | null;
|
|
34
54
|
expiresAt: string;
|
|
35
55
|
/** The public page the string points at. This is what you hand over. */
|
|
36
56
|
url: string;
|
|
@@ -61,21 +61,8 @@ export declare class PaymentsClient {
|
|
|
61
61
|
private readonly operatorKey;
|
|
62
62
|
private readonly agentId?;
|
|
63
63
|
private readonly baseUrl;
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
* @param laneId the wake's lane (a chat id, or `agrn-<agreementId>`), sent as
|
|
67
|
-
* X-Ziggs-Lane. It tells the backend which hire this wake is in, and the
|
|
68
|
-
* backend reads off it WHO the spend is for. An agent that serves several
|
|
69
|
-
* hirers holds all of their grants at once and the holder is the same agent
|
|
70
|
-
* in every one, so without a lane, spend authority one customer gave is
|
|
71
|
-
* spendable while the agent works for another. ConnectionsClient and
|
|
72
|
-
* ArtifactsClient send the same header for the same reason.
|
|
73
|
-
*
|
|
74
|
-
* Omitting it cannot widen anything — a stated lane can only cause a
|
|
75
|
-
* refusal — but it does leave the spend unfenced, so a caller that knows
|
|
76
|
-
* its lane should pass it.
|
|
77
|
-
*/
|
|
78
|
-
constructor(operatorKey: string, agentId?: string, baseUrl?: string, laneId?: string);
|
|
64
|
+
/** Authority is supplied by withWorkExecution / withWorkExecutionId. */
|
|
65
|
+
constructor(operatorKey: string, agentId?: string, baseUrl?: string);
|
|
79
66
|
balance(): Promise<WalletBalance>;
|
|
80
67
|
resolve({ userId, agentId }?: {
|
|
81
68
|
userId?: string;
|
|
@@ -22,27 +22,13 @@ export class PaymentsClient {
|
|
|
22
22
|
operatorKey;
|
|
23
23
|
agentId;
|
|
24
24
|
baseUrl;
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
* @param laneId the wake's lane (a chat id, or `agrn-<agreementId>`), sent as
|
|
28
|
-
* X-Ziggs-Lane. It tells the backend which hire this wake is in, and the
|
|
29
|
-
* backend reads off it WHO the spend is for. An agent that serves several
|
|
30
|
-
* hirers holds all of their grants at once and the holder is the same agent
|
|
31
|
-
* in every one, so without a lane, spend authority one customer gave is
|
|
32
|
-
* spendable while the agent works for another. ConnectionsClient and
|
|
33
|
-
* ArtifactsClient send the same header for the same reason.
|
|
34
|
-
*
|
|
35
|
-
* Omitting it cannot widen anything — a stated lane can only cause a
|
|
36
|
-
* refusal — but it does leave the spend unfenced, so a caller that knows
|
|
37
|
-
* its lane should pass it.
|
|
38
|
-
*/
|
|
39
|
-
constructor(operatorKey, agentId, baseUrl, laneId) {
|
|
25
|
+
/** Authority is supplied by withWorkExecution / withWorkExecutionId. */
|
|
26
|
+
constructor(operatorKey, agentId, baseUrl) {
|
|
40
27
|
if (!operatorKey)
|
|
41
28
|
throw new Error('PaymentsClient: operatorKey is required');
|
|
42
29
|
this.operatorKey = operatorKey;
|
|
43
30
|
this.agentId = agentId;
|
|
44
31
|
this.baseUrl = baseUrl || getBackendUrl();
|
|
45
|
-
this.laneId = laneId;
|
|
46
32
|
}
|
|
47
33
|
async balance() {
|
|
48
34
|
const w = (await this._get('/payments/wallet'));
|
|
@@ -87,7 +73,7 @@ export class PaymentsClient {
|
|
|
87
73
|
* (retired GET /payments/grants), following the cursor to completion.
|
|
88
74
|
*/
|
|
89
75
|
async listGrants() {
|
|
90
|
-
const grants = new GrantsClient(this.operatorKey, this.agentId, this.baseUrl
|
|
76
|
+
const grants = new GrantsClient(this.operatorKey, this.agentId, this.baseUrl);
|
|
91
77
|
return grants.listAllGrants({ scopeKind: 'wallet', health: 'active' });
|
|
92
78
|
}
|
|
93
79
|
async faucet({ amount, description = 'Faucet', idempotencyKey, } = {}) {
|
|
@@ -100,7 +86,7 @@ export class PaymentsClient {
|
|
|
100
86
|
async _request(method, path, body) {
|
|
101
87
|
const init = {
|
|
102
88
|
method,
|
|
103
|
-
headers: buildOperatorHeaders(this.operatorKey, this.agentId, body !== undefined ? { 'content-type': 'application/json' } : {}
|
|
89
|
+
headers: buildOperatorHeaders(this.operatorKey, this.agentId, body !== undefined ? { 'content-type': 'application/json' } : {}),
|
|
104
90
|
};
|
|
105
91
|
if (body !== undefined)
|
|
106
92
|
init.body = JSON.stringify(body);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Creds } from '../types.js';
|
|
2
|
+
/** One awake window of an assistant. */
|
|
3
|
+
export interface AssistantSessionRow {
|
|
4
|
+
sessionId: string;
|
|
5
|
+
name: string;
|
|
6
|
+
app: string;
|
|
7
|
+
startedAt: string;
|
|
8
|
+
lastSeenAt: string;
|
|
9
|
+
expiresAt: string;
|
|
10
|
+
workspaceIds: string[];
|
|
11
|
+
heldChatIds: string[];
|
|
12
|
+
awake: boolean;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* List sibling windows and hold a chat in this one.
|
|
16
|
+
* Uses the same session headers as the inbox so "this window" is the
|
|
17
|
+
* X-Ziggs-Session the host already sends.
|
|
18
|
+
*/
|
|
19
|
+
export declare class SessionsClient {
|
|
20
|
+
private readonly creds;
|
|
21
|
+
constructor(creds: Creds);
|
|
22
|
+
private headers;
|
|
23
|
+
private url;
|
|
24
|
+
list(): Promise<AssistantSessionRow[]>;
|
|
25
|
+
/**
|
|
26
|
+
* Hold `chatId` in this window (omit sessionId; server resolves from
|
|
27
|
+
* X-Ziggs-Session), move it to `sessionId`, or pass null to release.
|
|
28
|
+
*/
|
|
29
|
+
hold(chatId: string, sessionId?: string | null): Promise<{
|
|
30
|
+
success: true;
|
|
31
|
+
chatId: string;
|
|
32
|
+
sessionId: string | null;
|
|
33
|
+
}>;
|
|
34
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { getBackendUrl } from '../utils/urlUtils.js';
|
|
2
|
+
import { pollSurfaceError } from '../shared/rateLimit.js';
|
|
3
|
+
import { buildCredsHeaders } from './operatorHeaders.js';
|
|
4
|
+
/**
|
|
5
|
+
* List sibling windows and hold a chat in this one.
|
|
6
|
+
* Uses the same session headers as the inbox so "this window" is the
|
|
7
|
+
* X-Ziggs-Session the host already sends.
|
|
8
|
+
*/
|
|
9
|
+
export class SessionsClient {
|
|
10
|
+
creds;
|
|
11
|
+
constructor(creds) {
|
|
12
|
+
this.creds = creds;
|
|
13
|
+
if (!creds.operatorKey)
|
|
14
|
+
throw new Error('SessionsClient: operatorKey is required');
|
|
15
|
+
if (!creds.agentId)
|
|
16
|
+
throw new Error('SessionsClient: agentId is required');
|
|
17
|
+
}
|
|
18
|
+
headers() {
|
|
19
|
+
const headers = buildCredsHeaders({
|
|
20
|
+
operatorKey: this.creds.operatorKey,
|
|
21
|
+
agentId: this.creds.agentId,
|
|
22
|
+
laneId: this.creds.laneId,
|
|
23
|
+
});
|
|
24
|
+
if (this.creds.clientSessionId)
|
|
25
|
+
headers['X-Ziggs-Session'] = this.creds.clientSessionId;
|
|
26
|
+
if (this.creds.workspaceIds)
|
|
27
|
+
headers['X-Ziggs-Workspaces'] = this.creds.workspaceIds;
|
|
28
|
+
if (this.creds.app)
|
|
29
|
+
headers['X-Ziggs-App'] = this.creds.app;
|
|
30
|
+
if (this.creds.sessionName)
|
|
31
|
+
headers['X-Ziggs-Session-Name'] = this.creds.sessionName;
|
|
32
|
+
if (this.creds.instanceId)
|
|
33
|
+
headers['X-Ziggs-Instance'] = this.creds.instanceId;
|
|
34
|
+
return headers;
|
|
35
|
+
}
|
|
36
|
+
url(path) {
|
|
37
|
+
return `${getBackendUrl()}/agents/${encodeURIComponent(this.creds.agentId)}${path}`;
|
|
38
|
+
}
|
|
39
|
+
async list() {
|
|
40
|
+
const res = await fetch(this.url('/sessions'), {
|
|
41
|
+
method: 'GET',
|
|
42
|
+
headers: this.headers(),
|
|
43
|
+
});
|
|
44
|
+
const body = await res.json().catch(() => ({}));
|
|
45
|
+
if (!res.ok)
|
|
46
|
+
throw pollSurfaceError('SessionsClient.list', res, body);
|
|
47
|
+
const rows = body.sessions;
|
|
48
|
+
return Array.isArray(rows) ? rows : [];
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Hold `chatId` in this window (omit sessionId; server resolves from
|
|
52
|
+
* X-Ziggs-Session), move it to `sessionId`, or pass null to release.
|
|
53
|
+
*/
|
|
54
|
+
async hold(chatId, sessionId) {
|
|
55
|
+
if (!chatId?.trim())
|
|
56
|
+
throw new Error('SessionsClient.hold: chatId is required');
|
|
57
|
+
const body = {
|
|
58
|
+
chatId: chatId.trim(),
|
|
59
|
+
};
|
|
60
|
+
if (sessionId !== undefined)
|
|
61
|
+
body.sessionId = sessionId;
|
|
62
|
+
const res = await fetch(this.url('/sessions/hold'), {
|
|
63
|
+
method: 'POST',
|
|
64
|
+
headers: this.headers(),
|
|
65
|
+
body: JSON.stringify(body),
|
|
66
|
+
});
|
|
67
|
+
const parsed = await res.json().catch(() => ({}));
|
|
68
|
+
if (!res.ok)
|
|
69
|
+
throw pollSurfaceError('SessionsClient.hold', res, parsed);
|
|
70
|
+
return parsed;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { runtimeLog } from '../shared/runtimeLog.js';
|
|
2
2
|
import { getBackendUrl } from '../utils/urlUtils.js';
|
|
3
|
-
import { buildOperatorHeaders } from './operatorHeaders.js';
|
|
3
|
+
import { buildOperatorHeaders, withoutExecution } from './operatorHeaders.js';
|
|
4
4
|
export class TelemetryClient {
|
|
5
5
|
operatorKey;
|
|
6
6
|
agentId;
|
|
@@ -36,9 +36,9 @@ export class TelemetryClient {
|
|
|
36
36
|
try {
|
|
37
37
|
const res = await fetch(url, {
|
|
38
38
|
method: 'POST',
|
|
39
|
-
headers: buildOperatorHeaders(this.operatorKey, this.agentId, {
|
|
39
|
+
headers: withoutExecution(buildOperatorHeaders(this.operatorKey, this.agentId, {
|
|
40
40
|
'content-type': 'application/json',
|
|
41
|
-
}),
|
|
41
|
+
})),
|
|
42
42
|
body: JSON.stringify({ payload }),
|
|
43
43
|
});
|
|
44
44
|
if (!res.ok) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { buildCredsHeaders } from './operatorHeaders.js';
|
|
1
|
+
import { buildCredsHeaders, withoutExecution } from './operatorHeaders.js';
|
|
2
2
|
import { getBackendUrl } from '../utils/urlUtils.js';
|
|
3
3
|
/** A listing is always the fallback, so waiting long for a pick buys nothing. */
|
|
4
4
|
const TIMEOUT_MS = 6000;
|
|
@@ -15,7 +15,7 @@ export function backendToolPick(creds) {
|
|
|
15
15
|
try {
|
|
16
16
|
const res = await fetch(`${getBackendUrl()}/internal/tool-pick`, {
|
|
17
17
|
method: 'POST',
|
|
18
|
-
headers: buildCredsHeaders(creds),
|
|
18
|
+
headers: withoutExecution(buildCredsHeaders(creds)),
|
|
19
19
|
body: JSON.stringify({ need, options }),
|
|
20
20
|
signal: AbortSignal.timeout(TIMEOUT_MS),
|
|
21
21
|
});
|
package/dist/http/index.d.ts
CHANGED
|
@@ -31,7 +31,9 @@ export type { MyOrg, OrgResolution } from './OrgsClient.js';
|
|
|
31
31
|
export { AgentSearchClient } from './AgentSearchClient.js';
|
|
32
32
|
export { TelemetryClient } from './TelemetryClient.js';
|
|
33
33
|
export { InboxClient } from './InboxClient.js';
|
|
34
|
+
export { SessionsClient } from './SessionsClient.js';
|
|
35
|
+
export type { AssistantSessionRow } from './SessionsClient.js';
|
|
34
36
|
export { planInboxAck, planPartialInboxAck, inboxAckAllowed, inboxAckHandledIds, } from '../inboxAck.js';
|
|
35
37
|
export type { InboxAckDecision, InboxAckStep, InboxAckEnvelope, PlanInboxAckOptions, } from '../inboxAck.js';
|
|
36
|
-
export {
|
|
38
|
+
export { laneUnderAgreement, buildOperatorHeaders, buildCredsHeaders, } from './operatorHeaders.js';
|
|
37
39
|
export { backendToolPick, type ToolPickOption, type ToolPickAnswer, type PickTool } from './ToolPickClient.js';
|
package/dist/http/index.js
CHANGED
|
@@ -21,9 +21,10 @@ export { fetchMyOrgs, resolveOrgSelector, fetchDelegateAccess, fetchHostedAgentA
|
|
|
21
21
|
export { AgentSearchClient } from './AgentSearchClient.js';
|
|
22
22
|
export { TelemetryClient } from './TelemetryClient.js';
|
|
23
23
|
export { InboxClient } from './InboxClient.js';
|
|
24
|
+
export { SessionsClient } from './SessionsClient.js';
|
|
24
25
|
export { planInboxAck, planPartialInboxAck, inboxAckAllowed, inboxAckHandledIds, } from '../inboxAck.js';
|
|
25
26
|
// The lane header, built in one place — see operatorHeaders.ts. Exported
|
|
26
27
|
// because the MCP transports in agent-sdk and ziggs-mcp send it too, and a
|
|
27
28
|
// copy of a header nobody notices missing goes stale unseen.
|
|
28
|
-
export {
|
|
29
|
+
export { laneUnderAgreement, buildOperatorHeaders, buildCredsHeaders, } from './operatorHeaders.js';
|
|
29
30
|
export { backendToolPick } from './ToolPickClient.js';
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
import type { WorkExecutionContext } from '@ziggs-ai/contracts';
|
|
1
2
|
type AssistantHints = {
|
|
3
|
+
executionContext?: WorkExecutionContext;
|
|
4
|
+
executionContextId?: string;
|
|
2
5
|
orgId?: string;
|
|
3
6
|
resource?: string;
|
|
4
7
|
clientSessionId?: string;
|
|
@@ -10,49 +13,24 @@ type AssistantHints = {
|
|
|
10
13
|
/** Node-only, opt-in request context. Ordinary HTTP clients remain browser-safe. */
|
|
11
14
|
export declare function withAssistantRequest<T>(hints: AssistantHints, run: () => T): Promise<Awaited<T>>;
|
|
12
15
|
export declare function assistantRequestHeaders(): Record<string, string>;
|
|
13
|
-
/**
|
|
14
|
-
* Auth headers for operator-key HTTP clients.
|
|
15
|
-
*
|
|
16
|
-
* ONE place builds them, because the lane is the header a caller cannot be
|
|
17
|
-
* trusted to remember. Omitting `X-Ziggs-Lane` never fails: the server fences
|
|
18
|
-
* the wake to the agent's own org instead, so a door that forgets it answers
|
|
19
|
-
* short lists and refuses grants the agent's customer gave it — silently, and
|
|
20
|
-
* only for the agents that serve someone other than their owner. A header
|
|
21
|
-
* whose absence is indistinguishable from a legitimate answer has to be built
|
|
22
|
-
* in one place, not copied into each caller.
|
|
23
|
-
*/
|
|
24
|
-
/** The header the backend's wake fence reads (`access/wake-fence.ts`). */
|
|
25
|
-
export declare const WAKE_LANE_HEADER = "X-Ziggs-Lane";
|
|
26
|
-
/**
|
|
27
|
-
* The header naming the agreement a room wake works under. The backend takes
|
|
28
|
-
* it only for an agreement the agent has mail under in that room, and reads
|
|
29
|
-
* it off that mail when it is absent.
|
|
30
|
-
*/
|
|
31
|
-
export declare const WAKE_AGREEMENT_HEADER = "X-Ziggs-Agreement";
|
|
32
|
-
/**
|
|
33
|
-
* A room lane that also names the agreement the wake works under:
|
|
34
|
-
* `<chatId>@<agreementId>`. Every client already carries the lane to
|
|
35
|
-
* {@link laneHeader}, so the agreement travels with it without a second
|
|
36
|
-
* parameter on each of them. An `agrn-` lane already is its agreement.
|
|
37
|
-
*/
|
|
16
|
+
/** A presentation routing key; it carries no authority. */
|
|
38
17
|
export declare function laneUnderAgreement(laneId: string, agreementId?: string | null): string;
|
|
18
|
+
export declare function buildOperatorHeaders(operatorKey: string, agentId?: string, extra?: Record<string, string>): Record<string, string>;
|
|
39
19
|
/**
|
|
40
|
-
* The
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*/
|
|
44
|
-
export declare function laneHeader(laneId?: string): Record<string, string>;
|
|
45
|
-
export declare function buildOperatorHeaders(operatorKey: string, agentId?: string, extra?: Record<string, string>,
|
|
46
|
-
/**
|
|
47
|
-
* the lane this call belongs to, sent as `X-Ziggs-Lane`. The
|
|
48
|
-
* backend narrows the wake's reach to the engagement's orgs; omitting it
|
|
49
|
-
* narrows to the agent's own org, so it can never widen reach.
|
|
20
|
+
* The host's own calls (wake gate, telemetry, tool picker) are not work
|
|
21
|
+
* actions: they never carry the selected execution, so they neither need a
|
|
22
|
+
* route that supports one nor record anything into it.
|
|
50
23
|
*/
|
|
51
|
-
|
|
24
|
+
export declare function withoutExecution(headers: Record<string, string>): Record<string, string>;
|
|
52
25
|
/** What a `Creds`-shaped client sends on a JSON call: auth, agent, lane. */
|
|
53
26
|
export declare function buildCredsHeaders(creds: {
|
|
54
27
|
operatorKey: string;
|
|
55
28
|
agentId: string;
|
|
56
29
|
laneId?: string;
|
|
30
|
+
executionContextId?: string;
|
|
57
31
|
}): Record<string, string>;
|
|
32
|
+
/** Async-local scope: parallel agreement positions never share request headers. */
|
|
33
|
+
export declare function withWorkExecution<T>(executionContext: WorkExecutionContext | undefined, run: () => T): Promise<Awaited<T>>;
|
|
34
|
+
export declare function currentWorkExecution(): WorkExecutionContext | undefined;
|
|
35
|
+
export declare function withWorkExecutionId<T>(executionContextId: string | undefined, run: () => T): Promise<Awaited<T>>;
|
|
58
36
|
export {};
|
|
@@ -14,6 +14,7 @@ export async function withAssistantRequest(hints, run) {
|
|
|
14
14
|
export function assistantRequestHeaders() {
|
|
15
15
|
const hints = assistantRequest?.getStore();
|
|
16
16
|
return {
|
|
17
|
+
...((hints?.executionContextId ?? hints?.executionContext?.contextId) ? { 'X-Ziggs-Execution': (hints?.executionContextId ?? hints?.executionContext?.contextId) } : {}),
|
|
17
18
|
...(hints?.orgId ? { 'X-Workspace-Id': hints.orgId } : {}),
|
|
18
19
|
...(hints?.resource ? { 'X-Ziggs-Resource': hints.resource } : {}),
|
|
19
20
|
...(hints?.clientSessionId ? { 'X-Ziggs-Session': hints.clientSessionId } : {}),
|
|
@@ -23,73 +24,48 @@ export function assistantRequestHeaders() {
|
|
|
23
24
|
...(hints?.sessionName ? { 'X-Ziggs-Session-Name': hints.sessionName } : {}),
|
|
24
25
|
};
|
|
25
26
|
}
|
|
26
|
-
/**
|
|
27
|
-
* Auth headers for operator-key HTTP clients.
|
|
28
|
-
*
|
|
29
|
-
* ONE place builds them, because the lane is the header a caller cannot be
|
|
30
|
-
* trusted to remember. Omitting `X-Ziggs-Lane` never fails: the server fences
|
|
31
|
-
* the wake to the agent's own org instead, so a door that forgets it answers
|
|
32
|
-
* short lists and refuses grants the agent's customer gave it — silently, and
|
|
33
|
-
* only for the agents that serve someone other than their owner. A header
|
|
34
|
-
* whose absence is indistinguishable from a legitimate answer has to be built
|
|
35
|
-
* in one place, not copied into each caller.
|
|
36
|
-
*/
|
|
37
|
-
/** The header the backend's wake fence reads (`access/wake-fence.ts`). */
|
|
38
|
-
export const WAKE_LANE_HEADER = 'X-Ziggs-Lane';
|
|
39
|
-
/**
|
|
40
|
-
* The header naming the agreement a room wake works under. The backend takes
|
|
41
|
-
* it only for an agreement the agent has mail under in that room, and reads
|
|
42
|
-
* it off that mail when it is absent.
|
|
43
|
-
*/
|
|
44
|
-
export const WAKE_AGREEMENT_HEADER = 'X-Ziggs-Agreement';
|
|
45
|
-
/**
|
|
46
|
-
* A room lane that also names the agreement the wake works under:
|
|
47
|
-
* `<chatId>@<agreementId>`. Every client already carries the lane to
|
|
48
|
-
* {@link laneHeader}, so the agreement travels with it without a second
|
|
49
|
-
* parameter on each of them. An `agrn-` lane already is its agreement.
|
|
50
|
-
*/
|
|
27
|
+
/** A presentation routing key; it carries no authority. */
|
|
51
28
|
export function laneUnderAgreement(laneId, agreementId) {
|
|
52
29
|
if (!agreementId || laneId.startsWith('agrn-') || laneId.includes('@'))
|
|
53
30
|
return laneId;
|
|
54
31
|
return `${laneId}@${agreementId}`;
|
|
55
32
|
}
|
|
56
|
-
|
|
57
|
-
* The lane headers, or nothing. `laneId` is a chat id, `agrn-<agreementId>`
|
|
58
|
-
* for a task with no origin chat, or a chat id with the agreement the wake
|
|
59
|
-
* works under (`<chatId>@<agreementId>`, see {@link laneUnderAgreement}).
|
|
60
|
-
*/
|
|
61
|
-
export function laneHeader(laneId) {
|
|
62
|
-
if (!laneId)
|
|
63
|
-
return {};
|
|
64
|
-
const at = laneId.indexOf('@');
|
|
65
|
-
if (at <= 0)
|
|
66
|
-
return { [WAKE_LANE_HEADER]: laneId };
|
|
67
|
-
return {
|
|
68
|
-
[WAKE_LANE_HEADER]: laneId.slice(0, at),
|
|
69
|
-
[WAKE_AGREEMENT_HEADER]: laneId.slice(at + 1),
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
export function buildOperatorHeaders(operatorKey, agentId, extra,
|
|
73
|
-
/**
|
|
74
|
-
* the lane this call belongs to, sent as `X-Ziggs-Lane`. The
|
|
75
|
-
* backend narrows the wake's reach to the engagement's orgs; omitting it
|
|
76
|
-
* narrows to the agent's own org, so it can never widen reach.
|
|
77
|
-
*/
|
|
78
|
-
laneId) {
|
|
33
|
+
export function buildOperatorHeaders(operatorKey, agentId, extra) {
|
|
79
34
|
return {
|
|
80
35
|
Authorization: `Bearer ${operatorKey}`,
|
|
81
36
|
...assistantRequestHeaders(),
|
|
82
37
|
// `X-Agent-Id` is only sent when an agentId is present — agent-scoped keys
|
|
83
38
|
// identify the agent themselves; fleet keys must pass one.
|
|
84
39
|
...(agentId ? { 'X-Agent-Id': agentId } : {}),
|
|
85
|
-
...laneHeader(laneId),
|
|
86
40
|
...extra,
|
|
87
41
|
};
|
|
88
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* The host's own calls (wake gate, telemetry, tool picker) are not work
|
|
45
|
+
* actions: they never carry the selected execution, so they neither need a
|
|
46
|
+
* route that supports one nor record anything into it.
|
|
47
|
+
*/
|
|
48
|
+
export function withoutExecution(headers) {
|
|
49
|
+
const out = { ...headers };
|
|
50
|
+
delete out['X-Ziggs-Execution'];
|
|
51
|
+
delete out['x-ziggs-execution'];
|
|
52
|
+
return out;
|
|
53
|
+
}
|
|
89
54
|
/** What a `Creds`-shaped client sends on a JSON call: auth, agent, lane. */
|
|
90
55
|
export function buildCredsHeaders(creds) {
|
|
91
56
|
return {
|
|
92
57
|
'content-type': 'application/json',
|
|
93
|
-
...
|
|
58
|
+
...(creds.executionContextId ? { 'X-Ziggs-Execution': creds.executionContextId } : {}),
|
|
59
|
+
...buildOperatorHeaders(creds.operatorKey, creds.agentId),
|
|
94
60
|
};
|
|
95
61
|
}
|
|
62
|
+
/** Async-local scope: parallel agreement positions never share request headers. */
|
|
63
|
+
export async function withWorkExecution(executionContext, run) {
|
|
64
|
+
return withAssistantRequest({ ...assistantRequest?.getStore(), executionContext, executionContextId: executionContext?.contextId }, run);
|
|
65
|
+
}
|
|
66
|
+
export function currentWorkExecution() {
|
|
67
|
+
return assistantRequest?.getStore()?.executionContext;
|
|
68
|
+
}
|
|
69
|
+
export async function withWorkExecutionId(executionContextId, run) {
|
|
70
|
+
return withAssistantRequest({ ...assistantRequest?.getStore(), executionContextId: executionContextId ?? assistantRequest?.getStore()?.executionContextId }, run);
|
|
71
|
+
}
|