@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.1",
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/prokop.git",
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.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: 300000,
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: 300000,
136
+ timeout: null,
137
137
  inputSchema: {
138
138
  type: 'object',
139
139
  properties: {
@@ -34,7 +34,9 @@ export interface ExecuteToolOptions {
34
34
  workspaceId?: string;
35
35
  toolCallId?: string;
36
36
  abortSignal?: AbortSignal;
37
- timeout?: number;
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
- timeout = tool.definition.timeout ?? 30000,
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
- const timeoutPromise = new Promise<never>((_, reject) => {
215
- timeoutId = setTimeout(() => {
216
- toolAbortController.abort(new Error(`Tool execution timed out after ${timeout}ms`));
217
- reject(new Error(`Tool execution timed out after ${timeout}ms`));
218
- }, timeout);
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;
@@ -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 resolvePath(path: string): string {
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(effectiveRoot, path);
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]