lark-remote 0.1.7 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/dist/bridge/approval-coordinator.d.ts +30 -1
  3. package/dist/bridge/approval-coordinator.js +93 -5
  4. package/dist/bridge/index.d.ts +36 -6
  5. package/dist/bridge/index.js +56 -10
  6. package/dist/card/approval-render.js +287 -48
  7. package/dist/card/card-shared.d.ts +6 -4
  8. package/dist/card/card-shared.js +6 -4
  9. package/dist/card/run-renderer.d.ts +7 -0
  10. package/dist/card/run-renderer.js +22 -11
  11. package/dist/config/index.d.ts +24 -1
  12. package/dist/config/index.js +34 -3
  13. package/dist/index.js +40 -16
  14. package/dist/order/alias-resolve.d.ts +6 -2
  15. package/dist/order/index.d.ts +9 -2
  16. package/dist/order/index.js +33 -0
  17. package/dist/router/card-action-payload.d.ts +30 -0
  18. package/dist/router/card-action-payload.js +39 -0
  19. package/dist/router/config/claude.js +8 -3
  20. package/dist/router/config/dsh.d.ts +51 -0
  21. package/dist/router/config/dsh.js +179 -0
  22. package/dist/router/config/index.d.ts +1 -1
  23. package/dist/router/config/index.js +4 -1
  24. package/dist/router/config/types.d.ts +6 -0
  25. package/dist/router/index.d.ts +39 -4
  26. package/dist/router/index.js +471 -90
  27. package/dist/router/utils.js +12 -2
  28. package/dist/runner/agent-choices-common.js +3 -0
  29. package/dist/runner/claude/runner.js +22 -0
  30. package/dist/runner/claude/session.d.ts +26 -0
  31. package/dist/runner/claude/session.js +105 -9
  32. package/dist/runner/codex/app-server/protocol-types.d.ts +35 -0
  33. package/dist/runner/codex/app-server/protocol-types.js +1 -1
  34. package/dist/runner/codex/app-server/runner.d.ts +10 -0
  35. package/dist/runner/codex/app-server/runner.js +56 -1
  36. package/dist/runner/codex/app-server/translator.d.ts +7 -0
  37. package/dist/runner/codex/app-server/translator.js +45 -0
  38. package/dist/runner/common/acp/protocol-types.d.ts +33 -0
  39. package/dist/runner/common/acp/protocol-types.js +2 -0
  40. package/dist/runner/common/connection-based-runner.js +10 -5
  41. package/dist/runner/dsh/client.d.ts +95 -0
  42. package/dist/runner/dsh/client.js +418 -0
  43. package/dist/runner/dsh/fake-dsh-server.d.ts +60 -0
  44. package/dist/runner/dsh/fake-dsh-server.js +156 -0
  45. package/dist/runner/dsh/index.d.ts +3 -0
  46. package/dist/runner/dsh/index.js +4 -0
  47. package/dist/runner/dsh/runner.d.ts +57 -0
  48. package/dist/runner/dsh/runner.js +211 -0
  49. package/dist/runner/dsh/translator.d.ts +28 -0
  50. package/dist/runner/dsh/translator.js +98 -0
  51. package/dist/runner/dsh/types.d.ts +155 -0
  52. package/dist/runner/dsh/types.js +14 -0
  53. package/dist/runner/index.d.ts +1 -0
  54. package/dist/runner/index.js +1 -0
  55. package/dist/runner/kimi/acp/runner.js +84 -1
  56. package/dist/runner/kimi/acp/translator.d.ts +14 -0
  57. package/dist/runner/kimi/acp/translator.js +108 -3
  58. package/dist/runner/pi/rpc/runner.d.ts +1 -7
  59. package/dist/runner/pi/rpc/runner.js +8 -12
  60. package/dist/runner/probe.js +6 -1
  61. package/dist/runner/question-common.d.ts +26 -0
  62. package/dist/runner/question-common.js +42 -0
  63. package/dist/runner/types.d.ts +94 -10
  64. package/dist/session/claude/sessions.js +37 -15
  65. package/dist/session/dsh/index.d.ts +1 -0
  66. package/dist/session/dsh/index.js +2 -0
  67. package/dist/session/dsh/sessions.d.ts +44 -0
  68. package/dist/session/dsh/sessions.js +252 -0
  69. package/dist/session/index.d.ts +1 -0
  70. package/dist/session/index.js +1 -0
  71. package/dist/workspace/index.d.ts +12 -0
  72. package/dist/workspace/index.js +28 -2
  73. package/package.json +2 -1
  74. package/dist/order/alias-store.d.ts +0 -38
  75. package/dist/order/alias-store.js +0 -106
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Fake DSH Web Host HTTP server for agent-dsh tests.
3
+ *
4
+ * Mimics the real DSH wire protocol (deepseek-harness apiproxy handler.ts):
5
+ * - POST /api/<method>: body {type:'client-request',rpcId,method,payload},
6
+ * response {type:'server-response',rpcId,result:{ok:true,value}|{ok:false,error}}.
7
+ * - SSE GET /api/events.mux: frames `data: {type:'server-request',rpcId,
8
+ * method:<frame.type>, payload:<frame>}\n\n`.
9
+ *
10
+ * The mux frame queue is consumed ACROSS connections (shared pointer): the
11
+ * first connection drains a prefix then closes; a reconnect drains the rest —
12
+ * which is exactly how a server that drops mid-turn is modelled. Unary
13
+ * handlers and the history value are registered by the test.
14
+ */
15
+ import { createServer } from 'node:http';
16
+ import { randomUUID } from 'node:crypto';
17
+ export class FakeDshServer {
18
+ server;
19
+ unaryHandlers = new Map();
20
+ muxFrames = [];
21
+ historyValue = { events: [] };
22
+ port = 0;
23
+ closedConnections = 0;
24
+ requests = [];
25
+ constructor() {
26
+ this.server = createServer((req, res) => {
27
+ const url = req.url ?? '';
28
+ const path = url.split('?')[0];
29
+ if (req.method === 'GET' && path === '/api/events.mux') {
30
+ this.closedConnections++;
31
+ this.handleMux(res);
32
+ return;
33
+ }
34
+ if (req.method !== 'POST' || !path.startsWith('/api/')) {
35
+ res.writeHead(404);
36
+ res.end('not found');
37
+ return;
38
+ }
39
+ let raw = '';
40
+ req.on('data', (c) => (raw += c));
41
+ req.on('end', () => {
42
+ let body = {};
43
+ try {
44
+ body = JSON.parse(raw);
45
+ }
46
+ catch {
47
+ res.writeHead(400);
48
+ res.end('bad request');
49
+ return;
50
+ }
51
+ const method = path.slice('/api/'.length);
52
+ if (body.type !== 'client-request' || !body.rpcId || body.method !== method) {
53
+ res.writeHead(400);
54
+ res.end('bad request');
55
+ return;
56
+ }
57
+ const rpcId = body.rpcId;
58
+ this.requests.push({ method, rpcId, payload: body.payload });
59
+ const handler = this.unaryHandlers.get(method);
60
+ let result;
61
+ if (handler) {
62
+ result = handler(body.payload, rpcId);
63
+ }
64
+ else if (method === 'session.history') {
65
+ result = { ok: true, value: this.historyValue };
66
+ }
67
+ else if (method === 'session.list') {
68
+ result = { ok: true, value: { items: [] } };
69
+ }
70
+ else {
71
+ result = { ok: true, value: {} };
72
+ }
73
+ res.writeHead(200, { 'content-type': 'application/json' });
74
+ res.end(JSON.stringify({ type: 'server-response', rpcId, result }));
75
+ });
76
+ });
77
+ }
78
+ get baseUrl() {
79
+ return `http://127.0.0.1:${this.port}`;
80
+ }
81
+ get connectionCount() {
82
+ return this.closedConnections;
83
+ }
84
+ async start() {
85
+ await new Promise((resolve) => this.server.listen(0, '127.0.0.1', resolve));
86
+ const addr = this.server.address();
87
+ this.port = addr.port;
88
+ }
89
+ async stop() {
90
+ await new Promise((resolve) => this.server.close(() => resolve()));
91
+ }
92
+ register(method, handler) {
93
+ this.unaryHandlers.set(method, handler);
94
+ }
95
+ setMuxFrames(frames) {
96
+ this.muxFrames = frames;
97
+ }
98
+ setHistoryValue(value) {
99
+ this.historyValue = value;
100
+ }
101
+ /** Synchronous unary dispatch (no HTTP) — used by tests that inject the
102
+ * reader's sync transport to avoid the spawnSync-in-process deadlock. */
103
+ dispatchSync(method, payload, rpcId = `rpc-${randomUUID()}`) {
104
+ this.requests.push({ method, rpcId, payload });
105
+ const handler = this.unaryHandlers.get(method);
106
+ let result;
107
+ if (handler) {
108
+ result = handler(payload, rpcId);
109
+ }
110
+ else if (method === 'session.history') {
111
+ result = { ok: true, value: this.historyValue };
112
+ }
113
+ else if (method === 'session.list') {
114
+ result = { ok: true, value: { items: [] } };
115
+ }
116
+ else {
117
+ result = { ok: true, value: {} };
118
+ }
119
+ return { type: 'server-response', rpcId, result };
120
+ }
121
+ handleMux(res) {
122
+ res.writeHead(200, {
123
+ 'content-type': 'text/event-stream',
124
+ 'cache-control': 'no-cache',
125
+ connection: 'keep-alive',
126
+ });
127
+ // Drain the SHARED script (shift): the first connection consumes a prefix
128
+ // then closes (a {close:true} marker or an exhausted queue); a reconnect
129
+ // continues from where it left off — the natural model of a mid-turn drop.
130
+ const timer = setInterval(() => {
131
+ const item = this.muxFrames.shift();
132
+ if (item === undefined) {
133
+ clearInterval(timer);
134
+ res.end();
135
+ return;
136
+ }
137
+ if ('close' in item && item.close === true) {
138
+ clearInterval(timer);
139
+ res.end();
140
+ return;
141
+ }
142
+ const frame = item;
143
+ res.write(`data: ${JSON.stringify({
144
+ type: 'server-request',
145
+ rpcId: `mux-${randomUUID()}`,
146
+ method: frame.type,
147
+ payload: frame,
148
+ })}\n\n`);
149
+ }, 5);
150
+ reqClose(res, timer);
151
+ }
152
+ }
153
+ function reqClose(res, timer) {
154
+ res.on('close', () => clearInterval(timer));
155
+ }
156
+ //# sourceMappingURL=fake-dsh-server.js.map
@@ -0,0 +1,3 @@
1
+ export { DshRunner, type DshRunnerOptions } from './runner.js';
2
+ export { DshClient, DshError } from './client.js';
3
+ export { DshTranslator, mapUsage } from './translator.js';
@@ -0,0 +1,4 @@
1
+ export { DshRunner } from './runner.js';
2
+ export { DshClient, DshError } from './client.js';
3
+ export { DshTranslator, mapUsage } from './translator.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,57 @@
1
+ /**
2
+ * DshRunner — workspace-lifetime runner bridging to the DSH Web Host
3
+ * (DeepSeek Harness) over plain HTTP. No local subprocess is spawned.
4
+ *
5
+ * Flow per run: session.create(cwd) (when no sessionId) → synthetic system/init
6
+ * (DSH has no init event) → open per-session mux subscription → session.prompt
7
+ * (mode:'queue') → consume subscription until turn/end → result event.
8
+ *
9
+ * stop() → session.cancel + abort the mux subscription. killOrphan /
10
+ * registerExitHandlers are no-ops (no local process). approval/requested is
11
+ * surfaced as a WARN log + an assistant text prompt pointing to the DSH Web UI
12
+ * (never silently parked — the turn stays live until the user resolves it in
13
+ * the UI or the turn is cancelled/times out).
14
+ */
15
+ import type { AgentEvent, AgentSessionReader, AgentStatusInfo, SpawnOptions } from '../types.js';
16
+ import type { AgentRunner } from '../types.js';
17
+ import { DshClient, type WebSocketFactory } from './client.js';
18
+ /** DSH selectModel provider(deepseek-official 是 rc.7 模型目录的 provider)。 */
19
+ export declare const DSH_MODEL_PROVIDER = "deepseek-official";
20
+ export interface DshRunnerOptions {
21
+ kind: 'dsh';
22
+ sessionReader: AgentSessionReader;
23
+ /** DSH Web Host base URL, e.g. http://127.0.0.1:3080. */
24
+ host?: string;
25
+ /** Session preset(agentPreset)固定于 session 创建时;undefined = 服务端默认。 */
26
+ agentPreset?: string;
27
+ /** Display model name. */
28
+ model?: string;
29
+ /** 推理强度(off/low/high/max);undefined = 服务端默认。 */
30
+ reasoningEffort?: string;
31
+ /** Injectable fetch for tests. */
32
+ fetchImpl?: typeof fetch;
33
+ /** Injectable WebSocket factory (defaults to global WebSocket). */
34
+ webSocketImpl?: WebSocketFactory;
35
+ }
36
+ export declare class DshRunner implements AgentRunner {
37
+ readonly kind: "dsh";
38
+ readonly sessionReader: AgentSessionReader;
39
+ readonly client: DshClient;
40
+ /** 用户配置的模型 ID(run 前 selectModel 对齐用);undefined = 服务端默认。 */
41
+ private readonly configuredModel;
42
+ private readonly agentPreset;
43
+ private readonly reasoningEffort;
44
+ private activeAbort;
45
+ private activeSessionId;
46
+ private running;
47
+ constructor(opts: DshRunnerOptions);
48
+ get isRunning(): boolean;
49
+ getStatusInfo(): AgentStatusInfo;
50
+ run(message: string, opts: SpawnOptions): AsyncGenerator<AgentEvent>;
51
+ stop(_opts?: {
52
+ immediate?: boolean;
53
+ }): Promise<void>;
54
+ killOrphan(): void;
55
+ registerExitHandlers(): void;
56
+ unregisterExitHandlers(): void;
57
+ }
@@ -0,0 +1,211 @@
1
+ /**
2
+ * DshRunner — workspace-lifetime runner bridging to the DSH Web Host
3
+ * (DeepSeek Harness) over plain HTTP. No local subprocess is spawned.
4
+ *
5
+ * Flow per run: session.create(cwd) (when no sessionId) → synthetic system/init
6
+ * (DSH has no init event) → open per-session mux subscription → session.prompt
7
+ * (mode:'queue') → consume subscription until turn/end → result event.
8
+ *
9
+ * stop() → session.cancel + abort the mux subscription. killOrphan /
10
+ * registerExitHandlers are no-ops (no local process). approval/requested is
11
+ * surfaced as a WARN log + an assistant text prompt pointing to the DSH Web UI
12
+ * (never silently parked — the turn stays live until the user resolves it in
13
+ * the UI or the turn is cancelled/times out).
14
+ */
15
+ import { DshClient } from './client.js';
16
+ import { DshTranslator } from './translator.js';
17
+ import { getLogger } from '../../logger/index.js';
18
+ /** DSH selectModel provider(deepseek-official 是 rc.7 模型目录的 provider)。 */
19
+ export const DSH_MODEL_PROVIDER = 'deepseek-official';
20
+ export class DshRunner {
21
+ kind = 'dsh';
22
+ sessionReader;
23
+ client;
24
+ /** 用户配置的模型 ID(run 前 selectModel 对齐用);undefined = 服务端默认。 */
25
+ configuredModel;
26
+ agentPreset;
27
+ reasoningEffort;
28
+ activeAbort = null;
29
+ activeSessionId = null;
30
+ running = false;
31
+ constructor(opts) {
32
+ this.sessionReader = opts.sessionReader;
33
+ this.configuredModel = opts.model;
34
+ this.agentPreset = opts.agentPreset;
35
+ this.reasoningEffort = opts.reasoningEffort;
36
+ this.client = new DshClient(opts.host ?? 'http://127.0.0.1:3080', opts.fetchImpl, opts.webSocketImpl);
37
+ }
38
+ get isRunning() {
39
+ return this.running;
40
+ }
41
+ getStatusInfo() {
42
+ const extras = { host: this.client.baseUrl };
43
+ if (this.agentPreset)
44
+ extras.preset = this.agentPreset;
45
+ if (this.reasoningEffort)
46
+ extras.reasoning = this.reasoningEffort;
47
+ return {
48
+ kind: this.kind,
49
+ model: this.configuredModel ?? 'DSH',
50
+ extras,
51
+ };
52
+ }
53
+ async *run(message, opts) {
54
+ if (this.running) {
55
+ throw new Error('DshRunner is already running');
56
+ }
57
+ const abort = new AbortController();
58
+ this.activeAbort = abort;
59
+ this.running = true;
60
+ let sessionId = opts.sessionId;
61
+ let terminalEmitted = false;
62
+ try {
63
+ // CC-03: session.create 必须包进同一 try/finally,否则创建失败(服务端不可达/报错)
64
+ // 会在进入旧 try 之前抛异常,finally 不执行 → running 恒 true、activeAbort 残留。
65
+ if (!sessionId) {
66
+ const created = await this.client.createSession({
67
+ cwd: opts.cwd,
68
+ ...(this.agentPreset ? { agentPreset: this.agentPreset } : {}),
69
+ });
70
+ sessionId = created.sessionId;
71
+ }
72
+ this.activeSessionId = sessionId;
73
+ // 模型/推理强度变更 = 保留 session,每次 run 前对齐(§4.3):
74
+ // 该调用同时会写服务端全局默认;失败仅告警不阻断 run(模型校验由 DSH 负责)。
75
+ if (this.configuredModel) {
76
+ try {
77
+ await this.client.selectModel({
78
+ sessionId,
79
+ provider: DSH_MODEL_PROVIDER,
80
+ model: this.configuredModel,
81
+ ...(this.reasoningEffort ? { reasoningEffort: this.reasoningEffort } : {}),
82
+ });
83
+ }
84
+ catch (err) {
85
+ getLogger().warn(`[dsh] selectModel failed for session ${sessionId} (run continues): ${err.message}`);
86
+ }
87
+ }
88
+ yield {
89
+ type: 'system',
90
+ subtype: 'init',
91
+ session_id: sessionId,
92
+ cwd: opts.cwd,
93
+ model: this.configuredModel ?? 'DSH',
94
+ };
95
+ try {
96
+ // Snapshot the pre-prompt high-water mark before mutating the session.
97
+ // DSH session.history returns the whole session, so starting from seq 0
98
+ // would replay the first completed turn on every follow-up prompt.
99
+ const prePromptSeq = await this.client.latestEventSeq(sessionId);
100
+ await this.client.unary('session.prompt', {
101
+ sessionId,
102
+ mode: 'queue',
103
+ content: [{ type: 'text', text: message }],
104
+ });
105
+ const translator = new DshTranslator();
106
+ try {
107
+ for await (const item of this.client.sessionEvents(sessionId, opts.cwd, abort.signal, prePromptSeq)) {
108
+ if (abort.signal.aborted)
109
+ break;
110
+ if (item.kind === 'approval') {
111
+ getLogger().warn(`[dsh] approval requested approvalId=${item.approvalId} tool=${item.toolName} — resolve in DSH Web UI`);
112
+ yield {
113
+ type: 'assistant',
114
+ message: {
115
+ content: [
116
+ {
117
+ type: 'text',
118
+ text: `⚠️ 需要授权(${item.toolName}):请在 DSH Web UI 处理 ${this.client.baseUrl}`,
119
+ },
120
+ ],
121
+ },
122
+ };
123
+ continue;
124
+ }
125
+ for (const ev of translator.eventToAgentEvents(item.event, sessionId)) {
126
+ yield ev;
127
+ if (ev.type === 'result') {
128
+ terminalEmitted = true;
129
+ return;
130
+ }
131
+ }
132
+ }
133
+ }
134
+ catch (err) {
135
+ if (abort.signal.aborted)
136
+ throw err;
137
+ getLogger().error(`[dsh] run consume error: ${err.message}`);
138
+ yield {
139
+ type: 'result',
140
+ subtype: 'error',
141
+ session_id: sessionId,
142
+ errorMessage: `DSH stream error: ${err.message}`,
143
+ };
144
+ terminalEmitted = true;
145
+ }
146
+ }
147
+ catch (err) {
148
+ if (!abort.signal.aborted) {
149
+ yield {
150
+ type: 'result',
151
+ subtype: 'error',
152
+ session_id: sessionId,
153
+ errorMessage: `DSH run failed: ${err.message}`,
154
+ };
155
+ terminalEmitted = true;
156
+ }
157
+ }
158
+ }
159
+ catch (err) {
160
+ // CC-03: createSession 等启动期失败也产出 error result(而非抛异常),
161
+ // 由本 catch 统一转 error result;state 清理在 finally。
162
+ if (!abort.signal.aborted) {
163
+ yield {
164
+ type: 'result',
165
+ subtype: 'error',
166
+ session_id: sessionId ?? '',
167
+ errorMessage: `DSH run failed: ${err.message}`,
168
+ };
169
+ terminalEmitted = true;
170
+ }
171
+ }
172
+ finally {
173
+ this.running = false;
174
+ this.activeSessionId = null;
175
+ this.activeAbort = null;
176
+ }
177
+ if (!terminalEmitted) {
178
+ yield {
179
+ type: 'result',
180
+ subtype: abort.signal.aborted ? 'interrupted' : 'error',
181
+ session_id: sessionId ?? '',
182
+ errorMessage: abort.signal.aborted ? 'run stopped' : 'turn ended without a terminal event',
183
+ };
184
+ }
185
+ }
186
+ async stop(_opts) {
187
+ const sessionId = this.activeSessionId;
188
+ const abort = this.activeAbort;
189
+ if (abort && !abort.signal.aborted)
190
+ abort.abort();
191
+ if (sessionId) {
192
+ try {
193
+ await this.client.unary('session.cancel', { sessionId });
194
+ getLogger().info(`[dsh] cancelled session ${sessionId}`);
195
+ }
196
+ catch (err) {
197
+ getLogger().warn(`[dsh] session.cancel failed: ${err.message}`);
198
+ }
199
+ }
200
+ }
201
+ killOrphan() {
202
+ // No local subprocess for DSH — nothing to reap.
203
+ }
204
+ registerExitHandlers() {
205
+ // No local subprocess for DSH — nothing to register.
206
+ }
207
+ unregisterExitHandlers() {
208
+ // No-op (see registerExitHandlers).
209
+ }
210
+ }
211
+ //# sourceMappingURL=runner.js.map
@@ -0,0 +1,28 @@
1
+ /**
2
+ * DshTranslator — maps DSH SessionEvents to AgentEvents.
3
+ *
4
+ * Per-turn stateful: accumulates usage from assistant/message.usage records and
5
+ * converts assistant/chunk deltas to assistant text/thinking events.
6
+ *
7
+ * Mapping (task spec + DSH SessionEventMap):
8
+ * assistant/chunk {chunk:{type:'text-delta',text}} → assistant text (incremental)
9
+ * assistant/chunk {chunk:{type:'reasoning-delta',text}} → assistant thinking (incremental)
10
+ * assistant/message {usage?} → records usage (no event)
11
+ * turn/end {reason.kind} → result:
12
+ * completed → success; aborted/interrupted → interrupted;
13
+ * blocked/error/max-tokens → error (errorMessage carries the reason)
14
+ *
15
+ * DSH TokenUsage: {inputTokens, outputTokens, cacheReadTokens?, cacheWriteTokens?}
16
+ * → ResultEvent.usage: input_tokens/output_tokens/cache_read_tokens/
17
+ * cache_creation_tokens (cacheWrite = cache creation) + total_tokens sum.
18
+ */
19
+ import type { AgentEvent, ResultEvent } from '../types.js';
20
+ import type { DshSessionEvent, DshTokenUsage } from './types.js';
21
+ export declare class DshTranslator {
22
+ private liveUsage?;
23
+ private hasUsage;
24
+ /** Translate one session event into AgentEvents (may be empty). */
25
+ eventToAgentEvents(ev: DshSessionEvent, sessionId: string): AgentEvent[];
26
+ }
27
+ /** Map DSH TokenUsage → ResultEvent.usage (cacheWrite = cache creation). */
28
+ export declare function mapUsage(u: DshTokenUsage): NonNullable<ResultEvent['usage']>;
@@ -0,0 +1,98 @@
1
+ /**
2
+ * DshTranslator — maps DSH SessionEvents to AgentEvents.
3
+ *
4
+ * Per-turn stateful: accumulates usage from assistant/message.usage records and
5
+ * converts assistant/chunk deltas to assistant text/thinking events.
6
+ *
7
+ * Mapping (task spec + DSH SessionEventMap):
8
+ * assistant/chunk {chunk:{type:'text-delta',text}} → assistant text (incremental)
9
+ * assistant/chunk {chunk:{type:'reasoning-delta',text}} → assistant thinking (incremental)
10
+ * assistant/message {usage?} → records usage (no event)
11
+ * turn/end {reason.kind} → result:
12
+ * completed → success; aborted/interrupted → interrupted;
13
+ * blocked/error/max-tokens → error (errorMessage carries the reason)
14
+ *
15
+ * DSH TokenUsage: {inputTokens, outputTokens, cacheReadTokens?, cacheWriteTokens?}
16
+ * → ResultEvent.usage: input_tokens/output_tokens/cache_read_tokens/
17
+ * cache_creation_tokens (cacheWrite = cache creation) + total_tokens sum.
18
+ */
19
+ export class DshTranslator {
20
+ liveUsage;
21
+ hasUsage = false;
22
+ /** Translate one session event into AgentEvents (may be empty). */
23
+ eventToAgentEvents(ev, sessionId) {
24
+ switch (ev.type) {
25
+ case 'assistant/chunk': {
26
+ const chunk = ev.data.chunk;
27
+ if (!chunk)
28
+ return [];
29
+ const ts = new Date(ev.time).toISOString();
30
+ if (chunk.type === 'text-delta' && chunk.text) {
31
+ return [
32
+ {
33
+ type: 'assistant',
34
+ message: { content: [{ type: 'text', text: chunk.text }] },
35
+ timestamp: ts,
36
+ },
37
+ ];
38
+ }
39
+ if (chunk.type === 'reasoning-delta' && chunk.text) {
40
+ return [
41
+ {
42
+ type: 'assistant',
43
+ message: { content: [{ type: 'thinking', thinking: chunk.text }] },
44
+ timestamp: ts,
45
+ },
46
+ ];
47
+ }
48
+ return [];
49
+ }
50
+ case 'assistant/message': {
51
+ const raw = ev.data.usage;
52
+ if (raw && typeof raw === 'object') {
53
+ this.liveUsage = raw;
54
+ this.hasUsage = true;
55
+ }
56
+ return [];
57
+ }
58
+ case 'turn/end': {
59
+ const reason = ev.data.reason?.kind;
60
+ const subtype = reason === 'completed'
61
+ ? 'success'
62
+ : reason === 'aborted' || reason === 'interrupted'
63
+ ? 'interrupted'
64
+ : 'error';
65
+ const result = {
66
+ type: 'result',
67
+ subtype,
68
+ session_id: sessionId,
69
+ timestamp: new Date(ev.time).toISOString(),
70
+ };
71
+ if (this.hasUsage && this.liveUsage) {
72
+ result.usage = mapUsage(this.liveUsage);
73
+ }
74
+ if (subtype === 'error') {
75
+ result.errorMessage = `turn ended with reason: ${reason}`;
76
+ }
77
+ return [result];
78
+ }
79
+ default:
80
+ return [];
81
+ }
82
+ }
83
+ }
84
+ /** Map DSH TokenUsage → ResultEvent.usage (cacheWrite = cache creation). */
85
+ export function mapUsage(u) {
86
+ const input = u.inputTokens ?? 0;
87
+ const output = u.outputTokens ?? 0;
88
+ const cacheRead = u.cacheReadTokens ?? 0;
89
+ const cacheWrite = u.cacheWriteTokens ?? 0;
90
+ return {
91
+ input_tokens: input,
92
+ output_tokens: output,
93
+ cache_read_tokens: cacheRead,
94
+ cache_creation_tokens: cacheWrite,
95
+ total_tokens: input + output + cacheRead + cacheWrite,
96
+ };
97
+ }
98
+ //# sourceMappingURL=translator.js.map