@ziggs-ai/ziggs-mcp 0.37.7 → 0.37.9

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.
@@ -17,7 +17,8 @@
17
17
  * which `ziggs_auth_status` returns in full;
18
18
  * - a read step's `why` that only restates its tool and args, and the ack
19
19
  * step's long one, whose ids are the step's own args;
20
- * - an `outOfReach` entry's scope and remedy, which its delivery row carries;
20
+ * - a delivery's out-of-reach scope and remedy: the row keeps `outOfReach: true`
21
+ * beside its own ids, and the chat list says how to ask for the room;
21
22
  * - `sessionCardHint` ("the card is not in this response") when the card is.
22
23
  * Both doors assemble the session start from the long-poll result plus the
23
24
  * card, and the long-poll half's hint said the opposite of what followed.
@@ -92,5 +93,5 @@ export declare function compactTextResult(data: Json, reader?: InboxReader): {
92
93
  * inbox while assigned messages waited in `deliveries`. This adds what the
93
94
  * window holds, without changing what counts as pending or actionable.
94
95
  */
95
- export declare function windowNote(deliveries: unknown, selfAgentIds: readonly string[], outOfReach: number): string;
96
+ export declare function windowNote(deliveries: unknown, selfAgentIds: readonly string[]): string;
96
97
  export {};
@@ -17,7 +17,8 @@
17
17
  * which `ziggs_auth_status` returns in full;
18
18
  * - a read step's `why` that only restates its tool and args, and the ack
19
19
  * step's long one, whose ids are the step's own args;
20
- * - an `outOfReach` entry's scope and remedy, which its delivery row carries;
20
+ * - a delivery's out-of-reach scope and remedy: the row keeps `outOfReach: true`
21
+ * beside its own ids, and the chat list says how to ask for the room;
21
22
  * - `sessionCardHint` ("the card is not in this response") when the card is.
22
23
  * Both doors assemble the session start from the long-poll result plus the
23
24
  * card, and the long-poll half's hint said the opposite of what followed.
@@ -53,6 +54,10 @@ export function compactDelivery(row, kindOfParty, usualPartyId) {
53
54
  continue;
54
55
  if (key === 'partyId' && usualPartyId !== undefined && value === usualPartyId)
55
56
  continue;
57
+ if (key === 'outOfReach') {
58
+ out[key] = true;
59
+ continue;
60
+ }
56
61
  out[key] = value;
57
62
  }
58
63
  return out;
@@ -244,16 +249,6 @@ export function compactInboxResult(result, reader) {
244
249
  }
245
250
  if (isRecord(result.session))
246
251
  out.session = compactSession(result.session, result.connectionNotices);
247
- // The row already carries its scope and remedy; the list names the rows
248
- // and the request to make, which only it has.
249
- if (Array.isArray(result.outOfReach))
250
- out.outOfReach = result.outOfReach.map((entry) => isRecord(entry)
251
- ? {
252
- resourceId: entry.resourceId,
253
- ...(entry.requestAccess ? { requestAccess: entry.requestAccess } : {}),
254
- ...(entry.requestReplyAccess ? { requestReplyAccess: entry.requestReplyAccess } : {}),
255
- }
256
- : entry);
257
252
  if (Array.isArray(result.readPlan))
258
253
  out.readPlan = result.readPlan.map((step) => {
259
254
  if (!isRecord(step))
@@ -287,10 +282,11 @@ export function compactTextResult(data, reader) {
287
282
  * inbox while assigned messages waited in `deliveries`. This adds what the
288
283
  * window holds, without changing what counts as pending or actionable.
289
284
  */
290
- export function windowNote(deliveries, selfAgentIds, outOfReach) {
285
+ export function windowNote(deliveries, selfAgentIds) {
291
286
  const rows = Array.isArray(deliveries) ? deliveries.filter(isRecord) : [];
292
287
  if (!rows.length)
293
288
  return '';
289
+ const outOfReach = rows.filter((d) => d.outOfReach).length;
294
290
  // The same rule the grouping uses, so each count is its section's size.
295
291
  const count = (s) => rows.filter((d) => sectionOf(d, selfAgentIds) === s).length;
296
292
  const words = {
@@ -52,6 +52,8 @@ interface OutOfReachNote {
52
52
  /** Optional alternative when the person's work includes answering here. */
53
53
  requestReplyAccess?: AccessRequestCall<'reply'>;
54
54
  }
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.";
55
57
  /**
56
58
  * replace the free-text `nextActions` hints with typed `readPlan`
57
59
  * call objects — tool name + pre-filled args — so the most common loop
@@ -2,6 +2,8 @@ import { decisionWords, grantCaveat, hintsFromTasks, inboxEngagement, planInboxA
2
2
  import { buildDecidedForMeItems, formatPendingDecisionsPayload, humanAttentionForResult, resolveWebAppOrigin, } from './pendingDecisions.js';
3
3
  /** Keep the plan bounded; the full deliveries array still carries everything. */
4
4
  const MAX_READ_PLAN = 12;
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.';
5
7
  function outOfReachOf(d) {
6
8
  const note = d.outOfReach;
7
9
  if (!note || typeof note !== 'object')
@@ -416,10 +418,10 @@ self = { agentId: '' }) {
416
418
  : {}),
417
419
  ...(readPlan.length ? { readPlan } : {}),
418
420
  ...(readPlanTruncated ? { readPlanTruncated } : {}),
419
- // Named `outOfReach` on the result too, so the one word covers the whole
420
- // path: the row carries it, the plan skips it, and this is where the agent
421
- // reads what to say about it.
422
- ...(unreadable.length ? { outOfReach: unreadable } : {}),
421
+ // The row carries the flag and the plan skips it. Which room it is and how
422
+ // to ask for it is the chat list's answer, so the inbox says where to look
423
+ // once instead of repeating every row.
424
+ ...(unreadable.length ? { outOfReachNote: OUT_OF_REACH_NOTE } : {}),
423
425
  };
424
426
  const { humanAttention, ...rest } = inbox;
425
427
  const engagement = inboxEngagement({
package/dist/tools.js CHANGED
@@ -418,14 +418,25 @@ export function registerZiggsTools(server, creds, cfg) {
418
418
  fields: z
419
419
  .array(z.string())
420
420
  .optional()
421
- .describe('Return only these keys on each row. Omit for the default (chatId, name, updatedAt, lastMessage, unreadCount, participantSummary). Pass members and/or rosterSummary to include the other web-app rosters. Keep chatId if you will open or send next.'),
422
- }, readOnly('List your chats'), async ({ fields }) => {
421
+ .describe('Return only these keys on each row. Omit for the default (chatId, name, updatedAt, lastMessage, unreadCount, participantSummary, readable, requestAccess). Pass members and/or rosterSummary to include the other web-app rosters. Keep chatId if you will open or send next.'),
422
+ participantId: z
423
+ .string()
424
+ .optional()
425
+ .describe('Only rooms this person or agent is in.'),
426
+ readable: z
427
+ .boolean()
428
+ .optional()
429
+ .describe('true: only rooms you can read. false: only rooms you may list but not read, each with the read request to file.'),
430
+ }, readOnly('List your chats'), async ({ fields, participantId, readable }) => {
423
431
  try {
424
- const chats = await listMyChats(creds);
432
+ const chats = await listMyChats(creds, {
433
+ ...(participantId?.trim() ? { participantId: participantId.trim() } : {}),
434
+ ...(readable === undefined ? {} : { readable }),
435
+ });
425
436
  return textResult({
426
437
  count: chats.length,
427
438
  chats: projectChatListRows(chats, fields),
428
- note: 'These are existing rooms, not everyone you can contact. To contact a known person or agent, use ziggs_chat_open with their returned participant id; use ziggs_agent_search to find a teammate by name, which returns the person to message, not their agents. The server checks whether contact is allowed. No existing room does not mean contact is forbidden.',
439
+ note: 'These are existing rooms, not everyone you can contact. A row with readable: false is a room a person let you see in their list; its messages need the read request it carries. To list a person\'s rooms, ask them with ziggs_access_request (scopeKind person, permission list). To contact a known person or agent, use ziggs_chat_open with their returned participant id; use ziggs_agent_search to find a teammate by name, which returns the person to message, not their agents. The server checks whether contact is allowed. No existing room does not mean contact is forbidden.',
429
440
  });
430
441
  }
431
442
  catch (e) {
@@ -735,7 +746,7 @@ export function registerZiggsTools(server, creds, cfg) {
735
746
  // No prose list of next actions rides here: the calls are in
736
747
  // `readPlan`, the human cues sit on the `decisions` and `activeWork`
737
748
  // rows, and how to present them is said once, on connect.
738
- const note = windowNote(news.deliveries, [creds.agentId], Array.isArray(news.outOfReach) ? news.outOfReach.length : 0);
749
+ const note = windowNote(news.deliveries, [creds.agentId]);
739
750
  return compactTextResult({
740
751
  ...news,
741
752
  session,
@@ -869,31 +880,31 @@ export function registerZiggsTools(server, creds, cfg) {
869
880
  .array(z.string())
870
881
  .optional()
871
882
  .describe('Task ids this task waits for. It is created now but its assignee is not woken, and cannot start it, until every task named here has finished — so you can state a join up front (four probes and a synthesis that waits on all four) instead of polling for each one and creating the next yourself. Releases when ALL of them are terminal, whatever state each reached; what a failed dependency means is your call. You must be able to read a task to name it here.'),
872
- graph: z
873
- .object({
874
- nodes: z
875
- .array(z.object({
876
- nodeId: z.string().min(1).max(200),
877
- agreementId: z.string(),
878
- description: z.string().min(1),
879
- title: z.string().max(80).optional(),
880
- assigneeId: z.string().optional(),
881
- waitsOn: z.array(z.string()).optional(),
882
- joinKind: z.enum(['all', 'any']).optional(),
883
- runIf: z
884
- .string()
885
- .min(1)
886
- .max(500)
887
- .optional()
888
- .describe('A yes/no question over the results of the nodes this one waits on, such as "Does the review report at least one bug?". On yes the node runs; on no it is skipped, and nodes waiting on it go ahead. When the answer is unclear it waits for the buyer. Needs waitsOn.'),
889
- }))
883
+ closesBy: z
884
+ .enum(['result', 'children'])
885
+ .optional()
886
+ .describe('How this task closes, fixed now. children (the default with children): a job, never handed out, that closes when its children close. result (the default otherwise): closes by its own result.'),
887
+ children: z
888
+ .array(z.object({
889
+ name: z.string().min(1).max(200),
890
+ agreementId: z.string(),
891
+ description: z.string().min(1),
892
+ title: z.string().max(80).optional(),
893
+ assigneeId: z.string().optional(),
894
+ waitsOn: z.array(z.string()).optional(),
895
+ joinKind: z.enum(['all', 'any']).optional(),
896
+ runIf: z
897
+ .string()
890
898
  .min(1)
891
- .max(100),
899
+ .max(500)
900
+ .optional()
901
+ .describe('A yes/no question over the results of the children this one waits on, such as "Does the review report at least one bug?". On yes it runs; on no it is skipped, and children waiting on it go ahead. When the answer is unclear it waits for the buyer. Needs waitsOn.'),
892
902
  inputArtifactIds: z.array(z.string()).optional(),
893
- review: z.literal('before_execution').optional(),
894
- })
903
+ }))
904
+ .min(1)
905
+ .max(100)
895
906
  .optional()
896
- .describe('Native atomic graph. waitsOn values name nodeId values in this payload; all nodes are created or none are. Root nodes receive graph inputArtifactIds. review holds every node until the root acknowledgement. runIf makes a node conditional on what the nodes before it found.'),
907
+ .describe('Children created with this task, all or none. waitsOn names sibling names. The answer maps each name to its task id. This task\'s inputArtifactIds go to children that wait on nothing; planReviewTiming before_execution holds every child until this task\'s plan is acknowledged. runIf makes a child conditional on what the children before it found.'),
897
908
  // POST /tasks and the SDK's task_create have always taken
898
909
  // these three; only this surface hid them, so an agent on MCP had to
899
910
  // create-then-replace_plan even when it already knew the steps.
@@ -951,7 +962,7 @@ export function registerZiggsTools(server, creds, cfg) {
951
962
  .optional()
952
963
  .describe('How often the holder may remind, and how many times at most. Both halves are required together: a cadence with no budget is refused, because one reminder is one wake and a rule that never says stop is an open-ended loop. Omit entirely for a task nobody needs reminding about.'),
953
964
  sources: sourcesField(),
954
- }, write('Create a task'), async ({ agreementId, description, title, parentTaskId, assigneeId, inputArtifactIds, waitsOn, graph, plan, planReviewTiming, requireMidWorkPlanAck, dueAt, checkBack, sources, }) => {
965
+ }, write('Create a task'), async ({ agreementId, description, title, parentTaskId, assigneeId, inputArtifactIds, waitsOn, closesBy, children, plan, planReviewTiming, requireMidWorkPlanAck, dueAt, checkBack, sources, }) => {
955
966
  try {
956
967
  const task = await createTask({
957
968
  ...(sources ? { sources: sources } : {}),
@@ -962,7 +973,8 @@ export function registerZiggsTools(server, creds, cfg) {
962
973
  assigneeId,
963
974
  inputArtifactIds,
964
975
  waitsOn,
965
- graph,
976
+ closesBy,
977
+ children,
966
978
  plan,
967
979
  planReviewTiming,
968
980
  requireMidWorkPlanAck,
@@ -228,7 +228,7 @@ export class WorkspaceRouter {
228
228
  // The connection door gets the same compact read and the same
229
229
  // counter note as the compact door; this path builds its own result,
230
230
  // so it has to ask for both.
231
- const note = windowNote(news.deliveries, self.agentIds ?? [], Array.isArray(news.outOfReach) ? news.outOfReach.length : 0);
231
+ const note = windowNote(news.deliveries, self.agentIds ?? []);
232
232
  return compactTextResult({
233
233
  ...news,
234
234
  ...pending,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ziggs-ai/ziggs-mcp",
3
- "version": "0.37.7",
3
+ "version": "0.37.9",
4
4
  "description": "MCP server for Claude Code, Cursor, and other MCP hosts \u2014 act as your Ziggs delegate agent",
5
5
  "type": "module",
6
6
  "bin": {
@@ -47,7 +47,7 @@
47
47
  },
48
48
  "dependencies": {
49
49
  "@modelcontextprotocol/sdk": "^1.29.0",
50
- "@ziggs-ai/api-client": "0.37.7",
50
+ "@ziggs-ai/api-client": "0.37.9",
51
51
  "dotenv": "^16.6.1",
52
52
  "zod": "^3.24.2",
53
53
  "zod-to-json-schema": "^3.25.1"