@free-intelligence/core 1.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,21 @@
1
+ /**
2
+ * @free-intelligence/core — conversation library contract (DD-002B1).
3
+ *
4
+ * Local-first transcript persistence primitives: the record/summary types, the
5
+ * storage contract (ConversationLibrary), and pure helpers (title/preview
6
+ * derivation + privacy-by-structure sanitization). Adapters (IndexedDB, backend)
7
+ * implement ConversationLibrary in later layers — they live in fi-glass and the
8
+ * apps, never here.
9
+ */
10
+
11
+ export type { ConversationRecord, ConversationSummary } from './record';
12
+ export type { ConversationLibrary } from './library';
13
+ export {
14
+ CONVERSATION_SCHEMA_VERSION,
15
+ createConversationRecord,
16
+ summarizeConversation,
17
+ deriveConversationTitle,
18
+ deriveConversationPreview,
19
+ sanitizeConversationMessage,
20
+ type CreateConversationRecordArgs,
21
+ } from './helpers';
@@ -0,0 +1,25 @@
1
+ /**
2
+ * ConversationLibrary — the storage contract for local-first conversations.
3
+ *
4
+ * A pure async interface: adapters implement it over IndexedDB (fi-glass), a
5
+ * backend, or filesystem (later layers) without core taking a dependency on any
6
+ * of them. `list` returns light summaries (cheap); `get` hydrates one full
7
+ * record; `put` upserts; `delete`/`clear` remove. Keeping the contract in core
8
+ * is what stops a reusable persistence primitive from being trapped in a
9
+ * consumer app (DD-002-LESSON / framework-first-canary).
10
+ */
11
+
12
+ import type { ConversationRecord, ConversationSummary } from './record';
13
+
14
+ export interface ConversationLibrary {
15
+ /** All conversations as light summaries, newest `updatedAt` first. */
16
+ list(): Promise<ConversationSummary[]>;
17
+ /** The full record for `id`, or `null` if none. */
18
+ get(id: string): Promise<ConversationRecord | null>;
19
+ /** Insert or replace a record by its `id`. */
20
+ put(record: ConversationRecord): Promise<void>;
21
+ /** Remove the record for `id` (no-op if absent). */
22
+ delete(id: string): Promise<void>;
23
+ /** Remove every stored conversation. */
24
+ clear(): Promise<void>;
25
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * ConversationRecord — the persisted shape of one local-first conversation.
3
+ *
4
+ * DD-002B1: og118 (and every future fi-glass shell) needs the transcript to
5
+ * survive a refresh and a list of past chats for a sidebar. The record is the
6
+ * unit a ConversationLibrary stores; the summary is the light row a sidebar
7
+ * lists without paying for the full message array. Pure data — no React, no
8
+ * browser, no transport. The `id` doubles as the backend session_id so the
9
+ * local transcript and the server's conversation store key the same thread.
10
+ */
11
+
12
+ import type { ChatMessage } from '../chat/message';
13
+
14
+ export interface ConversationRecord {
15
+ /** Stable id. Doubles as the backend session_id for the same thread. */
16
+ id: string;
17
+ /** Human-readable title, derived from the first user message. */
18
+ title: string;
19
+ /** ISO 8601 creation timestamp. */
20
+ createdAt: string;
21
+ /** ISO 8601 timestamp of the last change. */
22
+ updatedAt: string;
23
+ /** The thread, sanitized for storage (role/content/timestamp only). */
24
+ messages: ChatMessage[];
25
+ /** Snippet of the last non-empty message, for the sidebar. */
26
+ preview: string;
27
+ /** Schema version of this record, for forward migrations. */
28
+ schemaVersion: number;
29
+ }
30
+
31
+ /** A light row for listing conversations in a sidebar (no messages). */
32
+ export interface ConversationSummary {
33
+ id: string;
34
+ title: string;
35
+ createdAt: string;
36
+ updatedAt: string;
37
+ preview: string;
38
+ }
package/src/index.ts ADDED
@@ -0,0 +1,54 @@
1
+ // @free-intelligence/core — public surface.
2
+ // Phase 1 ships the theme-token contract. Berkelio (97) adds the agent event
3
+ // contract (Plan/Steps/Sources/Guard) below, material-agnostic; the fi-runner
4
+ // stream client lands in a later phase.
5
+ export type { ThemeTokens } from './theme/contract';
6
+ export type {
7
+ VoiceAdapter,
8
+ VoiceOption,
9
+ AudioSource,
10
+ TranscriptResult,
11
+ TranscribeContext,
12
+ } from './voice/adapter';
13
+ export type { ChatMessage, ChatStreamingState } from './chat/message';
14
+ export type { ChatHook } from './chat/hook';
15
+
16
+ // Agent-turn contract (Berkelio). Types describe the wire stream + reduced
17
+ // state; the reducer + state factory are pure runtime values.
18
+ export type {
19
+ AgentStreamEvent,
20
+ StepStatus,
21
+ GuardLevel,
22
+ ToolCall,
23
+ GuardRejection,
24
+ AgentMeta,
25
+ } from './agent/events';
26
+ export type {
27
+ PlanStep,
28
+ AgentPlan,
29
+ PlanOutcome,
30
+ AgentTurnStatus,
31
+ AgentTurnState,
32
+ } from './agent/state';
33
+ export { initialAgentTurnState, applyAgentEvent } from './agent/state';
34
+ export { makeUserMessage, foldAssistantTurn } from './agent/transcript';
35
+ export type { AgentHook } from './agent/hook';
36
+
37
+ // Conversation library contract (DD-002B1). Local-first transcript persistence:
38
+ // record/summary types, the ConversationLibrary storage contract, and pure
39
+ // helpers (title/preview derivation + privacy-by-structure sanitization).
40
+ // Storage adapters (IndexedDB, backend) implement the contract in later layers.
41
+ export type {
42
+ ConversationRecord,
43
+ ConversationSummary,
44
+ ConversationLibrary,
45
+ CreateConversationRecordArgs,
46
+ } from './conversation';
47
+ export {
48
+ CONVERSATION_SCHEMA_VERSION,
49
+ createConversationRecord,
50
+ summarizeConversation,
51
+ deriveConversationTitle,
52
+ deriveConversationPreview,
53
+ sanitizeConversationMessage,
54
+ } from './conversation';
@@ -0,0 +1,30 @@
1
+ /**
2
+ * ThemeTokens — the material-agnostic slot contract for a frosted-surface theme.
3
+ *
4
+ * Every `fi-<material>` skin (fi-glass today; future fi-slate, fi-paper, …) fills
5
+ * these same slots with its own values. Consumers — fi-glass's shell/agentic
6
+ * components and the apps themselves — depend only on this shape, never on a
7
+ * concrete material. That is precisely what makes "material = token swap" hold
8
+ * without any material having to depend on another.
9
+ *
10
+ * This is pure data: no React, no styling, no logic. It lives in core so a future
11
+ * material never has to reach into fi-glass just to learn the slot shape.
12
+ */
13
+ export interface ThemeTokens {
14
+ /** Backdrop blur radius for frosted surfaces, e.g. `"12px"`. */
15
+ blur: string;
16
+ /** Reduced blur radius for small viewports (≤768px), e.g. `"8px"`. */
17
+ blurCompact: string;
18
+ /** Surface fill opacity, `0..1`. */
19
+ opacity: number;
20
+ /** Backdrop saturation boost, e.g. `"180%"`. */
21
+ saturation: string;
22
+ /** Light surface base fill as an `"r, g, b"` triple, e.g. `"255, 255, 255"`. */
23
+ surfaceLight: string;
24
+ /** Light surface border (full CSS color), e.g. `"rgba(255, 255, 255, 0.18)"`. */
25
+ borderLight: string;
26
+ /** Dark surface fill (full CSS color), e.g. `"rgba(15, 23, 42, 0.7)"`. */
27
+ surfaceDark: string;
28
+ /** Dark surface border (full CSS color), e.g. `"rgba(148, 163, 184, 0.2)"`. */
29
+ borderDark: string;
30
+ }
@@ -0,0 +1,71 @@
1
+ // @free-intelligence/core · voice engine contract
2
+ //
3
+ // Mirrors the StreamAdapter philosophy: the contract lives in core, the
4
+ // implementations live in the apps. fi-glass consumes ONLY this interface and
5
+ // stays backend-agnostic — it never knows about Azure, OpenAI, endpoints, auth,
6
+ // or session ids. An app implements VoiceAdapter once against its own backend
7
+ // and passes the instance to the voice UI.
8
+
9
+ /**
10
+ * Audio produced by synthesize().
11
+ *
12
+ * - `Blob` → a fully-rendered clip; fi-glass calls URL.createObjectURL.
13
+ * - `{ url }` → a ready (possibly streaming) URL; fi-glass uses it directly.
14
+ *
15
+ * Supporting both keeps streaming TTS (hear-while-generating) open without
16
+ * forcing every backend to stream.
17
+ */
18
+ export type AudioSource = Blob | { url: string };
19
+
20
+ /** A selectable TTS voice offered by the adapter. */
21
+ export interface VoiceOption {
22
+ /** Stable id passed back to synthesize() (e.g. "nova", "en-US-AvaNeural"). */
23
+ id: string;
24
+ /** Human label for the picker (e.g. "Nova"). */
25
+ label: string;
26
+ /** Optional group header for the picker (e.g. "OpenAI", "Azure Neural"). */
27
+ group?: string;
28
+ }
29
+
30
+ /** Result of transcribing one recorded audio chunk. */
31
+ export interface TranscriptResult {
32
+ /** Recognized text for this chunk (may be empty). */
33
+ text: string;
34
+ }
35
+
36
+ /** Metadata fi-glass passes alongside each transcription chunk. */
37
+ export interface TranscribeContext {
38
+ /**
39
+ * 0-based index of this chunk within the current recording. A streaming
40
+ * adapter uses it for session/ordering; a one-shot adapter (single chunk on
41
+ * stop) simply ignores it.
42
+ */
43
+ index: number;
44
+ /** Lets the app abort the in-flight request when recording stops. */
45
+ signal?: AbortSignal;
46
+ }
47
+
48
+ /**
49
+ * VoiceAdapter — the app's voice engine, injected whole into fi-glass.
50
+ *
51
+ * Capabilities are a la carte (all members optional):
52
+ * - no adapter -> no voice (UI hides mic + speak)
53
+ * - synthesize present -> TTS available (SpeakButton + AudioPlayer)
54
+ * - transcribe present -> STT available (VoiceMicButton dictation)
55
+ *
56
+ * Add a new capability by adding an optional member here — never by threading a
57
+ * new callback through the components.
58
+ */
59
+ export interface VoiceAdapter {
60
+ // ---- TTS (output) ----
61
+ /** Synthesize speech for `text` in an optional voice id; resolves to audio. */
62
+ synthesize?(text: string, voice?: string): Promise<AudioSource>;
63
+ /** Voices offered in the player's picker. */
64
+ readonly availableVoices?: VoiceOption[];
65
+ /** Voice id used when the caller doesn't specify one. */
66
+ readonly defaultVoice?: string;
67
+
68
+ // ---- STT (input) ----
69
+ /** Transcribe one recorded audio chunk to text. */
70
+ transcribe?(chunk: Blob, ctx?: TranscribeContext): Promise<TranscriptResult>;
71
+ }