@ziggs-ai/api-client 0.12.0 → 0.14.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.
@@ -0,0 +1,60 @@
1
+ /** What became of one hello. `expired` is derived by the server, not stored. */
2
+ export type IntroductionStatus = 'open' | 'redeemed' | 'declined' | 'revoked' | 'expired';
3
+ export type IntroductionOutcome = 'link_pending' | 'already_teammates' | 'already_linked';
4
+ export interface IntroductionView {
5
+ token: string;
6
+ status: IntroductionStatus;
7
+ outcome: IntroductionOutcome | null;
8
+ from: {
9
+ label: string;
10
+ agentId: string | null;
11
+ orgId: string;
12
+ };
13
+ venue: string | null;
14
+ venueRef: string | null;
15
+ note: string | null;
16
+ agreementId: string | null;
17
+ redeemedBy: {
18
+ agentId: string | null;
19
+ orgId: string | null;
20
+ } | null;
21
+ expiresAt: string;
22
+ /** The public page the string points at. This is what you hand over. */
23
+ url: string;
24
+ /**
25
+ * Returned to a redeemer only: the two ids the meeting is now worth, and what
26
+ * each is for. `agentId` is where messages go; `principal` is who can be a
27
+ * party to an agreement. Passing one where the other belongs is refused as if
28
+ * the agent did not exist, which is the wall this field exists to remove.
29
+ */
30
+ counterparty?: {
31
+ principal: string;
32
+ agentId: string | null;
33
+ label: string;
34
+ nextStep: string;
35
+ };
36
+ }
37
+ export interface MintIntroductionInput {
38
+ venue?: string;
39
+ venueRef?: string;
40
+ note?: string;
41
+ }
42
+ /**
43
+ * Introduction tokens — the one object to exchange when you meet an
44
+ * agent somewhere Ziggs does not own.
45
+ *
46
+ * The token carries no authority, which is why minting one is free: it widens
47
+ * nobody's reach. Redeeming one stages a link proposal the two humans sign.
48
+ */
49
+ export declare class IntroductionsClient {
50
+ private readonly operatorKey;
51
+ private readonly agentId?;
52
+ private readonly baseUrl;
53
+ constructor(operatorKey: string, agentId?: string, baseUrl?: string);
54
+ mint(input?: MintIntroductionInput): Promise<IntroductionView>;
55
+ redeem(token: string): Promise<IntroductionView>;
56
+ decline(token: string): Promise<IntroductionView>;
57
+ revoke(token: string): Promise<IntroductionView>;
58
+ listMine(limit?: number): Promise<IntroductionView[]>;
59
+ private call;
60
+ }
@@ -0,0 +1,54 @@
1
+ import { getBackendUrl } from '../utils/urlUtils.js';
2
+ import { throwApiError } from '../shared/apiError.js';
3
+ import { buildOperatorHeaders } from './operatorHeaders.js';
4
+ /**
5
+ * Introduction tokens — the one object to exchange when you meet an
6
+ * agent somewhere Ziggs does not own.
7
+ *
8
+ * The token carries no authority, which is why minting one is free: it widens
9
+ * nobody's reach. Redeeming one stages a link proposal the two humans sign.
10
+ */
11
+ export class IntroductionsClient {
12
+ operatorKey;
13
+ agentId;
14
+ baseUrl;
15
+ constructor(operatorKey, agentId, baseUrl) {
16
+ if (!operatorKey) {
17
+ throw new Error('IntroductionsClient: operatorKey is required');
18
+ }
19
+ this.operatorKey = operatorKey;
20
+ this.agentId = agentId;
21
+ this.baseUrl = baseUrl || getBackendUrl();
22
+ }
23
+ async mint(input = {}) {
24
+ return this.call('POST', '/introductions', input, 'mint');
25
+ }
26
+ async redeem(token) {
27
+ return this.call('POST', `/introductions/${encodeURIComponent(token)}/redeem`, {}, 'redeem');
28
+ }
29
+ async decline(token) {
30
+ return this.call('POST', `/introductions/${encodeURIComponent(token)}/decline`, {}, 'decline');
31
+ }
32
+ async revoke(token) {
33
+ return this.call('DELETE', `/introductions/${encodeURIComponent(token)}`, undefined, 'revoke');
34
+ }
35
+ async listMine(limit) {
36
+ const query = limit != null ? `?limit=${encodeURIComponent(String(limit))}` : '';
37
+ return this.call('GET', `/introductions${query}`, undefined, 'listMine');
38
+ }
39
+ async call(method, path, body, label) {
40
+ const res = await fetch(`${this.baseUrl}${path}`, {
41
+ method,
42
+ headers: {
43
+ ...buildOperatorHeaders(this.operatorKey, this.agentId),
44
+ ...(body === undefined ? {} : { 'Content-Type': 'application/json' }),
45
+ },
46
+ ...(body === undefined ? {} : { body: JSON.stringify(body) }),
47
+ });
48
+ const text = await res.text().catch(() => '');
49
+ if (!res.ok) {
50
+ throwApiError(res, text, `IntroductionsClient.${label} failed: ${res.status}`);
51
+ }
52
+ return JSON.parse(text);
53
+ }
54
+ }
@@ -23,21 +23,6 @@ export interface PullOffersOptions {
23
23
  since?: string;
24
24
  }
25
25
  export declare function pullOffers(options: PullOffersOptions | undefined, creds: Creds): Promise<Agreement[]>;
26
- export interface PublishRequestPayload {
27
- /** See PublishOfferPayload.billing. */
28
- billing?: 'total' | 'per_task';
29
- description: string;
30
- chatId?: string;
31
- payerId?: string;
32
- price?: number;
33
- lifecycle?: string;
34
- expiresAt?: string;
35
- maxExecutions?: number;
36
- agreementDescription?: string;
37
- /** Broadcast audience: 'everyone' (default, fully public) or 'org' (members of your active org only). */
38
- audience?: BroadcastAudience;
39
- }
40
- export declare function publishRequest(payload: PublishRequestPayload, creds: Creds): Promise<Agreement>;
41
26
  export interface PullRequestsOptions {
42
27
  limit?: number;
43
28
  since?: string;
@@ -52,6 +37,5 @@ export declare class MarketplaceClient {
52
37
  constructor(operatorKey: string, agentId?: string);
53
38
  publishOffer(payload: PublishOfferPayload): Promise<Agreement>;
54
39
  pullOffers(options?: PullOffersOptions): Promise<Agreement[]>;
55
- publishRequest(payload: PublishRequestPayload): Promise<Agreement>;
56
40
  pullRequests(options?: PullRequestsOptions): Promise<Agreement[]>;
57
41
  }
@@ -52,22 +52,6 @@ export async function pullOffers(options, creds) {
52
52
  const data = await res.json().catch(() => null);
53
53
  return data?.['offers'] ?? [];
54
54
  }
55
- export async function publishRequest(payload, creds) {
56
- assertCreds(creds, 'request publish');
57
- const res = await fetch(`${getMarketplaceBaseUrl()}/requests/publish`, {
58
- method: 'POST',
59
- headers: buildHeaders(creds),
60
- body: JSON.stringify(payload || {}),
61
- });
62
- if (!res.ok) {
63
- const body = await res.text().catch(() => '');
64
- throwApiError(res, body, `Request publish failed: ${res.status}`);
65
- }
66
- const data = await res.json().catch(() => null);
67
- if (!data?.['agreement'])
68
- throw new Error('Request publish returned no agreement');
69
- return shapeAgreement(data['agreement']);
70
- }
71
55
  export async function pullRequests(options, creds) {
72
56
  assertCreds(creds, 'request pull');
73
57
  const params = new URLSearchParams();
@@ -102,6 +86,5 @@ export class MarketplaceClient {
102
86
  }
103
87
  publishOffer(payload) { return publishOffer(payload, this.creds); }
104
88
  pullOffers(options) { return pullOffers(options, this.creds); }
105
- publishRequest(payload) { return publishRequest(payload, this.creds); }
106
89
  pullRequests(options) { return pullRequests(options, this.creds); }
107
90
  }
@@ -13,6 +13,7 @@ export interface TaskWriteConfirm {
13
13
  structureChanged?: boolean;
14
14
  stepId?: string;
15
15
  stepStatus?: string;
16
+ patchedCount?: number;
16
17
  resultRecorded?: boolean;
17
18
  terminalState?: string;
18
19
  [key: string]: unknown;
@@ -23,6 +24,20 @@ export interface TaskWriteConfirm {
23
24
  * subcontract inputs rather than teaching those routes to keep one.
24
25
  */
25
26
  export type PlanReviewTiming = 'with_proposal' | 'before_execution';
27
+ export interface CreateTaskGraphNodeData {
28
+ nodeId: string;
29
+ agreementId: string;
30
+ description: string;
31
+ title?: string;
32
+ assigneeId?: string;
33
+ waitsOn?: string[];
34
+ joinKind?: 'all' | 'any';
35
+ }
36
+ export interface CreateTaskGraphData {
37
+ nodes: CreateTaskGraphNodeData[];
38
+ inputArtifactIds?: string[];
39
+ review?: 'before_execution';
40
+ }
26
41
  export interface CreateTaskData {
27
42
  description: string;
28
43
  /**
@@ -37,10 +52,26 @@ export interface CreateTaskData {
37
52
  planReviewTiming?: PlanReviewTiming;
38
53
  requireMidWorkPlanAck?: boolean;
39
54
  idempotencyKey?: string;
40
- /** Explicit delegation target — must be a party to the agreement; validated server-side. */
55
+ /**
56
+ * Explicit delegation target — must hold a grant on the agreement, so work
57
+ * can only be handed to someone that contract already reaches; validated
58
+ * server-side.
59
+ */
41
60
  assigneeId?: string;
42
61
  /** prior-step artifact handles consumed as structured input links. */
43
62
  inputArtifactIds?: string[];
63
+ /**
64
+ * Tasks this one waits for. The task is created immediately, but its assignee
65
+ * is not woken and cannot start it until every task named here is terminal.
66
+ * Declaring an edge needs read reach on the task you name.
67
+ *
68
+ * Declared rather than enacted: state the join up front and the platform
69
+ * wakes the joiner once, instead of a live agent polling for it and holding
70
+ * the shape in its own control flow where nothing else can see it.
71
+ */
72
+ waitsOn?: string[];
73
+ /** Atomically declare a native work graph beneath the returned root task. */
74
+ graph?: CreateTaskGraphData;
44
75
  }
45
76
  export declare function createTask(taskData: CreateTaskData, creds: Creds): Promise<Task>;
46
77
  export declare function getTask(taskId: string, creds: Creds): Promise<Task>;
@@ -77,6 +108,13 @@ export interface PlanReplaceStep {
77
108
  result?: unknown;
78
109
  }
79
110
  export declare function replaceTaskPlan(taskId: string, steps: PlanReplaceStep[], creds: Creds): Promise<TaskWriteConfirm>;
111
+ /** Named-step progress — only the listed steps change. */
112
+ export type PlanStepPatch = {
113
+ stepId: string;
114
+ status: 'pending' | 'in_progress' | 'completed' | 'skipped';
115
+ result?: unknown;
116
+ };
117
+ export declare function updateTaskPlanSteps(taskId: string, patches: PlanStepPatch[], creds: Creds): Promise<TaskWriteConfirm>;
80
118
  export declare function updateTaskPlanStep(taskId: string, stepId: string, status: string, result: unknown, creds: Creds): Promise<TaskWriteConfirm>;
81
119
  export declare function reportTask(taskId: string, message: string | undefined, creds: Creds): Promise<TaskWriteConfirm>;
82
120
  export declare function setTaskSatisfaction(taskId: string, satisfaction: 'positive' | 'negative', creds: Creds): Promise<TaskWriteConfirm>;
@@ -121,6 +159,7 @@ export declare class TaskClient {
121
159
  getSubtasks(parentTaskId: string): Promise<Task[]>;
122
160
  replaceTaskPlan(taskId: string, steps: PlanReplaceStep[]): Promise<TaskWriteConfirm>;
123
161
  updateTaskPlanStep(taskId: string, stepId: string, status: string, result?: unknown): Promise<TaskWriteConfirm>;
162
+ updateTaskPlanSteps(taskId: string, patches: PlanStepPatch[]): Promise<TaskWriteConfirm>;
124
163
  reportTask(taskId: string, message?: string): Promise<TaskWriteConfirm>;
125
164
  setTaskSatisfaction(taskId: string, satisfaction: 'positive' | 'negative'): Promise<TaskWriteConfirm>;
126
165
  listTasks(options?: ListTasksOptions): Promise<ListTasksResult>;
@@ -55,6 +55,7 @@ function extractWriteConfirm(data) {
55
55
  : {}),
56
56
  ...(typeof d['stepId'] === 'string' ? { stepId: d['stepId'] } : {}),
57
57
  ...(typeof d['stepStatus'] === 'string' ? { stepStatus: d['stepStatus'] } : {}),
58
+ ...(typeof d['patchedCount'] === 'number' ? { patchedCount: d['patchedCount'] } : {}),
58
59
  ...(d['resultRecorded'] === true ? { resultRecorded: true } : {}),
59
60
  ...(typeof d['terminalState'] === 'string'
60
61
  ? { terminalState: d['terminalState'] }
@@ -152,10 +153,29 @@ export async function getActiveTasksForAgent(agentId, creds) {
152
153
  const data = await res.json().catch(() => null);
153
154
  const fromAgent = Array.isArray(data?.['tasks']) ? data['tasks'] : [];
154
155
  if (fromAgent.length > 0)
155
- return fromAgent;
156
+ return workable(fromAgent);
156
157
  // Fallback when /agents/:id/tasks is empty but party-agreement tasks exist.
157
158
  return getActiveTasksForAgentViaPartyAgreements(agentId, creds);
158
159
  }
160
+ /**
161
+ * Drop the tasks the platform created but deliberately did not deliver.
162
+ *
163
+ * `heldByDeps` is a DELIVERY condition, not a task state: a task waiting on its
164
+ * `waitsOn` edges is genuinely `active`, so `?state=active` returns it and the
165
+ * assignee's own work list hands over a task nobody woke it for. Every caller of
166
+ * this function is asking "what can I act on now", and for a withheld row the
167
+ * answer is nothing — `updateTaskState` refuses it with `dependencies_unmet`.
168
+ * Two of AgentHost's three callers do worse than waste a wake: the needs gate
169
+ * and the unmet-access sweep both FAIL the tasks they find, so a withheld node
170
+ * was one missing grant away from being failed before its turn came.
171
+ *
172
+ * Filtered here rather than at `/agents/:id/tasks`, which is also the route the
173
+ * UI lists an agent's work with — a person looking at the graph should see the
174
+ * withheld node sitting there waiting, which is the whole point of a join.
175
+ */
176
+ function workable(tasks) {
177
+ return tasks.filter((t) => t.heldByDeps !== true);
178
+ }
159
179
  async function getActiveTasksForAgentViaPartyAgreements(agentId, creds) {
160
180
  const { listAgreements } = await import('./AgreementClient.js');
161
181
  const agreements = await listAgreements({ status: 'active' }, creds);
@@ -175,7 +195,7 @@ async function getActiveTasksForAgentViaPartyAgreements(agentId, creds) {
175
195
  return Array.isArray(data?.['tasks']) ? data['tasks'] : [];
176
196
  }));
177
197
  const merged = lists.flat();
178
- return merged.filter((t) => !t.assigneeId || t.assigneeId === agentId);
198
+ return workable(merged.filter((t) => !t.assigneeId || t.assigneeId === agentId));
179
199
  }
180
200
  // Backend deliberately has no `/chats/:id/tasks` route — it's composable
181
201
  // from links → per-agreement tasks. We do the composition here so callers
@@ -263,6 +283,34 @@ export async function replaceTaskPlan(taskId, steps, creds) {
263
283
  throw new Error('Invalid response: task write confirm not found');
264
284
  return confirm;
265
285
  }
286
+ export async function updateTaskPlanSteps(taskId, patches, creds) {
287
+ if (!taskId)
288
+ throw new Error('updateTaskPlanSteps: taskId is required');
289
+ if (!patches || !Array.isArray(patches) || patches.length === 0) {
290
+ throw new Error('updateTaskPlanSteps: patches must name at least one step');
291
+ }
292
+ for (const patch of patches) {
293
+ if (!patch?.stepId)
294
+ throw new Error('updateTaskPlanSteps: each patch needs a stepId');
295
+ if (!patch?.status)
296
+ throw new Error('updateTaskPlanSteps: each patch needs a status');
297
+ }
298
+ assertCreds(creds, 'plan step patch');
299
+ const res = await fetch(`${getTaskBaseUrl()}/${taskId}/plan`, {
300
+ method: 'PATCH',
301
+ headers: buildHeaders(creds),
302
+ body: JSON.stringify({ patches }),
303
+ });
304
+ if (!res.ok) {
305
+ const responseBody = await res.text().catch(() => '');
306
+ throwApiError(res, responseBody, `Plan step patch failed: ${res.status} ${res.statusText}`);
307
+ }
308
+ const data = await res.json().catch(() => null);
309
+ const confirm = extractWriteConfirm(data);
310
+ if (!confirm)
311
+ throw new Error('Invalid response: task write confirm not found');
312
+ return confirm;
313
+ }
266
314
  export async function updateTaskPlanStep(taskId, stepId, status, result, creds) {
267
315
  if (!taskId)
268
316
  throw new Error('updateTaskPlanStep: taskId is required');
@@ -406,6 +454,7 @@ export class TaskClient {
406
454
  getSubtasks(parentTaskId) { return getSubtasks(parentTaskId, this.creds); }
407
455
  replaceTaskPlan(taskId, steps) { return replaceTaskPlan(taskId, steps, this.creds); }
408
456
  updateTaskPlanStep(taskId, stepId, status, result) { return updateTaskPlanStep(taskId, stepId, status, result, this.creds); }
457
+ updateTaskPlanSteps(taskId, patches) { return updateTaskPlanSteps(taskId, patches, this.creds); }
409
458
  reportTask(taskId, message) { return reportTask(taskId, message, this.creds); }
410
459
  setTaskSatisfaction(taskId, satisfaction) { return setTaskSatisfaction(taskId, satisfaction, this.creds); }
411
460
  listTasks(options) { return listTasks(options ?? {}, this.creds); }
@@ -36,9 +36,15 @@ 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"];
40
- /** Every scope kind across every rail: context + connection + payment. */
41
- export declare const GRANT_SCOPE_KINDS: readonly ["chat", "agreement", "org", "artifact", "connection", "wallet"];
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"];
42
48
  export type GrantScopeKind = (typeof GRANT_SCOPE_KINDS)[number];
43
49
  export interface GrantScopeView {
44
50
  kind: GrantScopeKind;
@@ -12,12 +12,25 @@ export const CONTEXT_GRANT_SCOPE_KINDS = [
12
12
  'org',
13
13
  // narrowest context scope
14
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',
15
18
  ];
16
- /** Every scope kind across every rail: context + connection + payment. */
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
+ */
17
26
  export const GRANT_SCOPE_KINDS = [
18
27
  ...CONTEXT_GRANT_SCOPE_KINDS,
19
28
  'connection',
20
29
  'wallet',
30
+ // approval authority — who may answer a slot
31
+ 'consent',
32
+ // carrier grants — who reads whose mailbox
33
+ 'inbox',
21
34
  ];
22
35
  /** Value of the first caveat of `type` on a grant, or undefined. */
23
36
  export function grantCaveat(grant, type) {
@@ -11,6 +11,8 @@ export { ContextReadClient, CONTEXT_READ_TYPES, CONTEXT_READ_VIA, VIA_KINDS, par
11
11
  export type { ContextReadType, ContextReadQuery, ContextReadEnvelope, ContextSnapshotResult, ContextSnapshotParticipant, ViaKind, } from './ContextReadClient.js';
12
12
  export { ContextDiscoveryClient } from './ContextDiscoveryClient.js';
13
13
  export type { DiscoverableItem } from './ContextDiscoveryClient.js';
14
+ export { IntroductionsClient } from './IntroductionsClient.js';
15
+ export type { IntroductionView, IntroductionStatus, IntroductionOutcome, MintIntroductionInput, } from './IntroductionsClient.js';
14
16
  export { GrantsClient } from './GrantsClient.js';
15
17
  export type { ListGrantsQuery, ListGrantsResult, UnreadableRail } from './GrantsClient.js';
16
18
  export { ContextGrantsClient } from './ContextGrantsClient.js';
@@ -9,6 +9,7 @@ export { ArtifactsClient,
9
9
  artifactScopeForSession, AGREEMENT_LANE_PREFIX, ARTIFACT_INLINE_TEXT_MAX_CHARS, ARTIFACT_INLINE_TEXT_OVER_LIMIT_HINT, } from './ArtifactsClient.js';
10
10
  export { ContextReadClient, CONTEXT_READ_TYPES, CONTEXT_READ_VIA, VIA_KINDS, parseVia, viaHint, } from './ContextReadClient.js';
11
11
  export { ContextDiscoveryClient } from './ContextDiscoveryClient.js';
12
+ export { IntroductionsClient } from './IntroductionsClient.js';
12
13
  export { GrantsClient } from './GrantsClient.js';
13
14
  export { ContextGrantsClient } from './ContextGrantsClient.js';
14
15
  export { grantCaveat, CONTEXT_GRANT_SCOPE_KINDS, GRANT_SCOPE_KINDS, } from './grants.js';
package/dist/index.d.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  export * from './http/index.js';
2
2
  export * from './capabilities/index.js';
3
- export * from './relay/provisionRelayWorkers.js';
4
3
  export { ConnectionManager } from './ConnectionManager.js';
5
4
  export { EntryTypes, ContentTypes, OPEN_AGREEMENT_TARGET, ORG_AGREEMENT_TARGET, BROADCAST_TARGETS, isBroadcastTarget, isPersonaRef, isRoomPresentationRef, isOpaquePresentationRef, AGREEMENT_ENGAGEMENT_KIND, isValidContentType, partySideIds, partyActorIds, } from './types.js';
6
5
  export type { PrincipalPresentation } from './types.js';
package/dist/index.js CHANGED
@@ -1,6 +1,5 @@
1
1
  export * from './http/index.js';
2
2
  export * from './capabilities/index.js';
3
- export * from './relay/provisionRelayWorkers.js';
4
3
  export { ConnectionManager } from './ConnectionManager.js';
5
4
  export { EntryTypes, ContentTypes, OPEN_AGREEMENT_TARGET, ORG_AGREEMENT_TARGET, BROADCAST_TARGETS, isBroadcastTarget, isPersonaRef, isRoomPresentationRef, isOpaquePresentationRef, AGREEMENT_ENGAGEMENT_KIND, isValidContentType, partySideIds, partyActorIds, } from './types.js';
6
5
  export { getBackendUrl } from './utils/urlUtils.js';
package/dist/types.d.ts CHANGED
@@ -71,6 +71,24 @@ export interface Task {
71
71
  updatedAt?: string;
72
72
  parentTaskId?: string;
73
73
  rootTaskId?: string;
74
+ /** Tasks this one waits for before it may start. */
75
+ waitsOn?: string[];
76
+ /**
77
+ * True while {@link waitsOn} is unmet: the task exists but has not been
78
+ * delivered, and its assignee cannot take the processing lock yet. Distinct
79
+ * from "nobody has picked it up" — this one is waiting on purpose.
80
+ */
81
+ heldByDeps?: boolean;
82
+ joinKind?: 'all' | 'any';
83
+ graph?: {
84
+ nodes: Array<{
85
+ nodeId: string;
86
+ taskId: string;
87
+ }>;
88
+ review?: 'before_execution';
89
+ } | null;
90
+ graphNodeId?: string | null;
91
+ heldByGraphReview?: boolean;
74
92
  plan?: {
75
93
  steps?: PlanStep[];
76
94
  };
@@ -140,6 +158,8 @@ export interface AgreementApprovalEntry {
140
158
  role?: string;
141
159
  status?: string;
142
160
  respondedAt?: string | null;
161
+ /** Why a pending slot is held (e.g. 'formation-gate', 'contact-basis'), when the server said. */
162
+ heldReason?: string | null;
143
163
  }
144
164
  export interface Agreement {
145
165
  agreementId: string;
@@ -361,6 +381,15 @@ export interface InboxProposalRef {
361
381
  agreementId: string;
362
382
  title: string;
363
383
  proposedAt: string | null;
384
+ /**
385
+ * The agent designated to wake for this proposal, or null when it is
386
+ * ambient/human work in this reader's merged inbox.
387
+ *
388
+ * Optional for rolling deploys. When an older server omits it, clients must
389
+ * only use an explicit agent-keyed approval/responder slot as a safe fallback;
390
+ * org visibility alone is never assignment.
391
+ */
392
+ assignedAgentId?: string | null;
364
393
  /**
365
394
  * party ids still owing a decision, and the named responder slot.
366
395
  * The inbox lists proposals awaiting the agent OR its human, and only the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ziggs-ai/api-client",
3
- "version": "0.12.0",
3
+ "version": "0.14.0",
4
4
  "description": "HTTP and WebSocket client for the Ziggs backend API",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,53 +0,0 @@
1
- import type { Agreement, Creds } from '../types.js';
2
- import { claimOpenAgreement, delegateAgreement, getMyAgreements, pullOffers } from '../http/index.js';
3
- /** Minimal relay step shape — keep field names aligned with agents/coordinators/relayTypes.ts */
4
- export interface RelayStepInput {
5
- stepId: string;
6
- order: number;
7
- assigneeId: string;
8
- description: string;
9
- /** Standing offer agreementId to claim for this worker (optional). */
10
- offerAgreementId?: string;
11
- }
12
- export interface RelayPayloadShape {
13
- inputArtifactIds?: string[];
14
- steps: Array<{
15
- stepId: string;
16
- order: number;
17
- agreementId: string;
18
- assigneeId: string;
19
- description: string;
20
- }>;
21
- }
22
- export type ProvisionMethod = 'existing' | 'claim' | 'delegate';
23
- export type StepProvisionStatus = 'active' | 'pending_approval';
24
- export type ProvisionedRelayStep = RelayPayloadShape['steps'][number] & {
25
- provisionMethod: ProvisionMethod;
26
- status: StepProvisionStatus;
27
- };
28
- export interface RelayProvisionDeps {
29
- getMyAgreements: typeof getMyAgreements;
30
- pullOffers: typeof pullOffers;
31
- claimOpenAgreement: typeof claimOpenAgreement;
32
- delegateAgreement: typeof delegateAgreement;
33
- }
34
- export interface ProvisionRelayWorkersInput {
35
- creds: Creds;
36
- hireAgreementId: string;
37
- chatId?: string;
38
- steps: RelayStepInput[];
39
- inputArtifactIds?: string[];
40
- }
41
- export interface ProvisionRelayWorkersResult {
42
- payload: RelayPayloadShape;
43
- steps: ProvisionedRelayStep[];
44
- pendingApprovals: string[];
45
- readyForKickoff: boolean;
46
- }
47
- type AgreementWithParent = Agreement & {
48
- parentAgreementId?: string;
49
- };
50
- export declare function findExistingWorkerDelegation(hireAgreementId: string, assigneeId: string, agreements: AgreementWithParent[]): AgreementWithParent | null;
51
- export declare function findOpenOfferForAgent(assigneeId: string, offers: Agreement[]): Agreement | null;
52
- export declare function provisionRelayWorkers(input: ProvisionRelayWorkersInput, deps?: RelayProvisionDeps): Promise<ProvisionRelayWorkersResult>;
53
- export {};