@ziggs-ai/api-client 0.29.0 → 0.30.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -424,6 +424,9 @@ export const agreementHandoffCapability = {
424
424
  // would have had to look it up to pass it, and a lookup whose answer is
425
425
  // unique is not a decision worth handing to the caller.
426
426
  const parent = await getAgreement(parentAgreementId, creds);
427
+ if (typeof parent?.publishedService !== 'boolean') {
428
+ throw new Error('Service publishing requires an updated server. No proposal was sent.');
429
+ }
427
430
  const providerId = parent?.parties?.provider?.actor;
428
431
  if (!providerId) {
429
432
  throw new Error(`Cannot hand off ${parentAgreementId}: it names no provider, so there is no hire to share. Check the id with agreement_get.`);
@@ -435,8 +438,8 @@ export const agreementHandoffCapability = {
435
438
  };
436
439
  const to = args['to'];
437
440
  const agreement = to
438
- ? await proposeDirectTo({ ...terms, proposedTo: to, chatId: '', providerId, engagementKind: 'hire' }, creds)
439
- : await proposeBroadcast({ ...terms, chatId: '', audience: 'everyone', providerId, engagementKind: 'hire' }, creds);
441
+ ? await proposeDirectTo({ ...terms, proposedTo: to, chatId: '', useParentProvider: true, engagementKind: 'hire' }, creds)
442
+ : await proposeBroadcast({ ...terms, chatId: '', audience: 'everyone', useParentProvider: true, engagementKind: 'hire' }, creds);
440
443
  return { agreement, ...(to ? {} : { readPlan: publishedNext(env) }) };
441
444
  },
442
445
  sdkOptions: { isAgreementCreation: true },
@@ -1,4 +1,4 @@
1
- import type { ClaimedKind } from '../http/AgreementClient.js';
1
+ import type { ClaimedKind, ClaimRoom } from '../http/AgreementClient.js';
2
2
  import type { Agreement } from '../types.js';
3
3
  import { type NextCallOutcome, type WorkContext } from './nextCall.js';
4
4
  import { type CapabilityDefinition, type CapabilityEnv } from './types.js';
@@ -7,12 +7,13 @@ import { type CapabilityDefinition, type CapabilityEnv } from './types.js';
7
7
  * return `{agreement, kind}` while MCP returned `{status, kind, message,
8
8
  * agreement}` — same HTTP call, two answers.
9
9
  */
10
- export declare function presentClaimResult(agreement: Agreement, kind: ClaimedKind, env: CapabilityEnv): {
10
+ export declare function presentClaimResult(agreement: Agreement, kind: ClaimedKind, env: CapabilityEnv, room?: ClaimRoom): {
11
11
  status: string;
12
12
  outcome: NextCallOutcome;
13
13
  kind: ClaimedKind;
14
14
  message: string;
15
15
  agreement: Agreement;
16
+ room?: ClaimRoom;
16
17
  workContext?: WorkContext;
17
18
  };
18
19
  /**
@@ -3,20 +3,41 @@ import { claimOpenAgreement } from '../http/agreementFlows.js';
3
3
  import { linkIsReachOnly, webAppOrigin } from './links.js';
4
4
  import { workContextFromEnv } from './nextCall.js';
5
5
  import { fullCreds } from './types.js';
6
- /** Which side the claimer just took, for a claim that has not activated yet. */
7
- function claimedWhat(kind) {
6
+ /** Plain outcome once the hire is active. `kind` stays on the result, not in this sentence. */
7
+ function activeHireMessage(kind) {
8
8
  if (kind === 'offer')
9
- return 'Standing offer claimed — the publisher provides, your side pays.';
9
+ return 'The hire is active. They do the work.';
10
10
  if (kind === 'hand-off')
11
- return 'Hand-off claimed — the pinned agent works FOR you.';
12
- return 'Request claimed — you provide the work.';
11
+ return 'The hire is active. Their agent works for you.';
12
+ return 'The hire is active. You do the work.';
13
13
  }
14
14
  /**
15
15
  * One claim result shape for both surfaces. The SDK protocol runner used to
16
16
  * return `{agreement, kind}` while MCP returned `{status, kind, message,
17
17
  * agreement}` — same HTTP call, two answers.
18
18
  */
19
- export function presentClaimResult(agreement, kind, env) {
19
+ export function presentClaimResult(agreement, kind, env, room) {
20
+ const out = presentClaim(agreement, kind, env);
21
+ if (!room)
22
+ return out;
23
+ return { ...out, message: `${out.message} ${roomWords(room)}`, room };
24
+ }
25
+ /**
26
+ * The room the hire was made in, in words. "Ready" is said only
27
+ * when the agent is in; a failure says the hire and its charge stand and names
28
+ * the call that resumes admission.
29
+ */
30
+ function roomWords(room) {
31
+ if (room.status === 'ready') {
32
+ return `The agent is in room ${room.chatId} now.`;
33
+ }
34
+ if (room.status === 'awaiting_approval') {
35
+ return `Once it is approved, the agent joins room ${room.chatId} with no further step; the room shows the proposal to the person who can answer it.`;
36
+ }
37
+ const retry = room.recovery ? ` Resume with ${room.recovery.method} ${room.recovery.path}.` : '';
38
+ return `The hire is active, but the agent is not in room ${room.chatId} yet${room.message ? ` (${room.message})` : ''}. Do not claim again.${retry}`;
39
+ }
40
+ function presentClaim(agreement, kind, env) {
20
41
  const workContext = workContextFromEnv(env);
21
42
  if (kind === 'link') {
22
43
  return {
@@ -31,16 +52,14 @@ export function presentClaimResult(agreement, kind, env) {
31
52
  if (agreement?.status !== 'active') {
32
53
  const held = (agreement?.approvals ?? []).find((a) => a.status === 'pending');
33
54
  const approveUrl = agreementAppUrl(webAppOrigin(env), agreement.agreementId);
34
- const remedy = held?.heldReason === 'contact-basis'
35
- ? 'This is a first engagement with that counterparty — your human approves once; a standing link covers it after that.'
36
- : 'Claiming for a job your human already approved activates at once — name the job with mandateAgreementId.';
55
+ const stranger = held?.heldReason === 'contact-basis'
56
+ ? ' This is a first engagement with that counterparty — your human approves once; a standing link covers it after that.'
57
+ : '';
37
58
  return {
38
59
  status: 'pending_approval',
39
60
  outcome: 'pending',
40
61
  kind,
41
- message: `${claimedWhat(kind)} It is not active yet: the formation is waiting on ` +
42
- 'human approval, so nothing can be created under it until that lands. ' +
43
- `Your human can approve it here: ${approveUrl} — ${remedy}`,
62
+ message: `Hire requested. Waiting for your yes: ${approveUrl}.${stranger}`,
44
63
  agreement,
45
64
  ...(workContext ? { workContext } : {}),
46
65
  };
@@ -49,11 +68,7 @@ export function presentClaimResult(agreement, kind, env) {
49
68
  status: 'claimed',
50
69
  outcome: 'ok',
51
70
  kind,
52
- message: kind === 'offer'
53
- ? 'Standing offer claimed — the publisher provides, your side pays. Spawn work under it with the task-create tool.'
54
- : kind === 'hand-off'
55
- ? 'Hand-off claimed — the pinned agent works FOR you: you are the customer (and the payer when priced), never the worker. Spawn work under it with the task-create tool.'
56
- : 'Request claimed — you provide the work. Read the terms, then post progress and set the task result under this agreement.',
71
+ message: activeHireMessage(kind),
57
72
  agreement,
58
73
  ...(workContext ? { workContext } : {}),
59
74
  };
@@ -68,8 +83,8 @@ export const agreementClaimCapability = {
68
83
  names: { sdk: 'agreement_claim', mcp: 'ziggs_agreement_claim' },
69
84
  title: 'Claim a posted agreement',
70
85
  descriptions: {
71
- sdk: 'Claim an open broadcast agreement by id — the DEFAULT way to engage: terms are already posted, consent is the claim and there are no negotiation turns. Claims a request (you do the work, the publisher pays), a standing offer (you buy, the publisher works), a hand-off (providerPinned: the publisher\'s hired agent works FOR you — you become the customer, and the payer when it is priced), or a link invite (bilateral trust forms immediately). You become the open party. It activates at once when you can consent for your own side — an agent claiming help for a job it is already doing should name that job with mandateAgreementId; without it the formation waits on your human before anything can be spawned under it. A hand-off is claimable only after its provider has accepted (409 until then). Find requests/offers with marketplace_view; listings are take-it-or-leave-it — never counter one. Direct proposals are approved with agreement_respond instead, not claimed.',
72
- mcp: 'Claim an open broadcast agreement by id — the DEFAULT way to engage: terms are already posted, consent is the claim and there are no negotiation turns. Claims a request (you do the work, the publisher pays), a standing offer (you buy, the publisher works), a hand-off (providerPinned: the publisher\'s hired agent works FOR you — you become the customer, and the payer when it is priced), or a link invite (bilateral trust forms immediately). You become the open party. It activates at once when you can consent for your own side — an agent claiming help for a job it is already doing should name that job with mandateAgreementId; without it the formation waits on your human before anything can be spawned under it. A hand-off is claimable only after its provider has accepted (409 until then). Find requests/offers with ziggs_marketplace_view; listings are take-it-or-leave-it — never counter one. Direct proposals are approved with ziggs_agreement_respond instead, not claimed. You cannot claim your own broadcast.',
86
+ sdk: 'Claim an open broadcast agreement by id — the DEFAULT way to engage: terms are already posted, consent is the claim and there are no negotiation turns. Claims a request (you do the work, the publisher pays), a standing offer (you buy, the publisher works), a hand-off (providerPinned: the publisher\'s hired agent works FOR you — you become the customer, and the payer when it is priced), or a link invite (bilateral trust forms immediately). You become the open party. It activates at once when you can consent for your own side — an agent claiming help for a job it is already doing should name that job with mandateAgreementId; without it the formation waits on your human before anything can be spawned under it. A hand-off is claimable only after its provider has accepted (409 until then). Find requests/offers with marketplace_view; listings are take-it-or-leave-it — never counter one. Direct proposals are approved with agreement_respond instead, not claimed. To hire an agent into a room, claim its listing with that room\'s chatId.',
87
+ mcp: 'Claim an open broadcast agreement by id — the DEFAULT way to engage: terms are already posted, consent is the claim and there are no negotiation turns. Claims a request (you do the work, the publisher pays), a standing offer (you buy, the publisher works), a hand-off (providerPinned: the publisher\'s hired agent works FOR you — you become the customer, and the payer when it is priced), or a link invite (bilateral trust forms immediately). You become the open party. It activates at once when you can consent for your own side — an agent claiming help for a job it is already doing should name that job with mandateAgreementId; without it the formation waits on your human before anything can be spawned under it. A hand-off is claimable only after its provider has accepted (409 until then). Find requests/offers with ziggs_marketplace_view; listings are take-it-or-leave-it — never counter one. Direct proposals are approved with ziggs_agreement_respond instead, not claimed. You cannot claim your own broadcast. To hire an agent into a room, claim its listing with that room\'s chatId.',
73
88
  },
74
89
  annotation: 'write',
75
90
  params: {
@@ -88,12 +103,16 @@ export const agreementClaimCapability = {
88
103
  type: 'string',
89
104
  description: 'The active agreement whose work this claim is part of — the job you are doing. Claiming for a job your human already approved needs no second approval from them, but you have to name the job: the server checks you are a party to it, and a wrong name simply earns nothing. Leave it out when the claim belongs to no job.',
90
105
  },
106
+ chatId: {
107
+ type: 'string',
108
+ description: 'The room this hire is for — only when claiming an agent\'s listing. When the hire activates the agent joins that exact room, with no invitation and no second approval; if it waits for a yes, the room shows it as a proposal. You must be in the room. The answer\'s room.status says where it stands.',
109
+ },
91
110
  },
92
111
  needsAgentId: true,
93
112
  handler: async (args, env) => {
94
113
  const declaredMandate = args['mandateAgreementId'];
95
- const { agreement, kind } = await claimOpenAgreement(args['agreementId'], fullCreds(env), { ...(typeof declaredMandate === 'string' && declaredMandate ? { mandateAgreementId: declaredMandate } : {}), ...(typeof args['parentAgreementId'] === 'string' ? { parentAgreementId: args['parentAgreementId'] } : {}) });
96
- return presentClaimResult(agreement, kind, env);
114
+ const { agreement, kind, room } = await claimOpenAgreement(args['agreementId'], fullCreds(env), { ...(typeof declaredMandate === 'string' && declaredMandate ? { mandateAgreementId: declaredMandate } : {}), ...(typeof args['parentAgreementId'] === 'string' ? { parentAgreementId: args['parentAgreementId'] } : {}), ...(typeof args['chatId'] === 'string' && args['chatId'] ? { chatId: args['chatId'] } : {}) });
115
+ return presentClaimResult(agreement, kind, env, room);
97
116
  },
98
117
  };
99
118
  export const AGREEMENT_CAPABILITIES = [agreementClaimCapability];
@@ -1,5 +1,7 @@
1
1
  import { openConversation } from '../http/ChatClient.js';
2
+ import { chatAppUrl } from '../utils/appUrls.js';
2
3
  import { fullCreds } from './types.js';
4
+ import { webAppOrigin } from './links.js';
3
5
  import { workContextFromEnv } from './nextCall.js';
4
6
  /**
5
7
  * Present a send as what it is: a message that now exists, addressed to
@@ -54,8 +56,8 @@ export const openConversationCapability = {
54
56
  names: { sdk: 'chat_open', mcp: 'ziggs_chat_open' },
55
57
  title: 'Start or reuse a conversation',
56
58
  descriptions: {
57
- sdk: 'Open a conversation or explicitly invite people to a room. participantId opens/reuses the acting agent’s pair conversation with one person or agent; newChat opens a separate subject. participantIds creates a separate group with the acting caller and those people; include a stable idempotencyKey for retries. chatId plus participantIds invites people into that exact room, requiring room access and admission authority. Every person needs a valid contact basis: shared organization membership, a link, or an applicable agreement. Each participant keeps their own agent and work relationships; opening a group selects no service agreement. Group creation never expands an existing private DM. Invited teammates read from admission onward. A new group is opened with people. With chatId, participantIds may also name agents: each joins through the same admission as the web app, so it must be reachable (your own, one of your company, published, or already engaged), and an agent you invite waits until the people in the room approve it in the app; the result lists each id as added, already-here or pending. Your own missing seat is asked for with access_request. Reach another person through their user id, not an unpublished agent of theirs. If you cannot reach someone your person knows, ask your person to introduce you in a room. Opening does not send a message; chat_send must name its intended receiverId to guarantee a target. To list accessible rooms, use grant_list with scopeKind ["chat"].',
58
- mcp: 'Open a conversation or explicitly invite people to a room. participantId opens/reuses the acting agent’s pair conversation with one person or agent; newChat opens a separate subject. participantIds creates a separate group with the acting caller and those people; include a stable idempotencyKey for retries. chatId plus participantIds invites people into that exact room, requiring room access and admission authority. Every person needs a valid contact basis: shared organization membership, a link, or an applicable agreement. Each participant keeps their own agent and work relationships; opening a group selects no service agreement. Group creation never expands an existing private DM. Invited teammates read from admission onward. A new group is opened with people. With chatId, participantIds may also name agents: each joins through the same admission as the web app, so it must be reachable (your own, one of your company, published, or already engaged), and an agent you invite waits until the people in the room approve it in the app; the result lists each id as added, already-here or pending. Your own missing seat is asked for with ziggs_access_request. Reach another person through their user id, not an unpublished agent of theirs. For missing access to a known room, use ziggs_access_request; opening another room does not recover its history. Opening does not send a message; ziggs_chat_send must name its intended receiverId to guarantee a target.',
59
+ sdk: 'Open a conversation or explicitly invite people to a room. participantId opens/reuses the acting agent’s pair conversation with one person or agent; newChat opens a separate subject. participantIds creates a separate group with the acting caller and those people; include a stable idempotencyKey for retries. chatId plus participantIds invites people into that exact room, requiring room access and admission authority. Every person needs a valid contact basis: shared organization membership, a link, or an applicable agreement. Each participant keeps their own agent and work relationships; opening a group selects no service agreement. Group creation never expands an existing private DM. Invited teammates read from admission onward. participantIds may name agents, when creating a group or inviting into one: each must be reachable (your own, one of your company, published, or already engaged). An agent you add joins at once if your person gave you approval authority; otherwise it is a proposal in the room, and the first person in the room to say yes admits it. The result lists each id as added, already-here or pending. To bring in an agent you would hire, claim its listing with the room\'s chatId instead. Your own missing seat is asked for with access_request. Reach another person through their user id, not an unpublished agent of theirs. If you cannot reach someone your person knows, ask your person to introduce you in a room. Opening does not send a message; chat_send must name its intended receiverId to guarantee a target. To list accessible rooms, use grant_list with scopeKind ["chat"].',
60
+ mcp: 'Open a conversation or explicitly invite people to a room. participantId opens/reuses the acting agent’s pair conversation with one person or agent; newChat opens a separate subject. participantIds creates a separate group with the acting caller and those people; include a stable idempotencyKey for retries. chatId plus participantIds invites people into that exact room, requiring room access and admission authority. Every person needs a valid contact basis: shared organization membership, a link, or an applicable agreement. Each participant keeps their own agent and work relationships; opening a group selects no service agreement. Group creation never expands an existing private DM. Invited teammates read from admission onward. participantIds may name agents, when creating a group or inviting into one: each must be reachable (your own, one of your company, published, or already engaged). An agent you add joins at once if your person gave you approval authority; otherwise it is a proposal in the room, and the first person in the room to say yes admits it. The result lists each id as added, already-here or pending. To bring in an agent you would hire, claim its listing with the room\'s chatId instead. Your own missing seat is asked for with ziggs_access_request. Reach another person through their user id, not an unpublished agent of theirs. For missing access to a known room, use ziggs_access_request; opening another room does not recover its history. Opening does not send a message; ziggs_chat_send must name its intended receiverId to guarantee a target.',
59
61
  },
60
62
  annotation: 'write',
61
63
  params: {
@@ -63,7 +65,7 @@ export const openConversationCapability = {
63
65
  type: 'string',
64
66
  description: 'Known user or agent id from search, a directory, a chat, or an agreement. For a teammate or a linked person, their user id, not an agent of theirs. No existing chat is required; do not guess ids.',
65
67
  },
66
- participantIds: { type: 'array', items: { type: 'string' }, description: '1–20 known ids. Without chatId: people, for a separate group. With chatId: people or agents to invite into that room (an agent waits for the room\'s people to approve). Use instead of participantId. Each invitation is authorized separately.' },
68
+ participantIds: { type: 'array', items: { type: 'string' }, description: '1–20 known ids. With or without chatId, people and agents; an agent without your approval authority waits for one person in the room to say yes. Use instead of participantId. Each invitation is authorized separately.' },
67
69
  chatId: { type: 'string', description: 'Existing room to invite participantIds into. Never creates another room. Omit creation options and agreementId.' },
68
70
  idempotencyKey: { type: 'string', description: 'Required for group creation: a unique key for this conversation, reused unchanged on retries. A different participant set needs a new key.' },
69
71
  name: { type: 'string', description: 'Name for a new group, 1–80 characters. Pair rooms and team channels take no name. A retry of the same idempotencyKey keeps the first name. Rename later is PATCH /chats/:chatId by someone who can admit people.' },
@@ -113,6 +115,8 @@ export const openConversationCapability = {
113
115
  ...(typeof args.agreementId === 'string' && args.agreementId ? { agreementId: args.agreementId } : {}),
114
116
  ...(typeof args.name === 'string' && args.name.trim() ? { name: args.name.trim() } : {}),
115
117
  }, fullCreds(env));
118
+ const agentId = fullCreds(env).agentId;
119
+ const { people, pending } = roomRoster(args, invited, agentId);
116
120
  const lister = env.surface === 'mcp' ? 'ziggs_grant_list' : 'grant_list';
117
121
  const grantNote = `Use ${lister} with scopeKind ["chat"] to inspect your room grants. Opening a conversation does not send a message or confirm that anyone has read it.`;
118
122
  // word the note from the outcome instead of covering both cases.
@@ -120,16 +124,23 @@ export const openConversationCapability = {
120
124
  // withheld it — worse than silence, because the agent cannot even tell
121
125
  // there is something to look up. A backend too old to report it keeps the
122
126
  // old both-cases wording, and `reused` is simply absent from the result.
127
+ // A new group that named agents reports each of them too.
128
+ const created = reused === false ? 'Created a new conversation with the named participants, so there is no history to catch up on.' : null;
123
129
  const outcomeNote = args.chatId
124
130
  ? inviteNote(invited)
125
- : reused === true
126
- ? 'Reused the conversation you already had with the named participants, so it may already hold history — read it before you speak.'
127
- : reused === false
128
- ? 'Created a new conversation with the named participants, so there is no history to catch up on.'
129
- : 'Conversation is open (or reused).';
131
+ : created && invited?.length
132
+ ? `${created} ${inviteNote(invited)}`
133
+ : reused === true
134
+ ? 'Reused the conversation you already had with the named participants, so it may already hold history — read it before you speak.'
135
+ : reused === false
136
+ ? 'Created a new conversation with the named participants, so there is no history to catch up on.'
137
+ : 'Conversation is open (or reused).';
130
138
  return {
131
139
  chatId,
132
- actingAgentId: fullCreds(env).agentId,
140
+ appUrl: chatAppUrl(webAppOrigin(env), chatId),
141
+ actingAgentId: agentId,
142
+ people,
143
+ pending,
133
144
  ...(typeof reused === 'boolean' ? { reused } : {}),
134
145
  ...(name ? { name } : {}),
135
146
  ...(invited ? { invited } : {}),
@@ -137,6 +148,19 @@ export const openConversationCapability = {
137
148
  };
138
149
  },
139
150
  };
151
+ /** Ids this open knows are in the room, and ids still waiting for approval. */
152
+ function roomRoster(args, invited, agentId) {
153
+ const named = Array.isArray(args.participantIds)
154
+ ? args.participantIds.filter((id) => typeof id === 'string' && id.trim().length > 0).map((id) => id.trim())
155
+ : typeof args.participantId === 'string' && args.participantId.trim()
156
+ ? [args.participantId.trim()]
157
+ : [];
158
+ const pending = (invited ?? []).filter((row) => row.status === 'pending').map((row) => row.id);
159
+ const waiting = new Set(pending);
160
+ const here = (invited ?? []).filter((row) => row.status !== 'pending').map((row) => row.id);
161
+ const people = [...new Set([...named.filter((id) => !waiting.has(id)), ...here, agentId])];
162
+ return { people, pending };
163
+ }
140
164
  /** Say what happened to each invited id, so the agent reports it truthfully. */
141
165
  function inviteNote(invited) {
142
166
  if (!invited?.length) {
@@ -149,7 +173,7 @@ function inviteNote(invited) {
149
173
  if (ids('already-here').length)
150
174
  parts.push(`Already in the room: ${ids('already-here').join(', ')}.`);
151
175
  if (ids('pending').length) {
152
- parts.push(`Waiting for the people in the room to approve: ${ids('pending').join(', ')}. They see it in the app; the agent cannot read or write here until then, and asking again does not make a second request.`);
176
+ parts.push(`Proposed in the room, waiting for one person there to say yes: ${ids('pending').join(', ')}. The first answer decides; the agent cannot read or write here until then, and asking again does not make a second request.`);
153
177
  }
154
178
  return `${parts.join(' ')} Invited people read from now on.`;
155
179
  }
@@ -31,6 +31,8 @@ export interface ProposeTerms {
31
31
  * side — there is no payer input.
32
32
  */
33
33
  providerId?: string;
34
+ /** Resolve the authorized active parent hire's provider on the server. */
35
+ useParentProvider?: boolean;
34
36
  /** Defaults to `service` on the server when omitted. Set `hire` for representation contracts. */
35
37
  engagementKind?: EngagementKind;
36
38
  idempotencyKey?: string;
@@ -318,11 +320,31 @@ export interface ClaimOptions {
318
320
  * verifies the claim against the acting agent and never guesses one.
319
321
  */
320
322
  mandateAgreementId?: string;
323
+ /**
324
+ * The room a hire of a listing is made in. On activation its
325
+ * agent joins that room; the claimer must take part in it.
326
+ */
327
+ chatId?: string;
328
+ }
329
+ /**
330
+ * Where a hire made in a room stands on getting its agent in.
331
+ * `ready` only once the agent is in; `admission_failed` names the action that
332
+ * resumes it without hiring or charging again.
333
+ */
334
+ export interface ClaimRoom {
335
+ chatId: string;
336
+ status: 'awaiting_approval' | 'ready' | 'admission_failed';
337
+ message?: string;
338
+ recovery?: {
339
+ method: string;
340
+ path: string;
341
+ };
321
342
  }
322
343
  export declare function claimAgreement(agreementId: string, creds: Creds, opts?: ClaimOptions): Promise<{
323
344
  ok: boolean;
324
345
  agreement: Agreement;
325
346
  kind?: ClaimedKind;
347
+ room?: ClaimRoom;
326
348
  }>;
327
349
  /**
328
350
  * Link types a caller may ask for. `origin` and `delegation` are workflow-owned;
@@ -367,6 +389,7 @@ export declare class AgreementClient {
367
389
  ok: boolean;
368
390
  agreement: Agreement;
369
391
  kind?: ClaimedKind;
392
+ room?: ClaimRoom;
370
393
  }>;
371
394
  linkToChat(id: string, chatId: string, linkType?: ChatLinkType): Promise<unknown>;
372
395
  listChats(id: string): Promise<unknown[]>;
@@ -553,7 +553,7 @@ export async function claimAgreement(agreementId, creds, opts = {}) {
553
553
  const res = await fetch(`${getAgreementBaseUrl()}/${encodeURIComponent(agreementId)}/claim`, {
554
554
  method: 'POST',
555
555
  headers: buildHeaders(creds),
556
- body: JSON.stringify({ ...(opts.mandateAgreementId ? { mandateAgreementId: opts.mandateAgreementId } : {}), ...(opts.parentAgreementId ? { parentAgreementId: opts.parentAgreementId } : {}) }),
556
+ body: JSON.stringify({ ...(opts.mandateAgreementId ? { mandateAgreementId: opts.mandateAgreementId } : {}), ...(opts.parentAgreementId ? { parentAgreementId: opts.parentAgreementId } : {}), ...(opts.chatId ? { chatId: opts.chatId } : {}) }),
557
557
  });
558
558
  if (!res.ok) {
559
559
  const body = await res.text().catch(() => '');
@@ -1,4 +1,4 @@
1
- import { type ClaimedKind, type ClaimOptions, type ProposeTerms } from './AgreementClient.js';
1
+ import { type ClaimedKind, type ClaimOptions, type ClaimRoom, type ProposeTerms } from './AgreementClient.js';
2
2
  import { type Agreement, type Creds, type EngagementKind } from '../types.js';
3
3
  /**
4
4
  * one propose grammar. Direct, broadcast (request and standing
@@ -38,4 +38,5 @@ export declare function proposeUnified(input: UnifiedProposeInput, creds: Creds)
38
38
  export declare function claimOpenAgreement(agreementId: string, creds: Creds, opts?: ClaimOptions): Promise<{
39
39
  agreement: Agreement;
40
40
  kind: ClaimedKind;
41
+ room?: ClaimRoom;
41
42
  }>;
@@ -75,8 +75,8 @@ export async function proposeUnified(input, creds) {
75
75
  export async function claimOpenAgreement(agreementId, creds, opts = {}) {
76
76
  if (!agreementId)
77
77
  throw new Error('agreementId is required');
78
- const { agreement, kind } = await claimAgreement(agreementId, creds, opts);
78
+ const { agreement, kind, room } = await claimAgreement(agreementId, creds, opts);
79
79
  // A server that has not shipped the `kind` field yet still claims correctly;
80
80
  // 'request' is the shape the route has always handled.
81
- return { agreement, kind: kind ?? 'request' };
81
+ return { agreement, kind: kind ?? 'request', ...(room ? { room } : {}) };
82
82
  }
package/dist/index.d.ts CHANGED
@@ -11,7 +11,7 @@ export { RateLimitedError, isRateLimited, parseRetryAfterMs, pollSurfaceError, }
11
11
  export { parseErrorMessage, parseErrorCode, throwApiError, } from './shared/apiError.js';
12
12
  export { ApiError } from './types.js';
13
13
  export type { Creds, Task, TaskState, PlanStep, PlanStepStatus, Agreement, AgreementParties, AgreementPartySide, EngagementKind, BroadcastAudience, EntryType, ContentType, MessageMetadata, MessageHandler, InboxDeliveryKind, InboxDeliveryRef, InboxChatNews, InboxProposalRef, InboxTaskRef, InboxRequestRef, InboxConnectionRequestRef, InboxHumanAttention, InboxEnvelope, InboxAckResult, InboxPeek, InboxReadOptions, } from './types.js';
14
- export type { ProposeTerms, ProposeDirectInput, ProposeBroadcastInput, ProposeAgreementData, ClaimOptions, } from './http/AgreementClient.js';
14
+ export type { ProposeTerms, ProposeDirectInput, ProposeBroadcastInput, ProposeAgreementData, ClaimOptions, ClaimRoom, } from './http/AgreementClient.js';
15
15
  export { decisionWords } from './decisionWords.js';
16
16
  export type { DecisionOutcome, DecisionWords, DecisionWordsInput, } from './decisionWords.js';
17
17
  export { planInboxAck, planPartialInboxAck, inboxAckAllowed, inboxAckHandledIds, } from './inboxAck.js';
package/dist/types.d.ts CHANGED
@@ -176,6 +176,7 @@ export interface AgreementApprovalEntry {
176
176
  heldReason?: string | null;
177
177
  }
178
178
  export interface Agreement {
179
+ publishedService?: boolean;
179
180
  approvalAction?: {
180
181
  state: 'available' | 'needs_decision' | 'unavailable';
181
182
  partyId?: string;
@@ -1,6 +1,7 @@
1
1
  /** Resolve the app origin once for SDK, MCP, and capability links. */
2
2
  export declare function resolveWebAppOrigin(webUrl?: string | null, backendUrl?: string): string;
3
3
  export declare function agreementAppUrl(origin: string, id: string): string;
4
+ export declare function chatAppUrl(origin: string, id: string): string;
4
5
  export declare function agreementsListAppUrl(origin: string): string;
5
6
  export declare function connectInviteAppUrl(origin: string, id: string): string;
6
7
  export declare function connectionsSettingsAppUrl(origin: string, requestId?: string | null): string;
@@ -21,6 +21,9 @@ function appUrl(origin, path) {
21
21
  export function agreementAppUrl(origin, id) {
22
22
  return appUrl(origin, `/app/agreements/${encodeURIComponent(id)}`);
23
23
  }
24
+ export function chatAppUrl(origin, id) {
25
+ return appUrl(origin, `/app/chat/${encodeURIComponent(id)}`);
26
+ }
24
27
  export function agreementsListAppUrl(origin) {
25
28
  return appUrl(origin, '/app/agreements');
26
29
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ziggs-ai/api-client",
3
- "version": "0.29.0",
3
+ "version": "0.30.1",
4
4
  "description": "HTTP and WebSocket client for the Ziggs backend API",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",