@ethosagent/core 0.1.0 → 0.2.0

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 CHANGED
@@ -1,4 +1,4 @@
1
- import { LLMProvider, ToolRegistry, PersonalityRegistry, MemoryProvider, SessionStore, HookRegistry, ContextInjector, Session, SessionFilter, StoredMessage, SessionUsage, SearchResult, MemoryLoadContext, MemoryUpdate, PersonalityConfig, VoidHooks, ModifyingHooks, ClaimingHooks, Tool, ToolContext, ToolResult } from '@ethosagent/types';
1
+ import { HookRegistry, LLMProvider, ToolRegistry, PersonalityRegistry, MemoryProvider, SessionStore, ContextInjector, Storage, Session, SessionFilter, StoredMessage, SessionUsage, SearchResult, MemoryLoadContext, MemoryUpdate, PersonalityConfig, VoidHooks, ModifyingHooks, ClaimingHooks, Tool, ToolFilterOpts, ToolContext, ToolResult } from '@ethosagent/types';
2
2
 
3
3
  type AgentEvent = {
4
4
  type: 'text_delta';
@@ -16,12 +16,22 @@ type AgentEvent = {
16
16
  toolName: string;
17
17
  message: string;
18
18
  percent?: number;
19
+ audience: 'internal' | 'user';
19
20
  } | {
20
21
  type: 'tool_end';
21
22
  toolCallId: string;
22
23
  toolName: string;
23
24
  ok: boolean;
24
25
  durationMs: number;
26
+ audience?: 'internal' | 'user';
27
+ /**
28
+ * Tool output body — the success value when `ok`, or the error
29
+ * message when `ok: false`. Optional so consumers that only care
30
+ * about the status (CLI ASCII chips, telemetry) can ignore it.
31
+ * The web chip surfaces this on expand-on-click without a
32
+ * follow-up history fetch.
33
+ */
34
+ result?: string;
25
35
  } | {
26
36
  type: 'usage';
27
37
  inputTokens: number;
@@ -35,6 +45,9 @@ type AgentEvent = {
35
45
  type: 'done';
36
46
  text: string;
37
47
  turnCount: number;
48
+ } | {
49
+ type: 'context_meta';
50
+ data: Record<string, unknown>;
38
51
  };
39
52
  interface AgentLoopConfig {
40
53
  llm: LLMProvider;
@@ -44,18 +57,64 @@ interface AgentLoopConfig {
44
57
  session?: SessionStore;
45
58
  hooks?: HookRegistry;
46
59
  injectors?: ContextInjector[];
60
+ /**
61
+ * Maps each plugin-registered injector to its plugin id so AgentLoop can
62
+ * gate injectors by personality. Built-in injectors are absent (always fire).
63
+ * Populated by PluginApiImpl.registerInjector(); passed through from wiring.
64
+ */
65
+ injectorPluginIds?: Map<ContextInjector, string>;
66
+ /**
67
+ * Base Storage instance handed to tools via `ToolContext.storage` after
68
+ * being decorated with a ScopedStorage that enforces the active
69
+ * personality's `fs_reach` allowlist. When unset, ToolContext.storage is
70
+ * left undefined and tools fall back to unrestricted node:fs (legacy
71
+ * behavior — existing CLI/TUI tests don't need a storage instance).
72
+ */
73
+ storage?: Storage;
74
+ /**
75
+ * Absolute path to ~/.ethos/ used for `${ETHOS_HOME}` substitution in
76
+ * `fs_reach` paths. Defaults to `${HOME}/.ethos`. Required only when
77
+ * `storage` is set.
78
+ */
79
+ dataDir?: string;
80
+ modelRouting?: Record<string, string>;
47
81
  options?: {
48
82
  maxIterations?: number;
49
83
  historyLimit?: number;
50
84
  platform?: string;
51
85
  workingDir?: string;
52
86
  resultBudgetChars?: number;
87
+ /**
88
+ * Hard cap on total tool calls per user turn (across all LLM iterations).
89
+ * Defaults to 20. Trips a `tool_progress` warning and exits cleanly.
90
+ * See plan/IMPROVEMENT.md P1-3.
91
+ */
92
+ maxToolCallsPerTurn?: number;
93
+ /**
94
+ * Hard cap on the number of times the same tool name can be invoked in a
95
+ * single turn. Catches the "infinite loop on a single tool" failure mode
96
+ * (e.g. tts loop reported as OpenClaw #67744). Defaults to 5.
97
+ */
98
+ maxIdenticalToolCalls?: number;
99
+ /**
100
+ * Default streaming watchdog in milliseconds. If no chunk arrives from the
101
+ * LLM within this window, the agent aborts the stream and emits an error.
102
+ * Reset on every chunk. Personalities can override via
103
+ * `personality.streamingTimeoutMs`. Defaults to 120000 (2 minutes).
104
+ */
105
+ streamingTimeoutMs?: number;
53
106
  };
54
107
  }
55
108
  interface RunOptions {
56
109
  sessionKey?: string;
57
110
  personalityId?: string;
58
111
  abortSignal?: AbortSignal;
112
+ /**
113
+ * Identifier surfaced to tools as `ToolContext.agentId`. Delegation tools
114
+ * use this to thread spawn depth (`depth:N`) into child loops so
115
+ * `MAX_SPAWN_DEPTH` can be enforced across recursive sub-agent calls.
116
+ */
117
+ agentId?: string;
59
118
  }
60
119
  declare class AgentLoop {
61
120
  private readonly llm;
@@ -63,17 +122,29 @@ declare class AgentLoop {
63
122
  private readonly personalities;
64
123
  private readonly memory;
65
124
  private readonly session;
66
- private readonly hooks;
125
+ /** Public so surfaces (web, ACP) can register late-binding hooks they own
126
+ * without re-running the whole wiring factory. The CLI/TUI register hooks
127
+ * before construction; web registers an approval hook after createAgentLoop
128
+ * returns. */
129
+ readonly hooks: HookRegistry;
67
130
  private readonly injectors;
131
+ private readonly injectorPluginIds;
68
132
  private readonly maxIterations;
69
133
  private readonly historyLimit;
70
134
  private readonly platform;
71
135
  private readonly workingDir;
72
136
  private readonly resultBudgetChars;
137
+ private readonly maxToolCallsPerTurn;
138
+ private readonly maxIdenticalToolCalls;
139
+ private readonly streamingTimeoutMs;
140
+ private readonly modelRouting;
141
+ private readonly storage?;
142
+ private readonly dataDir?;
73
143
  constructor(config: AgentLoopConfig);
74
144
  run(text: string, opts?: RunOptions): AsyncGenerator<AgentEvent>;
75
145
  private handleChunk;
76
146
  private toLLMMessages;
147
+ private buildScopedStorage;
77
148
  }
78
149
 
79
150
  declare class InMemorySessionStore implements SessionStore {
@@ -114,6 +185,7 @@ declare class DefaultPersonalityRegistry implements PersonalityRegistry {
114
185
  getDefault(): PersonalityConfig;
115
186
  setDefault(id: string): void;
116
187
  loadFromDirectory(_dir: string): Promise<void>;
188
+ remove(id: string): void;
117
189
  }
118
190
 
119
191
  declare class DefaultHookRegistry implements HookRegistry {
@@ -130,9 +202,9 @@ declare class DefaultHookRegistry implements HookRegistry {
130
202
  registerClaiming<K extends keyof ClaimingHooks>(name: K, handler: (payload: ClaimingHooks[K][0]) => Promise<ClaimingHooks[K][1]>, opts?: {
131
203
  pluginId?: string;
132
204
  }): () => void;
133
- fireVoid<K extends keyof VoidHooks>(name: K, payload: VoidHooks[K]): Promise<void>;
134
- fireModifying<K extends keyof ModifyingHooks>(name: K, payload: ModifyingHooks[K][0]): Promise<ModifyingHooks[K][1]>;
135
- fireClaiming<K extends keyof ClaimingHooks>(name: K, payload: ClaimingHooks[K][0]): Promise<ClaimingHooks[K][1]>;
205
+ fireVoid<K extends keyof VoidHooks>(name: K, payload: VoidHooks[K], allowedPlugins?: string[]): Promise<void>;
206
+ fireModifying<K extends keyof ModifyingHooks>(name: K, payload: ModifyingHooks[K][0], allowedPlugins?: string[]): Promise<ModifyingHooks[K][1]>;
207
+ fireClaiming<K extends keyof ClaimingHooks>(name: K, payload: ClaimingHooks[K][0], allowedPlugins?: string[]): Promise<ClaimingHooks[K][1]>;
136
208
  unregisterPlugin(pluginId: string): void;
137
209
  private remove;
138
210
  }
@@ -148,13 +220,15 @@ declare class PluginRegistry<T, C = unknown> {
148
220
 
149
221
  declare class DefaultToolRegistry implements ToolRegistry {
150
222
  private readonly tools;
151
- register(tool: Tool): void;
223
+ register(tool: Tool, opts?: {
224
+ pluginId?: string;
225
+ }): void;
152
226
  registerAll(tools: Tool[]): void;
153
227
  unregister(name: string): void;
154
228
  get(name: string): Tool | undefined;
155
229
  getAvailable(): Tool[];
156
230
  getForToolset(toolset: string): Tool[];
157
- toDefinitions(): {
231
+ toDefinitions(allowedTools?: string[], filterOpts?: ToolFilterOpts): {
158
232
  name: string;
159
233
  description: string;
160
234
  parameters: Record<string, unknown>;
@@ -163,7 +237,7 @@ declare class DefaultToolRegistry implements ToolRegistry {
163
237
  toolCallId: string;
164
238
  name: string;
165
239
  args: unknown;
166
- }>, ctx: ToolContext): Promise<Array<{
240
+ }>, ctx: ToolContext, allowedTools?: string[], filterOpts?: ToolFilterOpts): Promise<Array<{
167
241
  toolCallId: string;
168
242
  name: string;
169
243
  result: ToolResult;
package/dist/index.js CHANGED
@@ -1,3 +1,8 @@
1
+ // src/agent-loop.ts
2
+ import { homedir } from "os";
3
+ import { join } from "path";
4
+ import { ScopedStorage } from "@ethosagent/storage-fs";
5
+
1
6
  // src/defaults/in-memory-session.ts
2
7
  var InMemorySessionStore = class {
3
8
  sessions = /* @__PURE__ */ new Map();
@@ -150,9 +155,17 @@ var DefaultPersonalityRegistry = class {
150
155
  }
151
156
  async loadFromDirectory(_dir) {
152
157
  }
158
+ remove(id) {
159
+ this.personalities.delete(id);
160
+ }
153
161
  };
154
162
 
155
163
  // src/hook-registry.ts
164
+ function isAllowed(h, allowedPlugins) {
165
+ if (allowedPlugins === void 0) return true;
166
+ if (!h.pluginId) return true;
167
+ return allowedPlugins.includes(h.pluginId);
168
+ }
156
169
  var DefaultHookRegistry = class {
157
170
  voidHandlers = /* @__PURE__ */ new Map();
158
171
  modifyingHandlers = /* @__PURE__ */ new Map();
@@ -192,13 +205,18 @@ var DefaultHookRegistry = class {
192
205
  }
193
206
  // Void hooks: all handlers run in parallel via Promise.allSettled.
194
207
  // Failures are logged but never propagate (fail-open by default).
195
- async fireVoid(name, payload) {
196
- const handlers = this.voidHandlers.get(name) ?? [];
208
+ // allowedPlugins gates plugin-registered handlers; built-in handlers always fire.
209
+ async fireVoid(name, payload, allowedPlugins) {
210
+ const handlers = (this.voidHandlers.get(name) ?? []).filter(
211
+ (h) => isAllowed(h, allowedPlugins)
212
+ );
197
213
  await Promise.allSettled(handlers.map((h) => h.handler(payload)));
198
214
  }
199
215
  // Modifying hooks: handlers run sequentially; results are merged (first non-null value per key wins).
200
- async fireModifying(name, payload) {
201
- const handlers = this.modifyingHandlers.get(name) ?? [];
216
+ async fireModifying(name, payload, allowedPlugins) {
217
+ const handlers = (this.modifyingHandlers.get(name) ?? []).filter(
218
+ (h) => isAllowed(h, allowedPlugins)
219
+ );
202
220
  const merged = {};
203
221
  for (const h of handlers) {
204
222
  try {
@@ -216,8 +234,10 @@ var DefaultHookRegistry = class {
216
234
  return merged;
217
235
  }
218
236
  // Claiming hooks: handlers run sequentially, stop after first { handled: true }.
219
- async fireClaiming(name, payload) {
220
- const handlers = this.claimingHandlers.get(name) ?? [];
237
+ async fireClaiming(name, payload, allowedPlugins) {
238
+ const handlers = (this.claimingHandlers.get(name) ?? []).filter(
239
+ (h) => isAllowed(h, allowedPlugins)
240
+ );
221
241
  for (const h of handlers) {
222
242
  try {
223
243
  const result = await h.handler(payload);
@@ -249,10 +269,26 @@ var DefaultHookRegistry = class {
249
269
  };
250
270
 
251
271
  // src/tool-registry.ts
272
+ function mcpServerName(toolName) {
273
+ if (!toolName.startsWith("mcp__")) return void 0;
274
+ return toolName.split("__")[1];
275
+ }
276
+ function passesFilter(entry, filterOpts) {
277
+ if (!filterOpts) return true;
278
+ const { allowedMcpServers, allowedPlugins } = filterOpts;
279
+ if (allowedMcpServers !== void 0) {
280
+ const server = mcpServerName(entry.tool.name);
281
+ if (server !== void 0 && !allowedMcpServers.includes(server)) return false;
282
+ }
283
+ if (allowedPlugins !== void 0 && entry.pluginId !== void 0) {
284
+ if (!allowedPlugins.includes(entry.pluginId)) return false;
285
+ }
286
+ return true;
287
+ }
252
288
  var DefaultToolRegistry = class {
253
289
  tools = /* @__PURE__ */ new Map();
254
- register(tool) {
255
- this.tools.set(tool.name, tool);
290
+ register(tool, opts) {
291
+ this.tools.set(tool.name, { tool, pluginId: opts?.pluginId });
256
292
  }
257
293
  registerAll(tools) {
258
294
  for (const tool of tools) {
@@ -263,29 +299,49 @@ var DefaultToolRegistry = class {
263
299
  this.tools.delete(name);
264
300
  }
265
301
  get(name) {
266
- return this.tools.get(name);
302
+ return this.tools.get(name)?.tool;
267
303
  }
268
304
  getAvailable() {
269
- return [...this.tools.values()].filter((t) => !t.isAvailable || t.isAvailable());
305
+ return [...this.tools.values()].filter((e) => !e.tool.isAvailable || e.tool.isAvailable()).map((e) => e.tool);
270
306
  }
271
307
  getForToolset(toolset) {
272
308
  return this.getAvailable().filter((t) => t.toolset === toolset);
273
309
  }
274
- toDefinitions() {
275
- return this.getAvailable().map((t) => ({
276
- name: t.name,
277
- description: t.description,
278
- parameters: t.schema
310
+ toDefinitions(allowedTools, filterOpts) {
311
+ const entries = [...this.tools.values()].filter(
312
+ (e) => !e.tool.isAvailable || e.tool.isAvailable()
313
+ );
314
+ const filtered = entries.filter((e) => {
315
+ if (allowedTools && allowedTools.length > 0 && !allowedTools.includes(e.tool.name))
316
+ return false;
317
+ return passesFilter(e, filterOpts);
318
+ });
319
+ return filtered.map((e) => ({
320
+ name: e.tool.name,
321
+ description: e.tool.description,
322
+ parameters: e.tool.schema
279
323
  }));
280
324
  }
281
325
  // Runs all tool calls in parallel. Results are returned in input order.
282
326
  // Budget is split evenly across parallel calls; each result is post-trimmed to budget.
283
- async executeParallel(calls, ctx) {
327
+ // allowedTools + filterOpts enforce tool access at execution time (belt-and-suspenders).
328
+ async executeParallel(calls, ctx, allowedTools, filterOpts) {
284
329
  const perCallBudget = Math.floor(ctx.resultBudgetChars / Math.max(calls.length, 1));
285
330
  const results = await Promise.allSettled(
286
331
  calls.map(async (call) => {
287
- const tool = this.tools.get(call.name);
288
- if (!tool) {
332
+ if (allowedTools && allowedTools.length > 0 && !allowedTools.includes(call.name)) {
333
+ return {
334
+ toolCallId: call.toolCallId,
335
+ name: call.name,
336
+ result: {
337
+ ok: false,
338
+ error: `Tool ${call.name} is not permitted for this personality`,
339
+ code: "not_available"
340
+ }
341
+ };
342
+ }
343
+ const entry = this.tools.get(call.name);
344
+ if (!entry) {
289
345
  return {
290
346
  toolCallId: call.toolCallId,
291
347
  name: call.name,
@@ -296,7 +352,18 @@ var DefaultToolRegistry = class {
296
352
  }
297
353
  };
298
354
  }
299
- if (tool.isAvailable && !tool.isAvailable()) {
355
+ if (!passesFilter(entry, filterOpts)) {
356
+ return {
357
+ toolCallId: call.toolCallId,
358
+ name: call.name,
359
+ result: {
360
+ ok: false,
361
+ error: `Tool ${call.name} is not permitted for this personality`,
362
+ code: "not_available"
363
+ }
364
+ };
365
+ }
366
+ if (entry.tool.isAvailable && !entry.tool.isAvailable()) {
300
367
  return {
301
368
  toolCallId: call.toolCallId,
302
369
  name: call.name,
@@ -307,10 +374,10 @@ var DefaultToolRegistry = class {
307
374
  }
308
375
  };
309
376
  }
310
- const budget = Math.min(perCallBudget, tool.maxResultChars ?? perCallBudget);
377
+ const budget = Math.min(perCallBudget, entry.tool.maxResultChars ?? perCallBudget);
311
378
  const toolCtx = { ...ctx, resultBudgetChars: budget };
312
379
  try {
313
- const result = await tool.execute(call.args, toolCtx);
380
+ const result = await entry.tool.execute(call.args, toolCtx);
314
381
  if (result.ok && result.value.length > budget) {
315
382
  return {
316
383
  toolCallId: call.toolCallId,
@@ -359,13 +426,24 @@ var AgentLoop = class {
359
426
  personalities;
360
427
  memory;
361
428
  session;
429
+ /** Public so surfaces (web, ACP) can register late-binding hooks they own
430
+ * without re-running the whole wiring factory. The CLI/TUI register hooks
431
+ * before construction; web registers an approval hook after createAgentLoop
432
+ * returns. */
362
433
  hooks;
363
434
  injectors;
435
+ injectorPluginIds;
364
436
  maxIterations;
365
437
  historyLimit;
366
438
  platform;
367
439
  workingDir;
368
440
  resultBudgetChars;
441
+ maxToolCallsPerTurn;
442
+ maxIdenticalToolCalls;
443
+ streamingTimeoutMs;
444
+ modelRouting;
445
+ storage;
446
+ dataDir;
369
447
  constructor(config) {
370
448
  this.llm = config.llm;
371
449
  this.tools = config.tools ?? new DefaultToolRegistry();
@@ -374,11 +452,18 @@ var AgentLoop = class {
374
452
  this.session = config.session ?? new InMemorySessionStore();
375
453
  this.hooks = config.hooks ?? new DefaultHookRegistry();
376
454
  this.injectors = (config.injectors ?? []).sort((a, b) => b.priority - a.priority);
455
+ this.injectorPluginIds = config.injectorPluginIds ?? /* @__PURE__ */ new Map();
377
456
  this.maxIterations = config.options?.maxIterations ?? 50;
378
457
  this.historyLimit = config.options?.historyLimit ?? 200;
379
458
  this.platform = config.options?.platform ?? "cli";
380
459
  this.workingDir = config.options?.workingDir ?? process.cwd();
381
460
  this.resultBudgetChars = config.options?.resultBudgetChars ?? 8e4;
461
+ this.maxToolCallsPerTurn = config.options?.maxToolCallsPerTurn ?? 20;
462
+ this.maxIdenticalToolCalls = config.options?.maxIdenticalToolCalls ?? 5;
463
+ this.streamingTimeoutMs = config.options?.streamingTimeoutMs ?? 12e4;
464
+ this.modelRouting = config.modelRouting ?? {};
465
+ if (config.storage) this.storage = config.storage;
466
+ if (config.dataDir) this.dataDir = config.dataDir;
382
467
  }
383
468
  async *run(text, opts = {}) {
384
469
  const abortSignal = opts.abortSignal ?? new AbortController().signal;
@@ -402,12 +487,24 @@ var AgentLoop = class {
402
487
  });
403
488
  const sessionId = ethosSession.id;
404
489
  const personality = (opts.personalityId ? this.personalities.get(opts.personalityId) : null) ?? this.personalities.getDefault();
405
- await this.hooks.fireVoid("session_start", {
406
- sessionId,
407
- sessionKey,
408
- platform: this.platform,
409
- personalityId: personality.id
410
- });
490
+ const effectiveModel = this.modelRouting[personality.id] ?? this.llm.model;
491
+ const modelOverride = effectiveModel !== this.llm.model ? effectiveModel : void 0;
492
+ const allowedTools = personality.toolset?.length ? personality.toolset : void 0;
493
+ const allowedPlugins = personality.plugins ?? [];
494
+ const filterOpts = {
495
+ allowedMcpServers: personality.mcp_servers,
496
+ allowedPlugins
497
+ };
498
+ await this.hooks.fireVoid(
499
+ "session_start",
500
+ {
501
+ sessionId,
502
+ sessionKey,
503
+ platform: this.platform,
504
+ personalityId: personality.id
505
+ },
506
+ allowedPlugins
507
+ );
411
508
  await this.session.appendMessage({
412
509
  sessionId,
413
510
  role: "user",
@@ -420,7 +517,9 @@ var AgentLoop = class {
420
517
  sessionKey,
421
518
  platform: this.platform,
422
519
  workingDir: this.workingDir,
423
- personalityId: personality.id
520
+ personalityId: personality.id,
521
+ memoryScope: personality.memoryScope,
522
+ query: text
424
523
  });
425
524
  const promptCtx = {
426
525
  sessionId,
@@ -434,15 +533,13 @@ var AgentLoop = class {
434
533
  personalityId: personality.id
435
534
  };
436
535
  const systemParts = [];
437
- if (personality.ethosFile) {
438
- try {
439
- const fs = await import("fs/promises");
440
- const identity = await fs.readFile(personality.ethosFile, "utf-8");
441
- systemParts.push(identity.trim());
442
- } catch {
443
- }
536
+ if (personality.ethosFile && this.storage) {
537
+ const identity = await this.storage.read(personality.ethosFile);
538
+ if (identity) systemParts.push(identity.trim());
444
539
  }
445
540
  for (const injector of this.injectors) {
541
+ const injPluginId = this.injectorPluginIds.get(injector);
542
+ if (injPluginId !== void 0 && !allowedPlugins.includes(injPluginId)) continue;
446
543
  if (injector.shouldInject && !injector.shouldInject(promptCtx)) continue;
447
544
  const result = await injector.inject(promptCtx);
448
545
  if (result) {
@@ -453,16 +550,23 @@ var AgentLoop = class {
453
550
  }
454
551
  }
455
552
  }
553
+ if (promptCtx.meta && Object.keys(promptCtx.meta).length > 0) {
554
+ yield { type: "context_meta", data: promptCtx.meta };
555
+ }
456
556
  if (memCtx) {
457
557
  systemParts.push(`## Memory
458
558
 
459
559
  ${memCtx.content}`);
460
560
  }
461
- const buildResult = await this.hooks.fireModifying("before_prompt_build", {
462
- sessionId,
463
- personalityId: personality.id,
464
- history
465
- });
561
+ const buildResult = await this.hooks.fireModifying(
562
+ "before_prompt_build",
563
+ {
564
+ sessionId,
565
+ personalityId: personality.id,
566
+ history
567
+ },
568
+ allowedPlugins
569
+ );
466
570
  if (buildResult.overrideSystem) {
467
571
  systemParts.length = 0;
468
572
  systemParts.push(buildResult.overrideSystem);
@@ -474,38 +578,103 @@ ${memCtx.content}`);
474
578
  const llmMessages = this.toLLMMessages(history);
475
579
  let fullText = "";
476
580
  let turnCount = 0;
581
+ let totalToolCalls = 0;
582
+ const toolNameCounts = /* @__PURE__ */ new Map();
477
583
  for (let iteration = 0; iteration < this.maxIterations; iteration++) {
478
584
  if (abortSignal.aborted) {
479
585
  yield { type: "error", error: "Aborted", code: "aborted" };
480
586
  return;
481
587
  }
482
- await this.hooks.fireVoid("before_llm_call", {
483
- sessionId,
484
- model: this.llm.model,
485
- turnNumber: turnCount
486
- });
588
+ if (totalToolCalls >= this.maxToolCallsPerTurn) {
589
+ yield {
590
+ type: "tool_progress",
591
+ toolName: "_budget",
592
+ message: `Stopped: hit ${this.maxToolCallsPerTurn}-tool-call budget for this turn`,
593
+ audience: "user"
594
+ };
595
+ break;
596
+ }
597
+ const overusedTool = [...toolNameCounts.entries()].find(
598
+ ([, count]) => count >= this.maxIdenticalToolCalls
599
+ );
600
+ if (overusedTool) {
601
+ yield {
602
+ type: "tool_progress",
603
+ toolName: overusedTool[0],
604
+ message: `Stopped: ${overusedTool[0]} called ${overusedTool[1]} times in one turn (likely loop)`,
605
+ audience: "user"
606
+ };
607
+ break;
608
+ }
609
+ await this.hooks.fireVoid(
610
+ "before_llm_call",
611
+ {
612
+ sessionId,
613
+ model: this.llm.model,
614
+ turnNumber: turnCount
615
+ },
616
+ allowedPlugins
617
+ );
487
618
  const pendingToolCalls = [];
488
619
  let chunkText = "";
620
+ const watchdogMs = personality.streamingTimeoutMs ?? this.streamingTimeoutMs;
621
+ const watchdogController = new AbortController();
622
+ const combinedSignal = AbortSignal.any([abortSignal, watchdogController.signal]);
623
+ let watchdogTimer;
624
+ const armWatchdog = () => {
625
+ if (watchdogTimer) clearTimeout(watchdogTimer);
626
+ watchdogTimer = setTimeout(() => watchdogController.abort(), watchdogMs);
627
+ };
628
+ const disarmWatchdog = () => {
629
+ if (watchdogTimer) clearTimeout(watchdogTimer);
630
+ watchdogTimer = void 0;
631
+ };
489
632
  try {
490
- const stream = this.llm.complete(llmMessages, this.tools.toDefinitions(), {
633
+ armWatchdog();
634
+ const stream = this.llm.complete(llmMessages, this.tools.toDefinitions(allowedTools, filterOpts), {
491
635
  system: systemPrompt,
492
636
  cacheSystemPrompt: true,
493
- abortSignal
637
+ abortSignal: combinedSignal,
638
+ ...modelOverride ? { modelOverride } : {}
494
639
  });
495
640
  for await (const chunk of stream) {
496
641
  if (abortSignal.aborted) break;
642
+ if (watchdogController.signal.aborted) break;
643
+ armWatchdog();
497
644
  yield* this.handleChunk(chunk, pendingToolCalls, (t) => {
498
645
  chunkText += t;
499
646
  fullText += t;
500
647
  });
501
648
  }
649
+ disarmWatchdog();
650
+ if (watchdogController.signal.aborted && !abortSignal.aborted) {
651
+ yield {
652
+ type: "error",
653
+ error: `LLM stream stalled \u2014 no chunk for ${watchdogMs}ms`,
654
+ code: "streaming_timeout"
655
+ };
656
+ return;
657
+ }
502
658
  } catch (err) {
659
+ disarmWatchdog();
660
+ if (watchdogController.signal.aborted && !abortSignal.aborted) {
661
+ yield {
662
+ type: "error",
663
+ error: `LLM stream stalled \u2014 no chunk for ${watchdogMs}ms`,
664
+ code: "streaming_timeout"
665
+ };
666
+ return;
667
+ }
503
668
  const msg = err instanceof Error ? err.message : String(err);
504
669
  yield { type: "error", error: msg, code: "llm_error" };
505
670
  return;
506
671
  }
507
672
  turnCount++;
508
673
  const completedToolCalls = pendingToolCalls.filter((tc) => tc.args !== void 0);
674
+ totalToolCalls += completedToolCalls.length;
675
+ for (const tc of completedToolCalls) {
676
+ toolNameCounts.set(tc.toolName, (toolNameCounts.get(tc.toolName) ?? 0) + 1);
677
+ }
509
678
  await this.session.appendMessage({
510
679
  sessionId,
511
680
  role: "assistant",
@@ -518,11 +687,15 @@ ${memCtx.content}`);
518
687
  }))
519
688
  }
520
689
  });
521
- await this.hooks.fireVoid("after_llm_call", {
522
- sessionId,
523
- text: chunkText,
524
- usage: { inputTokens: 0, outputTokens: 0 }
525
- });
690
+ await this.hooks.fireVoid(
691
+ "after_llm_call",
692
+ {
693
+ sessionId,
694
+ text: chunkText,
695
+ usage: { inputTokens: 0, outputTokens: 0 }
696
+ },
697
+ allowedPlugins
698
+ );
526
699
  if (completedToolCalls.length > 0) {
527
700
  const assistantContent = [];
528
701
  if (chunkText) assistantContent.push({ type: "text", text: chunkText });
@@ -539,32 +712,50 @@ ${memCtx.content}`);
539
712
  llmMessages.push({ role: "assistant", content: chunkText });
540
713
  break;
541
714
  }
715
+ const progressBuffer = [];
716
+ const scopedStorage = this.buildScopedStorage(personality);
542
717
  const toolCtxBase = {
543
718
  sessionId,
544
719
  sessionKey,
545
720
  platform: this.platform,
546
721
  workingDir: this.workingDir,
722
+ agentId: opts.agentId,
723
+ personalityId: personality.id,
724
+ memoryScope: personality.memoryScope,
547
725
  currentTurn: turnCount,
548
726
  messageCount: allMessages.length + turnCount,
549
727
  abortSignal,
550
- emit: (_event) => {
728
+ emit: (event) => {
729
+ progressBuffer.push({
730
+ toolName: event.toolName,
731
+ message: event.message,
732
+ ...event.percent !== void 0 && { percent: event.percent },
733
+ audience: event.audience ?? "internal"
734
+ });
551
735
  },
552
- resultBudgetChars: this.resultBudgetChars
736
+ resultBudgetChars: this.resultBudgetChars,
737
+ ...scopedStorage ? { storage: scopedStorage } : {}
553
738
  };
554
739
  const prepped = [];
555
740
  for (const tc of completedToolCalls) {
556
- const beforeResult = await this.hooks.fireModifying("before_tool_call", {
557
- sessionId,
558
- toolName: tc.toolName,
559
- args: tc.args
560
- });
741
+ const beforeResult = await this.hooks.fireModifying(
742
+ "before_tool_call",
743
+ {
744
+ sessionId,
745
+ toolCallId: tc.toolCallId,
746
+ toolName: tc.toolName,
747
+ args: tc.args
748
+ },
749
+ allowedPlugins
750
+ );
561
751
  if (beforeResult.error) {
562
752
  yield {
563
753
  type: "tool_end",
564
754
  toolCallId: tc.toolCallId,
565
755
  toolName: tc.toolName,
566
756
  ok: false,
567
- durationMs: 0
757
+ durationMs: 0,
758
+ result: beforeResult.error
568
759
  };
569
760
  prepped.push({
570
761
  toolCallId: tc.toolCallId,
@@ -585,8 +776,12 @@ ${memCtx.content}`);
585
776
  }
586
777
  const execInputs = prepped.filter((p) => p.rejected === void 0).map((p) => ({ toolCallId: p.toolCallId, name: p.name, args: p.args }));
587
778
  const startedAt = Date.now();
588
- const execResults = execInputs.length > 0 ? await this.tools.executeParallel(execInputs, toolCtxBase) : [];
779
+ const execResults = execInputs.length > 0 ? await this.tools.executeParallel(execInputs, toolCtxBase, allowedTools, filterOpts) : [];
589
780
  const execResultMap = new Map(execResults.map((r) => [r.toolCallId, r]));
781
+ for (const ev of progressBuffer) {
782
+ yield { type: "tool_progress", ...ev };
783
+ }
784
+ progressBuffer.length = 0;
590
785
  const toolResultContent = [];
591
786
  for (const p of prepped) {
592
787
  const durationMs = Date.now() - startedAt;
@@ -605,14 +800,19 @@ ${memCtx.content}`);
605
800
  toolCallId: p.toolCallId,
606
801
  toolName: p.name,
607
802
  ok: result.ok,
608
- durationMs
803
+ durationMs,
804
+ result: result.ok ? result.value : result.error
609
805
  };
610
- await this.hooks.fireVoid("after_tool_call", {
611
- sessionId,
612
- toolName: p.name,
613
- result,
614
- durationMs
615
- });
806
+ await this.hooks.fireVoid(
807
+ "after_tool_call",
808
+ {
809
+ sessionId,
810
+ toolName: p.name,
811
+ result,
812
+ durationMs
813
+ },
814
+ allowedPlugins
815
+ );
616
816
  }
617
817
  await this.session.appendMessage({
618
818
  sessionId,
@@ -630,12 +830,8 @@ ${memCtx.content}`);
630
830
  }
631
831
  llmMessages.push({ role: "user", content: toolResultContent });
632
832
  }
633
- await this.memory.sync(
634
- { sessionId, sessionKey, platform: this.platform, workingDir: this.workingDir },
635
- []
636
- );
637
833
  await this.session.updateUsage(sessionId, { apiCallCount: turnCount });
638
- await this.hooks.fireVoid("agent_done", { sessionId, text: fullText, turnCount });
834
+ await this.hooks.fireVoid("agent_done", { sessionId, text: fullText, turnCount }, allowedPlugins);
639
835
  yield { type: "done", text: fullText, turnCount };
640
836
  }
641
837
  *handleChunk(chunk, pendingToolCalls, onText) {
@@ -719,7 +915,32 @@ ${memCtx.content}`);
719
915
  }
720
916
  return messages;
721
917
  }
918
+ // ---------------------------------------------------------------------------
919
+ // Per-turn ScopedStorage construction (Phase 4 — fs_reach enforcement).
920
+ //
921
+ // When the AgentLoop was wired with `storage` + `dataDir`, build a
922
+ // ScopedStorage decorated with the active personality's `fs_reach`
923
+ // allowlist for this turn. Substitutions (${ETHOS_HOME} / ${self} /
924
+ // ${CWD}) are resolved here so the underlying storage-fs class stays
925
+ // pristine. When `fs_reach` is unset, fall back to a sensible default:
926
+ // read: [<ethosHome>/personalities/<self>/, <ethosHome>/skills/, <cwd>]
927
+ // write: [<ethosHome>/personalities/<self>/, <cwd>]
928
+ // ---------------------------------------------------------------------------
929
+ buildScopedStorage(personality) {
930
+ if (!this.storage) return void 0;
931
+ const ethosHome = this.dataDir ?? join(homedir(), ".ethos");
932
+ const cwd = this.workingDir;
933
+ const self = personality.id;
934
+ const ownDir = `${join(ethosHome, "personalities", self)}/`;
935
+ const fsReach = personality.fs_reach;
936
+ const readPrefixes = fsReach?.read && fsReach.read.length > 0 ? fsReach.read.map((p) => substitute(p, { ethosHome, self, cwd })) : [ownDir, `${join(ethosHome, "skills")}/`, cwd];
937
+ const writePrefixes = fsReach?.write && fsReach.write.length > 0 ? fsReach.write.map((p) => substitute(p, { ethosHome, self, cwd })) : [ownDir, cwd];
938
+ return new ScopedStorage(this.storage, { read: readPrefixes, write: writePrefixes });
939
+ }
722
940
  };
941
+ function substitute(template, vars) {
942
+ return template.replace(/\$\{ETHOS_HOME\}/g, vars.ethosHome).replace(/\$\{self\}/g, vars.self).replace(/\$\{CWD\}/g, vars.cwd);
943
+ }
723
944
 
724
945
  // src/plugin-registry.ts
725
946
  var PluginRegistry = class {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../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/agent-loop.ts","../src/plugin-registry.ts"],"sourcesContent":["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","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\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 async fireVoid<K extends keyof VoidHooks>(name: K, payload: VoidHooks[K]): Promise<void> {\n const handlers = this.voidHandlers.get(name) ?? [];\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 ): Promise<ModifyingHooks[K][1]> {\n const handlers = this.modifyingHandlers.get(name) ?? [];\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 ): Promise<ClaimingHooks[K][1]> {\n const handlers = this.claimingHandlers.get(name) ?? [];\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, ToolRegistry, ToolResult } from '@ethosagent/types';\n\nexport class DefaultToolRegistry implements ToolRegistry {\n private readonly tools = new Map<string, Tool>();\n\n register(tool: Tool): void {\n this.tools.set(tool.name, tool);\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);\n }\n\n getAvailable(): Tool[] {\n return [...this.tools.values()].filter((t) => !t.isAvailable || t.isAvailable());\n }\n\n getForToolset(toolset: string): Tool[] {\n return this.getAvailable().filter((t) => t.toolset === toolset);\n }\n\n toDefinitions() {\n return this.getAvailable().map((t) => ({\n name: t.name,\n description: t.description,\n parameters: t.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 async executeParallel(\n calls: Array<{ toolCallId: string; name: string; args: unknown }>,\n ctx: ToolContext,\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 tool = this.tools.get(call.name);\n if (!tool) {\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 if (tool.isAvailable && !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, tool.maxResultChars ?? perCallBudget);\n const toolCtx: ToolContext = { ...ctx, resultBudgetChars: budget };\n\n try {\n const result = await 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","import type {\n CompletionChunk,\n ContextInjector,\n HookRegistry,\n LLMProvider,\n MemoryProvider,\n Message,\n MessageContent,\n PersonalityRegistry,\n SessionStore,\n StoredMessage,\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 | { type: 'tool_progress'; toolName: string; message: string; percent?: number }\n | { type: 'tool_end'; toolCallId: string; toolName: string; ok: boolean; durationMs: number }\n | { type: 'usage'; inputTokens: number; outputTokens: number; estimatedCostUsd: number }\n | { type: 'error'; error: string; code: string }\n | { type: 'done'; text: string; turnCount: number };\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 options?: {\n maxIterations?: number;\n historyLimit?: number;\n platform?: string;\n workingDir?: string;\n resultBudgetChars?: number;\n };\n}\n\nexport interface RunOptions {\n sessionKey?: string;\n personalityId?: string;\n abortSignal?: AbortSignal;\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 private readonly hooks: HookRegistry;\n private readonly injectors: ContextInjector[];\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\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.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 }\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 // Step 2: Fire session_start hooks\n await this.hooks.fireVoid('session_start', {\n sessionId,\n sessionKey,\n platform: this.platform,\n personalityId: personality.id,\n });\n\n // Step 3: Persist the user message\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 });\n\n // Step 6: Build system prompt from injectors\n const promptCtx = {\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\n if (personality.ethosFile) {\n try {\n const fs = await import('node:fs/promises');\n const identity = await fs.readFile(personality.ethosFile, 'utf-8');\n systemParts.push(identity.trim());\n } catch {\n // ethosFile not readable — skip\n }\n }\n\n // Context injectors sorted by priority (already sorted in constructor)\n for (const injector of this.injectors) {\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 // 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('before_prompt_build', {\n sessionId,\n personalityId: personality.id,\n history,\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 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 // Fire before_llm_call\n await this.hooks.fireVoid('before_llm_call', {\n sessionId,\n model: this.llm.model,\n turnNumber: turnCount,\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 try {\n const stream = this.llm.complete(llmMessages, this.tools.toDefinitions(), {\n system: systemPrompt,\n cacheSystemPrompt: true,\n abortSignal,\n });\n\n for await (const chunk of stream) {\n if (abortSignal.aborted) break;\n yield* this.handleChunk(chunk, pendingToolCalls, (t) => {\n chunkText += t;\n fullText += t;\n });\n }\n } catch (err) {\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 // 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('after_llm_call', {\n sessionId,\n text: chunkText,\n usage: { inputTokens: 0, outputTokens: 0 },\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 const toolCtxBase = {\n sessionId,\n sessionKey,\n platform: this.platform,\n workingDir: this.workingDir,\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 }) => {\n // Progress emission wired in Phase 6 (terminal streaming)\n },\n resultBudgetChars: this.resultBudgetChars,\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('before_tool_call', {\n sessionId,\n toolName: tc.toolName,\n args: tc.args,\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 };\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 ? await this.tools.executeParallel(execInputs, toolCtxBase) : [];\n const execResultMap = new Map(execResults.map((r) => [r.toolCallId, r]));\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 };\n await this.hooks.fireVoid('after_tool_call', {\n sessionId,\n toolName: p.name,\n result,\n durationMs,\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: Sync memory\n await this.memory.sync(\n { sessionId, sessionKey, platform: this.platform, workingDir: this.workingDir },\n [],\n );\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 });\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","// 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":"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","names":[]}
1
+ {"version":3,"sources":["../src/agent-loop.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 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":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ethosagent/core",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
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,8 @@
19
19
  }
20
20
  },
21
21
  "dependencies": {
22
- "@ethosagent/types": "0.1.0"
22
+ "@ethosagent/storage-fs": "0.0.0",
23
+ "@ethosagent/types": "0.2.0"
23
24
  },
24
25
  "scripts": {
25
26
  "build": "tsup src/index.ts --format esm --dts --sourcemap"