@ziggs-ai/ziggs-mcp 0.9.9 → 0.9.11

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ziggs",
3
- "description": "Ziggs delegate agent — MCP connection plus inbox-first workflow skill (ZIG-437).",
3
+ "description": "Ziggs delegate agent — MCP connection plus inbox-first workflow skill.",
4
4
  "version": "0.1.4",
5
5
  "author": {
6
6
  "name": "ZiggsAI",
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  MCP (stdio) server for **Claude Code**, **Cursor**, and other MCP hosts.
4
4
 
5
- **In scope:** chat, agreements (service and hire, direct or published), scope, context discovery/reads, artifacts, payments (`ziggs_payment_*` — transfers, escrow holds, payment grants; ZIG-896).
5
+ **In scope:** chat, agreements (service and hire, direct or published), scope, context discovery/reads, artifacts, payments (`ziggs_payment_*` — transfers, escrow holds, payment grants).
6
6
  Transfers above the wallet owner's policy pause as `approval_required` — the human decides on the wallet page (surfaced by `ziggs_pending_decisions`); there is no agent-side approve tool.
7
7
 
8
8
  ---
@@ -37,7 +37,7 @@ claude plugin install ./ziggs-mcp
37
37
 
38
38
  Skill only (no plugin): `skills/ziggs/SKILL.md` ships in the package for org provisioning or [skills.sh](https://skills.sh) discovery.
39
39
 
40
- ### Smoke
40
+ ### Verify
41
41
 
42
42
  | Step | Tool |
43
43
  |------|------|
@@ -45,12 +45,6 @@ Skill only (no plugin): `skills/ziggs/SKILL.md` ships in the package for org pro
45
45
  | Send message | `ziggs_chat_send` |
46
46
  | Propose + respond | `ziggs_agreement_propose`, `ziggs_agreement_respond` |
47
47
 
48
- Automated verify (same MCP path as `claude mcp add` + `npx`):
49
-
50
- ```bash
51
- ZIGGS_OPERATOR_KEY=<agent-scoped> node scripts/smoke-ziggs-mcp-z430-e2e.mjs
52
- ```
53
-
54
48
  ---
55
49
 
56
50
  ## claude.ai / remote MCP (OAuth)
@@ -59,36 +53,17 @@ Hosted Streamable HTTP: `https://mcp.ziggsai.com/mcp` (Bearer from OAuth, no key
59
53
 
60
54
  OAuth metadata: `https://api.ziggsai.com/.well-known/oauth-authorization-server`
61
55
 
62
- **Consent:** `GET /oauth/authorize` always redirects to `/app/oauth/mcp-consent` — even if you already have an API session. You must click **Allow**; only `POST /oauth/authorize` (after consent) issues the auth code. E2E smoke uses POST directly (same as the consent page).
63
-
64
- Automated E2E (DCR → consent → token → remote MCP → list chats + send message):
65
-
66
- ```bash
67
- # prod smoke with throwaway user
68
- node scripts/smoke-ziggs-mcp-oauth-z468-e2e.mjs --auto
69
-
70
- # or existing account
71
- ZIGGS_SMOKE_EMAIL=you@example.com ZIGGS_SMOKE_PASSWORD=... \
72
- node scripts/smoke-ziggs-mcp-oauth-z468-e2e.mjs
73
- ```
74
-
75
- **Consent probe** (GET must redirect to consent, not issue code):
76
-
77
- ```bash
78
- node scripts/smoke-ziggs-mcp-oauth-z474-consent-probe.mjs --auto
79
- ```
56
+ **Consent:** `GET /oauth/authorize` always redirects to `/app/oauth/mcp-consent` — even if you already have an API session. You must click **Allow**; there is no silent code issuance.
80
57
 
81
- **Manual claude.ai connector:** step-by-step checklist — [`examples/claude-ai-oauth.md`](examples/claude-ai-oauth.md).
58
+ **Setup guide:** [`examples/claude-ai-oauth.md`](examples/claude-ai-oauth.md).
82
59
 
83
60
  ### MCP registry (`server.json`)
84
61
 
85
- This package ships [`server.json`](./server.json) for [registry.modelcontextprotocol.io](https://registry.modelcontextprotocol.io) (ZIG-1004). `package.json` includes matching `mcpName: io.github.ZiggsAI/ziggs`. Description must be ≤100 characters (registry validation).
86
-
87
- **Publish path:** GitHub Actions workflow `Publish MCP registry` (OIDC as `ZiggsAI`) — runs on `ziggs-mcp-v*` tags after npm publish, or `workflow_dispatch`. Local `mcp-publisher login github` with a personal PAT only grants `io.github.<your-user>/*`, not the org namespace.
62
+ This package ships [`server.json`](./server.json) for [registry.modelcontextprotocol.io](https://registry.modelcontextprotocol.io). `package.json` includes the matching `mcpName: io.github.ZiggsAI/ziggs`.
88
63
 
89
64
  ### ChatGPT
90
65
 
91
- **Unsupported / unverified** — see [`examples/chatgpt-unsupported.md`](examples/chatgpt-unsupported.md). Use claude.ai, Claude Code, or Cursor.
66
+ **Not supported** — see [`examples/chatgpt-unsupported.md`](examples/chatgpt-unsupported.md). Use claude.ai, Claude Code, or Cursor.
92
67
 
93
68
  ---
94
69
 
@@ -112,16 +87,8 @@ Settings → Tools & MCP → **Connect** → Ziggs consent → use tools in chat
112
87
 
113
88
  Full walkthrough: [`examples/cursor-remote-oauth.md`](examples/cursor-remote-oauth.md)
114
89
 
115
- Parity probe (metadata + DCR + protected-resource):
116
-
117
- ```bash
118
- node scripts/probe-cursor-oauth-parity.mjs
119
- ```
120
-
121
90
  ### Local stdio (operator key)
122
91
 
123
- Build from source or use npm after publish:
124
-
125
92
  ```bash
126
93
  npm install && npm run build -w @ziggs-ai/ziggs-mcp
127
94
  ```
@@ -142,7 +109,7 @@ Sign up and log in at the web app.
142
109
 
143
110
  **Fleet key (Cursor / multi-agent):** Developer Portal → Operator keys, then set `ZIGGS_AGENT_ID`.
144
111
 
145
- Minimum scopes for boarding smoke: `agents:impersonate`, `agents:read`, `agents:write`, `context:read`.
112
+ Minimum scopes to get started: `agents:impersonate`, `agents:read`, `agents:write`, `context:read`.
146
113
 
147
114
  ### 3. Environment
148
115
 
@@ -162,41 +129,6 @@ X-Agent-Id: <resolved delegate agent id>
162
129
 
163
130
  Startup validates the key shape, expiry (JWT `exp`), and agent resolution — errors point to Developer Portal.
164
131
 
165
- ### 4. CLI smoke scripts
166
-
167
- From the **repository root** ([github.com/ZiggsAI/agentplus](https://github.com/ZiggsAI/agentplus)):
168
-
169
- ```bash
170
- # basic boarding smoke
171
- ZIGGS_OPERATOR_KEY=... ZIGGS_AGENT_ID=cursor-delegate \
172
- node scripts/smoke-ziggs-mcp.mjs
173
-
174
- # two delegates + negative 403
175
- ZIGGS_OPERATOR_KEY_A=... ZIGGS_AGENT_ID_A=... \
176
- ZIGGS_OPERATOR_KEY_B=... ZIGGS_AGENT_ID_B=... \
177
- ZIGGS_SMOKE_CHAT_ID=... \
178
- node scripts/smoke-ziggs-mcp-context.mjs
179
-
180
- # tools-only (npx trust tools after publish)
181
- ZIGGS_OPERATOR_KEY=<agent-scoped> node scripts/smoke-ziggs-mcp-z433-e2e.mjs --tools-only
182
-
183
- # auto two-org (provisions users/agents/chat, then full flow)
184
- HTTP_URL=https://api.ziggsai.com node scripts/smoke-ziggs-mcp-z433-e2e.mjs --auto
185
-
186
- # full two-org (manual env)
187
- ZIGGS_OPERATOR_KEY_A=... ZIGGS_AGENT_ID_A=... \
188
- ZIGGS_OPERATOR_KEY_B=... ZIGGS_AGENT_ID_B=... \
189
- ZIGGS_APPROVER_OPERATOR_KEY=... ZIGGS_APPROVER_USER_ID=... \
190
- ZIGGS_SMOKE_CHAT_ID=... \
191
- node scripts/smoke-ziggs-mcp-z433-e2e.mjs
192
-
193
- # party handshake (link agreement, prod)
194
- HTTP_URL=https://api.ziggsai.com \
195
- OP_KEY_A=... AGENT_A=... USER_B=... OP_KEY_B=... AGENT_B=... \
196
- npm run smoke:zig-481-prod
197
- # Runbook: docs/evals/party-connection-two-claude.md
198
- ```
199
-
200
132
  ---
201
133
 
202
134
  ## Tools
@@ -219,7 +151,6 @@ OP_KEY_A=... AGENT_A=... USER_B=... OP_KEY_B=... AGENT_B=... \
219
151
  | `ziggs_link_create_invite` | `POST /agreements` `{engagementKind:"link"}` open invite (claimUrl + paste text) |
220
152
  | `ziggs_link_list` | `GET /agreements?engagementKind=link` |
221
153
  | `ziggs_agreement_revoke` | `DELETE /agreements/:id` — any agreement (hire/service/quest/offer/link) |
222
- | `ziggs_smoke_impersonation` | [Internal/debug] connectivity check — only when `ZIGGS_MCP_DEBUG=1`; not part of normal delegate workflow |
223
154
  | `ziggs_context_snapshot` | `GET /context/snapshot?via=chat:` — one-shot chat orientation (history + agreements + roster), grant-fenced |
224
155
  | `ziggs_agreement_list` | `GET /agreements?scope=mine&partyOnly=true` — agreements you are a party to; `scope: "reachable"` drops `partyOnly` for every agreement your grant can read |
225
156
  | `ziggs_agreement_get` | `GET /agreements/:id` |
@@ -245,17 +176,3 @@ npm test -w @ziggs-ai/ziggs-mcp
245
176
  ```
246
177
 
247
178
  Logs must use **stderr** only (stdio MCP transport).
248
-
249
- ## Publish (maintainers)
250
-
251
- 1. Bump `@ziggs-ai/api-client` if needed → tag `api-client-v*`, push (publishes to npm).
252
- 2. Tag `ziggs-mcp-v*` → CI publishes `@ziggs-ai/ziggs-mcp`.
253
-
254
- ```bash
255
- git tag api-client-v0.1.9 && git push origin api-client-v0.1.9
256
- git tag ziggs-mcp-v0.1.4 && git push origin ziggs-mcp-v0.1.4
257
- ```
258
-
259
- CI publishes on tag push. Push **api-client tag first**, then ziggs-mcp.
260
-
261
- npm `--provenance` is not used: npm only supports provenance when the GitHub source repo is **public** (agentplus is private → 422).
@@ -14,7 +14,7 @@ export interface RegisterCapabilityOptions {
14
14
  webUrl?: string;
15
15
  /**
16
16
  * Description override for wording assembled from MCP-local shared consts
17
- * (e.g. the delegate-protocol reporting rule, ZIG-557) — schema and handler
17
+ * (e.g. the delegate-protocol reporting rule) — schema and handler
18
18
  * still come from the shared definition.
19
19
  */
20
20
  description?: string;
@@ -9,7 +9,7 @@ export function textResult(data) {
9
9
  };
10
10
  }
11
11
  /**
12
- * ZIG-956 — the MCP surface adapter for shared capability definitions
12
+ * the MCP surface adapter for shared capability definitions
13
13
  * (api-client `capabilities/`). Lowers the neutral param DSL to zod v3 (the
14
14
  * MCP SDK's ZodRawShape; api-client itself carries no zod, and agent-sdk is on
15
15
  * zod v4, so the schema is defined once and lowered per surface).
package/dist/config.d.ts CHANGED
@@ -6,16 +6,16 @@ declare const envSchema: z.ZodObject<{
6
6
  /** Web app base URL for constructing shareable links. Defaults to https://ziggsai.com. */
7
7
  ZIGGS_WEB_URL: z.ZodOptional<z.ZodString>;
8
8
  ZIGGS_OPERATOR_KEY: z.ZodString;
9
- /** Optional when the operator key is agent-scoped (ZIG-279). */
9
+ /** Optional when the operator key is agent-scoped. */
10
10
  ZIGGS_AGENT_ID: z.ZodOptional<z.ZodString>;
11
11
  /** Human user id for payer-side proposals — defaults the payer on propose/publish tools. */
12
12
  ZIGGS_OWNER_USER_ID: z.ZodOptional<z.ZodString>;
13
- /** Set to 1/true/yes to register internal/debug-only MCP tools (ZIG-672). */
13
+ /** Set to 1/true/yes to register internal/debug-only MCP tools. */
14
14
  ZIGGS_MCP_DEBUG: z.ZodOptional<z.ZodString>;
15
15
  /**
16
16
  * Set to 1/true/yes to register only the core everyday/session-start tools,
17
17
  * skipping the heavy groups (payments, links, marketplace, connections) to
18
- * cut cold-start deferred-loading (ZIG-941 #7). Unset = all tools register.
18
+ * cut cold-start deferred-loading (#7). Unset = all tools register.
19
19
  */
20
20
  ZIGGS_MCP_CORE_ONLY: z.ZodOptional<z.ZodString>;
21
21
  }, "strip", z.ZodTypeAny, {
@@ -45,10 +45,10 @@ export interface ZiggsMcpConfig extends EnvConfig {
45
45
  debugTools: boolean;
46
46
  /**
47
47
  * When true, register only the core everyday/session-start tool tier and skip
48
- * the heavy groups (payments, links, marketplace, connections) — ZIG-941 #7.
48
+ * the heavy groups (payments, links, marketplace, connections) #7.
49
49
  */
50
50
  coreOnly: boolean;
51
51
  }
52
- /** Load delegate credentials from the environment (ZIG-222 / ZIG-430). */
52
+ /** Load delegate credentials from the environment. */
53
53
  export declare function loadConfig(): ZiggsMcpConfig;
54
54
  export {};
package/dist/config.js CHANGED
@@ -8,16 +8,16 @@ const envSchema = z.object({
8
8
  /** Web app base URL for constructing shareable links. Defaults to https://ziggsai.com. */
9
9
  ZIGGS_WEB_URL: z.string().optional(),
10
10
  ZIGGS_OPERATOR_KEY: z.string().min(1, 'ZIGGS_OPERATOR_KEY is required'),
11
- /** Optional when the operator key is agent-scoped (ZIG-279). */
11
+ /** Optional when the operator key is agent-scoped. */
12
12
  ZIGGS_AGENT_ID: z.string().optional(),
13
13
  /** Human user id for payer-side proposals — defaults the payer on propose/publish tools. */
14
14
  ZIGGS_OWNER_USER_ID: z.string().optional(),
15
- /** Set to 1/true/yes to register internal/debug-only MCP tools (ZIG-672). */
15
+ /** Set to 1/true/yes to register internal/debug-only MCP tools. */
16
16
  ZIGGS_MCP_DEBUG: z.string().optional(),
17
17
  /**
18
18
  * Set to 1/true/yes to register only the core everyday/session-start tools,
19
19
  * skipping the heavy groups (payments, links, marketplace, connections) to
20
- * cut cold-start deferred-loading (ZIG-941 #7). Unset = all tools register.
20
+ * cut cold-start deferred-loading (#7). Unset = all tools register.
21
21
  */
22
22
  ZIGGS_MCP_CORE_ONLY: z.string().optional(),
23
23
  });
@@ -28,7 +28,7 @@ function parseBoolFlag(raw) {
28
28
  const v = raw.trim().toLowerCase();
29
29
  return v === '1' || v === 'true' || v === 'yes';
30
30
  }
31
- /** Load delegate credentials from the environment (ZIG-222 / ZIG-430). */
31
+ /** Load delegate credentials from the environment. */
32
32
  export function loadConfig() {
33
33
  const raw = {
34
34
  ZIGGS_API_URL: process.env.ZIGGS_API_URL,
@@ -52,7 +52,7 @@ export function loadConfig() {
52
52
  catch (e) {
53
53
  throw e instanceof Error ? e : new Error(String(e));
54
54
  }
55
- // api-client reads injected config, not the environment (ZIG-652). This
55
+ // api-client reads injected config, not the environment. This
56
56
  // server owns its process, so it hands over whichever base URL it resolved.
57
57
  const httpUrl = process.env.HTTP_URL || parsed.data.ZIGGS_API_URL;
58
58
  if (httpUrl) {
@@ -1,7 +1,7 @@
1
1
  import { type Creds } from '@ziggs-ai/api-client';
2
2
  import type { ZiggsMcpConfig } from './config.js';
3
3
  export declare function parseBearerAuthorization(header: string | string[] | undefined): string;
4
- /** Per-connection credentials from HTTP Authorization (ZIG-431 / ZIG-466). */
4
+ /** Per-connection credentials from HTTP Authorization. */
5
5
  export declare function connectionFromBearer(bearer: string, httpBaseUrl: string, ownerUserId?: string): {
6
6
  creds: Creds;
7
7
  cfg: ZiggsMcpConfig;
@@ -11,12 +11,12 @@ export function parseBearerAuthorization(header) {
11
11
  }
12
12
  return token;
13
13
  }
14
- /** Per-connection credentials from HTTP Authorization (ZIG-431 / ZIG-466). */
14
+ /** Per-connection credentials from HTTP Authorization. */
15
15
  export function connectionFromBearer(bearer, httpBaseUrl, ownerUserId) {
16
16
  const resolvedAgentId = resolveDelegateAgentId(bearer, undefined);
17
17
  if (httpBaseUrl) {
18
18
  process.env.HTTP_URL = httpBaseUrl;
19
- // Same global reach as the env write it replaces (ZIG-652): api-client
19
+ // Same global reach as the env write it replaces: api-client
20
20
  // holds one base URL per process, so the last connection wins here too.
21
21
  configureApiClient({ httpUrl: httpBaseUrl });
22
22
  }
@@ -1,7 +1,7 @@
1
1
  import type { GrantView, ContextReadType, InboxAckResult, InboxEnvelope, Task } from '@ziggs-ai/api-client';
2
2
  import { type DecisionSelfIds } from './pendingDecisions.js';
3
3
  /**
4
- * ZIG-634 (Step 1): a pre-filled next call. The agent can run it verbatim
4
+ * (Step 1): a pre-filled next call. The agent can run it verbatim
5
5
  * instead of assembling args from the ids scattered through the response.
6
6
  * `tool` + `args` mirror the MCP tool signature; `why` is a one-line reason.
7
7
  */
@@ -11,7 +11,7 @@ export interface ReadPlanCall {
11
11
  why: string;
12
12
  }
13
13
  /**
14
- * ZIG-660: buildReadPlan returns the (bounded, deduped) plan plus how many
14
+ * buildReadPlan returns the (bounded, deduped) plan plus how many
15
15
  * candidate entries the cap dropped, so the caller can surface truncation
16
16
  * explicitly instead of the plan silently ending short.
17
17
  */
@@ -20,7 +20,7 @@ export interface ReadPlanResult {
20
20
  truncated: number;
21
21
  }
22
22
  /**
23
- * ZIG-634: replace the free-text `nextActions` hints with typed `readPlan`
23
+ * replace the free-text `nextActions` hints with typed `readPlan`
24
24
  * call objects — tool name + pre-filled args — so the most common loop
25
25
  * (inbox → read what was addressed to you → ack) needs no guesswork.
26
26
  *
@@ -34,7 +34,7 @@ export interface ReadPlanResult {
34
34
  */
35
35
  export declare function buildReadPlan(inbox: InboxEnvelope, grantsByScope?: Map<string, ScopeGrantTag>, self?: DecisionSelfIds): ReadPlanResult;
36
36
  /**
37
- * ZIG-634: forward-continuation for a read_context page. Built only from fields
37
+ * forward-continuation for a read_context page. Built only from fields
38
38
  * already on the page (via, hasMore/nextCursor, latestSequence) plus the grant
39
39
  * id the caller presented — echoed back, never discovered. Nothing new is read.
40
40
  *
@@ -48,7 +48,7 @@ export declare function buildReadContextReadPlan(page: {
48
48
  latestSequence?: string | null;
49
49
  }, type: ContextReadType, via: string, presentedGrantId?: string): ReadPlanCall[];
50
50
  /**
51
- * ZIG-635 (Step 2): the covering grant attached to a scope so the agent can
51
+ * (Step 2): the covering grant attached to a scope so the agent can
52
52
  * pin the right X-Context-Grant-Id without a separate discover_context call.
53
53
  * Grant metadata only — never a resource-derived field.
54
54
  */
@@ -60,23 +60,23 @@ export interface ScopeGrantTag {
60
60
  parentGrantId: string | null;
61
61
  }
62
62
  /**
63
- * ZIG-635: index the caller's own live reach descriptors by scope. The reach
63
+ * index the caller's own live reach descriptors by scope. The reach
64
64
  * list is already the caller's non-expired grants (holderId == principalId),
65
65
  * so this is grant metadata the caller already holds — no protected content.
66
66
  */
67
67
  export declare function indexReachByScope(reach: GrantView[]): Map<string, ScopeGrantTag>;
68
68
  /**
69
- * Put humanAttention first so MCP hosts surface it before counts (ZIG-482),
69
+ * Put humanAttention first so MCP hosts surface it before counts,
70
70
  * and append readPlan last so each inbox call self-narrates the follow-up
71
- * calls (ZIG-634) without disturbing the leading humanAttention key.
71
+ * calls without disturbing the leading humanAttention key.
72
72
  *
73
73
  * When `reach` (the caller's own live grants) is passed, the read plan pins
74
- * each read's covering grant (ZIG-635) so the agent can present
74
+ * each read's covering grant so the agent can present
75
75
  * X-Context-Grant-Id without a separate discover round-trip.
76
76
  */
77
77
  export declare function formatInboxToolResult(inbox: InboxEnvelope, ack?: InboxAckResult | null, webOrigin?: string, activeTasks?: Task[], reach?: GrantView[], activeTasksError?: string,
78
78
  /**
79
- * The caller's own ids (ZIG-1087). Optional here alone: this result carries
79
+ * The caller's own ids. Optional here alone: this result carries
80
80
  * the session-start COUNTS and a pointer to ziggs_pending_decisions, never
81
81
  * the decision items themselves, and a count does not depend on which party
82
82
  * may answer. Production callers pass it regardless — if this shape ever
@@ -18,7 +18,7 @@ function artifactEntry(d) {
18
18
  return ['artifact', d.resourceId];
19
19
  }
20
20
  function readContextCall(type, kind, id, grantId) {
21
- // ZIG-660: pin the covering grant so the read presents the right
21
+ // pin the covering grant so the read presents the right
22
22
  // X-Context-Grant-Id without a separate discover_context round-trip.
23
23
  const grant = grantId ? { contextGrantId: grantId } : {};
24
24
  return {
@@ -28,7 +28,7 @@ function readContextCall(type, kind, id, grantId) {
28
28
  };
29
29
  }
30
30
  /**
31
- * ZIG-634: replace the free-text `nextActions` hints with typed `readPlan`
31
+ * replace the free-text `nextActions` hints with typed `readPlan`
32
32
  * call objects — tool name + pre-filled args — so the most common loop
33
33
  * (inbox → read what was addressed to you → ack) needs no guesswork.
34
34
  *
@@ -44,7 +44,7 @@ export function buildReadPlan(inbox, grantsByScope, self = { agentId: '' }) {
44
44
  const proposals = inbox.proposalsAwaitingMe ?? [];
45
45
  const connectionRequests = inbox.connectionRequestsAwaitingMe ?? [];
46
46
  const deliveries = inbox.deliveries ?? [];
47
- // ZIG-660: dedup by call signature so the same (type, via) can't appear
47
+ // dedup by call signature so the same (type, via) can't appear
48
48
  // twice when several deliveries land in one chat. Collect candidates
49
49
  // uncapped; the cap is applied once, after the ack is reserved, so the ack
50
50
  // step always survives.
@@ -59,7 +59,7 @@ export function buildReadPlan(inbox, grantsByScope, self = { agentId: '' }) {
59
59
  // Decisions first — these also drive humanAttention (pull-only: no push).
60
60
  // The decision (approve/reject) is the human's; we only pre-fill the target.
61
61
  //
62
- // ZIG-1087: only when the pending slot is OURS. A proposal bound to the
62
+ // only when the pending slot is OURS. A proposal bound to the
63
63
  // principal's slot cannot be answered with this tool by anyone on this
64
64
  // surface, so pre-filling the call would be handing over a step that fails
65
65
  // every time — the plan says to carry it to the human instead.
@@ -93,7 +93,7 @@ export function buildReadPlan(inbox, grantsByScope, self = { agentId: '' }) {
93
93
  why: `connection request ${c.requestId} is awaiting your HUMAN's approval, not yours — paste the card for them; ziggs_agreement_respond is refused for a delegate here`,
94
94
  });
95
95
  }
96
- // ZIG-635: pin the covering grant for a chat/agreement read when the caller
96
+ // pin the covering grant for a chat/agreement read when the caller
97
97
  // holds one, so the read presents the right X-Context-Grant-Id without a
98
98
  // separate discover round-trip. Untagged reads still work by id.
99
99
  const grantFor = (kind, id) => grantsByScope?.get(`${kind}:${id}`)?.grantId;
@@ -106,13 +106,13 @@ export function buildReadPlan(inbox, grantsByScope, self = { agentId: '' }) {
106
106
  // - over `kind`, so a delivery kind nobody plans a read for is a compile
107
107
  // error here rather than a plan that quietly ends at the ack;
108
108
  // - over the anchor, so is an artifact carrying none of chat/agreement/task
109
- // — it is read as `artifact:<id>`, the entry ZIG-1037 added for that case.
109
+ // — it is read as `artifact:<id>`, the entry added for that case.
110
110
  //
111
111
  // The first is history: while this looked at chatId/agreementId only, a
112
112
  // task-bound deliverable produced an envelope whose plan was the ack and
113
113
  // nothing else, and an agent following the plan acked work it never read. The
114
114
  // second is that hole closed ahead of an emitter — nothing writes an
115
- // anchor-less delivery today (ZIG-1100), and when something does it is planned.
115
+ // anchor-less delivery today, and when something does it is planned.
116
116
  for (const d of deliveries) {
117
117
  switch (d.kind) {
118
118
  case 'message':
@@ -127,7 +127,7 @@ export function buildReadPlan(inbox, grantsByScope, self = { agentId: '' }) {
127
127
  case 'agreement':
128
128
  case 'quest':
129
129
  // Deliberately no read call. Tasks/proposals arrive as standing state
130
- // elsewhere on the envelope; quests (ZIG-1185) ride `questsAwaitingMe`
130
+ // elsewhere on the envelope; quests ride `questsAwaitingMe`
131
131
  // and are host-triaged with a plain string compare — never an LLM read
132
132
  // plan entry (that would recreate the per-quest token drain).
133
133
  break;
@@ -141,24 +141,43 @@ export function buildReadPlan(inbox, grantsByScope, self = { agentId: '' }) {
141
141
  break;
142
142
  }
143
143
  }
144
- // ZIG-660: reserve a slot for the ack before capping, so the pre-filled ack
144
+ // reserve a slot for the ack before capping, so the pre-filled ack
145
145
  // never gets squeezed out exactly when there's the most news. Report how many
146
146
  // read/decision candidates the cap dropped as an explicit count.
147
- const reserve = inbox.ackTo ? 1 : 0;
148
- const budget = Math.max(0, MAX_READ_PLAN - reserve);
147
+ //
148
+ // ZIG-1305 only pre-fill ack when this plan covers the whole envelope.
149
+ // A truncated plan (or a capped delivery list) must not hand back an ack
150
+ // that would clear deliveries the plan never asked the agent to handle.
151
+ // Reserve the ack slot only when every candidate still fits beside it;
152
+ // otherwise spend the full budget on reads and omit ack.
153
+ const canAckFully = !!inbox.ackTo &&
154
+ !inbox.deliveriesCapped &&
155
+ (inbox.truncatedQuests ?? 0) === 0;
156
+ const leaveRoomForAck = canAckFully && candidates.length <= MAX_READ_PLAN - 1;
157
+ const budget = leaveRoomForAck ? MAX_READ_PLAN - 1 : MAX_READ_PLAN;
149
158
  const truncated = Math.max(0, candidates.length - budget);
150
159
  const plan = candidates.slice(0, budget);
151
- if (inbox.ackTo) {
160
+ if (leaveRoomForAck && truncated === 0) {
161
+ const handledResourceIds = [
162
+ ...new Set([
163
+ ...(inbox.deliveries ?? []).map((d) => d.resourceId),
164
+ ...(inbox.questsAwaitingMe ?? []).map((q) => q.agreementId),
165
+ ]),
166
+ ].filter((id) => typeof id === 'string' && id.length > 0);
152
167
  plan.push({
153
168
  tool: 'ziggs_inbox',
154
- args: { ack: inbox.ackTo },
155
- why: 'reading does not clear the inbox — ack once you have handled everything above',
169
+ args: {
170
+ ack: inbox.ackTo,
171
+ handledResourceIds,
172
+ },
173
+ why: 'reading does not clear the inbox — ack only after you have handled every step above; ' +
174
+ 'handledResourceIds must list every delivery this envelope carried',
156
175
  });
157
176
  }
158
177
  return { plan, truncated };
159
178
  }
160
179
  /**
161
- * ZIG-634: forward-continuation for a read_context page. Built only from fields
180
+ * forward-continuation for a read_context page. Built only from fields
162
181
  * already on the page (via, hasMore/nextCursor, latestSequence) plus the grant
163
182
  * id the caller presented — echoed back, never discovered. Nothing new is read.
164
183
  *
@@ -195,7 +214,7 @@ export function buildReadContextReadPlan(page, type, via, presentedGrantId) {
195
214
  function toScopeGrantTag(g) {
196
215
  return {
197
216
  grantId: g.grantId,
198
- // Context grants carry temporal/watermark as caveats (ZIG-646).
217
+ // Context grants carry temporal/watermark as caveats.
199
218
  temporal: grantCaveat(g, 'temporal') ?? 'from-now',
200
219
  watermarkAt: grantCaveat(g, 'watermark_at') ?? '',
201
220
  expiresAt: g.expiresAt,
@@ -213,7 +232,7 @@ function isBroaderGrant(a, b) {
213
232
  return a.watermarkAt < b.watermarkAt;
214
233
  }
215
234
  /**
216
- * ZIG-635: index the caller's own live reach descriptors by scope. The reach
235
+ * index the caller's own live reach descriptors by scope. The reach
217
236
  * list is already the caller's non-expired grants (holderId == principalId),
218
237
  * so this is grant metadata the caller already holds — no protected content.
219
238
  */
@@ -231,17 +250,17 @@ export function indexReachByScope(reach) {
231
250
  return byScope;
232
251
  }
233
252
  /**
234
- * Put humanAttention first so MCP hosts surface it before counts (ZIG-482),
253
+ * Put humanAttention first so MCP hosts surface it before counts,
235
254
  * and append readPlan last so each inbox call self-narrates the follow-up
236
- * calls (ZIG-634) without disturbing the leading humanAttention key.
255
+ * calls without disturbing the leading humanAttention key.
237
256
  *
238
257
  * When `reach` (the caller's own live grants) is passed, the read plan pins
239
- * each read's covering grant (ZIG-635) so the agent can present
258
+ * each read's covering grant so the agent can present
240
259
  * X-Context-Grant-Id without a separate discover round-trip.
241
260
  */
242
261
  export function formatInboxToolResult(inbox, ack, webOrigin, activeTasks, reach, activeTasksError,
243
262
  /**
244
- * The caller's own ids (ZIG-1087). Optional here alone: this result carries
263
+ * The caller's own ids. Optional here alone: this result carries
245
264
  * the session-start COUNTS and a pointer to ziggs_pending_decisions, never
246
265
  * the decision items themselves, and a count does not depend on which party
247
266
  * may answer. Production callers pass it regardless — if this shape ever
@@ -251,10 +270,10 @@ self = { agentId: '' }) {
251
270
  const byScope = reach?.length ? indexReachByScope(reach) : undefined;
252
271
  const { plan: readPlan, truncated: readPlanTruncated } = buildReadPlan(inbox, byScope, self);
253
272
  const origin = resolveWebAppOrigin(webOrigin);
254
- // ZIG-659: the inbox reports session-start counts and points to
273
+ // the inbox reports session-start counts and points to
255
274
  // ziggs_pending_decisions for the sessionChatCard — it no longer re-emits the
256
275
  // cards, so a session start doesn't ship the same card ~6× across tools.
257
- // ZIG-1120: omit `activeTasks` so counts come from inbox.tasksAwaitingMe —
276
+ // omit `activeTasks` so counts come from inbox.tasksAwaitingMe —
258
277
  // callers that still pass an array (or []) keep the listTasks-derived path.
259
278
  const pending = formatPendingDecisionsPayload(inbox, origin, self, {
260
279
  ...(activeTasks !== undefined ? { activeTasks, activeTasksError } : {}),
@@ -272,7 +291,7 @@ self = { agentId: '' }) {
272
291
  : {};
273
292
  const tail = {
274
293
  ...pendingTail,
275
- // ZIG-700 — always surface a task-fetch failure, even when there is nothing
294
+ // always surface a task-fetch failure, even when there is nothing
276
295
  // else actionable, so hasActiveWork:false is not read as "no tasks".
277
296
  ...(pending.activeTasksFetchError
278
297
  ? {
@@ -7,7 +7,7 @@ export interface OperatorKeyClaims {
7
7
  exp?: number;
8
8
  }
9
9
  declare const MINT_KEY_HELP: string;
10
- /** Decode operator JWT payload without verifying signature (ZIG-279 boundAgentId). */
10
+ /** Decode operator JWT payload without verifying signature (boundAgentId). */
11
11
  export declare function decodeOperatorKeyClaims(token: string): OperatorKeyClaims | null;
12
12
  export declare function isOperatorKeyExpired(claims: OperatorKeyClaims | null): boolean;
13
13
  /**
@@ -1,7 +1,7 @@
1
1
  const MINT_KEY_HELP = 'Mint a key in the Ziggs app: Developer Portal → Operator keys (fleet key + set ZIGGS_AGENT_ID), ' +
2
2
  'or open your delegate agent → Issue operator key (agent-scoped — no ZIGGS_AGENT_ID needed). ' +
3
3
  'Docs: https://ziggsai.com/docs (Claude Code MCP tier).';
4
- /** Decode operator JWT payload without verifying signature (ZIG-279 boundAgentId). */
4
+ /** Decode operator JWT payload without verifying signature (boundAgentId). */
5
5
  export function decodeOperatorKeyClaims(token) {
6
6
  const trimmed = token.trim();
7
7
  const parts = trimmed.split('.');
@@ -1,7 +1,7 @@
1
1
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
  import { type Creds } from '@ziggs-ai/api-client';
3
3
  /**
4
- * ZIG-896 / ZIG-956 — the wallet toolset on the MCP surface, same base names
4
+ * the wallet toolset on the MCP surface, same base names
5
5
  * as the SDK's PAYMENT_TOOLS (payment_* → ziggs_payment_*), now registered
6
6
  * from the shared capability definitions in api-client (one schema + handler,
7
7
  * two thin surface adapters). Safety is unchanged: money-moving calls are
@@ -1,7 +1,7 @@
1
1
  import { PAYMENT_CAPABILITIES } from '@ziggs-ai/api-client';
2
2
  import { registerCapabilities } from './capabilityAdapter.js';
3
3
  /**
4
- * ZIG-896 / ZIG-956 — the wallet toolset on the MCP surface, same base names
4
+ * the wallet toolset on the MCP surface, same base names
5
5
  * as the SDK's PAYMENT_TOOLS (payment_* → ziggs_payment_*), now registered
6
6
  * from the shared capability definitions in api-client (one schema + handler,
7
7
  * two thin surface adapters). Safety is unchanged: money-moving calls are