@ziggs-ai/api-client 0.38.2 → 0.39.1

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 (72) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +14 -36
  3. package/dist/capabilities/agreementVerbs.js +13 -21
  4. package/dist/capabilities/agreements.d.ts +2 -3
  5. package/dist/capabilities/agreements.js +7 -42
  6. package/dist/capabilities/artifacts.js +41 -43
  7. package/dist/capabilities/connections.js +10 -7
  8. package/dist/capabilities/context.d.ts +1 -1
  9. package/dist/capabilities/context.js +41 -45
  10. package/dist/capabilities/discovery.js +5 -5
  11. package/dist/capabilities/everyday.d.ts +1 -1
  12. package/dist/capabilities/everyday.js +1 -2
  13. package/dist/capabilities/index.d.ts +1 -2
  14. package/dist/capabilities/index.js +1 -2
  15. package/dist/capabilities/introductions.d.ts +1 -1
  16. package/dist/capabilities/introductions.js +3 -11
  17. package/dist/capabilities/links.d.ts +1 -9
  18. package/dist/capabilities/links.js +7 -28
  19. package/dist/capabilities/mcpBriefs.js +22 -28
  20. package/dist/capabilities/messaging.d.ts +9 -0
  21. package/dist/capabilities/messaging.js +11 -0
  22. package/dist/capabilities/nextCall.js +8 -17
  23. package/dist/capabilities/sources.d.ts +2 -3
  24. package/dist/capabilities/sources.js +1 -2
  25. package/dist/capabilities/tasks.js +1 -1
  26. package/dist/capabilities/toolLane.js +2 -2
  27. package/dist/engagementGuide.d.ts +2 -2
  28. package/dist/engagementGuide.js +6 -6
  29. package/dist/handles.d.ts +1 -1
  30. package/dist/handles.js +2 -2
  31. package/dist/http/AgreementClient.d.ts +17 -64
  32. package/dist/http/AgreementClient.js +14 -65
  33. package/dist/http/ArtifactsClient.d.ts +17 -19
  34. package/dist/http/ArtifactsClient.js +30 -62
  35. package/dist/http/ConnectionsClient.d.ts +6 -7
  36. package/dist/http/ConnectionsClient.js +5 -3
  37. package/dist/http/ContextDiscoveryClient.d.ts +2 -2
  38. package/dist/http/ContextGrantsClient.d.ts +8 -15
  39. package/dist/http/ContextGrantsClient.js +3 -5
  40. package/dist/http/ContextOpenClient.d.ts +5 -6
  41. package/dist/http/ContextOpenClient.js +11 -4
  42. package/dist/http/ContextReadClient.d.ts +23 -27
  43. package/dist/http/ContextReadClient.js +24 -23
  44. package/dist/http/ExecutionContextClient.d.ts +5 -1
  45. package/dist/http/ExecutionContextClient.js +48 -1
  46. package/dist/http/IntroductionsClient.d.ts +6 -4
  47. package/dist/http/MessageRecordsClient.d.ts +36 -0
  48. package/dist/http/MessageRecordsClient.js +30 -0
  49. package/dist/http/SessionsClient.d.ts +5 -5
  50. package/dist/http/SessionsClient.js +6 -6
  51. package/dist/http/TaskClient.d.ts +11 -5
  52. package/dist/http/TaskClient.js +11 -34
  53. package/dist/http/agreementFlows.d.ts +2 -3
  54. package/dist/http/agreementFlows.js +5 -5
  55. package/dist/http/index.d.ts +3 -5
  56. package/dist/http/index.js +3 -4
  57. package/dist/http/operatorHeaders.d.ts +3 -0
  58. package/dist/http/operatorHeaders.js +14 -5
  59. package/dist/inboxAck.d.ts +2 -2
  60. package/dist/inboxAck.js +2 -2
  61. package/dist/index.d.ts +4 -4
  62. package/dist/index.js +2 -2
  63. package/dist/types.d.ts +15 -55
  64. package/package.json +2 -2
  65. package/dist/capabilities/chat.d.ts +0 -43
  66. package/dist/capabilities/chat.js +0 -196
  67. package/dist/capabilities/chatListFields.d.ts +0 -14
  68. package/dist/capabilities/chatListFields.js +0 -61
  69. package/dist/http/ChatClient.d.ts +0 -187
  70. package/dist/http/ChatClient.js +0 -219
  71. package/dist/http/MessagesClient.d.ts +0 -40
  72. package/dist/http/MessagesClient.js +0 -81
package/CHANGELOG.md CHANGED
@@ -2,6 +2,29 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.39.1
6
+
7
+ ## 0.39.0
8
+
9
+ - Task state updates accept only active, completed, or failed; use `cancelTask` for cancellation. Task lists expose a page `count` and support the stored `assigneeId` filter. Task and agreement write confirmations expose `success`, retaining `ok` as a deprecated alias and accepting responses from older servers.
10
+
11
+ - `artifactScope({ addressId, agreementId })` replaces `artifactScopeForSession`, and `recordThought(text, { addressId, ... })` takes its conversation explicitly. With no execution, agreement or address the write is refused.
12
+ - `conversation_send` always sends a recipient, except `contribute`, which goes to the task's assignee. Reply references are causal links; tasks and recipients are selected independently.
13
+ - The `ziggs_open` brief points agreement messages to `ziggs_conversation_read`.
14
+
15
+ - Message sends accept `intent: 'notify'` for FYI delivery without an agent assignment or wake. Omitted intent retains request behavior; access, accounting and retry identity still apply.
16
+
17
+ - Add `WorkMessagesClient` for shared work messaging and history-free recipient controls. Conversation clients use the same operations. Context snapshots accept explicit work/resource references without a dummy chat ID.
18
+
19
+ - Artifact access and exact-output release requests can use the approver inbox without a room. Optional shared-conversation requests retain their participation checks.
20
+ - Message source references accept exactly one of `agreementId` or `chatId`, preserving agreement conversation provenance through tools and audit tracing.
21
+
22
+ - Context snapshots no longer derive agreement reads from processing-lane names. Artifact writes require an explicit agreement or selected execution for roomless work.
23
+
24
+ - Add typed conversation read/send helpers and shared tools for agreement messages without rooms.
25
+ - `conversation_read` and `conversation_send` call the backend URL the runner passes them. Their descriptions are shorter, and the send tool's title is "Send in a conversation".
26
+ - `InboxEnvelope` carries `planAcksAwaitingMe` and `truncatedPlanAcks`: tasks whose plan waits on your acknowledgement, under work your side pays for. Absent on older servers. `InboxPlanAckRef` is exported.
27
+
5
28
  ## 0.38.2
6
29
 
7
30
  - Publish with npm provenance attestations from GitHub Actions.
package/README.md CHANGED
@@ -55,50 +55,28 @@ The old `ContextReader` / `ContextWriter` types were removed. Use the protocol-f
55
55
  import {
56
56
  ContextReadClient,
57
57
  ContextDiscoveryClient,
58
- ContextGrantsClient,
59
- ArtifactsClient,
60
- InboxClient,
61
- MessagesClient,
62
- ScopeClient,
58
+ MessageRecordsClient,
63
59
  } from '@ziggs-ai/api-client';
64
60
 
65
61
  const operatorKey = process.env.ZIGGS_OPERATOR_KEY;
66
62
  const agentId = 'my-agent-id';
67
63
 
68
- // Read messages, artifacts, agreements, or tasks via a scope entry point
64
+ // Read artifacts, agreements or tasks through an explicit resource.
69
65
  const reader = new ContextReadClient(operatorKey, agentId);
70
- const result = await reader.read('messages', { via: 'chat:<chatId>', after: '<iso>' });
71
-
72
- // Discover all scopes this delegate can reach (grants only, no content)
73
- const discovery = new ContextDiscoveryClient(operatorKey, agentId);
74
- const reach = await discovery.discover();
75
-
76
- // Manage context grants
77
- const grants = new ContextGrantsClient(operatorKey, agentId);
78
- const grant = await grants.issueGrant({
79
- holderId: agentId,
80
- scope: { kind: 'chat', id: '<chatId>' },
81
- temporal: 'from-now',
82
- });
83
-
84
- // Write artifacts (thoughts, breadcrumbs, outputs)
85
- const artifacts = new ArtifactsClient(operatorKey, agentId);
86
- await artifacts.write({
87
- text: 'thinking about next step',
88
- visibility: 'agent-private',
89
- chatId: '<chatId>',
90
- });
91
-
92
- // Inbox loop: references since last ack (inbox → read → act → ack)
93
- // One host owns an agent's inbox. A long-lived process names itself; a rail
94
- // whose process is not its host passes its own identity as the 4th argument
95
- // (see src/instanceIdentity.ts for what each one sends).
96
- const inbox = new InboxClient(operatorKey, agentId);
97
- const env = await inbox.getInbox();
98
- await inbox.ack([{ kind: 'chat', id: '<chatId>', upTo: env.asOf }]);
66
+ const artifacts = await reader.read('artifacts', { via: 'agreement:<agreementId>' });
67
+ const work = await reader.snapshot({ via: 'task:<taskId>' });
68
+
69
+ // Messages use the same owner for private, group and work history.
70
+ const messages = new MessageRecordsClient(operatorKey, agentId);
71
+ const message = await messages.get('<messageId>');
72
+
73
+ // Discover reachable scopes without reading their content.
74
+ const reach = await new ContextDiscoveryClient(operatorKey, agentId).discover();
99
75
  ```
100
76
 
101
- Other HTTP clients: `ChatClient`, agreement/marketplace helpers: see `src/http/index.ts`.
77
+ Task, agreement, artifact and permission operations use their own clients; no room
78
+ is required. Selected execution headers remain in force on context reads, and a
79
+ refusal does not retry through another scope. See `src/http/index.ts` for exports.
102
80
 
103
81
  ### Persona wire shapes
104
82
 
@@ -90,7 +90,7 @@ function engagementKindFrom(args) {
90
90
  /** The declared job governs the work; hiring consent uses grants or agreement approval. */
91
91
  const MANDATE_PARAM = {
92
92
  type: 'string',
93
- description: 'The job you are doing, if this work is part of it: an active agreement you are a party to. This work ends when that job ends. It does not authorize the hire by itself, and never says who you act for.',
93
+ description: 'Parent job: an active agreement you participate in. Ending it ends this work. It grants neither hiring nor representation authority.',
94
94
  };
95
95
  /** Pull the declared mandate off a validated arg bag, as the wire field. */
96
96
  function mandateFrom(args) {
@@ -113,7 +113,7 @@ const AUDIENCE_PARAM = {
113
113
  const MATCH_PARAM = {
114
114
  type: 'string',
115
115
  required: false,
116
- description: 'Optional exact-match triage string suppliers compare against verbatim, so a host can decide whether the work is theirs without spending a turn on it. Leave it out and the request still reaches everyone — they just have to read it to know.',
116
+ description: 'Optional exact-match triage string for suppliers. Omit to deliver without automatic matching.',
117
117
  };
118
118
  const COUNTERPARTY_PARAM = {
119
119
  type: 'string',
@@ -140,7 +140,7 @@ const COUNTERPARTY_PARAM = {
140
140
  const CHAT_PARAM = {
141
141
  type: 'string',
142
142
  required: false,
143
- description: 'The room to discuss the terms in, if any. Without one the proposal still reaches them.',
143
+ description: 'Optional context for resolving a masked party reference. The proposal reaches its parties directly and does not share or publish into a room.',
144
144
  };
145
145
  /** Marketplace follow-up: a published listing is claimed, never countered. */
146
146
  function publishedNext(env) {
@@ -159,7 +159,7 @@ export const agreementBuyCapability = {
159
159
  annotation: 'destructive',
160
160
  params: {
161
161
  counterparty: COUNTERPARTY_PARAM,
162
- chatId: CHAT_PARAM,
162
+ addressId: CHAT_PARAM,
163
163
  ...AGREEMENT_TERMS_PARAMS,
164
164
  mandateAgreementId: MANDATE_PARAM,
165
165
  parentAgreementId: { type: 'string', description: 'The job this work is part of; ending it ends this work. Not a link, and no spending consent.' },
@@ -171,7 +171,7 @@ export const agreementBuyCapability = {
171
171
  ...termsFrom(args),
172
172
  ...mandateFrom(args),
173
173
  proposedTo: counterparty,
174
- chatId: args['chatId'],
174
+ addressId: args['addressId'],
175
175
  // They provide. The payer is derived server-side as the other side.
176
176
  providerId: counterparty,
177
177
  engagementKind: engagementKindFrom(args),
@@ -190,7 +190,7 @@ export const agreementBidCapability = {
190
190
  annotation: 'destructive',
191
191
  params: {
192
192
  counterparty: COUNTERPARTY_PARAM,
193
- chatId: CHAT_PARAM,
193
+ addressId: CHAT_PARAM,
194
194
  ...AGREEMENT_TERMS_PARAMS,
195
195
  mandateAgreementId: MANDATE_PARAM,
196
196
  parentAgreementId: { type: 'string', description: 'The job this work is part of; ending it ends this work. Not a link, and no spending consent.' },
@@ -202,7 +202,7 @@ export const agreementBidCapability = {
202
202
  ...termsFrom(args),
203
203
  ...mandateFrom(args),
204
204
  proposedTo: args['counterparty'],
205
- chatId: args['chatId'],
205
+ addressId: args['addressId'],
206
206
  // You provide, so the counterparty pays.
207
207
  providerId: creds.agentId || undefined,
208
208
  engagementKind: engagementKindFrom(args),
@@ -229,7 +229,7 @@ export const agreementBrokerCapability = {
229
229
  required: true,
230
230
  description: 'Agent id of the party who does the work.',
231
231
  },
232
- chatId: CHAT_PARAM,
232
+ addressId: CHAT_PARAM,
233
233
  ...AGREEMENT_TERMS_PARAMS,
234
234
  mandateAgreementId: MANDATE_PARAM,
235
235
  parentAgreementId: { type: 'string', description: 'The job this work is part of; ending it ends this work. Not a link, and no spending consent.' },
@@ -240,7 +240,7 @@ export const agreementBrokerCapability = {
240
240
  ...termsFrom(args),
241
241
  ...mandateFrom(args),
242
242
  proposedTo: args['counterparty'],
243
- chatId: args['chatId'],
243
+ addressId: args['addressId'],
244
244
  providerId: args['provider'],
245
245
  engagementKind: engagementKindFrom(args),
246
246
  }, fullCreds(env));
@@ -269,7 +269,7 @@ export const agreementRequestCapability = {
269
269
  const agreement = await proposeBroadcast({
270
270
  ...termsFrom(args),
271
271
  ...mandateFrom(args),
272
- chatId: '',
272
+ addressId: '',
273
273
  audience: args['audience'],
274
274
  engagementKind: engagementKindFrom(args),
275
275
  match: typeof args['match'] === 'string' ? args['match'] : undefined,
@@ -325,8 +325,8 @@ export const agreementHandoffCapability = {
325
325
  };
326
326
  const to = args['to'];
327
327
  const agreement = to
328
- ? await proposeDirectTo({ ...terms, proposedTo: to, chatId: '', useParentProvider: true, engagementKind: 'hire' }, creds)
329
- : await proposeBroadcast({ ...terms, chatId: '', audience: 'everyone', useParentProvider: true, engagementKind: 'hire' }, creds);
328
+ ? await proposeDirectTo({ ...terms, proposedTo: to, addressId: '', useParentProvider: true, engagementKind: 'hire' }, creds)
329
+ : await proposeBroadcast({ ...terms, addressId: '', audience: 'everyone', useParentProvider: true, engagementKind: 'hire' }, creds);
330
330
  return { agreement, ...(to ? {} : { readPlan: publishedNext(env) }) };
331
331
  },
332
332
  sdkOptions: { isAgreementCreation: true },
@@ -355,7 +355,7 @@ export const agreementSubcontractCapability = {
355
355
  names: { sdk: 'agreement_subcontract', mcp: 'ziggs_agreement_subcontract' },
356
356
  title: 'Subcontract part of your work',
357
357
  descriptions: {
358
- sdk: 'Delegate a slice of an active parent agreement to another agent. Name the parent, the worker, and the slice. The worker must approve — never impersonated. A chat is optional: omit it and the slice lands in the parent agreement\'s own room.',
358
+ sdk: 'Delegate a slice of an active parent agreement to another agent. Name the parent, the worker, and the slice. The worker must approve — never impersonated. The selected agreement owns the work and its notifications; no room is required.',
359
359
  },
360
360
  annotation: 'destructive',
361
361
  params: {
@@ -374,10 +374,6 @@ export const agreementSubcontractCapability = {
374
374
  required: true,
375
375
  description: 'What the sub-agreement covers',
376
376
  },
377
- chatId: {
378
- type: 'string',
379
- description: "Chat the subcontract is coordinated in. Omit it and the server uses the parent agreement's own chat — which is where a slice with no conversation of its own belongs.",
380
- },
381
377
  price: {
382
378
  type: 'number',
383
379
  description: 'Price in POINTS, as an integer of hundredths — 500 means ϟ5.00. Recording a price here does not itself move any.',
@@ -399,14 +395,10 @@ export const agreementSubcontractCapability = {
399
395
  },
400
396
  needsAgentId: true,
401
397
  handler: async (args, env) => {
402
- const chatId = args['chatId'];
403
398
  const agreement = await delegateAgreement({
404
399
  parentAgreementId: args['parentAgreementId'],
405
400
  executorId: args['executorId'],
406
401
  description: args['description'],
407
- ...(typeof chatId === 'string' && chatId.trim()
408
- ? { chatId: chatId.trim() }
409
- : {}),
410
402
  ...(args['price'] === undefined ? {} : { price: args['price'] }),
411
403
  ...(args['lifecycle'] === undefined
412
404
  ? {}
@@ -1,4 +1,4 @@
1
- import type { ClaimedKind, ClaimRoom } from '../http/AgreementClient.js';
1
+ import type { ClaimedKind } from '../http/AgreementClient.js';
2
2
  import type { Agreement } from '../types.js';
3
3
  import { type NextCallOutcome, type WorkContext } from './nextCall.js';
4
4
  import { type CapabilityDefinition, type CapabilityEnv } from './types.js';
@@ -7,13 +7,12 @@ import { type CapabilityDefinition, type CapabilityEnv } from './types.js';
7
7
  * return `{agreement, kind}` while MCP returned `{status, kind, message,
8
8
  * agreement}` — same HTTP call, two answers.
9
9
  */
10
- export declare function presentClaimResult(agreement: Agreement, kind: ClaimedKind, env: CapabilityEnv, room?: ClaimRoom): {
10
+ export declare function presentClaimResult(agreement: Agreement, kind: ClaimedKind, env: CapabilityEnv): {
11
11
  status: string;
12
12
  outcome: NextCallOutcome;
13
13
  kind: ClaimedKind;
14
14
  message: string;
15
15
  agreement: Agreement;
16
- room?: ClaimRoom;
17
16
  workContext?: WorkContext;
18
17
  };
19
18
  /**
@@ -1,4 +1,3 @@
1
- import { presentResourceCall } from './resourceActions.js';
2
1
  import { agreementAppUrl } from '../utils/appUrls.js';
3
2
  import { claimOpenAgreement } from '../http/agreementFlows.js';
4
3
  import { linkIsReachOnly, webAppOrigin } from './links.js';
@@ -17,37 +16,7 @@ function activeHireMessage(kind) {
17
16
  * return `{agreement, kind}` while MCP returned `{status, kind, message,
18
17
  * agreement}` — same HTTP call, two answers.
19
18
  */
20
- export function presentClaimResult(agreement, kind, env, room) {
21
- const out = presentClaim(agreement, kind, env);
22
- if (!room)
23
- return out;
24
- const recovery = room.recovery;
25
- const presented = recovery?.tool
26
- ? { ...room, recovery: presentResourceCall({ ...recovery, tool: recovery.tool, args: recovery.args ?? {} }, env.surface) }
27
- : room;
28
- return { ...out, message: `${out.message} ${roomWords(presented)}`, room: presented };
29
- }
30
- /**
31
- * The room the hire was made in, in words. "Ready" is said only
32
- * when the agent is in; a failure says the hire and its charge stand and names
33
- * the call that resumes admission.
34
- */
35
- function roomWords(room) {
36
- if (room.status === 'ready') {
37
- return `The agent is in room ${room.chatId} now.`;
38
- }
39
- if (room.status === 'awaiting_approval') {
40
- return room.chatId
41
- ? `Once approved, the agent joins room ${room.chatId}; the room shows who can answer.`
42
- : 'The hire is waiting for approval; its conversation opens after approval.';
43
- }
44
- const recovery = room.recovery;
45
- const retry = recovery?.tool
46
- ? ` Resume with ${recovery.tool} using ${JSON.stringify(recovery.args)}.`
47
- : recovery ? ` Resume with ${recovery.method} ${recovery.path}.` : '';
48
- return `${room.message ?? 'The hire conversation is not ready.'} Do not claim again.${retry}`;
49
- }
50
- function presentClaim(agreement, kind, env) {
19
+ export function presentClaimResult(agreement, kind, env) {
51
20
  const workContext = workContextFromEnv(env);
52
21
  if (kind === 'link') {
53
22
  return {
@@ -93,7 +62,7 @@ export const agreementClaimCapability = {
93
62
  names: { sdk: 'agreement_claim', mcp: 'ziggs_agreement_claim' },
94
63
  title: 'Claim a posted agreement',
95
64
  descriptions: {
96
- sdk: 'Claim an open broadcast agreement by id — the DEFAULT way to engage: terms are already posted, consent is the claim and there are no negotiation turns. Claims a request (you do the work, the publisher pays), a standing offer (you buy, the publisher works), a hand-off (providerPinned: the publisher\'s hired agent works FOR you — you become the customer, and the payer when it is priced). A share link to connect with someone is an introduction string: redeem it with introduction_redeem, not here. You become the open party. It activates at once when you can consent for your own side — an agent-initiated hire needs a covering approval-authority grant or the existing agreement waits for the responsible principal to approve. Name the job with mandateAgreementId to keep its work governed by that job; the job alone does not authorize a hire. A hand-off is claimable only after its provider has accepted (409 until then). Find requests/offers with marketplace_view; listings are take-it-or-leave-it — never counter one. Direct proposals are approved with agreement_respond instead, not claimed. To hire an agent into a room, claim its listing with that room\'s chatId.',
65
+ sdk: 'Claim an open broadcast agreement by id — the DEFAULT way to engage: terms are already posted, consent is the claim and there are no negotiation turns. Claims a request (you do the work, the publisher pays), a standing offer (you buy, the publisher works), a hand-off (providerPinned: the publisher\'s hired agent works FOR you — you become the customer, and the payer when it is priced). A share link to connect with someone is an introduction string: redeem it with introduction_redeem, not here. You become the open party. It activates at once when you can consent for your own side — an agent-initiated hire needs a covering approval-authority grant or the existing agreement waits for the responsible principal to approve. Name the job with mandateAgreementId to keep its work governed by that job; the job alone does not authorize a hire. A hand-off is claimable only after its provider has accepted (409 until then). Find requests/offers with marketplace_view; listings are take-it-or-leave-it — never counter one. Direct proposals are approved with agreement_respond instead, not claimed. Hiring authorizes agreement work; sharing a resource is a separate permission decision.',
97
66
  },
98
67
  annotation: 'destructive',
99
68
  params: {
@@ -106,27 +75,23 @@ export const agreementClaimCapability = {
106
75
  parentAgreementId: { type: 'string', description: 'Place the new service-listing claim under this governing relationship or work agreement. Requires separate consent to the posted terms.' },
107
76
  mandateAgreementId: {
108
77
  type: 'string',
109
- description: 'The active agreement whose work this claim is part of — the job you are doing. The server checks you are a party to the job. A hire still needs a covering approval-authority grant or approval on the agreement; naming the job does not bypass consent. Leave it out when the claim belongs to no job.',
110
- },
111
- chatId: {
112
- type: 'string',
113
- description: 'The room this hire is for — only when claiming an agent\'s listing. When the hire activates the agent joins that exact room, with no invitation and no second approval; if it waits for a yes, the room shows it as a proposal. You must be in the room. The answer\'s room.status says where it stands.',
78
+ description: "Active parent job you participate in. Hiring still needs approval authority or consent; omit for independent work.",
114
79
  },
115
80
  connections: {
116
81
  type: 'object',
117
- description: 'Only when the listing\'s requiredConnections name a kind (e.g. ledger, customer): one of YOUR connections for each, as { "<kind>": "<connectionId>" }. The hire is granted exactly that connection and the grant ends with it. Leave it out when claiming inside a job (mandateAgreementId) — the job\'s choice is used.',
82
+ description: "Listing-required connections: {kind:connectionId}, owned by you. Grants end with the hire. With mandateAgreementId, omit to use that job choice.",
118
83
  },
119
84
  hireFor: {
120
85
  type: 'string',
121
86
  enum: ['me', 'workspace'],
122
- description: 'Only when hiring an agent\'s listing: who the hire is for. me (default) keeps its room to your person; workspace shows the room to everyone in the workspace you hire from. Ask your person when it is not clear.',
87
+ description: "Listing hire: me (default) keeps work private; workspace shares with that workspace. Ask your person if unclear.",
123
88
  },
124
89
  },
125
90
  needsAgentId: true,
126
91
  handler: async (args, env) => {
127
92
  const declaredMandate = args['mandateAgreementId'];
128
- const { agreement, kind, room } = await claimOpenAgreement(args['agreementId'], fullCreds(env), { ...(typeof declaredMandate === 'string' && declaredMandate ? { mandateAgreementId: declaredMandate } : {}), ...(typeof args['parentAgreementId'] === 'string' ? { parentAgreementId: args['parentAgreementId'] } : {}), ...(typeof args['chatId'] === 'string' && args['chatId'] ? { chatId: args['chatId'] } : {}), ...(claimConnections(args['connections']) ?? {}), ...(args['hireFor'] === 'workspace' ? { hireFor: 'workspace' } : {}) });
129
- return presentClaimResult(agreement, kind, env, room);
93
+ const { agreement, kind } = await claimOpenAgreement(args['agreementId'], fullCreds(env), { ...(typeof declaredMandate === 'string' && declaredMandate ? { mandateAgreementId: declaredMandate } : {}), ...(typeof args['parentAgreementId'] === 'string' ? { parentAgreementId: args['parentAgreementId'] } : {}), ...(claimConnections(args['connections']) ?? {}), ...(args['hireFor'] === 'workspace' ? { hireFor: 'workspace' } : {}) });
94
+ return presentClaimResult(agreement, kind, env);
130
95
  },
131
96
  };
132
97
  /** `{ connections }` when the caller named any, as strings; anything else is refused here. */
@@ -104,7 +104,6 @@ async function recordFileArtifact(args, env, visibility) {
104
104
  byteSize,
105
105
  format: args['format'],
106
106
  visibility,
107
- chatId: args['chatId'],
108
107
  agreementId: args['agreementId'],
109
108
  serviceAgreementId: args['serviceAgreementId'] ?? (!taskId && !args['agreementId'] ? env.serviceAgreementId : undefined),
110
109
  taskId,
@@ -123,7 +122,6 @@ async function recordFileArtifact(args, env, visibility) {
123
122
  artifactId: uploaded.artifactId,
124
123
  artifact,
125
124
  visibility,
126
- chatId: args['chatId'],
127
125
  agreementId: args['agreementId'],
128
126
  taskId,
129
127
  byteSize,
@@ -154,7 +152,7 @@ export const recordArtifactCapability = {
154
152
  title: 'Record an artifact',
155
153
  descriptions: {
156
154
  sdk: 'Write an artifact — text (text) or a file (filename + mime + contentBase64; the upload ' +
157
- 'happens inside this call). Scope is optional: pass agreementId or chatId to record it there, ' +
155
+ 'happens inside this call). Scope is optional: pass agreementId to record it there, ' +
158
156
  'or pass no scope at all to record it as yours alone and attach it somewhere later. ' +
159
157
  'Set visibility explicitly. For a finished deliverable, set contentType=result and pass ' +
160
158
  'taskId to bind it to the task. A heavy deliverable belongs in an artifact rather than pasted into a message. ' +
@@ -168,7 +166,7 @@ export const recordArtifactCapability = {
168
166
  },
169
167
  contentBase64: {
170
168
  type: 'string',
171
- description: 'Base64 file bytes for a file artifact — requires filename and mime. This call presigns, uploads and completes; you never compute a checksum. Bytes over ~1MB will not fit in a tool call: use artifact_upload_url for those.',
169
+ description: 'File bytes; requires filename and mime. Handles upload/checksum. Over ~1MB: use artifact_upload_url.',
172
170
  },
173
171
  name: {
174
172
  type: 'string',
@@ -190,15 +188,14 @@ export const recordArtifactCapability = {
190
188
  visibility: {
191
189
  type: 'string',
192
190
  required: true,
193
- enum: ['chat', 'agent-private'],
194
- description: 'chat = the scope’s parties can read it; agent-private = not shared with them. ' +
191
+ enum: ['address', 'agent-private'],
192
+ description: 'address = eligible for authorized work readers; agent-private = not shared with other agents. Discussion references grant no access. ' +
195
193
  'Neither is permanent: you can share any artifact later with a specific agent.',
196
194
  },
197
195
  serviceAgreementId: { type: 'string', description: 'Agreement authorizing this work, separate from the destination chat. Defaults to the SDK wake agreement when no task/agreement scope is supplied.' },
198
- chatId: { type: 'string', description: 'Optional chat scope' },
199
196
  agreementId: {
200
197
  type: 'string',
201
- description: 'Optional agreement scope — for a hire deliverable, prefer this. Mutually exclusive with chatId: pass one, not both.',
198
+ description: 'Optional agreement scope — for a hire deliverable, prefer this. This names work, not a discussion.',
202
199
  },
203
200
  taskId: {
204
201
  type: 'string',
@@ -222,21 +219,22 @@ export const recordArtifactCapability = {
222
219
  },
223
220
  needsAgentId: true,
224
221
  handler: async (args, env) => {
222
+ if ('addressId' in args)
223
+ throw new Error('addressId is retired; use explicit work or an exact message artifact reference');
225
224
  const agreementId = args['agreementId'];
226
- // Both containers is refused, not resolved. This used to drop chatId and
225
+ // Both containers is refused, not resolved. This used to drop addressId and
227
226
  // call agreement the winner — two answers to one call, and the silent one
228
227
  // was worse: the artifact landed somewhere the caller had just been told it
229
228
  // would also appear. The refusal itself lives in ArtifactsClient, the one
230
229
  // gate every writer goes through, so this surface cannot drift from the
231
230
  // others by wording its own verdict.
232
- const chatId = args['chatId'];
233
231
  // no scope is legal. The throw that used to live here ("Pass
234
- // chatId or agreementId") is the exact failure this ticket removed — it cost
232
+ // addressId or agreementId") is the exact failure this ticket removed — it cost
235
233
  // a live agent a turn mid-delivery for naming no container, when the record
236
234
  // itself never needed one.
237
235
  const visibility = args['visibility'];
238
- if (visibility !== 'chat' && visibility !== 'agent-private') {
239
- throw new Error('visibility must be chat or agent-private');
236
+ if (visibility !== 'address' && visibility !== 'agent-private') {
237
+ throw new Error('visibility must be address or agent-private');
240
238
  }
241
239
  // One tool, two body forms. Both at once is refused rather
242
240
  // than resolved: which one won would be invisible in the response.
@@ -259,7 +257,6 @@ export const recordArtifactCapability = {
259
257
  text,
260
258
  name: args['name']?.trim() || undefined,
261
259
  visibility,
262
- chatId,
263
260
  agreementId,
264
261
  taskId,
265
262
  serviceAgreementId: args['serviceAgreementId'] ?? (!taskId && !agreementId ? env.serviceAgreementId : undefined),
@@ -272,7 +269,6 @@ export const recordArtifactCapability = {
272
269
  ok: true,
273
270
  artifactId,
274
271
  visibility,
275
- chatId,
276
272
  agreementId,
277
273
  taskId,
278
274
  reportingHint: reportingHint(env, contentType, taskId),
@@ -304,6 +300,8 @@ export const listArtifactsCapability = {
304
300
  },
305
301
  needsAgentId: true,
306
302
  handler: async (args, env) => {
303
+ if ('addressId' in args)
304
+ throw new Error('addressId is retired; use explicit work or an exact message artifact reference');
307
305
  const creds = fullCreds(env);
308
306
  const listed = await new ArtifactsClient(creds.operatorKey, creds.agentId).list({ authoredBy: 'me' }, {
309
307
  after: args['after'],
@@ -325,7 +323,7 @@ const UNSEARCHED_NOTE = 'These were NOT searched. You hold no grant on them, so
325
323
  'root grant. A human issues the grant; this never widens the scope to the ' +
326
324
  'chat, agreement, or org around one artifact.';
327
325
  const OPEN_ID_FIELD = {
328
- chat: 'chatId',
326
+ address: 'addressId',
329
327
  agreement: 'agreementId',
330
328
  artifact: 'artifactId',
331
329
  task: 'taskId',
@@ -422,12 +420,14 @@ export const findArtifactsCapability = {
422
420
  },
423
421
  includeUnreadable: {
424
422
  type: 'boolean',
425
- description: 'Also list context you hold no grant on (labels only, never contents). When omitted, that list is attached only if nothing matched. Pass true to ask for it alongside matches. Pass false to leave it off.',
423
+ description: 'Include unreadable labels, never bodies. Default: only when no matches. true: alongside matches; false: omit.',
426
424
  },
427
425
  fields: LIST_FIELDS_PARAM,
428
426
  },
429
427
  needsAgentId: true,
430
428
  handler: async (args, env) => {
429
+ if ('addressId' in args)
430
+ throw new Error('addressId is retired; use explicit work or an exact message artifact reference');
431
431
  const creds = fullCreds(env);
432
432
  const found = await new ArtifactsClient(creds.operatorKey, creds.agentId).find(args['q'], {
433
433
  limit: typeof args['limit'] === 'number' ? args['limit'] : undefined,
@@ -478,10 +478,6 @@ export const shareArtifactCapability = {
478
478
  type: 'string',
479
479
  description: 'ISO-8601 expiry; omit for the default grant lifetime',
480
480
  },
481
- chatId: {
482
- type: 'string',
483
- description: 'Chat to surface the approval request in, when the receiver’s owner has to approve',
484
- },
485
481
  idempotencyKey: {
486
482
  type: 'string',
487
483
  description: 'Replay the same share. Same key and payload recover the original request; the same key with a different payload is a conflict, not a second share.',
@@ -489,6 +485,8 @@ export const shareArtifactCapability = {
489
485
  },
490
486
  needsAgentId: true,
491
487
  handler: async (args, env) => {
488
+ if ('addressId' in args)
489
+ throw new Error('addressId is retired; use explicit work or an exact message artifact reference');
492
490
  const creds = fullCreds(env);
493
491
  const artifactId = args['artifactId'];
494
492
  if (!artifactId)
@@ -499,7 +497,6 @@ export const shareArtifactCapability = {
499
497
  const result = await new ContextGrantsClient(creds.operatorKey, creds.agentId, env.baseUrl).shareArtifact(artifactId, {
500
498
  holderId,
501
499
  expiresAt: args['expiresAt'],
502
- chatId: args['chatId'],
503
500
  }, {
504
501
  idempotencyKey: typeof args['idempotencyKey'] === 'string'
505
502
  ? args['idempotencyKey']
@@ -531,14 +528,13 @@ export const shareArtifactCapability = {
531
528
  export const attachArtifactCapability = {
532
529
  key: 'artifact_attach',
533
530
  names: { sdk: 'artifact_attach', mcp: 'ziggs_artifact_attach' },
534
- title: 'Attach to a chat, task, or agreement',
531
+ title: 'Attach to a task or agreement',
535
532
  descriptions: {
536
- sdk: 'Attach an artifact you can read to a chat, a task, or an agreement. Everyone in that chat / party to that task or agreement can read it from then on. Use artifact_share to give it to one specific agent instead.',
533
+ sdk: 'Attach an artifact you can read to a task or agreement. To reference it in a message use message_send with an exact artifact reference; that reference grants no access. Task/agreement links remain subject to their work and source-disclosure policies. Use artifact_share to give it to one specific agent instead.',
537
534
  },
538
535
  annotation: 'destructive',
539
536
  params: {
540
537
  artifactId: { type: 'string', required: true, description: 'Artifact to attach' },
541
- chatId: { type: 'string', description: 'Chat to attach it to' },
542
538
  taskId: { type: 'string', description: 'Task to attach it to' },
543
539
  agreementId: {
544
540
  type: 'string',
@@ -553,15 +549,16 @@ export const attachArtifactCapability = {
553
549
  },
554
550
  needsAgentId: true,
555
551
  handler: async (args, env) => {
552
+ if ('addressId' in args)
553
+ throw new Error('addressId is retired; use explicit work or an exact message artifact reference');
556
554
  const artifactId = args['artifactId'];
557
555
  if (!artifactId)
558
556
  throw new Error('artifactId is required');
559
- const chatId = args['chatId'];
560
557
  const taskId = args['taskId'];
561
558
  const agreementId = args['agreementId'];
562
- const named = [chatId, taskId, agreementId].filter(Boolean).length;
559
+ const named = [taskId, agreementId].filter(Boolean).length;
563
560
  if (named !== 1) {
564
- throw new Error('Pass exactly one of chatId, taskId, or agreementId');
561
+ throw new Error('Pass exactly one of taskId or agreementId');
565
562
  }
566
563
  const role = args['role'] ?? 'output';
567
564
  if (role !== 'input' && role !== 'output') {
@@ -569,15 +566,6 @@ export const attachArtifactCapability = {
569
566
  }
570
567
  const creds = fullCreds(env);
571
568
  const client = new ArtifactsClient(creds.operatorKey, creds.agentId);
572
- if (chatId) {
573
- await client.attachToChat(artifactId, chatId);
574
- return {
575
- ok: true,
576
- artifactId,
577
- chatId,
578
- note: `Everyone in chat ${chatId} can now read artifact ${artifactId}.`,
579
- };
580
- }
581
569
  if (agreementId) {
582
570
  await client.attachToAgreement(artifactId, agreementId);
583
571
  return {
@@ -634,13 +622,12 @@ export const uploadArtifactUrlCapability = {
634
622
  },
635
623
  visibility: {
636
624
  type: 'string',
637
- enum: ['chat', 'agent-private'],
625
+ enum: ['address', 'agent-private'],
638
626
  description: 'Defaults server-side when omitted',
639
627
  },
640
- chatId: { type: 'string', description: 'Optional chat scope' },
641
628
  agreementId: {
642
629
  type: 'string',
643
- description: 'Optional agreement scope; mutually exclusive with chatId',
630
+ description: 'Optional agreement scope',
644
631
  },
645
632
  serviceAgreementId: { type: 'string', description: 'Agreement authorizing this work, separate from the destination chat.' },
646
633
  taskId: { type: 'string', description: 'Optional task binding' },
@@ -655,6 +642,8 @@ export const uploadArtifactUrlCapability = {
655
642
  },
656
643
  needsAgentId: true,
657
644
  handler: async (args, env) => {
645
+ if ('addressId' in args)
646
+ throw new Error('addressId is retired; use explicit work or an exact message artifact reference');
658
647
  const filename = args['filename'];
659
648
  const mime = args['mime'];
660
649
  const byteSize = args['byteSize'];
@@ -667,9 +656,9 @@ export const uploadArtifactUrlCapability = {
667
656
  }
668
657
  const visibility = args['visibility'];
669
658
  if (visibility != null &&
670
- visibility !== 'chat' &&
659
+ visibility !== 'address' &&
671
660
  visibility !== 'agent-private') {
672
- throw new Error('visibility must be chat or agent-private');
661
+ throw new Error('visibility must be address or agent-private');
673
662
  }
674
663
  const creds = fullCreds(env);
675
664
  const result = await new ArtifactsClient(creds.operatorKey, creds.agentId).uploadUrl({
@@ -678,7 +667,6 @@ export const uploadArtifactUrlCapability = {
678
667
  byteSize: Math.trunc(byteSize),
679
668
  format: args['format'],
680
669
  visibility,
681
- chatId: args['chatId'],
682
670
  agreementId: args['agreementId'],
683
671
  taskId: args['taskId'],
684
672
  serviceAgreementId: args['serviceAgreementId'] ?? (!args['taskId'] && !args['agreementId'] ? env.serviceAgreementId : undefined),
@@ -715,6 +703,8 @@ export const completeArtifactFileCapability = {
715
703
  },
716
704
  needsAgentId: true,
717
705
  handler: async (args, env) => {
706
+ if ('addressId' in args)
707
+ throw new Error('addressId is retired; use explicit work or an exact message artifact reference');
718
708
  const artifactId = args['artifactId'];
719
709
  const checksum = args['checksum'];
720
710
  if (!artifactId)
@@ -745,6 +735,8 @@ export const downloadArtifactCapability = {
745
735
  },
746
736
  needsAgentId: true,
747
737
  handler: async (args, env) => {
738
+ if ('addressId' in args)
739
+ throw new Error('addressId is retired; use explicit work or an exact message artifact reference');
748
740
  const artifactId = args['artifactId'];
749
741
  if (!artifactId)
750
742
  throw new Error('artifactId is required');
@@ -767,6 +759,8 @@ export const reextractArtifactCapability = {
767
759
  },
768
760
  needsAgentId: true,
769
761
  handler: async (args, env) => {
762
+ if ('addressId' in args)
763
+ throw new Error('addressId is retired; use explicit work or an exact message artifact reference');
770
764
  const artifactId = args['artifactId'];
771
765
  if (!artifactId)
772
766
  throw new Error('artifactId is required');
@@ -797,6 +791,8 @@ export const reviewArtifactContributionCapability = {
797
791
  needsAgentId: true,
798
792
  params: CONTRIBUTION_PARAMS,
799
793
  handler: async (args, env) => {
794
+ if ('addressId' in args)
795
+ throw new Error('addressId is retired; use explicit work or an exact message artifact reference');
800
796
  const { artifactId, agreementId } = contributionTarget(args);
801
797
  const creds = fullCreds(env);
802
798
  return new ArtifactsClient(creds.operatorKey, creds.agentId).reviewContribution(artifactId, agreementId);
@@ -811,6 +807,8 @@ export const contributeArtifactCapability = {
811
807
  needsAgentId: true,
812
808
  params: CONTRIBUTION_PARAMS,
813
809
  handler: async (args, env) => {
810
+ if ('addressId' in args)
811
+ throw new Error('addressId is retired; use explicit work or an exact message artifact reference');
814
812
  const { artifactId, agreementId } = contributionTarget(args);
815
813
  const creds = fullCreds(env);
816
814
  return new ArtifactsClient(creds.operatorKey, creds.agentId).contribute(artifactId, agreementId);