@kuralle-agents/core 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/dist/debug.d.ts +3 -0
- package/dist/debug.js +7 -0
- package/dist/flow/emitInteractive.d.ts +3 -0
- package/dist/flow/emitInteractive.js +43 -0
- package/dist/flow/extraction.d.ts +0 -6
- package/dist/flow/extraction.js +6 -6
- package/dist/flow/nodeBuilders.d.ts +0 -2
- package/dist/flow/nodeBuilders.js +1 -1
- package/dist/flow/normalizeTransition.d.ts +0 -1
- package/dist/flow/normalizeTransition.js +1 -1
- package/dist/flow/reduceTransition.js +2 -0
- package/dist/flow/runFlow.d.ts +1 -3
- package/dist/flow/runFlow.js +4 -2
- package/dist/flows/extraction.d.ts +0 -7
- package/dist/flows/extraction.js +0 -16
- package/dist/flows/template.d.ts +2 -3
- package/dist/flows/template.js +0 -3
- package/dist/flows/transitions.d.ts +4 -32
- package/dist/flows/transitions.js +0 -20
- package/dist/guards/ToolEnforcer.js +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +1 -1
- package/dist/runtime/Runtime.d.ts +4 -0
- package/dist/runtime/Runtime.js +9 -0
- package/dist/runtime/channels/TextDriver.js +14 -10
- package/dist/runtime/channels/VoiceDriver.js +14 -10
- package/dist/runtime/channels/index.d.ts +1 -1
- package/dist/runtime/channels/index.js +1 -1
- package/dist/runtime/channels/voiceTools.d.ts +0 -1
- package/dist/runtime/channels/voiceTools.js +0 -10
- package/dist/runtime/citations/index.d.ts +1 -12
- package/dist/runtime/citations/index.js +0 -36
- package/dist/runtime/contextOverflow.d.ts +0 -11
- package/dist/runtime/contextOverflow.js +0 -23
- package/dist/runtime/ctx.d.ts +2 -2
- package/dist/runtime/ctx.js +20 -1
- package/dist/runtime/durable/idempotency.d.ts +0 -1
- package/dist/runtime/durable/idempotency.js +1 -1
- package/dist/runtime/durable/replay.d.ts +0 -1
- package/dist/runtime/durable/replay.js +1 -1
- package/dist/runtime/durable/types.d.ts +2 -1
- package/dist/runtime/openRun.d.ts +2 -0
- package/dist/runtime/openRun.js +9 -3
- package/dist/runtime/policies/agentTurn.d.ts +0 -5
- package/dist/runtime/policies/agentTurn.js +0 -6
- package/dist/runtime/policies/limits.d.ts +0 -1
- package/dist/runtime/policies/limits.js +1 -1
- package/dist/runtime/policies/resolvePolicies.d.ts +0 -3
- package/dist/runtime/policies/resolvePolicies.js +0 -13
- package/dist/runtime/promptCache.d.ts +0 -4
- package/dist/tools/Tool.d.ts +0 -6
- package/dist/tools/Tool.js +0 -8
- package/dist/tools/effect/ToolExecutor.d.ts +2 -0
- package/dist/tools/effect/ToolExecutor.js +6 -1
- package/dist/tools/effect/errors.d.ts +10 -0
- package/dist/tools/effect/errors.js +15 -0
- package/dist/tools/effect/index.d.ts +1 -0
- package/dist/tools/effect/index.js +1 -0
- package/dist/tools/errorHandling.d.ts +0 -1
- package/dist/types/channel.d.ts +2 -1
- package/dist/types/flow.d.ts +3 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +1 -0
- package/dist/types/run-context.d.ts +7 -0
- package/dist/types/selection.d.ts +18 -0
- package/dist/types/selection.js +1 -0
- package/dist/types/stream.d.ts +11 -0
- package/package.json +14 -10
- package/dist/events/index.d.ts +0 -2
- package/dist/events/index.js +0 -1
- package/dist/flow/index.d.ts +0 -10
- package/dist/flow/index.js +0 -10
- package/dist/runtime/SessionEventManager.d.ts +0 -16
- package/dist/runtime/SessionEventManager.js +0 -148
- package/dist/runtime/durable/index.d.ts +0 -5
- package/dist/runtime/durable/index.js +0 -5
- package/dist/runtime/policies/index.d.ts +0 -3
- package/dist/runtime/policies/index.js +0 -3
- package/dist/runtime/tokenSessionUtils.d.ts +0 -5
- package/dist/runtime/tokenSessionUtils.js +0 -24
- package/dist/tools/transferToTriage.d.ts +0 -15
- package/dist/tools/transferToTriage.js +0 -48
- package/dist/utils/aiStream.d.ts +0 -6
- package/dist/utils/aiStream.js +0 -48
- package/dist/utils/streamChunk.d.ts +0 -4
- package/dist/utils/streamChunk.js +0 -49
package/dist/runtime/Runtime.js
CHANGED
|
@@ -19,6 +19,7 @@ export class Runtime {
|
|
|
19
19
|
sessionStore;
|
|
20
20
|
defaultModel;
|
|
21
21
|
maxHandoffs;
|
|
22
|
+
terminalHandoffTargets;
|
|
22
23
|
hooks;
|
|
23
24
|
activeTurnAborts = new Map();
|
|
24
25
|
constructor(config) {
|
|
@@ -27,6 +28,7 @@ export class Runtime {
|
|
|
27
28
|
this.sessionStore = config.sessionStore ?? new MemoryStore();
|
|
28
29
|
this.defaultModel = config.defaultModel;
|
|
29
30
|
this.maxHandoffs = config.maxHandoffs ?? 5;
|
|
31
|
+
this.terminalHandoffTargets = new Set(config.terminalHandoffTargets ?? ['human']);
|
|
30
32
|
this.hooks = config.hooks;
|
|
31
33
|
}
|
|
32
34
|
run(opts) {
|
|
@@ -47,6 +49,7 @@ export class Runtime {
|
|
|
47
49
|
sessionId,
|
|
48
50
|
userId: opts.userId,
|
|
49
51
|
input: opts.input,
|
|
52
|
+
selection: opts.selection,
|
|
50
53
|
agentId: opts.agentId,
|
|
51
54
|
seedMessages: opts.seedMessages,
|
|
52
55
|
historyDelta: opts.historyDelta,
|
|
@@ -110,6 +113,12 @@ export class Runtime {
|
|
|
110
113
|
select: this.config.hostSelect,
|
|
111
114
|
});
|
|
112
115
|
if (loopResult.kind === 'handoff') {
|
|
116
|
+
if (this.terminalHandoffTargets.has(loopResult.to)) {
|
|
117
|
+
emit({ type: 'handoff', targetAgent: loopResult.to, reason: loopResult.reason });
|
|
118
|
+
runCtx.runState.status = 'paused';
|
|
119
|
+
await runCtx.runStore.putRunState(runCtx.runState);
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
113
122
|
handoffCount += 1;
|
|
114
123
|
if (handoffCount > this.maxHandoffs) {
|
|
115
124
|
throw new Error(`maxHandoffs exceeded (${this.maxHandoffs})`);
|
|
@@ -69,16 +69,20 @@ export class TextDriver {
|
|
|
69
69
|
toolCallId: call.toolCallId,
|
|
70
70
|
});
|
|
71
71
|
const localTool = node.localTools?.[call.toolName];
|
|
72
|
-
const toolResult =
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
72
|
+
const toolResult = await ctx.tool(call.toolName, call.input, {
|
|
73
|
+
toolCallId: call.toolCallId,
|
|
74
|
+
...(localTool && {
|
|
75
|
+
def: localTool,
|
|
76
|
+
toolCtx: {
|
|
77
|
+
session: ctx.session,
|
|
78
|
+
runState: ctx.runState,
|
|
79
|
+
tool: ctx.tool.bind(ctx),
|
|
80
|
+
now: ctx.now.bind(ctx),
|
|
81
|
+
uuid: ctx.uuid.bind(ctx),
|
|
82
|
+
emit: ctx.emit.bind(ctx),
|
|
83
|
+
},
|
|
84
|
+
}),
|
|
85
|
+
});
|
|
82
86
|
out.toolResults.push({
|
|
83
87
|
name: call.toolName,
|
|
84
88
|
args: call.input,
|
|
@@ -121,16 +121,20 @@ export class VoiceDriver {
|
|
|
121
121
|
try {
|
|
122
122
|
ctx.emit({ type: 'tool-call', toolName: name, args, toolCallId: id });
|
|
123
123
|
const localTool = localTools?.[name];
|
|
124
|
-
const toolResult =
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
124
|
+
const toolResult = await ctx.tool(name, args, {
|
|
125
|
+
toolCallId: id,
|
|
126
|
+
...(localTool && {
|
|
127
|
+
def: localTool,
|
|
128
|
+
toolCtx: {
|
|
129
|
+
session: ctx.session,
|
|
130
|
+
runState: ctx.runState,
|
|
131
|
+
tool: ctx.tool.bind(ctx),
|
|
132
|
+
now: ctx.now.bind(ctx),
|
|
133
|
+
uuid: ctx.uuid.bind(ctx),
|
|
134
|
+
emit: ctx.emit.bind(ctx),
|
|
135
|
+
},
|
|
136
|
+
}),
|
|
137
|
+
});
|
|
134
138
|
out.toolResults.push({ name, args, result: toolResult, toolCallId: id });
|
|
135
139
|
toolCallsMade.push({
|
|
136
140
|
toolCallId: id,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export type { ChannelDriver } from '../../types/channel.js';
|
|
2
2
|
export { TextDriver, buildNodePrompt } from './TextDriver.js';
|
|
3
3
|
export type { TextDriverConfig } from './TextDriver.js';
|
|
4
|
-
export { VoiceDriver
|
|
4
|
+
export { VoiceDriver } from './VoiceDriver.js';
|
|
5
5
|
export type { VoiceDriverConfig } from './VoiceDriver.js';
|
|
6
6
|
export { resolveVoiceGeminiTools, v2ToolsToGemini } from './voiceTools.js';
|
|
7
7
|
export { setPendingUserInput, consumePendingUserInput, peekPendingUserInput } from './inputBuffer.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { TextDriver, buildNodePrompt } from './TextDriver.js';
|
|
2
|
-
export { VoiceDriver
|
|
2
|
+
export { VoiceDriver } from './VoiceDriver.js';
|
|
3
3
|
export { resolveVoiceGeminiTools, v2ToolsToGemini } from './voiceTools.js';
|
|
4
4
|
export { setPendingUserInput, consumePendingUserInput, peekPendingUserInput } from './inputBuffer.js';
|
|
@@ -4,5 +4,4 @@ import type { AnyTool } from '../../types/effectTool.js';
|
|
|
4
4
|
import { buildToolSet } from '../../tools/effect/defineTool.js';
|
|
5
5
|
export declare function v2ToolsToGemini(tools: Record<string, AnyTool>): GeminiFunctionDeclaration[];
|
|
6
6
|
export declare function resolveVoiceGeminiTools(resolved: ResolvedNode, toolDefs: Record<string, AnyTool>): GeminiFunctionDeclaration[];
|
|
7
|
-
export declare function mergeVoiceToolDefs(resolved: ResolvedNode, toolDefs: Record<string, AnyTool>): Record<string, AnyTool>;
|
|
8
7
|
export { buildToolSet };
|
|
@@ -36,14 +36,4 @@ function toolSetToEffectTools(tools) {
|
|
|
36
36
|
}
|
|
37
37
|
return out;
|
|
38
38
|
}
|
|
39
|
-
export function mergeVoiceToolDefs(resolved, toolDefs) {
|
|
40
|
-
const merged = { ...toolDefs, ...(resolved.localTools ?? {}) };
|
|
41
|
-
const fromNode = toolSetToEffectTools(resolved.tools);
|
|
42
|
-
for (const [name, tool] of Object.entries(fromNode)) {
|
|
43
|
-
if (!merged[name]) {
|
|
44
|
-
merged[name] = tool;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
return merged;
|
|
48
|
-
}
|
|
49
39
|
export { buildToolSet };
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type { AgentKnowledgeOverrides, HarnessStreamPart, KnowledgeRetrievalResult, RetrievalCacheAdapter,
|
|
2
|
-
export declare const PENDING_CITATIONS_KEY = "__pendingCitations";
|
|
1
|
+
import type { AgentKnowledgeOverrides, HarnessStreamPart, KnowledgeRetrievalResult, RetrievalCacheAdapter, SourceRef } from '../../types/index.js';
|
|
3
2
|
export interface CitationRetrievalProvider {
|
|
4
3
|
retrieve(query: string, cache: RetrievalCacheAdapter | undefined, agentOverrides?: AgentKnowledgeOverrides, isVoice?: boolean): Promise<{
|
|
5
4
|
results: KnowledgeRetrievalResult[];
|
|
@@ -14,13 +13,3 @@ export interface CitationRetrievalResult {
|
|
|
14
13
|
}
|
|
15
14
|
export declare function retrieveWithCitations(provider: CitationRetrievalProvider, query: string, cache: RetrievalCacheAdapter | undefined, agentOverrides?: AgentKnowledgeOverrides, isVoice?: boolean): Promise<CitationRetrievalResult>;
|
|
16
15
|
export declare function normalizeCitations(results: KnowledgeRetrievalResult[], nativeCitations?: readonly SourceRef[]): SourceRef[];
|
|
17
|
-
export declare function getPendingCitations(session: Session): SourceRef[];
|
|
18
|
-
export declare function popPendingCitations(session: Session): SourceRef[];
|
|
19
|
-
export declare function withCitationMetadata<T extends {
|
|
20
|
-
role?: unknown;
|
|
21
|
-
metadata?: unknown;
|
|
22
|
-
}>(message: T, citations: readonly SourceRef[]): T;
|
|
23
|
-
export declare function withCitationsOnLastAssistantMessage<T extends {
|
|
24
|
-
role?: unknown;
|
|
25
|
-
metadata?: unknown;
|
|
26
|
-
}>(messages: readonly T[], citations: readonly SourceRef[]): T[];
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
|
-
export const PENDING_CITATIONS_KEY = '__pendingCitations';
|
|
3
2
|
export async function retrieveWithCitations(provider, query, cache, agentOverrides, isVoice = false) {
|
|
4
3
|
const raw = await provider.retrieve(query, cache, agentOverrides, isVoice);
|
|
5
4
|
return {
|
|
@@ -20,41 +19,6 @@ export function normalizeCitations(results, nativeCitations) {
|
|
|
20
19
|
}
|
|
21
20
|
return Array.from(byId.values()).sort((a, b) => (b.score ?? 0) - (a.score ?? 0));
|
|
22
21
|
}
|
|
23
|
-
export function getPendingCitations(session) {
|
|
24
|
-
const pending = session.workingMemory[PENDING_CITATIONS_KEY];
|
|
25
|
-
return Array.isArray(pending) ? pending : [];
|
|
26
|
-
}
|
|
27
|
-
export function popPendingCitations(session) {
|
|
28
|
-
const citations = getPendingCitations(session);
|
|
29
|
-
delete session.workingMemory[PENDING_CITATIONS_KEY];
|
|
30
|
-
return citations;
|
|
31
|
-
}
|
|
32
|
-
export function withCitationMetadata(message, citations) {
|
|
33
|
-
if (message.role !== 'assistant' || citations.length === 0)
|
|
34
|
-
return message;
|
|
35
|
-
const metadata = typeof message.metadata === 'object' && message.metadata !== null
|
|
36
|
-
? { ...message.metadata }
|
|
37
|
-
: {};
|
|
38
|
-
return {
|
|
39
|
-
...message,
|
|
40
|
-
metadata: {
|
|
41
|
-
...metadata,
|
|
42
|
-
citations,
|
|
43
|
-
},
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
export function withCitationsOnLastAssistantMessage(messages, citations) {
|
|
47
|
-
if (citations.length === 0)
|
|
48
|
-
return [...messages];
|
|
49
|
-
const next = [...messages];
|
|
50
|
-
for (let index = next.length - 1; index >= 0; index--) {
|
|
51
|
-
if (next[index].role === 'assistant') {
|
|
52
|
-
next[index] = withCitationMetadata(next[index], citations);
|
|
53
|
-
break;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
return next;
|
|
57
|
-
}
|
|
58
22
|
function sourceRefFromResult(result) {
|
|
59
23
|
const sourceId = result.sourceId || `synthetic-${hashShort(result.text)}`;
|
|
60
24
|
return {
|
|
@@ -30,11 +30,6 @@
|
|
|
30
30
|
* cost of a false negative is a fatal turn loss. Bias OR.
|
|
31
31
|
*/
|
|
32
32
|
import type { Session } from '../types/index.js';
|
|
33
|
-
/** Error subclass surfaced to TurnPipeline when overflow recovery fails twice. */
|
|
34
|
-
export declare class ContextOverflowUnrecoverableError extends Error {
|
|
35
|
-
readonly cause?: unknown;
|
|
36
|
-
constructor(cause?: unknown);
|
|
37
|
-
}
|
|
38
33
|
/**
|
|
39
34
|
* Returns true if the error is a provider context-overflow / "prompt too
|
|
40
35
|
* long" condition. Matches on AI SDK APICallError + raw provider errors +
|
|
@@ -44,12 +39,6 @@ export declare class ContextOverflowUnrecoverableError extends Error {
|
|
|
44
39
|
* (fatal turn loss).
|
|
45
40
|
*/
|
|
46
41
|
export declare function isContextOverflowError(error: unknown): boolean;
|
|
47
|
-
/**
|
|
48
|
-
* APICallError-aware check that also recognizes the AI SDK v6 type guard.
|
|
49
|
-
* Exported separately so callers can compose with other classifiers without
|
|
50
|
-
* paying the message-regex cost when the type tells them everything.
|
|
51
|
-
*/
|
|
52
|
-
export declare function isApiCallContextOverflow(error: unknown): boolean;
|
|
53
42
|
export interface OverflowRecoveryResult {
|
|
54
43
|
/** Whether at least one message was stripped from session.messages. */
|
|
55
44
|
stripped: boolean;
|
|
@@ -1,16 +1,3 @@
|
|
|
1
|
-
// AI SDK v6 ships APICallError both directly via 'ai' and re-exports the
|
|
2
|
-
// '@ai-sdk/provider' definition. We import the type only — runtime check
|
|
3
|
-
// uses `APICallError.isInstance` via the imported class.
|
|
4
|
-
import { APICallError } from 'ai';
|
|
5
|
-
/** Error subclass surfaced to TurnPipeline when overflow recovery fails twice. */
|
|
6
|
-
export class ContextOverflowUnrecoverableError extends Error {
|
|
7
|
-
cause;
|
|
8
|
-
constructor(cause) {
|
|
9
|
-
super('Context overflow could not be recovered after retry');
|
|
10
|
-
this.name = 'ContextOverflowUnrecoverableError';
|
|
11
|
-
this.cause = cause;
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
1
|
// Pre-compiled message patterns — keep tight and audited; loose patterns
|
|
15
2
|
// inflate false positives across providers.
|
|
16
3
|
const OVERFLOW_MESSAGE_PATTERNS = [
|
|
@@ -109,16 +96,6 @@ export function isContextOverflowError(error) {
|
|
|
109
96
|
}
|
|
110
97
|
return false;
|
|
111
98
|
}
|
|
112
|
-
/**
|
|
113
|
-
* APICallError-aware check that also recognizes the AI SDK v6 type guard.
|
|
114
|
-
* Exported separately so callers can compose with other classifiers without
|
|
115
|
-
* paying the message-regex cost when the type tells them everything.
|
|
116
|
-
*/
|
|
117
|
-
export function isApiCallContextOverflow(error) {
|
|
118
|
-
if (!APICallError.isInstance(error))
|
|
119
|
-
return false;
|
|
120
|
-
return isContextOverflowError(error);
|
|
121
|
-
}
|
|
122
99
|
/**
|
|
123
100
|
* Strip partial assistant + tool messages that followed the most recent
|
|
124
101
|
* user message — these are in-flight work from the turn that overflowed.
|
package/dist/runtime/ctx.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ import type { InputProcessor, OutputProcessor } from '../types/processors.js';
|
|
|
8
8
|
import type { Limits } from '../types/guardrails.js';
|
|
9
9
|
import type { RunState, StepRecord } from './durable/types.js';
|
|
10
10
|
import type { RunStore } from './durable/RunStore.js';
|
|
11
|
-
|
|
11
|
+
interface EffectClock {
|
|
12
12
|
now(): number;
|
|
13
13
|
uuid(): string;
|
|
14
14
|
}
|
|
@@ -32,5 +32,5 @@ export interface CtxDeps {
|
|
|
32
32
|
clock?: EffectClock;
|
|
33
33
|
emit?: (part: HarnessStreamPart) => void;
|
|
34
34
|
}
|
|
35
|
-
export declare function makeCtx(deps: CtxDeps): RunContext;
|
|
36
35
|
export declare function createRunContext(deps: CtxDeps): Promise<RunContext>;
|
|
36
|
+
export {};
|
package/dist/runtime/ctx.js
CHANGED
|
@@ -2,8 +2,9 @@ import { randomUUID } from 'node:crypto';
|
|
|
2
2
|
import { SuspendError } from './durable/RunStore.js';
|
|
3
3
|
import { clockEffectKey, pauseEffectKey, toolEffectKey, } from './durable/idempotency.js';
|
|
4
4
|
import { findStepByKey } from './durable/replay.js';
|
|
5
|
+
import { ToolApprovalDeniedError } from '../tools/effect/errors.js';
|
|
5
6
|
const APPROVAL_SIGNAL = '__approval';
|
|
6
|
-
|
|
7
|
+
function makeCtx(deps) {
|
|
7
8
|
let effectOrdinal = 0;
|
|
8
9
|
const steps = deps.steps;
|
|
9
10
|
const clock = deps.clock ?? {
|
|
@@ -108,6 +109,22 @@ export function makeCtx(deps) {
|
|
|
108
109
|
bargeIn: deps.bargeIn,
|
|
109
110
|
abortSignal: deps.abortSignal,
|
|
110
111
|
tool: async (name, args, options) => {
|
|
112
|
+
// needsApproval gate: a tool flagged `needsApproval` must be approved by a human
|
|
113
|
+
// before it runs. Approval is a durable pause (the `__approval` signal); on resume
|
|
114
|
+
// the recorded decision is replayed, then the tool effect runs exactly once. The
|
|
115
|
+
// approval pause consumes its own callsite ordinal before the tool effect, so the
|
|
116
|
+
// ordering is deterministic across replays. NOTE: the surrounding agent turn is not
|
|
117
|
+
// itself a replayable effect — this is fully deterministic for flow `action` tools;
|
|
118
|
+
// for model-issued tool calls, resume re-enters the agent turn.
|
|
119
|
+
const def = options?.def ?? deps.toolExecutor.getTool?.(name);
|
|
120
|
+
if (def?.needsApproval) {
|
|
121
|
+
const decision = (await pauseEffect(APPROVAL_SIGNAL, {
|
|
122
|
+
approval: { title: `Approve tool: ${name}` },
|
|
123
|
+
}));
|
|
124
|
+
if (!decision.approved) {
|
|
125
|
+
throw new ToolApprovalDeniedError(name, decision.by);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
111
128
|
const callsite = consumeCallsite();
|
|
112
129
|
const key = toolEffectKey(deps.runState.runId, callsite, name, args);
|
|
113
130
|
return replayOrExecute(key, 'tool', name, () => deps.toolExecutor.execute({
|
|
@@ -116,6 +133,8 @@ export function makeCtx(deps) {
|
|
|
116
133
|
session: deps.session,
|
|
117
134
|
toolCallId: options?.toolCallId,
|
|
118
135
|
abortSignal: deps.bargeIn ?? deps.abortSignal,
|
|
136
|
+
def: options?.def,
|
|
137
|
+
toolCtx: options?.toolCtx,
|
|
119
138
|
}));
|
|
120
139
|
},
|
|
121
140
|
approve: async (req) => {
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export declare function stableStringify(value: unknown): string;
|
|
2
1
|
export declare function idempotencyKey(runId: string, callsite: string, payload: unknown): string;
|
|
3
2
|
export declare function toolEffectKey(runId: string, callsite: string, name: string, args: unknown): string;
|
|
4
3
|
export declare function pauseEffectKey(runId: string, callsite: string, name: string): string;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { RunState, SignalDelivery, StepRecord } from './types.js';
|
|
2
2
|
import type { RunStore } from './RunStore.js';
|
|
3
3
|
export declare function findStepByKey(steps: StepRecord[], key: string): StepRecord | undefined;
|
|
4
|
-
export declare function findStepBySignalId(steps: StepRecord[], signalId: string): StepRecord | undefined;
|
|
5
4
|
export declare function recordSignalDelivery(runStore: RunStore, runState: RunState, delivery: SignalDelivery): Promise<boolean>;
|
|
6
5
|
export declare function loadRecordedSteps(runStore: RunStore, runId: string): Promise<StepRecord[]>;
|
|
@@ -2,7 +2,7 @@ import { pauseEffectKey } from './idempotency.js';
|
|
|
2
2
|
export function findStepByKey(steps, key) {
|
|
3
3
|
return steps.find((step) => step.key === key);
|
|
4
4
|
}
|
|
5
|
-
|
|
5
|
+
function findStepBySignalId(steps, signalId) {
|
|
6
6
|
return steps.find((step) => step.signalId === signalId);
|
|
7
7
|
}
|
|
8
8
|
export async function recordSignalDelivery(runStore, runState, delivery) {
|
|
@@ -14,7 +14,7 @@ export interface StepRecord {
|
|
|
14
14
|
startedAt: number;
|
|
15
15
|
finishedAt?: number;
|
|
16
16
|
}
|
|
17
|
-
|
|
17
|
+
interface WaitingFor {
|
|
18
18
|
signalName: string;
|
|
19
19
|
callsite: string;
|
|
20
20
|
deadline?: number;
|
|
@@ -48,3 +48,4 @@ export interface PersistedRun {
|
|
|
48
48
|
}
|
|
49
49
|
export declare const DURABLE_RUNS_KEY: "durableRuns";
|
|
50
50
|
export type SessionDurableRuns = Record<string, PersistedRun>;
|
|
51
|
+
export {};
|
|
@@ -5,10 +5,12 @@ import type { AgentConfig } from '../types/agentConfig.js';
|
|
|
5
5
|
import type { SignalDelivery } from './durable/types.js';
|
|
6
6
|
import { SessionRunStore } from './durable/SessionRunStore.js';
|
|
7
7
|
import type { RunState } from './durable/types.js';
|
|
8
|
+
import type { ResolvedSelection } from '../types/selection.js';
|
|
8
9
|
export interface OpenRunOptions {
|
|
9
10
|
sessionId: string;
|
|
10
11
|
userId?: string;
|
|
11
12
|
input?: string;
|
|
13
|
+
selection?: ResolvedSelection;
|
|
12
14
|
agentId?: string;
|
|
13
15
|
seedMessages?: ModelMessage[];
|
|
14
16
|
historyDelta?: ModelMessage[];
|
package/dist/runtime/openRun.js
CHANGED
|
@@ -40,16 +40,22 @@ export async function openRun(agentsById, options) {
|
|
|
40
40
|
await recordSignalDelivery(runStore, runState, options.signalDelivery);
|
|
41
41
|
runState = (await runStore.getRunState(runId)) ?? runState;
|
|
42
42
|
}
|
|
43
|
-
if (options.
|
|
43
|
+
if (options.selection?.formData) {
|
|
44
|
+
runState.state = { ...runState.state, ...options.selection.formData };
|
|
45
|
+
runState.updatedAt = Date.now();
|
|
46
|
+
await runStore.putRunState(runState);
|
|
47
|
+
}
|
|
48
|
+
const effectiveInput = options.selection?.id ?? options.input;
|
|
49
|
+
if (effectiveInput) {
|
|
44
50
|
runState.updatedAt = Date.now();
|
|
45
51
|
if (runState.activeFlow) {
|
|
46
52
|
await runStore.putRunState(runState);
|
|
47
53
|
const sessionAfterPersist = (await options.sessionStore.get(options.sessionId)) ?? session;
|
|
48
|
-
setPendingUserInput(sessionAfterPersist,
|
|
54
|
+
setPendingUserInput(sessionAfterPersist, effectiveInput);
|
|
49
55
|
await options.sessionStore.save(sessionAfterPersist);
|
|
50
56
|
}
|
|
51
57
|
else {
|
|
52
|
-
const userMessage = { role: 'user', content:
|
|
58
|
+
const userMessage = { role: 'user', content: effectiveInput };
|
|
53
59
|
runState.messages = [...runState.messages, userMessage];
|
|
54
60
|
runState.updatedAt = Date.now();
|
|
55
61
|
await runStore.putRunState(runState);
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
import type { InputProcessor, OutputProcessor } from '../../types/processors.js';
|
|
2
1
|
import type { ToolCallRecord } from '../../types/session.js';
|
|
3
2
|
import type { RunContext } from '../../types/run-context.js';
|
|
4
|
-
import type { RefinementCapability } from '../../capabilities/RefinementCapability.js';
|
|
5
|
-
import type { ValidationCapability } from '../../capabilities/ValidationCapability.js';
|
|
6
3
|
export interface PreTurnResult {
|
|
7
4
|
proceed: boolean;
|
|
8
5
|
userMessage: string;
|
|
@@ -15,5 +12,3 @@ export interface PostTurnResult {
|
|
|
15
12
|
}
|
|
16
13
|
export declare function applyPreTurnPolicies(ctx: RunContext): Promise<PreTurnResult>;
|
|
17
14
|
export declare function applyPostTurnPolicies(ctx: RunContext, assistantOutput: string, toolCallsMade?: ToolCallRecord[]): Promise<PostTurnResult>;
|
|
18
|
-
export declare function buildValidationPolicies(outputProcessors: OutputProcessor[], moderators: ValidationCapability[]): ValidationCapability[];
|
|
19
|
-
export declare function buildRefinementPolicies(inputProcessors: InputProcessor[], refinements: RefinementCapability[]): RefinementCapability[];
|
|
@@ -129,9 +129,3 @@ function patchLatestUserMessage(messages, next) {
|
|
|
129
129
|
}
|
|
130
130
|
messages.push({ role: 'user', content: next });
|
|
131
131
|
}
|
|
132
|
-
export function buildValidationPolicies(outputProcessors, moderators) {
|
|
133
|
-
return [...moderators];
|
|
134
|
-
}
|
|
135
|
-
export function buildRefinementPolicies(inputProcessors, refinements) {
|
|
136
|
-
return [...refinements];
|
|
137
|
-
}
|
|
@@ -3,7 +3,6 @@ import type { RunState } from '../durable/types.js';
|
|
|
3
3
|
export declare class LimitsExceededError extends Error {
|
|
4
4
|
constructor(message: string);
|
|
5
5
|
}
|
|
6
|
-
export declare function readTurnCount(run: RunState): number;
|
|
7
6
|
export declare function incrementTurnCount(run: RunState): number;
|
|
8
7
|
export declare function assertWithinTurnLimit(run: RunState, limits?: Limits): void;
|
|
9
8
|
export declare function resolveMaxSteps(limits: Limits | undefined, fallback: number): number;
|
|
@@ -2,7 +2,6 @@ import type { AgentConfig } from '../../types/agentConfig.js';
|
|
|
2
2
|
import type { RefinementCapability } from '../../capabilities/RefinementCapability.js';
|
|
3
3
|
import type { ValidationCapability } from '../../capabilities/ValidationCapability.js';
|
|
4
4
|
import type { InputProcessor, OutputProcessor } from '../../types/processors.js';
|
|
5
|
-
import type { EnforcementRule } from '../../types/tool.js';
|
|
6
5
|
import type { Limits } from '../../types/guardrails.js';
|
|
7
6
|
import { type ToolEnforcer } from '../../guards/ToolEnforcer.js';
|
|
8
7
|
export interface ResolvedAgentPolicies {
|
|
@@ -14,5 +13,3 @@ export interface ResolvedAgentPolicies {
|
|
|
14
13
|
enforcer?: ToolEnforcer;
|
|
15
14
|
}
|
|
16
15
|
export declare function resolveAgentPolicies(agent: AgentConfig): ResolvedAgentPolicies;
|
|
17
|
-
export declare function withValidationPolicies(base: ResolvedAgentPolicies, validationPolicies: ValidationCapability[]): ResolvedAgentPolicies;
|
|
18
|
-
export declare function withEnforcementRules(base: ResolvedAgentPolicies, rules: EnforcementRule[]): ResolvedAgentPolicies;
|
|
@@ -11,16 +11,3 @@ export function resolveAgentPolicies(agent) {
|
|
|
11
11
|
enforcer: enforcementRules.length > 0 ? createToolEnforcer(enforcementRules) : undefined,
|
|
12
12
|
};
|
|
13
13
|
}
|
|
14
|
-
export function withValidationPolicies(base, validationPolicies) {
|
|
15
|
-
return {
|
|
16
|
-
...base,
|
|
17
|
-
validationPolicies: [...base.validationPolicies, ...validationPolicies],
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
export function withEnforcementRules(base, rules) {
|
|
21
|
-
const merged = [...(base.enforcer?.getRules() ?? []), ...rules];
|
|
22
|
-
return {
|
|
23
|
-
...base,
|
|
24
|
-
enforcer: merged.length > 0 ? createToolEnforcer(merged) : undefined,
|
|
25
|
-
};
|
|
26
|
-
}
|
|
@@ -25,10 +25,6 @@
|
|
|
25
25
|
*/
|
|
26
26
|
import type { ModelMessage } from 'ai';
|
|
27
27
|
export type AnthropicCacheTtl = '5m' | '1h';
|
|
28
|
-
export interface AnthropicCacheControl {
|
|
29
|
-
type: 'ephemeral';
|
|
30
|
-
ttl?: '1h';
|
|
31
|
-
}
|
|
32
28
|
/**
|
|
33
29
|
* Returns a SHALLOW-COPIED message list with cache_control markers
|
|
34
30
|
* applied to the system message (if present) + the last 3 non-system
|
package/dist/tools/Tool.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { type Tool as AiTool, type ToolSet as AiToolSet, type ToolExecutionOptions as AiToolExecutionOptions, type ToolExecuteFunction } from 'ai';
|
|
2
|
-
import type { Runtime } from '../runtime/Runtime.js';
|
|
3
2
|
import type { ZodTypeAny, z } from 'zod';
|
|
4
3
|
export type Tool<TInput = unknown, TResult = unknown> = AiTool<TInput, TResult>;
|
|
5
4
|
export type ToolSet = AiToolSet;
|
|
@@ -16,11 +15,6 @@ export interface ToolExecutionContext {
|
|
|
16
15
|
[key: string]: unknown;
|
|
17
16
|
}
|
|
18
17
|
export type ToolExecutionOptions = AiToolExecutionOptions;
|
|
19
|
-
export type ToolExecutionContextWithRuntime = ToolExecutionContext & {
|
|
20
|
-
runtime: Runtime;
|
|
21
|
-
};
|
|
22
|
-
export declare function isAgentContext(value: unknown): value is ToolExecutionContextWithRuntime;
|
|
23
|
-
export declare function getRuntimeFromContext(context: ToolExecutionContext | undefined): Runtime | undefined;
|
|
24
18
|
export interface ToolDefinition<TInput = unknown, TResult = unknown> {
|
|
25
19
|
description: string;
|
|
26
20
|
inputSchema: ZodTypeAny;
|
package/dist/tools/Tool.js
CHANGED
|
@@ -1,12 +1,4 @@
|
|
|
1
1
|
import { tool as aiTool, zodSchema, } from 'ai';
|
|
2
|
-
export function isAgentContext(value) {
|
|
3
|
-
return (typeof value === 'object' &&
|
|
4
|
-
value !== null &&
|
|
5
|
-
'runtime' in value);
|
|
6
|
-
}
|
|
7
|
-
export function getRuntimeFromContext(context) {
|
|
8
|
-
return context?.runtime;
|
|
9
|
-
}
|
|
10
2
|
export function createTool(definition) {
|
|
11
3
|
const { description, inputSchema, execute } = definition;
|
|
12
4
|
// AI SDK tool() overload resolution fails when INPUT/OUTPUT are deferred wrapper generics.
|
|
@@ -17,6 +17,8 @@ export interface CoreExecuteArgs {
|
|
|
17
17
|
abortSignal?: AbortSignal;
|
|
18
18
|
toolCallId?: string;
|
|
19
19
|
toolCtx?: ToolContext;
|
|
20
|
+
/** Flow-local tool def passed by the driver; wins over the registry when both exist. */
|
|
21
|
+
def?: AnyTool;
|
|
20
22
|
}
|
|
21
23
|
export declare class CoreToolExecutor implements EffectToolExecutor {
|
|
22
24
|
private readonly tools;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { debug } from '../../debug.js';
|
|
2
3
|
import { cancelledPlaceholder, inProgressPlaceholder, PairingTracker, } from './pairing.js';
|
|
3
4
|
import { ToolValidationError, validateAndSanitize, validateOutput } from './schema.js';
|
|
4
5
|
export class CoreToolExecutor {
|
|
@@ -45,7 +46,11 @@ export class CoreToolExecutor {
|
|
|
45
46
|
}
|
|
46
47
|
async executeInner(args) {
|
|
47
48
|
const { name, session, abortSignal, toolCallId, toolCtx } = args;
|
|
48
|
-
const
|
|
49
|
+
const registryDef = this.tools.get(name);
|
|
50
|
+
if (args.def && registryDef) {
|
|
51
|
+
debug(`[ToolExecutor] flow-local tool "${name}" shadows a same-named registry tool`);
|
|
52
|
+
}
|
|
53
|
+
const def = args.def ?? registryDef;
|
|
49
54
|
if (!def) {
|
|
50
55
|
throw new Error(`Unknown tool: ${name}`);
|
|
51
56
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thrown by `ctx.tool(...)` when a tool declared `needsApproval: true` is denied
|
|
3
|
+
* by a human (the `__approval` signal resolves with `approved: false`). Catch it
|
|
4
|
+
* inside the calling flow `action` node to route gracefully (e.g. escalate or end).
|
|
5
|
+
*/
|
|
6
|
+
export declare class ToolApprovalDeniedError extends Error {
|
|
7
|
+
readonly toolName: string;
|
|
8
|
+
readonly by?: string;
|
|
9
|
+
constructor(toolName: string, by?: string);
|
|
10
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thrown by `ctx.tool(...)` when a tool declared `needsApproval: true` is denied
|
|
3
|
+
* by a human (the `__approval` signal resolves with `approved: false`). Catch it
|
|
4
|
+
* inside the calling flow `action` node to route gracefully (e.g. escalate or end).
|
|
5
|
+
*/
|
|
6
|
+
export class ToolApprovalDeniedError extends Error {
|
|
7
|
+
toolName;
|
|
8
|
+
by;
|
|
9
|
+
constructor(toolName, by) {
|
|
10
|
+
super(`Tool "${toolName}" was denied approval${by ? ` by ${by}` : ''}`);
|
|
11
|
+
this.name = 'ToolApprovalDeniedError';
|
|
12
|
+
this.toolName = toolName;
|
|
13
|
+
this.by = by;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -4,3 +4,4 @@ export type { CoreToolExecutorConfig, CoreExecuteArgs } from './ToolExecutor.js'
|
|
|
4
4
|
export { PairingTracker, cancelledPlaceholder, inProgressPlaceholder, } from './pairing.js';
|
|
5
5
|
export type { ToolCallPair, ToolPairStatus, ToolRequestRecord, ToolResponseRecord, CancelledToolResult, InProgressToolResult, } from './pairing.js';
|
|
6
6
|
export { validateAndSanitize, validateOutput, ToolValidationError } from './schema.js';
|
|
7
|
+
export { ToolApprovalDeniedError } from './errors.js';
|
|
@@ -2,3 +2,4 @@ export { defineTool, toolToAiSdk, buildToolSet } from './defineTool.js';
|
|
|
2
2
|
export { CoreToolExecutor } from './ToolExecutor.js';
|
|
3
3
|
export { PairingTracker, cancelledPlaceholder, inProgressPlaceholder, } from './pairing.js';
|
|
4
4
|
export { validateAndSanitize, validateOutput, ToolValidationError } from './schema.js';
|
|
5
|
+
export { ToolApprovalDeniedError } from './errors.js';
|
|
@@ -20,7 +20,6 @@ export declare function executeWithRetry<T>(fn: () => Promise<T>, config?: {
|
|
|
20
20
|
maxDelayMs?: number;
|
|
21
21
|
onRetry?: (error: Error, attempt: number) => void;
|
|
22
22
|
}): Promise<T>;
|
|
23
|
-
export type CircuitState = 'CLOSED' | 'OPEN' | 'HALF_OPEN';
|
|
24
23
|
export declare function createCircuitBreaker<T, Args extends unknown[]>(fn: (...args: Args) => Promise<T>, config?: {
|
|
25
24
|
failureThreshold?: number;
|
|
26
25
|
resetTimeoutMs?: number;
|
package/dist/types/channel.d.ts
CHANGED
|
@@ -27,7 +27,7 @@ export type UserSignal = {
|
|
|
27
27
|
type: 'message';
|
|
28
28
|
input: string;
|
|
29
29
|
};
|
|
30
|
-
|
|
30
|
+
interface ToolResultRecord {
|
|
31
31
|
name: string;
|
|
32
32
|
args: unknown;
|
|
33
33
|
result: unknown;
|
|
@@ -41,3 +41,4 @@ export type TurnControl = {
|
|
|
41
41
|
type: 'end';
|
|
42
42
|
reason: string;
|
|
43
43
|
};
|
|
44
|
+
export {};
|