pendingyou 0.34.0 → 0.34.2

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,26 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.34.2 (2026-10-09)
4
+
5
+ - Fixed: in bypassPermissions or dontAsk mode, a Claude Code dialog for a tool its agent had used before (a subagent's
6
+ second Bash) could get no card. Claude Code writes a prompted call to its transcript only once its dialog is
7
+ answered, so 20 seconds in the check for an unanswered call found the agent's earlier call of the same tool,
8
+ answered, and settled the prompt. Now only the prompt's own call counts: not in the transcript yet, it's still
9
+ waiting and its card goes up; the same command answered before the prompt was asked is an earlier call and doesn't
10
+ count; a call Claude Code denied by itself is written with its refusal at once, so it still gets no card. A
11
+ Notification picks the prompt it shows the same way, so it names the second call rather than putting up a card in
12
+ Claude Code's words.
13
+
14
+ ## 0.34.1 (2026-10-09)
15
+
16
+ - Fixed: a Claude Code permission prompt answered through the permission channel (`npx pendingyou claude`) could get
17
+ two cards, the relayed one with **Allow** and **Deny** and the old button-less “Claude Code is waiting for your OK”
18
+ one. It happened to any tool the session had used before (a second Write): Claude Code writes a prompted call to its
19
+ transcript only once it's answered, so the Notification hook, six seconds into the dialog, found the earlier call
20
+ answered, took the dialog for one nothing explained and put up a card for it. A relayed prompt now counts as on
21
+ screen, a Notification adds nothing while one is open, and a button-less card already up for a relayed dialog is
22
+ withdrawn as the relayed card goes up at once (“It has a card of its own now, with Allow and Deny.”).
23
+
3
24
  ## 0.34.0 (2026-10-09)
4
25
 
5
26
  - 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)
@@ -724,8 +740,7 @@ async function callState(calls, prompt) {
724
740
  const size = (await file.stat()).size;
725
741
  let position = size;
726
742
  let carry = Buffer.alloc(0);
727
- const results = new Set();
728
- let sameTool = null;
743
+ const results = new Map();
729
744
  let found = null;
730
745
  const read = (line) => {
731
746
  if (!line.includes('tool_'))
@@ -746,13 +761,16 @@ async function callState(calls, prompt) {
746
761
  continue;
747
762
  const id = text(block.tool_use_id) || text(block.id);
748
763
  if (block.type === 'tool_result' && id)
749
- results.add(id);
750
- else if (block.type === 'tool_use' && entry.type === 'assistant' && found === null) {
751
- const tool = text(block.name);
752
- if (promptKey(tool, block.input, prompt.agent) === prompt.key)
753
- found = results.has(id) ? 'answered' : 'waiting';
754
- else if (sameTool === null && prompt.tool && tool === prompt.tool)
755
- sameTool = id;
764
+ results.set(id, Date.parse(text(entry.timestamp)));
765
+ else if (block.type === 'tool_use' &&
766
+ entry.type === 'assistant' &&
767
+ found === null &&
768
+ promptKey(text(block.name), block.input, prompt.agent) === prompt.key) {
769
+ const answered = results.get(id);
770
+ if (answered === undefined)
771
+ found = 'waiting';
772
+ else if (!(answered < prompt.at))
773
+ found = 'answered';
756
774
  }
757
775
  }
758
776
  };
@@ -772,11 +790,7 @@ async function callState(calls, prompt) {
772
790
  }
773
791
  if (found === null && position === 0)
774
792
  read(carry.toString('utf8'));
775
- if (found !== null)
776
- return found;
777
- if (sameTool !== null)
778
- return results.has(sameTool) ? 'answered' : 'waiting';
779
- return 'waiting';
793
+ return found ?? 'waiting';
780
794
  }
781
795
  catch {
782
796
  return null;
@@ -940,6 +954,7 @@ async function post(io, client, state, prompt, worker) {
940
954
  const kept = await record(io, state.session, worker, (state) => {
941
955
  state.card = card;
942
956
  state.areaId = areaId;
957
+ markMoving(state);
943
958
  });
944
959
  if (!kept)
945
960
  await withdraw(io, client, card, state, 'answered').catch(() => { });
@@ -1064,19 +1079,24 @@ async function update(io, client, state, prompt, worker) {
1064
1079
  }
1065
1080
  const version = typeof result.version === 'number' ? result.version : card.version + 1;
1066
1081
  await record(io, state.session, worker, (state) => {
1067
- if (state.card?.requestId === card.requestId)
1068
- state.card = { ...card, version, key: prompt.key };
1082
+ if (state.card?.requestId === card.requestId) {
1083
+ const { moving: _, ...shown } = card;
1084
+ state.card = { ...shown, version, key: prompt.key };
1085
+ }
1069
1086
  });
1070
1087
  }
1088
+ const MOVED = 'It has a card of its own now, with Allow and Deny.';
1071
1089
  async function withdraw(io, client, card, state, how) {
1072
1090
  const machine = await computerOf(io, state.origin, appOf(state));
1073
1091
  try {
1074
1092
  await client.call('cancel_request', {
1075
1093
  requestId: card.requestId,
1076
1094
  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),
1095
+ reason: how === 'moved'
1096
+ ? MOVED
1097
+ : how === 'answered'
1098
+ ? cut(`Answered in ${APP_NAMES[appOf(state)]} on ${machine}.`, 200)
1099
+ : cut(`That ${APP_NAMES[appOf(state)]} session on ${machine} ended, so it isn’t waiting any more.`, 200),
1080
1100
  ...(how === 'answered' ? { answeredHere: true } : {}),
1081
1101
  });
1082
1102
  }
@@ -1192,7 +1212,7 @@ export async function permissionWorker(io, options) {
1192
1212
  }
1193
1213
  else {
1194
1214
  const card = state.card;
1195
- await withdraw(io, client, card, state, state.settled ?? 'answered');
1215
+ await withdraw(io, client, card, state, plan.moved ? 'moved' : (state.settled ?? 'answered'));
1196
1216
  await record(io, session, worker, (state) => {
1197
1217
  if (state.card?.requestId === card.requestId)
1198
1218
  delete state.card;
@@ -1288,6 +1308,7 @@ export async function recordRelay(io, origin, heard, options) {
1288
1308
  return state;
1289
1309
  prompt.relay = heard.relay;
1290
1310
  delete prompt.failed;
1311
+ relayed(state);
1291
1312
  matched = true;
1292
1313
  delete state.settled;
1293
1314
  worker = claim(state, io.now());
@@ -1324,6 +1345,7 @@ export async function recordRelay(io, origin, heard, options) {
1324
1345
  if (next.prompts.some((each) => each.relay?.id === heard.relay.id))
1325
1346
  return next;
1326
1347
  next.prompts.push(prompt);
1348
+ relayed(next);
1327
1349
  keepNewest(next);
1328
1350
  delete next.settled;
1329
1351
  next.at = now;
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = '0.34.0';
1
+ export const VERSION = '0.34.2';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pendingyou",
3
- "version": "0.34.0",
3
+ "version": "0.34.2",
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",