@kuralle-agents/core 0.12.0 → 0.14.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/README.md +89 -4
- package/dist/ai-sdk/uiMessageStream.d.ts +2 -2
- package/dist/ai-sdk/uiMessageStream.js +42 -29
- package/dist/capabilities/LivePromptAssembler.d.ts +1 -1
- package/dist/capabilities/LivePromptAssembler.js +2 -1
- package/dist/capabilities/index.d.ts +1 -1
- package/dist/eval/EvalRunner.js +4 -4
- package/dist/eval/simulation.js +4 -3
- package/dist/events/TurnHandle.d.ts +3 -3
- package/dist/flow/collectDigression.d.ts +5 -2
- package/dist/flow/collectDigression.js +48 -10
- package/dist/flow/collectUntilComplete.js +13 -5
- package/dist/flow/degrade.js +8 -4
- package/dist/flow/emitInteractive.d.ts +2 -2
- package/dist/flow/emitInteractive.js +6 -3
- package/dist/flow/extraction.d.ts +3 -2
- package/dist/flow/extraction.js +14 -4
- package/dist/flow/reduceTransition.d.ts +2 -2
- package/dist/flow/reduceTransition.js +7 -3
- package/dist/flow/runFlow.js +97 -17
- package/dist/foundation/AgentStateController.d.ts +1 -1
- package/dist/foundation/ToolExecutor.d.ts +2 -2
- package/dist/foundation/index.d.ts +0 -11
- package/dist/foundation/index.js +0 -5
- package/dist/index.d.ts +15 -15
- package/dist/index.js +9 -6
- package/dist/outcomes/streamPart.d.ts +2 -2
- package/dist/outcomes/streamPart.js +2 -3
- package/dist/prompts/PromptBuilder.d.ts +3 -3
- package/dist/prompts/PromptBuilder.js +13 -6
- package/dist/prompts/index.d.ts +1 -1
- package/dist/prompts/types.d.ts +9 -2
- package/dist/runtime/InMemoryRetrievalCache.d.ts +25 -0
- package/dist/runtime/InMemoryRetrievalCache.js +59 -0
- package/dist/runtime/KnowledgeProvider.d.ts +3 -2
- package/dist/runtime/KnowledgeProvider.js +60 -31
- package/dist/runtime/Runtime.d.ts +47 -1
- package/dist/runtime/Runtime.js +273 -36
- package/dist/runtime/TokenAccumulator.d.ts +7 -0
- package/dist/runtime/TokenAccumulator.js +7 -0
- package/dist/runtime/TraceRecorder.d.ts +51 -0
- package/dist/runtime/TraceRecorder.js +326 -0
- package/dist/runtime/buildAgentToolSurface.js +1 -1
- package/dist/runtime/channels/TextDriver.js +57 -31
- package/dist/runtime/channels/executeModelTool.d.ts +14 -1
- package/dist/runtime/channels/executeModelTool.js +132 -3
- package/dist/runtime/channels/extractionTurn.js +1 -1
- package/dist/runtime/channels/index.d.ts +0 -3
- package/dist/runtime/channels/index.js +0 -6
- package/dist/runtime/channels/inputBuffer.d.ts +1 -0
- package/dist/runtime/channels/inputBuffer.js +9 -0
- package/dist/runtime/channels/streaming/hostControlSpeak.js +5 -1
- package/dist/runtime/channels/streaming/speakGated.js +24 -12
- package/dist/runtime/citations/index.d.ts +3 -3
- package/dist/runtime/closeRun.js +14 -10
- package/dist/runtime/compaction.d.ts +2 -0
- package/dist/runtime/compaction.js +3 -2
- package/dist/runtime/controlFlowSignal.d.ts +24 -0
- package/dist/runtime/controlFlowSignal.js +29 -0
- package/dist/runtime/ctx.d.ts +3 -4
- package/dist/runtime/ctx.js +147 -60
- package/dist/runtime/durable/RunStore.d.ts +16 -0
- package/dist/runtime/durable/RunStore.js +7 -1
- package/dist/runtime/durable/SessionRunStore.d.ts +7 -2
- package/dist/runtime/durable/SessionRunStore.js +136 -34
- package/dist/runtime/durable/idempotency.d.ts +1 -0
- package/dist/runtime/durable/idempotency.js +3 -0
- package/dist/runtime/durable/replay.js +7 -2
- package/dist/runtime/durable/types.d.ts +11 -0
- package/dist/runtime/goals.d.ts +18 -0
- package/dist/runtime/goals.js +158 -0
- package/dist/runtime/grounding/gather.d.ts +1 -1
- package/dist/runtime/grounding/inMemoryKnowledge.d.ts +1 -1
- package/dist/runtime/grounding/knowledge.d.ts +1 -1
- package/dist/runtime/grounding/knowledge.js +1 -1
- package/dist/runtime/handoffContinuation.d.ts +20 -0
- package/dist/runtime/handoffContinuation.js +30 -0
- package/dist/runtime/handoffOscillation.d.ts +6 -0
- package/dist/runtime/handoffOscillation.js +17 -0
- package/dist/runtime/hostLoop.js +37 -6
- package/dist/runtime/index.d.ts +4 -3
- package/dist/runtime/index.js +2 -1
- package/dist/runtime/openRun.d.ts +2 -0
- package/dist/runtime/openRun.js +46 -17
- package/dist/runtime/outcomeMarking.d.ts +2 -2
- package/dist/runtime/outcomeMarking.js +2 -1
- package/dist/runtime/policies/agentTurn.d.ts +1 -1
- package/dist/runtime/policies/limits.d.ts +1 -0
- package/dist/runtime/policies/limits.js +3 -0
- package/dist/runtime/runHookSafely.d.ts +3 -0
- package/dist/runtime/runHookSafely.js +19 -0
- package/dist/runtime/select.d.ts +5 -1
- package/dist/runtime/select.js +30 -1
- package/dist/runtime/turnTokenUsage.d.ts +28 -0
- package/dist/runtime/turnTokenUsage.js +70 -0
- package/dist/scheduler/index.d.ts +2 -2
- package/dist/scheduler/index.js +1 -1
- package/dist/services/TracingService.js +2 -3
- package/dist/session/SessionStore.d.ts +6 -0
- package/dist/session/SessionStore.js +12 -1
- package/dist/session/stores/MemoryStore.d.ts +1 -1
- package/dist/session/stores/MemoryStore.js +16 -2
- package/dist/session/testing.d.ts +6 -1
- package/dist/session/testing.js +34 -0
- package/dist/session/utils.d.ts +3 -0
- package/dist/session/utils.js +23 -0
- package/dist/skills/collectSkills.d.ts +11 -3
- package/dist/skills/collectSkills.js +51 -8
- package/dist/skills/compositeSkillStore.d.ts +26 -0
- package/dist/skills/compositeSkillStore.js +54 -0
- package/dist/skills/defineSkill.d.ts +21 -0
- package/dist/skills/defineSkill.js +26 -0
- package/dist/skills/fsSkillStore.d.ts +3 -0
- package/dist/skills/fsSkillStore.js +79 -0
- package/dist/skills/index.d.ts +4 -0
- package/dist/skills/index.js +4 -0
- package/dist/skills/parseSkillFrontmatter.d.ts +14 -0
- package/dist/skills/parseSkillFrontmatter.js +183 -0
- package/dist/skills/wireAgentSkills.d.ts +2 -1
- package/dist/skills/wireAgentSkills.js +2 -2
- package/dist/testing/mocks.d.ts +3 -3
- package/dist/testing/mocks.js +7 -3
- package/dist/tools/controlResults.d.ts +14 -0
- package/dist/tools/controlResults.js +15 -0
- package/dist/tools/effect/ToolExecutor.d.ts +6 -0
- package/dist/tools/effect/ToolExecutor.js +75 -36
- package/dist/tools/effect/defineTool.d.ts +11 -0
- package/dist/tools/effect/defineTool.js +3 -0
- package/dist/tools/effect/errors.d.ts +2 -5
- package/dist/tools/effect/errors.js +2 -5
- package/dist/tools/index.d.ts +0 -1
- package/dist/tools/index.js +0 -1
- package/dist/tracing/MemoryTraceStore.d.ts +16 -0
- package/dist/tracing/MemoryTraceStore.js +56 -0
- package/dist/tracing/OtelTraceSink.d.ts +127 -0
- package/dist/tracing/OtelTraceSink.js +101 -0
- package/dist/tracing/TraceStore.d.ts +19 -0
- package/dist/tracing/TraceStore.js +32 -0
- package/dist/tracing/index.d.ts +3 -0
- package/dist/tracing/index.js +3 -0
- package/dist/tracing/testing.d.ts +3 -0
- package/dist/tracing/testing.js +45 -0
- package/dist/types/agentConfig.d.ts +2 -1
- package/dist/types/channel.d.ts +14 -1
- package/dist/types/effectTool.d.ts +6 -0
- package/dist/types/flow.d.ts +1 -1
- package/dist/types/guardrails.d.ts +5 -0
- package/dist/types/hooks.d.ts +2 -2
- package/dist/types/index.d.ts +25 -15
- package/dist/types/index.js +8 -12
- package/dist/types/knowledge.d.ts +213 -0
- package/dist/types/processors.d.ts +0 -58
- package/dist/types/run-context.d.ts +16 -7
- package/dist/types/runtime.d.ts +3 -74
- package/dist/types/session.d.ts +11 -1
- package/dist/types/skills.d.ts +15 -1
- package/dist/types/stream.d.ts +138 -68
- package/dist/types/stream.js +34 -1
- package/dist/types/trace.d.ts +52 -0
- package/guides/AGENTS.md +3 -3
- package/guides/EXAMPLE_VERIFICATION.md +4 -4
- package/guides/FLOWS.md +1 -1
- package/guides/GETTING_STARTED.md +1 -1
- package/guides/RUNTIME.md +20 -15
- package/guides/TOOLS.md +1 -1
- package/package.json +13 -14
- package/dist/foundation/AgentDefinition.d.ts +0 -17
- package/dist/foundation/ConversationEventLog.d.ts +0 -71
- package/dist/foundation/ConversationState.d.ts +0 -33
- package/dist/foundation/ConversationState.js +0 -1
- package/dist/foundation/DefaultConversationEventLog.d.ts +0 -27
- package/dist/foundation/DefaultConversationEventLog.js +0 -205
- package/dist/foundation/DefaultConversationState.d.ts +0 -36
- package/dist/foundation/DefaultConversationState.js +0 -103
- package/dist/foundation/DefaultToolExecutor.d.ts +0 -57
- package/dist/foundation/DefaultToolExecutor.js +0 -132
- package/dist/foundation/createFoundation.d.ts +0 -32
- package/dist/foundation/createFoundation.js +0 -33
- package/dist/hooks/HookRunner.d.ts +0 -37
- package/dist/hooks/HookRunner.js +0 -111
- package/dist/hooks/builtin/logging.d.ts +0 -5
- package/dist/hooks/builtin/logging.js +0 -98
- package/dist/hooks/builtin/metrics.d.ts +0 -7
- package/dist/hooks/builtin/metrics.js +0 -60
- package/dist/hooks/builtin/observability.d.ts +0 -20
- package/dist/hooks/builtin/observability.js +0 -572
- package/dist/hooks/helpers.d.ts +0 -22
- package/dist/hooks/helpers.js +0 -183
- package/dist/hooks/index.d.ts +0 -8
- package/dist/hooks/index.js +0 -6
- package/dist/realtime/RealtimeAudioClient.d.ts +0 -166
- package/dist/realtime/RealtimeAudioClient.js +0 -14
- package/dist/realtime/RealtimeSessionHandle.d.ts +0 -6
- package/dist/realtime/RealtimeSessionHandle.js +0 -1
- package/dist/realtime/index.d.ts +0 -2
- package/dist/realtime/index.js +0 -1
- package/dist/runtime/channels/VoiceDriver.d.ts +0 -31
- package/dist/runtime/channels/VoiceDriver.js +0 -321
- package/dist/runtime/channels/voiceTools.d.ts +0 -9
- package/dist/runtime/channels/voiceTools.js +0 -51
- package/dist/tools/errorHandling.d.ts +0 -35
- package/dist/tools/errorHandling.js +0 -145
- package/dist/types/voice.d.ts +0 -517
- package/dist/types/voice.js +0 -14
- package/dist/utils/isRecord.d.ts +0 -1
- package/dist/utils/isRecord.js +0 -3
- package/dist/utils/messageNormalization.d.ts +0 -2
- package/dist/utils/messageNormalization.js +0 -137
- /package/dist/{foundation/AgentDefinition.js → types/knowledge.js} +0 -0
- /package/dist/{foundation/ConversationEventLog.js → types/trace.js} +0 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @kuralle-agents/core
|
|
2
2
|
|
|
3
|
-
The runtime and primitives for building conversational AI agents
|
|
3
|
+
The runtime and primitives for building conversational AI agents with structured flows, routing, and durable tool execution.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -54,12 +54,97 @@ const runtime = createRuntime({ agents: [agent], defaultAgentId: 'support' });
|
|
|
54
54
|
|
|
55
55
|
const handle = runtime.run({ input: 'Hello', sessionId: 'demo' });
|
|
56
56
|
for await (const part of handle.events) { // events is a property, not a method
|
|
57
|
-
if (part.type === 'text-delta') process.stdout.write(part.delta);
|
|
58
|
-
if (part.type === 'done') console.log('\nSession:', part.sessionId);
|
|
57
|
+
if (part.type === 'text-delta') process.stdout.write(part.payload.delta);
|
|
58
|
+
if (part.type === 'done') console.log('\nSession:', part.payload.sessionId);
|
|
59
59
|
}
|
|
60
60
|
await handle; // resolves to TurnResult once the stream is consumed
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
+
## Single-run trace / `runOnce`
|
|
64
|
+
|
|
65
|
+
Use `runOnce` when an evaluator needs one complete, JSON-serializable turn instead
|
|
66
|
+
of a live stream. The trace includes the answer, tool roll-up, and nested spans.
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
const trace = await runtime.runOnce({
|
|
70
|
+
sessionId: 'grounding-eval-42',
|
|
71
|
+
input: 'What was my last invoice total?',
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const judgeContext = {
|
|
75
|
+
answer: trace.answer,
|
|
76
|
+
evidence: trace.toolResults.map(({ name, result }) => ({ name, result })),
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const verdict = await groundingJudge(judgeContext);
|
|
80
|
+
console.log(verdict, trace.usedTool, trace.traceId);
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
`runOnce` executes exactly one normal runtime turn and observes its existing event
|
|
84
|
+
stream. Runtime tracing also captures normal `run()` calls; trace persistence is
|
|
85
|
+
physically separate from the session store and durable effect journal.
|
|
86
|
+
|
|
87
|
+
## Observability
|
|
88
|
+
|
|
89
|
+
Tracing is enabled by default with an in-process `MemoryTraceStore`. Supply a native
|
|
90
|
+
store for durable reads and add any number of export sinks independently of
|
|
91
|
+
`sessionStore`:
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
import { createRuntime } from '@kuralle-agents/core';
|
|
95
|
+
import { RedisTraceStore } from '@kuralle-agents/redis-store';
|
|
96
|
+
|
|
97
|
+
const traceStore = new RedisTraceStore({
|
|
98
|
+
client,
|
|
99
|
+
traceTtlSeconds: 7 * 24 * 60 * 60,
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const runtime = createRuntime({
|
|
103
|
+
agents: [agent],
|
|
104
|
+
defaultAgentId: 'support',
|
|
105
|
+
tracing: {
|
|
106
|
+
store: traceStore,
|
|
107
|
+
sampling: 0.1,
|
|
108
|
+
sinks: [externalSink],
|
|
109
|
+
redact: (span) => ({
|
|
110
|
+
...span,
|
|
111
|
+
attributes: { ...span.attributes, input: undefined, output: undefined },
|
|
112
|
+
}),
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
const latest = (await runtime.listTraces('session-42'))[0];
|
|
117
|
+
const trace = latest ? await runtime.getTrace(latest.traceId) : null;
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Sink failures are swallowed and never change the run result. Redaction is off by
|
|
121
|
+
default for useful local debugging; use the hook above before persisting sensitive
|
|
122
|
+
tool inputs or outputs. Set `tracing.enabled: false` to disable capture.
|
|
123
|
+
|
|
124
|
+
### OTLP and Langfuse
|
|
125
|
+
|
|
126
|
+
The built-in exporter uses HTTP/JSON over `fetch`, so it runs in Bun, Node.js, and
|
|
127
|
+
Cloudflare Workers without a Node OpenTelemetry SDK:
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
import { langfuseSink, otelSink } from '@kuralle-agents/core';
|
|
131
|
+
|
|
132
|
+
const runtime = createRuntime({
|
|
133
|
+
agents: [agent],
|
|
134
|
+
defaultAgentId: 'support',
|
|
135
|
+
tracing: {
|
|
136
|
+
store: traceStore,
|
|
137
|
+
sinks: [
|
|
138
|
+
otelSink({ endpoint: 'https://collector.example.com', headers: { Authorization: 'Bearer token' } }),
|
|
139
|
+
langfuseSink({ publicKey: env.LANGFUSE_PUBLIC_KEY, secretKey: env.LANGFUSE_SECRET_KEY }),
|
|
140
|
+
],
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Endpoints may include `/v1/traces`; otherwise the sink appends it. For self-hosted
|
|
146
|
+
Langfuse, pass its OTLP base URL as `endpoint`.
|
|
147
|
+
|
|
63
148
|
## Flows
|
|
64
149
|
|
|
65
150
|
A flow is a node graph that enforces a multi-step procedure without embedding a 600-line SOP in a system prompt.
|
|
@@ -145,7 +230,7 @@ return handle.toUIMessageStreamResponse({ sessionId: 'demo' });
|
|
|
145
230
|
|
|
146
231
|
Kuralle orchestration events (flow telemetry, safety blocks, interactive choices) arrive as typed `data-kuralle-*` parts. Import `KuralleUIMessage` and `KuralleDataParts` for compile-time-safe `message.parts` and `useChat({ onData })` handlers.
|
|
147
232
|
|
|
148
|
-
For non-UI consumers (curl, custom transports), use `handle.toResponseStream('sse')` to emit raw `
|
|
233
|
+
For non-UI consumers (curl, custom transports), use `handle.toResponseStream('sse')` to emit raw `StreamPart` JSON-SSE. Or use `@kuralle-agents/hono-server` — `POST /api/chat/sse` defaults to native `UIMessageStream`; append `?format=raw` for the legacy wire.
|
|
149
234
|
|
|
150
235
|
## Related
|
|
151
236
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type UIMessage } from 'ai';
|
|
2
2
|
import type { ConversationOutcome } from '../outcomes/types.js';
|
|
3
3
|
import type { ChoiceOption } from '../types/selection.js';
|
|
4
|
-
import type {
|
|
4
|
+
import type { StreamPart } from '../types/stream.js';
|
|
5
5
|
export type KuralleMetadata = {
|
|
6
6
|
sessionId?: string;
|
|
7
7
|
};
|
|
@@ -46,6 +46,6 @@ export type KuralleDataParts = {
|
|
|
46
46
|
};
|
|
47
47
|
};
|
|
48
48
|
export type KuralleUIMessage = UIMessage<KuralleMetadata, KuralleDataParts>;
|
|
49
|
-
export declare function harnessToUIMessageStream(source: AsyncIterable<
|
|
49
|
+
export declare function harnessToUIMessageStream(source: AsyncIterable<StreamPart>, opts?: {
|
|
50
50
|
sessionId?: string;
|
|
51
51
|
}): ReadableStream;
|
|
@@ -2,64 +2,64 @@ import { createUIMessageStream, generateId, } from 'ai';
|
|
|
2
2
|
function writeHarnessPart(part, writer) {
|
|
3
3
|
switch (part.type) {
|
|
4
4
|
case 'text-start':
|
|
5
|
-
writer.write({ type: 'text-start', id: part.id });
|
|
5
|
+
writer.write({ type: 'text-start', id: part.payload.id });
|
|
6
6
|
break;
|
|
7
7
|
case 'text-delta':
|
|
8
|
-
writer.write({ type: 'text-delta', id: part.id, delta: part.delta });
|
|
8
|
+
writer.write({ type: 'text-delta', id: part.payload.id, delta: part.payload.delta });
|
|
9
9
|
break;
|
|
10
10
|
case 'text-end':
|
|
11
|
-
writer.write({ type: 'text-end', id: part.id });
|
|
11
|
+
writer.write({ type: 'text-end', id: part.payload.id });
|
|
12
12
|
break;
|
|
13
13
|
case 'text-cancel':
|
|
14
|
-
writer.write({ type: 'text-end', id: part.id });
|
|
14
|
+
writer.write({ type: 'text-end', id: part.payload.id });
|
|
15
15
|
break;
|
|
16
16
|
case 'tool-call':
|
|
17
17
|
writer.write({
|
|
18
18
|
type: 'tool-input-available',
|
|
19
|
-
toolCallId: part.toolCallId ?? generateId(),
|
|
20
|
-
toolName: part.toolName,
|
|
21
|
-
input: part.args,
|
|
19
|
+
toolCallId: part.payload.toolCallId ?? generateId(),
|
|
20
|
+
toolName: part.payload.toolName,
|
|
21
|
+
input: part.payload.args,
|
|
22
22
|
});
|
|
23
23
|
break;
|
|
24
24
|
case 'tool-result':
|
|
25
25
|
writer.write({
|
|
26
26
|
type: 'tool-output-available',
|
|
27
|
-
toolCallId: part.toolCallId ?? 'unknown',
|
|
28
|
-
output: part.result,
|
|
27
|
+
toolCallId: part.payload.toolCallId ?? 'unknown',
|
|
28
|
+
output: part.payload.result,
|
|
29
29
|
});
|
|
30
30
|
break;
|
|
31
31
|
case 'node-enter':
|
|
32
32
|
writer.write({
|
|
33
33
|
type: 'data-kuralle-node',
|
|
34
|
-
data: { event: 'enter', node: part.nodeName },
|
|
34
|
+
data: { event: 'enter', node: part.payload.nodeName },
|
|
35
35
|
transient: true,
|
|
36
36
|
});
|
|
37
37
|
break;
|
|
38
38
|
case 'node-exit':
|
|
39
39
|
writer.write({
|
|
40
40
|
type: 'data-kuralle-node',
|
|
41
|
-
data: { event: 'exit', node: part.nodeName },
|
|
41
|
+
data: { event: 'exit', node: part.payload.nodeName },
|
|
42
42
|
transient: true,
|
|
43
43
|
});
|
|
44
44
|
break;
|
|
45
45
|
case 'flow-enter':
|
|
46
46
|
writer.write({
|
|
47
47
|
type: 'data-kuralle-flow',
|
|
48
|
-
data: { event: 'enter', flow: part.flow },
|
|
48
|
+
data: { event: 'enter', flow: part.payload.flow },
|
|
49
49
|
transient: true,
|
|
50
50
|
});
|
|
51
51
|
break;
|
|
52
52
|
case 'flow-end':
|
|
53
53
|
writer.write({
|
|
54
54
|
type: 'data-kuralle-flow',
|
|
55
|
-
data: { event: 'end', flow: part.flow, reason: part.reason },
|
|
55
|
+
data: { event: 'end', flow: part.payload.flow, reason: part.payload.reason },
|
|
56
56
|
transient: true,
|
|
57
57
|
});
|
|
58
58
|
break;
|
|
59
59
|
case 'flow-transition':
|
|
60
60
|
writer.write({
|
|
61
61
|
type: 'data-kuralle-flow',
|
|
62
|
-
data: { event: 'transition', from: part.from, to: part.to },
|
|
62
|
+
data: { event: 'transition', from: part.payload.from, to: part.payload.to },
|
|
63
63
|
transient: true,
|
|
64
64
|
});
|
|
65
65
|
break;
|
|
@@ -67,14 +67,18 @@ function writeHarnessPart(part, writer) {
|
|
|
67
67
|
writer.write({
|
|
68
68
|
type: 'data-kuralle-handoff',
|
|
69
69
|
id: generateId(),
|
|
70
|
-
data: { targetAgent: part.targetAgent, reason: part.reason },
|
|
70
|
+
data: { targetAgent: part.payload.targetAgent, reason: part.payload.reason },
|
|
71
71
|
});
|
|
72
72
|
break;
|
|
73
73
|
case 'interactive':
|
|
74
74
|
writer.write({
|
|
75
75
|
type: 'data-kuralle-interactive',
|
|
76
|
-
id: part.nodeId,
|
|
77
|
-
data: {
|
|
76
|
+
id: part.payload.nodeId,
|
|
77
|
+
data: {
|
|
78
|
+
nodeId: part.payload.nodeId,
|
|
79
|
+
prompt: part.payload.prompt,
|
|
80
|
+
options: part.payload.options,
|
|
81
|
+
},
|
|
78
82
|
});
|
|
79
83
|
break;
|
|
80
84
|
case 'safety-blocked':
|
|
@@ -83,9 +87,9 @@ function writeHarnessPart(part, writer) {
|
|
|
83
87
|
id: generateId(),
|
|
84
88
|
data: {
|
|
85
89
|
kind: 'safety-blocked',
|
|
86
|
-
moderator: part.moderator,
|
|
87
|
-
rationale: part.rationale,
|
|
88
|
-
userFacingMessage: part.userFacingMessage,
|
|
90
|
+
moderator: part.payload.moderator,
|
|
91
|
+
rationale: part.payload.rationale,
|
|
92
|
+
userFacingMessage: part.payload.userFacingMessage,
|
|
89
93
|
},
|
|
90
94
|
});
|
|
91
95
|
break;
|
|
@@ -95,8 +99,8 @@ function writeHarnessPart(part, writer) {
|
|
|
95
99
|
id: generateId(),
|
|
96
100
|
data: {
|
|
97
101
|
kind: 'pipeline-validation-block',
|
|
98
|
-
rationale: part.rationale,
|
|
99
|
-
userFacingMessage: part.userFacingMessage,
|
|
102
|
+
rationale: part.payload.rationale,
|
|
103
|
+
userFacingMessage: part.payload.userFacingMessage,
|
|
100
104
|
},
|
|
101
105
|
});
|
|
102
106
|
break;
|
|
@@ -104,35 +108,44 @@ function writeHarnessPart(part, writer) {
|
|
|
104
108
|
writer.write({
|
|
105
109
|
type: 'data-kuralle-outcome',
|
|
106
110
|
id: generateId(),
|
|
107
|
-
data: { outcome: part.outcome },
|
|
111
|
+
data: { outcome: part.payload.outcome },
|
|
108
112
|
});
|
|
109
113
|
break;
|
|
110
114
|
case 'interrupted':
|
|
111
115
|
writer.write({
|
|
112
116
|
type: 'data-kuralle-control',
|
|
113
|
-
data: { event: 'interrupted', reason: part.reason },
|
|
117
|
+
data: { event: 'interrupted', reason: part.payload.reason },
|
|
114
118
|
transient: true,
|
|
115
119
|
});
|
|
116
120
|
break;
|
|
117
121
|
case 'paused':
|
|
118
122
|
writer.write({
|
|
119
123
|
type: 'data-kuralle-control',
|
|
120
|
-
data: { event: 'paused', waitingFor: part.waitingFor },
|
|
124
|
+
data: { event: 'paused', waitingFor: part.payload.waitingFor },
|
|
121
125
|
transient: true,
|
|
122
126
|
});
|
|
123
127
|
break;
|
|
124
128
|
case 'custom':
|
|
125
129
|
writer.write({
|
|
126
130
|
type: 'data-kuralle-custom',
|
|
127
|
-
data: { name: part.name, data: part.data },
|
|
131
|
+
data: { name: part.payload.name, data: part.payload.data },
|
|
128
132
|
transient: true,
|
|
129
133
|
});
|
|
130
134
|
break;
|
|
131
135
|
case 'error':
|
|
132
|
-
throw new Error(part.error);
|
|
136
|
+
throw new Error(part.payload.error);
|
|
133
137
|
case 'done':
|
|
134
138
|
case 'turn-end':
|
|
135
139
|
break;
|
|
140
|
+
default: {
|
|
141
|
+
// Client-channel exhaustiveness guard. Internal-channel parts are
|
|
142
|
+
// deliberately unmapped here — they never reach a UI client. But a new
|
|
143
|
+
// `client` variant added without a case above would be silently dropped
|
|
144
|
+
// on the ADR-0005 default output path, so pin the residual to internal:
|
|
145
|
+
// adding a client variant without a case fails this assignment.
|
|
146
|
+
const _internalOnly = part;
|
|
147
|
+
void _internalOnly;
|
|
148
|
+
}
|
|
136
149
|
}
|
|
137
150
|
}
|
|
138
151
|
export function harnessToUIMessageStream(source, opts) {
|
|
@@ -146,8 +159,8 @@ export function harnessToUIMessageStream(source, opts) {
|
|
|
146
159
|
});
|
|
147
160
|
}
|
|
148
161
|
for await (const part of source) {
|
|
149
|
-
if (part.type === 'done' && part.sessionId) {
|
|
150
|
-
doneSessionId = doneSessionId ?? part.sessionId;
|
|
162
|
+
if (part.type === 'done' && part.payload.sessionId) {
|
|
163
|
+
doneSessionId = doneSessionId ?? part.payload.sessionId;
|
|
151
164
|
}
|
|
152
165
|
else {
|
|
153
166
|
writeHarnessPart(part, writer);
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* CapabilityHost with Runtime context (working memory, long-term memory,
|
|
8
8
|
* policy injections) and structures the output per audio-model best practices.
|
|
9
9
|
*
|
|
10
|
-
* Adapters
|
|
10
|
+
* Adapters can extend or replace
|
|
11
11
|
* the default with backend-specific voice rules and guardrails.
|
|
12
12
|
*
|
|
13
13
|
* Prompt structure follows Google's Live API best practices and
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* CapabilityHost with Runtime context (working memory, long-term memory,
|
|
8
8
|
* policy injections) and structures the output per audio-model best practices.
|
|
9
9
|
*
|
|
10
|
-
* Adapters
|
|
10
|
+
* Adapters can extend or replace
|
|
11
11
|
* the default with backend-specific voice rules and guardrails.
|
|
12
12
|
*
|
|
13
13
|
* Prompt structure follows Google's Live API best practices and
|
|
@@ -47,6 +47,7 @@ const INTERNAL_MEMORY_KEYS = [
|
|
|
47
47
|
'flowStateByAgent',
|
|
48
48
|
'__ariaAssistantText',
|
|
49
49
|
'__ariaContextBudget',
|
|
50
|
+
'__goals',
|
|
50
51
|
];
|
|
51
52
|
/**
|
|
52
53
|
* Default LivePromptAssembler implementation.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ZodTypeAny } from 'zod';
|
|
2
2
|
/**
|
|
3
3
|
* Backend-agnostic tool declaration. Produced by capabilities,
|
|
4
|
-
* consumed by adapters (
|
|
4
|
+
* consumed by adapters (AI SDK and others).
|
|
5
5
|
*/
|
|
6
6
|
export interface ToolDeclaration<TInput = unknown, TOutput = unknown> {
|
|
7
7
|
name: string;
|
package/dist/eval/EvalRunner.js
CHANGED
|
@@ -25,16 +25,16 @@ export class EvalRunner {
|
|
|
25
25
|
const handle = runtime.run({ input: turn.input, sessionId });
|
|
26
26
|
for await (const part of handle.events) {
|
|
27
27
|
if (part.type === 'text-delta') {
|
|
28
|
-
response += part.delta;
|
|
28
|
+
response += part.payload.delta;
|
|
29
29
|
}
|
|
30
30
|
if (part.type === 'tool-call') {
|
|
31
|
-
toolsCalled.push(part.toolName);
|
|
31
|
+
toolsCalled.push(part.payload.toolName);
|
|
32
32
|
}
|
|
33
33
|
if (part.type === 'flow-transition') {
|
|
34
|
-
transitions.push({ from: part.from, to: part.to });
|
|
34
|
+
transitions.push({ from: part.payload.from, to: part.payload.to });
|
|
35
35
|
}
|
|
36
36
|
if (part.type === 'done') {
|
|
37
|
-
sessionId = part.sessionId;
|
|
37
|
+
sessionId = part.payload.sessionId;
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
await handle;
|
package/dist/eval/simulation.js
CHANGED
|
@@ -66,10 +66,11 @@ export async function simulateConversation(options) {
|
|
|
66
66
|
let reply = '';
|
|
67
67
|
for await (const part of handle.events) {
|
|
68
68
|
if (part.type === 'text-delta')
|
|
69
|
-
reply += part.delta;
|
|
69
|
+
reply += part.payload.delta;
|
|
70
70
|
if (part.type === 'tool-call')
|
|
71
|
-
toolsCalled.push(part.toolName);
|
|
72
|
-
if (part.type === 'escalation' ||
|
|
71
|
+
toolsCalled.push(part.payload.toolName);
|
|
72
|
+
if (part.type === 'escalation' ||
|
|
73
|
+
(part.type === 'handoff' && part.payload.targetAgent === 'human')) {
|
|
73
74
|
escalated = true;
|
|
74
75
|
}
|
|
75
76
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { StreamPart, TurnHandle } from '../types/stream.js';
|
|
2
2
|
export interface EventBus {
|
|
3
|
-
emit(part:
|
|
4
|
-
events(): AsyncIterable<
|
|
3
|
+
emit(part: StreamPart): void;
|
|
4
|
+
events(): AsyncIterable<StreamPart>;
|
|
5
5
|
close(): void;
|
|
6
6
|
}
|
|
7
7
|
export declare function createEventBus(): EventBus;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { AgentConfig } from '../types/agentConfig.js';
|
|
2
2
|
import type { ChannelDriver } from '../types/channel.js';
|
|
3
|
-
import type { CollectNode } from '../types/flow.js';
|
|
4
3
|
import type { RunContext } from '../types/run-context.js';
|
|
5
4
|
import type { RunState } from '../runtime/durable/types.js';
|
|
6
5
|
import type { NormalizedTransition } from './normalizeTransition.js';
|
|
@@ -9,11 +8,15 @@ export interface FlowPark {
|
|
|
9
8
|
flow: string;
|
|
10
9
|
node: string;
|
|
11
10
|
}
|
|
11
|
+
export declare function pushFlowPark(state: Record<string, unknown>, park: FlowPark): void;
|
|
12
|
+
export declare function popFlowPark(state: Record<string, unknown>): FlowPark | undefined;
|
|
12
13
|
export declare function getFlowPark(state: Record<string, unknown>): FlowPark | undefined;
|
|
13
14
|
export declare function looksLikeOffScriptQuestion(input: string): boolean;
|
|
14
15
|
export interface CollectDigressionOptions {
|
|
15
16
|
agent: AgentConfig;
|
|
16
|
-
node:
|
|
17
|
+
node: {
|
|
18
|
+
id: string;
|
|
19
|
+
};
|
|
17
20
|
activeFlowName: string;
|
|
18
21
|
run: RunState;
|
|
19
22
|
driver: ChannelDriver;
|
|
@@ -1,20 +1,57 @@
|
|
|
1
1
|
import { resolveReplyNode } from './nodeBuilders.js';
|
|
2
2
|
import { selectHostTarget } from '../runtime/select.js';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
import { persistTurnUsageFromTurn } from '../runtime/turnTokenUsage.js';
|
|
4
|
+
const FLOW_PARK_STACK_KEY = '__flowParkStack';
|
|
5
|
+
const LEGACY_FLOW_PARK_KEY = '__flowPark';
|
|
6
|
+
const MAX_FLOW_PARK_DEPTH = 8;
|
|
7
|
+
function isFlowPark(raw) {
|
|
6
8
|
if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {
|
|
7
|
-
return
|
|
9
|
+
return false;
|
|
8
10
|
}
|
|
9
11
|
const flow = raw.flow;
|
|
10
12
|
const node = raw.node;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
+
return typeof flow === 'string' && typeof node === 'string';
|
|
14
|
+
}
|
|
15
|
+
function getFlowParkStack(state) {
|
|
16
|
+
const raw = state[FLOW_PARK_STACK_KEY];
|
|
17
|
+
if (!Array.isArray(raw)) {
|
|
18
|
+
return undefined;
|
|
13
19
|
}
|
|
14
|
-
|
|
20
|
+
const stack = raw.filter(isFlowPark);
|
|
21
|
+
return stack.length > 0 ? stack : undefined;
|
|
22
|
+
}
|
|
23
|
+
export function pushFlowPark(state, park) {
|
|
24
|
+
const stack = getFlowParkStack(state) ?? [];
|
|
25
|
+
stack.push(park);
|
|
26
|
+
while (stack.length > MAX_FLOW_PARK_DEPTH) {
|
|
27
|
+
stack.shift();
|
|
28
|
+
}
|
|
29
|
+
state[FLOW_PARK_STACK_KEY] = stack;
|
|
15
30
|
}
|
|
16
|
-
function
|
|
17
|
-
|
|
31
|
+
export function popFlowPark(state) {
|
|
32
|
+
const stack = getFlowParkStack(state);
|
|
33
|
+
if (!stack) {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
const park = stack.pop();
|
|
37
|
+
if (stack.length === 0) {
|
|
38
|
+
delete state[FLOW_PARK_STACK_KEY];
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
state[FLOW_PARK_STACK_KEY] = stack;
|
|
42
|
+
}
|
|
43
|
+
return park;
|
|
44
|
+
}
|
|
45
|
+
export function getFlowPark(state) {
|
|
46
|
+
const stack = getFlowParkStack(state);
|
|
47
|
+
if (stack) {
|
|
48
|
+
return stack[stack.length - 1];
|
|
49
|
+
}
|
|
50
|
+
const legacy = state[LEGACY_FLOW_PARK_KEY];
|
|
51
|
+
if (isFlowPark(legacy)) {
|
|
52
|
+
return legacy;
|
|
53
|
+
}
|
|
54
|
+
return undefined;
|
|
18
55
|
}
|
|
19
56
|
function appendAssistantMessage(run, text) {
|
|
20
57
|
if (!text.trim()) {
|
|
@@ -53,7 +90,7 @@ export async function runCollectDigression(options) {
|
|
|
53
90
|
};
|
|
54
91
|
}
|
|
55
92
|
if (selection.kind === 'enterFlow') {
|
|
56
|
-
|
|
93
|
+
pushFlowPark(run.state, { flow: activeFlowName, node: node.id });
|
|
57
94
|
return {
|
|
58
95
|
kind: 'transition',
|
|
59
96
|
transition: {
|
|
@@ -74,6 +111,7 @@ export async function runCollectDigression(options) {
|
|
|
74
111
|
tools: ctx.globalTools,
|
|
75
112
|
};
|
|
76
113
|
const turn = await driver.runAgentTurn(resolveReplyNode(replyNode, run.state, { freeConversation: true }), ctx);
|
|
114
|
+
await persistTurnUsageFromTurn(ctx, turn);
|
|
77
115
|
if (turn.text.trim()) {
|
|
78
116
|
appendAssistantMessage(run, turn.text);
|
|
79
117
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { runCollectDigression } from './collectDigression.js';
|
|
2
|
+
import { persistTurnUsageFromTurn } from '../runtime/turnTokenUsage.js';
|
|
2
3
|
import { hasPendingUserInput } from '../runtime/channels/inputBuffer.js';
|
|
3
4
|
import { userInputToText } from '../runtime/userInput.js';
|
|
4
5
|
import { resolveCollectExtractionNode } from './nodeBuilders.js';
|
|
@@ -16,8 +17,13 @@ function appendUserMessage(run, input) {
|
|
|
16
17
|
run.messages = [...run.messages, message];
|
|
17
18
|
}
|
|
18
19
|
export async function collectUntilComplete(node, run, driver, ctx, options) {
|
|
20
|
+
// A satisfied collect defers completion for ONE pass when there is pending input,
|
|
21
|
+
// so a correction ("no, Tuesday") is re-extracted and overwrites before completing
|
|
22
|
+
// (G14). Bounded to a single pass so the loop cannot spin if a driver's awaitUser
|
|
23
|
+
// does not clear the pending buffer — completion no longer depends on that.
|
|
24
|
+
let pendingConsumed = false;
|
|
19
25
|
for (;;) {
|
|
20
|
-
if (schemaSatisfied(node, run.state)) {
|
|
26
|
+
if (schemaSatisfied(node, run.state) && (!hasPendingUserInput(ctx.session) || pendingConsumed)) {
|
|
21
27
|
const data = projectCollectData(node, run.state);
|
|
22
28
|
return normalizeTransition(await node.onComplete(data, run.state));
|
|
23
29
|
}
|
|
@@ -32,6 +38,7 @@ export async function collectUntilComplete(node, run, driver, ctx, options) {
|
|
|
32
38
|
if (hasPendingUserInput(ctx.session)) {
|
|
33
39
|
const signal = await driver.awaitUser(ctx);
|
|
34
40
|
appendUserMessage(run, signal.input);
|
|
41
|
+
pendingConsumed = true;
|
|
35
42
|
}
|
|
36
43
|
else if (ctx.turnInputConsumed) {
|
|
37
44
|
// No fresh input to extract this turn: ask (deterministically) for the
|
|
@@ -59,6 +66,7 @@ export async function collectUntilComplete(node, run, driver, ctx, options) {
|
|
|
59
66
|
const turn = await (driver.runExtraction
|
|
60
67
|
? driver.runExtraction(resolved, ctx)
|
|
61
68
|
: driver.runAgentTurn(resolved, ctx));
|
|
69
|
+
await persistTurnUsageFromTurn(ctx, turn);
|
|
62
70
|
mergeExtractionFromTurn(node, run, turn, ctx);
|
|
63
71
|
const missingAfter = computeMissingFields(node, getCollectData(run.state, node.id));
|
|
64
72
|
const advanced = missingAfter.length < missingBefore.length || schemaSatisfied(node, run.state);
|
|
@@ -113,10 +121,10 @@ export function emitCollectAsk(node, run, ctx) {
|
|
|
113
121
|
return;
|
|
114
122
|
}
|
|
115
123
|
const id = crypto.randomUUID();
|
|
116
|
-
ctx.emit({ type: 'text-start', id });
|
|
117
|
-
ctx.emit({ type: 'text-delta', id, delta: text });
|
|
118
|
-
ctx.emit({ type: 'text-end', id });
|
|
119
|
-
ctx.emit({ type: 'turn-end' });
|
|
124
|
+
ctx.emit({ channel: 'client', type: 'text-start', payload: { id } });
|
|
125
|
+
ctx.emit({ channel: 'client', type: 'text-delta', payload: { id, delta: text } });
|
|
126
|
+
ctx.emit({ channel: 'client', type: 'text-end', payload: { id } });
|
|
127
|
+
ctx.emit({ channel: 'internal', type: 'turn-end', payload: {} });
|
|
120
128
|
appendAssistantMessage(run, text);
|
|
121
129
|
}
|
|
122
130
|
function mergeExtractionFromTurn(node, run, turn, ctx) {
|
package/dist/flow/degrade.js
CHANGED
|
@@ -7,9 +7,9 @@ export function appendSafeAssistantMessage(run, ctx, text = SAFE_DEGRADED_MESSAG
|
|
|
7
7
|
const message = { role: 'assistant', content: text };
|
|
8
8
|
run.messages = [...run.messages, message];
|
|
9
9
|
const id = crypto.randomUUID();
|
|
10
|
-
ctx.emit({ type: 'text-start', id });
|
|
11
|
-
ctx.emit({ type: 'text-delta', id, delta: text });
|
|
12
|
-
ctx.emit({ type: 'text-end', id });
|
|
10
|
+
ctx.emit({ channel: 'client', type: 'text-start', payload: { id } });
|
|
11
|
+
ctx.emit({ channel: 'client', type: 'text-delta', payload: { id, delta: text } });
|
|
12
|
+
ctx.emit({ channel: 'client', type: 'text-end', payload: { id } });
|
|
13
13
|
}
|
|
14
14
|
export async function degradeFlowError(flow, registry, run, driver, ctx, dispatchNode) {
|
|
15
15
|
appendSafeAssistantMessage(run, ctx);
|
|
@@ -29,6 +29,10 @@ export async function degradeFlowError(flow, registry, run, driver, ctx, dispatc
|
|
|
29
29
|
// Fall through to graceful end if the escalate node also fails.
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
|
-
ctx.emit({
|
|
32
|
+
ctx.emit({
|
|
33
|
+
channel: 'internal',
|
|
34
|
+
type: 'flow-end',
|
|
35
|
+
payload: { flow: flow.name, reason: 'error_degraded' },
|
|
36
|
+
});
|
|
33
37
|
return { kind: 'ended', reason: 'error_degraded' };
|
|
34
38
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { FlowNode, FlowState } from '../types/flow.js';
|
|
2
|
-
import type {
|
|
3
|
-
export declare function emitInteractiveOnNodeEnter(node: FlowNode, state: FlowState, emit: (part:
|
|
2
|
+
import type { StreamPart } from '../types/stream.js';
|
|
3
|
+
export declare function emitInteractiveOnNodeEnter(node: FlowNode, state: FlowState, emit: (part: StreamPart) => void): void;
|
|
@@ -35,9 +35,12 @@ export function emitInteractiveOnNodeEnter(node, state, emit) {
|
|
|
35
35
|
return;
|
|
36
36
|
}
|
|
37
37
|
emit({
|
|
38
|
+
channel: 'internal',
|
|
38
39
|
type: 'interactive',
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
payload: {
|
|
41
|
+
nodeId: node.id,
|
|
42
|
+
options: node.choices,
|
|
43
|
+
prompt: interactivePrompt(node, state),
|
|
44
|
+
},
|
|
42
45
|
});
|
|
43
46
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { CollectNode, FlowState } from '../types/flow.js';
|
|
2
2
|
import type { Tool } from '../types/effectTool.js';
|
|
3
|
-
import type {
|
|
3
|
+
import type { StreamPart } from '../types/stream.js';
|
|
4
|
+
export declare function resetCollect(state: FlowState, nodeId: string): void;
|
|
4
5
|
export declare function getCollectData(state: FlowState, nodeId: string): Record<string, unknown>;
|
|
5
6
|
export declare function incrementCollectTurns(state: FlowState, nodeId: string): number;
|
|
6
7
|
export declare function computeMissingFields(node: CollectNode, data: Record<string, unknown>): string[];
|
|
@@ -19,4 +20,4 @@ export declare function mergeTurnExtraction(node: CollectNode, state: FlowState,
|
|
|
19
20
|
name: string;
|
|
20
21
|
result: unknown;
|
|
21
22
|
}>): MergeTurnExtractionResult;
|
|
22
|
-
export declare function emitExtractionTelemetry(node: CollectNode, state: FlowState, incoming: Record<string, unknown>, emit: (part:
|
|
23
|
+
export declare function emitExtractionTelemetry(node: CollectNode, state: FlowState, incoming: Record<string, unknown>, emit: (part: StreamPart) => void): void;
|