Skip to content

fix(ask_user): accept labeled-object options, header alias, JSON-string questions - #543

Merged
m-aebrer merged 3 commits into
masterfrom
feature/issue-542-ask-user-input-shapes
Oct 8, 2026
Merged

m-aebrer merged 3 commits into
masterfrom
feature/issue-542-ask-user-input-shapes

Conversation

@m-aebrer

@m-aebrer m-aebrer commented Oct 8, 2026

Copy link
Copy Markdown
Collaborator

Closes #542

Accept the ask_user argument shapes models commonly send (labeled-object options, header alias, JSON-string questions) by normalizing them inside the tool. UI/RPC/extension surfaces unchanged.

Implementation plan posted as a comment below.

@m-aebrer

m-aebrer commented Oct 8, 2026 •

Copy link
Copy Markdown
Collaborator Author

Implementation Plan (revised: ask_user only)

Scope: only the ask_user tool. No changes to the agent loop, shared tool types, TUI, Dashboard, RPC, or the ctx.ui.ask extension API.

Files touched

  • packages/coding-agent/src/core/tools/ask-user.ts
  • packages/coding-agent/test/tools/ask-user.test.ts
  • The ask_user paragraph in packages/coding-agent/README.md, only if it describes the input shape

Changes in ask-user.ts

  1. Schema (questionSchema)
    • Each option becomes "a nonblank string, or { label: nonblank string, description?: string }". It keeps the same llama.cpp-safe pattern and the same 2–4 limit.
    • header is added as an optional string.
    • The title description changes from "Short bold header…" to "Short bold title…".
  2. Schema (askUserSchema.questions): becomes "the existing array, or a string".
  3. In execute, before the existing normalizeQuestion() step:
    • If questions is a string, JSON.parse it and check the result against the same array schema using TypeBox Value.Check.
    • If parsing or the check fails, throw a clear error that includes the schema errors. The agent loop already turns an error thrown in execute into an error result for the model.
  4. In normalizeQuestion()
    • Object options become "label", or "label — description" when the description is nonblank.
    • The title is title ?? header.
    • Downstream code sees the same string[] / title as today.
  5. callLabel (the call display while it streams): falls back to header, and doesn't crash when questions is still a string.

Acceptance criteria

  • Object options work, and reach the UI as "label" / "label — description".
  • Plain-string options behave exactly as today.
  • header works as the title; title wins when both are sent.
  • JSON-string questions works. Invalid JSON, or JSON that isn't a valid question list, fails with a clear error.
  • Genuinely invalid calls still fail validation.
  • The only source file changed is ask-user.ts.
  • npm run build, npm run check and npm test pass.

Provider risk

Schemas using "this or that" (anyOf) already ship to every provider: the thinking field in subagent.ts and status in tasks.ts use Type.Union. What's new is mixing a string and an object inside one union. Verify this against llama.cpp, which broke in PR 422. If it doesn't work, stop and check with the maintainer rather than working around it.

Tests (ask-user.test.ts)

  • Schema: accepts object options and header; still rejects a blank label, a single option, more than 4 options, and an empty batch.
  • execute with a mocked ctx.ui.ask:
    • Object options reach the UI as "label" / "label — description".
    • A blank description gives just the label.
    • Mixed string and object options work.
    • header becomes the title; title wins when both are sent.
    • A JSON-string questions works.
    • Invalid JSON, or JSON that isn't a valid question list, throws.
  • Existing tests: keep passing unchanged.
  • renderCall: works with header and with string questions.

Plan created by mach6

@github-actions

github-actions Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

Vitest coverage

Metric Covered Total Coverage
Statements 42982 58719 73.19%
Branches 23488 37232 63.08%
Functions 9281 12433 74.64%
Lines 30946 42141 73.43%

View full coverage run

@m-aebrer

m-aebrer commented Oct 8, 2026

Copy link
Copy Markdown
Collaborator Author

Progress Update

Implemented the plan. Changes are limited to ask_user:

  • ask-user.ts
    • Each option can be a nonblank string or {label, description?}. Objects become "label" or "label — description" before reaching any UI.
    • header is accepted as the title; title wins when both are sent.
    • questions may be a JSON-encoded string. It is parsed, then checked against the same array schema. Invalid JSON or an invalid list throws a clear error, and the UI is never opened.
    • The title description no longer says "header". The call display handles header and string questions.
  • ask-user.test.ts: 7 new tests covering each shape and the invalid cases. The existing schema test is updated for the new shape.
  • packages/coding-agent/README.md: one sentence noting the accepted shapes.

llama.cpp check (the PR 422 risk):

  • The generated schema compiles with llama.cpp's C++ json_schema_to_grammar and grammar parser, as well as the Python converter.
  • As a sanity check, the old PR 422 \S pattern still crashes the same harness, so the check does catch that class of problem.

Verified:

  • npm run check, npm run build and npm test pass.
  • Run through dreb's real AJV validation (with coerceTypes), object options and string questions pass through unchanged, and an empty label is still rejected.

Note: the first commit attempt failed the pre-commit hook on an unrelated dashboard browser test (session-sidebar.browser.test.ts, "discards an in-flight drag on pointercancel": sidebar did not mount within the 5s timeout). It passed 7/7 times on its own, and the retried commit passed the full suite. It looks like a timing flake under full-suite load. It is not fixed in this PR.

Commit: d3ecb24


Progress tracked by mach6

@m-aebrer
m-aebrer marked this pull request as ready for review October 8, 2026 15:32
@m-aebrer

m-aebrer commented Oct 8, 2026

Copy link
Copy Markdown
Collaborator Author

Unverified Review Candidates — Pending Assessment

Review round: 1
Reviewed commit: d3ecb24

These are unverified candidates. Severity reflects reviewer confidence; do not treat any item as a merge blocker until the assessment comment is posted.

Critical

None.

Important

None.

Suggestions

  1. Redundant minLength: 1 next to the nonblank pattern (ask-user.ts ~lines 52–54, simplifier, confidence 96). NONBLANK_PATTERN already rejects empty strings, so minLength: 1 changes nothing about which values pass.
  2. Weak assertion on the error text for invalid JSON-string questions (ask-user.test.ts, test-reviewer, confidence 82). The test only matches the generic /not a valid questions array/ preamble. If the per-field detail built from Value.Errors broke, no test would catch it.

Strengths

  • Every invalid-input path fails loudly: bad JSON, valid JSON that isn't a valid list, a blank label, and wrong option counts (error-auditor).
  • All acceptance criteria and plan items are met with no scope creep, and the only source file changed is ask-user.ts (completeness-checker).
  • The new shapes are normalized to the existing AskRequest shape, so downstream UIs are unchanged (code-reviewer).
  • llama.cpp grammar compatibility was checked against the PR 422 failure class.

Agents run: code-reviewer, error-auditor, test-reviewer, completeness-checker, simplifier


Reviewed by mach6

@m-aebrer

m-aebrer commented Oct 8, 2026

Copy link
Copy Markdown
Collaborator Author

Review Assessment

#543 (comment)

Classifications

Finding Classification Reasoning
finding 1: redundant minLength: 1 nitpick Factual: True. The nonblank pattern already rejects empty strings. Scope: Not introduced by this PR. The same pair existed before, and the new label field just copies it. Practical: No harm. It may even help providers or grammar converters that honor minLength but drop pattern.
finding 2: weak assertion on the invalid-JSON error text nitpick Factual: True. The tests only match the generic preamble. Scope: The requirement is that invalid input "fails clearly", and that is tested: five invalid cases throw, and the UI mock is never called. Practical: Low value. Asserting TypeBox's own error text would make the test brittle when TypeBox is upgraded.

Action Plan

No merge blockers.


Assessment by mach6

@m-aebrer
m-aebrer merged commit 7278c55 into master Oct 8, 2026
5 of 6 checks passed
@m-aebrer
m-aebrer deleted the feature/issue-542-ask-user-input-shapes branch October 8, 2026 16:08
tobiasploetz pushed a commit to tobiasploetz/dreb that referenced this pull request Oct 12, 2026
…ng questions (aebrer#543)

* chore: open PR for issue 542

* fix(ask_user): accept labeled-object options, header alias, JSON-string questions

* chore: bump version to 2.70.1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Align ask_user options to labeled objects, coerce known variants

1 participant