@yolk-sdk/agent 0.0.1-canary.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/LICENSE +21 -0
- package/README.md +93 -0
- package/dist/client/index.d.mts +3 -0
- package/dist/client/index.mjs +3 -0
- package/dist/client/state.d.mts +99 -0
- package/dist/client/state.d.mts.map +1 -0
- package/dist/client/state.mjs +245 -0
- package/dist/client/state.mjs.map +1 -0
- package/dist/client/transport.d.mts +67 -0
- package/dist/client/transport.d.mts.map +1 -0
- package/dist/client/transport.mjs +219 -0
- package/dist/client/transport.mjs.map +1 -0
- package/dist/index.d.mts +1 -0
- package/dist/index.mjs +1 -0
- package/dist/loop/accumulator.d.mts +11 -0
- package/dist/loop/accumulator.d.mts.map +1 -0
- package/dist/loop/accumulator.mjs +40 -0
- package/dist/loop/accumulator.mjs.map +1 -0
- package/dist/loop/error.d.mts +36 -0
- package/dist/loop/error.d.mts.map +1 -0
- package/dist/loop/error.mjs +84 -0
- package/dist/loop/error.mjs.map +1 -0
- package/dist/loop/index.d.mts +9 -0
- package/dist/loop/index.mjs +9 -0
- package/dist/loop/llm-event.d.mts +44 -0
- package/dist/loop/llm-event.d.mts.map +1 -0
- package/dist/loop/llm-event.mjs +34 -0
- package/dist/loop/llm-event.mjs.map +1 -0
- package/dist/loop/run.d.mts +37 -0
- package/dist/loop/run.d.mts.map +1 -0
- package/dist/loop/run.mjs +624 -0
- package/dist/loop/run.mjs.map +1 -0
- package/dist/loop/services/context-transformer.d.mts +18 -0
- package/dist/loop/services/context-transformer.d.mts.map +1 -0
- package/dist/loop/services/context-transformer.mjs +12 -0
- package/dist/loop/services/context-transformer.mjs.map +1 -0
- package/dist/loop/services/llm-provider.d.mts +20 -0
- package/dist/loop/services/llm-provider.d.mts.map +1 -0
- package/dist/loop/services/llm-provider.mjs +7 -0
- package/dist/loop/services/llm-provider.mjs.map +1 -0
- package/dist/loop/services/loop-config.d.mts +17 -0
- package/dist/loop/services/loop-config.d.mts.map +1 -0
- package/dist/loop/services/loop-config.mjs +15 -0
- package/dist/loop/services/loop-config.mjs.map +1 -0
- package/dist/loop/services/tool-executor.d.mts +12 -0
- package/dist/loop/services/tool-executor.d.mts.map +1 -0
- package/dist/loop/services/tool-executor.mjs +7 -0
- package/dist/loop/services/tool-executor.mjs.map +1 -0
- package/dist/loop/testing/faux-provider.d.mts +31 -0
- package/dist/loop/testing/faux-provider.d.mts.map +1 -0
- package/dist/loop/testing/faux-provider.mjs +47 -0
- package/dist/loop/testing/faux-provider.mjs.map +1 -0
- package/dist/loop/testing/index.d.mts +3 -0
- package/dist/loop/testing/index.mjs +3 -0
- package/dist/loop/testing/test-tool-executor.d.mts +10 -0
- package/dist/loop/testing/test-tool-executor.d.mts.map +1 -0
- package/dist/loop/testing/test-tool-executor.mjs +21 -0
- package/dist/loop/testing/test-tool-executor.mjs.map +1 -0
- package/dist/protocol/capability.d.mts +20 -0
- package/dist/protocol/capability.d.mts.map +1 -0
- package/dist/protocol/capability.mjs +34 -0
- package/dist/protocol/capability.mjs.map +1 -0
- package/dist/protocol/content.d.mts +31 -0
- package/dist/protocol/content.d.mts.map +1 -0
- package/dist/protocol/content.mjs +52 -0
- package/dist/protocol/content.mjs.map +1 -0
- package/dist/protocol/event.d.mts +228 -0
- package/dist/protocol/event.d.mts.map +1 -0
- package/dist/protocol/event.mjs +217 -0
- package/dist/protocol/event.mjs.map +1 -0
- package/dist/protocol/index.d.mts +14 -0
- package/dist/protocol/index.d.mts.map +1 -0
- package/dist/protocol/index.mjs +9 -0
- package/dist/protocol/message.d.mts +53 -0
- package/dist/protocol/message.d.mts.map +1 -0
- package/dist/protocol/message.mjs +49 -0
- package/dist/protocol/message.mjs.map +1 -0
- package/dist/protocol/reasoning.d.mts +8 -0
- package/dist/protocol/reasoning.d.mts.map +1 -0
- package/dist/protocol/reasoning.mjs +13 -0
- package/dist/protocol/reasoning.mjs.map +1 -0
- package/dist/protocol/session.d.mts +39 -0
- package/dist/protocol/session.d.mts.map +1 -0
- package/dist/protocol/session.mjs +38 -0
- package/dist/protocol/session.mjs.map +1 -0
- package/dist/protocol/tool.d.mts +101 -0
- package/dist/protocol/tool.d.mts.map +1 -0
- package/dist/protocol/tool.mjs +102 -0
- package/dist/protocol/tool.mjs.map +1 -0
- package/dist/protocol/usage.d.mts +26 -0
- package/dist/protocol/usage.d.mts.map +1 -0
- package/dist/protocol/usage.mjs +40 -0
- package/dist/protocol/usage.mjs.map +1 -0
- package/dist/runtime/error.d.mts +29 -0
- package/dist/runtime/error.d.mts.map +1 -0
- package/dist/runtime/error.mjs +46 -0
- package/dist/runtime/error.mjs.map +1 -0
- package/dist/runtime/index.d.mts +9 -0
- package/dist/runtime/index.d.mts.map +1 -0
- package/dist/runtime/index.mjs +4 -0
- package/dist/runtime/run-runtime.d.mts +47 -0
- package/dist/runtime/run-runtime.d.mts.map +1 -0
- package/dist/runtime/run-runtime.mjs +112 -0
- package/dist/runtime/run-runtime.mjs.map +1 -0
- package/dist/runtime/session-event-store.d.mts +75 -0
- package/dist/runtime/session-event-store.d.mts.map +1 -0
- package/dist/runtime/session-event-store.mjs +124 -0
- package/dist/runtime/session-event-store.mjs.map +1 -0
- package/dist/tools/index.d.mts +4 -0
- package/dist/tools/index.mjs +4 -0
- package/dist/tools/question.d.mts +21 -0
- package/dist/tools/question.d.mts.map +1 -0
- package/dist/tools/question.mjs +41 -0
- package/dist/tools/question.mjs.map +1 -0
- package/dist/tools/registry.d.mts +61 -0
- package/dist/tools/registry.d.mts.map +1 -0
- package/dist/tools/registry.mjs +113 -0
- package/dist/tools/registry.mjs.map +1 -0
- package/dist/tools/task.d.mts +34 -0
- package/dist/tools/task.d.mts.map +1 -0
- package/dist/tools/task.mjs +81 -0
- package/dist/tools/task.mjs.map +1 -0
- package/package.json +86 -0
- package/src/client/README.md +23 -0
- package/src/client/index.ts +43 -0
- package/src/client/state.ts +380 -0
- package/src/client/transport.ts +517 -0
- package/src/index.ts +2 -0
- package/src/loop/README.md +23 -0
- package/src/loop/accumulator.ts +71 -0
- package/src/loop/error.ts +105 -0
- package/src/loop/index.ts +35 -0
- package/src/loop/llm-event.ts +52 -0
- package/src/loop/run.ts +1237 -0
- package/src/loop/services/context-transformer.ts +24 -0
- package/src/loop/services/llm-provider.ts +20 -0
- package/src/loop/services/loop-config.ts +20 -0
- package/src/loop/services/tool-executor.ts +11 -0
- package/src/loop/testing/faux-provider.ts +94 -0
- package/src/loop/testing/index.ts +3 -0
- package/src/loop/testing/test-tool-executor.ts +28 -0
- package/src/protocol/README.md +24 -0
- package/src/protocol/capability.ts +29 -0
- package/src/protocol/content.ts +76 -0
- package/src/protocol/event.ts +286 -0
- package/src/protocol/index.ts +109 -0
- package/src/protocol/message.ts +86 -0
- package/src/protocol/reasoning.ts +4 -0
- package/src/protocol/session.ts +47 -0
- package/src/protocol/tool.ts +154 -0
- package/src/protocol/usage.ts +48 -0
- package/src/runtime/README.md +44 -0
- package/src/runtime/error.ts +70 -0
- package/src/runtime/index.ts +43 -0
- package/src/runtime/run-runtime.ts +307 -0
- package/src/runtime/session-event-store.ts +254 -0
- package/src/tools/README.md +22 -0
- package/src/tools/index.ts +29 -0
- package/src/tools/question.ts +58 -0
- package/src/tools/registry.ts +228 -0
- package/src/tools/task.ts +132 -0
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import { Effect, Ref, Stream } from 'effect'
|
|
2
|
+
import type {
|
|
3
|
+
AgentEvent,
|
|
4
|
+
AgentMessage,
|
|
5
|
+
AgentModelCapabilities,
|
|
6
|
+
AgentReasoningEffort,
|
|
7
|
+
HitlResponse,
|
|
8
|
+
ToolDef
|
|
9
|
+
} from '@yolk-sdk/agent/protocol'
|
|
10
|
+
import {
|
|
11
|
+
run,
|
|
12
|
+
type AgentLoopError,
|
|
13
|
+
type ContextTransformer,
|
|
14
|
+
type LLMProvider,
|
|
15
|
+
type LoopConfig,
|
|
16
|
+
type ToolExecutor
|
|
17
|
+
} from '@yolk-sdk/agent/loop'
|
|
18
|
+
import { runtimeErrorToAgentError } from './error.ts'
|
|
19
|
+
import {
|
|
20
|
+
HitlResponseAppended,
|
|
21
|
+
InputAppended,
|
|
22
|
+
replayRuntimeHitlResponses,
|
|
23
|
+
replayRuntimeSessionEvents,
|
|
24
|
+
RunAwaitingInput,
|
|
25
|
+
RunCompleted,
|
|
26
|
+
RunFailed,
|
|
27
|
+
RunStarted,
|
|
28
|
+
SessionEventStore,
|
|
29
|
+
type RuntimeSessionEventLog,
|
|
30
|
+
type SessionEventStoreApi,
|
|
31
|
+
type SessionRevision
|
|
32
|
+
} from './session-event-store.ts'
|
|
33
|
+
import type { RuntimeError } from './error.ts'
|
|
34
|
+
|
|
35
|
+
export type RuntimeTranscript = readonly [AgentMessage, ...Array<AgentMessage>]
|
|
36
|
+
|
|
37
|
+
export type RuntimeConfig = {
|
|
38
|
+
readonly systemPrompt: string
|
|
39
|
+
readonly tools: ReadonlyArray<ToolDef>
|
|
40
|
+
readonly hitlResponses?: ReadonlyArray<HitlResponse>
|
|
41
|
+
readonly model: string
|
|
42
|
+
readonly reasoningEffort?: AgentReasoningEffort
|
|
43
|
+
readonly capabilities?: AgentModelCapabilities
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export type TranscriptRuntimeRequest = {
|
|
47
|
+
readonly _tag: 'Transcript'
|
|
48
|
+
readonly sessionId: string
|
|
49
|
+
readonly messages: RuntimeTranscript
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export type AppendInputRuntimeRequest = {
|
|
53
|
+
readonly _tag: 'AppendInput'
|
|
54
|
+
readonly sessionId: string
|
|
55
|
+
readonly input: AgentMessage
|
|
56
|
+
readonly runId: string
|
|
57
|
+
readonly expectedRevision?: SessionRevision
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export type AppendHitlResponseRuntimeRequest = {
|
|
61
|
+
readonly _tag: 'AppendHitlResponse'
|
|
62
|
+
readonly sessionId: string
|
|
63
|
+
readonly response: HitlResponse
|
|
64
|
+
readonly runId: string
|
|
65
|
+
readonly expectedRevision?: SessionRevision
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export type RuntimeRequest =
|
|
69
|
+
| TranscriptRuntimeRequest
|
|
70
|
+
| AppendInputRuntimeRequest
|
|
71
|
+
| AppendHitlResponseRuntimeRequest
|
|
72
|
+
|
|
73
|
+
type LoopRequirements = ContextTransformer | LLMProvider | LoopConfig | ToolExecutor
|
|
74
|
+
type AppendRuntimeRequirements = LoopRequirements | SessionEventStore
|
|
75
|
+
type RuntimeRequirements = LoopRequirements | AppendRuntimeRequirements
|
|
76
|
+
type RuntimeErrorUnion = RuntimeError | AgentLoopError
|
|
77
|
+
|
|
78
|
+
const extractNewMessages = (event: AgentEvent) => (event._tag === 'AgentEnd' ? event.messages : [])
|
|
79
|
+
|
|
80
|
+
const runtimeRunConfig = (config: RuntimeConfig, messages: ReadonlyArray<AgentMessage>) => ({
|
|
81
|
+
messages,
|
|
82
|
+
systemPrompt: config.systemPrompt,
|
|
83
|
+
tools: config.tools,
|
|
84
|
+
hitlResponses: config.hitlResponses,
|
|
85
|
+
reasoningEffort: config.reasoningEffort,
|
|
86
|
+
capabilities: config.capabilities,
|
|
87
|
+
model: config.model
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
const runAndCollectMessages = (
|
|
91
|
+
config: RuntimeConfig,
|
|
92
|
+
messages: ReadonlyArray<AgentMessage>,
|
|
93
|
+
createdMessages: Ref.Ref<ReadonlyArray<AgentMessage>>
|
|
94
|
+
) =>
|
|
95
|
+
run(runtimeRunConfig(config, messages)).pipe(
|
|
96
|
+
Stream.tap(event => {
|
|
97
|
+
const newMessages = extractNewMessages(event)
|
|
98
|
+
|
|
99
|
+
return newMessages.length === 0
|
|
100
|
+
? Effect.void
|
|
101
|
+
: Ref.update(createdMessages, messages => [...messages, ...newMessages])
|
|
102
|
+
})
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
const makeTranscriptRuntimeStream = (request: TranscriptRuntimeRequest, config: RuntimeConfig) =>
|
|
106
|
+
Stream.unwrap(
|
|
107
|
+
Ref.make<ReadonlyArray<AgentMessage>>([]).pipe(
|
|
108
|
+
Effect.map(createdMessages =>
|
|
109
|
+
runAndCollectMessages(config, request.messages, createdMessages)
|
|
110
|
+
)
|
|
111
|
+
)
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
const emptyRuntimeSessionEventLog = (sessionId: string): RuntimeSessionEventLog => ({
|
|
115
|
+
sessionId,
|
|
116
|
+
revision: 0,
|
|
117
|
+
events: []
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
const loadAppendLogOrEmpty = (store: SessionEventStoreApi, sessionId: string) =>
|
|
121
|
+
store
|
|
122
|
+
.load(sessionId)
|
|
123
|
+
.pipe(
|
|
124
|
+
Effect.catchTag('SessionNotFoundError', () =>
|
|
125
|
+
Effect.succeed(emptyRuntimeSessionEventLog(sessionId))
|
|
126
|
+
)
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
const appendRunFailed = (
|
|
130
|
+
store: SessionEventStoreApi,
|
|
131
|
+
request: AppendInputRuntimeRequest | AppendHitlResponseRuntimeRequest,
|
|
132
|
+
revision: SessionRevision,
|
|
133
|
+
error: AgentLoopError
|
|
134
|
+
) =>
|
|
135
|
+
store.append({
|
|
136
|
+
sessionId: request.sessionId,
|
|
137
|
+
expectedRevision: revision,
|
|
138
|
+
events: [RunFailed.make({ runId: request.runId, error: runtimeErrorToAgentError(error) })]
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
const makeAppendInputRuntimeStream = (request: AppendInputRuntimeRequest, config: RuntimeConfig) =>
|
|
142
|
+
Stream.unwrap(
|
|
143
|
+
Effect.gen(function* () {
|
|
144
|
+
const store = yield* SessionEventStore
|
|
145
|
+
const initialLog = yield* loadAppendLogOrEmpty(store, request.sessionId)
|
|
146
|
+
const startedLog = yield* store.append({
|
|
147
|
+
sessionId: request.sessionId,
|
|
148
|
+
expectedRevision: request.expectedRevision ?? initialLog.revision,
|
|
149
|
+
events: [
|
|
150
|
+
InputAppended.make({ message: request.input }),
|
|
151
|
+
RunStarted.make({ runId: request.runId })
|
|
152
|
+
]
|
|
153
|
+
})
|
|
154
|
+
const messages = [...replayRuntimeSessionEvents(initialLog.events), request.input]
|
|
155
|
+
return run(runtimeRunConfig(config, messages)).pipe(
|
|
156
|
+
Stream.tap(event =>
|
|
157
|
+
event._tag === 'AgentEnd'
|
|
158
|
+
? store
|
|
159
|
+
.append({
|
|
160
|
+
sessionId: request.sessionId,
|
|
161
|
+
expectedRevision: startedLog.revision,
|
|
162
|
+
events: [RunCompleted.make({ runId: request.runId, messages: event.messages })]
|
|
163
|
+
})
|
|
164
|
+
.pipe(Effect.asVoid)
|
|
165
|
+
: event._tag === 'AgentAwaitingInput'
|
|
166
|
+
? store
|
|
167
|
+
.append({
|
|
168
|
+
sessionId: request.sessionId,
|
|
169
|
+
expectedRevision: startedLog.revision,
|
|
170
|
+
events: [
|
|
171
|
+
RunAwaitingInput.make({
|
|
172
|
+
runId: request.runId,
|
|
173
|
+
requests: event.requests,
|
|
174
|
+
messages: event.messages
|
|
175
|
+
})
|
|
176
|
+
]
|
|
177
|
+
})
|
|
178
|
+
.pipe(Effect.asVoid)
|
|
179
|
+
: Effect.void
|
|
180
|
+
),
|
|
181
|
+
Stream.catchTags({
|
|
182
|
+
AbortError: error =>
|
|
183
|
+
Stream.fromEffect(appendRunFailed(store, request, startedLog.revision, error)).pipe(
|
|
184
|
+
Stream.flatMap(() => Stream.fail(error))
|
|
185
|
+
),
|
|
186
|
+
ContextTransformError: error =>
|
|
187
|
+
Stream.fromEffect(appendRunFailed(store, request, startedLog.revision, error)).pipe(
|
|
188
|
+
Stream.flatMap(() => Stream.fail(error))
|
|
189
|
+
),
|
|
190
|
+
FauxExhaustedError: error =>
|
|
191
|
+
Stream.fromEffect(appendRunFailed(store, request, startedLog.revision, error)).pipe(
|
|
192
|
+
Stream.flatMap(() => Stream.fail(error))
|
|
193
|
+
),
|
|
194
|
+
LLMError: error =>
|
|
195
|
+
Stream.fromEffect(appendRunFailed(store, request, startedLog.revision, error)).pipe(
|
|
196
|
+
Stream.flatMap(() => Stream.fail(error))
|
|
197
|
+
),
|
|
198
|
+
ToolError: error =>
|
|
199
|
+
Stream.fromEffect(appendRunFailed(store, request, startedLog.revision, error)).pipe(
|
|
200
|
+
Stream.flatMap(() => Stream.fail(error))
|
|
201
|
+
)
|
|
202
|
+
})
|
|
203
|
+
)
|
|
204
|
+
})
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
const makeAppendHitlResponseRuntimeStream = (
|
|
208
|
+
request: AppendHitlResponseRuntimeRequest,
|
|
209
|
+
config: RuntimeConfig
|
|
210
|
+
) =>
|
|
211
|
+
Stream.unwrap(
|
|
212
|
+
Effect.gen(function* () {
|
|
213
|
+
const store = yield* SessionEventStore
|
|
214
|
+
const initialLog = yield* loadAppendLogOrEmpty(store, request.sessionId)
|
|
215
|
+
const startedLog = yield* store.append({
|
|
216
|
+
sessionId: request.sessionId,
|
|
217
|
+
expectedRevision: request.expectedRevision ?? initialLog.revision,
|
|
218
|
+
events: [
|
|
219
|
+
HitlResponseAppended.make({ response: request.response }),
|
|
220
|
+
RunStarted.make({ runId: request.runId })
|
|
221
|
+
]
|
|
222
|
+
})
|
|
223
|
+
const messages = replayRuntimeSessionEvents(initialLog.events)
|
|
224
|
+
const priorResponses = replayRuntimeHitlResponses(initialLog.events)
|
|
225
|
+
const hitlResponses = [...priorResponses, request.response]
|
|
226
|
+
|
|
227
|
+
return run(runtimeRunConfig({ ...config, hitlResponses }, messages)).pipe(
|
|
228
|
+
Stream.tap(event =>
|
|
229
|
+
event._tag === 'AgentEnd'
|
|
230
|
+
? store
|
|
231
|
+
.append({
|
|
232
|
+
sessionId: request.sessionId,
|
|
233
|
+
expectedRevision: startedLog.revision,
|
|
234
|
+
events: [RunCompleted.make({ runId: request.runId, messages: event.messages })]
|
|
235
|
+
})
|
|
236
|
+
.pipe(Effect.asVoid)
|
|
237
|
+
: event._tag === 'AgentAwaitingInput'
|
|
238
|
+
? store
|
|
239
|
+
.append({
|
|
240
|
+
sessionId: request.sessionId,
|
|
241
|
+
expectedRevision: startedLog.revision,
|
|
242
|
+
events: [
|
|
243
|
+
RunAwaitingInput.make({
|
|
244
|
+
runId: request.runId,
|
|
245
|
+
requests: event.requests,
|
|
246
|
+
messages: event.messages
|
|
247
|
+
})
|
|
248
|
+
]
|
|
249
|
+
})
|
|
250
|
+
.pipe(Effect.asVoid)
|
|
251
|
+
: Effect.void
|
|
252
|
+
),
|
|
253
|
+
Stream.catchTags({
|
|
254
|
+
AbortError: error =>
|
|
255
|
+
Stream.fromEffect(appendRunFailed(store, request, startedLog.revision, error)).pipe(
|
|
256
|
+
Stream.flatMap(() => Stream.fail(error))
|
|
257
|
+
),
|
|
258
|
+
ContextTransformError: error =>
|
|
259
|
+
Stream.fromEffect(appendRunFailed(store, request, startedLog.revision, error)).pipe(
|
|
260
|
+
Stream.flatMap(() => Stream.fail(error))
|
|
261
|
+
),
|
|
262
|
+
FauxExhaustedError: error =>
|
|
263
|
+
Stream.fromEffect(appendRunFailed(store, request, startedLog.revision, error)).pipe(
|
|
264
|
+
Stream.flatMap(() => Stream.fail(error))
|
|
265
|
+
),
|
|
266
|
+
LLMError: error =>
|
|
267
|
+
Stream.fromEffect(appendRunFailed(store, request, startedLog.revision, error)).pipe(
|
|
268
|
+
Stream.flatMap(() => Stream.fail(error))
|
|
269
|
+
),
|
|
270
|
+
ToolError: error =>
|
|
271
|
+
Stream.fromEffect(appendRunFailed(store, request, startedLog.revision, error)).pipe(
|
|
272
|
+
Stream.flatMap(() => Stream.fail(error))
|
|
273
|
+
)
|
|
274
|
+
})
|
|
275
|
+
)
|
|
276
|
+
})
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
export function runRuntime(
|
|
280
|
+
request: TranscriptRuntimeRequest,
|
|
281
|
+
config: RuntimeConfig
|
|
282
|
+
): Stream.Stream<AgentEvent, AgentLoopError, LoopRequirements>
|
|
283
|
+
export function runRuntime(
|
|
284
|
+
request: AppendInputRuntimeRequest,
|
|
285
|
+
config: RuntimeConfig
|
|
286
|
+
): Stream.Stream<AgentEvent, RuntimeErrorUnion, AppendRuntimeRequirements>
|
|
287
|
+
export function runRuntime(
|
|
288
|
+
request: AppendHitlResponseRuntimeRequest,
|
|
289
|
+
config: RuntimeConfig
|
|
290
|
+
): Stream.Stream<AgentEvent, RuntimeErrorUnion, AppendRuntimeRequirements>
|
|
291
|
+
export function runRuntime(
|
|
292
|
+
request: RuntimeRequest,
|
|
293
|
+
config: RuntimeConfig
|
|
294
|
+
): Stream.Stream<AgentEvent, RuntimeErrorUnion, RuntimeRequirements>
|
|
295
|
+
export function runRuntime(
|
|
296
|
+
request: RuntimeRequest,
|
|
297
|
+
config: RuntimeConfig
|
|
298
|
+
): Stream.Stream<AgentEvent, RuntimeErrorUnion, RuntimeRequirements> {
|
|
299
|
+
switch (request._tag) {
|
|
300
|
+
case 'Transcript':
|
|
301
|
+
return makeTranscriptRuntimeStream(request, config)
|
|
302
|
+
case 'AppendInput':
|
|
303
|
+
return makeAppendInputRuntimeStream(request, config)
|
|
304
|
+
case 'AppendHitlResponse':
|
|
305
|
+
return makeAppendHitlResponseRuntimeStream(request, config)
|
|
306
|
+
}
|
|
307
|
+
}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { Context, Effect, Layer, Option, Ref } from 'effect'
|
|
2
|
+
import * as Schema from 'effect/Schema'
|
|
3
|
+
import { AgentError, AgentMessage, HitlRequest, HitlResponse } from '@yolk-sdk/agent/protocol'
|
|
4
|
+
import { SessionConflictError, SessionNotFoundError } from './error.ts'
|
|
5
|
+
import type { SessionLoadError, SessionSaveError } from './error.ts'
|
|
6
|
+
|
|
7
|
+
export type SessionRevision = number
|
|
8
|
+
|
|
9
|
+
export class InputAppended extends Schema.TaggedClass<InputAppended>()('InputAppended', {
|
|
10
|
+
message: AgentMessage
|
|
11
|
+
}) {}
|
|
12
|
+
|
|
13
|
+
export class HitlResponseAppended extends Schema.TaggedClass<HitlResponseAppended>()(
|
|
14
|
+
'HitlResponseAppended',
|
|
15
|
+
{
|
|
16
|
+
response: HitlResponse
|
|
17
|
+
}
|
|
18
|
+
) {}
|
|
19
|
+
|
|
20
|
+
export class RunStarted extends Schema.TaggedClass<RunStarted>()('RunStarted', {
|
|
21
|
+
runId: Schema.String
|
|
22
|
+
}) {}
|
|
23
|
+
|
|
24
|
+
export class RunCompleted extends Schema.TaggedClass<RunCompleted>()('RunCompleted', {
|
|
25
|
+
runId: Schema.String,
|
|
26
|
+
messages: Schema.Array(AgentMessage)
|
|
27
|
+
}) {}
|
|
28
|
+
|
|
29
|
+
export class RunAwaitingInput extends Schema.TaggedClass<RunAwaitingInput>()('RunAwaitingInput', {
|
|
30
|
+
runId: Schema.String,
|
|
31
|
+
requests: Schema.NonEmptyArray(HitlRequest),
|
|
32
|
+
messages: Schema.Array(AgentMessage)
|
|
33
|
+
}) {}
|
|
34
|
+
|
|
35
|
+
export class RunFailed extends Schema.TaggedClass<RunFailed>()('RunFailed', {
|
|
36
|
+
runId: Schema.String,
|
|
37
|
+
error: AgentError
|
|
38
|
+
}) {}
|
|
39
|
+
|
|
40
|
+
export class RunInterrupted extends Schema.TaggedClass<RunInterrupted>()('RunInterrupted', {
|
|
41
|
+
runId: Schema.String
|
|
42
|
+
}) {}
|
|
43
|
+
|
|
44
|
+
export const RuntimeSessionEvent = Schema.Union([
|
|
45
|
+
InputAppended,
|
|
46
|
+
HitlResponseAppended,
|
|
47
|
+
RunStarted,
|
|
48
|
+
RunCompleted,
|
|
49
|
+
RunAwaitingInput,
|
|
50
|
+
RunFailed,
|
|
51
|
+
RunInterrupted
|
|
52
|
+
])
|
|
53
|
+
export type RuntimeSessionEvent = typeof RuntimeSessionEvent.Type
|
|
54
|
+
|
|
55
|
+
export type StoredRuntimeSessionEvent = {
|
|
56
|
+
readonly id: string
|
|
57
|
+
readonly sessionId: string
|
|
58
|
+
readonly revision: SessionRevision
|
|
59
|
+
readonly event: RuntimeSessionEvent
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export type RuntimeSessionEventLog = {
|
|
63
|
+
readonly sessionId: string
|
|
64
|
+
readonly revision: SessionRevision
|
|
65
|
+
readonly events: ReadonlyArray<StoredRuntimeSessionEvent>
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export type IncompleteRuntimeRun = {
|
|
69
|
+
readonly runId: string
|
|
70
|
+
readonly startedRevision: SessionRevision
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export type AppendRuntimeSessionEventsInput = {
|
|
74
|
+
readonly sessionId: string
|
|
75
|
+
readonly expectedRevision?: SessionRevision
|
|
76
|
+
readonly events: ReadonlyArray<RuntimeSessionEvent>
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export type SessionEventStoreApi = {
|
|
80
|
+
readonly load: (
|
|
81
|
+
sessionId: string
|
|
82
|
+
) => Effect.Effect<RuntimeSessionEventLog, SessionNotFoundError | SessionLoadError>
|
|
83
|
+
readonly append: (
|
|
84
|
+
input: AppendRuntimeSessionEventsInput
|
|
85
|
+
) => Effect.Effect<RuntimeSessionEventLog, SessionSaveError | SessionConflictError>
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export class SessionEventStore extends Context.Service<SessionEventStore, SessionEventStoreApi>()(
|
|
89
|
+
'@yolk-sdk/agent/runtime/SessionEventStore'
|
|
90
|
+
) {}
|
|
91
|
+
|
|
92
|
+
export const replayRuntimeSessionEvents = (
|
|
93
|
+
events: ReadonlyArray<StoredRuntimeSessionEvent>
|
|
94
|
+
): ReadonlyArray<AgentMessage> =>
|
|
95
|
+
events.flatMap(stored => {
|
|
96
|
+
switch (stored.event._tag) {
|
|
97
|
+
case 'InputAppended':
|
|
98
|
+
return [stored.event.message]
|
|
99
|
+
case 'RunCompleted':
|
|
100
|
+
case 'RunAwaitingInput':
|
|
101
|
+
return stored.event.messages
|
|
102
|
+
case 'HitlResponseAppended':
|
|
103
|
+
case 'RunFailed':
|
|
104
|
+
case 'RunInterrupted':
|
|
105
|
+
case 'RunStarted':
|
|
106
|
+
return []
|
|
107
|
+
}
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
export const replayRuntimeHitlResponses = (
|
|
111
|
+
events: ReadonlyArray<StoredRuntimeSessionEvent>
|
|
112
|
+
): ReadonlyArray<HitlResponse> =>
|
|
113
|
+
events.flatMap(stored =>
|
|
114
|
+
stored.event._tag === 'HitlResponseAppended' ? [stored.event.response] : []
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
type IncompleteRunSearch =
|
|
118
|
+
| {
|
|
119
|
+
readonly _tag: 'Found'
|
|
120
|
+
readonly run: IncompleteRuntimeRun
|
|
121
|
+
}
|
|
122
|
+
| {
|
|
123
|
+
readonly _tag: 'Searching'
|
|
124
|
+
readonly terminalRunIds: ReadonlySet<string>
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const terminalRunId = (event: RuntimeSessionEvent): Option.Option<string> => {
|
|
128
|
+
switch (event._tag) {
|
|
129
|
+
case 'RunCompleted':
|
|
130
|
+
case 'RunAwaitingInput':
|
|
131
|
+
case 'RunFailed':
|
|
132
|
+
case 'RunInterrupted':
|
|
133
|
+
return Option.some(event.runId)
|
|
134
|
+
case 'HitlResponseAppended':
|
|
135
|
+
case 'InputAppended':
|
|
136
|
+
case 'RunStarted':
|
|
137
|
+
return Option.none()
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export const latestIncompleteRuntimeRun = (
|
|
142
|
+
events: ReadonlyArray<StoredRuntimeSessionEvent>
|
|
143
|
+
): Option.Option<IncompleteRuntimeRun> => {
|
|
144
|
+
const search = events.reduceRight<IncompleteRunSearch>(
|
|
145
|
+
(state, stored) => {
|
|
146
|
+
if (state._tag === 'Found') {
|
|
147
|
+
return state
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const terminal = terminalRunId(stored.event)
|
|
151
|
+
|
|
152
|
+
if (Option.isSome(terminal)) {
|
|
153
|
+
return {
|
|
154
|
+
_tag: 'Searching',
|
|
155
|
+
terminalRunIds: new Set([...state.terminalRunIds, terminal.value])
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (stored.event._tag === 'RunStarted' && !state.terminalRunIds.has(stored.event.runId)) {
|
|
160
|
+
return {
|
|
161
|
+
_tag: 'Found',
|
|
162
|
+
run: {
|
|
163
|
+
runId: stored.event.runId,
|
|
164
|
+
startedRevision: stored.revision
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return state
|
|
170
|
+
},
|
|
171
|
+
{ _tag: 'Searching', terminalRunIds: new Set() }
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
return search._tag === 'Found' ? Option.some(search.run) : Option.none()
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const emptyLog = (sessionId: string): RuntimeSessionEventLog => ({
|
|
178
|
+
sessionId,
|
|
179
|
+
revision: 0,
|
|
180
|
+
events: []
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
const makeStoredRuntimeSessionEvents = (
|
|
184
|
+
sessionId: string,
|
|
185
|
+
currentRevision: SessionRevision,
|
|
186
|
+
events: ReadonlyArray<RuntimeSessionEvent>
|
|
187
|
+
): ReadonlyArray<StoredRuntimeSessionEvent> =>
|
|
188
|
+
events.map((event, index) => {
|
|
189
|
+
const revision = currentRevision + index + 1
|
|
190
|
+
|
|
191
|
+
return {
|
|
192
|
+
id: `${sessionId}:${revision}`,
|
|
193
|
+
sessionId,
|
|
194
|
+
revision,
|
|
195
|
+
event
|
|
196
|
+
}
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
export const appendRuntimeSessionEventsToLog = (
|
|
200
|
+
current: RuntimeSessionEventLog,
|
|
201
|
+
input: AppendRuntimeSessionEventsInput
|
|
202
|
+
): RuntimeSessionEventLog => {
|
|
203
|
+
const stored = makeStoredRuntimeSessionEvents(input.sessionId, current.revision, input.events)
|
|
204
|
+
const last = stored.at(-1)
|
|
205
|
+
|
|
206
|
+
return {
|
|
207
|
+
sessionId: input.sessionId,
|
|
208
|
+
revision: last?.revision ?? current.revision,
|
|
209
|
+
events: [...current.events, ...stored]
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export const makeInMemorySessionEventStoreLayer = (
|
|
214
|
+
initial: ReadonlyArray<RuntimeSessionEventLog> = []
|
|
215
|
+
) =>
|
|
216
|
+
Layer.effect(
|
|
217
|
+
SessionEventStore,
|
|
218
|
+
Effect.gen(function* () {
|
|
219
|
+
const logs = yield* Ref.make(new Map(initial.map(log => [log.sessionId, log])))
|
|
220
|
+
|
|
221
|
+
return SessionEventStore.of({
|
|
222
|
+
load: sessionId =>
|
|
223
|
+
Effect.gen(function* () {
|
|
224
|
+
const current = yield* Ref.get(logs)
|
|
225
|
+
|
|
226
|
+
return yield* Effect.fromNullishOr(current.get(sessionId)).pipe(
|
|
227
|
+
Effect.mapError(() => new SessionNotFoundError({ sessionId }))
|
|
228
|
+
)
|
|
229
|
+
}),
|
|
230
|
+
append: input =>
|
|
231
|
+
Effect.gen(function* () {
|
|
232
|
+
const current = yield* Ref.get(logs)
|
|
233
|
+
const currentLog = current.get(input.sessionId) ?? emptyLog(input.sessionId)
|
|
234
|
+
|
|
235
|
+
if (
|
|
236
|
+
input.expectedRevision !== undefined &&
|
|
237
|
+
input.expectedRevision !== currentLog.revision
|
|
238
|
+
) {
|
|
239
|
+
return yield* Effect.fail(
|
|
240
|
+
new SessionConflictError({
|
|
241
|
+
sessionId: input.sessionId,
|
|
242
|
+
message: `Session revision conflict: expected ${input.expectedRevision}, got ${currentLog.revision}`
|
|
243
|
+
})
|
|
244
|
+
)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const nextLog = appendRuntimeSessionEventsToLog(currentLog, input)
|
|
248
|
+
yield* Ref.set(logs, new Map([...current, [input.sessionId, nextLog]]))
|
|
249
|
+
|
|
250
|
+
return nextLog
|
|
251
|
+
})
|
|
252
|
+
})
|
|
253
|
+
})
|
|
254
|
+
)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# @yolk-sdk/agent/tools
|
|
2
|
+
|
|
3
|
+
Generic host tool registration and resolution.
|
|
4
|
+
|
|
5
|
+
## What it provides
|
|
6
|
+
|
|
7
|
+
- `ToolModule<Context>` and `ToolRegistration<Context>` types.
|
|
8
|
+
- Tool resolution from host modules and context.
|
|
9
|
+
- Duplicate tool name validation.
|
|
10
|
+
- Adapter from resolved tools to `@yolk-sdk/agent/loop` `ToolExecutor`.
|
|
11
|
+
- Package-owned `task` and `question` tool contracts.
|
|
12
|
+
|
|
13
|
+
## Use it when
|
|
14
|
+
|
|
15
|
+
- A host app wants declarative tool modules with generic context.
|
|
16
|
+
- You need to filter/resolve tools before running the agent loop.
|
|
17
|
+
|
|
18
|
+
## Boundaries
|
|
19
|
+
|
|
20
|
+
- No app tool catalogs.
|
|
21
|
+
- No provider SDKs.
|
|
22
|
+
- Tool access/approval is metadata; host apps enforce product policy.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export { EmptyToolParams, makeTool, makeToolExecutorLayer, resolveTools, ToolAccess, ToolRegistryError } from './registry.ts'
|
|
2
|
+
export {
|
|
3
|
+
makeQuestionToolDef,
|
|
4
|
+
makeQuestionToolModule,
|
|
5
|
+
makeQuestionToolRegistration,
|
|
6
|
+
questionToolName
|
|
7
|
+
} from './question.ts'
|
|
8
|
+
export {
|
|
9
|
+
formatTaskResult,
|
|
10
|
+
makeTaskToolDef,
|
|
11
|
+
makeTaskToolModule,
|
|
12
|
+
makeTaskToolRegistration,
|
|
13
|
+
taskToolName
|
|
14
|
+
} from './task.ts'
|
|
15
|
+
export type {
|
|
16
|
+
ResolvedToolSet,
|
|
17
|
+
SchemaToolExecutionInput,
|
|
18
|
+
ToolExecutionInput,
|
|
19
|
+
ToolMetadata,
|
|
20
|
+
ToolModule,
|
|
21
|
+
ToolRegistration
|
|
22
|
+
} from './registry.ts'
|
|
23
|
+
export type { QuestionExecutionInput, QuestionToolOptions } from './question.ts'
|
|
24
|
+
export type {
|
|
25
|
+
TaskExecutionInput,
|
|
26
|
+
TaskSubagentDefinition,
|
|
27
|
+
TaskToolOptions,
|
|
28
|
+
TaskToolParams
|
|
29
|
+
} from './task.ts'
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Effect } from 'effect'
|
|
2
|
+
import { ToolError } from '@yolk-sdk/agent/loop'
|
|
3
|
+
import { QuestionToolParams, ToolResult, type ToolCall } from '@yolk-sdk/agent/protocol'
|
|
4
|
+
import { makeTool, type ToolModule, type ToolRegistration } from './registry.ts'
|
|
5
|
+
|
|
6
|
+
export const questionToolName = 'question'
|
|
7
|
+
|
|
8
|
+
export type QuestionExecutionInput<Context> = {
|
|
9
|
+
readonly call: ToolCall
|
|
10
|
+
readonly context: Context
|
|
11
|
+
readonly params: QuestionToolParams
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type QuestionToolOptions<Context> = {
|
|
15
|
+
readonly execute: (input: QuestionExecutionInput<Context>) => Effect.Effect<ToolResult, ToolError>
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const questionToolError = (message: string, cause: ToolError['cause']) =>
|
|
19
|
+
new ToolError({
|
|
20
|
+
tool: questionToolName,
|
|
21
|
+
message,
|
|
22
|
+
cause
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
const questionToolDescription = [
|
|
26
|
+
'Ask the user one or more structured questions and wait for their response.',
|
|
27
|
+
'Use this when you need explicit user input before continuing.',
|
|
28
|
+
'Each question has a stable id, prompt, optional options, and optional custom-answer support.',
|
|
29
|
+
'Yolk returns user answers as structured tool output.'
|
|
30
|
+
].join('\n\n')
|
|
31
|
+
|
|
32
|
+
export const makeQuestionToolRegistration = <Context>(
|
|
33
|
+
options: QuestionToolOptions<Context>
|
|
34
|
+
): ToolRegistration<Context> =>
|
|
35
|
+
makeTool({
|
|
36
|
+
name: questionToolName,
|
|
37
|
+
description: questionToolDescription,
|
|
38
|
+
parameters: QuestionToolParams,
|
|
39
|
+
access: 'read',
|
|
40
|
+
invalidParamsMessage: error =>
|
|
41
|
+
`Invalid question arguments: ${error instanceof Error ? error.message : String(error)}`,
|
|
42
|
+
execute: ({ call, context, params }) =>
|
|
43
|
+
call.name === questionToolName
|
|
44
|
+
? options.execute({ call, context, params })
|
|
45
|
+
: Effect.fail(questionToolError(`Tool is not configured: ${call.name}`, 'not_found'))
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
export const makeQuestionToolDef = () =>
|
|
49
|
+
makeQuestionToolRegistration({
|
|
50
|
+
execute: ({ call }) => Effect.succeed(ToolResult.make({ toolCallId: call.id, content: '' }))
|
|
51
|
+
}).def
|
|
52
|
+
|
|
53
|
+
export const makeQuestionToolModule = <Context>(
|
|
54
|
+
options: QuestionToolOptions<Context>
|
|
55
|
+
): ToolModule<Context> => ({
|
|
56
|
+
id: 'question',
|
|
57
|
+
tools: [makeQuestionToolRegistration(options)]
|
|
58
|
+
})
|