@paigy/mcp 0.40.39 → 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 +11 -12
- package/dist/{chunk-PMZBYLLJ.js → chunk-6FG3PZR2.js} +30 -7
- package/dist/{chunk-QE33A5XK.js → chunk-H2EBILBE.js} +750 -535
- package/dist/{chunk-4IAK4ATB.js → chunk-PKJJSHLW.js} +41 -44
- package/dist/{chunk-X4UIGUEX.js → chunk-V4OVLFMP.js} +644 -527
- package/dist/{dist-DOFR2565.js → dist-EJWDUF5J.js} +3 -1
- package/dist/enable.js +3 -3
- package/dist/index.js +45 -21
- package/dist/listen.js +32 -6
- package/dist/onboard.js +18 -6
- package/dist/slot.js +1 -1
- package/dist/stalled.js +10 -5
- package/dist/statusline.js +1 -1
- package/package.json +1 -1
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
|
|
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
|
|
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,
|
|
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
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
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`,
|
|
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-
|
|
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
|
-
|
|
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
|
|
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
|