@kuralle-agents/core 0.3.16 → 0.3.17
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/dist/flow/collectDigression.d.ts +31 -0
- package/dist/flow/collectDigression.js +99 -0
- package/dist/flow/collectUntilComplete.d.ts +6 -1
- package/dist/flow/collectUntilComplete.js +28 -5
- package/dist/flow/normalizeTransition.d.ts +6 -1
- package/dist/flow/runFlow.d.ts +2 -1
- package/dist/flow/runFlow.js +29 -7
- package/dist/runtime/hostLoop.js +4 -4
- package/dist/runtime/select.d.ts +2 -0
- package/dist/runtime/select.js +5 -1
- package/package.json +2 -2
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { AgentConfig } from '../types/agentConfig.js';
|
|
2
|
+
import type { ChannelDriver } from '../types/channel.js';
|
|
3
|
+
import type { CollectNode } from '../types/flow.js';
|
|
4
|
+
import type { RunContext } from '../types/run-context.js';
|
|
5
|
+
import type { RunState } from '../runtime/durable/types.js';
|
|
6
|
+
import type { NormalizedTransition } from './normalizeTransition.js';
|
|
7
|
+
import { selectHostTarget } from '../runtime/select.js';
|
|
8
|
+
export interface FlowPark {
|
|
9
|
+
flow: string;
|
|
10
|
+
node: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function getFlowPark(state: Record<string, unknown>): FlowPark | undefined;
|
|
13
|
+
export declare function looksLikeOffScriptQuestion(input: string): boolean;
|
|
14
|
+
export interface CollectDigressionOptions {
|
|
15
|
+
agent: AgentConfig;
|
|
16
|
+
node: CollectNode;
|
|
17
|
+
activeFlowName: string;
|
|
18
|
+
run: RunState;
|
|
19
|
+
driver: ChannelDriver;
|
|
20
|
+
ctx: RunContext;
|
|
21
|
+
select?: typeof selectHostTarget;
|
|
22
|
+
}
|
|
23
|
+
export type CollectDigressionResult = {
|
|
24
|
+
kind: 'transition';
|
|
25
|
+
transition: NormalizedTransition;
|
|
26
|
+
} | {
|
|
27
|
+
kind: 'answeredThenResume';
|
|
28
|
+
} | {
|
|
29
|
+
kind: 'none';
|
|
30
|
+
};
|
|
31
|
+
export declare function runCollectDigression(options: CollectDigressionOptions): Promise<CollectDigressionResult>;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { resolveReplyNode } from './nodeBuilders.js';
|
|
2
|
+
import { selectHostTarget } from '../runtime/select.js';
|
|
3
|
+
const FLOW_PARK_KEY = '__flowPark';
|
|
4
|
+
export function getFlowPark(state) {
|
|
5
|
+
const raw = state[FLOW_PARK_KEY];
|
|
6
|
+
if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {
|
|
7
|
+
return undefined;
|
|
8
|
+
}
|
|
9
|
+
const flow = raw.flow;
|
|
10
|
+
const node = raw.node;
|
|
11
|
+
if (typeof flow === 'string' && typeof node === 'string') {
|
|
12
|
+
return { flow, node };
|
|
13
|
+
}
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
function setFlowPark(state, park) {
|
|
17
|
+
state[FLOW_PARK_KEY] = park;
|
|
18
|
+
}
|
|
19
|
+
function appendAssistantMessage(run, text) {
|
|
20
|
+
if (!text.trim()) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const message = { role: 'assistant', content: text };
|
|
24
|
+
run.messages = [...run.messages, message];
|
|
25
|
+
}
|
|
26
|
+
export function looksLikeOffScriptQuestion(input) {
|
|
27
|
+
const trimmed = input.trim();
|
|
28
|
+
if (!trimmed) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
if (trimmed.includes('?')) {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
return /^(what|how|why|when|where|who|can|could|do|does|is|are|will|would)\b/i.test(trimmed);
|
|
35
|
+
}
|
|
36
|
+
export async function runCollectDigression(options) {
|
|
37
|
+
const { agent, node, activeFlowName, run, driver, ctx } = options;
|
|
38
|
+
const select = options.select ?? selectHostTarget;
|
|
39
|
+
const offScriptInput = peekLatestUserMessage(run);
|
|
40
|
+
if (!offScriptInput) {
|
|
41
|
+
return { kind: 'none' };
|
|
42
|
+
}
|
|
43
|
+
const selection = await select({
|
|
44
|
+
agent,
|
|
45
|
+
run,
|
|
46
|
+
model: agent.routing?.model ?? ctx.controlModel,
|
|
47
|
+
alwaysRoute: agent.routing?.always === true,
|
|
48
|
+
excludeFlowNames: [activeFlowName],
|
|
49
|
+
});
|
|
50
|
+
if (selection.kind === 'route') {
|
|
51
|
+
return {
|
|
52
|
+
kind: 'transition',
|
|
53
|
+
transition: { kind: 'handoff', to: selection.agentId, reason: selection.reason },
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
if (selection.kind === 'enterFlow') {
|
|
57
|
+
setFlowPark(run.state, { flow: activeFlowName, node: node.id });
|
|
58
|
+
return {
|
|
59
|
+
kind: 'transition',
|
|
60
|
+
transition: {
|
|
61
|
+
kind: 'switchFlow',
|
|
62
|
+
flow: selection.flow,
|
|
63
|
+
park: { flow: activeFlowName, node: node.id },
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
if (!looksLikeOffScriptQuestion(offScriptInput)) {
|
|
68
|
+
return { kind: 'none' };
|
|
69
|
+
}
|
|
70
|
+
const replyNode = {
|
|
71
|
+
kind: 'reply',
|
|
72
|
+
id: `${node.id}__digression`,
|
|
73
|
+
instructions: ctx.baseInstructions ??
|
|
74
|
+
'Answer the user helpfully and concisely. Do not mention internal routing or flows.',
|
|
75
|
+
tools: ctx.globalTools,
|
|
76
|
+
};
|
|
77
|
+
const turn = await driver.runAgentTurn(resolveReplyNode(replyNode, run.state, { freeConversation: true }), ctx);
|
|
78
|
+
if (turn.text.trim()) {
|
|
79
|
+
ctx.emit({ type: 'text-delta', text: turn.text });
|
|
80
|
+
ctx.emit({ type: 'turn-end' });
|
|
81
|
+
appendAssistantMessage(run, turn.text);
|
|
82
|
+
}
|
|
83
|
+
if (turn.control?.type === 'handoff') {
|
|
84
|
+
return {
|
|
85
|
+
kind: 'transition',
|
|
86
|
+
transition: { kind: 'handoff', to: turn.control.target, reason: turn.control.reason },
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
return { kind: 'answeredThenResume' };
|
|
90
|
+
}
|
|
91
|
+
function peekLatestUserMessage(run) {
|
|
92
|
+
for (let i = run.messages.length - 1; i >= 0; i -= 1) {
|
|
93
|
+
const message = run.messages[i];
|
|
94
|
+
if (message?.role === 'user' && typeof message.content === 'string') {
|
|
95
|
+
return message.content;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
+
import type { AgentConfig } from '../types/agentConfig.js';
|
|
1
2
|
import type { ChannelDriver } from '../types/channel.js';
|
|
2
3
|
import type { CollectNode } from '../types/flow.js';
|
|
3
4
|
import type { RunContext } from '../types/run-context.js';
|
|
4
5
|
import type { RunState } from '../runtime/durable/types.js';
|
|
5
6
|
import type { NormalizedTransition } from './normalizeTransition.js';
|
|
6
|
-
export declare function collectUntilComplete(node: CollectNode, run: RunState, driver: ChannelDriver, ctx: RunContext
|
|
7
|
+
export declare function collectUntilComplete(node: CollectNode, run: RunState, driver: ChannelDriver, ctx: RunContext, options?: {
|
|
8
|
+
agent?: AgentConfig;
|
|
9
|
+
activeFlowName?: string;
|
|
10
|
+
}): Promise<NormalizedTransition>;
|
|
11
|
+
export declare function emitCollectAsk(node: CollectNode, run: RunState, ctx: RunContext): void;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { runCollectDigression } from './collectDigression.js';
|
|
1
2
|
import { hasPendingUserInput } from '../runtime/channels/inputBuffer.js';
|
|
2
3
|
import { resolveCollectExtractionNode } from './nodeBuilders.js';
|
|
3
4
|
import { computeMissingFields, createExtractionSubmitTool, getCollectData, incrementCollectTurns, emitExtractionTelemetry, mergeTurnExtraction, projectCollectData, schemaSatisfied, } from './extraction.js';
|
|
@@ -13,7 +14,7 @@ function appendUserMessage(run, input) {
|
|
|
13
14
|
const message = { role: 'user', content: input };
|
|
14
15
|
run.messages = [...run.messages, message];
|
|
15
16
|
}
|
|
16
|
-
export async function collectUntilComplete(node, run, driver, ctx) {
|
|
17
|
+
export async function collectUntilComplete(node, run, driver, ctx, options) {
|
|
17
18
|
for (;;) {
|
|
18
19
|
if (schemaSatisfied(node, run.state)) {
|
|
19
20
|
const data = projectCollectData(node, run.state);
|
|
@@ -44,11 +45,11 @@ export async function collectUntilComplete(node, run, driver, ctx) {
|
|
|
44
45
|
const data = projectCollectData(node, run.state);
|
|
45
46
|
return normalizeTransition(await node.onComplete(data, run.state));
|
|
46
47
|
}
|
|
47
|
-
const
|
|
48
|
-
const submitTool = createExtractionSubmitTool(node,
|
|
48
|
+
const missingBefore = computeMissingFields(node, getCollectData(run.state, node.id));
|
|
49
|
+
const submitTool = createExtractionSubmitTool(node, missingBefore, {
|
|
49
50
|
userMessage: peekLatestUserMessage(run),
|
|
50
51
|
});
|
|
51
|
-
const resolved = resolveCollectExtractionNode(node,
|
|
52
|
+
const resolved = resolveCollectExtractionNode(node, missingBefore, run.state, submitTool);
|
|
52
53
|
// Non-speaking extraction: the model's prose is DISCARDED (never emitted or
|
|
53
54
|
// appended), so a collect turn cannot author narration that contradicts flow
|
|
54
55
|
// state. Falls back to runAgentTurn for drivers without runExtraction; its
|
|
@@ -58,6 +59,28 @@ export async function collectUntilComplete(node, run, driver, ctx) {
|
|
|
58
59
|
? driver.runExtraction(resolved, ctx)
|
|
59
60
|
: driver.runAgentTurn(resolved, ctx));
|
|
60
61
|
mergeExtractionFromTurn(node, run, turn, ctx);
|
|
62
|
+
const missingAfter = computeMissingFields(node, getCollectData(run.state, node.id));
|
|
63
|
+
const advanced = missingAfter.length < missingBefore.length || schemaSatisfied(node, run.state);
|
|
64
|
+
if (!advanced &&
|
|
65
|
+
ctx.outOfBandControl &&
|
|
66
|
+
options?.agent &&
|
|
67
|
+
options.activeFlowName) {
|
|
68
|
+
const digression = await runCollectDigression({
|
|
69
|
+
agent: options.agent,
|
|
70
|
+
node,
|
|
71
|
+
activeFlowName: options.activeFlowName,
|
|
72
|
+
run,
|
|
73
|
+
driver,
|
|
74
|
+
ctx,
|
|
75
|
+
});
|
|
76
|
+
if (digression.kind === 'transition') {
|
|
77
|
+
return digression.transition;
|
|
78
|
+
}
|
|
79
|
+
if (digression.kind === 'answeredThenResume') {
|
|
80
|
+
emitCollectAsk(node, run, ctx);
|
|
81
|
+
return { kind: 'stay' };
|
|
82
|
+
}
|
|
83
|
+
}
|
|
61
84
|
}
|
|
62
85
|
}
|
|
63
86
|
function humanizeField(field) {
|
|
@@ -82,7 +105,7 @@ function renderCollectAsk(node, missing, state) {
|
|
|
82
105
|
}
|
|
83
106
|
return 'Could you tell me a little more?';
|
|
84
107
|
}
|
|
85
|
-
function emitCollectAsk(node, run, ctx) {
|
|
108
|
+
export function emitCollectAsk(node, run, ctx) {
|
|
86
109
|
const missing = computeMissingFields(node, getCollectData(run.state, node.id));
|
|
87
110
|
const text = renderCollectAsk(node, missing, run.state);
|
|
88
111
|
if (!text.trim()) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { FlowNode, Transition } from '../types/flow.js';
|
|
1
|
+
import type { Flow, FlowNode, Transition } from '../types/flow.js';
|
|
2
|
+
import type { FlowPark } from './collectDigression.js';
|
|
2
3
|
export type NormalizedTransition = {
|
|
3
4
|
kind: 'goto';
|
|
4
5
|
node: FlowNode;
|
|
@@ -15,6 +16,10 @@ export type NormalizedTransition = {
|
|
|
15
16
|
reason: string;
|
|
16
17
|
} | {
|
|
17
18
|
kind: 'stay';
|
|
19
|
+
} | {
|
|
20
|
+
kind: 'switchFlow';
|
|
21
|
+
flow: Flow;
|
|
22
|
+
park: FlowPark;
|
|
18
23
|
};
|
|
19
24
|
export declare function resolveNodeRef(ref: FlowNode | (() => FlowNode)): FlowNode;
|
|
20
25
|
export declare function normalizeTransition(transition: Transition): NormalizedTransition;
|
package/dist/flow/runFlow.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { AgentConfig } from '../types/agentConfig.js';
|
|
1
2
|
import type { ChannelDriver } from '../types/channel.js';
|
|
2
3
|
import type { Flow } from '../types/flow.js';
|
|
3
4
|
import type { RunContext } from '../types/run-context.js';
|
|
@@ -15,4 +16,4 @@ export type FlowResult = {
|
|
|
15
16
|
export declare class FlowOscillationError extends Error {
|
|
16
17
|
constructor(from: string, to: string);
|
|
17
18
|
}
|
|
18
|
-
export declare function runFlow(flow: Flow, run: RunState, driver: ChannelDriver, ctx: RunContext): Promise<FlowResult>;
|
|
19
|
+
export declare function runFlow(flow: Flow, run: RunState, driver: ChannelDriver, ctx: RunContext, agent?: AgentConfig): Promise<FlowResult>;
|
package/dist/flow/runFlow.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getFlowPark } from './collectDigression.js';
|
|
1
2
|
import { parseConfirmation } from './confirmParse.js';
|
|
2
3
|
import { hasPendingUserInput } from '../runtime/channels/inputBuffer.js';
|
|
3
4
|
import { collectUntilComplete } from './collectUntilComplete.js';
|
|
@@ -87,12 +88,15 @@ function appendAssistantMessage(run, text) {
|
|
|
87
88
|
const message = { role: 'assistant', content: text };
|
|
88
89
|
run.messages = [...run.messages, message];
|
|
89
90
|
}
|
|
90
|
-
async function dispatchNode(node, run, driver, ctx) {
|
|
91
|
+
async function dispatchNode(node, run, driver, ctx, agent, flow) {
|
|
91
92
|
if (isActionNode(node)) {
|
|
92
93
|
return normalizeTransition(await node.run(run.state, toActionContext(ctx)));
|
|
93
94
|
}
|
|
94
95
|
if (isCollectNode(node)) {
|
|
95
|
-
return collectUntilComplete(node, run, driver, ctx
|
|
96
|
+
return collectUntilComplete(node, run, driver, ctx, {
|
|
97
|
+
agent,
|
|
98
|
+
activeFlowName: flow.name,
|
|
99
|
+
});
|
|
96
100
|
}
|
|
97
101
|
if (isDecideNode(node)) {
|
|
98
102
|
if (node.confirmGate) {
|
|
@@ -139,7 +143,7 @@ async function dispatchNode(node, run, driver, ctx) {
|
|
|
139
143
|
if (decision.kind === 'redispatch') {
|
|
140
144
|
const signal = await driver.awaitUser(ctx);
|
|
141
145
|
appendUserMessage(run, signal.input);
|
|
142
|
-
return dispatchNode(node, run, driver, ctx);
|
|
146
|
+
return dispatchNode(node, run, driver, ctx, agent, flow);
|
|
143
147
|
}
|
|
144
148
|
appendAssistantMessage(run, turn.text);
|
|
145
149
|
if (decision.kind === 'transition') {
|
|
@@ -151,7 +155,7 @@ async function dispatchNode(node, run, driver, ctx) {
|
|
|
151
155
|
if (turn.interrupted) {
|
|
152
156
|
const signal = await driver.awaitUser(ctx);
|
|
153
157
|
appendUserMessage(run, signal.input);
|
|
154
|
-
return dispatchNode(node, run, driver, ctx);
|
|
158
|
+
return dispatchNode(node, run, driver, ctx, agent, flow);
|
|
155
159
|
}
|
|
156
160
|
if (turn.control?.type === 'handoff') {
|
|
157
161
|
return { kind: 'handoff', to: turn.control.target, reason: turn.control.reason };
|
|
@@ -172,7 +176,7 @@ async function dispatchNode(node, run, driver, ctx) {
|
|
|
172
176
|
}
|
|
173
177
|
throw new Error(`Unknown node kind: ${node.kind}`);
|
|
174
178
|
}
|
|
175
|
-
export async function runFlow(flow, run, driver, ctx) {
|
|
179
|
+
export async function runFlow(flow, run, driver, ctx, agent) {
|
|
176
180
|
const registry = buildNodeRegistry(flow);
|
|
177
181
|
const startNode = resolveStartNode(flow);
|
|
178
182
|
const initialNodeId = run.activeNode ?? startNode.id;
|
|
@@ -192,7 +196,7 @@ export async function runFlow(flow, run, driver, ctx) {
|
|
|
192
196
|
for (;;) {
|
|
193
197
|
let transition;
|
|
194
198
|
try {
|
|
195
|
-
transition = await dispatchNode(node, run, driver, ctx);
|
|
199
|
+
transition = await dispatchNode(node, run, driver, ctx, agent, flow);
|
|
196
200
|
}
|
|
197
201
|
catch (error) {
|
|
198
202
|
if (error instanceof SuspendError || error instanceof ToolApprovalDeniedError) {
|
|
@@ -200,9 +204,27 @@ export async function runFlow(flow, run, driver, ctx) {
|
|
|
200
204
|
}
|
|
201
205
|
const message = error instanceof Error ? error.message : String(error);
|
|
202
206
|
ctx.emit({ type: 'error', error: message });
|
|
203
|
-
return degradeFlowError(flow, registry, run, driver, ctx, dispatchNode);
|
|
207
|
+
return degradeFlowError(flow, registry, run, driver, ctx, (n, r, d, c) => dispatchNode(n, r, d, c, agent, flow));
|
|
208
|
+
}
|
|
209
|
+
if (transition.kind === 'switchFlow') {
|
|
210
|
+
run.activeFlow = transition.flow.name;
|
|
211
|
+
run.activeNode = undefined;
|
|
212
|
+
await ctx.runStore.putRunState(run);
|
|
213
|
+
return runFlow(transition.flow, run, driver, ctx, agent);
|
|
204
214
|
}
|
|
205
215
|
if (transition.kind === 'end') {
|
|
216
|
+
const park = getFlowPark(run.state);
|
|
217
|
+
if (park && agent) {
|
|
218
|
+
delete run.state.__flowPark;
|
|
219
|
+
const parkedFlow = agent.flows?.find((candidate) => candidate.name === park.flow);
|
|
220
|
+
if (parkedFlow) {
|
|
221
|
+
run.activeFlow = park.flow;
|
|
222
|
+
run.activeNode = park.node;
|
|
223
|
+
await ctx.runStore.putRunState(run);
|
|
224
|
+
ctx.emit({ type: 'flow-end', flow: flow.name, reason: transition.reason });
|
|
225
|
+
return runFlow(parkedFlow, run, driver, ctx, agent);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
206
228
|
ctx.emit({ type: 'flow-end', flow: flow.name, reason: transition.reason });
|
|
207
229
|
return { kind: 'ended', reason: transition.reason };
|
|
208
230
|
}
|
package/dist/runtime/hostLoop.js
CHANGED
|
@@ -14,7 +14,7 @@ export async function hostLoop(options) {
|
|
|
14
14
|
if (!flow) {
|
|
15
15
|
throw new Error(`Active flow "${run.activeFlow}" not found on agent "${agent.id}"`);
|
|
16
16
|
}
|
|
17
|
-
return await runActiveFlow(flow, run, driver, ctx);
|
|
17
|
+
return await runActiveFlow(flow, run, driver, ctx, agent);
|
|
18
18
|
}
|
|
19
19
|
const alwaysRoute = agent.routing?.always === true;
|
|
20
20
|
if (shouldRunHostSelector(agent, run.activeFlow, alwaysRoute)) {
|
|
@@ -25,7 +25,7 @@ export async function hostLoop(options) {
|
|
|
25
25
|
alwaysRoute,
|
|
26
26
|
});
|
|
27
27
|
if (selection.kind === 'enterFlow') {
|
|
28
|
-
return await runActiveFlow(selection.flow, run, driver, ctx);
|
|
28
|
+
return await runActiveFlow(selection.flow, run, driver, ctx, agent);
|
|
29
29
|
}
|
|
30
30
|
if (selection.kind === 'route') {
|
|
31
31
|
ctx.emit({ type: 'handoff', targetAgent: selection.agentId, reason: selection.reason });
|
|
@@ -45,10 +45,10 @@ export async function hostLoop(options) {
|
|
|
45
45
|
throw error;
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
|
-
async function runActiveFlow(flow, run, driver, ctx) {
|
|
48
|
+
async function runActiveFlow(flow, run, driver, ctx, agent) {
|
|
49
49
|
incrementTurnCount(run);
|
|
50
50
|
assertWithinTurnLimit(run, ctx.limits);
|
|
51
|
-
const result = await runFlow(flow, run, driver, ctx);
|
|
51
|
+
const result = await runFlow(flow, run, driver, ctx, agent);
|
|
52
52
|
if (result.kind === 'handoff') {
|
|
53
53
|
return { kind: 'handoff', to: result.to, reason: result.reason };
|
|
54
54
|
}
|
package/dist/runtime/select.d.ts
CHANGED
|
@@ -17,5 +17,7 @@ export interface SelectHostOptions {
|
|
|
17
17
|
run: RunState;
|
|
18
18
|
model: LanguageModel;
|
|
19
19
|
alwaysRoute?: boolean;
|
|
20
|
+
/** Flow names to exclude from enterFlow (e.g. the active flow during in-flow digression). */
|
|
21
|
+
excludeFlowNames?: string[];
|
|
20
22
|
}
|
|
21
23
|
export declare function selectHostTarget(options: SelectHostOptions): Promise<HostSelection>;
|
package/dist/runtime/select.js
CHANGED
|
@@ -19,8 +19,12 @@ export async function selectHostTarget(options) {
|
|
|
19
19
|
}
|
|
20
20
|
const completed = run.state.__completedFlows;
|
|
21
21
|
const completedFlows = Array.isArray(completed) ? completed : [];
|
|
22
|
-
const
|
|
22
|
+
const excluded = new Set(options.excludeFlowNames ?? []);
|
|
23
|
+
const availableFlows = flows.filter((flow) => !completedFlows.includes(flow.name) && !excluded.has(flow.name));
|
|
23
24
|
const agentRoutes = routes.filter((route) => route.agent);
|
|
25
|
+
if (availableFlows.length === 0 && agentRoutes.length === 0) {
|
|
26
|
+
return { kind: 'keep' };
|
|
27
|
+
}
|
|
24
28
|
if (availableFlows.length === 1 && agentRoutes.length === 0) {
|
|
25
29
|
return { kind: 'enterFlow', flow: availableFlows[0] };
|
|
26
30
|
}
|
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"url": "git+https://github.com/kuralle/kuralle-agents.git",
|
|
7
7
|
"directory": "packages/kuralle-core"
|
|
8
8
|
},
|
|
9
|
-
"version": "0.3.
|
|
9
|
+
"version": "0.3.17",
|
|
10
10
|
"description": "A framework for structured conversational AI agents",
|
|
11
11
|
"publishConfig": {
|
|
12
12
|
"access": "public"
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
"dotenv": "^16.4.0",
|
|
98
98
|
"typescript": "^5.3.0",
|
|
99
99
|
"zod": "^3.23.0",
|
|
100
|
-
"@kuralle-agents/realtime-audio": "0.3.
|
|
100
|
+
"@kuralle-agents/realtime-audio": "0.3.17"
|
|
101
101
|
},
|
|
102
102
|
"dependencies": {
|
|
103
103
|
"chrono-node": "^2.6.0",
|