@ethosagent/core 0.2.1 → 0.2.6
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/index.d.ts +68 -2
- package/dist/index.js +210 -17
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as _ethosagent_types from '@ethosagent/types';
|
|
2
|
+
import { HookRegistry, LLMProvider, ToolRegistry, PersonalityRegistry, MemoryProvider, SessionStore, ContextInjector, Storage, Session, SessionFilter, StoredMessage, SessionUsage, SearchResult, MemoryLoadContext, MemoryUpdate, PersonalityConfig, VoidHooks, ModifyingHooks, ClaimingHooks, Message, ToolDefinitionLite, CompletionOptions, CompletionChunk, Tool, ToolFilterOpts, ToolContext, ToolResult } from '@ethosagent/types';
|
|
2
3
|
|
|
3
4
|
type AgentEvent = {
|
|
4
5
|
type: 'text_delta';
|
|
@@ -48,6 +49,18 @@ type AgentEvent = {
|
|
|
48
49
|
} | {
|
|
49
50
|
type: 'context_meta';
|
|
50
51
|
data: Record<string, unknown>;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Emitted once at the very start of each turn, before any LLM call.
|
|
55
|
+
* Carries the resolved provider/model and the routing source so consumers
|
|
56
|
+
* (TUI status bar, CLI verbose mode, telemetry) can show the effective model.
|
|
57
|
+
* `source` reflects which routing rule selected the model (see model_update.md).
|
|
58
|
+
*/
|
|
59
|
+
| {
|
|
60
|
+
type: 'run_start';
|
|
61
|
+
provider: string;
|
|
62
|
+
model: string;
|
|
63
|
+
source: 'team-coordinator' | 'team-personality' | 'personality' | 'global';
|
|
51
64
|
};
|
|
52
65
|
interface AgentLoopConfig {
|
|
53
66
|
llm: LLMProvider;
|
|
@@ -140,7 +153,19 @@ declare class AgentLoop {
|
|
|
140
153
|
private readonly modelRouting;
|
|
141
154
|
private readonly storage?;
|
|
142
155
|
private readonly dataDir?;
|
|
156
|
+
/** Per-session accumulated spend in USD. Keyed by sessionKey. Reset via resetSessionCost(). */
|
|
157
|
+
private readonly sessionCosts;
|
|
143
158
|
constructor(config: AgentLoopConfig);
|
|
159
|
+
/** Returns all available tools for inventory display (e.g. TUI splash screen). */
|
|
160
|
+
getAvailableTools(): _ethosagent_types.Tool[];
|
|
161
|
+
/** Returns all registered personalities for inventory display. */
|
|
162
|
+
getPersonalityIds(): string[];
|
|
163
|
+
/** Returns the budget cap for the given personality (undefined = no cap). */
|
|
164
|
+
getPersonalityBudgetCap(personalityId?: string): number | undefined;
|
|
165
|
+
/** Returns accumulated session spend in USD (0 if no spend recorded yet). */
|
|
166
|
+
getSessionCost(sessionKey: string): number;
|
|
167
|
+
/** Resets the session spend counter — call after /new or /personality switch. */
|
|
168
|
+
resetSessionCost(sessionKey: string): void;
|
|
144
169
|
run(text: string, opts?: RunOptions): AsyncGenerator<AgentEvent>;
|
|
145
170
|
private handleChunk;
|
|
146
171
|
private toLLMMessages;
|
|
@@ -218,6 +243,38 @@ declare class PluginRegistry<T, C = unknown> {
|
|
|
218
243
|
types(): string[];
|
|
219
244
|
}
|
|
220
245
|
|
|
246
|
+
interface ChainedProviderOptions {
|
|
247
|
+
/** Milliseconds to cool a provider down after a failover-eligible error. Default: 60000. */
|
|
248
|
+
cooldownMs?: number;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Wraps multiple LLMProviders with automatic failover.
|
|
252
|
+
*
|
|
253
|
+
* On a failover-eligible error (rate_limit, overloaded, timeout, network, unknown),
|
|
254
|
+
* the failing provider is put on cooldown and the next provider is tried.
|
|
255
|
+
* Non-retriable errors (auth, content_filter, context_overflow) propagate immediately.
|
|
256
|
+
*
|
|
257
|
+
* Once streaming starts (first CompletionChunk received), the stream is committed —
|
|
258
|
+
* no mid-stream retries. Failover only happens on errors thrown before the first chunk.
|
|
259
|
+
*
|
|
260
|
+
* Error codes:
|
|
261
|
+
* ALL_PROVIDERS_FAILED — every provider failed with a failover-eligible error
|
|
262
|
+
* ALL_PROVIDERS_REJECT_MODEL — every provider failed with model_not_found
|
|
263
|
+
*/
|
|
264
|
+
declare class ChainedProvider implements LLMProvider {
|
|
265
|
+
private readonly entries;
|
|
266
|
+
private readonly cooldownMs;
|
|
267
|
+
constructor(providers: LLMProvider[], opts?: ChainedProviderOptions);
|
|
268
|
+
get name(): string;
|
|
269
|
+
get model(): string;
|
|
270
|
+
get maxContextTokens(): number;
|
|
271
|
+
get supportsCaching(): boolean;
|
|
272
|
+
get supportsThinking(): boolean;
|
|
273
|
+
complete(messages: Message[], tools: ToolDefinitionLite[], options: CompletionOptions): AsyncIterable<CompletionChunk>;
|
|
274
|
+
countTokens(messages: Message[]): Promise<number>;
|
|
275
|
+
private activeEntry;
|
|
276
|
+
}
|
|
277
|
+
|
|
221
278
|
declare class DefaultToolRegistry implements ToolRegistry {
|
|
222
279
|
private readonly tools;
|
|
223
280
|
register(tool: Tool, opts?: {
|
|
@@ -233,6 +290,15 @@ declare class DefaultToolRegistry implements ToolRegistry {
|
|
|
233
290
|
description: string;
|
|
234
291
|
parameters: Record<string, unknown>;
|
|
235
292
|
}[];
|
|
293
|
+
/**
|
|
294
|
+
* Computes the effective tool reach for a personality:
|
|
295
|
+
* personality.toolset (built-in tools)
|
|
296
|
+
* ∪ tools from MCP servers in personality.mcp_servers
|
|
297
|
+
* ∪ tools from plugins in personality.plugins
|
|
298
|
+
*
|
|
299
|
+
* Used by IngestFilter to check skill.required_tools ⊆ effective_reach.
|
|
300
|
+
*/
|
|
301
|
+
toolNamesForPersonality(personality: PersonalityConfig): Set<string>;
|
|
236
302
|
executeParallel(calls: Array<{
|
|
237
303
|
toolCallId: string;
|
|
238
304
|
name: string;
|
|
@@ -244,4 +310,4 @@ declare class DefaultToolRegistry implements ToolRegistry {
|
|
|
244
310
|
}>>;
|
|
245
311
|
}
|
|
246
312
|
|
|
247
|
-
export { type AgentEvent, AgentLoop, type AgentLoopConfig, DefaultHookRegistry, DefaultPersonalityRegistry, DefaultToolRegistry, InMemorySessionStore, NoopMemoryProvider, type PluginFactory, PluginRegistry, type RunOptions };
|
|
313
|
+
export { type AgentEvent, AgentLoop, type AgentLoopConfig, ChainedProvider, type ChainedProviderOptions, DefaultHookRegistry, DefaultPersonalityRegistry, DefaultToolRegistry, InMemorySessionStore, NoopMemoryProvider, type PluginFactory, PluginRegistry, type RunOptions };
|
package/dist/index.js
CHANGED
|
@@ -405,7 +405,8 @@ var DefaultToolRegistry = class {
|
|
|
405
405
|
(e) => !e.tool.isAvailable || e.tool.isAvailable()
|
|
406
406
|
);
|
|
407
407
|
const filtered = entries.filter((e) => {
|
|
408
|
-
|
|
408
|
+
const isMcpOrPluginTool = e.tool.name.startsWith("mcp__") || e.pluginId !== void 0;
|
|
409
|
+
if (!isMcpOrPluginTool && !e.tool.alwaysInclude && allowedTools && allowedTools.length > 0 && !allowedTools.includes(e.tool.name))
|
|
409
410
|
return false;
|
|
410
411
|
return passesFilter(e, filterOpts);
|
|
411
412
|
});
|
|
@@ -415,6 +416,39 @@ var DefaultToolRegistry = class {
|
|
|
415
416
|
parameters: e.tool.schema
|
|
416
417
|
}));
|
|
417
418
|
}
|
|
419
|
+
/**
|
|
420
|
+
* Computes the effective tool reach for a personality:
|
|
421
|
+
* personality.toolset (built-in tools)
|
|
422
|
+
* ∪ tools from MCP servers in personality.mcp_servers
|
|
423
|
+
* ∪ tools from plugins in personality.plugins
|
|
424
|
+
*
|
|
425
|
+
* Used by IngestFilter to check skill.required_tools ⊆ effective_reach.
|
|
426
|
+
*/
|
|
427
|
+
toolNamesForPersonality(personality) {
|
|
428
|
+
const reach = /* @__PURE__ */ new Set();
|
|
429
|
+
for (const [name, entry] of this.tools) {
|
|
430
|
+
const isMcp = name.startsWith("mcp__");
|
|
431
|
+
const isPlugin = entry.pluginId !== void 0;
|
|
432
|
+
if (!isMcp && !isPlugin) {
|
|
433
|
+
const toolset = personality.toolset;
|
|
434
|
+
if (!toolset || toolset.length === 0 || toolset.includes(name)) {
|
|
435
|
+
reach.add(name);
|
|
436
|
+
}
|
|
437
|
+
} else if (isMcp) {
|
|
438
|
+
const server = mcpServerName(name);
|
|
439
|
+
const allowed = personality.mcp_servers;
|
|
440
|
+
if (server && allowed?.includes(server)) {
|
|
441
|
+
reach.add(name);
|
|
442
|
+
}
|
|
443
|
+
} else if (isPlugin) {
|
|
444
|
+
const allowed = personality.plugins;
|
|
445
|
+
if (entry.pluginId && allowed?.includes(entry.pluginId)) {
|
|
446
|
+
reach.add(name);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
return reach;
|
|
451
|
+
}
|
|
418
452
|
// Runs all tool calls in parallel. Results are returned in input order.
|
|
419
453
|
// Budget is split evenly across parallel calls; each result is post-trimmed to budget.
|
|
420
454
|
// allowedTools + filterOpts enforce tool access at execution time (belt-and-suspenders).
|
|
@@ -422,25 +456,26 @@ var DefaultToolRegistry = class {
|
|
|
422
456
|
const perCallBudget = Math.floor(ctx.resultBudgetChars / Math.max(calls.length, 1));
|
|
423
457
|
const results = await Promise.allSettled(
|
|
424
458
|
calls.map(async (call) => {
|
|
425
|
-
|
|
459
|
+
const entry = this.tools.get(call.name);
|
|
460
|
+
if (!entry) {
|
|
426
461
|
return {
|
|
427
462
|
toolCallId: call.toolCallId,
|
|
428
463
|
name: call.name,
|
|
429
464
|
result: {
|
|
430
465
|
ok: false,
|
|
431
|
-
error: `
|
|
466
|
+
error: `Unknown tool: ${call.name}`,
|
|
432
467
|
code: "not_available"
|
|
433
468
|
}
|
|
434
469
|
};
|
|
435
470
|
}
|
|
436
|
-
const
|
|
437
|
-
if (!
|
|
471
|
+
const isMcpOrPluginTool = call.name.startsWith("mcp__") || entry.pluginId !== void 0;
|
|
472
|
+
if (!isMcpOrPluginTool && allowedTools && allowedTools.length > 0 && !allowedTools.includes(call.name)) {
|
|
438
473
|
return {
|
|
439
474
|
toolCallId: call.toolCallId,
|
|
440
475
|
name: call.name,
|
|
441
476
|
result: {
|
|
442
477
|
ok: false,
|
|
443
|
-
error: `
|
|
478
|
+
error: `Tool ${call.name} is not permitted for this personality`,
|
|
444
479
|
code: "not_available"
|
|
445
480
|
}
|
|
446
481
|
};
|
|
@@ -537,6 +572,8 @@ var AgentLoop = class {
|
|
|
537
572
|
modelRouting;
|
|
538
573
|
storage;
|
|
539
574
|
dataDir;
|
|
575
|
+
/** Per-session accumulated spend in USD. Keyed by sessionKey. Reset via resetSessionCost(). */
|
|
576
|
+
sessionCosts = /* @__PURE__ */ new Map();
|
|
540
577
|
constructor(config) {
|
|
541
578
|
this.llm = config.llm;
|
|
542
579
|
this.tools = config.tools ?? new DefaultToolRegistry();
|
|
@@ -558,6 +595,27 @@ var AgentLoop = class {
|
|
|
558
595
|
if (config.storage) this.storage = config.storage;
|
|
559
596
|
if (config.dataDir) this.dataDir = config.dataDir;
|
|
560
597
|
}
|
|
598
|
+
/** Returns all available tools for inventory display (e.g. TUI splash screen). */
|
|
599
|
+
getAvailableTools() {
|
|
600
|
+
return this.tools.getAvailable();
|
|
601
|
+
}
|
|
602
|
+
/** Returns all registered personalities for inventory display. */
|
|
603
|
+
getPersonalityIds() {
|
|
604
|
+
return this.personalities.list().map((p) => p.id);
|
|
605
|
+
}
|
|
606
|
+
/** Returns the budget cap for the given personality (undefined = no cap). */
|
|
607
|
+
getPersonalityBudgetCap(personalityId) {
|
|
608
|
+
const p = (personalityId ? this.personalities.get(personalityId) : null) ?? this.personalities.getDefault();
|
|
609
|
+
return p.budgetCapUsd;
|
|
610
|
+
}
|
|
611
|
+
/** Returns accumulated session spend in USD (0 if no spend recorded yet). */
|
|
612
|
+
getSessionCost(sessionKey) {
|
|
613
|
+
return this.sessionCosts.get(sessionKey) ?? 0;
|
|
614
|
+
}
|
|
615
|
+
/** Resets the session spend counter — call after /new or /personality switch. */
|
|
616
|
+
resetSessionCost(sessionKey) {
|
|
617
|
+
this.sessionCosts.delete(sessionKey);
|
|
618
|
+
}
|
|
561
619
|
async *run(text, opts = {}) {
|
|
562
620
|
const abortSignal = opts.abortSignal ?? new AbortController().signal;
|
|
563
621
|
const sessionKey = opts.sessionKey ?? `${this.platform}:default`;
|
|
@@ -580,12 +638,29 @@ var AgentLoop = class {
|
|
|
580
638
|
});
|
|
581
639
|
const sessionId = ethosSession.id;
|
|
582
640
|
const personality = (opts.personalityId ? this.personalities.get(opts.personalityId) : null) ?? this.personalities.getDefault();
|
|
583
|
-
const
|
|
641
|
+
const currentSpend = this.sessionCosts.get(sessionKey) ?? 0;
|
|
642
|
+
if (personality.budgetCapUsd != null && currentSpend >= personality.budgetCapUsd) {
|
|
643
|
+
yield {
|
|
644
|
+
type: "error",
|
|
645
|
+
error: `Budget cap of $${personality.budgetCapUsd.toFixed(2)} exceeded for this session ($${currentSpend.toFixed(4)} spent). Use /budget reset to start a new budget window.`,
|
|
646
|
+
code: "BUDGET_EXCEEDED"
|
|
647
|
+
};
|
|
648
|
+
yield { type: "done", text: "", turnCount: 0 };
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
const personalityOverride = this.modelRouting[personality.id];
|
|
652
|
+
const effectiveModel = personalityOverride ?? this.llm.model;
|
|
584
653
|
const modelOverride = effectiveModel !== this.llm.model ? effectiveModel : void 0;
|
|
654
|
+
yield {
|
|
655
|
+
type: "run_start",
|
|
656
|
+
provider: this.llm.name,
|
|
657
|
+
model: effectiveModel,
|
|
658
|
+
source: personalityOverride ? "personality" : "global"
|
|
659
|
+
};
|
|
585
660
|
const allowedTools = personality.toolset?.length ? personality.toolset : void 0;
|
|
586
661
|
const allowedPlugins = personality.plugins ?? [];
|
|
587
662
|
const filterOpts = {
|
|
588
|
-
allowedMcpServers: personality.mcp_servers,
|
|
663
|
+
allowedMcpServers: personality.mcp_servers ?? [],
|
|
589
664
|
allowedPlugins
|
|
590
665
|
};
|
|
591
666
|
await this.hooks.fireVoid(
|
|
@@ -724,20 +799,32 @@ ${memCtx.content}`);
|
|
|
724
799
|
};
|
|
725
800
|
try {
|
|
726
801
|
armWatchdog();
|
|
727
|
-
const stream = this.llm.complete(
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
802
|
+
const stream = this.llm.complete(
|
|
803
|
+
llmMessages,
|
|
804
|
+
this.tools.toDefinitions(allowedTools, filterOpts),
|
|
805
|
+
{
|
|
806
|
+
system: systemPrompt,
|
|
807
|
+
cacheSystemPrompt: true,
|
|
808
|
+
abortSignal: combinedSignal,
|
|
809
|
+
...modelOverride ? { modelOverride } : {}
|
|
810
|
+
}
|
|
811
|
+
);
|
|
733
812
|
for await (const chunk of stream) {
|
|
734
813
|
if (abortSignal.aborted) break;
|
|
735
814
|
if (watchdogController.signal.aborted) break;
|
|
736
815
|
armWatchdog();
|
|
737
|
-
|
|
816
|
+
for (const event of this.handleChunk(chunk, pendingToolCalls, (t) => {
|
|
738
817
|
chunkText += t;
|
|
739
818
|
fullText += t;
|
|
740
|
-
})
|
|
819
|
+
})) {
|
|
820
|
+
if (event.type === "usage") {
|
|
821
|
+
this.sessionCosts.set(
|
|
822
|
+
sessionKey,
|
|
823
|
+
(this.sessionCosts.get(sessionKey) ?? 0) + event.estimatedCostUsd
|
|
824
|
+
);
|
|
825
|
+
}
|
|
826
|
+
yield event;
|
|
827
|
+
}
|
|
741
828
|
}
|
|
742
829
|
disarmWatchdog();
|
|
743
830
|
if (watchdogController.signal.aborted && !abortSignal.aborted) {
|
|
@@ -924,7 +1011,11 @@ ${memCtx.content}`);
|
|
|
924
1011
|
llmMessages.push({ role: "user", content: toolResultContent });
|
|
925
1012
|
}
|
|
926
1013
|
await this.session.updateUsage(sessionId, { apiCallCount: turnCount });
|
|
927
|
-
await this.hooks.fireVoid(
|
|
1014
|
+
await this.hooks.fireVoid(
|
|
1015
|
+
"agent_done",
|
|
1016
|
+
{ sessionId, text: fullText, turnCount },
|
|
1017
|
+
allowedPlugins
|
|
1018
|
+
);
|
|
928
1019
|
yield { type: "done", text: fullText, turnCount };
|
|
929
1020
|
}
|
|
930
1021
|
*handleChunk(chunk, pendingToolCalls, onText) {
|
|
@@ -1061,8 +1152,110 @@ var PluginRegistry = class {
|
|
|
1061
1152
|
return [...this.factories.keys()];
|
|
1062
1153
|
}
|
|
1063
1154
|
};
|
|
1155
|
+
|
|
1156
|
+
// src/providers/chained-provider.ts
|
|
1157
|
+
function classifyProviderError(err) {
|
|
1158
|
+
const msg = (err instanceof Error ? err.message : String(err)).toLowerCase();
|
|
1159
|
+
if (msg.includes("401") || msg.includes("403") || msg.includes("authentication") || msg.includes("api key") || msg.includes("unauthorized"))
|
|
1160
|
+
return "auth";
|
|
1161
|
+
if (msg.includes("429") || msg.includes("rate limit") || msg.includes("rate_limit") || msg.includes("too many requests"))
|
|
1162
|
+
return "rate_limit";
|
|
1163
|
+
if (msg.includes("529") || msg.includes("overloaded") || msg.includes("503") || msg.includes("service unavailable"))
|
|
1164
|
+
return "overloaded";
|
|
1165
|
+
if (msg.includes("context") && (msg.includes("overflow") || msg.includes("too long") || msg.includes("too large")))
|
|
1166
|
+
return "context_overflow";
|
|
1167
|
+
if (msg.includes("content") && (msg.includes("filter") || msg.includes("policy")))
|
|
1168
|
+
return "content_filter";
|
|
1169
|
+
if (msg.includes("404") || msg.includes("model not found") || msg.includes("model_not_found") || msg.includes("no such model"))
|
|
1170
|
+
return "model_not_found";
|
|
1171
|
+
if (msg.includes("timeout") || msg.includes("timed out") || msg.includes("etimedout"))
|
|
1172
|
+
return "timeout";
|
|
1173
|
+
if (msg.includes("network") || msg.includes("econnrefused") || msg.includes("enotfound") || msg.includes("socket hang up"))
|
|
1174
|
+
return "network";
|
|
1175
|
+
return "unknown";
|
|
1176
|
+
}
|
|
1177
|
+
var FAILOVER_REASONS = /* @__PURE__ */ new Set([
|
|
1178
|
+
"rate_limit",
|
|
1179
|
+
"overloaded",
|
|
1180
|
+
"timeout",
|
|
1181
|
+
"network",
|
|
1182
|
+
"unknown",
|
|
1183
|
+
"model_not_found"
|
|
1184
|
+
]);
|
|
1185
|
+
function shouldFailover(reason) {
|
|
1186
|
+
return FAILOVER_REASONS.has(reason);
|
|
1187
|
+
}
|
|
1188
|
+
var ChainedProvider = class {
|
|
1189
|
+
entries;
|
|
1190
|
+
cooldownMs;
|
|
1191
|
+
constructor(providers, opts = {}) {
|
|
1192
|
+
if (providers.length === 0) throw new Error("ChainedProvider requires at least one provider");
|
|
1193
|
+
this.entries = providers.map((p) => ({ provider: p, cooldownUntil: 0 }));
|
|
1194
|
+
this.cooldownMs = opts.cooldownMs ?? 6e4;
|
|
1195
|
+
}
|
|
1196
|
+
get name() {
|
|
1197
|
+
return `chain(${this.entries.map((e) => e.provider.name).join(",")})`;
|
|
1198
|
+
}
|
|
1199
|
+
get model() {
|
|
1200
|
+
return this.activeEntry()?.provider.model ?? this.entries[0]?.provider.model ?? "";
|
|
1201
|
+
}
|
|
1202
|
+
get maxContextTokens() {
|
|
1203
|
+
return this.activeEntry()?.provider.maxContextTokens ?? 2e5;
|
|
1204
|
+
}
|
|
1205
|
+
get supportsCaching() {
|
|
1206
|
+
return this.activeEntry()?.provider.supportsCaching ?? false;
|
|
1207
|
+
}
|
|
1208
|
+
get supportsThinking() {
|
|
1209
|
+
return this.activeEntry()?.provider.supportsThinking ?? false;
|
|
1210
|
+
}
|
|
1211
|
+
async *complete(messages, tools, options) {
|
|
1212
|
+
const now = Date.now();
|
|
1213
|
+
const available = this.entries.filter((e) => e.cooldownUntil <= now);
|
|
1214
|
+
if (available.length === 0) {
|
|
1215
|
+
const soonest = this.entries.reduce((a, b) => a.cooldownUntil < b.cooldownUntil ? a : b);
|
|
1216
|
+
available.push(soonest);
|
|
1217
|
+
}
|
|
1218
|
+
const reasons = [];
|
|
1219
|
+
for (const entry of available) {
|
|
1220
|
+
try {
|
|
1221
|
+
const stream = entry.provider.complete(messages, tools, options);
|
|
1222
|
+
for await (const chunk of stream) {
|
|
1223
|
+
yield chunk;
|
|
1224
|
+
}
|
|
1225
|
+
return;
|
|
1226
|
+
} catch (err) {
|
|
1227
|
+
const reason = classifyProviderError(err);
|
|
1228
|
+
reasons.push(reason);
|
|
1229
|
+
if (!shouldFailover(reason)) {
|
|
1230
|
+
throw err;
|
|
1231
|
+
}
|
|
1232
|
+
entry.cooldownUntil = Date.now() + this.cooldownMs;
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
const allModelNotFound = reasons.length > 0 && reasons.every((r) => r === "model_not_found");
|
|
1236
|
+
if (allModelNotFound) {
|
|
1237
|
+
throw new Error(
|
|
1238
|
+
`ALL_PROVIDERS_REJECT_MODEL: no provider in the chain supports the requested model. Tried: ${available.map((e) => `${e.provider.name}/${e.provider.model}`).join(", ")}`
|
|
1239
|
+
);
|
|
1240
|
+
}
|
|
1241
|
+
throw new Error(
|
|
1242
|
+
`ALL_PROVIDERS_FAILED: all providers in the chain have been exhausted. Tried: ${available.map((e, i) => `${e.provider.name}/${e.provider.model} (${reasons[i] ?? "unknown"})`).join(", ")}`
|
|
1243
|
+
);
|
|
1244
|
+
}
|
|
1245
|
+
async countTokens(messages) {
|
|
1246
|
+
const entry = this.activeEntry();
|
|
1247
|
+
if (!entry) return 0;
|
|
1248
|
+
return entry.provider.countTokens(messages);
|
|
1249
|
+
}
|
|
1250
|
+
// Returns the first non-cooled provider, or undefined if all are cooled.
|
|
1251
|
+
activeEntry() {
|
|
1252
|
+
const now = Date.now();
|
|
1253
|
+
return this.entries.find((e) => e.cooldownUntil <= now);
|
|
1254
|
+
}
|
|
1255
|
+
};
|
|
1064
1256
|
export {
|
|
1065
1257
|
AgentLoop,
|
|
1258
|
+
ChainedProvider,
|
|
1066
1259
|
DefaultHookRegistry,
|
|
1067
1260
|
DefaultPersonalityRegistry,
|
|
1068
1261
|
DefaultToolRegistry,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/agent-loop.ts","../../storage-fs/src/fs-storage.ts","../../storage-fs/src/in-memory-storage.ts","../../storage-fs/src/scoped-storage.ts","../src/defaults/in-memory-session.ts","../src/defaults/noop-memory.ts","../src/defaults/noop-personality.ts","../src/hook-registry.ts","../src/tool-registry.ts","../src/plugin-registry.ts"],"sourcesContent":["import { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { ScopedStorage } from '@ethosagent/storage-fs';\nimport type {\n CompletionChunk,\n ContextInjector,\n HookRegistry,\n LLMProvider,\n MemoryProvider,\n Message,\n MessageContent,\n PersonalityConfig,\n PersonalityRegistry,\n PromptContext,\n SessionStore,\n Storage,\n StoredMessage,\n ToolFilterOpts,\n ToolRegistry,\n ToolResult,\n} from '@ethosagent/types';\n\nimport { InMemorySessionStore } from './defaults/in-memory-session';\nimport { NoopMemoryProvider } from './defaults/noop-memory';\nimport { DefaultPersonalityRegistry } from './defaults/noop-personality';\nimport { DefaultHookRegistry } from './hook-registry';\nimport { DefaultToolRegistry } from './tool-registry';\n\n// ---------------------------------------------------------------------------\n// Agent events emitted by run()\n// ---------------------------------------------------------------------------\n\nexport type AgentEvent =\n | { type: 'text_delta'; text: string }\n | { type: 'thinking_delta'; thinking: string }\n | { type: 'tool_start'; toolCallId: string; toolName: string; args: unknown }\n // Phase 30.2 — `audience` gates whether channel adapters / chat.ts surface\n // this event to the user. Default is `'internal'`; tools opt in to `'user'`\n // per event. Framework-emitted budget warnings are `'user'` (see step 7).\n | {\n type: 'tool_progress';\n toolName: string;\n message: string;\n percent?: number;\n audience: 'internal' | 'user';\n }\n | {\n type: 'tool_end';\n toolCallId: string;\n toolName: string;\n ok: boolean;\n durationMs: number;\n // Phase 30.2 — same boundary applies to tool_end success rendering.\n // Failures (`ok: false`) ignore the field and always render.\n audience?: 'internal' | 'user';\n /**\n * Tool output body — the success value when `ok`, or the error\n * message when `ok: false`. Optional so consumers that only care\n * about the status (CLI ASCII chips, telemetry) can ignore it.\n * The web chip surfaces this on expand-on-click without a\n * follow-up history fetch.\n */\n result?: string;\n }\n | { type: 'usage'; inputTokens: number; outputTokens: number; estimatedCostUsd: number }\n | { type: 'error'; error: string; code: string }\n | { type: 'done'; text: string; turnCount: number }\n // Emitted once after context injectors run; carries any metadata they wrote to PromptContext.meta.\n | { type: 'context_meta'; data: Record<string, unknown> };\n\n// ---------------------------------------------------------------------------\n// Config\n// ---------------------------------------------------------------------------\n\nexport interface AgentLoopConfig {\n llm: LLMProvider;\n tools?: ToolRegistry;\n personalities?: PersonalityRegistry;\n memory?: MemoryProvider;\n session?: SessionStore;\n hooks?: HookRegistry;\n injectors?: ContextInjector[];\n /**\n * Maps each plugin-registered injector to its plugin id so AgentLoop can\n * gate injectors by personality. Built-in injectors are absent (always fire).\n * Populated by PluginApiImpl.registerInjector(); passed through from wiring.\n */\n injectorPluginIds?: Map<ContextInjector, string>;\n /**\n * Base Storage instance handed to tools via `ToolContext.storage` after\n * being decorated with a ScopedStorage that enforces the active\n * personality's `fs_reach` allowlist. When unset, ToolContext.storage is\n * left undefined and tools fall back to unrestricted node:fs (legacy\n * behavior — existing CLI/TUI tests don't need a storage instance).\n */\n storage?: Storage;\n /**\n * Absolute path to ~/.ethos/ used for `${ETHOS_HOME}` substitution in\n * `fs_reach` paths. Defaults to `${HOME}/.ethos`. Required only when\n * `storage` is set.\n */\n dataDir?: string;\n // Maps personality ID → model ID. Resolution: modelRouting[id] → personality.model → llm.model\n modelRouting?: Record<string, string>;\n options?: {\n maxIterations?: number;\n historyLimit?: number;\n platform?: string;\n workingDir?: string;\n resultBudgetChars?: number;\n /**\n * Hard cap on total tool calls per user turn (across all LLM iterations).\n * Defaults to 20. Trips a `tool_progress` warning and exits cleanly.\n * See plan/IMPROVEMENT.md P1-3.\n */\n maxToolCallsPerTurn?: number;\n /**\n * Hard cap on the number of times the same tool name can be invoked in a\n * single turn. Catches the \"infinite loop on a single tool\" failure mode\n * (e.g. tts loop reported as OpenClaw #67744). Defaults to 5.\n */\n maxIdenticalToolCalls?: number;\n /**\n * Default streaming watchdog in milliseconds. If no chunk arrives from the\n * LLM within this window, the agent aborts the stream and emits an error.\n * Reset on every chunk. Personalities can override via\n * `personality.streamingTimeoutMs`. Defaults to 120000 (2 minutes).\n */\n streamingTimeoutMs?: number;\n };\n}\n\nexport interface RunOptions {\n sessionKey?: string;\n personalityId?: string;\n abortSignal?: AbortSignal;\n /**\n * Identifier surfaced to tools as `ToolContext.agentId`. Delegation tools\n * use this to thread spawn depth (`depth:N`) into child loops so\n * `MAX_SPAWN_DEPTH` can be enforced across recursive sub-agent calls.\n */\n agentId?: string;\n}\n\n// ---------------------------------------------------------------------------\n// AgentLoop\n// ---------------------------------------------------------------------------\n\nexport class AgentLoop {\n private readonly llm: LLMProvider;\n private readonly tools: ToolRegistry;\n private readonly personalities: PersonalityRegistry;\n private readonly memory: MemoryProvider;\n private readonly session: SessionStore;\n /** Public so surfaces (web, ACP) can register late-binding hooks they own\n * without re-running the whole wiring factory. The CLI/TUI register hooks\n * before construction; web registers an approval hook after createAgentLoop\n * returns. */\n readonly hooks: HookRegistry;\n private readonly injectors: ContextInjector[];\n private readonly injectorPluginIds: Map<ContextInjector, string>;\n private readonly maxIterations: number;\n private readonly historyLimit: number;\n private readonly platform: string;\n private readonly workingDir: string;\n private readonly resultBudgetChars: number;\n private readonly maxToolCallsPerTurn: number;\n private readonly maxIdenticalToolCalls: number;\n private readonly streamingTimeoutMs: number;\n private readonly modelRouting: Record<string, string>;\n private readonly storage?: Storage;\n private readonly dataDir?: string;\n\n constructor(config: AgentLoopConfig) {\n this.llm = config.llm;\n this.tools = config.tools ?? new DefaultToolRegistry();\n this.personalities = config.personalities ?? new DefaultPersonalityRegistry();\n this.memory = config.memory ?? new NoopMemoryProvider();\n this.session = config.session ?? new InMemorySessionStore();\n this.hooks = config.hooks ?? new DefaultHookRegistry();\n this.injectors = (config.injectors ?? []).sort((a, b) => b.priority - a.priority);\n this.injectorPluginIds = config.injectorPluginIds ?? new Map();\n this.maxIterations = config.options?.maxIterations ?? 50;\n this.historyLimit = config.options?.historyLimit ?? 200;\n this.platform = config.options?.platform ?? 'cli';\n this.workingDir = config.options?.workingDir ?? process.cwd();\n this.resultBudgetChars = config.options?.resultBudgetChars ?? 80_000;\n this.maxToolCallsPerTurn = config.options?.maxToolCallsPerTurn ?? 20;\n this.maxIdenticalToolCalls = config.options?.maxIdenticalToolCalls ?? 5;\n this.streamingTimeoutMs = config.options?.streamingTimeoutMs ?? 120_000;\n this.modelRouting = config.modelRouting ?? {};\n if (config.storage) this.storage = config.storage;\n if (config.dataDir) this.dataDir = config.dataDir;\n }\n\n async *run(text: string, opts: RunOptions = {}): AsyncGenerator<AgentEvent> {\n const abortSignal = opts.abortSignal ?? new AbortController().signal;\n const sessionKey = opts.sessionKey ?? `${this.platform}:default`;\n\n // Step 1: Resolve or create session\n const ethosSession =\n (await this.session.getSessionByKey(sessionKey)) ??\n (await this.session.createSession({\n key: sessionKey,\n platform: this.platform,\n model: this.llm.model,\n provider: this.llm.name,\n personalityId: opts.personalityId,\n workingDir: this.workingDir,\n usage: {\n inputTokens: 0,\n outputTokens: 0,\n cacheReadTokens: 0,\n cacheCreationTokens: 0,\n estimatedCostUsd: 0,\n apiCallCount: 0,\n compactionCount: 0,\n },\n }));\n\n const sessionId = ethosSession.id;\n const personality =\n (opts.personalityId ? this.personalities.get(opts.personalityId) : null) ??\n this.personalities.getDefault();\n\n // Resolve effective model: explicit per-personality routing > LLM base model.\n // personality.model is intentionally skipped — those IDs are Anthropic-specific\n // and break non-Anthropic providers (OpenRouter, Gemini, Ollama, etc.).\n // Configure overrides via modelRouting in ~/.ethos/config.yaml instead.\n const effectiveModel = this.modelRouting[personality.id] ?? this.llm.model;\n const modelOverride = effectiveModel !== this.llm.model ? effectiveModel : undefined;\n // Allowed tool names for this personality (undefined = no restriction)\n const allowedTools = personality.toolset?.length ? personality.toolset : undefined;\n // Per-personality plugin + MCP gate (default-deny: missing field = no access)\n const allowedPlugins = personality.plugins ?? [];\n const filterOpts: ToolFilterOpts = {\n allowedMcpServers: personality.mcp_servers,\n allowedPlugins,\n };\n\n // Step 2: Fire session_start hooks\n await this.hooks.fireVoid(\n 'session_start',\n {\n sessionId,\n sessionKey,\n platform: this.platform,\n personalityId: personality.id,\n },\n allowedPlugins,\n );\n\n // Step 3: Persist the user message.\n //\n // Subagent task contract: the delegated task always lives in the child's\n // first user message (this `text`). It is NEVER copied into the system\n // prompt, NEVER injected via memory, and NEVER duplicated across both.\n // The regression test in\n // `extensions/tools-delegation/src/__tests__/task-contract.test.ts`\n // captures every `LLMProvider.complete()` request and asserts the marker\n // never appears in `opts.system` and appears exactly once across all\n // user-role messages.\n await this.session.appendMessage({\n sessionId,\n role: 'user',\n content: text,\n });\n\n // Step 4: Load history (trimmed to most-recent limit)\n const allMessages = await this.session.getMessages(sessionId, { limit: this.historyLimit });\n const history = allMessages.filter((m) => m.role !== 'system');\n\n // Step 5: Prefetch memory\n const memCtx = await this.memory.prefetch({\n sessionId,\n sessionKey,\n platform: this.platform,\n workingDir: this.workingDir,\n personalityId: personality.id,\n memoryScope: personality.memoryScope,\n query: text,\n });\n\n // Step 6: Build system prompt from injectors\n const promptCtx: PromptContext = {\n sessionId,\n sessionKey,\n platform: this.platform,\n model: this.llm.model,\n history,\n workingDir: this.workingDir,\n isDm: true,\n turnNumber: allMessages.length,\n personalityId: personality.id,\n };\n\n const systemParts: string[] = [];\n\n // ETHOS.md / personality identity — routes through Storage so ScopedStorage\n // and InMemoryStorage fixtures work correctly. Only runs when storage is\n // wired (production always provides it; tests without a real ethosFile skip).\n if (personality.ethosFile && this.storage) {\n const identity = await this.storage.read(personality.ethosFile);\n if (identity) systemParts.push(identity.trim());\n }\n\n // Context injectors sorted by priority (already sorted in constructor)\n for (const injector of this.injectors) {\n // Plugin-registered injectors only fire when the plugin is permitted.\n const injPluginId = this.injectorPluginIds.get(injector);\n if (injPluginId !== undefined && !allowedPlugins.includes(injPluginId)) continue;\n if (injector.shouldInject && !injector.shouldInject(promptCtx)) continue;\n const result = await injector.inject(promptCtx);\n if (result) {\n if (result.position === 'prepend') {\n systemParts.unshift(result.content);\n } else {\n systemParts.push(result.content);\n }\n }\n }\n\n // Emit injector metadata (e.g. skill_files_used) so eval harness can capture it.\n if (promptCtx.meta && Object.keys(promptCtx.meta).length > 0) {\n yield { type: 'context_meta', data: promptCtx.meta };\n }\n\n // Memory injected last, as context about the user\n if (memCtx) {\n systemParts.push(`## Memory\\n\\n${memCtx.content}`);\n }\n\n // Step 7: Before-prompt-build modifying hooks (plugins can prepend/append/override)\n const buildResult = await this.hooks.fireModifying(\n 'before_prompt_build',\n {\n sessionId,\n personalityId: personality.id,\n history,\n },\n allowedPlugins,\n );\n\n if (buildResult.overrideSystem) {\n systemParts.length = 0;\n systemParts.push(buildResult.overrideSystem);\n } else {\n if (buildResult.prependSystem) systemParts.unshift(buildResult.prependSystem);\n if (buildResult.appendSystem) systemParts.push(buildResult.appendSystem);\n }\n\n const systemPrompt = systemParts.join('\\n\\n').trim() || undefined;\n\n // Step 8: Agentic loop — LLM call → tool use → LLM call → ...\n const llmMessages = this.toLLMMessages(history);\n let fullText = '';\n let turnCount = 0;\n\n // Tool-call budget tracking — prevents runaway loops (see IMPROVEMENT.md P1-3).\n // Counted across all iterations within a single user turn.\n let totalToolCalls = 0;\n const toolNameCounts = new Map<string, number>();\n\n for (let iteration = 0; iteration < this.maxIterations; iteration++) {\n if (abortSignal.aborted) {\n yield { type: 'error', error: 'Aborted', code: 'aborted' };\n return;\n }\n\n // Budget guard: bail before the next LLM call if we've already exceeded\n // either the total tool-call budget or the per-tool repeat budget. The\n // previous iteration's tool_result is in llmMessages, so the LLM history\n // stays valid; we just refuse to call again.\n if (totalToolCalls >= this.maxToolCallsPerTurn) {\n yield {\n type: 'tool_progress',\n toolName: '_budget',\n message: `Stopped: hit ${this.maxToolCallsPerTurn}-tool-call budget for this turn`,\n audience: 'user',\n };\n break;\n }\n const overusedTool = [...toolNameCounts.entries()].find(\n ([, count]) => count >= this.maxIdenticalToolCalls,\n );\n if (overusedTool) {\n yield {\n type: 'tool_progress',\n toolName: overusedTool[0],\n message: `Stopped: ${overusedTool[0]} called ${overusedTool[1]} times in one turn (likely loop)`,\n audience: 'user',\n };\n break;\n }\n\n // Fire before_llm_call\n await this.hooks.fireVoid(\n 'before_llm_call',\n {\n sessionId,\n model: this.llm.model,\n turnNumber: turnCount,\n },\n allowedPlugins,\n );\n\n // Stream LLM response\n const pendingToolCalls: Array<{\n toolCallId: string;\n toolName: string;\n partialJson: string;\n args?: unknown;\n }> = [];\n let chunkText = '';\n\n // Streaming watchdog: cancel the stream if no chunk arrives within the\n // per-personality window. Reset every chunk so slow-but-progressing\n // reasoning is unaffected. See IMPROVEMENT.md P1-2 / OpenClaw #68596.\n const watchdogMs = personality.streamingTimeoutMs ?? this.streamingTimeoutMs;\n const watchdogController = new AbortController();\n const combinedSignal = AbortSignal.any([abortSignal, watchdogController.signal]);\n let watchdogTimer: ReturnType<typeof setTimeout> | undefined;\n const armWatchdog = () => {\n if (watchdogTimer) clearTimeout(watchdogTimer);\n watchdogTimer = setTimeout(() => watchdogController.abort(), watchdogMs);\n };\n const disarmWatchdog = () => {\n if (watchdogTimer) clearTimeout(watchdogTimer);\n watchdogTimer = undefined;\n };\n\n try {\n armWatchdog();\n const stream = this.llm.complete(llmMessages, this.tools.toDefinitions(allowedTools, filterOpts), {\n system: systemPrompt,\n cacheSystemPrompt: true,\n abortSignal: combinedSignal,\n ...(modelOverride ? { modelOverride } : {}),\n });\n\n for await (const chunk of stream) {\n if (abortSignal.aborted) break;\n if (watchdogController.signal.aborted) break;\n armWatchdog();\n yield* this.handleChunk(chunk, pendingToolCalls, (t) => {\n chunkText += t;\n fullText += t;\n });\n }\n disarmWatchdog();\n\n if (watchdogController.signal.aborted && !abortSignal.aborted) {\n yield {\n type: 'error',\n error: `LLM stream stalled — no chunk for ${watchdogMs}ms`,\n code: 'streaming_timeout',\n };\n return;\n }\n } catch (err) {\n disarmWatchdog();\n if (watchdogController.signal.aborted && !abortSignal.aborted) {\n yield {\n type: 'error',\n error: `LLM stream stalled — no chunk for ${watchdogMs}ms`,\n code: 'streaming_timeout',\n };\n return;\n }\n const msg = err instanceof Error ? err.message : String(err);\n yield { type: 'error', error: msg, code: 'llm_error' };\n return;\n }\n\n turnCount++;\n\n // Determine which tool calls completed parsing\n const completedToolCalls = pendingToolCalls.filter((tc) => tc.args !== undefined);\n\n // Update budget counters — these gate the NEXT iteration's LLM call.\n totalToolCalls += completedToolCalls.length;\n for (const tc of completedToolCalls) {\n toolNameCounts.set(tc.toolName, (toolNameCounts.get(tc.toolName) ?? 0) + 1);\n }\n\n // Persist assistant message — include tool_use references so history is LLM-replayable\n await this.session.appendMessage({\n sessionId,\n role: 'assistant',\n content: chunkText,\n ...(completedToolCalls.length > 0 && {\n toolCalls: completedToolCalls.map((tc) => ({\n id: tc.toolCallId,\n name: tc.toolName,\n input: tc.args,\n })),\n }),\n });\n\n // Fire after_llm_call\n await this.hooks.fireVoid(\n 'after_llm_call',\n {\n sessionId,\n text: chunkText,\n usage: { inputTokens: 0, outputTokens: 0 },\n },\n allowedPlugins,\n );\n\n // Push assistant message with proper content blocks for next iteration\n if (completedToolCalls.length > 0) {\n const assistantContent: MessageContent[] = [];\n if (chunkText) assistantContent.push({ type: 'text', text: chunkText });\n for (const tc of completedToolCalls) {\n assistantContent.push({\n type: 'tool_use',\n id: tc.toolCallId,\n name: tc.toolName,\n input: tc.args,\n });\n }\n llmMessages.push({ role: 'assistant', content: assistantContent });\n } else {\n llmMessages.push({ role: 'assistant', content: chunkText });\n break;\n }\n\n // Step 9: Pre-flight hooks → execute non-rejected tools → collect all results\n\n // Phase 30.2 — tools call ctx.emit() during execution; AsyncGenerator can't\n // yield from a sync callback, so we buffer per-batch then drain after\n // executeParallel resolves. Order is preserved (insertion = call order).\n const progressBuffer: Array<{\n toolName: string;\n message: string;\n percent?: number;\n audience: 'internal' | 'user';\n }> = [];\n\n const scopedStorage = this.buildScopedStorage(personality);\n\n const toolCtxBase = {\n sessionId,\n sessionKey,\n platform: this.platform,\n workingDir: this.workingDir,\n agentId: opts.agentId,\n personalityId: personality.id,\n memoryScope: personality.memoryScope,\n currentTurn: turnCount,\n messageCount: allMessages.length + turnCount,\n abortSignal,\n emit: (event: {\n type: 'progress';\n toolName: string;\n message: string;\n percent?: number;\n audience?: 'internal' | 'user';\n }) => {\n progressBuffer.push({\n toolName: event.toolName,\n message: event.message,\n ...(event.percent !== undefined && { percent: event.percent }),\n audience: event.audience ?? 'internal',\n });\n },\n resultBudgetChars: this.resultBudgetChars,\n ...(scopedStorage ? { storage: scopedStorage } : {}),\n };\n\n // Run before_tool_call hooks; build exec list with effective args\n // Rejected tools get tool_end ok:false + an error tool_result sent back to LLM\n type Prepped = { toolCallId: string; name: string; args: unknown; rejected?: string };\n const prepped: Prepped[] = [];\n\n for (const tc of completedToolCalls) {\n const beforeResult = await this.hooks.fireModifying(\n 'before_tool_call',\n {\n sessionId,\n toolCallId: tc.toolCallId,\n toolName: tc.toolName,\n args: tc.args,\n },\n allowedPlugins,\n );\n\n if (beforeResult.error) {\n yield {\n type: 'tool_end',\n toolCallId: tc.toolCallId,\n toolName: tc.toolName,\n ok: false,\n durationMs: 0,\n result: beforeResult.error,\n };\n prepped.push({\n toolCallId: tc.toolCallId,\n name: tc.toolName,\n args: tc.args,\n rejected: beforeResult.error,\n });\n continue;\n }\n\n const effectiveArgs = beforeResult.args ?? tc.args;\n yield {\n type: 'tool_start',\n toolCallId: tc.toolCallId,\n toolName: tc.toolName,\n args: effectiveArgs,\n };\n prepped.push({ toolCallId: tc.toolCallId, name: tc.toolName, args: effectiveArgs });\n }\n\n // Execute only non-rejected tools; results keyed by toolCallId\n const execInputs = prepped\n .filter((p) => p.rejected === undefined)\n .map((p) => ({ toolCallId: p.toolCallId, name: p.name, args: p.args }));\n\n const startedAt = Date.now();\n const execResults =\n execInputs.length > 0\n ? await this.tools.executeParallel(execInputs, toolCtxBase, allowedTools, filterOpts)\n : [];\n const execResultMap = new Map(execResults.map((r) => [r.toolCallId, r]));\n\n // Drain any progress events tools emitted during execution. Order is\n // call-order (across the parallel batch) — close enough for users; the\n // exact interleaving doesn't matter when ctx.emit is best-effort.\n for (const ev of progressBuffer) {\n yield { type: 'tool_progress', ...ev };\n }\n progressBuffer.length = 0;\n\n // Persist results + emit tool_end + build tool_result content blocks (original order)\n const toolResultContent: MessageContent[] = [];\n\n for (const p of prepped) {\n const durationMs = Date.now() - startedAt;\n let result: ToolResult;\n\n if (p.rejected !== undefined) {\n result = { ok: false, error: p.rejected, code: 'execution_failed' };\n // tool_end already emitted above; no after_tool_call hook for blocked tools\n } else {\n const execResult = execResultMap.get(p.toolCallId);\n result = execResult?.result ?? {\n ok: false,\n error: 'Tool result missing',\n code: 'execution_failed',\n };\n yield {\n type: 'tool_end',\n toolCallId: p.toolCallId,\n toolName: p.name,\n ok: result.ok,\n durationMs,\n result: result.ok ? result.value : result.error,\n };\n await this.hooks.fireVoid(\n 'after_tool_call',\n {\n sessionId,\n toolName: p.name,\n result,\n durationMs,\n },\n allowedPlugins,\n );\n }\n\n // Persist every result (rejected or not) so history matches what LLM sees\n await this.session.appendMessage({\n sessionId,\n role: 'tool_result',\n content: result.ok ? result.value : result.error,\n toolCallId: p.toolCallId,\n toolName: p.name,\n });\n\n toolResultContent.push({\n type: 'tool_result',\n tool_use_id: p.toolCallId,\n content: result.ok ? result.value : result.error,\n is_error: !result.ok,\n });\n }\n\n // Feed all tool results back to LLM as a single user message with content blocks\n llmMessages.push({ role: 'user', content: toolResultContent });\n }\n\n // Step 10: Memory writes flow through the `memory_save` tool during the\n // turn (see extensions/tools-memory). The agent-loop itself produces no\n // updates, so there's nothing to sync here.\n\n // Step 11: Update usage\n await this.session.updateUsage(sessionId, { apiCallCount: turnCount });\n\n // Step 12: Fire agent_done\n await this.hooks.fireVoid('agent_done', { sessionId, text: fullText, turnCount }, allowedPlugins);\n\n yield { type: 'done', text: fullText, turnCount };\n }\n\n private *handleChunk(\n chunk: CompletionChunk,\n pendingToolCalls: Array<{\n toolCallId: string;\n toolName: string;\n partialJson: string;\n args?: unknown;\n }>,\n onText: (t: string) => void,\n ): Generator<AgentEvent> {\n switch (chunk.type) {\n case 'text_delta':\n onText(chunk.text);\n yield { type: 'text_delta', text: chunk.text };\n break;\n\n case 'thinking_delta':\n yield { type: 'thinking_delta', thinking: chunk.thinking };\n break;\n\n case 'tool_use_start':\n pendingToolCalls.push({\n toolCallId: chunk.toolCallId,\n toolName: chunk.toolName,\n partialJson: '',\n });\n break;\n\n case 'tool_use_delta': {\n const tc = pendingToolCalls.find((t) => t.toolCallId === chunk.toolCallId);\n if (tc) tc.partialJson += chunk.partialJson;\n break;\n }\n\n case 'tool_use_end': {\n const tc = pendingToolCalls.find((t) => t.toolCallId === chunk.toolCallId);\n if (tc) {\n try {\n tc.args = JSON.parse(chunk.inputJson || tc.partialJson);\n } catch {\n tc.args = {};\n }\n }\n break;\n }\n\n case 'usage':\n yield {\n type: 'usage',\n inputTokens: chunk.usage.inputTokens,\n outputTokens: chunk.usage.outputTokens,\n estimatedCostUsd: chunk.usage.estimatedCostUsd,\n };\n break;\n\n case 'done':\n // finishReason available here for future context-compaction (Phase 3)\n break;\n }\n }\n\n // Reconstruct LLM-ready messages from stored history.\n // Assistant messages with tool calls produce proper tool_use content blocks.\n // Consecutive tool_result rows are grouped into a single user message.\n private toLLMMessages(stored: StoredMessage[]): Message[] {\n const messages: Message[] = [];\n\n for (const msg of stored) {\n if (msg.role === 'system') continue;\n\n if (msg.role === 'user') {\n messages.push({ role: 'user', content: msg.content });\n } else if (msg.role === 'assistant') {\n if (msg.toolCalls && msg.toolCalls.length > 0) {\n const content: MessageContent[] = [];\n if (msg.content) content.push({ type: 'text', text: msg.content });\n for (const tc of msg.toolCalls) {\n content.push({ type: 'tool_use', id: tc.id, name: tc.name, input: tc.input });\n }\n messages.push({ role: 'assistant', content });\n } else {\n messages.push({ role: 'assistant', content: msg.content });\n }\n } else if (msg.role === 'tool_result') {\n const resultBlock: MessageContent = {\n type: 'tool_result',\n tool_use_id: msg.toolCallId ?? '',\n content: msg.content,\n is_error: false,\n };\n const last = messages[messages.length - 1];\n // Append to existing tool_result user message, or start a new one\n if (last?.role === 'user' && Array.isArray(last.content)) {\n (last.content as MessageContent[]).push(resultBlock);\n } else {\n messages.push({ role: 'user', content: [resultBlock] });\n }\n }\n }\n\n return messages;\n }\n\n // ---------------------------------------------------------------------------\n // Per-turn ScopedStorage construction (Phase 4 — fs_reach enforcement).\n //\n // When the AgentLoop was wired with `storage` + `dataDir`, build a\n // ScopedStorage decorated with the active personality's `fs_reach`\n // allowlist for this turn. Substitutions (${ETHOS_HOME} / ${self} /\n // ${CWD}) are resolved here so the underlying storage-fs class stays\n // pristine. When `fs_reach` is unset, fall back to a sensible default:\n // read: [<ethosHome>/personalities/<self>/, <ethosHome>/skills/, <cwd>]\n // write: [<ethosHome>/personalities/<self>/, <cwd>]\n // ---------------------------------------------------------------------------\n\n private buildScopedStorage(personality: PersonalityConfig): Storage | undefined {\n if (!this.storage) return undefined;\n\n const ethosHome = this.dataDir ?? join(homedir(), '.ethos');\n const cwd = this.workingDir;\n const self = personality.id;\n const ownDir = `${join(ethosHome, 'personalities', self)}/`;\n\n const fsReach = personality.fs_reach;\n const readPrefixes =\n fsReach?.read && fsReach.read.length > 0\n ? fsReach.read.map((p) => substitute(p, { ethosHome, self, cwd }))\n : [ownDir, `${join(ethosHome, 'skills')}/`, cwd];\n const writePrefixes =\n fsReach?.write && fsReach.write.length > 0\n ? fsReach.write.map((p) => substitute(p, { ethosHome, self, cwd }))\n : [ownDir, cwd];\n\n return new ScopedStorage(this.storage, { read: readPrefixes, write: writePrefixes });\n }\n}\n\nfunction substitute(\n template: string,\n vars: { ethosHome: string; self: string; cwd: string },\n): string {\n return template\n .replace(/\\$\\{ETHOS_HOME\\}/g, vars.ethosHome)\n .replace(/\\$\\{self\\}/g, vars.self)\n .replace(/\\$\\{CWD\\}/g, vars.cwd);\n}\n","import {\n appendFile,\n mkdir,\n readdir,\n readFile,\n rename,\n rm,\n stat,\n writeFile,\n} from 'node:fs/promises';\nimport type {\n Storage,\n StorageDirEntry,\n StorageRemoveOptions,\n StorageWriteOptions,\n} from '@ethosagent/types';\n\nexport class FsStorage implements Storage {\n async read(path: string): Promise<string | null> {\n try {\n return await readFile(path, 'utf-8');\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return null;\n throw err;\n }\n }\n\n async exists(path: string): Promise<boolean> {\n try {\n await stat(path);\n return true;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return false;\n throw err;\n }\n }\n\n async mtime(path: string): Promise<number | null> {\n try {\n const s = await stat(path);\n return s.mtimeMs;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return null;\n throw err;\n }\n }\n\n async list(dir: string): Promise<string[]> {\n try {\n return await readdir(dir);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return [];\n throw err;\n }\n }\n\n async listEntries(dir: string): Promise<StorageDirEntry[]> {\n try {\n const entries = await readdir(dir, { withFileTypes: true });\n return entries.map((e) => ({ name: e.name, isDir: e.isDirectory() }));\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return [];\n throw err;\n }\n }\n\n async write(path: string, content: string, opts?: StorageWriteOptions): Promise<void> {\n if (opts?.mode !== undefined) {\n await writeFile(path, content, { encoding: 'utf-8', mode: opts.mode });\n return;\n }\n await writeFile(path, content, 'utf-8');\n }\n\n async append(path: string, content: string): Promise<void> {\n await appendFile(path, content, 'utf-8');\n }\n\n async writeAtomic(path: string, content: string, opts?: StorageWriteOptions): Promise<void> {\n const tmp = `${path}.tmp.${process.pid}.${Date.now()}`;\n if (opts?.mode !== undefined) {\n await writeFile(tmp, content, { encoding: 'utf-8', mode: opts.mode });\n } else {\n await writeFile(tmp, content, 'utf-8');\n }\n try {\n await rename(tmp, path);\n } catch (err) {\n // Best-effort cleanup of temp file on rename failure.\n try {\n await rm(tmp, { force: true });\n } catch {\n // ignore\n }\n throw err;\n }\n }\n\n async mkdir(dir: string): Promise<void> {\n await mkdir(dir, { recursive: true });\n }\n\n async remove(path: string, opts?: StorageRemoveOptions): Promise<void> {\n await rm(path, { recursive: opts?.recursive === true });\n }\n\n async rename(from: string, to: string): Promise<void> {\n await rename(from, to);\n }\n}\n","import { dirname } from 'node:path';\nimport type {\n Storage,\n StorageDirEntry,\n StorageRemoveOptions,\n StorageWriteOptions,\n} from '@ethosagent/types';\n\ninterface FileNode {\n type: 'file';\n content: string;\n mode?: number;\n mtimeMs: number;\n}\n\ninterface DirNode {\n type: 'dir';\n mtimeMs: number;\n}\n\ntype Node = FileNode | DirNode;\n\n/**\n * In-memory Storage implementation for tests. Mirrors fs semantics closely:\n *\n * - read/exists/mtime return null for missing paths.\n * - write requires the parent directory to exist (throws ENOENT otherwise) —\n * matches fs.writeFile and forces tests to mkdir first, just like prod.\n * - mkdir is always recursive; no-op on existing dirs; throws on file conflict.\n * - remove without recursive throws on missing paths and on non-empty dirs.\n * - writeAtomic round-trips through a temp key, identical to fs flow.\n *\n * mtime ticks forward on every write so cache-by-mtime patterns work in tests\n * without sleeping.\n */\nexport class InMemoryStorage implements Storage {\n // Absolute path → node\n private readonly nodes = new Map<string, Node>();\n private clock = 0;\n\n // Treat the filesystem root as always existing so consumers don't need to\n // mkdir('/') before writing to a file at the root.\n private isRootLike(path: string): boolean {\n return path === '/' || /^[A-Za-z]:[\\\\/]?$/.test(path);\n }\n\n private nextMtime(): number {\n this.clock += 1;\n return this.clock;\n }\n\n private getNode(path: string): Node | undefined {\n return this.nodes.get(path);\n }\n\n private requireParentDir(path: string): void {\n const parent = dirname(path);\n if (parent === path) return;\n if (this.isRootLike(parent)) return;\n const node = this.nodes.get(parent);\n if (!node) {\n const err = new Error(`ENOENT: no such file or directory, open '${path}'`);\n (err as NodeJS.ErrnoException).code = 'ENOENT';\n throw err;\n }\n if (node.type !== 'dir') {\n const err = new Error(`ENOTDIR: not a directory, open '${path}'`);\n (err as NodeJS.ErrnoException).code = 'ENOTDIR';\n throw err;\n }\n }\n\n async read(path: string): Promise<string | null> {\n const node = this.getNode(path);\n if (!node) return null;\n if (node.type === 'dir') {\n const err = new Error(`EISDIR: illegal operation on a directory, read '${path}'`);\n (err as NodeJS.ErrnoException).code = 'EISDIR';\n throw err;\n }\n return node.content;\n }\n\n async exists(path: string): Promise<boolean> {\n return this.nodes.has(path);\n }\n\n async mtime(path: string): Promise<number | null> {\n const node = this.getNode(path);\n return node ? node.mtimeMs : null;\n }\n\n async list(dir: string): Promise<string[]> {\n const node = this.getNode(dir);\n if (!node) return [];\n if (node.type !== 'dir') {\n const err = new Error(`ENOTDIR: not a directory, scandir '${dir}'`);\n (err as NodeJS.ErrnoException).code = 'ENOTDIR';\n throw err;\n }\n const prefix = dir.endsWith('/') ? dir : `${dir}/`;\n const names: string[] = [];\n for (const key of this.nodes.keys()) {\n if (key === dir) continue;\n if (!key.startsWith(prefix)) continue;\n const rest = key.slice(prefix.length);\n if (rest.includes('/')) continue;\n names.push(rest);\n }\n return names.sort();\n }\n\n async listEntries(dir: string): Promise<StorageDirEntry[]> {\n const names = await this.list(dir);\n const prefix = dir.endsWith('/') ? dir : `${dir}/`;\n return names.map((name) => {\n const node = this.nodes.get(prefix + name);\n return { name, isDir: node?.type === 'dir' };\n });\n }\n\n async write(path: string, content: string, opts?: StorageWriteOptions): Promise<void> {\n const existing = this.nodes.get(path);\n if (existing && existing.type === 'dir') {\n const err = new Error(`EISDIR: illegal operation on a directory, write '${path}'`);\n (err as NodeJS.ErrnoException).code = 'EISDIR';\n throw err;\n }\n this.requireParentDir(path);\n const node: FileNode = {\n type: 'file',\n content,\n mtimeMs: this.nextMtime(),\n };\n if (opts?.mode !== undefined) node.mode = opts.mode;\n this.nodes.set(path, node);\n }\n\n async writeAtomic(path: string, content: string, opts?: StorageWriteOptions): Promise<void> {\n // Same observable end-state as write — atomicity is a property of the\n // backing store; the in-memory map is single-step by definition.\n await this.write(path, content, opts);\n }\n\n async append(path: string, content: string): Promise<void> {\n const existing = this.nodes.get(path);\n if (existing && existing.type === 'dir') {\n const err = new Error(`EISDIR: illegal operation on a directory, append '${path}'`);\n (err as NodeJS.ErrnoException).code = 'EISDIR';\n throw err;\n }\n if (!existing) {\n // appendFile creates the file if missing — match that semantics.\n this.requireParentDir(path);\n this.nodes.set(path, {\n type: 'file',\n content,\n mtimeMs: this.nextMtime(),\n });\n return;\n }\n this.nodes.set(path, {\n ...existing,\n content: existing.content + content,\n mtimeMs: this.nextMtime(),\n });\n }\n\n async mkdir(dir: string): Promise<void> {\n if (this.isRootLike(dir)) return;\n const existing = this.nodes.get(dir);\n if (existing) {\n if (existing.type === 'dir') return;\n const err = new Error(`EEXIST: file already exists, mkdir '${dir}'`);\n (err as NodeJS.ErrnoException).code = 'EEXIST';\n throw err;\n }\n // Recursively create parents.\n const parent = dirname(dir);\n if (parent !== dir && !this.isRootLike(parent) && !this.nodes.has(parent)) {\n await this.mkdir(parent);\n }\n this.nodes.set(dir, { type: 'dir', mtimeMs: this.nextMtime() });\n }\n\n async remove(path: string, opts?: StorageRemoveOptions): Promise<void> {\n const node = this.nodes.get(path);\n if (!node) {\n const err = new Error(`ENOENT: no such file or directory, remove '${path}'`);\n (err as NodeJS.ErrnoException).code = 'ENOENT';\n throw err;\n }\n if (node.type === 'file') {\n this.nodes.delete(path);\n return;\n }\n // Directory.\n const prefix = path.endsWith('/') ? path : `${path}/`;\n const children: string[] = [];\n for (const key of this.nodes.keys()) {\n if (key !== path && key.startsWith(prefix)) children.push(key);\n }\n if (children.length > 0 && opts?.recursive !== true) {\n const err = new Error(`ENOTEMPTY: directory not empty, remove '${path}'`);\n (err as NodeJS.ErrnoException).code = 'ENOTEMPTY';\n throw err;\n }\n for (const key of children) this.nodes.delete(key);\n this.nodes.delete(path);\n }\n\n async rename(from: string, to: string): Promise<void> {\n const node = this.nodes.get(from);\n if (!node) {\n const err = new Error(`ENOENT: no such file or directory, rename '${from}' -> '${to}'`);\n (err as NodeJS.ErrnoException).code = 'ENOENT';\n throw err;\n }\n this.requireParentDir(to);\n\n if (node.type === 'file') {\n const target = this.nodes.get(to);\n if (target?.type === 'dir') {\n const err = new Error(`EISDIR: cannot rename file onto directory, '${from}' -> '${to}'`);\n (err as NodeJS.ErrnoException).code = 'EISDIR';\n throw err;\n }\n this.nodes.delete(from);\n this.nodes.set(to, { ...node, mtimeMs: this.nextMtime() });\n return;\n }\n\n // Directory rename — move the directory itself plus every descendant.\n const targetExisting = this.nodes.get(to);\n if (targetExisting) {\n const err = new Error(`EEXIST: target exists, rename '${from}' -> '${to}'`);\n (err as NodeJS.ErrnoException).code = 'EEXIST';\n throw err;\n }\n const fromPrefix = from.endsWith('/') ? from : `${from}/`;\n const moves: Array<[string, string]> = [[from, to]];\n for (const key of this.nodes.keys()) {\n if (key !== from && key.startsWith(fromPrefix)) {\n moves.push([key, to + key.slice(from.length)]);\n }\n }\n for (const [src, dst] of moves) {\n const n = this.nodes.get(src);\n if (!n) continue;\n this.nodes.delete(src);\n this.nodes.set(dst, { ...n, mtimeMs: this.nextMtime() });\n }\n }\n\n // --- Test helpers -----------------------------------------------------\n\n /** Drop all state. Useful for `beforeEach` resets without re-instantiating. */\n reset(): void {\n this.nodes.clear();\n this.clock = 0;\n }\n\n /** Return the recorded mode for a file (undefined if no mode was set). */\n getMode(path: string): number | undefined {\n const node = this.nodes.get(path);\n if (!node || node.type !== 'file') return undefined;\n return node.mode;\n }\n}\n","import {\n BoundaryError,\n type Storage,\n type StorageDirEntry,\n type StorageRemoveOptions,\n type StorageWriteOptions,\n} from '@ethosagent/types';\n\nexport interface ScopedStorageScope {\n /** Absolute path prefixes that may be read. */\n read: readonly string[];\n /** Absolute path prefixes that may be written / mutated. */\n write: readonly string[];\n}\n\n/**\n * Decorator over Storage that enforces a per-scope read/write allowlist.\n * Used by tools-file to bound a personality's filesystem reach to its own\n * directory + cwd (Phase 4 of the storage abstraction plan).\n *\n * A path is permitted if any allowed prefix is a prefix of the absolute path.\n * Prefixes are matched literally — there is no glob expansion. Pass paths\n * that end in `/` for directory scopes; ScopedStorage normalizes them so\n * `/a/b` does not also match `/a/bc/`.\n */\nexport class ScopedStorage implements Storage {\n private readonly readPrefixes: string[];\n private readonly writePrefixes: string[];\n\n constructor(\n private readonly inner: Storage,\n scope: ScopedStorageScope,\n ) {\n this.readPrefixes = scope.read.map(normalizePrefix);\n this.writePrefixes = scope.write.map(normalizePrefix);\n }\n\n private check(path: string, kind: 'read' | 'write'): void {\n const allowed = kind === 'read' ? this.readPrefixes : this.writePrefixes;\n if (!isPathAllowed(path, allowed)) {\n throw new BoundaryError(kind, path, allowed);\n }\n }\n\n async read(path: string): Promise<string | null> {\n this.check(path, 'read');\n return this.inner.read(path);\n }\n\n async exists(path: string): Promise<boolean> {\n this.check(path, 'read');\n return this.inner.exists(path);\n }\n\n async mtime(path: string): Promise<number | null> {\n this.check(path, 'read');\n return this.inner.mtime(path);\n }\n\n async list(dir: string): Promise<string[]> {\n this.check(dir, 'read');\n return this.inner.list(dir);\n }\n\n async listEntries(dir: string): Promise<StorageDirEntry[]> {\n this.check(dir, 'read');\n return this.inner.listEntries(dir);\n }\n\n async write(path: string, content: string, opts?: StorageWriteOptions): Promise<void> {\n this.check(path, 'write');\n return this.inner.write(path, content, opts);\n }\n\n async append(path: string, content: string): Promise<void> {\n this.check(path, 'write');\n return this.inner.append(path, content);\n }\n\n async writeAtomic(path: string, content: string, opts?: StorageWriteOptions): Promise<void> {\n this.check(path, 'write');\n return this.inner.writeAtomic(path, content, opts);\n }\n\n async mkdir(dir: string): Promise<void> {\n this.check(dir, 'write');\n return this.inner.mkdir(dir);\n }\n\n async remove(path: string, opts?: StorageRemoveOptions): Promise<void> {\n this.check(path, 'write');\n return this.inner.remove(path, opts);\n }\n\n async rename(from: string, to: string): Promise<void> {\n this.check(from, 'write');\n this.check(to, 'write');\n return this.inner.rename(from, to);\n }\n}\n\nfunction normalizePrefix(prefix: string): string {\n // A prefix matches any path where prefix is followed by '/' or end-of-string,\n // OR where the path equals the prefix exactly. We keep the prefix as-given\n // (with or without trailing slash) and handle the boundary in isPathAllowed.\n return prefix;\n}\n\nfunction isPathAllowed(path: string, prefixes: readonly string[]): boolean {\n for (const prefix of prefixes) {\n if (path === prefix) return true;\n const withoutSlash = prefix.endsWith('/') ? prefix.slice(0, -1) : prefix;\n // Allow the directory itself (without trailing slash) — needed so\n // `mkdir(<personality-dir>)` and `list(<personality-dir>)` succeed\n // when the configured prefix has a trailing slash.\n if (path === withoutSlash) return true;\n const withSlash = `${withoutSlash}/`;\n if (path.startsWith(withSlash)) return true;\n }\n return false;\n}\n","import type {\n SearchResult,\n Session,\n SessionFilter,\n SessionStore,\n SessionUsage,\n StoredMessage,\n} from '@ethosagent/types';\n\nexport class InMemorySessionStore implements SessionStore {\n private sessions = new Map<string, Session>();\n private messages = new Map<string, StoredMessage[]>();\n private idCounter = 0;\n\n async createSession(data: Omit<Session, 'id' | 'createdAt' | 'updatedAt'>): Promise<Session> {\n const session: Session = {\n ...data,\n id: `session_${++this.idCounter}`,\n createdAt: new Date(),\n updatedAt: new Date(),\n };\n this.sessions.set(session.id, session);\n this.messages.set(session.id, []);\n return session;\n }\n\n async getSession(id: string): Promise<Session | null> {\n return this.sessions.get(id) ?? null;\n }\n\n async getSessionByKey(key: string): Promise<Session | null> {\n for (const s of this.sessions.values()) {\n if (s.key === key) return s;\n }\n return null;\n }\n\n async updateSession(id: string, patch: Partial<Session>): Promise<void> {\n const session = this.sessions.get(id);\n if (!session) throw new Error(`Session not found: ${id}`);\n this.sessions.set(id, { ...session, ...patch, updatedAt: new Date() });\n }\n\n async deleteSession(id: string): Promise<void> {\n this.sessions.delete(id);\n this.messages.delete(id);\n }\n\n async listSessions(filter?: SessionFilter): Promise<Session[]> {\n let results = [...this.sessions.values()];\n if (filter?.platform) results = results.filter((s) => s.platform === filter.platform);\n if (filter?.personalityId)\n results = results.filter((s) => s.personalityId === filter.personalityId);\n if (filter?.workingDir) results = results.filter((s) => s.workingDir === filter.workingDir);\n if (filter?.since) {\n const since = filter.since;\n results = results.filter((s) => s.createdAt >= since);\n }\n results.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());\n const offset = filter?.offset ?? 0;\n const limit = filter?.limit ?? results.length;\n return results.slice(offset, offset + limit);\n }\n\n async appendMessage(data: Omit<StoredMessage, 'id' | 'timestamp'>): Promise<StoredMessage> {\n const message: StoredMessage = {\n ...data,\n id: `msg_${++this.idCounter}`,\n timestamp: new Date(),\n };\n const list = this.messages.get(data.sessionId) ?? [];\n list.push(message);\n this.messages.set(data.sessionId, list);\n return message;\n }\n\n async getMessages(\n sessionId: string,\n options?: { limit?: number; offset?: number },\n ): Promise<StoredMessage[]> {\n const all = this.messages.get(sessionId) ?? [];\n const offset = options?.offset ?? 0;\n // Return most-recent messages: trim from the tail, then skip `offset` from the end\n const end = all.length - offset;\n const start = options?.limit ? Math.max(0, end - options.limit) : 0;\n return all.slice(start, end);\n }\n\n async updateUsage(sessionId: string, delta: Partial<SessionUsage>): Promise<void> {\n const session = this.sessions.get(sessionId);\n if (!session) return;\n const usage = { ...session.usage };\n for (const [k, v] of Object.entries(delta) as [keyof SessionUsage, number][]) {\n (usage[k] as number) += v;\n }\n this.sessions.set(sessionId, { ...session, usage, updatedAt: new Date() });\n }\n\n async search(\n query: string,\n options?: { limit?: number; sessionId?: string },\n ): Promise<SearchResult[]> {\n const results: SearchResult[] = [];\n const lower = query.toLowerCase();\n for (const [sessionId, msgs] of this.messages.entries()) {\n if (options?.sessionId && sessionId !== options.sessionId) continue;\n for (const msg of msgs) {\n const idx = msg.content.toLowerCase().indexOf(lower);\n if (idx >= 0) {\n results.push({\n sessionId,\n messageId: msg.id,\n snippet: msg.content.slice(Math.max(0, idx - 50), idx + 150),\n score: 1,\n timestamp: msg.timestamp,\n });\n }\n }\n }\n results.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());\n return results.slice(0, options?.limit ?? 20);\n }\n\n async pruneOldSessions(olderThan: Date): Promise<number> {\n let count = 0;\n for (const [id, session] of this.sessions.entries()) {\n if (session.updatedAt < olderThan) {\n this.sessions.delete(id);\n this.messages.delete(id);\n count++;\n }\n }\n return count;\n }\n\n async vacuum(): Promise<void> {\n // No-op for in-memory store\n }\n}\n","import type { MemoryLoadContext, MemoryProvider, MemoryUpdate } from '@ethosagent/types';\n\nexport class NoopMemoryProvider implements MemoryProvider {\n async prefetch(_ctx: MemoryLoadContext) {\n return null;\n }\n\n async sync(_ctx: MemoryLoadContext, _updates: MemoryUpdate[]): Promise<void> {\n // No-op\n }\n}\n","import type { PersonalityConfig, PersonalityRegistry } from '@ethosagent/types';\n\nconst DEFAULT_PERSONALITY: PersonalityConfig = {\n id: 'default',\n name: 'Default',\n description: 'Default Ethos personality',\n};\n\nexport class DefaultPersonalityRegistry implements PersonalityRegistry {\n private readonly personalities = new Map<string, PersonalityConfig>([\n ['default', DEFAULT_PERSONALITY],\n ]);\n private defaultId = 'default';\n\n define(config: PersonalityConfig): void {\n this.personalities.set(config.id, config);\n }\n\n get(id: string): PersonalityConfig | undefined {\n return this.personalities.get(id);\n }\n\n list(): PersonalityConfig[] {\n return [...this.personalities.values()];\n }\n\n getDefault(): PersonalityConfig {\n return this.personalities.get(this.defaultId) ?? DEFAULT_PERSONALITY;\n }\n\n setDefault(id: string): void {\n if (!this.personalities.has(id)) {\n throw new Error(`Unknown personality: ${id}`);\n }\n this.defaultId = id;\n }\n\n async loadFromDirectory(_dir: string): Promise<void> {\n // Implemented in extensions/personalities\n }\n\n remove(id: string): void {\n this.personalities.delete(id);\n }\n}\n","import type { ClaimingHooks, HookRegistry, ModifyingHooks, VoidHooks } from '@ethosagent/types';\n\ntype AnyHandler = (...args: unknown[]) => Promise<unknown>;\n\ninterface RegisteredHandler {\n handler: AnyHandler;\n pluginId?: string;\n failurePolicy: 'fail-open' | 'fail-closed';\n}\n\n/** Returns true when a handler should fire given the allowedPlugins filter. */\nfunction isAllowed(h: RegisteredHandler, allowedPlugins: string[] | undefined): boolean {\n if (allowedPlugins === undefined) return true; // no filter — fire all\n if (!h.pluginId) return true; // built-in handler — always fires\n return allowedPlugins.includes(h.pluginId);\n}\n\nexport class DefaultHookRegistry implements HookRegistry {\n private readonly voidHandlers = new Map<string, RegisteredHandler[]>();\n private readonly modifyingHandlers = new Map<string, RegisteredHandler[]>();\n private readonly claimingHandlers = new Map<string, RegisteredHandler[]>();\n\n registerVoid<K extends keyof VoidHooks>(\n name: K,\n handler: (payload: VoidHooks[K]) => Promise<void>,\n opts?: { pluginId?: string; failurePolicy?: 'fail-open' | 'fail-closed' },\n ): () => void {\n const entry: RegisteredHandler = {\n handler: handler as AnyHandler,\n pluginId: opts?.pluginId,\n failurePolicy: opts?.failurePolicy ?? 'fail-open',\n };\n const list = this.voidHandlers.get(name) ?? [];\n list.push(entry);\n this.voidHandlers.set(name, list);\n return () => this.remove(this.voidHandlers, name, entry);\n }\n\n registerModifying<K extends keyof ModifyingHooks>(\n name: K,\n handler: (payload: ModifyingHooks[K][0]) => Promise<Partial<ModifyingHooks[K][1]> | null>,\n opts?: { pluginId?: string },\n ): () => void {\n const entry: RegisteredHandler = {\n handler: handler as AnyHandler,\n pluginId: opts?.pluginId,\n failurePolicy: 'fail-open',\n };\n const list = this.modifyingHandlers.get(name) ?? [];\n list.push(entry);\n this.modifyingHandlers.set(name, list);\n return () => this.remove(this.modifyingHandlers, name, entry);\n }\n\n registerClaiming<K extends keyof ClaimingHooks>(\n name: K,\n handler: (payload: ClaimingHooks[K][0]) => Promise<ClaimingHooks[K][1]>,\n opts?: { pluginId?: string },\n ): () => void {\n const entry: RegisteredHandler = {\n handler: handler as AnyHandler,\n pluginId: opts?.pluginId,\n failurePolicy: 'fail-open',\n };\n const list = this.claimingHandlers.get(name) ?? [];\n list.push(entry);\n this.claimingHandlers.set(name, list);\n return () => this.remove(this.claimingHandlers, name, entry);\n }\n\n // Void hooks: all handlers run in parallel via Promise.allSettled.\n // Failures are logged but never propagate (fail-open by default).\n // allowedPlugins gates plugin-registered handlers; built-in handlers always fire.\n async fireVoid<K extends keyof VoidHooks>(\n name: K,\n payload: VoidHooks[K],\n allowedPlugins?: string[],\n ): Promise<void> {\n const handlers = (this.voidHandlers.get(name) ?? []).filter((h) =>\n isAllowed(h, allowedPlugins),\n );\n await Promise.allSettled(handlers.map((h) => h.handler(payload)));\n }\n\n // Modifying hooks: handlers run sequentially; results are merged (first non-null value per key wins).\n async fireModifying<K extends keyof ModifyingHooks>(\n name: K,\n payload: ModifyingHooks[K][0],\n allowedPlugins?: string[],\n ): Promise<ModifyingHooks[K][1]> {\n const handlers = (this.modifyingHandlers.get(name) ?? []).filter((h) =>\n isAllowed(h, allowedPlugins),\n );\n const merged: Record<string, unknown> = {};\n for (const h of handlers) {\n try {\n const result = await h.handler(payload);\n if (result && typeof result === 'object') {\n for (const [k, v] of Object.entries(result)) {\n if (!(k in merged) && v !== null && v !== undefined) {\n merged[k] = v;\n }\n }\n }\n } catch {\n // fail-open: continue with other handlers\n }\n }\n return merged as ModifyingHooks[K][1];\n }\n\n // Claiming hooks: handlers run sequentially, stop after first { handled: true }.\n async fireClaiming<K extends keyof ClaimingHooks>(\n name: K,\n payload: ClaimingHooks[K][0],\n allowedPlugins?: string[],\n ): Promise<ClaimingHooks[K][1]> {\n const handlers = (this.claimingHandlers.get(name) ?? []).filter((h) =>\n isAllowed(h, allowedPlugins),\n );\n for (const h of handlers) {\n try {\n const result = (await h.handler(payload)) as ClaimingHooks[K][1];\n if (result && (result as { handled: boolean }).handled) {\n return result;\n }\n } catch {\n // fail-open: try next handler\n }\n }\n return { handled: false } as ClaimingHooks[K][1];\n }\n\n unregisterPlugin(pluginId: string): void {\n for (const map of [this.voidHandlers, this.modifyingHandlers, this.claimingHandlers]) {\n for (const [name, handlers] of map.entries()) {\n map.set(\n name,\n handlers.filter((h) => h.pluginId !== pluginId),\n );\n }\n }\n }\n\n private remove(\n map: Map<string, RegisteredHandler[]>,\n name: string,\n entry: RegisteredHandler,\n ): void {\n const list = map.get(name) ?? [];\n map.set(\n name,\n list.filter((h) => h !== entry),\n );\n }\n}\n","import type { Tool, ToolContext, ToolFilterOpts, ToolRegistry, ToolResult } from '@ethosagent/types';\n\ninterface ToolEntry {\n tool: Tool;\n pluginId?: string;\n}\n\n/** Extract MCP server name from `mcp__<server>__<tool>` naming convention. */\nfunction mcpServerName(toolName: string): string | undefined {\n if (!toolName.startsWith('mcp__')) return undefined;\n return toolName.split('__')[1];\n}\n\n/** Returns true when a tool passes the MCP server + plugin filters. */\nfunction passesFilter(entry: ToolEntry, filterOpts: ToolFilterOpts | undefined): boolean {\n if (!filterOpts) return true;\n\n const { allowedMcpServers, allowedPlugins } = filterOpts;\n\n // MCP server gate: MCP tools only appear when their server is in the allowlist.\n if (allowedMcpServers !== undefined) {\n const server = mcpServerName(entry.tool.name);\n if (server !== undefined && !allowedMcpServers.includes(server)) return false;\n }\n\n // Plugin gate: plugin tools only appear when their plugin is in the allowlist.\n if (allowedPlugins !== undefined && entry.pluginId !== undefined) {\n if (!allowedPlugins.includes(entry.pluginId)) return false;\n }\n\n return true;\n}\n\nexport class DefaultToolRegistry implements ToolRegistry {\n private readonly tools = new Map<string, ToolEntry>();\n\n register(tool: Tool, opts?: { pluginId?: string }): void {\n this.tools.set(tool.name, { tool, pluginId: opts?.pluginId });\n }\n\n registerAll(tools: Tool[]): void {\n for (const tool of tools) {\n this.register(tool);\n }\n }\n\n unregister(name: string): void {\n this.tools.delete(name);\n }\n\n get(name: string): Tool | undefined {\n return this.tools.get(name)?.tool;\n }\n\n getAvailable(): Tool[] {\n return [...this.tools.values()]\n .filter((e) => !e.tool.isAvailable || e.tool.isAvailable())\n .map((e) => e.tool);\n }\n\n getForToolset(toolset: string): Tool[] {\n return this.getAvailable().filter((t) => t.toolset === toolset);\n }\n\n toDefinitions(allowedTools?: string[], filterOpts?: ToolFilterOpts) {\n const entries = [...this.tools.values()].filter(\n (e) => !e.tool.isAvailable || e.tool.isAvailable(),\n );\n\n const filtered = entries.filter((e) => {\n if (allowedTools && allowedTools.length > 0 && !allowedTools.includes(e.tool.name))\n return false;\n return passesFilter(e, filterOpts);\n });\n\n return filtered.map((e) => ({\n name: e.tool.name,\n description: e.tool.description,\n parameters: e.tool.schema,\n }));\n }\n\n // Runs all tool calls in parallel. Results are returned in input order.\n // Budget is split evenly across parallel calls; each result is post-trimmed to budget.\n // allowedTools + filterOpts enforce tool access at execution time (belt-and-suspenders).\n async executeParallel(\n calls: Array<{ toolCallId: string; name: string; args: unknown }>,\n ctx: ToolContext,\n allowedTools?: string[],\n filterOpts?: ToolFilterOpts,\n ): Promise<Array<{ toolCallId: string; name: string; result: ToolResult }>> {\n const perCallBudget = Math.floor(ctx.resultBudgetChars / Math.max(calls.length, 1));\n\n const results = await Promise.allSettled(\n calls.map(async (call) => {\n // Name-based allowlist check\n if (allowedTools && allowedTools.length > 0 && !allowedTools.includes(call.name)) {\n return {\n toolCallId: call.toolCallId,\n name: call.name,\n result: {\n ok: false,\n error: `Tool ${call.name} is not permitted for this personality`,\n code: 'not_available',\n } as ToolResult,\n };\n }\n\n const entry = this.tools.get(call.name);\n if (!entry) {\n return {\n toolCallId: call.toolCallId,\n name: call.name,\n result: {\n ok: false,\n error: `Unknown tool: ${call.name}`,\n code: 'not_available',\n } as ToolResult,\n };\n }\n\n // MCP server + plugin filter check\n if (!passesFilter(entry, filterOpts)) {\n return {\n toolCallId: call.toolCallId,\n name: call.name,\n result: {\n ok: false,\n error: `Tool ${call.name} is not permitted for this personality`,\n code: 'not_available',\n } as ToolResult,\n };\n }\n\n if (entry.tool.isAvailable && !entry.tool.isAvailable()) {\n return {\n toolCallId: call.toolCallId,\n name: call.name,\n result: {\n ok: false,\n error: `Tool ${call.name} is not currently available`,\n code: 'not_available',\n } as ToolResult,\n };\n }\n\n const budget = Math.min(perCallBudget, entry.tool.maxResultChars ?? perCallBudget);\n const toolCtx: ToolContext = { ...ctx, resultBudgetChars: budget };\n\n try {\n const result = await entry.tool.execute(call.args, toolCtx);\n // Post-trim result to budget\n if (result.ok && result.value.length > budget) {\n return {\n toolCallId: call.toolCallId,\n name: call.name,\n result: {\n ok: true,\n value: `${result.value.slice(0, budget)}\\n[truncated — ${result.value.length} chars total]`,\n } as ToolResult,\n };\n }\n return { toolCallId: call.toolCallId, name: call.name, result };\n } catch (err) {\n return {\n toolCallId: call.toolCallId,\n name: call.name,\n result: {\n ok: false,\n error: err instanceof Error ? err.message : String(err),\n code: 'execution_failed',\n } as ToolResult,\n };\n }\n }),\n );\n\n // Unwrap settled results — always return, never throw\n return results.map((r, i) => {\n if (r.status === 'fulfilled') return r.value;\n const call = calls[i] ?? { toolCallId: 'unknown', name: 'unknown', args: {} };\n return {\n toolCallId: call.toolCallId,\n name: call.name,\n result: {\n ok: false,\n error: String(r.reason),\n code: 'execution_failed',\n } as ToolResult,\n };\n });\n }\n}\n","// Generic plugin registry — adapted from praxis PluginRegistry pattern.\n// Each subsystem (tools, channels, memory backends) gets its own instance.\n\nexport type PluginFactory<T, C = unknown> = (config: C) => T | null;\n\nexport class PluginRegistry<T, C = unknown> {\n private readonly factories = new Map<string, PluginFactory<T, C>>();\n\n register(type: string, factory: PluginFactory<T, C>): void {\n this.factories.set(type, factory);\n }\n\n create(type: string, config: C): T {\n const factory = this.factories.get(type);\n if (!factory) {\n throw new Error(\n `Unknown plugin type: \"${type}\". Registered: ${[...this.factories.keys()].join(', ')}`,\n );\n }\n const instance = factory(config);\n if (!instance) {\n throw new Error(`Plugin factory for \"${type}\" returned null`);\n }\n return instance;\n }\n\n has(type: string): boolean {\n return this.factories.has(type);\n }\n\n types(): string[] {\n return [...this.factories.keys()];\n }\n}\n"],"mappings":";AAAA,SAAS,eAAe;AACxB,SAAS,YAAY;;;ACDrB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;;;ACTP,SAAS,eAAe;;;ACAxB;AAAA,EACE;AAAA,OAKK;AAmBA,IAAM,gBAAN,MAAuC;AAAA,EAI5C,YACmB,OACjB,OACA;AAFiB;AAGjB,SAAK,eAAe,MAAM,KAAK,IAAI,eAAe;AAClD,SAAK,gBAAgB,MAAM,MAAM,IAAI,eAAe;AAAA,EACtD;AAAA,EALmB;AAAA,EAJF;AAAA,EACA;AAAA,EAUT,MAAM,MAAc,MAA8B;AACxD,UAAM,UAAU,SAAS,SAAS,KAAK,eAAe,KAAK;AAC3D,QAAI,CAAC,cAAc,MAAM,OAAO,GAAG;AACjC,YAAM,IAAI,cAAc,MAAM,MAAM,OAAO;AAAA,IAC7C;AAAA,EACF;AAAA,EAEA,MAAM,KAAK,MAAsC;AAC/C,SAAK,MAAM,MAAM,MAAM;AACvB,WAAO,KAAK,MAAM,KAAK,IAAI;AAAA,EAC7B;AAAA,EAEA,MAAM,OAAO,MAAgC;AAC3C,SAAK,MAAM,MAAM,MAAM;AACvB,WAAO,KAAK,MAAM,OAAO,IAAI;AAAA,EAC/B;AAAA,EAEA,MAAM,MAAM,MAAsC;AAChD,SAAK,MAAM,MAAM,MAAM;AACvB,WAAO,KAAK,MAAM,MAAM,IAAI;AAAA,EAC9B;AAAA,EAEA,MAAM,KAAK,KAAgC;AACzC,SAAK,MAAM,KAAK,MAAM;AACtB,WAAO,KAAK,MAAM,KAAK,GAAG;AAAA,EAC5B;AAAA,EAEA,MAAM,YAAY,KAAyC;AACzD,SAAK,MAAM,KAAK,MAAM;AACtB,WAAO,KAAK,MAAM,YAAY,GAAG;AAAA,EACnC;AAAA,EAEA,MAAM,MAAM,MAAc,SAAiB,MAA2C;AACpF,SAAK,MAAM,MAAM,OAAO;AACxB,WAAO,KAAK,MAAM,MAAM,MAAM,SAAS,IAAI;AAAA,EAC7C;AAAA,EAEA,MAAM,OAAO,MAAc,SAAgC;AACzD,SAAK,MAAM,MAAM,OAAO;AACxB,WAAO,KAAK,MAAM,OAAO,MAAM,OAAO;AAAA,EACxC;AAAA,EAEA,MAAM,YAAY,MAAc,SAAiB,MAA2C;AAC1F,SAAK,MAAM,MAAM,OAAO;AACxB,WAAO,KAAK,MAAM,YAAY,MAAM,SAAS,IAAI;AAAA,EACnD;AAAA,EAEA,MAAM,MAAM,KAA4B;AACtC,SAAK,MAAM,KAAK,OAAO;AACvB,WAAO,KAAK,MAAM,MAAM,GAAG;AAAA,EAC7B;AAAA,EAEA,MAAM,OAAO,MAAc,MAA4C;AACrE,SAAK,MAAM,MAAM,OAAO;AACxB,WAAO,KAAK,MAAM,OAAO,MAAM,IAAI;AAAA,EACrC;AAAA,EAEA,MAAM,OAAO,MAAc,IAA2B;AACpD,SAAK,MAAM,MAAM,OAAO;AACxB,SAAK,MAAM,IAAI,OAAO;AACtB,WAAO,KAAK,MAAM,OAAO,MAAM,EAAE;AAAA,EACnC;AACF;AAEA,SAAS,gBAAgB,QAAwB;AAI/C,SAAO;AACT;AAEA,SAAS,cAAc,MAAc,UAAsC;AACzE,aAAW,UAAU,UAAU;AAC7B,QAAI,SAAS,OAAQ,QAAO;AAC5B,UAAM,eAAe,OAAO,SAAS,GAAG,IAAI,OAAO,MAAM,GAAG,EAAE,IAAI;AAIlE,QAAI,SAAS,aAAc,QAAO;AAClC,UAAM,YAAY,GAAG,YAAY;AACjC,QAAI,KAAK,WAAW,SAAS,EAAG,QAAO;AAAA,EACzC;AACA,SAAO;AACT;;;AC/GO,IAAM,uBAAN,MAAmD;AAAA,EAChD,WAAW,oBAAI,IAAqB;AAAA,EACpC,WAAW,oBAAI,IAA6B;AAAA,EAC5C,YAAY;AAAA,EAEpB,MAAM,cAAc,MAAyE;AAC3F,UAAM,UAAmB;AAAA,MACvB,GAAG;AAAA,MACH,IAAI,WAAW,EAAE,KAAK,SAAS;AAAA,MAC/B,WAAW,oBAAI,KAAK;AAAA,MACpB,WAAW,oBAAI,KAAK;AAAA,IACtB;AACA,SAAK,SAAS,IAAI,QAAQ,IAAI,OAAO;AACrC,SAAK,SAAS,IAAI,QAAQ,IAAI,CAAC,CAAC;AAChC,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,WAAW,IAAqC;AACpD,WAAO,KAAK,SAAS,IAAI,EAAE,KAAK;AAAA,EAClC;AAAA,EAEA,MAAM,gBAAgB,KAAsC;AAC1D,eAAW,KAAK,KAAK,SAAS,OAAO,GAAG;AACtC,UAAI,EAAE,QAAQ,IAAK,QAAO;AAAA,IAC5B;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,cAAc,IAAY,OAAwC;AACtE,UAAM,UAAU,KAAK,SAAS,IAAI,EAAE;AACpC,QAAI,CAAC,QAAS,OAAM,IAAI,MAAM,sBAAsB,EAAE,EAAE;AACxD,SAAK,SAAS,IAAI,IAAI,EAAE,GAAG,SAAS,GAAG,OAAO,WAAW,oBAAI,KAAK,EAAE,CAAC;AAAA,EACvE;AAAA,EAEA,MAAM,cAAc,IAA2B;AAC7C,SAAK,SAAS,OAAO,EAAE;AACvB,SAAK,SAAS,OAAO,EAAE;AAAA,EACzB;AAAA,EAEA,MAAM,aAAa,QAA4C;AAC7D,QAAI,UAAU,CAAC,GAAG,KAAK,SAAS,OAAO,CAAC;AACxC,QAAI,QAAQ,SAAU,WAAU,QAAQ,OAAO,CAAC,MAAM,EAAE,aAAa,OAAO,QAAQ;AACpF,QAAI,QAAQ;AACV,gBAAU,QAAQ,OAAO,CAAC,MAAM,EAAE,kBAAkB,OAAO,aAAa;AAC1E,QAAI,QAAQ,WAAY,WAAU,QAAQ,OAAO,CAAC,MAAM,EAAE,eAAe,OAAO,UAAU;AAC1F,QAAI,QAAQ,OAAO;AACjB,YAAM,QAAQ,OAAO;AACrB,gBAAU,QAAQ,OAAO,CAAC,MAAM,EAAE,aAAa,KAAK;AAAA,IACtD;AACA,YAAQ,KAAK,CAAC,GAAG,MAAM,EAAE,UAAU,QAAQ,IAAI,EAAE,UAAU,QAAQ,CAAC;AACpE,UAAM,SAAS,QAAQ,UAAU;AACjC,UAAM,QAAQ,QAAQ,SAAS,QAAQ;AACvC,WAAO,QAAQ,MAAM,QAAQ,SAAS,KAAK;AAAA,EAC7C;AAAA,EAEA,MAAM,cAAc,MAAuE;AACzF,UAAM,UAAyB;AAAA,MAC7B,GAAG;AAAA,MACH,IAAI,OAAO,EAAE,KAAK,SAAS;AAAA,MAC3B,WAAW,oBAAI,KAAK;AAAA,IACtB;AACA,UAAM,OAAO,KAAK,SAAS,IAAI,KAAK,SAAS,KAAK,CAAC;AACnD,SAAK,KAAK,OAAO;AACjB,SAAK,SAAS,IAAI,KAAK,WAAW,IAAI;AACtC,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,YACJ,WACA,SAC0B;AAC1B,UAAM,MAAM,KAAK,SAAS,IAAI,SAAS,KAAK,CAAC;AAC7C,UAAM,SAAS,SAAS,UAAU;AAElC,UAAM,MAAM,IAAI,SAAS;AACzB,UAAM,QAAQ,SAAS,QAAQ,KAAK,IAAI,GAAG,MAAM,QAAQ,KAAK,IAAI;AAClE,WAAO,IAAI,MAAM,OAAO,GAAG;AAAA,EAC7B;AAAA,EAEA,MAAM,YAAY,WAAmB,OAA6C;AAChF,UAAM,UAAU,KAAK,SAAS,IAAI,SAAS;AAC3C,QAAI,CAAC,QAAS;AACd,UAAM,QAAQ,EAAE,GAAG,QAAQ,MAAM;AACjC,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,KAAK,GAAqC;AAC5E,MAAC,MAAM,CAAC,KAAgB;AAAA,IAC1B;AACA,SAAK,SAAS,IAAI,WAAW,EAAE,GAAG,SAAS,OAAO,WAAW,oBAAI,KAAK,EAAE,CAAC;AAAA,EAC3E;AAAA,EAEA,MAAM,OACJ,OACA,SACyB;AACzB,UAAM,UAA0B,CAAC;AACjC,UAAM,QAAQ,MAAM,YAAY;AAChC,eAAW,CAAC,WAAW,IAAI,KAAK,KAAK,SAAS,QAAQ,GAAG;AACvD,UAAI,SAAS,aAAa,cAAc,QAAQ,UAAW;AAC3D,iBAAW,OAAO,MAAM;AACtB,cAAM,MAAM,IAAI,QAAQ,YAAY,EAAE,QAAQ,KAAK;AACnD,YAAI,OAAO,GAAG;AACZ,kBAAQ,KAAK;AAAA,YACX;AAAA,YACA,WAAW,IAAI;AAAA,YACf,SAAS,IAAI,QAAQ,MAAM,KAAK,IAAI,GAAG,MAAM,EAAE,GAAG,MAAM,GAAG;AAAA,YAC3D,OAAO;AAAA,YACP,WAAW,IAAI;AAAA,UACjB,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AACA,YAAQ,KAAK,CAAC,GAAG,MAAM,EAAE,UAAU,QAAQ,IAAI,EAAE,UAAU,QAAQ,CAAC;AACpE,WAAO,QAAQ,MAAM,GAAG,SAAS,SAAS,EAAE;AAAA,EAC9C;AAAA,EAEA,MAAM,iBAAiB,WAAkC;AACvD,QAAI,QAAQ;AACZ,eAAW,CAAC,IAAI,OAAO,KAAK,KAAK,SAAS,QAAQ,GAAG;AACnD,UAAI,QAAQ,YAAY,WAAW;AACjC,aAAK,SAAS,OAAO,EAAE;AACvB,aAAK,SAAS,OAAO,EAAE;AACvB;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,SAAwB;AAAA,EAE9B;AACF;;;ACxIO,IAAM,qBAAN,MAAmD;AAAA,EACxD,MAAM,SAAS,MAAyB;AACtC,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,KAAK,MAAyB,UAAyC;AAAA,EAE7E;AACF;;;ACRA,IAAM,sBAAyC;AAAA,EAC7C,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,aAAa;AACf;AAEO,IAAM,6BAAN,MAAgE;AAAA,EACpD,gBAAgB,oBAAI,IAA+B;AAAA,IAClE,CAAC,WAAW,mBAAmB;AAAA,EACjC,CAAC;AAAA,EACO,YAAY;AAAA,EAEpB,OAAO,QAAiC;AACtC,SAAK,cAAc,IAAI,OAAO,IAAI,MAAM;AAAA,EAC1C;AAAA,EAEA,IAAI,IAA2C;AAC7C,WAAO,KAAK,cAAc,IAAI,EAAE;AAAA,EAClC;AAAA,EAEA,OAA4B;AAC1B,WAAO,CAAC,GAAG,KAAK,cAAc,OAAO,CAAC;AAAA,EACxC;AAAA,EAEA,aAAgC;AAC9B,WAAO,KAAK,cAAc,IAAI,KAAK,SAAS,KAAK;AAAA,EACnD;AAAA,EAEA,WAAW,IAAkB;AAC3B,QAAI,CAAC,KAAK,cAAc,IAAI,EAAE,GAAG;AAC/B,YAAM,IAAI,MAAM,wBAAwB,EAAE,EAAE;AAAA,IAC9C;AACA,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,MAAM,kBAAkB,MAA6B;AAAA,EAErD;AAAA,EAEA,OAAO,IAAkB;AACvB,SAAK,cAAc,OAAO,EAAE;AAAA,EAC9B;AACF;;;ACjCA,SAAS,UAAU,GAAsB,gBAA+C;AACtF,MAAI,mBAAmB,OAAW,QAAO;AACzC,MAAI,CAAC,EAAE,SAAU,QAAO;AACxB,SAAO,eAAe,SAAS,EAAE,QAAQ;AAC3C;AAEO,IAAM,sBAAN,MAAkD;AAAA,EACtC,eAAe,oBAAI,IAAiC;AAAA,EACpD,oBAAoB,oBAAI,IAAiC;AAAA,EACzD,mBAAmB,oBAAI,IAAiC;AAAA,EAEzE,aACE,MACA,SACA,MACY;AACZ,UAAM,QAA2B;AAAA,MAC/B;AAAA,MACA,UAAU,MAAM;AAAA,MAChB,eAAe,MAAM,iBAAiB;AAAA,IACxC;AACA,UAAM,OAAO,KAAK,aAAa,IAAI,IAAI,KAAK,CAAC;AAC7C,SAAK,KAAK,KAAK;AACf,SAAK,aAAa,IAAI,MAAM,IAAI;AAChC,WAAO,MAAM,KAAK,OAAO,KAAK,cAAc,MAAM,KAAK;AAAA,EACzD;AAAA,EAEA,kBACE,MACA,SACA,MACY;AACZ,UAAM,QAA2B;AAAA,MAC/B;AAAA,MACA,UAAU,MAAM;AAAA,MAChB,eAAe;AAAA,IACjB;AACA,UAAM,OAAO,KAAK,kBAAkB,IAAI,IAAI,KAAK,CAAC;AAClD,SAAK,KAAK,KAAK;AACf,SAAK,kBAAkB,IAAI,MAAM,IAAI;AACrC,WAAO,MAAM,KAAK,OAAO,KAAK,mBAAmB,MAAM,KAAK;AAAA,EAC9D;AAAA,EAEA,iBACE,MACA,SACA,MACY;AACZ,UAAM,QAA2B;AAAA,MAC/B;AAAA,MACA,UAAU,MAAM;AAAA,MAChB,eAAe;AAAA,IACjB;AACA,UAAM,OAAO,KAAK,iBAAiB,IAAI,IAAI,KAAK,CAAC;AACjD,SAAK,KAAK,KAAK;AACf,SAAK,iBAAiB,IAAI,MAAM,IAAI;AACpC,WAAO,MAAM,KAAK,OAAO,KAAK,kBAAkB,MAAM,KAAK;AAAA,EAC7D;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,SACJ,MACA,SACA,gBACe;AACf,UAAM,YAAY,KAAK,aAAa,IAAI,IAAI,KAAK,CAAC,GAAG;AAAA,MAAO,CAAC,MAC3D,UAAU,GAAG,cAAc;AAAA,IAC7B;AACA,UAAM,QAAQ,WAAW,SAAS,IAAI,CAAC,MAAM,EAAE,QAAQ,OAAO,CAAC,CAAC;AAAA,EAClE;AAAA;AAAA,EAGA,MAAM,cACJ,MACA,SACA,gBAC+B;AAC/B,UAAM,YAAY,KAAK,kBAAkB,IAAI,IAAI,KAAK,CAAC,GAAG;AAAA,MAAO,CAAC,MAChE,UAAU,GAAG,cAAc;AAAA,IAC7B;AACA,UAAM,SAAkC,CAAC;AACzC,eAAW,KAAK,UAAU;AACxB,UAAI;AACF,cAAM,SAAS,MAAM,EAAE,QAAQ,OAAO;AACtC,YAAI,UAAU,OAAO,WAAW,UAAU;AACxC,qBAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,MAAM,GAAG;AAC3C,gBAAI,EAAE,KAAK,WAAW,MAAM,QAAQ,MAAM,QAAW;AACnD,qBAAO,CAAC,IAAI;AAAA,YACd;AAAA,UACF;AAAA,QACF;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,MAAM,aACJ,MACA,SACA,gBAC8B;AAC9B,UAAM,YAAY,KAAK,iBAAiB,IAAI,IAAI,KAAK,CAAC,GAAG;AAAA,MAAO,CAAC,MAC/D,UAAU,GAAG,cAAc;AAAA,IAC7B;AACA,eAAW,KAAK,UAAU;AACxB,UAAI;AACF,cAAM,SAAU,MAAM,EAAE,QAAQ,OAAO;AACvC,YAAI,UAAW,OAAgC,SAAS;AACtD,iBAAO;AAAA,QACT;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AACA,WAAO,EAAE,SAAS,MAAM;AAAA,EAC1B;AAAA,EAEA,iBAAiB,UAAwB;AACvC,eAAW,OAAO,CAAC,KAAK,cAAc,KAAK,mBAAmB,KAAK,gBAAgB,GAAG;AACpF,iBAAW,CAAC,MAAM,QAAQ,KAAK,IAAI,QAAQ,GAAG;AAC5C,YAAI;AAAA,UACF;AAAA,UACA,SAAS,OAAO,CAAC,MAAM,EAAE,aAAa,QAAQ;AAAA,QAChD;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,OACN,KACA,MACA,OACM;AACN,UAAM,OAAO,IAAI,IAAI,IAAI,KAAK,CAAC;AAC/B,QAAI;AAAA,MACF;AAAA,MACA,KAAK,OAAO,CAAC,MAAM,MAAM,KAAK;AAAA,IAChC;AAAA,EACF;AACF;;;ACnJA,SAAS,cAAc,UAAsC;AAC3D,MAAI,CAAC,SAAS,WAAW,OAAO,EAAG,QAAO;AAC1C,SAAO,SAAS,MAAM,IAAI,EAAE,CAAC;AAC/B;AAGA,SAAS,aAAa,OAAkB,YAAiD;AACvF,MAAI,CAAC,WAAY,QAAO;AAExB,QAAM,EAAE,mBAAmB,eAAe,IAAI;AAG9C,MAAI,sBAAsB,QAAW;AACnC,UAAM,SAAS,cAAc,MAAM,KAAK,IAAI;AAC5C,QAAI,WAAW,UAAa,CAAC,kBAAkB,SAAS,MAAM,EAAG,QAAO;AAAA,EAC1E;AAGA,MAAI,mBAAmB,UAAa,MAAM,aAAa,QAAW;AAChE,QAAI,CAAC,eAAe,SAAS,MAAM,QAAQ,EAAG,QAAO;AAAA,EACvD;AAEA,SAAO;AACT;AAEO,IAAM,sBAAN,MAAkD;AAAA,EACtC,QAAQ,oBAAI,IAAuB;AAAA,EAEpD,SAAS,MAAY,MAAoC;AACvD,SAAK,MAAM,IAAI,KAAK,MAAM,EAAE,MAAM,UAAU,MAAM,SAAS,CAAC;AAAA,EAC9D;AAAA,EAEA,YAAY,OAAqB;AAC/B,eAAW,QAAQ,OAAO;AACxB,WAAK,SAAS,IAAI;AAAA,IACpB;AAAA,EACF;AAAA,EAEA,WAAW,MAAoB;AAC7B,SAAK,MAAM,OAAO,IAAI;AAAA,EACxB;AAAA,EAEA,IAAI,MAAgC;AAClC,WAAO,KAAK,MAAM,IAAI,IAAI,GAAG;AAAA,EAC/B;AAAA,EAEA,eAAuB;AACrB,WAAO,CAAC,GAAG,KAAK,MAAM,OAAO,CAAC,EAC3B,OAAO,CAAC,MAAM,CAAC,EAAE,KAAK,eAAe,EAAE,KAAK,YAAY,CAAC,EACzD,IAAI,CAAC,MAAM,EAAE,IAAI;AAAA,EACtB;AAAA,EAEA,cAAc,SAAyB;AACrC,WAAO,KAAK,aAAa,EAAE,OAAO,CAAC,MAAM,EAAE,YAAY,OAAO;AAAA,EAChE;AAAA,EAEA,cAAc,cAAyB,YAA6B;AAClE,UAAM,UAAU,CAAC,GAAG,KAAK,MAAM,OAAO,CAAC,EAAE;AAAA,MACvC,CAAC,MAAM,CAAC,EAAE,KAAK,eAAe,EAAE,KAAK,YAAY;AAAA,IACnD;AAEA,UAAM,WAAW,QAAQ,OAAO,CAAC,MAAM;AACrC,UAAI,gBAAgB,aAAa,SAAS,KAAK,CAAC,aAAa,SAAS,EAAE,KAAK,IAAI;AAC/E,eAAO;AACT,aAAO,aAAa,GAAG,UAAU;AAAA,IACnC,CAAC;AAED,WAAO,SAAS,IAAI,CAAC,OAAO;AAAA,MAC1B,MAAM,EAAE,KAAK;AAAA,MACb,aAAa,EAAE,KAAK;AAAA,MACpB,YAAY,EAAE,KAAK;AAAA,IACrB,EAAE;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,gBACJ,OACA,KACA,cACA,YAC0E;AAC1E,UAAM,gBAAgB,KAAK,MAAM,IAAI,oBAAoB,KAAK,IAAI,MAAM,QAAQ,CAAC,CAAC;AAElF,UAAM,UAAU,MAAM,QAAQ;AAAA,MAC5B,MAAM,IAAI,OAAO,SAAS;AAExB,YAAI,gBAAgB,aAAa,SAAS,KAAK,CAAC,aAAa,SAAS,KAAK,IAAI,GAAG;AAChF,iBAAO;AAAA,YACL,YAAY,KAAK;AAAA,YACjB,MAAM,KAAK;AAAA,YACX,QAAQ;AAAA,cACN,IAAI;AAAA,cACJ,OAAO,QAAQ,KAAK,IAAI;AAAA,cACxB,MAAM;AAAA,YACR;AAAA,UACF;AAAA,QACF;AAEA,cAAM,QAAQ,KAAK,MAAM,IAAI,KAAK,IAAI;AACtC,YAAI,CAAC,OAAO;AACV,iBAAO;AAAA,YACL,YAAY,KAAK;AAAA,YACjB,MAAM,KAAK;AAAA,YACX,QAAQ;AAAA,cACN,IAAI;AAAA,cACJ,OAAO,iBAAiB,KAAK,IAAI;AAAA,cACjC,MAAM;AAAA,YACR;AAAA,UACF;AAAA,QACF;AAGA,YAAI,CAAC,aAAa,OAAO,UAAU,GAAG;AACpC,iBAAO;AAAA,YACL,YAAY,KAAK;AAAA,YACjB,MAAM,KAAK;AAAA,YACX,QAAQ;AAAA,cACN,IAAI;AAAA,cACJ,OAAO,QAAQ,KAAK,IAAI;AAAA,cACxB,MAAM;AAAA,YACR;AAAA,UACF;AAAA,QACF;AAEA,YAAI,MAAM,KAAK,eAAe,CAAC,MAAM,KAAK,YAAY,GAAG;AACvD,iBAAO;AAAA,YACL,YAAY,KAAK;AAAA,YACjB,MAAM,KAAK;AAAA,YACX,QAAQ;AAAA,cACN,IAAI;AAAA,cACJ,OAAO,QAAQ,KAAK,IAAI;AAAA,cACxB,MAAM;AAAA,YACR;AAAA,UACF;AAAA,QACF;AAEA,cAAM,SAAS,KAAK,IAAI,eAAe,MAAM,KAAK,kBAAkB,aAAa;AACjF,cAAM,UAAuB,EAAE,GAAG,KAAK,mBAAmB,OAAO;AAEjE,YAAI;AACF,gBAAM,SAAS,MAAM,MAAM,KAAK,QAAQ,KAAK,MAAM,OAAO;AAE1D,cAAI,OAAO,MAAM,OAAO,MAAM,SAAS,QAAQ;AAC7C,mBAAO;AAAA,cACL,YAAY,KAAK;AAAA,cACjB,MAAM,KAAK;AAAA,cACX,QAAQ;AAAA,gBACN,IAAI;AAAA,gBACJ,OAAO,GAAG,OAAO,MAAM,MAAM,GAAG,MAAM,CAAC;AAAA,oBAAkB,OAAO,MAAM,MAAM;AAAA,cAC9E;AAAA,YACF;AAAA,UACF;AACA,iBAAO,EAAE,YAAY,KAAK,YAAY,MAAM,KAAK,MAAM,OAAO;AAAA,QAChE,SAAS,KAAK;AACZ,iBAAO;AAAA,YACL,YAAY,KAAK;AAAA,YACjB,MAAM,KAAK;AAAA,YACX,QAAQ;AAAA,cACN,IAAI;AAAA,cACJ,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,cACtD,MAAM;AAAA,YACR;AAAA,UACF;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAGA,WAAO,QAAQ,IAAI,CAAC,GAAG,MAAM;AAC3B,UAAI,EAAE,WAAW,YAAa,QAAO,EAAE;AACvC,YAAM,OAAO,MAAM,CAAC,KAAK,EAAE,YAAY,WAAW,MAAM,WAAW,MAAM,CAAC,EAAE;AAC5E,aAAO;AAAA,QACL,YAAY,KAAK;AAAA,QACjB,MAAM,KAAK;AAAA,QACX,QAAQ;AAAA,UACN,IAAI;AAAA,UACJ,OAAO,OAAO,EAAE,MAAM;AAAA,UACtB,MAAM;AAAA,QACR;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AACF;;;AR5CO,IAAM,YAAN,MAAgB;AAAA,EACJ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA,EAKR;AAAA,EACQ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEjB,YAAY,QAAyB;AACnC,SAAK,MAAM,OAAO;AAClB,SAAK,QAAQ,OAAO,SAAS,IAAI,oBAAoB;AACrD,SAAK,gBAAgB,OAAO,iBAAiB,IAAI,2BAA2B;AAC5E,SAAK,SAAS,OAAO,UAAU,IAAI,mBAAmB;AACtD,SAAK,UAAU,OAAO,WAAW,IAAI,qBAAqB;AAC1D,SAAK,QAAQ,OAAO,SAAS,IAAI,oBAAoB;AACrD,SAAK,aAAa,OAAO,aAAa,CAAC,GAAG,KAAK,CAAC,GAAG,MAAM,EAAE,WAAW,EAAE,QAAQ;AAChF,SAAK,oBAAoB,OAAO,qBAAqB,oBAAI,IAAI;AAC7D,SAAK,gBAAgB,OAAO,SAAS,iBAAiB;AACtD,SAAK,eAAe,OAAO,SAAS,gBAAgB;AACpD,SAAK,WAAW,OAAO,SAAS,YAAY;AAC5C,SAAK,aAAa,OAAO,SAAS,cAAc,QAAQ,IAAI;AAC5D,SAAK,oBAAoB,OAAO,SAAS,qBAAqB;AAC9D,SAAK,sBAAsB,OAAO,SAAS,uBAAuB;AAClE,SAAK,wBAAwB,OAAO,SAAS,yBAAyB;AACtE,SAAK,qBAAqB,OAAO,SAAS,sBAAsB;AAChE,SAAK,eAAe,OAAO,gBAAgB,CAAC;AAC5C,QAAI,OAAO,QAAS,MAAK,UAAU,OAAO;AAC1C,QAAI,OAAO,QAAS,MAAK,UAAU,OAAO;AAAA,EAC5C;AAAA,EAEA,OAAO,IAAI,MAAc,OAAmB,CAAC,GAA+B;AAC1E,UAAM,cAAc,KAAK,eAAe,IAAI,gBAAgB,EAAE;AAC9D,UAAM,aAAa,KAAK,cAAc,GAAG,KAAK,QAAQ;AAGtD,UAAM,eACH,MAAM,KAAK,QAAQ,gBAAgB,UAAU,KAC7C,MAAM,KAAK,QAAQ,cAAc;AAAA,MAChC,KAAK;AAAA,MACL,UAAU,KAAK;AAAA,MACf,OAAO,KAAK,IAAI;AAAA,MAChB,UAAU,KAAK,IAAI;AAAA,MACnB,eAAe,KAAK;AAAA,MACpB,YAAY,KAAK;AAAA,MACjB,OAAO;AAAA,QACL,aAAa;AAAA,QACb,cAAc;AAAA,QACd,iBAAiB;AAAA,QACjB,qBAAqB;AAAA,QACrB,kBAAkB;AAAA,QAClB,cAAc;AAAA,QACd,iBAAiB;AAAA,MACnB;AAAA,IACF,CAAC;AAEH,UAAM,YAAY,aAAa;AAC/B,UAAM,eACH,KAAK,gBAAgB,KAAK,cAAc,IAAI,KAAK,aAAa,IAAI,SACnE,KAAK,cAAc,WAAW;AAMhC,UAAM,iBAAiB,KAAK,aAAa,YAAY,EAAE,KAAK,KAAK,IAAI;AACrE,UAAM,gBAAgB,mBAAmB,KAAK,IAAI,QAAQ,iBAAiB;AAE3E,UAAM,eAAe,YAAY,SAAS,SAAS,YAAY,UAAU;AAEzE,UAAM,iBAAiB,YAAY,WAAW,CAAC;AAC/C,UAAM,aAA6B;AAAA,MACjC,mBAAmB,YAAY;AAAA,MAC/B;AAAA,IACF;AAGA,UAAM,KAAK,MAAM;AAAA,MACf;AAAA,MACA;AAAA,QACE;AAAA,QACA;AAAA,QACA,UAAU,KAAK;AAAA,QACf,eAAe,YAAY;AAAA,MAC7B;AAAA,MACA;AAAA,IACF;AAYA,UAAM,KAAK,QAAQ,cAAc;AAAA,MAC/B;AAAA,MACA,MAAM;AAAA,MACN,SAAS;AAAA,IACX,CAAC;AAGD,UAAM,cAAc,MAAM,KAAK,QAAQ,YAAY,WAAW,EAAE,OAAO,KAAK,aAAa,CAAC;AAC1F,UAAM,UAAU,YAAY,OAAO,CAAC,MAAM,EAAE,SAAS,QAAQ;AAG7D,UAAM,SAAS,MAAM,KAAK,OAAO,SAAS;AAAA,MACxC;AAAA,MACA;AAAA,MACA,UAAU,KAAK;AAAA,MACf,YAAY,KAAK;AAAA,MACjB,eAAe,YAAY;AAAA,MAC3B,aAAa,YAAY;AAAA,MACzB,OAAO;AAAA,IACT,CAAC;AAGD,UAAM,YAA2B;AAAA,MAC/B;AAAA,MACA;AAAA,MACA,UAAU,KAAK;AAAA,MACf,OAAO,KAAK,IAAI;AAAA,MAChB;AAAA,MACA,YAAY,KAAK;AAAA,MACjB,MAAM;AAAA,MACN,YAAY,YAAY;AAAA,MACxB,eAAe,YAAY;AAAA,IAC7B;AAEA,UAAM,cAAwB,CAAC;AAK/B,QAAI,YAAY,aAAa,KAAK,SAAS;AACzC,YAAM,WAAW,MAAM,KAAK,QAAQ,KAAK,YAAY,SAAS;AAC9D,UAAI,SAAU,aAAY,KAAK,SAAS,KAAK,CAAC;AAAA,IAChD;AAGA,eAAW,YAAY,KAAK,WAAW;AAErC,YAAM,cAAc,KAAK,kBAAkB,IAAI,QAAQ;AACvD,UAAI,gBAAgB,UAAa,CAAC,eAAe,SAAS,WAAW,EAAG;AACxE,UAAI,SAAS,gBAAgB,CAAC,SAAS,aAAa,SAAS,EAAG;AAChE,YAAM,SAAS,MAAM,SAAS,OAAO,SAAS;AAC9C,UAAI,QAAQ;AACV,YAAI,OAAO,aAAa,WAAW;AACjC,sBAAY,QAAQ,OAAO,OAAO;AAAA,QACpC,OAAO;AACL,sBAAY,KAAK,OAAO,OAAO;AAAA,QACjC;AAAA,MACF;AAAA,IACF;AAGA,QAAI,UAAU,QAAQ,OAAO,KAAK,UAAU,IAAI,EAAE,SAAS,GAAG;AAC5D,YAAM,EAAE,MAAM,gBAAgB,MAAM,UAAU,KAAK;AAAA,IACrD;AAGA,QAAI,QAAQ;AACV,kBAAY,KAAK;AAAA;AAAA,EAAgB,OAAO,OAAO,EAAE;AAAA,IACnD;AAGA,UAAM,cAAc,MAAM,KAAK,MAAM;AAAA,MACnC;AAAA,MACA;AAAA,QACE;AAAA,QACA,eAAe,YAAY;AAAA,QAC3B;AAAA,MACF;AAAA,MACA;AAAA,IACF;AAEA,QAAI,YAAY,gBAAgB;AAC9B,kBAAY,SAAS;AACrB,kBAAY,KAAK,YAAY,cAAc;AAAA,IAC7C,OAAO;AACL,UAAI,YAAY,cAAe,aAAY,QAAQ,YAAY,aAAa;AAC5E,UAAI,YAAY,aAAc,aAAY,KAAK,YAAY,YAAY;AAAA,IACzE;AAEA,UAAM,eAAe,YAAY,KAAK,MAAM,EAAE,KAAK,KAAK;AAGxD,UAAM,cAAc,KAAK,cAAc,OAAO;AAC9C,QAAI,WAAW;AACf,QAAI,YAAY;AAIhB,QAAI,iBAAiB;AACrB,UAAM,iBAAiB,oBAAI,IAAoB;AAE/C,aAAS,YAAY,GAAG,YAAY,KAAK,eAAe,aAAa;AACnE,UAAI,YAAY,SAAS;AACvB,cAAM,EAAE,MAAM,SAAS,OAAO,WAAW,MAAM,UAAU;AACzD;AAAA,MACF;AAMA,UAAI,kBAAkB,KAAK,qBAAqB;AAC9C,cAAM;AAAA,UACJ,MAAM;AAAA,UACN,UAAU;AAAA,UACV,SAAS,gBAAgB,KAAK,mBAAmB;AAAA,UACjD,UAAU;AAAA,QACZ;AACA;AAAA,MACF;AACA,YAAM,eAAe,CAAC,GAAG,eAAe,QAAQ,CAAC,EAAE;AAAA,QACjD,CAAC,CAAC,EAAE,KAAK,MAAM,SAAS,KAAK;AAAA,MAC/B;AACA,UAAI,cAAc;AAChB,cAAM;AAAA,UACJ,MAAM;AAAA,UACN,UAAU,aAAa,CAAC;AAAA,UACxB,SAAS,YAAY,aAAa,CAAC,CAAC,WAAW,aAAa,CAAC,CAAC;AAAA,UAC9D,UAAU;AAAA,QACZ;AACA;AAAA,MACF;AAGA,YAAM,KAAK,MAAM;AAAA,QACf;AAAA,QACA;AAAA,UACE;AAAA,UACA,OAAO,KAAK,IAAI;AAAA,UAChB,YAAY;AAAA,QACd;AAAA,QACA;AAAA,MACF;AAGA,YAAM,mBAKD,CAAC;AACN,UAAI,YAAY;AAKhB,YAAM,aAAa,YAAY,sBAAsB,KAAK;AAC1D,YAAM,qBAAqB,IAAI,gBAAgB;AAC/C,YAAM,iBAAiB,YAAY,IAAI,CAAC,aAAa,mBAAmB,MAAM,CAAC;AAC/E,UAAI;AACJ,YAAM,cAAc,MAAM;AACxB,YAAI,cAAe,cAAa,aAAa;AAC7C,wBAAgB,WAAW,MAAM,mBAAmB,MAAM,GAAG,UAAU;AAAA,MACzE;AACA,YAAM,iBAAiB,MAAM;AAC3B,YAAI,cAAe,cAAa,aAAa;AAC7C,wBAAgB;AAAA,MAClB;AAEA,UAAI;AACF,oBAAY;AACZ,cAAM,SAAS,KAAK,IAAI,SAAS,aAAa,KAAK,MAAM,cAAc,cAAc,UAAU,GAAG;AAAA,UAChG,QAAQ;AAAA,UACR,mBAAmB;AAAA,UACnB,aAAa;AAAA,UACb,GAAI,gBAAgB,EAAE,cAAc,IAAI,CAAC;AAAA,QAC3C,CAAC;AAED,yBAAiB,SAAS,QAAQ;AAChC,cAAI,YAAY,QAAS;AACzB,cAAI,mBAAmB,OAAO,QAAS;AACvC,sBAAY;AACZ,iBAAO,KAAK,YAAY,OAAO,kBAAkB,CAAC,MAAM;AACtD,yBAAa;AACb,wBAAY;AAAA,UACd,CAAC;AAAA,QACH;AACA,uBAAe;AAEf,YAAI,mBAAmB,OAAO,WAAW,CAAC,YAAY,SAAS;AAC7D,gBAAM;AAAA,YACJ,MAAM;AAAA,YACN,OAAO,0CAAqC,UAAU;AAAA,YACtD,MAAM;AAAA,UACR;AACA;AAAA,QACF;AAAA,MACF,SAAS,KAAK;AACZ,uBAAe;AACf,YAAI,mBAAmB,OAAO,WAAW,CAAC,YAAY,SAAS;AAC7D,gBAAM;AAAA,YACJ,MAAM;AAAA,YACN,OAAO,0CAAqC,UAAU;AAAA,YACtD,MAAM;AAAA,UACR;AACA;AAAA,QACF;AACA,cAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,cAAM,EAAE,MAAM,SAAS,OAAO,KAAK,MAAM,YAAY;AACrD;AAAA,MACF;AAEA;AAGA,YAAM,qBAAqB,iBAAiB,OAAO,CAAC,OAAO,GAAG,SAAS,MAAS;AAGhF,wBAAkB,mBAAmB;AACrC,iBAAW,MAAM,oBAAoB;AACnC,uBAAe,IAAI,GAAG,WAAW,eAAe,IAAI,GAAG,QAAQ,KAAK,KAAK,CAAC;AAAA,MAC5E;AAGA,YAAM,KAAK,QAAQ,cAAc;AAAA,QAC/B;AAAA,QACA,MAAM;AAAA,QACN,SAAS;AAAA,QACT,GAAI,mBAAmB,SAAS,KAAK;AAAA,UACnC,WAAW,mBAAmB,IAAI,CAAC,QAAQ;AAAA,YACzC,IAAI,GAAG;AAAA,YACP,MAAM,GAAG;AAAA,YACT,OAAO,GAAG;AAAA,UACZ,EAAE;AAAA,QACJ;AAAA,MACF,CAAC;AAGD,YAAM,KAAK,MAAM;AAAA,QACf;AAAA,QACA;AAAA,UACE;AAAA,UACA,MAAM;AAAA,UACN,OAAO,EAAE,aAAa,GAAG,cAAc,EAAE;AAAA,QAC3C;AAAA,QACA;AAAA,MACF;AAGA,UAAI,mBAAmB,SAAS,GAAG;AACjC,cAAM,mBAAqC,CAAC;AAC5C,YAAI,UAAW,kBAAiB,KAAK,EAAE,MAAM,QAAQ,MAAM,UAAU,CAAC;AACtE,mBAAW,MAAM,oBAAoB;AACnC,2BAAiB,KAAK;AAAA,YACpB,MAAM;AAAA,YACN,IAAI,GAAG;AAAA,YACP,MAAM,GAAG;AAAA,YACT,OAAO,GAAG;AAAA,UACZ,CAAC;AAAA,QACH;AACA,oBAAY,KAAK,EAAE,MAAM,aAAa,SAAS,iBAAiB,CAAC;AAAA,MACnE,OAAO;AACL,oBAAY,KAAK,EAAE,MAAM,aAAa,SAAS,UAAU,CAAC;AAC1D;AAAA,MACF;AAOA,YAAM,iBAKD,CAAC;AAEN,YAAM,gBAAgB,KAAK,mBAAmB,WAAW;AAEzD,YAAM,cAAc;AAAA,QAClB;AAAA,QACA;AAAA,QACA,UAAU,KAAK;AAAA,QACf,YAAY,KAAK;AAAA,QACjB,SAAS,KAAK;AAAA,QACd,eAAe,YAAY;AAAA,QAC3B,aAAa,YAAY;AAAA,QACzB,aAAa;AAAA,QACb,cAAc,YAAY,SAAS;AAAA,QACnC;AAAA,QACA,MAAM,CAAC,UAMD;AACJ,yBAAe,KAAK;AAAA,YAClB,UAAU,MAAM;AAAA,YAChB,SAAS,MAAM;AAAA,YACf,GAAI,MAAM,YAAY,UAAa,EAAE,SAAS,MAAM,QAAQ;AAAA,YAC5D,UAAU,MAAM,YAAY;AAAA,UAC9B,CAAC;AAAA,QACH;AAAA,QACA,mBAAmB,KAAK;AAAA,QACxB,GAAI,gBAAgB,EAAE,SAAS,cAAc,IAAI,CAAC;AAAA,MACpD;AAKA,YAAM,UAAqB,CAAC;AAE5B,iBAAW,MAAM,oBAAoB;AACnC,cAAM,eAAe,MAAM,KAAK,MAAM;AAAA,UACpC;AAAA,UACA;AAAA,YACE;AAAA,YACA,YAAY,GAAG;AAAA,YACf,UAAU,GAAG;AAAA,YACb,MAAM,GAAG;AAAA,UACX;AAAA,UACA;AAAA,QACF;AAEA,YAAI,aAAa,OAAO;AACtB,gBAAM;AAAA,YACJ,MAAM;AAAA,YACN,YAAY,GAAG;AAAA,YACf,UAAU,GAAG;AAAA,YACb,IAAI;AAAA,YACJ,YAAY;AAAA,YACZ,QAAQ,aAAa;AAAA,UACvB;AACA,kBAAQ,KAAK;AAAA,YACX,YAAY,GAAG;AAAA,YACf,MAAM,GAAG;AAAA,YACT,MAAM,GAAG;AAAA,YACT,UAAU,aAAa;AAAA,UACzB,CAAC;AACD;AAAA,QACF;AAEA,cAAM,gBAAgB,aAAa,QAAQ,GAAG;AAC9C,cAAM;AAAA,UACJ,MAAM;AAAA,UACN,YAAY,GAAG;AAAA,UACf,UAAU,GAAG;AAAA,UACb,MAAM;AAAA,QACR;AACA,gBAAQ,KAAK,EAAE,YAAY,GAAG,YAAY,MAAM,GAAG,UAAU,MAAM,cAAc,CAAC;AAAA,MACpF;AAGA,YAAM,aAAa,QAChB,OAAO,CAAC,MAAM,EAAE,aAAa,MAAS,EACtC,IAAI,CAAC,OAAO,EAAE,YAAY,EAAE,YAAY,MAAM,EAAE,MAAM,MAAM,EAAE,KAAK,EAAE;AAExE,YAAM,YAAY,KAAK,IAAI;AAC3B,YAAM,cACJ,WAAW,SAAS,IAChB,MAAM,KAAK,MAAM,gBAAgB,YAAY,aAAa,cAAc,UAAU,IAClF,CAAC;AACP,YAAM,gBAAgB,IAAI,IAAI,YAAY,IAAI,CAAC,MAAM,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC;AAKvE,iBAAW,MAAM,gBAAgB;AAC/B,cAAM,EAAE,MAAM,iBAAiB,GAAG,GAAG;AAAA,MACvC;AACA,qBAAe,SAAS;AAGxB,YAAM,oBAAsC,CAAC;AAE7C,iBAAW,KAAK,SAAS;AACvB,cAAM,aAAa,KAAK,IAAI,IAAI;AAChC,YAAI;AAEJ,YAAI,EAAE,aAAa,QAAW;AAC5B,mBAAS,EAAE,IAAI,OAAO,OAAO,EAAE,UAAU,MAAM,mBAAmB;AAAA,QAEpE,OAAO;AACL,gBAAM,aAAa,cAAc,IAAI,EAAE,UAAU;AACjD,mBAAS,YAAY,UAAU;AAAA,YAC7B,IAAI;AAAA,YACJ,OAAO;AAAA,YACP,MAAM;AAAA,UACR;AACA,gBAAM;AAAA,YACJ,MAAM;AAAA,YACN,YAAY,EAAE;AAAA,YACd,UAAU,EAAE;AAAA,YACZ,IAAI,OAAO;AAAA,YACX;AAAA,YACA,QAAQ,OAAO,KAAK,OAAO,QAAQ,OAAO;AAAA,UAC5C;AACA,gBAAM,KAAK,MAAM;AAAA,YACf;AAAA,YACA;AAAA,cACE;AAAA,cACA,UAAU,EAAE;AAAA,cACZ;AAAA,cACA;AAAA,YACF;AAAA,YACA;AAAA,UACF;AAAA,QACF;AAGA,cAAM,KAAK,QAAQ,cAAc;AAAA,UAC/B;AAAA,UACA,MAAM;AAAA,UACN,SAAS,OAAO,KAAK,OAAO,QAAQ,OAAO;AAAA,UAC3C,YAAY,EAAE;AAAA,UACd,UAAU,EAAE;AAAA,QACd,CAAC;AAED,0BAAkB,KAAK;AAAA,UACrB,MAAM;AAAA,UACN,aAAa,EAAE;AAAA,UACf,SAAS,OAAO,KAAK,OAAO,QAAQ,OAAO;AAAA,UAC3C,UAAU,CAAC,OAAO;AAAA,QACpB,CAAC;AAAA,MACH;AAGA,kBAAY,KAAK,EAAE,MAAM,QAAQ,SAAS,kBAAkB,CAAC;AAAA,IAC/D;AAOA,UAAM,KAAK,QAAQ,YAAY,WAAW,EAAE,cAAc,UAAU,CAAC;AAGrE,UAAM,KAAK,MAAM,SAAS,cAAc,EAAE,WAAW,MAAM,UAAU,UAAU,GAAG,cAAc;AAEhG,UAAM,EAAE,MAAM,QAAQ,MAAM,UAAU,UAAU;AAAA,EAClD;AAAA,EAEA,CAAS,YACP,OACA,kBAMA,QACuB;AACvB,YAAQ,MAAM,MAAM;AAAA,MAClB,KAAK;AACH,eAAO,MAAM,IAAI;AACjB,cAAM,EAAE,MAAM,cAAc,MAAM,MAAM,KAAK;AAC7C;AAAA,MAEF,KAAK;AACH,cAAM,EAAE,MAAM,kBAAkB,UAAU,MAAM,SAAS;AACzD;AAAA,MAEF,KAAK;AACH,yBAAiB,KAAK;AAAA,UACpB,YAAY,MAAM;AAAA,UAClB,UAAU,MAAM;AAAA,UAChB,aAAa;AAAA,QACf,CAAC;AACD;AAAA,MAEF,KAAK,kBAAkB;AACrB,cAAM,KAAK,iBAAiB,KAAK,CAAC,MAAM,EAAE,eAAe,MAAM,UAAU;AACzE,YAAI,GAAI,IAAG,eAAe,MAAM;AAChC;AAAA,MACF;AAAA,MAEA,KAAK,gBAAgB;AACnB,cAAM,KAAK,iBAAiB,KAAK,CAAC,MAAM,EAAE,eAAe,MAAM,UAAU;AACzE,YAAI,IAAI;AACN,cAAI;AACF,eAAG,OAAO,KAAK,MAAM,MAAM,aAAa,GAAG,WAAW;AAAA,UACxD,QAAQ;AACN,eAAG,OAAO,CAAC;AAAA,UACb;AAAA,QACF;AACA;AAAA,MACF;AAAA,MAEA,KAAK;AACH,cAAM;AAAA,UACJ,MAAM;AAAA,UACN,aAAa,MAAM,MAAM;AAAA,UACzB,cAAc,MAAM,MAAM;AAAA,UAC1B,kBAAkB,MAAM,MAAM;AAAA,QAChC;AACA;AAAA,MAEF,KAAK;AAEH;AAAA,IACJ;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,cAAc,QAAoC;AACxD,UAAM,WAAsB,CAAC;AAE7B,eAAW,OAAO,QAAQ;AACxB,UAAI,IAAI,SAAS,SAAU;AAE3B,UAAI,IAAI,SAAS,QAAQ;AACvB,iBAAS,KAAK,EAAE,MAAM,QAAQ,SAAS,IAAI,QAAQ,CAAC;AAAA,MACtD,WAAW,IAAI,SAAS,aAAa;AACnC,YAAI,IAAI,aAAa,IAAI,UAAU,SAAS,GAAG;AAC7C,gBAAM,UAA4B,CAAC;AACnC,cAAI,IAAI,QAAS,SAAQ,KAAK,EAAE,MAAM,QAAQ,MAAM,IAAI,QAAQ,CAAC;AACjE,qBAAW,MAAM,IAAI,WAAW;AAC9B,oBAAQ,KAAK,EAAE,MAAM,YAAY,IAAI,GAAG,IAAI,MAAM,GAAG,MAAM,OAAO,GAAG,MAAM,CAAC;AAAA,UAC9E;AACA,mBAAS,KAAK,EAAE,MAAM,aAAa,QAAQ,CAAC;AAAA,QAC9C,OAAO;AACL,mBAAS,KAAK,EAAE,MAAM,aAAa,SAAS,IAAI,QAAQ,CAAC;AAAA,QAC3D;AAAA,MACF,WAAW,IAAI,SAAS,eAAe;AACrC,cAAM,cAA8B;AAAA,UAClC,MAAM;AAAA,UACN,aAAa,IAAI,cAAc;AAAA,UAC/B,SAAS,IAAI;AAAA,UACb,UAAU;AAAA,QACZ;AACA,cAAM,OAAO,SAAS,SAAS,SAAS,CAAC;AAEzC,YAAI,MAAM,SAAS,UAAU,MAAM,QAAQ,KAAK,OAAO,GAAG;AACxD,UAAC,KAAK,QAA6B,KAAK,WAAW;AAAA,QACrD,OAAO;AACL,mBAAS,KAAK,EAAE,MAAM,QAAQ,SAAS,CAAC,WAAW,EAAE,CAAC;AAAA,QACxD;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcQ,mBAAmB,aAAqD;AAC9E,QAAI,CAAC,KAAK,QAAS,QAAO;AAE1B,UAAM,YAAY,KAAK,WAAW,KAAK,QAAQ,GAAG,QAAQ;AAC1D,UAAM,MAAM,KAAK;AACjB,UAAM,OAAO,YAAY;AACzB,UAAM,SAAS,GAAG,KAAK,WAAW,iBAAiB,IAAI,CAAC;AAExD,UAAM,UAAU,YAAY;AAC5B,UAAM,eACJ,SAAS,QAAQ,QAAQ,KAAK,SAAS,IACnC,QAAQ,KAAK,IAAI,CAAC,MAAM,WAAW,GAAG,EAAE,WAAW,MAAM,IAAI,CAAC,CAAC,IAC/D,CAAC,QAAQ,GAAG,KAAK,WAAW,QAAQ,CAAC,KAAK,GAAG;AACnD,UAAM,gBACJ,SAAS,SAAS,QAAQ,MAAM,SAAS,IACrC,QAAQ,MAAM,IAAI,CAAC,MAAM,WAAW,GAAG,EAAE,WAAW,MAAM,IAAI,CAAC,CAAC,IAChE,CAAC,QAAQ,GAAG;AAElB,WAAO,IAAI,cAAc,KAAK,SAAS,EAAE,MAAM,cAAc,OAAO,cAAc,CAAC;AAAA,EACrF;AACF;AAEA,SAAS,WACP,UACA,MACQ;AACR,SAAO,SACJ,QAAQ,qBAAqB,KAAK,SAAS,EAC3C,QAAQ,eAAe,KAAK,IAAI,EAChC,QAAQ,cAAc,KAAK,GAAG;AACnC;;;AS/0BO,IAAM,iBAAN,MAAqC;AAAA,EACzB,YAAY,oBAAI,IAAiC;AAAA,EAElE,SAAS,MAAc,SAAoC;AACzD,SAAK,UAAU,IAAI,MAAM,OAAO;AAAA,EAClC;AAAA,EAEA,OAAO,MAAc,QAAc;AACjC,UAAM,UAAU,KAAK,UAAU,IAAI,IAAI;AACvC,QAAI,CAAC,SAAS;AACZ,YAAM,IAAI;AAAA,QACR,yBAAyB,IAAI,kBAAkB,CAAC,GAAG,KAAK,UAAU,KAAK,CAAC,EAAE,KAAK,IAAI,CAAC;AAAA,MACtF;AAAA,IACF;AACA,UAAM,WAAW,QAAQ,MAAM;AAC/B,QAAI,CAAC,UAAU;AACb,YAAM,IAAI,MAAM,uBAAuB,IAAI,iBAAiB;AAAA,IAC9D;AACA,WAAO;AAAA,EACT;AAAA,EAEA,IAAI,MAAuB;AACzB,WAAO,KAAK,UAAU,IAAI,IAAI;AAAA,EAChC;AAAA,EAEA,QAAkB;AAChB,WAAO,CAAC,GAAG,KAAK,UAAU,KAAK,CAAC;AAAA,EAClC;AACF;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/agent-loop.ts","../../storage-fs/src/fs-storage.ts","../../storage-fs/src/in-memory-storage.ts","../../storage-fs/src/scoped-storage.ts","../src/defaults/in-memory-session.ts","../src/defaults/noop-memory.ts","../src/defaults/noop-personality.ts","../src/hook-registry.ts","../src/tool-registry.ts","../src/plugin-registry.ts","../src/providers/chained-provider.ts"],"sourcesContent":["import { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { ScopedStorage } from '@ethosagent/storage-fs';\nimport type {\n CompletionChunk,\n ContextInjector,\n HookRegistry,\n LLMProvider,\n MemoryProvider,\n Message,\n MessageContent,\n PersonalityConfig,\n PersonalityRegistry,\n PromptContext,\n SessionStore,\n Storage,\n StoredMessage,\n ToolFilterOpts,\n ToolRegistry,\n ToolResult,\n} from '@ethosagent/types';\n\nimport { InMemorySessionStore } from './defaults/in-memory-session';\nimport { NoopMemoryProvider } from './defaults/noop-memory';\nimport { DefaultPersonalityRegistry } from './defaults/noop-personality';\nimport { DefaultHookRegistry } from './hook-registry';\nimport { DefaultToolRegistry } from './tool-registry';\n\n// ---------------------------------------------------------------------------\n// Agent events emitted by run()\n// ---------------------------------------------------------------------------\n\nexport type AgentEvent =\n | { type: 'text_delta'; text: string }\n | { type: 'thinking_delta'; thinking: string }\n | { type: 'tool_start'; toolCallId: string; toolName: string; args: unknown }\n // Phase 30.2 — `audience` gates whether channel adapters / chat.ts surface\n // this event to the user. Default is `'internal'`; tools opt in to `'user'`\n // per event. Framework-emitted budget warnings are `'user'` (see step 7).\n | {\n type: 'tool_progress';\n toolName: string;\n message: string;\n percent?: number;\n audience: 'internal' | 'user';\n }\n | {\n type: 'tool_end';\n toolCallId: string;\n toolName: string;\n ok: boolean;\n durationMs: number;\n // Phase 30.2 — same boundary applies to tool_end success rendering.\n // Failures (`ok: false`) ignore the field and always render.\n audience?: 'internal' | 'user';\n /**\n * Tool output body — the success value when `ok`, or the error\n * message when `ok: false`. Optional so consumers that only care\n * about the status (CLI ASCII chips, telemetry) can ignore it.\n * The web chip surfaces this on expand-on-click without a\n * follow-up history fetch.\n */\n result?: string;\n }\n | { type: 'usage'; inputTokens: number; outputTokens: number; estimatedCostUsd: number }\n | { type: 'error'; error: string; code: string }\n | { type: 'done'; text: string; turnCount: number }\n // Emitted once after context injectors run; carries any metadata they wrote to PromptContext.meta.\n | { type: 'context_meta'; data: Record<string, unknown> }\n /**\n * Emitted once at the very start of each turn, before any LLM call.\n * Carries the resolved provider/model and the routing source so consumers\n * (TUI status bar, CLI verbose mode, telemetry) can show the effective model.\n * `source` reflects which routing rule selected the model (see model_update.md).\n */\n | {\n type: 'run_start';\n provider: string;\n model: string;\n source: 'team-coordinator' | 'team-personality' | 'personality' | 'global';\n };\n\n// ---------------------------------------------------------------------------\n// Config\n// ---------------------------------------------------------------------------\n\nexport interface AgentLoopConfig {\n llm: LLMProvider;\n tools?: ToolRegistry;\n personalities?: PersonalityRegistry;\n memory?: MemoryProvider;\n session?: SessionStore;\n hooks?: HookRegistry;\n injectors?: ContextInjector[];\n /**\n * Maps each plugin-registered injector to its plugin id so AgentLoop can\n * gate injectors by personality. Built-in injectors are absent (always fire).\n * Populated by PluginApiImpl.registerInjector(); passed through from wiring.\n */\n injectorPluginIds?: Map<ContextInjector, string>;\n /**\n * Base Storage instance handed to tools via `ToolContext.storage` after\n * being decorated with a ScopedStorage that enforces the active\n * personality's `fs_reach` allowlist. When unset, ToolContext.storage is\n * left undefined and tools fall back to unrestricted node:fs (legacy\n * behavior — existing CLI/TUI tests don't need a storage instance).\n */\n storage?: Storage;\n /**\n * Absolute path to ~/.ethos/ used for `${ETHOS_HOME}` substitution in\n * `fs_reach` paths. Defaults to `${HOME}/.ethos`. Required only when\n * `storage` is set.\n */\n dataDir?: string;\n // Maps personality ID → model ID. Resolution: modelRouting[id] → personality.model → llm.model\n modelRouting?: Record<string, string>;\n options?: {\n maxIterations?: number;\n historyLimit?: number;\n platform?: string;\n workingDir?: string;\n resultBudgetChars?: number;\n /**\n * Hard cap on total tool calls per user turn (across all LLM iterations).\n * Defaults to 20. Trips a `tool_progress` warning and exits cleanly.\n * See plan/IMPROVEMENT.md P1-3.\n */\n maxToolCallsPerTurn?: number;\n /**\n * Hard cap on the number of times the same tool name can be invoked in a\n * single turn. Catches the \"infinite loop on a single tool\" failure mode\n * (e.g. tts loop reported as OpenClaw #67744). Defaults to 5.\n */\n maxIdenticalToolCalls?: number;\n /**\n * Default streaming watchdog in milliseconds. If no chunk arrives from the\n * LLM within this window, the agent aborts the stream and emits an error.\n * Reset on every chunk. Personalities can override via\n * `personality.streamingTimeoutMs`. Defaults to 120000 (2 minutes).\n */\n streamingTimeoutMs?: number;\n };\n}\n\nexport interface RunOptions {\n sessionKey?: string;\n personalityId?: string;\n abortSignal?: AbortSignal;\n /**\n * Identifier surfaced to tools as `ToolContext.agentId`. Delegation tools\n * use this to thread spawn depth (`depth:N`) into child loops so\n * `MAX_SPAWN_DEPTH` can be enforced across recursive sub-agent calls.\n */\n agentId?: string;\n}\n\n// ---------------------------------------------------------------------------\n// AgentLoop\n// ---------------------------------------------------------------------------\n\nexport class AgentLoop {\n private readonly llm: LLMProvider;\n private readonly tools: ToolRegistry;\n private readonly personalities: PersonalityRegistry;\n private readonly memory: MemoryProvider;\n private readonly session: SessionStore;\n /** Public so surfaces (web, ACP) can register late-binding hooks they own\n * without re-running the whole wiring factory. The CLI/TUI register hooks\n * before construction; web registers an approval hook after createAgentLoop\n * returns. */\n readonly hooks: HookRegistry;\n private readonly injectors: ContextInjector[];\n private readonly injectorPluginIds: Map<ContextInjector, string>;\n private readonly maxIterations: number;\n private readonly historyLimit: number;\n private readonly platform: string;\n private readonly workingDir: string;\n private readonly resultBudgetChars: number;\n private readonly maxToolCallsPerTurn: number;\n private readonly maxIdenticalToolCalls: number;\n private readonly streamingTimeoutMs: number;\n private readonly modelRouting: Record<string, string>;\n private readonly storage?: Storage;\n private readonly dataDir?: string;\n /** Per-session accumulated spend in USD. Keyed by sessionKey. Reset via resetSessionCost(). */\n private readonly sessionCosts = new Map<string, number>();\n\n constructor(config: AgentLoopConfig) {\n this.llm = config.llm;\n this.tools = config.tools ?? new DefaultToolRegistry();\n this.personalities = config.personalities ?? new DefaultPersonalityRegistry();\n this.memory = config.memory ?? new NoopMemoryProvider();\n this.session = config.session ?? new InMemorySessionStore();\n this.hooks = config.hooks ?? new DefaultHookRegistry();\n this.injectors = (config.injectors ?? []).sort((a, b) => b.priority - a.priority);\n this.injectorPluginIds = config.injectorPluginIds ?? new Map();\n this.maxIterations = config.options?.maxIterations ?? 50;\n this.historyLimit = config.options?.historyLimit ?? 200;\n this.platform = config.options?.platform ?? 'cli';\n this.workingDir = config.options?.workingDir ?? process.cwd();\n this.resultBudgetChars = config.options?.resultBudgetChars ?? 80_000;\n this.maxToolCallsPerTurn = config.options?.maxToolCallsPerTurn ?? 20;\n this.maxIdenticalToolCalls = config.options?.maxIdenticalToolCalls ?? 5;\n this.streamingTimeoutMs = config.options?.streamingTimeoutMs ?? 120_000;\n this.modelRouting = config.modelRouting ?? {};\n if (config.storage) this.storage = config.storage;\n if (config.dataDir) this.dataDir = config.dataDir;\n }\n\n /** Returns all available tools for inventory display (e.g. TUI splash screen). */\n getAvailableTools(): import('@ethosagent/types').Tool[] {\n return this.tools.getAvailable();\n }\n\n /** Returns all registered personalities for inventory display. */\n getPersonalityIds(): string[] {\n return this.personalities.list().map((p) => p.id);\n }\n\n /** Returns the budget cap for the given personality (undefined = no cap). */\n getPersonalityBudgetCap(personalityId?: string): number | undefined {\n const p =\n (personalityId ? this.personalities.get(personalityId) : null) ??\n this.personalities.getDefault();\n return p.budgetCapUsd;\n }\n\n /** Returns accumulated session spend in USD (0 if no spend recorded yet). */\n getSessionCost(sessionKey: string): number {\n return this.sessionCosts.get(sessionKey) ?? 0;\n }\n\n /** Resets the session spend counter — call after /new or /personality switch. */\n resetSessionCost(sessionKey: string): void {\n this.sessionCosts.delete(sessionKey);\n }\n\n async *run(text: string, opts: RunOptions = {}): AsyncGenerator<AgentEvent> {\n const abortSignal = opts.abortSignal ?? new AbortController().signal;\n const sessionKey = opts.sessionKey ?? `${this.platform}:default`;\n\n // Step 1: Resolve or create session\n const ethosSession =\n (await this.session.getSessionByKey(sessionKey)) ??\n (await this.session.createSession({\n key: sessionKey,\n platform: this.platform,\n model: this.llm.model,\n provider: this.llm.name,\n personalityId: opts.personalityId,\n workingDir: this.workingDir,\n usage: {\n inputTokens: 0,\n outputTokens: 0,\n cacheReadTokens: 0,\n cacheCreationTokens: 0,\n estimatedCostUsd: 0,\n apiCallCount: 0,\n compactionCount: 0,\n },\n }));\n\n const sessionId = ethosSession.id;\n const personality =\n (opts.personalityId ? this.personalities.get(opts.personalityId) : null) ??\n this.personalities.getDefault();\n\n // Budget cap check — refuse before any LLM work when the session has already\n // exceeded the personality's per-session spending limit.\n const currentSpend = this.sessionCosts.get(sessionKey) ?? 0;\n if (personality.budgetCapUsd != null && currentSpend >= personality.budgetCapUsd) {\n yield {\n type: 'error',\n error: `Budget cap of $${personality.budgetCapUsd.toFixed(2)} exceeded for this session ($${currentSpend.toFixed(4)} spent). Use /budget reset to start a new budget window.`,\n code: 'BUDGET_EXCEEDED',\n };\n yield { type: 'done', text: '', turnCount: 0 };\n return;\n }\n\n // Resolve effective model: explicit per-personality routing > LLM base model.\n // personality.model is intentionally skipped — those IDs are Anthropic-specific\n // and break non-Anthropic providers (OpenRouter, Gemini, Ollama, etc.).\n // Configure overrides via modelRouting in ~/.ethos/config.yaml instead.\n const personalityOverride = this.modelRouting[personality.id];\n const effectiveModel = personalityOverride ?? this.llm.model;\n const modelOverride = effectiveModel !== this.llm.model ? effectiveModel : undefined;\n\n // Phase 5: emit run_start trace so consumers (TUI, CLI verbose, telemetry)\n // can surface the resolved provider/model and routing source.\n yield {\n type: 'run_start',\n provider: this.llm.name,\n model: effectiveModel,\n source: personalityOverride ? 'personality' : 'global',\n };\n\n // Allowed tool names for this personality (undefined = no restriction)\n const allowedTools = personality.toolset?.length ? personality.toolset : undefined;\n // Per-personality plugin + MCP gate (default-deny: missing field = no access)\n const allowedPlugins = personality.plugins ?? [];\n const filterOpts: ToolFilterOpts = {\n allowedMcpServers: personality.mcp_servers ?? [],\n allowedPlugins,\n };\n\n // Step 2: Fire session_start hooks\n await this.hooks.fireVoid(\n 'session_start',\n {\n sessionId,\n sessionKey,\n platform: this.platform,\n personalityId: personality.id,\n },\n allowedPlugins,\n );\n\n // Step 3: Persist the user message.\n //\n // Subagent task contract: the delegated task always lives in the child's\n // first user message (this `text`). It is NEVER copied into the system\n // prompt, NEVER injected via memory, and NEVER duplicated across both.\n // The regression test in\n // `extensions/tools-delegation/src/__tests__/task-contract.test.ts`\n // captures every `LLMProvider.complete()` request and asserts the marker\n // never appears in `opts.system` and appears exactly once across all\n // user-role messages.\n await this.session.appendMessage({\n sessionId,\n role: 'user',\n content: text,\n });\n\n // Step 4: Load history (trimmed to most-recent limit)\n const allMessages = await this.session.getMessages(sessionId, { limit: this.historyLimit });\n const history = allMessages.filter((m) => m.role !== 'system');\n\n // Step 5: Prefetch memory\n const memCtx = await this.memory.prefetch({\n sessionId,\n sessionKey,\n platform: this.platform,\n workingDir: this.workingDir,\n personalityId: personality.id,\n memoryScope: personality.memoryScope,\n query: text,\n });\n\n // Step 6: Build system prompt from injectors\n const promptCtx: PromptContext = {\n sessionId,\n sessionKey,\n platform: this.platform,\n model: this.llm.model,\n history,\n workingDir: this.workingDir,\n isDm: true,\n turnNumber: allMessages.length,\n personalityId: personality.id,\n };\n\n const systemParts: string[] = [];\n\n // ETHOS.md / personality identity — routes through Storage so ScopedStorage\n // and InMemoryStorage fixtures work correctly. Only runs when storage is\n // wired (production always provides it; tests without a real ethosFile skip).\n if (personality.ethosFile && this.storage) {\n const identity = await this.storage.read(personality.ethosFile);\n if (identity) systemParts.push(identity.trim());\n }\n\n // Context injectors sorted by priority (already sorted in constructor)\n for (const injector of this.injectors) {\n // Plugin-registered injectors only fire when the plugin is permitted.\n const injPluginId = this.injectorPluginIds.get(injector);\n if (injPluginId !== undefined && !allowedPlugins.includes(injPluginId)) continue;\n if (injector.shouldInject && !injector.shouldInject(promptCtx)) continue;\n const result = await injector.inject(promptCtx);\n if (result) {\n if (result.position === 'prepend') {\n systemParts.unshift(result.content);\n } else {\n systemParts.push(result.content);\n }\n }\n }\n\n // Emit injector metadata (e.g. skill_files_used) so eval harness can capture it.\n if (promptCtx.meta && Object.keys(promptCtx.meta).length > 0) {\n yield { type: 'context_meta', data: promptCtx.meta };\n }\n\n // Memory injected last, as context about the user\n if (memCtx) {\n systemParts.push(`## Memory\\n\\n${memCtx.content}`);\n }\n\n // Step 7: Before-prompt-build modifying hooks (plugins can prepend/append/override)\n const buildResult = await this.hooks.fireModifying(\n 'before_prompt_build',\n {\n sessionId,\n personalityId: personality.id,\n history,\n },\n allowedPlugins,\n );\n\n if (buildResult.overrideSystem) {\n systemParts.length = 0;\n systemParts.push(buildResult.overrideSystem);\n } else {\n if (buildResult.prependSystem) systemParts.unshift(buildResult.prependSystem);\n if (buildResult.appendSystem) systemParts.push(buildResult.appendSystem);\n }\n\n const systemPrompt = systemParts.join('\\n\\n').trim() || undefined;\n\n // Step 8: Agentic loop — LLM call → tool use → LLM call → ...\n const llmMessages = this.toLLMMessages(history);\n let fullText = '';\n let turnCount = 0;\n\n // Tool-call budget tracking — prevents runaway loops (see IMPROVEMENT.md P1-3).\n // Counted across all iterations within a single user turn.\n let totalToolCalls = 0;\n const toolNameCounts = new Map<string, number>();\n\n for (let iteration = 0; iteration < this.maxIterations; iteration++) {\n if (abortSignal.aborted) {\n yield { type: 'error', error: 'Aborted', code: 'aborted' };\n return;\n }\n\n // Budget guard: bail before the next LLM call if we've already exceeded\n // either the total tool-call budget or the per-tool repeat budget. The\n // previous iteration's tool_result is in llmMessages, so the LLM history\n // stays valid; we just refuse to call again.\n if (totalToolCalls >= this.maxToolCallsPerTurn) {\n yield {\n type: 'tool_progress',\n toolName: '_budget',\n message: `Stopped: hit ${this.maxToolCallsPerTurn}-tool-call budget for this turn`,\n audience: 'user',\n };\n break;\n }\n const overusedTool = [...toolNameCounts.entries()].find(\n ([, count]) => count >= this.maxIdenticalToolCalls,\n );\n if (overusedTool) {\n yield {\n type: 'tool_progress',\n toolName: overusedTool[0],\n message: `Stopped: ${overusedTool[0]} called ${overusedTool[1]} times in one turn (likely loop)`,\n audience: 'user',\n };\n break;\n }\n\n // Fire before_llm_call\n await this.hooks.fireVoid(\n 'before_llm_call',\n {\n sessionId,\n model: this.llm.model,\n turnNumber: turnCount,\n },\n allowedPlugins,\n );\n\n // Stream LLM response\n const pendingToolCalls: Array<{\n toolCallId: string;\n toolName: string;\n partialJson: string;\n args?: unknown;\n }> = [];\n let chunkText = '';\n\n // Streaming watchdog: cancel the stream if no chunk arrives within the\n // per-personality window. Reset every chunk so slow-but-progressing\n // reasoning is unaffected. See IMPROVEMENT.md P1-2 / OpenClaw #68596.\n const watchdogMs = personality.streamingTimeoutMs ?? this.streamingTimeoutMs;\n const watchdogController = new AbortController();\n const combinedSignal = AbortSignal.any([abortSignal, watchdogController.signal]);\n let watchdogTimer: ReturnType<typeof setTimeout> | undefined;\n const armWatchdog = () => {\n if (watchdogTimer) clearTimeout(watchdogTimer);\n watchdogTimer = setTimeout(() => watchdogController.abort(), watchdogMs);\n };\n const disarmWatchdog = () => {\n if (watchdogTimer) clearTimeout(watchdogTimer);\n watchdogTimer = undefined;\n };\n\n try {\n armWatchdog();\n const stream = this.llm.complete(\n llmMessages,\n this.tools.toDefinitions(allowedTools, filterOpts),\n {\n system: systemPrompt,\n cacheSystemPrompt: true,\n abortSignal: combinedSignal,\n ...(modelOverride ? { modelOverride } : {}),\n },\n );\n\n for await (const chunk of stream) {\n if (abortSignal.aborted) break;\n if (watchdogController.signal.aborted) break;\n armWatchdog();\n for (const event of this.handleChunk(chunk, pendingToolCalls, (t) => {\n chunkText += t;\n fullText += t;\n })) {\n if (event.type === 'usage') {\n this.sessionCosts.set(\n sessionKey,\n (this.sessionCosts.get(sessionKey) ?? 0) + event.estimatedCostUsd,\n );\n }\n yield event;\n }\n }\n disarmWatchdog();\n\n if (watchdogController.signal.aborted && !abortSignal.aborted) {\n yield {\n type: 'error',\n error: `LLM stream stalled — no chunk for ${watchdogMs}ms`,\n code: 'streaming_timeout',\n };\n return;\n }\n } catch (err) {\n disarmWatchdog();\n if (watchdogController.signal.aborted && !abortSignal.aborted) {\n yield {\n type: 'error',\n error: `LLM stream stalled — no chunk for ${watchdogMs}ms`,\n code: 'streaming_timeout',\n };\n return;\n }\n const msg = err instanceof Error ? err.message : String(err);\n yield { type: 'error', error: msg, code: 'llm_error' };\n return;\n }\n\n turnCount++;\n\n // Determine which tool calls completed parsing\n const completedToolCalls = pendingToolCalls.filter((tc) => tc.args !== undefined);\n\n // Update budget counters — these gate the NEXT iteration's LLM call.\n totalToolCalls += completedToolCalls.length;\n for (const tc of completedToolCalls) {\n toolNameCounts.set(tc.toolName, (toolNameCounts.get(tc.toolName) ?? 0) + 1);\n }\n\n // Persist assistant message — include tool_use references so history is LLM-replayable\n await this.session.appendMessage({\n sessionId,\n role: 'assistant',\n content: chunkText,\n ...(completedToolCalls.length > 0 && {\n toolCalls: completedToolCalls.map((tc) => ({\n id: tc.toolCallId,\n name: tc.toolName,\n input: tc.args,\n })),\n }),\n });\n\n // Fire after_llm_call\n await this.hooks.fireVoid(\n 'after_llm_call',\n {\n sessionId,\n text: chunkText,\n usage: { inputTokens: 0, outputTokens: 0 },\n },\n allowedPlugins,\n );\n\n // Push assistant message with proper content blocks for next iteration\n if (completedToolCalls.length > 0) {\n const assistantContent: MessageContent[] = [];\n if (chunkText) assistantContent.push({ type: 'text', text: chunkText });\n for (const tc of completedToolCalls) {\n assistantContent.push({\n type: 'tool_use',\n id: tc.toolCallId,\n name: tc.toolName,\n input: tc.args,\n });\n }\n llmMessages.push({ role: 'assistant', content: assistantContent });\n } else {\n llmMessages.push({ role: 'assistant', content: chunkText });\n break;\n }\n\n // Step 9: Pre-flight hooks → execute non-rejected tools → collect all results\n\n // Phase 30.2 — tools call ctx.emit() during execution; AsyncGenerator can't\n // yield from a sync callback, so we buffer per-batch then drain after\n // executeParallel resolves. Order is preserved (insertion = call order).\n const progressBuffer: Array<{\n toolName: string;\n message: string;\n percent?: number;\n audience: 'internal' | 'user';\n }> = [];\n\n const scopedStorage = this.buildScopedStorage(personality);\n\n const toolCtxBase = {\n sessionId,\n sessionKey,\n platform: this.platform,\n workingDir: this.workingDir,\n agentId: opts.agentId,\n personalityId: personality.id,\n memoryScope: personality.memoryScope,\n currentTurn: turnCount,\n messageCount: allMessages.length + turnCount,\n abortSignal,\n emit: (event: {\n type: 'progress';\n toolName: string;\n message: string;\n percent?: number;\n audience?: 'internal' | 'user';\n }) => {\n progressBuffer.push({\n toolName: event.toolName,\n message: event.message,\n ...(event.percent !== undefined && { percent: event.percent }),\n audience: event.audience ?? 'internal',\n });\n },\n resultBudgetChars: this.resultBudgetChars,\n ...(scopedStorage ? { storage: scopedStorage } : {}),\n };\n\n // Run before_tool_call hooks; build exec list with effective args\n // Rejected tools get tool_end ok:false + an error tool_result sent back to LLM\n type Prepped = { toolCallId: string; name: string; args: unknown; rejected?: string };\n const prepped: Prepped[] = [];\n\n for (const tc of completedToolCalls) {\n const beforeResult = await this.hooks.fireModifying(\n 'before_tool_call',\n {\n sessionId,\n toolCallId: tc.toolCallId,\n toolName: tc.toolName,\n args: tc.args,\n },\n allowedPlugins,\n );\n\n if (beforeResult.error) {\n yield {\n type: 'tool_end',\n toolCallId: tc.toolCallId,\n toolName: tc.toolName,\n ok: false,\n durationMs: 0,\n result: beforeResult.error,\n };\n prepped.push({\n toolCallId: tc.toolCallId,\n name: tc.toolName,\n args: tc.args,\n rejected: beforeResult.error,\n });\n continue;\n }\n\n const effectiveArgs = beforeResult.args ?? tc.args;\n yield {\n type: 'tool_start',\n toolCallId: tc.toolCallId,\n toolName: tc.toolName,\n args: effectiveArgs,\n };\n prepped.push({ toolCallId: tc.toolCallId, name: tc.toolName, args: effectiveArgs });\n }\n\n // Execute only non-rejected tools; results keyed by toolCallId\n const execInputs = prepped\n .filter((p) => p.rejected === undefined)\n .map((p) => ({ toolCallId: p.toolCallId, name: p.name, args: p.args }));\n\n const startedAt = Date.now();\n const execResults =\n execInputs.length > 0\n ? await this.tools.executeParallel(execInputs, toolCtxBase, allowedTools, filterOpts)\n : [];\n const execResultMap = new Map(execResults.map((r) => [r.toolCallId, r]));\n\n // Drain any progress events tools emitted during execution. Order is\n // call-order (across the parallel batch) — close enough for users; the\n // exact interleaving doesn't matter when ctx.emit is best-effort.\n for (const ev of progressBuffer) {\n yield { type: 'tool_progress', ...ev };\n }\n progressBuffer.length = 0;\n\n // Persist results + emit tool_end + build tool_result content blocks (original order)\n const toolResultContent: MessageContent[] = [];\n\n for (const p of prepped) {\n const durationMs = Date.now() - startedAt;\n let result: ToolResult;\n\n if (p.rejected !== undefined) {\n result = { ok: false, error: p.rejected, code: 'execution_failed' };\n // tool_end already emitted above; no after_tool_call hook for blocked tools\n } else {\n const execResult = execResultMap.get(p.toolCallId);\n result = execResult?.result ?? {\n ok: false,\n error: 'Tool result missing',\n code: 'execution_failed',\n };\n yield {\n type: 'tool_end',\n toolCallId: p.toolCallId,\n toolName: p.name,\n ok: result.ok,\n durationMs,\n result: result.ok ? result.value : result.error,\n };\n await this.hooks.fireVoid(\n 'after_tool_call',\n {\n sessionId,\n toolName: p.name,\n result,\n durationMs,\n },\n allowedPlugins,\n );\n }\n\n // Persist every result (rejected or not) so history matches what LLM sees\n await this.session.appendMessage({\n sessionId,\n role: 'tool_result',\n content: result.ok ? result.value : result.error,\n toolCallId: p.toolCallId,\n toolName: p.name,\n });\n\n toolResultContent.push({\n type: 'tool_result',\n tool_use_id: p.toolCallId,\n content: result.ok ? result.value : result.error,\n is_error: !result.ok,\n });\n }\n\n // Feed all tool results back to LLM as a single user message with content blocks\n llmMessages.push({ role: 'user', content: toolResultContent });\n }\n\n // Step 10: Memory writes flow through the `memory_save` tool during the\n // turn (see extensions/tools-memory). The agent-loop itself produces no\n // updates, so there's nothing to sync here.\n\n // Step 11: Update usage\n await this.session.updateUsage(sessionId, { apiCallCount: turnCount });\n\n // Step 12: Fire agent_done\n await this.hooks.fireVoid(\n 'agent_done',\n { sessionId, text: fullText, turnCount },\n allowedPlugins,\n );\n\n yield { type: 'done', text: fullText, turnCount };\n }\n\n private *handleChunk(\n chunk: CompletionChunk,\n pendingToolCalls: Array<{\n toolCallId: string;\n toolName: string;\n partialJson: string;\n args?: unknown;\n }>,\n onText: (t: string) => void,\n ): Generator<AgentEvent> {\n switch (chunk.type) {\n case 'text_delta':\n onText(chunk.text);\n yield { type: 'text_delta', text: chunk.text };\n break;\n\n case 'thinking_delta':\n yield { type: 'thinking_delta', thinking: chunk.thinking };\n break;\n\n case 'tool_use_start':\n pendingToolCalls.push({\n toolCallId: chunk.toolCallId,\n toolName: chunk.toolName,\n partialJson: '',\n });\n break;\n\n case 'tool_use_delta': {\n const tc = pendingToolCalls.find((t) => t.toolCallId === chunk.toolCallId);\n if (tc) tc.partialJson += chunk.partialJson;\n break;\n }\n\n case 'tool_use_end': {\n const tc = pendingToolCalls.find((t) => t.toolCallId === chunk.toolCallId);\n if (tc) {\n try {\n tc.args = JSON.parse(chunk.inputJson || tc.partialJson);\n } catch {\n tc.args = {};\n }\n }\n break;\n }\n\n case 'usage':\n yield {\n type: 'usage',\n inputTokens: chunk.usage.inputTokens,\n outputTokens: chunk.usage.outputTokens,\n estimatedCostUsd: chunk.usage.estimatedCostUsd,\n };\n break;\n\n case 'done':\n // finishReason available here for future context-compaction (Phase 3)\n break;\n }\n }\n\n // Reconstruct LLM-ready messages from stored history.\n // Assistant messages with tool calls produce proper tool_use content blocks.\n // Consecutive tool_result rows are grouped into a single user message.\n private toLLMMessages(stored: StoredMessage[]): Message[] {\n const messages: Message[] = [];\n\n for (const msg of stored) {\n if (msg.role === 'system') continue;\n\n if (msg.role === 'user') {\n messages.push({ role: 'user', content: msg.content });\n } else if (msg.role === 'assistant') {\n if (msg.toolCalls && msg.toolCalls.length > 0) {\n const content: MessageContent[] = [];\n if (msg.content) content.push({ type: 'text', text: msg.content });\n for (const tc of msg.toolCalls) {\n content.push({ type: 'tool_use', id: tc.id, name: tc.name, input: tc.input });\n }\n messages.push({ role: 'assistant', content });\n } else {\n messages.push({ role: 'assistant', content: msg.content });\n }\n } else if (msg.role === 'tool_result') {\n const resultBlock: MessageContent = {\n type: 'tool_result',\n tool_use_id: msg.toolCallId ?? '',\n content: msg.content,\n is_error: false,\n };\n const last = messages[messages.length - 1];\n // Append to existing tool_result user message, or start a new one\n if (last?.role === 'user' && Array.isArray(last.content)) {\n (last.content as MessageContent[]).push(resultBlock);\n } else {\n messages.push({ role: 'user', content: [resultBlock] });\n }\n }\n }\n\n return messages;\n }\n\n // ---------------------------------------------------------------------------\n // Per-turn ScopedStorage construction (Phase 4 — fs_reach enforcement).\n //\n // When the AgentLoop was wired with `storage` + `dataDir`, build a\n // ScopedStorage decorated with the active personality's `fs_reach`\n // allowlist for this turn. Substitutions (${ETHOS_HOME} / ${self} /\n // ${CWD}) are resolved here so the underlying storage-fs class stays\n // pristine. When `fs_reach` is unset, fall back to a sensible default:\n // read: [<ethosHome>/personalities/<self>/, <ethosHome>/skills/, <cwd>]\n // write: [<ethosHome>/personalities/<self>/, <cwd>]\n // ---------------------------------------------------------------------------\n\n private buildScopedStorage(personality: PersonalityConfig): Storage | undefined {\n if (!this.storage) return undefined;\n\n const ethosHome = this.dataDir ?? join(homedir(), '.ethos');\n const cwd = this.workingDir;\n const self = personality.id;\n const ownDir = `${join(ethosHome, 'personalities', self)}/`;\n\n const fsReach = personality.fs_reach;\n const readPrefixes =\n fsReach?.read && fsReach.read.length > 0\n ? fsReach.read.map((p) => substitute(p, { ethosHome, self, cwd }))\n : [ownDir, `${join(ethosHome, 'skills')}/`, cwd];\n const writePrefixes =\n fsReach?.write && fsReach.write.length > 0\n ? fsReach.write.map((p) => substitute(p, { ethosHome, self, cwd }))\n : [ownDir, cwd];\n\n return new ScopedStorage(this.storage, { read: readPrefixes, write: writePrefixes });\n }\n}\n\nfunction substitute(\n template: string,\n vars: { ethosHome: string; self: string; cwd: string },\n): string {\n return template\n .replace(/\\$\\{ETHOS_HOME\\}/g, vars.ethosHome)\n .replace(/\\$\\{self\\}/g, vars.self)\n .replace(/\\$\\{CWD\\}/g, vars.cwd);\n}\n","import {\n appendFile,\n mkdir,\n readdir,\n readFile,\n rename,\n rm,\n stat,\n writeFile,\n} from 'node:fs/promises';\nimport type {\n Storage,\n StorageDirEntry,\n StorageRemoveOptions,\n StorageWriteOptions,\n} from '@ethosagent/types';\n\nexport class FsStorage implements Storage {\n async read(path: string): Promise<string | null> {\n try {\n return await readFile(path, 'utf-8');\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return null;\n throw err;\n }\n }\n\n async exists(path: string): Promise<boolean> {\n try {\n await stat(path);\n return true;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return false;\n throw err;\n }\n }\n\n async mtime(path: string): Promise<number | null> {\n try {\n const s = await stat(path);\n return s.mtimeMs;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return null;\n throw err;\n }\n }\n\n async list(dir: string): Promise<string[]> {\n try {\n return await readdir(dir);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return [];\n throw err;\n }\n }\n\n async listEntries(dir: string): Promise<StorageDirEntry[]> {\n try {\n const entries = await readdir(dir, { withFileTypes: true });\n return entries.map((e) => ({ name: e.name, isDir: e.isDirectory() }));\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return [];\n throw err;\n }\n }\n\n async write(path: string, content: string, opts?: StorageWriteOptions): Promise<void> {\n if (opts?.mode !== undefined) {\n await writeFile(path, content, { encoding: 'utf-8', mode: opts.mode });\n return;\n }\n await writeFile(path, content, 'utf-8');\n }\n\n async append(path: string, content: string): Promise<void> {\n await appendFile(path, content, 'utf-8');\n }\n\n async writeAtomic(path: string, content: string, opts?: StorageWriteOptions): Promise<void> {\n const tmp = `${path}.tmp.${process.pid}.${Date.now()}`;\n if (opts?.mode !== undefined) {\n await writeFile(tmp, content, { encoding: 'utf-8', mode: opts.mode });\n } else {\n await writeFile(tmp, content, 'utf-8');\n }\n try {\n await rename(tmp, path);\n } catch (err) {\n // Best-effort cleanup of temp file on rename failure.\n try {\n await rm(tmp, { force: true });\n } catch {\n // ignore\n }\n throw err;\n }\n }\n\n async mkdir(dir: string): Promise<void> {\n await mkdir(dir, { recursive: true });\n }\n\n async remove(path: string, opts?: StorageRemoveOptions): Promise<void> {\n await rm(path, { recursive: opts?.recursive === true });\n }\n\n async rename(from: string, to: string): Promise<void> {\n await rename(from, to);\n }\n}\n","import { dirname } from 'node:path';\nimport type {\n Storage,\n StorageDirEntry,\n StorageRemoveOptions,\n StorageWriteOptions,\n} from '@ethosagent/types';\n\ninterface FileNode {\n type: 'file';\n content: string;\n mode?: number;\n mtimeMs: number;\n}\n\ninterface DirNode {\n type: 'dir';\n mtimeMs: number;\n}\n\ntype Node = FileNode | DirNode;\n\n/**\n * In-memory Storage implementation for tests. Mirrors fs semantics closely:\n *\n * - read/exists/mtime return null for missing paths.\n * - write requires the parent directory to exist (throws ENOENT otherwise) —\n * matches fs.writeFile and forces tests to mkdir first, just like prod.\n * - mkdir is always recursive; no-op on existing dirs; throws on file conflict.\n * - remove without recursive throws on missing paths and on non-empty dirs.\n * - writeAtomic round-trips through a temp key, identical to fs flow.\n *\n * mtime ticks forward on every write so cache-by-mtime patterns work in tests\n * without sleeping.\n */\nexport class InMemoryStorage implements Storage {\n // Absolute path → node\n private readonly nodes = new Map<string, Node>();\n private clock = 0;\n\n // Treat the filesystem root as always existing so consumers don't need to\n // mkdir('/') before writing to a file at the root.\n private isRootLike(path: string): boolean {\n return path === '/' || /^[A-Za-z]:[\\\\/]?$/.test(path);\n }\n\n private nextMtime(): number {\n this.clock += 1;\n return this.clock;\n }\n\n private getNode(path: string): Node | undefined {\n return this.nodes.get(path);\n }\n\n private requireParentDir(path: string): void {\n const parent = dirname(path);\n if (parent === path) return;\n if (this.isRootLike(parent)) return;\n const node = this.nodes.get(parent);\n if (!node) {\n const err = new Error(`ENOENT: no such file or directory, open '${path}'`);\n (err as NodeJS.ErrnoException).code = 'ENOENT';\n throw err;\n }\n if (node.type !== 'dir') {\n const err = new Error(`ENOTDIR: not a directory, open '${path}'`);\n (err as NodeJS.ErrnoException).code = 'ENOTDIR';\n throw err;\n }\n }\n\n async read(path: string): Promise<string | null> {\n const node = this.getNode(path);\n if (!node) return null;\n if (node.type === 'dir') {\n const err = new Error(`EISDIR: illegal operation on a directory, read '${path}'`);\n (err as NodeJS.ErrnoException).code = 'EISDIR';\n throw err;\n }\n return node.content;\n }\n\n async exists(path: string): Promise<boolean> {\n return this.nodes.has(path);\n }\n\n async mtime(path: string): Promise<number | null> {\n const node = this.getNode(path);\n return node ? node.mtimeMs : null;\n }\n\n async list(dir: string): Promise<string[]> {\n const node = this.getNode(dir);\n if (!node) return [];\n if (node.type !== 'dir') {\n const err = new Error(`ENOTDIR: not a directory, scandir '${dir}'`);\n (err as NodeJS.ErrnoException).code = 'ENOTDIR';\n throw err;\n }\n const prefix = dir.endsWith('/') ? dir : `${dir}/`;\n const names: string[] = [];\n for (const key of this.nodes.keys()) {\n if (key === dir) continue;\n if (!key.startsWith(prefix)) continue;\n const rest = key.slice(prefix.length);\n if (rest.includes('/')) continue;\n names.push(rest);\n }\n return names.sort();\n }\n\n async listEntries(dir: string): Promise<StorageDirEntry[]> {\n const names = await this.list(dir);\n const prefix = dir.endsWith('/') ? dir : `${dir}/`;\n return names.map((name) => {\n const node = this.nodes.get(prefix + name);\n return { name, isDir: node?.type === 'dir' };\n });\n }\n\n async write(path: string, content: string, opts?: StorageWriteOptions): Promise<void> {\n const existing = this.nodes.get(path);\n if (existing && existing.type === 'dir') {\n const err = new Error(`EISDIR: illegal operation on a directory, write '${path}'`);\n (err as NodeJS.ErrnoException).code = 'EISDIR';\n throw err;\n }\n this.requireParentDir(path);\n const node: FileNode = {\n type: 'file',\n content,\n mtimeMs: this.nextMtime(),\n };\n if (opts?.mode !== undefined) node.mode = opts.mode;\n this.nodes.set(path, node);\n }\n\n async writeAtomic(path: string, content: string, opts?: StorageWriteOptions): Promise<void> {\n // Same observable end-state as write — atomicity is a property of the\n // backing store; the in-memory map is single-step by definition.\n await this.write(path, content, opts);\n }\n\n async append(path: string, content: string): Promise<void> {\n const existing = this.nodes.get(path);\n if (existing && existing.type === 'dir') {\n const err = new Error(`EISDIR: illegal operation on a directory, append '${path}'`);\n (err as NodeJS.ErrnoException).code = 'EISDIR';\n throw err;\n }\n if (!existing) {\n // appendFile creates the file if missing — match that semantics.\n this.requireParentDir(path);\n this.nodes.set(path, {\n type: 'file',\n content,\n mtimeMs: this.nextMtime(),\n });\n return;\n }\n this.nodes.set(path, {\n ...existing,\n content: existing.content + content,\n mtimeMs: this.nextMtime(),\n });\n }\n\n async mkdir(dir: string): Promise<void> {\n if (this.isRootLike(dir)) return;\n const existing = this.nodes.get(dir);\n if (existing) {\n if (existing.type === 'dir') return;\n const err = new Error(`EEXIST: file already exists, mkdir '${dir}'`);\n (err as NodeJS.ErrnoException).code = 'EEXIST';\n throw err;\n }\n // Recursively create parents.\n const parent = dirname(dir);\n if (parent !== dir && !this.isRootLike(parent) && !this.nodes.has(parent)) {\n await this.mkdir(parent);\n }\n this.nodes.set(dir, { type: 'dir', mtimeMs: this.nextMtime() });\n }\n\n async remove(path: string, opts?: StorageRemoveOptions): Promise<void> {\n const node = this.nodes.get(path);\n if (!node) {\n const err = new Error(`ENOENT: no such file or directory, remove '${path}'`);\n (err as NodeJS.ErrnoException).code = 'ENOENT';\n throw err;\n }\n if (node.type === 'file') {\n this.nodes.delete(path);\n return;\n }\n // Directory.\n const prefix = path.endsWith('/') ? path : `${path}/`;\n const children: string[] = [];\n for (const key of this.nodes.keys()) {\n if (key !== path && key.startsWith(prefix)) children.push(key);\n }\n if (children.length > 0 && opts?.recursive !== true) {\n const err = new Error(`ENOTEMPTY: directory not empty, remove '${path}'`);\n (err as NodeJS.ErrnoException).code = 'ENOTEMPTY';\n throw err;\n }\n for (const key of children) this.nodes.delete(key);\n this.nodes.delete(path);\n }\n\n async rename(from: string, to: string): Promise<void> {\n const node = this.nodes.get(from);\n if (!node) {\n const err = new Error(`ENOENT: no such file or directory, rename '${from}' -> '${to}'`);\n (err as NodeJS.ErrnoException).code = 'ENOENT';\n throw err;\n }\n this.requireParentDir(to);\n\n if (node.type === 'file') {\n const target = this.nodes.get(to);\n if (target?.type === 'dir') {\n const err = new Error(`EISDIR: cannot rename file onto directory, '${from}' -> '${to}'`);\n (err as NodeJS.ErrnoException).code = 'EISDIR';\n throw err;\n }\n this.nodes.delete(from);\n this.nodes.set(to, { ...node, mtimeMs: this.nextMtime() });\n return;\n }\n\n // Directory rename — move the directory itself plus every descendant.\n const targetExisting = this.nodes.get(to);\n if (targetExisting) {\n const err = new Error(`EEXIST: target exists, rename '${from}' -> '${to}'`);\n (err as NodeJS.ErrnoException).code = 'EEXIST';\n throw err;\n }\n const fromPrefix = from.endsWith('/') ? from : `${from}/`;\n const moves: Array<[string, string]> = [[from, to]];\n for (const key of this.nodes.keys()) {\n if (key !== from && key.startsWith(fromPrefix)) {\n moves.push([key, to + key.slice(from.length)]);\n }\n }\n for (const [src, dst] of moves) {\n const n = this.nodes.get(src);\n if (!n) continue;\n this.nodes.delete(src);\n this.nodes.set(dst, { ...n, mtimeMs: this.nextMtime() });\n }\n }\n\n // --- Test helpers -----------------------------------------------------\n\n /** Drop all state. Useful for `beforeEach` resets without re-instantiating. */\n reset(): void {\n this.nodes.clear();\n this.clock = 0;\n }\n\n /** Return the recorded mode for a file (undefined if no mode was set). */\n getMode(path: string): number | undefined {\n const node = this.nodes.get(path);\n if (!node || node.type !== 'file') return undefined;\n return node.mode;\n }\n}\n","import {\n BoundaryError,\n type Storage,\n type StorageDirEntry,\n type StorageRemoveOptions,\n type StorageWriteOptions,\n} from '@ethosagent/types';\n\nexport interface ScopedStorageScope {\n /** Absolute path prefixes that may be read. */\n read: readonly string[];\n /** Absolute path prefixes that may be written / mutated. */\n write: readonly string[];\n}\n\n/**\n * Decorator over Storage that enforces a per-scope read/write allowlist.\n * Used by tools-file to bound a personality's filesystem reach to its own\n * directory + cwd (Phase 4 of the storage abstraction plan).\n *\n * A path is permitted if any allowed prefix is a prefix of the absolute path.\n * Prefixes are matched literally — there is no glob expansion. Pass paths\n * that end in `/` for directory scopes; ScopedStorage normalizes them so\n * `/a/b` does not also match `/a/bc/`.\n */\nexport class ScopedStorage implements Storage {\n private readonly readPrefixes: string[];\n private readonly writePrefixes: string[];\n\n constructor(\n private readonly inner: Storage,\n scope: ScopedStorageScope,\n ) {\n this.readPrefixes = scope.read.map(normalizePrefix);\n this.writePrefixes = scope.write.map(normalizePrefix);\n }\n\n private check(path: string, kind: 'read' | 'write'): void {\n const allowed = kind === 'read' ? this.readPrefixes : this.writePrefixes;\n if (!isPathAllowed(path, allowed)) {\n throw new BoundaryError(kind, path, allowed);\n }\n }\n\n async read(path: string): Promise<string | null> {\n this.check(path, 'read');\n return this.inner.read(path);\n }\n\n async exists(path: string): Promise<boolean> {\n this.check(path, 'read');\n return this.inner.exists(path);\n }\n\n async mtime(path: string): Promise<number | null> {\n this.check(path, 'read');\n return this.inner.mtime(path);\n }\n\n async list(dir: string): Promise<string[]> {\n this.check(dir, 'read');\n return this.inner.list(dir);\n }\n\n async listEntries(dir: string): Promise<StorageDirEntry[]> {\n this.check(dir, 'read');\n return this.inner.listEntries(dir);\n }\n\n async write(path: string, content: string, opts?: StorageWriteOptions): Promise<void> {\n this.check(path, 'write');\n return this.inner.write(path, content, opts);\n }\n\n async append(path: string, content: string): Promise<void> {\n this.check(path, 'write');\n return this.inner.append(path, content);\n }\n\n async writeAtomic(path: string, content: string, opts?: StorageWriteOptions): Promise<void> {\n this.check(path, 'write');\n return this.inner.writeAtomic(path, content, opts);\n }\n\n async mkdir(dir: string): Promise<void> {\n this.check(dir, 'write');\n return this.inner.mkdir(dir);\n }\n\n async remove(path: string, opts?: StorageRemoveOptions): Promise<void> {\n this.check(path, 'write');\n return this.inner.remove(path, opts);\n }\n\n async rename(from: string, to: string): Promise<void> {\n this.check(from, 'write');\n this.check(to, 'write');\n return this.inner.rename(from, to);\n }\n}\n\nfunction normalizePrefix(prefix: string): string {\n // A prefix matches any path where prefix is followed by '/' or end-of-string,\n // OR where the path equals the prefix exactly. We keep the prefix as-given\n // (with or without trailing slash) and handle the boundary in isPathAllowed.\n return prefix;\n}\n\nfunction isPathAllowed(path: string, prefixes: readonly string[]): boolean {\n for (const prefix of prefixes) {\n if (path === prefix) return true;\n const withoutSlash = prefix.endsWith('/') ? prefix.slice(0, -1) : prefix;\n // Allow the directory itself (without trailing slash) — needed so\n // `mkdir(<personality-dir>)` and `list(<personality-dir>)` succeed\n // when the configured prefix has a trailing slash.\n if (path === withoutSlash) return true;\n const withSlash = `${withoutSlash}/`;\n if (path.startsWith(withSlash)) return true;\n }\n return false;\n}\n","import type {\n SearchResult,\n Session,\n SessionFilter,\n SessionStore,\n SessionUsage,\n StoredMessage,\n} from '@ethosagent/types';\n\nexport class InMemorySessionStore implements SessionStore {\n private sessions = new Map<string, Session>();\n private messages = new Map<string, StoredMessage[]>();\n private idCounter = 0;\n\n async createSession(data: Omit<Session, 'id' | 'createdAt' | 'updatedAt'>): Promise<Session> {\n const session: Session = {\n ...data,\n id: `session_${++this.idCounter}`,\n createdAt: new Date(),\n updatedAt: new Date(),\n };\n this.sessions.set(session.id, session);\n this.messages.set(session.id, []);\n return session;\n }\n\n async getSession(id: string): Promise<Session | null> {\n return this.sessions.get(id) ?? null;\n }\n\n async getSessionByKey(key: string): Promise<Session | null> {\n for (const s of this.sessions.values()) {\n if (s.key === key) return s;\n }\n return null;\n }\n\n async updateSession(id: string, patch: Partial<Session>): Promise<void> {\n const session = this.sessions.get(id);\n if (!session) throw new Error(`Session not found: ${id}`);\n this.sessions.set(id, { ...session, ...patch, updatedAt: new Date() });\n }\n\n async deleteSession(id: string): Promise<void> {\n this.sessions.delete(id);\n this.messages.delete(id);\n }\n\n async listSessions(filter?: SessionFilter): Promise<Session[]> {\n let results = [...this.sessions.values()];\n if (filter?.platform) results = results.filter((s) => s.platform === filter.platform);\n if (filter?.personalityId)\n results = results.filter((s) => s.personalityId === filter.personalityId);\n if (filter?.workingDir) results = results.filter((s) => s.workingDir === filter.workingDir);\n if (filter?.since) {\n const since = filter.since;\n results = results.filter((s) => s.createdAt >= since);\n }\n results.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());\n const offset = filter?.offset ?? 0;\n const limit = filter?.limit ?? results.length;\n return results.slice(offset, offset + limit);\n }\n\n async appendMessage(data: Omit<StoredMessage, 'id' | 'timestamp'>): Promise<StoredMessage> {\n const message: StoredMessage = {\n ...data,\n id: `msg_${++this.idCounter}`,\n timestamp: new Date(),\n };\n const list = this.messages.get(data.sessionId) ?? [];\n list.push(message);\n this.messages.set(data.sessionId, list);\n return message;\n }\n\n async getMessages(\n sessionId: string,\n options?: { limit?: number; offset?: number },\n ): Promise<StoredMessage[]> {\n const all = this.messages.get(sessionId) ?? [];\n const offset = options?.offset ?? 0;\n // Return most-recent messages: trim from the tail, then skip `offset` from the end\n const end = all.length - offset;\n const start = options?.limit ? Math.max(0, end - options.limit) : 0;\n return all.slice(start, end);\n }\n\n async updateUsage(sessionId: string, delta: Partial<SessionUsage>): Promise<void> {\n const session = this.sessions.get(sessionId);\n if (!session) return;\n const usage = { ...session.usage };\n for (const [k, v] of Object.entries(delta) as [keyof SessionUsage, number][]) {\n (usage[k] as number) += v;\n }\n this.sessions.set(sessionId, { ...session, usage, updatedAt: new Date() });\n }\n\n async search(\n query: string,\n options?: { limit?: number; sessionId?: string },\n ): Promise<SearchResult[]> {\n const results: SearchResult[] = [];\n const lower = query.toLowerCase();\n for (const [sessionId, msgs] of this.messages.entries()) {\n if (options?.sessionId && sessionId !== options.sessionId) continue;\n for (const msg of msgs) {\n const idx = msg.content.toLowerCase().indexOf(lower);\n if (idx >= 0) {\n results.push({\n sessionId,\n messageId: msg.id,\n snippet: msg.content.slice(Math.max(0, idx - 50), idx + 150),\n score: 1,\n timestamp: msg.timestamp,\n });\n }\n }\n }\n results.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());\n return results.slice(0, options?.limit ?? 20);\n }\n\n async pruneOldSessions(olderThan: Date): Promise<number> {\n let count = 0;\n for (const [id, session] of this.sessions.entries()) {\n if (session.updatedAt < olderThan) {\n this.sessions.delete(id);\n this.messages.delete(id);\n count++;\n }\n }\n return count;\n }\n\n async vacuum(): Promise<void> {\n // No-op for in-memory store\n }\n}\n","import type { MemoryLoadContext, MemoryProvider, MemoryUpdate } from '@ethosagent/types';\n\nexport class NoopMemoryProvider implements MemoryProvider {\n async prefetch(_ctx: MemoryLoadContext) {\n return null;\n }\n\n async sync(_ctx: MemoryLoadContext, _updates: MemoryUpdate[]): Promise<void> {\n // No-op\n }\n}\n","import type { PersonalityConfig, PersonalityRegistry } from '@ethosagent/types';\n\nconst DEFAULT_PERSONALITY: PersonalityConfig = {\n id: 'default',\n name: 'Default',\n description: 'Default Ethos personality',\n};\n\nexport class DefaultPersonalityRegistry implements PersonalityRegistry {\n private readonly personalities = new Map<string, PersonalityConfig>([\n ['default', DEFAULT_PERSONALITY],\n ]);\n private defaultId = 'default';\n\n define(config: PersonalityConfig): void {\n this.personalities.set(config.id, config);\n }\n\n get(id: string): PersonalityConfig | undefined {\n return this.personalities.get(id);\n }\n\n list(): PersonalityConfig[] {\n return [...this.personalities.values()];\n }\n\n getDefault(): PersonalityConfig {\n return this.personalities.get(this.defaultId) ?? DEFAULT_PERSONALITY;\n }\n\n setDefault(id: string): void {\n if (!this.personalities.has(id)) {\n throw new Error(`Unknown personality: ${id}`);\n }\n this.defaultId = id;\n }\n\n async loadFromDirectory(_dir: string): Promise<void> {\n // Implemented in extensions/personalities\n }\n\n remove(id: string): void {\n this.personalities.delete(id);\n }\n}\n","import type { ClaimingHooks, HookRegistry, ModifyingHooks, VoidHooks } from '@ethosagent/types';\n\ntype AnyHandler = (...args: unknown[]) => Promise<unknown>;\n\ninterface RegisteredHandler {\n handler: AnyHandler;\n pluginId?: string;\n failurePolicy: 'fail-open' | 'fail-closed';\n}\n\n/** Returns true when a handler should fire given the allowedPlugins filter. */\nfunction isAllowed(h: RegisteredHandler, allowedPlugins: string[] | undefined): boolean {\n if (allowedPlugins === undefined) return true; // no filter — fire all\n if (!h.pluginId) return true; // built-in handler — always fires\n return allowedPlugins.includes(h.pluginId);\n}\n\nexport class DefaultHookRegistry implements HookRegistry {\n private readonly voidHandlers = new Map<string, RegisteredHandler[]>();\n private readonly modifyingHandlers = new Map<string, RegisteredHandler[]>();\n private readonly claimingHandlers = new Map<string, RegisteredHandler[]>();\n\n registerVoid<K extends keyof VoidHooks>(\n name: K,\n handler: (payload: VoidHooks[K]) => Promise<void>,\n opts?: { pluginId?: string; failurePolicy?: 'fail-open' | 'fail-closed' },\n ): () => void {\n const entry: RegisteredHandler = {\n handler: handler as AnyHandler,\n pluginId: opts?.pluginId,\n failurePolicy: opts?.failurePolicy ?? 'fail-open',\n };\n const list = this.voidHandlers.get(name) ?? [];\n list.push(entry);\n this.voidHandlers.set(name, list);\n return () => this.remove(this.voidHandlers, name, entry);\n }\n\n registerModifying<K extends keyof ModifyingHooks>(\n name: K,\n handler: (payload: ModifyingHooks[K][0]) => Promise<Partial<ModifyingHooks[K][1]> | null>,\n opts?: { pluginId?: string },\n ): () => void {\n const entry: RegisteredHandler = {\n handler: handler as AnyHandler,\n pluginId: opts?.pluginId,\n failurePolicy: 'fail-open',\n };\n const list = this.modifyingHandlers.get(name) ?? [];\n list.push(entry);\n this.modifyingHandlers.set(name, list);\n return () => this.remove(this.modifyingHandlers, name, entry);\n }\n\n registerClaiming<K extends keyof ClaimingHooks>(\n name: K,\n handler: (payload: ClaimingHooks[K][0]) => Promise<ClaimingHooks[K][1]>,\n opts?: { pluginId?: string },\n ): () => void {\n const entry: RegisteredHandler = {\n handler: handler as AnyHandler,\n pluginId: opts?.pluginId,\n failurePolicy: 'fail-open',\n };\n const list = this.claimingHandlers.get(name) ?? [];\n list.push(entry);\n this.claimingHandlers.set(name, list);\n return () => this.remove(this.claimingHandlers, name, entry);\n }\n\n // Void hooks: all handlers run in parallel via Promise.allSettled.\n // Failures are logged but never propagate (fail-open by default).\n // allowedPlugins gates plugin-registered handlers; built-in handlers always fire.\n async fireVoid<K extends keyof VoidHooks>(\n name: K,\n payload: VoidHooks[K],\n allowedPlugins?: string[],\n ): Promise<void> {\n const handlers = (this.voidHandlers.get(name) ?? []).filter((h) =>\n isAllowed(h, allowedPlugins),\n );\n await Promise.allSettled(handlers.map((h) => h.handler(payload)));\n }\n\n // Modifying hooks: handlers run sequentially; results are merged (first non-null value per key wins).\n async fireModifying<K extends keyof ModifyingHooks>(\n name: K,\n payload: ModifyingHooks[K][0],\n allowedPlugins?: string[],\n ): Promise<ModifyingHooks[K][1]> {\n const handlers = (this.modifyingHandlers.get(name) ?? []).filter((h) =>\n isAllowed(h, allowedPlugins),\n );\n const merged: Record<string, unknown> = {};\n for (const h of handlers) {\n try {\n const result = await h.handler(payload);\n if (result && typeof result === 'object') {\n for (const [k, v] of Object.entries(result)) {\n if (!(k in merged) && v !== null && v !== undefined) {\n merged[k] = v;\n }\n }\n }\n } catch {\n // fail-open: continue with other handlers\n }\n }\n return merged as ModifyingHooks[K][1];\n }\n\n // Claiming hooks: handlers run sequentially, stop after first { handled: true }.\n async fireClaiming<K extends keyof ClaimingHooks>(\n name: K,\n payload: ClaimingHooks[K][0],\n allowedPlugins?: string[],\n ): Promise<ClaimingHooks[K][1]> {\n const handlers = (this.claimingHandlers.get(name) ?? []).filter((h) =>\n isAllowed(h, allowedPlugins),\n );\n for (const h of handlers) {\n try {\n const result = (await h.handler(payload)) as ClaimingHooks[K][1];\n if (result && (result as { handled: boolean }).handled) {\n return result;\n }\n } catch {\n // fail-open: try next handler\n }\n }\n return { handled: false } as ClaimingHooks[K][1];\n }\n\n unregisterPlugin(pluginId: string): void {\n for (const map of [this.voidHandlers, this.modifyingHandlers, this.claimingHandlers]) {\n for (const [name, handlers] of map.entries()) {\n map.set(\n name,\n handlers.filter((h) => h.pluginId !== pluginId),\n );\n }\n }\n }\n\n private remove(\n map: Map<string, RegisteredHandler[]>,\n name: string,\n entry: RegisteredHandler,\n ): void {\n const list = map.get(name) ?? [];\n map.set(\n name,\n list.filter((h) => h !== entry),\n );\n }\n}\n","import type {\n PersonalityConfig,\n Tool,\n ToolContext,\n ToolFilterOpts,\n ToolRegistry,\n ToolResult,\n} from '@ethosagent/types';\n\ninterface ToolEntry {\n tool: Tool;\n pluginId?: string;\n}\n\n/** Extract MCP server name from `mcp__<server>__<tool>` naming convention. */\nfunction mcpServerName(toolName: string): string | undefined {\n if (!toolName.startsWith('mcp__')) return undefined;\n return toolName.split('__')[1];\n}\n\n/** Returns true when a tool passes the MCP server + plugin filters. */\nfunction passesFilter(entry: ToolEntry, filterOpts: ToolFilterOpts | undefined): boolean {\n if (!filterOpts) return true;\n\n const { allowedMcpServers, allowedPlugins } = filterOpts;\n\n // MCP server gate: MCP tools only appear when their server is in the allowlist.\n if (allowedMcpServers !== undefined) {\n const server = mcpServerName(entry.tool.name);\n if (server !== undefined && !allowedMcpServers.includes(server)) return false;\n }\n\n // Plugin gate: plugin tools only appear when their plugin is in the allowlist.\n if (allowedPlugins !== undefined && entry.pluginId !== undefined) {\n if (!allowedPlugins.includes(entry.pluginId)) return false;\n }\n\n return true;\n}\n\nexport class DefaultToolRegistry implements ToolRegistry {\n private readonly tools = new Map<string, ToolEntry>();\n\n register(tool: Tool, opts?: { pluginId?: string }): void {\n this.tools.set(tool.name, { tool, pluginId: opts?.pluginId });\n }\n\n registerAll(tools: Tool[]): void {\n for (const tool of tools) {\n this.register(tool);\n }\n }\n\n unregister(name: string): void {\n this.tools.delete(name);\n }\n\n get(name: string): Tool | undefined {\n return this.tools.get(name)?.tool;\n }\n\n getAvailable(): Tool[] {\n return [...this.tools.values()]\n .filter((e) => !e.tool.isAvailable || e.tool.isAvailable())\n .map((e) => e.tool);\n }\n\n getForToolset(toolset: string): Tool[] {\n return this.getAvailable().filter((t) => t.toolset === toolset);\n }\n\n toDefinitions(allowedTools?: string[], filterOpts?: ToolFilterOpts) {\n const entries = [...this.tools.values()].filter(\n (e) => !e.tool.isAvailable || e.tool.isAvailable(),\n );\n\n const filtered = entries.filter((e) => {\n // Toolset (allowedTools) gates BUILT-IN tools by exact name match. MCP and\n // plugin tools are gated separately via passesFilter() — their names are\n // dynamic, so requiring users to enumerate them in toolset.yaml is\n // unworkable. (mcp_servers / plugins allowlists are the gates for those.)\n const isMcpOrPluginTool = e.tool.name.startsWith('mcp__') || e.pluginId !== undefined;\n if (\n !isMcpOrPluginTool &&\n !e.tool.alwaysInclude &&\n allowedTools &&\n allowedTools.length > 0 &&\n !allowedTools.includes(e.tool.name)\n )\n return false;\n return passesFilter(e, filterOpts);\n });\n\n return filtered.map((e) => ({\n name: e.tool.name,\n description: e.tool.description,\n parameters: e.tool.schema,\n }));\n }\n\n /**\n * Computes the effective tool reach for a personality:\n * personality.toolset (built-in tools)\n * ∪ tools from MCP servers in personality.mcp_servers\n * ∪ tools from plugins in personality.plugins\n *\n * Used by IngestFilter to check skill.required_tools ⊆ effective_reach.\n */\n toolNamesForPersonality(personality: PersonalityConfig): Set<string> {\n const reach = new Set<string>();\n\n for (const [name, entry] of this.tools) {\n const isMcp = name.startsWith('mcp__');\n const isPlugin = entry.pluginId !== undefined;\n\n if (!isMcp && !isPlugin) {\n // Built-in tool — include if in personality.toolset (or if toolset is unrestricted)\n const toolset = personality.toolset;\n if (!toolset || toolset.length === 0 || toolset.includes(name)) {\n reach.add(name);\n }\n } else if (isMcp) {\n const server = mcpServerName(name);\n const allowed = personality.mcp_servers;\n if (server && allowed?.includes(server)) {\n reach.add(name);\n }\n } else if (isPlugin) {\n const allowed = personality.plugins;\n if (entry.pluginId && allowed?.includes(entry.pluginId)) {\n reach.add(name);\n }\n }\n }\n\n return reach;\n }\n\n // Runs all tool calls in parallel. Results are returned in input order.\n // Budget is split evenly across parallel calls; each result is post-trimmed to budget.\n // allowedTools + filterOpts enforce tool access at execution time (belt-and-suspenders).\n async executeParallel(\n calls: Array<{ toolCallId: string; name: string; args: unknown }>,\n ctx: ToolContext,\n allowedTools?: string[],\n filterOpts?: ToolFilterOpts,\n ): Promise<Array<{ toolCallId: string; name: string; result: ToolResult }>> {\n const perCallBudget = Math.floor(ctx.resultBudgetChars / Math.max(calls.length, 1));\n\n const results = await Promise.allSettled(\n calls.map(async (call) => {\n const entry = this.tools.get(call.name);\n if (!entry) {\n return {\n toolCallId: call.toolCallId,\n name: call.name,\n result: {\n ok: false,\n error: `Unknown tool: ${call.name}`,\n code: 'not_available',\n } as ToolResult,\n };\n }\n\n // Toolset (allowedTools) only gates built-in tools — see toDefinitions\n // for the rationale. MCP and plugin tools are gated by passesFilter().\n const isMcpOrPluginTool = call.name.startsWith('mcp__') || entry.pluginId !== undefined;\n if (\n !isMcpOrPluginTool &&\n allowedTools &&\n allowedTools.length > 0 &&\n !allowedTools.includes(call.name)\n ) {\n return {\n toolCallId: call.toolCallId,\n name: call.name,\n result: {\n ok: false,\n error: `Tool ${call.name} is not permitted for this personality`,\n code: 'not_available',\n } as ToolResult,\n };\n }\n\n // MCP server + plugin filter check\n if (!passesFilter(entry, filterOpts)) {\n return {\n toolCallId: call.toolCallId,\n name: call.name,\n result: {\n ok: false,\n error: `Tool ${call.name} is not permitted for this personality`,\n code: 'not_available',\n } as ToolResult,\n };\n }\n\n if (entry.tool.isAvailable && !entry.tool.isAvailable()) {\n return {\n toolCallId: call.toolCallId,\n name: call.name,\n result: {\n ok: false,\n error: `Tool ${call.name} is not currently available`,\n code: 'not_available',\n } as ToolResult,\n };\n }\n\n const budget = Math.min(perCallBudget, entry.tool.maxResultChars ?? perCallBudget);\n const toolCtx: ToolContext = { ...ctx, resultBudgetChars: budget };\n\n try {\n const result = await entry.tool.execute(call.args, toolCtx);\n // Post-trim result to budget\n if (result.ok && result.value.length > budget) {\n return {\n toolCallId: call.toolCallId,\n name: call.name,\n result: {\n ok: true,\n value: `${result.value.slice(0, budget)}\\n[truncated — ${result.value.length} chars total]`,\n } as ToolResult,\n };\n }\n return { toolCallId: call.toolCallId, name: call.name, result };\n } catch (err) {\n return {\n toolCallId: call.toolCallId,\n name: call.name,\n result: {\n ok: false,\n error: err instanceof Error ? err.message : String(err),\n code: 'execution_failed',\n } as ToolResult,\n };\n }\n }),\n );\n\n // Unwrap settled results — always return, never throw\n return results.map((r, i) => {\n if (r.status === 'fulfilled') return r.value;\n const call = calls[i] ?? { toolCallId: 'unknown', name: 'unknown', args: {} };\n return {\n toolCallId: call.toolCallId,\n name: call.name,\n result: {\n ok: false,\n error: String(r.reason),\n code: 'execution_failed',\n } as ToolResult,\n };\n });\n }\n}\n","// Generic plugin registry — adapted from praxis PluginRegistry pattern.\n// Each subsystem (tools, channels, memory backends) gets its own instance.\n\nexport type PluginFactory<T, C = unknown> = (config: C) => T | null;\n\nexport class PluginRegistry<T, C = unknown> {\n private readonly factories = new Map<string, PluginFactory<T, C>>();\n\n register(type: string, factory: PluginFactory<T, C>): void {\n this.factories.set(type, factory);\n }\n\n create(type: string, config: C): T {\n const factory = this.factories.get(type);\n if (!factory) {\n throw new Error(\n `Unknown plugin type: \"${type}\". Registered: ${[...this.factories.keys()].join(', ')}`,\n );\n }\n const instance = factory(config);\n if (!instance) {\n throw new Error(`Plugin factory for \"${type}\" returned null`);\n }\n return instance;\n }\n\n has(type: string): boolean {\n return this.factories.has(type);\n }\n\n types(): string[] {\n return [...this.factories.keys()];\n }\n}\n","import type {\n CompletionChunk,\n CompletionOptions,\n FailoverReason,\n LLMProvider,\n Message,\n ToolDefinitionLite,\n} from '@ethosagent/types';\n\n// ---------------------------------------------------------------------------\n// Error classification\n// ---------------------------------------------------------------------------\n\nfunction classifyProviderError(err: unknown): FailoverReason {\n const msg = (err instanceof Error ? err.message : String(err)).toLowerCase();\n if (\n msg.includes('401') ||\n msg.includes('403') ||\n msg.includes('authentication') ||\n msg.includes('api key') ||\n msg.includes('unauthorized')\n )\n return 'auth';\n if (\n msg.includes('429') ||\n msg.includes('rate limit') ||\n msg.includes('rate_limit') ||\n msg.includes('too many requests')\n )\n return 'rate_limit';\n if (\n msg.includes('529') ||\n msg.includes('overloaded') ||\n msg.includes('503') ||\n msg.includes('service unavailable')\n )\n return 'overloaded';\n if (\n msg.includes('context') &&\n (msg.includes('overflow') || msg.includes('too long') || msg.includes('too large'))\n )\n return 'context_overflow';\n if (msg.includes('content') && (msg.includes('filter') || msg.includes('policy')))\n return 'content_filter';\n if (\n msg.includes('404') ||\n msg.includes('model not found') ||\n msg.includes('model_not_found') ||\n msg.includes('no such model')\n )\n return 'model_not_found';\n if (msg.includes('timeout') || msg.includes('timed out') || msg.includes('etimedout'))\n return 'timeout';\n if (\n msg.includes('network') ||\n msg.includes('econnrefused') ||\n msg.includes('enotfound') ||\n msg.includes('socket hang up')\n )\n return 'network';\n return 'unknown';\n}\n\n// Reasons that warrant trying the next provider in the chain.\nconst FAILOVER_REASONS = new Set<FailoverReason>([\n 'rate_limit',\n 'overloaded',\n 'timeout',\n 'network',\n 'unknown',\n 'model_not_found',\n]);\n\nfunction shouldFailover(reason: FailoverReason): boolean {\n return FAILOVER_REASONS.has(reason);\n}\n\n// ---------------------------------------------------------------------------\n// ChainedProvider\n// ---------------------------------------------------------------------------\n\ninterface ProviderEntry {\n provider: LLMProvider;\n cooldownUntil: number;\n}\n\nexport interface ChainedProviderOptions {\n /** Milliseconds to cool a provider down after a failover-eligible error. Default: 60000. */\n cooldownMs?: number;\n}\n\n/**\n * Wraps multiple LLMProviders with automatic failover.\n *\n * On a failover-eligible error (rate_limit, overloaded, timeout, network, unknown),\n * the failing provider is put on cooldown and the next provider is tried.\n * Non-retriable errors (auth, content_filter, context_overflow) propagate immediately.\n *\n * Once streaming starts (first CompletionChunk received), the stream is committed —\n * no mid-stream retries. Failover only happens on errors thrown before the first chunk.\n *\n * Error codes:\n * ALL_PROVIDERS_FAILED — every provider failed with a failover-eligible error\n * ALL_PROVIDERS_REJECT_MODEL — every provider failed with model_not_found\n */\nexport class ChainedProvider implements LLMProvider {\n private readonly entries: ProviderEntry[];\n private readonly cooldownMs: number;\n\n constructor(providers: LLMProvider[], opts: ChainedProviderOptions = {}) {\n if (providers.length === 0) throw new Error('ChainedProvider requires at least one provider');\n this.entries = providers.map((p) => ({ provider: p, cooldownUntil: 0 }));\n this.cooldownMs = opts.cooldownMs ?? 60_000;\n }\n\n get name(): string {\n return `chain(${this.entries.map((e) => e.provider.name).join(',')})`;\n }\n\n get model(): string {\n return this.activeEntry()?.provider.model ?? this.entries[0]?.provider.model ?? '';\n }\n\n get maxContextTokens(): number {\n return this.activeEntry()?.provider.maxContextTokens ?? 200_000;\n }\n\n get supportsCaching(): boolean {\n return this.activeEntry()?.provider.supportsCaching ?? false;\n }\n\n get supportsThinking(): boolean {\n return this.activeEntry()?.provider.supportsThinking ?? false;\n }\n\n async *complete(\n messages: Message[],\n tools: ToolDefinitionLite[],\n options: CompletionOptions,\n ): AsyncIterable<CompletionChunk> {\n const now = Date.now();\n const available = this.entries.filter((e) => e.cooldownUntil <= now);\n\n if (available.length === 0) {\n // All on cooldown — wait for the soonest one to recover then use it.\n const soonest = this.entries.reduce((a, b) => (a.cooldownUntil < b.cooldownUntil ? a : b));\n available.push(soonest);\n }\n\n const reasons: FailoverReason[] = [];\n\n for (const entry of available) {\n try {\n const stream = entry.provider.complete(messages, tools, options);\n for await (const chunk of stream) {\n yield chunk;\n }\n return;\n } catch (err) {\n const reason = classifyProviderError(err);\n reasons.push(reason);\n\n if (!shouldFailover(reason)) {\n throw err;\n }\n\n entry.cooldownUntil = Date.now() + this.cooldownMs;\n }\n }\n\n // All available providers failed.\n const allModelNotFound = reasons.length > 0 && reasons.every((r) => r === 'model_not_found');\n if (allModelNotFound) {\n throw new Error(\n `ALL_PROVIDERS_REJECT_MODEL: no provider in the chain supports the requested model. ` +\n `Tried: ${available.map((e) => `${e.provider.name}/${e.provider.model}`).join(', ')}`,\n );\n }\n throw new Error(\n `ALL_PROVIDERS_FAILED: all providers in the chain have been exhausted. ` +\n `Tried: ${available.map((e, i) => `${e.provider.name}/${e.provider.model} (${reasons[i] ?? 'unknown'})`).join(', ')}`,\n );\n }\n\n async countTokens(messages: Message[]): Promise<number> {\n const entry = this.activeEntry();\n if (!entry) return 0;\n return entry.provider.countTokens(messages);\n }\n\n // Returns the first non-cooled provider, or undefined if all are cooled.\n private activeEntry(): ProviderEntry | undefined {\n const now = Date.now();\n return this.entries.find((e) => e.cooldownUntil <= now);\n }\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ethosagent/core",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.6",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Core agent loop, tool registry, and hook registry for the Ethos agent framework",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
}
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@ethosagent/types": "0.2.
|
|
22
|
+
"@ethosagent/types": "0.2.6"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@ethosagent/storage-fs": "0.0.0"
|