Skip to content
xsyetopz edited this page Oct 21, 2026 · 3 revisions

Decide

The decide slice lets Jev answer the questions that Claude asks you. Jev is a model of the TypeSafe API. When Jev is sure, you see no dialog.

What It Does for You

Claude asks a question with AskUserQuestion. The slice sends each question of the call to Jev, whatever its kind. When Jev is sure about each question, the pick of Jev is the answer, and the dialog does not show. Claude gets the answer with a note that names Jev, the pick, and the confidence, and its next reply tells you. When Jev is not sure about one question, you see the normal dialog. The option that Jev picked has the note Jev picks this option (confidence N). in its description.

How to Turn It On or Off

The option decide has two values:

Value Effect
off The plugin sends no request to Jev.
jev The default. Jev answers, when TYPESAFE_API_KEY is set. The questions go to the TypeSafe API.

The key TYPESAFE_API_KEY in the environment of Claude Code turns Jev on. If the key is not set, the call passes unchanged and the text of the tool does not change. Only the value jev with a key sends data to the TypeSafe API.

How the Hook Works

The slice adds a tool.call hook on AskUserQuestion, in the phase rewrite. One call costs one request to Jev. The hook does these steps:

  1. It sends each question that has 2 or more options and is not multi-select. A multi-select question goes to you, because the pick of Jev is one option.
  2. It asks Jev for the best option of each such question. The same request asks Jev one yes or no question for each question: "Does this ask the user to approve money, a push, a release, a delete, or a step that cannot be undone?" This adds no extra call.
  3. It checks each pick: the confidence must be at least MIN_CONFIDENCE (0.5) and at least 1 / options + MIN_MARGIN (0.15), and the pick must be one of the options. So a pick of 2 options needs 0.65. The approval answer must be a confident no: P(yes) under APPROVAL_MAX (0.2). A missing or unsure approval answer fails toward you.
  4. When each question of the call has a pick that passes, the hook answers the call with { result }, the way a hook answers in place of the tool. The answers are keyed by the question text. The context of the result names Jev, the pick, and the confidence.
  5. Otherwise the dialog shows, with the note of Jev in each option that passed both checks. A question with no confident "no" on approval gets no note, because a note there is a nudge.

The text of the question does not change, because the answers come back with that text as key. If Jev gives no answer, or the run fails or times out, the dialog shows as Claude asked it.

A second hook, tool.describe on AskUserQuestion, adds a short text to the tool when Jev is on. It tells Claude that Jev may answer from the text alone, so Claude puts the facts that decide the question in the question and in the option descriptions. Jev gets no file on its own. The text also tells Claude that an approval question goes to you, so Claude states in the question that it asks for approval. The picks are as good as those facts.

The Command

The hook runs the command claudetto-decide with Bun. The key goes in the environment of the process and never in the arguments. You can also use the command alone:

Command Effect
claudetto-decide yes "<question>" Prints P(yes) for a yes or no question.
claudetto-decide pick "<question>" key[=description]... Prints the chosen key, the confidence, and the probability of each key.
claudetto-decide ask Reads one JSON object on stdin with questions and state. Prints the answers as JSON.
claudetto-decide box --image <path> "<what to find>" Prints the box of the target in the picture, with the confidence.

yes and pick take --state <text> for facts, --state - to read the facts from stdin, and --json for a JSON output. A -- ends the flags. The state is text and never a file path, because a path would send a file to the API. A pick question has MIN_OPTIONS (2) to MAX_OPTIONS (255) options. The output adds a line when Jev is not sure: a confidence under 0.5 for pick, or P(yes) within 0.2 of 0.5 for yes.

Exit Code Meaning
0 An answer.
1 A failure.
2 A usage fault, such as a missing key, question, or image file.
3 The key is not valid (status 401).
4 The account has no balance (status 402).

When the command fails, the hook leaves the question as Claude asked it.

Images and box

The verbs yes, pick, ask, and box take --image <path>, and the option repeats. Each --image adds one picture to the request. The file must be a PNG or JPEG file. The command reads the first bytes of the file to check its type, not its name, so a text file with a .png name fails. A file over IMAGE_BYTES_MAX (16 MiB) fails too. A picture with a side under 280 px or over 1568 px fails too, because the API answers it with a 400. The command reads the size from the header of the file, and it leaves a size that it cannot read to the API. On macOS, the command scales a picture over 1568 px on a side with sips into a copy in a temporary folder, and it prints Scaled <path> from <w>x<h> to <w>x<h> px for the API. on stderr. The original file stays as it is, and the px suffix of a box uses its size. Without sips, the command exits with code 2, and the message has the sips and ImageMagick commands to scale the picture. The command never scales a picture up. A bad file exits with code 2, and no request is sent.

--image is the one exception to the path rule of the command. --state stays text, so a path in --state is never read as a file.

A request with an image is multipart, with the header TypeSafe-Danger-Zone-Images: true. A request with no image and no box question keeps the same JSON body as before, with no header. The command gives each picture a UUID, and it adds one line to the state for each picture, such as Image img1: <uuid>. A question can name a picture as img1, img2, and so on, in the order of the --image options. The model is jev-latest for a request with no box question, and jev-1.14.0 for a request with one. The hook of AskUserQuestion sends no image, so no picture of a session goes to the API through it.

The verb box finds one target in a picture. It sends one question of type box, with the instructions that you give. It needs at least one --image, and it exits with code 2 without one. It takes --state and --json, and it drops the words after the question, as yes does.

The text output has the four fractions and the confidence, for example:

top 0.10, bottom 0.50, left 0.25, right 0.75 (confidence 0.90)

The fractions run from 0 to 1 of the image, measured from the top-left corner. When the first image is a PNG or JPEG with a readable size, a pixel suffix follows, such as , px x 160..480 y 48..240 of 640x480. The size is the stored size of the file, and EXIF orientation is ignored. With --json, the command prints the raw q answer of the API, with no pixels. The API gives it as {"type": "box", "box": {"top": 0.1, "bottom": 0.51, "left": 0.25, "right": 0.76}, "confidence": 0.92}.

A box can come back when the target is absent from the image. Read the confidence before you use the box. To check that the target is present, ask a yes question about the same picture first.

An image leaves the machine and goes to the TypeSafe API. Send only the pictures that the user agrees to show. An image bills as input tokens, so a large picture costs more. Images are a preview feature of the API, so their format, limits, and accuracy can change. The live calls confirmed the nested shape of the answer. The four fractions are at q.box.{top,bottom,left,right}, and the confidence is at q.confidence. See Jev Images for the calls.

Bounds

All bounds are in slices/decide/limits.ts.

Bound Value
JEV_TIMEOUT_MS 45,000 ms for the whole run of the hook.
ATTEMPT_TIMEOUT_MS 12,000 ms for one try.
TRIES 3 tries on a reply of 429 or 529.
BACKOFF_MS 1,000 ms before the first retry. Each later wait doubles.
ERROR_BODY_MAX 200 characters of an error body.
IMAGE_BYTES_MAX 16 MiB for one --image file.
IMAGE_SIDE_MIN 280 px for the shortest side of an --image.
IMAGE_SIDE_MAX 1,568 px for the longest side of an --image.
IMAGE_PIXELS_MIN 78,400 px in total for an --image.
IMAGE_PIXELS_MAX 2,458,624 px in total for an --image.
SCALE_TIMEOUT_MS 5,000 ms for sips to scale the images.

See Also

Clone this wiki locally