@agentxm/extension-model 0.38.0 → 0.39.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/dist/src/unstable/agent-capabilities/catalog.d.ts +1820 -96
- package/dist/src/unstable/agent-capabilities/catalog.js +2 -1
- package/dist/src/unstable/agent-capabilities/data/agents/augment.d.ts +1 -1
- package/dist/src/unstable/agent-capabilities/data/agents/augment.js +1 -1
- package/dist/src/unstable/agent-capabilities/data/agents/claude-code.d.ts +21 -3
- package/dist/src/unstable/agent-capabilities/data/agents/claude-code.js +18 -3
- package/dist/src/unstable/agent-capabilities/data/agents/codebuddy.d.ts +1 -1
- package/dist/src/unstable/agent-capabilities/data/agents/codebuddy.js +1 -1
- package/dist/src/unstable/agent-capabilities/data/agents/codex.d.ts +3 -2
- package/dist/src/unstable/agent-capabilities/data/agents/codex.js +3 -2
- package/dist/src/unstable/agent-capabilities/data/agents/cursor.d.ts +186 -10
- package/dist/src/unstable/agent-capabilities/data/agents/cursor.js +223 -10
- package/dist/src/unstable/agent-capabilities/data/agents/deepagents.d.ts +1 -1
- package/dist/src/unstable/agent-capabilities/data/agents/deepagents.js +1 -1
- package/dist/src/unstable/agent-capabilities/data/agents/gemini-cli.d.ts +1 -1
- package/dist/src/unstable/agent-capabilities/data/agents/gemini-cli.js +1 -1
- package/dist/src/unstable/agent-capabilities/data/agents/github-copilot-cli.d.ts +19 -1
- package/dist/src/unstable/agent-capabilities/data/agents/github-copilot-cli.js +16 -1
- package/dist/src/unstable/agent-capabilities/data/agents/ibm-bob.d.ts +1 -1
- package/dist/src/unstable/agent-capabilities/data/agents/ibm-bob.js +1 -1
- package/dist/src/unstable/agent-capabilities/data/agents/index.d.ts +1 -0
- package/dist/src/unstable/agent-capabilities/data/agents/index.js +1 -0
- package/dist/src/unstable/agent-capabilities/data/agents/kiro-cli.d.ts +1 -1
- package/dist/src/unstable/agent-capabilities/data/agents/kiro-cli.js +1 -1
- package/dist/src/unstable/agent-capabilities/data/agents/opencode.d.ts +2 -1
- package/dist/src/unstable/agent-capabilities/data/agents/opencode.js +2 -1
- package/dist/src/unstable/agent-capabilities/data/agents/pi.d.ts +105 -6
- package/dist/src/unstable/agent-capabilities/data/agents/pi.js +77 -6
- package/dist/src/unstable/agent-capabilities/data/agents/qoder.d.ts +1 -1
- package/dist/src/unstable/agent-capabilities/data/agents/qoder.js +1 -1
- package/dist/src/unstable/agent-capabilities/data/agents/qwen-code.d.ts +1 -1
- package/dist/src/unstable/agent-capabilities/data/agents/qwen-code.js +1 -1
- package/dist/src/unstable/agent-capabilities/data/agents/roo.d.ts +1 -1
- package/dist/src/unstable/agent-capabilities/data/agents/roo.js +1 -1
- package/dist/src/unstable/agent-capabilities/data/agents/vscode.d.ts +263 -0
- package/dist/src/unstable/agent-capabilities/data/agents/vscode.js +139 -0
- package/dist/src/unstable/agent-capabilities/derive.d.ts +14 -22
- package/dist/src/unstable/agent-capabilities/derive.js +40 -51
- package/dist/src/unstable/agent-capabilities/identity.d.ts +3 -3
- package/dist/src/unstable/agent-capabilities/identity.js +1 -0
- package/dist/src/unstable/agent-capabilities/schema.d.ts +50 -12
- package/dist/src/unstable/agent-capabilities/schema.js +10 -2
- package/dist/src/unstable/agents/types.d.ts +1 -1
- package/dist/src/unstable/extensions/common.d.ts +17 -3
- package/dist/src/unstable/extensions/common.js +8 -8
- package/dist/src/unstable/extensions/installable-types.d.ts +2 -2
- package/dist/src/unstable/extensions/refs/mcp-server.d.ts +16 -2
- package/dist/src/unstable/extensions/refs/mcp-server.js +12 -1
- package/dist/src/unstable/extensions/refs/ref-base.d.ts +17 -2
- package/dist/src/unstable/extensions/refs/ref-base.js +16 -1
- package/dist/src/unstable/extensions/refs/skill.d.ts +14 -2
- package/dist/src/unstable/extensions/refs/skill.js +0 -9
- package/dist/src/unstable/hooks/manifest-schema.d.ts +221 -105
- package/dist/src/unstable/hooks/manifest-schema.js +271 -87
- package/dist/src/unstable/hooks/resolution.d.ts +24 -0
- package/dist/src/unstable/hooks/resolution.js +99 -0
- package/dist/src/unstable/sources/http-artifact.d.ts +24 -0
- package/dist/src/unstable/sources/http-artifact.js +46 -0
- package/dist/src/unstable/sources/printer.js +14 -1
- package/dist/src/unstable/sources/source-host-provider.d.ts +3 -0
- package/dist/src/unstable/sources/types.d.ts +16 -6
- package/dist/src/unstable/sources/types.js +2 -0
- package/dist/src/unstable/subagents/manifest-schema.d.ts +48 -18
- package/dist/src/unstable/subagents/manifest-schema.js +85 -18
- package/package.json +10 -2
|
@@ -1,112 +1,296 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* @experimental This API is unstable and may change without notice.
|
|
5
|
-
*/
|
|
1
|
+
/** Native Hook package contract. @experimental */
|
|
2
|
+
import * as Result from "effect/Result";
|
|
6
3
|
import * as Schema from "effect/Schema";
|
|
7
|
-
import {
|
|
8
|
-
import { CommonManifestBaseFields, ExtensionNameSchema, NonPackManifestFields, } from "../extensions/common.js";
|
|
4
|
+
import { HookBlockOutcomeSchema, HookModifyOperationSchema } from "../agent-capabilities/schema.js";
|
|
5
|
+
import { CommonManifestBaseFields, ConfigurableAgentIdSchema, ExtensionNameSchema, NonPackManifestFields, } from "../extensions/common.js";
|
|
6
|
+
import { VersionRangeSchema } from "../version-constraints/version-constraints.js";
|
|
9
7
|
export const HOOK_MANIFEST_FILENAME = "hook.json";
|
|
10
8
|
export const HOOK_EXTENSION_DIR = "hooks";
|
|
11
9
|
export const HOOK_MANIFEST_SCHEMA_URL = "https://axm.sh/schemas/hook.schema.json";
|
|
12
10
|
export const HookRuntimeSchema = Schema.Literals(["bash", "node", "python"]).annotate({
|
|
13
11
|
identifier: "HookRuntime",
|
|
14
|
-
|
|
15
|
-
description: "Interpreter family used to execute the materialized hook entrypoint.",
|
|
12
|
+
description: "Interpreter family used to execute this native command handler.",
|
|
16
13
|
});
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
identifier: "HookMatch",
|
|
26
|
-
title: "Hook Match",
|
|
27
|
-
description: "Portable hook matcher expressed over AXM canonical tool IDs.",
|
|
14
|
+
const HookIdSchema = Schema.NonEmptyString.check(Schema.isPattern(/^[a-z][a-z0-9-]*$/), Schema.isMaxLength(80));
|
|
15
|
+
const ConfigurationKeySchema = Schema.NonEmptyString.check(Schema.isPattern(/^[a-zA-Z][a-zA-Z0-9_]*$/), Schema.isMaxLength(64));
|
|
16
|
+
const EnvironmentKeySchema = Schema.NonEmptyString.check(Schema.isPattern(/^[a-zA-Z_][a-zA-Z0-9_]*$/));
|
|
17
|
+
export const HookPackagePathSchema = Schema.NonEmptyString.check(Schema.isPattern(
|
|
18
|
+
// eslint-disable-next-line no-control-regex -- Package paths must reject control characters, including NUL.
|
|
19
|
+
/^(?!\/)(?!.*(?:^|\/)\.{1,2}(?:\/|$))(?!.*\/\/)(?!.*[\\:\u0000-\u001f])[^/]+(?:\/[^/]+)*$/)).annotate({
|
|
20
|
+
identifier: "HookPackagePath",
|
|
21
|
+
description: "A safe, package-relative POSIX file path. Absolute paths and traversal are forbidden.",
|
|
28
22
|
});
|
|
29
|
-
|
|
30
|
-
|
|
23
|
+
/** Symbolic references are preserved, never resolved against the author's environment. */
|
|
24
|
+
export const HookEnvironmentReferenceSchema = Schema.Struct({ env: EnvironmentKeySchema });
|
|
25
|
+
export const HookValueSchema = Schema.Union([
|
|
26
|
+
Schema.String,
|
|
27
|
+
Schema.Struct({ config: ConfigurationKeySchema }),
|
|
28
|
+
HookEnvironmentReferenceSchema,
|
|
29
|
+
]).annotate({ identifier: "HookValue" });
|
|
30
|
+
export const HookConfigurationValueSchema = Schema.Union([
|
|
31
|
+
Schema.String,
|
|
32
|
+
Schema.Finite,
|
|
33
|
+
Schema.Boolean,
|
|
34
|
+
HookEnvironmentReferenceSchema,
|
|
35
|
+
]);
|
|
36
|
+
export const HookConfigurationValuesSchema = Schema.Record(ConfigurationKeySchema, HookConfigurationValueSchema);
|
|
37
|
+
const ConfigurationFields = {
|
|
38
|
+
description: Schema.optional(Schema.NonEmptyString),
|
|
39
|
+
required: Schema.optional(Schema.Boolean),
|
|
40
|
+
};
|
|
41
|
+
export const HookConfigurationFieldSchema = Schema.Union([
|
|
31
42
|
Schema.Struct({
|
|
32
|
-
|
|
33
|
-
|
|
43
|
+
...ConfigurationFields,
|
|
44
|
+
type: Schema.Literal("string"),
|
|
45
|
+
default: Schema.optional(Schema.String),
|
|
46
|
+
secret: Schema.optional(Schema.Boolean),
|
|
47
|
+
allowEmpty: Schema.optional(Schema.Boolean),
|
|
48
|
+
minLength: Schema.optional(Schema.Int.check(Schema.isGreaterThanOrEqualTo(0))),
|
|
49
|
+
maxLength: Schema.optional(Schema.Int.check(Schema.isGreaterThanOrEqualTo(0))),
|
|
34
50
|
}),
|
|
35
51
|
Schema.Struct({
|
|
36
|
-
|
|
37
|
-
|
|
52
|
+
...ConfigurationFields,
|
|
53
|
+
type: Schema.Literal("number"),
|
|
54
|
+
default: Schema.optional(Schema.Finite),
|
|
55
|
+
minimum: Schema.optional(Schema.Finite),
|
|
56
|
+
maximum: Schema.optional(Schema.Finite),
|
|
38
57
|
}),
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
|
|
58
|
+
Schema.Struct({
|
|
59
|
+
...ConfigurationFields,
|
|
60
|
+
type: Schema.Literal("boolean"),
|
|
61
|
+
default: Schema.optional(Schema.Boolean),
|
|
62
|
+
}),
|
|
63
|
+
Schema.Struct({
|
|
64
|
+
...ConfigurationFields,
|
|
65
|
+
type: Schema.Literal("enum"),
|
|
66
|
+
values: Schema.NonEmptyArray(Schema.NonEmptyString).check(Schema.isUnique()),
|
|
67
|
+
default: Schema.optional(Schema.NonEmptyString),
|
|
68
|
+
}),
|
|
69
|
+
]).annotate({ identifier: "HookConfigurationField" });
|
|
70
|
+
export const HookBindingRequirementsSchema = Schema.Struct({
|
|
71
|
+
outcomes: Schema.optional(Schema.NonEmptyArray(HookBlockOutcomeSchema).check(Schema.isUnique())),
|
|
72
|
+
operations: Schema.optional(Schema.NonEmptyArray(HookModifyOperationSchema).check(Schema.isUnique())),
|
|
46
73
|
}).annotate({
|
|
47
|
-
identifier: "
|
|
48
|
-
|
|
49
|
-
|
|
74
|
+
identifier: "HookBindingRequirements",
|
|
75
|
+
description: "Every declared outcome and operation must be supported by this native event. Omit for observation only.",
|
|
76
|
+
});
|
|
77
|
+
export const HookCommandHandlerSchema = Schema.Struct({
|
|
78
|
+
type: Schema.Literal("command"),
|
|
79
|
+
name: Schema.optional(Schema.NonEmptyString),
|
|
80
|
+
runtime: HookRuntimeSchema,
|
|
81
|
+
entrypoint: HookPackagePathSchema,
|
|
82
|
+
args: Schema.optional(Schema.Array(HookValueSchema)),
|
|
83
|
+
env: Schema.optional(Schema.Record(EnvironmentKeySchema, HookValueSchema)),
|
|
84
|
+
timeoutMs: Schema.optional(Schema.Int.check(Schema.isGreaterThan(0))),
|
|
85
|
+
}).annotate({ identifier: "HookCommandHandler" });
|
|
86
|
+
export const HookEventSchema = Schema.NonEmptyString.check(Schema.isPattern(/^[a-zA-Z][a-zA-Z0-9_.:-]*$/)).annotate({
|
|
87
|
+
identifier: "HookEvent",
|
|
88
|
+
description: "Exact event name in the implementation's native host protocol.",
|
|
50
89
|
});
|
|
51
|
-
export const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
90
|
+
export const HookBindingSchema = Schema.Struct({
|
|
91
|
+
id: HookIdSchema,
|
|
92
|
+
event: HookEventSchema,
|
|
93
|
+
matcher: Schema.optional(Schema.NonEmptyString),
|
|
94
|
+
handler: HookCommandHandlerSchema,
|
|
95
|
+
requires: Schema.optional(HookBindingRequirementsSchema),
|
|
96
|
+
}).annotate({ identifier: "HookBinding" });
|
|
97
|
+
export const HookImplementationSchema = Schema.Struct({
|
|
98
|
+
id: HookIdSchema,
|
|
99
|
+
protocol: ConfigurableAgentIdSchema,
|
|
56
100
|
requires: Schema.optional(Schema.Struct({
|
|
57
|
-
|
|
101
|
+
hostVersion: Schema.optional(VersionRangeSchema),
|
|
102
|
+
profiles: Schema.optional(Schema.NonEmptyArray(Schema.NonEmptyString).check(Schema.isUnique())),
|
|
103
|
+
platforms: Schema.optional(Schema.NonEmptyArray(Schema.Literals(["darwin", "linux", "win32"])).check(Schema.isUnique())),
|
|
104
|
+
scopes: Schema.optional(Schema.NonEmptyArray(Schema.Literals(["project", "user"])).check(Schema.isUnique())),
|
|
58
105
|
})),
|
|
106
|
+
bindings: Schema.NonEmptyArray(HookBindingSchema),
|
|
107
|
+
}).annotate({ identifier: "HookImplementation" });
|
|
108
|
+
export const HookFixtureSchema = Schema.Struct({
|
|
109
|
+
id: HookIdSchema,
|
|
110
|
+
implementation: HookIdSchema,
|
|
111
|
+
binding: HookIdSchema,
|
|
112
|
+
input: HookPackagePathSchema,
|
|
113
|
+
expect: Schema.Struct({
|
|
114
|
+
exitCode: Schema.Int.check(Schema.isBetween({ minimum: 0, maximum: 255 })),
|
|
115
|
+
stdout: Schema.optional(HookPackagePathSchema),
|
|
116
|
+
stderr: Schema.optional(HookPackagePathSchema),
|
|
117
|
+
}),
|
|
59
118
|
}).annotate({
|
|
60
|
-
identifier: "
|
|
61
|
-
|
|
62
|
-
description: "Universal hook event binding expressed in AXM canonical vocabulary.",
|
|
63
|
-
});
|
|
64
|
-
export const HookBindingSchema = CanonicalHookBindingSchema.annotate({
|
|
65
|
-
identifier: "HookBinding",
|
|
66
|
-
title: "Hook Binding",
|
|
67
|
-
description: "Universal hook event binding with portable canonical matching and raw native escape hatches.",
|
|
68
|
-
});
|
|
69
|
-
export const HookCapabilitiesSchema = Schema.Struct({
|
|
70
|
-
network: Schema.optional(Schema.Boolean),
|
|
71
|
-
filesystemWrite: Schema.optional(Schema.Boolean),
|
|
72
|
-
exec: Schema.optional(Schema.Array(Schema.NonEmptyString)),
|
|
73
|
-
env: Schema.optional(Schema.Array(Schema.NonEmptyString)),
|
|
74
|
-
}).annotate({
|
|
75
|
-
identifier: "HookCapabilities",
|
|
76
|
-
title: "Hook Capabilities",
|
|
77
|
-
description: "Advisory author-declared hook capability surface. v1 does not enforce it.",
|
|
119
|
+
identifier: "HookFixture",
|
|
120
|
+
description: "Native stdin and expected output files used only by explicitly requested fixture execution.",
|
|
78
121
|
});
|
|
79
|
-
|
|
80
|
-
* Schema for hook manifest files (hook.json).
|
|
81
|
-
*
|
|
82
|
-
* Hooks distribute an executable body plus agent event bindings. Install
|
|
83
|
-
* materializes the body under agent_extensions and registers native agent hook
|
|
84
|
-
* settings that invoke the materialized entrypoint.
|
|
85
|
-
*
|
|
86
|
-
* @experimental This API is unstable and may change without notice.
|
|
87
|
-
*/
|
|
88
|
-
export const HookManifestSchema = Schema.Struct({
|
|
122
|
+
const HookManifestStruct = Schema.Struct({
|
|
89
123
|
$schema: Schema.optional(Schema.String),
|
|
90
124
|
...CommonManifestBaseFields,
|
|
91
|
-
|
|
125
|
+
enhances: NonPackManifestFields.enhances,
|
|
126
|
+
requires: NonPackManifestFields.requires,
|
|
127
|
+
recommendedPacks: NonPackManifestFields.recommendedPacks,
|
|
128
|
+
standalone: NonPackManifestFields.standalone,
|
|
92
129
|
type: Schema.Literal("hook"),
|
|
93
|
-
name: ExtensionNameSchema
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
130
|
+
name: ExtensionNameSchema,
|
|
131
|
+
title: Schema.optional(Schema.NonEmptyString),
|
|
132
|
+
implementations: Schema.NonEmptyArray(HookImplementationSchema),
|
|
133
|
+
configuration: Schema.optional(Schema.Record(ConfigurationKeySchema, HookConfigurationFieldSchema)),
|
|
134
|
+
assets: Schema.optional(Schema.Array(HookPackagePathSchema).check(Schema.isUnique())),
|
|
135
|
+
fixtures: Schema.optional(Schema.Array(HookFixtureSchema)),
|
|
136
|
+
});
|
|
137
|
+
const configurationValueProblem = (field, value) => {
|
|
138
|
+
if (field.type === "string" && field.secret === true) {
|
|
139
|
+
return typeof value === "object"
|
|
140
|
+
? undefined
|
|
141
|
+
: "Secret configuration requires a symbolic environment reference.";
|
|
142
|
+
}
|
|
143
|
+
if (typeof value === "object")
|
|
144
|
+
return "Environment references are only accepted by secret string configuration.";
|
|
145
|
+
switch (field.type) {
|
|
146
|
+
case "string":
|
|
147
|
+
if (typeof value !== "string")
|
|
148
|
+
return "Expected a string.";
|
|
149
|
+
if (value.length === 0 && field.allowEmpty !== true)
|
|
150
|
+
return "An empty string is not allowed.";
|
|
151
|
+
if (field.minLength !== undefined && value.length < field.minLength)
|
|
152
|
+
return "String is shorter than minLength.";
|
|
153
|
+
if (field.maxLength !== undefined && value.length > field.maxLength)
|
|
154
|
+
return "String is longer than maxLength.";
|
|
155
|
+
return undefined;
|
|
156
|
+
case "boolean":
|
|
157
|
+
return typeof value === "boolean" ? undefined : "Expected a boolean.";
|
|
158
|
+
case "number":
|
|
159
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
160
|
+
return "Expected a finite number.";
|
|
161
|
+
if (field.minimum !== undefined && value < field.minimum)
|
|
162
|
+
return "Number is below minimum.";
|
|
163
|
+
if (field.maximum !== undefined && value > field.maximum)
|
|
164
|
+
return "Number exceeds maximum.";
|
|
165
|
+
return undefined;
|
|
166
|
+
case "enum":
|
|
167
|
+
return typeof value === "string" && field.values.includes(value)
|
|
168
|
+
? undefined
|
|
169
|
+
: "Expected a declared enum value.";
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
export const HookManifestSchema = HookManifestStruct.check(Schema.makeFilter((manifest) => {
|
|
173
|
+
const issues = [];
|
|
174
|
+
const implementationIds = new Set();
|
|
175
|
+
for (const [index, implementation] of manifest.implementations.entries()) {
|
|
176
|
+
if (implementationIds.has(implementation.id))
|
|
177
|
+
issues.push({
|
|
178
|
+
path: ["implementations", index, "id"],
|
|
179
|
+
issue: "Implementation IDs must be unique.",
|
|
180
|
+
});
|
|
181
|
+
implementationIds.add(implementation.id);
|
|
182
|
+
const bindingIds = new Set();
|
|
183
|
+
for (const [bindingIndex, binding] of implementation.bindings.entries()) {
|
|
184
|
+
if (bindingIds.has(binding.id))
|
|
185
|
+
issues.push({
|
|
186
|
+
path: ["implementations", index, "bindings", bindingIndex, "id"],
|
|
187
|
+
issue: "Binding IDs must be unique within an implementation.",
|
|
188
|
+
});
|
|
189
|
+
bindingIds.add(binding.id);
|
|
190
|
+
for (const value of [
|
|
191
|
+
...(binding.handler.args ?? []),
|
|
192
|
+
...Object.values(binding.handler.env ?? {}),
|
|
193
|
+
]) {
|
|
194
|
+
if (typeof value === "object" &&
|
|
195
|
+
"config" in value &&
|
|
196
|
+
!Object.hasOwn(manifest.configuration ?? {}, value.config)) {
|
|
197
|
+
issues.push({
|
|
198
|
+
path: ["implementations", index, "bindings", bindingIndex, "handler"],
|
|
199
|
+
issue: `Unknown configuration reference: ${value.config}.`,
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
for (const [key, field] of Object.entries(manifest.configuration ?? {})) {
|
|
206
|
+
if (field.default !== undefined) {
|
|
207
|
+
const problem = configurationValueProblem(field, field.default);
|
|
208
|
+
if (problem !== undefined)
|
|
209
|
+
issues.push({ path: ["configuration", key, "default"], issue: problem });
|
|
210
|
+
}
|
|
211
|
+
if (field.type === "string" &&
|
|
212
|
+
field.minLength !== undefined &&
|
|
213
|
+
field.maxLength !== undefined &&
|
|
214
|
+
field.minLength > field.maxLength)
|
|
215
|
+
issues.push({
|
|
216
|
+
path: ["configuration", key],
|
|
217
|
+
issue: "minLength must not exceed maxLength.",
|
|
218
|
+
});
|
|
219
|
+
if (field.type === "number" &&
|
|
220
|
+
field.minimum !== undefined &&
|
|
221
|
+
field.maximum !== undefined &&
|
|
222
|
+
field.minimum > field.maximum)
|
|
223
|
+
issues.push({ path: ["configuration", key], issue: "minimum must not exceed maximum." });
|
|
224
|
+
}
|
|
225
|
+
const fixtureIds = new Set();
|
|
226
|
+
for (const [index, fixture] of (manifest.fixtures ?? []).entries()) {
|
|
227
|
+
if (fixtureIds.has(fixture.id))
|
|
228
|
+
issues.push({ path: ["fixtures", index, "id"], issue: "Fixture IDs must be unique." });
|
|
229
|
+
fixtureIds.add(fixture.id);
|
|
230
|
+
const implementation = manifest.implementations.find((entry) => entry.id === fixture.implementation);
|
|
231
|
+
if (implementation === undefined ||
|
|
232
|
+
!implementation.bindings.some((entry) => entry.id === fixture.binding))
|
|
233
|
+
issues.push({
|
|
234
|
+
path: ["fixtures", index],
|
|
235
|
+
issue: "Fixture must reference an existing implementation and binding.",
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
return issues;
|
|
239
|
+
})).annotate({
|
|
108
240
|
identifier: "HookManifest",
|
|
109
241
|
title: "Hook Manifest",
|
|
110
|
-
description: "
|
|
242
|
+
description: "A package of explicit native Hook implementations, shared assets, typed consumer configuration, and optional native protocol fixtures. AXM installs native settings; it does not translate payloads or execute hook bodies during lifecycle operations.",
|
|
111
243
|
});
|
|
244
|
+
/** Files required to install and activate any declared implementation. */
|
|
245
|
+
export const requiredHookRuntimeFiles = (manifest) => [
|
|
246
|
+
...new Set([
|
|
247
|
+
...(manifest.assets ?? []),
|
|
248
|
+
...manifest.implementations.flatMap((implementation) => implementation.bindings.map((binding) => binding.handler.entrypoint)),
|
|
249
|
+
]),
|
|
250
|
+
];
|
|
251
|
+
/** Source-authoring closure; fixtures may be omitted from a distributed package. */
|
|
252
|
+
export const referencedHookPackageFiles = (manifest) => [
|
|
253
|
+
...new Set([
|
|
254
|
+
...requiredHookRuntimeFiles(manifest),
|
|
255
|
+
...(manifest.fixtures ?? []).flatMap((fixture) => [
|
|
256
|
+
fixture.input,
|
|
257
|
+
...(fixture.expect.stdout === undefined ? [] : [fixture.expect.stdout]),
|
|
258
|
+
...(fixture.expect.stderr === undefined ? [] : [fixture.expect.stderr]),
|
|
259
|
+
]),
|
|
260
|
+
]),
|
|
261
|
+
];
|
|
262
|
+
/** Explicit consumer values override author defaults; no source objects are mutated. */
|
|
263
|
+
export const resolveHookConfiguration = (manifest, values = {}) => {
|
|
264
|
+
const fields = manifest.configuration ?? {};
|
|
265
|
+
const issues = [];
|
|
266
|
+
const resolved = {};
|
|
267
|
+
for (const key of Object.keys(values))
|
|
268
|
+
if (!Object.hasOwn(fields, key))
|
|
269
|
+
issues.push({ key, code: "unknown", message: "Unknown hook configuration key." });
|
|
270
|
+
for (const [key, field] of Object.entries(fields)) {
|
|
271
|
+
const input = Object.hasOwn(values, key) ? values[key] : field.default;
|
|
272
|
+
if (input === undefined) {
|
|
273
|
+
if (field.required === true)
|
|
274
|
+
issues.push({ key, code: "missing", message: "Required hook configuration is missing." });
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
const decoded = Schema.decodeUnknownResult(HookConfigurationValueSchema)(input, {
|
|
278
|
+
onExcessProperty: "error",
|
|
279
|
+
});
|
|
280
|
+
if (Result.isFailure(decoded)) {
|
|
281
|
+
issues.push({
|
|
282
|
+
key,
|
|
283
|
+
code: "invalid",
|
|
284
|
+
message: "Expected a scalar value or symbolic environment reference.",
|
|
285
|
+
});
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
const problem = configurationValueProblem(field, decoded.success);
|
|
289
|
+
if (problem !== undefined)
|
|
290
|
+
issues.push({ key, code: "invalid", message: problem });
|
|
291
|
+
else
|
|
292
|
+
resolved[key] = decoded.success;
|
|
293
|
+
}
|
|
294
|
+
return issues.length > 0 ? Result.fail(issues) : Result.succeed(resolved);
|
|
295
|
+
};
|
|
112
296
|
//# sourceMappingURL=manifest-schema.js.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ConfigurableAgentId } from "../agent-capabilities/identity.js";
|
|
2
|
+
import type { WorkspaceScope } from "../workspace-scope.js";
|
|
3
|
+
import type { HookManifest, HookImplementation } from "./manifest-schema.js";
|
|
4
|
+
export interface HookImplementationContext {
|
|
5
|
+
readonly scope: WorkspaceScope;
|
|
6
|
+
readonly platform?: string;
|
|
7
|
+
readonly hostVersion?: string;
|
|
8
|
+
readonly profile?: string;
|
|
9
|
+
readonly availableRuntimes?: ReadonlyArray<"bash" | "node" | "python">;
|
|
10
|
+
}
|
|
11
|
+
export type HookImplementationResolution = {
|
|
12
|
+
readonly status: "selected" | "conditional";
|
|
13
|
+
readonly implementation: HookImplementation;
|
|
14
|
+
readonly conditions: ReadonlyArray<string>;
|
|
15
|
+
} | {
|
|
16
|
+
readonly status: "unsupported";
|
|
17
|
+
readonly reasons: ReadonlyArray<string>;
|
|
18
|
+
} | {
|
|
19
|
+
readonly status: "ambiguous";
|
|
20
|
+
readonly implementationIds: ReadonlyArray<string>;
|
|
21
|
+
};
|
|
22
|
+
/** Select declared implementations without treating unknown host facts as verified support. */
|
|
23
|
+
export declare const resolveHookImplementation: (manifest: HookManifest, agentId: ConfigurableAgentId, context: HookImplementationContext) => HookImplementationResolution;
|
|
24
|
+
//# sourceMappingURL=resolution.d.ts.map
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/** Exact native Hook realization selection, shared by projection and discovery. @experimental */
|
|
2
|
+
import * as semver from "semver";
|
|
3
|
+
import { installable } from "../agent-capabilities/derive.js";
|
|
4
|
+
import { agentById } from "../agent-capabilities/lookup.js";
|
|
5
|
+
/** Select declared implementations without treating unknown host facts as verified support. */
|
|
6
|
+
export const resolveHookImplementation = (manifest, agentId, context) => {
|
|
7
|
+
// Native command serialization currently uses POSIX shell quoting and expansion.
|
|
8
|
+
if (context.platform !== undefined &&
|
|
9
|
+
context.platform !== "linux" &&
|
|
10
|
+
context.platform !== "darwin")
|
|
11
|
+
return {
|
|
12
|
+
status: "unsupported",
|
|
13
|
+
reasons: [
|
|
14
|
+
`Native Hook command serialization is unavailable on ${context.platform}; supported platforms are linux and darwin.`,
|
|
15
|
+
],
|
|
16
|
+
};
|
|
17
|
+
const agent = agentById(agentId);
|
|
18
|
+
const hook = agent.capabilities.hook;
|
|
19
|
+
const writer = hook.axm.writer;
|
|
20
|
+
if (writer === null ||
|
|
21
|
+
!("locations" in hook.native) ||
|
|
22
|
+
!hook.native.locations.some((location) => location.scope === context.scope && writer.locationIds.includes(location.id)))
|
|
23
|
+
return {
|
|
24
|
+
status: "unsupported",
|
|
25
|
+
reasons: [`No native Hook writer supports ${agentId} at ${context.scope} scope.`],
|
|
26
|
+
};
|
|
27
|
+
const candidates = [];
|
|
28
|
+
const reasons = [];
|
|
29
|
+
for (const implementation of manifest.implementations) {
|
|
30
|
+
if (implementation.protocol !== agentId)
|
|
31
|
+
continue;
|
|
32
|
+
const failures = implementation.bindings
|
|
33
|
+
.map((binding) => installable(agent, binding))
|
|
34
|
+
.filter((verdict) => !verdict.installable);
|
|
35
|
+
if (failures.length > 0) {
|
|
36
|
+
reasons.push(...failures.map((failure) => `${implementation.id}: ${failure.reason}`));
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
const required = implementation.requires;
|
|
40
|
+
if (required?.scopes !== undefined && !required.scopes.includes(context.scope)) {
|
|
41
|
+
reasons.push(`${implementation.id} does not support ${context.scope} scope.`);
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
const conditions = context.platform === undefined
|
|
45
|
+
? ["Platform must support the native POSIX command serializer (linux or darwin)."]
|
|
46
|
+
: [];
|
|
47
|
+
let excluded = false;
|
|
48
|
+
const runtimes = [
|
|
49
|
+
...new Set(implementation.bindings.map((binding) => binding.handler.runtime)),
|
|
50
|
+
];
|
|
51
|
+
if (context.availableRuntimes === undefined)
|
|
52
|
+
conditions.push(`Native host environment must provide: ${runtimes.map((runtime) => (runtime === "python" ? "python3" : runtime)).join(", ")}; availability is unverified.`);
|
|
53
|
+
else if (runtimes.some((runtime) => !context.availableRuntimes?.includes(runtime))) {
|
|
54
|
+
reasons.push(`${implementation.id} requires unavailable runtimes: ${runtimes.filter((runtime) => !context.availableRuntimes?.includes(runtime)).join(", ")}.`);
|
|
55
|
+
excluded = true;
|
|
56
|
+
}
|
|
57
|
+
if (required?.platforms !== undefined) {
|
|
58
|
+
if (context.platform === undefined)
|
|
59
|
+
conditions.push(`Platform must be one of: ${required.platforms.join(", ")}.`);
|
|
60
|
+
else if (!required.platforms.some((platform) => platform === context.platform)) {
|
|
61
|
+
reasons.push(`${implementation.id} does not support ${context.platform}.`);
|
|
62
|
+
excluded = true;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (required?.profiles !== undefined) {
|
|
66
|
+
if (context.profile === undefined)
|
|
67
|
+
conditions.push(`Host profile must be one of: ${required.profiles.join(", ")}.`);
|
|
68
|
+
else if (!required.profiles.includes(context.profile)) {
|
|
69
|
+
reasons.push(`${implementation.id} does not support profile ${context.profile}.`);
|
|
70
|
+
excluded = true;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (required?.hostVersion !== undefined) {
|
|
74
|
+
if (context.hostVersion === undefined)
|
|
75
|
+
conditions.push(`Host version must satisfy ${required.hostVersion}.`);
|
|
76
|
+
else if (!semver.satisfies(context.hostVersion, required.hostVersion)) {
|
|
77
|
+
reasons.push(`${implementation.id} requires host version ${required.hostVersion}.`);
|
|
78
|
+
excluded = true;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (!excluded)
|
|
82
|
+
candidates.push({ implementation, conditions });
|
|
83
|
+
}
|
|
84
|
+
if (candidates.length > 1)
|
|
85
|
+
return {
|
|
86
|
+
status: "ambiguous",
|
|
87
|
+
implementationIds: candidates.map(({ implementation }) => implementation.id),
|
|
88
|
+
};
|
|
89
|
+
const candidate = candidates[0];
|
|
90
|
+
if (candidate === undefined)
|
|
91
|
+
return {
|
|
92
|
+
status: "unsupported",
|
|
93
|
+
reasons: reasons.length > 0
|
|
94
|
+
? reasons
|
|
95
|
+
: [`No implementation declares the ${agentId} native protocol.`],
|
|
96
|
+
};
|
|
97
|
+
return { status: candidate.conditions.length === 0 ? "selected" : "conditional", ...candidate };
|
|
98
|
+
};
|
|
99
|
+
//# sourceMappingURL=resolution.js.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as Schema from "effect/Schema";
|
|
2
|
+
/** Digest of the exact downloaded artifact bytes, independent of the unpacked tree. */
|
|
3
|
+
export declare const ArtifactDigestSchema: Schema.brand<Schema.String, "ArtifactDigest">;
|
|
4
|
+
export type ArtifactDigest = typeof ArtifactDigestSchema.Type;
|
|
5
|
+
/** A relative file in a downloaded package, never a filesystem escape. */
|
|
6
|
+
export declare const ArtifactPathSchema: Schema.NonEmptyString;
|
|
7
|
+
export declare const ArtifactUrlSchema: Schema.URLFromString;
|
|
8
|
+
/** Accepted download inputs; replay verifies each exact artifact without re-reading discovery. */
|
|
9
|
+
export declare const HttpArtifactSnapshotSchema: Schema.Union<readonly [Schema.Struct<{
|
|
10
|
+
readonly format: Schema.Literals<readonly ["skill-md", "zip", "tar", "tar.gz"]>;
|
|
11
|
+
readonly url: Schema.URLFromString;
|
|
12
|
+
readonly digest: Schema.brand<Schema.String, "ArtifactDigest">;
|
|
13
|
+
}>, Schema.Struct<{
|
|
14
|
+
readonly format: Schema.Literal<"files">;
|
|
15
|
+
readonly files: Schema.$Array<Schema.Struct<{
|
|
16
|
+
readonly path: Schema.NonEmptyString;
|
|
17
|
+
readonly url: Schema.URLFromString;
|
|
18
|
+
readonly digest: Schema.brand<Schema.String, "ArtifactDigest">;
|
|
19
|
+
}>>;
|
|
20
|
+
}>]>;
|
|
21
|
+
export type HttpArtifactSnapshot = typeof HttpArtifactSnapshotSchema.Type;
|
|
22
|
+
/** Recognizable public artifact and discovery locations, leaving clone URLs to Git. */
|
|
23
|
+
export declare const httpSourceKind: (url: URL) => "skill-md" | "archive" | "index" | undefined;
|
|
24
|
+
//# sourceMappingURL=http-artifact.d.ts.map
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import * as Schema from "effect/Schema";
|
|
2
|
+
/** Digest of the exact downloaded artifact bytes, independent of the unpacked tree. */
|
|
3
|
+
export const ArtifactDigestSchema = Schema.String.pipe(Schema.check(Schema.makeFilter((value) => /^sha256:[0-9a-f]{64}$/u.test(value)
|
|
4
|
+
? undefined
|
|
5
|
+
: "Expected SHA-256 artifact digest in sha256:<hex> form")), Schema.brand("ArtifactDigest"));
|
|
6
|
+
/** A relative file in a downloaded package, never a filesystem escape. */
|
|
7
|
+
export const ArtifactPathSchema = Schema.NonEmptyString.pipe(Schema.check(Schema.makeFilter((value) => value.includes("\\") ||
|
|
8
|
+
value.includes("\0") ||
|
|
9
|
+
value.startsWith("/") ||
|
|
10
|
+
/^[A-Za-z]:/u.test(value) ||
|
|
11
|
+
value.split("/").some((part) => part === ".." || part === "." || part.length === 0)
|
|
12
|
+
? "Expected a contained artifact file path"
|
|
13
|
+
: undefined)));
|
|
14
|
+
export const ArtifactUrlSchema = Schema.URLFromString.pipe(Schema.check(Schema.makeFilter((url) => url.protocol === "https:" && url.username === "" && url.password === ""
|
|
15
|
+
? undefined
|
|
16
|
+
: "Expected a credential-free HTTPS artifact URL")));
|
|
17
|
+
/** Accepted download inputs; replay verifies each exact artifact without re-reading discovery. */
|
|
18
|
+
export const HttpArtifactSnapshotSchema = Schema.Union([
|
|
19
|
+
Schema.Struct({
|
|
20
|
+
format: Schema.Literals(["skill-md", "zip", "tar", "tar.gz"]),
|
|
21
|
+
url: ArtifactUrlSchema,
|
|
22
|
+
digest: ArtifactDigestSchema,
|
|
23
|
+
}),
|
|
24
|
+
Schema.Struct({
|
|
25
|
+
format: Schema.Literal("files"),
|
|
26
|
+
files: Schema.Array(Schema.Struct({
|
|
27
|
+
path: ArtifactPathSchema,
|
|
28
|
+
url: ArtifactUrlSchema,
|
|
29
|
+
digest: ArtifactDigestSchema,
|
|
30
|
+
})).pipe(Schema.check(Schema.isMinLength(1))),
|
|
31
|
+
}),
|
|
32
|
+
]);
|
|
33
|
+
/** Recognizable public artifact and discovery locations, leaving clone URLs to Git. */
|
|
34
|
+
export const httpSourceKind = (url) => {
|
|
35
|
+
if (url.protocol !== "https:")
|
|
36
|
+
return undefined;
|
|
37
|
+
if (url.pathname.endsWith("/SKILL.md"))
|
|
38
|
+
return "skill-md";
|
|
39
|
+
if (/\.(zip|tar|tar\.gz|tgz)$/iu.test(url.pathname))
|
|
40
|
+
return "archive";
|
|
41
|
+
if (url.pathname === "/" ||
|
|
42
|
+
/^\/\.well-known\/(agent-skills|skills)(?:\/index\.json|\/?)$/u.test(url.pathname))
|
|
43
|
+
return "index";
|
|
44
|
+
return undefined;
|
|
45
|
+
};
|
|
46
|
+
//# sourceMappingURL=http-artifact.js.map
|
|
@@ -21,6 +21,12 @@ const printLocalSource = (source) => {
|
|
|
21
21
|
*/
|
|
22
22
|
export const printSourceParams = (source) => {
|
|
23
23
|
switch (source.type) {
|
|
24
|
+
case "http": {
|
|
25
|
+
const url = new URL(source.url.href);
|
|
26
|
+
if (source.entry !== undefined)
|
|
27
|
+
url.hash = `skill=${encodeURIComponent(source.entry)}`;
|
|
28
|
+
return url.href;
|
|
29
|
+
}
|
|
24
30
|
case "local":
|
|
25
31
|
return printLocalSource(source);
|
|
26
32
|
case "git": {
|
|
@@ -28,7 +34,14 @@ export const printSourceParams = (source) => {
|
|
|
28
34
|
onSome: printForgeCoordinate,
|
|
29
35
|
onNone: () => {
|
|
30
36
|
const url = new URL(source.url.href);
|
|
31
|
-
|
|
37
|
+
if (Option.isSome(source.subPath)) {
|
|
38
|
+
const selector = new URLSearchParams({ path: source.subPath.value });
|
|
39
|
+
if (Option.isSome(source.ref))
|
|
40
|
+
selector.set("ref", source.ref.value);
|
|
41
|
+
url.hash = `axm:${selector.toString()}`;
|
|
42
|
+
}
|
|
43
|
+
else
|
|
44
|
+
url.hash = Option.getOrElse(source.ref, () => "");
|
|
32
45
|
return url.href;
|
|
33
46
|
},
|
|
34
47
|
});
|
|
@@ -142,6 +142,9 @@ export interface NamedRegistryCandidate {
|
|
|
142
142
|
export interface ExtensionFiles {
|
|
143
143
|
/** Absolute path to directory containing extension files. */
|
|
144
144
|
readonly directory: string;
|
|
145
|
+
/** Complete package context for a selected component. */
|
|
146
|
+
readonly packageDirectory?: string;
|
|
147
|
+
readonly componentPath?: string;
|
|
145
148
|
}
|
|
146
149
|
/**
|
|
147
150
|
* Source host provider that unifies how all source types are accessed.
|