@kuralle-agents/core 0.4.1 → 0.6.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 +12 -3
- package/dist/ai-sdk/uiMessageStream.d.ts +51 -0
- package/dist/ai-sdk/uiMessageStream.js +164 -0
- package/dist/events/TurnHandle.js +7 -0
- package/dist/flow/nodeBuilders.d.ts +1 -1
- package/dist/flow/nodeBuilders.js +5 -3
- package/dist/index.d.ts +14 -4
- package/dist/index.js +8 -2
- package/dist/memory/blocks/FilePersistentMemoryStore.d.ts +1 -1
- package/dist/memory/blocks/FilePersistentMemoryStore.js +9 -0
- package/dist/memory/blocks/InMemoryPersistentMemoryStore.d.ts +8 -0
- package/dist/memory/blocks/InMemoryPersistentMemoryStore.js +25 -0
- package/dist/memory/blocks/RoutedPersistentMemoryStore.d.ts +18 -0
- package/dist/memory/blocks/RoutedPersistentMemoryStore.js +35 -0
- package/dist/memory/blocks/TieredPersistentMemoryStore.d.ts +10 -0
- package/dist/memory/blocks/TieredPersistentMemoryStore.js +30 -0
- package/dist/memory/blocks/memoryBlockTool.d.ts +5 -5
- package/dist/memory/blocks/testing.d.ts +11 -0
- package/dist/memory/blocks/testing.js +59 -0
- package/dist/memory/index.d.ts +4 -0
- package/dist/memory/index.js +3 -0
- package/dist/runtime/Runtime.d.ts +3 -0
- package/dist/runtime/Runtime.js +47 -5
- package/dist/runtime/agentReply.js +2 -1
- package/dist/runtime/channels/TextDriver.js +3 -2
- package/dist/runtime/channels/VoiceDriver.js +3 -3
- package/dist/runtime/channels/extractionTurn.js +1 -1
- package/dist/runtime/ctx.d.ts +2 -0
- package/dist/runtime/ctx.js +1 -0
- package/dist/runtime/grounding/defaultStoreRegistry.d.ts +3 -0
- package/dist/runtime/grounding/defaultStoreRegistry.js +10 -0
- package/dist/runtime/grounding/index.d.ts +1 -0
- package/dist/runtime/grounding/index.js +1 -0
- package/dist/runtime/grounding/workingMemory.d.ts +19 -0
- package/dist/runtime/grounding/workingMemory.js +80 -0
- package/dist/runtime/resolveAgentWorkspace.d.ts +10 -0
- package/dist/runtime/resolveAgentWorkspace.js +9 -0
- package/dist/skills/SkillsCapability.d.ts +10 -0
- package/dist/skills/SkillsCapability.js +52 -0
- package/dist/skills/collectSkills.d.ts +17 -0
- package/dist/skills/collectSkills.js +56 -0
- package/dist/skills/index.d.ts +5 -0
- package/dist/skills/index.js +4 -0
- package/dist/skills/inlineSkillStore.d.ts +9 -0
- package/dist/skills/inlineSkillStore.js +37 -0
- package/dist/skills/wireAgentSkills.d.ts +10 -0
- package/dist/skills/wireAgentSkills.js +25 -0
- package/dist/testing/mocks.js +7 -0
- package/dist/tools/effect/defineTool.js +1 -1
- package/dist/tools/effect/index.d.ts +1 -0
- package/dist/tools/effect/index.js +1 -0
- package/dist/tools/effect/wrapAiSdkTool.d.ts +3 -0
- package/dist/tools/effect/wrapAiSdkTool.js +12 -0
- package/dist/tools/fs/createFsTool.d.ts +30 -0
- package/dist/tools/fs/createFsTool.js +200 -0
- package/dist/types/agentConfig.d.ts +16 -3
- package/dist/types/filesystem.d.ts +85 -0
- package/dist/types/filesystem.js +6 -0
- package/dist/types/grounding.d.ts +12 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.js +2 -0
- package/dist/types/run-context.d.ts +11 -2
- package/dist/types/skills.d.ts +19 -0
- package/dist/types/skills.js +1 -0
- package/dist/types/stream.d.ts +3 -0
- package/guides/AGENTS.md +2 -3
- package/guides/FLOWS.md +2 -2
- package/guides/RUNTIME.md +18 -1
- package/guides/TOOLS.md +69 -2
- package/package.json +12 -3
package/dist/testing/mocks.js
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
|
+
import { createUIMessageStreamResponse } from 'ai';
|
|
2
|
+
import { harnessToUIMessageStream } from '../ai-sdk/uiMessageStream.js';
|
|
1
3
|
export function createMockTurnHandle(events, settled = { text: '', toolResults: [] }) {
|
|
2
4
|
return Object.assign(Promise.resolve(settled), {
|
|
3
5
|
events,
|
|
4
6
|
toResponseStream: () => new ReadableStream(),
|
|
7
|
+
toUIMessageStreamResponse(opts) {
|
|
8
|
+
return createUIMessageStreamResponse({
|
|
9
|
+
stream: harnessToUIMessageStream(events, opts),
|
|
10
|
+
});
|
|
11
|
+
},
|
|
5
12
|
cancel: () => { },
|
|
6
13
|
});
|
|
7
14
|
}
|
|
@@ -25,7 +25,7 @@ export function toolToAiSdk(def) {
|
|
|
25
25
|
// `buildToolSet` produces a model-facing ToolSet whose entries are schema-only
|
|
26
26
|
// (`toolToAiSdk` strips `execute`). Stash the raw effect tools (with executors),
|
|
27
27
|
// keyed by the returned ToolSet, so a flow node can recover its executors for
|
|
28
|
-
// in-flow execution without separately registering them on `agent.
|
|
28
|
+
// in-flow execution without separately registering them on `agent.tools`.
|
|
29
29
|
// (see `resolveReplyNode`). The WeakMap is GC-friendly and invisible to callers.
|
|
30
30
|
const rawToolsBySet = new WeakMap();
|
|
31
31
|
export function buildToolSet(tools) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { defineTool, toolToAiSdk, buildToolSet } from './defineTool.js';
|
|
2
|
+
export { wrapAiSdkTool } from './wrapAiSdkTool.js';
|
|
2
3
|
export { CoreToolExecutor } from './ToolExecutor.js';
|
|
3
4
|
export type { CoreToolExecutorConfig, CoreExecuteArgs } from './ToolExecutor.js';
|
|
4
5
|
export { PairingTracker, cancelledPlaceholder, inProgressPlaceholder, } from './pairing.js';
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { defineTool, toolToAiSdk, buildToolSet } from './defineTool.js';
|
|
2
|
+
export { wrapAiSdkTool } from './wrapAiSdkTool.js';
|
|
2
3
|
export { CoreToolExecutor } from './ToolExecutor.js';
|
|
3
4
|
export { PairingTracker, cancelledPlaceholder, inProgressPlaceholder, } from './pairing.js';
|
|
4
5
|
export { validateAndSanitize, validateOutput, ToolValidationError } from './schema.js';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function wrapAiSdkTool(name, aiTool) {
|
|
2
|
+
const exec = aiTool.execute;
|
|
3
|
+
if (typeof exec !== 'function') {
|
|
4
|
+
throw new Error(`wrapAiSdkTool("${name}"): AI SDK tool has no execute; use defineTool for schema-only tools.`);
|
|
5
|
+
}
|
|
6
|
+
return {
|
|
7
|
+
name,
|
|
8
|
+
description: aiTool.description ?? name,
|
|
9
|
+
input: aiTool.inputSchema,
|
|
10
|
+
execute: (args, ctx) => exec(args, ctx),
|
|
11
|
+
};
|
|
12
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { AnyTool } from '../../types/effectTool.js';
|
|
2
|
+
import { type FileSystem } from '../../types/filesystem.js';
|
|
3
|
+
export interface CreateFsToolOptions {
|
|
4
|
+
fs: FileSystem;
|
|
5
|
+
readOnly?: boolean;
|
|
6
|
+
timeoutMs?: number;
|
|
7
|
+
}
|
|
8
|
+
export interface GrepHit {
|
|
9
|
+
path: string;
|
|
10
|
+
line: number;
|
|
11
|
+
text: string;
|
|
12
|
+
}
|
|
13
|
+
export interface FsSearchHit {
|
|
14
|
+
slug: string;
|
|
15
|
+
chunkIndex: number;
|
|
16
|
+
text: string;
|
|
17
|
+
}
|
|
18
|
+
export interface FsWithSearch extends FileSystem {
|
|
19
|
+
search?(pattern: string, opts?: {
|
|
20
|
+
limit?: number;
|
|
21
|
+
path?: string;
|
|
22
|
+
}): Promise<FsSearchHit[]>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* One durable `workspace` tool over a {@link FileSystem} (ls/cat/grep/find/read/write/edit).
|
|
26
|
+
* Lives in core (not `@kuralle-agents/fs`) because it needs only `defineTool` + the
|
|
27
|
+
* `FileSystem` interface — both core-owned — so the runtime can auto-register it with a
|
|
28
|
+
* static import and no core->fs dependency (RFC-02 §5.2). `@kuralle-agents/fs` re-exports it.
|
|
29
|
+
*/
|
|
30
|
+
export declare function createFsTool(opts: CreateFsToolOptions): AnyTool;
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { defineTool } from '../effect/defineTool.js';
|
|
3
|
+
import { fsErrorCode } from '../../types/filesystem.js';
|
|
4
|
+
const DEFAULT_READ_ONLY = true;
|
|
5
|
+
const workspaceInput = z.object({
|
|
6
|
+
op: z.enum(['ls', 'cat', 'grep', 'find', 'read', 'write', 'edit']),
|
|
7
|
+
path: z.string().optional(),
|
|
8
|
+
pattern: z.string().optional(),
|
|
9
|
+
root: z.string().optional(),
|
|
10
|
+
glob: z.string().optional(),
|
|
11
|
+
flags: z.string().optional(),
|
|
12
|
+
content: z.string().optional(),
|
|
13
|
+
find: z.string().optional(),
|
|
14
|
+
replace: z.string().optional(),
|
|
15
|
+
});
|
|
16
|
+
function normalizeFsPath(path, fallback = '/') {
|
|
17
|
+
if (!path || path.trim() === '')
|
|
18
|
+
return fallback;
|
|
19
|
+
return path;
|
|
20
|
+
}
|
|
21
|
+
function requireField(args, field, op) {
|
|
22
|
+
const value = args[field];
|
|
23
|
+
if (typeof value !== 'string' || value.length === 0) {
|
|
24
|
+
throw new Error(`EINVAL: missing ${String(field)} for workspace op '${op}'`);
|
|
25
|
+
}
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
function eroFs(path) {
|
|
29
|
+
return new Error(`EROFS: read-only filesystem, write '${path}'`);
|
|
30
|
+
}
|
|
31
|
+
function assertWritable(readOnly, path) {
|
|
32
|
+
if (readOnly)
|
|
33
|
+
throw eroFs(path);
|
|
34
|
+
}
|
|
35
|
+
async function listFiles(fs, root) {
|
|
36
|
+
const normalized = root === '' ? '/' : root;
|
|
37
|
+
const out = [];
|
|
38
|
+
const stack = [normalized];
|
|
39
|
+
while (stack.length > 0) {
|
|
40
|
+
const dir = stack.pop();
|
|
41
|
+
if (!(await fs.exists(dir))) {
|
|
42
|
+
throw new Error(`ENOENT: no such file or directory, find '${dir}'`);
|
|
43
|
+
}
|
|
44
|
+
const stat = await fs.stat(dir);
|
|
45
|
+
if (stat.type !== 'directory') {
|
|
46
|
+
out.push(dir);
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
const entries = await fs.readdirWithFileTypes(dir);
|
|
50
|
+
for (const entry of entries) {
|
|
51
|
+
const child = fs.resolvePath(dir, entry.name);
|
|
52
|
+
if (entry.type === 'directory') {
|
|
53
|
+
stack.push(child);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
out.push(child);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return out.sort();
|
|
61
|
+
}
|
|
62
|
+
async function grepFiles(fs, pattern, root, flags) {
|
|
63
|
+
let re;
|
|
64
|
+
try {
|
|
65
|
+
re = new RegExp(pattern, flags?.includes('i') ? 'i' : undefined);
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
throw new Error(`EINVAL: invalid grep pattern '${pattern}'`);
|
|
69
|
+
}
|
|
70
|
+
const searchable = fs;
|
|
71
|
+
if (searchable.search) {
|
|
72
|
+
const coarse = await searchable.search(pattern, { limit: 500, path: root });
|
|
73
|
+
const slugs = [...new Set(coarse.map((hit) => hit.slug))];
|
|
74
|
+
const hits = [];
|
|
75
|
+
for (const filePath of slugs) {
|
|
76
|
+
let content;
|
|
77
|
+
try {
|
|
78
|
+
content = await fs.readFile(filePath);
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
if (fsErrorCode(err) === 'EISDIR')
|
|
82
|
+
continue;
|
|
83
|
+
throw err;
|
|
84
|
+
}
|
|
85
|
+
const lines = content.split('\n');
|
|
86
|
+
for (let i = 0; i < lines.length; i++) {
|
|
87
|
+
if (re.test(lines[i])) {
|
|
88
|
+
hits.push({ path: filePath, line: i + 1, text: lines[i] });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return hits;
|
|
93
|
+
}
|
|
94
|
+
const candidates = await listFiles(fs, root);
|
|
95
|
+
const hits = [];
|
|
96
|
+
for (const filePath of candidates) {
|
|
97
|
+
let content;
|
|
98
|
+
try {
|
|
99
|
+
content = await fs.readFile(filePath);
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
if (fsErrorCode(err) === 'EISDIR')
|
|
103
|
+
continue;
|
|
104
|
+
throw err;
|
|
105
|
+
}
|
|
106
|
+
const lines = content.split('\n');
|
|
107
|
+
for (let i = 0; i < lines.length; i++) {
|
|
108
|
+
if (re.test(lines[i])) {
|
|
109
|
+
hits.push({ path: filePath, line: i + 1, text: lines[i] });
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return hits;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* One durable `workspace` tool over a {@link FileSystem} (ls/cat/grep/find/read/write/edit).
|
|
117
|
+
* Lives in core (not `@kuralle-agents/fs`) because it needs only `defineTool` + the
|
|
118
|
+
* `FileSystem` interface — both core-owned — so the runtime can auto-register it with a
|
|
119
|
+
* static import and no core->fs dependency (RFC-02 §5.2). `@kuralle-agents/fs` re-exports it.
|
|
120
|
+
*/
|
|
121
|
+
export function createFsTool(opts) {
|
|
122
|
+
const { fs, readOnly = DEFAULT_READ_ONLY, timeoutMs } = opts;
|
|
123
|
+
return defineTool({
|
|
124
|
+
name: 'workspace',
|
|
125
|
+
description: 'Explore and edit the agent workspace. Ops: ls, cat, grep, find, read, write, edit.',
|
|
126
|
+
timeoutMs,
|
|
127
|
+
input: workspaceInput,
|
|
128
|
+
execute: async (args) => {
|
|
129
|
+
switch (args.op) {
|
|
130
|
+
case 'ls': {
|
|
131
|
+
const path = normalizeFsPath(args.path);
|
|
132
|
+
const entries = await fs.readdirWithFileTypes(path);
|
|
133
|
+
return {
|
|
134
|
+
op: args.op,
|
|
135
|
+
ok: true,
|
|
136
|
+
path,
|
|
137
|
+
entries,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
case 'cat':
|
|
141
|
+
case 'read': {
|
|
142
|
+
const path = requireField(args, 'path', args.op);
|
|
143
|
+
const content = await fs.readFile(path);
|
|
144
|
+
return { op: args.op, ok: true, path, content };
|
|
145
|
+
}
|
|
146
|
+
case 'find': {
|
|
147
|
+
const root = normalizeFsPath(args.root);
|
|
148
|
+
const glob = requireField(args, 'glob', args.op);
|
|
149
|
+
const paths = await fs.glob(glob);
|
|
150
|
+
const rooted = paths.filter((p) => {
|
|
151
|
+
const normalizedRoot = root === '/' ? '/' : root.replace(/\/$/, '');
|
|
152
|
+
return p === normalizedRoot || p.startsWith(`${normalizedRoot}/`);
|
|
153
|
+
});
|
|
154
|
+
return {
|
|
155
|
+
op: args.op,
|
|
156
|
+
ok: true,
|
|
157
|
+
root,
|
|
158
|
+
glob,
|
|
159
|
+
paths: rooted,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
case 'grep': {
|
|
163
|
+
const pattern = requireField(args, 'pattern', args.op);
|
|
164
|
+
const path = normalizeFsPath(args.path);
|
|
165
|
+
const hits = await grepFiles(fs, pattern, path, args.flags);
|
|
166
|
+
return {
|
|
167
|
+
op: args.op,
|
|
168
|
+
ok: true,
|
|
169
|
+
pattern,
|
|
170
|
+
path,
|
|
171
|
+
hits,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
case 'write': {
|
|
175
|
+
const path = requireField(args, 'path', args.op);
|
|
176
|
+
const content = requireField(args, 'content', args.op);
|
|
177
|
+
assertWritable(readOnly, path);
|
|
178
|
+
await fs.writeFile(path, content);
|
|
179
|
+
return { op: args.op, ok: true, path };
|
|
180
|
+
}
|
|
181
|
+
case 'edit': {
|
|
182
|
+
const path = requireField(args, 'path', args.op);
|
|
183
|
+
const find = requireField(args, 'find', args.op);
|
|
184
|
+
const replace = requireField(args, 'replace', args.op);
|
|
185
|
+
assertWritable(readOnly, path);
|
|
186
|
+
const current = await fs.readFile(path);
|
|
187
|
+
if (!current.includes(find)) {
|
|
188
|
+
throw new Error(`ENOENT: find string not found in file, edit '${path}'`);
|
|
189
|
+
}
|
|
190
|
+
const next = current.replace(find, replace);
|
|
191
|
+
await fs.writeFile(path, next);
|
|
192
|
+
return { op: args.op, ok: true, path };
|
|
193
|
+
}
|
|
194
|
+
default: {
|
|
195
|
+
throw new Error(`EINVAL: unknown workspace op '${String(args.op)}'`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
});
|
|
200
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { LanguageModel
|
|
1
|
+
import type { LanguageModel } from 'ai';
|
|
2
2
|
import type { AgentPrompt } from '../prompts/AgentPrompt.js';
|
|
3
3
|
import type { Flow } from './flow.js';
|
|
4
4
|
import type { Route, RoutingPolicy } from './route.js';
|
|
@@ -7,6 +7,12 @@ import type { AgentKnowledge, AgentMemory } from './grounding.js';
|
|
|
7
7
|
import type { RefinementCapability } from '../capabilities/RefinementCapability.js';
|
|
8
8
|
import type { ValidationCapability } from '../capabilities/ValidationCapability.js';
|
|
9
9
|
import type { AnyTool } from './effectTool.js';
|
|
10
|
+
import type { FileSystem } from './filesystem.js';
|
|
11
|
+
import type { SkillSource } from './skills.js';
|
|
12
|
+
export type AgentWorkspaceConfig = FileSystem | {
|
|
13
|
+
fs: FileSystem;
|
|
14
|
+
readOnly?: boolean;
|
|
15
|
+
};
|
|
10
16
|
export type Instructions = string | AgentPrompt | ((ctx: {
|
|
11
17
|
state: Record<string, unknown>;
|
|
12
18
|
}) => Instructions | Promise<Instructions>);
|
|
@@ -20,8 +26,8 @@ export interface AgentConfig {
|
|
|
20
26
|
* temperature 0 for determinism. Defaults to `model` (the speaker) when unset.
|
|
21
27
|
* Set this to pin control to a reliable provider independent of the speaker. */
|
|
22
28
|
controlModel?: LanguageModel;
|
|
23
|
-
tools
|
|
24
|
-
|
|
29
|
+
/** Durable, model-callable effect tools (exactly-once on replay). Wrap raw AI SDK tools with wrapAiSdkTool(). */
|
|
30
|
+
tools?: Record<string, AnyTool>;
|
|
25
31
|
/** Safe, always-available tools made model-visible in EVERY speaking node turn
|
|
26
32
|
* (the agent "base layer", ADR 0001) — e.g. a returns/FAQ knowledge-base
|
|
27
33
|
* lookup the user might ask for mid-flow. This is an explicit allow-list:
|
|
@@ -45,5 +51,12 @@ export interface AgentConfig {
|
|
|
45
51
|
/** Flow reply nodes: silo flow-transition control tools + deterministic evaluator (ADR 0003 H1). Default OFF. */
|
|
46
52
|
outOfBandControl?: boolean;
|
|
47
53
|
};
|
|
54
|
+
/** Portable workspace filesystem; auto-registers the durable `workspace` tool when set.
|
|
55
|
+
* Defaults to read-only; pass `{ fs, readOnly: false }` for write/edit. Read-only workspaces
|
|
56
|
+
* are exposed in globalTools (ADR 0001); read-write ones are executor-only. */
|
|
57
|
+
workspace?: AgentWorkspaceConfig;
|
|
58
|
+
/** Bundled procedural skills (Anthropic Agent Skill model): Level-1 name+description in prompt,
|
|
59
|
+
* body via `load_skill`, resources via `read_skill_resource`. Scripts = `allowedTools` only. */
|
|
60
|
+
skills?: SkillSource;
|
|
48
61
|
}
|
|
49
62
|
export declare function defineAgent(config: AgentConfig): AgentConfig;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
export type FileSystemEntryType = 'file' | 'directory' | 'symlink';
|
|
2
|
+
export type BufferEncoding = 'utf8' | 'utf-8' | 'ascii' | 'binary' | 'base64' | 'hex' | 'latin1';
|
|
3
|
+
export type FileContent = string | Uint8Array;
|
|
4
|
+
export interface FsStat {
|
|
5
|
+
type: FileSystemEntryType;
|
|
6
|
+
size: number;
|
|
7
|
+
mtime: Date;
|
|
8
|
+
mode?: number;
|
|
9
|
+
}
|
|
10
|
+
export interface FileSystemDirent {
|
|
11
|
+
name: string;
|
|
12
|
+
type: FileSystemEntryType;
|
|
13
|
+
}
|
|
14
|
+
export interface MkdirOptions {
|
|
15
|
+
recursive?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface RmOptions {
|
|
18
|
+
recursive?: boolean;
|
|
19
|
+
force?: boolean;
|
|
20
|
+
}
|
|
21
|
+
export interface CpOptions {
|
|
22
|
+
recursive?: boolean;
|
|
23
|
+
}
|
|
24
|
+
export interface FileSystem {
|
|
25
|
+
readFile(path: string): Promise<string>;
|
|
26
|
+
readFileBytes(path: string): Promise<Uint8Array>;
|
|
27
|
+
writeFile(path: string, content: string): Promise<void>;
|
|
28
|
+
writeFileBytes(path: string, content: Uint8Array): Promise<void>;
|
|
29
|
+
appendFile(path: string, content: string | Uint8Array): Promise<void>;
|
|
30
|
+
exists(path: string): Promise<boolean>;
|
|
31
|
+
stat(path: string): Promise<FsStat>;
|
|
32
|
+
lstat(path: string): Promise<FsStat>;
|
|
33
|
+
mkdir(path: string, options?: MkdirOptions): Promise<void>;
|
|
34
|
+
readdir(path: string): Promise<string[]>;
|
|
35
|
+
readdirWithFileTypes(path: string): Promise<FileSystemDirent[]>;
|
|
36
|
+
rm(path: string, options?: RmOptions): Promise<void>;
|
|
37
|
+
cp(src: string, dest: string, options?: CpOptions): Promise<void>;
|
|
38
|
+
mv(src: string, dest: string): Promise<void>;
|
|
39
|
+
symlink(target: string, linkPath: string): Promise<void>;
|
|
40
|
+
readlink(path: string): Promise<string>;
|
|
41
|
+
realpath(path: string): Promise<string>;
|
|
42
|
+
resolvePath(base: string, path: string): string;
|
|
43
|
+
glob(pattern: string): Promise<string[]>;
|
|
44
|
+
}
|
|
45
|
+
export interface ReadFileOptions {
|
|
46
|
+
encoding?: BufferEncoding | null;
|
|
47
|
+
}
|
|
48
|
+
export interface WriteFileOptions {
|
|
49
|
+
encoding?: BufferEncoding;
|
|
50
|
+
}
|
|
51
|
+
export interface FileEntry {
|
|
52
|
+
type: 'file';
|
|
53
|
+
content: string | Uint8Array;
|
|
54
|
+
mode: number;
|
|
55
|
+
mtime: Date;
|
|
56
|
+
}
|
|
57
|
+
export interface DirectoryEntry {
|
|
58
|
+
type: 'directory';
|
|
59
|
+
mode: number;
|
|
60
|
+
mtime: Date;
|
|
61
|
+
}
|
|
62
|
+
export interface SymlinkEntry {
|
|
63
|
+
type: 'symlink';
|
|
64
|
+
target: string;
|
|
65
|
+
mode: number;
|
|
66
|
+
mtime: Date;
|
|
67
|
+
}
|
|
68
|
+
export interface LazyFileEntry {
|
|
69
|
+
type: 'file';
|
|
70
|
+
lazy: () => string | Uint8Array | Promise<string | Uint8Array>;
|
|
71
|
+
mode: number;
|
|
72
|
+
mtime: Date;
|
|
73
|
+
}
|
|
74
|
+
export type FsEntry = FileEntry | LazyFileEntry | DirectoryEntry | SymlinkEntry;
|
|
75
|
+
export interface FileInit {
|
|
76
|
+
content: FileContent;
|
|
77
|
+
mode?: number;
|
|
78
|
+
mtime?: Date;
|
|
79
|
+
}
|
|
80
|
+
export type LazyFileProvider = () => string | Uint8Array | Promise<string | Uint8Array>;
|
|
81
|
+
export type InitialFiles = Record<string, FileContent | FileInit | LazyFileProvider>;
|
|
82
|
+
export interface FsError extends Error {
|
|
83
|
+
code?: string;
|
|
84
|
+
}
|
|
85
|
+
export declare function fsErrorCode(err: unknown): string | undefined;
|
|
@@ -1,7 +1,17 @@
|
|
|
1
|
+
import type { MemoryBlockScope, PersistentMemoryConfig } from '../memory/blocks/types.js';
|
|
1
2
|
export interface AgentKnowledge {
|
|
2
3
|
autoRetrieve?: boolean;
|
|
3
4
|
sources?: string[];
|
|
4
5
|
}
|
|
6
|
+
export interface WorkingMemoryBlockSpec {
|
|
7
|
+
scope: MemoryBlockScope;
|
|
8
|
+
key: string;
|
|
9
|
+
/** Seed content when the block is missing or empty (persisted on first write, not on read). */
|
|
10
|
+
template?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface WorkingMemoryConfig extends Omit<PersistentMemoryConfig, 'autoLoad'> {
|
|
13
|
+
autoLoad?: WorkingMemoryBlockSpec[];
|
|
14
|
+
}
|
|
5
15
|
export interface AgentMemory {
|
|
6
16
|
preload?: {
|
|
7
17
|
enabled?: boolean;
|
|
@@ -10,4 +20,6 @@ export interface AgentMemory {
|
|
|
10
20
|
ingest?: {
|
|
11
21
|
enabled?: boolean;
|
|
12
22
|
};
|
|
23
|
+
/** Persistent markdown blocks (USER/MEMORY) loaded at session start and editable via memory_block. */
|
|
24
|
+
workingMemory?: WorkingMemoryConfig;
|
|
13
25
|
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ export * from './selection.js';
|
|
|
2
2
|
export * from './telemetry.js';
|
|
3
3
|
export * from './processors.js';
|
|
4
4
|
export * from './agentConfig.js';
|
|
5
|
+
export * from './filesystem.js';
|
|
6
|
+
export * from './skills.js';
|
|
5
7
|
export * from './flow.js';
|
|
6
8
|
export * from './session.js';
|
|
7
9
|
export * from './tool.js';
|
package/dist/types/index.js
CHANGED
|
@@ -2,6 +2,8 @@ export * from './selection.js';
|
|
|
2
2
|
export * from './telemetry.js';
|
|
3
3
|
export * from './processors.js';
|
|
4
4
|
export * from './agentConfig.js';
|
|
5
|
+
export * from './filesystem.js';
|
|
6
|
+
export * from './skills.js';
|
|
5
7
|
export * from './flow.js';
|
|
6
8
|
export * from './session.js';
|
|
7
9
|
export * from './tool.js';
|
|
@@ -8,6 +8,7 @@ import type { RefinementCapability } from '../capabilities/RefinementCapability.
|
|
|
8
8
|
import type { ValidationCapability } from '../capabilities/ValidationCapability.js';
|
|
9
9
|
import type { Limits } from './guardrails.js';
|
|
10
10
|
import type { AnyTool } from './effectTool.js';
|
|
11
|
+
import type { FileSystem } from './filesystem.js';
|
|
11
12
|
import type { Instructions } from './agentConfig.js';
|
|
12
13
|
import type { AgentKnowledgeOverrides, SourceRef } from './voice.js';
|
|
13
14
|
export interface GatherScope {
|
|
@@ -85,6 +86,14 @@ export interface RunContext {
|
|
|
85
86
|
* in every speaking turn. */
|
|
86
87
|
baseInstructions?: Instructions;
|
|
87
88
|
globalTools?: Record<string, AnyTool>;
|
|
89
|
+
/** Level-1 skill metadata injected by `SkillsCapability` when `AgentConfig.skills` is set. */
|
|
90
|
+
skillPrompt?: string;
|
|
91
|
+
/** Frozen persistent memory blocks loaded at session start (`AgentMemory.workingMemory`). */
|
|
92
|
+
workingMemoryPrompt?: string;
|
|
93
|
+
/** Model-visible tools from working memory wiring (not in globalTools — mutating but scoped). */
|
|
94
|
+
workingMemoryTools?: Record<string, AnyTool>;
|
|
95
|
+
/** Agent workspace filesystem (same instance as `AgentConfig.workspace`). */
|
|
96
|
+
fs?: FileSystem;
|
|
88
97
|
tool(name: string, args: unknown, options?: {
|
|
89
98
|
toolCallId?: string;
|
|
90
99
|
def?: AnyTool;
|
|
@@ -104,6 +113,6 @@ export interface RunContext {
|
|
|
104
113
|
now(): Promise<number>;
|
|
105
114
|
uuid(): Promise<string>;
|
|
106
115
|
}
|
|
107
|
-
export type ActionContext = Pick<RunContext, 'tool' | 'approve' | 'signal' | 'now' | 'uuid' | 'emit'>;
|
|
108
|
-
export type ToolContext = Pick<RunContext, 'session' | 'runState' | 'tool' | 'now' | 'uuid' | 'emit'>;
|
|
116
|
+
export type ActionContext = Pick<RunContext, 'tool' | 'approve' | 'signal' | 'now' | 'uuid' | 'emit' | 'fs'>;
|
|
117
|
+
export type ToolContext = Pick<RunContext, 'session' | 'runState' | 'tool' | 'now' | 'uuid' | 'emit' | 'fs'>;
|
|
109
118
|
export type { ModelMessage };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface SkillMeta {
|
|
2
|
+
name: string;
|
|
3
|
+
description: string;
|
|
4
|
+
}
|
|
5
|
+
export interface SkillLike {
|
|
6
|
+
name: string;
|
|
7
|
+
description: string;
|
|
8
|
+
body: string;
|
|
9
|
+
resources?: Record<string, string | Uint8Array>;
|
|
10
|
+
allowedTools?: string[];
|
|
11
|
+
}
|
|
12
|
+
export interface SkillStoreLike {
|
|
13
|
+
list(): Promise<SkillMeta[]>;
|
|
14
|
+
loadBody(name: string): Promise<string>;
|
|
15
|
+
loadResource(name: string, path: string): Promise<string | Uint8Array>;
|
|
16
|
+
getAllSkills?(): SkillLike[] | Promise<SkillLike[]>;
|
|
17
|
+
loadAllSkills?(): Promise<SkillLike[]>;
|
|
18
|
+
}
|
|
19
|
+
export type SkillSource = SkillLike | SkillLike[] | SkillStoreLike;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/types/stream.d.ts
CHANGED
|
@@ -91,5 +91,8 @@ export type HarnessStreamPart = {
|
|
|
91
91
|
export interface TurnHandle extends Promise<import('./channel.js').TurnResult> {
|
|
92
92
|
readonly events: AsyncIterable<HarnessStreamPart>;
|
|
93
93
|
toResponseStream(format?: 'sse' | 'ndjson'): ReadableStream;
|
|
94
|
+
toUIMessageStreamResponse(opts?: {
|
|
95
|
+
sessionId?: string;
|
|
96
|
+
}): Response;
|
|
94
97
|
cancel(reason?: string): void;
|
|
95
98
|
}
|
package/guides/AGENTS.md
CHANGED
|
@@ -16,7 +16,7 @@ const model = openai('gpt-4o-mini');
|
|
|
16
16
|
| Fields | Effect |
|
|
17
17
|
|--------|--------|
|
|
18
18
|
| `id`, `model`, `instructions` | Base conversational agent |
|
|
19
|
-
| `tools`, `
|
|
19
|
+
| `tools`, `globalTools` | Durable tool access during free conversation and flow nodes |
|
|
20
20
|
| `flows` | Structured multi-step SOPs (`defineFlow` + node builders) |
|
|
21
21
|
| `routes`, `routing` | Route to flows or specialist agents |
|
|
22
22
|
| `agents` | Nested sub-agent configs for composition |
|
|
@@ -210,8 +210,7 @@ const lead = defineAgent({
|
|
|
210
210
|
instructions:
|
|
211
211
|
'Research assistant. Use consult_weather for weather and consult_news for news. Combine answers clearly.',
|
|
212
212
|
model,
|
|
213
|
-
tools:
|
|
214
|
-
effectTools: tools,
|
|
213
|
+
tools: tools,
|
|
215
214
|
});
|
|
216
215
|
|
|
217
216
|
const runtime = createRuntime({
|
package/guides/FLOWS.md
CHANGED
|
@@ -29,7 +29,7 @@ const collectDate = defineTool({
|
|
|
29
29
|
const greeting = reply({
|
|
30
30
|
id: 'greeting',
|
|
31
31
|
instructions: 'Welcome. What date would you like to book?',
|
|
32
|
-
tools: { collect_date: collectDate }, // wire via
|
|
32
|
+
tools: { collect_date: collectDate }, // wire via tools + ToolSet in production
|
|
33
33
|
next: (turn) => {
|
|
34
34
|
const r = turn.toolResults.find((t) => t.name === 'collect_date');
|
|
35
35
|
return r?.result ? { goto: confirm, data: r.result as Record<string, unknown> } : 'stay';
|
|
@@ -46,7 +46,7 @@ const agent = defineAgent({
|
|
|
46
46
|
id: 'booking',
|
|
47
47
|
instructions: 'You are a booking assistant.',
|
|
48
48
|
model,
|
|
49
|
-
|
|
49
|
+
tools: { collect_date: collectDate },
|
|
50
50
|
flows: [
|
|
51
51
|
defineFlow({
|
|
52
52
|
name: 'booking',
|
package/guides/RUNTIME.md
CHANGED
|
@@ -11,7 +11,24 @@
|
|
|
11
11
|
|
|
12
12
|
## Stream Events
|
|
13
13
|
|
|
14
|
-
`runtime.run()` returns a `TurnHandle` with an `.events` async iterable of `HarnessStreamPart` items.
|
|
14
|
+
`runtime.run()` returns a `TurnHandle` with an `.events` async iterable of `HarnessStreamPart` items.
|
|
15
|
+
|
|
16
|
+
### Web UI (AI SDK native, 0.5.0+)
|
|
17
|
+
|
|
18
|
+
For React/web consumers, return a native `UIMessageStream` — `useChat` works with no bridge:
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
const handle = runtime.run({ input, sessionId });
|
|
22
|
+
return handle.toUIMessageStreamResponse({ sessionId });
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Kuralle orchestration events map to typed `data-kuralle-*` parts (see `docs/adr/0005-ai-sdk-native-uimessage-default.md`). Import `KuralleUIMessage` for compile-time-safe `message.parts`.
|
|
26
|
+
|
|
27
|
+
With `@kuralle-agents/hono-server`, `POST /api/chat/sse` defaults to this native wire. Append `?format=raw` for legacy `HarnessStreamPart` JSON-SSE.
|
|
28
|
+
|
|
29
|
+
### Direct `HarnessStreamPart` consumption
|
|
30
|
+
|
|
31
|
+
For CLI scripts, cascaded voice, messaging, or custom transports, iterate `handle.events` directly. Typical usage renders `text-delta` chunks via `part.delta`.
|
|
15
32
|
|
|
16
33
|
Common types:
|
|
17
34
|
- `text-delta`
|