soothe-client-python 0.9.4__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.
@@ -0,0 +1,604 @@
1
+ """Client-side protocol-1 params validation models (RFC-450 §6.5).
2
+
3
+ These Pydantic models mirror the daemon's PARAMS_REGISTRY and allow clients
4
+ to validate params before sending, catching errors early and reducing
5
+ daemon-side error round-trips.
6
+
7
+ Design notes
8
+ ------------
9
+ - All models allow extra fields (``model_config = {"extra": "allow"}``) for
10
+ forward compatibility.
11
+ - Required string identifiers (``loop_id``, ``skill``, ``cmd``, ``content``)
12
+ use ``min_length=1`` to catch empty strings client-side.
13
+ - Optional fields are permissive — the daemon is the authority on domain
14
+ semantics (e.g. whether ``autonomous`` is honoured for a specific method).
15
+
16
+ Public API
17
+ ----------
18
+ Each model is named after the method it validates (e.g. ``LoopGetParams``,
19
+ ``LoopInputParams``, ``SubscribeParams``). Use with the ``WebSocketClient``
20
+ request/notify/subscribe methods to validate params before sending:
21
+
22
+ params = LoopInputParams(loop_id="abc123", content="Hello")
23
+ await client.notify("loop_input", params.model_dump())
24
+ """
25
+
26
+ from __future__ import annotations
27
+
28
+ from typing import Any, Literal
29
+
30
+ from pydantic import BaseModel, Field
31
+
32
+ __all__ = [
33
+ # Loop RPC params
34
+ "LoopGetParams",
35
+ "LoopListParams",
36
+ "LoopTreeParams",
37
+ "LoopPruneParams",
38
+ "LoopDeleteParams",
39
+ "LoopNewParams",
40
+ "LoopReattachParams",
41
+ "LoopInputParams",
42
+ "LoopMessagesParams",
43
+ "LoopStateGetParams",
44
+ "LoopStateUpdateParams",
45
+ "LoopCardsFetchParams",
46
+ "LoopDetachParams",
47
+ # Subscription params
48
+ "SubscribeParams",
49
+ "AutopilotSubscribeParams",
50
+ # Job RPC params
51
+ "JobCreateParams",
52
+ "JobStatusParams",
53
+ "JobPauseParams",
54
+ "JobResumeParams",
55
+ "JobCancelParams",
56
+ "JobDagParams",
57
+ "JobGuidanceParams",
58
+ # Cron RPC params
59
+ "CronAddParams",
60
+ "CronListParams",
61
+ "CronShowParams",
62
+ "CronCancelParams",
63
+ # Daemon & config params
64
+ "DaemonStatusParams",
65
+ "DaemonShutdownParams",
66
+ "ConfigGetParams",
67
+ "ConfigReloadParams",
68
+ # Skills & models params
69
+ "SkillsListParams",
70
+ "ModelsListParams",
71
+ "InvokeSkillParams",
72
+ "McpStatusParams",
73
+ # Auth params
74
+ "AuthParams",
75
+ "AuthRefreshParams",
76
+ # Command params
77
+ "SlashCommandParams",
78
+ "RpcCommandParams",
79
+ # Connection params
80
+ "ConnectionInitParams", # re-exported from wire.py
81
+ "DisconnectParams",
82
+ ]
83
+
84
+
85
+ class ParamsBase(BaseModel):
86
+ """Base for all client-side param models — allows extra fields for forward compat.
87
+
88
+ The protocol envelope carries ``proto``, ``type``, ``method``, and ``id``
89
+ alongside the operation-specific fields. All models validate against the
90
+ ``params`` dict so extra keys must be tolerated.
91
+ """
92
+
93
+ model_config = {"extra": "allow"}
94
+
95
+
96
+ class EmptyParams(ParamsBase):
97
+ """Params model for methods that carry no required fields."""
98
+
99
+
100
+ # ---------------------------------------------------------------------------
101
+ # Loop RPC params (RFC-450 §9.2)
102
+ # ---------------------------------------------------------------------------
103
+
104
+
105
+ class LoopGetParams(ParamsBase):
106
+ """Params for ``method=loop_get`` (RFC-450 §9.2).
107
+
108
+ Attributes:
109
+ loop_id: Loop identifier (required, non-empty).
110
+ verbose: Include verbose details.
111
+ tree: Include checkpoint tree.
112
+ """
113
+
114
+ loop_id: str = Field(..., min_length=1, description="Loop identifier")
115
+ verbose: bool = Field(default=False, description="Include verbose details")
116
+ tree: bool = Field(default=False, description="Include checkpoint tree")
117
+
118
+
119
+ class LoopListParams(ParamsBase):
120
+ """Params for ``method=loop_list`` (RFC-450 §9.2).
121
+
122
+ Attributes:
123
+ status: Optional status filter.
124
+ limit: Maximum number of results.
125
+ """
126
+
127
+ status: str | None = Field(default=None, description="Filter by loop status")
128
+ limit: int | None = Field(default=None, ge=1, description="Maximum results")
129
+
130
+
131
+ class LoopTreeParams(ParamsBase):
132
+ """Params for ``method=loop_tree`` (RFC-450 §9.2).
133
+
134
+ Attributes:
135
+ loop_id: Loop identifier (required).
136
+ """
137
+
138
+ loop_id: str = Field(..., min_length=1, description="Loop identifier")
139
+
140
+
141
+ class LoopPruneParams(ParamsBase):
142
+ """Params for ``method=loop_prune`` (RFC-450 §9.2).
143
+
144
+ Attributes:
145
+ loop_id: Loop identifier (required).
146
+ keep_latest: Number of recent branches to keep (default 1).
147
+ """
148
+
149
+ loop_id: str = Field(..., min_length=1)
150
+ keep_latest: int = Field(default=1, ge=1)
151
+
152
+
153
+ class LoopDeleteParams(ParamsBase):
154
+ """Params for ``method=loop_delete`` (RFC-450 §9.2).
155
+
156
+ Attributes:
157
+ loop_id: Loop identifier (required).
158
+ """
159
+
160
+ loop_id: str = Field(..., min_length=1)
161
+
162
+
163
+ class LoopNewParams(ParamsBase):
164
+ """Params for ``method=loop_new`` (RFC-450 §9.2).
165
+
166
+ Attributes:
167
+ workspace: Optional client workspace path.
168
+ user_id: Optional user identifier.
169
+ client_workspace_id: Optional stable workspace scope.
170
+ is_ephemeral: Create ephemeral loop.
171
+ """
172
+
173
+ workspace: str | None = None
174
+ user_id: str | None = None
175
+ client_workspace_id: str | None = None
176
+ is_ephemeral: bool = False
177
+
178
+
179
+ class LoopReattachParams(ParamsBase):
180
+ """Params for ``method=loop_reattach`` (RFC-450 §9.2).
181
+
182
+ Attributes:
183
+ loop_id: Loop to reattach (required).
184
+ """
185
+
186
+ loop_id: str = Field(..., min_length=1)
187
+
188
+
189
+ class LoopDetachParams(ParamsBase):
190
+ """Params for ``method=loop_detach`` (request mode, RFC-450 §9.2).
191
+
192
+ Attributes:
193
+ loop_id: Loop to detach from (required).
194
+ """
195
+
196
+ loop_id: str = Field(..., min_length=1)
197
+
198
+
199
+ class LoopInputParams(ParamsBase):
200
+ """Params for ``method=loop_input`` (request or notification, RFC-450 §9.2).
201
+
202
+ Attributes:
203
+ loop_id: Loop identifier (required).
204
+ content: User input text or structured content (required).
205
+ autonomous: Enable autonomous mode.
206
+ max_iterations: Max iterations for autonomous mode.
207
+ preferred_subagent: Routing hint.
208
+ model: Provider:model override.
209
+ model_params: Additional model parameters.
210
+ router_profile: Named ``router_profiles`` overlay for chat roles this turn.
211
+ attachments: Image attachments.
212
+ intent_hint: Daemon direct-model hint (text_completion, image_to_text, ocr, embed).
213
+ response_schema: Structured output schema.
214
+ response_schema_name: Schema name for logging.
215
+ response_schema_strict: Enable strict schema validation.
216
+ clarification_mode: RFC-622 clarification relay mode.
217
+ clarification_answer: Mark as answer to pending clarification.
218
+ clarification_answers: Per-question answers for multi-question clarification.
219
+ """
220
+
221
+ loop_id: str = Field(..., min_length=1)
222
+ content: str | dict[str, Any] = Field(..., description="User input text or structured content")
223
+ autonomous: bool = False
224
+ max_iterations: int | None = Field(default=None, gt=0)
225
+ preferred_subagent: str | None = None
226
+ model: str | None = None
227
+ model_params: dict[str, Any] | None = None
228
+ router_profile: str | None = None
229
+ attachments: list[dict[str, str]] | None = None
230
+ intent_hint: str | None = None
231
+ response_schema: dict[str, Any] | None = None
232
+ response_schema_name: str | None = None
233
+ response_schema_strict: bool | None = None
234
+ clarification_mode: str | None = Field(default=None, pattern=r"^(auto|manual)$")
235
+ clarification_answer: bool = False
236
+ clarification_answers: list[str] | None = None
237
+
238
+
239
+ class LoopMessagesParams(ParamsBase):
240
+ """Params for ``method=loop_messages`` (RFC-450 §9.2).
241
+
242
+ Attributes:
243
+ loop_id: Loop identifier (required).
244
+ limit: Maximum messages (default 100).
245
+ offset: Pagination offset.
246
+ include_events: Include tool events.
247
+ """
248
+
249
+ loop_id: str = Field(..., min_length=1)
250
+ limit: int = Field(default=100, ge=1)
251
+ offset: int = Field(default=0, ge=0)
252
+ include_events: bool = False
253
+
254
+
255
+ class LoopStateGetParams(ParamsBase):
256
+ """Params for ``method=loop_state_get`` (RFC-450 §9.2).
257
+
258
+ Attributes:
259
+ loop_id: Loop identifier (required).
260
+ keys: Specific channel keys to fetch.
261
+ """
262
+
263
+ loop_id: str = Field(..., min_length=1)
264
+ keys: list[str] | None = None
265
+
266
+
267
+ class LoopStateUpdateParams(ParamsBase):
268
+ """Params for ``method=loop_state_update`` (RFC-450 §9.2).
269
+
270
+ Attributes:
271
+ loop_id: Loop identifier (required).
272
+ values: Channel values to apply (required).
273
+ as_node: Node to apply update as.
274
+ """
275
+
276
+ loop_id: str = Field(..., min_length=1)
277
+ values: dict[str, Any]
278
+ as_node: str | None = None
279
+
280
+
281
+ class LoopCardsFetchParams(ParamsBase):
282
+ """Params for ``method=loop_cards_fetch`` (RFC-450 §9.2).
283
+
284
+ Attributes:
285
+ loop_id: Loop identifier (required).
286
+ since: Fetch cards after this sequence.
287
+ """
288
+
289
+ loop_id: str = Field(..., min_length=1)
290
+ since: str | None = None
291
+
292
+
293
+ class LoopHistoryFetchParams(ParamsBase):
294
+ """Params for ``method=loop_history_fetch`` (RFC-631).
295
+
296
+ Attributes:
297
+ loop_id: Loop identifier (required).
298
+ """
299
+
300
+ loop_id: str = Field(..., min_length=1)
301
+
302
+
303
+ # ---------------------------------------------------------------------------
304
+ # Subscription params (RFC-450 §9.2)
305
+ # ---------------------------------------------------------------------------
306
+
307
+
308
+ class SubscribeParams(ParamsBase):
309
+ """Params for ``method=loop_events`` subscription (RFC-450 §9.2).
310
+
311
+ Attributes:
312
+ loop_id: Loop to subscribe to (required).
313
+ stream_delivery: Delivery mode (batch/adaptive/streaming).
314
+ wire_tier: Wire filter tier (full/compact).
315
+ """
316
+
317
+ loop_id: str = Field(..., min_length=1)
318
+ stream_delivery: Literal["batch", "adaptive", "streaming"] = "adaptive"
319
+ wire_tier: Literal["full", "compact"] = "full"
320
+
321
+
322
+ class AutopilotSubscribeParams(ParamsBase):
323
+ """Params for ``method=autopilot_events`` subscription (RFC-450 §9.2).
324
+
325
+ Attributes:
326
+ job_id: Optional job filter.
327
+ filters: Event filter criteria.
328
+ """
329
+
330
+ job_id: str | None = None
331
+ filters: dict[str, Any] | None = None
332
+
333
+
334
+ # ---------------------------------------------------------------------------
335
+ # Job RPC params (RFC-450 §9.2)
336
+ # ---------------------------------------------------------------------------
337
+
338
+
339
+ class JobCreateParams(ParamsBase):
340
+ """Params for ``method=job_create`` (RFC-450 §9.2).
341
+
342
+ Attributes:
343
+ goal: Root goal description (required).
344
+ workspace: Optional workspace path.
345
+ user_id: Optional user identifier.
346
+ autonomous: Enable autonomous mode.
347
+ max_iterations: Max iterations.
348
+ guidance: Initial guidance.
349
+ intent_hint: Daemon direct-model hint (text_completion, image_to_text, ocr, embed).
350
+ """
351
+
352
+ goal: str = Field(..., min_length=1, description="Root goal text")
353
+ workspace: str | None = None
354
+ user_id: str | None = None
355
+ autonomous: bool = False
356
+ max_iterations: int | None = Field(default=None, gt=0)
357
+ guidance: str | None = None
358
+ intent_hint: str | None = None
359
+
360
+
361
+ class JobStatusParams(ParamsBase):
362
+ """Params for ``method=job_status`` (RFC-450 §9.2).
363
+
364
+ Attributes:
365
+ job_id: Job identifier (required).
366
+ """
367
+
368
+ job_id: str = Field(..., min_length=1)
369
+
370
+
371
+ class JobPauseParams(ParamsBase):
372
+ """Params for ``method=job_pause`` (RFC-450 §9.2).
373
+
374
+ Attributes:
375
+ job_id: Job to pause (required).
376
+ """
377
+
378
+ job_id: str = Field(..., min_length=1)
379
+
380
+
381
+ class JobResumeParams(ParamsBase):
382
+ """Params for ``method=job_resume`` (RFC-450 §9.2).
383
+
384
+ Attributes:
385
+ job_id: Job to resume (required).
386
+ """
387
+
388
+ job_id: str = Field(..., min_length=1)
389
+
390
+
391
+ class JobCancelParams(ParamsBase):
392
+ """Params for ``method=job_cancel`` (RFC-450 §9.2).
393
+
394
+ Attributes:
395
+ job_id: Job to cancel (required).
396
+ """
397
+
398
+ job_id: str = Field(..., min_length=1)
399
+
400
+
401
+ class JobDagParams(ParamsBase):
402
+ """Params for ``method=job_dag`` (RFC-450 §9.2).
403
+
404
+ Attributes:
405
+ job_id: Job identifier (required).
406
+ """
407
+
408
+ job_id: str = Field(..., min_length=1)
409
+
410
+
411
+ class JobGuidanceParams(ParamsBase):
412
+ """Params for ``method=job_guidance`` (RFC-450 §9.2).
413
+
414
+ Attributes:
415
+ job_id: Target job (required).
416
+ content: Guidance text (canonical, required).
417
+ goal_id: Optional specific goal target.
418
+ """
419
+
420
+ job_id: str = Field(..., min_length=1)
421
+ content: str = Field(..., min_length=1, description="Guidance text (canonical)")
422
+ goal_id: str | None = None
423
+
424
+
425
+ # ---------------------------------------------------------------------------
426
+ # Cron RPC params (RFC-229)
427
+ # ---------------------------------------------------------------------------
428
+
429
+
430
+ class CronAddParams(ParamsBase):
431
+ """Params for ``method=cron_add`` (RFC-229).
432
+
433
+ Attributes:
434
+ text: Natural language scheduling request (required).
435
+ priority: Optional job priority (1-100).
436
+ """
437
+
438
+ text: str = Field(..., min_length=1, description="Natural language scheduling request")
439
+ priority: int | None = Field(default=None, ge=1, le=100)
440
+
441
+
442
+ class CronListParams(ParamsBase):
443
+ """Params for ``method=cron_list`` (RFC-229).
444
+
445
+ Attributes:
446
+ status: Optional status filter.
447
+ """
448
+
449
+ status: str | None = None
450
+
451
+
452
+ class CronShowParams(ParamsBase):
453
+ """Params for ``method=cron_show`` (RFC-229).
454
+
455
+ Attributes:
456
+ job_id: Cron job identifier (required).
457
+ """
458
+
459
+ job_id: str = Field(..., min_length=1)
460
+
461
+
462
+ class CronCancelParams(ParamsBase):
463
+ """Params for ``method=cron_cancel`` (RFC-229).
464
+
465
+ Attributes:
466
+ job_id: Cron job to cancel (required).
467
+ """
468
+
469
+ job_id: str = Field(..., min_length=1)
470
+
471
+
472
+ # ---------------------------------------------------------------------------
473
+ # Daemon & config params (RFC-450 §9.2)
474
+ # ---------------------------------------------------------------------------
475
+
476
+
477
+ class DaemonStatusParams(EmptyParams):
478
+ """Params for ``method=daemon_status`` — no required fields."""
479
+
480
+
481
+ class DaemonShutdownParams(EmptyParams):
482
+ """Params for ``method=daemon_shutdown`` — no required fields."""
483
+
484
+
485
+ class ConfigGetParams(ParamsBase):
486
+ """Params for ``method=config_get`` (RFC-450 §9.2).
487
+
488
+ Attributes:
489
+ section: Config section to fetch.
490
+ """
491
+
492
+ section: str | None = None
493
+
494
+
495
+ class ConfigReloadParams(EmptyParams):
496
+ """Params for ``method=config_reload`` — no required fields."""
497
+
498
+
499
+ # ---------------------------------------------------------------------------
500
+ # Skills & models params (RFC-450 §9.2)
501
+ # ---------------------------------------------------------------------------
502
+
503
+
504
+ class SkillsListParams(EmptyParams):
505
+ """Params for ``method=skills_list`` — no required fields."""
506
+
507
+
508
+ class ModelsListParams(EmptyParams):
509
+ """Params for ``method=models_list`` — no required fields."""
510
+
511
+
512
+ class InvokeSkillParams(ParamsBase):
513
+ """Params for ``method=invoke_skill`` (RFC-450 §9.2).
514
+
515
+ Attributes:
516
+ skill: Skill name (required).
517
+ args: Skill arguments.
518
+ clarification_mode: RFC-622 clarification relay mode.
519
+ """
520
+
521
+ skill: str = Field(..., min_length=1)
522
+ args: str = ""
523
+ clarification_mode: str | None = Field(default=None, pattern=r"^(auto|manual)$")
524
+
525
+
526
+ class McpStatusParams(EmptyParams):
527
+ """Params for ``method=mcp_status`` — no required fields."""
528
+
529
+
530
+ # ---------------------------------------------------------------------------
531
+ # Auth params (RFC-450 §9.2)
532
+ # ---------------------------------------------------------------------------
533
+
534
+
535
+ class AuthParams(ParamsBase):
536
+ """Params for ``method=auth`` (RFC-450 §9.2).
537
+
538
+ Attributes:
539
+ access_key: Access key credential.
540
+ secret_key: Secret key credential.
541
+ """
542
+
543
+ access_key: str = ""
544
+ secret_key: str = ""
545
+
546
+
547
+ class AuthRefreshParams(ParamsBase):
548
+ """Params for ``method=auth_refresh`` (RFC-450 §9.2).
549
+
550
+ Attributes:
551
+ refresh_token: Token to refresh.
552
+ """
553
+
554
+ refresh_token: str = ""
555
+
556
+
557
+ # ---------------------------------------------------------------------------
558
+ # Command params (RFC-450 §9.4)
559
+ # ---------------------------------------------------------------------------
560
+
561
+
562
+ class SlashCommandParams(ParamsBase):
563
+ """Params for ``method=slash_command`` notification (RFC-450 §9.4).
564
+
565
+ Attributes:
566
+ cmd: Slash command string (e.g. ``/exit``, ``/cancel``).
567
+ """
568
+
569
+ cmd: str = Field(..., min_length=1)
570
+
571
+
572
+ class RpcCommandParams(ParamsBase):
573
+ """Params for ``method=rpc_command`` request (RFC-450 §9.4).
574
+
575
+ Attributes:
576
+ command: RPC command name.
577
+ payload: Command payload.
578
+ """
579
+
580
+ command: str | None = None
581
+ payload: dict[str, Any] | None = None
582
+
583
+
584
+ # ---------------------------------------------------------------------------
585
+ # Connection params (RFC-450 §8.2)
586
+ # ---------------------------------------------------------------------------
587
+
588
+
589
+ # Re-export ConnectionInitParams from wire.py so callers have one import path.
590
+ # The wire.py version carries the full handshake structure (client_version,
591
+ # client_name, accept_proto, capabilities) and is used by the actual
592
+ # connection_init envelope — see WireEnvelope/ConnectionInitEnvelope there.
593
+ from soothe_sdk.wire.codec import ConnectionInitParams # noqa: E402,F401
594
+
595
+
596
+ class DisconnectParams(EmptyParams):
597
+ """Params for ``method=disconnect`` notification — no required fields."""
598
+
599
+
600
+ class DeliveryAckParams(ParamsBase):
601
+ """Params for ``method=delivery_ack`` notification (stream termination drain)."""
602
+
603
+ loop_id: str = Field(..., min_length=1)
604
+ seq: int = Field(..., ge=0)
@@ -0,0 +1,55 @@
1
+ """Wire-safe protocol schemas for WebSocket communication.
2
+
3
+ These schemas are used in daemon-CLI communication via WebSocket protocol.
4
+ They're defined in SDK so both daemon and CLI can use them without daemon
5
+ runtime dependency in CLI.
6
+
7
+ This module is part of Phase 1 of IG-174: CLI import violations fix.
8
+
9
+ Note: These are simplified wire-safe versions. Full protocol implementations
10
+ are in soothe.protocols.planner (daemon-side).
11
+ """
12
+
13
+ from pydantic import BaseModel, Field
14
+
15
+
16
+ class PlanStep(BaseModel):
17
+ """Wire-safe plan step schema for WebSocket protocol."""
18
+
19
+ step_id: str = Field(description="Unique identifier for this step")
20
+ description: str = Field(description="Human-readable step description")
21
+ status: str = Field(
22
+ default="pending", description="Step status: pending/running/completed/failed"
23
+ )
24
+ result: str | None = Field(default=None, description="Step execution result")
25
+ error: str | None = Field(default=None, description="Error message if failed")
26
+
27
+
28
+ class Plan(BaseModel):
29
+ """Wire-safe plan schema for WebSocket protocol."""
30
+
31
+ plan_id: str = Field(description="Unique identifier for this plan")
32
+ goal: str = Field(description="The goal this plan addresses")
33
+ steps: list[PlanStep] = Field(default_factory=list, description="Ordered list of steps")
34
+ status: str = Field(
35
+ default="created", description="Plan status: created/executing/completed/failed"
36
+ )
37
+
38
+
39
+ class ToolOutput(BaseModel):
40
+ """Wire-safe tool output schema for WebSocket protocol.
41
+
42
+ Used to structure tool results for rendering in CLI.
43
+ """
44
+
45
+ tool_name: str = Field(description="Name of the tool that produced output")
46
+ output: str = Field(description="Tool output content")
47
+ error: str | None = Field(default=None, description="Error if tool failed")
48
+ metadata: dict = Field(default_factory=dict, description="Additional metadata")
49
+
50
+
51
+ __all__ = [
52
+ "Plan",
53
+ "PlanStep",
54
+ "ToolOutput",
55
+ ]