@osstack/hermes-adapter 0.2.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/dist/events.js ADDED
@@ -0,0 +1,205 @@
1
+ // Hermes Run Adapter Contract — event schemas (zod).
2
+ // Source: hermes-webui/docs/rfcs/hermes-run-adapter-contract.md
3
+ //
4
+ // 13 event families. Every event is wrapped in a common Envelope so the
5
+ // runtime can replay by (run_id, seq) and the browser can deduplicate.
6
+ import { z } from "zod";
7
+ // ──────────────────────────────────────────────────────────────────────
8
+ // Envelope (shared by every event family)
9
+ // ──────────────────────────────────────────────────────────────────────
10
+ export const EventEnvelopeBase = z.object({
11
+ event_id: z.string(), // stable cursor (e.g. "run_123:42")
12
+ seq: z.number().int().nonnegative(),
13
+ run_id: z.string(),
14
+ session_id: z.string(),
15
+ created_at: z.number(), // unix seconds, float OK
16
+ terminal: z.boolean().default(false),
17
+ });
18
+ // ──────────────────────────────────────────────────────────────────────
19
+ // Event family payloads
20
+ // ──────────────────────────────────────────────────────────────────────
21
+ // Lifecycle controls available to the browser at this moment. Snapshot,
22
+ // not delta — every status event carries the current set.
23
+ export const ControlSetSchema = z.object({
24
+ cancel: z.boolean(),
25
+ approval_pending: z.boolean(),
26
+ clarify_pending: z.boolean(),
27
+ queue: z.boolean(),
28
+ goal: z.boolean(),
29
+ });
30
+ export const RunStartedPayload = z.object({
31
+ status: z.literal("running"),
32
+ controls: ControlSetSchema,
33
+ workspace: z.string().optional(),
34
+ profile: z.string().optional(),
35
+ model: z.string().optional(),
36
+ toolset: z.array(z.string()).optional(),
37
+ });
38
+ export const StatusPayload = z.object({
39
+ status: z.enum([
40
+ "queued", "running", "awaiting_approval", "awaiting_clarify",
41
+ "paused", "cancelling", "cancelled", "failed", "completed", "expired",
42
+ ]),
43
+ controls: ControlSetSchema,
44
+ detail: z.string().optional(),
45
+ });
46
+ export const TokenDeltaPayload = z.object({
47
+ message_id: z.string(),
48
+ segment_id: z.string().optional(),
49
+ delta: z.string(),
50
+ content_type: z.enum(["text", "code", "thinking"]).default("text"),
51
+ });
52
+ export const ReasoningDeltaPayload = z.object({
53
+ block_id: z.string(),
54
+ delta: z.string(),
55
+ visibility: z.enum(["visible", "collapsed", "hidden"]).default("visible"),
56
+ });
57
+ export const ReasoningDonePayload = z.object({
58
+ block_id: z.string(),
59
+ final: z.string(),
60
+ });
61
+ export const ProgressPayload = z.object({
62
+ phase: z.string(),
63
+ detail: z.string().optional(),
64
+ tool_id: z.string().optional(),
65
+ });
66
+ export const ToolStartedPayload = z.object({
67
+ tool_call_id: z.string(),
68
+ name: z.string(),
69
+ args: z.unknown(), // sanitized; PII redactor runs upstream
70
+ started_at: z.number(),
71
+ });
72
+ export const ToolUpdatedPayload = z.object({
73
+ tool_call_id: z.string(),
74
+ stdout: z.string().optional(),
75
+ stderr: z.string().optional(),
76
+ partial: z.unknown().optional(),
77
+ progress_pct: z.number().min(0).max(100).optional(),
78
+ });
79
+ export const ToolDonePayload = z.object({
80
+ tool_call_id: z.string(),
81
+ result: z.unknown(),
82
+ exit_code: z.number().int().optional(),
83
+ duration_ms: z.number().nonnegative(),
84
+ error: z.boolean().default(false),
85
+ });
86
+ export const ApprovalRequestedPayload = z.object({
87
+ approval_id: z.string(),
88
+ action_summary: z.string(),
89
+ risk: z.enum(["low", "medium", "high"]).default("medium"),
90
+ choices: z.array(z.string()).min(1),
91
+ });
92
+ export const ApprovalResolvedPayload = z.object({
93
+ approval_id: z.string(),
94
+ choice: z.string(),
95
+ status: z.enum(["accepted", "rejected", "timed_out"]),
96
+ });
97
+ export const ClarifyRequestedPayload = z.object({
98
+ clarify_id: z.string(),
99
+ question: z.string(),
100
+ choices: z.array(z.string()).optional(),
101
+ input_mode: z.enum(["choice", "free_text"]).default("free_text"),
102
+ });
103
+ export const ClarifyResolvedPayload = z.object({
104
+ clarify_id: z.string(),
105
+ answer: z.string(),
106
+ status: z.enum(["answered", "timed_out", "abandoned"]),
107
+ });
108
+ export const TitleUpdatedPayload = z.object({
109
+ title: z.string(),
110
+ source: z.enum(["model", "user", "system"]).default("model"),
111
+ confidence: z.number().min(0).max(1).optional(),
112
+ });
113
+ export const UsageUpdatedPayload = z.object({
114
+ tokens_in: z.number().int().nonnegative(),
115
+ tokens_out: z.number().int().nonnegative(),
116
+ cost_usd: z.number().nonnegative().optional(),
117
+ model: z.string().optional(),
118
+ provider: z.string().optional(),
119
+ });
120
+ export const UsageFinalPayload = UsageUpdatedPayload.extend({
121
+ duration_ms: z.number().nonnegative(),
122
+ });
123
+ export const ErrorPayload = z.object({
124
+ code: z.string(), // stable, e.g. "agent_timeout"
125
+ message: z.string(), // safe-for-display
126
+ detail: z.string().optional(), // redacted diagnostics
127
+ terminal: z.boolean(),
128
+ });
129
+ export const DonePayload = z.object({
130
+ status: z.enum(["completed", "failed", "cancelled", "expired"]),
131
+ last_seq: z.number().int().nonnegative(),
132
+ usage: UsageFinalPayload.optional(),
133
+ result_summary: z.string().optional(),
134
+ });
135
+ // ──────────────────────────────────────────────────────────────────────
136
+ // Discriminated union of all 13 event families
137
+ // ──────────────────────────────────────────────────────────────────────
138
+ const make = (type, payload) => EventEnvelopeBase.extend({
139
+ type: z.literal(type),
140
+ payload,
141
+ });
142
+ export const HermesEventSchema = z.discriminatedUnion("type", [
143
+ make("run.started", RunStartedPayload),
144
+ make("status", StatusPayload),
145
+ make("token.delta", TokenDeltaPayload),
146
+ make("reasoning.delta", ReasoningDeltaPayload),
147
+ make("reasoning.done", ReasoningDonePayload),
148
+ make("progress", ProgressPayload),
149
+ make("tool.started", ToolStartedPayload),
150
+ make("tool.updated", ToolUpdatedPayload),
151
+ make("tool.done", ToolDonePayload),
152
+ make("approval.requested", ApprovalRequestedPayload),
153
+ make("approval.resolved", ApprovalResolvedPayload),
154
+ make("clarify.requested", ClarifyRequestedPayload),
155
+ make("clarify.resolved", ClarifyResolvedPayload),
156
+ make("title.updated", TitleUpdatedPayload),
157
+ make("usage.updated", UsageUpdatedPayload),
158
+ make("usage.final", UsageFinalPayload),
159
+ make("error", ErrorPayload),
160
+ make("done", DonePayload),
161
+ ]);
162
+ // ──────────────────────────────────────────────────────────────────────
163
+ // 7 control types (browser → runtime requests)
164
+ // ──────────────────────────────────────────────────────────────────────
165
+ export const ObserveControl = z.object({
166
+ type: z.literal("observe"),
167
+ run_id: z.string(),
168
+ after_seq: z.number().int().nonnegative().optional(),
169
+ last_event_id: z.string().optional(),
170
+ });
171
+ export const StatusControl = z.object({
172
+ type: z.literal("status"),
173
+ run_id: z.string(),
174
+ });
175
+ export const CancelControl = z.object({
176
+ type: z.literal("cancel"),
177
+ run_id: z.string(),
178
+ reason: z.string().optional(),
179
+ });
180
+ export const QueueControl = z.object({
181
+ type: z.literal("queue"),
182
+ run_id: z.string(),
183
+ prompt: z.string(),
184
+ });
185
+ export const ApprovalControl = z.object({
186
+ type: z.literal("approval"),
187
+ approval_id: z.string(),
188
+ choice: z.string(),
189
+ });
190
+ export const ClarifyControl = z.object({
191
+ type: z.literal("clarify"),
192
+ clarify_id: z.string(),
193
+ answer: z.string(),
194
+ });
195
+ export const GoalControl = z.object({
196
+ type: z.literal("goal"),
197
+ run_id: z.string(),
198
+ op: z.enum(["set", "status", "pause", "resume", "clear"]),
199
+ goal: z.string().optional(),
200
+ });
201
+ export const HermesControlSchema = z.discriminatedUnion("type", [
202
+ ObserveControl, StatusControl, CancelControl, QueueControl,
203
+ ApprovalControl, ClarifyControl, GoalControl,
204
+ ]);
205
+ //# sourceMappingURL=events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,64 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { HermesEventSchema, HermesControlSchema } from "./events.js";
3
+ describe("HermesEventSchema", () => {
4
+ it("parses a run.started event", () => {
5
+ const ev = HermesEventSchema.parse({
6
+ event_id: "run_1:0",
7
+ seq: 0,
8
+ run_id: "run_1",
9
+ session_id: "sess_1",
10
+ created_at: 1778750000,
11
+ terminal: false,
12
+ type: "run.started",
13
+ payload: {
14
+ status: "running",
15
+ controls: { cancel: true, approval_pending: false, clarify_pending: false, queue: true, goal: false },
16
+ },
17
+ });
18
+ expect(ev.type).toBe("run.started");
19
+ });
20
+ it("parses a tool.done terminal-flagged event", () => {
21
+ const ev = HermesEventSchema.parse({
22
+ event_id: "run_1:7",
23
+ seq: 7,
24
+ run_id: "run_1",
25
+ session_id: "sess_1",
26
+ created_at: 1778750100,
27
+ terminal: false,
28
+ type: "tool.done",
29
+ payload: {
30
+ tool_call_id: "tc_1",
31
+ result: { stdout: "ok" },
32
+ duration_ms: 42,
33
+ error: false,
34
+ },
35
+ });
36
+ expect(ev.type).toBe("tool.done");
37
+ });
38
+ it("rejects an event with an unknown type", () => {
39
+ expect(() => HermesEventSchema.parse({
40
+ event_id: "x", seq: 0, run_id: "r", session_id: "s",
41
+ created_at: 1, terminal: false, type: "tool.bogus", payload: {},
42
+ })).toThrow();
43
+ });
44
+ it("rejects a token.delta missing required fields", () => {
45
+ expect(() => HermesEventSchema.parse({
46
+ event_id: "x", seq: 0, run_id: "r", session_id: "s",
47
+ created_at: 1, terminal: false, type: "token.delta", payload: {},
48
+ })).toThrow();
49
+ });
50
+ });
51
+ describe("HermesControlSchema", () => {
52
+ it("parses an observe control with after_seq", () => {
53
+ const c = HermesControlSchema.parse({ type: "observe", run_id: "r1", after_seq: 5 });
54
+ expect(c.type).toBe("observe");
55
+ });
56
+ it("parses a cancel control", () => {
57
+ const c = HermesControlSchema.parse({ type: "cancel", run_id: "r1", reason: "user_clicked_stop" });
58
+ expect(c.type).toBe("cancel");
59
+ });
60
+ it("rejects unknown control types", () => {
61
+ expect(() => HermesControlSchema.parse({ type: "nope", run_id: "r1" })).toThrow();
62
+ });
63
+ });
64
+ //# sourceMappingURL=events.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.test.js","sourceRoot":"","sources":["../src/events.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ export declare const MAX_FRAME_BYTES: number;
2
+ export declare class FrameTooLarge extends Error {
3
+ readonly size: number;
4
+ constructor(size: number);
5
+ }
6
+ export declare class FrameTruncated extends Error {
7
+ constructor();
8
+ }
9
+ export declare function encodeFrame(payload: unknown): Buffer;
10
+ /**
11
+ * Stateful frame decoder. Feed it chunks from a stream as they arrive;
12
+ * yields complete frames as they assemble. Buffers partial frames internally.
13
+ */
14
+ export declare class FrameDecoder {
15
+ private buf;
16
+ push(chunk: Buffer): unknown[];
17
+ /** Call when the stream ends. Throws if a partial frame is dangling. */
18
+ end(): void;
19
+ }
@@ -0,0 +1,57 @@
1
+ // Length-prefixed JSON framing for the Python bridge ↔ TS daemon Unix socket.
2
+ //
3
+ // Wire format per message: [4-byte big-endian uint32 length][JSON payload bytes].
4
+ //
5
+ // This is the same shape used by LSP and MCP. Avoids the NDJSON pitfall where
6
+ // a `\n` inside a string field (e.g. token.delta) corrupts the stream.
7
+ export const MAX_FRAME_BYTES = 16 * 1024 * 1024; // 16 MiB hard cap
8
+ export class FrameTooLarge extends Error {
9
+ size;
10
+ constructor(size) {
11
+ super(`Frame size ${size} bytes exceeds MAX_FRAME_BYTES (${MAX_FRAME_BYTES})`);
12
+ this.name = "FrameTooLarge";
13
+ this.size = size;
14
+ }
15
+ }
16
+ export class FrameTruncated extends Error {
17
+ constructor() {
18
+ super("Socket closed mid-frame");
19
+ this.name = "FrameTruncated";
20
+ }
21
+ }
22
+ export function encodeFrame(payload) {
23
+ const body = Buffer.from(JSON.stringify(payload), "utf8");
24
+ if (body.byteLength > MAX_FRAME_BYTES)
25
+ throw new FrameTooLarge(body.byteLength);
26
+ const header = Buffer.alloc(4);
27
+ header.writeUInt32BE(body.byteLength, 0);
28
+ return Buffer.concat([header, body]);
29
+ }
30
+ /**
31
+ * Stateful frame decoder. Feed it chunks from a stream as they arrive;
32
+ * yields complete frames as they assemble. Buffers partial frames internally.
33
+ */
34
+ export class FrameDecoder {
35
+ buf = Buffer.alloc(0);
36
+ push(chunk) {
37
+ this.buf = this.buf.length === 0 ? chunk : Buffer.concat([this.buf, chunk]);
38
+ const out = [];
39
+ while (this.buf.length >= 4) {
40
+ const size = this.buf.readUInt32BE(0);
41
+ if (size > MAX_FRAME_BYTES)
42
+ throw new FrameTooLarge(size);
43
+ if (this.buf.length < 4 + size)
44
+ break;
45
+ const body = this.buf.subarray(4, 4 + size);
46
+ this.buf = this.buf.subarray(4 + size);
47
+ out.push(JSON.parse(body.toString("utf8")));
48
+ }
49
+ return out;
50
+ }
51
+ /** Call when the stream ends. Throws if a partial frame is dangling. */
52
+ end() {
53
+ if (this.buf.length > 0)
54
+ throw new FrameTruncated();
55
+ }
56
+ }
57
+ //# sourceMappingURL=framing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"framing.js","sourceRoot":"","sources":["../src/framing.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,EAAE;AACF,kFAAkF;AAClF,EAAE;AACF,8EAA8E;AAC9E,uEAAuE;AAEvE,MAAM,CAAC,MAAM,eAAe,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,kBAAkB;AAEnE,MAAM,OAAO,aAAc,SAAQ,KAAK;IAC7B,IAAI,CAAS;IACtB,YAAY,IAAY;QACtB,KAAK,CAAC,cAAc,IAAI,mCAAmC,eAAe,GAAG,CAAC,CAAC;QAC/E,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;QAC5B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AAED,MAAM,OAAO,cAAe,SAAQ,KAAK;IACvC;QACE,KAAK,CAAC,yBAAyB,CAAC,CAAC;QACjC,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;IAC/B,CAAC;CACF;AAED,MAAM,UAAU,WAAW,CAAC,OAAgB;IAC1C,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,CAAC;IAC1D,IAAI,IAAI,CAAC,UAAU,GAAG,eAAe;QAAE,MAAM,IAAI,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAChF,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC/B,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;IACzC,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;AACvC,CAAC;AAED;;;GAGG;AACH,MAAM,OAAO,YAAY;IACf,GAAG,GAAW,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAEtC,IAAI,CAAC,KAAa;QAChB,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC;QAC5E,MAAM,GAAG,GAAc,EAAE,CAAC;QAC1B,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;YAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YACtC,IAAI,IAAI,GAAG,eAAe;gBAAE,MAAM,IAAI,aAAa,CAAC,IAAI,CAAC,CAAC;YAC1D,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,GAAG,IAAI;gBAAE,MAAM;YACtC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;YAC5C,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;YACvC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAC9C,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IAED,wEAAwE;IACxE,GAAG;QACD,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC;YAAE,MAAM,IAAI,cAAc,EAAE,CAAC;IACtD,CAAC;CACF"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,44 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { encodeFrame, FrameDecoder, FrameTooLarge, FrameTruncated, MAX_FRAME_BYTES } from "./framing.js";
3
+ describe("framing", () => {
4
+ it("roundtrips a single frame", () => {
5
+ const payload = { id: 1, method: "version" };
6
+ const buf = encodeFrame(payload);
7
+ const dec = new FrameDecoder();
8
+ const out = dec.push(buf);
9
+ expect(out).toEqual([payload]);
10
+ });
11
+ it("roundtrips multiple frames in one chunk", () => {
12
+ const a = { id: 1, method: "version" };
13
+ const b = { id: 2, method: "health" };
14
+ const buf = Buffer.concat([encodeFrame(a), encodeFrame(b)]);
15
+ const dec = new FrameDecoder();
16
+ expect(dec.push(buf)).toEqual([a, b]);
17
+ });
18
+ it("buffers partial frames across pushes", () => {
19
+ const a = { hello: "world" };
20
+ const full = encodeFrame(a);
21
+ const half = full.byteLength >>> 1;
22
+ const dec = new FrameDecoder();
23
+ expect(dec.push(full.subarray(0, half))).toEqual([]);
24
+ expect(dec.push(full.subarray(half))).toEqual([a]);
25
+ });
26
+ it("rejects an oversized declared frame", () => {
27
+ // Construct a header claiming MAX_FRAME_BYTES + 1
28
+ const header = Buffer.alloc(4);
29
+ header.writeUInt32BE(MAX_FRAME_BYTES + 1, 0);
30
+ const dec = new FrameDecoder();
31
+ expect(() => dec.push(header)).toThrow(FrameTooLarge);
32
+ });
33
+ it("end() throws if a partial frame is dangling", () => {
34
+ const dec = new FrameDecoder();
35
+ dec.push(Buffer.from([0, 0, 0, 5, 0x68, 0x69])); // declares 5, only got 2
36
+ expect(() => dec.end()).toThrow(FrameTruncated);
37
+ });
38
+ it("survives JSON strings containing newlines (NDJSON pitfall)", () => {
39
+ const payload = { delta: "line one\nline two\n\nline four" };
40
+ const dec = new FrameDecoder();
41
+ expect(dec.push(encodeFrame(payload))).toEqual([payload]);
42
+ });
43
+ });
44
+ //# sourceMappingURL=framing.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"framing.test.js","sourceRoot":"","sources":["../src/framing.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export * from "./events.js";
2
+ export * from "./status-fsm.js";
3
+ export * from "./framing.js";
4
+ export declare const HERMES_ADAPTER_VERSION = "0.0.0";
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ // @osstack/hermes-adapter — Hermes Run Adapter Contract implementation.
2
+ // S1.1: schemas + status FSM + framing. S1.4 wires these into a real adapter.
3
+ export * from "./events.js";
4
+ export * from "./status-fsm.js";
5
+ export * from "./framing.js";
6
+ export const HERMES_ADAPTER_VERSION = "0.0.0";
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,8EAA8E;AAE9E,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAE7B,MAAM,CAAC,MAAM,sBAAsB,GAAG,OAAO,CAAC"}
@@ -0,0 +1,11 @@
1
+ export declare const RUN_STATUSES: readonly ["queued", "running", "awaiting_approval", "awaiting_clarify", "paused", "cancelling", "cancelled", "failed", "completed", "expired"];
2
+ export type RunStatus = (typeof RUN_STATUSES)[number];
3
+ export declare const TERMINAL_STATUSES: Set<"running" | "queued" | "awaiting_approval" | "awaiting_clarify" | "paused" | "cancelling" | "cancelled" | "failed" | "completed" | "expired">;
4
+ export declare function isLegalTransition(from: RunStatus, to: RunStatus): boolean;
5
+ export declare function isTerminal(status: RunStatus): boolean;
6
+ export declare class IllegalStatusTransition extends Error {
7
+ readonly from: RunStatus;
8
+ readonly to: RunStatus;
9
+ constructor(from: RunStatus, to: RunStatus);
10
+ }
11
+ export declare function assertTransition(from: RunStatus, to: RunStatus): void;
@@ -0,0 +1,75 @@
1
+ // Status FSM — 11 states from the Hermes Run Adapter Contract.
2
+ // The runtime is the source of truth; this module validates that any
3
+ // emitted status transition is legal. Illegal transitions are bugs.
4
+ export const RUN_STATUSES = [
5
+ "queued",
6
+ "running",
7
+ "awaiting_approval",
8
+ "awaiting_clarify",
9
+ "paused",
10
+ "cancelling",
11
+ "cancelled",
12
+ "failed",
13
+ "completed",
14
+ "expired",
15
+ ];
16
+ export const TERMINAL_STATUSES = new Set([
17
+ "cancelled",
18
+ "failed",
19
+ "completed",
20
+ "expired",
21
+ ]);
22
+ // ──────────────────────────────────────────────────────────────────────
23
+ // Legal transitions
24
+ //
25
+ // queued ──▶ running ──▶ awaiting_approval ──▶ running
26
+ // │ ▲ │
27
+ // │ └──── paused ──┐ │
28
+ // │ │ │
29
+ // │ ▼ ▼
30
+ // └──────────▶ awaiting_clarify ──▶ running
31
+ // │ │
32
+ // │ ▼
33
+ // │ ┌── completed
34
+ // │ ├── failed
35
+ // ├──▶ cancelling ──────────────┴── cancelled
36
+ // │
37
+ // └──▶ expired (timeout from any non-terminal)
38
+ // ──────────────────────────────────────────────────────────────────────
39
+ const TRANSITIONS = {
40
+ queued: new Set(["running", "cancelling", "expired"]),
41
+ running: new Set([
42
+ "awaiting_approval", "awaiting_clarify", "paused",
43
+ "cancelling", "completed", "failed", "expired",
44
+ ]),
45
+ awaiting_approval: new Set(["running", "cancelling", "expired"]),
46
+ awaiting_clarify: new Set(["running", "cancelling", "expired"]),
47
+ paused: new Set(["running", "cancelling", "expired"]),
48
+ cancelling: new Set(["cancelled", "failed"]),
49
+ // Terminal states: no outgoing transitions
50
+ cancelled: new Set(),
51
+ failed: new Set(),
52
+ completed: new Set(),
53
+ expired: new Set(),
54
+ };
55
+ export function isLegalTransition(from, to) {
56
+ return TRANSITIONS[from].has(to);
57
+ }
58
+ export function isTerminal(status) {
59
+ return TERMINAL_STATUSES.has(status);
60
+ }
61
+ export class IllegalStatusTransition extends Error {
62
+ from;
63
+ to;
64
+ constructor(from, to) {
65
+ super(`Illegal status transition: ${from} -> ${to}`);
66
+ this.name = "IllegalStatusTransition";
67
+ this.from = from;
68
+ this.to = to;
69
+ }
70
+ }
71
+ export function assertTransition(from, to) {
72
+ if (!isLegalTransition(from, to))
73
+ throw new IllegalStatusTransition(from, to);
74
+ }
75
+ //# sourceMappingURL=status-fsm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status-fsm.js","sourceRoot":"","sources":["../src/status-fsm.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,qEAAqE;AACrE,oEAAoE;AAEpE,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,QAAQ;IACR,SAAS;IACT,mBAAmB;IACnB,kBAAkB;IAClB,QAAQ;IACR,YAAY;IACZ,WAAW;IACX,QAAQ;IACR,WAAW;IACX,SAAS;CACD,CAAC;AAIX,MAAM,CAAC,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAY;IAClD,WAAW;IACX,QAAQ;IACR,WAAW;IACX,SAAS;CACV,CAAC,CAAC;AAEH,yEAAyE;AACzE,oBAAoB;AACpB,EAAE;AACF,8DAA8D;AAC9D,6DAA6D;AAC7D,6DAA6D;AAC7D,6DAA6D;AAC7D,6DAA6D;AAC7D,8DAA8D;AAC9D,6DAA6D;AAC7D,6DAA6D;AAC7D,+DAA+D;AAC/D,4DAA4D;AAC5D,+DAA+D;AAC/D,oBAAoB;AACpB,kEAAkE;AAClE,yEAAyE;AAEzE,MAAM,WAAW,GAA8C;IAC7D,MAAM,EAAE,IAAI,GAAG,CAAC,CAAC,SAAS,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC;IACrD,OAAO,EAAE,IAAI,GAAG,CAAC;QACf,mBAAmB,EAAE,kBAAkB,EAAE,QAAQ;QACjD,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,SAAS;KAC/C,CAAC;IACF,iBAAiB,EAAE,IAAI,GAAG,CAAC,CAAC,SAAS,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC;IAChE,gBAAgB,EAAE,IAAI,GAAG,CAAC,CAAC,SAAS,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC;IAC/D,MAAM,EAAE,IAAI,GAAG,CAAC,CAAC,SAAS,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC;IACrD,UAAU,EAAE,IAAI,GAAG,CAAC,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;IAC5C,2CAA2C;IAC3C,SAAS,EAAE,IAAI,GAAG,EAAE;IACpB,MAAM,EAAE,IAAI,GAAG,EAAE;IACjB,SAAS,EAAE,IAAI,GAAG,EAAE;IACpB,OAAO,EAAE,IAAI,GAAG,EAAE;CACnB,CAAC;AAEF,MAAM,UAAU,iBAAiB,CAAC,IAAe,EAAE,EAAa;IAC9D,OAAO,WAAW,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAiB;IAC1C,OAAO,iBAAiB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;AACvC,CAAC;AAED,MAAM,OAAO,uBAAwB,SAAQ,KAAK;IACvC,IAAI,CAAY;IAChB,EAAE,CAAY;IACvB,YAAY,IAAe,EAAE,EAAa;QACxC,KAAK,CAAC,8BAA8B,IAAI,OAAO,EAAE,EAAE,CAAC,CAAC;QACrD,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC;QACtC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;IACf,CAAC;CACF;AAED,MAAM,UAAU,gBAAgB,CAAC,IAAe,EAAE,EAAa;IAC7D,IAAI,CAAC,iBAAiB,CAAC,IAAI,EAAE,EAAE,CAAC;QAAE,MAAM,IAAI,uBAAuB,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;AAChF,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,41 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { RUN_STATUSES, TERMINAL_STATUSES, assertTransition, isLegalTransition, isTerminal, IllegalStatusTransition, } from "./status-fsm.js";
3
+ describe("status FSM", () => {
4
+ it("has the 11 statuses from the Hermes Run Adapter Contract", () => {
5
+ expect(RUN_STATUSES.length).toBe(10); // queued..expired listed above; verify list size
6
+ // Actually we have 10 named: queued, running, awaiting_approval, awaiting_clarify,
7
+ // paused, cancelling, cancelled, failed, completed, expired.
8
+ // RFC table also enumerates these 10; the "11" in plan v0.6 counts running pre-state separately.
9
+ // Lock the canonical count at 10 here so future RFC churn surfaces a failing test.
10
+ expect(new Set(RUN_STATUSES).size).toBe(RUN_STATUSES.length);
11
+ });
12
+ it("flags every terminal status correctly", () => {
13
+ const expected = new Set(["cancelled", "failed", "completed", "expired"]);
14
+ expect(TERMINAL_STATUSES).toEqual(expected);
15
+ for (const s of RUN_STATUSES)
16
+ expect(isTerminal(s)).toBe(expected.has(s));
17
+ });
18
+ it("allows queued -> running but not queued -> completed", () => {
19
+ expect(isLegalTransition("queued", "running")).toBe(true);
20
+ expect(isLegalTransition("queued", "completed")).toBe(false);
21
+ });
22
+ it("allows running -> awaiting_approval -> running", () => {
23
+ expect(isLegalTransition("running", "awaiting_approval")).toBe(true);
24
+ expect(isLegalTransition("awaiting_approval", "running")).toBe(true);
25
+ });
26
+ it("requires cancelling before cancelled", () => {
27
+ expect(isLegalTransition("running", "cancelled")).toBe(false);
28
+ expect(isLegalTransition("cancelling", "cancelled")).toBe(true);
29
+ });
30
+ it("rejects any transition out of a terminal state", () => {
31
+ for (const t of TERMINAL_STATUSES) {
32
+ for (const s of RUN_STATUSES) {
33
+ expect(isLegalTransition(t, s)).toBe(false);
34
+ }
35
+ }
36
+ });
37
+ it("assertTransition throws IllegalStatusTransition on illegal moves", () => {
38
+ expect(() => assertTransition("completed", "running")).toThrow(IllegalStatusTransition);
39
+ });
40
+ });
41
+ //# sourceMappingURL=status-fsm.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status-fsm.test.js","sourceRoot":"","sources":["../src/status-fsm.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EACjB,UAAU,EACV,uBAAuB,GAExB,MAAM,iBAAiB,CAAC;AAEzB,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;IAC1B,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;QAClE,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,iDAAiD;QACvF,mFAAmF;QACnF,6DAA6D;QAC7D,iGAAiG;QACjG,mFAAmF;QACnF,MAAM,CAAC,IAAI,GAAG,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAY,CAAC,WAAW,EAAE,QAAQ,EAAE,WAAW,EAAE,SAAS,CAAC,CAAC,CAAC;QACrF,MAAM,CAAC,iBAAiB,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC5C,KAAK,MAAM,CAAC,IAAI,YAAY;YAAE,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAC9D,MAAM,CAAC,iBAAiB,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1D,MAAM,CAAC,iBAAiB,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACxD,MAAM,CAAC,iBAAiB,CAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrE,MAAM,CAAC,iBAAiB,CAAC,mBAAmB,EAAE,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACvE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC9C,MAAM,CAAC,iBAAiB,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC9D,MAAM,CAAC,iBAAiB,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACxD,KAAK,MAAM,CAAC,IAAI,iBAAiB,EAAE,CAAC;YAClC,KAAK,MAAM,CAAC,IAAI,YAAY,EAAE,CAAC;gBAC7B,MAAM,CAAC,iBAAiB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC9C,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kEAAkE,EAAE,GAAG,EAAE;QAC1E,MAAM,CAAC,GAAG,EAAE,CAAC,gBAAgB,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,uBAAuB,CAAC,CAAC;IAC1F,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@osstack/hermes-adapter",
3
+ "version": "0.2.0",
4
+ "description": "Hermes Run Adapter Contract implementation — 13 event families, 7 control types, 11 status FSM, Last-Event-ID replay.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "dependencies": {
13
+ "zod": "^3.25.76",
14
+ "@osstack/core": "0.2.0"
15
+ },
16
+ "devDependencies": {
17
+ "@types/node": "^25.8.0"
18
+ },
19
+ "publishConfig": {
20
+ "access": "public"
21
+ },
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "https://github.com/gilosbornhello/osstack.git",
25
+ "directory": "packages/hermes-adapter"
26
+ },
27
+ "engines": {
28
+ "node": ">=20"
29
+ },
30
+ "scripts": {
31
+ "build": "tsc -p .",
32
+ "test": "vitest run",
33
+ "typecheck": "tsc --noEmit"
34
+ }
35
+ }