@jini-ai/agentic 0.3.7 → 0.4.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/CHANGELOG.md +94 -0
- package/README.md +33 -6
- package/dist/core/a2ui/agent-to-renderer.d.ts +3 -1
- package/dist/core/a2ui/agent-to-renderer.js +1 -1
- package/dist/core/a2ui/catalog.d.ts +14 -5
- package/dist/core/a2ui/catalog.js +5 -5
- package/dist/core/a2ui/common-types.d.ts +11 -3
- package/dist/core/a2ui/common-types.js +5 -3
- package/dist/core/a2ui/index.d.ts +1 -1
- package/dist/core/a2ui/index.js +1 -1
- package/dist/core/a2ui/interpreter.d.ts +45 -10
- package/dist/core/a2ui/interpreter.js +38 -43
- package/dist/core/a2ui/json-pointer.d.ts +16 -4
- package/dist/core/a2ui/json-pointer.js +7 -7
- package/dist/core/a2ui/renderer-to-agent.d.ts +22 -8
- package/dist/core/a2ui/renderer-to-agent.js +5 -5
- package/dist/core/a2ui/resolve.d.ts +7 -2
- package/dist/core/a2ui/resolve.js +10 -9
- package/dist/core/a2ui/tree.d.ts +8 -2
- package/dist/core/a2ui/tree.js +2 -2
- package/dist/core/ag-ui.d.ts +17 -9
- package/dist/core/ag-ui.js +4 -4
- package/dist/core/capability.d.ts +12 -3
- package/dist/core/capability.js +3 -3
- package/dist/core/dom/dom-page-driver.d.ts +8 -4
- package/dist/core/dom/dom-page-driver.js +19 -20
- package/dist/core/dom/index.d.ts +3 -3
- package/dist/core/dom/index.js +1 -1
- package/dist/core/dom/model-context.d.ts +18 -8
- package/dist/core/dom/model-context.js +12 -15
- package/dist/core/dom/native-model-context.d.ts +6 -0
- package/dist/core/dom/native-model-context.js +19 -0
- package/dist/core/element-handles.d.ts +6 -2
- package/dist/core/element-handles.js +3 -3
- package/dist/core/gen-ui/encoder.d.ts +21 -11
- package/dist/core/gen-ui/encoder.js +8 -7
- package/dist/core/gen-ui/events.d.ts +6 -5
- package/dist/core/gen-ui/events.js +6 -5
- package/dist/core/gen-ui/index.d.ts +1 -1
- package/dist/core/gen-ui/index.js +1 -1
- package/dist/core/guards.d.ts +17 -5
- package/dist/core/guards.js +6 -6
- package/dist/core/handle.d.ts +12 -5
- package/dist/core/handle.js +9 -8
- package/dist/core/index.d.ts +2 -1
- package/dist/core/index.js +2 -1
- package/dist/core/list-handles.d.ts +4 -1
- package/dist/core/list-handles.js +1 -1
- package/dist/core/mcp-ui-apps.d.ts +39 -9
- package/dist/core/mcp-ui-apps.js +10 -8
- package/dist/core/messages.d.ts +6 -0
- package/dist/core/messages.js +6 -0
- package/dist/core/page-capabilities.d.ts +0 -11
- package/dist/core/page-capabilities.js +13 -1
- package/dist/core/page-driver.d.ts +32 -11
- package/dist/core/page-executor.d.ts +8 -2
- package/dist/core/page-executor.js +31 -31
- package/dist/core/webmcp.d.ts +27 -8
- package/dist/core/webmcp.js +14 -14
- package/dist/index.d.ts +30 -0
- package/dist/index.js +30 -0
- package/dist/skills/install/archive.d.ts +8 -0
- package/dist/skills/install/archive.js +47 -0
- package/dist/skills/install/fetch-adapter.d.ts +6 -0
- package/dist/skills/install/fetch-adapter.js +6 -0
- package/dist/skills/install/github.d.ts +21 -0
- package/dist/skills/install/github.js +92 -0
- package/dist/skills/install/index.d.ts +11 -0
- package/dist/skills/install/index.js +11 -0
- package/dist/skills/install/install-service.d.ts +44 -0
- package/dist/skills/install/install-service.js +121 -0
- package/dist/skills/install/layout.d.ts +13 -0
- package/dist/skills/install/layout.js +22 -0
- package/dist/skills/install/live-registration.d.ts +61 -0
- package/dist/skills/install/live-registration.js +64 -0
- package/dist/skills/install/node-filesystem.d.ts +4 -0
- package/dist/skills/install/node-filesystem.js +41 -0
- package/dist/skills/install/ports.d.ts +101 -0
- package/dist/skills/install/ports.js +2 -0
- package/dist/skills/install/state.d.ts +13 -0
- package/dist/skills/install/state.js +23 -0
- package/dist/skills/install/validation.d.ts +45 -0
- package/dist/skills/install/validation.js +104 -0
- package/package.json +20 -5
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/** Bounded skill-bundle validation; scripts are stored as data and never executed. */
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
export class SkillInputError extends Error {
|
|
4
|
+
constructor({ message }, optional = {}) {
|
|
5
|
+
super(message, optional);
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
export const MAX_SKILL_BYTES = 8 * 1024 * 1024;
|
|
9
|
+
export const MAX_SKILL_FILES = 256;
|
|
10
|
+
export const MAX_SKILL_FILE_BYTES = 1024 * 1024;
|
|
11
|
+
const TEXT_EXTENSIONS = new Set([".md", ".txt", ".json", ".yaml", ".yml", ".csv", ".svg", ".sh", ".py", ".js", ".ts", ".mjs"]);
|
|
12
|
+
const ASSET_EXTENSIONS = new Set([".png", ".jpg", ".jpeg", ".webp"]);
|
|
13
|
+
/** Decodes bounded canonical base64; malformed encodings never silently turn into empty files. */
|
|
14
|
+
export function decodeSkillBase64({ value }, { maxBytes = MAX_SKILL_BYTES } = {}) {
|
|
15
|
+
if (typeof value !== "string" || value.length > Math.ceil(maxBytes / 3) * 4)
|
|
16
|
+
throw new SkillInputError({ message: "Skill upload exceeds its size limit." });
|
|
17
|
+
if (value.length % 4 !== 0 || !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(value))
|
|
18
|
+
throw new SkillInputError({ message: "Skill upload must use valid base64." });
|
|
19
|
+
const bytes = Buffer.from(value, "base64");
|
|
20
|
+
if (bytes.length > maxBytes)
|
|
21
|
+
throw new SkillInputError({ message: "Skill upload exceeds its size limit." });
|
|
22
|
+
return bytes;
|
|
23
|
+
}
|
|
24
|
+
export function validateSkillPath({ filePath }, _optional = {}) {
|
|
25
|
+
if (typeof filePath !== "string" || filePath.length === 0 || filePath.length > 240 || /[\\\x00-\x1f\x7f:]/.test(filePath) || path.posix.isAbsolute(filePath) || filePath.split("/").some(p => !p || p === "." || p === ".." || p.startsWith(".")))
|
|
26
|
+
throw new SkillInputError({ message: `Unsafe skill file path: ${filePath}` });
|
|
27
|
+
}
|
|
28
|
+
/** Strict frontmatter for new installs; the legacy disk loader remains backwards compatible. */
|
|
29
|
+
export function validateSkillMarkdown({ markdown, yamlReader }, _optional = {}) {
|
|
30
|
+
const frontmatter = markdown.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/);
|
|
31
|
+
if (!frontmatter)
|
|
32
|
+
throw new SkillInputError({ message: "SKILL.md must have YAML frontmatter with a name and description." });
|
|
33
|
+
let fields;
|
|
34
|
+
try {
|
|
35
|
+
fields = yamlReader.read({ yaml: frontmatter[1] });
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
throw new SkillInputError({ message: "SKILL.md must have valid YAML frontmatter." });
|
|
39
|
+
}
|
|
40
|
+
const { name, description } = (fields && typeof fields === "object" ? fields : {});
|
|
41
|
+
if (typeof name !== "string" || typeof description !== "string" || !description.trim())
|
|
42
|
+
throw new SkillInputError({ message: "SKILL.md must have YAML frontmatter with a name and description." });
|
|
43
|
+
if (name.length > 64 || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name))
|
|
44
|
+
throw new SkillInputError({ message: "Skill name must use lowercase letters, digits and single hyphens (1–64 characters)." });
|
|
45
|
+
if (description.length > 1024)
|
|
46
|
+
throw new SkillInputError({ message: "Skill description exceeds 1024 characters." });
|
|
47
|
+
return { name, description: description.replace(/\s+/g, " ").trim() };
|
|
48
|
+
}
|
|
49
|
+
/** Validates the whole package before writes, including cross-file collisions and combined limits.
|
|
50
|
+
* @complexity O(total bytes + files × path length), bounded to 8 MiB / 256 files.
|
|
51
|
+
*/
|
|
52
|
+
export function validateSkillFiles({ files: upload, yamlReader }, _optional = {}) {
|
|
53
|
+
if (!Array.isArray(upload) || upload.length === 0 || upload.length > MAX_SKILL_FILES)
|
|
54
|
+
throw new SkillInputError({ message: "A skill must contain 1–256 files." });
|
|
55
|
+
for (const file of upload)
|
|
56
|
+
validateSkillPath({ filePath: file.path });
|
|
57
|
+
const skillFiles = upload.filter(f => f.path === "SKILL.md" || f.path.endsWith("/SKILL.md"));
|
|
58
|
+
if (skillFiles.length !== 1) {
|
|
59
|
+
if (skillFiles.length > 1 && new Set(skillFiles.map(f => f.path)).size === 1)
|
|
60
|
+
throw new SkillInputError({ message: `Duplicate skill file path: ${skillFiles[0].path}` });
|
|
61
|
+
throw new SkillInputError({ message: "Choose one skill folder containing a single SKILL.md." });
|
|
62
|
+
}
|
|
63
|
+
const prefix = skillFiles[0].path.slice(0, -"SKILL.md".length);
|
|
64
|
+
const files = new Map();
|
|
65
|
+
let total = 0;
|
|
66
|
+
for (const file of upload) {
|
|
67
|
+
if (!file.path.startsWith(prefix))
|
|
68
|
+
throw new SkillInputError({ message: "All files must belong to the selected skill folder." });
|
|
69
|
+
const relative = file.path.slice(prefix.length);
|
|
70
|
+
validateSkillPath({ filePath: relative });
|
|
71
|
+
if (files.has(relative))
|
|
72
|
+
throw new SkillInputError({ message: `Duplicate skill file path: ${relative}` });
|
|
73
|
+
const extension = path.posix.extname(relative).toLowerCase();
|
|
74
|
+
const top = relative.split("/")[0];
|
|
75
|
+
if (relative !== "SKILL.md" && relative !== "README.md" && relative !== "LICENSE" && !["references", "scripts", "assets"].includes(top))
|
|
76
|
+
throw new SkillInputError({ message: `Unsupported skill file location: ${relative}` });
|
|
77
|
+
if (relative !== "LICENSE" && !TEXT_EXTENSIONS.has(extension) && !(top === "assets" && ASSET_EXTENSIONS.has(extension)))
|
|
78
|
+
throw new SkillInputError({ message: `Unsupported skill file type: ${relative}` });
|
|
79
|
+
const bytes = decodeSkillBase64({ value: file.contentBase64 }, { maxBytes: MAX_SKILL_FILE_BYTES });
|
|
80
|
+
if (relative === "SKILL.md" && bytes.length > 128 * 1024)
|
|
81
|
+
throw new SkillInputError({ message: "SKILL.md exceeds 128 KiB." });
|
|
82
|
+
total += bytes.length;
|
|
83
|
+
if (total > MAX_SKILL_BYTES)
|
|
84
|
+
throw new SkillInputError({ message: "Skill files exceed 8 MiB combined." });
|
|
85
|
+
if (TEXT_EXTENSIONS.has(extension) || relative === "LICENSE") {
|
|
86
|
+
try {
|
|
87
|
+
new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
throw new SkillInputError({ message: `Skill text file must be valid UTF-8 without null bytes: ${relative}` });
|
|
91
|
+
}
|
|
92
|
+
if (bytes.includes(0))
|
|
93
|
+
throw new SkillInputError({ message: `Skill text file must be valid UTF-8 without null bytes: ${relative}` });
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
const valid = extension === ".png" ? bytes.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10])) : extension === ".webp" ? bytes.toString("ascii", 0, 4) === "RIFF" && bytes.toString("ascii", 8, 12) === "WEBP" : bytes[0] === 255 && bytes[1] === 216 && bytes[2] === 255;
|
|
97
|
+
if (!valid)
|
|
98
|
+
throw new SkillInputError({ message: `Skill asset content does not match its file type: ${relative}` });
|
|
99
|
+
}
|
|
100
|
+
files.set(relative, bytes);
|
|
101
|
+
}
|
|
102
|
+
return { ...validateSkillMarkdown({ markdown: files.get("SKILL.md").toString("utf8"), yamlReader }), files };
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=validation.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jini-ai/agentic",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Framework-free agent-control layer: page-capability vocabulary and manifests, the data-agent-* markup convention, AG-UI projection, generative-UI streams, and A2UI.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -16,7 +16,9 @@
|
|
|
16
16
|
".": "universal",
|
|
17
17
|
"./core": "universal",
|
|
18
18
|
"./dom": "browser",
|
|
19
|
-
"./a2ui": "universal"
|
|
19
|
+
"./a2ui": "universal",
|
|
20
|
+
"./skills/install": "node",
|
|
21
|
+
"./skills/install/node": "node"
|
|
20
22
|
}
|
|
21
23
|
},
|
|
22
24
|
"type": "module",
|
|
@@ -43,16 +45,28 @@
|
|
|
43
45
|
"types": "./dist/core/a2ui/index.d.ts",
|
|
44
46
|
"import": "./dist/core/a2ui/index.js",
|
|
45
47
|
"default": "./dist/core/a2ui/index.js"
|
|
48
|
+
},
|
|
49
|
+
"./skills/install": {
|
|
50
|
+
"types": "./dist/skills/install/index.d.ts",
|
|
51
|
+
"import": "./dist/skills/install/index.js",
|
|
52
|
+
"default": "./dist/skills/install/index.js"
|
|
53
|
+
},
|
|
54
|
+
"./skills/install/node": {
|
|
55
|
+
"types": "./dist/skills/install/node-filesystem.d.ts",
|
|
56
|
+
"import": "./dist/skills/install/node-filesystem.js",
|
|
57
|
+
"default": "./dist/skills/install/node-filesystem.js"
|
|
46
58
|
}
|
|
47
59
|
},
|
|
48
60
|
"files": [
|
|
49
61
|
"dist",
|
|
62
|
+
"README.md",
|
|
63
|
+
"CHANGELOG.md",
|
|
64
|
+
"LICENSE",
|
|
50
65
|
"!dist/**/*.test.js",
|
|
51
66
|
"!dist/**/*.test.d.ts",
|
|
52
67
|
"!dist/**/*.test.js.map",
|
|
53
68
|
"!dist/**/*.test.d.ts.map",
|
|
54
|
-
"!dist/**/__tests__/**"
|
|
55
|
-
"!dist/**/*.map"
|
|
69
|
+
"!dist/**/__tests__/**"
|
|
56
70
|
],
|
|
57
71
|
"publishConfig": {
|
|
58
72
|
"access": "public",
|
|
@@ -60,7 +74,8 @@
|
|
|
60
74
|
},
|
|
61
75
|
"dependencies": {
|
|
62
76
|
"zod": "^3.25.76",
|
|
63
|
-
"@jini-ai/
|
|
77
|
+
"@jini-ai/core": "^0.4.0",
|
|
78
|
+
"@jini-ai/protocol": "0.4.0"
|
|
64
79
|
},
|
|
65
80
|
"devDependencies": {
|
|
66
81
|
"@vitest/coverage-v8": "^2.1.9",
|