plan-manager-client 0.2.152__tar.gz → 0.2.154__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.
- {plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/PKG-INFO +2 -2
- {plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client/dispatch.py +6 -0
- {plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client/facade_runtime_mixin/authoring_part.py +8 -3
- {plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client/facade_runtime_mixin/facade.py +2 -2
- plan_manager_client-0.2.154/plan_manager_client/id_rule.py +104 -0
- {plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client.egg-info/PKG-INFO +2 -2
- {plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client.egg-info/SOURCES.txt +1 -0
- {plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client.egg-info/requires.txt +1 -1
- {plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/pyproject.toml +1 -1
- {plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client/__init__.py +0 -0
- {plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client/_version.py +0 -0
- {plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client/authoring_diagnostics.py +0 -0
- {plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client/client.py +0 -0
- {plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client/config.py +0 -0
- {plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client/delivery/__init__.py +0 -0
- {plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client/delivery/archive.py +0 -0
- {plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client/delivery/atomicity.py +0 -0
- {plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client/delivery/commit.py +0 -0
- {plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client/delivery/destination.py +0 -0
- {plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client/delivery/provenance.py +0 -0
- {plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client/delivery/upload.py +0 -0
- {plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client/export_fetch_chunks.py +0 -0
- {plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client/export_fetch_request.py +0 -0
- {plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client/export_fetch_unpack.py +0 -0
- {plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client/export_fetch_verify.py +0 -0
- {plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client/facade_agent_config_mixin.py +0 -0
- {plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client/facade_bug_project_mixin.py +0 -0
- {plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client/facade_context_gate_mixin.py +0 -0
- {plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client/facade_entity_common_mixin.py +0 -0
- {plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client/facade_hrs_file_mixin.py +0 -0
- {plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client/facade_plan_hrs_mixin.py +0 -0
- {plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client/facade_runtime_mixin/__init__.py +0 -0
- {plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client/facade_runtime_mixin/overlay_part.py +0 -0
- {plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client/facade_step_graph_mixin.py +0 -0
- {plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client/info_walker.py +0 -0
- {plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client/scenario_runtime.py +0 -0
- {plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client/server_api.py +0 -0
- {plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client/server_api_mutating.py +0 -0
- {plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client.egg-info/dependency_links.txt +0 -0
- {plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client.egg-info/top_level.txt +0 -0
- {plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/setup.cfg +0 -0
- {plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/tests/test_client_server_api_sync.py +0 -0
- {plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/tests/test_contract_facade_passthrough.py +0 -0
- {plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/tests/test_dispatch_validation_error_classification.py +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: plan-manager-client
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.154
|
|
4
4
|
Summary: First-class client library for the plan_manager JSON-RPC API: full command-surface facade plus export delivery module boundary.
|
|
5
5
|
Requires-Python: >=3.12
|
|
6
|
-
Requires-Dist: mcp-proxy-adapter==8.11.
|
|
6
|
+
Requires-Dist: mcp-proxy-adapter==8.11.9
|
|
7
7
|
Provides-Extra: ca
|
|
8
8
|
Requires-Dist: code-analysis-client>=1.6.111; extra == "ca"
|
|
9
9
|
Provides-Extra: dev
|
|
@@ -16,6 +16,7 @@ from plan_manager_client.authoring_diagnostics import (
|
|
|
16
16
|
emit as emit_authoring_diagnostic,
|
|
17
17
|
new_correlation_id,
|
|
18
18
|
)
|
|
19
|
+
from plan_manager_client.id_rule import InvalidIdentifierError, check_call_ids
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
def _adapter_validation_error_result(status: object) -> dict[str, Any] | None:
|
|
@@ -289,6 +290,11 @@ class _CommandDispatchMixin:
|
|
|
289
290
|
wait remains bounded by the caller-selected budget. Returns the "result"
|
|
290
291
|
value from the unified response for both immediate and queued commands.
|
|
291
292
|
"""
|
|
293
|
+
try:
|
|
294
|
+
check_call_ids(params) # uuid4 discipline (block 2, D7): an invalid identifier is refused before the call is sent
|
|
295
|
+
except InvalidIdentifierError as refusal:
|
|
296
|
+
# The refusal is the command's own answer (-32602, violation value), never an exception out of the facade (0153 GI2).
|
|
297
|
+
return refusal.as_answer()
|
|
292
298
|
diagnostic = command in AUTHORING_DIAGNOSTIC_COMMANDS
|
|
293
299
|
correlation_id = new_correlation_id() if diagnostic else ""
|
|
294
300
|
if diagnostic:
|
|
@@ -117,9 +117,14 @@ class RuntimeAuthoringPart:
|
|
|
117
117
|
{"plan": plan, "step": step, "session_id": session_id},
|
|
118
118
|
)
|
|
119
119
|
|
|
120
|
-
async def authoring_job_delete(self, job_id: str) -> Any:
|
|
121
|
-
"""
|
|
122
|
-
|
|
120
|
+
async def authoring_job_delete(self, job_id: str, hard: bool = False, recursive: bool = False) -> Any:
|
|
121
|
+
"""Soft-delete (mark) one terminal authoring job; hard=True removes an already marked job (server default: false)."""
|
|
122
|
+
params: dict[str, Any] = {"job_id": job_id}
|
|
123
|
+
if hard:
|
|
124
|
+
params["hard"] = True
|
|
125
|
+
if recursive:
|
|
126
|
+
params["recursive"] = True
|
|
127
|
+
return await self._call("authoring_job_delete", params)
|
|
123
128
|
|
|
124
129
|
async def authoring_child_list(self, step: str, plan: str | None = None) -> Any:
|
|
125
130
|
"""List the direct planned children of the step ``step`` (its id, a uuid4, or with ``plan`` its path)."""
|
|
@@ -212,8 +212,8 @@ class RuntimeCommandsMixin:
|
|
|
212
212
|
plan, step, session_id
|
|
213
213
|
)
|
|
214
214
|
|
|
215
|
-
async def authoring_job_delete(self, job_id: str) -> Any:
|
|
216
|
-
return await self.runtimeAuthoringPart.authoring_job_delete(job_id)
|
|
215
|
+
async def authoring_job_delete(self, job_id: str, hard: bool = False, recursive: bool = False) -> Any:
|
|
216
|
+
return await self.runtimeAuthoringPart.authoring_job_delete(job_id, hard=hard, recursive=recursive)
|
|
217
217
|
|
|
218
218
|
async def authoring_child_list(self, step: str, plan: str | None = None) -> Any:
|
|
219
219
|
return await self.runtimeAuthoringPart.authoring_child_list(step, plan=plan)
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""Client-side check of the identifier rule D1 (uuid4 discipline, block 2, D7; TODO 93bc1adc-755b-461c-8f1e-20e51b3c0b25).
|
|
2
|
+
|
|
3
|
+
The client is a separate wheel, so the rule is a copy of ``plan_manager.storage.id_rule``: an identifier is canonical
|
|
4
|
+
8-4-4-4-12 text, version 4 (RFC 4122 variant), not nil, and has no run of eight or more '0' hex digits (hyphens ignored).
|
|
5
|
+
``check_call_ids`` runs in the facade before every call: a value of a parameter named in ``UUID_PARAMETER_NAMES`` (the
|
|
6
|
+
server's ``format: uuid`` parameters) that fails the rule is refused locally with the server's own answer shape
|
|
7
|
+
(-32602, violation ``value``, the parameter as field, the exact value). The server stays the authority.
|
|
8
|
+
|
|
9
|
+
Free-text identifiers are not in the table and are not checked: they are not uuids of this project (request, session and
|
|
10
|
+
call ids, provider request ids, uuid7 evidence ids, idempotency keys, sha256 hashes, dialogue and transcript references,
|
|
11
|
+
the server id of a tool). ``plan`` takes a name or an id: only a value of the id shape is checked (``ID_OR_NAME_PARAMETERS``).
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import re
|
|
17
|
+
import uuid
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
_ZERO_RUN = re.compile(r"0{8}")
|
|
21
|
+
_CANONICAL = re.compile(r"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$")
|
|
22
|
+
EXPECTED = "a valid identifier: uuid4, not nil, without a run of eight zeros"
|
|
23
|
+
|
|
24
|
+
ID_OR_NAME_PARAMETERS: frozenset[str] = frozenset({"plan"})
|
|
25
|
+
|
|
26
|
+
UUID_PARAMETER_NAMES: frozenset[str] = frozenset({
|
|
27
|
+
"anchor_plan", "anchor_plan_uuid", "anchor_project_id", "anchor_ref_id", "anchor_revision_uuid", "assignment_uuid",
|
|
28
|
+
"attempt_id", "binding_uuid", "bug", "bug_fix", "bug_id", "calendar_entry", "comment", "comment_uuid", "dependency_uuid",
|
|
29
|
+
"duplicate_of_uuid", "entity_id", "entity_ids", "entity_uuid", "envelope_uuid", "escalation_uuid", "from_entity_uuid",
|
|
30
|
+
"from_todo", "identifiers", "impact_uuid", "link", "linked_plan_uuid", "membership_uuid", "model_uuid",
|
|
31
|
+
"new_anchor_plan_uuid", "new_anchor_project_id", "new_anchor_ref_id", "new_anchor_revision_uuid", "new_owner_id",
|
|
32
|
+
"new_source_plan_uuid", "new_source_project_id", "new_source_ref_id", "new_source_revision_uuid", "parent_bug_uuid",
|
|
33
|
+
"policy_uuid", "profile_uuid", "project", "propagation_id", "provider_uuid", "review_uuid", "revision", "role_uuid",
|
|
34
|
+
"source_plan_uuid", "source_project_id", "source_ref", "source_ref_id", "source_revision_uuid", "supersedes_comment_uuid",
|
|
35
|
+
"target_plan_uuid", "target_project_id", "target_ref_id", "target_revision_uuid", "to_entity_uuid", "to_todo", "todo",
|
|
36
|
+
"tool_uuid", "toolset_uuid", "wish",
|
|
37
|
+
})
|
|
38
|
+
"""The parameters the server declares ``format: uuid`` (every command of the live catalog, generated from the schemas)."""
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class InvalidIdentifierError(ValueError):
|
|
42
|
+
"""A client-side refusal of an identifier value: the server's -32602 / violation value shape, before the call."""
|
|
43
|
+
|
|
44
|
+
code = -32602
|
|
45
|
+
violation = "value"
|
|
46
|
+
|
|
47
|
+
def __init__(self, field: str, value: Any, defect: str) -> None:
|
|
48
|
+
self.field = field
|
|
49
|
+
self.value = value
|
|
50
|
+
super().__init__(
|
|
51
|
+
f"parameter {field!r} must be {EXPECTED}, got {value!r} ({defect})"
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
def as_answer(self) -> dict[str, Any]:
|
|
55
|
+
"""The server's -32602 ErrorResult for this refusal: the answer a command gives, not an exception."""
|
|
56
|
+
return {
|
|
57
|
+
"success": False,
|
|
58
|
+
"error": {
|
|
59
|
+
"code": self.code,
|
|
60
|
+
"message": str(self),
|
|
61
|
+
"data": {"field": self.field, "violation": self.violation, "expected": EXPECTED, "got": self.value},
|
|
62
|
+
},
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def id_defect(value: Any) -> str | None:
|
|
67
|
+
"""Why ``value`` is not a valid identifier, or None when it is one (the rule D1 of the server)."""
|
|
68
|
+
if isinstance(value, uuid.UUID):
|
|
69
|
+
candidate = value
|
|
70
|
+
else:
|
|
71
|
+
try:
|
|
72
|
+
candidate = uuid.UUID(str(value))
|
|
73
|
+
except (AttributeError, TypeError, ValueError):
|
|
74
|
+
return f"invalid uuid: {value!r}"
|
|
75
|
+
if candidate.int == 0:
|
|
76
|
+
return f"nil uuid: {candidate}"
|
|
77
|
+
if candidate.version != 4:
|
|
78
|
+
return f"not version 4: {candidate}"
|
|
79
|
+
if _ZERO_RUN.search(candidate.hex):
|
|
80
|
+
return f"a run of eight zeros: {candidate}"
|
|
81
|
+
return None
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _is_id_shape(value: Any) -> bool:
|
|
85
|
+
return isinstance(value, str) and _CANONICAL.match(value) is not None
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def check_call_ids(params: dict[str, Any] | None) -> None:
|
|
89
|
+
"""Refuse locally every identifier value of a call that fails rule D1 (see the module docstring).
|
|
90
|
+
|
|
91
|
+
Raises InvalidIdentifierError naming the parameter and the exact value. Nothing is sent when it raises.
|
|
92
|
+
"""
|
|
93
|
+
for name, value in (params or {}).items():
|
|
94
|
+
if name in UUID_PARAMETER_NAMES:
|
|
95
|
+
values = value if isinstance(value, list) else [value]
|
|
96
|
+
for item in values:
|
|
97
|
+
if isinstance(item, str):
|
|
98
|
+
defect = id_defect(item)
|
|
99
|
+
if defect is not None:
|
|
100
|
+
raise InvalidIdentifierError(name, item, defect)
|
|
101
|
+
elif name in ID_OR_NAME_PARAMETERS and _is_id_shape(value):
|
|
102
|
+
defect = id_defect(value)
|
|
103
|
+
if defect is not None:
|
|
104
|
+
raise InvalidIdentifierError(name, value, defect)
|
{plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client.egg-info/PKG-INFO
RENAMED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: plan-manager-client
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.154
|
|
4
4
|
Summary: First-class client library for the plan_manager JSON-RPC API: full command-surface facade plus export delivery module boundary.
|
|
5
5
|
Requires-Python: >=3.12
|
|
6
|
-
Requires-Dist: mcp-proxy-adapter==8.11.
|
|
6
|
+
Requires-Dist: mcp-proxy-adapter==8.11.9
|
|
7
7
|
Provides-Extra: ca
|
|
8
8
|
Requires-Dist: code-analysis-client>=1.6.111; extra == "ca"
|
|
9
9
|
Provides-Extra: dev
|
{plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client.egg-info/SOURCES.txt
RENAMED
|
@@ -16,6 +16,7 @@ plan_manager_client/facade_entity_common_mixin.py
|
|
|
16
16
|
plan_manager_client/facade_hrs_file_mixin.py
|
|
17
17
|
plan_manager_client/facade_plan_hrs_mixin.py
|
|
18
18
|
plan_manager_client/facade_step_graph_mixin.py
|
|
19
|
+
plan_manager_client/id_rule.py
|
|
19
20
|
plan_manager_client/info_walker.py
|
|
20
21
|
plan_manager_client/scenario_runtime.py
|
|
21
22
|
plan_manager_client/server_api.py
|
|
@@ -8,7 +8,7 @@ dynamic = ["version"]
|
|
|
8
8
|
description = "First-class client library for the plan_manager JSON-RPC API: full command-surface facade plus export delivery module boundary."
|
|
9
9
|
requires-python = ">=3.12"
|
|
10
10
|
dependencies = [
|
|
11
|
-
"mcp-proxy-adapter==8.11.
|
|
11
|
+
"mcp-proxy-adapter==8.11.9",
|
|
12
12
|
]
|
|
13
13
|
|
|
14
14
|
[project.optional-dependencies]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client/delivery/__init__.py
RENAMED
|
File without changes
|
{plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client/delivery/archive.py
RENAMED
|
File without changes
|
|
File without changes
|
{plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client/delivery/commit.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client/delivery/upload.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client/info_walker.py
RENAMED
|
File without changes
|
{plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client/scenario_runtime.py
RENAMED
|
File without changes
|
{plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/plan_manager_client/server_api.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plan_manager_client-0.2.152 → plan_manager_client-0.2.154}/tests/test_client_server_api_sync.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|