@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.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.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: 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;
@@ -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
- 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
- });
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);