lark-remote 0.5.12 → 0.5.14

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 (172) hide show
  1. package/CHANGELOG.md +28 -2
  2. package/README.en.md +1 -14
  3. package/README.md +1 -14
  4. package/dist/agent-kinds.d.ts +27 -0
  5. package/dist/agent-kinds.js +38 -0
  6. package/dist/bridge/approval-actions.d.ts +95 -0
  7. package/dist/bridge/approval-actions.js +104 -0
  8. package/dist/bridge/approval-coordinator.d.ts +1 -0
  9. package/dist/bridge/approval-coordinator.js +12 -24
  10. package/dist/bridge/final-usage.d.ts +8 -17
  11. package/dist/bridge/final-usage.js +1 -18
  12. package/dist/bridge/index.d.ts +52 -38
  13. package/dist/bridge/index.js +255 -433
  14. package/dist/bridge/queue-manager.d.ts +25 -1
  15. package/dist/bridge/queue-manager.js +179 -160
  16. package/dist/bridge/run-approval.d.ts +16 -0
  17. package/dist/bridge/run-approval.js +41 -0
  18. package/dist/bridge/run-usage.d.ts +32 -0
  19. package/dist/bridge/run-usage.js +87 -0
  20. package/dist/bridge/session-writeback.d.ts +19 -0
  21. package/dist/bridge/session-writeback.js +36 -0
  22. package/dist/card/approval-render.d.ts +3 -2
  23. package/dist/card/approval-render.js +3 -2
  24. package/dist/card/bash-card-session.js +2 -5
  25. package/dist/{router → card}/card-helpers.d.ts +7 -7
  26. package/dist/{router → card}/card-helpers.js +8 -8
  27. package/dist/card/card-session.d.ts +6 -2
  28. package/dist/card/card-session.js +28 -28
  29. package/dist/card/card-shared.d.ts +4 -4
  30. package/dist/card/card-shared.js +4 -10
  31. package/dist/card/collapsible.js +3 -2
  32. package/dist/card/final-answer-overflow.d.ts +1 -0
  33. package/dist/card/final-answer-overflow.js +46 -0
  34. package/dist/card/run-card-session.d.ts +2 -2
  35. package/dist/card/run-card-session.js +4 -9
  36. package/dist/card/run-renderer.d.ts +14 -0
  37. package/dist/card/run-renderer.js +126 -23
  38. package/dist/card/run-state.d.ts +40 -51
  39. package/dist/card/run-state.js +96 -34
  40. package/dist/card/usage-format.d.ts +30 -0
  41. package/dist/{router → card}/usage-format.js +3 -10
  42. package/dist/clone.js +6 -1
  43. package/dist/config/codex-config.js +4 -4
  44. package/dist/config/index.d.ts +18 -20
  45. package/dist/config/index.js +6 -20
  46. package/dist/config/pi-config.js +6 -4
  47. package/dist/connector/index.d.ts +2 -4
  48. package/dist/connector/index.js +7 -11
  49. package/dist/{runner/dsh → dsh}/auth.js +3 -4
  50. package/dist/dsh/home.d.ts +2 -0
  51. package/dist/dsh/home.js +8 -0
  52. package/dist/dsh/protocol.d.ts +9 -0
  53. package/dist/dsh/protocol.js +61 -0
  54. package/dist/inbound/turn-assembler.js +6 -0
  55. package/dist/index.js +25 -414
  56. package/dist/message-handlers.d.ts +26 -0
  57. package/dist/message-handlers.js +275 -0
  58. package/dist/model-catalog.d.ts +14 -0
  59. package/dist/model-catalog.js +25 -0
  60. package/dist/platform/path.d.ts +9 -0
  61. package/dist/platform/path.js +18 -0
  62. package/dist/router/active-run-usage.d.ts +4 -0
  63. package/dist/router/active-run-usage.js +12 -0
  64. package/dist/router/card-action-payload.d.ts +1 -1
  65. package/dist/router/card-action-payload.js +1 -1
  66. package/dist/router/commands/ls.d.ts +9 -0
  67. package/dist/router/commands/ls.js +243 -0
  68. package/dist/router/commands/order.d.ts +12 -0
  69. package/dist/router/commands/order.js +178 -0
  70. package/dist/router/commands/resume.d.ts +48 -0
  71. package/dist/router/commands/resume.js +246 -0
  72. package/dist/router/commands/ws.d.ts +15 -0
  73. package/dist/router/commands/ws.js +189 -0
  74. package/dist/router/config/claude.js +5 -4
  75. package/dist/router/config/codex.js +2 -6
  76. package/dist/router/config/common/render.d.ts +5 -1
  77. package/dist/router/config/common/render.js +6 -13
  78. package/dist/router/config/common/turn-idle.d.ts +2 -0
  79. package/dist/router/config/common/turn-idle.js +9 -0
  80. package/dist/router/config/dsh.d.ts +1 -1
  81. package/dist/router/config/index.d.ts +1 -1
  82. package/dist/router/config/kimi.js +2 -6
  83. package/dist/router/config/opencode.js +2 -6
  84. package/dist/router/config/types.d.ts +11 -3
  85. package/dist/router/index.d.ts +55 -33
  86. package/dist/router/index.js +233 -1376
  87. package/dist/router/paging.d.ts +13 -0
  88. package/dist/router/paging.js +20 -0
  89. package/dist/router/path-validation.d.ts +27 -0
  90. package/dist/router/path-validation.js +45 -0
  91. package/dist/runner/agent-choices-common.d.ts +1 -1
  92. package/dist/runner/claude/runner.d.ts +23 -6
  93. package/dist/runner/claude/runner.js +6 -20
  94. package/dist/runner/claude/session.js +1 -1
  95. package/dist/runner/codex/app-server/runner.d.ts +3 -1
  96. package/dist/runner/codex/app-server/translator.d.ts +13 -0
  97. package/dist/runner/codex/app-server/translator.js +114 -151
  98. package/dist/runner/common/acp/base-acp-runner.d.ts +3 -1
  99. package/dist/runner/common/acp/base-translator.d.ts +3 -8
  100. package/dist/runner/common/acp/base-translator.js +16 -7
  101. package/dist/runner/common/connection-based-runner.d.ts +3 -1
  102. package/dist/runner/common/jsonrpc/transport.js +17 -7
  103. package/dist/runner/common/spawning-runner.js +10 -4
  104. package/dist/runner/common/turn-started.d.ts +2 -0
  105. package/dist/runner/common/turn-started.js +10 -0
  106. package/dist/runner/dsh/client.d.ts +2 -10
  107. package/dist/runner/dsh/client.js +13 -220
  108. package/dist/runner/dsh/daemon.d.ts +0 -2
  109. package/dist/runner/dsh/daemon.js +9 -7
  110. package/dist/runner/dsh/errors.d.ts +5 -0
  111. package/dist/runner/dsh/errors.js +11 -0
  112. package/dist/runner/dsh/exit-cancel.d.ts +0 -2
  113. package/dist/runner/dsh/exit-cancel.js +2 -2
  114. package/dist/runner/dsh/inflight.d.ts +0 -2
  115. package/dist/runner/dsh/inflight.js +1 -1
  116. package/dist/runner/dsh/mux-stream.d.ts +26 -0
  117. package/dist/runner/dsh/mux-stream.js +176 -0
  118. package/dist/runner/dsh/runner.d.ts +21 -1
  119. package/dist/runner/dsh/runner.js +76 -73
  120. package/dist/runner/dsh/translator.d.ts +1 -11
  121. package/dist/runner/dsh/translator.js +1 -1
  122. package/dist/runner/index.d.ts +1 -1
  123. package/dist/runner/kimi/acp/runner.d.ts +22 -1
  124. package/dist/runner/kimi/acp/runner.js +83 -60
  125. package/dist/runner/kimi/acp/translator.js +1 -12
  126. package/dist/runner/opencode/acp/error-monitor.d.ts +11 -0
  127. package/dist/runner/opencode/acp/error-monitor.js +31 -2
  128. package/dist/runner/opencode/acp/runner.d.ts +3 -1
  129. package/dist/runner/opencode/acp/translator.js +1 -12
  130. package/dist/runner/pi/rpc/runner.d.ts +2 -1
  131. package/dist/runner/pi/rpc/translator.d.ts +2 -8
  132. package/dist/runner/pi/rpc/translator.js +2 -7
  133. package/dist/runner/probe.d.ts +1 -1
  134. package/dist/runner/probe.js +10 -9
  135. package/dist/runner/registry.d.ts +2 -20
  136. package/dist/runner/registry.js +0 -32
  137. package/dist/runner/types.d.ts +37 -155
  138. package/dist/runner/types.js +4 -24
  139. package/dist/session/claude/session-index.d.ts +11 -4
  140. package/dist/session/claude/session-index.js +34 -10
  141. package/dist/session/claude/session-reader.d.ts +1 -1
  142. package/dist/session/claude/sessions.d.ts +2 -2
  143. package/dist/session/claude/sessions.js +41 -26
  144. package/dist/session/codex/rollout-reader.d.ts +1 -1
  145. package/dist/session/codex/rollout-reader.js +14 -13
  146. package/dist/session/codex/sessions.d.ts +1 -1
  147. package/dist/session/codex/sessions.js +4 -2
  148. package/dist/session/common/jsonl.js +7 -1
  149. package/dist/session/common/pagination.d.ts +2 -0
  150. package/dist/session/common/pagination.js +1 -1
  151. package/dist/session/common/recency.d.ts +6 -0
  152. package/dist/session/common/recency.js +7 -5
  153. package/dist/session/common/two-pass.d.ts +1 -1
  154. package/dist/session/common/usage-accumulator.d.ts +1 -1
  155. package/dist/session/dsh/sessions.d.ts +1 -1
  156. package/dist/session/dsh/sessions.js +37 -12
  157. package/dist/session/kimi/sessions.d.ts +1 -1
  158. package/dist/session/kimi/sessions.js +37 -10
  159. package/dist/session/opencode/sessions.d.ts +1 -1
  160. package/dist/session/opencode/sessions.js +53 -48
  161. package/dist/session/pi/sessions.d.ts +1 -1
  162. package/dist/session/pi/sessions.js +23 -9
  163. package/dist/session/registry.d.ts +2 -1
  164. package/dist/session/session-store.d.ts +1 -2
  165. package/dist/session/session-store.js +14 -7
  166. package/dist/session-contract.d.ts +178 -0
  167. package/dist/session-contract.js +9 -0
  168. package/package.json +1 -1
  169. package/dist/router/usage-format.d.ts +0 -60
  170. /package/dist/{runner/dsh → dsh}/auth.d.ts +0 -0
  171. /package/dist/{runner/dsh → dsh}/types.d.ts +0 -0
  172. /package/dist/{runner/dsh → dsh}/types.js +0 -0
@@ -0,0 +1,178 @@
1
+ import { cardButton } from '../../card/card-shared.js';
2
+ import { paginationBar } from '../../card/card-helpers.js';
3
+ import { pageSlice } from '../paging.js';
4
+ export const ORDER_PAGE_SIZE = 8;
5
+ export function buildOrderCommand(args, ctx, offset, deps) {
6
+ const sub = args[0]?.toLowerCase();
7
+ if (!sub || sub === 'list') {
8
+ deps.reload();
9
+ const allOrders = deps.list();
10
+ const elements = [];
11
+ if (allOrders.length === 0) {
12
+ elements.push({ tag: 'div', text: { tag: 'lark_md', content: '暂无指令' } });
13
+ }
14
+ else {
15
+ const { totalCount, totalPages, safeOffset, currentPage, pageItems: pageOrders, hasPagination, } = pageSlice(allOrders, offset, ORDER_PAGE_SIZE);
16
+ for (let i = 0; i < pageOrders.length; i++) {
17
+ const order = pageOrders[i];
18
+ const displayText = order.text.length > 100 ? order.text.slice(0, 97) + '...' : order.text;
19
+ elements.push({
20
+ tag: 'div',
21
+ text: {
22
+ tag: 'lark_md',
23
+ content: order.alias ? `\`$${order.alias}\` ${displayText}` : displayText,
24
+ },
25
+ });
26
+ const leftColumn = order.alias
27
+ ? [
28
+ cardButton(`$${order.alias}`, { cmd: 'order.aliasEdit', orderId: order.id, offset: safeOffset }, { type: 'default', size: 'small' }),
29
+ cardButton('✕', {
30
+ cmd: 'order.aliasRemove',
31
+ orderId: order.id,
32
+ aliasName: order.alias,
33
+ offset: safeOffset,
34
+ }, { type: 'default', size: 'small' }),
35
+ ]
36
+ : [
37
+ cardButton('+别名', { cmd: 'order.aliasEdit', orderId: order.id, offset: safeOffset }, { type: 'default', size: 'small' }),
38
+ ];
39
+ elements.push({
40
+ tag: 'column_set',
41
+ columns: [
42
+ { tag: 'column', width: 'weighted', weight: 1, elements: leftColumn },
43
+ {
44
+ tag: 'column',
45
+ width: 'weighted',
46
+ weight: 1,
47
+ vertical_align: 'center',
48
+ elements: [
49
+ {
50
+ tag: 'column_set',
51
+ columns: [
52
+ {
53
+ tag: 'column',
54
+ width: 'auto',
55
+ elements: [
56
+ cardButton('▶ 执行', { cmd: 'order.exec', orderId: order.id }, { type: 'primary', size: 'small' }),
57
+ ],
58
+ },
59
+ {
60
+ tag: 'column',
61
+ width: 'auto',
62
+ elements: [
63
+ cardButton('编辑', { cmd: 'order.textEdit', orderId: order.id, offset: safeOffset }, { type: 'default', size: 'small' }),
64
+ ],
65
+ },
66
+ {
67
+ tag: 'column',
68
+ width: 'auto',
69
+ elements: [
70
+ cardButton('删除', { cmd: 'order.delete', orderId: order.id, offset: safeOffset }, { type: 'danger', size: 'small' }),
71
+ ],
72
+ },
73
+ ],
74
+ },
75
+ ],
76
+ },
77
+ ],
78
+ });
79
+ if (i < pageOrders.length - 1)
80
+ elements.push({ tag: 'hr' });
81
+ }
82
+ if (hasPagination) {
83
+ elements.push({ tag: 'hr' }, ...paginationBar({
84
+ cmd: 'order.page',
85
+ offset: safeOffset,
86
+ pageSize: ORDER_PAGE_SIZE,
87
+ total: totalCount,
88
+ label: `**第 ${currentPage}/${totalPages} 页**(共 ${totalCount} 条)`,
89
+ }));
90
+ }
91
+ }
92
+ return {
93
+ card: {
94
+ schema: '2.0',
95
+ config: { wide_screen_mode: true, update_multi: true },
96
+ header: { title: { tag: 'plain_text', content: '📋 指令' }, template: 'turquoise' },
97
+ body: { elements },
98
+ },
99
+ };
100
+ }
101
+ if (sub === 'alias') {
102
+ const op = args[1];
103
+ if (op === 'add') {
104
+ const target = args[2];
105
+ const name = args[3];
106
+ if (!target || !name) {
107
+ return { text: '用法: /order alias add <orderId|序号> <别名名>' };
108
+ }
109
+ const order = deps.resolveTarget(target);
110
+ if (!order)
111
+ return { text: `指令不存在: ${target}` };
112
+ try {
113
+ deps.setAlias(order.id, name);
114
+ return { text: `✅ 已给指令绑定别名 $${name}` };
115
+ }
116
+ catch (err) {
117
+ return { text: `保存失败: ${err.message}` };
118
+ }
119
+ }
120
+ if (op === 'rm') {
121
+ const target = args[2];
122
+ if (!target)
123
+ return { text: '用法: /order alias rm <orderId|序号>' };
124
+ const order = deps.resolveTarget(target);
125
+ if (!order)
126
+ return { text: `指令不存在: ${target}` };
127
+ if (!order.alias)
128
+ return { text: '该指令没有别名' };
129
+ const aliasName = order.alias;
130
+ try {
131
+ deps.setAlias(order.id, undefined);
132
+ return { text: `✅ 已移除别名 $${aliasName}` };
133
+ }
134
+ catch (err) {
135
+ return { text: `保存失败: ${err.message}` };
136
+ }
137
+ }
138
+ if (!op || op === 'list')
139
+ return buildOrderCommand([], ctx, offset, deps);
140
+ return {
141
+ text: '用法: /order alias [add <orderId|序号> <别名名> | rm <orderId|序号> | list]',
142
+ };
143
+ }
144
+ if (sub === 'save') {
145
+ const text = args.slice(1).join(' ');
146
+ if (!text)
147
+ return { text: '用法: /order save <指令文本>' };
148
+ try {
149
+ deps.save(text);
150
+ return { text: `✅ 已保存指令: ${text.slice(0, 50)}${text.length > 50 ? '...' : ''}` };
151
+ }
152
+ catch (err) {
153
+ return { text: `保存失败: ${err.message}` };
154
+ }
155
+ }
156
+ if (sub === 'edit') {
157
+ const target = args[1];
158
+ const newText = args.slice(2).join(' ').trim();
159
+ if (!target || !newText) {
160
+ return { text: '用法: /order edit <orderId|序号> <新文本>' };
161
+ }
162
+ const order = deps.resolveTarget(target);
163
+ if (!order)
164
+ return { text: `指令不存在: ${target}` };
165
+ try {
166
+ deps.updateText(order.id, newText);
167
+ const preview = newText.length > 50 ? newText.slice(0, 50) + '...' : newText;
168
+ return { text: `✅ 已更新指令: ${preview}` };
169
+ }
170
+ catch (err) {
171
+ return { text: `保存失败: ${err.message}` };
172
+ }
173
+ }
174
+ return {
175
+ text: '用法: /order [list|save <text>|edit <orderId|序号> <新文本>|alias add <orderId|序号> <别名名>|alias rm <orderId|序号>]',
176
+ };
177
+ }
178
+ //# sourceMappingURL=order.js.map
@@ -0,0 +1,48 @@
1
+ import type { CommandContext, CommandResult } from '../../bridge/index.js';
2
+ import type { AgentKind } from '../../agent-kinds.js';
3
+ import type { AgentSessionContentEvent, AgentSessionReader } from '../../session-contract.js';
4
+ import type { SessionDisplayUsage } from '../../card/usage-format.js';
5
+ export declare const AUTO_RESUME_MAX_EVENTS = 5;
6
+ export declare const RESUME_PAGE_SIZE = 5;
7
+ interface ResumeSessionEntry {
8
+ cwd?: string;
9
+ /** SessionStore 的 entry 恒有 sessions(`get()` 只会返回整条或缺席)。 */
10
+ sessions: Map<string, string>;
11
+ }
12
+ interface SessionDisplay {
13
+ events: AgentSessionContentEvent[];
14
+ usage?: SessionDisplayUsage;
15
+ isActive: boolean;
16
+ aiTitle?: string;
17
+ recap?: string;
18
+ displayTitle?: string;
19
+ activeRunRunId?: string;
20
+ }
21
+ export interface ResumeCommandDeps {
22
+ defaultAgent: AgentKind;
23
+ getSessionEntry(userId: string): ResumeSessionEntry | undefined;
24
+ setSessionId(userId: string, agent: AgentKind, sessionId: string, cwd: string): void;
25
+ defaultReader(): AgentSessionReader;
26
+ readerFor(agent: AgentKind): AgentSessionReader;
27
+ dshPresetMismatch(reader: AgentSessionReader, sessionId: string, cwd: string): {
28
+ sessionPreset: string;
29
+ currentPreset: string;
30
+ } | undefined;
31
+ readDisplay(sessionId: string, cwd: string, opts: {
32
+ agentKind?: string;
33
+ maxEvents?: number;
34
+ }): SessionDisplay;
35
+ buildActions(opts: {
36
+ isActive: boolean;
37
+ activeRunRunId?: string;
38
+ cwd: string;
39
+ compact?: {
40
+ sessionId: string;
41
+ agentKind: string;
42
+ };
43
+ newSession: boolean;
44
+ }): object[];
45
+ canCompact(agent: string, cwd?: string): boolean;
46
+ }
47
+ export declare function buildResumeCommand(args: string[], ctx: CommandContext, offset: number, deps: ResumeCommandDeps): CommandResult;
48
+ export {};
@@ -0,0 +1,246 @@
1
+ import { isAgentKind } from '../../agent-kinds.js';
2
+ import { agentDisplayName } from '../../card/card-shared.js';
3
+ import { buildSessionHistoryCard, paginationBar } from '../../card/card-helpers.js';
4
+ import { pageInfo } from '../paging.js';
5
+ import { clampInt } from '../../common/clamp.js';
6
+ export const AUTO_RESUME_MAX_EVENTS = 5;
7
+ export const RESUME_PAGE_SIZE = 5;
8
+ const RESUME_CONTENT_PREFETCH = 5;
9
+ const RESUME_SUMMARY_PLACEHOLDERS = new Set(['', '(no user message)', '(无摘要)', 'New Session']);
10
+ const RESUME_PREVIEW_MAX = 60;
11
+ function sessionPreviewLine(text, max) {
12
+ const oneLine = text.replace(/\s+/g, ' ').trim();
13
+ return oneLine.length > max ? oneLine.slice(0, max - 1) + '…' : oneLine;
14
+ }
15
+ export function buildResumeCommand(args, ctx, offset, deps) {
16
+ const entry = deps.getSessionEntry(ctx.userId);
17
+ const cwd = entry?.cwd;
18
+ let agentKind = deps.defaultAgent;
19
+ let limit;
20
+ let sessionIdArg;
21
+ if (args[0]) {
22
+ if (isAgentKind(args[0])) {
23
+ agentKind = args[0];
24
+ if (args[1]) {
25
+ if (/^\d+$/.test(args[1]))
26
+ limit = parseInt(args[1], 10);
27
+ else
28
+ sessionIdArg = args[1];
29
+ }
30
+ }
31
+ else if (/^\d+$/.test(args[0])) {
32
+ limit = parseInt(args[0], 10);
33
+ }
34
+ else {
35
+ sessionIdArg = args[0];
36
+ }
37
+ }
38
+ if (sessionIdArg) {
39
+ if (!entry || !cwd)
40
+ return { text: '请先 /cd 设置工作目录' };
41
+ const reader = agentKind !== deps.defaultAgent ? deps.readerFor(agentKind) : deps.defaultReader();
42
+ const verifyContent = reader.readSessionContent(sessionIdArg, cwd);
43
+ if (verifyContent.events.length === 0 &&
44
+ !verifyContent.usage &&
45
+ !verifyContent.aiTitle &&
46
+ !verifyContent.recap &&
47
+ !verifyContent.displayTitle) {
48
+ return {
49
+ text: `未找到 session ${sessionIdArg}(当前目录: ${cwd})。请确认该 session 属于此目录,或先 /cd 到正确目录。`,
50
+ };
51
+ }
52
+ if (agentKind === 'dsh' && deps.defaultAgent === 'dsh') {
53
+ const mismatch = deps.dshPresetMismatch(reader, sessionIdArg, cwd);
54
+ if (mismatch) {
55
+ return {
56
+ text: `⚠️ 该 DSH session 的 preset 是「${mismatch.sessionPreset}」,与当前配置「${mismatch.currentPreset}」不一致。\n` +
57
+ `preset 在 session 创建时固定,无法中途切换(DSH 返回 agent-preset-conflict)。\n` +
58
+ `继续恢复将在下次 run 失败。请在 /config 中把预设模式改回「${mismatch.sessionPreset}」后重试,或保持当前配置并发新消息新建会话。`,
59
+ };
60
+ }
61
+ }
62
+ deps.setSessionId(ctx.userId, agentKind, sessionIdArg, cwd);
63
+ const display = deps.readDisplay(sessionIdArg, cwd, {
64
+ agentKind,
65
+ maxEvents: AUTO_RESUME_MAX_EVENTS,
66
+ });
67
+ const actions = deps.buildActions({
68
+ isActive: display.isActive,
69
+ activeRunRunId: display.activeRunRunId,
70
+ cwd,
71
+ compact: deps.canCompact(agentKind, cwd) ? { sessionId: sessionIdArg, agentKind } : undefined,
72
+ newSession: true,
73
+ });
74
+ return {
75
+ card: buildSessionHistoryCard({
76
+ sessionId: sessionIdArg,
77
+ cwd,
78
+ displayTitle: display.displayTitle,
79
+ aiTitle: display.aiTitle,
80
+ recap: display.recap,
81
+ events: display.events,
82
+ usage: display.usage,
83
+ }, {
84
+ agentKind,
85
+ headerText: `📂 \`${cwd}\`\n会话: **${sessionIdArg}**`,
86
+ title: `${display.isActive ? '⏳ 恢复会话(完成中)' : '🔁 恢复会话'} · ${agentDisplayName(agentKind)}`,
87
+ usageResult: 'success',
88
+ emptyPlaceholder: '_该会话暂无新消息可显示(最后一条为用户输入)_',
89
+ actions,
90
+ }),
91
+ };
92
+ }
93
+ if (!cwd)
94
+ return { text: '请先 /cd 设置工作目录' };
95
+ const pageSize = clampInt(limit ?? RESUME_PAGE_SIZE, 1, RESUME_PAGE_SIZE);
96
+ const reader = agentKind === deps.defaultAgent ? deps.defaultReader() : deps.readerFor(agentKind);
97
+ let pageOffset = offset;
98
+ let pageResult;
99
+ try {
100
+ pageResult = reader.listSessions(cwd, { limit: pageSize, offset: pageOffset });
101
+ const maxOffset = Math.max(0, Math.ceil(pageResult.total / pageSize) - 1) * pageSize;
102
+ if (pageOffset < 0 || pageOffset > maxOffset) {
103
+ pageOffset = clampInt(pageOffset, 0, maxOffset);
104
+ pageResult = reader.listSessions(cwd, { limit: pageSize, offset: pageOffset });
105
+ }
106
+ }
107
+ catch (err) {
108
+ const msg = err instanceof Error ? err.message : String(err);
109
+ return { text: `读取 ${agentDisplayName(agentKind)} session 列表失败: ${msg}` };
110
+ }
111
+ const { sessions: allSessions, total } = pageResult;
112
+ if (allSessions.length === 0) {
113
+ const hint = agentKind === deps.defaultAgent
114
+ ? `\n提示:可使用 /resume <agent> 切换查看其他 Agent 的 session`
115
+ : '';
116
+ return {
117
+ text: `当前目录没有 ${agentDisplayName(agentKind)} session 记录\n${cwd}${hint}`,
118
+ };
119
+ }
120
+ const sessionMeta = new Map();
121
+ for (const session of allSessions.slice(0, RESUME_CONTENT_PREFETCH)) {
122
+ if (session.title || session.lastUserMessage || session.lastAssistantMessage)
123
+ continue;
124
+ const meta = reader.readSessionSummary
125
+ ? reader.readSessionSummary(session.sessionId, cwd)
126
+ : (() => {
127
+ const content = reader.readSessionContent(session.sessionId, cwd);
128
+ return {
129
+ aiTitle: content.aiTitle,
130
+ recap: content.recap,
131
+ displayTitle: content.displayTitle,
132
+ };
133
+ })();
134
+ sessionMeta.set(session.sessionId, meta);
135
+ }
136
+ const now = Date.now();
137
+ const formatRelativeTime = (mtimeMs) => {
138
+ const diffSec = Math.floor((now - mtimeMs) / 1000);
139
+ if (diffSec < 60)
140
+ return '刚刚';
141
+ if (diffSec < 3600)
142
+ return `${Math.floor(diffSec / 60)} 分钟前`;
143
+ if (diffSec < 86400)
144
+ return `${Math.floor(diffSec / 3600)} 小时前`;
145
+ if (diffSec < 604800)
146
+ return `${Math.floor(diffSec / 86400)} 天前`;
147
+ return new Date(mtimeMs).toLocaleDateString('zh-CN');
148
+ };
149
+ const elements = [
150
+ { tag: 'div', text: { tag: 'lark_md', content: `📂 当前工作目录:\`${cwd}\`` } },
151
+ ];
152
+ const currentId = entry.sessions.get(agentKind) ?? '';
153
+ for (const session of allSessions) {
154
+ const isCurrent = agentKind === deps.defaultAgent && session.sessionId === currentId;
155
+ const meta = sessionMeta.get(session.sessionId);
156
+ const summaryIsPlaceholder = RESUME_SUMMARY_PLACEHOLDERS.has(session.summary.trim());
157
+ const prefetchedTitle = meta?.displayTitle;
158
+ const rowTitleRaw = session.title?.trim() ?? '';
159
+ const rowTitle = rowTitleRaw && !RESUME_SUMMARY_PLACEHOLDERS.has(rowTitleRaw) ? rowTitleRaw : undefined;
160
+ const titleText = prefetchedTitle ?? rowTitle ?? (summaryIsPlaceholder ? '' : session.summary);
161
+ const titleLabel = prefetchedTitle
162
+ ? meta?.aiTitle
163
+ ? 'AI 标题'
164
+ : '最近输入'
165
+ : rowTitle
166
+ ? '标题'
167
+ : summaryIsPlaceholder
168
+ ? ''
169
+ : '会话摘要';
170
+ const sections = [];
171
+ if (titleText) {
172
+ sections.push(`🏷️ **${titleLabel}**\n${titleText.slice(0, 80)}${titleText.length > 80 ? '...' : ''}`);
173
+ }
174
+ if (meta?.recap) {
175
+ sections.push(`📝 **Recap**\n${meta.recap.slice(0, 60)}${meta.recap.length > 60 ? '...' : ''}`);
176
+ }
177
+ if (session.lastUserMessage) {
178
+ sections.push(`✏️ **最近输入**\n${sessionPreviewLine(session.lastUserMessage, RESUME_PREVIEW_MAX)}`);
179
+ }
180
+ if (session.lastAssistantMessage) {
181
+ sections.push(`📤 **最后输出**\n${sessionPreviewLine(session.lastAssistantMessage, RESUME_PREVIEW_MAX)}`);
182
+ }
183
+ const infoStr = sections.length > 0 ? '\n' + sections.join('\n──\n') : '';
184
+ elements.push({ tag: 'hr' }, {
185
+ tag: 'div',
186
+ text: {
187
+ tag: 'lark_md',
188
+ content: `**${session.sessionId}**${infoStr}\n⏱ ${formatRelativeTime(session.mtime)}${isCurrent ? ' •' : ''}`,
189
+ },
190
+ }, {
191
+ tag: 'column_set',
192
+ columns: [
193
+ {
194
+ tag: 'column',
195
+ width: 'auto',
196
+ elements: [
197
+ {
198
+ tag: 'button',
199
+ text: {
200
+ tag: 'plain_text',
201
+ content: isCurrent ? '✓ 当前会话' : '恢复此会话',
202
+ },
203
+ type: isCurrent ? 'default' : 'primary',
204
+ disabled: isCurrent,
205
+ behaviors: [
206
+ {
207
+ type: 'callback',
208
+ value: {
209
+ cmd: 'resume.use',
210
+ sessionId: session.sessionId,
211
+ agent: agentKind,
212
+ },
213
+ },
214
+ ],
215
+ },
216
+ ],
217
+ },
218
+ ],
219
+ });
220
+ }
221
+ if (total > pageSize) {
222
+ const { totalPages, currentPage } = pageInfo(total, pageOffset, pageSize);
223
+ elements.push({ tag: 'hr' }, ...paginationBar({
224
+ cmd: 'resume.page',
225
+ offset: pageOffset,
226
+ pageSize,
227
+ total,
228
+ extra: { agent: agentKind, pageSize },
229
+ label: `第 ${currentPage}/${totalPages} 页 · 共 ${total} 个会话`,
230
+ }));
231
+ }
232
+ return {
233
+ card: {
234
+ schema: '2.0',
235
+ config: { wide_screen_mode: true },
236
+ header: {
237
+ title: {
238
+ tag: 'plain_text',
239
+ content: `🔁 恢复历史会话 · ${agentDisplayName(agentKind)}`,
240
+ },
241
+ },
242
+ body: { elements },
243
+ },
244
+ };
245
+ }
246
+ //# sourceMappingURL=resume.js.map
@@ -0,0 +1,15 @@
1
+ import type { CommandContext, CommandResult } from '../../bridge/index.js';
2
+ import type { WorkspaceEntry } from '../../workspace/index.js';
3
+ export declare const WS_PAGE_SIZE = 5;
4
+ export interface WsCommandDeps {
5
+ getCwd(userId: string): string | undefined;
6
+ saveWorkspace(name: string, cwd: string): void;
7
+ getWorkspace(name: string): string | undefined;
8
+ hasWorkspace(name: string): boolean;
9
+ removeWorkspace(name: string): void;
10
+ touchWorkspace(name: string): void;
11
+ listWorkspaces(): WorkspaceEntry[];
12
+ getSortMode(userId: string): 'recent' | 'alpha';
13
+ switchCwd(userId: string, cwd: string, ctx: CommandContext): CommandResult;
14
+ }
15
+ export declare function buildWsCommand(args: string[], ctx: CommandContext, offset: number, q: string | undefined, deps: WsCommandDeps): CommandResult;
@@ -0,0 +1,189 @@
1
+ import { cardButton } from '../../card/card-shared.js';
2
+ import { paginationBar, searchBar } from '../../card/card-helpers.js';
3
+ import { pageSlice } from '../paging.js';
4
+ import { resolveDirectory } from '../path-validation.js';
5
+ export const WS_PAGE_SIZE = 5;
6
+ export function buildWsCommand(args, ctx, offset, q, deps) {
7
+ const sub = args[0]?.toLowerCase();
8
+ switch (sub) {
9
+ case 'save': {
10
+ const name = args[1];
11
+ if (!name)
12
+ return { text: '用法: /ws save <name>' };
13
+ const cwd = deps.getCwd(ctx.userId);
14
+ if (!cwd)
15
+ return { text: '请先 /cd 设置工作目录' };
16
+ deps.saveWorkspace(name, cwd);
17
+ return { text: `已保存 workspace "${name}" -> ${cwd}` };
18
+ }
19
+ case 'use': {
20
+ const name = args[1];
21
+ if (!name)
22
+ return { text: '用法: /ws use <name>' };
23
+ const wsPath = deps.getWorkspace(name);
24
+ if (!wsPath)
25
+ return { text: `workspace "${name}" 不存在` };
26
+ const resolution = resolveDirectory(wsPath);
27
+ if (!resolution.ok) {
28
+ if (resolution.reason === 'missing')
29
+ return { text: `路径不存在: ${wsPath}` };
30
+ if (resolution.reason === 'not_directory')
31
+ return { text: `不是目录: ${wsPath}` };
32
+ return { text: `路径无效: ${wsPath}` };
33
+ }
34
+ deps.touchWorkspace(name);
35
+ return deps.switchCwd(ctx.userId, resolution.canonical, ctx);
36
+ }
37
+ case 'remove': {
38
+ const name = args[1];
39
+ if (!name)
40
+ return { text: '用法: /ws remove <name>' };
41
+ if (!deps.hasWorkspace(name))
42
+ return { text: `workspace "${name}" 不存在` };
43
+ deps.removeWorkspace(name);
44
+ return { text: `已删除 workspace "${name}"` };
45
+ }
46
+ case 'list':
47
+ default: {
48
+ const filter = q?.trim() || undefined;
49
+ const entries = deps.listWorkspaces();
50
+ const currentCwd = deps.getCwd(ctx.userId);
51
+ const sortMode = deps.getSortMode(ctx.userId);
52
+ if (sortMode === 'recent') {
53
+ entries.sort((a, b) => {
54
+ if (b.lastUsedAt !== a.lastUsedAt)
55
+ return b.lastUsedAt - a.lastUsedAt;
56
+ return a.name.localeCompare(b.name);
57
+ });
58
+ }
59
+ else {
60
+ entries.sort((a, b) => a.name.localeCompare(b.name));
61
+ }
62
+ const ql = filter?.toLowerCase();
63
+ const visible = ql
64
+ ? entries.filter((entry) => entry.name.toLowerCase().includes(ql) || entry.path.toLowerCase().includes(ql))
65
+ : entries;
66
+ const { totalCount, totalPages, safeOffset, currentPage, pageItems: pageEntries, hasPagination, } = pageSlice(visible, offset, WS_PAGE_SIZE);
67
+ const bodyElements = [
68
+ {
69
+ tag: 'div',
70
+ text: {
71
+ tag: 'lark_md',
72
+ content: `📂 当前工作目录:\`${currentCwd ?? '(未设置)'}\``,
73
+ },
74
+ },
75
+ { tag: 'hr' },
76
+ searchBar({
77
+ cmd: 'ws.filter',
78
+ placeholder: '搜索名称或路径,输完回车',
79
+ currentQuery: filter,
80
+ }),
81
+ ];
82
+ if (filter) {
83
+ bodyElements.push({
84
+ tag: 'div',
85
+ text: {
86
+ tag: 'lark_md',
87
+ content: `🔍 筛选:"${filter}" · 命中 ${totalCount} 个 workspace(点「清除筛选」取消)`,
88
+ },
89
+ });
90
+ }
91
+ if (entries.length === 0 && !filter) {
92
+ bodyElements.push({
93
+ tag: 'div',
94
+ text: { tag: 'lark_md', content: '没有保存的 workspace' },
95
+ });
96
+ }
97
+ else if (filter && visible.length === 0) {
98
+ bodyElements.push({
99
+ tag: 'div',
100
+ text: { tag: 'lark_md', content: `🔍 无匹配条目(关键词:"${filter}")` },
101
+ });
102
+ }
103
+ else {
104
+ const currentLabel = sortMode === 'recent' ? '🕐 最近使用' : '🔤 字母顺序';
105
+ const switchLabel = sortMode === 'recent' ? '🔤 字母顺序' : '🕐 最近使用';
106
+ bodyElements.push({ tag: 'hr' }, {
107
+ tag: 'column_set',
108
+ columns: [
109
+ {
110
+ tag: 'column',
111
+ width: 'weighted',
112
+ weight: 1,
113
+ vertical_align: 'center',
114
+ elements: [
115
+ {
116
+ tag: 'div',
117
+ text: { tag: 'lark_md', content: `排序:${currentLabel}` },
118
+ },
119
+ ],
120
+ },
121
+ {
122
+ tag: 'column',
123
+ width: 'auto',
124
+ elements: [
125
+ cardButton(`切换为 ${switchLabel}`, { cmd: 'ws.sort', ...(filter ? { q: filter } : {}) }, { type: 'default', size: 'small' }),
126
+ ],
127
+ },
128
+ ],
129
+ }, { tag: 'hr' });
130
+ for (const entry of pageEntries) {
131
+ bodyElements.push({
132
+ tag: 'div',
133
+ text: { tag: 'lark_md', content: `**${entry.name}** → \`${entry.path}\`` },
134
+ }, {
135
+ tag: 'column_set',
136
+ columns: [
137
+ {
138
+ tag: 'column',
139
+ width: 'auto',
140
+ elements: [
141
+ cardButton('切换', {
142
+ cmd: 'ws.use',
143
+ name: entry.name,
144
+ offset: safeOffset,
145
+ ...(filter ? { q: filter } : {}),
146
+ }, { type: 'primary', size: 'small' }),
147
+ ],
148
+ },
149
+ {
150
+ tag: 'column',
151
+ width: 'auto',
152
+ elements: [
153
+ cardButton('删除', {
154
+ cmd: 'ws.remove',
155
+ name: entry.name,
156
+ offset: safeOffset,
157
+ ...(filter ? { q: filter } : {}),
158
+ }, { type: 'danger', size: 'small' }),
159
+ ],
160
+ },
161
+ ],
162
+ }, { tag: 'hr' });
163
+ }
164
+ bodyElements.pop();
165
+ if (hasPagination) {
166
+ bodyElements.push({ tag: 'hr' }, ...paginationBar({
167
+ cmd: 'ws.page',
168
+ offset: safeOffset,
169
+ pageSize: WS_PAGE_SIZE,
170
+ total: totalCount,
171
+ extra: filter ? { q: filter } : {},
172
+ label: `**${currentPage}/${totalPages}**(${totalCount})`,
173
+ prevText: '⬅',
174
+ nextText: '➡',
175
+ }));
176
+ }
177
+ }
178
+ return {
179
+ card: {
180
+ schema: '2.0',
181
+ config: { wide_screen_mode: true, update_multi: true },
182
+ header: { title: { tag: 'plain_text', content: 'Workspaces' } },
183
+ body: { elements: bodyElements },
184
+ },
185
+ };
186
+ }
187
+ }
188
+ }
189
+ //# sourceMappingURL=ws.js.map