lark-remote 0.1.6 → 0.1.8
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 +51 -1
- package/README.en.md +9 -1
- package/README.md +14 -2
- package/dist/bridge/approval-coordinator.d.ts +71 -34
- package/dist/bridge/approval-coordinator.js +225 -41
- package/dist/bridge/inbound-media.d.ts +62 -0
- package/dist/bridge/inbound-media.js +282 -0
- package/dist/bridge/index.d.ts +97 -13
- package/dist/bridge/index.js +227 -87
- package/dist/bridge/queue-manager.d.ts +2 -1
- package/dist/bridge/queue-manager.js +1 -0
- package/dist/card/approval-render.js +294 -36
- package/dist/card/bash-card-session.d.ts +2 -24
- package/dist/card/bash-card-session.js +6 -73
- package/dist/card/bash-renderer.js +2 -3
- package/dist/card/card-budget.js +29 -75
- package/dist/card/card-session.d.ts +48 -0
- package/dist/card/card-session.js +86 -0
- package/dist/card/card-shared.d.ts +7 -16
- package/dist/card/card-shared.js +7 -30
- package/dist/card/run-card-session.d.ts +2 -55
- package/dist/card/run-card-session.js +6 -104
- package/dist/card/run-renderer.d.ts +7 -0
- package/dist/card/run-renderer.js +116 -238
- package/dist/card/run-state.d.ts +13 -0
- package/dist/card/run-state.js +25 -16
- package/dist/card/text-truncate.d.ts +13 -12
- package/dist/card/text-truncate.js +13 -63
- package/dist/common/fs.d.ts +8 -0
- package/dist/common/fs.js +17 -0
- package/dist/common/guards.d.ts +0 -20
- package/dist/common/guards.js +0 -43
- package/dist/common/sleep.d.ts +2 -0
- package/dist/common/sleep.js +5 -0
- package/dist/common/truncate.d.ts +39 -0
- package/dist/common/truncate.js +92 -0
- package/dist/config/codex-config.d.ts +1 -1
- package/dist/config/codex-config.js +1 -1
- package/dist/config/index.d.ts +112 -24
- package/dist/config/index.js +139 -37
- package/dist/connector/index.d.ts +69 -2
- package/dist/connector/index.js +107 -1
- package/dist/index.js +150 -118
- package/dist/instance-lock.js +4 -12
- package/dist/order/alias-resolve.d.ts +13 -0
- package/dist/order/alias-resolve.js +22 -0
- package/dist/order/alias-store.d.ts +38 -0
- package/dist/order/alias-store.js +106 -0
- package/dist/persistence/atomic-write.d.ts +8 -2
- package/dist/persistence/atomic-write.js +23 -7
- package/dist/restart.js +2 -1
- package/dist/router/card-action-payload.d.ts +30 -0
- package/dist/router/card-action-payload.js +39 -0
- package/dist/router/card-helpers.d.ts +63 -0
- package/dist/router/card-helpers.js +146 -0
- package/dist/router/config/claude.js +30 -1
- package/dist/router/config/codex.js +29 -50
- package/dist/router/config/common/model-patch.d.ts +22 -0
- package/dist/router/config/common/model-patch.js +26 -0
- package/dist/router/config/common/render.js +5 -4
- package/dist/router/config/dsh.d.ts +51 -0
- package/dist/router/config/dsh.js +179 -0
- package/dist/router/config/index.d.ts +12 -0
- package/dist/router/config/index.js +28 -1
- package/dist/router/config/kimi.d.ts +4 -1
- package/dist/router/config/kimi.js +27 -0
- package/dist/router/config/opencode.js +18 -5
- package/dist/router/config/pi.js +4 -5
- package/dist/router/config/types.d.ts +17 -2
- package/dist/router/index.d.ts +76 -22
- package/dist/router/index.js +479 -548
- package/dist/router/utils.d.ts +2 -0
- package/dist/router/utils.js +16 -2
- package/dist/runner/agent-choices-common.d.ts +16 -0
- package/dist/runner/agent-choices-common.js +34 -0
- package/dist/runner/claude/runner.d.ts +47 -10
- package/dist/runner/claude/runner.js +117 -46
- package/dist/runner/claude/session.d.ts +166 -0
- package/dist/runner/claude/session.js +768 -0
- package/dist/runner/codex/app-server/protocol-types.d.ts +66 -39
- package/dist/runner/codex/app-server/protocol-types.js +2 -13
- package/dist/runner/codex/app-server/runner.d.ts +52 -59
- package/dist/runner/codex/app-server/runner.js +216 -338
- package/dist/runner/codex/app-server/translator.d.ts +8 -21
- package/dist/runner/codex/app-server/translator.js +45 -18
- package/dist/runner/codex/index.d.ts +0 -2
- package/dist/runner/codex/index.js +0 -2
- package/dist/runner/common/acp/protocol-helpers.d.ts +14 -0
- package/dist/runner/common/acp/protocol-helpers.js +21 -0
- package/dist/runner/common/acp/protocol-types.d.ts +271 -0
- package/dist/runner/common/acp/protocol-types.js +77 -0
- package/dist/runner/common/connection-based-runner.d.ts +111 -0
- package/dist/runner/common/connection-based-runner.js +261 -0
- package/dist/runner/{codex/app-server → common/jsonrpc}/client.d.ts +14 -7
- package/dist/runner/{codex/app-server → common/jsonrpc}/client.js +27 -18
- package/dist/runner/common/jsonrpc/connection-manager.d.ts +125 -0
- package/dist/runner/{codex/app-server → common/jsonrpc}/connection-manager.js +63 -28
- package/dist/runner/{codex/app-server → common/jsonrpc}/transport.d.ts +5 -4
- package/dist/runner/{codex/app-server → common/jsonrpc}/transport.js +18 -19
- package/dist/runner/common/jsonrpc/types.d.ts +32 -0
- package/dist/runner/common/jsonrpc/types.js +9 -0
- package/dist/runner/common/process-stopper.d.ts +2 -1
- package/dist/runner/common/process-stopper.js +1 -0
- package/dist/runner/common/runner-utils.d.ts +6 -13
- package/dist/runner/common/runner-utils.js +6 -47
- package/dist/runner/common/spawning-runner.d.ts +24 -70
- package/dist/runner/common/spawning-runner.js +98 -152
- package/dist/runner/dsh/client.d.ts +95 -0
- package/dist/runner/dsh/client.js +418 -0
- package/dist/runner/dsh/fake-dsh-server.d.ts +60 -0
- package/dist/runner/dsh/fake-dsh-server.js +156 -0
- package/dist/runner/dsh/index.d.ts +3 -0
- package/dist/runner/dsh/index.js +4 -0
- package/dist/runner/dsh/runner.d.ts +57 -0
- package/dist/runner/dsh/runner.js +191 -0
- package/dist/runner/dsh/translator.d.ts +28 -0
- package/dist/runner/dsh/translator.js +98 -0
- package/dist/runner/dsh/types.d.ts +155 -0
- package/dist/runner/dsh/types.js +14 -0
- package/dist/runner/index.d.ts +5 -4
- package/dist/runner/index.js +5 -4
- package/dist/runner/kimi/acp/runner.d.ts +106 -0
- package/dist/runner/kimi/acp/runner.js +516 -0
- package/dist/runner/kimi/acp/translator.d.ts +138 -0
- package/dist/runner/kimi/acp/translator.js +441 -0
- package/dist/runner/kimi/index.d.ts +1 -1
- package/dist/runner/kimi/index.js +1 -1
- package/dist/runner/opencode/acp/index.d.ts +1 -0
- package/dist/runner/opencode/acp/index.js +2 -0
- package/dist/runner/opencode/acp/runner.d.ts +121 -0
- package/dist/runner/opencode/acp/runner.js +429 -0
- package/dist/runner/opencode/acp/translator.d.ts +119 -0
- package/dist/runner/opencode/acp/translator.js +345 -0
- package/dist/runner/opencode/index.d.ts +1 -1
- package/dist/runner/opencode/index.js +1 -1
- package/dist/runner/pi/index.d.ts +1 -1
- package/dist/runner/pi/index.js +1 -1
- package/dist/runner/pi/rpc/client.d.ts +52 -0
- package/dist/runner/pi/rpc/client.js +114 -0
- package/dist/runner/pi/rpc/index.d.ts +4 -0
- package/dist/runner/pi/rpc/index.js +4 -0
- package/dist/runner/pi/rpc/protocol-types.d.ts +127 -0
- package/dist/runner/pi/rpc/protocol-types.js +21 -0
- package/dist/runner/pi/rpc/runner.d.ts +74 -0
- package/dist/runner/pi/rpc/runner.js +190 -0
- package/dist/runner/pi/rpc/translator.d.ts +55 -0
- package/dist/runner/pi/rpc/translator.js +258 -0
- package/dist/runner/probe.js +6 -1
- package/dist/runner/question-common.d.ts +26 -0
- package/dist/runner/question-common.js +42 -0
- package/dist/runner/registry.d.ts +7 -8
- package/dist/runner/registry.js +3 -4
- package/dist/runner/resolve-agent-choices.js +18 -59
- package/dist/runner/sync-agent-choices.js +16 -67
- package/dist/runner/types.d.ts +97 -14
- package/dist/session/claude/session-index.js +2 -3
- package/dist/session/claude/session-reader.js +3 -6
- package/dist/session/claude/sessions.js +43 -28
- package/dist/session/codex/rollout-reader.d.ts +2 -2
- package/dist/session/codex/rollout-reader.js +14 -17
- package/dist/session/codex/sessions.d.ts +1 -1
- package/dist/session/codex/sessions.js +2 -2
- package/dist/session/common/content-blocks.d.ts +7 -0
- package/dist/session/common/content-blocks.js +13 -9
- package/dist/session/common/jsonl.d.ts +8 -25
- package/dist/session/common/jsonl.js +126 -261
- package/dist/session/common/pagination.d.ts +28 -0
- package/dist/session/common/pagination.js +33 -0
- package/dist/session/dsh/index.d.ts +1 -0
- package/dist/session/dsh/index.js +2 -0
- package/dist/session/dsh/sessions.d.ts +39 -0
- package/dist/session/dsh/sessions.js +237 -0
- package/dist/session/index.d.ts +1 -0
- package/dist/session/index.js +1 -0
- package/dist/session/kimi/sessions.d.ts +34 -1
- package/dist/session/kimi/sessions.js +208 -98
- package/dist/session/opencode/sessions.d.ts +17 -3
- package/dist/session/opencode/sessions.js +29 -29
- package/dist/session/pi/sessions.d.ts +5 -1
- package/dist/session/pi/sessions.js +12 -16
- package/dist/session/session-store.d.ts +11 -1
- package/dist/session/session-store.js +75 -178
- package/dist/update/index.d.ts +2 -2
- package/dist/update/index.js +2 -2
- package/dist/update/install.d.ts +20 -2
- package/dist/update/install.js +51 -7
- package/dist/workspace/index.d.ts +4 -0
- package/dist/workspace/index.js +8 -1
- package/package.json +2 -1
- package/dist/runner/codex/app-server/connection-manager.d.ts +0 -56
- package/dist/runner/codex/argv.d.ts +0 -33
- package/dist/runner/codex/argv.js +0 -40
- package/dist/runner/codex/factory.d.ts +0 -29
- package/dist/runner/codex/factory.js +0 -39
- package/dist/runner/codex/jsonl.d.ts +0 -37
- package/dist/runner/codex/jsonl.js +0 -221
- package/dist/runner/codex/runner.d.ts +0 -66
- package/dist/runner/codex/runner.js +0 -129
- package/dist/runner/common/exec-translator.d.ts +0 -73
- package/dist/runner/common/exec-translator.js +0 -105
- package/dist/runner/kimi/jsonl.d.ts +0 -28
- package/dist/runner/kimi/jsonl.js +0 -143
- package/dist/runner/kimi/runner.d.ts +0 -49
- package/dist/runner/kimi/runner.js +0 -105
- package/dist/runner/opencode/argv.d.ts +0 -32
- package/dist/runner/opencode/argv.js +0 -35
- package/dist/runner/opencode/jsonl.d.ts +0 -68
- package/dist/runner/opencode/jsonl.js +0 -295
- package/dist/runner/opencode/runner.d.ts +0 -61
- package/dist/runner/opencode/runner.js +0 -93
- package/dist/runner/pi/jsonl.d.ts +0 -128
- package/dist/runner/pi/jsonl.js +0 -249
- package/dist/runner/pi/runner.d.ts +0 -36
- package/dist/runner/pi/runner.js +0 -76
- package/dist/session/common/catch-up.d.ts +0 -12
- package/dist/session/common/catch-up.js +0 -31
package/dist/card/run-state.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { keepTail } from '../common/truncate.js';
|
|
1
2
|
const MAX_REASONING_CHARS = 4_000;
|
|
2
3
|
const MAX_TEXT_CHARS = 12_000;
|
|
3
4
|
const MAX_TOOL_DETAIL_CHARS = 3_000;
|
|
@@ -64,6 +65,7 @@ export function reduceRunState(state, event) {
|
|
|
64
65
|
if (event.type === 'approval_expired') {
|
|
65
66
|
return {
|
|
66
67
|
...state,
|
|
68
|
+
approvalExpired: true,
|
|
67
69
|
approvals: (state.approvals ?? []).map((a) => a.view.requestId === event.requestId ? { ...a, expired: true } : a),
|
|
68
70
|
};
|
|
69
71
|
}
|
|
@@ -100,14 +102,14 @@ function reduceTurnDiffEvent(state, event) {
|
|
|
100
102
|
blocks: keepLatestBlocks(upsertSnapshotBlock(blocks, (b) => b.kind === 'text' && b.itemId === event.itemId, () => ({
|
|
101
103
|
kind: 'text',
|
|
102
104
|
itemId: event.itemId,
|
|
103
|
-
content:
|
|
105
|
+
content: keepTail(event.text, MAX_TEXT_CHARS),
|
|
104
106
|
timestamp: ts,
|
|
105
107
|
...(complete ? { completedAt: ts } : {}),
|
|
106
108
|
}), (existing) => {
|
|
107
109
|
const textBlock = existing;
|
|
108
110
|
return {
|
|
109
111
|
...textBlock,
|
|
110
|
-
content:
|
|
112
|
+
content: keepTail(event.text, MAX_TEXT_CHARS),
|
|
111
113
|
...(complete ? { completedAt: ts } : {}),
|
|
112
114
|
};
|
|
113
115
|
}, !complete)),
|
|
@@ -120,7 +122,7 @@ function reduceTurnDiffEvent(state, event) {
|
|
|
120
122
|
blocks: keepLatestBlocks(upsertSnapshotBlock(next.blocks, (b) => b.kind === 'thinking' && b.itemId === event.itemId, () => ({
|
|
121
123
|
kind: 'thinking',
|
|
122
124
|
itemId: event.itemId,
|
|
123
|
-
content:
|
|
125
|
+
content: keepTail(event.reasoning, MAX_REASONING_CHARS),
|
|
124
126
|
active: !complete,
|
|
125
127
|
timestamp: ts,
|
|
126
128
|
...(complete ? { completedAt: ts } : {}),
|
|
@@ -128,7 +130,7 @@ function reduceTurnDiffEvent(state, event) {
|
|
|
128
130
|
const thinkingBlock = existing;
|
|
129
131
|
return {
|
|
130
132
|
...thinkingBlock,
|
|
131
|
-
content:
|
|
133
|
+
content: keepTail(event.reasoning, MAX_REASONING_CHARS),
|
|
132
134
|
active: !complete,
|
|
133
135
|
...(complete ? { completedAt: ts } : {}),
|
|
134
136
|
};
|
|
@@ -145,7 +147,7 @@ function reduceTurnDiffEvent(state, event) {
|
|
|
145
147
|
id: event.itemId,
|
|
146
148
|
name: 'command',
|
|
147
149
|
input: '',
|
|
148
|
-
output:
|
|
150
|
+
output: keepTail(event.toolOutput, MAX_TOOL_DETAIL_CHARS),
|
|
149
151
|
status: complete ? (event.toolStatus ?? 'ok') : 'running',
|
|
150
152
|
startedAt: ts,
|
|
151
153
|
...(complete ? { completedAt: ts } : {}),
|
|
@@ -154,7 +156,7 @@ function reduceTurnDiffEvent(state, event) {
|
|
|
154
156
|
kind: 'tool',
|
|
155
157
|
tool: {
|
|
156
158
|
...existing.tool,
|
|
157
|
-
output:
|
|
159
|
+
output: keepTail(event.toolOutput, MAX_TOOL_DETAIL_CHARS),
|
|
158
160
|
status: complete
|
|
159
161
|
? (event.toolStatus ?? 'ok')
|
|
160
162
|
: existing.tool.status,
|
|
@@ -165,7 +167,7 @@ function reduceTurnDiffEvent(state, event) {
|
|
|
165
167
|
};
|
|
166
168
|
}
|
|
167
169
|
if (event.plan !== undefined) {
|
|
168
|
-
const newPlan =
|
|
170
|
+
const newPlan = keepTail(event.plan, MAX_REASONING_CHARS * 2);
|
|
169
171
|
next = {
|
|
170
172
|
...next,
|
|
171
173
|
plan: newPlan,
|
|
@@ -298,7 +300,7 @@ function reduceAssistantEvent(state, event) {
|
|
|
298
300
|
...next.blocks.slice(0, -1),
|
|
299
301
|
{
|
|
300
302
|
kind: 'thinking',
|
|
301
|
-
content:
|
|
303
|
+
content: keepTail(last.content + '\n' + content.thinking, MAX_REASONING_CHARS),
|
|
302
304
|
active: true,
|
|
303
305
|
timestamp: last.timestamp ?? event.timestamp,
|
|
304
306
|
},
|
|
@@ -307,7 +309,7 @@ function reduceAssistantEvent(state, event) {
|
|
|
307
309
|
...next.blocks,
|
|
308
310
|
{
|
|
309
311
|
kind: 'thinking',
|
|
310
|
-
content:
|
|
312
|
+
content: keepTail(content.thinking, MAX_REASONING_CHARS),
|
|
311
313
|
active: true,
|
|
312
314
|
timestamp: event.timestamp,
|
|
313
315
|
},
|
|
@@ -326,7 +328,7 @@ function reduceAssistantEvent(state, event) {
|
|
|
326
328
|
...blocks.slice(0, -1),
|
|
327
329
|
{
|
|
328
330
|
kind: 'text',
|
|
329
|
-
content:
|
|
331
|
+
content: keepTail(last.content + content.text, MAX_TEXT_CHARS),
|
|
330
332
|
timestamp: last.timestamp ?? event.timestamp,
|
|
331
333
|
},
|
|
332
334
|
]
|
|
@@ -334,7 +336,7 @@ function reduceAssistantEvent(state, event) {
|
|
|
334
336
|
...blocks,
|
|
335
337
|
{
|
|
336
338
|
kind: 'text',
|
|
337
|
-
content:
|
|
339
|
+
content: keepTail(content.text, MAX_TEXT_CHARS),
|
|
338
340
|
timestamp: event.timestamp,
|
|
339
341
|
},
|
|
340
342
|
];
|
|
@@ -412,7 +414,7 @@ function reducePlanEvent(state, event) {
|
|
|
412
414
|
if (event.type !== 'plan')
|
|
413
415
|
return state;
|
|
414
416
|
const currentPlan = state.plan ?? '';
|
|
415
|
-
const newPlan =
|
|
417
|
+
const newPlan = keepTail(currentPlan + '\n' + event.plan, MAX_REASONING_CHARS * 2);
|
|
416
418
|
// Plan is a single evolving document — replace the existing plan block
|
|
417
419
|
// instead of appending a new one (avoids N overlapping blocks after N events).
|
|
418
420
|
const withoutOldPlan = state.blocks.filter((b) => b.kind !== 'plan');
|
|
@@ -458,12 +460,18 @@ export function finishRun(state, terminal, meta = {}) {
|
|
|
458
460
|
return state;
|
|
459
461
|
return { ...state, ...sanitizeMeta(meta) };
|
|
460
462
|
}
|
|
463
|
+
// interrupted 终态的中断来源:显式传入优先;否则从顶层持久标记推导
|
|
464
|
+
// (approval_expired 已置位但调用方未传 reason 的路径,如协调器过期自动
|
|
465
|
+
// cancel。原因必须在终态确定,不依赖调用方记得传参)。
|
|
466
|
+
const derivedReason = meta.interruptedReason ??
|
|
467
|
+
(terminal === 'interrupted' && state.approvalExpired === true ? 'approval_timeout' : undefined);
|
|
461
468
|
return {
|
|
462
469
|
...state,
|
|
463
470
|
terminal,
|
|
464
471
|
footer: null,
|
|
465
472
|
blocks: markThinkingInactive(state.blocks),
|
|
466
473
|
...sanitizeMeta(meta),
|
|
474
|
+
...(derivedReason !== undefined ? { interruptedReason: derivedReason } : {}),
|
|
467
475
|
};
|
|
468
476
|
}
|
|
469
477
|
/** Remove undefined/null meta fields to avoid overwriting existing state with nullish values. */
|
|
@@ -483,7 +491,11 @@ function stringifyUnknown(value) {
|
|
|
483
491
|
if (typeof value === 'string')
|
|
484
492
|
return value;
|
|
485
493
|
try {
|
|
486
|
-
|
|
494
|
+
// JSON.stringify(undefined) returns the value undefined, which would
|
|
495
|
+
// violate this function's string contract and crash callers that take
|
|
496
|
+
// .length on the result (2026-08-17 kimi ACP tool_call without rawInput
|
|
497
|
+
// → truncateDetail TypeError). Normalize to ''.
|
|
498
|
+
return JSON.stringify(value) ?? '';
|
|
487
499
|
}
|
|
488
500
|
catch {
|
|
489
501
|
return String(value);
|
|
@@ -507,9 +519,6 @@ function tryParseRecord(input) {
|
|
|
507
519
|
return null;
|
|
508
520
|
}
|
|
509
521
|
}
|
|
510
|
-
function keepLatest(value, maxChars) {
|
|
511
|
-
return value.length <= maxChars ? value : value.slice(-maxChars);
|
|
512
|
-
}
|
|
513
522
|
function keepLatestBlocks(blocks) {
|
|
514
523
|
return blocks.length <= MAX_BLOCKS ? blocks : blocks.slice(-MAX_BLOCKS);
|
|
515
524
|
}
|
|
@@ -1,20 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* UTF-8 byte-safe text truncation primitives.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* The core `fitUtf8` / `truncateUtf8` / `DEFAULT_TRUNCATE_SUFFIX` primitives
|
|
5
|
+
* live in `src/common/truncate.ts` (shared with session readers to avoid a
|
|
6
|
+
* card→session layer inversion); this module re-exports them and adds the
|
|
7
|
+
* card-specific markdown-table helpers.
|
|
8
8
|
*
|
|
9
9
|
* Why byte-safe: Feishu card size limits are measured in UTF-8 bytes, and a
|
|
10
10
|
* naive `str.slice(0, n)` can split a multi-byte CJK/emoji codepoint, yielding
|
|
11
11
|
* invalid UTF-8. `fitUtf8` iterates by Unicode codepoint (via `Array.from` /
|
|
12
12
|
* `for...of`) so it never breaks a surrogate pair or a multi-byte sequence.
|
|
13
13
|
*/
|
|
14
|
+
import { DEFAULT_TRUNCATE_SUFFIX, truncateUtf8 } from '../common/truncate.js';
|
|
15
|
+
export { DEFAULT_TRUNCATE_SUFFIX, truncateUtf8 };
|
|
14
16
|
/** Feishu 11310 limit: max markdown tables per card. */
|
|
15
17
|
export declare const FEISHU_MAX_TABLES = 5;
|
|
16
|
-
/**
|
|
17
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Feishu 单卡字节上限(安全阈值)。
|
|
20
|
+
*
|
|
21
|
+
* 供 run-renderer(流式卡)、card-budget(静态卡)、bash-renderer(bash 卡)
|
|
22
|
+
* 共用,避免三处各自定义后数值漂移。
|
|
23
|
+
*/
|
|
24
|
+
export declare const CARD_BUDGET_BYTES = 28000;
|
|
18
25
|
/**
|
|
19
26
|
* Max byte budget for a single session `tool_result` event's content when
|
|
20
27
|
* replaying an opencode session (L2 pre-fold). The static card budget enforcer
|
|
@@ -24,12 +31,6 @@ export declare const DEFAULT_TRUNCATE_SUFFIX = "\u2026\uFF08\u5DF2\u622A\u65AD\u
|
|
|
24
31
|
* 500KB file listing) never inflates the replay payload to megabytes.
|
|
25
32
|
*/
|
|
26
33
|
export declare const TOOL_RESULT_MAX_BYTES = 4000;
|
|
27
|
-
/**
|
|
28
|
-
* Truncate a UTF-8 string to fit within `maxBytes`, appending `suffix`
|
|
29
|
-
* (default `…(已截断)`) when truncation occurs. When `fromEnd` is true, keeps
|
|
30
|
-
* the tail and prepends the suffix (useful for "show the most recent N bytes").
|
|
31
|
-
*/
|
|
32
|
-
export declare function truncateUtf8(value: string, maxBytes: number, fromEnd?: boolean, suffix?: string): string;
|
|
33
34
|
/**
|
|
34
35
|
* Count the number of markdown tables in a string.
|
|
35
36
|
*
|
|
@@ -1,20 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* UTF-8 byte-safe text truncation primitives.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* The core `fitUtf8` / `truncateUtf8` / `DEFAULT_TRUNCATE_SUFFIX` primitives
|
|
5
|
+
* live in `src/common/truncate.ts` (shared with session readers to avoid a
|
|
6
|
+
* card→session layer inversion); this module re-exports them and adds the
|
|
7
|
+
* card-specific markdown-table helpers.
|
|
8
8
|
*
|
|
9
9
|
* Why byte-safe: Feishu card size limits are measured in UTF-8 bytes, and a
|
|
10
10
|
* naive `str.slice(0, n)` can split a multi-byte CJK/emoji codepoint, yielding
|
|
11
11
|
* invalid UTF-8. `fitUtf8` iterates by Unicode codepoint (via `Array.from` /
|
|
12
12
|
* `for...of`) so it never breaks a surrogate pair or a multi-byte sequence.
|
|
13
13
|
*/
|
|
14
|
+
import { DEFAULT_TRUNCATE_SUFFIX, truncateUtf8 } from '../common/truncate.js';
|
|
15
|
+
export { DEFAULT_TRUNCATE_SUFFIX, truncateUtf8 };
|
|
14
16
|
/** Feishu 11310 limit: max markdown tables per card. */
|
|
15
17
|
export const FEISHU_MAX_TABLES = 5;
|
|
16
|
-
/**
|
|
17
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Feishu 单卡字节上限(安全阈值)。
|
|
20
|
+
*
|
|
21
|
+
* 供 run-renderer(流式卡)、card-budget(静态卡)、bash-renderer(bash 卡)
|
|
22
|
+
* 共用,避免三处各自定义后数值漂移。
|
|
23
|
+
*/
|
|
24
|
+
export const CARD_BUDGET_BYTES = 28_000;
|
|
18
25
|
/**
|
|
19
26
|
* Max byte budget for a single session `tool_result` event's content when
|
|
20
27
|
* replaying an opencode session (L2 pre-fold). The static card budget enforcer
|
|
@@ -24,63 +31,6 @@ export const DEFAULT_TRUNCATE_SUFFIX = '…(已截断)';
|
|
|
24
31
|
* 500KB file listing) never inflates the replay payload to megabytes.
|
|
25
32
|
*/
|
|
26
33
|
export const TOOL_RESULT_MAX_BYTES = 4000;
|
|
27
|
-
/**
|
|
28
|
-
* Fit a UTF-8 string into `maxBytes` by iterating Unicode codepoints, never
|
|
29
|
-
* splitting a multi-byte sequence. When `fromEnd` is true, keeps the TAIL
|
|
30
|
-
* (useful for logs/tails); otherwise keeps the HEAD.
|
|
31
|
-
*
|
|
32
|
-
* P1-2 (2026-08-02): 单遍累计单字符字节数,替代逐字符 `Buffer.byteLength(result +
|
|
33
|
-
* char)` + `result += char`(后者每轮新建并扫描不断增长的字符串,O(budget²))。
|
|
34
|
-
* 字节长度对拼接可加(byteLength(a+b) = byteLength(a) + byteLength(b)),故累计
|
|
35
|
-
* 与旧实现的逐字符判断逐字节等价;最后一次 slice/join 物化结果。混合 CJK/emoji
|
|
36
|
-
* 样本实测 ~150× 提速(33.7ms/次 → 0.2ms/次,见 tests/anchor/priority/p0-fit-utf8.test.ts)。
|
|
37
|
-
*/
|
|
38
|
-
function fitUtf8(value, maxBytes, fromEnd = false) {
|
|
39
|
-
if (fromEnd) {
|
|
40
|
-
// tail 分支:需要从末尾找截断起点,Array.from 一次物化(旧实现同样 Array.from().reverse(),
|
|
41
|
-
// 无内存回归);数组仅用于定位,之后 slice+join。
|
|
42
|
-
const codepoints = Array.from(value);
|
|
43
|
-
let bytes = 0;
|
|
44
|
-
let start = codepoints.length;
|
|
45
|
-
for (let i = codepoints.length - 1; i >= 0; i--) {
|
|
46
|
-
const cb = Buffer.byteLength(codepoints[i], 'utf8');
|
|
47
|
-
if (bytes + cb > maxBytes)
|
|
48
|
-
break;
|
|
49
|
-
bytes += cb;
|
|
50
|
-
start = i;
|
|
51
|
-
}
|
|
52
|
-
return codepoints.slice(start).join('');
|
|
53
|
-
}
|
|
54
|
-
// head 分支:惰性 for-of 迭代 + 字符串索引累计(旧实现同 O(1) 内存)。
|
|
55
|
-
// 不物化数组——opencode-sessions 会对 MB 级 tool_result 做 head 截断,
|
|
56
|
-
// Array.from 全量分配会引入不必要的瞬态内存。
|
|
57
|
-
let bytes = 0;
|
|
58
|
-
let end = 0;
|
|
59
|
-
for (const char of value) {
|
|
60
|
-
const cb = Buffer.byteLength(char, 'utf8');
|
|
61
|
-
if (bytes + cb > maxBytes)
|
|
62
|
-
break;
|
|
63
|
-
bytes += cb;
|
|
64
|
-
end += char.length; // for-of 产出完整码点;char.length 为 1 或 2(代理对),
|
|
65
|
-
// end 落在码点边界上,value.slice(0, end) 不会切分代理对
|
|
66
|
-
}
|
|
67
|
-
return value.slice(0, end);
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* Truncate a UTF-8 string to fit within `maxBytes`, appending `suffix`
|
|
71
|
-
* (default `…(已截断)`) when truncation occurs. When `fromEnd` is true, keeps
|
|
72
|
-
* the tail and prepends the suffix (useful for "show the most recent N bytes").
|
|
73
|
-
*/
|
|
74
|
-
export function truncateUtf8(value, maxBytes, fromEnd = false, suffix = DEFAULT_TRUNCATE_SUFFIX) {
|
|
75
|
-
if (Buffer.byteLength(value, 'utf8') <= maxBytes)
|
|
76
|
-
return value;
|
|
77
|
-
const suffixBytes = Buffer.byteLength(suffix, 'utf8');
|
|
78
|
-
const budget = maxBytes - suffixBytes;
|
|
79
|
-
if (budget <= 0)
|
|
80
|
-
return suffix;
|
|
81
|
-
const truncated = fitUtf8(value, budget, fromEnd);
|
|
82
|
-
return fromEnd ? `${suffix}\n${truncated}` : `${truncated}${suffix}`;
|
|
83
|
-
}
|
|
84
34
|
/**
|
|
85
35
|
* Count the number of markdown tables in a string.
|
|
86
36
|
*
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unlink a file, silently ignoring ENOENT and other errors.
|
|
3
|
+
*
|
|
4
|
+
* Use for cleanup paths (pid files, temp files, stale locks) where the
|
|
5
|
+
* file may already be gone or was never created — a missing file is not
|
|
6
|
+
* an error worth propagating.
|
|
7
|
+
*/
|
|
8
|
+
export declare function silentlyUnlink(path: string): void;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
/**
|
|
3
|
+
* Unlink a file, silently ignoring ENOENT and other errors.
|
|
4
|
+
*
|
|
5
|
+
* Use for cleanup paths (pid files, temp files, stale locks) where the
|
|
6
|
+
* file may already be gone or was never created — a missing file is not
|
|
7
|
+
* an error worth propagating.
|
|
8
|
+
*/
|
|
9
|
+
export function silentlyUnlink(path) {
|
|
10
|
+
try {
|
|
11
|
+
fs.unlinkSync(path);
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
/* ignore — file may not exist */
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=fs.js.map
|
package/dist/common/guards.d.ts
CHANGED
|
@@ -11,27 +11,7 @@
|
|
|
11
11
|
* Used as a prerequisite before accessing string-keyed properties.
|
|
12
12
|
*/
|
|
13
13
|
export declare function isRecord(v: unknown): v is Record<string, unknown>;
|
|
14
|
-
/**
|
|
15
|
-
* Narrows `v` to `Record<string, unknown>` when it is a non-null object,
|
|
16
|
-
* otherwise returns `undefined`.
|
|
17
|
-
*/
|
|
18
|
-
export declare function recordValue(v: unknown): Record<string, unknown> | undefined;
|
|
19
14
|
/**
|
|
20
15
|
* Returns the string value when `v` is a `string`, otherwise `undefined`.
|
|
21
16
|
*/
|
|
22
17
|
export declare function stringValue(v: unknown): string | undefined;
|
|
23
|
-
/**
|
|
24
|
-
* Returns the number value when `v` is a `number`, otherwise `undefined`.
|
|
25
|
-
*/
|
|
26
|
-
export declare function numberValue(v: unknown): number | undefined;
|
|
27
|
-
/**
|
|
28
|
-
* Extracts a human-readable error message from an opaque error object.
|
|
29
|
-
*
|
|
30
|
-
* Priority:
|
|
31
|
-
* 1. `raw.message` (string)
|
|
32
|
-
* 2. `raw.error.message` (string, when `raw.error` is a record)
|
|
33
|
-
* 3. `raw.error.data.message` (string, AI SDK wraps provider errors as {error:{data:{message}}})
|
|
34
|
-
* 4. `raw.error` itself (when it is a string)
|
|
35
|
-
* 5. `fallback`
|
|
36
|
-
*/
|
|
37
|
-
export declare function extractErrorMessage(raw: Record<string, unknown>, fallback: string): string;
|
package/dist/common/guards.js
CHANGED
|
@@ -13,53 +13,10 @@
|
|
|
13
13
|
export function isRecord(v) {
|
|
14
14
|
return typeof v === 'object' && v !== null;
|
|
15
15
|
}
|
|
16
|
-
/**
|
|
17
|
-
* Narrows `v` to `Record<string, unknown>` when it is a non-null object,
|
|
18
|
-
* otherwise returns `undefined`.
|
|
19
|
-
*/
|
|
20
|
-
export function recordValue(v) {
|
|
21
|
-
return isRecord(v) ? v : undefined;
|
|
22
|
-
}
|
|
23
16
|
/**
|
|
24
17
|
* Returns the string value when `v` is a `string`, otherwise `undefined`.
|
|
25
18
|
*/
|
|
26
19
|
export function stringValue(v) {
|
|
27
20
|
return typeof v === 'string' ? v : undefined;
|
|
28
21
|
}
|
|
29
|
-
/**
|
|
30
|
-
* Returns the number value when `v` is a `number`, otherwise `undefined`.
|
|
31
|
-
*/
|
|
32
|
-
export function numberValue(v) {
|
|
33
|
-
return typeof v === 'number' ? v : undefined;
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Extracts a human-readable error message from an opaque error object.
|
|
37
|
-
*
|
|
38
|
-
* Priority:
|
|
39
|
-
* 1. `raw.message` (string)
|
|
40
|
-
* 2. `raw.error.message` (string, when `raw.error` is a record)
|
|
41
|
-
* 3. `raw.error.data.message` (string, AI SDK wraps provider errors as {error:{data:{message}}})
|
|
42
|
-
* 4. `raw.error` itself (when it is a string)
|
|
43
|
-
* 5. `fallback`
|
|
44
|
-
*/
|
|
45
|
-
export function extractErrorMessage(raw, fallback) {
|
|
46
|
-
const msg = stringValue(raw.message);
|
|
47
|
-
if (msg !== undefined)
|
|
48
|
-
return msg;
|
|
49
|
-
const err = raw.error;
|
|
50
|
-
if (isRecord(err)) {
|
|
51
|
-
const subMsg = stringValue(err.message);
|
|
52
|
-
if (subMsg !== undefined)
|
|
53
|
-
return subMsg;
|
|
54
|
-
const data = recordValue(err.data);
|
|
55
|
-
if (data) {
|
|
56
|
-
const dataMsg = stringValue(data.message);
|
|
57
|
-
if (dataMsg !== undefined)
|
|
58
|
-
return dataMsg;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
if (typeof err === 'string')
|
|
62
|
-
return err;
|
|
63
|
-
return fallback;
|
|
64
|
-
}
|
|
65
22
|
//# sourceMappingURL=guards.js.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared text truncation primitives.
|
|
3
|
+
*
|
|
4
|
+
* Centralizes the two truncation utilities previously split across the card
|
|
5
|
+
* layer (`card-shared.ts` char-based `truncate` and `card/text-truncate.ts`
|
|
6
|
+
* byte-safe `truncateUtf8`). Session readers import from here instead of the
|
|
7
|
+
* card layer (fixing the layer inversion), and the card layer re-exports for
|
|
8
|
+
* backward compatibility.
|
|
9
|
+
*/
|
|
10
|
+
/** Default truncation suffix, shared across all consumers for UX consistency. */
|
|
11
|
+
export declare const DEFAULT_TRUNCATE_SUFFIX = "\u2026\uFF08\u5DF2\u622A\u65AD\uFF09";
|
|
12
|
+
/**
|
|
13
|
+
* Max byte budget for a single session `tool_result` event's content when
|
|
14
|
+
* replaying an opencode session (L2 pre-fold). Pre-folding here bounds the
|
|
15
|
+
* in-memory `events[]` array and the intermediate card JSON so a single
|
|
16
|
+
* pathological tool_result never inflates the replay payload to megabytes.
|
|
17
|
+
*/
|
|
18
|
+
export declare const TOOL_RESULT_MAX_BYTES = 4000;
|
|
19
|
+
/**
|
|
20
|
+
* Fit a UTF-8 string into `maxBytes` by iterating Unicode codepoints, never
|
|
21
|
+
* splitting a multi-byte sequence. When `fromEnd` is true, keeps the TAIL
|
|
22
|
+
* (useful for logs/tails); otherwise keeps the HEAD.
|
|
23
|
+
*/
|
|
24
|
+
export declare function fitUtf8(value: string, maxBytes: number, fromEnd?: boolean): string;
|
|
25
|
+
/**
|
|
26
|
+
* Truncate a UTF-8 string to fit within `maxBytes`, appending `suffix`
|
|
27
|
+
* (default `…(已截断)`) when truncation occurs. When `fromEnd` is true, keeps
|
|
28
|
+
* the tail and prepends the suffix (useful for "show the most recent N bytes").
|
|
29
|
+
*/
|
|
30
|
+
export declare function truncateUtf8(value: string, maxBytes: number, fromEnd?: boolean, suffix?: string): string;
|
|
31
|
+
interface TruncateOptions {
|
|
32
|
+
suffix?: string;
|
|
33
|
+
normalizeWhitespace?: boolean;
|
|
34
|
+
}
|
|
35
|
+
/** Char-based truncation with optional suffix / whitespace normalization. */
|
|
36
|
+
export declare function truncate(str: string, max: number, options?: TruncateOptions): string;
|
|
37
|
+
/** Char-level tail keep (used for store-time caps, e.g. bash output). */
|
|
38
|
+
export declare function keepTail(value: string, maxChars: number): string;
|
|
39
|
+
export {};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared text truncation primitives.
|
|
3
|
+
*
|
|
4
|
+
* Centralizes the two truncation utilities previously split across the card
|
|
5
|
+
* layer (`card-shared.ts` char-based `truncate` and `card/text-truncate.ts`
|
|
6
|
+
* byte-safe `truncateUtf8`). Session readers import from here instead of the
|
|
7
|
+
* card layer (fixing the layer inversion), and the card layer re-exports for
|
|
8
|
+
* backward compatibility.
|
|
9
|
+
*/
|
|
10
|
+
/** Default truncation suffix, shared across all consumers for UX consistency. */
|
|
11
|
+
export const DEFAULT_TRUNCATE_SUFFIX = '…(已截断)';
|
|
12
|
+
/**
|
|
13
|
+
* Max byte budget for a single session `tool_result` event's content when
|
|
14
|
+
* replaying an opencode session (L2 pre-fold). Pre-folding here bounds the
|
|
15
|
+
* in-memory `events[]` array and the intermediate card JSON so a single
|
|
16
|
+
* pathological tool_result never inflates the replay payload to megabytes.
|
|
17
|
+
*/
|
|
18
|
+
export const TOOL_RESULT_MAX_BYTES = 4000;
|
|
19
|
+
/**
|
|
20
|
+
* Fit a UTF-8 string into `maxBytes` by iterating Unicode codepoints, never
|
|
21
|
+
* splitting a multi-byte sequence. When `fromEnd` is true, keeps the TAIL
|
|
22
|
+
* (useful for logs/tails); otherwise keeps the HEAD.
|
|
23
|
+
*/
|
|
24
|
+
export function fitUtf8(value, maxBytes, fromEnd = false) {
|
|
25
|
+
if (fromEnd) {
|
|
26
|
+
const codepoints = Array.from(value);
|
|
27
|
+
let bytes = 0;
|
|
28
|
+
let start = codepoints.length;
|
|
29
|
+
for (let i = codepoints.length - 1; i >= 0; i--) {
|
|
30
|
+
const cb = Buffer.byteLength(codepoints[i], 'utf8');
|
|
31
|
+
if (bytes + cb > maxBytes)
|
|
32
|
+
break;
|
|
33
|
+
bytes += cb;
|
|
34
|
+
start = i;
|
|
35
|
+
}
|
|
36
|
+
return codepoints.slice(start).join('');
|
|
37
|
+
}
|
|
38
|
+
// head 分支:惰性 for-of 迭代 + 字符串索引累计(O(1) 内存)。
|
|
39
|
+
let bytes = 0;
|
|
40
|
+
let end = 0;
|
|
41
|
+
for (const char of value) {
|
|
42
|
+
const cb = Buffer.byteLength(char, 'utf8');
|
|
43
|
+
if (bytes + cb > maxBytes)
|
|
44
|
+
break;
|
|
45
|
+
bytes += cb;
|
|
46
|
+
end += char.length;
|
|
47
|
+
}
|
|
48
|
+
return value.slice(0, end);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Truncate a UTF-8 string to fit within `maxBytes`, appending `suffix`
|
|
52
|
+
* (default `…(已截断)`) when truncation occurs. When `fromEnd` is true, keeps
|
|
53
|
+
* the tail and prepends the suffix (useful for "show the most recent N bytes").
|
|
54
|
+
*/
|
|
55
|
+
export function truncateUtf8(value, maxBytes, fromEnd = false, suffix = DEFAULT_TRUNCATE_SUFFIX) {
|
|
56
|
+
if (Buffer.byteLength(value, 'utf8') <= maxBytes)
|
|
57
|
+
return value;
|
|
58
|
+
const suffixBytes = Buffer.byteLength(suffix, 'utf8');
|
|
59
|
+
const budget = maxBytes - suffixBytes;
|
|
60
|
+
if (budget <= 0)
|
|
61
|
+
return suffix;
|
|
62
|
+
const truncated = fitUtf8(value, budget, fromEnd);
|
|
63
|
+
return fromEnd ? `${suffix}\n${truncated}` : `${truncated}${suffix}`;
|
|
64
|
+
}
|
|
65
|
+
/** Char-based truncation with optional suffix / whitespace normalization. */
|
|
66
|
+
export function truncate(str, max, options) {
|
|
67
|
+
const suffix = options?.suffix ?? '…';
|
|
68
|
+
const normalize = options?.normalizeWhitespace ?? false;
|
|
69
|
+
let s = str;
|
|
70
|
+
if (normalize)
|
|
71
|
+
s = s.replace(/\s+/g, ' ').trim();
|
|
72
|
+
if (s.length <= max)
|
|
73
|
+
return s;
|
|
74
|
+
const suffixLen = suffix.length;
|
|
75
|
+
const budget = max - suffixLen;
|
|
76
|
+
if (budget <= 0)
|
|
77
|
+
return suffix;
|
|
78
|
+
// Iterate by Unicode codepoint (for...of) so the cut never lands inside a
|
|
79
|
+
// surrogate pair (P2-31 anchor: must not emit a lone surrogate).
|
|
80
|
+
let end = 0;
|
|
81
|
+
for (const ch of s) {
|
|
82
|
+
if (end + ch.length > budget)
|
|
83
|
+
break;
|
|
84
|
+
end += ch.length; // 1 (BMP) or 2 (surrogate pair) — slice lands on a boundary
|
|
85
|
+
}
|
|
86
|
+
return s.slice(0, end) + suffix;
|
|
87
|
+
}
|
|
88
|
+
/** Char-level tail keep (used for store-time caps, e.g. bash output). */
|
|
89
|
+
export function keepTail(value, maxChars) {
|
|
90
|
+
return value.length <= maxChars ? value : value.slice(-maxChars);
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=truncate.js.map
|
|
@@ -80,7 +80,7 @@ export declare function getCodexCatalogModels(codexHome?: string): BundledModelI
|
|
|
80
80
|
/**
|
|
81
81
|
* Test utility: clear the catalog cache. Tests that exercise the bundled
|
|
82
82
|
* command path (`--bundled`) directly use `invalidateCodexBundledTestCache`
|
|
83
|
-
* from `codex-bundled-test-helpers.ts`.
|
|
83
|
+
* from `tests/lib/codex-bundled-test-helpers.ts`.
|
|
84
84
|
*/
|
|
85
85
|
export declare function invalidateCodexBundledCache(): void;
|
|
86
86
|
/**
|
|
@@ -208,7 +208,7 @@ export function getCodexCatalogModels(codexHome) {
|
|
|
208
208
|
/**
|
|
209
209
|
* Test utility: clear the catalog cache. Tests that exercise the bundled
|
|
210
210
|
* command path (`--bundled`) directly use `invalidateCodexBundledTestCache`
|
|
211
|
-
* from `codex-bundled-test-helpers.ts`.
|
|
211
|
+
* from `tests/lib/codex-bundled-test-helpers.ts`.
|
|
212
212
|
*/
|
|
213
213
|
export function invalidateCodexBundledCache() {
|
|
214
214
|
catalogCache = null;
|