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.
- apps/__init__.py +2 -0
- apps/api/__init__.py +2 -0
- apps/api/main.py +652 -0
- apps/benchmarks/__init__.py +1 -0
- apps/benchmarks/main.py +20 -0
- apps/sandbox/__init__.py +1 -0
- apps/sandbox/main.py +20 -0
- apps/worker/__init__.py +2 -0
- apps/worker/main.py +15 -0
- apps/worker/verify.py +14 -0
- patchr/__init__.py +12 -0
- patchr/sdk/__init__.py +20 -0
- patchr/sdk/client.py +12 -0
- patchr-0.1.0.dist-info/METADATA +137 -0
- patchr-0.1.0.dist-info/RECORD +116 -0
- patchr-0.1.0.dist-info/WHEEL +5 -0
- patchr-0.1.0.dist-info/entry_points.txt +5 -0
- patchr-0.1.0.dist-info/licenses/LICENSE +17 -0
- patchr-0.1.0.dist-info/top_level.txt +3 -0
- picux/__init__.py +6 -0
- picux/agents/__init__.py +5 -0
- picux/agents/registry.py +204 -0
- picux/api/__init__.py +5 -0
- picux/api/service.py +5075 -0
- picux/audit/__init__.py +31 -0
- picux/audit/activity.py +97 -0
- picux/audit/observability.py +55 -0
- picux/audit/verification/__init__.py +21 -0
- picux/audit/verification/ledger.py +633 -0
- picux/benchmarks/__init__.py +5 -0
- picux/benchmarks/local.py +286 -0
- picux/config.py +140 -0
- picux/contracts/__init__.py +22 -0
- picux/contracts/handshake.py +122 -0
- picux/contracts/integration.py +385 -0
- picux/contracts/openapi.py +187 -0
- picux/contracts/protocol_map.py +152 -0
- picux/contracts/routes.py +980 -0
- picux/contracts/schema_catalog.py +125 -0
- picux/core/__init__.py +17 -0
- picux/core/models.py +148 -0
- picux/core/router.py +131 -0
- picux/core/runtime.py +42 -0
- picux/core/state_machine.py +38 -0
- picux/domains/__init__.py +2 -0
- picux/domains/bridge/HostRun.py +1104 -0
- picux/domains/bridge/__init__.py +6 -0
- picux/domains/bridge/engine.py +345 -0
- picux/domains/hunt/__init__.py +6 -0
- picux/domains/hunt/engine.py +307 -0
- picux/domains/hunt/models.py +88 -0
- picux/domains/pay/__init__.py +16 -0
- picux/domains/pay/adapters.py +607 -0
- picux/domains/pay/engine.py +950 -0
- picux/domains/pay/models.py +95 -0
- picux/domains/proxy/__init__.py +5 -0
- picux/domains/proxy/engine.py +466 -0
- picux/domains/resolve/__init__.py +5 -0
- picux/domains/resolve/engine.py +546 -0
- picux/orchestrator/__init__.py +3 -0
- picux/orchestrator/engine.py +2840 -0
- picux/portals/__init__.py +17 -0
- picux/portals/templates.py +272 -0
- picux/protocols/__init__.py +1 -0
- picux/protocols/a2a/__init__.py +6 -0
- picux/protocols/a2a/client.py +51 -0
- picux/protocols/a2a/envelope.py +132 -0
- picux/protocols/mcp/__init__.py +7 -0
- picux/protocols/mcp/client.py +69 -0
- picux/protocols/mcp/contract.py +67 -0
- picux/protocols/mcp/server.py +76 -0
- picux/sandbox/__init__.py +6 -0
- picux/sandbox/midnight_arbitrage.py +215 -0
- picux/sandbox/models.py +90 -0
- picux/sdk/__init__.py +13 -0
- picux/sdk/client.py +768 -0
- picux/sdk/external.py +245 -0
- picux/security/__init__.py +18 -0
- picux/security/auth.py +86 -0
- picux/security/config_validator.py +58 -0
- picux/security/policy.py +158 -0
- picux/security/secrets.py +144 -0
- picux/signals/__init__.py +1 -0
- picux/signals/community/__init__.py +24 -0
- picux/signals/community/adapters/__init__.py +7 -0
- picux/signals/community/adapters/reddit.py +37 -0
- picux/signals/community/adapters/shopify.py +23 -0
- picux/signals/community/adapters/web.py +23 -0
- picux/signals/community/disambiguation.py +51 -0
- picux/signals/community/intake.py +227 -0
- picux/signals/community/models.py +102 -0
- picux/signals/community/rules.py +91 -0
- picux/signals/community/scoring.py +64 -0
- picux/storage/__init__.py +41 -0
- picux/storage/agents.py +50 -0
- picux/storage/cases.py +440 -0
- picux/storage/channels.py +476 -0
- picux/storage/connectors.py +411 -0
- picux/storage/envelopes.py +137 -0
- picux/storage/escrows.py +168 -0
- picux/storage/events.py +989 -0
- picux/storage/keyspace.py +60 -0
- picux/storage/mandates.py +107 -0
- picux/storage/portals.py +222 -0
- picux/storage/postgres.py +2049 -0
- picux/storage/providers.py +148 -0
- picux/storage/proxy.py +231 -0
- picux/storage/receipts.py +131 -0
- picux/storage/signals.py +147 -0
- picux/storage/tasks.py +179 -0
- picux/tools/__init__.py +11 -0
- picux/tools/shared.py +2048 -0
- picux/verification/__init__.py +5 -0
- picux/verification/rollout.py +183 -0
- picux/workflows/__init__.py +5 -0
- 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,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]}"
|