Files
Oleg Maslov 0c3e2ead3b
Some checks failed
Installer Smoke / installer-smoke (push) Has been cancelled
moving
2026-09-02 10:10:29 +02:00

83 lines
3.4 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import { existsSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import { dirname, join } from 'node:path';
import {
HERMES_EVENT_NAME,
HERMES_SESSION_START_OBSERVE_EVENT,
hermesAdapter,
} from '../src/adapter.js';
const HERE = dirname(fileURLToPath(import.meta.url));
const SRC = join(HERE, '..', 'src');
describe('HERMES_EVENT_NAME (lifecycle → native event key)', () => {
it('maps the three live lifecycles to snake_case Hermes events; pre-compact is undefined', () => {
expect(HERMES_EVENT_NAME['session-start']).toBe('pre_llm_call');
expect(HERMES_EVENT_NAME['user-prompt-submit']).toBe('pre_llm_call');
expect(HERMES_EVENT_NAME['stop']).toBe('post_llm_call');
// CONFIRMED ABSENT in source — Hermes ships no compaction hook.
expect(HERMES_EVENT_NAME['pre-compact']).toBeUndefined();
});
it('exposes the observer-only SessionStart key registered alongside pre_llm_call', () => {
expect(HERMES_SESSION_START_OBSERVE_EVENT).toBe('on_session_start');
});
});
describe('hermesAdapter field extraction (reads from the `extra` envelope)', () => {
const payload = {
extra: {
cwd: '/work/dir',
session_id: 'sess-42',
user_message: 'the user prompt',
assistant_response: 'the assistant reply',
parent_frame_id: 'frame-99',
},
};
it('source is hermes', () => {
expect(hermesAdapter.source).toBe('hermes');
});
it('extractCwd / extractSessionId / extractPrompt read from extra', () => {
expect(hermesAdapter.extractCwd(payload)).toBe('/work/dir');
expect(hermesAdapter.extractSessionId(payload)).toBe('sess-42');
expect(hermesAdapter.extractPrompt(payload)).toBe('the user prompt');
});
it('extractResponse / extractParent read from extra', async () => {
expect(await hermesAdapter.extractResponse(payload, {})).toBe('the assistant reply');
expect(hermesAdapter.extractParent(payload)).toBe('frame-99');
});
it('falls back to top-level keys when the extra envelope is absent (forward-compat)', () => {
const flat = { cwd: '/c', session_id: 's', user_message: 'p', parent_frame_id: 'pp' };
expect(hermesAdapter.extractCwd(flat)).toBe('/c');
expect(hermesAdapter.extractSessionId(flat)).toBe('s');
expect(hermesAdapter.extractPrompt(flat)).toBe('p');
expect(hermesAdapter.extractParent(flat)).toBe('pp');
});
it('formatInject produces the { context } shape (appended to the user message)', () => {
expect(hermesAdapter.formatInject?.('recalled frames here')).toEqual({ context: 'recalled frames here' });
});
it('returns undefined for missing fields rather than throwing', () => {
expect(hermesAdapter.extractCwd({})).toBeUndefined();
expect(hermesAdapter.extractSessionId({})).toBeUndefined();
expect(hermesAdapter.extractPrompt({})).toBeUndefined();
expect(hermesAdapter.extractParent({})).toBeUndefined();
});
});
describe('NO pre-compact entrypoint exists (structural invariant)', () => {
it('ships exactly the three hook entrypoints — session-start / user-prompt-submit / stop', () => {
expect(existsSync(join(SRC, 'hooks', 'session-start.ts'))).toBe(true);
expect(existsSync(join(SRC, 'hooks', 'user-prompt-submit.ts'))).toBe(true);
expect(existsSync(join(SRC, 'hooks', 'stop.ts'))).toBe(true);
// There is genuinely nothing to hook for compaction — no entrypoint.
expect(existsSync(join(SRC, 'hooks', 'pre-compact.ts'))).toBe(false);
});
});