@cuylabs/agent-core 0.4.0 → 0.6.0

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.
Files changed (66) hide show
  1. package/README.md +81 -323
  2. package/dist/builder-BKkipazh.d.ts +34 -0
  3. package/dist/capabilities/index.d.ts +97 -0
  4. package/dist/capabilities/index.js +46 -0
  5. package/dist/chunk-3C4VKG4P.js +2149 -0
  6. package/dist/chunk-6TDTQJ4P.js +116 -0
  7. package/dist/chunk-7MUFEN4K.js +559 -0
  8. package/dist/chunk-BDBZ3SLK.js +745 -0
  9. package/dist/chunk-DWYX7ASF.js +26 -0
  10. package/dist/chunk-FG4MD5MU.js +54 -0
  11. package/dist/chunk-IVUJDISU.js +556 -0
  12. package/dist/chunk-LRHOS4ZN.js +584 -0
  13. package/dist/chunk-O2ZCFQL6.js +764 -0
  14. package/dist/chunk-P6YF7USR.js +182 -0
  15. package/dist/chunk-QAQADS4X.js +258 -0
  16. package/dist/chunk-QWFMX226.js +879 -0
  17. package/dist/{chunk-6VKLWNRE.js → chunk-SDSBEQXG.js} +1 -132
  18. package/dist/chunk-VBWWUHWI.js +724 -0
  19. package/dist/chunk-VEKUXUVF.js +41 -0
  20. package/dist/chunk-X635CM2F.js +305 -0
  21. package/dist/chunk-YUUJK53A.js +91 -0
  22. package/dist/chunk-ZXAKHMWH.js +283 -0
  23. package/dist/config-D2xeGEHK.d.ts +52 -0
  24. package/dist/context/index.d.ts +259 -0
  25. package/dist/context/index.js +26 -0
  26. package/dist/identifiers-BLUxFqV_.d.ts +12 -0
  27. package/dist/index-DZQJD_hp.d.ts +1067 -0
  28. package/dist/index-ipP3_ztp.d.ts +198 -0
  29. package/dist/index.d.ts +210 -5736
  30. package/dist/index.js +2132 -7767
  31. package/dist/mcp/index.d.ts +26 -0
  32. package/dist/mcp/index.js +14 -0
  33. package/dist/messages-BYWGn8TY.d.ts +110 -0
  34. package/dist/middleware/index.d.ts +8 -0
  35. package/dist/middleware/index.js +12 -0
  36. package/dist/models/index.d.ts +33 -0
  37. package/dist/models/index.js +12 -0
  38. package/dist/network-D76DS5ot.d.ts +5 -0
  39. package/dist/prompt/index.d.ts +225 -0
  40. package/dist/prompt/index.js +45 -0
  41. package/dist/reasoning/index.d.ts +71 -0
  42. package/dist/reasoning/index.js +47 -0
  43. package/dist/registry-CuRWWtcT.d.ts +164 -0
  44. package/dist/resolver-DOfZ-xuk.d.ts +254 -0
  45. package/dist/runner-G1wxEgac.d.ts +852 -0
  46. package/dist/runtime/index.d.ts +357 -0
  47. package/dist/runtime/index.js +64 -0
  48. package/dist/session-manager-Uawm2Le7.d.ts +274 -0
  49. package/dist/skill/index.d.ts +103 -0
  50. package/dist/skill/index.js +39 -0
  51. package/dist/storage/index.d.ts +167 -0
  52. package/dist/storage/index.js +50 -0
  53. package/dist/sub-agent/index.d.ts +14 -0
  54. package/dist/sub-agent/index.js +15 -0
  55. package/dist/tool/index.d.ts +174 -1
  56. package/dist/tool/index.js +12 -3
  57. package/dist/tool-DYp6-cC3.d.ts +239 -0
  58. package/dist/tool-pFAnJc5Y.d.ts +419 -0
  59. package/dist/tracker-DClqYqTj.d.ts +96 -0
  60. package/dist/tracking/index.d.ts +109 -0
  61. package/dist/tracking/index.js +20 -0
  62. package/dist/types-BWo810L_.d.ts +648 -0
  63. package/dist/types-CQaXbRsS.d.ts +47 -0
  64. package/dist/types-VQgymC1N.d.ts +156 -0
  65. package/package.json +89 -5
  66. package/dist/index-BlSTfS-W.d.ts +0 -470
@@ -0,0 +1,182 @@
1
+ // src/tool/replay.ts
2
+ function inferSideEffectLevel(fileOps, requestedLevel) {
3
+ if (requestedLevel) {
4
+ return requestedLevel;
5
+ }
6
+ switch (fileOps?.operationType) {
7
+ case "read":
8
+ return "none";
9
+ case "write":
10
+ case "create":
11
+ case "delete":
12
+ return "local";
13
+ default:
14
+ return "external";
15
+ }
16
+ }
17
+ function inferReplayMode(sideEffectLevel, requestedMode) {
18
+ if (requestedMode) {
19
+ return requestedMode;
20
+ }
21
+ return sideEffectLevel === "none" ? "replay" : "manual";
22
+ }
23
+ function normalizeToolReplayPolicy(policy, fileOps) {
24
+ const sideEffectLevel = inferSideEffectLevel(
25
+ fileOps,
26
+ policy?.sideEffectLevel
27
+ );
28
+ const mode = inferReplayMode(sideEffectLevel, policy?.mode);
29
+ return {
30
+ mode,
31
+ sideEffectLevel,
32
+ ...policy?.reason ? { reason: policy.reason } : {}
33
+ };
34
+ }
35
+
36
+ // src/tool/truncation.ts
37
+ import * as fs from "fs/promises";
38
+ import * as path from "path";
39
+ import * as os from "os";
40
+ import * as crypto from "crypto";
41
+ var MAX_LINES = 2e3;
42
+ var MAX_BYTES = 1e5;
43
+ var TRUNCATE_DIR = path.join(os.tmpdir(), "cuylabs-agent-outputs");
44
+ var TRUNCATE_GLOB = path.join(TRUNCATE_DIR, "*");
45
+ function truncateOutput(output, options = {}) {
46
+ const maxLines = options.maxLines ?? MAX_LINES;
47
+ const maxBytes = options.maxBytes ?? MAX_BYTES;
48
+ const lines = output.split("\n");
49
+ const bytes = Buffer.byteLength(output, "utf-8");
50
+ if (lines.length <= maxLines && bytes <= maxBytes) {
51
+ return {
52
+ content: output,
53
+ truncated: false
54
+ };
55
+ }
56
+ const hash = crypto.createHash("sha256").update(output).digest("hex").slice(0, 16);
57
+ const outputPath = path.join(TRUNCATE_DIR, `output-${hash}.txt`);
58
+ fs.mkdir(TRUNCATE_DIR, { recursive: true }).then(() => fs.writeFile(outputPath, output, "utf-8")).catch(() => {
59
+ });
60
+ let truncated;
61
+ if (lines.length > maxLines) {
62
+ const keepLines = Math.floor(maxLines / 2);
63
+ const firstPart = lines.slice(0, keepLines);
64
+ const lastPart = lines.slice(-keepLines);
65
+ const omitted = lines.length - keepLines * 2;
66
+ truncated = [
67
+ ...firstPart,
68
+ `
69
+ ... (${omitted} lines omitted, full output saved to ${outputPath}) ...
70
+ `,
71
+ ...lastPart
72
+ ].join("\n");
73
+ } else {
74
+ const keepBytes = Math.floor(maxBytes / 2);
75
+ const start = output.slice(0, keepBytes);
76
+ const end = output.slice(-keepBytes);
77
+ const omittedBytes = bytes - keepBytes * 2;
78
+ truncated = `${start}
79
+ ... (${omittedBytes} bytes omitted, full output saved to ${outputPath}) ...
80
+ ${end}`;
81
+ }
82
+ return {
83
+ content: truncated,
84
+ truncated: true,
85
+ outputPath
86
+ };
87
+ }
88
+ function formatSize(bytes) {
89
+ if (bytes < 1024) return `${bytes} B`;
90
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
91
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
92
+ }
93
+
94
+ // src/tool/tool.ts
95
+ import { z } from "zod";
96
+ var Tool;
97
+ ((Tool2) => {
98
+ function define(id, init, options = {}) {
99
+ const staticReplayPolicy = typeof init === "function" ? options.replayPolicy ? normalizeToolReplayPolicy(options.replayPolicy) : void 0 : normalizeToolReplayPolicy(
100
+ options.replayPolicy ?? init.replayPolicy,
101
+ init.fileOps
102
+ );
103
+ return {
104
+ id,
105
+ replayPolicy: staticReplayPolicy,
106
+ init: async (initCtx) => {
107
+ const toolInfo = typeof init === "function" ? await init(initCtx) : init;
108
+ const replayPolicy = normalizeToolReplayPolicy(
109
+ toolInfo.replayPolicy ?? staticReplayPolicy,
110
+ toolInfo.fileOps
111
+ );
112
+ const originalExecute = toolInfo.execute;
113
+ const execute = async (params, ctx) => {
114
+ try {
115
+ toolInfo.parameters.parse(params);
116
+ } catch (error) {
117
+ if (error instanceof z.ZodError && toolInfo.formatValidationError) {
118
+ throw new Error(toolInfo.formatValidationError(error), {
119
+ cause: error
120
+ });
121
+ }
122
+ throw new Error(
123
+ `The ${id} tool was called with invalid arguments: ${error}.
124
+ Please rewrite the input so it satisfies the expected schema.`,
125
+ { cause: error }
126
+ );
127
+ }
128
+ const result = await originalExecute(params, ctx);
129
+ if (result.metadata.truncated !== void 0) {
130
+ return result;
131
+ }
132
+ const truncated = truncateOutput(result.output);
133
+ return {
134
+ ...result,
135
+ output: truncated.content,
136
+ metadata: {
137
+ ...result.metadata,
138
+ truncated: truncated.truncated,
139
+ ...truncated.truncated && { outputPath: truncated.outputPath }
140
+ }
141
+ };
142
+ };
143
+ return {
144
+ ...toolInfo,
145
+ replayPolicy,
146
+ execute
147
+ };
148
+ }
149
+ };
150
+ }
151
+ Tool2.define = define;
152
+ function defineSimple(id, config) {
153
+ return define(id, config);
154
+ }
155
+ Tool2.defineSimple = defineSimple;
156
+ })(Tool || (Tool = {}));
157
+ function defineTool(definition) {
158
+ return Tool.define(definition.id, {
159
+ description: definition.description,
160
+ parameters: definition.parameters,
161
+ execute: async (params, ctx) => {
162
+ const result = await definition.execute(params, ctx);
163
+ return {
164
+ title: result.title,
165
+ output: result.output,
166
+ metadata: result.metadata ?? {}
167
+ };
168
+ }
169
+ });
170
+ }
171
+
172
+ export {
173
+ normalizeToolReplayPolicy,
174
+ MAX_LINES,
175
+ MAX_BYTES,
176
+ TRUNCATE_DIR,
177
+ TRUNCATE_GLOB,
178
+ truncateOutput,
179
+ formatSize,
180
+ Tool,
181
+ defineTool
182
+ };
@@ -0,0 +1,258 @@
1
+ // src/context/estimation.ts
2
+ function estimateTokens(text) {
3
+ return Math.ceil(text.length / 4);
4
+ }
5
+ function estimateMessageTokens(message) {
6
+ if (typeof message.content === "string") {
7
+ return estimateTokens(message.content);
8
+ }
9
+ if (Array.isArray(message.content)) {
10
+ let total = 0;
11
+ for (const part of message.content) {
12
+ if (typeof part === "string") {
13
+ total += estimateTokens(part);
14
+ } else if ("text" in part && typeof part.text === "string") {
15
+ total += estimateTokens(part.text);
16
+ } else if ("type" in part && part.type === "image") {
17
+ total += 765;
18
+ }
19
+ }
20
+ return total;
21
+ }
22
+ return 0;
23
+ }
24
+ function estimateConversationTokens(messages) {
25
+ let total = 0;
26
+ for (const message of messages) {
27
+ total += estimateMessageTokens(message);
28
+ total += 4;
29
+ }
30
+ return total;
31
+ }
32
+
33
+ // src/types/agent/compaction.ts
34
+ var PRUNE_PROTECTED_TOOLS = ["skill"];
35
+
36
+ // src/context/pruning.ts
37
+ var DEFAULT_CONTEXT_LIMITS = {
38
+ contextWindow: 128e3,
39
+ reserveTokens: 16e3,
40
+ // Reserve for output
41
+ protectedTokens: 4e4,
42
+ // Keep recent 40 k tokens
43
+ pruneMinimum: 2e4
44
+ // Don't prune until 20 k tokens
45
+ };
46
+ function isContextOverflowing(tokens, limits = DEFAULT_CONTEXT_LIMITS) {
47
+ const threshold = limits.contextWindow - limits.reserveTokens;
48
+ return tokens > threshold;
49
+ }
50
+ function shouldPruneContext(tokens, limits = DEFAULT_CONTEXT_LIMITS) {
51
+ if (tokens < limits.pruneMinimum) return false;
52
+ return isContextOverflowing(tokens, limits);
53
+ }
54
+ function findCutPoint(messages, protectedTokens = DEFAULT_CONTEXT_LIMITS.protectedTokens) {
55
+ if (messages.length === 0) return 0;
56
+ let tokensFromEnd = 0;
57
+ let cutIndex = messages.length;
58
+ for (let i = messages.length - 1; i >= 0; i--) {
59
+ tokensFromEnd += estimateMessageTokens(messages[i]);
60
+ if (tokensFromEnd >= protectedTokens) {
61
+ cutIndex = i;
62
+ break;
63
+ }
64
+ }
65
+ if (cutIndex <= 1) return 0;
66
+ const startIndex = cutIndex >= messages.length ? messages.length - 1 : cutIndex;
67
+ for (let i = startIndex; i >= 1; i--) {
68
+ const msg = messages[i];
69
+ const prevMsg = messages[i - 1];
70
+ if (!msg || !prevMsg) continue;
71
+ if (msg.role === "tool") continue;
72
+ if (prevMsg.role === "assistant" || prevMsg.role === "user") {
73
+ return i;
74
+ }
75
+ }
76
+ return 0;
77
+ }
78
+ function pruneToolResults(messages, protectedTokens = DEFAULT_CONTEXT_LIMITS.protectedTokens, options) {
79
+ const protectedToolSet = /* @__PURE__ */ new Set([
80
+ ...PRUNE_PROTECTED_TOOLS,
81
+ ...options?.protectedTools ?? []
82
+ ]);
83
+ let tokensFromEnd = 0;
84
+ const tokenPositions = [];
85
+ for (let i = messages.length - 1; i >= 0; i--) {
86
+ tokensFromEnd += estimateMessageTokens(messages[i]);
87
+ tokenPositions[i] = tokensFromEnd;
88
+ }
89
+ return messages.map((msg, i) => {
90
+ if (tokenPositions[i] < protectedTokens) return msg;
91
+ if (!("role" in msg) || msg.role !== "tool") return msg;
92
+ const toolMsg = msg;
93
+ if ("compactedAt" in toolMsg && toolMsg.compactedAt) return msg;
94
+ if (toolMsg.toolName && protectedToolSet.has(toolMsg.toolName)) return msg;
95
+ const currentTokens = estimateTokens(toolMsg.content);
96
+ if (currentTokens < 500) return msg;
97
+ return {
98
+ ...toolMsg,
99
+ content: `[Output pruned - was ${currentTokens} tokens]`,
100
+ compactedAt: Date.now()
101
+ };
102
+ });
103
+ }
104
+
105
+ // src/context/summarization.ts
106
+ import { generateText } from "ai";
107
+ var DEFAULT_SUMMARY_PROMPT = `You are summarizing a conversation to continue it with context.
108
+
109
+ Create a structured summary that captures:
110
+ 1. **Goal**: What the user is trying to accomplish
111
+ 2. **Progress**: What has been done so far
112
+ 3. **Decisions**: Key decisions made during the conversation
113
+ 4. **Current State**: Where we left off
114
+ 5. **Next Steps**: What should happen next
115
+
116
+ Be concise but comprehensive. Include specific file paths, function names, and technical details that would be lost otherwise.
117
+
118
+ Format as a clear summary that could be given to another assistant to continue the work.`;
119
+ async function generateSummary(messages, options) {
120
+ const conversationText = messages.map((m) => {
121
+ const role = m.role.toUpperCase();
122
+ const content = typeof m.content === "string" ? m.content : JSON.stringify(m.content);
123
+ return `[${role}]: ${content}`;
124
+ }).join("\n\n");
125
+ const prompt = options.customPrompt || DEFAULT_SUMMARY_PROMPT;
126
+ const { text } = await generateText({
127
+ model: options.model,
128
+ maxOutputTokens: options.maxTokens ?? 2e3,
129
+ system: prompt,
130
+ prompt: `Summarize this conversation:
131
+
132
+ ${conversationText}`
133
+ });
134
+ return text;
135
+ }
136
+ async function pruneContext(messages, options = {}) {
137
+ const limits = options.limits ?? DEFAULT_CONTEXT_LIMITS;
138
+ let currentMessages = [...messages];
139
+ let tokensRemoved = 0;
140
+ let removedCount = 0;
141
+ let summarized = false;
142
+ let summary;
143
+ const initialTokens = estimateConversationTokens(currentMessages);
144
+ if (!shouldPruneContext(initialTokens, limits)) {
145
+ return { messages: currentMessages, removedCount: 0, tokensRemoved: 0, summarized: false };
146
+ }
147
+ const prunedMessages = pruneToolResults(currentMessages, limits.protectedTokens);
148
+ const afterPruneTokens = estimateConversationTokens(prunedMessages);
149
+ tokensRemoved = initialTokens - afterPruneTokens;
150
+ currentMessages = prunedMessages;
151
+ if (!isContextOverflowing(afterPruneTokens, limits)) {
152
+ return { messages: currentMessages, removedCount: 0, tokensRemoved, summarized: false };
153
+ }
154
+ const cutIndex = findCutPoint(currentMessages, limits.protectedTokens);
155
+ if (cutIndex === 0) {
156
+ return { messages: currentMessages, removedCount: 0, tokensRemoved, summarized: false };
157
+ }
158
+ const toSummarize = currentMessages.slice(0, cutIndex);
159
+ const toKeep = currentMessages.slice(cutIndex);
160
+ removedCount = toSummarize.length;
161
+ tokensRemoved += estimateConversationTokens(toSummarize);
162
+ if (options.model) {
163
+ summary = await generateSummary(toSummarize, {
164
+ model: options.model,
165
+ customPrompt: options.summaryPrompt
166
+ });
167
+ summarized = true;
168
+ const summaryMessage = {
169
+ id: crypto.randomUUID(),
170
+ role: "system",
171
+ content: `## Previous Conversation Summary
172
+
173
+ ${summary}`,
174
+ createdAt: /* @__PURE__ */ new Date()
175
+ };
176
+ currentMessages = [summaryMessage, ...toKeep];
177
+ } else {
178
+ currentMessages = toKeep;
179
+ }
180
+ return { messages: currentMessages, removedCount, tokensRemoved, summarized, summary };
181
+ }
182
+
183
+ // src/context/manager.ts
184
+ var ContextManager = class {
185
+ limits;
186
+ model;
187
+ summaryPrompt;
188
+ constructor(options) {
189
+ this.limits = { ...DEFAULT_CONTEXT_LIMITS, ...options?.limits };
190
+ this.model = options?.model;
191
+ this.summaryPrompt = options?.summaryPrompt;
192
+ }
193
+ /** Get a copy of the current context limits. */
194
+ getLimits() {
195
+ return { ...this.limits };
196
+ }
197
+ /** Update context limits (e.g. when switching models). */
198
+ setLimits(limits) {
199
+ this.limits = { ...this.limits, ...limits };
200
+ }
201
+ /** Set the model used for summarisation. */
202
+ setModel(model) {
203
+ this.model = model;
204
+ }
205
+ /** Estimate total tokens for a message array. */
206
+ estimateTokens(messages) {
207
+ return estimateConversationTokens(messages);
208
+ }
209
+ /** Check whether the context is overflowing. */
210
+ isOverflowing(messages) {
211
+ const tokens = this.estimateTokens(messages);
212
+ return isContextOverflowing(tokens, this.limits);
213
+ }
214
+ /** Check whether pruning should be triggered. */
215
+ shouldPrune(messages) {
216
+ const tokens = this.estimateTokens(messages);
217
+ return shouldPruneContext(tokens, this.limits);
218
+ }
219
+ /** Prune context to fit within limits. */
220
+ async prune(messages) {
221
+ return pruneContext(messages, {
222
+ model: this.model,
223
+ limits: this.limits,
224
+ summaryPrompt: this.summaryPrompt
225
+ });
226
+ }
227
+ /**
228
+ * Get a snapshot of token statistics.
229
+ *
230
+ * Useful for dashboards, logging, or deciding whether to prune.
231
+ */
232
+ getStats(messages) {
233
+ const tokens = this.estimateTokens(messages);
234
+ const limit = this.limits.contextWindow - this.limits.reserveTokens;
235
+ return {
236
+ tokens,
237
+ limit,
238
+ available: Math.max(0, limit - tokens),
239
+ utilizationPercent: Math.round(tokens / limit * 100),
240
+ isOverflowing: isContextOverflowing(tokens, this.limits),
241
+ shouldPrune: shouldPruneContext(tokens, this.limits)
242
+ };
243
+ }
244
+ };
245
+
246
+ export {
247
+ estimateTokens,
248
+ estimateMessageTokens,
249
+ estimateConversationTokens,
250
+ DEFAULT_CONTEXT_LIMITS,
251
+ isContextOverflowing,
252
+ shouldPruneContext,
253
+ findCutPoint,
254
+ pruneToolResults,
255
+ generateSummary,
256
+ pruneContext,
257
+ ContextManager
258
+ };