lark-remote 0.4.3 → 0.4.5
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/CHANGELOG.md +21 -0
- package/dist/binder.d.ts +11 -10
- package/dist/binder.js +17 -16
- package/dist/bridge/inbound-media.d.ts +1 -27
- package/dist/bridge/inbound-media.js +13 -14
- package/dist/bridge/index.d.ts +49 -49
- package/dist/bridge/index.js +99 -92
- package/dist/bridge/queue-manager.d.ts +33 -26
- package/dist/bridge/queue-manager.js +117 -117
- package/dist/card/bash-renderer.js +4 -4
- package/dist/card/card-shared.d.ts +1 -1
- package/dist/card/card-shared.js +2 -2
- package/dist/card/run-renderer.js +8 -8
- package/dist/card/run-state.js +81 -31
- package/dist/card/tool-render.js +6 -2
- package/dist/cli.js +5 -6
- package/dist/clone.d.ts +1 -6
- package/dist/clone.js +4 -4
- package/dist/common/clamp.d.ts +2 -0
- package/dist/common/clamp.js +5 -0
- package/dist/common/fs.d.ts +4 -3
- package/dist/common/fs.js +5 -4
- package/dist/config/dir.d.ts +4 -5
- package/dist/config/dir.js +7 -8
- package/dist/config/wizard.js +2 -2
- package/dist/connector/index.d.ts +0 -2
- package/dist/connector/index.js +5 -5
- package/dist/inbound/receipt.d.ts +1 -3
- package/dist/inbound/receipt.js +2 -2
- package/dist/inbound/turn-assembler.d.ts +1 -3
- package/dist/inbound/turn-assembler.js +2 -2
- package/dist/index.js +30 -22
- package/dist/instance-lock.js +4 -4
- package/dist/persistence/atomic-write.js +3 -3
- package/dist/platform/agent-stopper.d.ts +1 -1
- package/dist/platform/terminator-posix.js +5 -5
- package/dist/platform/terminator-win32.js +1 -1
- package/dist/router/card-helpers.d.ts +1 -3
- package/dist/router/card-helpers.js +2 -2
- package/dist/router/config/codex.js +1 -1
- package/dist/router/config/common/model-patch.d.ts +1 -2
- package/dist/router/config/common/model-patch.js +1 -2
- package/dist/router/config/opencode.js +1 -1
- package/dist/router/config/pi.js +1 -1
- package/dist/router/index.d.ts +22 -22
- package/dist/router/index.js +71 -52
- package/dist/router/order-exec-dispatch.d.ts +3 -10
- package/dist/router/order-exec-dispatch.js +3 -3
- package/dist/router/{utils.d.ts → usage-format.d.ts} +2 -4
- package/dist/router/{utils.js → usage-format.js} +3 -8
- package/dist/runner/claude/runner.d.ts +1 -1
- package/dist/runner/claude/runner.js +1 -2
- package/dist/runner/claude/session.d.ts +2 -4
- package/dist/runner/claude/session.js +7 -9
- package/dist/runner/common/acp/{protocol-helpers.d.ts → acp-approval.d.ts} +2 -8
- package/dist/runner/common/acp/{protocol-helpers.js → acp-approval.js} +4 -3
- package/dist/runner/common/acp/base-acp-runner.d.ts +1 -1
- package/dist/runner/common/acp/base-acp-runner.js +1 -1
- package/dist/runner/common/acp/base-translator.d.ts +43 -0
- package/dist/runner/common/acp/base-translator.js +83 -18
- package/dist/runner/common/connection-based-runner.d.ts +4 -4
- package/dist/runner/common/connection-based-runner.js +3 -3
- package/dist/runner/common/jsonrpc/connection-manager.d.ts +12 -12
- package/dist/runner/common/jsonrpc/connection-manager.js +36 -36
- package/dist/runner/common/{usage.d.ts → result-usage.d.ts} +1 -1
- package/dist/runner/common/{usage.js → result-usage.js} +2 -2
- package/dist/runner/common/spawning-runner.d.ts +2 -2
- package/dist/runner/common/spawning-runner.js +8 -8
- package/dist/runner/common/{runner-utils.d.ts → synthetic-events.d.ts} +4 -4
- package/dist/runner/common/{runner-utils.js → synthetic-events.js} +5 -5
- package/dist/runner/dsh/client.d.ts +1 -1
- package/dist/runner/dsh/client.js +1 -1
- package/dist/runner/dsh/runner.js +1 -1
- package/dist/runner/kimi/acp/runner.d.ts +1 -1
- package/dist/runner/kimi/acp/runner.js +1 -1
- package/dist/runner/kimi/acp/translator.js +1 -1
- package/dist/runner/opencode/acp/runner.d.ts +2 -2
- package/dist/runner/opencode/acp/runner.js +1 -1
- package/dist/runner/opencode/acp/translator.d.ts +22 -1
- package/dist/runner/opencode/acp/translator.js +61 -1
- package/dist/runner/pi/rpc/runner.d.ts +1 -1
- package/dist/runner/pi/rpc/translator.d.ts +0 -1
- package/dist/runner/pi/rpc/translator.js +0 -2
- package/dist/runner/registry.d.ts +3 -3
- package/dist/runner/registry.js +3 -3
- package/dist/session/claude/session-index.d.ts +1 -1
- package/dist/session/claude/session-index.js +1 -1
- package/dist/session/claude/session-reader.d.ts +1 -1
- package/dist/session/claude/session-reader.js +1 -1
- package/dist/session/codex/rollout-reader.js +2 -2
- package/dist/session/dsh/sessions.d.ts +1 -1
- package/dist/session/dsh/sessions.js +1 -1
- package/dist/session/kimi/sessions.d.ts +0 -4
- package/dist/session/kimi/sessions.js +2 -8
- package/dist/session/opencode/sessions.js +3 -3
- package/dist/session/registry.d.ts +1 -1
- package/dist/session/registry.js +1 -1
- package/dist/session/session-store.d.ts +2 -3
- package/dist/session/session-store.js +2 -4
- package/dist/update/index.d.ts +1 -1
- package/dist/update/index.js +1 -1
- package/dist/update/version-check.d.ts +5 -3
- package/dist/update/version-check.js +10 -8
- package/dist/workspace/index.d.ts +4 -4
- package/dist/workspace/index.js +5 -5
- package/package.json +1 -1
package/dist/router/index.js
CHANGED
|
@@ -10,11 +10,14 @@ import { resolveAlias } from '../order/alias-resolve.js';
|
|
|
10
10
|
import { diffConfig, setNestedValue } from './config-diff.js';
|
|
11
11
|
import { getLogger } from '../logger/index.js';
|
|
12
12
|
import { stripPlaceholders } from '../inbound/placeholder.js';
|
|
13
|
-
import {
|
|
13
|
+
import { clampInt } from '../common/clamp.js';
|
|
14
|
+
import { activeRunUsage } from './usage-format.js';
|
|
14
15
|
import { markdownDiv, buildSessionHistoryCard, paginationBar, searchBar, parseJumpOffset, SEARCH_INPUT_NAME, PAGE_JUMP_INVALID_HINT, } from './card-helpers.js';
|
|
15
16
|
import { MAX_FILE_UPLOAD_SIZE } from '../connector/file-limits.js';
|
|
17
|
+
import { newSessionButton, resumeCompactButton, agentDisplayName } from '../card/card-shared.js';
|
|
18
|
+
import { displayName } from '../platform/path.js';
|
|
16
19
|
import { atomicWrite } from '../persistence/atomic-write.js';
|
|
17
|
-
import { checkLatestVersion as defaultCheckLatestVersion,
|
|
20
|
+
import { checkLatestVersion as defaultCheckLatestVersion, compareVersions as defaultCompareVersions, runInstallLatest as defaultRunInstallLatest, } from '../update/index.js';
|
|
18
21
|
/** Config card builder - delegates to per-agent builders */
|
|
19
22
|
import { getConfigBuilder, listRegisteredAgents, sortAgentsForDisplay } from './config/index.js';
|
|
20
23
|
import { probeAllAgents, getCachedAvailability } from '../runner/probe.js';
|
|
@@ -128,8 +131,6 @@ const RESUME_CONTENT_PREFETCH = 5;
|
|
|
128
131
|
* `New Session`、空串),都不是真实输入,不得渲染成行标题。
|
|
129
132
|
*/
|
|
130
133
|
const RESUME_SUMMARY_PLACEHOLDERS = new Set(['', '(no user message)', '(无摘要)', 'New Session']);
|
|
131
|
-
import { newSessionButton, resumeCompactButton, agentDisplayName } from '../card/card-shared.js';
|
|
132
|
-
import { displayName } from '../platform/path.js';
|
|
133
134
|
/**
|
|
134
135
|
* Card-action commands that must run with enqueue({ immediate: true }).
|
|
135
136
|
* These bypass the serial queue and execute immediately.
|
|
@@ -145,7 +146,7 @@ import { displayName } from '../platform/path.js';
|
|
|
145
146
|
* stop — interrupt current run
|
|
146
147
|
* ls.file — send file only
|
|
147
148
|
* ws.page — paginate /ws list only
|
|
148
|
-
* ws.remove — delete
|
|
149
|
+
* ws.remove — delete cwd alias only
|
|
149
150
|
* resume.use — set sessionId + agent for correct reader routing
|
|
150
151
|
* help.* — read-only help commands
|
|
151
152
|
* order.delete — delete order only
|
|
@@ -320,6 +321,24 @@ const IMMEDIATE_ACTION_CMDS = new Set([
|
|
|
320
321
|
// coordinator 已删响应空转(线上复现:approval.respond 排队卡、审批永不生效)。
|
|
321
322
|
...APPROVAL_ACTION_CMDS,
|
|
322
323
|
]);
|
|
324
|
+
/**
|
|
325
|
+
* 会话 / cwd / agent 变更类卡片动作(单源)。这些动作会改写 sessionStore 的
|
|
326
|
+
* sessionId / cwd / defaultAgent,必须与入站装配器的 700ms 静默窗口定序:
|
|
327
|
+
* 控制层(index.ts)在执行它们之前先 flush 该 user/chat 的装配窗口,保证窗口内
|
|
328
|
+
* 先到的文本消息在本动作之前 commit(绑定动作前的旧状态)。否则窗口内消息会
|
|
329
|
+
* 漂移到动作之后的新状态(2026-10-07 事故:输入「重新构建」后立即点「新会话」,
|
|
330
|
+
* 消息落到新会话)。
|
|
331
|
+
*
|
|
332
|
+
* 文本命令(/new、/cd、/resume、/config)已由 index.ts 命令分支先 flush,
|
|
333
|
+
* 不在此列——只有卡片动作绕过了该分支。
|
|
334
|
+
*/
|
|
335
|
+
export const SESSION_MUTATING_ACTION_CMDS = new Set([
|
|
336
|
+
'new-session',
|
|
337
|
+
'resume.use',
|
|
338
|
+
'ls.switch',
|
|
339
|
+
'ws.use',
|
|
340
|
+
'config.save',
|
|
341
|
+
]);
|
|
323
342
|
/** W2.8 单源:payload.offset → 钳位 offset(原先 9 处逐字副本)。 */
|
|
324
343
|
function payloadOffset(value) {
|
|
325
344
|
return Math.max(0, Math.trunc(Number(value.offset) || 0));
|
|
@@ -426,7 +445,7 @@ export class CommandRouter {
|
|
|
426
445
|
this.cloneSession = opts.cloneSession;
|
|
427
446
|
this.updateFns = {
|
|
428
447
|
checkLatestVersion: opts.updateFns?.checkLatestVersion ?? defaultCheckLatestVersion,
|
|
429
|
-
|
|
448
|
+
compareVersions: opts.updateFns?.compareVersions ?? defaultCompareVersions,
|
|
430
449
|
runInstallLatest: opts.updateFns?.runInstallLatest ?? defaultRunInstallLatest,
|
|
431
450
|
};
|
|
432
451
|
}
|
|
@@ -447,7 +466,7 @@ export class CommandRouter {
|
|
|
447
466
|
/**
|
|
448
467
|
* Route a message: if it starts with /, handle as command; if it starts with !, execute as bash; otherwise forward to Claude.
|
|
449
468
|
*
|
|
450
|
-
* 命令识别前置条件(2026-09-15 P0
|
|
469
|
+
* 命令识别前置条件(2026-09-15 P0 修复,`inbound-unified-input-design.md` §5.6):
|
|
451
470
|
* 裸前缀判定会被结构占位符命中 —— SDK 把富文本里的图片渲染成 ``,
|
|
452
471
|
* 首字符恰好是 `!`,整段用户消息被当 shell 命令执行(`executeBash "[image](img_v3_…"`)。
|
|
453
472
|
* 因此命令前缀只在「调用方声明纯文本 + 剥离占位符后非空且无占位符 + 首字符是 / 或 !」
|
|
@@ -494,7 +513,7 @@ export class CommandRouter {
|
|
|
494
513
|
await this.bridge.executeBash(cmd, ctx);
|
|
495
514
|
return null;
|
|
496
515
|
}
|
|
497
|
-
// Forward to Claude (P1-14: pass the enqueue-time
|
|
516
|
+
// Forward to Claude (P1-14: pass the enqueue-time cwd through so the
|
|
498
517
|
// run uses the same cwd as the serial queue lane, even if /cd ran while
|
|
499
518
|
// the message was queued)
|
|
500
519
|
// 剥离占位符后再转发:agent 只该看到用户文本(附图已转成 attachments 块,
|
|
@@ -635,8 +654,8 @@ export class CommandRouter {
|
|
|
635
654
|
// 调 updateCardInPlace → Feishu API 乱序到达导致 toggle 卡死。
|
|
636
655
|
// 2026-07-18: 返回 enqueueConfigAction 的结果以支持 toast 响应
|
|
637
656
|
return this.enqueueConfigAction(value, ctx);
|
|
638
|
-
case '
|
|
639
|
-
await this.bridge.
|
|
657
|
+
case 'compact':
|
|
658
|
+
await this.bridge.handleCompactAction(value, ctx);
|
|
640
659
|
return;
|
|
641
660
|
case 'resume.compact':
|
|
642
661
|
await this.bridge.handleResumeCompact(value, ctx);
|
|
@@ -672,13 +691,13 @@ export class CommandRouter {
|
|
|
672
691
|
* Routes to the appropriate bridge method based on value.cmd.
|
|
673
692
|
* Returns a toast response for immediate user feedback.
|
|
674
693
|
*/
|
|
675
|
-
/** queue.* 卡片动作通用守卫:
|
|
694
|
+
/** queue.* 卡片动作通用守卫:cwd/messageId 缺失时回复错误并返回 null。 */
|
|
676
695
|
async queuePayloadOrReply(value, ctx) {
|
|
677
|
-
if (!value.
|
|
696
|
+
if (!value.cwd || !value.messageId) {
|
|
678
697
|
await this.bridge.sendResult({ text: CARD_PAYLOAD_MISSING }, ctx);
|
|
679
698
|
return null;
|
|
680
699
|
}
|
|
681
|
-
return {
|
|
700
|
+
return { cwd: value.cwd, messageId: value.messageId };
|
|
682
701
|
}
|
|
683
702
|
/** 答案类操作的统一失败反馈:重复投递(同一 nonce 第二次点击)是已生效的
|
|
684
703
|
* 中性事件,报 error 会误导(首次点击实际已成功)。 */
|
|
@@ -712,11 +731,11 @@ export class CommandRouter {
|
|
|
712
731
|
const payload = await this.queuePayloadOrReply(value, ctx);
|
|
713
732
|
if (!payload)
|
|
714
733
|
return;
|
|
715
|
-
const {
|
|
716
|
-
const removed = this.bridge.removeFromQueue(
|
|
734
|
+
const { cwd, messageId } = payload;
|
|
735
|
+
const removed = this.bridge.removeFromQueue(cwd, messageId);
|
|
717
736
|
if (removed) {
|
|
718
737
|
// Update the queue card in-place to "cancelled" state
|
|
719
|
-
await this.bridge.updateQueueCardToCancelled(
|
|
738
|
+
await this.bridge.updateQueueCardToCancelled(cwd, messageId);
|
|
720
739
|
// Send a brief confirmation (user can see the card has been updated)
|
|
721
740
|
await this.bridge.sendResult({ text: '✅ 已从队列中撤销' }, ctx);
|
|
722
741
|
}
|
|
@@ -732,11 +751,11 @@ export class CommandRouter {
|
|
|
732
751
|
const payload = await this.queuePayloadOrReply(value, ctx);
|
|
733
752
|
if (!payload)
|
|
734
753
|
return;
|
|
735
|
-
const {
|
|
754
|
+
const { cwd, messageId } = payload;
|
|
736
755
|
// 1. Check target exists BEFORE any await. The queue chain can advance
|
|
737
756
|
// while later awaits (interruptCurrentRun / markQueueCardExecuting's card
|
|
738
757
|
// send) are in flight, so the target must be read synchronously at entry.
|
|
739
|
-
const targetTask = this.bridge.getQueuedTask(
|
|
758
|
+
const targetTask = this.bridge.getQueuedTask(cwd, messageId);
|
|
740
759
|
if (!targetTask) {
|
|
741
760
|
// The target already began (or was cancelled) before this handler ran. We
|
|
742
761
|
// must NOT claim the queue was cleared — nothing is removed in this branch
|
|
@@ -760,9 +779,9 @@ export class CommandRouter {
|
|
|
760
779
|
const editedContent = targetTask.editedMessage;
|
|
761
780
|
// D3/Step3: 替换闭包复用原任务的 binding(不重新快照)+ 恢复丢失的
|
|
762
781
|
// cwdOverride(lane cwd 同源)。否则编辑后重新执行的 run 会丢 cwd 与绑定。
|
|
763
|
-
this.bridge.setTaskReplacement(
|
|
782
|
+
this.bridge.setTaskReplacement(cwd, messageId, async () => {
|
|
764
783
|
await this.handle(editedContent, ctx, {
|
|
765
|
-
cwdOverride:
|
|
784
|
+
cwdOverride: cwd,
|
|
766
785
|
binding: targetTask.binding,
|
|
767
786
|
});
|
|
768
787
|
});
|
|
@@ -773,7 +792,7 @@ export class CommandRouter {
|
|
|
773
792
|
const stopped = await this.bridge.interruptCurrentRun({
|
|
774
793
|
userId: ctx.userId,
|
|
775
794
|
chatId: ctx.chatId,
|
|
776
|
-
|
|
795
|
+
cwd,
|
|
777
796
|
});
|
|
778
797
|
getLogger().info(`[router] queue.immediate: stopped=${stopped}`);
|
|
779
798
|
// 3. Remove all tasks BEFORE this one (by position in the current queue).
|
|
@@ -781,7 +800,7 @@ export class CommandRouter {
|
|
|
781
800
|
// advances concurrently): then it is already out of the snapshot and every
|
|
782
801
|
// remaining queued task is BEHIND it — clearing must stop, not run off the
|
|
783
802
|
// end and delete tasks queued after the target.
|
|
784
|
-
const tasks = this.bridge.getQueuedTasks(
|
|
803
|
+
const tasks = this.bridge.getQueuedTasks(cwd);
|
|
785
804
|
const targetIdx = tasks.findIndex((t) => t.messageId === messageId);
|
|
786
805
|
let removedCount = 0;
|
|
787
806
|
const removedIds = [];
|
|
@@ -792,7 +811,7 @@ export class CommandRouter {
|
|
|
792
811
|
// in flight.
|
|
793
812
|
for (let i = 0; i < targetIdx; i++) {
|
|
794
813
|
const task = tasks[i];
|
|
795
|
-
if (this.bridge.removeFromQueue(
|
|
814
|
+
if (this.bridge.removeFromQueue(cwd, task.messageId)) {
|
|
796
815
|
removedCount++;
|
|
797
816
|
removedIds.push(task.messageId);
|
|
798
817
|
}
|
|
@@ -804,7 +823,7 @@ export class CommandRouter {
|
|
|
804
823
|
// target are already gone, so any chain advance can only reach the target
|
|
805
824
|
// itself — the card updates may safely run in the background.
|
|
806
825
|
for (const removedId of removedIds) {
|
|
807
|
-
void this.bridge.updateQueueCardToCancelled(
|
|
826
|
+
void this.bridge.updateQueueCardToCancelled(cwd, removedId);
|
|
808
827
|
}
|
|
809
828
|
// 4. DO NOT remove the target task - keep it in queue to execute immediately
|
|
810
829
|
// The queue will naturally execute it after the current task (which we just stopped)
|
|
@@ -812,13 +831,13 @@ export class CommandRouter {
|
|
|
812
831
|
// re-registering over the same slot). Covers the user editing the message
|
|
813
832
|
// again while interruptCurrentRun was awaiting; skipped automatically once
|
|
814
833
|
// the task has begun, because getQueuedTask then returns undefined.
|
|
815
|
-
const latestTask = this.bridge.getQueuedTask(
|
|
834
|
+
const latestTask = this.bridge.getQueuedTask(cwd, messageId);
|
|
816
835
|
if (latestTask?.editedMessage) {
|
|
817
836
|
const editedContent = latestTask.editedMessage;
|
|
818
837
|
// D3/Step3: 复用原任务 binding + 恢复 cwdOverride(同上)。
|
|
819
|
-
this.bridge.setTaskReplacement(
|
|
838
|
+
this.bridge.setTaskReplacement(cwd, messageId, async () => {
|
|
820
839
|
await this.handle(editedContent, ctx, {
|
|
821
|
-
cwdOverride:
|
|
840
|
+
cwdOverride: cwd,
|
|
822
841
|
binding: latestTask.binding,
|
|
823
842
|
});
|
|
824
843
|
});
|
|
@@ -826,9 +845,9 @@ export class CommandRouter {
|
|
|
826
845
|
// 4.5/6. The target must only be marked executing (and the toast may only
|
|
827
846
|
// promise immediate execution) when it is really the next task after the
|
|
828
847
|
// stop. While interruptCurrentRun was awaiting, a task ahead of the target
|
|
829
|
-
// may have begun and now occupy the
|
|
848
|
+
// may have begun and now occupy the cwd — the target is still queued
|
|
830
849
|
// but is NOT next, so its card must stay queued and the toast must say so.
|
|
831
|
-
const targetStillQueued = this.bridge.getQueuedTask(
|
|
850
|
+
const targetStillQueued = this.bridge.getQueuedTask(cwd, messageId) !== undefined;
|
|
832
851
|
if (!targetStillQueued) {
|
|
833
852
|
// The target began or was cancelled while earlier awaits were in flight —
|
|
834
853
|
// a success toast must never promise execution of a task that will not
|
|
@@ -847,14 +866,14 @@ export class CommandRouter {
|
|
|
847
866
|
}
|
|
848
867
|
return;
|
|
849
868
|
}
|
|
850
|
-
const workspaceBusy = this.bridge.isBusyFor(
|
|
869
|
+
const workspaceBusy = this.bridge.isBusyFor(cwd);
|
|
851
870
|
if (!workspaceBusy) {
|
|
852
871
|
// Normal path: nothing new began during the stop, the target is next.
|
|
853
872
|
// Mark its own card as executing now so buttons grey out immediately.
|
|
854
873
|
// Done BEFORE any removal so getQueuedTask still returns the task with
|
|
855
874
|
// its (possibly edited) messagePreview for the card. The queue callback's
|
|
856
875
|
// later updateQueueCardToExecuting call becomes a no-op (idempotent).
|
|
857
|
-
await this.bridge.markQueueCardExecuting(
|
|
876
|
+
await this.bridge.markQueueCardExecuting(cwd, messageId);
|
|
858
877
|
const stopPrefix = stopped ? '⚡ 已停止当前任务,' : '';
|
|
859
878
|
await this.bridge.sendResult({ text: `${stopPrefix}清除了 ${removedCount} 条排队消息。您的消息将立即执行。` }, ctx);
|
|
860
879
|
}
|
|
@@ -871,10 +890,10 @@ export class CommandRouter {
|
|
|
871
890
|
const payload = await this.queuePayloadOrReply(value, ctx);
|
|
872
891
|
if (!payload)
|
|
873
892
|
return;
|
|
874
|
-
const {
|
|
893
|
+
const { cwd, messageId } = payload;
|
|
875
894
|
const targetUserId = value.userId ?? ctx.userId;
|
|
876
|
-
const task = this.bridge.getQueuedTask(
|
|
877
|
-
const queueInfo = this.bridge.getQueueInfo(
|
|
895
|
+
const task = this.bridge.getQueuedTask(cwd, messageId);
|
|
896
|
+
const queueInfo = this.bridge.getQueueInfo(cwd);
|
|
878
897
|
const activeRuns = this.bridge.getAllActiveRuns();
|
|
879
898
|
// Build diagnostic info
|
|
880
899
|
const now = new Date().toLocaleString('zh-CN');
|
|
@@ -907,7 +926,7 @@ export class CommandRouter {
|
|
|
907
926
|
tag: 'div',
|
|
908
927
|
text: {
|
|
909
928
|
tag: 'lark_md',
|
|
910
|
-
content: `**消息信息**\n- 工作目录: \`${
|
|
929
|
+
content: `**消息信息**\n- 工作目录: \`${cwd}\`\n- 会话 ID: \`${sessionId ?? '(none)'}\`\n- 消息预览: ${task?.messagePreview ?? '(unknown)'}`,
|
|
911
930
|
},
|
|
912
931
|
},
|
|
913
932
|
{ tag: 'hr' },
|
|
@@ -943,9 +962,9 @@ export class CommandRouter {
|
|
|
943
962
|
const payload = await this.queuePayloadOrReply(value, ctx);
|
|
944
963
|
if (!payload)
|
|
945
964
|
return;
|
|
946
|
-
const {
|
|
965
|
+
const { cwd, messageId } = payload;
|
|
947
966
|
// Get current message preview
|
|
948
|
-
const task = this.bridge.getQueuedTask(
|
|
967
|
+
const task = this.bridge.getQueuedTask(cwd, messageId);
|
|
949
968
|
if (!task) {
|
|
950
969
|
await this.bridge.sendResult({ text: '⚠️ 该消息不在队列中(可能已开始执行)' }, ctx);
|
|
951
970
|
return;
|
|
@@ -987,7 +1006,7 @@ export class CommandRouter {
|
|
|
987
1006
|
placeholder: { tag: 'plain_text', content: '输入新的消息内容...' },
|
|
988
1007
|
default_value: task.messagePreview,
|
|
989
1008
|
behaviors: [
|
|
990
|
-
{ type: 'callback', value: { cmd: 'queue.input',
|
|
1009
|
+
{ type: 'callback', value: { cmd: 'queue.input', cwd, messageId } },
|
|
991
1010
|
],
|
|
992
1011
|
},
|
|
993
1012
|
],
|
|
@@ -1013,7 +1032,7 @@ export class CommandRouter {
|
|
|
1013
1032
|
const payload = await this.queuePayloadOrReply(value, ctx);
|
|
1014
1033
|
if (!payload)
|
|
1015
1034
|
return;
|
|
1016
|
-
const {
|
|
1035
|
+
const { cwd, messageId } = payload;
|
|
1017
1036
|
if (!newMessage) {
|
|
1018
1037
|
await this.bridge.sendResult({ text: '⚠️ 缺少新消息内容' }, ctx);
|
|
1019
1038
|
return;
|
|
@@ -1031,7 +1050,7 @@ export class CommandRouter {
|
|
|
1031
1050
|
// there is no interleaving window.
|
|
1032
1051
|
//
|
|
1033
1052
|
// D3/Step3:复用原任务 binding(不重新快照)+ 恢复 cwdOverride。
|
|
1034
|
-
const inputTask = this.bridge.getQueuedTask(
|
|
1053
|
+
const inputTask = this.bridge.getQueuedTask(cwd, messageId);
|
|
1035
1054
|
if (!inputTask) {
|
|
1036
1055
|
// Task already left the queue (began or was cancelled) before we could
|
|
1037
1056
|
// register the replacement. Return a toast so the edit-form card gets
|
|
@@ -1040,8 +1059,8 @@ export class CommandRouter {
|
|
|
1040
1059
|
// toast/card field).
|
|
1041
1060
|
return { toast: { type: 'info', content: '任务已不在队列中(可能已开始执行或被撤销)' } };
|
|
1042
1061
|
}
|
|
1043
|
-
this.bridge.setTaskReplacement(
|
|
1044
|
-
await this.handle(newMessage, ctx, { cwdOverride:
|
|
1062
|
+
this.bridge.setTaskReplacement(cwd, messageId, async () => {
|
|
1063
|
+
await this.handle(newMessage, ctx, { cwdOverride: cwd, binding: inputTask.binding });
|
|
1045
1064
|
});
|
|
1046
1065
|
// Update the message preview (and editedMessage so handleQueueImmediate
|
|
1047
1066
|
// re-enqueues the edited content instead of the stale original closure),
|
|
@@ -1054,7 +1073,7 @@ export class CommandRouter {
|
|
|
1054
1073
|
// await here: even if the queue chain advances and the replacement is
|
|
1055
1074
|
// consumed, the user-facing card update is best-effort (the executing card
|
|
1056
1075
|
// is updated by updateQueueCardToExecuting instead).
|
|
1057
|
-
const card = await this.bridge.updateMessagePreview(
|
|
1076
|
+
const card = await this.bridge.updateMessagePreview(cwd, messageId, newMessage);
|
|
1058
1077
|
if (!card) {
|
|
1059
1078
|
// Task left the queue during the await (began or cancelled). The
|
|
1060
1079
|
// replacement was already consumed (begin path) or cleaned up
|
|
@@ -1576,7 +1595,7 @@ export class CommandRouter {
|
|
|
1576
1595
|
});
|
|
1577
1596
|
}
|
|
1578
1597
|
/**
|
|
1579
|
-
* Handle ws.use card action: switch to the
|
|
1598
|
+
* Handle ws.use card action: switch to the cwd, then refresh the /ws
|
|
1580
1599
|
* list card in place so "recent" sort order is immediately visible.
|
|
1581
1600
|
*
|
|
1582
1601
|
* Unlike the command-line `/ws use` (which returns text), the card action
|
|
@@ -1586,7 +1605,7 @@ export class CommandRouter {
|
|
|
1586
1605
|
async handleWsUse(value, ctx) {
|
|
1587
1606
|
const name = value.name ?? '';
|
|
1588
1607
|
const useResult = this.cmdWs(['use', name], ctx);
|
|
1589
|
-
// Error cases (
|
|
1608
|
+
// Error cases (cwd not found, path invalid, etc.): relay as error toast
|
|
1590
1609
|
if (useResult.text && !useResult.text.includes('已切换')) {
|
|
1591
1610
|
return { toast: { type: 'error', content: useResult.text } };
|
|
1592
1611
|
}
|
|
@@ -1615,7 +1634,7 @@ export class CommandRouter {
|
|
|
1615
1634
|
};
|
|
1616
1635
|
}
|
|
1617
1636
|
/**
|
|
1618
|
-
* Handle ws.remove: remove the
|
|
1637
|
+
* Handle ws.remove: remove the cwd alias and refresh the /ws list card
|
|
1619
1638
|
* in place (mirrors handleOrderDelete). Without this the stale alias would
|
|
1620
1639
|
* remain visible on the card the user just clicked.
|
|
1621
1640
|
*/
|
|
@@ -2366,7 +2385,7 @@ ${sessionCwdLine}${agentLines.map((l) => `- ${l}`).join('\n')}
|
|
|
2366
2385
|
catch (err) {
|
|
2367
2386
|
return { text: `❌ 版本检查失败: ${err.message}` };
|
|
2368
2387
|
}
|
|
2369
|
-
if (
|
|
2388
|
+
if (this.updateFns.compareVersions(current, latest) !== 1) {
|
|
2370
2389
|
return { text: `✅ 已是最新版本 ${current}` };
|
|
2371
2390
|
}
|
|
2372
2391
|
if (checkOnly) {
|
|
@@ -2398,7 +2417,7 @@ ${sessionCwdLine}${agentLines.map((l) => `- ${l}`).join('\n')}
|
|
|
2398
2417
|
}
|
|
2399
2418
|
cmdNew(ctx) {
|
|
2400
2419
|
// 只清 sessionId 保留 cwd — 否则 /new 之后 /resume 会提示"请先 /cd
|
|
2401
|
-
// 设置工作目录",但用户的
|
|
2420
|
+
// 设置工作目录",但用户的 cwd 还在。2026-06-21 复盘:用户
|
|
2402
2421
|
// /new → /resume 后被误导以为需要重新 /cd。
|
|
2403
2422
|
const agentName = agentDisplayName(this.config.defaultAgent);
|
|
2404
2423
|
this.sessionStore.clearSessionId(ctx.userId, this.config.defaultAgent, {
|
|
@@ -2667,7 +2686,7 @@ ${sessionCwdLine}${agentLines.map((l) => `- ${l}`).join('\n')}
|
|
|
2667
2686
|
* @param offset 分页起点(条目数)
|
|
2668
2687
|
* @param rootDir 浏览起点:卡片「返回」按钮回到这里。省略时默认 = 本次列出的
|
|
2669
2688
|
* 目录(`/ls <dir>` 的起点即该目录),因此「返回」不会把用户
|
|
2670
|
-
* 丢回
|
|
2689
|
+
* 丢回 工作目录(用户明确反馈过)。
|
|
2671
2690
|
* @param q 关键词筛选:只匹配**当前层**条目名(不递归),大小写不敏感。
|
|
2672
2691
|
* 省略/空 = 无筛选。
|
|
2673
2692
|
*/
|
|
@@ -2709,7 +2728,7 @@ ${sessionCwdLine}${agentLines.map((l) => `- ${l}`).join('\n')}
|
|
|
2709
2728
|
}
|
|
2710
2729
|
// 浏览起点(「返回」按钮的目标):`/ls <dir>` 的起点就是该目录本身,之后
|
|
2711
2730
|
// 进入子目录/翻页/刷新时由卡片 payload 的 root 一路带过来。省略时 = 本次
|
|
2712
|
-
//
|
|
2731
|
+
// 列出的目录,因此「返回」永远不会把用户丢回工作目录。
|
|
2713
2732
|
const browseRoot = rootDir ? path.resolve(cwd, rootDir) : targetDir;
|
|
2714
2733
|
try {
|
|
2715
2734
|
const entries = fs.readdirSync(targetDir, { withFileTypes: true });
|
|
@@ -3025,7 +3044,7 @@ ${sessionCwdLine}${agentLines.map((l) => `- ${l}`).join('\n')}
|
|
|
3025
3044
|
text: { tag: 'plain_text', content: '上级' },
|
|
3026
3045
|
type: 'default',
|
|
3027
3046
|
size: 'small',
|
|
3028
|
-
// 浏览起点 = 文件所在目录:进入后「返回」回到这里,不回
|
|
3047
|
+
// 浏览起点 = 文件所在目录:进入后「返回」回到这里,不回 工作目录
|
|
3029
3048
|
behaviors: [
|
|
3030
3049
|
{ type: 'callback', value: { cmd: 'ls.browse', path: parentDir, root: parentDir } },
|
|
3031
3050
|
],
|
|
@@ -3145,7 +3164,7 @@ ${sessionCwdLine}${agentLines.map((l) => `- ${l}`).join('\n')}
|
|
|
3145
3164
|
// Pagination calculations (mirror cmdOrder): clamp stale/out-of-range
|
|
3146
3165
|
// offsets so prev/next always step by WS_PAGE_SIZE.
|
|
3147
3166
|
const { totalCount, totalPages, safeOffset, currentPage, pageItems: pageEntries, hasPagination, } = pageSlice(visible, offset, WS_PAGE_SIZE);
|
|
3148
|
-
// Build body elements: current cwd +
|
|
3167
|
+
// Build body elements: current cwd + cwd list with dividers
|
|
3149
3168
|
const bodyElements = [];
|
|
3150
3169
|
// Current working directory section
|
|
3151
3170
|
bodyElements.push({
|
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
import type { CommandRouter } from './index.js';
|
|
2
|
-
import type { Bridge } from '../bridge/index.js';
|
|
3
|
-
/** Context shared by all card-action dispatch paths (file-local; callers pass it structurally). */
|
|
4
|
-
interface DispatchContext {
|
|
5
|
-
userId: string;
|
|
6
|
-
chatId: string;
|
|
7
|
-
messageId: string;
|
|
8
|
-
}
|
|
2
|
+
import type { Bridge, CommandContext } from '../bridge/index.js';
|
|
9
3
|
/**
|
|
10
4
|
* Dispatch an `order.exec` card action as an equivalent queued user message
|
|
11
5
|
* (Plan A): resolve the order text at the enqueue boundary, then enqueue it
|
|
@@ -23,8 +17,7 @@ interface DispatchContext {
|
|
|
23
17
|
export declare function dispatchOrderExecForQueue(args: {
|
|
24
18
|
router: CommandRouter;
|
|
25
19
|
bridge: Bridge;
|
|
26
|
-
|
|
20
|
+
cwd: string;
|
|
27
21
|
orderId: string | undefined;
|
|
28
|
-
ctx:
|
|
22
|
+
ctx: CommandContext;
|
|
29
23
|
}): Promise<'enqueued' | 'missing-id' | 'not-found'>;
|
|
30
|
-
export {};
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* for the caller to log; performs its own error feedback via `bridge.sendResult`.
|
|
14
14
|
*/
|
|
15
15
|
export async function dispatchOrderExecForQueue(args) {
|
|
16
|
-
const { router, bridge,
|
|
16
|
+
const { router, bridge, cwd, orderId, ctx } = args;
|
|
17
17
|
if (!orderId) {
|
|
18
18
|
await bridge.sendResult({ text: '卡片 payload 缺少必要信息' }, ctx);
|
|
19
19
|
return 'missing-id';
|
|
@@ -27,8 +27,8 @@ export async function dispatchOrderExecForQueue(args) {
|
|
|
27
27
|
// 避免排队期间 /new、/config 改写 live 状态导致语义漂移。唯一捕获点
|
|
28
28
|
// Bridge.currentBinding。
|
|
29
29
|
const binding = bridge.currentBinding(ctx.userId);
|
|
30
|
-
bridge.enqueue(
|
|
31
|
-
await router.handle(resolved.orderText, ctx, { cwdOverride:
|
|
30
|
+
bridge.enqueue(cwd, async () => {
|
|
31
|
+
await router.handle(resolved.orderText, ctx, { cwdOverride: cwd, binding });
|
|
32
32
|
}, {
|
|
33
33
|
taskMeta: {
|
|
34
34
|
userId: ctx.userId,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Format session/run usage stats (context, compact count, tokens, cost) into
|
|
3
|
+
* the display strings shown on cards. Pure — no Router state.
|
|
4
4
|
*/
|
|
5
5
|
import type { ActiveRunSnapshot } from '../bridge/index.js';
|
|
6
6
|
export interface SessionDisplayUsage {
|
|
@@ -12,8 +12,6 @@ export interface SessionDisplayUsage {
|
|
|
12
12
|
cacheCreationTokens?: number;
|
|
13
13
|
}
|
|
14
14
|
export declare function activeRunUsage(activeRun: ActiveRunSnapshot | undefined): SessionDisplayUsage | undefined;
|
|
15
|
-
/** Clamp `v` into [min, max]. */
|
|
16
|
-
export declare function clampInt(v: number, min: number, max: number): number;
|
|
17
15
|
/**
|
|
18
16
|
* Format usage stats (context length, compact count, cache tokens) into a display string.
|
|
19
17
|
* Each stat on its own line with blank lines between sections.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Format session/run usage stats (context, compact count, tokens, cost) into
|
|
3
|
+
* the display strings shown on cards. Pure — no Router state.
|
|
4
4
|
*/
|
|
5
5
|
export function activeRunUsage(activeRun) {
|
|
6
6
|
if (!activeRun || activeRun.contextLength === undefined)
|
|
@@ -12,11 +12,6 @@ export function activeRunUsage(activeRun) {
|
|
|
12
12
|
cacheCreationTokens: activeRun.cacheCreationTokens,
|
|
13
13
|
};
|
|
14
14
|
}
|
|
15
|
-
// --- Extracted helpers for refactoring ---
|
|
16
|
-
/** Clamp `v` into [min, max]. */
|
|
17
|
-
export function clampInt(v, min, max) {
|
|
18
|
-
return Math.min(Math.max(v, min), max);
|
|
19
|
-
}
|
|
20
15
|
/**
|
|
21
16
|
* Format number to human-readable unit string.
|
|
22
17
|
* - >= 1M: one decimal, M suffix (e.g., 1200000 → 1.2M, 25000000 → 25M)
|
|
@@ -154,4 +149,4 @@ export function formatUsageStats(usage, options) {
|
|
|
154
149
|
}
|
|
155
150
|
return lines.join('\n');
|
|
156
151
|
}
|
|
157
|
-
//# sourceMappingURL=
|
|
152
|
+
//# sourceMappingURL=usage-format.js.map
|
|
@@ -22,7 +22,7 @@ export declare class ClaudeRunner implements AgentRunner {
|
|
|
22
22
|
settings?: string;
|
|
23
23
|
stopGraceMs?: number;
|
|
24
24
|
pidDir?: string;
|
|
25
|
-
|
|
25
|
+
cwd: string;
|
|
26
26
|
spawnHeartbeatMs?: number;
|
|
27
27
|
sessionReader?: AgentSessionReader;
|
|
28
28
|
/** Claude 权限模式(官方 --permission-mode 枚举;'default'=省略参数)。 */
|
|
@@ -44,12 +44,11 @@ export class ClaudeRunner {
|
|
|
44
44
|
this.sessionReader = opts.sessionReader ?? new ClaudeSessionReader();
|
|
45
45
|
this.session = new ClaudeSession({
|
|
46
46
|
pidDir: opts.pidDir,
|
|
47
|
-
|
|
47
|
+
cwd: opts.cwd,
|
|
48
48
|
stopGraceMs: opts.stopGraceMs,
|
|
49
49
|
spawnHeartbeatMs: opts.spawnHeartbeatMs,
|
|
50
50
|
permissionMode: this.permissionMode,
|
|
51
51
|
settings: opts.settings,
|
|
52
|
-
model: this.defaultModel,
|
|
53
52
|
effort: this.defaultEffort,
|
|
54
53
|
idleTtlMs: opts.idleTtlMs,
|
|
55
54
|
});
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* - result 事件 subtype 为 compact/compaction 是 turn 中途压缩,不是 turn 结束;
|
|
23
23
|
* 其余 subtype(success/error/...)才是 turn 终态。
|
|
24
24
|
*
|
|
25
|
-
* 生命周期:一个
|
|
25
|
+
* 生命周期:一个 cwd 一个长驻进程(lifetime='workspace')。每次
|
|
26
26
|
* run(message) = 写一条 user 消息 + 消费 stdout 事件直到本 turn 的 result;
|
|
27
27
|
* 进程在 turn 之间保持存活(stdin 不关闭),/stop / /new / /cd / 看门狗超时
|
|
28
28
|
* 时经 Terminator 组杀,下条消息按 SessionStore 的 sessionId --resume。
|
|
@@ -31,13 +31,12 @@ import { SpawningRunner } from '../common/spawning-runner.js';
|
|
|
31
31
|
import type { AgentEvent, SpawnOptions } from '../types.js';
|
|
32
32
|
export interface ClaudeSessionOptions {
|
|
33
33
|
pidDir?: string;
|
|
34
|
-
|
|
34
|
+
cwd: string;
|
|
35
35
|
stopGraceMs?: number;
|
|
36
36
|
spawnHeartbeatMs?: number;
|
|
37
37
|
/** Claude 权限模式(官方 --permission-mode 枚举;'default' 省略该参数)。 */
|
|
38
38
|
permissionMode?: string;
|
|
39
39
|
settings?: string;
|
|
40
|
-
model?: string;
|
|
41
40
|
effort?: string;
|
|
42
41
|
/** 会话级空闲回收 TTL(ms):turn 之间无活动超过该窗口则停止进程。0=禁用。 */
|
|
43
42
|
idleTtlMs?: number;
|
|
@@ -61,7 +60,6 @@ export interface PermissionResult {
|
|
|
61
60
|
export declare class ClaudeSession extends SpawningRunner {
|
|
62
61
|
private readonly permissionMode;
|
|
63
62
|
private readonly settings?;
|
|
64
|
-
private readonly defaultModel?;
|
|
65
63
|
private readonly defaultEffort?;
|
|
66
64
|
private readonly idleTtlMs;
|
|
67
65
|
/** 当前 turn 是否在途(防止并发 run 写乱 stdin)。 */
|
|
@@ -22,16 +22,16 @@
|
|
|
22
22
|
* - result 事件 subtype 为 compact/compaction 是 turn 中途压缩,不是 turn 结束;
|
|
23
23
|
* 其余 subtype(success/error/...)才是 turn 终态。
|
|
24
24
|
*
|
|
25
|
-
* 生命周期:一个
|
|
25
|
+
* 生命周期:一个 cwd 一个长驻进程(lifetime='workspace')。每次
|
|
26
26
|
* run(message) = 写一条 user 消息 + 消费 stdout 事件直到本 turn 的 result;
|
|
27
27
|
* 进程在 turn 之间保持存活(stdin 不关闭),/stop / /new / /cd / 看门狗超时
|
|
28
28
|
* 时经 Terminator 组杀,下条消息按 SessionStore 的 sessionId --resume。
|
|
29
29
|
*/
|
|
30
30
|
import { readFile } from 'node:fs/promises';
|
|
31
|
-
import {
|
|
31
|
+
import { bestEffortUnlink } from '../../common/fs.js';
|
|
32
32
|
import { getLogger } from '../../logger/index.js';
|
|
33
33
|
import { SpawningRunner } from '../common/spawning-runner.js';
|
|
34
|
-
import {
|
|
34
|
+
import { setupErrorEvent, syntheticInitEvent } from '../common/synthetic-events.js';
|
|
35
35
|
import { makeQuestionApprovalEvent } from '../question-common.js';
|
|
36
36
|
/** 拒绝权限时的默认提示(进入 claude 上下文,中文用户可读)。 */
|
|
37
37
|
const DENY_MESSAGE = '用户拒绝了此工具调用,请停止并等待用户指令。';
|
|
@@ -50,7 +50,6 @@ const COMPACTION_RESULT_SUBTYPES = new Set(['compact', 'compaction']);
|
|
|
50
50
|
export class ClaudeSession extends SpawningRunner {
|
|
51
51
|
permissionMode;
|
|
52
52
|
settings;
|
|
53
|
-
defaultModel;
|
|
54
53
|
defaultEffort;
|
|
55
54
|
idleTtlMs;
|
|
56
55
|
/** 当前 turn 是否在途(防止并发 run 写乱 stdin)。 */
|
|
@@ -92,7 +91,7 @@ export class ClaudeSession extends SpawningRunner {
|
|
|
92
91
|
constructor(opts) {
|
|
93
92
|
super({
|
|
94
93
|
pidDir: opts.pidDir,
|
|
95
|
-
|
|
94
|
+
cwd: opts.cwd,
|
|
96
95
|
stopGraceMs: opts.stopGraceMs,
|
|
97
96
|
spawnHeartbeatMs: opts.spawnHeartbeatMs,
|
|
98
97
|
pidFilePrefix: 'claude',
|
|
@@ -103,7 +102,6 @@ export class ClaudeSession extends SpawningRunner {
|
|
|
103
102
|
this.binary = 'claude';
|
|
104
103
|
this.permissionMode = opts.permissionMode ?? 'bypassPermissions';
|
|
105
104
|
this.settings = opts.settings;
|
|
106
|
-
this.defaultModel = opts.model;
|
|
107
105
|
this.defaultEffort = opts.effort;
|
|
108
106
|
this.idleTtlMs = opts.idleTtlMs ?? DEFAULT_IDLE_TTL_MS;
|
|
109
107
|
}
|
|
@@ -141,7 +139,7 @@ export class ClaudeSession extends SpawningRunner {
|
|
|
141
139
|
// §9.22 守卫:错误 result 前必须补 synthetic init,否则 bridge 的
|
|
142
140
|
// pre-init result guard 与 run-state reducer 会静默丢弃错误信息。
|
|
143
141
|
yield syntheticInitEvent(opts.sessionId);
|
|
144
|
-
yield
|
|
142
|
+
yield setupErrorEvent(spawnError);
|
|
145
143
|
return;
|
|
146
144
|
}
|
|
147
145
|
// turn 之间的 idle 噪音(如 prompt_suggestion)不属于本 turn,先清空。
|
|
@@ -372,7 +370,7 @@ export class ClaudeSession extends SpawningRunner {
|
|
|
372
370
|
// 进程自行结束(崩溃/上游退出):注销协议停止通道,别把死 pid 的
|
|
373
371
|
// 条目留在注册表里等 pid 复用。
|
|
374
372
|
this.unregisterStopper();
|
|
375
|
-
|
|
373
|
+
bestEffortUnlink(this.pidFilePath);
|
|
376
374
|
this.wakeWaiters();
|
|
377
375
|
}
|
|
378
376
|
}
|
|
@@ -482,7 +480,7 @@ export class ClaudeSession extends SpawningRunner {
|
|
|
482
480
|
this.idleTimer = null;
|
|
483
481
|
if (this.turnActive || !this.isRunning)
|
|
484
482
|
return;
|
|
485
|
-
getLogger().info(`[${this.logTag}] idle timeout ${this.idleTtlMs}ms, stopping session
|
|
483
|
+
getLogger().info(`[${this.logTag}] idle timeout ${this.idleTtlMs}ms, stopping session cwd=${this.cwd}`);
|
|
486
484
|
void this.stop().catch((err) => {
|
|
487
485
|
getLogger().warn(`[${this.logTag}] idle stop failed: ${err.message}`);
|
|
488
486
|
});
|
|
@@ -1,18 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* ACP approval/permission mapping and lifecycle shared by the kimi and
|
|
3
|
+
* opencode ACP runners.
|
|
3
4
|
*
|
|
4
5
|
* Extracted from runner.ts and translator.ts to avoid duplication (G5).
|
|
5
6
|
* Shape source: approval.ts:28-29 — optionId is opaque, echo back as-is.
|
|
6
7
|
*/
|
|
7
8
|
import type { ApprovalView } from '../../types.js';
|
|
8
9
|
import type { PermissionOption } from './protocol-types.js';
|
|
9
|
-
/**
|
|
10
|
-
* Find an option by matching its `kind` against a list of candidate values,
|
|
11
|
-
* and return the `optionId` to echo back in the approval response.
|
|
12
|
-
* ACP option kind values vary between kimi versions; this handles both
|
|
13
|
-
* the documented names and the observed real names (§2.3).
|
|
14
|
-
*/
|
|
15
|
-
export declare function findOptionIdByKind(options: PermissionOption[], candidateKinds: string[]): string | undefined;
|
|
16
10
|
/** 各 ACP 服务端的 option kind 候选表(kimi/opencode 观测名不同,语义对齐)。 */
|
|
17
11
|
export interface AcpApprovalKinds {
|
|
18
12
|
accept: string[];
|