@ziggs-ai/api-client 0.28.1 → 0.29.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.
@@ -1,3 +1,4 @@
1
+ import { presentResourceActions } from './resourceActions.js';
1
2
  import { ContextReadClient, CONTEXT_READ_TYPES, CONTEXT_READ_VIA, parseVia, viaHint, } from '../http/ContextReadClient.js';
2
3
  import { ContextOpenClient } from '../http/ContextOpenClient.js';
3
4
  import { ContextGrantsClient, } from '../http/ContextGrantsClient.js';
@@ -306,9 +307,16 @@ function presentOpenResult(result, env) {
306
307
  });
307
308
  return {
308
309
  ...result,
309
- actions: result.access.continuation?.request ? [requestAccessAction(result.access.continuation.request, env)] : result.ref.kind === 'artifact' && result.outcome === 'unreadable' ? [requestAccessAction({ scopeKind: 'artifact', scopeId: result.ref.id, permission: 'read', durationHours: 24 }, env)] : resumeActions,
310
+ actions: result.actions
311
+ ? [...presentResourceActions(result.actions, env.surface),
312
+ ...resumeActions.map(a => ({ ...a, action: 'resume', state: 'not_evaluated' }))]
313
+ : result.access.continuation?.request
314
+ ? [requestAccessAction(result.access.continuation.request, env)]
315
+ : result.ref.kind === 'artifact' && result.outcome === 'unreadable'
316
+ ? [requestAccessAction({ scopeKind: 'artifact', scopeId: result.ref.id, permission: 'read', durationHours: 24 }, env)]
317
+ : resumeActions,
310
318
  actingAgentId: fullCreds(env).agentId,
311
- ...(result.ref.kind === 'artifact' && result.outcome !== 'ok' ? {
319
+ ...(!result.actions && result.ref.kind === 'artifact' && result.outcome !== 'ok' ? {
312
320
  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.`,
313
321
  } : {}),
314
322
  };
@@ -318,8 +326,8 @@ export const openCapability = {
318
326
  names: { sdk: 'open', mcp: 'ziggs_open' },
319
327
  title: 'Open a returned resource',
320
328
  descriptions: {
321
- sdk: 'Open an artifact, chat, task, or agreement you were given an id for. Pass exactly one of artifactId, chatId, taskId, agreementId. Do not pick a grant id or reconstruct type/via — the server resolves the read path and rechecks authorization every time. Read-only: this never requests access or records an approval. Reading is not reply, share, delegate, approve, or original-file download. Extraction pending/failed is not an empty document.',
322
- mcp: 'Open an artifact, chat, task, or agreement from an ordinary returned id (inbox, find, task result). Pass exactly one of artifactId, chatId, taskId, agreementId. Do not call ziggs_grant_list first, do not pick a grant id, and do not reconstruct type/via — the server resolves the path and rechecks every call. A saved id is not authority. Read-only: never requests access or approves. Hidden and unknown look the same; a discoverable-but-unreadable row names an owner-decision, which this call does not execute. Text access is not a file download. Extraction pending/failed is not an empty document.',
329
+ sdk: 'Read a returned resource: pass exactly one of artifactId, chatId, taskId, agreementId. The backend resolves access and returns scoped actions with required inputs and recovery calls. Opening executes none of those actions. Extraction pending/failed is not an empty document.',
330
+ mcp: 'Read a returned artifact, chat, task, or agreement: pass exactly one corresponding id. No preliminary grant lookup. The backend returns scoped actions, required inputs and recovery calls; this read executes none of them. Hidden and unknown look the same. Extraction pending/failed is not an empty document.',
323
331
  },
324
332
  annotation: 'read-only',
325
333
  params: {
@@ -341,8 +349,8 @@ export const accessExplainCapability = {
341
349
  names: { sdk: 'access_explain', mcp: 'ziggs_access_explain' },
342
350
  title: 'Explain access to a resource',
343
351
  descriptions: {
344
- sdk: 'Read-only access explanation for an ordinary returned id. Same one-of artifactId/chatId/taskId/agreementId as open. Says whether you can read, and that reading does not imply reply, share, delegate, approve, or download. Does not return content and does not request or approve anything.',
345
- mcp: 'Read-only access explanation for an ordinary returned id (exactly one of artifactId, chatId, taskId, agreementId). No grant id. Rechecks authorization. Names the owner-decision route when you cannot read a discoverable resource; this call never executes that request. Hidden and unknown reveal no labels, owners, excerpts, or counts.',
352
+ sdk: 'Read-only access explanation for an ordinary returned id. Same one-of artifactId/chatId/taskId/agreementId as open. Returns scoped action states, required inputs and recovery calls. Does not return content and does not request or approve anything.',
353
+ mcp: 'Read-only access explanation for an ordinary returned id (exactly one of artifactId, chatId, taskId, agreementId). No grant id. Rechecks authorization. Returns scoped actions and recovery calls; this call never executes them. Hidden and unknown reveal no labels, owners, excerpts, or counts.',
346
354
  },
347
355
  annotation: 'read-only',
348
356
  params: OPEN_ID_PARAMS,
@@ -18,3 +18,4 @@ export { ARTIFACT_CAPABILITIES, recordArtifactCapability, listArtifactsCapabilit
18
18
  export { CHAT_CAPABILITIES, openConversationCapability, presentSendResult, type SendPresentation, } from './chat.js';
19
19
  export { EVERYDAY_MCP_TOOLS, EVERYDAY_DESTRUCTIVE_MCP, EVERYDAY_NO_SDK_TWIN, EVERYDAY_SDK_TOOLS, } from './everyday.js';
20
20
  export { annotationForSdkTool } from './toolLane.js';
21
+ export { presentResourceActions } from './resourceActions.js';
@@ -18,3 +18,4 @@ export { ARTIFACT_CAPABILITIES, recordArtifactCapability, listArtifactsCapabilit
18
18
  export { CHAT_CAPABILITIES, openConversationCapability, presentSendResult, } from './chat.js';
19
19
  export { EVERYDAY_MCP_TOOLS, EVERYDAY_DESTRUCTIVE_MCP, EVERYDAY_NO_SDK_TWIN, EVERYDAY_SDK_TOOLS, } from './everyday.js';
20
20
  export { annotationForSdkTool } from './toolLane.js';
21
+ export { presentResourceActions } from './resourceActions.js';
@@ -4,7 +4,9 @@ import type { CapabilityDefinition, CapabilityEnv } from './types.js';
4
4
  * One next step: the tool the calling surface registers, any args already
5
5
  * known, and why.
6
6
  *
7
- * Ready means a caller can run it verbatim. Incomplete means a required
7
+ * Ready means the call has complete arguments and no declared hold; it is
8
+ * not an authorization decision. The server checks permission on execution.
9
+ * Incomplete means a required
8
10
  * argument is missing or an address field is not an address — the suggestion
9
11
  * still names the tool, and `missing` says what has to be filled first.
10
12
  *
@@ -19,7 +21,7 @@ export interface NextCall {
19
21
  args?: Record<string, unknown>;
20
22
  /** One clause: what running this achieves, or what is still missing. */
21
23
  why: string;
22
- /** True only when every required argument is present and addressable. */
24
+ /** Arguments are complete and no hold is declared; does not prove authority. */
23
25
  ready: boolean;
24
26
  /** Required argument names that are absent or not an address. */
25
27
  missing?: readonly string[];
@@ -0,0 +1,3 @@
1
+ import type { ResourceAction } from '../http/ContextOpenClient.js';
2
+ /** Bind canonical tool names without changing the backend's decision or scope. */
3
+ export declare function presentResourceActions(actions: ResourceAction[], surface: 'sdk' | 'mcp'): ResourceAction[];
@@ -0,0 +1,9 @@
1
+ /** Bind canonical tool names without changing the backend's decision or scope. */
2
+ export function presentResourceActions(actions, surface) {
3
+ const toolName = (name) => surface === 'mcp' && !name.startsWith('ziggs_') ? `ziggs_${name}` : name;
4
+ const call = (value) => ({ ...value, tool: toolName(value.tool) });
5
+ return actions.map(action => ({ ...action,
6
+ ...(action.tool ? { tool: toolName(action.tool) } : {}),
7
+ ...(action.recovery ? { recovery: call(action.recovery) } : {}),
8
+ }));
9
+ }
@@ -190,8 +190,8 @@ export declare function resolveMyPendingApprovalPartyId(agreement: Agreement, op
190
190
  * Record this party's approval decision on an agreement
191
191
  * (`PUT /agreements/:agreementId/approvals/:partyId`).
192
192
  *
193
- * `partyId` MUST match the authenticated actor (`actor.id === partyId`): owner
194
- * principal when approving as human, delegate agent id when impersonating.
193
+ * Use the partyId from the server's approvalAction. Agents need covering
194
+ * approval authority even for their own slot; the server rechecks it on PUT.
195
195
  * Canonical for hire, service, link, and request proposals.
196
196
  */
197
197
  export declare function approveAgreementAsParty(agreementId: string, partyId: string, status: 'approved' | 'rejected', creds: Creds): Promise<Agreement>;
@@ -53,6 +53,7 @@ export function linkSummary(a) {
53
53
  // chat with. Dropping it here is what that looked like: the server sent the
54
54
  // peer and the allow-list ate it before any caller saw it.
55
55
  ...(a.peer ? { peer: a.peer } : {}),
56
+ ...(a.approvalAction ? { approvalAction: a.approvalAction } : {}),
56
57
  ...(a.description ? { description: a.description } : {}),
57
58
  // Seat bookkeeping on an open invite — link state, not commerce.
58
59
  ...(a.linkInvite ? { linkInvite: a.linkInvite } : {}),
@@ -222,7 +223,11 @@ export async function respondToAgreement(agreementId, action, creds, opts = {})
222
223
  if (!agreement) {
223
224
  throw new Error(`Agreement ${agreementId} not found`);
224
225
  }
225
- const partyId = resolveMyPendingApprovalPartyId(agreement, {
226
+ const approval = agreement.approvalAction;
227
+ if (approval && (approval.state !== 'available' || !approval.partyId)) {
228
+ throw new Error(`${approval.explanation}${opts.appUrl ? ` Review: ${opts.appUrl}` : ''}`);
229
+ }
230
+ const partyId = approval?.partyId ?? resolveMyPendingApprovalPartyId(agreement, {
226
231
  ownerUserId: opts.ownerUserId,
227
232
  agentId: creds.agentId,
228
233
  });
@@ -288,8 +293,8 @@ export function resolveMyPendingApprovalPartyId(agreement, opts) {
288
293
  * Record this party's approval decision on an agreement
289
294
  * (`PUT /agreements/:agreementId/approvals/:partyId`).
290
295
  *
291
- * `partyId` MUST match the authenticated actor (`actor.id === partyId`): owner
292
- * principal when approving as human, delegate agent id when impersonating.
296
+ * Use the partyId from the server's approvalAction. Agents need covering
297
+ * approval authority even for their own slot; the server rechecks it on PUT.
293
298
  * Canonical for hire, service, link, and request proposals.
294
299
  */
295
300
  export async function approveAgreementAsParty(agreementId, partyId, status, creds) {
@@ -14,16 +14,25 @@ export interface ContextOpenBody {
14
14
  after?: string;
15
15
  limit?: number;
16
16
  }
17
- export interface ContextOpenAbilities {
18
- read: boolean;
19
- reply: boolean;
20
- share: boolean;
21
- delegate: boolean;
22
- approve: boolean;
23
- download: boolean;
17
+ /** Backend-evaluated action, scoped to a resource. Never a permission token. */
18
+ export interface ResourceActionCall {
19
+ tool: string;
20
+ args: Record<string, unknown>;
21
+ required?: string[];
22
+ }
23
+ export interface ResourceAction extends Partial<ResourceActionCall> {
24
+ action: string;
25
+ state: 'available' | 'needs_decision' | 'unavailable' | 'not_evaluated';
26
+ reasonCode?: string;
27
+ reason?: unknown;
28
+ scope?: {
29
+ kind: string;
30
+ id: string;
31
+ agreementId?: string;
32
+ };
33
+ recovery?: ResourceActionCall;
24
34
  }
25
35
  export interface ContextOpenAccess {
26
- abilities: ContextOpenAbilities;
27
36
  summary: string;
28
37
  document?: {
29
38
  empty: boolean;
@@ -42,6 +51,8 @@ export interface ContextOpenAccess {
42
51
  };
43
52
  }
44
53
  export interface ContextOpenResult {
54
+ /** Optional for servers predating resource action projections. */
55
+ actions?: ResourceAction[];
45
56
  outcome: ContextOpenOutcome;
46
57
  ref: ContextOpenRef;
47
58
  access: ContextOpenAccess;
@@ -57,6 +57,8 @@ export interface ContextSnapshotParticipant {
57
57
  [key: string]: unknown;
58
58
  }
59
59
  export interface ContextSnapshotResult {
60
+ /** Server-resolved orientation; writes recheck authority. */
61
+ workBrief?: Record<string, unknown>;
60
62
  work?: Record<string, unknown>;
61
63
  history: unknown[];
62
64
  agreements: unknown[];
@@ -10,7 +10,7 @@ export type { ArtifactVisibility, ListArtifactsOptions, ListArtifactsQuery, List
10
10
  export { ContextReadClient, CONTEXT_READ_TYPES, CONTEXT_READ_VIA, VIA_KINDS, parseVia, viaHint, } from './ContextReadClient.js';
11
11
  export type { ContextReadType, ContextReadQuery, ContextReadEnvelope, ContextSnapshotResult, ContextSnapshotParticipant, ViaKind, } from './ContextReadClient.js';
12
12
  export { ContextOpenClient, CONTEXT_OPEN_KINDS } from './ContextOpenClient.js';
13
- export type { ContextOpenKind, ContextOpenOutcome, ContextOpenRef, ContextOpenBody, ContextOpenAbilities, ContextOpenAccess, ContextOpenResult, ContextExplainResult, } from './ContextOpenClient.js';
13
+ export type { ContextOpenKind, ContextOpenOutcome, ContextOpenRef, ContextOpenBody, ResourceAction, ResourceActionCall, ContextOpenAccess, ContextOpenResult, ContextExplainResult, } from './ContextOpenClient.js';
14
14
  export { ContextDiscoveryClient } from './ContextDiscoveryClient.js';
15
15
  export type { DiscoverableItem } from './ContextDiscoveryClient.js';
16
16
  export { IntroductionsClient } from './IntroductionsClient.js';
package/dist/types.d.ts CHANGED
@@ -176,6 +176,11 @@ export interface AgreementApprovalEntry {
176
176
  heldReason?: string | null;
177
177
  }
178
178
  export interface Agreement {
179
+ approvalAction?: {
180
+ state: 'available' | 'needs_decision' | 'unavailable';
181
+ partyId?: string;
182
+ explanation: string;
183
+ };
179
184
  agreementId: string;
180
185
  parentAgreementId?: string | null;
181
186
  rootAgreementId?: string | null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ziggs-ai/api-client",
3
- "version": "0.28.1",
3
+ "version": "0.29.0",
4
4
  "description": "HTTP and WebSocket client for the Ziggs backend API",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",