@capekai/core 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/README.md +12 -0
  2. package/package.json +105 -0
  3. package/src/adapters/ai-sdk.ts +84 -0
  4. package/src/compaction/contracts.ts +82 -0
  5. package/src/compaction/executor.ts +161 -0
  6. package/src/compaction/policy.ts +318 -0
  7. package/src/compaction/recovery.ts +139 -0
  8. package/src/compaction/task.ts +540 -0
  9. package/src/configuration/contracts.ts +58 -0
  10. package/src/configuration/defaults.ts +27 -0
  11. package/src/configuration/runtime.ts +42 -0
  12. package/src/configuration/single-model.ts +75 -0
  13. package/src/context/assembler.ts +112 -0
  14. package/src/context/index.ts +2 -0
  15. package/src/context/sources.ts +119 -0
  16. package/src/context/workspace.ts +63 -0
  17. package/src/core/agent.ts +401 -0
  18. package/src/core/build-tools.ts +139 -0
  19. package/src/core/chat-handler.ts +858 -0
  20. package/src/core/error-handling.ts +18 -0
  21. package/src/core/fork.ts +103 -0
  22. package/src/core/interrupt.ts +192 -0
  23. package/src/core/message-utils.ts +261 -0
  24. package/src/core/model-utils.ts +149 -0
  25. package/src/core/part-utils.ts +88 -0
  26. package/src/core/provider-utils.ts +67 -0
  27. package/src/core/revert.ts +46 -0
  28. package/src/core/step-handlers.ts +157 -0
  29. package/src/core/stream/finalization.ts +65 -0
  30. package/src/core/stream/stream-config.ts +82 -0
  31. package/src/core/stream-handlers.ts +242 -0
  32. package/src/core/structured-output.ts +68 -0
  33. package/src/core/tool-builders/agent-tools.ts +71 -0
  34. package/src/core/tool-builders/external-tools.ts +179 -0
  35. package/src/core/tool-builders/types.ts +16 -0
  36. package/src/core/tool-builders/workspace-tools.ts +293 -0
  37. package/src/core/tool-capabilities.ts +65 -0
  38. package/src/goals/evaluator.ts +171 -0
  39. package/src/goals/index.ts +3 -0
  40. package/src/goals/loop.ts +167 -0
  41. package/src/goals/service.ts +39 -0
  42. package/src/index.ts +10 -0
  43. package/src/internal/ask-authority.ts +29 -0
  44. package/src/internal/composition.ts +44 -0
  45. package/src/internal/configuration.ts +22 -0
  46. package/src/internal/execution.ts +108 -0
  47. package/src/internal/hosts.ts +64 -0
  48. package/src/internal/plugins.ts +71 -0
  49. package/src/internal/providers.ts +32 -0
  50. package/src/internal/sandbox.ts +19 -0
  51. package/src/internal/tools.ts +48 -0
  52. package/src/internal/workspace.ts +25 -0
  53. package/src/kernel/diagnostics.ts +249 -0
  54. package/src/kernel/errors.ts +120 -0
  55. package/src/kernel/events.ts +82 -0
  56. package/src/kernel/index.ts +72 -0
  57. package/src/kernel/kernel.ts +62 -0
  58. package/src/kernel/lifecycle.ts +72 -0
  59. package/src/kernel/plugin.ts +218 -0
  60. package/src/kernel/registry.ts +493 -0
  61. package/src/kernel/scope.ts +776 -0
  62. package/src/kernel/service-key.ts +19 -0
  63. package/src/kernel/types.ts +317 -0
  64. package/src/memory/index.ts +2 -0
  65. package/src/memory/memory-tool.ts +75 -0
  66. package/src/memory/registry.ts +172 -0
  67. package/src/permission/ask-user-api.ts +70 -0
  68. package/src/permission/contracts.ts +135 -0
  69. package/src/permission/permission-request-manager.ts +58 -0
  70. package/src/permission/policy.ts +277 -0
  71. package/src/permission/runtime.ts +612 -0
  72. package/src/plugins/compaction-policy.ts +46 -0
  73. package/src/plugins/compose.ts +171 -0
  74. package/src/plugins/context-sections.ts +246 -0
  75. package/src/plugins/default-agent-driver.ts +14 -0
  76. package/src/plugins/facade-plugins.ts +129 -0
  77. package/src/plugins/goal-domain.ts +82 -0
  78. package/src/plugins/legacy-system-message.ts +152 -0
  79. package/src/plugins/loaded-tools.ts +23 -0
  80. package/src/plugins/memory-domain.ts +264 -0
  81. package/src/plugins/orchestrator-session.ts +29 -0
  82. package/src/plugins/permission-policy.ts +49 -0
  83. package/src/plugins/retry-policy.ts +28 -0
  84. package/src/plugins/scheduler-domain.ts +192 -0
  85. package/src/plugins/service-keys.ts +294 -0
  86. package/src/plugins/session-search-domain.ts +238 -0
  87. package/src/plugins/skills-domain.ts +272 -0
  88. package/src/plugins/subagent-domain.ts +287 -0
  89. package/src/plugins/tool-catalog.ts +78 -0
  90. package/src/plugins/tool-output-policy.ts +52 -0
  91. package/src/plugins/value-plugins.ts +150 -0
  92. package/src/plugins/workflow-domain.ts +198 -0
  93. package/src/plugins/workspace-policy.ts +37 -0
  94. package/src/providers/registry.ts +63 -0
  95. package/src/providers/types.ts +44 -0
  96. package/src/retry/policy.ts +282 -0
  97. package/src/retry/stream-chat.ts +312 -0
  98. package/src/runtime/agent-runtime.ts +83 -0
  99. package/src/runtime/default-agent-driver.ts +23 -0
  100. package/src/runtime/domain-tool-source.ts +156 -0
  101. package/src/runtime/events.ts +61 -0
  102. package/src/runtime/host-dependencies.ts +71 -0
  103. package/src/runtime/host-guidance.ts +22 -0
  104. package/src/runtime/host-layout.ts +23 -0
  105. package/src/runtime/host.ts +129 -0
  106. package/src/runtime/standalone-host.ts +118 -0
  107. package/src/sandbox/controller.ts +204 -0
  108. package/src/sandbox/model.ts +305 -0
  109. package/src/sandbox/provider.ts +53 -0
  110. package/src/sandbox/types.ts +110 -0
  111. package/src/scheduler/host.ts +22 -0
  112. package/src/scheduler/scheduler-tool.ts +172 -0
  113. package/src/session-search/host.ts +56 -0
  114. package/src/session-search/index.ts +23 -0
  115. package/src/session-search/session-search-tool.ts +151 -0
  116. package/src/skills/index.ts +3 -0
  117. package/src/skills/registry.ts +63 -0
  118. package/src/skills/skill-manage-tool.ts +205 -0
  119. package/src/skills/skill-tool.ts +42 -0
  120. package/src/storage/contracts.ts +159 -0
  121. package/src/storage/memory.ts +321 -0
  122. package/src/storage/options.ts +75 -0
  123. package/src/storage/runtime.ts +115 -0
  124. package/src/storage/sqlite-tool-output-artifacts.ts +106 -0
  125. package/src/storage/sqlite.ts +321 -0
  126. package/src/storage/tool-output-artifacts.ts +75 -0
  127. package/src/storage.ts +31 -0
  128. package/src/subagent/child-session.ts +282 -0
  129. package/src/subagent/guidance.ts +8 -0
  130. package/src/subagent/policy.ts +198 -0
  131. package/src/subagent/task-tool.ts +584 -0
  132. package/src/tool-output/contracts.ts +111 -0
  133. package/src/tool-output/policy.ts +410 -0
  134. package/src/tool.ts +1 -0
  135. package/src/tools/executor.ts +258 -0
  136. package/src/tools/install-manifest.ts +40 -0
  137. package/src/tools/llm-api.ts +77 -0
  138. package/src/tools/registry.ts +206 -0
  139. package/src/tools/tool-artifact.ts +182 -0
  140. package/src/tools/tool-source.ts +53 -0
  141. package/src/utils/errors.ts +334 -0
  142. package/src/utils/strip-visualization.ts +50 -0
  143. package/src/workflow/decomposer.ts +139 -0
  144. package/src/workflow/execution.ts +523 -0
  145. package/src/workflow/orchestrator-session.ts +161 -0
  146. package/src/workflow/synthesizer.ts +130 -0
  147. package/src/workspace/contracts.ts +135 -0
  148. package/src/workspace/policy.ts +327 -0
@@ -0,0 +1,171 @@
1
+ /**
2
+ * C2 composition helpers. Scope creation is async (kernel activation);
3
+ * entering a composed agent scope is fully synchronous: every service is
4
+ * resolved and every AsyncLocalStorage accessor is seeded before the
5
+ * callback starts, so no async work runs with unseeded accessors.
6
+ */
7
+
8
+ import { tmpdir } from 'node:os';
9
+ import { join } from 'node:path';
10
+ import { withRuntimeConfiguration } from '../configuration/runtime';
11
+ import { withContextAssembler } from '../context/assembler';
12
+ import { withContextSources } from '../context/sources';
13
+ import { withRetryPolicy } from '../retry/policy';
14
+ import { withCompactionService } from '../compaction/policy';
15
+ import { withAskPermissionPolicy } from '../permission/policy';
16
+ import { withPermissionRuntimeService } from '../permission/runtime';
17
+ import { withWorkspaceService } from '../workspace/policy';
18
+ import { withToolOutputService } from '../tool-output/policy';
19
+ import { withGoalDomain } from '../goals/service';
20
+ import { createAgentScope } from '../kernel/kernel';
21
+ export { createAgentScope, createProcessScope } from '../kernel/kernel';
22
+ import type { AgentScopeHandle, ProcessScopeHandle } from '../kernel/types';
23
+ import { withProviderOverrides } from '../providers/registry';
24
+ import {
25
+ DOMAIN_TOOL_PAYLOAD_FIELD,
26
+ isDomainToolPayload,
27
+ withContributedDomainToolPayloads,
28
+ type DomainToolPayload,
29
+ } from '../runtime/domain-tool-source';
30
+ import { withRuntimeHost, getRuntimeHost, type RuntimeHost } from '../runtime/host';
31
+ import { createStandaloneHost } from '../runtime/standalone-host';
32
+ import { withSandboxController } from '../sandbox/controller';
33
+ import { withStorage } from '../storage/runtime';
34
+ import { withToolRegistryResolver } from '../tools/registry';
35
+ import { withWorkspaceToolDiscovery } from '../tools/tool-source';
36
+ import { createFacadeAgentPlugins, type FacadeScopeValues } from './facade-plugins';
37
+ export { facadeProcessPlugins } from './facade-plugins';
38
+ import { capekGoalDomainKey } from './goal-domain';
39
+ import {
40
+ capekContextAssemblerKey,
41
+ capekContextSourcesKey,
42
+ capekProviderOverridesKey,
43
+ capekRuntimeConfigurationKey,
44
+ capekRuntimeHostKey,
45
+ capekRetryPolicyKey,
46
+ capekCompactionServiceKey,
47
+ capekPermissionPolicyKey,
48
+ capekPermissionRuntimeKey,
49
+ capekWorkspacePolicyKey,
50
+ capekToolOutputPolicyKey,
51
+ capekSandboxControllerKey,
52
+ capekStorageKey,
53
+ capekToolResolverKey,
54
+ capekWorkspaceToolDiscoveryKey,
55
+ } from './service-keys';
56
+
57
+ export interface Composition {
58
+ readonly processScope: ProcessScopeHandle;
59
+ readonly agentScope: AgentScopeHandle;
60
+ }
61
+
62
+ /** Composes one agent scope above an explicit process scope using the
63
+ * package's curated plugin set. The C6 policy providers read the ambient
64
+ * runtime host (tool-output temp root) at activation, so composition runs
65
+ * inside `withRuntimeHost`; when no host is configured ambiently, the
66
+ * reference standalone host is installed for the composition's duration.
67
+ * The caller owns both scopes' lifetimes:
68
+ * `await composition.agentScope.dispose()` and
69
+ * `await composition.processScope.dispose()` when done. Multiple agent
70
+ * scopes may share one process scope concurrently. */
71
+ export async function createComposition(
72
+ processScope: ProcessScopeHandle,
73
+ values: FacadeScopeValues,
74
+ ): Promise<Composition> {
75
+ let ambient: RuntimeHost | undefined;
76
+ try {
77
+ ambient = getRuntimeHost();
78
+ } catch {
79
+ // No ambient host configured; the reference standalone host covers
80
+ // composition-time activation reads (tool-output temp root).
81
+ ambient = undefined;
82
+ }
83
+ return withRuntimeHost(ambient ?? createStandaloneHost({
84
+ workspace: process.cwd(),
85
+ sandboxActive: false,
86
+ tempRoot: join(tmpdir(), 'capek-composition'),
87
+ }), () =>
88
+ createAgentScope(
89
+ processScope,
90
+ [...createFacadeAgentPlugins(values)],
91
+ ).then((agentScope): Composition => ({ processScope, agentScope })));
92
+ }
93
+
94
+ /**
95
+ * Synchronously seeds every current accessor from the composed agent scope
96
+ * and then runs the callback. Seeding order is fixed: storage, runtime
97
+ * configuration, runtime host, context sources, provider overrides, optional
98
+ * tool resolver, tool source, sandbox controller, context assembler. The
99
+ * optional resolver layer is omitted when no plugin contributed it,
100
+ * exactly like the unseeded installed-tool path today. The scope's
101
+ * assembler is bound to this scope at composition time and seeded here
102
+ * through the context-assembler ALS runtime, so ordered context assembly
103
+ * always resolves this exact scope, even across async suspensions and
104
+ * interleaved scopes.
105
+ *
106
+ * Contributed domain tool payloads are seeded generically from the scope's
107
+ * visible tool contributions carrying `DOMAIN_TOOL_PAYLOAD_FIELD`; an empty
108
+ * map means a composed scope without domain payloads, which disables the
109
+ * unscoped legacy fallbacks for the callback duration.
110
+ */
111
+ export function enterAgentScope<T>(scope: AgentScopeHandle, callback: () => T): T {
112
+ const storage = scope.require(capekStorageKey);
113
+ const configuration = scope.require(capekRuntimeConfigurationKey);
114
+ const host = scope.require(capekRuntimeHostKey);
115
+ const retryPolicy = scope.require(capekRetryPolicyKey);
116
+ const compactionService = scope.require(capekCompactionServiceKey);
117
+ const permissionPolicy = scope.require(capekPermissionPolicyKey);
118
+ const permissionRuntime = scope.require(capekPermissionRuntimeKey);
119
+ const workspacePolicy = scope.require(capekWorkspacePolicyKey);
120
+ const toolOutputPolicy = scope.require(capekToolOutputPolicyKey);
121
+ const contextSources = scope.require(capekContextSourcesKey);
122
+ const providerOverrides = scope.require(capekProviderOverridesKey);
123
+ const workspaceToolDiscovery = scope.require(capekWorkspaceToolDiscoveryKey);
124
+ const sandboxController = scope.require(capekSandboxControllerKey);
125
+ const toolResolver = scope.optional(capekToolResolverKey);
126
+ const contextAssembler = scope.require(capekContextAssemblerKey);
127
+ const goalDomain = scope.optional(capekGoalDomainKey);
128
+
129
+ const domainToolPayloads = new Map<string, DomainToolPayload>();
130
+ for (const tool of scope.listTools()) {
131
+ if (!tool.visible) continue;
132
+ const candidate = tool.definition[DOMAIN_TOOL_PAYLOAD_FIELD];
133
+ if (isDomainToolPayload(candidate) && candidate.name === tool.definition.name) {
134
+ domainToolPayloads.set(candidate.name, candidate);
135
+ }
136
+ }
137
+
138
+ const resolveTools = toolResolver === undefined
139
+ ? (inner: () => T): T => withWorkspaceToolDiscovery(workspaceToolDiscovery, () =>
140
+ withSandboxController(sandboxController, inner))
141
+ : (inner: () => T): T => withToolRegistryResolver(toolResolver, () =>
142
+ withWorkspaceToolDiscovery(workspaceToolDiscovery, () =>
143
+ withSandboxController(sandboxController, inner)));
144
+
145
+ const resolveGoalDomain = goalDomain === undefined
146
+ ? (inner: () => T): T => inner()
147
+ : (inner: () => T): T => withGoalDomain(goalDomain, inner);
148
+
149
+ return withContributedDomainToolPayloads(domainToolPayloads, () =>
150
+ resolveGoalDomain(() =>
151
+ withContextAssembler(contextAssembler, () =>
152
+ withRetryPolicy(retryPolicy, () =>
153
+ withCompactionService(compactionService, () =>
154
+ withAskPermissionPolicy(permissionPolicy, () =>
155
+ withPermissionRuntimeService(permissionRuntime, () =>
156
+ withWorkspaceService(workspacePolicy, () =>
157
+ withToolOutputService(toolOutputPolicy, () =>
158
+ withStorage(storage, () =>
159
+ withRuntimeConfiguration(configuration, () =>
160
+ withRuntimeHost(host, () =>
161
+ withContextSources(contextSources, () =>
162
+ withProviderOverrides(providerOverrides, () =>
163
+ resolveTools(callback)))))))))))))));
164
+ }
165
+
166
+ export type {
167
+ AgentScopeHandle,
168
+ CapekPlugin,
169
+ ProcessScopeHandle,
170
+ ToolDefinition,
171
+ } from '../kernel/types';
@@ -0,0 +1,246 @@
1
+ import {
2
+ validateContextAssemblyData,
3
+ type ContextAssembler,
4
+ type ContextAssemblyData,
5
+ } from '../context/assembler';
6
+ import {
7
+ formatInstructions,
8
+ getAgentDirectory,
9
+ loadInstructions,
10
+ readAgentMemoryFile,
11
+ } from '../context/sources';
12
+ import { buildWorkspaceSystemPrompt } from '../context/workspace';
13
+ import type {
14
+ CapekPlugin,
15
+ ContextBuildContext,
16
+ ContextSectionContribution,
17
+ PluginContext,
18
+ ProvidedContextSection,
19
+ } from '../kernel/types';
20
+ import { loadMemoryInstructions } from '../memory';
21
+ import { getHostGuidance } from '../runtime/host-guidance';
22
+ import { getHostLayout } from '../runtime/host-layout';
23
+ import { getWorkspace } from '../storage/runtime';
24
+ import {
25
+ legacySelfDelegationGuidanceSection,
26
+ legacySessionSearchGuidanceSection,
27
+ } from './legacy-system-message';
28
+ import { capekContextAssemblerKey } from './service-keys';
29
+
30
+ /**
31
+ * C3 ordered context contributions.
32
+ *
33
+ * Registers the exact current system-message sections, wrappers, omission
34
+ * rules, and order as kernel context contributions. Byte parity with the
35
+ * fixed builder is preserved because the guidance constants and section
36
+ * formats are shared with `legacy-system-message`, and the assembler joins
37
+ * provided sections with '\n\n' exactly like the fixed builder's append
38
+ * chain, including the empty-prompt artifact (the system-prompt section
39
+ * always provides a string, even when it is empty).
40
+ *
41
+ * The ordered assembler is bound to its owning scope at composition time
42
+ * through the narrow `PluginContext.buildContext` closure: it never resolves
43
+ * an ambient active scope at build time. Two simultaneous agents never share
44
+ * an assembler view, and a captured assembler keeps building exactly its own
45
+ * scope's sections even while another scope is entered.
46
+ */
47
+
48
+ type SectionContribution = ContextSectionContribution<ContextAssemblyData>;
49
+
50
+ /** Every contribution receives assembly options through the typed narrow
51
+ * data path and validates them, so malformed data fails predictably instead
52
+ * of surfacing unsafe property access. */
53
+ function requiredData(context: ContextBuildContext<ContextAssemblyData>): ContextAssemblyData {
54
+ return validateContextAssemblyData(context.data);
55
+ }
56
+
57
+ export const CURRENT_CONTEXT_SECTION_IDS = [
58
+ 'agent-memory',
59
+ 'agent-user-preferences',
60
+ 'system-prompt',
61
+ 'memory-skills-guidance',
62
+ 'self-delegation',
63
+ 'instructions',
64
+ 'workspace',
65
+ 'workspace-memory',
66
+ 'memory-guidance',
67
+ 'skill-management-guidance',
68
+ 'session-search-guidance',
69
+ ] as const;
70
+
71
+ const CONTEXT_SECTIONS: readonly SectionContribution[] = [
72
+ {
73
+ id: 'system-prompt',
74
+ phase: 'identity',
75
+ order: 30,
76
+ provide: (context) => {
77
+ const data = requiredData(context);
78
+ // Never null: an empty prompt is a real artifact of the fixed builder,
79
+ // and the '\n\n' join must reproduce its exact bytes.
80
+ return data.preconfig.systemPrompt || '';
81
+ },
82
+ },
83
+ {
84
+ id: 'instructions',
85
+ phase: 'instructions',
86
+ order: 10,
87
+ provide: async (context) => {
88
+ const data = requiredData(context);
89
+ const instructions = await loadInstructions(data.workspacePath);
90
+ return formatInstructions(instructions);
91
+ },
92
+ },
93
+ {
94
+ id: 'workspace',
95
+ phase: 'workspace',
96
+ order: 10,
97
+ provide: (context) => {
98
+ const data = requiredData(context);
99
+ return data.workspacePath
100
+ ? buildWorkspaceSystemPrompt(data.workspacePath, data.additionalPaths)
101
+ : null;
102
+ },
103
+ },
104
+ ];
105
+
106
+ /** The legacy memory and skills contributions, kept for facade and legacy
107
+ * compositions that reproduce the fixed builder byte-for-byte. The C5
108
+ * memory and skills domain plugins own these sections in the current Jean2
109
+ * composition; `createContextSectionsPlugin` includes them only when its
110
+ * `includeMemorySkillsSections` option stays at the legacy default. */
111
+ const LEGACY_MEMORY_SKILLS_SECTIONS: readonly SectionContribution[] = [
112
+ {
113
+ id: 'agent-memory',
114
+ phase: 'identity',
115
+ order: 10,
116
+ provide: async (context) => {
117
+ const data = requiredData(context);
118
+ const agentDir = await getAgentDirectory(data.preconfig.id);
119
+ if (!agentDir) return null;
120
+ const memory = await readAgentMemoryFile(data.preconfig.id, 'MEMORY.md');
121
+ return memory ? `<agent_memory>\n${memory}\n</agent_memory>` : null;
122
+ },
123
+ },
124
+ {
125
+ id: 'agent-user-preferences',
126
+ phase: 'identity',
127
+ order: 20,
128
+ provide: async (context) => {
129
+ const data = requiredData(context);
130
+ const agentDir = await getAgentDirectory(data.preconfig.id);
131
+ if (!agentDir) return null;
132
+ const memory = await readAgentMemoryFile(data.preconfig.id, 'USER.md');
133
+ return memory ? `<agent_user_preferences>\n${memory}\n</agent_user_preferences>` : null;
134
+ },
135
+ },
136
+ {
137
+ id: 'memory-skills-guidance',
138
+ phase: 'identity',
139
+ order: 40,
140
+ provide: async (context) => {
141
+ const data = requiredData(context);
142
+ const agentDir = await getAgentDirectory(data.preconfig.id);
143
+ return agentDir ? getHostGuidance().agentMemorySkills : null;
144
+ },
145
+ },
146
+ {
147
+ id: 'workspace-memory',
148
+ phase: 'workspace',
149
+ order: 20,
150
+ provide: async (context) => {
151
+ const data = requiredData(context);
152
+ if (!data.workspaceId) return null;
153
+ const workspace = await getWorkspace(data.workspaceId);
154
+ if (!workspace?.settings?.memory?.enabled || !data.workspacePath) return null;
155
+ return loadMemoryInstructions(getHostLayout().workspaceMemoryDir(data.workspacePath));
156
+ },
157
+ },
158
+ {
159
+ id: 'memory-guidance',
160
+ phase: 'workspace',
161
+ order: 30,
162
+ provide: async (context) => {
163
+ const data = requiredData(context);
164
+ if (!data.workspaceId) return null;
165
+ const workspace = await getWorkspace(data.workspaceId);
166
+ return workspace?.settings?.memory?.enabled && data.workspacePath ? getHostGuidance().memory : null;
167
+ },
168
+ },
169
+ {
170
+ id: 'skill-management-guidance',
171
+ phase: 'workspace',
172
+ order: 40,
173
+ provide: async (context) => {
174
+ const data = requiredData(context);
175
+ if (!data.workspaceId) return null;
176
+ return (await getWorkspace(data.workspaceId))?.settings?.skills?.managementEnabled
177
+ ? getHostGuidance().skillManage
178
+ : null;
179
+ },
180
+ },
181
+ ];
182
+
183
+ /** Builds the ordered section list for one scope. Captured through
184
+ * `PluginContext.buildContext` at composition time so the assembler stays
185
+ * bound to the scope that owns it. */
186
+ export type ContextSectionBuilder = (
187
+ data: ContextAssemblyData,
188
+ ) => Promise<readonly ProvidedContextSection[]>;
189
+
190
+ /** The ordered assembler: validates the typed assembly options, asks its
191
+ * bound scope for the deterministic kernel-ordered sections, and joins them
192
+ * with the fixed builder's exact '\n\n' separator. */
193
+ export function createOrderedContextAssembler(
194
+ id: string,
195
+ buildSections: ContextSectionBuilder,
196
+ ): ContextAssembler {
197
+ return {
198
+ id,
199
+ async build(data: ContextAssemblyData): Promise<string> {
200
+ const validated = validateContextAssemblyData(data);
201
+ const sections = await buildSections(validated);
202
+ return sections.map((section) => section.content).join('\n\n');
203
+ },
204
+ };
205
+ }
206
+
207
+ /** One plugin provides the required context-assembler service and registers
208
+ * every current section. Facade and current compositions install it under
209
+ * their own deterministic plugin id, so diagnostics stay unambiguous. */
210
+ export function createContextSectionsPlugin(
211
+ id: string,
212
+ options: ContextSectionsPluginOptions = {},
213
+ ): CapekPlugin<unknown> {
214
+ const sections: readonly SectionContribution[] = [
215
+ ...CONTEXT_SECTIONS,
216
+ ...(options.includeMemorySkillsSections === false ? [] : LEGACY_MEMORY_SKILLS_SECTIONS),
217
+ ...(options.includeSelfDelegationGuidance === false ? [] : [legacySelfDelegationGuidanceSection]),
218
+ ...(options.includeSessionSearchGuidance === false ? [] : [legacySessionSearchGuidanceSection]),
219
+ ];
220
+ return {
221
+ id,
222
+ scope: 'agent',
223
+ provides: [capekContextAssemblerKey],
224
+ setup(context: PluginContext) {
225
+ context.provide(
226
+ capekContextAssemblerKey,
227
+ createOrderedContextAssembler(id, (data) => context.buildContext(data)),
228
+ );
229
+ for (const section of sections) {
230
+ context.contributeContext(section);
231
+ }
232
+ },
233
+ };
234
+ }
235
+
236
+ /** C5 ownership options. Defaults (omitted) keep the legacy full behavior
237
+ * including the memory, skills, self-delegation, and session-search
238
+ * guidance sections, so facade and legacy compositions stay byte-identical
239
+ * to the fixed builder. The current Jean2 composition passes false for all
240
+ * of them because the memory, skills, subagent, and session-search domain
241
+ * plugins own those sections there. */
242
+ export interface ContextSectionsPluginOptions {
243
+ includeMemorySkillsSections?: boolean;
244
+ includeSelfDelegationGuidance?: boolean;
245
+ includeSessionSearchGuidance?: boolean;
246
+ }
@@ -0,0 +1,14 @@
1
+ import type { CapekPlugin } from '../kernel/types';
2
+ import { DefaultAgentDriver } from '../runtime/default-agent-driver';
3
+ import { capekAgentDriverKey } from './service-keys';
4
+
5
+ export function defaultAgentDriverPlugin(id: string): CapekPlugin<unknown> {
6
+ return {
7
+ id,
8
+ scope: 'agent',
9
+ provides: [capekAgentDriverKey],
10
+ setup(context) {
11
+ context.provide(capekAgentDriverKey, new DefaultAgentDriver());
12
+ },
13
+ };
14
+ }
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Facade agent plugins. One facade agent gets its own agent scope whose
3
+ * plugins bind the per-agent values the facade used to seed directly. Every
4
+ * value stays owned by the agent instance, so two simultaneous facade agents
5
+ * never share storage, configuration, host, tool source, resolver, sandbox
6
+ * controller, or provider overrides.
7
+ */
8
+
9
+ import type { RuntimeConfiguration } from '../configuration/contracts';
10
+ import type { ContextSources } from '../context/sources';
11
+ import type { LoadedTool } from '@capekai/tool';
12
+ import type { CapekPlugin } from '../kernel/types';
13
+ import type { ConnectableProvider } from '../providers/types';
14
+ import type { RuntimeHost } from '../runtime/host';
15
+ import type { SandboxController } from '../sandbox/controller';
16
+ import type { StorageBundle } from '../storage/contracts';
17
+ import type { ToolRegistryResolver } from '../tools/registry';
18
+ import type { WorkspaceToolDiscovery } from '../tools/tool-source';
19
+ import { getSchedulerHost } from '../scheduler/host';
20
+ import { getSessionSearchHost } from '../session-search/host';
21
+ import { createContextSectionsPlugin } from './context-sections';
22
+ import { loadedToolsPlugin } from './loaded-tools';
23
+ import { retryPolicyPlugin } from './retry-policy';
24
+ import { compactionPolicyPlugin } from './compaction-policy';
25
+ import { permissionPolicyPlugin } from './permission-policy';
26
+ import { workspacePolicyPlugin } from './workspace-policy';
27
+ import { toolOutputPolicyPlugin } from './tool-output-policy';
28
+ import { defaultAgentDriverPlugin } from './default-agent-driver';
29
+ import { contributedToolResolverPlugin } from './tool-catalog';
30
+ import {
31
+ contextSourcesValuePlugin,
32
+ providerOverridesValuePlugin,
33
+ runtimeConfigurationValuePlugin,
34
+ runtimeHostValuePlugin,
35
+ sandboxControllerValuePlugin,
36
+ storageValuePlugin,
37
+ toolResolverValuePlugin,
38
+ workspaceToolDiscoveryValuePlugin,
39
+ installedToolRegistryValuePlugin,
40
+ providerRegistryValuePlugin,
41
+ schedulerHostValuePlugin,
42
+ sessionSearchHostValuePlugin,
43
+ } from './value-plugins';
44
+
45
+ export const FACADE_PROCESS_PLUGIN_IDS = [
46
+ 'facade.provider-registry',
47
+ 'facade.installed-tool-registry',
48
+ 'facade.session-search-host',
49
+ 'facade.scheduler-host',
50
+ ] as const;
51
+
52
+ export function facadeProcessPlugins(): readonly CapekPlugin<unknown>[] {
53
+ return [
54
+ providerRegistryValuePlugin('facade.provider-registry'),
55
+ installedToolRegistryValuePlugin('facade.installed-tool-registry'),
56
+ sessionSearchHostValuePlugin('facade.session-search-host', getSessionSearchHost()),
57
+ schedulerHostValuePlugin('facade.scheduler-host', getSchedulerHost()),
58
+ ];
59
+ }
60
+
61
+ export interface FacadeScopeValues {
62
+ storage: StorageBundle;
63
+ configuration: RuntimeConfiguration;
64
+ host: RuntimeHost;
65
+ contextSources: Partial<ContextSources>;
66
+ workspaceToolDiscovery: WorkspaceToolDiscovery;
67
+ /** Optional compatibility resolver. When omitted, the facade
68
+ * composition derives the resolver from the composed scope's effective
69
+ * contributed tool payloads. The explicit value is the rollback
70
+ * path and the C2 test seam. */
71
+ toolResolver?: ToolRegistryResolver;
72
+ /** Host-supplied plugins appended after the facade's own (external tool
73
+ * contributions land here). */
74
+ profilePlugins?: readonly CapekPlugin<unknown>[];
75
+ /** Convenience: loaded tools contributed by one generated plugin, as with
76
+ * the former `createAgent({ tools })` option. Equivalent to passing
77
+ * `loadedToolsPlugin('facade.loaded-tools', tools)` in profilePlugins. */
78
+ loadedTools?: readonly LoadedTool[];
79
+ sandboxController: SandboxController;
80
+ providerOverrides: ReadonlyMap<string, ConnectableProvider>;
81
+ }
82
+
83
+ export const FACADE_AGENT_PLUGIN_IDS = [
84
+ 'facade.storage',
85
+ 'facade.runtime-configuration',
86
+ 'facade.runtime-host',
87
+ 'facade.agent-driver',
88
+ 'facade.retry-policy',
89
+ 'facade.compaction-policy',
90
+ 'facade.permission-policy',
91
+ 'facade.workspace-policy',
92
+ 'facade.tool-output-policy',
93
+ 'facade.context-sources',
94
+ 'facade.context-sections',
95
+ 'facade.workspace-tool-discovery',
96
+ 'facade.tool-resolver',
97
+ 'facade.sandbox-controller',
98
+ 'facade.provider-overrides',
99
+ ] as const;
100
+
101
+ export function createFacadeAgentPlugins(values: FacadeScopeValues): readonly CapekPlugin<unknown>[] {
102
+ return [
103
+ storageValuePlugin('facade.storage', values.storage),
104
+ runtimeConfigurationValuePlugin('facade.runtime-configuration', values.configuration),
105
+ runtimeHostValuePlugin('facade.runtime-host', values.host),
106
+ defaultAgentDriverPlugin('facade.agent-driver'),
107
+ // C6: the agent scope owns the retry policy (and its circuit state)
108
+ // instead of the facade's pre-C6 per-agent withRetryCircuitState wrap.
109
+ retryPolicyPlugin('facade.retry-policy'),
110
+ compactionPolicyPlugin('facade.compaction-policy'),
111
+ permissionPolicyPlugin('facade.permission-policy'),
112
+ workspacePolicyPlugin('facade.workspace-policy'),
113
+ toolOutputPolicyPlugin('facade.tool-output-policy'),
114
+ contextSourcesValuePlugin('facade.context-sources', values.contextSources),
115
+ // Facade context parity: the facade keeps the legacy self-delegation and
116
+ // session-search guidance sections (the C5 domain plugins own them only
117
+ // in the current Jean2 composition), so context-sections stays at its
118
+ // pre-C5 defaults here.
119
+ createContextSectionsPlugin('facade.context-sections'),
120
+ workspaceToolDiscoveryValuePlugin('facade.workspace-tool-discovery', values.workspaceToolDiscovery),
121
+ values.toolResolver === undefined
122
+ ? contributedToolResolverPlugin('facade.tool-resolver')
123
+ : toolResolverValuePlugin('facade.tool-resolver', values.toolResolver),
124
+ sandboxControllerValuePlugin('facade.sandbox-controller', values.sandboxController),
125
+ providerOverridesValuePlugin('facade.provider-overrides', values.providerOverrides),
126
+ ...(values.loadedTools?.length ? [loadedToolsPlugin('facade.loaded-tools', values.loadedTools)] : []),
127
+ ...(values.profilePlugins ?? []),
128
+ ];
129
+ }
@@ -0,0 +1,82 @@
1
+ import { serviceKey } from '../kernel/service-key';
2
+ import type { CapekPlugin, PluginContext } from '../kernel/types';
3
+ import type { Session } from '@capekai/types';
4
+ import type { RuntimeHost } from '../runtime/host';
5
+ import type { StorageBundle } from '../storage/contracts';
6
+ import type { GoalDomainService } from '../goals/service';
7
+ import {
8
+ evaluateGoalWithDeps,
9
+ runGoalLoopWithDeps,
10
+ type GoalEvaluatorDeps,
11
+ type GoalLoopDeps,
12
+ } from '../goals';
13
+ import {
14
+ capekOrchestratorSessionKey,
15
+ capekRuntimeHostKey,
16
+ capekStorageKey,
17
+ type OrchestratorSessionContract,
18
+ } from './service-keys';
19
+
20
+ /**
21
+ * C5 goal domain plugin. Owns the agent-scoped `capek.goal-domain` service:
22
+ * goal evaluation and the persistent goal loop run directive over the
23
+ * scope-captured storage bundle and the shared `capek.orchestrator-session`
24
+ * contract. No model-facing goal tool exists in the product (goal mode is a
25
+ * client session directive through `handleChat`), so the domain contributes
26
+ * no tool and no context section. Live adoption: `core/chat-handler.ts`
27
+ * resolves this service through `getGoalDomain()`; the unscoped fallback in
28
+ * `goals/service.ts` keeps the module path for uncomposed consumers.
29
+ */
30
+
31
+ export const CURRENT_GOAL_DOMAIN_PLUGIN_ID = 'current.goal-domain';
32
+
33
+ export type { GoalDomainService };
34
+
35
+ export const capekGoalDomainKey = serviceKey<GoalDomainService>(
36
+ 'capek.goal-domain',
37
+ 'agent',
38
+ );
39
+
40
+ function sessionUpdatedBroadcast(host: RuntimeHost): (session: Session) => void {
41
+ return (session) => {
42
+ const delivery = {
43
+ event: { kind: 'session', action: 'updated', session } as const,
44
+ audience: { scope: 'global' } as const,
45
+ };
46
+ host.delivery.observe?.(delivery);
47
+ host.delivery.emit(delivery);
48
+ };
49
+ }
50
+
51
+ export function goalDomainPlugin(id: string): CapekPlugin<unknown> {
52
+ return {
53
+ id,
54
+ scope: 'agent',
55
+ provides: [capekGoalDomainKey],
56
+ requires: [capekStorageKey, capekOrchestratorSessionKey, capekRuntimeHostKey],
57
+ setup(context: PluginContext) {
58
+ const storage: StorageBundle = context.require(capekStorageKey);
59
+ const orchestrator: OrchestratorSessionContract = context.require(capekOrchestratorSessionKey);
60
+ const host: RuntimeHost = context.require(capekRuntimeHostKey);
61
+
62
+ const evaluatorDeps: GoalEvaluatorDeps = {
63
+ listTranscript: (sessionId) => storage.conversation.listMessagesWithParts(sessionId),
64
+ orchestrator,
65
+ };
66
+
67
+ const loopDeps: GoalLoopDeps = {
68
+ getSession: (sessionId) => storage.conversation.getSession(sessionId),
69
+ updateSession: (sessionId, updates) => storage.conversation.updateSession(sessionId, updates),
70
+ evaluate: (evaluateOptions) => evaluateGoalWithDeps(evaluateOptions, evaluatorDeps),
71
+ broadcastSessionUpdatedDefault: sessionUpdatedBroadcast(host),
72
+ };
73
+
74
+ const service: GoalDomainService = {
75
+ evaluateGoal: (options) => evaluateGoalWithDeps(options, evaluatorDeps),
76
+ runGoalLoop: (options) => runGoalLoopWithDeps(options, loopDeps),
77
+ };
78
+
79
+ context.provide(capekGoalDomainKey, service);
80
+ },
81
+ };
82
+ }