@moikapy/origen 0.4.1 → 0.4.2
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-DruYlWW-.d.ts +204 -0
- package/dist/adapter.d.ts +5 -204
- package/dist/chunk-QF3XSUMT.js +333 -0
- package/dist/chunk-QF3XSUMT.js.map +1 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/soul.js +4 -327
- package/dist/soul.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { Model, Api, Message, Context } from '@mariozechner/pi-ai';
|
|
2
|
+
import { AgentTool, AgentEvent } from '@mariozechner/pi-agent-core';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { ModelId } from './models.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Origen types — no runtime deps, safe for client + server.
|
|
8
|
+
*/
|
|
9
|
+
/** D1-compatible database interface for tool execution */
|
|
10
|
+
interface D1Like {
|
|
11
|
+
prepare(sql: string): {
|
|
12
|
+
bind(...params: unknown[]): {
|
|
13
|
+
all(): Promise<{
|
|
14
|
+
results?: Record<string, unknown>[];
|
|
15
|
+
}>;
|
|
16
|
+
first(): Promise<Record<string, unknown> | null>;
|
|
17
|
+
run(): Promise<{
|
|
18
|
+
meta?: {
|
|
19
|
+
changes: number;
|
|
20
|
+
last_row_id: number;
|
|
21
|
+
};
|
|
22
|
+
}>;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/** Function that provides a D1 instance to tool executors */
|
|
27
|
+
type D1Provider = () => Promise<D1Like>;
|
|
28
|
+
/** Chat context passed from the UI (what the user is reading) */
|
|
29
|
+
interface ReadingContext {
|
|
30
|
+
translation: string;
|
|
31
|
+
bookCode: string;
|
|
32
|
+
chapter: number;
|
|
33
|
+
selectedVerses?: number[];
|
|
34
|
+
}
|
|
35
|
+
interface Citation {
|
|
36
|
+
book: string;
|
|
37
|
+
chapter: number;
|
|
38
|
+
verse: number;
|
|
39
|
+
}
|
|
40
|
+
interface UsageInfo {
|
|
41
|
+
promptTokens?: number;
|
|
42
|
+
completionTokens?: number;
|
|
43
|
+
totalCost?: number;
|
|
44
|
+
}
|
|
45
|
+
/** Model configuration entry */
|
|
46
|
+
interface ModelConfig {
|
|
47
|
+
name: string;
|
|
48
|
+
description: string;
|
|
49
|
+
free: boolean;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Origen — Agent Engine (v0.3)
|
|
54
|
+
*
|
|
55
|
+
* Multi-provider agent harness built on pi-ai + pi-agent-core.
|
|
56
|
+
* Supports OpenRouter, Ollama, Anthropic, Google, and any OpenAI-compatible API.
|
|
57
|
+
* Soul.md personas, streaming, parallel tool execution, abort support.
|
|
58
|
+
*/
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* A tool that the host app registers with Origen.
|
|
62
|
+
* Simple interface: name, description, JSON schema, and an execute function
|
|
63
|
+
* that receives (args, getD1). The adapter wraps this into pi-agent-core's AgentTool.
|
|
64
|
+
*/
|
|
65
|
+
interface OrigenTool {
|
|
66
|
+
name: string;
|
|
67
|
+
description: string;
|
|
68
|
+
/** OpenAI function-calling parameter schema (JSON) */
|
|
69
|
+
parameters: Record<string, unknown>;
|
|
70
|
+
/** Zod schema for runtime validation (optional) */
|
|
71
|
+
inputSchema?: z.ZodType;
|
|
72
|
+
execute: (args: Record<string, unknown>, getD1: D1Provider) => Promise<string>;
|
|
73
|
+
}
|
|
74
|
+
interface AgentConfig {
|
|
75
|
+
appName?: string;
|
|
76
|
+
systemPrompt?: string;
|
|
77
|
+
tools: OrigenTool[];
|
|
78
|
+
getD1: D1Provider;
|
|
79
|
+
model?: ModelId;
|
|
80
|
+
maxSteps?: number;
|
|
81
|
+
/** Custom citation extractor */
|
|
82
|
+
extractCitations?: (text: string) => Citation[];
|
|
83
|
+
/** Dynamic API key resolution per provider (e.g., for expiring OAuth tokens) */
|
|
84
|
+
getApiKey?: (provider: string) => Promise<string | undefined>;
|
|
85
|
+
/** Ollama base URL override (default: http://localhost:11434/v1) */
|
|
86
|
+
ollamaBaseUrl?: string;
|
|
87
|
+
/** Tool execution mode: "parallel" (default) or "sequential" */
|
|
88
|
+
toolExecution?: "sequential" | "parallel";
|
|
89
|
+
/** Abort signal for cancellation */
|
|
90
|
+
signal?: AbortSignal;
|
|
91
|
+
/** Reasoning/thinking level for models that support it */
|
|
92
|
+
thinkingLevel?: "off" | "minimal" | "low" | "medium" | "high";
|
|
93
|
+
}
|
|
94
|
+
interface AuthCheckResult {
|
|
95
|
+
authenticated: boolean;
|
|
96
|
+
apiKey: string | null;
|
|
97
|
+
provider?: string;
|
|
98
|
+
error?: string;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Provider-aware auth check. Tests key availability for each provider.
|
|
102
|
+
* If no provider argument, checks OpenRouter + Ollama availability.
|
|
103
|
+
*/
|
|
104
|
+
declare function checkAuth(getApiKey: ((provider: string) => Promise<string | undefined>) | (() => Promise<string | null>)): Promise<AuthCheckResult>;
|
|
105
|
+
/** Convenience: check OpenRouter auth only (backward compat). */
|
|
106
|
+
declare function checkOpenRouterAuth(getApiKey: () => Promise<string | null>): Promise<AuthCheckResult>;
|
|
107
|
+
type StreamEvent = {
|
|
108
|
+
type: "reasoning";
|
|
109
|
+
content: string;
|
|
110
|
+
} | {
|
|
111
|
+
type: "tool_call";
|
|
112
|
+
name: string;
|
|
113
|
+
args: Record<string, unknown>;
|
|
114
|
+
} | {
|
|
115
|
+
type: "tool_result";
|
|
116
|
+
name: string;
|
|
117
|
+
result: string;
|
|
118
|
+
} | {
|
|
119
|
+
type: "text";
|
|
120
|
+
content: string;
|
|
121
|
+
} | {
|
|
122
|
+
type: "done";
|
|
123
|
+
message: string;
|
|
124
|
+
citations: Citation[];
|
|
125
|
+
usage?: UsageInfo;
|
|
126
|
+
} | {
|
|
127
|
+
type: "error";
|
|
128
|
+
message: string;
|
|
129
|
+
};
|
|
130
|
+
declare function streamOrigen(messages: Array<{
|
|
131
|
+
role: "user" | "assistant";
|
|
132
|
+
content: string;
|
|
133
|
+
}>, context: Record<string, unknown> | undefined, config: AgentConfig, apiKey?: string): AsyncGenerator<StreamEvent>;
|
|
134
|
+
interface AgentResponse {
|
|
135
|
+
message: string;
|
|
136
|
+
citations: Citation[];
|
|
137
|
+
usage?: UsageInfo;
|
|
138
|
+
}
|
|
139
|
+
declare function callOrigen(messages: Array<{
|
|
140
|
+
role: "user" | "assistant";
|
|
141
|
+
content: string;
|
|
142
|
+
}>, context: Record<string, unknown> | undefined, config: AgentConfig, apiKey?: string): Promise<AgentResponse>;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Adapter: bridges Origen's simple types to pi-agent-core/pi-ai types.
|
|
146
|
+
*
|
|
147
|
+
* - OrigenTool → AgentTool (injects D1Provider)
|
|
148
|
+
* - pi-ai Model resolution (OpenRouter, Ollama, Anthropic, Google)
|
|
149
|
+
* - StreamEvent translation (AgentEvent → Origen's StreamEvent)
|
|
150
|
+
*/
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Convert an OrigenTool into a pi-agent-core AgentTool.
|
|
154
|
+
* The D1Provider is captured in closure so the tool's execute gets it.
|
|
155
|
+
*/
|
|
156
|
+
declare function adaptTool(tool: OrigenTool, getD1: D1Provider): AgentTool;
|
|
157
|
+
/** Adapt all OrigenTools for an Agent instance. */
|
|
158
|
+
declare function adaptTools(tools: OrigenTool[], getD1: D1Provider): AgentTool[];
|
|
159
|
+
interface ModelResolutionOptions {
|
|
160
|
+
/** Ollama base URL, e.g. "http://localhost:11434/v1" */
|
|
161
|
+
ollamaBaseUrl?: string;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Resolve a model ID string to a pi-ai Model object.
|
|
165
|
+
* Tries pi-ai's registry first, then falls back to built-in Ollama definitions.
|
|
166
|
+
*/
|
|
167
|
+
declare function resolveModel(modelId: string, options?: ModelResolutionOptions): Model<Api>;
|
|
168
|
+
/** Convert Origen's simple messages to pi-ai Message format. */
|
|
169
|
+
declare function convertMessages(messages: Array<{
|
|
170
|
+
role: "user" | "assistant";
|
|
171
|
+
content: string;
|
|
172
|
+
}>): Message[];
|
|
173
|
+
/** Build a pi-ai Context from Origen's config. */
|
|
174
|
+
declare function buildContext(systemPrompt: string, messages: Message[], adaptedTools: AgentTool[]): Context;
|
|
175
|
+
/** Translate a pi-agent-core AgentEvent into an Origen StreamEvent. */
|
|
176
|
+
declare function translateEvent(event: AgentEvent, extractCitations?: (text: string) => Citation[]): StreamEvent | null;
|
|
177
|
+
/**
|
|
178
|
+
* Eagerly subscribe to an Agent and return an async iterable of Origen StreamEvents.
|
|
179
|
+
*
|
|
180
|
+
* CRITICAL: The subscription is created synchronously when this function is called,
|
|
181
|
+
* BEFORE agent.prompt() starts. This avoids the race condition where events
|
|
182
|
+
* emitted during prompt() are missed if subscription happens after.
|
|
183
|
+
*
|
|
184
|
+
* Usage:
|
|
185
|
+
* const { stream, unsubscribe } = createEventStream(agent, extractCitations);
|
|
186
|
+
* agent.prompt(messages); // events flow into stream via active subscription
|
|
187
|
+
* for await (const event of stream) { ... }
|
|
188
|
+
*/
|
|
189
|
+
declare function createEventStream(agent: any, // Agent from pi-agent-core
|
|
190
|
+
extractCitations?: (text: string) => Citation[]): {
|
|
191
|
+
stream: AsyncGenerator<StreamEvent>;
|
|
192
|
+
unsubscribe: () => void;
|
|
193
|
+
};
|
|
194
|
+
/**
|
|
195
|
+
* Subscribe to an Agent and yield Origen StreamEvents.
|
|
196
|
+
* Handles the full lifecycle from agent_start to agent_end.
|
|
197
|
+
*
|
|
198
|
+
* @deprecated Use createEventStream() instead to avoid race conditions.
|
|
199
|
+
* This function subscribes lazily (on first iteration) which can miss events
|
|
200
|
+
* if the agent has already started emitting.
|
|
201
|
+
*/
|
|
202
|
+
declare function agentToStreamEvents(agent: any, extractCitations?: (text: string) => Citation[]): AsyncGenerator<StreamEvent>;
|
|
203
|
+
|
|
204
|
+
export { type AgentConfig as A, type Citation as C, type D1Like as D, type ModelResolutionOptions as M, type OrigenTool as O, type ReadingContext as R, type StreamEvent as S, type UsageInfo as U, type AgentResponse as a, type AuthCheckResult as b, type D1Provider as c, type ModelConfig as d, callOrigen as e, checkAuth as f, checkOpenRouterAuth as g, createEventStream as h, adaptTool as i, adaptTools as j, agentToStreamEvents as k, buildContext as l, convertMessages as m, resolveModel as r, streamOrigen as s, translateEvent as t };
|
package/dist/adapter.d.ts
CHANGED
|
@@ -1,204 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Origen types — no runtime deps, safe for client + server.
|
|
8
|
-
*/
|
|
9
|
-
/** D1-compatible database interface for tool execution */
|
|
10
|
-
interface D1Like {
|
|
11
|
-
prepare(sql: string): {
|
|
12
|
-
bind(...params: unknown[]): {
|
|
13
|
-
all(): Promise<{
|
|
14
|
-
results?: Record<string, unknown>[];
|
|
15
|
-
}>;
|
|
16
|
-
first(): Promise<Record<string, unknown> | null>;
|
|
17
|
-
run(): Promise<{
|
|
18
|
-
meta?: {
|
|
19
|
-
changes: number;
|
|
20
|
-
last_row_id: number;
|
|
21
|
-
};
|
|
22
|
-
}>;
|
|
23
|
-
};
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
/** Function that provides a D1 instance to tool executors */
|
|
27
|
-
type D1Provider = () => Promise<D1Like>;
|
|
28
|
-
/** Chat context passed from the UI (what the user is reading) */
|
|
29
|
-
interface ReadingContext {
|
|
30
|
-
translation: string;
|
|
31
|
-
bookCode: string;
|
|
32
|
-
chapter: number;
|
|
33
|
-
selectedVerses?: number[];
|
|
34
|
-
}
|
|
35
|
-
interface Citation {
|
|
36
|
-
book: string;
|
|
37
|
-
chapter: number;
|
|
38
|
-
verse: number;
|
|
39
|
-
}
|
|
40
|
-
interface UsageInfo {
|
|
41
|
-
promptTokens?: number;
|
|
42
|
-
completionTokens?: number;
|
|
43
|
-
totalCost?: number;
|
|
44
|
-
}
|
|
45
|
-
/** Model configuration entry */
|
|
46
|
-
interface ModelConfig {
|
|
47
|
-
name: string;
|
|
48
|
-
description: string;
|
|
49
|
-
free: boolean;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Origen — Agent Engine (v0.3)
|
|
54
|
-
*
|
|
55
|
-
* Multi-provider agent harness built on pi-ai + pi-agent-core.
|
|
56
|
-
* Supports OpenRouter, Ollama, Anthropic, Google, and any OpenAI-compatible API.
|
|
57
|
-
* Soul.md personas, streaming, parallel tool execution, abort support.
|
|
58
|
-
*/
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* A tool that the host app registers with Origen.
|
|
62
|
-
* Simple interface: name, description, JSON schema, and an execute function
|
|
63
|
-
* that receives (args, getD1). The adapter wraps this into pi-agent-core's AgentTool.
|
|
64
|
-
*/
|
|
65
|
-
interface OrigenTool {
|
|
66
|
-
name: string;
|
|
67
|
-
description: string;
|
|
68
|
-
/** OpenAI function-calling parameter schema (JSON) */
|
|
69
|
-
parameters: Record<string, unknown>;
|
|
70
|
-
/** Zod schema for runtime validation (optional) */
|
|
71
|
-
inputSchema?: z.ZodType;
|
|
72
|
-
execute: (args: Record<string, unknown>, getD1: D1Provider) => Promise<string>;
|
|
73
|
-
}
|
|
74
|
-
interface AgentConfig {
|
|
75
|
-
appName?: string;
|
|
76
|
-
systemPrompt?: string;
|
|
77
|
-
tools: OrigenTool[];
|
|
78
|
-
getD1: D1Provider;
|
|
79
|
-
model?: ModelId;
|
|
80
|
-
maxSteps?: number;
|
|
81
|
-
/** Custom citation extractor */
|
|
82
|
-
extractCitations?: (text: string) => Citation[];
|
|
83
|
-
/** Dynamic API key resolution per provider (e.g., for expiring OAuth tokens) */
|
|
84
|
-
getApiKey?: (provider: string) => Promise<string | undefined>;
|
|
85
|
-
/** Ollama base URL override (default: http://localhost:11434/v1) */
|
|
86
|
-
ollamaBaseUrl?: string;
|
|
87
|
-
/** Tool execution mode: "parallel" (default) or "sequential" */
|
|
88
|
-
toolExecution?: "sequential" | "parallel";
|
|
89
|
-
/** Abort signal for cancellation */
|
|
90
|
-
signal?: AbortSignal;
|
|
91
|
-
/** Reasoning/thinking level for models that support it */
|
|
92
|
-
thinkingLevel?: "off" | "minimal" | "low" | "medium" | "high";
|
|
93
|
-
}
|
|
94
|
-
interface AuthCheckResult {
|
|
95
|
-
authenticated: boolean;
|
|
96
|
-
apiKey: string | null;
|
|
97
|
-
provider?: string;
|
|
98
|
-
error?: string;
|
|
99
|
-
}
|
|
100
|
-
/**
|
|
101
|
-
* Provider-aware auth check. Tests key availability for each provider.
|
|
102
|
-
* If no provider argument, checks OpenRouter + Ollama availability.
|
|
103
|
-
*/
|
|
104
|
-
declare function checkAuth(getApiKey: ((provider: string) => Promise<string | undefined>) | (() => Promise<string | null>)): Promise<AuthCheckResult>;
|
|
105
|
-
/** Convenience: check OpenRouter auth only (backward compat). */
|
|
106
|
-
declare function checkOpenRouterAuth(getApiKey: () => Promise<string | null>): Promise<AuthCheckResult>;
|
|
107
|
-
type StreamEvent = {
|
|
108
|
-
type: "reasoning";
|
|
109
|
-
content: string;
|
|
110
|
-
} | {
|
|
111
|
-
type: "tool_call";
|
|
112
|
-
name: string;
|
|
113
|
-
args: Record<string, unknown>;
|
|
114
|
-
} | {
|
|
115
|
-
type: "tool_result";
|
|
116
|
-
name: string;
|
|
117
|
-
result: string;
|
|
118
|
-
} | {
|
|
119
|
-
type: "text";
|
|
120
|
-
content: string;
|
|
121
|
-
} | {
|
|
122
|
-
type: "done";
|
|
123
|
-
message: string;
|
|
124
|
-
citations: Citation[];
|
|
125
|
-
usage?: UsageInfo;
|
|
126
|
-
} | {
|
|
127
|
-
type: "error";
|
|
128
|
-
message: string;
|
|
129
|
-
};
|
|
130
|
-
declare function streamOrigen(messages: Array<{
|
|
131
|
-
role: "user" | "assistant";
|
|
132
|
-
content: string;
|
|
133
|
-
}>, context: Record<string, unknown> | undefined, config: AgentConfig, apiKey?: string): AsyncGenerator<StreamEvent>;
|
|
134
|
-
interface AgentResponse {
|
|
135
|
-
message: string;
|
|
136
|
-
citations: Citation[];
|
|
137
|
-
usage?: UsageInfo;
|
|
138
|
-
}
|
|
139
|
-
declare function callOrigen(messages: Array<{
|
|
140
|
-
role: "user" | "assistant";
|
|
141
|
-
content: string;
|
|
142
|
-
}>, context: Record<string, unknown> | undefined, config: AgentConfig, apiKey?: string): Promise<AgentResponse>;
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Adapter: bridges Origen's simple types to pi-agent-core/pi-ai types.
|
|
146
|
-
*
|
|
147
|
-
* - OrigenTool → AgentTool (injects D1Provider)
|
|
148
|
-
* - pi-ai Model resolution (OpenRouter, Ollama, Anthropic, Google)
|
|
149
|
-
* - StreamEvent translation (AgentEvent → Origen's StreamEvent)
|
|
150
|
-
*/
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Convert an OrigenTool into a pi-agent-core AgentTool.
|
|
154
|
-
* The D1Provider is captured in closure so the tool's execute gets it.
|
|
155
|
-
*/
|
|
156
|
-
declare function adaptTool(tool: OrigenTool, getD1: D1Provider): AgentTool;
|
|
157
|
-
/** Adapt all OrigenTools for an Agent instance. */
|
|
158
|
-
declare function adaptTools(tools: OrigenTool[], getD1: D1Provider): AgentTool[];
|
|
159
|
-
interface ModelResolutionOptions {
|
|
160
|
-
/** Ollama base URL, e.g. "http://localhost:11434/v1" */
|
|
161
|
-
ollamaBaseUrl?: string;
|
|
162
|
-
}
|
|
163
|
-
/**
|
|
164
|
-
* Resolve a model ID string to a pi-ai Model object.
|
|
165
|
-
* Tries pi-ai's registry first, then falls back to built-in Ollama definitions.
|
|
166
|
-
*/
|
|
167
|
-
declare function resolveModel(modelId: string, options?: ModelResolutionOptions): Model<Api>;
|
|
168
|
-
/** Convert Origen's simple messages to pi-ai Message format. */
|
|
169
|
-
declare function convertMessages(messages: Array<{
|
|
170
|
-
role: "user" | "assistant";
|
|
171
|
-
content: string;
|
|
172
|
-
}>): Message[];
|
|
173
|
-
/** Build a pi-ai Context from Origen's config. */
|
|
174
|
-
declare function buildContext(systemPrompt: string, messages: Message[], adaptedTools: AgentTool[]): Context;
|
|
175
|
-
/** Translate a pi-agent-core AgentEvent into an Origen StreamEvent. */
|
|
176
|
-
declare function translateEvent(event: AgentEvent, extractCitations?: (text: string) => Citation[]): StreamEvent | null;
|
|
177
|
-
/**
|
|
178
|
-
* Eagerly subscribe to an Agent and return an async iterable of Origen StreamEvents.
|
|
179
|
-
*
|
|
180
|
-
* CRITICAL: The subscription is created synchronously when this function is called,
|
|
181
|
-
* BEFORE agent.prompt() starts. This avoids the race condition where events
|
|
182
|
-
* emitted during prompt() are missed if subscription happens after.
|
|
183
|
-
*
|
|
184
|
-
* Usage:
|
|
185
|
-
* const { stream, unsubscribe } = createEventStream(agent, extractCitations);
|
|
186
|
-
* agent.prompt(messages); // events flow into stream via active subscription
|
|
187
|
-
* for await (const event of stream) { ... }
|
|
188
|
-
*/
|
|
189
|
-
declare function createEventStream(agent: any, // Agent from pi-agent-core
|
|
190
|
-
extractCitations?: (text: string) => Citation[]): {
|
|
191
|
-
stream: AsyncGenerator<StreamEvent>;
|
|
192
|
-
unsubscribe: () => void;
|
|
193
|
-
};
|
|
194
|
-
/**
|
|
195
|
-
* Subscribe to an Agent and yield Origen StreamEvents.
|
|
196
|
-
* Handles the full lifecycle from agent_start to agent_end.
|
|
197
|
-
*
|
|
198
|
-
* @deprecated Use createEventStream() instead to avoid race conditions.
|
|
199
|
-
* This function subscribes lazily (on first iteration) which can miss events
|
|
200
|
-
* if the agent has already started emitting.
|
|
201
|
-
*/
|
|
202
|
-
declare function agentToStreamEvents(agent: any, extractCitations?: (text: string) => Citation[]): AsyncGenerator<StreamEvent>;
|
|
203
|
-
|
|
204
|
-
export { type AgentConfig as A, type Citation as C, type D1Like as D, type ModelConfig as M, type ModelResolutionOptions, type OrigenTool as O, type ReadingContext as R, type StreamEvent as S, type UsageInfo as U, type AgentResponse as a, adaptTool, adaptTools, agentToStreamEvents, type AuthCheckResult as b, buildContext, type D1Provider as c, convertMessages, createEventStream, callOrigen as d, checkAuth as e, checkOpenRouterAuth as f, resolveModel, streamOrigen as s, translateEvent };
|
|
1
|
+
import '@mariozechner/pi-ai';
|
|
2
|
+
import '@mariozechner/pi-agent-core';
|
|
3
|
+
export { M as ModelResolutionOptions, i as adaptTool, j as adaptTools, k as agentToStreamEvents, l as buildContext, m as convertMessages, h as createEventStream, r as resolveModel, t as translateEvent } from './adapter-DruYlWW-.js';
|
|
4
|
+
import 'zod';
|
|
5
|
+
import './models.js';
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
// src/soul.ts
|
|
2
|
+
function parseYamlFrontMatter(content) {
|
|
3
|
+
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)?$/);
|
|
4
|
+
if (!match) {
|
|
5
|
+
return { frontMatter: {}, body: content };
|
|
6
|
+
}
|
|
7
|
+
const rawYaml = match[1];
|
|
8
|
+
const markdownBody = match[2] ?? "";
|
|
9
|
+
const frontMatter = parseSoulYaml(rawYaml);
|
|
10
|
+
return { frontMatter, body: markdownBody };
|
|
11
|
+
}
|
|
12
|
+
function parseSoulYaml(raw) {
|
|
13
|
+
const lines = raw.split("\n").map((l) => l.replace(/\r$/, ""));
|
|
14
|
+
const { result } = parseBlock(lines, 0, 0);
|
|
15
|
+
return result;
|
|
16
|
+
}
|
|
17
|
+
function parseBlock(lines, start, baseIndent) {
|
|
18
|
+
const result = {};
|
|
19
|
+
let i = start;
|
|
20
|
+
while (i < lines.length) {
|
|
21
|
+
const line = lines[i];
|
|
22
|
+
const indent = lineLengths(line).indent;
|
|
23
|
+
if (line.trim() === "") {
|
|
24
|
+
i++;
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
if (indent < baseIndent) break;
|
|
28
|
+
const trimmed = line.trim();
|
|
29
|
+
if (trimmed.startsWith("- ")) {
|
|
30
|
+
i++;
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
const kvMatch = trimmed.match(/^([\w][\w.-]*):\s*(.*)$/);
|
|
34
|
+
if (!kvMatch) {
|
|
35
|
+
i++;
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
const key = kvMatch[1];
|
|
39
|
+
const inlineVal = kvMatch[2].trim();
|
|
40
|
+
if (inlineVal === "") {
|
|
41
|
+
const nextLine = i + 1 < lines.length ? lines[i + 1] : "";
|
|
42
|
+
const nextIndent = nextLine.trim() === "" ? 0 : lineLengths(nextLine).indent;
|
|
43
|
+
const nextTrimmed = nextLine.trim();
|
|
44
|
+
if (nextIndent > indent) {
|
|
45
|
+
if (nextTrimmed.startsWith("- ")) {
|
|
46
|
+
const { items, nextLine: afterList } = parseList(lines, i + 1, nextIndent);
|
|
47
|
+
result[key] = items;
|
|
48
|
+
i = afterList;
|
|
49
|
+
} else {
|
|
50
|
+
const { result: nested, nextLine: afterNested } = parseBlock(lines, i + 1, nextIndent);
|
|
51
|
+
result[key] = nested;
|
|
52
|
+
i = afterNested;
|
|
53
|
+
}
|
|
54
|
+
} else {
|
|
55
|
+
result[key] = null;
|
|
56
|
+
i++;
|
|
57
|
+
}
|
|
58
|
+
} else {
|
|
59
|
+
result[key] = parseScalar(inlineVal);
|
|
60
|
+
i++;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return { result, nextLine: i };
|
|
64
|
+
}
|
|
65
|
+
function parseList(lines, start, baseIndent) {
|
|
66
|
+
const items = [];
|
|
67
|
+
let i = start;
|
|
68
|
+
while (i < lines.length) {
|
|
69
|
+
const line = lines[i];
|
|
70
|
+
const indent = lineLengths(line).indent;
|
|
71
|
+
if (line.trim() === "") {
|
|
72
|
+
i++;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (indent < baseIndent) break;
|
|
76
|
+
if (!line.trim().startsWith("- ")) break;
|
|
77
|
+
const value = line.trim().slice(2).trim();
|
|
78
|
+
items.push(parseScalar(value));
|
|
79
|
+
i++;
|
|
80
|
+
}
|
|
81
|
+
return { items, nextLine: i };
|
|
82
|
+
}
|
|
83
|
+
function parseScalar(value) {
|
|
84
|
+
if (value === "null" || value === "~") return null;
|
|
85
|
+
if (value === "true") return true;
|
|
86
|
+
if (value === "false") return false;
|
|
87
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
88
|
+
return value.slice(1, -1);
|
|
89
|
+
}
|
|
90
|
+
if (/^-?\d+$/.test(value)) return parseInt(value, 10);
|
|
91
|
+
if (/^-?\d+\.\d+$/.test(value)) return parseFloat(value);
|
|
92
|
+
if (value.startsWith("[") && value.endsWith("]")) {
|
|
93
|
+
return value.slice(1, -1).split(",").map((s) => parseScalar(s.trim()));
|
|
94
|
+
}
|
|
95
|
+
return value;
|
|
96
|
+
}
|
|
97
|
+
function lineLengths(line) {
|
|
98
|
+
const match = line.match(/^(\s*)(.*)$/);
|
|
99
|
+
const indent = match ? match[1].length : 0;
|
|
100
|
+
const content = match ? match[2] : "";
|
|
101
|
+
return { indent, content };
|
|
102
|
+
}
|
|
103
|
+
var Soul = class _Soul {
|
|
104
|
+
config;
|
|
105
|
+
body;
|
|
106
|
+
constructor(config, body) {
|
|
107
|
+
this.config = config;
|
|
108
|
+
this.body = body;
|
|
109
|
+
}
|
|
110
|
+
/** Select a profile and return a new Soul with that profile merged in. */
|
|
111
|
+
selectProfile(profileName) {
|
|
112
|
+
const profiles = this.config.profiles ?? ["default"];
|
|
113
|
+
const overrides = this.config.profile_overrides ?? {};
|
|
114
|
+
const profileOverrides = overrides[profileName];
|
|
115
|
+
if (!profiles.includes(profileName) || !profileOverrides) {
|
|
116
|
+
return this;
|
|
117
|
+
}
|
|
118
|
+
const merged = mergeDeep(
|
|
119
|
+
structuredClone(this.config),
|
|
120
|
+
profileOverrides
|
|
121
|
+
);
|
|
122
|
+
return new _Soul(merged, this.body);
|
|
123
|
+
}
|
|
124
|
+
/** Build a system prompt from the Soul definition. */
|
|
125
|
+
buildPrompt() {
|
|
126
|
+
const parts = [];
|
|
127
|
+
const identity = this.config.identity;
|
|
128
|
+
if (identity) {
|
|
129
|
+
const role = identity.role ?? this.config.name;
|
|
130
|
+
const archetype = identity.archetype;
|
|
131
|
+
parts.push(`You are ${this.config.name}${archetype ? `, a ${archetype}` : ""}${role ? `. Role: ${role}` : ""}.`);
|
|
132
|
+
if (identity.domain_focus?.length) {
|
|
133
|
+
parts.push(`Domain expertise: ${identity.domain_focus.join(", ")}.`);
|
|
134
|
+
}
|
|
135
|
+
if (identity.non_goals?.length) {
|
|
136
|
+
parts.push(`Non-goals: ${identity.non_goals.join(", ")}.`);
|
|
137
|
+
}
|
|
138
|
+
} else {
|
|
139
|
+
parts.push(`You are ${this.config.name}.`);
|
|
140
|
+
}
|
|
141
|
+
const relationship = this.config.relationship;
|
|
142
|
+
if (relationship) {
|
|
143
|
+
if (relationship.stance) {
|
|
144
|
+
const stanceMap = {
|
|
145
|
+
subordinate: "You serve the user's direction.",
|
|
146
|
+
peer: "You collaborate with the user as a partner.",
|
|
147
|
+
authoritative: "You provide expert guidance and direction.",
|
|
148
|
+
adversarial: "You challenge the user's assumptions to improve outcomes."
|
|
149
|
+
};
|
|
150
|
+
parts.push(stanceMap[relationship.stance] ?? "");
|
|
151
|
+
}
|
|
152
|
+
if (relationship.user_model_default) {
|
|
153
|
+
const userModelMap = {
|
|
154
|
+
novice: "Assume the user is new to this domain. Explain terms and concepts.",
|
|
155
|
+
intermediate: "Assume moderate familiarity. Explain only when needed.",
|
|
156
|
+
expert: "Be concise. The user knows the domain well.",
|
|
157
|
+
unknown: "Adapt your explanation depth to the user's apparent knowledge level."
|
|
158
|
+
};
|
|
159
|
+
parts.push(userModelMap[relationship.user_model_default] ?? "");
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
const values = this.config.values;
|
|
163
|
+
if (values?.priorities?.length) {
|
|
164
|
+
parts.push(`
|
|
165
|
+
## Priorities (in order)
|
|
166
|
+
${values.priorities.map((p, i) => `${i + 1}. ${p}`).join("\n")}`);
|
|
167
|
+
}
|
|
168
|
+
if (values?.taboo?.length) {
|
|
169
|
+
parts.push(`
|
|
170
|
+
## Forbidden patterns
|
|
171
|
+
${values.taboo.map((t) => `- ${t}`).join("\n")}`);
|
|
172
|
+
}
|
|
173
|
+
const voice = this.config.voice;
|
|
174
|
+
if (voice) {
|
|
175
|
+
const voiceParts = ["\n## Voice & Style"];
|
|
176
|
+
if (voice.formality !== void 0) {
|
|
177
|
+
const level = voice.formality <= 30 ? "very casual" : voice.formality <= 60 ? "moderately formal" : voice.formality <= 80 ? "professional" : "highly formal";
|
|
178
|
+
voiceParts.push(`Formality: ${level} (${voice.formality}/100).`);
|
|
179
|
+
}
|
|
180
|
+
if (voice.warmth !== void 0) {
|
|
181
|
+
const level = voice.warmth <= 30 ? "cold/detached" : voice.warmth <= 60 ? "neutral" : voice.warmth <= 80 ? "warm and approachable" : "very friendly and encouraging";
|
|
182
|
+
voiceParts.push(`Tone: ${level} (${voice.warmth}/100).`);
|
|
183
|
+
}
|
|
184
|
+
if (voice.verbosity !== void 0) {
|
|
185
|
+
const level = voice.verbosity <= 25 ? "extremely concise" : voice.verbosity <= 50 ? "concise" : voice.verbosity <= 75 ? "moderate length" : "thorough and detailed";
|
|
186
|
+
voiceParts.push(`Brevity: ${level} (${voice.verbosity}/100).`);
|
|
187
|
+
}
|
|
188
|
+
if (voice.jargon !== void 0) {
|
|
189
|
+
const level = voice.jargon <= 30 ? "use plain language" : voice.jargon <= 60 ? "use moderate technical terms" : "use domain-specific terminology freely";
|
|
190
|
+
voiceParts.push(`Jargon: ${level} (${voice.jargon}/100).`);
|
|
191
|
+
}
|
|
192
|
+
if (voice.formatting) voiceParts.push(`Formatting: ${voice.formatting}.`);
|
|
193
|
+
if (voice.banned_phrases?.length) voiceParts.push(`Never say: ${voice.banned_phrases.map((p) => `"${p}"`).join(", ")}.`);
|
|
194
|
+
if (voice.preferred_phrases?.length) voiceParts.push(`Prefer: ${voice.preferred_phrases.map((p) => `"${p}"`).join(", ")}.`);
|
|
195
|
+
if (voice.emoji_policy && voice.emoji_policy !== "rare") voiceParts.push(`Emoji usage: ${voice.emoji_policy}.`);
|
|
196
|
+
parts.push(voiceParts.join(" "));
|
|
197
|
+
}
|
|
198
|
+
const interaction = this.config.interaction;
|
|
199
|
+
if (interaction) {
|
|
200
|
+
const interactionParts = ["\n## Interaction Policy"];
|
|
201
|
+
if (interaction.clarifying_questions) {
|
|
202
|
+
const qMap = {
|
|
203
|
+
never: "Never ask clarifying questions. Make reasonable assumptions.",
|
|
204
|
+
when_ambiguous: "Ask clarifying questions only when the query is ambiguous.",
|
|
205
|
+
always: "Always confirm your understanding before responding."
|
|
206
|
+
};
|
|
207
|
+
interactionParts.push(qMap[interaction.clarifying_questions] ?? "");
|
|
208
|
+
}
|
|
209
|
+
if (interaction.uncertainty) {
|
|
210
|
+
const uMap = {
|
|
211
|
+
explicit: "Explicitly mark uncertain information. Say when you're not sure.",
|
|
212
|
+
implicit: "Use hedging language (might, possibly, could) for uncertain claims.",
|
|
213
|
+
never: "Never express uncertainty. State your best answer confidently."
|
|
214
|
+
};
|
|
215
|
+
interactionParts.push(uMap[interaction.uncertainty] ?? "");
|
|
216
|
+
}
|
|
217
|
+
if (interaction.disagreement) {
|
|
218
|
+
const dMap = {
|
|
219
|
+
soft: "Disagree gently. Acknowledge the user's perspective first.",
|
|
220
|
+
neutral: "State disagreements directly but politely.",
|
|
221
|
+
direct: "Challenge incorrect views directly. Don't soften disagreements."
|
|
222
|
+
};
|
|
223
|
+
interactionParts.push(dMap[interaction.disagreement] ?? "");
|
|
224
|
+
}
|
|
225
|
+
if (interaction.confirmations === "none") {
|
|
226
|
+
interactionParts.push("Don't ask for confirmation before acting. Just do it.");
|
|
227
|
+
}
|
|
228
|
+
parts.push(interactionParts.join(" "));
|
|
229
|
+
}
|
|
230
|
+
const cognition = this.config.cognition;
|
|
231
|
+
if (cognition) {
|
|
232
|
+
const cogParts = ["\n## Cognition"];
|
|
233
|
+
if (cognition.mode) {
|
|
234
|
+
const modeMap = {
|
|
235
|
+
analytical: "Think analytically. Break problems down, examine evidence, reason step by step.",
|
|
236
|
+
creative: "Think creatively. Generate novel ideas, make unexpected connections.",
|
|
237
|
+
operational: "Focus on execution. Prioritize working solutions over theory.",
|
|
238
|
+
exploratory: "Explore broadly. Consider many angles before committing to an answer.",
|
|
239
|
+
teaching: "Teach and explain. Build understanding progressively from basics.",
|
|
240
|
+
mixed: "Adapt your thinking mode to the task at hand."
|
|
241
|
+
};
|
|
242
|
+
cogParts.push(modeMap[cognition.mode] ?? "");
|
|
243
|
+
}
|
|
244
|
+
if (cognition.verification?.fact_checking) {
|
|
245
|
+
const fcMap = {
|
|
246
|
+
none: "No explicit fact-checking.",
|
|
247
|
+
light: "Verify key claims before stating them.",
|
|
248
|
+
strict: "Always verify claims. Never state unverified information as fact."
|
|
249
|
+
};
|
|
250
|
+
cogParts.push(fcMap[cognition.verification.fact_checking] ?? "");
|
|
251
|
+
}
|
|
252
|
+
parts.push(cogParts.join(" "));
|
|
253
|
+
}
|
|
254
|
+
const safety = this.config.safety;
|
|
255
|
+
if (safety) {
|
|
256
|
+
const safetyParts = ["\n## Safety"];
|
|
257
|
+
if (safety.speculation) {
|
|
258
|
+
const specMap = {
|
|
259
|
+
allow: "You may speculate freely.",
|
|
260
|
+
mark: "Mark speculative content clearly (e.g., 'I believe', 'likely', 'possibly').",
|
|
261
|
+
avoid: "Do not speculate. Only state what you can verify."
|
|
262
|
+
};
|
|
263
|
+
safetyParts.push(specMap[safety.speculation] ?? "");
|
|
264
|
+
}
|
|
265
|
+
if (safety.refusal_style) {
|
|
266
|
+
const refMap = {
|
|
267
|
+
brief: "Refuse briefly. No lectures.",
|
|
268
|
+
explain: "Explain why you're refusing when you decline a request.",
|
|
269
|
+
policy_cite: "Cite specific policies when refusing requests."
|
|
270
|
+
};
|
|
271
|
+
safetyParts.push(refMap[safety.refusal_style] ?? "");
|
|
272
|
+
}
|
|
273
|
+
if (safety.no_fabrication) safetyParts.push("Never fabricate information. If you don't know, say so.");
|
|
274
|
+
if (safety.no_false_certainty) safetyParts.push("Never present uncertain information as certain.");
|
|
275
|
+
parts.push(safetyParts.join(" "));
|
|
276
|
+
}
|
|
277
|
+
const actions = this.config.actions;
|
|
278
|
+
if (actions) {
|
|
279
|
+
const actParts = ["\n## Actions"];
|
|
280
|
+
const toolMap = {
|
|
281
|
+
avoid_tools: "Minimize tool use. Answer from knowledge when possible.",
|
|
282
|
+
when_needed: "Use tools when they would improve your answer.",
|
|
283
|
+
prefer_tools: "Proactively use available tools. Always verify with tools rather than memory."
|
|
284
|
+
};
|
|
285
|
+
actParts.push(toolMap[actions.when_to_use_tools] ?? "");
|
|
286
|
+
if (actions.explain_actions === "brief" || actions.explain_actions === "full") {
|
|
287
|
+
actParts.push(actions.explain_actions === "full" ? "Explain what you're doing before and after tool use." : "Briefly explain tool use.");
|
|
288
|
+
}
|
|
289
|
+
parts.push(actParts.join(" "));
|
|
290
|
+
}
|
|
291
|
+
if (this.body.trim()) {
|
|
292
|
+
parts.push(`
|
|
293
|
+
## Additional Instructions
|
|
294
|
+
|
|
295
|
+
${this.body.trim()}`);
|
|
296
|
+
}
|
|
297
|
+
return parts.join("\n\n");
|
|
298
|
+
}
|
|
299
|
+
get defaultProfile() {
|
|
300
|
+
return this.config.profiles?.[0] ?? "default";
|
|
301
|
+
}
|
|
302
|
+
get profileNames() {
|
|
303
|
+
return this.config.profiles ?? ["default"];
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
function loadSoul(content) {
|
|
307
|
+
const { frontMatter, body } = parseYamlFrontMatter(content);
|
|
308
|
+
const config = frontMatter;
|
|
309
|
+
if (!config.id) throw new Error("Soul.md missing required field: id");
|
|
310
|
+
if (!config.name) throw new Error("Soul.md missing required field: name");
|
|
311
|
+
return new Soul(config, body);
|
|
312
|
+
}
|
|
313
|
+
function mergeDeep(base, overlay) {
|
|
314
|
+
const result = { ...base };
|
|
315
|
+
for (const key of Object.keys(overlay)) {
|
|
316
|
+
const baseVal = result[key];
|
|
317
|
+
const overVal = overlay[key];
|
|
318
|
+
if (overVal === null) {
|
|
319
|
+
result[key] = null;
|
|
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);
|
|
322
|
+
} else {
|
|
323
|
+
result[key] = overVal;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
return result;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
export {
|
|
330
|
+
Soul,
|
|
331
|
+
loadSoul
|
|
332
|
+
};
|
|
333
|
+
//# sourceMappingURL=chunk-QF3XSUMT.js.map
|
|
@@ -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":[]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
export { A as AgentConfig, a as AgentResponse, b as AuthCheckResult, C as Citation, D as D1Like, c as D1Provider, ModelResolutionOptions,
|
|
1
|
+
export { A as AgentConfig, a as AgentResponse, b as AuthCheckResult, C as Citation, D as D1Like, c as D1Provider, M as ModelResolutionOptions, d as OrigenModelConfig, O as OrigenTool, R as ReadingContext, S as StreamEvent, U as UsageInfo, e as callOrigen, f as checkAuth, g as checkOpenRouterAuth, h as createEventStream, r as resolveModel, s as streamOrigen } from './adapter-DruYlWW-.js';
|
|
2
2
|
export { DEFAULT_MODEL, DEFAULT_MODEL_ID, MODELS, ModelConfig, ModelId, THINKING_MODELS, UIModelConfig, getModelsByProvider, getModelsForUI, isOllamaModel, supportsThinking } from './models.js';
|
|
3
|
-
|
|
3
|
+
export { Soul, loadSoul } from './soul.js';
|
|
4
4
|
import '@mariozechner/pi-ai';
|
|
5
5
|
import '@mariozechner/pi-agent-core';
|
|
6
|
+
import 'zod';
|
package/dist/index.js
CHANGED
|
@@ -8,6 +8,9 @@ import {
|
|
|
8
8
|
isOllamaModel,
|
|
9
9
|
supportsThinking
|
|
10
10
|
} from "./chunk-ECRY7XDR.js";
|
|
11
|
+
import {
|
|
12
|
+
loadSoul
|
|
13
|
+
} from "./chunk-QF3XSUMT.js";
|
|
11
14
|
import {
|
|
12
15
|
adaptTools,
|
|
13
16
|
convertMessages,
|
|
@@ -123,6 +126,7 @@ export {
|
|
|
123
126
|
getModelsByProvider,
|
|
124
127
|
getModelsForUI,
|
|
125
128
|
isOllamaModel,
|
|
129
|
+
loadSoul,
|
|
126
130
|
resolveModel,
|
|
127
131
|
streamOrigen,
|
|
128
132
|
supportsThinking
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/agent.ts"],"sourcesContent":["/**\n * Origen — Agent Engine (v0.3)\n *\n * Multi-provider agent harness built on pi-ai + pi-agent-core.\n * Supports OpenRouter, Ollama, Anthropic, Google, and any OpenAI-compatible API.\n * Soul.md personas, streaming, parallel tool execution, abort support.\n */\n\nimport { Agent } from \"@mariozechner/pi-agent-core\";\nimport { streamSimple } from \"@mariozechner/pi-ai\";\nimport type { AgentEvent } from \"@mariozechner/pi-agent-core\";\nimport { z } from \"zod\";\nimport {\n adaptTools,\n convertMessages,\n buildContext,\n createEventStream,\n resolveModel,\n} from \"./adapter\";\nimport { DEFAULT_MODEL_ID, THINKING_MODELS, type ModelId } from \"./models\";\nimport type { D1Provider, Citation, UsageInfo } from \"./types\";\n\n// ── Tool definition ───────────────────────────────────────────────────\n\n/**\n * A tool that the host app registers with Origen.\n * Simple interface: name, description, JSON schema, and an execute function\n * that receives (args, getD1). The adapter wraps this into pi-agent-core's AgentTool.\n */\nexport interface OrigenTool {\n name: string;\n description: string;\n /** OpenAI function-calling parameter schema (JSON) */\n parameters: Record<string, unknown>;\n /** Zod schema for runtime validation (optional) */\n inputSchema?: z.ZodType;\n execute: (args: Record<string, unknown>, getD1: D1Provider) => Promise<string>;\n}\n\n// ── Agent configuration ───────────────────────────────────────────────\n\nexport interface AgentConfig {\n appName?: string;\n systemPrompt?: string;\n tools: OrigenTool[];\n getD1: D1Provider;\n model?: ModelId;\n maxSteps?: number;\n /** Custom citation extractor */\n extractCitations?: (text: string) => Citation[];\n /** Dynamic API key resolution per provider (e.g., for expiring OAuth tokens) */\n getApiKey?: (provider: string) => Promise<string | undefined>;\n /** Ollama base URL override (default: http://localhost:11434/v1) */\n ollamaBaseUrl?: string;\n /** Tool execution mode: \"parallel\" (default) or \"sequential\" */\n toolExecution?: \"sequential\" | \"parallel\";\n /** Abort signal for cancellation */\n signal?: AbortSignal;\n /** Reasoning/thinking level for models that support it */\n thinkingLevel?: \"off\" | \"minimal\" | \"low\" | \"medium\" | \"high\";\n}\n\n// ── Auth check ────────────────────────────────────────\n\nexport interface AuthCheckResult {\n authenticated: boolean;\n apiKey: string | null;\n provider?: string;\n error?: string;\n}\n\n/**\n * Provider-aware auth check. Tests key availability for each provider.\n * If no provider argument, checks OpenRouter + Ollama availability.\n */\nexport async function checkAuth(\n getApiKey: ((provider: string) => Promise<string | undefined>) | (() => Promise<string | null>),\n): Promise<AuthCheckResult> {\n // Normalize to per-provider signature\n const getProviderKey = getApiKey.length >= 1\n ? getApiKey as (provider: string) => Promise<string | undefined>\n : async (provider: string) => {\n const key = await (getApiKey as () => Promise<string | null>)();\n return key ?? undefined;\n };\n\n // Try OpenRouter first\n const orKey = await getProviderKey(\"openrouter\");\n if (orKey) return { authenticated: true, apiKey: orKey, provider: \"openrouter\" };\n\n // Try Ollama\n const ollamaKey = await getProviderKey(\"ollama\");\n if (ollamaKey) return { authenticated: true, apiKey: ollamaKey, provider: \"ollama\" };\n\n // Try Anthropic\n const anthropicKey = await getProviderKey(\"anthropic\");\n if (anthropicKey) return { authenticated: true, apiKey: anthropicKey, provider: \"anthropic\" };\n\n return {\n authenticated: false,\n apiKey: null,\n error: \"Connect your OpenRouter account or configure Ollama to enable AI-powered study.\",\n };\n}\n\n/** Convenience: check OpenRouter auth only (backward compat). */\nexport async function checkOpenRouterAuth(\n getApiKey: () => Promise<string | null>\n): Promise<AuthCheckResult> {\n const apiKey = await getApiKey();\n if (!apiKey) {\n return { authenticated: false, apiKey: null, error: \"Connect your OpenRouter account to enable AI-powered study.\" };\n }\n return { authenticated: true, apiKey, provider: \"openrouter\" };\n}\n\n// ── Stream event types ─────────────────────────────────────────────────\n\nexport type StreamEvent =\n | { type: \"reasoning\"; content: string }\n | { type: \"tool_call\"; name: string; args: Record<string, unknown> }\n | { type: \"tool_result\"; name: string; result: string }\n | { type: \"text\"; content: string }\n | { type: \"done\"; message: string; citations: Citation[]; usage?: UsageInfo }\n | { type: \"error\"; message: string };\n\n// ── Streaming agent call ───────────────────────────────────────────────\n\nexport async function* streamOrigen(\n messages: Array<{ role: \"user\" | \"assistant\"; content: string }>,\n context: Record<string, unknown> | undefined,\n config: AgentConfig,\n apiKey?: string,\n): AsyncGenerator<StreamEvent> {\n const systemPrompt = config.systemPrompt ?? `You are ${config.appName ?? \"Origen\"}, an AI assistant. Use your tools to help the user.`;\n const modelId = config.model ?? DEFAULT_MODEL_ID;\n const maxSteps = config.maxSteps ?? 5;\n const extractCitations = config.extractCitations;\n\n // Resolve model to pi-ai Model object\n const model = resolveModel(modelId, { ollamaBaseUrl: config.ollamaBaseUrl });\n\n // Adapt tools to AgentTool format\n const adaptedTools = adaptTools(config.tools, config.getD1);\n\n // Convert messages\n let piMessages = convertMessages(messages);\n\n // Inject context into last user message\n if (context && piMessages.length > 0) {\n const lastIdx = piMessages.length - 1;\n const lastMsg = piMessages[lastIdx];\n if (lastMsg.role === \"user\") {\n piMessages[lastIdx] = {\n ...lastMsg,\n content: `[Context: ${JSON.stringify(context)}] ${typeof lastMsg.content === \"string\" ? lastMsg.content : \"\"}`,\n };\n }\n }\n\n // Resolve API key per provider\n const resolveApiKey = async (provider: string): Promise<string | undefined> => {\n if (config.getApiKey) return config.getApiKey(provider);\n if (apiKey) return apiKey;\n return undefined;\n };\n\n // Create Agent\n const agent = new Agent({\n initialState: {\n systemPrompt,\n model,\n thinkingLevel: config.thinkingLevel ?? (THINKING_MODELS.has(modelId) ? \"medium\" : \"off\"),\n tools: adaptedTools,\n messages: piMessages as any,\n },\n getApiKey: resolveApiKey,\n toolExecution: config.toolExecution ?? \"parallel\",\n });\n\n // CRITICAL: Create event stream BEFORE calling prompt.\n // createEventStream subscribes eagerly (synchronously), so no events\n // are missed even though agent.prompt() emits events during execution.\n const { stream, unsubscribe } = createEventStream(agent, extractCitations);\n\n let streamError: string | null = null;\n\n // Start prompt without awaiting — events flow through active subscription\n agent.prompt(piMessages as any).catch((error) => {\n // If prompt throws without emitting agent_end, capture error\n // to yield after the stream ends\n streamError = error instanceof Error ? error.message : String(error);\n unsubscribe(); // clean up since agent won't emit agent_end\n });\n\n try {\n for await (const event of stream) {\n yield event;\n }\n } finally {\n unsubscribe();\n }\n\n // If prompt() threw without emitting events, yield the error now\n if (streamError) {\n yield { type: \"error\", message: `Agent error: ${streamError}` };\n }\n}\n\n// ── Non-streaming agent call ──────────────────────────────────────────\n\nexport interface AgentResponse {\n message: string;\n citations: Citation[];\n usage?: UsageInfo;\n}\n\nexport async function callOrigen(\n messages: Array<{ role: \"user\" | \"assistant\"; content: string }>,\n context: Record<string, unknown> | undefined,\n config: AgentConfig,\n apiKey?: string,\n): Promise<AgentResponse> {\n let message = \"\";\n const citations: Citation[] = [];\n let usage: UsageInfo | undefined;\n\n for await (const event of streamOrigen(messages, context, config, apiKey)) {\n switch (event.type) {\n case \"text\": message += event.content; break;\n case \"done\": citations.push(...event.citations); usage = event.usage; break;\n case \"error\": throw new Error(event.message);\n }\n }\n\n return { message, citations, usage };\n}"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/agent.ts"],"sourcesContent":["/**\n * Origen — Agent Engine (v0.3)\n *\n * Multi-provider agent harness built on pi-ai + pi-agent-core.\n * Supports OpenRouter, Ollama, Anthropic, Google, and any OpenAI-compatible API.\n * Soul.md personas, streaming, parallel tool execution, abort support.\n */\n\nimport { Agent } from \"@mariozechner/pi-agent-core\";\nimport { streamSimple } from \"@mariozechner/pi-ai\";\nimport type { AgentEvent } from \"@mariozechner/pi-agent-core\";\nimport { z } from \"zod\";\nimport {\n adaptTools,\n convertMessages,\n buildContext,\n createEventStream,\n resolveModel,\n} from \"./adapter\";\nimport { DEFAULT_MODEL_ID, THINKING_MODELS, type ModelId } from \"./models\";\nimport type { D1Provider, Citation, UsageInfo } from \"./types\";\n\n// ── Tool definition ───────────────────────────────────────────────────\n\n/**\n * A tool that the host app registers with Origen.\n * Simple interface: name, description, JSON schema, and an execute function\n * that receives (args, getD1). The adapter wraps this into pi-agent-core's AgentTool.\n */\nexport interface OrigenTool {\n name: string;\n description: string;\n /** OpenAI function-calling parameter schema (JSON) */\n parameters: Record<string, unknown>;\n /** Zod schema for runtime validation (optional) */\n inputSchema?: z.ZodType;\n execute: (args: Record<string, unknown>, getD1: D1Provider) => Promise<string>;\n}\n\n// ── Agent configuration ───────────────────────────────────────────────\n\nexport interface AgentConfig {\n appName?: string;\n systemPrompt?: string;\n tools: OrigenTool[];\n getD1: D1Provider;\n model?: ModelId;\n maxSteps?: number;\n /** Custom citation extractor */\n extractCitations?: (text: string) => Citation[];\n /** Dynamic API key resolution per provider (e.g., for expiring OAuth tokens) */\n getApiKey?: (provider: string) => Promise<string | undefined>;\n /** Ollama base URL override (default: http://localhost:11434/v1) */\n ollamaBaseUrl?: string;\n /** Tool execution mode: \"parallel\" (default) or \"sequential\" */\n toolExecution?: \"sequential\" | \"parallel\";\n /** Abort signal for cancellation */\n signal?: AbortSignal;\n /** Reasoning/thinking level for models that support it */\n thinkingLevel?: \"off\" | \"minimal\" | \"low\" | \"medium\" | \"high\";\n}\n\n// ── Auth check ────────────────────────────────────────\n\nexport interface AuthCheckResult {\n authenticated: boolean;\n apiKey: string | null;\n provider?: string;\n error?: string;\n}\n\n/**\n * Provider-aware auth check. Tests key availability for each provider.\n * If no provider argument, checks OpenRouter + Ollama availability.\n */\nexport async function checkAuth(\n getApiKey: ((provider: string) => Promise<string | undefined>) | (() => Promise<string | null>),\n): Promise<AuthCheckResult> {\n // Normalize to per-provider signature\n const getProviderKey = getApiKey.length >= 1\n ? getApiKey as (provider: string) => Promise<string | undefined>\n : async (provider: string) => {\n const key = await (getApiKey as () => Promise<string | null>)();\n return key ?? undefined;\n };\n\n // Try OpenRouter first\n const orKey = await getProviderKey(\"openrouter\");\n if (orKey) return { authenticated: true, apiKey: orKey, provider: \"openrouter\" };\n\n // Try Ollama\n const ollamaKey = await getProviderKey(\"ollama\");\n if (ollamaKey) return { authenticated: true, apiKey: ollamaKey, provider: \"ollama\" };\n\n // Try Anthropic\n const anthropicKey = await getProviderKey(\"anthropic\");\n if (anthropicKey) return { authenticated: true, apiKey: anthropicKey, provider: \"anthropic\" };\n\n return {\n authenticated: false,\n apiKey: null,\n error: \"Connect your OpenRouter account or configure Ollama to enable AI-powered study.\",\n };\n}\n\n/** Convenience: check OpenRouter auth only (backward compat). */\nexport async function checkOpenRouterAuth(\n getApiKey: () => Promise<string | null>\n): Promise<AuthCheckResult> {\n const apiKey = await getApiKey();\n if (!apiKey) {\n return { authenticated: false, apiKey: null, error: \"Connect your OpenRouter account to enable AI-powered study.\" };\n }\n return { authenticated: true, apiKey, provider: \"openrouter\" };\n}\n\n// ── Stream event types ─────────────────────────────────────────────────\n\nexport type StreamEvent =\n | { type: \"reasoning\"; content: string }\n | { type: \"tool_call\"; name: string; args: Record<string, unknown> }\n | { type: \"tool_result\"; name: string; result: string }\n | { type: \"text\"; content: string }\n | { type: \"done\"; message: string; citations: Citation[]; usage?: UsageInfo }\n | { type: \"error\"; message: string };\n\n// ── Streaming agent call ───────────────────────────────────────────────\n\nexport async function* streamOrigen(\n messages: Array<{ role: \"user\" | \"assistant\"; content: string }>,\n context: Record<string, unknown> | undefined,\n config: AgentConfig,\n apiKey?: string,\n): AsyncGenerator<StreamEvent> {\n const systemPrompt = config.systemPrompt ?? `You are ${config.appName ?? \"Origen\"}, an AI assistant. Use your tools to help the user.`;\n const modelId = config.model ?? DEFAULT_MODEL_ID;\n const maxSteps = config.maxSteps ?? 5;\n const extractCitations = config.extractCitations;\n\n // Resolve model to pi-ai Model object\n const model = resolveModel(modelId, { ollamaBaseUrl: config.ollamaBaseUrl });\n\n // Adapt tools to AgentTool format\n const adaptedTools = adaptTools(config.tools, config.getD1);\n\n // Convert messages\n let piMessages = convertMessages(messages);\n\n // Inject context into last user message\n if (context && piMessages.length > 0) {\n const lastIdx = piMessages.length - 1;\n const lastMsg = piMessages[lastIdx];\n if (lastMsg.role === \"user\") {\n piMessages[lastIdx] = {\n ...lastMsg,\n content: `[Context: ${JSON.stringify(context)}] ${typeof lastMsg.content === \"string\" ? lastMsg.content : \"\"}`,\n };\n }\n }\n\n // Resolve API key per provider\n const resolveApiKey = async (provider: string): Promise<string | undefined> => {\n if (config.getApiKey) return config.getApiKey(provider);\n if (apiKey) return apiKey;\n return undefined;\n };\n\n // Create Agent\n const agent = new Agent({\n initialState: {\n systemPrompt,\n model,\n thinkingLevel: config.thinkingLevel ?? (THINKING_MODELS.has(modelId) ? \"medium\" : \"off\"),\n tools: adaptedTools,\n messages: piMessages as any,\n },\n getApiKey: resolveApiKey,\n toolExecution: config.toolExecution ?? \"parallel\",\n });\n\n // CRITICAL: Create event stream BEFORE calling prompt.\n // createEventStream subscribes eagerly (synchronously), so no events\n // are missed even though agent.prompt() emits events during execution.\n const { stream, unsubscribe } = createEventStream(agent, extractCitations);\n\n let streamError: string | null = null;\n\n // Start prompt without awaiting — events flow through active subscription\n agent.prompt(piMessages as any).catch((error) => {\n // If prompt throws without emitting agent_end, capture error\n // to yield after the stream ends\n streamError = error instanceof Error ? error.message : String(error);\n unsubscribe(); // clean up since agent won't emit agent_end\n });\n\n try {\n for await (const event of stream) {\n yield event;\n }\n } finally {\n unsubscribe();\n }\n\n // If prompt() threw without emitting events, yield the error now\n if (streamError) {\n yield { type: \"error\", message: `Agent error: ${streamError}` };\n }\n}\n\n// ── Non-streaming agent call ──────────────────────────────────────────\n\nexport interface AgentResponse {\n message: string;\n citations: Citation[];\n usage?: UsageInfo;\n}\n\nexport async function callOrigen(\n messages: Array<{ role: \"user\" | \"assistant\"; content: string }>,\n context: Record<string, unknown> | undefined,\n config: AgentConfig,\n apiKey?: string,\n): Promise<AgentResponse> {\n let message = \"\";\n const citations: Citation[] = [];\n let usage: UsageInfo | undefined;\n\n for await (const event of streamOrigen(messages, context, config, apiKey)) {\n switch (event.type) {\n case \"text\": message += event.content; break;\n case \"done\": citations.push(...event.citations); usage = event.usage; break;\n case \"error\": throw new Error(event.message);\n }\n }\n\n return { message, citations, usage };\n}"],"mappings":"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","names":[]}
|
package/dist/soul.js
CHANGED
|
@@ -1,330 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
return { frontMatter: {}, body: content };
|
|
6
|
-
}
|
|
7
|
-
const rawYaml = match[1];
|
|
8
|
-
const markdownBody = match[2] ?? "";
|
|
9
|
-
const frontMatter = parseSoulYaml(rawYaml);
|
|
10
|
-
return { frontMatter, body: markdownBody };
|
|
11
|
-
}
|
|
12
|
-
function parseSoulYaml(raw) {
|
|
13
|
-
const lines = raw.split("\n").map((l) => l.replace(/\r$/, ""));
|
|
14
|
-
const { result } = parseBlock(lines, 0, 0);
|
|
15
|
-
return result;
|
|
16
|
-
}
|
|
17
|
-
function parseBlock(lines, start, baseIndent) {
|
|
18
|
-
const result = {};
|
|
19
|
-
let i = start;
|
|
20
|
-
while (i < lines.length) {
|
|
21
|
-
const line = lines[i];
|
|
22
|
-
const indent = lineLengths(line).indent;
|
|
23
|
-
if (line.trim() === "") {
|
|
24
|
-
i++;
|
|
25
|
-
continue;
|
|
26
|
-
}
|
|
27
|
-
if (indent < baseIndent) break;
|
|
28
|
-
const trimmed = line.trim();
|
|
29
|
-
if (trimmed.startsWith("- ")) {
|
|
30
|
-
i++;
|
|
31
|
-
continue;
|
|
32
|
-
}
|
|
33
|
-
const kvMatch = trimmed.match(/^([\w][\w.-]*):\s*(.*)$/);
|
|
34
|
-
if (!kvMatch) {
|
|
35
|
-
i++;
|
|
36
|
-
continue;
|
|
37
|
-
}
|
|
38
|
-
const key = kvMatch[1];
|
|
39
|
-
const inlineVal = kvMatch[2].trim();
|
|
40
|
-
if (inlineVal === "") {
|
|
41
|
-
const nextLine = i + 1 < lines.length ? lines[i + 1] : "";
|
|
42
|
-
const nextIndent = nextLine.trim() === "" ? 0 : lineLengths(nextLine).indent;
|
|
43
|
-
const nextTrimmed = nextLine.trim();
|
|
44
|
-
if (nextIndent > indent) {
|
|
45
|
-
if (nextTrimmed.startsWith("- ")) {
|
|
46
|
-
const { items, nextLine: afterList } = parseList(lines, i + 1, nextIndent);
|
|
47
|
-
result[key] = items;
|
|
48
|
-
i = afterList;
|
|
49
|
-
} else {
|
|
50
|
-
const { result: nested, nextLine: afterNested } = parseBlock(lines, i + 1, nextIndent);
|
|
51
|
-
result[key] = nested;
|
|
52
|
-
i = afterNested;
|
|
53
|
-
}
|
|
54
|
-
} else {
|
|
55
|
-
result[key] = null;
|
|
56
|
-
i++;
|
|
57
|
-
}
|
|
58
|
-
} else {
|
|
59
|
-
result[key] = parseScalar(inlineVal);
|
|
60
|
-
i++;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return { result, nextLine: i };
|
|
64
|
-
}
|
|
65
|
-
function parseList(lines, start, baseIndent) {
|
|
66
|
-
const items = [];
|
|
67
|
-
let i = start;
|
|
68
|
-
while (i < lines.length) {
|
|
69
|
-
const line = lines[i];
|
|
70
|
-
const indent = lineLengths(line).indent;
|
|
71
|
-
if (line.trim() === "") {
|
|
72
|
-
i++;
|
|
73
|
-
continue;
|
|
74
|
-
}
|
|
75
|
-
if (indent < baseIndent) break;
|
|
76
|
-
if (!line.trim().startsWith("- ")) break;
|
|
77
|
-
const value = line.trim().slice(2).trim();
|
|
78
|
-
items.push(parseScalar(value));
|
|
79
|
-
i++;
|
|
80
|
-
}
|
|
81
|
-
return { items, nextLine: i };
|
|
82
|
-
}
|
|
83
|
-
function parseScalar(value) {
|
|
84
|
-
if (value === "null" || value === "~") return null;
|
|
85
|
-
if (value === "true") return true;
|
|
86
|
-
if (value === "false") return false;
|
|
87
|
-
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
88
|
-
return value.slice(1, -1);
|
|
89
|
-
}
|
|
90
|
-
if (/^-?\d+$/.test(value)) return parseInt(value, 10);
|
|
91
|
-
if (/^-?\d+\.\d+$/.test(value)) return parseFloat(value);
|
|
92
|
-
if (value.startsWith("[") && value.endsWith("]")) {
|
|
93
|
-
return value.slice(1, -1).split(",").map((s) => parseScalar(s.trim()));
|
|
94
|
-
}
|
|
95
|
-
return value;
|
|
96
|
-
}
|
|
97
|
-
function lineLengths(line) {
|
|
98
|
-
const match = line.match(/^(\s*)(.*)$/);
|
|
99
|
-
const indent = match ? match[1].length : 0;
|
|
100
|
-
const content = match ? match[2] : "";
|
|
101
|
-
return { indent, content };
|
|
102
|
-
}
|
|
103
|
-
var Soul = class _Soul {
|
|
104
|
-
config;
|
|
105
|
-
body;
|
|
106
|
-
constructor(config, body) {
|
|
107
|
-
this.config = config;
|
|
108
|
-
this.body = body;
|
|
109
|
-
}
|
|
110
|
-
/** Select a profile and return a new Soul with that profile merged in. */
|
|
111
|
-
selectProfile(profileName) {
|
|
112
|
-
const profiles = this.config.profiles ?? ["default"];
|
|
113
|
-
const overrides = this.config.profile_overrides ?? {};
|
|
114
|
-
const profileOverrides = overrides[profileName];
|
|
115
|
-
if (!profiles.includes(profileName) || !profileOverrides) {
|
|
116
|
-
return this;
|
|
117
|
-
}
|
|
118
|
-
const merged = mergeDeep(
|
|
119
|
-
structuredClone(this.config),
|
|
120
|
-
profileOverrides
|
|
121
|
-
);
|
|
122
|
-
return new _Soul(merged, this.body);
|
|
123
|
-
}
|
|
124
|
-
/** Build a system prompt from the Soul definition. */
|
|
125
|
-
buildPrompt() {
|
|
126
|
-
const parts = [];
|
|
127
|
-
const identity = this.config.identity;
|
|
128
|
-
if (identity) {
|
|
129
|
-
const role = identity.role ?? this.config.name;
|
|
130
|
-
const archetype = identity.archetype;
|
|
131
|
-
parts.push(`You are ${this.config.name}${archetype ? `, a ${archetype}` : ""}${role ? `. Role: ${role}` : ""}.`);
|
|
132
|
-
if (identity.domain_focus?.length) {
|
|
133
|
-
parts.push(`Domain expertise: ${identity.domain_focus.join(", ")}.`);
|
|
134
|
-
}
|
|
135
|
-
if (identity.non_goals?.length) {
|
|
136
|
-
parts.push(`Non-goals: ${identity.non_goals.join(", ")}.`);
|
|
137
|
-
}
|
|
138
|
-
} else {
|
|
139
|
-
parts.push(`You are ${this.config.name}.`);
|
|
140
|
-
}
|
|
141
|
-
const relationship = this.config.relationship;
|
|
142
|
-
if (relationship) {
|
|
143
|
-
if (relationship.stance) {
|
|
144
|
-
const stanceMap = {
|
|
145
|
-
subordinate: "You serve the user's direction.",
|
|
146
|
-
peer: "You collaborate with the user as a partner.",
|
|
147
|
-
authoritative: "You provide expert guidance and direction.",
|
|
148
|
-
adversarial: "You challenge the user's assumptions to improve outcomes."
|
|
149
|
-
};
|
|
150
|
-
parts.push(stanceMap[relationship.stance] ?? "");
|
|
151
|
-
}
|
|
152
|
-
if (relationship.user_model_default) {
|
|
153
|
-
const userModelMap = {
|
|
154
|
-
novice: "Assume the user is new to this domain. Explain terms and concepts.",
|
|
155
|
-
intermediate: "Assume moderate familiarity. Explain only when needed.",
|
|
156
|
-
expert: "Be concise. The user knows the domain well.",
|
|
157
|
-
unknown: "Adapt your explanation depth to the user's apparent knowledge level."
|
|
158
|
-
};
|
|
159
|
-
parts.push(userModelMap[relationship.user_model_default] ?? "");
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
const values = this.config.values;
|
|
163
|
-
if (values?.priorities?.length) {
|
|
164
|
-
parts.push(`
|
|
165
|
-
## Priorities (in order)
|
|
166
|
-
${values.priorities.map((p, i) => `${i + 1}. ${p}`).join("\n")}`);
|
|
167
|
-
}
|
|
168
|
-
if (values?.taboo?.length) {
|
|
169
|
-
parts.push(`
|
|
170
|
-
## Forbidden patterns
|
|
171
|
-
${values.taboo.map((t) => `- ${t}`).join("\n")}`);
|
|
172
|
-
}
|
|
173
|
-
const voice = this.config.voice;
|
|
174
|
-
if (voice) {
|
|
175
|
-
const voiceParts = ["\n## Voice & Style"];
|
|
176
|
-
if (voice.formality !== void 0) {
|
|
177
|
-
const level = voice.formality <= 30 ? "very casual" : voice.formality <= 60 ? "moderately formal" : voice.formality <= 80 ? "professional" : "highly formal";
|
|
178
|
-
voiceParts.push(`Formality: ${level} (${voice.formality}/100).`);
|
|
179
|
-
}
|
|
180
|
-
if (voice.warmth !== void 0) {
|
|
181
|
-
const level = voice.warmth <= 30 ? "cold/detached" : voice.warmth <= 60 ? "neutral" : voice.warmth <= 80 ? "warm and approachable" : "very friendly and encouraging";
|
|
182
|
-
voiceParts.push(`Tone: ${level} (${voice.warmth}/100).`);
|
|
183
|
-
}
|
|
184
|
-
if (voice.verbosity !== void 0) {
|
|
185
|
-
const level = voice.verbosity <= 25 ? "extremely concise" : voice.verbosity <= 50 ? "concise" : voice.verbosity <= 75 ? "moderate length" : "thorough and detailed";
|
|
186
|
-
voiceParts.push(`Brevity: ${level} (${voice.verbosity}/100).`);
|
|
187
|
-
}
|
|
188
|
-
if (voice.jargon !== void 0) {
|
|
189
|
-
const level = voice.jargon <= 30 ? "use plain language" : voice.jargon <= 60 ? "use moderate technical terms" : "use domain-specific terminology freely";
|
|
190
|
-
voiceParts.push(`Jargon: ${level} (${voice.jargon}/100).`);
|
|
191
|
-
}
|
|
192
|
-
if (voice.formatting) voiceParts.push(`Formatting: ${voice.formatting}.`);
|
|
193
|
-
if (voice.banned_phrases?.length) voiceParts.push(`Never say: ${voice.banned_phrases.map((p) => `"${p}"`).join(", ")}.`);
|
|
194
|
-
if (voice.preferred_phrases?.length) voiceParts.push(`Prefer: ${voice.preferred_phrases.map((p) => `"${p}"`).join(", ")}.`);
|
|
195
|
-
if (voice.emoji_policy && voice.emoji_policy !== "rare") voiceParts.push(`Emoji usage: ${voice.emoji_policy}.`);
|
|
196
|
-
parts.push(voiceParts.join(" "));
|
|
197
|
-
}
|
|
198
|
-
const interaction = this.config.interaction;
|
|
199
|
-
if (interaction) {
|
|
200
|
-
const interactionParts = ["\n## Interaction Policy"];
|
|
201
|
-
if (interaction.clarifying_questions) {
|
|
202
|
-
const qMap = {
|
|
203
|
-
never: "Never ask clarifying questions. Make reasonable assumptions.",
|
|
204
|
-
when_ambiguous: "Ask clarifying questions only when the query is ambiguous.",
|
|
205
|
-
always: "Always confirm your understanding before responding."
|
|
206
|
-
};
|
|
207
|
-
interactionParts.push(qMap[interaction.clarifying_questions] ?? "");
|
|
208
|
-
}
|
|
209
|
-
if (interaction.uncertainty) {
|
|
210
|
-
const uMap = {
|
|
211
|
-
explicit: "Explicitly mark uncertain information. Say when you're not sure.",
|
|
212
|
-
implicit: "Use hedging language (might, possibly, could) for uncertain claims.",
|
|
213
|
-
never: "Never express uncertainty. State your best answer confidently."
|
|
214
|
-
};
|
|
215
|
-
interactionParts.push(uMap[interaction.uncertainty] ?? "");
|
|
216
|
-
}
|
|
217
|
-
if (interaction.disagreement) {
|
|
218
|
-
const dMap = {
|
|
219
|
-
soft: "Disagree gently. Acknowledge the user's perspective first.",
|
|
220
|
-
neutral: "State disagreements directly but politely.",
|
|
221
|
-
direct: "Challenge incorrect views directly. Don't soften disagreements."
|
|
222
|
-
};
|
|
223
|
-
interactionParts.push(dMap[interaction.disagreement] ?? "");
|
|
224
|
-
}
|
|
225
|
-
if (interaction.confirmations === "none") {
|
|
226
|
-
interactionParts.push("Don't ask for confirmation before acting. Just do it.");
|
|
227
|
-
}
|
|
228
|
-
parts.push(interactionParts.join(" "));
|
|
229
|
-
}
|
|
230
|
-
const cognition = this.config.cognition;
|
|
231
|
-
if (cognition) {
|
|
232
|
-
const cogParts = ["\n## Cognition"];
|
|
233
|
-
if (cognition.mode) {
|
|
234
|
-
const modeMap = {
|
|
235
|
-
analytical: "Think analytically. Break problems down, examine evidence, reason step by step.",
|
|
236
|
-
creative: "Think creatively. Generate novel ideas, make unexpected connections.",
|
|
237
|
-
operational: "Focus on execution. Prioritize working solutions over theory.",
|
|
238
|
-
exploratory: "Explore broadly. Consider many angles before committing to an answer.",
|
|
239
|
-
teaching: "Teach and explain. Build understanding progressively from basics.",
|
|
240
|
-
mixed: "Adapt your thinking mode to the task at hand."
|
|
241
|
-
};
|
|
242
|
-
cogParts.push(modeMap[cognition.mode] ?? "");
|
|
243
|
-
}
|
|
244
|
-
if (cognition.verification?.fact_checking) {
|
|
245
|
-
const fcMap = {
|
|
246
|
-
none: "No explicit fact-checking.",
|
|
247
|
-
light: "Verify key claims before stating them.",
|
|
248
|
-
strict: "Always verify claims. Never state unverified information as fact."
|
|
249
|
-
};
|
|
250
|
-
cogParts.push(fcMap[cognition.verification.fact_checking] ?? "");
|
|
251
|
-
}
|
|
252
|
-
parts.push(cogParts.join(" "));
|
|
253
|
-
}
|
|
254
|
-
const safety = this.config.safety;
|
|
255
|
-
if (safety) {
|
|
256
|
-
const safetyParts = ["\n## Safety"];
|
|
257
|
-
if (safety.speculation) {
|
|
258
|
-
const specMap = {
|
|
259
|
-
allow: "You may speculate freely.",
|
|
260
|
-
mark: "Mark speculative content clearly (e.g., 'I believe', 'likely', 'possibly').",
|
|
261
|
-
avoid: "Do not speculate. Only state what you can verify."
|
|
262
|
-
};
|
|
263
|
-
safetyParts.push(specMap[safety.speculation] ?? "");
|
|
264
|
-
}
|
|
265
|
-
if (safety.refusal_style) {
|
|
266
|
-
const refMap = {
|
|
267
|
-
brief: "Refuse briefly. No lectures.",
|
|
268
|
-
explain: "Explain why you're refusing when you decline a request.",
|
|
269
|
-
policy_cite: "Cite specific policies when refusing requests."
|
|
270
|
-
};
|
|
271
|
-
safetyParts.push(refMap[safety.refusal_style] ?? "");
|
|
272
|
-
}
|
|
273
|
-
if (safety.no_fabrication) safetyParts.push("Never fabricate information. If you don't know, say so.");
|
|
274
|
-
if (safety.no_false_certainty) safetyParts.push("Never present uncertain information as certain.");
|
|
275
|
-
parts.push(safetyParts.join(" "));
|
|
276
|
-
}
|
|
277
|
-
const actions = this.config.actions;
|
|
278
|
-
if (actions) {
|
|
279
|
-
const actParts = ["\n## Actions"];
|
|
280
|
-
const toolMap = {
|
|
281
|
-
avoid_tools: "Minimize tool use. Answer from knowledge when possible.",
|
|
282
|
-
when_needed: "Use tools when they would improve your answer.",
|
|
283
|
-
prefer_tools: "Proactively use available tools. Always verify with tools rather than memory."
|
|
284
|
-
};
|
|
285
|
-
actParts.push(toolMap[actions.when_to_use_tools] ?? "");
|
|
286
|
-
if (actions.explain_actions === "brief" || actions.explain_actions === "full") {
|
|
287
|
-
actParts.push(actions.explain_actions === "full" ? "Explain what you're doing before and after tool use." : "Briefly explain tool use.");
|
|
288
|
-
}
|
|
289
|
-
parts.push(actParts.join(" "));
|
|
290
|
-
}
|
|
291
|
-
if (this.body.trim()) {
|
|
292
|
-
parts.push(`
|
|
293
|
-
## Additional Instructions
|
|
294
|
-
|
|
295
|
-
${this.body.trim()}`);
|
|
296
|
-
}
|
|
297
|
-
return parts.join("\n\n");
|
|
298
|
-
}
|
|
299
|
-
get defaultProfile() {
|
|
300
|
-
return this.config.profiles?.[0] ?? "default";
|
|
301
|
-
}
|
|
302
|
-
get profileNames() {
|
|
303
|
-
return this.config.profiles ?? ["default"];
|
|
304
|
-
}
|
|
305
|
-
};
|
|
306
|
-
function loadSoul(content) {
|
|
307
|
-
const { frontMatter, body } = parseYamlFrontMatter(content);
|
|
308
|
-
const config = frontMatter;
|
|
309
|
-
if (!config.id) throw new Error("Soul.md missing required field: id");
|
|
310
|
-
if (!config.name) throw new Error("Soul.md missing required field: name");
|
|
311
|
-
return new Soul(config, body);
|
|
312
|
-
}
|
|
313
|
-
function mergeDeep(base, overlay) {
|
|
314
|
-
const result = { ...base };
|
|
315
|
-
for (const key of Object.keys(overlay)) {
|
|
316
|
-
const baseVal = result[key];
|
|
317
|
-
const overVal = overlay[key];
|
|
318
|
-
if (overVal === null) {
|
|
319
|
-
result[key] = null;
|
|
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);
|
|
322
|
-
} else {
|
|
323
|
-
result[key] = overVal;
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
return result;
|
|
327
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
Soul,
|
|
3
|
+
loadSoul
|
|
4
|
+
} from "./chunk-QF3XSUMT.js";
|
|
328
5
|
export {
|
|
329
6
|
Soul,
|
|
330
7
|
loadSoul
|
package/dist/soul.js.map
CHANGED
|
@@ -1 +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":[]}
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/package.json
CHANGED