@ziggs-ai/agent-sdk 0.11.1 → 0.13.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.
- package/CHANGELOG.md +11 -0
- package/dist/AgentHost.d.ts +27 -5
- package/dist/AgentHost.js +110 -15
- package/dist/agent/ClaudeSdkAgent.js +2 -2
- package/dist/agent/claudeSdkProtocol.d.ts +1 -1
- package/dist/agent/claudeSdkProtocol.js +3 -2
- package/dist/agent/claudeSdkTools.js +46 -10
- package/dist/cognition/validateContext.js +4 -1
- package/dist/context/batch.d.ts +3 -0
- package/dist/context/batch.js +8 -5
- package/dist/context/classifyEnvelope.js +4 -5
- package/dist/context/routingLabels.js +2 -3
- package/dist/formatters/AgreementFormatter.d.ts +7 -1
- package/dist/formatters/AgreementFormatter.js +17 -8
- package/dist/formatters/HistoryFormatter.d.ts +5 -0
- package/dist/formatters/HistoryFormatter.js +16 -7
- package/dist/index.d.ts +4 -2
- package/dist/index.js +8 -1
- package/dist/ingress/normalizeIncoming.d.ts +3 -8
- package/dist/runtime/PromptBuilder.js +32 -22
- package/dist/runtime/buildOutcome.js +4 -4
- package/dist/runtime/mandateDeclaration.d.ts +9 -0
- package/dist/runtime/mandateDeclaration.js +62 -0
- package/dist/runtime/runTurn.js +8 -0
- package/dist/server/ConnectionPool.d.ts +19 -0
- package/dist/server/ConnectionPool.js +30 -0
- package/dist/server/OutboxBuffer.d.ts +1 -1
- package/dist/server/ZiggsEffectHandler.d.ts +1 -1
- package/dist/server/ZiggsEffectHandler.js +6 -2
- package/dist/server/agreements/AgreementService.d.ts +19 -10
- package/dist/server/agreements/AgreementService.js +22 -11
- package/dist/server/inboxLoop.d.ts +6 -6
- package/dist/server/inboxLoop.js +31 -18
- package/dist/server/proactive/ProactiveTrigger.d.ts +7 -1
- package/dist/server/proactive/ProactiveTrigger.js +2 -2
- package/dist/server/requestMatch.d.ts +5 -0
- package/dist/server/{questMatch.js → requestMatch.js} +2 -2
- package/dist/server/runLauncher.js +24 -3
- package/dist/server/tools/index.d.ts +2 -2
- package/dist/server/tools/index.js +2 -2
- package/dist/server/tools/tier1/protocolRunner.js +16 -10
- package/dist/server/tools/tier1/protocolTools.js +12 -7
- package/dist/server/tools/tier2/linkTools.d.ts +1 -1
- package/dist/server/tools/tier2/linkTools.js +11 -9
- package/dist/server/tools/tier2/marketplaceTools.js +2 -2
- package/dist/server/tools/tier2/mcpConnectionTools.d.ts +33 -0
- package/dist/server/tools/tier2/mcpConnectionTools.js +138 -6
- package/dist/server/tools/tier2/paymentTools.d.ts +0 -8
- package/dist/server/tools/tier2/paymentTools.js +8 -12
- package/dist/types.d.ts +25 -10
- package/package.json +2 -2
- package/dist/server/questMatch.d.ts +0 -5
|
@@ -89,19 +89,145 @@ export const mcpToolsListTool = defineTool('mcp_tools_list', {
|
|
|
89
89
|
type: 'string',
|
|
90
90
|
description: 'Grant to use. Omit to use the live grant found on that connection.',
|
|
91
91
|
},
|
|
92
|
+
names: {
|
|
93
|
+
type: 'array',
|
|
94
|
+
items: { type: 'string' },
|
|
95
|
+
description: 'Tool names to return the FULL parameters (inputSchema) for. Omit to get the names and descriptions only — a whole server of schemas is tens of thousands of characters and gets cut, which is worse than not asking.',
|
|
96
|
+
},
|
|
92
97
|
}, async (args, ctx) => {
|
|
93
98
|
const t = await resolveTarget(ctx, args['connectionId'], args['grantId']);
|
|
99
|
+
/**
|
|
100
|
+
* Names first, schemas on demand — the same two-step this platform's own MCP
|
|
101
|
+
* surface settled on. One Linear server answers 53 tools and 78,000
|
|
102
|
+
* characters of schema; asking for all of it fills a wake with parameters for
|
|
103
|
+
* tools the agent will not call, and the cut lands mid-schema where the half
|
|
104
|
+
* that survives still looks complete.
|
|
105
|
+
*/
|
|
106
|
+
const wanted = args['names']?.filter((n) => typeof n === 'string' && n);
|
|
94
107
|
const tools = await withClient(ctx, t.connectionId, t.grantId, async (c) => {
|
|
95
108
|
const res = await c.listTools();
|
|
96
|
-
|
|
109
|
+
const rows = res.tools ?? [];
|
|
110
|
+
const picked = wanted?.length ? rows.filter((tool) => wanted.includes(tool.name)) : rows;
|
|
111
|
+
return picked.map((tool) => ({
|
|
97
112
|
name: tool.name,
|
|
98
113
|
description: tool.description ?? null,
|
|
114
|
+
/**
|
|
115
|
+
* The parameters, which this used to drop.
|
|
116
|
+
*
|
|
117
|
+
* Without them an agent has to GUESS what a tool takes, and a guessed
|
|
118
|
+
* argument is not an error — most servers ignore what they do not know
|
|
119
|
+
* and answer with an empty list. That empty list then reads as "nothing
|
|
120
|
+
* happened": a weekly digest reported one item shipped for a week with
|
|
121
|
+
* fifty, because `status`, `filter.state.name` and `updatedAfter` were
|
|
122
|
+
* all invented and all silently matched nothing.
|
|
123
|
+
*/
|
|
124
|
+
...(wanted?.length ? { inputSchema: tool.inputSchema ?? null } : {}),
|
|
99
125
|
}));
|
|
100
126
|
});
|
|
101
|
-
|
|
127
|
+
const missing = wanted?.filter((n) => !tools.some((row) => row.name === n)) ?? [];
|
|
128
|
+
const bounded = boundMcpResult({ tools }, MAX_MCP_TOOLS_LIST_CHARS);
|
|
129
|
+
return {
|
|
130
|
+
connectionId: t.connectionId,
|
|
131
|
+
grantId: t.grantId,
|
|
132
|
+
...bounded.result,
|
|
133
|
+
...(bounded.truncated ? { truncated: bounded.truncated } : {}),
|
|
134
|
+
...(missing.length ? { notFound: missing } : {}),
|
|
135
|
+
...(wanted?.length
|
|
136
|
+
? {}
|
|
137
|
+
: {
|
|
138
|
+
note: 'Names and descriptions only. Call again with names:["<tool>"] to get that tool\'s parameters before you pass any arguments — a parameter the server does not declare is usually ignored, and the empty result reads as real emptiness.',
|
|
139
|
+
}),
|
|
140
|
+
};
|
|
102
141
|
}, {
|
|
103
|
-
description: "List the tools a connected MCP server exposes, through the Ziggs gateway — the owner's credential stays server-side. The grant may allow only some
|
|
142
|
+
description: "List the tools a connected MCP server exposes, through the Ziggs gateway. Names and descriptions by default; pass names:[\"<tool>\"] for those tools' full parameters (inputSchema) — the owner's credential stays server-side. Read the schema before calling a tool with arguments: a parameter the server does not declare is usually ignored rather than rejected, so a guessed filter comes back as an empty result that looks like real emptiness. The grant may allow only some tools; a call outside it is refused by the gateway, not by you.",
|
|
104
143
|
});
|
|
144
|
+
/**
|
|
145
|
+
* How much of one MCP tool result a wake will carry.
|
|
146
|
+
*
|
|
147
|
+
* A remote MCP server hands back the upstream API's payload verbatim, and those
|
|
148
|
+
* are sized for programs, not for a context window: GitHub's `list_pull_requests`
|
|
149
|
+
* with no paging argument answered ~150,000 characters. Past the runtime's limit
|
|
150
|
+
* the result stops being a result — the Claude SDK spills an oversized tool
|
|
151
|
+
* result to a file, and a hosted agent has no filesystem tool to read it with, so
|
|
152
|
+
* the wake ends in "the answer was too big and I cannot see it". Same class of
|
|
153
|
+
* problem as an MCP server that ships a 30K-character tool description, one layer
|
|
154
|
+
* later.
|
|
155
|
+
*
|
|
156
|
+
* A bounded answer with a marker beats an unreadable one: the model can act on
|
|
157
|
+
* "truncated, narrow the call", and it cannot act on a file path.
|
|
158
|
+
*/
|
|
159
|
+
export const MAX_MCP_RESULT_CHARS = 24_000;
|
|
160
|
+
/**
|
|
161
|
+
* Tool schemas are bigger than tool results and are read once per wake, so they
|
|
162
|
+
* get their own, larger ceiling — a schema cut in half is worse than useless,
|
|
163
|
+
* because the half that survives looks complete.
|
|
164
|
+
*/
|
|
165
|
+
export const MAX_MCP_TOOLS_LIST_CHARS = 60_000;
|
|
166
|
+
const TRUNCATION_ADVICE = 'Narrow the call instead of repeating it: most servers take paging (perPage/page) ' +
|
|
167
|
+
'or filter arguments, and a named target usually has a narrower tool than a list.';
|
|
168
|
+
/**
|
|
169
|
+
* Bound one MCP result, telling the model plainly what was cut.
|
|
170
|
+
*
|
|
171
|
+
* Text blocks are cut individually rather than the JSON being sliced, because a
|
|
172
|
+
* sliced JSON string is invalid and a model handed invalid JSON reports the
|
|
173
|
+
* server as broken.
|
|
174
|
+
*/
|
|
175
|
+
export function boundMcpResult(result, max = MAX_MCP_RESULT_CHARS) {
|
|
176
|
+
const whole = JSON.stringify(result) ?? '';
|
|
177
|
+
if (whole.length <= max)
|
|
178
|
+
return { result };
|
|
179
|
+
const content = result?.content;
|
|
180
|
+
if (!Array.isArray(content)) {
|
|
181
|
+
return {
|
|
182
|
+
result: { text: whole.slice(0, max) },
|
|
183
|
+
truncated: {
|
|
184
|
+
originalChars: whole.length,
|
|
185
|
+
keptChars: max,
|
|
186
|
+
note: `Result was ${whole.length} characters and is cut to ${max}. ${TRUNCATION_ADVICE}`,
|
|
187
|
+
},
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
const kept = [];
|
|
191
|
+
let used = 0;
|
|
192
|
+
let dropped = 0;
|
|
193
|
+
for (const block of content) {
|
|
194
|
+
const text = block?.text;
|
|
195
|
+
if (typeof text !== 'string') {
|
|
196
|
+
const size = (JSON.stringify(block) ?? '').length;
|
|
197
|
+
if (used + size > max) {
|
|
198
|
+
dropped += 1;
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
used += size;
|
|
202
|
+
kept.push(block);
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
const room = max - used;
|
|
206
|
+
if (room <= 0) {
|
|
207
|
+
dropped += 1;
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
if (text.length <= room) {
|
|
211
|
+
used += text.length;
|
|
212
|
+
kept.push(block);
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
kept.push({
|
|
216
|
+
...block,
|
|
217
|
+
text: `${text.slice(0, room)}\n\n[…cut here: this block was ${text.length} characters]`,
|
|
218
|
+
});
|
|
219
|
+
used = max;
|
|
220
|
+
}
|
|
221
|
+
return {
|
|
222
|
+
result: { ...result, content: kept },
|
|
223
|
+
truncated: {
|
|
224
|
+
originalChars: whole.length,
|
|
225
|
+
keptChars: used,
|
|
226
|
+
droppedBlocks: dropped,
|
|
227
|
+
note: `Result was ${whole.length} characters, over the ${max}-character limit, so it is cut. What you have is the beginning, not the whole answer — do not report it as complete. ${TRUNCATION_ADVICE}`,
|
|
228
|
+
},
|
|
229
|
+
};
|
|
230
|
+
}
|
|
105
231
|
export const mcpToolCallTool = defineTool('mcp_tool_call', {
|
|
106
232
|
tool: {
|
|
107
233
|
type: 'string',
|
|
@@ -125,12 +251,18 @@ export const mcpToolCallTool = defineTool('mcp_tool_call', {
|
|
|
125
251
|
if (!name)
|
|
126
252
|
throw new Error('tool is required');
|
|
127
253
|
const t = await resolveTarget(ctx, args['connectionId'], args['grantId']);
|
|
128
|
-
const
|
|
254
|
+
const raw = await withClient(ctx, t.connectionId, t.grantId, (c) => c.callTool({
|
|
129
255
|
name,
|
|
130
256
|
arguments: args['args'] ?? {},
|
|
131
257
|
}));
|
|
132
|
-
|
|
258
|
+
const bounded = boundMcpResult(raw);
|
|
259
|
+
return {
|
|
260
|
+
connectionId: t.connectionId,
|
|
261
|
+
tool: name,
|
|
262
|
+
result: bounded.result,
|
|
263
|
+
...(bounded.truncated ? { truncated: bounded.truncated } : {}),
|
|
264
|
+
};
|
|
133
265
|
}, {
|
|
134
|
-
description: "Call one tool on a connected MCP server through the Ziggs gateway. Use this for a connection whose provider is a remote MCP server — connection_proxy is for named connectors (github, jira) and refuses these with \"Unknown provider: mcp\". The gateway injects the owner's token upstream and checks the tool name against your grant, so you never hold the credential. Discover names with mcp_tools_list, or read allowed_actions off the grant in grant_list.",
|
|
266
|
+
description: "Call one tool on a connected MCP server through the Ziggs gateway. Use this for a connection whose provider is a remote MCP server — connection_proxy is for named connectors (github, jira) and refuses these with \"Unknown provider: mcp\". The gateway injects the owner's token upstream and checks the tool name against your grant, so you never hold the credential. Discover names with mcp_tools_list, or read allowed_actions off the grant in grant_list. Results are bounded: a payload over the limit comes back cut, with a `truncated` field saying so — pass the server's paging or filter arguments rather than repeating the call.",
|
|
135
267
|
});
|
|
136
268
|
export const MCP_CONNECTION_TOOLS = [mcpToolsListTool, mcpToolCallTool];
|
|
@@ -1,11 +1,3 @@
|
|
|
1
1
|
import type { ToolDefinition } from '../../../tools/defineTool.js';
|
|
2
2
|
export declare const paymentBalanceTool: ToolDefinition;
|
|
3
|
-
export declare const paymentTransferTool: ToolDefinition;
|
|
4
|
-
export declare const paymentWaitForApprovalTool: ToolDefinition;
|
|
5
|
-
export declare const paymentHoldTool: ToolDefinition;
|
|
6
|
-
export declare const paymentReleaseTool: ToolDefinition;
|
|
7
|
-
export declare const paymentResolveWalletTool: ToolDefinition;
|
|
8
|
-
export declare const paymentIssueGrantTool: ToolDefinition;
|
|
9
|
-
export declare const paymentAttenuateGrantTool: ToolDefinition;
|
|
10
|
-
export declare const paymentRevokeGrantTool: ToolDefinition;
|
|
11
3
|
export declare const PAYMENT_TOOLS: ToolDefinition[];
|
|
@@ -1,16 +1,12 @@
|
|
|
1
|
-
import { PAYMENT_CAPABILITIES, paymentBalanceCapability
|
|
1
|
+
import { PAYMENT_CAPABILITIES, paymentBalanceCapability } from '@ziggs-ai/api-client';
|
|
2
2
|
import { toolFromCapability, toolsFromCapabilities } from '../../../tools/capabilityAdapter.js';
|
|
3
3
|
// the wallet toolset is defined once in api-client's shared
|
|
4
|
-
// capability layer; this file only adapts it onto the SDK surface.
|
|
5
|
-
//
|
|
6
|
-
//
|
|
4
|
+
// capability layer; this file only adapts it onto the SDK surface.
|
|
5
|
+
//
|
|
6
|
+
// The movement verbs are gone from both agent surfaces —
|
|
7
|
+
// transfer, hold, release, wait_for_approval, resolve_wallet, and the three
|
|
8
|
+
// grant mutations. Points are a meter, and an agent does not move the meter on
|
|
9
|
+
// its own. Agreements still settle in points server-side, so nothing an agent
|
|
10
|
+
// needs in order to be paid went with them. Reading your own balance survives.
|
|
7
11
|
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
12
|
export const PAYMENT_TOOLS = toolsFromCapabilities(PAYMENT_CAPABILITIES);
|
package/dist/types.d.ts
CHANGED
|
@@ -4,15 +4,11 @@
|
|
|
4
4
|
* One outcome vocabulary, one ctx shape, one transition signature.
|
|
5
5
|
* Every state — parked or thinking — re-enters with exactly one Outcome.
|
|
6
6
|
*/
|
|
7
|
+
import type { AgreementParties } from '@ziggs-ai/api-client';
|
|
7
8
|
import type { AgentNeed } from './tools/needs.js';
|
|
8
9
|
export interface AgreementRef {
|
|
9
10
|
agreementId: string;
|
|
10
|
-
parties?:
|
|
11
|
-
creator?: string;
|
|
12
|
-
provider?: string;
|
|
13
|
-
payer?: string;
|
|
14
|
-
proposedTo?: string;
|
|
15
|
-
};
|
|
11
|
+
parties?: AgreementParties;
|
|
16
12
|
proposal?: {
|
|
17
13
|
status?: 'pending' | 'approved' | 'rejected' | 'cancelled';
|
|
18
14
|
};
|
|
@@ -328,11 +324,23 @@ export interface LlmResponse {
|
|
|
328
324
|
completionTokens?: number;
|
|
329
325
|
};
|
|
330
326
|
}
|
|
327
|
+
/**
|
|
328
|
+
* One participant on the snapshot roster. `kind` is the declared holder kind the
|
|
329
|
+
* server projection stamped, so a consumer reads what a participant IS instead
|
|
330
|
+
* of inferring it from which array the row arrived in. Optional because a
|
|
331
|
+
* backend that predates it simply omits the field.
|
|
332
|
+
*/
|
|
333
|
+
export interface SnapshotParticipant {
|
|
334
|
+
role?: string;
|
|
335
|
+
kind?: string;
|
|
336
|
+
isYou?: boolean;
|
|
337
|
+
[key: string]: unknown;
|
|
338
|
+
}
|
|
331
339
|
export interface ContextSnapshot {
|
|
332
340
|
history: HistoryEntry[];
|
|
333
341
|
agreements: unknown[];
|
|
334
|
-
agents:
|
|
335
|
-
users:
|
|
342
|
+
agents: SnapshotParticipant[];
|
|
343
|
+
users: SnapshotParticipant[];
|
|
336
344
|
timelineSummary?: string | null;
|
|
337
345
|
latestSequence?: string | null;
|
|
338
346
|
unavailable?: boolean;
|
|
@@ -472,10 +480,17 @@ export type Effect = {
|
|
|
472
480
|
stateId?: string;
|
|
473
481
|
/** Replay-stable write keys for this wake — write-capable tools derive their idempotency key from here, never from the LLM. */
|
|
474
482
|
writeKeys?: DeliveryWriteKeys;
|
|
475
|
-
}
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* `receiverId` is optional: in a room with one other participant the backend
|
|
486
|
+
* resolves the recipient from the room itself, which is the only answer
|
|
487
|
+
* available when the wake's sender belongs to a DIFFERENT room.
|
|
488
|
+
* Name a receiver whenever the room holds more than two parties.
|
|
489
|
+
*/
|
|
490
|
+
| {
|
|
476
491
|
kind: 'send-message';
|
|
477
492
|
sessionId: string;
|
|
478
|
-
receiverId
|
|
493
|
+
receiverId?: string;
|
|
479
494
|
text: string;
|
|
480
495
|
messageId?: string;
|
|
481
496
|
} | {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ziggs-ai/agent-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.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.
|
|
29
|
+
"@ziggs-ai/api-client": "0.12.0",
|
|
30
30
|
"ajv": "^8.17.1",
|
|
31
31
|
"dotenv": "^17.2.3",
|
|
32
32
|
"openai": "^4.0.0",
|