nolo-cli 0.151.0-alpha.3 → 0.151.0-alpha.4

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.
@@ -9,7 +9,7 @@ import {
9
9
  resolveRunSettlementNote,
10
10
  resolveWorkflowReference,
11
11
  runAgentRunCommand
12
- } from "./chunk-TTH4HOJD.js";
12
+ } from "./chunk-OXCHKYMU.js";
13
13
  import "./chunk-6QDYSCPW.js";
14
14
  import {
15
15
  parseAgentRunArgs
@@ -16065,8 +16065,10 @@ export {
16065
16065
  resolveAgentContextWindow,
16066
16066
  platformCreditsFromUsage,
16067
16067
  formatCreditsChip,
16068
+ formatTokenCount,
16068
16069
  resolveAuthorityTurnCount,
16069
16070
  computeBaselineContextTokens,
16071
+ resolveContextUsage,
16070
16072
  renderTokenStatus,
16071
16073
  COLLAPSED_PASTE_PLACEHOLDER_RE,
16072
16074
  shouldCollapsePaste,
@@ -110,7 +110,7 @@ function createDaemonShortcutCommand(path, description, command) {
110
110
  // packages/cli/agentInternalCommandEntries.ts
111
111
  function getAgentInternalCommandEntries() {
112
112
  const createAgentRunCommand = (path, description) => createContextCommand(path, description, async (args, ctx) => {
113
- const { runAgentRunCommand } = await import("./agentRunCommand-JASIG3FZ.js");
113
+ const { runAgentRunCommand } = await import("./agentRunCommand-LTVIZBDD.js");
114
114
  return runAgentRunCommand(args, {
115
115
  env: ctx.env,
116
116
  scriptDir: ctx.scriptDir,
package/index.js CHANGED
@@ -71,7 +71,7 @@ async function runScript(script, forwardedArgs, env) {
71
71
  return proc.exited;
72
72
  }
73
73
  async function launchTuiWorkspace(args2) {
74
- const { startTuiWorkspace } = await import("./readlineWorkspace-UIQN7JUR.js");
74
+ const { startTuiWorkspace } = await import("./readlineWorkspace-XL65W6GB.js");
75
75
  const { createTuiSummaryLlmCaller } = await import("./tuiSummaryLlmCaller-BM3VSTBU.js");
76
76
  const { setCliAutoCompactionSummaryFallback } = await import("./localRuntimeAdapter-B4TAYYA7.js");
77
77
  setCliAutoCompactionSummaryFallback(
@@ -85,7 +85,7 @@ async function launchTuiWorkspace(args2) {
85
85
  });
86
86
  }
87
87
  async function main() {
88
- const { createCliRuntimeContext, looksLikeDaemonShortcut, renderHelpText, resolveCommand, runResolvedCommand } = await import("./commandRegistry-NBLZEMCW.js");
88
+ const { createCliRuntimeContext, looksLikeDaemonShortcut, renderHelpText, resolveCommand, runResolvedCommand } = await import("./commandRegistry-WIUTNXKC.js");
89
89
  const { buildCliRuntimeEnv, loadProfileConfig } = await import("./profileConfig-ZGOFORWV.js");
90
90
  const { resolveTuiLaunchMode } = await import("./runtimeModeArgs-2Y7BALTO.js");
91
91
  const runtimeEnv = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nolo-cli",
3
- "version": "0.151.0-alpha.3",
3
+ "version": "0.151.0-alpha.4",
4
4
  "type": "module",
5
5
  "description": "Agent-first terminal workspace for Nolo",
6
6
  "bin": {
@@ -44,6 +44,7 @@ import {
44
44
  fitAnsiLine,
45
45
  formatAssistantDisplay,
46
46
  formatCreditsChip,
47
+ formatTokenCount,
47
48
  getActiveThemeMode,
48
49
  getActiveThemeName,
49
50
  historyTokensBeforeLatestAssistant,
@@ -65,6 +66,7 @@ import {
65
66
  resolveAuthorityTurnCount,
66
67
  resolveCliColorEnabled,
67
68
  resolveCliMemory,
69
+ resolveContextUsage,
68
70
  resolveSkillReference,
69
71
  resolveTerminalProgramStatusState,
70
72
  resolveTuiBrightnessSignal,
@@ -89,7 +91,7 @@ import {
89
91
  wrapTextToLines,
90
92
  wrapTranscriptLine,
91
93
  wrapTranscriptLineWithLayout
92
- } from "./chunk-TTH4HOJD.js";
94
+ } from "./chunk-OXCHKYMU.js";
93
95
  import "./chunk-6QDYSCPW.js";
94
96
  import "./chunk-IK2TSJQA.js";
95
97
  import {
@@ -7833,6 +7835,16 @@ function formatCwd(cwd) {
7833
7835
  const parts = cwd.split(/[/\\]/);
7834
7836
  return parts.pop() || cwd;
7835
7837
  }
7838
+ function truncateToWidth(text, maxWidth) {
7839
+ if (displayWidth(text) <= maxWidth) return text;
7840
+ if (maxWidth < 2) return "";
7841
+ let out = "";
7842
+ for (const ch of text) {
7843
+ if (displayWidth(`${out}${ch}\u2026`) > maxWidth) break;
7844
+ out += ch;
7845
+ }
7846
+ return out ? `${out}\u2026` : "";
7847
+ }
7836
7848
  function formatElapsedSeconds(totalSeconds) {
7837
7849
  if (totalSeconds < 60) return `${totalSeconds}s`;
7838
7850
  const minutes = Math.floor(totalSeconds / 60);
@@ -7842,6 +7854,21 @@ function formatElapsedSeconds(totalSeconds) {
7842
7854
  const mins = minutes % 60;
7843
7855
  return `${hours}h ${mins}m`;
7844
7856
  }
7857
+ function renderStatusContextSegment(state, compact) {
7858
+ const usage = resolveContextUsage({
7859
+ tokens: state.turnTokens,
7860
+ fallbackContextWindow: state.contextWindow,
7861
+ estimatedContextTokens: state.estimatedContextTokens,
7862
+ turnEstimatedTokens: state.liveTurnEstimatedTokens
7863
+ });
7864
+ if (usage.used === void 0 || usage.contextWindow === void 0) {
7865
+ return "ctx \u2014";
7866
+ }
7867
+ const pct = Math.min(100, usage.pct ?? 0);
7868
+ const pctText = pct > 0 && pct < 10 ? pct.toFixed(1) : Math.round(pct).toString();
7869
+ if (compact) return `ctx ${pctText}%`;
7870
+ return `ctx ${pctText}% \xB7 ${formatTokenCount(usage.used)}/${formatTokenCount(usage.contextWindow)}`;
7871
+ }
7845
7872
  function resolveStatusLineCredits(state) {
7846
7873
  const base = state.dialogCreditsBase;
7847
7874
  const session = state.sessionCredits;
@@ -7867,57 +7894,141 @@ function renderStatusLine(state, maxWidth) {
7867
7894
  const untrackedText = untracked > 0 ? ` ${themeText(`?${untracked}`, "muted", colorEnabled)}` : "";
7868
7895
  parts.push(`${branchText}${modifiedText}${untrackedText}`);
7869
7896
  }
7897
+ const contextFull = renderStatusContextSegment(state, false);
7898
+ const contextCompact = renderStatusContextSegment(state, true);
7899
+ let contextPart = themeText(contextFull, "muted", colorEnabled);
7900
+ parts.push(contextPart);
7870
7901
  const credits = resolveStatusLineCredits(state);
7871
7902
  const creditsSegment = credits !== void 0 && credits > 0 ? themeText(formatCreditsChip(credits), "muted", colorEnabled) : "";
7872
7903
  if (creditsSegment) parts.push(creditsSegment);
7873
7904
  const pendingProcessCount = state.pendingProcessNotices?.length ?? 0;
7874
7905
  if (pendingProcessCount > 0) {
7875
- parts.push(themeText(`\u2699 ${pendingProcessCount} finished`, "info", colorEnabled));
7906
+ parts.push(themeText(`\u21AA ${pendingProcessCount} pending`, "info", colorEnabled));
7876
7907
  }
7877
7908
  if (state.autoConfirm === true) {
7878
7909
  parts.push(themeText("\u23F5 auto", "warning", colorEnabled));
7879
7910
  }
7911
+ let surface = colorEnabled ? surfaceBackgroundSequence() : "";
7912
+ let padWidth = surface ? 2 : 0;
7913
+ if (maxWidth && maxWidth > 0 && maxWidth <= padWidth) {
7914
+ surface = "";
7915
+ padWidth = 0;
7916
+ }
7917
+ let agentPart = agentSegment;
7918
+ let creditsPart = creditsSegment;
7880
7919
  let visibleParts = parts;
7881
7920
  if (maxWidth && maxWidth > 0) {
7882
- const widthOf = (segments) => displayWidth(stripAnsi(segments.join(" \xB7 "))) + 2;
7883
- for (const optional of [cwdSegment, agentSegment, creditsSegment]) {
7884
- if (!optional) continue;
7885
- if (widthOf(visibleParts) <= maxWidth) break;
7886
- visibleParts = visibleParts.filter((part) => part !== optional);
7921
+ const widthOf = (segments) => displayWidth(stripAnsi(segments.join(" \xB7 "))) + padWidth;
7922
+ if (contextFull !== contextCompact && widthOf(visibleParts) > maxWidth) {
7923
+ const contextIndex = visibleParts.indexOf(contextPart);
7924
+ if (contextIndex >= 0) {
7925
+ contextPart = themeText(contextCompact, "muted", colorEnabled);
7926
+ visibleParts = visibleParts.with(contextIndex, contextPart);
7927
+ }
7928
+ }
7929
+ if (widthOf(visibleParts) > maxWidth) {
7930
+ visibleParts = visibleParts.filter((part) => part !== cwdSegment);
7931
+ }
7932
+ if (widthOf(visibleParts) > maxWidth) {
7933
+ const agentIndex = visibleParts.indexOf(agentPart);
7934
+ if (agentIndex >= 0) {
7935
+ const othersWidth = widthOf(visibleParts.filter((part) => part !== agentPart));
7936
+ const nameBudget = maxWidth - othersWidth - 3 - displayWidth("\u2726 ") - displayWidth(modeSuffix);
7937
+ const shortName = truncateToWidth(state.agentName, nameBudget);
7938
+ if (shortName) {
7939
+ agentPart = themeText(`\u2726 ${shortName}${modeSuffix}`, "accent", colorEnabled);
7940
+ visibleParts = visibleParts.with(agentIndex, agentPart);
7941
+ }
7942
+ }
7943
+ }
7944
+ if (widthOf(visibleParts) > maxWidth) {
7945
+ visibleParts = visibleParts.filter((part) => part !== agentPart);
7946
+ }
7947
+ if (widthOf(visibleParts) > maxWidth) {
7948
+ visibleParts = visibleParts.filter((part) => part !== contextPart);
7949
+ }
7950
+ if (widthOf(visibleParts) > maxWidth && creditsPart) {
7951
+ const creditsIndex = visibleParts.indexOf(creditsPart);
7952
+ if (creditsIndex >= 0) {
7953
+ creditsPart = themeText(
7954
+ formatCreditsChip(credits ?? 0, { compact: true }),
7955
+ "muted",
7956
+ colorEnabled
7957
+ );
7958
+ visibleParts = visibleParts.with(creditsIndex, creditsPart);
7959
+ }
7887
7960
  }
7888
7961
  if (widthOf(visibleParts) > maxWidth && state.gitStatus) {
7889
7962
  const gitIndex = visibleParts.findIndex((part) => stripAnsi(part).startsWith("\u2442 "));
7890
7963
  if (gitIndex >= 0) {
7891
- const { modified, untracked } = state.gitStatus;
7892
- const compactDirty = themeText(
7893
- `\u2442${modified > 0 ? ` *${modified}` : ""}${untracked > 0 ? ` ?${untracked}` : ""}`,
7894
- "warning",
7895
- colorEnabled
7964
+ const { branch, modified, untracked } = state.gitStatus;
7965
+ const dirtyText = `${modified > 0 ? ` *${modified}` : ""}${untracked > 0 ? ` ?${untracked}` : ""}`;
7966
+ const othersWidth = widthOf(
7967
+ visibleParts.filter((part) => part !== visibleParts[gitIndex])
7896
7968
  );
7897
- visibleParts = visibleParts.with(gitIndex, compactDirty);
7969
+ const branchBudget = maxWidth - othersWidth - 3 - displayWidth("\u2442 ") - displayWidth(dirtyText);
7970
+ const shortBranch = truncateToWidth(branch, branchBudget);
7971
+ if (shortBranch) {
7972
+ const branchText = themeText(`\u2442 ${shortBranch}`, "warning", colorEnabled);
7973
+ const modifiedText = modified > 0 ? ` ${themeText(`*${modified}`, "danger", colorEnabled)}` : "";
7974
+ const untrackedText = untracked > 0 ? ` ${themeText(`?${untracked}`, "muted", colorEnabled)}` : "";
7975
+ visibleParts = visibleParts.with(
7976
+ gitIndex,
7977
+ `${branchText}${modifiedText}${untrackedText}`
7978
+ );
7979
+ } else {
7980
+ const compactDirty = themeText(`\u2442${dirtyText}`, "warning", colorEnabled);
7981
+ visibleParts = visibleParts.with(gitIndex, compactDirty);
7982
+ }
7898
7983
  }
7899
7984
  }
7900
7985
  if (widthOf(visibleParts) > maxWidth) {
7901
- const emergency = [];
7902
- if (state.autoConfirm === true) {
7903
- emergency.push(themeText("\u23F5", "warning", colorEnabled));
7904
- }
7905
- if (state.gitStatus) {
7906
- const { modified, untracked } = state.gitStatus;
7907
- emergency.push(themeText(
7908
- `\u2442${modified > 0 ? `*${modified}` : ""}${untracked > 0 ? `?${untracked}` : ""}`,
7909
- "warning",
7910
- colorEnabled
7911
- ));
7912
- }
7913
- if (creditsSegment) {
7914
- emergency.push(themeText(formatCreditsChip(credits ?? 0, { compact: true }), "muted", colorEnabled));
7915
- }
7916
- visibleParts = [emergency.join(" ")];
7986
+ const budget = maxWidth - padWidth;
7987
+ const autoMark = state.autoConfirm === true ? themeText("\u23F5", "warning", colorEnabled) : "";
7988
+ const gitCounts = state.gitStatus ? themeText(
7989
+ `\u2442${state.gitStatus.modified > 0 ? `*${state.gitStatus.modified}` : ""}${state.gitStatus.untracked > 0 ? `?${state.gitStatus.untracked}` : ""}`,
7990
+ "warning",
7991
+ colorEnabled
7992
+ ) : "";
7993
+ const gitMark = state.gitStatus ? themeText(
7994
+ `\u2442${state.gitStatus.modified > 0 ? "*" : ""}${state.gitStatus.untracked > 0 ? "?" : ""}`,
7995
+ "warning",
7996
+ colorEnabled
7997
+ ) : "";
7998
+ const noticeFull = pendingProcessCount > 0 ? themeText(`\u21AA${pendingProcessCount}`, "info", colorEnabled) : "";
7999
+ const noticeMark = pendingProcessCount > 0 ? themeText("\u21AA", "info", colorEnabled) : "";
8000
+ const creditsFull = creditsSegment ? themeText(
8001
+ formatCreditsChip(credits ?? 0, { compact: true }),
8002
+ "muted",
8003
+ colorEnabled
8004
+ ) : "";
8005
+ const creditsMark = creditsSegment ? themeText("\u26A1\u2026", "muted", colorEnabled) : "";
8006
+ const stages = [
8007
+ [autoMark, gitCounts, noticeFull, creditsFull],
8008
+ [autoMark, gitMark, noticeFull, creditsFull],
8009
+ [autoMark, gitMark, noticeMark, creditsFull],
8010
+ [autoMark, noticeMark, creditsFull],
8011
+ [autoMark, creditsFull],
8012
+ [autoMark, creditsMark],
8013
+ // `autoMark` alone is only an honest projection when nothing was omitted.
8014
+ // With a fee on the line, dropping it silently would claim "no cost".
8015
+ ...creditsSegment ? [] : [[autoMark]]
8016
+ ].map((stage) => stage.filter(Boolean)).filter((stage) => stage.length > 0);
8017
+ const fitted = stages.find(
8018
+ (stage) => displayWidth(stripAnsi(stage.join(" "))) <= budget
8019
+ );
8020
+ const autoWidth = displayWidth(stripAnsi(autoMark));
8021
+ const overflowMark = autoMark && autoWidth + displayWidth("\u2026") <= budget ? `${autoMark}\u2026` : budget >= 1 ? "\u2026" : "";
8022
+ visibleParts = fitted ? [fitted.join(" ")] : [overflowMark];
8023
+ }
8024
+ }
8025
+ let body = visibleParts.join(sep);
8026
+ if (maxWidth && maxWidth > 0) {
8027
+ const budget = Math.max(0, maxWidth - padWidth);
8028
+ if (displayWidth(stripAnsi(body)) > budget) {
8029
+ body = budget >= 2 ? truncateToWidth(stripAnsi(body), budget) : budget >= 1 ? "\u2026" : "";
7917
8030
  }
7918
8031
  }
7919
- const body = visibleParts.join(sep);
7920
- const surface = colorEnabled ? surfaceBackgroundSequence() : "";
7921
8032
  if (!surface) return body;
7922
8033
  return `${surface} ${body} \x1B[49m`;
7923
8034
  }
@@ -9316,15 +9427,21 @@ function buildProcessTaskCompletedTurnEvent(notice) {
9316
9427
  };
9317
9428
  }
9318
9429
  var PENDING_NOTICE_PREFIX = "process-notice-v1:";
9319
- function serializePendingProcessNotice(notice) {
9320
- return notice.resultCapsule ? `${PENDING_NOTICE_PREFIX}${JSON.stringify(notice)}` : formatProcessTerminalWakeLine(notice);
9430
+ function serializePendingProcessNotice(notice, ownerDialogId) {
9431
+ const withOwner = { ...notice, ownerDialogId: ownerDialogId ?? null };
9432
+ return `${PENDING_NOTICE_PREFIX}${JSON.stringify(withOwner)}`;
9321
9433
  }
9322
9434
  function parsePendingProcessNoticeLine(line) {
9323
9435
  if (line.startsWith(PENDING_NOTICE_PREFIX)) {
9324
9436
  try {
9325
9437
  const parsed = JSON.parse(line.slice(PENDING_NOTICE_PREFIX.length));
9326
- if (parsed && typeof parsed.taskId === "string" && parsed.resultCapsule) {
9327
- return parsed;
9438
+ if (parsed && typeof parsed.taskId === "string") {
9439
+ const { ownerDialogId, ...rest } = parsed;
9440
+ return {
9441
+ ...rest,
9442
+ // 类型校验:只有非空字符串才是可信归属,其他形状一律降级为未知。
9443
+ ownerDialogId: typeof ownerDialogId === "string" && ownerDialogId.length > 0 ? ownerDialogId : null
9444
+ };
9328
9445
  }
9329
9446
  } catch {
9330
9447
  }
@@ -9338,7 +9455,8 @@ function parsePendingProcessNoticeLine(line) {
9338
9455
  pid: 0,
9339
9456
  label: "",
9340
9457
  command: line,
9341
- status: "failed"
9458
+ status: "failed",
9459
+ ownerDialogId: null
9342
9460
  };
9343
9461
  }
9344
9462
  const [, taskId, rawLabel, status, rawExit] = taskMatch;
@@ -9349,9 +9467,19 @@ function parsePendingProcessNoticeLine(line) {
9349
9467
  label,
9350
9468
  command: "",
9351
9469
  status: normalizeStatus(status),
9470
+ ownerDialogId: null,
9352
9471
  ...rawExit !== void 0 ? { exitCode: Number(rawExit) } : {}
9353
9472
  };
9354
9473
  }
9474
+ function annotatePendingProcessNoticeOrigin(notice, currentDialogId) {
9475
+ const withOrigin = (origin) => ({
9476
+ ...notice,
9477
+ label: notice.label.trim() ? `${origin} ${notice.label}` : origin
9478
+ });
9479
+ if (notice.ownerDialogId == null) return withOrigin("[\u5F52\u5C5E\u672A\u77E5]");
9480
+ if (notice.ownerDialogId === currentDialogId) return notice;
9481
+ return withOrigin(`[\u6765\u81EA\u53E6\u4E00\u5BF9\u8BDD ${notice.ownerDialogId}\uFF0C\u4E0D\u4EE3\u8868\u5F53\u524D\u4EFB\u52A1]`);
9482
+ }
9355
9483
  function unescapeLabel(raw) {
9356
9484
  return raw.replace(/\\(["\\])/g, "$1");
9357
9485
  }
@@ -11629,7 +11757,14 @@ async function runAgentChat(scriptDir, state, message, env, output, agentRunner
11629
11757
  const injectedProcessNoticeLines = state.pendingProcessNotices ? [...state.pendingProcessNotices] : [];
11630
11758
  const processNoticeBlocks = injectedProcessNoticeLines.length > 0 ? [{
11631
11759
  content: buildProcessTerminalTurnMessage(
11632
- injectedProcessNoticeLines.map(parsePendingProcessNoticeLine)
11760
+ injectedProcessNoticeLines.map(
11761
+ (line) => annotatePendingProcessNoticeOrigin(
11762
+ parsePendingProcessNoticeLine(line),
11763
+ // 本函数的 state 即当前 session 状态;首轮尚未回填 dialogId
11764
+ // 时为 undefined(标注侧按「不冒充当前任务」处理)。
11765
+ state.dialogId
11766
+ )
11767
+ )
11633
11768
  ),
11634
11769
  cacheScope: "turn"
11635
11770
  }] : [];
@@ -14873,7 +15008,8 @@ async function runTuiWorkspace(options) {
14873
15008
  const deployWakeLifecycle = { dispose: null };
14874
15009
  const pushPendingProcessNotice = (notice) => {
14875
15010
  if (sessionEnded) return;
14876
- const line = serializePendingProcessNotice(notice);
15011
+ const ownerDialogId = getProcessRegistry().getByTaskId(notice.taskId)?.owner?.dialogId ?? null;
15012
+ const line = serializePendingProcessNotice(notice, ownerDialogId);
14877
15013
  state = {
14878
15014
  ...state,
14879
15015
  pendingProcessNotices: [...state.pendingProcessNotices ?? [], line]