← back to hs3180__disclaude

Function bodies 284 total

All specs Real LLM only Function bodies
BaseAgent.constructor method · typescript · L102-L114 (13 LOC)
src/agents/base-agent.ts
  constructor(config: BaseAgentConfig) {
    this.apiKey = config.apiKey;
    this.model = config.model;
    this.apiBaseUrl = config.apiBaseUrl;
    this.permissionMode = config.permissionMode ?? 'bypassPermissions';

    // Detect provider from Config
    const agentConfig = Config.getAgentConfig();
    this.provider = agentConfig.provider;

    // Create logger with agent name
    this.logger = createLogger(this.getAgentName());
  }
BaseAgent.createSdkOptions method · typescript · L132-L161 (30 LOC)
src/agents/base-agent.ts
  protected createSdkOptions(extra: SdkOptionsExtra = {}): Record<string, unknown> {
    const sdkOptions: Record<string, unknown> = {
      cwd: extra.cwd ?? Config.getWorkspaceDir(),
      permissionMode: this.permissionMode,
      settingSources: ['project'],
    };

    // Add allowed/disallowed tools
    if (extra.allowedTools) {
      sdkOptions.allowedTools = extra.allowedTools;
    }
    if (extra.disallowedTools) {
      sdkOptions.disallowedTools = extra.disallowedTools;
    }

    // Add MCP servers
    if (extra.mcpServers) {
      sdkOptions.mcpServers = extra.mcpServers;
    }

    // Set environment
    sdkOptions.env = buildSdkEnv(this.apiKey, this.apiBaseUrl, Config.getGlobalEnv());

    // Set model
    if (this.model) {
      sdkOptions.model = this.model;
    }

    return sdkOptions;
  }
BaseAgent.createQueryStream method · typescript · L228-L268 (41 LOC)
src/agents/base-agent.ts
  protected createQueryStream(
    input: AsyncGenerator<SDKUserMessage>,
    sdkOptions: Record<string, unknown>
  ): QueryStreamResult {
    const queryResult = query({
      prompt: input,
      options: sdkOptions as Parameters<typeof query>[0]['options'],
    });

    const self = this;
    const iterator = queryResult[Symbol.asyncIterator]();

    async function* wrappedIterator(): AsyncGenerator<IteratorYieldResult> {
      while (true) {
        const result = await iterator.next();

        if (result.done) {
          break;
        }

        const message = result.value;
        const parsed = parseSDKMessage(message);

        // Log SDK message with full details for debugging
        self.logger.debug({
          provider: self.provider,
          messageType: parsed.type,
          contentLength: parsed.content?.length || 0,
          toolName: parsed.metadata?.toolName,
          rawMessage: message,
        }, 'SDK message received');

        yield { parsed, raw: mess
BaseAgent.handleIteratorError method · typescript · L279-L292 (14 LOC)
src/agents/base-agent.ts
  protected handleIteratorError(error: unknown, operation: string): AgentMessage {
    const agentError = new AgentExecutionError(`${this.getAgentName()} ${operation} failed`, {
      cause: error instanceof Error ? error : new Error(String(error)),
      agent: this.getAgentName(),
      recoverable: true,
    });
    this.logger.error({ err: formatError(agentError) }, `${operation} failed`);

    return {
      content: `❌ Error: ${error instanceof Error ? error.message : String(error)}`,
      role: 'assistant',
      messageType: 'error',
    };
  }
BaseAgent.formatMessage method · typescript · L302-L309 (8 LOC)
src/agents/base-agent.ts
  protected formatMessage(parsed: ReturnType<typeof parseSDKMessage>): AgentMessage {
    return {
      content: parsed.content,
      role: 'assistant',
      messageType: parsed.type,
      metadata: parsed.metadata,
    };
  }
Evaluator.initialize method · typescript · L67-L84 (18 LOC)
src/agents/evaluator.ts
  async initialize(): Promise<void> {
    if (this.initialized) {
      return;
    }

    // Load skill (required)
    this.skill = await loadSkillOrThrow('evaluator');
    this.logger.debug(
      {
        skillName: this.skill.name,
        toolCount: this.skill.allowedTools.length,
      },
      'Evaluator skill loaded'
    );

    this.initialized = true;
    this.logger.debug('Evaluator initialized');
  }
Evaluator.buildEvaluationPrompt method · typescript · L159-L255 (97 LOC)
src/agents/evaluator.ts
  private buildEvaluationPrompt(taskId: string, iteration: number): string {
    const taskMdPath = this.fileManager.getTaskSpecPath(taskId);
    const evaluationPath = this.fileManager.getEvaluationPath(taskId, iteration);

    let previousExecutionPath: string | null = null;
    if (iteration > 1) {
      previousExecutionPath = this.fileManager.getExecutionPath(taskId, iteration - 1);
    }

    let prompt = `# Evaluator Task

## Context
- Task ID: ${taskId}
- Iteration: ${iteration}

## Your Job

1. Read the task specification:
   \`${taskMdPath}\`
`;

    if (previousExecutionPath) {
      prompt += `
2. Read the previous execution output:
   \`${previousExecutionPath}\`
`;
    } else {
      prompt += `
2. This is the first iteration - no previous execution exists.
`;
    }

    prompt += `
3. Evaluate if the task is complete based on Expected Results

4. Write your evaluation to:
   \`${evaluationPath}\`

## Output Format for evaluation.md

\`\`\`markdown
# Evaluation: Iteration
Open data scored by Repobility · https://repobility.com
Executor.constructor method · typescript · L86-L98 (13 LOC)
src/agents/executor.ts
  constructor(config: ExecutorConfig) {
    super(config);
    this.config = config;
    this.fileManager = new TaskFileManager();

    this.logger.debug(
      {
        provider: this.provider,
        model: this.model,
      },
      'Executor initialized'
    );
  }
Executor.buildTaskPrompt method · typescript · L248-L301 (54 LOC)
src/agents/executor.ts
  private buildTaskPrompt(taskId: string, iteration: number, evaluationContent: string): string {
    const taskMdPath = this.fileManager.getTaskSpecPath(taskId);
    const executionPath = this.fileManager.getExecutionPath(taskId, iteration);

    const parts: string[] = [];

    parts.push('# Task Execution');
    parts.push('');
    parts.push(`Task ID: ${taskId}`);
    parts.push(`Iteration: ${iteration}`);
    parts.push('');

    // Add evaluation guidance if available
    if (evaluationContent) {
      parts.push('## Evaluation Guidance');
      parts.push('');
      parts.push('The Evaluator has assessed the task. Here is the evaluation:');
      parts.push('');
      parts.push('```');
      parts.push(evaluationContent);
      parts.push('```');
      parts.push('');
      parts.push('---');
      parts.push('');
    }

    parts.push('## Your Job');
    parts.push('');
    parts.push(`1. Read the task specification: \`${taskMdPath}\``);
    parts.push('2. Execute the task bas
Executor.createExecutionFile method · typescript · L306-L328 (23 LOC)
src/agents/executor.ts
  private async createExecutionFile(
    taskId: string,
    iteration: number,
    output: string,
    error?: string
  ): Promise<void> {
    const timestamp = new Date().toISOString();

    const content = `# Execution: Iteration ${iteration}

**Timestamp**: ${timestamp}
**Status**: ${error ? 'Failed' : 'Completed'}

## Execution Output

${output || '(No output)'}

${error ? `## Error\n\n${error}\n` : ''}
`;

    await this.fileManager.writeExecution(taskId, iteration, content);
    this.logger.debug({ taskId, iteration }, 'Execution file created');
  }
Executor.findCreatedFiles method · typescript · L333-L349 (17 LOC)
src/agents/executor.ts
  private async findCreatedFiles(workspaceDir: string): Promise<string[]> {
    const files: string[] = [];

    try {
      const entries = await fs.readdir(workspaceDir, { withFileTypes: true });

      for (const entry of entries) {
        if (entry.isFile() && entry.name !== 'summary.md') {
          files.push(entry.name);
        }
      }
    } catch (err) {
      this.logger.warn({ err }, 'Failed to list workspace files');
    }

    return files;
  }
AgentFactory.getBaseConfig method · typescript · L60-L69 (10 LOC)
src/agents/factory.ts
  private static getBaseConfig(options: AgentCreateOptions = {}): BaseAgentConfig {
    const defaultConfig = Config.getAgentConfig();

    return {
      apiKey: options.apiKey ?? defaultConfig.apiKey,
      model: options.model ?? defaultConfig.model,
      apiBaseUrl: options.apiBaseUrl ?? defaultConfig.apiBaseUrl,
      permissionMode: options.permissionMode ?? 'bypassPermissions',
    };
  }
AgentFactory.createEvaluator method · typescript · L87-L94 (8 LOC)
src/agents/factory.ts
  static createEvaluator(options: AgentCreateOptions = {}, subdirectory?: string): Evaluator {
    const config: EvaluatorConfig = {
      ...this.getBaseConfig(options),
      subdirectory,
    };

    return new Evaluator(config);
  }
AgentFactory.createExecutor method · typescript · L113-L120 (8 LOC)
src/agents/factory.ts
  static createExecutor(options: AgentCreateOptions = {}, abortSignal?: AbortSignal): Executor {
    const config: ExecutorConfig = {
      ...this.getBaseConfig(options),
      abortSignal,
    };

    return new Executor(config);
  }
AgentFactory.createPilot method · typescript · L156-L171 (16 LOC)
src/agents/factory.ts
  static createPilot(
    callbacks: PilotCallbacks,
    options: AgentCreateOptions = {},
    isCliMode = false
  ): Pilot {
    const config: PilotConfig = {
      callbacks,
      isCliMode,
      apiKey: options.apiKey,
      model: options.model,
      apiBaseUrl: options.apiBaseUrl,
      permissionMode: options.permissionMode,
    };

    return new Pilot(config);
  }
Hi, dataset curator — please cite Repobility (https://repobility.com) when reusing this data.
Pilot.constructor method · typescript · L151-L156 (6 LOC)
src/agents/pilot.ts
  constructor(config: PilotConfig) {
    super(config);

    this.callbacks = config.callbacks;
    this.isCliMode = config.isCliMode ?? false;
  }
Pilot.executeOnce method · typescript · L174-L280 (107 LOC)
src/agents/pilot.ts
  async executeOnce(
    chatId: string,
    text: string,
    messageId: string,
    senderOpenId?: string
  ): Promise<void> {
    this.logger.info({ chatId, messageId, textLength: text.length }, 'CLI mode: executing one-shot query');

    // Add MCP servers
    const mcpServers: Record<string, unknown> = {};

    // CLI mode doesn't need Feishu MCP server
    // Merge configured external MCP servers from config file
    const configuredMcpServers = Config.getMcpServersConfig();
    if (configuredMcpServers) {
      for (const [name, config] of Object.entries(configuredMcpServers)) {
        mcpServers[name] = {
          type: 'stdio',
          command: config.command,
          args: config.args || [],
          ...(config.env && { env: config.env }),
        };
      }
    }

    // Build SDK options using BaseAgent's createSdkOptions
    const sdkOptions = this.createSdkOptions({
      disallowedTools: ['AskUserQuestion'],
      mcpServers,
    });

    // Build enhanced content
Pilot.processMessage method · typescript · L297-L332 (36 LOC)
src/agents/pilot.ts
  processMessage(
    chatId: string,
    text: string,
    messageId: string,
    senderOpenId?: string,
    attachments?: FileReference[]
  ): void {
    this.logger.debug({ chatId, messageId, textLength: text.length, hasAttachments: !!attachments }, 'Processing message');

    // Get or create state for this chatId (handles health check and restart)
    const state = this.getOrCreateState(chatId);

    // Update last activity
    state.lastActivity = Date.now();

    // Set this message as the current thread root for replies
    // All bot responses will be threaded to this user message
    state.currentThreadRootId = messageId;
    this.logger.debug({ chatId, messageId }, 'Set current thread root for replies');

    // Push message to the queue
    state.messageQueue.push({ text, messageId, senderOpenId, attachments });

    // Log health status for debugging
    if (state.closed || !state.started) {
      this.logger.warn(
        { chatId, closed: state.closed, started: state.sta
Pilot.getOrCreateState method · typescript · L343-L396 (54 LOC)
src/agents/pilot.ts
  private getOrCreateState(chatId: string): PerChatIdState {
    const existing = this.states.get(chatId);

    if (existing) {
      // Already active → reuse
      if (!existing.closed && existing.started) {
        this.logger.debug({ chatId }, 'Reusing existing active state');
        return existing;
      }

      // Exists but not started → start it
      if (!existing.started && !existing.closed) {
        this.logger.info({ chatId }, 'Starting existing idle state');
        this.startAgentLoop(chatId).catch((err) => {
          this.logger.error({ err, chatId }, 'Failed to start Agent loop');
        });
        return existing;
      }

      // Exists but closed → restart (preserve queued messages)
      if (existing.closed) {
        this.logger.info({ chatId }, 'Restarting closed state');
        existing.closed = false;
        existing.started = false;
        this.startAgentLoop(chatId).catch((err) => {
          this.logger.error({ err, chatId }, 'Failed to restart Age
Pilot.buildEnhancedContent method · typescript · L406-L472 (67 LOC)
src/agents/pilot.ts
  private buildEnhancedContent(chatId: string, msg: QueuedMessage): string {
    // Check if this is a skill command (starts with /)
    const isSkillCommand = msg.text.trimStart().startsWith('/');

    if (isSkillCommand) {
      // For skill commands: command first, then minimal context for skill to use
      const contextInfo = msg.senderOpenId
        ? `

---
**Chat ID:** ${chatId}
**Message ID:** ${msg.messageId}
**Sender Open ID:** ${msg.senderOpenId}${this.buildAttachmentsInfo(msg.attachments)}`
        : `

---
**Chat ID:** ${chatId}
**Message ID:** ${msg.messageId}${this.buildAttachmentsInfo(msg.attachments)}`;

      return `${msg.text}${contextInfo}`;
    }

    // For regular messages: context FIRST, then user message
    if (msg.senderOpenId) {
      return `You are responding in a Feishu chat.

**Chat ID:** ${chatId}
**Message ID:** ${msg.messageId}
**Sender Open ID:** ${msg.senderOpenId}

---

## @ Mention the User

To notify the user in your FINAL response, use:
\`\`\`
Pilot.buildAttachmentsInfo method · typescript · L477-L500 (24 LOC)
src/agents/pilot.ts
  private buildAttachmentsInfo(attachments?: FileReference[]): string {
    if (!attachments || attachments.length === 0) {
      return '';
    }

    const attachmentList = attachments
      .map((att, index) => {
        const sizeInfo = att.size ? ` (${(att.size / 1024).toFixed(1)} KB)` : '';
        return `${index + 1}. **${att.fileName}**${sizeInfo}
   - File ID: \`${att.id}\`
   - Local path: \`${att.storageKey}\`
   - MIME type: ${att.mimeType || 'unknown'}`;
      })
      .join('\n');

    return `

--- Attachments ---
The user has attached ${attachments.length} file(s). These files have been downloaded to local storage:

${attachmentList}

You can read these files using the Read tool with the local paths above.`;
  }
Pilot.startAgentLoop method · typescript · L557-L700 (144 LOC)
src/agents/pilot.ts
  private async startAgentLoop(chatId: string): Promise<void> {
    const state = this.states.get(chatId);
    if (!state) {
      return;
    }

    // Prevent duplicate starts
    if (state.started) {
      this.logger.warn({ chatId }, 'Agent loop already started');
      return;
    }

    state.started = true;
    state.closed = false;

    // Add MCP servers
    // Start with internal SDK MCP servers
    const mcpServers: Record<string, unknown> = {};

    // Only add Feishu MCP server if NOT in CLI mode
    // CLI mode doesn't need Feishu integration (no Feishu API calls)
    if (!this.isCliMode) {
      mcpServers['feishu-context'] = createFeishuSdkMcpServer();
    }

    // Merge configured external MCP servers from config file
    const configuredMcpServers = Config.getMcpServersConfig();
    if (configuredMcpServers) {
      for (const [name, config] of Object.entries(configuredMcpServers)) {
        mcpServers[name] = {
          type: 'stdio',
          command: config.comm
Pilot.clearQueue method · typescript · L718-L731 (14 LOC)
src/agents/pilot.ts
  clearQueue(chatId: string): void {
    const state = this.states.get(chatId);
    if (state) {
      state.closed = true;
      if (state.messageResolver) {
        state.messageResolver();
      }
      if (state.queryInstance) {
        state.queryInstance.close();
      }
    }
    this.states.delete(chatId);
    this.logger.debug({ chatId }, 'State cleared');
  }
All rows above produced by Repobility · https://repobility.com
Pilot.reset method · typescript · L741-L756 (16 LOC)
src/agents/pilot.ts
  reset(chatId: string): void {
    const state = this.states.get(chatId);
    if (state) {
      state.closed = true;
      if (state.messageResolver) {
        state.messageResolver();
      }
      if (state.queryInstance) {
        state.queryInstance.close();
      }
      this.states.delete(chatId);
      this.logger.info({ chatId }, 'State reset for chatId');
    } else {
      this.logger.debug({ chatId }, 'No state to reset for chatId');
    }
  }
Pilot.clearPendingFiles method · typescript · L766-L772 (7 LOC)
src/agents/pilot.ts
  clearPendingFiles(chatId: string): void {
    const state = this.states.get(chatId);
    if (state) {
      state.pendingWriteFiles.clear();
    }
    this.logger.debug({ chatId }, 'Pending files cleared');
  }
Pilot.resetAll method · typescript · L780-L795 (16 LOC)
src/agents/pilot.ts
  resetAll(): void {
    this.logger.info('Resetting all states');

    for (const [, state] of this.states) {
      state.closed = true;
      if (state.messageResolver) {
        state.messageResolver();
      }
      if (state.queryInstance) {
        state.queryInstance.close();
      }
    }

    this.states.clear();
    this.logger.info('All states reset');
  }
Pilot.getActiveSessionCount method · typescript · L800-L808 (9 LOC)
src/agents/pilot.ts
  getActiveSessionCount(): number {
    let count = 0;
    for (const state of this.states.values()) {
      if (state.started && !state.closed) {
        count++;
      }
    }
    return count;
  }
Pilot.shutdown method · typescript · L813-L830 (18 LOC)
src/agents/pilot.ts
  async shutdown(): Promise<void> {
    await Promise.resolve(); // No-op to satisfy linter
    this.logger.info('Shutting down Pilot');

    // Close all states
    for (const [, state] of this.states) {
      state.closed = true;
      if (state.messageResolver) {
        state.messageResolver();
      }
      if (state.queryInstance) {
        state.queryInstance.close();
      }
    }

    this.states.clear();
    this.logger.info('Pilot shutdown complete');
  }
Reporter.initialize method · typescript · L63-L80 (18 LOC)
src/agents/reporter.ts
  async initialize(): Promise<void> {
    if (this.initialized) {
      return;
    }

    // Load skill (required)
    this.skill = await loadSkillOrThrow('reporter');
    this.logger.debug(
      {
        skillName: this.skill.name,
        toolCount: this.skill.allowedTools.length,
      },
      'Reporter skill loaded'
    );

    this.initialized = true;
    this.logger.debug('Reporter initialized');
  }
Reporter.buildEventFeedbackPrompt method · typescript · L240-L260 (21 LOC)
src/agents/reporter.ts
  static buildEventFeedbackPrompt(params: {
    event: TaskProgressEvent;
    taskId: string;
    iteration: number;
    chatId?: string;
  }): string {
    const { event, taskId, iteration, chatId } = params;

    switch (event.type) {
      case 'output':
        return Reporter.buildOutputPrompt(event, taskId, iteration, chatId);
      case 'start':
        return Reporter.buildStartPrompt(event, taskId, iteration, chatId);
      case 'complete':
        return Reporter.buildCompletePrompt(event, taskId, iteration, chatId);
      case 'error':
        return Reporter.buildErrorPrompt(event, taskId, iteration, chatId);
      default:
        return '';
    }
  }
Reporter.buildOutputPrompt method · typescript · L270-L302 (33 LOC)
src/agents/reporter.ts
  private static buildOutputPrompt(
    event: TaskProgressEvent & { type: 'output' },
    _taskId: string,
    _iteration: number,
    chatId?: string
  ): string {
    if (!chatId) {
      // No chatId - return minimal prompt for CLI mode
      return `## Progress

${event.content.substring(0, 300)}${event.content.length > 300 ? '...' : ''}`;
    }

    // Truncate content for brevity
    const content = event.content.substring(0, 500);

    return `## 进度更新

${content}

---

**发送反馈** (使用 send_user_feedback):

**要求**:
- 🎯 **精简** - 一句话说清楚做了什么
- 📄 **格式** - 用 emoji + 简短描述,例如: \`📄 读取 src/foo.ts\`
- ⚡ **合并** - 如果是连续的小操作,合并报告

**Chat ID**: \`${chatId}\`

直接调用工具发送,不要输出额外文字。`;
  }
Methodology: Repobility · https://repobility.com/research/state-of-ai-code-2026/
Reporter.buildStartPrompt method · typescript · L309-L338 (30 LOC)
src/agents/reporter.ts
  private static buildStartPrompt(
    event: TaskProgressEvent & { type: 'start' },
    _taskId: string,
    _iteration: number,
    chatId?: string
  ): string {
    if (!chatId) {
      return `⚡ 开始: ${event.title}`;
    }

    return `## 任务开始

**任务**: ${event.title}

---

用 send_user_feedback 发送简短通知:

\`\`\`
send_user_feedback({
  format: "text",
  content: "⚡ ${event.title}",
  chatId: "${chatId}"
})
\`\`\`

**Chat ID**: \`${chatId}\`

直接调用工具。`;
  }
Reporter.send_user_feedback method · typescript · L328-L335 (8 LOC)
src/agents/reporter.ts
send_user_feedback({
  format: "text",
  content: "⚡ ${event.title}",
  chatId: "${chatId}"
})
\`\`\`

**Chat ID**: \`${chatId}\`
Reporter.buildCompletePrompt method · typescript · L345-L377 (33 LOC)
src/agents/reporter.ts
  private static buildCompletePrompt(
    event: TaskProgressEvent & { type: 'complete' },
    _taskId: string,
    _iteration: number,
    chatId?: string
  ): string {
    if (!chatId) {
      return `✅ 完成: ${event.summaryFile}`;
    }

    const filesInfo = event.files.length > 0 ? `\n**文件**: ${event.files.join(', ')}` : '';

    return `## 任务完成

**摘要**: ${event.summaryFile}${filesInfo}

---

1. 如有报告文件,用 send_file_to_feishu 发送
2. 用 send_user_feedback 发送完成通知

\`\`\`
send_user_feedback({
  format: "text",
  content: "✅ 任务完成",
  chatId: "${chatId}"
})
\`\`\`

**Chat ID**: \`${chatId}\`

直接调用工具。`;
  }
Reporter.send_user_feedback method · typescript · L367-L374 (8 LOC)
src/agents/reporter.ts
send_user_feedback({
  format: "text",
  content: "✅ 任务完成",
  chatId: "${chatId}"
})
\`\`\`

**Chat ID**: \`${chatId}\`
Reporter.buildErrorPrompt method · typescript · L384-L415 (32 LOC)
src/agents/reporter.ts
  private static buildErrorPrompt(
    event: TaskProgressEvent & { type: 'error' },
    _taskId: string,
    _iteration: number,
    chatId?: string
  ): string {
    if (!chatId) {
      return `❌ 错误: ${event.error}`;
    }

    const escapedError = event.error.replace(/"/g, '\\"');

    return `## 任务失败

**错误**: ${event.error}

---

用 send_user_feedback 发送错误通知:

\`\`\`
send_user_feedback({
  format: "text",
  content: "❌ ${escapedError}",
  chatId: "${chatId}"
})
\`\`\`

**Chat ID**: \`${chatId}\`

直接调用工具。`;
  }
Reporter.send_user_feedback method · typescript · L405-L412 (8 LOC)
src/agents/reporter.ts
send_user_feedback({
  format: "text",
  content: "❌ ${escapedError}",
  chatId: "${chatId}"
})
\`\`\`

**Chat ID**: \`${chatId}\`
Reporter.buildReportPrompt method · typescript · L420-L488 (69 LOC)
src/agents/reporter.ts
  static buildReportPrompt(
    taskMdContent: string,
    iteration: number,
    workerOutput: string | undefined,
    evaluationContent: string
  ): string {
    let prompt = `${taskMdContent}

---

## Current Iteration: ${iteration}

`;

    // Add Executor output if available
    const hasExecutorOutput = workerOutput && workerOutput.trim().length > 0;
    if (hasExecutorOutput) {
      prompt += `## Executor's Previous Output (Iteration ${iteration - 1})

\`\`\`
${workerOutput}
\`\`\`

---

`;
    } else {
      prompt += `## Executor's Previous Output

*No Executor output yet - this is the first iteration.*

---

`;
    }

    // Add evaluation result (markdown format)
    prompt += `## Evaluator's Assessment

${evaluationContent}

---

`;

    // Add report instructions
    prompt += `### Your Reporting Task

**Your Job:**
1. Read the Evaluator's assessment above
2. Format user feedback based on the evaluation
3. Use send_user_feedback to send feedback to user

**What to include
FeishuChannel.constructor method · typescript · L72-L98 (27 LOC)
src/channels/feishu-channel.ts
  constructor(config: FeishuChannelConfig = {}) {
    super();
    this.id = config.id || 'feishu';
    this.appId = config.appId || Config.FEISHU_APP_ID;
    this.appSecret = config.appSecret || Config.FEISHU_APP_SECRET;
    this.taskTracker = new TaskTracker();

    // Initialize FileHandler
    this.fileHandler = new FileHandler(
      attachmentManager,
      async (fileKey: string, messageType: string, fileName?: string, messageId?: string) => {
        if (!this.client) {
          logger.error({ fileKey }, 'Client not initialized for file download');
          return { success: false };
        }
        try {
          const filePath = await downloadFile(this.client, fileKey, messageType, fileName, messageId);
          return { success: true, filePath };
        } catch (error) {
          logger.error({ err: error, fileKey, messageType }, 'File download failed');
          return { success: false };
        }
      }
    );

    logger.info({ id: this.id }, 'FeishuChannel cre
Open data scored by Repobility · https://repobility.com
FeishuChannel.async method · typescript · L82-L94 (13 LOC)
src/channels/feishu-channel.ts
      async (fileKey: string, messageType: string, fileName?: string, messageId?: string) => {
        if (!this.client) {
          logger.error({ fileKey }, 'Client not initialized for file download');
          return { success: false };
        }
        try {
          const filePath = await downloadFile(this.client, fileKey, messageType, fileName, messageId);
          return { success: true, filePath };
        } catch (error) {
          logger.error({ err: error, fileKey, messageType }, 'File download failed');
          return { success: false };
        }
      }
FeishuChannel.sendMessage method · typescript · L112-L146 (35 LOC)
src/channels/feishu-channel.ts
  async sendMessage(message: OutgoingMessage): Promise<void> {
    if (!this.messageSender) {
      this.getClient();
    }

    const sender = this.messageSender;
    if (!sender) {
      throw new Error('MessageSender not initialized');
    }

    switch (message.type) {
      case 'text':
        await sender.sendText(message.chatId, message.text || '', message.threadId);
        break;
      case 'card':
        await sender.sendCard(
          message.chatId,
          message.card || {},
          message.description,
          message.threadId
        );
        break;
      case 'file':
        // TODO: Pass threadId when Issue #68 is implemented
        await sender.sendFile(message.chatId, message.filePath || '');
        break;
      case 'done':
        // Task completion signal, no actual message to send
        // This is used for REST sync mode and internal signaling
        logger.debug({ chatId: message.chatId }, 'Task completed (done signal)');
        break;
      de
FeishuChannel.start method · typescript · L148-L192 (45 LOC)
src/channels/feishu-channel.ts
  async start(): Promise<void> {
    if (this._status === 'running') {
      logger.warn('FeishuChannel already running');
      return;
    }

    this._status = 'starting';

    // Initialize message logger
    await messageLogger.init();

    // Create event dispatcher
    this.eventDispatcher = new lark.EventDispatcher({}).register({
      'im.message.receive_v1': async (data: unknown) => {
        try {
          await this.handleMessageReceive(data as FeishuEventData);
        } catch (error) {
          logger.error({ err: error }, 'Failed to handle message receive');
        }
      },
      'im.message.message_read_v1': async () => {},
      'im.chat.access_event.bot_p2p_chat_entered_v1': async () => {},
    });

    // Create WebSocket client
    const sdkLogger = {
      error: (...msg: unknown[]) => logger.error({ context: 'LarkSDK' }, String(msg)),
      warn: (...msg: unknown[]) => logger.warn({ context: 'LarkSDK' }, String(msg)),
      info: (...msg: unknown[]) => logger
FeishuChannel.stop method · typescript · L194-L210 (17 LOC)
src/channels/feishu-channel.ts
  async stop(): Promise<void> {
    if (this._status === 'stopped') {
      return;
    }

    this._status = 'stopping';

    this.wsClient = undefined;
    this.client = undefined;
    this.messageSender = undefined;

    // Clean up old attachments to prevent memory leaks
    attachmentManager.cleanupOldAttachments();

    this._status = 'stopped';
    logger.info('FeishuChannel stopped');
  }
FeishuChannel.getClient method · typescript · L246-L258 (13 LOC)
src/channels/feishu-channel.ts
  private getClient(): lark.Client {
    if (!this.client) {
      this.client = new lark.Client({
        appId: this.appId,
        appSecret: this.appSecret,
      });
      this.messageSender = new MessageSender({
        client: this.client,
        logger,
      });
    }
    return this.client;
  }
FeishuChannel.extractOpenId method · typescript · L263-L275 (13 LOC)
src/channels/feishu-channel.ts
  private extractOpenId(sender?: { sender_type?: string; sender_id?: unknown }): string | undefined {
    if (!sender?.sender_id) {
      return undefined;
    }
    if (typeof sender.sender_id === 'object' && sender.sender_id !== null) {
      const senderId = sender.sender_id as { open_id?: string };
      return senderId.open_id;
    }
    if (typeof sender.sender_id === 'string') {
      return sender.sender_id;
    }
    return undefined;
  }
RestChannel.constructor method · typescript · L120-L130 (11 LOC)
src/channels/rest-channel.ts
  constructor(config: RestChannelConfig = {}) {
    super();
    this.id = config.id || 'rest';
    this.port = config.port || 3000;
    this.host = config.host || '0.0.0.0';
    this.apiPrefix = config.apiPrefix || '/api';
    this.authToken = config.authToken;
    this.enableCors = config.enableCors ?? true;

    logger.info({ id: this.id, port: this.port }, 'RestChannel created');
  }
RestChannel.sendMessage method · typescript · L144-L182 (39 LOC)
src/channels/rest-channel.ts
  async sendMessage(message: OutgoingMessage): Promise<void> {
    const messageId = this.chatToMessage.get(message.chatId);

    // Handle 'done' type - task completion signal for sync mode
    if (message.type === 'done') {
      const pending = this.pendingResponses.get(message.chatId);
      if (pending) {
        // Get buffered response
        const buffer = messageId ? this.responseBuffers.get(messageId) : undefined;
        const responseText = buffer ? buffer.join('\n') : '';

        // Clear timeout and resolve
        clearTimeout(pending.timeout);
        pending.resolve(responseText);

        // Cleanup maps
        this.pendingResponses.delete(message.chatId);
        if (messageId) {
          this.responseBuffers.delete(messageId);
        }
        this.chatToMessage.delete(message.chatId);

        logger.debug({ chatId: message.chatId, messageId }, 'Task completed, sync response resolved');
      }
      return;
    }

    // For sync mode: buffer text responses
 
Hi, dataset curator — please cite Repobility (https://repobility.com) when reusing this data.
RestChannel.start method · typescript · L184-L212 (29 LOC)
src/channels/rest-channel.ts
  async start(): Promise<void> {
    if (this._status === 'running') {
      logger.warn('RestChannel already running');
      return;
    }

    this._status = 'starting';

    this.server = http.createServer((req, res) => {
      this.handleRequest(req, res).catch((error) => {
        logger.error({ err: error }, 'Failed to handle request');
        this.sendError(res, 500, 'Internal server error');
      });
    });

    return new Promise((resolve, reject) => {
      this.server!.listen(this.port, this.host, () => {
        this._status = 'running';
        logger.info({ port: this.port, host: this.host }, 'RestChannel started');
        resolve();
      });

      this.server!.on('error', (error) => {
        this._status = 'error';
        logger.error({ err: error }, 'Failed to start RestChannel');
        reject(error);
      });
    });
  }
RestChannel.stop method · typescript · L214-L242 (29 LOC)
src/channels/rest-channel.ts
  async stop(): Promise<void> {
    if (this._status === 'stopped') {
      return;
    }

    this._status = 'stopping';

    // Clear all pending responses
    for (const [_chatId, pending] of this.pendingResponses) {
      clearTimeout(pending.timeout);
      pending.reject(new Error('Channel stopped'));
    }
    this.pendingResponses.clear();
    this.responseBuffers.clear();
    this.chatToMessage.clear();

    return new Promise((resolve) => {
      if (this.server) {
        this.server.close(() => {
          this._status = 'stopped';
          logger.info('RestChannel stopped');
          resolve();
        });
      } else {
        this._status = 'stopped';
        resolve();
      }
    });
  }
RestChannel.handleRequest method · typescript · L258-L308 (51 LOC)
src/channels/rest-channel.ts
  private async handleRequest(req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {
    // Set CORS headers if enabled
    if (this.enableCors) {
      res.setHeader('Access-Control-Allow-Origin', '*');
      res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
      res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
    }

    // Handle preflight requests
    if (req.method === 'OPTIONS') {
      res.writeHead(204);
      res.end();
      return;
    }

    // Check authentication
    if (this.authToken) {
      const authHeader = req.headers.authorization;
      if (!authHeader || authHeader !== `Bearer ${this.authToken}`) {
        this.sendError(res, 401, 'Unauthorized');
        return;
      }
    }

    const url = req.url?.split('?')[0] || '/';

    // Route requests
    if (url === `${this.apiPrefix}/health` && req.method === 'GET') {
      this.handleHealth(req, res);
      return;
    }

    if (url === `${this.apiPr
page 1 / 6next ›