@ziggs-ai/api-client 0.1.23 → 0.1.25

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.
@@ -126,8 +126,15 @@ export async function respondToAgreement(agreementId, action, creds, opts = {})
126
126
  }
127
127
  const proposedTo = agreement.parties?.proposedTo;
128
128
  if (isBroadcastTarget(proposedTo)) {
129
- if (action === 'reject')
130
- return agreement;
129
+ if (action === 'reject') {
130
+ // ZIG-719: an open/broadcast proposal is offered to many via a sentinel
131
+ // `proposedTo` ('everyone' | 'org'), so there is no per-recipient party
132
+ // slot for one recipient to reject and nothing a single reject could
133
+ // change server-side. Returning the agreement here reported a fake
134
+ // success while the item stayed claimable. Fail honestly instead — the
135
+ // caller either claims it (action=approve → POST /claim) or ignores it.
136
+ throw new Error(`Agreement ${agreementId} is an open/broadcast proposal (proposedTo="${String(proposedTo)}") and cannot be rejected by a single recipient — it has no personal approval slot. Ignore it to pass on it, or use action=approve to claim it.`);
137
+ }
131
138
  const { agreement: updated } = await claimAgreement(agreementId, creds);
132
139
  return updated;
133
140
  }
@@ -11,6 +11,16 @@ export interface ContextReachDescriptor {
11
11
  parentGrantId: string | null;
12
12
  createdAt: string;
13
13
  }
14
+ /** Labels-only pointer to context the agent could request access to (P4). */
15
+ export interface DiscoverableItem {
16
+ type: 'chat';
17
+ label: string;
18
+ scopeRef: {
19
+ kind: 'chat' | 'agreement' | 'org';
20
+ id: string;
21
+ };
22
+ orgId: string;
23
+ }
14
24
  /**
15
25
  * ZIG-412 discovery — scope descriptors only, no content.
16
26
  */
@@ -24,4 +34,11 @@ export declare class ContextDiscoveryClient {
24
34
  */
25
35
  constructor(operatorKey: string, agentId?: string, baseUrl?: string);
26
36
  discover(): Promise<ContextReachDescriptor[]>;
37
+ /**
38
+ * P4: what context EXISTS in the agent's engaged orgs that it does NOT hold
39
+ * a grant for — so it can request access rather than fail blind. Labels only.
40
+ * Defensively re-shapes each row to the whitelist (type/label/scopeRef/orgId)
41
+ * so an over-broad server response can never leak content/tokens through here.
42
+ */
43
+ discoverGrantable(): Promise<DiscoverableItem[]>;
27
44
  }
@@ -31,4 +31,26 @@ export class ContextDiscoveryClient {
31
31
  const parsed = JSON.parse(body);
32
32
  return parsed.reach ?? [];
33
33
  }
34
+ /**
35
+ * P4: what context EXISTS in the agent's engaged orgs that it does NOT hold
36
+ * a grant for — so it can request access rather than fail blind. Labels only.
37
+ * Defensively re-shapes each row to the whitelist (type/label/scopeRef/orgId)
38
+ * so an over-broad server response can never leak content/tokens through here.
39
+ */
40
+ async discoverGrantable() {
41
+ const res = await fetch(`${this.baseUrl}/context/discovery/available`, {
42
+ headers: buildOperatorHeaders(this.operatorKey, this.agentId),
43
+ });
44
+ const body = await res.text().catch(() => '');
45
+ if (!res.ok) {
46
+ throw new Error(`ContextDiscoveryClient.discoverGrantable ${res.status} ${body.slice(0, 200)}`);
47
+ }
48
+ const parsed = JSON.parse(body);
49
+ return (parsed.items ?? []).map((i) => ({
50
+ type: i.type,
51
+ label: i.label,
52
+ scopeRef: { kind: i.scopeRef.kind, id: i.scopeRef.id },
53
+ orgId: i.orgId,
54
+ }));
55
+ }
34
56
  }
@@ -31,6 +31,17 @@ export interface DelegateContextGrantInput {
31
31
  expiresAt?: string | null;
32
32
  watermarkAt?: string;
33
33
  }
34
+ /** Delegating your own principal's grant mints immediately; passing a grant
35
+ * whose original owner is a different party opens a request that owner
36
+ * approves — no grant is minted until then. */
37
+ export type DelegateContextGrantResult = {
38
+ status: 'granted';
39
+ grant: ContextGrantRecord;
40
+ } | {
41
+ status: 'pending_approval';
42
+ agreementId: string;
43
+ ownerId?: string;
44
+ };
34
45
  /**
35
46
  * ZIG-411 context grant management — list / issue / delegate / revoke.
36
47
  */
@@ -44,7 +55,7 @@ export declare class ContextGrantsClient {
44
55
  */
45
56
  constructor(operatorKey: string, agentId?: string, baseUrl?: string);
46
57
  issueGrant(input: IssueContextGrantInput): Promise<ContextGrantRecord>;
47
- delegateGrant(parentGrantId: string, input: DelegateContextGrantInput): Promise<ContextGrantRecord>;
58
+ delegateGrant(parentGrantId: string, input: DelegateContextGrantInput): Promise<DelegateContextGrantResult>;
48
59
  revokeGrant(grantId: string): Promise<{
49
60
  status: string;
50
61
  revokedCount?: number;
@@ -74,10 +74,19 @@ export class ContextGrantsClient {
74
74
  throwApiError(res, body, `delegateGrant failed: ${res.status} ${res.statusText}`);
75
75
  }
76
76
  const parsed = JSON.parse(body);
77
+ // Re-granting a grant whose original owner is a different party does not
78
+ // mint — it opens a request that owner must approve.
79
+ if (parsed.status === 'pending_approval' && parsed.agreementId) {
80
+ return {
81
+ status: 'pending_approval',
82
+ agreementId: parsed.agreementId,
83
+ ownerId: parsed.ownerId,
84
+ };
85
+ }
77
86
  if (!parsed.grant?.grantId) {
78
- throw new Error('Invalid response: expected { grant } from POST /context/grants/:id/delegate');
87
+ throw new Error('Invalid response: expected { grant } or { status: "pending_approval" } from POST /context/grants/:id/delegate');
79
88
  }
80
- return parsed.grant;
89
+ return { status: 'granted', grant: parsed.grant };
81
90
  }
82
91
  async revokeGrant(grantId) {
83
92
  const res = await fetch(`${this.baseUrl}/context/grants/${encodeURIComponent(grantId)}`, {
@@ -8,6 +8,8 @@ export interface CreateTaskData {
8
8
  idempotencyKey?: string;
9
9
  /** Explicit delegation target (ZIG-586) — must be a party to the agreement; validated server-side. */
10
10
  assigneeId?: string;
11
+ /** ZIG-734: prior-step artifact handles consumed as structured input links. */
12
+ inputArtifactIds?: string[];
11
13
  }
12
14
  export declare function createTask(taskData: CreateTaskData, creds: Creds): Promise<Task>;
13
15
  export declare function getTask(taskId: string, creds: Creds): Promise<Task>;
@@ -26,6 +28,7 @@ export interface PlanReplaceStep {
26
28
  order: number;
27
29
  }
28
30
  export declare function replaceTaskPlan(taskId: string, steps: PlanReplaceStep[], creds: Creds): Promise<Task>;
31
+ export declare function updateTaskPlanStep(taskId: string, stepId: string, status: string, result: unknown, creds: Creds): Promise<Task>;
29
32
  export declare function reportTask(taskId: string, message: string | undefined, creds: Creds): Promise<Task>;
30
33
  export declare function setTaskSatisfaction(taskId: string, satisfaction: 'positive' | 'negative', creds: Creds): Promise<Task>;
31
34
  export interface ListTasksOptions {
@@ -63,6 +66,7 @@ export declare class TaskClient {
63
66
  cancelTask(taskId: string): Promise<Task>;
64
67
  getSubtasks(parentTaskId: string): Promise<Task[]>;
65
68
  replaceTaskPlan(taskId: string, steps: PlanReplaceStep[]): Promise<Task>;
69
+ updateTaskPlanStep(taskId: string, stepId: string, status: string, result?: unknown): Promise<Task>;
66
70
  reportTask(taskId: string, message?: string): Promise<Task>;
67
71
  setTaskSatisfaction(taskId: string, satisfaction: 'positive' | 'negative'): Promise<Task>;
68
72
  listTasks(options?: ListTasksOptions): Promise<ListTasksResult>;
@@ -205,6 +205,29 @@ export async function replaceTaskPlan(taskId, steps, creds) {
205
205
  throw new Error('Invalid response: task data not found');
206
206
  return task;
207
207
  }
208
+ export async function updateTaskPlanStep(taskId, stepId, status, result, creds) {
209
+ if (!taskId)
210
+ throw new Error('updateTaskPlanStep: taskId is required');
211
+ if (!stepId)
212
+ throw new Error('updateTaskPlanStep: stepId is required');
213
+ if (!status)
214
+ throw new Error('updateTaskPlanStep: status is required');
215
+ assertCreds(creds, 'plan step update');
216
+ const res = await fetch(`${getTaskBaseUrl()}/${taskId}/plan`, {
217
+ method: 'PATCH',
218
+ headers: buildHeaders(creds),
219
+ body: JSON.stringify({ stepId, status, ...(result !== undefined && { result }) }),
220
+ });
221
+ if (!res.ok) {
222
+ const responseBody = await res.text().catch(() => '');
223
+ throwApiError(res, responseBody, `Plan step update failed: ${res.status} ${res.statusText}`);
224
+ }
225
+ const data = await res.json().catch(() => null);
226
+ const task = extractTask(data);
227
+ if (!task)
228
+ throw new Error('Invalid response: task data not found');
229
+ return task;
230
+ }
208
231
  // ---------------------------------------------------------------------------
209
232
  // Reporting & satisfaction
210
233
  // ---------------------------------------------------------------------------
@@ -322,6 +345,7 @@ export class TaskClient {
322
345
  cancelTask(taskId) { return cancelTask(taskId, this.creds); }
323
346
  getSubtasks(parentTaskId) { return getSubtasks(parentTaskId, this.creds); }
324
347
  replaceTaskPlan(taskId, steps) { return replaceTaskPlan(taskId, steps, this.creds); }
348
+ updateTaskPlanStep(taskId, stepId, status, result) { return updateTaskPlanStep(taskId, stepId, status, result, this.creds); }
325
349
  reportTask(taskId, message) { return reportTask(taskId, message, this.creds); }
326
350
  setTaskSatisfaction(taskId, satisfaction) { return setTaskSatisfaction(taskId, satisfaction, this.creds); }
327
351
  listTasks(options) { return listTasks(options ?? {}, this.creds); }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ziggs-ai/api-client",
3
- "version": "0.1.23",
3
+ "version": "0.1.25",
4
4
  "description": "HTTP and WebSocket client for the Ziggs backend API",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",