koneck 2.25.44 → 2.25.46

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/dist/ink-chat.js CHANGED
@@ -32,6 +32,7 @@ const VERSION = (() => {
32
32
  })();
33
33
  import { startBus, listSessions as listBusSessions, resolveTarget, deliver, } from './session-bus.js';
34
34
  import { MODES as MODE_SPECS, modeSpec, decide, loadRules, saveRules, addRule, commandPrefixSuggestion, describeRule, } from './permissions.js';
35
+ import { AskQueue } from './ask-queue.js';
35
36
  import { planSummary } from './plan.js';
36
37
  import { describeSandbox, resolveSandbox } from './sandbox.js';
37
38
  const CYAN = '#54D7E7';
@@ -784,7 +785,15 @@ function App({ config: initialConfig }) {
784
785
  }
785
786
  catch { /* none */ }
786
787
  return new Promise(resolve => {
787
- setAsk({ tool, args, prefix: commandPrefixSuggestion(command), choice: 0, resolve });
788
+ // Queued rather than shown immediately. Sub-agents run concurrently and can each need an
789
+ // answer at the same moment; setting the question directly meant the second overwrote the
790
+ // first, whose promise then never settled — that agent waited forever, and the pool waiting
791
+ // on it hung the whole turn with no sign of why.
792
+ const isCurrent = askQueueRef.current.add({
793
+ tool, args, prefix: commandPrefixSuggestion(command), resolve,
794
+ });
795
+ if (isCurrent)
796
+ showNextAsk();
788
797
  });
789
798
  }
790
799
  const sessionOpts = {
@@ -912,15 +921,29 @@ function App({ config: initialConfig }) {
912
921
  }, []);
913
922
  useEffect(() => { modeRef.current = mode; }, [mode]);
914
923
  useEffect(() => { void loadRules(cfg.cwd).then(r => { rulesRef.current = r; }); }, [cfg.cwd]);
915
- /** Records a standing permission and lets the waiting call through. */
916
- function grant(rule, resolve, ok) {
924
+ /** Questions waiting for an answer, oldest first. Only the first is on screen. */
925
+ const askQueueRef = useRef(new AskQueue());
926
+ /** Puts the next waiting question on screen, or clears the panel when none are left. */
927
+ function showNextAsk() {
928
+ const next = askQueueRef.current.current();
929
+ setAsk(next ? { ...next, choice: 0, waiting: askQueueRef.current.pending } : null);
930
+ }
931
+ /** Answers the question on screen and moves to the next. */
932
+ function answerAsk(rule, ok) {
933
+ const current = askQueueRef.current.current();
934
+ if (!current) {
935
+ setAsk(null);
936
+ return;
937
+ }
917
938
  if (rule) {
918
939
  rulesRef.current = addRule(rulesRef.current, rule);
919
940
  void saveRules(cfg.cwd, rulesRef.current).catch(() => { });
920
941
  addSystem(`Allowed from now on: ${describeRule(rule)} (in this workspace)`);
921
942
  }
922
- setAsk(null);
923
- resolve(ok);
943
+ if (!ok)
944
+ addSystem(`Refused: ${current.tool}. The agent was told to carry on without it.`);
945
+ askQueueRef.current.answer(ok);
946
+ showNextAsk();
924
947
  }
925
948
  const [effort, setEffort] = useState('medium');
926
949
  const [showAnalytics, setShowAnalytics] = useState(false);
@@ -2198,6 +2221,14 @@ function App({ config: initialConfig }) {
2198
2221
  // deliberately left alone: cancelling should not cost whatever was typed while waiting.
2199
2222
  if (key.escape && busy && !picker && !keyPrompt && !updatePane && !usagePane) {
2200
2223
  abortRef.current?.abort();
2224
+ // Anything still waiting on a permission answer is refused rather than left hanging: the
2225
+ // agents are being abandoned, and an unsettled promise keeps the pool alive after the work
2226
+ // it was doing has been given up on.
2227
+ const stranded = askQueueRef.current.drain(false);
2228
+ if (stranded > 0) {
2229
+ setAsk(null);
2230
+ addSystem(`Cancelled with ${stranded} permission ${stranded === 1 ? 'request' : 'requests'} unanswered; refused.`);
2231
+ }
2201
2232
  return;
2202
2233
  }
2203
2234
  if (key.ctrl && input === 'c') {
@@ -2238,17 +2269,9 @@ function App({ config: initialConfig }) {
2238
2269
  // agent does next depends on the answer. Answerable either way — type the number, or move to
2239
2270
  // the line and press enter — because both are what people reach for.
2240
2271
  if (ask) {
2241
- const { tool, prefix, resolve } = ask;
2272
+ const { tool, prefix } = ask;
2242
2273
  const options = askOptions(tool, prefix);
2243
- const answer = (option) => {
2244
- if (option.allow) {
2245
- grant(option.rule, resolve, true);
2246
- return;
2247
- }
2248
- setAsk(null);
2249
- resolve(false);
2250
- addSystem(`Refused: ${tool}. The agent was told to carry on without it.`);
2251
- };
2274
+ const answer = (option) => answerAsk(option.rule, option.allow);
2252
2275
  if (key.upArrow || key.downArrow) {
2253
2276
  const step = key.downArrow ? 1 : options.length - 1; // wraps in both directions
2254
2277
  setAsk(a => (a ? { ...a, choice: (a.choice + step) % options.length } : a));
@@ -3181,7 +3204,9 @@ function App({ config: initialConfig }) {
3181
3204
  '',
3182
3205
  ...options.map((o, i) => `${i === ask.choice ? `**${G.caret} ${o.key}**` : ` ${o.key}`} ${o.label}`),
3183
3206
  '',
3184
- '*type the number, or arrows and enter · esc refuses*',
3207
+ ask.waiting > 0
3208
+ ? `*type the number, or arrows and enter · ${ask.waiting} more waiting*`
3209
+ : '*type the number, or arrows and enter · esc refuses*',
3185
3210
  ];
3186
3211
  return (_jsx(Box, { marginTop: 1, children: _jsx(Panel, { width: panelWidth(lines, barWidth, 46), color: AMBER, title: `Permission — ${modeSpec(mode).label} mode`, children: lines }) }));
3187
3212
  })(), _jsxs(Box, { marginTop: 1, borderStyle: "round", borderColor: busy ? DIM : CYAN, paddingX: 1, width: barWidth, children: [_jsxs(Text, { color: CYAN, bold: true, children: [G.caret, " "] }), _jsx(Text, { color: INK, children: draft.slice(0, caret) }), busy