@ziggs-ai/api-client 0.37.28 → 0.38.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 (41) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/capabilities/artifacts.js +9 -9
  3. package/dist/capabilities/connections.js +3 -8
  4. package/dist/capabilities/context.js +15 -4
  5. package/dist/capabilities/grants.js +1 -4
  6. package/dist/capabilities/mcpBriefs.js +1 -1
  7. package/dist/capabilities/payments.js +2 -7
  8. package/dist/http/AgreementClient.d.ts +10 -2
  9. package/dist/http/AgreementClient.js +6 -4
  10. package/dist/http/ArtifactsClient.d.ts +2 -9
  11. package/dist/http/ArtifactsClient.js +3 -11
  12. package/dist/http/AssistantConnectionClient.d.ts +1 -1
  13. package/dist/http/AssistantConnectionClient.js +2 -2
  14. package/dist/http/ConnectionsClient.d.ts +2 -12
  15. package/dist/http/ConnectionsClient.js +4 -15
  16. package/dist/http/ContextGrantsClient.d.ts +1 -0
  17. package/dist/http/ContextGrantsClient.js +1 -1
  18. package/dist/http/ContextOpenClient.d.ts +2 -3
  19. package/dist/http/ContextOpenClient.js +2 -4
  20. package/dist/http/ContextReadClient.d.ts +5 -13
  21. package/dist/http/ContextReadClient.js +4 -19
  22. package/dist/http/ExecutionContextClient.d.ts +3 -0
  23. package/dist/http/ExecutionContextClient.js +17 -0
  24. package/dist/http/GrantsClient.d.ts +2 -10
  25. package/dist/http/GrantsClient.js +3 -12
  26. package/dist/http/InboxClient.d.ts +2 -2
  27. package/dist/http/InboxClient.js +1 -3
  28. package/dist/http/PaymentsClient.d.ts +2 -15
  29. package/dist/http/PaymentsClient.js +4 -18
  30. package/dist/http/TelemetryClient.js +3 -3
  31. package/dist/http/ToolPickClient.js +2 -2
  32. package/dist/http/index.d.ts +1 -1
  33. package/dist/http/index.js +1 -1
  34. package/dist/http/operatorHeaders.d.ts +14 -36
  35. package/dist/http/operatorHeaders.js +26 -50
  36. package/dist/inboxAck.d.ts +14 -46
  37. package/dist/inboxAck.js +12 -102
  38. package/dist/index.d.ts +3 -0
  39. package/dist/index.js +3 -0
  40. package/dist/types.d.ts +15 -16
  41. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.38.0
6
+
7
+ - `respondToAgreement` and `approveAgreementAsParty` accept `mandateAgreementId`: the hire whose setting lets an agent answer without a person. It is sent in the request body and never inferred from the selected execution.
8
+ - Clarify that connection work consent applies to every selected execution, including personal work.
9
+ - Document owner-approved `work_context` consent for connection discovery and proxy calls during a selected execution.
10
+
5
11
  ## 0.37.28
6
12
 
7
13
  - `SessionsClient` lists sibling assistant windows and holds a chat in this window (`GET/POST /agents/:id/sessions`). Used by MCP `ziggs_session_list` / `ziggs_session_hold`. Not on the directory connector surface.
@@ -92,7 +92,7 @@ async function recordFileArtifact(args, env, visibility) {
92
92
  // can be tens of megabytes.
93
93
  const bytes = decodeFileBytes(contentBase64);
94
94
  const creds = fullCreds(env);
95
- const client = new ArtifactsClient(creds.operatorKey, creds.agentId, creds.laneId);
95
+ const client = new ArtifactsClient(creds.operatorKey, creds.agentId);
96
96
  const taskId = args['taskId'];
97
97
  const byteSize = bytes.byteLength;
98
98
  const uploaded = await client.uploadUrl({
@@ -255,7 +255,7 @@ export const recordArtifactCapability = {
255
255
  const taskId = args['taskId'];
256
256
  const externalRef = parseExternalRef(args['externalRef']);
257
257
  const creds = fullCreds(env);
258
- const { artifactId } = await new ArtifactsClient(creds.operatorKey, creds.agentId, creds.laneId).writeStrict({
258
+ const { artifactId } = await new ArtifactsClient(creds.operatorKey, creds.agentId).writeStrict({
259
259
  text,
260
260
  name: args['name']?.trim() || undefined,
261
261
  visibility,
@@ -305,7 +305,7 @@ export const listArtifactsCapability = {
305
305
  needsAgentId: true,
306
306
  handler: async (args, env) => {
307
307
  const creds = fullCreds(env);
308
- const listed = await new ArtifactsClient(creds.operatorKey, creds.agentId, creds.laneId).list({ authoredBy: 'me' }, {
308
+ const listed = await new ArtifactsClient(creds.operatorKey, creds.agentId).list({ authoredBy: 'me' }, {
309
309
  after: args['after'],
310
310
  limit: typeof args['limit'] === 'number' ? args['limit'] : undefined,
311
311
  });
@@ -429,7 +429,7 @@ export const findArtifactsCapability = {
429
429
  needsAgentId: true,
430
430
  handler: async (args, env) => {
431
431
  const creds = fullCreds(env);
432
- const found = await new ArtifactsClient(creds.operatorKey, creds.agentId, creds.laneId).find(args['q'], {
432
+ const found = await new ArtifactsClient(creds.operatorKey, creds.agentId).find(args['q'], {
433
433
  limit: typeof args['limit'] === 'number' ? args['limit'] : undefined,
434
434
  before: args['before'],
435
435
  });
@@ -568,7 +568,7 @@ export const attachArtifactCapability = {
568
568
  throw new Error('role must be input or output');
569
569
  }
570
570
  const creds = fullCreds(env);
571
- const client = new ArtifactsClient(creds.operatorKey, creds.agentId, creds.laneId);
571
+ const client = new ArtifactsClient(creds.operatorKey, creds.agentId);
572
572
  if (chatId) {
573
573
  await client.attachToChat(artifactId, chatId);
574
574
  return {
@@ -672,7 +672,7 @@ export const uploadArtifactUrlCapability = {
672
672
  throw new Error('visibility must be chat or agent-private');
673
673
  }
674
674
  const creds = fullCreds(env);
675
- const result = await new ArtifactsClient(creds.operatorKey, creds.agentId, creds.laneId).uploadUrl({
675
+ const result = await new ArtifactsClient(creds.operatorKey, creds.agentId).uploadUrl({
676
676
  filename,
677
677
  mime,
678
678
  byteSize: Math.trunc(byteSize),
@@ -722,7 +722,7 @@ export const completeArtifactFileCapability = {
722
722
  if (!checksum)
723
723
  throw new Error('checksum is required');
724
724
  const creds = fullCreds(env);
725
- const artifact = await new ArtifactsClient(creds.operatorKey, creds.agentId, creds.laneId).completeFile(artifactId, checksum);
725
+ const artifact = await new ArtifactsClient(creds.operatorKey, creds.agentId).completeFile(artifactId, checksum);
726
726
  return {
727
727
  ok: true,
728
728
  artifact,
@@ -749,7 +749,7 @@ export const downloadArtifactCapability = {
749
749
  if (!artifactId)
750
750
  throw new Error('artifactId is required');
751
751
  const creds = fullCreds(env);
752
- const out = await new ArtifactsClient(creds.operatorKey, creds.agentId, creds.laneId).download(artifactId);
752
+ const out = await new ArtifactsClient(creds.operatorKey, creds.agentId).download(artifactId);
753
753
  return { ok: true, ...out };
754
754
  },
755
755
  };
@@ -771,7 +771,7 @@ export const reextractArtifactCapability = {
771
771
  if (!artifactId)
772
772
  throw new Error('artifactId is required');
773
773
  const creds = fullCreds(env);
774
- const artifact = await new ArtifactsClient(creds.operatorKey, creds.agentId, creds.laneId).reExtract(artifactId);
774
+ const artifact = await new ArtifactsClient(creds.operatorKey, creds.agentId).reExtract(artifactId);
775
775
  return { ok: true, artifact };
776
776
  },
777
777
  };
@@ -1,15 +1,10 @@
1
1
  import { ConnectionsClient } from "../http/ConnectionsClient.js";
2
2
  import { rethrowWithContext, } from "./types.js";
3
3
  function client(env) {
4
- // laneId, not just the key and the agent. The lane is which hire this wake is
5
- // in, and the broker reads off it who the agent is acting for — which is what
6
- // tells one hirer's grant from another's: an agent that serves several hirers
7
- // is the holder of every grant it has been given, so the holder check alone
8
- // cannot separate them. Every other Creds-based client already carries it.
9
- const { operatorKey, agentId, laneId } = env.creds;
4
+ const { operatorKey, agentId } = env.creds;
10
5
  if (!operatorKey)
11
6
  throw new Error("operatorKey missing from tool context");
12
- return new ConnectionsClient(operatorKey, agentId, env.baseUrl, laneId);
7
+ return new ConnectionsClient(operatorKey, agentId, env.baseUrl);
13
8
  }
14
9
  export const connectionProxyCapability = {
15
10
  key: "connection_proxy",
@@ -17,7 +12,7 @@ export const connectionProxyCapability = {
17
12
  title: "Use a stored connection",
18
13
  descriptions: {
19
14
  sdk: "Use a named-connector stored connection (e.g. the owner's GitHub/Jira) without ever seeing the credential. " +
20
- "Calls the backend connections proxy with a grant the owner issued to this agent. " +
15
+ "Calls the backend connections proxy with a grant the owner issued to this agent. In selected work, the grant must also carry owner-approved work_context consent for that work and its readers, or be the grant the hire itself minted for it. Keep the execution context on discovery and calls. " +
21
16
  'Not for remote MCP servers (provider "mcp") — those use mcp_tool_call / mcp_tools_list; proxy refuses them with "Unknown provider: mcp". ' +
22
17
  "Don't know connectionId/grantId yet? Use grant_list (scopeKind [\"connection\"]) or connection_list_grants first.",
23
18
  },
@@ -119,7 +119,7 @@ export const contextReadCapability = {
119
119
  if (direction !== undefined && direction !== 'forward' && direction !== 'backward') {
120
120
  throw new Error('direction must be "forward" or "backward"');
121
121
  }
122
- return new ContextReadClient(creds.operatorKey, creds.agentId, undefined, creds.laneId).read(type, {
122
+ return new ContextReadClient(creds.operatorKey, creds.agentId, undefined).read(type, {
123
123
  via,
124
124
  agreementId: args['agreementId'],
125
125
  cursor: args['cursor'],
@@ -307,7 +307,7 @@ export async function explainRefusedRead(kind, id, env) {
307
307
  const creds = fullCreds(env);
308
308
  let presented;
309
309
  try {
310
- const client = new ContextOpenClient(creds.operatorKey, creds.agentId, env.baseUrl, creds.laneId);
310
+ const client = new ContextOpenClient(creds.operatorKey, creds.agentId, env.baseUrl);
311
311
  presented = presentOpenResult(await client.explain({ [`${kind}Id`]: id }), env);
312
312
  }
313
313
  catch {
@@ -348,7 +348,7 @@ export const openCapability = {
348
348
  needsAgentId: true,
349
349
  handler: async (args, env) => {
350
350
  const creds = fullCreds(env);
351
- const client = new ContextOpenClient(creds.operatorKey, creds.agentId, env.baseUrl, creds.laneId);
351
+ const client = new ContextOpenClient(creds.operatorKey, creds.agentId, env.baseUrl);
352
352
  return presentOpenResult(await client.open(args), env);
353
353
  },
354
354
  };
@@ -364,7 +364,7 @@ export const accessExplainCapability = {
364
364
  needsAgentId: true,
365
365
  handler: async (args, env) => {
366
366
  const creds = fullCreds(env);
367
- const client = new ContextOpenClient(creds.operatorKey, creds.agentId, env.baseUrl, creds.laneId);
367
+ const client = new ContextOpenClient(creds.operatorKey, creds.agentId, env.baseUrl);
368
368
  const result = await client.explain(args);
369
369
  return presentOpenResult(result, env);
370
370
  },
@@ -418,6 +418,9 @@ export const accessRequestCapability = {
418
418
  // comes back refused with the people this agent may ask. Required in the
419
419
  // schema, a host refused the call before that list could arrive.
420
420
  recipientId: { type: 'string', description: 'Who to ask: workBrief.actingFor.customer.id (the customer of this hire) or workBrief.actingFor.operator.id (your operator). Omit when only one approver is available; otherwise the response lists choices.' },
421
+ contributionTo: {
422
+ type: 'object', description: 'Artifact only: { kind: "chat" | "agreement", id }. Ask its owner to approve use in this shared work. Name all destination readers and yourself in holders. Existing read access does not replace this consent.',
423
+ },
421
424
  holders: {
422
425
  type: 'array',
423
426
  description: 'Artifact only, read only: up to 20 readers, each { kind: "agent" | "user" | "org", id }. Leave yourself out to arrange it for others. Omit to ask for yourself.',
@@ -461,6 +464,13 @@ export const accessRequestCapability = {
461
464
  const holders = args.holders === undefined ? undefined : readersOf(args.holders);
462
465
  if (holders && (scopeKind !== 'artifact' || agreementId || permission !== 'read'))
463
466
  throw new Error('holders is for one artifact named by scopeId, to read');
467
+ let contributionTo;
468
+ if (args.contributionTo !== undefined) {
469
+ const target = args.contributionTo;
470
+ if (!target || !['chat', 'agreement'].includes(String(target.kind)) || typeof target.id !== 'string' || !target.id.trim() || !holders || scopeKind !== 'artifact' || agreementId || permission !== 'read')
471
+ throw new Error('contributionTo requires an artifact, named readers, and a chat or agreement destination');
472
+ contributionTo = { kind: target.kind, id: target.id.trim() };
473
+ }
464
474
  const creds = fullCreds(env);
465
475
  const result = await new ContextGrantsClient(creds.operatorKey, creds.agentId, env.baseUrl).requestAccess({
466
476
  scopeKind, scopeId, agreementId, reason: String(args.reason).trim(),
@@ -468,6 +478,7 @@ export const accessRequestCapability = {
468
478
  durationHours, chatId: args.chatId,
469
479
  recipientId: typeof args.recipientId === 'string' ? args.recipientId.trim() : undefined,
470
480
  ...(holders ? { holders } : {}),
481
+ ...(contributionTo ? { contributionTo } : {}),
471
482
  }, { idempotencyKey: args.idempotencyKey, laneId: creds.laneId });
472
483
  const actingAgentId = result.actingAgentId ?? (creds.agentId || undefined);
473
484
  if (result.status === 'already_readable') {
@@ -81,10 +81,7 @@ export const listGrantsCapability = {
81
81
  if (roleArg !== undefined && roleArg !== 'holder' && roleArg !== 'issuer') {
82
82
  throw new Error('role must be holder or issuer');
83
83
  }
84
- const { items, nextCursor, unreadableRails } = await new GrantsClient(creds.operatorKey, creds.agentId, env.baseUrl,
85
- // The lane, so the server answers with the grants THIS wake may spend
86
- // rather than everything the agent holds across every customer.
87
- creds.laneId).listGrants({
84
+ const { items, nextCursor, unreadableRails } = await new GrantsClient(creds.operatorKey, creds.agentId, env.baseUrl).listGrants({
88
85
  scopeKind,
89
86
  scopeId: typeof args['scopeId'] === 'string' ? args['scopeId'] : undefined,
90
87
  role: roleArg,
@@ -45,7 +45,7 @@ const BRIEFS = {
45
45
  next: 'ziggs_inbox_ack after handling',
46
46
  },
47
47
  ziggs_inbox_ack: {
48
- does: 'Hands back handled deliveries: ackTo exactly as received, plus handledResourceIds for each one assigned to you (empty is safe when none is).',
48
+ does: 'Hands back handled deliveries: ackTo exactly as received, plus handledDeliveryIds for each one assigned to you (empty is safe when none is).',
49
49
  changes: 'moves the watermark; an older ackTo is a no-op; skipping your rows is refused (INBOX_ACK_UNHANDLED)',
50
50
  },
51
51
  ziggs_inbox_defer: {
@@ -1,15 +1,10 @@
1
1
  import { PaymentsClient } from '../http/PaymentsClient.js';
2
2
  import { rethrowWithContext, } from './types.js';
3
3
  function client(env) {
4
- // laneId too, like every other Creds-based client. Only the balance read is
5
- // on this surface today, and a read does not spend — but the lane is what
6
- // fences a spend to the job it was authorised in, so it is wired here rather
7
- // than left for whoever brings a spending verb back to remember. Omitting it
8
- // can only leave a spend unfenced; it can never widen one.
9
- const { operatorKey, agentId, laneId } = env.creds;
4
+ const { operatorKey, agentId } = env.creds;
10
5
  if (!operatorKey)
11
6
  throw new Error('operatorKey missing from tool context');
12
- return new PaymentsClient(operatorKey, agentId, env.baseUrl, laneId);
7
+ return new PaymentsClient(operatorKey, agentId, env.baseUrl);
13
8
  }
14
9
  /**
15
10
  * Reading the balance is the whole rail on an agent surface.
@@ -161,6 +161,8 @@ export declare function respondToAgreement(agreementId: string, action: 'approve
161
161
  agreement?: Agreement | null;
162
162
  /** Where to send the human when the decision is theirs. */
163
163
  appUrl?: string;
164
+ /** The hire whose setting lets this agent answer without a person. */
165
+ mandateAgreementId?: string;
164
166
  }): Promise<Agreement>;
165
167
  /**
166
168
  * The approval facts a "who decides this?" question is answered from: the party
@@ -202,7 +204,11 @@ export declare function resolveMyPendingApprovalPartyId(agreement: Agreement, op
202
204
  * approval authority even for their own slot; the server rechecks it on PUT.
203
205
  * Canonical for hire, service, link, and request proposals.
204
206
  */
205
- export declare function approveAgreementAsParty(agreementId: string, partyId: string, status: 'approved' | 'rejected', creds: Creds): Promise<Agreement>;
207
+ export declare function approveAgreementAsParty(agreementId: string, partyId: string, status: 'approved' | 'rejected', creds: Creds,
208
+ /** The hire whose setting lets this agent answer without a person. Named, never inferred from the execution. */
209
+ opts?: {
210
+ mandateAgreementId?: string;
211
+ }): Promise<Agreement>;
206
212
  export interface CounterAgreementData {
207
213
  price?: number;
208
214
  agreementDescription?: string;
@@ -392,7 +398,9 @@ export declare class AgreementClient {
392
398
  proposeDirect(data: ProposeDirectInput): Promise<Agreement>;
393
399
  proposeBroadcast(data: ProposeBroadcastInput): Promise<Agreement>;
394
400
  delegate(data: DelegateAgreementData): Promise<Agreement>;
395
- respond(id: string, action: 'approve' | 'reject'): Promise<Agreement>;
401
+ respond(id: string, action: 'approve' | 'reject', opts?: {
402
+ mandateAgreementId?: string;
403
+ }): Promise<Agreement>;
396
404
  approveAsParty(id: string, partyId: string, status: 'approved' | 'rejected'): Promise<Agreement>;
397
405
  counter(id: string, data: CounterAgreementData): Promise<Agreement>;
398
406
  getStatus(id: string): Promise<unknown>;
@@ -255,7 +255,7 @@ export async function respondToAgreement(agreementId, action, creds, opts = {})
255
255
  // If the slot is the principal's, the server accepts
256
256
  // only under a live approval-authority grant (else 403). Do not pre-refuse
257
257
  // here; the PUT is the source of truth.
258
- return approveAgreementAsParty(agreementId, partyId, action === 'approve' ? 'approved' : 'rejected', creds);
258
+ return approveAgreementAsParty(agreementId, partyId, action === 'approve' ? 'approved' : 'rejected', creds, opts.mandateAgreementId ? { mandateAgreementId: opts.mandateAgreementId } : {});
259
259
  }
260
260
  /**
261
261
  * Which of `candidateIds` holds the pending approval slot, first match wins —
@@ -298,7 +298,9 @@ export function resolveMyPendingApprovalPartyId(agreement, opts) {
298
298
  * approval authority even for their own slot; the server rechecks it on PUT.
299
299
  * Canonical for hire, service, link, and request proposals.
300
300
  */
301
- export async function approveAgreementAsParty(agreementId, partyId, status, creds) {
301
+ export async function approveAgreementAsParty(agreementId, partyId, status, creds,
302
+ /** The hire whose setting lets this agent answer without a person. Named, never inferred from the execution. */
303
+ opts = {}) {
302
304
  if (!agreementId)
303
305
  throw new Error('agreementId is required for approval');
304
306
  if (!partyId)
@@ -307,7 +309,7 @@ export async function approveAgreementAsParty(agreementId, partyId, status, cred
307
309
  const res = await fetch(`${getAgreementBaseUrl()}/${encodeURIComponent(agreementId)}/approvals/${encodeURIComponent(partyId)}`, {
308
310
  method: 'PUT',
309
311
  headers: buildHeaders(creds),
310
- body: JSON.stringify({ status }),
312
+ body: JSON.stringify({ status, ...(opts.mandateAgreementId ? { mandateAgreementId: opts.mandateAgreementId } : {}) }),
311
313
  });
312
314
  if (!res.ok) {
313
315
  const body = await res.text().catch(() => '');
@@ -688,7 +690,7 @@ export class AgreementClient {
688
690
  proposeDirect(data) { return proposeDirectTo(data, this.creds); }
689
691
  proposeBroadcast(data) { return proposeBroadcast(data, this.creds); }
690
692
  delegate(data) { return delegateAgreement(data, this.creds); }
691
- respond(id, action) { return respondToAgreement(id, action, this.creds); }
693
+ respond(id, action, opts = {}) { return respondToAgreement(id, action, this.creds, opts); }
692
694
  approveAsParty(id, partyId, status) { return approveAgreementAsParty(id, partyId, status, this.creds); }
693
695
  counter(id, data) { return counterAgreement(id, data, this.creds); }
694
696
  getStatus(id) { return getAgreementStatus(id, this.creds); }
@@ -146,15 +146,8 @@ export declare function artifactScopeForSession(sessionId: string): {
146
146
  export declare class ArtifactsClient {
147
147
  private readonly operatorKey;
148
148
  private readonly agentId?;
149
- private readonly laneId?;
150
- /**
151
- * @param operatorKey Agent-scoped or fleet operator key.
152
- * @param agentId Required for fleet keys (sent as X-Agent-Id). Omit for agent-scoped keys.
153
- * @param laneId the wake's lane (chat id, or `agrn-<agreementId>`),
154
- * sent as X-Ziggs-Lane so `authoredBy=me` is fenced to this engagement
155
- * rather than spanning every engagement the agent has authored in.
156
- */
157
- constructor(operatorKey: string, agentId?: string, laneId?: string);
149
+ /** Authority is supplied by withWorkExecution / withWorkExecutionId. */
150
+ constructor(operatorKey: string, agentId?: string);
158
151
  list(q: ListArtifactsQuery, opts?: ListArtifactsOptions): Promise<ListArtifactsResult>;
159
152
  /**
160
153
  * `GET /artifacts/find` — lexical search over the artifacts this agent can
@@ -52,20 +52,12 @@ export function artifactScopeForSession(sessionId) {
52
52
  export class ArtifactsClient {
53
53
  operatorKey;
54
54
  agentId;
55
- laneId;
56
- /**
57
- * @param operatorKey Agent-scoped or fleet operator key.
58
- * @param agentId Required for fleet keys (sent as X-Agent-Id). Omit for agent-scoped keys.
59
- * @param laneId the wake's lane (chat id, or `agrn-<agreementId>`),
60
- * sent as X-Ziggs-Lane so `authoredBy=me` is fenced to this engagement
61
- * rather than spanning every engagement the agent has authored in.
62
- */
63
- constructor(operatorKey, agentId, laneId) {
55
+ /** Authority is supplied by withWorkExecution / withWorkExecutionId. */
56
+ constructor(operatorKey, agentId) {
64
57
  if (!operatorKey)
65
58
  throw new Error('ArtifactsClient: operatorKey is required');
66
59
  this.operatorKey = operatorKey;
67
60
  this.agentId = agentId;
68
- this.laneId = laneId;
69
61
  }
70
62
  async list(q, opts = {}) {
71
63
  const selectors = [q.chatId, q.agreementId, q.taskId, q.authoredBy].filter(Boolean);
@@ -364,6 +356,6 @@ export class ArtifactsClient {
364
356
  }
365
357
  }
366
358
  _headers() {
367
- return buildOperatorHeaders(this.operatorKey, this.agentId, { 'content-type': 'application/json' }, this.laneId);
359
+ return buildOperatorHeaders(this.operatorKey, this.agentId, { 'content-type': 'application/json' });
368
360
  }
369
361
  }
@@ -55,7 +55,7 @@ export declare class AssistantConnectionClient {
55
55
  getChanges(options?: InboxChangesOptions): Promise<InboxChangesResult>;
56
56
  /** Receipt diagnostics only: this does not subscribe, renew or wake a host. */
57
57
  eventStatus(orgId: string, signal?: AbortSignal): Promise<McpEventStatus>;
58
- ack(upTo: string, handledResourceIds?: string[]): Promise<unknown>;
58
+ ack(upTo: string, handledDeliveryIds?: string[]): Promise<unknown>;
59
59
  park(agreementId: string): Promise<{
60
60
  agreementId: string;
61
61
  parked: true;
@@ -60,8 +60,8 @@ export class AssistantConnectionClient {
60
60
  eventStatus(orgId, signal) {
61
61
  return this.request(`events/status?${new URLSearchParams({ orgId })}`, undefined, signal);
62
62
  }
63
- ack(upTo, handledResourceIds = []) {
64
- return this.request('inbox/ack', { upTo, handledResourceIds });
63
+ ack(upTo, handledDeliveryIds = []) {
64
+ return this.request('inbox/ack', { upTo, handledDeliveryIds });
65
65
  }
66
66
  park(agreementId) {
67
67
  return this.request('inbox/park', { agreementId });
@@ -62,18 +62,8 @@ export declare class ConnectionsClient {
62
62
  private readonly operatorKey;
63
63
  private readonly agentId?;
64
64
  private readonly baseUrl;
65
- private readonly laneId?;
66
- /**
67
- * @param laneId the wake's lane (a chat id, or `agrn-<agreementId>`), sent as
68
- * X-Ziggs-Lane. It tells the broker which hire this wake is in, and the
69
- * broker reads off it WHO the agent is acting for: an agent that serves
70
- * several hirers holds all of their grants at once and is the holder of
71
- * every one, so without a lane one hirer's grant is indistinguishable from
72
- * another's, and a grant one customer gave can be spent while working for
73
- * somebody else.
74
- * ArtifactsClient sends the same header for the same reason.
75
- */
76
- constructor(operatorKey: string, agentId?: string, baseUrl?: string, laneId?: string);
65
+ /** Authority is supplied by withWorkExecution / withWorkExecutionId. */
66
+ constructor(operatorKey: string, agentId?: string, baseUrl?: string);
77
67
  /**
78
68
  * Present a ConnectionGrant to the broker and perform a provider action.
79
69
  * Returns the provider result — never the raw OAuth token. Requires an
@@ -35,24 +35,13 @@ export class ConnectionsClient {
35
35
  operatorKey;
36
36
  agentId;
37
37
  baseUrl;
38
- laneId;
39
- /**
40
- * @param laneId the wake's lane (a chat id, or `agrn-<agreementId>`), sent as
41
- * X-Ziggs-Lane. It tells the broker which hire this wake is in, and the
42
- * broker reads off it WHO the agent is acting for: an agent that serves
43
- * several hirers holds all of their grants at once and is the holder of
44
- * every one, so without a lane one hirer's grant is indistinguishable from
45
- * another's, and a grant one customer gave can be spent while working for
46
- * somebody else.
47
- * ArtifactsClient sends the same header for the same reason.
48
- */
49
- constructor(operatorKey, agentId, baseUrl, laneId) {
38
+ /** Authority is supplied by withWorkExecution / withWorkExecutionId. */
39
+ constructor(operatorKey, agentId, baseUrl) {
50
40
  if (!operatorKey)
51
41
  throw new Error("ConnectionsClient: operatorKey is required");
52
42
  this.operatorKey = operatorKey;
53
43
  this.agentId = agentId;
54
44
  this.baseUrl = baseUrl || getBackendUrl();
55
- this.laneId = laneId;
56
45
  }
57
46
  /**
58
47
  * Present a ConnectionGrant to the broker and perform a provider action.
@@ -104,7 +93,7 @@ export class ConnectionsClient {
104
93
  * defensively for leaked secrets, as `proxy` does.
105
94
  */
106
95
  async listForHolder() {
107
- const grantsClient = new GrantsClient(this.operatorKey, this.agentId, this.baseUrl, this.laneId);
96
+ const grantsClient = new GrantsClient(this.operatorKey, this.agentId, this.baseUrl);
108
97
  // All pages of the agent's live connection grants (not just the first page).
109
98
  const { items, unreadableRails } = await grantsClient.listAllGrantsWithRails({
110
99
  scopeKind: "connection",
@@ -188,7 +177,7 @@ export class ConnectionsClient {
188
177
  headers: buildOperatorHeaders(this.operatorKey, this.agentId, {
189
178
  ...(body !== undefined ? { "content-type": "application/json" } : {}),
190
179
  ...extraHeaders,
191
- }, this.laneId),
180
+ }),
192
181
  };
193
182
  if (body !== undefined)
194
183
  init.body = JSON.stringify(body);
@@ -111,6 +111,7 @@ export declare class ContextGrantsClient {
111
111
  recipientId?: string;
112
112
  /** Artifact only: who should read it, if not only the caller. One grant each. */
113
113
  holders?: AccessReader[];
114
+ contributionTo?: import('@ziggs-ai/contracts').ContributionTarget;
114
115
  }, opts?: {
115
116
  idempotencyKey?: string;
116
117
  laneId?: string;
@@ -70,7 +70,7 @@ export class ContextGrantsClient {
70
70
  headers: buildOperatorHeaders(this.operatorKey, this.agentId, {
71
71
  'content-type': 'application/json',
72
72
  ...(opts?.idempotencyKey ? { 'Idempotency-Key': opts.idempotencyKey } : {}),
73
- }, opts?.laneId),
73
+ }),
74
74
  body: JSON.stringify(input),
75
75
  });
76
76
  const body = await res.text().catch(() => '');
@@ -67,7 +67,7 @@ export interface ContextOpenResult {
67
67
  ref: ContextOpenRef;
68
68
  access: ContextOpenAccess;
69
69
  workContext: {
70
- laneId: string | null;
70
+ executionContextId: string | null;
71
71
  };
72
72
  page?: {
73
73
  type: string;
@@ -90,8 +90,7 @@ export declare class ContextOpenClient {
90
90
  private readonly operatorKey;
91
91
  private readonly agentId?;
92
92
  private readonly baseUrl?;
93
- private readonly laneId?;
94
- constructor(operatorKey: string, agentId?: string | undefined, baseUrl?: string | undefined, laneId?: string | undefined);
93
+ constructor(operatorKey: string, agentId?: string | undefined, baseUrl?: string | undefined);
95
94
  open(body: ContextOpenBody): Promise<ContextOpenResult>;
96
95
  explain(body: ContextOpenBody): Promise<ContextExplainResult>;
97
96
  private post;
@@ -16,12 +16,10 @@ export class ContextOpenClient {
16
16
  operatorKey;
17
17
  agentId;
18
18
  baseUrl;
19
- laneId;
20
- constructor(operatorKey, agentId, baseUrl, laneId) {
19
+ constructor(operatorKey, agentId, baseUrl) {
21
20
  this.operatorKey = operatorKey;
22
21
  this.agentId = agentId;
23
22
  this.baseUrl = baseUrl;
24
- this.laneId = laneId;
25
23
  if (!operatorKey)
26
24
  throw new Error('ContextOpenClient: operatorKey is required');
27
25
  }
@@ -37,7 +35,7 @@ export class ContextOpenClient {
37
35
  const res = await fetch(url, {
38
36
  method: 'POST',
39
37
  headers: {
40
- ...buildOperatorHeaders(this.operatorKey, this.agentId, { 'Content-Type': 'application/json' }, this.laneId),
38
+ ...buildOperatorHeaders(this.operatorKey, this.agentId, { 'Content-Type': 'application/json' }),
41
39
  },
42
40
  body: JSON.stringify(normalized),
43
41
  });
@@ -58,7 +58,9 @@ export interface ContextSnapshotParticipant {
58
58
  }
59
59
  export interface ContextSnapshotResult {
60
60
  /** Server-resolved orientation; writes recheck authority. */
61
- workBrief?: Record<string, unknown>;
61
+ workBrief?: Record<string, unknown> & {
62
+ position?: import('@ziggs-ai/contracts').WorkPositionView;
63
+ };
62
64
  work?: Record<string, unknown>;
63
65
  history: unknown[];
64
66
  agreements: unknown[];
@@ -95,18 +97,8 @@ export declare class ContextReadClient {
95
97
  private readonly operatorKey;
96
98
  private readonly agentId?;
97
99
  private readonly baseUrl;
98
- private readonly laneId?;
99
- /**
100
- * @param operatorKey Agent-scoped or fleet operator key.
101
- * @param agentId Required for fleet keys (sent as X-Agent-Id). Omit for agent-scoped keys.
102
- * @param laneId the wake's lane, sent as X-Ziggs-Lane. The server fences a
103
- * read to the lane's parties; without it, reach falls back to a wider
104
- * test and a read can be authorised on a weaker basis than the caller
105
- * intended. Send it on every read made while acting on a wake.
106
- */
107
- constructor(operatorKey: string, agentId?: string, baseUrl?: string, laneId?: string);
108
- /** The same reader acting on one wake's lane. */
109
- withLane(laneId: string | undefined): ContextReadClient;
100
+ /** Authority is supplied by withWorkExecution / withWorkExecutionId. */
101
+ constructor(operatorKey: string, agentId?: string, baseUrl?: string);
110
102
  read<T = unknown>(type: ContextReadType, query: ContextReadQuery): Promise<ContextReadEnvelope<T>>;
111
103
  /**
112
104
  * Aggregated chat snapshot — `GET /context/snapshot?via=chat:<id>`. The
@@ -57,28 +57,13 @@ export class ContextReadClient {
57
57
  operatorKey;
58
58
  agentId;
59
59
  baseUrl;
60
- laneId;
61
- /**
62
- * @param operatorKey Agent-scoped or fleet operator key.
63
- * @param agentId Required for fleet keys (sent as X-Agent-Id). Omit for agent-scoped keys.
64
- * @param laneId the wake's lane, sent as X-Ziggs-Lane. The server fences a
65
- * read to the lane's parties; without it, reach falls back to a wider
66
- * test and a read can be authorised on a weaker basis than the caller
67
- * intended. Send it on every read made while acting on a wake.
68
- */
69
- constructor(operatorKey, agentId, baseUrl, laneId) {
60
+ /** Authority is supplied by withWorkExecution / withWorkExecutionId. */
61
+ constructor(operatorKey, agentId, baseUrl) {
70
62
  if (!operatorKey)
71
63
  throw new Error('ContextReadClient: operatorKey is required');
72
64
  this.operatorKey = operatorKey;
73
65
  this.agentId = agentId;
74
66
  this.baseUrl = baseUrl || getBackendUrl();
75
- this.laneId = laneId;
76
- }
77
- /** The same reader acting on one wake's lane. */
78
- withLane(laneId) {
79
- if (!laneId || laneId === this.laneId)
80
- return this;
81
- return new ContextReadClient(this.operatorKey, this.agentId, this.baseUrl, laneId);
82
67
  }
83
68
  async read(type, query) {
84
69
  if (!query.via?.trim()) {
@@ -101,7 +86,7 @@ export class ContextReadClient {
101
86
  if (query.contextGrantId) {
102
87
  url.searchParams.set('contextGrantId', query.contextGrantId);
103
88
  }
104
- const headers = buildOperatorHeaders(this.operatorKey, this.agentId, undefined, this.laneId);
89
+ const headers = buildOperatorHeaders(this.operatorKey, this.agentId, undefined);
105
90
  if (query.contextGrantId) {
106
91
  headers['X-Context-Grant-Id'] = query.contextGrantId;
107
92
  }
@@ -138,7 +123,7 @@ export class ContextReadClient {
138
123
  if (opts.contextGrantId) {
139
124
  url.searchParams.set('contextGrantId', opts.contextGrantId);
140
125
  }
141
- const headers = buildOperatorHeaders(this.operatorKey, this.agentId, undefined, this.laneId);
126
+ const headers = buildOperatorHeaders(this.operatorKey, this.agentId, undefined);
142
127
  if (opts.contextGrantId) {
143
128
  headers['X-Context-Grant-Id'] = opts.contextGrantId;
144
129
  }
@@ -0,0 +1,3 @@
1
+ import type { WorkExecutionContext } from '@ziggs-ai/contracts';
2
+ /** Fail closed before resuming model state, including timers and recorded wakes. */
3
+ export declare function validateWorkExecution(context: WorkExecutionContext, operatorKey: string, baseUrl?: string): Promise<void>;
@@ -0,0 +1,17 @@
1
+ import { getBackendUrl } from '../utils/urlUtils.js';
2
+ import { buildOperatorHeaders, withWorkExecution } from './operatorHeaders.js';
3
+ import { throwApiError } from '../shared/apiError.js';
4
+ /** Fail closed before resuming model state, including timers and recorded wakes. */
5
+ export async function validateWorkExecution(context, operatorKey, baseUrl = getBackendUrl()) {
6
+ await withWorkExecution(context, async () => {
7
+ const response = await fetch(`${baseUrl}/execution/context`, {
8
+ headers: buildOperatorHeaders(operatorKey, context.actorId),
9
+ });
10
+ const text = await response.text();
11
+ if (!response.ok)
12
+ throwApiError(response, text, 'Execution context is no longer valid');
13
+ const body = JSON.parse(text);
14
+ if (body.contextId !== context.contextId)
15
+ throw new Error('Execution preflight returned a different context');
16
+ });
17
+ }
@@ -56,16 +56,8 @@ export declare class GrantsClient {
56
56
  private readonly operatorKey;
57
57
  private readonly agentId?;
58
58
  private readonly baseUrl;
59
- private readonly laneId?;
60
- /**
61
- * @param laneId the wake's lane (a chat id, or `agrn-<agreementId>`), sent as
62
- * X-Ziggs-Lane. The server reads off it who the agent is acting for, and
63
- * answers the spendable rails with only the grants this wake may spend —
64
- * so an agent serving several customers is never handed a list it has to
65
- * choose between. Without it the list is everything the agent holds, and
66
- * the grant it picks may then be refused with nothing saying why.
67
- */
68
- constructor(operatorKey: string, agentId?: string, baseUrl?: string, laneId?: string);
59
+ /** Authority is supplied by withWorkExecution / withWorkExecutionId. */
60
+ constructor(operatorKey: string, agentId?: string, baseUrl?: string);
69
61
  listGrants(query?: ListGrantsQuery): Promise<ListGrantsResult>;
70
62
  /**
71
63
  * Every grant matching `query`, following the cursor to completion. Use when a