inquirycraft 0.6.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. inquirycraft-0.6.0/LICENSE +9 -0
  2. inquirycraft-0.6.0/PKG-INFO +222 -0
  3. inquirycraft-0.6.0/README.md +189 -0
  4. inquirycraft-0.6.0/pyproject.toml +70 -0
  5. inquirycraft-0.6.0/setup.cfg +4 -0
  6. inquirycraft-0.6.0/src/inquirycraft/__init__.py +76 -0
  7. inquirycraft-0.6.0/src/inquirycraft/__main__.py +4 -0
  8. inquirycraft-0.6.0/src/inquirycraft/adapters.py +127 -0
  9. inquirycraft-0.6.0/src/inquirycraft/agent/__init__.py +5 -0
  10. inquirycraft-0.6.0/src/inquirycraft/agent/base.py +194 -0
  11. inquirycraft-0.6.0/src/inquirycraft/cli/__init__.py +13 -0
  12. inquirycraft-0.6.0/src/inquirycraft/cli/app.py +231 -0
  13. inquirycraft-0.6.0/src/inquirycraft/cli/contracts.py +46 -0
  14. inquirycraft-0.6.0/src/inquirycraft/cli/factory.py +33 -0
  15. inquirycraft-0.6.0/src/inquirycraft/events/__init__.py +25 -0
  16. inquirycraft-0.6.0/src/inquirycraft/events/models.py +70 -0
  17. inquirycraft-0.6.0/src/inquirycraft/events/replay.py +307 -0
  18. inquirycraft-0.6.0/src/inquirycraft/events/sink.py +231 -0
  19. inquirycraft-0.6.0/src/inquirycraft/executor/__init__.py +15 -0
  20. inquirycraft-0.6.0/src/inquirycraft/executor/base.py +88 -0
  21. inquirycraft-0.6.0/src/inquirycraft/executor/dep_utils.py +40 -0
  22. inquirycraft-0.6.0/src/inquirycraft/executor/output.py +167 -0
  23. inquirycraft-0.6.0/src/inquirycraft/executor/python.py +129 -0
  24. inquirycraft-0.6.0/src/inquirycraft/executor/shell.py +65 -0
  25. inquirycraft-0.6.0/src/inquirycraft/llm/__init__.py +42 -0
  26. inquirycraft-0.6.0/src/inquirycraft/llm/base.py +288 -0
  27. inquirycraft-0.6.0/src/inquirycraft/llm/errors.py +16 -0
  28. inquirycraft-0.6.0/src/inquirycraft/llm/factory.py +95 -0
  29. inquirycraft-0.6.0/src/inquirycraft/llm/logprob_stream.py +160 -0
  30. inquirycraft-0.6.0/src/inquirycraft/llm/message_format.py +81 -0
  31. inquirycraft-0.6.0/src/inquirycraft/llm/models.py +42 -0
  32. inquirycraft-0.6.0/src/inquirycraft/llm/online.py +71 -0
  33. inquirycraft-0.6.0/src/inquirycraft/llm/online_support.py +99 -0
  34. inquirycraft-0.6.0/src/inquirycraft/llm/openai_adapter.py +109 -0
  35. inquirycraft-0.6.0/src/inquirycraft/llm/pool.py +142 -0
  36. inquirycraft-0.6.0/src/inquirycraft/llm/pooled.py +344 -0
  37. inquirycraft-0.6.0/src/inquirycraft/llm/protocol.py +15 -0
  38. inquirycraft-0.6.0/src/inquirycraft/llm/public_requests.py +123 -0
  39. inquirycraft-0.6.0/src/inquirycraft/llm/request_client.py +230 -0
  40. inquirycraft-0.6.0/src/inquirycraft/llm/retry.py +50 -0
  41. inquirycraft-0.6.0/src/inquirycraft/llm/stream_types.py +123 -0
  42. inquirycraft-0.6.0/src/inquirycraft/llm/text_stream.py +154 -0
  43. inquirycraft-0.6.0/src/inquirycraft/llm/tool_args.py +134 -0
  44. inquirycraft-0.6.0/src/inquirycraft/llm/tool_requests.py +194 -0
  45. inquirycraft-0.6.0/src/inquirycraft/llm/tool_stream_result.py +90 -0
  46. inquirycraft-0.6.0/src/inquirycraft/llm/tool_stream_state.py +187 -0
  47. inquirycraft-0.6.0/src/inquirycraft/llm/usage.py +55 -0
  48. inquirycraft-0.6.0/src/inquirycraft/llm/utils.py +288 -0
  49. inquirycraft-0.6.0/src/inquirycraft/mcp/__init__.py +8 -0
  50. inquirycraft-0.6.0/src/inquirycraft/mcp/client.py +118 -0
  51. inquirycraft-0.6.0/src/inquirycraft/memory/__init__.py +83 -0
  52. inquirycraft-0.6.0/src/inquirycraft/memory/context.py +63 -0
  53. inquirycraft-0.6.0/src/inquirycraft/memory/conversation.py +83 -0
  54. inquirycraft-0.6.0/src/inquirycraft/memory/history.py +184 -0
  55. inquirycraft-0.6.0/src/inquirycraft/memory/kv_storage.py +69 -0
  56. inquirycraft-0.6.0/src/inquirycraft/memory/legacy_records.py +27 -0
  57. inquirycraft-0.6.0/src/inquirycraft/memory/message.py +260 -0
  58. inquirycraft-0.6.0/src/inquirycraft/memory/mutation.py +67 -0
  59. inquirycraft-0.6.0/src/inquirycraft/memory/paths.py +19 -0
  60. inquirycraft-0.6.0/src/inquirycraft/memory/record_store.py +218 -0
  61. inquirycraft-0.6.0/src/inquirycraft/memory/records.py +64 -0
  62. inquirycraft-0.6.0/src/inquirycraft/runtime/__init__.py +42 -0
  63. inquirycraft-0.6.0/src/inquirycraft/runtime/cancellation.py +24 -0
  64. inquirycraft-0.6.0/src/inquirycraft/runtime/engine.py +313 -0
  65. inquirycraft-0.6.0/src/inquirycraft/runtime/hooks.py +97 -0
  66. inquirycraft-0.6.0/src/inquirycraft/runtime/hosted.py +114 -0
  67. inquirycraft-0.6.0/src/inquirycraft/runtime/resume.py +172 -0
  68. inquirycraft-0.6.0/src/inquirycraft/runtime/state.py +15 -0
  69. inquirycraft-0.6.0/src/inquirycraft/runtime/streaming.py +95 -0
  70. inquirycraft-0.6.0/src/inquirycraft/runtime/subprocess.py +298 -0
  71. inquirycraft-0.6.0/src/inquirycraft/runtime/subprocess_sync.py +122 -0
  72. inquirycraft-0.6.0/src/inquirycraft/runtime/telemetry.py +87 -0
  73. inquirycraft-0.6.0/src/inquirycraft/tools/__init__.py +136 -0
  74. inquirycraft-0.6.0/src/inquirycraft/tools/artifacts/__init__.py +11 -0
  75. inquirycraft-0.6.0/src/inquirycraft/tools/artifacts/reducers.py +41 -0
  76. inquirycraft-0.6.0/src/inquirycraft/tools/artifacts/store.py +146 -0
  77. inquirycraft-0.6.0/src/inquirycraft/tools/base.py +188 -0
  78. inquirycraft-0.6.0/src/inquirycraft/tools/builtin.py +101 -0
  79. inquirycraft-0.6.0/src/inquirycraft/tools/collection.py +100 -0
  80. inquirycraft-0.6.0/src/inquirycraft/tools/dispatch.py +75 -0
  81. inquirycraft-0.6.0/src/inquirycraft/tools/execution/__init__.py +7 -0
  82. inquirycraft-0.6.0/src/inquirycraft/tools/execution/engine.py +105 -0
  83. inquirycraft-0.6.0/src/inquirycraft/tools/execution/middleware.py +30 -0
  84. inquirycraft-0.6.0/src/inquirycraft/tools/execution/models.py +19 -0
  85. inquirycraft-0.6.0/src/inquirycraft/tools/executor.py +54 -0
  86. inquirycraft-0.6.0/src/inquirycraft/tools/file_tools.py +269 -0
  87. inquirycraft-0.6.0/src/inquirycraft/tools/filesystem/__init__.py +65 -0
  88. inquirycraft-0.6.0/src/inquirycraft/tools/filesystem/edit.py +263 -0
  89. inquirycraft-0.6.0/src/inquirycraft/tools/filesystem/modify.py +168 -0
  90. inquirycraft-0.6.0/src/inquirycraft/tools/filesystem/observe.py +323 -0
  91. inquirycraft-0.6.0/src/inquirycraft/tools/filesystem/search.py +205 -0
  92. inquirycraft-0.6.0/src/inquirycraft/tools/guards.py +160 -0
  93. inquirycraft-0.6.0/src/inquirycraft/tools/path_policy.py +277 -0
  94. inquirycraft-0.6.0/src/inquirycraft/tools/search_tools.py +190 -0
  95. inquirycraft-0.6.0/src/inquirycraft/tools/shell_feedback.py +65 -0
  96. inquirycraft-0.6.0/src/inquirycraft/tools/shell_guards.py +51 -0
  97. inquirycraft-0.6.0/src/inquirycraft/tools/shell_output.py +109 -0
  98. inquirycraft-0.6.0/src/inquirycraft/tools/shell_reducer.py +198 -0
  99. inquirycraft-0.6.0/src/inquirycraft/tools/shell_traceback.py +220 -0
  100. inquirycraft-0.6.0/src/inquirycraft/tools/tool_calls.py +51 -0
  101. inquirycraft-0.6.0/src/inquirycraft/tools/write_safety.py +90 -0
  102. inquirycraft-0.6.0/src/inquirycraft.egg-info/PKG-INFO +222 -0
  103. inquirycraft-0.6.0/src/inquirycraft.egg-info/SOURCES.txt +105 -0
  104. inquirycraft-0.6.0/src/inquirycraft.egg-info/dependency_links.txt +1 -0
  105. inquirycraft-0.6.0/src/inquirycraft.egg-info/entry_points.txt +2 -0
  106. inquirycraft-0.6.0/src/inquirycraft.egg-info/requires.txt +19 -0
  107. inquirycraft-0.6.0/src/inquirycraft.egg-info/top_level.txt +1 -0
@@ -0,0 +1,9 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 InquiryCraft Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,222 @@
1
+ Metadata-Version: 2.4
2
+ Name: inquirycraft
3
+ Version: 0.6.0
4
+ Summary: A lightweight, extensible agent runtime with tools, memory, events, and replay
5
+ License-Expression: MIT
6
+ Project-URL: Repository, https://github.com/EE-PandaMinG/InquiryCraft
7
+ Project-URL: Issues, https://github.com/EE-PandaMinG/InquiryCraft/issues
8
+ Keywords: agent,llm,runtime,tools,mcp
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
14
+ Requires-Python: >=3.11
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Requires-Dist: click>=8.1
18
+ Provides-Extra: openai
19
+ Requires-Dist: openai>=1.0; extra == "openai"
20
+ Requires-Dist: httpx>=0.27; extra == "openai"
21
+ Requires-Dist: lazy-loader>=0.4; extra == "openai"
22
+ Requires-Dist: requests>=2.31; extra == "openai"
23
+ Requires-Dist: tenacity>=9.0.0; extra == "openai"
24
+ Provides-Extra: tokens
25
+ Requires-Dist: tiktoken>=0.7; extra == "tokens"
26
+ Provides-Extra: mcp
27
+ Requires-Dist: mcp[cli]<1.27,>=1.3.0; extra == "mcp"
28
+ Provides-Extra: dev
29
+ Requires-Dist: pytest>=7.0; extra == "dev"
30
+ Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
31
+ Requires-Dist: ruff>=0.15; extra == "dev"
32
+ Dynamic: license-file
33
+
34
+ # InquiryCraft
35
+
36
+ [![PyPI](https://img.shields.io/pypi/v/inquirycraft.svg)](https://pypi.org/project/inquirycraft/)
37
+ [![Python](https://img.shields.io/pypi/pyversions/inquirycraft.svg)](https://pypi.org/project/inquirycraft/)
38
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
39
+
40
+ InquiryCraft is a lightweight, extensible harness for building tool-using LLM agents.
41
+ It provides the execution loop and lifecycle primitives needed by standalone agents,
42
+ interactive CLIs, and larger host applications without imposing a domain workflow.
43
+
44
+ ## Why InquiryCraft?
45
+
46
+ - **Small core** — the minimal installation depends only on Click.
47
+ - **Real agent loop** — provider request, tool call, tool result, and continuation are
48
+ handled by a reusable `AgentRuntime`.
49
+ - **Provider-neutral contracts** — applications can implement `LLMClient` or use the
50
+ optional OpenAI-compatible adapter.
51
+ - **Composable tools** — typed schemas, execution middleware, path policy, concurrent
52
+ dispatch, output reduction, and raw artifact retention share one contract.
53
+ - **Durable runs** — append-only sessions, lifecycle events, cancellation, resume, and
54
+ telemetry are built in.
55
+ - **Deterministic validation** — record/replay detects drift in messages, model options,
56
+ tool schemas, call ordering, results, and stream mode.
57
+ - **Optional integrations** — provider SDKs, token counting, and MCP stay outside the
58
+ minimal package.
59
+
60
+ ## Install
61
+
62
+ Install the minimal runtime and CLI:
63
+
64
+ ```bash
65
+ pip install inquirycraft
66
+ inquirycraft tools list
67
+ ```
68
+
69
+ Install an OpenAI-compatible provider client:
70
+
71
+ ```bash
72
+ pip install 'inquirycraft[openai]'
73
+ ```
74
+
75
+ Other optional extras:
76
+
77
+ ```bash
78
+ pip install 'inquirycraft[tokens]'
79
+ pip install 'inquirycraft[mcp]'
80
+ ```
81
+
82
+ Importing `inquirycraft` resolves public symbols lazily. It does not initialize the CLI
83
+ or load provider SDKs.
84
+
85
+ ## Quick start
86
+
87
+ Configure any OpenAI-compatible endpoint:
88
+
89
+ ```bash
90
+ export INQUIRYCRAFT_API_KEY=...
91
+ export INQUIRYCRAFT_BASE_URL=http://127.0.0.1:8001/v1
92
+ export INQUIRYCRAFT_MODEL=your-model
93
+ ```
94
+
95
+ Run a single task with the built-in file and shell tools:
96
+
97
+ ```bash
98
+ mkdir -p workspace
99
+ inquirycraft run \
100
+ --workspace ./workspace \
101
+ "Inspect the workspace and write a concise summary to result.txt"
102
+ ```
103
+
104
+ Start an interactive session:
105
+
106
+ ```bash
107
+ inquirycraft repl --workspace ./workspace
108
+ ```
109
+
110
+ ## Python API
111
+
112
+ ```python
113
+ import asyncio
114
+ import os
115
+ from pathlib import Path
116
+
117
+ from inquirycraft.llm.openai_adapter import OpenAICompatibleClient
118
+ from inquirycraft.runtime import AgentRuntime, RuntimeOptions
119
+ from inquirycraft.tools import default_tools
120
+
121
+
122
+ async def main() -> None:
123
+ client = OpenAICompatibleClient(
124
+ api_key=os.environ["INQUIRYCRAFT_API_KEY"],
125
+ base_url=os.environ.get("INQUIRYCRAFT_BASE_URL"),
126
+ )
127
+ runtime = AgentRuntime(
128
+ llm=client,
129
+ options=RuntimeOptions(
130
+ model=os.environ["INQUIRYCRAFT_MODEL"],
131
+ workspace=Path("./workspace"),
132
+ system_prompt="You are a careful coding agent.",
133
+ ),
134
+ tools=default_tools(),
135
+ )
136
+ try:
137
+ print(await runtime.run("Inspect the workspace and summarize its contents."))
138
+ finally:
139
+ await client.aclose()
140
+
141
+
142
+ asyncio.run(main())
143
+ ```
144
+
145
+ Implementing the `LLMClient` protocol is enough to connect another provider. Custom
146
+ tools subclass `BaseTool` and return a `ToolResult`; hooks and event sinks can be added
147
+ without modifying the loop.
148
+
149
+ ## Sessions, events, and replay
150
+
151
+ Persist a conversation and its lifecycle events:
152
+
153
+ ```bash
154
+ inquirycraft run "continue the task" \
155
+ --session-log .inquirycraft/session.jsonl \
156
+ --event-log .inquirycraft/events.jsonl
157
+ ```
158
+
159
+ Session logs are append-only. If the file already exists, `run` and `repl` restore its
160
+ messages before the next turn.
161
+
162
+ `RecordingLLMClient` stores the exact provider-visible request and completion result or
163
+ stream chunks. `ReplayLLMClient` consumes that JSONL offline and raises
164
+ `ReplayMismatchError` when model parameters, messages, tool schemas, call ordering, or
165
+ stream mode drift. Generated timestamps and event IDs can be normalized without
166
+ loosening prompts, tool payloads, results, or ordering.
167
+
168
+ ## Tools and path policy
169
+
170
+ InquiryCraft includes independent read, write, edit, grep, glob, list, and shell tools.
171
+ Filesystem scope is configured separately from tool behavior:
172
+
173
+ ```python
174
+ from inquirycraft.tools import PathPolicy, ReadTool
175
+
176
+ policy = PathPolicy("./workspace", enabled=True)
177
+ read = ReadTool(path_policy=policy)
178
+ ```
179
+
180
+ A strict `PathPolicy` contains model-generated paths under a configured root and any
181
+ explicitly allowed extra roots. Hosts that execute untrusted model output should also
182
+ apply process isolation and least-privilege credentials appropriate to their threat
183
+ model.
184
+
185
+ ## CLI reference
186
+
187
+ ```text
188
+ inquirycraft run Run one tool-using agent task
189
+ inquirycraft repl Start an interactive multi-turn session
190
+ inquirycraft tools list List built-in tools
191
+ inquirycraft tools schema Print tool schemas
192
+ inquirycraft events show Inspect runtime event JSONL
193
+ ```
194
+
195
+ Use `inquirycraft COMMAND --help` for command-specific options.
196
+
197
+ ## Package layout
198
+
199
+ | Package | Responsibility |
200
+ | --- | --- |
201
+ | `inquirycraft.runtime` | Agent loop, hooks, cancellation, telemetry, and resume |
202
+ | `inquirycraft.llm` | Provider protocols, streaming models, retry, and adapters |
203
+ | `inquirycraft.tools` | Tool contracts, execution, file tools, and path policy |
204
+ | `inquirycraft.memory` | Messages, conversations, and persistent records |
205
+ | `inquirycraft.events` | Lifecycle JSONL and deterministic record/replay |
206
+ | `inquirycraft.executor` | Generic shell and Python execution helpers |
207
+ | `inquirycraft.mcp` | Optional MCP transport integration |
208
+ | `inquirycraft.cli` | Standalone commands and CLI composition contracts |
209
+
210
+ See [ARCHITECTURE.md](ARCHITECTURE.md) for design boundaries and validation layers.
211
+
212
+ ## Development
213
+
214
+ ```bash
215
+ uv sync --all-extras
216
+ uv run pytest -q
217
+ uv run ruff check .
218
+ uv run ruff format --check .
219
+ uv build
220
+ ```
221
+
222
+ InquiryCraft supports Python 3.11 and newer and is distributed under the MIT License.
@@ -0,0 +1,189 @@
1
+ # InquiryCraft
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/inquirycraft.svg)](https://pypi.org/project/inquirycraft/)
4
+ [![Python](https://img.shields.io/pypi/pyversions/inquirycraft.svg)](https://pypi.org/project/inquirycraft/)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
6
+
7
+ InquiryCraft is a lightweight, extensible harness for building tool-using LLM agents.
8
+ It provides the execution loop and lifecycle primitives needed by standalone agents,
9
+ interactive CLIs, and larger host applications without imposing a domain workflow.
10
+
11
+ ## Why InquiryCraft?
12
+
13
+ - **Small core** — the minimal installation depends only on Click.
14
+ - **Real agent loop** — provider request, tool call, tool result, and continuation are
15
+ handled by a reusable `AgentRuntime`.
16
+ - **Provider-neutral contracts** — applications can implement `LLMClient` or use the
17
+ optional OpenAI-compatible adapter.
18
+ - **Composable tools** — typed schemas, execution middleware, path policy, concurrent
19
+ dispatch, output reduction, and raw artifact retention share one contract.
20
+ - **Durable runs** — append-only sessions, lifecycle events, cancellation, resume, and
21
+ telemetry are built in.
22
+ - **Deterministic validation** — record/replay detects drift in messages, model options,
23
+ tool schemas, call ordering, results, and stream mode.
24
+ - **Optional integrations** — provider SDKs, token counting, and MCP stay outside the
25
+ minimal package.
26
+
27
+ ## Install
28
+
29
+ Install the minimal runtime and CLI:
30
+
31
+ ```bash
32
+ pip install inquirycraft
33
+ inquirycraft tools list
34
+ ```
35
+
36
+ Install an OpenAI-compatible provider client:
37
+
38
+ ```bash
39
+ pip install 'inquirycraft[openai]'
40
+ ```
41
+
42
+ Other optional extras:
43
+
44
+ ```bash
45
+ pip install 'inquirycraft[tokens]'
46
+ pip install 'inquirycraft[mcp]'
47
+ ```
48
+
49
+ Importing `inquirycraft` resolves public symbols lazily. It does not initialize the CLI
50
+ or load provider SDKs.
51
+
52
+ ## Quick start
53
+
54
+ Configure any OpenAI-compatible endpoint:
55
+
56
+ ```bash
57
+ export INQUIRYCRAFT_API_KEY=...
58
+ export INQUIRYCRAFT_BASE_URL=http://127.0.0.1:8001/v1
59
+ export INQUIRYCRAFT_MODEL=your-model
60
+ ```
61
+
62
+ Run a single task with the built-in file and shell tools:
63
+
64
+ ```bash
65
+ mkdir -p workspace
66
+ inquirycraft run \
67
+ --workspace ./workspace \
68
+ "Inspect the workspace and write a concise summary to result.txt"
69
+ ```
70
+
71
+ Start an interactive session:
72
+
73
+ ```bash
74
+ inquirycraft repl --workspace ./workspace
75
+ ```
76
+
77
+ ## Python API
78
+
79
+ ```python
80
+ import asyncio
81
+ import os
82
+ from pathlib import Path
83
+
84
+ from inquirycraft.llm.openai_adapter import OpenAICompatibleClient
85
+ from inquirycraft.runtime import AgentRuntime, RuntimeOptions
86
+ from inquirycraft.tools import default_tools
87
+
88
+
89
+ async def main() -> None:
90
+ client = OpenAICompatibleClient(
91
+ api_key=os.environ["INQUIRYCRAFT_API_KEY"],
92
+ base_url=os.environ.get("INQUIRYCRAFT_BASE_URL"),
93
+ )
94
+ runtime = AgentRuntime(
95
+ llm=client,
96
+ options=RuntimeOptions(
97
+ model=os.environ["INQUIRYCRAFT_MODEL"],
98
+ workspace=Path("./workspace"),
99
+ system_prompt="You are a careful coding agent.",
100
+ ),
101
+ tools=default_tools(),
102
+ )
103
+ try:
104
+ print(await runtime.run("Inspect the workspace and summarize its contents."))
105
+ finally:
106
+ await client.aclose()
107
+
108
+
109
+ asyncio.run(main())
110
+ ```
111
+
112
+ Implementing the `LLMClient` protocol is enough to connect another provider. Custom
113
+ tools subclass `BaseTool` and return a `ToolResult`; hooks and event sinks can be added
114
+ without modifying the loop.
115
+
116
+ ## Sessions, events, and replay
117
+
118
+ Persist a conversation and its lifecycle events:
119
+
120
+ ```bash
121
+ inquirycraft run "continue the task" \
122
+ --session-log .inquirycraft/session.jsonl \
123
+ --event-log .inquirycraft/events.jsonl
124
+ ```
125
+
126
+ Session logs are append-only. If the file already exists, `run` and `repl` restore its
127
+ messages before the next turn.
128
+
129
+ `RecordingLLMClient` stores the exact provider-visible request and completion result or
130
+ stream chunks. `ReplayLLMClient` consumes that JSONL offline and raises
131
+ `ReplayMismatchError` when model parameters, messages, tool schemas, call ordering, or
132
+ stream mode drift. Generated timestamps and event IDs can be normalized without
133
+ loosening prompts, tool payloads, results, or ordering.
134
+
135
+ ## Tools and path policy
136
+
137
+ InquiryCraft includes independent read, write, edit, grep, glob, list, and shell tools.
138
+ Filesystem scope is configured separately from tool behavior:
139
+
140
+ ```python
141
+ from inquirycraft.tools import PathPolicy, ReadTool
142
+
143
+ policy = PathPolicy("./workspace", enabled=True)
144
+ read = ReadTool(path_policy=policy)
145
+ ```
146
+
147
+ A strict `PathPolicy` contains model-generated paths under a configured root and any
148
+ explicitly allowed extra roots. Hosts that execute untrusted model output should also
149
+ apply process isolation and least-privilege credentials appropriate to their threat
150
+ model.
151
+
152
+ ## CLI reference
153
+
154
+ ```text
155
+ inquirycraft run Run one tool-using agent task
156
+ inquirycraft repl Start an interactive multi-turn session
157
+ inquirycraft tools list List built-in tools
158
+ inquirycraft tools schema Print tool schemas
159
+ inquirycraft events show Inspect runtime event JSONL
160
+ ```
161
+
162
+ Use `inquirycraft COMMAND --help` for command-specific options.
163
+
164
+ ## Package layout
165
+
166
+ | Package | Responsibility |
167
+ | --- | --- |
168
+ | `inquirycraft.runtime` | Agent loop, hooks, cancellation, telemetry, and resume |
169
+ | `inquirycraft.llm` | Provider protocols, streaming models, retry, and adapters |
170
+ | `inquirycraft.tools` | Tool contracts, execution, file tools, and path policy |
171
+ | `inquirycraft.memory` | Messages, conversations, and persistent records |
172
+ | `inquirycraft.events` | Lifecycle JSONL and deterministic record/replay |
173
+ | `inquirycraft.executor` | Generic shell and Python execution helpers |
174
+ | `inquirycraft.mcp` | Optional MCP transport integration |
175
+ | `inquirycraft.cli` | Standalone commands and CLI composition contracts |
176
+
177
+ See [ARCHITECTURE.md](ARCHITECTURE.md) for design boundaries and validation layers.
178
+
179
+ ## Development
180
+
181
+ ```bash
182
+ uv sync --all-extras
183
+ uv run pytest -q
184
+ uv run ruff check .
185
+ uv run ruff format --check .
186
+ uv build
187
+ ```
188
+
189
+ InquiryCraft supports Python 3.11 and newer and is distributed under the MIT License.
@@ -0,0 +1,70 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "inquirycraft"
7
+ version = "0.6.0"
8
+ description = "A lightweight, extensible agent runtime with tools, memory, events, and replay"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ license-files = ["LICENSE"]
12
+ requires-python = ">=3.11"
13
+ keywords = ["agent", "llm", "runtime", "tools", "mcp"]
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Programming Language :: Python :: 3",
17
+ "Programming Language :: Python :: 3.11",
18
+ "Programming Language :: Python :: 3.12",
19
+ "Topic :: Software Development :: Libraries :: Application Frameworks",
20
+ ]
21
+ dependencies = [
22
+ "click>=8.1",
23
+ ]
24
+
25
+ [project.optional-dependencies]
26
+ openai = [
27
+ "openai>=1.0",
28
+ "httpx>=0.27",
29
+ "lazy-loader>=0.4",
30
+ "requests>=2.31",
31
+ "tenacity>=9.0.0",
32
+ ]
33
+ tokens = [
34
+ "tiktoken>=0.7",
35
+ ]
36
+ mcp = [
37
+ "mcp[cli]>=1.3.0,<1.27",
38
+ ]
39
+ dev = [
40
+ "pytest>=7.0",
41
+ "pytest-asyncio>=0.21",
42
+ "ruff>=0.15",
43
+ ]
44
+
45
+ [project.scripts]
46
+ inquirycraft = "inquirycraft.cli:main"
47
+
48
+ [project.urls]
49
+ Repository = "https://github.com/EE-PandaMinG/InquiryCraft"
50
+ Issues = "https://github.com/EE-PandaMinG/InquiryCraft/issues"
51
+
52
+ [tool.setuptools]
53
+ package-dir = {"" = "src"}
54
+ include-package-data = false
55
+
56
+ [tool.setuptools.packages.find]
57
+ where = ["src"]
58
+ include = ["inquirycraft*"]
59
+
60
+ [tool.pytest.ini_options]
61
+ asyncio_mode = "auto"
62
+ testpaths = ["tests"]
63
+
64
+ [tool.ruff]
65
+ target-version = "py311"
66
+ line-length = 100
67
+ exclude = ["legacy_packages"]
68
+
69
+ [tool.ruff.lint]
70
+ select = ["E4", "E7", "E9", "F", "I"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,76 @@
1
+ """InquiryCraft's lightweight, domain-independent agent runtime.
2
+
3
+ Every public symbol is resolved lazily. Importing the package does not initialize
4
+ the runtime, CLI, or provider adapters.
5
+ """
6
+
7
+ from importlib import import_module
8
+ from importlib.metadata import PackageNotFoundError, version
9
+ from typing import Any
10
+
11
+ try:
12
+ __version__ = version("inquirycraft")
13
+ except PackageNotFoundError: # source checkout without an installed distribution
14
+ __version__ = "0+unknown"
15
+
16
+ _EXPORTS = {
17
+ "AgentHooks": ("inquirycraft.runtime", "AgentHooks"),
18
+ "AgentRuntime": ("inquirycraft.runtime", "AgentRuntime"),
19
+ "BaseTool": ("inquirycraft.tools", "BaseTool"),
20
+ "CompletionRequest": ("inquirycraft.llm", "CompletionRequest"),
21
+ "CompletionResult": ("inquirycraft.llm", "CompletionResult"),
22
+ "CompositeEventSink": ("inquirycraft.events", "CompositeEventSink"),
23
+ "Conversation": ("inquirycraft.memory", "Conversation"),
24
+ "EventSink": ("inquirycraft.events", "EventSink"),
25
+ "JsonlEventSink": ("inquirycraft.events", "JsonlEventSink"),
26
+ "LLMClient": ("inquirycraft.llm", "LLMClient"),
27
+ "Message": ("inquirycraft.memory", "Message"),
28
+ "RecordingLLMClient": ("inquirycraft.events", "RecordingLLMClient"),
29
+ "ReplayLLMClient": ("inquirycraft.events", "ReplayLLMClient"),
30
+ "ResumeState": ("inquirycraft.runtime", "ResumeState"),
31
+ "RuntimeContext": ("inquirycraft.runtime", "RuntimeContext"),
32
+ "RuntimeEvent": ("inquirycraft.events", "RuntimeEvent"),
33
+ "RuntimeOptions": ("inquirycraft.runtime", "RuntimeOptions"),
34
+ "ToolCollection": ("inquirycraft.tools", "ToolCollection"),
35
+ "ToolContext": ("inquirycraft.tools", "ToolContext"),
36
+ "ToolResult": ("inquirycraft.tools", "ToolResult"),
37
+ "Usage": ("inquirycraft.llm", "Usage"),
38
+ }
39
+
40
+ __all__ = [
41
+ "__version__",
42
+ "AgentHooks",
43
+ "AgentRuntime",
44
+ "BaseTool",
45
+ "CompletionRequest",
46
+ "CompletionResult",
47
+ "CompositeEventSink",
48
+ "Conversation",
49
+ "EventSink",
50
+ "JsonlEventSink",
51
+ "LLMClient",
52
+ "Message",
53
+ "RecordingLLMClient",
54
+ "ReplayLLMClient",
55
+ "ResumeState",
56
+ "RuntimeContext",
57
+ "RuntimeEvent",
58
+ "RuntimeOptions",
59
+ "ToolCollection",
60
+ "ToolContext",
61
+ "ToolResult",
62
+ "Usage",
63
+ ]
64
+
65
+
66
+ def __getattr__(name: str) -> Any:
67
+ if name not in _EXPORTS:
68
+ raise AttributeError(name)
69
+ module_name, attribute = _EXPORTS[name]
70
+ value = getattr(import_module(module_name), attribute)
71
+ globals()[name] = value
72
+ return value
73
+
74
+
75
+ def __dir__() -> list[str]:
76
+ return sorted(set(globals()) | set(__all__))
@@ -0,0 +1,4 @@
1
+ from .cli import main
2
+
3
+ if __name__ == "__main__":
4
+ main()