@ziggs-ai/ziggs-mcp 0.38.2 → 0.38.3
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 +4 -0
- package/dist/inboxCard.d.ts +2 -1
- package/dist/inboxCard.js +4 -2
- package/dist/pendingDecisions.d.ts +7 -2
- package/dist/pendingDecisions.js +61 -13
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.38.3
|
|
6
|
+
|
|
7
|
+
- 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.
|
|
8
|
+
|
|
5
9
|
## 0.38.2
|
|
6
10
|
|
|
7
11
|
- Publish with npm provenance attestations from GitHub Actions.
|
package/dist/inboxCard.d.ts
CHANGED
|
@@ -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) +
|
|
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");
|
|
@@ -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
|
-
/**
|
|
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). */
|
package/dist/pendingDecisions.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
301
|
-
|
|
302
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
package/package.json
CHANGED