@h-rig/core 0.0.6-alpha.155 → 0.0.6-alpha.157

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.
@@ -23,7 +23,7 @@ function applyConfigDefaults(raw) {
23
23
  const record = raw;
24
24
  return {
25
25
  ...record,
26
- plugins: Array.isArray(record.plugins) ? record.plugins : [],
26
+ plugins: Array.isArray(record.plugins) ? record.plugins.flat() : [],
27
27
  workspace: normalizeWorkspaceConfig(record.workspace)
28
28
  };
29
29
  }
@@ -1,5 +1,5 @@
1
- import type { BlockerClassifierRegistration, CliCommandRegistration, HookRegistration, PanelRegistration, ProductCapabilityRegistration, RepoSourceRegistration, AgentRoleRegistration, SkillRegistration, Stage, StageMutation, StageRun, TaskFieldExtension, TaskSourceRegistration, ValidatorRegistration } from "@rig/contracts";
2
- import type { RegisteredValidator, RigPluginWithRuntime, RuntimeBlockerClassifier, RuntimeCliCommand, RuntimeFeatureCapability, RuntimePanelProducer, TaskSourceFactoryEntry } from "./plugin-runtime";
1
+ import type { AgentRoleRegistration, BlockerClassifierRegistration, CliCommandRegistration, HookRegistration, PanelRegistration, ProductCapabilityRegistration, RepoSourceRegistration, SkillRegistration, Stage, StageMutation, StageRun, TaskFieldExtension, TaskSourceRegistration, ValidatorRegistration } from "@rig/contracts";
2
+ import type { BlockerClassifierEntry, CliCommand, FeatureCapability, Panel, RigPlugin, SessionExtensionEntry, TaskSource, Validator } from "./plugin-runtime";
3
3
  export interface PluginHost {
4
4
  getValidator(id: string): ValidatorRegistration | undefined;
5
5
  getHook(id: string): HookRegistration | undefined;
@@ -32,34 +32,41 @@ export interface PluginHost {
32
32
  */
33
33
  listStageMutations(): readonly StageMutation[];
34
34
  /**
35
- * Executable stage implementations contributed by plugins (`runtime.stages`),
36
- * merged by stage id. The kernel's stage runner uses these to execute
37
- * plugin-contributed stages inserted via stageMutations.
35
+ * Executable stage implementations contributed by plugins, derived from
36
+ * `contributes.stages` entries that carry a `run`, keyed by stage id. The
37
+ * kernel's stage runner uses these to execute plugin-contributed stages
38
+ * inserted via stageMutations.
38
39
  */
39
40
  listStageExecutors(): Readonly<Record<string, StageRun>>;
40
41
  /**
41
- * Executable validators contributed by plugins. Only validators whose
42
- * metadata appears in `listValidators()` and whose plugin shipped a runtime
43
- * implementation via `definePlugin(meta, { validators: [...] })` appear here.
44
- * Validators registered metadata-only (no run() implementation) are absent.
42
+ * Executable validators contributed by plugins (single-channel: the same
43
+ * `contributes.validators` entries, which carry their `run`).
45
44
  */
46
- listExecutableValidators(): readonly RegisteredValidator[];
45
+ listExecutableValidators(): readonly Validator[];
47
46
  /**
48
47
  * Executable task source factories contributed by plugins. Used by the
49
48
  * runtime's `buildTaskSourceRegistry` to instantiate non-standard task
50
49
  * source kinds (Linear, Jira, custom) declared by plugins.
51
50
  */
52
- listExecutableTaskSources(): readonly TaskSourceFactoryEntry[];
53
- listExecutableCapabilities(): readonly RuntimeFeatureCapability[];
54
- listExecutablePanels(): readonly RuntimePanelProducer[];
55
- listExecutableBlockerClassifiers(): readonly RuntimeBlockerClassifier[];
56
- listExecutableCliCommands(): readonly RuntimeCliCommand[];
57
- resolveExecutableCliCommand(requested: string): RuntimeCliCommand | undefined;
51
+ listExecutableTaskSources(): readonly TaskSource[];
52
+ listExecutableCapabilities(): readonly FeatureCapability[];
53
+ listExecutablePanels(): readonly Panel[];
54
+ listExecutableBlockerClassifiers(): readonly BlockerClassifierEntry[];
55
+ listExecutableCliCommands(): readonly CliCommand[];
56
+ /**
57
+ * Executable session-extension installers contributed by plugins. The
58
+ * agent-session host (rig-host) resolves these and invokes each `install(api)`
59
+ * at `createAgentSession` time, so the run lifecycle (and any other
60
+ * session-time extension) is resolved from the explicit plugin collection
61
+ * rather than injected directly by a leaf.
62
+ */
63
+ listSessionExtensions(): readonly SessionExtensionEntry[];
64
+ resolveExecutableCliCommand(requested: string): CliCommand | undefined;
58
65
  /**
59
66
  * Look up an executable task source factory by its `kind` (the value in
60
67
  * `config.taskSource.kind`). Returns undefined when no plugin provides
61
68
  * a factory for that kind.
62
69
  */
63
- resolveTaskSourceFactoryByKind(kind: string): TaskSourceFactoryEntry | undefined;
70
+ resolveTaskSourceFactoryByKind(kind: string): TaskSource | undefined;
64
71
  }
65
- export declare function createPluginHost(plugins: readonly RigPluginWithRuntime[]): PluginHost;
72
+ export declare function createPluginHost(plugins: readonly RigPlugin[]): PluginHost;
@@ -21,102 +21,8 @@ function assertUniquePluginNames(plugins) {
21
21
  seen.add(plugin.name);
22
22
  }
23
23
  }
24
- function assertRuntimeMatchesMetadata(plugin) {
25
- const declaredValidators = new Map((plugin.contributes?.validators ?? []).map((validator) => [validator.id, validator]));
26
- const runtimeValidators = new Map((plugin?.validators ?? []).map((validator) => [validator.id, validator]));
27
- for (const validator of runtimeValidators.values()) {
28
- const metadata = declaredValidators.get(validator.id);
29
- if (!metadata) {
30
- throw new Error(`plugin "${plugin.name}" executable validator "${validator.id}" has no matching metadata entry in contributes.validators`);
31
- }
32
- if (metadata.category !== validator.category) {
33
- throw new Error(`plugin "${plugin.name}" executable validator "${validator.id}" category "${validator.category}" does not match metadata category "${metadata.category}"`);
34
- }
35
- }
36
- if (plugin?.validators) {
37
- for (const validator of declaredValidators.values()) {
38
- if (!runtimeValidators.has(validator.id)) {
39
- throw new Error(`plugin "${plugin.name}" validator metadata "${validator.id}" has no runtime implementation`);
40
- }
41
- }
42
- }
43
- const declaredTaskSources = new Map((plugin.contributes?.taskSources ?? []).map((source) => [source.id, source]));
44
- const runtimeTaskSources = new Map((plugin?.taskSources ?? []).map((source) => [source.id, source]));
45
- for (const source of runtimeTaskSources.values()) {
46
- const metadata = declaredTaskSources.get(source.id);
47
- if (!metadata) {
48
- throw new Error(`plugin "${plugin.name}" executable task source "${source.id}" has no matching metadata entry in contributes.taskSources`);
49
- }
50
- if (metadata.kind !== source.kind) {
51
- throw new Error(`plugin "${plugin.name}" executable task source "${source.id}" kind "${source.kind}" does not match metadata kind "${metadata.kind}"`);
52
- }
53
- }
54
- if (plugin?.taskSources) {
55
- for (const source of declaredTaskSources.values()) {
56
- if (!runtimeTaskSources.has(source.id)) {
57
- throw new Error(`plugin "${plugin.name}" task source metadata "${source.id}" has no runtime implementation`);
58
- }
59
- }
60
- }
61
- const declaredHooks = new Map((plugin.contributes?.hooks ?? []).map((hook) => [hook.id, hook]));
62
- const runtimeHooks = plugin?.hooks;
63
- for (const hookId of Object.keys(runtimeHooks ?? {})) {
64
- const metadata = declaredHooks.get(hookId);
65
- if (!metadata) {
66
- throw new Error(`plugin "${plugin.name}" typed hook "${hookId}" has no matching metadata entry in contributes.hooks`);
67
- }
68
- if (metadata.command) {
69
- throw new Error(`plugin "${plugin.name}" hook "${hookId}" has both a typed implementation and a command string \u2014 pick one`);
70
- }
71
- }
72
- if (runtimeHooks) {
73
- for (const hook of declaredHooks.values()) {
74
- if (!runtimeHooks[hook.id] && !hook.command) {
75
- throw new Error(`plugin "${plugin.name}" hook metadata "${hook.id}" has no implementation (typed function or command)`);
76
- }
77
- }
78
- }
79
- const declaredCapabilities = new Map((plugin.contributes?.capabilities ?? []).map((capability) => [capability.id, capability]));
80
- const runtimeCapabilities = new Map((plugin?.featureCapabilities ?? []).map((capability) => [capability.id, capability]));
81
- for (const capability of runtimeCapabilities.values()) {
82
- if (!declaredCapabilities.has(capability.id)) {
83
- throw new Error(`plugin "${plugin.name}" executable capability "${capability.id}" has no matching metadata entry in contributes.capabilities`);
84
- }
85
- }
86
- const declaredPanels = new Map((plugin.contributes?.panels ?? []).map((panel) => [panel.id, panel]));
87
- const runtimePanels = new Map((plugin?.panels ?? []).map((panel) => [panel.id, panel]));
88
- for (const panel of runtimePanels.values()) {
89
- const metadata = declaredPanels.get(panel.id);
90
- if (!metadata) {
91
- throw new Error(`plugin "${plugin.name}" executable panel "${panel.id}" has no matching metadata entry in contributes.panels`);
92
- }
93
- if (metadata.slot !== panel.slot) {
94
- throw new Error(`plugin "${plugin.name}" executable panel "${panel.id}" slot "${panel.slot}" does not match metadata slot "${metadata.slot}"`);
95
- }
96
- if (metadata.capabilityId !== panel.capabilityId) {
97
- throw new Error(`plugin "${plugin.name}" executable panel "${panel.id}" capabilityId "${panel.capabilityId ?? "(none)"}" does not match metadata capabilityId "${metadata.capabilityId ?? "(none)"}"`);
98
- }
99
- }
100
- const declaredBlockerClassifiers = new Map((plugin.contributes?.blockerClassifiers ?? []).map((classifier) => [classifier.id, classifier]));
101
- const runtimeBlockerClassifiers = new Map((plugin?.blockerClassifiers ?? []).map((classifier) => [classifier.id, classifier]));
102
- for (const classifier of runtimeBlockerClassifiers.values()) {
103
- if (!declaredBlockerClassifiers.has(classifier.id)) {
104
- throw new Error(`plugin "${plugin.name}" executable blocker classifier "${classifier.id}" has no matching metadata entry in contributes.blockerClassifiers`);
105
- }
106
- }
107
- const declaredCliCommands = new Map((plugin.contributes?.cliCommands ?? []).map((command) => [command.id, command]));
108
- const runtimeCliCommands = new Map((plugin?.cliCommands ?? []).map((command) => [command.id, command]));
109
- for (const command of runtimeCliCommands.values()) {
110
- if (!declaredCliCommands.has(command.id)) {
111
- throw new Error(`plugin "${plugin.name}" executable cli command "${command.id}" has no matching metadata entry in contributes.cliCommands`);
112
- }
113
- }
114
- }
115
24
  function createPluginHost(plugins) {
116
25
  assertUniquePluginNames(plugins);
117
- for (const plugin of plugins) {
118
- assertRuntimeMatchesMetadata(plugin);
119
- }
120
26
  const validators = [];
121
27
  const hooks = [];
122
28
  const skills = [];
@@ -128,38 +34,15 @@ function createPluginHost(plugins) {
128
34
  const capabilities = [];
129
35
  const panels = [];
130
36
  const blockerClassifiers = [];
37
+ const sessionExtensions = [];
131
38
  const stages = [];
132
39
  const stageMutations = [];
133
40
  const stageExecutors = {};
134
- const executableValidators = [];
135
- const executableTaskSources = [];
136
- const executableCapabilities = [];
137
- const executablePanels = [];
138
- const executableBlockerClassifiers = [];
139
- const executableCliCommands = [];
140
41
  for (const plugin of plugins) {
141
42
  const c = plugin.contributes;
142
43
  if (!c)
143
44
  continue;
144
45
  const pluginName = plugin.name;
145
- if (plugin?.validators) {
146
- executableValidators.push(...plugin.validators.map((item) => ({ item, pluginName })));
147
- }
148
- if (plugin?.taskSources) {
149
- executableTaskSources.push(...plugin.taskSources.map((item) => ({ item, pluginName })));
150
- }
151
- if (plugin?.featureCapabilities) {
152
- executableCapabilities.push(...plugin.featureCapabilities.map((item) => ({ item, pluginName })));
153
- }
154
- if (plugin?.panels) {
155
- executablePanels.push(...plugin.panels.map((item) => ({ item, pluginName })));
156
- }
157
- if (plugin?.blockerClassifiers) {
158
- executableBlockerClassifiers.push(...plugin.blockerClassifiers.map((item) => ({ item, pluginName })));
159
- }
160
- if (plugin?.cliCommands) {
161
- executableCliCommands.push(...plugin.cliCommands.map((item) => ({ item, pluginName })));
162
- }
163
46
  if (c.validators)
164
47
  validators.push(...c.validators.map((item) => ({ item, pluginName })));
165
48
  if (c.hooks)
@@ -182,27 +65,17 @@ function createPluginHost(plugins) {
182
65
  panels.push(...c.panels.map((item) => ({ item, pluginName })));
183
66
  if (c.blockerClassifiers)
184
67
  blockerClassifiers.push(...c.blockerClassifiers.map((item) => ({ item, pluginName })));
185
- if (c.stages)
186
- stages.push(...c.stages.map((item) => ({ item, pluginName })));
68
+ if (c.sessionExtensions)
69
+ sessionExtensions.push(...c.sessionExtensions.map((item) => ({ item, pluginName })));
187
70
  if (c.stageMutations)
188
71
  stageMutations.push(...c.stageMutations.map((item) => ({ item, pluginName })));
189
- if (plugin?.stages)
190
- Object.assign(stageExecutors, plugin.stages);
191
- }
192
- indexById(executableValidators, "executableValidator");
193
- indexById(executableTaskSources, "executableTaskSource");
194
- indexById(executableCapabilities, "executableCapability");
195
- indexById(executablePanels, "executablePanel");
196
- indexById(executableBlockerClassifiers, "executableBlockerClassifier");
197
- indexById(executableCliCommands, "executableCliCommand");
198
- const taskSourceFactoryByKind = new Map;
199
- const taskSourceKindRegistrant = new Map;
200
- for (const { item, pluginName } of executableTaskSources) {
201
- if (taskSourceFactoryByKind.has(item.kind)) {
202
- throw new Error(`duplicate task source kind "${item.kind}": registered by plugins "${taskSourceKindRegistrant.get(item.kind)}" and "${pluginName}"`);
72
+ if (c.stages) {
73
+ for (const stage of c.stages) {
74
+ stages.push({ item: stage, pluginName });
75
+ if (stage.run)
76
+ stageExecutors[stage.id] = stage.run;
77
+ }
203
78
  }
204
- taskSourceFactoryByKind.set(item.kind, item);
205
- taskSourceKindRegistrant.set(item.kind, pluginName);
206
79
  }
207
80
  const validatorMap = indexById(validators, "validator");
208
81
  const hookMap = indexById(hooks, "hook");
@@ -215,6 +88,16 @@ function createPluginHost(plugins) {
215
88
  const capabilityMap = indexById(capabilities, "capability");
216
89
  const panelMap = indexById(panels, "panel");
217
90
  const blockerClassifierMap = indexById(blockerClassifiers, "blockerClassifier");
91
+ indexById(sessionExtensions, "sessionExtension");
92
+ const taskSourceFactoryByKind = new Map;
93
+ const taskSourceKindRegistrant = new Map;
94
+ for (const { item, pluginName } of taskSources) {
95
+ if (taskSourceFactoryByKind.has(item.kind)) {
96
+ throw new Error(`duplicate task source kind "${item.kind}": registered by plugins "${taskSourceKindRegistrant.get(item.kind)}" and "${pluginName}"`);
97
+ }
98
+ taskSourceFactoryByKind.set(item.kind, item);
99
+ taskSourceKindRegistrant.set(item.kind, pluginName);
100
+ }
218
101
  const allValidators = validators.map((c) => c.item);
219
102
  const allHooks = hooks.map((c) => c.item);
220
103
  const allSkills = skills.map((c) => c.item);
@@ -228,12 +111,7 @@ function createPluginHost(plugins) {
228
111
  const allCapabilities = capabilities.map((c) => c.item);
229
112
  const allPanels = panels.map((c) => c.item);
230
113
  const allBlockerClassifiers = blockerClassifiers.map((c) => c.item);
231
- const allExecutableValidators = executableValidators.map((c) => c.item);
232
- const allExecutableTaskSources = executableTaskSources.map((c) => c.item);
233
- const allExecutableCapabilities = executableCapabilities.map((c) => c.item);
234
- const allExecutablePanels = executablePanels.map((c) => c.item);
235
- const allExecutableBlockerClassifiers = executableBlockerClassifiers.map((c) => c.item);
236
- const allExecutableCliCommands = executableCliCommands.map((c) => c.item);
114
+ const allSessionExtensions = sessionExtensions.map((c) => c.item);
237
115
  const executableCliCommandByName = new Map;
238
116
  const registerExecutableCliCommandSelector = (selector, contribution) => {
239
117
  const existing = executableCliCommandByName.get(selector);
@@ -242,7 +120,7 @@ function createPluginHost(plugins) {
242
120
  }
243
121
  executableCliCommandByName.set(selector, contribution);
244
122
  };
245
- for (const contribution of executableCliCommands) {
123
+ for (const contribution of cliCommands) {
246
124
  const command = contribution.item;
247
125
  const family = command.family ?? command.id;
248
126
  registerExecutableCliCommandSelector(command.id, contribution);
@@ -277,12 +155,13 @@ function createPluginHost(plugins) {
277
155
  listStages: () => allStages,
278
156
  listStageMutations: () => allStageMutations,
279
157
  listStageExecutors: () => stageExecutors,
280
- listExecutableValidators: () => allExecutableValidators,
281
- listExecutableTaskSources: () => allExecutableTaskSources,
282
- listExecutableCapabilities: () => allExecutableCapabilities,
283
- listExecutablePanels: () => allExecutablePanels,
284
- listExecutableBlockerClassifiers: () => allExecutableBlockerClassifiers,
285
- listExecutableCliCommands: () => allExecutableCliCommands,
158
+ listExecutableValidators: () => allValidators.filter((v) => typeof v.run === "function"),
159
+ listExecutableTaskSources: () => allTaskSources,
160
+ listExecutableCapabilities: () => allCapabilities.filter((c) => typeof c.run === "function"),
161
+ listExecutablePanels: () => allPanels.filter((p) => typeof p.produce === "function"),
162
+ listExecutableBlockerClassifiers: () => allBlockerClassifiers,
163
+ listExecutableCliCommands: () => allCliCommands,
164
+ listSessionExtensions: () => allSessionExtensions,
286
165
  resolveExecutableCliCommand: (requested) => executableCliCommandByName.get(requested)?.item,
287
166
  resolveTaskSourceFactoryByKind: (kind) => taskSourceFactoryByKind.get(kind)
288
167
  };
@@ -1,4 +1,4 @@
1
- import type { BlockerClassifierRegistration, CliCommandRegistration, HookImplementation, PanelRegistration, ProductCapabilityRegistration, RegisteredTaskSource, RigConfig, RigPlugin, StageRun, TaskSourceConfig, TaskSourceRegistration, ValidatorRegistration } from "@rig/contracts";
1
+ import type { AgentRoleRegistration, BlockerClassifierRegistration, CapabilityReplacementSpec, CapabilityTag, CliCommandRegistration, HookImplementation, HookRegistration, PanelRegistration, ProductCapabilityRegistration, RegisteredTaskSource, RepoSourceRegistration, RigConfig, SessionExtensionRegistration, SkillRegistration, Stage, StageMutation, StageRun, TaskFieldExtension, TaskSourceConfig, TaskSourceRegistration, ValidatorRegistration } from "@rig/contracts";
2
2
  export interface ValidatorResult {
3
3
  id: string;
4
4
  passed: boolean;
@@ -13,7 +13,7 @@ export interface ValidatorContext {
13
13
  artifactsDir?: string;
14
14
  taskConfig?: unknown;
15
15
  }
16
- export interface RegisteredValidator extends ValidatorRegistration {
16
+ export interface Validator extends ValidatorRegistration {
17
17
  run(ctx: ValidatorContext): Promise<ValidatorResult>;
18
18
  }
19
19
  /**
@@ -33,71 +33,102 @@ export interface TaskSourceFactoryContext {
33
33
  rigConfig?: RigConfig;
34
34
  }
35
35
  /**
36
- * Executable task-source factory contributed by a plugin. A plugin
37
- * registers a TaskSourceRegistration metadata entry (with `id` and `kind`)
38
- * AND a factory here that turns a TaskSourceConfig into a RegisteredTaskSource.
39
- *
40
- * `kind` is matched against `config.taskSource.kind` at boot. The first
41
- * matching plugin factory wins; if multiple plugins claim the same kind,
42
- * `createPluginHost` throws a duplicate-kind error.
36
+ * Task-source contribution: metadata (`id`, `kind`) plus the `factory` that
37
+ * turns a TaskSourceConfig into a RegisteredTaskSource. `kind` is matched
38
+ * against `config.taskSource.kind` at boot; duplicate kinds throw in
39
+ * `createPluginHost`.
43
40
  */
44
- export interface TaskSourceFactoryEntry extends TaskSourceRegistration {
41
+ export interface TaskSource extends TaskSourceRegistration {
45
42
  factory(config: TaskSourceConfig, context?: TaskSourceFactoryContext): RegisteredTaskSource;
46
43
  }
47
- export interface RuntimeFeatureCapability extends ProductCapabilityRegistration {
44
+ export interface FeatureCapability extends ProductCapabilityRegistration {
48
45
  run?: (input: unknown) => Promise<unknown> | unknown;
49
46
  }
50
- export interface RuntimePanelProducer extends PanelRegistration {
47
+ /** @deprecated single-channel rename use {@link FeatureCapability}. */
48
+ export type RuntimeFeatureCapability = FeatureCapability;
49
+ export interface Panel extends PanelRegistration {
51
50
  produce?: (context?: unknown) => Promise<unknown> | unknown;
52
51
  }
53
- export interface RuntimeBlockerClassifier extends BlockerClassifierRegistration {
52
+ export interface BlockerClassifierEntry extends BlockerClassifierRegistration {
54
53
  classify(input: unknown): Promise<unknown> | unknown;
55
54
  }
55
+ /** @deprecated single-channel rename — use {@link BlockerClassifierEntry}. */
56
+ export type RuntimeBlockerClassifier = BlockerClassifierEntry;
56
57
  export interface RuntimeCliContext {
57
58
  projectRoot: string;
58
59
  outputMode?: string;
59
60
  dryRun?: boolean;
60
61
  }
61
- export interface RuntimeCliCommand extends CliCommandRegistration {
62
+ export interface CliCommand extends CliCommandRegistration {
62
63
  run(context: RuntimeCliContext, args: readonly string[]): Promise<unknown> | unknown;
63
64
  }
64
- export interface RigPluginRuntime {
65
- validators?: readonly RegisteredValidator[];
66
- taskSources?: readonly TaskSourceFactoryEntry[];
67
- /**
68
- * Typed hook implementations keyed by hook id. Each key must match a
69
- * declared `contributes.hooks` entry on the same plugin. A typed hook and
70
- * a metadata `command` string are mutually exclusive per hook — and when
71
- * this channel is present, every declared hook must be implemented one way
72
- * or the other (same all-or-none rule as validators). The runtime's
73
- * hook-materializer generates the shim command that routes Claude Code's
74
- * hook invocation to the typed function (see
75
- * `@rig/runtime/control-plane/hook-runner`).
76
- */
77
- hooks?: Record<string, HookImplementation>;
78
- /**
79
- * Executable stage implementations keyed by stage id — the runtime counterpart
80
- * to `contributes.stageMutations` (which carries only the ordering descriptor).
81
- * When a plugin inserts/replaces a stage, its `run(ctx)` lives here. The plugin
82
- * host flattens these (`listStageExecutors()`) so the kernel's stage runner can
83
- * execute plugin-contributed stages (e.g. the docs-drift pre-merge gate).
84
- */
85
- stages?: Record<string, StageRun>;
86
- /**
87
- * Executable product-capability registrations. This is deliberately separate
88
- * from `capabilities`: that map is reserved for the five kernel provider
89
- * capability tags resolved by `@rig/kernel-seed`.
90
- */
91
- featureCapabilities?: readonly RuntimeFeatureCapability[];
92
- panels?: readonly RuntimePanelProducer[];
93
- blockerClassifiers?: readonly RuntimeBlockerClassifier[];
94
- cliCommands?: readonly RuntimeCliCommand[];
65
+ /**
66
+ * Hook contribution: metadata plus an optional typed `handler`. A hook is
67
+ * implemented either by a typed `handler` or by a raw `command` string in its
68
+ * metadata — never both. The runtime's hook-materializer generates the shim
69
+ * command that routes Claude Code's hook invocation to the typed function.
70
+ */
71
+ export interface Hook extends HookRegistration {
72
+ handler?: HookImplementation;
73
+ }
74
+ /**
75
+ * Stage contribution: the stage descriptor plus its executor. When a plugin
76
+ * inserts/replaces a stage (via a matching `stageMutations` entry), its
77
+ * `run(ctx)` lives here so the kernel's stage runner can execute it.
78
+ */
79
+ export interface StageEntry extends Stage {
80
+ run?: StageRun;
81
+ }
82
+ /**
83
+ * Executable installer for a contributed session extension. The `api` argument
84
+ * is the agent-session extension API (the OMP/Pi `ExtensionAPI`), kept as `any`
85
+ * here on purpose: typing it against the OMP runtime would couple the
86
+ * substrate-agnostic plugin model to the OMP substrate (architecture reference
87
+ * §12). The plugin author (run-worker) and the consumer (rig-host) hold the
88
+ * concrete `ExtensionAPI` type; the plugin host only sees an opaque installer.
89
+ */
90
+ export type SessionExtensionInstall = (api: any) => void | Promise<void>;
91
+ export interface SessionExtensionEntry extends SessionExtensionRegistration {
92
+ install: SessionExtensionInstall;
93
+ }
94
+ /**
95
+ * Single-channel contributions. Fn-bearing kinds carry their executable fn on
96
+ * the same entry as their metadata; metadata-only kinds (skills, repoSources,
97
+ * agentRoles, taskFieldSchemas, stageMutations) are pure registrations.
98
+ */
99
+ export interface PluginContributes {
100
+ validators?: readonly Validator[];
101
+ hooks?: readonly Hook[];
102
+ skills?: readonly SkillRegistration[];
103
+ repoSources?: readonly RepoSourceRegistration[];
104
+ agentRoles?: readonly AgentRoleRegistration[];
105
+ taskFieldSchemas?: readonly TaskFieldExtension[];
106
+ taskSources?: readonly TaskSource[];
107
+ cliCommands?: readonly CliCommand[];
108
+ stages?: readonly StageEntry[];
109
+ stageMutations?: readonly StageMutation[];
110
+ capabilities?: readonly FeatureCapability[];
111
+ panels?: readonly Panel[];
112
+ blockerClassifiers?: readonly BlockerClassifierEntry[];
113
+ sessionExtensions?: readonly SessionExtensionEntry[];
114
+ }
115
+ /**
116
+ * The authored plugin object. Validated against the `RigPlugin` metadata schema
117
+ * in @rig/contracts; the executable fns on `contributes` entries and the kernel
118
+ * `capabilityProviders` ride alongside and are ignored by Schema decode.
119
+ */
120
+ export interface RigPlugin {
121
+ name: string;
122
+ version: string;
123
+ provides?: readonly CapabilityTag[];
124
+ requires?: readonly CapabilityTag[];
125
+ replaces?: readonly CapabilityReplacementSpec[];
126
+ contributes?: PluginContributes;
95
127
  /**
96
- * Executable kernel-capability providers keyed by **capability tag**
97
- * (`transport`, `journal`, …) the same shape the kernel-seed resolver reads
98
- * via `plugin.runtime.capabilities[tag]`. Which tags a plugin provides is
99
- * declared by the top-level `provides: CapabilityTag[]`; this holds the impls.
128
+ * Kernel capability implementations keyed by the tag named in `provides`
129
+ * (`transport`, `journal`, `stage-runner`, `loader-policy`, `kernel`). This
130
+ * is the single home for what the old kernel `LoadedPlugin.runtime.capabilities`
131
+ * / `runtime.kernel` carried — kernel-seed resolves capabilities from here.
100
132
  */
101
- capabilities?: Record<string, unknown>;
133
+ capabilityProviders?: Partial<Record<CapabilityTag, unknown>>;
102
134
  }
103
- export type RigPluginWithRuntime = RigPlugin & RigPluginRuntime;
@@ -0,0 +1,66 @@
1
+ import type { RigConfig } from "@rig/contracts";
2
+ import { type PluginHost } from "./plugin-host";
3
+ import type { RigPlugin } from "./plugin-runtime";
4
+ export type ProjectPluginResolutionMode = "strict-config-only";
5
+ export type ProjectConfigPresence = {
6
+ readonly status: "loaded";
7
+ readonly path: string | null;
8
+ } | {
9
+ readonly status: "missing";
10
+ readonly path: null;
11
+ };
12
+ export type ProjectPluginResolutionWarningCode = never;
13
+ export type ProjectPluginResolutionWarning = {
14
+ readonly code: ProjectPluginResolutionWarningCode;
15
+ readonly level: "warning" | "info";
16
+ readonly message: string;
17
+ };
18
+ export type ProjectPluginResolution = {
19
+ readonly mode: ProjectPluginResolutionMode;
20
+ readonly projectRoot: string;
21
+ readonly surfaceName: string;
22
+ readonly configPresence: ProjectConfigPresence;
23
+ readonly config: RigConfig | null;
24
+ readonly warnings: readonly ProjectPluginResolutionWarning[];
25
+ readonly plugins: readonly RigPlugin[];
26
+ };
27
+ export type ProjectPluginHostResult = {
28
+ readonly host: PluginHost;
29
+ readonly resolved: ProjectPluginResolution;
30
+ };
31
+ export type LoadProjectConfig = (projectRoot: string) => Promise<RigConfig>;
32
+ export type LoadProjectPluginSetOptions = {
33
+ readonly mode?: ProjectPluginResolutionMode;
34
+ readonly surfaceName?: string;
35
+ readonly load?: LoadProjectConfig;
36
+ };
37
+ export type ProjectPluginResolutionOptions = Omit<LoadProjectPluginSetOptions, "mode" | "surfaceName"> & {
38
+ readonly projectRoot: string;
39
+ readonly mode: ProjectPluginResolutionMode;
40
+ readonly surfaceName: string;
41
+ };
42
+ export declare function projectPluginResolutionWarningMessages(resolved: Pick<ProjectPluginResolution, "warnings">): readonly string[];
43
+ export declare function loadProjectPluginSet(options: ProjectPluginResolutionOptions): Promise<ProjectPluginResolution>;
44
+ export declare function loadProjectPluginSet(projectRoot: string, options?: LoadProjectPluginSetOptions): Promise<ProjectPluginResolution>;
45
+ export type ResolvedPluginHost = {
46
+ readonly host: PluginHost;
47
+ readonly config: RigConfig;
48
+ };
49
+ export type ResolvePluginHostOptions = {
50
+ readonly load?: LoadProjectConfig;
51
+ };
52
+ /**
53
+ * The single cached plugin-host resolver. Returns the resolved host plus the
54
+ * loaded config for a project root, memoized per normalized root and validated
55
+ * by the rig.config file mtime — an edited config rebuilds the host (preserving
56
+ * loadConfig's mtime-cache semantics) while every other consumer in the same
57
+ * generation shares one resolved host.
58
+ *
59
+ * It propagates loadConfig's throw on a missing/broken config; each consumer
60
+ * keeps its own try/catch so per-call-site degradation (graceful-null vs
61
+ * surfaced error) is unchanged. A custom `load` bypasses the shared cache so it
62
+ * cannot poison the canonical on-disk host other consumers read.
63
+ */
64
+ export declare function resolvePluginHost(projectRoot: string, options?: ResolvePluginHostOptions): Promise<ResolvedPluginHost>;
65
+ export declare function createProjectPluginHost(options: ProjectPluginResolutionOptions): Promise<ProjectPluginHostResult>;
66
+ export declare function createProjectPluginHost(projectRoot: string, options?: LoadProjectPluginSetOptions): Promise<ProjectPluginHostResult>;