keble-task 2.22.0__py3-none-any.whl

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.
keble_task/__init__.py ADDED
@@ -0,0 +1,143 @@
1
+ from .crud import CRUDTask, CRUDTaskCost, CRUDTaskRelation
2
+ from .exceptions import (
3
+ TaskException,
4
+ TaskExceptionType,
5
+ TaskFailedToStartException,
6
+ TaskNoSufficientDataException,
7
+ )
8
+ from .actions import (
9
+ CreateRelatedTaskAction,
10
+ CreateRelatedTaskActionedResult,
11
+ TaskAction,
12
+ TaskActionCreatedRelation,
13
+ TaskActionCreatedTask,
14
+ TaskActionEvent,
15
+ TaskActionType,
16
+ TaskActionedResult,
17
+ TaskActionedResults,
18
+ TaskActions,
19
+ TaskEventType,
20
+ TaskLifecycleEvent,
21
+ TaskLifecycleEventPayload,
22
+ TaskUxContext,
23
+ )
24
+ from .main import (
25
+ TaskClient,
26
+ TaskHandlerRequest,
27
+ TaskHandlerResponse,
28
+ TokenConsumptionPayload,
29
+ TokenConsumptionType,
30
+ )
31
+ from .agent import (
32
+ TaskAgentContext,
33
+ TaskAgentDeps,
34
+ TaskAgentMutationToolsConfig,
35
+ TaskAgentQueryToolsConfig,
36
+ TaskQueryChatToolProvider,
37
+ TaskSummaryForAgent,
38
+ register_mutation_tools,
39
+ register_query_tools,
40
+ )
41
+ from .schemas import (
42
+ TaskBase,
43
+ TaskCostAggregateBucket,
44
+ TaskCostAggregateGroupBy,
45
+ TaskCostAggregateRequest,
46
+ TaskCostAggregateResponse,
47
+ TaskCostBase,
48
+ TaskCostCreate,
49
+ TaskCostFilterBase,
50
+ TaskCostListRequest,
51
+ TaskCostListResponse,
52
+ TaskCostMetadata,
53
+ TaskCostMongoObject,
54
+ TaskCostTimeBucket,
55
+ TaskCostTokenRates,
56
+ TaskMongoObject,
57
+ TaskMongoObjectExtended,
58
+ TaskPublicRef,
59
+ TaskRelationMetadata,
60
+ TaskRoomGraphContext,
61
+ TaskRoomGraphRelation,
62
+ TaskRoomGraphTask,
63
+ TaskRoomResolution,
64
+ TaskRelationBase,
65
+ TaskRelationCreate,
66
+ TaskRelationMongoObject,
67
+ TaskRelationType,
68
+ TaskRelationUpdate,
69
+ TaskStage,
70
+ TaskUpdate,
71
+ build_task_cost_metadata,
72
+ build_task_relation_metadata,
73
+ )
74
+ from .utils import Difficulty
75
+
76
+ __all__ = [
77
+ "CRUDTask",
78
+ "CRUDTaskCost",
79
+ "CRUDTaskRelation",
80
+ "Difficulty",
81
+ "TaskBase",
82
+ "TaskCostAggregateBucket",
83
+ "TaskCostAggregateGroupBy",
84
+ "TaskCostAggregateRequest",
85
+ "TaskCostAggregateResponse",
86
+ "TaskCostBase",
87
+ "TaskCostCreate",
88
+ "TaskCostFilterBase",
89
+ "TaskCostListRequest",
90
+ "TaskCostListResponse",
91
+ "TaskCostMetadata",
92
+ "TaskCostMongoObject",
93
+ "TaskCostTimeBucket",
94
+ "TaskCostTokenRates",
95
+ "CreateRelatedTaskAction",
96
+ "CreateRelatedTaskActionedResult",
97
+ "TaskClient",
98
+ "TaskException",
99
+ "TaskExceptionType",
100
+ "TaskFailedToStartException",
101
+ "TaskHandlerRequest",
102
+ "TaskHandlerResponse",
103
+ "TaskMongoObject",
104
+ "TaskMongoObjectExtended",
105
+ "TaskPublicRef",
106
+ "TaskNoSufficientDataException",
107
+ "TaskAction",
108
+ "TaskActionCreatedRelation",
109
+ "TaskActionCreatedTask",
110
+ "TaskAgentContext",
111
+ "TaskAgentDeps",
112
+ "TaskActionEvent",
113
+ "TaskActionType",
114
+ "TaskActionedResult",
115
+ "TaskActionedResults",
116
+ "TaskActions",
117
+ "TaskEventType",
118
+ "TaskLifecycleEvent",
119
+ "TaskLifecycleEventPayload",
120
+ "TaskAgentMutationToolsConfig",
121
+ "TaskAgentQueryToolsConfig",
122
+ "TaskRelationBase",
123
+ "TaskRelationCreate",
124
+ "TaskRelationMetadata",
125
+ "TaskRelationMongoObject",
126
+ "TaskRelationType",
127
+ "TaskRoomGraphContext",
128
+ "TaskRoomGraphRelation",
129
+ "TaskRoomGraphTask",
130
+ "TaskRoomResolution",
131
+ "TaskRelationUpdate",
132
+ "TaskStage",
133
+ "TaskQueryChatToolProvider",
134
+ "TaskSummaryForAgent",
135
+ "TaskUpdate",
136
+ "TaskUxContext",
137
+ "TokenConsumptionPayload",
138
+ "TokenConsumptionType",
139
+ "build_task_cost_metadata",
140
+ "build_task_relation_metadata",
141
+ "register_mutation_tools",
142
+ "register_query_tools",
143
+ ]
keble_task/actions.py ADDED
@@ -0,0 +1,304 @@
1
+ """Generic task action schemas owned by `keble-task`.
2
+
3
+ The task package owns task creation, parent/root validation, relation rows,
4
+ terminal completion, and action events. Feature packages must expose their own
5
+ typed tools for feature-specific side effects.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from enum import Enum
11
+ from typing import Literal
12
+
13
+ from keble_helpers import (
14
+ AgenticActionEvent,
15
+ AgenticActionEventSource,
16
+ AgenticActionEventStatus,
17
+ AgenticActionPayload,
18
+ AgenticActionWarningLevel,
19
+ ObjectId,
20
+ PydanticModelConfig,
21
+ SharingScope,
22
+ )
23
+ from pydantic import BaseModel, Field, field_validator
24
+
25
+ from .schemas import (
26
+ TaskRelationMetadata,
27
+ TaskMongoObject,
28
+ TaskRelationMongoObject,
29
+ TaskRelationType,
30
+ TaskStage,
31
+ build_task_relation_metadata,
32
+ )
33
+
34
+ TASK_ACTION_EVENT_SOURCE = AgenticActionEventSource.KEBLE_TASK
35
+
36
+
37
+ class TaskActionType(str, Enum):
38
+ """Canonical action commands owned by `keble-task`."""
39
+
40
+ CREATE_RELATED_TASK = "CREATE_RELATED_TASK"
41
+
42
+
43
+ class TaskEventType(str, Enum):
44
+ """Canonical non-action task lifecycle events owned by `keble-task`."""
45
+
46
+ TASK_STAGE_CHANGED = "TASK_STAGE_CHANGED"
47
+
48
+
49
+ class TaskUxContext(BaseModel):
50
+ """Optional UI/task selection context for one task action batch.
51
+
52
+ Step by step:
53
+ 1. `selected_root_task_id` lets callers prove the visible root workspace;
54
+ 2. `selected_task_ids` captures extra user-selected task ids for future action
55
+ types without changing the canonical task model.
56
+ """
57
+
58
+ model_config = PydanticModelConfig.default()
59
+
60
+ selected_root_task_id: ObjectId | None = None
61
+ selected_task_ids: list[ObjectId] = Field(default_factory=list)
62
+
63
+
64
+ class CreateRelatedTaskAction(AgenticActionPayload):
65
+ """Create one child task and optional relation edges.
66
+
67
+ This action IS a single `AgenticActionPayload`: it carries its own
68
+ confirmation `message` and `warning_level`, so one approval card maps to
69
+ exactly one action. An agent that wants several related tasks emits several
70
+ `mutate_task_workspace` tool calls (the deferred batch holds them and the
71
+ frontend resolves one card per click) — it must NEVER be re-wrapped in a
72
+ `actions: list[...]` payload that inherits the single-action contract.
73
+
74
+ Step by step:
75
+ 1. create a task under `parent_task_id` or the current task by default;
76
+ 2. create relation rows from `from_task_ids` or the current task by default;
77
+ 3. optionally mark the created task successful for lightweight generic work.
78
+
79
+ Side effect if changes:
80
+ - `keble_task.agent.registry.mutate_task_workspace` binds this as the single
81
+ tool payload; `TaskClient.aapply_action` executes one of these.
82
+ - reused as the element type of the plain REST batch `TaskActions`.
83
+ """
84
+
85
+ model_config = PydanticModelConfig.default(extra="forbid")
86
+
87
+ # Defaulted approval copy (not required) so REST/programmatic callers that
88
+ # build this inside the plain `TaskActions` batch need not author per-action
89
+ # confirmation text; an agent may override `message` for a friendlier card.
90
+ message: str = Field(
91
+ default="Create a related task.",
92
+ min_length=1,
93
+ description="Human-friendly confirmation reason for this single action.",
94
+ )
95
+ warning_level: AgenticActionWarningLevel = AgenticActionWarningLevel.MUTATION
96
+
97
+ action_type: Literal[TaskActionType.CREATE_RELATED_TASK] = (
98
+ TaskActionType.CREATE_RELATED_TASK
99
+ )
100
+ relation_type: TaskRelationType
101
+ task_type: str | None = None
102
+ expected_token: int = Field(default=0, ge=0)
103
+ progress_key: str | None = None
104
+ image: str | None = None
105
+ title: str | None = None
106
+ subtitle: str | None = None
107
+ metadata: TaskRelationMetadata = Field(default_factory=dict)
108
+ timeout_mins: int = Field(default=120, ge=1)
109
+ sharing_scope: SharingScope | None = None
110
+ parent_task_id: ObjectId | None = Field(
111
+ default=None,
112
+ description=(
113
+ "Leave unset to attach the new task under the current task (the default). "
114
+ "Only set this to a real task id explicitly provided in the conversation or "
115
+ "context — never invent an id."
116
+ ),
117
+ )
118
+ from_task_ids: list[ObjectId] = Field(
119
+ default_factory=list,
120
+ description=(
121
+ "Leave empty to draw the relation from the current task (the default). Only "
122
+ "include real task ids explicitly provided in context — never invent ids."
123
+ ),
124
+ )
125
+ complete_created_task: bool = False
126
+ consuming_token: int = Field(default=0, ge=0)
127
+
128
+ @field_validator("metadata", mode="before")
129
+ @classmethod
130
+ def _normalize_metadata(cls, value: object) -> TaskRelationMetadata:
131
+ """Normalize action metadata before it is reused for tasks and relations."""
132
+
133
+ return build_task_relation_metadata(value)
134
+
135
+
136
+ TaskAction = CreateRelatedTaskAction
137
+
138
+
139
+ class TaskActionCreatedTask(BaseModel):
140
+ """Slim public payload for a task created by a task action.
141
+
142
+ Step by step:
143
+ 1. expose stable identifiers and display/status fields needed by tools;
144
+ 2. omit Mongo-only fields such as metadata, owner, and timestamps;
145
+ 3. keep `ObjectId` annotations so `model_dump(mode="json")` emits string ids.
146
+ """
147
+
148
+ model_config = PydanticModelConfig.default()
149
+
150
+ task_id: ObjectId
151
+ root_task_id: ObjectId | None = None
152
+ parent_task_id: ObjectId | None = None
153
+ task_type: str
154
+ stage: TaskStage
155
+ title: str | None = None
156
+ progress_key: str | None = None
157
+
158
+ @classmethod
159
+ def build(cls, *, task: TaskMongoObject) -> "TaskActionCreatedTask":
160
+ """Convert one internal Mongo task row into the slim action payload."""
161
+
162
+ return cls(
163
+ task_id=task.id,
164
+ root_task_id=task.root_task,
165
+ parent_task_id=task.parent_task,
166
+ task_type=task.task_type,
167
+ stage=task.stage,
168
+ title=task.title,
169
+ progress_key=task.progress_key,
170
+ )
171
+
172
+
173
+ class TaskActionCreatedRelation(BaseModel):
174
+ """Slim public payload for a relation created by a task action.
175
+
176
+ Step by step:
177
+ 1. expose the relation id, root id, endpoints, relation type, and metadata;
178
+ 2. omit Mongo timestamps while preserving public relation metadata;
179
+ 3. keep `ObjectId` annotations for JSON-mode string serialization.
180
+ """
181
+
182
+ model_config = PydanticModelConfig.default()
183
+
184
+ relation_id: ObjectId
185
+ root_task_id: ObjectId
186
+ from_task_id: ObjectId
187
+ to_task_id: ObjectId
188
+ relation_type: TaskRelationType
189
+ metadata: TaskRelationMetadata = Field(default_factory=dict)
190
+
191
+ @field_validator("metadata", mode="before")
192
+ @classmethod
193
+ def _normalize_metadata(cls, value: object) -> TaskRelationMetadata:
194
+ """Normalize relation event metadata before public JSON-mode dumps."""
195
+
196
+ return build_task_relation_metadata(value)
197
+
198
+ @classmethod
199
+ def build(cls, *, relation: TaskRelationMongoObject) -> "TaskActionCreatedRelation":
200
+ """Convert one internal Mongo relation row into the slim action payload.
201
+
202
+ Step by step:
203
+ 1. copy the stable relation ids and type;
204
+ 2. carry the persisted relation metadata through the public event DTO;
205
+ 3. leave persistence-only timestamps on the Mongo object.
206
+ """
207
+
208
+ return cls(
209
+ relation_id=relation.id,
210
+ root_task_id=relation.root_task,
211
+ from_task_id=relation.from_task_id,
212
+ to_task_id=relation.to_task_id,
213
+ relation_type=relation.relation_type,
214
+ metadata=relation.metadata,
215
+ )
216
+
217
+
218
+ class TaskActions(BaseModel):
219
+ """Plain ordered batch of canonical task actions for the REST/programmatic seam.
220
+
221
+ This is NOT an `AgenticActionPayload`: a batch cannot honestly carry a single
222
+ approval `message`/`warning_level`. Each element is itself a single
223
+ `AgenticActionPayload` action. The agent surface is one-action-per-tool-call
224
+ (`mutate_task_workspace` + `aapply_action`); only the REST endpoint and
225
+ programmatic callers use this container via `aapply_actions`.
226
+
227
+ Side effect if changes:
228
+ - `keble.backend` `app/api/endpoints/v3/features/tasks.py` REST body.
229
+ - `TaskClient.aapply_actions` iterates `actions` in order.
230
+ """
231
+
232
+ model_config = PydanticModelConfig.default(extra="forbid")
233
+
234
+ actions: list[TaskAction] = Field(default_factory=list)
235
+
236
+
237
+ class CreateRelatedTaskActionedResult(BaseModel):
238
+ """Result for one `CREATE_RELATED_TASK` action using tool-safe DTOs."""
239
+
240
+ model_config = PydanticModelConfig.default()
241
+
242
+ action_type: Literal[TaskActionType.CREATE_RELATED_TASK] = (
243
+ TaskActionType.CREATE_RELATED_TASK
244
+ )
245
+ created_task: TaskActionCreatedTask
246
+ created_relations: list[TaskActionCreatedRelation] = Field(default_factory=list)
247
+
248
+
249
+ TaskActionedResult = CreateRelatedTaskActionedResult
250
+
251
+
252
+ class TaskActionEvent(AgenticActionEvent[TaskActionedResult]):
253
+ """Task action event envelope backed by the shared helper event protocol."""
254
+
255
+ source: AgenticActionEventSource = TASK_ACTION_EVENT_SOURCE
256
+ status: AgenticActionEventStatus = AgenticActionEventStatus.SUCCEEDED
257
+
258
+
259
+ class TaskLifecycleEventPayload(BaseModel):
260
+ """Payload for one persisted task lifecycle transition.
261
+
262
+ Step by step:
263
+ 1. carry the reloaded persisted task after the stage mutation is committed;
264
+ 2. let consumers use the same task schema as task read APIs;
265
+ 3. keep lifecycle events separate from task action result payloads.
266
+ """
267
+
268
+ model_config = PydanticModelConfig.default()
269
+
270
+ task: TaskMongoObject
271
+
272
+
273
+ class TaskLifecycleEvent(AgenticActionEvent[TaskLifecycleEventPayload]):
274
+ """Task stage-change event envelope emitted by `TaskClient` lifecycle APIs."""
275
+
276
+ source: AgenticActionEventSource = TASK_ACTION_EVENT_SOURCE
277
+
278
+
279
+ class TaskActionedResults(BaseModel):
280
+ """Batch result returned by canonical task action execution."""
281
+
282
+ model_config = PydanticModelConfig.default()
283
+
284
+ actioned_results: list[TaskActionedResult] = Field(default_factory=list)
285
+ events: list[TaskActionEvent] = Field(default_factory=list)
286
+
287
+
288
+ __all__ = [
289
+ "TASK_ACTION_EVENT_SOURCE",
290
+ "CreateRelatedTaskAction",
291
+ "CreateRelatedTaskActionedResult",
292
+ "TaskActionCreatedRelation",
293
+ "TaskActionCreatedTask",
294
+ "TaskAction",
295
+ "TaskActionEvent",
296
+ "TaskActionType",
297
+ "TaskActionedResult",
298
+ "TaskActionedResults",
299
+ "TaskActions",
300
+ "TaskEventType",
301
+ "TaskLifecycleEvent",
302
+ "TaskLifecycleEventPayload",
303
+ "TaskUxContext",
304
+ ]
@@ -0,0 +1,27 @@
1
+ """Public agent-runtime exports for task-owned tool registration.
2
+
3
+ Agent tool I/O schemas + configs are re-exported from
4
+ ``keble_task.schemas.for_agent`` for convenience; the registrars live in
5
+ ``keble_task.agent.tools`` (symmetric ``mutation``/``query``).
6
+ """
7
+
8
+ from keble_task.schemas.for_agent import (
9
+ TaskAgentMutationToolsConfig,
10
+ TaskAgentQueryToolsConfig,
11
+ TaskSummaryForAgent,
12
+ )
13
+
14
+ from .chat_provider import TaskQueryChatToolProvider
15
+ from .deps import TaskAgentContext, TaskAgentDeps
16
+ from .tools import register_mutation_tools, register_query_tools
17
+
18
+ __all__ = [
19
+ "TaskAgentContext",
20
+ "TaskAgentDeps",
21
+ "TaskAgentMutationToolsConfig",
22
+ "TaskAgentQueryToolsConfig",
23
+ "TaskQueryChatToolProvider",
24
+ "TaskSummaryForAgent",
25
+ "register_mutation_tools",
26
+ "register_query_tools",
27
+ ]
@@ -0,0 +1,117 @@
1
+ """Native chat tool provider for task-owned READ/QUERY tools.
2
+
3
+ keble.backend used to wrap ``register_query_tools`` in a generic backend-side
4
+ adapter just to satisfy ``keble_helpers.ChatToolProviderProtocol``. This module
5
+ ships the provider natively from the package that OWNS the tools, so any
6
+ agentic-chat host can compose the task query tools declaratively (via
7
+ ``compose_tool_providers``-style assembly) without writing its own wrapper.
8
+
9
+ The provider captures its dependencies (``TaskClient`` + optional tool config)
10
+ at construction time; the protocol's ``register(*, agent)`` carries no per-request
11
+ context (deps are construction-captured).
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ from typing import TYPE_CHECKING, Any, cast
17
+
18
+ from keble_task.main import TaskClient
19
+
20
+ from keble_helpers import (
21
+ ChatToolKind,
22
+ ChatToolProviderId,
23
+ ChatToolProviderManifest,
24
+ AgentToolDescriptor,
25
+ )
26
+
27
+ from keble_task.schemas.for_agent import TaskAgentQueryToolsConfig
28
+
29
+ from .tools import register_query_tools
30
+
31
+ if TYPE_CHECKING:
32
+ from keble_helpers import ChatToolProviderProtocol
33
+
34
+ __all__ = ["TaskQueryChatToolProvider"]
35
+
36
+
37
+ class TaskQueryChatToolProvider:
38
+ """Native ``ChatToolProviderProtocol`` provider for owner-scoped task query tools.
39
+
40
+ One instance contributes the task-owned, non-mutating ``list_tasks`` /
41
+ ``get_task`` tools to a single chat-scope agent build. It satisfies the
42
+ framework-neutral ``keble_helpers.ChatToolProviderProtocol``
43
+ (``provider_id`` + ``manifest`` + ``register(*, agent)``) so hosts no longer
44
+ need a generic adapter around ``register_query_tools``.
45
+ """
46
+
47
+ def __init__(
48
+ self,
49
+ *,
50
+ task_client: TaskClient,
51
+ tools_config: TaskAgentQueryToolsConfig | dict[str, Any] | None = None,
52
+ ) -> None:
53
+ """Capture the registration dependencies at construction.
54
+
55
+ Step by step:
56
+ 1. store the ``TaskClient`` that backs the owner-scoped task reads;
57
+ 2. store the optional per-tool name/description override config
58
+ (validated later by ``TaskAgentQueryToolsConfig.build`` inside the
59
+ registrar, so dict payloads are accepted as-is here).
60
+ """
61
+
62
+ self._task_client = task_client
63
+ self._tools_config = tools_config
64
+
65
+ @property
66
+ def provider_id(self) -> ChatToolProviderId:
67
+ """Stable provider id recorded in room diagnostics.
68
+
69
+ The ``ChatToolProviderId.TASK_QUERY`` member's wire value
70
+ (``"task_query"``) is what the frontend maps to a user-readable label
71
+ and rooms persist in diagnostics.
72
+ """
73
+
74
+ return ChatToolProviderId.TASK_QUERY
75
+
76
+ def register(self, *, agent: Any) -> None:
77
+ """Attach the task query tools to ``agent`` for one scope build.
78
+
79
+ Run the canonical ``register_query_tools`` registrar with the
80
+ construction-captured client and config, mounting ``list_tasks`` /
81
+ ``get_task`` on the agent's tool registry.
82
+ """
83
+
84
+ register_query_tools(
85
+ agent,
86
+ task_client=self._task_client,
87
+ tools_config=self._tools_config,
88
+ )
89
+
90
+ @property
91
+ def manifest(self) -> ChatToolProviderManifest:
92
+ """Declared read-tool inventory (program Phase 6)."""
93
+
94
+ config = TaskAgentQueryToolsConfig.build(self._tools_config)
95
+ return ChatToolProviderManifest(
96
+ provider_id=self.provider_id,
97
+ tools=[
98
+ AgentToolDescriptor(
99
+ name=config.list_tasks.name or "list_tasks",
100
+ kind=ChatToolKind.QUERY,
101
+ summary="List the owner's recent tasks (filterable by type).",
102
+ ),
103
+ AgentToolDescriptor(
104
+ name=config.get_task.name or "get_task",
105
+ kind=ChatToolKind.QUERY,
106
+ summary="Read one task by id.",
107
+ ),
108
+ ],
109
+ )
110
+
111
+
112
+ if TYPE_CHECKING:
113
+ # Static structural conformance to the cross-repo standard (no runtime cost).
114
+ _conformance: ChatToolProviderProtocol = TaskQueryChatToolProvider(
115
+ task_client=cast(TaskClient, object()),
116
+ )
117
+ _ = _conformance
@@ -0,0 +1,38 @@
1
+ """Typed pydantic-ai deps contract for task-owned agent tools."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from keble_db import AgentDbDeps
6
+ from keble_helpers import AgenticEventEmitter, PydanticModelConfig
7
+ from pydantic import BaseModel, Field, SkipValidation
8
+
9
+ from keble_task.actions import TaskUxContext
10
+ from keble_task.schemas import TaskMongoObject
11
+
12
+
13
+ class TaskAgentContext(BaseModel):
14
+ """Task-owned runtime context nested under database-wide deps.
15
+
16
+ Step by step this context gives package registrars the runtime pieces that
17
+ cannot live in `AgentDbDeps`:
18
+ 1. `current_task` anchors generic task actions;
19
+ 2. `ux_context` optionally proves the visible task workspace;
20
+ 3. `event_emitter` forwards task action events when the parent runtime has one.
21
+ """
22
+
23
+ model_config = PydanticModelConfig.default(arbitrary_types_allowed=True)
24
+
25
+ current_task: TaskMongoObject
26
+ ux_context: TaskUxContext | None = None
27
+ event_emitter: SkipValidation[AgenticEventEmitter | None] = Field(default=None)
28
+
29
+
30
+ class TaskAgentDeps(AgentDbDeps):
31
+ """Database-wide deps plus task-owned runtime state."""
32
+
33
+ model_config = PydanticModelConfig.default(arbitrary_types_allowed=True)
34
+
35
+ task: TaskAgentContext
36
+
37
+
38
+ __all__ = ["TaskAgentContext", "TaskAgentDeps"]
@@ -0,0 +1,14 @@
1
+ """Task agent tool registrars (behavior only — symmetric mutation/query).
2
+
3
+ Schemas for these tools live in ``keble_task.schemas.for_agent`` (agent tool I/O)
4
+ and the external action module ``keble_task.actions`` (mutation payloads). These
5
+ modules contain ONLY registrars — no contract classes.
6
+ """
7
+
8
+ from .mutation import register_mutation_tools
9
+ from .query import register_query_tools
10
+
11
+ __all__ = [
12
+ "register_mutation_tools",
13
+ "register_query_tools",
14
+ ]