@yu_robotics/remote-cli 1.6.166 → 1.6.169

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.
package/README.md CHANGED
@@ -20,6 +20,8 @@ Router 1.6.117 and newer explicitly label standalone background-task notificatio
20
20
 
21
21
  ## Public Activity
22
22
 
23
+ From Router 1.6.169, reply-card thread lists start collapsed. Use **Show threads** to expand the list in the same card, or **Switch to this thread** to select the thread that produced the reply. Opening or hiding the list does not change the input target; a successful switch hides the list and confirms the new target without changing the reply's source label. No separate thread-management card is sent. The recursive element budget is 180 instead of 150, with room reserved for the expanded controls; table and size thresholds are unchanged. This presentation update requires the newer Router, not a new CLI protocol capability.
24
+
23
25
  From Router 1.6.158, completion pagination keeps the final body item (or complete worker group) with the completion note, optional model metadata and thread controls when they fit together. This avoids preventable footer-only continuations, including replies without model metadata. Indivisible oversized groups still split according to node, size and table limits; thread-button refreshes preserve the final body. This does not change streaming completion timing or the wire protocol.
24
26
 
25
27
  From CLI 1.6.153, Codex shell calls no longer lose their label when native command actions are absent or unknown. Native reads, searches, and directory listings remain preferred. Other calls use fixed operation labels such as `Run npm command` or `Run Git command` for simple allowlisted programs, and `Run shell command` for compound or unrecognized commands. These are deterministic operation labels, not model-authored purpose summaries: installed Codex 0.160.0 command items have no per-call description field. Arguments, full paths, output, and execution-wide reasoning are never copied into these fallback labels. Late native action metadata can enrich a pending row before its first result; repeated results retain one row. Existing description fields, heading limits, and card-node counts are unchanged; upgrade the CLI, with no new Router capability required.
@@ -176,7 +178,7 @@ Linux users upgrading from version 1.6.23 or earlier should run `remote-cli serv
176
178
  | `/clear` | Clear context for this thread |
177
179
  | `/new` | Alias for `/clear`; start a fresh conversation in this thread |
178
180
  | `/compact` | Compress history to save tokens |
179
- | `/cd <dir>` | Change directory; a different directory starts fresh conversations for this thread |
181
+ | `/cd [dir]` | Browse directories as a card, or change directory directly with a path |
180
182
  | `/model [name]` | Open native model settings, or set the active backend model with a text argument |
181
183
  | `/effort [auto|level]` | Open native effort settings, or set/clear the active backend effort with a text argument |
182
184
  | `/sandbox [on/off/read-only/default]` | Configure the current Codex or Claude Code thread sandbox, directory grants, and networking |
@@ -244,6 +246,15 @@ bound retained delegation results, not backend output buffers. Worker timeouts,
244
246
  cancellation, and cleanup still apply. This requires a local CLI upgrade and
245
247
  works with existing Routers.
246
248
 
249
+ With CLI and Router 1.6.167 or newer, terminal Worker card excerpts retain up to
250
+ 4 KiB of UTF-8 text. Excerpts containing standalone omission markers are shown
251
+ as escaped plain text with a separate explanation rather than interpreting
252
+ contextless Markdown fragments. Intact result Markdown is preserved. Upgrade
253
+ both peers for the larger preview; newer Routers also safely display older
254
+ CLI omission markers. Older or unknown Routers keep the legacy 1000-byte
255
+ head/tail summary, including after reconnect. The 32 KiB result and 64 KiB
256
+ continuation limits stay unchanged.
257
+
247
258
  Child progress, approvals, and questions use the existing reply flow. `/abort`
248
259
  stops managed children as well as the parent. While delegated results remain
249
260
  missing from the active execution, the CLI suppresses coordinator prose/plans/images and displays
@@ -416,6 +427,8 @@ Backend-specific commands are forwarded only where the active backend supports t
416
427
 
417
428
  Use one thread per project or task so each thread keeps its own working directory, backend session, model, and queue. Use `/backend <index>` for a global switch, or `/backend <index> @` to override only the current thread. `/backend default @` removes that override.
418
429
 
430
+ With CLI and Router 1.6.168 or newer, `/cd` opens a directory-only browser with child, Parent, Allowed roots, Refresh, Use this directory, and Cancel controls. Navigation never changes context or runs a backend. Only Apply changes the original thread, following the existing all-backend context reset and delegation-lane invalidation; the same directory remains a no-op. Both card Apply and text `/cd <path>` share exclusive settings admission and reject active, delegated, queued, aborting, or updating work. Older peers retain text behavior. Symbolic-link directories are omitted; Parent stops at the nearest allowed root. Snapshots expire after ten minutes and reject changed policy, cwd, root, directory, or ancestor identity. Scans cap at 4096 entries, 128 directories, or five seconds between filesystem operations, with explicit partial-list guidance; at most 32 roots are offered. Cards fill before budget-driven paging. Application-level canonical and identity checks are not an OS sandbox or protection against all external filesystem races and bind mounts.
431
+
419
432
  With CLI 1.6.93 or newer, `/cd` to a different normalized directory clears saved conversation bindings for every backend in that thread, including inactive ones. Returning to the old directory does not restore its previous conversation. Selecting the same directory preserves context; backend switching without a directory change still resumes conversations. Thread identity, model, effort, sandbox grants, delegation settings, and native history files remain intact. Separate plans should use separate threads, even when they share a directory. This change requires a CLI upgrade and works with existing Routers.
420
433
 
421
434
  Thread switch panels show `Reply from:` with the reply's full thread name and workspace. Under `Switch thread`, a check mark (`✓`) and primary button styling identify the destination of new top-level messages when the card is finalized or clicked. Selected buttons remain clickable. Switching updates only the clicked card; other historical cards retain their last displayed selection.
@@ -38,10 +38,13 @@ export declare class MessageHandler {
38
38
  private approvalCardsSupported;
39
39
  private streamingContextSupported;
40
40
  private delegationProgressSupported;
41
+ private routerVersion?;
41
42
  private delegationProgressTextSupported;
42
43
  private activityProgressSupported;
43
44
  private workerContextResetSupported;
44
45
  private settingsCardsSupported;
46
+ private directoryCardsSupported;
47
+ private readonly directoryBrowser;
45
48
  private delegationCardsSupported;
46
49
  private readonly settingsAdmission;
47
50
  private readonly settingsCatalogReader;
@@ -183,6 +186,8 @@ export declare class MessageHandler {
183
186
  private sendStructuredContent;
184
187
  private sendResponse;
185
188
  private executorConfiguration;
189
+ /** Shared card/text Apply path. Callers hold exclusive settings admission. */
190
+ private changeWorkingDirectory;
186
191
  private settingsState;
187
192
  private settingsBusy;
188
193
  private settingsFallback;
@@ -1 +1 @@
1
- {"version":3,"file":"MessageHandler.d.ts","sourceRoot":"","sources":["../../src/client/MessageHandler.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"MessageHandler.d.ts","sourceRoot":"","sources":["../../src/client/MessageHandler.ts"],"names":[],"mappings":"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"}
@@ -37,6 +37,7 @@ const ActivityProgress_1 = require("../utils/ActivityProgress");
37
37
  const SettingsAdmission_1 = require("../settings/SettingsAdmission");
38
38
  const SettingsCatalog_1 = require("../settings/SettingsCatalog");
39
39
  const SettingsService_1 = require("../settings/SettingsService");
40
+ const DirectoryBrowser_1 = require("../settings/DirectoryBrowser");
40
41
  const SessionResumeService_1 = require("../session/SessionResumeService");
41
42
  const adapters_1 = require("../session/adapters");
42
43
  const PiSessionCatalog_1 = require("../session/adapters/PiSessionCatalog");
@@ -170,10 +171,13 @@ class MessageHandler {
170
171
  approvalCardsSupported = false;
171
172
  streamingContextSupported = false;
172
173
  delegationProgressSupported = false;
174
+ routerVersion;
173
175
  delegationProgressTextSupported = false;
174
176
  activityProgressSupported = false;
175
177
  workerContextResetSupported = false;
176
178
  settingsCardsSupported = false;
179
+ directoryCardsSupported = false;
180
+ directoryBrowser;
177
181
  delegationCardsSupported = false;
178
182
  settingsAdmission = new SettingsAdmission_1.SettingsAdmission();
179
183
  settingsCatalogReader;
@@ -188,7 +192,10 @@ class MessageHandler {
188
192
  this.threadManager = threadManager;
189
193
  this.directoryGuard = directoryGuard;
190
194
  this.config = config;
191
- this.wsClient.onClose?.(() => this.fileInbox?.disconnect());
195
+ this.wsClient.onClose?.(() => {
196
+ this.routerVersion = undefined;
197
+ this.fileInbox?.disconnect();
198
+ });
192
199
  this.delegation = new DelegationManager_1.DelegationManager(directoryGuard);
193
200
  this.settingsCatalogReader = new SettingsCatalog_1.SettingsCatalogReader(directoryGuard);
194
201
  this.settingsService = new SettingsService_1.SettingsService({
@@ -228,7 +235,26 @@ class MessageHandler {
228
235
  delegation: (id, enabled) => this.applyDelegationSetting(id, enabled),
229
236
  delegationSupported: id => Boolean(this.threadPool.getExecutor(id).configureDelegation),
230
237
  });
231
- this.wsClient.onClose?.(() => this.settingsService.invalidate());
238
+ this.directoryBrowser = new DirectoryBrowser_1.DirectoryBrowser({
239
+ guard: this.directoryGuard,
240
+ state: id => {
241
+ const thread = this.threadManager.getThread(id);
242
+ const state = this.settingsState(id);
243
+ return thread && state ? { threadName: thread.name, cwd: thread.workingDirectory,
244
+ revision: state.revision, backend: state.coordinatorBackend } : undefined;
245
+ },
246
+ busy: (id, except) => this.settingsBusy(id, except),
247
+ exclusive: async (id, body) => {
248
+ try {
249
+ return await this.settingsAdmission.mutation(id, body);
250
+ }
251
+ finally {
252
+ void this.startNextQueuedCommand(id);
253
+ }
254
+ },
255
+ apply: (id, target) => this.changeWorkingDirectory(id, target),
256
+ });
257
+ this.wsClient.onClose?.(() => { this.settingsService.invalidate(); this.directoryCardsSupported = false; this.directoryBrowser.invalidate(); });
232
258
  this.notificationAdapter = new hooks_1.FeishuNotificationAdapter(wsClient);
233
259
  this.notificationAdapter.setThreadNameResolver((threadId) => this.threadManager.getThread(threadId)?.name);
234
260
  this.notificationAdapter.register();
@@ -267,7 +293,12 @@ class MessageHandler {
267
293
  return;
268
294
  case 'settings_action':
269
295
  if (this.settingsCardsSupported) {
270
- const result = await this.settingsService.action(message, this.delegationCardsSupported);
296
+ const request = message;
297
+ const directory = typeof request.snapshotId === 'string' && request.snapshotId.startsWith('directory-');
298
+ if (directory && !this.directoryCardsSupported)
299
+ return;
300
+ const result = directory ? await this.directoryBrowser.action(request)
301
+ : await this.settingsService.action(request, this.delegationCardsSupported);
271
302
  this.wsClient.send(result);
272
303
  }
273
304
  return;
@@ -290,6 +321,7 @@ class MessageHandler {
290
321
  const data = message.data;
291
322
  if (data?.success !== true)
292
323
  return;
324
+ this.routerVersion = typeof data.routerVersion === 'string' ? data.routerVersion : undefined;
293
325
  if (data.capabilities?.fileTransferV1 === true && !this.fileInbox) {
294
326
  try {
295
327
  const fileServer = this.config.get('serverUrl');
@@ -312,6 +344,7 @@ class MessageHandler {
312
344
  this.delegationProgressSupported = data.capabilities?.delegationProgress === true;
313
345
  this.workerContextResetSupported = this.delegationProgressSupported && data.capabilities?.workerContextReset === true;
314
346
  this.settingsCardsSupported = data.capabilities?.settingsCards === true;
347
+ this.directoryCardsSupported = this.settingsCardsSupported && data.capabilities?.directoryCards === true;
315
348
  this.delegationCardsSupported = this.settingsCardsSupported && data.capabilities?.delegationCards === true;
316
349
  this.delegationProgressTextSupported = this.delegationProgressSupported
317
350
  && data.capabilities?.delegationProgressText === true;
@@ -350,7 +383,8 @@ class MessageHandler {
350
383
  || this.pendingQueueConfirmations.size > 0
351
384
  || this.pendingReplaces.size > 0
352
385
  || this.activeThreadOperations.size > 0
353
- || this.abortOperations.size > 0) {
386
+ || this.abortOperations.size > 0
387
+ || this.threadManager.listThreads().some(thread => this.settingsAdmission.isBlocked(thread.id))) {
354
388
  return false;
355
389
  }
356
390
  this.automaticUpdateInProgress = true;
@@ -406,16 +440,18 @@ class MessageHandler {
406
440
  // Abort and addressed human replies must remain available during native work.
407
441
  if (content === '/abort' || /^(?:yes|no)(?:\s|$)/.test(content) || content.startsWith('/input '))
408
442
  return await body();
409
- if (/^\/(?:backend|model|effort)\s+\S/.test(content)) {
443
+ if (/^\/(?:backend|model|effort|cd)\s+\S/.test(content)) {
410
444
  const global = /^\/backend\s/.test(content) && !/\s@$/.test(content);
411
445
  await this.settingsAdmission.mutation(global ? '*' : threadId, async () => {
412
446
  this.settingsService.invalidate(global ? undefined : threadId);
447
+ if (/^\/cd\s/.test(content))
448
+ this.directoryBrowser.invalidate(threadId);
413
449
  await body();
414
450
  });
415
451
  }
416
452
  else {
417
453
  await this.settingsAdmission.command(threadId, message.messageId, async () => {
418
- if (/^\/(?:clear|new|cd|sandbox)(?:\s|$)/.test(content) || /^\/thread\s+delete(?:\s|$)/.test(content))
454
+ if (/^\/(?:clear|new|sandbox)(?:\s|$)/.test(content) || /^\/thread\s+delete(?:\s|$)/.test(content))
419
455
  this.settingsService.invalidate(threadId);
420
456
  await body();
421
457
  });
@@ -450,6 +486,31 @@ class MessageHandler {
450
486
  return;
451
487
  }
452
488
  const settingsCommand = content?.trim() ?? '';
489
+ if (this.directoryCardsSupported && openId && settingsCommand === '/cd') {
490
+ try {
491
+ const menu = await this.directoryBrowser.open(resolvedThreadId, openId, messageId);
492
+ this.sendResponse(messageId, resolvedThreadId, { success: true,
493
+ output: 'Directory browser. Navigation does not change the thread; choose Use this directory to apply. Text command: /cd <path>.', settingsMenu: menu }, true);
494
+ }
495
+ catch {
496
+ this.sendResponse(messageId, resolvedThreadId, { success: false,
497
+ error: 'The directory browser could not open. Check allowed directories and permissions, or use /cd <path>.' }, true);
498
+ }
499
+ return;
500
+ }
501
+ // Text Apply also runs before acquiring a model-turn busy flag. Its caller
502
+ // already holds the same exclusive admission writer as directory-card Apply.
503
+ if (/^\/cd\s+\S/.test(settingsCommand)) {
504
+ try {
505
+ const result = await this.changeWorkingDirectory(resolvedThreadId, settingsCommand.slice(3).trim());
506
+ this.sendResponse(messageId, resolvedThreadId, result, true);
507
+ }
508
+ catch (error) {
509
+ this.sendResponse(messageId, resolvedThreadId, { success: false,
510
+ error: error instanceof Error ? error.message : 'Failed to change directory. Check /status before retrying.' }, true);
511
+ }
512
+ return;
513
+ }
453
514
  if (this.settingsCardsSupported && openId && (/^\/(?:backend|model|effort)$/.test(settingsCommand)
454
515
  || (this.delegationCardsSupported && settingsCommand === '/delegation'))) {
455
516
  const kind = content.trim().slice(1);
@@ -1051,7 +1112,7 @@ You can also use natural language commands to control Claude Code CLI.`,
1051
1112
  await this.handleBackendCommand(messageId, threadId, trimmed);
1052
1113
  return true;
1053
1114
  }
1054
- if (trimmed.startsWith('/cd')) {
1115
+ if (trimmed === '/cd' || trimmed.startsWith('/cd ')) {
1055
1116
  const parts = trimmed.split(/\s+/);
1056
1117
  if (parts.length < 2) {
1057
1118
  this.sendResponse(messageId, threadId, { success: false, error: 'Usage: /cd <directory>' });
@@ -1059,18 +1120,7 @@ You can also use natural language commands to control Claude Code CLI.`,
1059
1120
  }
1060
1121
  const targetDir = parts.slice(1).join(' ');
1061
1122
  try {
1062
- if (this.delegation.hasActiveTasks(threadId)) {
1063
- throw new Error('Cannot change working directory while a delegated worker is running. Wait for it to finish or send /abort first.');
1064
- }
1065
- const generation = this.threadManager.getThread(threadId)?.delegationWorkspaceGeneration ?? 0;
1066
- const { cwd: newCwd, changed } = await this.threadPool.setWorkingDirectory(threadId, targetDir);
1067
- const cleanup = changed
1068
- ? await this.delegation.invalidateWorkspaceGeneration(threadId, generation + 1)
1069
- : { removed: 0, failed: 0 };
1070
- this.sendResponse(messageId, threadId, {
1071
- success: true,
1072
- output: `✅ Changed working directory to: ${newCwd}${changed ? '\nConversation context cleared for all direct and delegated backends in this thread.' : ''}${cleanup.failed ? `\n⚠️ ${cleanup.failed} delegated worker lane(s) could not be cleaned up. Retry with /delegation reset after checking filesystem permissions.` : ''}`,
1073
- });
1123
+ this.sendResponse(messageId, threadId, await this.changeWorkingDirectory(threadId, targetDir));
1074
1124
  }
1075
1125
  catch (error) {
1076
1126
  this.sendResponse(messageId, threadId, {
@@ -2044,6 +2094,7 @@ You can also use natural language commands to control Claude Code CLI.`,
2044
2094
  onToolUse: tool => this.sendToolUse(messageId, threadId, tool),
2045
2095
  onToolResult: result => this.sendToolResult(messageId, threadId, result),
2046
2096
  onNotice: text => this.sendStreamChunk(messageId, threadId, text),
2097
+ routerVersion: () => this.routerVersion,
2047
2098
  onProgress: this.delegationProgressSupported
2048
2099
  ? progress => this.sendDelegationProgress(messageId, threadId, progress)
2049
2100
  : undefined,
@@ -2630,7 +2681,8 @@ You can also use natural language commands to control Claude Code CLI.`,
2630
2681
  queueConfirmation: result.queueConfirmation,
2631
2682
  ...(result.executionMetadata ? { executionMetadata: result.executionMetadata } : {}),
2632
2683
  ...(this.settingsCardsSupported && result.settingsMenu
2633
- && (result.settingsMenu.kind !== 'delegation' || this.delegationCardsSupported) ? { settingsMenu: result.settingsMenu } : {}),
2684
+ && (result.settingsMenu.kind !== 'delegation' || this.delegationCardsSupported)
2685
+ && (result.settingsMenu.kind !== 'directory' || this.directoryCardsSupported) ? { settingsMenu: result.settingsMenu } : {}),
2634
2686
  cwd,
2635
2687
  timestamp: Date.now(),
2636
2688
  });
@@ -2644,6 +2696,22 @@ You can also use natural language commands to control Claude Code CLI.`,
2644
2696
  executorConfiguration() {
2645
2697
  return this.config.get('executor') ?? { type: 'auto' };
2646
2698
  }
2699
+ /** Shared card/text Apply path. Callers hold exclusive settings admission. */
2700
+ async changeWorkingDirectory(threadId, targetDir) {
2701
+ if (this.isDestroyed || this.automaticUpdateInProgress || this.abortOperations.has(threadId)
2702
+ || this.threadPool.isThreadBusy(threadId) || this.hasThreadQueueState(threadId)) {
2703
+ throw new Error('Cannot change working directory while the thread has active or queued work. Retry when it is idle.');
2704
+ }
2705
+ if (this.delegation.hasActiveTasks(threadId)) {
2706
+ throw new Error('Cannot change working directory while a delegated worker is running. Wait for it to finish or send /abort first.');
2707
+ }
2708
+ this.settingsService.invalidate(threadId);
2709
+ const generation = this.threadManager.getThread(threadId)?.delegationWorkspaceGeneration ?? 0;
2710
+ const { cwd, changed } = await this.threadPool.setWorkingDirectory(threadId, targetDir);
2711
+ const cleanup = changed ? await this.delegation.invalidateWorkspaceGeneration(threadId, generation + 1) : { removed: 0, failed: 0 };
2712
+ this.directoryBrowser.invalidate(threadId);
2713
+ return { success: true, output: `✅ Changed working directory to: ${cwd}${changed ? '\nConversation context cleared for all direct and delegated backends in this thread.' : ''}${cleanup.failed ? `\n⚠️ ${cleanup.failed} delegated worker lane(s) could not be cleaned up. Retry with /delegation reset after checking filesystem permissions.` : ''}` };
2714
+ }
2647
2715
  settingsState(threadId) {
2648
2716
  const thread = this.threadManager.getThread(threadId);
2649
2717
  if (!thread)
@@ -2926,6 +2994,7 @@ You can also use natural language commands to control Claude Code CLI.`,
2926
2994
  * Destroy handler and all executors
2927
2995
  */
2928
2996
  async destroy() {
2997
+ this.directoryBrowser.destroy();
2929
2998
  this.settingsService.destroy();
2930
2999
  this.isDestroyed = true;
2931
3000
  await this.fileInbox?.destroy();