@ziggs-ai/api-client 0.37.1 → 0.37.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.
@@ -63,7 +63,7 @@ export const openConversationCapability = {
63
63
  names: { sdk: 'chat_open', mcp: 'ziggs_chat_open' },
64
64
  title: 'Start or reuse a conversation',
65
65
  descriptions: {
66
- sdk: 'Open a conversation or explicitly invite people to a room. participantId opens/reuses the acting agent’s pair conversation with one person or agent; newChat opens a separate subject. participantIds creates a separate group with the acting caller and those people; include a stable idempotencyKey for retries. chatId plus participantIds invites people into that exact room, requiring room access and admission authority. Every person needs a valid contact basis: shared organization membership, a link, or an applicable agreement. Each participant keeps their own agent and work relationships; opening a group selects no service agreement. Group creation never expands an existing private DM. Invited teammates read from admission onward. participantIds may name agents, when creating a group or inviting into one: each must be reachable (your own, one of your company, published, or already engaged). An agent you add joins at once if your person gave you approval authority; otherwise it is a proposal in the room, and the first person in the room to say yes admits it. The result lists each id as added, already-here or pending. To bring in an agent you would hire, claim its listing with the room\'s chatId instead. Your own missing seat is asked for with access_request. Reach another person through their user id, not an unpublished agent of theirs. If you cannot reach someone your person knows, ask your person to introduce you in a room. Opening does not send a message; chat_send must name its intended receiverId to guarantee a target. To list accessible rooms, use grant_list with scopeKind ["chat"].',
66
+ sdk: 'Open a conversation or explicitly invite people to a room. participantId opens/reuses your person’s conversation with one person or agent, the same room each of their agents finds; newChat opens a separate subject, which can take a name. participantIds creates a separate group with the acting caller and those people; include a stable idempotencyKey for retries. chatId plus participantIds invites people into that exact room, requiring room access and admission authority. Every person needs a valid contact basis: shared organization membership, a link, or an applicable agreement. Each participant keeps their own agent and work relationships; opening a group selects no service agreement. Group creation never expands an existing private DM. Invited teammates read from admission onward. participantIds may name agents, when creating a group or inviting into one: each must be reachable (your own, one of your company, published, or already engaged). An agent you add joins at once if your person gave you approval authority; otherwise it is a proposal in the room, and the first person in the room to say yes admits it. The result lists each id as added, already-here or pending. To bring in an agent you would hire, claim its listing with the room\'s chatId instead. Your own missing seat is asked for with access_request. Reach another person through their user id, not an unpublished agent of theirs. If you cannot reach someone your person knows, ask your person to introduce you in a room. Opening does not send a message; chat_send must name its intended receiverId to guarantee a target. To list accessible rooms, use grant_list with scopeKind ["chat"].',
67
67
  },
68
68
  annotation: 'write',
69
69
  params: {
@@ -74,8 +74,8 @@ export const openConversationCapability = {
74
74
  participantIds: { type: 'array', items: { type: 'string' }, description: '1–20 known ids. With or without chatId, people and agents; an agent without your approval authority waits for one person in the room to say yes. Use instead of participantId. Each invitation is authorized separately.' },
75
75
  chatId: { type: 'string', description: 'Existing room to invite participantIds into. Never creates another room. Omit creation options and agreementId.' },
76
76
  idempotencyKey: { type: 'string', description: 'Required for group creation: a unique key for this conversation, reused unchanged on retries. A different participant set needs a new key.' },
77
- name: { type: 'string', description: 'Name for a new group, 1–80 characters. Pair rooms and team channels take no name. A retry of the same idempotencyKey keeps the first name. Rename later is PATCH /chats/:chatId by someone who can admit people.' },
78
- 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.' },
77
+ name: { type: 'string', description: 'Name for a new group or a newChat room, 1–80 characters. Pair rooms and team channels take no name. A retry of the same idempotencyKey keeps the first name. Rename later is PATCH /chats/:chatId by someone who can admit people.' },
78
+ createIfMissing: { type: 'boolean', description: 'Set false to reuse only an existing room with this participant. A missing room is refused without creating a replacement.' },
79
79
  newChat: {
80
80
  type: 'boolean',
81
81
  description: 'Open a separate chat even though one is already open with the named participants. ' +
@@ -108,8 +108,8 @@ export const openConversationCapability = {
108
108
  else if (args.chatId || args.idempotencyKey) {
109
109
  throw new Error('chatId and idempotencyKey require participantIds');
110
110
  }
111
- if (typeof args.name === 'string' && args.name.trim() && !Array.isArray(args.participantIds)) {
112
- throw new Error('A name is for a new group. Pair rooms take no name; the person is the name.');
111
+ if (typeof args.name === 'string' && args.name.trim() && !Array.isArray(args.participantIds) && args.newChat !== true) {
112
+ throw new Error('A name is for a new group or a newChat room. Pair rooms take no name; the person is the name.');
113
113
  }
114
114
  const { chatId, reused, invited, name } = await openConversation({
115
115
  ...(typeof args.participantId === 'string' ? { participantId: args.participantId } : {}),
@@ -430,13 +430,32 @@ function requestAccessAction(args, env) {
430
430
  requiresApproval: true,
431
431
  };
432
432
  }
433
+ /** The readers an access request names: kinds stated, ids distinct, at most 20. */
434
+ function readersOf(value) {
435
+ if (!Array.isArray(value) || value.length < 1 || value.length > 20)
436
+ throw new Error('holders must list 1 to 20 readers');
437
+ const seen = new Set();
438
+ return value.map((entry, i) => {
439
+ const kind = entry?.kind;
440
+ const id = entry?.id;
441
+ if (kind !== 'agent' && kind !== 'user' && kind !== 'org')
442
+ throw new Error(`holders[${i}].kind must be agent, user or org`);
443
+ if (typeof id !== 'string' || !id.trim())
444
+ throw new Error(`holders[${i}].id is required`);
445
+ const key = `${kind}:${id.trim()}`;
446
+ if (seen.has(key))
447
+ throw new Error(`holders names the same reader twice (holders[${i}])`);
448
+ seen.add(key);
449
+ return { kind, id: id.trim() };
450
+ });
451
+ }
433
452
  export const accessRequestCapability = {
434
453
  key: 'access_request',
435
454
  names: { sdk: 'access_request', mcp: 'ziggs_access_request' },
436
- title: 'Request access for yourself',
455
+ title: 'Request access for yourself or named readers',
437
456
  annotation: 'destructive', needsAgentId: true,
438
457
  descriptions: {
439
- sdk: 'Request optional bounded access to one artifact, chat, agreement context or task. Creates only a pending approval; never grants access or approves. Name who to ask in recipientId: 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 also need chatId, the conversation they approve in. 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 open the returned scope. Hiring alone grants no source access. Revocation and expiry are checked by the system.',
458
+ sdk: 'Request optional bounded access to one artifact, chat, agreement context or task. Creates only a pending approval; never grants access or approves. Name who to ask in recipientId: 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 also need chatId, the conversation they approve in. 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 open the returned scope. To arrange reading of one artifact for others, name them in holders (agents by id, people or workspaces by id or the participant reference this room shows you); 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. Hiring alone grants no source access. Revocation and expiry are checked by the system.',
440
459
  },
441
460
  params: {
442
461
  scopeKind: { type: 'string', required: true, enum: ['artifact', 'chat', 'agreement', 'task'] },
@@ -451,6 +470,18 @@ export const accessRequestCapability = {
451
470
  // comes back refused with the people this agent may ask. Required in the
452
471
  // schema, a host refused the call before that list could arrive.
453
472
  recipientId: { type: 'string', description: 'Who to ask: workBrief.actingFor.customer.id (the customer of this hire) or workBrief.actingFor.operator.id (your operator). Without it, the refusal lists who you can ask.' },
473
+ holders: {
474
+ type: 'array',
475
+ description: 'Artifact only, read only: up to 20 readers, each { kind: "agent" | "user" | "org", id }. Leave yourself out to arrange it for others. Omit to ask for yourself.',
476
+ items: {
477
+ type: 'object',
478
+ properties: {
479
+ kind: { type: 'string', enum: ['agent', 'user', 'org'] },
480
+ id: { type: 'string' },
481
+ },
482
+ required: ['kind', 'id'],
483
+ },
484
+ },
454
485
  idempotencyKey: { type: 'string', description: 'Reuse for the same logical request' },
455
486
  },
456
487
  handler: async (args, env) => {
@@ -477,14 +508,26 @@ export const accessRequestCapability = {
477
508
  const durationHours = args.durationHours ?? 24;
478
509
  if (typeof durationHours !== 'number' || !Number.isInteger(durationHours) || durationHours < 1 || durationHours > 168)
479
510
  throw new Error('durationHours must be an integer between 1 and 168');
511
+ const holders = args.holders === undefined ? undefined : readersOf(args.holders);
512
+ if (holders && (scopeKind !== 'artifact' || agreementId || permission !== 'read'))
513
+ throw new Error('holders is for one artifact named by scopeId, to read');
480
514
  const creds = fullCreds(env);
481
515
  const result = await new ContextGrantsClient(creds.operatorKey, creds.agentId, env.baseUrl).requestAccess({
482
516
  scopeKind, scopeId, agreementId, reason: String(args.reason).trim(),
483
517
  permission: permission, temporal: args.temporal,
484
518
  durationHours, chatId: args.chatId,
485
519
  recipientId: typeof args.recipientId === 'string' ? args.recipientId.trim() : undefined,
520
+ ...(holders ? { holders } : {}),
486
521
  }, { idempotencyKey: args.idempotencyKey, laneId: creds.laneId });
487
522
  const actingAgentId = result.actingAgentId ?? (creds.agentId || undefined);
523
+ if (result.status === 'already_readable') {
524
+ return {
525
+ ...result, outcome: 'already_readable', state: 'not_requested',
526
+ ...(actingAgentId ? { actingAgentId } : {}),
527
+ scope: result.scope ?? { kind: scopeKind, id: scopeId },
528
+ note: result.message ?? 'Every reader you named can already read it. Nothing was asked.',
529
+ };
530
+ }
488
531
  return {
489
532
  ...result, outcome: 'pending', state: 'requested',
490
533
  ...(actingAgentId ? { actingAgentId } : {}),
@@ -21,11 +21,11 @@ const BRIEFS = {
21
21
  },
22
22
  },
23
23
  ziggs_inbox: {
24
- does: 'Reads your inbox: deliveries oldest first, tasks, decisions, sessionChatCard; waitSeconds holds for news. Observes only: no claim or ack.',
24
+ does: 'Reads your inbox: deliveries by recipient, tasks, decisions, sessionChatCard; waitSeconds holds for news. Observes only: no claim or ack.',
25
25
  returns: 'a readPlan of next calls; backlog if more is unread',
26
26
  next: 'ziggs_inbox_claim to handle them',
27
27
  compact: {
28
- does: 'Reads your inbox: deliveries oldest first, tasks, decisions, sessionChatCard; waitSeconds holds for news.',
28
+ does: 'Reads your inbox: deliveries by recipient, tasks, decisions, sessionChatCard; waitSeconds holds for news.',
29
29
  changes: 'takes or renews this host’s lease',
30
30
  next: 'ziggs_inbox_ack after handling',
31
31
  },
@@ -109,19 +109,12 @@ export declare class ContextGrantsClient {
109
109
  ownerId?: string;
110
110
  /** Who to ask, from the people the server offers this wake. */
111
111
  recipientId?: string;
112
+ /** Artifact only: who should read it, if not only the caller. One grant each. */
113
+ holders?: AccessReader[];
112
114
  }, opts?: {
113
115
  idempotencyKey?: string;
114
116
  laneId?: string;
115
- }): Promise<{
116
- status: 'pending_approval';
117
- agreementId: string;
118
- accessGranted: false;
119
- actingAgentId?: string;
120
- scope?: {
121
- kind: string;
122
- id: string;
123
- };
124
- } & RequestSentTo>;
117
+ }): Promise<AccessRequestResult>;
125
118
  requestArtifactAccess(artifactId: string, input: {
126
119
  chatId: string;
127
120
  ownerId: string;
@@ -167,6 +160,31 @@ export declare class ContextGrantsClient {
167
160
  }>;
168
161
  }
169
162
  /** Who got a request card and where they answer it, as the server says. */
163
+ /** One reader an artifact access request asks for, named with its kind. */
164
+ export interface AccessReader {
165
+ kind: 'agent' | 'user' | 'org';
166
+ id: string;
167
+ }
168
+ /** What a request did for one named reader. */
169
+ export interface AccessReaderOutcome extends AccessReader {
170
+ outcome: 'already_readable' | 'awaiting_approval';
171
+ }
172
+ export type AccessRequestResult = {
173
+ accessGranted: false;
174
+ actingAgentId?: string;
175
+ scope?: {
176
+ kind: string;
177
+ id: string;
178
+ };
179
+ /** Per named reader, when the request named readers. */
180
+ readers?: AccessReaderOutcome[];
181
+ } & RequestSentTo & ({
182
+ status: 'pending_approval';
183
+ agreementId: string;
184
+ } | {
185
+ status: 'already_readable';
186
+ agreementId: null;
187
+ });
170
188
  export interface RequestSentTo {
171
189
  sentTo?: {
172
190
  id: string;
@@ -77,10 +77,22 @@ export class ContextGrantsClient {
77
77
  if (!res.ok)
78
78
  throwApiError(res, body, `requestAccess failed: ${res.status}`);
79
79
  const result = JSON.parse(body);
80
+ const readers = Array.isArray(result.readers) ? { readers: result.readers } : {};
81
+ const common = {
82
+ accessGranted: false,
83
+ ...(typeof result.actingAgentId === 'string' && result.actingAgentId ? { actingAgentId: result.actingAgentId } : {}),
84
+ ...(result.scope ? { scope: result.scope } : {}),
85
+ ...readers,
86
+ ...sentToOf(result),
87
+ };
88
+ // Every named reader already reads it: nothing was asked.
89
+ if (result.status === 'already_readable' && result.accessGranted === false) {
90
+ return { status: 'already_readable', agreementId: null, ...common };
91
+ }
80
92
  if (result.status !== 'pending_approval' || !result.agreementId || result.accessGranted !== false) {
81
93
  throw new Error('Expected a pending access request with accessGranted=false');
82
94
  }
83
- return { status: 'pending_approval', agreementId: result.agreementId, accessGranted: false, ...(typeof result.actingAgentId === 'string' && result.actingAgentId ? { actingAgentId: result.actingAgentId } : {}), ...(result.scope ? { scope: result.scope } : {}), ...sentToOf(result) };
95
+ return { status: 'pending_approval', agreementId: result.agreementId, ...common };
84
96
  }
85
97
  async requestArtifactAccess(artifactId, input, opts) {
86
98
  const res = await fetch(`${this.baseUrl}/context/artifacts/${encodeURIComponent(artifactId)}/access-requests`, {
@@ -36,16 +36,8 @@ export type GrantBasisKind = 'creation' | 'publication' | 'membership' | 'accept
36
36
  * stopped showing a whole scope kind after `artifact` was added; anything that
37
37
  * means "every context scope" should read this.
38
38
  */
39
- export declare const CONTEXT_GRANT_SCOPE_KINDS: readonly ["chat", "agreement", "org", "artifact", "task"];
40
- /**
41
- * Every scope kind across every rail: context + connection + payment + consent
42
- * + carrier. Mirrors the server's `GRANT_SCOPE_KINDS` (grants/grant-view.ts),
43
- * which is the list `GET /grants?scopeKind=` validates against — a kind missing
44
- * here cannot be asked for at all, which is the failure the comment above
45
- * describes.
46
- */
47
- export declare const GRANT_SCOPE_KINDS: readonly ["chat", "agreement", "org", "artifact", "task", "connection", "wallet", "consent", "inbox"];
48
- export type GrantScopeKind = (typeof GRANT_SCOPE_KINDS)[number];
39
+ import { CONTEXT_GRANT_SCOPE_KINDS, GRANT_SCOPE_KINDS, type ContextGrantScopeKind, type GrantScopeKind } from '@ziggs-ai/contracts';
40
+ export { CONTEXT_GRANT_SCOPE_KINDS, GRANT_SCOPE_KINDS, type ContextGrantScopeKind, type GrantScopeKind, };
49
41
  /**
50
42
  * The scope a grant is over, and one caveat on it — the wire's shapes.
51
43
  *
@@ -6,32 +6,8 @@
6
6
  * stopped showing a whole scope kind after `artifact` was added; anything that
7
7
  * means "every context scope" should read this.
8
8
  */
9
- export const CONTEXT_GRANT_SCOPE_KINDS = [
10
- 'chat',
11
- 'agreement',
12
- 'org',
13
- // narrowest context scope
14
- 'artifact',
15
- // a branch of a work graph: the named task and everything under it, now and
16
- // in future. Holding one is what being in a job means.
17
- 'task',
18
- ];
19
- /**
20
- * Every scope kind across every rail: context + connection + payment + consent
21
- * + carrier. Mirrors the server's `GRANT_SCOPE_KINDS` (grants/grant-view.ts),
22
- * which is the list `GET /grants?scopeKind=` validates against — a kind missing
23
- * here cannot be asked for at all, which is the failure the comment above
24
- * describes.
25
- */
26
- export const GRANT_SCOPE_KINDS = [
27
- ...CONTEXT_GRANT_SCOPE_KINDS,
28
- 'connection',
29
- 'wallet',
30
- // approval authority — who may answer a slot
31
- 'consent',
32
- // carrier grants — who reads whose mailbox
33
- 'inbox',
34
- ];
9
+ import { CONTEXT_GRANT_SCOPE_KINDS, GRANT_SCOPE_KINDS, } from '@ziggs-ai/contracts';
10
+ export { CONTEXT_GRANT_SCOPE_KINDS, GRANT_SCOPE_KINDS, };
35
11
  /** Value of the first caveat of `type` on a grant, or undefined. */
36
12
  export function grantCaveat(grant, type) {
37
13
  return grant.caveats.find((c) => c.type === type)?.value;
@@ -8,7 +8,6 @@
8
8
  */
9
9
  export interface InboxAckEnvelope {
10
10
  ackTo?: string | null;
11
- truncatedRequests?: number | null;
12
11
  deliveries?: ReadonlyArray<{
13
12
  resourceId?: string | null;
14
13
  assigneeId?: string | null;
package/dist/inboxAck.js CHANGED
@@ -31,8 +31,6 @@ export function inboxAckHandledIds(envelope, ownAgentId, ownAgentIds) {
31
31
  export function inboxAckAllowed(envelope, opts = {}) {
32
32
  if (!envelope.ackTo)
33
33
  return false;
34
- if ((envelope.truncatedRequests ?? 0) !== 0)
35
- return false;
36
34
  const failures = opts.failures ?? 0;
37
35
  if (failures > 0 && !opts.forceAck)
38
36
  return false;
@@ -80,8 +78,6 @@ function gatingRows(envelope, ownAgentId, ownAgentIds) {
80
78
  * backend is handled: the caller keeps its single end-of-pass ack.
81
79
  */
82
80
  export function planPartialInboxAck(envelope, handled, opts = {}) {
83
- if ((envelope.truncatedRequests ?? 0) !== 0)
84
- return null;
85
81
  const failures = opts.failures ?? 0;
86
82
  if (failures > 0 && !opts.forceAck)
87
83
  return null;
package/dist/types.d.ts CHANGED
@@ -1,6 +1,4 @@
1
- import type { AgreementPartySide, AgreementParties, PrincipalPresentation, InboxDeliveryRef } from '@ziggs-ai/contracts';
2
- import type { InboxRequestChannel } from '@ziggs-ai/contracts';
3
- import type { InboxPartyKind } from '@ziggs-ai/contracts';
1
+ import { AGREEMENT_ENGAGEMENT_KIND, BROADCAST_TARGETS, OPEN_AGREEMENT_TARGET, ORG_AGREEMENT_TARGET, isBroadcastTarget, partyActorIds, partySideIds, type AgreementPartySide, type AgreementParties, type AgreementStatusValue, type BroadcastAudience, type InboxDeliveryRef, type InboxPartyKind, type InboxRequestChannel, type PrincipalPresentation, type TaskStateValue } from '@ziggs-ai/contracts';
4
2
  /**
5
3
  * One failure shape for every HTTP client path.
6
4
  *
@@ -60,7 +58,7 @@ export interface Creds {
60
58
  /** Person-readable window name, sent as `X-Ziggs-Session-Name`. */
61
59
  sessionName?: string;
62
60
  }
63
- export type TaskState = 'active' | 'proposal' | 'completed' | 'failed' | 'cancelled' | 'ledger_open';
61
+ export type TaskState = TaskStateValue;
64
62
  export type PlanStepStatus = 'pending' | 'in_progress' | 'completed' | 'skipped';
65
63
  export interface PlanStep {
66
64
  stepId: string;
@@ -139,15 +137,9 @@ export interface Task {
139
137
  payerIsYou?: boolean;
140
138
  proposedToIsYou?: boolean;
141
139
  }
142
- export type AgreementStatus = 'pending' | 'active' | 'fulfilled' | 'cancelled' | 'rejected' | (string & {});
140
+ export type AgreementStatus = AgreementStatusValue;
143
141
  export type ProposalStatus = 'pending' | 'approved' | 'rejected' | 'countered' | 'expired' | (string & {});
144
- /** ⚠️ Mirrors the server's engagement-kind constant; the server is authoritative. */
145
- export declare const AGREEMENT_ENGAGEMENT_KIND: {
146
- readonly HIRE: "hire";
147
- readonly SERVICE: "service";
148
- /** a bilateral reach link between two agents (no money, no work). */
149
- readonly LINK: "link";
150
- };
142
+ export { AGREEMENT_ENGAGEMENT_KIND, BROADCAST_TARGETS, OPEN_AGREEMENT_TARGET, ORG_AGREEMENT_TARGET, isBroadcastTarget, partyActorIds, partySideIds, type BroadcastAudience, };
151
143
  export type EngagementKind = (typeof AGREEMENT_ENGAGEMENT_KIND)[keyof typeof AGREEMENT_ENGAGEMENT_KIND];
152
144
  /**
153
145
  * One side of an agreement, as the wire carries it.
@@ -161,15 +153,6 @@ export type EngagementKind = (typeof AGREEMENT_ENGAGEMENT_KIND)[keyof typeof AGR
161
153
  export type { AgreementPartySide };
162
154
  /** The four sides of an agreement, each {@link AgreementPartySide}. */
163
155
  export type { AgreementParties };
164
- /** Both ids on one side, principal first, absent ones dropped. */
165
- export declare function partySideIds(side: AgreementPartySide | null | undefined): string[];
166
- /**
167
- * Every agent that acted on this agreement, deduped.
168
- *
169
- * The read for "is this agent a party?" — an agent id lives in `actor` alone,
170
- * so widening the match to `principal` would hit an unrelated estate.
171
- */
172
- export declare function partyActorIds(parties: AgreementParties | null | undefined): string[];
173
156
  /** Compact agreement reference carried by task reads. */
174
157
  export interface AgreementSummary {
175
158
  agreementId: string;
@@ -289,7 +272,6 @@ export declare const EntryTypes: {
289
272
  readonly TASK_HISTORY: "task_history";
290
273
  };
291
274
  export type EntryType = (typeof EntryTypes)[keyof typeof EntryTypes];
292
- /** ⚠️ Mirrors the server's content-type constant; the server is authoritative. */
293
275
  export declare const ContentTypes: {
294
276
  readonly TEXT: "text";
295
277
  readonly OPERATION: "operation";
@@ -297,24 +279,13 @@ export declare const ContentTypes: {
297
279
  readonly RESULT: "result";
298
280
  readonly AGREEMENT_UPDATE: "agreement_update";
299
281
  readonly TASK: "task";
300
- readonly PROPOSAL: "proposal";
282
+ readonly TASK_PLAN_STEP: "task_plan_step";
301
283
  readonly THOUGHT: "thought";
302
- /** @deprecated legacy alias for AGREEMENT_UPDATE */
303
- readonly TASK_UPDATE: "task_update";
284
+ readonly PROPOSAL: "proposal";
304
285
  };
305
286
  export type ContentType = (typeof ContentTypes)[keyof typeof ContentTypes];
306
287
  /** ⚠️ Mirrors the server's entry/content-type validation; the server is authoritative. */
307
288
  export declare function isValidContentType(contentType: string, entryType: string): boolean;
308
- /** ⚠️ Mirrors the server's sentinel. Fully-public broadcast target. */
309
- export declare const OPEN_AGREEMENT_TARGET: "everyone";
310
- /** ⚠️ Mirrors the server's sentinel. Org-scoped broadcast: visible/claimable only by members of the agreement's orgId. */
311
- export declare const ORG_AGREEMENT_TARGET: "org";
312
- /** Audience for a broadcast proposal: fully public ('everyone') or org-scoped ('org'). */
313
- export type BroadcastAudience = typeof OPEN_AGREEMENT_TARGET | typeof ORG_AGREEMENT_TARGET;
314
- /** Both broadcast sentinels — values that occupy a party slot but are NOT real principal ids. */
315
- export declare const BROADCAST_TARGETS: readonly ["everyone", "org"];
316
- /** True when `id` is a broadcast sentinel ('everyone' | 'org') rather than a concrete principal id. */
317
- export declare function isBroadcastTarget(id: string | null | undefined): boolean;
318
289
  export { isPersonaFaceRef as isPersonaRef, isRoomBindingRef as isRoomPresentationRef, isPresentationRef as isOpaquePresentationRef, } from '@ziggs-ai/contracts';
319
290
  /** Display + entitlement face for a principal on the message/roster wire. */
320
291
  export type { PrincipalPresentation };
package/dist/types.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { AGREEMENT_ENGAGEMENT_KIND, BROADCAST_TARGETS, MESSAGE_CONTENT_TYPE, OPEN_AGREEMENT_TARGET, ORG_AGREEMENT_TARGET, isBroadcastTarget, partyActorIds, partySideIds, } from '@ziggs-ai/contracts';
1
2
  /**
2
3
  * One failure shape for every HTTP client path.
3
4
  *
@@ -27,32 +28,7 @@ export class RateLimitedError extends ApiError {
27
28
  this.retryAfterMs = retryAfterMs;
28
29
  }
29
30
  }
30
- /** ⚠️ Mirrors the server's engagement-kind constant; the server is authoritative. */
31
- export const AGREEMENT_ENGAGEMENT_KIND = {
32
- HIRE: 'hire',
33
- SERVICE: 'service',
34
- /** a bilateral reach link between two agents (no money, no work). */
35
- LINK: 'link',
36
- };
37
- /** The four sides, in the order every fan-out walks them. */
38
- const AGREEMENT_PARTY_SIDES = ['payer', 'provider', 'proposedTo', 'creator'];
39
- /** Both ids on one side, principal first, absent ones dropped. */
40
- export function partySideIds(side) {
41
- return [side?.principal, side?.actor].filter((id) => typeof id === 'string' && id.length > 0);
42
- }
43
- /**
44
- * Every agent that acted on this agreement, deduped.
45
- *
46
- * The read for "is this agent a party?" — an agent id lives in `actor` alone,
47
- * so widening the match to `principal` would hit an unrelated estate.
48
- */
49
- export function partyActorIds(parties) {
50
- // The wire always sends all four sides; a caller may still hand us nothing.
51
- const p = parties ?? {};
52
- return [
53
- ...new Set(AGREEMENT_PARTY_SIDES.map((name) => p[name]?.actor).filter((id) => typeof id === 'string' && id.length > 0)),
54
- ];
55
- }
31
+ export { AGREEMENT_ENGAGEMENT_KIND, BROADCAST_TARGETS, OPEN_AGREEMENT_TARGET, ORG_AGREEMENT_TARGET, isBroadcastTarget, partyActorIds, partySideIds, };
56
32
  /** ⚠️ Mirrors the server's entry-type constant; the server is authoritative. */
57
33
  export const EntryTypes = {
58
34
  MESSAGE: 'message',
@@ -62,38 +38,28 @@ export const EntryTypes = {
62
38
  /** @deprecated legacy alias for AGREEMENT_HISTORY */
63
39
  TASK_HISTORY: 'task_history',
64
40
  };
65
- /** ⚠️ Mirrors the server's content-type constant; the server is authoritative. */
66
- export const ContentTypes = {
67
- TEXT: 'text',
68
- OPERATION: 'operation',
69
- CONTEXT: 'context',
70
- RESULT: 'result',
71
- AGREEMENT_UPDATE: 'agreement_update',
72
- TASK: 'task',
73
- PROPOSAL: 'proposal',
74
- THOUGHT: 'thought',
75
- /** @deprecated legacy alias for AGREEMENT_UPDATE */
76
- TASK_UPDATE: 'task_update',
77
- };
41
+ export const ContentTypes = MESSAGE_CONTENT_TYPE;
78
42
  const VALID_CONTENT_TYPES = {
79
43
  [EntryTypes.MESSAGE]: [ContentTypes.TEXT],
80
- [EntryTypes.NOTIFICATION]: [ContentTypes.TEXT, ContentTypes.TASK, ContentTypes.PROPOSAL],
81
- [EntryTypes.ARTIFACT]: [ContentTypes.OPERATION, ContentTypes.CONTEXT, ContentTypes.RESULT, ContentTypes.TEXT, ContentTypes.THOUGHT],
82
- [EntryTypes.AGREEMENT_HISTORY]: [ContentTypes.AGREEMENT_UPDATE, ContentTypes.TASK_UPDATE],
83
- [EntryTypes.TASK_HISTORY]: [ContentTypes.AGREEMENT_UPDATE, ContentTypes.TASK_UPDATE],
44
+ [EntryTypes.NOTIFICATION]: [
45
+ ContentTypes.TEXT,
46
+ ContentTypes.TASK,
47
+ ContentTypes.TASK_PLAN_STEP,
48
+ ContentTypes.PROPOSAL,
49
+ ContentTypes.AGREEMENT_UPDATE,
50
+ ],
51
+ [EntryTypes.ARTIFACT]: [
52
+ ContentTypes.OPERATION,
53
+ ContentTypes.CONTEXT,
54
+ ContentTypes.RESULT,
55
+ ContentTypes.TEXT,
56
+ ContentTypes.THOUGHT,
57
+ ],
58
+ [EntryTypes.AGREEMENT_HISTORY]: [ContentTypes.AGREEMENT_UPDATE],
59
+ [EntryTypes.TASK_HISTORY]: [ContentTypes.AGREEMENT_UPDATE],
84
60
  };
85
61
  /** ⚠️ Mirrors the server's entry/content-type validation; the server is authoritative. */
86
62
  export function isValidContentType(contentType, entryType) {
87
63
  return VALID_CONTENT_TYPES[entryType]?.includes(contentType) ?? false;
88
64
  }
89
- /** ⚠️ Mirrors the server's sentinel. Fully-public broadcast target. */
90
- export const OPEN_AGREEMENT_TARGET = 'everyone';
91
- /** ⚠️ Mirrors the server's sentinel. Org-scoped broadcast: visible/claimable only by members of the agreement's orgId. */
92
- export const ORG_AGREEMENT_TARGET = 'org';
93
- /** Both broadcast sentinels — values that occupy a party slot but are NOT real principal ids. */
94
- export const BROADCAST_TARGETS = [OPEN_AGREEMENT_TARGET, ORG_AGREEMENT_TARGET];
95
- /** True when `id` is a broadcast sentinel ('everyone' | 'org') rather than a concrete principal id. */
96
- export function isBroadcastTarget(id) {
97
- return id === OPEN_AGREEMENT_TARGET || id === ORG_AGREEMENT_TARGET;
98
- }
99
65
  export { isPersonaFaceRef as isPersonaRef, isRoomBindingRef as isRoomPresentationRef, isPresentationRef as isOpaquePresentationRef, } from '@ziggs-ai/contracts';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ziggs-ai/api-client",
3
- "version": "0.37.1",
3
+ "version": "0.37.4",
4
4
  "description": "HTTP and WebSocket client for the Ziggs backend API",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -27,7 +27,7 @@
27
27
  "test:watch": "node --import tsx/esm --test --watch test/*.test.ts"
28
28
  },
29
29
  "dependencies": {
30
- "@ziggs-ai/contracts": "^0.21.0",
30
+ "@ziggs-ai/contracts": "^0.23.3",
31
31
  "socket.io-client": "^4.7.0"
32
32
  },
33
33
  "keywords": [