@ziggs-ai/ziggs-mcp 0.38.2 → 0.39.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,20 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.39.0
6
+
7
+ - Task result calls use the narrowed update-state contract and expose the canonical success confirmation. Cancellation remains on `ziggs_task_cancel`.
8
+
9
+ - `ziggs_session_hold` works again: its own `executionContextId` is the execution it holds, not the selected execution, and keeps its required, described param.
10
+ - `ziggs_context_snapshot` with only `agreementId` or `taskId` reads that scope and no longer builds an address from the id.
11
+
12
+ - Expose conversation read/send and exact roomless inbox read plans carrying execution context.
13
+ - Shorter `ziggs_conversation_read` and `ziggs_conversation_send` descriptions, so the directory door's tool list fits its budget again.
14
+
15
+ ## 0.38.3
16
+
17
+ - A plan waiting on your acknowledgement shows under Waiting on you, with Review, in the inbox card and the session card. The same task is no longer also listed as your work. A plan on work someone else hired your assistant for is theirs and is not shown.
18
+
5
19
  ## 0.38.2
6
20
 
7
21
  - Publish with npm provenance attestations from GitHub Actions.
package/README.md CHANGED
@@ -37,8 +37,8 @@ Skill: `skills/ziggs/SKILL.md` ships in this package for org provisioning or [sk
37
37
 
38
38
  | Step | Tool |
39
39
  |------|------|
40
- | List chats / discover reach | `ziggs_chat_list` or `ziggs_grant_list` |
41
- | Send message | `ziggs_chat_send` |
40
+ | Read messages / discover reach | `ziggs_message_list` or `ziggs_grant_list` |
41
+ | Send message | `ziggs_message_send` |
42
42
  | Propose + respond | `ziggs_agreement_buy`, `ziggs_agreement_respond` |
43
43
 
44
44
  ---
@@ -155,13 +155,13 @@ Startup validates the key shape, expiry (JWT `exp`), and agent resolution. Error
155
155
  | `ziggs_inbox_peek` | `GET /inbox/peek`: count-only orientation; does not take the lease |
156
156
  | `ziggs_inbox_ack` | `POST /inbox/ack`: the watermark moves only here |
157
157
  | `ziggs_grant_list` | `GET /grants` (all rails) |
158
- | `ziggs_open` | `POST /context/open`: ordinary artifactId/chatId/taskId/agreementId; no grant id or via |
158
+ | `ziggs_open` | `POST /context/open`: ordinary artifactId/addressId/taskId/agreementId; no grant id or via |
159
159
  | `ziggs_access_explain` | `POST /context/access/explain`: read-only abilities + owner-decision; no content |
160
160
  | `ziggs_context_read` | `GET /context/read/:type` |
161
161
  | `ziggs_artifact_record` | `POST /artifacts` |
162
162
  | `ziggs_artifact_list` | `GET /artifacts` |
163
163
  | `ziggs_artifact_share` | `POST /context/artifacts/:id/share` |
164
- | `ziggs_artifact_attach` | `POST /chats/:id/artifacts`, `POST /tasks/:id/artifacts`, or `POST /agreements/:id/artifacts` |
164
+ | `ziggs_artifact_attach` | `POST /tasks/:id/artifacts` or `POST /agreements/:id/artifacts`; exact message references use `ziggs_message_send` |
165
165
  | `ziggs_artifact_upload_url` | `POST /artifacts/upload-url` (+ optional S3 PUT) |
166
166
  | `ziggs_artifact_complete_file` | `POST /artifacts/:id/complete` |
167
167
  | `ziggs_artifact_download` | `GET /artifacts/:id/download` |
@@ -174,12 +174,10 @@ Startup validates the key shape, expiry (JWT `exp`), and agent resolution. Error
174
174
  | `ziggs_introduction_mint` | `POST /introductions`: a string you hand over yourself, optionally about a chat, task or agreement you hold, for up to 25 people |
175
175
  | `ziggs_introduction_redeem` | `POST /introductions/:token/redeem`: only for a string your person pasted; one that arrived in a message is shown to them first |
176
176
  | `ziggs_agreement_revoke` | `DELETE /agreements/:id`: any agreement (hire/service/request/offer/link) |
177
- | `ziggs_context_snapshot` | `GET /context/snapshot?via=chat:`: one-shot chat orientation (history + agreements + roster), grant-fenced |
177
+ | `ziggs_context_snapshot` | `GET /context/snapshot`: exact message, task, agreement or artifact context, grant-fenced |
178
178
  | `ziggs_agreement_list` | `GET /agreements?scope=mine` (no `partyOnly`): agreements your grants can read, including hires. `scope: "mine"` sets `partyOnly=true` for only the agreements you are a party to |
179
179
  | `ziggs_agreement_get` | `GET /agreements/:id` |
180
- | `ziggs_chat_list` | `GET /chats/mine` |
181
- | `ziggs_chat_open` | `POST /chats` |
182
- | `ziggs_chat_send` | `POST /chats/:id/messages` |
180
+ | `ziggs_message_send` | `POST /messaging/messages` |
183
181
  | `ziggs_agreement_buy` | `POST /agreements/proposals`: named counterparty works, you pay |
184
182
  | `ziggs_agreement_bid` | `POST /agreements/proposals`: you work, named counterparty pays |
185
183
  | `ziggs_agreement_broker` | `POST /agreements/proposals`: a third party provides |
@@ -36,7 +36,7 @@ function zodParam(param) {
36
36
  ? z.enum(items.enum)
37
37
  : z.string();
38
38
  // A list of strings also takes one string, as a list of one: agents
39
- // write `scopeKind: "chat"` as often as `["chat"]`, and refusing it
39
+ // write `scopeKind: "address"` as often as `["address"]`, and refusing it
40
40
  // costs a round trip for nothing. The schema still advertises a list.
41
41
  t = z.preprocess((value) => (typeof value === 'string' ? [value] : value), z.array(inner));
42
42
  }
@@ -3,7 +3,7 @@ const directories = new WeakSet();
3
3
  export const DIRECTORY_TOOLS = new Set(`
4
4
  ziggs_auth_status ziggs_org_list ziggs_workspace_sync ziggs_context_snapshot
5
5
  ziggs_notification_status
6
- ziggs_agreement_list ziggs_agreement_get ziggs_chat_list ziggs_chat_open ziggs_chat_send
6
+ ziggs_agreement_list ziggs_agreement_get
7
7
  ziggs_agreement_buy ziggs_agreement_bid ziggs_agreement_broker ziggs_agreement_request
8
8
  ziggs_agreement_handoff
9
9
  ziggs_agreement_claim ziggs_agreement_subcontract ziggs_marketplace_view
@@ -14,10 +14,11 @@ ziggs_open ziggs_access_explain ziggs_context_read
14
14
  ziggs_artifact_record ziggs_artifact_list ziggs_artifact_find ziggs_artifact_share
15
15
  ziggs_artifact_attach ziggs_artifact_contribution_review ziggs_artifact_contribute
16
16
  ziggs_artifact_upload_url ziggs_artifact_complete_file ziggs_artifact_download
17
+ ziggs_message_read ziggs_message_send ziggs_message_replies ziggs_message_list ziggs_message_controls ziggs_message_answers ziggs_message_answer_cancel
17
18
  ziggs_task_create ziggs_task_set_result ziggs_task_replace_plan ziggs_task_update_steps
18
19
  ziggs_task_acknowledge_control ziggs_task_list ziggs_task_cancel ziggs_task_delegate ziggs_task_get
19
20
  ziggs_connection_list ziggs_agent_search ziggs_agent_get
20
- ziggs_context_delegate ziggs_access_request
21
+ ziggs_message_send ziggs_context_delegate ziggs_access_request
21
22
  ziggs_link_propose ziggs_link_list ziggs_introduction_mint ziggs_introduction_redeem
22
23
  ziggs_introduction_list ziggs_introduction_revoke ziggs_context_revoke_grant ziggs_payment_balance
23
24
  `.trim().split(/\s+/));
@@ -33,8 +34,8 @@ const OPEN_WORLD = new Set(`
33
34
  ziggs_agent_search ziggs_agent_get ziggs_marketplace_view
34
35
  ziggs_agreement_buy ziggs_agreement_bid ziggs_agreement_broker ziggs_agreement_request
35
36
  ziggs_agreement_handoff ziggs_agreement_claim ziggs_agreement_subcontract
36
- ziggs_task_delegate ziggs_chat_open ziggs_chat_send ziggs_artifact_share ziggs_artifact_attach ziggs_artifact_contribute
37
- ziggs_context_delegate ziggs_access_request
37
+ ziggs_task_delegate ziggs_artifact_share ziggs_artifact_attach ziggs_artifact_contribute
38
+ ziggs_message_send ziggs_context_delegate ziggs_access_request
38
39
  ziggs_link_propose ziggs_introduction_mint ziggs_introduction_redeem
39
40
  `.trim().split(/\s+/));
40
41
  export function installDirectorySurface(server) {
@@ -56,6 +56,7 @@ interface SessionPayload {
56
56
  }>;
57
57
  truncatedProposals?: number;
58
58
  truncatedConnectionRequests?: number;
59
+ truncatedPlanAcks?: number;
59
60
  agreementsListAppUrl?: string;
60
61
  sessionChatCard?: string;
61
62
  notice?: string;
@@ -83,5 +84,5 @@ export declare function registerInboxCardResource(server: McpServer): void;
83
84
  * then it draws whatever `ui/notifications/tool-result` carries, and its
84
85
  * buttons send `ui/open-link`.
85
86
  */
86
- export declare const INBOX_CARD_HTML = "<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>Ziggs</title>\n<style>\n :root { --fg:#1d1a20; --muted:#6b6470; --line:#e7e2ea; --bg:#ffffff; --brand:#7c3aed; --chip:#f4f1f6; }\n @media (prefers-color-scheme: dark) { :root { --fg:#f2eef4; --muted:#a59cab; --line:#332d38; --bg:#1b171e; --brand:#a78bfa; --chip:#29232d; } }\n * { box-sizing: border-box; }\n body { margin:0; padding:14px 16px; font:14px/1.45 system-ui,-apple-system,\"Segoe UI\",sans-serif; color:var(--fg); background:var(--bg); }\n h2 { margin:0 0 8px; font-size:12px; font-weight:600; letter-spacing:.04em; text-transform:uppercase; color:var(--muted); }\n section + section { margin-top:16px; }\n ul { list-style:none; margin:0; padding:0; }\n li { display:flex; align-items:center; gap:12px; padding:9px 0; border-top:1px solid var(--line); }\n li:first-child { border-top:0; }\n .text { flex:1; min-width:0; }\n .title { font-weight:600; overflow-wrap:anywhere; }\n .meta { color:var(--muted); font-size:12px; }\n .chip { display:inline-block; padding:1px 7px; border-radius:999px; background:var(--chip); color:var(--muted); font-size:11px; margin-left:6px; font-weight:500; }\n button { flex:none; min-height:32px; padding:0 12px; border-radius:8px; border:1px solid var(--line); background:transparent; color:var(--fg); font:inherit; font-size:13px; cursor:pointer; }\n button.primary { background:var(--brand); border-color:var(--brand); color:#fff; }\n .empty { color:var(--muted); }\n .all { margin-top:12px; }\n</style>\n</head>\n<body>\n<main id=\"root\"><p class=\"empty\">Loading your Ziggs inbox\u2026</p></main>\n<script>\n(function () {\n var nextId = 1, pending = {}, remeasure = null;\n function send(method, params) {\n var id = nextId++;\n window.parent.postMessage({ jsonrpc: \"2.0\", id: id, method: method, params: params || {} }, \"*\");\n return new Promise(function (resolve, reject) { pending[id] = { resolve: resolve, reject: reject }; });\n }\n function notify(method, params) {\n window.parent.postMessage({ jsonrpc: \"2.0\", method: method, params: params || {} }, \"*\");\n }\n function el(tag, cls, text) {\n var node = document.createElement(tag);\n if (cls) node.className = cls;\n if (text != null) node.textContent = String(text);\n return node;\n }\n function open(url) {\n if (!url) return;\n send(\"ui/open-link\", { url: url }).catch(function () {});\n }\n function row(title, chip, meta, label, url, primary) {\n var li = el(\"li\"), text = el(\"div\", \"text\"), head = el(\"div\", \"title\", title);\n if (chip) head.appendChild(el(\"span\", \"chip\", chip));\n text.appendChild(head);\n if (meta) text.appendChild(el(\"div\", \"meta\", meta));\n li.appendChild(text);\n if (url) {\n var button = el(\"button\", primary ? \"primary\" : \"\", label);\n button.type = \"button\";\n button.addEventListener(\"click\", function () { open(url); });\n li.appendChild(button);\n }\n return li;\n }\n function kindLabel(kind) {\n return kind === \"link_request\" ? \"Link request\" : kind === \"proposal\" ? \"Proposal\" : \"\";\n }\n function render(data) {\n var root = document.getElementById(\"root\");\n root.textContent = \"\";\n data = data || {};\n var waiting = data.waitingOnYou || [], work = data.yourWork || [], more = data.moreWaiting || 0;\n if (!waiting.length && !more && !work.length) {\n root.appendChild(el(\"p\", \"empty\", \"Nothing is waiting on you in Ziggs.\"));\n }\n if (waiting.length || more) {\n var s1 = el(\"section\"), l1 = el(\"ul\");\n s1.appendChild(el(\"h2\", \"\", \"Waiting on you\"));\n waiting.forEach(function (item) {\n var meta = [item.who, item.when].filter(Boolean).join(\" \u00B7 \");\n l1.appendChild(row(item.title, kindLabel(item.kind), meta, \"Review\", item.url, true));\n });\n s1.appendChild(l1);\n if (more) s1.appendChild(el(\"p\", \"meta\", more + \" more on the website\"));\n root.appendChild(s1);\n }\n if (work.length) {\n var s2 = el(\"section\"), l2 = el(\"ul\");\n s2.appendChild(el(\"h2\", \"\", \"Your work\"));\n work.forEach(function (item) {\n l2.appendChild(row(item.title, null, item.progress ? \"Plan \" + item.progress : \"In progress\", \"Open\", item.url, false));\n });\n s2.appendChild(l2);\n root.appendChild(s2);\n }\n if (data.allUrl) {\n var all = el(\"button\", \"all\", \"Open Ziggs\");\n all.type = \"button\";\n all.addEventListener(\"click\", function () { open(data.allUrl); });\n root.appendChild(all);\n }\n if (remeasure) remeasure();\n }\n window.addEventListener(\"message\", function (event) {\n if (event.source !== window.parent) return;\n var msg = event.data;\n if (!msg || msg.jsonrpc !== \"2.0\") return;\n if (msg.id != null && pending[msg.id] && !msg.method) {\n var waiter = pending[msg.id];\n delete pending[msg.id];\n if (msg.error) waiter.reject(msg.error); else waiter.resolve(msg.result);\n return;\n }\n if (msg.method === \"ui/notifications/tool-result\") {\n render((msg.params || {}).structuredContent);\n }\n });\n // A host sizes the frame from what the view reports. Without a report the\n // frame stays at zero height and the card is drawn but never seen.\n function reportSize() {\n var lastWidth = 0, lastHeight = 0, queued = false;\n function measure() {\n if (queued) return;\n queued = true;\n var next = typeof requestAnimationFrame === \"function\" ? requestAnimationFrame : function (fn) { setTimeout(fn, 16); };\n next(function () {\n queued = false;\n var doc = document.documentElement, previous = doc.style.height;\n doc.style.height = \"max-content\";\n var height = Math.ceil(doc.getBoundingClientRect().height);\n doc.style.height = previous;\n var width = Math.ceil(window.innerWidth);\n if (width === lastWidth && height === lastHeight) return;\n lastWidth = width; lastHeight = height;\n notify(\"ui/notifications/size-changed\", { width: width, height: height });\n });\n }\n remeasure = measure;\n measure();\n if (typeof ResizeObserver === \"function\") {\n var observer = new ResizeObserver(measure);\n observer.observe(document.documentElement);\n observer.observe(document.body);\n }\n }\n send(\"ui/initialize\", {\n protocolVersion: \"2026-01-26\",\n appInfo: { name: \"Ziggs inbox card\", version: \"1\" },\n appCapabilities: {}\n }).then(function () {\n notify(\"ui/notifications/initialized\", {});\n reportSize();\n }, function () {});\n})();\n</script>\n</body>\n</html>\n";
87
+ export declare const INBOX_CARD_HTML = "<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>Ziggs</title>\n<style>\n :root { --fg:#1d1a20; --muted:#6b6470; --line:#e7e2ea; --bg:#ffffff; --brand:#7c3aed; --chip:#f4f1f6; }\n @media (prefers-color-scheme: dark) { :root { --fg:#f2eef4; --muted:#a59cab; --line:#332d38; --bg:#1b171e; --brand:#a78bfa; --chip:#29232d; } }\n * { box-sizing: border-box; }\n body { margin:0; padding:14px 16px; font:14px/1.45 system-ui,-apple-system,\"Segoe UI\",sans-serif; color:var(--fg); background:var(--bg); }\n h2 { margin:0 0 8px; font-size:12px; font-weight:600; letter-spacing:.04em; text-transform:uppercase; color:var(--muted); }\n section + section { margin-top:16px; }\n ul { list-style:none; margin:0; padding:0; }\n li { display:flex; align-items:center; gap:12px; padding:9px 0; border-top:1px solid var(--line); }\n li:first-child { border-top:0; }\n .text { flex:1; min-width:0; }\n .title { font-weight:600; overflow-wrap:anywhere; }\n .meta { color:var(--muted); font-size:12px; }\n .chip { display:inline-block; padding:1px 7px; border-radius:999px; background:var(--chip); color:var(--muted); font-size:11px; margin-left:6px; font-weight:500; }\n button { flex:none; min-height:32px; padding:0 12px; border-radius:8px; border:1px solid var(--line); background:transparent; color:var(--fg); font:inherit; font-size:13px; cursor:pointer; }\n button.primary { background:var(--brand); border-color:var(--brand); color:#fff; }\n .empty { color:var(--muted); }\n .all { margin-top:12px; }\n</style>\n</head>\n<body>\n<main id=\"root\"><p class=\"empty\">Loading your Ziggs inbox\u2026</p></main>\n<script>\n(function () {\n var nextId = 1, pending = {}, remeasure = null;\n function send(method, params) {\n var id = nextId++;\n window.parent.postMessage({ jsonrpc: \"2.0\", id: id, method: method, params: params || {} }, \"*\");\n return new Promise(function (resolve, reject) { pending[id] = { resolve: resolve, reject: reject }; });\n }\n function notify(method, params) {\n window.parent.postMessage({ jsonrpc: \"2.0\", method: method, params: params || {} }, \"*\");\n }\n function el(tag, cls, text) {\n var node = document.createElement(tag);\n if (cls) node.className = cls;\n if (text != null) node.textContent = String(text);\n return node;\n }\n function open(url) {\n if (!url) return;\n send(\"ui/open-link\", { url: url }).catch(function () {});\n }\n function row(title, chip, meta, label, url, primary) {\n var li = el(\"li\"), text = el(\"div\", \"text\"), head = el(\"div\", \"title\", title);\n if (chip) head.appendChild(el(\"span\", \"chip\", chip));\n text.appendChild(head);\n if (meta) text.appendChild(el(\"div\", \"meta\", meta));\n li.appendChild(text);\n if (url) {\n var button = el(\"button\", primary ? \"primary\" : \"\", label);\n button.type = \"button\";\n button.addEventListener(\"click\", function () { open(url); });\n li.appendChild(button);\n }\n return li;\n }\n function kindLabel(kind) {\n return kind === \"link_request\" ? \"Link request\" : kind === \"proposal\" ? \"Proposal\" : kind === \"plan_ack\" ? \"Plan\" : \"\";\n }\n function render(data) {\n var root = document.getElementById(\"root\");\n root.textContent = \"\";\n data = data || {};\n var waiting = data.waitingOnYou || [], work = data.yourWork || [], more = data.moreWaiting || 0;\n if (!waiting.length && !more && !work.length) {\n root.appendChild(el(\"p\", \"empty\", \"Nothing is waiting on you in Ziggs.\"));\n }\n if (waiting.length || more) {\n var s1 = el(\"section\"), l1 = el(\"ul\");\n s1.appendChild(el(\"h2\", \"\", \"Waiting on you\"));\n waiting.forEach(function (item) {\n var meta = [item.who, item.when].filter(Boolean).join(\" \u00B7 \");\n l1.appendChild(row(item.title, kindLabel(item.kind), meta, \"Review\", item.url, true));\n });\n s1.appendChild(l1);\n if (more) s1.appendChild(el(\"p\", \"meta\", more + \" more on the website\"));\n root.appendChild(s1);\n }\n if (work.length) {\n var s2 = el(\"section\"), l2 = el(\"ul\");\n s2.appendChild(el(\"h2\", \"\", \"Your work\"));\n work.forEach(function (item) {\n l2.appendChild(row(item.title, null, item.progress ? \"Plan \" + item.progress : \"In progress\", \"Open\", item.url, false));\n });\n s2.appendChild(l2);\n root.appendChild(s2);\n }\n if (data.allUrl) {\n var all = el(\"button\", \"all\", \"Open Ziggs\");\n all.type = \"button\";\n all.addEventListener(\"click\", function () { open(data.allUrl); });\n root.appendChild(all);\n }\n if (remeasure) remeasure();\n }\n window.addEventListener(\"message\", function (event) {\n if (event.source !== window.parent) return;\n var msg = event.data;\n if (!msg || msg.jsonrpc !== \"2.0\") return;\n if (msg.id != null && pending[msg.id] && !msg.method) {\n var waiter = pending[msg.id];\n delete pending[msg.id];\n if (msg.error) waiter.reject(msg.error); else waiter.resolve(msg.result);\n return;\n }\n if (msg.method === \"ui/notifications/tool-result\") {\n render((msg.params || {}).structuredContent);\n }\n });\n // A host sizes the frame from what the view reports. Without a report the\n // frame stays at zero height and the card is drawn but never seen.\n function reportSize() {\n var lastWidth = 0, lastHeight = 0, queued = false;\n function measure() {\n if (queued) return;\n queued = true;\n var next = typeof requestAnimationFrame === \"function\" ? requestAnimationFrame : function (fn) { setTimeout(fn, 16); };\n next(function () {\n queued = false;\n var doc = document.documentElement, previous = doc.style.height;\n doc.style.height = \"max-content\";\n var height = Math.ceil(doc.getBoundingClientRect().height);\n doc.style.height = previous;\n var width = Math.ceil(window.innerWidth);\n if (width === lastWidth && height === lastHeight) return;\n lastWidth = width; lastHeight = height;\n notify(\"ui/notifications/size-changed\", { width: width, height: height });\n });\n }\n remeasure = measure;\n measure();\n if (typeof ResizeObserver === \"function\") {\n var observer = new ResizeObserver(measure);\n observer.observe(document.documentElement);\n observer.observe(document.body);\n }\n }\n send(\"ui/initialize\", {\n protocolVersion: \"2026-01-26\",\n appInfo: { name: \"Ziggs inbox card\", version: \"1\" },\n appCapabilities: {}\n }).then(function () {\n notify(\"ui/notifications/initialized\", {});\n reportSize();\n }, function () {});\n})();\n</script>\n</body>\n</html>\n";
87
88
  export {};
package/dist/inboxCard.js CHANGED
@@ -22,7 +22,9 @@ export function inboxCardData(payload) {
22
22
  when: item.proposedAtLabel,
23
23
  url: item.appUrl,
24
24
  })),
25
- moreWaiting: (payload.truncatedProposals ?? 0) + (payload.truncatedConnectionRequests ?? 0),
25
+ moreWaiting: (payload.truncatedProposals ?? 0) +
26
+ (payload.truncatedConnectionRequests ?? 0) +
27
+ (payload.truncatedPlanAcks ?? 0),
26
28
  yourWork: (payload.activeWork ?? []).map((item) => ({
27
29
  title: item.title,
28
30
  progress: item.planTotal > 0 ? `${item.planDone}/${item.planTotal}` : null,
@@ -132,7 +134,7 @@ export const INBOX_CARD_HTML = `<!doctype html>
132
134
  return li;
133
135
  }
134
136
  function kindLabel(kind) {
135
- return kind === "link_request" ? "Link request" : kind === "proposal" ? "Proposal" : "";
137
+ return kind === "link_request" ? "Link request" : kind === "proposal" ? "Proposal" : kind === "plan_ack" ? "Plan" : "";
136
138
  }
137
139
  function render(data) {
138
140
  var root = document.getElementById("root");
@@ -42,8 +42,8 @@ export function observeFor(creds) {
42
42
  export function channelEvent(delivery, agentId) {
43
43
  const forYou = !!agentId && delivery.assigneeId === agentId;
44
44
  const meta = { kind: delivery.kind, resource_id: delivery.resourceId };
45
- if (delivery.chatId)
46
- meta.chat_id = delivery.chatId;
45
+ if (delivery.addressId)
46
+ meta.chat_id = delivery.addressId;
47
47
  if (delivery.taskId)
48
48
  meta.task_id = delivery.taskId;
49
49
  if (delivery.agreementId)
@@ -117,8 +117,8 @@ export function sectionOf(row, agentIds) {
117
117
  }
118
118
  /** What a row is about: its chat, else its task, else its agreement, else itself. */
119
119
  function anchorOf(row) {
120
- if (row.chatId)
121
- return ['chatId', String(row.chatId)];
120
+ if (row.addressId)
121
+ return ['addressId', String(row.addressId)];
122
122
  if (row.taskId)
123
123
  return ['taskId', String(row.taskId)];
124
124
  if (row.agreementId)
@@ -53,7 +53,7 @@ interface OutOfReachNote {
53
53
  requestReplyAccess?: AccessRequestCall<'reply'>;
54
54
  }
55
55
  /** What to do with rows marked `outOfReach`, said once per inbox read. */
56
- export declare const OUT_OF_REACH_NOTE = "Rows with outOfReach: true are in rooms or resources you cannot read. ziggs_chat_list with readable: false shows each room you may list and the read request to file; for anything else, ziggs_open on it returns the request to make.";
56
+ export declare const OUT_OF_REACH_NOTE = "Rows with outOfReach: true name resources you cannot read. Use ziggs_open on the exact returned resource to inspect its available access request. A receipt grants no access.";
57
57
  /**
58
58
  * replace the free-text `nextActions` hints with typed `readPlan`
59
59
  * call objects — tool name + pre-filled args — so the most common loop
@@ -3,7 +3,7 @@ import { buildDecidedForMeItems, formatPendingDecisionsPayload, humanAttentionFo
3
3
  /** Keep the plan bounded; the full deliveries array still carries everything. */
4
4
  const MAX_READ_PLAN = 12;
5
5
  /** What to do with rows marked `outOfReach`, said once per inbox read. */
6
- export const OUT_OF_REACH_NOTE = 'Rows with outOfReach: true are in rooms or resources you cannot read. ziggs_chat_list with readable: false shows each room you may list and the read request to file; for anything else, ziggs_open on it returns the request to make.';
6
+ export const OUT_OF_REACH_NOTE = 'Rows with outOfReach: true name resources you cannot read. Use ziggs_open on the exact returned resource to inspect its available access request. A receipt grants no access.';
7
7
  function outOfReachOf(d) {
8
8
  const note = d.outOfReach;
9
9
  if (!note || typeof note !== 'object')
@@ -48,8 +48,7 @@ export function buildReadPlan(inbox, grantsByScope, self = { agentId: '' }) {
48
48
  const proposals = inbox.proposalsAwaitingMe ?? [];
49
49
  const connectionRequests = inbox.connectionRequestsAwaitingMe ?? [];
50
50
  const deliveries = inbox.deliveries ?? [];
51
- // dedup by call signature so the same (type, via) can't appear
52
- // twice when several deliveries land in one chat. Collect candidates
51
+ // Deduplicate repeated receipts or resource calls. Collect candidates
53
52
  // uncapped; the cap is applied once, after the ack is reserved, so the ack
54
53
  // step always survives.
55
54
  //
@@ -65,12 +64,11 @@ export function buildReadPlan(inbox, grantsByScope, self = { agentId: '' }) {
65
64
  const seen = new Set();
66
65
  // Which assigned rows each candidate step settles, so a checkpoint ack can
67
66
  // name what is finished once that step is done. Keyed by the step's own
68
- // dedup key, because one read covers every row that landed in its chat.
67
+ // dedup key. Message responsibilities each keep their own execution.
69
68
  const settledBy = new Map();
70
69
  const candidateKeys = [];
71
70
  const add = (key, call, mine = true, settles) => {
72
- // Recorded before the dedup return: the second row landing in a chat adds
73
- // no step, but the one step there already covers it.
71
+ // Record every receipt covered by an identical resource call before deduplication.
74
72
  if (settles) {
75
73
  const rows = settledBy.get(key);
76
74
  if (rows)
@@ -107,8 +105,7 @@ export function buildReadPlan(inbox, grantsByScope, self = { agentId: '' }) {
107
105
  * the ack rides on all of it fitting, exactly as before.
108
106
  */
109
107
  const isMine = (d) => self.agentIds?.includes(d.assigneeId ?? '') || (!self.agentIds && self.agentId === '') || d.assigneeId === self.agentId;
110
- // Reads — one call per place mail actually landed. The delivery names the
111
- // chat, agreement or task directly, so nothing has to be inferred from a scope.
108
+ // Read the exact delivered resource. History and navigation are optional.
112
109
  //
113
110
  // Total over BOTH axes, and that is the whole point of this shape:
114
111
  //
@@ -117,7 +114,7 @@ export function buildReadPlan(inbox, grantsByScope, self = { agentId: '' }) {
117
114
  // - over the anchor, so is an artifact carrying none of chat/agreement/task
118
115
  // — it is read as `artifact:<id>`, the entry added for that case.
119
116
  //
120
- // The first is history: while this looked at chatId/agreementId only, a
117
+ // The first is history: while this looked at addressId/agreementId only, a
121
118
  // task-bound deliverable produced an envelope whose plan was the ack and
122
119
  // nothing else, and an agent following the plan acked work it never read. The
123
120
  // second is that hole closed ahead of an emitter — nothing writes an
@@ -140,13 +137,13 @@ export function buildReadPlan(inbox, grantsByScope, self = { agentId: '' }) {
140
137
  const note = outOfReachOf(d);
141
138
  if (note) {
142
139
  unreadable.push({ resourceId: d.resourceId, ...note,
143
- ...(['chat', 'agreement', 'task', 'artifact'].includes(note.scopeKind) ? {
144
- requestAccess: { tool: 'ziggs_access_request', args: { scopeKind: note.scopeKind, scopeId: note.scopeId, permission: 'read', temporal: 'from-start' }, requiredInput: note.scopeKind === 'artifact' ? ['reason', 'recipientId', 'chatId'] : ['reason', 'recipientId'], effect: 'request_only' },
140
+ ...(['address', 'agreement', 'task', 'artifact'].includes(note.scopeKind) ? {
141
+ requestAccess: { tool: 'ziggs_access_request', args: { scopeKind: note.scopeKind, scopeId: note.scopeId, permission: 'read', temporal: 'from-start' }, requiredInput: ['reason', 'recipientId'], effect: 'request_only' },
145
142
  } : {}),
146
- ...(note.scopeKind === 'chat' ? {
143
+ ...(note.scopeKind === 'address' ? {
147
144
  requestReplyAccess: {
148
145
  tool: 'ziggs_access_request',
149
- args: { scopeKind: 'chat', scopeId: note.scopeId, permission: 'reply', temporal: 'from-start' },
146
+ args: { scopeKind: 'address', scopeId: note.scopeId, permission: 'reply', temporal: 'from-start' },
150
147
  requiredInput: ['reason', 'recipientId'],
151
148
  effect: 'request_only',
152
149
  },
@@ -155,24 +152,27 @@ export function buildReadPlan(inbox, grantsByScope, self = { agentId: '' }) {
155
152
  continue;
156
153
  }
157
154
  const mine = isMine(d);
158
- if (mine && d.executionContext && d.responsibilityId) {
159
- const ref = d.kind === 'message' ? d.chatId : d.resourceId;
155
+ if (d.kind === 'message') {
156
+ add(`message:${d.receiptId ?? d.resourceId}`, {
157
+ tool: 'ziggs_message_read',
158
+ args: { messageId: d.resourceId, ...(d.executionContext ? { executionContextId: d.executionContext.contextId } : {}) },
159
+ why: 'Read the exact message. Keep executions separate; reading does not handle the receipt.',
160
+ }, mine, mine ? d.receiptId : undefined);
161
+ continue;
162
+ }
163
+ if (mine && d.executionContext && d.receiptId) {
164
+ const ref = d.resourceId;
160
165
  if (ref)
161
- add(`execution:${d.responsibilityId}`, {
166
+ add(`execution:${d.receiptId}`, {
162
167
  tool: 'ziggs_open', args: { ref, executionContextId: d.executionContext.contextId },
163
168
  why: `Handle this responsibility as ${d.executionContext.position}. Carry executionContextId on subsequent calls. Use separate model context for different execution contexts.`,
164
- }, true, d.responsibilityId);
169
+ }, true, d.receiptId);
165
170
  continue;
166
171
  }
167
172
  switch (d.kind) {
168
- case 'message':
169
- // A message always lands in a chat; open the ordinary chat id.
170
- if (d.chatId)
171
- open('chat', d.chatId, mine, mine ? d.responsibilityId : undefined);
172
- break;
173
173
  case 'artifact':
174
174
  // The delivery's resourceId is the artifact. Do not reconstruct via.
175
- open('artifact', d.resourceId, mine, mine ? d.responsibilityId : undefined);
175
+ open('artifact', d.resourceId, mine, mine ? d.receiptId : undefined);
176
176
  break;
177
177
  case 'agreement': {
178
178
  // An agreement row whose reason names a decision on a request this
@@ -192,7 +192,7 @@ export function buildReadPlan(inbox, grantsByScope, self = { agentId: '' }) {
192
192
  surface: 'mcp',
193
193
  });
194
194
  if (words) {
195
- add(`open:agreement:${agreementId}`, { tool: 'ziggs_open', args: { ref: agreementId }, why: words.sentence }, mine, mine ? d.responsibilityId : undefined);
195
+ add(`open:agreement:${agreementId}`, { tool: 'ziggs_open', args: { ref: agreementId }, why: words.sentence }, mine, mine ? d.receiptId : undefined);
196
196
  }
197
197
  break;
198
198
  }
@@ -294,7 +294,7 @@ export function buildReadPlan(inbox, grantsByScope, self = { agentId: '' }) {
294
294
  'Reads of rows assigned to others never count; skip them freely. '
295
295
  : 'no row in this window is assigned to you; acking only moves your mark past context that other windows handle. ') +
296
296
  'Pass ackTo back VERBATIM (it is opaque). ' +
297
- 'Handled only some? Pass the same ackTo with only their responsibilityIds in handledDeliveryIds; the rest stays pending',
297
+ 'Handled only some? Pass the same ackTo with only their receiptIds in handledDeliveryIds; the rest stays pending',
298
298
  });
299
299
  }
300
300
  return { plan, truncated, unreadable };
@@ -413,7 +413,7 @@ self = { agentId: '' }) {
413
413
  }
414
414
  : {};
415
415
  const blockedAssignments = [
416
- ...(inbox.deliveries ?? []).filter(d => d.executionBlocked).map(d => ({ resourceId: d.resourceId, responsibilityId: d.responsibilityId, ...d.executionBlocked })),
416
+ ...(inbox.deliveries ?? []).filter(d => d.executionBlocked).map(d => ({ resourceId: d.resourceId, receiptId: d.receiptId, ...d.executionBlocked })),
417
417
  ...(inbox.tasksAwaitingMe ?? []).filter(t => t.executionBlocked).map(t => ({ resourceId: t.taskId, ...t.executionBlocked })),
418
418
  ...(inbox.proposalsAwaitingMe ?? []).filter(p => p.executionBlocked).map(p => ({ resourceId: p.agreementId, ...p.executionBlocked })),
419
419
  ];
@@ -1,7 +1,7 @@
1
1
  export { resolveWebAppOrigin, agreementAppUrl, agreementsListAppUrl, connectionsSettingsAppUrl, walletAppUrl } from '@ziggs-ai/api-client';
2
2
  import { type DecisionOutcome, type InboxEnvelope, type InboxHumanAttention, type InboxTaskRef, type Task } from '@ziggs-ai/api-client';
3
3
  import type { ReadPlanCall } from './inboxToolResult.js';
4
- export type PendingDecisionKind = 'proposal' | 'link_request';
4
+ export type PendingDecisionKind = 'proposal' | 'link_request' | 'plan_ack';
5
5
  /**
6
6
  * A reference to a decision somebody is waiting on. Who may answer it is not
7
7
  * known here: the inbox lists slots, and a slot is not authority. The
@@ -9,8 +9,12 @@ export type PendingDecisionKind = 'proposal' | 'link_request';
9
9
  */
10
10
  export interface PendingDecisionItem {
11
11
  kind: PendingDecisionKind;
12
- /** Agreement id for proposals; requestId for link requests. */
12
+ /**
13
+ * Agreement id for proposals and plans; requestId for link requests.
14
+ */
13
15
  agreementId: string;
16
+ /** The task whose plan waits, on a `plan_ack`. */
17
+ taskId?: string;
14
18
  title: string;
15
19
  subtitle: string | null;
16
20
  proposedAt: string | null;
@@ -107,6 +111,7 @@ export declare function buildActiveWorkItemsFromInbox(refs: InboxTaskRef[], webO
107
111
  export declare function buildSessionChatCard(decisions: PendingDecisionItem[], work: ActiveWorkItem[], opts: {
108
112
  truncatedProposals?: number;
109
113
  truncatedConnectionRequests?: number;
114
+ truncatedPlanAcks?: number;
110
115
  agreementsListAppUrl?: string;
111
116
  }): string;
112
117
  /** Structured session payload for MCP tools ( + active work). */
@@ -181,8 +181,27 @@ function linkToItem(c, origin) {
181
181
  proposedAt: c.requestedAt,
182
182
  }, origin);
183
183
  }
184
+ /**
185
+ * A plan waiting on the person. The plan is acknowledged signed in on the
186
+ * agreement page, where the task and its plan are shown, so Review goes there.
187
+ */
188
+ function planAckToItem(a, origin) {
189
+ return {
190
+ ...decisionToItem('plan_ack', a.agreementId, {
191
+ title: displayTitle(a.title?.trim() || '(untitled task)'),
192
+ subtitle: a.scope === 'mid_work'
193
+ ? 'The plan changed during the work.'
194
+ : 'Work starts once you acknowledge the plan.',
195
+ proposedAt: a.updatedAt,
196
+ }, origin),
197
+ taskId: a.taskId,
198
+ };
199
+ }
184
200
  export function buildPendingDecisionItems(inbox, webOrigin) {
185
201
  const items = [];
202
+ for (const a of inbox.planAcksAwaitingMe ?? []) {
203
+ items.push(planAckToItem(a, webOrigin));
204
+ }
186
205
  for (const p of inbox.proposalsAwaitingMe ?? []) {
187
206
  items.push(proposalToItem(p, webOrigin));
188
207
  }
@@ -263,18 +282,24 @@ export function buildActiveWorkItemsFromInbox(refs, webOrigin) {
263
282
  function kindLabel(kind) {
264
283
  if (kind === 'proposal')
265
284
  return 'Agreement proposal';
285
+ if (kind === 'plan_ack')
286
+ return 'Plan to acknowledge';
266
287
  return 'Agent link request';
267
288
  }
268
289
  function kindHint(kind) {
269
290
  if (kind === 'proposal') {
270
291
  return 'Someone proposed work or terms — your approval opens or rejects it.';
271
292
  }
293
+ if (kind === 'plan_ack') {
294
+ return 'Work you are paying for has a plan waiting on you.';
295
+ }
272
296
  return 'Another agent wants to link — your approval enables cross-org reach.';
273
297
  }
274
298
  function buildDecisionSection(items, opts) {
275
299
  const truncatedProposals = opts.truncatedProposals ?? 0;
276
300
  const truncatedConnectionRequests = opts.truncatedConnectionRequests ?? 0;
277
- const truncated = truncatedProposals + truncatedConnectionRequests;
301
+ const truncatedPlanAcks = opts.truncatedPlanAcks ?? 0;
302
+ const truncated = truncatedProposals + truncatedConnectionRequests + truncatedPlanAcks;
278
303
  if (!items.length && truncated === 0)
279
304
  return [];
280
305
  const lines = [];
@@ -287,7 +312,7 @@ function buildDecisionSection(items, opts) {
287
312
  lines.push('');
288
313
  lines.push(`**${item.title}**`);
289
314
  lines.push('');
290
- lines.push(`\`${item.agreementId}\`${item.proposedAtLabel ? ` · ${item.proposedAtLabel}` : ''}`);
315
+ lines.push(`\`${item.taskId ?? item.agreementId}\`${item.proposedAtLabel ? ` · ${item.proposedAtLabel}` : ''}`);
291
316
  lines.push('');
292
317
  lines.push(`_${kindHint(item.kind)}_`);
293
318
  if (item.subtitle) {
@@ -297,15 +322,20 @@ function buildDecisionSection(items, opts) {
297
322
  lines.push('');
298
323
  lines.push(`[Review in Ziggs →](${item.appUrl})`);
299
324
  lines.push('');
300
- // No pre-filled respond call: a slot is not authority, and a call that
301
- // 403s every time is worse than a line that says where the answer is.
302
- lines.push('Review the terms here. The agent reads approvalAction on the agreement to check whether an existing grant lets it decide; otherwise the accountable party decides in Ziggs.');
325
+ if (item.kind === 'plan_ack') {
326
+ lines.push('The person acknowledges the plan in Ziggs, signed in as themselves.');
327
+ }
328
+ else {
329
+ // No pre-filled respond call: a slot is not authority, and a call that
330
+ // 403s every time is worse than a line that says where the answer is.
331
+ lines.push('Review the terms here. The agent reads approvalAction on the agreement to check whether an existing grant lets it decide; otherwise the accountable party decides in Ziggs.');
332
+ }
303
333
  lines.push('');
304
334
  }
305
335
  if (truncated > 0) {
306
336
  lines.push('---');
307
337
  lines.push('');
308
- lines.push(`_+${truncated} more pending (${truncatedProposals} proposal(s), ${truncatedConnectionRequests} link(s)) — not listed here._`);
338
+ lines.push(`_+${truncated} more pending (${truncatedProposals} proposal(s), ${truncatedConnectionRequests} link(s)${truncatedPlanAcks ? `, ${truncatedPlanAcks} plan(s)` : ''}) — not listed here._`);
309
339
  lines.push('');
310
340
  }
311
341
  return lines;
@@ -352,7 +382,9 @@ function buildWorkSection(work, startIndex = 1) {
352
382
  export function buildSessionChatCard(decisions, work, opts) {
353
383
  const truncatedProposals = opts.truncatedProposals ?? 0;
354
384
  const truncatedConnectionRequests = opts.truncatedConnectionRequests ?? 0;
355
- const truncated = truncatedProposals + truncatedConnectionRequests;
385
+ const truncatedPlanAcks = opts.truncatedPlanAcks ?? 0;
386
+ const truncated = truncatedProposals + truncatedConnectionRequests + truncatedPlanAcks;
387
+ const planAckCount = decisions.filter((d) => d.kind === 'plan_ack').length;
356
388
  const decisionListed = decisions.length + truncated;
357
389
  const workCount = work.length;
358
390
  const total = decisionListed + workCount;
@@ -365,9 +397,14 @@ export function buildSessionChatCard(decisions, work, opts) {
365
397
  '|:--|--:|',
366
398
  ...(decisionListed > 0
367
399
  ? [
368
- `| Approve / reject | **${decisionListed}** |`,
400
+ `| ${planAckCount + truncatedPlanAcks > 0 ? 'Approve / reject / acknowledge' : 'Approve / reject'} | **${decisionListed}** |`,
369
401
  `| — proposals | ${decisions.filter((d) => d.kind === 'proposal').length}${truncatedProposals ? ` (+${truncatedProposals} hidden)` : ''} |`,
370
402
  `| — link requests | ${decisions.filter((d) => d.kind === 'link_request').length}${truncatedConnectionRequests ? ` (+${truncatedConnectionRequests} hidden)` : ''} |`,
403
+ ...(planAckCount + truncatedPlanAcks > 0
404
+ ? [
405
+ `| — plans to acknowledge | ${planAckCount}${truncatedPlanAcks ? ` (+${truncatedPlanAcks} hidden)` : ''} |`,
406
+ ]
407
+ : []),
371
408
  ]
372
409
  : []),
373
410
  ...(workCount > 0 ? [`| Active tasks (your work) | **${workCount}** |`] : []),
@@ -393,18 +430,26 @@ export function buildSessionChatCard(decisions, work, opts) {
393
430
  export function formatPendingDecisionsPayload(inbox, webOrigin, opts) {
394
431
  const withSessionCard = opts?.withSessionCard !== false;
395
432
  const decisions = buildPendingDecisionItems(inbox, webOrigin);
396
- const activeWork = opts?.activeTasks !== undefined
433
+ // A task whose plan waits on the person is theirs to acknowledge before it
434
+ // is anyone's work, so it is listed once, as the decision.
435
+ const waitingPlans = new Set((inbox.planAcksAwaitingMe ?? []).map((a) => a.taskId));
436
+ const activeWork = (opts?.activeTasks !== undefined
397
437
  ? buildActiveWorkItems(opts.activeTasks, webOrigin)
398
- : buildActiveWorkItemsFromInbox(inbox.tasksAwaitingMe ?? [], webOrigin);
438
+ : buildActiveWorkItemsFromInbox(inbox.tasksAwaitingMe ?? [], webOrigin)).filter((w) => !waitingPlans.has(w.taskId));
399
439
  const truncatedProposals = inbox.truncatedProposals ?? 0;
400
440
  const truncatedConnectionRequests = inbox.truncatedConnectionRequests ?? 0;
401
- const pendingCount = decisions.length + truncatedProposals + truncatedConnectionRequests;
441
+ const truncatedPlanAcks = inbox.truncatedPlanAcks ?? 0;
442
+ const pendingCount = decisions.length +
443
+ truncatedProposals +
444
+ truncatedConnectionRequests +
445
+ truncatedPlanAcks;
402
446
  const activeWorkCount = activeWork.length;
403
447
  const actionCount = pendingCount + activeWorkCount;
404
448
  const listUrl = agreementsListAppUrl(webOrigin);
405
449
  const cardOpts = {
406
450
  truncatedProposals,
407
451
  truncatedConnectionRequests,
452
+ truncatedPlanAcks,
408
453
  agreementsListAppUrl: listUrl,
409
454
  };
410
455
  // Only the session-start read ships it. The long-poll shape passes
@@ -415,6 +460,7 @@ export function formatPendingDecisionsPayload(inbox, webOrigin, opts) {
415
460
  : null;
416
461
  const proposalCount = decisions.filter((d) => d.kind === 'proposal').length;
417
462
  const linkCount = decisions.filter((d) => d.kind === 'link_request').length;
463
+ const planAckCount = decisions.filter((d) => d.kind === 'plan_ack').length;
418
464
  // Decisions somebody made on requests this side made. Counted here and named
419
465
  // in the line the model reads; the entries themselves ride the news half of
420
466
  // the inbox result (`decidedForMe`), which the cold read already carries.
@@ -444,7 +490,7 @@ export function formatPendingDecisionsPayload(inbox, webOrigin, opts) {
444
490
  : 'No pending decisions or active tasks.');
445
491
  }
446
492
  else {
447
- parts.push(`${count(pendingCount, 'pending decision', 'pending decisions')} (${count(proposalCount + truncatedProposals, 'proposal', 'proposals')}, ${count(linkCount + truncatedConnectionRequests, 'link request', 'link requests')}) and ${count(activeWorkCount, 'active task', 'active tasks')}.`);
493
+ parts.push(`${count(pendingCount, 'pending decision', 'pending decisions')} (${count(proposalCount + truncatedProposals, 'proposal', 'proposals')}, ${count(linkCount + truncatedConnectionRequests, 'link request', 'link requests')}${planAckCount + truncatedPlanAcks > 0 ? `, ${count(planAckCount + truncatedPlanAcks, 'plan to acknowledge', 'plans to acknowledge')}` : ''}) and ${count(activeWorkCount, 'active task', 'active tasks')}.`);
448
494
  }
449
495
  if (decidedNote)
450
496
  parts.push(decidedNote);
@@ -464,9 +510,10 @@ export function formatPendingDecisionsPayload(inbox, webOrigin, opts) {
464
510
  summary: {
465
511
  proposals: proposalCount + truncatedProposals,
466
512
  linkRequests: linkCount + truncatedConnectionRequests,
513
+ planAcks: planAckCount + truncatedPlanAcks,
467
514
  activeTasks: activeWorkCount,
468
515
  listed: decisions.length,
469
- truncated: truncatedProposals + truncatedConnectionRequests,
516
+ truncated: truncatedProposals + truncatedConnectionRequests + truncatedPlanAcks,
470
517
  decidedForMe: decidedForMe.filter((i) => i.outcome !== 'held').length,
471
518
  refusals: decidedForMe.filter((i) => i.outcome === 'rejected').length,
472
519
  },
@@ -474,6 +521,7 @@ export function formatPendingDecisionsPayload(inbox, webOrigin, opts) {
474
521
  activeWork,
475
522
  truncatedProposals,
476
523
  truncatedConnectionRequests,
524
+ truncatedPlanAcks,
477
525
  agreementsListAppUrl: listUrl,
478
526
  connectionsSettingsAppUrl: connectionsSettingsAppUrl(webOrigin),
479
527
  // one card per session start. The owner (withSessionCard) embeds
@@ -25,7 +25,7 @@ const INBOX = {
25
25
  };
26
26
  const WAIT = 'If finishing the request needs a reply or decision, keep checking ziggs_inbox with waitSeconds: 55; handle arrivals and continue. After ten minutes, report what is missing. Promise later updates only with a configured host wake or schedule.';
27
27
  const ACCESS = 'Missing access: use ziggs_access_request for that resource. Choose requestAccess for reading or requestReplyAccess/the reply recovery for answering; reply includes read. A request grants nothing until approved.';
28
- const REPLY = 'Reply in the original chatId using its reply action. Sending only to a person may select a different pair room; missing access does not move the conversation.';
28
+ const REPLY = 'Reply in the original addressId using its reply action. Sending only to a person may select a different pair room; missing access does not move the conversation.';
29
29
  const GROUNDING = 'Ground summaries in source records: keep each date tied to its named milestone, and distinguish inquiries, approvals and completed actions. Verify before claiming completion.';
30
30
  const RULES = [
31
31
  WAIT,
@@ -34,7 +34,7 @@ const RULES = [
34
34
  ACCESS,
35
35
  REPLY,
36
36
  GROUNDING,
37
- 'A send refused with WORK_SELECTION_REQUIRED lists choices: set conversationAgreementId to one of them; never guess.',
37
+ 'A send refused with WORK_SELECTION_REQUIRED lists choices: set workAgreementId to one of them; never guess.',
38
38
  'Agreement and task writes can reserve or settle points. After an uncertain write, read the record, then retry with the same idempotencyKey.',
39
39
  ];
40
40
  /**