char-slop/berry

multipurpose self-modifiable agentic harness

git clone https://git.t4t.associates/char-slop/berry

Charlotte SomSupport conversation-scoped tools exposed directly to models8fcdffd

main
2.1 KiB58 linesraw
1import type { Context } from "@earendil-works/chord";
2import { awaitWithContext } from "@earendil-works/chord/context";
3import type {
4  Agent,
5  AnyTask,
6  ConversationId,
7  RegistryReader,
8  Task,
9} from "@earendil-works/pi-durable";
10
11/** Adapt phase agents until pi-durable supports conversation-scoped tool registration. */
12export function conversationRegistry(
13  registry: RegistryReader,
14  resolve: (agent: Agent, id: ConversationId, context: Context) => Promise<Agent>,
15): RegistryReader {
16  return {
17    subscribe: (listener) => registry.subscribe(listener),
18    snapshot() {
19      const snapshot = registry.snapshot();
20      const tasks = new Map<string, AnyTask>();
21      type Handler = Task<unknown, { phase: string }, unknown, object>["definition"]["abort"];
22      const wrap = (handler: Handler): Handler => (record, runtime, context) => {
23        let agent: Promise<Agent> | undefined;
24        const adapted = new Proxy(runtime, {
25          get(target, key) {
26            if (key === "agent") {
27              return async (during: Context) => {
28                const base = await target.agent(during);
29                agent ??= resolve(base, target.conversationId, during);
30                return await awaitWithContext(agent, during);
31              };
32            }
33            return Reflect.get(target, key);
34          },
35        });
36        return handler(record, adapted, context);
37      };
38      for (const original of snapshot.tasks()) {
39        const task = original as Task<unknown, { phase: string }, unknown, object>;
40        const phases = Object.fromEntries(
41          Object.entries(task.definition.phases).map(([name, phase]) => [name, wrap(phase)]),
42        );
43        tasks.set(task.definition.name, {
44          ...task,
45          definition: { ...task.definition, phases, abort: wrap(task.definition.abort) },
46        });
47      }
48      return {
49        installed: () => snapshot.installed(),
50        extension: (name) => snapshot.extension(name),
51        tools: () => snapshot.tools(),
52        sections: () => snapshot.sections(),
53        tasks: () => [...tasks.values()],
54        task: (name) => tasks.get(name),
55      };
56    },
57  };
58}