@ziggs-ai/ziggs-mcp 0.9.12 → 0.10.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/README.md +2 -2
- package/dist/capabilityAdapter.js +4 -4
- package/dist/protocol/delegateProtocol.d.ts +1 -1
- package/dist/protocol/delegateProtocol.js +1 -1
- package/dist/strictParams.d.ts +12 -2
- package/dist/strictParams.js +16 -1
- package/dist/toolAnnotations.d.ts +7 -3
- package/dist/toolAnnotations.js +9 -13
- package/dist/toolError.js +1 -1
- package/dist/tools.js +33 -109
- package/dist/trustTools.js +3 -3
- package/examples/claude-code.md +1 -1
- package/package.json +2 -2
- package/skills/ziggs/.cursorrules +1 -1
- package/skills/ziggs/SKILL.md +2 -2
- package/skills/ziggs/references/grants-and-approvals.md +1 -1
- package/skills/ziggs/references/inbox-rhythm.md +1 -1
- package/skills/ziggs/references/reporting-convention.md +1 -1
package/README.md
CHANGED
|
@@ -43,7 +43,7 @@ Skill only (no plugin): `skills/ziggs/SKILL.md` ships in the package for org pro
|
|
|
43
43
|
|------|------|
|
|
44
44
|
| List chats / discover reach | `ziggs_chat_list` or `ziggs_grant_list` |
|
|
45
45
|
| Send message | `ziggs_chat_send` |
|
|
46
|
-
| Propose + respond | `
|
|
46
|
+
| Propose + respond | `ziggs_agreement_commission`, `ziggs_agreement_respond` |
|
|
47
47
|
|
|
48
48
|
---
|
|
49
49
|
|
|
@@ -157,7 +157,7 @@ Startup validates the key shape, expiry (JWT `exp`), and agent resolution — er
|
|
|
157
157
|
| `ziggs_chat_list` | `GET /chats/mine` |
|
|
158
158
|
| `ziggs_chat_open` | `POST /chats` |
|
|
159
159
|
| `ziggs_chat_send` | `POST /chats/:id/messages` |
|
|
160
|
-
| `
|
|
160
|
+
| `ziggs_agreement_commission` | `POST /agreements/proposals` (direct), marketplace publish (broadcast: quest / standing offer), or `POST /agreements` (link) — one propose grammar |
|
|
161
161
|
| `ziggs_agreement_respond` | `PUT /agreements/:id/approvals/:partyId` (owner principal; approves direct hire, service, and `link` proposals) |
|
|
162
162
|
| `ziggs_agreement_claim` | `POST /agreements/:id/claim` — claim any open broadcast (quest / offer / hand-off / link invite) |
|
|
163
163
|
| `ziggs_agreement_subcontract` | `POST /agreements` delegation under a parent agreement |
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import {
|
|
2
|
+
import { readOnly, write, destructive } from './toolAnnotations.js';
|
|
3
3
|
import { registerStrictTool } from './strictParams.js';
|
|
4
4
|
import { toolError } from './toolError.js';
|
|
5
5
|
/** One JSON text-content result shape for every MCP tool (was copied 3×). */
|
|
@@ -55,10 +55,10 @@ export function toZodShape(params) {
|
|
|
55
55
|
}
|
|
56
56
|
export function registerCapability(server, cap, creds, opts = {}) {
|
|
57
57
|
const annotations = cap.annotation === 'read-only'
|
|
58
|
-
?
|
|
58
|
+
? readOnly(cap.title)
|
|
59
59
|
: cap.annotation === 'destructive'
|
|
60
|
-
?
|
|
61
|
-
:
|
|
60
|
+
? destructive(cap.title)
|
|
61
|
+
: write(cap.title);
|
|
62
62
|
registerStrictTool(server, cap.names.mcp, opts.description ?? cap.descriptions.mcp, toZodShape(cap.params), annotations, async (args) => {
|
|
63
63
|
try {
|
|
64
64
|
const env = { creds, webUrl: opts.webUrl, surface: 'mcp' };
|
|
@@ -23,7 +23,7 @@ export declare const PROTOCOL: {
|
|
|
23
23
|
/** Tasks are the unit of work. */
|
|
24
24
|
readonly task: "Work is a task under an agreement (the ticket). Read it from the inbox — or, if handed a bare taskId, open it with ziggs_task_get — then post progress as plan steps with ziggs_task_replace_plan.";
|
|
25
25
|
/** posted-first: how ANY engagement starts. */
|
|
26
|
-
readonly engage: "Engaging any counterparty follows the ladder: (1) REUSE an active agreement that already covers the work on matching terms; (2) CLAIM their posted listing (browse ziggs_marketplace_view; check listings after ziggs_agent_search) — listings are take-it-or-leave-it, never counter one; (3) POST a quest (
|
|
26
|
+
readonly engage: "Engaging any counterparty follows the ladder: (1) REUSE an active agreement that already covers the work on matching terms; (2) CLAIM their posted listing (browse ziggs_marketplace_view; check listings after ziggs_agent_search) — listings are take-it-or-leave-it, never counter one; (3) POST a quest (ziggs_agreement_quest) when nothing listed fits, and supply claims you; (4) go direct only for bespoke terms, renegotiation, or a named counterparty with no listing — ziggs_agreement_commission when they do the work, ziggs_agreement_bid when you do — most published agents are claim-only and refuse direct proposals with a pointer at their listing. Subcontracting under an active parent is its own rail, unaffected.";
|
|
27
27
|
/** The reporting rule — the heart of the batch. */
|
|
28
28
|
readonly reporting: "Finished work is the task result — set it with ziggs_task_set_result ({ taskId, state, result: { summary, status, links } }). For a heavy deliverable, record a task-bound result artifact (ziggs_artifact_record, contentType result). Never report finished work as a chat message — chat is conversation only; another agent can't consume prose.";
|
|
29
29
|
/** Pull-only hosts have no push channel. */
|
|
@@ -23,7 +23,7 @@ export const PROTOCOL = {
|
|
|
23
23
|
/** Tasks are the unit of work. */
|
|
24
24
|
task: 'Work is a task under an agreement (the ticket). Read it from the inbox — or, if handed a bare taskId, open it with ziggs_task_get — then post progress as plan steps with ziggs_task_replace_plan.',
|
|
25
25
|
/** posted-first: how ANY engagement starts. */
|
|
26
|
-
engage: 'Engaging any counterparty follows the ladder: (1) REUSE an active agreement that already covers the work on matching terms; (2) CLAIM their posted listing (browse ziggs_marketplace_view; check listings after ziggs_agent_search) — listings are take-it-or-leave-it, never counter one; (3) POST a quest (
|
|
26
|
+
engage: 'Engaging any counterparty follows the ladder: (1) REUSE an active agreement that already covers the work on matching terms; (2) CLAIM their posted listing (browse ziggs_marketplace_view; check listings after ziggs_agent_search) — listings are take-it-or-leave-it, never counter one; (3) POST a quest (ziggs_agreement_quest) when nothing listed fits, and supply claims you; (4) go direct only for bespoke terms, renegotiation, or a named counterparty with no listing — ziggs_agreement_commission when they do the work, ziggs_agreement_bid when you do — most published agents are claim-only and refuse direct proposals with a pointer at their listing. Subcontracting under an active parent is its own rail, unaffected.',
|
|
27
27
|
/** The reporting rule — the heart of the batch. */
|
|
28
28
|
reporting: "Finished work is the task result — set it with ziggs_task_set_result ({ taskId, state, result: { summary, status, links } }). For a heavy deliverable, record a task-bound result artifact (ziggs_artifact_record, contentType result). Never report finished work as a chat message — chat is conversation only; another agent can't consume prose.",
|
|
29
29
|
/** Pull-only hosts have no push channel. */
|
package/dist/strictParams.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
-
import type { ToolAnnotations } from '@modelcontextprotocol/sdk/types.js';
|
|
3
2
|
import { z, type ZodRawShape, type ZodObject } from 'zod';
|
|
3
|
+
import type { TitledAnnotations } from './toolAnnotations.js';
|
|
4
4
|
/**
|
|
5
5
|
* registering a tool with a plain `ZodRawShape` lets the MCP SDK
|
|
6
6
|
* wrap it in a default `z.object()`, which *strips* unknown keys instead of
|
|
@@ -20,5 +20,15 @@ export declare function strictParams<S extends ZodRawShape>(toolName: string, sh
|
|
|
20
20
|
* strict. The positional `tool()` overloads only accept a raw shape (a built
|
|
21
21
|
* ZodObject is parsed as annotations there), so the strict schema has to go
|
|
22
22
|
* through `registerTool`'s config object.
|
|
23
|
+
*
|
|
24
|
+
* Annotations must carry a title: a titleless tool displays as its wire name
|
|
25
|
+
* (`ziggs_agreement_fulfill`), and the connector directory flags the surface
|
|
26
|
+
* for it. Taking `TitledAnnotations` rather than `ToolAnnotations` is what
|
|
27
|
+
* makes that a compile error instead of a review catch.
|
|
28
|
+
*
|
|
29
|
+
* The title goes out twice from the one source. `title` is where the current
|
|
30
|
+
* spec puts it; `annotations.title` is where clients written against the
|
|
31
|
+
* 2024-11-05 spec still look, and display precedence is title →
|
|
32
|
+
* annotations.title → name, so a client reading either lands on the same copy.
|
|
23
33
|
*/
|
|
24
|
-
export declare function registerStrictTool<S extends ZodRawShape>(server: McpServer, name: string, description: string, shape: S, annotations:
|
|
34
|
+
export declare function registerStrictTool<S extends ZodRawShape>(server: McpServer, name: string, description: string, shape: S, annotations: TitledAnnotations, cb: Parameters<typeof server.registerTool<never, StrictShape<S>>>[2]): void;
|
package/dist/strictParams.js
CHANGED
|
@@ -26,7 +26,22 @@ export function strictParams(toolName, shape) {
|
|
|
26
26
|
* strict. The positional `tool()` overloads only accept a raw shape (a built
|
|
27
27
|
* ZodObject is parsed as annotations there), so the strict schema has to go
|
|
28
28
|
* through `registerTool`'s config object.
|
|
29
|
+
*
|
|
30
|
+
* Annotations must carry a title: a titleless tool displays as its wire name
|
|
31
|
+
* (`ziggs_agreement_fulfill`), and the connector directory flags the surface
|
|
32
|
+
* for it. Taking `TitledAnnotations` rather than `ToolAnnotations` is what
|
|
33
|
+
* makes that a compile error instead of a review catch.
|
|
34
|
+
*
|
|
35
|
+
* The title goes out twice from the one source. `title` is where the current
|
|
36
|
+
* spec puts it; `annotations.title` is where clients written against the
|
|
37
|
+
* 2024-11-05 spec still look, and display precedence is title →
|
|
38
|
+
* annotations.title → name, so a client reading either lands on the same copy.
|
|
29
39
|
*/
|
|
30
40
|
export function registerStrictTool(server, name, description, shape, annotations, cb) {
|
|
31
|
-
server.registerTool(name, {
|
|
41
|
+
server.registerTool(name, {
|
|
42
|
+
title: annotations.title,
|
|
43
|
+
description,
|
|
44
|
+
inputSchema: strictParams(name, shape),
|
|
45
|
+
annotations,
|
|
46
|
+
}, cb);
|
|
32
47
|
}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import type { ToolAnnotations } from '@modelcontextprotocol/sdk/types.js';
|
|
2
|
+
/** Annotations with a title — the shape `registerStrictTool` demands. */
|
|
3
|
+
export type TitledAnnotations = ToolAnnotations & {
|
|
4
|
+
title: string;
|
|
5
|
+
};
|
|
2
6
|
/** Reads state, never mutates. */
|
|
3
|
-
export declare
|
|
7
|
+
export declare function readOnly(title: string): TitledAnnotations;
|
|
4
8
|
/** Writes state, but additively/reversibly (create, send, grant). */
|
|
5
|
-
export declare
|
|
9
|
+
export declare function write(title: string): TitledAnnotations;
|
|
6
10
|
/** Mutates state irreversibly (revoke). */
|
|
7
|
-
export declare
|
|
11
|
+
export declare function destructive(title: string): TitledAnnotations;
|
package/dist/toolAnnotations.js
CHANGED
|
@@ -1,16 +1,12 @@
|
|
|
1
|
-
// MCP annotation hints so connector UIs (Claude, Cursor, …) can bucket Ziggs
|
|
2
|
-
// tools into "Read only" vs "Actions" instead of one undefined group.
|
|
3
|
-
// `readOnlyHint` drives that split; `destructiveHint` flags the irreversible
|
|
4
|
-
// ones so hosts can warn before running them.
|
|
5
1
|
/** Reads state, never mutates. */
|
|
6
|
-
export
|
|
2
|
+
export function readOnly(title) {
|
|
3
|
+
return { title, readOnlyHint: true };
|
|
4
|
+
}
|
|
7
5
|
/** Writes state, but additively/reversibly (create, send, grant). */
|
|
8
|
-
export
|
|
9
|
-
readOnlyHint: false,
|
|
10
|
-
|
|
11
|
-
};
|
|
6
|
+
export function write(title) {
|
|
7
|
+
return { title, readOnlyHint: false, destructiveHint: false };
|
|
8
|
+
}
|
|
12
9
|
/** Mutates state irreversibly (revoke). */
|
|
13
|
-
export
|
|
14
|
-
readOnlyHint: false,
|
|
15
|
-
|
|
16
|
-
};
|
|
10
|
+
export function destructive(title) {
|
|
11
|
+
return { title, readOnlyHint: false, destructiveHint: true };
|
|
12
|
+
}
|
package/dist/toolError.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
const SCOPE_DENIED_HINT = 'You are not authorized for this scope. To get access: ask the counterparty ' +
|
|
9
9
|
'to issue you a context grant (they run ziggs_context_issue_grant), or propose a ' +
|
|
10
|
-
'bilateral link first (
|
|
10
|
+
'bilateral link first (ziggs_link_propose). Check what you can already ' +
|
|
11
11
|
'reach with ziggs_grant_list / ziggs_context_snapshot.';
|
|
12
12
|
// a human-authority denial is not "try again with more scope": no
|
|
13
13
|
// retry by this caller can ever pass it, because the guard refuses on being an
|
package/dist/tools.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import { getAgreement, getMyAgreements, listMyChats,
|
|
3
|
+
import { getAgreement, getMyAgreements, listMyChats, delegateAgreement, provisionRelayWorkers, respondToAgreement, revokeAgreement, counterAgreement, fulfillAgreement, sendChatMessage, ConnectionsClient, PaymentsClient, ContextReadClient, GrantsClient, InboxClient, createTask, updateTaskState, replaceTaskPlan, listTasks, getTask, getBackendUrl, fetchMyOrgs, fetchSessionAccess, isMcpOAuthDelegateAgentId, GRANTS_CAPABILITIES, AGREEMENT_VERB_CAPABILITIES, CONTEXT_GRANT_SCOPE_KINDS, contextReadCapability, contextExpandReachCapability, contextDiscoverGrantableCapability, recordArtifactCapability, listArtifactsCapability, shareArtifactCapability, attachArtifactCapability, uploadArtifactUrlCapability, completeArtifactFileCapability, downloadArtifactCapability, reextractArtifactCapability, openConversationCapability, connectionProxyCapability, requestConnectionCapability, agreementClaimCapability, marketplaceViewCapability, } from '@ziggs-ai/api-client';
|
|
4
4
|
import { decodeOperatorKeyClaims } from './operatorKey.js';
|
|
5
5
|
import { registerTrustTools } from './trustTools.js';
|
|
6
6
|
import { registerPaymentTools } from './paymentTools.js';
|
|
@@ -17,7 +17,7 @@ function buildRelayCoordinatorTaskBody(opts) {
|
|
|
17
17
|
description: `${title}\nrelay:v1\n${JSON.stringify(opts.payload)}`,
|
|
18
18
|
};
|
|
19
19
|
}
|
|
20
|
-
import {
|
|
20
|
+
import { readOnly, write, destructive } from './toolAnnotations.js';
|
|
21
21
|
import { registerStrictTool } from './strictParams.js';
|
|
22
22
|
import { toolError } from './toolError.js';
|
|
23
23
|
import { registerCapability, registerCapabilities, textResult, } from './capabilityAdapter.js';
|
|
@@ -39,7 +39,7 @@ const ZIGGS_PENDING_DECISIONS_DESCRIPTION = 'Session start summary: approve/reje
|
|
|
39
39
|
// conversation only; finished work goes to the task result. Reporting rule is
|
|
40
40
|
// sourced from the shared const so it can't drift.
|
|
41
41
|
const ZIGGS_SEND_MESSAGE_DESCRIPTION = 'Send a chat message as the acting agent (requires chat membership). ' +
|
|
42
|
-
'Cross-org first contact requires an ACTIVE link first (propose one with
|
|
42
|
+
'Cross-org first contact requires an ACTIVE link first (propose one with ziggs_link_propose, or ziggs_link_create_invite when you lack the agent id; then approved/claimed); without it, messaging an agent outside your org fails with AGENT_NOT_PUBLISHED. ' +
|
|
43
43
|
PROTOCOL.reporting;
|
|
44
44
|
// (revised A4): always-on teaching, not wrong-slot detection. Name the
|
|
45
45
|
// result slot on the artifact_record description and success path so an agent
|
|
@@ -129,8 +129,7 @@ async function loadSessionActionsPayload(creds, cfg, opts) {
|
|
|
129
129
|
// #7 — heavy tool groups pulled out of registerZiggsTools so the
|
|
130
130
|
// lean session-start tier (ZIGGS_MCP_CORE_ONLY) can skip registering them.
|
|
131
131
|
// killed the dedicated publish tools: publishing IS
|
|
132
|
-
//
|
|
133
|
-
// quest, providerId = own id = standing offer). What remains here is the
|
|
132
|
+
// ziggs_agreement_quest and ziggs_agreement_offer. What remains here is the
|
|
134
133
|
// browse view and the relay-provisioning composite.
|
|
135
134
|
function registerMarketplaceTools(server, creds) {
|
|
136
135
|
registerStrictTool(server, 'ziggs_provision_relay_workers', 'Initiator path: provision per-step worker agreements before relay kickoff. Reuses active delegations under the hire, claims standing offers when available, otherwise proposes delegations (worker must approve — never impersonated). Returns relay:v1 payload and POST /tasks body when all steps are active.', {
|
|
@@ -154,7 +153,7 @@ function registerMarketplaceTools(server, creds) {
|
|
|
154
153
|
.boolean()
|
|
155
154
|
.optional()
|
|
156
155
|
.describe('When true and readyForKickoff, also POST /tasks on the hire for relay-coordinator'),
|
|
157
|
-
},
|
|
156
|
+
}, write('Line up workers for a relay'), async ({ hireAgreementId, chatId, inputArtifactIds, steps, kickoff }) => {
|
|
158
157
|
try {
|
|
159
158
|
const result = await provisionRelayWorkers({
|
|
160
159
|
creds,
|
|
@@ -192,7 +191,7 @@ function registerConnectionTools(server, creds) {
|
|
|
192
191
|
registerStrictTool(server, 'ziggs_connection_list', 'Discover the third-party connections (credentials like GitHub/Jira, and remote MCP servers — NOT agent-to-agent Links; see ziggs_link_list for that) you hold grants for, without the owner sharing connectionId/grantId out of band. ' +
|
|
193
192
|
'Returns, per connection: connectionId, provider, and the grant(s) you hold — each as the canonical grant shape (grantId, scope, caveats, and grant health active/expired/revoked). ' +
|
|
194
193
|
'Read-only — never returns credential material. ' +
|
|
195
|
-
'How to use a row: provider "mcp" → ziggs_mcp_tools_list / ziggs_mcp_tool_call; named connectors (github, jira, …) → ziggs_connection_proxy.', {},
|
|
194
|
+
'How to use a row: provider "mcp" → ziggs_mcp_tools_list / ziggs_mcp_tool_call; named connectors (github, jira, …) → ziggs_connection_proxy.', {}, readOnly('List connections you can use'), async () => {
|
|
196
195
|
try {
|
|
197
196
|
const connections = await new ConnectionsClient(creds.operatorKey, creds.agentId).listForHolder();
|
|
198
197
|
return textResult({ connections });
|
|
@@ -213,7 +212,7 @@ function registerConnectionTools(server, creds) {
|
|
|
213
212
|
.string()
|
|
214
213
|
.optional()
|
|
215
214
|
.describe('Grant to use. Omit to use the live grant on that connection.'),
|
|
216
|
-
},
|
|
215
|
+
}, readOnly('List tools on a connected server'), async ({ connectionId, grantId }) => {
|
|
217
216
|
try {
|
|
218
217
|
const t = await resolveMcpConnectionTarget(creds, connectionId, grantId);
|
|
219
218
|
const tools = await withMcpGatewayClient(creds, t.connectionId, t.grantId, async (c) => {
|
|
@@ -251,7 +250,7 @@ function registerConnectionTools(server, creds) {
|
|
|
251
250
|
.string()
|
|
252
251
|
.optional()
|
|
253
252
|
.describe('Grant to use. Omit to use the live grant on that connection.'),
|
|
254
|
-
},
|
|
253
|
+
}, write('Call a tool on a connected server'), async ({ tool, args, connectionId, grantId }) => {
|
|
255
254
|
try {
|
|
256
255
|
if (!tool)
|
|
257
256
|
throw new Error('tool is required');
|
|
@@ -273,7 +272,7 @@ function registerConnectionTools(server, creds) {
|
|
|
273
272
|
registerCapability(server, requestConnectionCapability, creds);
|
|
274
273
|
}
|
|
275
274
|
export function registerZiggsTools(server, creds, cfg) {
|
|
276
|
-
registerStrictTool(server, 'ziggs_auth_status', 'Verify the session binding: acting agent id, owner user id, and org scope. Call after connect before inbox/chats. Includes pendingDecisions summary when approve/reject is waiting. ("Connection" refers only to third-party credential connections, see ziggs_connection_proxy.)', {},
|
|
275
|
+
registerStrictTool(server, 'ziggs_auth_status', 'Verify the session binding: acting agent id, owner user id, and org scope. Call after connect before inbox/chats. Includes pendingDecisions summary when approve/reject is waiting. ("Connection" refers only to third-party credential connections, see ziggs_connection_proxy.)', {}, readOnly('Check session identity'), async () => {
|
|
277
276
|
const claims = decodeOperatorKeyClaims(creds.operatorKey);
|
|
278
277
|
const webOrigin = resolveWebAppOrigin(cfg.ZIGGS_WEB_URL);
|
|
279
278
|
// #4 — delegate access and session actions are independent.
|
|
@@ -361,7 +360,7 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
361
360
|
: 'Next: ziggs_inbox or ziggs_pending_decisions at session start.',
|
|
362
361
|
});
|
|
363
362
|
});
|
|
364
|
-
registerStrictTool(server, 'ziggs_org_list', 'List every org you (the operator) belong to — { orgId, name, kind, role }. Unlike ziggs_grant_list (granted scopes only), this is your full membership — useful before OAuth reconnect when the human wants to pick a target org.', {},
|
|
363
|
+
registerStrictTool(server, 'ziggs_org_list', 'List every org you (the operator) belong to — { orgId, name, kind, role }. Unlike ziggs_grant_list (granted scopes only), this is your full membership — useful before OAuth reconnect when the human wants to pick a target org.', {}, readOnly('List your orgs'), async () => {
|
|
365
364
|
try {
|
|
366
365
|
const orgs = await fetchMyOrgs(creds);
|
|
367
366
|
return textResult({ count: orgs.length, orgs });
|
|
@@ -370,7 +369,7 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
370
369
|
return toolError(e);
|
|
371
370
|
}
|
|
372
371
|
});
|
|
373
|
-
registerStrictTool(server, 'ziggs_pending_decisions', ZIGGS_PENDING_DECISIONS_DESCRIPTION, {},
|
|
372
|
+
registerStrictTool(server, 'ziggs_pending_decisions', ZIGGS_PENDING_DECISIONS_DESCRIPTION, {}, readOnly('Check what needs your attention'), async () => {
|
|
374
373
|
try {
|
|
375
374
|
const payload = await loadSessionActionsPayload(creds, cfg);
|
|
376
375
|
const decisions = (payload.decisions ?? []);
|
|
@@ -388,7 +387,7 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
388
387
|
}
|
|
389
388
|
});
|
|
390
389
|
if (cfg.debugTools) {
|
|
391
|
-
registerStrictTool(server, 'ziggs_smoke_impersonation', '[Internal/debug] Connectivity check for the operator-key impersonation path — lists agreements and snapshots the first chat. Not part of normal delegate workflow; use ziggs_agreement_list / ziggs_context_snapshot instead.', {},
|
|
390
|
+
registerStrictTool(server, 'ziggs_smoke_impersonation', '[Internal/debug] Connectivity check for the operator-key impersonation path — lists agreements and snapshots the first chat. Not part of normal delegate workflow; use ziggs_agreement_list / ziggs_context_snapshot instead.', {}, readOnly('Debug: check the impersonation path'), async () => {
|
|
392
391
|
try {
|
|
393
392
|
const agreements = await getMyAgreements({}, creds);
|
|
394
393
|
const chats = await listMyChats(creds);
|
|
@@ -417,7 +416,7 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
417
416
|
.string()
|
|
418
417
|
.optional()
|
|
419
418
|
.describe('Optional grant id when reading under a context grant'),
|
|
420
|
-
},
|
|
419
|
+
}, readOnly('Catch up on a chat'), async ({ chatId, maxMessages, contextGrantId }) => {
|
|
421
420
|
try {
|
|
422
421
|
const client = new ContextReadClient(creds.operatorKey, creds.agentId);
|
|
423
422
|
const result = await client.snapshot(chatId, {
|
|
@@ -439,7 +438,7 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
439
438
|
.string()
|
|
440
439
|
.optional()
|
|
441
440
|
.describe('Optional filter: pending, approved, rejected, …'),
|
|
442
|
-
},
|
|
441
|
+
}, readOnly('List your agreements'), async ({ scope, proposalStatus }) => {
|
|
443
442
|
try {
|
|
444
443
|
const agreements = await getMyAgreements({
|
|
445
444
|
...(proposalStatus ? { proposalStatus } : {}),
|
|
@@ -451,7 +450,7 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
451
450
|
return toolError(e);
|
|
452
451
|
}
|
|
453
452
|
});
|
|
454
|
-
registerStrictTool(server, 'ziggs_agreement_get', 'Fetch a single agreement by id.', { agreementId: z.string() },
|
|
453
|
+
registerStrictTool(server, 'ziggs_agreement_get', 'Fetch a single agreement by id.', { agreementId: z.string() }, readOnly('Read one agreement'), async ({ agreementId }) => {
|
|
455
454
|
try {
|
|
456
455
|
const agreement = await getAgreement(agreementId, creds);
|
|
457
456
|
if (!agreement)
|
|
@@ -462,7 +461,7 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
462
461
|
return toolError(e);
|
|
463
462
|
}
|
|
464
463
|
});
|
|
465
|
-
registerStrictTool(server, 'ziggs_chat_list', 'List chats the acting agent is a member of (GET /chats/mine).', {},
|
|
464
|
+
registerStrictTool(server, 'ziggs_chat_list', 'List chats the acting agent is a member of (GET /chats/mine).', {}, readOnly('List your chats'), async () => {
|
|
466
465
|
try {
|
|
467
466
|
const chats = await listMyChats(creds);
|
|
468
467
|
return textResult({ count: chats.length, chats });
|
|
@@ -487,7 +486,7 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
487
486
|
.string()
|
|
488
487
|
.optional()
|
|
489
488
|
.describe('Retry-safe key. Reuse the SAME key when re-sending the SAME logical message (e.g. after a network error/timeout) so it is stored and delivered exactly once — the backend dedupes on chatId + messageId. Use a fresh key (or omit) for a genuinely new message.'),
|
|
490
|
-
},
|
|
489
|
+
}, write('Send a chat message'), async ({ chatId, receiverId, text, entryType, idempotencyKey }) => {
|
|
491
490
|
try {
|
|
492
491
|
const result = await sendChatMessage({
|
|
493
492
|
chatId,
|
|
@@ -508,85 +507,10 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
508
507
|
return toolError(e);
|
|
509
508
|
}
|
|
510
509
|
});
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
chatId: z
|
|
516
|
-
.string()
|
|
517
|
-
.optional()
|
|
518
|
-
.describe('Required on a direct proposal; optional on broadcasts and links'),
|
|
519
|
-
description: z.string(),
|
|
520
|
-
providerId: z
|
|
521
|
-
.string()
|
|
522
|
-
.optional()
|
|
523
|
-
.describe(`${AGREEMENT_PROPOSE_PROVIDER_ID_DESCRIPTION} With parentAgreementId = an active hire and this set to its provider, the proposal is a HAND-OFF: the provider stays pinned and proposedTo/claimer is the customer.`),
|
|
524
|
-
price: z
|
|
525
|
-
.number()
|
|
526
|
-
.optional()
|
|
527
|
-
.describe('Amount in CENTS — 500 means $5.00, and ϟ5.00 in the UI. Optional; does not trigger a ' +
|
|
528
|
-
'transfer by itself. Convert BOTH ways or you are off by 100x: a human who says ' +
|
|
529
|
-
'"ϟ2" or "$2 per task" means price 200, not 2; quoting 500 back as "$500" or "ϟ500" ' +
|
|
530
|
-
'is the same mistake inverted. ϟ is the currency symbol the UI shows — it is not cents.'),
|
|
531
|
-
engagementKind: z
|
|
532
|
-
.enum(['hire', 'service', 'link'])
|
|
533
|
-
.optional()
|
|
534
|
-
.describe("'service' (default) = one-off deliverable; 'hire' = ongoing engagement; 'link' = bilateral trust link (no work, no money)"),
|
|
535
|
-
expiresAt: z
|
|
536
|
-
.string()
|
|
537
|
-
.optional()
|
|
538
|
-
.describe('ISO date: the agreement ends (is cancelled, tasks and all) at this time. Omit for a standing agreement.'),
|
|
539
|
-
maxExecutions: z
|
|
540
|
-
.number()
|
|
541
|
-
.int()
|
|
542
|
-
.positive()
|
|
543
|
-
.optional()
|
|
544
|
-
.describe('The agreement auto-fulfills after this many completed tasks. Omit for unlimited tasks.'),
|
|
545
|
-
lifecycle: z
|
|
546
|
-
.enum(['open', 'time-bound', 'count-bound'])
|
|
547
|
-
.optional()
|
|
548
|
-
.describe("Usually inferred: expiresAt → 'time-bound', maxExecutions → 'count-bound', neither → 'open' (standing)."),
|
|
549
|
-
billing: z
|
|
550
|
-
.enum(['total', 'per_task'])
|
|
551
|
-
.optional()
|
|
552
|
-
.describe("How price reads. 'total' (default) = one price for the whole engagement, escrowed now and paid at the end. 'per_task' = a RATE charged for each completed task, paid as work lands — requires a standing (open) agreement, and is the default for a hire. Never send 'per_task' for a one-off price or the payer is charged it once per task."),
|
|
553
|
-
}, WRITE, async ({ proposedTo, chatId, description, providerId, price, engagementKind, expiresAt, maxExecutions, lifecycle, billing, }) => {
|
|
554
|
-
try {
|
|
555
|
-
const { agreement, shape } = await proposeUnified({
|
|
556
|
-
proposedTo,
|
|
557
|
-
chatId,
|
|
558
|
-
description,
|
|
559
|
-
providerId: providerId?.trim() || undefined,
|
|
560
|
-
price,
|
|
561
|
-
engagementKind,
|
|
562
|
-
expiresAt,
|
|
563
|
-
maxExecutions,
|
|
564
|
-
lifecycle,
|
|
565
|
-
billing,
|
|
566
|
-
}, creds);
|
|
567
|
-
// surface the owner-routing rewrite so agents do not think
|
|
568
|
-
// the id they passed was ignored silently.
|
|
569
|
-
const routedProposedTo = shape === 'link' &&
|
|
570
|
-
agreement?.parties?.proposedTo &&
|
|
571
|
-
proposedTo &&
|
|
572
|
-
proposedTo !== agreement.parties.proposedTo
|
|
573
|
-
? `Routed approval to the target agent's owner (${agreement.parties.proposedTo}): a person decides who their delegate trusts. You passed proposedTo=${proposedTo}.`
|
|
574
|
-
: undefined;
|
|
575
|
-
return textResult({
|
|
576
|
-
shape,
|
|
577
|
-
agreement,
|
|
578
|
-
...(routedProposedTo ? { note: routedProposedTo } : {}),
|
|
579
|
-
...(shape === 'quest' || shape === 'offer'
|
|
580
|
-
? {
|
|
581
|
-
nextSteps: 'Published to the marketplace — claimable via ziggs_agreement_claim; it also appears in ziggs_marketplace_view.',
|
|
582
|
-
}
|
|
583
|
-
: {}),
|
|
584
|
-
});
|
|
585
|
-
}
|
|
586
|
-
catch (e) {
|
|
587
|
-
return toolError(e);
|
|
588
|
-
}
|
|
589
|
-
});
|
|
510
|
+
// One verb per shape, replacing the propose dispatch table. The direction
|
|
511
|
+
// of work is in the verb name, so no caller reconstructs a providerId
|
|
512
|
+
// permutation to reach the shape it already knew it wanted.
|
|
513
|
+
registerCapabilities(server, AGREEMENT_VERB_CAPABILITIES, creds);
|
|
590
514
|
registerCapability(server, agreementClaimCapability, creds);
|
|
591
515
|
registerStrictTool(server, 'ziggs_agreement_subcontract', 'Delegate part of an engagement to another agent under an existing parent agreement (a sub-agreement; the worker must approve — never impersonated). Use when you hold an active agreement and want a third agent to do a slice of it. Requires parentAgreementId and the chat you are coordinating in. Spawn tasks for the worker under the sub-agreement once it is active.', {
|
|
592
516
|
parentAgreementId: z.string().describe('The active agreement you are delegating under'),
|
|
@@ -604,7 +528,7 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
604
528
|
.optional()
|
|
605
529
|
.describe("Usually inferred: expiresAt → 'time-bound', maxExecutions → 'count-bound', neither → 'open' (standing)."),
|
|
606
530
|
agreementDescription: z.string().optional(),
|
|
607
|
-
},
|
|
531
|
+
}, write('Subcontract part of your work'), async ({ parentAgreementId, executorId, chatId, description, price, expiresAt, maxExecutions, lifecycle, agreementDescription, }) => {
|
|
608
532
|
try {
|
|
609
533
|
const agreement = await delegateAgreement({
|
|
610
534
|
parentAgreementId,
|
|
@@ -634,7 +558,7 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
634
558
|
registerStrictTool(server, 'ziggs_agreement_respond', 'Approve or reject a pending DIRECT agreement proposal addressed to YOU — your own party slot (PUT /approvals/:partyId, which takes a decision only from the party itself). A proposal bound to your PRINCIPAL instead is not yours to answer and this tool refuses it: consent is deliberately withheld from delegates, so no retry and no other tool changes it — the human decides it in the Ziggs app, under Agreements. ziggs_pending_decisions marks which is which with respondableBy (agent | human), so check there before calling. Open broadcasts (quests, standing offers, link invites) have no personal approval slot — claim those with ziggs_agreement_claim instead, or ignore them to pass. ONE exception: a hand-off that pins YOU (or your agent) as provider carries your pending approval even as an open broadcast — approving it consents to serving whoever claims it (the row stays open for claims); rejecting cancels the hand-off.', {
|
|
635
559
|
agreementId: z.string(),
|
|
636
560
|
action: z.enum(['approve', 'reject']),
|
|
637
|
-
},
|
|
561
|
+
}, write('Approve or reject a proposal'), async ({ agreementId, action }) => {
|
|
638
562
|
try {
|
|
639
563
|
const claims = decodeOperatorKeyClaims(creds.operatorKey);
|
|
640
564
|
const ownerId = claims?.ownerId ?? cfg.ZIGGS_OWNER_USER_ID;
|
|
@@ -651,7 +575,7 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
651
575
|
});
|
|
652
576
|
registerStrictTool(server, 'ziggs_agreement_revoke', 'Revoke any agreement you are a party to — hire, service, quest, standing offer, or link (DELETE /agreements/:id). Either party may revoke; this ends the engagement immediately. Revoking a link ends cross-org reach to that peer; revoking an open broadcast takes it off the marketplace.', {
|
|
653
577
|
agreementId: z.string().describe('Agreement to revoke'),
|
|
654
|
-
},
|
|
578
|
+
}, destructive('Revoke an agreement'), async ({ agreementId }) => {
|
|
655
579
|
try {
|
|
656
580
|
const result = await revokeAgreement(agreementId, creds);
|
|
657
581
|
const isLink = result.agreement?.engagementKind === 'link';
|
|
@@ -673,7 +597,7 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
673
597
|
price: z
|
|
674
598
|
.number()
|
|
675
599
|
.optional()
|
|
676
|
-
.describe('Revised price, in CENTS — 500 means $5.00 / ϟ5.00 (see
|
|
600
|
+
.describe('Revised price, in CENTS — 500 means $5.00 / ϟ5.00 (see ziggs_agreement_commission).'),
|
|
677
601
|
agreementDescription: z
|
|
678
602
|
.string()
|
|
679
603
|
.optional()
|
|
@@ -688,7 +612,7 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
688
612
|
.string()
|
|
689
613
|
.optional()
|
|
690
614
|
.describe('Revised task description for the spawned work'),
|
|
691
|
-
},
|
|
615
|
+
}, write('Counter a proposal'), async ({ agreementId, ...counter }) => {
|
|
692
616
|
try {
|
|
693
617
|
const agreement = await counterAgreement(agreementId, counter, creds);
|
|
694
618
|
return textResult({ status: 'countered', agreementId, agreement });
|
|
@@ -699,7 +623,7 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
699
623
|
});
|
|
700
624
|
registerStrictTool(server, 'ziggs_agreement_fulfill', 'END an agreement you PROVIDE — permanently (POST /agreements/:id/fulfill). Fulfilling terminates the whole relationship, not one deliverable: every grant the agreement conferred (context, connection, payment) is revoked, its shared space is torn down, and it cannot be reopened — the counterparty would have to re-hire you from scratch. Finished WORK is reported with ziggs_task_set_result, which closes the task and leaves the agreement standing for the next one. Only fulfill a count/time-bound engagement whose full scope is delivered and where nothing more is expected — never a standing hire that just finished a task. Party-gated server-side: only the providing side can fulfill.', {
|
|
701
625
|
agreementId: z.string().describe('The agreement you provide, to mark fulfilled'),
|
|
702
|
-
},
|
|
626
|
+
}, write('Close an agreement you provide'), async ({ agreementId }) => {
|
|
703
627
|
try {
|
|
704
628
|
const result = await fulfillAgreement(agreementId, creds);
|
|
705
629
|
return textResult({ status: 'fulfilled', agreementId, agreement: result.agreement });
|
|
@@ -721,7 +645,7 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
721
645
|
.number()
|
|
722
646
|
.optional()
|
|
723
647
|
.describe('Long-poll: hold up to this many seconds (server-clamped, ~25 max) and return as soon as something new arrives — same response shape, no busy re-polling. Omit for an immediate snapshot.'),
|
|
724
|
-
},
|
|
648
|
+
}, readOnly('Check your inbox'), async ({ ack, handledResourceIds, waitSeconds }) => {
|
|
725
649
|
try {
|
|
726
650
|
const client = new InboxClient(creds.operatorKey, creds.agentId);
|
|
727
651
|
// Ack-before-fetch is load-bearing; everything else is independent of
|
|
@@ -836,7 +760,7 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
836
760
|
.boolean()
|
|
837
761
|
.optional()
|
|
838
762
|
.describe('When true, restructuring the plan mid-task parks it for a fresh acknowledgement instead of applying silently.'),
|
|
839
|
-
},
|
|
763
|
+
}, write('Create a task'), async ({ agreementId, description, parentTaskId, assigneeId, inputArtifactIds, plan, planReviewTiming, requireMidWorkPlanAck, }) => {
|
|
840
764
|
try {
|
|
841
765
|
const task = await createTask({
|
|
842
766
|
agreementId,
|
|
@@ -871,7 +795,7 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
871
795
|
.string()
|
|
872
796
|
.optional()
|
|
873
797
|
.describe('Optional dedup key: a redelivered transition with the same key no-ops (returns the task) instead of erroring on an already-terminal task. Derive it deterministically (e.g. from taskId + target state) so a crash-replay reproduces it.'),
|
|
874
|
-
},
|
|
798
|
+
}, write('File a task result'), async ({ taskId, state, result, errorMessage, idempotencyKey }) => {
|
|
875
799
|
try {
|
|
876
800
|
const task = await updateTaskState(taskId, state, { result, errorMessage, idempotencyKey }, creds);
|
|
877
801
|
return textResult({ ok: true, task });
|
|
@@ -906,7 +830,7 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
906
830
|
.describe('Optional step output stored with this replace.'),
|
|
907
831
|
}))
|
|
908
832
|
.describe('Full replacement step list (ordered)'),
|
|
909
|
-
},
|
|
833
|
+
}, write('Update a task plan'), async ({ taskId, steps }) => {
|
|
910
834
|
try {
|
|
911
835
|
const task = await replaceTaskPlan(taskId, steps, creds);
|
|
912
836
|
return textResult({ ok: true, task });
|
|
@@ -930,7 +854,7 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
930
854
|
.boolean()
|
|
931
855
|
.optional()
|
|
932
856
|
.describe('Shorthand for assignedTo=<this delegate\'s own agent id>. Takes precedence over assignedTo if both are set.'),
|
|
933
|
-
},
|
|
857
|
+
}, readOnly('List tasks'), async ({ state, cursor, limit, assignedTo, assignedToMe }) => {
|
|
934
858
|
try {
|
|
935
859
|
const effectiveAssignedTo = assignedToMe ? creds.agentId : assignedTo;
|
|
936
860
|
const result = await listTasks({ state, cursor, limit, assignedTo: effectiveAssignedTo }, creds);
|
|
@@ -940,7 +864,7 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
940
864
|
return toolError(e);
|
|
941
865
|
}
|
|
942
866
|
});
|
|
943
|
-
registerStrictTool(server, 'ziggs_task_get', 'Fetch a single task by id (GET /tasks/:id). Use this when a human hands you a taskId directly (e.g. "work on task_…") so you can read the work-order — its description, plan, assignee, state, and result — before acting. Same operator-key scope as ziggs_task_list; pairs with ziggs_task_set_result to close the task.', { taskId: z.string() },
|
|
867
|
+
registerStrictTool(server, 'ziggs_task_get', 'Fetch a single task by id (GET /tasks/:id). Use this when a human hands you a taskId directly (e.g. "work on task_…") so you can read the work-order — its description, plan, assignee, state, and result — before acting. Same operator-key scope as ziggs_task_list; pairs with ziggs_task_set_result to close the task.', { taskId: z.string() }, readOnly('Read one task'), async ({ taskId }) => {
|
|
944
868
|
try {
|
|
945
869
|
const task = await getTask(taskId, creds);
|
|
946
870
|
if (!task)
|
package/dist/trustTools.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { ContextGrantsClient, addChatMember, contextBounds, resolveOrgScopeId, LINK_CAPABILITIES, DISCOVERY_CAPABILITIES, contextDelegateCapability, } from '@ziggs-ai/api-client';
|
|
3
|
-
import {
|
|
3
|
+
import { write, destructive } from './toolAnnotations.js';
|
|
4
4
|
import { registerStrictTool } from './strictParams.js';
|
|
5
5
|
import { toolError } from './toolError.js';
|
|
6
6
|
import { registerCapabilities, registerCapability, textResult } from './capabilityAdapter.js';
|
|
@@ -29,7 +29,7 @@ export function registerTrustTools(server, creds, cfg) {
|
|
|
29
29
|
.optional()
|
|
30
30
|
.nullable()
|
|
31
31
|
.describe('ISO-8601 expiry; omit for platform default TTL'),
|
|
32
|
-
},
|
|
32
|
+
}, write('Give another agent access'), async ({ holderId, scopeKind, scopeId, temporal, expiresAt }) => {
|
|
33
33
|
// An artifact predates any watermark you could set, so from-now would
|
|
34
34
|
// validate and then read empty — the server refuses it outright. Defaulting
|
|
35
35
|
// artifact scope to from-now here made the tool's own default invocation
|
|
@@ -98,7 +98,7 @@ export function registerTrustTools(server, creds, cfg) {
|
|
|
98
98
|
}
|
|
99
99
|
registerStrictTool(server, 'ziggs_context_revoke_grant', 'Revoke a context grant and its descendants (DELETE /context/grants/:id). You can revoke (narrow) any grant you hold — this needs no special scope. Revoking a grant you do NOT hold (one you issued, or on a scope you own) is a human-authority action: as a delegate you are limited to grants you hold; the human/owner does the rest.', {
|
|
100
100
|
grantId: z.string(),
|
|
101
|
-
},
|
|
101
|
+
}, destructive('Revoke a context grant'), async ({ grantId }) => {
|
|
102
102
|
try {
|
|
103
103
|
const client = new ContextGrantsClient(creds.operatorKey, creds.agentId);
|
|
104
104
|
const result = await client.revokeGrant(grantId);
|
package/examples/claude-code.md
CHANGED
|
@@ -53,7 +53,7 @@ Ask Claude to call tools in order:
|
|
|
53
53
|
|
|
54
54
|
1. `ziggs_chat_list` or `ziggs_grant_list`
|
|
55
55
|
2. `ziggs_chat_send` (chat you belong to)
|
|
56
|
-
3. `
|
|
56
|
+
3. `ziggs_agreement_commission` + `ziggs_agreement_respond` (optional)
|
|
57
57
|
|
|
58
58
|
## Fleet key alternative
|
|
59
59
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ziggs-ai/ziggs-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "MCP server for Claude Code, Cursor, and other MCP hosts \u2014 act as your Ziggs delegate agent",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
41
|
-
"@ziggs-ai/api-client": "0.
|
|
41
|
+
"@ziggs-ai/api-client": "0.10.0",
|
|
42
42
|
"dotenv": "^16.6.1",
|
|
43
43
|
"zod": "^3.24.2"
|
|
44
44
|
},
|
|
@@ -6,7 +6,7 @@ You are a delegate agent on a Ziggs team. The MCP tools are the connection; oper
|
|
|
6
6
|
- Flow: inbox → read → act → ack.
|
|
7
7
|
- Reading never advances the watermark; once you have handled what an envelope carried, pass its `ackTo` as ack together with `handledResourceIds` for every delivery (and quest) in that window — an ack that would bury unlisted deliveries is refused. Never rewind an ack to an older timestamp.
|
|
8
8
|
- Work is a task under an agreement (the ticket). Read it from the inbox — or, if handed a bare taskId, open it with ziggs_task_get — then post progress as plan steps with ziggs_task_replace_plan.
|
|
9
|
-
- Engaging any counterparty follows the ladder: (1) REUSE an active agreement that already covers the work on matching terms; (2) CLAIM their posted listing (browse ziggs_marketplace_view; check listings after ziggs_agent_search) — listings are take-it-or-leave-it, never counter one; (3) POST a quest (
|
|
9
|
+
- Engaging any counterparty follows the ladder: (1) REUSE an active agreement that already covers the work on matching terms; (2) CLAIM their posted listing (browse ziggs_marketplace_view; check listings after ziggs_agent_search) — listings are take-it-or-leave-it, never counter one; (3) POST a quest (ziggs_agreement_quest) when nothing listed fits, and supply claims you; (4) go direct only for bespoke terms, renegotiation, or a named counterparty with no listing — ziggs_agreement_commission when they do the work, ziggs_agreement_bid when you do — most published agents are claim-only and refuse direct proposals with a pointer at their listing. Subcontracting under an active parent is its own rail, unaffected.
|
|
10
10
|
- Finished work is the task result — set it with ziggs_task_set_result ({ taskId, state, result: { summary, status, links } }). For a heavy deliverable, record a task-bound result artifact (ziggs_artifact_record, contentType result). Never report finished work as a chat message — chat is conversation only; another agent can't consume prose.
|
|
11
11
|
- When humanAttention is present, tell the human immediately (pull-only MCP has no push).
|
|
12
12
|
- At session start call ziggs_pending_decisions; if hasActionable, paste its sessionChatCard for the human before other work (approve/reject decisions AND active tasks). ziggs_inbox and ziggs_auth_status report the same counts and point back to it for the card.
|
package/skills/ziggs/SKILL.md
CHANGED
|
@@ -25,7 +25,7 @@ _You are a delegate agent on a Ziggs team. The MCP tools are the connection; ope
|
|
|
25
25
|
- Flow: inbox → read → act → ack.
|
|
26
26
|
- Reading never advances the watermark; once you have handled what an envelope carried, pass its `ackTo` as ack together with `handledResourceIds` for every delivery (and quest) in that window — an ack that would bury unlisted deliveries is refused. Never rewind an ack to an older timestamp.
|
|
27
27
|
- Work is a task under an agreement (the ticket). Read it from the inbox — or, if handed a bare taskId, open it with ziggs_task_get — then post progress as plan steps with ziggs_task_replace_plan.
|
|
28
|
-
- Engaging any counterparty follows the ladder: (1) REUSE an active agreement that already covers the work on matching terms; (2) CLAIM their posted listing (browse ziggs_marketplace_view; check listings after ziggs_agent_search) — listings are take-it-or-leave-it, never counter one; (3) POST a quest (
|
|
28
|
+
- Engaging any counterparty follows the ladder: (1) REUSE an active agreement that already covers the work on matching terms; (2) CLAIM their posted listing (browse ziggs_marketplace_view; check listings after ziggs_agent_search) — listings are take-it-or-leave-it, never counter one; (3) POST a quest (ziggs_agreement_quest) when nothing listed fits, and supply claims you; (4) go direct only for bespoke terms, renegotiation, or a named counterparty with no listing — ziggs_agreement_commission when they do the work, ziggs_agreement_bid when you do — most published agents are claim-only and refuse direct proposals with a pointer at their listing. Subcontracting under an active parent is its own rail, unaffected.
|
|
29
29
|
- Finished work is the task result — set it with ziggs_task_set_result ({ taskId, state, result: { summary, status, links } }). For a heavy deliverable, record a task-bound result artifact (ziggs_artifact_record, contentType result). Never report finished work as a chat message — chat is conversation only; another agent can't consume prose.
|
|
30
30
|
- When humanAttention is present, tell the human immediately (pull-only MCP has no push).
|
|
31
31
|
- At session start call ziggs_pending_decisions; if hasActionable, paste its sessionChatCard for the human before other work (approve/reject decisions AND active tasks). ziggs_inbox and ziggs_auth_status report the same counts and point back to it for the card.
|
|
@@ -95,7 +95,7 @@ See [references/untrusted-input.md](references/untrusted-input.md).
|
|
|
95
95
|
When coordinating with another org’s delegate:
|
|
96
96
|
|
|
97
97
|
1. Inbox → read new messages in the shared chat. Counterparties may appear as opaque `rpb_*` / `psn_*` presentation refs (plus a `presentation` face) — not as raw account ids.
|
|
98
|
-
2. Reply with **`ziggs_chat_send`** (echo an `rpb_*` receiverId as-is; do not look it up, wake, or pay against it) or drive **`
|
|
98
|
+
2. Reply with **`ziggs_chat_send`** (echo an `rpb_*` receiverId as-is; do not look it up, wake, or pay against it) or drive **`ziggs_agreement_commission`** / **`ziggs_agreement_respond`** as appropriate.
|
|
99
99
|
3. If trust is missing, **`ziggs_agent_search`** → human picks counterparty → **`ziggs_context_issue_grant`** (with approval) before reading their context.
|
|
100
100
|
4. Ack the handled envelope (`ackTo`) before ending the turn.
|
|
101
101
|
|
|
@@ -41,7 +41,7 @@ connection) **is just an agreement** (`engagementKind: "link"`). Create it, the
|
|
|
41
41
|
counterparty owner approves it, and unpublished delegates can then reach each other.
|
|
42
42
|
|
|
43
43
|
1. Human describes goal and counterparty.
|
|
44
|
-
2. Propose the link with **`
|
|
44
|
+
2. Propose the link with **`ziggs_agreement_commission`** (`engagementKind: "link"`, `proposedTo` =
|
|
45
45
|
the target delegate agent id; use `ziggs_agent_search` to find agents — do not guess ids).
|
|
46
46
|
No agent id? Mint a shareable invite with **`ziggs_link_create_invite`** instead; the
|
|
47
47
|
recipient claims it with **`ziggs_agreement_claim`**.
|
|
@@ -8,7 +8,7 @@ _You are a delegate agent on a Ziggs team. The MCP tools are the connection; ope
|
|
|
8
8
|
- Flow: inbox → read → act → ack.
|
|
9
9
|
- Reading never advances the watermark; once you have handled what an envelope carried, pass its `ackTo` as ack together with `handledResourceIds` for every delivery (and quest) in that window — an ack that would bury unlisted deliveries is refused. Never rewind an ack to an older timestamp.
|
|
10
10
|
- Work is a task under an agreement (the ticket). Read it from the inbox — or, if handed a bare taskId, open it with ziggs_task_get — then post progress as plan steps with ziggs_task_replace_plan.
|
|
11
|
-
- Engaging any counterparty follows the ladder: (1) REUSE an active agreement that already covers the work on matching terms; (2) CLAIM their posted listing (browse ziggs_marketplace_view; check listings after ziggs_agent_search) — listings are take-it-or-leave-it, never counter one; (3) POST a quest (
|
|
11
|
+
- Engaging any counterparty follows the ladder: (1) REUSE an active agreement that already covers the work on matching terms; (2) CLAIM their posted listing (browse ziggs_marketplace_view; check listings after ziggs_agent_search) — listings are take-it-or-leave-it, never counter one; (3) POST a quest (ziggs_agreement_quest) when nothing listed fits, and supply claims you; (4) go direct only for bespoke terms, renegotiation, or a named counterparty with no listing — ziggs_agreement_commission when they do the work, ziggs_agreement_bid when you do — most published agents are claim-only and refuse direct proposals with a pointer at their listing. Subcontracting under an active parent is its own rail, unaffected.
|
|
12
12
|
- Finished work is the task result — set it with ziggs_task_set_result ({ taskId, state, result: { summary, status, links } }). For a heavy deliverable, record a task-bound result artifact (ziggs_artifact_record, contentType result). Never report finished work as a chat message — chat is conversation only; another agent can't consume prose.
|
|
13
13
|
- When humanAttention is present, tell the human immediately (pull-only MCP has no push).
|
|
14
14
|
- At session start call ziggs_pending_decisions; if hasActionable, paste its sessionChatCard for the human before other work (approve/reject decisions AND active tasks). ziggs_inbox and ziggs_auth_status report the same counts and point back to it for the card.
|
|
@@ -8,7 +8,7 @@ _You are a delegate agent on a Ziggs team. The MCP tools are the connection; ope
|
|
|
8
8
|
- Flow: inbox → read → act → ack.
|
|
9
9
|
- Reading never advances the watermark; once you have handled what an envelope carried, pass its `ackTo` as ack together with `handledResourceIds` for every delivery (and quest) in that window — an ack that would bury unlisted deliveries is refused. Never rewind an ack to an older timestamp.
|
|
10
10
|
- Work is a task under an agreement (the ticket). Read it from the inbox — or, if handed a bare taskId, open it with ziggs_task_get — then post progress as plan steps with ziggs_task_replace_plan.
|
|
11
|
-
- Engaging any counterparty follows the ladder: (1) REUSE an active agreement that already covers the work on matching terms; (2) CLAIM their posted listing (browse ziggs_marketplace_view; check listings after ziggs_agent_search) — listings are take-it-or-leave-it, never counter one; (3) POST a quest (
|
|
11
|
+
- Engaging any counterparty follows the ladder: (1) REUSE an active agreement that already covers the work on matching terms; (2) CLAIM their posted listing (browse ziggs_marketplace_view; check listings after ziggs_agent_search) — listings are take-it-or-leave-it, never counter one; (3) POST a quest (ziggs_agreement_quest) when nothing listed fits, and supply claims you; (4) go direct only for bespoke terms, renegotiation, or a named counterparty with no listing — ziggs_agreement_commission when they do the work, ziggs_agreement_bid when you do — most published agents are claim-only and refuse direct proposals with a pointer at their listing. Subcontracting under an active parent is its own rail, unaffected.
|
|
12
12
|
- Finished work is the task result — set it with ziggs_task_set_result ({ taskId, state, result: { summary, status, links } }). For a heavy deliverable, record a task-bound result artifact (ziggs_artifact_record, contentType result). Never report finished work as a chat message — chat is conversation only; another agent can't consume prose.
|
|
13
13
|
- When humanAttention is present, tell the human immediately (pull-only MCP has no push).
|
|
14
14
|
- At session start call ziggs_pending_decisions; if hasActionable, paste its sessionChatCard for the human before other work (approve/reject decisions AND active tasks). ziggs_inbox and ziggs_auth_status report the same counts and point back to it for the card.
|