Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions src/__tests__/ask-user-question.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@

it('renders question text with header', () => {
const card = buildAskUserQuestionCard('q1', singleQuestion);
const elements = card.elements as any[];

Check warning on line 42 in src/__tests__/ask-user-question.test.ts

View workflow job for this annotation

GitHub Actions / test

Unexpected any. Specify a different type
// First element should be a div with the question text
expect(elements[0].tag).toBe('div');
expect(elements[0].text.content).toContain('Approach');
Expand All @@ -48,14 +48,14 @@

it('renders option buttons with correct action values', () => {
const card = buildAskUserQuestionCard('q1', singleQuestion);
const elements = card.elements as any[];

Check warning on line 51 in src/__tests__/ask-user-question.test.ts

View workflow job for this annotation

GitHub Actions / test

Unexpected any. Specify a different type
// Second element should be the action group with buttons
const actionGroup = elements[1];
expect(actionGroup.tag).toBe('action');
expect(actionGroup.actions).toHaveLength(2);

// First option should be primary
expect(actionGroup.actions[0].type).toBe('primary');
// All options should be default (no misleading "primary" highlight)
expect(actionGroup.actions[0].type).toBe('default');
expect(actionGroup.actions[0].text.content).toBe('Option A');
expect(actionGroup.actions[0].value).toEqual({
action: 'ask_user_answer',
Expand All @@ -65,14 +65,13 @@
optionLabel: 'Option A',
});

// Second option should be default
expect(actionGroup.actions[1].type).toBe('default');
expect(actionGroup.actions[1].text.content).toBe('Option B');
});

it('renders option descriptions when provided', () => {
const card = buildAskUserQuestionCard('q1', singleQuestion);
const elements = card.elements as any[];

Check warning on line 74 in src/__tests__/ask-user-question.test.ts

View workflow job for this annotation

GitHub Actions / test

Unexpected any. Specify a different type
// Third element should be the descriptions div
const descDiv = elements[2];
expect(descDiv.tag).toBe('div');
Expand All @@ -82,7 +81,7 @@

it('renders note about custom answer at bottom', () => {
const card = buildAskUserQuestionCard('q1', singleQuestion);
const elements = card.elements as any[];

Check warning on line 84 in src/__tests__/ask-user-question.test.ts

View workflow job for this annotation

GitHub Actions / test

Unexpected any. Specify a different type
const lastElement = elements[elements.length - 1];
expect(lastElement.tag).toBe('note');
expect(lastElement.elements[0].content).toContain('自定义答案');
Expand All @@ -100,9 +99,9 @@
},
];
const card = buildAskUserQuestionCard('q2', multiQuestion);
const elements = card.elements as any[];

Check warning on line 102 in src/__tests__/ask-user-question.test.ts

View workflow job for this annotation

GitHub Actions / test

Unexpected any. Specify a different type
// Should have: div1, action1, hr, div2, action2, note
const hrElements = elements.filter((e: any) => e.tag === 'hr');

Check warning on line 104 in src/__tests__/ask-user-question.test.ts

View workflow job for this annotation

GitHub Actions / test

Unexpected any. Specify a different type
expect(hrElements.length).toBe(1);
});

Expand All @@ -114,7 +113,7 @@
},
];
const card = buildAskUserQuestionCard('q3', noHeader);
const elements = card.elements as any[];

Check warning on line 116 in src/__tests__/ask-user-question.test.ts

View workflow job for this annotation

GitHub Actions / test

Unexpected any. Specify a different type
expect(elements[0].text.content).toContain('Simple question?');
// Should not contain " — " separator used for header
expect(elements[0].text.content).not.toContain(' — ');
Expand All @@ -128,7 +127,7 @@
},
];
const card = buildAskUserQuestionCard('q4', noDesc);
const elements = card.elements as any[];

Check warning on line 130 in src/__tests__/ask-user-question.test.ts

View workflow job for this annotation

GitHub Actions / test

Unexpected any. Specify a different type
// Should have: div(question), action(buttons), note — no descriptions div
expect(elements.length).toBe(3);
});
Expand All @@ -155,7 +154,7 @@
{ question: 'Color?', header: 'Theme', options: [{ label: 'Blue' }, { label: 'Red' }] },
];
const card = buildAskUserAnsweredCard(questions, { 'Color?': 'Blue' });
const elements = card.elements as any[];

Check warning on line 157 in src/__tests__/ask-user-question.test.ts

View workflow job for this annotation

GitHub Actions / test

Unexpected any. Specify a different type
expect(elements[0].text.content).toContain('Theme');
expect(elements[0].text.content).toContain('Color?');
expect(elements[0].text.content).toContain('Blue');
Expand All @@ -166,7 +165,7 @@
{ question: 'Missing?', options: [{ label: 'A' }] },
];
const card = buildAskUserAnsweredCard(questions, {});
const elements = card.elements as any[];

Check warning on line 168 in src/__tests__/ask-user-question.test.ts

View workflow job for this annotation

GitHub Actions / test

Unexpected any. Specify a different type
expect(elements[0].text.content).toContain('—');
});
});
21 changes: 14 additions & 7 deletions src/feishu/event-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,13 @@ interface PendingQuestion {
/** questionId → PendingQuestion */
const pendingQuestions = new Map<string, PendingQuestion>();

/** AskUserQuestion 等待超时(5 分钟) */
const ASK_USER_TIMEOUT_MS = 5 * 60 * 1000;
/** AskUserQuestion 等待超时(毫秒)。0 表示永不超时(默认)。 */
const ASK_USER_TIMEOUT_MS = (() => {
const raw = process.env.ASK_USER_TIMEOUT_MS;
if (raw === undefined || raw === '') return 0;
const n = Number(raw);
return Number.isFinite(n) && n > 0 ? n : 0;
})();

/**
* 创建 AskUserQuestion 回调(供 executeClaudeTask / executeDirectTask 共用)
Expand Down Expand Up @@ -161,15 +166,17 @@ function createAskUserHandler(chatId: string, getThreadReplyMsgId: () => string
chatId,
};

pending.timeoutTimer = setTimeout(() => {
pendingQuestions.delete(questionId);
reject(new Error('AskUserQuestion timed out'));
}, ASK_USER_TIMEOUT_MS);
if (ASK_USER_TIMEOUT_MS > 0) {
pending.timeoutTimer = setTimeout(() => {
pendingQuestions.delete(questionId);
reject(new Error('AskUserQuestion timed out'));
}, ASK_USER_TIMEOUT_MS);
}

pendingQuestions.set(questionId, pending);

// 发送卡片(在话题内回复或直接发到群)
// 如果发送失败,立即 reject 而非等待 5 分钟超时
// 如果发送失败,立即 reject 而非等待用户回答(避免任务永久挂起)
const trySendCard = async () => {
try {
const threadMsgId = getThreadReplyMsgId();
Expand Down
2 changes: 1 addition & 1 deletion src/feishu/message-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1282,7 +1282,7 @@ export function buildAskUserQuestionCard(
const buttons = q.options.map((opt, oi) => ({
tag: 'button',
text: { tag: 'plain_text', content: opt.label },
type: oi === 0 ? 'primary' : 'default',
type: 'default',
value: {
action: 'ask_user_answer',
questionId,
Expand Down
Loading