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,125 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from dataclasses import dataclass
5
+ from pathlib import Path
6
+ from typing import Any
7
+
8
+
9
+ @dataclass(frozen=True)
10
+ class SchemaEntry:
11
+ schemaId: str
12
+ path: str
13
+ domain: str
14
+
15
+ def toMap(self) -> dict[str, Any]:
16
+ schema = _readSchema(self.path)
17
+ return {
18
+ "schemaId": self.schemaId,
19
+ "path": self.path,
20
+ "domain": self.domain,
21
+ "title": str(schema.get("title", self.schemaId) or self.schemaId),
22
+ "schemaUrl": str(schema.get("$id", "") or ""),
23
+ }
24
+
25
+
26
+ SCHEMA_ENTRIES: tuple[SchemaEntry, ...] = (
27
+ SchemaEntry("agentCard", "schemas/agents/agent-card.schema.json", "agents"),
28
+ SchemaEntry("apiStatus", "schemas/runtime/api-status.schema.json", "runtime"),
29
+ SchemaEntry("benchmarkReport", "schemas/benchmarks/local-benchmark-report.schema.json", "benchmarks"),
30
+ SchemaEntry("caseAction", "schemas/cases/case-action.schema.json", "cases"),
31
+ SchemaEntry("caseDeadline", "schemas/cases/case-deadline.schema.json", "cases"),
32
+ SchemaEntry("caseBundleDocument", "schemas/cases/case-bundle-document.schema.json", "cases"),
33
+ SchemaEntry("caseEvent", "schemas/cases/case-event.schema.json", "cases"),
34
+ SchemaEntry("caseNote", "schemas/cases/case-note.schema.json", "cases"),
35
+ SchemaEntry("caseStakeholder", "schemas/cases/case-stakeholder.schema.json", "cases"),
36
+ SchemaEntry("caseWorkspace", "schemas/cases/case-workspace.schema.json", "cases"),
37
+ SchemaEntry("bridgeConnector", "schemas/connectors/bridge-connector.schema.json", "connectors"),
38
+ SchemaEntry("bridgeConnectionSession", "schemas/connectors/bridge-connection-session.schema.json", "connectors"),
39
+ SchemaEntry("bridgePreflight", "schemas/connectors/bridge-preflight.schema.json", "connectors"),
40
+ SchemaEntry("channelState", "schemas/channels/channel-state.schema.json", "channels"),
41
+ SchemaEntry("channelTimeline", "schemas/channels/channel-timeline.schema.json", "channels"),
42
+ SchemaEntry("channelThread", "schemas/channels/channel-thread.schema.json", "channels"),
43
+ SchemaEntry("providerAction", "schemas/channels/provider-action.schema.json", "channels"),
44
+ SchemaEntry("providerCaseSync", "schemas/channels/provider-case-sync.schema.json", "channels"),
45
+ SchemaEntry("providerCallback", "schemas/channels/provider-callback.schema.json", "channels"),
46
+ SchemaEntry("touchpoint", "schemas/channels/touchpoint.schema.json", "channels"),
47
+ SchemaEntry("capabilityGrant", "schemas/security/capability-grant.schema.json", "security"),
48
+ SchemaEntry("credentialScope", "schemas/connectors/credential-scope.schema.json", "connectors"),
49
+ SchemaEntry("evidenceBundle", "schemas/cases/evidence-bundle.schema.json", "cases"),
50
+ SchemaEntry("integrationHandshake", "schemas/contracts/integration-handshake.schema.json", "contract"),
51
+ SchemaEntry("integrationManifest", "schemas/contracts/integration-manifest.schema.json", "contract"),
52
+ SchemaEntry("protocolMap", "schemas/contracts/protocol-map.schema.json", "contract"),
53
+ SchemaEntry("evidenceArtifact", "schemas/audit/evidence-artifact.schema.json", "audit"),
54
+ SchemaEntry("fraudSourceTaxonomy", "schemas/audit/fraud-source-taxonomy.schema.json", "audit"),
55
+ SchemaEntry("proofValue", "schemas/audit/proof-of-value.schema.json", "audit"),
56
+ SchemaEntry("proofCard", "schemas/audit/proof-card.schema.json", "audit"),
57
+ SchemaEntry("proofPack", "schemas/audit/proof-pack.schema.json", "audit"),
58
+ SchemaEntry("vaultEvidence", "schemas/audit/vault-evidence.schema.json", "audit"),
59
+ SchemaEntry("workflowTemplate", "schemas/workflows/workflow-template.schema.json", "workflows"),
60
+ SchemaEntry("caseOpsTimeline", "schemas/proxy/case-ops-timeline.schema.json", "proxy"),
61
+ SchemaEntry("proxyMission", "schemas/proxy/proxy-mission.schema.json", "proxy"),
62
+ SchemaEntry("proxyBrief", "schemas/proxy/proxy-brief.schema.json", "proxy"),
63
+ SchemaEntry("proxyOutcome", "schemas/proxy/proxy-outcome.schema.json", "proxy"),
64
+ SchemaEntry("proxyProof", "schemas/proxy/proxy-proof.schema.json", "proxy"),
65
+ SchemaEntry("proxyRoute", "schemas/proxy/proxy-route.schema.json", "proxy"),
66
+ SchemaEntry("channelMsg", "schemas/proxy/channel-msg.schema.json", "proxy"),
67
+ SchemaEntry("voiceCall", "schemas/proxy/voice-call.schema.json", "proxy"),
68
+ SchemaEntry("communitySignal", "schemas/events/community-signal.schema.json", "signals"),
69
+ SchemaEntry("communitySignalRecord", "schemas/events/community-signal-record.schema.json", "signals"),
70
+ SchemaEntry("integrationEvent", "schemas/events/integration-event.schema.json", "events"),
71
+ SchemaEntry("eventSubscription", "schemas/events/event-subscription.schema.json", "events"),
72
+ SchemaEntry("eventDelivery", "schemas/events/event-delivery.schema.json", "events"),
73
+ SchemaEntry("mandateRecord", "schemas/mandates/mandate-record.schema.json", "mandates"),
74
+ SchemaEntry("paymentMandate", "schemas/mandates/payment.schema.json", "mandates"),
75
+ SchemaEntry("procurementMandate", "schemas/mandates/procurement.schema.json", "mandates"),
76
+ SchemaEntry("payRail", "schemas/pay/pay-rail.schema.json", "pay"),
77
+ SchemaEntry("paymentChallenge", "schemas/pay/payment-challenge.schema.json", "pay"),
78
+ SchemaEntry("paymentCredential", "schemas/pay/payment-credential.schema.json", "pay"),
79
+ SchemaEntry("paymentSession", "schemas/pay/payment-session.schema.json", "pay"),
80
+ SchemaEntry("portalAction", "schemas/portals/portal-action.schema.json", "portals"),
81
+ SchemaEntry("portalActionTemplate", "schemas/portals/portal-action-template.schema.json", "portals"),
82
+ SchemaEntry("portalProof", "schemas/portals/portal-proof.schema.json", "portals"),
83
+ SchemaEntry("portalPlaybook", "schemas/portals/portal-playbook.schema.json", "portals"),
84
+ SchemaEntry("portalSession", "schemas/portals/portal-session.schema.json", "portals"),
85
+ SchemaEntry("portalSessionPreflight", "schemas/portals/portal-session-preflight.schema.json", "portals"),
86
+ SchemaEntry("portalStep", "schemas/portals/portal-step.schema.json", "portals"),
87
+ SchemaEntry("usageMandate", "schemas/pay/usage-mandate.schema.json", "pay"),
88
+ SchemaEntry("escrowMission", "schemas/pay/escrow-mission.schema.json", "pay"),
89
+ SchemaEntry("disputeCase", "schemas/pay/dispute-case.schema.json", "pay"),
90
+ SchemaEntry("reputationCard", "schemas/pay/reputation-card.schema.json", "pay"),
91
+ SchemaEntry("escrowRecord", "schemas/pay/escrow-record.schema.json", "pay"),
92
+ SchemaEntry("settlementAdapter", "schemas/pay/settlement-adapter.schema.json", "pay"),
93
+ SchemaEntry("settlementInstruction", "schemas/pay/settlement-instruction.schema.json", "pay"),
94
+ SchemaEntry("a2aEnvelope", "schemas/protocols/a2a-envelope.schema.json", "a2a"),
95
+ SchemaEntry("a2aEnvelopeRecord", "schemas/protocols/a2a-envelope-record.schema.json", "a2a"),
96
+ SchemaEntry("settlementReceipt", "schemas/receipts/settlement-receipt.schema.json", "receipts"),
97
+ SchemaEntry("mandateSession", "schemas/security/mandate-session.schema.json", "security"),
98
+ SchemaEntry("policyDecision", "schemas/security/policy-decision.schema.json", "security"),
99
+ SchemaEntry("secretCheck", "schemas/security/secret-check.schema.json", "security"),
100
+ SchemaEntry("sandboxRun", "schemas/sandbox/midnight-arbitrage-run.schema.json", "sandbox"),
101
+ SchemaEntry("protocolTask", "schemas/tasks/protocol-task.schema.json", "tasks"),
102
+ )
103
+
104
+
105
+ def schemaCatalog() -> dict[str, Any]:
106
+ entries = [entry.toMap() for entry in SCHEMA_ENTRIES]
107
+ return {"ok": True, "schemas": entries, "count": len(entries)}
108
+
109
+
110
+ def getSchema(schemaId: str) -> dict[str, Any]:
111
+ requested = str(schemaId or "")
112
+ entry = next((item for item in SCHEMA_ENTRIES if item.schemaId == requested), None)
113
+ if entry is None:
114
+ return {"ok": False, "error": "schemaNotFound", "schemaId": requested}
115
+ return {"ok": True, "schemaId": entry.schemaId, "schema": _readSchema(entry.path), "meta": entry.toMap()}
116
+
117
+
118
+ def _readSchema(path: str) -> dict[str, Any]:
119
+ raw = (_repoRoot() / path).read_text(encoding="utf-8")
120
+ value = json.loads(raw)
121
+ return value if isinstance(value, dict) else {}
122
+
123
+
124
+ def _repoRoot() -> Path:
125
+ return Path(__file__).resolve().parents[3]
picux/core/__init__.py ADDED
@@ -0,0 +1,17 @@
1
+ """Core Picux runtime primitives."""
2
+
3
+ from .models import Domain, ProtocolTask, ProtocolTaskStatus, ProtocolUser
4
+ from .router import PicuxIntentRouter, RouteDecision
5
+ from .runtime import PicuxRuntime
6
+ from .state_machine import PicuxStateMachine
7
+
8
+ __all__ = [
9
+ "PicuxIntentRouter",
10
+ "PicuxRuntime",
11
+ "PicuxStateMachine",
12
+ "Domain",
13
+ "ProtocolTask",
14
+ "ProtocolTaskStatus",
15
+ "ProtocolUser",
16
+ "RouteDecision",
17
+ ]
picux/core/models.py ADDED
@@ -0,0 +1,148 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import time
5
+ from dataclasses import dataclass, field
6
+ from enum import Enum
7
+ from typing import Any, Optional
8
+
9
+
10
+ class Domain(str, Enum):
11
+ HUNT = "hunt"
12
+ RESOLVE = "resolve"
13
+ BRIDGE = "bridge"
14
+ PAY = "pay"
15
+ PROXY = "proxy"
16
+ SIGNALS = "signals"
17
+ AUDIT = "audit"
18
+ UNKNOWN = "unknown"
19
+
20
+
21
+ class ProtocolTaskStatus(str, Enum):
22
+ PENDING = "pending"
23
+ PROCESSING = "processing"
24
+ AWAITING_PROXY = "awaitingProxy"
25
+ NEEDS_APPROVAL = "needs_approval"
26
+ COMPLETED = "completed"
27
+ FAILED = "failed"
28
+ CANCELED = "canceled"
29
+ FROZEN = "frozen"
30
+
31
+
32
+ def _to_int(raw: Any, default: int = 0) -> int:
33
+ try:
34
+ return int(raw)
35
+ except Exception:
36
+ return default
37
+
38
+
39
+ def _to_bool(raw: Any) -> bool:
40
+ return str(raw).strip().lower() in {"1", "true", "yes", "on"}
41
+
42
+
43
+ def _json_loads(raw: Any, default: Any) -> Any:
44
+ if raw in ("", None):
45
+ return default
46
+ try:
47
+ return json.loads(str(raw))
48
+ except Exception:
49
+ return default
50
+
51
+
52
+ @dataclass
53
+ class ProtocolUser:
54
+ userId: str
55
+ channel: str = "api"
56
+ trust: int = 1
57
+ refs: dict[str, str] = field(default_factory=dict)
58
+ prefs: dict[str, Any] = field(default_factory=dict)
59
+ meta: dict[str, Any] = field(default_factory=dict)
60
+ createdAt: int = field(default_factory=lambda: int(time.time()))
61
+ updatedAt: int = field(default_factory=lambda: int(time.time()))
62
+
63
+ def toMap(self) -> dict[str, str]:
64
+ return {
65
+ "userId": self.userId,
66
+ "channel": self.channel,
67
+ "trust": str(self.trust),
68
+ "refs": json.dumps(self.refs, ensure_ascii=True, sort_keys=True),
69
+ "prefs": json.dumps(self.prefs, ensure_ascii=True, sort_keys=True),
70
+ "meta": json.dumps(self.meta, ensure_ascii=True, sort_keys=True),
71
+ "createdAt": str(self.createdAt),
72
+ "updatedAt": str(self.updatedAt),
73
+ }
74
+
75
+ @classmethod
76
+ def fromMap(cls, data: dict[str, Any]) -> "ProtocolUser":
77
+ return cls(
78
+ userId=str(data.get("userId", "")),
79
+ channel=str(data.get("channel", "api") or "api"),
80
+ trust=_to_int(data.get("trust", 1), 1),
81
+ refs=_json_loads(data.get("refs"), {}),
82
+ prefs=_json_loads(data.get("prefs"), {}),
83
+ meta=_json_loads(data.get("meta"), {}),
84
+ createdAt=_to_int(data.get("createdAt", 0), 0),
85
+ updatedAt=_to_int(data.get("updatedAt", 0), 0),
86
+ )
87
+
88
+
89
+ @dataclass
90
+ class ProtocolTask:
91
+ taskId: str
92
+ userId: str
93
+ domain: Domain
94
+ status: ProtocolTaskStatus
95
+ channel: str = "api"
96
+ inData: dict[str, Any] = field(default_factory=dict)
97
+ outData: dict[str, Any] = field(default_factory=dict)
98
+ needsApproval: bool = False
99
+ mandateId: str = ""
100
+ extRef: str = ""
101
+ errorMsg: str = ""
102
+ meta: dict[str, Any] = field(default_factory=dict)
103
+ createdAt: int = field(default_factory=lambda: int(time.time()))
104
+ updatedAt: int = field(default_factory=lambda: int(time.time()))
105
+ approvalBy: Optional[int] = None
106
+
107
+ def toMap(self) -> dict[str, str]:
108
+ return {
109
+ "taskId": self.taskId,
110
+ "userId": self.userId,
111
+ "domain": self.domain.value,
112
+ "status": self.status.value,
113
+ "channel": self.channel,
114
+ "inData": json.dumps(self.inData, ensure_ascii=True, sort_keys=True),
115
+ "outData": json.dumps(self.outData, ensure_ascii=True, sort_keys=True),
116
+ "needsApproval": "1" if self.needsApproval else "0",
117
+ "mandateId": self.mandateId,
118
+ "extRef": self.extRef,
119
+ "errorMsg": self.errorMsg,
120
+ "meta": json.dumps(self.meta, ensure_ascii=True, sort_keys=True),
121
+ "createdAt": str(self.createdAt),
122
+ "updatedAt": str(self.updatedAt),
123
+ "approvalBy": "" if self.approvalBy is None else str(self.approvalBy),
124
+ }
125
+
126
+ @classmethod
127
+ def fromMap(cls, data: dict[str, Any]) -> "ProtocolTask":
128
+ domain = str(data.get("domain", Domain.UNKNOWN.value))
129
+ status = str(data.get("status", ProtocolTaskStatus.PENDING.value))
130
+ return cls(
131
+ taskId=str(data.get("taskId", "")),
132
+ userId=str(data.get("userId", "")),
133
+ domain=Domain(domain) if domain in Domain._value2member_map_ else Domain.UNKNOWN,
134
+ status=ProtocolTaskStatus(status)
135
+ if status in ProtocolTaskStatus._value2member_map_
136
+ else ProtocolTaskStatus.PENDING,
137
+ channel=str(data.get("channel", "api") or "api"),
138
+ inData=_json_loads(data.get("inData"), {}),
139
+ outData=_json_loads(data.get("outData"), {}),
140
+ needsApproval=_to_bool(data.get("needsApproval", "0")),
141
+ mandateId=str(data.get("mandateId", "")),
142
+ extRef=str(data.get("extRef", "")),
143
+ errorMsg=str(data.get("errorMsg", "")),
144
+ meta=_json_loads(data.get("meta"), {}),
145
+ createdAt=_to_int(data.get("createdAt", 0), 0),
146
+ updatedAt=_to_int(data.get("updatedAt", 0), 0),
147
+ approvalBy=_to_int(data.get("approvalBy", ""), 0) or None,
148
+ )
picux/core/router.py ADDED
@@ -0,0 +1,131 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ from dataclasses import dataclass
5
+
6
+ from picux.core.models import Domain
7
+
8
+
9
+ @dataclass(frozen=True)
10
+ class RouteDecision:
11
+ domain: Domain
12
+ reason: str
13
+
14
+
15
+ class PicuxIntentRouter:
16
+ """Deterministic scaffold router for Picux protocol domains."""
17
+
18
+ resolve_keywords = {
19
+ "invoice",
20
+ "bill",
21
+ "fee",
22
+ "refund",
23
+ "dispute",
24
+ "claim",
25
+ "complaint",
26
+ "damaged",
27
+ "broken",
28
+ "defective",
29
+ "in-transit",
30
+ "in transit",
31
+ "transit",
32
+ "followup",
33
+ "follow up",
34
+ "customer representative",
35
+ "overcharge",
36
+ "sla",
37
+ "cancel subscription",
38
+ "processing fee",
39
+ "regulatory recovery",
40
+ "evidence",
41
+ "verification",
42
+ }
43
+ bridge_keywords = {
44
+ "api",
45
+ "webhook",
46
+ "erp",
47
+ "connector",
48
+ "integration",
49
+ "slack",
50
+ "whatsapp",
51
+ "email",
52
+ "mcp",
53
+ "a2a",
54
+ }
55
+ proxy_keywords = {
56
+ "pick up",
57
+ "pickup",
58
+ "physically",
59
+ "physical",
60
+ "drop off",
61
+ "courier",
62
+ "runner",
63
+ "deliver",
64
+ "errand",
65
+ "human",
66
+ "human review",
67
+ "oversight",
68
+ "field",
69
+ "call",
70
+ "phone",
71
+ "subjective",
72
+ }
73
+ pay_keywords = {
74
+ "mandate",
75
+ "escrow",
76
+ "settle",
77
+ "settlement",
78
+ "payment",
79
+ "pay",
80
+ "receipt",
81
+ "proof of value",
82
+ }
83
+ signal_keywords = {
84
+ "reddit",
85
+ "community",
86
+ "public signal",
87
+ "signals",
88
+ "harness",
89
+ "swarm core",
90
+ }
91
+ hunt_keywords = {
92
+ "zillow",
93
+ "redfin",
94
+ "ebay",
95
+ "amazon",
96
+ "listing",
97
+ "price drop",
98
+ "deal",
99
+ "hunt",
100
+ "under $",
101
+ "arbitrage",
102
+ "vendor",
103
+ }
104
+
105
+ def classify(self, query: str) -> RouteDecision:
106
+ text = (query or "").strip().lower()
107
+ if not text:
108
+ return RouteDecision(Domain.UNKNOWN, "empty_query")
109
+ if _matchesAny(text, self.pay_keywords):
110
+ return RouteDecision(Domain.PAY, "pay_keyword_match")
111
+ if _matchesAny(text, self.resolve_keywords):
112
+ return RouteDecision(Domain.RESOLVE, "resolve_keyword_match")
113
+ if _matchesAny(text, self.proxy_keywords):
114
+ return RouteDecision(Domain.PROXY, "proxy_keyword_match")
115
+ if _matchesAny(text, self.bridge_keywords):
116
+ return RouteDecision(Domain.BRIDGE, "bridge_keyword_match")
117
+ if _matchesAny(text, self.signal_keywords):
118
+ return RouteDecision(Domain.SIGNALS, "signal_keyword_match")
119
+ if _matchesAny(text, self.hunt_keywords):
120
+ return RouteDecision(Domain.HUNT, "hunt_keyword_match")
121
+ return RouteDecision(Domain.HUNT, "default_to_hunt")
122
+
123
+
124
+ def _matchesAny(text: str, keywords: set[str]) -> bool:
125
+ for token in keywords:
126
+ if re.fullmatch(r"[a-z0-9]+", token):
127
+ if re.search(rf"\b{re.escape(token)}\b", text):
128
+ return True
129
+ elif token in text:
130
+ return True
131
+ return False
picux/core/runtime.py ADDED
@@ -0,0 +1,42 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Optional
5
+
6
+ from picux import __version__
7
+ from picux.config import PicuxSettings
8
+ from picux.security import assertProductionReady, validateProductionReady
9
+
10
+
11
+ @dataclass(frozen=True)
12
+ class PicuxRuntime:
13
+ service: str = "picux"
14
+ settings: Optional[PicuxSettings] = None
15
+
16
+ def settingsObj(self) -> PicuxSettings:
17
+ return self.settings or PicuxSettings.fromEnv()
18
+
19
+ def health(self) -> dict[str, object]:
20
+ settings = self.settingsObj()
21
+ readiness = validateProductionReady(settings)
22
+ return {
23
+ "ok": True,
24
+ "svc": self.service,
25
+ "ver": __version__,
26
+ "phase": "scaffold",
27
+ "env": settings.env,
28
+ "prod": settings.prod,
29
+ "ready": readiness.ok,
30
+ }
31
+
32
+ def summary(self) -> dict[str, object]:
33
+ settings = self.settingsObj()
34
+ return {
35
+ **self.health(),
36
+ "settings": settings.publicSummary(),
37
+ "configSources": settings.sources,
38
+ "productionReadiness": validateProductionReady(settings).toMap(),
39
+ }
40
+
41
+ def assertBootable(self) -> None:
42
+ assertProductionReady(self.settingsObj())
@@ -0,0 +1,38 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from picux.core.models import Domain
6
+ from picux.core.router import PicuxIntentRouter
7
+
8
+
9
+ class PicuxStateMachine:
10
+ """Minimal domain state machine for Phase 2 extraction."""
11
+
12
+ def __init__(self, router: PicuxIntentRouter | None = None) -> None:
13
+ self.router = router or PicuxIntentRouter()
14
+
15
+ def route(self, query: str) -> dict[str, Any]:
16
+ decision = self.router.classify(query)
17
+ return {
18
+ "query": query,
19
+ "domain": decision.domain.value,
20
+ "routeReason": decision.reason,
21
+ "phase": self._phase_forDomain(decision.domain),
22
+ }
23
+
24
+ @staticmethod
25
+ def _phase_forDomain(domain: Domain) -> str:
26
+ if domain == Domain.HUNT:
27
+ return "discovery"
28
+ if domain == Domain.RESOLVE:
29
+ return "orchestration"
30
+ if domain == Domain.BRIDGE:
31
+ return "connectivity"
32
+ if domain == Domain.PAY:
33
+ return "settlement"
34
+ if domain == Domain.SIGNALS:
35
+ return "signal_intake"
36
+ if domain == Domain.AUDIT:
37
+ return "audit"
38
+ return "unknown"
@@ -0,0 +1,2 @@
1
+ """Picux protocol domains."""
2
+