@paigy/mcp 0.28.0 → 0.28.1

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.
@@ -2280,14 +2280,28 @@ var CHUNK_MAX = 300;
2280
2280
  var ASK_MAX = 1e4;
2281
2281
  var OPTION_MAX = 80;
2282
2282
  var NEEDS_MAX = 6;
2283
- var UNSPEAKABLE = /```|\n/;
2283
+ var UNSPEAKABLE = /```/;
2284
+ function normalizeSpeech(req) {
2285
+ const flat = (s) => s.replace(/\s+/g, " ").trim();
2286
+ return {
2287
+ ...req,
2288
+ ...req.ask !== void 0 ? { ask: flat(req.ask) } : {},
2289
+ ...req.context ? {
2290
+ context: {
2291
+ ...req.context,
2292
+ title: flat(req.context.title),
2293
+ description: req.context.description.map(flat)
2294
+ }
2295
+ } : {}
2296
+ };
2297
+ }
2284
2298
  function lintNotify(req) {
2285
2299
  const problems = [];
2286
2300
  if (req.ask !== void 0) {
2287
2301
  if (req.ask.length > ASK_MAX)
2288
2302
  problems.push(`ask is ${req.ask.length} chars \u2014 state the need and why it matters now in \u2264${ASK_MAX}; move detail into a smaller follow-up`);
2289
2303
  if (UNSPEAKABLE.test(req.ask))
2290
- problems.push("ask contains code fences or newlines \u2014 write it as plain prose (it may be read aloud on a call)");
2304
+ problems.push("ask contains a code fence \u2014 write it as plain prose (it may be read aloud on a call)");
2291
2305
  for (const n of req.needs ?? []) {
2292
2306
  if (n.length > OPTION_MAX) problems.push(`need "${n.slice(0, 40)}\u2026" is too long \u2014 each need is a short phrase (\u2264${OPTION_MAX} chars)`);
2293
2307
  }
@@ -2300,7 +2314,7 @@ function lintNotify(req) {
2300
2314
  if (req.context.title.length > TITLE_MAX)
2301
2315
  problems.push(`title is ${req.context.title.length} chars \u2014 shorten to \u2264${TITLE_MAX} (it's what shows on the banner / gets spoken on a ring)`);
2302
2316
  if (UNSPEAKABLE.test(req.context.title))
2303
- problems.push("title contains code fences or newlines \u2014 one plain-prose line");
2317
+ problems.push("title contains a code fence \u2014 one plain-prose line");
2304
2318
  if (req.context.description.length > CHUNKS_MAX)
2305
2319
  problems.push(`${req.context.description.length} description chunks \u2014 cap at ${CHUNKS_MAX}; merge or drop the rest`);
2306
2320
  for (const [i, chunk] of req.context.description.entries()) {
@@ -2308,7 +2322,7 @@ function lintNotify(req) {
2308
2322
  problems.push(`description[${i}] is ${chunk.length} chars \u2014 split it into standalone points of \u2264${CHUNK_MAX}`);
2309
2323
  }
2310
2324
  if (spoken && UNSPEAKABLE.test(req.context.description.join(" ")))
2311
- problems.push("urgency is 'call'/'banner' but the description has code fences/newlines-in-chunk \u2014 rewrite in spoken register (it will be read aloud)");
2325
+ problems.push("urgency is 'call'/'banner' but the description has a code fence \u2014 rewrite in spoken register (it will be read aloud)");
2312
2326
  }
2313
2327
  for (const o of req.options ?? []) {
2314
2328
  if (o.label.length > OPTION_MAX)
@@ -2576,7 +2590,12 @@ var UserAnswerSchema = z.discriminatedUnion("kind", [
2576
2590
  z.object({ kind: z.literal("ranked"), optionIds: z.array(z.string()), labels: z.array(z.string()).optional() }),
2577
2591
  z.object({ kind: z.literal("clarify"), chunks: z.array(z.string()).min(1) }),
2578
2592
  z.object({ kind: z.literal("confirm"), approved: z.boolean() }),
2579
- z.object({ kind: z.literal("turns"), turns: z.array(TurnSchema).min(1) })
2593
+ z.object({ kind: z.literal("turns"), turns: z.array(TurnSchema).min(1) }),
2594
+ /** An auto-answer derived from the user's PAST decisions (broker/precedent-design.md §2):
2595
+ * delivered through the same settle/await path as a human answer, carrying the judge's
2596
+ * derivation and the precedent ids it grew from. Always paired with a visible trail
2597
+ * card the user can reply to — the broker never overrides the user. */
2598
+ z.object({ kind: z.literal("precedent"), answer: z.string(), derivation: z.string(), sources: z.array(z.string()).min(1) })
2580
2599
  ]);
2581
2600
  var IntentSchema = z.object({
2582
2601
  // The full vocabulary the bot's mapper emits (mapper.INTENT_KINDS) — the schema lagged
@@ -2589,7 +2608,12 @@ var IntentSchema = z.object({
2589
2608
  * "after lunch") against the agent machine's clock — the user's — and attaches
2590
2609
  * the seconds, ready to pass straight to schedule_callback. Absent when the
2591
2610
  * detail didn't parse (the agent interprets it) or the kind isn't defer. */
2592
- dueInSeconds: z.number().int().positive().optional()
2611
+ dueInSeconds: z.number().int().positive().optional(),
2612
+ /** Feedback only (#812): WHICH failure the complaint names — typed by the mapper that
2613
+ * already read the utterance, so `feedback_from_call.kind` stops defaulting to
2614
+ * 'other' on every row. A table that records that something was wrong and nothing
2615
+ * about what cannot answer "is the bot looping less this week?". */
2616
+ fault: z.enum(["loop", "unanswered", "overridden", "misheard", "slow", "other"]).optional()
2593
2617
  });
2594
2618
  var AwaitItemSchema = z.discriminatedUnion("type", [
2595
2619
  z.object({
@@ -2759,6 +2783,11 @@ var AgendaTurnSchema = z.object({
2759
2783
  claimId: z.string().optional(),
2760
2784
  /** The claim's voice key (#462) — the OUTBOUND identity, audible who-is-asking. */
2761
2785
  voice: z.string().optional(),
2786
+ /** The claim's AGENT NAME (#838) — the spoken identity. A voice alone doesn't say
2787
+ * whose request this is: an item that folded in from another agent arrived as a bare
2788
+ * non-sequitur ("First real production sign-in is yours to make whenever you want.")
2789
+ * and the owner answered "What?". The bot names the agent before its first turn. */
2790
+ agent: z.string().optional(),
2762
2791
  select: SelectShapeSchema.optional(),
2763
2792
  options: z.array(OptionSchema.omit({ id: true })).optional(),
2764
2793
  /** The seat was satisfied by the call's own ACCOUNT (replan-design.md, user_info): the
@@ -3938,6 +3967,7 @@ async function handoff(req) {
3938
3967
  export {
3939
3968
  BACKEND_URL,
3940
3969
  reach,
3970
+ normalizeSpeech,
3941
3971
  lintNotify,
3942
3972
  NotifyRequestSchema,
3943
3973
  SetTaskStateSchema,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  AGENT_NAME
3
- } from "./chunk-TE57PAKM.js";
3
+ } from "./chunk-7P4WMXGB.js";
4
4
 
5
5
  // src/clients.ts
6
6
  import { execFile } from "child_process";
@@ -215,7 +215,12 @@ var UserAnswerSchema = z.discriminatedUnion("kind", [
215
215
  z.object({ kind: z.literal("ranked"), optionIds: z.array(z.string()), labels: z.array(z.string()).optional() }),
216
216
  z.object({ kind: z.literal("clarify"), chunks: z.array(z.string()).min(1) }),
217
217
  z.object({ kind: z.literal("confirm"), approved: z.boolean() }),
218
- z.object({ kind: z.literal("turns"), turns: z.array(TurnSchema).min(1) })
218
+ z.object({ kind: z.literal("turns"), turns: z.array(TurnSchema).min(1) }),
219
+ /** An auto-answer derived from the user's PAST decisions (broker/precedent-design.md §2):
220
+ * delivered through the same settle/await path as a human answer, carrying the judge's
221
+ * derivation and the precedent ids it grew from. Always paired with a visible trail
222
+ * card the user can reply to — the broker never overrides the user. */
223
+ z.object({ kind: z.literal("precedent"), answer: z.string(), derivation: z.string(), sources: z.array(z.string()).min(1) })
219
224
  ]);
220
225
  var IntentSchema = z.object({
221
226
  // The full vocabulary the bot's mapper emits (mapper.INTENT_KINDS) — the schema lagged
@@ -228,7 +233,12 @@ var IntentSchema = z.object({
228
233
  * "after lunch") against the agent machine's clock — the user's — and attaches
229
234
  * the seconds, ready to pass straight to schedule_callback. Absent when the
230
235
  * detail didn't parse (the agent interprets it) or the kind isn't defer. */
231
- dueInSeconds: z.number().int().positive().optional()
236
+ dueInSeconds: z.number().int().positive().optional(),
237
+ /** Feedback only (#812): WHICH failure the complaint names — typed by the mapper that
238
+ * already read the utterance, so `feedback_from_call.kind` stops defaulting to
239
+ * 'other' on every row. A table that records that something was wrong and nothing
240
+ * about what cannot answer "is the bot looping less this week?". */
241
+ fault: z.enum(["loop", "unanswered", "overridden", "misheard", "slow", "other"]).optional()
232
242
  });
233
243
  var AwaitItemSchema = z.discriminatedUnion("type", [
234
244
  z.object({
@@ -398,6 +408,11 @@ var AgendaTurnSchema = z.object({
398
408
  claimId: z.string().optional(),
399
409
  /** The claim's voice key (#462) — the OUTBOUND identity, audible who-is-asking. */
400
410
  voice: z.string().optional(),
411
+ /** The claim's AGENT NAME (#838) — the spoken identity. A voice alone doesn't say
412
+ * whose request this is: an item that folded in from another agent arrived as a bare
413
+ * non-sequitur ("First real production sign-in is yours to make whenever you want.")
414
+ * and the owner answered "What?". The bot names the agent before its first turn. */
415
+ agent: z.string().optional(),
401
416
  select: SelectShapeSchema.optional(),
402
417
  options: z.array(OptionSchema.omit({ id: true })).optional(),
403
418
  /** The seat was satisfied by the call's own ACCOUNT (replan-design.md, user_info): the
package/dist/index.js CHANGED
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  HandoffSchema
4
- } from "./chunk-FRYGTKLM.js";
4
+ } from "./chunk-ZFBYZWK7.js";
5
5
  import {
6
6
  PAIGY_TOOL_IDS,
7
7
  autoConfigureClients,
8
8
  claudeInstallHint,
9
9
  enablePaigyTools
10
- } from "./chunk-XQVQ4JRT.js";
10
+ } from "./chunk-EQAFMUQC.js";
11
11
  import {
12
12
  clearSurface,
13
13
  writeSurface
@@ -29,6 +29,7 @@ import {
29
29
  hatch,
30
30
  lintNotify,
31
31
  listSlots,
32
+ normalizeSpeech,
32
33
  overrideToken,
33
34
  pairStep,
34
35
  readKeyFile,
@@ -43,7 +44,7 @@ import {
43
44
  sleep,
44
45
  startE2ee,
45
46
  submitNotification
46
- } from "./chunk-TE57PAKM.js";
47
+ } from "./chunk-7P4WMXGB.js";
47
48
 
48
49
  // src/index.ts
49
50
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -528,7 +529,7 @@ async function handleTool(request, signal) {
528
529
  case "contact":
529
530
  case "notify":
530
531
  case "notify_user": {
531
- const parsed = NotifyRequestSchema.parse(request.params.arguments);
532
+ const parsed = normalizeSpeech(NotifyRequestSchema.parse(request.params.arguments));
532
533
  const problems = lintNotify(parsed);
533
534
  if (problems.length) {
534
535
  return {
package/dist/listen.js CHANGED
@@ -2,11 +2,11 @@
2
2
  import {
3
3
  WAKE_EVENT,
4
4
  wakeChannel
5
- } from "./chunk-FRYGTKLM.js";
5
+ } from "./chunk-ZFBYZWK7.js";
6
6
  import {
7
7
  checkReplies,
8
8
  registerDelivery
9
- } from "./chunk-TE57PAKM.js";
9
+ } from "./chunk-7P4WMXGB.js";
10
10
 
11
11
  // src/listen.ts
12
12
  import { createClient } from "@supabase/supabase-js";
@@ -130,6 +130,9 @@ function answerText(answer) {
130
130
  // (the full exchange is in PAIGY_WORK for a launcher that wants it).
131
131
  case "turns":
132
132
  return answer.turns.map((t) => t.reply).join(" ");
133
+ // An auto-answer derived from the user's past decisions — reads like a fast human.
134
+ case "precedent":
135
+ return answer.answer;
133
136
  case "ignored":
134
137
  return "";
135
138
  }
package/dist/onboard.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  autoConfigureClients,
4
4
  claudeInstallHint,
5
5
  openBrowser
6
- } from "./chunk-XQVQ4JRT.js";
6
+ } from "./chunk-EQAFMUQC.js";
7
7
  import {
8
8
  AGENT_NAME,
9
9
  TOKEN_PATH,
@@ -11,7 +11,7 @@ import {
11
11
  requestCode,
12
12
  saveToken,
13
13
  sleep
14
- } from "./chunk-TE57PAKM.js";
14
+ } from "./chunk-7P4WMXGB.js";
15
15
 
16
16
  // src/onboard.ts
17
17
  async function main() {
@@ -6,7 +6,7 @@ import {
6
6
  BACKEND_URL,
7
7
  reach,
8
8
  readToken
9
- } from "./chunk-TE57PAKM.js";
9
+ } from "./chunk-7P4WMXGB.js";
10
10
 
11
11
  // src/statusline.ts
12
12
  import { mkdirSync, readFileSync, realpathSync, writeFileSync } from "fs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@paigy/mcp",
3
- "version": "0.28.0",
3
+ "version": "0.28.1",
4
4
  "description": "Paigy MCP server — a voice inbox for your AI agents. Lets an agent notify a user and await their reply.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -37,8 +37,8 @@
37
37
  "typescript": "^5.7.2",
38
38
  "vitest": "^2.1.8",
39
39
  "@paigy/crypto": "0.0.0",
40
- "@paigy/schema": "0.0.0",
41
- "@paigy/sdk": "0.1.0"
40
+ "@paigy/sdk": "0.1.0",
41
+ "@paigy/schema": "0.0.0"
42
42
  },
43
43
  "scripts": {
44
44
  "build": "tsup",