Skip to main content

InquiryCraft

PyPI Python License: MIT

InquiryCraft, shortened to iqcraft in project discussions and documentation, is a lightweight, extensible harness for building tool-using LLM agents. It provides the execution loop and lifecycle primitives needed by standalone agents, interactive CLIs, and larger host applications without imposing a domain workflow. The published package, Python import, and CLI command all remain inquirycraft; iqcraft is a short name, not a second compatibility entry point.

Release status

The current release is 0.9.3, published on PyPI. InquiryCraft is currently beta software and supports Python 3.11–3.13. Release tags use vMAJOR.MINOR.PATCH; the main branch may contain changes intended for the next release.

Version 0.9.3 keeps the two most recent historical chat summaries visible and folds older rounds into History, including after session restore.

Version 0.9.1 adds transactional chat compaction, incremental operation projections, and restored tool-state/history fixes. It reads legacy operation logs; new logs require this release or newer.

For a reproducible installation, pin the published version:

pip install 'inquirycraft==0.9.3'

Why InquiryCraft?

  • Small core — the minimal installation depends only on Click.
  • Real agent loop — provider request, tool call, tool result, and continuation are handled by a reusable AgentRuntime.
  • Provider-neutral contracts — applications can implement LLMClient or use the optional OpenAI-compatible adapter.
  • Composable tools — typed schemas, execution middleware, path policy, concurrent dispatch, output reduction, and raw artifact retention share one contract.
  • Durable runs — append-only sessions, lifecycle events, cancellation, resume, and telemetry are built in.
  • Deterministic validation — record/replay detects drift in messages, model options, tool schemas, call ordering, results, and stream mode.
  • Optional integrations — provider SDKs, token counting, and MCP stay outside the minimal package.

Install

Install the minimal runtime and CLI:

pip install inquirycraft
inquirycraft tools list

Install an OpenAI-compatible provider client:

pip install 'inquirycraft[openai]'

Other optional extras:

pip install 'inquirycraft[tokens]'
pip install 'inquirycraft[mcp]'
pip install 'inquirycraft[tui]'

Importing inquirycraft resolves public symbols lazily. It does not initialize the CLI or load provider SDKs.

Quick start

Create the private configuration file, then edit ~/.config/inquirycraft/models.json:

inquirycraft config init
inquirycraft config path
{
  "version": 1,
  "models": {
    "model-a": {
      "model": "model-a",
      "reasoning_replay": "preserve",
      "pricing": {
        "input_usd_per_1m": 0.15,
        "cached_input_usd_per_1m": 0.003,
        "output_usd_per_1m": 0.60
      },
      "endpoints": {
        "code": [{"url": "https://a.example/v1", "key": "key-a"}],
        "feedback": [{"url": "https://b.example/v1", "key": "key-b"}]
      }
    }
  },
  "defaults": {
    "code_models": ["model-a"],
    "feedback_models": ["model-a"],
    "selection": "auto"
  }
}

Each clean model alias may contain shared endpoints or role-specific endpoint lists. Optional pricing rates are USD per one million tokens; all three fields must be provided together. The registry is the only pricing source used by the chat monitor. Optional reasoning_replay controls how stored assistant reasoning is projected into later provider requests: preserve (the default) keeps real reasoning, required fills missing historical reasoning with a stable compatibility placeholder, and omit leaves reasoning out of the request. All modes keep canonical memory and JSONL records unchanged. CLI, REPL, and TUI use the same retry, cooldown, and failover pool. A call selects one model first; failover stays within that model's endpoints. Transient endpoint failures get three attempts with delay before the pool advances. Use --models alias-a,alias-b to select a subset, or --model-config /path/models.json to use another registry. Embedding hosts can switch the active chat provider pool at runtime without clearing the conversation. Hosts remain responsible for their own role-specific research configuration.

Run a single task with the built-in file and shell tools:

mkdir -p workspace
inquirycraft run \
  --workspace ./workspace \
  "Inspect the workspace and write a concise summary to result.txt"

Start an interactive session (saved automatically under the workspace):

inquirycraft repl --workspace ./workspace

Start the full-screen InquiryCraft TUI:

pip install 'inquirycraft[openai,tui]'
inquirycraft tui --workspace ./workspace

The TUI uses InquiryCraft's own IQ · InquiryCraft terminal wordmark and projects the same runtime event stream used by embedded hosts. It includes a scrollable transcript, streaming assistant and reasoning output, tool status cards, pending-message state, token/model/workspace footer data, and a multiline editor.

Submit with Enter (or Ctrl+S). Use Alt+Enter for a new line; Shift+Enter also works in terminals that distinguish it from Enter. While a run is busy, an ordinary submission is queued as steering instead of starting a concurrent run. Use /follow-up TEXT, /next TEXT, /cancel, /status, /clear, or /help; Ctrl+R toggles reasoning, Ctrl+O toggles tool details, Esc cancels the current interaction, and pressing Ctrl+C twice within two seconds exits (Ctrl+Q exits directly). When a host task list is visible, focus it with Tab, use Up/Down to select a task or the more-tasks row, and press Enter to open it. With selected editor or transcript text, Ctrl+C copies instead; terminal-managed Ctrl+Shift+C/V can be used for system clipboard copy/paste. Exiting closes ordinary chat resources; lifecycle of detached background tasks remains owned by the embedding host. The existing repl command remains available for plain terminals and scripts.

Embedding hosts may use the optional begin_transcript_section and fold_transcript_section methods to collapse a preparation exchange into a clickable summary. Original entries and their nested tool display state remain available; folding does not change the conversation or its JSONL records.

Completed chat exchanges collapse to two numbered rows (> user, - assistant) when the next chat begins. Click or press Enter to restore the original widgets, including their tool and thinking state. Four recent history summaries stay visible; older rounds share an expandable History entry. The current exchange stays open. The footer keeps context/cache/cost values, with only Cache labeled. Resume reconstructs numbering from stored user messages; hosts may optionally classify their preparation prompts with is_host_task_prompt.

Chat messages omit user/assistant headings and decorative side borders; user text has a display-only > prefix and a transparent background. host notices keep their labels. Tool groups are labeled TOOLS. Hosts may enable fold_system_notices: initial notices remain visible until an ordinary chat completes, then collect in History with a separate notice count. Task launch notices fold independently; errors stay visible. This changes only the UI. Copy appears only at the top right of Markdown code blocks, including shell commands, and copies only that block. Ordinary replies have no copy button. Real newlines and indentation remain intact; visual wrapping and padding are not copied. Native terminal selection still copies the screen, so prefer these controls for commands and scripts.

Applications embedding the InquiryCraft Click group can call create_cli(include_tui=False) when their dependency policy intentionally excludes Textual. The standalone inquirycraft command keeps TUI registration enabled by default.

Python API

import asyncio
from pathlib import Path

from inquirycraft.adapters import AskLLMAdapter
from inquirycraft.llm import LLMFactoryConfig, ModelRegistry, build_llm
from inquirycraft.runtime import AgentRuntime, RuntimeOptions
from inquirycraft.tools import default_tools


async def main() -> None:
    registry = ModelRegistry.from_json(Path.home() / ".config/inquirycraft/models.json")
    aliases = registry.default_aliases("code_models")
    client = AskLLMAdapter(build_llm(LLMFactoryConfig(
        model_registry=registry,
        model_aliases=aliases,
    )))
    runtime = AgentRuntime(
        llm=client,
        options=RuntimeOptions(
            model=registry.models[aliases[0]].model,
            workspace=Path("./workspace"),
            system_prompt="You are a careful coding agent.",
        ),
        tools=default_tools(),
    )
    try:
        print(await runtime.run("Inspect the workspace and summarize its contents."))
    finally:
        await client.aclose()


asyncio.run(main())

Implementing the LLMClient protocol is enough to connect another provider. Custom tools subclass BaseTool and return a ToolResult; hooks and event sinks can be added without modifying the loop.

Streaming, recovery, and runtime control

The compatibility run() -> str API and the event-stream API use the same agent loop. Enable provider streaming separately, and opt into the operation journal when a run must be recoverable across process restarts:

options = RuntimeOptions(
    model="your-model",
    workspace=Path("./workspace"),
    stream_llm=True,
    tool_execution="parallel",
    operation_log=Path(".inquirycraft/operations.jsonl"),
)
runtime = AgentRuntime(llm=client, options=options, tools=default_tools())

async for event in runtime.run_stream("Inspect the project"):
    print(event.type, event.payload)

Tool bundles are fully parsed and schema-validated before execution. Tools default to replay_policy = "never"; declare "safe" only when repeating an unsettled call is known to be safe. Runtime controls can be queued without starting a concurrent run:

await runtime.steer("Focus on the failing integration test")
await runtime.follow_up("Summarize the evidence")
await runtime.next_run("Use concise output on the next explicit run")

Hosted applications that execute their own domain policy loops can import process lifecycle helpers from inquirycraft.runtime and shell-output reducers from inquirycraft.tools. These package namespaces are the stable extension surface; hosts do not need to access the underlying runtime.subprocess or reducer modules. See the maintained Host Extension API for signatures, lifecycle ownership, platform constraints, safe cleanup patterns, and compatibility policy.

CompactionPolicy and injected context transformers change only the provider-visible projection. The append-only source conversation remains intact. When an operation log is configured, each conversation append also creates a durable projection; terminal records atomically carry the inactive state and final projection. On resume, settled LLM/tool deltas missing from the conversation store are reconstructed without repeating a completed provider request or a replay_policy="never" tool.

Context estimates use the highest observed provider token density as a conservative calibration, including tool-schema overhead. Default chat keeps a compacted provider prefix and appends new messages without reinserting archived history. Compacted requests persist a versioned, auditable checkpoint containing the source/provider views, summary boundary, retained tail, policy, token estimates, and optional provider usage.

Skills and prompt templates

ResourceLoader discovers declarative SKILL.md and *.prompt.md resources. Project roots require an explicit ResourceTrustStore decision by default. Discovery reads Markdown data only and never imports project Python. Skill catalogs expose only name and description; full instructions are read on demand.

Sessions, events, and replay

Each run, repl, or tui invocation writes a workspace-local session once there is activity, with conversation, operation, event, and usage logs in <workspace>/.inquirycraft/sessions/<ID>/. Resume an existing chat by choosing from the session list:

inquirycraft tui --workspace ./workspace --resume
inquirycraft repl --workspace ./workspace --resume
inquirycraft run --workspace ./workspace --resume "Next task"

The list always requires confirmation. For scripts or non-interactive terminals, add --session ID to --resume; a unique ID prefix also works. View IDs and update times with inquirycraft sessions list --workspace ./workspace. Resume restores the entire saved conversation but does not automatically rerun an interrupted step. Legacy standalone conversation logs remain readable through the Python API; they are not automatically discovered in the workspace session list. Import one without changing its original file using inquirycraft sessions import PATH --workspace ./workspace; optional --operation-log and --event-log paths import matching recovery logs.

RecordingLLMClient stores the exact provider-visible request and completion result or stream chunks. ReplayLLMClient consumes that JSONL offline and raises ReplayMismatchError when model parameters, messages, tool schemas, call ordering, or stream mode drift. Generated timestamps and event IDs can be normalized without loosening prompts, tool payloads, results, or ordering.

Tools and path policy

InquiryCraft includes independent read, write, edit, grep, glob, list, and shell tools. The ordinary chat shell tool has no implicit timeout; an explicit timeout still applies. It streams output, retains complete stdout/stderr under .logs/shell_outputs/, and records execution status and artifact references in tool events and operation settlements. The model-visible result is bounded; timeout and cancellation retain partial output and clean up the process group. Failed commands are not automatically rerun. Host-managed research tools keep their own execution and budget policies.

Filesystem scope is configured separately from tool behavior:

from inquirycraft.tools import PathPolicy, ReadTool

policy = PathPolicy("./workspace", enabled=True)
read = ReadTool(path_policy=policy)

A strict PathPolicy contains model-generated paths under a configured root and any explicitly allowed extra roots. Hosts that execute untrusted model output should also apply process isolation and least-privilege credentials appropriate to their threat model.

Tool-output bounds and raw artifacts

The Runtime applies one final output policy after tool hooks and before events, operation settlement, and Conversation persistence. Results up to 12,000 characters are unchanged. Larger results become an exact head/tail view; the complete rendered result is saved under .logs/tool_outputs/ with a SHA-256 fingerprint and an append-only index. Shell observation commands use the existing loss-preserving shell summary first.

from inquirycraft.tools import ToolOutputPolicy

runtime = AgentRuntime(
    ...,
    tool_output_policy=ToolOutputPolicy(max_model_chars=16_000),
)

# Explicit compatibility escape hatch; large output will remain model-visible.
unbounded = ToolOutputPolicy(enabled=False)

If raw artifact persistence fails, InquiryCraft returns the original unabridged result instead of silently discarding data. Error results remain errors after reduction.

CLI reference

inquirycraft run             Run one tool-using agent task
inquirycraft repl            Start an interactive multi-turn session
inquirycraft tui             Start the full-screen streaming terminal interface
inquirycraft tools list      List built-in tools
inquirycraft tools schema    Print tool schemas
inquirycraft events show     Inspect runtime event JSONL

Use inquirycraft COMMAND --help for command-specific options.

Package layout

Package Responsibility
inquirycraft.runtime Agent loop, hooks, cancellation, telemetry, and resume
inquirycraft.llm Provider protocols, streaming models, retry, and adapters
inquirycraft.tools Tool contracts, execution, file tools, and path policy
inquirycraft.memory Messages, conversations, and persistent records
inquirycraft.events Lifecycle JSONL and deterministic record/replay
inquirycraft.runtime.ProcessExecutionSession Typed process execution and cleanup
inquirycraft.mcp Optional MCP transport integration
inquirycraft.resources Trusted Skills and prompt-template discovery
inquirycraft.cli Standalone commands and CLI composition contracts
inquirycraft.tui Optional full-screen terminal host and InquiryCraft branding

See ARCHITECTURE.md for design boundaries and validation layers.

Development

uv sync --all-extras
uv run pytest -q
uv run ruff check .
uv run ruff format --check .
uv build

InquiryCraft supports Python 3.11 and newer and is distributed under the MIT License.

TUI assistant responses render Markdown by default (headings, lists, tables and syntax-highlighted fenced code). Use inquirycraft tui --no-markdown for literal text, or set INQUIRYCRAFT_TUI_MARKDOWN=false. --markdown explicitly enables it. Rendering applies to streamed and restored assistant messages; stored content remains the original Markdown. User input and tool output remain literal text.

Default CLI runtimes (run, REPL and TUI) enable deterministic context compaction with a default 256K window (262,144 tokens), an 8,192-token output reserve, a 2,048-token safety margin and an 80% input trigger. Original conversation records remain intact. This is not model context-window discovery or an LLM-generated semantic summary. Direct AgentRuntime embedding keeps its existing opt-in policy so host-owned context transformers are not compacted a second time.

In TUI, type / for command completion (Tab or Enter accepts, arrows select, Escape dismisses). /context configures 32K/64K/128K/256K/1M or a custom exact window (or enter /context 256K directly); output reserve, safety margin and trigger ratio are editable. /usage shows session totals. The model footer displays the last completed request's actual input tokens, token-weighted cache hit rate and cumulative estimated session cost. Before usage is available, context displays — rather than a stored-history estimate. Switching models resets the current input display, not accumulated cost; resume restores saved totals. Costs use registry pricing at call time; known historical costs are not repriced or cleared. The default capacity is 256K, and unknown prices remain unset. No model capabilities or billing rates are inferred. These profiles affect interactive chat factories, not embedding applications' independent context policies.

/compact immediately compacts idle chat context with the existing deterministic policy, without a model call or API cost. Original conversation, reasoning and tool records stay intact; the saved projection survives resume. Short context is left unchanged. It does not compact host-owned research contexts or merely hide UI history. The footer retains the last actual input usage until another request completes.

Metadata

Release files for inquirycraft 0.9.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for inquirycraft 0.9.3
File Size Uploaded Provenance
inquirycraft-0.9.3.tar.gz 204.4 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for inquirycraft 0.9.3
File Interpreter ABI Platform
inquirycraft-0.9.3-py3-none-any.whl Python 3 none any Details

Total release size: 465.3 kB

Release files / inquirycraft-0.9.3.tar.gz

Download URL inquirycraft-0.9.3.tar.gz
Size 204.4 kB
Tags Source
SHA-256 checksum
How to use checksums
c4bdf34d5a2cfe81ee8d30e8b3ceb866b9366cb39a57cd0e3aeee17a5b603b30
BLAKE2b-256 checksum
How to use checksums
046e8997261071401fdfca417eccb34270bbdf63ae7b3abd2330c582f4ad3e1d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.

Transparency log

Release files / inquirycraft-0.9.3-py3-none-any.whl

Download URL inquirycraft-0.9.3-py3-none-any.whl
Size 261.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c696f64e3681d0eb523596a8ebac6e545ce3b8e88416c76a206513acdbc9d5d2
BLAKE2b-256 checksum
How to use checksums
6961fce7bfa1269c7ab51ab63d42f8b71f1d5c1130612c7f25075b0ba2c6cde3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.

Transparency log

Security for inquirycraft 0.9.3

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for inquirycraft-0.9.3.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page