@paigy/mcp 0.40.38 → 0.40.40

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/README.md CHANGED
@@ -128,7 +128,7 @@ cancellable ~45-second window here for a response to what was sent (the hosted t
128
128
  returns after one read), and `contact({})` holds one for anything addressed to you; after an
129
129
  `expired` window, wait again without sending again. Every Goal a contact names must already
130
130
  exist (create it with manage_goals); a question with `workItBlocks` asks for a call; a
131
- question, an update or an answer is at most 250 characters with its context in `units`; and
131
+ question, an update or an answer is at most 80 characters with its context in `units`; and
132
132
  `withdrawQuestionIds` takes back a question of yours. `update_goal` is gone (2026-10-07): progress
133
133
  is a contact update (one the person does not need yet is kept as the Goal's progress), a review is
134
134
  acknowledged with `ackEventIds`, and a Goal is postponed with manage_goals `defer` (you are woken
@@ -242,12 +242,12 @@ script and branch on `$PAIGY_EVENT` there:
242
242
  codex exec "Paigy goal $PAIGY_GOAL_ID. Read it with get_goals first. The user said: $PAIGY_TEXT. Reply with contact when done."
243
243
  ```
244
244
 
245
- ## Stalled work (turn-end hook — Claude Code, Codex, Gemini CLI, Antigravity)
245
+ ## Stalled work (turn-end hook — Claude Code, Codex)
246
246
 
247
247
  An agent that goes quiet leaves its Goals behind. Three things hand it that work back, all
248
248
  reading one rule (no progress for 3 days, `COLD_AFTER_MS`): `contact({})` lists it under
249
249
  `stalled`, `paigy-listen` carries it in `PAIGY_WORK` (and launches for it on boot), and this
250
- hook holds a session's turn end once a day to list it. One program, four tools:
250
+ hook holds a session's turn end once a day to list it. One program, two tools:
251
251
 
252
252
  **Claude Code** (`~/.claude/settings.json`) — installed by the paigy plugin:
253
253
 
@@ -267,19 +267,18 @@ command = "npx -y -p @paigy/mcp@latest paigy-stalled --agent codex"
267
267
  timeout = 60
268
268
  ```
269
269
 
270
- **Gemini CLI / Antigravity** (`~/.gemini/settings.json`, `~/.gemini/antigravity-cli/settings.json`)
271
- — the turn-end event there is `AfterAgent`, and the reminder also arrives as a `systemMessage`,
272
- which is the field that event promises will reach somebody:
273
-
274
- ```json
275
- { "hooks": { "AfterAgent": [{ "matcher": "*", "hooks": [{ "name": "paigy-stalled", "type": "command",
276
- "command": "npx -y -p @paigy/mcp@latest paigy-stalled --agent antigravity", "timeout": 60000 }] }] } }
277
- ```
270
+ **Antigravity and Gemini CLI have no turn-end reminder (#3648).** Onboarding used to write one on
271
+ `AfterAgent` into their `settings.json`; agy 1.4.1 reads hooks only from `hooks.json` and ends a turn
272
+ on `Stop`, so it never fired, and in Gemini CLI's own file it named the wrong slot. `paigy-stalled`
273
+ answers nothing on `AfterAgent` from this release on, so a copy goes quiet once its `npx` fetches
274
+ it (the hook still starts `npx` each turn until removed), and onboarding (on every run) and
275
+ `scripts/paigy-wipe.sh` take it out. A `Stop` hook in agy's `hooks.json` waits on a check of its
276
+ payload on a real turn. The listener's wake and `contact({})`'s `stalled` still reach these agents.
278
277
 
279
278
  `--agent <slot>` is not optional outside Claude Code: a hook is not the MCP server, so it never
280
279
  inherits the `PAIGY_AGENT` that server was registered with, and without the flag it reads this
281
280
  session's own slot — a different identity, with no stalled work of its own. Pass the same value as
282
- the tool's MCP registration (`codex`, `antigravity`, or whatever you set `PAIGY_AGENT` to).
281
+ the tool's MCP registration (`codex`, or whatever you set `PAIGY_AGENT` to).
283
282
 
284
283
  It is silent when nothing is stalled, when the stop is already being continued by a hook, and
285
284
  after it has reminded that session once today. Codex's older `notify` program is not a place for
@@ -1,12 +1,13 @@
1
1
  import {
2
2
  NameTakenError,
3
+ ServerFault,
3
4
  isNetworkError,
4
5
  pollToken,
5
6
  requestCode,
6
7
  saveToken,
7
8
  setIdentity,
8
9
  sleep
9
- } from "./chunk-4PP522IN.js";
10
+ } from "./chunk-V4OVLFMP.js";
10
11
 
11
12
  // src/identity.ts
12
13
  var CLIENT_LABELS = {
@@ -44,8 +45,9 @@ async function identify(who, ask) {
44
45
  }
45
46
 
46
47
  // src/pairing.ts
47
- async function resolvePairing(deviceCode, capMs, pollMs = 2e3) {
48
+ async function resolvePairing(deviceCode, capMs, pollMs = 2e3, onFault) {
48
49
  const start = Date.now();
50
+ let fault;
49
51
  while (Date.now() - start < capMs) {
50
52
  try {
51
53
  const token = await pollToken(deviceCode);
@@ -53,8 +55,12 @@ async function resolvePairing(deviceCode, capMs, pollMs = 2e3) {
53
55
  saveToken(token);
54
56
  return { kind: "paired", token };
55
57
  }
58
+ fault = void 0;
59
+ onFault?.(fault);
56
60
  } catch (e) {
57
- if (isNetworkError(e)) {
61
+ if (isNetworkError(e) || e instanceof ServerFault) {
62
+ fault = e.message;
63
+ onFault?.(fault);
58
64
  await sleep(pollMs);
59
65
  continue;
60
66
  }
@@ -62,12 +68,25 @@ async function resolvePairing(deviceCode, capMs, pollMs = 2e3) {
62
68
  }
63
69
  await sleep(pollMs);
64
70
  }
65
- return { kind: "pending" };
71
+ return fault ? { kind: "pending", fault } : { kind: "pending" };
72
+ }
73
+ function pendingText(fault) {
74
+ if (fault) {
75
+ const said = /[.!?]$/.test(fault) ? fault : `${fault}.`;
76
+ return `Approval can't be checked right now: ${said} This is not the user being slow. Tell them once that Paigy is having trouble reaching its server, then call configure again with this device_code to keep trying; the code stays valid until it expires.`;
77
+ }
78
+ return "Still awaiting approval \u2014 the user hasn't entered the code yet. Call configure again with this device_code RIGHT NOW to keep waiting; each call returns after ~45s so it can't be killed by a host tool-call timeout. Nothing is lost between calls: the code stays valid and approval keeps being polled in the background, so a re-call returns the instant they approve.";
66
79
  }
67
80
  var bg = null;
68
81
  var started = null;
82
+ var startedUntil = 0;
83
+ function activePairing() {
84
+ if (started && Date.now() >= startedUntil) cancelBackgroundPair();
85
+ return started;
86
+ }
69
87
  async function startPairing(agent) {
70
- if (started) return started;
88
+ const live = activePairing();
89
+ if (live) return { ...live, expiresIn: Math.max(1, Math.round((startedUntil - Date.now()) / 1e3)) };
71
90
  const code = await requestCode(agent);
72
91
  started = {
73
92
  verificationUri: code.verification_uri_complete,
@@ -75,11 +94,14 @@ async function startPairing(agent) {
75
94
  deviceCode: code.device_code,
76
95
  expiresIn: code.expires_in
77
96
  };
97
+ startedUntil = Date.now() + code.expires_in * 1e3;
78
98
  startBackgroundPair(code.device_code, code.expires_in * 1e3);
79
99
  return started;
80
100
  }
81
101
  function startBackgroundPair(deviceCode, budgetMs) {
82
- const promise = resolvePairing(deviceCode, budgetMs).catch((e) => ({ kind: "error", message: e.message })).then((o) => {
102
+ const promise = resolvePairing(deviceCode, budgetMs, void 0, (fault) => {
103
+ if (bg?.deviceCode === deviceCode) bg.fault = fault;
104
+ }).catch((e) => ({ kind: "error", message: e.message })).then((o) => {
83
105
  if (bg?.deviceCode === deviceCode) bg.settled = o;
84
106
  return o;
85
107
  });
@@ -106,7 +128,7 @@ async function joinBackgroundPair(deviceCode, capMs) {
106
128
  if (s.kind === "paired" || s.kind === "error") started = null;
107
129
  return s;
108
130
  }
109
- return { kind: "pending" };
131
+ return bg?.fault ? { kind: "pending", fault: bg.fault } : { kind: "pending" };
110
132
  }
111
133
  function cancelBackgroundPair() {
112
134
  bg = null;
@@ -121,6 +143,7 @@ export {
121
143
  suggestedAgentName,
122
144
  identify,
123
145
  resolvePairing,
146
+ pendingText,
124
147
  startPairing,
125
148
  joinBackgroundPair,
126
149
  clearPairing