qoder-agent-sdk 1.0.14__tar.gz → 1.0.16__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.
- {qoder_agent_sdk-1.0.14 → qoder_agent_sdk-1.0.16}/.gitignore +1 -0
- {qoder_agent_sdk-1.0.14 → qoder_agent_sdk-1.0.16}/PKG-INFO +33 -1
- {qoder_agent_sdk-1.0.14 → qoder_agent_sdk-1.0.16}/README.md +32 -0
- {qoder_agent_sdk-1.0.14 → qoder_agent_sdk-1.0.16}/pyproject.toml +1 -1
- {qoder_agent_sdk-1.0.14 → qoder_agent_sdk-1.0.16}/src/qoder_agent_sdk/__init__.py +64 -0
- {qoder_agent_sdk-1.0.14 → qoder_agent_sdk-1.0.16}/src/qoder_agent_sdk/_cli_version.py +1 -1
- {qoder_agent_sdk-1.0.14 → qoder_agent_sdk-1.0.16}/src/qoder_agent_sdk/_internal/client.py +3 -0
- qoder_agent_sdk-1.0.16/src/qoder_agent_sdk/_internal/memory.py +186 -0
- {qoder_agent_sdk-1.0.14 → qoder_agent_sdk-1.0.16}/src/qoder_agent_sdk/_internal/message_parser.py +36 -8
- {qoder_agent_sdk-1.0.14 → qoder_agent_sdk-1.0.16}/src/qoder_agent_sdk/_internal/query.py +131 -4
- qoder_agent_sdk-1.0.16/src/qoder_agent_sdk/_tool_results.py +38 -0
- {qoder_agent_sdk-1.0.14 → qoder_agent_sdk-1.0.16}/src/qoder_agent_sdk/_version.py +1 -1
- {qoder_agent_sdk-1.0.14 → qoder_agent_sdk-1.0.16}/src/qoder_agent_sdk/client.py +67 -0
- qoder_agent_sdk-1.0.16/src/qoder_agent_sdk/memory.py +142 -0
- {qoder_agent_sdk-1.0.14 → qoder_agent_sdk-1.0.16}/src/qoder_agent_sdk/query.py +4 -0
- {qoder_agent_sdk-1.0.14 → qoder_agent_sdk-1.0.16}/src/qoder_agent_sdk/types.py +192 -2
- {qoder_agent_sdk-1.0.14 → qoder_agent_sdk-1.0.16}/LICENSE +0 -0
- {qoder_agent_sdk-1.0.14 → qoder_agent_sdk-1.0.16}/src/qoder_agent_sdk/_brand.py +0 -0
- {qoder_agent_sdk-1.0.14 → qoder_agent_sdk-1.0.16}/src/qoder_agent_sdk/_bundled/.gitignore +0 -0
- {qoder_agent_sdk-1.0.14 → qoder_agent_sdk-1.0.16}/src/qoder_agent_sdk/_errors.py +0 -0
- {qoder_agent_sdk-1.0.14 → qoder_agent_sdk-1.0.16}/src/qoder_agent_sdk/_internal/__init__.py +0 -0
- {qoder_agent_sdk-1.0.14 → qoder_agent_sdk-1.0.16}/src/qoder_agent_sdk/_internal/auth_helpers.py +0 -0
- {qoder_agent_sdk-1.0.14 → qoder_agent_sdk-1.0.16}/src/qoder_agent_sdk/_internal/cloud_agent.py +0 -0
- {qoder_agent_sdk-1.0.14 → qoder_agent_sdk-1.0.16}/src/qoder_agent_sdk/_internal/mcp_handler.py +0 -0
- {qoder_agent_sdk-1.0.14 → qoder_agent_sdk-1.0.16}/src/qoder_agent_sdk/_internal/mcp_serialize.py +0 -0
- {qoder_agent_sdk-1.0.14 → qoder_agent_sdk-1.0.16}/src/qoder_agent_sdk/_internal/sdk_mcp_transport.py +0 -0
- {qoder_agent_sdk-1.0.14 → qoder_agent_sdk-1.0.16}/src/qoder_agent_sdk/_internal/session_mutations.py +0 -0
- {qoder_agent_sdk-1.0.14 → qoder_agent_sdk-1.0.16}/src/qoder_agent_sdk/_internal/session_storage.py +0 -0
- {qoder_agent_sdk-1.0.14 → qoder_agent_sdk-1.0.16}/src/qoder_agent_sdk/_internal/sessions.py +0 -0
- {qoder_agent_sdk-1.0.14 → qoder_agent_sdk-1.0.16}/src/qoder_agent_sdk/_internal/skill_evolution.py +0 -0
- {qoder_agent_sdk-1.0.14 → qoder_agent_sdk-1.0.16}/src/qoder_agent_sdk/_internal/transport/__init__.py +0 -0
- {qoder_agent_sdk-1.0.14 → qoder_agent_sdk-1.0.16}/src/qoder_agent_sdk/_internal/transport/subprocess_cli.py +0 -0
- {qoder_agent_sdk-1.0.14 → qoder_agent_sdk-1.0.16}/src/qoder_agent_sdk/auth.py +0 -0
- {qoder_agent_sdk-1.0.14 → qoder_agent_sdk-1.0.16}/src/qoder_agent_sdk/plugins.py +0 -0
- {qoder_agent_sdk-1.0.14 → qoder_agent_sdk-1.0.16}/src/qoder_agent_sdk/py.typed +0 -0
- {qoder_agent_sdk-1.0.14 → qoder_agent_sdk-1.0.16}/src/qoder_agent_sdk/session_import.py +0 -0
- {qoder_agent_sdk-1.0.14 → qoder_agent_sdk-1.0.16}/src/qoder_agent_sdk/session_store.py +0 -0
- {qoder_agent_sdk-1.0.14 → qoder_agent_sdk-1.0.16}/src/qoder_agent_sdk/settings.py +0 -0
- {qoder_agent_sdk-1.0.14 → qoder_agent_sdk-1.0.16}/src/qoder_agent_sdk/store_sessions.py +0 -0
- {qoder_agent_sdk-1.0.14 → qoder_agent_sdk-1.0.16}/src/qoder_agent_sdk/testing/__init__.py +0 -0
- {qoder_agent_sdk-1.0.14 → qoder_agent_sdk-1.0.16}/src/qoder_agent_sdk/testing/session_store_conformance.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: qoder-agent-sdk
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.16
|
|
4
4
|
Summary: Python SDK for Qoder Agent
|
|
5
5
|
Author: Qoder
|
|
6
6
|
License: Copyright (c) 2026 Qoder
|
|
@@ -315,6 +315,34 @@ cancellation, and do not reuse UUIDs within a session.
|
|
|
315
315
|
`await client.cancel_async_message(message_uuid)` returns `True` when the
|
|
316
316
|
queued message is cancelled and `False` when it can no longer be cancelled.
|
|
317
317
|
|
|
318
|
+
Manage background tasks from a connected `QoderSDKClient`:
|
|
319
|
+
|
|
320
|
+
```python
|
|
321
|
+
await client.background_tasks(tool_use_id) # ToolUseBlock.id; omit for bulk conversion
|
|
322
|
+
await client.stop_task(task_id) # TaskStartedMessage.task_id
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
`background_tasks()` moves running foreground Bash / Agent executions into the
|
|
326
|
+
background; it does not list tasks. A targeted call returns whether the execution
|
|
327
|
+
moved; bulk `True` acknowledges the request without guaranteeing a transition.
|
|
328
|
+
`stop_task()` returns `None` and leaves the main turn and other independent tasks
|
|
329
|
+
running. Unknown IDs and repeated stops are safe no-ops. Both methods require the
|
|
330
|
+
CLI capability `background_tasks_v1` (otherwise `UnsupportedCliCapabilityError`)
|
|
331
|
+
and are unavailable in Cloud Agent sessions.
|
|
332
|
+
|
|
333
|
+
`interrupt()` does not stop existing background tasks. To clean them up, continue
|
|
334
|
+
consuming `receive_messages()` after the main `ResultMessage`, replace the roster
|
|
335
|
+
on each `BackgroundTasksChangedMessage`, and stop each task separately. Bulk
|
|
336
|
+
conversion can detach an Agent's Bash execution as an independent task.
|
|
337
|
+
|
|
338
|
+
Use `AgentDefinition(background=True, ...)` to launch delegated agents in the
|
|
339
|
+
background. Merge `TaskUpdatedMessage.patch` into stored task state. Task events
|
|
340
|
+
retain raw `data`; optional metadata depends on the CLI. Missing progress `usage`
|
|
341
|
+
is `None`, and `total_tokens` can be absent within usage.
|
|
342
|
+
|
|
343
|
+
For event fields, launch result types, the Bash result guard, and examples, see
|
|
344
|
+
[Python SDK Reference](https://docs.qoder.com/cli/sdk/references-python#clientstop-task).
|
|
345
|
+
|
|
318
346
|
## External Session Storage
|
|
319
347
|
|
|
320
348
|
Use `session_store` when a host needs durable transcripts outside the local
|
|
@@ -405,6 +433,10 @@ against shared storage operated by your application, then validate its
|
|
|
405
433
|
append/load ordering, project isolation, subkey handling, and deletion behavior
|
|
406
434
|
with `run_session_store_conformance`.
|
|
407
435
|
|
|
436
|
+
## Memory
|
|
437
|
+
|
|
438
|
+
Configure native or custom memory with `QoderAgentOptions(memory=...)`. Interactive clients expose `flush_memory()` and `refresh_memory()`. See [Memory](doc/memory.md) for configuration, generation callbacks, and lifecycle behavior.
|
|
439
|
+
|
|
408
440
|
## Custom Tools
|
|
409
441
|
|
|
410
442
|
You can expose Python functions to Qoder Agent as in-process SDK MCP servers.
|
|
@@ -273,6 +273,34 @@ cancellation, and do not reuse UUIDs within a session.
|
|
|
273
273
|
`await client.cancel_async_message(message_uuid)` returns `True` when the
|
|
274
274
|
queued message is cancelled and `False` when it can no longer be cancelled.
|
|
275
275
|
|
|
276
|
+
Manage background tasks from a connected `QoderSDKClient`:
|
|
277
|
+
|
|
278
|
+
```python
|
|
279
|
+
await client.background_tasks(tool_use_id) # ToolUseBlock.id; omit for bulk conversion
|
|
280
|
+
await client.stop_task(task_id) # TaskStartedMessage.task_id
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
`background_tasks()` moves running foreground Bash / Agent executions into the
|
|
284
|
+
background; it does not list tasks. A targeted call returns whether the execution
|
|
285
|
+
moved; bulk `True` acknowledges the request without guaranteeing a transition.
|
|
286
|
+
`stop_task()` returns `None` and leaves the main turn and other independent tasks
|
|
287
|
+
running. Unknown IDs and repeated stops are safe no-ops. Both methods require the
|
|
288
|
+
CLI capability `background_tasks_v1` (otherwise `UnsupportedCliCapabilityError`)
|
|
289
|
+
and are unavailable in Cloud Agent sessions.
|
|
290
|
+
|
|
291
|
+
`interrupt()` does not stop existing background tasks. To clean them up, continue
|
|
292
|
+
consuming `receive_messages()` after the main `ResultMessage`, replace the roster
|
|
293
|
+
on each `BackgroundTasksChangedMessage`, and stop each task separately. Bulk
|
|
294
|
+
conversion can detach an Agent's Bash execution as an independent task.
|
|
295
|
+
|
|
296
|
+
Use `AgentDefinition(background=True, ...)` to launch delegated agents in the
|
|
297
|
+
background. Merge `TaskUpdatedMessage.patch` into stored task state. Task events
|
|
298
|
+
retain raw `data`; optional metadata depends on the CLI. Missing progress `usage`
|
|
299
|
+
is `None`, and `total_tokens` can be absent within usage.
|
|
300
|
+
|
|
301
|
+
For event fields, launch result types, the Bash result guard, and examples, see
|
|
302
|
+
[Python SDK Reference](https://docs.qoder.com/cli/sdk/references-python#clientstop-task).
|
|
303
|
+
|
|
276
304
|
## External Session Storage
|
|
277
305
|
|
|
278
306
|
Use `session_store` when a host needs durable transcripts outside the local
|
|
@@ -363,6 +391,10 @@ against shared storage operated by your application, then validate its
|
|
|
363
391
|
append/load ordering, project isolation, subkey handling, and deletion behavior
|
|
364
392
|
with `run_session_store_conformance`.
|
|
365
393
|
|
|
394
|
+
## Memory
|
|
395
|
+
|
|
396
|
+
Configure native or custom memory with `QoderAgentOptions(memory=...)`. Interactive clients expose `flush_memory()` and `refresh_memory()`. See [Memory](doc/memory.md) for configuration, generation callbacks, and lifecycle behavior.
|
|
397
|
+
|
|
366
398
|
## Custom Tools
|
|
367
399
|
|
|
368
400
|
You can expose Python functions to Qoder Agent as in-process SDK MCP servers.
|
|
@@ -50,6 +50,7 @@ from ._internal.sessions import (
|
|
|
50
50
|
project_key_for_directory,
|
|
51
51
|
)
|
|
52
52
|
from ._internal.transport import Transport
|
|
53
|
+
from ._tool_results import is_bash_tool_background_launch_result
|
|
53
54
|
from ._version import __version__
|
|
54
55
|
from .auth import (
|
|
55
56
|
DEFAULT_ACCESS_TOKEN_ENV_VAR,
|
|
@@ -78,6 +79,29 @@ from .auth import (
|
|
|
78
79
|
service_account_from_env,
|
|
79
80
|
)
|
|
80
81
|
from .client import QoderSDKClient
|
|
82
|
+
from .memory import (
|
|
83
|
+
EffectiveMemoryConfig,
|
|
84
|
+
EffectiveMemoryRoot,
|
|
85
|
+
MemoryConsumptionCallback,
|
|
86
|
+
MemoryConsumptionFile,
|
|
87
|
+
MemoryConsumptionFileResult,
|
|
88
|
+
MemoryConsumptionOptions,
|
|
89
|
+
MemoryConsumptionResult,
|
|
90
|
+
MemoryGateContext,
|
|
91
|
+
MemoryGenerationCallback,
|
|
92
|
+
MemoryGenerationFailedFile,
|
|
93
|
+
MemoryGenerationGateInput,
|
|
94
|
+
MemoryGenerationGateResult,
|
|
95
|
+
MemoryGenerationOptions,
|
|
96
|
+
MemoryGenerationResult,
|
|
97
|
+
MemoryMode,
|
|
98
|
+
MemoryOptions,
|
|
99
|
+
MemoryRoot,
|
|
100
|
+
MemoryRootAccess,
|
|
101
|
+
MemoryShouldGenerate,
|
|
102
|
+
MemoryTurnCompleteOptions,
|
|
103
|
+
MemoryWrittenFile,
|
|
104
|
+
)
|
|
81
105
|
from .plugins import (
|
|
82
106
|
PluginError,
|
|
83
107
|
disable_plugin,
|
|
@@ -106,10 +130,14 @@ from .types import (
|
|
|
106
130
|
MAX_SKILL_EVOLUTION_REVIEW_THRESHOLD,
|
|
107
131
|
AgentCreateParams,
|
|
108
132
|
AgentDefinition,
|
|
133
|
+
AgentToolBackgroundLaunchResult,
|
|
109
134
|
AssistantMessage,
|
|
110
135
|
AssistantMessageWithRequestId,
|
|
111
136
|
AsyncHookJSONOutput,
|
|
137
|
+
BackgroundTask,
|
|
138
|
+
BackgroundTasksChangedMessage,
|
|
112
139
|
BaseHookInput,
|
|
140
|
+
BashToolBackgroundLaunchResult,
|
|
113
141
|
BetaManagedAgentsAgentToolName,
|
|
114
142
|
BetaManagedAgentsAgentToolset20260401Params,
|
|
115
143
|
BetaManagedAgentsCustomSkillParams,
|
|
@@ -186,6 +214,8 @@ from .types import (
|
|
|
186
214
|
McpStatusResponse,
|
|
187
215
|
McpToolAnnotations,
|
|
188
216
|
McpToolInfo,
|
|
217
|
+
MemoryConsumptionMessage,
|
|
218
|
+
MemoryGenerationMessage,
|
|
189
219
|
Message,
|
|
190
220
|
ModelContextConfig,
|
|
191
221
|
ModelContextWindowEntry,
|
|
@@ -306,6 +336,9 @@ from .types import (
|
|
|
306
336
|
TaskNotificationStatus,
|
|
307
337
|
TaskProgressMessage,
|
|
308
338
|
TaskStartedMessage,
|
|
339
|
+
TaskStatus,
|
|
340
|
+
TaskUpdatedMessage,
|
|
341
|
+
TaskUpdatePatch,
|
|
309
342
|
TaskUsage,
|
|
310
343
|
TextBlock,
|
|
311
344
|
ThinkingBlock,
|
|
@@ -844,6 +877,29 @@ def create_sdk_mcp_server(
|
|
|
844
877
|
|
|
845
878
|
|
|
846
879
|
__all__ = [
|
|
880
|
+
"EffectiveMemoryConfig",
|
|
881
|
+
"EffectiveMemoryRoot",
|
|
882
|
+
"MemoryMode",
|
|
883
|
+
"MemoryRootAccess",
|
|
884
|
+
"MemoryRoot",
|
|
885
|
+
"MemoryConsumptionFile",
|
|
886
|
+
"MemoryGenerationGateInput",
|
|
887
|
+
"MemoryGenerationGateResult",
|
|
888
|
+
"MemoryGateContext",
|
|
889
|
+
"MemoryWrittenFile",
|
|
890
|
+
"MemoryGenerationFailedFile",
|
|
891
|
+
"MemoryGenerationResult",
|
|
892
|
+
"MemoryConsumptionFileResult",
|
|
893
|
+
"MemoryConsumptionResult",
|
|
894
|
+
"MemoryShouldGenerate",
|
|
895
|
+
"MemoryGenerationCallback",
|
|
896
|
+
"MemoryConsumptionCallback",
|
|
897
|
+
"MemoryTurnCompleteOptions",
|
|
898
|
+
"MemoryGenerationOptions",
|
|
899
|
+
"MemoryConsumptionOptions",
|
|
900
|
+
"MemoryOptions",
|
|
901
|
+
"MemoryGenerationMessage",
|
|
902
|
+
"MemoryConsumptionMessage",
|
|
847
903
|
"GoalClearedMessage",
|
|
848
904
|
"GoalSnapshot",
|
|
849
905
|
"GoalStatus",
|
|
@@ -935,6 +991,14 @@ __all__ = [
|
|
|
935
991
|
"TaskNotificationMessage",
|
|
936
992
|
"TaskNotificationStatus",
|
|
937
993
|
"TaskUsage",
|
|
994
|
+
"TaskStatus",
|
|
995
|
+
"TaskUpdatePatch",
|
|
996
|
+
"TaskUpdatedMessage",
|
|
997
|
+
"BackgroundTask",
|
|
998
|
+
"BackgroundTasksChangedMessage",
|
|
999
|
+
"BashToolBackgroundLaunchResult",
|
|
1000
|
+
"AgentToolBackgroundLaunchResult",
|
|
1001
|
+
"is_bash_tool_background_launch_result",
|
|
938
1002
|
"ResultMessage",
|
|
939
1003
|
"StreamEvent",
|
|
940
1004
|
"CloudAgentEventMessage",
|
|
@@ -16,6 +16,7 @@ from .auth_helpers import (
|
|
|
16
16
|
get_fetch_service_account_token,
|
|
17
17
|
once_callback,
|
|
18
18
|
)
|
|
19
|
+
from .memory import prepare_memory_options
|
|
19
20
|
from .message_parser import parse_message
|
|
20
21
|
from .query import Query, resolve_initialize_timeout
|
|
21
22
|
from .session_storage import (
|
|
@@ -101,6 +102,7 @@ class InternalClient:
|
|
|
101
102
|
configured_options, on_auth_expired=on_auth_expired
|
|
102
103
|
)
|
|
103
104
|
|
|
105
|
+
prepared_memory = prepare_memory_options(configured_options.memory)
|
|
104
106
|
requires_store_resume = configured_options.session_store is not None and (
|
|
105
107
|
configured_options.resume is not None
|
|
106
108
|
or configured_options.continue_conversation
|
|
@@ -170,6 +172,7 @@ class InternalClient:
|
|
|
170
172
|
try:
|
|
171
173
|
query = Query(
|
|
172
174
|
transport=chosen_transport,
|
|
175
|
+
memory=prepared_memory,
|
|
173
176
|
is_streaming_mode=True, # Always streaming internally
|
|
174
177
|
can_use_tool=configured_options.can_use_tool,
|
|
175
178
|
hooks=convert_hooks_to_internal_format(configured_options.hooks)
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"""Validate memory options before starting the CLI; never serialize callbacks."""
|
|
2
|
+
|
|
3
|
+
import math
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
from pathlib import PurePosixPath, PureWindowsPath
|
|
6
|
+
from typing import Any, cast
|
|
7
|
+
|
|
8
|
+
from ..memory import (
|
|
9
|
+
MemoryConsumptionCallback,
|
|
10
|
+
MemoryGenerationCallback,
|
|
11
|
+
MemoryOptions,
|
|
12
|
+
MemoryShouldGenerate,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class PreparedMemoryOptions:
|
|
18
|
+
config: dict[str, Any] | None = None
|
|
19
|
+
gates: dict[str, MemoryShouldGenerate] = field(default_factory=dict)
|
|
20
|
+
on_generation: MemoryGenerationCallback | None = None
|
|
21
|
+
on_consumption: MemoryConsumptionCallback | None = None
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _object(value: Any, name: str) -> dict[str, Any]:
|
|
25
|
+
if not isinstance(value, dict):
|
|
26
|
+
raise TypeError(f"{name} must be an object")
|
|
27
|
+
return cast(dict[str, Any], value)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _string(value: Any, name: str) -> None:
|
|
31
|
+
if not isinstance(value, str) or not value.strip():
|
|
32
|
+
raise TypeError(f"{name} must be a non-empty string")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _boolean(data: dict[str, Any], key: str, name: str) -> None:
|
|
36
|
+
if key in data and not isinstance(data[key], bool):
|
|
37
|
+
raise TypeError(f"{name}.{key} must be a boolean")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _callback(data: dict[str, Any], key: str, name: str) -> None:
|
|
41
|
+
if key in data and not callable(data[key]):
|
|
42
|
+
raise TypeError(f"{name}.{key} must be callable")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _choice(
|
|
46
|
+
data: dict[str, Any], key: str, choices: tuple[str, ...], name: str
|
|
47
|
+
) -> None:
|
|
48
|
+
if key in data and data[key] not in choices:
|
|
49
|
+
raise TypeError(f"{name}.{key} must be one of {choices}")
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _files(value: Any, name: str, *, roots: bool) -> list[dict[str, Any]]:
|
|
53
|
+
if not isinstance(value, list) or not value:
|
|
54
|
+
raise TypeError(f"{name} must be a non-empty list")
|
|
55
|
+
ids: set[str] = set()
|
|
56
|
+
result = []
|
|
57
|
+
for index, raw in enumerate(value):
|
|
58
|
+
label = f"{name}[{index}]"
|
|
59
|
+
item = _object(raw, label)
|
|
60
|
+
for key in ("id", "path"):
|
|
61
|
+
_string(item.get(key), f"{label}.{key}")
|
|
62
|
+
if item["id"] in ids:
|
|
63
|
+
raise TypeError(f'{name} contains duplicate id "{item["id"]}"')
|
|
64
|
+
ids.add(item["id"])
|
|
65
|
+
output = {"id": item["id"], "path": item["path"]}
|
|
66
|
+
if roots:
|
|
67
|
+
_choice(item, "access", ("read", "read-write"), label)
|
|
68
|
+
if "access" in item:
|
|
69
|
+
output["access"] = item["access"]
|
|
70
|
+
if "index_file" in item:
|
|
71
|
+
path = item["index_file"]
|
|
72
|
+
_string(path, f"{label}.index_file")
|
|
73
|
+
normalized = path.replace("\\", "/")
|
|
74
|
+
if (
|
|
75
|
+
PurePosixPath(normalized).is_absolute()
|
|
76
|
+
or PureWindowsPath(path).root
|
|
77
|
+
or ".." in normalized.split("/")
|
|
78
|
+
):
|
|
79
|
+
raise TypeError(f"{label}.index_file must stay within its root")
|
|
80
|
+
output["indexFile"] = path
|
|
81
|
+
else:
|
|
82
|
+
_boolean(item, "required", label)
|
|
83
|
+
output["required"] = item.get("required", False)
|
|
84
|
+
result.append(output)
|
|
85
|
+
return result
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def prepare_memory_options(memory: MemoryOptions | None) -> PreparedMemoryOptions:
|
|
89
|
+
prepared = PreparedMemoryOptions()
|
|
90
|
+
if memory is None:
|
|
91
|
+
return prepared
|
|
92
|
+
data = _object(memory, "options.memory")
|
|
93
|
+
if not data:
|
|
94
|
+
return prepared
|
|
95
|
+
if data.get("mode") not in ("native", "custom"):
|
|
96
|
+
raise TypeError('options.memory.mode must be "native" or "custom"')
|
|
97
|
+
config: dict[str, Any] = {"requester": "sdk", "mode": data["mode"]}
|
|
98
|
+
for key, wire in (("project_scope", "projectScope"), ("user_scope", "userScope")):
|
|
99
|
+
_boolean(data, key, "options.memory")
|
|
100
|
+
if key in data:
|
|
101
|
+
config[wire] = data[key]
|
|
102
|
+
if (
|
|
103
|
+
data["mode"] == "native"
|
|
104
|
+
and data.get("project_scope") is False
|
|
105
|
+
and data.get("user_scope") is False
|
|
106
|
+
):
|
|
107
|
+
raise TypeError(
|
|
108
|
+
"native memory requires at least one enabled scope; use memory={} to disable"
|
|
109
|
+
)
|
|
110
|
+
for section in ("generation", "consumption"):
|
|
111
|
+
if section not in data:
|
|
112
|
+
continue
|
|
113
|
+
name = f"options.memory.{section}"
|
|
114
|
+
options = _object(data[section], name)
|
|
115
|
+
_callback(options, "on_result", name)
|
|
116
|
+
if section == "generation":
|
|
117
|
+
prepared.on_generation = options.get("on_result")
|
|
118
|
+
else:
|
|
119
|
+
prepared.on_consumption = options.get("on_result")
|
|
120
|
+
if data["mode"] == "native":
|
|
121
|
+
if options.keys() - {"on_result"}:
|
|
122
|
+
raise TypeError(
|
|
123
|
+
"native mode only accepts generation.on_result and consumption.on_result"
|
|
124
|
+
)
|
|
125
|
+
continue
|
|
126
|
+
_boolean(options, "enabled", name)
|
|
127
|
+
output: dict[str, Any] = {"enabled": options.get("enabled", True)}
|
|
128
|
+
if section == "generation":
|
|
129
|
+
if "roots" in options:
|
|
130
|
+
output["roots"] = _files(options["roots"], f"{name}.roots", roots=True)
|
|
131
|
+
if "prompt" in options:
|
|
132
|
+
_string(options["prompt"], f"{name}.prompt")
|
|
133
|
+
output["prompt"] = options["prompt"]
|
|
134
|
+
if "turn_complete" in options:
|
|
135
|
+
turn = _object(options["turn_complete"], f"{name}.turn_complete")
|
|
136
|
+
label = f"{name}.turn_complete"
|
|
137
|
+
_boolean(turn, "enabled", label)
|
|
138
|
+
_callback(turn, "should_generate", label)
|
|
139
|
+
_choice(turn, "on_gate_error", ("skip", "report_failed"), label)
|
|
140
|
+
tc: dict[str, Any] = {"enabled": turn.get("enabled", True)}
|
|
141
|
+
if "timeout_ms" in turn:
|
|
142
|
+
timeout = turn["timeout_ms"]
|
|
143
|
+
if (
|
|
144
|
+
isinstance(timeout, bool)
|
|
145
|
+
or not isinstance(timeout, (int, float))
|
|
146
|
+
or not math.isfinite(timeout)
|
|
147
|
+
or timeout <= 0
|
|
148
|
+
):
|
|
149
|
+
raise TypeError(
|
|
150
|
+
f"{label}.timeout_ms must be a positive finite number"
|
|
151
|
+
)
|
|
152
|
+
tc["timeoutMs"] = timeout
|
|
153
|
+
if "on_gate_error" in turn:
|
|
154
|
+
tc["onGateError"] = turn["on_gate_error"]
|
|
155
|
+
if "should_generate" in turn:
|
|
156
|
+
callback_id = "memory_should_generate_0"
|
|
157
|
+
prepared.gates[callback_id] = turn["should_generate"]
|
|
158
|
+
tc["shouldGenerateCallbackId"] = callback_id
|
|
159
|
+
if turn.get("enabled") is True and options.get("enabled") is False:
|
|
160
|
+
raise TypeError(
|
|
161
|
+
"turn_complete cannot be enabled when generation.enabled is false"
|
|
162
|
+
)
|
|
163
|
+
output["turnComplete"] = tc
|
|
164
|
+
else:
|
|
165
|
+
if "files" in options:
|
|
166
|
+
output["files"] = _files(options["files"], f"{name}.files", roots=False)
|
|
167
|
+
if "max_tokens" in options:
|
|
168
|
+
budget = options["max_tokens"]
|
|
169
|
+
if (
|
|
170
|
+
isinstance(budget, bool)
|
|
171
|
+
or not isinstance(budget, int)
|
|
172
|
+
or budget <= 0
|
|
173
|
+
):
|
|
174
|
+
raise TypeError(f"{name}.max_tokens must be a positive integer")
|
|
175
|
+
output["maxTokens"] = budget
|
|
176
|
+
_choice(options, "overflow", ("truncate", "fail_query"), name)
|
|
177
|
+
_choice(options, "failure_mode", ("best_effort", "fail_query"), name)
|
|
178
|
+
for key, wire in (
|
|
179
|
+
("overflow", "overflow"),
|
|
180
|
+
("failure_mode", "failureMode"),
|
|
181
|
+
):
|
|
182
|
+
if key in options:
|
|
183
|
+
output[wire] = options[key]
|
|
184
|
+
config[section] = output
|
|
185
|
+
prepared.config = config
|
|
186
|
+
return prepared
|
{qoder_agent_sdk-1.0.14 → qoder_agent_sdk-1.0.16}/src/qoder_agent_sdk/_internal/message_parser.py
RENAMED
|
@@ -7,10 +7,13 @@ from .._errors import MessageParseError
|
|
|
7
7
|
from ..types import (
|
|
8
8
|
AssistantMessage,
|
|
9
9
|
AssistantMessageWithRequestId,
|
|
10
|
+
BackgroundTasksChangedMessage,
|
|
10
11
|
CloudAgentEventMessage,
|
|
11
12
|
ContentBlock,
|
|
12
13
|
GoalClearedMessage,
|
|
13
14
|
GoalUpdatedMessage,
|
|
15
|
+
MemoryConsumptionMessage,
|
|
16
|
+
MemoryGenerationMessage,
|
|
14
17
|
Message,
|
|
15
18
|
PlanModeChangedMessage,
|
|
16
19
|
ResultMessage,
|
|
@@ -22,6 +25,7 @@ from ..types import (
|
|
|
22
25
|
TaskNotificationMessage,
|
|
23
26
|
TaskProgressMessage,
|
|
24
27
|
TaskStartedMessage,
|
|
28
|
+
TaskUpdatedMessage,
|
|
25
29
|
TextBlock,
|
|
26
30
|
ThinkingBlock,
|
|
27
31
|
ToolResultBlock,
|
|
@@ -166,6 +170,14 @@ def parse_message(data: dict[str, Any]) -> Message | None:
|
|
|
166
170
|
try:
|
|
167
171
|
subtype = data["subtype"]
|
|
168
172
|
match subtype:
|
|
173
|
+
case "memory_generation":
|
|
174
|
+
return MemoryGenerationMessage(
|
|
175
|
+
subtype=subtype, data=data, result=data["result"]
|
|
176
|
+
)
|
|
177
|
+
case "memory_consumption":
|
|
178
|
+
return MemoryConsumptionMessage(
|
|
179
|
+
subtype=subtype, data=data, result=data["result"]
|
|
180
|
+
)
|
|
169
181
|
case "permission_denied":
|
|
170
182
|
return SDKPermissionDeniedMessage(
|
|
171
183
|
subtype=subtype,
|
|
@@ -189,6 +201,10 @@ def parse_message(data: dict[str, Any]) -> Message | None:
|
|
|
189
201
|
session_id=data["session_id"],
|
|
190
202
|
tool_use_id=data.get("tool_use_id"),
|
|
191
203
|
task_type=data.get("task_type"),
|
|
204
|
+
subagent_type=data.get("subagent_type"),
|
|
205
|
+
workflow_name=data.get("workflow_name"),
|
|
206
|
+
prompt=data.get("prompt"),
|
|
207
|
+
tools=data.get("tools"),
|
|
192
208
|
)
|
|
193
209
|
case "task_progress":
|
|
194
210
|
return TaskProgressMessage(
|
|
@@ -196,18 +212,13 @@ def parse_message(data: dict[str, Any]) -> Message | None:
|
|
|
196
212
|
data=data,
|
|
197
213
|
task_id=data["task_id"],
|
|
198
214
|
description=data["description"],
|
|
199
|
-
usage=data.get(
|
|
200
|
-
"usage",
|
|
201
|
-
{
|
|
202
|
-
"total_tokens": 0,
|
|
203
|
-
"tool_uses": 0,
|
|
204
|
-
"duration_ms": 0,
|
|
205
|
-
},
|
|
206
|
-
),
|
|
215
|
+
usage=data.get("usage"),
|
|
207
216
|
uuid=data["uuid"],
|
|
208
217
|
session_id=data["session_id"],
|
|
209
218
|
tool_use_id=data.get("tool_use_id"),
|
|
210
219
|
last_tool_name=data.get("last_tool_name"),
|
|
220
|
+
subagent_type=data.get("subagent_type"),
|
|
221
|
+
summary=data.get("summary"),
|
|
211
222
|
)
|
|
212
223
|
case "task_notification":
|
|
213
224
|
return TaskNotificationMessage(
|
|
@@ -222,6 +233,23 @@ def parse_message(data: dict[str, Any]) -> Message | None:
|
|
|
222
233
|
tool_use_id=data.get("tool_use_id"),
|
|
223
234
|
usage=data.get("usage"),
|
|
224
235
|
)
|
|
236
|
+
case "task_updated":
|
|
237
|
+
return TaskUpdatedMessage(
|
|
238
|
+
subtype=subtype,
|
|
239
|
+
data=data,
|
|
240
|
+
task_id=data["task_id"],
|
|
241
|
+
patch=data["patch"],
|
|
242
|
+
uuid=data["uuid"],
|
|
243
|
+
session_id=data["session_id"],
|
|
244
|
+
)
|
|
245
|
+
case "background_tasks_changed":
|
|
246
|
+
return BackgroundTasksChangedMessage(
|
|
247
|
+
subtype=subtype,
|
|
248
|
+
data=data,
|
|
249
|
+
tasks=data["tasks"],
|
|
250
|
+
uuid=data["uuid"],
|
|
251
|
+
session_id=data["session_id"],
|
|
252
|
+
)
|
|
225
253
|
case "goal_updated":
|
|
226
254
|
return GoalUpdatedMessage(
|
|
227
255
|
subtype=subtype,
|