@harness-kernel/skills 0.0.0 → 0.5.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/README.md +73 -1
- package/dist/chunk-6XTSIVQV.js +219 -0
- package/dist/chunk-6XTSIVQV.js.map +1 -0
- package/dist/chunk-BF5PYWY6.js +55 -0
- package/dist/chunk-BF5PYWY6.js.map +1 -0
- package/dist/chunk-FUZYYJJU.js +57 -0
- package/dist/chunk-FUZYYJJU.js.map +1 -0
- package/dist/chunk-L3KCYHUW.js +24 -0
- package/dist/chunk-L3KCYHUW.js.map +1 -0
- package/dist/chunk-SIAWNALF.js +196 -0
- package/dist/chunk-SIAWNALF.js.map +1 -0
- package/dist/chunk-TC7PNGST.js +41 -0
- package/dist/chunk-TC7PNGST.js.map +1 -0
- package/dist/chunk-THKBSLCD.js +84 -0
- package/dist/chunk-THKBSLCD.js.map +1 -0
- package/dist/events.d.ts +70 -0
- package/dist/events.js +15 -0
- package/dist/events.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.js +94 -0
- package/dist/index.js.map +1 -0
- package/dist/logs.d.ts +46 -0
- package/dist/logs.js +13 -0
- package/dist/logs.js.map +1 -0
- package/dist/provider.d.ts +20 -0
- package/dist/provider.js +13 -0
- package/dist/provider.js.map +1 -0
- package/dist/registry.d.ts +26 -0
- package/dist/registry.js +9 -0
- package/dist/registry.js.map +1 -0
- package/dist/skill.d.ts +22 -0
- package/dist/skill.js +7 -0
- package/dist/skill.js.map +1 -0
- package/dist/state.d.ts +55 -0
- package/dist/state.js +26 -0
- package/dist/state.js.map +1 -0
- package/dist/tools.d.ts +70 -0
- package/dist/tools.js +23 -0
- package/dist/tools.js.map +1 -0
- package/package.json +48 -10
- package/index.d.ts +0 -1
- package/index.js +0 -1
package/README.md
CHANGED
|
@@ -1,3 +1,75 @@
|
|
|
1
1
|
# @harness-kernel/skills
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Package-only skill helpers for Harness Kernel agents.
|
|
4
|
+
|
|
5
|
+
A skill is a procedural capability, not an executable tool by itself. It declares when it should be used, adds prompt instructions when active, owns a set of gated tools, and records auditable events/logs for activation, deactivation, and blocked tool calls.
|
|
6
|
+
|
|
7
|
+
Skills do not own context providers. Attach the single `skills.provider` returned by `createSkillKit()` to `mode.providers`; keep other context providers as normal mode providers.
|
|
8
|
+
|
|
9
|
+
The package uses a soft gate outside `@harness-kernel/core`: skill tools are visible in the mode catalog, but the wrapper only delegates to the original tool after the skill is active.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pnpm add @harness-kernel/skills
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Basic Usage
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { defineAgent } from "@harness-kernel/core/agent";
|
|
21
|
+
import { HarnessMode } from "@harness-kernel/core/agent/mode";
|
|
22
|
+
import { createSkillKit, defineSkill } from "@harness-kernel/skills";
|
|
23
|
+
|
|
24
|
+
const githubSkill = defineSkill({
|
|
25
|
+
key: "github-pr-review",
|
|
26
|
+
description: "Review GitHub pull requests and address review comments.",
|
|
27
|
+
prompt: "Inspect unresolved comments before proposing code changes.",
|
|
28
|
+
tools: [readPullRequestTool, listReviewCommentsTool],
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const skills = createSkillKit([githubSkill]);
|
|
32
|
+
|
|
33
|
+
class DevMode extends HarnessMode {
|
|
34
|
+
prompt = "You are a coding agent.";
|
|
35
|
+
providers = [skills.provider];
|
|
36
|
+
tools = [...skills.tools];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const devMode = new DevMode();
|
|
40
|
+
|
|
41
|
+
export const agent = defineAgent({
|
|
42
|
+
label: "Dev Agent",
|
|
43
|
+
initialMode: devMode,
|
|
44
|
+
modes: [devMode],
|
|
45
|
+
declaredEvents: skills.events,
|
|
46
|
+
});
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Runtime Flow
|
|
50
|
+
|
|
51
|
+
1. The prompt provider lists available skills and active skills.
|
|
52
|
+
2. The model calls `activate_skill({ key, reason })`.
|
|
53
|
+
3. The package writes `state.skills.active[key]`.
|
|
54
|
+
4. The next context build includes the active skill prompt.
|
|
55
|
+
5. Gated tools from that skill delegate to the original tools.
|
|
56
|
+
|
|
57
|
+
If a gated tool is called too early, it returns structured data with `code: "skill.required"` and emits `SkillRequiredEvent`. It does not mark the run as a technical tool error.
|
|
58
|
+
|
|
59
|
+
## API
|
|
60
|
+
|
|
61
|
+
- `defineSkill(input)` normalizes a skill declaration.
|
|
62
|
+
- `createSkillRegistry(skills)` validates duplicate skill keys and duplicate skill tool names.
|
|
63
|
+
- `listAvailableSkills()`, `listActiveSkills()`, `listInactiveSkills()`, and `isSkillActive()` inspect registry/state.
|
|
64
|
+
- `activateSkill()` and `deactivateSkill()` mutate session state and emit events/logs.
|
|
65
|
+
- `createSkillPromptProvider()` injects active skill instructions.
|
|
66
|
+
- `createSkillActivationTool()`, `createSkillDeactivationTool()`, and `createSkillListTool()` expose model-facing controls.
|
|
67
|
+
- `createSkillGatedTools()` wraps skill tools with the soft gate.
|
|
68
|
+
- `createSkillKit()` returns `{ registry, provider, tools, events }`.
|
|
69
|
+
- `skillEvents()` returns the custom event classes for `declaredEvents`.
|
|
70
|
+
|
|
71
|
+
Use `createSkillGatedTools(registry, { skillKeys: ["docs-research"] })` when a mode should expose only a subset of skill tools.
|
|
72
|
+
|
|
73
|
+
The default state key is `skills`. Pass `{ stateKey: "mySkills" }` to helpers, tools, provider, or `createSkillKit()` to use another key.
|
|
74
|
+
|
|
75
|
+
Skills do not grant authority. Tool approval, risk, permissions, sandbox policy, and host approval callbacks still belong to the core runtime and host application.
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import {
|
|
2
|
+
activateSkill,
|
|
3
|
+
deactivateSkill,
|
|
4
|
+
isSkillActive,
|
|
5
|
+
listActiveSkills,
|
|
6
|
+
listAvailableSkills,
|
|
7
|
+
listInactiveSkills
|
|
8
|
+
} from "./chunk-SIAWNALF.js";
|
|
9
|
+
import {
|
|
10
|
+
SkillRequiredEvent
|
|
11
|
+
} from "./chunk-FUZYYJJU.js";
|
|
12
|
+
import {
|
|
13
|
+
SkillRequiredLog
|
|
14
|
+
} from "./chunk-TC7PNGST.js";
|
|
15
|
+
import {
|
|
16
|
+
toSkillRegistry
|
|
17
|
+
} from "./chunk-BF5PYWY6.js";
|
|
18
|
+
|
|
19
|
+
// src/tools.ts
|
|
20
|
+
import {
|
|
21
|
+
HarnessTool,
|
|
22
|
+
s
|
|
23
|
+
} from "@harness-kernel/core";
|
|
24
|
+
var skillKeySchema = s.object({
|
|
25
|
+
key: s.string().min(1),
|
|
26
|
+
reason: s.string().optional()
|
|
27
|
+
});
|
|
28
|
+
var listSkillsSchema = s.object({
|
|
29
|
+
includeTools: s.boolean().default(true),
|
|
30
|
+
includeInactive: s.boolean().default(true)
|
|
31
|
+
});
|
|
32
|
+
var SkillActivationTool = class extends HarnessTool {
|
|
33
|
+
constructor(registry, options = {}) {
|
|
34
|
+
super();
|
|
35
|
+
this.registry = registry;
|
|
36
|
+
this.options = options;
|
|
37
|
+
}
|
|
38
|
+
registry;
|
|
39
|
+
options;
|
|
40
|
+
name = "activate_skill";
|
|
41
|
+
description = "Activate a declared procedural skill before using its gated tools.";
|
|
42
|
+
schema = skillKeySchema;
|
|
43
|
+
risk = "safe";
|
|
44
|
+
async execute(args, session) {
|
|
45
|
+
const input = skillKeySchema.parse(args);
|
|
46
|
+
const result = await activateSkill(session, this.registry, input, this.options);
|
|
47
|
+
if (!result.ok) {
|
|
48
|
+
return {
|
|
49
|
+
content: `Unknown skill '${result.key}'. Available skills: ${result.availableSkills?.map((entry) => entry.key).join(", ") || "none"}.`,
|
|
50
|
+
data: result,
|
|
51
|
+
metadata: {
|
|
52
|
+
code: result.code,
|
|
53
|
+
skillKey: result.key
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
content: result.alreadyActive ? `Skill '${result.key}' was already active.` : `Activated skill '${result.key}'.`,
|
|
59
|
+
data: result,
|
|
60
|
+
metadata: {
|
|
61
|
+
skillKey: result.key,
|
|
62
|
+
alreadyActive: result.alreadyActive
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
var SkillDeactivationTool = class extends HarnessTool {
|
|
68
|
+
constructor(registry, options = {}) {
|
|
69
|
+
super();
|
|
70
|
+
this.registry = registry;
|
|
71
|
+
this.options = options;
|
|
72
|
+
}
|
|
73
|
+
registry;
|
|
74
|
+
options;
|
|
75
|
+
name = "deactivate_skill";
|
|
76
|
+
description = "Deactivate a declared procedural skill for this session.";
|
|
77
|
+
schema = skillKeySchema;
|
|
78
|
+
risk = "safe";
|
|
79
|
+
async execute(args, session) {
|
|
80
|
+
const input = skillKeySchema.parse(args);
|
|
81
|
+
const result = await deactivateSkill(session, this.registry, input, this.options);
|
|
82
|
+
if (!result.ok) {
|
|
83
|
+
return {
|
|
84
|
+
content: `Unknown skill '${result.key}'. Available skills: ${result.availableSkills?.map((entry) => entry.key).join(", ") || "none"}.`,
|
|
85
|
+
data: result,
|
|
86
|
+
metadata: {
|
|
87
|
+
code: result.code,
|
|
88
|
+
skillKey: result.key
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
content: result.alreadyInactive ? `Skill '${result.key}' was already inactive.` : `Deactivated skill '${result.key}'.`,
|
|
94
|
+
data: result,
|
|
95
|
+
metadata: {
|
|
96
|
+
skillKey: result.key,
|
|
97
|
+
alreadyInactive: result.alreadyInactive
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
var SkillListTool = class extends HarnessTool {
|
|
103
|
+
constructor(registry, options = {}) {
|
|
104
|
+
super();
|
|
105
|
+
this.registry = registry;
|
|
106
|
+
this.options = options;
|
|
107
|
+
}
|
|
108
|
+
registry;
|
|
109
|
+
options;
|
|
110
|
+
name = "list_skills";
|
|
111
|
+
description = "List declared skills, active skills, and inactive skills for this session.";
|
|
112
|
+
schema = listSkillsSchema;
|
|
113
|
+
risk = "safe";
|
|
114
|
+
execute(args, session) {
|
|
115
|
+
const input = listSkillsSchema.parse(args);
|
|
116
|
+
const catalogOptions = { includeToolNames: input.includeTools, includeMetadata: true };
|
|
117
|
+
const data = {
|
|
118
|
+
ok: true,
|
|
119
|
+
available: listAvailableSkills(this.registry, catalogOptions),
|
|
120
|
+
active: listActiveSkills(session, this.registry, { ...catalogOptions, stateKey: this.options.stateKey }),
|
|
121
|
+
...input.includeInactive ? { inactive: listInactiveSkills(session, this.registry, { ...catalogOptions, stateKey: this.options.stateKey }) } : {}
|
|
122
|
+
};
|
|
123
|
+
return {
|
|
124
|
+
content: `Available skills: ${data.available.map((entry) => entry.key).join(", ") || "none"}. Active skills: ${data.active.map((entry) => entry.key).join(", ") || "none"}.`,
|
|
125
|
+
data,
|
|
126
|
+
metadata: {
|
|
127
|
+
includeTools: input.includeTools,
|
|
128
|
+
includeInactive: input.includeInactive
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
var SkillGatedTool = class extends HarnessTool {
|
|
134
|
+
constructor(skill, original, options = {}) {
|
|
135
|
+
super();
|
|
136
|
+
this.skill = skill;
|
|
137
|
+
this.original = original;
|
|
138
|
+
this.options = options;
|
|
139
|
+
this.label = original.label;
|
|
140
|
+
this.name = original.name;
|
|
141
|
+
this.description = `${original.description} Requires activating skill '${skill.key}' first.`;
|
|
142
|
+
this.schema = original.inputSchema;
|
|
143
|
+
this.risk = original.risk;
|
|
144
|
+
this.permissions = original.permissions;
|
|
145
|
+
this.requiresApproval = original.requiresApproval;
|
|
146
|
+
this.approvalTimeoutMs = original.approvalTimeoutMs;
|
|
147
|
+
}
|
|
148
|
+
skill;
|
|
149
|
+
original;
|
|
150
|
+
options;
|
|
151
|
+
label;
|
|
152
|
+
name;
|
|
153
|
+
description;
|
|
154
|
+
schema;
|
|
155
|
+
risk;
|
|
156
|
+
permissions;
|
|
157
|
+
requiresApproval;
|
|
158
|
+
approvalTimeoutMs;
|
|
159
|
+
async execute(args, session) {
|
|
160
|
+
if (!isSkillActive(session, this.skill.key, this.options)) {
|
|
161
|
+
await session.events.emit(SkillRequiredEvent, {
|
|
162
|
+
key: this.skill.key,
|
|
163
|
+
...this.skill.label ? { label: this.skill.label } : {},
|
|
164
|
+
toolName: this.original.name,
|
|
165
|
+
reason: "inactive"
|
|
166
|
+
});
|
|
167
|
+
session.log.emit(SkillRequiredLog, {
|
|
168
|
+
skillKey: this.skill.key,
|
|
169
|
+
toolName: this.original.name,
|
|
170
|
+
reason: "inactive"
|
|
171
|
+
});
|
|
172
|
+
return {
|
|
173
|
+
content: `Tool '${this.original.name}' requires activating skill '${this.skill.key}' first. Call activate_skill with that key.`,
|
|
174
|
+
data: {
|
|
175
|
+
ok: false,
|
|
176
|
+
code: "skill.required",
|
|
177
|
+
requiredSkill: this.skill.key,
|
|
178
|
+
toolName: this.original.name
|
|
179
|
+
},
|
|
180
|
+
metadata: {
|
|
181
|
+
skillRequired: true,
|
|
182
|
+
requiredSkill: this.skill.key,
|
|
183
|
+
originalToolName: this.original.name
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
return this.original.execute(args, session);
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
function createSkillActivationTool(registry, options) {
|
|
191
|
+
return new SkillActivationTool(toSkillRegistry(registry), options);
|
|
192
|
+
}
|
|
193
|
+
function createSkillDeactivationTool(registry, options) {
|
|
194
|
+
return new SkillDeactivationTool(toSkillRegistry(registry), options);
|
|
195
|
+
}
|
|
196
|
+
function createSkillListTool(registry, options) {
|
|
197
|
+
return new SkillListTool(toSkillRegistry(registry), options);
|
|
198
|
+
}
|
|
199
|
+
function createSkillGatedTools(registry, options) {
|
|
200
|
+
const skills = toSkillRegistry(registry);
|
|
201
|
+
const filteredKeys = options?.skillKeys?.map((key) => key.trim()).filter(Boolean);
|
|
202
|
+
if (filteredKeys?.length) {
|
|
203
|
+
const unknownKeys = filteredKeys.filter((key) => !skills.get(key));
|
|
204
|
+
if (unknownKeys.length) throw new Error(`Unknown skill gate filter key '${unknownKeys[0]}'.`);
|
|
205
|
+
}
|
|
206
|
+
const allowedKeys = filteredKeys?.length ? new Set(filteredKeys) : void 0;
|
|
207
|
+
return skills.list().filter((skill) => !allowedKeys || allowedKeys.has(skill.key)).flatMap((skill) => (skill.tools ?? []).map((tool) => new SkillGatedTool(skill, tool, options)));
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export {
|
|
211
|
+
SkillActivationTool,
|
|
212
|
+
SkillDeactivationTool,
|
|
213
|
+
SkillListTool,
|
|
214
|
+
createSkillActivationTool,
|
|
215
|
+
createSkillDeactivationTool,
|
|
216
|
+
createSkillListTool,
|
|
217
|
+
createSkillGatedTools
|
|
218
|
+
};
|
|
219
|
+
//# sourceMappingURL=chunk-6XTSIVQV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/tools.ts"],"sourcesContent":["import {\n HarnessTool,\n s,\n type AgentActionSession,\n type AgentToolResult,\n type HarnessTool as HarnessToolType,\n type InferInput,\n type ToolApprovalResolver,\n type ToolPermission,\n type ToolRisk,\n} from \"@harness-kernel/core\";\nimport type { HarnessSkill } from \"./skill.js\";\nimport { type SkillRegistry, toSkillRegistry } from \"./registry.js\";\nimport {\n activateSkill,\n deactivateSkill,\n isSkillActive,\n listActiveSkills,\n listAvailableSkills,\n listInactiveSkills,\n type SkillActivationResult,\n type SkillDeactivationResult,\n type SkillToolOptions,\n} from \"./state.js\";\nimport { SkillRequiredEvent } from \"./events.js\";\nimport { SkillRequiredLog } from \"./logs.js\";\n\nconst skillKeySchema = s.object({\n key: s.string().min(1),\n reason: s.string().optional(),\n});\n\nconst listSkillsSchema = s.object({\n includeTools: s.boolean().default(true),\n includeInactive: s.boolean().default(true),\n});\n\ntype SkillKeyInput = InferInput<typeof skillKeySchema>;\ntype ListSkillsInput = InferInput<typeof listSkillsSchema>;\n\nexport interface SkillGateOptions extends SkillToolOptions {\n skillKeys?: string[];\n}\n\nexport interface SkillListResult {\n ok: true;\n available: ReturnType<typeof listAvailableSkills>;\n active: ReturnType<typeof listActiveSkills>;\n inactive?: ReturnType<typeof listInactiveSkills>;\n}\n\nexport class SkillActivationTool extends HarnessTool<SkillKeyInput, SkillActivationResult> {\n name = \"activate_skill\";\n description = \"Activate a declared procedural skill before using its gated tools.\";\n schema = skillKeySchema;\n risk = \"safe\" as const;\n\n constructor(\n private readonly registry: SkillRegistry,\n private readonly options: SkillToolOptions = {},\n ) {\n super();\n }\n\n async execute(args: SkillKeyInput, session: AgentActionSession): Promise<AgentToolResult<SkillActivationResult>> {\n const input = skillKeySchema.parse(args);\n const result = await activateSkill(session, this.registry, input, this.options);\n if (!result.ok) {\n return {\n content: `Unknown skill '${result.key}'. Available skills: ${result.availableSkills?.map((entry) => entry.key).join(\", \") || \"none\"}.`,\n data: result,\n metadata: {\n code: result.code,\n skillKey: result.key,\n },\n };\n }\n\n return {\n content: result.alreadyActive\n ? `Skill '${result.key}' was already active.`\n : `Activated skill '${result.key}'.`,\n data: result,\n metadata: {\n skillKey: result.key,\n alreadyActive: result.alreadyActive,\n },\n };\n }\n}\n\nexport class SkillDeactivationTool extends HarnessTool<SkillKeyInput, SkillDeactivationResult> {\n name = \"deactivate_skill\";\n description = \"Deactivate a declared procedural skill for this session.\";\n schema = skillKeySchema;\n risk = \"safe\" as const;\n\n constructor(\n private readonly registry: SkillRegistry,\n private readonly options: SkillToolOptions = {},\n ) {\n super();\n }\n\n async execute(args: SkillKeyInput, session: AgentActionSession): Promise<AgentToolResult<SkillDeactivationResult>> {\n const input = skillKeySchema.parse(args);\n const result = await deactivateSkill(session, this.registry, input, this.options);\n if (!result.ok) {\n return {\n content: `Unknown skill '${result.key}'. Available skills: ${result.availableSkills?.map((entry) => entry.key).join(\", \") || \"none\"}.`,\n data: result,\n metadata: {\n code: result.code,\n skillKey: result.key,\n },\n };\n }\n\n return {\n content: result.alreadyInactive\n ? `Skill '${result.key}' was already inactive.`\n : `Deactivated skill '${result.key}'.`,\n data: result,\n metadata: {\n skillKey: result.key,\n alreadyInactive: result.alreadyInactive,\n },\n };\n }\n}\n\nexport class SkillListTool extends HarnessTool<ListSkillsInput, SkillListResult> {\n name = \"list_skills\";\n description = \"List declared skills, active skills, and inactive skills for this session.\";\n schema = listSkillsSchema;\n risk = \"safe\" as const;\n\n constructor(\n private readonly registry: SkillRegistry,\n private readonly options: SkillToolOptions = {},\n ) {\n super();\n }\n\n execute(args: ListSkillsInput, session: AgentActionSession): AgentToolResult<SkillListResult> {\n const input = listSkillsSchema.parse(args);\n const catalogOptions = { includeToolNames: input.includeTools, includeMetadata: true };\n const data: SkillListResult = {\n ok: true,\n available: listAvailableSkills(this.registry, catalogOptions),\n active: listActiveSkills(session, this.registry, { ...catalogOptions, stateKey: this.options.stateKey }),\n ...(input.includeInactive\n ? { inactive: listInactiveSkills(session, this.registry, { ...catalogOptions, stateKey: this.options.stateKey }) }\n : {}),\n };\n return {\n content: `Available skills: ${data.available.map((entry) => entry.key).join(\", \") || \"none\"}. Active skills: ${data.active.map((entry) => entry.key).join(\", \") || \"none\"}.`,\n data,\n metadata: {\n includeTools: input.includeTools,\n includeInactive: input.includeInactive,\n },\n };\n }\n}\n\nclass SkillGatedTool extends HarnessTool<unknown> {\n label?: string;\n name: string;\n description: string;\n schema?: unknown;\n risk?: ToolRisk;\n permissions?: ToolPermission[];\n requiresApproval?: boolean | ToolApprovalResolver;\n approvalTimeoutMs?: number;\n\n constructor(\n private readonly skill: HarnessSkill,\n private readonly original: HarnessToolType,\n private readonly options: SkillGateOptions = {},\n ) {\n super();\n this.label = original.label;\n this.name = original.name;\n this.description = `${original.description} Requires activating skill '${skill.key}' first.`;\n this.schema = original.inputSchema;\n this.risk = original.risk;\n this.permissions = original.permissions;\n this.requiresApproval = original.requiresApproval;\n this.approvalTimeoutMs = original.approvalTimeoutMs;\n }\n\n async execute(args: unknown, session: AgentActionSession): Promise<AgentToolResult> {\n if (!isSkillActive(session, this.skill.key, this.options)) {\n await session.events.emit(SkillRequiredEvent, {\n key: this.skill.key,\n ...(this.skill.label ? { label: this.skill.label } : {}),\n toolName: this.original.name,\n reason: \"inactive\",\n });\n session.log.emit(SkillRequiredLog, {\n skillKey: this.skill.key,\n toolName: this.original.name,\n reason: \"inactive\",\n });\n return {\n content: `Tool '${this.original.name}' requires activating skill '${this.skill.key}' first. Call activate_skill with that key.`,\n data: {\n ok: false,\n code: \"skill.required\",\n requiredSkill: this.skill.key,\n toolName: this.original.name,\n },\n metadata: {\n skillRequired: true,\n requiredSkill: this.skill.key,\n originalToolName: this.original.name,\n },\n };\n }\n\n return this.original.execute(args, session);\n }\n}\n\nexport function createSkillActivationTool(\n registry: SkillRegistry | HarnessSkill[],\n options?: SkillToolOptions,\n): HarnessTool {\n return new SkillActivationTool(toSkillRegistry(registry), options);\n}\n\nexport function createSkillDeactivationTool(\n registry: SkillRegistry | HarnessSkill[],\n options?: SkillToolOptions,\n): HarnessTool {\n return new SkillDeactivationTool(toSkillRegistry(registry), options);\n}\n\nexport function createSkillListTool(\n registry: SkillRegistry | HarnessSkill[],\n options?: SkillToolOptions,\n): HarnessTool {\n return new SkillListTool(toSkillRegistry(registry), options);\n}\n\nexport function createSkillGatedTools(\n registry: SkillRegistry | HarnessSkill[],\n options?: SkillGateOptions,\n): HarnessTool[] {\n const skills = toSkillRegistry(registry);\n const filteredKeys = options?.skillKeys?.map((key) => key.trim()).filter(Boolean);\n if (filteredKeys?.length) {\n const unknownKeys = filteredKeys.filter((key) => !skills.get(key));\n if (unknownKeys.length) throw new Error(`Unknown skill gate filter key '${unknownKeys[0]}'.`);\n }\n const allowedKeys = filteredKeys?.length ? new Set(filteredKeys) : undefined;\n\n return skills\n .list()\n .filter((skill) => !allowedKeys || allowedKeys.has(skill.key))\n .flatMap((skill) => (skill.tools ?? []).map((tool) => new SkillGatedTool(skill, tool, options)));\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// src/registry.ts
|
|
2
|
+
function catalogEntry(skill, options = {}) {
|
|
3
|
+
const includeToolNames = options.includeToolNames ?? true;
|
|
4
|
+
const includeMetadata = options.includeMetadata ?? true;
|
|
5
|
+
return {
|
|
6
|
+
key: skill.key,
|
|
7
|
+
label: skill.label,
|
|
8
|
+
description: skill.description,
|
|
9
|
+
...includeToolNames ? { toolNames: skill.tools?.map((tool) => tool.name) ?? [] } : {},
|
|
10
|
+
...includeMetadata && skill.metadata ? { metadata: skill.metadata } : {}
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function createSkillRegistry(skills) {
|
|
14
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
15
|
+
const toolOwners = /* @__PURE__ */ new Map();
|
|
16
|
+
for (const skill of skills) {
|
|
17
|
+
const key = skill.key.trim();
|
|
18
|
+
if (!key) throw new Error("Skill key must not be empty.");
|
|
19
|
+
if (byKey.has(key)) throw new Error(`Duplicate skill key '${key}'.`);
|
|
20
|
+
const normalizedSkill = skill.key === key ? skill : { ...skill, key };
|
|
21
|
+
byKey.set(key, normalizedSkill);
|
|
22
|
+
for (const tool of normalizedSkill.tools ?? []) {
|
|
23
|
+
const toolName = tool.name?.trim();
|
|
24
|
+
if (!toolName) throw new Error(`Skill '${key}' declares a tool with an empty name.`);
|
|
25
|
+
const owner = toolOwners.get(toolName);
|
|
26
|
+
if (owner) {
|
|
27
|
+
throw new Error(`Duplicate skill tool name '${toolName}' declared by skills '${owner}' and '${key}'.`);
|
|
28
|
+
}
|
|
29
|
+
toolOwners.set(toolName, key);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const list = [...byKey.values()];
|
|
33
|
+
const flattenedTools = list.flatMap((skill) => skill.tools ?? []);
|
|
34
|
+
return {
|
|
35
|
+
list: () => [...list],
|
|
36
|
+
get: (key) => byKey.get(key.trim()),
|
|
37
|
+
require: (key) => {
|
|
38
|
+
const lookupKey = key.trim();
|
|
39
|
+
const skill = byKey.get(lookupKey);
|
|
40
|
+
if (!skill) throw new Error(`Unknown skill '${lookupKey}'.`);
|
|
41
|
+
return skill;
|
|
42
|
+
},
|
|
43
|
+
catalog: (options) => list.map((skill) => catalogEntry(skill, options)),
|
|
44
|
+
tools: () => [...flattenedTools]
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function toSkillRegistry(registryOrSkills) {
|
|
48
|
+
return Array.isArray(registryOrSkills) ? createSkillRegistry(registryOrSkills) : registryOrSkills;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export {
|
|
52
|
+
createSkillRegistry,
|
|
53
|
+
toSkillRegistry
|
|
54
|
+
};
|
|
55
|
+
//# sourceMappingURL=chunk-BF5PYWY6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/registry.ts"],"sourcesContent":["import type { HarnessTool, JsonObject } from \"@harness-kernel/core\";\nimport type { HarnessSkill } from \"./skill.js\";\n\nexport interface SkillCatalogOptions {\n includeToolNames?: boolean;\n includeMetadata?: boolean;\n}\n\nexport interface SkillCatalogEntry {\n key: string;\n label?: string;\n description: string;\n active?: boolean;\n toolNames?: string[];\n metadata?: JsonObject;\n}\n\nexport interface SkillRegistry {\n list(): HarnessSkill[];\n get(key: string): HarnessSkill | undefined;\n require(key: string): HarnessSkill;\n catalog(options?: SkillCatalogOptions): SkillCatalogEntry[];\n tools(): HarnessTool[];\n}\n\nfunction catalogEntry(skill: HarnessSkill, options: SkillCatalogOptions = {}): SkillCatalogEntry {\n const includeToolNames = options.includeToolNames ?? true;\n const includeMetadata = options.includeMetadata ?? true;\n return {\n key: skill.key,\n label: skill.label,\n description: skill.description,\n ...(includeToolNames ? { toolNames: skill.tools?.map((tool) => tool.name) ?? [] } : {}),\n ...(includeMetadata && skill.metadata ? { metadata: skill.metadata } : {}),\n };\n}\n\nexport function createSkillRegistry(skills: HarnessSkill[]): SkillRegistry {\n const byKey = new Map<string, HarnessSkill>();\n const toolOwners = new Map<string, string>();\n\n for (const skill of skills) {\n const key = skill.key.trim();\n if (!key) throw new Error(\"Skill key must not be empty.\");\n if (byKey.has(key)) throw new Error(`Duplicate skill key '${key}'.`);\n const normalizedSkill = skill.key === key ? skill : { ...skill, key };\n byKey.set(key, normalizedSkill);\n\n for (const tool of normalizedSkill.tools ?? []) {\n const toolName = tool.name?.trim();\n if (!toolName) throw new Error(`Skill '${key}' declares a tool with an empty name.`);\n const owner = toolOwners.get(toolName);\n if (owner) {\n throw new Error(`Duplicate skill tool name '${toolName}' declared by skills '${owner}' and '${key}'.`);\n }\n toolOwners.set(toolName, key);\n }\n }\n\n const list = [...byKey.values()];\n const flattenedTools = list.flatMap((skill) => skill.tools ?? []);\n\n return {\n list: () => [...list],\n get: (key) => byKey.get(key.trim()),\n require: (key) => {\n const lookupKey = key.trim();\n const skill = byKey.get(lookupKey);\n if (!skill) throw new Error(`Unknown skill '${lookupKey}'.`);\n return skill;\n },\n catalog: (options) => list.map((skill) => catalogEntry(skill, options)),\n tools: () => [...flattenedTools],\n };\n}\n\nexport function toSkillRegistry(registryOrSkills: SkillRegistry | HarnessSkill[]): SkillRegistry {\n return Array.isArray(registryOrSkills)\n ? createSkillRegistry(registryOrSkills)\n : registryOrSkills;\n}\n"],"mappings":";AAyBA,SAAS,aAAa,OAAqB,UAA+B,CAAC,GAAsB;AAC/F,QAAM,mBAAmB,QAAQ,oBAAoB;AACrD,QAAM,kBAAkB,QAAQ,mBAAmB;AACnD,SAAO;AAAA,IACL,KAAK,MAAM;AAAA,IACX,OAAO,MAAM;AAAA,IACb,aAAa,MAAM;AAAA,IACnB,GAAI,mBAAmB,EAAE,WAAW,MAAM,OAAO,IAAI,CAAC,SAAS,KAAK,IAAI,KAAK,CAAC,EAAE,IAAI,CAAC;AAAA,IACrF,GAAI,mBAAmB,MAAM,WAAW,EAAE,UAAU,MAAM,SAAS,IAAI,CAAC;AAAA,EAC1E;AACF;AAEO,SAAS,oBAAoB,QAAuC;AACzE,QAAM,QAAQ,oBAAI,IAA0B;AAC5C,QAAM,aAAa,oBAAI,IAAoB;AAE3C,aAAW,SAAS,QAAQ;AAC1B,UAAM,MAAM,MAAM,IAAI,KAAK;AAC3B,QAAI,CAAC,IAAK,OAAM,IAAI,MAAM,8BAA8B;AACxD,QAAI,MAAM,IAAI,GAAG,EAAG,OAAM,IAAI,MAAM,wBAAwB,GAAG,IAAI;AACnE,UAAM,kBAAkB,MAAM,QAAQ,MAAM,QAAQ,EAAE,GAAG,OAAO,IAAI;AACpE,UAAM,IAAI,KAAK,eAAe;AAE9B,eAAW,QAAQ,gBAAgB,SAAS,CAAC,GAAG;AAC9C,YAAM,WAAW,KAAK,MAAM,KAAK;AACjC,UAAI,CAAC,SAAU,OAAM,IAAI,MAAM,UAAU,GAAG,uCAAuC;AACnF,YAAM,QAAQ,WAAW,IAAI,QAAQ;AACrC,UAAI,OAAO;AACT,cAAM,IAAI,MAAM,8BAA8B,QAAQ,yBAAyB,KAAK,UAAU,GAAG,IAAI;AAAA,MACvG;AACA,iBAAW,IAAI,UAAU,GAAG;AAAA,IAC9B;AAAA,EACF;AAEA,QAAM,OAAO,CAAC,GAAG,MAAM,OAAO,CAAC;AAC/B,QAAM,iBAAiB,KAAK,QAAQ,CAAC,UAAU,MAAM,SAAS,CAAC,CAAC;AAEhE,SAAO;AAAA,IACL,MAAM,MAAM,CAAC,GAAG,IAAI;AAAA,IACpB,KAAK,CAAC,QAAQ,MAAM,IAAI,IAAI,KAAK,CAAC;AAAA,IAClC,SAAS,CAAC,QAAQ;AAChB,YAAM,YAAY,IAAI,KAAK;AAC3B,YAAM,QAAQ,MAAM,IAAI,SAAS;AACjC,UAAI,CAAC,MAAO,OAAM,IAAI,MAAM,kBAAkB,SAAS,IAAI;AAC3D,aAAO;AAAA,IACT;AAAA,IACA,SAAS,CAAC,YAAY,KAAK,IAAI,CAAC,UAAU,aAAa,OAAO,OAAO,CAAC;AAAA,IACtE,OAAO,MAAM,CAAC,GAAG,cAAc;AAAA,EACjC;AACF;AAEO,SAAS,gBAAgB,kBAAiE;AAC/F,SAAO,MAAM,QAAQ,gBAAgB,IACjC,oBAAoB,gBAAgB,IACpC;AACN;","names":[]}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// src/events.ts
|
|
2
|
+
import { HarnessEvent, s } from "@harness-kernel/core";
|
|
3
|
+
var SkillActivationRequestedEvent = class extends HarnessEvent {
|
|
4
|
+
static type = "skill:activation_requested";
|
|
5
|
+
static schema = s.object({
|
|
6
|
+
key: s.string().min(1),
|
|
7
|
+
reason: s.string().optional(),
|
|
8
|
+
known: s.boolean(),
|
|
9
|
+
alreadyActive: s.boolean()
|
|
10
|
+
});
|
|
11
|
+
};
|
|
12
|
+
var SkillActivatedEvent = class extends HarnessEvent {
|
|
13
|
+
static type = "skill:activated";
|
|
14
|
+
static schema = s.object({
|
|
15
|
+
key: s.string().min(1),
|
|
16
|
+
label: s.string().optional(),
|
|
17
|
+
reason: s.string().optional(),
|
|
18
|
+
alreadyActive: s.boolean(),
|
|
19
|
+
activatedByToolCallId: s.string().optional()
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
var SkillDeactivatedEvent = class extends HarnessEvent {
|
|
23
|
+
static type = "skill:deactivated";
|
|
24
|
+
static schema = s.object({
|
|
25
|
+
key: s.string().min(1),
|
|
26
|
+
label: s.string().optional(),
|
|
27
|
+
reason: s.string().optional(),
|
|
28
|
+
alreadyInactive: s.boolean(),
|
|
29
|
+
deactivatedByToolCallId: s.string().optional()
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
var SkillRequiredEvent = class extends HarnessEvent {
|
|
33
|
+
static type = "skill:required";
|
|
34
|
+
static schema = s.object({
|
|
35
|
+
key: s.string().min(1),
|
|
36
|
+
label: s.string().optional(),
|
|
37
|
+
toolName: s.string().min(1),
|
|
38
|
+
reason: s.enum(["inactive", "unknown"])
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
function skillEvents() {
|
|
42
|
+
return [
|
|
43
|
+
SkillActivationRequestedEvent,
|
|
44
|
+
SkillActivatedEvent,
|
|
45
|
+
SkillDeactivatedEvent,
|
|
46
|
+
SkillRequiredEvent
|
|
47
|
+
];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export {
|
|
51
|
+
SkillActivationRequestedEvent,
|
|
52
|
+
SkillActivatedEvent,
|
|
53
|
+
SkillDeactivatedEvent,
|
|
54
|
+
SkillRequiredEvent,
|
|
55
|
+
skillEvents
|
|
56
|
+
};
|
|
57
|
+
//# sourceMappingURL=chunk-FUZYYJJU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/events.ts"],"sourcesContent":["import { HarnessEvent, s } from \"@harness-kernel/core\";\n\nexport interface SkillActivationRequestedPayload {\n key: string;\n reason?: string;\n known: boolean;\n alreadyActive: boolean;\n}\n\nexport interface SkillActivatedPayload {\n key: string;\n label?: string;\n reason?: string;\n alreadyActive: boolean;\n activatedByToolCallId?: string;\n}\n\nexport interface SkillDeactivatedPayload {\n key: string;\n label?: string;\n reason?: string;\n alreadyInactive: boolean;\n deactivatedByToolCallId?: string;\n}\n\nexport interface SkillRequiredPayload {\n key: string;\n label?: string;\n toolName: string;\n reason: \"inactive\" | \"unknown\";\n}\n\nexport class SkillActivationRequestedEvent extends HarnessEvent<SkillActivationRequestedPayload> {\n static override type = \"skill:activation_requested\";\n static override schema = s.object({\n key: s.string().min(1),\n reason: s.string().optional(),\n known: s.boolean(),\n alreadyActive: s.boolean(),\n });\n}\n\nexport class SkillActivatedEvent extends HarnessEvent<SkillActivatedPayload> {\n static override type = \"skill:activated\";\n static override schema = s.object({\n key: s.string().min(1),\n label: s.string().optional(),\n reason: s.string().optional(),\n alreadyActive: s.boolean(),\n activatedByToolCallId: s.string().optional(),\n });\n}\n\nexport class SkillDeactivatedEvent extends HarnessEvent<SkillDeactivatedPayload> {\n static override type = \"skill:deactivated\";\n static override schema = s.object({\n key: s.string().min(1),\n label: s.string().optional(),\n reason: s.string().optional(),\n alreadyInactive: s.boolean(),\n deactivatedByToolCallId: s.string().optional(),\n });\n}\n\nexport class SkillRequiredEvent extends HarnessEvent<SkillRequiredPayload> {\n static override type = \"skill:required\";\n static override schema = s.object({\n key: s.string().min(1),\n label: s.string().optional(),\n toolName: s.string().min(1),\n reason: s.enum([\"inactive\", \"unknown\"] as const),\n });\n}\n\nexport function skillEvents() {\n return [\n SkillActivationRequestedEvent,\n SkillActivatedEvent,\n SkillDeactivatedEvent,\n SkillRequiredEvent,\n ];\n}\n"],"mappings":";AAAA,SAAS,cAAc,SAAS;AAgCzB,IAAM,gCAAN,cAA4C,aAA8C;AAAA,EAC/F,OAAgB,OAAO;AAAA,EACvB,OAAgB,SAAS,EAAE,OAAO;AAAA,IAChC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,IACrB,QAAQ,EAAE,OAAO,EAAE,SAAS;AAAA,IAC5B,OAAO,EAAE,QAAQ;AAAA,IACjB,eAAe,EAAE,QAAQ;AAAA,EAC3B,CAAC;AACH;AAEO,IAAM,sBAAN,cAAkC,aAAoC;AAAA,EAC3E,OAAgB,OAAO;AAAA,EACvB,OAAgB,SAAS,EAAE,OAAO;AAAA,IAChC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,IACrB,OAAO,EAAE,OAAO,EAAE,SAAS;AAAA,IAC3B,QAAQ,EAAE,OAAO,EAAE,SAAS;AAAA,IAC5B,eAAe,EAAE,QAAQ;AAAA,IACzB,uBAAuB,EAAE,OAAO,EAAE,SAAS;AAAA,EAC7C,CAAC;AACH;AAEO,IAAM,wBAAN,cAAoC,aAAsC;AAAA,EAC/E,OAAgB,OAAO;AAAA,EACvB,OAAgB,SAAS,EAAE,OAAO;AAAA,IAChC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,IACrB,OAAO,EAAE,OAAO,EAAE,SAAS;AAAA,IAC3B,QAAQ,EAAE,OAAO,EAAE,SAAS;AAAA,IAC5B,iBAAiB,EAAE,QAAQ;AAAA,IAC3B,yBAAyB,EAAE,OAAO,EAAE,SAAS;AAAA,EAC/C,CAAC;AACH;AAEO,IAAM,qBAAN,cAAiC,aAAmC;AAAA,EACzE,OAAgB,OAAO;AAAA,EACvB,OAAgB,SAAS,EAAE,OAAO;AAAA,IAChC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,IACrB,OAAO,EAAE,OAAO,EAAE,SAAS;AAAA,IAC3B,UAAU,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,IAC1B,QAAQ,EAAE,KAAK,CAAC,YAAY,SAAS,CAAU;AAAA,EACjD,CAAC;AACH;AAEO,SAAS,cAAc;AAC5B,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;","names":[]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// src/skill.ts
|
|
2
|
+
function normalizeKey(key) {
|
|
3
|
+
return key.trim();
|
|
4
|
+
}
|
|
5
|
+
function labelFromKey(key) {
|
|
6
|
+
return key.split(/[-_.\s]+/u).filter(Boolean).map((part) => `${part.slice(0, 1).toUpperCase()}${part.slice(1)}`).join(" ");
|
|
7
|
+
}
|
|
8
|
+
function defineSkill(input) {
|
|
9
|
+
const key = normalizeKey(input.key);
|
|
10
|
+
if (!key) throw new Error("Skill key must not be empty.");
|
|
11
|
+
if (!input.description?.trim()) throw new Error(`Skill '${key}' description must not be empty.`);
|
|
12
|
+
return {
|
|
13
|
+
...input,
|
|
14
|
+
key,
|
|
15
|
+
label: input.label?.trim() || labelFromKey(key),
|
|
16
|
+
description: input.description,
|
|
17
|
+
tools: input.tools ? [...input.tools] : void 0
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export {
|
|
22
|
+
defineSkill
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=chunk-L3KCYHUW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/skill.ts"],"sourcesContent":["import type {\n AgentReadSession,\n HarnessTool,\n JsonObject,\n} from \"@harness-kernel/core\";\n\nexport type SkillPromptResolver = (\n session: AgentReadSession,\n skill: HarnessSkill,\n) => string | Promise<string>;\n\nexport interface HarnessSkill {\n key: string;\n label?: string;\n description: string;\n prompt?: string | SkillPromptResolver;\n tools?: HarnessTool[];\n metadata?: JsonObject;\n}\n\nexport interface HarnessSkillInput {\n key: string;\n label?: string;\n description: string;\n prompt?: string | SkillPromptResolver;\n tools?: HarnessTool[];\n metadata?: JsonObject;\n}\n\nfunction normalizeKey(key: string): string {\n return key.trim();\n}\n\nfunction labelFromKey(key: string): string {\n return key\n .split(/[-_.\\s]+/u)\n .filter(Boolean)\n .map((part) => `${part.slice(0, 1).toUpperCase()}${part.slice(1)}`)\n .join(\" \");\n}\n\nexport function defineSkill(input: HarnessSkillInput): HarnessSkill {\n const key = normalizeKey(input.key);\n if (!key) throw new Error(\"Skill key must not be empty.\");\n if (!input.description?.trim()) throw new Error(`Skill '${key}' description must not be empty.`);\n\n return {\n ...input,\n key,\n label: input.label?.trim() || labelFromKey(key),\n description: input.description,\n tools: input.tools ? [...input.tools] : undefined,\n };\n}\n"],"mappings":";AA6BA,SAAS,aAAa,KAAqB;AACzC,SAAO,IAAI,KAAK;AAClB;AAEA,SAAS,aAAa,KAAqB;AACzC,SAAO,IACJ,MAAM,WAAW,EACjB,OAAO,OAAO,EACd,IAAI,CAAC,SAAS,GAAG,KAAK,MAAM,GAAG,CAAC,EAAE,YAAY,CAAC,GAAG,KAAK,MAAM,CAAC,CAAC,EAAE,EACjE,KAAK,GAAG;AACb;AAEO,SAAS,YAAY,OAAwC;AAClE,QAAM,MAAM,aAAa,MAAM,GAAG;AAClC,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,8BAA8B;AACxD,MAAI,CAAC,MAAM,aAAa,KAAK,EAAG,OAAM,IAAI,MAAM,UAAU,GAAG,kCAAkC;AAE/F,SAAO;AAAA,IACL,GAAG;AAAA,IACH;AAAA,IACA,OAAO,MAAM,OAAO,KAAK,KAAK,aAAa,GAAG;AAAA,IAC9C,aAAa,MAAM;AAAA,IACnB,OAAO,MAAM,QAAQ,CAAC,GAAG,MAAM,KAAK,IAAI;AAAA,EAC1C;AACF;","names":[]}
|