opencode-lark 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/LICENSE +21 -0
  3. package/README.md +322 -0
  4. package/README.zh-CN.md +324 -0
  5. package/bin/opencode-lark.js +2 -0
  6. package/dist/channel/base-plugin.d.ts +31 -0
  7. package/dist/channel/base-plugin.d.ts.map +1 -0
  8. package/dist/channel/base-plugin.js +42 -0
  9. package/dist/channel/base-plugin.js.map +1 -0
  10. package/dist/channel/feishu/feishu-plugin.d.ts +36 -0
  11. package/dist/channel/feishu/feishu-plugin.d.ts.map +1 -0
  12. package/dist/channel/feishu/feishu-plugin.js +149 -0
  13. package/dist/channel/feishu/feishu-plugin.js.map +1 -0
  14. package/dist/channel/feishu/index.d.ts +2 -0
  15. package/dist/channel/feishu/index.d.ts.map +1 -0
  16. package/dist/channel/feishu/index.js +2 -0
  17. package/dist/channel/feishu/index.js.map +1 -0
  18. package/dist/channel/index.d.ts +4 -0
  19. package/dist/channel/index.d.ts.map +1 -0
  20. package/dist/channel/index.js +4 -0
  21. package/dist/channel/index.js.map +1 -0
  22. package/dist/channel/manager.d.ts +37 -0
  23. package/dist/channel/manager.d.ts.map +1 -0
  24. package/dist/channel/manager.js +68 -0
  25. package/dist/channel/manager.js.map +1 -0
  26. package/dist/channel/mock/mock-plugin.d.ts +24 -0
  27. package/dist/channel/mock/mock-plugin.d.ts.map +1 -0
  28. package/dist/channel/mock/mock-plugin.js +42 -0
  29. package/dist/channel/mock/mock-plugin.js.map +1 -0
  30. package/dist/channel/types.d.ts +226 -0
  31. package/dist/channel/types.d.ts.map +1 -0
  32. package/dist/channel/types.js +7 -0
  33. package/dist/channel/types.js.map +1 -0
  34. package/dist/cron/cron-service.d.ts +40 -0
  35. package/dist/cron/cron-service.d.ts.map +1 -0
  36. package/dist/cron/cron-service.js +140 -0
  37. package/dist/cron/cron-service.js.map +1 -0
  38. package/dist/cron/heartbeat.d.ts +30 -0
  39. package/dist/cron/heartbeat.d.ts.map +1 -0
  40. package/dist/cron/heartbeat.js +76 -0
  41. package/dist/cron/heartbeat.js.map +1 -0
  42. package/dist/feishu/api-client.d.ts +19 -0
  43. package/dist/feishu/api-client.d.ts.map +1 -0
  44. package/dist/feishu/api-client.js +98 -0
  45. package/dist/feishu/api-client.js.map +1 -0
  46. package/dist/feishu/card-builder.d.ts +10 -0
  47. package/dist/feishu/card-builder.d.ts.map +1 -0
  48. package/dist/feishu/card-builder.js +74 -0
  49. package/dist/feishu/card-builder.js.map +1 -0
  50. package/dist/feishu/cardkit-client.d.ts +48 -0
  51. package/dist/feishu/cardkit-client.d.ts.map +1 -0
  52. package/dist/feishu/cardkit-client.js +97 -0
  53. package/dist/feishu/cardkit-client.js.map +1 -0
  54. package/dist/feishu/message-dedup.d.ts +28 -0
  55. package/dist/feishu/message-dedup.d.ts.map +1 -0
  56. package/dist/feishu/message-dedup.js +58 -0
  57. package/dist/feishu/message-dedup.js.map +1 -0
  58. package/dist/feishu/webhook-server.d.ts +20 -0
  59. package/dist/feishu/webhook-server.d.ts.map +1 -0
  60. package/dist/feishu/webhook-server.js +111 -0
  61. package/dist/feishu/webhook-server.js.map +1 -0
  62. package/dist/feishu/ws-client.d.ts +17 -0
  63. package/dist/feishu/ws-client.d.ts.map +1 -0
  64. package/dist/feishu/ws-client.js +158 -0
  65. package/dist/feishu/ws-client.js.map +1 -0
  66. package/dist/handler/interactive-handler.d.ts +16 -0
  67. package/dist/handler/interactive-handler.d.ts.map +1 -0
  68. package/dist/handler/interactive-handler.js +86 -0
  69. package/dist/handler/interactive-handler.js.map +1 -0
  70. package/dist/handler/interactive-poller.d.ts +16 -0
  71. package/dist/handler/interactive-poller.d.ts.map +1 -0
  72. package/dist/handler/interactive-poller.js +147 -0
  73. package/dist/handler/interactive-poller.js.map +1 -0
  74. package/dist/handler/message-handler.d.ts +34 -0
  75. package/dist/handler/message-handler.d.ts.map +1 -0
  76. package/dist/handler/message-handler.js +305 -0
  77. package/dist/handler/message-handler.js.map +1 -0
  78. package/dist/handler/streaming-integration.d.ts +21 -0
  79. package/dist/handler/streaming-integration.d.ts.map +1 -0
  80. package/dist/handler/streaming-integration.js +325 -0
  81. package/dist/handler/streaming-integration.js.map +1 -0
  82. package/dist/index.d.ts +16 -0
  83. package/dist/index.d.ts.map +1 -0
  84. package/dist/index.js +281 -0
  85. package/dist/index.js.map +1 -0
  86. package/dist/memory/memory-manager.d.ts +16 -0
  87. package/dist/memory/memory-manager.d.ts.map +1 -0
  88. package/dist/memory/memory-manager.js +58 -0
  89. package/dist/memory/memory-manager.js.map +1 -0
  90. package/dist/session/progress-tracker.d.ts +12 -0
  91. package/dist/session/progress-tracker.d.ts.map +1 -0
  92. package/dist/session/progress-tracker.js +46 -0
  93. package/dist/session/progress-tracker.js.map +1 -0
  94. package/dist/session/session-manager.d.ts +15 -0
  95. package/dist/session/session-manager.d.ts.map +1 -0
  96. package/dist/session/session-manager.js +91 -0
  97. package/dist/session/session-manager.js.map +1 -0
  98. package/dist/streaming/event-processor.d.ts +74 -0
  99. package/dist/streaming/event-processor.d.ts.map +1 -0
  100. package/dist/streaming/event-processor.js +240 -0
  101. package/dist/streaming/event-processor.js.map +1 -0
  102. package/dist/streaming/session-observer.d.ts +19 -0
  103. package/dist/streaming/session-observer.d.ts.map +1 -0
  104. package/dist/streaming/session-observer.js +140 -0
  105. package/dist/streaming/session-observer.js.map +1 -0
  106. package/dist/streaming/streaming-card.d.ts +37 -0
  107. package/dist/streaming/streaming-card.d.ts.map +1 -0
  108. package/dist/streaming/streaming-card.js +139 -0
  109. package/dist/streaming/streaming-card.js.map +1 -0
  110. package/dist/streaming/subagent-card.d.ts +32 -0
  111. package/dist/streaming/subagent-card.d.ts.map +1 -0
  112. package/dist/streaming/subagent-card.js +103 -0
  113. package/dist/streaming/subagent-card.js.map +1 -0
  114. package/dist/streaming/subagent-tracker.d.ts +45 -0
  115. package/dist/streaming/subagent-tracker.d.ts.map +1 -0
  116. package/dist/streaming/subagent-tracker.js +118 -0
  117. package/dist/streaming/subagent-tracker.js.map +1 -0
  118. package/dist/types.d.ts +55 -0
  119. package/dist/types.d.ts.map +1 -0
  120. package/dist/types.js +5 -0
  121. package/dist/types.js.map +1 -0
  122. package/dist/utils/config.d.ts +197 -0
  123. package/dist/utils/config.d.ts.map +1 -0
  124. package/dist/utils/config.js +87 -0
  125. package/dist/utils/config.js.map +1 -0
  126. package/dist/utils/db.d.ts +12 -0
  127. package/dist/utils/db.d.ts.map +1 -0
  128. package/dist/utils/db.js +35 -0
  129. package/dist/utils/db.js.map +1 -0
  130. package/dist/utils/event-listeners.d.ts +12 -0
  131. package/dist/utils/event-listeners.d.ts.map +1 -0
  132. package/dist/utils/event-listeners.js +21 -0
  133. package/dist/utils/event-listeners.js.map +1 -0
  134. package/dist/utils/logger.d.ts +11 -0
  135. package/dist/utils/logger.d.ts.map +1 -0
  136. package/dist/utils/logger.js +38 -0
  137. package/dist/utils/logger.js.map +1 -0
  138. package/package.json +41 -0
@@ -0,0 +1,140 @@
1
+ // ═══════════════════════════════════════════
2
+ // Session Observer
3
+ // Observes opencode sessions and forwards TUI-initiated
4
+ // messages to Feishu chats.
5
+ // ═══════════════════════════════════════════
6
+ import { buildQuestionCard, buildPermissionCard } from "../handler/streaming-integration.js";
7
+ // ---------------------------------------------------------------------------
8
+ // Helpers
9
+ // ---------------------------------------------------------------------------
10
+ function extractMessageId(rawEvent) {
11
+ if (rawEvent === null || typeof rawEvent !== "object")
12
+ return undefined;
13
+ const props = rawEvent.properties;
14
+ if (!props || typeof props !== "object")
15
+ return undefined;
16
+ const p = props;
17
+ // message.part.updated → properties.part.messageID
18
+ // message.part.delta → properties.messageID
19
+ const part = p.part;
20
+ if (part && typeof part === "object") {
21
+ const mid = part.messageID;
22
+ if (typeof mid === "string")
23
+ return mid;
24
+ }
25
+ const mid = p.messageID;
26
+ if (typeof mid === "string")
27
+ return mid;
28
+ return undefined;
29
+ }
30
+ // ---------------------------------------------------------------------------
31
+ // Factory
32
+ // ---------------------------------------------------------------------------
33
+ export function createSessionObserver(deps) {
34
+ const { feishuClient, eventProcessor, addListener, removeListener, logger, seenInteractiveIds } = deps;
35
+ // Feishu-initiated message IDs — skip these in forwarding
36
+ const knownMessageIds = new Set();
37
+ // Per-messageId text accumulation
38
+ const textBuffers = new Map();
39
+ // Active observation state per session
40
+ const observedSessions = new Map();
41
+ function flushBuffers(chatId) {
42
+ for (const [messageId, text] of textBuffers) {
43
+ if (text.trim().length === 0)
44
+ continue;
45
+ feishuClient
46
+ .sendMessage(chatId, {
47
+ msg_type: "text",
48
+ content: JSON.stringify({ text }),
49
+ })
50
+ .catch((err) => {
51
+ logger.error(`Failed to send TUI message for ${messageId}: ${err}`);
52
+ });
53
+ }
54
+ textBuffers.clear();
55
+ }
56
+ return {
57
+ observe(sessionId, chatId) {
58
+ if (observedSessions.has(sessionId))
59
+ return;
60
+ const listener = (rawEvent) => {
61
+ const action = eventProcessor.processEvent(rawEvent);
62
+ if (!action)
63
+ return;
64
+ const messageId = extractMessageId(rawEvent);
65
+ // Skip events belonging to Feishu-initiated messages
66
+ if (messageId && knownMessageIds.has(messageId))
67
+ return;
68
+ switch (action.type) {
69
+ case "TextDelta": {
70
+ if (!messageId)
71
+ break;
72
+ const current = textBuffers.get(messageId) ?? "";
73
+ textBuffers.set(messageId, current + action.text);
74
+ break;
75
+ }
76
+ case "SessionIdle": {
77
+ flushBuffers(chatId);
78
+ break;
79
+ }
80
+ case "QuestionAsked": {
81
+ if (seenInteractiveIds.has(action.requestId))
82
+ break;
83
+ seenInteractiveIds.add(action.requestId);
84
+ logger.info(`Question event received in observer for chat ${chatId}, requestId=${action.requestId}`);
85
+ const questionCard = buildQuestionCard(action);
86
+ feishuClient
87
+ .sendMessage(chatId, {
88
+ msg_type: "interactive",
89
+ content: JSON.stringify(questionCard),
90
+ })
91
+ .catch((err) => {
92
+ logger.warn(`Question card send failed (observer): ${err}`);
93
+ });
94
+ break;
95
+ }
96
+ case "PermissionRequested": {
97
+ if (seenInteractiveIds.has(action.requestId))
98
+ break;
99
+ seenInteractiveIds.add(action.requestId);
100
+ logger.info(`Permission event received in observer for chat ${chatId}, requestId=${action.requestId}`);
101
+ const permissionCard = buildPermissionCard(action);
102
+ feishuClient
103
+ .sendMessage(chatId, {
104
+ msg_type: "interactive",
105
+ content: JSON.stringify(permissionCard),
106
+ })
107
+ .catch((err) => {
108
+ logger.warn(`Permission card send failed (observer): ${err}`);
109
+ });
110
+ break;
111
+ }
112
+ default:
113
+ // ToolStateChange, SubtaskDiscovered — ignored
114
+ break;
115
+ }
116
+ };
117
+ addListener(sessionId, listener);
118
+ observedSessions.set(sessionId, { chatId, listener });
119
+ logger.info(`Observing session ${sessionId} for chat ${chatId}`);
120
+ },
121
+ markOwned(messageId) {
122
+ knownMessageIds.add(messageId);
123
+ // Drop any buffered text for this message
124
+ textBuffers.delete(messageId);
125
+ },
126
+ getChatForSession(sessionId) {
127
+ return observedSessions.get(sessionId)?.chatId;
128
+ },
129
+ stop() {
130
+ for (const [sessionId, { listener }] of observedSessions) {
131
+ removeListener(sessionId, listener);
132
+ }
133
+ observedSessions.clear();
134
+ textBuffers.clear();
135
+ knownMessageIds.clear();
136
+ logger.info("Session observer stopped");
137
+ },
138
+ };
139
+ }
140
+ //# sourceMappingURL=session-observer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-observer.js","sourceRoot":"","sources":["../../src/streaming/session-observer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * High-level streaming card session manager.
3
+ * Wraps CardKitClient with throttling, tool status, sub-agent buttons,
4
+ * and full lifecycle management.
5
+ */
6
+ import type { CardKitClient } from "../feishu/cardkit-client.js";
7
+ import type { FeishuApiClient } from "../feishu/api-client.js";
8
+ export interface StreamingCardOptions {
9
+ cardkitClient: CardKitClient;
10
+ feishuClient: FeishuApiClient;
11
+ chatId: string;
12
+ }
13
+ export declare class StreamingCardSession {
14
+ private readonly cardkitClient;
15
+ private readonly feishuClient;
16
+ private readonly chatId;
17
+ private state;
18
+ private closed;
19
+ private queue;
20
+ private lastUpdateTime;
21
+ private pendingText;
22
+ private lastSentContent;
23
+ private readonly updateThrottleMs;
24
+ private toolStatuses;
25
+ private subtaskButtons;
26
+ constructor(options: StreamingCardOptions);
27
+ get isActive(): boolean;
28
+ start(): Promise<void>;
29
+ setToolStatus(name: string, state: "running" | "completed" | "error", title?: string): Promise<void>;
30
+ addSubtaskButton(label: string, actionValue: string): Promise<void>;
31
+ close(finalText?: string): Promise<void>;
32
+ private enqueueUpdate;
33
+ private buildToolStatusText;
34
+ private buildButtonsText;
35
+ private buildFullContent;
36
+ }
37
+ //# sourceMappingURL=streaming-card.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streaming-card.d.ts","sourceRoot":"","sources":["../../src/streaming/streaming-card.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAiB,MAAM,6BAA6B,CAAA;AAC/E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AAE9D,MAAM,WAAW,oBAAoB;IACnC,aAAa,EAAE,aAAa,CAAA;IAC5B,YAAY,EAAE,eAAe,CAAA;IAC7B,MAAM,EAAE,MAAM,CAAA;CACf;AAoBD,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAe;IAC7C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAiB;IAC9C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;IAE/B,OAAO,CAAC,KAAK,CAAyB;IACtC,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,KAAK,CAAmC;IAChD,OAAO,CAAC,cAAc,CAAI;IAC1B,OAAO,CAAC,WAAW,CAAsB;IACzC,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAM;IAEvC,OAAO,CAAC,YAAY,CAAmB;IACvC,OAAO,CAAC,cAAc,CAAsB;gBAEhC,OAAO,EAAE,oBAAoB;IAMzC,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAsCtB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,WAAW,GAAG,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBpG,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAYnE,KAAK,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YA8BhC,aAAa;IAiB3B,OAAO,CAAC,mBAAmB;IAe3B,OAAO,CAAC,gBAAgB;IAUxB,OAAO,CAAC,gBAAgB;CAKzB"}
@@ -0,0 +1,139 @@
1
+ /**
2
+ * High-level streaming card session manager.
3
+ * Wraps CardKitClient with throttling, tool status, sub-agent buttons,
4
+ * and full lifecycle management.
5
+ */
6
+ export class StreamingCardSession {
7
+ cardkitClient;
8
+ feishuClient;
9
+ chatId;
10
+ state = null;
11
+ closed = false;
12
+ queue = Promise.resolve();
13
+ lastUpdateTime = 0;
14
+ pendingText = null;
15
+ lastSentContent = "";
16
+ updateThrottleMs = 100;
17
+ toolStatuses = [];
18
+ subtaskButtons = [];
19
+ constructor(options) {
20
+ this.cardkitClient = options.cardkitClient;
21
+ this.feishuClient = options.feishuClient;
22
+ this.chatId = options.chatId;
23
+ }
24
+ get isActive() {
25
+ return this.state !== null && !this.closed;
26
+ }
27
+ async start() {
28
+ if (this.state) {
29
+ return;
30
+ }
31
+ const cardJson = {
32
+ schema: "2.0",
33
+ config: {
34
+ streaming_mode: true,
35
+ summary: { content: "[Generating...]" },
36
+ streaming_config: {
37
+ print_frequency_ms: { default: 200 },
38
+ print_step: { default: 10 },
39
+ },
40
+ },
41
+ body: {
42
+ elements: [
43
+ { tag: "markdown", content: "🛠️ Processing...", element_id: "content" },
44
+ ],
45
+ },
46
+ };
47
+ const cardId = await this.cardkitClient.createCard(cardJson);
48
+ const result = await this.feishuClient.sendMessage(this.chatId, {
49
+ msg_type: "interactive",
50
+ content: JSON.stringify({ type: "card", data: { card_id: cardId } }),
51
+ });
52
+ const messageId = result.data?.["message_id"];
53
+ if (!messageId) {
54
+ throw new Error("sendMessage returned no message_id");
55
+ }
56
+ this.state = { cardId, messageId, sequence: 1, currentText: "" };
57
+ }
58
+ async setToolStatus(name, state, title) {
59
+ if (!this.state || this.closed) {
60
+ return;
61
+ }
62
+ const existing = this.toolStatuses.find((t) => t.name === name);
63
+ if (existing) {
64
+ existing.state = state;
65
+ if (title !== undefined)
66
+ existing.title = title;
67
+ }
68
+ else {
69
+ this.toolStatuses.push({ name, state, title });
70
+ }
71
+ // Build tool status text and update the content element
72
+ const fullContent = this.buildFullContent();
73
+ await this.enqueueUpdate(fullContent);
74
+ }
75
+ async addSubtaskButton(label, actionValue) {
76
+ if (!this.state || this.closed) {
77
+ return;
78
+ }
79
+ this.subtaskButtons.push({ label, actionValue });
80
+ // Rebuild full content with buttons section
81
+ const fullContent = this.buildFullContent();
82
+ await this.enqueueUpdate(fullContent);
83
+ }
84
+ async close(finalText) {
85
+ if (!this.state || this.closed) {
86
+ return;
87
+ }
88
+ this.closed = true;
89
+ await this.queue;
90
+ // Final content: use finalText override, tool status content, or "Done" fallback
91
+ const text = finalText ?? (this.toolStatuses.length > 0 ? this.buildFullContent() : "✅ Done");
92
+ // Only send final update if content differs from what was last sent
93
+ if (text && text !== this.lastSentContent) {
94
+ this.state.sequence += 1;
95
+ await this.cardkitClient.updateElement(this.state.cardId, "content", text, this.state.sequence);
96
+ }
97
+ // Close streaming mode with tool-focused summary
98
+ const completed = this.toolStatuses.filter(t => t.state === "completed").length;
99
+ const summary = completed > 0 ? `✅ ${completed} tool(s) used` : "Done";
100
+ this.state.sequence += 1;
101
+ await this.cardkitClient.closeStreaming(this.state.cardId, summary, this.state.sequence);
102
+ }
103
+ async enqueueUpdate(content) {
104
+ this.queue = this.queue.then(async () => {
105
+ if (!this.state || this.closed) {
106
+ return;
107
+ }
108
+ this.state.sequence += 1;
109
+ await this.cardkitClient.updateElement(this.state.cardId, "content", content, this.state.sequence);
110
+ this.lastSentContent = content;
111
+ });
112
+ await this.queue;
113
+ }
114
+ buildToolStatusText() {
115
+ if (this.toolStatuses.length === 0) {
116
+ return "";
117
+ }
118
+ const icons = {
119
+ running: "🔄",
120
+ completed: "✅",
121
+ error: "❌",
122
+ };
123
+ const lines = this.toolStatuses.map((t) => t.title ? `${icons[t.state]} ${t.name} · ${t.title}` : `${icons[t.state]} ${t.name}`);
124
+ return "\n\n---\n" + lines.join("\n");
125
+ }
126
+ buildButtonsText() {
127
+ if (this.subtaskButtons.length === 0) {
128
+ return "";
129
+ }
130
+ const lines = this.subtaskButtons.map((b) => `🔗 [${b.label}](${b.actionValue})`);
131
+ return "\n\n---\n" + lines.join("\n");
132
+ }
133
+ buildFullContent() {
134
+ const toolText = this.buildToolStatusText();
135
+ const buttonText = this.buildButtonsText();
136
+ return toolText + buttonText || "🛠️ Processing...";
137
+ }
138
+ }
139
+ //# sourceMappingURL=streaming-card.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streaming-card.js","sourceRoot":"","sources":["../../src/streaming/streaming-card.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Sub-agent card handler for Feishu.
3
+ * Handles button clicks to view sub-agent session conversations.
4
+ * Formats messages and sends as interactive cards.
5
+ */
6
+ import type { Logger } from "../utils/logger.js";
7
+ import type { FeishuApiClient } from "../feishu/api-client.js";
8
+ import type { FeishuCardAction } from "../types.js";
9
+ import type { SubAgentTracker, MessageSummary } from "./subagent-tracker.js";
10
+ export interface SubAgentCardDeps {
11
+ subAgentTracker: SubAgentTracker;
12
+ feishuClient: FeishuApiClient;
13
+ logger: Logger;
14
+ }
15
+ /**
16
+ * Creates a handler for sub-agent card button clicks.
17
+ * Fetches child session messages and sends as Feishu card.
18
+ */
19
+ export declare function createSubAgentCardHandler(deps: SubAgentCardDeps): (action: FeishuCardAction) => Promise<void>;
20
+ /**
21
+ * Formats messages with role indicators and truncation.
22
+ * Role indicators: 👤 user / 🤖 assistant / 🛠 tool
23
+ * Truncates at 4000 chars with "...(内容过长,已截断)"
24
+ */
25
+ export declare function formatSubAgentMessages(messages: MessageSummary[]): string;
26
+ /**
27
+ * Builds a Feishu card for sub-agent messages.
28
+ * Header: "🔍 子任务详情" with blue template
29
+ * Body: div with lark_md content
30
+ */
31
+ export declare function buildSubAgentCard(description: string, content: string): Record<string, unknown>;
32
+ //# sourceMappingURL=subagent-card.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subagent-card.d.ts","sourceRoot":"","sources":["../../src/streaming/subagent-card.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAA;AAChD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AAC9D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AACnD,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAM5E,MAAM,WAAW,gBAAgB;IAC/B,eAAe,EAAE,eAAe,CAAA;IAChC,YAAY,EAAE,eAAe,CAAA;IAC7B,MAAM,EAAE,MAAM,CAAA;CACf;AAMD;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,gBAAgB,IAGhD,QAAQ,gBAAgB,KAAG,OAAO,CAAC,IAAI,CAAC,CAkCvD;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,MAAM,CAkCzE;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,GACd,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAoBzB"}
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Sub-agent card handler for Feishu.
3
+ * Handles button clicks to view sub-agent session conversations.
4
+ * Formats messages and sends as interactive cards.
5
+ */
6
+ // ─────────────────────────────────────────────────────────
7
+ // Exports
8
+ // ─────────────────────────────────────────────────────────
9
+ /**
10
+ * Creates a handler for sub-agent card button clicks.
11
+ * Fetches child session messages and sends as Feishu card.
12
+ */
13
+ export function createSubAgentCardHandler(deps) {
14
+ const { subAgentTracker, feishuClient, logger } = deps;
15
+ return async (action) => {
16
+ // Check action type early
17
+ const actionType = action.action?.value?.action;
18
+ if (actionType !== "view_subagent") {
19
+ return;
20
+ }
21
+ const childSessionId = action.action?.value?.childSessionId;
22
+ const messageId = action.open_message_id;
23
+ if (!childSessionId || !messageId) {
24
+ logger.warn("Missing childSessionId or messageId in subagent card action");
25
+ return;
26
+ }
27
+ try {
28
+ // Fetch child messages
29
+ const messages = await subAgentTracker.getChildMessages(childSessionId, 50);
30
+ // Format messages for display
31
+ const content = formatSubAgentMessages(messages);
32
+ // Build and send card
33
+ const card = buildSubAgentCard("子任务进展", content);
34
+ await feishuClient.replyMessage(messageId, {
35
+ msg_type: "interactive",
36
+ content: JSON.stringify(card),
37
+ });
38
+ logger.info(`Sent subagent card for session ${childSessionId}`);
39
+ }
40
+ catch (error) {
41
+ logger.error(`Failed to send subagent card: ${String(error)}`);
42
+ }
43
+ };
44
+ }
45
+ /**
46
+ * Formats messages with role indicators and truncation.
47
+ * Role indicators: 👤 user / 🤖 assistant / 🛠 tool
48
+ * Truncates at 4000 chars with "...(内容过长,已截断)"
49
+ */
50
+ export function formatSubAgentMessages(messages) {
51
+ if (messages.length === 0) {
52
+ return "暂无对话内容";
53
+ }
54
+ const roleIcons = {
55
+ user: "👤",
56
+ assistant: "🤖",
57
+ tool: "🛠",
58
+ };
59
+ const lines = [];
60
+ for (const msg of messages) {
61
+ const icon = roleIcons[msg.role] ?? "❓";
62
+ const roleLabel = msg.role.charAt(0).toUpperCase() + msg.role.slice(1);
63
+ lines.push(`${icon} **${roleLabel}**`);
64
+ lines.push(msg.text);
65
+ if (msg.toolCalls && msg.toolCalls.length > 0) {
66
+ lines.push(` _工具调用: ${msg.toolCalls.join(", ")}_`);
67
+ }
68
+ lines.push("");
69
+ }
70
+ let content = lines.join("\n");
71
+ // Truncate at 4000 chars
72
+ if (content.length > 4000) {
73
+ content = content.slice(0, 4000) + "\n\n...(内容过长,已截断)";
74
+ }
75
+ return content;
76
+ }
77
+ /**
78
+ * Builds a Feishu card for sub-agent messages.
79
+ * Header: "🔍 子任务详情" with blue template
80
+ * Body: div with lark_md content
81
+ */
82
+ export function buildSubAgentCard(description, content) {
83
+ return {
84
+ config: { wide_screen_mode: true },
85
+ header: {
86
+ title: {
87
+ tag: "plain_text",
88
+ content: `🔍 ${description}`,
89
+ },
90
+ template: "blue",
91
+ },
92
+ elements: [
93
+ {
94
+ tag: "div",
95
+ text: {
96
+ tag: "lark_md",
97
+ content,
98
+ },
99
+ },
100
+ ],
101
+ };
102
+ }
103
+ //# sourceMappingURL=subagent-card.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subagent-card.js","sourceRoot":"","sources":["../../src/streaming/subagent-card.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ import type { SubtaskDiscovered } from "./event-processor.js";
2
+ export interface TrackedSubAgent {
3
+ parentSessionId: string;
4
+ childSessionId?: string;
5
+ prompt: string;
6
+ description: string;
7
+ agent: string;
8
+ status: "discovering" | "active" | "completed" | "failed";
9
+ }
10
+ export interface MessageSummary {
11
+ role: string;
12
+ text: string;
13
+ toolCalls?: string[];
14
+ }
15
+ export interface SubAgentTrackerOptions {
16
+ serverUrl: string;
17
+ maxDepth?: number;
18
+ }
19
+ export declare class SubAgentTracker {
20
+ private readonly serverUrl;
21
+ private readonly maxDepth;
22
+ private readonly tracked;
23
+ constructor(options: SubAgentTrackerOptions);
24
+ /**
25
+ * Register a newly-discovered sub-agent and start polling for its child session.
26
+ * Returns the TrackedSubAgent immediately; status updates asynchronously.
27
+ */
28
+ onSubtaskDiscovered(action: SubtaskDiscovered, depth?: number): Promise<TrackedSubAgent>;
29
+ /**
30
+ * Poll `GET /session/{parentSessionId}/children` to find the child session.
31
+ * Retries up to 5 times with exponential backoff: 500ms, 1s, 2s, 4s, 8s.
32
+ */
33
+ pollChildSession(parentSessionId: string, retries?: number): Promise<string | null>;
34
+ /**
35
+ * Fetch messages from a child session.
36
+ * GET /session/{childSessionId}/message?limit=N
37
+ */
38
+ getChildMessages(childSessionId: string, limit?: number): Promise<MessageSummary[]>;
39
+ /**
40
+ * Return all tracked sub-agents with current status.
41
+ */
42
+ getTrackedSubAgents(): TrackedSubAgent[];
43
+ private sleep;
44
+ }
45
+ //# sourceMappingURL=subagent-tracker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subagent-tracker.d.ts","sourceRoot":"","sources":["../../src/streaming/subagent-tracker.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;AAM7D,MAAM,WAAW,eAAe;IAC9B,eAAe,EAAE,MAAM,CAAA;IACvB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,MAAM,CAAA;IACnB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,aAAa,GAAG,QAAQ,GAAG,WAAW,GAAG,QAAQ,CAAA;CAC1D;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,CAAC,EAAE,MAAM,EAAE,CAAA;CACrB;AAED,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAaD,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAQ;IAClC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAQ;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwB;gBAEpC,OAAO,EAAE,sBAAsB;IAK3C;;;OAGG;IACG,mBAAmB,CACvB,MAAM,EAAE,iBAAiB,EACzB,KAAK,GAAE,MAAU,GAChB,OAAO,CAAC,eAAe,CAAC;IAkC3B;;;OAGG;IACG,gBAAgB,CACpB,eAAe,EAAE,MAAM,EACvB,OAAO,GAAE,MAAoB,GAC5B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IA8BzB;;;OAGG;IACG,gBAAgB,CACpB,cAAc,EAAE,MAAM,EACtB,KAAK,GAAE,MAAW,GACjB,OAAO,CAAC,cAAc,EAAE,CAAC;IA4B5B;;OAEG;IACH,mBAAmB,IAAI,eAAe,EAAE;IAQxC,OAAO,CAAC,KAAK;CAGd"}
@@ -0,0 +1,118 @@
1
+ // ═══════════════════════════════════════════
2
+ // Sub-Agent Tracker
3
+ // Tracks sub-agent lifecycle and fetches child session details via REST API polling.
4
+ // ═══════════════════════════════════════════
5
+ // ---------------------------------------------------------------------------
6
+ // Constants
7
+ // ---------------------------------------------------------------------------
8
+ const MAX_RETRIES = 5;
9
+ const BACKOFF_BASE_MS = 500;
10
+ // ---------------------------------------------------------------------------
11
+ // SubAgentTracker
12
+ // ---------------------------------------------------------------------------
13
+ export class SubAgentTracker {
14
+ serverUrl;
15
+ maxDepth;
16
+ tracked = [];
17
+ constructor(options) {
18
+ this.serverUrl = options.serverUrl;
19
+ this.maxDepth = Math.min(options.maxDepth ?? 1, 1);
20
+ }
21
+ /**
22
+ * Register a newly-discovered sub-agent and start polling for its child session.
23
+ * Returns the TrackedSubAgent immediately; status updates asynchronously.
24
+ */
25
+ async onSubtaskDiscovered(action, depth = 1) {
26
+ if (depth > this.maxDepth) {
27
+ throw new Error(`Max sub-agent depth is ${this.maxDepth}; requested depth ${depth}`);
28
+ }
29
+ const agent = {
30
+ parentSessionId: action.sessionId,
31
+ prompt: action.prompt,
32
+ description: action.description,
33
+ agent: action.agent,
34
+ status: "discovering",
35
+ };
36
+ this.tracked.push(agent);
37
+ // Fire-and-forget: poll for child session in background
38
+ this.pollChildSession(action.sessionId)
39
+ .then((childSessionId) => {
40
+ if (childSessionId) {
41
+ agent.childSessionId = childSessionId;
42
+ agent.status = "active";
43
+ }
44
+ else {
45
+ agent.status = "failed";
46
+ }
47
+ })
48
+ .catch(() => {
49
+ agent.status = "failed";
50
+ });
51
+ return agent;
52
+ }
53
+ /**
54
+ * Poll `GET /session/{parentSessionId}/children` to find the child session.
55
+ * Retries up to 5 times with exponential backoff: 500ms, 1s, 2s, 4s, 8s.
56
+ */
57
+ async pollChildSession(parentSessionId, retries = MAX_RETRIES) {
58
+ for (let attempt = 0; attempt < retries; attempt++) {
59
+ try {
60
+ const response = await fetch(`${this.serverUrl}/session/${parentSessionId}/children`);
61
+ if (response.ok) {
62
+ const children = (await response.json());
63
+ if (children.length > 0) {
64
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
65
+ return children[children.length - 1].id;
66
+ }
67
+ }
68
+ }
69
+ catch {
70
+ // Network error — will retry
71
+ }
72
+ // Don't sleep after the last attempt
73
+ if (attempt < retries - 1) {
74
+ const delay = BACKOFF_BASE_MS * Math.pow(2, attempt);
75
+ await this.sleep(delay);
76
+ }
77
+ }
78
+ return null;
79
+ }
80
+ /**
81
+ * Fetch messages from a child session.
82
+ * GET /session/{childSessionId}/message?limit=N
83
+ */
84
+ async getChildMessages(childSessionId, limit = 20) {
85
+ try {
86
+ const response = await fetch(`${this.serverUrl}/session/${childSessionId}/message?limit=${limit}`);
87
+ if (!response.ok)
88
+ return [];
89
+ const messages = (await response.json());
90
+ return messages.map((msg) => {
91
+ const summary = {
92
+ role: msg.role ?? "unknown",
93
+ text: msg.text ?? "",
94
+ };
95
+ if (msg.toolCalls && msg.toolCalls.length > 0) {
96
+ summary.toolCalls = msg.toolCalls.map((tc) => tc.name ?? "unknown");
97
+ }
98
+ return summary;
99
+ });
100
+ }
101
+ catch {
102
+ return [];
103
+ }
104
+ }
105
+ /**
106
+ * Return all tracked sub-agents with current status.
107
+ */
108
+ getTrackedSubAgents() {
109
+ return [...this.tracked];
110
+ }
111
+ // -------------------------------------------------------------------------
112
+ // Private helpers
113
+ // -------------------------------------------------------------------------
114
+ sleep(ms) {
115
+ return new Promise((resolve) => setTimeout(resolve, ms));
116
+ }
117
+ }
118
+ //# sourceMappingURL=subagent-tracker.js.map