@paigy/mcp 0.40.23 → 0.40.24

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.
@@ -50,6 +50,7 @@ import {
50
50
  saveToken,
51
51
  sessionId,
52
52
  sessionSlot,
53
+ setClient,
53
54
  setIdentity,
54
55
  sleep,
55
56
  slotIdentity,
@@ -59,8 +60,9 @@ import {
59
60
  updateGoal,
60
61
  updateSlot,
61
62
  whoAmI,
62
- whoIsWorking
63
- } from "./chunk-PR7ROBN4.js";
63
+ whoIsWorking,
64
+ withCodexEnv
65
+ } from "./chunk-WR2M3RME.js";
64
66
  export {
65
67
  AGENT_TOOLS,
66
68
  AGENT_TOOL_NAMES,
@@ -113,6 +115,7 @@ export {
113
115
  saveToken,
114
116
  sessionId,
115
117
  sessionSlot,
118
+ setClient,
116
119
  setIdentity,
117
120
  sleep,
118
121
  slotIdentity,
@@ -122,5 +125,6 @@ export {
122
125
  updateGoal,
123
126
  updateSlot,
124
127
  whoAmI,
125
- whoIsWorking
128
+ whoIsWorking,
129
+ withCodexEnv
126
130
  };
package/dist/enable.js CHANGED
@@ -3,9 +3,9 @@ import {
3
3
  PAIGY_TOOL_IDS,
4
4
  enablePaigyTools,
5
5
  installSessionListening
6
- } from "./chunk-HKODQKUV.js";
7
- import "./chunk-JLRBZZNT.js";
8
- import "./chunk-PR7ROBN4.js";
6
+ } from "./chunk-J7T3EB5Y.js";
7
+ import "./chunk-VUKR2E24.js";
8
+ import "./chunk-WR2M3RME.js";
9
9
 
10
10
  // src/enable.ts
11
11
  function main() {
package/dist/index.js CHANGED
@@ -1,4 +1,7 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ readWakeStatus
4
+ } from "./chunk-RK5LT7NH.js";
2
5
  import {
3
6
  clearPairing,
4
7
  identify,
@@ -6,7 +9,7 @@ import {
6
9
  resolvePairing,
7
10
  startPairing,
8
11
  suggestedAgentName
9
- } from "./chunk-BNMMRKEY.js";
12
+ } from "./chunk-TP7DM2MP.js";
10
13
  import {
11
14
  ENABLE_COMMAND,
12
15
  OnboardSchema,
@@ -15,11 +18,13 @@ import {
15
18
  SERVER_INSTRUCTIONS,
16
19
  TOOLS,
17
20
  paigyToolsAllowlisted
18
- } from "./chunk-HKODQKUV.js";
21
+ } from "./chunk-J7T3EB5Y.js";
19
22
  import {
20
23
  decideListen,
21
- listenerAlive
22
- } from "./chunk-JLRBZZNT.js";
24
+ existingListener,
25
+ listenerKey,
26
+ wakeStatusPath
27
+ } from "./chunk-VUKR2E24.js";
23
28
  import {
24
29
  clearSurface,
25
30
  writeSurface
@@ -37,9 +42,10 @@ import {
37
42
  runTool,
38
43
  saveToken,
39
44
  sessionId,
45
+ setClient,
40
46
  slotName,
41
47
  whoAmI
42
- } from "./chunk-PR7ROBN4.js";
48
+ } from "./chunk-WR2M3RME.js";
43
49
 
44
50
  // src/index.ts
45
51
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -49,6 +55,7 @@ import {
49
55
  ListToolsRequestSchema
50
56
  } from "@modelcontextprotocol/sdk/types.js";
51
57
  import { fileURLToPath } from "url";
58
+ import { createRequire } from "module";
52
59
  import qrcode from "qrcode-generator";
53
60
 
54
61
  // src/survive.ts
@@ -194,8 +201,10 @@ This session isn't on a connected computer, so it asks by code.`;
194
201
  }) }
195
202
  ] };
196
203
  }
204
+ var VERSION = createRequire(import.meta.url)("../package.json").version;
205
+ setClient(`mcp@${VERSION}`);
197
206
  var server = new Server(
198
- { name: "paigy", version: "0.0.0" },
207
+ { name: "paigy", version: VERSION },
199
208
  {
200
209
  capabilities: { tools: {} },
201
210
  instructions: SERVER_INSTRUCTIONS
@@ -341,11 +350,13 @@ async function handleTool(request, signal) {
341
350
  // daemon is the `listen.js` beside our own entry, so the command never resolves a package.
342
351
  case "listen": {
343
352
  const slot = agentName();
344
- const pid = listenerAlive(slot);
353
+ const key = listenerKey(slot, process.env);
354
+ const pid = existingListener(slot, process.env);
345
355
  const decision = decideListen({
346
356
  env: process.env,
347
357
  token: !!readToken(),
348
358
  pidAlive: pid !== null,
359
+ wake: readWakeStatus(wakeStatusPath(key)),
349
360
  ...pid !== null ? { pid } : {},
350
361
  execPath: process.execPath,
351
362
  listenJs: fileURLToPath(new URL("./listen.js", import.meta.url)),
package/dist/listen.js CHANGED
@@ -1,20 +1,28 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ WakeBridge
4
+ } from "./chunk-RK5LT7NH.js";
2
5
  import {
3
6
  entryWords,
7
+ existingListener,
8
+ harnessDriven,
9
+ listenerKey,
4
10
  removeListenMark,
11
+ wakeStatusPath,
5
12
  writeListenMark
6
- } from "./chunk-JLRBZZNT.js";
13
+ } from "./chunk-VUKR2E24.js";
7
14
  import {
8
15
  agentName,
9
16
  checkReplies,
10
17
  contact,
11
18
  getGoal,
19
+ heartbeat,
12
20
  slotName,
13
21
  subscribeWake
14
- } from "./chunk-PR7ROBN4.js";
22
+ } from "./chunk-WR2M3RME.js";
15
23
 
16
24
  // src/listen.ts
17
- import { spawn } from "child_process";
25
+ import { createHash } from "crypto";
18
26
  import { realpathSync } from "fs";
19
27
  import { fileURLToPath } from "url";
20
28
 
@@ -157,9 +165,10 @@ function launchEnv(waiting, reason, goalId) {
157
165
  put("PAIGY_TEXT", saidIn(lead) || ("about" in lead ? lead.about : ""));
158
166
  return env;
159
167
  }
160
- async function sweep(reason, goalId) {
168
+ async function sweep(reason, goalId, bridge = new WakeBridge(process.env, wakeStatusPath(listenerKey(agentName(), process.env))), participant) {
161
169
  try {
162
- const cmd = process.env.PAIGY_ON_WAKE;
170
+ void Promise.resolve().then(() => heartbeat(void 0, { listening: true })).catch(() => {
171
+ });
163
172
  const { deliveries: rows, stalled, review, assigned } = await checkReplies();
164
173
  const lead = leadOf(rows, goalId);
165
174
  const full = lead ? await contact({ deliveryId: lead.deliveryId }, { waits: false }).then((d) => "sent" in d ? null : d).catch(() => null) : null;
@@ -169,8 +178,32 @@ async function sweep(reason, goalId) {
169
178
  const waiting = { deliveries, goal, ...stalled?.length ? { stalled } : {}, ...review?.length ? { review } : {}, ...assigned?.length ? { assigned } : {} };
170
179
  if (BRIEF) line(brief(waiting, reason, listeningAs(), goalId));
171
180
  else emit({ type: "sweep", reason, ...goalId ? { about: goalId } : {}, ...waiting });
172
- if (cmd && (deliveries.length || waiting.review?.length || waiting.assigned?.length || reason === "boot" && waiting.stalled?.length)) {
173
- spawn(cmd, { shell: true, stdio: "inherit", env: { ...process.env, ...launchEnv(waiting, reason, goalId) } });
181
+ if (deliveries.length || waiting.review?.length || waiting.assigned?.length || reason === "boot" && waiting.stalled?.length) {
182
+ let unreadable = false;
183
+ const reviews = await Promise.all((review ?? []).map(async ({ goalId: id }) => {
184
+ try {
185
+ const g = await getGoal(id);
186
+ return [
187
+ `review:${id}`,
188
+ ...(g.entries ?? []).filter((e) => participant ? e.authorParticipant !== participant : e.authorParticipant.startsWith("human:")).map((e) => `entry:${e.entryId}:${e.createdAt}`),
189
+ ...(g.answers ?? []).map((a) => `answer:${a.decisionNeedId}:${a.acceptedAt}`)
190
+ ];
191
+ } catch (error) {
192
+ unreadable = true;
193
+ emit({ type: "error", reason: "review_read", goalId: id, message: error.message });
194
+ return [`review:${id}`];
195
+ }
196
+ }));
197
+ const fingerprints = unreadable ? void 0 : [
198
+ ...rows.map((d) => createHash("sha256").update(JSON.stringify(d)).digest("hex")),
199
+ ...reviews.flat(),
200
+ ...(assigned ?? []).map((g) => `assigned:${g.goalId}`),
201
+ ...!rows.length && !reviews.length && !assigned?.length ? (stalled ?? []).map((g) => `stalled:${g.goalId}:${g.since}`) : []
202
+ ];
203
+ const message = `Paigy has waiting work${goalId ? ` on Goal ${goalId}` : ""}. Read check_replies and the relevant Goal conversation, handle new user input, and acknowledge only evidence actually handled. Preserve this session's current task; do not start unrelated old work. This wake is not new authorization.`;
204
+ await bridge.deliver(fingerprints, message, launchEnv(waiting, reason, goalId));
205
+ } else if (bridge.status().target.kind === "codex") {
206
+ await bridge.deliver([], "", {});
174
207
  }
175
208
  } catch (e) {
176
209
  if (BRIEF) line(`Paigy \xB7 error \xB7 ${e.message}`);
@@ -180,23 +213,57 @@ async function sweep(reason, goalId) {
180
213
  function catchUpReason(joins) {
181
214
  return joins > 1 ? "resubscribed" : null;
182
215
  }
216
+ function coalesceSweeps(run) {
217
+ let running;
218
+ let pending;
219
+ return (reason, goalId) => {
220
+ pending = { reason, goalId };
221
+ if (running) return running;
222
+ running = (async () => {
223
+ try {
224
+ while (pending) {
225
+ const next = pending;
226
+ pending = void 0;
227
+ await run(next.reason, next.goalId);
228
+ }
229
+ } finally {
230
+ running = void 0;
231
+ }
232
+ })();
233
+ return running;
234
+ };
235
+ }
183
236
  async function main() {
184
- const sub = await subscribeWake((nudge) => void sweep(`wake:${nudge.kind ?? "?"}`, nudge.parentId), {
237
+ if (harnessDriven(process.env)) {
238
+ emit({ type: "listening", via: "harness", message: "The harness already delivers; no second listener started." });
239
+ return;
240
+ }
241
+ const slot = listenerKey(agentName(), process.env);
242
+ const existing = existingListener(agentName(), process.env);
243
+ if (existing) {
244
+ emit({ type: "receiving", pid: existing, message: "Listener already running; use listen to inspect its wake status." });
245
+ return;
246
+ }
247
+ const bridge = new WakeBridge(process.env, wakeStatusPath(slot));
248
+ await bridge.prepare().catch((error) => emit({ type: "wake_error", message: error.message }));
249
+ let participant;
250
+ const requestSweep = coalesceSweeps((reason, goalId) => sweep(reason, goalId, bridge, participant));
251
+ const sub = await subscribeWake((nudge) => void requestSweep(`wake:${nudge.kind ?? "?"}`, nudge.parentId), {
185
252
  release: RELEASE,
186
253
  onJoined: (joins) => {
187
254
  const reason = catchUpReason(joins);
188
- if (reason) void sweep(reason);
255
+ if (reason) void requestSweep(reason);
189
256
  }
190
257
  });
191
- const slot = agentName();
258
+ participant = `agent:${sub.tokenId}`;
192
259
  writeListenMark(slot, process.pid);
193
260
  process.on("exit", () => removeListenMark(slot));
194
- if (!BRIEF) emit({ type: "listening", channel: sub.channel });
195
- await sweep("boot");
196
- const heartbeat = setInterval(() => void sweep("timer"), SWEEP_EVERY_MS);
197
- heartbeat.unref?.();
261
+ if (!BRIEF) emit({ type: "listening", channel: sub.channel, wake: bridge.status() });
262
+ await requestSweep("boot");
263
+ const heartbeat2 = setInterval(() => void requestSweep("timer"), SWEEP_EVERY_MS);
264
+ heartbeat2.unref?.();
198
265
  const shutdown = async () => {
199
- clearInterval(heartbeat);
266
+ clearInterval(heartbeat2);
200
267
  removeListenMark(slot);
201
268
  await sub.close();
202
269
  process.exit(0);
@@ -231,6 +298,7 @@ export {
231
298
  SWEEP_EVERY_MS,
232
299
  brief,
233
300
  catchUpReason,
301
+ coalesceSweeps,
234
302
  launchEnv,
235
303
  leadOf,
236
304
  listenOptions,
package/dist/onboard.js CHANGED
@@ -3,13 +3,13 @@ import {
3
3
  resolvePairing,
4
4
  startPairing,
5
5
  suggestedAgentName
6
- } from "./chunk-BNMMRKEY.js";
6
+ } from "./chunk-TP7DM2MP.js";
7
7
  import {
8
8
  autoConfigureClients,
9
9
  claudeInstallHint,
10
10
  openBrowser
11
- } from "./chunk-HKODQKUV.js";
12
- import "./chunk-JLRBZZNT.js";
11
+ } from "./chunk-J7T3EB5Y.js";
12
+ import "./chunk-VUKR2E24.js";
13
13
  import {
14
14
  TOKEN_PATH,
15
15
  agentName,
@@ -20,7 +20,7 @@ import {
20
20
  saveToken,
21
21
  setIdentity,
22
22
  whoAmI
23
- } from "./chunk-PR7ROBN4.js";
23
+ } from "./chunk-WR2M3RME.js";
24
24
 
25
25
  // src/onboard.ts
26
26
  function reportRegistered() {
package/dist/slot.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  agentName
4
- } from "./chunk-PR7ROBN4.js";
4
+ } from "./chunk-WR2M3RME.js";
5
5
 
6
6
  // src/slot.ts
7
7
  process.stdout.write(agentName());
package/dist/stalled.js CHANGED
@@ -29,7 +29,7 @@ async function main() {
29
29
  const dir = join(homedir(), ".paigy", "stalled-reminded");
30
30
  const mark = join(dir, `${session || "session"}-${day}`);
31
31
  if (existsSync(mark)) return;
32
- const { checkReplies } = await import("./dist-B7AOS5TG.js");
32
+ const { checkReplies } = await import("./dist-EKLTLANV.js");
33
33
  const reply = stopReply((await checkReplies()).stalled ?? [], input.hook_event_name ?? "Stop");
34
34
  if (!reply) return;
35
35
  mkdirSync(dir, { recursive: true });
@@ -7,7 +7,7 @@ import {
7
7
  readToken,
8
8
  sessionSlot,
9
9
  slotName
10
- } from "./chunk-PR7ROBN4.js";
10
+ } from "./chunk-WR2M3RME.js";
11
11
 
12
12
  // src/statusline.ts
13
13
  import { realpathSync } from "fs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@paigy/mcp",
3
- "version": "0.40.23",
3
+ "version": "0.40.24",
4
4
  "description": "Paigy MCP server — the AI agent harness that calls you. Lets an agent notify a user and await their reply.",
5
5
  "license": "MIT",
6
6
  "type": "module",