pendingyou 0.34.0 → 0.34.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.34.1 (2026-10-09)
4
+
5
+ - Fixed: a Claude Code permission prompt answered through the permission channel (`npx pendingyou claude`) could get
6
+ two cards, the relayed one with **Allow** and **Deny** and the old button-less “Claude Code is waiting for your OK”
7
+ one. It happened to any tool the session had used before (a second Write): Claude Code writes a prompted call to its
8
+ transcript only once it's answered, so the Notification hook, six seconds into the dialog, found the earlier call
9
+ answered, took the dialog for one nothing explained and put up a card for it. A relayed prompt now counts as on
10
+ screen, a Notification adds nothing while one is open, and a button-less card already up for a relayed dialog is
11
+ withdrawn as the relayed card goes up at once (“It has a card of its own now, with Allow and Deny.”).
12
+
3
13
  ## 0.34.0 (2026-10-09)
4
14
 
5
15
  - Codex can ask on your phone first. `npx pendingyou codex-answers --wait <minutes>` (0 to 10; 0, the default, is off)
@@ -232,6 +232,18 @@ function keepNewest(state) {
232
232
  closeCards(state, state.prompts.slice(0, -MAX_PROMPTS), 'answered');
233
233
  state.prompts = state.prompts.slice(-MAX_PROMPTS);
234
234
  }
235
+ function markMoving(state) {
236
+ const card = state.card;
237
+ if (!card)
238
+ return;
239
+ const shown = state.prompts.find((prompt) => prompt.key === card.key && !prompt.notice);
240
+ if (shown ? shown.relay : state.prompts.some((prompt) => prompt.relay))
241
+ card.moving = true;
242
+ }
243
+ function relayed(state) {
244
+ state.prompts = state.prompts.filter((prompt) => !prompt.notice);
245
+ markMoving(state);
246
+ }
235
247
  const ADOPT_MS = 30_000;
236
248
  function adopt(state, prompt) {
237
249
  const own = state.prompts.find((each) => each.relay?.own &&
@@ -407,7 +419,7 @@ async function notified(io, origin, input) {
407
419
  for (const prompt of (known?.prompts ?? []).slice().reverse()) {
408
420
  if (prompt.shown !== undefined || prompt.notice || prompt.at > before)
409
421
  continue;
410
- if (await stillAsked(known, prompt)) {
422
+ if (prompt.relay || (await stillAsked(known, prompt))) {
411
423
  picked = promptId(prompt);
412
424
  break;
413
425
  }
@@ -436,7 +448,7 @@ async function notified(io, origin, input) {
436
448
  shows.shown = now;
437
449
  delete shows.failed;
438
450
  }
439
- else if (next.prompts.some((prompt) => prompt.shown !== undefined)) {
451
+ else if (next.prompts.some((prompt) => prompt.shown !== undefined || prompt.relay)) {
440
452
  return state;
441
453
  }
442
454
  else
@@ -506,7 +518,9 @@ function relayPlan(state, now, grace, then) {
506
518
  const waiting = state.prompts.filter((prompt) => prompt.relay && !prompt.card && !prompt.failed);
507
519
  if (waiting.length === 0)
508
520
  return then;
509
- const dueAt = (prompt) => Math.min(Math.min(prompt.at, prompt.relay.at) + grace, prompt.shown ?? Number.POSITIVE_INFINITY);
521
+ const dueAt = (prompt) => state.card?.moving
522
+ ? now
523
+ : Math.min(Math.min(prompt.at, prompt.relay.at) + grace, prompt.shown ?? Number.POSITIVE_INFINITY);
510
524
  const first = waiting.reduce((soonest, prompt) => dueAt(prompt) < dueAt(soonest) ? prompt : soonest);
511
525
  const due = dueAt(first) - now;
512
526
  if (due <= 0)
@@ -520,7 +534,9 @@ function relayPlan(state, now, grace, then) {
520
534
  function cardPlan(state, now, grace) {
521
535
  const prompts = state.prompts.filter((prompt) => seen(prompt) && !prompt.relay);
522
536
  if (prompts.length === 0)
523
- return state.card ? { kind: 'cancel' } : { kind: 'done' };
537
+ return state.card
538
+ ? { kind: 'cancel', ...(state.card.moving ? { moved: true } : {}) }
539
+ : { kind: 'done' };
524
540
  if (state.card) {
525
541
  const newest = shownOf(prompts);
526
542
  if (state.card.key === newest.key)
@@ -940,6 +956,7 @@ async function post(io, client, state, prompt, worker) {
940
956
  const kept = await record(io, state.session, worker, (state) => {
941
957
  state.card = card;
942
958
  state.areaId = areaId;
959
+ markMoving(state);
943
960
  });
944
961
  if (!kept)
945
962
  await withdraw(io, client, card, state, 'answered').catch(() => { });
@@ -1064,19 +1081,24 @@ async function update(io, client, state, prompt, worker) {
1064
1081
  }
1065
1082
  const version = typeof result.version === 'number' ? result.version : card.version + 1;
1066
1083
  await record(io, state.session, worker, (state) => {
1067
- if (state.card?.requestId === card.requestId)
1068
- state.card = { ...card, version, key: prompt.key };
1084
+ if (state.card?.requestId === card.requestId) {
1085
+ const { moving: _, ...shown } = card;
1086
+ state.card = { ...shown, version, key: prompt.key };
1087
+ }
1069
1088
  });
1070
1089
  }
1090
+ const MOVED = 'It has a card of its own now, with Allow and Deny.';
1071
1091
  async function withdraw(io, client, card, state, how) {
1072
1092
  const machine = await computerOf(io, state.origin, appOf(state));
1073
1093
  try {
1074
1094
  await client.call('cancel_request', {
1075
1095
  requestId: card.requestId,
1076
1096
  name: card.name,
1077
- reason: how === 'answered'
1078
- ? cut(`Answered in ${APP_NAMES[appOf(state)]} on ${machine}.`, 200)
1079
- : cut(`That ${APP_NAMES[appOf(state)]} session on ${machine} ended, so it isn’t waiting any more.`, 200),
1097
+ reason: how === 'moved'
1098
+ ? MOVED
1099
+ : how === 'answered'
1100
+ ? cut(`Answered in ${APP_NAMES[appOf(state)]} on ${machine}.`, 200)
1101
+ : cut(`That ${APP_NAMES[appOf(state)]} session on ${machine} ended, so it isn’t waiting any more.`, 200),
1080
1102
  ...(how === 'answered' ? { answeredHere: true } : {}),
1081
1103
  });
1082
1104
  }
@@ -1192,7 +1214,7 @@ export async function permissionWorker(io, options) {
1192
1214
  }
1193
1215
  else {
1194
1216
  const card = state.card;
1195
- await withdraw(io, client, card, state, state.settled ?? 'answered');
1217
+ await withdraw(io, client, card, state, plan.moved ? 'moved' : (state.settled ?? 'answered'));
1196
1218
  await record(io, session, worker, (state) => {
1197
1219
  if (state.card?.requestId === card.requestId)
1198
1220
  delete state.card;
@@ -1288,6 +1310,7 @@ export async function recordRelay(io, origin, heard, options) {
1288
1310
  return state;
1289
1311
  prompt.relay = heard.relay;
1290
1312
  delete prompt.failed;
1313
+ relayed(state);
1291
1314
  matched = true;
1292
1315
  delete state.settled;
1293
1316
  worker = claim(state, io.now());
@@ -1324,6 +1347,7 @@ export async function recordRelay(io, origin, heard, options) {
1324
1347
  if (next.prompts.some((each) => each.relay?.id === heard.relay.id))
1325
1348
  return next;
1326
1349
  next.prompts.push(prompt);
1350
+ relayed(next);
1327
1351
  keepNewest(next);
1328
1352
  delete next.settled;
1329
1353
  next.at = now;
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = '0.34.0';
1
+ export const VERSION = '0.34.1';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pendingyou",
3
- "version": "0.34.0",
3
+ "version": "0.34.1",
4
4
  "description": "Pending You for your coding agents: one command sets up Claude Code, Codex, OpenCode and Pi to ask you through Pending You and hear your answers, even while they sit idle.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://www.pendingyou.com",