nolo-cli 0.1.47 → 0.1.48

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 (2) hide show
  1. package/index.js +1486 -179
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -1473,7 +1473,9 @@ var init_agentAliases = __esm({
1473
1473
  grok: LOCAL_GROK_AGENT_KEY,
1474
1474
  "grok-agent": LOCAL_GROK_AGENT_KEY,
1475
1475
  "grok cli": LOCAL_GROK_AGENT_KEY,
1476
- "grok-cli": LOCAL_GROK_AGENT_KEY
1476
+ "grok-cli": LOCAL_GROK_AGENT_KEY,
1477
+ "minimax-m3": "agent-0e95801d90-minimax-m3",
1478
+ minimax: "agent-0e95801d90-minimax-m3"
1477
1479
  };
1478
1480
  }
1479
1481
  });
@@ -1602,6 +1604,19 @@ function buildMessages(args2) {
1602
1604
  { role: "user", content: args2.input }
1603
1605
  ];
1604
1606
  }
1607
+ function mergeTurnUsage(current, next) {
1608
+ if (!next) return current;
1609
+ const read2 = (usage2) => ({
1610
+ input: Number(usage2.input_tokens ?? usage2.prompt_tokens ?? 0),
1611
+ output: Number(usage2.output_tokens ?? usage2.completion_tokens ?? 0)
1612
+ });
1613
+ const right = read2(next);
1614
+ const left = current ? read2(current) : { input: 0, output: 0 };
1615
+ return {
1616
+ input_tokens: right.input || left.input,
1617
+ output_tokens: left.output + right.output
1618
+ };
1619
+ }
1605
1620
  function extractUserInputText(content) {
1606
1621
  if (typeof content === "string") return content;
1607
1622
  if (!Array.isArray(content)) return "";
@@ -1627,11 +1642,14 @@ async function runLocalAgentTurn(input2) {
1627
1642
  const userInputText = extractUserInputText(input2.input);
1628
1643
  let toolCallCount = 0;
1629
1644
  let result;
1645
+ let turnUsage;
1630
1646
  let round = 0;
1631
1647
  while (true) {
1632
1648
  result = await provider.complete(messages, {
1633
- ...typeof input2.timeoutMs === "number" ? { timeoutMs: input2.timeoutMs } : {}
1649
+ ...typeof input2.timeoutMs === "number" ? { timeoutMs: input2.timeoutMs } : {},
1650
+ ...input2.onTextDelta ? { onTextDelta: input2.onTextDelta } : {}
1634
1651
  });
1652
+ turnUsage = mergeTurnUsage(turnUsage, result.usage);
1635
1653
  const toolCalls = result.tool_calls ?? [];
1636
1654
  if (toolCalls.length === 0) break;
1637
1655
  toolCallCount += toolCalls.length;
@@ -1724,6 +1742,7 @@ async function runLocalAgentTurn(input2) {
1724
1742
  });
1725
1743
  return {
1726
1744
  ...result,
1745
+ ...turnUsage ? { usage: turnUsage } : {},
1727
1746
  ...toolCallCount > 0 ? { toolCallCount } : {},
1728
1747
  ...agentConfig.toolSurface ? { runtimeToolSurface: agentConfig.toolSurface } : {},
1729
1748
  dialogId: saved.dialogId,
@@ -3359,9 +3378,10 @@ function buildOpenAiCompatibleChatCompletionRequest(args2) {
3359
3378
  const body = {
3360
3379
  model: args2.providerConfig.model,
3361
3380
  messages: toOpenAiCompatibleMessages(args2.messages),
3362
- stream: false,
3381
+ stream: args2.stream ?? false,
3363
3382
  ...args2.providerConfig.requestOptions,
3364
- ...args2.tools && args2.tools.length > 0 ? { tools: args2.tools } : {}
3383
+ ...args2.tools && args2.tools.length > 0 ? { tools: args2.tools } : {},
3384
+ ...args2.stream ? { stream_options: { include_usage: true } } : {}
3365
3385
  };
3366
3386
  return {
3367
3387
  url: args2.providerConfig.endpoint,
@@ -3391,6 +3411,138 @@ function parseOpenAiCompatibleChatCompletionResponse(args2) {
3391
3411
  trace: args2.trace
3392
3412
  };
3393
3413
  }
3414
+ function accumulateToolCallDelta(accumulated, deltas) {
3415
+ for (const delta of deltas) {
3416
+ const index = typeof delta.index === "number" ? delta.index : 0;
3417
+ const current = accumulated[index] ?? {
3418
+ id: "",
3419
+ type: "function",
3420
+ function: { name: "", arguments: "" }
3421
+ };
3422
+ if (typeof delta.id === "string" && delta.id) current.id = delta.id;
3423
+ const fn = delta.function;
3424
+ if (fn && typeof fn === "object") {
3425
+ const functionDelta = fn;
3426
+ if (typeof functionDelta.name === "string" && functionDelta.name) {
3427
+ current.function.name += functionDelta.name;
3428
+ }
3429
+ if (typeof functionDelta.arguments === "string" && functionDelta.arguments) {
3430
+ current.function.arguments += functionDelta.arguments;
3431
+ }
3432
+ }
3433
+ accumulated[index] = current;
3434
+ }
3435
+ }
3436
+ function finalizeAccumulatedToolCalls(accumulated) {
3437
+ return Object.keys(accumulated).map((key2) => accumulated[Number(key2)]).filter((call) => call?.function?.name);
3438
+ }
3439
+ async function readOpenAiCompatibleSseCompletion(args2) {
3440
+ const reader = args2.response.body?.getReader();
3441
+ if (!reader) {
3442
+ throw new Error("OpenAI-compatible stream response did not include a readable body.");
3443
+ }
3444
+ const decoder = new TextDecoder();
3445
+ let buffer = "";
3446
+ let content = "";
3447
+ let reasoning = "";
3448
+ let usage2;
3449
+ const accumulatedToolCalls = {};
3450
+ while (true) {
3451
+ const { done, value } = await reader.read();
3452
+ if (done) break;
3453
+ buffer += decoder.decode(value, { stream: true });
3454
+ while (true) {
3455
+ const boundary = buffer.indexOf("\n\n");
3456
+ if (boundary === -1) break;
3457
+ const event = buffer.slice(0, boundary);
3458
+ buffer = buffer.slice(boundary + 2);
3459
+ for (const line of event.split("\n")) {
3460
+ const trimmed = line.trim();
3461
+ if (!trimmed.startsWith("data:")) continue;
3462
+ const payload = trimmed.slice(5).trim();
3463
+ if (!payload || payload === "[DONE]") continue;
3464
+ let parsed;
3465
+ try {
3466
+ parsed = JSON.parse(payload);
3467
+ } catch {
3468
+ continue;
3469
+ }
3470
+ if (parsed?.usage && typeof parsed.usage === "object") {
3471
+ usage2 = parsed.usage;
3472
+ }
3473
+ const delta = parsed?.choices?.[0]?.delta;
3474
+ if (!delta || typeof delta !== "object") continue;
3475
+ const reasoningChunk = typeof delta.reasoning_content === "string" ? delta.reasoning_content : typeof delta.reasoning === "string" ? delta.reasoning : "";
3476
+ if (reasoningChunk) reasoning += reasoningChunk;
3477
+ if (Array.isArray(delta.tool_calls) && delta.tool_calls.length > 0) {
3478
+ accumulateToolCallDelta(accumulatedToolCalls, delta.tool_calls);
3479
+ }
3480
+ const textChunk = typeof delta.content === "string" ? delta.content : "";
3481
+ if (textChunk) {
3482
+ content += textChunk;
3483
+ args2.onTextDelta?.(textChunk);
3484
+ }
3485
+ }
3486
+ }
3487
+ }
3488
+ const tool_calls = finalizeAccumulatedToolCalls(accumulatedToolCalls);
3489
+ return {
3490
+ content,
3491
+ ...reasoning ? { reasoning_content: reasoning } : {},
3492
+ ...tool_calls.length > 0 ? { tool_calls } : {},
3493
+ ...usage2 ? { usage: usage2 } : {}
3494
+ };
3495
+ }
3496
+ async function executeOpenAiCompatibleChatCompletion(args2) {
3497
+ const request = buildOpenAiCompatibleChatCompletionRequest({
3498
+ providerConfig: args2.providerConfig,
3499
+ messages: args2.messages,
3500
+ tools: args2.tools,
3501
+ stream: args2.stream
3502
+ });
3503
+ const res = await args2.fetchImpl(request.url, {
3504
+ ...request.init,
3505
+ ...args2.signal ? { signal: args2.signal } : {}
3506
+ });
3507
+ if (!res.ok) {
3508
+ const raw2 = await res.text().catch(() => "");
3509
+ let data2 = raw2;
3510
+ try {
3511
+ data2 = JSON.parse(raw2);
3512
+ } catch {
3513
+ }
3514
+ throw new Error(`local provider failed: HTTP ${res.status} ${JSON.stringify(data2)}`);
3515
+ }
3516
+ const contentType = res.headers.get("content-type") ?? "";
3517
+ const shouldStream = Boolean(args2.stream && args2.onTextDelta) && contentType.includes("text/event-stream");
3518
+ if (shouldStream && args2.onTextDelta) {
3519
+ const streamed = await readOpenAiCompatibleSseCompletion({
3520
+ response: res,
3521
+ onTextDelta: args2.onTextDelta
3522
+ });
3523
+ return {
3524
+ content: streamed.content,
3525
+ model: args2.providerConfig.model,
3526
+ provider: args2.providerConfig.provider,
3527
+ ...streamed.tool_calls ? { tool_calls: streamed.tool_calls } : {},
3528
+ ...streamed.reasoning_content ? { reasoning_content: streamed.reasoning_content } : {},
3529
+ ...streamed.usage ? { usage: streamed.usage } : {},
3530
+ trace: args2.messages
3531
+ };
3532
+ }
3533
+ const raw = await res.text().catch(() => "");
3534
+ let data = {};
3535
+ try {
3536
+ data = raw ? JSON.parse(raw) : {};
3537
+ } catch {
3538
+ data = {};
3539
+ }
3540
+ return parseOpenAiCompatibleChatCompletionResponse({
3541
+ providerConfig: args2.providerConfig,
3542
+ data,
3543
+ trace: args2.messages
3544
+ });
3545
+ }
3394
3546
  var init_openAiCompatibleProvider = __esm({
3395
3547
  "packages/agent-runtime/openAiCompatibleProvider.ts"() {
3396
3548
  "use strict";
@@ -3688,7 +3840,8 @@ function buildPlatformChatCompletionRequest(args2) {
3688
3840
  const body = {
3689
3841
  model: args2.providerConfig.model,
3690
3842
  ...usesResponsesApi ? { input: convertMessagesToResponsesInput(args2.messages) } : { messages: toOpenAiCompatibleMessages2(args2.messages) },
3691
- stream: false,
3843
+ stream: args2.stream ?? false,
3844
+ ...args2.stream ? { stream_options: { include_usage: true } } : {},
3692
3845
  ...requestOptions,
3693
3846
  ...args2.tools && args2.tools.length > 0 ? {
3694
3847
  tools: usesResponsesApi ? toResponsesTools(args2.tools) : args2.tools,
@@ -10118,6 +10271,8 @@ var init_ai_locale = __esm({
10118
10271
  creating: "Creating...",
10119
10272
  edit: "Edit",
10120
10273
  delete: "Delete",
10274
+ favorite: "Favorite",
10275
+ unfavorite: "Unfavorite",
10121
10276
  confirmDelete: "Confirm Delete",
10122
10277
  resetToDefaults: "Reset to Defaults",
10123
10278
  loading: "Loading...",
@@ -10429,6 +10584,8 @@ var init_ai_locale = __esm({
10429
10584
  creating: "\u521B\u5EFA\u4E2D...",
10430
10585
  edit: "\u7F16\u8F91",
10431
10586
  delete: "\u5220\u9664",
10587
+ favorite: "\u6536\u85CF",
10588
+ unfavorite: "\u53D6\u6D88\u6536\u85CF",
10432
10589
  confirmDelete: "\u786E\u8BA4\u5220\u9664",
10433
10590
  resetToDefaults: "\u91CD\u7F6E\u4E3A\u9ED8\u8BA4\u503C",
10434
10591
  loading: "\u52A0\u8F7D\u4E2D...",
@@ -10741,6 +10898,8 @@ var init_ai_locale = __esm({
10741
10898
  creating: "\u5EFA\u7ACB\u4E2D...",
10742
10899
  edit: "\u7DE8\u8F2F",
10743
10900
  delete: "\u522A\u9664",
10901
+ favorite: "\u6536\u85CF",
10902
+ unfavorite: "\u53D6\u6D88\u6536\u85CF",
10744
10903
  confirmDelete: "\u78BA\u8A8D\u522A\u9664",
10745
10904
  resetToDefaults: "\u91CD\u8A2D\u70BA\u9810\u8A2D\u503C",
10746
10905
  loading: "\u8F09\u5165\u4E2D...",
@@ -10976,6 +11135,8 @@ var init_ai_locale = __esm({
10976
11135
  creating: "\u4F5C\u6210\u4E2D...",
10977
11136
  edit: "\u7DE8\u96C6",
10978
11137
  delete: "\u524A\u9664",
11138
+ favorite: "\u304A\u6C17\u306B\u5165\u308A",
11139
+ unfavorite: "\u304A\u6C17\u306B\u5165\u308A\u89E3\u9664",
10979
11140
  confirmDelete: "\u524A\u9664\u306E\u78BA\u8A8D",
10980
11141
  resetToDefaults: "\u30C7\u30D5\u30A9\u30EB\u30C8\u306B\u30EA\u30BB\u30C3\u30C8",
10981
11142
  loading: "\u8AAD\u307F\u8FBC\u307F\u4E2D...",
@@ -13051,6 +13212,31 @@ var init_interface_locale = __esm({
13051
13212
  stop: "Stop",
13052
13213
  refresh: "Refresh"
13053
13214
  },
13215
+ productivity: {
13216
+ title: "Productivity",
13217
+ default_space: {
13218
+ title: "Default startup space",
13219
+ description: "Choose the workspace you land in after signing in to get into flow faster.",
13220
+ all: "All (no fixed default space)"
13221
+ },
13222
+ shortcuts: {
13223
+ title: "Keyboard shortcuts",
13224
+ description: "View and customize shortcuts for common actions to keep your workflow smooth.",
13225
+ sendMessage: "Send message",
13226
+ newChat: "New chat",
13227
+ deleteDialog: "Delete current conversation",
13228
+ clickToRecord: "Click to change shortcut",
13229
+ updated: "Shortcut updated",
13230
+ pressKeys: "Press shortcut keys...",
13231
+ none: "Disabled",
13232
+ cleared: "Shortcut disabled",
13233
+ clearError: "Failed to disable shortcut",
13234
+ clear: "Disable shortcut",
13235
+ resetSuccess: "Shortcut reset",
13236
+ resetError: "Failed to reset shortcut",
13237
+ reset: "Reset to default"
13238
+ }
13239
+ },
13054
13240
  appearance: {
13055
13241
  title: "Appearance",
13056
13242
  theme: {
@@ -13744,6 +13930,31 @@ var init_interface_locale = __esm({
13744
13930
  productivity: "\u6548\u7387",
13745
13931
  secrets: "\u5BC6\u94A5"
13746
13932
  },
13933
+ productivity: {
13934
+ title: "\u6548\u7387\u8BBE\u7F6E",
13935
+ default_space: {
13936
+ title: "\u9ED8\u8BA4\u542F\u52A8\u7A7A\u95F4",
13937
+ description: "\u9009\u62E9\u4F60\u767B\u5F55\u540E\u9ED8\u8BA4\u8FDB\u5165\u7684\u5DE5\u4F5C\u7A7A\u95F4\u3002\u8FD9\u80FD\u5E2E\u52A9\u4F60\u5FEB\u901F\u8FDB\u5165\u5DE5\u4F5C\u72B6\u6001\u3002",
13938
+ all: "\u5168\u90E8\uFF08\u4E0D\u56FA\u5B9A\u9ED8\u8BA4\u7A7A\u95F4\uFF09"
13939
+ },
13940
+ shortcuts: {
13941
+ title: "\u952E\u76D8\u5FEB\u6377\u952E",
13942
+ description: "\u67E5\u770B\u5E76\u7BA1\u7406\u5E38\u7528\u64CD\u4F5C\u7684\u5FEB\u6377\u952E\u914D\u7F6E\uFF0C\u8BA9\u4F60\u7684\u5DE5\u4F5C\u6D41\u66F4\u52A0\u987A\u7545\u3002",
13943
+ sendMessage: "\u53D1\u9001\u6D88\u606F",
13944
+ newChat: "\u65B0\u5EFA\u5BF9\u8BDD",
13945
+ deleteDialog: "\u5220\u9664\u5F53\u524D\u4F1A\u8BDD",
13946
+ clickToRecord: "\u70B9\u51FB\u4FEE\u6539\u5FEB\u6377\u952E",
13947
+ updated: "\u5FEB\u6377\u952E\u5DF2\u66F4\u65B0",
13948
+ pressKeys: "\u6309\u4E0B\u5FEB\u6377\u952E...",
13949
+ none: "\u5DF2\u7981\u7528",
13950
+ cleared: "\u5FEB\u6377\u952E\u5DF2\u7981\u7528",
13951
+ clearError: "\u7981\u7528\u5931\u8D25",
13952
+ clear: "\u7981\u7528\u5FEB\u6377\u952E",
13953
+ resetSuccess: "\u5FEB\u6377\u952E\u5DF2\u91CD\u7F6E",
13954
+ resetError: "\u91CD\u7F6E\u5931\u8D25",
13955
+ reset: "\u91CD\u7F6E\u4E3A\u9ED8\u8BA4"
13956
+ }
13957
+ },
13747
13958
  appearance: {
13748
13959
  title: "\u5916\u89C2",
13749
13960
  theme: {
@@ -14438,6 +14649,31 @@ var init_interface_locale = __esm({
14438
14649
  productivity: "\u6548\u7387",
14439
14650
  secrets: "\u5BC6\u9470"
14440
14651
  },
14652
+ productivity: {
14653
+ title: "\u6548\u7387\u8A2D\u5B9A",
14654
+ default_space: {
14655
+ title: "\u9810\u8A2D\u555F\u52D5\u7A7A\u9593",
14656
+ description: "\u9078\u64C7\u4F60\u767B\u5165\u5F8C\u9810\u8A2D\u9032\u5165\u7684\u5DE5\u4F5C\u7A7A\u9593\uFF0C\u5E6B\u52A9\u4F60\u66F4\u5FEB\u9032\u5165\u5DE5\u4F5C\u72C0\u614B\u3002",
14657
+ all: "\u5168\u90E8\uFF08\u4E0D\u56FA\u5B9A\u9810\u8A2D\u7A7A\u9593\uFF09"
14658
+ },
14659
+ shortcuts: {
14660
+ title: "\u9375\u76E4\u5FEB\u6377\u9375",
14661
+ description: "\u67E5\u770B\u4E26\u7BA1\u7406\u5E38\u7528\u64CD\u4F5C\u7684\u5FEB\u6377\u9375\u8A2D\u5B9A\uFF0C\u8B93\u4F60\u7684\u5DE5\u4F5C\u6D41\u66F4\u52A0\u9806\u66A2\u3002",
14662
+ sendMessage: "\u50B3\u9001\u8A0A\u606F",
14663
+ newChat: "\u65B0\u5EFA\u5C0D\u8A71",
14664
+ deleteDialog: "\u522A\u9664\u76EE\u524D\u6703\u8A71",
14665
+ clickToRecord: "\u9EDE\u64CA\u4FEE\u6539\u5FEB\u6377\u9375",
14666
+ updated: "\u5FEB\u6377\u9375\u5DF2\u66F4\u65B0",
14667
+ pressKeys: "\u6309\u4E0B\u5FEB\u6377\u9375...",
14668
+ none: "\u5DF2\u505C\u7528",
14669
+ cleared: "\u5FEB\u6377\u9375\u5DF2\u505C\u7528",
14670
+ clearError: "\u505C\u7528\u5931\u6557",
14671
+ clear: "\u505C\u7528\u5FEB\u6377\u9375",
14672
+ resetSuccess: "\u5FEB\u6377\u9375\u5DF2\u91CD\u8A2D",
14673
+ resetError: "\u91CD\u8A2D\u5931\u6557",
14674
+ reset: "\u91CD\u8A2D\u70BA\u9810\u8A2D"
14675
+ }
14676
+ },
14441
14677
  appearance: {
14442
14678
  title: "\u5916\u89C0",
14443
14679
  theme: {
@@ -15130,6 +15366,31 @@ var init_interface_locale = __esm({
15130
15366
  productivity: "\u52B9\u7387",
15131
15367
  secrets: "\u30B7\u30FC\u30AF\u30EC\u30C3\u30C8"
15132
15368
  },
15369
+ productivity: {
15370
+ title: "\u52B9\u7387\u8A2D\u5B9A",
15371
+ default_space: {
15372
+ title: "\u65E2\u5B9A\u306E\u8D77\u52D5\u30B9\u30DA\u30FC\u30B9",
15373
+ description: "\u30ED\u30B0\u30A4\u30F3\u5F8C\u306B\u6700\u521D\u306B\u958B\u304F\u30EF\u30FC\u30AF\u30B9\u30DA\u30FC\u30B9\u3092\u9078\u3073\u3001\u3059\u3050\u4F5C\u696D\u306B\u5165\u308C\u308B\u3088\u3046\u306B\u3057\u307E\u3059\u3002",
15374
+ all: "\u3059\u3079\u3066\uFF08\u65E2\u5B9A\u30B9\u30DA\u30FC\u30B9\u3092\u56FA\u5B9A\u3057\u306A\u3044\uFF09"
15375
+ },
15376
+ shortcuts: {
15377
+ title: "\u30AD\u30FC\u30DC\u30FC\u30C9\u30B7\u30E7\u30FC\u30C8\u30AB\u30C3\u30C8",
15378
+ description: "\u3088\u304F\u4F7F\u3046\u64CD\u4F5C\u306E\u30B7\u30E7\u30FC\u30C8\u30AB\u30C3\u30C8\u3092\u78BA\u8A8D\u30FB\u5909\u66F4\u3057\u3066\u3001\u4F5C\u696D\u306E\u6D41\u308C\u3092\u30B9\u30E0\u30FC\u30BA\u306B\u3057\u307E\u3059\u3002",
15379
+ sendMessage: "\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u9001\u4FE1",
15380
+ newChat: "\u65B0\u3057\u3044\u30C1\u30E3\u30C3\u30C8",
15381
+ deleteDialog: "\u73FE\u5728\u306E\u4F1A\u8A71\u3092\u524A\u9664",
15382
+ clickToRecord: "\u30AF\u30EA\u30C3\u30AF\u3057\u3066\u30B7\u30E7\u30FC\u30C8\u30AB\u30C3\u30C8\u3092\u5909\u66F4",
15383
+ updated: "\u30B7\u30E7\u30FC\u30C8\u30AB\u30C3\u30C8\u3092\u66F4\u65B0\u3057\u307E\u3057\u305F",
15384
+ pressKeys: "\u30B7\u30E7\u30FC\u30C8\u30AB\u30C3\u30C8\u30AD\u30FC\u3092\u62BC\u3057\u3066\u304F\u3060\u3055\u3044...",
15385
+ none: "\u7121\u52B9",
15386
+ cleared: "\u30B7\u30E7\u30FC\u30C8\u30AB\u30C3\u30C8\u3092\u7121\u52B9\u306B\u3057\u307E\u3057\u305F",
15387
+ clearError: "\u7121\u52B9\u5316\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
15388
+ clear: "\u30B7\u30E7\u30FC\u30C8\u30AB\u30C3\u30C8\u3092\u7121\u52B9\u5316",
15389
+ resetSuccess: "\u30B7\u30E7\u30FC\u30C8\u30AB\u30C3\u30C8\u3092\u30EA\u30BB\u30C3\u30C8\u3057\u307E\u3057\u305F",
15390
+ resetError: "\u30EA\u30BB\u30C3\u30C8\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
15391
+ reset: "\u65E2\u5B9A\u306B\u623B\u3059"
15392
+ }
15393
+ },
15133
15394
  appearance: {
15134
15395
  title: "\u5916\u89B3",
15135
15396
  theme: {
@@ -20768,6 +21029,7 @@ __export(settingSlice_exports, {
20768
21029
  selectDefaultAgentId: () => selectDefaultAgentId,
20769
21030
  selectDefaultAgentPreference: () => selectDefaultAgentPreference,
20770
21031
  selectDefaultSpaceId: () => selectDefaultSpaceId,
21032
+ selectDeleteShortcut: () => selectDeleteShortcut,
20771
21033
  selectDensity: () => selectDensity,
20772
21034
  selectDesktopChromeConnectorEnabled: () => selectDesktopChromeConnectorEnabled,
20773
21035
  selectEditorAutoSave: () => selectEditorAutoSave,
@@ -20836,7 +21098,7 @@ import {
20836
21098
  asyncThunkCreator,
20837
21099
  createSelector as createSelector2
20838
21100
  } from "@reduxjs/toolkit";
20839
- var SYSTEM_DEFAULT_AGENT_ID, initialState, createSliceWithThunks, hasOwn, normalizeSidebarVisibleTypesSetting, normalizeDefaultAgentIdSetting, normalizeAuthorityHomeServerSetting, normalizeTonePresetSetting, normalizePolicyLevelSetting, resolveDefaultAgentIdSetting, selectResolvedDefaultAgentId, hexToRgbString, alphaColor, getCachedDefaultAgentRegisterRecord, persistDefaultAgentRegister, omitKeys, normalizeSettingChanges, LOCAL_FIRST_APPEARANCE_KEYS, LOCAL_ONLY_SETTINGS_KEYS, isLocalFirstAppearanceChange, stripRegisterBackedFieldsFromSettingsWrite, sanitizeStoredSettingsRecord, hydrateStoredSettings, buildSettingsPersistencePlan, settingSlice, getSettings, setSettings, clearDefaultSpaceId, addHostToCurrentServer, changeTheme, changeDensity, changeFontPreset, changeDarkMode, toggleShowThinking, setThemeFollowsSystem, setSidebarWidth, toggleEnableReadCurrentSpace, setEditorDefaultMode, setEditorLightCodeTheme, setEditorDarkCodeTheme, setEditorCodeTheme, toggleEditorWordCount, toggleEditorShortcut, setEditorFontSize, toggleEditorAutoSave, setEditorAutoSaveInterval, setGlobalPrompt, setUserTonePreset, setKnowledgeCaptureLevel, setSpaceContextLevel, setAiRecentContentLimit, setContextRetention, setMaxExecutionTime, setDefaultAgentId, setPreferredAnimationSet, setThemeMode, selectSettings, isLocalServerUrl2, resolveDesktopSafeServer, selectCurrentServer, selectSyncServers, selectRemoteServer, selectRemoteSyncServers, selectRemoteServers, selectDefaultSpaceId, selectPreferredAnimationSet, selectShowThinking, selectMaxCost, selectMaxExecutionTime, selectIsDark, selectThemeMode, selectHeaderHeight, selectThemeName, selectThemeFollowsSystem, selectSidebarWidth, selectDensity, selectFontPreset, selectEnableReadCurrentSpace, selectSidebarVisibleTypes, selectGlobalPrompt, selectUserTonePreset, selectKnowledgeCaptureLevel, selectSpaceContextLevel, selectAutoApproveSelfUpdateFields, selectAiRecentContentLimit, selectContextRetention, selectDefaultAgentPreference, selectDefaultAgentId, selectOcrModel, selectShowScrollToTopButton, selectShowScrollToBottomButton, selectCreateMenuOpenCount, selectDesktopChromeConnectorEnabled, selectEditorDefaultMode, selectEditorLightCodeTheme, selectEditorDarkCodeTheme, selectEditorCodeTheme, selectEditorWordCountEnabled, selectEditorShortcuts, selectEditorFontSize, selectEditorAutoSave, selectEditorAutoSaveInterval, selectTheme, selectEditorConfig, settingSlice_default;
21101
+ var SYSTEM_DEFAULT_AGENT_ID, initialState, createSliceWithThunks, hasOwn, normalizeSidebarVisibleTypesSetting, normalizeDefaultAgentIdSetting, normalizeAuthorityHomeServerSetting, normalizeTonePresetSetting, normalizePolicyLevelSetting, resolveDefaultAgentIdSetting, selectResolvedDefaultAgentId, hexToRgbString, alphaColor, getCachedDefaultAgentRegisterRecord, persistDefaultAgentRegister, omitKeys, normalizeSettingChanges, LOCAL_FIRST_APPEARANCE_KEYS, LOCAL_ONLY_SETTINGS_KEYS, isLocalFirstAppearanceChange, stripRegisterBackedFieldsFromSettingsWrite, sanitizeStoredSettingsRecord, hydrateStoredSettings, buildSettingsPersistencePlan, settingSlice, getSettings, setSettings, clearDefaultSpaceId, addHostToCurrentServer, changeTheme, changeDensity, changeFontPreset, changeDarkMode, toggleShowThinking, setThemeFollowsSystem, setSidebarWidth, toggleEnableReadCurrentSpace, setEditorDefaultMode, setEditorLightCodeTheme, setEditorDarkCodeTheme, setEditorCodeTheme, toggleEditorWordCount, toggleEditorShortcut, setEditorFontSize, toggleEditorAutoSave, setEditorAutoSaveInterval, setGlobalPrompt, setUserTonePreset, setKnowledgeCaptureLevel, setSpaceContextLevel, setAiRecentContentLimit, setContextRetention, setMaxExecutionTime, setDefaultAgentId, setPreferredAnimationSet, setThemeMode, selectSettings, isLocalServerUrl2, resolveDesktopSafeServer, selectCurrentServer, selectSyncServers, selectRemoteServer, selectRemoteSyncServers, selectRemoteServers, selectDefaultSpaceId, selectPreferredAnimationSet, selectShowThinking, selectMaxCost, selectMaxExecutionTime, selectIsDark, selectThemeMode, selectHeaderHeight, selectThemeName, selectThemeFollowsSystem, selectSidebarWidth, selectDensity, selectFontPreset, selectEnableReadCurrentSpace, selectSidebarVisibleTypes, selectGlobalPrompt, selectUserTonePreset, selectKnowledgeCaptureLevel, selectSpaceContextLevel, selectAutoApproveSelfUpdateFields, selectAiRecentContentLimit, selectContextRetention, selectDefaultAgentPreference, selectDefaultAgentId, selectOcrModel, selectShowScrollToTopButton, selectShowScrollToBottomButton, selectCreateMenuOpenCount, selectDesktopChromeConnectorEnabled, selectEditorDefaultMode, selectEditorLightCodeTheme, selectEditorDarkCodeTheme, selectEditorCodeTheme, selectEditorWordCountEnabled, selectEditorShortcuts, selectDeleteShortcut, selectEditorFontSize, selectEditorAutoSave, selectEditorAutoSaveInterval, selectTheme, selectEditorConfig, settingSlice_default;
20840
21102
  var init_settingSlice = __esm({
20841
21103
  "packages/app/settings/settingSlice.tsx"() {
20842
21104
  "use strict";
@@ -20909,7 +21171,8 @@ var init_settingSlice = __esm({
20909
21171
  showScrollToTopButton: false,
20910
21172
  showScrollToBottomButton: false,
20911
21173
  createMenuOpenCount: 0,
20912
- desktopChromeConnectorEnabled: false
21174
+ desktopChromeConnectorEnabled: false,
21175
+ deleteShortcut: typeof window !== "undefined" && typeof window.navigator !== "undefined" && /Mac|iPod|iPhone|iPad/.test(window.navigator.platform) ? "meta+backspace" : "ctrl+backspace"
20913
21176
  };
20914
21177
  createSliceWithThunks = buildCreateSlice({
20915
21178
  creators: { asyncThunk: asyncThunkCreator }
@@ -21537,6 +21800,7 @@ var init_settingSlice = __esm({
21537
21800
  );
21538
21801
  selectEditorWordCountEnabled = (state) => state.settings.editorWordCountEnabled;
21539
21802
  selectEditorShortcuts = (state) => state.settings.editorShortcuts;
21803
+ selectDeleteShortcut = (state) => state.settings.deleteShortcut;
21540
21804
  selectEditorFontSize = (state) => state.settings.editorFontSize;
21541
21805
  selectEditorAutoSave = (state) => state.settings.editorAutoSave;
21542
21806
  selectEditorAutoSaveInterval = (state) => state.settings.editorAutoSaveInterval;
@@ -63690,14 +63954,6 @@ function summarizeEndpoint(value) {
63690
63954
  return "invalid-url";
63691
63955
  }
63692
63956
  }
63693
- function parseJsonObject2(raw) {
63694
- try {
63695
- const parsed = JSON.parse(raw);
63696
- return parsed && typeof parsed === "object" ? parsed : {};
63697
- } catch {
63698
- return {};
63699
- }
63700
- }
63701
63957
  function isCliProviderAgent(agentConfig) {
63702
63958
  return Boolean(
63703
63959
  agentConfig.apiSource === "cli" || agentConfig.provider === "cli" || agentConfig.cliProvider
@@ -64463,12 +64719,15 @@ function createCliLocalRuntimeAdapter(deps) {
64463
64719
  toolNames: requestedToolNames,
64464
64720
  env: deps.env
64465
64721
  });
64722
+ const timeoutSignal = buildRequestTimeoutSignal(options?.timeoutMs);
64723
+ const usesResponsesApi = providerConfig2.endpoint.includes("/responses");
64724
+ const stream = Boolean(options?.onTextDelta) && !usesResponsesApi;
64466
64725
  const request = buildPlatformChatCompletionRequest({
64467
64726
  providerConfig: providerConfig2,
64468
64727
  messages,
64469
- tools
64728
+ tools,
64729
+ stream
64470
64730
  });
64471
- const timeoutSignal = buildRequestTimeoutSignal(options?.timeoutMs);
64472
64731
  logLocalRuntimeDiagnostic("provider.request.start", {
64473
64732
  agentKey: agentConfig.key,
64474
64733
  transport: "platform-proxy",
@@ -64479,32 +64738,62 @@ function createCliLocalRuntimeAdapter(deps) {
64479
64738
  toolCount: tools.length,
64480
64739
  requestedToolNames,
64481
64740
  openAiToolNames: summarizeOpenAiToolNames(tools),
64482
- timeoutMs: options?.timeoutMs ?? null
64483
- });
64484
- const res = await fetchWithTransientRetry(fetchImpl, request.url, {
64485
- ...request.init,
64486
- ...timeoutSignal ? { signal: timeoutSignal.signal } : {}
64487
- }, {
64488
- sleep: deps.sleep,
64489
- loopbackRequest
64490
- }).finally(() => timeoutSignal?.clear());
64491
- const raw = await res.text().catch(() => "");
64492
- logLocalRuntimeDiagnostic("provider.request.result", {
64493
- agentKey: agentConfig.key,
64494
- transport: "platform-proxy",
64495
- status: res.status,
64496
- ok: res.ok,
64497
- responseBytes: raw.length
64741
+ timeoutMs: options?.timeoutMs ?? null,
64742
+ stream
64498
64743
  });
64499
- const data = parsePlatformChatCompletionData(raw);
64500
- if (!res.ok) {
64501
- throw new Error(`platform provider failed: HTTP ${res.status} ${JSON.stringify(data)}`);
64744
+ try {
64745
+ const res = await fetchWithTransientRetry(fetchImpl, request.url, {
64746
+ ...request.init,
64747
+ ...timeoutSignal ? { signal: timeoutSignal.signal } : {}
64748
+ }, {
64749
+ sleep: deps.sleep,
64750
+ loopbackRequest
64751
+ });
64752
+ if (!res.ok) {
64753
+ const raw2 = await res.text().catch(() => "");
64754
+ const data2 = parsePlatformChatCompletionData(raw2);
64755
+ throw new Error(`platform provider failed: HTTP ${res.status} ${JSON.stringify(data2)}`);
64756
+ }
64757
+ if (stream && options?.onTextDelta) {
64758
+ const streamed = await readOpenAiCompatibleSseCompletion({
64759
+ response: res,
64760
+ onTextDelta: options.onTextDelta
64761
+ });
64762
+ logLocalRuntimeDiagnostic("provider.request.result", {
64763
+ agentKey: agentConfig.key,
64764
+ transport: "platform-proxy",
64765
+ ok: true,
64766
+ stream: true,
64767
+ contentChars: streamed.content.length,
64768
+ toolCallCount: streamed.tool_calls?.length ?? 0
64769
+ });
64770
+ return {
64771
+ content: streamed.content,
64772
+ model: providerConfig2.model,
64773
+ provider: providerConfig2.provider,
64774
+ ...streamed.tool_calls ? { tool_calls: streamed.tool_calls } : {},
64775
+ ...streamed.reasoning_content ? { reasoning_content: streamed.reasoning_content } : {},
64776
+ ...streamed.usage ? { usage: streamed.usage } : {},
64777
+ trace: messages
64778
+ };
64779
+ }
64780
+ const raw = await res.text().catch(() => "");
64781
+ logLocalRuntimeDiagnostic("provider.request.result", {
64782
+ agentKey: agentConfig.key,
64783
+ transport: "platform-proxy",
64784
+ status: res.status,
64785
+ ok: res.ok,
64786
+ responseBytes: raw.length
64787
+ });
64788
+ const data = parsePlatformChatCompletionData(raw);
64789
+ return parsePlatformChatCompletionResponse({
64790
+ providerConfig: providerConfig2,
64791
+ data,
64792
+ trace: messages
64793
+ });
64794
+ } finally {
64795
+ timeoutSignal?.clear();
64502
64796
  }
64503
- return parsePlatformChatCompletionResponse({
64504
- providerConfig: providerConfig2,
64505
- data,
64506
- trace: messages
64507
- });
64508
64797
  }
64509
64798
  };
64510
64799
  }
@@ -64536,47 +64825,45 @@ function createCliLocalRuntimeAdapter(deps) {
64536
64825
  toolNames: requestedToolNames,
64537
64826
  env: deps.env
64538
64827
  });
64539
- const request = buildOpenAiCompatibleChatCompletionRequest({
64540
- providerConfig,
64541
- messages,
64542
- tools
64543
- });
64544
64828
  const timeoutSignal = buildRequestTimeoutSignal(options?.timeoutMs);
64829
+ const stream = Boolean(options?.onTextDelta);
64545
64830
  logLocalRuntimeDiagnostic("provider.request.start", {
64546
64831
  agentKey: agentConfig.key,
64547
64832
  transport: "direct-openai-compatible",
64548
- requestUrl: summarizeEndpoint(request.url) ?? null,
64833
+ requestUrl: summarizeEndpoint(providerConfig.endpoint) ?? null,
64549
64834
  model: providerConfig.model,
64550
64835
  messageCount: messages.length,
64551
64836
  toolCount: tools.length,
64552
64837
  requestedToolNames,
64553
64838
  openAiToolNames: summarizeOpenAiToolNames(tools),
64554
- timeoutMs: options?.timeoutMs ?? null
64839
+ timeoutMs: options?.timeoutMs ?? null,
64840
+ stream
64555
64841
  });
64556
- const res = await fetchWithTransientRetry(fetchImpl, request.url, {
64557
- ...request.init,
64558
- ...timeoutSignal ? { signal: timeoutSignal.signal } : {}
64559
- }, {
64560
- sleep: deps.sleep,
64561
- loopbackRequest
64562
- }).finally(() => timeoutSignal?.clear());
64563
- const raw = await res.text().catch(() => "");
64564
- logLocalRuntimeDiagnostic("provider.request.result", {
64565
- agentKey: agentConfig.key,
64566
- transport: "direct-openai-compatible",
64567
- status: res.status,
64568
- ok: res.ok,
64569
- responseBytes: raw.length
64570
- });
64571
- const data = parseJsonObject2(raw);
64572
- if (!res.ok) {
64573
- throw new Error(`local provider failed: HTTP ${res.status} ${JSON.stringify(data)}`);
64842
+ try {
64843
+ const result = await executeOpenAiCompatibleChatCompletion({
64844
+ providerConfig,
64845
+ messages,
64846
+ tools,
64847
+ fetchImpl: (url, init) => fetchWithTransientRetry(fetchImpl, url, init, {
64848
+ sleep: deps.sleep,
64849
+ loopbackRequest
64850
+ }),
64851
+ stream,
64852
+ onTextDelta: options?.onTextDelta,
64853
+ signal: timeoutSignal?.signal
64854
+ });
64855
+ logLocalRuntimeDiagnostic("provider.request.result", {
64856
+ agentKey: agentConfig.key,
64857
+ transport: "direct-openai-compatible",
64858
+ ok: true,
64859
+ stream,
64860
+ contentChars: result.content.length,
64861
+ toolCallCount: result.tool_calls?.length ?? 0
64862
+ });
64863
+ return result;
64864
+ } finally {
64865
+ timeoutSignal?.clear();
64574
64866
  }
64575
- return parseOpenAiCompatibleChatCompletionResponse({
64576
- providerConfig,
64577
- data,
64578
- trace: messages
64579
- });
64580
64867
  }
64581
64868
  };
64582
64869
  },
@@ -67743,6 +68030,412 @@ function createStreamingTextWriter({
67743
68030
  };
67744
68031
  }
67745
68032
 
68033
+ // packages/cli/client/assistantOutput.ts
68034
+ var ANSI = {
68035
+ reset: "\x1B[0m",
68036
+ bold: "\x1B[1m",
68037
+ dim: "\x1B[2m",
68038
+ cyan: "\x1B[36m"
68039
+ };
68040
+ function normalizeRenderDisplayMode(raw, fallback = "rich") {
68041
+ const normalized = raw?.trim().toLowerCase();
68042
+ if (normalized === "plain" || normalized === "raw" || normalized === "off" || normalized === "0") {
68043
+ return "plain";
68044
+ }
68045
+ if (normalized === "rich" || normalized === "on" || normalized === "1" || normalized === "styled") {
68046
+ return "rich";
68047
+ }
68048
+ return fallback;
68049
+ }
68050
+ function resolveRenderDisplayMode(env = process.env) {
68051
+ return normalizeRenderDisplayMode(env.NOLO_CLI_RENDER ?? env.NOLO_RENDER, "rich");
68052
+ }
68053
+ function splitTableCells(line) {
68054
+ const trimmed = line.trim();
68055
+ if (!trimmed.includes("|")) return [];
68056
+ const core = trimmed.replace(/^\|/, "").replace(/\|$/, "");
68057
+ return core.split("|").map((cell) => cell.trim()).filter(Boolean);
68058
+ }
68059
+ function isTableSeparator(line) {
68060
+ const cells = splitTableCells(line);
68061
+ return cells.length > 0 && cells.every((cell) => /^:?-{3,}:?$/.test(cell));
68062
+ }
68063
+ function isTableRow(line) {
68064
+ const cells = splitTableCells(line);
68065
+ return cells.length >= 2 && cells.some((cell) => cell.length > 0);
68066
+ }
68067
+ function convertMarkdownTablesForTerminal(text) {
68068
+ const lines = text.split("\n");
68069
+ const out = [];
68070
+ for (let index = 0; index < lines.length; index += 1) {
68071
+ const line = lines[index] ?? "";
68072
+ const next = lines[index + 1] ?? "";
68073
+ if (isTableRow(line) && isTableSeparator(next)) {
68074
+ const headers = splitTableCells(line);
68075
+ index += 1;
68076
+ while (index + 1 < lines.length && isTableRow(lines[index + 1] ?? "") && !isTableSeparator(lines[index + 1] ?? "")) {
68077
+ index += 1;
68078
+ const row = splitTableCells(lines[index] ?? "");
68079
+ const label = row[0] ?? "";
68080
+ const detail = row.slice(1).join(" \u2014 ").trim();
68081
+ out.push(detail ? ` \u2022 ${label} \u2014 ${detail}` : ` \u2022 ${label}`);
68082
+ }
68083
+ if (out.length > 0 && out[out.length - 1] !== "") out.push("");
68084
+ continue;
68085
+ }
68086
+ out.push(line);
68087
+ }
68088
+ return out.join("\n");
68089
+ }
68090
+ function polishAssistantStructure(text) {
68091
+ return convertMarkdownTablesForTerminal(text).replace(/\r\n/g, "\n").replace(/([^\n])\n(#{1,3} )/g, "$1\n\n$2").replace(/\n{4,}/g, "\n\n\n").trim();
68092
+ }
68093
+ function styleInlineMarkdown(line, mode) {
68094
+ if (mode === "plain") return line;
68095
+ return line.replace(/\*\*(.+?)\*\*/g, `${ANSI.bold}$1${ANSI.reset}`);
68096
+ }
68097
+ function styleRichMarkdownLine(line) {
68098
+ const heading = line.match(/^(#{1,3})\s+(.+)$/);
68099
+ if (heading) {
68100
+ const level = heading[1].length;
68101
+ const title = heading[2];
68102
+ if (level <= 2) return `${ANSI.bold}${title}${ANSI.reset}`;
68103
+ return `${ANSI.cyan}${title}${ANSI.reset}`;
68104
+ }
68105
+ if (/^---+$/.test(line.trim())) {
68106
+ return `${ANSI.dim}${line}${ANSI.reset}`;
68107
+ }
68108
+ return styleInlineMarkdown(line, "rich");
68109
+ }
68110
+ function formatAssistantDisplay(text, mode = "rich") {
68111
+ const polished = polishAssistantStructure(text);
68112
+ if (mode === "plain") {
68113
+ return polished.split("\n").map((line) => styleInlineMarkdown(line, "plain")).join("\n");
68114
+ }
68115
+ return polished.split("\n").map((line) => styleRichMarkdownLine(line)).join("\n");
68116
+ }
68117
+
68118
+ // packages/cli/client/thinkingOutput.ts
68119
+ var THINK_OPEN = /<think>/i;
68120
+ var THINK_CLOSE = /<\/think>/i;
68121
+ var COLLAPSED_MARKER = /^\s*▸ 思考已折叠\s*$/;
68122
+ function normalizeThinkingDisplayMode(raw, fallback = "hide") {
68123
+ const normalized = raw?.trim().toLowerCase();
68124
+ if (normalized === "hide" || normalized === "off" || normalized === "false" || normalized === "0") {
68125
+ return "hide";
68126
+ }
68127
+ if (normalized === "marker" || normalized === "collapsed" || normalized === "fold") {
68128
+ return "marker";
68129
+ }
68130
+ if (normalized === "show" || normalized === "on" || normalized === "true" || normalized === "1") {
68131
+ return "show";
68132
+ }
68133
+ return fallback;
68134
+ }
68135
+ function resolveThinkingDisplayMode(env = process.env) {
68136
+ return normalizeThinkingDisplayMode(env.NOLO_CLI_THINKING ?? env.NOLO_THINKING, "hide");
68137
+ }
68138
+ function collapseThinkingBlocks(text, mode = "hide") {
68139
+ if (mode === "show") return text;
68140
+ const replacement = mode === "marker" ? "\u25B8 \u601D\u8003\u5DF2\u6298\u53E0\n" : "";
68141
+ return text.replace(/<think>[\s\S]*?<\/think>\s*/gi, replacement).replace(/\n{3,}/g, "\n\n").trim();
68142
+ }
68143
+ function stripCollapsedThinkingMarkers(text) {
68144
+ return text.split("\n").filter((line) => !COLLAPSED_MARKER.test(line)).join("\n").replace(/\n{2,}/g, "\n").trim();
68145
+ }
68146
+ function formatAssistantTextForCli(text, mode = "hide") {
68147
+ const collapsed = collapseThinkingBlocks(text, mode);
68148
+ return mode === "hide" ? stripCollapsedThinkingMarkers(collapsed) : collapsed;
68149
+ }
68150
+ function createThinkingAwareStreamFilter(write2, mode = "hide") {
68151
+ if (mode === "show") {
68152
+ return {
68153
+ push(chunk) {
68154
+ write2(chunk);
68155
+ },
68156
+ flush() {
68157
+ }
68158
+ };
68159
+ }
68160
+ let pending = "";
68161
+ let insideThink = false;
68162
+ let markerEmitted = false;
68163
+ const emit = (chunk) => {
68164
+ if (chunk) write2(chunk);
68165
+ };
68166
+ const emitMarker = () => {
68167
+ if (mode !== "marker" || markerEmitted) return;
68168
+ emit("\n\u25B8 \u601D\u8003\u5DF2\u6298\u53E0\n");
68169
+ markerEmitted = true;
68170
+ };
68171
+ const consume = (input2) => {
68172
+ pending += input2;
68173
+ while (pending.length > 0) {
68174
+ if (insideThink) {
68175
+ const closeMatch = pending.match(THINK_CLOSE);
68176
+ if (!closeMatch || closeMatch.index == null) {
68177
+ emitMarker();
68178
+ pending = "";
68179
+ return;
68180
+ }
68181
+ emitMarker();
68182
+ pending = pending.slice(closeMatch.index + closeMatch[0].length);
68183
+ insideThink = false;
68184
+ markerEmitted = false;
68185
+ continue;
68186
+ }
68187
+ const openMatch = pending.match(THINK_OPEN);
68188
+ if (!openMatch || openMatch.index == null) {
68189
+ const nextMarker = pending.indexOf("<");
68190
+ if (nextMarker === -1) {
68191
+ const cleaned = mode === "hide" ? pending.split("\n").filter((line) => !COLLAPSED_MARKER.test(line)).join("\n") : pending;
68192
+ emit(cleaned);
68193
+ pending = "";
68194
+ return;
68195
+ }
68196
+ if (nextMarker > 0) {
68197
+ const head = pending.slice(0, nextMarker);
68198
+ const cleaned = mode === "hide" ? head.split("\n").filter((line) => !COLLAPSED_MARKER.test(line)).join("\n") : head;
68199
+ emit(cleaned);
68200
+ pending = pending.slice(nextMarker);
68201
+ continue;
68202
+ }
68203
+ if (pending.length < 7) return;
68204
+ emit(pending[0]);
68205
+ pending = pending.slice(1);
68206
+ continue;
68207
+ }
68208
+ if (openMatch.index > 0) {
68209
+ const head = pending.slice(0, openMatch.index);
68210
+ const cleaned = mode === "hide" ? head.split("\n").filter((line) => !COLLAPSED_MARKER.test(line)).join("\n") : head;
68211
+ emit(cleaned);
68212
+ pending = pending.slice(openMatch.index);
68213
+ continue;
68214
+ }
68215
+ pending = pending.slice(openMatch[0].length);
68216
+ insideThink = true;
68217
+ markerEmitted = false;
68218
+ }
68219
+ };
68220
+ return {
68221
+ push(chunk) {
68222
+ consume(chunk);
68223
+ },
68224
+ flush() {
68225
+ if (insideThink) emitMarker();
68226
+ if (pending) {
68227
+ const cleaned = mode === "hide" ? pending.split("\n").filter((line) => !COLLAPSED_MARKER.test(line)).join("\n") : pending;
68228
+ emit(cleaned);
68229
+ }
68230
+ pending = "";
68231
+ insideThink = false;
68232
+ markerEmitted = false;
68233
+ }
68234
+ };
68235
+ }
68236
+
68237
+ // packages/cli/client/tokenUsage.ts
68238
+ init_providers();
68239
+ var PROVIDER_LOOKUP_ORDER = [
68240
+ "openrouter",
68241
+ "fireworks",
68242
+ "openai",
68243
+ "mimo",
68244
+ "gmi",
68245
+ "google",
68246
+ "deepseek",
68247
+ "mistral",
68248
+ "vultr",
68249
+ "deepinfra",
68250
+ "cloudflare"
68251
+ ];
68252
+ function parseUsageRecord(usage2) {
68253
+ if (!usage2 || typeof usage2 !== "object") return void 0;
68254
+ const input2 = Number(usage2.input_tokens ?? usage2.prompt_tokens ?? 0);
68255
+ const output2 = Number(usage2.output_tokens ?? usage2.completion_tokens ?? 0);
68256
+ if (!Number.isFinite(input2) || !Number.isFinite(output2)) return void 0;
68257
+ if (!input2 && !output2) return void 0;
68258
+ return { input: input2, output: output2 };
68259
+ }
68260
+ function fuzzyContextWindow(model) {
68261
+ const lower = model.toLowerCase();
68262
+ if (lower.includes("minimax-m3") || lower.includes("minimax_m3")) return 1e6;
68263
+ if (lower.includes("minimax-m2")) return 262144;
68264
+ if (lower.includes("gpt-5") || lower.includes("gpt-4.1")) return 1047576;
68265
+ if (lower.includes("claude")) return 2e5;
68266
+ return void 0;
68267
+ }
68268
+ function resolveContextWindow(model) {
68269
+ const raw = model?.trim();
68270
+ if (!raw) return void 0;
68271
+ for (const provider of PROVIDER_LOOKUP_ORDER) {
68272
+ const config = findModelConfig(provider, raw);
68273
+ if (config?.contextWindow) return config.contextWindow;
68274
+ }
68275
+ return fuzzyContextWindow(raw);
68276
+ }
68277
+ function buildTurnTokenUsage(usage2, model) {
68278
+ const parsed = parseUsageRecord(usage2);
68279
+ if (!parsed) return void 0;
68280
+ const contextWindow = resolveContextWindow(model);
68281
+ const remaining = contextWindow && parsed.input > 0 ? Math.max(0, contextWindow - parsed.input) : void 0;
68282
+ return {
68283
+ ...parsed,
68284
+ ...contextWindow ? { contextWindow } : {},
68285
+ ...remaining != null ? { remaining } : {}
68286
+ };
68287
+ }
68288
+ function formatTokenCount(value) {
68289
+ if (!Number.isFinite(value) || value < 0) return "\u2014";
68290
+ if (value < 1e3) return String(Math.round(value));
68291
+ if (value < 1e6) {
68292
+ const compact3 = value / 1e3;
68293
+ if (Number.isInteger(compact3)) return `${compact3}k`;
68294
+ return `${compact3.toFixed(1).replace(/\.0$/, "")}k`;
68295
+ }
68296
+ const compact2 = value / 1e6;
68297
+ return compact2 >= 100 ? `${Math.round(compact2)}M` : `${compact2.toFixed(1).replace(/\.0$/, "")}M`;
68298
+ }
68299
+ function renderTokenStatus(tokens) {
68300
+ if (!tokens) return "in \u2014 out \u2014 left \u2014";
68301
+ const left = tokens.remaining != null ? formatTokenCount(tokens.remaining) : tokens.contextWindow ? "\u2014" : "\u2014";
68302
+ return `in ${formatTokenCount(tokens.input)} out ${formatTokenCount(tokens.output)} left ${left}`;
68303
+ }
68304
+
68305
+ // packages/cli/client/terminalStyles.ts
68306
+ var ANSI2 = {
68307
+ dim: "\x1B[2m",
68308
+ bold: "\x1B[1m",
68309
+ cyan: "\x1B[36m",
68310
+ green: "\x1B[32m",
68311
+ red: "\x1B[31m"
68312
+ };
68313
+ var RESET = "\x1B[0m";
68314
+ function resolveCliColorEnabled(env = process.env, isTTY = Boolean(process.stdout.isTTY)) {
68315
+ const setting = (env.NOLO_CLI_COLOR ?? "").trim().toLowerCase();
68316
+ if (setting === "0" || setting === "false" || setting === "off") return false;
68317
+ if (env.NO_COLOR) return false;
68318
+ if (setting === "1" || setting === "true" || setting === "on") return true;
68319
+ return isTTY;
68320
+ }
68321
+ function styleCliText(text, style, enabled = resolveCliColorEnabled()) {
68322
+ if (!enabled || !text) return text;
68323
+ return `${ANSI2[style]}${text}${RESET}`;
68324
+ }
68325
+ function dimCliText(text, enabled = resolveCliColorEnabled()) {
68326
+ return styleCliText(text, "dim", enabled);
68327
+ }
68328
+
68329
+ // packages/cli/client/toolOutput.ts
68330
+ function normalizeToolDisplayMode(raw, fallback = "compact") {
68331
+ const normalized = raw?.trim().toLowerCase();
68332
+ if (normalized === "hide" || normalized === "off" || normalized === "false" || normalized === "0") {
68333
+ return "hide";
68334
+ }
68335
+ if (normalized === "verbose" || normalized === "debug" || normalized === "trace" || normalized === "full") {
68336
+ return "verbose";
68337
+ }
68338
+ if (normalized === "compact" || normalized === "minimal" || normalized === "short") {
68339
+ return "compact";
68340
+ }
68341
+ return fallback;
68342
+ }
68343
+ function resolveToolDisplayMode(env = process.env) {
68344
+ const legacyTrace = env.NOLO_TRACE_TOOLS?.trim().toLowerCase();
68345
+ if (legacyTrace === "0" || legacyTrace === "false" || legacyTrace === "off") {
68346
+ return "hide";
68347
+ }
68348
+ if (legacyTrace === "verbose" || legacyTrace === "full") {
68349
+ return "verbose";
68350
+ }
68351
+ return normalizeToolDisplayMode(env.NOLO_CLI_TOOLS ?? env.NOLO_TOOLS, "compact");
68352
+ }
68353
+ function shouldEmitToolEvents(mode) {
68354
+ return mode !== "hide";
68355
+ }
68356
+ function clip4(value, max = 72) {
68357
+ const compact2 = value.replace(/\s+/g, " ").trim();
68358
+ return compact2.length > max ? `${compact2.slice(0, max - 1)}\u2026` : compact2;
68359
+ }
68360
+ function compactResultHint(summary, toolName) {
68361
+ if (!summary) return "";
68362
+ const linesMatch = summary.match(/(\d+)\s+lines?/);
68363
+ if (linesMatch) {
68364
+ if (toolName === "readFile" || toolName === "listFiles" || toolName === "globFiles") {
68365
+ return `${linesMatch[1]} lines`;
68366
+ }
68367
+ if (toolName === "execShell" || toolName === "runCommand") {
68368
+ return `${linesMatch[1]} lines`;
68369
+ }
68370
+ }
68371
+ const exitMatch = summary.match(/exit=(\d+)/);
68372
+ if (exitMatch && exitMatch[1] !== "0") return `exit ${exitMatch[1]}`;
68373
+ return "";
68374
+ }
68375
+ function formatToolTraceLine(text, colorEnabled, accent = "none") {
68376
+ if (!colorEnabled) return `${text}
68377
+ `;
68378
+ if (accent === "error") {
68379
+ return `${styleCliText(text, "red", true)}
68380
+ `;
68381
+ }
68382
+ return `${dimCliText(text, true)}
68383
+ `;
68384
+ }
68385
+ function formatVerboseToolEvent(event, colorEnabled) {
68386
+ const round = event.round + 1;
68387
+ const detail = event.argumentsPreview ? ` ${event.argumentsPreview}` : "";
68388
+ if (event.type === "tool-call") {
68389
+ return formatToolTraceLine(`[nolo:tool] #${round} -> ${event.toolName}${detail}`, colorEnabled);
68390
+ }
68391
+ if (event.type === "tool-error") {
68392
+ const elapsed2 = typeof event.elapsedMs === "number" ? ` ${event.elapsedMs}ms` : "";
68393
+ return formatToolTraceLine(
68394
+ `[nolo:tool] #${round} !! ${event.toolName}${elapsed2}: ${event.message ?? "failed"}`,
68395
+ colorEnabled,
68396
+ "error"
68397
+ );
68398
+ }
68399
+ const elapsed = typeof event.elapsedMs === "number" ? ` ${event.elapsedMs}ms` : "";
68400
+ const summary = event.summary ? ` ${event.summary}` : "";
68401
+ return formatToolTraceLine(
68402
+ `[nolo:tool] #${round} <- ${event.toolName}${elapsed}${summary}`,
68403
+ colorEnabled
68404
+ );
68405
+ }
68406
+ function formatCompactToolLine(event, pending, colorEnabled) {
68407
+ const toolName = event.toolName || pending?.toolName || "tool";
68408
+ const args2 = clip4(event.argumentsPreview || pending?.argumentsPreview || "");
68409
+ const label = args2 ? `${toolName} ${args2}` : toolName;
68410
+ const ms = typeof event.elapsedMs === "number" ? `${event.elapsedMs}ms` : "";
68411
+ if (event.type === "tool-error") {
68412
+ const message = clip4(event.message ?? "failed", 96);
68413
+ const timing2 = ms ? ` \xB7 ${ms}` : "";
68414
+ return formatToolTraceLine(` \u25B8 ${label} \u2717 ${message}${timing2}`, colorEnabled, "error");
68415
+ }
68416
+ const hint = compactResultHint(event.summary, toolName);
68417
+ const timing = ms ? ` ${ms}` : "";
68418
+ const suffix = hint ? ` \xB7 ${hint}` : "";
68419
+ return formatToolTraceLine(` \u25B8 ${label} \u2713${timing}${suffix}`, colorEnabled);
68420
+ }
68421
+ function createToolEventFormatter(mode, colorEnabled = resolveCliColorEnabled()) {
68422
+ const pending = /* @__PURE__ */ new Map();
68423
+ return (event) => {
68424
+ if (mode === "hide") return "";
68425
+ if (mode === "verbose") return formatVerboseToolEvent(event, colorEnabled);
68426
+ if (event.type === "tool-call") {
68427
+ pending.set(event.toolCallId, {
68428
+ toolName: event.toolName,
68429
+ argumentsPreview: event.argumentsPreview
68430
+ });
68431
+ return "";
68432
+ }
68433
+ const call = pending.get(event.toolCallId);
68434
+ pending.delete(event.toolCallId);
68435
+ return formatCompactToolLine(event, call, colorEnabled);
68436
+ };
68437
+ }
68438
+
67746
68439
  // packages/cli/client/agentRun.ts
67747
68440
  var Spinner = class {
67748
68441
  constructor(output2, text) {
@@ -67988,10 +68681,13 @@ function buildSubjectRefs(options) {
67988
68681
  }
67989
68682
  return refs.length ? refs : void 0;
67990
68683
  }
67991
- function shouldTraceLocalTools(options) {
67992
- const setting = (options.env.NOLO_TRACE_TOOLS ?? "").trim().toLowerCase();
67993
- if (setting === "0" || setting === "false" || setting === "off") return false;
67994
- return true;
68684
+ function formatAssistantResponseForCli(text, options) {
68685
+ const thinkingMode = resolveThinkingDisplayMode(options.env);
68686
+ const renderMode = resolveRenderDisplayMode(options.env);
68687
+ return formatAssistantDisplay(
68688
+ formatAssistantTextForCli(text, thinkingMode),
68689
+ renderMode
68690
+ );
67995
68691
  }
67996
68692
  function resolveAgentEventMode(options) {
67997
68693
  if (options.eventsMode === "jsonl") return "jsonl";
@@ -68002,23 +68698,6 @@ function isMissingLocalAgentConfigError(error, agentRef) {
68002
68698
  error && typeof error === "object" && "code" in error && error.code === LOCAL_AGENT_CONFIG_MISSING_CODE && error.agentRef === agentRef
68003
68699
  );
68004
68700
  }
68005
- function formatToolTraceEvent(event) {
68006
- const round = event.round + 1;
68007
- const detail = event.argumentsPreview ? ` ${event.argumentsPreview}` : "";
68008
- if (event.type === "tool-call") {
68009
- return `[nolo:tool] #${round} -> ${event.toolName}${detail}
68010
- `;
68011
- }
68012
- if (event.type === "tool-error") {
68013
- const elapsed2 = typeof event.elapsedMs === "number" ? ` ${event.elapsedMs}ms` : "";
68014
- return `[nolo:tool] #${round} !! ${event.toolName}${elapsed2}: ${event.message ?? "failed"}
68015
- `;
68016
- }
68017
- const elapsed = typeof event.elapsedMs === "number" ? ` ${event.elapsedMs}ms` : "";
68018
- const summary = event.summary ? ` ${event.summary}` : "";
68019
- return `[nolo:tool] #${round} <- ${event.toolName}${elapsed}${summary}
68020
- `;
68021
- }
68022
68701
  function formatToolJsonEvent(event) {
68023
68702
  return `${JSON.stringify({
68024
68703
  schemaVersion: 1,
@@ -68150,8 +68829,8 @@ async function runHttpAgentTurn(options, authToken) {
68150
68829
  const contentType = res.headers.get("content-type") || "";
68151
68830
  if (contentType.includes("text/event-stream") && res.body) {
68152
68831
  spinner.stop();
68153
- const result2 = await readStreamingAgentRun(options, res);
68154
- return result2;
68832
+ const result = await readStreamingAgentRun(options, res);
68833
+ return result;
68155
68834
  }
68156
68835
  spinner.stop();
68157
68836
  let data = {};
@@ -68194,7 +68873,10 @@ async function runHttpAgentTurn(options, authToken) {
68194
68873
  }
68195
68874
  return dialogIdText ? { exitCode: 1, dialogId: dialogIdText } : { exitCode: 1 };
68196
68875
  }
68197
- const content = String(data?.content ?? data?.message ?? "").trim();
68876
+ const content = formatAssistantResponseForCli(
68877
+ String(data?.content ?? data?.message ?? ""),
68878
+ options
68879
+ );
68198
68880
  if (content) {
68199
68881
  options.output.write(`
68200
68882
  ${options.agentName} > ${content}
@@ -68207,11 +68889,14 @@ ${options.agentName} > (no text response)
68207
68889
  const usage2 = formatUsage(data?.usage, data?.dialogId);
68208
68890
  if (usage2 && shouldShowUsage(options.env)) options.output.write(`${usage2}
68209
68891
  `);
68210
- const result = {
68892
+ return {
68211
68893
  exitCode: 0,
68212
- ...typeof data?.dialogId === "string" && data.dialogId ? { dialogId: data.dialogId } : {}
68894
+ ...typeof data?.dialogId === "string" && data.dialogId ? { dialogId: data.dialogId } : {},
68895
+ turnTokens: buildTurnTokenUsage(
68896
+ data?.usage,
68897
+ typeof data?.model === "string" ? data.model : options.agentKey
68898
+ )
68213
68899
  };
68214
- return result;
68215
68900
  }
68216
68901
  async function runInjectedLocalAgentTurn(options) {
68217
68902
  return runLocalAgentTurnForCli(options, { reportFailure: true });
@@ -68231,8 +68916,18 @@ async function runLocalAgentTurnForCli(options, settings) {
68231
68916
  const spinner = new Spinner(options.output, `${options.agentName} -> working locally`);
68232
68917
  spinner.start();
68233
68918
  try {
68234
- const traceLocalTools = shouldTraceLocalTools(options);
68919
+ const toolDisplayMode = resolveToolDisplayMode(options.env);
68920
+ const traceLocalTools = shouldEmitToolEvents(toolDisplayMode);
68921
+ const formatToolEvent = createToolEventFormatter(toolDisplayMode);
68235
68922
  const eventMode = resolveAgentEventMode(options);
68923
+ let wroteToolTrace = false;
68924
+ let streamedAssistantText = false;
68925
+ let printedAssistantLabel = false;
68926
+ const thinkingMode = resolveThinkingDisplayMode(options.env);
68927
+ const thinkingFilter = createThinkingAwareStreamFilter(
68928
+ (chunk) => options.output.write(chunk),
68929
+ thinkingMode
68930
+ );
68236
68931
  const subjectRefs = buildSubjectRefs(options);
68237
68932
  const allowedChildAgentKeys = options.allowedChildAgentKeys?.filter((key2) => key2.trim());
68238
68933
  const allowedToolNames = options.allowedToolNames?.filter((name) => name.trim());
@@ -68261,27 +68956,48 @@ async function runLocalAgentTurnForCli(options, settings) {
68261
68956
  ...typeof options.timeoutMs === "number" ? { timeoutMs: options.timeoutMs } : {},
68262
68957
  ...traceLocalTools ? {
68263
68958
  onToolEvent: (event) => {
68264
- if (traceLocalTools) {
68265
- spinner.stop();
68266
- options.output.write(
68267
- eventMode === "jsonl" ? formatToolJsonEvent(event) : formatToolTraceEvent(event)
68268
- );
68959
+ spinner.stop();
68960
+ const chunk = eventMode === "jsonl" ? formatToolJsonEvent(event) : formatToolEvent(event);
68961
+ if (chunk) {
68962
+ wroteToolTrace = true;
68963
+ options.output.write(chunk);
68269
68964
  }
68270
68965
  }
68966
+ } : {},
68967
+ ...!options.noStream ? {
68968
+ onTextDelta: (chunk) => {
68969
+ spinner.stop();
68970
+ if (!printedAssistantLabel) {
68971
+ options.output.write(`
68972
+ ${options.agentName} > `);
68973
+ printedAssistantLabel = true;
68974
+ }
68975
+ streamedAssistantText = true;
68976
+ thinkingFilter.push(chunk);
68977
+ }
68271
68978
  } : {}
68272
68979
  });
68273
68980
  spinner.stop();
68274
- const content = result.content.trim();
68275
- if (content) {
68276
- options.output.write(`
68981
+ if (streamedAssistantText) {
68982
+ thinkingFilter.flush();
68983
+ options.output.write("\n");
68984
+ } else {
68985
+ const content = formatAssistantResponseForCli(result.content.trim(), options);
68986
+ if (content) {
68987
+ options.output.write(`
68277
68988
  ${options.agentName} > ${content}
68278
68989
  `);
68279
- } else {
68280
- options.output.write(`
68990
+ } else {
68991
+ options.output.write(`
68281
68992
  ${options.agentName} > (no text response)
68282
68993
  `);
68994
+ }
68283
68995
  }
68284
- return { exitCode: 0, dialogId: result.dialogId };
68996
+ return {
68997
+ exitCode: 0,
68998
+ dialogId: result.dialogId,
68999
+ turnTokens: buildTurnTokenUsage(result.usage, result.model)
69000
+ };
68285
69001
  } catch (error) {
68286
69002
  spinner.stop();
68287
69003
  if (settings.reportFailure) {
@@ -68300,9 +69016,14 @@ async function readStreamingAgentRun(options, res) {
68300
69016
  return { exitCode: 1 };
68301
69017
  }
68302
69018
  const decoder = new TextDecoder();
69019
+ const thinkingMode = resolveThinkingDisplayMode(options.env);
68303
69020
  const writer = createStreamingTextWriter({
68304
69021
  write: (chunk) => options.output.write(chunk)
68305
69022
  });
69023
+ const thinkingFilter = createThinkingAwareStreamFilter(
69024
+ (chunk) => writer.push(chunk),
69025
+ thinkingMode
69026
+ );
68306
69027
  let buffer = "";
68307
69028
  let content = "";
68308
69029
  let dialogId;
@@ -68332,7 +69053,7 @@ ${options.agentName} > `);
68332
69053
  if (!chunk) return;
68333
69054
  printLabel();
68334
69055
  content += chunk;
68335
- writer.push(chunk);
69056
+ thinkingFilter.push(chunk);
68336
69057
  };
68337
69058
  try {
68338
69059
  while (true) {
@@ -68369,6 +69090,7 @@ ${options.agentName} > `);
68369
69090
  return { exitCode: 1 };
68370
69091
  } finally {
68371
69092
  writer.flushAll();
69093
+ thinkingFilter.flush();
68372
69094
  }
68373
69095
  if (!content) {
68374
69096
  options.output.write(`
@@ -68382,7 +69104,8 @@ ${options.agentName} > (no text response)
68382
69104
  `);
68383
69105
  return {
68384
69106
  exitCode: 0,
68385
- ...dialogId ? { dialogId } : {}
69107
+ ...dialogId ? { dialogId } : {},
69108
+ turnTokens: buildTurnTokenUsage(usage2, options.agentKey)
68386
69109
  };
68387
69110
  }
68388
69111
  async function runAgentTurn(options) {
@@ -68395,10 +69118,17 @@ async function runAgentTurn(options) {
68395
69118
  const skipLocal = await shouldSkipAutoLocalForServerPlatformTools(options);
68396
69119
  if (!skipLocal) {
68397
69120
  const localResult = await runLocalAgentTurnForCli(options, { reportFailure: false });
69121
+ if (localResult.exitCode !== 0 && localResult.localError) {
69122
+ options.output.write(
69123
+ `[nolo] auto runtime: local run unavailable (${localResult.localError instanceof Error ? localResult.localError.message : String(localResult.localError)}); falling back to server.
69124
+ `
69125
+ );
69126
+ }
68398
69127
  if (localResult.exitCode === 0) {
68399
69128
  return {
68400
69129
  exitCode: localResult.exitCode,
68401
- ...localResult.dialogId ? { dialogId: localResult.dialogId } : {}
69130
+ ...localResult.dialogId ? { dialogId: localResult.dialogId } : {},
69131
+ ...localResult.turnTokens ? { turnTokens: localResult.turnTokens } : {}
68402
69132
  };
68403
69133
  }
68404
69134
  if (isMissingLocalAgentConfigError(localResult.localError, options.agentKey)) {
@@ -68415,7 +69145,8 @@ async function runAgentTurn(options) {
68415
69145
  if (retriedLocalResult.exitCode === 0) {
68416
69146
  return {
68417
69147
  exitCode: retriedLocalResult.exitCode,
68418
- ...retriedLocalResult.dialogId ? { dialogId: retriedLocalResult.dialogId } : {}
69148
+ ...retriedLocalResult.dialogId ? { dialogId: retriedLocalResult.dialogId } : {},
69149
+ ...retriedLocalResult.turnTokens ? { turnTokens: retriedLocalResult.turnTokens } : {}
68419
69150
  };
68420
69151
  }
68421
69152
  }
@@ -69617,7 +70348,7 @@ function countByName(values) {
69617
70348
  function uniq(values) {
69618
70349
  return [...new Set(values.filter(Boolean))];
69619
70350
  }
69620
- function parseJsonObject3(raw) {
70351
+ function parseJsonObject2(raw) {
69621
70352
  if (typeof raw !== "string" || !raw.trim()) return null;
69622
70353
  try {
69623
70354
  const parsed = JSON.parse(raw);
@@ -69633,7 +70364,7 @@ function deriveWrittenFiles(toolMessages) {
69633
70364
  const toolName = toToolName(message?.toolName ?? message?.name);
69634
70365
  if (!writeToolNames.has(toolName)) continue;
69635
70366
  const payload = message?.toolPayload ?? {};
69636
- const content = parseJsonObject3(message?.content);
70367
+ const content = parseJsonObject2(message?.content);
69637
70368
  for (const candidate of [payload?.input?.filePath, payload?.response?.filePath, content?.filePath]) {
69638
70369
  const normalized = typeof candidate === "string" ? candidate.trim() : "";
69639
70370
  if (normalized) files.push(normalized);
@@ -69646,7 +70377,7 @@ function deriveToolErrors(toolMessages) {
69646
70377
  for (const message of toolMessages) {
69647
70378
  const toolName = toToolName(message?.toolName ?? message?.name);
69648
70379
  const payload = message?.toolPayload ?? {};
69649
- const content = parseJsonObject3(message?.content);
70380
+ const content = parseJsonObject2(message?.content);
69650
70381
  if (payload?.status === "failed" || content?.ok === false || content?.applied === false || content?.success === false) {
69651
70382
  errors.push(toolName || "unknown-tool");
69652
70383
  }
@@ -71578,6 +72309,18 @@ function getCurrentProfile(config) {
71578
72309
  if (!config) return null;
71579
72310
  return config.profiles[config.currentProfile] ?? null;
71580
72311
  }
72312
+ function saveProfileAgentSelection(selection, path7 = getDefaultProfileConfigPath()) {
72313
+ const config = loadProfileConfig(path7);
72314
+ if (!config) return null;
72315
+ const profile = config.profiles[config.currentProfile];
72316
+ if (!profile) return null;
72317
+ profile.agentKey = selection.agentKey.trim();
72318
+ profile.agentName = selection.agentName.trim();
72319
+ mkdirSync5(dirname6(path7), { recursive: true });
72320
+ writeFileSync4(path7, `${JSON.stringify(config, null, 2)}
72321
+ `, "utf8");
72322
+ return config;
72323
+ }
71581
72324
 
71582
72325
  // packages/cli/authCommands.ts
71583
72326
  init_cliEnvHelpers();
@@ -73315,23 +74058,410 @@ async function compactDialog(options) {
73315
74058
  // packages/cli/tui/readlineWorkspace.ts
73316
74059
  init_processSpawn();
73317
74060
 
73318
- // packages/cli/tui/session.ts
73319
- init_defaultServer();
74061
+ // packages/cli/tui/agentPicker.ts
74062
+ init_agentAliases();
74063
+
74064
+ // packages/cli/tui/agentCatalog.ts
74065
+ init_cliEnvHelpers();
73320
74066
  var DEFAULT_TUI_AGENT_KEY = "agent-pub-01NOLOAPPBLD000000019KCKT0";
73321
- var DEFAULT_TUI_SERVER_URL = DEFAULT_NOLO_SERVER_URL;
73322
- var KNOWN_AGENTS = [
74067
+ var PLATFORM_AGENTS = [
73323
74068
  {
73324
74069
  name: "nolo",
73325
- key: "agent-pub-01NOLOAPPBLD000000019KCKT0",
74070
+ key: DEFAULT_TUI_AGENT_KEY,
74071
+ model: "-",
74072
+ kind: "platform",
73326
74073
  description: "one assistant that routes work across your agents and data"
73327
74074
  },
73328
74075
  {
73329
74076
  name: "app-builder",
73330
74077
  key: "agent-pub-01APPBUILDER00000001YAII3I",
74078
+ model: "-",
74079
+ kind: "platform",
73331
74080
  description: "builds web apps, tools, charts, and product prototypes"
73332
74081
  }
73333
74082
  ];
73334
- var KNOWN_AGENT_ALIASES = Object.fromEntries(KNOWN_AGENTS.map((agent) => [agent.name, agent]));
74083
+ function toUpdatedAt(value) {
74084
+ if (value == null) return 0;
74085
+ const parsed = new Date(value).getTime();
74086
+ return Number.isFinite(parsed) ? parsed : 0;
74087
+ }
74088
+ function listedAgentToCatalogEntry(agent) {
74089
+ return {
74090
+ name: agent.name,
74091
+ key: agent.privateKey,
74092
+ model: agent.model,
74093
+ kind: "private",
74094
+ updatedAt: toUpdatedAt(agent.updatedAt)
74095
+ };
74096
+ }
74097
+ function mergeCatalogEntries(currentKey, platformAgents, privateAgents) {
74098
+ const seen = /* @__PURE__ */ new Set();
74099
+ const merged = [];
74100
+ const push = (entry) => {
74101
+ if (seen.has(entry.key)) return;
74102
+ seen.add(entry.key);
74103
+ merged.push(entry);
74104
+ };
74105
+ const current = [...platformAgents, ...privateAgents].find((entry) => entry.key === currentKey) ?? null;
74106
+ if (current) push(current);
74107
+ for (const entry of platformAgents) {
74108
+ if (entry.key !== currentKey) push(entry);
74109
+ }
74110
+ const sortedPrivate = [...privateAgents].sort((a, b) => {
74111
+ const tb = b.updatedAt ?? 0;
74112
+ const ta = a.updatedAt ?? 0;
74113
+ if (tb !== ta) return tb - ta;
74114
+ return a.name.localeCompare(b.name);
74115
+ });
74116
+ for (const entry of sortedPrivate) {
74117
+ if (entry.key !== currentKey) push(entry);
74118
+ }
74119
+ return merged;
74120
+ }
74121
+ async function loadAgentCatalog(args2) {
74122
+ const env = args2.env ?? process.env;
74123
+ const fetchImpl = args2.fetchImpl ?? fetch;
74124
+ const fallbackFetchImpl = args2.fallbackFetchImpl;
74125
+ const authToken = resolveAuthToken([], env);
74126
+ const userId = authToken ? parseUserIdFromAuthToken(authToken) : null;
74127
+ if (!authToken || !userId) {
74128
+ return mergeCatalogEntries(args2.currentKey, PLATFORM_AGENTS, []);
74129
+ }
74130
+ const serverUrl = resolveServerUrl(env);
74131
+ const serverUrls = resolveServerCandidates([], env, serverUrl);
74132
+ let privateAgents = [];
74133
+ try {
74134
+ const remoteResult = await listRemoteAgentsAcrossServers({
74135
+ authToken,
74136
+ fallbackFetchImpl,
74137
+ fetchImpl,
74138
+ includeLegacy: false,
74139
+ serverUrls,
74140
+ userId
74141
+ });
74142
+ privateAgents = remoteResult.agents.map(listedAgentToCatalogEntry);
74143
+ } catch {
74144
+ try {
74145
+ const db = await getReadableCliDb();
74146
+ const cached = await listLocalCachedAgents({ db, userId });
74147
+ privateAgents = cached.map(listedAgentToCatalogEntry);
74148
+ } catch {
74149
+ privateAgents = (await listRemoteAgents({
74150
+ authToken,
74151
+ fallbackFetchImpl,
74152
+ fetchImpl,
74153
+ includeLegacy: false,
74154
+ serverUrl,
74155
+ userId,
74156
+ queryUserRecords,
74157
+ readDbRecord
74158
+ })).map(listedAgentToCatalogEntry);
74159
+ }
74160
+ }
74161
+ return mergeCatalogEntries(args2.currentKey, PLATFORM_AGENTS, privateAgents);
74162
+ }
74163
+ function renderAgentCatalogList(entries, currentKey) {
74164
+ const lines = ["Agents:"];
74165
+ entries.forEach((entry, index) => {
74166
+ const current = entry.key === currentKey ? " (current)" : "";
74167
+ const kind = entry.kind === "platform" ? "platform" : "private";
74168
+ const detail = entry.description ? ` \u2014 ${entry.description}` : "";
74169
+ lines.push(
74170
+ ` ${String(index + 1).padStart(2)} ${entry.name.padEnd(18)} ${entry.model.padEnd(14)} ${kind}${detail}${current}`
74171
+ );
74172
+ });
74173
+ lines.push("");
74174
+ lines.push("Tip: run /agent in an interactive terminal to pick with \u2191\u2193.");
74175
+ return lines.join("\n");
74176
+ }
74177
+ function findAgentCatalogEntry(entries, rawTarget) {
74178
+ const target = rawTarget.trim();
74179
+ if (!target) return null;
74180
+ if (/^\d+$/.test(target)) {
74181
+ const entry = entries[Number(target) - 1];
74182
+ return entry ? { name: entry.name, key: entry.key } : null;
74183
+ }
74184
+ const lower = target.toLowerCase();
74185
+ const byName = entries.find(
74186
+ (entry) => entry.name.toLowerCase() === lower || entry.key.toLowerCase() === lower || entry.key.toLowerCase().endsWith(`-${lower}`)
74187
+ );
74188
+ if (byName) return { name: byName.name, key: byName.key };
74189
+ if (target.startsWith("agent-") || target.startsWith("agent-pub-") || target.startsWith("cybot-")) {
74190
+ return { name: target, key: target };
74191
+ }
74192
+ return null;
74193
+ }
74194
+
74195
+ // packages/cli/tui/selectDialog.ts
74196
+ var CSI_ARROW_UP = "\x1B[A";
74197
+ var CSI_ARROW_DOWN = "\x1B[B";
74198
+ var CSI_ARROW_UP_APP = "\x1BOA";
74199
+ var CSI_ARROW_DOWN_APP = "\x1BOB";
74200
+ var DEFAULT_MAX_VISIBLE = 8;
74201
+ function computeVisibleWindow(args2) {
74202
+ const maxVisible = Math.max(1, args2.maxVisible ?? DEFAULT_MAX_VISIBLE);
74203
+ if (args2.total <= maxVisible) {
74204
+ return { start: 0, end: args2.total, maxVisible };
74205
+ }
74206
+ let start = Math.max(0, args2.selectedIndex - Math.floor(maxVisible / 2));
74207
+ if (start + maxVisible > args2.total) {
74208
+ start = args2.total - maxVisible;
74209
+ }
74210
+ return { start, end: start + maxVisible, maxVisible };
74211
+ }
74212
+ function renderSelectDialog(args2) {
74213
+ const total = args2.items.length;
74214
+ const window2 = computeVisibleWindow({
74215
+ selectedIndex: args2.selectedIndex,
74216
+ total,
74217
+ maxVisible: args2.maxVisible
74218
+ });
74219
+ const lines = [
74220
+ args2.title ?? `Select agent (\u2191\u2193 Enter Esc) ${args2.selectedIndex + 1}/${total}`
74221
+ ];
74222
+ if (window2.start > 0) {
74223
+ lines.push(` ... ${window2.start} more above`);
74224
+ }
74225
+ for (let index = window2.start; index < window2.end; index += 1) {
74226
+ const item = args2.items[index];
74227
+ const marker = index === args2.selectedIndex ? ">" : " ";
74228
+ const detail = item.detail ? ` ${item.detail}` : "";
74229
+ lines.push(`${marker} ${item.label}${detail}`);
74230
+ }
74231
+ if (window2.end < total) {
74232
+ lines.push(` ... ${total - window2.end} more below`);
74233
+ }
74234
+ return lines.join("\n");
74235
+ }
74236
+ function countRenderedLines(text) {
74237
+ return text.split("\n").length;
74238
+ }
74239
+ function clearRenderedLines(output2, lineCount) {
74240
+ if (!output2.isTTY || lineCount <= 0) return;
74241
+ for (let index = 0; index < lineCount; index += 1) {
74242
+ output2.write("\x1B[1A\x1B[2K");
74243
+ }
74244
+ }
74245
+ function isArrowUp(sequence) {
74246
+ return sequence === CSI_ARROW_UP || sequence === CSI_ARROW_UP_APP;
74247
+ }
74248
+ function isArrowDown(sequence) {
74249
+ return sequence === CSI_ARROW_DOWN || sequence === CSI_ARROW_DOWN_APP;
74250
+ }
74251
+ function isSubmit(sequence) {
74252
+ return sequence === "\r" || sequence === "\n";
74253
+ }
74254
+ function isCancel(sequence) {
74255
+ return sequence === "" || sequence === "\x1B";
74256
+ }
74257
+ function createRawKeyReader(input2) {
74258
+ let buffer = "";
74259
+ const tryParseSequence = () => {
74260
+ if (!buffer) return null;
74261
+ if (isSubmit(buffer) || isCancel(buffer)) {
74262
+ const sequence2 = buffer;
74263
+ buffer = "";
74264
+ return sequence2;
74265
+ }
74266
+ if (buffer.startsWith("\x1B")) {
74267
+ for (const candidate of [
74268
+ CSI_ARROW_UP,
74269
+ CSI_ARROW_DOWN,
74270
+ CSI_ARROW_UP_APP,
74271
+ CSI_ARROW_DOWN_APP
74272
+ ]) {
74273
+ if (buffer.startsWith(candidate)) {
74274
+ buffer = buffer.slice(candidate.length);
74275
+ return candidate;
74276
+ }
74277
+ }
74278
+ if (buffer.length >= 8) {
74279
+ buffer = "";
74280
+ return null;
74281
+ }
74282
+ return void 0;
74283
+ }
74284
+ const sequence = buffer;
74285
+ buffer = "";
74286
+ return sequence;
74287
+ };
74288
+ return () => new Promise((resolve6) => {
74289
+ const finalize = (sequence) => {
74290
+ cleanup();
74291
+ resolve6(sequence ?? null);
74292
+ };
74293
+ const onReadable = () => {
74294
+ while (true) {
74295
+ const chunk = input2.read();
74296
+ if (chunk == null) break;
74297
+ buffer += typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf8");
74298
+ const parsed = tryParseSequence();
74299
+ if (parsed === void 0) return;
74300
+ finalize(parsed);
74301
+ return;
74302
+ }
74303
+ };
74304
+ const cleanup = () => {
74305
+ input2.off("readable", onReadable);
74306
+ };
74307
+ onReadable();
74308
+ if (buffer) {
74309
+ const parsed = tryParseSequence();
74310
+ if (parsed !== void 0) {
74311
+ finalize(parsed);
74312
+ return;
74313
+ }
74314
+ }
74315
+ input2.on("readable", onReadable);
74316
+ });
74317
+ }
74318
+ function drainInputBuffer(input2) {
74319
+ if (typeof input2.read !== "function") return;
74320
+ while (input2.read() !== null) {
74321
+ }
74322
+ }
74323
+ async function runSelectDialog(args2) {
74324
+ const items = args2.items;
74325
+ if (items.length === 0) {
74326
+ return { kind: "cancelled" };
74327
+ }
74328
+ let selectedIndex = Math.min(
74329
+ Math.max(args2.initialIndex ?? 0, 0),
74330
+ items.length - 1
74331
+ );
74332
+ const output2 = args2.output ?? process.stdout;
74333
+ const input2 = args2.input ?? process.stdin;
74334
+ const readKey = args2.readKey ?? createRawKeyReader(input2);
74335
+ const wasRaw = Boolean(input2.isTTY && input2.isRaw);
74336
+ const wasPaused = typeof input2.isPaused === "function" ? input2.isPaused() : false;
74337
+ let renderedLineCount = 0;
74338
+ const paint = () => {
74339
+ const frame = renderSelectDialog({
74340
+ items,
74341
+ selectedIndex,
74342
+ title: args2.title,
74343
+ maxVisible: args2.maxVisible
74344
+ });
74345
+ if (output2.isTTY && typeof output2.write === "function") {
74346
+ clearRenderedLines(output2, renderedLineCount);
74347
+ output2.write(`${frame}
74348
+ `);
74349
+ renderedLineCount = countRenderedLines(frame);
74350
+ return;
74351
+ }
74352
+ if (typeof output2.write === "function") {
74353
+ output2.write(`${frame}
74354
+ `);
74355
+ }
74356
+ renderedLineCount = countRenderedLines(frame);
74357
+ };
74358
+ if (input2.isTTY) {
74359
+ if (!wasRaw) input2.setRawMode(true);
74360
+ if (!wasPaused) input2.pause();
74361
+ }
74362
+ paint();
74363
+ try {
74364
+ while (true) {
74365
+ const sequence = await readKey();
74366
+ if (sequence == null) {
74367
+ return { kind: "cancelled" };
74368
+ }
74369
+ if (isCancel(sequence)) {
74370
+ return { kind: "cancelled" };
74371
+ }
74372
+ if (isSubmit(sequence)) {
74373
+ return { kind: "selected", index: selectedIndex, item: items[selectedIndex] };
74374
+ }
74375
+ if (isArrowUp(sequence)) {
74376
+ selectedIndex = selectedIndex <= 0 ? items.length - 1 : selectedIndex - 1;
74377
+ paint();
74378
+ continue;
74379
+ }
74380
+ if (isArrowDown(sequence)) {
74381
+ selectedIndex = selectedIndex >= items.length - 1 ? 0 : selectedIndex + 1;
74382
+ paint();
74383
+ continue;
74384
+ }
74385
+ }
74386
+ } finally {
74387
+ if (input2.isTTY) {
74388
+ drainInputBuffer(input2);
74389
+ if (!wasRaw) input2.setRawMode(false);
74390
+ if (!wasPaused) input2.resume();
74391
+ clearRenderedLines(output2, renderedLineCount);
74392
+ renderedLineCount = 0;
74393
+ }
74394
+ }
74395
+ }
74396
+
74397
+ // packages/cli/tui/agentPicker.ts
74398
+ function toAgentPickerItems(entries) {
74399
+ return entries.map((entry) => ({
74400
+ label: entry.name,
74401
+ detail: `${entry.model} ${entry.kind}`,
74402
+ entry
74403
+ }));
74404
+ }
74405
+ function formatAgentSwitchMessage(args2) {
74406
+ const dialog = args2.dialogId ? `Dialog kept: ${args2.dialogId}` : "Dialog kept: new";
74407
+ return `Switched to ${args2.name}. ${dialog}`;
74408
+ }
74409
+ async function runAgentPicker(args2) {
74410
+ const output2 = args2.output ?? process.stdout;
74411
+ const input2 = args2.input ?? process.stdin;
74412
+ const interactive = args2.interactive ?? Boolean(input2.isTTY && output2.isTTY);
74413
+ const entries = await loadAgentCatalog({
74414
+ env: args2.env,
74415
+ currentKey: args2.currentKey,
74416
+ fetchImpl: args2.fetchImpl,
74417
+ fallbackFetchImpl: args2.fallbackFetchImpl
74418
+ });
74419
+ if (!interactive) {
74420
+ return {
74421
+ kind: "list",
74422
+ output: renderAgentCatalogList(entries, args2.currentKey),
74423
+ entries
74424
+ };
74425
+ }
74426
+ const items = toAgentPickerItems(entries);
74427
+ const initialIndex = Math.max(
74428
+ entries.findIndex((entry) => entry.key === args2.currentKey),
74429
+ 0
74430
+ );
74431
+ const result = await runSelectDialog({
74432
+ items,
74433
+ initialIndex,
74434
+ title: void 0,
74435
+ input: input2,
74436
+ output: output2,
74437
+ readKey: args2.readKey
74438
+ });
74439
+ if (result.kind === "cancelled") {
74440
+ return { kind: "cancelled", entries };
74441
+ }
74442
+ const selected = result.item.entry;
74443
+ return {
74444
+ kind: "selected",
74445
+ name: selected.name,
74446
+ key: selected.key,
74447
+ entries
74448
+ };
74449
+ }
74450
+ function resolveAgentSwitchTarget(rawTarget, catalogEntries = []) {
74451
+ const resolvedKey = resolveCliAgentKeyInput(rawTarget);
74452
+ if (resolvedKey !== rawTarget.trim()) {
74453
+ const aliasEntry = catalogEntries.find((entry) => entry.key === resolvedKey);
74454
+ return {
74455
+ name: aliasEntry?.name ?? rawTarget.trim().toLowerCase(),
74456
+ key: resolvedKey
74457
+ };
74458
+ }
74459
+ return findAgentCatalogEntry(catalogEntries, rawTarget);
74460
+ }
74461
+
74462
+ // packages/cli/tui/session.ts
74463
+ init_defaultServer();
74464
+ var DEFAULT_TUI_SERVER_URL = DEFAULT_NOLO_SERVER_URL;
73335
74465
  function shortenDialogId(dialogId) {
73336
74466
  return dialogId.length > 12 ? `${dialogId.slice(0, 6)}...${dialogId.slice(-4)}` : dialogId;
73337
74467
  }
@@ -73353,25 +74483,29 @@ function createInitialTuiState(env = process.env) {
73353
74483
  ),
73354
74484
  cliVersion: env.NOLO_CLI_VERSION?.trim() || void 0,
73355
74485
  attachedDocs: [],
73356
- runtimeMode: env.NOLO_RUNTIME_MODE === "local" || env.NOLO_RUNTIME_MODE === "server" ? env.NOLO_RUNTIME_MODE : "auto"
74486
+ runtimeMode: env.NOLO_RUNTIME_MODE === "local" || env.NOLO_RUNTIME_MODE === "server" ? env.NOLO_RUNTIME_MODE : "auto",
74487
+ thinkingDisplay: normalizeThinkingDisplayMode(
74488
+ env.NOLO_CLI_THINKING ?? env.NOLO_THINKING,
74489
+ "hide"
74490
+ ),
74491
+ toolDisplay: normalizeToolDisplayMode(env.NOLO_CLI_TOOLS ?? env.NOLO_TOOLS, "compact"),
74492
+ renderDisplay: normalizeRenderDisplayMode(env.NOLO_CLI_RENDER ?? env.NOLO_RENDER, "rich")
73357
74493
  };
73358
74494
  }
73359
74495
  function renderStatusLine(state) {
73360
- const docs = state.attachedDocs.length > 0 ? String(state.attachedDocs.length) : "0";
73361
- return [
73362
- `agent ${state.agentName}`,
73363
- `dialog ${resolveDialogLabel(state)}`,
73364
- `docs ${docs}`,
73365
- `runtime ${state.runtimeMode}`,
73366
- `profile ${state.profileName}`
73367
- ].join(" | ");
74496
+ const colorEnabled = resolveCliColorEnabled();
74497
+ const agent = styleCliText(state.agentName, "cyan", colorEnabled);
74498
+ const tokens = dimCliText(renderTokenStatus(state.turnTokens), colorEnabled);
74499
+ const profile = dimCliText(`profile ${state.profileName}`, colorEnabled);
74500
+ return [`agent ${agent}`, tokens, profile].join(
74501
+ dimCliText(" | ", colorEnabled)
74502
+ );
73368
74503
  }
73369
74504
  function renderWelcome(state) {
73370
- const docs = state.attachedDocs.length > 0 ? `${state.attachedDocs.length} attached` : "none";
73371
74505
  return [
73372
74506
  "",
73373
74507
  `Nolo workspace${state.cliVersion ? ` nolo ${state.cliVersion}` : ""}`,
73374
- `agent ${state.agentName} | dialog ${resolveDialogLabel(state)} | docs ${docs} | profile ${state.profileName}`,
74508
+ `agent ${state.agentName} | ${renderTokenStatus(state.turnTokens)} | profile ${state.profileName}`,
73375
74509
  `server ${state.serverUrl}`,
73376
74510
  "",
73377
74511
  "Tell nolo what you want. Use /help for commands. Use /version if this install feels stale.",
@@ -73389,9 +74523,14 @@ function renderTuiHelp() {
73389
74523
  " /compact Compact current dialog and fork a new one",
73390
74524
  " /context Show workspace context and next actions",
73391
74525
  " /runtime <mode> Use auto, local, or server runtime",
73392
- " /agent Show the current agent",
73393
- " /agents List built-in agent shortcuts",
73394
- " /switch <agent> Switch the current agent",
74526
+ " /tools <mode> Control tool trace: hide, compact, verbose",
74527
+ " /thinking <mode> Control thinking output: hide, marker, show",
74528
+ " /render <mode> Control assistant output: plain, rich",
74529
+ " /agent Pick an agent interactively (\u2191\u2193, Enter)",
74530
+ " /agent list List agents as text",
74531
+ " /agent <name> Switch directly by name, alias, or key",
74532
+ " /agents List platform agent shortcuts",
74533
+ " /switch <agent> Switch the current agent (alias of /agent <name>)",
73395
74534
  " /dialog Show the current dialog",
73396
74535
  " /doc List attached docs",
73397
74536
  " /doc attach <doc> Attach a doc to this workspace",
@@ -73410,12 +74549,16 @@ function renderContextPanel(state) {
73410
74549
  return [
73411
74550
  "Workspace context",
73412
74551
  "-----------------",
73413
- `agent ${state.agentName}`,
73414
- `dialog ${resolveDialogLabel(state)}`,
73415
- `docs ${docs}`,
73416
- `profile ${state.profileName}`,
73417
- `runtime ${state.runtimeMode}`,
73418
- `server ${state.serverUrl}`,
74552
+ `agent ${state.agentName}`,
74553
+ `tokens ${renderTokenStatus(state.turnTokens)}`,
74554
+ `dialog ${resolveDialogLabel(state)}`,
74555
+ `docs ${docs}`,
74556
+ `profile ${state.profileName}`,
74557
+ `runtime ${state.runtimeMode}`,
74558
+ `tools ${state.toolDisplay}`,
74559
+ `thinking ${state.thinkingDisplay}`,
74560
+ `render ${state.renderDisplay}`,
74561
+ `server ${state.serverUrl}`,
73419
74562
  "",
73420
74563
  "Next:",
73421
74564
  " /agents see specialist shortcuts",
@@ -73426,25 +74569,22 @@ function renderContextPanel(state) {
73426
74569
  function renderKnownAgents() {
73427
74570
  return [
73428
74571
  "Agents:",
73429
- ...KNOWN_AGENTS.map(
73430
- (agent, index) => ` ${index + 1} ${agent.name.padEnd(11)} ${agent.description}`
74572
+ ...PLATFORM_AGENTS.map(
74573
+ (agent, index) => ` ${index + 1} ${agent.name.padEnd(11)} ${agent.description ?? ""}`
73431
74574
  ),
73432
74575
  "",
73433
- "Tip: stay on nolo for the one-assistant feel; switch only when you want a specialist directly."
74576
+ "Tip: run /agent for the full picker, or /agent list for your private agents too."
73434
74577
  ].join("\n");
73435
74578
  }
73436
- function resolveSwitchTarget(rawTarget) {
73437
- const target = rawTarget.trim();
73438
- if (/^\d+$/.test(target)) {
73439
- const agent = KNOWN_AGENTS[Number(target) - 1];
73440
- return agent ? { name: agent.name, key: agent.key } : null;
73441
- }
73442
- const alias = KNOWN_AGENT_ALIASES[target.toLowerCase()];
73443
- if (alias) return alias;
73444
- if (target.startsWith("agent-") || target.startsWith("agent-pub-")) {
73445
- return { name: target, key: target };
73446
- }
73447
- return null;
74579
+ function applyAgentSwitch(state, target) {
74580
+ return {
74581
+ nextState: {
74582
+ ...state,
74583
+ agentName: target.name,
74584
+ agentKey: target.key
74585
+ },
74586
+ output: `Switched to ${target.name}. ${state.dialogId ? `Dialog kept: ${state.dialogId}` : "Dialog kept: new"}`
74587
+ };
73448
74588
  }
73449
74589
  var DIALOG_ID_PATTERN = /[0-9A-HJKMNP-TV-Z]{26}/i;
73450
74590
  var DIALOG_KEY_PATTERN = /dialog-[^\s"'<>]+-[0-9A-HJKMNP-TV-Z]{26}/i;
@@ -73622,6 +74762,66 @@ function handleTuiInput(input2, state) {
73622
74762
  output: `Runtime: ${argText}`
73623
74763
  };
73624
74764
  }
74765
+ case "/tools": {
74766
+ if (!argText) {
74767
+ return {
74768
+ nextState: state,
74769
+ output: `Tool display: ${state.toolDisplay} (hide | compact | verbose)`
74770
+ };
74771
+ }
74772
+ const normalizedArg = argText.trim().toLowerCase();
74773
+ if (!["hide", "compact", "verbose", "on", "off"].includes(normalizedArg)) {
74774
+ return {
74775
+ nextState: state,
74776
+ output: "Usage: /tools <hide|compact|verbose>"
74777
+ };
74778
+ }
74779
+ const nextMode = normalizeToolDisplayMode(normalizedArg, state.toolDisplay);
74780
+ return {
74781
+ nextState: { ...state, toolDisplay: nextMode },
74782
+ output: `Tool display: ${nextMode}`
74783
+ };
74784
+ }
74785
+ case "/thinking": {
74786
+ if (!argText) {
74787
+ return {
74788
+ nextState: state,
74789
+ output: `Thinking display: ${state.thinkingDisplay} (hide | marker | show)`
74790
+ };
74791
+ }
74792
+ const normalizedArg = argText.trim().toLowerCase();
74793
+ if (!["hide", "marker", "show", "on", "off"].includes(normalizedArg)) {
74794
+ return {
74795
+ nextState: state,
74796
+ output: "Usage: /thinking <hide|marker|show>"
74797
+ };
74798
+ }
74799
+ const nextMode = normalizeThinkingDisplayMode(normalizedArg, state.thinkingDisplay);
74800
+ return {
74801
+ nextState: { ...state, thinkingDisplay: nextMode },
74802
+ output: `Thinking display: ${nextMode}`
74803
+ };
74804
+ }
74805
+ case "/render": {
74806
+ if (!argText) {
74807
+ return {
74808
+ nextState: state,
74809
+ output: `Render display: ${state.renderDisplay} (plain | rich)`
74810
+ };
74811
+ }
74812
+ const normalizedArg = argText.trim().toLowerCase();
74813
+ if (!["plain", "rich", "on", "off"].includes(normalizedArg)) {
74814
+ return {
74815
+ nextState: state,
74816
+ output: "Usage: /render <plain|rich>"
74817
+ };
74818
+ }
74819
+ const nextMode = normalizeRenderDisplayMode(normalizedArg, state.renderDisplay);
74820
+ return {
74821
+ nextState: { ...state, renderDisplay: nextMode },
74822
+ output: `Render display: ${nextMode}`
74823
+ };
74824
+ }
73625
74825
  case "/exit":
73626
74826
  case "/quit":
73627
74827
  return { nextState: state, output: "Bye.", action: { type: "exit" } };
@@ -73631,7 +74831,8 @@ function handleTuiInput(input2, state) {
73631
74831
  ...state,
73632
74832
  dialogId: void 0,
73633
74833
  dialogLabel: "new",
73634
- attachedDocs: []
74834
+ attachedDocs: [],
74835
+ turnTokens: void 0
73635
74836
  },
73636
74837
  output: "Started a fresh dialog."
73637
74838
  };
@@ -73655,11 +74856,37 @@ ${renderTuiHelp()}`
73655
74856
  output: "Compacting current dialog...",
73656
74857
  action: { type: "compact", dialogId: state.dialogId }
73657
74858
  };
73658
- case "/agent":
73659
- return {
73660
- nextState: state,
73661
- output: `Current agent: ${state.agentName} (${state.agentKey})`
73662
- };
74859
+ case "/agent": {
74860
+ if (!argText) {
74861
+ return {
74862
+ nextState: state,
74863
+ output: "",
74864
+ action: { type: "pick-agent" }
74865
+ };
74866
+ }
74867
+ if (argText === "list") {
74868
+ return {
74869
+ nextState: state,
74870
+ output: "",
74871
+ action: { type: "list-agents" }
74872
+ };
74873
+ }
74874
+ if (argText === "current" || argText === "show") {
74875
+ return {
74876
+ nextState: state,
74877
+ output: `Current agent: ${state.agentName} (${state.agentKey})`
74878
+ };
74879
+ }
74880
+ const resolvedTarget = resolveAgentSwitchTarget(argText, PLATFORM_AGENTS);
74881
+ if (!resolvedTarget) {
74882
+ return {
74883
+ nextState: state,
74884
+ output: `I don't know agent "${argText}" yet.
74885
+ Use /agent, /agent list, /agent minimax-m3, or a full agent key.`
74886
+ };
74887
+ }
74888
+ return applyAgentSwitch(state, resolvedTarget);
74889
+ }
73663
74890
  case "/agents":
73664
74891
  return {
73665
74892
  nextState: state,
@@ -73669,25 +74896,18 @@ ${renderTuiHelp()}`
73669
74896
  if (!argText) {
73670
74897
  return {
73671
74898
  nextState: state,
73672
- output: "Usage: /switch <agent-key|alias>"
74899
+ output: "Usage: /switch <agent-key|alias> (or run /agent)"
73673
74900
  };
73674
74901
  }
73675
- const resolvedTarget = resolveSwitchTarget(argText);
74902
+ const resolvedTarget = resolveAgentSwitchTarget(argText, PLATFORM_AGENTS);
73676
74903
  if (!resolvedTarget) {
73677
74904
  return {
73678
74905
  nextState: state,
73679
74906
  output: `I don't know agent shortcut "${argText}" yet.
73680
- Use /switch nolo, /switch app-builder, or a full agent key like agent-pub-...`
74907
+ Use /agent, /switch nolo, /switch minimax-m3, or a full agent key.`
73681
74908
  };
73682
74909
  }
73683
- return {
73684
- nextState: {
73685
- ...state,
73686
- agentName: resolvedTarget.name,
73687
- agentKey: resolvedTarget.key
73688
- },
73689
- output: `Switched to ${resolvedTarget.name}.`
73690
- };
74910
+ return applyAgentSwitch(state, resolvedTarget);
73691
74911
  }
73692
74912
  case "/dialog":
73693
74913
  return {
@@ -73758,8 +74978,14 @@ async function runAgentChat(scriptDir, state, message, env, output2, agentRunner
73758
74978
  message,
73759
74979
  continueDialogId: state.dialogId,
73760
74980
  runtimeMode: state.runtimeMode,
74981
+ localRuntimeCwd: process.cwd(),
73761
74982
  scriptDir,
73762
- env,
74983
+ env: {
74984
+ ...env,
74985
+ NOLO_CLI_THINKING: state.thinkingDisplay,
74986
+ NOLO_CLI_TOOLS: state.toolDisplay,
74987
+ NOLO_CLI_RENDER: state.renderDisplay
74988
+ },
73763
74989
  output: output2
73764
74990
  });
73765
74991
  return result;
@@ -73785,6 +75011,19 @@ async function runCliCommandInChildProcess(args2, context) {
73785
75011
  ]);
73786
75012
  return proc.exited;
73787
75013
  }
75014
+ function persistAgentSelection(state, env) {
75015
+ try {
75016
+ saveProfileAgentSelection({
75017
+ agentKey: state.agentKey,
75018
+ agentName: state.agentName
75019
+ });
75020
+ } catch {
75021
+ }
75022
+ if (env) {
75023
+ env.NOLO_AGENT = state.agentKey;
75024
+ env.NOLO_AGENT_NAME = state.agentName;
75025
+ }
75026
+ }
73788
75027
  async function startTuiWorkspace(options) {
73789
75028
  let state = createInitialTuiState(options.env ?? process.env);
73790
75029
  const input2 = options.input ?? defaultInput;
@@ -73798,12 +75037,19 @@ async function startTuiWorkspace(options) {
73798
75037
  rl.prompt();
73799
75038
  try {
73800
75039
  for await (const line of rl) {
75040
+ if (!line.trim()) {
75041
+ continue;
75042
+ }
73801
75043
  const result = handleTuiInput(line, state);
75044
+ const previousAgentKey = state.agentKey;
73802
75045
  state = result.nextState;
73803
75046
  if (result.output) {
73804
75047
  output2.write(`${result.output}
73805
75048
  `);
73806
75049
  }
75050
+ if (state.agentKey !== previousAgentKey && result.output?.startsWith("Switched to ")) {
75051
+ persistAgentSelection(state, options.env ?? process.env);
75052
+ }
73807
75053
  if (result.action?.type === "exit") {
73808
75054
  break;
73809
75055
  }
@@ -73842,6 +75088,64 @@ async function startTuiWorkspace(options) {
73842
75088
  output2.write("Update failed. Check the error above, then run /update again or use nolo update.\n");
73843
75089
  }
73844
75090
  }
75091
+ if (result.action?.type === "pick-agent") {
75092
+ rl.pause();
75093
+ try {
75094
+ const pickResult = await runAgentPicker({
75095
+ currentKey: state.agentKey,
75096
+ env: options.env ?? process.env,
75097
+ input: input2,
75098
+ output: output2
75099
+ });
75100
+ if (pickResult.kind === "list") {
75101
+ output2.write(`${pickResult.output}
75102
+ `);
75103
+ } else if (pickResult.kind === "selected") {
75104
+ state = {
75105
+ ...state,
75106
+ agentName: pickResult.name,
75107
+ agentKey: pickResult.key
75108
+ };
75109
+ persistAgentSelection(state, options.env ?? process.env);
75110
+ output2.write(
75111
+ `${formatAgentSwitchMessage({
75112
+ name: pickResult.name,
75113
+ dialogId: state.dialogId
75114
+ })}
75115
+ `
75116
+ );
75117
+ } else {
75118
+ output2.write("Agent switch cancelled.\n");
75119
+ }
75120
+ } catch (error) {
75121
+ output2.write(
75122
+ `[nolo] Agent picker failed: ${error instanceof Error ? error.message : String(error)}
75123
+ `
75124
+ );
75125
+ } finally {
75126
+ rl.resume();
75127
+ }
75128
+ }
75129
+ if (result.action?.type === "list-agents") {
75130
+ try {
75131
+ const pickResult = await runAgentPicker({
75132
+ currentKey: state.agentKey,
75133
+ env: options.env ?? process.env,
75134
+ input: input2,
75135
+ output: output2,
75136
+ interactive: false
75137
+ });
75138
+ if (pickResult.kind === "list") {
75139
+ output2.write(`${pickResult.output}
75140
+ `);
75141
+ }
75142
+ } catch (error) {
75143
+ output2.write(
75144
+ `[nolo] Agent list failed: ${error instanceof Error ? error.message : String(error)}
75145
+ `
75146
+ );
75147
+ }
75148
+ }
73845
75149
  if (result.action?.type === "cli-command") {
73846
75150
  try {
73847
75151
  const exitCode2 = await cliCommandRunner(result.action.args, {
@@ -73870,11 +75174,14 @@ async function startTuiWorkspace(options) {
73870
75174
  output2,
73871
75175
  options.agentRunner
73872
75176
  );
73873
- if (runResult.dialogId) {
75177
+ if (runResult.dialogId || runResult.turnTokens) {
73874
75178
  state = {
73875
75179
  ...state,
73876
- dialogId: runResult.dialogId,
73877
- dialogLabel: runResult.dialogId
75180
+ ...runResult.dialogId ? {
75181
+ dialogId: runResult.dialogId,
75182
+ dialogLabel: runResult.dialogId
75183
+ } : {},
75184
+ ...runResult.turnTokens ? { turnTokens: runResult.turnTokens } : {}
73878
75185
  };
73879
75186
  }
73880
75187
  }