@kuralle-agents/core 0.8.5 → 0.10.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 +2 -2
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/runtime/channels/TextDriver.js +2 -2
- package/dist/runtime/channels/index.d.ts +1 -1
- package/dist/runtime/channels/index.js +1 -1
- package/dist/runtime/channels/inputBuffer.d.ts +4 -1
- package/dist/runtime/channels/inputBuffer.js +8 -0
- package/dist/runtime/index.d.ts +2 -2
- package/dist/runtime/index.js +2 -2
- package/dist/runtime/userInput.d.ts +2 -0
- package/dist/runtime/userInput.js +20 -0
- package/dist/tools/fs/createFsTool.js +5 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -27,6 +27,7 @@ One tagless primitive — `defineAgent` — derives behavior from the fields you
|
|
|
27
27
|
- **`createFactMemoryService`** — cross-session fact memory (LLM merge-on-ingest) keyed by `userId` on any persistent block store.
|
|
28
28
|
- **Built-in guardrails** — `createPromptInjectionGuard`, `createPiiInputGuard`/`OutputGuard`, `createModerationGuard`, `createGroundingValidator` (see `guides/GUARDRAILS.md`).
|
|
29
29
|
- **Simulation eval** — `simulateConversation` + `createJudge` + `runSimulationSuite`: persona-driven simulated users scored by an LLM judge.
|
|
30
|
+
- **Pending-input drain-and-merge** — `setPendingUserInput` / `consumeAllPendingUserInput`: mid-turn messages enqueue; the next `awaitUser` drains the FIFO into one merged turn (pair with `@kuralle-agents/messaging` `inboundCoalescing` for WhatsApp bursts).
|
|
30
31
|
|
|
31
32
|
## Usage
|
|
32
33
|
|
|
@@ -46,8 +47,7 @@ const agent = defineAgent({
|
|
|
46
47
|
id: 'support',
|
|
47
48
|
instructions: 'You are a helpful support agent.',
|
|
48
49
|
model: openai('gpt-4o-mini'),
|
|
49
|
-
tools:
|
|
50
|
-
tools: { echo }, // durable executor
|
|
50
|
+
tools: { echo }, // durable effect tools — model-visible AND executor-registered
|
|
51
51
|
});
|
|
52
52
|
|
|
53
53
|
const runtime = createRuntime({ agents: [agent], defaultAgentId: 'support' });
|
package/dist/index.d.ts
CHANGED
|
@@ -118,4 +118,5 @@ export type { RunContext, ToolContext, ActionContext } from './types/run-context
|
|
|
118
118
|
export type { AnyTool } from './types/effectTool.js';
|
|
119
119
|
export { createRuntime, Runtime, type HarnessConfig, type RunOptions, } from './runtime/Runtime.js';
|
|
120
120
|
export type { RuntimeLike } from './runtime/RuntimeLike.js';
|
|
121
|
-
export { userInputToText, hasMediaParts, transcribeAudioParts, type UserInputContent, } from './runtime/userInput.js';
|
|
121
|
+
export { userInputToText, mergeUserInputContents, hasMediaParts, transcribeAudioParts, type UserInputContent, } from './runtime/userInput.js';
|
|
122
|
+
export { setPendingUserInput, consumePendingUserInput, consumeAllPendingUserInput, peekPendingUserInput, hasPendingUserInput, } from './runtime/channels/inputBuffer.js';
|
package/dist/index.js
CHANGED
|
@@ -59,4 +59,5 @@ export { parseConfirmation } from './flow/confirmParse.js';
|
|
|
59
59
|
export { harnessToUIMessageStream } from './ai-sdk/uiMessageStream.js';
|
|
60
60
|
export { DURABLE_RUNS_KEY } from './runtime/durable/types.js';
|
|
61
61
|
export { createRuntime, Runtime, } from './runtime/Runtime.js';
|
|
62
|
-
export { userInputToText, hasMediaParts, transcribeAudioParts, } from './runtime/userInput.js';
|
|
62
|
+
export { userInputToText, mergeUserInputContents, hasMediaParts, transcribeAudioParts, } from './runtime/userInput.js';
|
|
63
|
+
export { setPendingUserInput, consumePendingUserInput, consumeAllPendingUserInput, peekPendingUserInput, hasPendingUserInput, } from './runtime/channels/inputBuffer.js';
|
|
@@ -2,7 +2,7 @@ import { streamText } from 'ai';
|
|
|
2
2
|
import { buildNodePrompt, resolveInstructions, composeSystem } from '../../flow/nodeBuilders.js';
|
|
3
3
|
import { buildToolSet } from '../../tools/effect/index.js';
|
|
4
4
|
import { executeModelToolCall, toolResultMessage } from './executeModelTool.js';
|
|
5
|
-
import {
|
|
5
|
+
import { consumeAllPendingUserInput } 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';
|
|
@@ -167,7 +167,7 @@ export class TextDriver {
|
|
|
167
167
|
return resolveStructuredDecide(node, ctx, system);
|
|
168
168
|
}
|
|
169
169
|
async awaitUser(ctx) {
|
|
170
|
-
const input =
|
|
170
|
+
const input = consumeAllPendingUserInput(ctx.session) ?? '';
|
|
171
171
|
return { type: 'message', input };
|
|
172
172
|
}
|
|
173
173
|
resolveTools(resolved, ctx) {
|
|
@@ -4,4 +4,4 @@ export type { TextDriverConfig } from './TextDriver.js';
|
|
|
4
4
|
export { VoiceDriver } from './VoiceDriver.js';
|
|
5
5
|
export type { VoiceDriverConfig } from './VoiceDriver.js';
|
|
6
6
|
export { resolveVoiceGeminiTools, v2ToolsToGemini } from './voiceTools.js';
|
|
7
|
-
export { setPendingUserInput, consumePendingUserInput, peekPendingUserInput, hasPendingUserInput, } from './inputBuffer.js';
|
|
7
|
+
export { setPendingUserInput, consumePendingUserInput, consumeAllPendingUserInput, peekPendingUserInput, hasPendingUserInput, } from './inputBuffer.js';
|
|
@@ -5,4 +5,4 @@ export { TextDriver, buildNodePrompt } from './TextDriver.js';
|
|
|
5
5
|
// the primary primitive; cascaded voice runs over text (see livekit-plugin).
|
|
6
6
|
export { VoiceDriver } from './VoiceDriver.js';
|
|
7
7
|
export { resolveVoiceGeminiTools, v2ToolsToGemini } from './voiceTools.js';
|
|
8
|
-
export { setPendingUserInput, consumePendingUserInput, peekPendingUserInput, hasPendingUserInput, } from './inputBuffer.js';
|
|
8
|
+
export { setPendingUserInput, consumePendingUserInput, consumeAllPendingUserInput, peekPendingUserInput, hasPendingUserInput, } from './inputBuffer.js';
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { Session } from '../../types/session.js';
|
|
2
|
-
import type
|
|
2
|
+
import { type UserInputContent } from '../userInput.js';
|
|
3
3
|
export declare function setPendingUserInput(session: Session, input: UserInputContent): void;
|
|
4
|
+
/** Dequeue one pending input. Built-in drivers drain the full queue via {@link consumeAllPendingUserInput}. */
|
|
4
5
|
export declare function consumePendingUserInput(session: Session): UserInputContent;
|
|
6
|
+
/** Drain the pending-input FIFO and merge into one turn (mid-turn enqueue-merge). */
|
|
7
|
+
export declare function consumeAllPendingUserInput(session: Session): UserInputContent | undefined;
|
|
5
8
|
export declare function peekPendingUserInput(session: Session): UserInputContent | undefined;
|
|
6
9
|
export declare function hasPendingUserInput(session: Session): boolean;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { mergeUserInputContents } from '../userInput.js';
|
|
1
2
|
const PENDING_INPUT_KEY = '__v2_pendingUserInput';
|
|
2
3
|
function queue(session) {
|
|
3
4
|
const v = session.workingMemory[PENDING_INPUT_KEY];
|
|
@@ -10,6 +11,7 @@ function queue(session) {
|
|
|
10
11
|
export function setPendingUserInput(session, input) {
|
|
11
12
|
session.workingMemory[PENDING_INPUT_KEY] = [...queue(session), input];
|
|
12
13
|
}
|
|
14
|
+
/** Dequeue one pending input. Built-in drivers drain the full queue via {@link consumeAllPendingUserInput}. */
|
|
13
15
|
export function consumePendingUserInput(session) {
|
|
14
16
|
const q = queue(session);
|
|
15
17
|
const next = q.shift() ?? '';
|
|
@@ -19,6 +21,12 @@ export function consumePendingUserInput(session) {
|
|
|
19
21
|
session.workingMemory[PENDING_INPUT_KEY] = q;
|
|
20
22
|
return next;
|
|
21
23
|
}
|
|
24
|
+
/** Drain the pending-input FIFO and merge into one turn (mid-turn enqueue-merge). */
|
|
25
|
+
export function consumeAllPendingUserInput(session) {
|
|
26
|
+
const q = queue(session);
|
|
27
|
+
delete session.workingMemory[PENDING_INPUT_KEY];
|
|
28
|
+
return mergeUserInputContents(q);
|
|
29
|
+
}
|
|
22
30
|
export function peekPendingUserInput(session) {
|
|
23
31
|
return queue(session)[0];
|
|
24
32
|
}
|
package/dist/runtime/index.d.ts
CHANGED
|
@@ -3,6 +3,6 @@ export type { RuntimeLike } from './RuntimeLike.js';
|
|
|
3
3
|
export { SessionWorkingMemory } from './WorkingMemory.js';
|
|
4
4
|
export { TextDriver, VoiceDriver } from './channels/index.js';
|
|
5
5
|
export type { VoiceDriverConfig } from './channels/index.js';
|
|
6
|
-
export { setPendingUserInput, consumePendingUserInput, peekPendingUserInput, hasPendingUserInput, } from './channels/index.js';
|
|
7
|
-
export { userInputToText, hasMediaParts, transcribeAudioParts, type UserInputContent, } from './userInput.js';
|
|
6
|
+
export { setPendingUserInput, consumePendingUserInput, consumeAllPendingUserInput, peekPendingUserInput, hasPendingUserInput, } from './channels/index.js';
|
|
7
|
+
export { userInputToText, mergeUserInputContents, hasMediaParts, transcribeAudioParts, type UserInputContent, } from './userInput.js';
|
|
8
8
|
export { isAbortSignal, type InterruptionEvent, type AbortOptions, type CancellationReason, } from '../types/index.js';
|
package/dist/runtime/index.js
CHANGED
|
@@ -4,9 +4,9 @@ export { TextDriver, VoiceDriver } from './channels/index.js';
|
|
|
4
4
|
// Pending-input buffer helpers — required by custom ChannelDriver authors to
|
|
5
5
|
// implement awaitUser the same FIFO-aware way the built-in drivers do (the
|
|
6
6
|
// buffer is an ordered queue since 0.3.13/H3, not a single slot).
|
|
7
|
-
export { setPendingUserInput, consumePendingUserInput, peekPendingUserInput, hasPendingUserInput, } from './channels/index.js';
|
|
7
|
+
export { setPendingUserInput, consumePendingUserInput, consumeAllPendingUserInput, peekPendingUserInput, hasPendingUserInput, } from './channels/index.js';
|
|
8
8
|
// Multimodal user input — `UserInputContent` is the runtime's accepted user-turn
|
|
9
9
|
// shape (text or AI SDK file/image/audio parts). Helpers project to text and
|
|
10
10
|
// transcribe audio so ingress adapters (web/messaging) can build it uniformly.
|
|
11
|
-
export { userInputToText, hasMediaParts, transcribeAudioParts, } from './userInput.js';
|
|
11
|
+
export { userInputToText, mergeUserInputContents, hasMediaParts, transcribeAudioParts, } from './userInput.js';
|
|
12
12
|
export { isAbortSignal, } from '../types/index.js';
|
|
@@ -10,6 +10,8 @@ import { type UserContent, type TranscriptionModel } from 'ai';
|
|
|
10
10
|
* input buffer are all persisted through the `SessionStore` (JSON/Redis/Postgres).
|
|
11
11
|
*/
|
|
12
12
|
export type UserInputContent = UserContent;
|
|
13
|
+
/** Merge multiple user inputs into one turn (ingress coalescing / mid-turn drain). */
|
|
14
|
+
export declare function mergeUserInputContents(items: UserInputContent[]): UserInputContent | undefined;
|
|
13
15
|
/** Text projection of user input — for confirm-gate parsing, choice matching, and
|
|
14
16
|
* extraction hints. Non-text parts are dropped. A plain string returns as-is. */
|
|
15
17
|
export declare function userInputToText(input: UserInputContent): string;
|
|
@@ -23,6 +23,26 @@ function audioSource(data) {
|
|
|
23
23
|
}
|
|
24
24
|
return data;
|
|
25
25
|
}
|
|
26
|
+
/** Merge multiple user inputs into one turn (ingress coalescing / mid-turn drain). */
|
|
27
|
+
export function mergeUserInputContents(items) {
|
|
28
|
+
if (items.length === 0)
|
|
29
|
+
return undefined;
|
|
30
|
+
if (items.length === 1)
|
|
31
|
+
return items[0];
|
|
32
|
+
const parts = [];
|
|
33
|
+
for (const item of items) {
|
|
34
|
+
if (typeof item === 'string') {
|
|
35
|
+
if (item.length > 0)
|
|
36
|
+
parts.push({ type: 'text', text: item });
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
parts.push(...item);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (parts.length === 0)
|
|
43
|
+
return '';
|
|
44
|
+
return parts;
|
|
45
|
+
}
|
|
26
46
|
/** Text projection of user input — for confirm-gate parsing, choice matching, and
|
|
27
47
|
* extraction hints. Non-text parts are dropped. A plain string returns as-is. */
|
|
28
48
|
export function userInputToText(input) {
|
|
@@ -122,7 +122,11 @@ export function createFsTool(opts) {
|
|
|
122
122
|
const { fs, readOnly = DEFAULT_READ_ONLY, timeoutMs } = opts;
|
|
123
123
|
return defineTool({
|
|
124
124
|
name: 'workspace',
|
|
125
|
-
description: 'Explore and edit the agent workspace.
|
|
125
|
+
description: 'Explore and edit the agent workspace. Cheapest first: ls/find to locate files, ' +
|
|
126
|
+
'grep for exact terms, names, codes, or keywords (returns matching lines only), ' +
|
|
127
|
+
'cat/read for full file contents. Prefer grep over semantic knowledge search when ' +
|
|
128
|
+
'the user mentions an exact term or identifier — it is faster, cheaper, and exact. ' +
|
|
129
|
+
'Ops: ls, cat, grep, find, read, write, edit.',
|
|
126
130
|
timeoutMs,
|
|
127
131
|
input: workspaceInput,
|
|
128
132
|
execute: async (args) => {
|
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.
|
|
9
|
+
"version": "0.10.0",
|
|
10
10
|
"description": "A framework for structured conversational AI agents",
|
|
11
11
|
"publishConfig": {
|
|
12
12
|
"access": "public"
|
|
@@ -103,7 +103,7 @@
|
|
|
103
103
|
"typescript": "^5.3.0",
|
|
104
104
|
"vitest": "^3.2.4",
|
|
105
105
|
"zod": "^4.0.0",
|
|
106
|
-
"@kuralle-agents/realtime-audio": "0.
|
|
106
|
+
"@kuralle-agents/realtime-audio": "0.10.0"
|
|
107
107
|
},
|
|
108
108
|
"dependencies": {
|
|
109
109
|
"chrono-node": "^2.6.0"
|