open-research-protocol 0.4.24 → 0.4.25
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/CHANGELOG.md +426 -0
- package/README.md +17 -0
- package/cli/orp.py +901 -3
- package/docs/AGENT_RUNTIME_BORROWING_NOTES.md +68 -0
- package/package.json +2 -1
- package/packages/orp-workspace-launcher/src/index.js +3 -0
- package/packages/orp-workspace-launcher/src/ledger.js +192 -33
- package/packages/orp-workspace-launcher/src/orp.js +61 -1
- package/packages/orp-workspace-launcher/src/tabs.js +147 -4
- package/packages/orp-workspace-launcher/test/ledger.test.js +226 -0
- package/packages/orp-workspace-launcher/test/tabs.test.js +60 -0
- package/cli/__pycache__/orp.cpython-311.pyc +0 -0
- package/scripts/__pycache__/orp-kernel-agent-pilot.cpython-311.pyc +0 -0
- package/scripts/__pycache__/orp-kernel-agent-replication.cpython-311.pyc +0 -0
- package/scripts/__pycache__/orp-kernel-benchmark.cpython-311.pyc +0 -0
- package/scripts/__pycache__/orp-kernel-canonical-continuation.cpython-311.pyc +0 -0
- package/scripts/__pycache__/orp-kernel-continuation-pilot.cpython-311.pyc +0 -0
|
@@ -57,6 +57,120 @@ async function withTempConfigHome(fn) {
|
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
async function makeFakeIdeaBridgeOrpCommand(tempDir, options = {}) {
|
|
61
|
+
const scriptPath = path.join(tempDir, "fake-orp.cjs");
|
|
62
|
+
const logPath = path.join(tempDir, "orp-calls.jsonl");
|
|
63
|
+
const failCreate = options.failCreate === true;
|
|
64
|
+
const script = `#!/usr/bin/env node
|
|
65
|
+
const fs = require("node:fs");
|
|
66
|
+
|
|
67
|
+
const args = process.argv.slice(2);
|
|
68
|
+
const logPath = process.env.FAKE_ORP_LOG;
|
|
69
|
+
if (logPath) {
|
|
70
|
+
fs.appendFileSync(logPath, JSON.stringify({ args }) + "\\n", "utf8");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const ideaId = "idea-main-123";
|
|
74
|
+
const bridgeWorkspaceId = "captured-iterm-window-20260401t032225z";
|
|
75
|
+
const hostedWorkspaceId = "ws-main-cody-1";
|
|
76
|
+
const failCreate = ${JSON.stringify(failCreate)};
|
|
77
|
+
const baseTabs = [
|
|
78
|
+
{ tab_id: "tab-orp", title: "orp", project_root: "/Volumes/Code_2TB/code/orp" },
|
|
79
|
+
{ tab_id: "tab-frg", title: "frg-site", project_root: "/Volumes/Code_2TB/code/frg-site" },
|
|
80
|
+
];
|
|
81
|
+
|
|
82
|
+
function write(payload) {
|
|
83
|
+
process.stdout.write(JSON.stringify(payload) + "\\n");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function linkedIdea() {
|
|
87
|
+
return {
|
|
88
|
+
idea_id: ideaId,
|
|
89
|
+
idea_title: "Terminal paths and codex sessions 03-26-2026",
|
|
90
|
+
relationship: "primary",
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function state(tabs) {
|
|
95
|
+
return {
|
|
96
|
+
captured_at_utc: "2026-04-15T12:00:00.000Z",
|
|
97
|
+
updated_at_utc: "2026-04-15T12:00:00.000Z",
|
|
98
|
+
tab_count: tabs.length,
|
|
99
|
+
capture_context: {
|
|
100
|
+
source_app: "test",
|
|
101
|
+
mode: "snapshot",
|
|
102
|
+
machine_id: "test-mac:darwin",
|
|
103
|
+
machine_label: "Test Mac",
|
|
104
|
+
platform: "darwin",
|
|
105
|
+
},
|
|
106
|
+
tabs,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function bridgeWorkspace() {
|
|
111
|
+
return {
|
|
112
|
+
workspace_id: bridgeWorkspaceId,
|
|
113
|
+
title: "Main Cody 1",
|
|
114
|
+
source_kind: "idea_bridge",
|
|
115
|
+
linked_idea: linkedIdea(),
|
|
116
|
+
state: state(baseTabs),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function hostedWorkspace(tabs = []) {
|
|
121
|
+
return {
|
|
122
|
+
workspace_id: hostedWorkspaceId,
|
|
123
|
+
title: "main-cody-1",
|
|
124
|
+
source_kind: "hosted",
|
|
125
|
+
linked_idea: linkedIdea(),
|
|
126
|
+
state: state(tabs),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (args[0] === "workspaces" && args[1] === "list") {
|
|
131
|
+
write({ ok: true, source: "hosted", workspaces: [bridgeWorkspace()], has_more: false, cursor: "" });
|
|
132
|
+
} else if (args[0] === "workspaces" && args[1] === "show") {
|
|
133
|
+
if (args[2] === bridgeWorkspaceId) {
|
|
134
|
+
write({ ok: true, workspace: bridgeWorkspace() });
|
|
135
|
+
} else if (args[2] === hostedWorkspaceId) {
|
|
136
|
+
write({ ok: true, workspace: hostedWorkspace(baseTabs) });
|
|
137
|
+
} else {
|
|
138
|
+
process.stderr.write("unknown workspace: " + args[2] + "\\n");
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
} else if (args[0] === "workspaces" && args[1] === "add") {
|
|
142
|
+
if (failCreate) {
|
|
143
|
+
process.stderr.write("Hosted ORP returned an HTML error page instead of JSON (status=404 path=/api/cli/workspaces)\\n");
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
146
|
+
const title = args[args.indexOf("--title") + 1];
|
|
147
|
+
const linked = args[args.indexOf("--idea-id") + 1];
|
|
148
|
+
if (title !== "main-cody-1" || linked !== ideaId) {
|
|
149
|
+
process.stderr.write("unexpected add args: " + JSON.stringify(args) + "\\n");
|
|
150
|
+
process.exit(1);
|
|
151
|
+
}
|
|
152
|
+
write({ ok: true, workspace: hostedWorkspace([]) });
|
|
153
|
+
} else if (args[0] === "workspaces" && args[1] === "push-state") {
|
|
154
|
+
const stateFile = args[args.indexOf("--state-file") + 1];
|
|
155
|
+
const pushedState = JSON.parse(fs.readFileSync(stateFile, "utf8"));
|
|
156
|
+
write({ ok: true, workspace: hostedWorkspace(pushedState.tabs || []) });
|
|
157
|
+
} else if (args[0] === "ideas" && args[1] === "list") {
|
|
158
|
+
write({ ok: true, ideas: [], has_more: false, cursor: "" });
|
|
159
|
+
} else if (args[0] === "idea" && args[1] === "update") {
|
|
160
|
+
process.stderr.write("idea update should not be called\\n");
|
|
161
|
+
process.exit(2);
|
|
162
|
+
} else {
|
|
163
|
+
process.stderr.write("unexpected args: " + JSON.stringify(args) + "\\n");
|
|
164
|
+
process.exit(1);
|
|
165
|
+
}
|
|
166
|
+
`;
|
|
167
|
+
|
|
168
|
+
await fs.writeFile(scriptPath, script, "utf8");
|
|
169
|
+
await fs.chmod(scriptPath, 0o755);
|
|
170
|
+
await fs.writeFile(logPath, "", "utf8");
|
|
171
|
+
return { scriptPath, logPath };
|
|
172
|
+
}
|
|
173
|
+
|
|
60
174
|
function sampleManifest() {
|
|
61
175
|
return normalizeWorkspaceManifest({
|
|
62
176
|
version: "1",
|
|
@@ -326,6 +440,118 @@ test("runWorkspaceAddTab updates a local workspace manifest file", async () => {
|
|
|
326
440
|
});
|
|
327
441
|
});
|
|
328
442
|
|
|
443
|
+
test("runWorkspaceAddTab promotes idea-bridge workspaces to hosted workspace state", async () => {
|
|
444
|
+
await withTempConfigHome(async (configHome) => {
|
|
445
|
+
const tempDir = await makeTempDir();
|
|
446
|
+
const { scriptPath, logPath } = await makeFakeIdeaBridgeOrpCommand(tempDir);
|
|
447
|
+
const originalLogPath = process.env.FAKE_ORP_LOG;
|
|
448
|
+
process.env.FAKE_ORP_LOG = logPath;
|
|
449
|
+
|
|
450
|
+
try {
|
|
451
|
+
const { code, stdout } = await captureStdout(() =>
|
|
452
|
+
runWorkspaceAddTab([
|
|
453
|
+
"main",
|
|
454
|
+
"--path",
|
|
455
|
+
"/Volumes/Code_2TB/code/anthropic-lab",
|
|
456
|
+
"--title",
|
|
457
|
+
"anthropic-lab",
|
|
458
|
+
"--remote-url",
|
|
459
|
+
"git@github.com:anthropic/anthropic-lab.git",
|
|
460
|
+
"--resume-tool",
|
|
461
|
+
"codex",
|
|
462
|
+
"--resume-session-id",
|
|
463
|
+
"019d4f24-c8ba-78b2-a726-48b1ce9f0fe9",
|
|
464
|
+
"--orp-command",
|
|
465
|
+
scriptPath,
|
|
466
|
+
"--json",
|
|
467
|
+
]),
|
|
468
|
+
);
|
|
469
|
+
const payload = JSON.parse(stdout);
|
|
470
|
+
const calls = (await fs.readFile(logPath, "utf8"))
|
|
471
|
+
.trim()
|
|
472
|
+
.split("\n")
|
|
473
|
+
.filter(Boolean)
|
|
474
|
+
.map((line) => JSON.parse(line).args);
|
|
475
|
+
const slots = JSON.parse(await fs.readFile(path.join(configHome, "orp", "workspace-slots.json"), "utf8"));
|
|
476
|
+
|
|
477
|
+
assert.equal(code, 0);
|
|
478
|
+
assert.equal(payload.persistedTo, "hosted-workspace");
|
|
479
|
+
assert.equal(payload.promotedFromIdeaId, "idea-main-123");
|
|
480
|
+
assert.equal(payload.createdHostedWorkspace, true);
|
|
481
|
+
assert.equal(payload.workspaceSourceId, "ws-main-cody-1");
|
|
482
|
+
assert.equal(payload.tabCount, 3);
|
|
483
|
+
assert.equal(payload.tab.restartCommand, "cd '/Volumes/Code_2TB/code/anthropic-lab' && codex resume 019d4f24-c8ba-78b2-a726-48b1ce9f0fe9");
|
|
484
|
+
assert.equal(payload.manifest.tabs[2]?.path, "/Volumes/Code_2TB/code/anthropic-lab");
|
|
485
|
+
assert.ok(calls.some((args) => args[0] === "workspaces" && args[1] === "add"));
|
|
486
|
+
assert.ok(calls.some((args) => args[0] === "workspaces" && args[1] === "push-state"));
|
|
487
|
+
assert.equal(calls.some((args) => args[0] === "idea" && args[1] === "update"), false);
|
|
488
|
+
assert.equal(slots.slots.main.kind, "hosted-workspace");
|
|
489
|
+
assert.equal(slots.slots.main.hostedWorkspaceId, "ws-main-cody-1");
|
|
490
|
+
} finally {
|
|
491
|
+
if (originalLogPath == null) {
|
|
492
|
+
delete process.env.FAKE_ORP_LOG;
|
|
493
|
+
} else {
|
|
494
|
+
process.env.FAKE_ORP_LOG = originalLogPath;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
});
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
test("runWorkspaceAddTab falls back to local ledger when hosted workspace creation is unavailable", async () => {
|
|
501
|
+
await withTempConfigHome(async (configHome) => {
|
|
502
|
+
const tempDir = await makeTempDir();
|
|
503
|
+
const { scriptPath, logPath } = await makeFakeIdeaBridgeOrpCommand(tempDir, { failCreate: true });
|
|
504
|
+
const originalLogPath = process.env.FAKE_ORP_LOG;
|
|
505
|
+
process.env.FAKE_ORP_LOG = logPath;
|
|
506
|
+
|
|
507
|
+
try {
|
|
508
|
+
const { code, stdout } = await captureStdout(() =>
|
|
509
|
+
runWorkspaceAddTab([
|
|
510
|
+
"main",
|
|
511
|
+
"--path",
|
|
512
|
+
"/Volumes/Code_2TB/code/anthropic-lab",
|
|
513
|
+
"--title",
|
|
514
|
+
"anthropic-lab",
|
|
515
|
+
"--remote-url",
|
|
516
|
+
"git@github.com:anthropic/anthropic-lab.git",
|
|
517
|
+
"--resume-tool",
|
|
518
|
+
"codex",
|
|
519
|
+
"--resume-session-id",
|
|
520
|
+
"019d4f24-c8ba-78b2-a726-48b1ce9f0fe9",
|
|
521
|
+
"--orp-command",
|
|
522
|
+
scriptPath,
|
|
523
|
+
"--json",
|
|
524
|
+
]),
|
|
525
|
+
);
|
|
526
|
+
const payload = JSON.parse(stdout);
|
|
527
|
+
const calls = (await fs.readFile(logPath, "utf8"))
|
|
528
|
+
.trim()
|
|
529
|
+
.split("\n")
|
|
530
|
+
.filter(Boolean)
|
|
531
|
+
.map((line) => JSON.parse(line).args);
|
|
532
|
+
const saved = JSON.parse(await fs.readFile(payload.manifestPath, "utf8"));
|
|
533
|
+
const slots = JSON.parse(await fs.readFile(path.join(configHome, "orp", "workspace-slots.json"), "utf8"));
|
|
534
|
+
|
|
535
|
+
assert.equal(code, 0);
|
|
536
|
+
assert.equal(payload.persistedTo, "workspace-file");
|
|
537
|
+
assert.equal(payload.promotedFromIdeaId, "idea-main-123");
|
|
538
|
+
assert.match(payload.hostedMigrationSkippedReason, /status=404/);
|
|
539
|
+
assert.equal(payload.tabCount, 3);
|
|
540
|
+
assert.equal(saved.tabs[2]?.path, "/Volumes/Code_2TB/code/anthropic-lab");
|
|
541
|
+
assert.ok(calls.some((args) => args[0] === "workspaces" && args[1] === "add"));
|
|
542
|
+
assert.equal(calls.some((args) => args[0] === "idea" && args[1] === "update"), false);
|
|
543
|
+
assert.equal(slots.slots.main.kind, "workspace-file");
|
|
544
|
+
assert.equal(slots.slots.main.manifestPath, payload.manifestPath);
|
|
545
|
+
} finally {
|
|
546
|
+
if (originalLogPath == null) {
|
|
547
|
+
delete process.env.FAKE_ORP_LOG;
|
|
548
|
+
} else {
|
|
549
|
+
process.env.FAKE_ORP_LOG = originalLogPath;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
});
|
|
553
|
+
});
|
|
554
|
+
|
|
329
555
|
test("runWorkspaceAddTab upserts an existing tab and returns the rendered recovery command", async () => {
|
|
330
556
|
await withTempConfigHome(async () => {
|
|
331
557
|
const tempDir = await makeTempDir();
|
|
@@ -123,6 +123,66 @@ test("buildWorkspaceTabsReport keeps duplicate titles unique and exposes generic
|
|
|
123
123
|
assert.equal(report.tabs[2]?.codexSessionId, null);
|
|
124
124
|
});
|
|
125
125
|
|
|
126
|
+
test("buildWorkspaceTabsReport ranks Codex tabs by recent local session activity", async () => {
|
|
127
|
+
const tempDir = await makeTempDir();
|
|
128
|
+
const codexHome = path.join(tempDir, "codex-home");
|
|
129
|
+
const sessionsDir = path.join(codexHome, "sessions", "2026", "04", "15");
|
|
130
|
+
await fs.mkdir(sessionsDir, { recursive: true });
|
|
131
|
+
|
|
132
|
+
const olderSessionId = "019d0000-0000-7000-8000-000000000001";
|
|
133
|
+
const newerSessionId = "019d0000-0000-7000-8000-000000000002";
|
|
134
|
+
const olderPath = path.join(sessionsDir, `rollout-2026-04-15T01-00-00-${olderSessionId}.jsonl`);
|
|
135
|
+
const newerPath = path.join(sessionsDir, `rollout-2026-04-15T02-00-00-${newerSessionId}.jsonl`);
|
|
136
|
+
await fs.writeFile(olderPath, "{}\n", "utf8");
|
|
137
|
+
await fs.writeFile(newerPath, "{}\n", "utf8");
|
|
138
|
+
await fs.utimes(olderPath, new Date("2026-04-15T01:00:00Z"), new Date("2026-04-15T01:00:00Z"));
|
|
139
|
+
await fs.utimes(newerPath, new Date("2026-04-15T02:00:00Z"), new Date("2026-04-15T02:00:00Z"));
|
|
140
|
+
|
|
141
|
+
const parsed = parseWorkspaceSource({
|
|
142
|
+
sourceType: "workspace-file",
|
|
143
|
+
sourceLabel: "/tmp/workspace.json",
|
|
144
|
+
title: "workspace",
|
|
145
|
+
workspaceManifest: {
|
|
146
|
+
version: "1",
|
|
147
|
+
workspaceId: "orp-main",
|
|
148
|
+
tabs: [
|
|
149
|
+
{
|
|
150
|
+
title: "older-project",
|
|
151
|
+
path: "/Volumes/Code_2TB/code/older-project",
|
|
152
|
+
resumeCommand: `codex resume ${olderSessionId}`,
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
title: "no-session-project",
|
|
156
|
+
path: "/Volumes/Code_2TB/code/no-session-project",
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
title: "newer-project",
|
|
160
|
+
path: "/Volumes/Code_2TB/code/newer-project",
|
|
161
|
+
resumeCommand: `codex resume ${newerSessionId}`,
|
|
162
|
+
},
|
|
163
|
+
],
|
|
164
|
+
},
|
|
165
|
+
notes: "",
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
const report = buildWorkspaceTabsReport(
|
|
169
|
+
{
|
|
170
|
+
sourceType: "workspace-file",
|
|
171
|
+
sourceLabel: "/tmp/workspace.json",
|
|
172
|
+
title: "workspace",
|
|
173
|
+
},
|
|
174
|
+
parsed,
|
|
175
|
+
{ codexHome },
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
assert.equal(report.tabs[0]?.title, "newer-project");
|
|
179
|
+
assert.equal(report.tabs[1]?.title, "older-project");
|
|
180
|
+
assert.equal(report.tabs[2]?.title, "no-session-project");
|
|
181
|
+
assert.equal(report.projects[0]?.path, "/Volumes/Code_2TB/code/newer-project");
|
|
182
|
+
assert.equal(report.projects[1]?.path, "/Volumes/Code_2TB/code/older-project");
|
|
183
|
+
assert.equal(report.projects[2]?.path, "/Volumes/Code_2TB/code/no-session-project");
|
|
184
|
+
});
|
|
185
|
+
|
|
126
186
|
test("runWorkspaceTabs prints JSON without launch commands", async () => {
|
|
127
187
|
const tempDir = await makeTempDir();
|
|
128
188
|
const manifestPath = path.join(tempDir, "workspace.json");
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|