makecindy / makecindy/cindy

bug(codex): ask_user_question 答案已落库但未进入模型可见结果

Open
#1,496 2 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
TypeScript
Stars
2.7k
Forks
401
Avg merge
21h 48m
Merged PRs (30d)
776

Description

## 问题描述 / What happened

在 Codex 会话中,`cindy.ask_user_question` 的单选答案已经由 UI 成功提交,且本地消息库中同时存在正确的 `ask_user` answered 记录和非空 `tool_result`,但模型侧看到的执行输出仍为空,随后误判用户没有选择并再次追问。

这与 #1323 相关,但本次证据表明是另一条失败路径:答案没有在 renderer → main 投递阶段丢失。#1336 处理的是 `resolveInteraction` 失败后 UI 过早进入 answered 状态;本次 `resolveInteraction` 已成功,答案也已落库,因此 #1336 不能覆盖。

### 实际行为

用户点击唯一一道单选题的选项后:

- 卡片显示该选项已勾选;
- `ask_user` 记录为 `status: "answered"`,answers 包含所选项;
- 配对的 `tool_result` 非空,内容为 `{"audit_times":{"answers":["09:15 / 21:15(推荐)"]}}`;
- 但模型可见的执行输出为空,下一条 assistant 消息仍称“唯一缺少的是每天两次的具体执行时间”。

### 期望行为

动态工具的成功结果必须进入同一轮模型上下文。只要 UI 与持久化层已确认 answered,模型就应收到包含所选答案的非空结果;如果最后一跳注入失败,应显式报错,不能以成功状态静默返回空输出。

## 环境 / Environment

- Cindy 版本:0.1.27(`com.xd.cindycn`)
- 平台:macOS 26.5.2,Apple Silicon
- Agent:Codex
- 时间:2026-08-03,Asia/Singapore

## 复现步骤 / Steps to reproduce

1. 在 Codex 会话调用 `cindy.ask_user_question`,只传一道单选题。
2. 点击任一选项。单题单选按当前实现会立即调用 `onAnswer`,无需另点 Submit。
3. 观察卡片进入 answered,并检查本地 `messages`:`ask_user.answers` 与配对 `tool_result` 均非空。
4. 观察工具执行返回给模型的输出以及下一条 assistant 消息。
5. 实际可出现:执行输出为空,模型认为用户未回答并重复追问。

## 定位证据 / Investigation

已确认的本地代码链路本应产生非空结果:

- 单题单选点击会直接 `advance(label)` 并调用 `onAnswer`:`apps/desktop/src/renderer/components/new-chat/AskUserQuestionPrompt.tsx`
- renderer 通过 `resolveInteraction(requestId, { kind: 'ask_user_question', answers })` 提交:`apps/desktop/src/renderer/lib/makerChatStore.ts`
- 问题文本答案会转换为按 question id 编码的数组:`packages/maker-core/src/agents/codex/index.ts` 的 `responseFromAskUserAnswers`
- dynamic tool 明确返回 `success: true` 和非空 `contentItems[].text`:同文件的 `dynamicToolResponseFromUserInput`
- app-server host/client 只负责把 handler 结果写回 JSON-RPC:`packages/maker-core/src/agents/codex/app-server/host.ts`、`client.ts`

现有 `packages/maker-core/src/agents/codex/index.test.ts` 只验证 mock handler 返回的 shape,尚未覆盖真实 app-server 收到 dynamic tool response 后,答案是否进入下一次模型上下文。

另外,当前调用面将该工具结果类型暴露为 `Promise`。在 code-mode 执行中,若调用方按普通 MCP `CallToolResult.content[]` 读取,结构化结果可能被静默忽略。需要确认真实失败发生在 app-server 的 dynamic-result 注入,还是 code-mode 对 nested dynamic tool 返回 shape 的适配层;无论哪种,都不应让成功答案变成空的模型可见输出。

## 建议修复 / Acceptance criteria

1. 增加真实 daemon/app-server 级回归:捕获 `item/tool/call` 的入站请求、出站 JSON-RPC response,并断言下一次模型上下文包含所选答案。
2. 为 code-mode 暴露明确的 `ask_user_question` 返回 schema,或统一成标准 `CallToolResult`;禁止只声明 `Promise` 后静默丢弃结构化结果。
3. 成功结果为空时 fail closed:返回明确错误并允许重试,不得让 Agent 归因于用户未回答。
4. 覆盖单选、多选/自定义答案、重复同轮请求与取消路径。
5. 回归测试必须能区分:renderer 投递失败、JSON-RPC 回写失败、模型上下文注入失败。

Contributor guide

Open the contributing guide

Research direction

Start with AskUserQuestionPrompt.tsx, makerChatStore.ts, and the Codex functions responseFromAskUserAnswers and dynamicToolResponseFromUserInput. Read the existing packages/maker-core/src/agents/codex/index.test.ts, then trace host.ts and client.ts while designing an app-server regression around item/tool/call. Done means the selected answer reaches the next model context and failures are distinguishable across renderer, JSON-RPC, and injection paths.

Written by the indexing model from the issue text.

Assessment

Tech stack
electron, typescript
Domain
api, frontend, testing
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.