@ziggs-ai/api-client 0.23.1 → 0.25.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.
@@ -3,25 +3,22 @@ export declare const agreementBuyCapability: CapabilityDefinition;
3
3
  export declare const agreementBidCapability: CapabilityDefinition;
4
4
  export declare const agreementBrokerCapability: CapabilityDefinition;
5
5
  export declare const agreementRequestCapability: CapabilityDefinition;
6
+ export declare const agreementOfferCapability: CapabilityDefinition;
6
7
  /**
7
- * No mandate param here, deliberately: a listing is formed inside no job. It is
8
- * a standing invitation to the world that outlives whatever the agent happens
9
- * to be doing today, and the publish path says so at its own end
10
- * (`POST /marketplace/offers/publish`). Hanging one under a job would kill the
11
- * listing when the job ended.
12
- *
13
- * No price default here on purpose: an unstated price is a free hire, and the
14
- * publish path is the one place that says so. Stating a price publishes a paid
15
- * listing.
16
- *
17
- * No replay key either, and that one has a cost worth knowing: each call
18
- * publishes a fresh row rather than replaying, and the previous listing is
19
- * retired as it goes, so an agent that republishes leaves dead rows behind it.
20
- * One listing per agent still holds; the churn does not.
8
+ * Pause and resume act on a listing that already exists and form nothing. A
9
+ * pause keeps the same row, id and sold hires; revoke is the verb that ends a
10
+ * listing for good.
21
11
  */
22
- export declare const agreementOfferCapability: CapabilityDefinition;
12
+ export declare const listingPauseCapability: CapabilityDefinition;
13
+ export declare const listingResumeCapability: CapabilityDefinition;
23
14
  export declare const agreementHandoffCapability: CapabilityDefinition;
24
15
  export declare const AGREEMENT_VERB_CAPABILITIES: CapabilityDefinition[];
16
+ /**
17
+ * The door verbs. Not in {@link AGREEMENT_VERB_CAPABILITIES}: those six form an
18
+ * agreement, while pause and resume open and close the door on a listing that
19
+ * already exists.
20
+ */
21
+ export declare const LISTING_DOOR_CAPABILITIES: CapabilityDefinition[];
25
22
  /**
26
23
  * Subcontract sits next to the propose verbs, not inside them: the parent
27
24
  * already exists, and the conversation is optional. The server uses the
@@ -1,5 +1,5 @@
1
1
  import { delegateAgreement, getAgreement, proposeBroadcast, proposeDirectTo, } from '../http/AgreementClient.js';
2
- import { publishOffer } from '../http/MarketplaceClient.js';
2
+ import { pauseOffer, publishOffer, resumeOffer } from '../http/MarketplaceClient.js';
3
3
  import { fullCreds } from './types.js';
4
4
  import { nextCall } from './nextCall.js';
5
5
  /**
@@ -306,6 +306,15 @@ export const agreementRequestCapability = {
306
306
  * retired as it goes, so an agent that republishes leaves dead rows behind it.
307
307
  * One listing per agent still holds; the churn does not.
308
308
  */
309
+ /**
310
+ * Seats on a listing: how many customers may hold a live hire from it at once.
311
+ * A hire that ends gives its seat back, so this is a capacity, not a lifetime
312
+ * count. Only the listing verb takes it; a direct proposal has one customer.
313
+ */
314
+ const MAX_CLAIMS_PARAM = {
315
+ type: 'number',
316
+ description: 'How many customers may hold a live hire from this listing at once. A hire that ends gives its seat back. While every seat is taken, new claims are refused as full until one frees. Omit for unlimited. Set it when your attention is limited: every customer who hires you can wake you.',
317
+ };
309
318
  export const agreementOfferCapability = {
310
319
  key: 'agreement_offer',
311
320
  names: { sdk: 'agreement_offer', mcp: 'ziggs_agreement_offer' },
@@ -315,7 +324,7 @@ export const agreementOfferCapability = {
315
324
  mcp: 'Publish work you will do, for whoever claims it and pays: you provide. This is your listing — it stands until revoked or exhausted, and claimers take it as posted rather than countering it. To offer one named counterparty instead, use ziggs_agreement_bid. To ask for work rather than supply it, use ziggs_agreement_request.',
316
325
  },
317
326
  annotation: 'write',
318
- params: { audience: AUDIENCE_PARAM, ...TERMS_PARAMS },
327
+ params: { audience: AUDIENCE_PARAM, ...TERMS_PARAMS, maxClaims: MAX_CLAIMS_PARAM },
319
328
  needsAgentId: true,
320
329
  handler: async (args, env) => {
321
330
  const terms = termsFrom(args);
@@ -331,11 +340,56 @@ export const agreementOfferCapability = {
331
340
  billing: terms.billing,
332
341
  engagementKind: engagementKindFrom(args),
333
342
  audience: args['audience'],
343
+ ...(typeof args['maxClaims'] === 'number' ? { maxClaims: args['maxClaims'] } : {}),
334
344
  }, fullCreds(env));
335
345
  return { agreement, readPlan: publishedNext(env) };
336
346
  },
337
347
  sdkOptions: { isAgreementCreation: true },
338
348
  };
349
+ /* ───────────────────────── the door on your own listing ──────────────────── */
350
+ /** The listing the pause and resume verbs act on. */
351
+ const LISTING_ID_PARAM = {
352
+ type: 'string',
353
+ required: true,
354
+ description: 'Your listing: the agreement id your offer was published under (what ziggs_agreement_offer answered with). On your own profile it is doors.listingAgreementId.',
355
+ };
356
+ /**
357
+ * Pause and resume act on a listing that already exists and form nothing. A
358
+ * pause keeps the same row, id and sold hires; revoke is the verb that ends a
359
+ * listing for good.
360
+ */
361
+ export const listingPauseCapability = {
362
+ key: 'listing_pause',
363
+ names: { sdk: 'listing_pause', mcp: 'ziggs_listing_pause' },
364
+ title: 'Pause your listing',
365
+ descriptions: {
366
+ sdk: 'Stop taking new customers on a listing you publish, without deleting it. New claims on it are refused until you resume it with listing_resume. Same listing, same id; hires already sold keep running. To end a listing for good, use agreement_revoke.',
367
+ mcp: 'Stop taking new customers on a listing you publish, without deleting it. New claims on it are refused until you resume it with ziggs_listing_resume. Same listing, same id; hires already sold keep running. Use it when the person you act for asks you to stop taking work for now. To end a listing for good, use ziggs_agreement_revoke. Only the publisher of a listing can pause it.',
368
+ },
369
+ annotation: 'write',
370
+ params: { agreementId: LISTING_ID_PARAM },
371
+ needsAgentId: true,
372
+ handler: async (args, env) => {
373
+ const agreement = await pauseOffer(args['agreementId'], fullCreds(env));
374
+ return { status: 'paused', agreement };
375
+ },
376
+ };
377
+ export const listingResumeCapability = {
378
+ key: 'listing_resume',
379
+ names: { sdk: 'listing_resume', mcp: 'ziggs_listing_resume' },
380
+ title: 'Resume your listing',
381
+ descriptions: {
382
+ sdk: 'Reopen a listing you paused with listing_pause: same listing, same id, taking new customers again. A listing closed because every seat is taken, or because its agent stopped answering, reopens on its own and needs no resume.',
383
+ mcp: 'Reopen a listing you paused with ziggs_listing_pause: same listing, same id, taking new customers again. A listing closed because every seat is taken, or because its agent stopped answering, reopens on its own and needs no resume. Only the publisher of a listing can resume it.',
384
+ },
385
+ annotation: 'write',
386
+ params: { agreementId: LISTING_ID_PARAM },
387
+ needsAgentId: true,
388
+ handler: async (args, env) => {
389
+ const agreement = await resumeOffer(args['agreementId'], fullCreds(env));
390
+ return { status: 'resumed', agreement };
391
+ },
392
+ };
339
393
  /* ──────────────────────────────── hand-off ───────────────────────────────── */
340
394
  export const agreementHandoffCapability = {
341
395
  key: 'agreement_handoff',
@@ -395,6 +449,15 @@ export const AGREEMENT_VERB_CAPABILITIES = [
395
449
  agreementOfferCapability,
396
450
  agreementHandoffCapability,
397
451
  ];
452
+ /**
453
+ * The door verbs. Not in {@link AGREEMENT_VERB_CAPABILITIES}: those six form an
454
+ * agreement, while pause and resume open and close the door on a listing that
455
+ * already exists.
456
+ */
457
+ export const LISTING_DOOR_CAPABILITIES = [
458
+ listingPauseCapability,
459
+ listingResumeCapability,
460
+ ];
398
461
  /**
399
462
  * Subcontract sits next to the propose verbs, not inside them: the parent
400
463
  * already exists, and the conversation is optional. The server uses the
@@ -51,8 +51,8 @@ export const openConversationCapability = {
51
51
  names: { sdk: 'chat_open', mcp: 'ziggs_chat_open' },
52
52
  title: 'Start or reuse a conversation',
53
53
  descriptions: {
54
- sdk: 'Open or reuse a chat with a user or agent participant. Calling it again for the same participant returns the SAME chat, so it is how you find the conversation you already have with someone — pass newChat only when this really is a separate subject. This is also how you reach the HUMAN who hired you when the work needs an answer only they have: pass their user id (context_snapshot lists it under users; on your agreement they are the payer/creator party), then chat_send in the chat it returns with no receiverId. Opening a room is issuing the other side access to it, so it needs something that authorizes that: they publish a listing, you share an org, you hold a live agreement with them (a link counts, so link_propose is how you get one with a peer in another org), or they claimed your invite. Someone else\'s agent is not opened directly, inside your org or outside it: open the chat with the person it answers for (their user id); which of their agents answers is theirs to decide, and an agent of theirs already in a room with you can be written to there. With none of those the call is refused and the refusal names the levers. When the person you cannot reach is somebody YOUR OWN PERSON already knows — their teammate, their partner, their customer — none of those levers is the right one: ask the person you work for to open a room with them and add you, in plain words in your conversation with them, then stop until you are in it. Claiming a listing or sending an invite is for a stranger you are doing business with, and using it on somebody your person could introduce you to in two clicks costs them a negotiation instead. To list chats you can already read, use grant_list scopeKind=chat.',
55
- mcp: 'Open or reuse a chat with a user or agent participant. Calling it again for the same participant returns the SAME chat; pass newChat only when this really is a separate subject. Opening a room is issuing the other side access to it, so it needs something that authorizes that: they publish a listing, you share an org, you hold a live agreement with them (a link counts, so ziggs_link_propose is how you get one with a peer in another org), or they claimed your invite. Someone else\'s agent is not opened directly, inside your org or outside it: open the chat with the person it answers for (their user id); which of their agents answers is theirs to decide, and an agent of theirs already in a room with you can be written to there. With none of those the call is refused and names the levers. When the person you cannot reach is somebody your own person already knows — their teammate, their partner, their customer — ask that person to open a room with them and add you, rather than reaching for a listing or an invite: those are for strangers you are doing business with.',
54
+ sdk: 'Open or reuse a chat with a user or agent participant. Reuse is for this acting agent and that participant; it does not find another assistant’s or the human owner’s conversation, so it is how you find the conversation you already have with someone — pass newChat only when this really is a separate subject. This is also how you reach the HUMAN who hired you when the work needs an answer only they have: pass their user id (context_snapshot lists it under users; on your agreement they are the payer/creator party), then chat_send in the chat it returns with no receiverId. Opening a room is issuing the other side access to it, so it needs something that authorizes that: they publish a listing, you share an org, you hold a live agreement with them (a link counts, so link_propose is how you get one with a peer in another org), or they claimed your invite. Someone else\'s agent is not opened directly, inside your org or outside it: open the chat with the person it answers for (their user id); which of their agents answers is theirs to decide, and an agent of theirs already in a room with you can be written to there. With none of those the call is refused and the refusal names the levers. When the person you cannot reach is somebody YOUR OWN PERSON already knows — their teammate, their partner, their customer — none of those levers is the right one: ask the person you work for to open a room with them and add you, in plain words in your conversation with them, then stop until you are in it. Claiming a listing or sending an invite is for a stranger you are doing business with, and using it on somebody your person could introduce you to in two clicks costs them a negotiation instead. To list chats you can already read, use grant_list scopeKind=chat.',
55
+ mcp: 'Open or reuse a chat with a user or agent participant. Reuse is for this acting agent and that participant; it does not find another assistant’s or the human owner’s conversation; pass newChat only when this really is a separate subject. Opening a room is issuing the other side access to it, so it needs something that authorizes that: they publish a listing, you share an org, you hold a live agreement with them (a link counts, so ziggs_link_propose is how you get one with a peer in another org), or they claimed your invite. Someone else\'s agent is not opened directly, inside your org or outside it: open the chat with the person it answers for (their user id); which of their agents answers is theirs to decide, and an agent of theirs already in a room with you can be written to there. With none of those the call is refused and names the levers. If you already have the intended chatId, use ziggs_open and ziggs_access_request for missing access; opening another room does not recover its history. When the person you cannot reach is somebody your own person already knows — their teammate, their partner, their customer — ask that person to open a room with them and add you, rather than reaching for a listing or an invite: those are for strangers you are doing business with.',
56
56
  },
57
57
  annotation: 'write',
58
58
  params: {
@@ -61,6 +61,7 @@ export const openConversationCapability = {
61
61
  required: true,
62
62
  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.',
63
63
  },
64
+ createIfMissing: { type: 'boolean', description: 'Set false to reuse only an existing room for this acting agent and participant. A missing room is refused without creating a replacement.' },
64
65
  newChat: {
65
66
  type: 'boolean',
66
67
  description: 'Open a separate chat even though one is already open with this participant. ' +
@@ -81,6 +82,7 @@ export const openConversationCapability = {
81
82
  throw new Error('participantId is required');
82
83
  const { chatId, reused } = await openConversation(args['participantId'], fullCreds(env), {
83
84
  newChat: args['newChat'] === true,
85
+ ...(typeof args.createIfMissing === 'boolean' ? { createIfMissing: args.createIfMissing } : {}),
84
86
  ...(typeof args['agreementId'] === 'string' && args['agreementId']
85
87
  ? { agreementId: args['agreementId'] }
86
88
  : {}),
@@ -99,6 +101,7 @@ export const openConversationCapability = {
99
101
  : 'Conversation is open (or reused).';
100
102
  return {
101
103
  chatId,
104
+ actingAgentId: fullCreds(env).agentId,
102
105
  ...(typeof reused === 'boolean' ? { reused } : {}),
103
106
  note: `${outcomeNote} ${grantNote}`,
104
107
  };
@@ -22,5 +22,6 @@ export declare const contextDiscoverGrantableCapability: CapabilityDefinition;
22
22
  export declare const contextDelegateCapability: CapabilityDefinition;
23
23
  export declare const openCapability: CapabilityDefinition;
24
24
  export declare const accessExplainCapability: CapabilityDefinition;
25
- export declare const CONTEXT_CAPABILITIES: CapabilityDefinition[];
26
25
  export declare const contextRequestCapability: CapabilityDefinition;
26
+ export declare const accessRequestCapability: CapabilityDefinition;
27
+ export declare const CONTEXT_CAPABILITIES: CapabilityDefinition[];
@@ -160,8 +160,8 @@ export const contextDiscoverGrantableCapability = {
160
160
  names: { sdk: 'context_discover_grantable', mcp: 'ziggs_context_discover_grantable' },
161
161
  title: 'Find context you cannot read yet',
162
162
  descriptions: {
163
- sdk: 'See what context EXISTS in orgs you actively work in that you CANNOT read yet — the inverse of grant_list. Covers chats, agreements, and connections (type is one of "chat" | "agreement" | "connection"; connection labels are the provider name only). Returns labels only ({ type, label, scopeRef, orgId } per item), never content, member names, tokens, or money. Bounded to orgs you hold an active WORK agreement in (hire/service; a link to a peer org does not open that org), and excludes anything you already hold a grant for. Use it to notice you may be missing context, then either ask your human to grant a scopeRef, or (if you hold a broader grant) context_delegate using that scopeRef. Pair with grant_list (what you hold) and context_expand_reach (what a held grant covers).',
164
- mcp: 'See what context EXISTS in orgs you actively work in that you CANNOT read yet — so you can ask for it instead of failing blind. Covers chats, agreements, and connections (type is "chat" | "agreement" | "connection"; connection labels are the provider name only). Returns labels only: { type, label, scopeRef, orgId } per item, never content, member names, tokens, or money. Bounded to orgs you hold an active WORK agreement in (hire/service; a link to a peer org does not open that org). To act on one, ask your human to grant it, or (if you hold a broader grant of your own) delegate via ziggs_context_delegate using the scopeRef. Use ziggs_grant_list for what you already hold; this is what you lack.',
163
+ sdk: 'See what context EXISTS in orgs you actively work in that you CANNOT read yet — the inverse of grant_list. Covers chats, agreements, and connections (type is one of "chat" | "agreement" | "connection"; connection labels are the provider name only). Returns labels only ({ type, label, scopeRef, orgId } per item), never content, member names, tokens, or money. Bounded to orgs you hold an active WORK agreement in (hire/service; a link to a peer org does not open that org), and excludes context your current read access covers. For a missing chat or agreement use access_request with its scopeRef; for a connection ask your human to grant it, or (if you hold a broader grant) context_delegate using that scopeRef. Pair with grant_list (what you hold) and context_expand_reach (what a held grant covers).',
164
+ mcp: 'See what context EXISTS in orgs you actively work in that you CANNOT read yet — so you can ask for it instead of failing blind. Covers chats, agreements, and connections (type is "chat" | "agreement" | "connection"; connection labels are the provider name only). Returns labels only: { type, label, scopeRef, orgId } per item, never content, member names, tokens, or money. Bounded to orgs you hold an active WORK agreement in (hire/service; a link to a peer org does not open that org). To act on a chat or agreement, create a pending request with ziggs_access_request; for a connection ask your human to grant it, or (if you hold a broader grant of your own) delegate via ziggs_context_delegate using the scopeRef. Use ziggs_grant_list for what you already hold; this is what you lack.',
165
165
  },
166
166
  annotation: 'read-only',
167
167
  params: {},
@@ -169,7 +169,9 @@ export const contextDiscoverGrantableCapability = {
169
169
  handler: async (_args, env) => {
170
170
  const creds = fullCreds(env);
171
171
  const items = await new ContextDiscoveryClient(creds.operatorKey, creds.agentId).discoverGrantable();
172
- return { count: items.length, items };
172
+ return { count: items.length, items: items.map((item) => ({ ...item,
173
+ actions: item.type === 'connection' ? [] : [requestAccessAction({ scopeKind: item.type, scopeId: item.scopeRef.id }, env)],
174
+ })) };
173
175
  },
174
176
  };
175
177
  export const contextDelegateCapability = {
@@ -289,15 +291,25 @@ function openBodyFromArgs(args) {
289
291
  return body;
290
292
  }
291
293
  function presentOpenResult(result, env) {
292
- // Never attach a ready write. An unreadable row already carries
293
- // access.continuation (owner-decision); this call must not execute it.
294
- const requestTool = env.surface === 'mcp' ? 'ziggs_context_request' : 'context_request';
294
+ // Expose a request-only next action, never execute it as part of a read.
295
+ const requestTool = env.surface === 'mcp' ? 'ziggs_access_request' : 'access_request';
295
296
  const chatTool = env.surface === 'mcp' ? 'ziggs_chat_open' : 'chat_open';
297
+ const resumeActions = (result.outcome === 'ok' && result.ref.kind === 'agreement'
298
+ ? result.page?.items ?? [] : []).flatMap((item) => {
299
+ if (!item || typeof item !== 'object')
300
+ return [];
301
+ const row = item;
302
+ const ask = row.grantRequest;
303
+ if (row.status !== 'active' || ask?.primitive !== 'context' || ask.kind !== 'access-request' || ask.holderId !== fullCreds(env).agentId || !ask.scope?.id || !['artifact', 'chat', 'agreement', 'task'].includes(ask.scope.kind ?? ''))
304
+ return [];
305
+ return [{ kind: 'resume', tool: env.surface === 'mcp' ? 'ziggs_open' : 'open', args: { [`${ask.scope.kind}Id`]: ask.scope.id }, effect: 'read_only', note: 'Approval recorded. Open the original scope; the server rechecks expiry and revocation.' }];
306
+ });
296
307
  return {
297
308
  ...result,
298
- actions: [],
309
+ actions: result.access.continuation?.request ? [requestAccessAction(result.access.continuation.request, env)] : result.ref.kind === 'artifact' && result.outcome === 'unreadable' ? [requestAccessAction({ scopeKind: 'artifact', scopeId: result.ref.id, permission: 'read', durationHours: 24 }, env)] : resumeActions,
310
+ actingAgentId: fullCreds(env).agentId,
299
311
  ...(result.ref.kind === 'artifact' && result.outcome !== 'ok' ? {
300
- requestAccessHint: `If a source you already read supplied this artifact reference and its human owner, ${requestTool} can ask that owner for temporary read access without a prior grant. It needs a conversation with the owner; ${chatTool} can open or reuse one, subject to authorization. Discover the request tool's schema before calling it. This response does not confirm existence or ownership. A request grants no access until the human owner approves.`,
312
+ requestAccessHint: `If a source you already read supplied this artifact reference and its human owner, ${requestTool} scopeKind=artifact can ask that owner for temporary read access without a prior grant. It needs a conversation with the owner; ${chatTool} can open or reuse one, subject to authorization. Discover the request tool's schema before calling it. This response does not confirm existence or ownership. A request grants no access until the human owner approves.`,
301
313
  } : {}),
302
314
  };
303
315
  }
@@ -342,14 +354,6 @@ export const accessExplainCapability = {
342
354
  return presentOpenResult(result, env);
343
355
  },
344
356
  };
345
- export const CONTEXT_CAPABILITIES = [
346
- contextReadCapability,
347
- openCapability,
348
- accessExplainCapability,
349
- contextDelegateCapability,
350
- contextExpandReachCapability,
351
- contextDiscoverGrantableCapability,
352
- ];
353
357
  export const contextRequestCapability = {
354
358
  key: 'context_request',
355
359
  names: { sdk: 'context_request', mcp: 'ziggs_context_request' },
@@ -381,3 +385,73 @@ export const contextRequestCapability = {
381
385
  return { ...result, outcome: 'pending', state: 'requested', appUrl: `${(env.webUrl ?? 'https://ziggsai.com').replace(/\/$/, '')}/app/agreements/${encodeURIComponent(result.agreementId)}`, note: 'Waiting for the artifact owner. No access granted yet; open the original artifact only after approval.' };
382
386
  },
383
387
  };
388
+ /** A suggested request still needs a purpose; it is never a grant or an approval. */
389
+ function requestAccessAction(args, env) {
390
+ return {
391
+ kind: 'request_access',
392
+ tool: env.surface === 'mcp' ? 'ziggs_access_request' : 'access_request',
393
+ args, requiredInput: args.scopeKind === 'artifact' ? ['reason', 'chatId', 'ownerId'] : ['reason'], effect: 'request_only',
394
+ requiresApproval: true,
395
+ };
396
+ }
397
+ export const accessRequestCapability = {
398
+ key: 'access_request',
399
+ names: { sdk: 'access_request', mcp: 'ziggs_access_request' },
400
+ title: 'Request access for yourself',
401
+ descriptions: {
402
+ sdk: 'Create a pending approval agreement for the calling agent to access one artifact, chat, agreement context, or task branch for 1–168 hours. This only requests permission: it never grants access, signs, or approves. After the authorized person approves, open the original resource. Artifact requests need a writable conversation with the named owner; other requests go to your accountable person, who must have authority over the scope. Read-only by default; permission=reply requests read and reply to one chat. No spending or work commitment. Reuse idempotencyKey for a retry.',
403
+ mcp: 'Ask for missing access through a pending approval agreement. Use this when open/access_explain reports an unreadable resource, or discovery/inbox names a scope you need. It requests access ONLY for this MCP agent; never grants access or approves anything. Scope is one artifact, chat, agreement context, or task branch (including subtasks), for 1–168 hours (default 24). Read-only by default; permission=reply requests read and reply in one chat. For earlier chat messages explicitly set temporal=from-start. Artifact requests require chatId of your existing writable conversation with the owner and that ownerId; other requests route to your accountable person, who must be allowed to share the resource. Surface appUrl, wait for the decision when asked, then open the original resource and resume. Pending grants no access. Never use issue_grant, change identity, create another room, or form a generic work agreement to recover missing access.',
404
+ },
405
+ annotation: 'write', needsAgentId: true,
406
+ params: {
407
+ scopeKind: { type: 'string', required: true, enum: ['artifact', 'chat', 'agreement', 'task'] },
408
+ scopeId: { type: 'string', required: true, description: 'Original returned resource id; this request does not open a new resource' },
409
+ reason: { type: 'string', required: true, description: 'Why access is needed and who will receive the reply or summary' },
410
+ permission: { type: 'string', enum: ['read', 'reply'], description: 'read by default; reply is read and reply and is only valid for a chat' },
411
+ temporal: { type: 'string', enum: ['from-now', 'from-start'], description: 'Chat history requested; default from-now. Earlier messages need from-start. Other resources use from-start.' },
412
+ durationHours: { type: 'number', description: 'Integer 1–168 hours after approval; default 24' },
413
+ chatId: { type: 'string', description: 'Artifact requests only: existing conversation with the owner' },
414
+ ownerId: { type: 'string', description: 'Artifact requests only: human account or participant reference in that conversation' },
415
+ idempotencyKey: { type: 'string', description: 'Reuse for the same logical request' },
416
+ },
417
+ handler: async (args, env) => {
418
+ for (const key of ['scopeKind', 'scopeId', 'reason']) {
419
+ if (typeof args[key] !== 'string' || !String(args[key]).trim())
420
+ throw new Error(`${key} is required`);
421
+ }
422
+ const scopeKind = args.scopeKind;
423
+ if (!['artifact', 'chat', 'agreement', 'task'].includes(scopeKind))
424
+ throw new Error('Unsupported scopeKind');
425
+ const permission = args.permission ?? 'read';
426
+ if (!['read', 'reply'].includes(String(permission)) || (permission === 'reply' && scopeKind !== 'chat'))
427
+ throw new Error('Only chat access may request reply permission');
428
+ if (args.temporal !== undefined && (!['from-now', 'from-start'].includes(String(args.temporal)) || (scopeKind !== 'chat' && args.temporal === 'from-now')))
429
+ throw new Error('Only chats support from-now access');
430
+ const durationHours = args.durationHours ?? 24;
431
+ if (typeof durationHours !== 'number' || !Number.isInteger(durationHours) || durationHours < 1 || durationHours > 168)
432
+ throw new Error('durationHours must be an integer between 1 and 168');
433
+ const creds = fullCreds(env);
434
+ const result = await new ContextGrantsClient(creds.operatorKey, creds.agentId, env.baseUrl).requestAccess({
435
+ scopeKind, scopeId: String(args.scopeId).trim(), reason: String(args.reason).trim(),
436
+ permission: permission, temporal: args.temporal,
437
+ durationHours, chatId: args.chatId, ownerId: args.ownerId,
438
+ }, { idempotencyKey: args.idempotencyKey, laneId: creds.laneId });
439
+ return {
440
+ ...result, outcome: 'pending', state: 'requested', actingAgentId: creds.agentId,
441
+ scope: { kind: scopeKind, id: args.scopeId },
442
+ appUrl: `${(env.webUrl ?? 'https://ziggsai.com').replace(/\/$/, '')}/app/agreements/${encodeURIComponent(result.agreementId)}`,
443
+ resumeAfterApproval: { tool: env.surface === 'mcp' ? 'ziggs_open' : 'open', args: { [`${scopeKind}Id`]: args.scopeId } },
444
+ note: 'No access granted. The authorized person must approve; then open the original resource using the same agent identity.',
445
+ };
446
+ },
447
+ };
448
+ export const CONTEXT_CAPABILITIES = [
449
+ contextReadCapability,
450
+ openCapability,
451
+ accessExplainCapability,
452
+ accessRequestCapability,
453
+ contextRequestCapability,
454
+ contextDelegateCapability,
455
+ contextExpandReachCapability,
456
+ contextDiscoverGrantableCapability,
457
+ ];
@@ -0,0 +1,22 @@
1
+ /**
2
+ * The tools a cold session gets with their full schemas.
3
+ *
4
+ * One list for both doors. The MCP door imports this as its native
5
+ * set. The hosted brain loads the same set under the SDK names — the `ziggs_`
6
+ * prefix stripped — except the tools that have no hosted twin: the host owns
7
+ * the inbox, and the SDK has no context-grant revoke.
8
+ *
9
+ * Everything else stays callable. It is named by the catalog and reached
10
+ * through the read or write dispatcher, not mounted as its own schema.
11
+ */
12
+ export declare const EVERYDAY_MCP_TOOLS: readonly ["ziggs_inbox", "ziggs_inbox_peek", "ziggs_inbox_ack", "ziggs_open", "ziggs_context_read", "ziggs_chat_open", "ziggs_chat_send", "ziggs_task_set_result", "ziggs_agreement_revoke", "ziggs_context_revoke_grant"];
13
+ /**
14
+ * Irreversible tools stay natively listed, and the write dispatcher refuses
15
+ * them. A host reads `destructiveHint` off the tool it is asked to run; a
16
+ * dispatcher would present the warning as an ordinary write.
17
+ */
18
+ export declare const EVERYDAY_DESTRUCTIVE_MCP: readonly ["ziggs_agreement_revoke", "ziggs_context_revoke_grant"];
19
+ /** Everyday MCP tools the hosted brain does not register. */
20
+ export declare const EVERYDAY_NO_SDK_TWIN: readonly ["ziggs_inbox", "ziggs_inbox_peek", "ziggs_inbox_ack", "ziggs_context_revoke_grant"];
21
+ /** SDK names of the everyday set. Same order as the MCP list, twins only. */
22
+ export declare const EVERYDAY_SDK_TOOLS: readonly string[];
@@ -0,0 +1,50 @@
1
+ /**
2
+ * The tools a cold session gets with their full schemas.
3
+ *
4
+ * One list for both doors. The MCP door imports this as its native
5
+ * set. The hosted brain loads the same set under the SDK names — the `ziggs_`
6
+ * prefix stripped — except the tools that have no hosted twin: the host owns
7
+ * the inbox, and the SDK has no context-grant revoke.
8
+ *
9
+ * Everything else stays callable. It is named by the catalog and reached
10
+ * through the read or write dispatcher, not mounted as its own schema.
11
+ */
12
+ export const EVERYDAY_MCP_TOOLS = [
13
+ 'ziggs_inbox',
14
+ // Count-only orientation: who you are and whether mail is waiting, without
15
+ // taking the mailbox. The full read below is the one that acquires.
16
+ 'ziggs_inbox_peek',
17
+ // The ack is native beside the read it follows: a readPlan ends with it, and
18
+ // a caller that had to go through the catalog to close its loop would pay the
19
+ // dispatcher on every pass.
20
+ 'ziggs_inbox_ack',
21
+ 'ziggs_open',
22
+ 'ziggs_context_read',
23
+ 'ziggs_chat_open',
24
+ 'ziggs_chat_send',
25
+ 'ziggs_task_set_result',
26
+ // And every irreversible tool — see EVERYDAY_DESTRUCTIVE_MCP below.
27
+ 'ziggs_agreement_revoke',
28
+ 'ziggs_context_revoke_grant',
29
+ ];
30
+ /**
31
+ * Irreversible tools stay natively listed, and the write dispatcher refuses
32
+ * them. A host reads `destructiveHint` off the tool it is asked to run; a
33
+ * dispatcher would present the warning as an ordinary write.
34
+ */
35
+ export const EVERYDAY_DESTRUCTIVE_MCP = [
36
+ 'ziggs_agreement_revoke',
37
+ 'ziggs_context_revoke_grant',
38
+ ];
39
+ /** Everyday MCP tools the hosted brain does not register. */
40
+ export const EVERYDAY_NO_SDK_TWIN = [
41
+ 'ziggs_inbox',
42
+ 'ziggs_inbox_peek',
43
+ 'ziggs_inbox_ack',
44
+ 'ziggs_context_revoke_grant',
45
+ ];
46
+ const noSdkTwin = new Set(EVERYDAY_NO_SDK_TWIN);
47
+ /** SDK names of the everyday set. Same order as the MCP list, twins only. */
48
+ export const EVERYDAY_SDK_TOOLS = EVERYDAY_MCP_TOOLS
49
+ .filter((name) => !noSdkTwin.has(name))
50
+ .map((name) => name.slice('ziggs_'.length));
@@ -1,6 +1,6 @@
1
1
  export { type CapabilitySurface, type CapabilityAnnotation, type CapabilityParam, type CapabilityEnv, type CapabilityDefinition, fullCreds, rethrowWithContext, } from './types.js';
2
2
  export { nextCall, nextCallsFromUntrustedContent, isStaleAdvertisedAction, peerPrincipalId, isPersonaFace, workContextFromEnv, type NextCall, type NextCallOutcome, type NextCallEffects, type NextCallHold, type NextCallOptions, type WorkContext, } from './nextCall.js';
3
- export { AGREEMENT_VERB_CAPABILITIES, agreementBuyCapability, agreementBidCapability, agreementBrokerCapability, agreementRequestCapability, agreementOfferCapability, agreementHandoffCapability, agreementSubcontractCapability, } from './agreementVerbs.js';
3
+ export { AGREEMENT_VERB_CAPABILITIES, LISTING_DOOR_CAPABILITIES, listingPauseCapability, listingResumeCapability, agreementBuyCapability, agreementBidCapability, agreementBrokerCapability, agreementRequestCapability, agreementOfferCapability, agreementHandoffCapability, agreementSubcontractCapability, } from './agreementVerbs.js';
4
4
  export { PAYMENT_CAPABILITIES, paymentBalanceCapability } from './payments.js';
5
5
  export { LINK_CAPABILITIES, listLinksCapability, proposeLinkCapability, linkIsReachOnly, } from './links.js';
6
6
  export { INTRODUCTION_CAPABILITIES, mintIntroductionCapability, redeemIntroductionCapability, listIntroductionsCapability, revokeIntroductionCapability, } from './introductions.js';
@@ -11,8 +11,10 @@ export { TASK_CAPABILITIES, listTasksCapability, cancelTaskCapability, presentTa
11
11
  export { AGREEMENT_PROPOSE_PROVIDER_ID_DESCRIPTION } from './proposeProviderId.js';
12
12
  export { MARKETPLACE_CAPABILITIES, marketplaceViewCapability } from './marketplace.js';
13
13
  export { GRANTS_CAPABILITIES, listGrantsCapability } from './grants.js';
14
- export { CONTEXT_CAPABILITIES, contextReadCapability, openCapability, accessExplainCapability, contextDelegateCapability, contextRequestCapability, contextExpandReachCapability, contextDiscoverGrantableCapability, contextBounds, resolveOrgScopeId, } from './context.js';
14
+ export { CONTEXT_CAPABILITIES, contextReadCapability, openCapability, accessExplainCapability, contextDelegateCapability, contextRequestCapability, accessRequestCapability, contextExpandReachCapability, contextDiscoverGrantableCapability, contextBounds, resolveOrgScopeId, } from './context.js';
15
15
  export { CONNECTION_CAPABILITIES, connectionProxyCapability, requestConnectionCapability, } from './connections.js';
16
16
  export { DISCOVERY_CAPABILITIES, agentSearchCapability, agentGetCapability, } from './discovery.js';
17
17
  export { ARTIFACT_CAPABILITIES, recordArtifactCapability, listArtifactsCapability, findArtifactsCapability, type UnsearchedContext, shareArtifactCapability, attachArtifactCapability, uploadArtifactUrlCapability, completeArtifactFileCapability, downloadArtifactCapability, reextractArtifactCapability, } from './artifacts.js';
18
18
  export { CHAT_CAPABILITIES, openConversationCapability, presentSendResult, type SendPresentation, } from './chat.js';
19
+ export { EVERYDAY_MCP_TOOLS, EVERYDAY_DESTRUCTIVE_MCP, EVERYDAY_NO_SDK_TWIN, EVERYDAY_SDK_TOOLS, } from './everyday.js';
20
+ export { annotationForSdkTool } from './toolLane.js';
@@ -1,6 +1,6 @@
1
1
  export { fullCreds, rethrowWithContext, } from './types.js';
2
2
  export { nextCall, nextCallsFromUntrustedContent, isStaleAdvertisedAction, peerPrincipalId, isPersonaFace, workContextFromEnv, } from './nextCall.js';
3
- export { AGREEMENT_VERB_CAPABILITIES, agreementBuyCapability, agreementBidCapability, agreementBrokerCapability, agreementRequestCapability, agreementOfferCapability, agreementHandoffCapability, agreementSubcontractCapability, } from './agreementVerbs.js';
3
+ export { AGREEMENT_VERB_CAPABILITIES, LISTING_DOOR_CAPABILITIES, listingPauseCapability, listingResumeCapability, agreementBuyCapability, agreementBidCapability, agreementBrokerCapability, agreementRequestCapability, agreementOfferCapability, agreementHandoffCapability, agreementSubcontractCapability, } from './agreementVerbs.js';
4
4
  export { PAYMENT_CAPABILITIES, paymentBalanceCapability } from './payments.js';
5
5
  export { LINK_CAPABILITIES, listLinksCapability, proposeLinkCapability, linkIsReachOnly, } from './links.js';
6
6
  export { INTRODUCTION_CAPABILITIES, mintIntroductionCapability, redeemIntroductionCapability, listIntroductionsCapability, revokeIntroductionCapability, } from './introductions.js';
@@ -11,8 +11,10 @@ export { TASK_CAPABILITIES, listTasksCapability, cancelTaskCapability, presentTa
11
11
  export { AGREEMENT_PROPOSE_PROVIDER_ID_DESCRIPTION } from './proposeProviderId.js';
12
12
  export { MARKETPLACE_CAPABILITIES, marketplaceViewCapability } from './marketplace.js';
13
13
  export { GRANTS_CAPABILITIES, listGrantsCapability } from './grants.js';
14
- export { CONTEXT_CAPABILITIES, contextReadCapability, openCapability, accessExplainCapability, contextDelegateCapability, contextRequestCapability, contextExpandReachCapability, contextDiscoverGrantableCapability, contextBounds, resolveOrgScopeId, } from './context.js';
14
+ export { CONTEXT_CAPABILITIES, contextReadCapability, openCapability, accessExplainCapability, contextDelegateCapability, contextRequestCapability, accessRequestCapability, contextExpandReachCapability, contextDiscoverGrantableCapability, contextBounds, resolveOrgScopeId, } from './context.js';
15
15
  export { CONNECTION_CAPABILITIES, connectionProxyCapability, requestConnectionCapability, } from './connections.js';
16
16
  export { DISCOVERY_CAPABILITIES, agentSearchCapability, agentGetCapability, } from './discovery.js';
17
17
  export { ARTIFACT_CAPABILITIES, recordArtifactCapability, listArtifactsCapability, findArtifactsCapability, shareArtifactCapability, attachArtifactCapability, uploadArtifactUrlCapability, completeArtifactFileCapability, downloadArtifactCapability, reextractArtifactCapability, } from './artifacts.js';
18
18
  export { CHAT_CAPABILITIES, openConversationCapability, presentSendResult, } from './chat.js';
19
+ export { EVERYDAY_MCP_TOOLS, EVERYDAY_DESTRUCTIVE_MCP, EVERYDAY_NO_SDK_TWIN, EVERYDAY_SDK_TOOLS, } from './everyday.js';
20
+ export { annotationForSdkTool } from './toolLane.js';
@@ -0,0 +1,3 @@
1
+ import type { CapabilityAnnotation } from './types.js';
2
+ /** Annotation of a shared capability, by its SDK tool name. */
3
+ export declare function annotationForSdkTool(name: string): CapabilityAnnotation | undefined;
@@ -0,0 +1,39 @@
1
+ import { AGREEMENT_CAPABILITIES } from './agreements.js';
2
+ import { AGREEMENT_VERB_CAPABILITIES, LISTING_DOOR_CAPABILITIES, agreementSubcontractCapability, } from './agreementVerbs.js';
3
+ import { ARTIFACT_CAPABILITIES } from './artifacts.js';
4
+ import { CHAT_CAPABILITIES } from './chat.js';
5
+ import { CONNECTION_CAPABILITIES } from './connections.js';
6
+ import { CONTEXT_CAPABILITIES } from './context.js';
7
+ import { DISCOVERY_CAPABILITIES } from './discovery.js';
8
+ import { GRANTS_CAPABILITIES } from './grants.js';
9
+ import { INTRODUCTION_CAPABILITIES } from './introductions.js';
10
+ import { LINK_CAPABILITIES } from './links.js';
11
+ import { MARKETPLACE_CAPABILITIES } from './marketplace.js';
12
+ import { PAYMENT_CAPABILITIES } from './payments.js';
13
+ import { TASK_CAPABILITIES } from './tasks.js';
14
+ const CAPABILITIES = [
15
+ ...AGREEMENT_CAPABILITIES,
16
+ ...AGREEMENT_VERB_CAPABILITIES,
17
+ agreementSubcontractCapability,
18
+ ...LISTING_DOOR_CAPABILITIES,
19
+ ...ARTIFACT_CAPABILITIES,
20
+ ...CHAT_CAPABILITIES,
21
+ ...CONNECTION_CAPABILITIES,
22
+ ...CONTEXT_CAPABILITIES,
23
+ ...DISCOVERY_CAPABILITIES,
24
+ ...GRANTS_CAPABILITIES,
25
+ ...INTRODUCTION_CAPABILITIES,
26
+ ...LINK_CAPABILITIES,
27
+ ...MARKETPLACE_CAPABILITIES,
28
+ ...PAYMENT_CAPABILITIES,
29
+ ...TASK_CAPABILITIES,
30
+ ];
31
+ const bySdkName = new Map();
32
+ for (const cap of CAPABILITIES) {
33
+ if (!bySdkName.has(cap.names.sdk))
34
+ bySdkName.set(cap.names.sdk, cap.annotation);
35
+ }
36
+ /** Annotation of a shared capability, by its SDK tool name. */
37
+ export function annotationForSdkTool(name) {
38
+ return bySdkName.get(name);
39
+ }
@@ -13,9 +13,10 @@ import { type Creds } from '../types.js';
13
13
  * with without a second call.
14
14
  */
15
15
  export type ChatSummary = ChatReadDto;
16
- export declare function openConversation(participantId: string, creds: Creds, { newChat, agreementId }?: {
16
+ export declare function openConversation(participantId: string, creds: Creds, { newChat, agreementId, createIfMissing }?: {
17
17
  newChat?: boolean;
18
18
  agreementId?: string;
19
+ createIfMissing?: boolean;
19
20
  }): Promise<{
20
21
  chatId: string;
21
22
  reused?: boolean;
@@ -34,9 +35,8 @@ export interface SendChatMessageInput {
34
35
  to?: string;
35
36
  /**
36
37
  * Recipient id. Optional: when omitted, the backend infers the
37
- * receiver if the chat has exactly one other member (one agent, or one
38
- * other human). Provide it explicitly in chats with multiple members.
39
- * Sent on the wire as `{ id }`; the SDK still takes a string.
38
+ * receiver when Jev is confident. Otherwise it stores room context without
39
+ * a specific wake. Explicit addressing is required for a guaranteed target.
40
40
  */
41
41
  receiverId?: string;
42
42
  text: string;
@@ -47,7 +47,6 @@ export interface SendChatMessageInput {
47
47
  /** Governing service agreement; independent of representation. */
48
48
  conversationAgreementId?: string;
49
49
  taskId?: string;
50
- replyToMessageId?: string;
51
50
  }
52
51
  /**
53
52
  * Where the server says a send landed.
@@ -122,6 +121,7 @@ export declare class ChatClient {
122
121
  open(participantId: string, opts?: {
123
122
  newChat?: boolean;
124
123
  agreementId?: string;
124
+ createIfMissing?: boolean;
125
125
  }): Promise<{
126
126
  chatId: string;
127
127
  reused?: boolean;
@@ -10,7 +10,7 @@ function assertCreds(creds, op) {
10
10
  if (!creds?.agentId)
11
11
  throw new Error(`agentId is required for ${op}`);
12
12
  }
13
- export async function openConversation(participantId, creds, { newChat = false, agreementId } = {}) {
13
+ export async function openConversation(participantId, creds, { newChat = false, agreementId, createIfMissing } = {}) {
14
14
  if (!participantId)
15
15
  throw new Error('participantId is required for openConversation');
16
16
  assertCreds(creds, 'open conversation');
@@ -22,6 +22,7 @@ export async function openConversation(participantId, creds, { newChat = false,
22
22
  body: JSON.stringify({
23
23
  participantId,
24
24
  ...(newChat ? { newChat: true } : {}),
25
+ ...(createIfMissing !== undefined ? { createIfMissing } : {}),
25
26
  ...(agreementId ? { agreementId } : {}),
26
27
  }),
27
28
  });
@@ -107,7 +108,6 @@ export async function sendChatMessage(input, creds) {
107
108
  underAgreementId: input.underAgreementId,
108
109
  conversationAgreementId: input.conversationAgreementId,
109
110
  taskId: input.taskId,
110
- replyToMessageId: input.replyToMessageId,
111
111
  }),
112
112
  });
113
113
  if (!res.ok) {
@@ -96,6 +96,24 @@ export declare class ContextGrantsClient {
96
96
  */
97
97
  constructor(operatorKey: string, agentId?: string, baseUrl?: string);
98
98
  issueGrant(input: IssueContextGrantInput): Promise<GrantView>;
99
+ /** Request-only endpoint: a successful call must still have granted no access. */
100
+ requestAccess(input: {
101
+ scopeKind: 'artifact' | 'chat' | 'agreement' | 'task';
102
+ scopeId: string;
103
+ reason: string;
104
+ permission?: 'read' | 'reply';
105
+ temporal?: 'from-now' | 'from-start';
106
+ durationHours?: number;
107
+ chatId?: string;
108
+ ownerId?: string;
109
+ }, opts?: {
110
+ idempotencyKey?: string;
111
+ laneId?: string;
112
+ }): Promise<{
113
+ status: 'pending_approval';
114
+ agreementId: string;
115
+ accessGranted: false;
116
+ }>;
99
117
  requestArtifactAccess(artifactId: string, input: {
100
118
  chatId: string;
101
119
  ownerId: string;
@@ -63,6 +63,25 @@ export class ContextGrantsClient {
63
63
  }
64
64
  return parsed.grant;
65
65
  }
66
+ /** Request-only endpoint: a successful call must still have granted no access. */
67
+ async requestAccess(input, opts) {
68
+ const res = await fetch(`${this.baseUrl}/context/access/requests`, {
69
+ method: 'POST',
70
+ headers: buildOperatorHeaders(this.operatorKey, this.agentId, {
71
+ 'content-type': 'application/json',
72
+ ...(opts?.idempotencyKey ? { 'Idempotency-Key': opts.idempotencyKey } : {}),
73
+ }, opts?.laneId),
74
+ body: JSON.stringify(input),
75
+ });
76
+ const body = await res.text().catch(() => '');
77
+ if (!res.ok)
78
+ throwApiError(res, body, `requestAccess failed: ${res.status}`);
79
+ const result = JSON.parse(body);
80
+ if (result.status !== 'pending_approval' || !result.agreementId || result.accessGranted !== false) {
81
+ throw new Error('Expected a pending access request with accessGranted=false');
82
+ }
83
+ return { status: 'pending_approval', agreementId: result.agreementId, accessGranted: false };
84
+ }
66
85
  async requestArtifactAccess(artifactId, input, opts) {
67
86
  const res = await fetch(`${this.baseUrl}/context/artifacts/${encodeURIComponent(artifactId)}/access-requests`, {
68
87
  method: 'POST',
@@ -32,6 +32,13 @@ export interface ContextOpenAccess {
32
32
  continuation?: {
33
33
  kind: 'owner_decision';
34
34
  summary: string;
35
+ request?: {
36
+ scopeKind: 'chat' | 'agreement' | 'task';
37
+ scopeId: string;
38
+ permission: 'read' | 'reply';
39
+ temporal: 'from-now' | 'from-start';
40
+ durationHours: number;
41
+ };
35
42
  };
36
43
  }
37
44
  export interface ContextOpenResult {
@@ -67,6 +67,15 @@ export interface ContextSnapshotResult {
67
67
  contentType: string;
68
68
  timestamp: string | null;
69
69
  }>;
70
+ /** Direct artifact grants missing from the room inventory. Id and name only. */
71
+ grantedArtifacts?: {
72
+ items: Array<{
73
+ artifactId: string;
74
+ name: string | null;
75
+ via: string;
76
+ }>;
77
+ omitted: number;
78
+ };
70
79
  agents: Array<ContextSnapshotParticipant & {
71
80
  agentId: string;
72
81
  }>;
@@ -19,6 +19,11 @@ export interface PublishOfferPayload {
19
19
  billing?: 'total' | 'per_task';
20
20
  /** Broadcast audience: 'everyone' (default, fully public) or 'org' (members of your active org only). */
21
21
  audience?: BroadcastAudience;
22
+ /**
23
+ * How many customers may hold a live hire from this listing at once. A hire
24
+ * that ends gives its seat back. Omit for unlimited.
25
+ */
26
+ maxClaims?: number;
22
27
  metadata?: Record<string, unknown>;
23
28
  /**
24
29
  * Replay handle, sent as the `Idempotency-Key` header — the same way
@@ -29,6 +34,14 @@ export interface PublishOfferPayload {
29
34
  idempotencyKey?: string;
30
35
  }
31
36
  export declare function publishOffer(payload: PublishOfferPayload, creds: Creds): Promise<Agreement>;
37
+ /**
38
+ * Stop taking new customers on a listing you publish, without deleting it. It
39
+ * leaves the board and claims on it are refused until you resume it; hires
40
+ * already sold keep running. The server refuses anyone but the publisher.
41
+ */
42
+ export declare function pauseOffer(agreementId: string, creds: Creds): Promise<Agreement>;
43
+ /** Reopen a paused listing: same row, same id, back on the board. */
44
+ export declare function resumeOffer(agreementId: string, creds: Creds): Promise<Agreement>;
32
45
  export interface PullOffersOptions {
33
46
  limit?: number;
34
47
  since?: string;
@@ -47,6 +60,8 @@ export declare class MarketplaceClient {
47
60
  */
48
61
  constructor(operatorKey: string, agentId?: string);
49
62
  publishOffer(payload: PublishOfferPayload): Promise<Agreement>;
63
+ pauseOffer(agreementId: string): Promise<Agreement>;
64
+ resumeOffer(agreementId: string): Promise<Agreement>;
50
65
  pullOffers(options?: PullOffersOptions): Promise<Agreement[]>;
51
66
  pullRequests(options?: PullRequestsOptions): Promise<Agreement[]>;
52
67
  }
@@ -35,6 +35,33 @@ export async function publishOffer(payload, creds) {
35
35
  }
36
36
  return shapeAgreement(data['agreement']);
37
37
  }
38
+ /**
39
+ * Stop taking new customers on a listing you publish, without deleting it. It
40
+ * leaves the board and claims on it are refused until you resume it; hires
41
+ * already sold keep running. The server refuses anyone but the publisher.
42
+ */
43
+ export async function pauseOffer(agreementId, creds) {
44
+ return setOfferPaused(agreementId, 'pause', creds);
45
+ }
46
+ /** Reopen a paused listing: same row, same id, back on the board. */
47
+ export async function resumeOffer(agreementId, creds) {
48
+ return setOfferPaused(agreementId, 'resume', creds);
49
+ }
50
+ async function setOfferPaused(agreementId, verb, creds) {
51
+ if (!agreementId)
52
+ throw new Error(`agreementId is required to ${verb} a listing`);
53
+ assertCreds(creds, `marketplace offer ${verb}`);
54
+ const res = await fetch(`${getMarketplaceBaseUrl()}/offers/${encodeURIComponent(agreementId)}/${verb}`, { method: 'POST', headers: buildHeaders(creds), body: JSON.stringify({}) });
55
+ if (!res.ok) {
56
+ const body = await res.text().catch(() => '');
57
+ throwApiError(res, body, `Marketplace offer ${verb} failed: ${res.status}`);
58
+ }
59
+ const data = await res.json().catch(() => null);
60
+ if (!data?.['agreement']) {
61
+ throw new Error(`Invalid response: expected { agreement } from POST /marketplace/offers/:id/${verb}`);
62
+ }
63
+ return shapeAgreement(data['agreement']);
64
+ }
38
65
  export async function pullOffers(options, creds) {
39
66
  assertCreds(creds, 'marketplace offers pull');
40
67
  const params = new URLSearchParams();
@@ -87,6 +114,8 @@ export class MarketplaceClient {
87
114
  this.creds = { operatorKey, agentId };
88
115
  }
89
116
  publishOffer(payload) { return publishOffer(payload, this.creds); }
117
+ pauseOffer(agreementId) { return pauseOffer(agreementId, this.creds); }
118
+ resumeOffer(agreementId) { return resumeOffer(agreementId, this.creds); }
90
119
  pullOffers(options) { return pullOffers(options, this.creds); }
91
120
  pullRequests(options) { return pullRequests(options, this.creds); }
92
121
  }
package/dist/types.d.ts CHANGED
@@ -244,6 +244,12 @@ export interface Agreement {
244
244
  } | null;
245
245
  /** Set on a link that was formed by claiming the invite with this id. */
246
246
  linkInviteTemplateId?: string | null;
247
+ /**
248
+ * When the publisher paused this listing, or null while it takes customers.
249
+ * Only a listing carries it; a paused listing refuses new claims until it is
250
+ * resumed, and hires already sold from it keep running.
251
+ */
252
+ listingPausedAt?: string | null;
247
253
  /**
248
254
  * the provider slot is FIXED (a hand-off): claiming or
249
255
  * approving this makes you the party the work is done FOR, not the worker —
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ziggs-ai/api-client",
3
- "version": "0.23.1",
3
+ "version": "0.25.0",
4
4
  "description": "HTTP and WebSocket client for the Ziggs backend API",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",