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.
- package/CHANGELOG.md +28 -2
- package/README.en.md +1 -14
- package/README.md +1 -14
- package/dist/agent-kinds.d.ts +27 -0
- package/dist/agent-kinds.js +38 -0
- package/dist/bridge/approval-actions.d.ts +95 -0
- package/dist/bridge/approval-actions.js +104 -0
- package/dist/bridge/approval-coordinator.d.ts +1 -0
- package/dist/bridge/approval-coordinator.js +12 -24
- package/dist/bridge/final-usage.d.ts +8 -17
- package/dist/bridge/final-usage.js +1 -18
- package/dist/bridge/index.d.ts +52 -38
- package/dist/bridge/index.js +255 -433
- package/dist/bridge/queue-manager.d.ts +25 -1
- package/dist/bridge/queue-manager.js +179 -160
- package/dist/bridge/run-approval.d.ts +16 -0
- package/dist/bridge/run-approval.js +41 -0
- package/dist/bridge/run-usage.d.ts +32 -0
- package/dist/bridge/run-usage.js +87 -0
- package/dist/bridge/session-writeback.d.ts +19 -0
- package/dist/bridge/session-writeback.js +36 -0
- package/dist/card/approval-render.d.ts +3 -2
- package/dist/card/approval-render.js +3 -2
- package/dist/card/bash-card-session.js +2 -5
- package/dist/{router → card}/card-helpers.d.ts +7 -7
- package/dist/{router → card}/card-helpers.js +8 -8
- package/dist/card/card-session.d.ts +6 -2
- package/dist/card/card-session.js +28 -28
- package/dist/card/card-shared.d.ts +4 -4
- package/dist/card/card-shared.js +4 -10
- package/dist/card/collapsible.js +3 -2
- package/dist/card/final-answer-overflow.d.ts +1 -0
- package/dist/card/final-answer-overflow.js +46 -0
- package/dist/card/run-card-session.d.ts +2 -2
- package/dist/card/run-card-session.js +4 -9
- package/dist/card/run-renderer.d.ts +14 -0
- package/dist/card/run-renderer.js +126 -23
- package/dist/card/run-state.d.ts +40 -51
- package/dist/card/run-state.js +96 -34
- package/dist/card/usage-format.d.ts +30 -0
- package/dist/{router → card}/usage-format.js +3 -10
- package/dist/clone.js +6 -1
- package/dist/config/codex-config.js +4 -4
- package/dist/config/index.d.ts +18 -20
- package/dist/config/index.js +6 -20
- package/dist/config/pi-config.js +6 -4
- package/dist/connector/index.d.ts +2 -4
- package/dist/connector/index.js +7 -11
- package/dist/{runner/dsh → dsh}/auth.js +3 -4
- package/dist/dsh/home.d.ts +2 -0
- package/dist/dsh/home.js +8 -0
- package/dist/dsh/protocol.d.ts +9 -0
- package/dist/dsh/protocol.js +61 -0
- package/dist/inbound/turn-assembler.js +6 -0
- package/dist/index.js +25 -414
- package/dist/message-handlers.d.ts +26 -0
- package/dist/message-handlers.js +275 -0
- package/dist/model-catalog.d.ts +14 -0
- package/dist/model-catalog.js +25 -0
- package/dist/platform/path.d.ts +9 -0
- package/dist/platform/path.js +18 -0
- package/dist/router/active-run-usage.d.ts +4 -0
- package/dist/router/active-run-usage.js +12 -0
- package/dist/router/card-action-payload.d.ts +1 -1
- package/dist/router/card-action-payload.js +1 -1
- package/dist/router/commands/ls.d.ts +9 -0
- package/dist/router/commands/ls.js +243 -0
- package/dist/router/commands/order.d.ts +12 -0
- package/dist/router/commands/order.js +178 -0
- package/dist/router/commands/resume.d.ts +48 -0
- package/dist/router/commands/resume.js +246 -0
- package/dist/router/commands/ws.d.ts +15 -0
- package/dist/router/commands/ws.js +189 -0
- package/dist/router/config/claude.js +5 -4
- package/dist/router/config/codex.js +2 -6
- package/dist/router/config/common/render.d.ts +5 -1
- package/dist/router/config/common/render.js +6 -13
- package/dist/router/config/common/turn-idle.d.ts +2 -0
- package/dist/router/config/common/turn-idle.js +9 -0
- package/dist/router/config/dsh.d.ts +1 -1
- package/dist/router/config/index.d.ts +1 -1
- package/dist/router/config/kimi.js +2 -6
- package/dist/router/config/opencode.js +2 -6
- package/dist/router/config/types.d.ts +11 -3
- package/dist/router/index.d.ts +55 -33
- package/dist/router/index.js +233 -1376
- package/dist/router/paging.d.ts +13 -0
- package/dist/router/paging.js +20 -0
- package/dist/router/path-validation.d.ts +27 -0
- package/dist/router/path-validation.js +45 -0
- package/dist/runner/agent-choices-common.d.ts +1 -1
- package/dist/runner/claude/runner.d.ts +23 -6
- package/dist/runner/claude/runner.js +6 -20
- package/dist/runner/claude/session.js +1 -1
- package/dist/runner/codex/app-server/runner.d.ts +3 -1
- package/dist/runner/codex/app-server/translator.d.ts +13 -0
- package/dist/runner/codex/app-server/translator.js +114 -151
- package/dist/runner/common/acp/base-acp-runner.d.ts +3 -1
- package/dist/runner/common/acp/base-translator.d.ts +3 -8
- package/dist/runner/common/acp/base-translator.js +16 -7
- package/dist/runner/common/connection-based-runner.d.ts +3 -1
- package/dist/runner/common/jsonrpc/transport.js +17 -7
- package/dist/runner/common/spawning-runner.js +10 -4
- package/dist/runner/common/turn-started.d.ts +2 -0
- package/dist/runner/common/turn-started.js +10 -0
- package/dist/runner/dsh/client.d.ts +2 -10
- package/dist/runner/dsh/client.js +13 -220
- package/dist/runner/dsh/daemon.d.ts +0 -2
- package/dist/runner/dsh/daemon.js +9 -7
- package/dist/runner/dsh/errors.d.ts +5 -0
- package/dist/runner/dsh/errors.js +11 -0
- package/dist/runner/dsh/exit-cancel.d.ts +0 -2
- package/dist/runner/dsh/exit-cancel.js +2 -2
- package/dist/runner/dsh/inflight.d.ts +0 -2
- package/dist/runner/dsh/inflight.js +1 -1
- package/dist/runner/dsh/mux-stream.d.ts +26 -0
- package/dist/runner/dsh/mux-stream.js +176 -0
- package/dist/runner/dsh/runner.d.ts +21 -1
- package/dist/runner/dsh/runner.js +76 -73
- package/dist/runner/dsh/translator.d.ts +1 -11
- package/dist/runner/dsh/translator.js +1 -1
- package/dist/runner/index.d.ts +1 -1
- package/dist/runner/kimi/acp/runner.d.ts +22 -1
- package/dist/runner/kimi/acp/runner.js +83 -60
- package/dist/runner/kimi/acp/translator.js +1 -12
- package/dist/runner/opencode/acp/error-monitor.d.ts +11 -0
- package/dist/runner/opencode/acp/error-monitor.js +31 -2
- package/dist/runner/opencode/acp/runner.d.ts +3 -1
- package/dist/runner/opencode/acp/translator.js +1 -12
- package/dist/runner/pi/rpc/runner.d.ts +2 -1
- package/dist/runner/pi/rpc/translator.d.ts +2 -8
- package/dist/runner/pi/rpc/translator.js +2 -7
- package/dist/runner/probe.d.ts +1 -1
- package/dist/runner/probe.js +10 -9
- package/dist/runner/registry.d.ts +2 -20
- package/dist/runner/registry.js +0 -32
- package/dist/runner/types.d.ts +37 -155
- package/dist/runner/types.js +4 -24
- package/dist/session/claude/session-index.d.ts +11 -4
- package/dist/session/claude/session-index.js +34 -10
- package/dist/session/claude/session-reader.d.ts +1 -1
- package/dist/session/claude/sessions.d.ts +2 -2
- package/dist/session/claude/sessions.js +41 -26
- package/dist/session/codex/rollout-reader.d.ts +1 -1
- package/dist/session/codex/rollout-reader.js +14 -13
- package/dist/session/codex/sessions.d.ts +1 -1
- package/dist/session/codex/sessions.js +4 -2
- package/dist/session/common/jsonl.js +7 -1
- package/dist/session/common/pagination.d.ts +2 -0
- package/dist/session/common/pagination.js +1 -1
- package/dist/session/common/recency.d.ts +6 -0
- package/dist/session/common/recency.js +7 -5
- package/dist/session/common/two-pass.d.ts +1 -1
- package/dist/session/common/usage-accumulator.d.ts +1 -1
- package/dist/session/dsh/sessions.d.ts +1 -1
- package/dist/session/dsh/sessions.js +37 -12
- package/dist/session/kimi/sessions.d.ts +1 -1
- package/dist/session/kimi/sessions.js +37 -10
- package/dist/session/opencode/sessions.d.ts +1 -1
- package/dist/session/opencode/sessions.js +53 -48
- package/dist/session/pi/sessions.d.ts +1 -1
- package/dist/session/pi/sessions.js +23 -9
- package/dist/session/registry.d.ts +2 -1
- package/dist/session/session-store.d.ts +1 -2
- package/dist/session/session-store.js +14 -7
- package/dist/session-contract.d.ts +178 -0
- package/dist/session-contract.js +9 -0
- package/package.json +1 -1
- package/dist/router/usage-format.d.ts +0 -60
- /package/dist/{runner/dsh → dsh}/auth.d.ts +0 -0
- /package/dist/{runner/dsh → dsh}/types.d.ts +0 -0
- /package/dist/{runner/dsh → dsh}/types.js +0 -0
|
@@ -22,9 +22,9 @@ import os from 'node:os';
|
|
|
22
22
|
import path from 'node:path';
|
|
23
23
|
import { truncateUtf8, TOOL_RESULT_MAX_BYTES } from '../../common/truncate.js';
|
|
24
24
|
import { isStale } from '../common/constants.js';
|
|
25
|
-
import { samePath } from '../../platform/path.js';
|
|
25
|
+
import { safeRealpath, samePath } from '../../platform/path.js';
|
|
26
26
|
import { paginate, capEvents } from '../common/pagination.js';
|
|
27
|
-
import { sortByRecencyDesc } from '../common/recency.js';
|
|
27
|
+
import { newestOf, sortByRecencyDesc } from '../common/recency.js';
|
|
28
28
|
import { TtlCache } from '../../common/ttl-cache.js';
|
|
29
29
|
import { getLogger } from '../../logger/index.js';
|
|
30
30
|
/** v2 CLI defaults `session list` to 100 entries; keep the reader's historical
|
|
@@ -81,6 +81,35 @@ function v2ToolOutput(state) {
|
|
|
81
81
|
return state.result;
|
|
82
82
|
return undefined;
|
|
83
83
|
}
|
|
84
|
+
/**
|
|
85
|
+
* 把会话级累计 token 落到 usage 上(v1 export 与 v2 export 两条读取路径共用)。
|
|
86
|
+
*
|
|
87
|
+
* 已有 usage 就地补累计字段;没有 usage 但累计非零(例如只有 cache 计数)时,
|
|
88
|
+
* 构造一条只带累计的,避免"会话有消耗却显示无 usage"。
|
|
89
|
+
*/
|
|
90
|
+
function withCumulativeUsage(usage, cum) {
|
|
91
|
+
if (usage) {
|
|
92
|
+
usage.cumulativeTotalTokens = cum.total;
|
|
93
|
+
usage.cumulativeInputTokens = cum.input;
|
|
94
|
+
usage.cumulativeOutputTokens = cum.output;
|
|
95
|
+
usage.cumulativeCacheReadTokens = cum.cacheRead;
|
|
96
|
+
usage.cumulativeCacheCreationTokens = cum.cacheWrite;
|
|
97
|
+
return usage;
|
|
98
|
+
}
|
|
99
|
+
if (!(cum.input || cum.output || cum.total || cum.cacheRead || cum.cacheWrite)) {
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
inputTokens: 0,
|
|
104
|
+
outputTokens: 0,
|
|
105
|
+
contextLength: 0,
|
|
106
|
+
cumulativeTotalTokens: cum.total,
|
|
107
|
+
cumulativeInputTokens: cum.input,
|
|
108
|
+
cumulativeOutputTokens: cum.output,
|
|
109
|
+
cumulativeCacheReadTokens: cum.cacheRead,
|
|
110
|
+
cumulativeCacheCreationTokens: cum.cacheWrite,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
84
113
|
export class OpencodeSessionReader {
|
|
85
114
|
binary;
|
|
86
115
|
cacheTtlMs;
|
|
@@ -124,7 +153,7 @@ export class OpencodeSessionReader {
|
|
|
124
153
|
* Get the newest session in a given cwd.
|
|
125
154
|
*/
|
|
126
155
|
getNewestSession(cwd) {
|
|
127
|
-
return this
|
|
156
|
+
return newestOf(this, cwd);
|
|
128
157
|
}
|
|
129
158
|
/**
|
|
130
159
|
* Read session content: extract catch-up tail (last user message + everything after).
|
|
@@ -283,25 +312,13 @@ export class OpencodeSessionReader {
|
|
|
283
312
|
}
|
|
284
313
|
}
|
|
285
314
|
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
}
|
|
293
|
-
else if (cumInput || cumOutput || cumTotal || cumCacheRead || cumCacheWrite) {
|
|
294
|
-
usage = {
|
|
295
|
-
inputTokens: 0,
|
|
296
|
-
outputTokens: 0,
|
|
297
|
-
contextLength: 0,
|
|
298
|
-
cumulativeTotalTokens: cumTotal,
|
|
299
|
-
cumulativeInputTokens: cumInput,
|
|
300
|
-
cumulativeOutputTokens: cumOutput,
|
|
301
|
-
cumulativeCacheReadTokens: cumCacheRead,
|
|
302
|
-
cumulativeCacheCreationTokens: cumCacheWrite,
|
|
303
|
-
};
|
|
304
|
-
}
|
|
315
|
+
usage = withCumulativeUsage(usage, {
|
|
316
|
+
total: cumTotal,
|
|
317
|
+
input: cumInput,
|
|
318
|
+
output: cumOutput,
|
|
319
|
+
cacheRead: cumCacheRead,
|
|
320
|
+
cacheWrite: cumCacheWrite,
|
|
321
|
+
});
|
|
305
322
|
// Use info.title as aiTitle
|
|
306
323
|
if (data.info.title) {
|
|
307
324
|
aiTitle = data.info.title;
|
|
@@ -470,25 +487,13 @@ export class OpencodeSessionReader {
|
|
|
470
487
|
};
|
|
471
488
|
}
|
|
472
489
|
}
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
}
|
|
480
|
-
else if (cumInput || cumOutput || cumTotal || cumCacheRead || cumCacheWrite) {
|
|
481
|
-
latest = {
|
|
482
|
-
inputTokens: 0,
|
|
483
|
-
outputTokens: 0,
|
|
484
|
-
contextLength: 0,
|
|
485
|
-
cumulativeTotalTokens: cumTotal,
|
|
486
|
-
cumulativeInputTokens: cumInput,
|
|
487
|
-
cumulativeOutputTokens: cumOutput,
|
|
488
|
-
cumulativeCacheReadTokens: cumCacheRead,
|
|
489
|
-
cumulativeCacheCreationTokens: cumCacheWrite,
|
|
490
|
-
};
|
|
491
|
-
}
|
|
490
|
+
latest = withCumulativeUsage(latest, {
|
|
491
|
+
total: cumTotal,
|
|
492
|
+
input: cumInput,
|
|
493
|
+
output: cumOutput,
|
|
494
|
+
cacheRead: cumCacheRead,
|
|
495
|
+
cacheWrite: cumCacheWrite,
|
|
496
|
+
});
|
|
492
497
|
return {
|
|
493
498
|
events: capEvents(events, opts?.maxEvents),
|
|
494
499
|
usage: latest,
|
|
@@ -510,19 +515,19 @@ export class OpencodeSessionReader {
|
|
|
510
515
|
// Check if updated within STALE_MS
|
|
511
516
|
return !isStale(entry.updated);
|
|
512
517
|
}
|
|
513
|
-
catch {
|
|
518
|
+
catch (err) {
|
|
519
|
+
getLogger().debug(`[opencode-session-reader] isSessionActive failed session=${sessionId}: ${err?.message ?? String(err)}`);
|
|
514
520
|
return false;
|
|
515
521
|
}
|
|
516
522
|
}
|
|
517
523
|
// --- Private helpers ---
|
|
518
524
|
/** realpath 归一化(public:测试直接调用,替代 as unknown as)。 */
|
|
519
525
|
realpath(cwd) {
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
}
|
|
526
|
+
return safeRealpath(cwd, (err) => {
|
|
527
|
+
if (err.code === 'ENOENT')
|
|
528
|
+
return;
|
|
529
|
+
getLogger().debug(`[opencode-session-reader] realpath ${cwd} failed, using the path as given: ${err?.message ?? String(err)}`);
|
|
530
|
+
});
|
|
526
531
|
}
|
|
527
532
|
fetchSessionList(cwd) {
|
|
528
533
|
const realCwd = this.realpath(cwd);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ContentBlockMapping } from '../common/content-blocks.js';
|
|
2
|
-
import type { AgentSession, AgentSessionReader, SessionContent } from '../../
|
|
2
|
+
import type { AgentSession, AgentSessionReader, SessionContent } from '../../session-contract.js';
|
|
3
3
|
/** Pi-specific field-name mapping for content block extraction. */
|
|
4
4
|
export declare const PI_MAPPING: ContentBlockMapping;
|
|
5
5
|
/**
|
|
@@ -8,8 +8,9 @@ import { UsageAccumulator, contextWindowOccupancy, cumulativeUsageFields, } from
|
|
|
8
8
|
import { isStale } from '../common/constants.js';
|
|
9
9
|
import { encodeProjectDirName, samePath } from '../../platform/path.js';
|
|
10
10
|
import { paginate } from '../common/pagination.js';
|
|
11
|
-
import { sortByRecencyDesc } from '../common/recency.js';
|
|
11
|
+
import { newestOf, sortByRecencyDesc } from '../common/recency.js';
|
|
12
12
|
import { TtlCache } from '../../common/ttl-cache.js';
|
|
13
|
+
import { getLogger } from '../../logger/index.js';
|
|
13
14
|
function defaultPiDir() {
|
|
14
15
|
return path.join(os.homedir(), '.pi', 'agent');
|
|
15
16
|
}
|
|
@@ -134,8 +135,11 @@ function findSessionFile(sessionId, cwd, sessionsDir) {
|
|
|
134
135
|
}
|
|
135
136
|
}
|
|
136
137
|
}
|
|
137
|
-
catch {
|
|
138
|
-
// dir
|
|
138
|
+
catch (err) {
|
|
139
|
+
// dir 不存在(陈旧 cwd / 该 cwd 从没有会话)是正常空态;其余留痕。
|
|
140
|
+
if (err.code !== 'ENOENT') {
|
|
141
|
+
getLogger().debug(`[pi-session-reader] cannot read ${dir}: ${err?.message ?? String(err)}`);
|
|
142
|
+
}
|
|
139
143
|
}
|
|
140
144
|
return null;
|
|
141
145
|
}
|
|
@@ -281,7 +285,7 @@ export class PiSessionReader {
|
|
|
281
285
|
};
|
|
282
286
|
}
|
|
283
287
|
getNewestSession(cwd) {
|
|
284
|
-
return this
|
|
288
|
+
return newestOf(this, cwd);
|
|
285
289
|
}
|
|
286
290
|
readSessionContent(sessionId, cwd, opts) {
|
|
287
291
|
let filePath = null;
|
|
@@ -318,7 +322,10 @@ export class PiSessionReader {
|
|
|
318
322
|
const st = fs.statSync(filePath);
|
|
319
323
|
return !isStale(st.mtimeMs);
|
|
320
324
|
}
|
|
321
|
-
catch {
|
|
325
|
+
catch (err) {
|
|
326
|
+
if (err.code !== 'ENOENT') {
|
|
327
|
+
getLogger().debug(`[pi-session-reader] stat ${filePath} failed: ${err?.message ?? String(err)}`);
|
|
328
|
+
}
|
|
322
329
|
return false;
|
|
323
330
|
}
|
|
324
331
|
}
|
|
@@ -334,8 +341,15 @@ export class PiSessionReader {
|
|
|
334
341
|
try {
|
|
335
342
|
files = fs.readdirSync(dir).filter((f) => f.endsWith('.jsonl'));
|
|
336
343
|
}
|
|
337
|
-
catch {
|
|
338
|
-
|
|
344
|
+
catch (err) {
|
|
345
|
+
// ENOENT:该 cwd 还没有会话目录(正常空态)。其余失败上抛,由 router
|
|
346
|
+
// 显示「列表失败」——静默 [] 与真空不可区分(与 opencode 同口径)。
|
|
347
|
+
if (err.code === 'ENOENT') {
|
|
348
|
+
return [];
|
|
349
|
+
}
|
|
350
|
+
getLogger().error(`[pi-session-reader] listSessions failed: ${err}`);
|
|
351
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
352
|
+
throw new Error(`pi session list 读取失败: ${msg}`, { cause: err });
|
|
339
353
|
}
|
|
340
354
|
const sessions = [];
|
|
341
355
|
for (const f of files) {
|
|
@@ -356,8 +370,8 @@ export class PiSessionReader {
|
|
|
356
370
|
mtime: st.mtimeMs,
|
|
357
371
|
});
|
|
358
372
|
}
|
|
359
|
-
catch {
|
|
360
|
-
|
|
373
|
+
catch (err) {
|
|
374
|
+
getLogger().debug(`[pi-session-reader] skipped ${full}: ${err?.message ?? String(err)}`);
|
|
361
375
|
}
|
|
362
376
|
}
|
|
363
377
|
// Same-mtime ties: secondary key keeps the full order deterministic.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { AgentKind
|
|
1
|
+
import type { AgentKind } from '../agent-kinds.js';
|
|
2
|
+
import type { AgentSessionReader } from '../session-contract.js';
|
|
2
3
|
/**
|
|
3
4
|
* Registry of `AgentSessionReader` instances, keyed by `AgentKind`. Mirrors
|
|
4
5
|
* `AgentRegistry` but for session-history reads. `index.ts` registers one
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AgentKind } from '../
|
|
1
|
+
import type { AgentKind } from '../agent-kinds.js';
|
|
2
2
|
interface SessionEntry {
|
|
3
3
|
/** Session IDs per agent kind (Map for in-memory, plain object for persistence). */
|
|
4
4
|
sessions: Map<AgentKind, string>;
|
|
@@ -58,7 +58,6 @@ export declare class SessionStore {
|
|
|
58
58
|
*/
|
|
59
59
|
private mutateEntry;
|
|
60
60
|
get(userId: string): SessionEntry | undefined;
|
|
61
|
-
set(userId: string, entry: SessionEntry): void;
|
|
62
61
|
delete(userId: string): void;
|
|
63
62
|
/** Clear sessionId for a specific agent, keeping cwd and other agent sessions */
|
|
64
63
|
clearSessionId(userId: string, agent?: AgentKind, opts?: {
|
|
@@ -85,10 +85,6 @@ export class SessionStore {
|
|
|
85
85
|
get(userId) {
|
|
86
86
|
return this.sessions.get(userId);
|
|
87
87
|
}
|
|
88
|
-
set(userId, entry) {
|
|
89
|
-
this.sessions.set(userId, entry);
|
|
90
|
-
this.autoPersist();
|
|
91
|
-
}
|
|
92
88
|
delete(userId) {
|
|
93
89
|
this.sessions.delete(userId);
|
|
94
90
|
this.autoPersist();
|
|
@@ -170,7 +166,7 @@ export class SessionStore {
|
|
|
170
166
|
/** Get sessionId for a specific agent, or undefined if no session or sessionId is empty */
|
|
171
167
|
getSessionId(userId, agent = 'claude') {
|
|
172
168
|
const entry = this.sessions.get(userId);
|
|
173
|
-
if (!entry
|
|
169
|
+
if (!entry)
|
|
174
170
|
return undefined;
|
|
175
171
|
const sessionId = entry.sessions.get(agent);
|
|
176
172
|
if (!sessionId)
|
|
@@ -284,8 +280,19 @@ export class SessionStore {
|
|
|
284
280
|
}
|
|
285
281
|
}
|
|
286
282
|
}
|
|
287
|
-
catch {
|
|
288
|
-
getLogger().warn(
|
|
283
|
+
catch (err) {
|
|
284
|
+
getLogger().warn(`[session] persisted session store corrupted (${this.persistPath ?? '(memory)'}), ignoring:`, err);
|
|
285
|
+
// 改名保留现场:原文件继续原地躺着会让下次启动重复告警,也丢掉排障证据。
|
|
286
|
+
if (this.persistPath) {
|
|
287
|
+
const brokenPath = `${this.persistPath}.corrupt-${Date.now()}`;
|
|
288
|
+
try {
|
|
289
|
+
fs.renameSync(this.persistPath, brokenPath);
|
|
290
|
+
getLogger().warn(`[session] moved corrupt store to ${brokenPath}`);
|
|
291
|
+
}
|
|
292
|
+
catch (renameErr) {
|
|
293
|
+
getLogger().warn('[session] failed to preserve corrupt store:', renameErr);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
289
296
|
}
|
|
290
297
|
}
|
|
291
298
|
autoPersist() {
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 会话读取契约:`AgentSessionReader` 与其数据类型。
|
|
3
|
+
*
|
|
4
|
+
* 中立模块——runner 与 session 两侧都只是它的实现方/消费方。此前住在
|
|
5
|
+
* `runner/types.ts` 里,于是 session/* 有十一处为了一个类型反向 import 上层
|
|
6
|
+
* runner(runner ↔ session 双向循环的一半);挪到这里后两边同向依赖它。
|
|
7
|
+
*/
|
|
8
|
+
/** Unified session descriptor returned by every AgentSessionReader. */
|
|
9
|
+
export interface AgentSession {
|
|
10
|
+
sessionId: string;
|
|
11
|
+
summary: string;
|
|
12
|
+
mtime: number;
|
|
13
|
+
/** DSH:session 创建时固定的 preset;其他 agent 无此概念(undefined)。 */
|
|
14
|
+
agentPreset?: string;
|
|
15
|
+
/**
|
|
16
|
+
* 服务端投影已有的会话标题(DSH `projections.values.title`)。
|
|
17
|
+
*
|
|
18
|
+
* 与 `summary` 的区别:`summary` 是「首条用户输入」的兜底口径(claude /
|
|
19
|
+
* codex / pi / kimi 无标题字段),`title` 是 agent 自己起的标题。列表卡
|
|
20
|
+
* 用它决定标签文案(标题 / 会话摘要),缺省时回退 `summary`。
|
|
21
|
+
*/
|
|
22
|
+
title?: string;
|
|
23
|
+
/**
|
|
24
|
+
* 最后一轮用户输入(服务端投影,单行预览)。
|
|
25
|
+
*
|
|
26
|
+
* 只给「列表响应里已顺带返回」的 reader 用(DSH 的 `turnOutline` 末条),
|
|
27
|
+
* 目的是让列表卡不必为每行做一次全量内容读取。
|
|
28
|
+
*/
|
|
29
|
+
lastUserMessage?: string;
|
|
30
|
+
/** 最后一轮助手输出(服务端投影已截断,单行预览)。 */
|
|
31
|
+
lastAssistantMessage?: string;
|
|
32
|
+
}
|
|
33
|
+
/** A single renderable event from session history. */
|
|
34
|
+
export interface AgentSessionContentEvent {
|
|
35
|
+
type: string;
|
|
36
|
+
/** 正文(展开后的面板内容)。 */
|
|
37
|
+
content: string;
|
|
38
|
+
/**
|
|
39
|
+
* 面板标题(可选,覆盖 reader 的默认标签)。
|
|
40
|
+
*
|
|
41
|
+
* 折叠面板默认只显示标题:工具调用这种"标题即全部信息"的事件必须把
|
|
42
|
+
* `bash — npm test` 这类摘要放进标题,否则折叠态只剩 `🔧 工具调用`。
|
|
43
|
+
*/
|
|
44
|
+
title?: string;
|
|
45
|
+
timestamp?: string;
|
|
46
|
+
}
|
|
47
|
+
export interface AgentSessionUsage {
|
|
48
|
+
inputTokens: number;
|
|
49
|
+
outputTokens: number;
|
|
50
|
+
contextLength: number;
|
|
51
|
+
/** Agent 上报的当前模型 context window 上限(codex token_count.info.model_context_window)。
|
|
52
|
+
* 仅 codex 提供;缺省时卡片只显示绝对量、不显示百分比。 */
|
|
53
|
+
contextLimit?: number;
|
|
54
|
+
/** Claude auto-compact 次数(数 compact_boundary 事件);codex/opencode 无此概念,留 undefined。 */
|
|
55
|
+
compactCount?: number;
|
|
56
|
+
/** 压缩前上下文水位(codex compact 后从会话 jsonl 读取,仅会话以压缩收尾时有值)。 */
|
|
57
|
+
compactPreContextLength?: number;
|
|
58
|
+
/** 从缓存读取的 token 数(节省的费用)。 */
|
|
59
|
+
cacheReadTokens?: number;
|
|
60
|
+
/** 新建缓存的 token 数。 */
|
|
61
|
+
cacheCreationTokens?: number;
|
|
62
|
+
/** Agent 声明的总量;展示用 max(total, 分项和)。 */
|
|
63
|
+
totalTokens?: number;
|
|
64
|
+
/** 会话累计 total token(所有 run 之和,含本次)。用于 Run 卡片"累计"展示。 */
|
|
65
|
+
cumulativeTotalTokens?: number;
|
|
66
|
+
/** 会话累计 input token(所有 run 之和,含本次)。用于 Run 卡片"累计"展示。 */
|
|
67
|
+
cumulativeInputTokens?: number;
|
|
68
|
+
/** 会话累计 output token(所有 run 之和,含本次)。 */
|
|
69
|
+
cumulativeOutputTokens?: number;
|
|
70
|
+
/** 会话累计 cache read token(所有 run 之和)。用于 Run 卡片展示累计缓存命中。 */
|
|
71
|
+
cumulativeCacheReadTokens?: number;
|
|
72
|
+
/** 会话累计 cache creation token(所有 run 之和)。 */
|
|
73
|
+
cumulativeCacheCreationTokens?: number;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* 一张 run 卡的用量快照:`AgentSessionUsage` 的可选形态 + 只有 live 侧有的
|
|
77
|
+
* 推理计数。桥侧记账、reducer 写入 RunState、渲染层格式化都吃这一份——
|
|
78
|
+
* 字段清单原先手抄在 6 个 interface 里(§9.21「五处一致」靠人记),
|
|
79
|
+
* 新增一个 usage 字段要同步改 6 处,漏一处就是卡片少一行。
|
|
80
|
+
*
|
|
81
|
+
* 中立层:card / bridge / runner / router 都引用它,谁也不向上依赖。
|
|
82
|
+
*/
|
|
83
|
+
export type UsageSnapshot = Partial<AgentSessionUsage> & {
|
|
84
|
+
/** 推理 token(pi usage.reasoning 累加);reader 契约里没有,只有 live 事件带。 */
|
|
85
|
+
reasoningTokens?: number;
|
|
86
|
+
};
|
|
87
|
+
/** Unified session content payload returned by every AgentSessionReader. */
|
|
88
|
+
export interface SessionContent {
|
|
89
|
+
events: AgentSessionContentEvent[];
|
|
90
|
+
usage?: AgentSessionUsage;
|
|
91
|
+
aiTitle?: string;
|
|
92
|
+
recap?: string;
|
|
93
|
+
displayTitle?: string;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Lightweight session summary used by `/resume` list prefetch.
|
|
97
|
+
*
|
|
98
|
+
* Carries only the display fields (title / recap / usage) — **no `events`
|
|
99
|
+
* array**. Readers that can extract these without constructing the full event
|
|
100
|
+
* list (e.g. codex, which would otherwise JSON.parse every response_item and
|
|
101
|
+
* build an `events[]` that the prefetch immediately discards) implement the
|
|
102
|
+
* optional `readSessionSummary` on `AgentSessionReader`.
|
|
103
|
+
*/
|
|
104
|
+
export interface SessionSummary {
|
|
105
|
+
aiTitle?: string;
|
|
106
|
+
recap?: string;
|
|
107
|
+
displayTitle?: string;
|
|
108
|
+
usage?: AgentSessionUsage;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* 手动压缩的在途状态(由会话里的 begin/terminal 记录重建)。
|
|
112
|
+
* 只有把压缩写成 wire 状态机的 agent 能提供,见
|
|
113
|
+
* {@link AgentSessionReader.readCompactionState}。
|
|
114
|
+
*/
|
|
115
|
+
export interface CompactionState {
|
|
116
|
+
inFlight: boolean;
|
|
117
|
+
/** 在途 begin 的 time(epoch ms)。 */
|
|
118
|
+
inFlightSince?: number;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Reads an agent's session history. Each agent implements its own (Claude reads
|
|
122
|
+
* `~/.claude/projects/`, Codex calls `thread/list`, OpenCode calls `GET /session`).
|
|
123
|
+
*
|
|
124
|
+
* Q-C decision: `projectsDir` and other agent-specific path config live on the
|
|
125
|
+
* concrete reader's constructor, NOT on these method opts. The router/bridge
|
|
126
|
+
* depend only on this interface and never pass path config.
|
|
127
|
+
*/
|
|
128
|
+
export interface AgentSessionReader {
|
|
129
|
+
/**
|
|
130
|
+
* List sessions for a cwd, newest first by mtime.
|
|
131
|
+
*
|
|
132
|
+
* `sessions` is the page `[offset, offset+limit)` of the full set ordered
|
|
133
|
+
* by mtime desc, with same-mtime ties broken by a deterministic secondary
|
|
134
|
+
* key (sessionId/file path) so ordering is stable across calls. When
|
|
135
|
+
* `limit` is omitted, codex/pi/kimi return their default-sized page
|
|
136
|
+
* (default 20); claude/opencode return the full remaining set from
|
|
137
|
+
* `offset`. `total` is ALWAYS the size of the full cwd-matched set before
|
|
138
|
+
* pagination — never the truncated page length. A negative `offset` is
|
|
139
|
+
* clamped to 0 (first page).
|
|
140
|
+
*/
|
|
141
|
+
listSessions(cwd: string, opts?: {
|
|
142
|
+
limit?: number;
|
|
143
|
+
offset?: number;
|
|
144
|
+
}): {
|
|
145
|
+
sessions: AgentSession[];
|
|
146
|
+
total: number;
|
|
147
|
+
};
|
|
148
|
+
getNewestSession(cwd: string): AgentSession | null;
|
|
149
|
+
readSessionContent(sessionId: string, cwd: string, opts?: {
|
|
150
|
+
maxEvents?: number;
|
|
151
|
+
}): SessionContent;
|
|
152
|
+
/**
|
|
153
|
+
* Optional lightweight summary for list-prefetch.
|
|
154
|
+
*
|
|
155
|
+
* Returns only the display fields (aiTitle/recap/displayTitle/usage) without
|
|
156
|
+
* constructing the full `events` array. When absent, callers fall back to
|
|
157
|
+
* `readSessionContent` and take the needed fields.
|
|
158
|
+
*/
|
|
159
|
+
readSessionSummary?(sessionId: string, cwd: string): SessionSummary;
|
|
160
|
+
/**
|
|
161
|
+
* 这个会话此刻是否还在被使用。**证据强度按 reader 不同,调用方必须知道自己
|
|
162
|
+
* 问的是哪一个**:
|
|
163
|
+
*
|
|
164
|
+
* - DSH:服务端事实(session 有在跑的 turn)。启动自动恢复路径用它,
|
|
165
|
+
* 见 `src/index.ts` 的 `stillRunning` 分支。
|
|
166
|
+
* - claude/codex/opencode/pi/kimi:只有「最近一段时间有写入」这一层文件证据
|
|
167
|
+
* (mtime + 末行终态标记),拿它当 turn 在跑会每次误报。
|
|
168
|
+
*
|
|
169
|
+
* 「agent 现在忙不忙」的运行时真相仍归 `bridge.isBusyFor(cwd)`(内存态,
|
|
170
|
+
* 见 cmdPs);本方法是文件/服务端侧的补充探针,不是它的替代品。
|
|
171
|
+
*/
|
|
172
|
+
isSessionActive(sessionId: string, cwd: string): boolean;
|
|
173
|
+
/**
|
|
174
|
+
* 手动压缩是否已在途(只有把压缩写成 wire 状态机的 agent 能提供,目前 kimi)。
|
|
175
|
+
* 未实现 = 该 agent 没有这个概念,调用方按「不在途」处理。
|
|
176
|
+
*/
|
|
177
|
+
readCompactionState?(sessionId: string, cwd: string): CompactionState;
|
|
178
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 会话读取契约:`AgentSessionReader` 与其数据类型。
|
|
3
|
+
*
|
|
4
|
+
* 中立模块——runner 与 session 两侧都只是它的实现方/消费方。此前住在
|
|
5
|
+
* `runner/types.ts` 里,于是 session/* 有十一处为了一个类型反向 import 上层
|
|
6
|
+
* runner(runner ↔ session 双向循环的一半);挪到这里后两边同向依赖它。
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=session-contract.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lark-remote",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.14",
|
|
4
4
|
"description": "Feishu (Lark) private-chat bridge to local coding agents — Claude Code / Codex / opencode / pi / kimi, with live-streaming CardKit 2.0 cards",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Format session/run usage stats (context, compact count, tokens, cost) into
|
|
3
|
-
* the display strings shown on cards. Pure — no Router state.
|
|
4
|
-
*/
|
|
5
|
-
import type { ActiveRunSnapshot } from '../bridge/index.js';
|
|
6
|
-
export interface SessionDisplayUsage {
|
|
7
|
-
contextLength?: number;
|
|
8
|
-
/** 当前模型 context window 上限(仅 codex 提供);用于渲染 "Context - X (Y%)"。 */
|
|
9
|
-
contextLimit?: number;
|
|
10
|
-
compactCount?: number;
|
|
11
|
-
cacheReadTokens?: number;
|
|
12
|
-
cacheCreationTokens?: number;
|
|
13
|
-
}
|
|
14
|
-
export declare function activeRunUsage(activeRun: ActiveRunSnapshot | undefined): SessionDisplayUsage | undefined;
|
|
15
|
-
/**
|
|
16
|
-
* Format usage stats (context length, compact count, cache tokens) into a display string.
|
|
17
|
-
* Each stat on its own line with blank lines between sections.
|
|
18
|
-
* @param usage - Usage stats object
|
|
19
|
-
* @param options - Optional display options (showResult adds result line)
|
|
20
|
-
*/
|
|
21
|
-
export declare function formatUsageStats(usage?: {
|
|
22
|
-
contextLength?: number;
|
|
23
|
-
contextLimit?: number;
|
|
24
|
-
compactCount?: number;
|
|
25
|
-
/** 压缩前上下文水位;与 contextLength(压缩后)成对出现在 Compact 卡上。 */
|
|
26
|
-
compactPreContextLength?: number;
|
|
27
|
-
cacheReadTokens?: number;
|
|
28
|
-
cacheCreationTokens?: number;
|
|
29
|
-
/** Agent-declared total; when present the display uses max(total, sum of parts). */
|
|
30
|
-
totalTokens?: number;
|
|
31
|
-
/** Real input tokens from the agent's result event. When present, used
|
|
32
|
-
* directly for the "Input token" line instead of contextLength+cache. */
|
|
33
|
-
inputTokens?: number;
|
|
34
|
-
/** Real output tokens from the agent's result event. When present, used
|
|
35
|
-
* directly instead of the 10% estimate. */
|
|
36
|
-
outputTokens?: number;
|
|
37
|
-
/** Session-cumulative total/input/output (all runs), from session jsonl. When
|
|
38
|
-
* present, appended as "· 累计 X" on the Total/Input/Output lines. */
|
|
39
|
-
cumulativeTotalTokens?: number;
|
|
40
|
-
cumulativeInputTokens?: number;
|
|
41
|
-
cumulativeOutputTokens?: number;
|
|
42
|
-
/** Session-cumulative cache read tokens (all runs). */
|
|
43
|
-
cumulativeCacheReadTokens?: number;
|
|
44
|
-
/** Session-cumulative cache creation tokens (all runs). */
|
|
45
|
-
cumulativeCacheCreationTokens?: number;
|
|
46
|
-
/** 实际生效模型(system.init 携带)。非空字符串才显示。 */
|
|
47
|
-
model?: string;
|
|
48
|
-
/** 本次 run 花费(USD)。存在才显示。 */
|
|
49
|
-
costUsd?: number;
|
|
50
|
-
/** 推理 token(pi 提供)。> 0 才显示。 */
|
|
51
|
-
reasoningTokens?: number;
|
|
52
|
-
}, options?: {
|
|
53
|
-
showResult?: boolean;
|
|
54
|
-
result?: string;
|
|
55
|
-
/**
|
|
56
|
-
* 首行状态文案。默认 `✅ 已完成`;goal 仍 active 的收尾卡必须换掉它——
|
|
57
|
-
* 本轮 turn 结束不等于任务结束。
|
|
58
|
-
*/
|
|
59
|
-
completedLabel?: string;
|
|
60
|
-
}): string;
|
|
File without changes
|
|
File without changes
|
|
File without changes
|