@swirls/looms 0.0.1
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/LICENSE +190 -0
- package/README.md +9 -0
- package/dist/actor/cell.d.ts +27 -0
- package/dist/actor/cell.js +41 -0
- package/dist/actor/host.d.ts +27 -0
- package/dist/actor/host.js +82 -0
- package/dist/actor/index.d.ts +3 -0
- package/dist/actor/index.js +3 -0
- package/dist/actor/routing.d.ts +15 -0
- package/dist/actor/routing.js +80 -0
- package/dist/agent/definitions-store.d.ts +11 -0
- package/dist/agent/definitions-store.js +10 -0
- package/dist/agent/definitions.d.ts +152 -0
- package/dist/agent/definitions.js +74 -0
- package/dist/agent/effects.d.ts +425 -0
- package/dist/agent/effects.js +318 -0
- package/dist/agent/events.d.ts +170 -0
- package/dist/agent/events.js +61 -0
- package/dist/agent/index.d.ts +13 -0
- package/dist/agent/index.js +13 -0
- package/dist/agent/llm.d.ts +35 -0
- package/dist/agent/llm.js +48 -0
- package/dist/agent/module.d.ts +1037 -0
- package/dist/agent/module.js +27 -0
- package/dist/agent/projections.d.ts +17 -0
- package/dist/agent/projections.js +81 -0
- package/dist/agent/scope.d.ts +171 -0
- package/dist/agent/scope.js +7 -0
- package/dist/agent/signals.d.ts +17 -0
- package/dist/agent/signals.js +19 -0
- package/dist/agent/stop-when.d.ts +12 -0
- package/dist/agent/stop-when.js +6 -0
- package/dist/agent/threads.d.ts +34 -0
- package/dist/agent/threads.js +407 -0
- package/dist/agent/tool-schema.d.ts +21 -0
- package/dist/agent/tool-schema.js +67 -0
- package/dist/agent/types.d.ts +87 -0
- package/dist/agent/types.js +32 -0
- package/dist/ai-vercel/index.d.ts +2 -0
- package/dist/ai-vercel/index.js +2 -0
- package/dist/ai-vercel/messages.d.ts +9 -0
- package/dist/ai-vercel/messages.js +68 -0
- package/dist/ai-vercel/vercel-llm.d.ts +8 -0
- package/dist/ai-vercel/vercel-llm.js +98 -0
- package/dist/approval/effects.d.ts +133 -0
- package/dist/approval/effects.js +13 -0
- package/dist/approval/events.d.ts +52 -0
- package/dist/approval/events.js +28 -0
- package/dist/approval/index.d.ts +6 -0
- package/dist/approval/index.js +6 -0
- package/dist/approval/module.d.ts +342 -0
- package/dist/approval/module.js +10 -0
- package/dist/approval/projections.d.ts +42 -0
- package/dist/approval/projections.js +57 -0
- package/dist/approval/scope.d.ts +45 -0
- package/dist/approval/scope.js +7 -0
- package/dist/approval/signals.d.ts +19 -0
- package/dist/approval/signals.js +34 -0
- package/dist/cli/cli.d.ts +2 -0
- package/dist/cli/cli.js +6 -0
- package/dist/cli/commands.d.ts +13 -0
- package/dist/cli/commands.js +133 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +1 -0
- package/dist/client/client.d.ts +69 -0
- package/dist/client/client.js +179 -0
- package/dist/client/index.d.ts +2 -0
- package/dist/client/index.js +2 -0
- package/dist/client/sse.d.ts +10 -0
- package/dist/client/sse.js +103 -0
- package/dist/cloudflare/alarm-scheduler.d.ts +9 -0
- package/dist/cloudflare/alarm-scheduler.js +13 -0
- package/dist/cloudflare/durable-object.d.ts +24 -0
- package/dist/cloudflare/durable-object.js +43 -0
- package/dist/cloudflare/event-store.d.ts +10 -0
- package/dist/cloudflare/event-store.js +18 -0
- package/dist/cloudflare/index.d.ts +4 -0
- package/dist/cloudflare/index.js +4 -0
- package/dist/cloudflare/router.d.ts +12 -0
- package/dist/cloudflare/router.js +26 -0
- package/dist/core/bun-sqlite.d.ts +13 -0
- package/dist/core/bun-sqlite.js +19 -0
- package/dist/core/catalog.d.ts +49 -0
- package/dist/core/catalog.js +39 -0
- package/dist/core/coalesce.d.ts +21 -0
- package/dist/core/coalesce.js +178 -0
- package/dist/core/compose.d.ts +59 -0
- package/dist/core/compose.js +97 -0
- package/dist/core/cursor.d.ts +16 -0
- package/dist/core/cursor.js +46 -0
- package/dist/core/effects.d.ts +110 -0
- package/dist/core/effects.js +99 -0
- package/dist/core/envelope.d.ts +79 -0
- package/dist/core/envelope.js +68 -0
- package/dist/core/event-codec.d.ts +8 -0
- package/dist/core/event-codec.js +82 -0
- package/dist/core/event-index.d.ts +28 -0
- package/dist/core/event-index.js +86 -0
- package/dist/core/fold.d.ts +19 -0
- package/dist/core/fold.js +362 -0
- package/dist/core/ids.d.ts +11 -0
- package/dist/core/ids.js +47 -0
- package/dist/core/index.d.ts +26 -0
- package/dist/core/index.js +25 -0
- package/dist/core/match.d.ts +8 -0
- package/dist/core/match.js +58 -0
- package/dist/core/module-scope.d.ts +99 -0
- package/dist/core/module-scope.js +72 -0
- package/dist/core/module.d.ts +74 -0
- package/dist/core/module.js +3 -0
- package/dist/core/projection.d.ts +83 -0
- package/dist/core/projection.js +241 -0
- package/dist/core/protocol.d.ts +99 -0
- package/dist/core/protocol.js +101 -0
- package/dist/core/replay.d.ts +13 -0
- package/dist/core/replay.js +34 -0
- package/dist/core/schema.d.ts +35 -0
- package/dist/core/schema.js +130 -0
- package/dist/core/serialize.d.ts +14 -0
- package/dist/core/serialize.js +42 -0
- package/dist/core/snapshot-store.d.ts +39 -0
- package/dist/core/snapshot-store.js +53 -0
- package/dist/core/snapshots.d.ts +26 -0
- package/dist/core/snapshots.js +100 -0
- package/dist/core/sqlite-store.d.ts +31 -0
- package/dist/core/sqlite-store.js +328 -0
- package/dist/core/state.d.ts +43 -0
- package/dist/core/state.js +31 -0
- package/dist/core/store.d.ts +146 -0
- package/dist/core/store.js +229 -0
- package/dist/core/thread.d.ts +41 -0
- package/dist/core/thread.js +13 -0
- package/dist/core/types.d.ts +27 -0
- package/dist/core/types.js +42 -0
- package/dist/core/validate-event.d.ts +19 -0
- package/dist/core/validate-event.js +42 -0
- package/dist/debugger/components/debugger-split.d.ts +23 -0
- package/dist/debugger/components/debugger-split.js +25 -0
- package/dist/debugger/components/event-inspector.d.ts +5 -0
- package/dist/debugger/components/event-inspector.js +38 -0
- package/dist/debugger/components/event-stream.d.ts +13 -0
- package/dist/debugger/components/event-stream.js +119 -0
- package/dist/debugger/components/json-view.d.ts +5 -0
- package/dist/debugger/components/json-view.js +5 -0
- package/dist/debugger/components/run-tree.d.ts +14 -0
- package/dist/debugger/components/run-tree.js +38 -0
- package/dist/debugger/components/status-dot.d.ts +3 -0
- package/dist/debugger/components/status-dot.js +5 -0
- package/dist/debugger/contracts.d.ts +22 -0
- package/dist/debugger/contracts.js +1 -0
- package/dist/debugger/events/catalog.d.ts +6 -0
- package/dist/debugger/events/catalog.js +27 -0
- package/dist/debugger/events/domain-summarize.d.ts +3 -0
- package/dist/debugger/events/domain-summarize.js +107 -0
- package/dist/debugger/events/family.d.ts +8 -0
- package/dist/debugger/events/family.js +44 -0
- package/dist/debugger/events/protocol-summarize.d.ts +3 -0
- package/dist/debugger/events/protocol-summarize.js +70 -0
- package/dist/debugger/index.d.ts +17 -0
- package/dist/debugger/index.js +16 -0
- package/dist/debugger/lib/cn.d.ts +5 -0
- package/dist/debugger/lib/cn.js +17 -0
- package/dist/debugger/lib/event-counts.d.ts +5 -0
- package/dist/debugger/lib/event-counts.js +23 -0
- package/dist/debugger/lib/event-meta.d.ts +8 -0
- package/dist/debugger/lib/event-meta.js +20 -0
- package/dist/debugger/lib/project-run.d.ts +9 -0
- package/dist/debugger/lib/project-run.js +14 -0
- package/dist/debugger/lib/status.d.ts +3 -0
- package/dist/debugger/lib/status.js +31 -0
- package/dist/debugger/lib/visible-events.d.ts +8 -0
- package/dist/debugger/lib/visible-events.js +21 -0
- package/dist/debugger/styles.css +71 -0
- package/dist/debugger/ui/checkbox.d.ts +4 -0
- package/dist/debugger/ui/checkbox.js +9 -0
- package/dist/debugger/ui/input.d.ts +3 -0
- package/dist/debugger/ui/input.js +7 -0
- package/dist/debugger/ui/scroll-area.d.ts +9 -0
- package/dist/debugger/ui/scroll-area.js +10 -0
- package/dist/debugger/ui/tabs.d.ts +7 -0
- package/dist/debugger/ui/tabs.js +17 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/projectors/index-model.d.ts +56 -0
- package/dist/projectors/index-model.js +117 -0
- package/dist/projectors/index.d.ts +4 -0
- package/dist/projectors/index.js +3 -0
- package/dist/projectors/memory.d.ts +5 -0
- package/dist/projectors/memory.js +77 -0
- package/dist/projectors/postgres.d.ts +9 -0
- package/dist/projectors/postgres.js +108 -0
- package/dist/projectors/projector.d.ts +11 -0
- package/dist/projectors/projector.js +1 -0
- package/dist/projectors/schema.d.ts +5 -0
- package/dist/projectors/schema.js +43 -0
- package/dist/projectors/sqlite.d.ts +9 -0
- package/dist/projectors/sqlite.js +103 -0
- package/dist/projectors/with-projectors.d.ts +17 -0
- package/dist/projectors/with-projectors.js +55 -0
- package/dist/react/derived.d.ts +19 -0
- package/dist/react/derived.js +50 -0
- package/dist/react/index.d.ts +6 -0
- package/dist/react/index.js +5 -0
- package/dist/react/materialize.d.ts +3 -0
- package/dist/react/materialize.js +86 -0
- package/dist/react/notify.d.ts +18 -0
- package/dist/react/notify.js +80 -0
- package/dist/react/react.d.ts +61 -0
- package/dist/react/react.js +187 -0
- package/dist/react/register.d.ts +31 -0
- package/dist/react/register.js +1 -0
- package/dist/react/sse.d.ts +1 -0
- package/dist/react/sse.js +1 -0
- package/dist/react/store.d.ts +56 -0
- package/dist/react/store.js +340 -0
- package/dist/react/tables.d.ts +21 -0
- package/dist/react/tables.js +6 -0
- package/dist/runtime/dispatch-effect.d.ts +3 -0
- package/dist/runtime/dispatch-effect.js +113 -0
- package/dist/runtime/events-api.d.ts +4 -0
- package/dist/runtime/events-api.js +84 -0
- package/dist/runtime/index.d.ts +5 -0
- package/dist/runtime/index.js +5 -0
- package/dist/runtime/looms.d.ts +74 -0
- package/dist/runtime/looms.js +109 -0
- package/dist/runtime/runtime-helpers.d.ts +51 -0
- package/dist/runtime/runtime-helpers.js +230 -0
- package/dist/runtime/runtime.d.ts +91 -0
- package/dist/runtime/runtime.js +531 -0
- package/dist/runtime/server.d.ts +17 -0
- package/dist/runtime/server.js +226 -0
- package/dist/runtime/sse.d.ts +15 -0
- package/dist/runtime/sse.js +96 -0
- package/dist/runtime/wake-scheduler.d.ts +8 -0
- package/dist/runtime/wake-scheduler.js +38 -0
- package/dist/s2/config.d.ts +27 -0
- package/dist/s2/config.js +54 -0
- package/dist/s2/index.d.ts +6 -0
- package/dist/s2/index.js +6 -0
- package/dist/s2/lite.d.ts +26 -0
- package/dist/s2/lite.js +150 -0
- package/dist/s2/projector.d.ts +8 -0
- package/dist/s2/projector.js +28 -0
- package/dist/s2/serialize.d.ts +1 -0
- package/dist/s2/serialize.js +1 -0
- package/dist/s2/snapshot-store.d.ts +17 -0
- package/dist/s2/snapshot-store.js +320 -0
- package/dist/s2/store.d.ts +35 -0
- package/dist/s2/store.js +337 -0
- package/dist/testing/index.d.ts +11 -0
- package/dist/testing/index.js +63 -0
- package/dist/workflow/definitions-store.d.ts +11 -0
- package/dist/workflow/definitions-store.js +8 -0
- package/dist/workflow/definitions.d.ts +82 -0
- package/dist/workflow/definitions.js +32 -0
- package/dist/workflow/effects.d.ts +305 -0
- package/dist/workflow/effects.js +208 -0
- package/dist/workflow/events.d.ts +60 -0
- package/dist/workflow/events.js +39 -0
- package/dist/workflow/index.d.ts +8 -0
- package/dist/workflow/index.js +8 -0
- package/dist/workflow/module.d.ts +926 -0
- package/dist/workflow/module.js +19 -0
- package/dist/workflow/projections.d.ts +30 -0
- package/dist/workflow/projections.js +43 -0
- package/dist/workflow/scope.d.ts +61 -0
- package/dist/workflow/scope.js +7 -0
- package/dist/workflow/threads.d.ts +171 -0
- package/dist/workflow/threads.js +287 -0
- package/package.json +183 -0
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
import { Predicate } from 'effect';
|
|
2
|
+
import { createWaitId, emit, invoke, isWithdrawnError, parseEffectId, spawn, wait, } from '@swirls/looms/core';
|
|
3
|
+
import { callLlmEffect, executeToolEffect } from './effects.js';
|
|
4
|
+
import { agentModule } from './scope.js';
|
|
5
|
+
import { AgentStateSchema } from './types.js';
|
|
6
|
+
function inputToLine(input) {
|
|
7
|
+
if (input === null) {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
if (Predicate.isString(input)) {
|
|
11
|
+
return { role: 'user', content: input };
|
|
12
|
+
}
|
|
13
|
+
if (Predicate.isObject(input)) {
|
|
14
|
+
if (Predicate.isString(input.text)) {
|
|
15
|
+
return { role: 'user', content: input.text };
|
|
16
|
+
}
|
|
17
|
+
if (Predicate.isString(input.task)) {
|
|
18
|
+
return { role: 'user', content: input.task };
|
|
19
|
+
}
|
|
20
|
+
if (Predicate.isString(input.topic)) {
|
|
21
|
+
return { role: 'user', content: input.topic };
|
|
22
|
+
}
|
|
23
|
+
if (Predicate.isString(input.prompt)) {
|
|
24
|
+
return { role: 'user', content: input.prompt };
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return { role: 'user', content: JSON.stringify(input) };
|
|
28
|
+
}
|
|
29
|
+
function attachToolCall(lines, toolCall) {
|
|
30
|
+
for (let index = lines.length - 1; index >= 0; index -= 1) {
|
|
31
|
+
const line = lines[index];
|
|
32
|
+
if (line?.role !== 'assistant') {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
const existing = line.toolCalls ?? [];
|
|
36
|
+
if (existing.some((item) => item.id === toolCall.id)) {
|
|
37
|
+
return [...lines];
|
|
38
|
+
}
|
|
39
|
+
const next = [...lines];
|
|
40
|
+
next[index] = { ...line, toolCalls: [...existing, toolCall] };
|
|
41
|
+
return next;
|
|
42
|
+
}
|
|
43
|
+
return [...lines];
|
|
44
|
+
}
|
|
45
|
+
export const agentThread = agentModule.thread({
|
|
46
|
+
kind: 'agent',
|
|
47
|
+
shape: AgentStateSchema,
|
|
48
|
+
initialState: (ctx) => ({
|
|
49
|
+
definitionName: ctx.definitionName,
|
|
50
|
+
lines: [],
|
|
51
|
+
pendingToolCalls: [],
|
|
52
|
+
executingToolCalls: [],
|
|
53
|
+
pendingSpawns: [],
|
|
54
|
+
pendingEffects: [],
|
|
55
|
+
pendingEmits: [],
|
|
56
|
+
needsLlmCall: false,
|
|
57
|
+
turn: 0,
|
|
58
|
+
maxTurns: 20,
|
|
59
|
+
pendingSteer: null,
|
|
60
|
+
input: ctx.input,
|
|
61
|
+
output: null,
|
|
62
|
+
pendingEffectTools: {},
|
|
63
|
+
}),
|
|
64
|
+
step(state, event, ctx) {
|
|
65
|
+
switch (event.type) {
|
|
66
|
+
case 'runtime.thread.started': {
|
|
67
|
+
const payload = event.payload;
|
|
68
|
+
const defName = payload.definitionName || state.definitionName;
|
|
69
|
+
// SAFETY: event payload input is JSON
|
|
70
|
+
const input = payload.input !== undefined ? payload.input : state.input;
|
|
71
|
+
const line = inputToLine(input);
|
|
72
|
+
const lines = line ? [line] : state.lines;
|
|
73
|
+
return {
|
|
74
|
+
...state,
|
|
75
|
+
definitionName: defName,
|
|
76
|
+
input,
|
|
77
|
+
lines,
|
|
78
|
+
needsLlmCall: true,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
case 'agent.turn.started': {
|
|
82
|
+
return {
|
|
83
|
+
...state,
|
|
84
|
+
turn: event.payload.turn,
|
|
85
|
+
needsLlmCall: false,
|
|
86
|
+
pendingSteer: null,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
case 'agent.message.received': {
|
|
90
|
+
const message = event.payload.message;
|
|
91
|
+
const lines = [...state.lines, message];
|
|
92
|
+
const needsLlmCall = state.pendingToolCalls.length === 0;
|
|
93
|
+
return {
|
|
94
|
+
...state,
|
|
95
|
+
lines,
|
|
96
|
+
needsLlmCall,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
case 'agent.message': {
|
|
100
|
+
return { ...state, lines: [...state.lines, event.payload.message] };
|
|
101
|
+
}
|
|
102
|
+
case 'agent.tool_call.requested': {
|
|
103
|
+
const { toolCall } = event.payload;
|
|
104
|
+
return {
|
|
105
|
+
...state,
|
|
106
|
+
lines: attachToolCall(state.lines, toolCall),
|
|
107
|
+
pendingToolCalls: [...state.pendingToolCalls, toolCall],
|
|
108
|
+
executingToolCalls: [...state.executingToolCalls, toolCall],
|
|
109
|
+
needsLlmCall: false,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
case 'agent.tool.result': {
|
|
113
|
+
const { toolCallId, name, result, error } = event.payload;
|
|
114
|
+
const pendingToolCalls = state.pendingToolCalls.filter((item) => item.id !== toolCallId);
|
|
115
|
+
const executingToolCalls = state.executingToolCalls.filter((item) => item.id !== toolCallId);
|
|
116
|
+
const pendingSpawns = state.pendingSpawns.filter((item) => item.toolCallId !== toolCallId);
|
|
117
|
+
const pendingEffects = state.pendingEffects.filter((item) => item.toolCallId !== toolCallId);
|
|
118
|
+
const pendingEmits = state.pendingEmits.filter((item) => item.id !== `res_${toolCallId}`);
|
|
119
|
+
const content = Predicate.isString(error) && error.length > 0 ? error : JSON.stringify(result ?? null);
|
|
120
|
+
const lines = [
|
|
121
|
+
...state.lines,
|
|
122
|
+
{
|
|
123
|
+
role: 'tool',
|
|
124
|
+
content,
|
|
125
|
+
toolCallId,
|
|
126
|
+
name: name ?? undefined,
|
|
127
|
+
},
|
|
128
|
+
];
|
|
129
|
+
const needsLlmCall = pendingToolCalls.length === 0;
|
|
130
|
+
return {
|
|
131
|
+
...state,
|
|
132
|
+
lines,
|
|
133
|
+
pendingToolCalls,
|
|
134
|
+
executingToolCalls,
|
|
135
|
+
pendingSpawns,
|
|
136
|
+
pendingEffects,
|
|
137
|
+
pendingEmits,
|
|
138
|
+
needsLlmCall,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
case 'agent.steered': {
|
|
142
|
+
const message = event.payload.message;
|
|
143
|
+
const interrupt = event.payload.interrupt ?? false;
|
|
144
|
+
if (interrupt) {
|
|
145
|
+
return {
|
|
146
|
+
...state,
|
|
147
|
+
lines: [...state.lines, message],
|
|
148
|
+
pendingSteer: message,
|
|
149
|
+
pendingToolCalls: [],
|
|
150
|
+
executingToolCalls: [],
|
|
151
|
+
pendingSpawns: [],
|
|
152
|
+
pendingEffects: [],
|
|
153
|
+
pendingEmits: [],
|
|
154
|
+
needsLlmCall: true,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
...state,
|
|
159
|
+
lines: [...state.lines, message],
|
|
160
|
+
pendingSteer: message,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
case 'agent.spawn.requested': {
|
|
164
|
+
const { childThreadId, kind, definitionName, toolCallId, input } = event.payload;
|
|
165
|
+
return {
|
|
166
|
+
...state,
|
|
167
|
+
executingToolCalls: state.executingToolCalls.filter((item) => item.id !== toolCallId),
|
|
168
|
+
pendingSpawns: [
|
|
169
|
+
...state.pendingSpawns.filter((item) => item.toolCallId !== toolCallId),
|
|
170
|
+
{
|
|
171
|
+
childThreadId,
|
|
172
|
+
kind,
|
|
173
|
+
definitionName,
|
|
174
|
+
toolCallId,
|
|
175
|
+
// SAFETY: schema-decoded input is JSON
|
|
176
|
+
input: input ?? null,
|
|
177
|
+
},
|
|
178
|
+
],
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
case 'agent.effects.requested': {
|
|
182
|
+
const { toolCallId, effects: rawEffects, waitOn } = event.payload;
|
|
183
|
+
// SAFETY: handler serialized RuntimeEffect values into the event payload.
|
|
184
|
+
const effects = Array.isArray(rawEffects)
|
|
185
|
+
? [...rawEffects]
|
|
186
|
+
: [];
|
|
187
|
+
const taggedEffects = effects.map((effect, idx) => {
|
|
188
|
+
if (!('tag' in effect && Predicate.isString(effect.tag))) {
|
|
189
|
+
return { ...effect, tag: `${toolCallId}_${effect.type}_${idx}` };
|
|
190
|
+
}
|
|
191
|
+
return effect;
|
|
192
|
+
});
|
|
193
|
+
const pending = state.executingToolCalls.find((item) => item.id === toolCallId);
|
|
194
|
+
const effectToolEntries = [
|
|
195
|
+
[String(event.seq), { toolCallId, name: pending?.name ?? 'tool' }],
|
|
196
|
+
[toolCallId, { toolCallId, name: pending?.name ?? 'tool' }],
|
|
197
|
+
];
|
|
198
|
+
for (const eff of taggedEffects) {
|
|
199
|
+
if ('tag' in eff && Predicate.isString(eff.tag)) {
|
|
200
|
+
effectToolEntries.push([eff.tag, { toolCallId, name: pending?.name ?? 'tool' }]);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return {
|
|
204
|
+
...state,
|
|
205
|
+
executingToolCalls: state.executingToolCalls.filter((item) => item.id !== toolCallId),
|
|
206
|
+
pendingEffects: [
|
|
207
|
+
...state.pendingEffects.filter((item) => item.toolCallId !== toolCallId),
|
|
208
|
+
{
|
|
209
|
+
toolCallId,
|
|
210
|
+
effects: taggedEffects,
|
|
211
|
+
waitOn: waitOn
|
|
212
|
+
? {
|
|
213
|
+
type: waitOn.type,
|
|
214
|
+
// SAFETY: schema-decoded match is JSON
|
|
215
|
+
match: waitOn.match ?? undefined,
|
|
216
|
+
}
|
|
217
|
+
: undefined,
|
|
218
|
+
},
|
|
219
|
+
],
|
|
220
|
+
pendingEffectTools: {
|
|
221
|
+
...state.pendingEffectTools,
|
|
222
|
+
...Object.fromEntries(effectToolEntries),
|
|
223
|
+
},
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
case 'runtime.effect.failed': {
|
|
227
|
+
const payload = event.payload;
|
|
228
|
+
const error = payload.error || 'effect failed';
|
|
229
|
+
if (isWithdrawnError(error)) {
|
|
230
|
+
return state;
|
|
231
|
+
}
|
|
232
|
+
const parsed = event.effectId ? parseEffectId(event.effectId) : null;
|
|
233
|
+
const tag = parsed?.tag;
|
|
234
|
+
const causingKey = parsed?.causingSeq !== undefined ? String(parsed.causingSeq) : '';
|
|
235
|
+
const pending = (tag ? state.pendingEffectTools?.[tag] : undefined) ??
|
|
236
|
+
(causingKey ? state.pendingEffectTools?.[causingKey] : undefined) ??
|
|
237
|
+
(() => {
|
|
238
|
+
const match = state.pendingEffects.find((item) => item.effects.some((e) => 'tag' in e && e.tag === tag) ||
|
|
239
|
+
(tag !== undefined && tag.startsWith(item.toolCallId)));
|
|
240
|
+
return match ? { toolCallId: match.toolCallId, name: 'tool' } : undefined;
|
|
241
|
+
})();
|
|
242
|
+
if (!pending) {
|
|
243
|
+
return state;
|
|
244
|
+
}
|
|
245
|
+
const rest = {};
|
|
246
|
+
for (const [key, val] of Object.entries(state.pendingEffectTools ?? {})) {
|
|
247
|
+
if (val.toolCallId !== pending.toolCallId) {
|
|
248
|
+
rest[key] = val;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return {
|
|
252
|
+
...state,
|
|
253
|
+
pendingEffectTools: rest,
|
|
254
|
+
pendingEffects: state.pendingEffects.filter((item) => item.toolCallId !== pending.toolCallId),
|
|
255
|
+
pendingEmits: [
|
|
256
|
+
...state.pendingEmits.filter((item) => item.id !== `res_${pending.toolCallId}`),
|
|
257
|
+
{
|
|
258
|
+
id: `res_${pending.toolCallId}`,
|
|
259
|
+
event: {
|
|
260
|
+
type: 'agent.tool.result',
|
|
261
|
+
payload: {
|
|
262
|
+
turn: state.turn,
|
|
263
|
+
toolCallId: pending.toolCallId,
|
|
264
|
+
name: pending.name,
|
|
265
|
+
result: null,
|
|
266
|
+
error,
|
|
267
|
+
},
|
|
268
|
+
threadId: ctx.threadId,
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
],
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
case 'runtime.wait.satisfied': {
|
|
275
|
+
if (!Predicate.isObject(event.payload)) {
|
|
276
|
+
return state;
|
|
277
|
+
}
|
|
278
|
+
const tag = event.payload.tag;
|
|
279
|
+
if (!Predicate.isObject(tag)) {
|
|
280
|
+
return state;
|
|
281
|
+
}
|
|
282
|
+
const toolCallId = Predicate.isString(tag.toolCallId) ? tag.toolCallId : undefined;
|
|
283
|
+
if (!toolCallId) {
|
|
284
|
+
return state;
|
|
285
|
+
}
|
|
286
|
+
const embedded = event.payload.event;
|
|
287
|
+
const embeddedObj = Predicate.isObject(embedded) && Predicate.isObject(embedded.payload)
|
|
288
|
+
? embedded.payload
|
|
289
|
+
: {};
|
|
290
|
+
const threadId = ctx.threadId;
|
|
291
|
+
const name = Predicate.isString(tag.name) ? tag.name : 'child';
|
|
292
|
+
const error = Predicate.isString(embeddedObj.error) ? embeddedObj.error : null;
|
|
293
|
+
// SAFETY: embedded output/payload is JSON
|
|
294
|
+
const result = embeddedObj.output ??
|
|
295
|
+
(Predicate.isObject(embedded) ? embedded.payload : null) ??
|
|
296
|
+
null;
|
|
297
|
+
return {
|
|
298
|
+
...state,
|
|
299
|
+
pendingSpawns: state.pendingSpawns.filter((item) => item.toolCallId !== toolCallId),
|
|
300
|
+
pendingEffects: state.pendingEffects.filter((item) => item.toolCallId !== toolCallId),
|
|
301
|
+
pendingEmits: [
|
|
302
|
+
...state.pendingEmits.filter((item) => item.id !== `res_${toolCallId}`),
|
|
303
|
+
{
|
|
304
|
+
id: `res_${toolCallId}`,
|
|
305
|
+
event: {
|
|
306
|
+
type: 'agent.tool.result',
|
|
307
|
+
payload: {
|
|
308
|
+
turn: state.turn,
|
|
309
|
+
toolCallId,
|
|
310
|
+
name,
|
|
311
|
+
result,
|
|
312
|
+
error,
|
|
313
|
+
},
|
|
314
|
+
threadId,
|
|
315
|
+
},
|
|
316
|
+
},
|
|
317
|
+
],
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
default:
|
|
321
|
+
return state;
|
|
322
|
+
}
|
|
323
|
+
},
|
|
324
|
+
effects(state, ctx) {
|
|
325
|
+
const effects = [];
|
|
326
|
+
if (state.needsLlmCall) {
|
|
327
|
+
effects.push(invoke(callLlmEffect, {
|
|
328
|
+
turn: state.turn + 1,
|
|
329
|
+
definitionName: state.definitionName,
|
|
330
|
+
messages: state.lines,
|
|
331
|
+
input: state.input,
|
|
332
|
+
}, `llm_turn_${state.turn + 1}`));
|
|
333
|
+
}
|
|
334
|
+
for (const toolCall of state.executingToolCalls) {
|
|
335
|
+
effects.push(invoke(executeToolEffect, {
|
|
336
|
+
turn: state.turn,
|
|
337
|
+
definitionName: state.definitionName,
|
|
338
|
+
toolCall,
|
|
339
|
+
}, `tool_${toolCall.id}`));
|
|
340
|
+
}
|
|
341
|
+
for (const spawnReq of state.pendingSpawns) {
|
|
342
|
+
const waitId = createWaitId(ctx.threadId, `spawn_${spawnReq.childThreadId}`);
|
|
343
|
+
effects.push(spawn({
|
|
344
|
+
childThreadId: spawnReq.childThreadId,
|
|
345
|
+
kind: spawnReq.kind,
|
|
346
|
+
definitionName: spawnReq.definitionName,
|
|
347
|
+
input: spawnReq.input,
|
|
348
|
+
}), wait({
|
|
349
|
+
waitId,
|
|
350
|
+
on: {
|
|
351
|
+
type: ['runtime.thread.completed', 'runtime.thread.failed'],
|
|
352
|
+
match: { threadId: spawnReq.childThreadId },
|
|
353
|
+
},
|
|
354
|
+
tag: { toolCallId: spawnReq.toolCallId, name: spawnReq.definitionName },
|
|
355
|
+
}));
|
|
356
|
+
}
|
|
357
|
+
for (const effectReq of state.pendingEffects) {
|
|
358
|
+
for (const eff of effectReq.effects) {
|
|
359
|
+
effects.push(eff);
|
|
360
|
+
}
|
|
361
|
+
if (effectReq.waitOn) {
|
|
362
|
+
effects.push(wait({
|
|
363
|
+
waitId: createWaitId(ctx.threadId, `effects_${effectReq.toolCallId}`),
|
|
364
|
+
on: { type: effectReq.waitOn.type, match: effectReq.waitOn.match },
|
|
365
|
+
tag: { toolCallId: effectReq.toolCallId },
|
|
366
|
+
}));
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
for (const emitReq of state.pendingEmits) {
|
|
370
|
+
effects.push(emit(emitReq.event));
|
|
371
|
+
}
|
|
372
|
+
return effects;
|
|
373
|
+
},
|
|
374
|
+
});
|
|
375
|
+
export function satisfiedToToolResult(event) {
|
|
376
|
+
if (!Predicate.isObject(event.payload)) {
|
|
377
|
+
return null;
|
|
378
|
+
}
|
|
379
|
+
const tag = event.payload.tag;
|
|
380
|
+
if (!Predicate.isObject(tag)) {
|
|
381
|
+
return null;
|
|
382
|
+
}
|
|
383
|
+
const toolCallId = Predicate.isString(tag.toolCallId) ? tag.toolCallId : undefined;
|
|
384
|
+
if (!toolCallId) {
|
|
385
|
+
return null;
|
|
386
|
+
}
|
|
387
|
+
const embedded = event.payload.event;
|
|
388
|
+
const embeddedPayload = Predicate.isObject(embedded) && Predicate.isObject(embedded.payload) ? embedded.payload : {};
|
|
389
|
+
const threadId = event.threadId ?? null;
|
|
390
|
+
const name = Predicate.isString(tag.name) ? tag.name : 'child';
|
|
391
|
+
const error = Predicate.isString(embeddedPayload.error) ? embeddedPayload.error : null;
|
|
392
|
+
// SAFETY: embedded output/payload is JSON
|
|
393
|
+
const result = embeddedPayload.output ??
|
|
394
|
+
(Predicate.isObject(embedded) ? embedded.payload : null) ??
|
|
395
|
+
null;
|
|
396
|
+
return emit({
|
|
397
|
+
type: 'agent.tool.result',
|
|
398
|
+
payload: {
|
|
399
|
+
turn: 0,
|
|
400
|
+
toolCallId,
|
|
401
|
+
name,
|
|
402
|
+
result,
|
|
403
|
+
error,
|
|
404
|
+
},
|
|
405
|
+
threadId,
|
|
406
|
+
});
|
|
407
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Schema } from 'effect';
|
|
2
|
+
import type { JsonValue, SchemaInput } from '@swirls/looms/core';
|
|
3
|
+
import { type AgentToolEntry, type ToolLike } from './definitions';
|
|
4
|
+
import type { LlmToolSpec } from './llm';
|
|
5
|
+
interface StandardJsonSchemaConverter {
|
|
6
|
+
readonly input: (options: {
|
|
7
|
+
target: string;
|
|
8
|
+
}) => JsonValue;
|
|
9
|
+
}
|
|
10
|
+
interface StandardJsonSchemaHolder {
|
|
11
|
+
readonly '~standard': {
|
|
12
|
+
readonly jsonSchema?: {
|
|
13
|
+
readonly input?: StandardJsonSchemaConverter['input'];
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
type SchemaCandidate = SchemaInput | StandardJsonSchemaHolder | Schema.Codec<JsonValue>;
|
|
18
|
+
export declare function toolJsonSchema(tool: ToolLike): JsonValue;
|
|
19
|
+
export declare function toolSpecs(tools?: ReadonlyArray<AgentToolEntry>): LlmToolSpec[];
|
|
20
|
+
export declare function jsonSchemaFromStandard(schema: SchemaCandidate | null | undefined): JsonValue;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Schema } from 'effect';
|
|
2
|
+
import { normalizeTools } from './definitions.js';
|
|
3
|
+
const EMPTY_OBJECT_SCHEMA = { type: 'object' };
|
|
4
|
+
function isStandardJsonSchemaHolder(value) {
|
|
5
|
+
return '~standard' in value;
|
|
6
|
+
}
|
|
7
|
+
function fromStandardJsonSchema(schema) {
|
|
8
|
+
if (!isStandardJsonSchemaHolder(schema)) {
|
|
9
|
+
return undefined;
|
|
10
|
+
}
|
|
11
|
+
const input = schema['~standard'].jsonSchema?.input;
|
|
12
|
+
if (!input) {
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
return input({ target: 'draft-2020-12' });
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function fromEffectSchema(schema) {
|
|
23
|
+
if (!Schema.isSchema(schema)) {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
// SAFETY: Effect Schema values used as tool input codecs produce Standard JSON Schema.
|
|
28
|
+
const standard = Schema.toStandardJSONSchemaV1(schema);
|
|
29
|
+
return fromStandardJsonSchema(standard);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function fromInputCandidate(input) {
|
|
36
|
+
if (!input) {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
return fromStandardJsonSchema(input) ?? fromEffectSchema(input);
|
|
40
|
+
}
|
|
41
|
+
function toolInputCandidate(tool) {
|
|
42
|
+
switch (tool.kind) {
|
|
43
|
+
case 'function':
|
|
44
|
+
return tool.input;
|
|
45
|
+
case 'thread':
|
|
46
|
+
return tool.input;
|
|
47
|
+
case 'effects':
|
|
48
|
+
return tool.input;
|
|
49
|
+
default: {
|
|
50
|
+
const exhaustiveCheck = tool;
|
|
51
|
+
return exhaustiveCheck;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export function toolJsonSchema(tool) {
|
|
56
|
+
return tool.inputSchema ?? fromInputCandidate(toolInputCandidate(tool)) ?? EMPTY_OBJECT_SCHEMA;
|
|
57
|
+
}
|
|
58
|
+
export function toolSpecs(tools = []) {
|
|
59
|
+
return normalizeTools(tools).map((tool) => ({
|
|
60
|
+
name: tool.name,
|
|
61
|
+
description: tool.description,
|
|
62
|
+
inputJsonSchema: toolJsonSchema(tool),
|
|
63
|
+
}));
|
|
64
|
+
}
|
|
65
|
+
export function jsonSchemaFromStandard(schema) {
|
|
66
|
+
return fromInputCandidate(schema ?? undefined) ?? EMPTY_OBJECT_SCHEMA;
|
|
67
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { Schema } from 'effect';
|
|
2
|
+
import type { EventInput, JsonValue, RuntimeEffect } from '@swirls/looms/core';
|
|
3
|
+
export declare const ToolCallSchema: Schema.Struct<{
|
|
4
|
+
readonly id: Schema.String;
|
|
5
|
+
readonly name: Schema.String;
|
|
6
|
+
readonly arguments: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
7
|
+
}>;
|
|
8
|
+
export type ToolCall = Schema.Schema.Type<typeof ToolCallSchema>;
|
|
9
|
+
export declare const MessageRoleSchema: Schema.Union<readonly [Schema.Literal<"system">, Schema.Literal<"user">, Schema.Literal<"assistant">, Schema.Literal<"tool">]>;
|
|
10
|
+
export type MessageRole = Schema.Schema.Type<typeof MessageRoleSchema>;
|
|
11
|
+
export declare const MessageSchema: Schema.Struct<{
|
|
12
|
+
readonly role: Schema.Union<readonly [Schema.Literal<"system">, Schema.Literal<"user">, Schema.Literal<"assistant">, Schema.Literal<"tool">]>;
|
|
13
|
+
readonly content: Schema.String;
|
|
14
|
+
readonly toolCallId: Schema.optional<Schema.String>;
|
|
15
|
+
readonly name: Schema.optional<Schema.String>;
|
|
16
|
+
readonly toolCalls: Schema.optional<Schema.mutable<Schema.$Array<Schema.Struct<{
|
|
17
|
+
readonly id: Schema.String;
|
|
18
|
+
readonly name: Schema.String;
|
|
19
|
+
readonly arguments: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
20
|
+
}>>>>;
|
|
21
|
+
}>;
|
|
22
|
+
export type Message = Schema.Schema.Type<typeof MessageSchema>;
|
|
23
|
+
export declare const ConversationSchema: Schema.Struct<{
|
|
24
|
+
readonly lines: Schema.mutable<Schema.$Array<Schema.Struct<{
|
|
25
|
+
readonly role: Schema.Union<readonly [Schema.Literal<"system">, Schema.Literal<"user">, Schema.Literal<"assistant">, Schema.Literal<"tool">]>;
|
|
26
|
+
readonly content: Schema.String;
|
|
27
|
+
readonly toolCallId: Schema.optional<Schema.String>;
|
|
28
|
+
readonly name: Schema.optional<Schema.String>;
|
|
29
|
+
readonly toolCalls: Schema.optional<Schema.mutable<Schema.$Array<Schema.Struct<{
|
|
30
|
+
readonly id: Schema.String;
|
|
31
|
+
readonly name: Schema.String;
|
|
32
|
+
readonly arguments: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
33
|
+
}>>>>;
|
|
34
|
+
}>>>;
|
|
35
|
+
}>;
|
|
36
|
+
export type ConversationState = Schema.Schema.Type<typeof ConversationSchema>;
|
|
37
|
+
export declare const TokenUsageSchema: Schema.Struct<{
|
|
38
|
+
readonly input: Schema.Number;
|
|
39
|
+
readonly output: Schema.Number;
|
|
40
|
+
}>;
|
|
41
|
+
export type TokenUsage = Schema.Schema.Type<typeof TokenUsageSchema>;
|
|
42
|
+
export declare const ThreadStartedPayloadSchema: Schema.Struct<{
|
|
43
|
+
readonly definitionName: Schema.optional<Schema.String>;
|
|
44
|
+
readonly input: Schema.optional<Schema.Unknown>;
|
|
45
|
+
}>;
|
|
46
|
+
export interface PendingEffectTool {
|
|
47
|
+
toolCallId: string;
|
|
48
|
+
name: string;
|
|
49
|
+
}
|
|
50
|
+
export interface AgentPendingSpawn {
|
|
51
|
+
readonly childThreadId: string;
|
|
52
|
+
readonly kind: string;
|
|
53
|
+
readonly definitionName: string;
|
|
54
|
+
readonly toolCallId: string;
|
|
55
|
+
readonly input: JsonValue;
|
|
56
|
+
}
|
|
57
|
+
export interface AgentPendingEffects {
|
|
58
|
+
readonly toolCallId: string;
|
|
59
|
+
readonly effects: RuntimeEffect[];
|
|
60
|
+
readonly waitOn?: {
|
|
61
|
+
type: string | readonly string[];
|
|
62
|
+
match?: JsonValue;
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
export interface AgentPendingEmit {
|
|
66
|
+
readonly id: string;
|
|
67
|
+
readonly event: EventInput;
|
|
68
|
+
}
|
|
69
|
+
export interface AgentState {
|
|
70
|
+
definitionName?: string;
|
|
71
|
+
lines: Message[];
|
|
72
|
+
pendingToolCalls: ToolCall[];
|
|
73
|
+
executingToolCalls: ToolCall[];
|
|
74
|
+
pendingSpawns: AgentPendingSpawn[];
|
|
75
|
+
pendingEffects: AgentPendingEffects[];
|
|
76
|
+
pendingEmits: AgentPendingEmit[];
|
|
77
|
+
needsLlmCall: boolean;
|
|
78
|
+
turn: number;
|
|
79
|
+
maxTurns: number;
|
|
80
|
+
pendingSteer: Message | null;
|
|
81
|
+
input: JsonValue;
|
|
82
|
+
output: JsonValue | null;
|
|
83
|
+
pendingEffectTools: {
|
|
84
|
+
[causingSeq: string]: PendingEffectTool;
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
export declare const AgentStateSchema: Schema.Schema<AgentState>;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Schema } from 'effect';
|
|
2
|
+
export const ToolCallSchema = Schema.Struct({
|
|
3
|
+
id: Schema.String,
|
|
4
|
+
name: Schema.String,
|
|
5
|
+
arguments: Schema.Json,
|
|
6
|
+
});
|
|
7
|
+
export const MessageRoleSchema = Schema.Union([
|
|
8
|
+
Schema.Literal('system'),
|
|
9
|
+
Schema.Literal('user'),
|
|
10
|
+
Schema.Literal('assistant'),
|
|
11
|
+
Schema.Literal('tool'),
|
|
12
|
+
]);
|
|
13
|
+
export const MessageSchema = Schema.Struct({
|
|
14
|
+
role: MessageRoleSchema,
|
|
15
|
+
content: Schema.String,
|
|
16
|
+
toolCallId: Schema.optional(Schema.String),
|
|
17
|
+
name: Schema.optional(Schema.String),
|
|
18
|
+
toolCalls: Schema.optional(Schema.mutable(Schema.Array(ToolCallSchema))),
|
|
19
|
+
});
|
|
20
|
+
export const ConversationSchema = Schema.Struct({
|
|
21
|
+
lines: Schema.mutable(Schema.Array(MessageSchema)),
|
|
22
|
+
});
|
|
23
|
+
export const TokenUsageSchema = Schema.Struct({
|
|
24
|
+
input: Schema.Number,
|
|
25
|
+
output: Schema.Number,
|
|
26
|
+
});
|
|
27
|
+
export const ThreadStartedPayloadSchema = Schema.Struct({
|
|
28
|
+
definitionName: Schema.optional(Schema.String),
|
|
29
|
+
input: Schema.optional(Schema.Unknown),
|
|
30
|
+
});
|
|
31
|
+
// SAFETY: AgentState is folded by reducers; Schema.Unknown is a typed placeholder, not a decoder.
|
|
32
|
+
export const AgentStateSchema = Schema.Unknown;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type ModelMessage } from 'ai';
|
|
2
|
+
import type { Message, ToolCall } from '@swirls/looms/agent';
|
|
3
|
+
import type { JsonValue } from '@swirls/looms/core';
|
|
4
|
+
export declare function toModelMessages(messages: Message[]): ModelMessage[];
|
|
5
|
+
export declare function toLoomsToolCalls(calls: ReadonlyArray<{
|
|
6
|
+
toolCallId: string;
|
|
7
|
+
toolName: string;
|
|
8
|
+
input: JsonValue;
|
|
9
|
+
}>): ToolCall[];
|