nolo-cli 0.1.47 → 0.1.49

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 +1567 -180
  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,148 @@ 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
+ function processOpenAiCompatibleSseEvent(event, state) {
3440
+ for (const line of event.split("\n")) {
3441
+ const trimmed = line.trim();
3442
+ if (!trimmed.startsWith("data:")) continue;
3443
+ const payload = trimmed.slice(5).trim();
3444
+ if (!payload || payload === "[DONE]") continue;
3445
+ let parsed;
3446
+ try {
3447
+ parsed = JSON.parse(payload);
3448
+ } catch {
3449
+ continue;
3450
+ }
3451
+ if (parsed?.usage && typeof parsed.usage === "object") {
3452
+ state.usage = parsed.usage;
3453
+ }
3454
+ const delta = parsed?.choices?.[0]?.delta;
3455
+ if (!delta || typeof delta !== "object") continue;
3456
+ const reasoningChunk = typeof delta.reasoning_content === "string" ? delta.reasoning_content : typeof delta.reasoning === "string" ? delta.reasoning : "";
3457
+ if (reasoningChunk) state.reasoning += reasoningChunk;
3458
+ if (Array.isArray(delta.tool_calls) && delta.tool_calls.length > 0) {
3459
+ accumulateToolCallDelta(state.accumulatedToolCalls, delta.tool_calls);
3460
+ }
3461
+ const textChunk = typeof delta.content === "string" ? delta.content : "";
3462
+ if (textChunk) {
3463
+ state.content += textChunk;
3464
+ state.onTextDelta?.(textChunk);
3465
+ }
3466
+ }
3467
+ }
3468
+ async function readOpenAiCompatibleSseCompletion(args2) {
3469
+ const reader = args2.response.body?.getReader();
3470
+ if (!reader) {
3471
+ throw new Error("OpenAI-compatible stream response did not include a readable body.");
3472
+ }
3473
+ const decoder = new TextDecoder();
3474
+ let buffer = "";
3475
+ const state = {
3476
+ content: "",
3477
+ reasoning: "",
3478
+ usage: void 0,
3479
+ accumulatedToolCalls: {},
3480
+ onTextDelta: args2.onTextDelta
3481
+ };
3482
+ while (true) {
3483
+ const { done, value } = await reader.read();
3484
+ if (done) break;
3485
+ buffer += decoder.decode(value, { stream: true });
3486
+ while (true) {
3487
+ const boundary = buffer.indexOf("\n\n");
3488
+ if (boundary === -1) break;
3489
+ const event = buffer.slice(0, boundary);
3490
+ buffer = buffer.slice(boundary + 2);
3491
+ processOpenAiCompatibleSseEvent(event, state);
3492
+ }
3493
+ }
3494
+ buffer += decoder.decode();
3495
+ if (buffer.trim()) {
3496
+ processOpenAiCompatibleSseEvent(buffer, state);
3497
+ }
3498
+ const tool_calls = finalizeAccumulatedToolCalls(state.accumulatedToolCalls);
3499
+ return {
3500
+ content: state.content,
3501
+ ...state.reasoning ? { reasoning_content: state.reasoning } : {},
3502
+ ...tool_calls.length > 0 ? { tool_calls } : {},
3503
+ ...state.usage ? { usage: state.usage } : {}
3504
+ };
3505
+ }
3506
+ async function executeOpenAiCompatibleChatCompletion(args2) {
3507
+ const request = buildOpenAiCompatibleChatCompletionRequest({
3508
+ providerConfig: args2.providerConfig,
3509
+ messages: args2.messages,
3510
+ tools: args2.tools,
3511
+ stream: args2.stream
3512
+ });
3513
+ const res = await args2.fetchImpl(request.url, {
3514
+ ...request.init,
3515
+ ...args2.signal ? { signal: args2.signal } : {}
3516
+ });
3517
+ if (!res.ok) {
3518
+ const raw2 = await res.text().catch(() => "");
3519
+ let data2 = raw2;
3520
+ try {
3521
+ data2 = JSON.parse(raw2);
3522
+ } catch {
3523
+ }
3524
+ throw new Error(`local provider failed: HTTP ${res.status} ${JSON.stringify(data2)}`);
3525
+ }
3526
+ const contentType = res.headers.get("content-type") ?? "";
3527
+ const shouldStream = Boolean(args2.stream && args2.onTextDelta) && contentType.includes("text/event-stream");
3528
+ if (shouldStream && args2.onTextDelta) {
3529
+ const streamed = await readOpenAiCompatibleSseCompletion({
3530
+ response: res,
3531
+ onTextDelta: args2.onTextDelta
3532
+ });
3533
+ return {
3534
+ content: streamed.content,
3535
+ model: args2.providerConfig.model,
3536
+ provider: args2.providerConfig.provider,
3537
+ ...streamed.tool_calls ? { tool_calls: streamed.tool_calls } : {},
3538
+ ...streamed.reasoning_content ? { reasoning_content: streamed.reasoning_content } : {},
3539
+ ...streamed.usage ? { usage: streamed.usage } : {},
3540
+ trace: args2.messages
3541
+ };
3542
+ }
3543
+ const raw = await res.text().catch(() => "");
3544
+ let data = {};
3545
+ try {
3546
+ data = raw ? JSON.parse(raw) : {};
3547
+ } catch {
3548
+ data = {};
3549
+ }
3550
+ return parseOpenAiCompatibleChatCompletionResponse({
3551
+ providerConfig: args2.providerConfig,
3552
+ data,
3553
+ trace: args2.messages
3554
+ });
3555
+ }
3394
3556
  var init_openAiCompatibleProvider = __esm({
3395
3557
  "packages/agent-runtime/openAiCompatibleProvider.ts"() {
3396
3558
  "use strict";
@@ -3688,7 +3850,8 @@ function buildPlatformChatCompletionRequest(args2) {
3688
3850
  const body = {
3689
3851
  model: args2.providerConfig.model,
3690
3852
  ...usesResponsesApi ? { input: convertMessagesToResponsesInput(args2.messages) } : { messages: toOpenAiCompatibleMessages2(args2.messages) },
3691
- stream: false,
3853
+ stream: args2.stream ?? false,
3854
+ ...args2.stream ? { stream_options: { include_usage: true } } : {},
3692
3855
  ...requestOptions,
3693
3856
  ...args2.tools && args2.tools.length > 0 ? {
3694
3857
  tools: usesResponsesApi ? toResponsesTools(args2.tools) : args2.tools,
@@ -10118,6 +10281,8 @@ var init_ai_locale = __esm({
10118
10281
  creating: "Creating...",
10119
10282
  edit: "Edit",
10120
10283
  delete: "Delete",
10284
+ favorite: "Favorite",
10285
+ unfavorite: "Unfavorite",
10121
10286
  confirmDelete: "Confirm Delete",
10122
10287
  resetToDefaults: "Reset to Defaults",
10123
10288
  loading: "Loading...",
@@ -10429,6 +10594,8 @@ var init_ai_locale = __esm({
10429
10594
  creating: "\u521B\u5EFA\u4E2D...",
10430
10595
  edit: "\u7F16\u8F91",
10431
10596
  delete: "\u5220\u9664",
10597
+ favorite: "\u6536\u85CF",
10598
+ unfavorite: "\u53D6\u6D88\u6536\u85CF",
10432
10599
  confirmDelete: "\u786E\u8BA4\u5220\u9664",
10433
10600
  resetToDefaults: "\u91CD\u7F6E\u4E3A\u9ED8\u8BA4\u503C",
10434
10601
  loading: "\u52A0\u8F7D\u4E2D...",
@@ -10741,6 +10908,8 @@ var init_ai_locale = __esm({
10741
10908
  creating: "\u5EFA\u7ACB\u4E2D...",
10742
10909
  edit: "\u7DE8\u8F2F",
10743
10910
  delete: "\u522A\u9664",
10911
+ favorite: "\u6536\u85CF",
10912
+ unfavorite: "\u53D6\u6D88\u6536\u85CF",
10744
10913
  confirmDelete: "\u78BA\u8A8D\u522A\u9664",
10745
10914
  resetToDefaults: "\u91CD\u8A2D\u70BA\u9810\u8A2D\u503C",
10746
10915
  loading: "\u8F09\u5165\u4E2D...",
@@ -10976,6 +11145,8 @@ var init_ai_locale = __esm({
10976
11145
  creating: "\u4F5C\u6210\u4E2D...",
10977
11146
  edit: "\u7DE8\u96C6",
10978
11147
  delete: "\u524A\u9664",
11148
+ favorite: "\u304A\u6C17\u306B\u5165\u308A",
11149
+ unfavorite: "\u304A\u6C17\u306B\u5165\u308A\u89E3\u9664",
10979
11150
  confirmDelete: "\u524A\u9664\u306E\u78BA\u8A8D",
10980
11151
  resetToDefaults: "\u30C7\u30D5\u30A9\u30EB\u30C8\u306B\u30EA\u30BB\u30C3\u30C8",
10981
11152
  loading: "\u8AAD\u307F\u8FBC\u307F\u4E2D...",
@@ -13051,6 +13222,31 @@ var init_interface_locale = __esm({
13051
13222
  stop: "Stop",
13052
13223
  refresh: "Refresh"
13053
13224
  },
13225
+ productivity: {
13226
+ title: "Productivity",
13227
+ default_space: {
13228
+ title: "Default startup space",
13229
+ description: "Choose the workspace you land in after signing in to get into flow faster.",
13230
+ all: "All (no fixed default space)"
13231
+ },
13232
+ shortcuts: {
13233
+ title: "Keyboard shortcuts",
13234
+ description: "View and customize shortcuts for common actions to keep your workflow smooth.",
13235
+ sendMessage: "Send message",
13236
+ newChat: "New chat",
13237
+ deleteDialog: "Delete current conversation",
13238
+ clickToRecord: "Click to change shortcut",
13239
+ updated: "Shortcut updated",
13240
+ pressKeys: "Press shortcut keys...",
13241
+ none: "Disabled",
13242
+ cleared: "Shortcut disabled",
13243
+ clearError: "Failed to disable shortcut",
13244
+ clear: "Disable shortcut",
13245
+ resetSuccess: "Shortcut reset",
13246
+ resetError: "Failed to reset shortcut",
13247
+ reset: "Reset to default"
13248
+ }
13249
+ },
13054
13250
  appearance: {
13055
13251
  title: "Appearance",
13056
13252
  theme: {
@@ -13744,6 +13940,31 @@ var init_interface_locale = __esm({
13744
13940
  productivity: "\u6548\u7387",
13745
13941
  secrets: "\u5BC6\u94A5"
13746
13942
  },
13943
+ productivity: {
13944
+ title: "\u6548\u7387\u8BBE\u7F6E",
13945
+ default_space: {
13946
+ title: "\u9ED8\u8BA4\u542F\u52A8\u7A7A\u95F4",
13947
+ 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",
13948
+ all: "\u5168\u90E8\uFF08\u4E0D\u56FA\u5B9A\u9ED8\u8BA4\u7A7A\u95F4\uFF09"
13949
+ },
13950
+ shortcuts: {
13951
+ title: "\u952E\u76D8\u5FEB\u6377\u952E",
13952
+ 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",
13953
+ sendMessage: "\u53D1\u9001\u6D88\u606F",
13954
+ newChat: "\u65B0\u5EFA\u5BF9\u8BDD",
13955
+ deleteDialog: "\u5220\u9664\u5F53\u524D\u4F1A\u8BDD",
13956
+ clickToRecord: "\u70B9\u51FB\u4FEE\u6539\u5FEB\u6377\u952E",
13957
+ updated: "\u5FEB\u6377\u952E\u5DF2\u66F4\u65B0",
13958
+ pressKeys: "\u6309\u4E0B\u5FEB\u6377\u952E...",
13959
+ none: "\u5DF2\u7981\u7528",
13960
+ cleared: "\u5FEB\u6377\u952E\u5DF2\u7981\u7528",
13961
+ clearError: "\u7981\u7528\u5931\u8D25",
13962
+ clear: "\u7981\u7528\u5FEB\u6377\u952E",
13963
+ resetSuccess: "\u5FEB\u6377\u952E\u5DF2\u91CD\u7F6E",
13964
+ resetError: "\u91CD\u7F6E\u5931\u8D25",
13965
+ reset: "\u91CD\u7F6E\u4E3A\u9ED8\u8BA4"
13966
+ }
13967
+ },
13747
13968
  appearance: {
13748
13969
  title: "\u5916\u89C2",
13749
13970
  theme: {
@@ -14438,6 +14659,31 @@ var init_interface_locale = __esm({
14438
14659
  productivity: "\u6548\u7387",
14439
14660
  secrets: "\u5BC6\u9470"
14440
14661
  },
14662
+ productivity: {
14663
+ title: "\u6548\u7387\u8A2D\u5B9A",
14664
+ default_space: {
14665
+ title: "\u9810\u8A2D\u555F\u52D5\u7A7A\u9593",
14666
+ 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",
14667
+ all: "\u5168\u90E8\uFF08\u4E0D\u56FA\u5B9A\u9810\u8A2D\u7A7A\u9593\uFF09"
14668
+ },
14669
+ shortcuts: {
14670
+ title: "\u9375\u76E4\u5FEB\u6377\u9375",
14671
+ 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",
14672
+ sendMessage: "\u50B3\u9001\u8A0A\u606F",
14673
+ newChat: "\u65B0\u5EFA\u5C0D\u8A71",
14674
+ deleteDialog: "\u522A\u9664\u76EE\u524D\u6703\u8A71",
14675
+ clickToRecord: "\u9EDE\u64CA\u4FEE\u6539\u5FEB\u6377\u9375",
14676
+ updated: "\u5FEB\u6377\u9375\u5DF2\u66F4\u65B0",
14677
+ pressKeys: "\u6309\u4E0B\u5FEB\u6377\u9375...",
14678
+ none: "\u5DF2\u505C\u7528",
14679
+ cleared: "\u5FEB\u6377\u9375\u5DF2\u505C\u7528",
14680
+ clearError: "\u505C\u7528\u5931\u6557",
14681
+ clear: "\u505C\u7528\u5FEB\u6377\u9375",
14682
+ resetSuccess: "\u5FEB\u6377\u9375\u5DF2\u91CD\u8A2D",
14683
+ resetError: "\u91CD\u8A2D\u5931\u6557",
14684
+ reset: "\u91CD\u8A2D\u70BA\u9810\u8A2D"
14685
+ }
14686
+ },
14441
14687
  appearance: {
14442
14688
  title: "\u5916\u89C0",
14443
14689
  theme: {
@@ -15130,6 +15376,31 @@ var init_interface_locale = __esm({
15130
15376
  productivity: "\u52B9\u7387",
15131
15377
  secrets: "\u30B7\u30FC\u30AF\u30EC\u30C3\u30C8"
15132
15378
  },
15379
+ productivity: {
15380
+ title: "\u52B9\u7387\u8A2D\u5B9A",
15381
+ default_space: {
15382
+ title: "\u65E2\u5B9A\u306E\u8D77\u52D5\u30B9\u30DA\u30FC\u30B9",
15383
+ 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",
15384
+ all: "\u3059\u3079\u3066\uFF08\u65E2\u5B9A\u30B9\u30DA\u30FC\u30B9\u3092\u56FA\u5B9A\u3057\u306A\u3044\uFF09"
15385
+ },
15386
+ shortcuts: {
15387
+ title: "\u30AD\u30FC\u30DC\u30FC\u30C9\u30B7\u30E7\u30FC\u30C8\u30AB\u30C3\u30C8",
15388
+ 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",
15389
+ sendMessage: "\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u9001\u4FE1",
15390
+ newChat: "\u65B0\u3057\u3044\u30C1\u30E3\u30C3\u30C8",
15391
+ deleteDialog: "\u73FE\u5728\u306E\u4F1A\u8A71\u3092\u524A\u9664",
15392
+ clickToRecord: "\u30AF\u30EA\u30C3\u30AF\u3057\u3066\u30B7\u30E7\u30FC\u30C8\u30AB\u30C3\u30C8\u3092\u5909\u66F4",
15393
+ updated: "\u30B7\u30E7\u30FC\u30C8\u30AB\u30C3\u30C8\u3092\u66F4\u65B0\u3057\u307E\u3057\u305F",
15394
+ pressKeys: "\u30B7\u30E7\u30FC\u30C8\u30AB\u30C3\u30C8\u30AD\u30FC\u3092\u62BC\u3057\u3066\u304F\u3060\u3055\u3044...",
15395
+ none: "\u7121\u52B9",
15396
+ cleared: "\u30B7\u30E7\u30FC\u30C8\u30AB\u30C3\u30C8\u3092\u7121\u52B9\u306B\u3057\u307E\u3057\u305F",
15397
+ clearError: "\u7121\u52B9\u5316\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
15398
+ clear: "\u30B7\u30E7\u30FC\u30C8\u30AB\u30C3\u30C8\u3092\u7121\u52B9\u5316",
15399
+ resetSuccess: "\u30B7\u30E7\u30FC\u30C8\u30AB\u30C3\u30C8\u3092\u30EA\u30BB\u30C3\u30C8\u3057\u307E\u3057\u305F",
15400
+ resetError: "\u30EA\u30BB\u30C3\u30C8\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
15401
+ reset: "\u65E2\u5B9A\u306B\u623B\u3059"
15402
+ }
15403
+ },
15133
15404
  appearance: {
15134
15405
  title: "\u5916\u89B3",
15135
15406
  theme: {
@@ -20768,6 +21039,7 @@ __export(settingSlice_exports, {
20768
21039
  selectDefaultAgentId: () => selectDefaultAgentId,
20769
21040
  selectDefaultAgentPreference: () => selectDefaultAgentPreference,
20770
21041
  selectDefaultSpaceId: () => selectDefaultSpaceId,
21042
+ selectDeleteShortcut: () => selectDeleteShortcut,
20771
21043
  selectDensity: () => selectDensity,
20772
21044
  selectDesktopChromeConnectorEnabled: () => selectDesktopChromeConnectorEnabled,
20773
21045
  selectEditorAutoSave: () => selectEditorAutoSave,
@@ -20836,7 +21108,7 @@ import {
20836
21108
  asyncThunkCreator,
20837
21109
  createSelector as createSelector2
20838
21110
  } 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;
21111
+ 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
21112
  var init_settingSlice = __esm({
20841
21113
  "packages/app/settings/settingSlice.tsx"() {
20842
21114
  "use strict";
@@ -20909,7 +21181,8 @@ var init_settingSlice = __esm({
20909
21181
  showScrollToTopButton: false,
20910
21182
  showScrollToBottomButton: false,
20911
21183
  createMenuOpenCount: 0,
20912
- desktopChromeConnectorEnabled: false
21184
+ desktopChromeConnectorEnabled: false,
21185
+ deleteShortcut: typeof window !== "undefined" && typeof window.navigator !== "undefined" && /Mac|iPod|iPhone|iPad/.test(window.navigator.platform) ? "meta+backspace" : "ctrl+backspace"
20913
21186
  };
20914
21187
  createSliceWithThunks = buildCreateSlice({
20915
21188
  creators: { asyncThunk: asyncThunkCreator }
@@ -21537,6 +21810,7 @@ var init_settingSlice = __esm({
21537
21810
  );
21538
21811
  selectEditorWordCountEnabled = (state) => state.settings.editorWordCountEnabled;
21539
21812
  selectEditorShortcuts = (state) => state.settings.editorShortcuts;
21813
+ selectDeleteShortcut = (state) => state.settings.deleteShortcut;
21540
21814
  selectEditorFontSize = (state) => state.settings.editorFontSize;
21541
21815
  selectEditorAutoSave = (state) => state.settings.editorAutoSave;
21542
21816
  selectEditorAutoSaveInterval = (state) => state.settings.editorAutoSaveInterval;
@@ -63690,14 +63964,6 @@ function summarizeEndpoint(value) {
63690
63964
  return "invalid-url";
63691
63965
  }
63692
63966
  }
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
63967
  function isCliProviderAgent(agentConfig) {
63702
63968
  return Boolean(
63703
63969
  agentConfig.apiSource === "cli" || agentConfig.provider === "cli" || agentConfig.cliProvider
@@ -64463,12 +64729,15 @@ function createCliLocalRuntimeAdapter(deps) {
64463
64729
  toolNames: requestedToolNames,
64464
64730
  env: deps.env
64465
64731
  });
64732
+ const timeoutSignal = buildRequestTimeoutSignal(options?.timeoutMs);
64733
+ const usesResponsesApi = providerConfig2.endpoint.includes("/responses");
64734
+ const stream = Boolean(options?.onTextDelta) && !usesResponsesApi;
64466
64735
  const request = buildPlatformChatCompletionRequest({
64467
64736
  providerConfig: providerConfig2,
64468
64737
  messages,
64469
- tools
64738
+ tools,
64739
+ stream
64470
64740
  });
64471
- const timeoutSignal = buildRequestTimeoutSignal(options?.timeoutMs);
64472
64741
  logLocalRuntimeDiagnostic("provider.request.start", {
64473
64742
  agentKey: agentConfig.key,
64474
64743
  transport: "platform-proxy",
@@ -64479,32 +64748,64 @@ function createCliLocalRuntimeAdapter(deps) {
64479
64748
  toolCount: tools.length,
64480
64749
  requestedToolNames,
64481
64750
  openAiToolNames: summarizeOpenAiToolNames(tools),
64482
- timeoutMs: options?.timeoutMs ?? null
64751
+ timeoutMs: options?.timeoutMs ?? null,
64752
+ stream
64483
64753
  });
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
64498
- });
64499
- const data = parsePlatformChatCompletionData(raw);
64500
- if (!res.ok) {
64501
- throw new Error(`platform provider failed: HTTP ${res.status} ${JSON.stringify(data)}`);
64754
+ try {
64755
+ const res = await fetchWithTransientRetry(fetchImpl, request.url, {
64756
+ ...request.init,
64757
+ ...timeoutSignal ? { signal: timeoutSignal.signal } : {}
64758
+ }, {
64759
+ sleep: deps.sleep,
64760
+ loopbackRequest
64761
+ });
64762
+ if (!res.ok) {
64763
+ const raw2 = await res.text().catch(() => "");
64764
+ const data2 = parsePlatformChatCompletionData(raw2);
64765
+ throw new Error(`platform provider failed: HTTP ${res.status} ${JSON.stringify(data2)}`);
64766
+ }
64767
+ const contentType = res.headers.get("content-type") ?? "";
64768
+ const shouldStream = Boolean(stream && options?.onTextDelta) && contentType.includes("text/event-stream");
64769
+ if (shouldStream && options?.onTextDelta) {
64770
+ const streamed = await readOpenAiCompatibleSseCompletion({
64771
+ response: res,
64772
+ onTextDelta: options.onTextDelta
64773
+ });
64774
+ logLocalRuntimeDiagnostic("provider.request.result", {
64775
+ agentKey: agentConfig.key,
64776
+ transport: "platform-proxy",
64777
+ ok: true,
64778
+ stream: true,
64779
+ contentChars: streamed.content.length,
64780
+ toolCallCount: streamed.tool_calls?.length ?? 0
64781
+ });
64782
+ return {
64783
+ content: streamed.content,
64784
+ model: providerConfig2.model,
64785
+ provider: providerConfig2.provider,
64786
+ ...streamed.tool_calls ? { tool_calls: streamed.tool_calls } : {},
64787
+ ...streamed.reasoning_content ? { reasoning_content: streamed.reasoning_content } : {},
64788
+ ...streamed.usage ? { usage: streamed.usage } : {},
64789
+ trace: messages
64790
+ };
64791
+ }
64792
+ const raw = await res.text().catch(() => "");
64793
+ logLocalRuntimeDiagnostic("provider.request.result", {
64794
+ agentKey: agentConfig.key,
64795
+ transport: "platform-proxy",
64796
+ status: res.status,
64797
+ ok: res.ok,
64798
+ responseBytes: raw.length
64799
+ });
64800
+ const data = parsePlatformChatCompletionData(raw);
64801
+ return parsePlatformChatCompletionResponse({
64802
+ providerConfig: providerConfig2,
64803
+ data,
64804
+ trace: messages
64805
+ });
64806
+ } finally {
64807
+ timeoutSignal?.clear();
64502
64808
  }
64503
- return parsePlatformChatCompletionResponse({
64504
- providerConfig: providerConfig2,
64505
- data,
64506
- trace: messages
64507
- });
64508
64809
  }
64509
64810
  };
64510
64811
  }
@@ -64536,47 +64837,45 @@ function createCliLocalRuntimeAdapter(deps) {
64536
64837
  toolNames: requestedToolNames,
64537
64838
  env: deps.env
64538
64839
  });
64539
- const request = buildOpenAiCompatibleChatCompletionRequest({
64540
- providerConfig,
64541
- messages,
64542
- tools
64543
- });
64544
64840
  const timeoutSignal = buildRequestTimeoutSignal(options?.timeoutMs);
64841
+ const stream = Boolean(options?.onTextDelta);
64545
64842
  logLocalRuntimeDiagnostic("provider.request.start", {
64546
64843
  agentKey: agentConfig.key,
64547
64844
  transport: "direct-openai-compatible",
64548
- requestUrl: summarizeEndpoint(request.url) ?? null,
64845
+ requestUrl: summarizeEndpoint(providerConfig.endpoint) ?? null,
64549
64846
  model: providerConfig.model,
64550
64847
  messageCount: messages.length,
64551
64848
  toolCount: tools.length,
64552
64849
  requestedToolNames,
64553
64850
  openAiToolNames: summarizeOpenAiToolNames(tools),
64554
- timeoutMs: options?.timeoutMs ?? null
64851
+ timeoutMs: options?.timeoutMs ?? null,
64852
+ stream
64555
64853
  });
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)}`);
64854
+ try {
64855
+ const result = await executeOpenAiCompatibleChatCompletion({
64856
+ providerConfig,
64857
+ messages,
64858
+ tools,
64859
+ fetchImpl: (url, init) => fetchWithTransientRetry(fetchImpl, url, init, {
64860
+ sleep: deps.sleep,
64861
+ loopbackRequest
64862
+ }),
64863
+ stream,
64864
+ onTextDelta: options?.onTextDelta,
64865
+ signal: timeoutSignal?.signal
64866
+ });
64867
+ logLocalRuntimeDiagnostic("provider.request.result", {
64868
+ agentKey: agentConfig.key,
64869
+ transport: "direct-openai-compatible",
64870
+ ok: true,
64871
+ stream,
64872
+ contentChars: result.content.length,
64873
+ toolCallCount: result.tool_calls?.length ?? 0
64874
+ });
64875
+ return result;
64876
+ } finally {
64877
+ timeoutSignal?.clear();
64574
64878
  }
64575
- return parseOpenAiCompatibleChatCompletionResponse({
64576
- providerConfig,
64577
- data,
64578
- trace: messages
64579
- });
64580
64879
  }
64581
64880
  };
64582
64881
  },
@@ -67743,6 +68042,470 @@ function createStreamingTextWriter({
67743
68042
  };
67744
68043
  }
67745
68044
 
68045
+ // packages/cli/client/assistantOutput.ts
68046
+ var ANSI = {
68047
+ reset: "\x1B[0m",
68048
+ bold: "\x1B[1m",
68049
+ dim: "\x1B[2m",
68050
+ cyan: "\x1B[36m"
68051
+ };
68052
+ function normalizeRenderDisplayMode(raw, fallback = "rich") {
68053
+ const normalized = raw?.trim().toLowerCase();
68054
+ if (normalized === "plain" || normalized === "raw" || normalized === "off" || normalized === "0") {
68055
+ return "plain";
68056
+ }
68057
+ if (normalized === "rich" || normalized === "on" || normalized === "1" || normalized === "styled") {
68058
+ return "rich";
68059
+ }
68060
+ return fallback;
68061
+ }
68062
+ function resolveRenderDisplayMode(env = process.env) {
68063
+ return normalizeRenderDisplayMode(env.NOLO_CLI_RENDER ?? env.NOLO_RENDER, "rich");
68064
+ }
68065
+ function splitTableCells(line) {
68066
+ const trimmed = line.trim();
68067
+ if (!trimmed.includes("|")) return [];
68068
+ const core = trimmed.replace(/^\|/, "").replace(/\|$/, "");
68069
+ return core.split("|").map((cell) => cell.trim()).filter(Boolean);
68070
+ }
68071
+ function isTableSeparator(line) {
68072
+ const cells = splitTableCells(line);
68073
+ return cells.length > 0 && cells.every((cell) => /^:?-{3,}:?$/.test(cell));
68074
+ }
68075
+ function isTableRow(line) {
68076
+ const cells = splitTableCells(line);
68077
+ return cells.length >= 2 && cells.some((cell) => cell.length > 0);
68078
+ }
68079
+ function convertMarkdownTablesForTerminal(text) {
68080
+ const lines = text.split("\n");
68081
+ const out = [];
68082
+ for (let index = 0; index < lines.length; index += 1) {
68083
+ const line = lines[index] ?? "";
68084
+ const next = lines[index + 1] ?? "";
68085
+ if (isTableRow(line) && isTableSeparator(next)) {
68086
+ const headers = splitTableCells(line);
68087
+ index += 1;
68088
+ while (index + 1 < lines.length && isTableRow(lines[index + 1] ?? "") && !isTableSeparator(lines[index + 1] ?? "")) {
68089
+ index += 1;
68090
+ const row = splitTableCells(lines[index] ?? "");
68091
+ const label = row[0] ?? "";
68092
+ const detail = row.slice(1).join(" \u2014 ").trim();
68093
+ out.push(detail ? ` \u2022 ${label} \u2014 ${detail}` : ` \u2022 ${label}`);
68094
+ }
68095
+ if (out.length > 0 && out[out.length - 1] !== "") out.push("");
68096
+ continue;
68097
+ }
68098
+ out.push(line);
68099
+ }
68100
+ return out.join("\n");
68101
+ }
68102
+ function polishAssistantStructure(text) {
68103
+ 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();
68104
+ }
68105
+ function styleInlineMarkdown(line, mode) {
68106
+ if (mode === "plain") return line;
68107
+ return line.replace(/\*\*(.+?)\*\*/g, `${ANSI.bold}$1${ANSI.reset}`);
68108
+ }
68109
+ function styleRichMarkdownLine(line) {
68110
+ const heading = line.match(/^(#{1,3})\s+(.+)$/);
68111
+ if (heading) {
68112
+ const level = heading[1].length;
68113
+ const title = heading[2];
68114
+ if (level <= 2) return `${ANSI.bold}${title}${ANSI.reset}`;
68115
+ return `${ANSI.cyan}${title}${ANSI.reset}`;
68116
+ }
68117
+ if (/^---+$/.test(line.trim())) {
68118
+ return `${ANSI.dim}${line}${ANSI.reset}`;
68119
+ }
68120
+ return styleInlineMarkdown(line, "rich");
68121
+ }
68122
+ function formatAssistantDisplay(text, mode = "rich") {
68123
+ const polished = polishAssistantStructure(text);
68124
+ if (mode === "plain") {
68125
+ return polished.split("\n").map((line) => styleInlineMarkdown(line, "plain")).join("\n");
68126
+ }
68127
+ return polished.split("\n").map((line) => styleRichMarkdownLine(line)).join("\n");
68128
+ }
68129
+ function emitFormattedAssistantBlock(write2, text, renderMode, trailingNewline = false) {
68130
+ if (!text) return;
68131
+ write2(formatAssistantDisplay(text, renderMode));
68132
+ if (trailingNewline) write2("\n");
68133
+ }
68134
+ function createRenderAwareStreamWriter(args2) {
68135
+ let buffer = "";
68136
+ const flushCompleteBlocks = () => {
68137
+ if (args2.renderMode === "plain") {
68138
+ if (!buffer) return;
68139
+ args2.write(buffer);
68140
+ buffer = "";
68141
+ return;
68142
+ }
68143
+ while (buffer.includes("\n")) {
68144
+ const lines = buffer.split("\n");
68145
+ if (lines.length < 2) break;
68146
+ if (isTableRow(lines[0] ?? "") && isTableSeparator(lines[1] ?? "")) {
68147
+ let end = 2;
68148
+ while (end < lines.length && isTableRow(lines[end] ?? "") && !isTableSeparator(lines[end] ?? "")) {
68149
+ end += 1;
68150
+ }
68151
+ const tableComplete = end < lines.length || buffer.endsWith("\n");
68152
+ if (!tableComplete) break;
68153
+ emitFormattedAssistantBlock(
68154
+ args2.write,
68155
+ lines.slice(0, end).join("\n"),
68156
+ args2.renderMode,
68157
+ true
68158
+ );
68159
+ buffer = lines.slice(end).join("\n");
68160
+ continue;
68161
+ }
68162
+ emitFormattedAssistantBlock(args2.write, lines[0] ?? "", args2.renderMode, true);
68163
+ buffer = lines.slice(1).join("\n");
68164
+ }
68165
+ };
68166
+ return {
68167
+ push(chunk) {
68168
+ if (!chunk) return;
68169
+ if (args2.renderMode === "plain") {
68170
+ args2.write(chunk);
68171
+ return;
68172
+ }
68173
+ buffer += chunk;
68174
+ flushCompleteBlocks();
68175
+ },
68176
+ flush() {
68177
+ if (!buffer) return;
68178
+ if (args2.renderMode === "plain") {
68179
+ args2.write(buffer);
68180
+ } else {
68181
+ emitFormattedAssistantBlock(args2.write, buffer, args2.renderMode);
68182
+ }
68183
+ buffer = "";
68184
+ }
68185
+ };
68186
+ }
68187
+
68188
+ // packages/cli/client/thinkingOutput.ts
68189
+ var THINK_OPEN = /<think>/i;
68190
+ var THINK_CLOSE = /<\/think>/i;
68191
+ var COLLAPSED_MARKER = /^\s*▸ 思考已折叠\s*$/;
68192
+ function normalizeThinkingDisplayMode(raw, fallback = "hide") {
68193
+ const normalized = raw?.trim().toLowerCase();
68194
+ if (normalized === "hide" || normalized === "off" || normalized === "false" || normalized === "0") {
68195
+ return "hide";
68196
+ }
68197
+ if (normalized === "marker" || normalized === "collapsed" || normalized === "fold") {
68198
+ return "marker";
68199
+ }
68200
+ if (normalized === "show" || normalized === "on" || normalized === "true" || normalized === "1") {
68201
+ return "show";
68202
+ }
68203
+ return fallback;
68204
+ }
68205
+ function resolveThinkingDisplayMode(env = process.env) {
68206
+ return normalizeThinkingDisplayMode(env.NOLO_CLI_THINKING ?? env.NOLO_THINKING, "hide");
68207
+ }
68208
+ function collapseThinkingBlocks(text, mode = "hide") {
68209
+ if (mode === "show") return text;
68210
+ const replacement = mode === "marker" ? "\u25B8 \u601D\u8003\u5DF2\u6298\u53E0\n" : "";
68211
+ return text.replace(/<think>[\s\S]*?<\/think>\s*/gi, replacement).replace(/\n{3,}/g, "\n\n").trim();
68212
+ }
68213
+ function stripCollapsedThinkingMarkers(text) {
68214
+ return text.split("\n").filter((line) => !COLLAPSED_MARKER.test(line)).join("\n").replace(/\n{2,}/g, "\n").trim();
68215
+ }
68216
+ function formatAssistantTextForCli(text, mode = "hide") {
68217
+ const collapsed = collapseThinkingBlocks(text, mode);
68218
+ return mode === "hide" ? stripCollapsedThinkingMarkers(collapsed) : collapsed;
68219
+ }
68220
+ function createThinkingAwareStreamFilter(write2, mode = "hide") {
68221
+ if (mode === "show") {
68222
+ return {
68223
+ push(chunk) {
68224
+ write2(chunk);
68225
+ },
68226
+ flush() {
68227
+ }
68228
+ };
68229
+ }
68230
+ let pending = "";
68231
+ let insideThink = false;
68232
+ let markerEmitted = false;
68233
+ const emit = (chunk) => {
68234
+ if (chunk) write2(chunk);
68235
+ };
68236
+ const emitMarker = () => {
68237
+ if (mode !== "marker" || markerEmitted) return;
68238
+ emit("\n\u25B8 \u601D\u8003\u5DF2\u6298\u53E0\n");
68239
+ markerEmitted = true;
68240
+ };
68241
+ const consume = (input2) => {
68242
+ pending += input2;
68243
+ while (pending.length > 0) {
68244
+ if (insideThink) {
68245
+ const closeMatch = pending.match(THINK_CLOSE);
68246
+ if (!closeMatch || closeMatch.index == null) {
68247
+ emitMarker();
68248
+ pending = "";
68249
+ return;
68250
+ }
68251
+ emitMarker();
68252
+ pending = pending.slice(closeMatch.index + closeMatch[0].length);
68253
+ insideThink = false;
68254
+ markerEmitted = false;
68255
+ continue;
68256
+ }
68257
+ const openMatch = pending.match(THINK_OPEN);
68258
+ if (!openMatch || openMatch.index == null) {
68259
+ const nextMarker = pending.indexOf("<");
68260
+ if (nextMarker === -1) {
68261
+ const cleaned = mode === "hide" ? pending.split("\n").filter((line) => !COLLAPSED_MARKER.test(line)).join("\n") : pending;
68262
+ emit(cleaned);
68263
+ pending = "";
68264
+ return;
68265
+ }
68266
+ if (nextMarker > 0) {
68267
+ const head = pending.slice(0, nextMarker);
68268
+ const cleaned = mode === "hide" ? head.split("\n").filter((line) => !COLLAPSED_MARKER.test(line)).join("\n") : head;
68269
+ emit(cleaned);
68270
+ pending = pending.slice(nextMarker);
68271
+ continue;
68272
+ }
68273
+ if (pending.length < 7) return;
68274
+ emit(pending[0]);
68275
+ pending = pending.slice(1);
68276
+ continue;
68277
+ }
68278
+ if (openMatch.index > 0) {
68279
+ const head = pending.slice(0, openMatch.index);
68280
+ const cleaned = mode === "hide" ? head.split("\n").filter((line) => !COLLAPSED_MARKER.test(line)).join("\n") : head;
68281
+ emit(cleaned);
68282
+ pending = pending.slice(openMatch.index);
68283
+ continue;
68284
+ }
68285
+ pending = pending.slice(openMatch[0].length);
68286
+ insideThink = true;
68287
+ markerEmitted = false;
68288
+ }
68289
+ };
68290
+ return {
68291
+ push(chunk) {
68292
+ consume(chunk);
68293
+ },
68294
+ flush() {
68295
+ if (insideThink) emitMarker();
68296
+ if (pending) {
68297
+ const cleaned = mode === "hide" ? pending.split("\n").filter((line) => !COLLAPSED_MARKER.test(line)).join("\n") : pending;
68298
+ emit(cleaned);
68299
+ }
68300
+ pending = "";
68301
+ insideThink = false;
68302
+ markerEmitted = false;
68303
+ }
68304
+ };
68305
+ }
68306
+
68307
+ // packages/cli/client/tokenUsage.ts
68308
+ init_providers();
68309
+ var PROVIDER_LOOKUP_ORDER = [
68310
+ "openrouter",
68311
+ "fireworks",
68312
+ "openai",
68313
+ "mimo",
68314
+ "gmi",
68315
+ "google",
68316
+ "deepseek",
68317
+ "mistral",
68318
+ "vultr",
68319
+ "deepinfra",
68320
+ "cloudflare"
68321
+ ];
68322
+ function parseUsageRecord(usage2) {
68323
+ if (!usage2 || typeof usage2 !== "object") return void 0;
68324
+ const input2 = Number(usage2.input_tokens ?? usage2.prompt_tokens ?? 0);
68325
+ const output2 = Number(usage2.output_tokens ?? usage2.completion_tokens ?? 0);
68326
+ if (!Number.isFinite(input2) || !Number.isFinite(output2)) return void 0;
68327
+ if (!input2 && !output2) return void 0;
68328
+ return { input: input2, output: output2 };
68329
+ }
68330
+ function fuzzyContextWindow(model) {
68331
+ const lower = model.toLowerCase();
68332
+ if (lower.includes("minimax-m3") || lower.includes("minimax_m3")) return 1e6;
68333
+ if (lower.includes("minimax-m2")) return 262144;
68334
+ if (lower.includes("gpt-5") || lower.includes("gpt-4.1")) return 1047576;
68335
+ if (lower.includes("claude")) return 2e5;
68336
+ return void 0;
68337
+ }
68338
+ function resolveContextWindow(model) {
68339
+ const raw = model?.trim();
68340
+ if (!raw) return void 0;
68341
+ for (const provider of PROVIDER_LOOKUP_ORDER) {
68342
+ const config = findModelConfig(provider, raw);
68343
+ if (config?.contextWindow) return config.contextWindow;
68344
+ }
68345
+ return fuzzyContextWindow(raw);
68346
+ }
68347
+ function buildTurnTokenUsage(usage2, model) {
68348
+ const parsed = parseUsageRecord(usage2);
68349
+ if (!parsed) return void 0;
68350
+ const contextWindow = resolveContextWindow(model);
68351
+ const remaining = contextWindow && parsed.input > 0 ? Math.max(0, contextWindow - parsed.input) : void 0;
68352
+ return {
68353
+ ...parsed,
68354
+ ...contextWindow ? { contextWindow } : {},
68355
+ ...remaining != null ? { remaining } : {}
68356
+ };
68357
+ }
68358
+ function formatTokenCount(value) {
68359
+ if (!Number.isFinite(value) || value < 0) return "\u2014";
68360
+ if (value < 1e3) return String(Math.round(value));
68361
+ if (value < 1e6) {
68362
+ const compact3 = value / 1e3;
68363
+ if (Number.isInteger(compact3)) return `${compact3}k`;
68364
+ return `${compact3.toFixed(1).replace(/\.0$/, "")}k`;
68365
+ }
68366
+ const compact2 = value / 1e6;
68367
+ return compact2 >= 100 ? `${Math.round(compact2)}M` : `${compact2.toFixed(1).replace(/\.0$/, "")}M`;
68368
+ }
68369
+ function renderTokenStatus(tokens) {
68370
+ if (!tokens) return "in \u2014 out \u2014 left \u2014";
68371
+ const left = tokens.remaining != null ? formatTokenCount(tokens.remaining) : tokens.contextWindow ? "\u2014" : "\u2014";
68372
+ return `in ${formatTokenCount(tokens.input)} out ${formatTokenCount(tokens.output)} left ${left}`;
68373
+ }
68374
+
68375
+ // packages/cli/client/terminalStyles.ts
68376
+ var ANSI2 = {
68377
+ dim: "\x1B[2m",
68378
+ bold: "\x1B[1m",
68379
+ cyan: "\x1B[36m",
68380
+ green: "\x1B[32m",
68381
+ red: "\x1B[31m"
68382
+ };
68383
+ var RESET = "\x1B[0m";
68384
+ function resolveCliColorEnabled(env = process.env, isTTY = Boolean(process.stdout.isTTY)) {
68385
+ const setting = (env.NOLO_CLI_COLOR ?? "").trim().toLowerCase();
68386
+ if (setting === "0" || setting === "false" || setting === "off") return false;
68387
+ if (env.NO_COLOR) return false;
68388
+ if (setting === "1" || setting === "true" || setting === "on") return true;
68389
+ return isTTY;
68390
+ }
68391
+ function styleCliText(text, style, enabled = resolveCliColorEnabled()) {
68392
+ if (!enabled || !text) return text;
68393
+ return `${ANSI2[style]}${text}${RESET}`;
68394
+ }
68395
+ function dimCliText(text, enabled = resolveCliColorEnabled()) {
68396
+ return styleCliText(text, "dim", enabled);
68397
+ }
68398
+
68399
+ // packages/cli/client/toolOutput.ts
68400
+ function normalizeToolDisplayMode(raw, fallback = "compact") {
68401
+ const normalized = raw?.trim().toLowerCase();
68402
+ if (normalized === "hide" || normalized === "off" || normalized === "false" || normalized === "0") {
68403
+ return "hide";
68404
+ }
68405
+ if (normalized === "verbose" || normalized === "debug" || normalized === "trace" || normalized === "full") {
68406
+ return "verbose";
68407
+ }
68408
+ if (normalized === "compact" || normalized === "minimal" || normalized === "short") {
68409
+ return "compact";
68410
+ }
68411
+ return fallback;
68412
+ }
68413
+ function resolveToolDisplayMode(env = process.env) {
68414
+ const legacyTrace = env.NOLO_TRACE_TOOLS?.trim().toLowerCase();
68415
+ if (legacyTrace === "0" || legacyTrace === "false" || legacyTrace === "off") {
68416
+ return "hide";
68417
+ }
68418
+ if (legacyTrace === "verbose" || legacyTrace === "full") {
68419
+ return "verbose";
68420
+ }
68421
+ return normalizeToolDisplayMode(env.NOLO_CLI_TOOLS ?? env.NOLO_TOOLS, "compact");
68422
+ }
68423
+ function shouldEmitToolEvents(mode) {
68424
+ return mode !== "hide";
68425
+ }
68426
+ function clip4(value, max = 72) {
68427
+ const compact2 = value.replace(/\s+/g, " ").trim();
68428
+ return compact2.length > max ? `${compact2.slice(0, max - 1)}\u2026` : compact2;
68429
+ }
68430
+ function compactResultHint(summary, toolName) {
68431
+ if (!summary) return "";
68432
+ const linesMatch = summary.match(/(\d+)\s+lines?/);
68433
+ if (linesMatch) {
68434
+ if (toolName === "readFile" || toolName === "listFiles" || toolName === "globFiles") {
68435
+ return `${linesMatch[1]} lines`;
68436
+ }
68437
+ if (toolName === "execShell" || toolName === "runCommand") {
68438
+ return `${linesMatch[1]} lines`;
68439
+ }
68440
+ }
68441
+ const exitMatch = summary.match(/exit=(\d+)/);
68442
+ if (exitMatch && exitMatch[1] !== "0") return `exit ${exitMatch[1]}`;
68443
+ return "";
68444
+ }
68445
+ function formatToolTraceLine(text, colorEnabled, accent = "none") {
68446
+ if (!colorEnabled) return `${text}
68447
+ `;
68448
+ if (accent === "error") {
68449
+ return `${styleCliText(text, "red", true)}
68450
+ `;
68451
+ }
68452
+ return `${dimCliText(text, true)}
68453
+ `;
68454
+ }
68455
+ function formatVerboseToolEvent(event, colorEnabled) {
68456
+ const round = event.round + 1;
68457
+ const detail = event.argumentsPreview ? ` ${event.argumentsPreview}` : "";
68458
+ if (event.type === "tool-call") {
68459
+ return formatToolTraceLine(`[nolo:tool] #${round} -> ${event.toolName}${detail}`, colorEnabled);
68460
+ }
68461
+ if (event.type === "tool-error") {
68462
+ const elapsed2 = typeof event.elapsedMs === "number" ? ` ${event.elapsedMs}ms` : "";
68463
+ return formatToolTraceLine(
68464
+ `[nolo:tool] #${round} !! ${event.toolName}${elapsed2}: ${event.message ?? "failed"}`,
68465
+ colorEnabled,
68466
+ "error"
68467
+ );
68468
+ }
68469
+ const elapsed = typeof event.elapsedMs === "number" ? ` ${event.elapsedMs}ms` : "";
68470
+ const summary = event.summary ? ` ${event.summary}` : "";
68471
+ return formatToolTraceLine(
68472
+ `[nolo:tool] #${round} <- ${event.toolName}${elapsed}${summary}`,
68473
+ colorEnabled
68474
+ );
68475
+ }
68476
+ function formatCompactToolLine(event, pending, colorEnabled) {
68477
+ const toolName = event.toolName || pending?.toolName || "tool";
68478
+ const args2 = clip4(event.argumentsPreview || pending?.argumentsPreview || "");
68479
+ const label = args2 ? `${toolName} ${args2}` : toolName;
68480
+ const ms = typeof event.elapsedMs === "number" ? `${event.elapsedMs}ms` : "";
68481
+ if (event.type === "tool-error") {
68482
+ const message = clip4(event.message ?? "failed", 96);
68483
+ const timing2 = ms ? ` \xB7 ${ms}` : "";
68484
+ return formatToolTraceLine(` \u25B8 ${label} \u2717 ${message}${timing2}`, colorEnabled, "error");
68485
+ }
68486
+ const hint = compactResultHint(event.summary, toolName);
68487
+ const timing = ms ? ` ${ms}` : "";
68488
+ const suffix = hint ? ` \xB7 ${hint}` : "";
68489
+ return formatToolTraceLine(` \u25B8 ${label} \u2713${timing}${suffix}`, colorEnabled);
68490
+ }
68491
+ function createToolEventFormatter(mode, colorEnabled = resolveCliColorEnabled()) {
68492
+ const pending = /* @__PURE__ */ new Map();
68493
+ return (event) => {
68494
+ if (mode === "hide") return "";
68495
+ if (mode === "verbose") return formatVerboseToolEvent(event, colorEnabled);
68496
+ if (event.type === "tool-call") {
68497
+ pending.set(event.toolCallId, {
68498
+ toolName: event.toolName,
68499
+ argumentsPreview: event.argumentsPreview
68500
+ });
68501
+ return "";
68502
+ }
68503
+ const call = pending.get(event.toolCallId);
68504
+ pending.delete(event.toolCallId);
68505
+ return formatCompactToolLine(event, call, colorEnabled);
68506
+ };
68507
+ }
68508
+
67746
68509
  // packages/cli/client/agentRun.ts
67747
68510
  var Spinner = class {
67748
68511
  constructor(output2, text) {
@@ -67988,10 +68751,13 @@ function buildSubjectRefs(options) {
67988
68751
  }
67989
68752
  return refs.length ? refs : void 0;
67990
68753
  }
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;
68754
+ function formatAssistantResponseForCli(text, options) {
68755
+ const thinkingMode = resolveThinkingDisplayMode(options.env);
68756
+ const renderMode = resolveRenderDisplayMode(options.env);
68757
+ return formatAssistantDisplay(
68758
+ formatAssistantTextForCli(text, thinkingMode),
68759
+ renderMode
68760
+ );
67995
68761
  }
67996
68762
  function resolveAgentEventMode(options) {
67997
68763
  if (options.eventsMode === "jsonl") return "jsonl";
@@ -68002,23 +68768,6 @@ function isMissingLocalAgentConfigError(error, agentRef) {
68002
68768
  error && typeof error === "object" && "code" in error && error.code === LOCAL_AGENT_CONFIG_MISSING_CODE && error.agentRef === agentRef
68003
68769
  );
68004
68770
  }
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
68771
  function formatToolJsonEvent(event) {
68023
68772
  return `${JSON.stringify({
68024
68773
  schemaVersion: 1,
@@ -68150,8 +68899,8 @@ async function runHttpAgentTurn(options, authToken) {
68150
68899
  const contentType = res.headers.get("content-type") || "";
68151
68900
  if (contentType.includes("text/event-stream") && res.body) {
68152
68901
  spinner.stop();
68153
- const result2 = await readStreamingAgentRun(options, res);
68154
- return result2;
68902
+ const result = await readStreamingAgentRun(options, res);
68903
+ return result;
68155
68904
  }
68156
68905
  spinner.stop();
68157
68906
  let data = {};
@@ -68194,7 +68943,10 @@ async function runHttpAgentTurn(options, authToken) {
68194
68943
  }
68195
68944
  return dialogIdText ? { exitCode: 1, dialogId: dialogIdText } : { exitCode: 1 };
68196
68945
  }
68197
- const content = String(data?.content ?? data?.message ?? "").trim();
68946
+ const content = formatAssistantResponseForCli(
68947
+ String(data?.content ?? data?.message ?? ""),
68948
+ options
68949
+ );
68198
68950
  if (content) {
68199
68951
  options.output.write(`
68200
68952
  ${options.agentName} > ${content}
@@ -68207,11 +68959,14 @@ ${options.agentName} > (no text response)
68207
68959
  const usage2 = formatUsage(data?.usage, data?.dialogId);
68208
68960
  if (usage2 && shouldShowUsage(options.env)) options.output.write(`${usage2}
68209
68961
  `);
68210
- const result = {
68962
+ return {
68211
68963
  exitCode: 0,
68212
- ...typeof data?.dialogId === "string" && data.dialogId ? { dialogId: data.dialogId } : {}
68964
+ ...typeof data?.dialogId === "string" && data.dialogId ? { dialogId: data.dialogId } : {},
68965
+ turnTokens: buildTurnTokenUsage(
68966
+ data?.usage,
68967
+ typeof data?.model === "string" ? data.model : options.agentKey
68968
+ )
68213
68969
  };
68214
- return result;
68215
68970
  }
68216
68971
  async function runInjectedLocalAgentTurn(options) {
68217
68972
  return runLocalAgentTurnForCli(options, { reportFailure: true });
@@ -68231,8 +68986,22 @@ async function runLocalAgentTurnForCli(options, settings) {
68231
68986
  const spinner = new Spinner(options.output, `${options.agentName} -> working locally`);
68232
68987
  spinner.start();
68233
68988
  try {
68234
- const traceLocalTools = shouldTraceLocalTools(options);
68989
+ const toolDisplayMode = resolveToolDisplayMode(options.env);
68990
+ const traceLocalTools = shouldEmitToolEvents(toolDisplayMode);
68991
+ const formatToolEvent = createToolEventFormatter(toolDisplayMode);
68235
68992
  const eventMode = resolveAgentEventMode(options);
68993
+ let streamedAssistantText = false;
68994
+ let printedAssistantLabel = false;
68995
+ const thinkingMode = resolveThinkingDisplayMode(options.env);
68996
+ const renderMode = resolveRenderDisplayMode(options.env);
68997
+ const renderWriter = createRenderAwareStreamWriter({
68998
+ write: (chunk) => options.output.write(chunk),
68999
+ renderMode
69000
+ });
69001
+ const thinkingFilter = createThinkingAwareStreamFilter(
69002
+ (chunk) => renderWriter.push(chunk),
69003
+ thinkingMode
69004
+ );
68236
69005
  const subjectRefs = buildSubjectRefs(options);
68237
69006
  const allowedChildAgentKeys = options.allowedChildAgentKeys?.filter((key2) => key2.trim());
68238
69007
  const allowedToolNames = options.allowedToolNames?.filter((name) => name.trim());
@@ -68261,27 +69030,48 @@ async function runLocalAgentTurnForCli(options, settings) {
68261
69030
  ...typeof options.timeoutMs === "number" ? { timeoutMs: options.timeoutMs } : {},
68262
69031
  ...traceLocalTools ? {
68263
69032
  onToolEvent: (event) => {
68264
- if (traceLocalTools) {
68265
- spinner.stop();
68266
- options.output.write(
68267
- eventMode === "jsonl" ? formatToolJsonEvent(event) : formatToolTraceEvent(event)
68268
- );
69033
+ spinner.stop();
69034
+ const chunk = eventMode === "jsonl" ? formatToolJsonEvent(event) : formatToolEvent(event);
69035
+ if (chunk) {
69036
+ options.output.write(chunk);
69037
+ }
69038
+ }
69039
+ } : {},
69040
+ ...!options.noStream ? {
69041
+ onTextDelta: (chunk) => {
69042
+ spinner.stop();
69043
+ if (!printedAssistantLabel) {
69044
+ options.output.write(`
69045
+ ${options.agentName} > `);
69046
+ printedAssistantLabel = true;
68269
69047
  }
69048
+ streamedAssistantText = true;
69049
+ thinkingFilter.push(chunk);
68270
69050
  }
68271
69051
  } : {}
68272
69052
  });
68273
69053
  spinner.stop();
68274
- const content = result.content.trim();
68275
- if (content) {
68276
- options.output.write(`
69054
+ if (streamedAssistantText) {
69055
+ thinkingFilter.flush();
69056
+ renderWriter.flush();
69057
+ options.output.write("\n");
69058
+ } else {
69059
+ const content = formatAssistantResponseForCli(result.content.trim(), options);
69060
+ if (content) {
69061
+ options.output.write(`
68277
69062
  ${options.agentName} > ${content}
68278
69063
  `);
68279
- } else {
68280
- options.output.write(`
69064
+ } else {
69065
+ options.output.write(`
68281
69066
  ${options.agentName} > (no text response)
68282
69067
  `);
69068
+ }
68283
69069
  }
68284
- return { exitCode: 0, dialogId: result.dialogId };
69070
+ return {
69071
+ exitCode: 0,
69072
+ dialogId: result.dialogId,
69073
+ turnTokens: buildTurnTokenUsage(result.usage, result.model)
69074
+ };
68285
69075
  } catch (error) {
68286
69076
  spinner.stop();
68287
69077
  if (settings.reportFailure) {
@@ -68300,9 +69090,19 @@ async function readStreamingAgentRun(options, res) {
68300
69090
  return { exitCode: 1 };
68301
69091
  }
68302
69092
  const decoder = new TextDecoder();
69093
+ const thinkingMode = resolveThinkingDisplayMode(options.env);
69094
+ const renderMode = resolveRenderDisplayMode(options.env);
69095
+ const renderWriter = createRenderAwareStreamWriter({
69096
+ write: (chunk) => options.output.write(chunk),
69097
+ renderMode
69098
+ });
68303
69099
  const writer = createStreamingTextWriter({
68304
- write: (chunk) => options.output.write(chunk)
69100
+ write: (chunk) => renderWriter.push(chunk)
68305
69101
  });
69102
+ const thinkingFilter = createThinkingAwareStreamFilter(
69103
+ (chunk) => writer.push(chunk),
69104
+ thinkingMode
69105
+ );
68306
69106
  let buffer = "";
68307
69107
  let content = "";
68308
69108
  let dialogId;
@@ -68332,7 +69132,7 @@ ${options.agentName} > `);
68332
69132
  if (!chunk) return;
68333
69133
  printLabel();
68334
69134
  content += chunk;
68335
- writer.push(chunk);
69135
+ thinkingFilter.push(chunk);
68336
69136
  };
68337
69137
  try {
68338
69138
  while (true) {
@@ -68369,6 +69169,8 @@ ${options.agentName} > `);
68369
69169
  return { exitCode: 1 };
68370
69170
  } finally {
68371
69171
  writer.flushAll();
69172
+ thinkingFilter.flush();
69173
+ renderWriter.flush();
68372
69174
  }
68373
69175
  if (!content) {
68374
69176
  options.output.write(`
@@ -68382,7 +69184,8 @@ ${options.agentName} > (no text response)
68382
69184
  `);
68383
69185
  return {
68384
69186
  exitCode: 0,
68385
- ...dialogId ? { dialogId } : {}
69187
+ ...dialogId ? { dialogId } : {},
69188
+ turnTokens: buildTurnTokenUsage(usage2, options.agentKey)
68386
69189
  };
68387
69190
  }
68388
69191
  async function runAgentTurn(options) {
@@ -68395,10 +69198,17 @@ async function runAgentTurn(options) {
68395
69198
  const skipLocal = await shouldSkipAutoLocalForServerPlatformTools(options);
68396
69199
  if (!skipLocal) {
68397
69200
  const localResult = await runLocalAgentTurnForCli(options, { reportFailure: false });
69201
+ if (localResult.exitCode !== 0 && localResult.localError) {
69202
+ options.output.write(
69203
+ `[nolo] auto runtime: local run unavailable (${localResult.localError instanceof Error ? localResult.localError.message : String(localResult.localError)}); falling back to server.
69204
+ `
69205
+ );
69206
+ }
68398
69207
  if (localResult.exitCode === 0) {
68399
69208
  return {
68400
69209
  exitCode: localResult.exitCode,
68401
- ...localResult.dialogId ? { dialogId: localResult.dialogId } : {}
69210
+ ...localResult.dialogId ? { dialogId: localResult.dialogId } : {},
69211
+ ...localResult.turnTokens ? { turnTokens: localResult.turnTokens } : {}
68402
69212
  };
68403
69213
  }
68404
69214
  if (isMissingLocalAgentConfigError(localResult.localError, options.agentKey)) {
@@ -68415,7 +69225,8 @@ async function runAgentTurn(options) {
68415
69225
  if (retriedLocalResult.exitCode === 0) {
68416
69226
  return {
68417
69227
  exitCode: retriedLocalResult.exitCode,
68418
- ...retriedLocalResult.dialogId ? { dialogId: retriedLocalResult.dialogId } : {}
69228
+ ...retriedLocalResult.dialogId ? { dialogId: retriedLocalResult.dialogId } : {},
69229
+ ...retriedLocalResult.turnTokens ? { turnTokens: retriedLocalResult.turnTokens } : {}
68419
69230
  };
68420
69231
  }
68421
69232
  }
@@ -69617,7 +70428,7 @@ function countByName(values) {
69617
70428
  function uniq(values) {
69618
70429
  return [...new Set(values.filter(Boolean))];
69619
70430
  }
69620
- function parseJsonObject3(raw) {
70431
+ function parseJsonObject2(raw) {
69621
70432
  if (typeof raw !== "string" || !raw.trim()) return null;
69622
70433
  try {
69623
70434
  const parsed = JSON.parse(raw);
@@ -69633,7 +70444,7 @@ function deriveWrittenFiles(toolMessages) {
69633
70444
  const toolName = toToolName(message?.toolName ?? message?.name);
69634
70445
  if (!writeToolNames.has(toolName)) continue;
69635
70446
  const payload = message?.toolPayload ?? {};
69636
- const content = parseJsonObject3(message?.content);
70447
+ const content = parseJsonObject2(message?.content);
69637
70448
  for (const candidate of [payload?.input?.filePath, payload?.response?.filePath, content?.filePath]) {
69638
70449
  const normalized = typeof candidate === "string" ? candidate.trim() : "";
69639
70450
  if (normalized) files.push(normalized);
@@ -69646,7 +70457,7 @@ function deriveToolErrors(toolMessages) {
69646
70457
  for (const message of toolMessages) {
69647
70458
  const toolName = toToolName(message?.toolName ?? message?.name);
69648
70459
  const payload = message?.toolPayload ?? {};
69649
- const content = parseJsonObject3(message?.content);
70460
+ const content = parseJsonObject2(message?.content);
69650
70461
  if (payload?.status === "failed" || content?.ok === false || content?.applied === false || content?.success === false) {
69651
70462
  errors.push(toolName || "unknown-tool");
69652
70463
  }
@@ -71578,6 +72389,18 @@ function getCurrentProfile(config) {
71578
72389
  if (!config) return null;
71579
72390
  return config.profiles[config.currentProfile] ?? null;
71580
72391
  }
72392
+ function saveProfileAgentSelection(selection, path7 = getDefaultProfileConfigPath()) {
72393
+ const config = loadProfileConfig(path7);
72394
+ if (!config) return null;
72395
+ const profile = config.profiles[config.currentProfile];
72396
+ if (!profile) return null;
72397
+ profile.agentKey = selection.agentKey.trim();
72398
+ profile.agentName = selection.agentName.trim();
72399
+ mkdirSync5(dirname6(path7), { recursive: true });
72400
+ writeFileSync4(path7, `${JSON.stringify(config, null, 2)}
72401
+ `, "utf8");
72402
+ return config;
72403
+ }
71581
72404
 
71582
72405
  // packages/cli/authCommands.ts
71583
72406
  init_cliEnvHelpers();
@@ -73315,23 +74138,410 @@ async function compactDialog(options) {
73315
74138
  // packages/cli/tui/readlineWorkspace.ts
73316
74139
  init_processSpawn();
73317
74140
 
73318
- // packages/cli/tui/session.ts
73319
- init_defaultServer();
74141
+ // packages/cli/tui/agentPicker.ts
74142
+ init_agentAliases();
74143
+
74144
+ // packages/cli/tui/agentCatalog.ts
74145
+ init_cliEnvHelpers();
73320
74146
  var DEFAULT_TUI_AGENT_KEY = "agent-pub-01NOLOAPPBLD000000019KCKT0";
73321
- var DEFAULT_TUI_SERVER_URL = DEFAULT_NOLO_SERVER_URL;
73322
- var KNOWN_AGENTS = [
74147
+ var PLATFORM_AGENTS = [
73323
74148
  {
73324
74149
  name: "nolo",
73325
- key: "agent-pub-01NOLOAPPBLD000000019KCKT0",
74150
+ key: DEFAULT_TUI_AGENT_KEY,
74151
+ model: "-",
74152
+ kind: "platform",
73326
74153
  description: "one assistant that routes work across your agents and data"
73327
74154
  },
73328
74155
  {
73329
74156
  name: "app-builder",
73330
74157
  key: "agent-pub-01APPBUILDER00000001YAII3I",
74158
+ model: "-",
74159
+ kind: "platform",
73331
74160
  description: "builds web apps, tools, charts, and product prototypes"
73332
74161
  }
73333
74162
  ];
73334
- var KNOWN_AGENT_ALIASES = Object.fromEntries(KNOWN_AGENTS.map((agent) => [agent.name, agent]));
74163
+ function toUpdatedAt(value) {
74164
+ if (value == null) return 0;
74165
+ const parsed = new Date(value).getTime();
74166
+ return Number.isFinite(parsed) ? parsed : 0;
74167
+ }
74168
+ function listedAgentToCatalogEntry(agent) {
74169
+ return {
74170
+ name: agent.name,
74171
+ key: agent.privateKey,
74172
+ model: agent.model,
74173
+ kind: "private",
74174
+ updatedAt: toUpdatedAt(agent.updatedAt)
74175
+ };
74176
+ }
74177
+ function mergeCatalogEntries(currentKey, platformAgents, privateAgents) {
74178
+ const seen = /* @__PURE__ */ new Set();
74179
+ const merged = [];
74180
+ const push = (entry) => {
74181
+ if (seen.has(entry.key)) return;
74182
+ seen.add(entry.key);
74183
+ merged.push(entry);
74184
+ };
74185
+ const current = [...platformAgents, ...privateAgents].find((entry) => entry.key === currentKey) ?? null;
74186
+ if (current) push(current);
74187
+ for (const entry of platformAgents) {
74188
+ if (entry.key !== currentKey) push(entry);
74189
+ }
74190
+ const sortedPrivate = [...privateAgents].sort((a, b) => {
74191
+ const tb = b.updatedAt ?? 0;
74192
+ const ta = a.updatedAt ?? 0;
74193
+ if (tb !== ta) return tb - ta;
74194
+ return a.name.localeCompare(b.name);
74195
+ });
74196
+ for (const entry of sortedPrivate) {
74197
+ if (entry.key !== currentKey) push(entry);
74198
+ }
74199
+ return merged;
74200
+ }
74201
+ async function loadAgentCatalog(args2) {
74202
+ const env = args2.env ?? process.env;
74203
+ const fetchImpl = args2.fetchImpl ?? fetch;
74204
+ const fallbackFetchImpl = args2.fallbackFetchImpl;
74205
+ const authToken = resolveAuthToken([], env);
74206
+ const userId = authToken ? parseUserIdFromAuthToken(authToken) : null;
74207
+ if (!authToken || !userId) {
74208
+ return mergeCatalogEntries(args2.currentKey, PLATFORM_AGENTS, []);
74209
+ }
74210
+ const serverUrl = resolveServerUrl(env);
74211
+ const serverUrls = resolveServerCandidates([], env, serverUrl);
74212
+ let privateAgents = [];
74213
+ try {
74214
+ const remoteResult = await listRemoteAgentsAcrossServers({
74215
+ authToken,
74216
+ fallbackFetchImpl,
74217
+ fetchImpl,
74218
+ includeLegacy: false,
74219
+ serverUrls,
74220
+ userId
74221
+ });
74222
+ privateAgents = remoteResult.agents.map(listedAgentToCatalogEntry);
74223
+ } catch {
74224
+ try {
74225
+ const db = await getReadableCliDb();
74226
+ const cached = await listLocalCachedAgents({ db, userId });
74227
+ privateAgents = cached.map(listedAgentToCatalogEntry);
74228
+ } catch {
74229
+ privateAgents = (await listRemoteAgents({
74230
+ authToken,
74231
+ fallbackFetchImpl,
74232
+ fetchImpl,
74233
+ includeLegacy: false,
74234
+ serverUrl,
74235
+ userId,
74236
+ queryUserRecords,
74237
+ readDbRecord
74238
+ })).map(listedAgentToCatalogEntry);
74239
+ }
74240
+ }
74241
+ return mergeCatalogEntries(args2.currentKey, PLATFORM_AGENTS, privateAgents);
74242
+ }
74243
+ function renderAgentCatalogList(entries, currentKey) {
74244
+ const lines = ["Agents:"];
74245
+ entries.forEach((entry, index) => {
74246
+ const current = entry.key === currentKey ? " (current)" : "";
74247
+ const kind = entry.kind === "platform" ? "platform" : "private";
74248
+ const detail = entry.description ? ` \u2014 ${entry.description}` : "";
74249
+ lines.push(
74250
+ ` ${String(index + 1).padStart(2)} ${entry.name.padEnd(18)} ${entry.model.padEnd(14)} ${kind}${detail}${current}`
74251
+ );
74252
+ });
74253
+ lines.push("");
74254
+ lines.push("Tip: run /agent in an interactive terminal to pick with \u2191\u2193.");
74255
+ return lines.join("\n");
74256
+ }
74257
+ function findAgentCatalogEntry(entries, rawTarget) {
74258
+ const target = rawTarget.trim();
74259
+ if (!target) return null;
74260
+ if (/^\d+$/.test(target)) {
74261
+ const entry = entries[Number(target) - 1];
74262
+ return entry ? { name: entry.name, key: entry.key } : null;
74263
+ }
74264
+ const lower = target.toLowerCase();
74265
+ const byName = entries.find(
74266
+ (entry) => entry.name.toLowerCase() === lower || entry.key.toLowerCase() === lower || entry.key.toLowerCase().endsWith(`-${lower}`)
74267
+ );
74268
+ if (byName) return { name: byName.name, key: byName.key };
74269
+ if (target.startsWith("agent-") || target.startsWith("agent-pub-") || target.startsWith("cybot-")) {
74270
+ return { name: target, key: target };
74271
+ }
74272
+ return null;
74273
+ }
74274
+
74275
+ // packages/cli/tui/selectDialog.ts
74276
+ var CSI_ARROW_UP = "\x1B[A";
74277
+ var CSI_ARROW_DOWN = "\x1B[B";
74278
+ var CSI_ARROW_UP_APP = "\x1BOA";
74279
+ var CSI_ARROW_DOWN_APP = "\x1BOB";
74280
+ var DEFAULT_MAX_VISIBLE = 8;
74281
+ function computeVisibleWindow(args2) {
74282
+ const maxVisible = Math.max(1, args2.maxVisible ?? DEFAULT_MAX_VISIBLE);
74283
+ if (args2.total <= maxVisible) {
74284
+ return { start: 0, end: args2.total, maxVisible };
74285
+ }
74286
+ let start = Math.max(0, args2.selectedIndex - Math.floor(maxVisible / 2));
74287
+ if (start + maxVisible > args2.total) {
74288
+ start = args2.total - maxVisible;
74289
+ }
74290
+ return { start, end: start + maxVisible, maxVisible };
74291
+ }
74292
+ function renderSelectDialog(args2) {
74293
+ const total = args2.items.length;
74294
+ const window2 = computeVisibleWindow({
74295
+ selectedIndex: args2.selectedIndex,
74296
+ total,
74297
+ maxVisible: args2.maxVisible
74298
+ });
74299
+ const lines = [
74300
+ args2.title ?? `Select agent (\u2191\u2193 Enter Esc) ${args2.selectedIndex + 1}/${total}`
74301
+ ];
74302
+ if (window2.start > 0) {
74303
+ lines.push(` ... ${window2.start} more above`);
74304
+ }
74305
+ for (let index = window2.start; index < window2.end; index += 1) {
74306
+ const item = args2.items[index];
74307
+ const marker = index === args2.selectedIndex ? ">" : " ";
74308
+ const detail = item.detail ? ` ${item.detail}` : "";
74309
+ lines.push(`${marker} ${item.label}${detail}`);
74310
+ }
74311
+ if (window2.end < total) {
74312
+ lines.push(` ... ${total - window2.end} more below`);
74313
+ }
74314
+ return lines.join("\n");
74315
+ }
74316
+ function countRenderedLines(text) {
74317
+ return text.split("\n").length;
74318
+ }
74319
+ function clearRenderedLines(output2, lineCount) {
74320
+ if (!output2.isTTY || lineCount <= 0) return;
74321
+ for (let index = 0; index < lineCount; index += 1) {
74322
+ output2.write("\x1B[1A\x1B[2K");
74323
+ }
74324
+ }
74325
+ function isArrowUp(sequence) {
74326
+ return sequence === CSI_ARROW_UP || sequence === CSI_ARROW_UP_APP;
74327
+ }
74328
+ function isArrowDown(sequence) {
74329
+ return sequence === CSI_ARROW_DOWN || sequence === CSI_ARROW_DOWN_APP;
74330
+ }
74331
+ function isSubmit(sequence) {
74332
+ return sequence === "\r" || sequence === "\n";
74333
+ }
74334
+ function isCancel(sequence) {
74335
+ return sequence === "" || sequence === "\x1B";
74336
+ }
74337
+ function createRawKeyReader(input2) {
74338
+ let buffer = "";
74339
+ const tryParseSequence = () => {
74340
+ if (!buffer) return null;
74341
+ if (isSubmit(buffer) || isCancel(buffer)) {
74342
+ const sequence2 = buffer;
74343
+ buffer = "";
74344
+ return sequence2;
74345
+ }
74346
+ if (buffer.startsWith("\x1B")) {
74347
+ for (const candidate of [
74348
+ CSI_ARROW_UP,
74349
+ CSI_ARROW_DOWN,
74350
+ CSI_ARROW_UP_APP,
74351
+ CSI_ARROW_DOWN_APP
74352
+ ]) {
74353
+ if (buffer.startsWith(candidate)) {
74354
+ buffer = buffer.slice(candidate.length);
74355
+ return candidate;
74356
+ }
74357
+ }
74358
+ if (buffer.length >= 8) {
74359
+ buffer = "";
74360
+ return null;
74361
+ }
74362
+ return void 0;
74363
+ }
74364
+ const sequence = buffer;
74365
+ buffer = "";
74366
+ return sequence;
74367
+ };
74368
+ return () => new Promise((resolve6) => {
74369
+ const finalize = (sequence) => {
74370
+ cleanup();
74371
+ resolve6(sequence ?? null);
74372
+ };
74373
+ const onReadable = () => {
74374
+ while (true) {
74375
+ const chunk = input2.read();
74376
+ if (chunk == null) break;
74377
+ buffer += typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf8");
74378
+ const parsed = tryParseSequence();
74379
+ if (parsed === void 0) return;
74380
+ finalize(parsed);
74381
+ return;
74382
+ }
74383
+ };
74384
+ const cleanup = () => {
74385
+ input2.off("readable", onReadable);
74386
+ };
74387
+ onReadable();
74388
+ if (buffer) {
74389
+ const parsed = tryParseSequence();
74390
+ if (parsed !== void 0) {
74391
+ finalize(parsed);
74392
+ return;
74393
+ }
74394
+ }
74395
+ input2.on("readable", onReadable);
74396
+ });
74397
+ }
74398
+ function drainInputBuffer(input2) {
74399
+ if (typeof input2.read !== "function") return;
74400
+ while (input2.read() !== null) {
74401
+ }
74402
+ }
74403
+ async function runSelectDialog(args2) {
74404
+ const items = args2.items;
74405
+ if (items.length === 0) {
74406
+ return { kind: "cancelled" };
74407
+ }
74408
+ let selectedIndex = Math.min(
74409
+ Math.max(args2.initialIndex ?? 0, 0),
74410
+ items.length - 1
74411
+ );
74412
+ const output2 = args2.output ?? process.stdout;
74413
+ const input2 = args2.input ?? process.stdin;
74414
+ const readKey = args2.readKey ?? createRawKeyReader(input2);
74415
+ const wasRaw = Boolean(input2.isTTY && input2.isRaw);
74416
+ const wasPaused = typeof input2.isPaused === "function" ? input2.isPaused() : false;
74417
+ let renderedLineCount = 0;
74418
+ const paint = () => {
74419
+ const frame = renderSelectDialog({
74420
+ items,
74421
+ selectedIndex,
74422
+ title: args2.title,
74423
+ maxVisible: args2.maxVisible
74424
+ });
74425
+ if (output2.isTTY && typeof output2.write === "function") {
74426
+ clearRenderedLines(output2, renderedLineCount);
74427
+ output2.write(`${frame}
74428
+ `);
74429
+ renderedLineCount = countRenderedLines(frame);
74430
+ return;
74431
+ }
74432
+ if (typeof output2.write === "function") {
74433
+ output2.write(`${frame}
74434
+ `);
74435
+ }
74436
+ renderedLineCount = countRenderedLines(frame);
74437
+ };
74438
+ if (input2.isTTY) {
74439
+ if (!wasRaw) input2.setRawMode(true);
74440
+ if (!wasPaused) input2.pause();
74441
+ }
74442
+ paint();
74443
+ try {
74444
+ while (true) {
74445
+ const sequence = await readKey();
74446
+ if (sequence == null) {
74447
+ return { kind: "cancelled" };
74448
+ }
74449
+ if (isCancel(sequence)) {
74450
+ return { kind: "cancelled" };
74451
+ }
74452
+ if (isSubmit(sequence)) {
74453
+ return { kind: "selected", index: selectedIndex, item: items[selectedIndex] };
74454
+ }
74455
+ if (isArrowUp(sequence)) {
74456
+ selectedIndex = selectedIndex <= 0 ? items.length - 1 : selectedIndex - 1;
74457
+ paint();
74458
+ continue;
74459
+ }
74460
+ if (isArrowDown(sequence)) {
74461
+ selectedIndex = selectedIndex >= items.length - 1 ? 0 : selectedIndex + 1;
74462
+ paint();
74463
+ continue;
74464
+ }
74465
+ }
74466
+ } finally {
74467
+ if (input2.isTTY) {
74468
+ drainInputBuffer(input2);
74469
+ if (!wasRaw) input2.setRawMode(false);
74470
+ if (!wasPaused) input2.resume();
74471
+ clearRenderedLines(output2, renderedLineCount);
74472
+ renderedLineCount = 0;
74473
+ }
74474
+ }
74475
+ }
74476
+
74477
+ // packages/cli/tui/agentPicker.ts
74478
+ function toAgentPickerItems(entries) {
74479
+ return entries.map((entry) => ({
74480
+ label: entry.name,
74481
+ detail: `${entry.model} ${entry.kind}`,
74482
+ entry
74483
+ }));
74484
+ }
74485
+ function formatAgentSwitchMessage(args2) {
74486
+ const dialog = args2.dialogId ? `Dialog kept: ${args2.dialogId}` : "Dialog kept: new";
74487
+ return `Switched to ${args2.name}. ${dialog}`;
74488
+ }
74489
+ async function runAgentPicker(args2) {
74490
+ const output2 = args2.output ?? process.stdout;
74491
+ const input2 = args2.input ?? process.stdin;
74492
+ const interactive = args2.interactive ?? Boolean(input2.isTTY && output2.isTTY);
74493
+ const entries = await loadAgentCatalog({
74494
+ env: args2.env,
74495
+ currentKey: args2.currentKey,
74496
+ fetchImpl: args2.fetchImpl,
74497
+ fallbackFetchImpl: args2.fallbackFetchImpl
74498
+ });
74499
+ if (!interactive) {
74500
+ return {
74501
+ kind: "list",
74502
+ output: renderAgentCatalogList(entries, args2.currentKey),
74503
+ entries
74504
+ };
74505
+ }
74506
+ const items = toAgentPickerItems(entries);
74507
+ const initialIndex = Math.max(
74508
+ entries.findIndex((entry) => entry.key === args2.currentKey),
74509
+ 0
74510
+ );
74511
+ const result = await runSelectDialog({
74512
+ items,
74513
+ initialIndex,
74514
+ title: void 0,
74515
+ input: input2,
74516
+ output: output2,
74517
+ readKey: args2.readKey
74518
+ });
74519
+ if (result.kind === "cancelled") {
74520
+ return { kind: "cancelled", entries };
74521
+ }
74522
+ const selected = result.item.entry;
74523
+ return {
74524
+ kind: "selected",
74525
+ name: selected.name,
74526
+ key: selected.key,
74527
+ entries
74528
+ };
74529
+ }
74530
+ function resolveAgentSwitchTarget(rawTarget, catalogEntries = []) {
74531
+ const resolvedKey = resolveCliAgentKeyInput(rawTarget);
74532
+ if (resolvedKey !== rawTarget.trim()) {
74533
+ const aliasEntry = catalogEntries.find((entry) => entry.key === resolvedKey);
74534
+ return {
74535
+ name: aliasEntry?.name ?? rawTarget.trim().toLowerCase(),
74536
+ key: resolvedKey
74537
+ };
74538
+ }
74539
+ return findAgentCatalogEntry(catalogEntries, rawTarget);
74540
+ }
74541
+
74542
+ // packages/cli/tui/session.ts
74543
+ init_defaultServer();
74544
+ var DEFAULT_TUI_SERVER_URL = DEFAULT_NOLO_SERVER_URL;
73335
74545
  function shortenDialogId(dialogId) {
73336
74546
  return dialogId.length > 12 ? `${dialogId.slice(0, 6)}...${dialogId.slice(-4)}` : dialogId;
73337
74547
  }
@@ -73353,25 +74563,29 @@ function createInitialTuiState(env = process.env) {
73353
74563
  ),
73354
74564
  cliVersion: env.NOLO_CLI_VERSION?.trim() || void 0,
73355
74565
  attachedDocs: [],
73356
- runtimeMode: env.NOLO_RUNTIME_MODE === "local" || env.NOLO_RUNTIME_MODE === "server" ? env.NOLO_RUNTIME_MODE : "auto"
74566
+ runtimeMode: env.NOLO_RUNTIME_MODE === "local" || env.NOLO_RUNTIME_MODE === "server" ? env.NOLO_RUNTIME_MODE : "auto",
74567
+ thinkingDisplay: normalizeThinkingDisplayMode(
74568
+ env.NOLO_CLI_THINKING ?? env.NOLO_THINKING,
74569
+ "hide"
74570
+ ),
74571
+ toolDisplay: normalizeToolDisplayMode(env.NOLO_CLI_TOOLS ?? env.NOLO_TOOLS, "compact"),
74572
+ renderDisplay: normalizeRenderDisplayMode(env.NOLO_CLI_RENDER ?? env.NOLO_RENDER, "rich")
73357
74573
  };
73358
74574
  }
73359
74575
  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(" | ");
74576
+ const colorEnabled = resolveCliColorEnabled();
74577
+ const agent = styleCliText(state.agentName, "cyan", colorEnabled);
74578
+ const tokens = dimCliText(renderTokenStatus(state.turnTokens), colorEnabled);
74579
+ const profile = dimCliText(`profile ${state.profileName}`, colorEnabled);
74580
+ return [`agent ${agent}`, tokens, profile].join(
74581
+ dimCliText(" | ", colorEnabled)
74582
+ );
73368
74583
  }
73369
74584
  function renderWelcome(state) {
73370
- const docs = state.attachedDocs.length > 0 ? `${state.attachedDocs.length} attached` : "none";
73371
74585
  return [
73372
74586
  "",
73373
74587
  `Nolo workspace${state.cliVersion ? ` nolo ${state.cliVersion}` : ""}`,
73374
- `agent ${state.agentName} | dialog ${resolveDialogLabel(state)} | docs ${docs} | profile ${state.profileName}`,
74588
+ `agent ${state.agentName} | ${renderTokenStatus(state.turnTokens)} | profile ${state.profileName}`,
73375
74589
  `server ${state.serverUrl}`,
73376
74590
  "",
73377
74591
  "Tell nolo what you want. Use /help for commands. Use /version if this install feels stale.",
@@ -73389,9 +74603,14 @@ function renderTuiHelp() {
73389
74603
  " /compact Compact current dialog and fork a new one",
73390
74604
  " /context Show workspace context and next actions",
73391
74605
  " /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",
74606
+ " /tools <mode> Control tool trace: hide, compact, verbose",
74607
+ " /thinking <mode> Control thinking output: hide, marker, show",
74608
+ " /render <mode> Control assistant output: plain, rich",
74609
+ " /agent Pick an agent interactively (\u2191\u2193, Enter)",
74610
+ " /agent list List agents as text",
74611
+ " /agent <name> Switch directly by name, alias, or key",
74612
+ " /agents List platform agent shortcuts",
74613
+ " /switch <agent> Switch the current agent (alias of /agent <name>)",
73395
74614
  " /dialog Show the current dialog",
73396
74615
  " /doc List attached docs",
73397
74616
  " /doc attach <doc> Attach a doc to this workspace",
@@ -73410,12 +74629,16 @@ function renderContextPanel(state) {
73410
74629
  return [
73411
74630
  "Workspace context",
73412
74631
  "-----------------",
73413
- `agent ${state.agentName}`,
73414
- `dialog ${resolveDialogLabel(state)}`,
73415
- `docs ${docs}`,
73416
- `profile ${state.profileName}`,
73417
- `runtime ${state.runtimeMode}`,
73418
- `server ${state.serverUrl}`,
74632
+ `agent ${state.agentName}`,
74633
+ `tokens ${renderTokenStatus(state.turnTokens)}`,
74634
+ `dialog ${resolveDialogLabel(state)}`,
74635
+ `docs ${docs}`,
74636
+ `profile ${state.profileName}`,
74637
+ `runtime ${state.runtimeMode}`,
74638
+ `tools ${state.toolDisplay}`,
74639
+ `thinking ${state.thinkingDisplay}`,
74640
+ `render ${state.renderDisplay}`,
74641
+ `server ${state.serverUrl}`,
73419
74642
  "",
73420
74643
  "Next:",
73421
74644
  " /agents see specialist shortcuts",
@@ -73426,25 +74649,22 @@ function renderContextPanel(state) {
73426
74649
  function renderKnownAgents() {
73427
74650
  return [
73428
74651
  "Agents:",
73429
- ...KNOWN_AGENTS.map(
73430
- (agent, index) => ` ${index + 1} ${agent.name.padEnd(11)} ${agent.description}`
74652
+ ...PLATFORM_AGENTS.map(
74653
+ (agent, index) => ` ${index + 1} ${agent.name.padEnd(11)} ${agent.description ?? ""}`
73431
74654
  ),
73432
74655
  "",
73433
- "Tip: stay on nolo for the one-assistant feel; switch only when you want a specialist directly."
74656
+ "Tip: run /agent for the full picker, or /agent list for your private agents too."
73434
74657
  ].join("\n");
73435
74658
  }
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;
74659
+ function applyAgentSwitch(state, target) {
74660
+ return {
74661
+ nextState: {
74662
+ ...state,
74663
+ agentName: target.name,
74664
+ agentKey: target.key
74665
+ },
74666
+ output: `Switched to ${target.name}. ${state.dialogId ? `Dialog kept: ${state.dialogId}` : "Dialog kept: new"}`
74667
+ };
73448
74668
  }
73449
74669
  var DIALOG_ID_PATTERN = /[0-9A-HJKMNP-TV-Z]{26}/i;
73450
74670
  var DIALOG_KEY_PATTERN = /dialog-[^\s"'<>]+-[0-9A-HJKMNP-TV-Z]{26}/i;
@@ -73622,6 +74842,66 @@ function handleTuiInput(input2, state) {
73622
74842
  output: `Runtime: ${argText}`
73623
74843
  };
73624
74844
  }
74845
+ case "/tools": {
74846
+ if (!argText) {
74847
+ return {
74848
+ nextState: state,
74849
+ output: `Tool display: ${state.toolDisplay} (hide | compact | verbose)`
74850
+ };
74851
+ }
74852
+ const normalizedArg = argText.trim().toLowerCase();
74853
+ if (!["hide", "compact", "verbose", "on", "off"].includes(normalizedArg)) {
74854
+ return {
74855
+ nextState: state,
74856
+ output: "Usage: /tools <hide|compact|verbose>"
74857
+ };
74858
+ }
74859
+ const nextMode = normalizeToolDisplayMode(normalizedArg, state.toolDisplay);
74860
+ return {
74861
+ nextState: { ...state, toolDisplay: nextMode },
74862
+ output: `Tool display: ${nextMode}`
74863
+ };
74864
+ }
74865
+ case "/thinking": {
74866
+ if (!argText) {
74867
+ return {
74868
+ nextState: state,
74869
+ output: `Thinking display: ${state.thinkingDisplay} (hide | marker | show)`
74870
+ };
74871
+ }
74872
+ const normalizedArg = argText.trim().toLowerCase();
74873
+ if (!["hide", "marker", "show", "on", "off"].includes(normalizedArg)) {
74874
+ return {
74875
+ nextState: state,
74876
+ output: "Usage: /thinking <hide|marker|show>"
74877
+ };
74878
+ }
74879
+ const nextMode = normalizeThinkingDisplayMode(normalizedArg, state.thinkingDisplay);
74880
+ return {
74881
+ nextState: { ...state, thinkingDisplay: nextMode },
74882
+ output: `Thinking display: ${nextMode}`
74883
+ };
74884
+ }
74885
+ case "/render": {
74886
+ if (!argText) {
74887
+ return {
74888
+ nextState: state,
74889
+ output: `Render display: ${state.renderDisplay} (plain | rich)`
74890
+ };
74891
+ }
74892
+ const normalizedArg = argText.trim().toLowerCase();
74893
+ if (!["plain", "rich", "on", "off"].includes(normalizedArg)) {
74894
+ return {
74895
+ nextState: state,
74896
+ output: "Usage: /render <plain|rich>"
74897
+ };
74898
+ }
74899
+ const nextMode = normalizeRenderDisplayMode(normalizedArg, state.renderDisplay);
74900
+ return {
74901
+ nextState: { ...state, renderDisplay: nextMode },
74902
+ output: `Render display: ${nextMode}`
74903
+ };
74904
+ }
73625
74905
  case "/exit":
73626
74906
  case "/quit":
73627
74907
  return { nextState: state, output: "Bye.", action: { type: "exit" } };
@@ -73631,7 +74911,8 @@ function handleTuiInput(input2, state) {
73631
74911
  ...state,
73632
74912
  dialogId: void 0,
73633
74913
  dialogLabel: "new",
73634
- attachedDocs: []
74914
+ attachedDocs: [],
74915
+ turnTokens: void 0
73635
74916
  },
73636
74917
  output: "Started a fresh dialog."
73637
74918
  };
@@ -73655,11 +74936,37 @@ ${renderTuiHelp()}`
73655
74936
  output: "Compacting current dialog...",
73656
74937
  action: { type: "compact", dialogId: state.dialogId }
73657
74938
  };
73658
- case "/agent":
73659
- return {
73660
- nextState: state,
73661
- output: `Current agent: ${state.agentName} (${state.agentKey})`
73662
- };
74939
+ case "/agent": {
74940
+ if (!argText) {
74941
+ return {
74942
+ nextState: state,
74943
+ output: "",
74944
+ action: { type: "pick-agent" }
74945
+ };
74946
+ }
74947
+ if (argText === "list") {
74948
+ return {
74949
+ nextState: state,
74950
+ output: "",
74951
+ action: { type: "list-agents" }
74952
+ };
74953
+ }
74954
+ if (argText === "current" || argText === "show") {
74955
+ return {
74956
+ nextState: state,
74957
+ output: `Current agent: ${state.agentName} (${state.agentKey})`
74958
+ };
74959
+ }
74960
+ const resolvedTarget = resolveAgentSwitchTarget(argText, PLATFORM_AGENTS);
74961
+ if (!resolvedTarget) {
74962
+ return {
74963
+ nextState: state,
74964
+ output: `I don't know agent "${argText}" yet.
74965
+ Use /agent, /agent list, /agent minimax-m3, or a full agent key.`
74966
+ };
74967
+ }
74968
+ return applyAgentSwitch(state, resolvedTarget);
74969
+ }
73663
74970
  case "/agents":
73664
74971
  return {
73665
74972
  nextState: state,
@@ -73669,25 +74976,18 @@ ${renderTuiHelp()}`
73669
74976
  if (!argText) {
73670
74977
  return {
73671
74978
  nextState: state,
73672
- output: "Usage: /switch <agent-key|alias>"
74979
+ output: "Usage: /switch <agent-key|alias> (or run /agent)"
73673
74980
  };
73674
74981
  }
73675
- const resolvedTarget = resolveSwitchTarget(argText);
74982
+ const resolvedTarget = resolveAgentSwitchTarget(argText, PLATFORM_AGENTS);
73676
74983
  if (!resolvedTarget) {
73677
74984
  return {
73678
74985
  nextState: state,
73679
74986
  output: `I don't know agent shortcut "${argText}" yet.
73680
- Use /switch nolo, /switch app-builder, or a full agent key like agent-pub-...`
74987
+ Use /agent, /switch nolo, /switch minimax-m3, or a full agent key.`
73681
74988
  };
73682
74989
  }
73683
- return {
73684
- nextState: {
73685
- ...state,
73686
- agentName: resolvedTarget.name,
73687
- agentKey: resolvedTarget.key
73688
- },
73689
- output: `Switched to ${resolvedTarget.name}.`
73690
- };
74990
+ return applyAgentSwitch(state, resolvedTarget);
73691
74991
  }
73692
74992
  case "/dialog":
73693
74993
  return {
@@ -73758,8 +75058,14 @@ async function runAgentChat(scriptDir, state, message, env, output2, agentRunner
73758
75058
  message,
73759
75059
  continueDialogId: state.dialogId,
73760
75060
  runtimeMode: state.runtimeMode,
75061
+ localRuntimeCwd: process.cwd(),
73761
75062
  scriptDir,
73762
- env,
75063
+ env: {
75064
+ ...env,
75065
+ NOLO_CLI_THINKING: state.thinkingDisplay,
75066
+ NOLO_CLI_TOOLS: state.toolDisplay,
75067
+ NOLO_CLI_RENDER: state.renderDisplay
75068
+ },
73763
75069
  output: output2
73764
75070
  });
73765
75071
  return result;
@@ -73785,6 +75091,19 @@ async function runCliCommandInChildProcess(args2, context) {
73785
75091
  ]);
73786
75092
  return proc.exited;
73787
75093
  }
75094
+ function persistAgentSelection(state, env) {
75095
+ try {
75096
+ saveProfileAgentSelection({
75097
+ agentKey: state.agentKey,
75098
+ agentName: state.agentName
75099
+ });
75100
+ } catch {
75101
+ }
75102
+ if (env) {
75103
+ env.NOLO_AGENT = state.agentKey;
75104
+ env.NOLO_AGENT_NAME = state.agentName;
75105
+ }
75106
+ }
73788
75107
  async function startTuiWorkspace(options) {
73789
75108
  let state = createInitialTuiState(options.env ?? process.env);
73790
75109
  const input2 = options.input ?? defaultInput;
@@ -73798,12 +75117,19 @@ async function startTuiWorkspace(options) {
73798
75117
  rl.prompt();
73799
75118
  try {
73800
75119
  for await (const line of rl) {
75120
+ if (!line.trim()) {
75121
+ continue;
75122
+ }
73801
75123
  const result = handleTuiInput(line, state);
75124
+ const previousAgentKey = state.agentKey;
73802
75125
  state = result.nextState;
73803
75126
  if (result.output) {
73804
75127
  output2.write(`${result.output}
73805
75128
  `);
73806
75129
  }
75130
+ if (state.agentKey !== previousAgentKey && result.output?.startsWith("Switched to ")) {
75131
+ persistAgentSelection(state, options.env ?? process.env);
75132
+ }
73807
75133
  if (result.action?.type === "exit") {
73808
75134
  break;
73809
75135
  }
@@ -73842,6 +75168,64 @@ async function startTuiWorkspace(options) {
73842
75168
  output2.write("Update failed. Check the error above, then run /update again or use nolo update.\n");
73843
75169
  }
73844
75170
  }
75171
+ if (result.action?.type === "pick-agent") {
75172
+ rl.pause();
75173
+ try {
75174
+ const pickResult = await runAgentPicker({
75175
+ currentKey: state.agentKey,
75176
+ env: options.env ?? process.env,
75177
+ input: input2,
75178
+ output: output2
75179
+ });
75180
+ if (pickResult.kind === "list") {
75181
+ output2.write(`${pickResult.output}
75182
+ `);
75183
+ } else if (pickResult.kind === "selected") {
75184
+ state = {
75185
+ ...state,
75186
+ agentName: pickResult.name,
75187
+ agentKey: pickResult.key
75188
+ };
75189
+ persistAgentSelection(state, options.env ?? process.env);
75190
+ output2.write(
75191
+ `${formatAgentSwitchMessage({
75192
+ name: pickResult.name,
75193
+ dialogId: state.dialogId
75194
+ })}
75195
+ `
75196
+ );
75197
+ } else {
75198
+ output2.write("Agent switch cancelled.\n");
75199
+ }
75200
+ } catch (error) {
75201
+ output2.write(
75202
+ `[nolo] Agent picker failed: ${error instanceof Error ? error.message : String(error)}
75203
+ `
75204
+ );
75205
+ } finally {
75206
+ rl.resume();
75207
+ }
75208
+ }
75209
+ if (result.action?.type === "list-agents") {
75210
+ try {
75211
+ const pickResult = await runAgentPicker({
75212
+ currentKey: state.agentKey,
75213
+ env: options.env ?? process.env,
75214
+ input: input2,
75215
+ output: output2,
75216
+ interactive: false
75217
+ });
75218
+ if (pickResult.kind === "list") {
75219
+ output2.write(`${pickResult.output}
75220
+ `);
75221
+ }
75222
+ } catch (error) {
75223
+ output2.write(
75224
+ `[nolo] Agent list failed: ${error instanceof Error ? error.message : String(error)}
75225
+ `
75226
+ );
75227
+ }
75228
+ }
73845
75229
  if (result.action?.type === "cli-command") {
73846
75230
  try {
73847
75231
  const exitCode2 = await cliCommandRunner(result.action.args, {
@@ -73870,11 +75254,14 @@ async function startTuiWorkspace(options) {
73870
75254
  output2,
73871
75255
  options.agentRunner
73872
75256
  );
73873
- if (runResult.dialogId) {
75257
+ if (runResult.dialogId || runResult.turnTokens) {
73874
75258
  state = {
73875
75259
  ...state,
73876
- dialogId: runResult.dialogId,
73877
- dialogLabel: runResult.dialogId
75260
+ ...runResult.dialogId ? {
75261
+ dialogId: runResult.dialogId,
75262
+ dialogLabel: runResult.dialogId
75263
+ } : {},
75264
+ ...runResult.turnTokens ? { turnTokens: runResult.turnTokens } : {}
73878
75265
  };
73879
75266
  }
73880
75267
  }