@capekai/core 1.0.1 → 1.0.3
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,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@capekai/core",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "Bun-native composable agent runtime and framework for Capek.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
|
-
"url": "git+https://github.com/capek-dev/
|
|
8
|
+
"url": "git+https://github.com/capek-dev/capek.git",
|
|
9
9
|
"directory": "packages/capek"
|
|
10
10
|
},
|
|
11
11
|
"type": "module",
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
"dependencies": {
|
|
78
78
|
"@ai-sdk/deepseek": "^2.0.35",
|
|
79
79
|
"@ai-sdk/openai": "^3.0.84",
|
|
80
|
-
"@capekai/tool": "^1.0.
|
|
80
|
+
"@capekai/tool": "^1.0.2",
|
|
81
81
|
"@capekai/types": "^1.0.1",
|
|
82
82
|
"@openrouter/ai-sdk-provider": "^2.3.3",
|
|
83
83
|
"@zip.js/zip.js": "^2.7.60",
|
|
@@ -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;
|
|
@@ -201,6 +205,7 @@ export async function executeTool(options: ExecuteToolOptions): Promise<ToolResu
|
|
|
201
205
|
logger: createLogger(tool.definition.name, sessionId),
|
|
202
206
|
fetch: globalThis.fetch.bind(globalThis),
|
|
203
207
|
resolvePath: workspace.resolvePath,
|
|
208
|
+
resolvePathFrom: workspace.resolvePathFrom,
|
|
204
209
|
isWithinWorkspace: workspace.isWithinWorkspace,
|
|
205
210
|
isSensitivePath: workspace.isSensitivePath,
|
|
206
211
|
isBlockedPath: workspace.isBlockedPath,
|
|
@@ -211,12 +216,16 @@ export async function executeTool(options: ExecuteToolOptions): Promise<ToolResu
|
|
|
211
216
|
const executePromise = tool.execute(args, ctx);
|
|
212
217
|
|
|
213
218
|
let timeoutId: ReturnType<typeof setTimeout> | undefined;
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
219
|
+
// `timeout === null` is the tool's explicit "no deadline": the executor
|
|
220
|
+
// arms no timer and the tool runs until it settles or is interrupted.
|
|
221
|
+
const timeoutPromise = timeout === null
|
|
222
|
+
? null
|
|
223
|
+
: new Promise<never>((_, reject) => {
|
|
224
|
+
timeoutId = setTimeout(() => {
|
|
225
|
+
toolAbortController.abort(new Error(`Tool execution timed out after ${timeout}ms`));
|
|
226
|
+
reject(new Error(`Tool execution timed out after ${timeout}ms`));
|
|
227
|
+
}, timeout);
|
|
228
|
+
});
|
|
220
229
|
|
|
221
230
|
// Abort must settle the race even when the tool ignores its abort signal
|
|
222
231
|
// (for example a tool blocked on ctx.ask()). Promise.race keeps handlers
|
|
@@ -232,10 +241,12 @@ export async function executeTool(options: ExecuteToolOptions): Promise<ToolResu
|
|
|
232
241
|
})
|
|
233
242
|
: null;
|
|
234
243
|
|
|
244
|
+
const racers = timeoutPromise
|
|
245
|
+
? (abortPromise ? [executePromise, timeoutPromise, abortPromise] : [executePromise, timeoutPromise])
|
|
246
|
+
: (abortPromise ? [executePromise, abortPromise] : [executePromise]);
|
|
247
|
+
|
|
235
248
|
try {
|
|
236
|
-
const result = await Promise.race(
|
|
237
|
-
abortPromise ? [executePromise, timeoutPromise, abortPromise] : [executePromise, timeoutPromise],
|
|
238
|
-
);
|
|
249
|
+
const result = await Promise.race(racers);
|
|
239
250
|
return result;
|
|
240
251
|
} catch (err: unknown) {
|
|
241
252
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -41,6 +41,7 @@ export interface WorkspaceCapability {
|
|
|
41
41
|
allowedRoots: string[];
|
|
42
42
|
tempDir: string;
|
|
43
43
|
resolvePath(path: string): string;
|
|
44
|
+
resolvePathFrom(path: string, basePath: string): string;
|
|
44
45
|
isWithinWorkspace(path: string): boolean;
|
|
45
46
|
isSensitivePath(path: string): boolean;
|
|
46
47
|
isBlockedPath(path: string): boolean;
|
package/src/workspace/policy.ts
CHANGED
|
@@ -78,14 +78,18 @@ export function createWorkspaceCapabilityWithOptions(
|
|
|
78
78
|
const additionalRoots = (host.additionalRoots ?? []).map((path) => resolve(path));
|
|
79
79
|
const allowedRoots = (host.allowedRoots ?? []).map((path) => resolve(path));
|
|
80
80
|
|
|
81
|
-
function
|
|
81
|
+
function resolvePathFrom(path: string, basePath: string): string {
|
|
82
82
|
if (path === '~' || path.startsWith('~/')) {
|
|
83
83
|
return join(options.homeDir, path.slice(1));
|
|
84
84
|
}
|
|
85
85
|
if (isAbsolute(path)) {
|
|
86
86
|
return resolve(path);
|
|
87
87
|
}
|
|
88
|
-
return resolve(
|
|
88
|
+
return resolve(basePath, path);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function resolvePath(path: string): string {
|
|
92
|
+
return resolvePathFrom(path, effectiveRoot);
|
|
89
93
|
}
|
|
90
94
|
|
|
91
95
|
return {
|
|
@@ -94,6 +98,7 @@ export function createWorkspaceCapabilityWithOptions(
|
|
|
94
98
|
allowedRoots,
|
|
95
99
|
tempDir: host.tempDir,
|
|
96
100
|
resolvePath,
|
|
101
|
+
resolvePathFrom,
|
|
97
102
|
isWithinWorkspace(path: string): boolean {
|
|
98
103
|
const resolvedPath = resolvePath(path);
|
|
99
104
|
return [effectiveRoot, ...additionalRoots]
|