File size: 1,735 Bytes
4e23b01
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import type { SystemMessage, UserMessage } from '#/llm/message';
import type { AgentEmitted } from '#/agent/machine';
import { createSystemEntry, createUserEntry, type SystemEntry, type UserEntry } from '#/agent/turn';
import type { ToolDefinition } from '#/tool/tool';

export interface AgentPluginTarget {
  kind: 'agent';
  on(type: AgentEmitted['type'], handler: (event: AgentEmitted) => void): unknown;
  notify(message: UserMessage): void;
  remind(key: string, message: UserMessage | SystemMessage): void;
}

export type PluginTarget = AgentPluginTarget;

export interface Plugin {
  readonly name: string;
  tools?(): readonly ToolDefinition[];
  connect?(target: PluginTarget): void;
}

export function collectPluginTools(plugins: readonly Plugin[]): readonly ToolDefinition[] {
  return plugins.flatMap((plugin) => plugin.tools?.() ?? []);
}

export interface AgentPluginSource {
  on(type: AgentEmitted['type'], handler: (event: AgentEmitted) => void): unknown;
  send(
    event:
      | { type: 'input.notify'; entry: UserEntry }
      | { type: 'input.remind'; key: string; entry: SystemEntry | UserEntry },
  ): void;
}

export function connectPlugins(actor: AgentPluginSource, plugins: readonly Plugin[]): void {
  const target: AgentPluginTarget = {
    kind: 'agent',
    on: (type, handler) => {
      actor.on(type, handler);
    },
    notify: (message) => {
      actor.send({ type: 'input.notify', entry: { message } });
    },
    remind: (key, message) => {
      actor.send({
        type: 'input.remind',
        key,
        entry: message.role === 'system' ? createSystemEntry(message) : createUserEntry(message),
      });
    },
  };
  for (const plugin of plugins) {
    plugin.connect?.(target);
  }
}