@capekai/core 1.0.0 → 1.0.2
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@capekai/core",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Bun-native composable agent runtime and framework for Capek.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
"@ai-sdk/deepseek": "^2.0.35",
|
|
79
79
|
"@ai-sdk/openai": "^3.0.84",
|
|
80
80
|
"@capekai/tool": "^1.0.0",
|
|
81
|
-
"@capekai/types": "^1.0.
|
|
81
|
+
"@capekai/types": "^1.0.1",
|
|
82
82
|
"@openrouter/ai-sdk-provider": "^2.3.3",
|
|
83
83
|
"@zip.js/zip.js": "^2.7.60",
|
|
84
84
|
"ai": "^6.0.116",
|
|
@@ -99,8 +99,9 @@ function sessionSearchPayload(deps: SessionSearchPayloadDeps): DomainToolPayload
|
|
|
99
99
|
if (results) {
|
|
100
100
|
return {
|
|
101
101
|
type: 'file-list',
|
|
102
|
-
collapsed: true,
|
|
103
102
|
badge: `${results.length} result${results.length === 1 ? '' : 's'}`,
|
|
103
|
+
singularLabel: 'result',
|
|
104
|
+
pluralLabel: 'results',
|
|
104
105
|
title: String(result.query ?? String(result.title ?? 'Search')),
|
|
105
106
|
files: results.slice(0, 20).map((r) => ({
|
|
106
107
|
path: String((r as { sessionTitle?: string }).sessionTitle ?? (r as { sessionId?: string }).sessionId ?? ''),
|
|
@@ -112,8 +113,9 @@ function sessionSearchPayload(deps: SessionSearchPayloadDeps): DomainToolPayload
|
|
|
112
113
|
if (sessions) {
|
|
113
114
|
return {
|
|
114
115
|
type: 'file-list',
|
|
115
|
-
collapsed: true,
|
|
116
116
|
badge: `${sessions.length} session${sessions.length === 1 ? '' : 's'}`,
|
|
117
|
+
singularLabel: 'session',
|
|
118
|
+
pluralLabel: 'sessions',
|
|
117
119
|
files: sessions.slice(0, 20).map((s) => ({
|
|
118
120
|
path: String((s as { title?: string }).title ?? (s as { id?: string }).id ?? ''),
|
|
119
121
|
})),
|
|
@@ -65,7 +65,6 @@ export function createSkillToolPayload(): DomainToolPayload {
|
|
|
65
65
|
if (content && content.length <= 20_000) {
|
|
66
66
|
return {
|
|
67
67
|
type: 'markdown',
|
|
68
|
-
collapsed: true,
|
|
69
68
|
badge: `${(content.length / 1024).toFixed(1)} KB`,
|
|
70
69
|
content: content.slice(0, 20_000),
|
|
71
70
|
title: typeof title === 'string' ? title : String(input.name ?? 'Skill'),
|
|
@@ -109,8 +108,9 @@ export function createSkillManageToolPayload(): DomainToolPayload {
|
|
|
109
108
|
if (skills) {
|
|
110
109
|
return {
|
|
111
110
|
type: 'file-list',
|
|
112
|
-
collapsed: true,
|
|
113
111
|
badge: `${skills.length} skill${skills.length === 1 ? '' : 's'}`,
|
|
112
|
+
singularLabel: 'skill',
|
|
113
|
+
pluralLabel: 'skills',
|
|
114
114
|
files: skills.slice(0, 20).map((s) => ({
|
|
115
115
|
path: String((s as { name?: string }).name ?? ''),
|
|
116
116
|
content: (s as { description?: string }).description,
|
|
@@ -161,8 +161,9 @@ export function createAgentSkillManageToolPayload(): DomainToolPayload {
|
|
|
161
161
|
if (skills) {
|
|
162
162
|
return {
|
|
163
163
|
type: 'file-list',
|
|
164
|
-
collapsed: true,
|
|
165
164
|
badge: `${skills.length} skill${skills.length === 1 ? '' : 's'}`,
|
|
165
|
+
singularLabel: 'skill',
|
|
166
|
+
pluralLabel: 'skills',
|
|
166
167
|
files: skills.slice(0, 20).map((s) => ({
|
|
167
168
|
path: String((s as { name?: string }).name ?? ''),
|
|
168
169
|
content: (s as { description?: string }).description,
|
|
@@ -276,7 +276,7 @@ export function subagentDomainPlugin(id: string): CapekPlugin<unknown> {
|
|
|
276
276
|
name: service.tools[0].name,
|
|
277
277
|
description: service.tools[0].description,
|
|
278
278
|
inputSchema: service.tools[0].inputSchema,
|
|
279
|
-
timeout:
|
|
279
|
+
timeout: null,
|
|
280
280
|
[DOMAIN_TOOL_PAYLOAD_FIELD]: service.tools[0],
|
|
281
281
|
} as KernelToolDefinition,
|
|
282
282
|
requiredCapabilities: [capekSubagentDomainKey],
|
|
@@ -133,7 +133,7 @@ When NOT to use it:
|
|
|
133
133
|
Pattern for aggregation (map-reduce): define one schema, spawn N agents each with that outputSchema, then in your next turn merge the returned JSON objects. This keeps your context clean because you can reason about the data instead of re-parsing prose from each agent.
|
|
134
134
|
|
|
135
135
|
Note: Subagent depth is limited to 2 levels. You cannot spawn further subagents at the maximum depth.`,
|
|
136
|
-
timeout:
|
|
136
|
+
timeout: null,
|
|
137
137
|
inputSchema: {
|
|
138
138
|
type: 'object',
|
|
139
139
|
properties: {
|
package/src/tools/executor.ts
CHANGED
|
@@ -34,7 +34,9 @@ export interface ExecuteToolOptions {
|
|
|
34
34
|
workspaceId?: string;
|
|
35
35
|
toolCallId?: string;
|
|
36
36
|
abortSignal?: AbortSignal;
|
|
37
|
-
|
|
37
|
+
/** Milliseconds deadline. Pass `null` (or declare it on the tool
|
|
38
|
+
* definition) to run without a deadline until interrupted. */
|
|
39
|
+
timeout?: number | null;
|
|
38
40
|
createLlmApi?: (defaultModel?: string) => LlmApi;
|
|
39
41
|
createAskApi?: (toolCallId: string) => AskApi;
|
|
40
42
|
broadcastFn?: (event: { type: string; [key: string]: unknown }) => void;
|
|
@@ -172,7 +174,9 @@ export async function executeTool(options: ExecuteToolOptions): Promise<ToolResu
|
|
|
172
174
|
workspace,
|
|
173
175
|
sessionId,
|
|
174
176
|
abortSignal,
|
|
175
|
-
|
|
177
|
+
// Definition null is authoritative "no deadline"; only an absent
|
|
178
|
+
// definition falls back to the 30s default.
|
|
179
|
+
timeout = tool.definition.timeout === undefined ? 30000 : tool.definition.timeout,
|
|
176
180
|
createLlmApi,
|
|
177
181
|
createAskApi,
|
|
178
182
|
} = options;
|
|
@@ -211,12 +215,16 @@ export async function executeTool(options: ExecuteToolOptions): Promise<ToolResu
|
|
|
211
215
|
const executePromise = tool.execute(args, ctx);
|
|
212
216
|
|
|
213
217
|
let timeoutId: ReturnType<typeof setTimeout> | undefined;
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
218
|
+
// `timeout === null` is the tool's explicit "no deadline": the executor
|
|
219
|
+
// arms no timer and the tool runs until it settles or is interrupted.
|
|
220
|
+
const timeoutPromise = timeout === null
|
|
221
|
+
? null
|
|
222
|
+
: new Promise<never>((_, reject) => {
|
|
223
|
+
timeoutId = setTimeout(() => {
|
|
224
|
+
toolAbortController.abort(new Error(`Tool execution timed out after ${timeout}ms`));
|
|
225
|
+
reject(new Error(`Tool execution timed out after ${timeout}ms`));
|
|
226
|
+
}, timeout);
|
|
227
|
+
});
|
|
220
228
|
|
|
221
229
|
// Abort must settle the race even when the tool ignores its abort signal
|
|
222
230
|
// (for example a tool blocked on ctx.ask()). Promise.race keeps handlers
|
|
@@ -232,10 +240,12 @@ export async function executeTool(options: ExecuteToolOptions): Promise<ToolResu
|
|
|
232
240
|
})
|
|
233
241
|
: null;
|
|
234
242
|
|
|
243
|
+
const racers = timeoutPromise
|
|
244
|
+
? (abortPromise ? [executePromise, timeoutPromise, abortPromise] : [executePromise, timeoutPromise])
|
|
245
|
+
: (abortPromise ? [executePromise, abortPromise] : [executePromise]);
|
|
246
|
+
|
|
235
247
|
try {
|
|
236
|
-
const result = await Promise.race(
|
|
237
|
-
abortPromise ? [executePromise, timeoutPromise, abortPromise] : [executePromise, timeoutPromise],
|
|
238
|
-
);
|
|
248
|
+
const result = await Promise.race(racers);
|
|
239
249
|
return result;
|
|
240
250
|
} catch (err: unknown) {
|
|
241
251
|
const message = err instanceof Error ? err.message : String(err);
|