@ziggs-ai/api-client 0.10.4 → 0.12.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.
Files changed (51) hide show
  1. package/dist/capabilities/agreementVerbs.d.ts +9 -2
  2. package/dist/capabilities/agreementVerbs.js +61 -21
  3. package/dist/capabilities/agreements.d.ts +1 -1
  4. package/dist/capabilities/agreements.js +18 -6
  5. package/dist/capabilities/artifacts.d.ts +4 -2
  6. package/dist/capabilities/artifacts.js +168 -33
  7. package/dist/capabilities/chat.d.ts +2 -3
  8. package/dist/capabilities/chat.js +5 -6
  9. package/dist/capabilities/connections.js +1 -1
  10. package/dist/capabilities/context.js +3 -0
  11. package/dist/capabilities/grants.d.ts +7 -6
  12. package/dist/capabilities/grants.js +9 -8
  13. package/dist/capabilities/index.d.ts +4 -4
  14. package/dist/capabilities/index.js +4 -4
  15. package/dist/capabilities/links.d.ts +17 -6
  16. package/dist/capabilities/links.js +71 -86
  17. package/dist/capabilities/marketplace.js +23 -17
  18. package/dist/capabilities/nextCall.d.ts +36 -5
  19. package/dist/capabilities/nextCall.js +53 -7
  20. package/dist/capabilities/payments.d.ts +24 -8
  21. package/dist/capabilities/payments.js +28 -392
  22. package/dist/capabilities/proposeProviderId.d.ts +1 -1
  23. package/dist/capabilities/proposeProviderId.js +1 -1
  24. package/dist/http/AgreementClient.d.ts +63 -27
  25. package/dist/http/AgreementClient.js +51 -39
  26. package/dist/http/ChatClient.d.ts +1 -0
  27. package/dist/http/ChatClient.js +4 -1
  28. package/dist/http/ConnectionsClient.js +12 -1
  29. package/dist/http/ContextGrantsClient.d.ts +15 -1
  30. package/dist/http/ContextGrantsClient.js +2 -0
  31. package/dist/http/ContextReadClient.d.ts +14 -5
  32. package/dist/http/GrantsClient.d.ts +14 -0
  33. package/dist/http/GrantsClient.js +18 -2
  34. package/dist/http/InboxClient.js +4 -0
  35. package/dist/http/MarketplaceClient.d.ts +6 -8
  36. package/dist/http/MarketplaceClient.js +11 -30
  37. package/dist/http/TaskClient.d.ts +5 -0
  38. package/dist/http/TaskClient.js +4 -7
  39. package/dist/http/agreementFlows.d.ts +6 -7
  40. package/dist/http/agreementFlows.js +14 -20
  41. package/dist/http/grants.d.ts +28 -0
  42. package/dist/http/index.d.ts +2 -2
  43. package/dist/index.d.ts +3 -3
  44. package/dist/index.js +1 -1
  45. package/dist/instanceIdentity.d.ts +4 -0
  46. package/dist/instanceIdentity.js +44 -0
  47. package/dist/relay/provisionRelayWorkers.d.ts +2 -2
  48. package/dist/relay/provisionRelayWorkers.js +5 -5
  49. package/dist/types.d.ts +80 -31
  50. package/dist/types.js +18 -0
  51. package/package.json +1 -1
@@ -1,4 +1,5 @@
1
1
  import { getBackendUrl } from '../utils/urlUtils.js';
2
+ import { partyActorIds } from '../types.js';
2
3
  import { throwApiError } from '../shared/apiError.js';
3
4
  function getTaskBaseUrl() { return `${getBackendUrl()}/tasks`; }
4
5
  function buildHeaders(creds) {
@@ -159,13 +160,7 @@ async function getActiveTasksForAgentViaPartyAgreements(agentId, creds) {
159
160
  const { listAgreements } = await import('./AgreementClient.js');
160
161
  const agreements = await listAgreements({ status: 'active' }, creds);
161
162
  const partyIds = agreements
162
- .filter((a) => {
163
- const p = a.parties ?? {};
164
- return (p.provider === agentId ||
165
- p.providerAgent === agentId ||
166
- p.creator === agentId ||
167
- p.payer === agentId);
168
- })
163
+ .filter((a) => partyActorIds(a.parties).includes(agentId))
169
164
  .map((a) => a.agreementId)
170
165
  .filter(Boolean);
171
166
  if (partyIds.length === 0)
@@ -345,6 +340,8 @@ export async function listTasks(options = {}, creds) {
345
340
  url.searchParams.set('limit', String(options.limit));
346
341
  if (options.assignedTo)
347
342
  url.searchParams.set('assignedTo', options.assignedTo);
343
+ if (options.createdBy)
344
+ url.searchParams.set('createdBy', options.createdBy);
348
345
  const res = await fetch(url.toString(), {
349
346
  method: 'GET',
350
347
  headers: buildHeaders(creds),
@@ -1,14 +1,13 @@
1
- import { type ClaimedKind, type ProposeTerms } from './AgreementClient.js';
1
+ import { type ClaimedKind, type ClaimOptions, type ProposeTerms } from './AgreementClient.js';
2
2
  import { type Agreement, type Creds, type EngagementKind } from '../types.js';
3
3
  /**
4
- * one propose grammar. Direct, broadcast (quest and standing
4
+ * one propose grammar. Direct, broadcast (request and standing
5
5
  * offer), and link proposals all flow through here; the surfaces expose a
6
6
  * single propose tool instead of dedicated publish/request tools.
7
7
  *
8
8
  * Routing:
9
- * - engagementKind 'link' → POST /agreements (link proposal / open invite)
10
9
  * - proposedTo 'everyone' | 'org', providerId = self → seller-broadcast standing offer
11
- * - proposedTo 'everyone' | 'org', no providerId → buyer-broadcast quest
10
+ * - proposedTo 'everyone' | 'org', no providerId → buyer-broadcast request
12
11
  * - anything else → direct proposal
13
12
  */
14
13
  export interface UnifiedProposeInput extends ProposeTerms {
@@ -17,13 +16,13 @@ export interface UnifiedProposeInput extends ProposeTerms {
17
16
  chatId?: string;
18
17
  engagementKind?: EngagementKind;
19
18
  }
20
- export type ProposeShape = 'direct' | 'quest' | 'offer' | 'link';
19
+ export type ProposeShape = 'direct' | 'request' | 'offer';
21
20
  export declare function proposeUnified(input: UnifiedProposeInput, creds: Creds): Promise<{
22
21
  agreement: Agreement;
23
22
  shape: ProposeShape;
24
23
  }>;
25
24
  /**
26
- * one claim verb for any open broadcast: link invite, quest,
25
+ * one claim verb for any open broadcast: link invite, request,
27
26
  * hand-off, or standing offer.
28
27
  *
29
28
  * One request. This used to read the agreement first to decide which endpoint to
@@ -36,7 +35,7 @@ export declare function proposeUnified(input: UnifiedProposeInput, creds: Creds)
36
35
  * `kind` arrives on the claim response — the route that did the routing reports
37
36
  * which broadcast kind this turned out to be.
38
37
  */
39
- export declare function claimOpenAgreement(agreementId: string, creds: Creds): Promise<{
38
+ export declare function claimOpenAgreement(agreementId: string, creds: Creds, opts?: ClaimOptions): Promise<{
40
39
  agreement: Agreement;
41
40
  kind: ClaimedKind;
42
41
  }>;
@@ -1,21 +1,15 @@
1
- import { createAgreement, claimAgreement, proposeBroadcast, proposeDirectTo, } from './AgreementClient.js';
1
+ import { claimAgreement, proposeBroadcast, proposeDirectTo, } from './AgreementClient.js';
2
2
  import { publishOffer } from './MarketplaceClient.js';
3
3
  import { isBroadcastTarget, } from '../types.js';
4
4
  export async function proposeUnified(input, creds) {
5
5
  const { proposedTo, chatId, engagementKind, providerId, ...terms } = input;
6
6
  if (!proposedTo)
7
7
  throw new Error('proposedTo is required (a user/agent id, or "everyone"/"org" to broadcast)');
8
- if (engagementKind === 'link') {
9
- // A link is an agreement, proposed to one agent (providerId) or opened as
10
- // an invite (proposedTo 'everyone'); no chat, no money.
11
- const target = isBroadcastTarget(proposedTo) ? undefined : proposedTo;
12
- const { agreement } = await createAgreement({
13
- engagementKind: 'link',
14
- ...(target ? { providerId: target } : {}),
15
- ...(terms.description ? { description: terms.description } : {}),
16
- }, creds);
17
- return { agreement, shape: 'link' };
18
- }
8
+ // The 'link' arm is gone. A link is not a proposal shape: it has no
9
+ // price, no chat and no work, and it is formed by `link_propose`, which takes
10
+ // an email or an agent id and answers the same either way. Keeping a second
11
+ // door here would have meant a caller reaching a link through the propose
12
+ // grammar, where the constant answer and the org-root refusal do not apply.
19
13
  if (isBroadcastTarget(proposedTo)) {
20
14
  const audience = proposedTo;
21
15
  if (providerId && creds.agentId && providerId === creds.agentId) {
@@ -33,16 +27,16 @@ export async function proposeUnified(input, creds) {
33
27
  return { agreement, shape: 'offer' };
34
28
  }
35
29
  if (providerId) {
36
- throw new Error('On a broadcast, providerId must be your own agent id (a standing offer: you work, the claimer pays) or omitted (a quest: the claimer works, you pay). A third-party providerId is not broadcastable.');
30
+ throw new Error('On a broadcast, providerId must be your own agent id (a standing offer: you work, the claimer pays) or omitted (a request: the claimer works, you pay). A third-party providerId is not broadcastable.');
37
31
  }
38
- // Buyer-broadcast: the claimer works, your side pays — an open quest.
32
+ // Buyer-broadcast: the claimer works, your side pays — an open request.
39
33
  const agreement = await proposeBroadcast({
40
34
  ...terms,
41
35
  chatId: chatId ?? '',
42
36
  engagementKind: engagementKind ?? 'service',
43
37
  audience,
44
38
  }, creds);
45
- return { agreement, shape: 'quest' };
39
+ return { agreement, shape: 'request' };
46
40
  }
47
41
  if (!chatId)
48
42
  throw new Error('chatId is required on a direct proposal');
@@ -56,7 +50,7 @@ export async function proposeUnified(input, creds) {
56
50
  return { agreement, shape: 'direct' };
57
51
  }
58
52
  /**
59
- * one claim verb for any open broadcast: link invite, quest,
53
+ * one claim verb for any open broadcast: link invite, request,
60
54
  * hand-off, or standing offer.
61
55
  *
62
56
  * One request. This used to read the agreement first to decide which endpoint to
@@ -69,11 +63,11 @@ export async function proposeUnified(input, creds) {
69
63
  * `kind` arrives on the claim response — the route that did the routing reports
70
64
  * which broadcast kind this turned out to be.
71
65
  */
72
- export async function claimOpenAgreement(agreementId, creds) {
66
+ export async function claimOpenAgreement(agreementId, creds, opts = {}) {
73
67
  if (!agreementId)
74
68
  throw new Error('agreementId is required');
75
- const { agreement, kind } = await claimAgreement(agreementId, creds);
69
+ const { agreement, kind } = await claimAgreement(agreementId, creds, opts);
76
70
  // A server that has not shipped the `kind` field yet still claims correctly;
77
- // 'quest' is the shape the route has always handled.
78
- return { agreement, kind: kind ?? 'quest' };
71
+ // 'request' is the shape the route has always handled.
72
+ return { agreement, kind: kind ?? 'request' };
79
73
  }
@@ -11,6 +11,23 @@
11
11
  * watermark are presented as `temporal` / `watermark_at` caveats.
12
12
  */
13
13
  export type GrantHealth = 'active' | 'expired' | 'revoked';
14
+ /**
15
+ * Who holds a grant. Declared at issuance and carried on the row — nothing
16
+ * about an id says what it names. An `org` holder is how one grant covers every
17
+ * member of that organization.
18
+ */
19
+ export type GrantHolderKind = 'user' | 'org' | 'agent';
20
+ /**
21
+ * What a grant lets its holder do. `write` implies `read`, `admit` implies
22
+ * both: `read` sees the scope, `write` takes part in it, `admit` brings others
23
+ * in as well.
24
+ */
25
+ export type GrantAccessKind = 'read' | 'write' | 'admit';
26
+ /**
27
+ * The kind of principal-authored consent object a grant rests on. Every grant
28
+ * is minted by one of these, or delegated from a parent grant that was.
29
+ */
30
+ export type GrantBasisKind = 'creation' | 'publication' | 'membership' | 'acceptance' | 'agreement';
14
31
  /**
15
32
  * The context rail's scope kinds, as VALUES — the type below is derived from
16
33
  * them, so widening the rail is one edit rather than a type change plus however
@@ -51,6 +68,17 @@ export interface GrantView {
51
68
  /** ISO-8601. */
52
69
  createdAt: string;
53
70
  health: GrantHealth;
71
+ /**
72
+ * What the holder is, what the grant lets them do, and which consent object
73
+ * authorized it. Present on rails that record them (context grants do);
74
+ * absent means the rail does not say, not that there is nothing.
75
+ */
76
+ holderKind?: GrantHolderKind;
77
+ access?: GrantAccessKind;
78
+ basis?: {
79
+ kind: GrantBasisKind;
80
+ id: string | null;
81
+ };
54
82
  }
55
83
  /** Value of the first caveat of `type` on a grant, or undefined. */
56
84
  export declare function grantCaveat(grant: GrantView, type: string): unknown | undefined;
@@ -8,7 +8,7 @@ export type { ListMessagesOptions, ListMessagesResult } from './MessagesClient.j
8
8
  export { ArtifactsClient, artifactScopeForSession, AGREEMENT_LANE_PREFIX, ARTIFACT_INLINE_TEXT_MAX_CHARS, ARTIFACT_INLINE_TEXT_OVER_LIMIT_HINT, } from './ArtifactsClient.js';
9
9
  export type { ArtifactVisibility, ListArtifactsOptions, ListArtifactsQuery, ListArtifactsResult, WriteArtifactInput, } from './ArtifactsClient.js';
10
10
  export { ContextReadClient, CONTEXT_READ_TYPES, CONTEXT_READ_VIA, VIA_KINDS, parseVia, viaHint, } from './ContextReadClient.js';
11
- export type { ContextReadType, ContextReadQuery, ContextReadEnvelope, ContextSnapshotResult, ViaKind, } from './ContextReadClient.js';
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
14
  export { GrantsClient } from './GrantsClient.js';
@@ -16,7 +16,7 @@ export type { ListGrantsQuery, ListGrantsResult, UnreadableRail } from './Grants
16
16
  export { ContextGrantsClient } from './ContextGrantsClient.js';
17
17
  export type { ContextGrantRecord, ContextGrantScope, ContextGrantScopeKind, ContextTemporal, IssueContextGrantInput, DelegateContextGrantInput, DelegateContextGrantResult, ReachEntry, GrantReachResult, } from './ContextGrantsClient.js';
18
18
  export { grantCaveat, CONTEXT_GRANT_SCOPE_KINDS, GRANT_SCOPE_KINDS, } from './grants.js';
19
- export type { GrantView, GrantScopeKind, GrantScopeView, GrantCaveatView, GrantHealth, } from './grants.js';
19
+ export type { GrantView, GrantScopeKind, GrantScopeView, GrantCaveatView, GrantHealth, GrantHolderKind, GrantAccessKind, GrantBasisKind, } from './grants.js';
20
20
  export { PaymentsClient } from './PaymentsClient.js';
21
21
  export type { PaymentsError, WalletBalance, WalletRef, PaymentTransactionView, TransferResult, HoldResult, ReleaseResult, PaymentGrantView, PaymentGrantEnvelope, RevokeGrantResult, PaymentApproval, WaitForApprovalResult, } from './PaymentsClient.js';
22
22
  export { ConnectionsClient, assertNoLeakedConnectionSecret, } from './ConnectionsClient.js';
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ export * from './http/index.js';
2
2
  export * from './capabilities/index.js';
3
3
  export * from './relay/provisionRelayWorkers.js';
4
4
  export { ConnectionManager } from './ConnectionManager.js';
5
- export { EntryTypes, ContentTypes, OPEN_AGREEMENT_TARGET, ORG_AGREEMENT_TARGET, BROADCAST_TARGETS, isBroadcastTarget, isPersonaRef, isRoomPresentationRef, isOpaquePresentationRef, AGREEMENT_ENGAGEMENT_KIND, isValidContentType, } from './types.js';
5
+ 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
6
  export type { PrincipalPresentation } from './types.js';
7
7
  export { getBackendUrl } from './utils/urlUtils.js';
8
8
  export { configureApiClient, apiClientConfig } from './config.js';
@@ -11,5 +11,5 @@ export { runtimeLog, resetRuntimeLogLevelCache } from './shared/runtimeLog.js';
11
11
  export { RateLimitedError, isRateLimited, parseRetryAfterMs, pollSurfaceError, } from './shared/rateLimit.js';
12
12
  export { parseErrorMessage, parseErrorCode, throwApiError, } from './shared/apiError.js';
13
13
  export { ApiError } from './types.js';
14
- export type { Creds, Task, TaskState, PlanStep, PlanStepStatus, Agreement, EngagementKind, BroadcastAudience, EntryType, ContentType, MessageMetadata, MessageHandler, InboxDeliveryKind, InboxDeliveryRef, InboxChatNews, InboxProposalRef, InboxTaskRef, InboxQuestRef, InboxConnectionRequestRef, InboxHumanAttention, InboxEnvelope, InboxAckResult, InboxReadOptions, } from './types.js';
15
- export type { ProposeTerms, ProposeDirectInput, ProposeBroadcastInput, ProposeAgreementData, } from './http/AgreementClient.js';
14
+ export type { Creds, Task, TaskState, PlanStep, PlanStepStatus, Agreement, AgreementParties, AgreementPartySide, EngagementKind, BroadcastAudience, EntryType, ContentType, MessageMetadata, MessageHandler, InboxDeliveryKind, InboxDeliveryRef, InboxChatNews, InboxProposalRef, InboxTaskRef, InboxRequestRef, InboxConnectionRequestRef, InboxHumanAttention, InboxEnvelope, InboxAckResult, InboxReadOptions, } from './types.js';
15
+ export type { ProposeTerms, ProposeDirectInput, ProposeBroadcastInput, ProposeAgreementData, ClaimOptions, } from './http/AgreementClient.js';
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ export * from './http/index.js';
2
2
  export * from './capabilities/index.js';
3
3
  export * from './relay/provisionRelayWorkers.js';
4
4
  export { ConnectionManager } from './ConnectionManager.js';
5
- export { EntryTypes, ContentTypes, OPEN_AGREEMENT_TARGET, ORG_AGREEMENT_TARGET, BROADCAST_TARGETS, isBroadcastTarget, isPersonaRef, isRoomPresentationRef, isOpaquePresentationRef, AGREEMENT_ENGAGEMENT_KIND, isValidContentType, } from './types.js';
5
+ 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
6
  export { getBackendUrl } from './utils/urlUtils.js';
7
7
  // the host injects the environment; this package never reads it.
8
8
  export { configureApiClient, apiClientConfig } from './config.js';
@@ -0,0 +1,4 @@
1
+ export declare const INBOX_HOST_CLAIMANT_HEADER = "X-Ziggs-Instance";
2
+ export declare function instanceIdentity(): string;
3
+ /** Test seam: forget the memoized value so a case can set different env. */
4
+ export declare function resetInstanceIdentityForTests(): void;
@@ -0,0 +1,44 @@
1
+ import { hostname } from 'node:os';
2
+ /**
3
+ * Which process is calling GET /inbox.
4
+ *
5
+ * The exclusive-read claimant must be the *host* (this fleet task, a
6
+ * laptop fleet, an MCP process), not the API process. Two fleets share
7
+ * one backend task, so stamping the API's identity would let both wake.
8
+ *
9
+ * `ecs:<id>` on Fargate, `local:<host>:<pid>` otherwise. Env-derived
10
+ * and memoized.
11
+ */
12
+ let cached = null;
13
+ const MAX_LENGTH = 64;
14
+ export const INBOX_HOST_CLAIMANT_HEADER = 'X-Ziggs-Instance';
15
+ export function instanceIdentity() {
16
+ if (cached)
17
+ return cached;
18
+ cached = resolve().slice(0, MAX_LENGTH);
19
+ return cached;
20
+ }
21
+ /** Test seam: forget the memoized value so a case can set different env. */
22
+ export function resetInstanceIdentityForTests() {
23
+ cached = null;
24
+ }
25
+ function resolve() {
26
+ const metadataUri = process.env.ECS_CONTAINER_METADATA_URI_V4 ??
27
+ process.env.ECS_CONTAINER_METADATA_URI;
28
+ if (metadataUri) {
29
+ const id = metadataUri.split('/').filter(Boolean).pop() ?? '';
30
+ const short = id.replace(/[^A-Za-z0-9]/g, '').slice(0, 12);
31
+ if (short.length >= 8)
32
+ return `ecs:${short}`;
33
+ }
34
+ const host = process.env.HOSTNAME || safeHostname();
35
+ return `local:${host}:${process.pid}`;
36
+ }
37
+ function safeHostname() {
38
+ try {
39
+ return hostname() || 'unknown-host';
40
+ }
41
+ catch {
42
+ return 'unknown-host';
43
+ }
44
+ }
@@ -1,5 +1,5 @@
1
1
  import type { Agreement, Creds } from '../types.js';
2
- import { claimOffer, delegateAgreement, getMyAgreements, pullOffers } from '../http/index.js';
2
+ import { claimOpenAgreement, delegateAgreement, getMyAgreements, pullOffers } from '../http/index.js';
3
3
  /** Minimal relay step shape — keep field names aligned with agents/coordinators/relayTypes.ts */
4
4
  export interface RelayStepInput {
5
5
  stepId: string;
@@ -28,7 +28,7 @@ export type ProvisionedRelayStep = RelayPayloadShape['steps'][number] & {
28
28
  export interface RelayProvisionDeps {
29
29
  getMyAgreements: typeof getMyAgreements;
30
30
  pullOffers: typeof pullOffers;
31
- claimOffer: typeof claimOffer;
31
+ claimOpenAgreement: typeof claimOpenAgreement;
32
32
  delegateAgreement: typeof delegateAgreement;
33
33
  }
34
34
  export interface ProvisionRelayWorkersInput {
@@ -1,6 +1,6 @@
1
- import { claimOffer, delegateAgreement, getMyAgreements, pullOffers, } from '../http/index.js';
1
+ import { claimOpenAgreement, delegateAgreement, getMyAgreements, pullOffers, } from '../http/index.js';
2
2
  function providerAgentId(agreement) {
3
- return agreement.parties?.providerAgent ?? agreement.parties?.provider ?? null;
3
+ return agreement.parties?.provider?.actor ?? null;
4
4
  }
5
5
  function isActiveAgreement(agreement) {
6
6
  return agreement.status === 'active';
@@ -30,7 +30,7 @@ export function findOpenOfferForAgent(assigneeId, offers) {
30
30
  export async function provisionRelayWorkers(input, deps = {
31
31
  getMyAgreements,
32
32
  pullOffers,
33
- claimOffer,
33
+ claimOpenAgreement,
34
34
  delegateAgreement,
35
35
  }) {
36
36
  const { creds, hireAgreementId, chatId, steps, inputArtifactIds } = input;
@@ -55,7 +55,7 @@ export async function provisionRelayWorkers(input, deps = {
55
55
  continue;
56
56
  }
57
57
  if (step.offerAgreementId) {
58
- const claimed = await deps.claimOffer(step.offerAgreementId, creds);
58
+ const { agreement: claimed } = await deps.claimOpenAgreement(step.offerAgreementId, creds);
59
59
  const agreementId = claimed.agreementId;
60
60
  const status = isActiveAgreement(claimed)
61
61
  ? 'active'
@@ -78,7 +78,7 @@ export async function provisionRelayWorkers(input, deps = {
78
78
  }
79
79
  const openOffer = findOpenOfferForAgent(step.assigneeId, offersCache);
80
80
  if (openOffer?.agreementId) {
81
- const claimed = await deps.claimOffer(openOffer.agreementId, creds);
81
+ const { agreement: claimed } = await deps.claimOpenAgreement(openOffer.agreementId, creds);
82
82
  const agreementId = claimed.agreementId;
83
83
  const status = isActiveAgreement(claimed)
84
84
  ? 'active'
package/dist/types.d.ts CHANGED
@@ -91,19 +91,46 @@ export declare const AGREEMENT_ENGAGEMENT_KIND: {
91
91
  readonly LINK: "link";
92
92
  };
93
93
  export type EngagementKind = (typeof AGREEMENT_ENGAGEMENT_KIND)[keyof typeof AGREEMENT_ENGAGEMENT_KIND];
94
+ /**
95
+ * One side of an agreement.
96
+ *
97
+ * An agent id occupies `actor` and nowhere else, so "is this agent on this
98
+ * side?" is a structural read rather than a lookup against the Agent
99
+ * collection. Asking `principal` about an agent id is always the wrong
100
+ * question — it would match that agent's estate instead.
101
+ */
102
+ export interface AgreementPartySide {
103
+ /**
104
+ * The accountable person or org — never an agent. On `payer` and `proposedTo`
105
+ * this may instead hold a broadcast sentinel ({@link isBroadcastTarget}), in
106
+ * which case nobody has taken the side up yet.
107
+ */
108
+ principal?: string | null;
109
+ /** The agent that performed on this side. Null when the principal acted itself. */
110
+ actor?: string | null;
111
+ }
112
+ /** The four sides of an agreement, each {@link AgreementPartySide}. */
113
+ export interface AgreementParties {
114
+ payer?: AgreementPartySide;
115
+ provider?: AgreementPartySide;
116
+ proposedTo?: AgreementPartySide;
117
+ creator?: AgreementPartySide;
118
+ }
119
+ /** Both ids on one side, principal first, absent ones dropped. */
120
+ export declare function partySideIds(side: AgreementPartySide | null | undefined): string[];
121
+ /**
122
+ * Every agent that acted on this agreement, deduped.
123
+ *
124
+ * The read for "is this agent a party?" — an agent id lives in `actor` alone,
125
+ * so widening the match to `principal` would hit an unrelated estate.
126
+ */
127
+ export declare function partyActorIds(parties: AgreementParties | null | undefined): string[];
94
128
  /** Compact agreement reference carried by task reads. */
95
129
  export interface AgreementSummary {
96
130
  agreementId: string;
97
131
  description: string;
98
132
  status: AgreementStatus;
99
- parties?: {
100
- proposedTo?: string | null;
101
- provider?: string | null;
102
- payer?: string | null;
103
- creator?: string | null;
104
- creatorAgent?: string | null;
105
- providerAgent?: string | null;
106
- };
133
+ parties?: AgreementParties;
107
134
  /** The opposite party relative to the requesting agent; null for observers,
108
135
  * broadcast sentinels, and self-agreements. */
109
136
  counterparty: string | null;
@@ -120,14 +147,7 @@ export interface Agreement {
120
147
  status?: AgreementStatus;
121
148
  engagementKind?: EngagementKind;
122
149
  proposalStatus?: ProposalStatus;
123
- parties?: {
124
- proposedTo?: string;
125
- provider?: string;
126
- payer?: string;
127
- creator?: string;
128
- creatorAgent?: string;
129
- providerAgent?: string;
130
- };
150
+ parties?: AgreementParties;
131
151
  approvals?: AgreementApprovalEntry[];
132
152
  /** Legacy root-level price — always null in practice. Use money.price instead. */
133
153
  price?: number | null;
@@ -287,14 +307,34 @@ export type MessageHandler = (text: string, metadata: MessageMetadata) => Promis
287
307
  * on this side validates a delivery kind at runtime (the server does that on the
288
308
  * way in), and exhaustiveness checking is purely type-level.
289
309
  */
290
- export type InboxDeliveryKind = 'message' | 'artifact' | 'task-state' | 'agreement' | 'quest';
310
+ export type InboxDeliveryKind = 'message' | 'artifact' | 'task-state' | 'agreement' | 'request';
311
+ /** The two mailbox owners. Agents own no mailbox — they read through grants. */
312
+ export type InboxPartyKind = 'human' | 'org';
313
+ /** One mailbox this reader's merged view includes. */
314
+ export interface InboxSourceRef {
315
+ partyId: string;
316
+ partyKind: InboxPartyKind;
317
+ }
291
318
  /**
292
- * One thing addressed to this agent. A reference, never content — following it
293
- * (a chat read, a task read) is where this agent's grants are enforced.
319
+ * One row in this reader's merged view. A reference, never content — following
320
+ * it (a chat read, a task read) is where this agent's grants are enforced.
321
+ *
322
+ * "Mine to act on" is `assigneeId === my agent id`, nothing else. A row
323
+ * without my stamp is context I may read, never a wake and never mine to ack
324
+ * as handled.
294
325
  */
295
326
  export interface InboxDeliveryRef {
296
327
  kind: InboxDeliveryKind;
297
328
  resourceId: string;
329
+ /** One emit, one id — copies of the same event collapse on this. */
330
+ eventId: string;
331
+ /** The mailbox this copy lives in. */
332
+ partyId: string;
333
+ partyKind: InboxPartyKind;
334
+ /** The ONE agent stamped to act; null when nothing has to. */
335
+ assigneeId: string | null;
336
+ /** The owner's human should see this. */
337
+ needsHuman: boolean;
298
338
  chatId: string | null;
299
339
  agreementId: string | null;
300
340
  taskId: string | null;
@@ -377,10 +417,10 @@ export interface InboxTaskRef {
377
417
  updatedAt: string | null;
378
418
  }
379
419
  /**
380
- * A marketplace quest doorbell. Own channel so the host can
420
+ * A marketplace request doorbell. Own channel so the host can
381
421
  * exact-match triage with zero LLM tokens before any wake.
382
422
  */
383
- export interface InboxQuestRef {
423
+ export interface InboxRequestRef {
384
424
  agreementId: string;
385
425
  /** Exact-match string from the publisher — compare to the agent's tags. */
386
426
  match: string;
@@ -390,27 +430,36 @@ export interface InboxQuestRef {
390
430
  export interface InboxEnvelope {
391
431
  asOf: string;
392
432
  /**
393
- * Unacked deliveries addressed to this agent, newest first. This IS the
394
- * inbox — read straight out of the delivery log, not derived from grants.
433
+ * The mailboxes this reader's view merges — its owner's, plus whatever
434
+ * inbox grants add. Absent on older servers.
435
+ */
436
+ sources?: InboxSourceRef[];
437
+ /**
438
+ * Unacked rows across every source, newest first, copies of one event
439
+ * collapsed. This IS the inbox — read straight out of the party delivery
440
+ * logs, not derived from grants. Rows with `assigneeId === me` are mine to
441
+ * act on; the rest are readable context.
395
442
  */
396
443
  deliveries: InboxDeliveryRef[];
397
444
  /** True when there was more than one envelope's worth; the rest stay unacked. */
398
445
  deliveriesCapped: boolean;
399
- /** The chat-bearing deliveries above, folded by chat. */
446
+ /** This agent's ASSIGNED chat mail, folded by chat. */
400
447
  chats: InboxChatNews[];
401
448
  /**
402
- * Pass to `ack(upTo, { handledResourceIds })` after acting.
403
- * Null when there is nothing to ack. Ack after acting, not after reading:
404
- * a crash in between redelivers. Listing every handled resourceId is what
405
- * stops a partial triage from burying other chats.
449
+ * Opaque watermark bundle. Pass to `ack(upTo, { handledResourceIds })`
450
+ * VERBATIM after acting — never construct or parse one; the per-mailbox
451
+ * positions live inside the value. Null when there is nothing to ack. Ack
452
+ * after acting, not after reading: a crash in between redelivers. Listing
453
+ * every ASSIGNED resourceId is what stops a partial triage from burying
454
+ * work.
406
455
  */
407
456
  ackTo: string | null;
408
457
  /** Open tasks assigned to this agent — the work channel. */
409
458
  tasksAwaitingMe: InboxTaskRef[];
410
459
  truncatedTasks: number;
411
- /** Unacked quest deliveries — triaged before any LLM wake. */
412
- questsAwaitingMe?: InboxQuestRef[];
413
- truncatedQuests?: number;
460
+ /** Unacked request deliveries — triaged before any LLM wake. */
461
+ requestsAwaitingMe?: InboxRequestRef[];
462
+ truncatedRequests?: number;
414
463
  proposalsAwaitingMe: InboxProposalRef[];
415
464
  truncatedProposals: number;
416
465
  connectionRequestsAwaitingMe: InboxConnectionRequestRef[];
package/dist/types.js CHANGED
@@ -34,6 +34,24 @@ export const AGREEMENT_ENGAGEMENT_KIND = {
34
34
  /** a bilateral reach link between two agents (no money, no work). */
35
35
  LINK: 'link',
36
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
+ const p = parties ?? {};
51
+ return [
52
+ ...new Set(AGREEMENT_PARTY_SIDES.map((name) => p[name]?.actor).filter((id) => typeof id === 'string' && id.length > 0)),
53
+ ];
54
+ }
37
55
  /** ⚠️ Mirrors the server's entry-type constant; the server is authoritative. */
38
56
  export const EntryTypes = {
39
57
  MESSAGE: 'message',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ziggs-ai/api-client",
3
- "version": "0.10.4",
3
+ "version": "0.12.0",
4
4
  "description": "HTTP and WebSocket client for the Ziggs backend API",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",