@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.
- package/API_EVIDENCE.md +418 -0
- package/CHANGELOG.md +5 -0
- package/DISCOVERY.md +54 -0
- package/README.md +290 -0
- package/SUPPORT.md +16 -0
- package/dist/chunk-E3W77NV6.js +821 -0
- package/dist/chunk-E3W77NV6.js.map +7 -0
- package/dist/chunk-RLLHIXKE.js +3618 -0
- package/dist/chunk-RLLHIXKE.js.map +7 -0
- package/dist/cli.js +19 -0
- package/dist/cli.js.map +7 -0
- package/dist/index.d.ts +1285 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +7 -0
- package/dist/server.d.ts +1742 -0
- package/dist/server.js +12 -0
- package/dist/server.js.map +7 -0
- package/evidence/fingerprint-python.json +156 -0
- package/evidence/pinned-oracle.json +616 -0
- package/openapi.yaml +342 -0
- package/oracle/README.md +99 -0
- package/oracle/compare.mjs +274 -0
- package/oracle/comparison.mjs +16 -0
- package/oracle/comparison.test.mjs +41 -0
- package/oracle/fetch.py +29 -0
- package/oracle/requirements.lock +11 -0
- package/oracle/server.py +227 -0
- package/oracle/source-lock.json +52 -0
- package/oracle/sources.py +71 -0
- package/oracle/test_sources.py +50 -0
- package/package.json +117 -0
package/API_EVIDENCE.md
ADDED
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
# Hermes peer-run API evidence
|
|
2
|
+
|
|
3
|
+
Research for the public tracked-run mock; no Hermes runtime, inference, provider
|
|
4
|
+
credentials, or Kanban dispatcher was executed. Source inspection is not observed
|
|
5
|
+
parity. The package remains unimplemented until the later implementation stories.
|
|
6
|
+
|
|
7
|
+
## Provenance and compatibility target
|
|
8
|
+
|
|
9
|
+
Retrieved 2026-09-27. Required release: `v2026.8.31` of
|
|
10
|
+
[NousResearch/hermes-agent](https://github.com/NousResearch/hermes-agent).
|
|
11
|
+
`git ls-remote` resolved annotated tag object
|
|
12
|
+
`6e8f8418e6378eb2617e4de074e13dedd091b8af` to commit
|
|
13
|
+
`29112bef099274229cadff79cdff7bf7b99c4b77`. All pinned links below use that commit.
|
|
14
|
+
Downloaded source copies are retained under the ignored project directory
|
|
15
|
+
`.mockingbird/hermes-evidence/v2026.8.31/`; they were read, not imported or executed.
|
|
16
|
+
|
|
17
|
+
Context7 resolution selected `/nousresearch/hermes-agent`. Its advertised versions
|
|
18
|
+
were `v2026.4.8`, `v2026.4.16`, and `v2026.6.5`; it did not advertise the target.
|
|
19
|
+
Two queries covered submission/polling/stopping and idempotency/fingerprint,
|
|
20
|
+
profile/session scope, restart and retention. Results referenced current `main`:
|
|
21
|
+
|
|
22
|
+
- [Programmatic integration](https://github.com/NousResearch/hermes-agent/blob/main/website/docs/developer-guide/programmatic-integration.md): asynchronous POST, poll, and stop route descriptions; not exact envelopes.
|
|
23
|
+
- [Current run handlers](https://github.com/NousResearch/hermes-agent/blob/main/gateway/platforms/api_server_runs.py): routes and request processing, including newer session-key-to-conversation resolution not present in the pinned handler.
|
|
24
|
+
- [Current API guide](https://github.com/NousResearch/hermes-agent/blob/main/website/docs/user-guide/features/api-server.md): cooperative stopping. This is documentation, not a claim that every stop ends as cancelled.
|
|
25
|
+
|
|
26
|
+
No unsupported Context7 version was requested or invented. Current-main snippets
|
|
27
|
+
have no immutable revision from Context7 and are discovery evidence only.
|
|
28
|
+
|
|
29
|
+
### Pinned sources
|
|
30
|
+
|
|
31
|
+
| ID | Source and relevant location | Evidence class |
|
|
32
|
+
| --- | --- | --- |
|
|
33
|
+
| R | [Run handlers](https://github.com/NousResearch/hermes-agent/blob/29112bef099274229cadff79cdff7bf7b99c4b77/gateway/platforms/api_server_runs.py), `_http_routes`, `_handle_runs`, `_handle_get_run`, `_handle_stop_run`, `_durable_run_status`, `_sweep_orphaned_runs_once` | Pinned source |
|
|
34
|
+
| I | [Idempotency store](https://github.com/NousResearch/hermes-agent/blob/29112bef099274229cadff79cdff7bf7b99c4b77/gateway/platforms/api_server_run_idempotency.py), `RunIdempotencyStore` | Pinned source |
|
|
35
|
+
| A | [API adapter](https://github.com/NousResearch/hermes-agent/blob/29112bef099274229cadff79cdff7bf7b99c4b77/gateway/platforms/api_server.py), error helper, admission, header parser, concurrency, TTL constants | Pinned source |
|
|
36
|
+
| D | [API guide](https://github.com/NousResearch/hermes-agent/blob/29112bef099274229cadff79cdff7bf7b99c4b77/website/docs/user-guide/features/api-server.md), tracked-run and session-key sections | Pinned documentation |
|
|
37
|
+
| T | [Run tests](https://github.com/NousResearch/hermes-agent/blob/29112bef099274229cadff79cdff7bf7b99c4b77/tests/gateway/test_api_server_runs.py), stop races, replay, retention, restart and scope tests | Pinned test source; not run here |
|
|
38
|
+
|
|
39
|
+
SHA-256 of the retrieved bytes:
|
|
40
|
+
|
|
41
|
+
| File | SHA-256 |
|
|
42
|
+
| --- | --- |
|
|
43
|
+
| `api_server_runs.py` | `048ae843592d701ff47437bd8edd47cd64ca6c0fdf88a71045bdb6fb337fbc63` |
|
|
44
|
+
| `api_server_run_idempotency.py` | `746904b3b6ed45d8359655b2e87c1f101394b13f6253e718ac9a83acd46174f2` |
|
|
45
|
+
| `api_server.py` | `6aec6687d47c81b3567e930882a499c5c5c041ecbe40e1d08f93038212401962` |
|
|
46
|
+
| `api-server.md` | `4f2f3f30cdcc4c5635bfba5dff97009120e1a357647715c1935806184d443b29` |
|
|
47
|
+
| `test_api_server_runs.py` | `c610484550aef243d05c9dab4d4672e4af1f71161026c838fdaf2b84d886e287` |
|
|
48
|
+
|
|
49
|
+
## Submission and polling
|
|
50
|
+
|
|
51
|
+
R lines404–1014 establish `POST /v1/runs`. Admission authenticates and checks
|
|
52
|
+
gateway draining first (A `_admit_api_agent_request`). Within the handler:
|
|
53
|
+
parse the memory-scope header, parse JSON, normalize room dispatch, validate the
|
|
54
|
+
idempotency key, fingerprint, validate input/history/routing, look up an existing
|
|
55
|
+
key, then check capacity and atomically reserve a new run. Invalid input and
|
|
56
|
+
capacity rejection do not reserve a key; replay precedes capacity rejection.
|
|
57
|
+
Drain rejection can still precede replay.
|
|
58
|
+
|
|
59
|
+
For the ordinary peer path, `input` is a nonempty string or array whose final
|
|
60
|
+
item supplies a truthy `content`. Earlier array messages become conversation
|
|
61
|
+
history when explicit/resolved history is absent. The pinned code does not check
|
|
62
|
+
that the final item's role is `user`; do not invent such validation. It does not
|
|
63
|
+
robustly validate arbitrary array elements or every JSON root type; exact malformed
|
|
64
|
+
non-object behavior is an oracle gap, not permission to invent a provider envelope.
|
|
65
|
+
|
|
66
|
+
Optional fields read by R include `instructions`, `session_id`, `model`,
|
|
67
|
+
`conversation_history`, `previous_response_id`, and model/provider options through
|
|
68
|
+
A `_request_agent_overrides`. Explicit truthy history must be an array of objects
|
|
69
|
+
with `role` and `content`; those values are coerced to strings. Explicit history
|
|
70
|
+
wins over previous-response history. A missing previous-response record is ignored
|
|
71
|
+
by this pinned run handler, unlike the separate Responses API. Transcript loading,
|
|
72
|
+
model routing and execution are not inference requirements for the mock; retain
|
|
73
|
+
synthetic metadata and script public observations without executing them.
|
|
74
|
+
|
|
75
|
+
A new run has an opaque `run_` plus UUID-hex ID. The POST response is exactly
|
|
76
|
+
HTTP202 with `{ "run_id": "run_<opaque>", "status": "started", "replayed": false }`.
|
|
77
|
+
`started` is an admission response, not the stored execution state. Initial stored
|
|
78
|
+
state is `queued`, then background execution sets `running`. Echo
|
|
79
|
+
`X-Hermes-Session-Key` when the validated header is present; no replay header is
|
|
80
|
+
added to a new submission. [R `_handle_runs`; T `test_start_returns_202`]
|
|
81
|
+
|
|
82
|
+
`GET /v1/runs/{run_id}` returns HTTP200 with the current status object. Normal
|
|
83
|
+
newly admitted records contain `object: "hermes.run"`, `run_id`, `status`, numeric
|
|
84
|
+
Unix-seconds `created_at` and `updated_at`, `session_id`, and `model`. Session ID
|
|
85
|
+
is the supplied/resolved ID or defaults to the run ID; model is the supplied
|
|
86
|
+
value when the `model` key is present (including null or empty values), and the
|
|
87
|
+
adapter virtual model only when the key is absent. R initial_status uses
|
|
88
|
+
`model=body.get("model", self._model_name)`, not Python `or`. Updates preserve other fields and the original
|
|
89
|
+
creation timestamp. `last_event` appears when an event sets it. Completion adds
|
|
90
|
+
`output` and `usage` with `input_tokens`, `output_tokens`, `total_tokens`; failure
|
|
91
|
+
adds `error`; approval state can add `approval`; completion can include
|
|
92
|
+
`pending_steer`. Do not synthesize these optional fields in every state.
|
|
93
|
+
[Sources R `_set_run_status`, execution completion, `_handle_get_run`.]
|
|
94
|
+
|
|
95
|
+
Public states observed in source are `queued`, `running`, `waiting_for_approval`,
|
|
96
|
+
`stopping`, `completed`, `failed`, `cancelled`, and `interrupted`. Only the latter
|
|
97
|
+
four are terminal for durable replay. The mock can script observations without
|
|
98
|
+
implementing tools, approval resolution, SSE, steering, or agent execution.
|
|
99
|
+
|
|
100
|
+
## Errors and overload
|
|
101
|
+
|
|
102
|
+
A `_openai_error` normally yields
|
|
103
|
+
`{"error":{"message":"…","type":"invalid_request_error","param":null,"code":null}}`.
|
|
104
|
+
Codes/types below replace the null/default values. Messages are upstream templates,
|
|
105
|
+
not examples containing real user input. [A lines1224–1234; R handlers.]
|
|
106
|
+
|
|
107
|
+
| Trigger | HTTP | Message | Code/type or headers |
|
|
108
|
+
| --- | --- | --- | --- |
|
|
109
|
+
| JSON parse failure | 400 | `Invalid JSON` | Default envelope |
|
|
110
|
+
| Falsy/missing input | 400 | `Missing 'input' field` | Default envelope |
|
|
111
|
+
| No truthy final input content | 400 | `No user message found in input` | Default envelope |
|
|
112
|
+
| Invalid truthy history type | 400 | `'conversation_history' must be an array of message objects` | Default envelope |
|
|
113
|
+
| Invalid history item at index i | 400 | `conversation_history[i] must have 'role' and 'content' fields` | Substitute decimal index |
|
|
114
|
+
| Invalid nonempty idempotency key | 400 | `Idempotency-Key must be 1-255 visible ASCII characters` | `invalid_idempotency_key` |
|
|
115
|
+
| Same scoped key, different fingerprint | 409 | `Idempotency-Key was already used with a different request payload` | `idempotency_key_conflict` |
|
|
116
|
+
| Missing or inaccessible run | 404 | `Run not found: <run_id>` | `run_not_found`; GET and stop |
|
|
117
|
+
| Nonterminal run owned by another live process, no local worker | 409 | `Run is not active in this gateway process: <run_id>` | `run_not_active`; stop |
|
|
118
|
+
| Capacity exhausted | 429 | `Too many concurrent runs (max <limit>)` | type `rate_limit_error`, code `rate_limit_exceeded`, `Retry-After: 1` |
|
|
119
|
+
| Gateway draining | 503 | `Gateway is draining existing work; retry shortly.` | `gateway_draining`, `Retry-After: 1` |
|
|
120
|
+
|
|
121
|
+
A capacity limit of zero disables the limit; transport-buffer expiry does not
|
|
122
|
+
release a live worker's capacity. Configured auth/profile validation and room grants
|
|
123
|
+
have additional errors outside this synthetic peer contract; no real authorization
|
|
124
|
+
implementation or credentials are required. Request-size middleware has separate
|
|
125
|
+
400/413 behavior; the 10,000,000-byte configured bound is source evidence, not a
|
|
126
|
+
claim of verified transport parity.
|
|
127
|
+
|
|
128
|
+
## Fingerprint, replay and identity
|
|
129
|
+
|
|
130
|
+
`Idempotency-Key` is trimmed first. An absent or whitespace-only header disables
|
|
131
|
+
idempotency. A remaining value longer than255 characters or containing code points
|
|
132
|
+
outside33–126 fails400. Do not reject whitespace-only values as nonempty invalid
|
|
133
|
+
keys after trimming. [R lines452–480.]
|
|
134
|
+
|
|
135
|
+
The fingerprint is SHA-256 of UTF-8 Python `json.dumps` of
|
|
136
|
+
`{"body": normalized_body, "gateway_session_key": validated_key_or_empty_string}`,
|
|
137
|
+
with `sort_keys=True`, `separators=(",", ":")`, `ensure_ascii=False`. It includes
|
|
138
|
+
the entire normalized body, including unknown fields, not merely input text.
|
|
139
|
+
Object-key order is immaterial; array order, changed fields and memory key matter.
|
|
140
|
+
Changing `session_id` changes the fingerprint rather than granting a fresh key
|
|
141
|
+
namespace. Matching Python serialization requires explicit numeric/unicode test
|
|
142
|
+
vectors before claiming fidelity: JavaScript JSON serialization alone does not
|
|
143
|
+
preserve Python distinctions such as parsed integer1 versus float1.0.
|
|
144
|
+
|
|
145
|
+
For ordinary API calls the ownership/replay scope is SHA-256 of
|
|
146
|
+
`profile + NUL + configured_identity`; profile defaults to `default`, identity is
|
|
147
|
+
the expected configured API key or the literal unauthenticated-listener fallback.
|
|
148
|
+
It is not derived from arbitrary supplied bearer text. Hosted-room requests use a
|
|
149
|
+
separate verified-claims scope (room/install/authority epoch/member/target/profile);
|
|
150
|
+
room token generation and validation are excluded. The mock must model isolation
|
|
151
|
+
using explicit synthetic profile/identity controls, without persisting credentials
|
|
152
|
+
or changing consumer authorization. [R `_run_idempotency_scope`.]
|
|
153
|
+
|
|
154
|
+
`X-Hermes-Session-Key` is a distinct long-term-memory scope. A strips it, treats
|
|
155
|
+
empty as absent, caps it at256 characters and rejects CR/LF/NUL. In the real
|
|
156
|
+
adapter a nonempty key without configured API-key support gets403. The two header
|
|
157
|
+
validation400 errors have only `message` and `type`, omitting `param` and `code`:
|
|
158
|
+
`Invalid session key` or `Session key too long`. These facts must not become an
|
|
159
|
+
unsolicited auth implementation in Mockingbird. The pinned run handler passes the
|
|
160
|
+
memory key to the agent but does not derive `session_id` from it; the current-main
|
|
161
|
+
Context7 excerpt does. [A `_parse_session_key_header`; R lines546–607.]
|
|
162
|
+
|
|
163
|
+
Replay returns HTTP202 with the original `run_id`, current stored `status`, and
|
|
164
|
+
`replayed: true`; header `Idempotency-Replayed: true`, plus the memory-key echo if
|
|
165
|
+
present. This applies to terminal as well as in-flight retained runs. A unique
|
|
166
|
+
(scope,key) reservation and unique run ID enforce one admission for concurrent
|
|
167
|
+
identical submissions. Conflict reserves no replacement. Requests with no key
|
|
168
|
+
always create a new run. There is no public idempotency lookup endpoint.
|
|
169
|
+
[Sources R replay/reservation branches; I `lookup`/`reserve`; T replay/scope tests.]
|
|
170
|
+
|
|
171
|
+
Peer-run ID is the public lifecycle identity. Session ID is conversation context;
|
|
172
|
+
memory key is long-term memory context; idempotency key is a scoped admission key.
|
|
173
|
+
Kanban attempt IDs and client intake IDs are external identities, never substitutes
|
|
174
|
+
for a public run ID. No Kanban or Initiative behavior is inferred from these routes.
|
|
175
|
+
|
|
176
|
+
## Stop, completion and logical restart
|
|
177
|
+
|
|
178
|
+
`POST /v1/runs/{run_id}/stop` returns HTTP200
|
|
179
|
+
`{"run_id":"run_<opaque>","status":"stopping"}` for active work. It updates
|
|
180
|
+
stored state to `stopping`/`last_event: "run.stopping"` and requests interruption;
|
|
181
|
+
it does not synchronously declare completion or remove worker handles. Repeated
|
|
182
|
+
stop on still-active work follows the same branch. A terminal run returns its
|
|
183
|
+
whole stored status object with HTTP200; it is not rewritten to cancelled.
|
|
184
|
+
A missing run returns404; a retained nonterminal run without a local agent/task
|
|
185
|
+
returns409 as above. [R lines1353–1422.]
|
|
186
|
+
|
|
187
|
+
When a stopped executor returns `interrupted: true`, status becomes `cancelled`.
|
|
188
|
+
If completion wins and returns a normal final result, it becomes `completed`,
|
|
189
|
+
even after a stop request. Structured execution failure remains `failed`.
|
|
190
|
+
Stopping queued work before agent creation also ends as cancelled. The mock must
|
|
191
|
+
script these distinct outcomes and preserve pending work between acceptance and
|
|
192
|
+
settlement. [R lines705–719 and858–927; T
|
|
193
|
+
`test_completion_wins_before_uncooperative_stop_is_acknowledged`.]
|
|
194
|
+
|
|
195
|
+
Only keyed runs are persisted in I. On cache miss, R hydrates durable status in
|
|
196
|
+
scope. A nonterminal record whose owner PID/process-start identity is no longer
|
|
197
|
+
alive becomes `interrupted`, with `error: "The gateway restarted before this run settled."`,
|
|
198
|
+
`last_event: "run.interrupted"`, and an updated timestamp; the changed status is
|
|
199
|
+
persisted. Terminal records remain terminal. A still-live owner is not falsely
|
|
200
|
+
interrupted; stop from another process may therefore return409. Keyless records
|
|
201
|
+
are in-memory and unavailable after real adapter replacement. Durable storage can
|
|
202
|
+
fall back to memory, explicitly losing restart durability. Model restart as a
|
|
203
|
+
synthetic control, not an assertion of actual process/disk persistence.
|
|
204
|
+
[R `_durable_run_status`; I constructor; T restart/hydration tests.]
|
|
205
|
+
|
|
206
|
+
## Retention and exact boundaries
|
|
207
|
+
|
|
208
|
+
There are separate lifetimes, not a single run TTL. [A lines7507–7508;
|
|
209
|
+
R lines1425–1474; I `_prune_stale_terminal_locked`, `status_for_run`, `update_status`.]
|
|
210
|
+
|
|
211
|
+
| State | Rule in pinned source |
|
|
212
|
+
| --- | --- |
|
|
213
|
+
| Unsubscribed SSE buffer | Sweep when age is strictly greater than300seconds; active execution/control survives |
|
|
214
|
+
| Cached completed/failed/cancelled status | Sweep when now minus `updated_at` is strictly greater than3600seconds |
|
|
215
|
+
| Cached interrupted status | Omitted from this in-memory sweep's terminal set; do not silently add it |
|
|
216
|
+
| Ordinary durable keyed terminal reservation | Prune when stored DB `updated_at` is strictly less than now minus86400seconds; equality retains it |
|
|
217
|
+
| Durable nonterminal reservation | Never prune solely because of age |
|
|
218
|
+
| Room retention horizon | Terminal pruning at now greater than or equal to positive `retention_until` |
|
|
219
|
+
| Acknowledged room receipt | Terminal pruning at acknowledged time less than or equal to now minus86400seconds |
|
|
220
|
+
|
|
221
|
+
The periodic memory sweep runs every60seconds. Durable pruning is lazy in
|
|
222
|
+
`lookup`/`reserve`; `status_for_run` and ownership lookup do not prune. Thus a GET
|
|
223
|
+
after24hours is not automatically404: it may return cache or hydrate a still-stored
|
|
224
|
+
row until admission triggers pruning. A pruned row can also coexist temporarily
|
|
225
|
+
with cached status. After both applicable stores no longer contain the run, GET
|
|
226
|
+
returns the normal `run_not_found`404; never invent an `expired` execution status.
|
|
227
|
+
After expiry releases a reservation, the same key can admit a new run.
|
|
228
|
+
|
|
229
|
+
Status changes and terminal/payload updates persist; repeated progress events
|
|
230
|
+
with unchanged execution status may update the memory timestamp without refreshing
|
|
231
|
+
the database timestamp. Preserve these clocks independently for exact retention
|
|
232
|
+
claims. Room horizon/acknowledgment operations are documented only as boundaries
|
|
233
|
+
of the ordinary peer subset, not as promised new room endpoints.
|
|
234
|
+
|
|
235
|
+
## Reconciliation, implementation gates and remaining gaps
|
|
236
|
+
|
|
237
|
+
- Documentation's concise new-run example omits `replayed`; pinned source includes
|
|
238
|
+
it. Use source envelopes. Documentation's general24hour statement omits active
|
|
239
|
+
reservations, lazy pruning, cached status and room exceptions; use the rules above.
|
|
240
|
+
- Current documentation describes stopping as ending in cancelled. Pinned source
|
|
241
|
+
and its completion-race test also allow completed or failed. Preserve that race.
|
|
242
|
+
- Current-main session-key-to-conversation resolution is not in this pinned handler.
|
|
243
|
+
Do not import it as a silent upgrade. Pinned `input` handling also differs from
|
|
244
|
+
other API families; do not copy Responses API validation wholesale.
|
|
245
|
+
- Later implementation must characterize Python fingerprint serialization with
|
|
246
|
+
synthetic numeric/unicode vectors, exact retention equality and trigger points,
|
|
247
|
+
cached-versus-durable lifetime, replay at capacity, stop races, and restart of
|
|
248
|
+
keyed versus keyless records. No caller recovery/intake policy is implied.
|
|
249
|
+
- Arbitrary malformed roots/elements, model-routing/transcript resolution,
|
|
250
|
+
hosted-room grants, SSE/approval/steer execution, provider credential errors,
|
|
251
|
+
and HTTP middleware edge cases are not independently verified. They block
|
|
252
|
+
corresponding compatibility claims; they do not authorize invented envelopes.
|
|
253
|
+
- No pinned runtime/oracle has run. US-022 must verify the required lifecycle,
|
|
254
|
+
replay/conflict/stop/restart/retention behavior with a scripted execution boundary
|
|
255
|
+
and disposable storage before any real-runtime parity claim. Dependency installs,
|
|
256
|
+
database migrations/cleanup and service lifecycle still require applicable approval.
|
|
257
|
+
- This evidence establishes the research contract for US-015, not delivery of
|
|
258
|
+
US-016–023 or Ready status. No credentials, real prompts or customer data were used.
|
|
259
|
+
|
|
260
|
+
## US-017 implementation refresh (2026-09-27)
|
|
261
|
+
|
|
262
|
+
Resolved `/nousresearch/hermes-agent` again and queried submission/polling fields
|
|
263
|
+
and validation. Context7 still lacks `v2026.8.31`; results cite current-main run
|
|
264
|
+
handlers and programmatic-integration documentation. Its newer terminal flags
|
|
265
|
+
(`completed`, `partial`, `turn_exit_reason`) and shutdown wording are not imported
|
|
266
|
+
into this pinned mock. Re-fetched R at immutable commit
|
|
267
|
+
`29112bef099274229cadff79cdff7bf7b99c4b77`; SHA-256 remains
|
|
268
|
+
`048ae843592d701ff47437bd8edd47cd64ca6c0fdf88a71045bdb6fb337fbc63`.
|
|
269
|
+
Re-read admission, `_set_run_status`, completion/failure and GET branches.
|
|
270
|
+
|
|
271
|
+
The implemented subset admits keyless ordinary submissions, preserves session/model
|
|
272
|
+
metadata, and polls stored public observations. Python JSON truthiness is retained
|
|
273
|
+
for input/history validation. Non-object roots and malformed final message elements
|
|
274
|
+
return explicit mock-only 501 responses because their upstream error behavior is
|
|
275
|
+
not verified. Hosted rooms, nonempty idempotency keys and invalid memory headers
|
|
276
|
+
also return explicit 501 responses at this stage. Valid memory keys are echoed;
|
|
277
|
+
no credentials are checked or used as namespace identity.
|
|
278
|
+
|
|
279
|
+
The default virtual model is `hermes-agent` (A `_resolve_model_name` fallback).
|
|
280
|
+
Model routing, transcript loading and inference are not executed; history is
|
|
281
|
+
validated and discarded with prompt text. Synthetic observation controls are under
|
|
282
|
+
`/__admin/hermes/runs/{id}/observe`, never under vendor paths. GET does not advance
|
|
283
|
+
state. These controls can script the public states and terminal payloads, without
|
|
284
|
+
claiming to execute stop, restart, approval, SSE or steering. Real stop/restart
|
|
285
|
+
behavior remains US-019. Terminal observations cannot be revived by this control.
|
|
286
|
+
Completion supplies empty output/zero usage unless synthetic values are supplied;
|
|
287
|
+
failed/interrupted defaults follow the pinned strings. Leaving approval state
|
|
288
|
+
removes its approval field, as in `_set_run_status`. IDs use the shared deterministic
|
|
289
|
+
sequence encoded into opaque 32-character hex tokens, not actual random UUIDs.
|
|
290
|
+
|
|
291
|
+
Package tests establish internal contract consistency only. The pinned runtime
|
|
292
|
+
comparison remains US-022.
|
|
293
|
+
|
|
294
|
+
## US-018 implementation refresh (2026-09-27)
|
|
295
|
+
|
|
296
|
+
Context7 resolution still lists no target release. Its idempotency query returned
|
|
297
|
+
unrelated current-main profile/session material, not an exact pinned fingerprint
|
|
298
|
+
contract; no such snippets were promoted to evidence. Re-read R fingerprint,
|
|
299
|
+
validation, replay, scope and reservation order, and I lookup/reserve transactions.
|
|
300
|
+
Re-fetched I at pinned commit29112bef099274229cadff79cdff7bf7b99c4b77;
|
|
301
|
+
SHA256 remains746904b3b6ed45d8359655b2e87c1f101394b13f6253e718ac9a83acd46174f2.
|
|
302
|
+
|
|
303
|
+
Idempotency now supports ordinary synthetic scopes: SHA256 of profile, NUL and
|
|
304
|
+
synthetic listener identity; settings never derive from bearer text. The whole
|
|
305
|
+
body plus validated memory-key string is canonicalized and hashed. A local raw
|
|
306
|
+
JSON tokenizer retains number lexemes and duplicate-key last-value semantics;
|
|
307
|
+
integers retain arbitrary precision, floats use Python exponent thresholds and
|
|
308
|
+
integral `.0`, keys sort by Unicode code point. A small local tokenizer was chosen
|
|
309
|
+
over changing the shared HTTP decoder or importing an undeclared transitive JSON
|
|
310
|
+
library. Fetch and exposed Hono entries both preserve raw bytes as decoded text
|
|
311
|
+
until admission. No raw request or canonical prompt text is stored.
|
|
312
|
+
|
|
313
|
+
The committed `evidence/fingerprint-python.json` has 25 synthetic canonical-byte
|
|
314
|
+
and SHA256 vectors generated independently with Python stdlib json/hashlib (Python
|
|
315
|
+
version recorded there). Cases cover int/float distinction, negative zeros, large
|
|
316
|
+
integers, exponent thresholds, subnormal/maximum/overflow/underflow floats,
|
|
317
|
+
BMP/astral sorting, escapes, duplicate keys and prototype-like keys. An additional
|
|
318
|
+
seeded corpus of2,000 finite binary64 values matched Python's canonical text with
|
|
319
|
+
zero differences. This establishes those tested values, not exhaustive float-space
|
|
320
|
+
proof or Hermes runtime parity. Lone surrogate strings are explicit501 because
|
|
321
|
+
pinned Python UTF8 encoding cannot fingerprint them. Nonstandard JSON NaN/Infinity
|
|
322
|
+
literal tokens and middleware/malformed-root behavior remain outside verified input
|
|
323
|
+
claims; standard JSON numeric exponents that overflow are included in vectors.
|
|
324
|
+
|
|
325
|
+
Scoped lookup/reservation/run creation is one synchronous shared SQLite transaction
|
|
326
|
+
after asynchronous hashing. Across concurrent facades, identical submissions create
|
|
327
|
+
one run; conflicts preserve the original. Replays use current public status and
|
|
328
|
+
retain headers. Python header stripping includes U+0085; JSON syntax errors precede
|
|
329
|
+
key validation. The synthetic scope control, owners and reservations participate
|
|
330
|
+
in shared reset and Timeline. Terminal replay survives reconstructing the API with
|
|
331
|
+
the same backing client/namespace. This is a modeled adapter/storage boundary, not
|
|
332
|
+
real process/disk persistence or completed logical-restart semantics: unfinished
|
|
333
|
+
restart interruption remainsUS-019 and retention remainsUS-020.
|
|
334
|
+
|
|
335
|
+
## US-019 stop and logical restart refresh (2026-09-27)
|
|
336
|
+
|
|
337
|
+
Resolved and queried Context7 for stop/race/restart again. Results remain current
|
|
338
|
+
main and advertise no target version; new shutdown flags/text were not substituted
|
|
339
|
+
for the pinned source. Refetched R at immutable29112bef099274229cadff79cdff7bf7b99c4b77,
|
|
340
|
+
SHA256 still048ae843592d701ff47437bd8edd47cd64ca6c0fdf88a71045bdb6fb337fbc63.
|
|
341
|
+
Read R `_handle_stop_run` and `_durable_run_status` plus pinned completion-race and
|
|
342
|
+
restart test locations. Stop first checks scoped visibility, returns full terminal
|
|
343
|
+
status unchanged, rejects a live foreign owner without local work with409, or
|
|
344
|
+
records stopping and returns the two-field acceptance200.
|
|
345
|
+
|
|
346
|
+
Implemented explicit namespaced logical restart, with stale/alive modeled owner
|
|
347
|
+
states. No PID probing, thread, inference, process kill or actual service restart
|
|
348
|
+
occurs. Keyless state disappears; keyed records/reservations remain. Stale-owner
|
|
349
|
+
nonterminal hydration occurs on poll/replay/stop, preserves creation time and
|
|
350
|
+
updates interruption time at that first access. In particular, pinned durable
|
|
351
|
+
hydration uses a direct status.update, so a waiting approval payload is retained
|
|
352
|
+
on interruption (normal `_set_run_status` would clear it). Terminal records and
|
|
353
|
+
repeat interruption reads remain unchanged. A live foreign owner does not become
|
|
354
|
+
interrupted and cannot be stopped in this modeled gateway. Admin observations may
|
|
355
|
+
script its eventual result. Terminal controls still reject revival.
|
|
356
|
+
|
|
357
|
+
All unfinished/terminal states, stop acceptance versus cancellation/failure/winning
|
|
358
|
+
completion, original-key interrupted replay versus new-key admission, keyless
|
|
359
|
+
loss, scopes and shared Timeline ownership restoration are covered by package
|
|
360
|
+
tests. Actual pinned runtime comparison remainsUS-022; retention/cache/durable
|
|
361
|
+
expiry separation remainsUS-020. These controls do not claim actual process or disk
|
|
362
|
+
restart durability.
|
|
363
|
+
|
|
364
|
+
## US-020 retention evidence
|
|
365
|
+
|
|
366
|
+
Re-read pinned R lines 1425–1474 and I lines 241–380; refreshed I from commit
|
|
367
|
+
`29112bef099274229cadff79cdff7bf7b99c4b77`, retaining SHA256
|
|
368
|
+
`746904b3b6ed45d8359655b2e87c1f101394b13f6253e718ac9a83acd46174f2`.
|
|
369
|
+
Context7 still did not advertise the target release; unrelated/current-main
|
|
370
|
+
results were not used as pinned evidence.
|
|
371
|
+
|
|
372
|
+
R sweeps every 60 seconds and drops cached completed/failed/cancelled results only
|
|
373
|
+
when update age exceeds 3600 seconds. Interrupted results are excluded. I prunes
|
|
374
|
+
ordinary terminal rows only on lookup/reserve, with durable updated_at strictly
|
|
375
|
+
less than now minus 86400; status lookup does not prune, and active rows do not age
|
|
376
|
+
out. Persistence tracks status changes, terminal observations and selected payload
|
|
377
|
+
fields separately from cached progress timestamps. Pruning commits before a later
|
|
378
|
+
conflict is returned. Hosted-room retention and SSE buffer expiry are excluded.
|
|
379
|
+
|
|
380
|
+
Tests exercise exact equality and one millisecond beyond, cache recovery from
|
|
381
|
+
durable rows, pruning versus cached survivors, active and interrupted states,
|
|
382
|
+
progress persistence, restart and Timeline. The mock folds elapsed sweep ticks
|
|
383
|
+
into the next admission/observation and offers an explicit single-sweep control;
|
|
384
|
+
it does not start a background worker. This source-backed model remains subject
|
|
385
|
+
to the independent runtime comparison in US-022.
|
|
386
|
+
|
|
387
|
+
## US-021 fault evidence
|
|
388
|
+
|
|
389
|
+
Context7 resolution still lacks v2026.8.31; its general retry/current-main snippets
|
|
390
|
+
were not used as pinned envelopes. Pinned A `_draining_response` lines1693–1704
|
|
391
|
+
returns503, invalid_request_error/gateway_draining and Retry-After1. Capacity check
|
|
392
|
+
lines7190–7208 returns429, rate_limit_error/rate_limit_exceeded and Retry-After1;
|
|
393
|
+
the fixture message uses the pinned default limit10. The explicit presets do not
|
|
394
|
+
implement automatic capacity accounting or gateway lifecycle.
|
|
395
|
+
|
|
396
|
+
Response loss and poll delay are Mockingbird transport scenarios, not invented
|
|
397
|
+
Hermes error responses. Scripted executor failure uses the existing pinned failed
|
|
398
|
+
observation envelope. The native Node consumer uses literal HTTP contracts and no
|
|
399
|
+
provider state helpers. Eligible self-parity and deliberate lifecycle divergence
|
|
400
|
+
tests exercise the comparator; actual pinned runtime execution remains US-022.
|
|
401
|
+
|
|
402
|
+
## US-022 executed pinned oracle
|
|
403
|
+
|
|
404
|
+
The authorized loopback oracle executed the exact pinned run and idempotency
|
|
405
|
+
modules, original ancillary auth/redaction/PID helpers and original SQLite
|
|
406
|
+
journal-mode helper against a new disposable file-backed database. Its scripted
|
|
407
|
+
executor replaced inference; session/approval/tool-process integrations and
|
|
408
|
+
routing/capacity were explicitly bounded as documented in [oracle/README.md](oracle/README.md).
|
|
409
|
+
No credential-enforcement claim is made.
|
|
410
|
+
|
|
411
|
+
[Recorded report](evidence/pinned-oracle.json):33 public HTTP comparisons passed
|
|
412
|
+
with Python3.11.16, aiohttp3.14.3 and SQLite3.53.1, WAL in both distinct process
|
|
413
|
+
epochs. Evidence covers replay/conflict, stop/cancel/completion race/failure,
|
|
414
|
+
terminal/unfinished/keyless actual process restart, interrupted replay, exact
|
|
415
|
+
one-hour cache and24-hour durable boundaries, GET without durable pruning,
|
|
416
|
+
expiry/re-admission and404 after pruning plus sweep. This establishes that matrix,
|
|
417
|
+
not inference, hosted rooms, whole-gateway startup, production auth, automatic
|
|
418
|
+
capacity, power-loss durability or arbitrary malformed-input compatibility.
|
package/CHANGELOG.md
ADDED
package/DISCOVERY.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# @crvouga/mockingbird-service-hermes discovery
|
|
2
|
+
|
|
3
|
+
This is the installed-package index for coding agents and tooling. All relative links resolve
|
|
4
|
+
inside `node_modules/@crvouga/mockingbird-service-hermes/`; no repository checkout is needed to discover the mock's
|
|
5
|
+
supported surface or documented behavior.
|
|
6
|
+
|
|
7
|
+
## Capability and behavior sources
|
|
8
|
+
|
|
9
|
+
| Question | Authoritative file | What it contains |
|
|
10
|
+
| --- | --- | --- |
|
|
11
|
+
| Behaviour and integration | [`README.md`](README.md) | Routes, state transitions, auth, webhooks, controls, presets and deliberate omissions. |
|
|
12
|
+
| Exact capabilities | [`SUPPORT.md`](SUPPORT.md) | Supported, unsupported and parity-covered operations or commands, including reasons for gaps. |
|
|
13
|
+
| Wire contract | [`openapi.yaml`](openapi.yaml) | Machine-readable paths, methods, schemas, responses and parity annotations. |
|
|
14
|
+
| Public API | [`dist/index.d.ts`](dist/index.d.ts) | The installed package's exact TypeScript exports and signatures. |
|
|
15
|
+
| Package metadata | [`package.json`](package.json) | Runtime/entry-point claims, vendor links, parity scope/tier and `mockingbird.discovery`. |
|
|
16
|
+
|
|
17
|
+
Read these together: the contract/capability matrix says *what* is available, while the README
|
|
18
|
+
defines stateful behavior, lifecycle rules, test controls, and intentional oracle differences.
|
|
19
|
+
If prose and an executable surface disagree, report a parity mismatch instead of adding a
|
|
20
|
+
consumer-side workaround.
|
|
21
|
+
|
|
22
|
+
## Parity and oracle
|
|
23
|
+
|
|
24
|
+
- Declared parity surface: **Pinned Hermes run lifecycle and recovery**.
|
|
25
|
+
- Oracle: **Live vendor API or sandbox**.
|
|
26
|
+
- Repository command: `bun run parity:service -- hermes`.
|
|
27
|
+
- Evidence model: Run from a Mockingbird checkout; credentials come only from .env.local or GitHub Actions secrets. Missing credentials exit 2.
|
|
28
|
+
|
|
29
|
+
The npm package contains evidence summaries and the exact contract, not credentials or the
|
|
30
|
+
repository-only parity harness. Self-parity/property and acceptance tests run in the Mockingbird
|
|
31
|
+
repository; live parity is an additional oracle check, not a substitute for the packaged matrix.
|
|
32
|
+
|
|
33
|
+
## Runtime introspection
|
|
34
|
+
|
|
35
|
+
- `GET /__admin/health`
|
|
36
|
+
- `GET /__admin`
|
|
37
|
+
- `GET /__admin/state`
|
|
38
|
+
- `GET /__admin/requests`
|
|
39
|
+
- `GET /__admin/metrics`
|
|
40
|
+
- `GET /__admin/faults/presets`
|
|
41
|
+
- `GET /__admin/ui`
|
|
42
|
+
|
|
43
|
+
For HTTP services, use `x-mockingbird-namespace` (or the documented credential/path carrier) so
|
|
44
|
+
parallel tests do not share state. Admin state, journal, metrics and fault-preset endpoints are
|
|
45
|
+
designed for assertions and diagnosis by consuming test suites.
|
|
46
|
+
|
|
47
|
+
## Report a mismatch or missing capability
|
|
48
|
+
|
|
49
|
+
Follow the [agent reporting contract](https://github.com/crvouga/mockingbird/blob/main/docs/REPORTING_ISSUES.md). Include package version,
|
|
50
|
+
operation/command, a minimal redacted request, actual mock result, expected oracle result or vendor
|
|
51
|
+
documentation, and whether the mismatch appears in the matrix. Never include keys, tokens,
|
|
52
|
+
customer data, prompts, PHI, card data, or unredacted recordings.
|
|
53
|
+
|
|
54
|
+
Service key: `hermes`.
|