linkshell-cli 0.3.10 → 0.3.12

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.
@@ -10,6 +10,8 @@ import { listCodexStoredSessions, loadCodexStoredTimeline } from "./codex-sessio
10
10
  import { resolveAgentCommand } from "./provider-resolver.js";
11
11
  const PERMISSION_TIMEOUT_MS = 5 * 60_000;
12
12
  const MAX_TIMELINE_ITEMS = 200;
13
+ const MAX_SNAPSHOT_ITEMS = 80;
14
+ const MAX_SNAPSHOT_TEXT_BYTES = 128 * 1024;
13
15
  function id(prefix) {
14
16
  return `${prefix}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
15
17
  }
@@ -23,6 +25,62 @@ function stringify(value) {
23
25
  return String(value);
24
26
  }
25
27
  }
28
+ function truncateUtf8(value, maxBytes = MAX_SNAPSHOT_TEXT_BYTES) {
29
+ if (!value)
30
+ return value;
31
+ if (Buffer.byteLength(value, "utf8") <= maxBytes)
32
+ return value;
33
+ let end = Math.min(value.length, maxBytes);
34
+ while (end > 0 && Buffer.byteLength(value.slice(0, end), "utf8") > maxBytes) {
35
+ end = Math.floor(end * 0.9);
36
+ }
37
+ return `${value.slice(0, end)}\n\n[truncated by LinkShell: original ${Buffer.byteLength(value, "utf8")} bytes]`;
38
+ }
39
+ function snapshotContentBlocks(blocks, options = {}) {
40
+ if (!blocks)
41
+ return undefined;
42
+ return blocks.map((block) => block.type === "image" && options.stripImages !== false
43
+ ? { ...block, data: undefined, text: block.text || "图片附件" }
44
+ : { ...block, text: truncateUtf8(block.text) });
45
+ }
46
+ function snapshotTimelineItem(item, options = {}) {
47
+ return {
48
+ ...item,
49
+ content: snapshotContentBlocks(item.content, options),
50
+ text: truncateUtf8(item.text),
51
+ toolCall: item.toolCall
52
+ ? {
53
+ ...item.toolCall,
54
+ input: truncateUtf8(item.toolCall.input),
55
+ output: truncateUtf8(item.toolCall.output),
56
+ }
57
+ : undefined,
58
+ commandExecution: item.commandExecution
59
+ ? {
60
+ ...item.commandExecution,
61
+ command: truncateUtf8(item.commandExecution.command, 16 * 1024),
62
+ output: truncateUtf8(item.commandExecution.output),
63
+ }
64
+ : undefined,
65
+ fileChange: item.fileChange
66
+ ? {
67
+ ...item.fileChange,
68
+ diff: truncateUtf8(item.fileChange.diff),
69
+ summary: truncateUtf8(item.fileChange.summary),
70
+ }
71
+ : undefined,
72
+ permission: item.permission
73
+ ? {
74
+ ...item.permission,
75
+ toolInput: truncateUtf8(item.permission.toolInput),
76
+ context: truncateUtf8(item.permission.context),
77
+ }
78
+ : undefined,
79
+ };
80
+ }
81
+ function snapshotTimelineItems(items) {
82
+ return items.slice(-MAX_SNAPSHOT_ITEMS).map((item) => snapshotTimelineItem(item));
83
+ }
26
84
  function asRecord(value) {
27
85
  return typeof value === "object" && value ? value : undefined;
28
86
  }
@@ -556,6 +614,8 @@ function providerLabel(provider) {
556
614
  return "Custom";
557
615
  }
558
616
  const ALL_REASONING_EFFORTS = ["none", "minimal", "low", "medium", "high", "xhigh"];
617
+ const CLAUDE_REASONING_EFFORTS = ["low", "medium", "high", "xhigh"];
618
+ const AGENT_PERMISSION_MODES = ["read_only", "workspace_write", "full_access"];
559
619
  const CLAUDE_REMOTE_HIDDEN_COMMANDS = new Set([
560
620
  "add-dir",
561
621
  "agents",
@@ -1236,7 +1296,7 @@ export class AgentWorkspaceProxy {
1236
1296
  const supportsImages = enabled && protocolSupportsImages(protocol);
1237
1297
  const isClaudeFallback = protocol === "claude-stream-json";
1238
1298
  const supportsPermission = enabled && !isClaudeFallback;
1239
- const supportsReasoningEffort = enabled && !isClaudeFallback;
1299
+ const supportsReasoningEffort = enabled;
1240
1300
  const commands = mergeCommands(defaultProviderCommands(provider, this.input.cwd, enabled), runtimeCapabilities?.commands);
1241
1301
  const currentMode = [...this.conversations.values()].find((conversation) => conversation.provider === provider)?.collaborationMode;
1242
1302
  return {
@@ -1248,12 +1308,12 @@ export class AgentWorkspaceProxy {
1248
1308
  supportsPermission,
1249
1309
  supportsPlan: enabled,
1250
1310
  supportsCancel: enabled,
1251
- models: runtimeCapabilities?.models,
1311
+ models: runtimeCapabilities?.models ?? [{ id: "default", label: "默认模型" }],
1252
1312
  defaultModel: runtimeCapabilities?.defaultModel,
1253
1313
  reasoningEfforts: supportsReasoningEffort
1254
- ? runtimeCapabilities?.reasoningEfforts ?? []
1314
+ ? runtimeCapabilities?.reasoningEfforts ?? (provider === "claude" ? [...CLAUDE_REASONING_EFFORTS] : [...ALL_REASONING_EFFORTS])
1255
1315
  : [],
1256
- permissionModes: [],
1316
+ permissionModes: supportsPermission ? AGENT_PERMISSION_MODES : [],
1257
1317
  commands,
1258
1318
  modes: runtimeCapabilities?.modes ?? [],
1259
1319
  currentMode,
@@ -1306,7 +1366,7 @@ export class AgentWorkspaceProxy {
1306
1366
  hostDeviceId: this.input.hostDeviceId,
1307
1367
  payload: {
1308
1368
  conversation: existingConversation,
1309
- snapshot: this.timelines.get(existingConversation.id) ?? [],
1369
+ snapshot: snapshotTimelineItems(this.timelines.get(existingConversation.id) ?? []),
1310
1370
  },
1311
1371
  }));
1312
1372
  return existingConversation;
@@ -1353,7 +1413,7 @@ export class AgentWorkspaceProxy {
1353
1413
  this.input.send(createEnvelope({
1354
1414
  type: "agent.v2.conversation.opened",
1355
1415
  hostDeviceId: this.input.hostDeviceId,
1356
- payload: { conversation, snapshot: this.timelines.get(conversation.id) ?? [] },
1416
+ payload: { conversation, snapshot: snapshotTimelineItems(this.timelines.get(conversation.id) ?? []) },
1357
1417
  }));
1358
1418
  return conversation;
1359
1419
  }
@@ -1392,7 +1452,7 @@ export class AgentWorkspaceProxy {
1392
1452
  this.input.send(createEnvelope({
1393
1453
  type: "agent.v2.conversation.opened",
1394
1454
  hostDeviceId: this.input.hostDeviceId,
1395
- payload: { conversation, snapshot: this.timelines.get(conversation.id) ?? [] },
1455
+ payload: { conversation, snapshot: snapshotTimelineItems(this.timelines.get(conversation.id) ?? []) },
1396
1456
  }));
1397
1457
  return conversation;
1398
1458
  }
@@ -1436,10 +1496,18 @@ export class AgentWorkspaceProxy {
1436
1496
  });
1437
1497
  return;
1438
1498
  }
1439
- conversation.model = payload.model ?? conversation.model;
1440
- conversation.reasoningEffort = payload.reasoningEffort ?? conversation.reasoningEffort;
1441
- conversation.permissionMode = payload.permissionMode ?? conversation.permissionMode;
1442
- conversation.collaborationMode = payload.collaborationMode ?? conversation.collaborationMode;
1499
+ if (Object.prototype.hasOwnProperty.call(payload, "model")) {
1500
+ conversation.model = payload.model ?? undefined;
1501
+ }
1502
+ if (Object.prototype.hasOwnProperty.call(payload, "reasoningEffort")) {
1503
+ conversation.reasoningEffort = payload.reasoningEffort ?? undefined;
1504
+ }
1505
+ if (Object.prototype.hasOwnProperty.call(payload, "permissionMode")) {
1506
+ conversation.permissionMode = payload.permissionMode ?? undefined;
1507
+ }
1508
+ if (Object.prototype.hasOwnProperty.call(payload, "collaborationMode")) {
1509
+ conversation.collaborationMode = payload.collaborationMode ?? "default";
1510
+ }
1443
1511
  conversation.status = "running";
1444
1512
  conversation.lastActivityAt = Date.now();
1445
1513
  this.activeConversationId = conversation.id;
@@ -1459,10 +1527,10 @@ export class AgentWorkspaceProxy {
1459
1527
  sessionId: conversation.agentSessionId,
1460
1528
  content: payload.contentBlocks,
1461
1529
  clientMessageId: payload.clientMessageId,
1462
- model: payload.model,
1463
- reasoningEffort: payload.reasoningEffort,
1464
- permissionMode: payload.permissionMode,
1465
- collaborationMode: payload.collaborationMode ?? conversation.collaborationMode,
1530
+ model: conversation.model,
1531
+ reasoningEffort: conversation.reasoningEffort,
1532
+ permissionMode: conversation.permissionMode,
1533
+ collaborationMode: conversation.collaborationMode,
1466
1534
  cwd: conversation.cwd,
1467
1535
  });
1468
1536
  const nextAgentSessionId = this.extractSessionId(result);
@@ -2540,7 +2608,7 @@ export class AgentWorkspaceProxy {
2540
2608
  this.input.send(createEnvelope({
2541
2609
  type: "agent.v2.event",
2542
2610
  hostDeviceId: this.input.hostDeviceId,
2543
- payload: { conversationId, conversation, item },
2611
+ payload: { conversationId, conversation, item: snapshotTimelineItem(item, { stripImages: false }) },
2544
2612
  }));
2545
2613
  }
2546
2614
  emitConversation(conversation) {
@@ -2593,8 +2661,8 @@ export class AgentWorkspaceProxy {
2593
2661
  }
2594
2662
  const conversations = [...this.conversations.values()];
2595
2663
  const items = conversationId
2596
- ? this.timelines.get(conversationId) ?? []
2597
- : [...this.timelines.values()].flat();
2664
+ ? snapshotTimelineItems(this.timelines.get(conversationId) ?? [])
2665
+ : [];
2598
2666
  this.input.send(createEnvelope({
2599
2667
  type: "agent.v2.snapshot",
2600
2668
  hostDeviceId: this.input.hostDeviceId,