@ziggs-ai/api-client 0.19.0 → 0.21.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 (45) hide show
  1. package/dist/capabilities/agreementVerbs.d.ts +7 -0
  2. package/dist/capabilities/agreementVerbs.js +84 -1
  3. package/dist/capabilities/agreements.d.ts +3 -0
  4. package/dist/capabilities/agreements.js +9 -1
  5. package/dist/capabilities/artifacts.d.ts +38 -0
  6. package/dist/capabilities/artifacts.js +148 -7
  7. package/dist/capabilities/chat.d.ts +31 -1
  8. package/dist/capabilities/chat.js +39 -0
  9. package/dist/capabilities/context.d.ts +3 -0
  10. package/dist/capabilities/context.js +125 -1
  11. package/dist/capabilities/index.d.ts +6 -6
  12. package/dist/capabilities/index.js +6 -6
  13. package/dist/capabilities/introductions.js +5 -10
  14. package/dist/capabilities/links.js +1 -0
  15. package/dist/capabilities/marketplace.js +15 -6
  16. package/dist/capabilities/nextCall.d.ts +72 -14
  17. package/dist/capabilities/nextCall.js +279 -4
  18. package/dist/capabilities/tasks.d.ts +68 -1
  19. package/dist/capabilities/tasks.js +112 -3
  20. package/dist/engagementGuide.d.ts +148 -0
  21. package/dist/engagementGuide.js +349 -0
  22. package/dist/http/AgreementClient.d.ts +2 -1
  23. package/dist/http/AgreementClient.js +4 -0
  24. package/dist/http/ArtifactsClient.d.ts +47 -0
  25. package/dist/http/ArtifactsClient.js +33 -0
  26. package/dist/http/ChatClient.d.ts +28 -2
  27. package/dist/http/ChatClient.js +7 -0
  28. package/dist/http/ContextGrantsClient.d.ts +17 -1
  29. package/dist/http/ContextGrantsClient.js +26 -2
  30. package/dist/http/ContextOpenClient.d.ts +70 -0
  31. package/dist/http/ContextOpenClient.js +52 -0
  32. package/dist/http/InboxClient.d.ts +8 -0
  33. package/dist/http/InboxClient.js +18 -0
  34. package/dist/http/TaskClient.d.ts +2 -0
  35. package/dist/http/TaskClient.js +1 -0
  36. package/dist/http/index.d.ts +2 -0
  37. package/dist/http/index.js +1 -0
  38. package/dist/index.d.ts +4 -0
  39. package/dist/index.js +2 -0
  40. package/dist/instanceIdentity.d.ts +1 -1
  41. package/dist/instanceIdentity.js +2 -2
  42. package/dist/sessionOrientation.d.ts +50 -0
  43. package/dist/sessionOrientation.js +43 -0
  44. package/dist/types.d.ts +11 -0
  45. package/package.json +2 -2
@@ -1,4 +1,5 @@
1
1
  import { ContextReadClient, CONTEXT_READ_TYPES, CONTEXT_READ_VIA, parseVia, viaHint, } from '../http/ContextReadClient.js';
2
+ import { ContextOpenClient } from '../http/ContextOpenClient.js';
2
3
  import { ContextGrantsClient, } from '../http/ContextGrantsClient.js';
3
4
  import { ContextDiscoveryClient } from '../http/ContextDiscoveryClient.js';
4
5
  import { grantCaveat, CONTEXT_GRANT_SCOPE_KINDS, } from '../http/grants.js';
@@ -194,6 +195,10 @@ export const contextDelegateCapability = {
194
195
  type: 'string',
195
196
  description: 'from-now watermark ISO-8601 (optional; server may default)',
196
197
  },
198
+ idempotencyKey: {
199
+ type: 'string',
200
+ description: 'Replay the same delegate. Same key and payload recover the original request; the same key with a different payload is a conflict, not a second card.',
201
+ },
197
202
  },
198
203
  needsAgentId: true,
199
204
  handler: async (args, env) => {
@@ -221,11 +226,18 @@ export const contextDelegateCapability = {
221
226
  temporal: temporal,
222
227
  expiresAt: args['expiresAt'] ?? undefined,
223
228
  watermarkAt: args['watermarkAt'],
229
+ }, {
230
+ idempotencyKey: typeof args['idempotencyKey'] === 'string'
231
+ ? args['idempotencyKey']
232
+ : undefined,
224
233
  });
225
234
  if (result.status === 'pending_approval') {
226
235
  return {
227
236
  status: 'pending_approval',
228
- message: "This grant's original owner must approve sharing it. A request was opened for them — surface it to the human; nothing is granted yet.",
237
+ state: 'requested',
238
+ outcome: 'pending',
239
+ recoverable: true,
240
+ message: "Requested, not granted — this grant's original owner must approve. Recover agreementId; do not treat this as a share and do not widen the scope.",
229
241
  parentGrantId: args['parentGrantId'],
230
242
  agreementId: result.agreementId,
231
243
  ownerId: result.ownerId,
@@ -234,15 +246,127 @@ export const contextDelegateCapability = {
234
246
  const grant = result.grant;
235
247
  return {
236
248
  status: 'delegated',
249
+ outcome: 'ok',
237
250
  parentGrantId: args['parentGrantId'],
238
251
  grant,
239
252
  bounds: contextBounds(grant),
240
253
  };
241
254
  },
242
255
  };
256
+ const OPEN_ID_PARAMS = {
257
+ artifactId: {
258
+ type: 'string',
259
+ description: 'Artifact id from a prior response. Pass exactly one id field.',
260
+ },
261
+ chatId: {
262
+ type: 'string',
263
+ description: 'Chat id from a prior response. Pass exactly one id field.',
264
+ },
265
+ taskId: {
266
+ type: 'string',
267
+ description: 'Task id from a prior response. Pass exactly one id field.',
268
+ },
269
+ agreementId: {
270
+ type: 'string',
271
+ description: 'Agreement id from a prior response. Pass exactly one id field.',
272
+ },
273
+ };
274
+ function openBodyFromArgs(args) {
275
+ const body = {};
276
+ for (const key of ['artifactId', 'chatId', 'taskId', 'agreementId']) {
277
+ const value = args[key];
278
+ if (typeof value === 'string' && value.trim())
279
+ body[key] = value.trim();
280
+ }
281
+ if (typeof args['cursor'] === 'string')
282
+ body.cursor = args['cursor'];
283
+ if (typeof args['after'] === 'string')
284
+ body.after = args['after'];
285
+ if (typeof args['limit'] === 'number')
286
+ body.limit = args['limit'];
287
+ return body;
288
+ }
289
+ function presentOpenResult(result) {
290
+ // Never attach a ready write. An unreadable row already carries
291
+ // access.continuation (owner-decision); this call must not execute it.
292
+ return { ...result, actions: [] };
293
+ }
294
+ export const openCapability = {
295
+ key: 'open',
296
+ names: { sdk: 'open', mcp: 'ziggs_open' },
297
+ title: 'Open a returned resource',
298
+ descriptions: {
299
+ 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.',
300
+ 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.',
301
+ },
302
+ annotation: 'read-only',
303
+ params: {
304
+ ...OPEN_ID_PARAMS,
305
+ cursor: { type: 'string', description: 'Opaque cursor from a prior open page' },
306
+ after: { type: 'string', description: 'ISO timestamp for a forward-delta' },
307
+ limit: { type: 'number', description: 'Page size' },
308
+ },
309
+ needsAgentId: true,
310
+ handler: async (args, env) => {
311
+ const creds = fullCreds(env);
312
+ const client = new ContextOpenClient(creds.operatorKey, creds.agentId, env.baseUrl, creds.laneId);
313
+ return presentOpenResult(await client.open(openBodyFromArgs(args)));
314
+ },
315
+ };
316
+ export const accessExplainCapability = {
317
+ key: 'access_explain',
318
+ names: { sdk: 'access_explain', mcp: 'ziggs_access_explain' },
319
+ title: 'Explain access to a resource',
320
+ descriptions: {
321
+ 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.',
322
+ 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.',
323
+ },
324
+ annotation: 'read-only',
325
+ params: OPEN_ID_PARAMS,
326
+ needsAgentId: true,
327
+ handler: async (args, env) => {
328
+ const creds = fullCreds(env);
329
+ const client = new ContextOpenClient(creds.operatorKey, creds.agentId, env.baseUrl, creds.laneId);
330
+ const result = await client.explain(openBodyFromArgs(args));
331
+ return presentOpenResult(result);
332
+ },
333
+ };
243
334
  export const CONTEXT_CAPABILITIES = [
244
335
  contextReadCapability,
336
+ openCapability,
337
+ accessExplainCapability,
245
338
  contextDelegateCapability,
246
339
  contextExpandReachCapability,
247
340
  contextDiscoverGrantableCapability,
248
341
  ];
342
+ export const contextRequestCapability = {
343
+ key: 'context_request',
344
+ names: { sdk: 'context_request', mcp: 'ziggs_context_request' },
345
+ title: 'Ask an owner for artifact access',
346
+ descriptions: {
347
+ sdk: 'Ask a human owner for temporary read-only access to one artifact you were given a reference for. Use your existing chat with that owner. Creates an approval agreement; gives no access until the owner approves. You are the only recipient. After approval, open the original artifact. Never use a generic service agreement as a substitute for this access request.',
348
+ mcp: 'Request access to a restricted artifact directly from its human owner. Pass the artifact id you were given, your conversation with the owner, and the owner account id or participant reference shown in that chat. This creates a pending approval agreement, NOT a grant. No prior grant is needed. The human approves at the agreement link. After approval, ziggs_open the original artifact. Scope is read-only, one artifact, for 1–168 hours (default 24); no other artifacts or chats. Include the intended summary recipient in reason. Keep polling for the decision when asked to wait. A request does not confirm that the artifact exists or belongs to the named owner; ownership is checked at approval.',
349
+ },
350
+ annotation: 'write', needsAgentId: true,
351
+ params: {
352
+ artifactId: { type: 'string', required: true },
353
+ chatId: { type: 'string', required: true },
354
+ ownerId: { type: 'string', required: true, description: 'Human account id or room participant reference, not a persona face' },
355
+ reason: { type: 'string', required: true, description: 'Purpose, including whom you will summarize the artifact to' },
356
+ durationHours: { type: 'number', description: 'Integer 1–168 hours after approval; default 24' },
357
+ idempotencyKey: { type: 'string', description: 'Reuse for the same request; do not create duplicate approval cards' },
358
+ },
359
+ handler: async (args, env) => {
360
+ for (const key of ['artifactId', 'chatId', 'ownerId', 'reason'])
361
+ if (typeof args[key] !== 'string' || !String(args[key]).trim())
362
+ throw new Error(`${key} is required`);
363
+ const durationHours = args.durationHours === undefined ? 24 : Number(args.durationHours);
364
+ if (!Number.isInteger(durationHours) || durationHours < 1 || durationHours > 168)
365
+ throw new Error('durationHours must be an integer between 1 and 168');
366
+ const creds = fullCreds(env);
367
+ const result = await new ContextGrantsClient(creds.operatorKey, creds.agentId, env.baseUrl).requestArtifactAccess(args.artifactId, {
368
+ chatId: args.chatId, ownerId: args.ownerId, reason: args.reason, durationHours,
369
+ }, { idempotencyKey: args.idempotencyKey, laneId: creds.laneId });
370
+ return { ...result, outcome: 'pending', state: 'requested', appUrl: `${(env.webUrl ?? 'https://ziggsai.com').replace(/\/$/, '')}/app/agreements/${encodeURIComponent(result.agreementId)}`, note: 'Waiting for the artifact owner. No access granted yet; open the original artifact only after approval.' };
371
+ },
372
+ };
@@ -1,17 +1,17 @@
1
1
  export { type CapabilitySurface, type CapabilityAnnotation, type CapabilityParam, type CapabilityEnv, type CapabilityDefinition, fullCreds, rethrowWithContext, } from './types.js';
2
- export { nextCall, peerPrincipalId, type NextCall, } from './nextCall.js';
3
- export { AGREEMENT_VERB_CAPABILITIES, agreementBuyCapability, agreementBidCapability, agreementBrokerCapability, agreementRequestCapability, agreementOfferCapability, agreementHandoffCapability, } from './agreementVerbs.js';
2
+ export { nextCall, nextCallsFromUntrustedContent, isStaleAdvertisedAction, peerPrincipalId, isPersonaFace, workContextFromEnv, type NextCall, type NextCallOutcome, type NextCallEffects, type NextCallHold, type NextCallOptions, type WorkContext, } from './nextCall.js';
3
+ export { AGREEMENT_VERB_CAPABILITIES, agreementBuyCapability, agreementBidCapability, agreementBrokerCapability, agreementRequestCapability, agreementOfferCapability, agreementHandoffCapability, agreementSubcontractCapability, } from './agreementVerbs.js';
4
4
  export { PAYMENT_CAPABILITIES, paymentBalanceCapability } from './payments.js';
5
5
  export { LINK_CAPABILITIES, listLinksCapability, proposeLinkCapability, linkIsReachOnly, } from './links.js';
6
6
  export { INTRODUCTION_CAPABILITIES, mintIntroductionCapability, redeemIntroductionCapability, listIntroductionsCapability, revokeIntroductionCapability, } from './introductions.js';
7
7
  export { AGREEMENT_CAPABILITIES, agreementClaimCapability, presentClaimResult, } from './agreements.js';
8
8
  export { LIST_FIELDS_PARAM, parseListFields, pickListedRow, pickListedRows, } from './listedFields.js';
9
- export { TASK_CAPABILITIES, listTasksCapability } from './tasks.js';
9
+ export { TASK_CAPABILITIES, listTasksCapability, cancelTaskCapability, presentTaskOutcome, type TaskOutcomeEffects, type TaskOutcomePresentation, } from './tasks.js';
10
10
  export { AGREEMENT_PROPOSE_PROVIDER_ID_DESCRIPTION } from './proposeProviderId.js';
11
11
  export { MARKETPLACE_CAPABILITIES, marketplaceViewCapability } from './marketplace.js';
12
12
  export { GRANTS_CAPABILITIES, listGrantsCapability } from './grants.js';
13
- export { CONTEXT_CAPABILITIES, contextReadCapability, contextDelegateCapability, contextExpandReachCapability, contextDiscoverGrantableCapability, contextBounds, resolveOrgScopeId, } from './context.js';
13
+ export { CONTEXT_CAPABILITIES, contextReadCapability, openCapability, accessExplainCapability, contextDelegateCapability, contextRequestCapability, contextExpandReachCapability, contextDiscoverGrantableCapability, contextBounds, resolveOrgScopeId, } from './context.js';
14
14
  export { CONNECTION_CAPABILITIES, connectionProxyCapability, requestConnectionCapability, } from './connections.js';
15
15
  export { DISCOVERY_CAPABILITIES, agentSearchCapability, agentGetCapability, } from './discovery.js';
16
- export { ARTIFACT_CAPABILITIES, recordArtifactCapability, listArtifactsCapability, shareArtifactCapability, attachArtifactCapability, uploadArtifactUrlCapability, completeArtifactFileCapability, downloadArtifactCapability, reextractArtifactCapability, } from './artifacts.js';
17
- export { CHAT_CAPABILITIES, openConversationCapability } from './chat.js';
16
+ export { ARTIFACT_CAPABILITIES, recordArtifactCapability, listArtifactsCapability, findArtifactsCapability, type UnsearchedContext, shareArtifactCapability, attachArtifactCapability, uploadArtifactUrlCapability, completeArtifactFileCapability, downloadArtifactCapability, reextractArtifactCapability, } from './artifacts.js';
17
+ export { CHAT_CAPABILITIES, openConversationCapability, presentSendResult, type SendPresentation, } from './chat.js';
@@ -1,17 +1,17 @@
1
1
  export { fullCreds, rethrowWithContext, } from './types.js';
2
- export { nextCall, peerPrincipalId, } from './nextCall.js';
3
- export { AGREEMENT_VERB_CAPABILITIES, agreementBuyCapability, agreementBidCapability, agreementBrokerCapability, agreementRequestCapability, agreementOfferCapability, agreementHandoffCapability, } from './agreementVerbs.js';
2
+ export { nextCall, nextCallsFromUntrustedContent, isStaleAdvertisedAction, peerPrincipalId, isPersonaFace, workContextFromEnv, } from './nextCall.js';
3
+ export { AGREEMENT_VERB_CAPABILITIES, agreementBuyCapability, agreementBidCapability, agreementBrokerCapability, agreementRequestCapability, agreementOfferCapability, agreementHandoffCapability, agreementSubcontractCapability, } from './agreementVerbs.js';
4
4
  export { PAYMENT_CAPABILITIES, paymentBalanceCapability } from './payments.js';
5
5
  export { LINK_CAPABILITIES, listLinksCapability, proposeLinkCapability, linkIsReachOnly, } from './links.js';
6
6
  export { INTRODUCTION_CAPABILITIES, mintIntroductionCapability, redeemIntroductionCapability, listIntroductionsCapability, revokeIntroductionCapability, } from './introductions.js';
7
7
  export { AGREEMENT_CAPABILITIES, agreementClaimCapability, presentClaimResult, } from './agreements.js';
8
8
  export { LIST_FIELDS_PARAM, parseListFields, pickListedRow, pickListedRows, } from './listedFields.js';
9
- export { TASK_CAPABILITIES, listTasksCapability } from './tasks.js';
9
+ export { TASK_CAPABILITIES, listTasksCapability, cancelTaskCapability, presentTaskOutcome, } from './tasks.js';
10
10
  export { AGREEMENT_PROPOSE_PROVIDER_ID_DESCRIPTION } from './proposeProviderId.js';
11
11
  export { MARKETPLACE_CAPABILITIES, marketplaceViewCapability } from './marketplace.js';
12
12
  export { GRANTS_CAPABILITIES, listGrantsCapability } from './grants.js';
13
- export { CONTEXT_CAPABILITIES, contextReadCapability, contextDelegateCapability, contextExpandReachCapability, contextDiscoverGrantableCapability, contextBounds, resolveOrgScopeId, } from './context.js';
13
+ export { CONTEXT_CAPABILITIES, contextReadCapability, openCapability, accessExplainCapability, contextDelegateCapability, contextRequestCapability, contextExpandReachCapability, contextDiscoverGrantableCapability, contextBounds, resolveOrgScopeId, } from './context.js';
14
14
  export { CONNECTION_CAPABILITIES, connectionProxyCapability, requestConnectionCapability, } from './connections.js';
15
15
  export { DISCOVERY_CAPABILITIES, agentSearchCapability, agentGetCapability, } from './discovery.js';
16
- export { ARTIFACT_CAPABILITIES, recordArtifactCapability, listArtifactsCapability, shareArtifactCapability, attachArtifactCapability, uploadArtifactUrlCapability, completeArtifactFileCapability, downloadArtifactCapability, reextractArtifactCapability, } from './artifacts.js';
17
- export { CHAT_CAPABILITIES, openConversationCapability } from './chat.js';
16
+ export { ARTIFACT_CAPABILITIES, recordArtifactCapability, listArtifactsCapability, findArtifactsCapability, shareArtifactCapability, attachArtifactCapability, uploadArtifactUrlCapability, completeArtifactFileCapability, downloadArtifactCapability, reextractArtifactCapability, } from './artifacts.js';
17
+ export { CHAT_CAPABILITIES, openConversationCapability, presentSendResult, } from './chat.js';
@@ -79,6 +79,7 @@ export const redeemIntroductionCapability = {
79
79
  const staged = intro.outcome === 'link_pending';
80
80
  return {
81
81
  introduction: intro,
82
+ outcome: staged ? 'pending' : 'ok',
82
83
  message: staged
83
84
  ? `Redeemed. A pending link with org ${intro.from.orgId} (${intro.from.agentId ?? 'no agent id'}) is staged (${intro.agreementId}). The display name they claimed is self-chosen and unverified. The link becomes real when both people approve it — a link is reach only, and shares no context by itself.`
84
85
  : intro.outcome === 'already_teammates'
@@ -88,6 +89,9 @@ export const redeemIntroductionCapability = {
88
89
  // relationship exists, so the next move is simply to talk. Leaving the
89
90
  // plan empty there read as "nothing to do" on the branch a returning
90
91
  // counterparty is most likely to land on.
92
+ //
93
+ // A staged redeem is not a live hire: do not advertise a ready chat_open
94
+ // or a ready agreement_respond the agent can run. The human signs.
91
95
  readPlan: !staged
92
96
  ? intro.counterparty?.agentId
93
97
  ? [
@@ -95,16 +99,7 @@ export const redeemIntroductionCapability = {
95
99
  ]
96
100
  : []
97
101
  : [
98
- nextCall(env, 'agreement_respond', intro.agreementId ? { agreementId: intro.agreementId } : undefined, 'the link is pending — your human must approve it; do not sign on their behalf'),
99
- // The minter's AGENT, not their principal. A link's party principal
100
- // can be a persona face, which the chat rail refuses as an address
101
- // ("a face is display state and names no single party") — so the
102
- // door to that person is the agent that carried the introduction.
103
- ...(intro.from.agentId
104
- ? [
105
- nextCall(env, 'chat_open', { participantId: intro.from.agentId }, 'once the link is live, this is the door to the agent that introduced itself'),
106
- ]
107
- : []),
102
+ nextCall(env, 'agreement_respond', intro.agreementId ? { agreementId: intro.agreementId } : undefined, 'the link is pending — your human must approve it; do not sign on their behalf', { hold: 'human_approval' }),
108
103
  ],
109
104
  };
110
105
  },
@@ -177,6 +177,7 @@ export const proposeLinkCapability = {
177
177
  // in both directions, because none of them could ask.
178
178
  return {
179
179
  status: 'sent',
180
+ outcome: 'pending',
180
181
  shareUrl,
181
182
  agreementId,
182
183
  message: to
@@ -1,14 +1,16 @@
1
+ import { marketplaceFormation } from '../engagementGuide.js';
1
2
  import { pullOffers, pullRequests } from '../http/MarketplaceClient.js';
2
3
  import { fullCreds } from './types.js';
3
4
  import { nextCall } from './nextCall.js';
4
5
  import { LIST_FIELDS_PARAM, parseListFields, pickListedRows } from './listedFields.js';
5
6
  const VIEW_KINDS = ['all', 'requests', 'offers'];
6
7
  function publishHint(env) {
7
- const propose = env.surface === 'mcp' ? 'ziggs_agreement_request' : 'agreement_request';
8
8
  const claim = env.surface === 'mcp' ? 'ziggs_agreement_claim' : 'agreement_claim';
9
- return (`Claim any row with ${claim} (agreementId). Publish your own with ${propose}: ` +
10
- `proposedTo "everyone" or "org" with no providerId broadcasts a request (claimer works, you pay); ` +
11
- `the same with providerId = your own id publishes a standing offer (you work, claimer pays).`);
9
+ const publish = env.surface === 'mcp'
10
+ ? 'Publish a request with ziggs_agreement_request (claimer works, you pay) or a standing offer with ziggs_agreement_offer (you work, claimer pays).'
11
+ : 'Publish your own via agreement_propose with proposedTo "everyone"/"org" (providerId = your id for an offer, omitted for a request).';
12
+ return (`Reuse an active agreement that already covers the work. Otherwise claim any row with ${claim} ` +
13
+ `(agreementId) — listings are take-it-or-leave-it, never counter one. ${publish}`);
12
14
  }
13
15
  /** A broadcast sentinel in a party slot means "open", not a counterparty. */
14
16
  const BROADCASTS = new Set(['everyone', 'org']);
@@ -98,6 +100,10 @@ export const marketplaceViewCapability = {
98
100
  const fields = parseListFields(args['fields']);
99
101
  const requestRows = pickListedRows(requests.map((q) => toListingRow(q, 'request')), fields);
100
102
  const offerRows = pickListedRows(offers.map((o) => toListingRow(o, 'offer')), fields);
103
+ const listingAgreementIds = [...requests, ...offers]
104
+ .map((row) => row.agreementId)
105
+ .filter((id) => typeof id === 'string' && id.length > 0);
106
+ const engagement = marketplaceFormation(env, { listingAgreementIds });
101
107
  return {
102
108
  ...(kind !== 'offers'
103
109
  ? { requests: requestRows, requestCount: requests.length }
@@ -105,13 +111,16 @@ export const marketplaceViewCapability = {
105
111
  ...(kind !== 'requests'
106
112
  ? { offers: offerRows, offerCount: offers.length }
107
113
  : {}),
114
+ engagement,
108
115
  // Claiming is the move after browsing, and the id is in the row the
109
116
  // caller just received. The prose hint stays for the publish side, which
110
117
  // is a choice rather than a call.
111
118
  readPlan: [
112
- ...(requests.length || offers.length
119
+ ...(listingAgreementIds.length
113
120
  ? [
114
- nextCall(env, 'agreement_claim', undefined, 'claim a listing from this view by passing its agreementId — listings are take-it-or-leave-it, never countered'),
121
+ nextCall(env, 'agreement_claim', listingAgreementIds.length === 1
122
+ ? { agreementId: listingAgreementIds[0] }
123
+ : undefined, 'claim a listing from this view by passing its agreementId — listings are take-it-or-leave-it, never countered'),
115
124
  ]
116
125
  : []),
117
126
  ],
@@ -1,28 +1,72 @@
1
1
  import type { AgreementParties } from '../types.js';
2
- import type { CapabilityEnv } from './types.js';
2
+ import type { CapabilityDefinition, CapabilityEnv } from './types.js';
3
3
  /**
4
- * One runnable next step: a tool name, pre-filled arguments, and why.
4
+ * One next step: the tool the calling surface registers, any args already
5
+ * known, and why.
5
6
  *
6
- * The inbox and the context read already answer this way, and it is the part of
7
- * those tools that works best: a caller runs the entries verbatim instead of
8
- * parsing a paragraph for a tool name and then digging the ids out of the
9
- * response body it just received.
7
+ * Ready means a caller can run it verbatim. Incomplete means a required
8
+ * argument is missing or an address field is not an address — the suggestion
9
+ * still names the tool, and `missing` says what has to be filled first.
10
10
  *
11
- * Everywhere else said the same thing in prose, under two other field names, so
12
- * an agent had to read English to find out that "open a chat with the peer"
13
- * meant `chat_open` with a `participantId` it had to locate itself. Prose is
14
- * still right when the next move is not a call at all (a human has to decide
15
- * something); it is wrong when the call and its arguments are both already
16
- * known here.
11
+ * `http` is the same invocation on the published HTTP surface, bound from the
12
+ * capability key rather than a second implementation. Profile is not a field
13
+ * here: assistant vs worker is not a transport.
17
14
  */
18
15
  export interface NextCall {
19
16
  /** Registered tool name on the calling surface. */
20
17
  tool: string;
21
18
  /** Arguments, filled in from what the response already carries. */
22
19
  args?: Record<string, unknown>;
23
- /** One clause: what running this achieves. */
20
+ /** One clause: what running this achieves, or what is still missing. */
24
21
  why: string;
22
+ /** True only when every required argument is present and addressable. */
23
+ ready: boolean;
24
+ /** Required argument names that are absent or not an address. */
25
+ missing?: readonly string[];
26
+ /** Published HTTP method/path for the same capability, when we have one. */
27
+ http?: {
28
+ method: string;
29
+ path: string;
30
+ };
31
+ /**
32
+ * Whether this step is a finished action, a parked human decision, a
33
+ * half-filled suggestion, or something this surface cannot run.
34
+ */
35
+ outcome: NextCallOutcome;
36
+ /** Material effects of running this, when the verb has any. */
37
+ effects?: NextCallEffects;
38
+ /** Who would run it, from the env — never a caller-chosen principal. */
39
+ workContext?: WorkContext;
40
+ /** Why a complete-looking call is still not runnable. */
41
+ hold?: NextCallHold;
25
42
  }
43
+ export type NextCallOutcome = 'ok' | 'pending' | 'partial' | 'unavailable';
44
+ export type NextCallHold = 'human_approval' | 'relationship';
45
+ export interface NextCallEffects {
46
+ disclosure?: string;
47
+ commitment?: string;
48
+ metering?: string;
49
+ relationship?: string;
50
+ }
51
+ export interface WorkContext {
52
+ actingAgentId?: string;
53
+ laneId?: string;
54
+ }
55
+ export interface NextCallOptions {
56
+ /** Live capability schema — required args come from here when present. */
57
+ definition?: Pick<CapabilityDefinition, 'params'>;
58
+ outcome?: NextCallOutcome;
59
+ effects?: NextCallEffects;
60
+ /**
61
+ * Args may be complete but the caller must not run it: a human signs, or
62
+ * a relationship is not live yet.
63
+ */
64
+ hold?: NextCallHold;
65
+ }
66
+ /** A persona face (`psn_…`) is display state, not a message address. */
67
+ export declare function isPersonaFace(id: unknown): boolean;
68
+ /** Acting agent and lane from the env. Never a caller-chosen principal. */
69
+ export declare function workContextFromEnv(env: CapabilityEnv): WorkContext | undefined;
26
70
  /**
27
71
  * Build a next step naming the tool as the calling surface registers it.
28
72
  *
@@ -31,7 +75,21 @@ export interface NextCall {
31
75
  * the 31 capability definitions follows that one rule, so the key is enough and
32
76
  * a caller cannot accidentally emit a name the surface does not have.
33
77
  */
34
- export declare function nextCall(env: CapabilityEnv, capabilityKey: string, args: Record<string, unknown> | undefined, why: string): NextCall;
78
+ export declare function nextCall(env: CapabilityEnv, capabilityKey: string, args: Record<string, unknown> | undefined, why: string, opts?: NextCallOptions): NextCall;
79
+ /**
80
+ * E23: messages, artifacts, and agreement prose are data. They cannot mint
81
+ * a server action. The only legal parse is "none".
82
+ */
83
+ export declare function nextCallsFromUntrustedContent(_content: unknown): NextCall[];
84
+ /**
85
+ * E22: a next call advertised against an earlier snapshot is stale when the
86
+ * row is no longer in the state that action assumed. Execution still has to
87
+ * hit the existing precondition (claim 400, respond refuse) — this is the
88
+ * client-side invalidation so a caller does not treat the suggestion as live.
89
+ */
90
+ export declare function isStaleAdvertisedAction(advertised: Pick<NextCall, 'tool' | 'args'>, current: {
91
+ status?: string | null;
92
+ }): boolean;
35
93
  /**
36
94
  * The other PRINCIPAL in a two-party agreement, from the perspective of
37
95
  * `selfId`.