@kuralle-agents/core 0.3.8 → 0.3.10
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/authoring/index.d.ts +1 -1
- package/dist/authoring/index.js +1 -1
- package/dist/authoring/nodes.d.ts +1 -1
- package/dist/authoring/nodes.js +1 -1
- package/dist/flow/confirmParse.d.ts +2 -0
- package/dist/flow/confirmParse.js +170 -0
- package/dist/flow/runFlow.js +39 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +2 -1
- package/dist/runtime/channels/TextDriver.js +3 -2
- package/dist/runtime/channels/VoiceDriver.js +3 -2
- package/dist/runtime/grounding/gather.d.ts +3 -2
- package/dist/runtime/grounding/gather.js +7 -3
- package/dist/runtime/grounding/index.d.ts +2 -1
- package/dist/runtime/grounding/index.js +1 -0
- package/dist/runtime/grounding/knowledge.js +7 -6
- package/dist/runtime/grounding/memory.js +3 -3
- package/dist/runtime/grounding/nodeScope.d.ts +4 -0
- package/dist/runtime/grounding/nodeScope.js +11 -0
- package/dist/types/flow.d.ts +39 -4
- package/dist/types/flow.js +13 -0
- package/dist/types/run-context.d.ts +13 -2
- package/package.json +2 -2
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { reply, collect, action, decide, defineFlow } from './nodes.js';
|
|
1
|
+
export { reply, collect, action, decide, confirmGate, defineFlow } from './nodes.js';
|
|
2
2
|
export { defineAgent, type AgentConfig, type Instructions } from './defineAgent.js';
|
package/dist/authoring/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { reply, collect, action, decide, defineFlow } from './nodes.js';
|
|
1
|
+
export { reply, collect, action, decide, confirmGate, defineFlow } from './nodes.js';
|
|
2
2
|
export { defineAgent } from './defineAgent.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { reply, collect, action, decide, defineFlow } from '../types/flow.js';
|
|
1
|
+
export { reply, collect, action, decide, confirmGate, defineFlow } from '../types/flow.js';
|
package/dist/authoring/nodes.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { reply, collect, action, decide, defineFlow } from '../types/flow.js';
|
|
1
|
+
export { reply, collect, action, decide, confirmGate, defineFlow } from '../types/flow.js';
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
const AFFIRM_TOKENS = new Set([
|
|
2
|
+
'yes',
|
|
3
|
+
'yeah',
|
|
4
|
+
'yep',
|
|
5
|
+
'yup',
|
|
6
|
+
'ya',
|
|
7
|
+
'sure',
|
|
8
|
+
'ok',
|
|
9
|
+
'okay',
|
|
10
|
+
'okey',
|
|
11
|
+
'k',
|
|
12
|
+
'confirm',
|
|
13
|
+
'confirmed',
|
|
14
|
+
'correct',
|
|
15
|
+
'right',
|
|
16
|
+
'proceed',
|
|
17
|
+
'do',
|
|
18
|
+
'done',
|
|
19
|
+
'yessir',
|
|
20
|
+
'affirmative',
|
|
21
|
+
'ow',
|
|
22
|
+
'ova',
|
|
23
|
+
'hari',
|
|
24
|
+
'hariyata',
|
|
25
|
+
'ehenam',
|
|
26
|
+
'aam',
|
|
27
|
+
'aama',
|
|
28
|
+
'sari',
|
|
29
|
+
'seri',
|
|
30
|
+
]);
|
|
31
|
+
const AFFIRM_PHRASES = [
|
|
32
|
+
'go ahead',
|
|
33
|
+
'do it',
|
|
34
|
+
'book it',
|
|
35
|
+
'place it',
|
|
36
|
+
'place the order',
|
|
37
|
+
'place my order',
|
|
38
|
+
'sounds good',
|
|
39
|
+
"that's right",
|
|
40
|
+
'thats right',
|
|
41
|
+
'that is correct',
|
|
42
|
+
'please do',
|
|
43
|
+
"let's do it",
|
|
44
|
+
'lets do it',
|
|
45
|
+
'go for it',
|
|
46
|
+
'confirm the order',
|
|
47
|
+
'yes please',
|
|
48
|
+
];
|
|
49
|
+
const AFFIRM_SCRIPT = ['ඔව්', 'හරි', 'හා', 'හරියට', 'ஆம்', 'சரி', 'ஆமா'];
|
|
50
|
+
const DECLINE_TOKENS = new Set([
|
|
51
|
+
'no',
|
|
52
|
+
'nope',
|
|
53
|
+
'nah',
|
|
54
|
+
'na',
|
|
55
|
+
'not',
|
|
56
|
+
'dont',
|
|
57
|
+
"don't",
|
|
58
|
+
'stop',
|
|
59
|
+
'cancel',
|
|
60
|
+
'wait',
|
|
61
|
+
'hold',
|
|
62
|
+
'change',
|
|
63
|
+
'edit',
|
|
64
|
+
'incorrect',
|
|
65
|
+
'wrong',
|
|
66
|
+
'nevermind',
|
|
67
|
+
'never',
|
|
68
|
+
'naha',
|
|
69
|
+
'nae',
|
|
70
|
+
'epa',
|
|
71
|
+
'epaa',
|
|
72
|
+
'wenas',
|
|
73
|
+
'illa',
|
|
74
|
+
'illai',
|
|
75
|
+
'vendam',
|
|
76
|
+
'vendaam',
|
|
77
|
+
'vena',
|
|
78
|
+
]);
|
|
79
|
+
const DECLINE_PHRASES = [
|
|
80
|
+
'not yet',
|
|
81
|
+
'hold on',
|
|
82
|
+
'no thanks',
|
|
83
|
+
'no thank you',
|
|
84
|
+
"don't",
|
|
85
|
+
'do not',
|
|
86
|
+
'change it',
|
|
87
|
+
'change the',
|
|
88
|
+
'let me change',
|
|
89
|
+
'something else',
|
|
90
|
+
'go back',
|
|
91
|
+
];
|
|
92
|
+
const DECLINE_SCRIPT = ['නැහැ', 'නෑ', 'එපා', 'වෙනස්', 'இல்லை', 'வேண்டாம்', 'வேற'];
|
|
93
|
+
const INTERROGATIVE_WORDS = [
|
|
94
|
+
'what',
|
|
95
|
+
'how',
|
|
96
|
+
'when',
|
|
97
|
+
'where',
|
|
98
|
+
'why',
|
|
99
|
+
'which',
|
|
100
|
+
'who',
|
|
101
|
+
'do you',
|
|
102
|
+
'can you',
|
|
103
|
+
'could you',
|
|
104
|
+
'is there',
|
|
105
|
+
'tell me',
|
|
106
|
+
'show me',
|
|
107
|
+
];
|
|
108
|
+
function normalize(raw) {
|
|
109
|
+
return raw.trim().toLowerCase().replace(/\s+/g, ' ');
|
|
110
|
+
}
|
|
111
|
+
function stripTokenPunctuation(token) {
|
|
112
|
+
return token.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu, '');
|
|
113
|
+
}
|
|
114
|
+
function tokenize(normalized) {
|
|
115
|
+
return normalized
|
|
116
|
+
.split(/\s+/)
|
|
117
|
+
.map(stripTokenPunctuation)
|
|
118
|
+
.filter((token) => token.length > 0);
|
|
119
|
+
}
|
|
120
|
+
function hasDeclineToken(tokens) {
|
|
121
|
+
return tokens.some((token) => DECLINE_TOKENS.has(token));
|
|
122
|
+
}
|
|
123
|
+
function hasDeclinePhrase(normalized) {
|
|
124
|
+
return DECLINE_PHRASES.some((phrase) => normalized.includes(phrase));
|
|
125
|
+
}
|
|
126
|
+
function hasDeclineScript(normalized, raw) {
|
|
127
|
+
return DECLINE_SCRIPT.some((fragment) => normalized.includes(fragment) || raw.includes(fragment));
|
|
128
|
+
}
|
|
129
|
+
function hasDecline(normalized, tokens, raw) {
|
|
130
|
+
return hasDeclineToken(tokens) || hasDeclinePhrase(normalized) || hasDeclineScript(normalized, raw);
|
|
131
|
+
}
|
|
132
|
+
function hasInterrogative(normalized) {
|
|
133
|
+
if (normalized.includes('?')) {
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
return INTERROGATIVE_WORDS.some((word) => {
|
|
137
|
+
const pattern = new RegExp(`(?:^|\\s)${word.replace(/\s+/g, '\\s+')}(?:\\s|$|[?.!,])`, 'i');
|
|
138
|
+
return pattern.test(normalized);
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
function hasAffirmScript(normalized, raw) {
|
|
142
|
+
return AFFIRM_SCRIPT.some((fragment) => normalized.includes(fragment) || raw.includes(fragment));
|
|
143
|
+
}
|
|
144
|
+
function hasAffirmPhrase(normalized) {
|
|
145
|
+
return AFFIRM_PHRASES.some((phrase) => normalized.startsWith(phrase));
|
|
146
|
+
}
|
|
147
|
+
function hasAffirmToken(tokens) {
|
|
148
|
+
const head = tokens.slice(0, 3);
|
|
149
|
+
if (head.some((token) => AFFIRM_TOKENS.has(token))) {
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
return tokens.length === 1 && tokens[0] === 'y';
|
|
153
|
+
}
|
|
154
|
+
function hasAffirm(normalized, tokens, raw) {
|
|
155
|
+
return hasAffirmScript(normalized, raw) || hasAffirmPhrase(normalized) || hasAffirmToken(tokens);
|
|
156
|
+
}
|
|
157
|
+
export function parseConfirmation(raw) {
|
|
158
|
+
const normalized = normalize(raw);
|
|
159
|
+
const tokens = tokenize(normalized);
|
|
160
|
+
if (hasDecline(normalized, tokens, raw)) {
|
|
161
|
+
return 'decline';
|
|
162
|
+
}
|
|
163
|
+
if (hasInterrogative(normalized)) {
|
|
164
|
+
return 'ambiguous';
|
|
165
|
+
}
|
|
166
|
+
if (hasAffirm(normalized, tokens, raw)) {
|
|
167
|
+
return 'affirm';
|
|
168
|
+
}
|
|
169
|
+
return 'ambiguous';
|
|
170
|
+
}
|
package/dist/flow/runFlow.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { parseConfirmation } from './confirmParse.js';
|
|
1
2
|
import { hasPendingUserInput } from '../runtime/channels/inputBuffer.js';
|
|
2
3
|
import { collectUntilComplete } from './collectUntilComplete.js';
|
|
3
4
|
import { isActionNode, isCollectNode, isDecideNode, isReplyNode, } from './nodeKinds.js';
|
|
@@ -46,6 +47,38 @@ function appendUserMessage(run, input) {
|
|
|
46
47
|
const message = { role: 'user', content: input };
|
|
47
48
|
run.messages = [...run.messages, message];
|
|
48
49
|
}
|
|
50
|
+
function latestUserText(run) {
|
|
51
|
+
for (let i = run.messages.length - 1; i >= 0; i -= 1) {
|
|
52
|
+
const message = run.messages[i];
|
|
53
|
+
if (message?.role === 'user' && typeof message.content === 'string') {
|
|
54
|
+
return message.content;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return '';
|
|
58
|
+
}
|
|
59
|
+
async function dispatchConfirmGate(node, run, driver, ctx) {
|
|
60
|
+
const gate = node.confirmGate;
|
|
61
|
+
if (!hasPendingUserInput(ctx.session) && ctx.turnInputConsumed) {
|
|
62
|
+
return { kind: 'stay' };
|
|
63
|
+
}
|
|
64
|
+
let input = '';
|
|
65
|
+
if (hasPendingUserInput(ctx.session)) {
|
|
66
|
+
const signal = await driver.awaitUser(ctx);
|
|
67
|
+
input = signal.input;
|
|
68
|
+
appendUserMessage(run, signal.input);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
input = latestUserText(run);
|
|
72
|
+
}
|
|
73
|
+
ctx.turnInputConsumed = true;
|
|
74
|
+
const verdict = parseConfirmation(input);
|
|
75
|
+
const branch = verdict === 'affirm'
|
|
76
|
+
? gate.onConfirm
|
|
77
|
+
: verdict === 'decline'
|
|
78
|
+
? gate.onDecline
|
|
79
|
+
: (gate.onAmbiguous ?? 'stay');
|
|
80
|
+
return normalizeTransition(branch);
|
|
81
|
+
}
|
|
49
82
|
function appendAssistantMessage(run, text) {
|
|
50
83
|
if (!text.trim()) {
|
|
51
84
|
return;
|
|
@@ -61,9 +94,15 @@ async function dispatchNode(node, run, driver, ctx) {
|
|
|
61
94
|
return collectUntilComplete(node, run, driver, ctx);
|
|
62
95
|
}
|
|
63
96
|
if (isDecideNode(node)) {
|
|
97
|
+
if (node.confirmGate) {
|
|
98
|
+
return dispatchConfirmGate(node, run, driver, ctx);
|
|
99
|
+
}
|
|
64
100
|
if (!driver.runStructured) {
|
|
65
101
|
throw new Error('ChannelDriver.runStructured is required for decide nodes');
|
|
66
102
|
}
|
|
103
|
+
if (!node.schema || !node.decide) {
|
|
104
|
+
throw new Error(`decide node "${node.id}" requires schema and decide`);
|
|
105
|
+
}
|
|
67
106
|
// An interactive choice node (withChoices) reached when the turn's input was
|
|
68
107
|
// already consumed by a prior node: its choices were presented on node-enter,
|
|
69
108
|
// so wait for the user to actually pick rather than auto-deciding on stale
|
package/dist/index.d.ts
CHANGED
|
@@ -65,13 +65,15 @@ export type { AuditConfig, AuditEntryBase, AuditEntryType, AuditListOptions, Aud
|
|
|
65
65
|
export type { RealtimeAudioClient, RealtimeSessionConfig, RealtimeAudioConfig, RealtimeToolResponse, RealtimeEventMap, RealtimeSessionHandle, } from './realtime/index.js';
|
|
66
66
|
export type { Hooks } from './types/hooks.js';
|
|
67
67
|
export type { HarnessHooks } from './types/runtime.js';
|
|
68
|
-
export { defineAgent, defineFlow, reply, collect, action, decide, } from './authoring/index.js';
|
|
68
|
+
export { defineAgent, defineFlow, reply, collect, action, decide, confirmGate, } from './authoring/index.js';
|
|
69
69
|
export { defineTool } from './types/effectTool.js';
|
|
70
70
|
export { buildToolSet, toolToAiSdk, ToolApprovalDeniedError } from './tools/effect/index.js';
|
|
71
71
|
export type { Tool as EffectTool } from './types/effectTool.js';
|
|
72
72
|
export type { AgentRoute } from './types/processors.js';
|
|
73
73
|
export type { AgentConfig, Instructions } from './types/agentConfig.js';
|
|
74
|
-
export type { Flow, FlowNode, Transition, CollectNode, DecideNode } from './types/flow.js';
|
|
74
|
+
export type { Flow, FlowNode, Transition, CollectNode, DecideNode, ConfirmGate, NodeGrounding, } from './types/flow.js';
|
|
75
|
+
export { parseConfirmation } from './flow/confirmParse.js';
|
|
76
|
+
export type { ConfirmVerdict } from './flow/confirmParse.js';
|
|
75
77
|
export type { Route } from './types/route.js';
|
|
76
78
|
export type { TurnHandle } from './types/stream.js';
|
|
77
79
|
export type { HarnessStreamPart } from './types/stream.js';
|
package/dist/index.js
CHANGED
|
@@ -36,7 +36,8 @@ export { handoffFilters, removeToolHistory, keepRecentMessages, removeKeys, comp
|
|
|
36
36
|
export { DefaultToolExecutor, DefaultConversationState, DefaultConversationEventLog, DefaultAgentStateController, createFoundation, } from './foundation/index.js';
|
|
37
37
|
export { CapabilityHost, TriageCapability, ExtractionCapability, HandoffCapability, GuardrailCapability, AutoRetrieveCapability, PassThroughRefinement, PassThroughValidation, toGeminiDeclarations, toAISDKTools, } from './capabilities/index.js';
|
|
38
38
|
export { filterAuditEntries } from './audit/index.js';
|
|
39
|
-
export { defineAgent, defineFlow, reply, collect, action, decide, } from './authoring/index.js';
|
|
39
|
+
export { defineAgent, defineFlow, reply, collect, action, decide, confirmGate, } from './authoring/index.js';
|
|
40
40
|
export { defineTool } from './types/effectTool.js';
|
|
41
41
|
export { buildToolSet, toolToAiSdk, ToolApprovalDeniedError } from './tools/effect/index.js';
|
|
42
|
+
export { parseConfirmation } from './flow/confirmParse.js';
|
|
42
43
|
export { createRuntime, Runtime, } from './runtime/Runtime.js';
|
|
@@ -6,7 +6,7 @@ import { consumePendingUserInput } from './inputBuffer.js';
|
|
|
6
6
|
import { runSilentExtraction } from './extractionTurn.js';
|
|
7
7
|
import { applyPreTurnPolicies, applyPostTurnPolicies } from '../policies/agentTurn.js';
|
|
8
8
|
import { resolveMaxSteps } from '../policies/limits.js';
|
|
9
|
-
import { appendGatherBlocks, runGatherPhase } from '../grounding/index.js';
|
|
9
|
+
import { appendGatherBlocks, resolveNodeGatherScope, runGatherPhase } from '../grounding/index.js';
|
|
10
10
|
export class TextDriver {
|
|
11
11
|
toolDefs;
|
|
12
12
|
maxSteps;
|
|
@@ -26,7 +26,8 @@ export class TextDriver {
|
|
|
26
26
|
ctx.emit({ type: 'turn-end' });
|
|
27
27
|
return { text: blocked, toolResults: [] };
|
|
28
28
|
}
|
|
29
|
-
const
|
|
29
|
+
const scope = resolveNodeGatherScope(replyNode, ctx.runState.state, ctx.runState.messages);
|
|
30
|
+
const gather = await runGatherPhase(ctx, scope);
|
|
30
31
|
const out = { text: '', toolResults: [] };
|
|
31
32
|
const model = replyNode.model ?? ctx.model;
|
|
32
33
|
const nodeSystem = node.prompt || buildNodePrompt(replyNode, ctx.runState.state);
|
|
@@ -4,7 +4,7 @@ import { buildNodePrompt, resolveInstructions, composeSystem } from '../../flow/
|
|
|
4
4
|
import { executeModelToolCall } from './executeModelTool.js';
|
|
5
5
|
import { applyPreTurnPolicies, applyPostTurnPolicies } from '../policies/agentTurn.js';
|
|
6
6
|
import { resolveMaxSteps } from '../policies/limits.js';
|
|
7
|
-
import { appendGatherBlocks, runGatherPhase } from '../grounding/index.js';
|
|
7
|
+
import { appendGatherBlocks, resolveNodeGatherScope, runGatherPhase } from '../grounding/index.js';
|
|
8
8
|
import { resolveVoiceGeminiTools } from './voiceTools.js';
|
|
9
9
|
export class VoiceDriver {
|
|
10
10
|
client;
|
|
@@ -29,7 +29,8 @@ export class VoiceDriver {
|
|
|
29
29
|
ctx.emit({ type: 'turn-end' });
|
|
30
30
|
return { text: blocked, toolResults: [] };
|
|
31
31
|
}
|
|
32
|
-
const
|
|
32
|
+
const scope = resolveNodeGatherScope(replyNode, ctx.runState.state, ctx.runState.messages);
|
|
33
|
+
const gather = await runGatherPhase(ctx, scope);
|
|
33
34
|
const out = { text: '', toolResults: [] };
|
|
34
35
|
const nodeSystem = node.prompt || buildNodePrompt(replyNode, ctx.runState.state);
|
|
35
36
|
const baseSystem = composeSystem(ctx.baseInstructions, nodeSystem, ctx.runState.state);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type { RunContext } from '../../types/run-context.js';
|
|
1
|
+
import type { GatherScope, RunContext } from '../../types/run-context.js';
|
|
2
|
+
export type { GatherScope } from '../../types/run-context.js';
|
|
2
3
|
export interface GatherResult {
|
|
3
4
|
retrievalBlock?: string;
|
|
4
5
|
memoryBlock?: string;
|
|
5
6
|
}
|
|
6
|
-
export declare function runGatherPhase(ctx: RunContext): Promise<GatherResult>;
|
|
7
|
+
export declare function runGatherPhase(ctx: RunContext, scope?: GatherScope): Promise<GatherResult>;
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
export async function runGatherPhase(ctx) {
|
|
2
|
-
const retrievalBlock = ctx.autoRetrieve
|
|
3
|
-
|
|
1
|
+
export async function runGatherPhase(ctx, scope) {
|
|
2
|
+
const retrievalBlock = ctx.autoRetrieve && scope?.knowledge?.autoRetrieve !== false
|
|
3
|
+
? await ctx.autoRetrieve.retrieve(ctx, scope)
|
|
4
|
+
: undefined;
|
|
5
|
+
const memoryBlock = ctx.memoryService?.preload && scope?.memory?.preload !== false
|
|
6
|
+
? await ctx.memoryService.preload(ctx, scope)
|
|
7
|
+
: undefined;
|
|
4
8
|
return { retrievalBlock, memoryBlock };
|
|
5
9
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { appendGatherBlocks, buildAutoRetrieveProvider, buildKnowledgeProvider, } from './knowledge.js';
|
|
2
2
|
export { buildMemoryService, resetMissingUserIdWarningsForTests, runMemoryIngest, warnMissingUserId, } from './memory.js';
|
|
3
|
-
export { runGatherPhase, type GatherResult } from './gather.js';
|
|
3
|
+
export { runGatherPhase, type GatherResult, type GatherScope } from './gather.js';
|
|
4
|
+
export { resolveNodeGatherScope } from './nodeScope.js';
|
|
4
5
|
export { createInMemoryKnowledgeConfig, createInMemoryKnowledgeRetriever, type InMemoryKnowledgeDocument, } from './inMemoryKnowledge.js';
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { appendGatherBlocks, buildAutoRetrieveProvider, buildKnowledgeProvider, } from './knowledge.js';
|
|
2
2
|
export { buildMemoryService, resetMissingUserIdWarningsForTests, runMemoryIngest, warnMissingUserId, } from './memory.js';
|
|
3
3
|
export { runGatherPhase } from './gather.js';
|
|
4
|
+
export { resolveNodeGatherScope } from './nodeScope.js';
|
|
4
5
|
export { createInMemoryKnowledgeConfig, createInMemoryKnowledgeRetriever, } from './inMemoryKnowledge.js';
|
|
@@ -35,22 +35,23 @@ export function buildAutoRetrieveProvider(provider, agent) {
|
|
|
35
35
|
return undefined;
|
|
36
36
|
}
|
|
37
37
|
const overrides = agent.knowledge;
|
|
38
|
-
const maxOutputTokens = provider.resolveConfig(overrides).maxOutputTokens;
|
|
39
38
|
return {
|
|
40
|
-
retrieve: async (ctx) => {
|
|
41
|
-
const query = latestUserMessage(ctx);
|
|
39
|
+
retrieve: async (ctx, scope) => {
|
|
40
|
+
const query = scope?.query ?? latestUserMessage(ctx);
|
|
41
|
+
const merged = scope?.knowledge ? { ...overrides, ...scope.knowledge } : overrides;
|
|
42
42
|
const cache = undefined;
|
|
43
|
-
const { results, events } = await provider.retrieve(query || ' ', cache,
|
|
43
|
+
const { results, events } = await provider.retrieve(query || ' ', cache, merged, false);
|
|
44
44
|
for (const event of events) {
|
|
45
45
|
ctx.emit(event);
|
|
46
46
|
}
|
|
47
|
-
const compiled = provider.getCompiledKnowledge(
|
|
47
|
+
const compiled = provider.getCompiledKnowledge(merged);
|
|
48
48
|
const retrievalResults = results.length > 0 ? results : [];
|
|
49
49
|
const combined = [
|
|
50
50
|
...(compiled ? [{ text: compiled }] : []),
|
|
51
51
|
...retrievalResults.map((result) => ({ text: result.text })),
|
|
52
52
|
];
|
|
53
|
-
|
|
53
|
+
const maxChars = provider.resolveConfig(merged).maxOutputTokens * 4;
|
|
54
|
+
return formatRetrievalBlock(combined, maxChars);
|
|
54
55
|
},
|
|
55
56
|
};
|
|
56
57
|
}
|
|
@@ -31,16 +31,16 @@ export function buildMemoryService(service, agent) {
|
|
|
31
31
|
}
|
|
32
32
|
return {
|
|
33
33
|
preload: preloadEnabled
|
|
34
|
-
? async (ctx) => {
|
|
34
|
+
? async (ctx, scope) => {
|
|
35
35
|
if (!ctx.session.userId) {
|
|
36
36
|
warnMissingUserId(ctx.session.id);
|
|
37
37
|
return undefined;
|
|
38
38
|
}
|
|
39
|
-
const userInput = latestUserMessage(ctx);
|
|
39
|
+
const userInput = scope?.query ?? latestUserMessage(ctx);
|
|
40
40
|
if (!userInput.trim()) {
|
|
41
41
|
return undefined;
|
|
42
42
|
}
|
|
43
|
-
const budget = agent.memory?.preload?.tokenBudget ?? 500;
|
|
43
|
+
const budget = scope?.memory?.tokenBudget ?? agent.memory?.preload?.tokenBudget ?? 500;
|
|
44
44
|
const block = await preloadMemoryContext(service, ctx.session, userInput, budget);
|
|
45
45
|
return block ? `\n\n${block}` : undefined;
|
|
46
46
|
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ModelMessage } from 'ai';
|
|
2
|
+
import type { FlowState, ReplyNode } from '../../types/flow.js';
|
|
3
|
+
import type { GatherScope } from '../../types/run-context.js';
|
|
4
|
+
export declare function resolveNodeGatherScope(node: ReplyNode, state: FlowState, history: ModelMessage[]): GatherScope | undefined;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function resolveNodeGatherScope(node, state, history) {
|
|
2
|
+
const g = node.grounding;
|
|
3
|
+
if (!g) {
|
|
4
|
+
return undefined;
|
|
5
|
+
}
|
|
6
|
+
return {
|
|
7
|
+
query: typeof g.query === 'function' ? g.query(state, history) : g.query,
|
|
8
|
+
knowledge: g.knowledge,
|
|
9
|
+
memory: g.memory,
|
|
10
|
+
};
|
|
11
|
+
}
|
package/dist/types/flow.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type { LanguageModel, ToolSet } from 'ai';
|
|
1
|
+
import type { LanguageModel, ModelMessage, ToolSet } from 'ai';
|
|
2
2
|
import type { Instructions } from './agentConfig.js';
|
|
3
|
+
import type { AgentKnowledgeOverrides } from './voice.js';
|
|
3
4
|
import type { StandardSchemaV1 } from './standard-schema.js';
|
|
4
5
|
import type { ContextStrategy } from './context.js';
|
|
5
6
|
import type { TurnResult } from './channel.js';
|
|
@@ -17,6 +18,25 @@ export interface Flow {
|
|
|
17
18
|
maxOscillations?: number;
|
|
18
19
|
}
|
|
19
20
|
export type FlowNode = ReplyNode | CollectNode | ActionNode | DecideNode;
|
|
21
|
+
/** Per-node retrieval/memory scoping (W3). All optional; omitting `grounding`
|
|
22
|
+
* entirely leaves the node grounded exactly as the agent-wide default. */
|
|
23
|
+
export interface NodeGrounding {
|
|
24
|
+
/** Retrieval/memory query for this node. A fixed topic string, or a function
|
|
25
|
+
* over current flow state + message history. Defaults to the last user message. */
|
|
26
|
+
query?: string | ((state: FlowState, history: ModelMessage[]) => string);
|
|
27
|
+
/** Node-scoped knowledge overrides, merged OVER the agent's (node wins). Most
|
|
28
|
+
* useful: `filter` (restrict to a node-specific doc subset) and `autoRetrieve:false`
|
|
29
|
+
* to skip retrieval for this node. `topK`/`maxOutputTokens` also honored. */
|
|
30
|
+
knowledge?: AgentKnowledgeOverrides & {
|
|
31
|
+
autoRetrieve?: boolean;
|
|
32
|
+
};
|
|
33
|
+
/** Node-scoped memory: `preload:false` skips memory preload for this node;
|
|
34
|
+
* `tokenBudget` overrides the agent default for this node only. */
|
|
35
|
+
memory?: {
|
|
36
|
+
preload?: boolean;
|
|
37
|
+
tokenBudget?: number;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
20
40
|
export type Transition = FlowNode | (() => FlowNode) | {
|
|
21
41
|
goto: FlowNode | (() => FlowNode);
|
|
22
42
|
data?: Record<string, unknown>;
|
|
@@ -35,6 +55,7 @@ export interface ReplyNode {
|
|
|
35
55
|
tools?: ToolSet | ((state: FlowState) => ToolSet);
|
|
36
56
|
model?: LanguageModel;
|
|
37
57
|
context?: ContextStrategy;
|
|
58
|
+
grounding?: NodeGrounding;
|
|
38
59
|
next?: (turn: TurnResult, state: FlowState) => Transition | Promise<Transition>;
|
|
39
60
|
}
|
|
40
61
|
export interface CollectNode {
|
|
@@ -61,16 +82,30 @@ export interface ActionNode {
|
|
|
61
82
|
outputSchema?: StandardSchemaV1;
|
|
62
83
|
run: (state: FlowState, ctx: ActionContext) => Transition | Promise<Transition>;
|
|
63
84
|
}
|
|
85
|
+
export interface ConfirmGate {
|
|
86
|
+
onConfirm: Transition;
|
|
87
|
+
onDecline: Transition;
|
|
88
|
+
onAmbiguous?: Transition;
|
|
89
|
+
}
|
|
64
90
|
export interface DecideNode {
|
|
65
91
|
kind: 'decide';
|
|
66
92
|
id: string;
|
|
67
93
|
instructions: Instructions;
|
|
68
|
-
schema
|
|
94
|
+
schema?: StandardSchemaV1;
|
|
69
95
|
choices?: ChoiceOption[];
|
|
70
|
-
|
|
96
|
+
confirmGate?: ConfirmGate;
|
|
97
|
+
decide?: (data: unknown, state: FlowState) => Transition | Promise<Transition>;
|
|
71
98
|
}
|
|
72
99
|
export declare function reply(node: Omit<ReplyNode, 'kind'>): ReplyNode;
|
|
73
100
|
export declare function collect(node: Omit<CollectNode, 'kind'>): CollectNode;
|
|
74
101
|
export declare function action(node: Omit<ActionNode, 'kind'>): ActionNode;
|
|
75
|
-
export declare function decide(node: Omit<DecideNode, 'kind'>): DecideNode;
|
|
102
|
+
export declare function decide(node: Omit<DecideNode, 'kind' | 'confirmGate'> & Required<Pick<DecideNode, 'schema' | 'decide'>>): DecideNode;
|
|
103
|
+
export declare function confirmGate(node: {
|
|
104
|
+
id: string;
|
|
105
|
+
instructions: Instructions;
|
|
106
|
+
onConfirm: Transition;
|
|
107
|
+
onDecline: Transition;
|
|
108
|
+
onAmbiguous?: Transition;
|
|
109
|
+
choices?: ChoiceOption[];
|
|
110
|
+
}): DecideNode;
|
|
76
111
|
export declare function defineFlow(flow: Flow): Flow;
|
package/dist/types/flow.js
CHANGED
|
@@ -10,6 +10,19 @@ export function action(node) {
|
|
|
10
10
|
export function decide(node) {
|
|
11
11
|
return { kind: 'decide', ...node };
|
|
12
12
|
}
|
|
13
|
+
export function confirmGate(node) {
|
|
14
|
+
return {
|
|
15
|
+
kind: 'decide',
|
|
16
|
+
id: node.id,
|
|
17
|
+
instructions: node.instructions,
|
|
18
|
+
choices: node.choices,
|
|
19
|
+
confirmGate: {
|
|
20
|
+
onConfirm: node.onConfirm,
|
|
21
|
+
onDecline: node.onDecline,
|
|
22
|
+
onAmbiguous: node.onAmbiguous,
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
}
|
|
13
26
|
export function defineFlow(flow) {
|
|
14
27
|
return flow;
|
|
15
28
|
}
|
|
@@ -9,6 +9,17 @@ import type { ValidationCapability } from '../capabilities/ValidationCapability.
|
|
|
9
9
|
import type { Limits } from './guardrails.js';
|
|
10
10
|
import type { AnyTool } from './effectTool.js';
|
|
11
11
|
import type { Instructions } from './agentConfig.js';
|
|
12
|
+
import type { AgentKnowledgeOverrides } from './voice.js';
|
|
13
|
+
export interface GatherScope {
|
|
14
|
+
query?: string;
|
|
15
|
+
knowledge?: AgentKnowledgeOverrides & {
|
|
16
|
+
autoRetrieve?: boolean;
|
|
17
|
+
};
|
|
18
|
+
memory?: {
|
|
19
|
+
preload?: boolean;
|
|
20
|
+
tokenBudget?: number;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
12
23
|
export interface EffectToolExecutor {
|
|
13
24
|
execute(args: {
|
|
14
25
|
name: string;
|
|
@@ -23,10 +34,10 @@ export interface EffectToolExecutor {
|
|
|
23
34
|
getTool?(name: string): AnyTool | undefined;
|
|
24
35
|
}
|
|
25
36
|
export interface AutoRetrieveProvider {
|
|
26
|
-
retrieve(ctx: RunContext): Promise<string | undefined>;
|
|
37
|
+
retrieve(ctx: RunContext, scope?: GatherScope): Promise<string | undefined>;
|
|
27
38
|
}
|
|
28
39
|
export interface MemoryService {
|
|
29
|
-
preload?(ctx: RunContext): Promise<string | undefined>;
|
|
40
|
+
preload?(ctx: RunContext, scope?: GatherScope): Promise<string | undefined>;
|
|
30
41
|
ingest?(ctx: RunContext): Promise<void>;
|
|
31
42
|
}
|
|
32
43
|
export interface HookRunner {
|
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.10",
|
|
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.10"
|
|
101
101
|
},
|
|
102
102
|
"dependencies": {
|
|
103
103
|
"chrono-node": "^2.6.0",
|