nolo-cli 0.74.0-alpha.1 → 0.74.0

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.
Files changed (86) hide show
  1. package/{_executeModel-YEVZAJYX.js → _executeModel-IULR47HG.js} +20 -20
  2. package/{agentDeleteCommand-FSIMTL4K.js → agentDeleteCommand-2S4WCDQC.js} +16 -15
  3. package/{agentEmailCommands-HQT4EUOA.js → agentEmailCommands-V3ADGHX7.js} +30 -28
  4. package/{agentListCommands-2YWZYH52.js → agentListCommands-EPK3S274.js} +18 -17
  5. package/{agentMachineCommands-3TR7DVYO.js → agentMachineCommands-XMZWK7P3.js} +16 -15
  6. package/{agentRecordCommands-TRUY7Z6N.js → agentRecordCommands-WFPWLWOF.js} +29 -27
  7. package/{agentRunCommand-EHJOESHA.js → agentRunCommand-VUWNWC7C.js} +29 -27
  8. package/{agentRuntimeLocal-O6DZDTK4.js → agentRuntimeLocal-YRONTDLM.js} +15 -14
  9. package/{agentSlice-RRZS2UII.js → agentSlice-2P7OJFES.js} +1 -1
  10. package/{appDeleteCommands-HIJNWSK7.js → appDeleteCommands-YS7UGMP3.js} +17 -16
  11. package/{appDeployCommands-XULV2ZN6.js → appDeployCommands-XIE5Z4BE.js} +17 -16
  12. package/{appDeployStatusCommands-ETY4Z7K6.js → appDeployStatusCommands-54UDVYET.js} +17 -16
  13. package/{appGetCommands-M253LKDA.js → appGetCommands-6XYLBCRN.js} +17 -16
  14. package/{appListCommands-XYFXJMNP.js → appListCommands-VEGH7SF5.js} +17 -16
  15. package/{authCommand-EVGFLAHO.js → authCommand-JIEOFHXW.js} +84 -9
  16. package/{chatQueueReduxAdapter-LSBTGZW2.js → chatQueueReduxAdapter-GPYCH5Z2.js} +5 -5
  17. package/chromeCommands-VOIRNJBL.js +499 -0
  18. package/chromeOpsCommands-M7DPCX2T.js +258 -0
  19. package/{chunk-FARB62OB.js → chunk-3VPDELAP.js} +42 -31
  20. package/{chunk-CS36XM4N.js → chunk-4SZ37KLL.js} +21 -9
  21. package/{chunk-FPTQQSS6.js → chunk-73BR2IQT.js} +23 -2
  22. package/{chunk-PYXL2CJL.js → chunk-7FWPLK4M.js} +1 -1
  23. package/{chunk-DSRXPQWA.js → chunk-7QVPPOEC.js} +1 -1
  24. package/{chunk-KIS76A2P.js → chunk-AX7RMXSH.js} +1 -1
  25. package/{chunk-OXY5PKLG.js → chunk-BKJO7LDP.js} +2 -2
  26. package/{chunk-Q6FF2MCK.js → chunk-BOYUESO7.js} +1 -1
  27. package/{chunk-GBPZKFYK.js → chunk-BZNGCVAW.js} +1 -1
  28. package/{chunk-EXMHJN7C.js → chunk-CADAR6QU.js} +1 -1
  29. package/{chunk-2QDDKFG5.js → chunk-CQ3WA2NE.js} +77 -0
  30. package/{chunk-B62OSA77.js → chunk-D6XYLSOS.js} +1 -1
  31. package/{chunk-TI4CCL34.js → chunk-DM4FBSWG.js} +3 -3
  32. package/{chunk-JWO3RQSH.js → chunk-DUJWWUGV.js} +1 -1
  33. package/{chunk-XGZM6BTX.js → chunk-EBKMQULC.js} +66 -120
  34. package/{chunk-ZLPA7K55.js → chunk-ETLZZQJE.js} +2 -2
  35. package/chunk-FMODTASJ.js +109 -0
  36. package/{chunk-T7PQFKJU.js → chunk-FNGAF7C4.js} +2 -2
  37. package/{chunk-MYU3MJCF.js → chunk-GCJ3IS5M.js} +0 -1
  38. package/{chunk-XPQJXIF5.js → chunk-GHEJJGJI.js} +125 -25
  39. package/{chunk-DFY6PUV4.js → chunk-HTMVBPOF.js} +7 -7
  40. package/{chunk-VOEW2KF3.js → chunk-IDGYOD2O.js} +59 -55
  41. package/{chunk-YCDWJX45.js → chunk-KC5FAHVS.js} +1 -1
  42. package/{chunk-2JCTE5UN.js → chunk-KEZNSFGD.js} +3 -3
  43. package/{chunk-I4UWPYBH.js → chunk-KRMWO2NN.js} +2 -2
  44. package/{chunk-PGSKWZCB.js → chunk-LHDV2QUI.js} +1 -1
  45. package/{chunk-UJ63UXGE.js → chunk-MXUSPS2N.js} +289 -2
  46. package/{chunk-3IT2PFHL.js → chunk-NB5SIWC4.js} +1 -1
  47. package/{chunk-HXAINXPQ.js → chunk-NFT2AOK5.js} +2 -2
  48. package/{chunk-AUKZOZFG.js → chunk-NRALD3CC.js} +2 -2
  49. package/{chunk-CJ23A7U4.js → chunk-PF6O3GR3.js} +533 -57
  50. package/{chunk-K7AG465Q.js → chunk-PZCTYKGU.js} +3 -1
  51. package/chunk-QCZX4FTW.js +242 -0
  52. package/{chunk-QBA5KMJI.js → chunk-RZOJRTKR.js} +29 -15
  53. package/{chunk-OH5WRY7G.js → chunk-T25L377S.js} +18 -6
  54. package/{chunk-CMH2SYHX.js → chunk-TMPLPCVX.js} +1 -1
  55. package/{chunk-3U3EA3BO.js → chunk-U7YP7ECK.js} +3 -3
  56. package/{chunk-AWPU7GXU.js → chunk-URWDSZRW.js} +2 -2
  57. package/{chunk-Q4OT2DC7.js → chunk-WL2RKIBF.js} +1 -1
  58. package/{chunk-IDZTNRP2.js → chunk-X7HHHIOZ.js} +10 -2
  59. package/{chunk-57VRXRLF.js → chunk-Y3NZYLPG.js} +1 -1
  60. package/{chunk-V3ZKNK5D.js → chunk-ZVN6I33J.js} +5 -5
  61. package/{codingSkills-E6KSDNSJ.js → codingSkills-7S6523WH.js} +2 -2
  62. package/{contentThunks-476Y6CFR.js → contentThunks-THSCADSK.js} +6 -6
  63. package/{createAgentAutomationAction-DN4SFTP5.js → createAgentAutomationAction-WGN3UQ5A.js} +6 -6
  64. package/{createDialogAction-2GS4JCMU.js → createDialogAction-3EN2NLH7.js} +8 -8
  65. package/{createPageAction-44WTUMZ5.js → createPageAction-4VK473QV.js} +6 -6
  66. package/{dialogCommands-PE5FEFJ5.js → dialogCommands-PS4NWYY7.js} +17 -16
  67. package/{dialogSlice-7HEVCJFT.js → dialogSlice-CLV7SVOZ.js} +5 -5
  68. package/{docCreateCommands-IPDFZVPG.js → docCreateCommands-4KANZJR6.js} +16 -15
  69. package/{docDeleteCommands-VQ4Y66CM.js → docDeleteCommands-F2S2TRUF.js} +17 -16
  70. package/{docReadCommands-PD33VV27.js → docReadCommands-B2WDPZQE.js} +16 -15
  71. package/{docStore-LJ7KNKFB.js → docStore-TDLCHWPF.js} +1 -1
  72. package/{docUpdateCommands-CGUQ2JY3.js → docUpdateCommands-E7TKHRIO.js} +16 -15
  73. package/{getPricing-HRMSOF3E.js → getPricing-QVTUQQTM.js} +2 -2
  74. package/{handleSendMessageAction-ETA5QZIN.js → handleSendMessageAction-AHL3ZHE4.js} +5 -5
  75. package/index.js +118 -54
  76. package/{machineCommands-INAP62WH.js → machineCommands-OTABVXOA.js} +28 -26
  77. package/{machineWsRunDispatch-MT7VRNKX.js → machineWsRunDispatch-V4EVQSMZ.js} +28 -26
  78. package/package.json +1 -1
  79. package/{readlineWorkspace-5TV6ES57.js → readlineWorkspace-IIT3FQ6I.js} +439 -117
  80. package/{runAgentClientLoop-2PGZSCET.js → runAgentClientLoop-7TQM7IAR.js} +18 -18
  81. package/{spaceCommands-Y47DDAQD.js → spaceCommands-FSYH7AL4.js} +16 -15
  82. package/{streamAgentChatTurn-I6FUM6RV.js → streamAgentChatTurn-6EF6TVFK.js} +22 -22
  83. package/{tools-KU4X5HGZ.js → tools-R3QNWWTD.js} +15 -13
  84. package/{tuiSummaryLlmCaller-J5BEPLW5.js → tuiSummaryLlmCaller-PWOXTCBF.js} +5 -5
  85. package/{updateCommands-AJYSD73Y.js → updateCommands-Y363MC3L.js} +2 -2
  86. package/{updateDialogTitleAction-E3SNB75W.js → updateDialogTitleAction-F3EUOWOB.js} +6 -6
@@ -8,7 +8,7 @@ import {
8
8
  normalizeDialogRecord,
9
9
  readDialogSnapshot,
10
10
  sortDialogs
11
- } from "./chunk-T7PQFKJU.js";
11
+ } from "./chunk-FNGAF7C4.js";
12
12
  import {
13
13
  ANSI_ESCAPE_REGEX,
14
14
  COLLAPSED_PASTE_PLACEHOLDER_RE,
@@ -72,7 +72,7 @@ import {
72
72
  wrapTextToLines,
73
73
  wrapTranscriptLine,
74
74
  wrapTranscriptLineWithLayout
75
- } from "./chunk-FARB62OB.js";
75
+ } from "./chunk-3VPDELAP.js";
76
76
  import "./chunk-S7Z2HMCN.js";
77
77
  import {
78
78
  listFavoriteAgentIdsAcrossServers,
@@ -80,12 +80,12 @@ import {
80
80
  listRemoteAgents,
81
81
  listRemoteAgentsAcrossServers,
82
82
  normalizeListedAgent
83
- } from "./chunk-3U3EA3BO.js";
83
+ } from "./chunk-U7YP7ECK.js";
84
84
  import "./chunk-ID4G2MAR.js";
85
85
  import {
86
86
  listUserRecordsFromServers,
87
87
  readLiveDbRecordAfterTombstoneMerge
88
- } from "./chunk-Q4OT2DC7.js";
88
+ } from "./chunk-WL2RKIBF.js";
89
89
  import "./chunk-JYYZCK2Z.js";
90
90
  import {
91
91
  RUN_WAKE_CHANNEL_ENV,
@@ -96,8 +96,9 @@ import {
96
96
  readTimestamp,
97
97
  setCliLocale,
98
98
  t
99
- } from "./chunk-XPQJXIF5.js";
100
- import "./chunk-GBPZKFYK.js";
99
+ } from "./chunk-GHEJJGJI.js";
100
+ import "./chunk-BZNGCVAW.js";
101
+ import "./chunk-QCZX4FTW.js";
101
102
  import "./chunk-EADNWBAZ.js";
102
103
  import {
103
104
  backfillRunRecordParentDialog,
@@ -114,8 +115,8 @@ import {
114
115
  deleteDbRecord,
115
116
  queryUserRecords,
116
117
  readDbRecord
117
- } from "./chunk-AUKZOZFG.js";
118
- import "./chunk-Q6FF2MCK.js";
118
+ } from "./chunk-NRALD3CC.js";
119
+ import "./chunk-BOYUESO7.js";
119
120
  import {
120
121
  NOLO_DEFAULT_AGENT_KEY,
121
122
  resolveCliAgentKeyInput
@@ -134,12 +135,12 @@ import "./chunk-ATNTHIZI.js";
134
135
  import "./chunk-JEXVIMYZ.js";
135
136
  import {
136
137
  prepareTools
137
- } from "./chunk-I4UWPYBH.js";
138
+ } from "./chunk-KRMWO2NN.js";
138
139
  import {
139
140
  buildResponseGuidelines
140
141
  } from "./chunk-EOARMOVD.js";
141
142
  import "./chunk-HS7MLR74.js";
142
- import "./chunk-QBA5KMJI.js";
143
+ import "./chunk-RZOJRTKR.js";
143
144
  import "./chunk-5XTOKQLW.js";
144
145
  import "./chunk-NF64D32W.js";
145
146
  import "./chunk-3L2WKC3W.js";
@@ -154,7 +155,7 @@ import {
154
155
  runShowsLogTail,
155
156
  shortRunId
156
157
  } from "./chunk-NTW5GWDM.js";
157
- import "./chunk-FPTQQSS6.js";
158
+ import "./chunk-73BR2IQT.js";
158
159
  import "./chunk-KQHGZWSV.js";
159
160
  import "./chunk-UDQWGU7F.js";
160
161
  import "./chunk-PZM3EENV.js";
@@ -165,18 +166,18 @@ import "./chunk-FZGV4FGK.js";
165
166
  import "./chunk-MEYIRKDN.js";
166
167
  import "./chunk-63W5N6P3.js";
167
168
  import "./chunk-VF4K2QE6.js";
168
- import "./chunk-AWPU7GXU.js";
169
- import "./chunk-2JCTE5UN.js";
170
- import "./chunk-XGZM6BTX.js";
169
+ import "./chunk-URWDSZRW.js";
170
+ import "./chunk-KEZNSFGD.js";
171
+ import "./chunk-EBKMQULC.js";
171
172
  import "./chunk-ZJ7UHBJS.js";
172
173
  import {
173
174
  extractReferenceKeysFromMessage
174
- } from "./chunk-VOEW2KF3.js";
175
+ } from "./chunk-IDGYOD2O.js";
175
176
  import "./chunk-EVBFYPDC.js";
176
177
  import "./chunk-HQUORJTM.js";
177
178
  import "./chunk-YYLHWIW6.js";
178
179
  import "./chunk-AGGIDPCZ.js";
179
- import "./chunk-TI4CCL34.js";
180
+ import "./chunk-DM4FBSWG.js";
180
181
  import "./chunk-BJCWNHVU.js";
181
182
  import "./chunk-5RGU3J7P.js";
182
183
  import "./chunk-6YTOJY35.js";
@@ -190,43 +191,45 @@ import {
190
191
  buildSkillDiscoveryContextBlock,
191
192
  buildSkillDiscoveryContextLayer
192
193
  } from "./chunk-E4MARS3Q.js";
193
- import "./chunk-3IT2PFHL.js";
194
+ import "./chunk-NB5SIWC4.js";
194
195
  import {
195
196
  expandEnabledPacks
196
- } from "./chunk-MYU3MJCF.js";
197
+ } from "./chunk-GCJ3IS5M.js";
197
198
  import "./chunk-AETZ7AD6.js";
198
199
  import "./chunk-KHCLMKZZ.js";
199
200
  import {
200
201
  buildLocalWorkspaceOpenAiTools,
201
202
  buildLocalWorkspaceToolset,
203
+ formatToolOverflowMarker,
202
204
  getProcessRegistry,
203
205
  readCommandActionGatePayload
204
- } from "./chunk-CJ23A7U4.js";
205
- import "./chunk-HXAINXPQ.js";
206
+ } from "./chunk-PF6O3GR3.js";
206
207
  import "./chunk-ZNFJWEN4.js";
207
- import "./chunk-KIS76A2P.js";
208
- import "./chunk-2QDDKFG5.js";
208
+ import "./chunk-NFT2AOK5.js";
209
+ import "./chunk-FMODTASJ.js";
210
+ import "./chunk-AX7RMXSH.js";
211
+ import "./chunk-CQ3WA2NE.js";
209
212
  import "./chunk-N7J63Q6C.js";
210
213
  import {
211
214
  buildIdentityBlock,
212
215
  buildRuntimeGuidanceBlocks,
213
216
  resolveToolGuidedSections
214
- } from "./chunk-ZLPA7K55.js";
217
+ } from "./chunk-ETLZZQJE.js";
215
218
  import "./chunk-PUOC4YRO.js";
216
- import "./chunk-PGSKWZCB.js";
217
- import {
218
- resolvePlatformAuthToken
219
- } from "./chunk-CMH2SYHX.js";
220
- import "./chunk-V3ZKNK5D.js";
219
+ import "./chunk-ZVN6I33J.js";
221
220
  import {
222
221
  isQuotaExhaustedError
223
222
  } from "./chunk-CJCJF5XB.js";
223
+ import "./chunk-LHDV2QUI.js";
224
+ import {
225
+ resolvePlatformAuthToken
226
+ } from "./chunk-TMPLPCVX.js";
224
227
  import "./chunk-2MBTWINN.js";
225
228
  import {
226
229
  DEFAULT_CONTEXT_WINDOW,
227
230
  getModelContextWindow,
228
231
  normalizeUsage
229
- } from "./chunk-DSRXPQWA.js";
232
+ } from "./chunk-7QVPPOEC.js";
230
233
  import {
231
234
  buildCompactionMetricsFromPlan,
232
235
  buildCompactionUserContent,
@@ -237,12 +240,12 @@ import {
237
240
  planCompression,
238
241
  serializeMessageContent
239
242
  } from "./chunk-E36HZARC.js";
240
- import "./chunk-K7AG465Q.js";
243
+ import "./chunk-PZCTYKGU.js";
241
244
  import {
242
245
  estimateTokenCount
243
246
  } from "./chunk-PSCNXBYA.js";
244
247
  import "./chunk-BYXBJQAS.js";
245
- import "./chunk-UJ63UXGE.js";
248
+ import "./chunk-MXUSPS2N.js";
246
249
  import {
247
250
  compactWhitespace
248
251
  } from "./chunk-DWLEOOGR.js";
@@ -268,7 +271,7 @@ import "./chunk-BYEJ7K2Z.js";
268
271
  import {
269
272
  checkForCliUpdate,
270
273
  runSelfUpdateDetailed
271
- } from "./chunk-YCDWJX45.js";
274
+ } from "./chunk-KC5FAHVS.js";
272
275
  import {
273
276
  getAgentSelectionAuditPath,
274
277
  getDefaultProfileConfigPath,
@@ -282,7 +285,7 @@ import {
282
285
  import {
283
286
  readPipeText,
284
287
  spawnProcess
285
- } from "./chunk-IDZTNRP2.js";
288
+ } from "./chunk-X7HHHIOZ.js";
286
289
  import {
287
290
  parseUserIdFromAuthToken,
288
291
  resolveAuthToken,
@@ -346,7 +349,7 @@ import { stdin as defaultInput, stdout as defaultOutput } from "node:process";
346
349
  import nacl from "tweetnacl";
347
350
 
348
351
  // node_modules/js-base64/base64.mjs
349
- var version = "3.9.3";
352
+ var version = "3.9.4";
350
353
  var VERSION = version;
351
354
  var _TD = typeof TextDecoder === "function" ? new TextDecoder("utf-8", { ignoreBOM: true }) : void 0;
352
355
  var _TE = typeof TextEncoder === "function" ? new TextEncoder() : void 0;
@@ -1655,13 +1658,14 @@ function layoutTurnRows(role, content, contentWidth, colorEnabled, command) {
1655
1658
  const surfaceSeq = colorEnabled ? userSurfaceBackgroundSequence() : "";
1656
1659
  const boldOn = colorEnabled ? "\x1B[1m" : "";
1657
1660
  const boldOff = colorEnabled ? "\x1B[22m" : "";
1661
+ const fgOff = colorEnabled ? "\x1B[39m" : "";
1658
1662
  const prefixWidth = 3;
1659
1663
  let lineSourceStart = 0;
1660
1664
  const rows2 = [];
1661
1665
  for (let i = 0; i < logicalLines.length; i++) {
1662
1666
  const line = logicalLines[i];
1663
1667
  const prefix = i === 0 ? firstPrefix : multilinePrefix;
1664
- const styledLine = `${prefix}${boldOn}${line}${boldOff}`;
1668
+ const styledLine = `${prefix}${accentSeq}${boldOn}${line}${boldOff}${fgOff}`;
1665
1669
  const prefixCharCount = prefix.length;
1666
1670
  const wrappedRows = wrapTranscriptLineWithLayout(
1667
1671
  styledLine,
@@ -1854,9 +1858,10 @@ function renderTailTurnBlock(role, content, contentWidth, colorEnabled) {
1854
1858
  const hangingIndent = colorEnabled ? `${accentSeq}\x1B[1m\u2503 \x1B[0m` : "\u2503 ";
1855
1859
  const boldOn = colorEnabled ? "\x1B[1m" : "";
1856
1860
  const boldOff = colorEnabled ? "\x1B[22m" : "";
1861
+ const fgOff = colorEnabled ? "\x1B[39m" : "";
1857
1862
  const lines = [];
1858
1863
  for (const rawLine of content.split("\n")) {
1859
- const styledLine = `${multilinePrefix}${boldOn}${rawLine}${boldOff}`;
1864
+ const styledLine = `${multilinePrefix}${accentSeq}${boldOn}${rawLine}${boldOff}${fgOff}`;
1860
1865
  const rows = wrapTranscriptLine(styledLine, contentWidth, hangingIndent);
1861
1866
  lines.push(
1862
1867
  ...surfaceSeq ? rows.map((row) => fillUserBubbleRow(row, surfaceSeq, contentWidth)) : rows
@@ -4502,6 +4507,36 @@ function detectImagePaths(line, cwd, opts = {}) {
4502
4507
  }
4503
4508
  return out;
4504
4509
  }
4510
+ function detectFileReferences(line, cwd, opts = {}) {
4511
+ const wsl = opts.wsl ?? isWslEnvironment();
4512
+ const excluded = new Set(opts.exclude ?? []);
4513
+ const out = [];
4514
+ const seen = /* @__PURE__ */ new Set();
4515
+ for (const token of tokenizePasteLine(line)) {
4516
+ if (!token.raw || excluded.has(token.raw)) continue;
4517
+ let candidate = token.decoded;
4518
+ const uriPath = fileUriToPath(candidate);
4519
+ if (uriPath !== null) candidate = uriPath;
4520
+ if (wsl) candidate = mapWindowsPathToWsl(candidate);
4521
+ if (candidate.startsWith("~")) candidate = resolveImageSource(candidate, cwd);
4522
+ const looksLikePath = /[/\\]/.test(candidate) || candidate.startsWith("~") || /^[a-zA-Z]:/.test(candidate);
4523
+ if (!looksLikePath) continue;
4524
+ const ext = extnameOf(candidate);
4525
+ if (!/^[a-z0-9]{1,8}$/.test(ext)) continue;
4526
+ if (IMAGE_EXTENSION_SET.has(ext)) continue;
4527
+ const resolved = resolveImageSource(candidate, cwd);
4528
+ if (excluded.has(resolved) || seen.has(resolved)) continue;
4529
+ if (!isReadableFilePath(resolved)) continue;
4530
+ try {
4531
+ if (!statSync(resolved).isFile()) continue;
4532
+ } catch {
4533
+ continue;
4534
+ }
4535
+ seen.add(resolved);
4536
+ out.push({ raw: token.raw, resolvedPath: resolved });
4537
+ }
4538
+ return out;
4539
+ }
4505
4540
  function tokenizePasteLine(line) {
4506
4541
  const tokens = [];
4507
4542
  let rawBuf = "";
@@ -5004,22 +5039,69 @@ async function readClipboardImage(deps = {}) {
5004
5039
  });
5005
5040
  }
5006
5041
 
5042
+ // packages/cli/tui/pasteFlow.ts
5043
+ function isTextFallbackEligibleCode(code) {
5044
+ return code === "empty-clipboard" || code === "binary-missing" || code === "unsupported-platform";
5045
+ }
5046
+ function errorCodeOf(error) {
5047
+ if (typeof error !== "object" || error === null) return void 0;
5048
+ const code = error.code;
5049
+ return code === void 0 || code === null ? void 0 : String(code);
5050
+ }
5051
+ function messageOf(error) {
5052
+ return error instanceof Error ? error.message : String(error);
5053
+ }
5054
+ async function resolveClipboardPaste(deps) {
5055
+ try {
5056
+ const image = await deps.readImage();
5057
+ return { kind: "image", image };
5058
+ } catch (imageError) {
5059
+ const code = errorCodeOf(imageError);
5060
+ if (!isTextFallbackEligibleCode(code)) {
5061
+ return {
5062
+ kind: "error",
5063
+ code: code ?? "read-failed",
5064
+ message: messageOf(imageError)
5065
+ };
5066
+ }
5067
+ let text;
5068
+ try {
5069
+ text = await deps.readText();
5070
+ } catch (textError) {
5071
+ return {
5072
+ kind: "error",
5073
+ code: "text-read-failed",
5074
+ message: `${messageOf(textError)} (${messageOf(imageError)})`
5075
+ };
5076
+ }
5077
+ if (text.length > 0) {
5078
+ return { kind: "text", text };
5079
+ }
5080
+ return { kind: "empty", message: messageOf(imageError) };
5081
+ }
5082
+ }
5083
+ function shouldApplyPastedResult(snapshot, current) {
5084
+ if (current.sessionEnded) return false;
5085
+ return snapshot.epoch === current.epoch;
5086
+ }
5087
+
5007
5088
  // packages/cli/tui/clipboard.ts
5008
5089
  function osc52SetClipboard(text, output) {
5009
5090
  const payload = Buffer.from(text, "utf8").toString("base64");
5010
5091
  output.write(`\x1B]52;c;${payload}\x07`);
5011
5092
  }
5012
5093
  async function writeClipboard(text, target) {
5013
- try {
5094
+ if (target.transport === "system") {
5014
5095
  await target.systemWrite(text);
5015
- } catch {
5096
+ return;
5016
5097
  }
5017
- if (target.sendOsc52 !== false) {
5018
- try {
5019
- osc52SetClipboard(text, target.output);
5020
- } catch {
5021
- }
5098
+ if (target.transport === "osc52") {
5099
+ osc52SetClipboard(text, target.output);
5100
+ return;
5022
5101
  }
5102
+ throw new Error(
5103
+ "Client clipboard is unavailable in a non-interactive remote session."
5104
+ );
5023
5105
  }
5024
5106
 
5025
5107
  // packages/cli/tui/gitStatus.ts
@@ -5633,6 +5715,7 @@ var SLASH_COMMANDS = [
5633
5715
  "/resume",
5634
5716
  "/lang",
5635
5717
  "/copy",
5718
+ "/paste",
5636
5719
  "/mouse",
5637
5720
  "/math",
5638
5721
  "/doc",
@@ -5989,12 +6072,18 @@ function handleTuiInput(input, state) {
5989
6072
  const stripped = stripImageTokens(trimmed, readableHints);
5990
6073
  const finalMessage = stripped.length > 0 ? stripped : trimmed;
5991
6074
  const imagePaths = readableHints.map((hint) => hint.resolvedPath);
5992
- const preview = hints.length > 0 ? hints.map(
5993
- (hint) => hint.unreadable ? `image path unreadable: ${hint.resolvedPath}` : `found image: ${hint.resolvedPath}`
5994
- ).join("\n") : "";
6075
+ const fileRefs = detectFileReferences(trimmed, state.cwd, {
6076
+ exclude: hints.map((hint) => hint.resolvedPath)
6077
+ });
6078
+ const previewLines = [
6079
+ ...hints.map(
6080
+ (hint) => hint.unreadable ? `image path unreadable: ${hint.resolvedPath}` : `found image: ${hint.resolvedPath}`
6081
+ ),
6082
+ ...fileRefs.map((ref) => t("pasteFilePathHint", ref.resolvedPath))
6083
+ ];
5995
6084
  return {
5996
6085
  nextState: state,
5997
- output: preview,
6086
+ output: previewLines.join("\n"),
5998
6087
  action: {
5999
6088
  type: "chat",
6000
6089
  message: finalMessage,
@@ -6341,6 +6430,15 @@ ${t("autoUsage")}`
6341
6430
  }
6342
6431
  return { nextState: state, output: t("copyUsage") };
6343
6432
  }
6433
+ case "/paste":
6434
+ if (argText) {
6435
+ return { nextState: state, output: t("pasteUsage") };
6436
+ }
6437
+ return {
6438
+ nextState: state,
6439
+ output: "",
6440
+ action: { type: "paste-clipboard" }
6441
+ };
6344
6442
  case "/mouse": {
6345
6443
  if (argText !== "on" && argText !== "off") {
6346
6444
  return { nextState: state, output: t("mouseUsage") };
@@ -6653,13 +6751,45 @@ function createTurnInjectionInbox() {
6653
6751
 
6654
6752
  // packages/cli/tui/processTerminalNotice.ts
6655
6753
  function formatProcessTerminalWakeMessage(notices) {
6754
+ const missingCapsule = notices.some((notice) => !notice.resultCapsule);
6656
6755
  return [
6657
6756
  `<background_task_completion count="${notices.length}">`,
6658
- ...notices.map(formatProcessTerminalNoticeLine),
6659
- `\u9700\u8981\u8F93\u51FA/\u8BE6\u60C5\u65F6: taskLogs(taskId) \u6216 tasks`,
6757
+ ...notices.flatMap((notice) => [
6758
+ formatProcessTerminalNoticeLine(notice),
6759
+ ...notice.resultCapsule ? formatProcessResultCapsuleLines(notice.resultCapsule) : []
6760
+ ]),
6761
+ ...missingCapsule ? ["\u751F\u547D\u5468\u671F\u8BE6\u60C5\u53EF\u7528 taskLogs(taskId)"] : [],
6660
6762
  `</background_task_completion>`
6661
6763
  ].join("\n");
6662
6764
  }
6765
+ function formatProcessResultCapsuleLines(capsule) {
6766
+ const meta = [];
6767
+ if (capsule.durationMs !== void 0) meta.push(`duration=${capsule.durationMs}ms`);
6768
+ meta.push(capsule.truncated ? "tail-only" : "full");
6769
+ const stream = (name, value) => [
6770
+ `--- ${name} ---`,
6771
+ ...value.trim() ? value.replace(/\n+$/, "").split("\n") : ["(empty)"]
6772
+ ];
6773
+ const omittedChars = capsule.spill ? Math.max(0, capsule.spill.totalChars - capsule.stdout.length - capsule.stderr.length) : 0;
6774
+ return [
6775
+ `result capsule (${meta.join(", ")}):`,
6776
+ ...capsule.captureError ? [`capture error: ${capsule.captureError}`] : [],
6777
+ ...stream("stdout", capsule.stdout),
6778
+ ...stream("stderr", capsule.stderr),
6779
+ // Retrieval pointer reuses the tool-overflow convention: the model learns
6780
+ // one marker for "the tail above is not the whole story, full text lives
6781
+ // here", whether the truncation happened in a foreground tool result or in
6782
+ // a detached task capsule.
6783
+ ...capsule.spill ? [
6784
+ formatToolOverflowMarker({
6785
+ spillRef: capsule.spill.displayPath,
6786
+ totalChars: capsule.spill.totalChars,
6787
+ totalLines: capsule.spill.totalLines,
6788
+ omittedChars
6789
+ }).trim()
6790
+ ] : []
6791
+ ];
6792
+ }
6663
6793
  function formatProcessTerminalNoticeLine(notice) {
6664
6794
  const informative = notice.label && notice.label.trim() !== "" && notice.label !== notice.command;
6665
6795
  const label = informative ? ` (label: "${notice.label.replace(/(["\\])/g, "\\$1")}")` : "";
@@ -6670,7 +6800,30 @@ function buildProcessTerminalTurnMessage(notices) {
6670
6800
  if (notices.length === 0) return "";
6671
6801
  return formatProcessTerminalWakeMessage(notices);
6672
6802
  }
6803
+ function buildProcessTaskCompletedTurnEvent(notice) {
6804
+ return {
6805
+ kind: "background-task-completed",
6806
+ taskId: notice.taskId,
6807
+ status: notice.status,
6808
+ ...notice.exitCode !== void 0 ? { exitCode: notice.exitCode } : {},
6809
+ text: buildProcessTerminalTurnMessage([notice]),
6810
+ displayText: formatProcessTerminalNoticeLine(notice)
6811
+ };
6812
+ }
6813
+ var PENDING_NOTICE_PREFIX = "process-notice-v1:";
6814
+ function serializePendingProcessNotice(notice) {
6815
+ return notice.resultCapsule ? `${PENDING_NOTICE_PREFIX}${JSON.stringify(notice)}` : formatProcessTerminalNoticeLine(notice);
6816
+ }
6673
6817
  function parsePendingProcessNoticeLine(line) {
6818
+ if (line.startsWith(PENDING_NOTICE_PREFIX)) {
6819
+ try {
6820
+ const parsed = JSON.parse(line.slice(PENDING_NOTICE_PREFIX.length));
6821
+ if (parsed && typeof parsed.taskId === "string" && parsed.resultCapsule) {
6822
+ return parsed;
6823
+ }
6824
+ } catch {
6825
+ }
6826
+ }
6674
6827
  const taskMatch = line.match(
6675
6828
  /^\[Background task ([^ \]]+?)(?:\s+\(label: "((?:[^"\\]|\\.)*)"\))? finished: status=(\w+)(?: exitCode=(-?\d+))?\]$/
6676
6829
  );
@@ -6702,6 +6855,80 @@ function normalizeStatus(raw) {
6702
6855
  return "failed";
6703
6856
  }
6704
6857
 
6858
+ // packages/agent-runtime/processTerminalResume.ts
6859
+ var DEFAULT_MAX_CHAINED_COMPLETION_TURNS = 3;
6860
+ function createProcessTerminalResumeState() {
6861
+ const claimedTaskIds = /* @__PURE__ */ new Set();
6862
+ const chainDepthByDialog = /* @__PURE__ */ new Map();
6863
+ return {
6864
+ claimedTaskIds,
6865
+ chainDepthByDialog,
6866
+ noteUserTurn() {
6867
+ chainDepthByDialog.clear();
6868
+ }
6869
+ };
6870
+ }
6871
+ var resolveCap = (value) => {
6872
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 1) {
6873
+ return DEFAULT_MAX_CHAINED_COMPLETION_TURNS;
6874
+ }
6875
+ return Math.floor(value);
6876
+ };
6877
+ function decideProcessTerminalResume(input) {
6878
+ const taskId = input.notice.taskId;
6879
+ if (input.canDeliver === false) {
6880
+ return { kind: "notice-only", reason: "no-channel", taskId };
6881
+ }
6882
+ const dialogId = input.owner?.dialogId;
6883
+ if (!input.owner || !dialogId) {
6884
+ return { kind: "notice-only", reason: "unowned", taskId };
6885
+ }
6886
+ if (!input.notice.promoted) {
6887
+ return { kind: "notice-only", reason: "not-promoted", taskId };
6888
+ }
6889
+ if (input.state.claimedTaskIds.has(taskId)) {
6890
+ return { kind: "notice-only", reason: "duplicate", taskId };
6891
+ }
6892
+ const currentDialogId = input.currentDialogId ?? null;
6893
+ if (currentDialogId !== dialogId) {
6894
+ return { kind: "notice-only", reason: "dialog-mismatch", taskId };
6895
+ }
6896
+ if (input.turnActive) {
6897
+ input.state.claimedTaskIds.add(taskId);
6898
+ return { kind: "inject-turn", taskId, owner: input.owner, notice: input.notice };
6899
+ }
6900
+ const cap = resolveCap(input.maxChainedCompletionTurns);
6901
+ const depth = input.state.chainDepthByDialog.get(dialogId) ?? 0;
6902
+ if (depth >= cap) {
6903
+ return { kind: "notice-only", reason: "chain-cap", taskId };
6904
+ }
6905
+ input.state.claimedTaskIds.add(taskId);
6906
+ input.state.chainDepthByDialog.set(dialogId, depth + 1);
6907
+ return { kind: "completion-turn", taskId, owner: input.owner, notice: input.notice };
6908
+ }
6909
+ function createProcessTerminalAutoResumer(deps) {
6910
+ const state = createProcessTerminalResumeState();
6911
+ return {
6912
+ state,
6913
+ noteUserTurn: () => state.noteUserTurn(),
6914
+ decide: (notice) => {
6915
+ const promoted = notice.promoted ?? deps.isPromoted?.(notice.taskId) ?? false;
6916
+ const effectiveNotice = notice.promoted === promoted ? notice : { ...notice, promoted };
6917
+ const decision = decideProcessTerminalResume({
6918
+ notice: effectiveNotice,
6919
+ owner: deps.readOwner(notice.taskId),
6920
+ currentDialogId: deps.getCurrentDialogId(),
6921
+ turnActive: deps.isTurnActive(),
6922
+ state,
6923
+ ...deps.canDeliver ? { canDeliver: deps.canDeliver() } : {},
6924
+ ...deps.maxChainedCompletionTurns === void 0 ? {} : { maxChainedCompletionTurns: deps.maxChainedCompletionTurns }
6925
+ });
6926
+ deps.onDecision?.(decision);
6927
+ return decision;
6928
+ }
6929
+ };
6930
+ }
6931
+
6705
6932
  // packages/cli/client/turnBillingAudit.ts
6706
6933
  import { appendFileSync, mkdirSync as mkdirSync2 } from "node:fs";
6707
6934
  import { homedir as homedir4 } from "node:os";
@@ -7504,6 +7731,8 @@ async function runAskChoiceDialog(args) {
7504
7731
  // packages/chat/messages/contextualFragment.ts
7505
7732
  var BACKGROUND_RUN_OPEN = "<background_run_completion";
7506
7733
  var BACKGROUND_RUN_CLOSE = "</background_run_completion>";
7734
+ var BACKGROUND_TASK_OPEN = "<background_task_completion";
7735
+ var BACKGROUND_TASK_CLOSE = "</background_task_completion>";
7507
7736
  var SERVER_LEGACY_PREFIX = "--- \u540E\u53F0 run \u7EC8\u6001\u4E8B\u4EF6 ---";
7508
7737
  var TUI_LEGACY_PREFIX = "\u3010\u540E\u53F0 run \u7EC8\u6001\u901A\u77E5\u3011";
7509
7738
  var TIME_BLOCK_PREFIX = "--- \u5F53\u524D\u65F6\u95F4 ---";
@@ -7518,6 +7747,13 @@ var registry = [
7518
7747
  return trimmed.startsWith(BACKGROUND_RUN_OPEN) && trimmed.endsWith(BACKGROUND_RUN_CLOSE);
7519
7748
  }
7520
7749
  },
7750
+ {
7751
+ kind: "background_task_completion",
7752
+ match: (text) => {
7753
+ const trimmed = text.trim();
7754
+ return trimmed.startsWith(BACKGROUND_TASK_OPEN) && trimmed.endsWith(BACKGROUND_TASK_CLOSE);
7755
+ }
7756
+ },
7521
7757
  {
7522
7758
  kind: "server_legacy_wake",
7523
7759
  match: (text) => {
@@ -8217,6 +8453,7 @@ async function runOneAgentTurn(ctx, inputMsg, imageUrls, actionGateHandler, conf
8217
8453
  }
8218
8454
  }
8219
8455
  const message = req.text;
8456
+ if (req.event.kind === "user") ctx.noteUserTurn?.();
8220
8457
  ctx.forcedStop = false;
8221
8458
  ctx.turnEpoch += 1;
8222
8459
  const myEpoch = ctx.turnEpoch;
@@ -8224,7 +8461,7 @@ async function runOneAgentTurn(ctx, inputMsg, imageUrls, actionGateHandler, conf
8224
8461
  ctx.history.followBottom = true;
8225
8462
  const isInternalEvent = req.event.kind !== "user";
8226
8463
  const isContextualFragment = matchContextualFragment(message) !== null;
8227
- const transcriptText = isContextualFragment && req.event.kind === "child-run-completed" ? req.event.displayText ?? req.event.text : message;
8464
+ const transcriptText = isContextualFragment && (req.event.kind === "child-run-completed" || req.event.kind === "background-task-completed") ? req.event.displayText ?? req.event.text : message;
8228
8465
  startTurn(ctx.history, isInternalEvent ? "assistant" : "user");
8229
8466
  appendToCurrentTurn(
8230
8467
  ctx.history,
@@ -8525,6 +8762,8 @@ function createTuiRender(host) {
8525
8762
  // packages/cli/tui/tuiScrollAnimation.ts
8526
8763
  var SCROLL_ANIMATION_TICK_MS = 16;
8527
8764
  var DEFAULT_SCROLL_MAX_LINES_PER_TICK = 6;
8765
+ var ADAPTIVE_STEP_CAP = 20;
8766
+ var EXPLICIT_STEP_CAP = 100;
8528
8767
  function readPositiveInt(raw, fallback, max) {
8529
8768
  if (raw === void 0 || raw.trim() === "") return fallback;
8530
8769
  const parsed = Number.parseInt(raw, 10);
@@ -8532,15 +8771,20 @@ function readPositiveInt(raw, fallback, max) {
8532
8771
  return Math.min(parsed, max);
8533
8772
  }
8534
8773
  function resolveScrollTuning(env = process.env) {
8774
+ const rawStep = env.NOLO_TUI_SCROLL_MAX_STEP;
8775
+ const explicit = parseExplicitStep(rawStep);
8535
8776
  return {
8536
8777
  wheelLines: readPositiveInt(env.NOLO_TUI_WHEEL_LINES, WHEEL_SCROLL_LINES, 50),
8537
- maxLinesPerTick: readPositiveInt(
8538
- env.NOLO_TUI_SCROLL_MAX_STEP,
8539
- DEFAULT_SCROLL_MAX_LINES_PER_TICK,
8540
- 100
8541
- )
8778
+ maxLinesPerTick: explicit ?? DEFAULT_SCROLL_MAX_LINES_PER_TICK,
8779
+ adaptiveStep: explicit === null
8542
8780
  };
8543
8781
  }
8782
+ function parseExplicitStep(raw) {
8783
+ if (raw === void 0 || raw.trim() === "") return null;
8784
+ const parsed = Number.parseInt(raw, 10);
8785
+ if (!Number.isFinite(parsed) || parsed < 1) return null;
8786
+ return Math.min(parsed, EXPLICIT_STEP_CAP);
8787
+ }
8544
8788
  var defaultTickTimer = {
8545
8789
  every(ms, fn) {
8546
8790
  const handle = setInterval(fn, ms);
@@ -8549,9 +8793,10 @@ var defaultTickTimer = {
8549
8793
  }
8550
8794
  };
8551
8795
  function createScrollAnimator(deps) {
8552
- const { wheelLines, maxLinesPerTick } = resolveScrollTuning(
8796
+ const { wheelLines, maxLinesPerTick, adaptiveStep } = resolveScrollTuning(
8553
8797
  deps.env ?? process.env
8554
8798
  );
8799
+ const stepCap = adaptiveStep ? Math.max(ADAPTIVE_STEP_CAP, wheelLines) : maxLinesPerTick;
8555
8800
  const timer = deps.tickTimer ?? defaultTickTimer;
8556
8801
  const history = deps.history;
8557
8802
  let goal = null;
@@ -8589,7 +8834,17 @@ function createScrollAnimator(deps) {
8589
8834
  settle(maxScrollTop);
8590
8835
  return;
8591
8836
  }
8592
- const step = Math.sign(delta) * Math.min(Math.abs(delta), maxLinesPerTick);
8837
+ const backlog = Math.abs(delta);
8838
+ const limit = adaptiveStep ? Math.min(
8839
+ backlog,
8840
+ Math.max(
8841
+ DEFAULT_SCROLL_MAX_LINES_PER_TICK,
8842
+ wheelLines,
8843
+ Math.ceil(backlog / 4)
8844
+ ),
8845
+ stepCap
8846
+ ) : Math.min(backlog, maxLinesPerTick);
8847
+ const step = Math.sign(delta) * limit;
8593
8848
  history.scrollTop += step;
8594
8849
  deps.onPaint();
8595
8850
  if (history.scrollTop === goal) {
@@ -9743,6 +9998,8 @@ async function runSubmittedSlashLine(line, host, actionGateHandler, confirmDestr
9743
9998
  persistExplicitAgentSwitch,
9744
9999
  persistAgentSelection: persistAgentSelection2,
9745
10000
  writeClipboard: writeClipboard2,
10001
+ readAndApplyClipboard,
10002
+ bumpDraftEpoch,
9746
10003
  selfUpdater,
9747
10004
  spawnRunner,
9748
10005
  installAltScreenRestoreHandlers: installAltScreenRestoreHandlers2
@@ -9751,9 +10008,12 @@ async function runSubmittedSlashLine(line, host, actionGateHandler, confirmDestr
9751
10008
  const previousAgentKey = host.state.agentKey;
9752
10009
  host.state = result.nextState;
9753
10010
  const interactive = isInteractiveInput(input);
10011
+ const isChatAction = result.action?.type === "chat";
9754
10012
  if (result.action?.type !== "exit" && result.output) {
9755
- const shouldEmit = result.action?.type !== "chat" || !interactive;
9756
- if (shouldEmit) emitCommandOutput(result.output, interactive ? line.trim() : "");
10013
+ emitCommandOutput(
10014
+ result.output,
10015
+ interactive && !isChatAction ? line.trim() : ""
10016
+ );
9757
10017
  }
9758
10018
  if (host.state.agentKey !== previousAgentKey) {
9759
10019
  persistExplicitAgentSwitch(previousAgentKey);
@@ -9784,6 +10044,7 @@ async function runSubmittedSlashLine(line, host, actionGateHandler, confirmDestr
9784
10044
  }
9785
10045
  }
9786
10046
  clearCollapsedPasteStore(pasteStore);
10047
+ bumpDraftEpoch();
9787
10048
  host.state = {
9788
10049
  ...host.state,
9789
10050
  turnTokens: void 0,
@@ -10044,6 +10305,9 @@ async function runSubmittedSlashLine(line, host, actionGateHandler, confirmDestr
10044
10305
  }
10045
10306
  }
10046
10307
  }
10308
+ if (result.action?.type === "paste-clipboard") {
10309
+ await readAndApplyClipboard("slash");
10310
+ }
10047
10311
  if (result.action?.type === "pick-dialog") {
10048
10312
  const interactivePicker = isInteractiveInput(input);
10049
10313
  try {
@@ -10086,6 +10350,7 @@ async function runSubmittedSlashLine(line, host, actionGateHandler, confirmDestr
10086
10350
  };
10087
10351
  seedDialogCreditsBase(pickResult.dialog.id, pickResult.dialog.dbKey);
10088
10352
  clearCollapsedPasteStore(pasteStore);
10353
+ bumpDraftEpoch();
10089
10354
  emitCommandOutput(
10090
10355
  `${t("resumedDialogPrefix")}: ${pickResult.dialog.title} (${pickResult.dialog.id})`
10091
10356
  );
@@ -10325,7 +10590,7 @@ var WELCOME_ANIM_MAX_FRAME = 8;
10325
10590
  var WELCOME_ANIM_INTERVAL_MS = 70;
10326
10591
  function isClipboardImageFallbackEligible(error) {
10327
10592
  const code = error instanceof ClipboardImageError ? error.code : typeof error === "object" && error !== null && "code" in error ? String(error.code) : void 0;
10328
- return code === "empty-clipboard" || code === "binary-missing" || code === "unsupported-platform";
10593
+ return isTextFallbackEligibleCode(code);
10329
10594
  }
10330
10595
  async function runTuiWorkspace(options) {
10331
10596
  const startupThemeMode = resolveTuiThemeMode(
@@ -10352,7 +10617,12 @@ async function runTuiWorkspace(options) {
10352
10617
  await clipboard.write(t2);
10353
10618
  },
10354
10619
  output,
10355
- sendOsc52: isInteractiveInput(input)
10620
+ // Local sessions have direct access to the user's OS clipboard and
10621
+ // must not enqueue a redundant OSC 52 write behind it: that late
10622
+ // terminal-side write can overwrite a newer browser copy. Over SSH,
10623
+ // OSC 52 remains the only path to the client clipboard; a pipe/non-
10624
+ // interactive remote session has no valid client clipboard channel.
10625
+ transport: isRemoteSession(runtimeEnv) ? isInteractiveInput(input) ? "osc52" : "unavailable" : "system"
10356
10626
  });
10357
10627
  const readClipboardText = options.clipboardReader ?? (async () => {
10358
10628
  const { default: clipboard } = await import("./clipboardy-BX4NMYHE.js");
@@ -10439,16 +10709,17 @@ async function runTuiWorkspace(options) {
10439
10709
  });
10440
10710
  const pushPendingProcessNotice = (notice) => {
10441
10711
  if (sessionEnded) return;
10442
- const line = formatProcessTerminalNoticeLine(notice);
10712
+ const line = serializePendingProcessNotice(notice);
10443
10713
  state = {
10444
10714
  ...state,
10445
10715
  pendingProcessNotices: [...state.pendingProcessNotices ?? [], line]
10446
10716
  };
10447
10717
  scheduleRender();
10448
10718
  };
10449
- const unsubscribeProcessTerminal = getProcessRegistry().onProcessTerminal(
10450
- pushPendingProcessNotice
10451
- );
10719
+ let deliverProcessTerminalNotice = pushPendingProcessNotice;
10720
+ const unsubscribeProcessTerminal = getProcessRegistry().onProcessTerminal((notice) => {
10721
+ deliverProcessTerminalNotice(notice);
10722
+ });
10452
10723
  const effectiveEnv = options.env ? { ...process.env, ...options.env } : process.env;
10453
10724
  const runRegistryPoller = createRunRegistryPoller({
10454
10725
  getDockedRuns: () => activityIndicator.getAgentRuns(),
@@ -10648,6 +10919,10 @@ async function runTuiWorkspace(options) {
10648
10919
  state = { ...state, sessionCredits: (state.sessionCredits ?? 0) + credits };
10649
10920
  };
10650
10921
  let composerDecoderDrain = null;
10922
+ let draftEpoch = 0;
10923
+ const bumpDraftEpoch = () => {
10924
+ draftEpoch += 1;
10925
+ };
10651
10926
  const emitCommandOutput = (text, command = "") => {
10652
10927
  if (!text) return;
10653
10928
  if (!isInteractiveInput(input)) {
@@ -10660,8 +10935,56 @@ async function runTuiWorkspace(options) {
10660
10935
  renderHistoryToOutput();
10661
10936
  if (fixedInput.active) fixedInput.repaint(buffer, cursorPos);
10662
10937
  };
10938
+ const applyClipboardPasteResult = (outcome, source) => {
10939
+ const colorEnabled = resolveCliColorEnabled();
10940
+ if (outcome.kind === "image") {
10941
+ state = {
10942
+ ...state,
10943
+ attachedImages: mergeAttachedImages(state.attachedImages, [outcome.image])
10944
+ };
10945
+ emitCommandOutput(summarizeAttachment(outcome.image));
10946
+ if (fixedInput.active) fixedInput.repaint(buffer, cursorPos);
10947
+ return;
10948
+ }
10949
+ if (outcome.kind === "text") {
10950
+ const pasteResult = applyTuiInputKey(
10951
+ buffer,
10952
+ `${PASTE_TOKEN_PREFIX}${outcome.text}`,
10953
+ {},
10954
+ cursorPos,
10955
+ { pasteStore, cwd: state.cwd }
10956
+ );
10957
+ buffer = pasteResult.buffer;
10958
+ cursorPos = pasteResult.cursorPos ?? buffer.length;
10959
+ if (fixedInput.active) fixedInput.repaint(buffer, cursorPos);
10960
+ if (source === "slash") {
10961
+ emitCommandOutput(t("pasteTextInserted", String(outcome.text.length)));
10962
+ }
10963
+ return;
10964
+ }
10965
+ emitCommandOutput(
10966
+ themeText(`[nolo] ${outcome.message}`, "warning", colorEnabled)
10967
+ );
10968
+ if (fixedInput.active) fixedInput.repaint(buffer, cursorPos);
10969
+ };
10970
+ const readAndApplyClipboard = async (source) => {
10971
+ const snapshot = { epoch: draftEpoch, sessionEnded };
10972
+ const outcome = await resolveClipboardPaste({
10973
+ readImage: () => readImage({ env: options.env ?? process.env }),
10974
+ readText: () => readClipboardText()
10975
+ });
10976
+ if (!shouldApplyPastedResult(snapshot, { epoch: draftEpoch, sessionEnded })) {
10977
+ if (!sessionEnded) {
10978
+ emitCommandOutput(t("pasteStaleDropped"));
10979
+ }
10980
+ return false;
10981
+ }
10982
+ applyClipboardPasteResult(outcome, source);
10983
+ return true;
10984
+ };
10663
10985
  let chatQueueBinding = null;
10664
10986
  let turnInjectionInbox = null;
10987
+ let noteUserTurnHook = null;
10665
10988
  const turnCtx = {
10666
10989
  get state() {
10667
10990
  return state;
@@ -10711,6 +11034,7 @@ async function runTuiWorkspace(options) {
10711
11034
  set turnInjectionInbox(v) {
10712
11035
  turnInjectionInbox = v;
10713
11036
  },
11037
+ noteUserTurn: () => noteUserTurnHook?.(),
10714
11038
  get sessionEnded() {
10715
11039
  return sessionEnded;
10716
11040
  },
@@ -10782,6 +11106,8 @@ async function runTuiWorkspace(options) {
10782
11106
  persistExplicitAgentSwitch,
10783
11107
  persistAgentSelection,
10784
11108
  writeClipboard: writeClipboard2,
11109
+ readAndApplyClipboard,
11110
+ bumpDraftEpoch,
10785
11111
  selfUpdater,
10786
11112
  spawnRunner,
10787
11113
  installAltScreenRestoreHandlers
@@ -10928,6 +11254,14 @@ async function runTuiWorkspace(options) {
10928
11254
  };
10929
11255
  return { actionGateHandler, confirmDestructiveAction };
10930
11256
  };
11257
+ const processTerminalResumer = createProcessTerminalAutoResumer({
11258
+ readOwner: (taskId) => getProcessRegistry().getByTaskId(taskId)?.owner ?? null,
11259
+ isPromoted: (taskId) => getProcessRegistry().getByTaskId(taskId)?.promoted ?? false,
11260
+ getCurrentDialogId: () => state.dialogId ?? null,
11261
+ isTurnActive: () => busy || fixedInput.isPaused(),
11262
+ canDeliver: () => !sessionEnded && !done && runWakeHandler !== null
11263
+ });
11264
+ noteUserTurnHook = () => processTerminalResumer.noteUserTurn();
10931
11265
  const runIdleTextTurn = async (inputMsg) => {
10932
11266
  const req = createTurnRequest(inputMsg);
10933
11267
  const { actionGateHandler, confirmDestructiveAction } = buildInteractiveTurnHandlers();
@@ -10958,25 +11292,28 @@ ${err.message}` : ""}`
10958
11292
  fixedInput.exitOutputMode(buffer, cursorPos);
10959
11293
  };
10960
11294
  (options.env ?? process.env)[RUN_WAKE_CHANNEL_ENV] = "1";
11295
+ const printInternalEventLine = (displayText) => {
11296
+ const noticeLine = dimCliText(displayText, resolveCliColorEnabled());
11297
+ if (!isInteractiveInput(input)) {
11298
+ output.write(`${noticeLine}
11299
+ `);
11300
+ return;
11301
+ }
11302
+ history.followBottom = true;
11303
+ appendStreamSafeNotice(history, noticeLine, {
11304
+ appendLocalTurn,
11305
+ startTurn
11306
+ });
11307
+ renderHistoryToOutput();
11308
+ };
10961
11309
  runWakeHandler = (event) => {
10962
11310
  if (done) return;
10963
11311
  if (busy || fixedInput.isPaused()) {
10964
11312
  const text = typeof event === "string" ? event : event.text;
10965
11313
  const injected = !!turnInjectionInbox && turnInjectionInbox.push({ text, fallback: event });
10966
11314
  if (injected) {
10967
- const displayText = typeof event !== "string" && event.kind === "child-run-completed" ? event.displayText ?? event.text : text;
10968
- const noticeLine = dimCliText(displayText, resolveCliColorEnabled());
10969
- if (!isInteractiveInput(input)) {
10970
- output.write(`${noticeLine}
10971
- `);
10972
- } else {
10973
- history.followBottom = true;
10974
- appendStreamSafeNotice(history, noticeLine, {
10975
- appendLocalTurn,
10976
- startTurn
10977
- });
10978
- renderHistoryToOutput();
10979
- }
11315
+ const displayText = typeof event === "string" ? event : event.kind === "user" ? event.text : event.displayText ?? event.text;
11316
+ printInternalEventLine(displayText);
10980
11317
  } else {
10981
11318
  const { actionGateHandler, confirmDestructiveAction } = buildInteractiveTurnHandlers();
10982
11319
  ensureChatQueueBinding(turnCtx, actionGateHandler, confirmDestructiveAction).enqueue(event);
@@ -10993,6 +11330,18 @@ ${err.message}` : ""}`
10993
11330
  );
10994
11331
  });
10995
11332
  };
11333
+ deliverProcessTerminalNotice = (notice) => {
11334
+ if (done || sessionEnded) return;
11335
+ const decision = processTerminalResumer.decide(notice);
11336
+ if (decision.kind === "notice-only") {
11337
+ pushPendingProcessNotice(notice);
11338
+ return;
11339
+ }
11340
+ runWakeHandler?.(buildProcessTaskCompletedTurnEvent(notice));
11341
+ if (fixedInput.active && !fixedInput.isPaused()) {
11342
+ fixedInput.repaint(buffer, cursorPos);
11343
+ }
11344
+ };
10996
11345
  let autoScrollDirection = "up";
10997
11346
  const autoScrollStep = () => autoScrollStepForTicks(++autoScrollTicks);
10998
11347
  const startAutoScroll = (direction, mouseX) => {
@@ -11090,6 +11439,7 @@ ${err.message}` : ""}`
11090
11439
  clearSelection();
11091
11440
  }
11092
11441
  if (buffer.length > 0) {
11442
+ bumpDraftEpoch();
11093
11443
  buffer = "";
11094
11444
  cursorPos = 0;
11095
11445
  if (fixedInput.active) fixedInput.repaint(buffer, cursorPos);
@@ -11107,11 +11457,16 @@ ${err.message}` : ""}`
11107
11457
  };
11108
11458
  const handleBusyLocalSlash = async (submittedText, busySlashCommand) => {
11109
11459
  releaseCollapsedPasteReferences(submittedText, pasteStore);
11460
+ bumpDraftEpoch();
11110
11461
  buffer = "";
11111
11462
  cursorPos = 0;
11112
11463
  if (fixedInput.active) fixedInput.repaint(buffer, cursorPos);
11113
11464
  const beforeAgentKey = state.agentKey;
11114
11465
  const res = handleTuiInput(submittedText, state);
11466
+ if (res.action?.type === "paste-clipboard") {
11467
+ await readAndApplyClipboard("slash");
11468
+ return;
11469
+ }
11115
11470
  if (res.action?.type === "theme-refresh") {
11116
11471
  state = res.nextState;
11117
11472
  const detected = await detectTerminalBackground({
@@ -11340,45 +11695,10 @@ ${merged}` : merged;
11340
11695
  return;
11341
11696
  }
11342
11697
  const isPasteToken = sequence.startsWith(PASTE_TOKEN_PREFIX);
11343
- const isPasteShortcut = sequence === "" || isPasteToken && sequence.slice(PASTE_TOKEN_PREFIX.length).trim() === "";
11698
+ const isEmptyBracketedPaste = isPasteToken && sequence.slice(PASTE_TOKEN_PREFIX.length).trim() === "";
11699
+ const isPasteShortcut = sequence === "" || isEmptyBracketedPaste;
11344
11700
  if (isPasteShortcut) {
11345
- try {
11346
- const image = await readImage({
11347
- env: options.env ?? process.env
11348
- });
11349
- state = {
11350
- ...state,
11351
- attachedImages: mergeAttachedImages(state.attachedImages, [image])
11352
- };
11353
- emitCommandOutput(summarizeAttachment(image));
11354
- } catch (error) {
11355
- if (isClipboardImageFallbackEligible(error)) {
11356
- let clipboardText = "";
11357
- try {
11358
- clipboardText = await readClipboardText();
11359
- } catch {
11360
- clipboardText = "";
11361
- }
11362
- if (clipboardText.length > 0) {
11363
- const pasteResult = applyTuiInputKey(
11364
- buffer,
11365
- `${PASTE_TOKEN_PREFIX}${clipboardText}`,
11366
- {},
11367
- cursorPos,
11368
- { pasteStore, cwd: state.cwd }
11369
- );
11370
- buffer = pasteResult.buffer;
11371
- cursorPos = pasteResult.cursorPos;
11372
- if (fixedInput.active) fixedInput.repaint(buffer, cursorPos);
11373
- return;
11374
- }
11375
- }
11376
- const msg = error instanceof Error ? error.message : String(error);
11377
- emitCommandOutput(
11378
- themeText(`[nolo] ${msg}`, "warning", resolveCliColorEnabled())
11379
- );
11380
- }
11381
- if (fixedInput.active) fixedInput.repaint(buffer, cursorPos);
11701
+ await readAndApplyClipboard(isEmptyBracketedPaste ? "bracketed-empty" : "key");
11382
11702
  return;
11383
11703
  }
11384
11704
  if (isBackspaceSequence(sequence, {}) && buffer === "" && state.attachedImages.length > 0) {
@@ -11417,7 +11737,7 @@ ${merged}` : merged;
11417
11737
  if (busyLock) {
11418
11738
  const trimmedText = submittedText.trim();
11419
11739
  const busySlashCommand = trimmedText.split(/\s+/)[0]?.toLowerCase();
11420
- const isBusyLocalSlash = busySlashCommand === "/context" || busySlashCommand === "/ctx" || busySlashCommand === "/cd" || busySlashCommand === "/switch" || busySlashCommand === "/theme" || busySlashCommand === "/runtime" || busySlashCommand === "/tools" || busySlashCommand === "/thinking" || busySlashCommand === "/auto" || busySlashCommand === "/tasks" || busySlashCommand === "/jobs" || busySlashCommand === "/procs" || busySlashCommand === "/agents" || busySlashCommand === "/doc" || busySlashCommand === "/skill" || busySlashCommand === "/customize" || busySlashCommand === "/login" || busySlashCommand === "/profile" || busySlashCommand === "/version";
11740
+ const isBusyLocalSlash = busySlashCommand === "/context" || busySlashCommand === "/ctx" || busySlashCommand === "/cd" || busySlashCommand === "/switch" || busySlashCommand === "/theme" || busySlashCommand === "/runtime" || busySlashCommand === "/tools" || busySlashCommand === "/thinking" || busySlashCommand === "/auto" || busySlashCommand === "/tasks" || busySlashCommand === "/jobs" || busySlashCommand === "/procs" || busySlashCommand === "/agents" || busySlashCommand === "/doc" || busySlashCommand === "/skill" || busySlashCommand === "/customize" || busySlashCommand === "/login" || busySlashCommand === "/profile" || busySlashCommand === "/paste" || busySlashCommand === "/version";
11421
11741
  if (isBusyLocalSlash) {
11422
11742
  await handleBusyLocalSlash(submittedText, busySlashCommand);
11423
11743
  return;
@@ -11446,11 +11766,13 @@ ${merged}` : merged;
11446
11766
  if (drainedText !== null) {
11447
11767
  buffer = "";
11448
11768
  cursorPos = 0;
11769
+ bumpDraftEpoch();
11449
11770
  await runIdleTextTurn(drainedText);
11450
11771
  return;
11451
11772
  }
11452
11773
  }
11453
11774
  busy = startsChatTurn;
11775
+ bumpDraftEpoch();
11454
11776
  buffer = "";
11455
11777
  cursorPos = 0;
11456
11778
  fixedInput.enterOutputMode(submittedText);