handsoff 0.1.2-beta.1 → 0.1.2

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.
@@ -2,12 +2,30 @@
2
2
  var __create = Object.create;
3
3
  var __defProp = Object.defineProperty;
4
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
7
  var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name);
6
8
  var __typeError = (msg) => {
7
9
  throw TypeError(msg);
8
10
  };
9
11
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
10
12
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
13
+ var __esm = (fn, res) => function __init() {
14
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
15
+ };
16
+ var __export = (target, all) => {
17
+ for (var name in all)
18
+ __defProp(target, name, { get: all[name], enumerable: true });
19
+ };
20
+ var __copyProps = (to, from, except, desc) => {
21
+ if (from && typeof from === "object" || typeof from === "function") {
22
+ for (let key of __getOwnPropNames(from))
23
+ if (!__hasOwnProp.call(to, key) && key !== except)
24
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
25
+ }
26
+ return to;
27
+ };
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
11
29
  var __decoratorStart = (base) => [, , , __create(base?.[__knownSymbol("metadata")] ?? null)];
12
30
  var __decoratorStrings = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"];
13
31
  var __expectFn = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError("Function expected") : fn;
@@ -47,7 +65,312 @@ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read fr
47
65
  var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
48
66
  var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
49
67
 
68
+ // node_modules/.pnpm/tsup@8.5.1_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3/node_modules/tsup/assets/esm_shims.js
69
+ import path from "path";
70
+ import { fileURLToPath } from "url";
71
+ var init_esm_shims = __esm({
72
+ "node_modules/.pnpm/tsup@8.5.1_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3/node_modules/tsup/assets/esm_shims.js"() {
73
+ "use strict";
74
+ }
75
+ });
76
+
77
+ // src/adapters/channels/feishu/templates/message.ts
78
+ var message_exports = {};
79
+ __export(message_exports, {
80
+ buildMessageCard: () => buildMessageCard
81
+ });
82
+ function buildMessageCard(input) {
83
+ const elements = [];
84
+ elements.push({
85
+ tag: "markdown",
86
+ content: input.content,
87
+ text_align: "left",
88
+ text_size: "normal",
89
+ margin: "0px 0px 0px 0px"
90
+ });
91
+ if (input.meta) {
92
+ elements.push({ tag: "hr", margin: "0px 0px 0px 0px" });
93
+ elements.push({
94
+ tag: "column_set",
95
+ flex_mode: "stretch",
96
+ horizontal_spacing: "8px",
97
+ horizontal_align: "left",
98
+ columns: [
99
+ {
100
+ tag: "column",
101
+ width: "weighted",
102
+ elements: [
103
+ {
104
+ tag: "markdown",
105
+ content: input.meta,
106
+ text_align: "left",
107
+ text_size: "notation",
108
+ margin: "0px 0px 0px 0px",
109
+ icon: {
110
+ tag: "standard_icon",
111
+ token: "ccm-filter_outlined",
112
+ color: "grey"
113
+ }
114
+ }
115
+ ],
116
+ vertical_spacing: "8px",
117
+ horizontal_align: "left",
118
+ vertical_align: "center",
119
+ weight: 1
120
+ }
121
+ ],
122
+ margin: "0px 0px 0px 0px"
123
+ });
124
+ }
125
+ const card = {
126
+ schema: "2.0",
127
+ config: { update_multi: true },
128
+ body: {
129
+ direction: "vertical",
130
+ elements
131
+ },
132
+ header: {
133
+ title: {
134
+ tag: "plain_text",
135
+ content: input.title
136
+ },
137
+ subtitle: {
138
+ tag: "plain_text",
139
+ content: ""
140
+ },
141
+ template: "blue",
142
+ padding: "12px 8px 12px 8px"
143
+ }
144
+ };
145
+ return JSON.stringify(card);
146
+ }
147
+ var init_message = __esm({
148
+ "src/adapters/channels/feishu/templates/message.ts"() {
149
+ "use strict";
150
+ init_esm_shims();
151
+ }
152
+ });
153
+
154
+ // src/adapters/channels/feishu/templates/permission.ts
155
+ var permission_exports = {};
156
+ __export(permission_exports, {
157
+ buildPermissionCard: () => buildPermissionCard
158
+ });
159
+ function buildPermissionCard(input) {
160
+ const columns = input.buttons.map((btn) => {
161
+ const buttonEl = {
162
+ tag: "button",
163
+ text: { tag: "plain_text", content: btn.text },
164
+ type: btn.type,
165
+ width: "default",
166
+ size: "small",
167
+ behaviors: [
168
+ {
169
+ type: "callback",
170
+ value: { action: btn.value }
171
+ }
172
+ ]
173
+ };
174
+ if (btn.confirm) {
175
+ buttonEl.confirm = {
176
+ title: { tag: "plain_text", content: btn.confirm.title },
177
+ text: { tag: "plain_text", content: btn.confirm.text }
178
+ };
179
+ }
180
+ return {
181
+ tag: "column",
182
+ width: "auto",
183
+ elements: [buttonEl],
184
+ padding: "0px 0px 0px 0px",
185
+ direction: "horizontal",
186
+ horizontal_spacing: "4px",
187
+ vertical_spacing: "4px",
188
+ horizontal_align: "center",
189
+ vertical_align: "center",
190
+ margin: "0px 0px 0px 0px"
191
+ };
192
+ });
193
+ const card = {
194
+ schema: "2.0",
195
+ config: { update_multi: true },
196
+ body: {
197
+ direction: "vertical",
198
+ elements: [
199
+ {
200
+ tag: "markdown",
201
+ content: input.content,
202
+ text_align: "left",
203
+ text_size: "normal",
204
+ margin: "0px 0px 0px 0px"
205
+ },
206
+ { tag: "hr", margin: "0px 0px 0px 0px" },
207
+ {
208
+ tag: "column_set",
209
+ horizontal_spacing: "4px",
210
+ horizontal_align: "left",
211
+ columns,
212
+ margin: "0px 0px 0px 0px"
213
+ }
214
+ ]
215
+ },
216
+ header: {
217
+ title: { tag: "plain_text", content: input.title },
218
+ subtitle: { tag: "plain_text", content: "" },
219
+ template: "blue",
220
+ padding: "12px 8px 12px 8px"
221
+ }
222
+ };
223
+ return JSON.stringify(card);
224
+ }
225
+ var init_permission = __esm({
226
+ "src/adapters/channels/feishu/templates/permission.ts"() {
227
+ "use strict";
228
+ init_esm_shims();
229
+ }
230
+ });
231
+
232
+ // src/adapters/channels/feishu/templates/question.ts
233
+ var question_exports = {};
234
+ __export(question_exports, {
235
+ buildQuestionCard: () => buildQuestionCard
236
+ });
237
+ function buildQuestionCard(input) {
238
+ const elements = [
239
+ {
240
+ tag: "markdown",
241
+ content: input.content,
242
+ text_align: "left",
243
+ text_size: "normal",
244
+ margin: "0px 0px 0px 0px"
245
+ },
246
+ { tag: "hr", margin: "0px 0px 0px 0px" }
247
+ ];
248
+ if (input.mode === "single") {
249
+ const columns = input.items.map((item) => ({
250
+ tag: "column",
251
+ width: "auto",
252
+ elements: [
253
+ {
254
+ tag: "button",
255
+ text: { tag: "plain_text", content: item.label },
256
+ hover_tips: { tag: "plain_text", content: item.label },
257
+ type: "primary",
258
+ width: "default",
259
+ size: "small",
260
+ behaviors: [
261
+ {
262
+ type: "callback",
263
+ value: { action: "select", optionId: item.id, requestId: input.requestId }
264
+ }
265
+ ]
266
+ }
267
+ ],
268
+ padding: "0px 0px 0px 0px",
269
+ direction: "horizontal",
270
+ horizontal_spacing: "4px",
271
+ vertical_spacing: "4px",
272
+ horizontal_align: "center",
273
+ vertical_align: "center",
274
+ margin: "0px 0px 0px 0px"
275
+ }));
276
+ elements.push({
277
+ tag: "column_set",
278
+ horizontal_spacing: "4px",
279
+ horizontal_align: "left",
280
+ columns,
281
+ margin: "0px 0px 0px 0px"
282
+ });
283
+ } else {
284
+ const checkerElements = input.items.map((item) => ({
285
+ tag: "checker",
286
+ element_id: `checker_${item.id}`,
287
+ name: `checker_${item.id}`,
288
+ checked: false,
289
+ text: { tag: "plain_text", content: item.label },
290
+ behaviors: [
291
+ {
292
+ type: "callback",
293
+ value: { action: "check", optionId: item.id, requestId: input.requestId }
294
+ }
295
+ ]
296
+ }));
297
+ elements.push({
298
+ tag: "column_set",
299
+ horizontal_spacing: "4px",
300
+ horizontal_align: "left",
301
+ columns: [
302
+ {
303
+ tag: "column",
304
+ width: "weighted",
305
+ elements: checkerElements,
306
+ padding: "0px 0px 0px 0px",
307
+ direction: "horizontal",
308
+ horizontal_spacing: "4px",
309
+ vertical_spacing: "4px",
310
+ horizontal_align: "left",
311
+ vertical_align: "center",
312
+ margin: "0px 0px 0px 0px",
313
+ weight: 1
314
+ },
315
+ {
316
+ tag: "column",
317
+ width: "auto",
318
+ elements: [
319
+ {
320
+ tag: "button",
321
+ text: { tag: "plain_text", content: "Submit" },
322
+ type: "primary_filled",
323
+ width: "default",
324
+ size: "small",
325
+ margin: "0px 0px 0px 0px",
326
+ behaviors: [
327
+ {
328
+ type: "callback",
329
+ value: { action: "submit", requestId: input.requestId }
330
+ }
331
+ ]
332
+ }
333
+ ],
334
+ padding: "0px 0px 0px 0px",
335
+ direction: "vertical",
336
+ horizontal_spacing: "8px",
337
+ vertical_spacing: "8px",
338
+ horizontal_align: "right",
339
+ vertical_align: "center",
340
+ margin: "0px 0px 0px 0px"
341
+ }
342
+ ],
343
+ margin: "0px 0px 0px 0px"
344
+ });
345
+ }
346
+ const card = {
347
+ schema: "2.0",
348
+ config: { update_multi: true },
349
+ body: {
350
+ direction: "vertical",
351
+ elements
352
+ },
353
+ header: {
354
+ title: { tag: "plain_text", content: input.title },
355
+ subtitle: { tag: "plain_text", content: "" },
356
+ template: "blue",
357
+ padding: "12px 8px 12px 8px"
358
+ }
359
+ };
360
+ return JSON.stringify(card);
361
+ }
362
+ var init_question = __esm({
363
+ "src/adapters/channels/feishu/templates/question.ts"() {
364
+ "use strict";
365
+ init_esm_shims();
366
+ }
367
+ });
368
+
369
+ // src/gateway/process.ts
370
+ init_esm_shims();
371
+
50
372
  // src/shared/pidfile.ts
373
+ init_esm_shims();
51
374
  import { writeFileSync, readFileSync, existsSync, unlinkSync } from "fs";
52
375
  import { dirname } from "path";
53
376
  import { mkdirSync } from "fs";
@@ -68,6 +391,7 @@ function removePidFile(pidFilePath) {
68
391
  }
69
392
 
70
393
  // src/shared/logger.ts
394
+ init_esm_shims();
71
395
  import pino from "pino";
72
396
  import { mkdirSync as mkdirSync2, existsSync as existsSync2, appendFileSync } from "fs";
73
397
  import { dirname as dirname2 } from "path";
@@ -184,6 +508,7 @@ function getLogger(_level) {
184
508
  }
185
509
 
186
510
  // src/shared/session-logger.ts
511
+ init_esm_shims();
187
512
  var SessionLogger = class {
188
513
  bus;
189
514
  unsubscribeFns = [];
@@ -264,7 +589,11 @@ var SessionLogger = class {
264
589
  }
265
590
  };
266
591
 
592
+ // src/gateway/index.ts
593
+ init_esm_shims();
594
+
267
595
  // src/core/agent/AgentRegistry.ts
596
+ init_esm_shims();
268
597
  import { randomBytes } from "crypto";
269
598
  var AgentRegistry = class {
270
599
  registrations = /* @__PURE__ */ new Map();
@@ -294,9 +623,11 @@ var AgentRegistry = class {
294
623
  };
295
624
 
296
625
  // src/core/session/SessionManager.ts
626
+ init_esm_shims();
297
627
  import { randomUUID } from "crypto";
298
628
 
299
629
  // src/core/adapter/AgentAdapterRegistry.ts
630
+ init_esm_shims();
300
631
  var AgentAdapterRegistry = class _AgentAdapterRegistry {
301
632
  static instance = null;
302
633
  adapters = /* @__PURE__ */ new Map();
@@ -366,52 +697,90 @@ var AgentAdapterRegistry = class _AgentAdapterRegistry {
366
697
  };
367
698
 
368
699
  // src/core/session/Session.ts
700
+ init_esm_shims();
369
701
  var Session = class {
370
- id;
371
- agentType;
702
+ _id;
703
+ agent;
372
704
  token;
373
705
  metadata;
374
- adapter;
375
706
  createdAt;
707
+ _status = "active";
708
+ _updatedAt;
709
+ _transportState;
376
710
  cwd;
377
- status = "active";
378
- transportState;
711
+ model;
712
+ permissionMode;
713
+ slashCommands;
379
714
  constructor(options) {
380
- this.id = options.id;
381
- this.agentType = options.agentType;
715
+ this._id = options.id;
716
+ this.agent = options.adapter;
382
717
  this.token = options.token;
383
718
  this.metadata = options.metadata ?? {};
384
- this.adapter = options.adapter;
385
- this.transportState = options.transportState;
386
- this.createdAt = Date.now();
719
+ this._transportState = options.transportState;
720
+ this.createdAt = options.createdAt ?? Date.now();
721
+ this._updatedAt = this.createdAt;
387
722
  this.cwd = options.cwd;
388
723
  }
724
+ get id() {
725
+ return this._id;
726
+ }
727
+ setId(newId) {
728
+ this._id = newId;
729
+ }
730
+ get agentType() {
731
+ return this.agent.agentType;
732
+ }
733
+ get status() {
734
+ return this._status;
735
+ }
736
+ get updatedAt() {
737
+ return this._updatedAt;
738
+ }
739
+ get transportState() {
740
+ return this._transportState;
741
+ }
742
+ updateInfo(partial) {
743
+ if (partial.status !== void 0) this._status = partial.status;
744
+ if (partial.cwd !== void 0) this.cwd = partial.cwd;
745
+ if (partial.model !== void 0) this.model = partial.model;
746
+ if (partial.permissionMode !== void 0) this.permissionMode = partial.permissionMode;
747
+ if (partial.slashCommands !== void 0) this.slashCommands = partial.slashCommands;
748
+ this._updatedAt = Date.now();
749
+ }
389
750
  async sendCommand(command) {
390
- if (this.status !== "active") {
751
+ if (this._status !== "active") {
391
752
  throw new Error(`Cannot send command to closed session: ${this.id}`);
392
753
  }
393
- return this.adapter.sendCommand(this.transportState, command);
754
+ return this.agent.sendCommand(this._transportState, command);
394
755
  }
395
756
  async close() {
396
- if (this.status !== "active") {
757
+ if (this._status !== "active") {
397
758
  return;
398
759
  }
399
- await this.adapter.closeTransportState(this.transportState);
400
- this.status = "closed";
760
+ await this.agent.closeTransportState(this._transportState);
761
+ this._status = "closed";
401
762
  }
763
+ /** @deprecated Use `status` getter instead */
402
764
  getStatus() {
403
- return this.status;
765
+ return this._status;
766
+ }
767
+ /** @deprecated Use `toInfo()` instead */
768
+ toJSON() {
769
+ return this.toInfo();
404
770
  }
405
771
  toInfo() {
406
772
  return {
407
773
  id: this.id,
408
774
  agentType: this.agentType,
409
- status: this.status,
775
+ transportType: this.agent.transportType,
776
+ status: this._status,
410
777
  createdAt: this.createdAt,
411
- updatedAt: Date.now(),
778
+ updatedAt: this._updatedAt,
412
779
  token: this.token,
413
- startedAt: new Date(this.createdAt),
414
- cwd: this.cwd
780
+ cwd: this.cwd,
781
+ model: this.model,
782
+ permissionMode: this.permissionMode,
783
+ slashCommands: this.slashCommands
415
784
  };
416
785
  }
417
786
  };
@@ -434,12 +803,12 @@ var SessionManager = class {
434
803
  this.logger.debug({ sessionId, optionsCwd: options?.cwd, transportCwd, resolvedCwd: cwd }, "[SessionManager] Resolved cwd for session");
435
804
  const session = new Session({
436
805
  id: sessionId,
437
- agentType,
438
806
  adapter,
439
807
  transportState,
440
808
  token: options?.token,
441
809
  metadata: options?.metadata,
442
- cwd
810
+ cwd,
811
+ createdAt: options?.createdAt
443
812
  });
444
813
  this.sessions.set(sessionId, session);
445
814
  this.logger.info({ sessionId, agentType, cwd, token: options?.token?.slice(0, 8) }, "[Session] Created");
@@ -469,13 +838,53 @@ var SessionManager = class {
469
838
  getSessionCount() {
470
839
  return this.sessions.size;
471
840
  }
841
+ updateSessionInfo(sessionId, info) {
842
+ const session = this.sessions.get(sessionId);
843
+ if (!session) {
844
+ this.logger.warn({ sessionId }, "[SessionManager] updateSessionInfo: session not found");
845
+ return false;
846
+ }
847
+ session.updateInfo(info);
848
+ this.logger.debug({ sessionId, ...info }, "[SessionManager] Session info updated");
849
+ return true;
850
+ }
851
+ renameSessionId(oldId, newId) {
852
+ const session = this.sessions.get(oldId);
853
+ if (!session) {
854
+ this.logger.warn({ oldId, newId }, "[SessionManager] renameSessionId: session not found");
855
+ return false;
856
+ }
857
+ if (this.sessions.has(newId)) {
858
+ this.logger.warn({ oldId, newId }, "[SessionManager] renameSessionId: newId already exists");
859
+ return false;
860
+ }
861
+ session.setId(newId);
862
+ this.sessions.delete(oldId);
863
+ this.sessions.set(newId, session);
864
+ this.logger.info({ oldId, newId }, "[SessionManager] Session renamed");
865
+ return true;
866
+ }
472
867
  };
473
868
 
869
+ // src/gateway/events.ts
870
+ init_esm_shims();
871
+
872
+ // src/gateway/interaction-service.ts
873
+ init_esm_shims();
874
+
474
875
  // src/shared/permissions.ts
876
+ init_esm_shims();
475
877
  import { readFileSync as readFileSync2, existsSync as existsSync3 } from "fs";
476
878
  import { join } from "path";
477
879
 
880
+ // node_modules/.pnpm/minimatch@10.2.5/node_modules/minimatch/dist/esm/index.js
881
+ init_esm_shims();
882
+
883
+ // node_modules/.pnpm/brace-expansion@5.0.5/node_modules/brace-expansion/dist/esm/index.js
884
+ init_esm_shims();
885
+
478
886
  // node_modules/.pnpm/balanced-match@4.0.4/node_modules/balanced-match/dist/esm/index.js
887
+ init_esm_shims();
479
888
  var balanced = (a, b, str) => {
480
889
  const ma = a instanceof RegExp ? maybeMatch(a, str) : a;
481
890
  const mb = b instanceof RegExp ? maybeMatch(b, str) : b;
@@ -689,6 +1098,7 @@ function expand_(str, max, isTop) {
689
1098
  }
690
1099
 
691
1100
  // node_modules/.pnpm/minimatch@10.2.5/node_modules/minimatch/dist/esm/assert-valid-pattern.js
1101
+ init_esm_shims();
692
1102
  var MAX_PATTERN_LENGTH = 1024 * 64;
693
1103
  var assertValidPattern = (pattern) => {
694
1104
  if (typeof pattern !== "string") {
@@ -699,7 +1109,11 @@ var assertValidPattern = (pattern) => {
699
1109
  }
700
1110
  };
701
1111
 
1112
+ // node_modules/.pnpm/minimatch@10.2.5/node_modules/minimatch/dist/esm/ast.js
1113
+ init_esm_shims();
1114
+
702
1115
  // node_modules/.pnpm/minimatch@10.2.5/node_modules/minimatch/dist/esm/brace-expressions.js
1116
+ init_esm_shims();
703
1117
  var posixClasses = {
704
1118
  "[:alnum:]": ["\\p{L}\\p{Nl}\\p{Nd}", true],
705
1119
  "[:alpha:]": ["\\p{L}\\p{Nl}", true],
@@ -809,6 +1223,7 @@ var parseClass = (glob, position) => {
809
1223
  };
810
1224
 
811
1225
  // node_modules/.pnpm/minimatch@10.2.5/node_modules/minimatch/dist/esm/unescape.js
1226
+ init_esm_shims();
812
1227
  var unescape = (s, { windowsPathsNoEscape = false, magicalBraces = true } = {}) => {
813
1228
  if (magicalBraces) {
814
1229
  return windowsPathsNoEscape ? s.replace(/\[([^/\\])\]/g, "$1") : s.replace(/((?!\\).|^)\[([^/\\])\]/g, "$1$2").replace(/\\([^/])/g, "$1");
@@ -1461,6 +1876,7 @@ var AST = class {
1461
1876
  _a = AST;
1462
1877
 
1463
1878
  // node_modules/.pnpm/minimatch@10.2.5/node_modules/minimatch/dist/esm/escape.js
1879
+ init_esm_shims();
1464
1880
  var escape = (s, { windowsPathsNoEscape = false, magicalBraces = false } = {}) => {
1465
1881
  if (magicalBraces) {
1466
1882
  return windowsPathsNoEscape ? s.replace(/[?*()[\]{}]/g, "[$&]") : s.replace(/[?*()[\]\\{}]/g, "\\$&");
@@ -1527,11 +1943,11 @@ var qmarksTestNoExtDot = ([$0]) => {
1527
1943
  return (f) => f.length === len && f !== "." && f !== "..";
1528
1944
  };
1529
1945
  var defaultPlatform = typeof process === "object" && process ? typeof process.env === "object" && process.env && process.env.__MINIMATCH_TESTING_PLATFORM__ || process.platform : "posix";
1530
- var path = {
1946
+ var path2 = {
1531
1947
  win32: { sep: "\\" },
1532
1948
  posix: { sep: "/" }
1533
1949
  };
1534
- var sep = defaultPlatform === "win32" ? path.win32.sep : path.posix.sep;
1950
+ var sep = defaultPlatform === "win32" ? path2.win32.sep : path2.posix.sep;
1535
1951
  minimatch.sep = sep;
1536
1952
  var GLOBSTAR = /* @__PURE__ */ Symbol("globstar **");
1537
1953
  minimatch.GLOBSTAR = GLOBSTAR;
@@ -2340,7 +2756,7 @@ function matchesRule(toolName, toolInput, rule) {
2340
2756
  }
2341
2757
  if (toolName === "codex_patch_apply" && toolInput.fileChanges && typeof toolInput.fileChanges === "object") {
2342
2758
  const paths = Object.keys(toolInput.fileChanges);
2343
- return paths.some((path2) => matchesPattern(path2, pattern));
2759
+ return paths.some((path3) => matchesPattern(path3, pattern));
2344
2760
  }
2345
2761
  return false;
2346
2762
  }
@@ -2556,7 +2972,7 @@ var Gateway = class {
2556
2972
  agentType: session.agentType,
2557
2973
  payload: { ...options?.metadata ?? {}, cwd: session.cwd },
2558
2974
  metadata: {
2559
- transportType: session.adapter.transportType
2975
+ transportType: session.agent.transportType
2560
2976
  }
2561
2977
  },
2562
2978
  session
@@ -2577,6 +2993,12 @@ var Gateway = class {
2577
2993
  const session = this.sessionManager.getSession(sessionId);
2578
2994
  return session?.agentType;
2579
2995
  }
2996
+ /**
2997
+ * Get session info snapshot (including model/permissionMode/slashCommands)
2998
+ */
2999
+ getSessionInfo(sessionId) {
3000
+ return this.sessionManager.getSessionInfo(sessionId);
3001
+ }
2580
3002
  /**
2581
3003
  * List all sessions
2582
3004
  */
@@ -2667,13 +3089,20 @@ var Gateway = class {
2667
3089
  toolInput: request.toolInput,
2668
3090
  requestId: request.requestId
2669
3091
  },
2670
- metadata: { transportType: session.adapter.transportType }
3092
+ metadata: { transportType: session.agent.transportType }
2671
3093
  };
2672
3094
  return await this.interactionService.evaluate(envelope);
2673
3095
  }
3096
+ updateSessionInfo(sessionId, info) {
3097
+ this.sessionManager.updateSessionInfo(sessionId, info);
3098
+ }
3099
+ renameSessionId(oldId, newId) {
3100
+ return this.sessionManager.renameSessionId(oldId, newId);
3101
+ }
2674
3102
  };
2675
3103
 
2676
3104
  // src/gateway/publisher.ts
3105
+ init_esm_shims();
2677
3106
  var EventPublisher = class {
2678
3107
  constructor(bus, broadcastFn, persistence) {
2679
3108
  this.bus = bus;
@@ -2779,7 +3208,17 @@ var EventPublisher = class {
2779
3208
  }
2780
3209
  };
2781
3210
 
3211
+ // src/adapters/agent/claude/index.ts
3212
+ init_esm_shims();
3213
+
3214
+ // src/adapters/agent/claude/ClaudeHookAgentAdapter.ts
3215
+ init_esm_shims();
3216
+
3217
+ // src/core/agent/BaseAgentAdapter.ts
3218
+ init_esm_shims();
3219
+
2782
3220
  // src/core/agent/inbound-utils.ts
3221
+ init_esm_shims();
2783
3222
  function escapeRegExp(str) {
2784
3223
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2785
3224
  }
@@ -2805,6 +3244,7 @@ var BaseAgentAdapter = class {
2805
3244
  agentCommands = /* @__PURE__ */ new Map();
2806
3245
  eventHandler = null;
2807
3246
  unsubscribeFns = [];
3247
+ enricher;
2808
3248
  constructor(config) {
2809
3249
  this.bus = config.bus;
2810
3250
  this.bridge = config.bridge;
@@ -2872,6 +3312,30 @@ var BaseAgentAdapter = class {
2872
3312
  }
2873
3313
  this.bridge.emitEvent(envelope);
2874
3314
  }
3315
+ /**
3316
+ * Enrich the event envelope if a {@link TranscriptEnricher} is configured and
3317
+ * capable of handling this event type.
3318
+ *
3319
+ * Subclasses should call this in their async methods **before**
3320
+ * {@link emitEvent}. {@link emitEvent} remains synchronous by design because
3321
+ * downstream consumers (e.g. {@link TurnAggregator} callbacks) expect sync
3322
+ * execution. The async nature of enrichment means there may be an interleaving
3323
+ * gap between the call to this method and the subsequent emit.
3324
+ */
3325
+ async enrichIfNeeded(envelope) {
3326
+ if (!this.enricher?.canEnrich(envelope.type)) {
3327
+ return envelope;
3328
+ }
3329
+ try {
3330
+ return await this.enricher.enrich(envelope);
3331
+ } catch (err) {
3332
+ this.logger.warn(
3333
+ { sessionId: envelope.sessionId, eventType: envelope.type, error: err instanceof Error ? err.message : String(err) },
3334
+ "Transcript enrichment failed, emitting original envelope"
3335
+ );
3336
+ return envelope;
3337
+ }
3338
+ }
2875
3339
  getHelpText() {
2876
3340
  const lines = [
2877
3341
  `${this.agentType} commands:`,
@@ -2898,13 +3362,21 @@ var BaseAgentAdapter = class {
2898
3362
  this.bus.onInbound((msg) => this.handleInboundMessage(msg))
2899
3363
  );
2900
3364
  }
3365
+ /**
3366
+ * Resolve session ID from inbound message. Subclasses may override to
3367
+ * return a stable session ID based on transport-level identity (e.g.
3368
+ * Claude SDK session_id).
3369
+ */
3370
+ resolveSessionId(_message) {
3371
+ return void 0;
3372
+ }
2901
3373
  async handleInboundMessage(message) {
2902
3374
  const text = message.text.trim();
2903
3375
  this.logger.debug({ channel: message.channel, chatId: message.chatId, targetAgent: message.targetAgent }, "Inbound message received");
2904
3376
  if (message.systemCommand) return;
2905
3377
  if (message.targetAgent !== this.agentType) return;
2906
3378
  const parsed = parseInboundCommand(text, this.prefix);
2907
- const sessionId = `${this.agentType}-${message.chatId}`;
3379
+ const sessionId = this.resolveSessionId(message) ?? `${this.agentType}-${message.sender.id}`;
2908
3380
  if (!parsed) {
2909
3381
  if (!this.supportsInboundPrompt) {
2910
3382
  this.logger.debug({ sessionId, textPreview: text.slice(0, 60) }, "Inbound prompt ignored: adapter does not support user input");
@@ -2973,6 +3445,7 @@ Session ID: ${sessionId}` : `No active ${this.agentType} session for this chat.`
2973
3445
  };
2974
3446
 
2975
3447
  // src/core/agent/EventNormalizer.ts
3448
+ init_esm_shims();
2976
3449
  var EventNormalizer = class {
2977
3450
  static sessionStart(sessionId, agentType, transportType, payload, extraMetadata) {
2978
3451
  return this.build("session:start", sessionId, agentType, transportType, {
@@ -3003,7 +3476,8 @@ var EventNormalizer = class {
3003
3476
  isTimeout: t2.isTimeout
3004
3477
  })),
3005
3478
  durationMs: extra?.durationMs,
3006
- usage: extra?.usage
3479
+ usage: extra?.usage,
3480
+ transcriptPath: extra?.transcriptPath
3007
3481
  });
3008
3482
  }
3009
3483
  static toolExecuted(sessionId, agentType, transportType, tool) {
@@ -3106,7 +3580,8 @@ var EventNormalizer = class {
3106
3580
  ...common,
3107
3581
  reason: payload.reason,
3108
3582
  lastAssistantMessage: payload.last_assistant_message,
3109
- durationMs: typeof payload.duration === "number" ? payload.duration : void 0
3583
+ durationMs: typeof payload.duration === "number" ? payload.duration : void 0,
3584
+ transcriptPath: typeof payload.transcript_path === "string" ? payload.transcript_path : void 0
3110
3585
  }, { rawEventType: eventType });
3111
3586
  default:
3112
3587
  return null;
@@ -3128,6 +3603,7 @@ var EventNormalizer = class {
3128
3603
  };
3129
3604
 
3130
3605
  // src/core/agent/interaction-utils.ts
3606
+ init_esm_shims();
3131
3607
  async function evaluateInteraction(bridge, request) {
3132
3608
  const originalResponse = await bridge.evaluateInteraction(request);
3133
3609
  if (request.toolName === "AskUserQuestion" && (originalResponse.action === "select" || originalResponse.action === "input")) {
@@ -3146,6 +3622,118 @@ async function evaluateInteraction(bridge, request) {
3146
3622
  return originalResponse;
3147
3623
  }
3148
3624
 
3625
+ // src/adapters/agent/claude/jsonl-reader.ts
3626
+ init_esm_shims();
3627
+ import { readFile } from "fs/promises";
3628
+ import { resolve } from "path";
3629
+ async function readClaudeJSONL(transcriptPath) {
3630
+ let lastError;
3631
+ for (let attempt = 1; attempt <= 5; attempt++) {
3632
+ try {
3633
+ const content = await readFile(transcriptPath, "utf-8");
3634
+ const summary = parseTranscript(content);
3635
+ if (summary && (summary.lastAssistantMessage !== void 0 || summary.durationMs !== void 0)) {
3636
+ return summary;
3637
+ }
3638
+ if (summary) {
3639
+ getLogger().debug({ attempt, summary }, "Transcript read but incomplete, retrying...");
3640
+ }
3641
+ } catch (error2) {
3642
+ lastError = error2;
3643
+ }
3644
+ if (attempt < 5) {
3645
+ await new Promise((resolve3) => setTimeout(resolve3, 200));
3646
+ }
3647
+ }
3648
+ getLogger().warn({ error: lastError instanceof Error ? lastError.message : String(lastError), transcriptPath }, "Failed to read transcript or transcript incomplete");
3649
+ return void 0;
3650
+ }
3651
+ function resolveTranscriptPath(cwd, sessionId) {
3652
+ const projectDirName = cwd.replace(/[\\/]/g, "-");
3653
+ return resolve(process.env.HOME || "", ".claude", "projects", projectDirName, `${sessionId}.jsonl`);
3654
+ }
3655
+ function parseTranscript(content) {
3656
+ const lines = content.split("\n").filter((line) => line.trim().length > 0);
3657
+ let lastAssistantMessage;
3658
+ let thinking;
3659
+ let inputTokens;
3660
+ let outputTokens;
3661
+ let lastTextAssistant;
3662
+ let lastAnyAssistant;
3663
+ const userTimestamps = /* @__PURE__ */ new Map();
3664
+ const parentMap = /* @__PURE__ */ new Map();
3665
+ for (const line of lines) {
3666
+ let entry;
3667
+ try {
3668
+ entry = JSON.parse(line);
3669
+ } catch (parseError) {
3670
+ getLogger().warn(
3671
+ { error: parseError instanceof Error ? parseError.message : String(parseError), line: line.slice(0, 200) },
3672
+ "Skipping malformed transcript line"
3673
+ );
3674
+ continue;
3675
+ }
3676
+ const ts = typeof entry.timestamp === "string" ? new Date(entry.timestamp).getTime() : typeof entry.timestamp === "number" ? entry.timestamp : void 0;
3677
+ if (entry.uuid && typeof entry.parentUuid === "string") {
3678
+ parentMap.set(entry.uuid, entry.parentUuid);
3679
+ }
3680
+ if (entry.type === "user" && ts !== void 0 && entry.uuid) {
3681
+ userTimestamps.set(entry.uuid, ts);
3682
+ }
3683
+ if (entry.type === "assistant" && ts !== void 0) {
3684
+ const arr = Array.isArray(entry.message?.content) ? entry.message.content : [];
3685
+ const textBlock = arr.find((c) => c.type === "text");
3686
+ const thinkingBlock = arr.find((c) => c.type === "thinking");
3687
+ const assistantRef = { ts, uuid: entry.uuid, parentUuid: entry.parentUuid };
3688
+ lastAnyAssistant = assistantRef;
3689
+ if (textBlock && typeof textBlock.text === "string") {
3690
+ lastAssistantMessage = textBlock.text;
3691
+ lastTextAssistant = assistantRef;
3692
+ }
3693
+ if (thinkingBlock && typeof thinkingBlock.thinking === "string") {
3694
+ thinking = thinkingBlock.thinking;
3695
+ }
3696
+ if (textBlock) {
3697
+ const usage = entry.message?.usage;
3698
+ if (usage) {
3699
+ if (typeof usage.input_tokens === "number") inputTokens = usage.input_tokens;
3700
+ if (typeof usage.output_tokens === "number") outputTokens = usage.output_tokens;
3701
+ }
3702
+ }
3703
+ }
3704
+ }
3705
+ const durationMs = resolveDuration(lastTextAssistant, userTimestamps, parentMap) ?? resolveDuration(lastAnyAssistant, userTimestamps, parentMap);
3706
+ const summary = {};
3707
+ if (lastAssistantMessage !== void 0) summary.lastAssistantMessage = lastAssistantMessage;
3708
+ if (thinking !== void 0) summary.thinking = thinking;
3709
+ if (inputTokens !== void 0) summary.inputTokens = inputTokens;
3710
+ if (outputTokens !== void 0) summary.outputTokens = outputTokens;
3711
+ if (durationMs !== void 0) summary.durationMs = durationMs;
3712
+ return Object.keys(summary).length > 0 ? summary : void 0;
3713
+ }
3714
+ function resolveDuration(assistantRef, userTimestamps, parentMap) {
3715
+ if (!assistantRef) return void 0;
3716
+ if (assistantRef.parentUuid) {
3717
+ const directParentTs = userTimestamps.get(assistantRef.parentUuid);
3718
+ if (directParentTs !== void 0) {
3719
+ return assistantRef.ts - directParentTs;
3720
+ }
3721
+ }
3722
+ let currentUuid = assistantRef.uuid;
3723
+ const visited = /* @__PURE__ */ new Set();
3724
+ while (currentUuid && !visited.has(currentUuid)) {
3725
+ visited.add(currentUuid);
3726
+ const parentUuid = parentMap.get(currentUuid);
3727
+ if (!parentUuid) break;
3728
+ const parentTs = userTimestamps.get(parentUuid);
3729
+ if (parentTs !== void 0) {
3730
+ return assistantRef.ts - parentTs;
3731
+ }
3732
+ currentUuid = parentUuid;
3733
+ }
3734
+ return void 0;
3735
+ }
3736
+
3149
3737
  // src/adapters/agent/claude/ClaudeHookAgentAdapter.ts
3150
3738
  var ClaudeHookAgentAdapter = class extends BaseAgentAdapter {
3151
3739
  agentType = "claude";
@@ -3159,6 +3747,7 @@ var ClaudeHookAgentAdapter = class extends BaseAgentAdapter {
3159
3747
  constructor(config) {
3160
3748
  super(config);
3161
3749
  this.token = config.token ?? "";
3750
+ this.enricher = this;
3162
3751
  }
3163
3752
  // === Transport Lifecycle ===
3164
3753
  async createTransportState(opts) {
@@ -3182,16 +3771,22 @@ var ClaudeHookAgentAdapter = class extends BaseAgentAdapter {
3182
3771
  return;
3183
3772
  }
3184
3773
  if (eventType === "SessionStart") {
3185
- await this.bridge.ensureSession(sessionId, {
3186
- cwd: payload.cwd,
3187
- chatId: payload.chat_id,
3188
- channel: payload.channel,
3189
- metadata: {
3190
- rawEventType: eventType
3191
- }
3192
- }, this.agentType).catch((err) => {
3774
+ try {
3775
+ await this.bridge.ensureSession(sessionId, {
3776
+ cwd: payload.cwd,
3777
+ chatId: payload.chat_id,
3778
+ channel: payload.channel,
3779
+ metadata: {
3780
+ rawEventType: eventType
3781
+ }
3782
+ }, this.agentType);
3783
+ const model = typeof payload.model === "string" ? payload.model : void 0;
3784
+ if (model) {
3785
+ this.bridge.updateSessionInfo(sessionId, { model });
3786
+ }
3787
+ } catch (err) {
3193
3788
  this.logger.warn({ sessionId, error: err instanceof Error ? err.message : String(err) }, "ensureSession failed during SessionStart");
3194
- });
3789
+ }
3195
3790
  return;
3196
3791
  }
3197
3792
  const envelope = EventNormalizer.fromHookEvent(
@@ -3201,8 +3796,51 @@ var ClaudeHookAgentAdapter = class extends BaseAgentAdapter {
3201
3796
  this.transportType
3202
3797
  );
3203
3798
  if (!envelope) return;
3204
- this.emitEvent(envelope);
3799
+ const enriched = await this.enrichIfNeeded(envelope);
3800
+ this.emitEvent(enriched);
3801
+ }
3802
+ // === TranscriptEnricher implementation ===
3803
+ canEnrich(eventType) {
3804
+ return eventType === "turn:finished";
3805
+ }
3806
+ async enrich(envelope) {
3807
+ const { sessionId, payload } = envelope;
3808
+ const transcriptPath = typeof payload.transcriptPath === "string" ? payload.transcriptPath : typeof payload.cwd === "string" ? resolveTranscriptPath(payload.cwd, sessionId) : void 0;
3809
+ if (!transcriptPath) {
3810
+ return envelope;
3811
+ }
3812
+ const summary = await readClaudeJSONL(transcriptPath);
3813
+ if (!summary) {
3814
+ return envelope;
3815
+ }
3816
+ const enrichedPayload = { ...payload };
3817
+ if (payload.durationMs === void 0 && summary.durationMs !== void 0) {
3818
+ enrichedPayload.durationMs = summary.durationMs;
3819
+ }
3820
+ if (payload.usage === void 0 && summary.inputTokens !== void 0 && summary.outputTokens !== void 0) {
3821
+ enrichedPayload.usage = {
3822
+ inputTokens: summary.inputTokens,
3823
+ outputTokens: summary.outputTokens
3824
+ };
3825
+ }
3826
+ return {
3827
+ ...envelope,
3828
+ payload: enrichedPayload
3829
+ };
3205
3830
  }
3831
+ /**
3832
+ * Handle permission/question requests from Claude hook events.
3833
+ *
3834
+ * Called for both PreToolUse (regular tools) and PermissionRequest events.
3835
+ * When toolName === 'AskUserQuestion', the request type is 'question' instead
3836
+ * of 'permission', which causes channels to render question cards (single/multi
3837
+ * select) instead of permission cards (Allow/Deny).
3838
+ *
3839
+ * evaluateInteraction blocks until the user responds via a channel. The response
3840
+ * is returned to Claude as part of the HTTP hook response body.
3841
+ *
3842
+ * See file header for full flow diagrams.
3843
+ */
3206
3844
  async processPermissionRequest(eventType, payload, requestId) {
3207
3845
  const sessionId = typeof payload.session_id === "string" ? payload.session_id : void 0;
3208
3846
  const toolName = typeof payload.tool_name === "string" ? payload.tool_name : "unknown";
@@ -3245,9 +3883,11 @@ var ClaudeHookAgentAdapter = class extends BaseAgentAdapter {
3245
3883
  };
3246
3884
 
3247
3885
  // src/adapters/agent/claude/ClaudeRemoteAgentAdapter.ts
3886
+ init_esm_shims();
3248
3887
  import { randomUUID as randomUUID2 } from "crypto";
3249
3888
 
3250
3889
  // src/core/agent/TurnAggregator.ts
3890
+ init_esm_shims();
3251
3891
  var TurnAggregator = class {
3252
3892
  constructor(logger5, sessionId) {
3253
3893
  this.logger = logger5;
@@ -3378,7 +4018,14 @@ var TurnAggregator = class {
3378
4018
  }
3379
4019
  };
3380
4020
 
4021
+ // src/core/session-scanner/index.ts
4022
+ init_esm_shims();
4023
+
4024
+ // src/core/session-scanner/types.ts
4025
+ init_esm_shims();
4026
+
3381
4027
  // src/core/session-scanner/registry.ts
4028
+ init_esm_shims();
3382
4029
  var SessionScannerRegistry = class _SessionScannerRegistry {
3383
4030
  scanners = /* @__PURE__ */ new Map();
3384
4031
  /**
@@ -3440,13 +4087,14 @@ var SessionScannerRegistry = class _SessionScannerRegistry {
3440
4087
  };
3441
4088
 
3442
4089
  // src/adapters/agent/claude/scanner.ts
3443
- import { readdir, readFile } from "fs/promises";
3444
- import { join as join2, resolve } from "path";
4090
+ init_esm_shims();
4091
+ import { readdir, readFile as readFile2 } from "fs/promises";
4092
+ import { join as join2, resolve as resolve2 } from "path";
3445
4093
  var ClaudeSessionScanner = class {
3446
4094
  agentType = "claude";
3447
4095
  sessionDir;
3448
4096
  constructor() {
3449
- this.sessionDir = resolve(process.env.HOME || "", ".claude/sessions");
4097
+ this.sessionDir = resolve2(process.env.HOME || "", ".claude/sessions");
3450
4098
  }
3451
4099
  getSessionDir() {
3452
4100
  return this.sessionDir;
@@ -3459,7 +4107,7 @@ var ClaudeSessionScanner = class {
3459
4107
  for (const file of jsonFiles) {
3460
4108
  try {
3461
4109
  const filePath = join2(this.sessionDir, file);
3462
- const content = await readFile(filePath, "utf-8");
4110
+ const content = await readFile2(filePath, "utf-8");
3463
4111
  const meta = JSON.parse(content);
3464
4112
  if (!meta.sessionId) {
3465
4113
  continue;
@@ -3469,8 +4117,9 @@ var ClaudeSessionScanner = class {
3469
4117
  id: meta.sessionId,
3470
4118
  agentType: this.agentType,
3471
4119
  transportType: "hook",
3472
- status: isActive ? "active" : "ended",
3473
- startedAt: meta.startedAt ? new Date(meta.startedAt) : /* @__PURE__ */ new Date(),
4120
+ status: isActive ? "active" : "archived",
4121
+ createdAt: meta.startedAt ?? Date.now(),
4122
+ updatedAt: meta.startedAt ?? Date.now(),
3474
4123
  cwd: meta.cwd
3475
4124
  });
3476
4125
  } catch {
@@ -3481,23 +4130,25 @@ var ClaudeSessionScanner = class {
3481
4130
  return sessions;
3482
4131
  }
3483
4132
  async checkProcessAlive(pid) {
3484
- return new Promise((resolve2) => {
4133
+ return new Promise((resolve3) => {
3485
4134
  try {
3486
4135
  process.kill(pid, 0);
3487
- resolve2(true);
4136
+ resolve3(true);
3488
4137
  } catch {
3489
- resolve2(false);
4138
+ resolve3(false);
3490
4139
  }
3491
4140
  });
3492
4141
  }
3493
4142
  };
3494
4143
 
3495
4144
  // src/adapters/agent/claude/sdk/query.ts
4145
+ init_esm_shims();
3496
4146
  import { spawn } from "child_process";
3497
4147
  import { createInterface } from "readline";
3498
4148
  import { existsSync as existsSync4 } from "fs";
3499
4149
 
3500
4150
  // src/adapters/agent/claude/sdk/stream.ts
4151
+ init_esm_shims();
3501
4152
  var Stream = class {
3502
4153
  constructor(returned) {
3503
4154
  this.returned = returned;
@@ -3526,18 +4177,18 @@ var Stream = class {
3526
4177
  if (this.hasError) {
3527
4178
  return Promise.reject(this.hasError);
3528
4179
  }
3529
- return new Promise((resolve2, reject) => {
3530
- this.readResolve = resolve2;
4180
+ return new Promise((resolve3, reject) => {
4181
+ this.readResolve = resolve3;
3531
4182
  this.readReject = reject;
3532
4183
  });
3533
4184
  }
3534
4185
  enqueue(value) {
3535
4186
  if (this.isDone || this.hasError) return;
3536
4187
  if (this.readResolve) {
3537
- const resolve2 = this.readResolve;
4188
+ const resolve3 = this.readResolve;
3538
4189
  this.readResolve = void 0;
3539
4190
  this.readReject = void 0;
3540
- resolve2({ done: false, value });
4191
+ resolve3({ done: false, value });
3541
4192
  } else {
3542
4193
  this.queue.push(value);
3543
4194
  }
@@ -3546,10 +4197,10 @@ var Stream = class {
3546
4197
  if (this.isDone || this.hasError) return;
3547
4198
  this.isDone = true;
3548
4199
  if (this.readResolve) {
3549
- const resolve2 = this.readResolve;
4200
+ const resolve3 = this.readResolve;
3550
4201
  this.readResolve = void 0;
3551
4202
  this.readReject = void 0;
3552
- resolve2({ done: true, value: void 0 });
4203
+ resolve3({ done: true, value: void 0 });
3553
4204
  }
3554
4205
  }
3555
4206
  error(error2) {
@@ -3572,6 +4223,7 @@ var Stream = class {
3572
4223
  };
3573
4224
 
3574
4225
  // src/adapters/agent/claude/sdk/types.ts
4226
+ init_esm_shims();
3575
4227
  var AbortError = class extends Error {
3576
4228
  constructor(message) {
3577
4229
  super(message);
@@ -3726,10 +4378,10 @@ var Query = class {
3726
4378
  type: "control_request",
3727
4379
  request
3728
4380
  };
3729
- return new Promise((resolve2, reject) => {
4381
+ return new Promise((resolve3, reject) => {
3730
4382
  this.pendingControlResponses.set(requestId, (response) => {
3731
4383
  if (response.subtype === "success") {
3732
- resolve2(response);
4384
+ resolve3(response);
3733
4385
  } else {
3734
4386
  reject(new Error(response.error));
3735
4387
  }
@@ -3880,7 +4532,7 @@ function query(config) {
3880
4532
  if (abort?.aborted) break;
3881
4533
  const data = JSON.stringify(msg) + "\n";
3882
4534
  if (!child.stdin.write(data)) {
3883
- await new Promise((resolve2) => child.stdin.once("drain", resolve2));
4535
+ await new Promise((resolve3) => child.stdin.once("drain", resolve3));
3884
4536
  }
3885
4537
  }
3886
4538
  } finally {
@@ -3893,14 +4545,14 @@ function query(config) {
3893
4545
  if (!child.killed) child.kill("SIGTERM");
3894
4546
  };
3895
4547
  abort?.addEventListener("abort", cleanup);
3896
- const processExitPromise = new Promise((resolve2) => {
4548
+ const processExitPromise = new Promise((resolve3) => {
3897
4549
  child.on("close", (code) => {
3898
4550
  if (abort?.aborted) {
3899
4551
  q.setError(new AbortError("Claude Code process aborted by user"));
3900
4552
  } else if (code !== 0 && code !== null) {
3901
4553
  q.setError(new Error(`Claude Code process exited with code ${code}`));
3902
4554
  } else {
3903
- resolve2();
4555
+ resolve3();
3904
4556
  }
3905
4557
  });
3906
4558
  });
@@ -3928,8 +4580,8 @@ var PushableAsyncIterable = class {
3928
4580
  push(value) {
3929
4581
  if (this.ended) return;
3930
4582
  if (this.resolvers.length > 0) {
3931
- const resolve2 = this.resolvers.shift();
3932
- resolve2({ done: false, value });
4583
+ const resolve3 = this.resolvers.shift();
4584
+ resolve3({ done: false, value });
3933
4585
  } else {
3934
4586
  this.queue.push(value);
3935
4587
  }
@@ -3937,8 +4589,8 @@ var PushableAsyncIterable = class {
3937
4589
  end() {
3938
4590
  this.ended = true;
3939
4591
  while (this.resolvers.length > 0) {
3940
- const resolve2 = this.resolvers.shift();
3941
- resolve2({ done: true, value: void 0 });
4592
+ const resolve3 = this.resolvers.shift();
4593
+ resolve3({ done: true, value: void 0 });
3942
4594
  }
3943
4595
  }
3944
4596
  [Symbol.asyncIterator]() {
@@ -3950,8 +4602,8 @@ var PushableAsyncIterable = class {
3950
4602
  if (this.ended) {
3951
4603
  return Promise.resolve({ done: true, value: void 0 });
3952
4604
  }
3953
- return new Promise((resolve2) => {
3954
- this.resolvers.push(resolve2);
4605
+ return new Promise((resolve3) => {
4606
+ this.resolvers.push(resolve3);
3955
4607
  });
3956
4608
  },
3957
4609
  return: () => {
@@ -3975,6 +4627,8 @@ var ClaudeRemoteAgentAdapter = class _ClaudeRemoteAgentAdapter extends BaseAgent
3975
4627
  defaultModel;
3976
4628
  defaultPermissionMode;
3977
4629
  static VALID_PERMISSION_MODES = ["default", "acceptEdits", "bypassPermissions", "plan"];
4630
+ /** Maps sender.id → real Claude session_id for stable multi-turn sessions */
4631
+ senderSessionMap = /* @__PURE__ */ new Map();
3978
4632
  constructor(options) {
3979
4633
  super({ bus: options.bus, bridge: options.bridge, logger: options.logger });
3980
4634
  this.token = options.token;
@@ -4025,6 +4679,13 @@ var ClaudeRemoteAgentAdapter = class _ClaudeRemoteAgentAdapter extends BaseAgent
4025
4679
  this.logger.info("Claude session scanner registered");
4026
4680
  }
4027
4681
  }
4682
+ /**
4683
+ * Return the real Claude session_id if this sender already has an active session.
4684
+ * This makes multi-turn conversations stable across turns.
4685
+ */
4686
+ resolveSessionId(message) {
4687
+ return this.senderSessionMap.get(message.sender.id);
4688
+ }
4028
4689
  async createTransportState(opts) {
4029
4690
  const sessionId = opts.sessionId ?? `claude-${randomUUID2().slice(0, 8)}`;
4030
4691
  const chatId = opts.chatId ?? sessionId;
@@ -4058,7 +4719,9 @@ var ClaudeRemoteAgentAdapter = class _ClaudeRemoteAgentAdapter extends BaseAgent
4058
4719
  turnInProgress: false,
4059
4720
  chatId,
4060
4721
  channel,
4061
- cwd
4722
+ cwd,
4723
+ permissionMode: this.defaultPermissionMode,
4724
+ senderId: opts.metadata?.senderId
4062
4725
  };
4063
4726
  this.transports.set(sessionId, state);
4064
4727
  this.emitEvent(EventNormalizer.sessionStart(sessionId, this.agentType, this.transportType, {
@@ -4067,7 +4730,7 @@ var ClaudeRemoteAgentAdapter = class _ClaudeRemoteAgentAdapter extends BaseAgent
4067
4730
  cwd,
4068
4731
  ...opts.metadata
4069
4732
  }));
4070
- this.consumeQueryMessages(q, sessionId, state).catch((err) => {
4733
+ this.consumeQueryMessages(q, state).catch((err) => {
4071
4734
  this.logger.error({ sessionId, error: err }, "Query consumer error");
4072
4735
  });
4073
4736
  this.logger.info({ sessionId }, "Claude remote transport state created");
@@ -4081,6 +4744,9 @@ var ClaudeRemoteAgentAdapter = class _ClaudeRemoteAgentAdapter extends BaseAgent
4081
4744
  remoteState.query.kill("SIGTERM");
4082
4745
  remoteState.aggregator.reset();
4083
4746
  this.transports.delete(sessionId);
4747
+ if (remoteState.senderId) {
4748
+ this.senderSessionMap.delete(remoteState.senderId);
4749
+ }
4084
4750
  this.emitEvent(EventNormalizer.sessionEnd(sessionId, this.agentType, this.transportType, "close"));
4085
4751
  }
4086
4752
  async sendCommand(state, command) {
@@ -4122,13 +4788,13 @@ var ClaudeRemoteAgentAdapter = class _ClaudeRemoteAgentAdapter extends BaseAgent
4122
4788
  }
4123
4789
  }
4124
4790
  // === Internal ===
4125
- async consumeQueryMessages(q, sessionId, state) {
4791
+ async consumeQueryMessages(q, state) {
4126
4792
  try {
4127
4793
  for await (const msg of q) {
4128
- await this.handleSdkMessage(msg, sessionId, state);
4794
+ await this.handleSdkMessage(msg, state);
4129
4795
  }
4130
4796
  } catch (err) {
4131
- this.logger.error({ sessionId, error: err }, "Claude remote stream ended with error");
4797
+ this.logger.error({ sessionId: state.sessionId, error: err }, "Claude remote stream ended with error");
4132
4798
  this.bus.publishOutbound({
4133
4799
  channel: state.channel,
4134
4800
  chatId: state.chatId,
@@ -4137,16 +4803,35 @@ var ClaudeRemoteAgentAdapter = class _ClaudeRemoteAgentAdapter extends BaseAgent
4137
4803
  } finally {
4138
4804
  state.aggregator.reset();
4139
4805
  state.turnInProgress = false;
4140
- this.transports.delete(sessionId);
4141
- this.emitEvent(EventNormalizer.sessionEnd(sessionId, this.agentType, this.transportType, "stream_ended"));
4806
+ this.transports.delete(state.sessionId);
4807
+ this.emitEvent(EventNormalizer.sessionEnd(state.sessionId, this.agentType, this.transportType, "stream_ended"));
4142
4808
  }
4143
4809
  }
4144
- async handleSdkMessage(msg, sessionId, state) {
4810
+ async handleSdkMessage(msg, state) {
4811
+ const sessionId = state.sessionId;
4145
4812
  switch (msg.type) {
4146
4813
  case "system": {
4147
4814
  const system = msg;
4148
4815
  if (system.subtype === "init" && system.session_id) {
4149
- this.logger.info({ sessionId, claudeSessionId: system.session_id }, "Claude session initialized");
4816
+ const claudeSessionId = system.session_id;
4817
+ this.logger.info({ sessionId, claudeSessionId }, "Claude session initialized");
4818
+ if (claudeSessionId !== sessionId) {
4819
+ const renamed = this.bridge.renameSessionId(sessionId, claudeSessionId);
4820
+ if (renamed) {
4821
+ this.transports.delete(sessionId);
4822
+ state.sessionId = claudeSessionId;
4823
+ this.transports.set(claudeSessionId, state);
4824
+ this.logger.info({ oldId: sessionId, newId: claudeSessionId }, "Session renamed to Claude session_id");
4825
+ }
4826
+ }
4827
+ this.bridge.updateSessionInfo(claudeSessionId, {
4828
+ model: system.model,
4829
+ slashCommands: system.slash_commands,
4830
+ permissionMode: state.permissionMode
4831
+ });
4832
+ if (state.senderId) {
4833
+ this.senderSessionMap.set(state.senderId, claudeSessionId);
4834
+ }
4150
4835
  }
4151
4836
  break;
4152
4837
  }
@@ -4199,7 +4884,9 @@ var ClaudeRemoteAgentAdapter = class _ClaudeRemoteAgentAdapter extends BaseAgent
4199
4884
  durationMs: result.duration_ms,
4200
4885
  usage: result.usage ? {
4201
4886
  inputTokens: result.usage.input_tokens,
4202
- outputTokens: result.usage.output_tokens
4887
+ outputTokens: result.usage.output_tokens,
4888
+ cacheReadInputTokens: result.usage.cache_read_input_tokens,
4889
+ cacheCreationInputTokens: result.usage.cache_creation_input_tokens
4203
4890
  } : void 0
4204
4891
  })
4205
4892
  );
@@ -4218,6 +4905,18 @@ var ClaudeRemoteAgentAdapter = class _ClaudeRemoteAgentAdapter extends BaseAgent
4218
4905
  }
4219
4906
  }
4220
4907
  }
4908
+ /**
4909
+ * Handle tool permission requests from the Claude SDK.
4910
+ *
4911
+ * The SDK calls this callback when Claude wants to run a tool. We block
4912
+ * the Promise until the user responds via a channel (Feishu/Telegram).
4913
+ *
4914
+ * Resolution values:
4915
+ * - allow → { behavior: 'allow', updatedInput? }
4916
+ * - deny → { behavior: 'deny', message }
4917
+ *
4918
+ * See file header for full flow diagram.
4919
+ */
4221
4920
  async handleCanCallTool(sessionId, toolName, toolInput, signal) {
4222
4921
  const state = this.transports.get(sessionId);
4223
4922
  if (!state) {
@@ -4234,14 +4933,14 @@ var ClaudeRemoteAgentAdapter = class _ClaudeRemoteAgentAdapter extends BaseAgent
4234
4933
  toolInput: toolInput ?? {},
4235
4934
  timestamp: Date.now()
4236
4935
  };
4237
- return new Promise((resolve2) => {
4936
+ return new Promise((resolve3) => {
4238
4937
  const timeout = setTimeout(() => {
4239
4938
  this.logger.warn({ sessionId, requestId }, "Permission request timeout");
4240
- resolve2({ behavior: "deny", message: "Timeout" });
4939
+ resolve3({ behavior: "deny", message: "Timeout" });
4241
4940
  }, _ClaudeRemoteAgentAdapter.CONTROL_REQUEST_TIMEOUT_MS);
4242
4941
  const onAbort = () => {
4243
4942
  clearTimeout(timeout);
4244
- resolve2({ behavior: "deny", message: "Aborted" });
4943
+ resolve3({ behavior: "deny", message: "Aborted" });
4245
4944
  };
4246
4945
  signal.addEventListener("abort", onAbort, { once: true });
4247
4946
  evaluateInteraction(this.bridge, interactionRequest).then((response) => {
@@ -4249,7 +4948,7 @@ var ClaudeRemoteAgentAdapter = class _ClaudeRemoteAgentAdapter extends BaseAgent
4249
4948
  signal.removeEventListener("abort", onAbort);
4250
4949
  const decision = response.action === "allow" ? "allow" : "deny";
4251
4950
  this.logger.info({ sessionId, requestId, toolName, decision }, "Claude remote permission resolved");
4252
- resolve2({
4951
+ resolve3({
4253
4952
  behavior: decision,
4254
4953
  updatedInput: response.updatedInput,
4255
4954
  ...decision === "deny" ? { message: "User denied this tool call" } : {}
@@ -4258,10 +4957,25 @@ var ClaudeRemoteAgentAdapter = class _ClaudeRemoteAgentAdapter extends BaseAgent
4258
4957
  clearTimeout(timeout);
4259
4958
  signal.removeEventListener("abort", onAbort);
4260
4959
  this.logger.error({ error: err, sessionId, requestId }, "Claude remote permission handler error");
4261
- resolve2({ behavior: "deny", message: "Error" });
4960
+ resolve3({ behavior: "deny", message: "Error" });
4262
4961
  });
4263
4962
  });
4264
4963
  }
4964
+ /**
4965
+ * Handle question requests from the Claude SDK (AskUserQuestion tool).
4966
+ *
4967
+ * The SDK calls this callback when Claude runs AskUserQuestion. We block
4968
+ * the Promise until the user selects an answer via a channel.
4969
+ *
4970
+ * For multi-select questions, the interaction-utils wraps the response
4971
+ * with updatedInput containing { answers: { [questionText]: answerValue } }.
4972
+ *
4973
+ * Resolution values:
4974
+ * - answer → { action: 'answer', answer: string }
4975
+ * - deny → { action: 'deny', message }
4976
+ *
4977
+ * See file header for full flow diagram.
4978
+ */
4265
4979
  async handleCanAnswerQuestion(sessionId, question, signal) {
4266
4980
  const state = this.transports.get(sessionId);
4267
4981
  if (!state) {
@@ -4278,40 +4992,41 @@ var ClaudeRemoteAgentAdapter = class _ClaudeRemoteAgentAdapter extends BaseAgent
4278
4992
  toolInput: { questions: [{ question }] },
4279
4993
  timestamp: Date.now()
4280
4994
  };
4281
- return new Promise((resolve2) => {
4995
+ return new Promise((resolve3) => {
4282
4996
  const timeout = setTimeout(() => {
4283
4997
  this.logger.warn({ sessionId, requestId }, "Question request timeout");
4284
- resolve2({ action: "deny", message: "Timeout" });
4998
+ resolve3({ action: "deny", message: "Timeout" });
4285
4999
  }, _ClaudeRemoteAgentAdapter.CONTROL_REQUEST_TIMEOUT_MS);
4286
5000
  const onAbort = () => {
4287
5001
  clearTimeout(timeout);
4288
- resolve2({ action: "deny", message: "Aborted" });
5002
+ resolve3({ action: "deny", message: "Aborted" });
4289
5003
  };
4290
5004
  signal.addEventListener("abort", onAbort, { once: true });
4291
5005
  evaluateInteraction(this.bridge, interactionRequest).then((response) => {
4292
5006
  clearTimeout(timeout);
4293
5007
  signal.removeEventListener("abort", onAbort);
4294
5008
  if (!response) {
4295
- resolve2({ action: "deny", message: "No response" });
5009
+ resolve3({ action: "deny", message: "No response" });
4296
5010
  return;
4297
5011
  }
4298
5012
  if (response.action === "allow" || response.action === "select") {
4299
5013
  const answer = response.selectedValues?.[0] ?? response.customInput ?? "";
4300
- resolve2({ action: "answer", answer: String(answer) });
5014
+ resolve3({ action: "answer", answer: String(answer) });
4301
5015
  } else {
4302
- resolve2({ action: "deny", message: response.message ?? "User denied" });
5016
+ resolve3({ action: "deny", message: response.message ?? "User denied" });
4303
5017
  }
4304
5018
  }).catch((err) => {
4305
5019
  clearTimeout(timeout);
4306
5020
  signal.removeEventListener("abort", onAbort);
4307
5021
  this.logger.error({ error: err, sessionId, requestId }, "Claude remote question handler error");
4308
- resolve2({ action: "deny", message: "Error" });
5022
+ resolve3({ action: "deny", message: "Error" });
4309
5023
  });
4310
5024
  });
4311
5025
  }
4312
5026
  };
4313
5027
 
4314
5028
  // src/adapters/agent/claude/hook-types.ts
5029
+ init_esm_shims();
4315
5030
  var VALID_CLAUDE_EVENTS = [
4316
5031
  "SessionStart",
4317
5032
  "SessionEnd",
@@ -4325,10 +5040,15 @@ function isClaudeHookEventType(eventType) {
4325
5040
  return VALID_CLAUDE_EVENTS.includes(eventType);
4326
5041
  }
4327
5042
 
5043
+ // src/adapters/agent/codex/index.ts
5044
+ init_esm_shims();
5045
+
4328
5046
  // src/adapters/agent/codex/CodexAppAgentAdapter.ts
5047
+ init_esm_shims();
4329
5048
  import { randomUUID as randomUUID3 } from "crypto";
4330
5049
 
4331
5050
  // src/adapters/agent/codex/codexAppServerClient.ts
5051
+ init_esm_shims();
4332
5052
  import { spawn as spawn2, execFileSync } from "child_process";
4333
5053
  import { createInterface as createInterface2 } from "readline";
4334
5054
  function isAppServerAvailable(command = "codex") {
@@ -4348,8 +5068,8 @@ function normalizeRawFileChangeList(changes) {
4348
5068
  if (!change || typeof change !== "object" || Array.isArray(change)) {
4349
5069
  continue;
4350
5070
  }
4351
- const path2 = typeof change.path === "string" ? change.path : null;
4352
- if (!path2) {
5071
+ const path3 = typeof change.path === "string" ? change.path : null;
5072
+ if (!path3) {
4353
5073
  continue;
4354
5074
  }
4355
5075
  const entry = {};
@@ -4359,7 +5079,7 @@ function normalizeRawFileChangeList(changes) {
4359
5079
  if (change.kind && typeof change.kind === "object" && !Array.isArray(change.kind)) {
4360
5080
  entry.kind = change.kind;
4361
5081
  }
4362
- normalized[path2] = entry;
5082
+ normalized[path3] = entry;
4363
5083
  }
4364
5084
  return Object.keys(normalized).length > 0 ? normalized : void 0;
4365
5085
  }
@@ -4821,7 +5541,7 @@ var CodexAppServerClient = class _CodexAppServerClient {
4821
5541
  if (Date.now() >= deadline) {
4822
5542
  return false;
4823
5543
  }
4824
- await new Promise((resolve2) => setTimeout(resolve2, 25));
5544
+ await new Promise((resolve3) => setTimeout(resolve3, 25));
4825
5545
  }
4826
5546
  return true;
4827
5547
  }
@@ -4894,13 +5614,13 @@ var CodexAppServerClient = class _CodexAppServerClient {
4894
5614
  async sendTurnAndWait(prompt, opts) {
4895
5615
  if (this.pendingInterrupt) {
4896
5616
  await this.pendingInterrupt;
4897
- await new Promise((resolve2) => setTimeout(resolve2, 0));
5617
+ await new Promise((resolve3) => setTimeout(resolve3, 0));
4898
5618
  }
4899
5619
  const timeoutMs = opts?.turnTimeoutMs ?? _CodexAppServerClient.TURN_TIMEOUT_MS;
4900
5620
  let timer = null;
4901
- const completion = new Promise((resolve2) => {
5621
+ const completion = new Promise((resolve3) => {
4902
5622
  this.pendingTurnCompletion = {
4903
- resolve: resolve2,
5623
+ resolve: resolve3,
4904
5624
  started: false,
4905
5625
  turnId: null
4906
5626
  };
@@ -4951,7 +5671,7 @@ var CodexAppServerClient = class _CodexAppServerClient {
4951
5671
  static REQUEST_TIMEOUT_MS = 3e4;
4952
5672
  request(method, params, timeoutMs) {
4953
5673
  const timeout = timeoutMs ?? _CodexAppServerClient.REQUEST_TIMEOUT_MS;
4954
- return new Promise((resolve2, reject) => {
5674
+ return new Promise((resolve3, reject) => {
4955
5675
  if (!this.process?.stdin?.writable) {
4956
5676
  reject(new Error(`Cannot send ${method}: stdin not writable`));
4957
5677
  return;
@@ -4964,7 +5684,7 @@ var CodexAppServerClient = class _CodexAppServerClient {
4964
5684
  this.pending.set(id, {
4965
5685
  resolve: (result) => {
4966
5686
  clearTimeout(timer);
4967
- resolve2(result);
5687
+ resolve3(result);
4968
5688
  },
4969
5689
  reject: (err) => {
4970
5690
  clearTimeout(timer);
@@ -5417,8 +6137,12 @@ var CodexAppAgentAdapter = class extends BaseAgentAdapter {
5417
6137
  }
5418
6138
  };
5419
6139
 
6140
+ // src/adapters/agent/codex/codexAppServerTypes.ts
6141
+ init_esm_shims();
6142
+
5420
6143
  // src/shared/persistence.ts
5421
- import { appendFile, mkdir, readdir as readdir2, readFile as readFile2 } from "fs/promises";
6144
+ init_esm_shims();
6145
+ import { appendFile, mkdir, readdir as readdir2, readFile as readFile3 } from "fs/promises";
5422
6146
  import { readdirSync, statSync } from "fs";
5423
6147
  import { join as join3 } from "path";
5424
6148
  import { existsSync as existsSync5 } from "fs";
@@ -5458,7 +6182,7 @@ var Persistence = class {
5458
6182
  const fileName = files.find((f) => f.endsWith(`-${sessionId}.jsonl`));
5459
6183
  if (fileName) {
5460
6184
  const filePath = join3(dirPath, fileName);
5461
- const content = await readFile2(filePath, "utf-8");
6185
+ const content = await readFile3(filePath, "utf-8");
5462
6186
  const lines = content.trim().split("\n").filter((line) => line.length > 0);
5463
6187
  for (const line of lines) {
5464
6188
  try {
@@ -5509,6 +6233,7 @@ var Persistence = class {
5509
6233
  };
5510
6234
 
5511
6235
  // src/server/http.ts
6236
+ init_esm_shims();
5512
6237
  import { createServer } from "http";
5513
6238
  import { URL } from "url";
5514
6239
  var VERSION = "0.1.0";
@@ -5524,19 +6249,19 @@ var HttpServer = class {
5524
6249
  /**
5525
6250
  * Register a route handler
5526
6251
  */
5527
- registerRoute(method, path2, handler) {
5528
- this.routes.push({ method: method.toUpperCase(), path: path2, handler });
5529
- logger2.debug(`Registered route: ${method} ${path2}`);
6252
+ registerRoute(method, path3, handler) {
6253
+ this.routes.push({ method: method.toUpperCase(), path: path3, handler });
6254
+ logger2.debug(`Registered route: ${method} ${path3}`);
5530
6255
  }
5531
6256
  /**
5532
6257
  * Register a WebSocket upgrade handler for a specific path
5533
6258
  */
5534
- onUpgrade(path2, handler) {
5535
- this.upgradeHandlers.set(path2, handler);
5536
- logger2.debug(`Registered upgrade handler: ${path2}`);
6259
+ onUpgrade(path3, handler) {
6260
+ this.upgradeHandlers.set(path3, handler);
6261
+ logger2.debug(`Registered upgrade handler: ${path3}`);
5537
6262
  return () => {
5538
- this.upgradeHandlers.delete(path2);
5539
- logger2.debug(`Unregistered upgrade handler: ${path2}`);
6263
+ this.upgradeHandlers.delete(path3);
6264
+ logger2.debug(`Unregistered upgrade handler: ${path3}`);
5540
6265
  };
5541
6266
  }
5542
6267
  /**
@@ -5553,10 +6278,10 @@ var HttpServer = class {
5553
6278
  socket.destroy();
5554
6279
  }
5555
6280
  });
5556
- return new Promise((resolve2, reject) => {
6281
+ return new Promise((resolve3, reject) => {
5557
6282
  this.server?.listen(this.port, () => {
5558
6283
  logger2.info(`HTTP server listening on port ${this.port}`);
5559
- resolve2();
6284
+ resolve3();
5560
6285
  });
5561
6286
  this.server?.on("error", reject);
5562
6287
  });
@@ -5565,10 +6290,10 @@ var HttpServer = class {
5565
6290
  * Stop the HTTP server
5566
6291
  */
5567
6292
  async stop() {
5568
- return new Promise((resolve2) => {
6293
+ return new Promise((resolve3) => {
5569
6294
  this.server?.close(() => {
5570
6295
  logger2.info("HTTP server stopped");
5571
- resolve2();
6296
+ resolve3();
5572
6297
  });
5573
6298
  });
5574
6299
  }
@@ -5612,7 +6337,7 @@ var HttpServer = class {
5612
6337
  }
5613
6338
  };
5614
6339
  function parseJsonBody(req) {
5615
- return new Promise((resolve2, reject) => {
6340
+ return new Promise((resolve3, reject) => {
5616
6341
  let body = "";
5617
6342
  req.on("data", (chunk) => {
5618
6343
  body += chunk.toString();
@@ -5620,10 +6345,10 @@ function parseJsonBody(req) {
5620
6345
  req.on("end", () => {
5621
6346
  try {
5622
6347
  if (!body) {
5623
- resolve2({});
6348
+ resolve3({});
5624
6349
  return;
5625
6350
  }
5626
- resolve2(JSON.parse(body));
6351
+ resolve3(JSON.parse(body));
5627
6352
  } catch (error2) {
5628
6353
  reject(error2);
5629
6354
  }
@@ -5702,7 +6427,7 @@ function setupRoutes(httpServer, gateway, getClientCount) {
5702
6427
  sessionId: s.id,
5703
6428
  agentType: s.agentType,
5704
6429
  status: s.status,
5705
- startedAt: s.startedAt?.toISOString(),
6430
+ startedAt: new Date(s.createdAt).toISOString(),
5706
6431
  updatedAt: new Date(s.updatedAt).toISOString()
5707
6432
  })),
5708
6433
  clients: getClientCount()
@@ -5807,6 +6532,7 @@ function setupRoutes(httpServer, gateway, getClientCount) {
5807
6532
  }
5808
6533
 
5809
6534
  // src/server/websocket.ts
6535
+ init_esm_shims();
5810
6536
  import { WebSocketServer as WSWebSocketServer, WebSocket } from "ws";
5811
6537
  import { URL as URL2 } from "url";
5812
6538
  var VERSION2 = "0.1.0";
@@ -5835,13 +6561,13 @@ var WebSocketServer = class {
5835
6561
  this.wss = new WSWebSocketServer({ noServer: true });
5836
6562
  this.setupConnectionHandlers();
5837
6563
  this.startHeartbeat();
5838
- const path2 = this.options.path || "/ws";
5839
- this.httpServer.onUpgrade(path2, (request, socket, head) => {
6564
+ const path3 = this.options.path || "/ws";
6565
+ this.httpServer.onUpgrade(path3, (request, socket, head) => {
5840
6566
  this.wss?.handleUpgrade(request, socket, head, (ws) => {
5841
6567
  this.wss?.emit("connection", ws, request);
5842
6568
  });
5843
6569
  });
5844
- logger3.info(`WebSocket server registered on path: ${path2}`);
6570
+ logger3.info(`WebSocket server registered on path: ${path3}`);
5845
6571
  }
5846
6572
  /**
5847
6573
  * Stop the WebSocket server
@@ -5852,10 +6578,10 @@ var WebSocketServer = class {
5852
6578
  this.heartbeatInterval = null;
5853
6579
  }
5854
6580
  this.closeAll();
5855
- return new Promise((resolve2) => {
6581
+ return new Promise((resolve3) => {
5856
6582
  this.wss?.close(() => {
5857
6583
  logger3.info("WebSocket server stopped");
5858
- resolve2();
6584
+ resolve3();
5859
6585
  });
5860
6586
  });
5861
6587
  }
@@ -6071,7 +6797,7 @@ function setupWebSocketHandlers(wsServer, gateway, options = {}) {
6071
6797
  id: s.id,
6072
6798
  agentType: s.agentType,
6073
6799
  status: s.status,
6074
- startedAt: s.startedAt?.toISOString(),
6800
+ startedAt: new Date(s.createdAt).toISOString(),
6075
6801
  updatedAt: new Date(s.updatedAt).toISOString()
6076
6802
  }))
6077
6803
  }
@@ -6133,6 +6859,7 @@ function broadcastEvent(wsServer, frame) {
6133
6859
  }
6134
6860
 
6135
6861
  // src/shared/credentials.ts
6862
+ init_esm_shims();
6136
6863
  import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
6137
6864
  import { join as join4, dirname as dirname4 } from "path";
6138
6865
  import { homedir } from "os";
@@ -6140,15 +6867,15 @@ function getCredentialsPath() {
6140
6867
  return join4(homedir(), ".handsoff", "credentials.json");
6141
6868
  }
6142
6869
  function loadCredentials() {
6143
- const path2 = getCredentialsPath();
6144
- if (!existsSync6(path2)) {
6870
+ const path3 = getCredentialsPath();
6871
+ if (!existsSync6(path3)) {
6145
6872
  return null;
6146
6873
  }
6147
6874
  try {
6148
- const content = readFileSync3(path2, "utf-8");
6875
+ const content = readFileSync3(path3, "utf-8");
6149
6876
  return JSON.parse(content);
6150
6877
  } catch (error2) {
6151
- console.error(`Failed to load credentials from ${path2}:`, error2);
6878
+ console.error(`Failed to load credentials from ${path3}:`, error2);
6152
6879
  return null;
6153
6880
  }
6154
6881
  }
@@ -6156,7 +6883,14 @@ function getHookToken() {
6156
6883
  return loadCredentials()?.hookToken ?? null;
6157
6884
  }
6158
6885
 
6886
+ // src/adapters/interaction/index.ts
6887
+ init_esm_shims();
6888
+
6889
+ // src/adapters/interaction/types.ts
6890
+ init_esm_shims();
6891
+
6159
6892
  // src/adapters/interaction/queue.ts
6893
+ init_esm_shims();
6160
6894
  import { randomUUID as randomUUID4 } from "crypto";
6161
6895
  var InteractionQueue = class {
6162
6896
  pending = /* @__PURE__ */ new Map();
@@ -6168,13 +6902,13 @@ var InteractionQueue = class {
6168
6902
  }
6169
6903
  request(request) {
6170
6904
  const requestId = randomUUID4();
6171
- const responsePromise = new Promise((resolve2, reject) => {
6905
+ const responsePromise = new Promise((resolve3, reject) => {
6172
6906
  const timeoutId = setTimeout(() => {
6173
6907
  this.pending.delete(requestId);
6174
6908
  getLogger().info(
6175
6909
  `[Interaction] Timeout: ${request.toolName} | requestId=${requestId} | default=${this.defaultOnTimeout}`
6176
6910
  );
6177
- resolve2({
6911
+ resolve3({
6178
6912
  requestId,
6179
6913
  action: this.defaultOnTimeout,
6180
6914
  message: `Interaction request timed out after ${this.timeoutMs}ms`
@@ -6191,7 +6925,7 @@ var InteractionQueue = class {
6191
6925
  resolve: (response) => {
6192
6926
  clearTimeout(timeoutId);
6193
6927
  this.pending.delete(requestId);
6194
- resolve2(response);
6928
+ resolve3(response);
6195
6929
  },
6196
6930
  reject: (error2) => {
6197
6931
  clearTimeout(timeoutId);
@@ -6260,6 +6994,7 @@ var InteractionQueue = class {
6260
6994
  };
6261
6995
 
6262
6996
  // src/config.ts
6997
+ init_esm_shims();
6263
6998
  import { readFileSync as readFileSync4, existsSync as existsSync7 } from "fs";
6264
6999
  import { join as join5 } from "path";
6265
7000
  import { homedir as homedir2 } from "os";
@@ -6408,7 +7143,16 @@ function loadConfig() {
6408
7143
  }
6409
7144
 
6410
7145
  // src/core/bus/index.ts
7146
+ init_esm_shims();
6411
7147
  import EventEmitter from "events";
7148
+
7149
+ // src/core/bus/types.ts
7150
+ init_esm_shims();
7151
+
7152
+ // src/core/bus/events.ts
7153
+ init_esm_shims();
7154
+
7155
+ // src/core/bus/index.ts
6412
7156
  var EventBus = class {
6413
7157
  emitter;
6414
7158
  logger;
@@ -6686,7 +7430,11 @@ var EventBus = class {
6686
7430
  }
6687
7431
  };
6688
7432
 
7433
+ // src/core/channel/manager.ts
7434
+ init_esm_shims();
7435
+
6689
7436
  // src/core/channel/registry.ts
7437
+ init_esm_shims();
6690
7438
  var ChannelRegistry = class _ChannelRegistry {
6691
7439
  static instance = null;
6692
7440
  registry = /* @__PURE__ */ new Map();
@@ -6853,7 +7601,7 @@ var ChannelManager = class {
6853
7601
  }
6854
7602
  const delay = 1e3 * Math.pow(2, attempt - 1);
6855
7603
  this.logger.debug({ channel: name, delay }, "[Channel] Retrying");
6856
- await new Promise((resolve2) => setTimeout(resolve2, delay));
7604
+ await new Promise((resolve3) => setTimeout(resolve3, delay));
6857
7605
  }
6858
7606
  }
6859
7607
  }
@@ -7126,7 +7874,17 @@ var ChannelManager = class {
7126
7874
  }
7127
7875
  };
7128
7876
 
7877
+ // src/core/routing/index.ts
7878
+ init_esm_shims();
7879
+
7880
+ // src/core/routing/InboundRouter.ts
7881
+ init_esm_shims();
7882
+
7883
+ // src/core/binding/index.ts
7884
+ init_esm_shims();
7885
+
7129
7886
  // src/core/binding/BindingService.ts
7887
+ init_esm_shims();
7130
7888
  import { writeFileSync as writeFileSync3 } from "fs";
7131
7889
  import { join as join6 } from "path";
7132
7890
  import { homedir as homedir3 } from "os";
@@ -7244,6 +8002,7 @@ var BindingService = class {
7244
8002
  };
7245
8003
 
7246
8004
  // src/core/command/routing.ts
8005
+ init_esm_shims();
7247
8006
  var COMMAND_ROOT_SUBCOMMAND = "__root__";
7248
8007
  var SYSTEM_COMMAND_ROOTS = /* @__PURE__ */ new Set([
7249
8008
  "start",
@@ -7366,7 +8125,23 @@ var InboundRouter = class {
7366
8125
  }
7367
8126
  };
7368
8127
 
8128
+ // src/gateway/handlers/index.ts
8129
+ init_esm_shims();
8130
+
8131
+ // src/gateway/handlers/command.ts
8132
+ init_esm_shims();
8133
+
8134
+ // src/core/command/index.ts
8135
+ init_esm_shims();
8136
+
8137
+ // src/core/command/types.ts
8138
+ init_esm_shims();
8139
+
8140
+ // src/core/command/executor.ts
8141
+ init_esm_shims();
8142
+
7369
8143
  // src/core/command/parser.ts
8144
+ init_esm_shims();
7370
8145
  var CommandParser = class {
7371
8146
  /**
7372
8147
  * Check if content is a command (starts with /)
@@ -7512,7 +8287,11 @@ var CommandExecutor = class {
7512
8287
  }
7513
8288
  };
7514
8289
 
8290
+ // src/core/command/builtin/index.ts
8291
+ init_esm_shims();
8292
+
7515
8293
  // src/core/command/builtin/bind.ts
8294
+ init_esm_shims();
7516
8295
  var bindingService = new BindingService();
7517
8296
  var bindCommand = {
7518
8297
  description: "Bind this channel to an agent",
@@ -7567,6 +8346,7 @@ var unbindAgentCommand = {
7567
8346
  };
7568
8347
 
7569
8348
  // src/core/command/builtin/help.ts
8349
+ init_esm_shims();
7570
8350
  var helpCommand = {
7571
8351
  description: "Show system and agent command help",
7572
8352
  usage: "/help",
@@ -7591,6 +8371,7 @@ var helpCommand = {
7591
8371
  };
7592
8372
 
7593
8373
  // src/core/command/builtin/session.ts
8374
+ init_esm_shims();
7594
8375
  var bindingService2 = new BindingService();
7595
8376
  function formatDuration(start, end) {
7596
8377
  const endTime = end ?? /* @__PURE__ */ new Date();
@@ -7648,7 +8429,7 @@ var listHandler = {
7648
8429
  for (const [agentType, agentSessions] of byAgent) {
7649
8430
  lines.push(`**${agentType}** (${agentSessions.length})`);
7650
8431
  for (const session of agentSessions) {
7651
- lines.push(`- \`${session.id}\` (${session.status}, ${formatDuration(session.startedAt)})`);
8432
+ lines.push(`- \`${session.id}\` (${session.status}, ${formatDuration(new Date(session.createdAt))})`);
7652
8433
  }
7653
8434
  lines.push("");
7654
8435
  }
@@ -7674,8 +8455,8 @@ var statusHandler = {
7674
8455
  `Agent: ${session.agentType}`,
7675
8456
  `Transport: ${session.transportType}`,
7676
8457
  `Status: ${session.status}`,
7677
- `Started: ${session.startedAt.toISOString()}`,
7678
- `Duration: ${formatDuration(session.startedAt)}`
8458
+ `Started: ${new Date(session.createdAt).toISOString()}`,
8459
+ `Duration: ${formatDuration(new Date(session.createdAt))}`
7679
8460
  ];
7680
8461
  if (session.cwd) {
7681
8462
  lines.push(`Workdir: \`${session.cwd}\``);
@@ -7768,6 +8549,7 @@ function registerSessionCommands(registerFn) {
7768
8549
  }
7769
8550
 
7770
8551
  // src/core/command/builtin/start.ts
8552
+ init_esm_shims();
7771
8553
  var bindingService3 = new BindingService();
7772
8554
  var startCommand = {
7773
8555
  description: "Show the quick-start guide for this channel",
@@ -7789,6 +8571,7 @@ var startCommand = {
7789
8571
  };
7790
8572
 
7791
8573
  // src/core/command/builtin/system.ts
8574
+ init_esm_shims();
7792
8575
  import process2 from "process";
7793
8576
  import os from "os";
7794
8577
  var systemStartTime = Date.now();
@@ -8077,6 +8860,7 @@ function setupCommandHandler(bus, sessionManager, logger5) {
8077
8860
  }
8078
8861
 
8079
8862
  // src/gateway/handlers/permission.ts
8863
+ init_esm_shims();
8080
8864
  function setupPermissionHandler(bus, interactionQueue, logger5) {
8081
8865
  const unsubscribe = bus.onInteractionResponse((event) => {
8082
8866
  const { requestId, response, reason } = event;
@@ -8114,11 +8898,13 @@ function setupAllHandlers(bus, gateway, interactionQueue, logger5) {
8114
8898
  }
8115
8899
 
8116
8900
  // src/gateway/notification-handler.ts
8901
+ init_esm_shims();
8117
8902
  var NotificationHandler = class {
8118
- constructor(bus, logger5, getSessionAgentType, bindingService4) {
8903
+ constructor(bus, logger5, getSessionAgentType, getSessionInfo, bindingService4) {
8119
8904
  this.bus = bus;
8120
8905
  this.logger = logger5;
8121
8906
  this.getSessionAgentType = getSessionAgentType;
8907
+ this.getSessionInfo = getSessionInfo;
8122
8908
  this.bindingService = bindingService4;
8123
8909
  if (!this.bindingService) {
8124
8910
  this.bindingService = new BindingService(logger5);
@@ -8127,6 +8913,7 @@ var NotificationHandler = class {
8127
8913
  bus;
8128
8914
  logger;
8129
8915
  getSessionAgentType;
8916
+ getSessionInfo;
8130
8917
  bindingService;
8131
8918
  unsubscribeFns = [];
8132
8919
  sessionAgentMap = /* @__PURE__ */ new Map();
@@ -8264,6 +9051,7 @@ var NotificationHandler = class {
8264
9051
  }
8265
9052
  async handleTurnFinished(event) {
8266
9053
  this.logger.debug({ sessionId: event.sessionId, cwd: event.cwd }, "[NotificationHandler] handleTurnFinished");
9054
+ const sessionInfo = this.getSessionInfo?.(event.sessionId);
8267
9055
  const context = {
8268
9056
  eventType: "turn:finished",
8269
9057
  sessionId: event.sessionId,
@@ -8276,6 +9064,7 @@ var NotificationHandler = class {
8276
9064
  thinking: event.thinking,
8277
9065
  durationMs: event.durationMs,
8278
9066
  usage: event.usage,
9067
+ model: sessionInfo?.model,
8279
9068
  summary: event.usage || event.durationMs ? {
8280
9069
  durationMs: event.durationMs,
8281
9070
  inputTokens: event.usage?.inputTokens,
@@ -8374,7 +9163,14 @@ var NotificationHandler = class {
8374
9163
  }
8375
9164
  };
8376
9165
 
9166
+ // src/adapters/channels/index.ts
9167
+ init_esm_shims();
9168
+
9169
+ // src/adapters/channels/telegram/index.ts
9170
+ init_esm_shims();
9171
+
8377
9172
  // src/core/channel/base.ts
9173
+ init_esm_shims();
8378
9174
  var BaseChannel = class {
8379
9175
  /**
8380
9176
  * 是否支持流式消息输出
@@ -8628,6 +9424,7 @@ var BaseChannel = class {
8628
9424
  };
8629
9425
 
8630
9426
  // src/adapters/channels/telegram/bot.ts
9427
+ init_esm_shims();
8631
9428
  import { Bot } from "grammy";
8632
9429
  var TelegramBot = class {
8633
9430
  /** grammy Bot 实例 */
@@ -8758,13 +9555,13 @@ var TelegramBot = class {
8758
9555
  * @param timeoutMs - 超时时间(毫秒)
8759
9556
  */
8760
9557
  async startWithTimeout(timeoutMs) {
8761
- return new Promise((resolve2, reject) => {
9558
+ return new Promise((resolve3, reject) => {
8762
9559
  const timeout = setTimeout(() => {
8763
9560
  reject(new Error(`Bot start timed out after ${timeoutMs}ms`));
8764
9561
  }, timeoutMs);
8765
9562
  this.bot.start().then(() => {
8766
9563
  clearTimeout(timeout);
8767
- resolve2();
9564
+ resolve3();
8768
9565
  }).catch((error2) => {
8769
9566
  clearTimeout(timeout);
8770
9567
  reject(error2);
@@ -8776,14 +9573,27 @@ var TelegramBot = class {
8776
9573
  * @param ms - 毫秒
8777
9574
  */
8778
9575
  sleep(ms) {
8779
- return new Promise((resolve2) => setTimeout(resolve2, ms));
9576
+ return new Promise((resolve3) => setTimeout(resolve3, ms));
8780
9577
  }
8781
9578
  };
8782
9579
 
9580
+ // src/adapters/channels/telegram/formatter.ts
9581
+ init_esm_shims();
9582
+
9583
+ // src/gateway/notification-context.ts
9584
+ init_esm_shims();
9585
+
9586
+ // src/i18n/index.ts
9587
+ init_esm_shims();
9588
+
9589
+ // src/i18n/types.ts
9590
+ init_esm_shims();
9591
+
8783
9592
  // src/i18n/index.ts
8784
9593
  import i18next from "i18next";
8785
9594
 
8786
9595
  // src/i18n/locales/en.ts
9596
+ init_esm_shims();
8787
9597
  var en_default = {
8788
9598
  wizard: {
8789
9599
  section: {
@@ -9092,6 +9902,33 @@ function getEventTitle(eventType) {
9092
9902
  }
9093
9903
  }
9094
9904
 
9905
+ // src/shared/format.ts
9906
+ init_esm_shims();
9907
+ function formatDuration3(ms) {
9908
+ if (ms === void 0 || ms < 0) return "-";
9909
+ if (ms < 1e3) return `${ms}ms`;
9910
+ if (ms < 6e4) return `${Math.round(ms / 1e3)}s`;
9911
+ if (ms < 36e5) return `${Math.round(ms / 6e4)}m`;
9912
+ const hours = Math.floor(ms / 36e5);
9913
+ const minutes = Math.round(ms % 36e5 / 6e4);
9914
+ if (minutes === 0) return `${hours}h`;
9915
+ return `${hours}h${minutes}m`;
9916
+ }
9917
+ function formatTokenCount(n) {
9918
+ if (n === void 0 || n < 0) return "-";
9919
+ if (n < 1e3) return `${n}`;
9920
+ if (n < 1e6) {
9921
+ const val2 = n / 1e3;
9922
+ return `${stripZeroDecimal(val2)}k`;
9923
+ }
9924
+ const val = n / 1e6;
9925
+ return `${stripZeroDecimal(val)}M`;
9926
+ }
9927
+ function stripZeroDecimal(val) {
9928
+ const fixed = val.toFixed(1);
9929
+ return fixed.endsWith(".0") ? fixed.slice(0, -2) : fixed;
9930
+ }
9931
+
9095
9932
  // src/adapters/channels/telegram/formatter.ts
9096
9933
  var TelegramFormatter = class {
9097
9934
  /**
@@ -9207,6 +10044,7 @@ var TelegramFormatter = class {
9207
10044
  case "turn:finished":
9208
10045
  return this.formatFinished(
9209
10046
  title,
10047
+ context.model || context.agentType,
9210
10048
  context.durationMs,
9211
10049
  context.usage?.inputTokens,
9212
10050
  context.usage?.outputTokens,
@@ -9242,17 +10080,19 @@ var TelegramFormatter = class {
9242
10080
  /**
9243
10081
  * 格式化结果通知消息
9244
10082
  */
9245
- formatFinished(title, durationMs, inputTokens, outputTokens, result) {
10083
+ formatFinished(title, model, durationMs, inputTokens, outputTokens, result) {
9246
10084
  const lines = [
9247
10085
  `${this.escape(title)}`,
9248
10086
  ""
9249
10087
  ];
10088
+ if (model) {
10089
+ lines.push(`<b>Model:</b> ${this.escape(model)}`);
10090
+ }
9250
10091
  if (durationMs !== void 0) {
9251
- const seconds = (durationMs / 1e3).toFixed(1);
9252
- lines.push(`<b>Duration:</b> ${this.escape(seconds)}s`);
10092
+ lines.push(`<b>Duration:</b> ${this.escape(formatDuration3(durationMs))}`);
9253
10093
  }
9254
10094
  if (inputTokens !== void 0 && outputTokens !== void 0) {
9255
- lines.push(`<b>Tokens:</b> in ${inputTokens} / out ${outputTokens}`);
10095
+ lines.push(`<b>Tokens:</b> ${formatTokenCount(inputTokens)}\u2191 / ${formatTokenCount(outputTokens)}\u2193`);
9256
10096
  }
9257
10097
  if (result) {
9258
10098
  lines.push("", "<b>Result:</b>");
@@ -9319,6 +10159,7 @@ var TelegramFormatter = class {
9319
10159
  var formatter = new TelegramFormatter();
9320
10160
 
9321
10161
  // src/shared/channelInstance.ts
10162
+ init_esm_shims();
9322
10163
  import { createHash } from "crypto";
9323
10164
  function getChannelInstanceId(channelType, credential) {
9324
10165
  const hash = createHash("sha256").update(credential).digest("hex").slice(0, 12);
@@ -9982,6 +10823,7 @@ TelegramChannel = __decorateElement(_init, 0, "TelegramChannel", _TelegramChanne
9982
10823
  __runInitializers(_init, 1, TelegramChannel);
9983
10824
 
9984
10825
  // src/adapters/channels/logger/index.ts
10826
+ init_esm_shims();
9985
10827
  var _LoggerChannel_decorators, _init2, _a3;
9986
10828
  _LoggerChannel_decorators = [Channel("logger")];
9987
10829
  var LoggerChannel = class extends (_a3 = BaseChannel) {
@@ -10024,7 +10866,11 @@ _init2 = __decoratorStart(_a3);
10024
10866
  LoggerChannel = __decorateElement(_init2, 0, "LoggerChannel", _LoggerChannel_decorators, LoggerChannel);
10025
10867
  __runInitializers(_init2, 1, LoggerChannel);
10026
10868
 
10869
+ // src/adapters/channels/feishu/index.ts
10870
+ init_esm_shims();
10871
+
10027
10872
  // src/adapters/channels/feishu/client.ts
10873
+ init_esm_shims();
10028
10874
  import {
10029
10875
  Client,
10030
10876
  WSClient,
@@ -10176,7 +11022,8 @@ var FeishuClient = class {
10176
11022
  userId: operator?.open_id,
10177
11023
  messageId: context?.open_message_id,
10178
11024
  chatId: context?.open_chat_id,
10179
- optionId: value?.optionId
11025
+ optionId: value?.optionId,
11026
+ checked: actionObj?.checked
10180
11027
  };
10181
11028
  await this.cardActionHandler(cardAction);
10182
11029
  } catch (error2) {
@@ -10224,6 +11071,7 @@ var FeishuClient = class {
10224
11071
  };
10225
11072
 
10226
11073
  // src/adapters/channels/feishu/formatter.ts
11074
+ init_esm_shims();
10227
11075
  var FeishuFormatter = class {
10228
11076
  /**
10229
11077
  * 将内容转换为飞书 post 格式
@@ -10472,6 +11320,27 @@ ${JSON.stringify(content.permission.args, null, 2)}
10472
11320
  };
10473
11321
  return JSON.stringify(cardJson);
10474
11322
  }
11323
+ /**
11324
+ * Format a message notification card
11325
+ */
11326
+ formatMessageCard(params) {
11327
+ const { buildMessageCard: buildMessageCard2 } = (init_message(), __toCommonJS(message_exports));
11328
+ return buildMessageCard2(params);
11329
+ }
11330
+ /**
11331
+ * Format a permission request card
11332
+ */
11333
+ formatPermissionCard(params) {
11334
+ const { buildPermissionCard: buildPermissionCard2 } = (init_permission(), __toCommonJS(permission_exports));
11335
+ return buildPermissionCard2(params);
11336
+ }
11337
+ /**
11338
+ * Format a question request card
11339
+ */
11340
+ formatQuestionCard(params) {
11341
+ const { buildQuestionCard: buildQuestionCard2 } = (init_question(), __toCommonJS(question_exports));
11342
+ return buildQuestionCard2(params);
11343
+ }
10475
11344
  };
10476
11345
 
10477
11346
  // src/adapters/channels/feishu/index.ts
@@ -10498,6 +11367,7 @@ var FeishuChannel = class extends (_a4 = BaseChannel) {
10498
11367
  pendingOptions = /* @__PURE__ */ new Map();
10499
11368
  pendingToolNames = /* @__PURE__ */ new Map();
10500
11369
  // requestId -> toolName for always_allow
11370
+ pendingSelections = /* @__PURE__ */ new Map();
10501
11371
  unsubscribeInteractionResponse;
10502
11372
  formatter = new FeishuFormatter();
10503
11373
  constructor(config, bus, logger5) {
@@ -10561,6 +11431,7 @@ var FeishuChannel = class extends (_a4 = BaseChannel) {
10561
11431
  this.pendingRequests.clear();
10562
11432
  this.pendingOptions.clear();
10563
11433
  this.pendingToolNames.clear();
11434
+ this.pendingSelections.clear();
10564
11435
  this.client?.stopWebSocket();
10565
11436
  await super.stop();
10566
11437
  this.logger.info({ channel: this.name }, "[Channel] Stopped");
@@ -10587,33 +11458,48 @@ var FeishuChannel = class extends (_a4 = BaseChannel) {
10587
11458
  const ctx = message.context;
10588
11459
  const interaction = ctx?.interaction;
10589
11460
  const requestId = ctx?.requestId;
10590
- let buttons;
10591
- let isInteractionRequest = false;
10592
- if ((eventType === "permission:request" || eventType === "question:request") && requestId && interaction) {
10593
- isInteractionRequest = true;
10594
- if (eventType === "permission:request") {
10595
- const toolName = ctx?.toolName || "";
11461
+ if (eventType === "permission:request") {
11462
+ const toolName = ctx?.toolName || "";
11463
+ if (requestId) {
10596
11464
  this.pendingToolNames.set(requestId, toolName);
10597
- buttons = [
10598
- { text: "\u2705 Allow", type: "primary_filled", callbackValue: { action: "agree", requestId } },
10599
- { text: "\u274C Deny", type: "danger_filled", callbackValue: { action: "reject", requestId } },
10600
- { text: "\u{1F513} Always Allow", type: "primary", callbackValue: { action: "always_allow", requestId } }
10601
- ];
10602
- } else if (eventType === "question:request" && interaction.question) {
10603
- const question = interaction.question;
11465
+ }
11466
+ const content = this.formatContextToMarkdown(message.context);
11467
+ const buttons = [
11468
+ { text: "\u2705 Allow", type: "primary_filled", value: "agree" },
11469
+ { text: "\u274C Deny", type: "danger_filled", value: "reject", confirm: { title: "\u786E\u8BA4\u62D2\u7EDD", text: "\u786E\u8BA4\u62D2\u7EDD\u8BE5\u5DE5\u5177\u8C03\u7528\uFF1F" } },
11470
+ { text: "\u{1F513} Always Allow", type: "default", value: "always_allow", confirm: { title: "\u786E\u8BA4\u603B\u662F\u5141\u8BB8", text: "\u603B\u662F\u5141\u8BB8\u5C06\u653E\u884C\u8BE5\u5DE5\u5177\u7684\u6240\u6709\u8C03\u7528\uFF0C\u8BF7\u786E\u8BA4\u3002" } }
11471
+ ];
11472
+ const cardContent = this.formatter.formatPermissionCard({ title: this.getTitleForContext(message.context), content, buttons });
11473
+ await this.sendCardRaw(cardContent, message);
11474
+ } else if (eventType === "question:request") {
11475
+ const question = interaction?.question;
11476
+ const content = this.formatContextToMarkdown(message.context);
11477
+ const mode = question?.multiSelect ? "multiple" : "single";
11478
+ const items = (question?.options || []).map((opt) => ({ id: opt.id, label: opt.label }));
11479
+ if (requestId && question) {
10604
11480
  this.pendingOptions.set(
10605
11481
  requestId,
10606
11482
  question.options.map((opt) => ({ id: opt.id, value: opt.value }))
10607
11483
  );
10608
- buttons = question.options.map((opt) => ({
10609
- text: opt.label,
10610
- type: "primary",
10611
- callbackValue: { action: "select", requestId, optionId: opt.id }
10612
- }));
10613
11484
  }
10614
- }
10615
- await this.sendCard(message, buttons);
10616
- if (isInteractionRequest && requestId) {
11485
+ const cardContent = this.formatter.formatQuestionCard({ title: this.getTitleForContext(message.context), content, mode, items, requestId });
11486
+ await this.sendCardRaw(cardContent, message);
11487
+ } else {
11488
+ const content = message.context ? this.formatContextToMarkdown(message.context) : message.text || "";
11489
+ let meta = "";
11490
+ if (message.context?.eventType === "turn:finished") {
11491
+ const finishedCtx = message.context;
11492
+ const model = finishedCtx.model || finishedCtx.agentType || "-";
11493
+ const duration = formatDuration3(finishedCtx.durationMs);
11494
+ const outTokens = finishedCtx.usage?.outputTokens;
11495
+ const inTokens = finishedCtx.usage?.inputTokens;
11496
+ const tokens = outTokens !== void 0 && inTokens !== void 0 ? `${formatTokenCount(inTokens)}\u2191 ${formatTokenCount(outTokens)}\u2193` : "-";
11497
+ meta = `${model} \xB7 ${duration} \xB7 ${tokens}`;
11498
+ }
11499
+ const cardContent = this.formatter.formatMessageCard({ title: this.getTitleForContext(message.context), content, meta });
11500
+ await this.sendCardRaw(cardContent, message);
11501
+ }
11502
+ if ((eventType === "permission:request" || eventType === "question:request") && requestId) {
10617
11503
  if (!this.pendingRequests.has(requestId)) {
10618
11504
  this.pendingRequests.set(requestId, /* @__PURE__ */ new Set());
10619
11505
  }
@@ -10731,7 +11617,7 @@ var FeishuChannel = class extends (_a4 = BaseChannel) {
10731
11617
  this.pendingOptions.delete(event.requestId);
10732
11618
  }
10733
11619
  /**
10734
- * 处理卡片按钮回调
11620
+ * Handle card button / checker callbacks
10735
11621
  */
10736
11622
  async handleCardAction(data) {
10737
11623
  const { action, requestId, userId, optionId } = data;
@@ -10743,10 +11629,6 @@ var FeishuChannel = class extends (_a4 = BaseChannel) {
10743
11629
  this.logger.warn({ requestId }, "Invalid request ID format");
10744
11630
  return;
10745
11631
  }
10746
- if (!["agree", "reject", "always_allow", "select"].includes(action)) {
10747
- this.logger.warn({ action }, "Invalid card action");
10748
- return;
10749
- }
10750
11632
  if (!this.isAllowed(userId)) {
10751
11633
  this.logger.warn({ userId, requestId }, "Unauthorized user tried to respond to request");
10752
11634
  return;
@@ -10760,6 +11642,47 @@ var FeishuChannel = class extends (_a4 = BaseChannel) {
10760
11642
  this.logger.warn({ userId, requestId }, "User tried to respond to request they did not receive");
10761
11643
  return;
10762
11644
  }
11645
+ if (action === "check") {
11646
+ if (!optionId) {
11647
+ this.logger.warn({ requestId, userId }, "[Channel] Check missing optionId");
11648
+ return;
11649
+ }
11650
+ if (!this.pendingSelections.has(requestId)) {
11651
+ this.pendingSelections.set(requestId, /* @__PURE__ */ new Set());
11652
+ }
11653
+ const selected = this.pendingSelections.get(requestId);
11654
+ if (data.checked) {
11655
+ selected.add(optionId);
11656
+ } else {
11657
+ selected.delete(optionId);
11658
+ }
11659
+ this.logger.info({ requestId, userId, optionId, checked: data.checked, selectedCount: selected.size }, "[Channel] Checker changed");
11660
+ return;
11661
+ }
11662
+ if (action === "submit") {
11663
+ const selected = this.pendingSelections.get(requestId);
11664
+ const optionIds = selected ? Array.from(selected) : [];
11665
+ if (optionIds.length === 0) {
11666
+ this.logger.warn({ requestId, userId }, "[Channel] Submit with no selections");
11667
+ return;
11668
+ }
11669
+ const options = this.pendingOptions.get(requestId);
11670
+ const selectedValues = optionIds.map((id) => options?.find((opt) => opt.id === id)?.value).filter((v) => v !== void 0);
11671
+ this.bus.publishInteractionResponse({
11672
+ requestId,
11673
+ response: {
11674
+ requestId,
11675
+ action: "select",
11676
+ selectedValues
11677
+ },
11678
+ reason: `User submitted multi-select via Feishu`
11679
+ });
11680
+ this.logger.info({ requestId, userId, selectedCount: optionIds.length }, "[Channel] Multi-select submitted");
11681
+ this.pendingSelections.delete(requestId);
11682
+ this.pendingRequests.delete(requestId);
11683
+ this.pendingOptions.delete(requestId);
11684
+ return;
11685
+ }
10763
11686
  if (action === "agree" || action === "reject" || action === "always_allow") {
10764
11687
  const decision = action === "reject" ? "deny" : "allow";
10765
11688
  const isAlwaysAllow = action === "always_allow";
@@ -10768,7 +11691,6 @@ var FeishuChannel = class extends (_a4 = BaseChannel) {
10768
11691
  rules: [{ toolName: this.pendingToolNames.get(requestId) ?? "*", ruleContent: "*" }],
10769
11692
  behavior: "allow",
10770
11693
  destination: "localSettings"
10771
- // Project's settings.local.json
10772
11694
  }] : void 0;
10773
11695
  this.bus.publishInteractionResponse({
10774
11696
  requestId,
@@ -10780,6 +11702,8 @@ var FeishuChannel = class extends (_a4 = BaseChannel) {
10780
11702
  reason: `User clicked ${action} button via Feishu`
10781
11703
  });
10782
11704
  this.pendingToolNames.delete(requestId);
11705
+ this.pendingRequests.delete(requestId);
11706
+ this.pendingOptions.delete(requestId);
10783
11707
  this.logger.info({ requestId, userId, action: decision }, "[Channel] User response received");
10784
11708
  return;
10785
11709
  }
@@ -10804,28 +11728,20 @@ var FeishuChannel = class extends (_a4 = BaseChannel) {
10804
11728
  reason: `User selected option ${optionId} via Feishu`
10805
11729
  });
10806
11730
  this.logger.info({ requestId, userId, action: "select", optionId }, "[Channel] User response received");
11731
+ this.pendingRequests.delete(requestId);
11732
+ this.pendingOptions.delete(requestId);
10807
11733
  return;
10808
11734
  }
11735
+ this.logger.warn({ action, requestId }, "Unknown card action");
10809
11736
  }
10810
11737
  /**
10811
- * 发送带按钮的卡片消息
11738
+ * Send a pre-built card JSON string
10812
11739
  */
10813
- async sendCard(message, buttons) {
11740
+ async sendCardRaw(cardContent, message) {
10814
11741
  if (!this.client) {
10815
11742
  this.logger.error("Feishu client not initialized");
10816
11743
  throw new Error("Feishu client not initialized");
10817
11744
  }
10818
- let content;
10819
- if (message.context) {
10820
- content = this.formatContextToMarkdown(message.context);
10821
- } else if (message.text) {
10822
- content = message.text;
10823
- } else {
10824
- this.logger.warn("No content to send in Feishu card message");
10825
- return;
10826
- }
10827
- const title = this.getTitleForContext(message.context);
10828
- const cardContent = this.formatter.formatCard({ title, content, buttons });
10829
11745
  const targetUsers = this.feishuConfig.allowed_users || [];
10830
11746
  for (const userId of targetUsers) {
10831
11747
  if (userId === "*") continue;
@@ -10887,20 +11803,10 @@ ${context.thinking}`;
10887
11803
  `;
10888
11804
  if (context.cwd) {
10889
11805
  md += `**WorkDir:** ${context.cwd}
10890
- `;
10891
- }
10892
- md += `**Status:** Turn finished
10893
- `;
10894
- if (context.durationMs) {
10895
- md += `**Duration:** ${(context.durationMs / 1e3).toFixed(1)}s
10896
- `;
10897
- }
10898
- if (context.usage?.inputTokens !== void 0 && context.usage?.outputTokens !== void 0) {
10899
- md += `**Tokens:** in ${context.usage.inputTokens} / out ${context.usage.outputTokens}
10900
11806
  `;
10901
11807
  }
10902
11808
  if (context.lastAssistantMessage) {
10903
- md += `**Message:** ${context.lastAssistantMessage}`;
11809
+ md += `${context.lastAssistantMessage}`;
10904
11810
  }
10905
11811
  break;
10906
11812
  case "tool:post":
@@ -11001,7 +11907,11 @@ _init3 = __decoratorStart(_a4);
11001
11907
  FeishuChannel = __decorateElement(_init3, 0, "FeishuChannel", _FeishuChannel_decorators, FeishuChannel);
11002
11908
  __runInitializers(_init3, 1, FeishuChannel);
11003
11909
 
11910
+ // src/adapters/channels/app/index.ts
11911
+ init_esm_shims();
11912
+
11004
11913
  // src/adapters/channels/app/mapper.ts
11914
+ init_esm_shims();
11005
11915
  function mapOutboundMessageToAppEvent(message, options = {}) {
11006
11916
  const context = message.context;
11007
11917
  if (!context) {
@@ -11161,6 +12071,7 @@ function readChannelInstanceId(message) {
11161
12071
  }
11162
12072
 
11163
12073
  // src/adapters/channels/app/server.ts
12074
+ init_esm_shims();
11164
12075
  import { WebSocketServer as NodeWebSocketServer, WebSocket as WebSocket2 } from "ws";
11165
12076
  var logger4 = getLogger();
11166
12077
  var DEFAULT_PATH = "/ws/channels/app";
@@ -11211,8 +12122,8 @@ var AppChannelWebSocketServer = class {
11211
12122
  if (!this.wss) {
11212
12123
  return;
11213
12124
  }
11214
- await new Promise((resolve2) => {
11215
- this.wss?.close(() => resolve2());
12125
+ await new Promise((resolve3) => {
12126
+ this.wss?.close(() => resolve3());
11216
12127
  });
11217
12128
  this.wss = null;
11218
12129
  }
@@ -11405,9 +12316,11 @@ function readFrameId(frame) {
11405
12316
  }
11406
12317
 
11407
12318
  // src/adapters/channels/app/protocol.ts
12319
+ init_esm_shims();
11408
12320
  import { randomUUID as randomUUID5 } from "crypto";
11409
12321
 
11410
12322
  // src/adapters/channels/app/query-service.ts
12323
+ init_esm_shims();
11411
12324
  var DEFAULT_CAPABILITIES = {
11412
12325
  chat: true,
11413
12326
  multiTurn: true,
@@ -11763,6 +12676,7 @@ function error(id, code, message) {
11763
12676
  }
11764
12677
 
11765
12678
  // src/core/session/SessionRecoveryService.ts
12679
+ init_esm_shims();
11766
12680
  import { readdirSync as readdirSync2, readFileSync as readFileSync5 } from "fs";
11767
12681
  import { join as join7, extname } from "path";
11768
12682
  var SessionRecoveryService = class {
@@ -11813,7 +12727,11 @@ var SessionRecoveryService = class {
11813
12727
  await gateway.createSession(
11814
12728
  claudeAgent.token,
11815
12729
  sessionData.sessionId,
11816
- { metadata: { kind: sessionData.kind, entrypoint: sessionData.entrypoint }, cwd: sessionData.cwd },
12730
+ {
12731
+ metadata: { kind: sessionData.kind, entrypoint: sessionData.entrypoint },
12732
+ cwd: sessionData.cwd,
12733
+ createdAt: sessionData.startedAt
12734
+ },
11817
12735
  true
11818
12736
  // skipPublishSessionStart — don't duplicate start notification
11819
12737
  );
@@ -11845,9 +12763,9 @@ var SessionRecoveryService = class {
11845
12763
  return [];
11846
12764
  }
11847
12765
  }
11848
- parseSessionFile(path2) {
12766
+ parseSessionFile(path3) {
11849
12767
  try {
11850
- const raw = readFileSync5(path2, "utf-8");
12768
+ const raw = readFileSync5(path3, "utf-8");
11851
12769
  const data = JSON.parse(raw);
11852
12770
  if (!data.sessionId || typeof data.pid !== "number") {
11853
12771
  return null;
@@ -11855,7 +12773,7 @@ var SessionRecoveryService = class {
11855
12773
  return data;
11856
12774
  } catch (err) {
11857
12775
  this.logger.warn(
11858
- { file: path2, error: err instanceof Error ? err.message : String(err) },
12776
+ { file: path3, error: err instanceof Error ? err.message : String(err) },
11859
12777
  "Failed to parse session file"
11860
12778
  );
11861
12779
  return null;
@@ -11983,6 +12901,7 @@ async function main() {
11983
12901
  bus,
11984
12902
  logger5,
11985
12903
  (id) => gateway.getSessionAgentType(id),
12904
+ (id) => gateway.getSessionInfo(id),
11986
12905
  bindingService4
11987
12906
  );
11988
12907
  notificationHandler.start();