patchr 0.1.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.
Files changed (116) hide show
  1. apps/__init__.py +2 -0
  2. apps/api/__init__.py +2 -0
  3. apps/api/main.py +652 -0
  4. apps/benchmarks/__init__.py +1 -0
  5. apps/benchmarks/main.py +20 -0
  6. apps/sandbox/__init__.py +1 -0
  7. apps/sandbox/main.py +20 -0
  8. apps/worker/__init__.py +2 -0
  9. apps/worker/main.py +15 -0
  10. apps/worker/verify.py +14 -0
  11. patchr/__init__.py +12 -0
  12. patchr/sdk/__init__.py +20 -0
  13. patchr/sdk/client.py +12 -0
  14. patchr-0.1.0.dist-info/METADATA +137 -0
  15. patchr-0.1.0.dist-info/RECORD +116 -0
  16. patchr-0.1.0.dist-info/WHEEL +5 -0
  17. patchr-0.1.0.dist-info/entry_points.txt +5 -0
  18. patchr-0.1.0.dist-info/licenses/LICENSE +17 -0
  19. patchr-0.1.0.dist-info/top_level.txt +3 -0
  20. picux/__init__.py +6 -0
  21. picux/agents/__init__.py +5 -0
  22. picux/agents/registry.py +204 -0
  23. picux/api/__init__.py +5 -0
  24. picux/api/service.py +5075 -0
  25. picux/audit/__init__.py +31 -0
  26. picux/audit/activity.py +97 -0
  27. picux/audit/observability.py +55 -0
  28. picux/audit/verification/__init__.py +21 -0
  29. picux/audit/verification/ledger.py +633 -0
  30. picux/benchmarks/__init__.py +5 -0
  31. picux/benchmarks/local.py +286 -0
  32. picux/config.py +140 -0
  33. picux/contracts/__init__.py +22 -0
  34. picux/contracts/handshake.py +122 -0
  35. picux/contracts/integration.py +385 -0
  36. picux/contracts/openapi.py +187 -0
  37. picux/contracts/protocol_map.py +152 -0
  38. picux/contracts/routes.py +980 -0
  39. picux/contracts/schema_catalog.py +125 -0
  40. picux/core/__init__.py +17 -0
  41. picux/core/models.py +148 -0
  42. picux/core/router.py +131 -0
  43. picux/core/runtime.py +42 -0
  44. picux/core/state_machine.py +38 -0
  45. picux/domains/__init__.py +2 -0
  46. picux/domains/bridge/HostRun.py +1104 -0
  47. picux/domains/bridge/__init__.py +6 -0
  48. picux/domains/bridge/engine.py +345 -0
  49. picux/domains/hunt/__init__.py +6 -0
  50. picux/domains/hunt/engine.py +307 -0
  51. picux/domains/hunt/models.py +88 -0
  52. picux/domains/pay/__init__.py +16 -0
  53. picux/domains/pay/adapters.py +607 -0
  54. picux/domains/pay/engine.py +950 -0
  55. picux/domains/pay/models.py +95 -0
  56. picux/domains/proxy/__init__.py +5 -0
  57. picux/domains/proxy/engine.py +466 -0
  58. picux/domains/resolve/__init__.py +5 -0
  59. picux/domains/resolve/engine.py +546 -0
  60. picux/orchestrator/__init__.py +3 -0
  61. picux/orchestrator/engine.py +2840 -0
  62. picux/portals/__init__.py +17 -0
  63. picux/portals/templates.py +272 -0
  64. picux/protocols/__init__.py +1 -0
  65. picux/protocols/a2a/__init__.py +6 -0
  66. picux/protocols/a2a/client.py +51 -0
  67. picux/protocols/a2a/envelope.py +132 -0
  68. picux/protocols/mcp/__init__.py +7 -0
  69. picux/protocols/mcp/client.py +69 -0
  70. picux/protocols/mcp/contract.py +67 -0
  71. picux/protocols/mcp/server.py +76 -0
  72. picux/sandbox/__init__.py +6 -0
  73. picux/sandbox/midnight_arbitrage.py +215 -0
  74. picux/sandbox/models.py +90 -0
  75. picux/sdk/__init__.py +13 -0
  76. picux/sdk/client.py +768 -0
  77. picux/sdk/external.py +245 -0
  78. picux/security/__init__.py +18 -0
  79. picux/security/auth.py +86 -0
  80. picux/security/config_validator.py +58 -0
  81. picux/security/policy.py +158 -0
  82. picux/security/secrets.py +144 -0
  83. picux/signals/__init__.py +1 -0
  84. picux/signals/community/__init__.py +24 -0
  85. picux/signals/community/adapters/__init__.py +7 -0
  86. picux/signals/community/adapters/reddit.py +37 -0
  87. picux/signals/community/adapters/shopify.py +23 -0
  88. picux/signals/community/adapters/web.py +23 -0
  89. picux/signals/community/disambiguation.py +51 -0
  90. picux/signals/community/intake.py +227 -0
  91. picux/signals/community/models.py +102 -0
  92. picux/signals/community/rules.py +91 -0
  93. picux/signals/community/scoring.py +64 -0
  94. picux/storage/__init__.py +41 -0
  95. picux/storage/agents.py +50 -0
  96. picux/storage/cases.py +440 -0
  97. picux/storage/channels.py +476 -0
  98. picux/storage/connectors.py +411 -0
  99. picux/storage/envelopes.py +137 -0
  100. picux/storage/escrows.py +168 -0
  101. picux/storage/events.py +989 -0
  102. picux/storage/keyspace.py +60 -0
  103. picux/storage/mandates.py +107 -0
  104. picux/storage/portals.py +222 -0
  105. picux/storage/postgres.py +2049 -0
  106. picux/storage/providers.py +148 -0
  107. picux/storage/proxy.py +231 -0
  108. picux/storage/receipts.py +131 -0
  109. picux/storage/signals.py +147 -0
  110. picux/storage/tasks.py +179 -0
  111. picux/tools/__init__.py +11 -0
  112. picux/tools/shared.py +2048 -0
  113. picux/verification/__init__.py +5 -0
  114. picux/verification/rollout.py +183 -0
  115. picux/workflows/__init__.py +5 -0
  116. picux/workflows/templates.py +74 -0
@@ -0,0 +1,95 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import Any, Protocol
5
+
6
+
7
+ @dataclass(frozen=True)
8
+ class Money:
9
+ amount: float
10
+ currency: str = "USD"
11
+
12
+ @classmethod
13
+ def fromObj(cls, value: dict[str, Any]) -> "Money":
14
+ return cls(
15
+ amount=float(value.get("amount", 0.0) or 0.0),
16
+ currency=str(value.get("currency", "USD") or "USD").upper(),
17
+ )
18
+
19
+ def toMap(self) -> dict[str, Any]:
20
+ return {"amount": round(max(0.0, float(self.amount or 0.0)), 2), "currency": self.currency}
21
+
22
+
23
+ @dataclass(frozen=True)
24
+ class ProofOfValue:
25
+ povId: str
26
+ rules: tuple[str, ...] = ()
27
+ meta: dict[str, Any] = field(default_factory=dict)
28
+
29
+ @classmethod
30
+ def fromObj(cls, value: dict[str, Any] | None) -> "ProofOfValue":
31
+ value = value or {}
32
+ return cls(
33
+ povId=str(value.get("povId", value.get("id", "")) or ""),
34
+ rules=tuple(str(item) for item in value.get("rules", []) if str(item).strip()),
35
+ meta=value.get("meta", {}) if isinstance(value.get("meta", {}), dict) else {},
36
+ )
37
+
38
+ def toMap(self) -> dict[str, Any]:
39
+ return {"povId": self.povId, "rules": list(self.rules), "meta": self.meta}
40
+
41
+
42
+ @dataclass(frozen=True)
43
+ class SettlementRequest:
44
+ taskId: str
45
+ vendorId: str
46
+ amount: Money
47
+ pov: ProofOfValue
48
+
49
+ @classmethod
50
+ def fromObj(cls, value: dict[str, Any]) -> "SettlementRequest":
51
+ return cls(
52
+ taskId=str(value.get("taskId", "") or ""),
53
+ vendorId=str(value.get("vendorId", value.get("payeeId", "")) or ""),
54
+ amount=Money.fromObj(value.get("amount", {}) if isinstance(value.get("amount"), dict) else {}),
55
+ pov=ProofOfValue.fromObj(value.get("pov") if isinstance(value.get("pov"), dict) else {}),
56
+ )
57
+
58
+
59
+ @dataclass(frozen=True)
60
+ class SettlementAdapterSpec:
61
+ adapterId: str
62
+ rail: str
63
+ mode: str = "prepare"
64
+ network: str = ""
65
+ requiredEnv: tuple[str, ...] = ()
66
+ meta: dict[str, Any] = field(default_factory=dict)
67
+
68
+ @classmethod
69
+ def fromObj(cls, value: dict[str, Any] | None) -> "SettlementAdapterSpec":
70
+ value = value or {}
71
+ return cls(
72
+ adapterId=str(value.get("adapterId", value.get("id", "adapter.prepare")) or "adapter.prepare"),
73
+ rail=str(value.get("rail", value.get("railId", "offchain")) or "offchain"),
74
+ mode=str(value.get("mode", "prepare") or "prepare"),
75
+ network=str(value.get("network", "") or ""),
76
+ requiredEnv=tuple(str(item) for item in value.get("requiredEnv", []) if str(item).strip()),
77
+ meta=value.get("meta", {}) if isinstance(value.get("meta", {}), dict) else {},
78
+ )
79
+
80
+ def toMap(self) -> dict[str, Any]:
81
+ return {
82
+ "adapterId": self.adapterId,
83
+ "rail": self.rail,
84
+ "mode": self.mode,
85
+ "network": self.network,
86
+ "requiredEnv": list(self.requiredEnv),
87
+ "meta": self.meta,
88
+ }
89
+
90
+
91
+ class SettlementAdapter(Protocol):
92
+ adapterId: str
93
+
94
+ def prepare(self, instruction: dict[str, Any]) -> dict[str, Any]:
95
+ ...
@@ -0,0 +1,5 @@
1
+ """PROXY domain primitives."""
2
+
3
+ from .engine import ProxyDomain
4
+
5
+ __all__ = ["ProxyDomain"]
@@ -0,0 +1,466 @@
1
+ from __future__ import annotations
2
+
3
+ import hashlib
4
+ import json
5
+ import re
6
+ from dataclasses import dataclass
7
+ from typing import Any
8
+
9
+
10
+ @dataclass(frozen=True)
11
+ class ProxyDomain:
12
+ """Human-as-callable-node primitives for oversight, logistics, and proof."""
13
+
14
+ feeRate: float = 0.05
15
+ feeCap: float = 5.0
16
+
17
+ def brief(self, task: str = "", *, context: dict[str, Any] | None = None) -> dict[str, Any]:
18
+ context = context or {}
19
+ clean = str(task or context.get("task", context.get("goal", "")) or "").strip()
20
+ overview = self._overview(clean, context)
21
+ decisionFork = self._dict(context.get("decisionFork", context.get("fork", {})))
22
+ logistics = self._dict(context.get("logistics", context.get("logisticsBrief", {}))) or {
23
+ "pickup": overview["pickup"],
24
+ "delivery": overview["delivery"],
25
+ "deadline": overview["deadline"],
26
+ "contact": overview["contact"],
27
+ }
28
+ proofReq = self._cleanList(context.get("proofReq", context.get("proof", []))) or ["photo", "gps", "receiptRef"]
29
+ steps = self._cleanList(context.get("steps", [])) or [
30
+ "Review the context snapshot before acting.",
31
+ "Confirm identity, scope, and safety constraints.",
32
+ "Complete the requested human action or judgment.",
33
+ "Return proof with photo, GPS, receipt, tracking, transcript, or note.",
34
+ "Escalate before changing scope.",
35
+ ]
36
+ channelMsg = self.channelMsg(context)
37
+ voiceCall = self.voiceCall(context)
38
+ caseOpsTimeline = self._caseOpsTimeline(context, steps)
39
+ brief = {
40
+ "briefId": self._stableId("pbrief", {"task": clean, "context": context}),
41
+ "kind": "proxyBrief",
42
+ "missionType": self._missionType(context),
43
+ "state": "awaitingProxy",
44
+ "reason": str(context.get("reason", "humanIntervention") or "humanIntervention"),
45
+ "task": clean,
46
+ "missionState": self._dict(context.get("missionState", context.get("state", {}))),
47
+ "decisionFork": decisionFork,
48
+ "logistics": logistics,
49
+ "overview": overview,
50
+ "steps": steps,
51
+ "caseOpsTimeline": caseOpsTimeline,
52
+ "proofReq": proofReq,
53
+ "constraints": self._dict(context.get("constraints", {})),
54
+ "channelMsg": channelMsg,
55
+ "voiceCall": voiceCall,
56
+ }
57
+ brief["markdown"] = self._markdown(brief)
58
+ return brief
59
+
60
+ def route(self, payload: dict[str, Any] | None = None) -> dict[str, Any]:
61
+ payload = payload or {}
62
+ routeType = str(payload.get("routeType", "") or "")
63
+ if not routeType:
64
+ routeType = self._regulatedRouteType(payload)
65
+ assignedTo = str(payload.get("assignedTo", payload.get("reviewerId", "")) or "")
66
+ status = "assigned" if assignedTo else str(payload.get("status", "queued") or "queued")
67
+ return {
68
+ "routeId": str(payload.get("routeId", "") or self._stableId("proute", payload)),
69
+ "kind": "proxyRoute",
70
+ "status": status,
71
+ "routeType": routeType,
72
+ "queue": str(payload.get("queue", "proxyQueue") or "proxyQueue"),
73
+ "assignedTo": assignedTo,
74
+ "credentials": self._credentials(payload, routeType),
75
+ "sla": self._dict(payload.get("sla", {})),
76
+ "share": self._sharePolicy(routeType),
77
+ "fallback": self._fallback(routeType),
78
+ }
79
+
80
+ def channelMsg(self, payload: dict[str, Any] | None = None) -> dict[str, Any]:
81
+ payload = payload or {}
82
+ raw = payload.get("channelMsg", {}) if isinstance(payload.get("channelMsg"), dict) else {}
83
+ channel = str(raw.get("channel", payload.get("channel", "")) or "")
84
+ to = str(raw.get("to", payload.get("to", payload.get("userId", ""))) or "")
85
+ text = str(raw.get("text", payload.get("message", "")) or "")
86
+ return {
87
+ "msgId": str(raw.get("msgId", "") or self._stableId("pmsg", {"channel": channel, "to": to, "text": text})),
88
+ "kind": "channelMsg",
89
+ "channel": channel,
90
+ "to": to,
91
+ "text": text,
92
+ "status": str(raw.get("status", "pending") or "pending"),
93
+ "connectorId": str(raw.get("connectorId", payload.get("connectorId", "")) or ""),
94
+ }
95
+
96
+ def voiceCall(self, payload: dict[str, Any] | None = None) -> dict[str, Any]:
97
+ payload = payload or {}
98
+ raw = payload.get("voiceCall", {}) if isinstance(payload.get("voiceCall"), dict) else {}
99
+ phone = str(raw.get("phone", raw.get("phoneNumber", payload.get("phone", ""))) or "")
100
+ phoneRef = str(raw.get("phoneRef", raw.get("contactRef", payload.get("phoneRef", payload.get("contactRef", "")))) or "")
101
+ if not phone and not phoneRef:
102
+ return {}
103
+ call = {
104
+ "callId": str(raw.get("callId", "") or self._stableId("pcall", {"phone": phone, "phoneRef": phoneRef, "taskId": payload.get("taskId", "")})),
105
+ "kind": "voiceCall",
106
+ "status": str(raw.get("status", "pending") or "pending"),
107
+ "connectorId": str(raw.get("connectorId", payload.get("connectorId", "")) or ""),
108
+ "prompt": str(raw.get("prompt", payload.get("prompt", "")) or ""),
109
+ "transcript": raw.get("transcript", []) if isinstance(raw.get("transcript"), list) else [],
110
+ }
111
+ if phone:
112
+ call["phone"] = phone
113
+ if phoneRef:
114
+ call["phoneRef"] = phoneRef
115
+ return call
116
+
117
+ def pickNode(self, nodes: list[dict[str, Any]]) -> dict[str, Any] | None:
118
+ eligible = [
119
+ node
120
+ for node in nodes
121
+ if float(node.get("rating", 0.0) or 0.0) >= 4.9
122
+ and int(node.get("done", node.get("tasksDone", node.get("tasks_completed", 0))) or 0) >= 50
123
+ ]
124
+ if not eligible:
125
+ return None
126
+ eligible.sort(
127
+ key=lambda item: (
128
+ float(item.get("rating", 0.0) or 0.0),
129
+ int(item.get("done", item.get("tasksDone", item.get("tasks_completed", 0))) or 0),
130
+ ),
131
+ reverse=True,
132
+ )
133
+ return eligible[0]
134
+
135
+ def verifyProof(self, *, text: str = "", payload: dict[str, Any] | None = None) -> dict[str, Any]:
136
+ payload = payload or {}
137
+ lowered = str(text or "").strip().lower()
138
+ status = str(payload.get("status") or payload.get("outcome") or "").strip().lower()
139
+ if status in {"failed", "failure", "cancelled", "canceled", "aborted"}:
140
+ return self._proofResult("confirmedFailure", "providerFailure", payload=payload, text=text)
141
+ if any(token in lowered for token in ("failed", "not delivered", "cancelled", "canceled", "abort")):
142
+ return self._proofResult("confirmedFailure", "textFailure", payload=payload, text=text)
143
+
144
+ photos = self._photos(payload) + self._photoUrls(text)
145
+ gps = self._gps(payload) or self._gpsText(text)
146
+ receipt = self._ref(payload, ("receiptId", "receipt", "receiptRef", "orderId", "confirmationId")) or self._receiptText(text)
147
+ tracking = self._ref(payload, ("trackingId", "tracking", "trackingNumber", "shipmentId")) or self._trackingText(text)
148
+ transcript = payload.get("transcript", []) if isinstance(payload.get("transcript"), list) else []
149
+ note = str(payload.get("note", payload.get("summary", text)) or "").strip()
150
+ proofTypes = []
151
+ if photos:
152
+ proofTypes.append("photo")
153
+ if isinstance(gps, dict):
154
+ proofTypes.append("gps")
155
+ if receipt:
156
+ proofTypes.append("receiptRef")
157
+ if tracking:
158
+ proofTypes.append("trackingId")
159
+ if transcript:
160
+ proofTypes.append("transcript")
161
+ if note:
162
+ proofTypes.append("note")
163
+
164
+ hasPhoto = bool(photos)
165
+ hasGps = isinstance(gps, dict)
166
+ hasRef = bool(receipt or tracking)
167
+ if hasPhoto and hasGps:
168
+ outStatus = "confirmedSuccess"
169
+ reason = "photoGps"
170
+ elif hasPhoto and hasRef:
171
+ outStatus = "confirmedSuccess"
172
+ reason = "photoRef"
173
+ elif hasGps:
174
+ outStatus = "confirmedSuccess"
175
+ reason = "gps"
176
+ elif note and not payload.get("requiresPhysicalProof"):
177
+ outStatus = "confirmedSuccess"
178
+ reason = "humanNote"
179
+ else:
180
+ outStatus = "pending"
181
+ reason = "missingProof"
182
+
183
+ return {
184
+ "status": outStatus,
185
+ "reason": reason,
186
+ "proofTypes": list(dict.fromkeys(proofTypes)),
187
+ "proof": {
188
+ "photo": photos[0] if photos else "",
189
+ "photoCount": len(photos),
190
+ "gps": gps or {},
191
+ "receiptRef": receipt,
192
+ "trackingId": tracking,
193
+ "transcript": transcript,
194
+ "note": note,
195
+ },
196
+ "laborCost": self._cost(payload, text),
197
+ "settleReady": outStatus == "confirmedSuccess" and (hasGps or hasRef or bool(note and not payload.get("requiresPhysicalProof"))),
198
+ }
199
+
200
+ def outcome(self, *, text: str = "", payload: dict[str, Any] | None = None) -> dict[str, Any]:
201
+ payload = payload or {}
202
+ proof = self.verifyProof(text=text, payload=payload)
203
+ resumeState = "ready" if proof["status"] in {"confirmedSuccess", "confirmedFailure"} else "awaitingProxy"
204
+ return {
205
+ "outcomeId": str(payload.get("outcomeId", "") or self._stableId("pout", {"text": text, "payload": payload})),
206
+ "kind": "proxyOutcome",
207
+ "status": proof["status"],
208
+ "reason": proof["reason"],
209
+ "proofTypes": proof["proofTypes"],
210
+ "proof": proof["proof"],
211
+ "laborCost": proof["laborCost"],
212
+ "settleReady": proof["settleReady"],
213
+ "resume": {
214
+ "state": resumeState,
215
+ "source": "proxy",
216
+ "sourceOfTruth": proof["status"] == "confirmedSuccess",
217
+ "inject": {"proof": proof["proof"], "reason": proof["reason"]},
218
+ },
219
+ "settlementGate": {
220
+ "status": "ready" if proof["settleReady"] else "blocked",
221
+ "reason": "proxyProofVerified" if proof["settleReady"] else proof["reason"],
222
+ },
223
+ }
224
+
225
+ def mgmtFee(self, laborCost: float) -> float:
226
+ cost = max(0.0, float(laborCost or 0.0))
227
+ if cost <= 0:
228
+ return 0.0
229
+ fee = cost * max(0.0, self.feeRate)
230
+ if self.feeCap > 0:
231
+ fee = min(fee, self.feeCap)
232
+ return round(max(0.0, fee), 2)
233
+
234
+ def _proofResult(self, status: str, reason: str, *, payload: dict[str, Any], text: str) -> dict[str, Any]:
235
+ return {
236
+ "status": status,
237
+ "reason": reason,
238
+ "proofTypes": [],
239
+ "proof": {},
240
+ "laborCost": self._cost(payload, text),
241
+ "settleReady": False,
242
+ }
243
+
244
+ @staticmethod
245
+ def _overview(text: str, context: dict[str, Any]) -> dict[str, str]:
246
+ pickup = str(context.get("pickup", "") or "") or ProxyDomain._match(text, r"(?:pickup|pick up|collect|from)\s+(?:at\s+)?(?P<v>[^,.;]+)")
247
+ delivery = str(context.get("delivery", "") or "") or ProxyDomain._match(text, r"(?:deliver|drop\s*off|to)\s+(?:at\s+)?(?P<v>[^,.;]+)")
248
+ deadline = str(context.get("deadline", "") or "") or ProxyDomain._match(text, r"(?:by|before|no later than)\s+(?P<v>[0-2]?\d(?::[0-5]\d)?(?:\s?[ap]m)?)")
249
+ contact = str(context.get("contact", "") or "") or ProxyDomain._match(text, r"(?:contact|ask for)\s+(?P<v>[A-Za-z][A-Za-z\s'\-]{1,40})")
250
+ return {
251
+ "pickup": pickup or "unspecified",
252
+ "delivery": delivery or "unspecified",
253
+ "deadline": deadline or "ASAP",
254
+ "contact": contact or "humanProxy",
255
+ "item": str(context.get("item", "") or text or "Proxy action"),
256
+ }
257
+
258
+ @staticmethod
259
+ def _missionType(context: dict[str, Any]) -> str:
260
+ raw = str(context.get("missionType", context.get("kind", "logistics")) or "logistics")
261
+ return raw if raw in {"review", "logistics", "call", "message", "approval", "custom"} else "custom"
262
+
263
+ @staticmethod
264
+ def _caseOpsTimeline(context: dict[str, Any], steps: list[str]) -> list[dict[str, Any]]:
265
+ raw = context.get("caseOpsTimeline", [])
266
+ if isinstance(raw, list):
267
+ timeline = [item for item in raw if isinstance(item, dict)]
268
+ if timeline:
269
+ return timeline
270
+ owner = str(context.get("owner", "proxy") or "proxy")
271
+ if owner not in {"hunt", "resolve", "bridge", "proxy", "pay", "client"}:
272
+ owner = "proxy"
273
+ return [
274
+ {
275
+ "stepId": f"proxy_step_{idx}",
276
+ "status": "queued" if idx == 1 else "blocked",
277
+ "owner": owner,
278
+ "label": step,
279
+ "approvalRequired": idx == 2,
280
+ }
281
+ for idx, step in enumerate(steps, start=1)
282
+ ]
283
+
284
+ @staticmethod
285
+ def _match(text: str, pattern: str) -> str:
286
+ match = re.search(pattern, text or "", re.IGNORECASE)
287
+ if not match:
288
+ return ""
289
+ value = str(match.group("v")).strip()
290
+ return re.sub(r"^(?:from|to|at)\s+", "", value, flags=re.IGNORECASE).strip()
291
+
292
+ @staticmethod
293
+ def _markdown(brief: dict[str, Any]) -> str:
294
+ overview = brief.get("overview", {})
295
+ lines = [
296
+ f"PROXY: {brief.get('missionType', 'custom')}",
297
+ f"State: {brief.get('state', 'awaitingProxy')}",
298
+ f"Reason: {brief.get('reason', 'humanIntervention')}",
299
+ "",
300
+ f"- Pickup: {overview.get('pickup', '-')}",
301
+ f"- Delivery: {overview.get('delivery', '-')}",
302
+ f"- Deadline: {overview.get('deadline', '-')}",
303
+ f"- Contact: {overview.get('contact', '-')}",
304
+ f"- Item: {overview.get('item', '-')}",
305
+ "",
306
+ "STEPS",
307
+ ]
308
+ for idx, step in enumerate(brief.get("steps", []), start=1):
309
+ lines.append(f"{idx}. {step}")
310
+ return "\n".join(lines)
311
+
312
+ @staticmethod
313
+ def _photoUrls(text: str) -> list[str]:
314
+ return [url.strip() for url in re.findall(r"https?://[^\s)]+", str(text or "")) if url.strip()]
315
+
316
+ @staticmethod
317
+ def _photos(payload: dict[str, Any]) -> list[str]:
318
+ urls = []
319
+ for key in ("photo", "photoUrl", "proofPhoto", "proofUrl", "imageUrl"):
320
+ value = payload.get(key)
321
+ if isinstance(value, str) and value.strip():
322
+ urls.append(value.strip())
323
+ for key in ("photos", "images"):
324
+ values = payload.get(key)
325
+ if isinstance(values, list):
326
+ for value in values:
327
+ if isinstance(value, str) and value.strip():
328
+ urls.append(value.strip())
329
+ elif isinstance(value, dict) and isinstance(value.get("url"), str):
330
+ urls.append(value["url"].strip())
331
+ deduped = []
332
+ for url in urls:
333
+ if url and url not in deduped:
334
+ deduped.append(url)
335
+ return deduped
336
+
337
+ @staticmethod
338
+ def _gps(payload: dict[str, Any]) -> dict[str, float] | None:
339
+ lat = payload.get("lat")
340
+ lng = payload.get("lng", payload.get("lon"))
341
+ if lat is not None and lng is not None:
342
+ return ProxyDomain._coerceGps(lat, lng)
343
+ for key in ("gps", "geo", "location"):
344
+ value = payload.get(key)
345
+ if not isinstance(value, dict):
346
+ continue
347
+ found = ProxyDomain._coerceGps(value.get("lat", value.get("latitude")), value.get("lng", value.get("lon", value.get("longitude"))))
348
+ if found:
349
+ return found
350
+ return None
351
+
352
+ @staticmethod
353
+ def _gpsText(text: str) -> dict[str, float] | None:
354
+ match = re.search(r"(-?\d{1,2}\.\d+)\s*,\s*(-?\d{1,3}\.\d+)", str(text or ""))
355
+ if not match:
356
+ return None
357
+ return ProxyDomain._coerceGps(match.group(1), match.group(2))
358
+
359
+ @staticmethod
360
+ def _coerceGps(lat: Any, lng: Any) -> dict[str, float] | None:
361
+ try:
362
+ latF = float(lat)
363
+ lngF = float(lng)
364
+ except Exception:
365
+ return None
366
+ if -90 <= latF <= 90 and -180 <= lngF <= 180:
367
+ return {"lat": latF, "lng": lngF}
368
+ return None
369
+
370
+ @staticmethod
371
+ def _ref(payload: dict[str, Any], keys: tuple[str, ...]) -> str:
372
+ for key in keys:
373
+ value = payload.get(key)
374
+ if isinstance(value, str) and value.strip():
375
+ return value.strip()
376
+ return ""
377
+
378
+ @staticmethod
379
+ def _receiptText(text: str) -> str:
380
+ match = re.search(r"(?:receipt|order|confirmation)\s*(?:#|no\.?|number)?\s*([A-Z0-9\-]{4,32})", str(text or ""), re.IGNORECASE)
381
+ return str(match.group(1)).strip() if match else ""
382
+
383
+ @staticmethod
384
+ def _trackingText(text: str) -> str:
385
+ match = re.search(r"(?:tracking|tracker)\s*(?:#|id|number)?\s*([A-Z0-9\-]{6,40})", str(text or ""), re.IGNORECASE)
386
+ return str(match.group(1)).strip() if match else ""
387
+
388
+ @staticmethod
389
+ def _cost(payload: dict[str, Any], text: str) -> float:
390
+ for key in ("laborCost", "taskCost", "cost", "amount"):
391
+ try:
392
+ value = float(payload.get(key))
393
+ if value > 0:
394
+ return value
395
+ except (TypeError, ValueError):
396
+ continue
397
+ match = re.search(r"(?:cost|taskCost|laborCost|amount)\s*[:=]\s*\$?\s*(\d+(?:\.\d+)?)|\$\s*(\d+(?:\.\d+)?)", str(text or ""), re.IGNORECASE)
398
+ if not match:
399
+ return 0.0
400
+ return float(match.group(1) or match.group(2) or 0.0)
401
+
402
+ @staticmethod
403
+ def _dict(value: Any) -> dict[str, Any]:
404
+ return value if isinstance(value, dict) else {}
405
+
406
+ @staticmethod
407
+ def _cleanList(value: Any) -> list[str]:
408
+ if not isinstance(value, list):
409
+ return []
410
+ out = []
411
+ for item in value:
412
+ clean = str(item or "").strip()
413
+ if clean and clean not in out:
414
+ out.append(clean)
415
+ return out
416
+
417
+ @staticmethod
418
+ def _regulated(payload: dict[str, Any]) -> bool:
419
+ return ProxyDomain._regulatedRouteType(payload) != "humanOperator"
420
+
421
+ @staticmethod
422
+ def _regulatedRouteType(payload: dict[str, Any]) -> str:
423
+ text = json.dumps(payload, ensure_ascii=True, sort_keys=True).lower()
424
+ if "legal aid" in text or "lawyer" in text or "attorney" in text:
425
+ return "legalAid"
426
+ if "accredited counselor" in text or "counselor" in text:
427
+ return "accreditedCounselor"
428
+ if "licensed consultant" in text or "licensed" in text:
429
+ return "licensedConsultant"
430
+ if any(token in text for token in ("legal", "regulated", "medical", "professional")):
431
+ return "qualifiedHuman"
432
+ return "humanOperator"
433
+
434
+ @staticmethod
435
+ def _sharePolicy(routeType: str) -> dict[str, Any]:
436
+ qualified = routeType in {"qualifiedHuman", "legalAid", "accreditedCounselor", "licensedConsultant"}
437
+ return {
438
+ "allowFullDocs": qualified,
439
+ "allowPaymentDetails": qualified,
440
+ "redactSensitive": True,
441
+ }
442
+
443
+ @staticmethod
444
+ def _fallback(routeType: str) -> dict[str, str]:
445
+ if routeType in {"qualifiedHuman", "legalAid", "accreditedCounselor", "licensedConsultant"}:
446
+ return {"trigger": "slaOverdue", "action": "reassignQualifiedHuman"}
447
+ return {"trigger": "unassigned", "action": "escalateProxyQueue"}
448
+
449
+ @staticmethod
450
+ def _credentials(payload: dict[str, Any], routeType: str) -> dict[str, Any]:
451
+ raw = payload.get("credentials", payload.get("credentialReq", {}))
452
+ raw = raw if isinstance(raw, dict) else {}
453
+ if routeType == "humanOperator":
454
+ return raw
455
+ return {
456
+ "required": True,
457
+ "routeType": routeType,
458
+ "jurisdiction": str(raw.get("jurisdiction", payload.get("jurisdiction", "")) or ""),
459
+ "licenseRef": str(raw.get("licenseRef", raw.get("accreditationRef", "")) or ""),
460
+ "verified": bool(raw.get("verified", False)),
461
+ }
462
+
463
+ @staticmethod
464
+ def _stableId(prefix: str, payload: dict[str, Any]) -> str:
465
+ digest = hashlib.sha256(json.dumps(payload, ensure_ascii=True, sort_keys=True).encode("utf-8")).hexdigest()
466
+ return f"{prefix}_{digest[:24]}"
@@ -0,0 +1,5 @@
1
+ """RESOLVE domain primitives."""
2
+
3
+ from .engine import ResolveDomain
4
+
5
+ __all__ = ["ResolveDomain"]