@crvouga/mockingbird-service-hermes 0.1.0

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,274 @@
1
+ import assert from "node:assert/strict"
2
+ import { spawn } from "node:child_process"
3
+ import { mkdir, readFile, writeFile } from "node:fs/promises"
4
+ import { resolve } from "node:path"
5
+ import { createInterface } from "node:readline"
6
+ import { createServer } from "../dist/server.js"
7
+ import { normalizeObservation } from "./comparison.mjs"
8
+
9
+ if (!process.argv.includes("--run") || process.env.MOCKINGBIRD_HERMES_ORACLE_APPROVED !== "1") {
10
+ throw new Error(
11
+ "Opt-in oracle requires --run and explicit approved disposable-process/database execution",
12
+ )
13
+ }
14
+ const root = resolve(import.meta.dirname, "../../../..")
15
+ const python = process.env.HERMES_ORACLE_PYTHON
16
+ if (!python)
17
+ throw new Error("Set HERMES_ORACLE_PYTHON to the approved local virtualenv interpreter")
18
+ const source = resolve(root, ".mockingbird/hermes-evidence/v2026.8.31")
19
+ const scratch = resolve(root, ".mockingbird/hermes-oracle", crypto.randomUUID())
20
+ await mkdir(scratch, { recursive: true })
21
+ const lock = JSON.parse(await readFile(new URL("source-lock.json", import.meta.url), "utf8"))
22
+ const mock = await createServer()
23
+ let currentTime = 1700000000
24
+ let upstream
25
+ const epochs = []
26
+ const comparisons = []
27
+ const ids = { real: new Map(), mock: new Map() }
28
+
29
+ const launch = () =>
30
+ new Promise((resolveLaunch, reject) => {
31
+ const child = spawn(
32
+ python,
33
+ [
34
+ resolve(import.meta.dirname, "server.py"),
35
+ "--sources",
36
+ source,
37
+ "--state",
38
+ resolve(scratch, "runs.db"),
39
+ "--time",
40
+ String(currentTime),
41
+ "--allow-disposable-state",
42
+ ],
43
+ {
44
+ cwd: root,
45
+ env: { PATH: process.env.PATH, PYTHONDONTWRITEBYTECODE: "1", HERMES_HOME: scratch },
46
+ stdio: ["ignore", "pipe", "pipe"],
47
+ },
48
+ )
49
+ const lines = createInterface({ input: child.stdout })
50
+ let stderr = ""
51
+ child.stderr.on("data", (chunk) => {
52
+ stderr += chunk.toString()
53
+ })
54
+ const timer = setTimeout(() => {
55
+ child.kill("SIGTERM")
56
+ reject(new Error(`Oracle startup deadline: ${stderr}`))
57
+ }, 10000)
58
+ child.once("error", (error) => {
59
+ clearTimeout(timer)
60
+ reject(error)
61
+ })
62
+ child.once("exit", (code) => {
63
+ clearTimeout(timer)
64
+ reject(new Error(`Oracle exited before ready (${code}): ${stderr}`))
65
+ })
66
+ lines.once("line", (line) => {
67
+ clearTimeout(timer)
68
+ try {
69
+ const ready = JSON.parse(line)
70
+ epochs.push({ ...ready, url: undefined })
71
+ resolveLaunch({ child, url: ready.url, stderr: () => stderr })
72
+ } catch (error) {
73
+ child.kill("SIGTERM")
74
+ reject(error)
75
+ }
76
+ })
77
+ })
78
+ const raw = async (url, method, path, body, key = "") => {
79
+ const response = await fetch(`${url}${path}`, {
80
+ method,
81
+ headers: { "content-type": "application/json", "Idempotency-Key": key },
82
+ ...(body === undefined ? {} : { body: JSON.stringify(body) }),
83
+ signal: AbortSignal.timeout(5000),
84
+ })
85
+ return {
86
+ status: response.status,
87
+ replayed: response.headers.get("Idempotency-Replayed"),
88
+ body: await response.json(),
89
+ }
90
+ }
91
+ const checkedControl = async (url, path, body) => {
92
+ const response = await raw(url, "POST", path, body)
93
+ assert.equal(response.status, 200, `control ${path}: ${JSON.stringify(response.body)}`)
94
+ return response
95
+ }
96
+ const control = (body) => checkedControl(upstream.url, "/__oracle/control", body)
97
+ const admin = (path, body) => checkedControl(mock.url, `/__admin/${path}`, body)
98
+ const equal = (label, real, mocked) => {
99
+ const expected = normalizeObservation(real, ids.real)
100
+ const actual = normalizeObservation(mocked, ids.mock)
101
+ assert.deepEqual(actual, expected, label)
102
+ comparisons.push({ label, status: real.status, observation: expected.body })
103
+ }
104
+ const poll = async (pair, label) => {
105
+ equal(
106
+ label,
107
+ await raw(upstream.url, "GET", `/v1/runs/${pair.real}`),
108
+ await raw(mock.url, "GET", `/v1/runs/${pair.mock}`),
109
+ )
110
+ }
111
+ const waitRunning = async (id) => {
112
+ const deadline = performance.now() + 3000
113
+ while (true) {
114
+ const response = await raw(upstream.url, "GET", `/v1/runs/${id}`)
115
+ if (response.body.status === "running") return
116
+ if (performance.now() > deadline || ["failed", "cancelled"].includes(response.body.status))
117
+ throw new Error(`Upstream executor did not reach running: ${JSON.stringify(response)}`)
118
+ await new Promise((resolve) => setImmediate(resolve))
119
+ }
120
+ }
121
+ const submit = async (name, key = name) => {
122
+ const body = { input: "synthetic oracle input" }
123
+ const real = await raw(upstream.url, "POST", "/v1/runs", body, key)
124
+ const mocked = await raw(mock.url, "POST", "/v1/runs", body, key)
125
+ assert.equal(real.status, 202)
126
+ assert.equal(mocked.status, 202)
127
+ const pair = { real: real.body.run_id, mock: mocked.body.run_id, key, body }
128
+ ids.real.set(pair.real, name)
129
+ ids.mock.set(pair.mock, name)
130
+ equal(`${name}:admission`, real, mocked)
131
+ await waitRunning(pair.real)
132
+ await admin(`hermes/runs/${pair.mock}/observe`, { status: "running" })
133
+ await poll(pair, `${name}:running`)
134
+ return pair
135
+ }
136
+ const replay = async (pair, label, body = pair.body) =>
137
+ equal(
138
+ label,
139
+ await raw(upstream.url, "POST", "/v1/runs", body, pair.key),
140
+ await raw(mock.url, "POST", "/v1/runs", body, pair.key),
141
+ )
142
+ const stop = async (pair) =>
143
+ equal(
144
+ `${pair.key}:stop`,
145
+ await raw(upstream.url, "POST", `/v1/runs/${pair.real}/stop`, {}),
146
+ await raw(mock.url, "POST", `/v1/runs/${pair.mock}/stop`, {}),
147
+ )
148
+ const settle = async (pair, status) => {
149
+ const result =
150
+ status === "cancelled"
151
+ ? { interrupted: true }
152
+ : status === "failed"
153
+ ? { failed: true, error: "synthetic failure" }
154
+ : { final_response: "synthetic output" }
155
+ await control({ settle: pair.real, result })
156
+ await admin(`hermes/runs/${pair.mock}/observe`, {
157
+ status,
158
+ last_event: `run.${status}`,
159
+ ...(status === "completed"
160
+ ? {
161
+ output: "synthetic output",
162
+ usage: { input_tokens: 0, output_tokens: 0, total_tokens: 0 },
163
+ }
164
+ : {}),
165
+ ...(status === "failed" ? { error: "synthetic failure" } : {}),
166
+ })
167
+ await poll(pair, `${pair.key}:${status}`)
168
+ }
169
+ const setTime = async (time) => {
170
+ currentTime = time
171
+ await control({ time })
172
+ await admin("clock", { set: time * 1000, freeze: true })
173
+ }
174
+ const crash = async () => {
175
+ const old = upstream
176
+ upstream = undefined
177
+ if (old.child.exitCode !== null || old.child.signalCode !== null)
178
+ throw new Error(`Oracle exited unexpectedly: ${old.stderr()}`)
179
+ let timer
180
+ const exited = new Promise((resolveExit, reject) => {
181
+ old.child.once("exit", (code) => {
182
+ clearTimeout(timer)
183
+ if (code === 0) resolveExit()
184
+ else reject(new Error(`Oracle process exited with ${code}`))
185
+ })
186
+ timer = setTimeout(() => {
187
+ old.child.kill("SIGTERM")
188
+ reject(new Error("Oracle crash endpoint did not exit the owned process"))
189
+ }, 6000)
190
+ })
191
+ // Observe rejection immediately while awaiting the expected lost connection.
192
+ const outcome = exited.then(
193
+ () => null,
194
+ (error) => error,
195
+ )
196
+ await fetch(`${old.url}/__oracle/crash`, {
197
+ method: "POST",
198
+ signal: AbortSignal.timeout(5000),
199
+ }).catch(() => {})
200
+ const error = await outcome
201
+ if (error) throw error
202
+ }
203
+ try {
204
+ upstream = await launch()
205
+ await admin("clock", { set: currentTime * 1000, freeze: true })
206
+ const cancelled = await submit("cancelled")
207
+ await replay(cancelled, "active replay")
208
+ await replay(cancelled, "changed body conflicts", { input: "different synthetic input" })
209
+ await stop(cancelled)
210
+ await settle(cancelled, "cancelled")
211
+ await replay(cancelled, "terminal replay")
212
+ const completed = await submit("completion-race")
213
+ await stop(completed)
214
+ await settle(completed, "completed")
215
+ const failed = await submit("failure")
216
+ await settle(failed, "failed")
217
+ const cached = await submit("cache-boundary", "")
218
+ await settle(cached, "completed")
219
+ await setTime(1700000000 + 3600)
220
+ await control({ sweep: true })
221
+ await admin("hermes/sweep", {})
222
+ await poll(cached, "cache TTL equality retains keyless terminal observation")
223
+ await setTime(1700000000 + 3600.001)
224
+ await control({ sweep: true })
225
+ await admin("hermes/sweep", {})
226
+ await poll(cached, "cache TTL strictly greater expires keyless terminal observation")
227
+ const unfinished = await submit("unfinished")
228
+ const keyless = await submit("keyless", "")
229
+ await crash()
230
+ upstream = await launch()
231
+ assert.notEqual(epochs[0].pid, epochs[1].pid, "restart must create a fresh process")
232
+ await admin("hermes/restart", { owner: "stale" })
233
+ await poll(completed, "terminal survives real process restart")
234
+ await poll(unfinished, "unfinished run interrupted after real process restart")
235
+ await poll(keyless, "keyless run lost after real process restart")
236
+ await replay(unfinished, "interrupted reservation replay")
237
+ await setTime(1700000000 + 86400)
238
+ await replay(completed, "durable TTL equality retains reservation")
239
+ await setTime(1700000000 + 86400.001)
240
+ await poll(completed, "GET alone does not prune durable history")
241
+ const renewed = await submit("renewed", completed.key)
242
+ assert.notEqual(renewed.real, completed.real)
243
+ assert.notEqual(renewed.mock, completed.mock)
244
+ await control({ sweep: true })
245
+ await admin("hermes/sweep", {})
246
+ await poll(completed, "pruned and swept result returns ordinary404")
247
+ await settle(renewed, "completed")
248
+ const report = {
249
+ release: lock.release,
250
+ commit: lock.commit,
251
+ sources: lock.sources,
252
+ epochs,
253
+ comparisons,
254
+ limitations: [
255
+ "scripted executor; no inference",
256
+ "ordinary default test listener; no credential enforcement claim",
257
+ "routing/session/approval/process-tool integrations substituted; no hosted rooms",
258
+ "controlled clock and explicit sweep; no background timing claim",
259
+ ],
260
+ }
261
+ await writeFile(resolve(scratch, "report.json"), `${JSON.stringify(report, null, 2)}\n`)
262
+ console.log(
263
+ JSON.stringify({ comparisons: comparisons.length, report: resolve(scratch, "report.json") }),
264
+ )
265
+ } catch (error) {
266
+ if (upstream) console.error(upstream.stderr())
267
+ throw error
268
+ } finally {
269
+ try {
270
+ if (upstream) await crash()
271
+ } finally {
272
+ await mock.close()
273
+ }
274
+ }
@@ -0,0 +1,16 @@
1
+ /** Map only public identity fields and the exact pinned missing-run error template. */
2
+ export const normalizeObservation = (response, ids) => {
3
+ const body = structuredClone(response.body)
4
+ for (const field of ["run_id", "session_id"]) {
5
+ if (typeof body[field] === "string" && ids.has(body[field])) body[field] = ids.get(body[field])
6
+ }
7
+ if (response.status === 404 && body.error?.code === "run_not_found") {
8
+ for (const [id, name] of ids) {
9
+ if (body.error.message === `Run not found: ${id}`) {
10
+ body.error.message = `Run not found: ${name}`
11
+ break
12
+ }
13
+ }
14
+ }
15
+ return { ...response, body }
16
+ }
@@ -0,0 +1,41 @@
1
+ import assert from "node:assert/strict"
2
+ import { test } from "node:test"
3
+ import { normalizeObservation } from "./comparison.mjs"
4
+
5
+ const realIds = new Map([["real-id", "run"]])
6
+ const mockIds = new Map([["mock-id", "run"]])
7
+ const same = (real, mock) =>
8
+ assert.deepEqual(normalizeObservation(real, realIds), normalizeObservation(mock, mockIds))
9
+
10
+ test("identity fields map without mutating the response", () => {
11
+ const real = { status: 200, body: { run_id: "real-id", session_id: "real-id" } }
12
+ same(real, { status: 200, body: { run_id: "mock-id", session_id: "mock-id" } })
13
+ assert.equal(real.body.run_id, "real-id")
14
+ })
15
+ test("result and arbitrary error strings retain generated IDs", () => {
16
+ for (const field of ["output", "error"]) {
17
+ assert.throws(
18
+ () =>
19
+ same(
20
+ { status: 200, body: { [field]: "result real-id" } },
21
+ { status: 200, body: { [field]: "result mock-id" } },
22
+ ),
23
+ assert.AssertionError,
24
+ )
25
+ }
26
+ })
27
+ test("missing-run normalization requires the exact error template", () => {
28
+ const missing = (message) => ({
29
+ status: 404,
30
+ body: { error: { code: "run_not_found", message } },
31
+ })
32
+ same(missing("Run not found: real-id"), missing("Run not found: mock-id"))
33
+ assert.throws(
34
+ () => same(missing("Run not found: real-id"), missing("Different: mock-id")),
35
+ assert.AssertionError,
36
+ )
37
+ assert.throws(
38
+ () => same(missing("Run not found: real-id"), missing("Run not found: mock-id suffix")),
39
+ assert.AssertionError,
40
+ )
41
+ })
@@ -0,0 +1,29 @@
1
+ """Explicitly fetch hash-locked source files; never install or execute upstream."""
2
+ import argparse
3
+ import hashlib
4
+ from pathlib import Path
5
+ from urllib.request import urlopen
6
+ from sources import LOCK, verify
7
+
8
+ parser = argparse.ArgumentParser()
9
+ parser.add_argument("--fetch", action="store_true")
10
+ args = parser.parse_args()
11
+ if not args.fetch:
12
+ parser.error("pass --fetch to download the pinned source files")
13
+ root = Path(__file__).resolve().parents[4]
14
+ destination = root / ".mockingbird/hermes-evidence/v2026.8.31"
15
+ destination.mkdir(parents=True, exist_ok=True)
16
+ for name, entry in LOCK["sources"].items():
17
+ path = destination / name
18
+ if path.exists():
19
+ data = path.read_bytes()
20
+ else:
21
+ url = f"https://raw.githubusercontent.com/NousResearch/hermes-agent/{LOCK['commit']}/{entry['path']}"
22
+ with urlopen(url, timeout=30) as response:
23
+ data = response.read()
24
+ if hashlib.sha256(data).hexdigest() != entry["sha256"]:
25
+ raise ValueError(f"Pinned source hash mismatch: {name}")
26
+ if not path.exists():
27
+ path.write_bytes(data)
28
+ verify(destination)
29
+ print(f"Verified {len(LOCK['sources'])} files at {LOCK['commit']}")
@@ -0,0 +1,11 @@
1
+ # Versions observed in the passing pinned oracle run.
2
+ aiohappyeyeballs==2.7.1
3
+ aiohttp==3.14.3
4
+ aiosignal==1.4.0
5
+ attrs==26.1.0
6
+ frozenlist==1.8.0
7
+ idna==3.20
8
+ multidict==6.9.1
9
+ propcache==0.5.4
10
+ typing-extensions==4.16.0
11
+ yarl==1.25.1
@@ -0,0 +1,227 @@
1
+ """Opt-in loopback oracle: pinned run/store code, scripted non-inference executor."""
2
+ import argparse
3
+ import asyncio
4
+ import contextlib
5
+ import contextvars
6
+ import hmac
7
+ import itertools
8
+ import importlib.metadata
9
+ import json
10
+ import logging
11
+ import os
12
+ import re
13
+ import sqlite3
14
+ import subprocess
15
+ import sys
16
+ import threading
17
+ import time
18
+ import types
19
+ import typing
20
+ from pathlib import Path
21
+
22
+ from sources import LOCK, definitions, load_module, verify
23
+
24
+
25
+ def package(name):
26
+ module = types.ModuleType(name)
27
+ module.__path__ = []
28
+ sys.modules[name] = module
29
+ return module
30
+
31
+
32
+ def main():
33
+ parser = argparse.ArgumentParser()
34
+ parser.add_argument("--sources", required=True)
35
+ parser.add_argument("--state", required=True)
36
+ parser.add_argument("--time", type=float, required=True)
37
+ parser.add_argument("--allow-disposable-state", action="store_true")
38
+ args = parser.parse_args()
39
+ if not args.allow_disposable_state:
40
+ parser.error("explicit --allow-disposable-state is required")
41
+ root = Path(__file__).resolve().parents[4]
42
+ state = Path(args.state).resolve()
43
+ state.relative_to(root / ".mockingbird" / "hermes-oracle")
44
+ if not state.parent.is_dir():
45
+ parser.error("create a fresh oracle state directory before launch")
46
+ source = Path(args.sources).resolve()
47
+ verify(source)
48
+ if not (3, 11) <= sys.version_info[:2] < (3, 14):
49
+ parser.error("pinned Hermes requires Python >=3.11,<3.14")
50
+ import aiohttp
51
+ from aiohttp import web
52
+ if aiohttp.__version__ != LOCK["aiohttp"]:
53
+ parser.error("aiohttp version differs from source-lock.json")
54
+
55
+ provenance = {}
56
+ base = {**{key: value for key, value in vars(typing).items() if not key.startswith("__")}, "asyncio": asyncio, "contextlib": contextlib,
57
+ "ContextVar": contextvars.ContextVar, "contextmanager": contextlib.contextmanager,
58
+ "hmac": hmac, "itertools": itertools, "json": json, "logging": logging,
59
+ "os": os, "re": re, "sqlite3": sqlite3, "subprocess": subprocess,
60
+ "sys": sys, "threading": threading, "time": time, "Path": Path,
61
+ "logger": logging.getLogger("oracle"), "web": web}
62
+ def selected(filename, names, extra=None):
63
+ namespace = {**base, **(extra or {})}
64
+ provenance[filename] = definitions(source / filename, names, namespace)
65
+ return namespace
66
+ for name in ["agent", "gateway", "tools", "hermes_cli"]:
67
+ package(name)
68
+ safety = types.ModuleType("agent.file_safety")
69
+ safety.__dict__.update(selected("file_safety.py", ["_BLOCKED_PROJECT_ENV_BASENAMES"]))
70
+ sys.modules[safety.__name__] = safety
71
+ redactor = load_module(source / "agent_redact.py", "agent.redact")
72
+ interrupt = load_module(source / "interrupt_compat.py", "agent.interrupt_compat")
73
+ sqlite_runtime = load_module(source / "sqlite_runtime.py", "hermes_cli.sqlite_runtime")
74
+ sys.modules[sqlite_runtime.__name__] = sqlite_runtime
75
+ state_module = types.ModuleType("hermes_state")
76
+ state_module.__dict__.update(selected("hermes_state.py", ["apply_wal_with_fallback"], {
77
+ "_is_sqlite_wal_reset_vulnerable": sqlite_runtime.is_sqlite_wal_reset_vulnerable,
78
+ }))
79
+ sys.modules["hermes_state"] = state_module
80
+ status = types.ModuleType("gateway.status")
81
+ status.__dict__.update(selected("gateway_status.py", ["_pid_exists", "get_process_start_time"]))
82
+ sys.modules["gateway.status"] = status
83
+ # Only these execution integration hooks are substituted. They never dispatch
84
+ # tools, persist sessions, spawn child tools or register real approval work.
85
+ approval = types.ModuleType("tools.approval")
86
+ for name in ["register_gateway_notify", "unregister_gateway_notify", "reset_current_session_key", "set_current_session_key"]:
87
+ setattr(approval, name, lambda *a, **k: None)
88
+ sys.modules[approval.__name__] = approval
89
+ session = types.ModuleType("gateway.session_context")
90
+ session.clear_session_vars = lambda *a: None
91
+ sys.modules[session.__name__] = session
92
+ clock = [args.time]
93
+ controlled_time = types.SimpleNamespace(time=lambda: clock[0])
94
+ store_module = load_module(source / "api_server_run_idempotency.py", "oracle_store")
95
+ store_module.time = controlled_time
96
+ runs = load_module(source / "api_server_runs.py", "oracle_runs")
97
+ runs.time = controlled_time
98
+ api = types.SimpleNamespace(**selected("api_server.py", [
99
+ "_openai_error", "_redact_api_error_text", "_request_agent_overrides",
100
+ "_approval_event_choices", "_expected_api_key", "_check_auth", "_parse_session_key_header",
101
+ ], {"redact_sensitive_text": redactor.redact_sensitive_text}))
102
+ api._api_request_profile = contextvars.ContextVar("oracle_profile", default="default")
103
+ api._api_request_browser_control_principal = contextvars.ContextVar("oracle_principal", default=None)
104
+ api._api_request_browser_control_transport_family = contextvars.ContextVar("oracle_transport", default=None)
105
+ api._ProviderAuthResolutionError = type("UnexercisedProviderAuthError", (Exception,), {})
106
+ api._publish_turn_process_ownership = lambda *a: None
107
+ api._clear_turn_process_ownership = lambda *a: None
108
+ api._reap_disconnected_agent_processes = lambda *a, **k: None
109
+ api.request_hard_interrupt = interrupt.request_hard_interrupt
110
+ # Preserve the selected functions' original global context variable binding.
111
+ for name in ["_expected_api_key", "_check_auth"]:
112
+ getattr(api, name).__globals__["_api_request_profile"] = api._api_request_profile
113
+ room = selected("room_grants.py", ["_room_grant_token"])
114
+
115
+ class ScriptedAgent:
116
+ session_prompt_tokens = 0
117
+ session_completion_tokens = 0
118
+ session_total_tokens = 0
119
+ def __init__(self):
120
+ self.release = threading.Event()
121
+ self.result = {"final_response": "synthetic output"}
122
+ def hard_interrupt(self, message=None):
123
+ # Hold acknowledgement until the fixture selects cancellation or a
124
+ # completion-winning race; the upstream stop handler owns stopping.
125
+ self.interrupted = True
126
+ def run_conversation(self, **kwargs):
127
+ if not self.release.wait(30):
128
+ raise TimeoutError("oracle executor was not explicitly settled")
129
+ return self.result
130
+
131
+ class Adapter:
132
+ _model_name = "hermes-agent"
133
+ _api_key = ""
134
+ _MAX_SESSION_HEADER_LEN = 256
135
+ _RUN_STATUS_TTL = 3600
136
+ _RUN_STREAM_TTL = 300
137
+ _room_grant_token = staticmethod(room["_room_grant_token"])
138
+ _expected_api_key = api._expected_api_key
139
+ _check_auth = api._check_auth
140
+ _parse_session_key_header = api._parse_session_key_header
141
+ def __init__(self):
142
+ self._background_tasks = set()
143
+ self._stopping_run_ids = set()
144
+ self._active_run_agents = {}
145
+ self._active_run_tasks = {}
146
+ self._response_store = {}
147
+ runs._initialize_run_state(self, store_factory=lambda: store_module.RunIdempotencyStore(str(state)))
148
+ def _make_run_event_callback(self, run_id, loop):
149
+ return runs._make_run_event_callback(self, run_id, loop, _api_server=api)
150
+ def _run_idempotency_scope(self, request):
151
+ return runs._run_idempotency_scope(self, request, _api_server=api)
152
+ def _check_run_auth(self, request, *, permission):
153
+ return runs._check_run_auth(self, request, permission=permission, _api_server=api)
154
+ async def _normalize_room_dispatch(self, request, body):
155
+ if self._room_grant_token(request) or "hosted_room_dispatch" in body or "_room_execution_policy" in body:
156
+ raise ValueError("hosted rooms are outside this oracle")
157
+ return body, None
158
+ def _resolve_route(self, model):
159
+ return None
160
+ def _request_route_conflict_error(self, **kwargs):
161
+ return None
162
+ def _concurrency_limited_response(self):
163
+ return None
164
+ async def _conversation_history_for_session(self, session):
165
+ return []
166
+ def _profile_scope(self, profile):
167
+ return contextlib.nullcontext()
168
+ def _bind_api_server_session(self, **kwargs):
169
+ return []
170
+ def _activate_admitted_request(self):
171
+ pass
172
+ def _create_agent(self, **kwargs):
173
+ return ScriptedAgent()
174
+ for name in ["_set_run_status", "_durable_run_status", "_request_owns_run"]:
175
+ setattr(Adapter, name, getattr(runs, name))
176
+ adapter = Adapter()
177
+
178
+ async def submit(request):
179
+ error = adapter._check_auth(request)
180
+ if error is not None:
181
+ return error
182
+ return await runs._handle_runs(adapter, request, _api_server=api)
183
+ async def poll(request):
184
+ return await runs._handle_get_run(adapter, request, _api_server=api)
185
+ async def stop(request):
186
+ return await runs._handle_stop_run(adapter, request, _api_server=api)
187
+ async def control(request):
188
+ body = await request.json()
189
+ if "time" in body:
190
+ clock[0] = float(body["time"])
191
+ if body.get("sweep"):
192
+ runs._sweep_orphaned_runs_once(adapter, clock[0])
193
+ if "settle" in body:
194
+ run_id = body["settle"]
195
+ agent = adapter._active_run_agents[run_id]
196
+ agent.result = body["result"]
197
+ agent.release.set()
198
+ await asyncio.wait_for(asyncio.shield(adapter._active_run_tasks[run_id]), 5)
199
+ return web.json_response({"ok": True})
200
+ async def crash(request):
201
+ # Deliberately omit executor cleanup and SQLite close: next invocation
202
+ # opens the same disposable database in a genuinely new process.
203
+ os._exit(0)
204
+ async def serve():
205
+ app = web.Application()
206
+ app.router.add_post("/v1/runs", submit)
207
+ app.router.add_get("/v1/runs/{run_id}", poll)
208
+ app.router.add_post("/v1/runs/{run_id}/stop", stop)
209
+ app.router.add_post("/__oracle/control", control)
210
+ app.router.add_post("/__oracle/crash", crash)
211
+ runner = web.AppRunner(app, access_log=None)
212
+ await runner.setup()
213
+ site = web.TCPSite(runner, "127.0.0.1", 0)
214
+ await site.start()
215
+ port = site._server.sockets[0].getsockname()[1]
216
+ print(json.dumps({"url": f"http://127.0.0.1:{port}", "commit": LOCK["commit"],
217
+ "python": sys.version.split()[0], "aiohttp": aiohttp.__version__,
218
+ "sqlite": sqlite3.sqlite_version, "pid": os.getpid(),
219
+ "journal_mode": adapter._run_idempotency_store._conn.execute("PRAGMA journal_mode").fetchone()[0],
220
+ "dependencies": {name: importlib.metadata.version(name) for name in ["aiohttp", "aiohappyeyeballs", "aiosignal", "attrs", "frozenlist", "idna", "multidict", "propcache", "typing-extensions", "yarl"]},
221
+ "definitions": provenance}), flush=True)
222
+ await asyncio.Event().wait()
223
+ asyncio.run(serve())
224
+
225
+
226
+ if __name__ == "__main__":
227
+ main()
@@ -0,0 +1,52 @@
1
+ {
2
+ "release": "v2026.8.31",
3
+ "commit": "29112bef099274229cadff79cdff7bf7b99c4b77",
4
+ "python": ">=3.11,<3.14",
5
+ "aiohttp": "3.14.3",
6
+ "sources": {
7
+ "api_server_runs.py": {
8
+ "path": "gateway/platforms/api_server_runs.py",
9
+ "sha256": "048ae843592d701ff47437bd8edd47cd64ca6c0fdf88a71045bdb6fb337fbc63"
10
+ },
11
+ "api_server_run_idempotency.py": {
12
+ "path": "gateway/platforms/api_server_run_idempotency.py",
13
+ "sha256": "746904b3b6ed45d8359655b2e87c1f101394b13f6253e718ac9a83acd46174f2"
14
+ },
15
+ "api_server.py": {
16
+ "path": "gateway/platforms/api_server.py",
17
+ "sha256": "6aec6687d47c81b3567e930882a499c5c5c041ecbe40e1d08f93038212401962"
18
+ },
19
+ "hermes_state.py": {
20
+ "path": "hermes_state.py",
21
+ "sha256": "696fd2d37d66a599228f5b1be5d5b76423b744149c83a2fbd59bf3f45a5f3891"
22
+ },
23
+ "sqlite_runtime.py": {
24
+ "path": "hermes_cli/sqlite_runtime.py",
25
+ "sha256": "682ae7f6a783673b7687377a37f210f5b1f86f54d227baed95df279bdd561310"
26
+ },
27
+ "agent_redact.py": {
28
+ "path": "agent/redact.py",
29
+ "sha256": "db98c5b9cf5727abbb471b5009d19d3f8230863869cde207d53c5e5d8d4831f0"
30
+ },
31
+ "file_safety.py": {
32
+ "path": "agent/file_safety.py",
33
+ "sha256": "f2cb782cda3f091acba507e3af6a31e7326770dfc6b75657cd5adb1006521db9"
34
+ },
35
+ "interrupt_compat.py": {
36
+ "path": "agent/interrupt_compat.py",
37
+ "sha256": "f10ecfe97b6be6d150c3e41d5d377a9ca845e74d454240f2d6506f753dc23441"
38
+ },
39
+ "room_grants.py": {
40
+ "path": "gateway/platforms/api_server_room_grants.py",
41
+ "sha256": "d6e51eae92043ed8ab116e4f555db3c100829c93b61d516e31fc239ff90da204"
42
+ },
43
+ "gateway_status.py": {
44
+ "path": "gateway/status.py",
45
+ "sha256": "c7f2615117643f536f4d678a185dc1f9e6e407daea79e85f351613efac0ecefd"
46
+ },
47
+ "pyproject.toml": {
48
+ "path": "pyproject.toml",
49
+ "sha256": "c70c8b52f6cc08a4e65f0fc1713c26814fd4f19811bc7e01de645009b2a76600"
50
+ }
51
+ }
52
+ }