@ziggs-ai/api-client 0.37.1 → 0.37.5

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 } : {}),
@@ -50,6 +50,5 @@ export declare const RESOURCE_UNREADABLE_MESSAGE = "This exists and you cannot r
50
50
  export declare function explainRefusedRead(kind: 'agreement' | 'task' | 'chat' | 'artifact', id: string, env: CapabilityEnv): Promise<RefusedRead | null>;
51
51
  export declare const openCapability: CapabilityDefinition;
52
52
  export declare const accessExplainCapability: CapabilityDefinition;
53
- export declare const contextRequestCapability: CapabilityDefinition;
54
53
  export declare const accessRequestCapability: CapabilityDefinition;
55
54
  export declare const CONTEXT_CAPABILITIES: CapabilityDefinition[];
@@ -391,36 +391,6 @@ export const accessExplainCapability = {
391
391
  return presentOpenResult(result, env);
392
392
  },
393
393
  };
394
- export const contextRequestCapability = {
395
- key: 'context_request',
396
- names: { sdk: 'context_request', mcp: 'ziggs_context_request' },
397
- title: 'Ask an owner for artifact access',
398
- descriptions: {
399
- sdk: 'Ask a human owner for temporary read-only access to one artifact you were given a reference for. Use your existing chat with that owner. Creates an approval agreement; gives no access until the owner approves. You are the only recipient. After approval, open the original artifact. Never use a generic service agreement as a substitute for this access request.',
400
- },
401
- annotation: 'destructive', needsAgentId: true,
402
- params: {
403
- artifactId: { type: 'string', required: true },
404
- chatId: { type: 'string', required: true },
405
- ownerId: { type: 'string', required: true, description: 'Who to ask, from your work brief: workBrief.actingFor.customer.id or workBrief.actingFor.operator.id. Anyone else is refused, and the refusal lists who you can ask.' },
406
- reason: { type: 'string', required: true, description: 'Purpose, including whom you will summarize the artifact to' },
407
- durationHours: { type: 'number', description: 'Integer 1–168 hours after approval; default 24' },
408
- idempotencyKey: { type: 'string', description: 'Reuse for the same request; do not create duplicate approval cards' },
409
- },
410
- handler: async (args, env) => {
411
- for (const key of ['artifactId', 'chatId', 'ownerId', 'reason'])
412
- if (typeof args[key] !== 'string' || !String(args[key]).trim())
413
- throw new Error(`${key} is required`);
414
- const durationHours = args.durationHours === undefined ? 24 : Number(args.durationHours);
415
- if (!Number.isInteger(durationHours) || durationHours < 1 || durationHours > 168)
416
- throw new Error('durationHours must be an integer between 1 and 168');
417
- const creds = fullCreds(env);
418
- const result = await new ContextGrantsClient(creds.operatorKey, creds.agentId, env.baseUrl).requestArtifactAccess(args.artifactId, {
419
- chatId: args.chatId, ownerId: args.ownerId, reason: args.reason, durationHours,
420
- }, { idempotencyKey: args.idempotencyKey, laneId: creds.laneId });
421
- return { ...result, outcome: 'pending', state: 'requested', appUrl: `${(env.webUrl ?? 'https://ziggsai.com').replace(/\/$/, '')}/app/agreements/${encodeURIComponent(result.agreementId)}`, note: `${result.message ?? 'Waiting for the artifact owner.'} No access granted yet; open the original artifact only after approval.` };
422
- },
423
- };
424
394
  /** A suggested request still needs a purpose; it is never a grant or an approval. */
425
395
  function requestAccessAction(args, env) {
426
396
  return {
@@ -430,13 +400,32 @@ function requestAccessAction(args, env) {
430
400
  requiresApproval: true,
431
401
  };
432
402
  }
403
+ /** The readers an access request names: kinds stated, ids distinct, at most 20. */
404
+ function readersOf(value) {
405
+ if (!Array.isArray(value) || value.length < 1 || value.length > 20)
406
+ throw new Error('holders must list 1 to 20 readers');
407
+ const seen = new Set();
408
+ return value.map((entry, i) => {
409
+ const kind = entry?.kind;
410
+ const id = entry?.id;
411
+ if (kind !== 'agent' && kind !== 'user' && kind !== 'org')
412
+ throw new Error(`holders[${i}].kind must be agent, user or org`);
413
+ if (typeof id !== 'string' || !id.trim())
414
+ throw new Error(`holders[${i}].id is required`);
415
+ const key = `${kind}:${id.trim()}`;
416
+ if (seen.has(key))
417
+ throw new Error(`holders names the same reader twice (holders[${i}])`);
418
+ seen.add(key);
419
+ return { kind, id: id.trim() };
420
+ });
421
+ }
433
422
  export const accessRequestCapability = {
434
423
  key: 'access_request',
435
424
  names: { sdk: 'access_request', mcp: 'ziggs_access_request' },
436
- title: 'Request access for yourself',
425
+ title: 'Request access for yourself or named readers',
437
426
  annotation: 'destructive', needsAgentId: true,
438
427
  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.',
428
+ 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
429
  },
441
430
  params: {
442
431
  scopeKind: { type: 'string', required: true, enum: ['artifact', 'chat', 'agreement', 'task'] },
@@ -451,6 +440,18 @@ export const accessRequestCapability = {
451
440
  // comes back refused with the people this agent may ask. Required in the
452
441
  // schema, a host refused the call before that list could arrive.
453
442
  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.' },
443
+ holders: {
444
+ type: 'array',
445
+ 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.',
446
+ items: {
447
+ type: 'object',
448
+ properties: {
449
+ kind: { type: 'string', enum: ['agent', 'user', 'org'] },
450
+ id: { type: 'string' },
451
+ },
452
+ required: ['kind', 'id'],
453
+ },
454
+ },
454
455
  idempotencyKey: { type: 'string', description: 'Reuse for the same logical request' },
455
456
  },
456
457
  handler: async (args, env) => {
@@ -477,14 +478,26 @@ export const accessRequestCapability = {
477
478
  const durationHours = args.durationHours ?? 24;
478
479
  if (typeof durationHours !== 'number' || !Number.isInteger(durationHours) || durationHours < 1 || durationHours > 168)
479
480
  throw new Error('durationHours must be an integer between 1 and 168');
481
+ const holders = args.holders === undefined ? undefined : readersOf(args.holders);
482
+ if (holders && (scopeKind !== 'artifact' || agreementId || permission !== 'read'))
483
+ throw new Error('holders is for one artifact named by scopeId, to read');
480
484
  const creds = fullCreds(env);
481
485
  const result = await new ContextGrantsClient(creds.operatorKey, creds.agentId, env.baseUrl).requestAccess({
482
486
  scopeKind, scopeId, agreementId, reason: String(args.reason).trim(),
483
487
  permission: permission, temporal: args.temporal,
484
488
  durationHours, chatId: args.chatId,
485
489
  recipientId: typeof args.recipientId === 'string' ? args.recipientId.trim() : undefined,
490
+ ...(holders ? { holders } : {}),
486
491
  }, { idempotencyKey: args.idempotencyKey, laneId: creds.laneId });
487
492
  const actingAgentId = result.actingAgentId ?? (creds.agentId || undefined);
493
+ if (result.status === 'already_readable') {
494
+ return {
495
+ ...result, outcome: 'already_readable', state: 'not_requested',
496
+ ...(actingAgentId ? { actingAgentId } : {}),
497
+ scope: result.scope ?? { kind: scopeKind, id: scopeId },
498
+ note: result.message ?? 'Every reader you named can already read it. Nothing was asked.',
499
+ };
500
+ }
488
501
  return {
489
502
  ...result, outcome: 'pending', state: 'requested',
490
503
  ...(actingAgentId ? { actingAgentId } : {}),
@@ -500,7 +513,6 @@ export const CONTEXT_CAPABILITIES = [
500
513
  openCapability,
501
514
  accessExplainCapability,
502
515
  accessRequestCapability,
503
- contextRequestCapability,
504
516
  contextDelegateCapability,
505
517
  contextExpandReachCapability,
506
518
  contextDiscoverGrantableCapability,
@@ -12,7 +12,7 @@ export { TASK_CAPABILITIES, listTasksCapability, cancelTaskCapability, presentTa
12
12
  export { AGREEMENT_PROPOSE_PROVIDER_ID_DESCRIPTION } from './proposeProviderId.js';
13
13
  export { MARKETPLACE_CAPABILITIES, marketplaceViewCapability } from './marketplace.js';
14
14
  export { GRANTS_CAPABILITIES, listGrantsCapability } from './grants.js';
15
- export { CONTEXT_CAPABILITIES, contextReadCapability, openCapability, accessExplainCapability, contextDelegateCapability, contextRequestCapability, accessRequestCapability, contextExpandReachCapability, contextDiscoverGrantableCapability, contextBounds, resolveOrgScopeId, explainRefusedRead, RESOURCE_HIDDEN_MESSAGE, RESOURCE_UNREADABLE_MESSAGE, type RefusedRead, } from './context.js';
15
+ export { CONTEXT_CAPABILITIES, contextReadCapability, openCapability, accessExplainCapability, contextDelegateCapability, accessRequestCapability, contextExpandReachCapability, contextDiscoverGrantableCapability, contextBounds, resolveOrgScopeId, explainRefusedRead, RESOURCE_HIDDEN_MESSAGE, RESOURCE_UNREADABLE_MESSAGE, type RefusedRead, } from './context.js';
16
16
  export { CONNECTION_CAPABILITIES, connectionProxyCapability, requestConnectionCapability, } from './connections.js';
17
17
  export { DISCOVERY_CAPABILITIES, agentSearchCapability, agentGetCapability, } from './discovery.js';
18
18
  export { ARTIFACT_CAPABILITIES, recordArtifactCapability, listArtifactsCapability, findArtifactsCapability, type UnsearchedContext, shareArtifactCapability, attachArtifactCapability, uploadArtifactUrlCapability, completeArtifactFileCapability, downloadArtifactCapability, reextractArtifactCapability, } from './artifacts.js';
@@ -12,7 +12,7 @@ export { TASK_CAPABILITIES, listTasksCapability, cancelTaskCapability, presentTa
12
12
  export { AGREEMENT_PROPOSE_PROVIDER_ID_DESCRIPTION } from './proposeProviderId.js';
13
13
  export { MARKETPLACE_CAPABILITIES, marketplaceViewCapability } from './marketplace.js';
14
14
  export { GRANTS_CAPABILITIES, listGrantsCapability } from './grants.js';
15
- export { CONTEXT_CAPABILITIES, contextReadCapability, openCapability, accessExplainCapability, contextDelegateCapability, contextRequestCapability, accessRequestCapability, contextExpandReachCapability, contextDiscoverGrantableCapability, contextBounds, resolveOrgScopeId, explainRefusedRead, RESOURCE_HIDDEN_MESSAGE, RESOURCE_UNREADABLE_MESSAGE, } from './context.js';
15
+ export { CONTEXT_CAPABILITIES, contextReadCapability, openCapability, accessExplainCapability, contextDelegateCapability, accessRequestCapability, contextExpandReachCapability, contextDiscoverGrantableCapability, contextBounds, resolveOrgScopeId, explainRefusedRead, RESOURCE_HIDDEN_MESSAGE, RESOURCE_UNREADABLE_MESSAGE, } from './context.js';
16
16
  export { CONNECTION_CAPABILITIES, connectionProxyCapability, requestConnectionCapability, } from './connections.js';
17
17
  export { DISCOVERY_CAPABILITIES, agentSearchCapability, agentGetCapability, } from './discovery.js';
18
18
  export { ARTIFACT_CAPABILITIES, recordArtifactCapability, listArtifactsCapability, findArtifactsCapability, shareArtifactCapability, attachArtifactCapability, uploadArtifactUrlCapability, completeArtifactFileCapability, downloadArtifactCapability, reextractArtifactCapability, } from './artifacts.js';
@@ -5,10 +5,9 @@ export declare function webAppOrigin(env: CapabilityEnv): string;
5
5
  *
6
6
  * Opening a chat is itself the admission: the backend issues a standing from-now
7
7
  * write grant to each side as it creates the room, so both delegates can
8
- * read and post immediately. Do NOT tell agents to follow chat_open with an
9
- * issue_grant on that same new chat — that only mints a duplicate grant carrying
10
- * the default 30-day expiry. issue_grant / context_delegate are for scopes that
11
- * already exist (an older chat, an agreement, an org).
8
+ * read and post immediately. Sharing what already exists is artifact_share for
9
+ * one artifact, a chat_open invite for a room, or context_delegate for a slice
10
+ * of a grant the agent holds.
12
11
  */
13
12
  export declare function linkIsReachOnly(env: CapabilityEnv): string;
14
13
  export declare const listLinksCapability: CapabilityDefinition;
@@ -10,14 +10,13 @@ export function webAppOrigin(env) {
10
10
  *
11
11
  * Opening a chat is itself the admission: the backend issues a standing from-now
12
12
  * write grant to each side as it creates the room, so both delegates can
13
- * read and post immediately. Do NOT tell agents to follow chat_open with an
14
- * issue_grant on that same new chat — that only mints a duplicate grant carrying
15
- * the default 30-day expiry. issue_grant / context_delegate are for scopes that
16
- * already exist (an older chat, an agreement, an org).
13
+ * read and post immediately. Sharing what already exists is artifact_share for
14
+ * one artifact, a chat_open invite for a room, or context_delegate for a slice
15
+ * of a grant the agent holds.
17
16
  */
18
17
  export function linkIsReachOnly(env) {
19
18
  return env.surface === 'mcp'
20
- ? 'A link is reach-only — it shares no context on its own. Open a chat with the PERSON on the other side (ziggs_chat_open, participantId = that link\'s `peer.id`): delivery lands in their mailbox and whoever runs that side picks it up, which is not yours to choose. Use `peer.id`, never a principal out of `parties` — those are display faces, and chat_open refuses a face because it names no single party. Opening the room admits both sides to read and post from then on. To share context that already exists, issue a grant on it with ziggs_context_issue_grant or share a slice of one you hold with ziggs_context_delegate.'
19
+ ? 'A link is reach-only — it shares no context on its own. Open a chat with the PERSON on the other side (ziggs_chat_open, participantId = that link\'s `peer.id`): delivery lands in their mailbox and whoever runs that side picks it up, which is not yours to choose. Use `peer.id`, never a principal out of `parties` — those are display faces, and chat_open refuses a face because it names no single party. Opening the room admits both sides to read and post from then on. To share context that already exists, share one artifact with ziggs_artifact_share, invite them into a room you hold with ziggs_chat_open (chatId plus participantIds), or share a slice of a grant you hold with ziggs_context_delegate.'
21
20
  : 'A link is reach-only — it shares no context on its own. Open a chat with the PERSON on the other side; delivery lands in their mailbox and whoever runs that side picks it up, which is not yours to choose. Opening the room admits both sides to it from then on. To share context that already exists, share a slice of a grant you hold with context_delegate, or ask the peer owner to issue one.';
22
21
  }
23
22
  const LINK_STATUSES = ['active', 'open', 'cancelled', 'all'];
@@ -84,7 +83,7 @@ export const listLinksCapability = {
84
83
  readPlan.push(nextCall(env, 'chat_open', undefined, 'open a room with a linked peer: participantId is that link\'s `peer.id`. Do NOT pass a principal out of `parties` — those are display faces and chat_open refuses them'));
85
84
  }
86
85
  if (active.length) {
87
- readPlan.push(nextCall(env, 'context_issue_grant', undefined, 'share context that already exists: a link does not share any on its own'));
86
+ readPlan.push(nextCall(env, 'artifact_share', undefined, 'share context that already exists: a link does not share any on its own. One artifact with artifact_share; a room you hold by inviting them with chat_open (chatId plus participantIds)'));
88
87
  }
89
88
  return {
90
89
  count: links.length,
@@ -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
  },
@@ -137,14 +137,6 @@ const BRIEFS = {
137
137
  does: 'Asks for access to an artifact, chat, agreement or task for this agent. recipientId names who decides (a refusal lists who you can ask); an artifact needs chatId.',
138
138
  changes: 'creates a pending approval; grants nothing until approved',
139
139
  },
140
- ziggs_context_request: {
141
- does: 'Asks an artifact’s owner for read access to it, for 1 to 168 hours.',
142
- changes: 'creates a pending approval; grants nothing until approved',
143
- },
144
- ziggs_context_issue_grant: {
145
- does: 'Admits another agent to a room. Agreement, org, artifact and task grants need your person’s authority.',
146
- changes: 'admits it, pending until the people there consent',
147
- },
148
140
  ziggs_context_delegate: {
149
141
  does: 'Gives another agent a narrower slice of a grant you hold.',
150
142
  changes: 'grants it, or opens a request its owner approves (pending_approval)',
@@ -204,7 +196,7 @@ const BRIEFS = {
204
196
  changes: 'the old steps are replaced',
205
197
  },
206
198
  ziggs_task_set_result: {
207
- does: 'Closes a task as completed, failed or cancelled; the result is for the next agent, a person reads it in your conversation. The agreement stays active.',
199
+ does: 'Closes a task as completed or failed; the result is for the next agent, a person reads it in your conversation. The agreement stays active.',
208
200
  changes: 'closes the task, proving nobody read or paid; the same idempotencyKey is a no-op',
209
201
  },
210
202
  ziggs_task_cancel: {
@@ -28,7 +28,6 @@ const REQUIRED_ARGS = {
28
28
  agreement_subcontract: ['parentAgreementId', 'executorId', 'description'],
29
29
  agreement_respond: ['agreementId'],
30
30
  introduction_redeem: ['token'],
31
- context_issue_grant: ['holderId', 'scopeKind', 'scopeId'],
32
31
  artifact_share: ['artifactId', 'holderId'],
33
32
  context_delegate: ['parentGrantId', 'holderId', 'scopeKind', 'scopeId', 'temporal'],
34
33
  task_create: ['agreementId', 'description'],
@@ -59,7 +58,6 @@ const HTTP_BINDINGS = {
59
58
  path: '/agreements/:agreementId/approvals/:partyId',
60
59
  },
61
60
  marketplace_view: { method: 'GET', path: '/marketplace/offers' },
62
- context_issue_grant: { method: 'POST', path: '/context/grants' },
63
61
  artifact_share: { method: 'POST', path: '/context/artifacts/:artifactId/share' },
64
62
  context_delegate: { method: 'POST', path: '/context/grants/:parentGrantId/delegate' },
65
63
  open: { method: 'POST', path: '/context/open' },
@@ -74,11 +72,9 @@ const HTTP_BINDINGS = {
74
72
  };
75
73
  /**
76
74
  * Intentional surface exclusions. A ready next call has to exist as a tool
77
- * on the calling surface — SDK context exposes delegate, not a top-level
78
- * issue grant (tool-surface-parity).
75
+ * on the calling surface (tool-surface-parity).
79
76
  */
80
77
  const SURFACE_EXPOSURE = {
81
- context_issue_grant: ['mcp'],
82
78
  // The hosted SDK surface enumerates its own work with task_list and reads a
83
79
  // task from the wake it arrived on; ziggs_task_get is the MCP delegate's
84
80
  // one-task read. Naming it to an SDK agent would offer a tool it cannot call.
@@ -98,9 +94,6 @@ const MATERIAL_EFFECTS = {
98
94
  introduction_redeem: {
99
95
  relationship: 'a stranger redeem stages a link both humans must approve — it is not a live connection yet',
100
96
  },
101
- context_issue_grant: {
102
- disclosure: 'issues a grant that widens who can read that scope',
103
- },
104
97
  link_propose: {
105
98
  relationship: 'proposes a reach-only link; nothing is shared until they accept',
106
99
  },
@@ -109,31 +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>;
125
- requestArtifactAccess(artifactId: string, input: {
126
- chatId: string;
127
- ownerId: string;
128
- reason: string;
129
- durationHours?: number;
130
- }, opts?: {
131
- idempotencyKey?: string;
132
- laneId?: string;
133
- }): Promise<{
134
- status: 'pending_approval';
135
- agreementId: string;
136
- } & RequestSentTo>;
117
+ }): Promise<AccessRequestResult>;
137
118
  delegateGrant(parentGrantId: string, input: DelegateContextGrantInput, opts?: {
138
119
  idempotencyKey?: string;
139
120
  }): Promise<DelegateContextGrantResult>;
@@ -167,6 +148,31 @@ export declare class ContextGrantsClient {
167
148
  }>;
168
149
  }
169
150
  /** Who got a request card and where they answer it, as the server says. */
151
+ /** One reader an artifact access request asks for, named with its kind. */
152
+ export interface AccessReader {
153
+ kind: 'agent' | 'user' | 'org';
154
+ id: string;
155
+ }
156
+ /** What a request did for one named reader. */
157
+ export interface AccessReaderOutcome extends AccessReader {
158
+ outcome: 'already_readable' | 'awaiting_approval';
159
+ }
160
+ export type AccessRequestResult = {
161
+ accessGranted: false;
162
+ actingAgentId?: string;
163
+ scope?: {
164
+ kind: string;
165
+ id: string;
166
+ };
167
+ /** Per named reader, when the request named readers. */
168
+ readers?: AccessReaderOutcome[];
169
+ } & RequestSentTo & ({
170
+ status: 'pending_approval';
171
+ agreementId: string;
172
+ } | {
173
+ status: 'already_readable';
174
+ agreementId: null;
175
+ });
170
176
  export interface RequestSentTo {
171
177
  sentTo?: {
172
178
  id: string;
@@ -77,28 +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) };
84
- }
85
- async requestArtifactAccess(artifactId, input, opts) {
86
- const res = await fetch(`${this.baseUrl}/context/artifacts/${encodeURIComponent(artifactId)}/access-requests`, {
87
- method: 'POST',
88
- headers: buildOperatorHeaders(this.operatorKey, this.agentId, {
89
- 'content-type': 'application/json',
90
- ...(opts?.idempotencyKey ? { 'Idempotency-Key': opts.idempotencyKey } : {}),
91
- ...(opts?.laneId ? { 'x-ziggs-lane': opts.laneId } : {}),
92
- }),
93
- body: JSON.stringify(input),
94
- });
95
- const body = await res.text().catch(() => '');
96
- if (!res.ok)
97
- throwApiError(res, body, `requestArtifactAccess failed: ${res.status}`);
98
- const result = JSON.parse(body);
99
- if (result.status !== 'pending_approval' || !result.agreementId)
100
- throw new Error('Expected a pending artifact access request');
101
- return { status: 'pending_approval', agreementId: result.agreementId, ...sentToOf(result) };
95
+ return { status: 'pending_approval', agreementId: result.agreementId, ...common };
102
96
  }
103
97
  async delegateGrant(parentGrantId, input, opts) {
104
98
  const res = await fetch(`${this.baseUrl}/context/grants/${encodeURIComponent(parentGrantId)}/delegate`, {
@@ -48,6 +48,8 @@ export interface CreateTaskGraphNodeData {
48
48
  assigneeId?: string;
49
49
  waitsOn?: string[];
50
50
  joinKind?: 'all' | 'any';
51
+ /** A yes/no question over the steps this one waits on; it runs only on yes. */
52
+ runIf?: string;
51
53
  }
52
54
  export interface CreateTaskGraphData {
53
55
  nodes: CreateTaskGraphNodeData[];
@@ -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;
@@ -114,6 +112,20 @@ export interface Task {
114
112
  } | null;
115
113
  graphNodeId?: string | null;
116
114
  heldByGraphReview?: boolean;
115
+ /**
116
+ * A graph step that runs only if a yes/no question holds over the steps it
117
+ * waits on. Once asked: `run`, `skipped` (closed cancelled without running),
118
+ * `held` (waiting for the buyer to acknowledge its plan) or `buyer_run`.
119
+ */
120
+ runIf?: {
121
+ question: string;
122
+ outcome?: 'run' | 'skipped' | 'held' | 'buyer_run';
123
+ answer?: 'yes' | 'no' | null;
124
+ confidence?: number | null;
125
+ reason?: string;
126
+ decidedAt?: string;
127
+ decidedBy?: string | null;
128
+ } | null;
117
129
  plan?: {
118
130
  steps?: PlanStep[];
119
131
  };
@@ -139,15 +151,9 @@ export interface Task {
139
151
  payerIsYou?: boolean;
140
152
  proposedToIsYou?: boolean;
141
153
  }
142
- export type AgreementStatus = 'pending' | 'active' | 'fulfilled' | 'cancelled' | 'rejected' | (string & {});
154
+ export type AgreementStatus = AgreementStatusValue;
143
155
  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
- };
156
+ export { AGREEMENT_ENGAGEMENT_KIND, BROADCAST_TARGETS, OPEN_AGREEMENT_TARGET, ORG_AGREEMENT_TARGET, isBroadcastTarget, partyActorIds, partySideIds, type BroadcastAudience, };
151
157
  export type EngagementKind = (typeof AGREEMENT_ENGAGEMENT_KIND)[keyof typeof AGREEMENT_ENGAGEMENT_KIND];
152
158
  /**
153
159
  * One side of an agreement, as the wire carries it.
@@ -161,15 +167,6 @@ export type EngagementKind = (typeof AGREEMENT_ENGAGEMENT_KIND)[keyof typeof AGR
161
167
  export type { AgreementPartySide };
162
168
  /** The four sides of an agreement, each {@link AgreementPartySide}. */
163
169
  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
170
  /** Compact agreement reference carried by task reads. */
174
171
  export interface AgreementSummary {
175
172
  agreementId: string;
@@ -289,7 +286,6 @@ export declare const EntryTypes: {
289
286
  readonly TASK_HISTORY: "task_history";
290
287
  };
291
288
  export type EntryType = (typeof EntryTypes)[keyof typeof EntryTypes];
292
- /** ⚠️ Mirrors the server's content-type constant; the server is authoritative. */
293
289
  export declare const ContentTypes: {
294
290
  readonly TEXT: "text";
295
291
  readonly OPERATION: "operation";
@@ -297,24 +293,13 @@ export declare const ContentTypes: {
297
293
  readonly RESULT: "result";
298
294
  readonly AGREEMENT_UPDATE: "agreement_update";
299
295
  readonly TASK: "task";
300
- readonly PROPOSAL: "proposal";
296
+ readonly TASK_PLAN_STEP: "task_plan_step";
301
297
  readonly THOUGHT: "thought";
302
- /** @deprecated legacy alias for AGREEMENT_UPDATE */
303
- readonly TASK_UPDATE: "task_update";
298
+ readonly PROPOSAL: "proposal";
304
299
  };
305
300
  export type ContentType = (typeof ContentTypes)[keyof typeof ContentTypes];
306
301
  /** ⚠️ Mirrors the server's entry/content-type validation; the server is authoritative. */
307
302
  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
303
  export { isPersonaFaceRef as isPersonaRef, isRoomBindingRef as isRoomPresentationRef, isPresentationRef as isOpaquePresentationRef, } from '@ziggs-ai/contracts';
319
304
  /** Display + entitlement face for a principal on the message/roster wire. */
320
305
  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.5",
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": [