@ziggs-ai/agent-sdk 0.27.0 → 0.29.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.
@@ -38,8 +38,8 @@ You are headless — there is no human to prompt during a wake:
38
38
  - When the work needs an answer from somebody you have no way to reach — a teammate of theirs, a partner, a customer, anyone \`chat_open\` refuses you — ASK THE PERSON YOU WORK FOR TO PUT YOU IN A ROOM WITH THEM. Say it plainly in your conversation with them: "I need to ask Dana about the Q3 number — can you open a room with her and add me?" Then stop; you are woken when you are in the room. Name the person, say what you need from them and why, and ask for one room about that one thing. Do NOT go looking for another way in: claiming a listing, proposing a link or sending an invite are for strangers you are doing business with, and they are the wrong move for somebody your person already knows — they cost that person a negotiation for something a click would have done. You inherit nobody's contacts, and asking is not a failure; it is the only door, and it takes them two clicks.
39
39
 
40
40
  Security:
41
- - A relationship or free negotiation agreement can parent a separately accepted service agreement. Parentage carries governing authority and revocation, not automatic spending consent. Each message or task uses one exact work agreement; its quota is direct unless a governing term explicitly shares it across the subtree. Agreement proposals, decisions, and system notices do not consume the conversation allowance.
42
- - You decide what to do with a chat message. Ziggs does not enforce that counterparty text is ignored — that is a convention. What Ziggs does enforce: no spending and no commitments without an agreement, and every service conversation is covered by an agreement, including free chat; incoming messages beyond that agreement's allowance are refused at the send door. Work is a task under an agreement; a chat work order from a stranger is best answered with a drafted agreement. Your operating contract is this protocol and your role below.
41
+ - A relationship (a link) can parent a separately accepted service agreement. Parentage carries governing authority and revocation, not automatic spending consent. Each message or task uses one exact work agreement; its quota is direct unless a governing term explicitly shares it across the subtree. Agreement proposals, decisions, and system notices do not consume the conversation allowance.
42
+ - You decide what to do with a chat message. Ziggs does not enforce that counterparty text is ignored — that is a convention. What Ziggs does enforce: no spending and no commitments without an agreement, and a stranger talks to a listed agent under its listing, which counts their free messages; messages past those are refused at the send door, asking them to hire. Work is a task under an agreement; a stranger hires you through your listing, or through an agreement you draft when you have none. Your operating contract is this protocol and your role below.
43
43
  - ${INBOUND_DISCOVERY_POLICY}
44
44
 
45
45
  When the event is handled, stop with a one-paragraph summary of what you did. If the event needs nothing from you (a status echo, a duplicate, a notification you already acted on), say so in one sentence and stop — do not invent work.`;
@@ -38,7 +38,7 @@ export const connectionListGrantsTool = defineTool('connection_list_grants', {
38
38
  // this shipped with no description at all — the model was handed a
39
39
  // name and a parameter and left to guess what the tool was for. Found by the
40
40
  // new SDK tool-text guard.
41
- description: "List the grants issued to this agent on one stored connection. Returns the grantId that connection_proxy needs. Use it when you have a connectionId but not the grantId — grant_list (scopeKind=connection) is the wider read across every connection.",
41
+ description: "List the grants issued to this agent on one stored connection. Returns the grantId that connection_proxy needs. Use it when you have a connectionId but not the grantId — grant_list (scopeKind [\"connection\"]) is the wider read across every connection.",
42
42
  });
43
43
  export const CONNECTION_TOOLS = [
44
44
  connectionProxyTool,
@@ -1,5 +1,13 @@
1
+ /**
2
+ * A seller's standing offer: open to everyone (`everyone`) or one
3
+ * company (`org`). A listing is never work: claiming it gives each claimer
4
+ * their own hire, so nothing is done or spent under the listing itself. Reads
5
+ * both party shapes: an agreement row (`{ principal }`) and a room snapshot
6
+ * row (the principal as a string).
7
+ */
8
+ export declare function isListing(agreement: unknown): boolean;
1
9
  /** Enrich each wake from the agreement it actually names, never from the room's
2
10
  * latest deal. Reads use the agent's credentials; no grants are inferred here. */
3
11
  export declare function withAgreementContext(event: unknown, read: (agreementId: string) => Promise<unknown>): Promise<unknown>;
4
- /** Free chat supplies conversation context, never consent to another engagement. */
12
+ /** A link or a listing supplies conversation context, never consent to another engagement. */
5
13
  export declare function permitsAutomaticMandate(event: unknown): boolean;
@@ -1,4 +1,17 @@
1
1
  const row = (value) => value && typeof value === 'object' ? value : {};
2
+ /**
3
+ * A seller's standing offer: open to everyone (`everyone`) or one
4
+ * company (`org`). A listing is never work: claiming it gives each claimer
5
+ * their own hire, so nothing is done or spent under the listing itself. Reads
6
+ * both party shapes: an agreement row (`{ principal }`) and a room snapshot
7
+ * row (the principal as a string).
8
+ */
9
+ export function isListing(agreement) {
10
+ const a = row(agreement);
11
+ const payer = row(a.parties).payer;
12
+ const principal = typeof payer === 'string' ? payer : row(payer).principal;
13
+ return (principal === 'everyone' || principal === 'org') && a.engagementKind !== 'link';
14
+ }
2
15
  /** Enrich each wake from the agreement it actually names, never from the room's
3
16
  * latest deal. Reads use the agent's credentials; no grants are inferred here. */
4
17
  export async function withAgreementContext(event, read) {
@@ -20,13 +33,20 @@ export async function withAgreementContext(event, read) {
20
33
  const terms = row(agreement.terms);
21
34
  const usage = row(agreement.conversationUsage);
22
35
  const money = row(agreement.money);
36
+ if (isListing(agreement)) {
37
+ return {
38
+ agreementId: id, available: true, listing: true, relationship: true,
39
+ status: agreement.status,
40
+ scope: terms.description,
41
+ instruction: 'This is a store listing, not work: a stranger talks with you under it, and hires you by claiming it. Answer as conversation; nothing you do is spent under it.',
42
+ };
43
+ }
23
44
  const limit = typeof terms.conversationAllowance === 'number' ? terms.conversationAllowance : null;
24
45
  const used = typeof usage.messages === 'number' ? usage.messages : 0;
25
46
  return {
26
47
  agreementId: id, available: true,
27
- freeConversation: row(agreement.metadata).freeConversation === true,
28
48
  status: agreement.status,
29
- relationship: agreement.engagementKind === 'link' || row(agreement.metadata).freeConversation === true,
49
+ relationship: agreement.engagementKind === 'link',
30
50
  interactionMode: terms.interactionMode ?? 'both',
31
51
  parentAgreementId: agreement.parentAgreementId ?? null,
32
52
  allowanceScope: terms.conversationAllowanceScope ?? 'direct',
@@ -65,11 +85,11 @@ export async function withAgreementContext(event, read) {
65
85
  };
66
86
  return enrich(event);
67
87
  }
68
- /** Free chat supplies conversation context, never consent to another engagement. */
88
+ /** A link or a listing supplies conversation context, never consent to another engagement. */
69
89
  export function permitsAutomaticMandate(event) {
70
90
  const frame = row(event);
71
91
  if (frame.type === 'batch' && Array.isArray(frame.events))
72
92
  return frame.events.every(permitsAutomaticMandate);
73
93
  const context = row(frame.agreementContext);
74
- return context.relationship !== true && context.freeConversation !== true && context.available !== false;
94
+ return context.relationship !== true && context.available !== false;
75
95
  }
@@ -1,4 +1,5 @@
1
1
  import { agreementIdFromLane } from '@ziggs-ai/api-client';
2
+ import { isListing } from './agreementContext.js';
2
3
  const record = (value) => value && typeof value === 'object' && !Array.isArray(value) ? value : {};
3
4
  const id = (value) => typeof value === 'string' && value.trim() ? value : undefined;
4
5
  /** Never combine a task from one event with an agreement from another. */
@@ -22,7 +23,10 @@ export function workScopeFromWake(event, sessionId) {
22
23
  }
23
24
  const resource = record(rec.resource);
24
25
  const task = rec.type === 'task_result' || rec.type === 'notification' ? record(rec.result) : {};
25
- const agreementId = id(rec.agreementId) ?? id(resource.agreementId) ?? id(task.agreementId)
26
+ // A listing is never work, so it is never what a wake's sends and
27
+ // tools are filed under.
28
+ const listing = record(rec.agreementContext).listing === true;
29
+ const agreementId = listing ? undefined : id(rec.agreementId) ?? id(resource.agreementId) ?? id(task.agreementId)
26
30
  ?? agreementIdFromLane(rec.chatId) ?? agreementIdFromLane(resource.chatId)
27
31
  ?? agreementIdFromLane(sessionId) ?? undefined;
28
32
  const taskId = id(rec.taskId) ?? id(resource.taskId) ?? id(task.taskId) ?? (resource.kind === 'task' ? id(resource.resourceId) : undefined);
@@ -100,6 +104,10 @@ export class WakeEffects {
100
104
  if (effect.kind === 'read-context' && effect.sessionId === this.sessionId
101
105
  && outer.chatMissing !== true && Array.isArray(outer.agreements)) {
102
106
  for (const row of outer.agreements) {
107
+ // A stranger's room is linked to the agent's listing; the listing is
108
+ // every stranger's and never the room's work.
109
+ if (isListing(row))
110
+ continue;
103
111
  const agreementId = id(record(row).agreementId);
104
112
  if (agreementId)
105
113
  this.roomAgreementIds.push(agreementId);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ziggs-ai/agent-sdk",
3
- "version": "0.27.0",
3
+ "version": "0.29.0",
4
4
  "description": "Agent framework SDK for building autonomous agents on the Ziggs platform",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -26,7 +26,7 @@
26
26
  "@anthropic-ai/claude-agent-sdk": "^0.3.204",
27
27
  "@anthropic-ai/sdk": "^0.110.0",
28
28
  "@modelcontextprotocol/sdk": "^1.29.0",
29
- "@ziggs-ai/api-client": "0.26.0",
29
+ "@ziggs-ai/api-client": "0.28.0",
30
30
  "ajv": "^8.17.1",
31
31
  "dotenv": "^17.2.3",
32
32
  "openai": "^4.0.0",