@h-rig/core 0.0.6-alpha.18 → 0.0.6-alpha.181

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.
Files changed (105) hide show
  1. package/dist/src/agent-role-registry.d.ts +4 -0
  2. package/dist/src/agent-role-registry.js +27 -0
  3. package/dist/src/authority-paths.d.ts +15 -0
  4. package/dist/src/authority-paths.js +80 -0
  5. package/dist/src/baked-secrets.d.ts +3 -0
  6. package/dist/src/baked-secrets.js +63 -0
  7. package/dist/src/build-time-config.d.ts +12 -0
  8. package/dist/src/build-time-config.js +25 -0
  9. package/dist/src/build-time-config.macro.d.ts +1 -0
  10. package/dist/src/capability-loaders.d.ts +51 -0
  11. package/dist/src/capability-loaders.js +870 -0
  12. package/dist/src/capability.d.ts +79 -0
  13. package/dist/src/capability.js +63 -0
  14. package/dist/src/checkout-root.d.ts +1 -0
  15. package/dist/src/checkout-root.js +30 -0
  16. package/dist/src/config-env.d.ts +4 -0
  17. package/dist/src/config-env.js +23 -0
  18. package/dist/src/config.d.ts +3 -0
  19. package/dist/src/config.js +44 -0
  20. package/dist/src/declarative-config.d.ts +14 -0
  21. package/dist/src/declarative-config.js +85 -0
  22. package/dist/src/default-kernel.d.ts +1 -0
  23. package/dist/src/default-kernel.js +12 -0
  24. package/dist/src/define-config.d.ts +20 -0
  25. package/dist/src/define-config.js +28 -15
  26. package/dist/src/define-plugin.d.ts +13 -0
  27. package/dist/src/define-plugin.js +4 -43
  28. package/dist/src/embedded-plugins.d.ts +59 -0
  29. package/dist/src/embedded-plugins.js +22 -0
  30. package/dist/src/exec.d.ts +13 -0
  31. package/dist/src/exec.js +101 -0
  32. package/dist/src/harness-paths.d.ts +9 -0
  33. package/dist/src/harness-paths.js +126 -0
  34. package/dist/src/hook-materializer.d.ts +21 -0
  35. package/dist/src/hook-materializer.js +152 -0
  36. package/dist/src/hook-protocol.d.ts +2 -0
  37. package/dist/src/hook-protocol.js +432 -0
  38. package/dist/src/hook-runner.d.ts +48 -0
  39. package/dist/src/hook-runner.js +868 -0
  40. package/dist/src/hook-runtime.d.ts +52 -0
  41. package/dist/src/hook-runtime.js +432 -0
  42. package/dist/src/index.d.ts +8 -0
  43. package/dist/src/index.js +210 -2499
  44. package/dist/src/json-files.d.ts +9 -0
  45. package/dist/src/json-files.js +124 -0
  46. package/dist/src/kernel-boot.d.ts +2 -0
  47. package/dist/src/kernel-boot.js +10 -0
  48. package/dist/src/kernel-entrypoint.d.ts +22 -0
  49. package/dist/src/kernel-entrypoint.js +660 -0
  50. package/dist/src/kernel-plugin-abi.d.ts +1 -0
  51. package/dist/src/kernel-plugin-abi.js +1 -0
  52. package/dist/src/kernel-resolver.d.ts +2 -0
  53. package/dist/src/kernel-resolver.js +6 -0
  54. package/dist/src/layout.d.ts +10 -0
  55. package/dist/src/layout.js +138 -0
  56. package/dist/src/load-config.d.ts +2 -0
  57. package/dist/src/load-config.js +535 -30
  58. package/dist/src/placement.d.ts +58 -0
  59. package/dist/src/placement.js +53 -0
  60. package/dist/src/plugin-host-context.d.ts +65 -0
  61. package/dist/src/plugin-host-context.js +1171 -0
  62. package/dist/src/plugin-host-registries.d.ts +31 -0
  63. package/dist/src/plugin-host-registries.js +79 -0
  64. package/dist/src/plugin-host.d.ts +77 -0
  65. package/dist/src/plugin-host.js +127 -63
  66. package/dist/src/plugin-runtime.d.ts +173 -0
  67. package/dist/src/project-plugins.d.ts +63 -0
  68. package/dist/src/project-plugins.js +905 -0
  69. package/dist/src/remote-config.d.ts +125 -0
  70. package/dist/src/remote-config.js +85 -0
  71. package/dist/src/root-resolver.d.ts +5 -0
  72. package/dist/src/root-resolver.js +68 -0
  73. package/dist/src/run-provisioning.d.ts +37 -0
  74. package/dist/src/run-provisioning.js +35 -0
  75. package/dist/src/runtime-context.d.ts +20 -0
  76. package/dist/src/runtime-context.js +257 -0
  77. package/dist/src/runtime-events.d.ts +44 -0
  78. package/dist/src/runtime-events.js +208 -0
  79. package/dist/src/runtime-overlay.d.ts +11 -0
  80. package/dist/src/runtime-overlay.js +69 -0
  81. package/dist/src/runtime-paths.d.ts +21 -0
  82. package/dist/src/runtime-paths.js +181 -0
  83. package/dist/src/runtime-provisioning-env.d.ts +5 -0
  84. package/dist/src/runtime-provisioning-env.js +217 -0
  85. package/dist/src/runtime-runner-context.d.ts +12 -0
  86. package/dist/src/runtime-runner-context.js +1 -0
  87. package/dist/src/safe-identifiers.d.ts +44 -0
  88. package/dist/src/safe-identifiers.js +96 -0
  89. package/dist/src/scope-rules.d.ts +4 -0
  90. package/dist/src/scope-rules.js +21 -0
  91. package/dist/src/server-paths.d.ts +22 -0
  92. package/dist/src/server-paths.js +219 -0
  93. package/dist/src/setup-version.d.ts +3 -0
  94. package/dist/src/setup-version.js +14 -0
  95. package/dist/src/task-record-reader.d.ts +3 -0
  96. package/dist/src/task-record-reader.js +9 -0
  97. package/dist/src/validator-registry.d.ts +27 -0
  98. package/dist/src/validator-registry.js +64 -0
  99. package/package.json +162 -10
  100. package/dist/src/engineReadModelReducer.js +0 -1780
  101. package/dist/src/rig-init-builder.js +0 -57
  102. package/dist/src/rigSelectors.js +0 -293
  103. package/dist/src/taskGraph.js +0 -64
  104. package/dist/src/taskGraphCodes.js +0 -26
  105. package/dist/src/taskGraphLayout.js +0 -374
@@ -0,0 +1,31 @@
1
+ import type { RegisteredTaskSource, RigConfig, TaskFieldExtension } from "@rig/contracts";
2
+ import type { PluginHost } from "./plugin-host";
3
+ import type { TaskSourceFactoryContext } from "./plugin-runtime";
4
+ export interface TaskSourceRegistry {
5
+ register(s: RegisteredTaskSource): void;
6
+ resolveById(id: string): RegisteredTaskSource;
7
+ resolveByKind(kind: string): RegisteredTaskSource;
8
+ list(): readonly RegisteredTaskSource[];
9
+ }
10
+ export declare function createTaskSourceRegistry(): TaskSourceRegistry;
11
+ /**
12
+ * Instantiate the task-source adapter selected by `config.taskSource.kind`
13
+ * from executable task-source factories contributed by loaded plugins.
14
+ *
15
+ * First-party adapters are not special-cased here: projects that use the
16
+ * standard GitHub Issues or files sources get those factories by loading
17
+ * `standard()` in `rig.config.ts`, exactly like any third-party adapter.
18
+ */
19
+ export declare function buildTaskSourceRegistry(config: RigConfig, pluginHost?: PluginHost, context?: TaskSourceFactoryContext): TaskSourceRegistry;
20
+ export interface TaskFieldRegistry {
21
+ get(id: string): TaskFieldExtension | undefined;
22
+ list(): readonly TaskFieldExtension[];
23
+ fieldNames(): readonly string[];
24
+ validateTaskFields(task: Record<string, unknown>): {
25
+ ok: true;
26
+ } | {
27
+ ok: false;
28
+ errors: string[];
29
+ };
30
+ }
31
+ export declare function createTaskFieldRegistry(extensions: readonly TaskFieldExtension[]): TaskFieldRegistry;
@@ -0,0 +1,79 @@
1
+ // @bun
2
+ // packages/core/src/plugin-host-registries.ts
3
+ function createTaskSourceRegistry() {
4
+ const byId = new Map;
5
+ const order = [];
6
+ return {
7
+ register(s) {
8
+ if (byId.has(s.id))
9
+ throw new Error(`task source already registered: ${s.id}`);
10
+ byId.set(s.id, s);
11
+ order.push(s);
12
+ },
13
+ resolveById(id) {
14
+ const s = byId.get(id);
15
+ if (!s)
16
+ throw new Error(`task source not registered: ${id}`);
17
+ return s;
18
+ },
19
+ resolveByKind(kind) {
20
+ for (const s of order)
21
+ if (s.kind === kind)
22
+ return s;
23
+ throw new Error(`no task source registered for kind: ${kind}`);
24
+ },
25
+ list: () => order
26
+ };
27
+ }
28
+ function formatRegisteredKinds(pluginHost) {
29
+ const kinds = pluginHost ? pluginHost.listExecutableTaskSources().map((source) => source.kind) : [];
30
+ return kinds.length > 0 ? kinds.join(", ") : "none";
31
+ }
32
+ function buildTaskSourceRegistry(config, pluginHost, context) {
33
+ const registry = createTaskSourceRegistry();
34
+ const taskSourceConfig = config.taskSource;
35
+ const factory = pluginHost?.resolveTaskSourceFactoryByKind(taskSourceConfig.kind);
36
+ if (!factory) {
37
+ throw new Error(`No task source factory registered for kind "${taskSourceConfig.kind}". ` + `Registered kinds: ${formatRegisteredKinds(pluginHost)}. ` + "Load a plugin that contributes an executable task source factory for this kind.");
38
+ }
39
+ registry.register(factory.factory(taskSourceConfig, context ? { ...context, rigConfig: config } : undefined));
40
+ return registry;
41
+ }
42
+ function createTaskFieldRegistry(extensions) {
43
+ const byId = new Map;
44
+ for (const e of extensions) {
45
+ if (byId.has(e.id))
46
+ throw new Error(`task field extension already registered: ${e.id}`);
47
+ byId.set(e.id, e);
48
+ }
49
+ return {
50
+ get: (id) => byId.get(id),
51
+ list: () => Array.from(byId.values()),
52
+ fieldNames: () => Array.from(byId.values()).map((e) => e.fieldName),
53
+ validateTaskFields(task) {
54
+ const errors = [];
55
+ for (const ext of byId.values()) {
56
+ let schema;
57
+ try {
58
+ schema = JSON.parse(ext.schemaJson);
59
+ } catch {
60
+ errors.push(`task field "${ext.id}": schemaJson is not valid JSON`);
61
+ continue;
62
+ }
63
+ const isRequired = typeof schema === "object" && schema !== null && schema.required === true;
64
+ if (!isRequired)
65
+ continue;
66
+ const value = task[ext.fieldName];
67
+ if (value === undefined || value === null || value === "") {
68
+ errors.push(`task field "${ext.fieldName}" (from extension "${ext.id}") is required but missing`);
69
+ }
70
+ }
71
+ return errors.length === 0 ? { ok: true } : { ok: false, errors };
72
+ }
73
+ };
74
+ }
75
+ export {
76
+ createTaskSourceRegistry,
77
+ createTaskFieldRegistry,
78
+ buildTaskSourceRegistry
79
+ };
@@ -0,0 +1,77 @@
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, SeedEntrypoint, TaskSource, Validator } from "./plugin-runtime";
3
+ export interface PluginHost {
4
+ getValidator(id: string): ValidatorRegistration | undefined;
5
+ getHook(id: string): HookRegistration | undefined;
6
+ getSkill(id: string): SkillRegistration | undefined;
7
+ getRepoSource(id: string): RepoSourceRegistration | undefined;
8
+ getAgentRole(id: string): AgentRoleRegistration | undefined;
9
+ getTaskFieldExtension(id: string): TaskFieldExtension | undefined;
10
+ getTaskSource(id: string): TaskSourceRegistration | undefined;
11
+ getCliCommand(id: string): CliCommandRegistration | undefined;
12
+ getCapability(id: string): ProductCapabilityRegistration | undefined;
13
+ getPanel(id: string): PanelRegistration | undefined;
14
+ getBlockerClassifier(id: string): BlockerClassifierRegistration | undefined;
15
+ listValidators(): readonly ValidatorRegistration[];
16
+ listHooks(): readonly HookRegistration[];
17
+ listSkills(): readonly SkillRegistration[];
18
+ listRepoSources(): readonly RepoSourceRegistration[];
19
+ listAgentRoles(): readonly AgentRoleRegistration[];
20
+ listTaskFieldExtensions(): readonly TaskFieldExtension[];
21
+ listTaskSources(): readonly TaskSourceRegistration[];
22
+ listCliCommands(): readonly CliCommandRegistration[];
23
+ listCapabilities(): readonly ProductCapabilityRegistration[];
24
+ listPanels(): readonly PanelRegistration[];
25
+ listBlockerClassifiers(): readonly BlockerClassifierRegistration[];
26
+ listStages(): readonly Stage[];
27
+ /**
28
+ * Stage mutations contributed by all loaded plugins, flattened in load order.
29
+ * The kernel resolves these (+ the default-lifecycle stages) into the run
30
+ * pipeline — this is the wire that makes "every plugin mutates the lifecycle"
31
+ * real (drift pre-merge gate, closure observer, reclassify observer).
32
+ */
33
+ listStageMutations(): readonly StageMutation[];
34
+ /**
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.
39
+ */
40
+ listStageExecutors(): Readonly<Record<string, StageRun>>;
41
+ /**
42
+ * Executable validators contributed by plugins (single-channel: the same
43
+ * `contributes.validators` entries, which carry their `run`).
44
+ */
45
+ listExecutableValidators(): readonly Validator[];
46
+ /**
47
+ * Executable task source factories contributed by plugins. Used by the
48
+ * runtime's `buildTaskSourceRegistry` to instantiate non-standard task
49
+ * source kinds (Linear, Jira, custom) declared by plugins.
50
+ */
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
+ listSeedEntrypoints(): readonly SeedEntrypoint[];
65
+ resolveSeedEntrypointByBasename(basename: string): SeedEntrypoint | undefined;
66
+ resolveSeedEntrypointByWorkerArg(workerArg: string): SeedEntrypoint | undefined;
67
+ resolveInsidePtySeedEntrypoint(): SeedEntrypoint | undefined;
68
+ resolveExecutableCliCommand(requested: string): CliCommand | undefined;
69
+ resolveDefaultRootCliCommand(): CliCommand | undefined;
70
+ /**
71
+ * Look up an executable task source factory by its `kind` (the value in
72
+ * `config.taskSource.kind`). Returns undefined when no plugin provides
73
+ * a factory for that kind.
74
+ */
75
+ resolveTaskSourceFactoryByKind(kind: string): TaskSource | undefined;
76
+ }
77
+ export declare function createPluginHost(plugins: readonly RigPlugin[]): PluginHost;
@@ -21,49 +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.__runtime?.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.__runtime?.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.__runtime?.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.__runtime?.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
- }
62
24
  function createPluginHost(plugins) {
63
25
  assertUniquePluginNames(plugins);
64
- for (const plugin of plugins) {
65
- assertRuntimeMatchesMetadata(plugin);
66
- }
67
26
  const validators = [];
68
27
  const hooks = [];
69
28
  const skills = [];
@@ -72,19 +31,19 @@ function createPluginHost(plugins) {
72
31
  const taskFieldExtensions = [];
73
32
  const taskSources = [];
74
33
  const cliCommands = [];
75
- const executableValidators = [];
76
- const executableTaskSources = [];
34
+ const capabilities = [];
35
+ const panels = [];
36
+ const blockerClassifiers = [];
37
+ const sessionExtensions = [];
38
+ const seedEntrypoints = [];
39
+ const stages = [];
40
+ const stageMutations = [];
41
+ const stageExecutors = {};
77
42
  for (const plugin of plugins) {
78
43
  const c = plugin.contributes;
79
44
  if (!c)
80
45
  continue;
81
46
  const pluginName = plugin.name;
82
- if (plugin.__runtime?.validators) {
83
- executableValidators.push(...plugin.__runtime.validators.map((item) => ({ item, pluginName })));
84
- }
85
- if (plugin.__runtime?.taskSources) {
86
- executableTaskSources.push(...plugin.__runtime.taskSources.map((item) => ({ item, pluginName })));
87
- }
88
47
  if (c.validators)
89
48
  validators.push(...c.validators.map((item) => ({ item, pluginName })));
90
49
  if (c.hooks)
@@ -101,17 +60,25 @@ function createPluginHost(plugins) {
101
60
  taskSources.push(...c.taskSources.map((item) => ({ item, pluginName })));
102
61
  if (c.cliCommands)
103
62
  cliCommands.push(...c.cliCommands.map((item) => ({ item, pluginName })));
104
- }
105
- indexById(executableValidators, "executableValidator");
106
- indexById(executableTaskSources, "executableTaskSource");
107
- const taskSourceFactoryByKind = new Map;
108
- const taskSourceKindRegistrant = new Map;
109
- for (const { item, pluginName } of executableTaskSources) {
110
- if (taskSourceFactoryByKind.has(item.kind)) {
111
- throw new Error(`duplicate task source kind "${item.kind}": registered by plugins "${taskSourceKindRegistrant.get(item.kind)}" and "${pluginName}"`);
63
+ if (c.capabilities)
64
+ capabilities.push(...c.capabilities.map((item) => ({ item, pluginName })));
65
+ if (c.panels)
66
+ panels.push(...c.panels.map((item) => ({ item, pluginName })));
67
+ if (c.blockerClassifiers)
68
+ blockerClassifiers.push(...c.blockerClassifiers.map((item) => ({ item, pluginName })));
69
+ if (c.sessionExtensions)
70
+ sessionExtensions.push(...c.sessionExtensions.map((item) => ({ item, pluginName })));
71
+ if (c.seedEntrypoints)
72
+ seedEntrypoints.push(...c.seedEntrypoints.map((item) => ({ item, pluginName })));
73
+ if (c.stageMutations)
74
+ stageMutations.push(...c.stageMutations.map((item) => ({ item, pluginName })));
75
+ if (c.stages) {
76
+ for (const stage of c.stages) {
77
+ stages.push({ item: stage, pluginName });
78
+ if (stage.run)
79
+ stageExecutors[stage.id] = stage.run;
80
+ }
112
81
  }
113
- taskSourceFactoryByKind.set(item.kind, item);
114
- taskSourceKindRegistrant.set(item.kind, pluginName);
115
82
  }
116
83
  const validatorMap = indexById(validators, "validator");
117
84
  const hookMap = indexById(hooks, "hook");
@@ -121,6 +88,52 @@ function createPluginHost(plugins) {
121
88
  const taskFieldExtMap = indexById(taskFieldExtensions, "taskFieldExtension");
122
89
  const taskSourceMap = indexById(taskSources, "taskSource");
123
90
  const cliCommandMap = indexById(cliCommands, "cliCommand");
91
+ const capabilityMap = indexById(capabilities, "capability");
92
+ const panelMap = indexById(panels, "panel");
93
+ const blockerClassifierMap = indexById(blockerClassifiers, "blockerClassifier");
94
+ indexById(sessionExtensions, "sessionExtension");
95
+ indexById(seedEntrypoints, "seedEntrypoint");
96
+ const taskSourceFactoryByKind = new Map;
97
+ const taskSourceKindRegistrant = new Map;
98
+ for (const { item, pluginName } of taskSources) {
99
+ if (taskSourceFactoryByKind.has(item.kind)) {
100
+ throw new Error(`duplicate task source kind "${item.kind}": registered by plugins "${taskSourceKindRegistrant.get(item.kind)}" and "${pluginName}"`);
101
+ }
102
+ taskSourceFactoryByKind.set(item.kind, item);
103
+ taskSourceKindRegistrant.set(item.kind, pluginName);
104
+ }
105
+ const seedEntrypointByBasename = new Map;
106
+ const seedEntrypointByWorkerArg = new Map;
107
+ let insidePtySeedEntrypoint;
108
+ const registerSeedEntrypointSelector = (map, selectorKind, selector, contribution) => {
109
+ const existing = map.get(selector);
110
+ if (existing) {
111
+ throw new Error(`duplicate seed entrypoint ${selectorKind} "${selector}": registered by entrypoint "${existing.item.id}" from plugin "${existing.pluginName}" and entrypoint "${contribution.item.id}" from plugin "${contribution.pluginName}"`);
112
+ }
113
+ map.set(selector, contribution);
114
+ };
115
+ for (const contribution of seedEntrypoints) {
116
+ const entrypoint = contribution.item;
117
+ let selectorCount = 0;
118
+ if (entrypoint.basename) {
119
+ selectorCount += 1;
120
+ registerSeedEntrypointSelector(seedEntrypointByBasename, "basename", entrypoint.basename, contribution);
121
+ }
122
+ if (entrypoint.workerArg) {
123
+ selectorCount += 1;
124
+ registerSeedEntrypointSelector(seedEntrypointByWorkerArg, "workerArg", entrypoint.workerArg, contribution);
125
+ }
126
+ if (entrypoint.insidePty) {
127
+ selectorCount += 1;
128
+ if (insidePtySeedEntrypoint) {
129
+ throw new Error(`duplicate seed entrypoint inside-pty selector: registered by entrypoint "${insidePtySeedEntrypoint.item.id}" from plugin "${insidePtySeedEntrypoint.pluginName}" and entrypoint "${entrypoint.id}" from plugin "${contribution.pluginName}"`);
130
+ }
131
+ insidePtySeedEntrypoint = contribution;
132
+ }
133
+ if (selectorCount === 0) {
134
+ throw new Error(`seed entrypoint "${entrypoint.id}" from plugin "${contribution.pluginName}" must declare at least one selector (basename, workerArg, or insidePty)`);
135
+ }
136
+ }
124
137
  const allValidators = validators.map((c) => c.item);
125
138
  const allHooks = hooks.map((c) => c.item);
126
139
  const allSkills = skills.map((c) => c.item);
@@ -129,8 +142,39 @@ function createPluginHost(plugins) {
129
142
  const allTaskFieldExtensions = taskFieldExtensions.map((c) => c.item);
130
143
  const allTaskSources = taskSources.map((c) => c.item);
131
144
  const allCliCommands = cliCommands.map((c) => c.item);
132
- const allExecutableValidators = executableValidators.map((c) => c.item);
133
- const allExecutableTaskSources = executableTaskSources.map((c) => c.item);
145
+ const allStageMutations = stageMutations.map((c) => c.item);
146
+ const allStages = stages.map((c) => c.item);
147
+ const allCapabilities = capabilities.map((c) => c.item);
148
+ const allPanels = panels.map((c) => c.item);
149
+ const allBlockerClassifiers = blockerClassifiers.map((c) => c.item);
150
+ const allSessionExtensions = sessionExtensions.map((c) => c.item);
151
+ const allSeedEntrypoints = seedEntrypoints.map((c) => c.item);
152
+ const executableCliCommandByName = new Map;
153
+ const registerExecutableCliCommandSelector = (selector, contribution) => {
154
+ const existing = executableCliCommandByName.get(selector);
155
+ if (existing && existing.item.id !== contribution.item.id) {
156
+ throw new Error(`duplicate executable CLI selector "${selector}" registered by command "${existing.item.id}" from plugin "${existing.pluginName}" and command "${contribution.item.id}" from plugin "${contribution.pluginName}"`);
157
+ }
158
+ executableCliCommandByName.set(selector, contribution);
159
+ };
160
+ for (const contribution of cliCommands) {
161
+ const command = contribution.item;
162
+ const family = command.family ?? command.id;
163
+ registerExecutableCliCommandSelector(command.id, contribution);
164
+ registerExecutableCliCommandSelector(family, contribution);
165
+ for (const alias of command.aliases ?? []) {
166
+ registerExecutableCliCommandSelector(alias, contribution);
167
+ }
168
+ }
169
+ let defaultRootCliCommand;
170
+ for (const contribution of cliCommands) {
171
+ if (!contribution.item.rootDefault)
172
+ continue;
173
+ if (defaultRootCliCommand) {
174
+ throw new Error(`duplicate default root CLI command registered by command "${defaultRootCliCommand.item.id}" from plugin "${defaultRootCliCommand.pluginName}" and command "${contribution.item.id}" from plugin "${contribution.pluginName}"`);
175
+ }
176
+ defaultRootCliCommand = contribution;
177
+ }
134
178
  return {
135
179
  getValidator: (id) => validatorMap.get(id),
136
180
  getHook: (id) => hookMap.get(id),
@@ -140,6 +184,9 @@ function createPluginHost(plugins) {
140
184
  getTaskFieldExtension: (id) => taskFieldExtMap.get(id),
141
185
  getTaskSource: (id) => taskSourceMap.get(id),
142
186
  getCliCommand: (id) => cliCommandMap.get(id),
187
+ getCapability: (id) => capabilityMap.get(id),
188
+ getPanel: (id) => panelMap.get(id),
189
+ getBlockerClassifier: (id) => blockerClassifierMap.get(id),
143
190
  listValidators: () => allValidators,
144
191
  listHooks: () => allHooks,
145
192
  listSkills: () => allSkills,
@@ -148,8 +195,25 @@ function createPluginHost(plugins) {
148
195
  listTaskFieldExtensions: () => allTaskFieldExtensions,
149
196
  listTaskSources: () => allTaskSources,
150
197
  listCliCommands: () => allCliCommands,
151
- listExecutableValidators: () => allExecutableValidators,
152
- listExecutableTaskSources: () => allExecutableTaskSources,
198
+ listCapabilities: () => allCapabilities,
199
+ listPanels: () => allPanels,
200
+ listBlockerClassifiers: () => allBlockerClassifiers,
201
+ listStages: () => allStages,
202
+ listStageMutations: () => allStageMutations,
203
+ listStageExecutors: () => stageExecutors,
204
+ listExecutableValidators: () => allValidators.filter((v) => typeof v.run === "function"),
205
+ listExecutableTaskSources: () => allTaskSources,
206
+ listExecutableCapabilities: () => allCapabilities.filter((c) => typeof c.run === "function"),
207
+ listExecutablePanels: () => allPanels.filter((p) => typeof p.produce === "function"),
208
+ listExecutableBlockerClassifiers: () => allBlockerClassifiers,
209
+ listExecutableCliCommands: () => allCliCommands,
210
+ listSessionExtensions: () => allSessionExtensions,
211
+ listSeedEntrypoints: () => allSeedEntrypoints,
212
+ resolveSeedEntrypointByBasename: (value) => seedEntrypointByBasename.get(value)?.item,
213
+ resolveSeedEntrypointByWorkerArg: (value) => seedEntrypointByWorkerArg.get(value)?.item,
214
+ resolveInsidePtySeedEntrypoint: () => insidePtySeedEntrypoint?.item,
215
+ resolveExecutableCliCommand: (requested) => executableCliCommandByName.get(requested)?.item,
216
+ resolveDefaultRootCliCommand: () => defaultRootCliCommand?.item,
153
217
  resolveTaskSourceFactoryByKind: (kind) => taskSourceFactoryByKind.get(kind)
154
218
  };
155
219
  }
@@ -0,0 +1,173 @@
1
+ import type { AgentRoleRegistration, BlockerClassifierRegistration, CapabilityReplacementSpec, CapabilityTag, CliCommandRegistration, HookImplementation, HookRegistration, PanelRegistration, ProductCapabilityRegistration, RegisteredTaskSource, RepoSourceRegistration, RigConfig, SeedEntrypointRegistration, SessionExtensionRegistration, SkillRegistration, Stage, StageMutation, StageRun, TaskFieldExtension, TaskSourceConfig, TaskSourceRegistration, ValidatorRegistration } from "@rig/contracts";
2
+ export interface ValidatorResult {
3
+ id: string;
4
+ passed: boolean;
5
+ summary: string;
6
+ details?: string;
7
+ }
8
+ export interface ValidatorContext {
9
+ taskId: string;
10
+ workspaceRoot: string;
11
+ scope: readonly string[];
12
+ monorepoRoot?: string;
13
+ artifactsDir?: string;
14
+ taskConfig?: unknown;
15
+ }
16
+ export interface Validator extends ValidatorRegistration {
17
+ run(ctx: ValidatorContext): Promise<ValidatorResult>;
18
+ }
19
+ /**
20
+ * Project context handed to task-source factories at instantiation. The
21
+ * server process's cwd is NOT necessarily the project root (workspace-spawned
22
+ * servers run from the engine checkout), so factories that touch the
23
+ * filesystem must resolve relative config paths against `projectRoot`,
24
+ * never cwd.
25
+ */
26
+ export interface TaskSourceFactoryContext {
27
+ projectRoot: string;
28
+ /**
29
+ * Full project config for adapters that need source-adjacent top-level
30
+ * settings (for example github.projects). Factories should still treat
31
+ * TaskSourceConfig as the primary adapter config.
32
+ */
33
+ rigConfig?: RigConfig;
34
+ }
35
+ /**
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`.
40
+ */
41
+ export interface TaskSource extends TaskSourceRegistration {
42
+ factory(config: TaskSourceConfig, context?: TaskSourceFactoryContext): RegisteredTaskSource;
43
+ }
44
+ export type FeatureCapability = ProductCapabilityRegistration;
45
+ /** @deprecated single-channel rename — use {@link FeatureCapability}. */
46
+ export interface Panel extends PanelRegistration {
47
+ produce?: (context?: unknown) => Promise<unknown> | unknown;
48
+ }
49
+ export interface BlockerClassifierEntry extends BlockerClassifierRegistration {
50
+ classify(input: unknown): Promise<unknown> | unknown;
51
+ }
52
+ /** @deprecated single-channel rename — use {@link BlockerClassifierEntry}. */
53
+ export interface RuntimeCliContext {
54
+ projectRoot: string;
55
+ outputMode?: string;
56
+ dryRun?: boolean;
57
+ }
58
+ export interface CliCommand extends CliCommandRegistration {
59
+ run(context: RuntimeCliContext, args: readonly string[]): Promise<unknown> | unknown;
60
+ }
61
+ /**
62
+ * Hook contribution: metadata plus an optional typed `handler`. A hook is
63
+ * implemented either by a typed `handler` or by a raw `command` string in its
64
+ * metadata — never both. The runtime's hook-materializer generates the shim
65
+ * command that routes Claude Code's hook invocation to the typed function.
66
+ */
67
+ export interface Hook extends HookRegistration {
68
+ handler?: HookImplementation;
69
+ }
70
+ /**
71
+ * Stage contribution: the stage descriptor plus its executor. When a plugin
72
+ * inserts/replaces a stage (via a matching `stageMutations` entry), its
73
+ * `run(ctx)` lives here so the kernel's stage runner can execute it.
74
+ */
75
+ export interface StageEntry extends Stage {
76
+ run?: StageRun;
77
+ }
78
+ /**
79
+ * Executable installer for a contributed session extension. The `api` argument
80
+ * is the agent-session extension API (the OMP/Pi `ExtensionAPI`), kept as `any`
81
+ * here on purpose: typing it against the OMP runtime would couple the
82
+ * substrate-agnostic plugin model to the OMP substrate (architecture reference
83
+ * §12). The plugin author (run-worker) and the consumer (rig-host) hold the
84
+ * concrete `ExtensionAPI` type; the plugin host only sees an opaque installer.
85
+ */
86
+ export type SessionExtensionInstall = (api: any) => void | Promise<void>;
87
+ export interface SessionExtensionEntry extends SessionExtensionRegistration {
88
+ install: SessionExtensionInstall;
89
+ }
90
+ export type SeedEntrypointRunResult = boolean | void | number;
91
+ export interface SeedEntrypointContext {
92
+ argv: readonly string[];
93
+ execPath?: string;
94
+ basename?: string;
95
+ workerArg?: string;
96
+ parentPort?: unknown;
97
+ }
98
+ export interface SeedEntrypoint extends SeedEntrypointRegistration {
99
+ run(context: SeedEntrypointContext): Promise<SeedEntrypointRunResult> | SeedEntrypointRunResult;
100
+ }
101
+ /**
102
+ * Single-channel contributions. Fn-bearing kinds carry their executable fn on
103
+ * the same entry as their metadata; metadata-only kinds (skills, repoSources,
104
+ * agentRoles, taskFieldSchemas, stageMutations) are pure registrations.
105
+ */
106
+ /** Context handed to a plugin when it default-fills its config fragment. */
107
+ export interface ConfigDefaultsContext {
108
+ /** Absolute path to the project root being configured. */
109
+ readonly projectRoot: string;
110
+ /**
111
+ * Explicit `owner/repo` slug when a caller already knows it (the init wizard,
112
+ * a setup flow). When absent, a plugin that needs the repo identity derives it
113
+ * from its own domain (e.g. the github plugin reads the git remote). This is
114
+ * the single override seam so autocreate and the wizard share one composer.
115
+ */
116
+ readonly repoSlug?: string;
117
+ }
118
+ /**
119
+ * A plugin's contribution to the declarative `.rig/rigfig.toml`.
120
+ *
121
+ * Plugins are the things that ENRICH the config: each one owns the config
122
+ * properties IT needs and default-fills them with its OWN domain logic — the
123
+ * github plugin detects owner/repo from the git remote and fills the github
124
+ * task source + workspace id; the pr plugin fills `[pr]`; planning fills
125
+ * `[planning]`; and so on. The autocreate/init composer deep-merges every
126
+ * active plugin's fragment (in plugin order) into the written config. Returning
127
+ * null (or a plugin omitting `config`) contributes nothing.
128
+ *
129
+ * `defaults` is a fn — it rides on the authored RigPlugin and is ignored by the
130
+ * serializable manifest Schema, like every other executable contribution entry.
131
+ */
132
+ export interface PluginConfigContribution {
133
+ defaults(context: ConfigDefaultsContext): Record<string, unknown> | null;
134
+ }
135
+ export interface PluginContributes {
136
+ validators?: readonly Validator[];
137
+ hooks?: readonly Hook[];
138
+ skills?: readonly SkillRegistration[];
139
+ repoSources?: readonly RepoSourceRegistration[];
140
+ agentRoles?: readonly AgentRoleRegistration[];
141
+ taskFieldSchemas?: readonly TaskFieldExtension[];
142
+ taskSources?: readonly TaskSource[];
143
+ cliCommands?: readonly CliCommand[];
144
+ stages?: readonly StageEntry[];
145
+ stageMutations?: readonly StageMutation[];
146
+ capabilities?: readonly FeatureCapability[];
147
+ panels?: readonly Panel[];
148
+ blockerClassifiers?: readonly BlockerClassifierEntry[];
149
+ sessionExtensions?: readonly SessionExtensionEntry[];
150
+ seedEntrypoints?: readonly SeedEntrypoint[];
151
+ /** Declarative config defaults this plugin contributes to `.rig/rigfig.toml`. */
152
+ config?: PluginConfigContribution;
153
+ }
154
+ /**
155
+ * The authored plugin object. Validated against the `RigPlugin` metadata schema
156
+ * in @rig/contracts; the executable fns on `contributes` entries and the kernel
157
+ * `capabilityProviders` ride alongside and are ignored by Schema decode.
158
+ */
159
+ export interface RigPlugin {
160
+ name: string;
161
+ version: string;
162
+ provides?: readonly CapabilityTag[];
163
+ requires?: readonly CapabilityTag[];
164
+ replaces?: readonly CapabilityReplacementSpec[];
165
+ contributes?: PluginContributes;
166
+ /**
167
+ * Kernel capability implementations keyed by the tag named in `provides`
168
+ * (`transport`, `journal`, `stage-runner`, `loader-policy`, `kernel`). This
169
+ * is the single home for what the old kernel `LoadedPlugin.runtime.capabilities`
170
+ * / `runtime.kernel` carried — kernel-seed resolves capabilities from here.
171
+ */
172
+ capabilityProviders?: Partial<Record<CapabilityTag, unknown>>;
173
+ }
@@ -0,0 +1,63 @@
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 ProjectPluginResolution = {
13
+ readonly mode: ProjectPluginResolutionMode;
14
+ readonly projectRoot: string;
15
+ readonly configPresence: ProjectConfigPresence;
16
+ readonly config: RigConfig | null;
17
+ readonly plugins: readonly RigPlugin[];
18
+ };
19
+ export type ProjectPluginHostResult = {
20
+ readonly host: PluginHost;
21
+ readonly resolved: ProjectPluginResolution;
22
+ };
23
+ export type LoadProjectConfig = (projectRoot: string) => Promise<RigConfig>;
24
+ export type LoadProjectPluginSetOptions = {
25
+ readonly mode?: ProjectPluginResolutionMode;
26
+ readonly load?: LoadProjectConfig;
27
+ };
28
+ export type ProjectPluginResolutionOptions = Omit<LoadProjectPluginSetOptions, "mode"> & {
29
+ readonly projectRoot: string;
30
+ readonly mode: ProjectPluginResolutionMode;
31
+ };
32
+ export declare function loadProjectPluginSet(options: ProjectPluginResolutionOptions): Promise<ProjectPluginResolution>;
33
+ export declare function loadProjectPluginSet(projectRoot: string, options?: LoadProjectPluginSetOptions): Promise<ProjectPluginResolution>;
34
+ export type ResolvedPluginHost = {
35
+ readonly host: PluginHost;
36
+ readonly config: RigConfig;
37
+ };
38
+ export type ResolvePluginHostOptions = {
39
+ readonly load?: LoadProjectConfig;
40
+ };
41
+ /**
42
+ * The single cached plugin-host resolver. Returns the resolved host plus the
43
+ * loaded config for a project root, memoized per normalized root and validated
44
+ * by the rig.config file mtime — an edited config rebuilds the host (preserving
45
+ * loadConfig's mtime-cache semantics) while every other consumer in the same
46
+ * generation shares one resolved host.
47
+ *
48
+ * It propagates loadConfig's throw on a missing/broken config; each consumer
49
+ * keeps its own try/catch so per-call-site degradation (graceful-null vs
50
+ * surfaced error) is unchanged. A custom `load` bypasses the shared cache so it
51
+ * cannot poison the canonical on-disk host other consumers read.
52
+ */
53
+ export declare function resolvePluginHost(projectRoot: string, options?: ResolvePluginHostOptions): Promise<ResolvedPluginHost>;
54
+ /**
55
+ * Resolve the plugin host for a project root, returning the host plus the full
56
+ * resolution shape. This delegates to the single cached `resolvePluginHost`
57
+ * memo so every consumer in a process SHARES one resolved host for a root
58
+ * instead of re-evaluating rig.config.ts and constructing an independent host
59
+ * (architecture-reference §18: one resolved host per process/root). Graceful
60
+ * missing-config degradation and the custom-`load` bypass are preserved.
61
+ */
62
+ export declare function createProjectPluginHost(options: ProjectPluginResolutionOptions): Promise<ProjectPluginHostResult>;
63
+ export declare function createProjectPluginHost(projectRoot: string, options?: LoadProjectPluginSetOptions): Promise<ProjectPluginHostResult>;