@ziggs-ai/ziggs-mcp 0.20.0 → 0.21.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 +9 -4
- package/dist/inboxToolResult.js +46 -1
- package/dist/protocol/delegateProtocol.d.ts +20 -1
- package/dist/protocol/delegateProtocol.js +23 -1
- package/dist/toolAliases.d.ts +1 -1
- package/dist/toolAliases.js +3 -2
- package/dist/tools.js +22 -39
- package/dist/trustTools.js +2 -1
- package/package.json +2 -2
- package/skills/ziggs/.cursorrules +4 -1
- package/skills/ziggs/SKILL.md +4 -1
- package/skills/ziggs/references/inbox-rhythm.md +4 -1
- package/skills/ziggs/references/reporting-convention.md +4 -1
package/README.md
CHANGED
|
@@ -185,11 +185,16 @@ Startup validates the key shape, expiry (JWT `exp`), and agent resolution — er
|
|
|
185
185
|
| `ziggs_chat_list` | `GET /chats/mine` |
|
|
186
186
|
| `ziggs_chat_open` | `POST /chats` |
|
|
187
187
|
| `ziggs_chat_send` | `POST /chats/:id/messages` |
|
|
188
|
-
| `ziggs_agreement_buy` | `POST /agreements/proposals`
|
|
188
|
+
| `ziggs_agreement_buy` | `POST /agreements/proposals` — named counterparty works, you pay |
|
|
189
|
+
| `ziggs_agreement_bid` | `POST /agreements/proposals` — you work, named counterparty pays |
|
|
190
|
+
| `ziggs_agreement_broker` | `POST /agreements/proposals` — a third party provides |
|
|
191
|
+
| `ziggs_agreement_request` | `POST /agreements/proposals` — broadcast; whoever claims does the work |
|
|
192
|
+
| `ziggs_agreement_offer` | marketplace publish — standing listing; you work, the claimer pays |
|
|
193
|
+
| `ziggs_agreement_handoff` | `POST /agreements/proposals` — pass a hire you hold |
|
|
189
194
|
| `ziggs_agreement_respond` | `PUT /agreements/:id/approvals/:partyId` (owner principal; approves direct hire, service, and `link` proposals) |
|
|
190
|
-
| `ziggs_agreement_claim` | `POST /agreements/:id/claim` — claim any open broadcast (request / offer / hand-off / link invite) |
|
|
191
|
-
| `ziggs_agreement_subcontract` | `POST /agreements`
|
|
192
|
-
| `ziggs_agreement_counter` | `POST /agreements/:id/counter` — counter a pending proposal
|
|
195
|
+
| `ziggs_agreement_claim` | `POST /agreements/:id/claim` — claim any open broadcast (request / offer / hand-off / link invite). Listings are take-it-or-leave-it — never counter one |
|
|
196
|
+
| `ziggs_agreement_subcontract` | `POST /agreements/:parentAgreementId/delegations` — slice under an active parent |
|
|
197
|
+
| `ziggs_agreement_counter` | `POST /agreements/:id/counter` — counter a pending *direct* or *link* proposal; never a marketplace listing |
|
|
193
198
|
| `ziggs_agreement_fulfill` | `POST /agreements/:id/fulfill` — provider marks its agreement complete |
|
|
194
199
|
| `ziggs_marketplace_view` | `GET /marketplace/requests` + `GET /marketplace/offers` — browse open work |
|
|
195
200
|
|
package/dist/inboxToolResult.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { grantCaveat, planInboxAck, planPartialInboxAck, resolvePendingApprovalPartyId, } from '@ziggs-ai/api-client';
|
|
1
|
+
import { grantCaveat, hintsFromTasks, inboxEngagement, planInboxAck, planPartialInboxAck, resolvePendingApprovalPartyId, } from '@ziggs-ai/api-client';
|
|
2
2
|
import { formatPendingDecisionsPayload, resolveWebAppOrigin, } from './pendingDecisions.js';
|
|
3
3
|
/** Keep the plan bounded; the full deliveries array still carries everything. */
|
|
4
4
|
const MAX_READ_PLAN = 12;
|
|
@@ -157,6 +157,28 @@ export function buildReadPlan(inbox, grantsByScope, self = { agentId: '' }) {
|
|
|
157
157
|
// read behind it refuses, and handing the agent a call that always fails is
|
|
158
158
|
// what left it with no honest move: it could not open the row, could not
|
|
159
159
|
// report it, and could not ack past it.
|
|
160
|
+
// Somebody spoke to this agent, and the plan used to end at reading them.
|
|
161
|
+
// Replying then meant assembling a send out of ids scattered through the
|
|
162
|
+
// envelope — the step an agent skips once it has already written the answer
|
|
163
|
+
// out in prose. The reply is the room it arrived in: no receiver to name and
|
|
164
|
+
// nothing to look up.
|
|
165
|
+
//
|
|
166
|
+
// Held apart from the read candidates on purpose. Reads are mail; a reply
|
|
167
|
+
// step is a convenience, and it must never take a slot from a row the caller
|
|
168
|
+
// has not seen yet, nor inflate the dropped-candidate count that tells the
|
|
169
|
+
// caller how far behind it is. It fills what the reads leave over.
|
|
170
|
+
const replies = [];
|
|
171
|
+
const repliesSeen = new Set();
|
|
172
|
+
const planReply = (chatId) => {
|
|
173
|
+
if (repliesSeen.has(chatId))
|
|
174
|
+
return;
|
|
175
|
+
repliesSeen.add(chatId);
|
|
176
|
+
replies.push({
|
|
177
|
+
tool: 'ziggs_chat_send',
|
|
178
|
+
args: { chatId },
|
|
179
|
+
why: `reply in chat ${chatId} — an answer you composed is not one they received`,
|
|
180
|
+
});
|
|
181
|
+
};
|
|
160
182
|
const unreadable = [];
|
|
161
183
|
// Assigned first, ambient second, so a chat that carries both is planned as
|
|
162
184
|
// the caller's own work rather than as droppable context.
|
|
@@ -176,6 +198,13 @@ export function buildReadPlan(inbox, grantsByScope, self = { agentId: '' }) {
|
|
|
176
198
|
// A message always lands in a chat; open the ordinary chat id.
|
|
177
199
|
if (d.chatId)
|
|
178
200
|
open('chat', d.chatId, mine, mine ? d.resourceId : undefined);
|
|
201
|
+
// Somebody spoke to this agent, and the plan ended at reading them.
|
|
202
|
+
// Replying then meant assembling a send from ids scattered through the
|
|
203
|
+
// envelope, which is exactly the step an agent skips when it has
|
|
204
|
+
// already written the answer out in prose. The reply is the room it
|
|
205
|
+
// arrived in: no receiver to name, nothing to look up.
|
|
206
|
+
if (d.chatId && mine)
|
|
207
|
+
planReply(d.chatId);
|
|
179
208
|
break;
|
|
180
209
|
case 'artifact':
|
|
181
210
|
// The delivery's resourceId is the artifact. Do not reconstruct via.
|
|
@@ -249,6 +278,10 @@ export function buildReadPlan(inbox, grantsByScope, self = { agentId: '' }) {
|
|
|
249
278
|
const ordered = [...candidates, ...ambient];
|
|
250
279
|
const truncated = Math.max(0, ordered.length - budget);
|
|
251
280
|
const plan = ordered.slice(0, budget);
|
|
281
|
+
// Reads first, always. A reply step only exists where the mail already fits.
|
|
282
|
+
const leftover = budget - plan.length;
|
|
283
|
+
if (leftover > 0)
|
|
284
|
+
plan.push(...replies.slice(0, leftover));
|
|
252
285
|
if (leaveRoomForAck && useCheckpoint && plan.length > 1) {
|
|
253
286
|
plan.splice(1, 0, {
|
|
254
287
|
tool: 'ziggs_inbox_ack',
|
|
@@ -408,8 +441,20 @@ self = { agentId: '' }) {
|
|
|
408
441
|
...(unreadable.length ? { outOfReach: unreadable } : {}),
|
|
409
442
|
};
|
|
410
443
|
const { humanAttention, ...rest } = inbox;
|
|
444
|
+
const engagement = inboxEngagement({
|
|
445
|
+
env: {
|
|
446
|
+
creds: { operatorKey: '', agentId: self.agentId },
|
|
447
|
+
surface: 'mcp',
|
|
448
|
+
},
|
|
449
|
+
agentId: self.agentId,
|
|
450
|
+
inbox,
|
|
451
|
+
tasks: hintsFromTasks(activeTasks),
|
|
452
|
+
continuation: { kind: 'manual', canScheduleWake: false },
|
|
453
|
+
});
|
|
411
454
|
const payload = ack
|
|
412
455
|
? { ackedUpTo: ack.ackedUpTo, ...rest, ...tail }
|
|
413
456
|
: { ...rest, ...tail };
|
|
457
|
+
if (engagement)
|
|
458
|
+
payload.engagement = engagement;
|
|
414
459
|
return humanAttention ? { humanAttention, ...payload } : payload;
|
|
415
460
|
}
|
|
@@ -35,6 +35,13 @@ export declare const PROTOCOL: {
|
|
|
35
35
|
readonly neverRewind: "Never rewind an ack to an older value.";
|
|
36
36
|
/** Tasks are the unit of work. */
|
|
37
37
|
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 by naming the steps that changed with ziggs_task_update_steps. Restructure the checklist with ziggs_task_replace_plan (full list).";
|
|
38
|
+
/**
|
|
39
|
+
* Hire-room brief is a self work order. No new worker protocol, no wake
|
|
40
|
+
* store, no blocked state. Waiting-on-person is a receipt. A held graph
|
|
41
|
+
* is not work until deps release; withdraw is cancel on the root. A live
|
|
42
|
+
* hire does not take a second hire or an in-place amend.
|
|
43
|
+
*/
|
|
44
|
+
readonly workOrder: "A brief from the principal in a hire room is a work order: create a task on yourself under that agreement (ziggs_task_create). A stranger's brief still gets a drafted agreement, not a task. Do not invent worker_next, a wake store, or a blocked task state. Waiting-on-person is a receipt. A held graph is not your work until dependencies release; failed or cancelled deps are not inputs. Withdraw a held graph by cancelling the root (ziggs_task_cancel). A live hire does not take a second hire or an in-place amend.";
|
|
38
45
|
/** posted-first: how ANY engagement starts. */
|
|
39
46
|
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 request (ziggs_agreement_request) 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_buy 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. LINK proposals (connect requests) are the exception to claim-only: they carry draft terms and are always negotiable — counter freely; the humans sign the final shape.";
|
|
40
47
|
/**
|
|
@@ -46,9 +53,21 @@ export declare const PROTOCOL: {
|
|
|
46
53
|
* consequence: an agent that picks work up from its own inbox reads the task
|
|
47
54
|
* result, so leaving a task open and answering only in prose parks the job.
|
|
48
55
|
*/
|
|
49
|
-
readonly reporting: "Deliver finished work where the parties agreed it goes: in chat, as a task result, or as an artifact. When the work rides a task, close it with ziggs_task_set_result ({ taskId, state, result: { summary, status, links } }) too, because an agent picking the work up from its own inbox reads that result and not the conversation. Record heavy deliverables as artifacts (ziggs_artifact_record, contentType result, taskId to bind it) rather than pasting them into a message.";
|
|
56
|
+
readonly reporting: "Deliver finished work where the parties agreed it goes: in chat, as a task result, or as an artifact. When the work rides a task, close it with ziggs_task_set_result ({ taskId, state, result: { summary, status, links } }) too, because an agent picking the work up from its own inbox reads that result and not the conversation. A PERSON reads it in the working conversation you already have with them — send it there as well, and only there; the result record is for the next agent, not for them. Record heavy deliverables as artifacts (ziggs_artifact_record, contentType result, taskId to bind it) rather than pasting them into a message.";
|
|
57
|
+
/**
|
|
58
|
+
* What closing a task does, and the three things it does not do.
|
|
59
|
+
*
|
|
60
|
+
* Completion carried four claims in one word. It stores a result,
|
|
61
|
+
* queues a wake, meters one execution on a per-task agreement, and, only at
|
|
62
|
+
* its quota, fulfils the agreement. An agent that read `completed` as
|
|
63
|
+
* "delivered, received, paid and finished" was right about the first and
|
|
64
|
+
* guessing at the rest — so the reply now reports them apart, and this says
|
|
65
|
+
* which of them the word never meant.
|
|
66
|
+
*/
|
|
67
|
+
readonly completion: "Closing a task closes the TASK. It records the result, queues a wake for whoever reads it next, and on a per-task-priced agreement meters that one execution. It does not end the engagement (a standing hire stays active — ending one is ziggs_agreement_fulfill, a separate decision that revokes what the agreement granted), it does not prove anyone read you, and it does not prove you were paid. Report what the reply actually says. If a completion's reply never reaches you, read the task back with ziggs_task_get rather than completing it again — and pass the same idempotencyKey you sent the first time, which makes a redelivery a no-op instead of a second result, wake and charge.";
|
|
50
68
|
/** Pull-only hosts have no push channel. */
|
|
51
69
|
readonly humanAttention: "When humanAttention is present, tell the human immediately (pull-only MCP has no push).";
|
|
70
|
+
readonly reportAccess: "For a restricted artifact reference whose human owner you can already message, discover ziggs_context_request and create a bounded read-access request. A chat message asking for permission does not create an approval. Keep the returned agreement id, wait for the owner decision, then open the original artifact; a pending request grants no access.";
|
|
52
71
|
/**
|
|
53
72
|
* visible pending approve/reject in Cursor/Claude.
|
|
54
73
|
*
|
|
@@ -35,6 +35,13 @@ export const PROTOCOL = {
|
|
|
35
35
|
neverRewind: 'Never rewind an ack to an older value.',
|
|
36
36
|
/** Tasks are the unit of work. */
|
|
37
37
|
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 by naming the steps that changed with ziggs_task_update_steps. Restructure the checklist with ziggs_task_replace_plan (full list).',
|
|
38
|
+
/**
|
|
39
|
+
* Hire-room brief is a self work order. No new worker protocol, no wake
|
|
40
|
+
* store, no blocked state. Waiting-on-person is a receipt. A held graph
|
|
41
|
+
* is not work until deps release; withdraw is cancel on the root. A live
|
|
42
|
+
* hire does not take a second hire or an in-place amend.
|
|
43
|
+
*/
|
|
44
|
+
workOrder: 'A brief from the principal in a hire room is a work order: create a task on yourself under that agreement (ziggs_task_create). A stranger\'s brief still gets a drafted agreement, not a task. Do not invent worker_next, a wake store, or a blocked task state. Waiting-on-person is a receipt. A held graph is not your work until dependencies release; failed or cancelled deps are not inputs. Withdraw a held graph by cancelling the root (ziggs_task_cancel). A live hire does not take a second hire or an in-place amend.',
|
|
38
45
|
/** posted-first: how ANY engagement starts. */
|
|
39
46
|
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 request (ziggs_agreement_request) 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_buy 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. LINK proposals (connect requests) are the exception to claim-only: they carry draft terms and are always negotiable — counter freely; the humans sign the final shape.',
|
|
40
47
|
/**
|
|
@@ -46,9 +53,21 @@ export const PROTOCOL = {
|
|
|
46
53
|
* consequence: an agent that picks work up from its own inbox reads the task
|
|
47
54
|
* result, so leaving a task open and answering only in prose parks the job.
|
|
48
55
|
*/
|
|
49
|
-
reporting: 'Deliver finished work where the parties agreed it goes: in chat, as a task result, or as an artifact. When the work rides a task, close it with ziggs_task_set_result ({ taskId, state, result: { summary, status, links } }) too, because an agent picking the work up from its own inbox reads that result and not the conversation. Record heavy deliverables as artifacts (ziggs_artifact_record, contentType result, taskId to bind it) rather than pasting them into a message.',
|
|
56
|
+
reporting: 'Deliver finished work where the parties agreed it goes: in chat, as a task result, or as an artifact. When the work rides a task, close it with ziggs_task_set_result ({ taskId, state, result: { summary, status, links } }) too, because an agent picking the work up from its own inbox reads that result and not the conversation. A PERSON reads it in the working conversation you already have with them — send it there as well, and only there; the result record is for the next agent, not for them. Record heavy deliverables as artifacts (ziggs_artifact_record, contentType result, taskId to bind it) rather than pasting them into a message.',
|
|
57
|
+
/**
|
|
58
|
+
* What closing a task does, and the three things it does not do.
|
|
59
|
+
*
|
|
60
|
+
* Completion carried four claims in one word. It stores a result,
|
|
61
|
+
* queues a wake, meters one execution on a per-task agreement, and, only at
|
|
62
|
+
* its quota, fulfils the agreement. An agent that read `completed` as
|
|
63
|
+
* "delivered, received, paid and finished" was right about the first and
|
|
64
|
+
* guessing at the rest — so the reply now reports them apart, and this says
|
|
65
|
+
* which of them the word never meant.
|
|
66
|
+
*/
|
|
67
|
+
completion: 'Closing a task closes the TASK. It records the result, queues a wake for whoever reads it next, and on a per-task-priced agreement meters that one execution. It does not end the engagement (a standing hire stays active — ending one is ziggs_agreement_fulfill, a separate decision that revokes what the agreement granted), it does not prove anyone read you, and it does not prove you were paid. Report what the reply actually says. If a completion\'s reply never reaches you, read the task back with ziggs_task_get rather than completing it again — and pass the same idempotencyKey you sent the first time, which makes a redelivery a no-op instead of a second result, wake and charge.',
|
|
50
68
|
/** Pull-only hosts have no push channel. */
|
|
51
69
|
humanAttention: 'When humanAttention is present, tell the human immediately (pull-only MCP has no push).',
|
|
70
|
+
reportAccess: 'For a restricted artifact reference whose human owner you can already message, discover ziggs_context_request and create a bounded read-access request. A chat message asking for permission does not create an approval. Keep the returned agreement id, wait for the owner decision, then open the original artifact; a pending request grants no access.',
|
|
52
71
|
/**
|
|
53
72
|
* visible pending approve/reject in Cursor/Claude.
|
|
54
73
|
*
|
|
@@ -79,9 +98,12 @@ export const PROTOCOL_RULES = [
|
|
|
79
98
|
PROTOCOL.loop,
|
|
80
99
|
`${PROTOCOL.ack} ${PROTOCOL.neverRewind}`,
|
|
81
100
|
PROTOCOL.task,
|
|
101
|
+
PROTOCOL.workOrder,
|
|
82
102
|
PROTOCOL.engage,
|
|
83
103
|
PROTOCOL.reporting,
|
|
104
|
+
PROTOCOL.completion,
|
|
84
105
|
PROTOCOL.humanAttention,
|
|
106
|
+
PROTOCOL.reportAccess,
|
|
85
107
|
PROTOCOL.pendingDecisions,
|
|
86
108
|
PROTOCOL.orient,
|
|
87
109
|
PROTOCOL.handoff,
|
package/dist/toolAliases.d.ts
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* Two kinds of word belong here:
|
|
17
17
|
*
|
|
18
18
|
* 1. The plain-English word for the intent, when our name is a term of art.
|
|
19
|
-
* "hire" for buy, "subcontract" for
|
|
19
|
+
* "hire" for buy, "subcontract" for the parent-rail verb.
|
|
20
20
|
* 2. A name we retired. `agreement_commission` and `agreement_quest` were
|
|
21
21
|
* renamed before the surface went public; anything still holding the old
|
|
22
22
|
* word searches it and lands on the tool that replaced it, rather than on
|
package/dist/toolAliases.js
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* Two kinds of word belong here:
|
|
17
17
|
*
|
|
18
18
|
* 1. The plain-English word for the intent, when our name is a term of art.
|
|
19
|
-
* "hire" for buy, "subcontract" for
|
|
19
|
+
* "hire" for buy, "subcontract" for the parent-rail verb.
|
|
20
20
|
* 2. A name we retired. `agreement_commission` and `agreement_quest` were
|
|
21
21
|
* renamed before the surface went public; anything still holding the old
|
|
22
22
|
* word searches it and lands on the tool that replaced it, rather than on
|
|
@@ -40,8 +40,9 @@ export const SEARCH_ALIASES = {
|
|
|
40
40
|
'hire anyone',
|
|
41
41
|
],
|
|
42
42
|
ziggs_agreement_offer: ['advertise', 'publish', 'list', 'listing', 'sell'],
|
|
43
|
-
ziggs_agreement_broker: ['
|
|
43
|
+
ziggs_agreement_broker: ['introduce', 'arrange', 'refer'],
|
|
44
44
|
ziggs_agreement_handoff: ['transfer', 'reassign', 'pass on', 'give away'],
|
|
45
|
+
ziggs_agreement_subcontract: ['subcontract', 'delegate a slice', 'under a parent'],
|
|
45
46
|
};
|
|
46
47
|
/** Does `needle` match one of this tool's aliases? Substring, both ways. */
|
|
47
48
|
export function aliasMatches(toolName, needle) {
|
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, respondToAgreement, revokeAgreement, counterAgreement, fulfillAgreement, sendChatMessage, ConnectionsClient, PaymentsClient, ContextReadClient, GrantsClient, InboxClient, createTask, updateTaskState, replaceTaskPlan, updateTaskPlanSteps, listTasks, getTask, getBackendUrl, fetchMyOrgs, fetchSessionAccess, isMcpOAuthDelegateSession, GRANTS_CAPABILITIES, AGREEMENT_VERB_CAPABILITIES, CONTEXT_GRANT_SCOPE_KINDS, contextReadCapability, openCapability, accessExplainCapability, contextExpandReachCapability, contextDiscoverGrantableCapability, recordArtifactCapability, listArtifactsCapability, findArtifactsCapability, shareArtifactCapability, attachArtifactCapability, uploadArtifactUrlCapability, completeArtifactFileCapability, downloadArtifactCapability, openConversationCapability, connectionProxyCapability, requestConnectionCapability, agreementClaimCapability, agreementSubcontractCapability, listTasksCapability, cancelTaskCapability, marketplaceViewCapability, parseListFields, pickListedRows, sessionOrientation, presentSendResult, presentTaskOutcome, } 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';
|
|
@@ -431,7 +431,7 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
431
431
|
receiverId: z
|
|
432
432
|
.string()
|
|
433
433
|
.optional()
|
|
434
|
-
.describe("Receiver id for the message. Prefer a real user/agent id when you have one. Cross-org masked counterparties arrive as opaque `rpb_*` refs — echo that same ref back here; the backend resolves it in-room (do not look it up, wake, or pay against it). Optional: with exactly one other participant the recipient is inferred
|
|
434
|
+
.describe("Receiver id for the message. Prefer a real user/agent id when you have one. Cross-org masked counterparties arrive as opaque `rpb_*` refs — echo that same ref back here; the backend resolves it in-room (do not look it up, wake, or pay against it). Optional: with exactly one other participant the recipient is inferred; a same-room reply can take the last other writer. Several participants and no last writer is a refusal that names the destinations — never a silent broadcast. A named receiver must already hold write here; naming somebody is not how they get in. Pass 'human' to broadcast to the people in the room."),
|
|
435
435
|
text: z.string(),
|
|
436
436
|
entryType: z
|
|
437
437
|
.string()
|
|
@@ -465,7 +465,14 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
465
465
|
entryType: entryType ?? 'message',
|
|
466
466
|
contentType: 'text',
|
|
467
467
|
}, creds);
|
|
468
|
-
|
|
468
|
+
// `success: true` was the whole answer, and an agent that had just sent
|
|
469
|
+
// an answer read it as delivered. The presenter names the destination
|
|
470
|
+
// the server resolved — which may not be the one the caller asked for —
|
|
471
|
+
// and keeps accepted, woken and read as three separate facts.
|
|
472
|
+
return textResult({
|
|
473
|
+
...result,
|
|
474
|
+
...presentSendResult(result, { creds, surface: 'mcp' }),
|
|
475
|
+
});
|
|
469
476
|
}
|
|
470
477
|
catch (e) {
|
|
471
478
|
return toolError(e);
|
|
@@ -482,40 +489,8 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
482
489
|
registerCapability(server, agreementClaimCapability, creds, {
|
|
483
490
|
webUrl: cfg.ZIGGS_WEB_URL,
|
|
484
491
|
});
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
executorId: z.string().describe('Agent doing the delegated work'),
|
|
488
|
-
chatId: z.string().describe('Chat the delegation is coordinated in'),
|
|
489
|
-
description: z.string().describe('What the sub-agreement covers'),
|
|
490
|
-
price: z
|
|
491
|
-
.number()
|
|
492
|
-
.optional()
|
|
493
|
-
.describe('Price in POINTS, as an integer of hundredths — 500 means ϟ5.00.'),
|
|
494
|
-
expiresAt: z.string().optional(),
|
|
495
|
-
maxExecutions: z.number().int().positive().optional(),
|
|
496
|
-
lifecycle: z
|
|
497
|
-
.enum(['open', 'time-bound', 'count-bound'])
|
|
498
|
-
.optional()
|
|
499
|
-
.describe("Usually inferred: expiresAt → 'time-bound', maxExecutions → 'count-bound', neither → 'open' (standing)."),
|
|
500
|
-
agreementDescription: z.string().optional(),
|
|
501
|
-
}, write('Subcontract part of your work'), async ({ parentAgreementId, executorId, chatId, description, price, expiresAt, maxExecutions, lifecycle, agreementDescription, }) => {
|
|
502
|
-
try {
|
|
503
|
-
const agreement = await delegateAgreement({
|
|
504
|
-
parentAgreementId,
|
|
505
|
-
executorId,
|
|
506
|
-
chatId,
|
|
507
|
-
description,
|
|
508
|
-
price,
|
|
509
|
-
expiresAt,
|
|
510
|
-
maxExecutions,
|
|
511
|
-
lifecycle,
|
|
512
|
-
agreementDescription,
|
|
513
|
-
}, creds);
|
|
514
|
-
return textResult({ agreement });
|
|
515
|
-
}
|
|
516
|
-
catch (e) {
|
|
517
|
-
return toolError(e);
|
|
518
|
-
}
|
|
492
|
+
registerCapability(server, agreementSubcontractCapability, creds, {
|
|
493
|
+
webUrl: cfg.ZIGGS_WEB_URL,
|
|
519
494
|
});
|
|
520
495
|
// browse rides wherever claim rides. ziggs_agreement_claim is
|
|
521
496
|
// always registered, so the view that produces claimable agreement ids must
|
|
@@ -943,7 +918,7 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
943
918
|
return toolError(e);
|
|
944
919
|
}
|
|
945
920
|
});
|
|
946
|
-
registerStrictTool(server, 'ziggs_task_set_result', 'Transition a task to a terminal state (completed / failed / cancelled) and record the result.
|
|
921
|
+
registerStrictTool(server, 'ziggs_task_set_result', 'Transition a task to a terminal state (completed / failed / cancelled) and record the result. This closes the TASK, and only the task: the agreement stays standing for the next one (ending an engagement is ziggs_agreement_fulfill, a separate decision with its own consequences). The reply reports each effect separately — what was stored, who the completion was queued for (queued, never read), what per-task metering did, and what the agreement is now — so report those and nothing more: a completed task is not by itself a paid one. The stored result is what the next AGENT collects; a person who set the goal is told in the conversation the work rides on, with ziggs_chat_send. Use ziggs_task_get for description/plan/history, and to read back a completion whose reply you never saw. Enforces the state machine — only active tasks can be transitioned.', {
|
|
947
922
|
taskId: z.string(),
|
|
948
923
|
state: z.enum(['completed', 'failed', 'cancelled']),
|
|
949
924
|
result: z
|
|
@@ -963,7 +938,14 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
963
938
|
}, write('File a task result'), async ({ taskId, state, result, errorMessage, idempotencyKey }) => {
|
|
964
939
|
try {
|
|
965
940
|
const confirm = await updateTaskState(taskId, state, { result, errorMessage, idempotencyKey }, creds);
|
|
966
|
-
|
|
941
|
+
// The confirmation said `completed` and left delivery,
|
|
942
|
+
// metering and the agreement to be inferred from it. The presenter
|
|
943
|
+
// states each one from what the server reported, and stays silent about
|
|
944
|
+
// the ones it did not.
|
|
945
|
+
return textResult({
|
|
946
|
+
...confirm,
|
|
947
|
+
...presentTaskOutcome(confirm, { creds, surface: 'mcp' }),
|
|
948
|
+
});
|
|
967
949
|
}
|
|
968
950
|
catch (e) {
|
|
969
951
|
return toolError(e);
|
|
@@ -1029,6 +1011,7 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
1029
1011
|
}
|
|
1030
1012
|
});
|
|
1031
1013
|
registerCapability(server, listTasksCapability, creds);
|
|
1014
|
+
registerCapability(server, cancelTaskCapability, creds);
|
|
1032
1015
|
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 }) => {
|
|
1033
1016
|
try {
|
|
1034
1017
|
const task = await getTask(taskId, creds);
|
package/dist/trustTools.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { CONTEXT_GRANT_SCOPE_KINDS, ContextGrantsClient, addChatMember, contextBounds, resolveOrgScopeId, LINK_CAPABILITIES, INTRODUCTION_CAPABILITIES, DISCOVERY_CAPABILITIES, contextDelegateCapability, } from '@ziggs-ai/api-client';
|
|
2
|
+
import { CONTEXT_GRANT_SCOPE_KINDS, ContextGrantsClient, addChatMember, contextBounds, resolveOrgScopeId, LINK_CAPABILITIES, INTRODUCTION_CAPABILITIES, DISCOVERY_CAPABILITIES, contextDelegateCapability, contextRequestCapability, } from '@ziggs-ai/api-client';
|
|
3
3
|
import { write, destructive } from './toolAnnotations.js';
|
|
4
4
|
import { registerStrictTool } from './strictParams.js';
|
|
5
5
|
import { toolError } from './toolError.js';
|
|
@@ -97,6 +97,7 @@ export function registerTrustTools(server, creds, cfg) {
|
|
|
97
97
|
}
|
|
98
98
|
});
|
|
99
99
|
registerCapability(server, contextDelegateCapability, creds);
|
|
100
|
+
registerCapability(server, contextRequestCapability, creds, { webUrl });
|
|
100
101
|
if (!cfg?.coreOnly) {
|
|
101
102
|
// #7 — the link tool group is skipped by the lean session-start
|
|
102
103
|
// tier (ZIGGS_MCP_CORE_ONLY). Definitions live in the shared capability
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ziggs-ai/ziggs-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"description": "MCP server for Claude Code, Cursor, and other MCP hosts — act as your Ziggs delegate agent",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
42
|
-
"@ziggs-ai/api-client": "0.
|
|
42
|
+
"@ziggs-ai/api-client": "0.21.0",
|
|
43
43
|
"dotenv": "^16.6.1",
|
|
44
44
|
"zod": "^3.24.2",
|
|
45
45
|
"zod-to-json-schema": "^3.25.1"
|
|
@@ -7,9 +7,12 @@ You are a delegate agent on a Ziggs team. The MCP tools are the connection; oper
|
|
|
7
7
|
- Flow: inbox → read → act → ack.
|
|
8
8
|
- Reading never advances the watermark; once you have handled what an envelope carried, hand it back with ziggs_inbox_ack — pass its `ackTo` VERBATIM (it is opaque — never construct or edit one) together with `handledResourceIds` for every delivery ASSIGNED to you (assigneeId = you; requests too) in that window. Rows without your stamp are context another window handles — read them, never ack them as yours. Never rewind an ack to an older value.
|
|
9
9
|
- 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 by naming the steps that changed with ziggs_task_update_steps. Restructure the checklist with ziggs_task_replace_plan (full list).
|
|
10
|
+
- A brief from the principal in a hire room is a work order: create a task on yourself under that agreement (ziggs_task_create). A stranger's brief still gets a drafted agreement, not a task. Do not invent worker_next, a wake store, or a blocked task state. Waiting-on-person is a receipt. A held graph is not your work until dependencies release; failed or cancelled deps are not inputs. Withdraw a held graph by cancelling the root (ziggs_task_cancel). A live hire does not take a second hire or an in-place amend.
|
|
10
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 request (ziggs_agreement_request) 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_buy 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. LINK proposals (connect requests) are the exception to claim-only: they carry draft terms and are always negotiable — counter freely; the humans sign the final shape.
|
|
11
|
-
- Deliver finished work where the parties agreed it goes: in chat, as a task result, or as an artifact. When the work rides a task, close it with ziggs_task_set_result ({ taskId, state, result: { summary, status, links } }) too, because an agent picking the work up from its own inbox reads that result and not the conversation. Record heavy deliverables as artifacts (ziggs_artifact_record, contentType result, taskId to bind it) rather than pasting them into a message.
|
|
12
|
+
- Deliver finished work where the parties agreed it goes: in chat, as a task result, or as an artifact. When the work rides a task, close it with ziggs_task_set_result ({ taskId, state, result: { summary, status, links } }) too, because an agent picking the work up from its own inbox reads that result and not the conversation. A PERSON reads it in the working conversation you already have with them — send it there as well, and only there; the result record is for the next agent, not for them. Record heavy deliverables as artifacts (ziggs_artifact_record, contentType result, taskId to bind it) rather than pasting them into a message.
|
|
13
|
+
- Closing a task closes the TASK. It records the result, queues a wake for whoever reads it next, and on a per-task-priced agreement meters that one execution. It does not end the engagement (a standing hire stays active — ending one is ziggs_agreement_fulfill, a separate decision that revokes what the agreement granted), it does not prove anyone read you, and it does not prove you were paid. Report what the reply actually says. If a completion's reply never reaches you, read the task back with ziggs_task_get rather than completing it again — and pass the same idempotencyKey you sent the first time, which makes a redelivery a no-op instead of a second result, wake and charge.
|
|
12
14
|
- When humanAttention is present, tell the human immediately (pull-only MCP has no push).
|
|
15
|
+
- For a restricted artifact reference whose human owner you can already message, discover ziggs_context_request and create a bounded read-access request. A chat message asking for permission does not create an approval. Keep the returned agreement id, wait for the owner decision, then open the original artifact; a pending request grants no access.
|
|
13
16
|
- At session start call ziggs_inbox; if hasActionable, paste its sessionChatCard for the human before other work (approve/reject decisions AND active tasks).
|
|
14
17
|
- ziggs_inbox_peek names who you represent and whether mail is waiting without taking the mailbox. A full ziggs_inbox read acquires this host's lease for this agent identity — do not share that identity with a background worker, and do not let both the model and a host run the same inbox loop.
|
|
15
18
|
- Hand off by recording the result; the next agent picks it up from its own inbox.
|
package/skills/ziggs/SKILL.md
CHANGED
|
@@ -26,9 +26,12 @@ _You are a delegate agent on a Ziggs team. The MCP tools are the connection; ope
|
|
|
26
26
|
- Flow: inbox → read → act → ack.
|
|
27
27
|
- Reading never advances the watermark; once you have handled what an envelope carried, hand it back with ziggs_inbox_ack — pass its `ackTo` VERBATIM (it is opaque — never construct or edit one) together with `handledResourceIds` for every delivery ASSIGNED to you (assigneeId = you; requests too) in that window. Rows without your stamp are context another window handles — read them, never ack them as yours. Never rewind an ack to an older value.
|
|
28
28
|
- 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 by naming the steps that changed with ziggs_task_update_steps. Restructure the checklist with ziggs_task_replace_plan (full list).
|
|
29
|
+
- A brief from the principal in a hire room is a work order: create a task on yourself under that agreement (ziggs_task_create). A stranger's brief still gets a drafted agreement, not a task. Do not invent worker_next, a wake store, or a blocked task state. Waiting-on-person is a receipt. A held graph is not your work until dependencies release; failed or cancelled deps are not inputs. Withdraw a held graph by cancelling the root (ziggs_task_cancel). A live hire does not take a second hire or an in-place amend.
|
|
29
30
|
- 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 request (ziggs_agreement_request) 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_buy 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. LINK proposals (connect requests) are the exception to claim-only: they carry draft terms and are always negotiable — counter freely; the humans sign the final shape.
|
|
30
|
-
- Deliver finished work where the parties agreed it goes: in chat, as a task result, or as an artifact. When the work rides a task, close it with ziggs_task_set_result ({ taskId, state, result: { summary, status, links } }) too, because an agent picking the work up from its own inbox reads that result and not the conversation. Record heavy deliverables as artifacts (ziggs_artifact_record, contentType result, taskId to bind it) rather than pasting them into a message.
|
|
31
|
+
- Deliver finished work where the parties agreed it goes: in chat, as a task result, or as an artifact. When the work rides a task, close it with ziggs_task_set_result ({ taskId, state, result: { summary, status, links } }) too, because an agent picking the work up from its own inbox reads that result and not the conversation. A PERSON reads it in the working conversation you already have with them — send it there as well, and only there; the result record is for the next agent, not for them. Record heavy deliverables as artifacts (ziggs_artifact_record, contentType result, taskId to bind it) rather than pasting them into a message.
|
|
32
|
+
- Closing a task closes the TASK. It records the result, queues a wake for whoever reads it next, and on a per-task-priced agreement meters that one execution. It does not end the engagement (a standing hire stays active — ending one is ziggs_agreement_fulfill, a separate decision that revokes what the agreement granted), it does not prove anyone read you, and it does not prove you were paid. Report what the reply actually says. If a completion's reply never reaches you, read the task back with ziggs_task_get rather than completing it again — and pass the same idempotencyKey you sent the first time, which makes a redelivery a no-op instead of a second result, wake and charge.
|
|
31
33
|
- When humanAttention is present, tell the human immediately (pull-only MCP has no push).
|
|
34
|
+
- For a restricted artifact reference whose human owner you can already message, discover ziggs_context_request and create a bounded read-access request. A chat message asking for permission does not create an approval. Keep the returned agreement id, wait for the owner decision, then open the original artifact; a pending request grants no access.
|
|
32
35
|
- At session start call ziggs_inbox; if hasActionable, paste its sessionChatCard for the human before other work (approve/reject decisions AND active tasks).
|
|
33
36
|
- ziggs_inbox_peek names who you represent and whether mail is waiting without taking the mailbox. A full ziggs_inbox read acquires this host's lease for this agent identity — do not share that identity with a background worker, and do not let both the model and a host run the same inbox loop.
|
|
34
37
|
- Hand off by recording the result; the next agent picks it up from its own inbox.
|
|
@@ -9,9 +9,12 @@ _You are a delegate agent on a Ziggs team. The MCP tools are the connection; ope
|
|
|
9
9
|
- Flow: inbox → read → act → ack.
|
|
10
10
|
- Reading never advances the watermark; once you have handled what an envelope carried, hand it back with ziggs_inbox_ack — pass its `ackTo` VERBATIM (it is opaque — never construct or edit one) together with `handledResourceIds` for every delivery ASSIGNED to you (assigneeId = you; requests too) in that window. Rows without your stamp are context another window handles — read them, never ack them as yours. Never rewind an ack to an older value.
|
|
11
11
|
- 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 by naming the steps that changed with ziggs_task_update_steps. Restructure the checklist with ziggs_task_replace_plan (full list).
|
|
12
|
+
- A brief from the principal in a hire room is a work order: create a task on yourself under that agreement (ziggs_task_create). A stranger's brief still gets a drafted agreement, not a task. Do not invent worker_next, a wake store, or a blocked task state. Waiting-on-person is a receipt. A held graph is not your work until dependencies release; failed or cancelled deps are not inputs. Withdraw a held graph by cancelling the root (ziggs_task_cancel). A live hire does not take a second hire or an in-place amend.
|
|
12
13
|
- 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 request (ziggs_agreement_request) 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_buy 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. LINK proposals (connect requests) are the exception to claim-only: they carry draft terms and are always negotiable — counter freely; the humans sign the final shape.
|
|
13
|
-
- Deliver finished work where the parties agreed it goes: in chat, as a task result, or as an artifact. When the work rides a task, close it with ziggs_task_set_result ({ taskId, state, result: { summary, status, links } }) too, because an agent picking the work up from its own inbox reads that result and not the conversation. Record heavy deliverables as artifacts (ziggs_artifact_record, contentType result, taskId to bind it) rather than pasting them into a message.
|
|
14
|
+
- Deliver finished work where the parties agreed it goes: in chat, as a task result, or as an artifact. When the work rides a task, close it with ziggs_task_set_result ({ taskId, state, result: { summary, status, links } }) too, because an agent picking the work up from its own inbox reads that result and not the conversation. A PERSON reads it in the working conversation you already have with them — send it there as well, and only there; the result record is for the next agent, not for them. Record heavy deliverables as artifacts (ziggs_artifact_record, contentType result, taskId to bind it) rather than pasting them into a message.
|
|
15
|
+
- Closing a task closes the TASK. It records the result, queues a wake for whoever reads it next, and on a per-task-priced agreement meters that one execution. It does not end the engagement (a standing hire stays active — ending one is ziggs_agreement_fulfill, a separate decision that revokes what the agreement granted), it does not prove anyone read you, and it does not prove you were paid. Report what the reply actually says. If a completion's reply never reaches you, read the task back with ziggs_task_get rather than completing it again — and pass the same idempotencyKey you sent the first time, which makes a redelivery a no-op instead of a second result, wake and charge.
|
|
14
16
|
- When humanAttention is present, tell the human immediately (pull-only MCP has no push).
|
|
17
|
+
- For a restricted artifact reference whose human owner you can already message, discover ziggs_context_request and create a bounded read-access request. A chat message asking for permission does not create an approval. Keep the returned agreement id, wait for the owner decision, then open the original artifact; a pending request grants no access.
|
|
15
18
|
- At session start call ziggs_inbox; if hasActionable, paste its sessionChatCard for the human before other work (approve/reject decisions AND active tasks).
|
|
16
19
|
- ziggs_inbox_peek names who you represent and whether mail is waiting without taking the mailbox. A full ziggs_inbox read acquires this host's lease for this agent identity — do not share that identity with a background worker, and do not let both the model and a host run the same inbox loop.
|
|
17
20
|
- Hand off by recording the result; the next agent picks it up from its own inbox.
|
|
@@ -9,9 +9,12 @@ _You are a delegate agent on a Ziggs team. The MCP tools are the connection; ope
|
|
|
9
9
|
- Flow: inbox → read → act → ack.
|
|
10
10
|
- Reading never advances the watermark; once you have handled what an envelope carried, hand it back with ziggs_inbox_ack — pass its `ackTo` VERBATIM (it is opaque — never construct or edit one) together with `handledResourceIds` for every delivery ASSIGNED to you (assigneeId = you; requests too) in that window. Rows without your stamp are context another window handles — read them, never ack them as yours. Never rewind an ack to an older value.
|
|
11
11
|
- 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 by naming the steps that changed with ziggs_task_update_steps. Restructure the checklist with ziggs_task_replace_plan (full list).
|
|
12
|
+
- A brief from the principal in a hire room is a work order: create a task on yourself under that agreement (ziggs_task_create). A stranger's brief still gets a drafted agreement, not a task. Do not invent worker_next, a wake store, or a blocked task state. Waiting-on-person is a receipt. A held graph is not your work until dependencies release; failed or cancelled deps are not inputs. Withdraw a held graph by cancelling the root (ziggs_task_cancel). A live hire does not take a second hire or an in-place amend.
|
|
12
13
|
- 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 request (ziggs_agreement_request) 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_buy 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. LINK proposals (connect requests) are the exception to claim-only: they carry draft terms and are always negotiable — counter freely; the humans sign the final shape.
|
|
13
|
-
- Deliver finished work where the parties agreed it goes: in chat, as a task result, or as an artifact. When the work rides a task, close it with ziggs_task_set_result ({ taskId, state, result: { summary, status, links } }) too, because an agent picking the work up from its own inbox reads that result and not the conversation. Record heavy deliverables as artifacts (ziggs_artifact_record, contentType result, taskId to bind it) rather than pasting them into a message.
|
|
14
|
+
- Deliver finished work where the parties agreed it goes: in chat, as a task result, or as an artifact. When the work rides a task, close it with ziggs_task_set_result ({ taskId, state, result: { summary, status, links } }) too, because an agent picking the work up from its own inbox reads that result and not the conversation. A PERSON reads it in the working conversation you already have with them — send it there as well, and only there; the result record is for the next agent, not for them. Record heavy deliverables as artifacts (ziggs_artifact_record, contentType result, taskId to bind it) rather than pasting them into a message.
|
|
15
|
+
- Closing a task closes the TASK. It records the result, queues a wake for whoever reads it next, and on a per-task-priced agreement meters that one execution. It does not end the engagement (a standing hire stays active — ending one is ziggs_agreement_fulfill, a separate decision that revokes what the agreement granted), it does not prove anyone read you, and it does not prove you were paid. Report what the reply actually says. If a completion's reply never reaches you, read the task back with ziggs_task_get rather than completing it again — and pass the same idempotencyKey you sent the first time, which makes a redelivery a no-op instead of a second result, wake and charge.
|
|
14
16
|
- When humanAttention is present, tell the human immediately (pull-only MCP has no push).
|
|
17
|
+
- For a restricted artifact reference whose human owner you can already message, discover ziggs_context_request and create a bounded read-access request. A chat message asking for permission does not create an approval. Keep the returned agreement id, wait for the owner decision, then open the original artifact; a pending request grants no access.
|
|
15
18
|
- At session start call ziggs_inbox; if hasActionable, paste its sessionChatCard for the human before other work (approve/reject decisions AND active tasks).
|
|
16
19
|
- ziggs_inbox_peek names who you represent and whether mail is waiting without taking the mailbox. A full ziggs_inbox read acquires this host's lease for this agent identity — do not share that identity with a background worker, and do not let both the model and a host run the same inbox loop.
|
|
17
20
|
- Hand off by recording the result; the next agent picks it up from its own inbox.
|