@rotifer/playground 0.25.1 → 0.26.1
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/CHANGELOG.md +35 -0
- package/README.md +8 -8
- package/README.zh.md +9 -9
- package/dist/commands/arena-submit.d.ts.map +1 -1
- package/dist/commands/arena-submit.js +2 -57
- package/dist/commands/arena-submit.js.map +1 -1
- package/dist/commands/compile.d.ts.map +1 -1
- package/dist/commands/compile.js +22 -0
- package/dist/commands/compile.js.map +1 -1
- package/dist/commands/install.d.ts.map +1 -1
- package/dist/commands/install.js +18 -0
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/publish.d.ts.map +1 -1
- package/dist/commands/publish.js +49 -0
- package/dist/commands/publish.js.map +1 -1
- package/dist/commands/test.d.ts.map +1 -1
- package/dist/commands/test.js +164 -0
- package/dist/commands/test.js.map +1 -1
- package/dist/scanner/rules.d.ts.map +1 -1
- package/dist/scanner/rules.js +14 -4
- package/dist/scanner/rules.js.map +1 -1
- package/dist/testsuite/gate.d.ts +82 -0
- package/dist/testsuite/gate.d.ts.map +1 -0
- package/dist/testsuite/gate.js +179 -0
- package/dist/testsuite/gate.js.map +1 -0
- package/dist/testsuite/generate.d.ts +12 -0
- package/dist/testsuite/generate.d.ts.map +1 -0
- package/dist/testsuite/generate.js +70 -0
- package/dist/testsuite/generate.js.map +1 -0
- package/dist/testsuite/load.d.ts +23 -0
- package/dist/testsuite/load.d.ts.map +1 -0
- package/dist/testsuite/load.js +121 -0
- package/dist/testsuite/load.js.map +1 -0
- package/dist/testsuite/run.d.ts +109 -0
- package/dist/testsuite/run.d.ts.map +1 -0
- package/dist/testsuite/run.js +298 -0
- package/dist/testsuite/run.js.map +1 -0
- package/dist/testsuite/scaffold.d.ts +20 -0
- package/dist/testsuite/scaffold.d.ts.map +1 -0
- package/dist/testsuite/scaffold.js +146 -0
- package/dist/testsuite/scaffold.js.map +1 -0
- package/dist/testsuite/types.d.ts +67 -0
- package/dist/testsuite/types.d.ts.map +1 -0
- package/dist/testsuite/types.js +19 -0
- package/dist/testsuite/types.js.map +1 -0
- package/dist/utils/artifact-consistency.d.ts +37 -0
- package/dist/utils/artifact-consistency.d.ts.map +1 -0
- package/dist/utils/artifact-consistency.js +43 -0
- package/dist/utils/artifact-consistency.js.map +1 -0
- package/genes/genesis-code-format/.cloud-manifest.json +4 -4
- package/genes/genesis-code-format/index.ts +24 -6
- package/genes/genesis-code-format/phenotype.json +36 -7
- package/genes/genesis-code-format/testsuite.json +25 -0
- package/genes/genesis-file-read/.cloud-manifest.json +4 -4
- package/genes/genesis-file-read/index.ts +21 -2
- package/genes/genesis-file-read/phenotype.json +32 -8
- package/genes/genesis-file-read/testsuite.json +41 -0
- package/genes/genesis-web-search/.cloud-manifest.json +4 -4
- package/genes/genesis-web-search/index.ts +23 -4
- package/genes/genesis-web-search/phenotype.json +36 -9
- package/genes/genesis-web-search/testsuite.json +82 -0
- package/genes/genesis-web-search-lite/.cloud-manifest.json +4 -4
- package/genes/genesis-web-search-lite/index.ts +14 -2
- package/genes/genesis-web-search-lite/phenotype.json +21 -5
- package/genes/genesis-web-search-lite/testsuite.json +24 -0
- package/package.json +12 -7
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Does a Gene's declared fidelity match what is actually on disk / in the
|
|
4
|
+
* registry record?
|
|
5
|
+
*
|
|
6
|
+
* spec §4.2 defines NATIVE as "core logic compiled to Rotifer IR". A record
|
|
7
|
+
* declaring Native with no artifact is therefore self-contradicting, and until
|
|
8
|
+
* 2026-09-07 both commands that meet one said nothing about it:
|
|
9
|
+
*
|
|
10
|
+
* install downloaded nothing (no else branch on `if (gene.wasm_url)`) and
|
|
11
|
+
* then reported "Gene installed! Fidelity: Native" — for a Gene that
|
|
12
|
+
* answers `rotifer run` with "No runnable source found".
|
|
13
|
+
* compile wrote `.compile-result.json` with `fidelity: "Wrapped"` while
|
|
14
|
+
* leaving phenotype.json saying Native, and reported *success*:
|
|
15
|
+
* "validated (Wrapped fidelity)". Two files in one directory
|
|
16
|
+
* disagreeing, and a green tick over a Gene that cannot run.
|
|
17
|
+
*
|
|
18
|
+
* The judgement lives here, apart from the commands, so it can be tested
|
|
19
|
+
* without a network or a compiler.
|
|
20
|
+
*/
|
|
21
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
+
exports.checkNativeArtifact = checkNativeArtifact;
|
|
23
|
+
function checkNativeArtifact(input) {
|
|
24
|
+
if (input.fidelity !== "Native")
|
|
25
|
+
return { status: "consistent" };
|
|
26
|
+
if (input.hasArtifact)
|
|
27
|
+
return { status: "consistent" };
|
|
28
|
+
// Source but no artifact is a normal working state — it compiles into one.
|
|
29
|
+
// Only the case with neither is a Gene that claims Native and cannot be.
|
|
30
|
+
if (input.hasSource)
|
|
31
|
+
return { status: "consistent" };
|
|
32
|
+
return {
|
|
33
|
+
status: "native-without-artifact",
|
|
34
|
+
message: `'${input.geneName}' declares fidelity Native but ships no compiled artifact. ` +
|
|
35
|
+
`spec §4.2 defines Native as core logic compiled to Rotifer IR, so this Gene ` +
|
|
36
|
+
`cannot run: 'rotifer run ${input.geneName}' will report "No runnable source found".`,
|
|
37
|
+
hint: [
|
|
38
|
+
`This is the Gene's own defect, not your setup — nothing you can configure fixes it.`,
|
|
39
|
+
`Ask its author to republish with the artifact, or pick another Gene.`,
|
|
40
|
+
],
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=artifact-consistency.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifact-consistency.js","sourceRoot":"","sources":["../../src/utils/artifact-consistency.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;GAkBG;;AAOH,kDA0BC;AA1BD,SAAgB,mBAAmB,CAAC,KAOnC;IACC,IAAI,KAAK,CAAC,QAAQ,KAAK,QAAQ;QAAE,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;IACjE,IAAI,KAAK,CAAC,WAAW;QAAE,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;IAEvD,2EAA2E;IAC3E,yEAAyE;IACzE,IAAI,KAAK,CAAC,SAAS;QAAE,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;IAErD,OAAO;QACL,MAAM,EAAE,yBAAyB;QACjC,OAAO,EACL,IAAI,KAAK,CAAC,QAAQ,6DAA6D;YAC/E,8EAA8E;YAC9E,4BAA4B,KAAK,CAAC,QAAQ,2CAA2C;QACvF,IAAI,EAAE;YACJ,qFAAqF;YACrF,sEAAsE;SACvE;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"cloud_id": "
|
|
3
|
-
"owner": "
|
|
4
|
-
"version": "0.
|
|
5
|
-
"published_at": "2026-
|
|
2
|
+
"cloud_id": "27903d83-b263-4eb4-acb4-67a2695b51dd",
|
|
3
|
+
"owner": "xiaoba-dev",
|
|
4
|
+
"version": "0.2.0",
|
|
5
|
+
"published_at": "2026-09-08T15:40:25.013Z"
|
|
6
6
|
}
|
|
@@ -15,19 +15,37 @@ interface CodeFormatOutput {
|
|
|
15
15
|
* Formats source code. MVP uses simple heuristic formatting.
|
|
16
16
|
* Production binding would delegate to prettier/rustfmt/etc.
|
|
17
17
|
*/
|
|
18
|
+
const LANGUAGES = ["typescript", "javascript", "json", "markdown", "rust"] as const;
|
|
19
|
+
|
|
18
20
|
export function express(input: CodeFormatInput): CodeFormatOutput {
|
|
21
|
+
// `input.code` went straight into .replace(), so anything that was not a
|
|
22
|
+
// string threw — all three illegal shapes crashed this Gene rather than
|
|
23
|
+
// producing an answer. The schema declares the type, so a value of another
|
|
24
|
+
// type is treated as a missing one.
|
|
25
|
+
const rawCode: unknown = input.code;
|
|
26
|
+
const code = typeof rawCode === "string" ? rawCode : "";
|
|
27
|
+
|
|
28
|
+
// `language` was echoed into the output unchecked, so a value outside the
|
|
29
|
+
// declared enum came back out as if the Gene had recognised it. It did not:
|
|
30
|
+
// an unrecognised language falls through to the generic whitespace pass, and
|
|
31
|
+
// the output should say so rather than repeat what it was handed.
|
|
32
|
+
const rawLanguage: unknown = input.language;
|
|
33
|
+
const language = (LANGUAGES as readonly unknown[]).includes(rawLanguage)
|
|
34
|
+
? (rawLanguage as string)
|
|
35
|
+
: "unknown";
|
|
36
|
+
|
|
19
37
|
let formatted: string;
|
|
20
38
|
|
|
21
|
-
switch (
|
|
39
|
+
switch (language) {
|
|
22
40
|
case "json":
|
|
23
41
|
try {
|
|
24
|
-
formatted = JSON.stringify(JSON.parse(
|
|
42
|
+
formatted = JSON.stringify(JSON.parse(code), null, 2);
|
|
25
43
|
} catch {
|
|
26
|
-
formatted =
|
|
44
|
+
formatted = code;
|
|
27
45
|
}
|
|
28
46
|
break;
|
|
29
47
|
default:
|
|
30
|
-
formatted =
|
|
48
|
+
formatted = code
|
|
31
49
|
.replace(/\t/g, " ")
|
|
32
50
|
.replace(/[ \t]+$/gm, "")
|
|
33
51
|
.replace(/\n{3,}/g, "\n\n");
|
|
@@ -36,7 +54,7 @@ export function express(input: CodeFormatInput): CodeFormatOutput {
|
|
|
36
54
|
|
|
37
55
|
return {
|
|
38
56
|
formatted,
|
|
39
|
-
changed: formatted !==
|
|
40
|
-
language
|
|
57
|
+
changed: formatted !== code,
|
|
58
|
+
language,
|
|
41
59
|
};
|
|
42
60
|
}
|
|
@@ -4,17 +4,39 @@
|
|
|
4
4
|
"inputSchema": {
|
|
5
5
|
"type": "object",
|
|
6
6
|
"properties": {
|
|
7
|
-
"code": {
|
|
8
|
-
|
|
7
|
+
"code": {
|
|
8
|
+
"type": "string",
|
|
9
|
+
"description": "Source code to format"
|
|
10
|
+
},
|
|
11
|
+
"language": {
|
|
12
|
+
"type": "string",
|
|
13
|
+
"description": "Programming language",
|
|
14
|
+
"enum": [
|
|
15
|
+
"typescript",
|
|
16
|
+
"javascript",
|
|
17
|
+
"json",
|
|
18
|
+
"markdown",
|
|
19
|
+
"rust"
|
|
20
|
+
]
|
|
21
|
+
}
|
|
9
22
|
},
|
|
10
|
-
"required": [
|
|
23
|
+
"required": [
|
|
24
|
+
"code",
|
|
25
|
+
"language"
|
|
26
|
+
]
|
|
11
27
|
},
|
|
12
28
|
"outputSchema": {
|
|
13
29
|
"type": "object",
|
|
14
30
|
"properties": {
|
|
15
|
-
"formatted": {
|
|
16
|
-
|
|
17
|
-
|
|
31
|
+
"formatted": {
|
|
32
|
+
"type": "string"
|
|
33
|
+
},
|
|
34
|
+
"changed": {
|
|
35
|
+
"type": "boolean"
|
|
36
|
+
},
|
|
37
|
+
"language": {
|
|
38
|
+
"type": "string"
|
|
39
|
+
}
|
|
18
40
|
}
|
|
19
41
|
},
|
|
20
42
|
"dependencies": [],
|
|
@@ -27,5 +49,12 @@
|
|
|
27
49
|
"concurrencyModel": "Stateless",
|
|
28
50
|
"failureSemantics": "FAIL_FAST"
|
|
29
51
|
},
|
|
30
|
-
"tags": [
|
|
52
|
+
"tags": [
|
|
53
|
+
"formatter",
|
|
54
|
+
"prettier",
|
|
55
|
+
"typescript",
|
|
56
|
+
"rust",
|
|
57
|
+
"genesis"
|
|
58
|
+
],
|
|
59
|
+
"irHash": "1250405b99303f13211fde6df73f75b19ea933b3b800b76711008eafa8933a9b"
|
|
31
60
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"testCases": [
|
|
3
|
+
{
|
|
4
|
+
"input": { "code": "{\"b\":2,\"a\":1}", "language": "json" },
|
|
5
|
+
"expectedOutput": {
|
|
6
|
+
"formatted": "{\n \"b\": 2,\n \"a\": 1\n}",
|
|
7
|
+
"changed": true,
|
|
8
|
+
"language": "json"
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"input": { "code": 42, "language": "json" },
|
|
13
|
+
"expectedOutput": { "formatted": "", "changed": false, "language": "json" }
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"input": { "language": "json" },
|
|
17
|
+
"expectedOutput": { "formatted": "", "changed": false, "language": "json" }
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"input": {},
|
|
21
|
+
"expectedOutput": { "formatted": "", "changed": false, "language": "unknown" }
|
|
22
|
+
}
|
|
23
|
+
],
|
|
24
|
+
"config": { "fuzzIterations": 10, "seed": 1 }
|
|
25
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"cloud_id": "
|
|
3
|
-
"owner": "
|
|
4
|
-
"version": "0.
|
|
5
|
-
"published_at": "2026-
|
|
2
|
+
"cloud_id": "948ba7e7-3f7b-42c0-9b25-ec1abf6cefae",
|
|
3
|
+
"owner": "xiaoba-dev",
|
|
4
|
+
"version": "0.2.0",
|
|
5
|
+
"published_at": "2026-09-08T15:37:52.362Z"
|
|
6
6
|
}
|
|
@@ -18,11 +18,30 @@ interface FileReadOutput {
|
|
|
18
18
|
* Restricted by L0 sandbox constraints (path allowlist).
|
|
19
19
|
*/
|
|
20
20
|
export function express(input: FileReadInput): FileReadOutput {
|
|
21
|
-
|
|
21
|
+
// The schema declares encoding as an enum, so a value outside it is not a
|
|
22
|
+
// third choice — fall back to the documented default rather than passing it
|
|
23
|
+
// to Buffer.toString(), which would throw on an unknown encoding.
|
|
24
|
+
const rawEncoding: unknown = input.encoding;
|
|
25
|
+
const encoding = rawEncoding === "base64" ? "base64" : "utf-8";
|
|
26
|
+
|
|
27
|
+
// readFileSync accepts a number as a *file descriptor*, so `{"path": 42}`
|
|
28
|
+
// did not fail as a bad path — it attempted to read whatever fd 42 happened
|
|
29
|
+
// to be, and reported EBADF only because that one was closed. A caller
|
|
30
|
+
// choosing the number gets to choose the descriptor, which is not a path
|
|
31
|
+
// read at all. The type check has to happen before the call, not inside the
|
|
32
|
+
// catch: by then the read has already been attempted.
|
|
33
|
+
const rawPath: unknown = input.path;
|
|
34
|
+
if (typeof rawPath !== "string" || rawPath === "") {
|
|
35
|
+
return {
|
|
36
|
+
content: "[error] INVALID_PATH: path must be a non-empty string",
|
|
37
|
+
size: 0,
|
|
38
|
+
encoding,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
22
41
|
|
|
23
42
|
let buffer: Buffer;
|
|
24
43
|
try {
|
|
25
|
-
buffer = readFileSync(
|
|
44
|
+
buffer = readFileSync(rawPath);
|
|
26
45
|
} catch (err: any) {
|
|
27
46
|
return {
|
|
28
47
|
content: `[error] ${err.code ?? "UNKNOWN"}: ${err.message}`,
|
|
@@ -4,28 +4,52 @@
|
|
|
4
4
|
"inputSchema": {
|
|
5
5
|
"type": "object",
|
|
6
6
|
"properties": {
|
|
7
|
-
"path": {
|
|
8
|
-
|
|
7
|
+
"path": {
|
|
8
|
+
"type": "string",
|
|
9
|
+
"description": "File path to read"
|
|
10
|
+
},
|
|
11
|
+
"encoding": {
|
|
12
|
+
"type": "string",
|
|
13
|
+
"default": "utf-8",
|
|
14
|
+
"enum": [
|
|
15
|
+
"utf-8",
|
|
16
|
+
"base64"
|
|
17
|
+
]
|
|
18
|
+
}
|
|
9
19
|
},
|
|
10
|
-
"required": [
|
|
20
|
+
"required": [
|
|
21
|
+
"path"
|
|
22
|
+
]
|
|
11
23
|
},
|
|
12
24
|
"outputSchema": {
|
|
13
25
|
"type": "object",
|
|
14
26
|
"properties": {
|
|
15
|
-
"content": {
|
|
16
|
-
|
|
17
|
-
|
|
27
|
+
"content": {
|
|
28
|
+
"type": "string"
|
|
29
|
+
},
|
|
30
|
+
"size": {
|
|
31
|
+
"type": "integer"
|
|
32
|
+
},
|
|
33
|
+
"encoding": {
|
|
34
|
+
"type": "string"
|
|
35
|
+
}
|
|
18
36
|
}
|
|
19
37
|
},
|
|
20
38
|
"dependencies": [],
|
|
21
39
|
"version": "0.2.0",
|
|
22
40
|
"author": "rotifer-genesis",
|
|
23
|
-
"fidelity": "
|
|
41
|
+
"fidelity": "Wrapped",
|
|
24
42
|
"transparency": "Open",
|
|
25
43
|
"semanticRequirements": {
|
|
26
44
|
"timeModel": "Sync",
|
|
27
45
|
"concurrencyModel": "Stateless",
|
|
28
46
|
"failureSemantics": "FAIL_FAST"
|
|
29
47
|
},
|
|
30
|
-
"tags": [
|
|
48
|
+
"tags": [
|
|
49
|
+
"file-io",
|
|
50
|
+
"encoding",
|
|
51
|
+
"base64",
|
|
52
|
+
"local-files",
|
|
53
|
+
"genesis"
|
|
54
|
+
]
|
|
31
55
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"testCases": [
|
|
3
|
+
{
|
|
4
|
+
"input": { "path": "package.json" },
|
|
5
|
+
"expectedSchema": {
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["content", "size", "encoding"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"content": { "type": "string", "pattern": "@rotifer/playground" },
|
|
10
|
+
"size": { "type": "integer", "minimum": 1 },
|
|
11
|
+
"encoding": { "const": "utf-8" }
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"input": { "path": 42 },
|
|
17
|
+
"expectedOutput": {
|
|
18
|
+
"content": "[error] INVALID_PATH: path must be a non-empty string",
|
|
19
|
+
"size": 0,
|
|
20
|
+
"encoding": "utf-8"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"input": { "encoding": "utf-8" },
|
|
25
|
+
"expectedOutput": {
|
|
26
|
+
"content": "[error] INVALID_PATH: path must be a non-empty string",
|
|
27
|
+
"size": 0,
|
|
28
|
+
"encoding": "utf-8"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"input": {},
|
|
33
|
+
"expectedOutput": {
|
|
34
|
+
"content": "[error] INVALID_PATH: path must be a non-empty string",
|
|
35
|
+
"size": 0,
|
|
36
|
+
"encoding": "utf-8"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
],
|
|
40
|
+
"config": { "fuzzIterations": 10, "seed": 1 }
|
|
41
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"cloud_id": "
|
|
3
|
-
"owner": "
|
|
4
|
-
"version": "0.
|
|
5
|
-
"published_at": "2026-
|
|
2
|
+
"cloud_id": "ebb624a7-56aa-47be-8d98-52130e8d81c1",
|
|
3
|
+
"owner": "xiaoba-dev",
|
|
4
|
+
"version": "0.2.0",
|
|
5
|
+
"published_at": "2026-09-08T15:37:15.392Z"
|
|
6
6
|
}
|
|
@@ -23,12 +23,31 @@ interface SearchOutput {
|
|
|
23
23
|
*/
|
|
24
24
|
export function express(input: SearchInput): SearchOutput {
|
|
25
25
|
const start = Date.now();
|
|
26
|
-
|
|
26
|
+
|
|
27
|
+
// `input.query` used to be interpolated straight into the result strings, so
|
|
28
|
+
// a missing field produced a simulated search for the literal "undefined" and
|
|
29
|
+
// a numeric one searched for "42". Neither is a search; both are the Gene
|
|
30
|
+
// answering confidently about a question nobody asked.
|
|
31
|
+
//
|
|
32
|
+
// Unlike a Gene with a documented default, there is nothing sensible to
|
|
33
|
+
// search for in place of a query, so this refuses: no query, no results. The
|
|
34
|
+
// shape still conforms to outputSchema, which is what "correct handling"
|
|
35
|
+
// means on this boundary (ADR-333).
|
|
36
|
+
const query: unknown = input.query;
|
|
37
|
+
if (typeof query !== "string" || query.trim() === "") {
|
|
38
|
+
return { results: [], totalResults: 0, searchTime: Date.now() - start };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const rawMax: unknown = input.maxResults;
|
|
42
|
+
const max =
|
|
43
|
+
typeof rawMax === "number" && Number.isInteger(rawMax) && rawMax > 0
|
|
44
|
+
? Math.min(rawMax, 20)
|
|
45
|
+
: 5;
|
|
27
46
|
|
|
28
47
|
const results: SearchResult[] = Array.from({ length: max }, (_, i) => ({
|
|
29
|
-
title: `Result ${i + 1} for "${
|
|
30
|
-
url: `https://example.com/search?q=${encodeURIComponent(
|
|
31
|
-
snippet: `This is a simulated search result #${i + 1} for the query "${
|
|
48
|
+
title: `Result ${i + 1} for "${query}"`,
|
|
49
|
+
url: `https://example.com/search?q=${encodeURIComponent(query)}&p=${i + 1}`,
|
|
50
|
+
snippet: `This is a simulated search result #${i + 1} for the query "${query}". In production, this gene binds to a real search provider.`,
|
|
32
51
|
}));
|
|
33
52
|
|
|
34
53
|
return {
|
|
@@ -4,10 +4,20 @@
|
|
|
4
4
|
"inputSchema": {
|
|
5
5
|
"type": "object",
|
|
6
6
|
"properties": {
|
|
7
|
-
"query": {
|
|
8
|
-
|
|
7
|
+
"query": {
|
|
8
|
+
"type": "string",
|
|
9
|
+
"description": "Search query string"
|
|
10
|
+
},
|
|
11
|
+
"maxResults": {
|
|
12
|
+
"type": "integer",
|
|
13
|
+
"default": 5,
|
|
14
|
+
"minimum": 1,
|
|
15
|
+
"maximum": 20
|
|
16
|
+
}
|
|
9
17
|
},
|
|
10
|
-
"required": [
|
|
18
|
+
"required": [
|
|
19
|
+
"query"
|
|
20
|
+
]
|
|
11
21
|
},
|
|
12
22
|
"outputSchema": {
|
|
13
23
|
"type": "object",
|
|
@@ -17,14 +27,24 @@
|
|
|
17
27
|
"items": {
|
|
18
28
|
"type": "object",
|
|
19
29
|
"properties": {
|
|
20
|
-
"title": {
|
|
21
|
-
|
|
22
|
-
|
|
30
|
+
"title": {
|
|
31
|
+
"type": "string"
|
|
32
|
+
},
|
|
33
|
+
"url": {
|
|
34
|
+
"type": "string"
|
|
35
|
+
},
|
|
36
|
+
"snippet": {
|
|
37
|
+
"type": "string"
|
|
38
|
+
}
|
|
23
39
|
}
|
|
24
40
|
}
|
|
25
41
|
},
|
|
26
|
-
"totalResults": {
|
|
27
|
-
|
|
42
|
+
"totalResults": {
|
|
43
|
+
"type": "integer"
|
|
44
|
+
},
|
|
45
|
+
"searchTime": {
|
|
46
|
+
"type": "number"
|
|
47
|
+
}
|
|
28
48
|
}
|
|
29
49
|
},
|
|
30
50
|
"dependencies": [],
|
|
@@ -38,5 +58,12 @@
|
|
|
38
58
|
"concurrencyModel": "Stateless",
|
|
39
59
|
"failureSemantics": "Retry"
|
|
40
60
|
},
|
|
41
|
-
"tags": [
|
|
61
|
+
"tags": [
|
|
62
|
+
"web-search",
|
|
63
|
+
"results",
|
|
64
|
+
"snippets",
|
|
65
|
+
"async",
|
|
66
|
+
"retrieval"
|
|
67
|
+
],
|
|
68
|
+
"irHash": "9af9ccc90df36c586661876203b87f182c9fac65ebcfdec22c1e0d683be19b49"
|
|
42
69
|
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
{
|
|
2
|
+
"testCases": [
|
|
3
|
+
{
|
|
4
|
+
"input": {
|
|
5
|
+
"query": "rotifer protocol",
|
|
6
|
+
"maxResults": 2
|
|
7
|
+
},
|
|
8
|
+
"expectedSchema": {
|
|
9
|
+
"type": "object",
|
|
10
|
+
"required": [
|
|
11
|
+
"results",
|
|
12
|
+
"totalResults"
|
|
13
|
+
],
|
|
14
|
+
"properties": {
|
|
15
|
+
"totalResults": {
|
|
16
|
+
"const": 2
|
|
17
|
+
},
|
|
18
|
+
"results": {
|
|
19
|
+
"type": "array",
|
|
20
|
+
"minItems": 2,
|
|
21
|
+
"items": {
|
|
22
|
+
"properties": {
|
|
23
|
+
"title": {
|
|
24
|
+
"pattern": "rotifer protocol"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"input": {
|
|
34
|
+
"maxResults": 3
|
|
35
|
+
},
|
|
36
|
+
"expectedSchema": {
|
|
37
|
+
"type": "object",
|
|
38
|
+
"required": [
|
|
39
|
+
"results",
|
|
40
|
+
"totalResults"
|
|
41
|
+
],
|
|
42
|
+
"properties": {
|
|
43
|
+
"totalResults": {
|
|
44
|
+
"const": 0
|
|
45
|
+
},
|
|
46
|
+
"results": {
|
|
47
|
+
"type": "array",
|
|
48
|
+
"maxItems": 0
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"input": {
|
|
55
|
+
"query": 42
|
|
56
|
+
},
|
|
57
|
+
"expectedSchema": {
|
|
58
|
+
"type": "object",
|
|
59
|
+
"properties": {
|
|
60
|
+
"totalResults": {
|
|
61
|
+
"const": 0
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"input": {},
|
|
68
|
+
"expectedSchema": {
|
|
69
|
+
"type": "object",
|
|
70
|
+
"properties": {
|
|
71
|
+
"totalResults": {
|
|
72
|
+
"const": 0
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
],
|
|
78
|
+
"config": {
|
|
79
|
+
"fuzzIterations": 10,
|
|
80
|
+
"seed": 1
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"cloud_id": "
|
|
3
|
-
"owner": "
|
|
4
|
-
"version": "0.
|
|
5
|
-
"published_at": "2026-
|
|
2
|
+
"cloud_id": "9f3410dc-4edb-492e-bdd6-fb103fd106e1",
|
|
3
|
+
"owner": "xiaoba-dev",
|
|
4
|
+
"version": "0.2.0",
|
|
5
|
+
"published_at": "2026-09-08T15:37:48.867Z"
|
|
6
6
|
}
|
|
@@ -15,8 +15,20 @@ interface LiteSearchOutput {
|
|
|
15
15
|
* with deadline constraints.
|
|
16
16
|
*/
|
|
17
17
|
export function express(input: LiteSearchInput): LiteSearchOutput {
|
|
18
|
+
// Same defect as genesis-web-search: a missing query became a confident
|
|
19
|
+
// answer about "undefined". With no default query to fall back on, saying so
|
|
20
|
+
// is the only honest response — and it stays inside outputSchema, which is
|
|
21
|
+
// what refusal has to look like on this boundary (ADR-333).
|
|
22
|
+
const query: unknown = input.query;
|
|
23
|
+
if (typeof query !== "string" || query.trim() === "") {
|
|
24
|
+
return {
|
|
25
|
+
answer: "No query provided.",
|
|
26
|
+
source: "",
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
18
30
|
return {
|
|
19
|
-
answer: `[Simulated] Quick answer for "${
|
|
20
|
-
source: "https://example.com/instant?q=" + encodeURIComponent(
|
|
31
|
+
answer: `[Simulated] Quick answer for "${query}": This is a placeholder response from genesis-web-search-lite. In production, this returns a concise answer from a search provider.`,
|
|
32
|
+
source: "https://example.com/instant?q=" + encodeURIComponent(query),
|
|
21
33
|
};
|
|
22
34
|
}
|
|
@@ -4,15 +4,24 @@
|
|
|
4
4
|
"inputSchema": {
|
|
5
5
|
"type": "object",
|
|
6
6
|
"properties": {
|
|
7
|
-
"query": {
|
|
7
|
+
"query": {
|
|
8
|
+
"type": "string",
|
|
9
|
+
"description": "Search query string"
|
|
10
|
+
}
|
|
8
11
|
},
|
|
9
|
-
"required": [
|
|
12
|
+
"required": [
|
|
13
|
+
"query"
|
|
14
|
+
]
|
|
10
15
|
},
|
|
11
16
|
"outputSchema": {
|
|
12
17
|
"type": "object",
|
|
13
18
|
"properties": {
|
|
14
|
-
"answer": {
|
|
15
|
-
|
|
19
|
+
"answer": {
|
|
20
|
+
"type": "string"
|
|
21
|
+
},
|
|
22
|
+
"source": {
|
|
23
|
+
"type": "string"
|
|
24
|
+
}
|
|
16
25
|
}
|
|
17
26
|
},
|
|
18
27
|
"dependencies": [],
|
|
@@ -25,5 +34,12 @@
|
|
|
25
34
|
"concurrencyModel": "Stateless",
|
|
26
35
|
"failureSemantics": "SILENT_DEGRADE"
|
|
27
36
|
},
|
|
28
|
-
"tags": [
|
|
37
|
+
"tags": [
|
|
38
|
+
"web-search",
|
|
39
|
+
"single-answer",
|
|
40
|
+
"attribution",
|
|
41
|
+
"lightweight",
|
|
42
|
+
"async"
|
|
43
|
+
],
|
|
44
|
+
"irHash": "e1bae37cdb28cdce1c4a3631bcb9e58eb8009a01d3d8f4c6b7f1555d38fd040d"
|
|
29
45
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"testCases": [
|
|
3
|
+
{
|
|
4
|
+
"input": { "query": "rotifer protocol" },
|
|
5
|
+
"expectedSchema": {
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["answer", "source"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"answer": { "type": "string", "pattern": "rotifer protocol" },
|
|
10
|
+
"source": { "type": "string", "minLength": 1 }
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"input": { "query": 42 },
|
|
16
|
+
"expectedOutput": { "answer": "No query provided.", "source": "" }
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"input": {},
|
|
20
|
+
"expectedOutput": { "answer": "No query provided.", "source": "" }
|
|
21
|
+
}
|
|
22
|
+
],
|
|
23
|
+
"config": { "fuzzIterations": 10, "seed": 1 }
|
|
24
|
+
}
|