open-research-protocol 0.4.24 → 0.4.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,205 @@
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import json
5
+ from pathlib import Path
6
+ import subprocess
7
+ import sys
8
+ from typing import Any
9
+
10
+
11
+ ROOT = Path(__file__).resolve().parents[1]
12
+ CLI = ROOT / "cli" / "orp.py"
13
+
14
+
15
+ TOOLS: list[dict[str, Any]] = [
16
+ {
17
+ "name": "orp_research_ask",
18
+ "description": "Create an ORP OpenAI research-loop run with high-reasoning, web-synthesis, and deep-research call moments. Dry-run by default; set execute=true for live provider lanes.",
19
+ "inputSchema": {
20
+ "type": "object",
21
+ "required": ["question"],
22
+ "properties": {
23
+ "question": {"type": "string", "minLength": 1},
24
+ "repo_root": {"type": "string", "description": "Repository root. Defaults to current directory."},
25
+ "run_id": {"type": "string"},
26
+ "profile": {"type": "string", "default": "openai-council"},
27
+ "profile_file": {"type": "string"},
28
+ "execute": {"type": "boolean", "default": False},
29
+ "lane_fixtures": {
30
+ "type": "object",
31
+ "description": "Mapping of lane_id to fixture path.",
32
+ "additionalProperties": {"type": "string"},
33
+ },
34
+ "chimera_bin": {"type": "string", "default": "chimera"},
35
+ "timeout_sec": {"type": "integer", "minimum": 1, "default": 120},
36
+ },
37
+ },
38
+ },
39
+ {
40
+ "name": "orp_research_status",
41
+ "description": "Show status and lane summary for an ORP research council run.",
42
+ "inputSchema": {
43
+ "type": "object",
44
+ "properties": {
45
+ "repo_root": {"type": "string", "description": "Repository root. Defaults to current directory."},
46
+ "run_id": {"type": "string", "default": "latest"},
47
+ },
48
+ },
49
+ },
50
+ {
51
+ "name": "orp_research_show",
52
+ "description": "Show an ORP research council answer payload.",
53
+ "inputSchema": {
54
+ "type": "object",
55
+ "properties": {
56
+ "repo_root": {"type": "string", "description": "Repository root. Defaults to current directory."},
57
+ "run_id": {"type": "string", "default": "latest"},
58
+ },
59
+ },
60
+ },
61
+ ]
62
+
63
+
64
+ def _json_response(request_id: Any, result: Any | None = None, error: Any | None = None) -> dict[str, Any]:
65
+ response: dict[str, Any] = {"jsonrpc": "2.0", "id": request_id}
66
+ if error is not None:
67
+ response["error"] = error
68
+ else:
69
+ response["result"] = result if result is not None else {}
70
+ return response
71
+
72
+
73
+ def _write(payload: dict[str, Any]) -> None:
74
+ sys.stdout.write(json.dumps(payload, separators=(",", ":")) + "\n")
75
+ sys.stdout.flush()
76
+
77
+
78
+ def _text_result(text: str, *, is_error: bool = False) -> dict[str, Any]:
79
+ return {
80
+ "content": [{"type": "text", "text": text}],
81
+ "isError": is_error,
82
+ }
83
+
84
+
85
+ def _repo_root(args: dict[str, Any]) -> str:
86
+ raw = str(args.get("repo_root", "") or "").strip()
87
+ return raw or "."
88
+
89
+
90
+ def _run_orp(args: list[str]) -> tuple[int, str, str]:
91
+ proc = subprocess.run(
92
+ [sys.executable, str(CLI), *args],
93
+ capture_output=True,
94
+ text=True,
95
+ cwd=str(ROOT),
96
+ )
97
+ return proc.returncode, proc.stdout, proc.stderr
98
+
99
+
100
+ def _call_research_ask(args: dict[str, Any]) -> dict[str, Any]:
101
+ question = str(args.get("question", "") or "").strip()
102
+ if not question:
103
+ return _text_result("question is required", is_error=True)
104
+ cmd = ["--repo-root", _repo_root(args), "research", "ask", question]
105
+ profile = str(args.get("profile", "") or "").strip()
106
+ if profile:
107
+ cmd.extend(["--profile", profile])
108
+ profile_file = str(args.get("profile_file", "") or "").strip()
109
+ if profile_file:
110
+ cmd.extend(["--profile-file", profile_file])
111
+ run_id = str(args.get("run_id", "") or "").strip()
112
+ if run_id:
113
+ cmd.extend(["--run-id", run_id])
114
+ if bool(args.get("execute", False)):
115
+ cmd.append("--execute")
116
+ lane_fixtures = args.get("lane_fixtures")
117
+ if isinstance(lane_fixtures, dict):
118
+ for lane_id, path in lane_fixtures.items():
119
+ cmd.extend(["--lane-fixture", f"{lane_id}={path}"])
120
+ chimera_bin = str(args.get("chimera_bin", "") or "").strip()
121
+ if chimera_bin:
122
+ cmd.extend(["--chimera-bin", chimera_bin])
123
+ timeout_sec = args.get("timeout_sec")
124
+ if timeout_sec is not None:
125
+ cmd.extend(["--timeout-sec", str(timeout_sec)])
126
+ cmd.append("--json")
127
+ code, stdout, stderr = _run_orp(cmd)
128
+ if code != 0:
129
+ return _text_result((stderr or stdout or f"orp exited {code}").strip(), is_error=True)
130
+ return _text_result(stdout.strip())
131
+
132
+
133
+ def _call_research_status(args: dict[str, Any]) -> dict[str, Any]:
134
+ run_id = str(args.get("run_id", "") or "latest").strip() or "latest"
135
+ cmd = ["--repo-root", _repo_root(args), "research", "status", run_id, "--json"]
136
+ code, stdout, stderr = _run_orp(cmd)
137
+ if code != 0:
138
+ return _text_result((stderr or stdout or f"orp exited {code}").strip(), is_error=True)
139
+ return _text_result(stdout.strip())
140
+
141
+
142
+ def _call_research_show(args: dict[str, Any]) -> dict[str, Any]:
143
+ run_id = str(args.get("run_id", "") or "latest").strip() or "latest"
144
+ cmd = ["--repo-root", _repo_root(args), "research", "show", run_id, "--json"]
145
+ code, stdout, stderr = _run_orp(cmd)
146
+ if code != 0:
147
+ return _text_result((stderr or stdout or f"orp exited {code}").strip(), is_error=True)
148
+ return _text_result(stdout.strip())
149
+
150
+
151
+ def _handle(request: dict[str, Any]) -> dict[str, Any] | None:
152
+ request_id = request.get("id")
153
+ method = str(request.get("method", "") or "")
154
+ if method.startswith("notifications/"):
155
+ return None
156
+ if method == "initialize":
157
+ return _json_response(
158
+ request_id,
159
+ {
160
+ "protocolVersion": str(request.get("params", {}).get("protocolVersion", "2024-11-05")),
161
+ "capabilities": {"tools": {}},
162
+ "serverInfo": {"name": "orp-research", "version": "0.1.0"},
163
+ },
164
+ )
165
+ if method == "tools/list":
166
+ return _json_response(request_id, {"tools": TOOLS})
167
+ if method == "tools/call":
168
+ params = request.get("params")
169
+ if not isinstance(params, dict):
170
+ return _json_response(request_id, error={"code": -32602, "message": "params must be an object"})
171
+ name = str(params.get("name", "") or "")
172
+ arguments = params.get("arguments")
173
+ if not isinstance(arguments, dict):
174
+ arguments = {}
175
+ if name == "orp_research_ask":
176
+ return _json_response(request_id, _call_research_ask(arguments))
177
+ if name == "orp_research_status":
178
+ return _json_response(request_id, _call_research_status(arguments))
179
+ if name == "orp_research_show":
180
+ return _json_response(request_id, _call_research_show(arguments))
181
+ return _json_response(request_id, error={"code": -32601, "message": f"unknown tool: {name}"})
182
+ return _json_response(request_id, error={"code": -32601, "message": f"unknown method: {method}"})
183
+
184
+
185
+ def main() -> int:
186
+ for line in sys.stdin:
187
+ text = line.strip()
188
+ if not text:
189
+ continue
190
+ try:
191
+ request = json.loads(text)
192
+ except Exception as exc:
193
+ _write(_json_response(None, error={"code": -32700, "message": str(exc)}))
194
+ continue
195
+ if not isinstance(request, dict):
196
+ _write(_json_response(None, error={"code": -32600, "message": "request must be an object"}))
197
+ continue
198
+ response = _handle(request)
199
+ if response is not None:
200
+ _write(response)
201
+ return 0
202
+
203
+
204
+ if __name__ == "__main__":
205
+ raise SystemExit(main())
@@ -0,0 +1,223 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://openresearchprotocol.com/spec/v1/project-context.schema.json",
4
+ "title": "ORP Project Context",
5
+ "description": "Machine-readable ORP process artifact describing a local directory's authority surfaces, directory signals, research timing policy, and evolution policy.",
6
+ "type": "object",
7
+ "additionalProperties": true,
8
+ "required": [
9
+ "schema_version",
10
+ "kind",
11
+ "project",
12
+ "initialized_at_utc",
13
+ "refreshed_at_utc",
14
+ "refresh_source",
15
+ "authority_surfaces",
16
+ "directory_signals",
17
+ "research_policy",
18
+ "evolution_policy",
19
+ "next_actions",
20
+ "notes"
21
+ ],
22
+ "properties": {
23
+ "schema_version": {
24
+ "const": "1.0.0"
25
+ },
26
+ "kind": {
27
+ "const": "orp_project_context"
28
+ },
29
+ "project": {
30
+ "type": "object",
31
+ "required": [
32
+ "name",
33
+ "root"
34
+ ],
35
+ "properties": {
36
+ "name": {
37
+ "type": "string",
38
+ "minLength": 1
39
+ },
40
+ "root": {
41
+ "type": "string",
42
+ "minLength": 1
43
+ }
44
+ }
45
+ },
46
+ "initialized_at_utc": {
47
+ "type": "string",
48
+ "minLength": 1
49
+ },
50
+ "refreshed_at_utc": {
51
+ "type": "string",
52
+ "minLength": 1
53
+ },
54
+ "refresh_source": {
55
+ "type": "string",
56
+ "minLength": 1
57
+ },
58
+ "authority_surfaces": {
59
+ "type": "array",
60
+ "items": {
61
+ "type": "object",
62
+ "required": [
63
+ "path",
64
+ "kind",
65
+ "role",
66
+ "exists",
67
+ "size_bytes"
68
+ ],
69
+ "properties": {
70
+ "path": {
71
+ "type": "string",
72
+ "minLength": 1
73
+ },
74
+ "kind": {
75
+ "type": "string",
76
+ "minLength": 1
77
+ },
78
+ "role": {
79
+ "type": "string",
80
+ "minLength": 1
81
+ },
82
+ "exists": {
83
+ "type": "boolean"
84
+ },
85
+ "size_bytes": {
86
+ "type": "integer",
87
+ "minimum": 0
88
+ }
89
+ }
90
+ }
91
+ },
92
+ "directory_signals": {
93
+ "type": "object",
94
+ "required": [
95
+ "source_dirs",
96
+ "languages_or_stacks",
97
+ "has_tests",
98
+ "has_docs",
99
+ "has_orp_config",
100
+ "authority_surface_count"
101
+ ],
102
+ "properties": {
103
+ "source_dirs": {
104
+ "type": "array",
105
+ "items": {
106
+ "type": "string"
107
+ }
108
+ },
109
+ "languages_or_stacks": {
110
+ "type": "array",
111
+ "items": {
112
+ "type": "string"
113
+ }
114
+ },
115
+ "has_tests": {
116
+ "type": "boolean"
117
+ },
118
+ "has_docs": {
119
+ "type": "boolean"
120
+ },
121
+ "has_orp_config": {
122
+ "type": "boolean"
123
+ },
124
+ "authority_surface_count": {
125
+ "type": "integer",
126
+ "minimum": 0
127
+ }
128
+ }
129
+ },
130
+ "research_policy": {
131
+ "type": "object",
132
+ "required": [
133
+ "default_timing",
134
+ "provider_calls_are_explicit",
135
+ "live_calls_require_execute",
136
+ "call_moments"
137
+ ],
138
+ "properties": {
139
+ "default_timing": {
140
+ "type": "string",
141
+ "minLength": 1
142
+ },
143
+ "provider_calls_are_explicit": {
144
+ "type": "boolean"
145
+ },
146
+ "live_calls_require_execute": {
147
+ "type": "boolean"
148
+ },
149
+ "secret_alias": {
150
+ "type": "string"
151
+ },
152
+ "env_var": {
153
+ "type": "string"
154
+ },
155
+ "call_moments": {
156
+ "type": "array",
157
+ "items": {
158
+ "type": "object",
159
+ "required": [
160
+ "moment_id",
161
+ "calls_api",
162
+ "when"
163
+ ],
164
+ "properties": {
165
+ "moment_id": {
166
+ "type": "string",
167
+ "minLength": 1
168
+ },
169
+ "calls_api": {
170
+ "type": "boolean"
171
+ },
172
+ "lane": {
173
+ "type": "string"
174
+ },
175
+ "model": {
176
+ "type": "string"
177
+ },
178
+ "when": {
179
+ "type": "string",
180
+ "minLength": 1
181
+ },
182
+ "capability_note": {
183
+ "type": "string"
184
+ }
185
+ }
186
+ }
187
+ },
188
+ "skip_research_when": {
189
+ "type": "array",
190
+ "items": {
191
+ "type": "string"
192
+ }
193
+ },
194
+ "escalate_to_deep_research_when": {
195
+ "type": "array",
196
+ "items": {
197
+ "type": "string"
198
+ }
199
+ }
200
+ }
201
+ },
202
+ "evolution_policy": {
203
+ "type": "object",
204
+ "required": [
205
+ "refresh_surfaces",
206
+ "evolution_loop",
207
+ "boundary"
208
+ ]
209
+ },
210
+ "next_actions": {
211
+ "type": "array",
212
+ "items": {
213
+ "type": "string"
214
+ }
215
+ },
216
+ "notes": {
217
+ "type": "array",
218
+ "items": {
219
+ "type": "string"
220
+ }
221
+ }
222
+ }
223
+ }
@@ -0,0 +1,245 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://openresearchprotocol.com/spec/v1/research-run.schema.json",
4
+ "title": "ORP Research Run",
5
+ "description": "Machine-readable ORP artifact for a durable multi-lane research council run.",
6
+ "type": "object",
7
+ "additionalProperties": true,
8
+ "required": [
9
+ "schema_version",
10
+ "kind",
11
+ "run_id",
12
+ "status",
13
+ "question",
14
+ "execute",
15
+ "generated_at_utc",
16
+ "profile",
17
+ "breakdown",
18
+ "lanes",
19
+ "synthesis",
20
+ "artifacts",
21
+ "notes"
22
+ ],
23
+ "properties": {
24
+ "schema_version": {
25
+ "const": "1.0.0"
26
+ },
27
+ "kind": {
28
+ "const": "research_run"
29
+ },
30
+ "run_id": {
31
+ "type": "string",
32
+ "minLength": 1
33
+ },
34
+ "status": {
35
+ "type": "string",
36
+ "enum": [
37
+ "planned",
38
+ "in_progress",
39
+ "partial",
40
+ "complete"
41
+ ]
42
+ },
43
+ "question": {
44
+ "type": "string",
45
+ "minLength": 1
46
+ },
47
+ "execute": {
48
+ "type": "boolean"
49
+ },
50
+ "generated_at_utc": {
51
+ "type": "string",
52
+ "minLength": 1
53
+ },
54
+ "profile": {
55
+ "type": "object",
56
+ "required": [
57
+ "profile_id",
58
+ "lane_count"
59
+ ],
60
+ "properties": {
61
+ "profile_id": {
62
+ "type": "string",
63
+ "minLength": 1
64
+ },
65
+ "label": {
66
+ "type": "string"
67
+ },
68
+ "lane_count": {
69
+ "type": "integer",
70
+ "minimum": 0
71
+ }
72
+ }
73
+ },
74
+ "call_moments": {
75
+ "type": "array",
76
+ "items": {
77
+ "type": "object",
78
+ "required": [
79
+ "moment_id",
80
+ "calls_api"
81
+ ],
82
+ "properties": {
83
+ "moment_id": {
84
+ "type": "string",
85
+ "minLength": 1
86
+ },
87
+ "label": {
88
+ "type": "string"
89
+ },
90
+ "calls_api": {
91
+ "type": "boolean"
92
+ },
93
+ "secret_alias": {
94
+ "type": "string"
95
+ },
96
+ "env_var": {
97
+ "type": "string"
98
+ },
99
+ "description": {
100
+ "type": "string"
101
+ }
102
+ }
103
+ }
104
+ },
105
+ "breakdown": {
106
+ "type": "object",
107
+ "required": [
108
+ "question",
109
+ "sequence",
110
+ "output_contract",
111
+ "lanes"
112
+ ]
113
+ },
114
+ "lanes": {
115
+ "type": "array",
116
+ "items": {
117
+ "type": "object",
118
+ "required": [
119
+ "lane_id",
120
+ "provider",
121
+ "model",
122
+ "adapter",
123
+ "status",
124
+ "started_at_utc",
125
+ "finished_at_utc",
126
+ "text"
127
+ ],
128
+ "properties": {
129
+ "lane_id": {
130
+ "type": "string",
131
+ "minLength": 1
132
+ },
133
+ "label": {
134
+ "type": "string"
135
+ },
136
+ "provider": {
137
+ "type": "string"
138
+ },
139
+ "model": {
140
+ "type": "string"
141
+ },
142
+ "adapter": {
143
+ "type": "string"
144
+ },
145
+ "call_moment": {
146
+ "type": "string"
147
+ },
148
+ "api_call": {
149
+ "type": "object",
150
+ "properties": {
151
+ "call_moment": {
152
+ "type": "string"
153
+ },
154
+ "called": {
155
+ "type": "boolean"
156
+ },
157
+ "secret_alias": {
158
+ "type": "string"
159
+ },
160
+ "env_var": {
161
+ "type": "string"
162
+ },
163
+ "secret_source": {
164
+ "type": "string"
165
+ },
166
+ "secret_value_persisted": {
167
+ "type": "boolean"
168
+ }
169
+ }
170
+ },
171
+ "status": {
172
+ "type": "string"
173
+ },
174
+ "text": {
175
+ "type": "string"
176
+ },
177
+ "citations": {
178
+ "type": "array"
179
+ },
180
+ "notes": {
181
+ "type": "array",
182
+ "items": {
183
+ "type": "string"
184
+ }
185
+ }
186
+ }
187
+ }
188
+ },
189
+ "synthesis": {
190
+ "type": "object",
191
+ "required": [
192
+ "answer",
193
+ "completed_lane_count",
194
+ "confidence",
195
+ "next_actions"
196
+ ],
197
+ "properties": {
198
+ "answer": {
199
+ "type": "string"
200
+ },
201
+ "completed_lane_count": {
202
+ "type": "integer",
203
+ "minimum": 0
204
+ },
205
+ "planned_or_skipped_lane_count": {
206
+ "type": "integer",
207
+ "minimum": 0
208
+ },
209
+ "failed_lane_count": {
210
+ "type": "integer",
211
+ "minimum": 0
212
+ },
213
+ "confidence": {
214
+ "type": "string"
215
+ },
216
+ "citations": {
217
+ "type": "array"
218
+ },
219
+ "next_actions": {
220
+ "type": "array",
221
+ "items": {
222
+ "type": "string"
223
+ }
224
+ }
225
+ }
226
+ },
227
+ "artifacts": {
228
+ "type": "object",
229
+ "required": [
230
+ "request_json",
231
+ "breakdown_json",
232
+ "profile_json",
233
+ "answer_json",
234
+ "summary_md",
235
+ "lanes_root"
236
+ ]
237
+ },
238
+ "notes": {
239
+ "type": "array",
240
+ "items": {
241
+ "type": "string"
242
+ }
243
+ }
244
+ }
245
+ }
Binary file