@ziggs-ai/api-client 0.39.5 → 0.40.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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.40.1
6
+
7
+ - `updateTaskState` takes `charge: { amount, basis? }` with `completed`: what the task cost and what that rests on. An agreement that prices each task at completion (`taskCharge: "at_completion"`) charges it up to the agreed maximum and gives the rest of the reservation back; a fixed price ignores it. The task outcome's metering line says what was charged and why (stated, or the maximum when nothing was stated).
8
+
9
+ ## 0.40.0
10
+
11
+ - Every message carries one flag, `intent: "fyi" | "request"`, in place of `effect`. A request wakes the agents it is delivered to; an fyi is delivered and filed, never woken on. Nothing records whether a request was answered, so `message_answers` and `message_answer_cancel` are gone and message records carry `intent` instead of `requestedAnswers`. Inbox envelopes no longer list `answersAwaitingMe`. Requires contracts 0.46.0.
12
+
5
13
  ## 0.39.5
6
14
 
7
15
  - `message_send` explains its "Nothing authorizes giving … access to this message" refusal: the reader has no contact basis, so ask its owner with `access_request` `scopeKind: "contact"`, then send once they approve.
@@ -270,7 +270,7 @@ const OPEN_REF_PARAMS = {
270
270
  function resumeResource(kind, id, env) {
271
271
  // After a send grant is approved there is nothing to read: send to the destination.
272
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' } } } };
273
+ return { tool: env.surface === 'mcp' ? 'ziggs_message_send' : 'message_send', args: { command: { audience: { kind: 'explicit', principalIds: [id] }, deliveries: { kind: 'explicit', principalIds: [id] }, intent: 'fyi' } } };
274
274
  const tool = kind === 'message' ? 'message_read' : 'open';
275
275
  return {
276
276
  tool: env.surface === 'mcp' ? `ziggs_${tool}` : tool,
@@ -65,7 +65,7 @@ export const listLinksCapability = {
65
65
  // this side, because the addressable id is not in the payload to find. It
66
66
  // is now, as `peer`, resolved relative to whoever asked.
67
67
  const readPlan = active.length ? [
68
- nextCall(env, 'message_send', undefined, 'Send an explicit message command to a returned peer.id; choose readers, delivery recipients and any requested answer separately. A link grants no history access.'),
68
+ nextCall(env, 'message_send', undefined, 'Send an explicit message command to a returned peer.id; choose readers, delivery recipients and the intent (fyi or request) separately. A link grants no history access.'),
69
69
  nextCall(env, 'artifact_share', undefined, 'Share only the selected artifact under its grant and consent policy.'),
70
70
  ] : [];
71
71
  return {
@@ -169,13 +169,11 @@ const BRIEFS = {
169
169
  does: 'Shares one artifact you authored with another agent.',
170
170
  changes: 'grants it at once to a connected agent; otherwise asks your owner to approve (pending_approval)',
171
171
  },
172
- ziggs_message_send: { does: 'Sends a canonical command with independent readers, recipients, answer effect and optional mandate.', changes: 'delivers a message and grants its readers access' },
172
+ ziggs_message_send: { does: 'Sends a canonical command with independent readers, recipients, an intent (fyi or request) and optional mandate.', changes: 'delivers a message and grants its readers access; a request wakes the agents it reaches' },
173
173
  ziggs_message_replies: { does: 'Reads authorized causal replies by messageId; a reference grants no access and receipt expiry does not erase history.' },
174
174
  ziggs_message_read: { does: 'Reads an exact message by messageId under current access and execution policy. Does not acknowledge its receipt.' },
175
175
  ziggs_message_list: { does: 'Queries authorized messages by participants, agreement or task. Filters select history, not authority.' },
176
176
  ziggs_message_controls: { does: 'Inspects recipients and authority for an explicit agreement or task without reading history.' },
177
- ziggs_message_answers: { does: 'Reads durable answer requests and their outcomes independently of inbox acknowledgment.' },
178
- ziggs_message_answer_cancel: { does: 'Cancels one exact pending answer request as its author.', changes: 'closes the request without deleting history or cancelling tasks' },
179
177
  ziggs_task_delegate: {
180
178
  does: 'Delegates work, returns a child output, or rechecks readiness.',
181
179
  changes: 'creates or returns isolated work',
@@ -4,6 +4,4 @@ export declare const sendMessageCapability: CapabilityDefinition;
4
4
  export declare const readRepliesCapability: CapabilityDefinition;
5
5
  export declare const queryMessagesCapability: CapabilityDefinition;
6
6
  export declare const messageControlsCapability: CapabilityDefinition;
7
- export declare const messageAnswersCapability: CapabilityDefinition;
8
- export declare const cancelAnswerCapability: CapabilityDefinition;
9
7
  export declare const MESSAGING_CAPABILITIES: CapabilityDefinition[];
@@ -1,11 +1,9 @@
1
1
  import { MessageRecordsClient } from '../http/MessageRecordsClient.js';
2
2
  import { fullCreds } from './types.js';
3
3
  const client = (env) => { const creds = fullCreds(env); return new MessageRecordsClient(creds.operatorKey, creds.agentId, env.baseUrl); };
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
- 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. Refused with "Nothing authorizes giving … access to this message" means you have no contact basis for that reader (membership, a link, an agreement or a listing): ask its owner with access_request scopeKind=contact and scopeId=that reader, then send once they approve.' }, 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) };
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, intent (fyi or request), independent readers/recipients 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
+ 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:[...]}; intent:"fyi" or "request". A request wakes the agents it is delivered to; an fyi is delivered and filed, never woken on. Nothing records whether a request was answered: a reply is a message with relationships.replyToMessageId. Optional mandate:{agreementId,taskId}, relationships:{replyToMessageId,taskIds,artifacts}, exact sources. Reuse operationId unchanged on retry. No room required. Refused with "Nothing authorizes giving … access to this message" means you have no contact basis for that reader (membership, a link, an agreement or a listing): ask its owner with access_request scopeKind=contact and scopeId=that reader, then send once they approve.' }, annotation: 'destructive', needsAgentId: true, params: { command: { type: 'object', required: true, description: 'MessageCommand with explicit independent audience, deliveries, intent 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
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
- 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
- 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)) };
11
- export const MESSAGING_CAPABILITIES = [readMessageCapability, sendMessageCapability, readRepliesCapability, queryMessagesCapability, messageControlsCapability, messageAnswersCapability, cancelAnswerCapability];
9
+ export const MESSAGING_CAPABILITIES = [readMessageCapability, sendMessageCapability, readRepliesCapability, queryMessagesCapability, messageControlsCapability];
@@ -32,6 +32,14 @@ export interface TaskOutcomeEffects {
32
32
  reason?: string;
33
33
  amount?: number;
34
34
  transactionId?: string;
35
+ /** What the charge rests on: `fixed`, `stated` (with the provider's note) or `maximum`. */
36
+ charge?: {
37
+ amount?: number;
38
+ maximum?: number;
39
+ basis?: 'fixed' | 'stated' | 'maximum' | string;
40
+ note?: string;
41
+ capped?: boolean;
42
+ };
35
43
  };
36
44
  agreement?: {
37
45
  agreementId: string;
@@ -149,7 +149,7 @@ export function presentTaskOutcome(confirm, env) {
149
149
  const metering = !settlement
150
150
  ? undefined
151
151
  : settlement.settled
152
- ? `Metered per completed task: this completion settled ${settlement.amount ?? 'the agreed rate'}${settlement.transactionId ? ` (tx ${settlement.transactionId})` : ''}. One execution, once — a retry of this same transition does not charge again.`
152
+ ? `Metered per completed task: this completion settled ${settlement.amount ?? 'the agreed rate'}${settlement.transactionId ? ` (tx ${settlement.transactionId})` : ''}${chargeBasisLine(settlement.charge)}. One execution, once — a retry of this same transition does not charge again.`
153
153
  : `No payment moved on this completion — ${SETTLEMENT_REASONS[settlement.reason ?? ''] ?? settlement.reason ?? 'settlement did not run'}. Do not report this task as paid.`;
154
154
  const agreementFacts = effects?.agreement;
155
155
  const agreement = !agreementFacts
@@ -216,3 +216,12 @@ export const TASK_CAPABILITIES = [
216
216
  cancelTaskCapability,
217
217
  delegateTaskCapability,
218
218
  ];
219
+ /** Why a settled amount is what it is, when the agreement priced the task at completion. */
220
+ function chargeBasisLine(charge) {
221
+ if (!charge || charge.basis === 'fixed' || charge.basis == null)
222
+ return '';
223
+ const max = charge.maximum != null ? ` of up to ${charge.maximum}` : '';
224
+ if (charge.basis === 'maximum')
225
+ return `${max}, the maximum, because no charge was stated with the completion`;
226
+ return `${max}, as stated${charge.note ? ` (${charge.note})` : ''}${charge.capped ? ', capped at the maximum' : ''}`;
227
+ }
@@ -227,7 +227,7 @@ export function contactPath(env, input) {
227
227
  summary: sameOrg && !linked
228
228
  ? 'Same-org person: reach them under this scoped identity.'
229
229
  : 'Linked person: choose an explicit message audience and delivery. A link does not reveal their org roster.',
230
- next: nextCall(env, 'message_send', undefined, `send an explicit message command to ${partyId}; choose readers, delivery and answer responsibility separately`),
230
+ next: nextCall(env, 'message_send', undefined, `send an explicit message command to ${partyId}; choose readers, delivery and intent (fyi or request) separately`),
231
231
  };
232
232
  }
233
233
  if (partyKind === 'person' && !linked) {
@@ -29,9 +29,5 @@ export declare class MessageRecordsClient {
29
29
  limit?: number;
30
30
  }): Promise<MessagePage>;
31
31
  controls(agreementId: string, taskId?: string): Promise<MessagingControls>;
32
- answers(id: string): Promise<unknown>;
33
- cancelAnswer(id: string, requestId: string): Promise<{
34
- state: 'cancelled';
35
- }>;
36
32
  }
37
33
  export type { MessageCommand, MessageCommandResult, MessageRecord } from '@ziggs-ai/contracts';
@@ -2,6 +2,7 @@ import { parseMessageCommand } from '@ziggs-ai/contracts';
2
2
  import { buildOperatorHeaders } from './operatorHeaders.js';
3
3
  import { getBackendUrl } from '../utils/urlUtils.js';
4
4
  import { throwApiError } from '../shared/apiError.js';
5
+ const isIntent = (value) => value === 'fyi' || value === 'request';
5
6
  /** One command and one representation, independent of navigation. */
6
7
  export class MessageRecordsClient {
7
8
  operatorKey;
@@ -14,9 +15,9 @@ export class MessageRecordsClient {
14
15
  }
15
16
  async request(path, init = {}) { const res = await fetch(`${this.baseUrl ?? getBackendUrl()}/messaging/${path}`, { ...init, headers: { 'content-type': 'application/json', ...buildOperatorHeaders(this.operatorKey, this.agentId) } }); if (!res.ok)
16
17
  throwApiError(res, await res.text().catch(() => ''), `Messaging failed: ${res.status}`); return await res.json(); }
17
- async get(id) { const row = await this.request(`messages/${encodeURIComponent(id)}`); if (row.messageId !== id || !row.content || !Array.isArray(row.requestedAnswers))
18
+ async get(id) { const row = await this.request(`messages/${encodeURIComponent(id)}`); if (row.messageId !== id || !row.content || !isIntent(row.intent))
18
19
  throw new Error('Invalid exact message response'); return row; }
19
- async send(command) { const result = await this.request('messages', { method: 'POST', body: JSON.stringify(parseMessageCommand(command)) }); if (!['created', 'existing'].includes(result.outcome) || !result.message?.messageId || !result.message.content || !Array.isArray(result.message.recipients) || !Array.isArray(result.message.requestedAnswers))
20
+ async send(command) { const result = await this.request('messages', { method: 'POST', body: JSON.stringify(parseMessageCommand(command)) }); if (!['created', 'existing'].includes(result.outcome) || !result.message?.messageId || !result.message.content || !Array.isArray(result.message.recipients) || !isIntent(result.message.intent))
20
21
  throw new Error('Invalid message command response'); return result; }
21
22
  list(input = {}) { const query = new URLSearchParams(); for (const [key, value] of Object.entries(input))
22
23
  if (value !== undefined)
@@ -25,6 +26,4 @@ export class MessageRecordsClient {
25
26
  if (value !== undefined)
26
27
  query.set(key, String(value)); return this.request(`messages/${encodeURIComponent(id)}/replies?${query}`); }
27
28
  controls(agreementId, taskId) { const query = new URLSearchParams({ agreementId, ...(taskId ? { taskId } : {}) }); return this.request(`controls?${query}`); }
28
- answers(id) { return this.request(`messages/${encodeURIComponent(id)}/answers`); }
29
- cancelAnswer(id, requestId) { return this.request(`messages/${encodeURIComponent(id)}/answers/${encodeURIComponent(requestId)}/cancel`, { method: 'POST' }); }
30
29
  }
@@ -155,6 +155,20 @@ export interface UpdateTaskStateData {
155
155
  idempotencyKey?: string;
156
156
  /** What the result drew on. Recorded only when a result is written. */
157
157
  sources?: WriteSource[];
158
+ /**
159
+ * What this task cost, stated with `completed`. Read only when the agreement
160
+ * prices each task at completion (`taskCharge: 'at_completion'`), where it is
161
+ * capped at the agreed maximum and the rest of the reservation goes back to
162
+ * the payer. Omitted there, the task costs the maximum.
163
+ */
164
+ charge?: TaskChargeStatement;
165
+ }
166
+ /** What one completed task cost, and what that amount rests on. */
167
+ export interface TaskChargeStatement {
168
+ /** Points, a whole number, at least 0. */
169
+ amount: number;
170
+ /** A sentence the payer reads, e.g. "3 searches and a 2-page brief". */
171
+ basis?: string;
158
172
  }
159
173
  export declare function updateTaskState(taskId: string, state: TaskUpdateState, data: UpdateTaskStateData | undefined, creds: Creds): Promise<TaskWriteConfirm>;
160
174
  export declare function getActiveTasksForAgent(agentId: string, creds: Creds): Promise<Task[]>;
package/dist/types.d.ts CHANGED
@@ -457,7 +457,6 @@ export interface InboxEnvelope extends InboxRequestChannel, Partial<InboxPlanAck
457
457
  */
458
458
  ackTo: string | null;
459
459
  /** Open tasks assigned to this agent — the work channel. */
460
- answersAwaitingMe?: InboxDeliveryRef[];
461
460
  tasksAwaitingMe: InboxTaskRef[];
462
461
  truncatedTasks: number;
463
462
  proposalsAwaitingMe: InboxProposalRef[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ziggs-ai/api-client",
3
- "version": "0.39.5",
3
+ "version": "0.40.1",
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.45.0",
34
+ "@ziggs-ai/contracts": "0.46.0",
35
35
  "socket.io-client": "^4.7.0"
36
36
  },
37
37
  "keywords": [