@seanhogg/builderforce-memory-engine 2026.9.28 → 2026.9.29
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/evermind/adapt.d.ts +13 -0
- package/dist/evermind/adapt.d.ts.map +1 -1
- package/dist/evermind/adapt.js +13 -1
- package/dist/evermind/adapt.js.map +1 -1
- package/dist/evermind/index.d.ts +3 -3
- package/dist/evermind/index.d.ts.map +1 -1
- package/dist/evermind/index.js +2 -2
- package/dist/evermind/index.js.map +1 -1
- package/dist/evermind/package.d.ts +16 -0
- package/dist/evermind/package.d.ts.map +1 -1
- package/dist/evermind/package.js +40 -14
- package/dist/evermind/package.js.map +1 -1
- package/dist/experience/approval.d.ts +16 -0
- package/dist/experience/approval.d.ts.map +1 -0
- package/dist/experience/approval.js +40 -0
- package/dist/experience/approval.js.map +1 -0
- package/dist/experience/corpus.d.ts +24 -0
- package/dist/experience/corpus.d.ts.map +1 -0
- package/dist/experience/corpus.js +90 -0
- package/dist/experience/corpus.js.map +1 -0
- package/dist/experience/index.d.ts +18 -0
- package/dist/experience/index.d.ts.map +1 -0
- package/dist/experience/index.js +8 -0
- package/dist/experience/index.js.map +1 -0
- package/dist/experience/overview.d.ts +52 -0
- package/dist/experience/overview.d.ts.map +1 -0
- package/dist/experience/overview.js +92 -0
- package/dist/experience/overview.js.map +1 -0
- package/dist/experience/schedule.d.ts +10 -0
- package/dist/experience/schedule.d.ts.map +1 -0
- package/dist/experience/schedule.js +22 -0
- package/dist/experience/schedule.js.map +1 -0
- package/dist/experience/store.d.ts +88 -0
- package/dist/experience/store.d.ts.map +1 -0
- package/dist/experience/store.js +128 -0
- package/dist/experience/store.js.map +1 -0
- package/dist/experience/trainOnce.d.ts +18 -0
- package/dist/experience/trainOnce.d.ts.map +1 -0
- package/dist/experience/trainOnce.js +74 -0
- package/dist/experience/trainOnce.js.map +1 -0
- package/dist/experience/types.d.ts +171 -0
- package/dist/experience/types.d.ts.map +1 -0
- package/dist/experience/types.js +22 -0
- package/dist/experience/types.js.map +1 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/evermind/adapt.ts +26 -1
- package/src/evermind/index.ts +3 -3
- package/src/evermind/package.ts +39 -12
- package/src/experience/approval.ts +49 -0
- package/src/experience/corpus.ts +103 -0
- package/src/experience/index.ts +35 -0
- package/src/experience/overview.ts +143 -0
- package/src/experience/schedule.ts +29 -0
- package/src/experience/store.ts +188 -0
- package/src/experience/trainOnce.ts +85 -0
- package/src/experience/types.ts +162 -0
- package/src/index.ts +8 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* experience/store.ts — the persistence port for experience, and its snapshot format.
|
|
3
|
+
*
|
|
4
|
+
* The engine does no I/O (checkpoints are `ArrayBuffer`s the host stores); experience
|
|
5
|
+
* follows the same rule. `ExperienceStore` is the port every host implements over its own
|
|
6
|
+
* storage — a JSON file on disk (memory-mcp), IndexedDB, a KV. `InMemoryExperienceStore`
|
|
7
|
+
* is the reference implementation, and `ExperienceSnapshot` is the one serialized shape,
|
|
8
|
+
* so a store on one runtime can be read by another.
|
|
9
|
+
*/
|
|
10
|
+
import type { Adaptation, Episode, EpisodeSummary, Run, RunStepLog, Skill } from './types.js';
|
|
11
|
+
|
|
12
|
+
export const EXPERIENCE_SCHEMA = 'evermind.experience/1';
|
|
13
|
+
/** Runs kept, newest first — the audit trail is recent history, not an archive. */
|
|
14
|
+
export const MAX_RUNS = 500;
|
|
15
|
+
/** Adaptations kept, newest first — enough history to chart how learning went. */
|
|
16
|
+
export const MAX_ADAPTATIONS = 200;
|
|
17
|
+
|
|
18
|
+
export interface ExperienceSnapshot {
|
|
19
|
+
schema: typeof EXPERIENCE_SCHEMA;
|
|
20
|
+
episodes: Episode[];
|
|
21
|
+
skills: Skill[];
|
|
22
|
+
runs: Run[];
|
|
23
|
+
/**
|
|
24
|
+
* Which episodes and skills the local Evermind has been adapted on, and by which
|
|
25
|
+
* adaptation (the N of the model's `+expN` version) — so training reads each
|
|
26
|
+
* procedure once, and a model rolled back to an earlier N re-learns what the
|
|
27
|
+
* discarded versions had learned.
|
|
28
|
+
*/
|
|
29
|
+
learned: Record<string, number>;
|
|
30
|
+
/** Every adaptation's measurements, newest first (capped at {@link MAX_ADAPTATIONS}). */
|
|
31
|
+
adaptations: Adaptation[];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface ExperienceStore {
|
|
35
|
+
putEpisode(episode: Episode): Promise<void>;
|
|
36
|
+
getEpisode(id: string): Promise<Episode | undefined>;
|
|
37
|
+
listEpisodes(): Promise<EpisodeSummary[]>;
|
|
38
|
+
/** Forget a demonstration. Skills compiled from it keep working — they carry their own steps. */
|
|
39
|
+
deleteEpisode(id: string): Promise<boolean>;
|
|
40
|
+
|
|
41
|
+
putSkill(skill: Skill): Promise<void>;
|
|
42
|
+
getSkill(id: string): Promise<Skill | undefined>;
|
|
43
|
+
listSkills(): Promise<Skill[]>;
|
|
44
|
+
deleteSkill(id: string): Promise<boolean>;
|
|
45
|
+
|
|
46
|
+
putRun(run: Run): Promise<void>;
|
|
47
|
+
/** Append one audit line to a run; false when the run is unknown. */
|
|
48
|
+
appendRunStep(runId: string, step: RunStepLog): Promise<boolean>;
|
|
49
|
+
getRun(id: string): Promise<Run | undefined>;
|
|
50
|
+
listRuns(limit: number): Promise<Run[]>;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Record one adaptation: its items join the learned ledger under its index (the
|
|
54
|
+
* model's `+expN`) and its measurements join the history — one write, so the ledger
|
|
55
|
+
* and the chart can never disagree.
|
|
56
|
+
*/
|
|
57
|
+
recordAdaptation(adaptation: Adaptation): Promise<void>;
|
|
58
|
+
|
|
59
|
+
snapshot(): Promise<ExperienceSnapshot>;
|
|
60
|
+
/** Delete every episode, skill and run. */
|
|
61
|
+
clear(): Promise<void>;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function emptySnapshot(): ExperienceSnapshot {
|
|
65
|
+
return { schema: EXPERIENCE_SCHEMA, episodes: [], skills: [], runs: [], learned: {}, adaptations: [] };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Parse a snapshot, tolerating a missing or foreign file as empty. */
|
|
69
|
+
export function parseSnapshot(raw: unknown): ExperienceSnapshot {
|
|
70
|
+
const s = raw as Partial<ExperienceSnapshot> | null;
|
|
71
|
+
if (!s || s.schema !== EXPERIENCE_SCHEMA) return emptySnapshot();
|
|
72
|
+
return {
|
|
73
|
+
schema: EXPERIENCE_SCHEMA,
|
|
74
|
+
episodes: Array.isArray(s.episodes) ? s.episodes : [],
|
|
75
|
+
skills: Array.isArray(s.skills) ? s.skills : [],
|
|
76
|
+
runs: Array.isArray(s.runs) ? s.runs : [],
|
|
77
|
+
learned: s.learned && typeof s.learned === 'object' && !Array.isArray(s.learned) ? s.learned : {},
|
|
78
|
+
// Optional within schema 1: snapshots written before the history existed have none.
|
|
79
|
+
adaptations: Array.isArray(s.adaptations) ? s.adaptations : [],
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function summary(e: Episode): EpisodeSummary {
|
|
84
|
+
return { id: e.id, name: e.name, program: e.program, startedAt: e.startedAt, steps: e.steps.length };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* The reference store: the snapshot held in memory. Hosts that persist wrap it and
|
|
89
|
+
* write `snapshot()` after each change (see `onChange`).
|
|
90
|
+
*/
|
|
91
|
+
export class InMemoryExperienceStore implements ExperienceStore {
|
|
92
|
+
private data: ExperienceSnapshot;
|
|
93
|
+
|
|
94
|
+
constructor(initial?: ExperienceSnapshot, private readonly onChange?: (s: ExperienceSnapshot) => void | Promise<void>) {
|
|
95
|
+
this.data = initial ? parseSnapshot(initial) : emptySnapshot();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Replace the contents (a host re-reading its file after another process wrote it). */
|
|
99
|
+
load(snapshot: ExperienceSnapshot): void {
|
|
100
|
+
this.data = parseSnapshot(snapshot);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
private async changed(): Promise<void> {
|
|
104
|
+
await this.onChange?.(this.data);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
private static upsert<T extends { id: string }>(list: T[], item: T): void {
|
|
108
|
+
const i = list.findIndex((x) => x.id === item.id);
|
|
109
|
+
if (i >= 0) list[i] = item;
|
|
110
|
+
else list.push(item);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private static remove<T extends { id: string }>(list: T[], id: string): boolean {
|
|
114
|
+
const i = list.findIndex((x) => x.id === id);
|
|
115
|
+
if (i < 0) return false;
|
|
116
|
+
list.splice(i, 1);
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async putEpisode(episode: Episode): Promise<void> {
|
|
121
|
+
InMemoryExperienceStore.upsert(this.data.episodes, episode);
|
|
122
|
+
await this.changed();
|
|
123
|
+
}
|
|
124
|
+
async getEpisode(id: string): Promise<Episode | undefined> {
|
|
125
|
+
return this.data.episodes.find((e) => e.id === id);
|
|
126
|
+
}
|
|
127
|
+
async listEpisodes(): Promise<EpisodeSummary[]> {
|
|
128
|
+
return [...this.data.episodes].sort((a, b) => b.startedAt - a.startedAt).map(summary);
|
|
129
|
+
}
|
|
130
|
+
async deleteEpisode(id: string): Promise<boolean> {
|
|
131
|
+
const ok = InMemoryExperienceStore.remove(this.data.episodes, id);
|
|
132
|
+
delete this.data.learned[id];
|
|
133
|
+
if (ok) await this.changed();
|
|
134
|
+
return ok;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async putSkill(skill: Skill): Promise<void> {
|
|
138
|
+
InMemoryExperienceStore.upsert(this.data.skills, skill);
|
|
139
|
+
await this.changed();
|
|
140
|
+
}
|
|
141
|
+
async getSkill(id: string): Promise<Skill | undefined> {
|
|
142
|
+
return this.data.skills.find((s) => s.id === id);
|
|
143
|
+
}
|
|
144
|
+
async listSkills(): Promise<Skill[]> {
|
|
145
|
+
return [...this.data.skills].sort((a, b) => b.createdAt - a.createdAt);
|
|
146
|
+
}
|
|
147
|
+
async deleteSkill(id: string): Promise<boolean> {
|
|
148
|
+
const ok = InMemoryExperienceStore.remove(this.data.skills, id);
|
|
149
|
+
delete this.data.learned[id];
|
|
150
|
+
if (ok) await this.changed();
|
|
151
|
+
return ok;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async putRun(run: Run): Promise<void> {
|
|
155
|
+
InMemoryExperienceStore.upsert(this.data.runs, run);
|
|
156
|
+
this.data.runs.sort((a, b) => b.startedAt - a.startedAt);
|
|
157
|
+
this.data.runs.length = Math.min(this.data.runs.length, MAX_RUNS);
|
|
158
|
+
await this.changed();
|
|
159
|
+
}
|
|
160
|
+
async appendRunStep(runId: string, step: RunStepLog): Promise<boolean> {
|
|
161
|
+
const run = this.data.runs.find((r) => r.id === runId);
|
|
162
|
+
if (!run) return false;
|
|
163
|
+
run.steps.push(step);
|
|
164
|
+
await this.changed();
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
async getRun(id: string): Promise<Run | undefined> {
|
|
168
|
+
return this.data.runs.find((r) => r.id === id);
|
|
169
|
+
}
|
|
170
|
+
async listRuns(limit: number): Promise<Run[]> {
|
|
171
|
+
return this.data.runs.slice(0, Math.max(0, limit));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async recordAdaptation(adaptation: Adaptation): Promise<void> {
|
|
175
|
+
for (const id of adaptation.learned) this.data.learned[id] = adaptation.index;
|
|
176
|
+
this.data.adaptations.unshift(adaptation);
|
|
177
|
+
this.data.adaptations.length = Math.min(this.data.adaptations.length, MAX_ADAPTATIONS);
|
|
178
|
+
await this.changed();
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async snapshot(): Promise<ExperienceSnapshot> {
|
|
182
|
+
return structuredClone(this.data);
|
|
183
|
+
}
|
|
184
|
+
async clear(): Promise<void> {
|
|
185
|
+
this.data = emptySnapshot();
|
|
186
|
+
await this.changed();
|
|
187
|
+
}
|
|
188
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* experience/trainOnce.ts — Train Once: one reviewed demonstration becomes a skill that
|
|
3
|
+
* runs again with new values.
|
|
4
|
+
*
|
|
5
|
+
* Deterministic, local, no model call. Every value the person typed becomes a named
|
|
6
|
+
* parameter (named after the field it went into), every secret field a secret parameter,
|
|
7
|
+
* and every irreversible click an approval gate. The review edits decide what stays.
|
|
8
|
+
*/
|
|
9
|
+
import { elementLabel, needsApproval } from './approval.js';
|
|
10
|
+
import { newExperienceId, type Episode, type Skill, type SkillAction, type SkillParam, type SkillStep } from './types.js';
|
|
11
|
+
|
|
12
|
+
/** What the person changed in review, keyed by recorded step id. */
|
|
13
|
+
export interface ReviewEdits {
|
|
14
|
+
/** The skill's name (defaults to the episode's). */
|
|
15
|
+
name?: string | null;
|
|
16
|
+
/** Steps left out of the skill. */
|
|
17
|
+
removed?: string[];
|
|
18
|
+
/** Typed values kept exactly as recorded instead of asked for each run. */
|
|
19
|
+
fixed?: string[];
|
|
20
|
+
/** Approval gate per step, where the person overrode the default. */
|
|
21
|
+
approvals?: Record<string, boolean>;
|
|
22
|
+
/** Parameter names the person chose, per step. */
|
|
23
|
+
paramNames?: Record<string, string>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function trainOnce(episode: Episode, edits: ReviewEdits = {}, now: number = Date.now()): Skill {
|
|
27
|
+
const removed = new Set(edits.removed ?? []);
|
|
28
|
+
const fixed = new Set(edits.fixed ?? []);
|
|
29
|
+
const params: SkillParam[] = [];
|
|
30
|
+
const steps: SkillStep[] = [];
|
|
31
|
+
for (const rec of episode.steps) {
|
|
32
|
+
if (removed.has(rec.id)) continue;
|
|
33
|
+
const a = rec.action;
|
|
34
|
+
let action: SkillAction;
|
|
35
|
+
if (a.kind === 'setValue') {
|
|
36
|
+
if (!a.secret && fixed.has(rec.id)) {
|
|
37
|
+
action = { kind: 'setValue', target: a.target, value: { from: 'literal', text: a.value } };
|
|
38
|
+
} else {
|
|
39
|
+
const chosen = slug(edits.paramNames?.[rec.id] ?? '');
|
|
40
|
+
const base = chosen || slug(elementLabel(a.target)) || 'value';
|
|
41
|
+
const name = uniqueName(params, base);
|
|
42
|
+
params.push({ name, label: elementLabel(a.target), default: a.secret ? null : a.value, secret: !!a.secret });
|
|
43
|
+
action = { kind: 'setValue', target: a.target, value: { from: 'param', name } };
|
|
44
|
+
}
|
|
45
|
+
} else {
|
|
46
|
+
action = a;
|
|
47
|
+
}
|
|
48
|
+
steps.push({
|
|
49
|
+
id: rec.id,
|
|
50
|
+
action,
|
|
51
|
+
requiresApproval: edits.approvals?.[rec.id] ?? needsApproval(a),
|
|
52
|
+
screenshot: rec.screenshot ?? null,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
id: newExperienceId(),
|
|
57
|
+
name: edits.name?.trim() || episode.name,
|
|
58
|
+
program: episode.program,
|
|
59
|
+
args: [...episode.args],
|
|
60
|
+
params,
|
|
61
|
+
steps,
|
|
62
|
+
sourceEpisode: episode.id,
|
|
63
|
+
createdAt: now,
|
|
64
|
+
routine: null,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** `Invoice Amount (USD)` → `invoice_amount_usd`. Letters in any script survive. */
|
|
69
|
+
export function slug(label: string): string {
|
|
70
|
+
return label
|
|
71
|
+
.toLowerCase()
|
|
72
|
+
.split(/[^\p{L}\p{N}]+/u)
|
|
73
|
+
.filter(Boolean)
|
|
74
|
+
.join('_')
|
|
75
|
+
.slice(0, 40)
|
|
76
|
+
.replace(/_+$/, '');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function uniqueName(params: SkillParam[], base: string): string {
|
|
80
|
+
if (!params.some((p) => p.name === base)) return base;
|
|
81
|
+
for (let i = 2; ; i++) {
|
|
82
|
+
const n = `${base}_${i}`;
|
|
83
|
+
if (!params.some((p) => p.name === n)) return n;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* experience/types.ts — what Evermind has DONE, as opposed to what it knows.
|
|
3
|
+
*
|
|
4
|
+
* Facts live in `@seanhogg/builderforce-memory` (MemoryStore / EvermindCognition) and
|
|
5
|
+
* weights in the model; this is the third kind of learned item: experience.
|
|
6
|
+
* - an **Episode** is one demonstration — a person doing a task in a program, recorded
|
|
7
|
+
* as semantic steps (the UI element acted on and the value set, never raw keystrokes);
|
|
8
|
+
* - a **Skill** is what Train Once compiles from an episode — a procedure that runs
|
|
9
|
+
* again with new values, with approval gates on irreversible steps;
|
|
10
|
+
* - a **Run** is one execution of a skill, with its step-by-step audit trail.
|
|
11
|
+
*
|
|
12
|
+
* Plain data, JSON-shaped (camelCase, actions tagged by `kind`), so any recorder — the
|
|
13
|
+
* native Windows UI Automation one in Synapse included — produces it and any runtime
|
|
14
|
+
* persists it. Timestamps are epoch milliseconds.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/** A UI element, identified the way the platform's accessibility API sees it. */
|
|
18
|
+
export interface ElementRef {
|
|
19
|
+
name: string;
|
|
20
|
+
automationId: string;
|
|
21
|
+
/** `Button`, `Edit`, `MenuItem`, … */
|
|
22
|
+
controlType: string;
|
|
23
|
+
className: string;
|
|
24
|
+
/** The top-level window the element sits in. */
|
|
25
|
+
windowTitle: string;
|
|
26
|
+
processName: string;
|
|
27
|
+
/** Where it was acted on, relative to the window's top-left — the replay fallback. */
|
|
28
|
+
relX: number;
|
|
29
|
+
relY: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** One thing the person did. A secret field records that it was filled, never the value. */
|
|
33
|
+
export type Action =
|
|
34
|
+
| { kind: 'click'; target: ElementRef }
|
|
35
|
+
| { kind: 'setValue'; target: ElementRef; value: string; secret?: boolean }
|
|
36
|
+
| { kind: 'keys'; keys: string; target?: ElementRef | null };
|
|
37
|
+
|
|
38
|
+
export interface RecordedStep {
|
|
39
|
+
id: string;
|
|
40
|
+
/** Milliseconds since the recording started. */
|
|
41
|
+
atMs: number;
|
|
42
|
+
action: Action;
|
|
43
|
+
/** Screenshot file name inside the episode's folder, if one was taken. */
|
|
44
|
+
screenshot?: string | null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface Episode {
|
|
48
|
+
id: string;
|
|
49
|
+
name: string;
|
|
50
|
+
program: string;
|
|
51
|
+
args: string[];
|
|
52
|
+
startedAt: number;
|
|
53
|
+
endedAt?: number | null;
|
|
54
|
+
steps: RecordedStep[];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface EpisodeSummary {
|
|
58
|
+
id: string;
|
|
59
|
+
name: string;
|
|
60
|
+
program: string;
|
|
61
|
+
startedAt: number;
|
|
62
|
+
steps: number;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** A value a skill asks for at run time. */
|
|
66
|
+
export interface SkillParam {
|
|
67
|
+
/** Stable key, e.g. `amount`. */
|
|
68
|
+
name: string;
|
|
69
|
+
/** What the field was called where it was typed, e.g. `Amount`. */
|
|
70
|
+
label: string;
|
|
71
|
+
/** The value from the demonstration; never set for a secret. */
|
|
72
|
+
default?: string | null;
|
|
73
|
+
secret: boolean;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export type SkillValue = { from: 'param'; name: string } | { from: 'literal'; text: string };
|
|
77
|
+
|
|
78
|
+
export type SkillAction =
|
|
79
|
+
| { kind: 'click'; target: ElementRef }
|
|
80
|
+
| { kind: 'setValue'; target: ElementRef; value: SkillValue }
|
|
81
|
+
| { kind: 'keys'; keys: string; target?: ElementRef | null };
|
|
82
|
+
|
|
83
|
+
export interface SkillStep {
|
|
84
|
+
id: string;
|
|
85
|
+
action: SkillAction;
|
|
86
|
+
/** Pause and ask the person before this step. */
|
|
87
|
+
requiresApproval: boolean;
|
|
88
|
+
screenshot?: string | null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** When a skill runs by itself. Local time of day for `day`. */
|
|
92
|
+
export type Schedule = { every: 'minutes'; minutes: number } | { every: 'day'; hour: number; minute: number };
|
|
93
|
+
|
|
94
|
+
/** A skill's routine: at most one per skill, so it lives on the skill. */
|
|
95
|
+
export interface SkillRoutine {
|
|
96
|
+
schedule: Schedule;
|
|
97
|
+
/** Values for the non-secret parameters; secrets come from the host's vault. */
|
|
98
|
+
values: Record<string, string>;
|
|
99
|
+
enabled: boolean;
|
|
100
|
+
lastRunAt?: number | null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export interface Skill {
|
|
104
|
+
id: string;
|
|
105
|
+
name: string;
|
|
106
|
+
program: string;
|
|
107
|
+
args: string[];
|
|
108
|
+
params: SkillParam[];
|
|
109
|
+
steps: SkillStep[];
|
|
110
|
+
sourceEpisode: string;
|
|
111
|
+
createdAt: number;
|
|
112
|
+
routine?: SkillRoutine | null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export type RunStatus = 'running' | 'succeeded' | 'failed' | 'stopped' | 'denied';
|
|
116
|
+
|
|
117
|
+
export type StepOutcome = 'ok' | 'fallback' | 'failed' | 'approved' | 'denied';
|
|
118
|
+
|
|
119
|
+
/** One line of a run's audit trail. A step can have two (approved, then ok). */
|
|
120
|
+
export interface RunStepLog {
|
|
121
|
+
idx: number;
|
|
122
|
+
stepId: string;
|
|
123
|
+
outcome: StepOutcome;
|
|
124
|
+
detail?: string | null;
|
|
125
|
+
at: number;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export interface Run {
|
|
129
|
+
id: string;
|
|
130
|
+
skillId: string;
|
|
131
|
+
skillName: string;
|
|
132
|
+
/** `manual` or `routine`. */
|
|
133
|
+
trigger: string;
|
|
134
|
+
status: RunStatus;
|
|
135
|
+
startedAt: number;
|
|
136
|
+
endedAt?: number | null;
|
|
137
|
+
error?: string | null;
|
|
138
|
+
steps: RunStepLog[];
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* One adaptation of the private model on experience: what it learned and how the fit
|
|
143
|
+
* went. Measured when the adaptation ran, never estimated afterwards.
|
|
144
|
+
*/
|
|
145
|
+
export interface Adaptation {
|
|
146
|
+
/** N of the `+expN` version this adaptation wrote. */
|
|
147
|
+
index: number;
|
|
148
|
+
version: string;
|
|
149
|
+
/** Episode and skill ids it learned. */
|
|
150
|
+
learned: string[];
|
|
151
|
+
passes: number;
|
|
152
|
+
/** Mean fit loss over the passes. */
|
|
153
|
+
loss: number;
|
|
154
|
+
at: number;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** A short random id — unique enough for one person's store. */
|
|
158
|
+
export function newExperienceId(): string {
|
|
159
|
+
const bytes = new Uint8Array(8);
|
|
160
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
161
|
+
return Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');
|
|
162
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -35,7 +35,9 @@ export type {
|
|
|
35
35
|
// ── Evermind (the .evermind artifact + the shared learning pipeline) ───────────
|
|
36
36
|
export {
|
|
37
37
|
EvermindModelPackage,
|
|
38
|
+
PACKAGE_HEADER_BYTES,
|
|
38
39
|
adaptAndDiff,
|
|
40
|
+
adaptPackage,
|
|
39
41
|
EVERMIND_ADAPT_MAX_CHARS,
|
|
40
42
|
parseDeltaLearnPayload,
|
|
41
43
|
buildDeltaLearnPayload,
|
|
@@ -53,6 +55,7 @@ export type {
|
|
|
53
55
|
PackageMeta,
|
|
54
56
|
ValidationResult,
|
|
55
57
|
AdaptResult,
|
|
58
|
+
AdaptedPackage,
|
|
56
59
|
AdaptOptions,
|
|
57
60
|
AdaptTokenizer,
|
|
58
61
|
DeltaLearnPayload,
|
|
@@ -363,3 +366,8 @@ export type {
|
|
|
363
366
|
} from './limbic/index.js';
|
|
364
367
|
|
|
365
368
|
export { LIMBIC_AFFECT_WGSL } from './kernels/limbic_affect.js';
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
// ── Experience (demonstrations, Train Once skills, runs) ─────────────────────
|
|
372
|
+
|
|
373
|
+
export * from './experience/index.js';
|