@kuralle-agents/core 0.5.0 → 0.6.1
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 +1 -1
- package/dist/capabilities/adapters/gemini.js +2 -2
- package/dist/flow/choiceMatch.d.ts +1 -1
- package/dist/flow/nodeBuilders.d.ts +1 -1
- package/dist/flow/nodeBuilders.js +5 -3
- package/dist/index.d.ts +12 -4
- package/dist/index.js +7 -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 +8 -8
- 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/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 +18 -0
- package/dist/tools/effect/wrapAiSdkTool.js +18 -0
- package/dist/tools/fs/createFsTool.d.ts +30 -0
- package/dist/tools/fs/createFsTool.js +200 -0
- package/dist/tools/handoff.d.ts +1 -1
- package/dist/tools/http.js +1 -1
- 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/utils/chrono.d.ts +1 -9
- package/guides/AGENTS.md +2 -3
- package/guides/FLOWS.md +2 -2
- package/guides/TOOLS.md +69 -2
- package/package.json +15 -7
|
@@ -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
|
+
}
|
package/dist/tools/handoff.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export interface HandoffResult {
|
|
|
7
7
|
summary?: string;
|
|
8
8
|
message?: string;
|
|
9
9
|
}
|
|
10
|
-
export declare function createHandoffTool(availableAgents: AgentConfig[], currentAgentId?: string): import("ai").Tool<
|
|
10
|
+
export declare function createHandoffTool(availableAgents: AgentConfig[], currentAgentId?: string): import("ai").Tool<Record<string, never>, {
|
|
11
11
|
error: string;
|
|
12
12
|
}> | import("ai").Tool<{
|
|
13
13
|
targetAgentId: string;
|
package/dist/tools/http.js
CHANGED
|
@@ -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/utils/chrono.d.ts
CHANGED
|
@@ -16,15 +16,7 @@ declare const dateParserInputSchema: z.ZodObject<{
|
|
|
16
16
|
text: z.ZodString;
|
|
17
17
|
referenceDate: z.ZodOptional<z.ZodString>;
|
|
18
18
|
timezone: z.ZodOptional<z.ZodString>;
|
|
19
|
-
},
|
|
20
|
-
text: string;
|
|
21
|
-
referenceDate?: string | undefined;
|
|
22
|
-
timezone?: string | undefined;
|
|
23
|
-
}, {
|
|
24
|
-
text: string;
|
|
25
|
-
referenceDate?: string | undefined;
|
|
26
|
-
timezone?: string | undefined;
|
|
27
|
-
}>;
|
|
19
|
+
}, z.core.$strip>;
|
|
28
20
|
type DateParserInput = z.infer<typeof dateParserInputSchema>;
|
|
29
21
|
type DateParserOutput = {
|
|
30
22
|
success: true;
|
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/TOOLS.md
CHANGED
|
@@ -2,6 +2,74 @@
|
|
|
2
2
|
|
|
3
3
|
Kuralle tools use the Vercel AI SDK `tool(...)` API. Tools are how agents read data, write state, and trigger flow transitions.
|
|
4
4
|
|
|
5
|
+
## Durable agent tools
|
|
6
|
+
|
|
7
|
+
Agent-level `tools` is a `Record<string, AnyTool>` from `defineTool` — every call is journaled (exactly-once on replay). Flow nodes use `buildToolSet({ ... })` for model-visible schema; executors come from the agent registry and flow-local tools.
|
|
8
|
+
|
|
9
|
+
For third-party AI SDK tools, use `wrapAiSdkTool(name, aiTool)` — it captures `execute` and routes through the same journal:
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { defineTool, wrapAiSdkTool } from '@kuralle-agents/core';
|
|
13
|
+
import { tool } from 'ai';
|
|
14
|
+
|
|
15
|
+
const native = defineTool({ name: 'lookup', description: '...', input: z.object({ id: z.string() }), execute: async ({ id }) => ({ id }) });
|
|
16
|
+
|
|
17
|
+
const sdk = tool({ description: 'Legacy SDK tool', inputSchema: z.object({ id: z.string() }), execute: async ({ id }) => ({ id }) });
|
|
18
|
+
|
|
19
|
+
const agent = defineAgent({
|
|
20
|
+
id: 'a',
|
|
21
|
+
model,
|
|
22
|
+
tools: { lookup: native, legacy: wrapAiSdkTool('legacy', sdk) },
|
|
23
|
+
});
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Agent workspace
|
|
27
|
+
|
|
28
|
+
Set `workspace` to a portable `FileSystem` (from `@kuralle-agents/fs`) to auto-register the durable `workspace` tool (`ls`, `cat`, `grep`, `find`, `read`, `write`, `edit`). The same instance is exposed on `RunContext.fs` for flow `action` nodes.
|
|
29
|
+
|
|
30
|
+
**Read-only by default** (ADR 0006): a bare `FileSystem` is mounted read-only and exposed in `globalTools` (safe for every speaking turn). Pass `{ fs, readOnly: false }` for a writable scratchpad — the executor is registered, but the tool is **not** auto-added to `globalTools` (mutating tools stay flow-gated).
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
import { InMemoryFs } from '@kuralle-agents/fs';
|
|
34
|
+
|
|
35
|
+
const agent = defineAgent({
|
|
36
|
+
id: 'kb',
|
|
37
|
+
model,
|
|
38
|
+
workspace: new InMemoryFs({ '/docs/faq.md': '# FAQ' }),
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// Writable scratchpad (not in globalTools):
|
|
42
|
+
// workspace: { fs: new InMemoryFs({ '/scratch': '' }), readOnly: false },
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Requires `@kuralle-agents/fs` when using `workspace`.
|
|
46
|
+
|
|
47
|
+
## Working memory blocks
|
|
48
|
+
|
|
49
|
+
Cross-session markdown blocks (`USER`, `MEMORY`, …) via `agent.memory.workingMemory`. Blocks load at session start, inject into the system prompt, and are editable with the auto-registered `memory_block` tool.
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
import { defineAgent, FilePersistentMemoryStore } from '@kuralle-agents/core';
|
|
53
|
+
|
|
54
|
+
const agent = defineAgent({
|
|
55
|
+
id: 'support',
|
|
56
|
+
model,
|
|
57
|
+
memory: {
|
|
58
|
+
workingMemory: {
|
|
59
|
+
store: new FilePersistentMemoryStore(),
|
|
60
|
+
autoLoad: [
|
|
61
|
+
{ scope: 'user', key: 'USER', template: 'name:\npreferences:' },
|
|
62
|
+
{ scope: 'agent', key: 'MEMORY' },
|
|
63
|
+
],
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
On Workers, pass an explicit `store` (or `HarnessConfig.defaultWorkingMemoryStore`). Semantic recall (`preload` / `ingest`) is unchanged — working memory is an additional axis.
|
|
70
|
+
|
|
71
|
+
See [examples/agents/working-memory.ts](../examples/agents/working-memory.ts) for a live cross-session demo.
|
|
72
|
+
|
|
5
73
|
## Tool Basics
|
|
6
74
|
|
|
7
75
|
```ts
|
|
@@ -70,8 +138,7 @@ const lead = defineAgent({
|
|
|
70
138
|
instructions:
|
|
71
139
|
'Research assistant. Use consult_weather for weather questions. Combine answers clearly.',
|
|
72
140
|
model,
|
|
73
|
-
tools:
|
|
74
|
-
effectTools: { consult_weather: consultWeather },
|
|
141
|
+
tools: { consult_weather: consultWeather },
|
|
75
142
|
});
|
|
76
143
|
|
|
77
144
|
const runtime = createRuntime({
|
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.6.1",
|
|
10
10
|
"description": "A framework for structured conversational AI agents",
|
|
11
11
|
"publishConfig": {
|
|
12
12
|
"access": "public"
|
|
@@ -72,6 +72,10 @@
|
|
|
72
72
|
"types": "./dist/memory/index.d.ts",
|
|
73
73
|
"default": "./dist/memory/index.js"
|
|
74
74
|
},
|
|
75
|
+
"./memory/testing": {
|
|
76
|
+
"types": "./dist/memory/blocks/testing.d.ts",
|
|
77
|
+
"default": "./dist/memory/blocks/testing.js"
|
|
78
|
+
},
|
|
75
79
|
"./foundation": {
|
|
76
80
|
"types": "./dist/foundation/index.d.ts",
|
|
77
81
|
"default": "./dist/foundation/index.js"
|
|
@@ -87,28 +91,32 @@
|
|
|
87
91
|
},
|
|
88
92
|
"peerDependencies": {
|
|
89
93
|
"ai": "^6.0.0",
|
|
90
|
-
"zod": "^
|
|
94
|
+
"zod": "^4.0.0"
|
|
91
95
|
},
|
|
92
96
|
"devDependencies": {
|
|
97
|
+
"@cloudflare/vitest-pool-workers": "^0.12.7",
|
|
93
98
|
"@ai-sdk/openai": "^3.0.0",
|
|
94
99
|
"@types/node": "^20.11.0",
|
|
95
100
|
"ai": "^6.0.0",
|
|
96
101
|
"bun-types": "^1.3.0",
|
|
97
102
|
"dotenv": "^16.4.0",
|
|
98
103
|
"typescript": "^5.3.0",
|
|
99
|
-
"
|
|
100
|
-
"
|
|
104
|
+
"vitest": "^3.2.4",
|
|
105
|
+
"zod": "^4.0.0",
|
|
106
|
+
"@kuralle-agents/realtime-audio": "0.6.1"
|
|
101
107
|
},
|
|
102
108
|
"dependencies": {
|
|
103
|
-
"chrono-node": "^2.6.0"
|
|
104
|
-
"zod-to-json-schema": "^3.24.0"
|
|
109
|
+
"chrono-node": "^2.6.0"
|
|
105
110
|
},
|
|
106
111
|
"scripts": {
|
|
107
112
|
"prebuild": "rm -rf dist",
|
|
108
113
|
"build": "tsc -p tsconfig.json",
|
|
109
114
|
"typecheck:examples": "tsc --noEmit -p tsconfig.examples.json",
|
|
110
115
|
"clean": "rm -rf dist",
|
|
111
|
-
"test": "bun test test",
|
|
116
|
+
"test": "bun test ./test && vitest run --config vitest.config.ts",
|
|
117
|
+
"test:journal-key-workers": "vitest run --config vitest.config.ts",
|
|
118
|
+
"test:workspace-autoregister": "bun test ./test/core-workspace/workspace-autoregister.test.ts",
|
|
119
|
+
"test:skill-wire": "bun test ./test/core-skills/skill-wire.test.ts",
|
|
112
120
|
"smoke:textdriver": "bun test ./test/core-channel/textdriver.smoke.ts",
|
|
113
121
|
"smoke:flow": "bun test ./test/core-flow/flow.smoke.ts",
|
|
114
122
|
"smoke:agent": "bun test ./test/core-agent/agent.smoke.ts",
|