@sudosandwich/limps 0.2.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.
- package/LICENSE +21 -0
- package/README.md +190 -0
- package/dist/agent-parser.d.ts +146 -0
- package/dist/agent-parser.d.ts.map +1 -0
- package/dist/agent-parser.js +448 -0
- package/dist/agent-parser.js.map +1 -0
- package/dist/config.d.ts +54 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +146 -0
- package/dist/config.js.map +1 -0
- package/dist/coordination.d.ts +102 -0
- package/dist/coordination.d.ts.map +1 -0
- package/dist/coordination.js +157 -0
- package/dist/coordination.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +256 -0
- package/dist/index.js.map +1 -0
- package/dist/indexer.d.ts +83 -0
- package/dist/indexer.d.ts.map +1 -0
- package/dist/indexer.js +467 -0
- package/dist/indexer.js.map +1 -0
- package/dist/resources/agents-status.d.ts +32 -0
- package/dist/resources/agents-status.d.ts.map +1 -0
- package/dist/resources/agents-status.js +73 -0
- package/dist/resources/agents-status.js.map +1 -0
- package/dist/resources/decisions-log.d.ts +21 -0
- package/dist/resources/decisions-log.d.ts.map +1 -0
- package/dist/resources/decisions-log.js +146 -0
- package/dist/resources/decisions-log.js.map +1 -0
- package/dist/resources/index.d.ts +10 -0
- package/dist/resources/index.d.ts.map +1 -0
- package/dist/resources/index.js +74 -0
- package/dist/resources/index.js.map +1 -0
- package/dist/resources/plans-full.d.ts +11 -0
- package/dist/resources/plans-full.d.ts.map +1 -0
- package/dist/resources/plans-full.js +71 -0
- package/dist/resources/plans-full.js.map +1 -0
- package/dist/resources/plans-index.d.ts +30 -0
- package/dist/resources/plans-index.d.ts.map +1 -0
- package/dist/resources/plans-index.js +177 -0
- package/dist/resources/plans-index.js.map +1 -0
- package/dist/resources/plans-summary.d.ts +33 -0
- package/dist/resources/plans-summary.d.ts.map +1 -0
- package/dist/resources/plans-summary.js +238 -0
- package/dist/resources/plans-summary.js.map +1 -0
- package/dist/rlm/extractors.d.ts +39 -0
- package/dist/rlm/extractors.d.ts.map +1 -0
- package/dist/rlm/extractors.js +291 -0
- package/dist/rlm/extractors.js.map +1 -0
- package/dist/rlm/helpers-inject.d.ts +13 -0
- package/dist/rlm/helpers-inject.d.ts.map +1 -0
- package/dist/rlm/helpers-inject.js +586 -0
- package/dist/rlm/helpers-inject.js.map +1 -0
- package/dist/rlm/helpers.d.ts +124 -0
- package/dist/rlm/helpers.d.ts.map +1 -0
- package/dist/rlm/helpers.js +381 -0
- package/dist/rlm/helpers.js.map +1 -0
- package/dist/rlm/index.d.ts +12 -0
- package/dist/rlm/index.d.ts.map +1 -0
- package/dist/rlm/index.js +19 -0
- package/dist/rlm/index.js.map +1 -0
- package/dist/rlm/parallel.d.ts +45 -0
- package/dist/rlm/parallel.d.ts.map +1 -0
- package/dist/rlm/parallel.js +76 -0
- package/dist/rlm/parallel.js.map +1 -0
- package/dist/rlm/recursion.d.ts +96 -0
- package/dist/rlm/recursion.d.ts.map +1 -0
- package/dist/rlm/recursion.js +113 -0
- package/dist/rlm/recursion.js.map +1 -0
- package/dist/rlm/sampling.d.ts +100 -0
- package/dist/rlm/sampling.d.ts.map +1 -0
- package/dist/rlm/sampling.js +96 -0
- package/dist/rlm/sampling.js.map +1 -0
- package/dist/rlm/sandbox.d.ts +73 -0
- package/dist/rlm/sandbox.d.ts.map +1 -0
- package/dist/rlm/sandbox.js +160 -0
- package/dist/rlm/sandbox.js.map +1 -0
- package/dist/rlm/security.d.ts +28 -0
- package/dist/rlm/security.d.ts.map +1 -0
- package/dist/rlm/security.js +154 -0
- package/dist/rlm/security.js.map +1 -0
- package/dist/server.d.ts +21 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +107 -0
- package/dist/server.js.map +1 -0
- package/dist/task-parser.d.ts +47 -0
- package/dist/task-parser.d.ts.map +1 -0
- package/dist/task-parser.js +112 -0
- package/dist/task-parser.js.map +1 -0
- package/dist/test-setup.d.ts +6 -0
- package/dist/test-setup.d.ts.map +1 -0
- package/dist/test-setup.js +37 -0
- package/dist/test-setup.js.map +1 -0
- package/dist/tools/claim-task.d.ts +28 -0
- package/dist/tools/claim-task.d.ts.map +1 -0
- package/dist/tools/claim-task.js +288 -0
- package/dist/tools/claim-task.js.map +1 -0
- package/dist/tools/create-doc.d.ts +47 -0
- package/dist/tools/create-doc.d.ts.map +1 -0
- package/dist/tools/create-doc.js +137 -0
- package/dist/tools/create-doc.js.map +1 -0
- package/dist/tools/create-plan.d.ts +25 -0
- package/dist/tools/create-plan.d.ts.map +1 -0
- package/dist/tools/create-plan.js +179 -0
- package/dist/tools/create-plan.js.map +1 -0
- package/dist/tools/delete-doc.d.ts +51 -0
- package/dist/tools/delete-doc.d.ts.map +1 -0
- package/dist/tools/delete-doc.js +194 -0
- package/dist/tools/delete-doc.js.map +1 -0
- package/dist/tools/get-next-task.d.ts +49 -0
- package/dist/tools/get-next-task.d.ts.map +1 -0
- package/dist/tools/get-next-task.js +204 -0
- package/dist/tools/get-next-task.js.map +1 -0
- package/dist/tools/index.d.ts +10 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +122 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/list-docs.d.ts +53 -0
- package/dist/tools/list-docs.d.ts.map +1 -0
- package/dist/tools/list-docs.js +236 -0
- package/dist/tools/list-docs.js.map +1 -0
- package/dist/tools/open-document-in-cursor.d.ts +62 -0
- package/dist/tools/open-document-in-cursor.d.ts.map +1 -0
- package/dist/tools/open-document-in-cursor.js +211 -0
- package/dist/tools/open-document-in-cursor.js.map +1 -0
- package/dist/tools/read-doc.d.ts +44 -0
- package/dist/tools/read-doc.d.ts.map +1 -0
- package/dist/tools/read-doc.js +174 -0
- package/dist/tools/read-doc.js.map +1 -0
- package/dist/tools/release-task.d.ts +28 -0
- package/dist/tools/release-task.d.ts.map +1 -0
- package/dist/tools/release-task.js +154 -0
- package/dist/tools/release-task.js.map +1 -0
- package/dist/tools/rlm-multi-query.d.ts +110 -0
- package/dist/tools/rlm-multi-query.d.ts.map +1 -0
- package/dist/tools/rlm-multi-query.js +348 -0
- package/dist/tools/rlm-multi-query.js.map +1 -0
- package/dist/tools/rlm-query.d.ts +56 -0
- package/dist/tools/rlm-query.d.ts.map +1 -0
- package/dist/tools/rlm-query.js +228 -0
- package/dist/tools/rlm-query.js.map +1 -0
- package/dist/tools/search-docs.d.ts +34 -0
- package/dist/tools/search-docs.d.ts.map +1 -0
- package/dist/tools/search-docs.js +292 -0
- package/dist/tools/search-docs.js.map +1 -0
- package/dist/tools/update-doc.d.ts +149 -0
- package/dist/tools/update-doc.d.ts.map +1 -0
- package/dist/tools/update-doc.js +195 -0
- package/dist/tools/update-doc.js.map +1 -0
- package/dist/tools/update-task-status.d.ts +31 -0
- package/dist/tools/update-task-status.d.ts.map +1 -0
- package/dist/tools/update-task-status.js +303 -0
- package/dist/tools/update-task-status.js.map +1 -0
- package/dist/types.d.ts +50 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/backup.d.ts +76 -0
- package/dist/utils/backup.d.ts.map +1 -0
- package/dist/utils/backup.js +172 -0
- package/dist/utils/backup.js.map +1 -0
- package/dist/utils/errors.d.ts +93 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +125 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/index.d.ts +8 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +9 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/os-paths.d.ts +45 -0
- package/dist/utils/os-paths.d.ts.map +1 -0
- package/dist/utils/os-paths.js +81 -0
- package/dist/utils/os-paths.js.map +1 -0
- package/dist/utils/paths.d.ts +71 -0
- package/dist/utils/paths.d.ts.map +1 -0
- package/dist/utils/paths.js +165 -0
- package/dist/utils/paths.js.map +1 -0
- package/dist/watcher.d.ts +19 -0
- package/dist/watcher.d.ts.map +1 -0
- package/dist/watcher.js +109 -0
- package/dist/watcher.js.map +1 -0
- package/package.json +85 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recursion.d.ts","sourceRoot":"","sources":["../../src/rlm/recursion.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAEL,KAAK,cAAc,EAEnB,KAAK,cAAc,EACnB,KAAK,cAAc,EACpB,MAAM,eAAe,CAAC;AAEvB;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,8BAA8B;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,4BAA4B;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,sCAAsC;IACtC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,uCAAuC;IACvC,IAAI,EAAE,OAAO,CAAC;IACd,2BAA2B;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,uBAAuB;IACvB,OAAO,EAAE,cAAc,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,qCAAqC;IACrC,OAAO,EAAE,OAAO,CAAC;IACjB,sCAAsC;IACtC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gCAAgC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wCAAwC;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,+CAA+C;IAC/C,SAAS,CAAC,EAAE,cAAc,EAAE,CAAC;CAC9B;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gDAAgD;IAChD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uCAAuC;IACvC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iDAAiD;IACjD,KAAK,CAAC,EAAE,cAAc,EAAE,CAAC;IACzB,kEAAkE;IAClE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,sDAAsD;IACtD,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAI7D;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,eAAe,CACnC,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,EAC1B,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,aAAa,EAAE,CAAC,CAgF1B"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recursive sub-call handler for RLM queries.
|
|
3
|
+
* Feature #5: Recursive Sub-Call Handler (MCP Spec 2025-11-25)
|
|
4
|
+
*
|
|
5
|
+
* Processes filtered results from RLM queries using MCP Sampling with Tools.
|
|
6
|
+
* Enables server-side agent loops for recursive LLM processing.
|
|
7
|
+
*/
|
|
8
|
+
import { DepthLimitError } from '../utils/errors.js';
|
|
9
|
+
import { parallelMap } from './parallel.js';
|
|
10
|
+
import { createSamplingMessage, } from './sampling.js';
|
|
11
|
+
/**
|
|
12
|
+
* Check if depth limit is exceeded and throw if so.
|
|
13
|
+
*
|
|
14
|
+
* @param context - Sub-call context
|
|
15
|
+
* @throws DepthLimitError if depth exceeds maxDepth
|
|
16
|
+
*/
|
|
17
|
+
export function checkDepthLimit(context) {
|
|
18
|
+
if (context.depth >= context.maxDepth) {
|
|
19
|
+
throw new DepthLimitError(context.maxDepth, context.depth);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Process sub-calls for items returned from RLM query code execution.
|
|
24
|
+
*
|
|
25
|
+
* This function:
|
|
26
|
+
* 1. Normalizes items to an array (handles single item)
|
|
27
|
+
* 2. Checks depth limit
|
|
28
|
+
* 3. Processes items in parallel with concurrency control
|
|
29
|
+
* 4. For each item, creates a sampling message with the prompt
|
|
30
|
+
* 5. Captures results and tool calls
|
|
31
|
+
* 6. Returns aggregated results in input order
|
|
32
|
+
*
|
|
33
|
+
* @param items - Items from code result (array or single item)
|
|
34
|
+
* @param prompt - Sub_query prompt to apply to each item
|
|
35
|
+
* @param options - Recursion options
|
|
36
|
+
* @returns Array of sub-call results in input order
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* const results = await processSubCalls(
|
|
40
|
+
* ['item1', 'item2', 'item3'],
|
|
41
|
+
* 'Summarize each item',
|
|
42
|
+
* { maxDepth: 1, concurrency: 5 }
|
|
43
|
+
* );
|
|
44
|
+
*/
|
|
45
|
+
export async function processSubCalls(items, prompt, options) {
|
|
46
|
+
const { maxDepth = 1, concurrency = 5, timeout, tools, samplingClient } = options || {};
|
|
47
|
+
// Normalize items to array
|
|
48
|
+
const itemsArray = Array.isArray(items) ? items : [items];
|
|
49
|
+
if (itemsArray.length === 0) {
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
// Create sub-call context
|
|
53
|
+
const context = {
|
|
54
|
+
depth: 0, // Starting depth (will be incremented for nested calls)
|
|
55
|
+
maxDepth,
|
|
56
|
+
};
|
|
57
|
+
// Check depth limit before processing
|
|
58
|
+
checkDepthLimit(context);
|
|
59
|
+
// Process items in parallel
|
|
60
|
+
const parallelOptions = {
|
|
61
|
+
concurrency,
|
|
62
|
+
timeout,
|
|
63
|
+
};
|
|
64
|
+
const results = await parallelMap(itemsArray, async (item, index) => {
|
|
65
|
+
try {
|
|
66
|
+
// Build prompt with item context
|
|
67
|
+
const itemContext = JSON.stringify(item, null, 2);
|
|
68
|
+
const fullPrompt = `${prompt}\n\nContext:\n${itemContext}`;
|
|
69
|
+
// Create sampling request
|
|
70
|
+
const samplingRequest = {
|
|
71
|
+
messages: [{ role: 'user', content: fullPrompt }],
|
|
72
|
+
tools,
|
|
73
|
+
toolChoice: tools && tools.length > 0 ? 'auto' : 'none',
|
|
74
|
+
maxTokens: 2000, // Default max tokens for sub-calls
|
|
75
|
+
};
|
|
76
|
+
// Call sampling (will throw if client not available)
|
|
77
|
+
const response = await createSamplingMessage(samplingRequest, samplingClient);
|
|
78
|
+
// Build result
|
|
79
|
+
return {
|
|
80
|
+
success: true,
|
|
81
|
+
result: response.content,
|
|
82
|
+
index,
|
|
83
|
+
toolCalls: response.toolCalls,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
// Handle errors gracefully - don't throw, include in result
|
|
88
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
89
|
+
return {
|
|
90
|
+
success: false,
|
|
91
|
+
error: errorMessage,
|
|
92
|
+
index,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
}, parallelOptions);
|
|
96
|
+
// Map parallel results to SubCallResult (they're already in the right format)
|
|
97
|
+
return results.map((result) => {
|
|
98
|
+
if (result.success) {
|
|
99
|
+
return result.value;
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
// Find the index from the original items array
|
|
103
|
+
// Since parallelMap preserves order, we can use the result index
|
|
104
|
+
const index = results.indexOf(result);
|
|
105
|
+
return {
|
|
106
|
+
success: false,
|
|
107
|
+
error: result.error,
|
|
108
|
+
index,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=recursion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recursion.js","sourceRoot":"","sources":["../../src/rlm/recursion.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Sampling with Tools wrapper for RLM sub-calls.
|
|
3
|
+
* Feature #5: Recursive Sub-Call Handler (MCP Spec 2025-11-25)
|
|
4
|
+
*
|
|
5
|
+
* Enables server-side agent loops using MCP Sampling with Tools (SEP-1577).
|
|
6
|
+
* This allows the server to invoke the LLM directly for sub-queries.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Tool definition for sampling requests (JSON Schema 2020-12).
|
|
10
|
+
*/
|
|
11
|
+
export interface ToolDefinition {
|
|
12
|
+
name: string;
|
|
13
|
+
description: string;
|
|
14
|
+
inputSchema: object;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Tool call result captured during sampling.
|
|
18
|
+
*/
|
|
19
|
+
export interface ToolCallResult {
|
|
20
|
+
name: string;
|
|
21
|
+
input: unknown;
|
|
22
|
+
output: unknown;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Sampling request matching MCP Spec 2025-11-25.
|
|
26
|
+
*/
|
|
27
|
+
export interface SamplingRequest {
|
|
28
|
+
messages: {
|
|
29
|
+
role: 'user' | 'assistant';
|
|
30
|
+
content: string;
|
|
31
|
+
}[];
|
|
32
|
+
tools?: ToolDefinition[];
|
|
33
|
+
toolChoice?: 'auto' | 'none' | {
|
|
34
|
+
type: 'tool';
|
|
35
|
+
name: string;
|
|
36
|
+
};
|
|
37
|
+
maxTokens?: number;
|
|
38
|
+
systemPrompt?: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Sampling response with content and tool calls.
|
|
42
|
+
*/
|
|
43
|
+
export interface SamplingResponse {
|
|
44
|
+
content: string;
|
|
45
|
+
toolCalls?: ToolCallResult[];
|
|
46
|
+
stopReason: 'end_turn' | 'tool_use' | 'max_tokens';
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Client interface for MCP sampling capability.
|
|
50
|
+
* This abstraction allows for mocking in tests and future real client integration.
|
|
51
|
+
*/
|
|
52
|
+
export interface SamplingClient {
|
|
53
|
+
/**
|
|
54
|
+
* Create a sampling message (MCP createMessage request).
|
|
55
|
+
* @param request - Sampling request
|
|
56
|
+
* @returns Sampling response with content and tool calls
|
|
57
|
+
*/
|
|
58
|
+
createMessage(request: SamplingRequest): Promise<SamplingResponse>;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Create a sampling message using the provided client.
|
|
62
|
+
*
|
|
63
|
+
* This is a wrapper around MCP Sampling with Tools (SEP-1577) that:
|
|
64
|
+
* - Formats the request according to MCP spec
|
|
65
|
+
* - Handles tool_use stop reason (executes tool, continues sampling)
|
|
66
|
+
* - Captures tool call results
|
|
67
|
+
* - Respects toolChoice setting
|
|
68
|
+
*
|
|
69
|
+
* @param request - Sampling request
|
|
70
|
+
* @param client - Optional sampling client (for testing/mocking)
|
|
71
|
+
* @returns Sampling response with content and tool calls
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* const response = await createSamplingMessage({
|
|
75
|
+
* messages: [{ role: 'user', content: 'Summarize this section' }],
|
|
76
|
+
* tools: [{ name: 'analyze', description: '...', inputSchema: {...} }],
|
|
77
|
+
* toolChoice: 'auto',
|
|
78
|
+
* maxTokens: 1000
|
|
79
|
+
* }, mockClient);
|
|
80
|
+
*/
|
|
81
|
+
export declare function createSamplingMessage(request: SamplingRequest, client?: SamplingClient): Promise<SamplingResponse>;
|
|
82
|
+
/**
|
|
83
|
+
* Mock sampling client for testing.
|
|
84
|
+
* Returns predictable responses based on request content.
|
|
85
|
+
*/
|
|
86
|
+
export declare class MockSamplingClient implements SamplingClient {
|
|
87
|
+
private stringResponses;
|
|
88
|
+
private regexResponses;
|
|
89
|
+
private defaultResponse?;
|
|
90
|
+
/**
|
|
91
|
+
* Set a default response generator.
|
|
92
|
+
*/
|
|
93
|
+
setDefaultResponse(generator: (request: SamplingRequest) => SamplingResponse): void;
|
|
94
|
+
/**
|
|
95
|
+
* Set a specific response for a message content pattern.
|
|
96
|
+
*/
|
|
97
|
+
setResponse(pattern: string | RegExp, response: SamplingResponse): void;
|
|
98
|
+
createMessage(request: SamplingRequest): Promise<SamplingResponse>;
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=sampling.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sampling.d.ts","sourceRoot":"","sources":["../../src/rlm/sampling.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC5D,KAAK,CAAC,EAAE,cAAc,EAAE,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,cAAc,EAAE,CAAC;IAC7B,UAAU,EAAE,UAAU,GAAG,UAAU,GAAG,YAAY,CAAC;CACpD;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B;;;;OAIG;IACH,aAAa,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;CACpE;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,eAAe,EACxB,MAAM,CAAC,EAAE,cAAc,GACtB,OAAO,CAAC,gBAAgB,CAAC,CAqB3B;AAED;;;GAGG;AACH,qBAAa,kBAAmB,YAAW,cAAc;IACvD,OAAO,CAAC,eAAe,CAAuC;IAC9D,OAAO,CAAC,cAAc,CAAuD;IAC7E,OAAO,CAAC,eAAe,CAAC,CAAiD;IAEzE;;OAEG;IACH,kBAAkB,CAAC,SAAS,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,gBAAgB,GAAG,IAAI;IAInF;;OAEG;IACH,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,gBAAgB,GAAG,IAAI;IAQjE,aAAa,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,CAAC;CA4BzE"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Sampling with Tools wrapper for RLM sub-calls.
|
|
3
|
+
* Feature #5: Recursive Sub-Call Handler (MCP Spec 2025-11-25)
|
|
4
|
+
*
|
|
5
|
+
* Enables server-side agent loops using MCP Sampling with Tools (SEP-1577).
|
|
6
|
+
* This allows the server to invoke the LLM directly for sub-queries.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Create a sampling message using the provided client.
|
|
10
|
+
*
|
|
11
|
+
* This is a wrapper around MCP Sampling with Tools (SEP-1577) that:
|
|
12
|
+
* - Formats the request according to MCP spec
|
|
13
|
+
* - Handles tool_use stop reason (executes tool, continues sampling)
|
|
14
|
+
* - Captures tool call results
|
|
15
|
+
* - Respects toolChoice setting
|
|
16
|
+
*
|
|
17
|
+
* @param request - Sampling request
|
|
18
|
+
* @param client - Optional sampling client (for testing/mocking)
|
|
19
|
+
* @returns Sampling response with content and tool calls
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* const response = await createSamplingMessage({
|
|
23
|
+
* messages: [{ role: 'user', content: 'Summarize this section' }],
|
|
24
|
+
* tools: [{ name: 'analyze', description: '...', inputSchema: {...} }],
|
|
25
|
+
* toolChoice: 'auto',
|
|
26
|
+
* maxTokens: 1000
|
|
27
|
+
* }, mockClient);
|
|
28
|
+
*/
|
|
29
|
+
export async function createSamplingMessage(request, client) {
|
|
30
|
+
// If no client provided, throw error (real implementation will get client from context)
|
|
31
|
+
if (!client) {
|
|
32
|
+
throw new Error('Sampling client not available. Sub-calls require MCP client with sampling capability.');
|
|
33
|
+
}
|
|
34
|
+
// Call the client's createMessage method
|
|
35
|
+
const response = await client.createMessage(request);
|
|
36
|
+
// The response should already include content and tool calls
|
|
37
|
+
// In a real implementation, if stopReason is 'tool_use', we would:
|
|
38
|
+
// 1. Execute the tool
|
|
39
|
+
// 2. Continue sampling with tool result
|
|
40
|
+
// 3. Repeat until stopReason is 'end_turn' or 'max_tokens'
|
|
41
|
+
//
|
|
42
|
+
// For now, we return the response as-is. The mock client can simulate
|
|
43
|
+
// this behavior for testing.
|
|
44
|
+
return response;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Mock sampling client for testing.
|
|
48
|
+
* Returns predictable responses based on request content.
|
|
49
|
+
*/
|
|
50
|
+
export class MockSamplingClient {
|
|
51
|
+
stringResponses = new Map();
|
|
52
|
+
regexResponses = [];
|
|
53
|
+
defaultResponse;
|
|
54
|
+
/**
|
|
55
|
+
* Set a default response generator.
|
|
56
|
+
*/
|
|
57
|
+
setDefaultResponse(generator) {
|
|
58
|
+
this.defaultResponse = generator;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Set a specific response for a message content pattern.
|
|
62
|
+
*/
|
|
63
|
+
setResponse(pattern, response) {
|
|
64
|
+
if (pattern instanceof RegExp) {
|
|
65
|
+
this.regexResponses.push({ regex: pattern, response });
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
this.stringResponses.set(pattern, response);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
async createMessage(request) {
|
|
72
|
+
const messageContent = request.messages[request.messages.length - 1]?.content || '';
|
|
73
|
+
// Check regex patterns first (more specific)
|
|
74
|
+
for (const { regex, response } of this.regexResponses) {
|
|
75
|
+
if (regex.test(messageContent)) {
|
|
76
|
+
return response;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// Check string patterns
|
|
80
|
+
for (const [pattern, response] of this.stringResponses.entries()) {
|
|
81
|
+
if (messageContent.includes(pattern)) {
|
|
82
|
+
return response;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Use default response generator if available
|
|
86
|
+
if (this.defaultResponse) {
|
|
87
|
+
return this.defaultResponse(request);
|
|
88
|
+
}
|
|
89
|
+
// Default: return a simple response
|
|
90
|
+
return {
|
|
91
|
+
content: `Mock response for: ${messageContent.substring(0, 50)}...`,
|
|
92
|
+
stopReason: 'end_turn',
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=sampling.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sampling.js","sourceRoot":"","sources":["../../src/rlm/sampling.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAqDH;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,OAAwB,EACxB,MAAuB;IAEvB,wFAAwF;IACxF,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,uFAAuF,CACxF,CAAC;IACJ,CAAC;IAED,yCAAyC;IACzC,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IAErD,6DAA6D;IAC7D,mEAAmE;IACnE,sBAAsB;IACtB,wCAAwC;IACxC,2DAA2D;IAC3D,EAAE;IACF,sEAAsE;IACtE,6BAA6B;IAE7B,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;GAGG;AACH,MAAM,OAAO,kBAAkB;IACrB,eAAe,GAAG,IAAI,GAAG,EAA4B,CAAC;IACtD,cAAc,GAAoD,EAAE,CAAC;IACrE,eAAe,CAAkD;IAEzE;;OAEG;IACH,kBAAkB,CAAC,SAAyD;QAC1E,IAAI,CAAC,eAAe,GAAG,SAAS,CAAC;IACnC,CAAC;IAED;;OAEG;IACH,WAAW,CAAC,OAAwB,EAAE,QAA0B;QAC9D,IAAI,OAAO,YAAY,MAAM,EAAE,CAAC;YAC9B,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC;QACzD,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC9C,CAAC;IACH,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,OAAwB;QAC1C,MAAM,cAAc,GAAG,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,OAAO,IAAI,EAAE,CAAC;QAEpF,6CAA6C;QAC7C,KAAK,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACtD,IAAI,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC;gBAC/B,OAAO,QAAQ,CAAC;YAClB,CAAC;QACH,CAAC;QAED,wBAAwB;QACxB,KAAK,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,IAAI,IAAI,CAAC,eAAe,CAAC,OAAO,EAAE,EAAE,CAAC;YACjE,IAAI,cAAc,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;gBACrC,OAAO,QAAQ,CAAC;YAClB,CAAC;QACH,CAAC;QAED,8CAA8C;QAC9C,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YACzB,OAAO,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;QACvC,CAAC;QAED,oCAAoC;QACpC,OAAO;YACL,OAAO,EAAE,sBAAsB,cAAc,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK;YACnE,UAAU,EAAE,UAAU;SACvB,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sandboxed JavaScript execution environment for RLM queries.
|
|
3
|
+
* Feature #1: RLM Environment Core
|
|
4
|
+
*
|
|
5
|
+
* Uses QuickJS via quickjs-emscripten for true sandboxing.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Document representation in RLM environment.
|
|
9
|
+
*/
|
|
10
|
+
export interface DocVariable {
|
|
11
|
+
content: string;
|
|
12
|
+
metadata: {
|
|
13
|
+
path: string;
|
|
14
|
+
size: number;
|
|
15
|
+
lines: number;
|
|
16
|
+
modified: string;
|
|
17
|
+
};
|
|
18
|
+
path: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Sandbox configuration options.
|
|
22
|
+
*/
|
|
23
|
+
export interface SandboxOptions {
|
|
24
|
+
/** Execution timeout in ms (default: 5000) */
|
|
25
|
+
timeout?: number;
|
|
26
|
+
/** Memory limit in MB (default: 64) */
|
|
27
|
+
memoryLimit?: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Result of code execution.
|
|
31
|
+
*/
|
|
32
|
+
export interface ExecutionResult<T> {
|
|
33
|
+
result: T;
|
|
34
|
+
executionTimeMs: number;
|
|
35
|
+
memoryUsed?: number;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Timeout error thrown when execution exceeds time limit.
|
|
39
|
+
*/
|
|
40
|
+
export declare class TimeoutError extends Error {
|
|
41
|
+
constructor(timeoutMs: number);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Memory error thrown when execution exceeds memory limit.
|
|
45
|
+
*/
|
|
46
|
+
export declare class MemoryError extends Error {
|
|
47
|
+
constructor(limitMb: number);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Sandboxed RLM execution environment.
|
|
51
|
+
*/
|
|
52
|
+
export interface RlmEnvironment {
|
|
53
|
+
/**
|
|
54
|
+
* Execute JavaScript code with doc/docs variable available.
|
|
55
|
+
* @param code - JavaScript code to execute
|
|
56
|
+
* @returns Promise resolving to execution result
|
|
57
|
+
* @throws TimeoutError if execution exceeds timeout
|
|
58
|
+
* @throws MemoryError if execution exceeds memory limit
|
|
59
|
+
* @throws SecurityError if code attempts prohibited operations
|
|
60
|
+
*/
|
|
61
|
+
execute<T>(code: string): Promise<ExecutionResult<T>>;
|
|
62
|
+
/**
|
|
63
|
+
* Release resources. Always call when done.
|
|
64
|
+
*/
|
|
65
|
+
dispose(): void;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Create a sandboxed execution environment with document(s) loaded.
|
|
69
|
+
* @param doc - Single DocVariable or array for multi-query
|
|
70
|
+
* @param options - Sandbox configuration
|
|
71
|
+
*/
|
|
72
|
+
export declare function createEnvironment(doc: DocVariable | DocVariable[], options?: SandboxOptions): Promise<RlmEnvironment>;
|
|
73
|
+
//# sourceMappingURL=sandbox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../../src/rlm/sandbox.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAUH;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE;QACR,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,8CAA8C;IAC9C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uCAAuC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe,CAAC,CAAC;IAChC,MAAM,EAAE,CAAC,CAAC;IACV,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,qBAAa,YAAa,SAAQ,KAAK;gBACzB,SAAS,EAAE,MAAM;CAQ9B;AAED;;GAEG;AACH,qBAAa,WAAY,SAAQ,KAAK;gBACxB,OAAO,EAAE,MAAM;CAQ5B;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B;;;;;;;OAOG;IACH,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC;IAEtD;;OAEG;IACH,OAAO,IAAI,IAAI,CAAC;CACjB;AAuBD;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,WAAW,GAAG,WAAW,EAAE,EAChC,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,cAAc,CAAC,CA2HzB"}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sandboxed JavaScript execution environment for RLM queries.
|
|
3
|
+
* Feature #1: RLM Environment Core
|
|
4
|
+
*
|
|
5
|
+
* Uses QuickJS via quickjs-emscripten for true sandboxing.
|
|
6
|
+
*/
|
|
7
|
+
import { getQuickJS, shouldInterruptAfterDeadline, } from 'quickjs-emscripten';
|
|
8
|
+
import { validateCode } from './security.js';
|
|
9
|
+
import { getHelpersCode } from './helpers-inject.js';
|
|
10
|
+
/**
|
|
11
|
+
* Timeout error thrown when execution exceeds time limit.
|
|
12
|
+
*/
|
|
13
|
+
export class TimeoutError extends Error {
|
|
14
|
+
constructor(timeoutMs) {
|
|
15
|
+
super(`Execution timeout after ${timeoutMs}ms`);
|
|
16
|
+
this.name = 'TimeoutError';
|
|
17
|
+
Object.setPrototypeOf(this, TimeoutError.prototype);
|
|
18
|
+
if (Error.captureStackTrace) {
|
|
19
|
+
Error.captureStackTrace(this, TimeoutError);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Memory error thrown when execution exceeds memory limit.
|
|
25
|
+
*/
|
|
26
|
+
export class MemoryError extends Error {
|
|
27
|
+
constructor(limitMb) {
|
|
28
|
+
super(`Memory limit exceeded (${limitMb}MB)`);
|
|
29
|
+
this.name = 'MemoryError';
|
|
30
|
+
Object.setPrototypeOf(this, MemoryError.prototype);
|
|
31
|
+
if (Error.captureStackTrace) {
|
|
32
|
+
Error.captureStackTrace(this, MemoryError);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// Cached QuickJS module
|
|
37
|
+
let quickJSModule = null;
|
|
38
|
+
/**
|
|
39
|
+
* Get or initialize the QuickJS WASM module.
|
|
40
|
+
*/
|
|
41
|
+
async function getQuickJSModule() {
|
|
42
|
+
if (!quickJSModule) {
|
|
43
|
+
quickJSModule = await getQuickJS();
|
|
44
|
+
}
|
|
45
|
+
return quickJSModule;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Default sandbox options.
|
|
49
|
+
*/
|
|
50
|
+
const DEFAULT_OPTIONS = {
|
|
51
|
+
timeout: 5000,
|
|
52
|
+
memoryLimit: 64,
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Create a sandboxed execution environment with document(s) loaded.
|
|
56
|
+
* @param doc - Single DocVariable or array for multi-query
|
|
57
|
+
* @param options - Sandbox configuration
|
|
58
|
+
*/
|
|
59
|
+
export async function createEnvironment(doc, options) {
|
|
60
|
+
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
61
|
+
const QuickJS = await getQuickJSModule();
|
|
62
|
+
// Create runtime with memory limit
|
|
63
|
+
const runtime = QuickJS.newRuntime();
|
|
64
|
+
runtime.setMemoryLimit(opts.memoryLimit * 1024 * 1024); // Convert MB to bytes
|
|
65
|
+
runtime.setMaxStackSize(1024 * 1024); // 1MB stack
|
|
66
|
+
// Create context
|
|
67
|
+
const context = runtime.newContext();
|
|
68
|
+
// Set up doc/docs globals
|
|
69
|
+
const isArray = Array.isArray(doc);
|
|
70
|
+
const docData = isArray ? doc : doc;
|
|
71
|
+
// Inject helper functions first
|
|
72
|
+
const helpersCode = getHelpersCode();
|
|
73
|
+
const helpersResult = context.evalCode(helpersCode);
|
|
74
|
+
if (helpersResult.error) {
|
|
75
|
+
const errorMsg = context.dump(helpersResult.error);
|
|
76
|
+
helpersResult.error.dispose();
|
|
77
|
+
context.dispose();
|
|
78
|
+
runtime.dispose();
|
|
79
|
+
throw new Error(`Failed to inject helpers: ${String(errorMsg)}`);
|
|
80
|
+
}
|
|
81
|
+
helpersResult.value.dispose();
|
|
82
|
+
// Inject the document data as JSON and parse it in the sandbox
|
|
83
|
+
const docJson = JSON.stringify(docData);
|
|
84
|
+
const setupCode = isArray
|
|
85
|
+
? `const docs = ${docJson}; const doc = docs[0];`
|
|
86
|
+
: `const doc = ${docJson};`;
|
|
87
|
+
const setupResult = context.evalCode(setupCode);
|
|
88
|
+
if (setupResult.error) {
|
|
89
|
+
const errorMsg = context.dump(setupResult.error);
|
|
90
|
+
setupResult.error.dispose();
|
|
91
|
+
context.dispose();
|
|
92
|
+
runtime.dispose();
|
|
93
|
+
throw new Error(`Failed to initialize environment: ${String(errorMsg)}`);
|
|
94
|
+
}
|
|
95
|
+
setupResult.value.dispose();
|
|
96
|
+
let disposed = false;
|
|
97
|
+
return {
|
|
98
|
+
async execute(code) {
|
|
99
|
+
if (disposed) {
|
|
100
|
+
throw new Error('Environment has been disposed');
|
|
101
|
+
}
|
|
102
|
+
// Validate code for security
|
|
103
|
+
validateCode(code);
|
|
104
|
+
const startTime = performance.now();
|
|
105
|
+
// Set up interrupt handler for timeout
|
|
106
|
+
const deadline = Date.now() + opts.timeout;
|
|
107
|
+
runtime.setInterruptHandler(shouldInterruptAfterDeadline(deadline));
|
|
108
|
+
// Evaluate the code directly - QuickJS returns the last expression value
|
|
109
|
+
const result = context.evalCode(code);
|
|
110
|
+
const executionTimeMs = performance.now() - startTime;
|
|
111
|
+
// Check for errors
|
|
112
|
+
if (result.error) {
|
|
113
|
+
const errorDump = context.dump(result.error);
|
|
114
|
+
result.error.dispose();
|
|
115
|
+
// Handle the error dump - it could be an object with message property
|
|
116
|
+
let errorStr;
|
|
117
|
+
if (typeof errorDump === 'object' && errorDump !== null) {
|
|
118
|
+
const errorObj = errorDump;
|
|
119
|
+
errorStr = errorObj.message
|
|
120
|
+
? String(errorObj.message)
|
|
121
|
+
: errorObj.name
|
|
122
|
+
? String(errorObj.name)
|
|
123
|
+
: JSON.stringify(errorDump);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
errorStr = String(errorDump);
|
|
127
|
+
}
|
|
128
|
+
// Check if it's an interrupt (timeout)
|
|
129
|
+
if (errorStr.includes('interrupted') ||
|
|
130
|
+
errorStr.includes('InternalError') ||
|
|
131
|
+
errorStr.includes('stack') // QuickJS throws stack overflow on interrupt
|
|
132
|
+
) {
|
|
133
|
+
throw new TimeoutError(opts.timeout);
|
|
134
|
+
}
|
|
135
|
+
// Check if it's a memory error
|
|
136
|
+
if (errorStr.includes('out of memory') ||
|
|
137
|
+
errorStr.includes('memory') ||
|
|
138
|
+
errorStr.includes('allocation')) {
|
|
139
|
+
throw new MemoryError(opts.memoryLimit);
|
|
140
|
+
}
|
|
141
|
+
throw new Error(`Execution error: ${errorStr}`);
|
|
142
|
+
}
|
|
143
|
+
// Get result value
|
|
144
|
+
const value = context.dump(result.value);
|
|
145
|
+
result.value.dispose();
|
|
146
|
+
return {
|
|
147
|
+
result: value,
|
|
148
|
+
executionTimeMs,
|
|
149
|
+
};
|
|
150
|
+
},
|
|
151
|
+
dispose() {
|
|
152
|
+
if (!disposed) {
|
|
153
|
+
disposed = true;
|
|
154
|
+
context.dispose();
|
|
155
|
+
runtime.dispose();
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=sandbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../../src/rlm/sandbox.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security validation for RLM sandbox execution.
|
|
3
|
+
* Feature #6: Security Sandbox
|
|
4
|
+
*
|
|
5
|
+
* Blocks dangerous APIs and validates code before execution.
|
|
6
|
+
* This is defense-in-depth; isolated-vm provides true isolation.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* APIs that are blocked from sandbox execution.
|
|
10
|
+
* These are validated via static analysis before code runs.
|
|
11
|
+
*/
|
|
12
|
+
export declare const BLOCKED_APIS: readonly string[];
|
|
13
|
+
/**
|
|
14
|
+
* Security error thrown when code contains prohibited operations.
|
|
15
|
+
*/
|
|
16
|
+
export declare class SecurityError extends Error {
|
|
17
|
+
readonly violation: string;
|
|
18
|
+
constructor(violation: string, message: string);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Pre-execution validation of code string.
|
|
22
|
+
* Scans for BLOCKED_APIS patterns and throws SecurityError if found.
|
|
23
|
+
*
|
|
24
|
+
* @param code - JavaScript code to validate
|
|
25
|
+
* @throws SecurityError if code contains blocked patterns
|
|
26
|
+
*/
|
|
27
|
+
export declare function validateCode(code: string): void;
|
|
28
|
+
//# sourceMappingURL=security.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security.d.ts","sourceRoot":"","sources":["../../src/rlm/security.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;GAGG;AACH,eAAO,MAAM,YAAY,EAAE,SAAS,MAAM,EAkBhC,CAAC;AAEX;;GAEG;AACH,qBAAa,aAAc,SAAQ,KAAK;IACtC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;gBAEf,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAa/C;AAqGD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAY/C"}
|