Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Results

Every tool returns a JSON object. Tools do not throw for expected errors, because browsers give the agent no message for a thrown error.

Shapes

ResultWhen
{success: true, ...}A command result that has a boolean success, such as the results of jupyterlab-ai-commands. It is returned as is
{success: true, result}Any other command result. Widgets, also nested ones, are described as in get_context. A value that JSON cannot hold becomes a string
{success: false, error, isError: true}An unknown, blocked or failed command, an invalid input, an error in the code. When a command gives no error, it is made from errorName: errorValue, or else from message
{status: "pending", pendingId, tool, message, dialog?}A dialog opened during the call, or the call runs longer than executionTimeout. It has no success field

These fields can be added to a result:

Pending calls

Browser bridges wait for a tool call to return, and some run one call at a time. So a call never blocks on a dialog or on a long run:

Dialogs

Jupyter shows one dialog at a time, and the other dialogs wait in a queue.

Bounding

A result must fit within maxResultLength (40000) characters of JSON.

  1. Always, in MIME bundles (objects whose keys are all MIME types): binary data (images other than SVG, audio, video, application/pdf, application/octet-stream) is replaced by a placeholder such as [omitted image/png data, 52100 characters; see text/plain]. Other rich data longer than 2000 characters is replaced too when the bundle has text/plain. Values under a metadata key are kept. ANSI escape codes are removed from outputs.

  2. When the result is still too long, these steps run in order until it fits:

    1. the strings in outputs and traceback keep a quarter of maxResultLength at each end (10000 characters by default);

    2. the strings in outputs and traceback keep 1000 characters at each end;

    3. all strings keep 500 characters at each end, and arrays keep 50 items;

    4. all strings keep 150 characters at each end, and arrays keep 10 items.

    Tracebacks keep their last items, where the error is. A string is cut only when it is longer than twice the kept length plus 40 characters, and its middle becomes [... N characters omitted ...]. Each step starts from the original result, so a value is cut only once.

  3. A shortened result keeps its fields and gets truncated: true, a truncationNote for the agent, and truncation: the first 20 cuts, as {path, omittedCharacters} or {path, omittedItems}. truncationCount gives the number of cuts when there are more.

  4. When the last step is not enough, the result keeps only success, isError, status, pendingId and error (the ones it has), with truncated: true, a message that gives the original length, and preview: the beginning of its JSON.

Example

These calls and results come from JupyterLab, through the example client. Values are shortened with ….

run_code {"kernelId": "6843…", "code": "import time\nfor i in range(120):\n    time.sleep(1)"}
{"status": "pending", "pendingId": "call-4", "tool": "run_code",
 "message": "The call is still running after 20 seconds and continues in the background. Call wait_for_result with this pendingId to get its result. To stop running code, interrupt the kernel."}

interrupt_kernel {"kernelId": "6843…"}
{"success": true, "kernelId": "6843…", "message": "The kernel was interrupted."}

wait_for_result {"pendingId": "call-4"}
{"success": false, "status": "error", "message": "Kernel execution failed", "errorName": "KeyboardInterrupt",
 "errorValue": "", "traceback": […], "error": "KeyboardInterrupt", "isError": true, …}

execute_command {"command_id": "notebook:restart-kernel", "args": {"semanticWidget": "id-5220…", "activate": false}}
{"status": "pending", "pendingId": "call-6", "tool": "execute_command",
 "message": "A dialog opened while the call ran, and the call probably waits for an answer. Click a button with answer_dialog (or ask the user if it is not from this call); the call then continues.",
 "dialog": {"id": "dialog-1", "title": "Restart Kernel?",
            "body": "Do you want to restart the kernel of demo.ipynb? All variables will be lost.",
            "buttons": ["Cancel", "Restart"], "shown": true}}

answer_dialog {"button": "Restart"}
{"success": true, "result": true, "answered": {"dialogId": "dialog-1", "button": "Restart"}}

run_cell {"notebook": "demo.ipynb", "cellId": "x"}
{"success": false, "error": "Unknown argument \"notebook\". Accepted arguments: \"notebookPath\", \"cellId\".", "isError": true}

run_code {"kernelId": "6843…", "code": "print('x' * 100000)"}
{"success": true, "status": "ok",
 "outputs": [{"output_type": "stream", "name": "stdout", "text": "xxx…\n[... 80001 characters omitted ...]\nxxx…"}], …,
 "truncated": true, "truncationNote": "Some values were shortened to fit the result size limit (see \"truncation\"). …",
 "truncation": [{"path": "outputs[0].text", "omittedCharacters": 80001}]}