@ziggs-ai/api-client 0.38.0 → 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.
Files changed (75) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.md +14 -36
  3. package/dist/capabilities/agreementVerbs.js +13 -21
  4. package/dist/capabilities/agreements.d.ts +2 -3
  5. package/dist/capabilities/agreements.js +8 -43
  6. package/dist/capabilities/artifacts.d.ts +6 -0
  7. package/dist/capabilities/artifacts.js +84 -43
  8. package/dist/capabilities/connections.js +10 -7
  9. package/dist/capabilities/context.d.ts +1 -1
  10. package/dist/capabilities/context.js +52 -45
  11. package/dist/capabilities/discovery.js +5 -5
  12. package/dist/capabilities/everyday.d.ts +1 -1
  13. package/dist/capabilities/everyday.js +1 -2
  14. package/dist/capabilities/index.d.ts +3 -4
  15. package/dist/capabilities/index.js +3 -4
  16. package/dist/capabilities/introductions.d.ts +1 -0
  17. package/dist/capabilities/introductions.js +64 -17
  18. package/dist/capabilities/links.d.ts +1 -9
  19. package/dist/capabilities/links.js +40 -39
  20. package/dist/capabilities/mcpBriefs.js +41 -34
  21. package/dist/capabilities/messaging.d.ts +9 -0
  22. package/dist/capabilities/messaging.js +11 -0
  23. package/dist/capabilities/nextCall.js +8 -17
  24. package/dist/capabilities/sources.d.ts +2 -3
  25. package/dist/capabilities/sources.js +1 -2
  26. package/dist/capabilities/tasks.d.ts +1 -0
  27. package/dist/capabilities/tasks.js +43 -2
  28. package/dist/capabilities/toolLane.js +2 -2
  29. package/dist/engagementGuide.d.ts +2 -2
  30. package/dist/engagementGuide.js +6 -6
  31. package/dist/handles.d.ts +1 -1
  32. package/dist/handles.js +2 -2
  33. package/dist/http/AgreementClient.d.ts +25 -63
  34. package/dist/http/AgreementClient.js +14 -65
  35. package/dist/http/ArtifactsClient.d.ts +31 -19
  36. package/dist/http/ArtifactsClient.js +43 -62
  37. package/dist/http/ConnectionsClient.d.ts +6 -7
  38. package/dist/http/ConnectionsClient.js +5 -3
  39. package/dist/http/ContextDiscoveryClient.d.ts +2 -2
  40. package/dist/http/ContextGrantsClient.d.ts +9 -15
  41. package/dist/http/ContextGrantsClient.js +3 -5
  42. package/dist/http/ContextOpenClient.d.ts +5 -6
  43. package/dist/http/ContextOpenClient.js +11 -4
  44. package/dist/http/ContextReadClient.d.ts +23 -27
  45. package/dist/http/ContextReadClient.js +24 -23
  46. package/dist/http/ExecutionContextClient.d.ts +5 -1
  47. package/dist/http/ExecutionContextClient.js +48 -1
  48. package/dist/http/IntroductionsClient.d.ts +33 -4
  49. package/dist/http/IntroductionsClient.js +6 -2
  50. package/dist/http/MessageRecordsClient.d.ts +36 -0
  51. package/dist/http/MessageRecordsClient.js +30 -0
  52. package/dist/http/SessionsClient.d.ts +5 -5
  53. package/dist/http/SessionsClient.js +6 -6
  54. package/dist/http/TaskClient.d.ts +35 -6
  55. package/dist/http/TaskClient.js +38 -36
  56. package/dist/http/agreementFlows.d.ts +2 -3
  57. package/dist/http/agreementFlows.js +5 -5
  58. package/dist/http/index.d.ts +3 -5
  59. package/dist/http/index.js +3 -4
  60. package/dist/http/operatorHeaders.d.ts +3 -0
  61. package/dist/http/operatorHeaders.js +14 -5
  62. package/dist/inboxAck.d.ts +2 -2
  63. package/dist/inboxAck.js +2 -2
  64. package/dist/index.d.ts +4 -4
  65. package/dist/index.js +2 -2
  66. package/dist/types.d.ts +25 -55
  67. package/package.json +2 -2
  68. package/dist/capabilities/chat.d.ts +0 -43
  69. package/dist/capabilities/chat.js +0 -196
  70. package/dist/capabilities/chatListFields.d.ts +0 -14
  71. package/dist/capabilities/chatListFields.js +0 -61
  72. package/dist/http/ChatClient.d.ts +0 -187
  73. package/dist/http/ChatClient.js +0 -219
  74. package/dist/http/MessagesClient.d.ts +0 -40
  75. package/dist/http/MessagesClient.js +0 -81
@@ -43,17 +43,8 @@ export interface ProposeTerms {
43
43
  /** 1:1 proposal to a specific user or agent (`proposedTo` = their id). */
44
44
  export interface ProposeDirectInput extends ProposeTerms {
45
45
  proposedTo: string;
46
- /**
47
- * The room the terms are discussed in, when there is one.
48
- *
49
- * Optional, matching the server, which has always accepted a proposal
50
- * without one. Delivery does not depend on it: the create-time agreement
51
- * event targets the party slots, so the counterparty is told either way; a
52
- * room only gives them somewhere to talk about it. Required here was what
53
- * forced a bookkeeping agreement between parties with no conversation to
54
- * invent a chat id in order to exist.
55
- */
56
- chatId?: string;
46
+ /** Optional display-reference context for resolving a masked party; does not publish or share. */
47
+ addressId?: string;
57
48
  }
58
49
  /**
59
50
  * Open buyer-broadcast. `audience` selects who may see/claim it:
@@ -133,8 +124,6 @@ export interface DelegateAgreementData {
133
124
  /** What this text drew on. See {@link WriteSource}. */
134
125
  sources?: WriteSource[];
135
126
  executorId: string;
136
- /** Omit it and the server uses the parent agreement's own chat. */
137
- chatId?: string;
138
127
  parentAgreementId: string;
139
128
  price?: number;
140
129
  lifecycle?: string;
@@ -258,6 +247,20 @@ export interface CreateLinkBody {
258
247
  description?: string;
259
248
  /** Open invites only: how many people may claim this one link. Default 1. */
260
249
  maxClaims?: number;
250
+ /**
251
+ * A chat, task or agreement you hold. Approving the link lets the person
252
+ * and the agent named in `to` into it; already linked, they are in now.
253
+ * Needs `to`.
254
+ */
255
+ about?: {
256
+ kind: 'agreement' | 'task' | 'address';
257
+ id: string;
258
+ };
259
+ }
260
+ export interface WriteSuccess {
261
+ success: boolean;
262
+ /** @deprecated Use success. */
263
+ ok: boolean;
261
264
  }
262
265
  /**
263
266
  * The route's answer.
@@ -278,8 +281,7 @@ export interface CreateLinkBody {
278
281
  * Without it a caller could not reference what it had just created, check on it
279
282
  * later, or tell a repeat from a first attempt.
280
283
  */
281
- export interface CreateLinkResult {
282
- ok: boolean;
284
+ export interface CreateLinkResult extends WriteSuccess {
283
285
  status: 'sent';
284
286
  /** The link row, for a named agent id or an open invite. Null for an email. */
285
287
  agreementId: string | null;
@@ -295,14 +297,12 @@ export interface CreateLinkResult {
295
297
  * carries links and nothing else.
296
298
  */
297
299
  export declare function createLink(body: CreateLinkBody, creds: Creds): Promise<CreateLinkResult>;
298
- export declare function revokeAgreement(agreementId: string, creds: Creds): Promise<{
299
- ok: boolean;
300
+ export declare function revokeAgreement(agreementId: string, creds: Creds): Promise<WriteSuccess & {
300
301
  agreement: Agreement;
301
302
  }>;
302
303
  /** mark an agreement fulfilled (complete). A provider closing its own
303
304
  * delivered work — party-gated server-side. */
304
- export declare function fulfillAgreement(agreementId: string, creds: Creds): Promise<{
305
- ok: boolean;
305
+ export declare function fulfillAgreement(agreementId: string, creds: Creds): Promise<WriteSuccess & {
306
306
  agreement: Agreement;
307
307
  }>;
308
308
  /** What an open-broadcast claim resolved to. ⚠️ Mirrors the server; it is authoritative. */
@@ -335,11 +335,6 @@ export interface ClaimOptions {
335
335
  * verifies the claim against the acting agent and never guesses one.
336
336
  */
337
337
  mandateAgreementId?: string;
338
- /**
339
- * The room a hire of a listing is made in. On activation its
340
- * agent joins that room; the claimer must take part in it.
341
- */
342
- chatId?: string;
343
338
  /**
344
339
  * Your connection for each kind of access the listing asks for,
345
340
  * `{ <kind>: <connectionId> }`. The hire is granted exactly that connection. A claim made inside a job (mandateAgreementId)
@@ -347,42 +342,15 @@ export interface ClaimOptions {
347
342
  */
348
343
  connections?: Record<string, string>;
349
344
  /**
350
- * Who a hire of a listing is for. `me` (the default) keeps its room to the
351
- * person; `workspace` makes the workspace the payer and shows the room to
352
- * every member.
345
+ * Who a hire is for. `me` keeps the agreement private; `workspace` makes
346
+ * the workspace the payer and shares work with its members.
353
347
  */
354
348
  hireFor?: 'me' | 'workspace';
355
349
  }
356
- /**
357
- * Where a hire made in a room stands on getting its agent in.
358
- * `ready` only once the agent is in; `admission_failed` names the action that
359
- * resumes it without hiring or charging again.
360
- */
361
- export interface ClaimRoom {
362
- chatId?: string;
363
- status: 'awaiting_approval' | 'ready' | 'admission_failed' | 'preparation_failed' | 'unavailable';
364
- message?: string;
365
- recovery?: {
366
- method: string;
367
- path: string;
368
- tool?: string;
369
- args?: Record<string, unknown>;
370
- };
371
- }
372
- export declare function claimAgreement(agreementId: string, creds: Creds, opts?: ClaimOptions): Promise<{
373
- ok: boolean;
350
+ export declare function claimAgreement(agreementId: string, creds: Creds, opts?: ClaimOptions): Promise<WriteSuccess & {
374
351
  agreement: Agreement;
375
352
  kind?: ClaimedKind;
376
- room?: ClaimRoom;
377
353
  }>;
378
- /**
379
- * Link types a caller may ask for. `origin` and `delegation` are workflow-owned;
380
- * callers may only pin an agreement into a chat as a reference (`mention`).
381
- */
382
- export declare const CHAT_LINK_TYPES: readonly ["mention"];
383
- export type ChatLinkType = (typeof CHAT_LINK_TYPES)[number];
384
- export declare function linkAgreementToChat(agreementId: string, chatId: string, linkType: ChatLinkType | undefined, creds: Creds): Promise<unknown | null>;
385
- export declare function getChatsForAgreement(agreementId: string, creds: Creds): Promise<unknown[]>;
386
354
  export declare const ARTIFACT_LINK_TYPES: readonly ["produced", "referenced"];
387
355
  export type ArtifactLinkType = (typeof ARTIFACT_LINK_TYPES)[number];
388
356
  export declare function linkArtifactToAgreement(agreementId: string, artifactId: string, linkType: ArtifactLinkType | undefined, creds: Creds): Promise<unknown | null>;
@@ -408,22 +376,16 @@ export declare class AgreementClient {
408
376
  listMine(filters?: GetMyAgreementsFilters): Promise<Agreement[]>;
409
377
  get(id: string): Promise<Agreement | null>;
410
378
  createLink(data: CreateLinkBody): Promise<CreateLinkResult>;
411
- revoke(id: string): Promise<{
412
- ok: boolean;
379
+ revoke(id: string): Promise<WriteSuccess & {
413
380
  agreement: Agreement;
414
381
  }>;
415
- fulfill(id: string): Promise<{
416
- ok: boolean;
382
+ fulfill(id: string): Promise<WriteSuccess & {
417
383
  agreement: Agreement;
418
384
  }>;
419
- claimAgreement(id: string): Promise<{
420
- ok: boolean;
385
+ claimAgreement(id: string): Promise<WriteSuccess & {
421
386
  agreement: Agreement;
422
387
  kind?: ClaimedKind;
423
- room?: ClaimRoom;
424
388
  }>;
425
- linkToChat(id: string, chatId: string, linkType?: ChatLinkType): Promise<unknown>;
426
- listChats(id: string): Promise<unknown[]>;
427
389
  linkArtifact(id: string, artifactId: string, linkType?: ArtifactLinkType): Promise<unknown>;
428
390
  listArtifacts(id: string): Promise<unknown[]>;
429
391
  }
@@ -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 { ok, status: "sent", shareUrl } from POST /agreements/links');
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
- ok: data['ok'] === true,
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
- // canonical REST verb. Legacy POST /agreements/:id/revoke stays
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 { ok, agreement } from DELETE /agreements/:id');
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 { ok, agreement } from POST /agreements/:id/fulfill');
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.chatId ? { chatId: opts.chatId } : {}), ...(opts.connections && Object.keys(opts.connections).length ? { connections: opts.connections } : {}), ...(opts.hireFor === 'workspace' ? { hireFor: 'workspace' } : {}) }),
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 { ok, agreement } from POST /agreements/:id/claim');
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 = 'chat' | 'agent-private';
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 artifactScopeForSession(sessionId: string): {
142
- chatId: string;
143
- } | {
144
- agreementId: string;
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 `chatId` here rather than ignoring them, so a narrowing
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(sessionId: string, text: string, opts?: {
180
+ recordThought(text: string, opts?: {
178
181
  idempotencyKey?: string;
179
182
  serviceAgreementId?: string;
180
183
  }): Promise<void>;
@@ -190,19 +193,29 @@ export declare class ArtifactsClient {
190
193
  artifactId?: string;
191
194
  }>;
192
195
  /**
193
- * attach an existing artifact to a chat, a task, or an agreement.
196
+ * attach an existing artifact to a task or agreement.
194
197
  *
195
- * Attaching confers nothing on its own: it places the artifact inside the
196
- * container, and that container's audience (chat members / task or agreement
197
- * parties) can read it from then on. This is how a free-standing artifact
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
  }>;
205
+ /** Inspect the exact-output approvals without dropping the source execution. */
206
+ reviewContribution(artifactId: string, agreementId: string): Promise<{
207
+ artifactId: string;
208
+ target: import('@ziggs-ai/contracts').ContributionTarget;
209
+ ready: boolean;
210
+ releaseSources: import('@ziggs-ai/contracts').ContributionSource[];
211
+ privateSourceCount: number;
212
+ }>;
213
+ /** Delivers only this immutable artifact after all required consent is live. */
214
+ contribute(artifactId: string, agreementId: string): Promise<{
215
+ artifactId: string;
216
+ agreementId: string;
217
+ outcome: 'created' | 'existing';
218
+ }>;
206
219
  attachToAgreement(artifactId: string, agreementId: string): Promise<{
207
220
  success: boolean;
208
221
  }>;
@@ -232,6 +245,5 @@ export declare class ArtifactsClient {
232
245
  * binds it to a task. Both containers at once is still a mistake worth naming,
233
246
  * since only one of them would take effect.
234
247
  */
235
- private _assertScopeXor;
236
248
  private _headers;
237
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
- export function artifactScopeForSession(sessionId) {
47
- const agreementId = agreementIdFromLane(sessionId);
48
- if (agreementId)
49
- return { agreementId };
50
- return { chatId: sessionId };
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
- const selectors = [q.chatId, q.agreementId, q.taskId, q.authoredBy].filter(Boolean);
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 chatId, agreementId, taskId, or authoredBy');
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 `chatId` here rather than ignoring them, so a narrowing
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(sessionId, text, opts = {}) {
130
- // `sessionId` may be an agreement lane, which is not a chat.
122
+ async recordThought(text, opts = {}) {
131
123
  return this.write({
132
- ...artifactScopeForSession(sessionId),
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
- this._assertScopeXor(input);
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 ?? 'chat',
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,24 +193,31 @@ export class ArtifactsClient {
202
193
  }
203
194
  }
204
195
  /**
205
- * attach an existing artifact to a chat, a task, or an agreement.
196
+ * attach an existing artifact to a task or agreement.
206
197
  *
207
- * Attaching confers nothing on its own: it places the artifact inside the
208
- * container, and that container's audience (chat members / task or agreement
209
- * parties) can read it from then on. This is how a free-standing artifact
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,
220
205
  role,
221
206
  });
222
207
  }
208
+ /** Inspect the exact-output approvals without dropping the source execution. */
209
+ async reviewContribution(artifactId, agreementId) {
210
+ const url = new URL(`${getBackendUrl()}/artifacts/${encodeURIComponent(artifactId)}/contribution-review`);
211
+ url.searchParams.set('agreementId', agreementId);
212
+ const response = await fetch(url, { headers: this._headers() });
213
+ if (!response.ok)
214
+ throwApiError(response, await response.text(), 'reviewContribution failed');
215
+ return response.json();
216
+ }
217
+ /** Delivers only this immutable artifact after all required consent is live. */
218
+ async contribute(artifactId, agreementId) {
219
+ return this._post(`/agreements/${encodeURIComponent(agreementId)}/contributions`, { artifactId }, 'contributeArtifact');
220
+ }
223
221
  async attachToAgreement(artifactId, agreementId) {
224
222
  return this._attach(`/agreements/${encodeURIComponent(agreementId)}/artifacts`, { artifactId });
225
223
  }
@@ -229,6 +227,7 @@ export class ArtifactsClient {
229
227
  * S3 PUT here and returns a sha256 so complete can follow immediately.
230
228
  */
231
229
  async uploadUrl(input) {
230
+ rejectRoomScope(input);
232
231
  if (!input.filename?.trim()) {
233
232
  throw new Error('ArtifactsClient.uploadUrl: filename is required');
234
233
  }
@@ -238,11 +237,6 @@ export class ArtifactsClient {
238
237
  if (!Number.isInteger(input.byteSize) || input.byteSize < 1) {
239
238
  throw new Error('ArtifactsClient.uploadUrl: byteSize must be a positive integer');
240
239
  }
241
- this._assertScopeXor({
242
- text: '.',
243
- chatId: input.chatId,
244
- agreementId: input.agreementId,
245
- });
246
240
  const bytes = resolveUploadBytes(input);
247
241
  if (bytes && bytes.byteLength !== input.byteSize) {
248
242
  throw new Error(`ArtifactsClient.uploadUrl: byteSize ${input.byteSize} does not match content length ${bytes.byteLength}`);
@@ -254,7 +248,6 @@ export class ArtifactsClient {
254
248
  byteSize: input.byteSize,
255
249
  format: input.format,
256
250
  visibility: input.visibility,
257
- chatId: input.chatId,
258
251
  agreementId: input.agreementId,
259
252
  taskId: input.taskId,
260
253
  serviceAgreementId: input.serviceAgreementId,
@@ -343,18 +336,6 @@ export class ArtifactsClient {
343
336
  * binds it to a task. Both containers at once is still a mistake worth naming,
344
337
  * since only one of them would take effect.
345
338
  */
346
- _assertScopeXor(input) {
347
- if (input.chatId && input.agreementId) {
348
- // the refusal names the recovery, because this is the one gate
349
- // for every caller — the exposed tool surfaces inherit it rather than each
350
- // wording their own. Wording matters: this fires at the last step of a
351
- // finished task, where a bare "pick one" leaves the deliverable unfiled.
352
- throw new Error('pass at most one of chatId or agreementId — a deliverable under an ' +
353
- 'agreement wants agreementId alone (its parties see it); use chatId ' +
354
- 'only for a chat-scoped note. To put it in both places, record it ' +
355
- 'with agreementId, then attach it to the chat.');
356
- }
357
- }
358
339
  _headers() {
359
340
  return buildOperatorHeaders(this.operatorKey, this.agentId, { 'content-type': 'application/json' });
360
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
- * Working chat the consent card is opened into. Required by the backend for
43
- * the agent-tool path; the per-user gateway trigger omits it.
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 in the working chat; on approval the
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, chatId, recipientId, onBehalfOfUserId, }: McpConnectionRequestParams): Promise<Record<string, unknown>>;
117
+ requestMcpConnection({ serverUrl, tools, reason, context, recipientId, onBehalfOfUserId, }: McpConnectionRequestParams): Promise<Record<string, unknown>>;
119
118
  private _requestRaw;
120
119
  private _request;
121
120
  }