@ziggs-ai/agent-sdk 0.4.0 → 0.5.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.
@@ -22,6 +22,12 @@ export declare class AgreementService {
22
22
  counter(agreementId: string, counter: CounterAgreementData): Promise<Agreement>;
23
23
  getStatus(agreementId: string): Promise<unknown | null>;
24
24
  list(filters?: ListAgreementsFilters): Promise<Agreement[]>;
25
+ /**
26
+ * ZIG-942: despite the name, the default returns every agreement the
27
+ * caller's grant can read in the active org — including ones the caller is
28
+ * not a party to (`isYou` all-false). Pass `{ partyOnly: true }` for only
29
+ * agreements where the caller is a party.
30
+ */
25
31
  mine(filters?: GetMyAgreementsFilters): Promise<Agreement[]>;
26
32
  get(agreementId: string): Promise<Agreement | null>;
27
33
  create(body: CreateAgreementBody): Promise<{
@@ -46,6 +46,12 @@ export class AgreementService {
46
46
  list(filters = {}) {
47
47
  return listAgreements(filters, this.creds);
48
48
  }
49
+ /**
50
+ * ZIG-942: despite the name, the default returns every agreement the
51
+ * caller's grant can read in the active org — including ones the caller is
52
+ * not a party to (`isYou` all-false). Pass `{ partyOnly: true }` for only
53
+ * agreements where the caller is a party.
54
+ */
49
55
  mine(filters = {}) {
50
56
  return getMyAgreements(filters, this.creds);
51
57
  }
@@ -1,3 +1,3 @@
1
- import { type ToolDefinition } from '../../../tools/defineTool.js';
1
+ import type { ToolDefinition } from '../../../tools/defineTool.js';
2
2
  export declare const recordArtifactTool: ToolDefinition;
3
3
  export declare const ARTIFACT_TOOLS: ToolDefinition[];
@@ -1,82 +1,7 @@
1
- import { defineTool } from '../../../tools/defineTool.js';
2
- import { ArtifactsClient } from '@ziggs-ai/api-client';
3
- /**
4
- * ZIG-899 — deliberate artifact recording on the SDK surface, same base name
5
- * as ziggs-mcp's ziggs_record_artifact. Until now artifacts were written only
6
- * as a host-side effect (recordReport / record-event); the model could not
7
- * decide "this is a deliverable, record it" — which is exactly what the
8
- * delegate protocol tells agents to do for heavy results. Rides
9
- * ArtifactsClient.writeStrict (fail loudly, return the artifactId) — the same
10
- * wire call as the MCP tool.
11
- */
12
- function creds(ctx) {
13
- const operatorKey = ctx?.['operatorKey'];
14
- const agentId = ctx?.['agentId'];
15
- if (!operatorKey)
16
- throw new Error('operatorKey missing from tool context');
17
- if (!agentId)
18
- throw new Error('agentId missing from tool context');
19
- return { operatorKey, agentId };
20
- }
21
- /**
22
- * ZIG-560 teaching carried over from the MCP tool: name the result slot on the
23
- * success path so an agent finds the right move unaided — reworded to the SDK
24
- * task grammar (task_update carries the terminal result on this surface).
25
- */
26
- function reportingHint(contentType, taskId) {
27
- if (contentType === 'result') {
28
- return taskId
29
- ? 'Recorded as a task-bound result artifact. Close the task by setting its terminal result with task_update.'
30
- : 'Recorded as a result artifact, but not bound to a task — pass taskId to bind it, then close the task with task_update.';
31
- }
32
- return 'Reporting finished work? Record it with content_type=result bound to the task (taskId), then close the task with task_update — chat messages are conversation only.';
33
- }
34
- export const recordArtifactTool = defineTool('record_artifact', {
35
- text: { type: 'string', required: true, description: 'Artifact body' },
36
- visibility: {
37
- type: 'string',
38
- required: true,
39
- enum: ['chat', 'agent-private'],
40
- description: 'chat = visible to scope parties; agent-private = your eyes only',
41
- },
42
- chatId: { type: 'string', description: 'Target chat (xor agreementId)' },
43
- agreementId: { type: 'string', description: 'Target agreement (xor chatId)' },
44
- taskId: {
45
- type: 'string',
46
- description: 'Optional task — creates a TaskArtifactLink alongside the primary scope link',
47
- },
48
- content_type: { type: 'string', description: 'Default text; use result for a finished deliverable' },
49
- }, async (args, ctx) => {
50
- const chatId = args['chatId'];
51
- const agreementId = args['agreementId'];
52
- if ((chatId && agreementId) || (!chatId && !agreementId)) {
53
- throw new Error('Pass exactly one of chatId or agreementId');
54
- }
55
- const visibility = args['visibility'];
56
- if (visibility !== 'chat' && visibility !== 'agent-private') {
57
- throw new Error('visibility must be chat or agent-private');
58
- }
59
- const contentType = args['content_type'];
60
- const taskId = args['taskId'];
61
- const { operatorKey, agentId } = creds(ctx);
62
- const { artifactId } = await new ArtifactsClient(operatorKey, agentId).writeStrict({
63
- text: args['text'],
64
- visibility,
65
- chatId,
66
- agreementId,
67
- taskId,
68
- content_type: contentType,
69
- });
70
- return {
71
- ok: true,
72
- artifactId,
73
- visibility,
74
- chatId,
75
- agreementId,
76
- taskId,
77
- reportingHint: reportingHint(contentType, taskId),
78
- };
79
- }, {
80
- description: 'Write an artifact to a chat or agreement scope. Set visibility explicitly. For a finished deliverable, set content_type=result and pass taskId to bind it to the task — heavy results belong in artifacts, not chat messages.',
81
- });
82
- export const ARTIFACT_TOOLS = [recordArtifactTool];
1
+ import { ARTIFACT_CAPABILITIES, recordArtifactCapability } from '@ziggs-ai/api-client';
2
+ import { toolFromCapability, toolsFromCapabilities } from '../../../tools/capabilityAdapter.js';
3
+ // ZIG-899 / ZIG-956 — deliberate artifact recording, defined once in
4
+ // api-client's shared capability layer (including the ZIG-560 result-slot
5
+ // teaching, worded per surface task grammar).
6
+ export const recordArtifactTool = toolFromCapability(recordArtifactCapability);
7
+ export const ARTIFACT_TOOLS = toolsFromCapabilities(ARTIFACT_CAPABILITIES);
@@ -1,3 +1,3 @@
1
- import { type ToolDefinition } from '../../../tools/defineTool.js';
1
+ import type { ToolDefinition } from '../../../tools/defineTool.js';
2
2
  export declare const openConversationTool: ToolDefinition;
3
3
  export declare const CHAT_TOOLS: ToolDefinition[];
@@ -1,37 +1,7 @@
1
- import { defineTool } from '../../../tools/defineTool.js';
2
- import { openConversation } from '@ziggs-ai/api-client';
3
- /**
4
- * ZIG-900 — the decided chat surface for hosted agents: open_conversation
5
- * only, same base name as MCP's ziggs_open_conversation. There is deliberately
6
- * NO chat-listing tool on the SDK: agents read context only via held grants
7
- * (ZIG-425) and chat membership auto-mints a chat grant (ZIG-428/426), so
8
- * "what chats can I see" is already answered by list_grants scopeKind=chat —
9
- * adding a lister would duplicate a context-family read (ZIG-626 rule).
10
- */
11
- function creds(ctx) {
12
- const operatorKey = ctx?.['operatorKey'];
13
- const agentId = ctx?.['agentId'];
14
- if (!operatorKey)
15
- throw new Error('operatorKey missing from tool context');
16
- if (!agentId)
17
- throw new Error('agentId missing from tool context');
18
- return { operatorKey, agentId };
19
- }
20
- export const openConversationTool = defineTool('open_conversation', {
21
- participantId: {
22
- type: 'string',
23
- required: true,
24
- description: 'User or agent id to converse with (from agent_search — do not guess ids)',
25
- },
26
- }, async (args, ctx) => {
27
- if (!args['participantId'])
28
- throw new Error('participantId is required');
29
- const { chatId } = await openConversation(args['participantId'], creds(ctx));
30
- return {
31
- chatId,
32
- note: 'Conversation is open (or reused). Your membership auto-mints a chat grant, so the chat also appears in list_grants scopeKind=chat for context reads.',
33
- };
34
- }, {
35
- description: 'Open or reuse a chat with a user or agent participant. To reach an agent in ANOTHER org, establish a link first — request_link (if you have its agent id) or create_link_invite (if you do not), approved/claimed — otherwise this fails with AGENT_NOT_PUBLISHED. To list chats you can already read, use list_grants scopeKind=chat.',
36
- });
37
- export const CHAT_TOOLS = [openConversationTool];
1
+ import { CHAT_CAPABILITIES, openConversationCapability } from '@ziggs-ai/api-client';
2
+ import { toolFromCapability, toolsFromCapabilities } from '../../../tools/capabilityAdapter.js';
3
+ // ZIG-900 / ZIG-956 — open_conversation only (chat listing stays a
4
+ // context-rail read, ZIG-626 rule); defined once in api-client's shared
5
+ // capability layer.
6
+ export const openConversationTool = toolFromCapability(openConversationCapability);
7
+ export const CHAT_TOOLS = toolsFromCapabilities(CHAT_CAPABILITIES);
@@ -1,5 +1,5 @@
1
1
  import { type ToolDefinition } from '../../../tools/defineTool.js';
2
2
  export declare const connectionProxyTool: ToolDefinition;
3
- export declare const connectionListGrantsTool: ToolDefinition;
4
3
  export declare const requestConnectionTool: ToolDefinition;
4
+ export declare const connectionListGrantsTool: ToolDefinition;
5
5
  export declare const CONNECTION_TOOLS: ToolDefinition[];
@@ -1,107 +1,33 @@
1
+ import { connectionProxyCapability, requestConnectionCapability, ConnectionsClient, } from '@ziggs-ai/api-client';
1
2
  import { defineTool } from '../../../tools/defineTool.js';
2
- import { ConnectionsClient } from '@ziggs-ai/api-client';
3
- function clientFor(ctx) {
3
+ import { toolFromCapability } from '../../../tools/capabilityAdapter.js';
4
+ // ZIG-956 — connection_proxy / request_connection are shared capability
5
+ // definitions (api-client). connection_list_grants stays SDK-only: the MCP
6
+ // surface folds connection listing into ziggs_list_my_connections (ZIG-893
7
+ // allowance in the parity table).
8
+ export const connectionProxyTool = toolFromCapability(connectionProxyCapability);
9
+ export const requestConnectionTool = toolFromCapability(requestConnectionCapability);
10
+ export const connectionListGrantsTool = defineTool('connection_list_grants', { connectionId: { type: 'string', required: true } }, async (args, ctx) => {
11
+ if (!args['connectionId'])
12
+ throw new Error('connectionId is required');
4
13
  const operatorKey = ctx?.['operatorKey'];
5
14
  if (!operatorKey)
6
15
  throw new Error('operatorKey missing from tool context');
7
- // BACKEND_URL / ZIGGS_BACKEND_URL are the SDK runner's overrides; the shared
8
- // client defaults to HTTP_URL (api-client convention) when neither is set.
9
16
  const baseUrl = process.env.BACKEND_URL || process.env.ZIGGS_BACKEND_URL || undefined;
10
- return new ConnectionsClient(operatorKey, ctx?.['agentId'] || undefined, baseUrl);
11
- }
12
- function rethrowWithContext(error, prefix) {
13
- const e = error;
14
- const wrapped = new Error(`${prefix}: ${e.message}`);
15
- if (e.status !== undefined)
16
- wrapped.status = e.status;
17
- if (e.body !== undefined)
18
- wrapped.body = e.body;
19
- wrapped['cause'] = error;
20
- throw wrapped;
21
- }
22
- export const connectionProxyTool = defineTool('connection_proxy', {
23
- connectionId: { type: 'string', required: true },
24
- grantId: { type: 'string', required: true },
25
- action: { type: 'string', required: true },
26
- payload: { type: 'object' },
27
- }, async (args, ctx) => {
28
- if (!args['connectionId'])
29
- throw new Error('connectionId is required');
30
- if (!args['grantId'])
31
- throw new Error('grantId is required');
32
- if (!args['action'])
33
- throw new Error('action is required');
17
+ const client = new ConnectionsClient(operatorKey, ctx?.['agentId'] || undefined, baseUrl);
34
18
  try {
35
- const result = await clientFor(ctx).proxy({
36
- connectionId: args['connectionId'],
37
- grantId: args['grantId'],
38
- action: args['action'],
39
- payload: args['payload'],
40
- });
41
- return { status: 'ok', result };
42
- }
43
- catch (e) {
44
- rethrowWithContext(e, 'Connection proxy failed');
45
- }
46
- });
47
- export const connectionListGrantsTool = defineTool('connection_list_grants', { connectionId: { type: 'string', required: true } }, async (args, ctx) => {
48
- if (!args['connectionId'])
49
- throw new Error('connectionId is required');
50
- try {
51
- const grants = await clientFor(ctx).listGrants({
52
- connectionId: args['connectionId'],
53
- });
19
+ const grants = await client.listGrants({ connectionId: args['connectionId'] });
54
20
  return { grants: grants || [] };
55
21
  }
56
22
  catch (e) {
57
- rethrowWithContext(e, 'Failed to list connection grants');
58
- }
59
- });
60
- export const requestConnectionTool = defineTool('request_connection', {
61
- chatId: {
62
- type: 'string',
63
- required: true,
64
- description: 'The chat you are working in — the consent card is opened there',
65
- },
66
- serverUrl: { type: 'string', required: true, description: 'Remote MCP server URL (https)' },
67
- tools: {
68
- type: 'array',
69
- items: { type: 'string' },
70
- required: true,
71
- description: "Tool names you want — become the grant's allowed_actions caveats",
72
- },
73
- reason: {
74
- type: 'string',
75
- description: 'Plain-language reason shown to the human deciding',
76
- },
77
- }, async (args, ctx) => {
78
- if (!args['chatId'])
79
- throw new Error('chatId is required');
80
- if (!args['serverUrl'])
81
- throw new Error('serverUrl is required');
82
- const tools = args['tools'];
83
- if (!Array.isArray(tools) || tools.length === 0) {
84
- throw new Error('tools must be a non-empty array of tool names');
85
- }
86
- try {
87
- const result = await clientFor(ctx).requestMcpConnection({
88
- chatId: args['chatId'],
89
- serverUrl: args['serverUrl'],
90
- tools: tools,
91
- reason: args['reason'],
92
- });
93
- return {
94
- ok: true,
95
- ...result,
96
- note: 'A connection-consent card is now in the chat awaiting your principal — they approve it right there (ZIG-798 flow). ' +
97
- 'Once approved, the connection + grant appear in list_grants (scopeKind=connection) / connection_list_grants for connection_proxy.',
98
- };
99
- }
100
- catch (e) {
101
- rethrowWithContext(e, 'Connection request failed');
23
+ const err = e;
24
+ const wrapped = new Error(`Failed to list connection grants: ${err.message}`);
25
+ if (err.status !== undefined)
26
+ wrapped.status = err.status;
27
+ if (err.body !== undefined)
28
+ wrapped.body = err.body;
29
+ throw wrapped;
102
30
  }
103
- }, {
104
- description: 'Ask your principal (the human) to connect a remote MCP server and grant you the listed tools. Opens a connection-consent agreement as an approvable card in the chat you pass — the human approves it there like any other agreement. On approval the server is connected (browser OAuth if needed) and you are granted the tools; use them via connection_proxy.',
105
31
  });
106
32
  export const CONNECTION_TOOLS = [
107
33
  connectionProxyTool,
@@ -1,6 +1,6 @@
1
- import { type ToolDefinition } from '../../../tools/defineTool.js';
1
+ import type { ToolDefinition } from '../../../tools/defineTool.js';
2
2
  export declare const contextReadTool: ToolDefinition;
3
3
  export declare const contextDelegateTool: ToolDefinition;
4
4
  export declare const contextExpandReachTool: ToolDefinition;
5
5
  export declare const contextDiscoverGrantableTool: ToolDefinition;
6
- export declare const CONTEXT_TOOLS: readonly [ToolDefinition, ToolDefinition, ToolDefinition, ToolDefinition];
6
+ export declare const CONTEXT_TOOLS: ToolDefinition[];
@@ -1,139 +1,10 @@
1
- import { defineTool } from '../../../tools/defineTool.js';
2
- import { ContextDiscoveryClient, ContextGrantsClient, ContextReadClient, } from '@ziggs-ai/api-client';
3
- /**
4
- * Context grant calls impersonate the grant holder, so both operatorKey (fleet
5
- * or agent-scoped key) and agentId (X-Agent-Id) must be on the tool context.
6
- * The same api-client clients back ziggs-mcp — one wire contract, no fork
7
- * (ZIG-869 / ZIG-775).
8
- */
9
- function creds(ctx) {
10
- const operatorKey = ctx?.['operatorKey'];
11
- const agentId = ctx?.['agentId'];
12
- if (!operatorKey)
13
- throw new Error('operatorKey missing from tool context');
14
- if (!agentId)
15
- throw new Error('agentId missing from tool context');
16
- return { operatorKey, agentId };
17
- }
18
- const CONTEXT_READ_TYPES = [
19
- 'messages',
20
- 'artifacts',
21
- 'agreements',
22
- 'tasks',
23
- ];
24
- export const contextReadTool = defineTool('context_read', {
25
- type: {
26
- type: 'string',
27
- required: true,
28
- enum: [...CONTEXT_READ_TYPES],
29
- description: 'Resource type to read from the scope',
30
- },
31
- via: {
32
- type: 'string',
33
- required: true,
34
- description: 'Scope entry you hold, e.g. chat:<id>, agreement:<id>, task:<id>',
35
- },
36
- cursor: { type: 'string', description: 'Opaque cursor from a prior nextCursor to page' },
37
- after: {
38
- type: 'string',
39
- description: 'ISO timestamp for forward-delta (messages/artifacts)',
40
- },
41
- direction: {
42
- type: 'string',
43
- enum: ['forward'],
44
- description: 'Use "forward" with after for a message forward-delta',
45
- },
46
- limit: { type: 'number', description: 'Page size (server default when omitted)' },
47
- state: { type: 'string', description: 'Task state filter (tasks only)' },
48
- contextGrantId: {
49
- type: 'string',
50
- description: 'Pin a specific grant when you hold several over the same scope',
51
- },
52
- }, async (args, ctx) => {
53
- const { operatorKey, agentId } = creds(ctx);
54
- const type = args['type'];
55
- if (!CONTEXT_READ_TYPES.includes(type)) {
56
- throw new Error(`type must be one of ${CONTEXT_READ_TYPES.join(', ')}`);
57
- }
58
- const via = args['via'];
59
- if (!via)
60
- throw new Error('via is required');
61
- const direction = args['direction'];
62
- if (direction !== undefined && direction !== 'forward') {
63
- throw new Error('direction must be "forward"');
64
- }
65
- return new ContextReadClient(operatorKey, agentId).read(type, {
66
- via,
67
- cursor: args['cursor'],
68
- after: args['after'],
69
- direction: direction,
70
- limit: typeof args['limit'] === 'number' ? args['limit'] : undefined,
71
- state: args['state'],
72
- contextGrantId: args['contextGrantId'],
73
- });
74
- }, {
75
- description: 'Read the contents of a scope you already hold a grant for: messages | artifacts | agreements | tasks. Use list_grants first to see which scopes your grants cover, then read through any of them. Cursored; all access is grant-fenced server-side.',
76
- });
77
- export const contextDelegateTool = defineTool('context_delegate', {
78
- parentGrantId: { type: 'string', required: true },
79
- holderId: { type: 'string', required: true },
80
- scopeKind: { type: 'string', required: true },
81
- scopeId: { type: 'string', required: true },
82
- temporal: { type: 'string', required: true },
83
- expiresAt: 'string',
84
- watermarkAt: 'string',
85
- }, async (args, ctx) => {
86
- const temporal = args['temporal'];
87
- if (temporal !== 'from-now' && temporal !== 'from-start') {
88
- throw new Error('temporal must be from-now or from-start');
89
- }
90
- const scopeKind = args['scopeKind'];
91
- if (scopeKind !== 'chat' && scopeKind !== 'agreement' && scopeKind !== 'org') {
92
- throw new Error('scopeKind must be chat, agreement, or org');
93
- }
94
- const { operatorKey, agentId } = creds(ctx);
95
- return new ContextGrantsClient(operatorKey, agentId).delegateGrant(args['parentGrantId'], {
96
- holderId: args['holderId'],
97
- scope: { kind: scopeKind, id: args['scopeId'] },
98
- temporal,
99
- expiresAt: args['expiresAt'] ?? undefined,
100
- watermarkAt: args['watermarkAt'],
101
- });
102
- }, {
103
- description: 'Delegate a narrower child context grant (tighter scope, shorter expiry, or from-now). Holder-only. Delegating a grant whose original owner is another party opens an approval request rather than minting immediately (status: pending_approval).',
104
- });
105
- export const contextExpandReachTool = defineTool('context_expand_reach', {
106
- grantId: {
107
- type: 'string',
108
- required: true,
109
- description: 'A grant you hold (grantId from list_grants) to expand',
110
- },
111
- }, async (args, ctx) => {
112
- const { operatorKey, agentId } = creds(ctx);
113
- const grantId = args['grantId'];
114
- if (!grantId)
115
- throw new Error('grantId is required');
116
- return new ContextGrantsClient(operatorKey, agentId).getReach(grantId);
117
- }, {
118
- description: 'Expand a grant you hold into the chat/agreement ids inside its scope, so you can read through it. list_grants tells you that you hold e.g. org:acme or agreement:x; this returns the actual { chats, agreements } (ids + labels only, no content) that scope covers — feed an id to context_read (via=chat:<id> / agreement:<id>). Org scope is capped: truncatedChats/truncatedAgreements say how many were left off. Holder-only, grant-fenced.',
119
- });
120
- // ZIG-754 — the negative-space complement to the reach tools. list_grants /
121
- // context_expand_reach answer "what CAN I read?"; this answers "what exists
122
- // just outside my fence that I CAN'T read yet?" so the agent can flag missing
123
- // context and make an informed widen request instead of silently
124
- // under-delivering on partial context. The MCP surface has had this as
125
- // ziggs_discover_grantable; this is its SDK/legibility complement, backed by
126
- // the same ContextDiscoveryClient (one wire contract, no fork).
127
- export const contextDiscoverGrantableTool = defineTool('context_discover_grantable', {}, async (_args, ctx) => {
128
- const { operatorKey, agentId } = creds(ctx);
129
- const items = await new ContextDiscoveryClient(operatorKey, agentId).discoverGrantable();
130
- return { count: items.length, items };
131
- }, {
132
- description: 'See what context EXISTS in your orgs that you CANNOT read yet — the inverse of list_grants. Covers chats, agreements, and connections (type is one of "chat" | "agreement" | "connection"; connection labels are the provider name only). Returns labels only ({ type, label, scopeRef, orgId } per item), never content, member names, tokens, or money. Bounded to orgs you have an active agreement in, and excludes anything you already hold a grant for. Use it to notice you may be missing context, then either ask your human to grant a scopeRef, or (if you hold a broader grant) context_delegate using that scopeRef. Pair with list_grants (what you hold) and context_expand_reach (what a held grant covers).',
133
- });
134
- export const CONTEXT_TOOLS = [
135
- contextReadTool,
136
- contextDelegateTool,
137
- contextExpandReachTool,
138
- contextDiscoverGrantableTool,
139
- ];
1
+ import { CONTEXT_CAPABILITIES, contextReadCapability, contextDelegateCapability, contextExpandReachCapability, contextDiscoverGrantableCapability, } from '@ziggs-ai/api-client';
2
+ import { toolFromCapability, toolsFromCapabilities } from '../../../tools/capabilityAdapter.js';
3
+ // ZIG-869 / ZIG-775 / ZIG-956 — context grant tools ride the same api-client
4
+ // clients as ziggs-mcp; the whole definition (schema + handler) is now shared
5
+ // via the capability layer, so the two surfaces cannot fork.
6
+ export const contextReadTool = toolFromCapability(contextReadCapability);
7
+ export const contextDelegateTool = toolFromCapability(contextDelegateCapability);
8
+ export const contextExpandReachTool = toolFromCapability(contextExpandReachCapability);
9
+ export const contextDiscoverGrantableTool = toolFromCapability(contextDiscoverGrantableCapability);
10
+ export const CONTEXT_TOOLS = toolsFromCapabilities(CONTEXT_CAPABILITIES);
@@ -1,4 +1,4 @@
1
- import { type ToolDefinition } from '../../../tools/defineTool.js';
1
+ import type { ToolDefinition } from '../../../tools/defineTool.js';
2
2
  export declare const agentSearchTool: ToolDefinition;
3
3
  export declare const agentGetTool: ToolDefinition;
4
4
  export declare const DISCOVERY_TOOLS: ToolDefinition[];
@@ -1,25 +1,8 @@
1
- import { defineTool } from '../../../tools/defineTool.js';
2
- import { AgentSearchClient } from '@ziggs-ai/api-client';
3
- function clientFor(ctx) {
4
- const operatorKey = ctx?.['operatorKey'];
5
- const agentId = ctx?.['agentId'];
6
- if (!operatorKey)
7
- throw new Error('operatorKey missing from tool context');
8
- if (!agentId)
9
- throw new Error('agentId missing from tool context');
10
- return new AgentSearchClient(operatorKey, agentId);
11
- }
12
- export const agentSearchTool = defineTool('agent_search', { query: { type: 'string', required: true }, limit: 'number' }, async (args, ctx) => {
13
- if (!args['query'])
14
- throw new Error('query is required');
15
- const options = {};
16
- if (typeof args['limit'] === 'number')
17
- options.limit = args['limit'];
18
- return clientFor(ctx).searchAgents(args['query'], options);
19
- }, { description: 'Search for agents by capability, name, or description. Returns ranked results with relevance scores. Use before agreement_propose or agreement_subcontract to discover the right agent for a job.', isGenericFallback: true });
20
- export const agentGetTool = defineTool('agent_get', { agentId: { type: 'string', required: true } }, async (args, ctx) => {
21
- if (!args['agentId'])
22
- throw new Error('agentId is required');
23
- return clientFor(ctx).getAgentById(args['agentId']);
24
- }, { description: 'Fetch the full profile of a specific agent by ID. Use to confirm capabilities and terms before proposing an agreement.' });
1
+ import { agentSearchCapability, agentGetCapability } from '@ziggs-ai/api-client';
2
+ import { toolFromCapability } from '../../../tools/capabilityAdapter.js';
3
+ // ZIG-956 — agent discovery is a shared capability definition (api-client),
4
+ // so search shaping (ZIG-664 zero-hit hint, reachability rows) is identical
5
+ // on both surfaces.
6
+ export const agentSearchTool = toolFromCapability(agentSearchCapability);
7
+ export const agentGetTool = toolFromCapability(agentGetCapability);
25
8
  export const DISCOVERY_TOOLS = [agentSearchTool, agentGetTool];
@@ -1,3 +1,3 @@
1
- import { type ToolDefinition } from '../../../tools/defineTool.js';
1
+ import type { ToolDefinition } from '../../../tools/defineTool.js';
2
2
  export declare const listGrantsTool: ToolDefinition;
3
3
  export declare const GRANTS_TOOLS: ToolDefinition[];
@@ -1,81 +1,6 @@
1
- import { defineTool } from '../../../tools/defineTool.js';
2
- import { GrantsClient, unreadableGrantRails, } from '@ziggs-ai/api-client';
3
- /**
4
- * ZIG-893 — the single "what authority do I hold?" tool. One name, every rail
5
- * (context chat/agreement/org, connection, wallet), holder-scoped, cross-session.
6
- * Wraps the unified `GET /grants` (GrantsClient) that already replaced the three
7
- * per-rail listers server-side; this lifts that unification up to the tool layer
8
- * so an agent no longer has to know three tool names — or, as in the 2026-07-20
9
- * incident, tell its principal grant lookup doesn't exist because its config
10
- * happened to omit the one rail's lister.
11
- */
12
- function creds(ctx) {
13
- const operatorKey = ctx?.['operatorKey'];
14
- const agentId = ctx?.['agentId'];
15
- if (!operatorKey)
16
- throw new Error('operatorKey missing from tool context');
17
- if (!agentId)
18
- throw new Error('agentId missing from tool context');
19
- return { operatorKey, agentId };
20
- }
21
- const SCOPE_KINDS = [
22
- 'chat',
23
- 'agreement',
24
- 'org',
25
- 'connection',
26
- 'wallet',
27
- ];
28
- const HEALTHS = ['active', 'expired', 'revoked'];
29
- function parseScopeKinds(raw) {
30
- if (raw == null)
31
- return undefined;
32
- const list = Array.isArray(raw) ? raw : [raw];
33
- const out = [];
34
- for (const r of list) {
35
- if (!SCOPE_KINDS.includes(r)) {
36
- throw new Error(`scopeKind must be one of ${SCOPE_KINDS.join(', ')}`);
37
- }
38
- if (!out.includes(r))
39
- out.push(r);
40
- }
41
- return out.length ? out : undefined;
42
- }
43
- export const listGrantsTool = defineTool('list_grants', {
44
- scopeKind: {
45
- type: 'array',
46
- items: { type: 'string', enum: [...SCOPE_KINDS] },
47
- description: 'Rail filter (repeatable): chat | agreement | org (context) | connection | wallet. Omit for every rail you can read.',
48
- },
49
- health: {
50
- type: 'string',
51
- enum: [...HEALTHS],
52
- description: 'Grant health filter. Defaults to active (live grants only).',
53
- },
54
- cursor: { type: 'string', description: 'Opaque cursor from a prior nextCursor to page' },
55
- limit: { type: 'number', description: 'Page size (server default when omitted)' },
56
- }, async (args, ctx) => {
57
- const { operatorKey, agentId } = creds(ctx);
58
- const scopeKind = parseScopeKinds(args['scopeKind']);
59
- const healthArg = args['health'];
60
- if (healthArg !== undefined && !HEALTHS.includes(healthArg)) {
61
- throw new Error(`health must be one of ${HEALTHS.join(', ')}`);
62
- }
63
- const { items, nextCursor } = await new GrantsClient(operatorKey, agentId).listGrants({
64
- scopeKind,
65
- // Reach = live grants only by default; pass health to see expired/revoked.
66
- health: healthArg ?? 'active',
67
- cursor: args['cursor'],
68
- limit: typeof args['limit'] === 'number' ? args['limit'] : undefined,
69
- });
70
- const unreadable = unreadableGrantRails(operatorKey, scopeKind);
71
- return {
72
- count: items.length,
73
- grants: items,
74
- nextCursor,
75
- ...(unreadable && unreadable.length ? { unreadableRails: unreadable } : {}),
76
- };
77
- }, {
78
- description: 'List every grant this agent holds across all rails in one call — context (chat/agreement/org), connection, and wallet — as canonical grants (grantId, scope, caveats, expiresAt, health; no message/artifact content or credentials). The single answer to "what grants of mine do you hold?", holder-scoped and cross-session. Filter by scopeKind (rail) and health (defaults to active). Rails you lack the operator-key read scope for are named in unreadableRails, not silently dropped. Cursor-paginated: pass cursor from a prior nextCursor. Pair with context_read to read through a context grant, or context_expand_reach to enumerate a scope.',
79
- isGenericFallback: true,
80
- });
81
- export const GRANTS_TOOLS = [listGrantsTool];
1
+ import { GRANTS_CAPABILITIES, listGrantsCapability } from '@ziggs-ai/api-client';
2
+ import { toolFromCapability, toolsFromCapabilities } from '../../../tools/capabilityAdapter.js';
3
+ // ZIG-893 / ZIG-956 — the single "what authority do I hold?" tool, defined
4
+ // once in api-client's shared capability layer.
5
+ export const listGrantsTool = toolFromCapability(listGrantsCapability);
6
+ export const GRANTS_TOOLS = toolsFromCapabilities(GRANTS_CAPABILITIES);
@@ -1,4 +1,4 @@
1
- import { type ToolDefinition } from '../../../tools/defineTool.js';
1
+ import type { ToolDefinition } from '../../../tools/defineTool.js';
2
2
  export declare const requestLinkTool: ToolDefinition;
3
3
  export declare const createLinkInviteTool: ToolDefinition;
4
4
  export declare const claimLinkInviteTool: ToolDefinition;
@@ -1,156 +1,12 @@
1
- import { defineTool } from '../../../tools/defineTool.js';
2
- import { createAgreement, claimAgreement, listAgreements, revokeAgreement, } from '@ziggs-ai/api-client';
3
- /**
4
- * ZIG-898 — link tools on the SDK surface, same base names as ziggs-mcp's
5
- * trustTools (ziggs_request_link → request_link, …). Links are agreements with
6
- * engagementKind 'link' — bilateral agent-to-agent trust that gives unpublished
7
- * delegates cross-org reach. No new endpoints: everything rides the agreements
8
- * rail exactly as the MCP tools do.
9
- *
10
- * A link is reach-only: it lets the peers find and message each other, never
11
- * read context — grants stay separate (context_delegate / the owner's grant
12
- * tools).
13
- */
14
- function creds(ctx) {
15
- const operatorKey = ctx?.['operatorKey'];
16
- const agentId = ctx?.['agentId'];
17
- if (!operatorKey)
18
- throw new Error('operatorKey missing from tool context');
19
- if (!agentId)
20
- throw new Error('agentId missing from tool context');
21
- return { operatorKey, agentId };
22
- }
23
- function webAppOrigin() {
24
- return (process.env.ZIGGS_WEB_URL?.trim() || 'https://ziggsai.com').replace(/\/$/, '');
25
- }
26
- /**
27
- * ZIG-670 rule carried over from the MCP tools: link-shaped summaries, not raw
28
- * agreement documents — the money block, approvals array, and Mongo internals
29
- * are noise on a trust relationship.
30
- */
31
- function linkSummary(a) {
32
- return {
33
- agreementId: a.agreementId,
34
- status: a.status,
35
- proposalStatus: a.proposalStatus,
36
- parties: {
37
- creatorAgent: a.parties?.creatorAgent ?? null,
38
- providerAgent: a.parties?.providerAgent ?? null,
39
- creator: a.parties?.creator ?? null,
40
- proposedTo: a.parties?.proposedTo ?? null,
41
- },
42
- ...(a.description ? { description: a.description } : {}),
43
- createdAt: a.createdAt,
44
- };
45
- }
46
- const LINK_IS_REACH_ONLY = 'A link is reach-only: it does not grant context. Share a slice of a grant you hold with context_delegate, or ask the peer owner to issue one, before reading.';
47
- export const requestLinkTool = defineTool('request_link', {
48
- providerId: {
49
- type: 'string',
50
- required: true,
51
- description: 'Bare agent id to link with (the target delegate). Use agent_search or a known delegate id — do not guess.',
52
- },
53
- message: {
54
- type: 'string',
55
- description: 'Optional note shown to the counterparty human on approval (agreement description)',
56
- },
57
- }, async (args, ctx) => {
58
- const { agreement } = await createAgreement({
59
- engagementKind: 'link',
60
- providerId: args['providerId'],
61
- description: args['message'],
62
- }, creds(ctx));
63
- return {
64
- status: 'pending',
65
- message: 'Link agreement created — the counterparty owner must approve (agreement_respond on their side) before cross-org reach. Surface pending state to your principal.',
66
- agreement: linkSummary(agreement),
67
- };
68
- }, {
69
- description: 'Request a bilateral trust link with another agent before cross-org reach (party-to-party, NOT a third-party service connection — that is the connection rail). A link is just an agreement (engagementKind "link"). The target OWNER must approve it before unpublished delegates can message each other.',
70
- isAgreementCreation: true,
71
- });
72
- export const createLinkInviteTool = defineTool('create_link_invite', {
73
- message: {
74
- type: 'string',
75
- description: 'Optional note shown to whoever opens the invite (agreement description)',
76
- },
77
- }, async (args, ctx) => {
78
- const { agreement } = await createAgreement({ engagementKind: 'link', description: args['message'] }, creds(ctx));
79
- return {
80
- status: 'open',
81
- inviteId: agreement.agreementId,
82
- claimUrl: `${webAppOrigin()}/app/link-invites/${agreement.agreementId}`,
83
- message: 'Open link invite created. Share claimUrl (or the inviteId) with the counterparty — they claim it in the Ziggs web app or via their claim_link_invite tool. No agent id needed on either side. Revoke with revoke_link to disable.',
84
- agreement: linkSummary(agreement),
85
- };
86
- }, {
87
- description: "Create a shareable OPEN link invite (bilateral agent-to-agent trust) when you do NOT have the counterparty's agent id (e.g. connecting across orgs). Creates an open link agreement proposed to everyone; the recipient forms the link by claiming the returned inviteId.",
88
- isAgreementCreation: true,
89
- });
90
- export const claimLinkInviteTool = defineTool('claim_link_invite', {
91
- agreementId: {
92
- type: 'string',
93
- required: true,
94
- description: 'The invite id (agreementId) shared by the issuer',
95
- },
96
- }, async (args, ctx) => {
97
- const { agreement } = await claimAgreement(args['agreementId'], creds(ctx));
98
- return {
99
- status: 'linked',
100
- message: `Link invite claimed — you are now linked. ${LINK_IS_REACH_ONLY}`,
101
- agreement: linkSummary(agreement),
102
- };
103
- }, {
104
- description: 'Claim an open link invite by its id to form a bilateral link. You become the counterparty and the link activates immediately (cross-org reach). You cannot claim your own invite.',
105
- });
106
- export const listLinksTool = defineTool('list_links', {
107
- status: {
108
- type: 'string',
109
- enum: ['active', 'open', 'cancelled', 'all'],
110
- description: 'active (default) = established links; open = pending proposals/invites awaiting approval or claim; cancelled = revoked; all = every link regardless of status',
111
- },
112
- }, async (args, ctx) => {
113
- const status = args['status'] ?? 'active';
114
- if (!['active', 'open', 'cancelled', 'all'].includes(status)) {
115
- throw new Error('status must be one of active, open, cancelled, all');
116
- }
117
- const links = await listAgreements({
118
- engagementKind: 'link',
119
- ...(status === 'all' ? {} : { status }),
120
- }, creds(ctx));
121
- const summaries = links.map(linkSummary);
122
- const hasActive = links.some((a) => a.status === 'active');
123
- return {
124
- count: summaries.length,
125
- status,
126
- links: summaries,
127
- ...(hasActive ? { nextSteps: LINK_IS_REACH_ONLY } : {}),
128
- };
129
- }, {
130
- description: 'List link agreements for this agent — bilateral agent-to-agent trust relationships (GET /agreements?engagementKind=link). Defaults to ACTIVE links only; pass status to see pending proposals ("open") or revoked ones ("cancelled"). Each item is a link summary: agreementId, status, proposalStatus, parties.',
131
- isGenericFallback: true,
132
- });
133
- export const revokeLinkTool = defineTool('revoke_link', {
134
- agreementId: {
135
- type: 'string',
136
- required: true,
137
- description: 'agreementId of the link agreement (from list_links)',
138
- },
139
- }, async (args, ctx) => {
140
- const result = await revokeAgreement(args['agreementId'], creds(ctx));
141
- return {
142
- status: 'revoked',
143
- message: 'Link revoked — unpublished cross-org reach to this peer is blocked again.',
144
- agreementId: args['agreementId'],
145
- agreement: result.agreement ? linkSummary(result.agreement) : null,
146
- };
147
- }, {
148
- description: 'Revoke a bilateral link agreement (DELETE /agreements/:agreementId). Either party may revoke; cross-org reach ends immediately. For non-link agreements (hire/service/quest), use agreement_revoke — same endpoint, different messaging.',
149
- });
150
- export const LINK_TOOLS = [
151
- requestLinkTool,
152
- createLinkInviteTool,
153
- claimLinkInviteTool,
154
- listLinksTool,
155
- revokeLinkTool,
156
- ];
1
+ import { LINK_CAPABILITIES, requestLinkCapability, createLinkInviteCapability, claimLinkInviteCapability, listLinksCapability, revokeLinkCapability, } from '@ziggs-ai/api-client';
2
+ import { toolFromCapability, toolsFromCapabilities } from '../../../tools/capabilityAdapter.js';
3
+ // ZIG-898 / ZIG-956 — links are agreements with engagementKind 'link':
4
+ // bilateral agent-to-agent trust that gives unpublished delegates cross-org
5
+ // reach. Defined once in api-client's shared capability layer (including the
6
+ // ZIG-670 linkSummary shaping); this file only adapts them onto the SDK.
7
+ export const requestLinkTool = toolFromCapability(requestLinkCapability);
8
+ export const createLinkInviteTool = toolFromCapability(createLinkInviteCapability);
9
+ export const claimLinkInviteTool = toolFromCapability(claimLinkInviteCapability);
10
+ export const listLinksTool = toolFromCapability(listLinksCapability);
11
+ export const revokeLinkTool = toolFromCapability(revokeLinkCapability);
12
+ export const LINK_TOOLS = toolsFromCapabilities(LINK_CAPABILITIES);
@@ -1,10 +1,10 @@
1
- import { type ToolDefinition } from '../../../tools/defineTool.js';
1
+ import type { ToolDefinition } from '../../../tools/defineTool.js';
2
2
  export declare const paymentBalanceTool: ToolDefinition;
3
3
  export declare const paymentTransferTool: ToolDefinition;
4
4
  export declare const paymentWaitForApprovalTool: ToolDefinition;
5
- export declare const paymentResolveWalletTool: ToolDefinition;
6
5
  export declare const paymentHoldTool: ToolDefinition;
7
6
  export declare const paymentReleaseTool: ToolDefinition;
7
+ export declare const paymentResolveWalletTool: ToolDefinition;
8
8
  export declare const paymentIssueGrantTool: ToolDefinition;
9
9
  export declare const paymentAttenuateGrantTool: ToolDefinition;
10
10
  export declare const paymentRevokeGrantTool: ToolDefinition;
@@ -1,161 +1,16 @@
1
- import { defineTool } from '../../../tools/defineTool.js';
2
- import { PaymentsClient } from '@ziggs-ai/api-client';
3
- function clientFor(ctx) {
4
- const operatorKey = ctx?.['operatorKey'];
5
- if (!operatorKey)
6
- throw new Error('operatorKey missing from tool context');
7
- // BACKEND_URL / ZIGGS_BACKEND_URL are the SDK runner's overrides; the shared
8
- // client defaults to HTTP_URL (api-client convention) when neither is set.
9
- const baseUrl = process.env.BACKEND_URL || process.env.ZIGGS_BACKEND_URL || undefined;
10
- return new PaymentsClient(operatorKey, ctx?.['agentId'] || undefined, baseUrl);
11
- }
12
- function rethrowWithContext(error, prefix) {
13
- const e = error;
14
- const wrapped = new Error(`${prefix}: ${e.message}`);
15
- if (e.status !== undefined)
16
- wrapped.status = e.status;
17
- if (e.body !== undefined)
18
- wrapped.body = e.body;
19
- wrapped['cause'] = error;
20
- throw wrapped;
21
- }
22
- function buildCaveats(args) {
23
- const caveats = [];
24
- if (args['maxAmount'] != null)
25
- caveats.push({ type: 'max_amount', value: args['maxAmount'] });
26
- if (args['dailyBudget'] != null)
27
- caveats.push({ type: 'daily_budget', value: args['dailyBudget'] });
28
- if (args['allowedRecipients'] != null)
29
- caveats.push({ type: 'allowed_recipients', value: args['allowedRecipients'] });
30
- if (args['expiresInSeconds'] != null)
31
- caveats.push({ type: 'expires_at', value: Date.now() + args['expiresInSeconds'] * 1000 });
32
- return caveats;
33
- }
34
- export const paymentBalanceTool = defineTool('payment_balance', {}, async (_args, ctx) => {
35
- try {
36
- return await clientFor(ctx).balance();
37
- }
38
- catch (e) {
39
- rethrowWithContext(e, 'Failed to get balance');
40
- }
41
- }, { description: "Check the caller's current wallet balance and available balance (total minus active holds). Use before a transfer to confirm sufficient funds." });
42
- export const paymentTransferTool = defineTool('payment_transfer', { toWalletId: { type: 'string', required: true }, amount: { type: 'number', required: true }, description: 'string', idempotencyKey: 'string', paymentGrantId: 'string' }, async (args, ctx) => {
43
- if (!args['toWalletId'])
44
- throw new Error('toWalletId is required');
45
- if (!args['amount'] || args['amount'] <= 0)
46
- throw new Error('amount must be positive');
47
- const client = clientFor(ctx);
48
- let result;
49
- try {
50
- result = await client.transfer({ to: args['toWalletId'], amount: Math.round(args['amount']), description: args['description'] || 'Agent-initiated transfer', idempotencyKey: args['idempotencyKey'], paymentGrantId: args['paymentGrantId'] });
51
- }
52
- catch (e) {
53
- rethrowWithContext(e, 'Transfer failed');
54
- }
55
- if (result['status'] === 'approval_required') {
56
- return { status: 'approval_required', approvalId: result['approvalId'] || null, expiresAt: result['expiresAt'] || null, reason: result['reason'] || null, amount: args['amount'], toWalletId: result['toWalletId'],
57
- message: 'Transfer paused: the wallet owner must approve this amount.',
58
- next_actions: [{ tool: 'payment_wait_for_approval', when: 'You expect a quick decision (≤2 min) and can wait inline.', args: { approvalId: result['approvalId'], timeoutMs: 120000 } }, { tool: 'task_update_plan_step', when: 'You want to abandon the transfer and route around it.' }] };
59
- }
60
- return { status: 'transferred', transactionId: result['transactionId'] || null, amount: args['amount'], toWalletId: result['toWalletId'] };
61
- });
62
- export const paymentWaitForApprovalTool = defineTool('payment_wait_for_approval', { approvalId: { type: 'string', required: true }, timeoutMs: 'number', pollMs: 'number' }, async (args, ctx) => {
63
- if (!args['approvalId'])
64
- throw new Error('approvalId is required');
65
- try {
66
- const result = await clientFor(ctx).waitForApproval(args['approvalId'], { timeoutMs: args['timeoutMs'], pollMs: args['pollMs'] });
67
- return { status: result['status'], approvalId: args['approvalId'], transactionId: result['transactionId'] || null, approval: result['approval'] || null };
68
- }
69
- catch (e) {
70
- rethrowWithContext(e, 'wait_for_approval failed');
71
- }
72
- });
73
- export const paymentResolveWalletTool = defineTool('payment_resolve_wallet', { userId: 'string', agentId: 'string' }, async (args, ctx) => {
74
- if (!args['userId'] && !args['agentId'])
75
- throw new Error('Provide userId or agentId to resolve a wallet');
76
- try {
77
- const wallet = await clientFor(ctx).resolve({ userId: args['userId'], agentId: args['agentId'] });
78
- return { walletId: wallet?.['walletId'] || null, ownerId: wallet?.['ownerId'] || null, currency: wallet?.['currency'] || 'pez', status: wallet?.['status'] || null };
79
- }
80
- catch (e) {
81
- rethrowWithContext(e, 'Failed to resolve wallet');
82
- }
83
- }, { description: 'Look up a walletId by userId or agentId. Use before a transfer when you only know the recipient by their platform ID.' });
84
- export const paymentHoldTool = defineTool('payment_hold', { amount: { type: 'number', required: true }, description: 'string', idempotencyKey: 'string' }, async (args, ctx) => {
85
- if (!args['amount'] || args['amount'] <= 0)
86
- throw new Error('amount must be positive');
87
- try {
88
- const result = await clientFor(ctx).hold({ amount: Math.round(args['amount']), description: args['description'] || 'Agent escrow hold', idempotencyKey: args['idempotencyKey'] });
89
- return { status: 'held', transactionId: result['transaction']?.['transactionId'] || null, amount: args['amount'] };
90
- }
91
- catch (e) {
92
- rethrowWithContext(e, 'Hold failed');
93
- }
94
- }, { description: 'Pre-authorize (escrow) funds without moving them. Use to reserve payment at agreement formation; release with payment_release once work is complete or to refund if work is cancelled.' });
95
- export const paymentReleaseTool = defineTool('payment_release', { holdId: { type: 'string', required: true }, action: { type: 'string', required: true }, toWalletId: 'string', idempotencyKey: 'string' }, async (args, ctx) => {
96
- if (!args['holdId'])
97
- throw new Error('holdId is required');
98
- if (args['action'] !== 'complete' && args['action'] !== 'refund')
99
- throw new Error("action must be 'complete' or 'refund'");
100
- if (args['action'] === 'complete' && !args['toWalletId'])
101
- throw new Error('toWalletId is required when action=complete');
102
- try {
103
- const result = await clientFor(ctx).release({ holdId: args['holdId'], action: args['action'], toWalletId: args['toWalletId'], idempotencyKey: args['idempotencyKey'] });
104
- return { status: args['action'] === 'complete' ? 'settled' : 'refunded', transactionId: result['transaction']?.['transactionId'] || null, holdId: args['holdId'], action: args['action'] };
105
- }
106
- catch (e) {
107
- rethrowWithContext(e, 'Release failed');
108
- }
109
- }, { description: "Settle or refund an escrow hold. Use action='complete' to transfer held funds to toWalletId (work done), or action='refund' to return funds to the sender (work cancelled)." });
110
- const issueGrantHandler = async (args, ctx) => {
111
- if (!args['holderId'])
112
- throw new Error('holderId is required');
113
- try {
114
- const caveats = buildCaveats(args);
115
- const result = await clientFor(ctx).issueGrant({ holderId: args['holderId'], caveats });
116
- const grant = result['grant'];
117
- return { grantId: grant?.['grantId'] || null, holderId: grant?.['holderId'] || args['holderId'], caveats: grant?.['caveats'] || caveats, expiresAt: grant?.['expiresAt'] || null };
118
- }
119
- catch (e) {
120
- rethrowWithContext(e, 'Failed to issue payment grant');
121
- }
122
- };
123
- export const paymentIssueGrantTool = defineTool('payment_issue_grant', { holderId: { type: 'string', required: true }, maxAmount: 'number', dailyBudget: 'number', allowedRecipients: ['string'], expiresInSeconds: 'number' }, issueGrantHandler);
124
- const attenuateGrantHandler = async (args, ctx) => {
125
- const grantId = args['grantId'];
126
- if (!grantId)
127
- throw new Error('grantId is required');
128
- if (!args['holderId'])
129
- throw new Error('holderId is required');
130
- try {
131
- const caveats = buildCaveats(args);
132
- const result = await clientFor(ctx).attenuateGrant({ grantId, holderId: args['holderId'], caveats });
133
- const grant = result['grant'];
134
- return { grantId: grant?.['grantId'] || null, parentGrantId: grant?.['parentGrantId'] || grantId, holderId: grant?.['holderId'] || args['holderId'], caveats: grant?.['caveats'] || caveats, expiresAt: grant?.['expiresAt'] || null };
135
- }
136
- catch (e) {
137
- rethrowWithContext(e, 'Failed to attenuate payment grant');
138
- }
139
- };
140
- export const paymentAttenuateGrantTool = defineTool('payment_attenuate_grant', { grantId: { type: 'string', required: true }, holderId: { type: 'string', required: true }, maxAmount: 'number', dailyBudget: 'number', allowedRecipients: ['string'], expiresInSeconds: 'number' }, attenuateGrantHandler);
141
- const revokeGrantHandler = async (args, ctx) => {
142
- const grantId = args['grantId'];
143
- if (!grantId)
144
- throw new Error('grantId is required');
145
- try {
146
- const result = await clientFor(ctx).revokeGrant(grantId);
147
- return { status: 'revoked', grantId, revoked: result['revoked'] ?? null };
148
- }
149
- catch (e) {
150
- rethrowWithContext(e, 'Failed to revoke payment grant');
151
- }
152
- };
153
- export const paymentRevokeGrantTool = defineTool('payment_revoke_grant', { grantId: { type: 'string', required: true } }, revokeGrantHandler);
154
- // ZIG-893 — payment_list_grants retired; the wallet rail is now part of the
155
- // unified list_grants tool (scopeKind: 'wallet'). Grant *mutations* stay on the
156
- // payment rail (issue/attenuate/revoke below).
157
- export const PAYMENT_TOOLS = [
158
- paymentBalanceTool, paymentTransferTool, paymentWaitForApprovalTool,
159
- paymentHoldTool, paymentReleaseTool, paymentResolveWalletTool,
160
- paymentIssueGrantTool, paymentAttenuateGrantTool, paymentRevokeGrantTool,
161
- ];
1
+ import { PAYMENT_CAPABILITIES, paymentBalanceCapability, paymentTransferCapability, paymentWaitForApprovalCapability, paymentHoldCapability, paymentReleaseCapability, paymentResolveWalletCapability, paymentIssueGrantCapability, paymentAttenuateGrantCapability, paymentRevokeGrantCapability, } from '@ziggs-ai/api-client';
2
+ import { toolFromCapability, toolsFromCapabilities } from '../../../tools/capabilityAdapter.js';
3
+ // ZIG-956 — the wallet toolset is defined once in api-client's shared
4
+ // capability layer; this file only adapts it onto the SDK surface. Safety is
5
+ // unchanged: money-moving calls stay policy-gated server-side, and there is
6
+ // deliberately NO approve/decide tool on the agent surface.
7
+ export const paymentBalanceTool = toolFromCapability(paymentBalanceCapability);
8
+ export const paymentTransferTool = toolFromCapability(paymentTransferCapability);
9
+ export const paymentWaitForApprovalTool = toolFromCapability(paymentWaitForApprovalCapability);
10
+ export const paymentHoldTool = toolFromCapability(paymentHoldCapability);
11
+ export const paymentReleaseTool = toolFromCapability(paymentReleaseCapability);
12
+ export const paymentResolveWalletTool = toolFromCapability(paymentResolveWalletCapability);
13
+ export const paymentIssueGrantTool = toolFromCapability(paymentIssueGrantCapability);
14
+ export const paymentAttenuateGrantTool = toolFromCapability(paymentAttenuateGrantCapability);
15
+ export const paymentRevokeGrantTool = toolFromCapability(paymentRevokeGrantCapability);
16
+ export const PAYMENT_TOOLS = toolsFromCapabilities(PAYMENT_CAPABILITIES);
@@ -0,0 +1,11 @@
1
+ import { type ToolDefinition } from './defineTool.js';
2
+ import type { CapabilityDefinition } from '@ziggs-ai/api-client';
3
+ /**
4
+ * ZIG-956 — the SDK surface adapter for shared capability definitions
5
+ * (api-client `capabilities/`). One capability (schema + handler + guidance)
6
+ * becomes a defineTool ToolDefinition: the neutral param DSL feeds defineTool's
7
+ * JSON-schema converter unchanged, and the handler runs with creds pulled from
8
+ * tool context plus the SDK runner's base-URL overrides.
9
+ */
10
+ export declare function toolFromCapability(cap: CapabilityDefinition): ToolDefinition;
11
+ export declare function toolsFromCapabilities(caps: CapabilityDefinition[]): ToolDefinition[];
@@ -0,0 +1,30 @@
1
+ import { defineTool } from './defineTool.js';
2
+ /**
3
+ * ZIG-956 — the SDK surface adapter for shared capability definitions
4
+ * (api-client `capabilities/`). One capability (schema + handler + guidance)
5
+ * becomes a defineTool ToolDefinition: the neutral param DSL feeds defineTool's
6
+ * JSON-schema converter unchanged, and the handler runs with creds pulled from
7
+ * tool context plus the SDK runner's base-URL overrides.
8
+ */
9
+ export function toolFromCapability(cap) {
10
+ return defineTool(cap.names.sdk, cap.params, async (args, ctx) => {
11
+ const operatorKey = ctx?.['operatorKey'];
12
+ const agentId = ctx?.['agentId'];
13
+ if (!operatorKey)
14
+ throw new Error('operatorKey missing from tool context');
15
+ if (cap.needsAgentId && !agentId)
16
+ throw new Error('agentId missing from tool context');
17
+ const env = {
18
+ creds: { operatorKey, ...(agentId ? { agentId } : {}) },
19
+ // BACKEND_URL / ZIGGS_BACKEND_URL are the SDK runner's overrides; the
20
+ // shared clients default to HTTP_URL (api-client convention) otherwise.
21
+ baseUrl: process.env.BACKEND_URL || process.env.ZIGGS_BACKEND_URL || undefined,
22
+ webUrl: process.env.ZIGGS_WEB_URL,
23
+ surface: 'sdk',
24
+ };
25
+ return cap.handler(args, env);
26
+ }, { description: cap.descriptions.sdk, ...(cap.sdkOptions ?? {}) });
27
+ }
28
+ export function toolsFromCapabilities(caps) {
29
+ return caps.map(toolFromCapability);
30
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ziggs-ai/agent-sdk",
3
- "version": "0.4.0",
3
+ "version": "0.5.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.3.0",
29
+ "@ziggs-ai/api-client": "^0.4.0",
30
30
  "ajv": "^8.17.1",
31
31
  "dotenv": "^17.2.3",
32
32
  "openai": "^4.0.0",