nolo-cli 0.1.46 → 0.1.48

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/index.js +2174 -354
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -1443,6 +1443,15 @@ var init_agentAliases = __esm({
1443
1443
  nolo: NOLO_DEFAULT_AGENT_KEY,
1444
1444
  default: NOLO_DEFAULT_AGENT_KEY,
1445
1445
  "default-nolo": NOLO_DEFAULT_AGENT_KEY,
1446
+ // Product implementation & coordination aliases
1447
+ "frontend-implementer": NOLO_FRONTEND_AGENT_KEY,
1448
+ "frontend-agent": NOLO_FRONTEND_AGENT_KEY,
1449
+ frontend: NOLO_FRONTEND_AGENT_KEY,
1450
+ "frontend agent": NOLO_FRONTEND_AGENT_KEY,
1451
+ "\u524D\u7AEFagent": NOLO_FRONTEND_AGENT_KEY,
1452
+ "\u524D\u7AEF agent": NOLO_FRONTEND_AGENT_KEY,
1453
+ "project-manager": NOLO_PROJECT_MANAGER_AGENT_KEY,
1454
+ pm: NOLO_PROJECT_MANAGER_AGENT_KEY,
1446
1455
  // Explicit local CLI agents. These are ordinary private agent records that
1447
1456
  // run through the current computer local runtime when unbound.
1448
1457
  "local-codex": LOCAL_CODEX_AGENT_KEY,
@@ -1464,7 +1473,9 @@ var init_agentAliases = __esm({
1464
1473
  grok: LOCAL_GROK_AGENT_KEY,
1465
1474
  "grok-agent": LOCAL_GROK_AGENT_KEY,
1466
1475
  "grok cli": LOCAL_GROK_AGENT_KEY,
1467
- "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"
1468
1479
  };
1469
1480
  }
1470
1481
  });
@@ -1593,6 +1604,19 @@ function buildMessages(args2) {
1593
1604
  { role: "user", content: args2.input }
1594
1605
  ];
1595
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
+ }
1596
1620
  function extractUserInputText(content) {
1597
1621
  if (typeof content === "string") return content;
1598
1622
  if (!Array.isArray(content)) return "";
@@ -1618,11 +1642,14 @@ async function runLocalAgentTurn(input2) {
1618
1642
  const userInputText = extractUserInputText(input2.input);
1619
1643
  let toolCallCount = 0;
1620
1644
  let result;
1645
+ let turnUsage;
1621
1646
  let round = 0;
1622
1647
  while (true) {
1623
1648
  result = await provider.complete(messages, {
1624
- ...typeof input2.timeoutMs === "number" ? { timeoutMs: input2.timeoutMs } : {}
1649
+ ...typeof input2.timeoutMs === "number" ? { timeoutMs: input2.timeoutMs } : {},
1650
+ ...input2.onTextDelta ? { onTextDelta: input2.onTextDelta } : {}
1625
1651
  });
1652
+ turnUsage = mergeTurnUsage(turnUsage, result.usage);
1626
1653
  const toolCalls = result.tool_calls ?? [];
1627
1654
  if (toolCalls.length === 0) break;
1628
1655
  toolCallCount += toolCalls.length;
@@ -1715,6 +1742,7 @@ async function runLocalAgentTurn(input2) {
1715
1742
  });
1716
1743
  return {
1717
1744
  ...result,
1745
+ ...turnUsage ? { usage: turnUsage } : {},
1718
1746
  ...toolCallCount > 0 ? { toolCallCount } : {},
1719
1747
  ...agentConfig.toolSurface ? { runtimeToolSurface: agentConfig.toolSurface } : {},
1720
1748
  dialogId: saved.dialogId,
@@ -3350,9 +3378,10 @@ function buildOpenAiCompatibleChatCompletionRequest(args2) {
3350
3378
  const body = {
3351
3379
  model: args2.providerConfig.model,
3352
3380
  messages: toOpenAiCompatibleMessages(args2.messages),
3353
- stream: false,
3381
+ stream: args2.stream ?? false,
3354
3382
  ...args2.providerConfig.requestOptions,
3355
- ...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 } } : {}
3356
3385
  };
3357
3386
  return {
3358
3387
  url: args2.providerConfig.endpoint,
@@ -3382,6 +3411,138 @@ function parseOpenAiCompatibleChatCompletionResponse(args2) {
3382
3411
  trace: args2.trace
3383
3412
  };
3384
3413
  }
3414
+ function accumulateToolCallDelta(accumulated, deltas) {
3415
+ for (const delta of deltas) {
3416
+ const index = typeof delta.index === "number" ? delta.index : 0;
3417
+ const current = accumulated[index] ?? {
3418
+ id: "",
3419
+ type: "function",
3420
+ function: { name: "", arguments: "" }
3421
+ };
3422
+ if (typeof delta.id === "string" && delta.id) current.id = delta.id;
3423
+ const fn = delta.function;
3424
+ if (fn && typeof fn === "object") {
3425
+ const functionDelta = fn;
3426
+ if (typeof functionDelta.name === "string" && functionDelta.name) {
3427
+ current.function.name += functionDelta.name;
3428
+ }
3429
+ if (typeof functionDelta.arguments === "string" && functionDelta.arguments) {
3430
+ current.function.arguments += functionDelta.arguments;
3431
+ }
3432
+ }
3433
+ accumulated[index] = current;
3434
+ }
3435
+ }
3436
+ function finalizeAccumulatedToolCalls(accumulated) {
3437
+ return Object.keys(accumulated).map((key2) => accumulated[Number(key2)]).filter((call) => call?.function?.name);
3438
+ }
3439
+ async function readOpenAiCompatibleSseCompletion(args2) {
3440
+ const reader = args2.response.body?.getReader();
3441
+ if (!reader) {
3442
+ throw new Error("OpenAI-compatible stream response did not include a readable body.");
3443
+ }
3444
+ const decoder = new TextDecoder();
3445
+ let buffer = "";
3446
+ let content = "";
3447
+ let reasoning = "";
3448
+ let usage2;
3449
+ const accumulatedToolCalls = {};
3450
+ while (true) {
3451
+ const { done, value } = await reader.read();
3452
+ if (done) break;
3453
+ buffer += decoder.decode(value, { stream: true });
3454
+ while (true) {
3455
+ const boundary = buffer.indexOf("\n\n");
3456
+ if (boundary === -1) break;
3457
+ const event = buffer.slice(0, boundary);
3458
+ buffer = buffer.slice(boundary + 2);
3459
+ for (const line of event.split("\n")) {
3460
+ const trimmed = line.trim();
3461
+ if (!trimmed.startsWith("data:")) continue;
3462
+ const payload = trimmed.slice(5).trim();
3463
+ if (!payload || payload === "[DONE]") continue;
3464
+ let parsed;
3465
+ try {
3466
+ parsed = JSON.parse(payload);
3467
+ } catch {
3468
+ continue;
3469
+ }
3470
+ if (parsed?.usage && typeof parsed.usage === "object") {
3471
+ usage2 = parsed.usage;
3472
+ }
3473
+ const delta = parsed?.choices?.[0]?.delta;
3474
+ if (!delta || typeof delta !== "object") continue;
3475
+ const reasoningChunk = typeof delta.reasoning_content === "string" ? delta.reasoning_content : typeof delta.reasoning === "string" ? delta.reasoning : "";
3476
+ if (reasoningChunk) reasoning += reasoningChunk;
3477
+ if (Array.isArray(delta.tool_calls) && delta.tool_calls.length > 0) {
3478
+ accumulateToolCallDelta(accumulatedToolCalls, delta.tool_calls);
3479
+ }
3480
+ const textChunk = typeof delta.content === "string" ? delta.content : "";
3481
+ if (textChunk) {
3482
+ content += textChunk;
3483
+ args2.onTextDelta?.(textChunk);
3484
+ }
3485
+ }
3486
+ }
3487
+ }
3488
+ const tool_calls = finalizeAccumulatedToolCalls(accumulatedToolCalls);
3489
+ return {
3490
+ content,
3491
+ ...reasoning ? { reasoning_content: reasoning } : {},
3492
+ ...tool_calls.length > 0 ? { tool_calls } : {},
3493
+ ...usage2 ? { usage: usage2 } : {}
3494
+ };
3495
+ }
3496
+ async function executeOpenAiCompatibleChatCompletion(args2) {
3497
+ const request = buildOpenAiCompatibleChatCompletionRequest({
3498
+ providerConfig: args2.providerConfig,
3499
+ messages: args2.messages,
3500
+ tools: args2.tools,
3501
+ stream: args2.stream
3502
+ });
3503
+ const res = await args2.fetchImpl(request.url, {
3504
+ ...request.init,
3505
+ ...args2.signal ? { signal: args2.signal } : {}
3506
+ });
3507
+ if (!res.ok) {
3508
+ const raw2 = await res.text().catch(() => "");
3509
+ let data2 = raw2;
3510
+ try {
3511
+ data2 = JSON.parse(raw2);
3512
+ } catch {
3513
+ }
3514
+ throw new Error(`local provider failed: HTTP ${res.status} ${JSON.stringify(data2)}`);
3515
+ }
3516
+ const contentType = res.headers.get("content-type") ?? "";
3517
+ const shouldStream = Boolean(args2.stream && args2.onTextDelta) && contentType.includes("text/event-stream");
3518
+ if (shouldStream && args2.onTextDelta) {
3519
+ const streamed = await readOpenAiCompatibleSseCompletion({
3520
+ response: res,
3521
+ onTextDelta: args2.onTextDelta
3522
+ });
3523
+ return {
3524
+ content: streamed.content,
3525
+ model: args2.providerConfig.model,
3526
+ provider: args2.providerConfig.provider,
3527
+ ...streamed.tool_calls ? { tool_calls: streamed.tool_calls } : {},
3528
+ ...streamed.reasoning_content ? { reasoning_content: streamed.reasoning_content } : {},
3529
+ ...streamed.usage ? { usage: streamed.usage } : {},
3530
+ trace: args2.messages
3531
+ };
3532
+ }
3533
+ const raw = await res.text().catch(() => "");
3534
+ let data = {};
3535
+ try {
3536
+ data = raw ? JSON.parse(raw) : {};
3537
+ } catch {
3538
+ data = {};
3539
+ }
3540
+ return parseOpenAiCompatibleChatCompletionResponse({
3541
+ providerConfig: args2.providerConfig,
3542
+ data,
3543
+ trace: args2.messages
3544
+ });
3545
+ }
3385
3546
  var init_openAiCompatibleProvider = __esm({
3386
3547
  "packages/agent-runtime/openAiCompatibleProvider.ts"() {
3387
3548
  "use strict";
@@ -3679,7 +3840,8 @@ function buildPlatformChatCompletionRequest(args2) {
3679
3840
  const body = {
3680
3841
  model: args2.providerConfig.model,
3681
3842
  ...usesResponsesApi ? { input: convertMessagesToResponsesInput(args2.messages) } : { messages: toOpenAiCompatibleMessages2(args2.messages) },
3682
- stream: false,
3843
+ stream: args2.stream ?? false,
3844
+ ...args2.stream ? { stream_options: { include_usage: true } } : {},
3683
3845
  ...requestOptions,
3684
3846
  ...args2.tools && args2.tools.length > 0 ? {
3685
3847
  tools: usesResponsesApi ? toResponsesTools(args2.tools) : args2.tools,
@@ -10109,6 +10271,8 @@ var init_ai_locale = __esm({
10109
10271
  creating: "Creating...",
10110
10272
  edit: "Edit",
10111
10273
  delete: "Delete",
10274
+ favorite: "Favorite",
10275
+ unfavorite: "Unfavorite",
10112
10276
  confirmDelete: "Confirm Delete",
10113
10277
  resetToDefaults: "Reset to Defaults",
10114
10278
  loading: "Loading...",
@@ -10420,6 +10584,8 @@ var init_ai_locale = __esm({
10420
10584
  creating: "\u521B\u5EFA\u4E2D...",
10421
10585
  edit: "\u7F16\u8F91",
10422
10586
  delete: "\u5220\u9664",
10587
+ favorite: "\u6536\u85CF",
10588
+ unfavorite: "\u53D6\u6D88\u6536\u85CF",
10423
10589
  confirmDelete: "\u786E\u8BA4\u5220\u9664",
10424
10590
  resetToDefaults: "\u91CD\u7F6E\u4E3A\u9ED8\u8BA4\u503C",
10425
10591
  loading: "\u52A0\u8F7D\u4E2D...",
@@ -10732,6 +10898,8 @@ var init_ai_locale = __esm({
10732
10898
  creating: "\u5EFA\u7ACB\u4E2D...",
10733
10899
  edit: "\u7DE8\u8F2F",
10734
10900
  delete: "\u522A\u9664",
10901
+ favorite: "\u6536\u85CF",
10902
+ unfavorite: "\u53D6\u6D88\u6536\u85CF",
10735
10903
  confirmDelete: "\u78BA\u8A8D\u522A\u9664",
10736
10904
  resetToDefaults: "\u91CD\u8A2D\u70BA\u9810\u8A2D\u503C",
10737
10905
  loading: "\u8F09\u5165\u4E2D...",
@@ -10967,6 +11135,8 @@ var init_ai_locale = __esm({
10967
11135
  creating: "\u4F5C\u6210\u4E2D...",
10968
11136
  edit: "\u7DE8\u96C6",
10969
11137
  delete: "\u524A\u9664",
11138
+ favorite: "\u304A\u6C17\u306B\u5165\u308A",
11139
+ unfavorite: "\u304A\u6C17\u306B\u5165\u308A\u89E3\u9664",
10970
11140
  confirmDelete: "\u524A\u9664\u306E\u78BA\u8A8D",
10971
11141
  resetToDefaults: "\u30C7\u30D5\u30A9\u30EB\u30C8\u306B\u30EA\u30BB\u30C3\u30C8",
10972
11142
  loading: "\u8AAD\u307F\u8FBC\u307F\u4E2D...",
@@ -13042,6 +13212,31 @@ var init_interface_locale = __esm({
13042
13212
  stop: "Stop",
13043
13213
  refresh: "Refresh"
13044
13214
  },
13215
+ productivity: {
13216
+ title: "Productivity",
13217
+ default_space: {
13218
+ title: "Default startup space",
13219
+ description: "Choose the workspace you land in after signing in to get into flow faster.",
13220
+ all: "All (no fixed default space)"
13221
+ },
13222
+ shortcuts: {
13223
+ title: "Keyboard shortcuts",
13224
+ description: "View and customize shortcuts for common actions to keep your workflow smooth.",
13225
+ sendMessage: "Send message",
13226
+ newChat: "New chat",
13227
+ deleteDialog: "Delete current conversation",
13228
+ clickToRecord: "Click to change shortcut",
13229
+ updated: "Shortcut updated",
13230
+ pressKeys: "Press shortcut keys...",
13231
+ none: "Disabled",
13232
+ cleared: "Shortcut disabled",
13233
+ clearError: "Failed to disable shortcut",
13234
+ clear: "Disable shortcut",
13235
+ resetSuccess: "Shortcut reset",
13236
+ resetError: "Failed to reset shortcut",
13237
+ reset: "Reset to default"
13238
+ }
13239
+ },
13045
13240
  appearance: {
13046
13241
  title: "Appearance",
13047
13242
  theme: {
@@ -13735,6 +13930,31 @@ var init_interface_locale = __esm({
13735
13930
  productivity: "\u6548\u7387",
13736
13931
  secrets: "\u5BC6\u94A5"
13737
13932
  },
13933
+ productivity: {
13934
+ title: "\u6548\u7387\u8BBE\u7F6E",
13935
+ default_space: {
13936
+ title: "\u9ED8\u8BA4\u542F\u52A8\u7A7A\u95F4",
13937
+ description: "\u9009\u62E9\u4F60\u767B\u5F55\u540E\u9ED8\u8BA4\u8FDB\u5165\u7684\u5DE5\u4F5C\u7A7A\u95F4\u3002\u8FD9\u80FD\u5E2E\u52A9\u4F60\u5FEB\u901F\u8FDB\u5165\u5DE5\u4F5C\u72B6\u6001\u3002",
13938
+ all: "\u5168\u90E8\uFF08\u4E0D\u56FA\u5B9A\u9ED8\u8BA4\u7A7A\u95F4\uFF09"
13939
+ },
13940
+ shortcuts: {
13941
+ title: "\u952E\u76D8\u5FEB\u6377\u952E",
13942
+ description: "\u67E5\u770B\u5E76\u7BA1\u7406\u5E38\u7528\u64CD\u4F5C\u7684\u5FEB\u6377\u952E\u914D\u7F6E\uFF0C\u8BA9\u4F60\u7684\u5DE5\u4F5C\u6D41\u66F4\u52A0\u987A\u7545\u3002",
13943
+ sendMessage: "\u53D1\u9001\u6D88\u606F",
13944
+ newChat: "\u65B0\u5EFA\u5BF9\u8BDD",
13945
+ deleteDialog: "\u5220\u9664\u5F53\u524D\u4F1A\u8BDD",
13946
+ clickToRecord: "\u70B9\u51FB\u4FEE\u6539\u5FEB\u6377\u952E",
13947
+ updated: "\u5FEB\u6377\u952E\u5DF2\u66F4\u65B0",
13948
+ pressKeys: "\u6309\u4E0B\u5FEB\u6377\u952E...",
13949
+ none: "\u5DF2\u7981\u7528",
13950
+ cleared: "\u5FEB\u6377\u952E\u5DF2\u7981\u7528",
13951
+ clearError: "\u7981\u7528\u5931\u8D25",
13952
+ clear: "\u7981\u7528\u5FEB\u6377\u952E",
13953
+ resetSuccess: "\u5FEB\u6377\u952E\u5DF2\u91CD\u7F6E",
13954
+ resetError: "\u91CD\u7F6E\u5931\u8D25",
13955
+ reset: "\u91CD\u7F6E\u4E3A\u9ED8\u8BA4"
13956
+ }
13957
+ },
13738
13958
  appearance: {
13739
13959
  title: "\u5916\u89C2",
13740
13960
  theme: {
@@ -14429,6 +14649,31 @@ var init_interface_locale = __esm({
14429
14649
  productivity: "\u6548\u7387",
14430
14650
  secrets: "\u5BC6\u9470"
14431
14651
  },
14652
+ productivity: {
14653
+ title: "\u6548\u7387\u8A2D\u5B9A",
14654
+ default_space: {
14655
+ title: "\u9810\u8A2D\u555F\u52D5\u7A7A\u9593",
14656
+ description: "\u9078\u64C7\u4F60\u767B\u5165\u5F8C\u9810\u8A2D\u9032\u5165\u7684\u5DE5\u4F5C\u7A7A\u9593\uFF0C\u5E6B\u52A9\u4F60\u66F4\u5FEB\u9032\u5165\u5DE5\u4F5C\u72C0\u614B\u3002",
14657
+ all: "\u5168\u90E8\uFF08\u4E0D\u56FA\u5B9A\u9810\u8A2D\u7A7A\u9593\uFF09"
14658
+ },
14659
+ shortcuts: {
14660
+ title: "\u9375\u76E4\u5FEB\u6377\u9375",
14661
+ description: "\u67E5\u770B\u4E26\u7BA1\u7406\u5E38\u7528\u64CD\u4F5C\u7684\u5FEB\u6377\u9375\u8A2D\u5B9A\uFF0C\u8B93\u4F60\u7684\u5DE5\u4F5C\u6D41\u66F4\u52A0\u9806\u66A2\u3002",
14662
+ sendMessage: "\u50B3\u9001\u8A0A\u606F",
14663
+ newChat: "\u65B0\u5EFA\u5C0D\u8A71",
14664
+ deleteDialog: "\u522A\u9664\u76EE\u524D\u6703\u8A71",
14665
+ clickToRecord: "\u9EDE\u64CA\u4FEE\u6539\u5FEB\u6377\u9375",
14666
+ updated: "\u5FEB\u6377\u9375\u5DF2\u66F4\u65B0",
14667
+ pressKeys: "\u6309\u4E0B\u5FEB\u6377\u9375...",
14668
+ none: "\u5DF2\u505C\u7528",
14669
+ cleared: "\u5FEB\u6377\u9375\u5DF2\u505C\u7528",
14670
+ clearError: "\u505C\u7528\u5931\u6557",
14671
+ clear: "\u505C\u7528\u5FEB\u6377\u9375",
14672
+ resetSuccess: "\u5FEB\u6377\u9375\u5DF2\u91CD\u8A2D",
14673
+ resetError: "\u91CD\u8A2D\u5931\u6557",
14674
+ reset: "\u91CD\u8A2D\u70BA\u9810\u8A2D"
14675
+ }
14676
+ },
14432
14677
  appearance: {
14433
14678
  title: "\u5916\u89C0",
14434
14679
  theme: {
@@ -15121,6 +15366,31 @@ var init_interface_locale = __esm({
15121
15366
  productivity: "\u52B9\u7387",
15122
15367
  secrets: "\u30B7\u30FC\u30AF\u30EC\u30C3\u30C8"
15123
15368
  },
15369
+ productivity: {
15370
+ title: "\u52B9\u7387\u8A2D\u5B9A",
15371
+ default_space: {
15372
+ title: "\u65E2\u5B9A\u306E\u8D77\u52D5\u30B9\u30DA\u30FC\u30B9",
15373
+ description: "\u30ED\u30B0\u30A4\u30F3\u5F8C\u306B\u6700\u521D\u306B\u958B\u304F\u30EF\u30FC\u30AF\u30B9\u30DA\u30FC\u30B9\u3092\u9078\u3073\u3001\u3059\u3050\u4F5C\u696D\u306B\u5165\u308C\u308B\u3088\u3046\u306B\u3057\u307E\u3059\u3002",
15374
+ all: "\u3059\u3079\u3066\uFF08\u65E2\u5B9A\u30B9\u30DA\u30FC\u30B9\u3092\u56FA\u5B9A\u3057\u306A\u3044\uFF09"
15375
+ },
15376
+ shortcuts: {
15377
+ title: "\u30AD\u30FC\u30DC\u30FC\u30C9\u30B7\u30E7\u30FC\u30C8\u30AB\u30C3\u30C8",
15378
+ description: "\u3088\u304F\u4F7F\u3046\u64CD\u4F5C\u306E\u30B7\u30E7\u30FC\u30C8\u30AB\u30C3\u30C8\u3092\u78BA\u8A8D\u30FB\u5909\u66F4\u3057\u3066\u3001\u4F5C\u696D\u306E\u6D41\u308C\u3092\u30B9\u30E0\u30FC\u30BA\u306B\u3057\u307E\u3059\u3002",
15379
+ sendMessage: "\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u9001\u4FE1",
15380
+ newChat: "\u65B0\u3057\u3044\u30C1\u30E3\u30C3\u30C8",
15381
+ deleteDialog: "\u73FE\u5728\u306E\u4F1A\u8A71\u3092\u524A\u9664",
15382
+ clickToRecord: "\u30AF\u30EA\u30C3\u30AF\u3057\u3066\u30B7\u30E7\u30FC\u30C8\u30AB\u30C3\u30C8\u3092\u5909\u66F4",
15383
+ updated: "\u30B7\u30E7\u30FC\u30C8\u30AB\u30C3\u30C8\u3092\u66F4\u65B0\u3057\u307E\u3057\u305F",
15384
+ pressKeys: "\u30B7\u30E7\u30FC\u30C8\u30AB\u30C3\u30C8\u30AD\u30FC\u3092\u62BC\u3057\u3066\u304F\u3060\u3055\u3044...",
15385
+ none: "\u7121\u52B9",
15386
+ cleared: "\u30B7\u30E7\u30FC\u30C8\u30AB\u30C3\u30C8\u3092\u7121\u52B9\u306B\u3057\u307E\u3057\u305F",
15387
+ clearError: "\u7121\u52B9\u5316\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
15388
+ clear: "\u30B7\u30E7\u30FC\u30C8\u30AB\u30C3\u30C8\u3092\u7121\u52B9\u5316",
15389
+ resetSuccess: "\u30B7\u30E7\u30FC\u30C8\u30AB\u30C3\u30C8\u3092\u30EA\u30BB\u30C3\u30C8\u3057\u307E\u3057\u305F",
15390
+ resetError: "\u30EA\u30BB\u30C3\u30C8\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
15391
+ reset: "\u65E2\u5B9A\u306B\u623B\u3059"
15392
+ }
15393
+ },
15124
15394
  appearance: {
15125
15395
  title: "\u5916\u89B3",
15126
15396
  theme: {
@@ -19818,7 +20088,7 @@ var init_userPreferenceRegister = __esm({
19818
20088
  });
19819
20089
 
19820
20090
  // packages/app/theme/colors.ts
19821
- var semantic, mkLight, mkDark, neutral, ocean, iris, forest, trail, wave, rose, ember, blue, purple, green, graphite, mocha, pink, red, orange, yellow;
20091
+ var semantic, mkLight, mkDark, neutral, ocean, iris, forest, trail, wave, rose, ember, catppuccin, blue, purple, green, graphite, mocha, pink, red, orange, yellow;
19822
20092
  var init_colors = __esm({
19823
20093
  "packages/app/theme/colors.ts"() {
19824
20094
  "use strict";
@@ -19847,7 +20117,8 @@ var init_colors = __esm({
19847
20117
  codeBackground: bg3,
19848
20118
  shadowLight: `rgba(0,0,0,0.04)`,
19849
20119
  shadowMedium: `rgba(0,0,0,0.08)`,
19850
- shadowHeavy: `rgba(0,0,0,0.14)`
20120
+ shadowHeavy: `rgba(0,0,0,0.14)`,
20121
+ textOnPrimary: "#FFFFFF"
19851
20122
  });
19852
20123
  mkDark = (bg, bg2, bg3, textPrimary, textSec, textTer, border, borderL, shadowBase = "0,0,0") => ({
19853
20124
  background: bg,
@@ -19870,7 +20141,8 @@ var init_colors = __esm({
19870
20141
  codeBackground: bg2,
19871
20142
  shadowLight: `rgba(${shadowBase},0.18)`,
19872
20143
  shadowMedium: `rgba(${shadowBase},0.30)`,
19873
- shadowHeavy: `rgba(${shadowBase},0.44)`
20144
+ shadowHeavy: `rgba(${shadowBase},0.44)`,
20145
+ textOnPrimary: "#FFFFFF"
19874
20146
  });
19875
20147
  neutral = {
19876
20148
  light: {
@@ -19881,7 +20153,8 @@ var init_colors = __esm({
19881
20153
  primaryGhost: "rgba(113,113,122,0.08)",
19882
20154
  primaryHover: "rgba(113,113,122,0.10)",
19883
20155
  borderAccent: "#D4D4D8",
19884
- ...semantic.light
20156
+ ...semantic.light,
20157
+ ...mkLight("#FFFFFF", "#F4F4F5", "#E4E4E7", "#18181B", "#52525B", "#71717A", "#E4E4E7", "#F4F4F5")
19885
20158
  },
19886
20159
  dark: {
19887
20160
  primary: "#A1A1AA",
@@ -19891,7 +20164,9 @@ var init_colors = __esm({
19891
20164
  primaryGhost: "rgba(161,161,170,0.10)",
19892
20165
  primaryHover: "rgba(161,161,170,0.12)",
19893
20166
  borderAccent: "#52525B",
19894
- ...semantic.dark
20167
+ ...semantic.dark,
20168
+ ...mkDark("#18181B", "#27272A", "#3F3F46", "#FAFAFA", "#D4D4D8", "#A1A1AA", "#3F3F46", "#27272A", "24,24,27"),
20169
+ textOnPrimary: "#18181B"
19895
20170
  }
19896
20171
  };
19897
20172
  ocean = {
@@ -19916,7 +20191,8 @@ var init_colors = __esm({
19916
20191
  borderAccent: "#1F6FEB",
19917
20192
  ...semantic.dark,
19918
20193
  ...mkDark("#0D1117", "#161B22", "#21262D", "#E6EDF3", "#8B949E", "#6E7681", "#30363D", "#161B22", "13,17,23"),
19919
- textHeading: "#E6EDF3"
20194
+ textHeading: "#E6EDF3",
20195
+ textOnPrimary: "#0D1117"
19920
20196
  }
19921
20197
  };
19922
20198
  iris = {
@@ -19941,7 +20217,8 @@ var init_colors = __esm({
19941
20217
  borderAccent: "#4A55C0",
19942
20218
  ...semantic.dark,
19943
20219
  ...mkDark("#0F0E17", "#16141F", "#1E1B2E", "#E8E5F7", "#A8A4C8", "#7B7698", "#272435", "#16141F", "15,14,23"),
19944
- textHeading: "#E8E5F7"
20220
+ textHeading: "#E8E5F7",
20221
+ textOnPrimary: "#0F0E17"
19945
20222
  }
19946
20223
  };
19947
20224
  forest = {
@@ -19967,10 +20244,12 @@ var init_colors = __esm({
19967
20244
  ...semantic.dark,
19968
20245
  ...mkDark("#0C1209", "#121A0E", "#1A2416", "#E4F0E2", "#87A882", "#5E7A5A", "#1A2416", "#121A0E", "12,18,9"),
19969
20246
  success: "#4ADE80",
19970
- textHeading: "#C8EEC4"
20247
+ textHeading: "#C8EEC4",
20248
+ textOnPrimary: "#0C1209"
19971
20249
  }
19972
20250
  };
19973
20251
  trail = {
20252
+ meta: { radiusBoost: 1, motionEase: "cubic-bezier(0.22, 1, 0.36, 1)" },
19974
20253
  light: {
19975
20254
  primary: "#2E7DB5",
19976
20255
  primaryLight: "#6BB5E0",
@@ -20007,7 +20286,8 @@ var init_colors = __esm({
20007
20286
  shadowLight: "rgba(28, 36, 48, 0.05)",
20008
20287
  shadowMedium: "rgba(28, 36, 48, 0.09)",
20009
20288
  shadowHeavy: "rgba(28, 36, 48, 0.14)",
20010
- textHeading: "#1C2430"
20289
+ textHeading: "#1C2430",
20290
+ textOnPrimary: "#FFFFFF"
20011
20291
  },
20012
20292
  dark: {
20013
20293
  primary: "#5BA3D9",
@@ -20045,7 +20325,8 @@ var init_colors = __esm({
20045
20325
  shadowLight: "rgba(4, 8, 12, 0.22)",
20046
20326
  shadowMedium: "rgba(4, 8, 12, 0.34)",
20047
20327
  shadowHeavy: "rgba(4, 8, 12, 0.46)",
20048
- textHeading: "#E8F4FC"
20328
+ textHeading: "#E8F4FC",
20329
+ textOnPrimary: "#0B1218"
20049
20330
  }
20050
20331
  };
20051
20332
  wave = {
@@ -20082,7 +20363,8 @@ var init_colors = __esm({
20082
20363
  codeBackground: "#ECEAE3",
20083
20364
  shadowLight: "rgba(26, 26, 34, 0.05)",
20084
20365
  shadowMedium: "rgba(26, 26, 34, 0.09)",
20085
- shadowHeavy: "rgba(26, 26, 34, 0.14)"
20366
+ shadowHeavy: "rgba(26, 26, 34, 0.14)",
20367
+ textOnPrimary: "#FFFFFF"
20086
20368
  },
20087
20369
  dark: {
20088
20370
  // Kanagawa Wave — crystalBlue accent(#7E9CD8 是函数名颜色,最标志性的 Kanagawa 蓝)
@@ -20129,8 +20411,9 @@ var init_colors = __esm({
20129
20411
  shadowLight: "rgba(13, 12, 12, 0.18)",
20130
20412
  shadowMedium: "rgba(13, 12, 12, 0.28)",
20131
20413
  shadowHeavy: "rgba(13, 12, 12, 0.38)",
20132
- textHeading: "#DCD7BA"
20414
+ textHeading: "#DCD7BA",
20133
20415
  // fujiWhite — 暖色标题
20416
+ textOnPrimary: "#1F1F28"
20134
20417
  }
20135
20418
  };
20136
20419
  rose = {
@@ -20166,7 +20449,8 @@ var init_colors = __esm({
20166
20449
  codeBackground: "#F2E9E1",
20167
20450
  shadowLight: "rgba(87, 82, 121, 0.05)",
20168
20451
  shadowMedium: "rgba(87, 82, 121, 0.09)",
20169
- shadowHeavy: "rgba(87, 82, 121, 0.14)"
20452
+ shadowHeavy: "rgba(87, 82, 121, 0.14)",
20453
+ textOnPrimary: "#FFFFFF"
20170
20454
  },
20171
20455
  dark: {
20172
20456
  // Rosé Pine main
@@ -20201,7 +20485,8 @@ var init_colors = __esm({
20201
20485
  shadowLight: "rgba(12, 10, 20, 0.20)",
20202
20486
  shadowMedium: "rgba(12, 10, 20, 0.30)",
20203
20487
  shadowHeavy: "rgba(12, 10, 20, 0.44)",
20204
- textHeading: "#E0DEF4"
20488
+ textHeading: "#E0DEF4",
20489
+ textOnPrimary: "#191724"
20205
20490
  }
20206
20491
  };
20207
20492
  ember = {
@@ -20226,14 +20511,54 @@ var init_colors = __esm({
20226
20511
  borderAccent: "#92400E",
20227
20512
  ...semantic.dark,
20228
20513
  ...mkDark("#1A1207", "#221809", "#2E220C", "#F0E2C0", "#C4A056", "#8A7040", "#3A2A10", "#221809", "26,18,7"),
20229
- textHeading: "#FCD34D"
20514
+ textHeading: "#FCD34D",
20515
+ textOnPrimary: "#1A1207"
20516
+ }
20517
+ };
20518
+ catppuccin = {
20519
+ light: {
20520
+ // Catppuccin Latte
20521
+ primary: "#1e66f5",
20522
+ // Blue
20523
+ primaryLight: "#89b4fa",
20524
+ // Blue Light
20525
+ primaryDark: "#4c4f69",
20526
+ // Text
20527
+ primaryGradient: "linear-gradient(135deg, #1e66f5, #89b4fa)",
20528
+ primaryGhost: "rgba(30, 102, 245, 0.08)",
20529
+ primaryHover: "rgba(30, 102, 245, 0.10)",
20530
+ borderAccent: "#ccd0da",
20531
+ success: "#40a02b",
20532
+ warning: "#df8e1d",
20533
+ info: "#1e66f5",
20534
+ error: "#d20f39",
20535
+ ...mkLight("#eff1f5", "#e6e9ef", "#dce0e8", "#4c4f69", "#5c5f77", "#6c6f85", "#ccd0da", "#e6e9ef")
20536
+ },
20537
+ dark: {
20538
+ // Catppuccin Mocha
20539
+ primary: "#89b4fa",
20540
+ // Blue
20541
+ primaryLight: "#b4befe",
20542
+ // Lavender
20543
+ primaryDark: "#1e66f5",
20544
+ primaryGradient: "linear-gradient(135deg, #89b4fa, #b4befe)",
20545
+ primaryGhost: "rgba(137, 180, 250, 0.10)",
20546
+ primaryHover: "rgba(137, 180, 250, 0.14)",
20547
+ borderAccent: "#313244",
20548
+ success: "#a6e3a1",
20549
+ warning: "#f9e2af",
20550
+ info: "#89b4fa",
20551
+ error: "#f38ba8",
20552
+ ...mkDark("#1e1e2e", "#181825", "#11111b", "#cdd6f4", "#bac2de", "#a6adc8", "#313244", "#181825", "30,30,46"),
20553
+ textHeading: "#f5e0dc",
20554
+ textOnPrimary: "#11111b"
20230
20555
  }
20231
20556
  };
20232
20557
  blue = ocean;
20233
20558
  purple = iris;
20234
20559
  green = forest;
20235
20560
  graphite = neutral;
20236
- mocha = rose;
20561
+ mocha = catppuccin;
20237
20562
  pink = rose;
20238
20563
  red = ember;
20239
20564
  orange = ember;
@@ -20242,7 +20567,7 @@ var init_colors = __esm({
20242
20567
  });
20243
20568
 
20244
20569
  // packages/app/theme/theme.config.ts
20245
- var SPACE, THEME_COLORS, THEME_NAME_ALIASES, DEFAULT_THEME_NAME, BASE_COLORS, MODE_COLORS;
20570
+ var SPACE, THEME_COLORS, THEME_NAME_ALIASES, DEFAULT_THEME_NAME;
20246
20571
  var init_theme_config = __esm({
20247
20572
  "packages/app/theme/theme.config.ts"() {
20248
20573
  "use strict";
@@ -20273,6 +20598,7 @@ var init_theme_config = __esm({
20273
20598
  wave,
20274
20599
  rose,
20275
20600
  ember,
20601
+ catppuccin,
20276
20602
  // alias — DB 中可能存了旧 key,映射到最近似的新主题
20277
20603
  blue,
20278
20604
  purple,
@@ -20285,7 +20611,8 @@ var init_theme_config = __esm({
20285
20611
  mocha
20286
20612
  };
20287
20613
  THEME_NAME_ALIASES = {
20288
- blue: "ocean",
20614
+ blue: "catppuccin",
20615
+ ocean: "catppuccin",
20289
20616
  purple: "iris",
20290
20617
  green: "forest",
20291
20618
  orange: "ember",
@@ -20293,86 +20620,9 @@ var init_theme_config = __esm({
20293
20620
  graphite: "neutral",
20294
20621
  pink: "rose",
20295
20622
  red: "ember",
20296
- mocha: "rose"
20623
+ mocha: "catppuccin"
20297
20624
  };
20298
- DEFAULT_THEME_NAME = "ocean";
20299
- BASE_COLORS = {
20300
- light: {
20301
- background: "#FFFFFF",
20302
- backgroundSecondary: "#F4F4F5",
20303
- // zinc-100
20304
- backgroundTertiary: "#E4E4E7",
20305
- // zinc-200
20306
- backgroundGhost: "rgba(255, 255, 255, 0.94)",
20307
- backgroundHover: "#D4D4D8",
20308
- // zinc-300
20309
- backgroundSelected: "#A1A1AA",
20310
- // zinc-400
20311
- text: "#18181B",
20312
- // zinc-900
20313
- textSecondary: "#52525B",
20314
- // zinc-600
20315
- textTertiary: "#71717A",
20316
- // zinc-500
20317
- textQuaternary: "#A1A1AA",
20318
- // zinc-400
20319
- textLight: "#D4D4D8",
20320
- // zinc-300
20321
- placeholder: "#A1A1AA",
20322
- // zinc-400
20323
- border: "#E4E4E7",
20324
- // zinc-200
20325
- borderHover: "#A1A1AA",
20326
- // zinc-400
20327
- borderLight: "#F4F4F5",
20328
- // zinc-100
20329
- error: "#DC2626",
20330
- shadowLight: "rgba(0, 0, 0, 0.04)",
20331
- shadowMedium: "rgba(0, 0, 0, 0.08)",
20332
- shadowHeavy: "rgba(0, 0, 0, 0.14)",
20333
- messageBackground: "#FFFFFF",
20334
- codeBackground: "#F4F4F5"
20335
- },
20336
- dark: {
20337
- background: "#18181B",
20338
- // zinc-900
20339
- backgroundSecondary: "#27272A",
20340
- // zinc-800
20341
- backgroundTertiary: "#3F3F46",
20342
- // zinc-700
20343
- backgroundGhost: "rgba(24, 24, 27, 0.94)",
20344
- backgroundHover: "#52525B",
20345
- // zinc-600
20346
- backgroundSelected: "#3F3F46",
20347
- // zinc-700
20348
- text: "#FAFAFA",
20349
- // zinc-50
20350
- textSecondary: "#D4D4D8",
20351
- // zinc-300
20352
- textTertiary: "#A1A1AA",
20353
- // zinc-400
20354
- textQuaternary: "#71717A",
20355
- // zinc-500
20356
- textLight: "#52525B",
20357
- // zinc-600
20358
- placeholder: "#71717A",
20359
- // zinc-500
20360
- border: "#3F3F46",
20361
- // zinc-700
20362
- borderHover: "#71717A",
20363
- // zinc-500
20364
- borderLight: "#27272A",
20365
- // zinc-800
20366
- error: "#F87171",
20367
- shadowLight: "rgba(0, 0, 0, 0.16)",
20368
- shadowMedium: "rgba(0, 0, 0, 0.28)",
20369
- shadowHeavy: "rgba(0, 0, 0, 0.40)",
20370
- messageBackground: "#27272A",
20371
- codeBackground: "#09090B"
20372
- // zinc-950
20373
- }
20374
- };
20375
- MODE_COLORS = BASE_COLORS;
20625
+ DEFAULT_THEME_NAME = "catppuccin";
20376
20626
  }
20377
20627
  });
20378
20628
 
@@ -20779,6 +21029,7 @@ __export(settingSlice_exports, {
20779
21029
  selectDefaultAgentId: () => selectDefaultAgentId,
20780
21030
  selectDefaultAgentPreference: () => selectDefaultAgentPreference,
20781
21031
  selectDefaultSpaceId: () => selectDefaultSpaceId,
21032
+ selectDeleteShortcut: () => selectDeleteShortcut,
20782
21033
  selectDensity: () => selectDensity,
20783
21034
  selectDesktopChromeConnectorEnabled: () => selectDesktopChromeConnectorEnabled,
20784
21035
  selectEditorAutoSave: () => selectEditorAutoSave,
@@ -20847,7 +21098,7 @@ import {
20847
21098
  asyncThunkCreator,
20848
21099
  createSelector as createSelector2
20849
21100
  } from "@reduxjs/toolkit";
20850
- var SYSTEM_DEFAULT_AGENT_ID, initialState, createSliceWithThunks, hasOwn, normalizeSidebarVisibleTypesSetting, normalizeDefaultAgentIdSetting, normalizeAuthorityHomeServerSetting, normalizeTonePresetSetting, normalizePolicyLevelSetting, resolveDefaultAgentIdSetting, selectResolvedDefaultAgentId, hexToRgbString, alphaColor, getCachedDefaultAgentRegisterRecord, persistDefaultAgentRegister, omitKeys, normalizeSettingChanges, LOCAL_FIRST_APPEARANCE_KEYS, LOCAL_ONLY_SETTINGS_KEYS, isLocalFirstAppearanceChange, stripRegisterBackedFieldsFromSettingsWrite, sanitizeStoredSettingsRecord, hydrateStoredSettings, buildSettingsPersistencePlan, settingSlice, getSettings, setSettings, clearDefaultSpaceId, addHostToCurrentServer, changeTheme, changeDensity, changeFontPreset, changeDarkMode, toggleShowThinking, setThemeFollowsSystem, setSidebarWidth, toggleEnableReadCurrentSpace, setEditorDefaultMode, setEditorLightCodeTheme, setEditorDarkCodeTheme, setEditorCodeTheme, toggleEditorWordCount, toggleEditorShortcut, setEditorFontSize, toggleEditorAutoSave, setEditorAutoSaveInterval, setGlobalPrompt, setUserTonePreset, setKnowledgeCaptureLevel, setSpaceContextLevel, setAiRecentContentLimit, setContextRetention, setMaxExecutionTime, setDefaultAgentId, setPreferredAnimationSet, setThemeMode, selectSettings, isLocalServerUrl2, resolveDesktopSafeServer, selectCurrentServer, selectSyncServers, selectRemoteServer, selectRemoteSyncServers, selectRemoteServers, selectDefaultSpaceId, selectPreferredAnimationSet, selectShowThinking, selectMaxCost, selectMaxExecutionTime, selectIsDark, selectThemeMode, selectHeaderHeight, selectThemeName, selectThemeFollowsSystem, selectSidebarWidth, selectDensity, selectFontPreset, selectEnableReadCurrentSpace, selectSidebarVisibleTypes, selectGlobalPrompt, selectUserTonePreset, selectKnowledgeCaptureLevel, selectSpaceContextLevel, selectAutoApproveSelfUpdateFields, selectAiRecentContentLimit, selectContextRetention, selectDefaultAgentPreference, selectDefaultAgentId, selectOcrModel, selectShowScrollToTopButton, selectShowScrollToBottomButton, selectCreateMenuOpenCount, selectDesktopChromeConnectorEnabled, selectEditorDefaultMode, selectEditorLightCodeTheme, selectEditorDarkCodeTheme, selectEditorCodeTheme, selectEditorWordCountEnabled, selectEditorShortcuts, selectEditorFontSize, selectEditorAutoSave, selectEditorAutoSaveInterval, selectTheme, selectEditorConfig, settingSlice_default;
21101
+ var SYSTEM_DEFAULT_AGENT_ID, initialState, createSliceWithThunks, hasOwn, normalizeSidebarVisibleTypesSetting, normalizeDefaultAgentIdSetting, normalizeAuthorityHomeServerSetting, normalizeTonePresetSetting, normalizePolicyLevelSetting, resolveDefaultAgentIdSetting, selectResolvedDefaultAgentId, hexToRgbString, alphaColor, getCachedDefaultAgentRegisterRecord, persistDefaultAgentRegister, omitKeys, normalizeSettingChanges, LOCAL_FIRST_APPEARANCE_KEYS, LOCAL_ONLY_SETTINGS_KEYS, isLocalFirstAppearanceChange, stripRegisterBackedFieldsFromSettingsWrite, sanitizeStoredSettingsRecord, hydrateStoredSettings, buildSettingsPersistencePlan, settingSlice, getSettings, setSettings, clearDefaultSpaceId, addHostToCurrentServer, changeTheme, changeDensity, changeFontPreset, changeDarkMode, toggleShowThinking, setThemeFollowsSystem, setSidebarWidth, toggleEnableReadCurrentSpace, setEditorDefaultMode, setEditorLightCodeTheme, setEditorDarkCodeTheme, setEditorCodeTheme, toggleEditorWordCount, toggleEditorShortcut, setEditorFontSize, toggleEditorAutoSave, setEditorAutoSaveInterval, setGlobalPrompt, setUserTonePreset, setKnowledgeCaptureLevel, setSpaceContextLevel, setAiRecentContentLimit, setContextRetention, setMaxExecutionTime, setDefaultAgentId, setPreferredAnimationSet, setThemeMode, selectSettings, isLocalServerUrl2, resolveDesktopSafeServer, selectCurrentServer, selectSyncServers, selectRemoteServer, selectRemoteSyncServers, selectRemoteServers, selectDefaultSpaceId, selectPreferredAnimationSet, selectShowThinking, selectMaxCost, selectMaxExecutionTime, selectIsDark, selectThemeMode, selectHeaderHeight, selectThemeName, selectThemeFollowsSystem, selectSidebarWidth, selectDensity, selectFontPreset, selectEnableReadCurrentSpace, selectSidebarVisibleTypes, selectGlobalPrompt, selectUserTonePreset, selectKnowledgeCaptureLevel, selectSpaceContextLevel, selectAutoApproveSelfUpdateFields, selectAiRecentContentLimit, selectContextRetention, selectDefaultAgentPreference, selectDefaultAgentId, selectOcrModel, selectShowScrollToTopButton, selectShowScrollToBottomButton, selectCreateMenuOpenCount, selectDesktopChromeConnectorEnabled, selectEditorDefaultMode, selectEditorLightCodeTheme, selectEditorDarkCodeTheme, selectEditorCodeTheme, selectEditorWordCountEnabled, selectEditorShortcuts, selectDeleteShortcut, selectEditorFontSize, selectEditorAutoSave, selectEditorAutoSaveInterval, selectTheme, selectEditorConfig, settingSlice_default;
20851
21102
  var init_settingSlice = __esm({
20852
21103
  "packages/app/settings/settingSlice.tsx"() {
20853
21104
  "use strict";
@@ -20920,7 +21171,8 @@ var init_settingSlice = __esm({
20920
21171
  showScrollToTopButton: false,
20921
21172
  showScrollToBottomButton: false,
20922
21173
  createMenuOpenCount: 0,
20923
- desktopChromeConnectorEnabled: false
21174
+ desktopChromeConnectorEnabled: false,
21175
+ deleteShortcut: typeof window !== "undefined" && typeof window.navigator !== "undefined" && /Mac|iPod|iPhone|iPad/.test(window.navigator.platform) ? "meta+backspace" : "ctrl+backspace"
20924
21176
  };
20925
21177
  createSliceWithThunks = buildCreateSlice({
20926
21178
  creators: { asyncThunk: asyncThunkCreator }
@@ -21548,6 +21800,7 @@ var init_settingSlice = __esm({
21548
21800
  );
21549
21801
  selectEditorWordCountEnabled = (state) => state.settings.editorWordCountEnabled;
21550
21802
  selectEditorShortcuts = (state) => state.settings.editorShortcuts;
21803
+ selectDeleteShortcut = (state) => state.settings.deleteShortcut;
21551
21804
  selectEditorFontSize = (state) => state.settings.editorFontSize;
21552
21805
  selectEditorAutoSave = (state) => state.settings.editorAutoSave;
21553
21806
  selectEditorAutoSaveInterval = (state) => state.settings.editorAutoSaveInterval;
@@ -21564,9 +21817,9 @@ var init_settingSlice = __esm({
21564
21817
  const mode = isDark ? "dark" : "light";
21565
21818
  const validThemeName = THEME_COLORS[themeName] ? themeName : DEFAULT_THEME_NAME;
21566
21819
  const validFontPreset = normalizeFontPreset(fontPreset) ?? DEFAULT_FONT_PRESET;
21567
- const baseColors = MODE_COLORS[mode];
21568
- const themeColors = THEME_COLORS[validThemeName][mode];
21569
- const c = { ...baseColors, ...themeColors };
21820
+ const themeData = THEME_COLORS[validThemeName];
21821
+ const c = themeData[mode];
21822
+ const meta = themeData.meta;
21570
21823
  const compact2 = density === "compact";
21571
21824
  return {
21572
21825
  sidebarWidth: `${sidebarWidth}px`,
@@ -21618,14 +21871,14 @@ var init_settingSlice = __esm({
21618
21871
  },
21619
21872
  // Border radius scale — xs/sm/md map to control/surface/overlay tiers.
21620
21873
  // lg/xl remain as aliases for gradual migration of legacy CSS.
21621
- radius: /* @__PURE__ */ (() => {
21622
- const trail2 = validThemeName === "trail";
21874
+ radius: (() => {
21875
+ const boost = meta?.radiusBoost ?? 0;
21623
21876
  const xs = compact2 ? "10px" : "12px";
21624
- const sm = trail2 ? compact2 ? "15px" : "17px" : compact2 ? "14px" : "16px";
21877
+ const sm = compact2 ? `${14 + boost}px` : `${16 + boost}px`;
21625
21878
  const md = compact2 ? "20px" : "24px";
21626
21879
  return { xs, sm, md, lg: sm, xl: md };
21627
21880
  })(),
21628
- motionEase: validThemeName === "trail" ? "cubic-bezier(0.22, 1, 0.36, 1)" : "cubic-bezier(0.33, 0, 0.2, 1)",
21881
+ motionEase: meta?.motionEase ?? "cubic-bezier(0.33, 0, 0.2, 1)",
21629
21882
  motionEaseTide: "cubic-bezier(0.22, 1, 0.36, 1)",
21630
21883
  motionEaseBreath: "cubic-bezier(0.4, 0, 0.6, 1)",
21631
21884
  motionDuration: "0.32s",
@@ -22130,7 +22383,7 @@ var init_addContentAction = __esm({
22130
22383
  contentKey,
22131
22384
  fileCategory,
22132
22385
  mimeType,
22133
- fileSize,
22386
+ fileSize: fileSize2,
22134
22387
  originalName,
22135
22388
  categoryId: rawCategoryId,
22136
22389
  pinned = false,
@@ -22168,7 +22421,7 @@ var init_addContentAction = __esm({
22168
22421
  contentKey,
22169
22422
  ...fileCategory !== void 0 ? { fileCategory } : {},
22170
22423
  ...mimeType !== void 0 ? { mimeType } : {},
22171
- ...fileSize !== void 0 ? { fileSize } : {},
22424
+ ...fileSize2 !== void 0 ? { fileSize: fileSize2 } : {},
22172
22425
  ...originalName !== void 0 ? { originalName } : {},
22173
22426
  ...categoryIdForStorage !== void 0 && {
22174
22427
  categoryId: categoryIdForStorage
@@ -43545,7 +43798,7 @@ function appendCliCapabilityWarnings(content, warnings) {
43545
43798
  ${warnings.map((warning) => `- ${warning}`).join("\n")}`;
43546
43799
  return `${content}${warningBlock}`;
43547
43800
  }
43548
- var buildParallelMessageMetadata, filterMessagesForParallelBranch, buildDesktopRuntimeToolMessagesForUi, extractDesktopRuntimeToolCallsForUi, shouldUseDesktopLocalRuntime, resolveWebAgentRuntimeToolSurface, formatMachineAgentRunError, normalizeThreadMetadataPatch, patchDialogThreadMetadata, patchDialogActiveAgent, QUICK_CHAT_AGENT_CONFIG_READ_TIMEOUT_MS, QUICK_CHAT_DYNAMIC_CONTEXT_TIMEOUT_MS, EMPTY_DYNAMIC_CONTEXTS, logQuickChatPerfStage4, readAgentConfigForTurn, buildDynamicContextsForTurn, finalizeQuickChatAgentTurnFailure, normalizeAgentRunUserInput, isSimpleTextInput, canUseQuickChatEmptyDynamicContexts, QUICK_CHAT_DIRECT_ANSWER_PATTERN, QUICK_CHAT_TOOL_INTENT_PATTERN, shouldDisableQuickChatToolsForDirectAnswer, classifyQuickChatAccessError, isUsableAgentConfig, extractAgentRunUserText, hasAgentRunUserInputContent, setLoopStopReason2, streamAgentChatTurnHandler;
43801
+ var buildParallelMessageMetadata, filterMessagesForParallelBranch, buildDesktopRuntimeToolMessagesForUi, extractDesktopRuntimeToolCallsForUi, shouldUseDesktopLocalRuntime, readCurrentDesktopMachineId, resolveRemoteBoundMachineId, resolveWebAgentRuntimeToolSurface, formatMachineAgentRunError, normalizeThreadMetadataPatch, patchDialogThreadMetadata, patchDialogActiveAgent, QUICK_CHAT_AGENT_CONFIG_READ_TIMEOUT_MS, QUICK_CHAT_DYNAMIC_CONTEXT_TIMEOUT_MS, EMPTY_DYNAMIC_CONTEXTS, logQuickChatPerfStage4, readAgentConfigForTurn, buildDynamicContextsForTurn, finalizeQuickChatAgentTurnFailure, normalizeAgentRunUserInput, isSimpleTextInput, canUseQuickChatEmptyDynamicContexts, QUICK_CHAT_DIRECT_ANSWER_PATTERN, QUICK_CHAT_TOOL_INTENT_PATTERN, shouldDisableQuickChatToolsForDirectAnswer, classifyQuickChatAccessError, isUsableAgentConfig, extractAgentRunUserText, hasAgentRunUserInputContent, setLoopStopReason2, streamAgentChatTurnHandler;
43549
43802
  var init_streamAgentChatTurn = __esm({
43550
43803
  "packages/ai/agent/streamAgentChatTurn.ts"() {
43551
43804
  "use strict";
@@ -43675,6 +43928,18 @@ var init_streamAgentChatTurn = __esm({
43675
43928
  if (!getIsDesktopApp()) return false;
43676
43929
  return agentConfig?.apiSource !== "cli";
43677
43930
  };
43931
+ readCurrentDesktopMachineId = () => {
43932
+ const fromProcess = typeof process !== "undefined" ? (process.env?.NOLO_CURRENT_MACHINE_ID || process.env?.NOLO_MACHINE_ID || "").trim() : "";
43933
+ if (fromProcess) return fromProcess;
43934
+ const w = typeof globalThis !== "undefined" && globalThis.window ? globalThis.window : null;
43935
+ const fromWindow = typeof w?.__NOLO_CURRENT_MACHINE_ID__ === "string" ? w.__NOLO_CURRENT_MACHINE_ID__.trim() : typeof w?.__NOLO_MACHINE_ID__ === "string" ? w.__NOLO_MACHINE_ID__.trim() : "";
43936
+ return fromWindow;
43937
+ };
43938
+ resolveRemoteBoundMachineId = (machineId) => {
43939
+ if (!machineId || !getIsDesktopApp()) return machineId;
43940
+ const currentMachineId = readCurrentDesktopMachineId();
43941
+ return currentMachineId && currentMachineId === machineId ? "" : machineId;
43942
+ };
43678
43943
  resolveWebAgentRuntimeToolSurface = (agentConfig, state) => {
43679
43944
  const toolSurface = resolveRuntimeToolSurfaceForAgent({
43680
43945
  explicitToolNames: Array.isArray(agentConfig.tools) ? agentConfig.tools : [],
@@ -43999,7 +44264,8 @@ var init_streamAgentChatTurn = __esm({
43999
44264
  apiSource: agentConfig.apiSource,
44000
44265
  source: cachedAgentConfig === rawAgentConfig ? "cache" : "read"
44001
44266
  });
44002
- const boundMachineId = typeof agentConfig.runtimeBinding?.machineId === "string" ? agentConfig.runtimeBinding.machineId.trim() : "";
44267
+ const configuredBoundMachineId = typeof agentConfig.runtimeBinding?.machineId === "string" ? agentConfig.runtimeBinding.machineId.trim() : "";
44268
+ const boundMachineId = resolveRemoteBoundMachineId(configuredBoundMachineId);
44003
44269
  if (agentConfig.apiSource === "cli" || boundMachineId && !getIsDesktopApp()) {
44004
44270
  console.info("[streamAgentChatTurn] Triggered CLI/machine route. apiSource:", agentConfig.apiSource, "boundMachineId:", boundMachineId, "agentKey:", agentKey);
44005
44271
  const currentState = getState();
@@ -63688,14 +63954,6 @@ function summarizeEndpoint(value) {
63688
63954
  return "invalid-url";
63689
63955
  }
63690
63956
  }
63691
- function parseJsonObject2(raw) {
63692
- try {
63693
- const parsed = JSON.parse(raw);
63694
- return parsed && typeof parsed === "object" ? parsed : {};
63695
- } catch {
63696
- return {};
63697
- }
63698
- }
63699
63957
  function isCliProviderAgent(agentConfig) {
63700
63958
  return Boolean(
63701
63959
  agentConfig.apiSource === "cli" || agentConfig.provider === "cli" || agentConfig.cliProvider
@@ -64419,6 +64677,9 @@ function createCliLocalRuntimeAdapter(deps) {
64419
64677
  raw: result.raw
64420
64678
  };
64421
64679
  } catch (error) {
64680
+ if (error instanceof CliProviderQuotaError) {
64681
+ throw error;
64682
+ }
64422
64683
  const message = error instanceof Error ? error.message : String(error);
64423
64684
  throw new Error(
64424
64685
  `Local CLI provider "${provider}" is unavailable or failed: ${message}`
@@ -64458,12 +64719,15 @@ function createCliLocalRuntimeAdapter(deps) {
64458
64719
  toolNames: requestedToolNames,
64459
64720
  env: deps.env
64460
64721
  });
64722
+ const timeoutSignal = buildRequestTimeoutSignal(options?.timeoutMs);
64723
+ const usesResponsesApi = providerConfig2.endpoint.includes("/responses");
64724
+ const stream = Boolean(options?.onTextDelta) && !usesResponsesApi;
64461
64725
  const request = buildPlatformChatCompletionRequest({
64462
64726
  providerConfig: providerConfig2,
64463
64727
  messages,
64464
- tools
64728
+ tools,
64729
+ stream
64465
64730
  });
64466
- const timeoutSignal = buildRequestTimeoutSignal(options?.timeoutMs);
64467
64731
  logLocalRuntimeDiagnostic("provider.request.start", {
64468
64732
  agentKey: agentConfig.key,
64469
64733
  transport: "platform-proxy",
@@ -64474,32 +64738,62 @@ function createCliLocalRuntimeAdapter(deps) {
64474
64738
  toolCount: tools.length,
64475
64739
  requestedToolNames,
64476
64740
  openAiToolNames: summarizeOpenAiToolNames(tools),
64477
- timeoutMs: options?.timeoutMs ?? null
64741
+ timeoutMs: options?.timeoutMs ?? null,
64742
+ stream
64478
64743
  });
64479
- const res = await fetchWithTransientRetry(fetchImpl, request.url, {
64480
- ...request.init,
64481
- ...timeoutSignal ? { signal: timeoutSignal.signal } : {}
64482
- }, {
64483
- sleep: deps.sleep,
64484
- loopbackRequest
64485
- }).finally(() => timeoutSignal?.clear());
64486
- const raw = await res.text().catch(() => "");
64487
- logLocalRuntimeDiagnostic("provider.request.result", {
64488
- agentKey: agentConfig.key,
64489
- transport: "platform-proxy",
64490
- status: res.status,
64491
- ok: res.ok,
64492
- responseBytes: raw.length
64493
- });
64494
- const data = parsePlatformChatCompletionData(raw);
64495
- if (!res.ok) {
64496
- throw new Error(`platform provider failed: HTTP ${res.status} ${JSON.stringify(data)}`);
64744
+ try {
64745
+ const res = await fetchWithTransientRetry(fetchImpl, request.url, {
64746
+ ...request.init,
64747
+ ...timeoutSignal ? { signal: timeoutSignal.signal } : {}
64748
+ }, {
64749
+ sleep: deps.sleep,
64750
+ loopbackRequest
64751
+ });
64752
+ if (!res.ok) {
64753
+ const raw2 = await res.text().catch(() => "");
64754
+ const data2 = parsePlatformChatCompletionData(raw2);
64755
+ throw new Error(`platform provider failed: HTTP ${res.status} ${JSON.stringify(data2)}`);
64756
+ }
64757
+ if (stream && options?.onTextDelta) {
64758
+ const streamed = await readOpenAiCompatibleSseCompletion({
64759
+ response: res,
64760
+ onTextDelta: options.onTextDelta
64761
+ });
64762
+ logLocalRuntimeDiagnostic("provider.request.result", {
64763
+ agentKey: agentConfig.key,
64764
+ transport: "platform-proxy",
64765
+ ok: true,
64766
+ stream: true,
64767
+ contentChars: streamed.content.length,
64768
+ toolCallCount: streamed.tool_calls?.length ?? 0
64769
+ });
64770
+ return {
64771
+ content: streamed.content,
64772
+ model: providerConfig2.model,
64773
+ provider: providerConfig2.provider,
64774
+ ...streamed.tool_calls ? { tool_calls: streamed.tool_calls } : {},
64775
+ ...streamed.reasoning_content ? { reasoning_content: streamed.reasoning_content } : {},
64776
+ ...streamed.usage ? { usage: streamed.usage } : {},
64777
+ trace: messages
64778
+ };
64779
+ }
64780
+ const raw = await res.text().catch(() => "");
64781
+ logLocalRuntimeDiagnostic("provider.request.result", {
64782
+ agentKey: agentConfig.key,
64783
+ transport: "platform-proxy",
64784
+ status: res.status,
64785
+ ok: res.ok,
64786
+ responseBytes: raw.length
64787
+ });
64788
+ const data = parsePlatformChatCompletionData(raw);
64789
+ return parsePlatformChatCompletionResponse({
64790
+ providerConfig: providerConfig2,
64791
+ data,
64792
+ trace: messages
64793
+ });
64794
+ } finally {
64795
+ timeoutSignal?.clear();
64497
64796
  }
64498
- return parsePlatformChatCompletionResponse({
64499
- providerConfig: providerConfig2,
64500
- data,
64501
- trace: messages
64502
- });
64503
64797
  }
64504
64798
  };
64505
64799
  }
@@ -64531,47 +64825,45 @@ function createCliLocalRuntimeAdapter(deps) {
64531
64825
  toolNames: requestedToolNames,
64532
64826
  env: deps.env
64533
64827
  });
64534
- const request = buildOpenAiCompatibleChatCompletionRequest({
64535
- providerConfig,
64536
- messages,
64537
- tools
64538
- });
64539
64828
  const timeoutSignal = buildRequestTimeoutSignal(options?.timeoutMs);
64829
+ const stream = Boolean(options?.onTextDelta);
64540
64830
  logLocalRuntimeDiagnostic("provider.request.start", {
64541
64831
  agentKey: agentConfig.key,
64542
64832
  transport: "direct-openai-compatible",
64543
- requestUrl: summarizeEndpoint(request.url) ?? null,
64833
+ requestUrl: summarizeEndpoint(providerConfig.endpoint) ?? null,
64544
64834
  model: providerConfig.model,
64545
64835
  messageCount: messages.length,
64546
64836
  toolCount: tools.length,
64547
64837
  requestedToolNames,
64548
64838
  openAiToolNames: summarizeOpenAiToolNames(tools),
64549
- timeoutMs: options?.timeoutMs ?? null
64839
+ timeoutMs: options?.timeoutMs ?? null,
64840
+ stream
64550
64841
  });
64551
- const res = await fetchWithTransientRetry(fetchImpl, request.url, {
64552
- ...request.init,
64553
- ...timeoutSignal ? { signal: timeoutSignal.signal } : {}
64554
- }, {
64555
- sleep: deps.sleep,
64556
- loopbackRequest
64557
- }).finally(() => timeoutSignal?.clear());
64558
- const raw = await res.text().catch(() => "");
64559
- logLocalRuntimeDiagnostic("provider.request.result", {
64560
- agentKey: agentConfig.key,
64561
- transport: "direct-openai-compatible",
64562
- status: res.status,
64563
- ok: res.ok,
64564
- responseBytes: raw.length
64565
- });
64566
- const data = parseJsonObject2(raw);
64567
- if (!res.ok) {
64568
- throw new Error(`local provider failed: HTTP ${res.status} ${JSON.stringify(data)}`);
64842
+ try {
64843
+ const result = await executeOpenAiCompatibleChatCompletion({
64844
+ providerConfig,
64845
+ messages,
64846
+ tools,
64847
+ fetchImpl: (url, init) => fetchWithTransientRetry(fetchImpl, url, init, {
64848
+ sleep: deps.sleep,
64849
+ loopbackRequest
64850
+ }),
64851
+ stream,
64852
+ onTextDelta: options?.onTextDelta,
64853
+ signal: timeoutSignal?.signal
64854
+ });
64855
+ logLocalRuntimeDiagnostic("provider.request.result", {
64856
+ agentKey: agentConfig.key,
64857
+ transport: "direct-openai-compatible",
64858
+ ok: true,
64859
+ stream,
64860
+ contentChars: result.content.length,
64861
+ toolCallCount: result.tool_calls?.length ?? 0
64862
+ });
64863
+ return result;
64864
+ } finally {
64865
+ timeoutSignal?.clear();
64569
64866
  }
64570
- return parseOpenAiCompatibleChatCompletionResponse({
64571
- providerConfig,
64572
- data,
64573
- trace: messages
64574
- });
64575
64867
  }
64576
64868
  };
64577
64869
  },
@@ -65989,8 +66281,8 @@ var init_localDialogRead = __esm({
65989
66281
  });
65990
66282
 
65991
66283
  // packages/cli/index.ts
65992
- import { dirname as dirname8, join as join12 } from "node:path";
65993
- import { fileURLToPath as fileURLToPath5 } from "node:url";
66284
+ import { dirname as dirname9, join as join13 } from "node:path";
66285
+ import { fileURLToPath as fileURLToPath6 } from "node:url";
65994
66286
 
65995
66287
  // packages/cli/cliCommandDispatch.ts
65996
66288
  import { fileURLToPath } from "node:url";
@@ -66155,17 +66447,6 @@ function parseIsoTimestampOption(raw, flag) {
66155
66447
  }
66156
66448
  return new Date(time).toISOString();
66157
66449
  }
66158
- function buildUsageProbeConfig(kind) {
66159
- if (!kind) return void 0;
66160
- if (kind !== "qoder-local-quota-log") {
66161
- throw new Error("--usage-probe currently supports qoder-local-quota-log.");
66162
- }
66163
- return {
66164
- kind,
66165
- command: "nolo agent usage qoder",
66166
- capability: "qoder-usage"
66167
- };
66168
- }
66169
66450
  async function curlFetch(url, init) {
66170
66451
  const method = init?.method ?? "GET";
66171
66452
  const headers = new Headers(init?.headers ?? {});
@@ -66422,7 +66703,6 @@ function parseAgentUpdateArgs(args2) {
66422
66703
  "--max-concurrent"
66423
66704
  );
66424
66705
  const expiresAt = parseIsoTimestampOption(readOption2(args2, "--expires-at"), "--expires-at");
66425
- const usageProbe = buildUsageProbeConfig(readOption2(args2, "--usage-probe"));
66426
66706
  const handle = readOption2(args2, "--handle")?.trim();
66427
66707
  const promptSources = [prompt, promptFile, promptDoc].filter(Boolean);
66428
66708
  if (promptSources.length > 1) {
@@ -66439,11 +66719,8 @@ function parseAgentUpdateArgs(args2) {
66439
66719
  if (customProviderUrl) updates.customProviderUrl = customProviderUrl;
66440
66720
  if (apiKey) updates.apiKey = apiKey;
66441
66721
  if (maxConcurrent != null) updates.admission = { maxConcurrent };
66442
- if (expiresAt || usageProbe) {
66443
- updates.scheduling = {
66444
- ...expiresAt ? { expiresAt } : {},
66445
- ...usageProbe ? { usageProbe } : {}
66446
- };
66722
+ if (expiresAt) {
66723
+ updates.scheduling = { expiresAt };
66447
66724
  }
66448
66725
  if (handle) updates.handle = handle;
66449
66726
  for (const entry of readRepeatedOption(args2, "--field")) {
@@ -67582,7 +67859,7 @@ async function runAgentUpdateCommand(args2, deps = {}) {
67582
67859
  }
67583
67860
  if (!parsed) {
67584
67861
  output2.write(
67585
- "Usage: nolo agent update <agent> [--model <id>] [--cli-provider <provider>] [--api-source <source>] [--max-concurrent <n>] [--expires-at <iso>] [--usage-probe qoder-local-quota-log] [--prompt <text> | --prompt-file <path> | --prompt-doc <pageKey>] [--tools <json>] [--copy-provider-from <agent>] [--field key=value]\n".replace("[--field key=value]", "[--handle <name>] [--field key=value]")
67862
+ "Usage: nolo agent update <agent> [--model <id>] [--cli-provider <provider>] [--api-source <source>] [--max-concurrent <n>] [--expires-at <iso>] [--prompt <text> | --prompt-file <path> | --prompt-doc <pageKey>] [--tools <json>] [--copy-provider-from <agent>] [--field key=value]\n".replace("[--field key=value]", "[--handle <name>] [--field key=value]")
67586
67863
  );
67587
67864
  return args2[0] ? 0 : 1;
67588
67865
  }
@@ -67654,7 +67931,7 @@ async function runAgentCreateCommand(args2, deps = {}) {
67654
67931
  }
67655
67932
  if (!parsed) {
67656
67933
  output2.write(
67657
- "Usage: nolo agent create <agent> [--model <id>] [--cli-provider <provider>] [--api-source <source>] [--max-concurrent <n>] [--expires-at <iso>] [--usage-probe qoder-local-quota-log] [--prompt <text> | --prompt-file <path> | --prompt-doc <pageKey>] [--tools <json>] [--copy-provider-from <agent>] [--name <name>] [--custom-provider-url <url>] [--provider-api-key <key>] [--field key=value]\n".replace("[--field key=value]", "[--handle <name>] [--field key=value]")
67934
+ "Usage: nolo agent create <agent> [--model <id>] [--cli-provider <provider>] [--api-source <source>] [--max-concurrent <n>] [--expires-at <iso>] [--prompt <text> | --prompt-file <path> | --prompt-doc <pageKey>] [--tools <json>] [--copy-provider-from <agent>] [--name <name>] [--custom-provider-url <url>] [--provider-api-key <key>] [--field key=value]\n".replace("[--field key=value]", "[--handle <name>] [--field key=value]")
67658
67935
  );
67659
67936
  return args2[0] ? 0 : 1;
67660
67937
  }
@@ -67753,6 +68030,412 @@ function createStreamingTextWriter({
67753
68030
  };
67754
68031
  }
67755
68032
 
68033
+ // packages/cli/client/assistantOutput.ts
68034
+ var ANSI = {
68035
+ reset: "\x1B[0m",
68036
+ bold: "\x1B[1m",
68037
+ dim: "\x1B[2m",
68038
+ cyan: "\x1B[36m"
68039
+ };
68040
+ function normalizeRenderDisplayMode(raw, fallback = "rich") {
68041
+ const normalized = raw?.trim().toLowerCase();
68042
+ if (normalized === "plain" || normalized === "raw" || normalized === "off" || normalized === "0") {
68043
+ return "plain";
68044
+ }
68045
+ if (normalized === "rich" || normalized === "on" || normalized === "1" || normalized === "styled") {
68046
+ return "rich";
68047
+ }
68048
+ return fallback;
68049
+ }
68050
+ function resolveRenderDisplayMode(env = process.env) {
68051
+ return normalizeRenderDisplayMode(env.NOLO_CLI_RENDER ?? env.NOLO_RENDER, "rich");
68052
+ }
68053
+ function splitTableCells(line) {
68054
+ const trimmed = line.trim();
68055
+ if (!trimmed.includes("|")) return [];
68056
+ const core = trimmed.replace(/^\|/, "").replace(/\|$/, "");
68057
+ return core.split("|").map((cell) => cell.trim()).filter(Boolean);
68058
+ }
68059
+ function isTableSeparator(line) {
68060
+ const cells = splitTableCells(line);
68061
+ return cells.length > 0 && cells.every((cell) => /^:?-{3,}:?$/.test(cell));
68062
+ }
68063
+ function isTableRow(line) {
68064
+ const cells = splitTableCells(line);
68065
+ return cells.length >= 2 && cells.some((cell) => cell.length > 0);
68066
+ }
68067
+ function convertMarkdownTablesForTerminal(text) {
68068
+ const lines = text.split("\n");
68069
+ const out = [];
68070
+ for (let index = 0; index < lines.length; index += 1) {
68071
+ const line = lines[index] ?? "";
68072
+ const next = lines[index + 1] ?? "";
68073
+ if (isTableRow(line) && isTableSeparator(next)) {
68074
+ const headers = splitTableCells(line);
68075
+ index += 1;
68076
+ while (index + 1 < lines.length && isTableRow(lines[index + 1] ?? "") && !isTableSeparator(lines[index + 1] ?? "")) {
68077
+ index += 1;
68078
+ const row = splitTableCells(lines[index] ?? "");
68079
+ const label = row[0] ?? "";
68080
+ const detail = row.slice(1).join(" \u2014 ").trim();
68081
+ out.push(detail ? ` \u2022 ${label} \u2014 ${detail}` : ` \u2022 ${label}`);
68082
+ }
68083
+ if (out.length > 0 && out[out.length - 1] !== "") out.push("");
68084
+ continue;
68085
+ }
68086
+ out.push(line);
68087
+ }
68088
+ return out.join("\n");
68089
+ }
68090
+ function polishAssistantStructure(text) {
68091
+ return convertMarkdownTablesForTerminal(text).replace(/\r\n/g, "\n").replace(/([^\n])\n(#{1,3} )/g, "$1\n\n$2").replace(/\n{4,}/g, "\n\n\n").trim();
68092
+ }
68093
+ function styleInlineMarkdown(line, mode) {
68094
+ if (mode === "plain") return line;
68095
+ return line.replace(/\*\*(.+?)\*\*/g, `${ANSI.bold}$1${ANSI.reset}`);
68096
+ }
68097
+ function styleRichMarkdownLine(line) {
68098
+ const heading = line.match(/^(#{1,3})\s+(.+)$/);
68099
+ if (heading) {
68100
+ const level = heading[1].length;
68101
+ const title = heading[2];
68102
+ if (level <= 2) return `${ANSI.bold}${title}${ANSI.reset}`;
68103
+ return `${ANSI.cyan}${title}${ANSI.reset}`;
68104
+ }
68105
+ if (/^---+$/.test(line.trim())) {
68106
+ return `${ANSI.dim}${line}${ANSI.reset}`;
68107
+ }
68108
+ return styleInlineMarkdown(line, "rich");
68109
+ }
68110
+ function formatAssistantDisplay(text, mode = "rich") {
68111
+ const polished = polishAssistantStructure(text);
68112
+ if (mode === "plain") {
68113
+ return polished.split("\n").map((line) => styleInlineMarkdown(line, "plain")).join("\n");
68114
+ }
68115
+ return polished.split("\n").map((line) => styleRichMarkdownLine(line)).join("\n");
68116
+ }
68117
+
68118
+ // packages/cli/client/thinkingOutput.ts
68119
+ var THINK_OPEN = /<think>/i;
68120
+ var THINK_CLOSE = /<\/think>/i;
68121
+ var COLLAPSED_MARKER = /^\s*▸ 思考已折叠\s*$/;
68122
+ function normalizeThinkingDisplayMode(raw, fallback = "hide") {
68123
+ const normalized = raw?.trim().toLowerCase();
68124
+ if (normalized === "hide" || normalized === "off" || normalized === "false" || normalized === "0") {
68125
+ return "hide";
68126
+ }
68127
+ if (normalized === "marker" || normalized === "collapsed" || normalized === "fold") {
68128
+ return "marker";
68129
+ }
68130
+ if (normalized === "show" || normalized === "on" || normalized === "true" || normalized === "1") {
68131
+ return "show";
68132
+ }
68133
+ return fallback;
68134
+ }
68135
+ function resolveThinkingDisplayMode(env = process.env) {
68136
+ return normalizeThinkingDisplayMode(env.NOLO_CLI_THINKING ?? env.NOLO_THINKING, "hide");
68137
+ }
68138
+ function collapseThinkingBlocks(text, mode = "hide") {
68139
+ if (mode === "show") return text;
68140
+ const replacement = mode === "marker" ? "\u25B8 \u601D\u8003\u5DF2\u6298\u53E0\n" : "";
68141
+ return text.replace(/<think>[\s\S]*?<\/think>\s*/gi, replacement).replace(/\n{3,}/g, "\n\n").trim();
68142
+ }
68143
+ function stripCollapsedThinkingMarkers(text) {
68144
+ return text.split("\n").filter((line) => !COLLAPSED_MARKER.test(line)).join("\n").replace(/\n{2,}/g, "\n").trim();
68145
+ }
68146
+ function formatAssistantTextForCli(text, mode = "hide") {
68147
+ const collapsed = collapseThinkingBlocks(text, mode);
68148
+ return mode === "hide" ? stripCollapsedThinkingMarkers(collapsed) : collapsed;
68149
+ }
68150
+ function createThinkingAwareStreamFilter(write2, mode = "hide") {
68151
+ if (mode === "show") {
68152
+ return {
68153
+ push(chunk) {
68154
+ write2(chunk);
68155
+ },
68156
+ flush() {
68157
+ }
68158
+ };
68159
+ }
68160
+ let pending = "";
68161
+ let insideThink = false;
68162
+ let markerEmitted = false;
68163
+ const emit = (chunk) => {
68164
+ if (chunk) write2(chunk);
68165
+ };
68166
+ const emitMarker = () => {
68167
+ if (mode !== "marker" || markerEmitted) return;
68168
+ emit("\n\u25B8 \u601D\u8003\u5DF2\u6298\u53E0\n");
68169
+ markerEmitted = true;
68170
+ };
68171
+ const consume = (input2) => {
68172
+ pending += input2;
68173
+ while (pending.length > 0) {
68174
+ if (insideThink) {
68175
+ const closeMatch = pending.match(THINK_CLOSE);
68176
+ if (!closeMatch || closeMatch.index == null) {
68177
+ emitMarker();
68178
+ pending = "";
68179
+ return;
68180
+ }
68181
+ emitMarker();
68182
+ pending = pending.slice(closeMatch.index + closeMatch[0].length);
68183
+ insideThink = false;
68184
+ markerEmitted = false;
68185
+ continue;
68186
+ }
68187
+ const openMatch = pending.match(THINK_OPEN);
68188
+ if (!openMatch || openMatch.index == null) {
68189
+ const nextMarker = pending.indexOf("<");
68190
+ if (nextMarker === -1) {
68191
+ const cleaned = mode === "hide" ? pending.split("\n").filter((line) => !COLLAPSED_MARKER.test(line)).join("\n") : pending;
68192
+ emit(cleaned);
68193
+ pending = "";
68194
+ return;
68195
+ }
68196
+ if (nextMarker > 0) {
68197
+ const head = pending.slice(0, nextMarker);
68198
+ const cleaned = mode === "hide" ? head.split("\n").filter((line) => !COLLAPSED_MARKER.test(line)).join("\n") : head;
68199
+ emit(cleaned);
68200
+ pending = pending.slice(nextMarker);
68201
+ continue;
68202
+ }
68203
+ if (pending.length < 7) return;
68204
+ emit(pending[0]);
68205
+ pending = pending.slice(1);
68206
+ continue;
68207
+ }
68208
+ if (openMatch.index > 0) {
68209
+ const head = pending.slice(0, openMatch.index);
68210
+ const cleaned = mode === "hide" ? head.split("\n").filter((line) => !COLLAPSED_MARKER.test(line)).join("\n") : head;
68211
+ emit(cleaned);
68212
+ pending = pending.slice(openMatch.index);
68213
+ continue;
68214
+ }
68215
+ pending = pending.slice(openMatch[0].length);
68216
+ insideThink = true;
68217
+ markerEmitted = false;
68218
+ }
68219
+ };
68220
+ return {
68221
+ push(chunk) {
68222
+ consume(chunk);
68223
+ },
68224
+ flush() {
68225
+ if (insideThink) emitMarker();
68226
+ if (pending) {
68227
+ const cleaned = mode === "hide" ? pending.split("\n").filter((line) => !COLLAPSED_MARKER.test(line)).join("\n") : pending;
68228
+ emit(cleaned);
68229
+ }
68230
+ pending = "";
68231
+ insideThink = false;
68232
+ markerEmitted = false;
68233
+ }
68234
+ };
68235
+ }
68236
+
68237
+ // packages/cli/client/tokenUsage.ts
68238
+ init_providers();
68239
+ var PROVIDER_LOOKUP_ORDER = [
68240
+ "openrouter",
68241
+ "fireworks",
68242
+ "openai",
68243
+ "mimo",
68244
+ "gmi",
68245
+ "google",
68246
+ "deepseek",
68247
+ "mistral",
68248
+ "vultr",
68249
+ "deepinfra",
68250
+ "cloudflare"
68251
+ ];
68252
+ function parseUsageRecord(usage2) {
68253
+ if (!usage2 || typeof usage2 !== "object") return void 0;
68254
+ const input2 = Number(usage2.input_tokens ?? usage2.prompt_tokens ?? 0);
68255
+ const output2 = Number(usage2.output_tokens ?? usage2.completion_tokens ?? 0);
68256
+ if (!Number.isFinite(input2) || !Number.isFinite(output2)) return void 0;
68257
+ if (!input2 && !output2) return void 0;
68258
+ return { input: input2, output: output2 };
68259
+ }
68260
+ function fuzzyContextWindow(model) {
68261
+ const lower = model.toLowerCase();
68262
+ if (lower.includes("minimax-m3") || lower.includes("minimax_m3")) return 1e6;
68263
+ if (lower.includes("minimax-m2")) return 262144;
68264
+ if (lower.includes("gpt-5") || lower.includes("gpt-4.1")) return 1047576;
68265
+ if (lower.includes("claude")) return 2e5;
68266
+ return void 0;
68267
+ }
68268
+ function resolveContextWindow(model) {
68269
+ const raw = model?.trim();
68270
+ if (!raw) return void 0;
68271
+ for (const provider of PROVIDER_LOOKUP_ORDER) {
68272
+ const config = findModelConfig(provider, raw);
68273
+ if (config?.contextWindow) return config.contextWindow;
68274
+ }
68275
+ return fuzzyContextWindow(raw);
68276
+ }
68277
+ function buildTurnTokenUsage(usage2, model) {
68278
+ const parsed = parseUsageRecord(usage2);
68279
+ if (!parsed) return void 0;
68280
+ const contextWindow = resolveContextWindow(model);
68281
+ const remaining = contextWindow && parsed.input > 0 ? Math.max(0, contextWindow - parsed.input) : void 0;
68282
+ return {
68283
+ ...parsed,
68284
+ ...contextWindow ? { contextWindow } : {},
68285
+ ...remaining != null ? { remaining } : {}
68286
+ };
68287
+ }
68288
+ function formatTokenCount(value) {
68289
+ if (!Number.isFinite(value) || value < 0) return "\u2014";
68290
+ if (value < 1e3) return String(Math.round(value));
68291
+ if (value < 1e6) {
68292
+ const compact3 = value / 1e3;
68293
+ if (Number.isInteger(compact3)) return `${compact3}k`;
68294
+ return `${compact3.toFixed(1).replace(/\.0$/, "")}k`;
68295
+ }
68296
+ const compact2 = value / 1e6;
68297
+ return compact2 >= 100 ? `${Math.round(compact2)}M` : `${compact2.toFixed(1).replace(/\.0$/, "")}M`;
68298
+ }
68299
+ function renderTokenStatus(tokens) {
68300
+ if (!tokens) return "in \u2014 out \u2014 left \u2014";
68301
+ const left = tokens.remaining != null ? formatTokenCount(tokens.remaining) : tokens.contextWindow ? "\u2014" : "\u2014";
68302
+ return `in ${formatTokenCount(tokens.input)} out ${formatTokenCount(tokens.output)} left ${left}`;
68303
+ }
68304
+
68305
+ // packages/cli/client/terminalStyles.ts
68306
+ var ANSI2 = {
68307
+ dim: "\x1B[2m",
68308
+ bold: "\x1B[1m",
68309
+ cyan: "\x1B[36m",
68310
+ green: "\x1B[32m",
68311
+ red: "\x1B[31m"
68312
+ };
68313
+ var RESET = "\x1B[0m";
68314
+ function resolveCliColorEnabled(env = process.env, isTTY = Boolean(process.stdout.isTTY)) {
68315
+ const setting = (env.NOLO_CLI_COLOR ?? "").trim().toLowerCase();
68316
+ if (setting === "0" || setting === "false" || setting === "off") return false;
68317
+ if (env.NO_COLOR) return false;
68318
+ if (setting === "1" || setting === "true" || setting === "on") return true;
68319
+ return isTTY;
68320
+ }
68321
+ function styleCliText(text, style, enabled = resolveCliColorEnabled()) {
68322
+ if (!enabled || !text) return text;
68323
+ return `${ANSI2[style]}${text}${RESET}`;
68324
+ }
68325
+ function dimCliText(text, enabled = resolveCliColorEnabled()) {
68326
+ return styleCliText(text, "dim", enabled);
68327
+ }
68328
+
68329
+ // packages/cli/client/toolOutput.ts
68330
+ function normalizeToolDisplayMode(raw, fallback = "compact") {
68331
+ const normalized = raw?.trim().toLowerCase();
68332
+ if (normalized === "hide" || normalized === "off" || normalized === "false" || normalized === "0") {
68333
+ return "hide";
68334
+ }
68335
+ if (normalized === "verbose" || normalized === "debug" || normalized === "trace" || normalized === "full") {
68336
+ return "verbose";
68337
+ }
68338
+ if (normalized === "compact" || normalized === "minimal" || normalized === "short") {
68339
+ return "compact";
68340
+ }
68341
+ return fallback;
68342
+ }
68343
+ function resolveToolDisplayMode(env = process.env) {
68344
+ const legacyTrace = env.NOLO_TRACE_TOOLS?.trim().toLowerCase();
68345
+ if (legacyTrace === "0" || legacyTrace === "false" || legacyTrace === "off") {
68346
+ return "hide";
68347
+ }
68348
+ if (legacyTrace === "verbose" || legacyTrace === "full") {
68349
+ return "verbose";
68350
+ }
68351
+ return normalizeToolDisplayMode(env.NOLO_CLI_TOOLS ?? env.NOLO_TOOLS, "compact");
68352
+ }
68353
+ function shouldEmitToolEvents(mode) {
68354
+ return mode !== "hide";
68355
+ }
68356
+ function clip4(value, max = 72) {
68357
+ const compact2 = value.replace(/\s+/g, " ").trim();
68358
+ return compact2.length > max ? `${compact2.slice(0, max - 1)}\u2026` : compact2;
68359
+ }
68360
+ function compactResultHint(summary, toolName) {
68361
+ if (!summary) return "";
68362
+ const linesMatch = summary.match(/(\d+)\s+lines?/);
68363
+ if (linesMatch) {
68364
+ if (toolName === "readFile" || toolName === "listFiles" || toolName === "globFiles") {
68365
+ return `${linesMatch[1]} lines`;
68366
+ }
68367
+ if (toolName === "execShell" || toolName === "runCommand") {
68368
+ return `${linesMatch[1]} lines`;
68369
+ }
68370
+ }
68371
+ const exitMatch = summary.match(/exit=(\d+)/);
68372
+ if (exitMatch && exitMatch[1] !== "0") return `exit ${exitMatch[1]}`;
68373
+ return "";
68374
+ }
68375
+ function formatToolTraceLine(text, colorEnabled, accent = "none") {
68376
+ if (!colorEnabled) return `${text}
68377
+ `;
68378
+ if (accent === "error") {
68379
+ return `${styleCliText(text, "red", true)}
68380
+ `;
68381
+ }
68382
+ return `${dimCliText(text, true)}
68383
+ `;
68384
+ }
68385
+ function formatVerboseToolEvent(event, colorEnabled) {
68386
+ const round = event.round + 1;
68387
+ const detail = event.argumentsPreview ? ` ${event.argumentsPreview}` : "";
68388
+ if (event.type === "tool-call") {
68389
+ return formatToolTraceLine(`[nolo:tool] #${round} -> ${event.toolName}${detail}`, colorEnabled);
68390
+ }
68391
+ if (event.type === "tool-error") {
68392
+ const elapsed2 = typeof event.elapsedMs === "number" ? ` ${event.elapsedMs}ms` : "";
68393
+ return formatToolTraceLine(
68394
+ `[nolo:tool] #${round} !! ${event.toolName}${elapsed2}: ${event.message ?? "failed"}`,
68395
+ colorEnabled,
68396
+ "error"
68397
+ );
68398
+ }
68399
+ const elapsed = typeof event.elapsedMs === "number" ? ` ${event.elapsedMs}ms` : "";
68400
+ const summary = event.summary ? ` ${event.summary}` : "";
68401
+ return formatToolTraceLine(
68402
+ `[nolo:tool] #${round} <- ${event.toolName}${elapsed}${summary}`,
68403
+ colorEnabled
68404
+ );
68405
+ }
68406
+ function formatCompactToolLine(event, pending, colorEnabled) {
68407
+ const toolName = event.toolName || pending?.toolName || "tool";
68408
+ const args2 = clip4(event.argumentsPreview || pending?.argumentsPreview || "");
68409
+ const label = args2 ? `${toolName} ${args2}` : toolName;
68410
+ const ms = typeof event.elapsedMs === "number" ? `${event.elapsedMs}ms` : "";
68411
+ if (event.type === "tool-error") {
68412
+ const message = clip4(event.message ?? "failed", 96);
68413
+ const timing2 = ms ? ` \xB7 ${ms}` : "";
68414
+ return formatToolTraceLine(` \u25B8 ${label} \u2717 ${message}${timing2}`, colorEnabled, "error");
68415
+ }
68416
+ const hint = compactResultHint(event.summary, toolName);
68417
+ const timing = ms ? ` ${ms}` : "";
68418
+ const suffix = hint ? ` \xB7 ${hint}` : "";
68419
+ return formatToolTraceLine(` \u25B8 ${label} \u2713${timing}${suffix}`, colorEnabled);
68420
+ }
68421
+ function createToolEventFormatter(mode, colorEnabled = resolveCliColorEnabled()) {
68422
+ const pending = /* @__PURE__ */ new Map();
68423
+ return (event) => {
68424
+ if (mode === "hide") return "";
68425
+ if (mode === "verbose") return formatVerboseToolEvent(event, colorEnabled);
68426
+ if (event.type === "tool-call") {
68427
+ pending.set(event.toolCallId, {
68428
+ toolName: event.toolName,
68429
+ argumentsPreview: event.argumentsPreview
68430
+ });
68431
+ return "";
68432
+ }
68433
+ const call = pending.get(event.toolCallId);
68434
+ pending.delete(event.toolCallId);
68435
+ return formatCompactToolLine(event, call, colorEnabled);
68436
+ };
68437
+ }
68438
+
67756
68439
  // packages/cli/client/agentRun.ts
67757
68440
  var Spinner = class {
67758
68441
  constructor(output2, text) {
@@ -67858,6 +68541,23 @@ function isCliProviderAgentConfig(agentConfig) {
67858
68541
  const provider = typeof agentConfig?.provider === "string" ? agentConfig.provider.trim() : "";
67859
68542
  return Boolean(cliProvider) || provider === "agy" || provider === "codex" || provider === "claude";
67860
68543
  }
68544
+ function resolveBoundMachineId(agentConfig) {
68545
+ return agentConfig?.runtimeBinding && typeof agentConfig.runtimeBinding === "object" ? String(agentConfig.runtimeBinding.machineId ?? "").trim() : "";
68546
+ }
68547
+ async function detectCurrentMachineId(env) {
68548
+ const fromEnv = (env.NOLO_CURRENT_MACHINE_ID || env.NOLO_MACHINE_ID || "").trim();
68549
+ if (fromEnv) return fromEnv;
68550
+ try {
68551
+ const { detectMachineInfo: detectMachineInfo2 } = await Promise.resolve().then(() => (init_machineInfo(), machineInfo_exports));
68552
+ const machine = await detectMachineInfo2({ probeLaunchable: true });
68553
+ return typeof machine?.machineId === "string" && machine.machineId.trim() ? machine.machineId.trim() : void 0;
68554
+ } catch {
68555
+ return void 0;
68556
+ }
68557
+ }
68558
+ async function resolveCurrentMachineId(options) {
68559
+ return options.currentMachineIdResolver ? options.currentMachineIdResolver(options.env) : detectCurrentMachineId(options.env);
68560
+ }
67861
68561
  function resolveAuthToken5(env) {
67862
68562
  return env.AUTH_TOKEN || env.AUTH || env.BENCHMARK_AUTH_TOKEN || "";
67863
68563
  }
@@ -67902,7 +68602,16 @@ async function shouldSkipAutoLocalForServerPlatformTools(options) {
67902
68602
  }
67903
68603
  return false;
67904
68604
  }
67905
- if (isCliProviderAgentConfig(agentConfig)) return false;
68605
+ if (isCliProviderAgentConfig(agentConfig)) {
68606
+ const boundMachineId = resolveBoundMachineId(agentConfig);
68607
+ if (!boundMachineId) return false;
68608
+ const currentMachineId = (await resolveCurrentMachineId(options))?.trim() || "";
68609
+ if (currentMachineId && currentMachineId === boundMachineId) return false;
68610
+ options.output.write(
68611
+ `[nolo] auto runtime: skipping local runtime because ${options.agentKey} is bound to ${boundMachineId}` + (currentMachineId ? ` and this machine is ${currentMachineId}.` : ".") + " Use --local explicitly to force the current machine.\n"
68612
+ );
68613
+ return true;
68614
+ }
67906
68615
  if (knownServerPlatformAgent) {
67907
68616
  options.output.write(
67908
68617
  `[nolo] auto runtime: skipping local runtime because ${options.agentKey} is a known platform agent. Use --local explicitly to force local workspace tools.
@@ -67972,10 +68681,13 @@ function buildSubjectRefs(options) {
67972
68681
  }
67973
68682
  return refs.length ? refs : void 0;
67974
68683
  }
67975
- function shouldTraceLocalTools(options) {
67976
- const setting = (options.env.NOLO_TRACE_TOOLS ?? "").trim().toLowerCase();
67977
- if (setting === "0" || setting === "false" || setting === "off") return false;
67978
- return true;
68684
+ function formatAssistantResponseForCli(text, options) {
68685
+ const thinkingMode = resolveThinkingDisplayMode(options.env);
68686
+ const renderMode = resolveRenderDisplayMode(options.env);
68687
+ return formatAssistantDisplay(
68688
+ formatAssistantTextForCli(text, thinkingMode),
68689
+ renderMode
68690
+ );
67979
68691
  }
67980
68692
  function resolveAgentEventMode(options) {
67981
68693
  if (options.eventsMode === "jsonl") return "jsonl";
@@ -67986,23 +68698,6 @@ function isMissingLocalAgentConfigError(error, agentRef) {
67986
68698
  error && typeof error === "object" && "code" in error && error.code === LOCAL_AGENT_CONFIG_MISSING_CODE && error.agentRef === agentRef
67987
68699
  );
67988
68700
  }
67989
- function formatToolTraceEvent(event) {
67990
- const round = event.round + 1;
67991
- const detail = event.argumentsPreview ? ` ${event.argumentsPreview}` : "";
67992
- if (event.type === "tool-call") {
67993
- return `[nolo:tool] #${round} -> ${event.toolName}${detail}
67994
- `;
67995
- }
67996
- if (event.type === "tool-error") {
67997
- const elapsed2 = typeof event.elapsedMs === "number" ? ` ${event.elapsedMs}ms` : "";
67998
- return `[nolo:tool] #${round} !! ${event.toolName}${elapsed2}: ${event.message ?? "failed"}
67999
- `;
68000
- }
68001
- const elapsed = typeof event.elapsedMs === "number" ? ` ${event.elapsedMs}ms` : "";
68002
- const summary = event.summary ? ` ${event.summary}` : "";
68003
- return `[nolo:tool] #${round} <- ${event.toolName}${elapsed}${summary}
68004
- `;
68005
- }
68006
68701
  function formatToolJsonEvent(event) {
68007
68702
  return `${JSON.stringify({
68008
68703
  schemaVersion: 1,
@@ -68023,6 +68718,9 @@ function shouldAttemptAutoLocal(options) {
68023
68718
  if (options.env.NOLO_DISABLE_CLI_WORKSPACE_TOOLS !== "1" && isBuiltinNoloAgentRef(options.agentKey) && resolveAuthToken5(options.env)) {
68024
68719
  return true;
68025
68720
  }
68721
+ if (options.env.NOLO_DISABLE_CLI_WORKSPACE_TOOLS !== "1" && resolveAuthToken5(options.env) && !isKnownServerPlatformAgent(options)) {
68722
+ return true;
68723
+ }
68026
68724
  return Boolean(
68027
68725
  options.env.NOLO_LOCAL_OPENAI_API_KEY || options.env.OPENAI_API_KEY || options.env.NOLO_LOCAL_OPENAI_BASE_URL || options.env.OPENAI_BASE_URL || options.env.NOLO_LOCAL_AGENT_KEY
68028
68726
  );
@@ -68131,8 +68829,8 @@ async function runHttpAgentTurn(options, authToken) {
68131
68829
  const contentType = res.headers.get("content-type") || "";
68132
68830
  if (contentType.includes("text/event-stream") && res.body) {
68133
68831
  spinner.stop();
68134
- const result2 = await readStreamingAgentRun(options, res);
68135
- return result2;
68832
+ const result = await readStreamingAgentRun(options, res);
68833
+ return result;
68136
68834
  }
68137
68835
  spinner.stop();
68138
68836
  let data = {};
@@ -68175,7 +68873,10 @@ async function runHttpAgentTurn(options, authToken) {
68175
68873
  }
68176
68874
  return dialogIdText ? { exitCode: 1, dialogId: dialogIdText } : { exitCode: 1 };
68177
68875
  }
68178
- const content = String(data?.content ?? data?.message ?? "").trim();
68876
+ const content = formatAssistantResponseForCli(
68877
+ String(data?.content ?? data?.message ?? ""),
68878
+ options
68879
+ );
68179
68880
  if (content) {
68180
68881
  options.output.write(`
68181
68882
  ${options.agentName} > ${content}
@@ -68188,11 +68889,14 @@ ${options.agentName} > (no text response)
68188
68889
  const usage2 = formatUsage(data?.usage, data?.dialogId);
68189
68890
  if (usage2 && shouldShowUsage(options.env)) options.output.write(`${usage2}
68190
68891
  `);
68191
- const result = {
68892
+ return {
68192
68893
  exitCode: 0,
68193
- ...typeof data?.dialogId === "string" && data.dialogId ? { dialogId: data.dialogId } : {}
68894
+ ...typeof data?.dialogId === "string" && data.dialogId ? { dialogId: data.dialogId } : {},
68895
+ turnTokens: buildTurnTokenUsage(
68896
+ data?.usage,
68897
+ typeof data?.model === "string" ? data.model : options.agentKey
68898
+ )
68194
68899
  };
68195
- return result;
68196
68900
  }
68197
68901
  async function runInjectedLocalAgentTurn(options) {
68198
68902
  return runLocalAgentTurnForCli(options, { reportFailure: true });
@@ -68212,8 +68916,18 @@ async function runLocalAgentTurnForCli(options, settings) {
68212
68916
  const spinner = new Spinner(options.output, `${options.agentName} -> working locally`);
68213
68917
  spinner.start();
68214
68918
  try {
68215
- const traceLocalTools = shouldTraceLocalTools(options);
68919
+ const toolDisplayMode = resolveToolDisplayMode(options.env);
68920
+ const traceLocalTools = shouldEmitToolEvents(toolDisplayMode);
68921
+ const formatToolEvent = createToolEventFormatter(toolDisplayMode);
68216
68922
  const eventMode = resolveAgentEventMode(options);
68923
+ let wroteToolTrace = false;
68924
+ let streamedAssistantText = false;
68925
+ let printedAssistantLabel = false;
68926
+ const thinkingMode = resolveThinkingDisplayMode(options.env);
68927
+ const thinkingFilter = createThinkingAwareStreamFilter(
68928
+ (chunk) => options.output.write(chunk),
68929
+ thinkingMode
68930
+ );
68217
68931
  const subjectRefs = buildSubjectRefs(options);
68218
68932
  const allowedChildAgentKeys = options.allowedChildAgentKeys?.filter((key2) => key2.trim());
68219
68933
  const allowedToolNames = options.allowedToolNames?.filter((name) => name.trim());
@@ -68242,27 +68956,48 @@ async function runLocalAgentTurnForCli(options, settings) {
68242
68956
  ...typeof options.timeoutMs === "number" ? { timeoutMs: options.timeoutMs } : {},
68243
68957
  ...traceLocalTools ? {
68244
68958
  onToolEvent: (event) => {
68245
- if (traceLocalTools) {
68246
- spinner.stop();
68247
- options.output.write(
68248
- eventMode === "jsonl" ? formatToolJsonEvent(event) : formatToolTraceEvent(event)
68249
- );
68959
+ spinner.stop();
68960
+ const chunk = eventMode === "jsonl" ? formatToolJsonEvent(event) : formatToolEvent(event);
68961
+ if (chunk) {
68962
+ wroteToolTrace = true;
68963
+ options.output.write(chunk);
68964
+ }
68965
+ }
68966
+ } : {},
68967
+ ...!options.noStream ? {
68968
+ onTextDelta: (chunk) => {
68969
+ spinner.stop();
68970
+ if (!printedAssistantLabel) {
68971
+ options.output.write(`
68972
+ ${options.agentName} > `);
68973
+ printedAssistantLabel = true;
68250
68974
  }
68975
+ streamedAssistantText = true;
68976
+ thinkingFilter.push(chunk);
68251
68977
  }
68252
68978
  } : {}
68253
68979
  });
68254
68980
  spinner.stop();
68255
- const content = result.content.trim();
68256
- if (content) {
68257
- options.output.write(`
68981
+ if (streamedAssistantText) {
68982
+ thinkingFilter.flush();
68983
+ options.output.write("\n");
68984
+ } else {
68985
+ const content = formatAssistantResponseForCli(result.content.trim(), options);
68986
+ if (content) {
68987
+ options.output.write(`
68258
68988
  ${options.agentName} > ${content}
68259
68989
  `);
68260
- } else {
68261
- options.output.write(`
68990
+ } else {
68991
+ options.output.write(`
68262
68992
  ${options.agentName} > (no text response)
68263
68993
  `);
68994
+ }
68264
68995
  }
68265
- return { exitCode: 0, dialogId: result.dialogId };
68996
+ return {
68997
+ exitCode: 0,
68998
+ dialogId: result.dialogId,
68999
+ turnTokens: buildTurnTokenUsage(result.usage, result.model)
69000
+ };
68266
69001
  } catch (error) {
68267
69002
  spinner.stop();
68268
69003
  if (settings.reportFailure) {
@@ -68281,9 +69016,14 @@ async function readStreamingAgentRun(options, res) {
68281
69016
  return { exitCode: 1 };
68282
69017
  }
68283
69018
  const decoder = new TextDecoder();
69019
+ const thinkingMode = resolveThinkingDisplayMode(options.env);
68284
69020
  const writer = createStreamingTextWriter({
68285
69021
  write: (chunk) => options.output.write(chunk)
68286
69022
  });
69023
+ const thinkingFilter = createThinkingAwareStreamFilter(
69024
+ (chunk) => writer.push(chunk),
69025
+ thinkingMode
69026
+ );
68287
69027
  let buffer = "";
68288
69028
  let content = "";
68289
69029
  let dialogId;
@@ -68313,7 +69053,7 @@ ${options.agentName} > `);
68313
69053
  if (!chunk) return;
68314
69054
  printLabel();
68315
69055
  content += chunk;
68316
- writer.push(chunk);
69056
+ thinkingFilter.push(chunk);
68317
69057
  };
68318
69058
  try {
68319
69059
  while (true) {
@@ -68350,6 +69090,7 @@ ${options.agentName} > `);
68350
69090
  return { exitCode: 1 };
68351
69091
  } finally {
68352
69092
  writer.flushAll();
69093
+ thinkingFilter.flush();
68353
69094
  }
68354
69095
  if (!content) {
68355
69096
  options.output.write(`
@@ -68363,7 +69104,8 @@ ${options.agentName} > (no text response)
68363
69104
  `);
68364
69105
  return {
68365
69106
  exitCode: 0,
68366
- ...dialogId ? { dialogId } : {}
69107
+ ...dialogId ? { dialogId } : {},
69108
+ turnTokens: buildTurnTokenUsage(usage2, options.agentKey)
68367
69109
  };
68368
69110
  }
68369
69111
  async function runAgentTurn(options) {
@@ -68376,10 +69118,17 @@ async function runAgentTurn(options) {
68376
69118
  const skipLocal = await shouldSkipAutoLocalForServerPlatformTools(options);
68377
69119
  if (!skipLocal) {
68378
69120
  const localResult = await runLocalAgentTurnForCli(options, { reportFailure: false });
69121
+ if (localResult.exitCode !== 0 && localResult.localError) {
69122
+ options.output.write(
69123
+ `[nolo] auto runtime: local run unavailable (${localResult.localError instanceof Error ? localResult.localError.message : String(localResult.localError)}); falling back to server.
69124
+ `
69125
+ );
69126
+ }
68379
69127
  if (localResult.exitCode === 0) {
68380
69128
  return {
68381
69129
  exitCode: localResult.exitCode,
68382
- ...localResult.dialogId ? { dialogId: localResult.dialogId } : {}
69130
+ ...localResult.dialogId ? { dialogId: localResult.dialogId } : {},
69131
+ ...localResult.turnTokens ? { turnTokens: localResult.turnTokens } : {}
68383
69132
  };
68384
69133
  }
68385
69134
  if (isMissingLocalAgentConfigError(localResult.localError, options.agentKey)) {
@@ -68396,7 +69145,8 @@ async function runAgentTurn(options) {
68396
69145
  if (retriedLocalResult.exitCode === 0) {
68397
69146
  return {
68398
69147
  exitCode: retriedLocalResult.exitCode,
68399
- ...retriedLocalResult.dialogId ? { dialogId: retriedLocalResult.dialogId } : {}
69148
+ ...retriedLocalResult.dialogId ? { dialogId: retriedLocalResult.dialogId } : {},
69149
+ ...retriedLocalResult.turnTokens ? { turnTokens: retriedLocalResult.turnTokens } : {}
68400
69150
  };
68401
69151
  }
68402
69152
  }
@@ -69598,7 +70348,7 @@ function countByName(values) {
69598
70348
  function uniq(values) {
69599
70349
  return [...new Set(values.filter(Boolean))];
69600
70350
  }
69601
- function parseJsonObject3(raw) {
70351
+ function parseJsonObject2(raw) {
69602
70352
  if (typeof raw !== "string" || !raw.trim()) return null;
69603
70353
  try {
69604
70354
  const parsed = JSON.parse(raw);
@@ -69614,7 +70364,7 @@ function deriveWrittenFiles(toolMessages) {
69614
70364
  const toolName = toToolName(message?.toolName ?? message?.name);
69615
70365
  if (!writeToolNames.has(toolName)) continue;
69616
70366
  const payload = message?.toolPayload ?? {};
69617
- const content = parseJsonObject3(message?.content);
70367
+ const content = parseJsonObject2(message?.content);
69618
70368
  for (const candidate of [payload?.input?.filePath, payload?.response?.filePath, content?.filePath]) {
69619
70369
  const normalized = typeof candidate === "string" ? candidate.trim() : "";
69620
70370
  if (normalized) files.push(normalized);
@@ -69627,7 +70377,7 @@ function deriveToolErrors(toolMessages) {
69627
70377
  for (const message of toolMessages) {
69628
70378
  const toolName = toToolName(message?.toolName ?? message?.name);
69629
70379
  const payload = message?.toolPayload ?? {};
69630
- const content = parseJsonObject3(message?.content);
70380
+ const content = parseJsonObject2(message?.content);
69631
70381
  if (payload?.status === "failed" || content?.ok === false || content?.applied === false || content?.success === false) {
69632
70382
  errors.push(toolName || "unknown-tool");
69633
70383
  }
@@ -71467,7 +72217,6 @@ function getSpaceInternalCommandEntries() {
71467
72217
  // packages/cli/authCommands.ts
71468
72218
  import { createInterface } from "node:readline/promises";
71469
72219
  import { stdin as input, stdout as output } from "node:process";
71470
- import { rmSync as rmSync2 } from "node:fs";
71471
72220
  import { spawn as spawn5 } from "node:child_process";
71472
72221
 
71473
72222
  // packages/cli/client/profileConfig.ts
@@ -71502,16 +72251,31 @@ function loadProfileConfig(path7 = getDefaultProfileConfigPath()) {
71502
72251
  }
71503
72252
  return parsed;
71504
72253
  }
72254
+ function clearProfileAuthToken(path7 = getDefaultProfileConfigPath()) {
72255
+ const config = loadProfileConfig(path7);
72256
+ if (!config) return false;
72257
+ const profile = config.profiles[config.currentProfile];
72258
+ if (!profile?.authToken?.trim()) return false;
72259
+ delete profile.authToken;
72260
+ mkdirSync5(dirname6(path7), { recursive: true });
72261
+ writeFileSync4(path7, `${JSON.stringify(config, null, 2)}
72262
+ `, "utf8");
72263
+ return true;
72264
+ }
71505
72265
  function saveDefaultProfile(path7, profile) {
72266
+ const existing = loadProfileConfig(path7);
72267
+ const mergedDefault = {
72268
+ ...existing?.profiles?.default ?? {},
72269
+ serverUrl: normalizeProfileServerUrl(profile.serverUrl),
72270
+ authToken: profile.authToken.trim()
72271
+ };
71506
72272
  const config = {
71507
72273
  currentProfile: "default",
71508
72274
  profiles: {
71509
- default: profile
72275
+ ...existing?.profiles ?? {},
72276
+ default: mergedDefault
71510
72277
  }
71511
72278
  };
71512
- config.profiles.default.serverUrl = normalizeProfileServerUrl(
71513
- config.profiles.default.serverUrl
71514
- );
71515
72279
  mkdirSync5(dirname6(path7), { recursive: true });
71516
72280
  writeFileSync4(path7, `${JSON.stringify(config, null, 2)}
71517
72281
  `, "utf8");
@@ -71524,7 +72288,7 @@ function buildEnvFromProfile(config) {
71524
72288
  return {
71525
72289
  NOLO_PROFILE: config.currentProfile,
71526
72290
  NOLO_SERVER: profile.serverUrl,
71527
- AUTH_TOKEN: profile.authToken,
72291
+ ...profile.authToken?.trim() ? { AUTH_TOKEN: profile.authToken.trim() } : {},
71528
72292
  ...profile.agentKey ? { NOLO_AGENT: profile.agentKey } : {},
71529
72293
  ...profile.agentName ? { NOLO_AGENT_NAME: profile.agentName } : {}
71530
72294
  };
@@ -71545,9 +72309,65 @@ function getCurrentProfile(config) {
71545
72309
  if (!config) return null;
71546
72310
  return config.profiles[config.currentProfile] ?? null;
71547
72311
  }
72312
+ function saveProfileAgentSelection(selection, path7 = getDefaultProfileConfigPath()) {
72313
+ const config = loadProfileConfig(path7);
72314
+ if (!config) return null;
72315
+ const profile = config.profiles[config.currentProfile];
72316
+ if (!profile) return null;
72317
+ profile.agentKey = selection.agentKey.trim();
72318
+ profile.agentName = selection.agentName.trim();
72319
+ mkdirSync5(dirname6(path7), { recursive: true });
72320
+ writeFileSync4(path7, `${JSON.stringify(config, null, 2)}
72321
+ `, "utf8");
72322
+ return config;
72323
+ }
71548
72324
 
71549
72325
  // packages/cli/authCommands.ts
72326
+ init_cliEnvHelpers();
71550
72327
  init_defaultServer();
72328
+ var LOGIN_HELP_TEXT = `Log in to Nolo and save a local profile.
72329
+
72330
+ Usage:
72331
+ nolo login [--server <url>] [--no-browser] [--token <jwt>] [--manual]
72332
+
72333
+ Options:
72334
+ --server <url> Nolo server URL (default: ${DEFAULT_NOLO_SERVER_URL})
72335
+ --no-browser Print the authorization URL instead of opening a browser
72336
+ --token <jwt> Save a token directly without browser authorization
72337
+ --manual Prompt to paste a token interactively
72338
+
72339
+ By default, opens the Nolo website and polls until authorization completes.
72340
+ In SSH sessions, use --no-browser and open the printed URL on a machine with a browser
72341
+ where you are already logged into Nolo.
72342
+ `;
72343
+ var LOGOUT_HELP_TEXT = `Log out of Nolo by clearing the saved auth token.
72344
+
72345
+ Usage:
72346
+ nolo logout
72347
+
72348
+ Keeps other profile settings such as server URL and default agent preferences.
72349
+ Does not unset AUTH_TOKEN or other auth-related environment variables.
72350
+ `;
72351
+ var WHOAMI_HELP_TEXT = `Show the current login state.
72352
+
72353
+ Usage:
72354
+ nolo whoami
72355
+ `;
72356
+ function wantsHelp(args2) {
72357
+ return args2.includes("--help") || args2.includes("-h");
72358
+ }
72359
+ function formatDuration(ms) {
72360
+ const totalSec = Math.max(0, Math.ceil(ms / 1e3));
72361
+ const minutes = Math.floor(totalSec / 60);
72362
+ const seconds = totalSec % 60;
72363
+ if (minutes > 0) return `${minutes}m ${seconds}s`;
72364
+ return `${seconds}s`;
72365
+ }
72366
+ function formatTokenPreview(token) {
72367
+ const trimmed = token.trim();
72368
+ if (!trimmed) return "";
72369
+ return `${trimmed.slice(0, 8)}...`;
72370
+ }
71551
72371
  function getArg(args2, flag) {
71552
72372
  const index = args2.indexOf(flag);
71553
72373
  return index >= 0 ? args2[index + 1] : void 0;
@@ -71609,6 +72429,11 @@ async function runWebLogin(args2) {
71609
72429
  const intervalMs = Math.max(1, Number(start.interval) || 2) * 1e3;
71610
72430
  const timeoutMs = Math.max(1, Number(start.expiresIn) || 600) * 1e3;
71611
72431
  const deadline = args2.now() + timeoutMs;
72432
+ const statusLogIntervalMs = 15e3;
72433
+ let lastStatusLogAt = 0;
72434
+ args2.output.log(
72435
+ `[nolo] Waiting for browser authorization (${formatDuration(timeoutMs)} remaining)...`
72436
+ );
71612
72437
  while (args2.now() <= deadline) {
71613
72438
  const pollResponse = await postJson(
71614
72439
  args2.fetchImpl,
@@ -71617,6 +72442,16 @@ async function runWebLogin(args2) {
71617
72442
  );
71618
72443
  const poll = await pollResponse.json().catch(() => ({}));
71619
72444
  if (pollResponse.status === 202) {
72445
+ const currentTime = args2.now();
72446
+ if (lastStatusLogAt > 0 && currentTime - lastStatusLogAt >= statusLogIntervalMs) {
72447
+ const remainingMs = Math.max(0, deadline - currentTime);
72448
+ args2.output.log(
72449
+ `[nolo] Still waiting... (${formatDuration(remainingMs)} remaining)`
72450
+ );
72451
+ lastStatusLogAt = currentTime;
72452
+ } else if (lastStatusLogAt === 0) {
72453
+ lastStatusLogAt = currentTime;
72454
+ }
71620
72455
  await args2.sleep(intervalMs);
71621
72456
  continue;
71622
72457
  }
@@ -71639,12 +72474,16 @@ async function runWebLogin(args2) {
71639
72474
  return 1;
71640
72475
  }
71641
72476
  async function runLoginCommand(args2, deps = {}) {
72477
+ const outputTarget = deps.output ?? console;
72478
+ const errorTarget = deps.error ?? console;
72479
+ if (wantsHelp(args2)) {
72480
+ outputTarget.log(LOGIN_HELP_TEXT);
72481
+ return 0;
72482
+ }
71642
72483
  const configPath = deps.configPath ?? getDefaultProfileConfigPath();
71643
72484
  const serverArg = getArg(args2, "--server");
71644
72485
  const tokenArg = getArg(args2, "--token");
71645
72486
  const noBrowser = args2.includes("--no-browser");
71646
- const outputTarget = deps.output ?? console;
71647
- const errorTarget = deps.error ?? console;
71648
72487
  const serverUrl = (serverArg || DEFAULT_NOLO_SERVER_URL).replace(/\/+$/, "");
71649
72488
  if (tokenArg) {
71650
72489
  return saveTokenLogin({
@@ -71684,33 +72523,68 @@ async function runLoginCommand(args2, deps = {}) {
71684
72523
  });
71685
72524
  }
71686
72525
  function runWhoamiCommand(deps = {}) {
71687
- const env = deps.env ?? process.env;
71688
72526
  const output2 = deps.output ?? console;
72527
+ if (wantsHelp(deps.args ?? [])) {
72528
+ output2.log(WHOAMI_HELP_TEXT);
72529
+ return 0;
72530
+ }
72531
+ const env = deps.env ?? process.env;
71689
72532
  const config = loadProfileConfig(deps.configPath);
71690
72533
  const profile = getCurrentProfile(config);
71691
- if (!config || !profile) {
72534
+ const profileToken = profile?.authToken?.trim() ?? "";
72535
+ const envToken = resolveAuthTokenFromEnv(env);
72536
+ if (!profileToken && !envToken) {
71692
72537
  output2.log("Not logged in. Run: nolo login");
71693
72538
  return 1;
71694
72539
  }
71695
72540
  const runtimeEnv2 = buildCliRuntimeEnv(env, config);
71696
- const effectiveServer = runtimeEnv2.NOLO_SERVER ?? profile.serverUrl;
71697
72541
  const explicitServer = env.NOLO_SERVER || env.NOLO_SERVER_URL || env.BASE_URL;
72542
+ if (!profileToken && envToken) {
72543
+ output2.log("profile: not logged in (no saved token)");
72544
+ if (profile?.serverUrl) {
72545
+ output2.log(`profile server: ${profile.serverUrl}`);
72546
+ }
72547
+ output2.log(`effective server: ${runtimeEnv2.NOLO_SERVER ?? resolveServerUrlFallback(profile)}`);
72548
+ output2.log(`server source: ${explicitServer ? "env" : profile?.serverUrl ? "profile" : "default"}`);
72549
+ output2.log(`env token: ${formatTokenPreview(envToken)} (source: environment)`);
72550
+ return 0;
72551
+ }
72552
+ const effectiveServer = runtimeEnv2.NOLO_SERVER ?? profile.serverUrl;
71698
72553
  output2.log(`profile: ${config.currentProfile}`);
71699
72554
  output2.log(`profile server: ${profile.serverUrl}`);
71700
72555
  output2.log(`effective server: ${effectiveServer}`);
71701
72556
  output2.log(`server source: ${explicitServer ? "env" : "profile"}`);
71702
- output2.log(`token: ${profile.authToken.slice(0, 8)}...`);
72557
+ output2.log(`token: ${formatTokenPreview(profileToken)} (source: profile)`);
72558
+ if (envToken && envToken !== profileToken) {
72559
+ output2.log(`env token: ${formatTokenPreview(envToken)} (source: environment, unused while profile token is set)`);
72560
+ }
71703
72561
  return 0;
71704
72562
  }
71705
- function runLogoutCommand() {
71706
- const configPath = getDefaultProfileConfigPath();
71707
- rmSync2(configPath, { force: true });
71708
- console.log("Logged out.");
72563
+ function resolveServerUrlFallback(profile) {
72564
+ return profile?.serverUrl ?? DEFAULT_NOLO_SERVER_URL;
72565
+ }
72566
+ function runLogoutCommand(deps = {}) {
72567
+ const output2 = deps.output ?? console;
72568
+ if (wantsHelp(deps.args ?? [])) {
72569
+ output2.log(LOGOUT_HELP_TEXT);
72570
+ return 0;
72571
+ }
72572
+ const env = deps.env ?? process.env;
72573
+ const configPath = deps.configPath ?? getDefaultProfileConfigPath();
72574
+ if (!clearProfileAuthToken(configPath)) {
72575
+ output2.log("Not logged in.");
72576
+ return 0;
72577
+ }
72578
+ output2.log("Logged out.");
72579
+ if (resolveAuthTokenFromEnv(env)) {
72580
+ output2.log(
72581
+ "Note: AUTH_TOKEN in the environment is unchanged and may still authenticate commands."
72582
+ );
72583
+ }
71709
72584
  return 0;
71710
72585
  }
71711
72586
 
71712
72587
  // packages/cli/systemInternalCommandEntries.ts
71713
- init_defaultServer();
71714
72588
  init_machineCommands();
71715
72589
 
71716
72590
  // packages/cli/runtimeDoctorCommands.ts
@@ -71871,15 +72745,352 @@ async function runDoctorRuntimeCommand(args2, deps = {}) {
71871
72745
  return decision.runnable ? 0 : 1;
71872
72746
  }
71873
72747
 
71874
- // packages/cli/updateCommands.ts
72748
+ // packages/cli/standaloneBundle.ts
71875
72749
  init_processSpawn();
71876
- import { readFileSync as readFileSync8 } from "node:fs";
72750
+ import { createHash as createHash3 } from "node:crypto";
72751
+ import {
72752
+ cpSync,
72753
+ existsSync as existsSync9,
72754
+ mkdirSync as mkdirSync6,
72755
+ readFileSync as readFileSync8,
72756
+ readdirSync as readdirSync3,
72757
+ renameSync,
72758
+ rmSync as rmSync2,
72759
+ statSync as statSync2,
72760
+ writeFileSync as writeFileSync5
72761
+ } from "node:fs";
72762
+ import { tmpdir as tmpdir2 } from "node:os";
71877
72763
  import { dirname as dirname7, join as join10 } from "node:path";
71878
72764
  import { fileURLToPath as fileURLToPath4 } from "node:url";
71879
- var CLI_DIR2 = dirname7(fileURLToPath4(import.meta.url));
71880
- var PACKAGE_JSON_PATH = join10(CLI_DIR2, "package.json");
72765
+ var CLI_DOWNLOADS_SUBPATH = "cli";
72766
+ function getCliInstallChannel(serverUrl) {
72767
+ const normalized = typeof serverUrl === "string" ? serverUrl.trim().replace(/\/+$/, "") : "";
72768
+ if (!normalized) {
72769
+ return "latest";
72770
+ }
72771
+ try {
72772
+ const hostname2 = new URL(normalized).hostname.toLowerCase();
72773
+ if (hostname2 === "us.nolo.chat" || hostname2.endsWith(".us.nolo.chat")) {
72774
+ return "alpha";
72775
+ }
72776
+ } catch {
72777
+ if (/us\.nolo\.chat/i.test(normalized)) {
72778
+ return "alpha";
72779
+ }
72780
+ }
72781
+ return "latest";
72782
+ }
72783
+ function resolveBundleUpdateChannel(args2) {
72784
+ const fromServer = getCliInstallChannel(args2.serverUrl);
72785
+ const fromInstall = args2.install.manifest.channel;
72786
+ if (fromInstall && fromInstall !== fromServer) {
72787
+ args2.output?.write(
72788
+ `[nolo] Warning: server channel (${fromServer}) differs from installed bundle channel (${fromInstall}); using installed channel.
72789
+ `
72790
+ );
72791
+ return fromInstall;
72792
+ }
72793
+ return fromServer;
72794
+ }
72795
+ function resolveCliBundlePlatform() {
72796
+ const { platform: platform2, arch: arch2 } = process;
72797
+ if (platform2 === "linux" && (arch2 === "x64" || arch2 === "amd64")) {
72798
+ return "linux-x64";
72799
+ }
72800
+ if (platform2 === "darwin" && arch2 === "arm64") {
72801
+ return "darwin-arm64";
72802
+ }
72803
+ if (platform2 === "darwin" && (arch2 === "x64" || arch2 === "amd64")) {
72804
+ return "darwin-x64";
72805
+ }
72806
+ if (platform2 === "win32" && (arch2 === "x64" || arch2 === "amd64")) {
72807
+ return "win-x64";
72808
+ }
72809
+ return null;
72810
+ }
72811
+ function detectStandaloneBundleInstall(entrypointPath) {
72812
+ const cliDir = entrypointPath ? entrypointPath.startsWith("file:") ? dirname7(fileURLToPath4(entrypointPath)) : dirname7(entrypointPath) : dirname7(fileURLToPath4(import.meta.url));
72813
+ const bundleRoot = join10(cliDir, "..", "..");
72814
+ const manifestPath = join10(bundleRoot, "manifest.json");
72815
+ if (!existsSync9(manifestPath)) {
72816
+ return null;
72817
+ }
72818
+ try {
72819
+ const manifest = JSON.parse(
72820
+ readFileSync8(manifestPath, "utf8")
72821
+ );
72822
+ if (manifest?.name !== "nolo-cli" || manifest?.schemaVersion !== 1) {
72823
+ return null;
72824
+ }
72825
+ const entrypoint = join10(cliDir, "index.js");
72826
+ if (!existsSync9(entrypoint)) {
72827
+ return null;
72828
+ }
72829
+ return { bundleRoot, cliDir, manifest };
72830
+ } catch {
72831
+ return null;
72832
+ }
72833
+ }
72834
+ function buildBundleManifestUrl(args2) {
72835
+ const origin = args2.serverUrl.trim().replace(/\/+$/, "");
72836
+ return `${origin}/public/downloads/${CLI_DOWNLOADS_SUBPATH}/nolo-cli-${args2.channel}-${args2.platform}-manifest.json`;
72837
+ }
72838
+ function buildNpmSelfUpdateCommand(channel = "latest") {
72839
+ return ["npm", "install", "-g", `nolo-cli@${channel}`, "--force"];
72840
+ }
72841
+ function validatePublishedBundleManifest(args2) {
72842
+ if (args2.published.schemaVersion !== 1) {
72843
+ throw new Error(
72844
+ `Unsupported standalone bundle manifest schema: ${args2.published.schemaVersion}`
72845
+ );
72846
+ }
72847
+ if (args2.published.channel !== args2.expectedChannel) {
72848
+ throw new Error(
72849
+ `Standalone bundle manifest channel mismatch (expected ${args2.expectedChannel}, got ${args2.published.channel})`
72850
+ );
72851
+ }
72852
+ if (args2.published.platform !== args2.expectedPlatform) {
72853
+ throw new Error(
72854
+ `Standalone bundle manifest platform mismatch (expected ${args2.expectedPlatform}, got ${args2.published.platform})`
72855
+ );
72856
+ }
72857
+ if (!args2.published.fileName || !args2.published.url || !args2.published.sha256) {
72858
+ throw new Error("Standalone bundle manifest is missing required fields");
72859
+ }
72860
+ }
72861
+ async function runCommand(command2, spawn6 = resolveDefaultSpawn(), env = process.env) {
72862
+ const proc = spawn6({
72863
+ cmd: command2,
72864
+ stdin: "ignore",
72865
+ stdout: "inherit",
72866
+ stderr: "inherit",
72867
+ env
72868
+ });
72869
+ return proc.exited;
72870
+ }
72871
+ function hashFile(path7) {
72872
+ const hash = createHash3("sha256");
72873
+ hash.update(readFileSync8(path7));
72874
+ return hash.digest("hex");
72875
+ }
72876
+ function fileSize(path7) {
72877
+ return statSync2(path7).size;
72878
+ }
72879
+ async function downloadFile(url, destinationPath) {
72880
+ const response = await fetch(url);
72881
+ if (!response.ok) {
72882
+ throw new Error(`Download failed (${response.status}): ${url}`);
72883
+ }
72884
+ const bytes = await response.arrayBuffer();
72885
+ mkdirSync6(dirname7(destinationPath), { recursive: true });
72886
+ writeFileSync5(destinationPath, Buffer.from(bytes));
72887
+ }
72888
+ function assertCliDir(cliDir) {
72889
+ const entrypoint = join10(cliDir, "index.js");
72890
+ if (!existsSync9(entrypoint)) {
72891
+ throw new Error(`Standalone CLI layout is invalid: missing ${entrypoint}`);
72892
+ }
72893
+ }
72894
+ async function extractArchive(args2) {
72895
+ mkdirSync6(args2.destDir, { recursive: true });
72896
+ const spawn6 = args2.spawn ?? resolveDefaultSpawn();
72897
+ const env = args2.env ?? process.env;
72898
+ if (args2.archivePath.endsWith(".zip")) {
72899
+ if (process.platform === "win32") {
72900
+ const command2 = [
72901
+ "powershell",
72902
+ "-NoProfile",
72903
+ "-Command",
72904
+ `Expand-Archive -Path '${args2.archivePath.replace(/'/g, "''")}' -DestinationPath '${args2.destDir.replace(/'/g, "''")}' -Force`
72905
+ ];
72906
+ return runCommand(command2, spawn6, env);
72907
+ }
72908
+ return runCommand(["unzip", "-qo", args2.archivePath, "-d", args2.destDir], spawn6, env);
72909
+ }
72910
+ return runCommand(["tar", "--zstd", "-xf", args2.archivePath, "-C", args2.destDir], spawn6, env);
72911
+ }
72912
+ function findExtractedBundleRoot(extractDir) {
72913
+ const matches = readdirSync3(extractDir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.startsWith("nolo-cli-")).map((entry) => join10(extractDir, entry.name));
72914
+ if (matches.length === 1) {
72915
+ return matches[0];
72916
+ }
72917
+ throw new Error(
72918
+ `Could not locate extracted standalone CLI bundle under ${extractDir}`
72919
+ );
72920
+ }
72921
+ function atomicReplaceDirectory(args2) {
72922
+ assertCliDir(args2.sourceDir);
72923
+ const parent = dirname7(args2.targetDir);
72924
+ mkdirSync6(parent, { recursive: true });
72925
+ const stagingDir = `${args2.targetDir}.staging-${process.pid}`;
72926
+ const backupDir = `${args2.targetDir}.backup-${process.pid}`;
72927
+ rmSync2(stagingDir, { recursive: true, force: true });
72928
+ cpSync(args2.sourceDir, stagingDir, { recursive: true });
72929
+ assertCliDir(stagingDir);
72930
+ if (existsSync9(args2.targetDir)) {
72931
+ rmSync2(backupDir, { recursive: true, force: true });
72932
+ renameSync(args2.targetDir, backupDir);
72933
+ }
72934
+ try {
72935
+ renameSync(stagingDir, args2.targetDir);
72936
+ } catch (error) {
72937
+ if (existsSync9(backupDir) && !existsSync9(args2.targetDir)) {
72938
+ renameSync(backupDir, args2.targetDir);
72939
+ }
72940
+ throw error;
72941
+ }
72942
+ rmSync2(backupDir, { recursive: true, force: true });
72943
+ rmSync2(stagingDir, { recursive: true, force: true });
72944
+ }
72945
+ function replaceFileWithFallback(args2) {
72946
+ if (!existsSync9(args2.sourcePath)) {
72947
+ return;
72948
+ }
72949
+ mkdirSync6(dirname7(args2.targetPath), { recursive: true });
72950
+ const pendingPath = `${args2.targetPath}.pending-${process.pid}`;
72951
+ cpSync(args2.sourcePath, pendingPath);
72952
+ try {
72953
+ rmSync2(args2.targetPath, { force: true });
72954
+ renameSync(pendingPath, args2.targetPath);
72955
+ } catch (error) {
72956
+ rmSync2(pendingPath, { force: true });
72957
+ args2.output?.write(
72958
+ `[nolo] Warning: could not replace ${args2.targetPath} (${error instanceof Error ? error.message : String(error)}). Restart all nolo processes and run nolo update again.
72959
+ `
72960
+ );
72961
+ }
72962
+ }
72963
+ function replaceStandaloneBundleContents(args2) {
72964
+ const nextCliDir = join10(args2.nextRoot, "lib", "nolo-cli");
72965
+ const currentCliDir = join10(args2.currentRoot, "lib", "nolo-cli");
72966
+ if (!existsSync9(nextCliDir)) {
72967
+ throw new Error(`Downloaded bundle is missing ${nextCliDir}`);
72968
+ }
72969
+ atomicReplaceDirectory({
72970
+ targetDir: currentCliDir,
72971
+ sourceDir: nextCliDir
72972
+ });
72973
+ for (const relativePath of ["VERSION", "manifest.json"]) {
72974
+ const source = join10(args2.nextRoot, relativePath);
72975
+ if (existsSync9(source)) {
72976
+ replaceFileWithFallback({
72977
+ sourcePath: source,
72978
+ targetPath: join10(args2.currentRoot, relativePath),
72979
+ output: args2.output
72980
+ });
72981
+ }
72982
+ }
72983
+ const bunName = process.platform === "win32" ? "bun.exe" : "bun";
72984
+ replaceFileWithFallback({
72985
+ sourcePath: join10(args2.nextRoot, "bin", bunName),
72986
+ targetPath: join10(args2.currentRoot, "bin", bunName),
72987
+ output: args2.output
72988
+ });
72989
+ const launcherName = process.platform === "win32" ? "nolo.cmd" : "nolo";
72990
+ replaceFileWithFallback({
72991
+ sourcePath: join10(args2.nextRoot, "bin", launcherName),
72992
+ targetPath: join10(args2.currentRoot, "bin", launcherName),
72993
+ output: args2.output
72994
+ });
72995
+ }
72996
+ async function runStandaloneBundleUpdate(args2) {
72997
+ const output2 = args2.output ?? process.stdout;
72998
+ const env = args2.env ?? process.env;
72999
+ const platform2 = resolveCliBundlePlatform() ?? args2.install.manifest.platform ?? null;
73000
+ if (!platform2) {
73001
+ throw new Error(
73002
+ `Standalone CLI self-update is not supported on ${process.platform}/${process.arch}`
73003
+ );
73004
+ }
73005
+ const channel = resolveBundleUpdateChannel({
73006
+ install: args2.install,
73007
+ serverUrl: args2.serverUrl,
73008
+ output: output2
73009
+ });
73010
+ const manifestUrl = buildBundleManifestUrl({
73011
+ serverUrl: args2.serverUrl,
73012
+ channel,
73013
+ platform: platform2
73014
+ });
73015
+ output2.write(`Checking standalone bundle update: ${manifestUrl}
73016
+ `);
73017
+ const response = await fetch(manifestUrl, { cache: "no-store" });
73018
+ if (!response.ok) {
73019
+ throw new Error(
73020
+ `Standalone bundle manifest not found (${response.status}). Publish the CLI bundle for ${channel}/${platform2} first.`
73021
+ );
73022
+ }
73023
+ const published = await response.json();
73024
+ validatePublishedBundleManifest({
73025
+ published,
73026
+ expectedChannel: channel,
73027
+ expectedPlatform: platform2
73028
+ });
73029
+ const currentVersion = args2.install.manifest.version;
73030
+ if (published.version === currentVersion) {
73031
+ output2.write(`Already on latest standalone bundle (${currentVersion}).
73032
+ `);
73033
+ return 0;
73034
+ }
73035
+ const workDir = join10(tmpdir2(), `nolo-cli-update-${process.pid}`);
73036
+ rmSync2(workDir, { recursive: true, force: true });
73037
+ mkdirSync6(workDir, { recursive: true });
73038
+ try {
73039
+ const archivePath = join10(workDir, published.fileName);
73040
+ output2.write(
73041
+ `Updating standalone bundle ${currentVersion} -> ${published.version}
73042
+ `
73043
+ );
73044
+ output2.write(`Downloading ${published.url}
73045
+ `);
73046
+ await downloadFile(published.url, archivePath);
73047
+ const actualSize = fileSize(archivePath);
73048
+ if (actualSize !== published.size) {
73049
+ throw new Error(
73050
+ `Downloaded bundle size mismatch (expected ${published.size} bytes, got ${actualSize} bytes)`
73051
+ );
73052
+ }
73053
+ const actualSha256 = hashFile(archivePath);
73054
+ if (actualSha256 !== published.sha256) {
73055
+ throw new Error(
73056
+ `Downloaded bundle checksum mismatch (expected ${published.sha256}, got ${actualSha256})`
73057
+ );
73058
+ }
73059
+ const extractDir = join10(workDir, "extracted");
73060
+ const exitCode2 = await extractArchive({
73061
+ archivePath,
73062
+ destDir: extractDir,
73063
+ spawn: args2.spawn,
73064
+ env
73065
+ });
73066
+ if (exitCode2 !== 0) {
73067
+ throw new Error("Failed to extract standalone bundle archive");
73068
+ }
73069
+ const nextRoot = findExtractedBundleRoot(extractDir);
73070
+ replaceStandaloneBundleContents({
73071
+ currentRoot: args2.install.bundleRoot,
73072
+ nextRoot,
73073
+ output: output2
73074
+ });
73075
+ output2.write(`Standalone bundle updated to ${published.version}.
73076
+ `);
73077
+ output2.write("Restart open nolo sessions to use the new version.\n");
73078
+ return 0;
73079
+ } finally {
73080
+ rmSync2(workDir, { recursive: true, force: true });
73081
+ }
73082
+ }
73083
+
73084
+ // packages/cli/updateCommands.ts
73085
+ init_defaultServer();
73086
+ import { readFileSync as readFileSync9 } from "node:fs";
73087
+ import { dirname as dirname8, join as join11 } from "node:path";
73088
+ import { fileURLToPath as fileURLToPath5 } from "node:url";
73089
+ init_processSpawn();
73090
+ var CLI_DIR2 = dirname8(fileURLToPath5(import.meta.url));
73091
+ var PACKAGE_JSON_PATH = join11(CLI_DIR2, "package.json");
71881
73092
  function readPackageInfo() {
71882
- const raw = readFileSync8(PACKAGE_JSON_PATH, "utf8");
73093
+ const raw = readFileSync9(PACKAGE_JSON_PATH, "utf8");
71883
73094
  const parsed = JSON.parse(raw);
71884
73095
  return {
71885
73096
  name: parsed.name || "nolo-cli",
@@ -71889,28 +73100,42 @@ function readPackageInfo() {
71889
73100
  function buildCliVersionText(info) {
71890
73101
  return `${info.name} ${info.version}`;
71891
73102
  }
71892
- function buildSelfUpdateCommand() {
71893
- return ["npm", "install", "-g", "nolo-cli@latest", "--force"];
73103
+ function resolveSelfUpdateServerUrl(env = process.env, override) {
73104
+ if (override?.trim()) {
73105
+ return override.trim().replace(/\/+$/, "");
73106
+ }
73107
+ const fromEnv = env.NOLO_SERVER || env.BASE_URL;
73108
+ if (typeof fromEnv === "string" && fromEnv.trim()) {
73109
+ return fromEnv.trim().replace(/\/+$/, "");
73110
+ }
73111
+ const profile = loadProfileConfig();
73112
+ const profileUrl = profile?.profiles?.[profile.currentProfile]?.serverUrl;
73113
+ if (profileUrl?.trim()) {
73114
+ return profileUrl.trim().replace(/\/+$/, "");
73115
+ }
73116
+ return DEFAULT_NOLO_SERVER_URL;
71894
73117
  }
71895
73118
  function buildCliDoctorText(info) {
71896
- const updateCommand = buildSelfUpdateCommand().join(" ");
73119
+ const updateCommand = info.installKind === "standalone-bundle" ? "nolo update" : buildNpmSelfUpdateCommand(info.updateChannel).join(" ");
71897
73120
  return [
71898
73121
  "Nolo CLI doctor",
71899
73122
  "---------------",
71900
73123
  `version ${info.packageName} ${info.version}`,
73124
+ `install ${info.installKind}`,
73125
+ `channel ${info.updateChannel}`,
71901
73126
  `entry ${info.entrypoint}`,
71902
73127
  `server ${info.serverUrl}`,
71903
73128
  `profile ${info.profileName}`,
71904
73129
  `update nolo update`,
71905
73130
  "",
71906
73131
  "If direct `nolo` differs from repo-local `bun ./packages/cli/index.ts`,",
71907
- "the global npm install is older than this checkout.",
73132
+ "the global install is older than this checkout.",
71908
73133
  "",
71909
73134
  `Manual update: ${updateCommand}`
71910
73135
  ].join("\n");
71911
73136
  }
71912
73137
  function isRunSelfUpdateOptions(value) {
71913
- return typeof value === "object" && value !== null && ("output" in value || "spawn" in value);
73138
+ return typeof value === "object" && value !== null && ("output" in value || "spawn" in value || "entrypointPath" in value || "serverUrl" in value || "env" in value);
71914
73139
  }
71915
73140
  async function forwardSpawnOutput(stream, output2) {
71916
73141
  if (!stream) {
@@ -71933,7 +73158,20 @@ async function runSelfUpdate(outputOrOptions) {
71933
73158
  const options = outputOrOptions === void 0 ? {} : isRunSelfUpdateOptions(outputOrOptions) ? outputOrOptions : { output: outputOrOptions };
71934
73159
  const output2 = options.output ?? process.stdout;
71935
73160
  const spawn6 = options.spawn ?? resolveDefaultSpawn();
71936
- const command2 = buildSelfUpdateCommand();
73161
+ const env = options.env ?? process.env;
73162
+ const serverUrl = resolveSelfUpdateServerUrl(env, options.serverUrl);
73163
+ const entrypointPath = options.entrypointPath ?? join11(CLI_DIR2, "index.js");
73164
+ const bundleInstall = detectStandaloneBundleInstall(entrypointPath);
73165
+ if (bundleInstall) {
73166
+ return runStandaloneBundleUpdate({
73167
+ install: bundleInstall,
73168
+ serverUrl,
73169
+ output: output2,
73170
+ spawn: spawn6,
73171
+ env
73172
+ });
73173
+ }
73174
+ const command2 = buildNpmSelfUpdateCommand(getCliInstallChannel(serverUrl));
71937
73175
  output2.write(`Updating nolo with: ${command2.join(" ")}
71938
73176
  `);
71939
73177
  const useCustomSink = options.output !== void 0;
@@ -71942,7 +73180,7 @@ async function runSelfUpdate(outputOrOptions) {
71942
73180
  stdin: "inherit",
71943
73181
  stdout: useCustomSink ? "pipe" : "inherit",
71944
73182
  stderr: useCustomSink ? "pipe" : "inherit",
71945
- env: process.env
73183
+ env
71946
73184
  });
71947
73185
  if (!useCustomSink) {
71948
73186
  return await proc.exited;
@@ -71960,24 +73198,43 @@ function getSystemInternalCommandEntries(renderHelpText2) {
71960
73198
  return [
71961
73199
  createEnvCommand(["doctor", "runtime"], "Diagnose local-first agent runtime selection", runDoctorRuntimeCommand),
71962
73200
  createArgsCommand(["login"], "Log in to Nolo", (args2) => Promise.resolve(runLoginCommand(args2))),
71963
- createArgsCommand(["logout"], "Log out of Nolo", async () => runLogoutCommand()),
71964
- createArgsCommand(["whoami"], "Show the current login state", async () => runWhoamiCommand()),
73201
+ createContextCommand(
73202
+ ["logout"],
73203
+ "Log out of Nolo",
73204
+ async (args2, ctx) => runLogoutCommand({ args: args2, env: ctx.env })
73205
+ ),
73206
+ createContextCommand(
73207
+ ["whoami"],
73208
+ "Show the current login state",
73209
+ async (args2, ctx) => runWhoamiCommand({ args: args2, env: ctx.env })
73210
+ ),
71965
73211
  createEntrypointEnvCommand(["connect"], "Send machine heartbeats or hold a connector websocket", runMachineConnectCommand),
71966
73212
  createDaemonShortcutCommand(["daemon"], "Run the connector daemon shortcut", runMachineConnectCommand),
71967
73213
  createEnvCommand(["machine", "status"], "List machines registered to the current profile", runMachineStatusCommand),
71968
73214
  createContextCommand(["doctor"], "Show CLI doctor information", async (_args, ctx) => {
73215
+ const serverUrl = resolveSelfUpdateServerUrl(ctx.env);
71969
73216
  console.log(
71970
73217
  buildCliDoctorText({
71971
73218
  packageName: ctx.packageInfo.name,
71972
73219
  version: ctx.packageInfo.version,
71973
73220
  entrypoint: ctx.entrypointPath,
71974
- serverUrl: ctx.env.NOLO_SERVER || ctx.env.BASE_URL || DEFAULT_NOLO_SERVER_URL,
71975
- profileName: ctx.env.NOLO_PROFILE || "local"
73221
+ serverUrl,
73222
+ profileName: ctx.env.NOLO_PROFILE || "local",
73223
+ installKind: detectStandaloneBundleInstall(ctx.entrypointPath) ? "standalone-bundle" : "npm-global",
73224
+ updateChannel: getCliInstallChannel(serverUrl)
71976
73225
  })
71977
73226
  );
71978
73227
  return 0;
71979
73228
  }),
71980
- createArgsCommand(["update"], "Update the CLI", async () => runSelfUpdate()),
73229
+ createContextCommand(
73230
+ ["update"],
73231
+ "Update the CLI",
73232
+ async (_args, ctx) => runSelfUpdate({
73233
+ entrypointPath: ctx.entrypointPath,
73234
+ serverUrl: resolveSelfUpdateServerUrl(ctx.env),
73235
+ env: ctx.env
73236
+ })
73237
+ ),
71981
73238
  createContextCommand(["version"], "Show CLI version", async (_args, ctx) => {
71982
73239
  console.log(buildCliVersionText(ctx.packageInfo));
71983
73240
  return 0;
@@ -72559,7 +73816,6 @@ function renderHelpText() {
72559
73816
  " nolo space list --json",
72560
73817
  " nolo agent pull agent-pub-01APPBUILDER00000001YAII3I",
72561
73818
  " nolo agent read agent-pub-01APPBUILDER00000001YAII3I",
72562
- " nolo agent usage qoder",
72563
73819
  ' nolo agent run frontend-implementer --msg "polish notifications"',
72564
73820
  " nolo agent bind-current agent-user-1-agent-1",
72565
73821
  " nolo agent runtime-doctor agent-user-1-agent-1",
@@ -72654,7 +73910,7 @@ function resolveTuiLaunchMode(args2) {
72654
73910
  // packages/cli/tui/readlineWorkspace.ts
72655
73911
  import { createInterface as createInterface2 } from "node:readline";
72656
73912
  import { stdin as defaultInput, stdout as defaultOutput } from "node:process";
72657
- import { join as join11 } from "node:path";
73913
+ import { join as join12 } from "node:path";
72658
73914
 
72659
73915
  // packages/cli/client/compactDialog.ts
72660
73916
  import { ulid as ulid11 } from "ulid";
@@ -72802,23 +74058,410 @@ async function compactDialog(options) {
72802
74058
  // packages/cli/tui/readlineWorkspace.ts
72803
74059
  init_processSpawn();
72804
74060
 
72805
- // packages/cli/tui/session.ts
72806
- init_defaultServer();
74061
+ // packages/cli/tui/agentPicker.ts
74062
+ init_agentAliases();
74063
+
74064
+ // packages/cli/tui/agentCatalog.ts
74065
+ init_cliEnvHelpers();
72807
74066
  var DEFAULT_TUI_AGENT_KEY = "agent-pub-01NOLOAPPBLD000000019KCKT0";
72808
- var DEFAULT_TUI_SERVER_URL = DEFAULT_NOLO_SERVER_URL;
72809
- var KNOWN_AGENTS = [
74067
+ var PLATFORM_AGENTS = [
72810
74068
  {
72811
74069
  name: "nolo",
72812
- key: "agent-pub-01NOLOAPPBLD000000019KCKT0",
74070
+ key: DEFAULT_TUI_AGENT_KEY,
74071
+ model: "-",
74072
+ kind: "platform",
72813
74073
  description: "one assistant that routes work across your agents and data"
72814
74074
  },
72815
74075
  {
72816
74076
  name: "app-builder",
72817
74077
  key: "agent-pub-01APPBUILDER00000001YAII3I",
74078
+ model: "-",
74079
+ kind: "platform",
72818
74080
  description: "builds web apps, tools, charts, and product prototypes"
72819
74081
  }
72820
74082
  ];
72821
- var KNOWN_AGENT_ALIASES = Object.fromEntries(KNOWN_AGENTS.map((agent) => [agent.name, agent]));
74083
+ function toUpdatedAt(value) {
74084
+ if (value == null) return 0;
74085
+ const parsed = new Date(value).getTime();
74086
+ return Number.isFinite(parsed) ? parsed : 0;
74087
+ }
74088
+ function listedAgentToCatalogEntry(agent) {
74089
+ return {
74090
+ name: agent.name,
74091
+ key: agent.privateKey,
74092
+ model: agent.model,
74093
+ kind: "private",
74094
+ updatedAt: toUpdatedAt(agent.updatedAt)
74095
+ };
74096
+ }
74097
+ function mergeCatalogEntries(currentKey, platformAgents, privateAgents) {
74098
+ const seen = /* @__PURE__ */ new Set();
74099
+ const merged = [];
74100
+ const push = (entry) => {
74101
+ if (seen.has(entry.key)) return;
74102
+ seen.add(entry.key);
74103
+ merged.push(entry);
74104
+ };
74105
+ const current = [...platformAgents, ...privateAgents].find((entry) => entry.key === currentKey) ?? null;
74106
+ if (current) push(current);
74107
+ for (const entry of platformAgents) {
74108
+ if (entry.key !== currentKey) push(entry);
74109
+ }
74110
+ const sortedPrivate = [...privateAgents].sort((a, b) => {
74111
+ const tb = b.updatedAt ?? 0;
74112
+ const ta = a.updatedAt ?? 0;
74113
+ if (tb !== ta) return tb - ta;
74114
+ return a.name.localeCompare(b.name);
74115
+ });
74116
+ for (const entry of sortedPrivate) {
74117
+ if (entry.key !== currentKey) push(entry);
74118
+ }
74119
+ return merged;
74120
+ }
74121
+ async function loadAgentCatalog(args2) {
74122
+ const env = args2.env ?? process.env;
74123
+ const fetchImpl = args2.fetchImpl ?? fetch;
74124
+ const fallbackFetchImpl = args2.fallbackFetchImpl;
74125
+ const authToken = resolveAuthToken([], env);
74126
+ const userId = authToken ? parseUserIdFromAuthToken(authToken) : null;
74127
+ if (!authToken || !userId) {
74128
+ return mergeCatalogEntries(args2.currentKey, PLATFORM_AGENTS, []);
74129
+ }
74130
+ const serverUrl = resolveServerUrl(env);
74131
+ const serverUrls = resolveServerCandidates([], env, serverUrl);
74132
+ let privateAgents = [];
74133
+ try {
74134
+ const remoteResult = await listRemoteAgentsAcrossServers({
74135
+ authToken,
74136
+ fallbackFetchImpl,
74137
+ fetchImpl,
74138
+ includeLegacy: false,
74139
+ serverUrls,
74140
+ userId
74141
+ });
74142
+ privateAgents = remoteResult.agents.map(listedAgentToCatalogEntry);
74143
+ } catch {
74144
+ try {
74145
+ const db = await getReadableCliDb();
74146
+ const cached = await listLocalCachedAgents({ db, userId });
74147
+ privateAgents = cached.map(listedAgentToCatalogEntry);
74148
+ } catch {
74149
+ privateAgents = (await listRemoteAgents({
74150
+ authToken,
74151
+ fallbackFetchImpl,
74152
+ fetchImpl,
74153
+ includeLegacy: false,
74154
+ serverUrl,
74155
+ userId,
74156
+ queryUserRecords,
74157
+ readDbRecord
74158
+ })).map(listedAgentToCatalogEntry);
74159
+ }
74160
+ }
74161
+ return mergeCatalogEntries(args2.currentKey, PLATFORM_AGENTS, privateAgents);
74162
+ }
74163
+ function renderAgentCatalogList(entries, currentKey) {
74164
+ const lines = ["Agents:"];
74165
+ entries.forEach((entry, index) => {
74166
+ const current = entry.key === currentKey ? " (current)" : "";
74167
+ const kind = entry.kind === "platform" ? "platform" : "private";
74168
+ const detail = entry.description ? ` \u2014 ${entry.description}` : "";
74169
+ lines.push(
74170
+ ` ${String(index + 1).padStart(2)} ${entry.name.padEnd(18)} ${entry.model.padEnd(14)} ${kind}${detail}${current}`
74171
+ );
74172
+ });
74173
+ lines.push("");
74174
+ lines.push("Tip: run /agent in an interactive terminal to pick with \u2191\u2193.");
74175
+ return lines.join("\n");
74176
+ }
74177
+ function findAgentCatalogEntry(entries, rawTarget) {
74178
+ const target = rawTarget.trim();
74179
+ if (!target) return null;
74180
+ if (/^\d+$/.test(target)) {
74181
+ const entry = entries[Number(target) - 1];
74182
+ return entry ? { name: entry.name, key: entry.key } : null;
74183
+ }
74184
+ const lower = target.toLowerCase();
74185
+ const byName = entries.find(
74186
+ (entry) => entry.name.toLowerCase() === lower || entry.key.toLowerCase() === lower || entry.key.toLowerCase().endsWith(`-${lower}`)
74187
+ );
74188
+ if (byName) return { name: byName.name, key: byName.key };
74189
+ if (target.startsWith("agent-") || target.startsWith("agent-pub-") || target.startsWith("cybot-")) {
74190
+ return { name: target, key: target };
74191
+ }
74192
+ return null;
74193
+ }
74194
+
74195
+ // packages/cli/tui/selectDialog.ts
74196
+ var CSI_ARROW_UP = "\x1B[A";
74197
+ var CSI_ARROW_DOWN = "\x1B[B";
74198
+ var CSI_ARROW_UP_APP = "\x1BOA";
74199
+ var CSI_ARROW_DOWN_APP = "\x1BOB";
74200
+ var DEFAULT_MAX_VISIBLE = 8;
74201
+ function computeVisibleWindow(args2) {
74202
+ const maxVisible = Math.max(1, args2.maxVisible ?? DEFAULT_MAX_VISIBLE);
74203
+ if (args2.total <= maxVisible) {
74204
+ return { start: 0, end: args2.total, maxVisible };
74205
+ }
74206
+ let start = Math.max(0, args2.selectedIndex - Math.floor(maxVisible / 2));
74207
+ if (start + maxVisible > args2.total) {
74208
+ start = args2.total - maxVisible;
74209
+ }
74210
+ return { start, end: start + maxVisible, maxVisible };
74211
+ }
74212
+ function renderSelectDialog(args2) {
74213
+ const total = args2.items.length;
74214
+ const window2 = computeVisibleWindow({
74215
+ selectedIndex: args2.selectedIndex,
74216
+ total,
74217
+ maxVisible: args2.maxVisible
74218
+ });
74219
+ const lines = [
74220
+ args2.title ?? `Select agent (\u2191\u2193 Enter Esc) ${args2.selectedIndex + 1}/${total}`
74221
+ ];
74222
+ if (window2.start > 0) {
74223
+ lines.push(` ... ${window2.start} more above`);
74224
+ }
74225
+ for (let index = window2.start; index < window2.end; index += 1) {
74226
+ const item = args2.items[index];
74227
+ const marker = index === args2.selectedIndex ? ">" : " ";
74228
+ const detail = item.detail ? ` ${item.detail}` : "";
74229
+ lines.push(`${marker} ${item.label}${detail}`);
74230
+ }
74231
+ if (window2.end < total) {
74232
+ lines.push(` ... ${total - window2.end} more below`);
74233
+ }
74234
+ return lines.join("\n");
74235
+ }
74236
+ function countRenderedLines(text) {
74237
+ return text.split("\n").length;
74238
+ }
74239
+ function clearRenderedLines(output2, lineCount) {
74240
+ if (!output2.isTTY || lineCount <= 0) return;
74241
+ for (let index = 0; index < lineCount; index += 1) {
74242
+ output2.write("\x1B[1A\x1B[2K");
74243
+ }
74244
+ }
74245
+ function isArrowUp(sequence) {
74246
+ return sequence === CSI_ARROW_UP || sequence === CSI_ARROW_UP_APP;
74247
+ }
74248
+ function isArrowDown(sequence) {
74249
+ return sequence === CSI_ARROW_DOWN || sequence === CSI_ARROW_DOWN_APP;
74250
+ }
74251
+ function isSubmit(sequence) {
74252
+ return sequence === "\r" || sequence === "\n";
74253
+ }
74254
+ function isCancel(sequence) {
74255
+ return sequence === "" || sequence === "\x1B";
74256
+ }
74257
+ function createRawKeyReader(input2) {
74258
+ let buffer = "";
74259
+ const tryParseSequence = () => {
74260
+ if (!buffer) return null;
74261
+ if (isSubmit(buffer) || isCancel(buffer)) {
74262
+ const sequence2 = buffer;
74263
+ buffer = "";
74264
+ return sequence2;
74265
+ }
74266
+ if (buffer.startsWith("\x1B")) {
74267
+ for (const candidate of [
74268
+ CSI_ARROW_UP,
74269
+ CSI_ARROW_DOWN,
74270
+ CSI_ARROW_UP_APP,
74271
+ CSI_ARROW_DOWN_APP
74272
+ ]) {
74273
+ if (buffer.startsWith(candidate)) {
74274
+ buffer = buffer.slice(candidate.length);
74275
+ return candidate;
74276
+ }
74277
+ }
74278
+ if (buffer.length >= 8) {
74279
+ buffer = "";
74280
+ return null;
74281
+ }
74282
+ return void 0;
74283
+ }
74284
+ const sequence = buffer;
74285
+ buffer = "";
74286
+ return sequence;
74287
+ };
74288
+ return () => new Promise((resolve6) => {
74289
+ const finalize = (sequence) => {
74290
+ cleanup();
74291
+ resolve6(sequence ?? null);
74292
+ };
74293
+ const onReadable = () => {
74294
+ while (true) {
74295
+ const chunk = input2.read();
74296
+ if (chunk == null) break;
74297
+ buffer += typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf8");
74298
+ const parsed = tryParseSequence();
74299
+ if (parsed === void 0) return;
74300
+ finalize(parsed);
74301
+ return;
74302
+ }
74303
+ };
74304
+ const cleanup = () => {
74305
+ input2.off("readable", onReadable);
74306
+ };
74307
+ onReadable();
74308
+ if (buffer) {
74309
+ const parsed = tryParseSequence();
74310
+ if (parsed !== void 0) {
74311
+ finalize(parsed);
74312
+ return;
74313
+ }
74314
+ }
74315
+ input2.on("readable", onReadable);
74316
+ });
74317
+ }
74318
+ function drainInputBuffer(input2) {
74319
+ if (typeof input2.read !== "function") return;
74320
+ while (input2.read() !== null) {
74321
+ }
74322
+ }
74323
+ async function runSelectDialog(args2) {
74324
+ const items = args2.items;
74325
+ if (items.length === 0) {
74326
+ return { kind: "cancelled" };
74327
+ }
74328
+ let selectedIndex = Math.min(
74329
+ Math.max(args2.initialIndex ?? 0, 0),
74330
+ items.length - 1
74331
+ );
74332
+ const output2 = args2.output ?? process.stdout;
74333
+ const input2 = args2.input ?? process.stdin;
74334
+ const readKey = args2.readKey ?? createRawKeyReader(input2);
74335
+ const wasRaw = Boolean(input2.isTTY && input2.isRaw);
74336
+ const wasPaused = typeof input2.isPaused === "function" ? input2.isPaused() : false;
74337
+ let renderedLineCount = 0;
74338
+ const paint = () => {
74339
+ const frame = renderSelectDialog({
74340
+ items,
74341
+ selectedIndex,
74342
+ title: args2.title,
74343
+ maxVisible: args2.maxVisible
74344
+ });
74345
+ if (output2.isTTY && typeof output2.write === "function") {
74346
+ clearRenderedLines(output2, renderedLineCount);
74347
+ output2.write(`${frame}
74348
+ `);
74349
+ renderedLineCount = countRenderedLines(frame);
74350
+ return;
74351
+ }
74352
+ if (typeof output2.write === "function") {
74353
+ output2.write(`${frame}
74354
+ `);
74355
+ }
74356
+ renderedLineCount = countRenderedLines(frame);
74357
+ };
74358
+ if (input2.isTTY) {
74359
+ if (!wasRaw) input2.setRawMode(true);
74360
+ if (!wasPaused) input2.pause();
74361
+ }
74362
+ paint();
74363
+ try {
74364
+ while (true) {
74365
+ const sequence = await readKey();
74366
+ if (sequence == null) {
74367
+ return { kind: "cancelled" };
74368
+ }
74369
+ if (isCancel(sequence)) {
74370
+ return { kind: "cancelled" };
74371
+ }
74372
+ if (isSubmit(sequence)) {
74373
+ return { kind: "selected", index: selectedIndex, item: items[selectedIndex] };
74374
+ }
74375
+ if (isArrowUp(sequence)) {
74376
+ selectedIndex = selectedIndex <= 0 ? items.length - 1 : selectedIndex - 1;
74377
+ paint();
74378
+ continue;
74379
+ }
74380
+ if (isArrowDown(sequence)) {
74381
+ selectedIndex = selectedIndex >= items.length - 1 ? 0 : selectedIndex + 1;
74382
+ paint();
74383
+ continue;
74384
+ }
74385
+ }
74386
+ } finally {
74387
+ if (input2.isTTY) {
74388
+ drainInputBuffer(input2);
74389
+ if (!wasRaw) input2.setRawMode(false);
74390
+ if (!wasPaused) input2.resume();
74391
+ clearRenderedLines(output2, renderedLineCount);
74392
+ renderedLineCount = 0;
74393
+ }
74394
+ }
74395
+ }
74396
+
74397
+ // packages/cli/tui/agentPicker.ts
74398
+ function toAgentPickerItems(entries) {
74399
+ return entries.map((entry) => ({
74400
+ label: entry.name,
74401
+ detail: `${entry.model} ${entry.kind}`,
74402
+ entry
74403
+ }));
74404
+ }
74405
+ function formatAgentSwitchMessage(args2) {
74406
+ const dialog = args2.dialogId ? `Dialog kept: ${args2.dialogId}` : "Dialog kept: new";
74407
+ return `Switched to ${args2.name}. ${dialog}`;
74408
+ }
74409
+ async function runAgentPicker(args2) {
74410
+ const output2 = args2.output ?? process.stdout;
74411
+ const input2 = args2.input ?? process.stdin;
74412
+ const interactive = args2.interactive ?? Boolean(input2.isTTY && output2.isTTY);
74413
+ const entries = await loadAgentCatalog({
74414
+ env: args2.env,
74415
+ currentKey: args2.currentKey,
74416
+ fetchImpl: args2.fetchImpl,
74417
+ fallbackFetchImpl: args2.fallbackFetchImpl
74418
+ });
74419
+ if (!interactive) {
74420
+ return {
74421
+ kind: "list",
74422
+ output: renderAgentCatalogList(entries, args2.currentKey),
74423
+ entries
74424
+ };
74425
+ }
74426
+ const items = toAgentPickerItems(entries);
74427
+ const initialIndex = Math.max(
74428
+ entries.findIndex((entry) => entry.key === args2.currentKey),
74429
+ 0
74430
+ );
74431
+ const result = await runSelectDialog({
74432
+ items,
74433
+ initialIndex,
74434
+ title: void 0,
74435
+ input: input2,
74436
+ output: output2,
74437
+ readKey: args2.readKey
74438
+ });
74439
+ if (result.kind === "cancelled") {
74440
+ return { kind: "cancelled", entries };
74441
+ }
74442
+ const selected = result.item.entry;
74443
+ return {
74444
+ kind: "selected",
74445
+ name: selected.name,
74446
+ key: selected.key,
74447
+ entries
74448
+ };
74449
+ }
74450
+ function resolveAgentSwitchTarget(rawTarget, catalogEntries = []) {
74451
+ const resolvedKey = resolveCliAgentKeyInput(rawTarget);
74452
+ if (resolvedKey !== rawTarget.trim()) {
74453
+ const aliasEntry = catalogEntries.find((entry) => entry.key === resolvedKey);
74454
+ return {
74455
+ name: aliasEntry?.name ?? rawTarget.trim().toLowerCase(),
74456
+ key: resolvedKey
74457
+ };
74458
+ }
74459
+ return findAgentCatalogEntry(catalogEntries, rawTarget);
74460
+ }
74461
+
74462
+ // packages/cli/tui/session.ts
74463
+ init_defaultServer();
74464
+ var DEFAULT_TUI_SERVER_URL = DEFAULT_NOLO_SERVER_URL;
72822
74465
  function shortenDialogId(dialogId) {
72823
74466
  return dialogId.length > 12 ? `${dialogId.slice(0, 6)}...${dialogId.slice(-4)}` : dialogId;
72824
74467
  }
@@ -72840,25 +74483,29 @@ function createInitialTuiState(env = process.env) {
72840
74483
  ),
72841
74484
  cliVersion: env.NOLO_CLI_VERSION?.trim() || void 0,
72842
74485
  attachedDocs: [],
72843
- runtimeMode: env.NOLO_RUNTIME_MODE === "local" || env.NOLO_RUNTIME_MODE === "server" ? env.NOLO_RUNTIME_MODE : "auto"
74486
+ runtimeMode: env.NOLO_RUNTIME_MODE === "local" || env.NOLO_RUNTIME_MODE === "server" ? env.NOLO_RUNTIME_MODE : "auto",
74487
+ thinkingDisplay: normalizeThinkingDisplayMode(
74488
+ env.NOLO_CLI_THINKING ?? env.NOLO_THINKING,
74489
+ "hide"
74490
+ ),
74491
+ toolDisplay: normalizeToolDisplayMode(env.NOLO_CLI_TOOLS ?? env.NOLO_TOOLS, "compact"),
74492
+ renderDisplay: normalizeRenderDisplayMode(env.NOLO_CLI_RENDER ?? env.NOLO_RENDER, "rich")
72844
74493
  };
72845
74494
  }
72846
74495
  function renderStatusLine(state) {
72847
- const docs = state.attachedDocs.length > 0 ? String(state.attachedDocs.length) : "0";
72848
- return [
72849
- `agent ${state.agentName}`,
72850
- `dialog ${resolveDialogLabel(state)}`,
72851
- `docs ${docs}`,
72852
- `runtime ${state.runtimeMode}`,
72853
- `profile ${state.profileName}`
72854
- ].join(" | ");
74496
+ const colorEnabled = resolveCliColorEnabled();
74497
+ const agent = styleCliText(state.agentName, "cyan", colorEnabled);
74498
+ const tokens = dimCliText(renderTokenStatus(state.turnTokens), colorEnabled);
74499
+ const profile = dimCliText(`profile ${state.profileName}`, colorEnabled);
74500
+ return [`agent ${agent}`, tokens, profile].join(
74501
+ dimCliText(" | ", colorEnabled)
74502
+ );
72855
74503
  }
72856
74504
  function renderWelcome(state) {
72857
- const docs = state.attachedDocs.length > 0 ? `${state.attachedDocs.length} attached` : "none";
72858
74505
  return [
72859
74506
  "",
72860
74507
  `Nolo workspace${state.cliVersion ? ` nolo ${state.cliVersion}` : ""}`,
72861
- `agent ${state.agentName} | dialog ${resolveDialogLabel(state)} | docs ${docs} | profile ${state.profileName}`,
74508
+ `agent ${state.agentName} | ${renderTokenStatus(state.turnTokens)} | profile ${state.profileName}`,
72862
74509
  `server ${state.serverUrl}`,
72863
74510
  "",
72864
74511
  "Tell nolo what you want. Use /help for commands. Use /version if this install feels stale.",
@@ -72876,16 +74523,21 @@ function renderTuiHelp() {
72876
74523
  " /compact Compact current dialog and fork a new one",
72877
74524
  " /context Show workspace context and next actions",
72878
74525
  " /runtime <mode> Use auto, local, or server runtime",
72879
- " /agent Show the current agent",
72880
- " /agents List built-in agent shortcuts",
72881
- " /switch <agent> Switch the current agent",
74526
+ " /tools <mode> Control tool trace: hide, compact, verbose",
74527
+ " /thinking <mode> Control thinking output: hide, marker, show",
74528
+ " /render <mode> Control assistant output: plain, rich",
74529
+ " /agent Pick an agent interactively (\u2191\u2193, Enter)",
74530
+ " /agent list List agents as text",
74531
+ " /agent <name> Switch directly by name, alias, or key",
74532
+ " /agents List platform agent shortcuts",
74533
+ " /switch <agent> Switch the current agent (alias of /agent <name>)",
72882
74534
  " /dialog Show the current dialog",
72883
74535
  " /doc List attached docs",
72884
74536
  " /doc attach <doc> Attach a doc to this workspace",
72885
74537
  " /customize Describe how you want to tune nolo",
72886
74538
  " /login Show login/profile hint",
72887
74539
  " /profile Show active profile",
72888
- " /update Update the global nolo install",
74540
+ " /update Update the nolo CLI install",
72889
74541
  " /version Show version/update hint",
72890
74542
  " /exit Leave the workspace",
72891
74543
  "",
@@ -72897,12 +74549,16 @@ function renderContextPanel(state) {
72897
74549
  return [
72898
74550
  "Workspace context",
72899
74551
  "-----------------",
72900
- `agent ${state.agentName}`,
72901
- `dialog ${resolveDialogLabel(state)}`,
72902
- `docs ${docs}`,
72903
- `profile ${state.profileName}`,
72904
- `runtime ${state.runtimeMode}`,
72905
- `server ${state.serverUrl}`,
74552
+ `agent ${state.agentName}`,
74553
+ `tokens ${renderTokenStatus(state.turnTokens)}`,
74554
+ `dialog ${resolveDialogLabel(state)}`,
74555
+ `docs ${docs}`,
74556
+ `profile ${state.profileName}`,
74557
+ `runtime ${state.runtimeMode}`,
74558
+ `tools ${state.toolDisplay}`,
74559
+ `thinking ${state.thinkingDisplay}`,
74560
+ `render ${state.renderDisplay}`,
74561
+ `server ${state.serverUrl}`,
72906
74562
  "",
72907
74563
  "Next:",
72908
74564
  " /agents see specialist shortcuts",
@@ -72913,25 +74569,22 @@ function renderContextPanel(state) {
72913
74569
  function renderKnownAgents() {
72914
74570
  return [
72915
74571
  "Agents:",
72916
- ...KNOWN_AGENTS.map(
72917
- (agent, index) => ` ${index + 1} ${agent.name.padEnd(11)} ${agent.description}`
74572
+ ...PLATFORM_AGENTS.map(
74573
+ (agent, index) => ` ${index + 1} ${agent.name.padEnd(11)} ${agent.description ?? ""}`
72918
74574
  ),
72919
74575
  "",
72920
- "Tip: stay on nolo for the one-assistant feel; switch only when you want a specialist directly."
74576
+ "Tip: run /agent for the full picker, or /agent list for your private agents too."
72921
74577
  ].join("\n");
72922
74578
  }
72923
- function resolveSwitchTarget(rawTarget) {
72924
- const target = rawTarget.trim();
72925
- if (/^\d+$/.test(target)) {
72926
- const agent = KNOWN_AGENTS[Number(target) - 1];
72927
- return agent ? { name: agent.name, key: agent.key } : null;
72928
- }
72929
- const alias = KNOWN_AGENT_ALIASES[target.toLowerCase()];
72930
- if (alias) return alias;
72931
- if (target.startsWith("agent-") || target.startsWith("agent-pub-")) {
72932
- return { name: target, key: target };
72933
- }
72934
- return null;
74579
+ function applyAgentSwitch(state, target) {
74580
+ return {
74581
+ nextState: {
74582
+ ...state,
74583
+ agentName: target.name,
74584
+ agentKey: target.key
74585
+ },
74586
+ output: `Switched to ${target.name}. ${state.dialogId ? `Dialog kept: ${state.dialogId}` : "Dialog kept: new"}`
74587
+ };
72935
74588
  }
72936
74589
  var DIALOG_ID_PATTERN = /[0-9A-HJKMNP-TV-Z]{26}/i;
72937
74590
  var DIALOG_KEY_PATTERN = /dialog-[^\s"'<>]+-[0-9A-HJKMNP-TV-Z]{26}/i;
@@ -73109,6 +74762,66 @@ function handleTuiInput(input2, state) {
73109
74762
  output: `Runtime: ${argText}`
73110
74763
  };
73111
74764
  }
74765
+ case "/tools": {
74766
+ if (!argText) {
74767
+ return {
74768
+ nextState: state,
74769
+ output: `Tool display: ${state.toolDisplay} (hide | compact | verbose)`
74770
+ };
74771
+ }
74772
+ const normalizedArg = argText.trim().toLowerCase();
74773
+ if (!["hide", "compact", "verbose", "on", "off"].includes(normalizedArg)) {
74774
+ return {
74775
+ nextState: state,
74776
+ output: "Usage: /tools <hide|compact|verbose>"
74777
+ };
74778
+ }
74779
+ const nextMode = normalizeToolDisplayMode(normalizedArg, state.toolDisplay);
74780
+ return {
74781
+ nextState: { ...state, toolDisplay: nextMode },
74782
+ output: `Tool display: ${nextMode}`
74783
+ };
74784
+ }
74785
+ case "/thinking": {
74786
+ if (!argText) {
74787
+ return {
74788
+ nextState: state,
74789
+ output: `Thinking display: ${state.thinkingDisplay} (hide | marker | show)`
74790
+ };
74791
+ }
74792
+ const normalizedArg = argText.trim().toLowerCase();
74793
+ if (!["hide", "marker", "show", "on", "off"].includes(normalizedArg)) {
74794
+ return {
74795
+ nextState: state,
74796
+ output: "Usage: /thinking <hide|marker|show>"
74797
+ };
74798
+ }
74799
+ const nextMode = normalizeThinkingDisplayMode(normalizedArg, state.thinkingDisplay);
74800
+ return {
74801
+ nextState: { ...state, thinkingDisplay: nextMode },
74802
+ output: `Thinking display: ${nextMode}`
74803
+ };
74804
+ }
74805
+ case "/render": {
74806
+ if (!argText) {
74807
+ return {
74808
+ nextState: state,
74809
+ output: `Render display: ${state.renderDisplay} (plain | rich)`
74810
+ };
74811
+ }
74812
+ const normalizedArg = argText.trim().toLowerCase();
74813
+ if (!["plain", "rich", "on", "off"].includes(normalizedArg)) {
74814
+ return {
74815
+ nextState: state,
74816
+ output: "Usage: /render <plain|rich>"
74817
+ };
74818
+ }
74819
+ const nextMode = normalizeRenderDisplayMode(normalizedArg, state.renderDisplay);
74820
+ return {
74821
+ nextState: { ...state, renderDisplay: nextMode },
74822
+ output: `Render display: ${nextMode}`
74823
+ };
74824
+ }
73112
74825
  case "/exit":
73113
74826
  case "/quit":
73114
74827
  return { nextState: state, output: "Bye.", action: { type: "exit" } };
@@ -73118,7 +74831,8 @@ function handleTuiInput(input2, state) {
73118
74831
  ...state,
73119
74832
  dialogId: void 0,
73120
74833
  dialogLabel: "new",
73121
- attachedDocs: []
74834
+ attachedDocs: [],
74835
+ turnTokens: void 0
73122
74836
  },
73123
74837
  output: "Started a fresh dialog."
73124
74838
  };
@@ -73142,11 +74856,37 @@ ${renderTuiHelp()}`
73142
74856
  output: "Compacting current dialog...",
73143
74857
  action: { type: "compact", dialogId: state.dialogId }
73144
74858
  };
73145
- case "/agent":
73146
- return {
73147
- nextState: state,
73148
- output: `Current agent: ${state.agentName} (${state.agentKey})`
73149
- };
74859
+ case "/agent": {
74860
+ if (!argText) {
74861
+ return {
74862
+ nextState: state,
74863
+ output: "",
74864
+ action: { type: "pick-agent" }
74865
+ };
74866
+ }
74867
+ if (argText === "list") {
74868
+ return {
74869
+ nextState: state,
74870
+ output: "",
74871
+ action: { type: "list-agents" }
74872
+ };
74873
+ }
74874
+ if (argText === "current" || argText === "show") {
74875
+ return {
74876
+ nextState: state,
74877
+ output: `Current agent: ${state.agentName} (${state.agentKey})`
74878
+ };
74879
+ }
74880
+ const resolvedTarget = resolveAgentSwitchTarget(argText, PLATFORM_AGENTS);
74881
+ if (!resolvedTarget) {
74882
+ return {
74883
+ nextState: state,
74884
+ output: `I don't know agent "${argText}" yet.
74885
+ Use /agent, /agent list, /agent minimax-m3, or a full agent key.`
74886
+ };
74887
+ }
74888
+ return applyAgentSwitch(state, resolvedTarget);
74889
+ }
73150
74890
  case "/agents":
73151
74891
  return {
73152
74892
  nextState: state,
@@ -73156,25 +74896,18 @@ ${renderTuiHelp()}`
73156
74896
  if (!argText) {
73157
74897
  return {
73158
74898
  nextState: state,
73159
- output: "Usage: /switch <agent-key|alias>"
74899
+ output: "Usage: /switch <agent-key|alias> (or run /agent)"
73160
74900
  };
73161
74901
  }
73162
- const resolvedTarget = resolveSwitchTarget(argText);
74902
+ const resolvedTarget = resolveAgentSwitchTarget(argText, PLATFORM_AGENTS);
73163
74903
  if (!resolvedTarget) {
73164
74904
  return {
73165
74905
  nextState: state,
73166
74906
  output: `I don't know agent shortcut "${argText}" yet.
73167
- Use /switch nolo, /switch app-builder, or a full agent key like agent-pub-...`
74907
+ Use /agent, /switch nolo, /switch minimax-m3, or a full agent key.`
73168
74908
  };
73169
74909
  }
73170
- return {
73171
- nextState: {
73172
- ...state,
73173
- agentName: resolvedTarget.name,
73174
- agentKey: resolvedTarget.key
73175
- },
73176
- output: `Switched to ${resolvedTarget.name}.`
73177
- };
74910
+ return applyAgentSwitch(state, resolvedTarget);
73178
74911
  }
73179
74912
  case "/dialog":
73180
74913
  return {
@@ -73245,8 +74978,14 @@ async function runAgentChat(scriptDir, state, message, env, output2, agentRunner
73245
74978
  message,
73246
74979
  continueDialogId: state.dialogId,
73247
74980
  runtimeMode: state.runtimeMode,
74981
+ localRuntimeCwd: process.cwd(),
73248
74982
  scriptDir,
73249
- env,
74983
+ env: {
74984
+ ...env,
74985
+ NOLO_CLI_THINKING: state.thinkingDisplay,
74986
+ NOLO_CLI_TOOLS: state.toolDisplay,
74987
+ NOLO_CLI_RENDER: state.renderDisplay
74988
+ },
73250
74989
  output: output2
73251
74990
  });
73252
74991
  return result;
@@ -73257,7 +74996,7 @@ async function pipeReadableToOutput(stream, output2) {
73257
74996
  }
73258
74997
  function resolveDefaultCliEntrypoint(scriptDir) {
73259
74998
  if (process.argv[1]) return process.argv[1];
73260
- return join11(scriptDir, "..", "packages", "cli", "index.ts");
74999
+ return join12(scriptDir, "..", "packages", "cli", "index.ts");
73261
75000
  }
73262
75001
  async function runCliCommandInChildProcess(args2, context) {
73263
75002
  const proc = spawnProcess({
@@ -73272,6 +75011,19 @@ async function runCliCommandInChildProcess(args2, context) {
73272
75011
  ]);
73273
75012
  return proc.exited;
73274
75013
  }
75014
+ function persistAgentSelection(state, env) {
75015
+ try {
75016
+ saveProfileAgentSelection({
75017
+ agentKey: state.agentKey,
75018
+ agentName: state.agentName
75019
+ });
75020
+ } catch {
75021
+ }
75022
+ if (env) {
75023
+ env.NOLO_AGENT = state.agentKey;
75024
+ env.NOLO_AGENT_NAME = state.agentName;
75025
+ }
75026
+ }
73275
75027
  async function startTuiWorkspace(options) {
73276
75028
  let state = createInitialTuiState(options.env ?? process.env);
73277
75029
  const input2 = options.input ?? defaultInput;
@@ -73285,12 +75037,19 @@ async function startTuiWorkspace(options) {
73285
75037
  rl.prompt();
73286
75038
  try {
73287
75039
  for await (const line of rl) {
75040
+ if (!line.trim()) {
75041
+ continue;
75042
+ }
73288
75043
  const result = handleTuiInput(line, state);
75044
+ const previousAgentKey = state.agentKey;
73289
75045
  state = result.nextState;
73290
75046
  if (result.output) {
73291
75047
  output2.write(`${result.output}
73292
75048
  `);
73293
75049
  }
75050
+ if (state.agentKey !== previousAgentKey && result.output?.startsWith("Switched to ")) {
75051
+ persistAgentSelection(state, options.env ?? process.env);
75052
+ }
73294
75053
  if (result.action?.type === "exit") {
73295
75054
  break;
73296
75055
  }
@@ -73321,12 +75080,70 @@ async function startTuiWorkspace(options) {
73321
75080
  if (exitCode2 === 0) {
73322
75081
  output2.write("Update finished. Restart nolo to use the new version.\n");
73323
75082
  } else {
73324
- output2.write("Update failed. Fix the npm error above, then run /update again or use nolo update.\n");
75083
+ output2.write("Update failed. Check the error above, then run /update again or use nolo update.\n");
73325
75084
  }
73326
75085
  } catch (error) {
73327
75086
  output2.write(`${error instanceof Error ? error.message : String(error)}
73328
75087
  `);
73329
- output2.write("Update failed. Fix the npm error above, then run /update again or use nolo update.\n");
75088
+ output2.write("Update failed. Check the error above, then run /update again or use nolo update.\n");
75089
+ }
75090
+ }
75091
+ if (result.action?.type === "pick-agent") {
75092
+ rl.pause();
75093
+ try {
75094
+ const pickResult = await runAgentPicker({
75095
+ currentKey: state.agentKey,
75096
+ env: options.env ?? process.env,
75097
+ input: input2,
75098
+ output: output2
75099
+ });
75100
+ if (pickResult.kind === "list") {
75101
+ output2.write(`${pickResult.output}
75102
+ `);
75103
+ } else if (pickResult.kind === "selected") {
75104
+ state = {
75105
+ ...state,
75106
+ agentName: pickResult.name,
75107
+ agentKey: pickResult.key
75108
+ };
75109
+ persistAgentSelection(state, options.env ?? process.env);
75110
+ output2.write(
75111
+ `${formatAgentSwitchMessage({
75112
+ name: pickResult.name,
75113
+ dialogId: state.dialogId
75114
+ })}
75115
+ `
75116
+ );
75117
+ } else {
75118
+ output2.write("Agent switch cancelled.\n");
75119
+ }
75120
+ } catch (error) {
75121
+ output2.write(
75122
+ `[nolo] Agent picker failed: ${error instanceof Error ? error.message : String(error)}
75123
+ `
75124
+ );
75125
+ } finally {
75126
+ rl.resume();
75127
+ }
75128
+ }
75129
+ if (result.action?.type === "list-agents") {
75130
+ try {
75131
+ const pickResult = await runAgentPicker({
75132
+ currentKey: state.agentKey,
75133
+ env: options.env ?? process.env,
75134
+ input: input2,
75135
+ output: output2,
75136
+ interactive: false
75137
+ });
75138
+ if (pickResult.kind === "list") {
75139
+ output2.write(`${pickResult.output}
75140
+ `);
75141
+ }
75142
+ } catch (error) {
75143
+ output2.write(
75144
+ `[nolo] Agent list failed: ${error instanceof Error ? error.message : String(error)}
75145
+ `
75146
+ );
73330
75147
  }
73331
75148
  }
73332
75149
  if (result.action?.type === "cli-command") {
@@ -73357,11 +75174,14 @@ async function startTuiWorkspace(options) {
73357
75174
  output2,
73358
75175
  options.agentRunner
73359
75176
  );
73360
- if (runResult.dialogId) {
75177
+ if (runResult.dialogId || runResult.turnTokens) {
73361
75178
  state = {
73362
75179
  ...state,
73363
- dialogId: runResult.dialogId,
73364
- dialogLabel: runResult.dialogId
75180
+ ...runResult.dialogId ? {
75181
+ dialogId: runResult.dialogId,
75182
+ dialogLabel: runResult.dialogId
75183
+ } : {},
75184
+ ...runResult.turnTokens ? { turnTokens: runResult.turnTokens } : {}
73365
75185
  };
73366
75186
  }
73367
75187
  }
@@ -73377,12 +75197,12 @@ ${renderStatusLine(state)}
73377
75197
  }
73378
75198
 
73379
75199
  // packages/cli/index.ts
73380
- var CLI_DIR3 = dirname8(fileURLToPath5(import.meta.url));
73381
- var ROOT_DIR = join12(CLI_DIR3, "..", "..");
73382
- var SCRIPT_DIR = join12(ROOT_DIR, "scripts");
75200
+ var CLI_DIR3 = dirname9(fileURLToPath6(import.meta.url));
75201
+ var ROOT_DIR = join13(CLI_DIR3, "..", "..");
75202
+ var SCRIPT_DIR = join13(ROOT_DIR, "scripts");
73383
75203
  var packageInfo = readPackageInfo();
73384
75204
  async function runScript(script, forwardedArgs, env) {
73385
- const scriptPath = join12(SCRIPT_DIR, script);
75205
+ const scriptPath = join13(SCRIPT_DIR, script);
73386
75206
  const proc = spawnProcess({
73387
75207
  cmd: [process.execPath, scriptPath, ...forwardedArgs],
73388
75208
  stdin: "inherit",
@@ -73400,7 +75220,7 @@ var runtimeEnv = {
73400
75220
  var runtimeContext = createCliRuntimeContext({
73401
75221
  env: runtimeEnv,
73402
75222
  scriptDir: SCRIPT_DIR,
73403
- entrypointPath: fileURLToPath5(import.meta.url),
75223
+ entrypointPath: fileURLToPath6(import.meta.url),
73404
75224
  packageInfo
73405
75225
  });
73406
75226
  if (args.length === 0) {