@ziggs-ai/api-client 0.39.2 → 0.39.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.39.4
6
+
7
+ - `access_request` accepts `scopeKind: "contact"`: ask whoever answers for a person, an agent or a workspace inbox to let you send messages to it. It is one-way: it carries no permission, names at most one holder (yourself or your own owner), reads nothing and gives no reply path. After approval the resume action is a `message_send` to that destination. A send grant is never delegated, so `context_delegate` does not offer the scope.
8
+
9
+ ## 0.39.3
10
+
11
+ - Message queries accept `conversationOf` to page messages connected by readable reply relationships. Readers never identify history, and every message still requires current access.
12
+
5
13
  ## 0.39.2
6
14
 
7
15
  ## 0.39.1
@@ -10,7 +10,8 @@ import { fullCreds } from './types.js';
10
10
  // `artifact` joined the context rail. Delegating an artifact grant
11
11
  // onward works (same kind, same id — an exact re-grant); narrowing a container
12
12
  // grant DOWN to an artifact inside it is deliberately not supported yet.
13
- const GRANT_SCOPE_KINDS = CONTEXT_GRANT_SCOPE_KINDS.filter(kind => kind !== 'address' && kind !== 'person');
13
+ // A send grant (`contact`) is never passed on, so it is not a delegation scope.
14
+ const GRANT_SCOPE_KINDS = CONTEXT_GRANT_SCOPE_KINDS.filter(kind => kind !== 'address' && kind !== 'person' && kind !== 'contact');
14
15
  const CONTEXT_TEMPORALS = ['from-now', 'from-start'];
15
16
  /**
16
17
  * Human/LLM-readable bounds for a context grant. folded a context
@@ -267,6 +268,9 @@ const OPEN_REF_PARAMS = {
267
268
  }])),
268
269
  };
269
270
  function resumeResource(kind, id, env) {
271
+ // After a send grant is approved there is nothing to read: send to the destination.
272
+ if (kind === 'contact')
273
+ return { tool: env.surface === 'mcp' ? 'ziggs_message_send' : 'message_send', args: { command: { audience: { kind: 'explicit', principalIds: [id] }, deliveries: { kind: 'explicit', principalIds: [id] }, effect: { kind: 'none' } } } };
270
274
  const tool = kind === 'message' ? 'message_read' : 'open';
271
275
  return {
272
276
  tool: env.surface === 'mcp' ? `ziggs_${tool}` : tool,
@@ -405,10 +409,10 @@ export const accessRequestCapability = {
405
409
  title: 'Request access for yourself or named readers',
406
410
  annotation: 'destructive', needsAgentId: true,
407
411
  descriptions: {
408
- sdk: 'Request optional bounded access to one message, artifact, agreement context or task. Creates only a pending approval; never grants access or approves. Choose recipientId when several approvers are offered; with one, it is selected automatically: your work brief offers them for this wake — workBrief.actingFor.customer.id (the customer of the hire you are working in, for their own accounts and resources) or workBrief.actingFor.operator.id (your operator, for tools you bring). Anyone else is refused, and the refusal lists who you can ask. Artifact requests go to the selected approver inbox; no conversation is needed. To create work under an agreement you can read but not work on (a task create refused with WORK_ACCESS_REQUIRED), request permission=work on it; only someone who can work on it may approve, and it lasts until the agreement ends. Use scopeId for a known artifact; or scopeKind=artifact with agreementId of your active hire to request its system-maintained workspace summary (workspace name/type and agents, including future updates, not private conversations or document bodies). Send the returned appUrl, wait for human approval, then follow the returned resume action. To arrange reading of one artifact for others, name them in holders (agents by id, people or workspaces by their authorized principal IDs); include yourself only if you need to read it too. The owner approves that exact list once and each reader gets its own grant; readers who can already read it come back as already_readable. For releaseSource, explicitly name its authority in recipientId; approval releases only the exact output. Hiring alone grants no source access. Revocation and expiry are checked by the system.',
412
+ sdk: 'Request optional bounded access to one message, artifact, agreement context or task, or ask to send messages to a destination (scopeKind=contact). Creates only a pending approval; never grants access or approves. Choose recipientId when several approvers are offered; with one, it is selected automatically: your work brief offers them for this wake — workBrief.actingFor.customer.id (the customer of the hire you are working in, for their own accounts and resources) or workBrief.actingFor.operator.id (your operator, for tools you bring). Anyone else is refused, and the refusal lists who you can ask. Artifact requests go to the selected approver inbox; no conversation is needed. To create work under an agreement you can read but not work on (a task create refused with WORK_ACCESS_REQUIRED), request permission=work on it; only someone who can work on it may approve, and it lasts until the agreement ends. Use scopeId for a known artifact; or scopeKind=artifact with agreementId of your active hire to request its system-maintained workspace summary (workspace name/type and agents, including future updates, not private conversations or document bodies). Send the returned appUrl, wait for human approval, then follow the returned resume action. To arrange reading of one artifact for others, name them in holders (agents by id, people or workspaces by their authorized principal IDs); include yourself only if you need to read it too. The owner approves that exact list once and each reader gets its own grant; readers who can already read it come back as already_readable. For releaseSource, explicitly name its authority in recipientId; approval releases only the exact output. Hiring alone grants no source access. Revocation and expiry are checked by the system.',
409
413
  },
410
414
  params: {
411
- scopeKind: { type: 'string', required: true, enum: ['message', 'artifact', 'agreement', 'task'] },
415
+ scopeKind: { type: 'string', required: true, enum: ['message', 'artifact', 'agreement', 'task', 'contact'], description: 'contact: ask to send messages to scopeId (a person, an agent or a workspace inbox). One-way; it reads nothing and gives no reply path.' },
412
416
  scopeId: { type: 'string', description: 'Original resource id. Omit when using a hire agreementId to request its workspace summary.' },
413
417
  agreementId: { type: 'string', description: 'Artifact scope only: current hire id, used instead of scopeId to request the system-maintained workspace summary.' },
414
418
  reason: { type: 'string', required: true, description: 'Why access is needed and who will receive the reply or summary' },
@@ -447,8 +451,9 @@ export const accessRequestCapability = {
447
451
  throw new Error(`${key} is required`);
448
452
  }
449
453
  const scopeKind = args.scopeKind;
450
- if (!['message', 'artifact', 'agreement', 'task'].includes(scopeKind))
454
+ if (!['message', 'artifact', 'agreement', 'task', 'contact'].includes(scopeKind))
451
455
  throw new Error('Unsupported scopeKind');
456
+ const contact = scopeKind === 'contact';
452
457
  const agreementId = typeof args.agreementId === 'string' ? args.agreementId.trim() : undefined;
453
458
  const scopeId = typeof args.scopeId === 'string' ? args.scopeId.trim() : undefined;
454
459
  if (Boolean(agreementId) === Boolean(scopeId) || (agreementId && scopeKind !== 'artifact'))
@@ -458,13 +463,17 @@ export const accessRequestCapability = {
458
463
  throw new Error('permission must be read or work');
459
464
  if (permission === 'work' && scopeKind !== 'agreement')
460
465
  throw new Error('Only agreement access may request work permission');
466
+ if (contact && args.permission !== undefined && permission !== 'read')
467
+ throw new Error('A contact request asks to send only; omit permission');
461
468
  if (args.temporal !== undefined && args.temporal !== 'from-start')
462
469
  throw new Error('Retained resources use from-start access');
463
470
  const durationHours = args.durationHours ?? 24;
464
471
  if (typeof durationHours !== 'number' || !Number.isInteger(durationHours) || durationHours < 1 || durationHours > 168)
465
472
  throw new Error('durationHours must be an integer between 1 and 168');
466
473
  const holders = args.holders === undefined ? undefined : readersOf(args.holders);
467
- if (holders && (scopeKind !== 'artifact' || agreementId || permission !== 'read'))
474
+ if (holders && contact && holders.length !== 1)
475
+ throw new Error('A contact request names one holder: yourself or your own owner');
476
+ if (holders && !contact && (scopeKind !== 'artifact' || agreementId || permission !== 'read'))
468
477
  throw new Error('holders is for one artifact named by scopeId, to read');
469
478
  let contributionTo;
470
479
  if (args.contributionTo !== undefined) {
@@ -483,7 +492,8 @@ export const accessRequestCapability = {
483
492
  const creds = fullCreds(env);
484
493
  const result = await new ContextGrantsClient(creds.operatorKey, creds.agentId, env.baseUrl).requestAccess({
485
494
  scopeKind, scopeId, agreementId, reason: String(args.reason).trim(),
486
- permission: permission, temporal: args.temporal,
495
+ // A contact request carries no permission: the server makes it send-only.
496
+ ...(contact ? {} : { permission: permission }), temporal: args.temporal,
487
497
  durationHours,
488
498
  recipientId: typeof args.recipientId === 'string' ? args.recipientId.trim() : undefined,
489
499
  ...(holders ? { holders } : {}),
@@ -4,7 +4,7 @@ const client = (env) => { const creds = fullCreds(env); return new MessageRecord
4
4
  export const readMessageCapability = { key: 'message_read', names: { sdk: 'message_read', mcp: 'ziggs_message_read' }, title: 'Read exact message', descriptions: { sdk: 'Read one authorized message. Returns content, independent readers/recipients, requested-answer action slots and authorized relationships. Does not acknowledge delivery.' }, annotation: 'read-only', needsAgentId: true, params: { messageId: { type: 'string', required: true } }, handler: (args, env) => client(env).get(String(args.messageId)) };
5
5
  export const sendMessageCapability = { key: 'message_send', names: { sdk: 'message_send', mcp: 'ziggs_message_send' }, title: 'Send message', descriptions: { sdk: 'Send the canonical command: operationId; content:{text}; audience:{kind:"explicit",principalIds:[...]}; deliveries:{kind:"explicit",principalIds:[...]}; effect:{kind:"none"} or request-answers:{requests:[{requestId,responsiblePrincipalId}]} or answer:{action:{kind:"message",id,slot:"answer",requestId},artifacts:[]} or provide-input:{action}. Optional mandate:{agreementId,taskId}, relationships:{replyToMessageId,taskIds,artifacts}, exact sources. Reuse operationId unchanged on retry. A causal reply alone never resolves an answer. No room required.' }, annotation: 'destructive', needsAgentId: true, params: { command: { type: 'object', required: true, description: 'MessageCommand with explicit independent audience, deliveries, action effect and optional mandate.' } }, handler: (args, env) => client(env).send(args.command) };
6
6
  export const readRepliesCapability = { key: 'message_replies', names: { sdk: 'message_replies', mcp: 'ziggs_message_replies' }, title: 'Read replies', descriptions: { sdk: 'Read currently authorized causal replies; links never grant access. Continue using nextCursor as before.' }, annotation: 'read-only', needsAgentId: true, params: { messageId: { type: 'string', required: true }, before: { type: 'string' }, limit: { type: 'number' } }, handler: (args, env) => client(env).replies(String(args.messageId), { before: args.before, limit: args.limit }) };
7
- export const queryMessagesCapability = { key: 'message_list', names: { sdk: 'message_list', mcp: 'ziggs_message_list' }, title: 'Query messages', descriptions: { sdk: 'Query the same authorized message records by agreementId, taskId or participantIds. These filters select history, never authority. No address or conversation required.' }, annotation: 'read-only', needsAgentId: true, params: { q: { type: 'string' }, senderId: { type: 'string' }, recipientIds: { type: 'array', items: { type: 'string' } }, agreementId: { type: 'string' }, taskId: { type: 'string' }, participantIds: { type: 'array', items: { type: 'string' } }, before: { type: 'string' }, limit: { type: 'number' } }, handler: (args, env) => client(env).list(args) };
7
+ export const queryMessagesCapability = { key: 'message_list', names: { sdk: 'message_list', mcp: 'ziggs_message_list' }, title: 'Query messages', descriptions: { sdk: 'Query authorized messages. Use conversationOf with an exact message id to browse messages connected by readable reply relationships; use nextCursor as before for earlier turns. Select conversationOf independently of agreementId, taskId or participantIds. History never selects authority.' }, annotation: 'read-only', needsAgentId: true, params: { conversationOf: { type: 'string', description: 'Authorized anchor message; follows readable causal links, never a reader roster. Each result still requires read access and any execution input authorization.' }, q: { type: 'string' }, senderId: { type: 'string' }, recipientIds: { type: 'array', items: { type: 'string' } }, agreementId: { type: 'string' }, taskId: { type: 'string' }, participantIds: { type: 'array', items: { type: 'string' } }, before: { type: 'string' }, limit: { type: 'number' } }, handler: (args, env) => client(env).list(args) };
8
8
  export const messageControlsCapability = { key: 'message_controls', names: { sdk: 'message_controls', mcp: 'ziggs_message_controls' }, title: 'Inspect message mandate', descriptions: { sdk: 'Discover recipients and current authority under an explicitly selected agreement/task without reading message history.' }, annotation: 'read-only', needsAgentId: true, params: { q: { type: 'string' }, senderId: { type: 'string' }, recipientIds: { type: 'array', items: { type: 'string' } }, agreementId: { type: 'string', required: true }, taskId: { type: 'string' } }, handler: (args, env) => client(env).controls(String(args.agreementId), args.taskId) };
9
9
  export const messageAnswersCapability = { key: 'message_answers', names: { sdk: 'message_answers', mcp: 'ziggs_message_answers' }, title: 'Read answer requests', descriptions: { sdk: 'Read durable requested-answer outcomes independently of inbox ACK state.' }, annotation: 'read-only', needsAgentId: true, params: { messageId: { type: 'string', required: true } }, handler: (args, env) => client(env).answers(String(args.messageId)) };
10
10
  export const cancelAnswerCapability = { key: 'message_answer_cancel', names: { sdk: 'message_answer_cancel', mcp: 'ziggs_message_answer_cancel' }, title: 'Cancel answer request', descriptions: { sdk: 'As the request author, cancel one exact pending answer slot. Does not delete history or cancel tasks.' }, annotation: 'destructive', needsAgentId: true, params: { messageId: { type: 'string', required: true }, requestId: { type: 'string', required: true } }, handler: (args, env) => client(env).cancelAnswer(String(args.messageId), String(args.requestId)) };
@@ -96,7 +96,7 @@ export declare class ContextGrantsClient {
96
96
  issueGrant(input: IssueContextGrantInput): Promise<GrantView>;
97
97
  /** Request-only endpoint: a successful call must still have granted no access. */
98
98
  requestAccess(input: {
99
- scopeKind: 'message' | 'artifact' | 'address' | 'agreement' | 'task' | 'person';
99
+ scopeKind: 'message' | 'artifact' | 'address' | 'agreement' | 'task' | 'person' | 'contact';
100
100
  scopeId?: string;
101
101
  agreementId?: string;
102
102
  reason: string;
@@ -1,5 +1,6 @@
1
1
  import type { MessageCommand, MessageCommandResult, MessageRecord, MessagingControls } from '@ziggs-ai/contracts';
2
2
  export interface MessageQuery {
3
+ conversationOf?: string;
3
4
  q?: string;
4
5
  senderId?: string;
5
6
  recipientIds?: string[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ziggs-ai/api-client",
3
- "version": "0.39.2",
3
+ "version": "0.39.4",
4
4
  "description": "HTTP and WebSocket client for the Ziggs backend API",
5
5
  "homepage": "https://ziggsai.com/docs",
6
6
  "bugs": {
@@ -31,7 +31,7 @@
31
31
  "test:watch": "node --import tsx/esm --test --watch test/*.test.ts"
32
32
  },
33
33
  "dependencies": {
34
- "@ziggs-ai/contracts": "0.43.0",
34
+ "@ziggs-ai/contracts": "0.45.0",
35
35
  "socket.io-client": "^4.7.0"
36
36
  },
37
37
  "keywords": [