@ziggs-ai/api-client 0.32.0 → 0.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -293,6 +293,10 @@ function openBodyFromArgs(args) {
293
293
  return body;
294
294
  }
295
295
  function presentOpenResult(result, env) {
296
+ // Resource-routed OAuth calls let the backend select the assistant. Their
297
+ // local credentials have no agent id, so only the response can name it.
298
+ const actingAgentId = result.actingAgentId !== undefined
299
+ ? result.actingAgentId : fullCreds(env).agentId || undefined;
296
300
  // Expose a request-only next action, never execute it as part of a read.
297
301
  const requestTool = env.surface === 'mcp' ? 'ziggs_access_request' : 'access_request';
298
302
  const chatTool = env.surface === 'mcp' ? 'ziggs_chat_open' : 'chat_open';
@@ -302,7 +306,7 @@ function presentOpenResult(result, env) {
302
306
  return [];
303
307
  const row = item;
304
308
  const ask = row.grantRequest;
305
- if (row.status !== 'active' || !isContextAccessRequest(ask) || ask.holderId !== fullCreds(env).agentId || !ask.scope?.id || !['artifact', 'chat', 'agreement', 'task'].includes(ask.scope.kind ?? ''))
309
+ if (row.status !== 'active' || !isContextAccessRequest(ask) || !actingAgentId || ask.holderId !== actingAgentId || !ask.scope?.id || !['artifact', 'chat', 'agreement', 'task'].includes(ask.scope.kind ?? ''))
306
310
  return [];
307
311
  return [{ kind: 'resume', tool: env.surface === 'mcp' ? 'ziggs_open' : 'open', args: { [`${ask.scope.kind}Id`]: ask.scope.id }, effect: 'read_only', note: 'Approval recorded. Open the original scope; the server rechecks expiry and revocation.' }];
308
312
  });
@@ -316,7 +320,7 @@ function presentOpenResult(result, env) {
316
320
  : result.ref.kind === 'artifact' && result.outcome === 'unreadable'
317
321
  ? [requestAccessAction({ scopeKind: 'artifact', scopeId: result.ref.id, permission: 'read', durationHours: 24 }, env)]
318
322
  : resumeActions,
319
- actingAgentId: fullCreds(env).agentId,
323
+ ...(actingAgentId !== undefined ? { actingAgentId } : {}),
320
324
  ...(!result.actions && result.ref.kind === 'artifact' && result.outcome !== 'ok' ? {
321
325
  requestAccessHint: `If a source you already read supplied this artifact reference and its human owner, ${requestTool} scopeKind=artifact can ask that owner for temporary read access without a prior grant. It needs a conversation with the owner; ${chatTool} can open or reuse one, subject to authorization. Discover the request tool's schema before calling it. This response does not confirm existence or ownership. A request grants no access until the human owner approves.`,
322
326
  } : {}),
@@ -409,15 +413,15 @@ export const accessRequestCapability = {
409
413
  title: 'Request access for yourself',
410
414
  annotation: 'write', needsAgentId: true,
411
415
  descriptions: {
412
- 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. 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.',
413
- mcp: 'Request access for this agent, never approve or grant it. Use scopeId for an artifact, chat, agreement or task; scopeKind=artifact with agreementId of your active hire resolves its system-maintained workspace summary artifact. Name who to ask in recipientId; a pick the server cannot use here is refused, and the refusal lists who you can ask. Artifact requests also require chatId. Explain the scope and lifetime; summary access includes future updates only to that artifact. Surface appUrl and wait for human approval; then open the returned scope. Pending grants no access.',
416
+ 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.',
417
+ mcp: 'Request access for this agent, never approve or grant it. Use scopeId for an artifact, chat, agreement or task; scopeKind=artifact with agreementId of your active hire resolves its system-maintained workspace summary artifact. Name who to ask in recipientId; a pick the server cannot use here is refused, and the refusal lists who you can ask. Artifact requests also require chatId. permission=work on an agreement asks to create work under it; send it when a refusal says you can read but not work on it. Explain the scope and lifetime; summary access includes future updates only to that artifact. Surface appUrl and wait for human approval; then open the returned scope. Pending grants no access.',
414
418
  },
415
419
  params: {
416
420
  scopeKind: { type: 'string', required: true, enum: ['artifact', 'chat', 'agreement', 'task'] },
417
421
  scopeId: { type: 'string', description: 'Original resource id. Omit when using a hire agreementId to request its workspace summary.' },
418
422
  agreementId: { type: 'string', description: 'Artifact scope only: current hire id, used instead of scopeId to request the system-maintained workspace summary.' },
419
423
  reason: { type: 'string', required: true, description: 'Why access is needed and who will receive the reply or summary' },
420
- permission: { type: 'string', enum: ['read', 'reply'], description: 'read by default; reply is read and reply and is only valid for a chat' },
424
+ permission: { type: 'string', enum: ['read', 'reply', 'work'], description: 'read by default; reply is read and reply and is only valid for a chat; work lets you create work under an agreement you can read but not work on, and lasts until it ends' },
421
425
  temporal: { type: 'string', enum: ['from-now', 'from-start'], description: 'Chat history requested; default from-now. Earlier messages need from-start. Other resources use from-start.' },
422
426
  durationHours: { type: 'number', description: 'Integer 1–168 hours after approval; default 24' },
423
427
  chatId: { type: 'string', description: 'Artifact or workspace requests only: existing conversation with the owner' },
@@ -437,8 +441,10 @@ export const accessRequestCapability = {
437
441
  if (Boolean(agreementId) === Boolean(scopeId) || (agreementId && scopeKind !== 'artifact'))
438
442
  throw new Error('Provide scopeId, or agreementId for its workspace summary artifact');
439
443
  const permission = args.permission ?? 'read';
440
- if (!['read', 'reply'].includes(String(permission)) || (permission === 'reply' && scopeKind !== 'chat'))
444
+ if (!['read', 'reply', 'work'].includes(String(permission)) || (permission === 'reply' && scopeKind !== 'chat'))
441
445
  throw new Error('Only chat access may request reply permission');
446
+ if (permission === 'work' && scopeKind !== 'agreement')
447
+ throw new Error('Only agreement access may request work permission');
442
448
  if (args.temporal !== undefined && (!['from-now', 'from-start'].includes(String(args.temporal)) || (scopeKind !== 'chat' && args.temporal === 'from-now')))
443
449
  throw new Error('Only chats support from-now access');
444
450
  const durationHours = args.durationHours ?? 24;
@@ -451,8 +457,10 @@ export const accessRequestCapability = {
451
457
  durationHours, chatId: args.chatId,
452
458
  recipientId: typeof args.recipientId === 'string' ? args.recipientId.trim() : undefined,
453
459
  }, { idempotencyKey: args.idempotencyKey, laneId: creds.laneId });
460
+ const actingAgentId = result.actingAgentId ?? (creds.agentId || undefined);
454
461
  return {
455
- ...result, outcome: 'pending', state: 'requested', actingAgentId: creds.agentId,
462
+ ...result, outcome: 'pending', state: 'requested',
463
+ ...(actingAgentId ? { actingAgentId } : {}),
456
464
  scope: result.scope ?? { kind: scopeKind, id: scopeId },
457
465
  appUrl: `${(env.webUrl ?? 'https://ziggsai.com').replace(/\/$/, '')}/app/agreements/${encodeURIComponent(result.agreementId)}`,
458
466
  resumeAfterApproval: { tool: env.surface === 'mcp' ? 'ziggs_open' : 'open', args: result.scope ? { [`${result.scope.kind}Id`]: result.scope.id } : { [`${scopeKind}Id`]: scopeId } },
@@ -90,14 +90,10 @@ export interface WriteArtifactInput {
90
90
  * When the deliverable lives outside Ziggs: both provider and http(s) URL.
91
91
  * The `text` is the in-Ziggs summary. Ziggs does not fetch the URL.
92
92
  */
93
- externalRef?: {
94
- provider: string;
95
- url: string;
96
- };
93
+ externalRef?: ArtifactExternalRef;
97
94
  }
98
- /** Every file format the server stores, in its order. One list for the type and the tool enums. */
99
- export declare const ARTIFACT_FILE_FORMATS: readonly ["pdf", "docx", "hwpx", "pptx", "md", "txt", "html", "mp3"];
100
- export type ArtifactFileFormat = (typeof ARTIFACT_FILE_FORMATS)[number];
95
+ /** Every file format the server stores — the contracts list, re-exported under the names callers already use. */
96
+ export { ARTIFACT_FILE_FORMATS, type ArtifactExternalRef, type ArtifactFileFormat } from '@ziggs-ai/contracts';
101
97
  export interface ArtifactUploadUrlInput {
102
98
  /** Short list name. Defaults to filename on the server when omitted. */
103
99
  name?: string;
@@ -137,6 +133,7 @@ export type ArtifactFileView = Record<string, unknown> & {
137
133
  extractionError?: string | null;
138
134
  filename?: string | null;
139
135
  };
136
+ import type { ArtifactExternalRef, ArtifactFileFormat } from '@ziggs-ai/contracts';
140
137
  export { AGREEMENT_LANE_PREFIX } from '@ziggs-ai/contracts';
141
138
  export declare function artifactScopeForSession(sessionId: string): {
142
139
  chatId: string;
@@ -13,8 +13,8 @@ export const ARTIFACT_INLINE_TEXT_OVER_LIMIT_HINT = `text exceeds ${ARTIFACT_INL
13
13
  'For larger content use ziggs_artifact_upload_url (file rail), or split into ' +
14
14
  'an index artifact plus part artifacts and list the part ids in the index. ' +
15
15
  'The server does not auto-split.';
16
- /** Every file format the server stores, in its order. One list for the type and the tool enums. */
17
- export const ARTIFACT_FILE_FORMATS = ['pdf', 'docx', 'hwpx', 'pptx', 'md', 'txt', 'html', 'mp3'];
16
+ /** Every file format the server stores — the contracts list, re-exported under the names callers already use. */
17
+ export { ARTIFACT_FILE_FORMATS } from '@ziggs-ai/contracts';
18
18
  function resolveUploadBytes(input) {
19
19
  if (input.content != null) {
20
20
  return Buffer.isBuffer(input.content)
@@ -102,7 +102,7 @@ export declare class ContextGrantsClient {
102
102
  scopeId?: string;
103
103
  agreementId?: string;
104
104
  reason: string;
105
- permission?: 'read' | 'reply';
105
+ permission?: 'read' | 'reply' | 'work';
106
106
  temporal?: 'from-now' | 'from-start';
107
107
  durationHours?: number;
108
108
  chatId?: string;
@@ -116,6 +116,7 @@ export declare class ContextGrantsClient {
116
116
  status: 'pending_approval';
117
117
  agreementId: string;
118
118
  accessGranted: false;
119
+ actingAgentId?: string;
119
120
  scope?: {
120
121
  kind: string;
121
122
  id: string;
@@ -80,7 +80,7 @@ export class ContextGrantsClient {
80
80
  if (result.status !== 'pending_approval' || !result.agreementId || result.accessGranted !== false) {
81
81
  throw new Error('Expected a pending access request with accessGranted=false');
82
82
  }
83
- return { status: 'pending_approval', agreementId: result.agreementId, accessGranted: false, ...(result.scope ? { scope: result.scope } : {}), ...sentToOf(result) };
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
84
  }
85
85
  async requestArtifactAccess(artifactId, input, opts) {
86
86
  const res = await fetch(`${this.baseUrl}/context/artifacts/${encodeURIComponent(artifactId)}/access-requests`, {
@@ -51,6 +51,8 @@ export interface ContextOpenAccess {
51
51
  };
52
52
  }
53
53
  export interface ContextOpenResult {
54
+ /** Server-resolved caller; absent on older servers, null for human callers. */
55
+ actingAgentId?: string | null;
54
56
  /** Optional for servers predating resource action projections. */
55
57
  actions?: ResourceAction[];
56
58
  outcome: ContextOpenOutcome;
@@ -20,7 +20,9 @@ export declare class InboxClient {
20
20
  constructor(operatorKey: string, agentId?: string, baseUrl?: string, instanceId?: string);
21
21
  private headers;
22
22
  private inboxUrl;
23
- getInbox(opts?: InboxReadOptions): Promise<InboxEnvelope>;
23
+ getInbox(opts?: InboxReadOptions & {
24
+ signal?: AbortSignal;
25
+ }): Promise<InboxEnvelope>;
24
26
  /**
25
27
  * Wait for assigned mail without taking the host lease.
26
28
  * Still sends X-Ziggs-Instance; the server ignores it on this route.
@@ -50,6 +50,7 @@ export class InboxClient {
50
50
  async getInbox(opts = {}) {
51
51
  const res = await fetch(this.inboxUrl('/inbox', opts), {
52
52
  headers: this.headers(),
53
+ ...(opts.signal ? { signal: opts.signal } : {}),
53
54
  });
54
55
  const body = await res.text().catch(() => '');
55
56
  if (!res.ok) {
@@ -22,7 +22,7 @@ export type GrantHolderKind = 'user' | 'org' | 'agent';
22
22
  * both: `read` sees the scope, `write` takes part in it, `admit` brings others
23
23
  * in as well.
24
24
  */
25
- export type GrantAccessKind = 'read' | 'write' | 'admit';
25
+ export type { GrantAccessKind };
26
26
  /**
27
27
  * The kind of principal-authored consent object a grant rests on. Every grant
28
28
  * is minted by one of these, or delegated from a parent grant that was.
@@ -62,6 +62,7 @@ export type { GrantScopeView, GrantCaveatView };
62
62
  * client believed a different subset of the row.
63
63
  */
64
64
  import type { GrantView, GrantScopeView, GrantCaveatView } from '@ziggs-ai/contracts';
65
+ import type { GrantAccessKind } from '@ziggs-ai/contracts';
65
66
  export type { GrantView };
66
67
  /** Value of the first caveat of `type` on a grant, or undefined. */
67
68
  export declare function grantCaveat(grant: GrantView, type: string): unknown | undefined;
package/dist/index.d.ts CHANGED
@@ -17,6 +17,8 @@ export type { DecisionOutcome, DecisionWords, DecisionWordsInput, } from './deci
17
17
  export { planInboxAck, planPartialInboxAck, inboxAckAllowed, inboxAckHandledIds, } from './inboxAck.js';
18
18
  export type { InboxAckDecision, InboxAckStep, InboxAckEnvelope, PlanInboxAckOptions, } from './inboxAck.js';
19
19
  export { agreementLaneId, agreementIdFromLane, isAgreementLaneId, isAgreementId } from '@ziggs-ai/contracts';
20
+ /** The one way to show points (hundredths → ϟ5.00), shared with the web and the phone. */
21
+ export { POINTS_GLYPH, formatPoints, formatPointsPrice } from '@ziggs-ai/contracts';
20
22
  export * from './shared/operatorKey.js';
21
23
  export { INBOX_HOST_CLAIMANT_HEADER, INSTANCE_IDENTITY_MAX_LENGTH, cliHostIdentity, hostIdentity, instanceIdentity, mcpConnectionIdentity, } from './instanceIdentity.js';
22
24
  export * from './utils/appUrls.js';
package/dist/index.js CHANGED
@@ -18,6 +18,8 @@ export { ApiError } from './types.js';
18
18
  export { decisionWords, speakableActor } from './decisionWords.js';
19
19
  export { planInboxAck, planPartialInboxAck, inboxAckAllowed, inboxAckHandledIds, } from './inboxAck.js';
20
20
  export { agreementLaneId, agreementIdFromLane, isAgreementLaneId, isAgreementId } from '@ziggs-ai/contracts';
21
+ /** The one way to show points (hundredths → ϟ5.00), shared with the web and the phone. */
22
+ export { POINTS_GLYPH, formatPoints, formatPointsPrice } from '@ziggs-ai/contracts';
21
23
  export * from './shared/operatorKey.js';
22
24
  export { INBOX_HOST_CLAIMANT_HEADER, INSTANCE_IDENTITY_MAX_LENGTH, cliHostIdentity, hostIdentity, instanceIdentity, mcpConnectionIdentity, } from './instanceIdentity.js';
23
25
  export * from './utils/appUrls.js';
package/dist/types.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { AgreementPartySide, AgreementParties, PrincipalPresentation, InboxDeliveryRef } from '@ziggs-ai/contracts';
2
2
  import type { InboxRequestChannel } from '@ziggs-ai/contracts';
3
+ import type { InboxPartyKind } from '@ziggs-ai/contracts';
3
4
  /**
4
5
  * One failure shape for every HTTP client path.
5
6
  *
@@ -368,7 +369,7 @@ export type MessageHandler = (text: string, metadata: MessageMetadata) => Promis
368
369
  */
369
370
  export type InboxDeliveryKind = 'message' | 'artifact' | 'task-state' | 'agreement' | 'request';
370
371
  /** The two mailbox owners. Agents own no mailbox — they read through grants. */
371
- export type InboxPartyKind = 'human' | 'org';
372
+ export type { InboxPartyKind };
372
373
  /** One mailbox this reader's merged view includes. */
373
374
  export interface InboxSourceRef {
374
375
  partyId: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ziggs-ai/api-client",
3
- "version": "0.32.0",
3
+ "version": "0.33.0",
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.9.0",
30
+ "@ziggs-ai/contracts": "^0.21.0",
31
31
  "socket.io-client": "^4.7.0"
32
32
  },
33
33
  "keywords": [