open-research-protocol 0.4.8 → 0.4.10

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.
package/README.md CHANGED
@@ -33,6 +33,7 @@ verification remains independent of framing. See `modules/instruments/README.md`
33
33
  - `docs/ORP_REASONING_KERNEL_CANONICAL_CONTINUATION_PILOT.md` — harder live downstream benchmark where the agent must produce the next canonical task artifact
34
34
  - `docs/ORP_REASONING_KERNEL_EVIDENCE_MATRIX.md` — honest map of what the kernel proves, only suggests, or still leaves unproven
35
35
  - `docs/ORP_REASONING_KERNEL_EVALUATION_PLAN.md` — comparative experiment plan for upgrading kernel evidence beyond implementation validity
36
+ - `docs/ORP_YOUTUBE_INSPECT.md` — first-class YouTube metadata/transcript ingestion surface for agent-readable external source context
36
37
  - `docs/EXTERNAL_CONTRIBUTION_GOVERNANCE.md` — canonical local-first workflow for external OSS PR work
37
38
  - `docs/OSS_CONTRIBUTION_AGENT_LOOP.md` — agent operating rhythm for external contribution workflows
38
39
  - `templates/` — claim, verification, failure, and issue templates
@@ -51,11 +52,13 @@ verification remains independent of framing. See `modules/instruments/README.md`
51
52
  ORP should feel like one CLI with built-in abilities:
52
53
 
53
54
  - `workspace` for hosted auth, idea, feature, world, checkpoint, and worker operations
55
+ - `youtube` for public video metadata and transcript ingestion
54
56
  - `governance` for local-first repo initialization, branch safety, checkpoint commits, backup refs, readiness, repair, and cleanup
55
57
  - `discover` for profile-based GitHub scanning and opportunity selection
56
58
  - `collaborate` for repository collaboration setup and workflow execution
57
59
  - `erdos` for Erdos-specific data and workflow support
58
60
  - `report` and `packet` for ORP artifacts
61
+ - `compute` for targeted compute admission, local execution, and paid approval gating
59
62
 
60
63
  The `pack` layer still exists, but it is now an advanced/internal surface rather
61
64
  than the main product story.
@@ -118,6 +121,8 @@ orp home --json
118
121
  orp about --json
119
122
  orp auth login
120
123
  orp whoami --json
124
+ orp youtube inspect https://www.youtube.com/watch?v=<video_id> --json
125
+ orp youtube inspect https://www.youtube.com/watch?v=<video_id> --save --json
121
126
  orp ideas list --json
122
127
  orp world bind --idea-id <idea-id> --project-root /abs/path --codex-session-id <session-id> --json
123
128
  orp checkpoint queue --idea-id <idea-id> --json
@@ -141,6 +146,8 @@ orp pack install --pack-id erdos-open-problems --json
141
146
  orp pack fetch --source <git-url> --pack-id <pack-id> --install-target . --json
142
147
  orp gate run --profile default --json
143
148
  orp packet emit --profile default --json
149
+ orp compute decide --input orp.compute.json --json
150
+ orp compute run-local --input orp.compute.json --task orp.compute.task.json --json
144
151
  orp report summary --json
145
152
  ```
146
153
 
@@ -149,6 +156,8 @@ These surfaces are meant to help automated systems discover ORP quickly:
149
156
  - bare `orp` opens a home screen with repo/runtime status, available packs, and next commands
150
157
  - `orp home --json` returns the same landing context in machine-readable form
151
158
  - `orp auth ...`, `orp ideas ...`, `orp world ...`, `orp checkpoint ...`, `orp runner ...`, and `orp agent ...` expose the hosted workspace surface directly through ORP
159
+ - `orp compute ...` exposes targeted-compute admission, local execution, and paid-approval gating through a stable ORP wrapper surface
160
+ - `orp youtube inspect ...` exposes public YouTube metadata plus full transcript ingestion through a stable ORP artifact shape for agent use when caption tracks are available
152
161
  - `orp init`, `orp status`, `orp branch start`, `orp checkpoint create`, `orp backup`, `orp ready`, `orp doctor`, and `orp cleanup` expose the local-first repo governance surface directly through ORP
153
162
  - `orp discover ...` exposes profile-based GitHub scanning as a built-in ORP ability
154
163
  - `orp collaborate ...` exposes built-in collaboration setup and workflow execution without asking users to think in terms of separate governance packs
@@ -212,6 +221,7 @@ Minimal CLI skeleton:
212
221
 
213
222
  ```bash
214
223
  orp auth login
224
+ orp youtube inspect https://www.youtube.com/watch?v=<video_id> --json
215
225
  orp ideas list --json
216
226
  orp world bind --idea-id <idea-id> --project-root /abs/path --codex-session-id <session-id> --json
217
227
  orp checkpoint queue --idea-id <idea-id> --json
@@ -0,0 +1,341 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "node:fs/promises";
4
+ import path from "node:path";
5
+ import process from "node:process";
6
+ import {
7
+ buildOrpComputeGateResult,
8
+ buildOrpComputePacket,
9
+ defineComputePacket,
10
+ defineDecision,
11
+ defineImpactRead,
12
+ definePolicy,
13
+ defineResultBundle,
14
+ defineRung,
15
+ evaluateDispatch,
16
+ runLocalShellPacket,
17
+ } from "breakthroughs";
18
+
19
+ function printHelp() {
20
+ console.log(`ORP compute
21
+
22
+ Usage:
23
+ orp compute decide --input <path> [--packet-out <path>] [--json]
24
+ orp compute run-local --input <path> --task <path> [--receipt-out <path>] [--packet-out <path>] [--json]
25
+
26
+ Input JSON shape:
27
+ {
28
+ "decision": { ... },
29
+ "rung": { ... },
30
+ "policy": { ... },
31
+ "packet": { ... },
32
+ "repo": {
33
+ "rootPath": "/abs/path",
34
+ "git": { "branch": "main", "commit": "abc123" }
35
+ },
36
+ "orp": {
37
+ "boardId": "targeted_compute",
38
+ "problemId": "adult-vs-developmental-rgc",
39
+ "artifactRoot": "orp/artifacts"
40
+ }
41
+ }
42
+
43
+ Task JSON shape for run-local:
44
+ {
45
+ "command": "node",
46
+ "args": ["-e", "console.log('hello')"],
47
+ "cwd": "/abs/path",
48
+ "timeoutMs": 30000,
49
+ "env": {}
50
+ }
51
+
52
+ Policy semantics:
53
+ - local admitted rungs can resolve to run_local
54
+ - paid admitted rungs resolve to request_paid_approval unless the rung is explicitly approved in policy.paid.approvedRungs
55
+ `);
56
+ }
57
+
58
+ function parseArgs(argv) {
59
+ const options = {
60
+ json: false,
61
+ };
62
+
63
+ for (let i = 0; i < argv.length; i += 1) {
64
+ const arg = argv[i];
65
+
66
+ if (arg === "--json") {
67
+ options.json = true;
68
+ continue;
69
+ }
70
+ if (arg === "-h" || arg === "--help") {
71
+ options.help = true;
72
+ continue;
73
+ }
74
+ if (arg.startsWith("--")) {
75
+ const key = arg.slice(2).replace(/-([a-z])/g, (_, ch) => ch.toUpperCase());
76
+ const value = argv[i + 1];
77
+ if (value == null || value.startsWith("--")) {
78
+ throw new Error(`missing value for ${arg}`);
79
+ }
80
+ options[key] = value;
81
+ i += 1;
82
+ continue;
83
+ }
84
+
85
+ if (!options.command) {
86
+ options.command = arg;
87
+ } else {
88
+ throw new Error(`unexpected argument: ${arg}`);
89
+ }
90
+ }
91
+
92
+ return options;
93
+ }
94
+
95
+ async function readJson(filePath) {
96
+ const raw = await fs.readFile(filePath, "utf8");
97
+ return JSON.parse(raw);
98
+ }
99
+
100
+ async function writeJson(filePath, payload) {
101
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
102
+ await fs.writeFile(filePath, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
103
+ }
104
+
105
+ function buildContext(raw) {
106
+ if (!raw || typeof raw !== "object") {
107
+ throw new Error("input must be a JSON object");
108
+ }
109
+
110
+ return {
111
+ raw,
112
+ decision: defineDecision(raw.decision),
113
+ rung: defineRung(raw.rung),
114
+ policy: definePolicy(raw.policy),
115
+ packet: defineComputePacket(raw.packet),
116
+ };
117
+ }
118
+
119
+ function commandLabel(subcommand, options) {
120
+ const parts = ["orp", "compute", subcommand];
121
+ if (options.input) {
122
+ parts.push("--input", options.input);
123
+ }
124
+ if (options.task) {
125
+ parts.push("--task", options.task);
126
+ }
127
+ return parts.join(" ");
128
+ }
129
+
130
+ function gateStatusForDispatch(action) {
131
+ if (action === "hold_packet") {
132
+ return "fail";
133
+ }
134
+ if (action === "request_paid_approval") {
135
+ return "hold";
136
+ }
137
+ return "pass";
138
+ }
139
+
140
+ function summarizeDispatch(dispatchResult) {
141
+ if (dispatchResult.action === "request_paid_approval") {
142
+ return `compute packet requires explicit paid approval for rung ${dispatchResult.rungId}`;
143
+ }
144
+ if (dispatchResult.action === "hold_packet") {
145
+ return `compute packet is being held because ${dispatchResult.reason}`;
146
+ }
147
+ return `compute packet admitted with action ${dispatchResult.action}`;
148
+ }
149
+
150
+ async function runDecide(options) {
151
+ if (!options.input) {
152
+ throw new Error("compute decide requires --input <path>");
153
+ }
154
+
155
+ const context = buildContext(await readJson(options.input));
156
+ const dispatchResult = evaluateDispatch(context);
157
+ const gateResult = buildOrpComputeGateResult({
158
+ gateId: context.packet.rungId,
159
+ command: commandLabel("decide", options),
160
+ status: gateStatusForDispatch(dispatchResult.action),
161
+ evidenceNote: summarizeDispatch(dispatchResult),
162
+ });
163
+
164
+ const payload = {
165
+ ok: dispatchResult.action !== "hold_packet",
166
+ command: "compute decide",
167
+ dispatch_result: dispatchResult,
168
+ gate_result: gateResult,
169
+ };
170
+
171
+ if (options.packetOut) {
172
+ const orpPacket = buildOrpComputePacket({
173
+ repoRoot: context.raw.repo?.rootPath || process.cwd(),
174
+ repoGit: context.raw.repo?.git,
175
+ decision: context.decision,
176
+ packet: context.packet,
177
+ dispatchResult,
178
+ gateResults: [gateResult],
179
+ artifactRoot: context.raw.orp?.artifactRoot,
180
+ boardId: context.raw.orp?.boardId,
181
+ problemId: context.raw.orp?.problemId,
182
+ stateNote: summarizeDispatch(dispatchResult),
183
+ });
184
+ await writeJson(options.packetOut, orpPacket);
185
+ payload.orp_packet_path = path.resolve(options.packetOut);
186
+ }
187
+
188
+ if (options.json) {
189
+ console.log(JSON.stringify(payload, null, 2));
190
+ } else {
191
+ console.log(`${dispatchResult.action}: ${summarizeDispatch(dispatchResult)}`);
192
+ }
193
+
194
+ return dispatchResult.action === "hold_packet" ? 1 : 0;
195
+ }
196
+
197
+ async function runLocal(options) {
198
+ if (!options.input) {
199
+ throw new Error("compute run-local requires --input <path>");
200
+ }
201
+ if (!options.task) {
202
+ throw new Error("compute run-local requires --task <path>");
203
+ }
204
+
205
+ const context = buildContext(await readJson(options.input));
206
+ const task = await readJson(options.task);
207
+ const dispatchResult = evaluateDispatch(context);
208
+
209
+ if (dispatchResult.action !== "run_local") {
210
+ const message = `compute packet is not locally runnable; dispatch action is ${dispatchResult.action}`;
211
+ if (options.json) {
212
+ console.log(JSON.stringify({ ok: false, error: message, dispatch_result: dispatchResult }, null, 2));
213
+ } else {
214
+ console.error(message);
215
+ }
216
+ return 1;
217
+ }
218
+
219
+ const executionReceipt = await runLocalShellPacket({
220
+ decision: context.decision,
221
+ rung: context.rung,
222
+ packet: context.packet,
223
+ dispatchResult,
224
+ task,
225
+ });
226
+
227
+ const gateResult = buildOrpComputeGateResult({
228
+ gateId: context.packet.rungId,
229
+ command: `${executionReceipt.command} ${executionReceipt.args.join(" ")}`.trim(),
230
+ status: executionReceipt.status === "pass" ? "pass" : "fail",
231
+ exitCode: executionReceipt.exitCode == null ? 1 : executionReceipt.exitCode,
232
+ durationMs: executionReceipt.durationMs,
233
+ evidenceNote: `local shell execution completed with status ${executionReceipt.status}`,
234
+ });
235
+
236
+ const resultBundle = defineResultBundle({
237
+ id: `${context.packet.id}-result`,
238
+ packetId: context.packet.id,
239
+ outputs: context.packet.requiredOutputs,
240
+ status: executionReceipt.status,
241
+ metrics: {
242
+ exitCode: executionReceipt.exitCode,
243
+ durationMs: executionReceipt.durationMs,
244
+ timedOut: executionReceipt.timedOut,
245
+ },
246
+ });
247
+
248
+ const impactRead = defineImpactRead({
249
+ id: `${context.packet.id}-impact`,
250
+ bundleId: resultBundle.id,
251
+ nextAction: executionReceipt.status === "pass" ? "review_result_bundle" : "reroute_or_debug",
252
+ summary:
253
+ executionReceipt.status === "pass"
254
+ ? `local compute packet ${context.packet.id} completed successfully`
255
+ : `local compute packet ${context.packet.id} failed and needs follow-up`,
256
+ });
257
+
258
+ const payload = {
259
+ ok: executionReceipt.status === "pass",
260
+ command: "compute run-local",
261
+ dispatch_result: dispatchResult,
262
+ execution_receipt: executionReceipt,
263
+ gate_result: gateResult,
264
+ result_bundle: resultBundle,
265
+ impact_read: impactRead,
266
+ };
267
+
268
+ if (options.receiptOut) {
269
+ await writeJson(options.receiptOut, executionReceipt);
270
+ payload.execution_receipt_path = path.resolve(options.receiptOut);
271
+ }
272
+
273
+ if (options.packetOut) {
274
+ const orpPacket = buildOrpComputePacket({
275
+ repoRoot: context.raw.repo?.rootPath || process.cwd(),
276
+ repoGit: context.raw.repo?.git,
277
+ decision: context.decision,
278
+ packet: context.packet,
279
+ dispatchResult,
280
+ resultBundle,
281
+ impactRead,
282
+ gateResults: [gateResult],
283
+ artifactRoot: context.raw.orp?.artifactRoot,
284
+ boardId: context.raw.orp?.boardId,
285
+ problemId: context.raw.orp?.problemId,
286
+ extraPaths: options.receiptOut ? [path.resolve(options.receiptOut)] : [],
287
+ stateNote: impactRead.summary,
288
+ });
289
+ await writeJson(options.packetOut, orpPacket);
290
+ payload.orp_packet_path = path.resolve(options.packetOut);
291
+ }
292
+
293
+ if (options.json) {
294
+ console.log(JSON.stringify(payload, null, 2));
295
+ } else {
296
+ console.log(`${executionReceipt.status}: ${impactRead.summary}`);
297
+ }
298
+
299
+ if (executionReceipt.exitCode == null) {
300
+ return 1;
301
+ }
302
+ return executionReceipt.exitCode;
303
+ }
304
+
305
+ export async function runComputeCli(argv = process.argv.slice(2)) {
306
+ let options;
307
+ try {
308
+ options = parseArgs(argv);
309
+ } catch (error) {
310
+ console.error(String(error.message || error));
311
+ printHelp();
312
+ return 1;
313
+ }
314
+
315
+ if (options.help || !options.command) {
316
+ printHelp();
317
+ return 0;
318
+ }
319
+
320
+ try {
321
+ if (options.command === "decide") {
322
+ return await runDecide(options);
323
+ }
324
+ if (options.command === "run-local") {
325
+ return await runLocal(options);
326
+ }
327
+ throw new Error(`unknown compute command: ${options.command}`);
328
+ } catch (error) {
329
+ if (options.json) {
330
+ console.log(JSON.stringify({ ok: false, error: String(error.message || error) }, null, 2));
331
+ } else {
332
+ console.error(String(error.message || error));
333
+ }
334
+ return 1;
335
+ }
336
+ }
337
+
338
+ if (import.meta.url === `file://${process.argv[1]}`) {
339
+ const code = await runComputeCli(process.argv.slice(2));
340
+ process.exit(code == null ? 0 : code);
341
+ }
package/bin/orp.js CHANGED
@@ -1,9 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  const path = require("path");
4
+ const { pathToFileURL } = require("url");
4
5
  const { spawnSync } = require("child_process");
5
6
 
6
7
  const cliPath = path.resolve(__dirname, "..", "cli", "orp.py");
8
+ const computeCliUrl = pathToFileURL(path.resolve(__dirname, "orp-compute.mjs")).href;
7
9
  const argv = process.argv.slice(2);
8
10
 
9
11
  const candidates = [];
@@ -15,24 +17,65 @@ if (process.platform === "win32") {
15
17
  }
16
18
  candidates.push("python3", "python");
17
19
 
18
- let lastErr = null;
20
+ function isTopLevelHelp(args) {
21
+ return args.length === 0 || args.includes("-h") || args.includes("--help");
22
+ }
23
+
24
+ async function runCompute(args) {
25
+ const mod = await import(computeCliUrl);
26
+ const code = await mod.runComputeCli(args);
27
+ process.exit(code == null ? 0 : code);
28
+ }
19
29
 
20
- for (const py of candidates) {
21
- const args = py === "py" ? ["-3", cliPath, ...argv] : [cliPath, ...argv];
22
- const result = spawnSync(py, args, { stdio: "inherit" });
23
- if (!result.error) {
24
- process.exit(result.status == null ? 1 : result.status);
30
+ async function main() {
31
+ if (argv[0] === "compute") {
32
+ await runCompute(argv.slice(1));
33
+ return;
25
34
  }
26
- if (result.error && result.error.code === "ENOENT") {
27
- continue;
35
+
36
+ const captureOutput = isTopLevelHelp(argv);
37
+ let lastErr = null;
38
+
39
+ for (const py of candidates) {
40
+ const args = py === "py" ? ["-3", cliPath, ...argv] : [cliPath, ...argv];
41
+ const result = spawnSync(
42
+ py,
43
+ args,
44
+ captureOutput
45
+ ? { encoding: "utf8" }
46
+ : { stdio: "inherit" },
47
+ );
48
+
49
+ if (!result.error) {
50
+ if (captureOutput) {
51
+ if (result.stdout) {
52
+ process.stdout.write(result.stdout);
53
+ }
54
+ if (result.stderr) {
55
+ process.stderr.write(result.stderr);
56
+ }
57
+ if (result.status === 0) {
58
+ process.stdout.write("\nAdditional wrapper surface:\n orp compute -h\n");
59
+ }
60
+ }
61
+ process.exit(result.status == null ? 1 : result.status);
62
+ }
63
+
64
+ if (result.error && result.error.code === "ENOENT") {
65
+ continue;
66
+ }
67
+ lastErr = result.error;
28
68
  }
29
- lastErr = result.error;
30
- }
31
69
 
32
- console.error("ORP CLI requires Python 3 on PATH.");
33
- console.error("Tried: " + candidates.join(", "));
34
- if (lastErr) {
35
- console.error(String(lastErr));
70
+ console.error("ORP CLI requires Python 3 on PATH.");
71
+ console.error("Tried: " + candidates.join(", "));
72
+ if (lastErr) {
73
+ console.error(String(lastErr));
74
+ }
75
+ process.exit(1);
36
76
  }
37
- process.exit(1);
38
77
 
78
+ main().catch((error) => {
79
+ console.error(String(error && error.stack ? error.stack : error));
80
+ process.exit(1);
81
+ });