@ziggs-ai/agent-sdk 0.35.1 → 0.37.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 CHANGED
@@ -12,6 +12,11 @@
12
12
 
13
13
  ## Unreleased
14
14
 
15
+ ### Changed (breaking)
16
+
17
+ - Hosted Claude agents load Ziggs tools on demand. Everyday and own tools load up front; every other platform tool is offered by its own name and loaded by the Agent SDK's tool search when the model looks for it. The `tools` / `tool_read` / `tool_write` catalog is gone from the hosted brain.
18
+ - `isHostedCatalogOnly` is now `isHostedDeferred`; `hostedModelTools(all)` takes no executor and returns every tool; `HostedToolExecute` is removed.
19
+
15
20
  ### Fixed
16
21
 
17
22
  - Hosted Claude agents can discover and invoke the access explanation tool named by their recovery instructions.
package/dist/Agent.js CHANGED
@@ -13,7 +13,6 @@ import { CLAUDE_SDK_TOOLS } from './brain/claudeSdkTools.js';
13
13
  import { resolveClaudeModel } from './brain/claudeCognition.js';
14
14
  import { CHAT_TOOLS } from './platform/tools/tier2/chatTools.js';
15
15
  import { contextReadTool, openTool, accessExplainTool } from './platform/tools/tier2/contextTools.js';
16
- import { hostedModelTools } from './brain/hostedToolSurface.js';
17
16
  import { EventQueue } from './platform/EventQueue.js';
18
17
  import { StateTimers } from './platform/StateTimers.js';
19
18
  import { laneKeyFor } from './platform/laneKey.js';
@@ -95,8 +94,8 @@ function createToolManager(options) {
95
94
  // chat_open is always-on so claim/provider kickoff (FSM) and the
96
95
  // Claude always-on set can open the buyer chat after a store claim. CHAT_TOOLS
97
96
  // is also folded into CLAUDE_SDK_TOOLS; Map registration makes duplicates a no-op.
98
- // wake_at stays registered for the FSM and for the hosted catalog. The Claude
99
- // model reaches it through tool_write; an agent that can be handed a task
97
+ // wake_at stays registered for the FSM and for the hosted model, where it is
98
+ // deferred behind tool search; an agent that can be handed a task
100
99
  // with a check-back rule but cannot arm a wake would have to busy-wait, and
101
100
  // there is nothing to busy-wait with.
102
101
  // open / context_read are the hosted twins of the MCP everyday set.
@@ -110,18 +109,6 @@ function createToolManager(options) {
110
109
  const platformTools = [...taskTools, ...CHAT_TOOLS, ...browseTools, ...claudeSdkTools, ...everydayContext, ...WAKE_TOOLS];
111
110
  warnOnShadowedPlatformTools(platformTools, ownTools, options.agentId);
112
111
  toolManager.registerAll([...platformTools, ...ownTools]);
113
- // The Claude bridge executes every mounted name through the tool manager,
114
- // including the catalog. Register those three here or a wake that calls
115
- // `tools` dies with "Tool not found" before the dispatcher runs.
116
- if (isClaudeCognition(options.cognition)) {
117
- const catalog = hostedModelTools([...platformTools, ...ownTools], (name, args, ctx) => toolManager.executeTool(name, args, ctx));
118
- for (const tool of catalog) {
119
- const name = toolName(tool);
120
- if ((name === 'tools' || name === 'tool_read' || name === 'tool_write') && !toolManager.getTool(name)) {
121
- toolManager.register(tool);
122
- }
123
- }
124
- }
125
112
  return toolManager;
126
113
  }
127
114
  /**
@@ -1162,13 +1149,6 @@ export class Agent {
1162
1149
  const args = effect.args;
1163
1150
  if (typeof args.taskId === 'string')
1164
1151
  return args.taskId;
1165
- const nested = args.args;
1166
- if ((effect.name === 'tool_read' || effect.name === 'tool_write')
1167
- && nested && typeof nested === 'object' && !Array.isArray(nested)) {
1168
- const inner = nested.taskId;
1169
- if (typeof inner === 'string')
1170
- return inner;
1171
- }
1172
1152
  return undefined;
1173
1153
  };
1174
1154
  const eff = async (effect) => {
@@ -7,7 +7,7 @@ import { bootstrapCtx } from '../runtime/bootstrapCtx.js';
7
7
  import { createDeliveryWriteKeys } from '../runtime/deliveryWriteKeys.js';
8
8
  import { CLAUDE_SDK_PROTOCOL } from './claudeSdkProtocol.js';
9
9
  import { hireIdsFromEvent, memorySectionForWake } from '../memory/MemoryStore.js';
10
- import { hostedModelTools } from './hostedToolSurface.js';
10
+ import { hostedModelTools, isHostedDeferred } from './hostedToolSurface.js';
11
11
  import { buildZiggsSdkMcpServer } from './toolManagerToSdkMcp.js';
12
12
  import { claudeQueryOptions, buildClaudeEventPrompt, summarizeContext, extractAssistantText, } from './claudeCognition.js';
13
13
  import { assertHostedAgentZiggsTools } from './hostedAgentConnection.js';
@@ -418,7 +418,7 @@ export class ClaudeBrain {
418
418
  let sdkMcpServer = this.sdkMcpByLane.get(lane);
419
419
  if (!sdkMcpServer) {
420
420
  const availableTools = await this.toolManager.getAvailableTools();
421
- const mounted = hostedModelTools(availableTools, (name, args, ctx) => this.toolManager.executeTool(name, args, ctx));
421
+ const mounted = hostedModelTools(availableTools);
422
422
  sdkMcpServer = buildZiggsSdkMcpServer(mounted, () => {
423
423
  const ctx = this.toolContextByLane.get(lane);
424
424
  if (!ctx) {
@@ -427,7 +427,8 @@ export class ClaudeBrain {
427
427
  return ctx;
428
428
  }, {
429
429
  name: 'ziggs',
430
- instructions: 'In-process Ziggs tools. Everyday tools and your own tools are loaded by name. tools lists the rest; tool_read and tool_write call them. agreement_revoke is called by name only.',
430
+ instructions: 'In-process Ziggs tools. Everyday tools and your own tools are loaded; every other Ziggs tool loads when you search for it.',
431
+ alwaysLoad: (name) => !isHostedDeferred(name),
431
432
  });
432
433
  this.sdkMcpByLane.set(lane, sdkMcpServer);
433
434
  }
@@ -58,7 +58,10 @@ export function claudeQueryOptions(params) {
58
58
  mcpServers: params.mcpServers,
59
59
  // The agent's ONLY capabilities are its MCP tools — no built-in file/bash/web
60
60
  // tools, no project/user/plugin MCP config, no local settings / CLAUDE.md.
61
- tools: [],
61
+ // ToolSearch is the one built-in: it loads a deferred Ziggs tool's schema
62
+ // when the model looks for it. Without it the SDK ignores deferral and
63
+ // sends every schema on every wake.
64
+ tools: ['ToolSearch'],
62
65
  strictMcpConfig: true,
63
66
  settingSources: [],
64
67
  permissionMode: 'bypassPermissions',
@@ -16,7 +16,7 @@ export const CLAUDE_SDK_PROTOCOL = `You are an autonomous agent on a Ziggs team,
16
16
 
17
17
  Each wake hands you ONE event (or a small batch). Read workBrief for current work and audience. Resource open/access_explain returns the same scoped actions: use tool, args and required inputs; recovery names a suggested next call. Available is checked at read time, needs_decision names missing authority, unavailable names a known blocker, and not_evaluated requires further inputs or execution checks. It is backend-resolved context, not a permission token. Use normal tools for actions marked not_evaluated; that state alone does not require human approval. Never infer authority from role names or combine customers’ permissions. actingFor.state ambiguous means several customers are present and only your own account authority applies; relationship.side is the agreement side you hold and authorizes nothing by itself. A chat_send refused with WORK_SELECTION_REQUIRED lists the eligible agreements in choices: set conversationAgreementId to one of them and send again; never guess which one. Handle the event, then stop.
18
18
 
19
- Ziggs tools loaded with their full schemas are ${loadedByName}, plus any tools that belong to you specifically. Every other Ziggs tool is one call away: \`tools\` lists each one (name and one line); pass \`describe\` to \`tools\` for the schema before you call a tool you have not used. A read goes through \`tool_read\` { tool, args }; a change goes through \`tool_write\` { tool, args }. \`agreement_revoke\` is irreversible, so call it by its own name — the dispatchers refuse it. When the lines below name \`context_snapshot\`, \`wake_at\`, \`task_list\`, \`task_update_plan_step\`, \`grant_list\`, or an agreement verb, that is how you reach it.
19
+ Ziggs tools loaded with their full schemas are ${loadedByName}, plus any tools that belong to you specifically. Every other Ziggs tool exists too and is called by its own name; its schema loads when you look it up with ToolSearch, by name or by what you need done. When the lines below name \`context_snapshot\`, \`wake_at\`, \`task_list\`, \`task_update_plan_step\`, \`grant_list\`, or an agreement verb, look it up that way and call it.
20
20
 
21
21
  Your prose and final summary are private host notes, not messages to the people or agents you work with. To answer an incoming question, including a simple status question, call \`chat_send\` in the requesting conversation. Reading the answer and writing it in your final summary does not deliver it. Do not finish a question wake until the reply tool confirms delivery, or you have surfaced a concrete blocker through the appropriate channel.
22
22
 
@@ -1,14 +1,15 @@
1
1
  import { type ToolDefinition } from '../tools/defineTool.js';
2
- export type HostedToolExecute = (name: string, args: Record<string, unknown>, context: Record<string, unknown>) => Promise<unknown>;
3
2
  type Lane = 'read-only' | 'write' | 'destructive';
4
3
  export declare function hostedToolLane(name: string): Lane;
5
- /** A Ziggs platform tool the model reaches through the catalog, not by schema. */
6
- export declare function isHostedCatalogOnly(name: string): boolean;
7
4
  /**
8
- * The tool block the hosted model is shown.
9
- *
10
- * `execute` runs a catalog call through the ToolManager. Omit it when only
11
- * the schemas are being priced — the dispatchers then refuse to run.
5
+ * A Ziggs platform tool whose schema loads when the model searches for it,
6
+ * rather than on every wake. Everyday tools and the agent's own tools are
7
+ * never deferred.
12
8
  */
13
- export declare function hostedModelTools(all: ToolDefinition[], execute?: HostedToolExecute): ToolDefinition[];
9
+ export declare function isHostedDeferred(name: string): boolean;
10
+ /**
11
+ * The tools the hosted model is offered: every registered tool, each by its
12
+ * own name and schema. Which of them load up front is `isHostedDeferred`.
13
+ */
14
+ export declare function hostedModelTools(all: ToolDefinition[]): ToolDefinition[];
14
15
  export {};
@@ -1,43 +1,41 @@
1
1
  /**
2
2
  * What the hosted Claude brain mounts into the model.
3
3
  *
4
- * The MCP door already lists only the everyday set plus a catalog. This is
5
- * that same cut for the in-process brain: everyday SDK tools stay loaded by
6
- * name, agent-owned tools stay loaded by name, and every other Ziggs platform
7
- * tool is named by `tools` and called through `tool_read` or `tool_write`.
8
- * The tools stay registered on the ToolManager — the FSM brain and the
9
- * dispatcher both call them — they just stop riding every wake as schemas.
4
+ * Every tool the agent has is mounted with its own schema. Everyday SDK tools
5
+ * and the agent's own tools load on every wake; every other Ziggs platform
6
+ * tool is deferred, and the Agent SDK's tool search loads its schema when the
7
+ * model looks for it. The model sees each tool by its own name and calls it
8
+ * directly — there is no catalog to read and no dispatcher in between, so the
9
+ * host's per-tool permission and the trace name the real tool.
10
10
  */
11
- import { EVERYDAY_SDK_TOOLS, annotationForSdkTool, backendToolPick } from '@ziggs-ai/api-client';
12
- import { credsFromToolContext } from '../tools/toolCreds.js';
13
- import { defineTool, toolName } from '../tools/defineTool.js';
11
+ import { EVERYDAY_SDK_TOOLS, annotationForSdkTool } from '@ziggs-ai/api-client';
12
+ import { toolName } from '../tools/defineTool.js';
14
13
  /**
15
- * Platform tools written by hand, not as a shared capability. Their lane is
16
- * what the dispatcher uses; a name here is also a platform name, so it leaves
17
- * the model block unless it is in the everyday set.
14
+ * Platform tools written by hand, not as a shared capability. A name here is
15
+ * a platform name, so it is deferred unless it is in the everyday set.
18
16
  */
19
17
  const HAND_LANE = {
20
18
  context_snapshot: 'read-only',
21
- chat_send: 'write',
19
+ // Sends, commitments, overwrites, cancellations and revocations are
20
+ // destructive; only private additive writes are plain writes.
21
+ chat_send: 'destructive',
22
22
  wake_at: 'write',
23
- agreement_propose: 'write',
24
- agreement_subcontract: 'write',
25
- agreement_respond: 'write',
26
- agreement_counter: 'write',
23
+ agreement_propose: 'destructive',
24
+ agreement_subcontract: 'destructive',
25
+ agreement_respond: 'destructive',
26
+ agreement_counter: 'destructive',
27
27
  agreement_get: 'read-only',
28
28
  agreement_revoke: 'destructive',
29
- agreement_fulfill: 'write',
30
- task_set_result: 'write',
31
- task_update_plan_step: 'write',
29
+ agreement_fulfill: 'destructive',
30
+ task_set_result: 'destructive',
31
+ task_update_plan_step: 'destructive',
32
32
  task_create: 'write',
33
- task_replace_plan: 'write',
33
+ task_replace_plan: 'destructive',
34
34
  mcp_tools_list: 'read-only',
35
- mcp_tool_call: 'write',
35
+ mcp_tool_call: 'destructive',
36
36
  connection_list_grants: 'read-only',
37
37
  };
38
38
  const EVERYDAY = new Set(EVERYDAY_SDK_TOOLS);
39
- const CATALOG_NAMES = ['tools', 'tool_read', 'tool_write'];
40
- const CATALOG = new Set(CATALOG_NAMES);
41
39
  export function hostedToolLane(name) {
42
40
  const hand = HAND_LANE[name];
43
41
  if (hand)
@@ -50,172 +48,27 @@ export function hostedToolLane(name) {
50
48
  function isPlatform(name) {
51
49
  return name in HAND_LANE || annotationForSdkTool(name) !== undefined;
52
50
  }
53
- /** A Ziggs platform tool the model reaches through the catalog, not by schema. */
54
- export function isHostedCatalogOnly(name) {
55
- return isPlatform(name) && !EVERYDAY.has(name);
56
- }
57
- function oneLine(description, name) {
58
- const text = String(description ?? name).replace(/\s+/g, ' ').trim();
59
- const sentence = text.split(/(?<=\.)\s/)[0] ?? text;
60
- return sentence.length > 180 ? `${sentence.slice(0, 177)}...` : sentence;
61
- }
62
- function callLine(name, lane, native) {
63
- if (lane === 'destructive') {
64
- return `call it by name only: ${name}; the dispatchers refuse it`;
65
- }
66
- const dispatcher = lane === 'read-only' ? 'tool_read' : 'tool_write';
67
- if (native) {
68
- return `call it by name: ${name}; ${dispatcher} { tool: "${name}", args } also works`;
69
- }
70
- return `call via ${dispatcher} { tool, args }`;
71
- }
72
- function rowFor(tool) {
73
- const name = toolName(tool) ?? '';
74
- const lane = hostedToolLane(name);
75
- const native = !isHostedCatalogOnly(name);
76
- return {
77
- name,
78
- line: oneLine(tool.schema.function['description'], name),
79
- readOnly: lane === 'read-only',
80
- native,
81
- call: callLine(name, lane, native),
82
- };
83
- }
84
- function refuseWrongLane(name, lane, via) {
85
- if (lane === 'destructive') {
86
- throw new Error(`${name} cannot be called through ${via}: it is irreversible, and it is loaded directly so the call names what it does. Call ${name} by name.`);
87
- }
88
- const readOnly = lane === 'read-only';
89
- const askedRead = via === 'tool_read';
90
- if (readOnly === askedRead)
91
- return;
92
- throw new Error(readOnly
93
- ? `${name} is read-only: call it with tool_read { tool, args }, not tool_write.`
94
- : `${name} changes state: call it with tool_write { tool, args }, not tool_read.`);
95
- }
96
51
  /**
97
- * The Claude bridge does not call a tool's handler. It emits `tool-call`, and
98
- * the effect handler looks the name up on the ToolManager. A catalog call has
99
- * to go back through that same effect so the target still gets the timeline,
100
- * the write keys, and the wake scope. A direct executor is only for a caller
101
- * that has no effect handler (a schema probe, a unit test).
52
+ * A Ziggs platform tool whose schema loads when the model searches for it,
53
+ * rather than on every wake. Everyday tools and the agent's own tools are
54
+ * never deferred.
102
55
  */
103
- async function callCatalogTarget(name, payload, ctx, execute) {
104
- const eff = ctx.eff;
105
- const sessionId = typeof ctx.chatId === 'string' ? ctx.chatId
106
- : typeof ctx.sessionId === 'string' ? ctx.sessionId
107
- : undefined;
108
- if (typeof eff === 'function' && sessionId) {
109
- const result = await eff({
110
- kind: 'tool-call',
111
- sessionId,
112
- name,
113
- args: payload,
114
- senderId: typeof ctx.senderId === 'string' ? ctx.senderId : undefined,
115
- senderType: typeof ctx.senderType === 'string' ? ctx.senderType : undefined,
116
- onBehalfOfAgreementId: typeof ctx.onBehalfOfAgreementId === 'string' ? ctx.onBehalfOfAgreementId : undefined,
117
- serviceAgreementId: typeof ctx.serviceAgreementId === 'string' ? ctx.serviceAgreementId : undefined,
118
- runId: typeof ctx.runId === 'string' ? ctx.runId : undefined,
119
- writeKeys: ctx.writeKeys,
120
- memory: ctx.memory,
121
- });
122
- const settled = result;
123
- if (settled && settled.ok === false)
124
- throw new Error(settled.error ?? `${name} failed`);
125
- if (settled && 'ok' in settled)
126
- return settled.result;
127
- return result;
128
- }
129
- if (!execute)
130
- throw new Error('hosted catalog has no executor');
131
- return execute(name, payload, ctx);
132
- }
133
- function catalogTools(present, execute) {
134
- const list = () => [...present.values()].map(rowFor).sort((a, b) => a.name.localeCompare(b.name));
135
- const toolsTool = defineTool('tools', {
136
- need: { type: 'string', description: 'Describe what you need to do. Suggests a tool and its schema; falls back to the catalog when unsure.' },
137
- search: {
138
- type: 'string',
139
- description: 'Filter by name or the one-line description. Omit to list every tool.',
140
- },
141
- describe: {
142
- type: 'array',
143
- items: { type: 'string' },
144
- description: 'Tool names whose full schema you want before calling them.',
145
- },
146
- }, async (args, ctx) => {
147
- const describe = Array.isArray(args.describe) ? args.describe.map(String) : [];
148
- if (describe.length > 0) {
149
- return describe.map((name) => {
150
- const tool = present.get(name);
151
- if (!tool) {
152
- return { name, error: `No such tool: ${name}. Call tools for the full list of names.` };
153
- }
154
- return { ...rowFor(tool), schema: tool.schema.function };
155
- });
156
- }
157
- const need = typeof args.need === 'string' ? args.need.trim() : '';
158
- if (need) {
159
- const rows = list();
160
- try {
161
- const pick = await backendToolPick(credsFromToolContext(ctx, 'tools'))(need, rows.map(({ name, line }) => ({ name, line })));
162
- const tool = pick?.how === 'pick' && pick.tool ? present.get(pick.tool) : undefined;
163
- if (tool)
164
- return { suggestion: { ...rowFor(tool), schema: tool.schema.function }, confidence: pick?.confidence };
165
- }
166
- catch { /* Missing credentials or picker failure still leaves every tool available. */ }
167
- return { tools: rows, note: 'No confident suggestion. Choose from the catalog; no action was taken.' };
168
- }
169
- const q = typeof args.search === 'string' ? args.search.toLowerCase() : '';
170
- return list().filter((row) => !q || row.name.includes(q) || row.line.toLowerCase().includes(q));
171
- }, {
172
- description: 'Every Ziggs tool that exists: name plus one line. A handful are already loaded by name; the rest are called through tool_read or tool_write. Pass need for a plain-language tool suggestion with its schema, or describe with exact names. Suggestions never execute tools. Nothing is hidden.',
173
- });
174
- const dispatch = (via) => async (args, ctx) => {
175
- const name = typeof args.tool === 'string' ? args.tool : '';
176
- if (!name || !present.has(name)) {
177
- throw new Error(`No such tool: ${name || '(missing)'}. Call tools for the full list of names.`);
178
- }
179
- refuseWrongLane(name, hostedToolLane(name), via);
180
- const payload = args.args && typeof args.args === 'object' && !Array.isArray(args.args)
181
- ? args.args
182
- : {};
183
- return callCatalogTarget(name, payload, ctx, execute);
184
- };
185
- const toolRead = defineTool('tool_read', {
186
- tool: { type: 'string', required: true, description: 'Read-only tool name from tools.' },
187
- args: { type: 'object', description: 'Arguments for that tool, as its schema defines them.' },
188
- }, dispatch('tool_read'), {
189
- description: 'Call a read-only Ziggs tool by name. A tool that writes is refused here and pointed at tool_write. Look it up with tools describe=[name] first when you do not already know its parameters. The result is exactly what the tool itself returns.',
190
- });
191
- const toolWrite = defineTool('tool_write', {
192
- tool: { type: 'string', required: true, description: 'Writing tool name from tools.' },
193
- args: { type: 'object', description: 'Arguments for that tool, as its schema defines them.' },
194
- }, dispatch('tool_write'), {
195
- description: 'Call a Ziggs tool that creates or changes something, by name. Read-only tools are refused here and pointed at tool_read. Irreversible tools (agreement_revoke) are called by their own name only — this dispatcher refuses them. Look the tool up with tools describe=[name] first when you do not already know its parameters.',
196
- });
197
- return [toolsTool, toolRead, toolWrite];
56
+ export function isHostedDeferred(name) {
57
+ return isPlatform(name) && !EVERYDAY.has(name);
198
58
  }
199
59
  /**
200
- * The tool block the hosted model is shown.
201
- *
202
- * `execute` runs a catalog call through the ToolManager. Omit it when only
203
- * the schemas are being priced — the dispatchers then refuse to run.
60
+ * The tools the hosted model is offered: every registered tool, each by its
61
+ * own name and schema. Which of them load up front is `isHostedDeferred`.
204
62
  */
205
- export function hostedModelTools(all, execute) {
206
- const present = new Map();
63
+ export function hostedModelTools(all) {
64
+ const seen = new Set();
65
+ const out = [];
207
66
  for (const tool of all) {
208
67
  const name = toolName(tool);
209
- if (!name || CATALOG.has(name))
210
- continue;
211
- present.set(name, tool);
212
- }
213
- const visible = [];
214
- for (const [name, tool] of present) {
215
- if (isHostedCatalogOnly(name))
68
+ if (!name || seen.has(name))
216
69
  continue;
217
- visible.push(tool);
70
+ seen.add(name);
71
+ out.push(tool);
218
72
  }
219
- visible.push(...catalogTools(present, execute));
220
- return visible;
73
+ return out;
221
74
  }
@@ -5,8 +5,12 @@ import type { ClaudeSdkToolContext } from './claudeSdkTools.js';
5
5
  * Map Ziggs `ToolDefinition`s to Claude Agent SDK MCP tools. Each handler routes
6
6
  * execution through `eff({ kind: 'tool-call' })` — the shared executor.
7
7
  */
8
- export declare function buildSdkToolDefs(toolDefs: ToolDefinition[], getContext: () => ClaudeSdkToolContext): Array<SdkMcpToolDefinition>;
8
+ export declare function buildSdkToolDefs(toolDefs: ToolDefinition[], getContext: () => ClaudeSdkToolContext,
9
+ /** Which tools load on every wake; the rest wait for tool search. Default: all. */
10
+ alwaysLoad?: (name: string) => boolean): Array<SdkMcpToolDefinition>;
9
11
  export declare function buildZiggsSdkMcpServer(toolDefs: ToolDefinition[], getContext: () => ClaudeSdkToolContext, options?: {
10
12
  name?: string;
11
13
  instructions?: string;
14
+ /** Per tool; a tool it returns false for is deferred behind tool search. */
15
+ alwaysLoad?: (name: string) => boolean;
12
16
  }): import("@anthropic-ai/claude-agent-sdk").McpSdkServerConfigWithInstance;
@@ -85,7 +85,9 @@ function toCallToolError(err) {
85
85
  * Map Ziggs `ToolDefinition`s to Claude Agent SDK MCP tools. Each handler routes
86
86
  * execution through `eff({ kind: 'tool-call' })` — the shared executor.
87
87
  */
88
- export function buildSdkToolDefs(toolDefs, getContext) {
88
+ export function buildSdkToolDefs(toolDefs, getContext,
89
+ /** Which tools load on every wake; the rest wait for tool search. Default: all. */
90
+ alwaysLoad = () => true) {
89
91
  return toolDefs.map((def) => {
90
92
  const fn = def.schema.function;
91
93
  const name = String(fn['name'] ?? '');
@@ -99,7 +101,7 @@ export function buildSdkToolDefs(toolDefs, getContext) {
99
101
  name,
100
102
  description,
101
103
  inputSchema,
102
- _meta: { 'anthropic/alwaysLoad': true },
104
+ _meta: { 'anthropic/alwaysLoad': alwaysLoad(name) },
103
105
  handler: async (args) => {
104
106
  try {
105
107
  const ctx = getContext();
@@ -147,7 +149,6 @@ export function buildZiggsSdkMcpServer(toolDefs, getContext, options = {}) {
147
149
  name: options.name ?? 'ziggs',
148
150
  version: '1.0.0',
149
151
  instructions: options.instructions,
150
- tools: buildSdkToolDefs(toolDefs, getContext),
151
- alwaysLoad: true,
152
+ tools: buildSdkToolDefs(toolDefs, getContext, options.alwaysLoad),
152
153
  });
153
154
  }
package/dist/index.d.ts CHANGED
@@ -69,8 +69,7 @@ export { PollRegistry, declaredPolls, type PollBinding, type PollContext, type P
69
69
  export { PollRunner, type PollRunnerOptions, type WakeTarget } from './platform/polls/PollRunner.js';
70
70
  export { PROTOCOL_TOOLS, agreementProposeTool, agreementSubcontractTool, agreementRespondTool, agreementClaimTool, agreementCounterTool, agreementGetTool, agreementRevokeTool, agreementFulfillTool, taskCreateTool, taskSetResultTool, taskUpdatePlanStepTool, dispatchProtocolOp, PAYMENT_TOOLS, paymentBalanceTool, CONNECTION_TOOLS, connectionProxyTool, connectionListGrantsTool, requestConnectionTool, MCP_CONNECTION_TOOLS, mcpToolCallTool, mcpToolsListTool, DISCOVERY_TOOLS, agentSearchTool, agentGetTool, MARKETPLACE_TOOLS, marketplaceViewTool, CONTEXT_TOOLS, contextReadTool, contextRequestTool, accessRequestTool, contextDelegateTool, contextExpandReachTool, contextDiscoverGrantableTool, GRANTS_TOOLS, listGrantsTool, PLAN_TOOLS, taskReplacePlanTool, LINK_TOOLS, linkProposeTool, linkListTool, INTRODUCTION_TOOLS, introductionMintTool, introductionRedeemTool, introductionListTool, introductionRevokeTool, ARTIFACT_TOOLS, recordArtifactTool, CHAT_TOOLS, openConversationTool, LISTING_TOOLS, } from './platform/tools/index.js';
71
71
  export { CLAUDE_SDK_TOOLS } from './brain/claudeSdkTools.js';
72
- export { hostedModelTools, isHostedCatalogOnly, hostedToolLane } from './brain/hostedToolSurface.js';
73
- export type { HostedToolExecute } from './brain/hostedToolSurface.js';
72
+ export { hostedModelTools, isHostedDeferred, hostedToolLane } from './brain/hostedToolSurface.js';
74
73
  export { PROTOCOL_TOOL_NAMES, PROTOCOL_TOOL_TO_OPERATION, mapProtocolToolToOperation, isProtocolToolName } from './tasks/protocolRegistry.js';
75
74
  export { InMemoryStore, FileMemoryStore } from './memory/MemoryStore.js';
76
75
  export type { MemoryStore } from './memory/MemoryStore.js';
package/dist/index.js CHANGED
@@ -83,7 +83,7 @@ PAYMENT_TOOLS, paymentBalanceTool, CONNECTION_TOOLS, connectionProxyTool, connec
83
83
  // The always-on toolset for the hosted Claude cognition (parity guard
84
84
  // reads this as part of the real SDK registry).
85
85
  export { CLAUDE_SDK_TOOLS } from './brain/claudeSdkTools.js';
86
- export { hostedModelTools, isHostedCatalogOnly, hostedToolLane } from './brain/hostedToolSurface.js';
86
+ export { hostedModelTools, isHostedDeferred, hostedToolLane } from './brain/hostedToolSurface.js';
87
87
  export { PROTOCOL_TOOL_NAMES, PROTOCOL_TOOL_TO_OPERATION, mapProtocolToolToOperation, isProtocolToolName } from './tasks/protocolRegistry.js';
88
88
  export { InMemoryStore, FileMemoryStore } from './memory/MemoryStore.js';
89
89
  export { classifyIncomingEvent } from './context/classifyEnvelope.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ziggs-ai/agent-sdk",
3
- "version": "0.35.1",
3
+ "version": "0.37.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.284",
27
27
  "@anthropic-ai/sdk": "^0.110.0",
28
28
  "@modelcontextprotocol/sdk": "^1.29.0",
29
- "@ziggs-ai/api-client": "0.34.1",
29
+ "@ziggs-ai/api-client": "0.36.0",
30
30
  "ajv": "^8.17.1",
31
31
  "dotenv": "^17.2.3",
32
32
  "openai": "^4.0.0",