@sayknow-cli/coding-agent 0.3.0 → 0.3.2
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/bin/skc.js +4 -0
- package/dist/types/cli/mcp-cli.d.ts +25 -0
- package/dist/types/cli/notify-cli.d.ts +2 -0
- package/dist/types/cli/plugin-cli.d.ts +2 -0
- package/dist/types/cli.d.ts +6 -0
- package/dist/types/commands/mcp.d.ts +70 -0
- package/dist/types/commands/plugin.d.ts +6 -0
- package/dist/types/commands/session.d.ts +6 -0
- package/dist/types/config/keybindings.d.ts +2 -2
- package/dist/types/config/model-profile-activation.d.ts +8 -1
- package/dist/types/config/settings-schema.d.ts +39 -2
- package/dist/types/deep-interview/plaintext-gate-guard.d.ts +11 -0
- package/dist/types/defaults/skc-defaults.d.ts +2 -13
- package/dist/types/extensibility/shared-events.d.ts +1 -0
- package/dist/types/extensibility/skc-plugins/compiler.d.ts +19 -0
- package/dist/types/extensibility/skc-plugins/constrained-hooks.d.ts +29 -0
- package/dist/types/extensibility/skc-plugins/index.d.ts +9 -0
- package/dist/types/extensibility/skc-plugins/injection.d.ts +9 -0
- package/dist/types/extensibility/skc-plugins/installer.d.ts +13 -0
- package/dist/types/extensibility/skc-plugins/mcp-policy.d.ts +26 -0
- package/dist/types/extensibility/skc-plugins/observability.d.ts +27 -0
- package/dist/types/extensibility/skc-plugins/prompt-appendix.d.ts +16 -0
- package/dist/types/extensibility/skc-plugins/registry.d.ts +32 -0
- package/dist/types/extensibility/skc-plugins/runtime-adapters.d.ts +64 -0
- package/dist/types/extensibility/skc-plugins/session-validation.d.ts +42 -0
- package/dist/types/extensibility/skc-plugins/types.d.ts +158 -2
- package/dist/types/extensibility/skc-plugins/validation.d.ts +8 -1
- package/dist/types/lsp/types.d.ts +2 -0
- package/dist/types/main.d.ts +2 -0
- package/dist/types/modes/components/custom-editor.d.ts +1 -1
- package/dist/types/modes/components/model-selector.d.ts +8 -0
- package/dist/types/modes/components/status-line/git-utils.d.ts +6 -0
- package/dist/types/modes/theme/defaults/index.d.ts +99 -0
- package/dist/types/notifications/attachment-registry.d.ts +17 -0
- package/dist/types/notifications/chat-adapters.d.ts +9 -0
- package/dist/types/notifications/config.d.ts +9 -1
- package/dist/types/notifications/engine.d.ts +59 -0
- package/dist/types/notifications/html-format.d.ts +11 -0
- package/dist/types/notifications/index.d.ts +149 -1
- package/dist/types/notifications/lifecycle-commands.d.ts +60 -0
- package/dist/types/notifications/lifecycle-control-runtime.d.ts +98 -0
- package/dist/types/notifications/lifecycle-orchestrator.d.ts +144 -0
- package/dist/types/notifications/managed-daemon.d.ts +48 -0
- package/dist/types/notifications/operator-runtime.d.ts +52 -0
- package/dist/types/notifications/rate-limit-pool.d.ts +2 -0
- package/dist/types/notifications/recent-activity.d.ts +35 -0
- package/dist/types/notifications/telegram-daemon.d.ts +133 -16
- package/dist/types/notifications/telegram-reference.d.ts +3 -1
- package/dist/types/notifications/threaded-inbound.d.ts +19 -0
- package/dist/types/notifications/threaded-render.d.ts +6 -1
- package/dist/types/notifications/topic-registry.d.ts +12 -9
- package/dist/types/runtime-mcp/types.d.ts +7 -0
- package/dist/types/sdk.d.ts +2 -0
- package/dist/types/session/agent-session.d.ts +16 -4
- package/dist/types/session/blob-store.d.ts +25 -0
- package/dist/types/session/session-manager.d.ts +57 -0
- package/dist/types/skc-runtime/launch-tmux.d.ts +1 -0
- package/dist/types/skc-runtime/psmux-detect.d.ts +78 -0
- package/dist/types/skc-runtime/ralplan-runtime.d.ts +1 -1
- package/dist/types/skc-runtime/team-runtime.d.ts +2 -0
- package/dist/types/skc-runtime/tmux-common.d.ts +30 -2
- package/dist/types/skc-runtime/tmux-sessions.d.ts +18 -0
- package/dist/types/slash-commands/helpers/fast-status-report.d.ts +6 -0
- package/dist/types/system-prompt.d.ts +2 -0
- package/dist/types/task/executor.d.ts +9 -1
- package/dist/types/tools/composer-bash-policy.d.ts +14 -0
- package/dist/types/tools/fetch.d.ts +23 -0
- package/dist/types/tools/index.d.ts +4 -1
- package/dist/types/tools/telegram-send.d.ts +32 -0
- package/dist/types/utils/changelog.d.ts +1 -0
- package/dist/types/web/insane/bridge.d.ts +103 -0
- package/dist/types/web/insane/url-guard.d.ts +25 -0
- package/dist/types/web/scrapers/types.d.ts +5 -0
- package/dist/types/web/scrapers/utils.d.ts +7 -1
- package/dist/types/web/search/provider.d.ts +18 -1
- package/dist/types/web/search/providers/insane.d.ts +53 -0
- package/dist/types/web/search/providers/text-citations.d.ts +23 -0
- package/dist/types/web/search/types.d.ts +12 -4
- package/package.json +14 -10
- package/scripts/g004-tmux-smoke.ts +100 -0
- package/scripts/g005-daemon-smoke.ts +180 -0
- package/scripts/g011-daemon-path-smoke.ts +153 -0
- package/scripts/verify-insane-vendor.ts +132 -0
- package/src/cli/args.ts +1 -1
- package/src/cli/fast-help.ts +30 -2
- package/src/cli/mcp-cli.ts +272 -0
- package/src/cli/notify-cli.ts +152 -5
- package/src/cli/plugin-cli.ts +66 -3
- package/src/cli.ts +30 -12
- package/src/commands/mcp.ts +117 -0
- package/src/commands/plugin.ts +4 -0
- package/src/commands/session.ts +18 -0
- package/src/commands/team.ts +1 -1
- package/src/config/keybindings.ts +2 -2
- package/src/config/model-profile-activation.ts +55 -7
- package/src/config/settings-schema.ts +30 -1
- package/src/deep-interview/plaintext-gate-guard.ts +94 -0
- package/src/defaults/skc/extensions/grok-cli-vendor/biome.json +1 -1
- package/src/defaults/skc/skills/deep-interview/SKILL.md +7 -6
- package/src/defaults/skc/skills/ralplan/SKILL.md +11 -4
- package/src/defaults/skc/skills/team/SKILL.md +5 -3
- package/src/defaults/skc/skills/ultragoal/SKILL.md +41 -13
- package/src/defaults/skc-defaults.ts +2 -27
- package/src/export/html/index.ts +2 -2
- package/src/extensibility/extensions/runner.ts +1 -0
- package/src/extensibility/shared-events.ts +1 -0
- package/src/extensibility/skc-plugins/compiler.ts +351 -0
- package/src/extensibility/skc-plugins/constrained-hooks.ts +170 -0
- package/src/extensibility/skc-plugins/index.ts +9 -0
- package/src/extensibility/skc-plugins/injection.ts +109 -0
- package/src/extensibility/skc-plugins/installer.ts +434 -0
- package/src/extensibility/skc-plugins/loader.ts +3 -1
- package/src/extensibility/skc-plugins/mcp-policy.ts +239 -0
- package/src/extensibility/skc-plugins/observability.ts +84 -0
- package/src/extensibility/skc-plugins/paths.ts +1 -1
- package/src/extensibility/skc-plugins/prompt-appendix.ts +109 -0
- package/src/extensibility/skc-plugins/registry.ts +180 -0
- package/src/extensibility/skc-plugins/runtime-adapters.ts +234 -0
- package/src/extensibility/skc-plugins/schema.ts +250 -20
- package/src/extensibility/skc-plugins/session-validation.ts +147 -0
- package/src/extensibility/skc-plugins/types.ts +199 -3
- package/src/extensibility/skc-plugins/validation.ts +80 -0
- package/src/extensibility/skills.ts +15 -0
- package/src/hooks/skill-state.ts +57 -0
- package/src/internal-urls/docs-index.generated.ts +17 -13
- package/src/lsp/config.ts +16 -3
- package/src/lsp/defaults.json +7 -0
- package/src/lsp/types.ts +2 -0
- package/src/main.ts +14 -3
- package/src/modes/bridge/bridge-mode.ts +11 -0
- package/src/modes/components/assistant-message.ts +49 -1
- package/src/modes/components/custom-editor.ts +2 -0
- package/src/modes/components/footer.ts +2 -3
- package/src/modes/components/hook-editor.ts +1 -1
- package/src/modes/components/hook-selector.ts +67 -43
- package/src/modes/components/model-selector.ts +56 -11
- package/src/modes/components/status-line/git-utils.ts +25 -0
- package/src/modes/components/status-line.ts +10 -11
- package/src/modes/components/welcome.ts +2 -3
- package/src/modes/controllers/event-controller.ts +15 -0
- package/src/modes/controllers/extension-ui-controller.ts +0 -27
- package/src/modes/controllers/selector-controller.ts +53 -11
- package/src/modes/interactive-mode.ts +50 -3
- package/src/modes/shared/agent-wire/scopes.ts +1 -1
- package/src/modes/theme/defaults/gruvbox-dark.json +99 -0
- package/src/modes/theme/defaults/index.ts +2 -0
- package/src/modes/utils/context-usage.ts +2 -2
- package/src/modes/utils/hotkeys-markdown.ts +1 -1
- package/src/notifications/attachment-registry.ts +23 -0
- package/src/notifications/chat-adapters.ts +147 -0
- package/src/notifications/config.ts +23 -2
- package/src/notifications/engine.ts +100 -0
- package/src/notifications/html-format.ts +38 -0
- package/src/notifications/index.ts +417 -45
- package/src/notifications/lifecycle-commands.ts +238 -0
- package/src/notifications/lifecycle-control-runtime.ts +405 -0
- package/src/notifications/lifecycle-orchestrator.ts +358 -0
- package/src/notifications/managed-daemon.ts +163 -0
- package/src/notifications/operator-runtime.ts +171 -0
- package/src/notifications/rate-limit-pool.ts +19 -0
- package/src/notifications/recent-activity.ts +132 -0
- package/src/notifications/telegram-daemon.ts +984 -242
- package/src/notifications/telegram-reference.ts +25 -7
- package/src/notifications/threaded-inbound.ts +60 -4
- package/src/notifications/threaded-render.ts +20 -2
- package/src/notifications/topic-registry.ts +23 -9
- package/src/prompts/agents/executor.md +2 -2
- package/src/runtime-mcp/transports/stdio.ts +38 -4
- package/src/runtime-mcp/types.ts +7 -0
- package/src/sdk.ts +157 -10
- package/src/session/agent-session.ts +248 -125
- package/src/session/blob-store.ts +196 -8
- package/src/session/session-manager.ts +762 -12
- package/src/skc-runtime/launch-tmux.ts +85 -22
- package/src/skc-runtime/ledger-event-renderer.ts +1 -0
- package/src/skc-runtime/psmux-detect.ts +239 -0
- package/src/skc-runtime/ralplan-runtime.ts +2 -2
- package/src/skc-runtime/team-runtime.ts +56 -23
- package/src/skc-runtime/tmux-common.ts +88 -4
- package/src/skc-runtime/tmux-sessions.ts +111 -9
- package/src/skc-runtime/ultragoal-guard.ts +25 -8
- package/src/skc-runtime/ultragoal-runtime.ts +75 -15
- package/src/skc-runtime/workflow-manifest.generated.json +29 -0
- package/src/skc-runtime/workflow-manifest.ts +7 -2
- package/src/slash-commands/builtin-registry.ts +23 -3
- package/src/slash-commands/helpers/fast-status-report.ts +13 -3
- package/src/slash-commands/helpers/parse.ts +2 -1
- package/src/system-prompt.ts +9 -0
- package/src/task/executor.ts +31 -7
- package/src/task/index.ts +2 -0
- package/src/tools/ask.ts +5 -1
- package/src/tools/bash.ts +9 -0
- package/src/tools/composer-bash-policy.ts +96 -0
- package/src/tools/fetch.ts +94 -1
- package/src/tools/index.ts +6 -1
- package/src/tools/telegram-send.ts +137 -0
- package/src/utils/changelog.ts +8 -0
- package/src/web/insane/bridge.ts +350 -0
- package/src/web/insane/url-guard.ts +159 -0
- package/src/web/scrapers/types.ts +143 -45
- package/src/web/scrapers/utils.ts +70 -19
- package/src/web/search/provider.ts +77 -18
- package/src/web/search/providers/anthropic.ts +70 -3
- package/src/web/search/providers/codex.ts +1 -119
- package/src/web/search/providers/gemini.ts +99 -0
- package/src/web/search/providers/insane.ts +551 -0
- package/src/web/search/providers/openai-compatible.ts +66 -32
- package/src/web/search/providers/text-citations.ts +111 -0
- package/src/web/search/types.ts +13 -2
- package/vendor/insane-search/LICENSE +21 -0
- package/vendor/insane-search/MANIFEST.json +24 -0
- package/vendor/insane-search/engine/__init__.py +23 -0
- package/vendor/insane-search/engine/__main__.py +128 -0
- package/vendor/insane-search/engine/bias_check.py +183 -0
- package/vendor/insane-search/engine/executor.py +254 -0
- package/vendor/insane-search/engine/fetch_chain.py +725 -0
- package/vendor/insane-search/engine/learning.py +175 -0
- package/vendor/insane-search/engine/phase0.py +214 -0
- package/vendor/insane-search/engine/safety.py +91 -0
- package/vendor/insane-search/engine/templates/package.json +11 -0
- package/vendor/insane-search/engine/templates/playwright_mobile_chrome.js +188 -0
- package/vendor/insane-search/engine/templates/playwright_real_chrome.js +243 -0
- package/vendor/insane-search/engine/tests/test_hardening.py +57 -0
- package/vendor/insane-search/engine/tests/test_smoke.py +152 -0
- package/vendor/insane-search/engine/tests/test_u1.py +200 -0
- package/vendor/insane-search/engine/tests/test_u4.py +131 -0
- package/vendor/insane-search/engine/tests/test_u5.py +163 -0
- package/vendor/insane-search/engine/tests/test_u7.py +124 -0
- package/vendor/insane-search/engine/transport.py +211 -0
- package/vendor/insane-search/engine/url_transforms.py +98 -0
- package/vendor/insane-search/engine/validators.py +331 -0
- package/vendor/insane-search/engine/waf_detector.py +214 -0
- package/vendor/insane-search/engine/waf_profiles.yaml +162 -0
|
@@ -1,6 +1,43 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
SKC_PLUGIN_KIND,
|
|
3
|
+
SKC_SUBSKILL_PARENT_AGENTS,
|
|
4
|
+
type SkcPluginAgentAppendixManifestEntry,
|
|
5
|
+
type SkcPluginAppendixManifestEntry,
|
|
6
|
+
type SkcPluginHookManifestEntry,
|
|
7
|
+
SkcPluginLoadError,
|
|
8
|
+
type SkcPluginManifest,
|
|
9
|
+
type SkcPluginMcpManifestEntry,
|
|
10
|
+
type SkcPluginMcpTransport,
|
|
11
|
+
type SkcPluginToolManifestEntry,
|
|
12
|
+
type SkcSubskillParentAgent,
|
|
13
|
+
type SubskillFrontmatter,
|
|
14
|
+
} from "./types";
|
|
2
15
|
|
|
3
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Top-level surfaces that may never appear in a SKC plugin bundle: bundles may
|
|
18
|
+
* only EXTEND existing skills/agents, never register new top-level ones.
|
|
19
|
+
*/
|
|
20
|
+
const FORBIDDEN_MANIFEST_KEYS = ["skills", "slash-commands", "commands", "agents"];
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Ambiguous legacy aliases. `mcps` is the only canonical MCP key; these are
|
|
24
|
+
* rejected as `unsupported_surface` to avoid accidental legacy shape ambiguity.
|
|
25
|
+
*/
|
|
26
|
+
const UNSUPPORTED_ALIAS_KEYS = ["mcp", "mcpServers"];
|
|
27
|
+
|
|
28
|
+
const KNOWN_MANIFEST_KEYS = new Set([
|
|
29
|
+
"kind",
|
|
30
|
+
"name",
|
|
31
|
+
"version",
|
|
32
|
+
"subskills",
|
|
33
|
+
"tools",
|
|
34
|
+
"hooks",
|
|
35
|
+
"mcps",
|
|
36
|
+
"system_appendix",
|
|
37
|
+
"agent-appendix",
|
|
38
|
+
]);
|
|
39
|
+
|
|
40
|
+
const MCP_TRANSPORTS: readonly SkcPluginMcpTransport[] = ["stdio", "http", "sse"];
|
|
4
41
|
|
|
5
42
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
6
43
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -16,14 +53,194 @@ function requireNonEmptyString(value: unknown, field: string, filePath: string):
|
|
|
16
53
|
return value;
|
|
17
54
|
}
|
|
18
55
|
|
|
19
|
-
function
|
|
56
|
+
function manifestString(value: unknown, field: string, manifestPath: string): string {
|
|
57
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
58
|
+
throw new SkcPluginLoadError(
|
|
59
|
+
"invalid_manifest",
|
|
60
|
+
`Invalid SKC plugin manifest at ${manifestPath}: ${field} must be a non-empty string`,
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
return value;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function optionalStringArray(value: unknown, field: string, manifestPath: string): string[] {
|
|
67
|
+
if (value === undefined) return [];
|
|
20
68
|
if (!Array.isArray(value) || !value.every(item => typeof item === "string")) {
|
|
21
69
|
throw new SkcPluginLoadError(
|
|
22
70
|
"invalid_manifest",
|
|
23
71
|
`Invalid SKC plugin manifest at ${manifestPath}: ${field} must be a string array`,
|
|
24
72
|
);
|
|
25
73
|
}
|
|
26
|
-
return [...value];
|
|
74
|
+
return [...(value as string[])];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function optionalArray(value: unknown, field: string, manifestPath: string): unknown[] {
|
|
78
|
+
if (value === undefined) return [];
|
|
79
|
+
if (!Array.isArray(value)) {
|
|
80
|
+
throw new SkcPluginLoadError(
|
|
81
|
+
"invalid_manifest",
|
|
82
|
+
`Invalid SKC plugin manifest at ${manifestPath}: ${field} must be an array`,
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
return value;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function deriveToolName(toolPath: string): string {
|
|
89
|
+
const base = toolPath.split("/").pop() ?? toolPath;
|
|
90
|
+
return base.replace(/\.[^.]+$/, "");
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function parseTools(value: unknown, manifestPath: string): SkcPluginToolManifestEntry[] {
|
|
94
|
+
const raw = optionalArray(value, "tools", manifestPath);
|
|
95
|
+
return raw.map((entry, index) => {
|
|
96
|
+
// Legacy string shorthand: subskill-scoped tool path only.
|
|
97
|
+
if (typeof entry === "string") {
|
|
98
|
+
if (entry.trim().length === 0) {
|
|
99
|
+
throw new SkcPluginLoadError(
|
|
100
|
+
"invalid_manifest",
|
|
101
|
+
`Invalid SKC plugin manifest at ${manifestPath}: tools[${index}] must be a non-empty path`,
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
return { name: deriveToolName(entry), path: entry, surface: "subskill" };
|
|
105
|
+
}
|
|
106
|
+
if (!isRecord(entry)) {
|
|
107
|
+
throw new SkcPluginLoadError(
|
|
108
|
+
"invalid_manifest",
|
|
109
|
+
`Invalid SKC plugin manifest at ${manifestPath}: tools[${index}] must be a string or object`,
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
const name = manifestString(entry.name, `tools[${index}].name`, manifestPath);
|
|
113
|
+
const path = manifestString(entry.path, `tools[${index}].path`, manifestPath);
|
|
114
|
+
const description =
|
|
115
|
+
entry.description === undefined
|
|
116
|
+
? undefined
|
|
117
|
+
: manifestString(entry.description, `tools[${index}].description`, manifestPath);
|
|
118
|
+
const sha256 =
|
|
119
|
+
entry.sha256 === undefined ? undefined : manifestString(entry.sha256, `tools[${index}].sha256`, manifestPath);
|
|
120
|
+
return { name, path, description, sha256, surface: "always-on" };
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function parseHooks(value: unknown, manifestPath: string): SkcPluginHookManifestEntry[] {
|
|
125
|
+
const raw = optionalArray(value, "hooks", manifestPath);
|
|
126
|
+
return raw.map((entry, index) => {
|
|
127
|
+
if (!isRecord(entry)) {
|
|
128
|
+
throw new SkcPluginLoadError(
|
|
129
|
+
"invalid_manifest",
|
|
130
|
+
`Invalid SKC plugin manifest at ${manifestPath}: hooks[${index}] must be an object`,
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
const name = manifestString(entry.name, `hooks[${index}].name`, manifestPath);
|
|
134
|
+
const event = manifestString(entry.event, `hooks[${index}].event`, manifestPath);
|
|
135
|
+
const path = manifestString(entry.path, `hooks[${index}].path`, manifestPath);
|
|
136
|
+
const target =
|
|
137
|
+
entry.target === undefined ? undefined : manifestString(entry.target, `hooks[${index}].target`, manifestPath);
|
|
138
|
+
let phase: "before" | "after" | undefined;
|
|
139
|
+
if (entry.phase !== undefined) {
|
|
140
|
+
if (entry.phase !== "before" && entry.phase !== "after") {
|
|
141
|
+
throw new SkcPluginLoadError(
|
|
142
|
+
"invalid_manifest",
|
|
143
|
+
`Invalid SKC plugin manifest at ${manifestPath}: hooks[${index}].phase must be "before" or "after"`,
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
phase = entry.phase;
|
|
147
|
+
}
|
|
148
|
+
const sha256 =
|
|
149
|
+
entry.sha256 === undefined ? undefined : manifestString(entry.sha256, `hooks[${index}].sha256`, manifestPath);
|
|
150
|
+
return { name, event, target, phase, path, sha256 };
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function parseMcps(value: unknown, manifestPath: string): SkcPluginMcpManifestEntry[] {
|
|
155
|
+
const raw = optionalArray(value, "mcps", manifestPath);
|
|
156
|
+
return raw.map((entry, index) => {
|
|
157
|
+
if (!isRecord(entry)) {
|
|
158
|
+
throw new SkcPluginLoadError(
|
|
159
|
+
"invalid_manifest",
|
|
160
|
+
`Invalid SKC plugin manifest at ${manifestPath}: mcps[${index}] must be an object`,
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
const name = manifestString(entry.name, `mcps[${index}].name`, manifestPath);
|
|
164
|
+
const transport = entry.transport;
|
|
165
|
+
if (typeof transport !== "string" || !MCP_TRANSPORTS.includes(transport as SkcPluginMcpTransport)) {
|
|
166
|
+
throw new SkcPluginLoadError(
|
|
167
|
+
"invalid_manifest",
|
|
168
|
+
`Invalid SKC plugin manifest at ${manifestPath}: mcps[${index}].transport must be one of ${MCP_TRANSPORTS.join(", ")}`,
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
const command =
|
|
172
|
+
entry.command === undefined
|
|
173
|
+
? undefined
|
|
174
|
+
: manifestString(entry.command, `mcps[${index}].command`, manifestPath);
|
|
175
|
+
const url = entry.url === undefined ? undefined : manifestString(entry.url, `mcps[${index}].url`, manifestPath);
|
|
176
|
+
const cwd = entry.cwd === undefined ? undefined : manifestString(entry.cwd, `mcps[${index}].cwd`, manifestPath);
|
|
177
|
+
let args: string[] | undefined;
|
|
178
|
+
if (entry.args !== undefined) {
|
|
179
|
+
if (!Array.isArray(entry.args) || !entry.args.every(item => typeof item === "string")) {
|
|
180
|
+
throw new SkcPluginLoadError(
|
|
181
|
+
"invalid_manifest",
|
|
182
|
+
`Invalid SKC plugin manifest at ${manifestPath}: mcps[${index}].args must be a string array`,
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
args = [...(entry.args as string[])];
|
|
186
|
+
}
|
|
187
|
+
let headers: Record<string, string> | undefined;
|
|
188
|
+
if (entry.headers !== undefined) {
|
|
189
|
+
if (!isRecord(entry.headers) || !Object.values(entry.headers).every(v => typeof v === "string")) {
|
|
190
|
+
throw new SkcPluginLoadError(
|
|
191
|
+
"invalid_manifest",
|
|
192
|
+
`Invalid SKC plugin manifest at ${manifestPath}: mcps[${index}].headers must be a string map`,
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
headers = { ...(entry.headers as Record<string, string>) };
|
|
196
|
+
}
|
|
197
|
+
const sha256 =
|
|
198
|
+
entry.sha256 === undefined ? undefined : manifestString(entry.sha256, `mcps[${index}].sha256`, manifestPath);
|
|
199
|
+
return { name, transport: transport as SkcPluginMcpTransport, command, args, cwd, url, headers, sha256 };
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function parseAppendixEntry(entry: unknown, field: string, manifestPath: string): SkcPluginAppendixManifestEntry {
|
|
204
|
+
if (!isRecord(entry)) {
|
|
205
|
+
throw new SkcPluginLoadError(
|
|
206
|
+
"invalid_manifest",
|
|
207
|
+
`Invalid SKC plugin manifest at ${manifestPath}: ${field} must be an object`,
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
const name = manifestString(entry.name, `${field}.name`, manifestPath);
|
|
211
|
+
const path = entry.path === undefined ? undefined : manifestString(entry.path, `${field}.path`, manifestPath);
|
|
212
|
+
// Content may be empty/whitespace here; the compiler enforces non-empty and
|
|
213
|
+
// maps emptiness to invalid_appendix (not invalid_manifest).
|
|
214
|
+
if (entry.content !== undefined && typeof entry.content !== "string") {
|
|
215
|
+
throw new SkcPluginLoadError(
|
|
216
|
+
"invalid_manifest",
|
|
217
|
+
`Invalid SKC plugin manifest at ${manifestPath}: ${field}.content must be a string`,
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
const content = entry.content as string | undefined;
|
|
221
|
+
const sha256 =
|
|
222
|
+
entry.sha256 === undefined ? undefined : manifestString(entry.sha256, `${field}.sha256`, manifestPath);
|
|
223
|
+
return { name, path, content, sha256 };
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function parseSystemAppendix(value: unknown, manifestPath: string): SkcPluginAppendixManifestEntry[] {
|
|
227
|
+
const raw = optionalArray(value, "system_appendix", manifestPath);
|
|
228
|
+
return raw.map((entry, index) => parseAppendixEntry(entry, `system_appendix[${index}]`, manifestPath));
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function parseAgentAppendix(value: unknown, manifestPath: string): SkcPluginAgentAppendixManifestEntry[] {
|
|
232
|
+
const raw = optionalArray(value, "agent-appendix", manifestPath);
|
|
233
|
+
return raw.map((entry, index) => {
|
|
234
|
+
const base = parseAppendixEntry(entry, `agent-appendix[${index}]`, manifestPath);
|
|
235
|
+
const agent = (entry as Record<string, unknown>).agent;
|
|
236
|
+
if (typeof agent !== "string" || !SKC_SUBSKILL_PARENT_AGENTS.includes(agent as SkcSubskillParentAgent)) {
|
|
237
|
+
throw new SkcPluginLoadError(
|
|
238
|
+
"invalid_parent",
|
|
239
|
+
`Invalid SKC plugin manifest at ${manifestPath}: agent-appendix[${index}].agent must be one of ${SKC_SUBSKILL_PARENT_AGENTS.join(", ")}`,
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
return { ...base, agent: agent as SkcSubskillParentAgent };
|
|
243
|
+
});
|
|
27
244
|
}
|
|
28
245
|
|
|
29
246
|
export function parseManifest(raw: unknown, manifestPath: string): SkcPluginManifest {
|
|
@@ -40,31 +257,44 @@ export function parseManifest(raw: unknown, manifestPath: string): SkcPluginMani
|
|
|
40
257
|
}
|
|
41
258
|
}
|
|
42
259
|
|
|
260
|
+
for (const key of UNSUPPORTED_ALIAS_KEYS) {
|
|
261
|
+
if (Object.hasOwn(raw, key)) {
|
|
262
|
+
throw new SkcPluginLoadError(
|
|
263
|
+
"unsupported_surface",
|
|
264
|
+
`Unsupported SKC plugin surface in ${manifestPath}: ${key} (use the canonical "mcps" key)`,
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
for (const key of Object.keys(raw)) {
|
|
270
|
+
if (!KNOWN_MANIFEST_KEYS.has(key)) {
|
|
271
|
+
throw new SkcPluginLoadError(
|
|
272
|
+
"unsupported_surface",
|
|
273
|
+
`Unsupported SKC plugin surface in ${manifestPath}: ${key}`,
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
43
278
|
if (raw.kind !== SKC_PLUGIN_KIND) {
|
|
44
279
|
throw new SkcPluginLoadError(
|
|
45
280
|
"invalid_kind",
|
|
46
281
|
`Invalid SKC plugin kind in ${manifestPath}: expected ${SKC_PLUGIN_KIND}`,
|
|
47
282
|
);
|
|
48
283
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
`Invalid SKC plugin manifest at ${manifestPath}: name must be a non-empty string`,
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
if (typeof raw.version !== "string" || raw.version.trim().length === 0) {
|
|
56
|
-
throw new SkcPluginLoadError(
|
|
57
|
-
"invalid_manifest",
|
|
58
|
-
`Invalid SKC plugin manifest at ${manifestPath}: version must be a non-empty string`,
|
|
59
|
-
);
|
|
60
|
-
}
|
|
284
|
+
|
|
285
|
+
const name = manifestString(raw.name, "name", manifestPath);
|
|
286
|
+
const version = manifestString(raw.version, "version", manifestPath);
|
|
61
287
|
|
|
62
288
|
return {
|
|
63
|
-
name
|
|
64
|
-
version
|
|
289
|
+
name,
|
|
290
|
+
version,
|
|
65
291
|
kind: SKC_PLUGIN_KIND,
|
|
66
|
-
subskills:
|
|
67
|
-
tools:
|
|
292
|
+
subskills: optionalStringArray(raw.subskills, "subskills", manifestPath),
|
|
293
|
+
tools: parseTools(raw.tools, manifestPath),
|
|
294
|
+
hooks: parseHooks(raw.hooks, manifestPath),
|
|
295
|
+
mcps: parseMcps(raw.mcps, manifestPath),
|
|
296
|
+
systemAppendix: parseSystemAppendix(raw.system_appendix, manifestPath),
|
|
297
|
+
agentAppendix: parseAgentAppendix(raw["agent-appendix"], manifestPath),
|
|
68
298
|
};
|
|
69
299
|
}
|
|
70
300
|
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import * as fs from "node:fs/promises";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import type { SkcPluginLoadErrorCode, SkcPluginRegistryEntry } from "./types";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Session-start validation: the registry is the collision authority. Capability
|
|
8
|
+
* provider output is supplied as EVIDENCE only; plugin surfaces are never
|
|
9
|
+
* resolved by capability first-wins. Offending surfaces are quarantined
|
|
10
|
+
* fail-closed with a stable error code rather than silently shadowed.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export interface SessionCapabilityEvidence {
|
|
14
|
+
/** Built-in + provider tool names already present before plugin insertion. */
|
|
15
|
+
toolNames?: Iterable<string>;
|
|
16
|
+
/** Non-plugin MCP server names from providers/built-ins. */
|
|
17
|
+
mcpNames?: Iterable<string>;
|
|
18
|
+
/** Non-plugin hook keys (event:phase:target:name) from providers/built-ins. */
|
|
19
|
+
hookKeys?: Iterable<string>;
|
|
20
|
+
/** Existing appendix extension ids already present. */
|
|
21
|
+
appendixIds?: Iterable<string>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface SessionQuarantine {
|
|
25
|
+
plugin: string;
|
|
26
|
+
surfaceId: string;
|
|
27
|
+
code: SkcPluginLoadErrorCode;
|
|
28
|
+
message: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface SessionValidationResult {
|
|
32
|
+
/** Registry entries (enabled, non-quarantined) whose surfaces may activate. */
|
|
33
|
+
active: SkcPluginRegistryEntry[];
|
|
34
|
+
/** Per-surface quarantine records (fail-closed). */
|
|
35
|
+
quarantine: SessionQuarantine[];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function sha256(buf: Buffer): string {
|
|
39
|
+
return createHash("sha256").update(buf).digest("hex");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Re-verify that the installed files still match the registry's recorded hashes.
|
|
44
|
+
* Drift (manual edits, partial writes) quarantines the whole plugin with
|
|
45
|
+
* runtime_mismatch.
|
|
46
|
+
*/
|
|
47
|
+
export async function verifyEntryHashes(entry: SkcPluginRegistryEntry): Promise<SessionQuarantine | null> {
|
|
48
|
+
for (const file of entry.copiedFiles) {
|
|
49
|
+
const abs = path.join(entry.pluginRoot, file.relativePath);
|
|
50
|
+
let buf: Buffer;
|
|
51
|
+
try {
|
|
52
|
+
buf = await fs.readFile(abs);
|
|
53
|
+
} catch {
|
|
54
|
+
return {
|
|
55
|
+
plugin: entry.name,
|
|
56
|
+
surfaceId: `plugin:${entry.name}`,
|
|
57
|
+
code: "runtime_mismatch",
|
|
58
|
+
message: `Installed file missing: ${file.relativePath}`,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
if (sha256(buf) !== file.sha256) {
|
|
62
|
+
return {
|
|
63
|
+
plugin: entry.name,
|
|
64
|
+
surfaceId: `plugin:${entry.name}`,
|
|
65
|
+
code: "runtime_mismatch",
|
|
66
|
+
message: `Installed file hash drift: ${file.relativePath}`,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function activeSurfaceIds(entry: SkcPluginRegistryEntry): {
|
|
74
|
+
tools: { id: string; name: string }[];
|
|
75
|
+
mcps: { id: string; name: string }[];
|
|
76
|
+
hooks: { id: string }[];
|
|
77
|
+
appendices: { id: string }[];
|
|
78
|
+
} {
|
|
79
|
+
const disabled = new Set(entry.disabledSurfaceIds);
|
|
80
|
+
return {
|
|
81
|
+
tools: entry.surfaces.tools
|
|
82
|
+
.filter(t => !disabled.has(t.extensionId))
|
|
83
|
+
.map(t => ({ id: t.extensionId, name: t.name })),
|
|
84
|
+
mcps: entry.surfaces.mcps
|
|
85
|
+
.filter(m => !disabled.has(m.extensionId))
|
|
86
|
+
.map(m => ({ id: m.extensionId, name: m.name })),
|
|
87
|
+
hooks: entry.surfaces.hooks.filter(h => !disabled.has(h.extensionId)).map(h => ({ id: h.extensionId })),
|
|
88
|
+
appendices: [...entry.surfaces.systemAppendices, ...entry.surfaces.agentAppendices]
|
|
89
|
+
.filter(a => !disabled.has(a.extensionId))
|
|
90
|
+
.map(a => ({ id: a.extensionId })),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Validate the effective installed registry against the current capability
|
|
96
|
+
* universe (evidence) and across plugins. Returns the entries that may activate
|
|
97
|
+
* plus per-surface quarantine records. Disabled entries/surfaces are skipped
|
|
98
|
+
* (not an error).
|
|
99
|
+
*/
|
|
100
|
+
export function validateSessionBundles(
|
|
101
|
+
entries: readonly SkcPluginRegistryEntry[],
|
|
102
|
+
evidence: SessionCapabilityEvidence = {},
|
|
103
|
+
preQuarantined: readonly SessionQuarantine[] = [],
|
|
104
|
+
): SessionValidationResult {
|
|
105
|
+
const quarantine: SessionQuarantine[] = [...preQuarantined];
|
|
106
|
+
const quarantinedPlugins = new Set(preQuarantined.map(q => q.plugin));
|
|
107
|
+
|
|
108
|
+
const seenTools = new Set<string>(evidence.toolNames ?? []);
|
|
109
|
+
const seenMcps = new Set<string>(evidence.mcpNames ?? []);
|
|
110
|
+
const seenHooks = new Set<string>(evidence.hookKeys ?? []);
|
|
111
|
+
const seenAppendices = new Set<string>(evidence.appendixIds ?? []);
|
|
112
|
+
|
|
113
|
+
const active: SkcPluginRegistryEntry[] = [];
|
|
114
|
+
for (const entry of entries) {
|
|
115
|
+
if (!entry.enabled) continue; // user-disabled, not an error
|
|
116
|
+
if (quarantinedPlugins.has(entry.name)) continue;
|
|
117
|
+
const surfaces = activeSurfaceIds(entry);
|
|
118
|
+
let collided = false;
|
|
119
|
+
const recordCollision = (surfaceId: string, what: string): void => {
|
|
120
|
+
collided = true;
|
|
121
|
+
quarantine.push({
|
|
122
|
+
plugin: entry.name,
|
|
123
|
+
surfaceId,
|
|
124
|
+
code: "session_collision",
|
|
125
|
+
message: `${what} collides with an existing capability/plugin; fail-closed (no shadowing)`,
|
|
126
|
+
});
|
|
127
|
+
};
|
|
128
|
+
for (const t of surfaces.tools) if (seenTools.has(t.name)) recordCollision(t.id, `tool "${t.name}"`);
|
|
129
|
+
for (const m of surfaces.mcps) if (seenMcps.has(m.name)) recordCollision(m.id, `mcp "${m.name}"`);
|
|
130
|
+
for (const h of surfaces.hooks) if (seenHooks.has(h.id)) recordCollision(h.id, `hook "${h.id}"`);
|
|
131
|
+
for (const a of surfaces.appendices) if (seenAppendices.has(a.id)) recordCollision(a.id, `appendix "${a.id}"`);
|
|
132
|
+
|
|
133
|
+
if (collided) {
|
|
134
|
+
// Fail-closed for the whole plugin entry so partial activation cannot
|
|
135
|
+
// leave a half-applied bundle.
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
// Reserve this plugin's surfaces so a later plugin colliding with it is
|
|
139
|
+
// also caught (deterministic order => earlier plugin wins reservation).
|
|
140
|
+
for (const t of surfaces.tools) seenTools.add(t.name);
|
|
141
|
+
for (const m of surfaces.mcps) seenMcps.add(m.name);
|
|
142
|
+
for (const h of surfaces.hooks) seenHooks.add(h.id);
|
|
143
|
+
for (const a of surfaces.appendices) seenAppendices.add(a.id);
|
|
144
|
+
active.push(entry);
|
|
145
|
+
}
|
|
146
|
+
return { active, quarantine };
|
|
147
|
+
}
|
|
@@ -19,12 +19,62 @@ export const SKC_AGENT_SUBSKILL_PHASES: Record<SkcSubskillParentAgent, string[]>
|
|
|
19
19
|
critic: ["prompt"],
|
|
20
20
|
};
|
|
21
21
|
|
|
22
|
+
export interface SkcPluginToolManifestEntry {
|
|
23
|
+
name: string;
|
|
24
|
+
path: string;
|
|
25
|
+
description?: string;
|
|
26
|
+
sha256?: string;
|
|
27
|
+
/**
|
|
28
|
+
* "always-on" object entries are activated for the whole session; legacy
|
|
29
|
+
* string shorthand stays "subskill"-scoped and is only attached to subskill
|
|
30
|
+
* bindings (never registered as an always-on tool surface).
|
|
31
|
+
*/
|
|
32
|
+
surface: "subskill" | "always-on";
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface SkcPluginHookManifestEntry {
|
|
36
|
+
name: string;
|
|
37
|
+
event: string;
|
|
38
|
+
target?: string;
|
|
39
|
+
phase?: "before" | "after";
|
|
40
|
+
path: string;
|
|
41
|
+
sha256?: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export type SkcPluginMcpTransport = "stdio" | "http" | "sse";
|
|
45
|
+
|
|
46
|
+
export interface SkcPluginMcpManifestEntry {
|
|
47
|
+
name: string;
|
|
48
|
+
transport: SkcPluginMcpTransport;
|
|
49
|
+
command?: string;
|
|
50
|
+
args?: string[];
|
|
51
|
+
cwd?: string;
|
|
52
|
+
url?: string;
|
|
53
|
+
headers?: Record<string, string>;
|
|
54
|
+
sha256?: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface SkcPluginAppendixManifestEntry {
|
|
58
|
+
name: string;
|
|
59
|
+
path?: string;
|
|
60
|
+
content?: string;
|
|
61
|
+
sha256?: string;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface SkcPluginAgentAppendixManifestEntry extends SkcPluginAppendixManifestEntry {
|
|
65
|
+
agent: SkcSubskillParentAgent;
|
|
66
|
+
}
|
|
67
|
+
|
|
22
68
|
export interface SkcPluginManifest {
|
|
23
69
|
name: string;
|
|
24
70
|
version: string;
|
|
25
71
|
kind: "sayknow-cli-plugin";
|
|
26
72
|
subskills: string[];
|
|
27
|
-
tools:
|
|
73
|
+
tools: SkcPluginToolManifestEntry[];
|
|
74
|
+
hooks: SkcPluginHookManifestEntry[];
|
|
75
|
+
mcps: SkcPluginMcpManifestEntry[];
|
|
76
|
+
systemAppendix: SkcPluginAppendixManifestEntry[];
|
|
77
|
+
agentAppendix: SkcPluginAgentAppendixManifestEntry[];
|
|
28
78
|
}
|
|
29
79
|
|
|
30
80
|
export interface SubskillFrontmatter {
|
|
@@ -76,15 +126,33 @@ export interface LoadedSkcPlugin {
|
|
|
76
126
|
}
|
|
77
127
|
|
|
78
128
|
export type SkcPluginLoadErrorCode =
|
|
129
|
+
// Parse-time
|
|
79
130
|
| "forbidden_surface"
|
|
80
131
|
| "invalid_manifest"
|
|
132
|
+
| "invalid_kind"
|
|
133
|
+
| "unsupported_surface"
|
|
134
|
+
// Compile-time
|
|
81
135
|
| "invalid_frontmatter"
|
|
82
136
|
| "invalid_parent"
|
|
83
137
|
| "invalid_phase"
|
|
138
|
+
| "missing_file"
|
|
139
|
+
| "hash_mismatch"
|
|
140
|
+
| "invalid_appendix"
|
|
141
|
+
| "invalid_hook"
|
|
142
|
+
| "invalid_mcp"
|
|
143
|
+
// Install-time
|
|
84
144
|
| "duplicate_arg"
|
|
85
145
|
| "duplicate_parent_phase"
|
|
86
|
-
| "
|
|
87
|
-
| "
|
|
146
|
+
| "duplicate_tool"
|
|
147
|
+
| "duplicate_hook"
|
|
148
|
+
| "duplicate_mcp"
|
|
149
|
+
| "duplicate_appendix"
|
|
150
|
+
| "security_policy"
|
|
151
|
+
| "install_conflict"
|
|
152
|
+
// Session-start / runtime
|
|
153
|
+
| "session_collision"
|
|
154
|
+
| "runtime_mismatch"
|
|
155
|
+
| "quarantined_surface";
|
|
88
156
|
|
|
89
157
|
export class SkcPluginLoadError extends Error {
|
|
90
158
|
readonly code: SkcPluginLoadErrorCode;
|
|
@@ -95,3 +163,131 @@ export class SkcPluginLoadError extends Error {
|
|
|
95
163
|
this.code = code;
|
|
96
164
|
}
|
|
97
165
|
}
|
|
166
|
+
|
|
167
|
+
export type SkcPluginScope = "user" | "project";
|
|
168
|
+
|
|
169
|
+
export type SkcPluginSourceKind = "path" | "git" | "tarball";
|
|
170
|
+
|
|
171
|
+
export interface SkcPluginCopiedFile {
|
|
172
|
+
relativePath: string;
|
|
173
|
+
sha256: string;
|
|
174
|
+
bytes: number;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export interface NormalizedSubskillSurface {
|
|
178
|
+
extensionId: string;
|
|
179
|
+
name: string;
|
|
180
|
+
description: string;
|
|
181
|
+
parent: string;
|
|
182
|
+
phase: string;
|
|
183
|
+
activationArg: string;
|
|
184
|
+
relativePath: string;
|
|
185
|
+
sha256: string;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export interface NormalizedToolSurface {
|
|
189
|
+
extensionId: string;
|
|
190
|
+
name: string;
|
|
191
|
+
relativePath: string;
|
|
192
|
+
sha256: string;
|
|
193
|
+
description?: string;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export interface NormalizedHookSurface {
|
|
197
|
+
extensionId: string;
|
|
198
|
+
name: string;
|
|
199
|
+
event: string;
|
|
200
|
+
target?: string;
|
|
201
|
+
phase?: "before" | "after";
|
|
202
|
+
relativePath: string;
|
|
203
|
+
sha256: string;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export interface NormalizedMcpSurface {
|
|
207
|
+
extensionId: string;
|
|
208
|
+
name: string;
|
|
209
|
+
transport: SkcPluginMcpTransport;
|
|
210
|
+
configHash: string;
|
|
211
|
+
config: SkcPluginMcpManifestEntry;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export interface NormalizedAppendixSurface {
|
|
215
|
+
extensionId: string;
|
|
216
|
+
name: string;
|
|
217
|
+
relativePath?: string;
|
|
218
|
+
/** Inline appendix body (when the manifest used `content` instead of `path`). */
|
|
219
|
+
content?: string;
|
|
220
|
+
contentHash: string;
|
|
221
|
+
bytes: number;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export interface NormalizedAgentAppendixSurface extends NormalizedAppendixSurface {
|
|
225
|
+
agent: SkcSubskillParentAgent;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export interface NormalizedSkcPluginSurfaces {
|
|
229
|
+
subskills: NormalizedSubskillSurface[];
|
|
230
|
+
tools: NormalizedToolSurface[];
|
|
231
|
+
hooks: NormalizedHookSurface[];
|
|
232
|
+
mcps: NormalizedMcpSurface[];
|
|
233
|
+
systemAppendices: NormalizedAppendixSurface[];
|
|
234
|
+
agentAppendices: NormalizedAgentAppendixSurface[];
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Result of the pure compile step. Computed from manifest, frontmatter, and
|
|
239
|
+
* declared files read as bytes only — never by importing plugin code.
|
|
240
|
+
*/
|
|
241
|
+
export interface NormalizedSkcPluginBundle {
|
|
242
|
+
name: string;
|
|
243
|
+
version: string;
|
|
244
|
+
root: string;
|
|
245
|
+
manifestPath: string;
|
|
246
|
+
manifestHash: string;
|
|
247
|
+
surfaces: NormalizedSkcPluginSurfaces;
|
|
248
|
+
files: SkcPluginCopiedFile[];
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export interface SkcPluginQuarantineEntry {
|
|
252
|
+
surfaceId: string;
|
|
253
|
+
code: SkcPluginLoadErrorCode;
|
|
254
|
+
message: string;
|
|
255
|
+
detectedAt: string;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export interface SkcPluginRegistrySource {
|
|
259
|
+
kind: SkcPluginSourceKind;
|
|
260
|
+
uri: string;
|
|
261
|
+
ref?: string;
|
|
262
|
+
sha?: string;
|
|
263
|
+
resolvedAt: string;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export interface SkcPluginRegistryEntry {
|
|
267
|
+
name: string;
|
|
268
|
+
version: string;
|
|
269
|
+
scope: SkcPluginScope;
|
|
270
|
+
enabled: boolean;
|
|
271
|
+
pluginRoot: string;
|
|
272
|
+
manifestPath: string;
|
|
273
|
+
manifestHash: string;
|
|
274
|
+
source: SkcPluginRegistrySource;
|
|
275
|
+
installedAt: string;
|
|
276
|
+
updatedAt: string;
|
|
277
|
+
copiedFiles: SkcPluginCopiedFile[];
|
|
278
|
+
surfaces: NormalizedSkcPluginSurfaces;
|
|
279
|
+
disabledSurfaceIds: string[];
|
|
280
|
+
quarantine?: SkcPluginQuarantineEntry[];
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export interface SkcPluginRegistry {
|
|
284
|
+
version: 1;
|
|
285
|
+
scope: SkcPluginScope;
|
|
286
|
+
plugins: SkcPluginRegistryEntry[];
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Stable identifiers for plugin-contributed surfaces used by observability,
|
|
291
|
+
* disabledSurfaceIds, and quarantine bookkeeping.
|
|
292
|
+
*/
|
|
293
|
+
export type SkcPluginSurfaceExtensionId = string;
|