@moikapy/origen 0.3.1 → 0.4.1
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/adapter.d.ts +204 -0
- package/dist/adapter.js +21 -0
- package/dist/adapter.js.map +1 -0
- package/dist/chunk-ECRY7XDR.js +109 -0
- package/dist/chunk-ECRY7XDR.js.map +1 -0
- package/dist/chunk-TECUAB3E.js +296 -0
- package/dist/chunk-TECUAB3E.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +130 -0
- package/dist/index.js.map +1 -0
- package/dist/models.d.ts +34 -0
- package/dist/models.js +21 -0
- package/dist/models.js.map +1 -0
- package/dist/soul.d.ts +122 -0
- package/{src/soul.ts → dist/soul.js} +92 -268
- package/dist/soul.js.map +1 -0
- package/package.json +27 -9
- package/src/adapter.ts +0 -395
- package/src/agent.ts +0 -237
- package/src/index.ts +0 -58
- package/src/models.ts +0 -143
- package/src/types.ts +0 -59
|
@@ -1,23 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
* Implements Soul.md Standard (RFC-1, v1.0.0-rc1):
|
|
5
|
-
* https://github.com/rokoss21/soul.md
|
|
6
|
-
*
|
|
7
|
-
* Parses a portable, provider-agnostic persona definition and produces
|
|
8
|
-
* a system prompt, runtime config, and profile overlays for Origen.
|
|
9
|
-
*
|
|
10
|
-
* Usage:
|
|
11
|
-
* import { Soul, loadSoul } from "@moikapy/origen/soul";
|
|
12
|
-
*
|
|
13
|
-
* const soul = loadSoul(soulMdContent);
|
|
14
|
-
* const systemPrompt = soul.buildPrompt();
|
|
15
|
-
* const profile = soul.selectProfile("concise");
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
// ── YAML front matter parser ──────────────────────────────────────────
|
|
19
|
-
|
|
20
|
-
function parseYamlFrontMatter(content: string): { frontMatter: Record<string, unknown>; body: string } {
|
|
1
|
+
// src/soul.ts
|
|
2
|
+
function parseYamlFrontMatter(content) {
|
|
21
3
|
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)?$/);
|
|
22
4
|
if (!match) {
|
|
23
5
|
return { frontMatter: {}, body: content };
|
|
@@ -27,261 +9,121 @@ function parseYamlFrontMatter(content: string): { frontMatter: Record<string, un
|
|
|
27
9
|
const frontMatter = parseSoulYaml(rawYaml);
|
|
28
10
|
return { frontMatter, body: markdownBody };
|
|
29
11
|
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Minimal YAML parser for Soul.md front matter.
|
|
33
|
-
* Handles maps, sequences, inline values, and quoted strings.
|
|
34
|
-
* Does NOT support anchors, aliases, merge keys, or complex types.
|
|
35
|
-
*/
|
|
36
|
-
function parseSoulYaml(raw: string): Record<string, unknown> {
|
|
12
|
+
function parseSoulYaml(raw) {
|
|
37
13
|
const lines = raw.split("\n").map((l) => l.replace(/\r$/, ""));
|
|
38
14
|
const { result } = parseBlock(lines, 0, 0);
|
|
39
15
|
return result;
|
|
40
16
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
result: Record<string, unknown>;
|
|
44
|
-
nextLine: number;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function parseBlock(lines: string[], start: number, baseIndent: number): BlockResult {
|
|
48
|
-
const result: Record<string, unknown> = {};
|
|
17
|
+
function parseBlock(lines, start, baseIndent) {
|
|
18
|
+
const result = {};
|
|
49
19
|
let i = start;
|
|
50
|
-
|
|
51
20
|
while (i < lines.length) {
|
|
52
21
|
const line = lines[i];
|
|
53
22
|
const indent = lineLengths(line).indent;
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
23
|
+
if (line.trim() === "") {
|
|
24
|
+
i++;
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
if (indent < baseIndent) break;
|
|
58
28
|
const trimmed = line.trim();
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
// key: value
|
|
29
|
+
if (trimmed.startsWith("- ")) {
|
|
30
|
+
i++;
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
64
33
|
const kvMatch = trimmed.match(/^([\w][\w.-]*):\s*(.*)$/);
|
|
65
|
-
if (!kvMatch) {
|
|
66
|
-
|
|
34
|
+
if (!kvMatch) {
|
|
35
|
+
i++;
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
67
38
|
const key = kvMatch[1];
|
|
68
39
|
const inlineVal = kvMatch[2].trim();
|
|
69
|
-
|
|
70
40
|
if (inlineVal === "") {
|
|
71
|
-
// Value continues on next lines
|
|
72
41
|
const nextLine = i + 1 < lines.length ? lines[i + 1] : "";
|
|
73
42
|
const nextIndent = nextLine.trim() === "" ? 0 : lineLengths(nextLine).indent;
|
|
74
43
|
const nextTrimmed = nextLine.trim();
|
|
75
|
-
|
|
76
44
|
if (nextIndent > indent) {
|
|
77
45
|
if (nextTrimmed.startsWith("- ")) {
|
|
78
|
-
// It's a list block
|
|
79
46
|
const { items, nextLine: afterList } = parseList(lines, i + 1, nextIndent);
|
|
80
47
|
result[key] = items;
|
|
81
48
|
i = afterList;
|
|
82
49
|
} else {
|
|
83
|
-
// It's a nested map block
|
|
84
50
|
const { result: nested, nextLine: afterNested } = parseBlock(lines, i + 1, nextIndent);
|
|
85
51
|
result[key] = nested;
|
|
86
52
|
i = afterNested;
|
|
87
53
|
}
|
|
88
54
|
} else {
|
|
89
|
-
// Empty value
|
|
90
55
|
result[key] = null;
|
|
91
56
|
i++;
|
|
92
57
|
}
|
|
93
58
|
} else {
|
|
94
|
-
// Inline value
|
|
95
59
|
result[key] = parseScalar(inlineVal);
|
|
96
60
|
i++;
|
|
97
61
|
}
|
|
98
62
|
}
|
|
99
|
-
|
|
100
63
|
return { result, nextLine: i };
|
|
101
64
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
items: unknown[];
|
|
105
|
-
nextLine: number;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function parseList(lines: string[], start: number, baseIndent: number): ListResult {
|
|
109
|
-
const items: unknown[] = [];
|
|
65
|
+
function parseList(lines, start, baseIndent) {
|
|
66
|
+
const items = [];
|
|
110
67
|
let i = start;
|
|
111
|
-
|
|
112
68
|
while (i < lines.length) {
|
|
113
69
|
const line = lines[i];
|
|
114
70
|
const indent = lineLengths(line).indent;
|
|
115
|
-
|
|
116
|
-
|
|
71
|
+
if (line.trim() === "") {
|
|
72
|
+
i++;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
117
75
|
if (indent < baseIndent) break;
|
|
118
76
|
if (!line.trim().startsWith("- ")) break;
|
|
119
|
-
|
|
120
77
|
const value = line.trim().slice(2).trim();
|
|
121
78
|
items.push(parseScalar(value));
|
|
122
79
|
i++;
|
|
123
80
|
}
|
|
124
|
-
|
|
125
81
|
return { items, nextLine: i };
|
|
126
82
|
}
|
|
127
|
-
|
|
128
|
-
function parseScalar(value: string): unknown {
|
|
83
|
+
function parseScalar(value) {
|
|
129
84
|
if (value === "null" || value === "~") return null;
|
|
130
85
|
if (value === "true") return true;
|
|
131
86
|
if (value === "false") return false;
|
|
132
|
-
|
|
133
|
-
// Quoted string
|
|
134
|
-
if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
|
|
87
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
135
88
|
return value.slice(1, -1);
|
|
136
89
|
}
|
|
137
|
-
|
|
138
|
-
// Number
|
|
139
90
|
if (/^-?\d+$/.test(value)) return parseInt(value, 10);
|
|
140
91
|
if (/^-?\d+\.\d+$/.test(value)) return parseFloat(value);
|
|
141
|
-
|
|
142
|
-
// Inline list [a, b, c]
|
|
143
92
|
if (value.startsWith("[") && value.endsWith("]")) {
|
|
144
|
-
return value
|
|
145
|
-
.slice(1, -1)
|
|
146
|
-
.split(",")
|
|
147
|
-
.map((s) => parseScalar(s.trim()));
|
|
93
|
+
return value.slice(1, -1).split(",").map((s) => parseScalar(s.trim()));
|
|
148
94
|
}
|
|
149
|
-
|
|
150
95
|
return value;
|
|
151
96
|
}
|
|
152
|
-
|
|
153
|
-
function lineLengths(line: string): { indent: number; content: string } {
|
|
97
|
+
function lineLengths(line) {
|
|
154
98
|
const match = line.match(/^(\s*)(.*)$/);
|
|
155
99
|
const indent = match ? match[1].length : 0;
|
|
156
100
|
const content = match ? match[2] : "";
|
|
157
101
|
return { indent, content };
|
|
158
102
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
formality: number;
|
|
164
|
-
warmth: number;
|
|
165
|
-
verbosity: number;
|
|
166
|
-
jargon: number;
|
|
167
|
-
formatting: "minimal" | "plain" | "markdown";
|
|
168
|
-
banned_phrases?: string[];
|
|
169
|
-
preferred_phrases?: string[];
|
|
170
|
-
emoji_policy?: "never" | "rare" | "normal";
|
|
171
|
-
punctuation?: "normal" | "sparse";
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
export interface SoulInteraction {
|
|
175
|
-
clarifying_questions: "never" | "when_ambiguous" | "always";
|
|
176
|
-
uncertainty: "explicit" | "implicit" | "never";
|
|
177
|
-
disagreement: "soft" | "neutral" | "direct";
|
|
178
|
-
confirmations: "none" | "implicit" | "explicit";
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
export interface SoulCognition {
|
|
182
|
-
mode?: "analytical" | "creative" | "operational" | "exploratory" | "teaching" | "mixed";
|
|
183
|
-
depth?: number;
|
|
184
|
-
speed_vs_rigor?: number;
|
|
185
|
-
verification?: {
|
|
186
|
-
fact_checking?: "none" | "light" | "strict";
|
|
187
|
-
cross_validation?: number;
|
|
188
|
-
consistency_checks?: number;
|
|
189
|
-
assumption_tracking?: "none" | "implicit" | "explicit";
|
|
190
|
-
};
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
export interface SoulSafety {
|
|
194
|
-
refusal_style: "brief" | "explain" | "policy_cite";
|
|
195
|
-
privacy: "normal" | "strict";
|
|
196
|
-
speculation: "allow" | "mark" | "avoid";
|
|
197
|
-
no_fabrication?: boolean;
|
|
198
|
-
no_false_certainty?: boolean;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
export interface SoulActions {
|
|
202
|
-
when_to_use_tools: "avoid_tools" | "when_needed" | "prefer_tools";
|
|
203
|
-
explain_actions: "no" | "brief" | "full";
|
|
204
|
-
failover: "retry" | "alternative_method" | "ask_user";
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
export interface SoulConfig {
|
|
208
|
-
soul_spec?: string;
|
|
209
|
-
id: string;
|
|
210
|
-
name: string;
|
|
211
|
-
locale?: string;
|
|
212
|
-
version?: string;
|
|
213
|
-
description?: string;
|
|
214
|
-
composition?: {
|
|
215
|
-
extends?: string[];
|
|
216
|
-
mixins?: string[];
|
|
217
|
-
merge_policy?: string;
|
|
218
|
-
};
|
|
219
|
-
profiles?: string[];
|
|
220
|
-
profile_overrides?: Record<string, Record<string, unknown>>;
|
|
221
|
-
values?: {
|
|
222
|
-
priorities?: string[];
|
|
223
|
-
tradeoffs?: string[];
|
|
224
|
-
taboo?: string[];
|
|
225
|
-
};
|
|
226
|
-
identity?: {
|
|
227
|
-
role?: string;
|
|
228
|
-
archetype?: string;
|
|
229
|
-
domain_focus?: string[];
|
|
230
|
-
non_goals?: string[];
|
|
231
|
-
};
|
|
232
|
-
relationship?: {
|
|
233
|
-
stance?: "subordinate" | "peer" | "authoritative" | "adversarial";
|
|
234
|
-
user_model_default?: "novice" | "intermediate" | "expert" | "unknown";
|
|
235
|
-
trust_baseline?: number;
|
|
236
|
-
boundary_distance?: number;
|
|
237
|
-
};
|
|
238
|
-
voice?: Partial<SoulVoice>;
|
|
239
|
-
interaction?: Partial<SoulInteraction>;
|
|
240
|
-
cognition?: SoulCognition;
|
|
241
|
-
safety?: Partial<SoulSafety>;
|
|
242
|
-
actions?: SoulActions;
|
|
243
|
-
state?: {
|
|
244
|
-
base?: string;
|
|
245
|
-
states?: Record<string, Record<string, unknown>>;
|
|
246
|
-
triggers?: Array<{ if: string; shift_to: string; duration?: string }>;
|
|
247
|
-
};
|
|
248
|
-
examples?: Array<{ user: string; agent: string }>;
|
|
249
|
-
extensions?: Record<string, unknown>;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
// ── Soul class ─────────────────────────────────────────────────────────
|
|
253
|
-
|
|
254
|
-
export class Soul {
|
|
255
|
-
readonly config: SoulConfig;
|
|
256
|
-
readonly body: string;
|
|
257
|
-
|
|
258
|
-
constructor(config: SoulConfig, body: string) {
|
|
103
|
+
var Soul = class _Soul {
|
|
104
|
+
config;
|
|
105
|
+
body;
|
|
106
|
+
constructor(config, body) {
|
|
259
107
|
this.config = config;
|
|
260
108
|
this.body = body;
|
|
261
109
|
}
|
|
262
|
-
|
|
263
110
|
/** Select a profile and return a new Soul with that profile merged in. */
|
|
264
|
-
selectProfile(profileName
|
|
111
|
+
selectProfile(profileName) {
|
|
265
112
|
const profiles = this.config.profiles ?? ["default"];
|
|
266
113
|
const overrides = this.config.profile_overrides ?? {};
|
|
267
114
|
const profileOverrides = overrides[profileName];
|
|
268
|
-
|
|
269
115
|
if (!profiles.includes(profileName) || !profileOverrides) {
|
|
270
116
|
return this;
|
|
271
117
|
}
|
|
272
|
-
|
|
273
118
|
const merged = mergeDeep(
|
|
274
|
-
structuredClone(this.config)
|
|
275
|
-
profileOverrides
|
|
276
|
-
)
|
|
277
|
-
return new
|
|
119
|
+
structuredClone(this.config),
|
|
120
|
+
profileOverrides
|
|
121
|
+
);
|
|
122
|
+
return new _Soul(merged, this.body);
|
|
278
123
|
}
|
|
279
|
-
|
|
280
124
|
/** Build a system prompt from the Soul definition. */
|
|
281
|
-
buildPrompt()
|
|
282
|
-
const parts
|
|
283
|
-
|
|
284
|
-
// Identity
|
|
125
|
+
buildPrompt() {
|
|
126
|
+
const parts = [];
|
|
285
127
|
const identity = this.config.identity;
|
|
286
128
|
if (identity) {
|
|
287
129
|
const role = identity.role ?? this.config.name;
|
|
@@ -296,56 +138,54 @@ export class Soul {
|
|
|
296
138
|
} else {
|
|
297
139
|
parts.push(`You are ${this.config.name}.`);
|
|
298
140
|
}
|
|
299
|
-
|
|
300
|
-
// Relationship
|
|
301
141
|
const relationship = this.config.relationship;
|
|
302
142
|
if (relationship) {
|
|
303
143
|
if (relationship.stance) {
|
|
304
|
-
const stanceMap
|
|
144
|
+
const stanceMap = {
|
|
305
145
|
subordinate: "You serve the user's direction.",
|
|
306
146
|
peer: "You collaborate with the user as a partner.",
|
|
307
147
|
authoritative: "You provide expert guidance and direction.",
|
|
308
|
-
adversarial: "You challenge the user's assumptions to improve outcomes."
|
|
148
|
+
adversarial: "You challenge the user's assumptions to improve outcomes."
|
|
309
149
|
};
|
|
310
150
|
parts.push(stanceMap[relationship.stance] ?? "");
|
|
311
151
|
}
|
|
312
152
|
if (relationship.user_model_default) {
|
|
313
|
-
const userModelMap
|
|
153
|
+
const userModelMap = {
|
|
314
154
|
novice: "Assume the user is new to this domain. Explain terms and concepts.",
|
|
315
155
|
intermediate: "Assume moderate familiarity. Explain only when needed.",
|
|
316
156
|
expert: "Be concise. The user knows the domain well.",
|
|
317
|
-
unknown: "Adapt your explanation depth to the user's apparent knowledge level."
|
|
157
|
+
unknown: "Adapt your explanation depth to the user's apparent knowledge level."
|
|
318
158
|
};
|
|
319
159
|
parts.push(userModelMap[relationship.user_model_default] ?? "");
|
|
320
160
|
}
|
|
321
161
|
}
|
|
322
|
-
|
|
323
|
-
// Values
|
|
324
162
|
const values = this.config.values;
|
|
325
163
|
if (values?.priorities?.length) {
|
|
326
|
-
parts.push(
|
|
164
|
+
parts.push(`
|
|
165
|
+
## Priorities (in order)
|
|
166
|
+
${values.priorities.map((p, i) => `${i + 1}. ${p}`).join("\n")}`);
|
|
327
167
|
}
|
|
328
168
|
if (values?.taboo?.length) {
|
|
329
|
-
parts.push(
|
|
169
|
+
parts.push(`
|
|
170
|
+
## Forbidden patterns
|
|
171
|
+
${values.taboo.map((t) => `- ${t}`).join("\n")}`);
|
|
330
172
|
}
|
|
331
|
-
|
|
332
|
-
// Voice
|
|
333
173
|
const voice = this.config.voice;
|
|
334
174
|
if (voice) {
|
|
335
|
-
const voiceParts
|
|
336
|
-
if (voice.formality !==
|
|
175
|
+
const voiceParts = ["\n## Voice & Style"];
|
|
176
|
+
if (voice.formality !== void 0) {
|
|
337
177
|
const level = voice.formality <= 30 ? "very casual" : voice.formality <= 60 ? "moderately formal" : voice.formality <= 80 ? "professional" : "highly formal";
|
|
338
178
|
voiceParts.push(`Formality: ${level} (${voice.formality}/100).`);
|
|
339
179
|
}
|
|
340
|
-
if (voice.warmth !==
|
|
180
|
+
if (voice.warmth !== void 0) {
|
|
341
181
|
const level = voice.warmth <= 30 ? "cold/detached" : voice.warmth <= 60 ? "neutral" : voice.warmth <= 80 ? "warm and approachable" : "very friendly and encouraging";
|
|
342
182
|
voiceParts.push(`Tone: ${level} (${voice.warmth}/100).`);
|
|
343
183
|
}
|
|
344
|
-
if (voice.verbosity !==
|
|
184
|
+
if (voice.verbosity !== void 0) {
|
|
345
185
|
const level = voice.verbosity <= 25 ? "extremely concise" : voice.verbosity <= 50 ? "concise" : voice.verbosity <= 75 ? "moderate length" : "thorough and detailed";
|
|
346
186
|
voiceParts.push(`Brevity: ${level} (${voice.verbosity}/100).`);
|
|
347
187
|
}
|
|
348
|
-
if (voice.jargon !==
|
|
188
|
+
if (voice.jargon !== void 0) {
|
|
349
189
|
const level = voice.jargon <= 30 ? "use plain language" : voice.jargon <= 60 ? "use moderate technical terms" : "use domain-specific terminology freely";
|
|
350
190
|
voiceParts.push(`Jargon: ${level} (${voice.jargon}/100).`);
|
|
351
191
|
}
|
|
@@ -355,32 +195,30 @@ export class Soul {
|
|
|
355
195
|
if (voice.emoji_policy && voice.emoji_policy !== "rare") voiceParts.push(`Emoji usage: ${voice.emoji_policy}.`);
|
|
356
196
|
parts.push(voiceParts.join(" "));
|
|
357
197
|
}
|
|
358
|
-
|
|
359
|
-
// Interaction
|
|
360
198
|
const interaction = this.config.interaction;
|
|
361
199
|
if (interaction) {
|
|
362
|
-
const interactionParts
|
|
200
|
+
const interactionParts = ["\n## Interaction Policy"];
|
|
363
201
|
if (interaction.clarifying_questions) {
|
|
364
|
-
const qMap
|
|
202
|
+
const qMap = {
|
|
365
203
|
never: "Never ask clarifying questions. Make reasonable assumptions.",
|
|
366
204
|
when_ambiguous: "Ask clarifying questions only when the query is ambiguous.",
|
|
367
|
-
always: "Always confirm your understanding before responding."
|
|
205
|
+
always: "Always confirm your understanding before responding."
|
|
368
206
|
};
|
|
369
207
|
interactionParts.push(qMap[interaction.clarifying_questions] ?? "");
|
|
370
208
|
}
|
|
371
209
|
if (interaction.uncertainty) {
|
|
372
|
-
const uMap
|
|
210
|
+
const uMap = {
|
|
373
211
|
explicit: "Explicitly mark uncertain information. Say when you're not sure.",
|
|
374
212
|
implicit: "Use hedging language (might, possibly, could) for uncertain claims.",
|
|
375
|
-
never: "Never express uncertainty. State your best answer confidently."
|
|
213
|
+
never: "Never express uncertainty. State your best answer confidently."
|
|
376
214
|
};
|
|
377
215
|
interactionParts.push(uMap[interaction.uncertainty] ?? "");
|
|
378
216
|
}
|
|
379
217
|
if (interaction.disagreement) {
|
|
380
|
-
const dMap
|
|
218
|
+
const dMap = {
|
|
381
219
|
soft: "Disagree gently. Acknowledge the user's perspective first.",
|
|
382
220
|
neutral: "State disagreements directly but politely.",
|
|
383
|
-
direct: "Challenge incorrect views directly. Don't soften disagreements."
|
|
221
|
+
direct: "Challenge incorrect views directly. Don't soften disagreements."
|
|
384
222
|
};
|
|
385
223
|
interactionParts.push(dMap[interaction.disagreement] ?? "");
|
|
386
224
|
}
|
|
@@ -389,50 +227,46 @@ export class Soul {
|
|
|
389
227
|
}
|
|
390
228
|
parts.push(interactionParts.join(" "));
|
|
391
229
|
}
|
|
392
|
-
|
|
393
|
-
// Cognition
|
|
394
230
|
const cognition = this.config.cognition;
|
|
395
231
|
if (cognition) {
|
|
396
|
-
const cogParts
|
|
232
|
+
const cogParts = ["\n## Cognition"];
|
|
397
233
|
if (cognition.mode) {
|
|
398
|
-
const modeMap
|
|
234
|
+
const modeMap = {
|
|
399
235
|
analytical: "Think analytically. Break problems down, examine evidence, reason step by step.",
|
|
400
236
|
creative: "Think creatively. Generate novel ideas, make unexpected connections.",
|
|
401
237
|
operational: "Focus on execution. Prioritize working solutions over theory.",
|
|
402
238
|
exploratory: "Explore broadly. Consider many angles before committing to an answer.",
|
|
403
239
|
teaching: "Teach and explain. Build understanding progressively from basics.",
|
|
404
|
-
mixed: "Adapt your thinking mode to the task at hand."
|
|
240
|
+
mixed: "Adapt your thinking mode to the task at hand."
|
|
405
241
|
};
|
|
406
242
|
cogParts.push(modeMap[cognition.mode] ?? "");
|
|
407
243
|
}
|
|
408
244
|
if (cognition.verification?.fact_checking) {
|
|
409
|
-
const fcMap
|
|
245
|
+
const fcMap = {
|
|
410
246
|
none: "No explicit fact-checking.",
|
|
411
247
|
light: "Verify key claims before stating them.",
|
|
412
|
-
strict: "Always verify claims. Never state unverified information as fact."
|
|
248
|
+
strict: "Always verify claims. Never state unverified information as fact."
|
|
413
249
|
};
|
|
414
250
|
cogParts.push(fcMap[cognition.verification.fact_checking] ?? "");
|
|
415
251
|
}
|
|
416
252
|
parts.push(cogParts.join(" "));
|
|
417
253
|
}
|
|
418
|
-
|
|
419
|
-
// Safety
|
|
420
254
|
const safety = this.config.safety;
|
|
421
255
|
if (safety) {
|
|
422
|
-
const safetyParts
|
|
256
|
+
const safetyParts = ["\n## Safety"];
|
|
423
257
|
if (safety.speculation) {
|
|
424
|
-
const specMap
|
|
258
|
+
const specMap = {
|
|
425
259
|
allow: "You may speculate freely.",
|
|
426
260
|
mark: "Mark speculative content clearly (e.g., 'I believe', 'likely', 'possibly').",
|
|
427
|
-
avoid: "Do not speculate. Only state what you can verify."
|
|
261
|
+
avoid: "Do not speculate. Only state what you can verify."
|
|
428
262
|
};
|
|
429
263
|
safetyParts.push(specMap[safety.speculation] ?? "");
|
|
430
264
|
}
|
|
431
265
|
if (safety.refusal_style) {
|
|
432
|
-
const refMap
|
|
266
|
+
const refMap = {
|
|
433
267
|
brief: "Refuse briefly. No lectures.",
|
|
434
268
|
explain: "Explain why you're refusing when you decline a request.",
|
|
435
|
-
policy_cite: "Cite specific policies when refusing requests."
|
|
269
|
+
policy_cite: "Cite specific policies when refusing requests."
|
|
436
270
|
};
|
|
437
271
|
safetyParts.push(refMap[safety.refusal_style] ?? "");
|
|
438
272
|
}
|
|
@@ -440,15 +274,13 @@ export class Soul {
|
|
|
440
274
|
if (safety.no_false_certainty) safetyParts.push("Never present uncertain information as certain.");
|
|
441
275
|
parts.push(safetyParts.join(" "));
|
|
442
276
|
}
|
|
443
|
-
|
|
444
|
-
// Actions
|
|
445
277
|
const actions = this.config.actions;
|
|
446
278
|
if (actions) {
|
|
447
|
-
const actParts
|
|
448
|
-
const toolMap
|
|
279
|
+
const actParts = ["\n## Actions"];
|
|
280
|
+
const toolMap = {
|
|
449
281
|
avoid_tools: "Minimize tool use. Answer from knowledge when possible.",
|
|
450
282
|
when_needed: "Use tools when they would improve your answer.",
|
|
451
|
-
prefer_tools: "Proactively use available tools. Always verify with tools rather than memory."
|
|
283
|
+
prefer_tools: "Proactively use available tools. Always verify with tools rather than memory."
|
|
452
284
|
};
|
|
453
285
|
actParts.push(toolMap[actions.when_to_use_tools] ?? "");
|
|
454
286
|
if (actions.explain_actions === "brief" || actions.explain_actions === "full") {
|
|
@@ -456,53 +288,45 @@ export class Soul {
|
|
|
456
288
|
}
|
|
457
289
|
parts.push(actParts.join(" "));
|
|
458
290
|
}
|
|
459
|
-
|
|
460
|
-
// Markdown body
|
|
461
291
|
if (this.body.trim()) {
|
|
462
|
-
parts.push(
|
|
463
|
-
|
|
292
|
+
parts.push(`
|
|
293
|
+
## Additional Instructions
|
|
464
294
|
|
|
295
|
+
${this.body.trim()}`);
|
|
296
|
+
}
|
|
465
297
|
return parts.join("\n\n");
|
|
466
298
|
}
|
|
467
|
-
|
|
468
|
-
get defaultProfile(): string {
|
|
299
|
+
get defaultProfile() {
|
|
469
300
|
return this.config.profiles?.[0] ?? "default";
|
|
470
301
|
}
|
|
471
|
-
|
|
472
|
-
get profileNames(): string[] {
|
|
302
|
+
get profileNames() {
|
|
473
303
|
return this.config.profiles ?? ["default"];
|
|
474
304
|
}
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
// ── Public API ──────────────────────────────────────────────────────────
|
|
478
|
-
|
|
479
|
-
/** Parse a Soul.md string into a Soul instance. */
|
|
480
|
-
export function loadSoul(content: string): Soul {
|
|
305
|
+
};
|
|
306
|
+
function loadSoul(content) {
|
|
481
307
|
const { frontMatter, body } = parseYamlFrontMatter(content);
|
|
482
|
-
const config = frontMatter
|
|
483
|
-
|
|
308
|
+
const config = frontMatter;
|
|
484
309
|
if (!config.id) throw new Error("Soul.md missing required field: id");
|
|
485
310
|
if (!config.name) throw new Error("Soul.md missing required field: name");
|
|
486
|
-
|
|
487
311
|
return new Soul(config, body);
|
|
488
312
|
}
|
|
489
|
-
|
|
490
|
-
/** Deep merge (Standard Merge semantics from Soul.md spec). */
|
|
491
|
-
function mergeDeep(base: Record<string, unknown>, overlay: Record<string, unknown>): Record<string, unknown> {
|
|
313
|
+
function mergeDeep(base, overlay) {
|
|
492
314
|
const result = { ...base };
|
|
493
315
|
for (const key of Object.keys(overlay)) {
|
|
494
316
|
const baseVal = result[key];
|
|
495
317
|
const overVal = overlay[key];
|
|
496
318
|
if (overVal === null) {
|
|
497
319
|
result[key] = null;
|
|
498
|
-
} else if (
|
|
499
|
-
|
|
500
|
-
typeof overVal === "object" && overVal !== null && !Array.isArray(overVal)
|
|
501
|
-
) {
|
|
502
|
-
result[key] = mergeDeep(baseVal as Record<string, unknown>, overVal as Record<string, unknown>);
|
|
320
|
+
} else if (typeof baseVal === "object" && baseVal !== null && !Array.isArray(baseVal) && typeof overVal === "object" && overVal !== null && !Array.isArray(overVal)) {
|
|
321
|
+
result[key] = mergeDeep(baseVal, overVal);
|
|
503
322
|
} else {
|
|
504
323
|
result[key] = overVal;
|
|
505
324
|
}
|
|
506
325
|
}
|
|
507
326
|
return result;
|
|
508
|
-
}
|
|
327
|
+
}
|
|
328
|
+
export {
|
|
329
|
+
Soul,
|
|
330
|
+
loadSoul
|
|
331
|
+
};
|
|
332
|
+
//# sourceMappingURL=soul.js.map
|
package/dist/soul.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/soul.ts"],"sourcesContent":["/**\n * Soul.md parser for Origen agent personas.\n *\n * Implements Soul.md Standard (RFC-1, v1.0.0-rc1):\n * https://github.com/rokoss21/soul.md\n *\n * Parses a portable, provider-agnostic persona definition and produces\n * a system prompt, runtime config, and profile overlays for Origen.\n *\n * Usage:\n * import { Soul, loadSoul } from \"@moikapy/origen/soul\";\n *\n * const soul = loadSoul(soulMdContent);\n * const systemPrompt = soul.buildPrompt();\n * const profile = soul.selectProfile(\"concise\");\n */\n\n// ── YAML front matter parser ──────────────────────────────────────────\n\nfunction parseYamlFrontMatter(content: string): { frontMatter: Record<string, unknown>; body: string } {\n const match = content.match(/^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n?([\\s\\S]*)?$/);\n if (!match) {\n return { frontMatter: {}, body: content };\n }\n const rawYaml = match[1];\n const markdownBody = match[2] ?? \"\";\n const frontMatter = parseSoulYaml(rawYaml);\n return { frontMatter, body: markdownBody };\n}\n\n/**\n * Minimal YAML parser for Soul.md front matter.\n * Handles maps, sequences, inline values, and quoted strings.\n * Does NOT support anchors, aliases, merge keys, or complex types.\n */\nfunction parseSoulYaml(raw: string): Record<string, unknown> {\n const lines = raw.split(\"\\n\").map((l) => l.replace(/\\r$/, \"\"));\n const { result } = parseBlock(lines, 0, 0);\n return result;\n}\n\ninterface BlockResult {\n result: Record<string, unknown>;\n nextLine: number;\n}\n\nfunction parseBlock(lines: string[], start: number, baseIndent: number): BlockResult {\n const result: Record<string, unknown> = {};\n let i = start;\n\n while (i < lines.length) {\n const line = lines[i];\n const indent = lineLengths(line).indent;\n\n if (line.trim() === \"\") { i++; continue; }\n if (indent < baseIndent) break; // End of this block\n\n const trimmed = line.trim();\n\n // Skip list items at top level of a map block (shouldn't happen, but guard)\n if (trimmed.startsWith(\"- \")) { i++; continue; }\n\n // key: value\n const kvMatch = trimmed.match(/^([\\w][\\w.-]*):\\s*(.*)$/);\n if (!kvMatch) { i++; continue; }\n\n const key = kvMatch[1];\n const inlineVal = kvMatch[2].trim();\n\n if (inlineVal === \"\") {\n // Value continues on next lines\n const nextLine = i + 1 < lines.length ? lines[i + 1] : \"\";\n const nextIndent = nextLine.trim() === \"\" ? 0 : lineLengths(nextLine).indent;\n const nextTrimmed = nextLine.trim();\n\n if (nextIndent > indent) {\n if (nextTrimmed.startsWith(\"- \")) {\n // It's a list block\n const { items, nextLine: afterList } = parseList(lines, i + 1, nextIndent);\n result[key] = items;\n i = afterList;\n } else {\n // It's a nested map block\n const { result: nested, nextLine: afterNested } = parseBlock(lines, i + 1, nextIndent);\n result[key] = nested;\n i = afterNested;\n }\n } else {\n // Empty value\n result[key] = null;\n i++;\n }\n } else {\n // Inline value\n result[key] = parseScalar(inlineVal);\n i++;\n }\n }\n\n return { result, nextLine: i };\n}\n\ninterface ListResult {\n items: unknown[];\n nextLine: number;\n}\n\nfunction parseList(lines: string[], start: number, baseIndent: number): ListResult {\n const items: unknown[] = [];\n let i = start;\n\n while (i < lines.length) {\n const line = lines[i];\n const indent = lineLengths(line).indent;\n\n if (line.trim() === \"\") { i++; continue; }\n if (indent < baseIndent) break;\n if (!line.trim().startsWith(\"- \")) break;\n\n const value = line.trim().slice(2).trim();\n items.push(parseScalar(value));\n i++;\n }\n\n return { items, nextLine: i };\n}\n\nfunction parseScalar(value: string): unknown {\n if (value === \"null\" || value === \"~\") return null;\n if (value === \"true\") return true;\n if (value === \"false\") return false;\n\n // Quoted string\n if ((value.startsWith('\"') && value.endsWith('\"')) || (value.startsWith(\"'\") && value.endsWith(\"'\"))) {\n return value.slice(1, -1);\n }\n\n // Number\n if (/^-?\\d+$/.test(value)) return parseInt(value, 10);\n if (/^-?\\d+\\.\\d+$/.test(value)) return parseFloat(value);\n\n // Inline list [a, b, c]\n if (value.startsWith(\"[\") && value.endsWith(\"]\")) {\n return value\n .slice(1, -1)\n .split(\",\")\n .map((s) => parseScalar(s.trim()));\n }\n\n return value;\n}\n\nfunction lineLengths(line: string): { indent: number; content: string } {\n const match = line.match(/^(\\s*)(.*)$/);\n const indent = match ? match[1].length : 0;\n const content = match ? match[2] : \"\";\n return { indent, content };\n}\n\n// ── Soul types ─────────────────────────────────────────────────────────\n\nexport interface SoulVoice {\n formality: number;\n warmth: number;\n verbosity: number;\n jargon: number;\n formatting: \"minimal\" | \"plain\" | \"markdown\";\n banned_phrases?: string[];\n preferred_phrases?: string[];\n emoji_policy?: \"never\" | \"rare\" | \"normal\";\n punctuation?: \"normal\" | \"sparse\";\n}\n\nexport interface SoulInteraction {\n clarifying_questions: \"never\" | \"when_ambiguous\" | \"always\";\n uncertainty: \"explicit\" | \"implicit\" | \"never\";\n disagreement: \"soft\" | \"neutral\" | \"direct\";\n confirmations: \"none\" | \"implicit\" | \"explicit\";\n}\n\nexport interface SoulCognition {\n mode?: \"analytical\" | \"creative\" | \"operational\" | \"exploratory\" | \"teaching\" | \"mixed\";\n depth?: number;\n speed_vs_rigor?: number;\n verification?: {\n fact_checking?: \"none\" | \"light\" | \"strict\";\n cross_validation?: number;\n consistency_checks?: number;\n assumption_tracking?: \"none\" | \"implicit\" | \"explicit\";\n };\n}\n\nexport interface SoulSafety {\n refusal_style: \"brief\" | \"explain\" | \"policy_cite\";\n privacy: \"normal\" | \"strict\";\n speculation: \"allow\" | \"mark\" | \"avoid\";\n no_fabrication?: boolean;\n no_false_certainty?: boolean;\n}\n\nexport interface SoulActions {\n when_to_use_tools: \"avoid_tools\" | \"when_needed\" | \"prefer_tools\";\n explain_actions: \"no\" | \"brief\" | \"full\";\n failover: \"retry\" | \"alternative_method\" | \"ask_user\";\n}\n\nexport interface SoulConfig {\n soul_spec?: string;\n id: string;\n name: string;\n locale?: string;\n version?: string;\n description?: string;\n composition?: {\n extends?: string[];\n mixins?: string[];\n merge_policy?: string;\n };\n profiles?: string[];\n profile_overrides?: Record<string, Record<string, unknown>>;\n values?: {\n priorities?: string[];\n tradeoffs?: string[];\n taboo?: string[];\n };\n identity?: {\n role?: string;\n archetype?: string;\n domain_focus?: string[];\n non_goals?: string[];\n };\n relationship?: {\n stance?: \"subordinate\" | \"peer\" | \"authoritative\" | \"adversarial\";\n user_model_default?: \"novice\" | \"intermediate\" | \"expert\" | \"unknown\";\n trust_baseline?: number;\n boundary_distance?: number;\n };\n voice?: Partial<SoulVoice>;\n interaction?: Partial<SoulInteraction>;\n cognition?: SoulCognition;\n safety?: Partial<SoulSafety>;\n actions?: SoulActions;\n state?: {\n base?: string;\n states?: Record<string, Record<string, unknown>>;\n triggers?: Array<{ if: string; shift_to: string; duration?: string }>;\n };\n examples?: Array<{ user: string; agent: string }>;\n extensions?: Record<string, unknown>;\n}\n\n// ── Soul class ─────────────────────────────────────────────────────────\n\nexport class Soul {\n readonly config: SoulConfig;\n readonly body: string;\n\n constructor(config: SoulConfig, body: string) {\n this.config = config;\n this.body = body;\n }\n\n /** Select a profile and return a new Soul with that profile merged in. */\n selectProfile(profileName: string): Soul {\n const profiles = this.config.profiles ?? [\"default\"];\n const overrides = this.config.profile_overrides ?? {};\n const profileOverrides = overrides[profileName];\n\n if (!profiles.includes(profileName) || !profileOverrides) {\n return this;\n }\n\n const merged = mergeDeep(\n structuredClone(this.config) as unknown as Record<string, unknown>,\n profileOverrides as Record<string, unknown>\n ) as unknown as SoulConfig;\n return new Soul(merged, this.body);\n }\n\n /** Build a system prompt from the Soul definition. */\n buildPrompt(): string {\n const parts: string[] = [];\n\n // Identity\n const identity = this.config.identity;\n if (identity) {\n const role = identity.role ?? this.config.name;\n const archetype = identity.archetype;\n parts.push(`You are ${this.config.name}${archetype ? `, a ${archetype}` : \"\"}${role ? `. Role: ${role}` : \"\"}.`);\n if (identity.domain_focus?.length) {\n parts.push(`Domain expertise: ${identity.domain_focus.join(\", \")}.`);\n }\n if (identity.non_goals?.length) {\n parts.push(`Non-goals: ${identity.non_goals.join(\", \")}.`);\n }\n } else {\n parts.push(`You are ${this.config.name}.`);\n }\n\n // Relationship\n const relationship = this.config.relationship;\n if (relationship) {\n if (relationship.stance) {\n const stanceMap: Record<string, string> = {\n subordinate: \"You serve the user's direction.\",\n peer: \"You collaborate with the user as a partner.\",\n authoritative: \"You provide expert guidance and direction.\",\n adversarial: \"You challenge the user's assumptions to improve outcomes.\",\n };\n parts.push(stanceMap[relationship.stance] ?? \"\");\n }\n if (relationship.user_model_default) {\n const userModelMap: Record<string, string> = {\n novice: \"Assume the user is new to this domain. Explain terms and concepts.\",\n intermediate: \"Assume moderate familiarity. Explain only when needed.\",\n expert: \"Be concise. The user knows the domain well.\",\n unknown: \"Adapt your explanation depth to the user's apparent knowledge level.\",\n };\n parts.push(userModelMap[relationship.user_model_default] ?? \"\");\n }\n }\n\n // Values\n const values = this.config.values;\n if (values?.priorities?.length) {\n parts.push(`\\n## Priorities (in order)\\n${values.priorities.map((p, i) => `${i + 1}. ${p}`).join(\"\\n\")}`);\n }\n if (values?.taboo?.length) {\n parts.push(`\\n## Forbidden patterns\\n${values.taboo.map((t) => `- ${t}`).join(\"\\n\")}`);\n }\n\n // Voice\n const voice = this.config.voice;\n if (voice) {\n const voiceParts: string[] = [\"\\n## Voice & Style\"];\n if (voice.formality !== undefined) {\n const level = voice.formality <= 30 ? \"very casual\" : voice.formality <= 60 ? \"moderately formal\" : voice.formality <= 80 ? \"professional\" : \"highly formal\";\n voiceParts.push(`Formality: ${level} (${voice.formality}/100).`);\n }\n if (voice.warmth !== undefined) {\n const level = voice.warmth <= 30 ? \"cold/detached\" : voice.warmth <= 60 ? \"neutral\" : voice.warmth <= 80 ? \"warm and approachable\" : \"very friendly and encouraging\";\n voiceParts.push(`Tone: ${level} (${voice.warmth}/100).`);\n }\n if (voice.verbosity !== undefined) {\n const level = voice.verbosity <= 25 ? \"extremely concise\" : voice.verbosity <= 50 ? \"concise\" : voice.verbosity <= 75 ? \"moderate length\" : \"thorough and detailed\";\n voiceParts.push(`Brevity: ${level} (${voice.verbosity}/100).`);\n }\n if (voice.jargon !== undefined) {\n const level = voice.jargon <= 30 ? \"use plain language\" : voice.jargon <= 60 ? \"use moderate technical terms\" : \"use domain-specific terminology freely\";\n voiceParts.push(`Jargon: ${level} (${voice.jargon}/100).`);\n }\n if (voice.formatting) voiceParts.push(`Formatting: ${voice.formatting}.`);\n if (voice.banned_phrases?.length) voiceParts.push(`Never say: ${voice.banned_phrases.map((p) => `\"${p}\"`).join(\", \")}.`);\n if (voice.preferred_phrases?.length) voiceParts.push(`Prefer: ${voice.preferred_phrases.map((p) => `\"${p}\"`).join(\", \")}.`);\n if (voice.emoji_policy && voice.emoji_policy !== \"rare\") voiceParts.push(`Emoji usage: ${voice.emoji_policy}.`);\n parts.push(voiceParts.join(\" \"));\n }\n\n // Interaction\n const interaction = this.config.interaction;\n if (interaction) {\n const interactionParts: string[] = [\"\\n## Interaction Policy\"];\n if (interaction.clarifying_questions) {\n const qMap: Record<string, string> = {\n never: \"Never ask clarifying questions. Make reasonable assumptions.\",\n when_ambiguous: \"Ask clarifying questions only when the query is ambiguous.\",\n always: \"Always confirm your understanding before responding.\",\n };\n interactionParts.push(qMap[interaction.clarifying_questions] ?? \"\");\n }\n if (interaction.uncertainty) {\n const uMap: Record<string, string> = {\n explicit: \"Explicitly mark uncertain information. Say when you're not sure.\",\n implicit: \"Use hedging language (might, possibly, could) for uncertain claims.\",\n never: \"Never express uncertainty. State your best answer confidently.\",\n };\n interactionParts.push(uMap[interaction.uncertainty] ?? \"\");\n }\n if (interaction.disagreement) {\n const dMap: Record<string, string> = {\n soft: \"Disagree gently. Acknowledge the user's perspective first.\",\n neutral: \"State disagreements directly but politely.\",\n direct: \"Challenge incorrect views directly. Don't soften disagreements.\",\n };\n interactionParts.push(dMap[interaction.disagreement] ?? \"\");\n }\n if (interaction.confirmations === \"none\") {\n interactionParts.push(\"Don't ask for confirmation before acting. Just do it.\");\n }\n parts.push(interactionParts.join(\" \"));\n }\n\n // Cognition\n const cognition = this.config.cognition;\n if (cognition) {\n const cogParts: string[] = [\"\\n## Cognition\"];\n if (cognition.mode) {\n const modeMap: Record<string, string> = {\n analytical: \"Think analytically. Break problems down, examine evidence, reason step by step.\",\n creative: \"Think creatively. Generate novel ideas, make unexpected connections.\",\n operational: \"Focus on execution. Prioritize working solutions over theory.\",\n exploratory: \"Explore broadly. Consider many angles before committing to an answer.\",\n teaching: \"Teach and explain. Build understanding progressively from basics.\",\n mixed: \"Adapt your thinking mode to the task at hand.\",\n };\n cogParts.push(modeMap[cognition.mode] ?? \"\");\n }\n if (cognition.verification?.fact_checking) {\n const fcMap: Record<string, string> = {\n none: \"No explicit fact-checking.\",\n light: \"Verify key claims before stating them.\",\n strict: \"Always verify claims. Never state unverified information as fact.\",\n };\n cogParts.push(fcMap[cognition.verification.fact_checking] ?? \"\");\n }\n parts.push(cogParts.join(\" \"));\n }\n\n // Safety\n const safety = this.config.safety;\n if (safety) {\n const safetyParts: string[] = [\"\\n## Safety\"];\n if (safety.speculation) {\n const specMap: Record<string, string> = {\n allow: \"You may speculate freely.\",\n mark: \"Mark speculative content clearly (e.g., 'I believe', 'likely', 'possibly').\",\n avoid: \"Do not speculate. Only state what you can verify.\",\n };\n safetyParts.push(specMap[safety.speculation] ?? \"\");\n }\n if (safety.refusal_style) {\n const refMap: Record<string, string> = {\n brief: \"Refuse briefly. No lectures.\",\n explain: \"Explain why you're refusing when you decline a request.\",\n policy_cite: \"Cite specific policies when refusing requests.\",\n };\n safetyParts.push(refMap[safety.refusal_style] ?? \"\");\n }\n if (safety.no_fabrication) safetyParts.push(\"Never fabricate information. If you don't know, say so.\");\n if (safety.no_false_certainty) safetyParts.push(\"Never present uncertain information as certain.\");\n parts.push(safetyParts.join(\" \"));\n }\n\n // Actions\n const actions = this.config.actions;\n if (actions) {\n const actParts: string[] = [\"\\n## Actions\"];\n const toolMap: Record<string, string> = {\n avoid_tools: \"Minimize tool use. Answer from knowledge when possible.\",\n when_needed: \"Use tools when they would improve your answer.\",\n prefer_tools: \"Proactively use available tools. Always verify with tools rather than memory.\",\n };\n actParts.push(toolMap[actions.when_to_use_tools] ?? \"\");\n if (actions.explain_actions === \"brief\" || actions.explain_actions === \"full\") {\n actParts.push(actions.explain_actions === \"full\" ? \"Explain what you're doing before and after tool use.\" : \"Briefly explain tool use.\");\n }\n parts.push(actParts.join(\" \"));\n }\n\n // Markdown body\n if (this.body.trim()) {\n parts.push(`\\n## Additional Instructions\\n\\n${this.body.trim()}`);\n }\n\n return parts.join(\"\\n\\n\");\n }\n\n get defaultProfile(): string {\n return this.config.profiles?.[0] ?? \"default\";\n }\n\n get profileNames(): string[] {\n return this.config.profiles ?? [\"default\"];\n }\n}\n\n// ── Public API ──────────────────────────────────────────────────────────\n\n/** Parse a Soul.md string into a Soul instance. */\nexport function loadSoul(content: string): Soul {\n const { frontMatter, body } = parseYamlFrontMatter(content);\n const config = frontMatter as unknown as SoulConfig;\n\n if (!config.id) throw new Error(\"Soul.md missing required field: id\");\n if (!config.name) throw new Error(\"Soul.md missing required field: name\");\n\n return new Soul(config, body);\n}\n\n/** Deep merge (Standard Merge semantics from Soul.md spec). */\nfunction mergeDeep(base: Record<string, unknown>, overlay: Record<string, unknown>): Record<string, unknown> {\n const result = { ...base };\n for (const key of Object.keys(overlay)) {\n const baseVal = result[key];\n const overVal = overlay[key];\n if (overVal === null) {\n result[key] = null;\n } else if (\n typeof baseVal === \"object\" && baseVal !== null && !Array.isArray(baseVal) &&\n typeof overVal === \"object\" && overVal !== null && !Array.isArray(overVal)\n ) {\n result[key] = mergeDeep(baseVal as Record<string, unknown>, overVal as Record<string, unknown>);\n } else {\n result[key] = overVal;\n }\n }\n return result;\n}"],"mappings":"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","names":[]}
|