@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,75 @@
1
+ import type { RuntimeConfiguration } from './contracts';
2
+ import { createDefaultRuntimeConfiguration } from './defaults';
3
+
4
+ export interface ModelSpecifierSelection {
5
+ modelId: string;
6
+ providerId: string;
7
+ }
8
+
9
+ /** Splits a `provider/model` string on the first separator; the provider
10
+ * part is kept as-is (matching `parseModelSpecifier` for known provider
11
+ * ids); a bare model gets no provider. Self-contained so the
12
+ * configuration leaf concern stays import-clean. */
13
+ export function resolveModelSpecifier(model: string): ModelSpecifierSelection {
14
+ const separator = model.indexOf('/');
15
+ if (separator <= 0 || separator === model.length - 1) {
16
+ return { modelId: model, providerId: 'openai' };
17
+ }
18
+ return {
19
+ providerId: model.slice(0, separator),
20
+ modelId: model.slice(separator + 1),
21
+ };
22
+ }
23
+
24
+ /** A starter `RuntimeConfiguration` for exactly one model string: wraps the
25
+ * package defaults, answers `findModel` for the given selection with a
26
+ * synthetic 128k entry, exposes it through `getModelsConfig`, and resolves
27
+ * API keys from the conventional `<PROVIDER>_API_KEY` env vars. Copy and
28
+ * extend when you outgrow one model. */
29
+ export function createSingleModelConfiguration(selection: ModelSpecifierSelection): RuntimeConfiguration {
30
+ const defaults = createDefaultRuntimeConfiguration();
31
+ return {
32
+ ...defaults,
33
+ findModel(modelId, providerId) {
34
+ const found = defaults.findModel(modelId, providerId);
35
+ if (found) return found;
36
+ if (modelId !== selection.modelId || (providerId && providerId !== selection.providerId)) return undefined;
37
+ return {
38
+ id: selection.modelId,
39
+ name: selection.modelId,
40
+ contextWindow: 128_000,
41
+ maxOutputTokens: 16_384,
42
+ tier: 'standard',
43
+ providerId: selection.providerId,
44
+ providerName: selection.providerId,
45
+ };
46
+ },
47
+ getModelsConfig() {
48
+ const base = defaults.getModelsConfig();
49
+ const providers = base.providers.map((provider) => ({ ...provider, models: [...provider.models] }));
50
+ let provider = providers.find((candidate) => candidate.id === selection.providerId);
51
+ if (!provider) {
52
+ provider = { id: selection.providerId, name: selection.providerId, models: [] };
53
+ providers.push(provider);
54
+ }
55
+ if (!provider.models.some((candidate) => candidate.id === selection.modelId)) {
56
+ provider.models.push({
57
+ id: selection.modelId,
58
+ name: selection.modelId,
59
+ contextWindow: 128_000,
60
+ maxOutputTokens: 16_384,
61
+ tier: 'standard',
62
+ });
63
+ }
64
+ return {
65
+ providers,
66
+ defaultModel: selection.modelId,
67
+ defaultProvider: selection.providerId,
68
+ };
69
+ },
70
+ getApiKey(providerId) {
71
+ const conventional = process.env[`${providerId.toUpperCase().replaceAll('-', '_')}_API_KEY`];
72
+ return conventional ?? defaults.getApiKey(providerId);
73
+ },
74
+ };
75
+ }
@@ -0,0 +1,112 @@
1
+ import { AsyncLocalStorage } from 'node:async_hooks';
2
+ import type { Preconfig } from '@capekai/types';
3
+
4
+ /**
5
+ * Context assembler contract and runtime accessors.
6
+ *
7
+ * The runtime core depends on this contract only: `getContextAssembler()`
8
+ * resolves the assembler seeded for the active agent scope and `build()` is
9
+ * the single entry point for ordered context assembly. The ordered
10
+ * implementation lives in the plugin layer; the legacy fixed builder stays a
11
+ * migration adapter and is never imported by the runtime core.
12
+ */
13
+
14
+ /** Assembly options passed to every ordered context build. This is the exact
15
+ * option set the fixed builder consumed; no new task content is allowed. */
16
+ export interface ContextAssemblyData {
17
+ preconfig: Preconfig;
18
+ workspacePath?: string;
19
+ workspaceId?: string;
20
+ additionalPaths?: string[];
21
+ selfDelegationAvailable?: boolean;
22
+ }
23
+
24
+ /** The required runtime service contract for context assembly. */
25
+ export interface ContextAssembler {
26
+ readonly id: string;
27
+ build(data: ContextAssemblyData): Promise<string>;
28
+ }
29
+
30
+ /** Malformed assembly options fail predictably with this error instead of
31
+ * surfacing unsafe property access deep inside a section provider. */
32
+ export class ContextAssemblyDataError extends Error {
33
+ constructor(message: string) {
34
+ super(message);
35
+ this.name = new.target.name;
36
+ }
37
+ }
38
+
39
+ /** Validates the typed assembly options contract. Returns the data unchanged
40
+ * so callers can pass the validated value onward without casts. */
41
+ export function validateContextAssemblyData(data: unknown): ContextAssemblyData {
42
+ if (typeof data !== 'object' || data === null) {
43
+ throw new ContextAssemblyDataError('context assembly data must be an object');
44
+ }
45
+ const candidate = data as Partial<ContextAssemblyData>;
46
+ const preconfig = candidate.preconfig;
47
+ if (typeof preconfig !== 'object' || preconfig === null) {
48
+ throw new ContextAssemblyDataError('context assembly data preconfig must be an object');
49
+ }
50
+ if (typeof (preconfig as { id?: unknown }).id !== 'string') {
51
+ throw new ContextAssemblyDataError('context assembly data preconfig must declare a string id');
52
+ }
53
+ const systemPrompt = (preconfig as { systemPrompt?: unknown }).systemPrompt;
54
+ if (systemPrompt !== undefined && typeof systemPrompt !== 'string') {
55
+ throw new ContextAssemblyDataError(
56
+ 'context assembly data preconfig systemPrompt must be a string when present',
57
+ );
58
+ }
59
+ for (const key of ['workspacePath', 'workspaceId'] as const) {
60
+ const value = candidate[key];
61
+ if (value !== undefined && typeof value !== 'string') {
62
+ throw new ContextAssemblyDataError(
63
+ `context assembly data ${key} must be a string when present`,
64
+ );
65
+ }
66
+ }
67
+ const additionalPaths = candidate.additionalPaths;
68
+ if (
69
+ additionalPaths !== undefined
70
+ && (!Array.isArray(additionalPaths) || additionalPaths.some((entry) => typeof entry !== 'string'))
71
+ ) {
72
+ throw new ContextAssemblyDataError(
73
+ 'context assembly data additionalPaths must be an array of strings when present',
74
+ );
75
+ }
76
+ const selfDelegationAvailable = candidate.selfDelegationAvailable;
77
+ if (selfDelegationAvailable !== undefined && typeof selfDelegationAvailable !== 'boolean') {
78
+ throw new ContextAssemblyDataError(
79
+ 'context assembly data selfDelegationAvailable must be a boolean when present',
80
+ );
81
+ }
82
+ return candidate as ContextAssemblyData;
83
+ }
84
+
85
+ const scopedAssembler = new AsyncLocalStorage<ContextAssembler>();
86
+
87
+ /** Fallback used when no composed scope has seeded an assembler. The plugin
88
+ * layer installs the fixed legacy builder adapter here, so consumers that
89
+ * run outside `enterAgentScope` (the current Jean2 server path) keep the
90
+ * exact pre-C3 behavior until they adopt the composed entry. */
91
+ let defaultAssembler: ContextAssembler | undefined;
92
+
93
+ export function setDefaultContextAssembler(assembler: ContextAssembler): void {
94
+ defaultAssembler = assembler;
95
+ }
96
+
97
+ /** Resolves the assembler seeded for the active agent scope, falling back to
98
+ * the default assembler for consumers that run outside a composed scope. */
99
+ export function getContextAssembler(): ContextAssembler {
100
+ const assembler = scopedAssembler.getStore() ?? defaultAssembler;
101
+ if (assembler === undefined) {
102
+ throw new Error(
103
+ 'no ContextAssembler is active and no default assembler is installed',
104
+ );
105
+ }
106
+ return assembler;
107
+ }
108
+
109
+ /** Seeds the active agent scope's assembler for the callback duration. */
110
+ export function withContextAssembler<T>(assembler: ContextAssembler, callback: () => T): T {
111
+ return scopedAssembler.run(assembler, callback);
112
+ }
@@ -0,0 +1,2 @@
1
+ export * from './sources';
2
+ export * from './workspace';
@@ -0,0 +1,119 @@
1
+ import { AsyncLocalStorage } from 'node:async_hooks';
2
+ import { existsSync } from 'fs';
3
+ import { readFile } from 'fs/promises';
4
+ import { join } from 'path';
5
+ import type { Preconfig } from '@capekai/types';
6
+
7
+ export interface LoadedInstructions {
8
+ global: string | null;
9
+ project: string | null;
10
+ }
11
+
12
+ export interface PreconfigSource {
13
+ get(id: string): Promise<Preconfig | null>;
14
+ getDefault(): Promise<Preconfig | null>;
15
+ getForAgent(id: string): Promise<Preconfig | null>;
16
+ list(): Promise<Preconfig[]>;
17
+ listSubagents(): Promise<Preconfig[]>;
18
+ }
19
+
20
+ export interface AgentSource {
21
+ getDirectory(id: string): Promise<string | null>;
22
+ readMemoryFile(id: string, filename: 'USER.md' | 'MEMORY.md'): Promise<string | null>;
23
+ }
24
+
25
+ export interface InstructionSource {
26
+ getGlobalPath(): string | undefined;
27
+ }
28
+
29
+ export interface ContextSources {
30
+ preconfigs: PreconfigSource;
31
+ agents: AgentSource;
32
+ instructions: InstructionSource;
33
+ }
34
+
35
+ const defaultPreconfigs: PreconfigSource = {
36
+ async get() { return null; },
37
+ async getDefault() { return null; },
38
+ async getForAgent() { return null; },
39
+ async list() { return []; },
40
+ async listSubagents() { return []; },
41
+ };
42
+
43
+ const defaultAgents: AgentSource = {
44
+ async getDirectory() { return null; },
45
+ async readMemoryFile() { return null; },
46
+ };
47
+
48
+ const defaultInstructions: InstructionSource = { getGlobalPath: () => undefined };
49
+ let preconfigs = defaultPreconfigs;
50
+ let agents = defaultAgents;
51
+ let instructions = defaultInstructions;
52
+ const scopedSources = new AsyncLocalStorage<ContextSources>();
53
+
54
+ function activeSources(): ContextSources {
55
+ return scopedSources.getStore() ?? { preconfigs, agents, instructions };
56
+ }
57
+
58
+ export function getContextSources(): ContextSources {
59
+ return activeSources();
60
+ }
61
+
62
+ export function withContextSources<T>(
63
+ value: Partial<ContextSources>,
64
+ callback: () => T,
65
+ ): T {
66
+ return scopedSources.run({
67
+ preconfigs: value.preconfigs ?? defaultPreconfigs,
68
+ agents: value.agents ?? defaultAgents,
69
+ instructions: value.instructions ?? defaultInstructions,
70
+ }, callback);
71
+ }
72
+
73
+ export function configurePreconfigSource(value?: PreconfigSource): void {
74
+ preconfigs = value ?? defaultPreconfigs;
75
+ }
76
+
77
+ export function configureAgentSource(value?: AgentSource): void {
78
+ agents = value ?? defaultAgents;
79
+ }
80
+
81
+ export function configureInstructionSource(value?: InstructionSource): void {
82
+ instructions = value ?? defaultInstructions;
83
+ }
84
+
85
+ export const getPreconfig = (id: string) => activeSources().preconfigs.get(id);
86
+ export const getDefaultPreconfig = () => activeSources().preconfigs.getDefault();
87
+ export const getPreconfigOrAgent = (id: string) => activeSources().preconfigs.getForAgent(id);
88
+ export const listPreconfigs = () => activeSources().preconfigs.list();
89
+ export const listSubagentPreconfigs = () => activeSources().preconfigs.listSubagents();
90
+ export const getAgentDirectory = (id: string) => activeSources().agents.getDirectory(id);
91
+ export const readAgentMemoryFile = (id: string, filename: 'USER.md' | 'MEMORY.md') =>
92
+ activeSources().agents.readMemoryFile(id, filename);
93
+
94
+ async function readTrimmed(path: string | undefined, label: string): Promise<string | null> {
95
+ if (!path || !existsSync(path)) return null;
96
+ try {
97
+ const content = (await readFile(path, 'utf-8')).trim();
98
+ return content || null;
99
+ } catch (error: unknown) {
100
+ console.error(`Failed to read ${label} instructions:`, error);
101
+ return null;
102
+ }
103
+ }
104
+
105
+ export async function loadInstructions(workspacePath?: string): Promise<LoadedInstructions> {
106
+ return {
107
+ global: await readTrimmed(activeSources().instructions.getGlobalPath(), 'global'),
108
+ project: workspacePath
109
+ ? await readTrimmed(join(workspacePath, 'AGENTS.md'), 'project')
110
+ : null,
111
+ };
112
+ }
113
+
114
+ export function formatInstructions(value: LoadedInstructions): string | null {
115
+ const sections: string[] = [];
116
+ if (value.global) sections.push(`<instructions source="global">\n${value.global}\n</instructions>`);
117
+ if (value.project) sections.push(`<instructions source="project">\n${value.project}\n</instructions>`);
118
+ return sections.length > 0 ? sections.join('\n\n') : null;
119
+ }
@@ -0,0 +1,63 @@
1
+ export function buildWorkspaceSystemPrompt(
2
+ workspacePath: string,
3
+ additionalPaths: string[] = [],
4
+ ): string {
5
+ let additionalSection = '';
6
+ if (additionalPaths.length > 0) {
7
+ additionalSection = `
8
+
9
+ ### Additional Paths
10
+
11
+ This workspace has additional directories you have full access to:
12
+ ${additionalPaths.map((path) => `- ${path}`).join('\n')}
13
+
14
+ You can read, write, search, and explore files in these directories using absolute paths.
15
+ Relative paths still resolve from the primary workspace. Use absolute paths for additional paths.
16
+
17
+ `;
18
+ }
19
+
20
+ return `
21
+ <workspace>
22
+ ## Working Directory
23
+
24
+ You are operating in: ${workspacePath}
25
+
26
+ ### Path Resolution
27
+
28
+ All file operations support three path types:
29
+
30
+ 1. **Relative Paths** (RECOMMENDED for workspace files)
31
+ - Input: "src/app.ts"
32
+ - Resolves to: "${workspacePath}/src/app.ts"
33
+
34
+ 2. **Absolute Paths**
35
+ - Input: "${workspacePath}/src/app.ts"
36
+ - Used as-is
37
+
38
+ 3. **Home Paths**
39
+ - Input: "~/Documents/file.txt"
40
+ - Expands relative to the current user's home directory
41
+
42
+ ### Default Behaviors
43
+
44
+ - **File Operations**: Relative paths resolve from workspace root
45
+ - **Shell Commands**: Execute from workspace root by default
46
+ - **Search Operations**: Scoped to workspace by default
47
+ ${additionalSection}### Security
48
+
49
+ Operations outside the workspace directory require explicit approval:
50
+ - Writing outside workspace: Requires approval
51
+ - Reading outside workspace: Requires approval (configurable)
52
+ - System directories: Blocked
53
+
54
+ ### Best Practices
55
+
56
+ 1. Use relative paths for files within the workspace
57
+ 2. Use the \`cwd\` parameter in shell commands instead of \`cd\`
58
+ 3. When in doubt, use absolute paths
59
+
60
+ Current workspace: ${workspacePath}
61
+ </workspace>
62
+ `.trim();
63
+ }