@ziggs-ai/api-client 0.38.2 → 0.39.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 +21 -0
- package/README.md +14 -36
- package/dist/capabilities/agreementVerbs.js +13 -21
- package/dist/capabilities/agreements.d.ts +2 -3
- package/dist/capabilities/agreements.js +7 -42
- package/dist/capabilities/artifacts.js +41 -43
- package/dist/capabilities/connections.js +10 -7
- package/dist/capabilities/context.d.ts +1 -1
- package/dist/capabilities/context.js +41 -45
- package/dist/capabilities/discovery.js +5 -5
- package/dist/capabilities/everyday.d.ts +1 -1
- package/dist/capabilities/everyday.js +1 -2
- package/dist/capabilities/index.d.ts +1 -2
- package/dist/capabilities/index.js +1 -2
- package/dist/capabilities/introductions.d.ts +1 -1
- package/dist/capabilities/introductions.js +3 -11
- package/dist/capabilities/links.d.ts +1 -9
- package/dist/capabilities/links.js +7 -28
- package/dist/capabilities/mcpBriefs.js +22 -28
- package/dist/capabilities/messaging.d.ts +9 -0
- package/dist/capabilities/messaging.js +11 -0
- package/dist/capabilities/nextCall.js +8 -17
- package/dist/capabilities/sources.d.ts +2 -3
- package/dist/capabilities/sources.js +1 -2
- package/dist/capabilities/tasks.js +1 -1
- package/dist/capabilities/toolLane.js +2 -2
- package/dist/engagementGuide.d.ts +2 -2
- package/dist/engagementGuide.js +6 -6
- package/dist/handles.d.ts +1 -1
- package/dist/handles.js +2 -2
- package/dist/http/AgreementClient.d.ts +17 -64
- package/dist/http/AgreementClient.js +14 -65
- package/dist/http/ArtifactsClient.d.ts +17 -19
- package/dist/http/ArtifactsClient.js +30 -62
- package/dist/http/ConnectionsClient.d.ts +6 -7
- package/dist/http/ConnectionsClient.js +5 -3
- package/dist/http/ContextDiscoveryClient.d.ts +2 -2
- package/dist/http/ContextGrantsClient.d.ts +8 -15
- package/dist/http/ContextGrantsClient.js +3 -5
- package/dist/http/ContextOpenClient.d.ts +5 -6
- package/dist/http/ContextOpenClient.js +11 -4
- package/dist/http/ContextReadClient.d.ts +23 -27
- package/dist/http/ContextReadClient.js +24 -23
- package/dist/http/ExecutionContextClient.d.ts +5 -1
- package/dist/http/ExecutionContextClient.js +48 -1
- package/dist/http/IntroductionsClient.d.ts +6 -4
- package/dist/http/MessageRecordsClient.d.ts +36 -0
- package/dist/http/MessageRecordsClient.js +30 -0
- package/dist/http/SessionsClient.d.ts +5 -5
- package/dist/http/SessionsClient.js +6 -6
- package/dist/http/TaskClient.d.ts +11 -5
- package/dist/http/TaskClient.js +11 -34
- package/dist/http/agreementFlows.d.ts +2 -3
- package/dist/http/agreementFlows.js +5 -5
- package/dist/http/index.d.ts +3 -5
- package/dist/http/index.js +3 -4
- package/dist/http/operatorHeaders.d.ts +3 -0
- package/dist/http/operatorHeaders.js +14 -5
- package/dist/inboxAck.d.ts +2 -2
- package/dist/inboxAck.js +2 -2
- package/dist/index.d.ts +4 -4
- package/dist/index.js +2 -2
- package/dist/types.d.ts +15 -55
- package/package.json +2 -2
- package/dist/capabilities/chat.d.ts +0 -43
- package/dist/capabilities/chat.js +0 -196
- package/dist/capabilities/chatListFields.d.ts +0 -14
- package/dist/capabilities/chatListFields.js +0 -61
- package/dist/http/ChatClient.d.ts +0 -187
- package/dist/http/ChatClient.js +0 -219
- package/dist/http/MessagesClient.d.ts +0 -40
- package/dist/http/MessagesClient.js +0 -81
|
@@ -472,6 +472,10 @@ export async function getAgreement(agreementId, creds) {
|
|
|
472
472
|
const agreement = await getAgreementDocument(agreementId, creds);
|
|
473
473
|
return agreement ? shapeAgreement(agreement) : null;
|
|
474
474
|
}
|
|
475
|
+
function writeSuccess(data) {
|
|
476
|
+
const success = data['success'] === true || (data['success'] === undefined && data['ok'] === true);
|
|
477
|
+
return { success, ok: success };
|
|
478
|
+
}
|
|
475
479
|
/**
|
|
476
480
|
* Connect with someone — by email, by agent id, or with a shareable invite.
|
|
477
481
|
*
|
|
@@ -496,12 +500,12 @@ export async function createLink(body, creds) {
|
|
|
496
500
|
}
|
|
497
501
|
const data = await res.json().catch(() => null);
|
|
498
502
|
if (data?.['status'] !== 'sent') {
|
|
499
|
-
throw new Error('Invalid response: expected {
|
|
503
|
+
throw new Error('Invalid response: expected { success, status: "sent", shareUrl } from POST /agreements/links');
|
|
500
504
|
}
|
|
501
505
|
const shareUrl = data['shareUrl'];
|
|
502
506
|
const agreementId = data['agreementId'];
|
|
503
507
|
return {
|
|
504
|
-
|
|
508
|
+
...writeSuccess(data),
|
|
505
509
|
status: 'sent',
|
|
506
510
|
agreementId: typeof agreementId === 'string' ? agreementId : null,
|
|
507
511
|
shareUrl: typeof shareUrl === 'string' ? shareUrl : null,
|
|
@@ -511,8 +515,7 @@ export async function revokeAgreement(agreementId, creds) {
|
|
|
511
515
|
if (!agreementId)
|
|
512
516
|
throw new Error('agreementId is required for revocation');
|
|
513
517
|
assertCreds(creds, 'agreement revocation');
|
|
514
|
-
//
|
|
515
|
-
// serving the identical handler (with Deprecation headers) until PR-F.
|
|
518
|
+
// Revocation uses the resource DELETE operation.
|
|
516
519
|
const res = await fetch(`${getAgreementBaseUrl()}/${encodeURIComponent(agreementId)}`, {
|
|
517
520
|
method: 'DELETE',
|
|
518
521
|
headers: buildHeaders(creds),
|
|
@@ -523,10 +526,10 @@ export async function revokeAgreement(agreementId, creds) {
|
|
|
523
526
|
}
|
|
524
527
|
const data = await res.json().catch(() => null);
|
|
525
528
|
if (!data?.['agreement']) {
|
|
526
|
-
throw new Error('Invalid response: expected {
|
|
529
|
+
throw new Error('Invalid response: expected { success, agreement } from DELETE /agreements/:id');
|
|
527
530
|
}
|
|
528
531
|
const envelope = data;
|
|
529
|
-
return { ...envelope, agreement: shapeAgreement(envelope.agreement) };
|
|
532
|
+
return { ...envelope, ...writeSuccess(data), agreement: shapeAgreement(envelope.agreement) };
|
|
530
533
|
}
|
|
531
534
|
/** mark an agreement fulfilled (complete). A provider closing its own
|
|
532
535
|
* delivered work — party-gated server-side. */
|
|
@@ -544,10 +547,10 @@ export async function fulfillAgreement(agreementId, creds) {
|
|
|
544
547
|
}
|
|
545
548
|
const data = await res.json().catch(() => null);
|
|
546
549
|
if (!data?.['agreement']) {
|
|
547
|
-
throw new Error('Invalid response: expected {
|
|
550
|
+
throw new Error('Invalid response: expected { success, agreement } from POST /agreements/:id/fulfill');
|
|
548
551
|
}
|
|
549
552
|
const envelope = data;
|
|
550
|
-
return { ...envelope, agreement: shapeAgreement(envelope.agreement) };
|
|
553
|
+
return { ...envelope, ...writeSuccess(data), agreement: shapeAgreement(envelope.agreement) };
|
|
551
554
|
}
|
|
552
555
|
export async function claimAgreement(agreementId, creds, opts = {}) {
|
|
553
556
|
if (!agreementId)
|
|
@@ -556,7 +559,7 @@ export async function claimAgreement(agreementId, creds, opts = {}) {
|
|
|
556
559
|
const res = await fetch(`${getAgreementBaseUrl()}/${encodeURIComponent(agreementId)}/claim`, {
|
|
557
560
|
method: 'POST',
|
|
558
561
|
headers: buildHeaders(creds),
|
|
559
|
-
body: JSON.stringify({ ...(opts.mandateAgreementId ? { mandateAgreementId: opts.mandateAgreementId } : {}), ...(opts.parentAgreementId ? { parentAgreementId: opts.parentAgreementId } : {}), ...(opts.
|
|
562
|
+
body: JSON.stringify({ ...(opts.mandateAgreementId ? { mandateAgreementId: opts.mandateAgreementId } : {}), ...(opts.parentAgreementId ? { parentAgreementId: opts.parentAgreementId } : {}), ...(opts.connections && Object.keys(opts.connections).length ? { connections: opts.connections } : {}), ...(opts.hireFor === 'workspace' ? { hireFor: 'workspace' } : {}) }),
|
|
560
563
|
});
|
|
561
564
|
if (!res.ok) {
|
|
562
565
|
const body = await res.text().catch(() => '');
|
|
@@ -564,66 +567,14 @@ export async function claimAgreement(agreementId, creds, opts = {}) {
|
|
|
564
567
|
}
|
|
565
568
|
const data = await res.json().catch(() => null);
|
|
566
569
|
if (!data?.['agreement']) {
|
|
567
|
-
throw new Error('Invalid response: expected {
|
|
570
|
+
throw new Error('Invalid response: expected { success, agreement } from POST /agreements/:id/claim');
|
|
568
571
|
}
|
|
569
572
|
// the route reports which kind of broadcast this turned out to be.
|
|
570
573
|
// A caller cannot work it out from the row — post-claim no sentinel is left,
|
|
571
574
|
// and a request (you do the work) reads the same shape as a standing offer (you
|
|
572
575
|
// pay for it) unless you know which slot you landed in.
|
|
573
576
|
const envelope = data;
|
|
574
|
-
return { ...envelope, agreement: shapeAgreement(envelope.agreement) };
|
|
575
|
-
}
|
|
576
|
-
// ---------------------------------------------------------------------------
|
|
577
|
-
// Chat links
|
|
578
|
-
// ---------------------------------------------------------------------------
|
|
579
|
-
/**
|
|
580
|
-
* Link types a caller may ask for. `origin` and `delegation` are workflow-owned;
|
|
581
|
-
* callers may only pin an agreement into a chat as a reference (`mention`).
|
|
582
|
-
*/
|
|
583
|
-
export const CHAT_LINK_TYPES = ['mention'];
|
|
584
|
-
export async function linkAgreementToChat(agreementId, chatId, linkType = 'mention', creds) {
|
|
585
|
-
if (!agreementId || !chatId)
|
|
586
|
-
return null;
|
|
587
|
-
assertCreds(creds, 'link agreement to chat');
|
|
588
|
-
try {
|
|
589
|
-
const res = await fetch(`${getAgreementBaseUrl()}/${agreementId}/link`, {
|
|
590
|
-
method: 'POST',
|
|
591
|
-
headers: buildHeaders(creds),
|
|
592
|
-
body: JSON.stringify({ chatId, linkType }),
|
|
593
|
-
});
|
|
594
|
-
if (!res.ok) {
|
|
595
|
-
const body = await res.text().catch(() => '');
|
|
596
|
-
runtimeLog.warn('AgreementClient', `⚠️ Link agreement to chat failed: ${res.status} ${res.statusText} ${body?.slice(0, 200)}`);
|
|
597
|
-
return null;
|
|
598
|
-
}
|
|
599
|
-
return await res.json().catch(() => null);
|
|
600
|
-
}
|
|
601
|
-
catch (e) {
|
|
602
|
-
runtimeLog.warn('AgreementClient', `⚠️ Link agreement to chat failed: ${e.message}`);
|
|
603
|
-
return null;
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
export async function getChatsForAgreement(agreementId, creds) {
|
|
607
|
-
if (!agreementId)
|
|
608
|
-
return [];
|
|
609
|
-
assertCreds(creds, 'get chats for agreement');
|
|
610
|
-
try {
|
|
611
|
-
const res = await fetch(`${getAgreementBaseUrl()}/${agreementId}/chats`, {
|
|
612
|
-
method: 'GET',
|
|
613
|
-
headers: buildHeaders(creds),
|
|
614
|
-
});
|
|
615
|
-
if (!res.ok) {
|
|
616
|
-
const body = await res.text().catch(() => '');
|
|
617
|
-
runtimeLog.warn('AgreementClient', `⚠️ Get chats for agreement failed: ${res.status} ${res.statusText} ${body?.slice(0, 200)}`);
|
|
618
|
-
return [];
|
|
619
|
-
}
|
|
620
|
-
const data = await res.json().catch(() => null);
|
|
621
|
-
return Array.isArray(data?.['links']) ? data['links'] : [];
|
|
622
|
-
}
|
|
623
|
-
catch (e) {
|
|
624
|
-
runtimeLog.warn('AgreementClient', `⚠️ Get chats for agreement failed: ${e.message}`);
|
|
625
|
-
return [];
|
|
626
|
-
}
|
|
577
|
+
return { ...envelope, ...writeSuccess(data), agreement: shapeAgreement(envelope.agreement) };
|
|
627
578
|
}
|
|
628
579
|
// ---------------------------------------------------------------------------
|
|
629
580
|
// Artifact links
|
|
@@ -701,8 +652,6 @@ export class AgreementClient {
|
|
|
701
652
|
revoke(id) { return revokeAgreement(id, this.creds); }
|
|
702
653
|
fulfill(id) { return fulfillAgreement(id, this.creds); }
|
|
703
654
|
claimAgreement(id) { return claimAgreement(id, this.creds); }
|
|
704
|
-
linkToChat(id, chatId, linkType) { return linkAgreementToChat(id, chatId, linkType ?? 'mention', this.creds); }
|
|
705
|
-
listChats(id) { return getChatsForAgreement(id, this.creds); }
|
|
706
655
|
linkArtifact(id, artifactId, linkType) { return linkArtifactToAgreement(id, artifactId, linkType ?? 'produced', this.creds); }
|
|
707
656
|
listArtifacts(id) { return getArtifactsForAgreement(id, this.creds); }
|
|
708
657
|
}
|
|
@@ -8,13 +8,12 @@ import type { ListReachableArtifactsResult } from '@ziggs-ai/contracts';
|
|
|
8
8
|
export declare const ARTIFACT_INLINE_TEXT_MAX_CHARS = 50000;
|
|
9
9
|
/** Named escape hatch for agents that hit the inline cap. */
|
|
10
10
|
export declare const ARTIFACT_INLINE_TEXT_OVER_LIMIT_HINT: string;
|
|
11
|
-
export type ArtifactVisibility = '
|
|
11
|
+
export type ArtifactVisibility = 'address' | 'agent-private';
|
|
12
12
|
export interface ListArtifactsOptions {
|
|
13
13
|
after?: string;
|
|
14
14
|
limit?: number;
|
|
15
15
|
}
|
|
16
16
|
export interface ListArtifactsQuery {
|
|
17
|
-
chatId?: string;
|
|
18
17
|
agreementId?: string;
|
|
19
18
|
taskId?: string;
|
|
20
19
|
serviceAgreementId?: string;
|
|
@@ -73,7 +72,6 @@ export interface WriteArtifactInput {
|
|
|
73
72
|
contentType?: string;
|
|
74
73
|
visibility?: ArtifactVisibility;
|
|
75
74
|
/** When the artifact is associated with a chat. */
|
|
76
|
-
chatId?: string;
|
|
77
75
|
/** When the artifact is associated with an agreement. */
|
|
78
76
|
agreementId?: string;
|
|
79
77
|
/** Optional task binding — creates a TaskArtifactLink alongside the primary scope link. */
|
|
@@ -106,7 +104,6 @@ export interface ArtifactUploadUrlInput {
|
|
|
106
104
|
byteSize: number;
|
|
107
105
|
format?: ArtifactFileFormat;
|
|
108
106
|
visibility?: ArtifactVisibility;
|
|
109
|
-
chatId?: string;
|
|
110
107
|
agreementId?: string;
|
|
111
108
|
taskId?: string;
|
|
112
109
|
serviceAgreementId?: string;
|
|
@@ -136,12 +133,18 @@ export type ArtifactFileView = Record<string, unknown> & {
|
|
|
136
133
|
extractionError?: string | null;
|
|
137
134
|
filename?: string | null;
|
|
138
135
|
};
|
|
136
|
+
/**
|
|
137
|
+
* Replaces `ContextReader`'s artifact reads + `ContextWriter`'s breadcrumb
|
|
138
|
+
* writes. `visibility: 'agent-private'` is the new home for agent thoughts —
|
|
139
|
+
* they persist, are searchable, but are not visible to other chat parties.
|
|
140
|
+
*/
|
|
141
|
+
/** Artifact scope is explicit selected work. */
|
|
139
142
|
import type { ArtifactExternalRef, ArtifactFileFormat } from '@ziggs-ai/contracts';
|
|
140
143
|
export { AGREEMENT_LANE_PREFIX } from '@ziggs-ai/contracts';
|
|
141
|
-
export declare function
|
|
142
|
-
|
|
143
|
-
}
|
|
144
|
-
agreementId
|
|
144
|
+
export declare function artifactScope(target?: {
|
|
145
|
+
agreementId?: string;
|
|
146
|
+
}): {
|
|
147
|
+
agreementId?: string;
|
|
145
148
|
};
|
|
146
149
|
export declare class ArtifactsClient {
|
|
147
150
|
private readonly operatorKey;
|
|
@@ -158,7 +161,7 @@ export declare class ArtifactsClient {
|
|
|
158
161
|
* This is not `list()` with a query string. `list()` answers "what is in this
|
|
159
162
|
* container"; a person's cross-container view lives behind `reach` arms that
|
|
160
163
|
* an agent has no business asking for. The server refuses `reach`,
|
|
161
|
-
* `authoredBy` and `
|
|
164
|
+
* `authoredBy` and `addressId` here rather than ignoring them, so a narrowing
|
|
162
165
|
* filter can never be silently dropped — hence no options for them.
|
|
163
166
|
*
|
|
164
167
|
* The result always carries `coverage`. An empty `artifacts` is a statement
|
|
@@ -174,7 +177,7 @@ export declare class ArtifactsClient {
|
|
|
174
177
|
* endpoint is updated to accept `visibility`, this falls back to logging
|
|
175
178
|
* locally (operators can still wire their own sink).
|
|
176
179
|
*/
|
|
177
|
-
recordThought(
|
|
180
|
+
recordThought(text: string, opts?: {
|
|
178
181
|
idempotencyKey?: string;
|
|
179
182
|
serviceAgreementId?: string;
|
|
180
183
|
}): Promise<void>;
|
|
@@ -190,16 +193,12 @@ export declare class ArtifactsClient {
|
|
|
190
193
|
artifactId?: string;
|
|
191
194
|
}>;
|
|
192
195
|
/**
|
|
193
|
-
* attach an existing artifact to a
|
|
196
|
+
* attach an existing artifact to a task or agreement.
|
|
194
197
|
*
|
|
195
|
-
*
|
|
196
|
-
*
|
|
197
|
-
*
|
|
198
|
-
* reaches anyone without granting it to one specific agent.
|
|
198
|
+
* A chat link is a presentation reference, never an access grant. Task and
|
|
199
|
+
* agreement associations use their work authority and source-disclosure policy.
|
|
200
|
+
* Explicit artifact sharing grants access to the selected holder.
|
|
199
201
|
*/
|
|
200
|
-
attachToChat(artifactId: string, chatId: string): Promise<{
|
|
201
|
-
success: boolean;
|
|
202
|
-
}>;
|
|
203
202
|
attachToTask(artifactId: string, taskId: string, role?: 'input' | 'output'): Promise<{
|
|
204
203
|
success: boolean;
|
|
205
204
|
}>;
|
|
@@ -246,6 +245,5 @@ export declare class ArtifactsClient {
|
|
|
246
245
|
* binds it to a task. Both containers at once is still a mistake worth naming,
|
|
247
246
|
* since only one of them would take effect.
|
|
248
247
|
*/
|
|
249
|
-
private _assertScopeXor;
|
|
250
248
|
private _headers;
|
|
251
249
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { runtimeLog } from '../shared/runtimeLog.js';
|
|
2
2
|
import { throwApiError } from '../shared/apiError.js';
|
|
3
3
|
import { getBackendUrl } from '../utils/urlUtils.js';
|
|
4
|
-
import { buildOperatorHeaders } from './operatorHeaders.js';
|
|
4
|
+
import { buildOperatorHeaders, currentWorkExecution } from './operatorHeaders.js';
|
|
5
5
|
/**
|
|
6
6
|
* Inline `POST /artifacts` body cap (matches backend
|
|
7
7
|
* `ARTIFACT_PUBLIC_TEXT_MAX_CHARS`). Over this → file rail or multi-part; the
|
|
@@ -26,28 +26,22 @@ function resolveUploadBytes(input) {
|
|
|
26
26
|
}
|
|
27
27
|
return null;
|
|
28
28
|
}
|
|
29
|
-
/**
|
|
30
|
-
* Replaces `ContextReader`'s artifact reads + `ContextWriter`'s breadcrumb
|
|
31
|
-
* writes. `visibility: 'agent-private'` is the new home for agent thoughts —
|
|
32
|
-
* they persist, are searchable, but are not visible to other chat parties.
|
|
33
|
-
*/
|
|
34
|
-
/**
|
|
35
|
-
* turn a runtime lane id into a scope the backend can accept.
|
|
36
|
-
*
|
|
37
|
-
* A task with no origin chat runs on the lane `agrn-<agreementId>` (see
|
|
38
|
-
* Agent.laneSessionIdForTask). That is a routing key, not a chat: no such
|
|
39
|
-
* chat row exists, so passing it as `chatId` made every breadcrumb and recorded
|
|
40
|
-
* thought on an agreement lane come back 403 "not authorized for this scope" —
|
|
41
|
-
* a fictional scope reading like an auth failure. The work is agreement-scoped,
|
|
42
|
-
* so say so; same rule settled for deliverables.
|
|
43
|
-
*/
|
|
44
|
-
import { agreementIdFromLane } from '@ziggs-ai/contracts';
|
|
45
29
|
export { AGREEMENT_LANE_PREFIX } from '@ziggs-ai/contracts';
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
30
|
+
function rejectRoomScope(input) {
|
|
31
|
+
if ('addressId' in input)
|
|
32
|
+
throw new Error('addressId is retired; use explicit task/agreement work or an exact message artifact reference');
|
|
33
|
+
}
|
|
34
|
+
export function artifactScope(target = {}) {
|
|
35
|
+
rejectRoomScope(target);
|
|
36
|
+
const execution = currentWorkExecution();
|
|
37
|
+
if (execution?.agreementId)
|
|
38
|
+
return { agreementId: execution.agreementId };
|
|
39
|
+
if (execution)
|
|
40
|
+
return {};
|
|
41
|
+
if (target.agreementId)
|
|
42
|
+
return { agreementId: target.agreementId };
|
|
43
|
+
// An unattached artifact is private to its owner; a container is optional.
|
|
44
|
+
return {};
|
|
51
45
|
}
|
|
52
46
|
export class ArtifactsClient {
|
|
53
47
|
operatorKey;
|
|
@@ -60,13 +54,12 @@ export class ArtifactsClient {
|
|
|
60
54
|
this.agentId = agentId;
|
|
61
55
|
}
|
|
62
56
|
async list(q, opts = {}) {
|
|
63
|
-
|
|
57
|
+
rejectRoomScope(q);
|
|
58
|
+
const selectors = [q.agreementId, q.taskId, q.authoredBy].filter(Boolean);
|
|
64
59
|
if (selectors.length !== 1) {
|
|
65
|
-
throw new Error('ArtifactsClient.list: pass exactly one of
|
|
60
|
+
throw new Error('ArtifactsClient.list: pass exactly one of agreementId, taskId, or authoredBy');
|
|
66
61
|
}
|
|
67
62
|
const url = new URL(`${getBackendUrl()}/artifacts`);
|
|
68
|
-
if (q.chatId)
|
|
69
|
-
url.searchParams.set('chatId', q.chatId);
|
|
70
63
|
if (q.agreementId)
|
|
71
64
|
url.searchParams.set('agreementId', q.agreementId);
|
|
72
65
|
if (q.taskId)
|
|
@@ -93,7 +86,7 @@ export class ArtifactsClient {
|
|
|
93
86
|
* This is not `list()` with a query string. `list()` answers "what is in this
|
|
94
87
|
* container"; a person's cross-container view lives behind `reach` arms that
|
|
95
88
|
* an agent has no business asking for. The server refuses `reach`,
|
|
96
|
-
* `authoredBy` and `
|
|
89
|
+
* `authoredBy` and `addressId` here rather than ignoring them, so a narrowing
|
|
97
90
|
* filter can never be silently dropped — hence no options for them.
|
|
98
91
|
*
|
|
99
92
|
* The result always carries `coverage`. An empty `artifacts` is a statement
|
|
@@ -126,10 +119,9 @@ export class ArtifactsClient {
|
|
|
126
119
|
* endpoint is updated to accept `visibility`, this falls back to logging
|
|
127
120
|
* locally (operators can still wire their own sink).
|
|
128
121
|
*/
|
|
129
|
-
async recordThought(
|
|
130
|
-
// `sessionId` may be an agreement lane, which is not a chat.
|
|
122
|
+
async recordThought(text, opts = {}) {
|
|
131
123
|
return this.write({
|
|
132
|
-
...
|
|
124
|
+
...artifactScope({ agreementId: opts.serviceAgreementId }),
|
|
133
125
|
text,
|
|
134
126
|
contentType: 'thought',
|
|
135
127
|
visibility: 'agent-private',
|
|
@@ -138,9 +130,9 @@ export class ArtifactsClient {
|
|
|
138
130
|
});
|
|
139
131
|
}
|
|
140
132
|
async write(input) {
|
|
133
|
+
rejectRoomScope(input);
|
|
141
134
|
if (!input.text || !input.text.trim())
|
|
142
135
|
return;
|
|
143
|
-
this._assertScopeXor(input);
|
|
144
136
|
try {
|
|
145
137
|
await this.writeStrict(input);
|
|
146
138
|
}
|
|
@@ -161,7 +153,7 @@ export class ArtifactsClient {
|
|
|
161
153
|
if (!input.text || !input.text.trim()) {
|
|
162
154
|
throw new Error('ArtifactsClient.writeStrict: text is required');
|
|
163
155
|
}
|
|
164
|
-
|
|
156
|
+
rejectRoomScope(input);
|
|
165
157
|
const text = input.text.trim();
|
|
166
158
|
// Refuse before the wire so MCP/SDK get a named escape hatch
|
|
167
159
|
// instead of a bare class-validator string.
|
|
@@ -176,8 +168,7 @@ export class ArtifactsClient {
|
|
|
176
168
|
text,
|
|
177
169
|
...(input.name?.trim() ? { name: input.name.trim() } : {}),
|
|
178
170
|
contentType: input.contentType ?? 'text',
|
|
179
|
-
visibility: input.visibility ?? '
|
|
180
|
-
chatId: input.chatId,
|
|
171
|
+
visibility: input.visibility ?? 'address',
|
|
181
172
|
agreementId: input.agreementId,
|
|
182
173
|
taskId: input.taskId,
|
|
183
174
|
serviceAgreementId: input.serviceAgreementId,
|
|
@@ -202,18 +193,12 @@ export class ArtifactsClient {
|
|
|
202
193
|
}
|
|
203
194
|
}
|
|
204
195
|
/**
|
|
205
|
-
* attach an existing artifact to a
|
|
196
|
+
* attach an existing artifact to a task or agreement.
|
|
206
197
|
*
|
|
207
|
-
*
|
|
208
|
-
*
|
|
209
|
-
*
|
|
210
|
-
* reaches anyone without granting it to one specific agent.
|
|
198
|
+
* A chat link is a presentation reference, never an access grant. Task and
|
|
199
|
+
* agreement associations use their work authority and source-disclosure policy.
|
|
200
|
+
* Explicit artifact sharing grants access to the selected holder.
|
|
211
201
|
*/
|
|
212
|
-
async attachToChat(artifactId, chatId) {
|
|
213
|
-
return this._attach(`/chats/${encodeURIComponent(chatId)}/artifacts`, {
|
|
214
|
-
artifactId,
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
202
|
async attachToTask(artifactId, taskId, role = 'output') {
|
|
218
203
|
return this._attach(`/tasks/${encodeURIComponent(taskId)}/artifacts`, {
|
|
219
204
|
artifactId,
|
|
@@ -242,6 +227,7 @@ export class ArtifactsClient {
|
|
|
242
227
|
* S3 PUT here and returns a sha256 so complete can follow immediately.
|
|
243
228
|
*/
|
|
244
229
|
async uploadUrl(input) {
|
|
230
|
+
rejectRoomScope(input);
|
|
245
231
|
if (!input.filename?.trim()) {
|
|
246
232
|
throw new Error('ArtifactsClient.uploadUrl: filename is required');
|
|
247
233
|
}
|
|
@@ -251,11 +237,6 @@ export class ArtifactsClient {
|
|
|
251
237
|
if (!Number.isInteger(input.byteSize) || input.byteSize < 1) {
|
|
252
238
|
throw new Error('ArtifactsClient.uploadUrl: byteSize must be a positive integer');
|
|
253
239
|
}
|
|
254
|
-
this._assertScopeXor({
|
|
255
|
-
text: '.',
|
|
256
|
-
chatId: input.chatId,
|
|
257
|
-
agreementId: input.agreementId,
|
|
258
|
-
});
|
|
259
240
|
const bytes = resolveUploadBytes(input);
|
|
260
241
|
if (bytes && bytes.byteLength !== input.byteSize) {
|
|
261
242
|
throw new Error(`ArtifactsClient.uploadUrl: byteSize ${input.byteSize} does not match content length ${bytes.byteLength}`);
|
|
@@ -267,7 +248,6 @@ export class ArtifactsClient {
|
|
|
267
248
|
byteSize: input.byteSize,
|
|
268
249
|
format: input.format,
|
|
269
250
|
visibility: input.visibility,
|
|
270
|
-
chatId: input.chatId,
|
|
271
251
|
agreementId: input.agreementId,
|
|
272
252
|
taskId: input.taskId,
|
|
273
253
|
serviceAgreementId: input.serviceAgreementId,
|
|
@@ -356,18 +336,6 @@ export class ArtifactsClient {
|
|
|
356
336
|
* binds it to a task. Both containers at once is still a mistake worth naming,
|
|
357
337
|
* since only one of them would take effect.
|
|
358
338
|
*/
|
|
359
|
-
_assertScopeXor(input) {
|
|
360
|
-
if (input.chatId && input.agreementId) {
|
|
361
|
-
// the refusal names the recovery, because this is the one gate
|
|
362
|
-
// for every caller — the exposed tool surfaces inherit it rather than each
|
|
363
|
-
// wording their own. Wording matters: this fires at the last step of a
|
|
364
|
-
// finished task, where a bare "pick one" leaves the deliverable unfiled.
|
|
365
|
-
throw new Error('pass at most one of chatId or agreementId — a deliverable under an ' +
|
|
366
|
-
'agreement wants agreementId alone (its parties see it); use chatId ' +
|
|
367
|
-
'only for a chat-scoped note. To put it in both places, record it ' +
|
|
368
|
-
'with agreementId, then attach it to the chat.');
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
339
|
_headers() {
|
|
372
340
|
return buildOperatorHeaders(this.operatorKey, this.agentId, { 'content-type': 'application/json' });
|
|
373
341
|
}
|
|
@@ -38,11 +38,10 @@ export interface McpConnectionRequestParams {
|
|
|
38
38
|
* per-user relay may send it as `onBehalfOfUserId` instead.
|
|
39
39
|
*/
|
|
40
40
|
recipientId?: string;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
chatId?: string;
|
|
41
|
+
context: {
|
|
42
|
+
kind: 'message' | 'task' | 'agreement';
|
|
43
|
+
id: string;
|
|
44
|
+
};
|
|
46
45
|
/**
|
|
47
46
|
* Target the consent request at this end-user (X-On-Behalf-Of-User) instead
|
|
48
47
|
* of the operator — the per-user MCP gateway's connect prompt.
|
|
@@ -112,10 +111,10 @@ export declare class ConnectionsClient {
|
|
|
112
111
|
/**
|
|
113
112
|
* agent-initiated MCP connection request: ask the principal to
|
|
114
113
|
* connect a remote MCP server and grant this agent the listed tools. Opens a
|
|
115
|
-
* connection-consent agreement
|
|
114
|
+
* connection-consent agreement for explicit work/message context; on approval the
|
|
116
115
|
* server is connected (if needed) and the agent is granted the tools.
|
|
117
116
|
*/
|
|
118
|
-
requestMcpConnection({ serverUrl, tools, reason,
|
|
117
|
+
requestMcpConnection({ serverUrl, tools, reason, context, recipientId, onBehalfOfUserId, }: McpConnectionRequestParams): Promise<Record<string, unknown>>;
|
|
119
118
|
private _requestRaw;
|
|
120
119
|
private _request;
|
|
121
120
|
}
|
|
@@ -161,13 +161,15 @@ export class ConnectionsClient {
|
|
|
161
161
|
/**
|
|
162
162
|
* agent-initiated MCP connection request: ask the principal to
|
|
163
163
|
* connect a remote MCP server and grant this agent the listed tools. Opens a
|
|
164
|
-
* connection-consent agreement
|
|
164
|
+
* connection-consent agreement for explicit work/message context; on approval the
|
|
165
165
|
* server is connected (if needed) and the agent is granted the tools.
|
|
166
166
|
*/
|
|
167
|
-
async requestMcpConnection({ serverUrl, tools, reason,
|
|
167
|
+
async requestMcpConnection({ serverUrl, tools, reason, context, recipientId, onBehalfOfUserId, }) {
|
|
168
168
|
if (!serverUrl)
|
|
169
169
|
throw new Error("requestMcpConnection: serverUrl is required");
|
|
170
|
-
|
|
170
|
+
if (!context || !["message", "task", "agreement"].includes(context.kind) || !context.id)
|
|
171
|
+
throw new Error("requestMcpConnection: context is required");
|
|
172
|
+
return (await this._request("POST", "/connections/mcp/requests", { serverUrl, tools, reason, context, ...(recipientId ? { recipientId } : {}) }, onBehalfOfUserId
|
|
171
173
|
? { "X-On-Behalf-Of-User": onBehalfOfUserId }
|
|
172
174
|
: undefined));
|
|
173
175
|
}
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
* provider name only (never tokens/account labels).
|
|
5
5
|
*/
|
|
6
6
|
export interface DiscoverableItem {
|
|
7
|
-
type: '
|
|
7
|
+
type: 'address' | 'agreement' | 'connection';
|
|
8
8
|
label: string;
|
|
9
9
|
scopeRef: {
|
|
10
|
-
kind: '
|
|
10
|
+
kind: 'address' | 'agreement' | 'org' | 'connection';
|
|
11
11
|
id: string;
|
|
12
12
|
};
|
|
13
13
|
orgId: string;
|
|
@@ -69,18 +69,16 @@ export interface ReachEntry {
|
|
|
69
69
|
label: string;
|
|
70
70
|
}
|
|
71
71
|
/**
|
|
72
|
-
*
|
|
72
|
+
* Reach expansion — the agreement ids a grant you hold actually
|
|
73
73
|
* covers, so an org/agreement-scoped grant becomes a concrete list you can
|
|
74
|
-
*
|
|
75
|
-
* never content. Org scope is capped; `
|
|
76
|
-
*
|
|
74
|
+
* inspect using the retained resource operations. Ids + labels only,
|
|
75
|
+
* never content. Org scope is capped; `truncatedAgreements`
|
|
76
|
+
* reports how many were left off.
|
|
77
77
|
*/
|
|
78
78
|
export interface GrantReachResult {
|
|
79
79
|
grantId: string;
|
|
80
80
|
scope: ContextGrantScope;
|
|
81
|
-
chats: ReachEntry[];
|
|
82
81
|
agreements: ReachEntry[];
|
|
83
|
-
truncatedChats: number;
|
|
84
82
|
truncatedAgreements: number;
|
|
85
83
|
}
|
|
86
84
|
/**
|
|
@@ -98,14 +96,13 @@ export declare class ContextGrantsClient {
|
|
|
98
96
|
issueGrant(input: IssueContextGrantInput): Promise<GrantView>;
|
|
99
97
|
/** Request-only endpoint: a successful call must still have granted no access. */
|
|
100
98
|
requestAccess(input: {
|
|
101
|
-
scopeKind: 'artifact' | '
|
|
99
|
+
scopeKind: 'message' | 'artifact' | 'address' | 'agreement' | 'task' | 'person';
|
|
102
100
|
scopeId?: string;
|
|
103
101
|
agreementId?: string;
|
|
104
102
|
reason: string;
|
|
105
103
|
permission?: 'read' | 'reply' | 'work' | 'list';
|
|
106
104
|
temporal?: 'from-now' | 'from-start';
|
|
107
105
|
durationHours?: number;
|
|
108
|
-
chatId?: string;
|
|
109
106
|
ownerId?: string;
|
|
110
107
|
/** Who to ask, from the people the server offers this wake. */
|
|
111
108
|
recipientId?: string;
|
|
@@ -121,9 +118,9 @@ export declare class ContextGrantsClient {
|
|
|
121
118
|
idempotencyKey?: string;
|
|
122
119
|
}): Promise<DelegateContextGrantResult>;
|
|
123
120
|
/**
|
|
124
|
-
*
|
|
121
|
+
* Expand a grant you hold into the agreement ids inside its
|
|
125
122
|
* scope. A grant is a fence, not a listing: discovery says "you hold
|
|
126
|
-
* org:acme", this says which
|
|
123
|
+
* org:acme", this says which agreements that covers. Holder-only,
|
|
127
124
|
* labels-only, grant-fenced server-side.
|
|
128
125
|
*/
|
|
129
126
|
getReach(grantId: string): Promise<GrantReachResult>;
|
|
@@ -140,7 +137,6 @@ export declare class ContextGrantsClient {
|
|
|
140
137
|
shareArtifact(artifactId: string, input: {
|
|
141
138
|
holderId: string;
|
|
142
139
|
expiresAt?: string | null;
|
|
143
|
-
chatId?: string;
|
|
144
140
|
}, opts?: {
|
|
145
141
|
idempotencyKey?: string;
|
|
146
142
|
}): Promise<DelegateContextGrantResult>;
|
|
@@ -182,11 +178,8 @@ export interface RequestSentTo {
|
|
|
182
178
|
role: 'customer' | 'operator';
|
|
183
179
|
};
|
|
184
180
|
approveIn?: {
|
|
185
|
-
kind: 'chat';
|
|
186
|
-
chatId: string;
|
|
187
|
-
} | {
|
|
188
181
|
kind: 'inbox';
|
|
189
182
|
};
|
|
190
|
-
/**
|
|
183
|
+
/** Human-readable approval destination. */
|
|
191
184
|
message?: string;
|
|
192
185
|
}
|
|
@@ -114,9 +114,9 @@ export class ContextGrantsClient {
|
|
|
114
114
|
return parseGrantOrPending(body, 'POST /context/grants/:id/delegate');
|
|
115
115
|
}
|
|
116
116
|
/**
|
|
117
|
-
*
|
|
117
|
+
* Expand a grant you hold into the agreement ids inside its
|
|
118
118
|
* scope. A grant is a fence, not a listing: discovery says "you hold
|
|
119
|
-
* org:acme", this says which
|
|
119
|
+
* org:acme", this says which agreements that covers. Holder-only,
|
|
120
120
|
* labels-only, grant-fenced server-side.
|
|
121
121
|
*/
|
|
122
122
|
async getReach(grantId) {
|
|
@@ -127,14 +127,12 @@ export class ContextGrantsClient {
|
|
|
127
127
|
}
|
|
128
128
|
const parsed = JSON.parse(body);
|
|
129
129
|
if (!parsed.scope) {
|
|
130
|
-
throw new Error('Invalid response: expected { scope,
|
|
130
|
+
throw new Error('Invalid response: expected { scope, agreements } from GET /context/grants/:id/reach');
|
|
131
131
|
}
|
|
132
132
|
return {
|
|
133
133
|
grantId: parsed.grantId ?? grantId,
|
|
134
134
|
scope: parsed.scope,
|
|
135
|
-
chats: parsed.chats ?? [],
|
|
136
135
|
agreements: parsed.agreements ?? [],
|
|
137
|
-
truncatedChats: parsed.truncatedChats ?? 0,
|
|
138
136
|
truncatedAgreements: parsed.truncatedAgreements ?? 0,
|
|
139
137
|
};
|
|
140
138
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const CONTEXT_OPEN_KINDS: readonly ["artifact", "
|
|
1
|
+
export declare const CONTEXT_OPEN_KINDS: readonly ["artifact", "task", "agreement"];
|
|
2
2
|
export type ContextOpenKind = (typeof CONTEXT_OPEN_KINDS)[number];
|
|
3
3
|
export type ContextOpenOutcome = 'ok' | 'unreadable' | 'hidden';
|
|
4
4
|
export interface ContextOpenRef {
|
|
@@ -6,10 +6,9 @@ export interface ContextOpenRef {
|
|
|
6
6
|
id: string;
|
|
7
7
|
}
|
|
8
8
|
export interface ContextOpenBody {
|
|
9
|
-
/** A handle (T…/A…/
|
|
9
|
+
/** A handle (T…/A…/F…), a Ziggs URL holding one, or an older id. */
|
|
10
10
|
ref?: string;
|
|
11
11
|
artifactId?: string;
|
|
12
|
-
chatId?: string;
|
|
13
12
|
taskId?: string;
|
|
14
13
|
agreementId?: string;
|
|
15
14
|
cursor?: string;
|
|
@@ -50,10 +49,10 @@ export interface ContextOpenAccess {
|
|
|
50
49
|
kind: 'owner_decision';
|
|
51
50
|
summary: string;
|
|
52
51
|
request?: {
|
|
53
|
-
scopeKind: '
|
|
52
|
+
scopeKind: 'agreement' | 'task';
|
|
54
53
|
scopeId: string;
|
|
55
|
-
permission: 'read'
|
|
56
|
-
temporal: 'from-
|
|
54
|
+
permission: 'read';
|
|
55
|
+
temporal: 'from-start';
|
|
57
56
|
durationHours: number;
|
|
58
57
|
};
|
|
59
58
|
};
|