|
1 | 1 | /** |
2 | | - * Local mirror of the proactive-runtime SDK contract. |
| 2 | + * The proactive-runtime SDK contract for these agents. |
3 | 3 | * |
4 | | - * Once `@agent-relay/agent` is published, replace this whole file with: |
5 | | - * export { agent, type AgentDefinition, type AgentEvent, type Context } from "@agent-relay/agent"; |
| 4 | + * The TYPES below are the published `@agent-relay/agent` contract — the package |
| 5 | + * shipped, so the local mirror that used to live here is gone and handlers now |
| 6 | + * type-check against the real SDK (`AgentDefinition`, `AgentEvent`, `Context`, |
| 7 | + * `ScheduleSpec`). |
6 | 8 | * |
7 | | - * Until then we keep the types here so the handlers type-check, and `agent()` |
8 | | - * runs as a no-op shim in local dev (registers the definition, doesn't dispatch). |
| 9 | + * `agent()`, however, stays a LOCAL registrar rather than the SDK's hosted |
| 10 | + * runtime. These agents dispatch from Cloudflare Pages Functions |
| 11 | + * (`functions/api/...`) — request-scoped Workers that cannot hold the SDK's |
| 12 | + * long-lived broker connection. The Pages Function reaches into |
| 13 | + * `handle.definition.onEvent(ctx, event)` directly, so we expose `.definition`, |
| 14 | + * which the hosted `agent()` (a connection-backed handle) does not. Move to the |
| 15 | + * hosted runtime only if these ever run on a long-lived host instead of Pages |
| 16 | + * Functions. |
9 | 17 | */ |
10 | | - |
| 18 | +export type { |
| 19 | + AgentDefinition, |
| 20 | + AgentEvent, |
| 21 | + AgentHandle, |
| 22 | + Context, |
| 23 | + EventType, |
| 24 | + Logger, |
| 25 | + ScheduleSpec |
| 26 | +} from "@agent-relay/agent"; |
| 27 | + |
| 28 | +import type { AgentDefinition, AgentHandle, Context } from "@agent-relay/agent"; |
| 29 | + |
| 30 | +/** Local trigger taxonomy used by the activity log; not part of the SDK. */ |
11 | 31 | export type Trigger = "time" | "change" | "message"; |
12 | 32 |
|
13 | | -export type ScheduleSpec = |
14 | | - | string |
15 | | - | { cron: string; tz?: string } |
16 | | - | { at: string | Date }; |
17 | | - |
18 | | -export type EventType = |
19 | | - | "startup" |
20 | | - | "cron.tick" |
21 | | - | "relayfile.changed" |
22 | | - | "relaycast.message" |
23 | | - | (string & {}); // provider events: "github.pull_request.opened", "notion.page.updated", etc. |
24 | | - |
25 | | -export type AgentEvent<T extends EventType = EventType> = { |
26 | | - id: string; |
27 | | - workspace: string; |
28 | | - type: T; |
29 | | - occurredAt: string; |
30 | | - attempt: number; |
31 | | - resource: { |
32 | | - path: string; |
33 | | - kind: string; |
34 | | - id: string; |
35 | | - provider: string; |
36 | | - }; |
37 | | - summary: { |
38 | | - title?: string; |
39 | | - status?: string; |
40 | | - priority?: string; |
41 | | - labels?: string[]; |
42 | | - actor?: { id: string; displayName?: string }; |
43 | | - fieldsChanged?: string[]; |
44 | | - tags?: string[]; |
45 | | - }; |
46 | | - expand: <L extends "summary" | "full" | "diff" | "thread">(level?: L) => Promise<unknown>; |
47 | | - digest?: string; |
48 | | -}; |
49 | | - |
50 | | -export type Logger = { |
51 | | - info(msg: string, meta?: Record<string, unknown>): void; |
52 | | - warn(msg: string, meta?: Record<string, unknown>): void; |
53 | | - error(msg: string, meta?: Record<string, unknown>): void; |
54 | | -}; |
55 | | - |
56 | | -export type Context = { |
57 | | - workspace: string; |
58 | | - agentId: string; |
59 | | - logger: Logger; |
60 | | - signal: AbortSignal; |
61 | | - files: { |
62 | | - read(path: string): Promise<{ body: unknown; meta?: unknown } | null>; |
63 | | - write(path: string, body: unknown, meta?: Record<string, unknown>): Promise<void>; |
64 | | - delete(path: string): Promise<void>; |
65 | | - list(glob: string): Promise<{ path: string }[]>; |
66 | | - }; |
67 | | - messages: { |
68 | | - post(channel: string, text: string, opts?: Record<string, unknown>): Promise<{ id: string }>; |
69 | | - reply(threadId: string, text: string, opts?: Record<string, unknown>): Promise<{ id: string }>; |
70 | | - dm(agentOrUser: string, text: string): Promise<{ id: string }>; |
71 | | - }; |
72 | | - schedule: { |
73 | | - at(when: string | Date, payload?: unknown): Promise<{ id: string }>; |
74 | | - every(cron: string, payload?: unknown, opts?: { tz?: string }): Promise<{ id: string }>; |
75 | | - cancel(id: string): Promise<void>; |
76 | | - }; |
77 | | - once<T>(key: string, fn: () => Promise<T>): Promise<T>; |
78 | | -}; |
79 | | - |
80 | | -export type AgentDefinition = { |
81 | | - workspace: string; |
82 | | - name?: string; |
83 | | - schedule?: ScheduleSpec | ScheduleSpec[]; |
84 | | - watch?: string | string[]; |
85 | | - inbox?: string | string[]; |
86 | | - onEvent: (ctx: Context, event: AgentEvent) => Promise<void> | void; |
87 | | - onStart?: (ctx: Context) => Promise<void> | void; |
88 | | - onError?: (ctx: Context, error: Error, event: AgentEvent) => Promise<void> | void; |
89 | | - options?: { |
90 | | - concurrency?: number; |
91 | | - handlerTimeoutMs?: number; |
92 | | - replayOnStart?: "none" | string; |
93 | | - }; |
94 | | -}; |
95 | | - |
96 | | -export type AgentHandle = { |
97 | | - ready: Promise<void>; |
98 | | - stop: () => Promise<void>; |
99 | | - trigger: (event: Partial<AgentEvent>) => Promise<void>; |
100 | | - ctx: Context; |
101 | | -}; |
102 | | - |
103 | 33 | /** |
104 | | - * Local shim. When @agent-relay/agent ships, replace this whole module with: |
105 | | - * export { agent, type AgentDefinition, ... } from "@agent-relay/agent"; |
106 | | - * |
107 | | - * Until then `agent()` returns a handle whose `trigger()` invokes the |
108 | | - * registered `onEvent` synchronously with whatever event you pass it. This |
109 | | - * is exactly the spec's "imperative trigger — useful in tests" semantic, and |
110 | | - * it's how the Pages Function (functions/api/cron/[agent].ts) dispatches |
111 | | - * cron.tick events until the real runtime takes over. |
| 34 | + * Local handle shape. Extends the SDK {@link AgentHandle} with the original |
| 35 | + * `definition` so the Pages Function dispatch can invoke `onEvent` directly |
| 36 | + * (see the module comment). |
112 | 37 | */ |
113 | 38 | export type AgentHandleWithDef = AgentHandle & { |
114 | 39 | /** The original definition. Lets the Pages Function reach `onEvent`. */ |
115 | 40 | definition: AgentDefinition; |
116 | 41 | }; |
117 | 42 |
|
| 43 | +/** |
| 44 | + * Register a proactive agent. In the Pages-Functions deployment this records |
| 45 | + * the definition and returns a handle whose `definition` the entry-point |
| 46 | + * dispatches against; it does not open a broker connection. `trigger()` throws |
| 47 | + * — callers invoke `handle.definition.onEvent(ctx, event)` directly. |
| 48 | + */ |
118 | 49 | export function agent(definition: AgentDefinition): AgentHandleWithDef { |
119 | | - // No real-time dispatch in the shim — schedules / watches / inbox are not |
120 | | - // wired to anything. The runtime takes that over. Until then, callers |
121 | | - // (e.g. functions/api/cron/[agent].ts) reach into `handle.definition` and |
122 | | - // invoke `onEvent(realCtx, event)` directly. |
123 | 50 | return { |
124 | 51 | definition, |
125 | 52 | ready: Promise.resolve(), |
126 | 53 | stop: async () => {}, |
127 | 54 | ctx: {} as Context, |
128 | 55 | trigger: async () => { |
129 | 56 | throw new Error( |
130 | | - "shim: handle.trigger() not implemented — invoke handle.definition.onEvent(ctx, event) directly", |
| 57 | + "local registrar: invoke handle.definition.onEvent(ctx, event) directly", |
131 | 58 | ); |
132 | 59 | }, |
133 | 60 | }; |
|
0 commit comments