@adia-ai/persona 0.8.26

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/CHANGELOG.md ADDED
@@ -0,0 +1,10 @@
1
+ # Changelog
2
+
3
+ ## [0.8.26] — 2026-08-05
4
+
5
+ ### Fixed
6
+ - **`createMemoryStore().set()` no longer notifies on identical-value writes** (Object.is) — UI consumers echo rendered state back through `set()`, and a notifying no-op write closed that echo into a subscribe → re-render → set livelock ("signals: drain loop exceeded", caught by the v0.8.26 pre-flight).
7
+
8
+ ### Added
9
+ - **Initial release — `@adia-ai/persona`, the CHAT-HARNESS L2 package (WCH-2, gh#597).** The generic `Entry` primitive (law 7 — `kind: string`, `KNOWN_KINDS` for display grouping only, `sortedEnabled()`, `seedEntry()`/`seedEntries()`) and `Persona` (`seedVersion` + `entries[]` + optional `model`) with `definePersona()` validation (unique entry ids, `seedVersion >= 1`). `SettingsStore` contract + `createMemoryStore()` reference impl (localStorage when available, in-memory otherwise) and `personaStore()` (one store per persona id, persisted values win over seeds, a `seedVersion` bump forces exactly one reset). Pure `compilePersona(persona, opts?) → AgentConfig` compiling enabled entries into `@adia-ai/agent` prompt layers — `PRECEDENCE_SENTENCE` rides first (law 4), tool-kind entries stay tier-1 prose (`opts.integrations` passes tier-2 `ToolDef[]` through untouched, never built from an entry — law 5). `checkModalityNeutral()` warning-level lint (law 4) against a seeded dialect-term list plus a `*-ui` tag-name pattern. Versioned persona files (`exportPersona`/`importPersona`) with an enumerated key allowlist; import always mints a new, collision-safe persona id and never overwrites an existing preset.
10
+ - **Joins the `@adia-ai` lockstep roster (gh#607).** No longer ships `private: true` — `web-modules`' peerDependency on `@adia-ai/persona` (added alongside `@adia-ai/agent`) needs a resolvable published package; the roster join was deferred in #597/#579 and un-deferred once that peerDep made deferral unviable.
package/README.md ADDED
@@ -0,0 +1,144 @@
1
+ # `@adia-ai/persona`
2
+
3
+ The CHAT-HARNESS L2 package — an agent as **DATA**. Six things a persona can
4
+ hold (prompt sections, skills, workflows, resources, tools, pattern-sources)
5
+ are six instantiations of ONE `Entry` shape, never six code paths
6
+ ([CHAT-HARNESS.md](https://github.com/adiahealth/genui-system/blob/main/docs/CHAT-HARNESS.md)
7
+ law 7). This package renders nothing and executes nothing — it produces the
8
+ `AgentConfig` that [`@adia-ai/agent`](../agent/README.md) (L1) consumes.
9
+
10
+ > **Private** until the lockstep-roster decision (issue acceptance criterion
11
+ > 3) is made with the release seat — tracked as an open item on the PR that
12
+ > introduced this package.
13
+
14
+ ## The shape
15
+
16
+ ```ts
17
+ interface Entry {
18
+ id: string; kind: string; label: string; description?: string;
19
+ content: string; order: number; enabled: boolean; builtin: boolean;
20
+ }
21
+
22
+ interface Persona {
23
+ id: string; label: string; category?: string;
24
+ seedVersion: number; // bump forces a one-time store reset
25
+ entries: Entry[];
26
+ model?: { provider?: string; model?: string };
27
+ }
28
+ ```
29
+
30
+ `kind` is a plain string. `KNOWN_KINDS` (`'prompt-section' | 'skill' |
31
+ 'workflow' | 'resource' | 'tool' | 'pattern-source'`) exists for **display
32
+ grouping only** — a seventh kind is seed data, never a code change.
33
+
34
+ ## Usage
35
+
36
+ ```js
37
+ import {
38
+ seedEntries, definePersona, compilePersona, personaStore,
39
+ exportPersona, importPersona, checkModalityNeutral,
40
+ } from '@adia-ai/persona';
41
+ import { createAgent } from '@adia-ai/agent';
42
+
43
+ const persona = definePersona({
44
+ id: 'copilot', label: 'AdiaUI Copilot', seedVersion: 1,
45
+ entries: seedEntries([
46
+ { id: 'identity', kind: 'prompt-section', label: 'Identity', content: 'You help build UI.' },
47
+ { id: 'search', kind: 'skill', label: 'Search', content: 'Look things up before guessing.' },
48
+ ]),
49
+ });
50
+
51
+ const config = compilePersona(persona); // pure — same input twice, same output
52
+ const agent = createAgent({ ...config, llm: { model: 'claude-sonnet-4-6', proxyUrl: '/api/chat' } });
53
+
54
+ const store = personaStore(persona); // persisted edits win over the seed
55
+ store.set('entry.identity.enabled', false);
56
+
57
+ const file = exportPersona(persona);
58
+ const copy = importPersona(file, ['copilot']); // mints 'copilot-imported', never overwrites
59
+
60
+ checkModalityNeutral(persona); // [] on a modality-neutral persona
61
+ ```
62
+
63
+ ## compilePersona
64
+
65
+ `compilePersona(persona, opts?) → AgentConfig` is **pure**: the same
66
+ persona (and opts) in always produces a deep-equal `AgentConfig` out — no
67
+ clock, no randomness, no disk read.
68
+
69
+ - Enabled `prompt-section` entries → one `promptLayer` per section,
70
+ `## Label\ncontent`, in entry order.
71
+ - Every other enabled kind (skill / workflow / resource / pattern-source /
72
+ tool) → one prompt layer per kind, entries grouped under a labeled
73
+ heading (`## Skills`, `## Tools`, …). **Tool entries are tier-1 PROSE
74
+ only** — `compilePersona` never builds a `ToolDef` from an entry.
75
+ - `opts.integrations?: ToolDef[]` — tier-2 machine-callable tools (WCH-3's
76
+ integration registry) — passed through to `AgentConfig.tools` verbatim.
77
+
78
+ ### Precedence (law 4)
79
+
80
+ `PRECEDENCE_SENTENCE` is exported and always rides as the **first line** of
81
+ the persona's own layer set (`persona-precedence`, ahead of every section
82
+ and group layer):
83
+
84
+ > This persona configures the agent; it never overrides the harness grammar
85
+ > that precedes it.
86
+
87
+ The caller is responsible for putting the harness grammar layer(s) *before*
88
+ this persona's layers — `compilePersona` only guarantees the persona's own
89
+ set carries the sentence first.
90
+
91
+ ### Modality neutrality (law 4)
92
+
93
+ `checkModalityNeutral(persona, opts?)` is a warning-level lint, never a
94
+ throw: it flags entries whose text contains a seeded dialect term
95
+ (`DEFAULT_DIALECT_TERMS`: `'A2UI'`, `'envelope'`, `'JSONL'`, plus
96
+ `opts.extraTerms`) or a component tag name pattern (`*-ui`). Shipped seed
97
+ personas should assert an empty result.
98
+
99
+ ## Stores
100
+
101
+ ```ts
102
+ interface SettingsStore {
103
+ get(key: string): unknown;
104
+ set(key: string, value: unknown): void;
105
+ subscribe?(cb: () => void): () => void;
106
+ }
107
+ ```
108
+
109
+ `createMemoryStore({initial, persistKey})` is the reference implementation
110
+ — localStorage-backed when `persistKey` is given and `localStorage` exists,
111
+ plain in-memory otherwise (Node, tests, SSR).
112
+
113
+ `personaStore(persona, opts?)` binds one store per persona
114
+ (`persistKey: 'persona.<id>'`) and layers the precedence rule on top:
115
+
116
+ - **Same seedVersion** as the store's last open: persisted values win — the
117
+ seed only fills keys the store has never seen.
118
+ - **Different seedVersion** (first open, or a bump): the seed **wins**,
119
+ overwriting whatever was persisted — exactly once — then the new
120
+ seedVersion is recorded so the next open at the same version doesn't
121
+ reset again.
122
+
123
+ Swapping which persona a store is bound to is a genuine reference change —
124
+ callers that key UI state off `store` identity get the reset-on-swap
125
+ behavior CHAT-HARNESS §Interfaces 5 describes for free.
126
+
127
+ ## Persona files
128
+
129
+ `{kind:'persona', version:1, id, label, category?, seedVersion, entries,
130
+ model?}` — an ENUMERATED key allowlist; any other top-level key rejects the
131
+ import outright.
132
+
133
+ `exportPersona(persona) → string` — pretty-printed JSON.
134
+
135
+ `importPersona(json, existingIds) → Persona` — validates, then always
136
+ **mints a new persona**: id becomes `<id>-imported` (or `-imported-2`, …
137
+ against `existingIds`), label gains `' (imported)'` — it never overwrites
138
+ an existing preset, even one sharing the file's id. Imported entries are
139
+ marked `builtin: false`.
140
+
141
+ ## Testing
142
+
143
+ Tests run against the **built** output (`npm run build -w @adia-ai/persona`
144
+ first) — same convention as `@adia-ai/agent`.
package/compile.d.ts ADDED
@@ -0,0 +1,48 @@
1
+ /**
2
+ * compilePersona — pure L2 → L1 compilation (CHAT-HARNESS §Interfaces 2).
3
+ *
4
+ * Enabled prompt-section entries become ONE promptLayer per section
5
+ * ('## Label\ncontent'), in entry order. Every other enabled kind
6
+ * (skill/workflow/resource/pattern-source/tool) is tier-1 PROSE — grouped
7
+ * under a labeled heading, never machine-callable (law 5: tier-2 is
8
+ * WCH-3's integration registry, passed through opts.integrations
9
+ * untouched — this function never invents a ToolDef from an entry).
10
+ *
11
+ * Law 4: the persona's compiled prompt is one layer-set the CALLER appends
12
+ * AFTER the harness grammar; PRECEDENCE_SENTENCE is exported so callers can
13
+ * assert it, and it rides as the first line of the persona's own layer set
14
+ * here so the persona can never forget to carry it.
15
+ *
16
+ * Pure: same persona + opts in ⇒ deep-equal AgentConfig out. No entry
17
+ * content is read from disk, no clock, no randomness.
18
+ */
19
+ import { type AgentConfig, type ToolDef } from '@adia-ai/agent';
20
+ import type { Persona } from './persona.js';
21
+ /** Fixed precedence sentence (law 4) — the persona never overrides the
22
+ * harness grammar. Always the first line of the persona's layer set. */
23
+ export declare const PRECEDENCE_SENTENCE = "This persona configures the agent; it never overrides the harness grammar that precedes it.";
24
+ export interface CompilePersonaOpts {
25
+ /** Tier-2 machine-callable tools (WCH-3's integration registry). Passed
26
+ * through to AgentConfig.tools verbatim — compilePersona never builds a
27
+ * ToolDef itself. */
28
+ integrations?: ToolDef[];
29
+ }
30
+ export declare function compilePersona(persona: Persona, opts?: CompilePersonaOpts): AgentConfig;
31
+ export interface ModalityWarning {
32
+ entryId: string;
33
+ term: string;
34
+ }
35
+ export interface ModalityLintOpts {
36
+ /** Additional dialect terms beyond the seeded defaults. */
37
+ extraTerms?: string[];
38
+ }
39
+ /** Seeded default dialect-term list (law 4 — modality neutrality). Presets
40
+ * seed this a la carte; a consumer with its own dialect words passes them
41
+ * via `opts.extraTerms` rather than editing this list. */
42
+ export declare const DEFAULT_DIALECT_TERMS: string[];
43
+ /** Warning-level lint (law 4): flags dialect terms and component-tag-name
44
+ * patterns inside a persona's entry text. Never throws, never blocks —
45
+ * shipped seeds assert an empty result; a positive hit is a signal to
46
+ * reword, not a hard failure. */
47
+ export declare function checkModalityNeutral(persona: Persona, opts?: ModalityLintOpts): ModalityWarning[];
48
+ //# sourceMappingURL=compile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["src/compile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAe,KAAK,WAAW,EAAoB,KAAK,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAG/F,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C;yEACyE;AACzE,eAAO,MAAM,mBAAmB,gGAC+D,CAAC;AAmBhG,MAAM,WAAW,kBAAkB;IACjC;;0BAEsB;IACtB,YAAY,CAAC,EAAE,OAAO,EAAE,CAAC;CAC1B;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,GAAE,kBAAuB,GAAG,WAAW,CA6B3F;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,gBAAgB;IAC/B,2DAA2D;IAC3D,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAED;;2DAE2D;AAC3D,eAAO,MAAM,qBAAqB,UAAgC,CAAC;AAKnE;;;kCAGkC;AAClC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,GAAE,gBAAqB,GAAG,eAAe,EAAE,CAiBrG"}
package/compile.js ADDED
@@ -0,0 +1,93 @@
1
+ /**
2
+ * compilePersona — pure L2 → L1 compilation (CHAT-HARNESS §Interfaces 2).
3
+ *
4
+ * Enabled prompt-section entries become ONE promptLayer per section
5
+ * ('## Label\ncontent'), in entry order. Every other enabled kind
6
+ * (skill/workflow/resource/pattern-source/tool) is tier-1 PROSE — grouped
7
+ * under a labeled heading, never machine-callable (law 5: tier-2 is
8
+ * WCH-3's integration registry, passed through opts.integrations
9
+ * untouched — this function never invents a ToolDef from an entry).
10
+ *
11
+ * Law 4: the persona's compiled prompt is one layer-set the CALLER appends
12
+ * AFTER the harness grammar; PRECEDENCE_SENTENCE is exported so callers can
13
+ * assert it, and it rides as the first line of the persona's own layer set
14
+ * here so the persona can never forget to carry it.
15
+ *
16
+ * Pure: same persona + opts in ⇒ deep-equal AgentConfig out. No entry
17
+ * content is read from disk, no clock, no randomness.
18
+ */
19
+ import { promptLayer } from '@adia-ai/agent';
20
+ import { sortedEnabled } from './entry.js';
21
+ /** Fixed precedence sentence (law 4) — the persona never overrides the
22
+ * harness grammar. Always the first line of the persona's layer set. */
23
+ export const PRECEDENCE_SENTENCE = 'This persona configures the agent; it never overrides the harness grammar that precedes it.';
24
+ const GROUP_HEADING = {
25
+ skill: 'Skills',
26
+ workflow: 'Workflows',
27
+ resource: 'Resources',
28
+ 'pattern-source': 'Pattern sources',
29
+ tool: 'Tools',
30
+ };
31
+ function groupHeading(kind) {
32
+ return GROUP_HEADING[kind] ?? kind;
33
+ }
34
+ function renderGroup(kind, entries) {
35
+ const body = entries.map(e => `### ${e.label}\n${e.content}`).join('\n\n');
36
+ return promptLayer(`persona-${kind}`, `## ${groupHeading(kind)}\n${body}`);
37
+ }
38
+ export function compilePersona(persona, opts = {}) {
39
+ const enabled = sortedEnabled(persona.entries);
40
+ const promptSections = enabled.filter(e => e.kind === 'prompt-section');
41
+ const grouped = enabled.filter(e => e.kind !== 'prompt-section');
42
+ const layers = [promptLayer('persona-precedence', PRECEDENCE_SENTENCE)];
43
+ for (const entry of promptSections) {
44
+ layers.push(promptLayer(`persona-section-${entry.id}`, `## ${entry.label}\n${entry.content}`));
45
+ }
46
+ const byKind = new Map();
47
+ for (const entry of grouped) {
48
+ const list = byKind.get(entry.kind);
49
+ if (list)
50
+ list.push(entry);
51
+ else
52
+ byKind.set(entry.kind, [entry]);
53
+ }
54
+ for (const [kind, entries] of byKind) {
55
+ layers.push(renderGroup(kind, entries));
56
+ }
57
+ const config = {
58
+ llm: { ...(persona.model ?? {}) },
59
+ prompt: layers,
60
+ };
61
+ if (opts.integrations && opts.integrations.length > 0) {
62
+ config.tools = opts.integrations;
63
+ }
64
+ return config;
65
+ }
66
+ /** Seeded default dialect-term list (law 4 — modality neutrality). Presets
67
+ * seed this a la carte; a consumer with its own dialect words passes them
68
+ * via `opts.extraTerms` rather than editing this list. */
69
+ export const DEFAULT_DIALECT_TERMS = ['A2UI', 'envelope', 'JSONL'];
70
+ /** Component tag names — `foo-ui`, `data-table-ui`, … */
71
+ const TAG_PATTERN = /\b[a-z][a-z0-9]*-ui\b/gi;
72
+ /** Warning-level lint (law 4): flags dialect terms and component-tag-name
73
+ * patterns inside a persona's entry text. Never throws, never blocks —
74
+ * shipped seeds assert an empty result; a positive hit is a signal to
75
+ * reword, not a hard failure. */
76
+ export function checkModalityNeutral(persona, opts = {}) {
77
+ const terms = [...DEFAULT_DIALECT_TERMS, ...(opts.extraTerms ?? [])];
78
+ const warnings = [];
79
+ for (const entry of persona.entries) {
80
+ const haystack = `${entry.label}\n${entry.description ?? ''}\n${entry.content}`;
81
+ const lower = haystack.toLowerCase();
82
+ for (const term of terms) {
83
+ if (lower.includes(term.toLowerCase())) {
84
+ warnings.push({ entryId: entry.id, term });
85
+ }
86
+ }
87
+ for (const match of haystack.matchAll(TAG_PATTERN)) {
88
+ warnings.push({ entryId: entry.id, term: match[0] });
89
+ }
90
+ }
91
+ return warnings;
92
+ }
93
+ //# sourceMappingURL=compile.js.map
package/compile.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compile.js","sourceRoot":"","sources":["src/compile.ts"],"names":[],"mappings":"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"}
package/entry.d.ts ADDED
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Entry — the ONE generic primitive (CHAT-HARNESS law 7).
3
+ *
4
+ * Prompt sections, skills, workflows, resources, tools, pattern-sources are
5
+ * six INSTANTIATIONS of this one shape, not six code paths. `kind` is a
6
+ * plain string — KNOWN_KINDS exists for display grouping only; nothing in
7
+ * this package (or its caller) is allowed to switch on it for logic. A
8
+ * seventh kind is seed data, never a code change.
9
+ */
10
+ export interface Entry {
11
+ id: string;
12
+ kind: string;
13
+ label: string;
14
+ description?: string;
15
+ content: string;
16
+ order: number;
17
+ enabled: boolean;
18
+ builtin: boolean;
19
+ }
20
+ /** The six kinds this suite ships today — DISPLAY grouping only. */
21
+ export declare const KNOWN_KINDS: readonly ["prompt-section", "skill", "workflow", "resource", "tool", "pattern-source"];
22
+ export type KnownKind = (typeof KNOWN_KINDS)[number];
23
+ /** Enabled entries, in composition order. The only order compilePersona
24
+ * (or any consumer) should ever read entries in. */
25
+ export declare function sortedEnabled(entries: Entry[]): Entry[];
26
+ export type SeedEntryInput = Omit<Entry, 'order' | 'enabled' | 'builtin'> & Partial<Pick<Entry, 'order' | 'enabled' | 'builtin'>>;
27
+ /** One seed entry with sane defaults: enabled, builtin, order 0 unless
28
+ * given. Presets author entries with this rather than the raw shape. */
29
+ export declare function seedEntry(entry: SeedEntryInput): Entry;
30
+ /** A whole seed list at once — entries that omit `order` get one assigned
31
+ * by their position in the array, so a preset author never hand-numbers a
32
+ * long list. Entries that DO specify `order` keep it untouched. */
33
+ export declare function seedEntries(entries: SeedEntryInput[]): Entry[];
34
+ //# sourceMappingURL=entry.d.ts.map
package/entry.d.ts.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entry.d.ts","sourceRoot":"","sources":["src/entry.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,WAAW,KAAK;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,oEAAoE;AACpE,eAAO,MAAM,WAAW,wFAOd,CAAC;AAEX,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;AAErD;qDACqD;AACrD,wBAAgB,aAAa,CAAC,OAAO,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,CAEvD;AAED,MAAM,MAAM,cAAc,GAAG,IAAI,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,CAAC,GACvE,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,CAAC,CAAC,CAAC;AAExD;yEACyE;AACzE,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,KAAK,CAOtD;AAED;;oEAEoE;AACpE,wBAAgB,WAAW,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,KAAK,EAAE,CAE9D"}
package/entry.js ADDED
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Entry — the ONE generic primitive (CHAT-HARNESS law 7).
3
+ *
4
+ * Prompt sections, skills, workflows, resources, tools, pattern-sources are
5
+ * six INSTANTIATIONS of this one shape, not six code paths. `kind` is a
6
+ * plain string — KNOWN_KINDS exists for display grouping only; nothing in
7
+ * this package (or its caller) is allowed to switch on it for logic. A
8
+ * seventh kind is seed data, never a code change.
9
+ */
10
+ /** The six kinds this suite ships today — DISPLAY grouping only. */
11
+ export const KNOWN_KINDS = [
12
+ 'prompt-section',
13
+ 'skill',
14
+ 'workflow',
15
+ 'resource',
16
+ 'tool',
17
+ 'pattern-source',
18
+ ];
19
+ /** Enabled entries, in composition order. The only order compilePersona
20
+ * (or any consumer) should ever read entries in. */
21
+ export function sortedEnabled(entries) {
22
+ return entries.filter(e => e.enabled).sort((a, b) => a.order - b.order);
23
+ }
24
+ /** One seed entry with sane defaults: enabled, builtin, order 0 unless
25
+ * given. Presets author entries with this rather than the raw shape. */
26
+ export function seedEntry(entry) {
27
+ return {
28
+ order: 0,
29
+ enabled: true,
30
+ builtin: true,
31
+ ...entry,
32
+ };
33
+ }
34
+ /** A whole seed list at once — entries that omit `order` get one assigned
35
+ * by their position in the array, so a preset author never hand-numbers a
36
+ * long list. Entries that DO specify `order` keep it untouched. */
37
+ export function seedEntries(entries) {
38
+ return entries.map((entry, i) => seedEntry({ order: i, ...entry }));
39
+ }
40
+ //# sourceMappingURL=entry.js.map
package/entry.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entry.js","sourceRoot":"","sources":["src/entry.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAaH,oEAAoE;AACpE,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,gBAAgB;IAChB,OAAO;IACP,UAAU;IACV,UAAU;IACV,MAAM;IACN,gBAAgB;CACR,CAAC;AAIX;qDACqD;AACrD,MAAM,UAAU,aAAa,CAAC,OAAgB;IAC5C,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;AAC1E,CAAC;AAKD;yEACyE;AACzE,MAAM,UAAU,SAAS,CAAC,KAAqB;IAC7C,OAAO;QACL,KAAK,EAAE,CAAC;QACR,OAAO,EAAE,IAAI;QACb,OAAO,EAAE,IAAI;QACb,GAAG,KAAK;KACT,CAAC;AACJ,CAAC;AAED;;oEAEoE;AACpE,MAAM,UAAU,WAAW,CAAC,OAAyB;IACnD,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC;AACtE,CAAC"}
package/index.d.ts ADDED
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @adia-ai/persona — CHAT-HARNESS L2: an agent as DATA.
3
+ *
4
+ * Entry (six kinds, one shape) + Persona (seedVersion + entries) +
5
+ * SettingsStore (per-persona persistence) + compilePersona (pure, →
6
+ * @adia-ai/agent's AgentConfig) + versioned persona files. Renders
7
+ * nothing, executes nothing — L1 (@adia-ai/agent) and L4 (an admin
8
+ * composite) are the consumers.
9
+ */
10
+ export { KNOWN_KINDS, seedEntry, seedEntries, sortedEnabled } from './entry.js';
11
+ export type { Entry, KnownKind, SeedEntryInput } from './entry.js';
12
+ export { definePersona } from './persona.js';
13
+ export type { Persona } from './persona.js';
14
+ export { createMemoryStore, personaStore } from './store.js';
15
+ export type { MemoryStoreOpts, PersonaStoreOpts, SettingsStore } from './store.js';
16
+ export { checkModalityNeutral, compilePersona, DEFAULT_DIALECT_TERMS, PRECEDENCE_SENTENCE } from './compile.js';
17
+ export type { CompilePersonaOpts, ModalityLintOpts, ModalityWarning } from './compile.js';
18
+ export { exportPersona, importPersona, PERSONA_FILE_KIND, PERSONA_FILE_VERSION } from './persona-file.js';
19
+ //# sourceMappingURL=index.d.ts.map
package/index.d.ts.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChF,YAAY,EAAE,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEnE,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,YAAY,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC7D,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEnF,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAChH,YAAY,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE1F,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC"}
package/index.js ADDED
@@ -0,0 +1,15 @@
1
+ /**
2
+ * @adia-ai/persona — CHAT-HARNESS L2: an agent as DATA.
3
+ *
4
+ * Entry (six kinds, one shape) + Persona (seedVersion + entries) +
5
+ * SettingsStore (per-persona persistence) + compilePersona (pure, →
6
+ * @adia-ai/agent's AgentConfig) + versioned persona files. Renders
7
+ * nothing, executes nothing — L1 (@adia-ai/agent) and L4 (an admin
8
+ * composite) are the consumers.
9
+ */
10
+ export { KNOWN_KINDS, seedEntry, seedEntries, sortedEnabled } from './entry.js';
11
+ export { definePersona } from './persona.js';
12
+ export { createMemoryStore, personaStore } from './store.js';
13
+ export { checkModalityNeutral, compilePersona, DEFAULT_DIALECT_TERMS, PRECEDENCE_SENTENCE } from './compile.js';
14
+ export { exportPersona, importPersona, PERSONA_FILE_KIND, PERSONA_FILE_VERSION } from './persona-file.js';
15
+ //# sourceMappingURL=index.js.map
package/index.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAGhF,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAG7C,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG7D,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAGhH,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC"}
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@adia-ai/persona",
3
+ "version": "0.8.26",
4
+ "description": "Agent-as-data (CHAT-HARNESS L2): Entry primitive, Persona + SettingsStore contracts, pure compilePersona → @adia-ai/agent AgentConfig, versioned persona file import/export.",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./index.d.ts",
9
+ "import": "./index.js",
10
+ "default": "./index.js"
11
+ },
12
+ "./package.json": "./package.json"
13
+ },
14
+ "files": [
15
+ "*.js",
16
+ "*.d.ts",
17
+ "*.d.ts.map",
18
+ "*.js.map",
19
+ "README.md",
20
+ "CHANGELOG.md",
21
+ "!**/*.test.js"
22
+ ],
23
+ "scripts": {
24
+ "build": "tsc --build tsconfig.build.json",
25
+ "prepublishOnly": "npm run build"
26
+ },
27
+ "dependencies": {
28
+ "@adia-ai/agent": "^0.8.0"
29
+ },
30
+ "sideEffects": false,
31
+ "publishConfig": {
32
+ "access": "public",
33
+ "registry": "https://registry.npmjs.org"
34
+ },
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "git+https://github.com/adiahealth/gen-ui-kit.git",
38
+ "directory": "packages/persona"
39
+ },
40
+ "license": "MIT"
41
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Persona files — versioned export/import (CHAT-HARNESS §Interfaces 1).
3
+ *
4
+ * `{kind:'persona', version:1, ...}` with an ENUMERATED key allowlist:
5
+ * anything else in the JSON is a rejection, not a silently-dropped field.
6
+ * Import always MINTS a new persona (collision-safe id suffix, label
7
+ * " (imported)") — it never overwrites an existing preset, even one with
8
+ * the same id in the file.
9
+ */
10
+ import { type Persona } from './persona.js';
11
+ export declare const PERSONA_FILE_KIND = "persona";
12
+ export declare const PERSONA_FILE_VERSION = 1;
13
+ export declare function exportPersona(persona: Persona): string;
14
+ export declare function importPersona(json: string, existingIds: Iterable<string>): Persona;
15
+ //# sourceMappingURL=persona-file.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"persona-file.d.ts","sourceRoot":"","sources":["src/persona-file.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAiB,KAAK,OAAO,EAAE,MAAM,cAAc,CAAC;AAE3D,eAAO,MAAM,iBAAiB,YAAY,CAAC;AAC3C,eAAO,MAAM,oBAAoB,IAAI,CAAC;AAItC,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAYtD;AAcD,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,OAAO,CAsDlF"}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Persona files — versioned export/import (CHAT-HARNESS §Interfaces 1).
3
+ *
4
+ * `{kind:'persona', version:1, ...}` with an ENUMERATED key allowlist:
5
+ * anything else in the JSON is a rejection, not a silently-dropped field.
6
+ * Import always MINTS a new persona (collision-safe id suffix, label
7
+ * " (imported)") — it never overwrites an existing preset, even one with
8
+ * the same id in the file.
9
+ */
10
+ import { definePersona } from './persona.js';
11
+ export const PERSONA_FILE_KIND = 'persona';
12
+ export const PERSONA_FILE_VERSION = 1;
13
+ const ALLOWED_KEYS = new Set(['kind', 'version', 'id', 'label', 'category', 'seedVersion', 'entries', 'model']);
14
+ export function exportPersona(persona) {
15
+ const file = {
16
+ kind: PERSONA_FILE_KIND,
17
+ version: PERSONA_FILE_VERSION,
18
+ id: persona.id,
19
+ label: persona.label,
20
+ seedVersion: persona.seedVersion,
21
+ entries: persona.entries,
22
+ };
23
+ if (persona.category !== undefined)
24
+ file['category'] = persona.category;
25
+ if (persona.model !== undefined)
26
+ file['model'] = persona.model;
27
+ return JSON.stringify(file, null, 2);
28
+ }
29
+ /** Mint a collision-safe id: `<base>-imported`, then `<base>-imported-2`,
30
+ * `-3`, … against the given existing id set. */
31
+ function mintImportedId(baseId, existingIds) {
32
+ let candidate = `${baseId}-imported`;
33
+ let n = 2;
34
+ while (existingIds.has(candidate)) {
35
+ candidate = `${baseId}-imported-${n}`;
36
+ n += 1;
37
+ }
38
+ return candidate;
39
+ }
40
+ export function importPersona(json, existingIds) {
41
+ let parsed;
42
+ try {
43
+ parsed = JSON.parse(json);
44
+ }
45
+ catch (err) {
46
+ throw new Error(`importPersona: invalid JSON — ${err.message}`);
47
+ }
48
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
49
+ throw new Error('importPersona: expected a JSON object');
50
+ }
51
+ const obj = parsed;
52
+ const unknownKeys = Object.keys(obj).filter(k => !ALLOWED_KEYS.has(k));
53
+ if (unknownKeys.length > 0) {
54
+ throw new Error(`importPersona: unknown key(s): ${unknownKeys.join(', ')}`);
55
+ }
56
+ if (obj['kind'] !== PERSONA_FILE_KIND) {
57
+ throw new Error(`importPersona: expected kind "${PERSONA_FILE_KIND}", got ${JSON.stringify(obj['kind'])}`);
58
+ }
59
+ if (obj['version'] !== PERSONA_FILE_VERSION) {
60
+ throw new Error(`importPersona: unsupported version ${JSON.stringify(obj['version'])}`);
61
+ }
62
+ if (typeof obj['id'] !== 'string' || !obj['id']) {
63
+ throw new Error('importPersona: "id" must be a non-empty string');
64
+ }
65
+ if (typeof obj['label'] !== 'string' || !obj['label']) {
66
+ throw new Error('importPersona: "label" must be a non-empty string');
67
+ }
68
+ if (typeof obj['seedVersion'] !== 'number') {
69
+ throw new Error('importPersona: "seedVersion" must be a number');
70
+ }
71
+ if (!Array.isArray(obj['entries'])) {
72
+ throw new Error('importPersona: "entries" must be an array');
73
+ }
74
+ const existing = new Set(existingIds);
75
+ const id = mintImportedId(obj['id'], existing);
76
+ const persona = {
77
+ id,
78
+ label: `${obj['label']} (imported)`,
79
+ seedVersion: obj['seedVersion'],
80
+ // Imported entries are never builtin — they came from a file, not a
81
+ // shipped preset — everything else on each entry rides through as-is.
82
+ entries: obj['entries'].map(entry => ({ ...entry, builtin: false })),
83
+ };
84
+ const category = obj['category'];
85
+ if (typeof category === 'string')
86
+ persona.category = category;
87
+ const model = obj['model'];
88
+ if (model && typeof model === 'object' && !Array.isArray(model)) {
89
+ persona.model = model;
90
+ }
91
+ return definePersona(persona);
92
+ }
93
+ //# sourceMappingURL=persona-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"persona-file.js","sourceRoot":"","sources":["src/persona-file.ts"],"names":[],"mappings":"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"}
package/persona.d.ts ADDED
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Persona — an agent as DATA (CHAT-HARNESS §Interfaces 1).
3
+ *
4
+ * A persona is a bag of Entry rows plus the two axes that aren't entries:
5
+ * seedVersion (forces a one-time store reset — see store.ts) and an
6
+ * optional model hint. `definePersona` validates the invariants a bad seed
7
+ * would otherwise violate silently: unique entry ids, seedVersion >= 1.
8
+ */
9
+ import type { Entry } from './entry.js';
10
+ export interface Persona {
11
+ id: string;
12
+ label: string;
13
+ category?: string;
14
+ /** Bumping this forces every per-persona store to reset persisted edits
15
+ * back to the seed once (store.ts personaStore). */
16
+ seedVersion: number;
17
+ entries: Entry[];
18
+ model?: {
19
+ provider?: string;
20
+ model?: string;
21
+ };
22
+ }
23
+ export declare function definePersona(persona: Persona): Persona;
24
+ //# sourceMappingURL=persona.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"persona.d.ts","sourceRoot":"","sources":["src/persona.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExC,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;yDACqD;IACrD,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,KAAK,EAAE,CAAC;IACjB,KAAK,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC/C;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAevD"}
package/persona.js ADDED
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Persona — an agent as DATA (CHAT-HARNESS §Interfaces 1).
3
+ *
4
+ * A persona is a bag of Entry rows plus the two axes that aren't entries:
5
+ * seedVersion (forces a one-time store reset — see store.ts) and an
6
+ * optional model hint. `definePersona` validates the invariants a bad seed
7
+ * would otherwise violate silently: unique entry ids, seedVersion >= 1.
8
+ */
9
+ export function definePersona(persona) {
10
+ if (!persona.id) {
11
+ throw new Error('definePersona: "id" is required');
12
+ }
13
+ if (!Number.isInteger(persona.seedVersion) || persona.seedVersion < 1) {
14
+ throw new Error(`definePersona "${persona.id}": seedVersion must be an integer >= 1, got ${persona.seedVersion}`);
15
+ }
16
+ const seen = new Set();
17
+ for (const entry of persona.entries) {
18
+ if (seen.has(entry.id)) {
19
+ throw new Error(`definePersona "${persona.id}": duplicate entry id "${entry.id}"`);
20
+ }
21
+ seen.add(entry.id);
22
+ }
23
+ return persona;
24
+ }
25
+ //# sourceMappingURL=persona.js.map
package/persona.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"persona.js","sourceRoot":"","sources":["src/persona.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAeH,MAAM,UAAU,aAAa,CAAC,OAAgB;IAC5C,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;IACrD,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,OAAO,CAAC,WAAW,GAAG,CAAC,EAAE,CAAC;QACtE,MAAM,IAAI,KAAK,CAAC,kBAAkB,OAAO,CAAC,EAAE,+CAA+C,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IACpH,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACpC,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CAAC,kBAAkB,OAAO,CAAC,EAAE,0BAA0B,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC;QACrF,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACrB,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
package/store.d.ts ADDED
@@ -0,0 +1,42 @@
1
+ /**
2
+ * SettingsStore — the L2 persistence seam (CHAT-HARNESS §Interfaces 5).
3
+ *
4
+ * `createMemoryStore` is the reference implementation: localStorage-backed
5
+ * when available (browser), plain in-memory otherwise (Node, tests, SSR).
6
+ * `personaStore` layers the per-persona precedence rule on top of any
7
+ * SettingsStore factory: persisted values win over seed data across round
8
+ * trips, and a seedVersion bump forces exactly one reset back to the seed.
9
+ */
10
+ import type { Persona } from './persona.js';
11
+ export interface SettingsStore {
12
+ get(key: string): unknown;
13
+ set(key: string, value: unknown): void;
14
+ subscribe?(cb: () => void): () => void;
15
+ }
16
+ export interface MemoryStoreOpts {
17
+ initial?: Record<string, unknown>;
18
+ /** When set and `localStorage` exists, reads/writes persist there under
19
+ * this key (one JSON blob). Absent — or no localStorage — stays in
20
+ * memory for the life of the store object. */
21
+ persistKey?: string;
22
+ }
23
+ /** Reference SettingsStore: localStorage when available and a persistKey is
24
+ * given, in-memory otherwise. `subscribe` fires on every `set`, including
25
+ * ones that don't change the value — callers debounce if they need to. */
26
+ export declare function createMemoryStore(opts?: MemoryStoreOpts): SettingsStore;
27
+ export interface PersonaStoreOpts {
28
+ /** Store factory to bind — defaults to createMemoryStore. Tests inject a
29
+ * bare in-memory factory to avoid localStorage entirely. */
30
+ createStore?: (opts: MemoryStoreOpts) => SettingsStore;
31
+ }
32
+ /** One SettingsStore bound to one persona (persistKey `persona.<id>`).
33
+ *
34
+ * Precedence (CHAT-HARNESS §Interfaces 5):
35
+ * - Same seedVersion as last time this store was opened: persisted values
36
+ * win — the seed only fills keys the store has never seen.
37
+ * - Different seedVersion (first open, or a bump): the seed WINS — every
38
+ * seed key is written, overwriting whatever was persisted — exactly
39
+ * once, then the new seedVersion is recorded so the next open at the
40
+ * same version doesn't reset again. */
41
+ export declare function personaStore(persona: Persona, opts?: PersonaStoreOpts): SettingsStore;
42
+ //# sourceMappingURL=store.d.ts.map
package/store.d.ts.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["src/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAC1B,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IACvC,SAAS,CAAC,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;CACxC;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC;;mDAE+C;IAC/C,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAUD;;2EAE2E;AAC3E,wBAAgB,iBAAiB,CAAC,IAAI,GAAE,eAAoB,GAAG,aAAa,CA+C3E;AAED,MAAM,WAAW,gBAAgB;IAC/B;iEAC6D;IAC7D,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,eAAe,KAAK,aAAa,CAAC;CACxD;AAiBD;;;;;;;;0CAQ0C;AAC1C,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,GAAE,gBAAqB,GAAG,aAAa,CAezF"}
package/store.js ADDED
@@ -0,0 +1,110 @@
1
+ /**
2
+ * SettingsStore — the L2 persistence seam (CHAT-HARNESS §Interfaces 5).
3
+ *
4
+ * `createMemoryStore` is the reference implementation: localStorage-backed
5
+ * when available (browser), plain in-memory otherwise (Node, tests, SSR).
6
+ * `personaStore` layers the per-persona precedence rule on top of any
7
+ * SettingsStore factory: persisted values win over seed data across round
8
+ * trips, and a seedVersion bump forces exactly one reset back to the seed.
9
+ */
10
+ function hasLocalStorage() {
11
+ try {
12
+ return typeof localStorage !== 'undefined';
13
+ }
14
+ catch {
15
+ return false;
16
+ }
17
+ }
18
+ /** Reference SettingsStore: localStorage when available and a persistKey is
19
+ * given, in-memory otherwise. `subscribe` fires on every `set`, including
20
+ * ones that don't change the value — callers debounce if they need to. */
21
+ export function createMemoryStore(opts = {}) {
22
+ const persistKey = opts.persistKey;
23
+ const persistent = !!persistKey && hasLocalStorage();
24
+ let data = { ...(opts.initial ?? {}) };
25
+ if (persistent) {
26
+ try {
27
+ const raw = localStorage.getItem(persistKey);
28
+ if (raw)
29
+ data = { ...data, ...JSON.parse(raw) };
30
+ }
31
+ catch {
32
+ // Corrupt or inaccessible storage — fall back to `initial` only.
33
+ }
34
+ }
35
+ const subscribers = new Set();
36
+ function persist() {
37
+ if (!persistent)
38
+ return;
39
+ try {
40
+ localStorage.setItem(persistKey, JSON.stringify(data));
41
+ }
42
+ catch {
43
+ // Quota/denied — the in-memory copy still holds for this session.
44
+ }
45
+ }
46
+ return {
47
+ get(key) {
48
+ return data[key];
49
+ },
50
+ set(key, value) {
51
+ // A write of the identical value must NOT notify: UI consumers echo
52
+ // rendered state back through set() (a render assigns .checked, the
53
+ // primitive emits change, the handler writes the same value) — and a
54
+ // notifying no-op write closes that echo into a subscribe → re-render
55
+ // → set loop (the v0.8.26 pre-flight's "signals: drain loop exceeded"
56
+ // livelock in admin-settings). Object.is matches the reactive kernel's
57
+ // own equality cutoff.
58
+ if (key in data && Object.is(data[key], value))
59
+ return;
60
+ data = { ...data, [key]: value };
61
+ persist();
62
+ for (const cb of subscribers)
63
+ cb();
64
+ },
65
+ subscribe(cb) {
66
+ subscribers.add(cb);
67
+ return () => subscribers.delete(cb);
68
+ },
69
+ };
70
+ }
71
+ const SEED_VERSION_KEY = '__seedVersion';
72
+ function entryKey(entryId, field) {
73
+ return `entry.${entryId}.${field}`;
74
+ }
75
+ function seedFromPersona(persona) {
76
+ const seed = {};
77
+ for (const entry of persona.entries) {
78
+ seed[entryKey(entry.id, 'enabled')] = entry.enabled;
79
+ seed[entryKey(entry.id, 'order')] = entry.order;
80
+ }
81
+ return seed;
82
+ }
83
+ /** One SettingsStore bound to one persona (persistKey `persona.<id>`).
84
+ *
85
+ * Precedence (CHAT-HARNESS §Interfaces 5):
86
+ * - Same seedVersion as last time this store was opened: persisted values
87
+ * win — the seed only fills keys the store has never seen.
88
+ * - Different seedVersion (first open, or a bump): the seed WINS — every
89
+ * seed key is written, overwriting whatever was persisted — exactly
90
+ * once, then the new seedVersion is recorded so the next open at the
91
+ * same version doesn't reset again. */
92
+ export function personaStore(persona, opts = {}) {
93
+ const createStore = opts.createStore ?? createMemoryStore;
94
+ const store = createStore({ persistKey: `persona.${persona.id}` });
95
+ const seed = seedFromPersona(persona);
96
+ const storedVersion = store.get(SEED_VERSION_KEY);
97
+ if (storedVersion !== persona.seedVersion) {
98
+ for (const [key, value] of Object.entries(seed))
99
+ store.set(key, value);
100
+ store.set(SEED_VERSION_KEY, persona.seedVersion);
101
+ }
102
+ else {
103
+ for (const [key, value] of Object.entries(seed)) {
104
+ if (store.get(key) === undefined)
105
+ store.set(key, value);
106
+ }
107
+ }
108
+ return store;
109
+ }
110
+ //# sourceMappingURL=store.js.map
package/store.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["src/store.ts"],"names":[],"mappings":"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"}