@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.
- package/README.md +12 -0
- package/package.json +105 -0
- package/src/adapters/ai-sdk.ts +84 -0
- package/src/compaction/contracts.ts +82 -0
- package/src/compaction/executor.ts +161 -0
- package/src/compaction/policy.ts +318 -0
- package/src/compaction/recovery.ts +139 -0
- package/src/compaction/task.ts +540 -0
- package/src/configuration/contracts.ts +58 -0
- package/src/configuration/defaults.ts +27 -0
- package/src/configuration/runtime.ts +42 -0
- package/src/configuration/single-model.ts +75 -0
- package/src/context/assembler.ts +112 -0
- package/src/context/index.ts +2 -0
- package/src/context/sources.ts +119 -0
- package/src/context/workspace.ts +63 -0
- package/src/core/agent.ts +401 -0
- package/src/core/build-tools.ts +139 -0
- package/src/core/chat-handler.ts +858 -0
- package/src/core/error-handling.ts +18 -0
- package/src/core/fork.ts +103 -0
- package/src/core/interrupt.ts +192 -0
- package/src/core/message-utils.ts +261 -0
- package/src/core/model-utils.ts +149 -0
- package/src/core/part-utils.ts +88 -0
- package/src/core/provider-utils.ts +67 -0
- package/src/core/revert.ts +46 -0
- package/src/core/step-handlers.ts +157 -0
- package/src/core/stream/finalization.ts +65 -0
- package/src/core/stream/stream-config.ts +82 -0
- package/src/core/stream-handlers.ts +242 -0
- package/src/core/structured-output.ts +68 -0
- package/src/core/tool-builders/agent-tools.ts +71 -0
- package/src/core/tool-builders/external-tools.ts +179 -0
- package/src/core/tool-builders/types.ts +16 -0
- package/src/core/tool-builders/workspace-tools.ts +293 -0
- package/src/core/tool-capabilities.ts +65 -0
- package/src/goals/evaluator.ts +171 -0
- package/src/goals/index.ts +3 -0
- package/src/goals/loop.ts +167 -0
- package/src/goals/service.ts +39 -0
- package/src/index.ts +10 -0
- package/src/internal/ask-authority.ts +29 -0
- package/src/internal/composition.ts +44 -0
- package/src/internal/configuration.ts +22 -0
- package/src/internal/execution.ts +108 -0
- package/src/internal/hosts.ts +64 -0
- package/src/internal/plugins.ts +71 -0
- package/src/internal/providers.ts +32 -0
- package/src/internal/sandbox.ts +19 -0
- package/src/internal/tools.ts +48 -0
- package/src/internal/workspace.ts +25 -0
- package/src/kernel/diagnostics.ts +249 -0
- package/src/kernel/errors.ts +120 -0
- package/src/kernel/events.ts +82 -0
- package/src/kernel/index.ts +72 -0
- package/src/kernel/kernel.ts +62 -0
- package/src/kernel/lifecycle.ts +72 -0
- package/src/kernel/plugin.ts +218 -0
- package/src/kernel/registry.ts +493 -0
- package/src/kernel/scope.ts +776 -0
- package/src/kernel/service-key.ts +19 -0
- package/src/kernel/types.ts +317 -0
- package/src/memory/index.ts +2 -0
- package/src/memory/memory-tool.ts +75 -0
- package/src/memory/registry.ts +172 -0
- package/src/permission/ask-user-api.ts +70 -0
- package/src/permission/contracts.ts +135 -0
- package/src/permission/permission-request-manager.ts +58 -0
- package/src/permission/policy.ts +277 -0
- package/src/permission/runtime.ts +612 -0
- package/src/plugins/compaction-policy.ts +46 -0
- package/src/plugins/compose.ts +171 -0
- package/src/plugins/context-sections.ts +246 -0
- package/src/plugins/default-agent-driver.ts +14 -0
- package/src/plugins/facade-plugins.ts +129 -0
- package/src/plugins/goal-domain.ts +82 -0
- package/src/plugins/legacy-system-message.ts +152 -0
- package/src/plugins/loaded-tools.ts +23 -0
- package/src/plugins/memory-domain.ts +264 -0
- package/src/plugins/orchestrator-session.ts +29 -0
- package/src/plugins/permission-policy.ts +49 -0
- package/src/plugins/retry-policy.ts +28 -0
- package/src/plugins/scheduler-domain.ts +192 -0
- package/src/plugins/service-keys.ts +294 -0
- package/src/plugins/session-search-domain.ts +238 -0
- package/src/plugins/skills-domain.ts +272 -0
- package/src/plugins/subagent-domain.ts +287 -0
- package/src/plugins/tool-catalog.ts +78 -0
- package/src/plugins/tool-output-policy.ts +52 -0
- package/src/plugins/value-plugins.ts +150 -0
- package/src/plugins/workflow-domain.ts +198 -0
- package/src/plugins/workspace-policy.ts +37 -0
- package/src/providers/registry.ts +63 -0
- package/src/providers/types.ts +44 -0
- package/src/retry/policy.ts +282 -0
- package/src/retry/stream-chat.ts +312 -0
- package/src/runtime/agent-runtime.ts +83 -0
- package/src/runtime/default-agent-driver.ts +23 -0
- package/src/runtime/domain-tool-source.ts +156 -0
- package/src/runtime/events.ts +61 -0
- package/src/runtime/host-dependencies.ts +71 -0
- package/src/runtime/host-guidance.ts +22 -0
- package/src/runtime/host-layout.ts +23 -0
- package/src/runtime/host.ts +129 -0
- package/src/runtime/standalone-host.ts +118 -0
- package/src/sandbox/controller.ts +204 -0
- package/src/sandbox/model.ts +305 -0
- package/src/sandbox/provider.ts +53 -0
- package/src/sandbox/types.ts +110 -0
- package/src/scheduler/host.ts +22 -0
- package/src/scheduler/scheduler-tool.ts +172 -0
- package/src/session-search/host.ts +56 -0
- package/src/session-search/index.ts +23 -0
- package/src/session-search/session-search-tool.ts +151 -0
- package/src/skills/index.ts +3 -0
- package/src/skills/registry.ts +63 -0
- package/src/skills/skill-manage-tool.ts +205 -0
- package/src/skills/skill-tool.ts +42 -0
- package/src/storage/contracts.ts +159 -0
- package/src/storage/memory.ts +321 -0
- package/src/storage/options.ts +75 -0
- package/src/storage/runtime.ts +115 -0
- package/src/storage/sqlite-tool-output-artifacts.ts +106 -0
- package/src/storage/sqlite.ts +321 -0
- package/src/storage/tool-output-artifacts.ts +75 -0
- package/src/storage.ts +31 -0
- package/src/subagent/child-session.ts +282 -0
- package/src/subagent/guidance.ts +8 -0
- package/src/subagent/policy.ts +198 -0
- package/src/subagent/task-tool.ts +584 -0
- package/src/tool-output/contracts.ts +111 -0
- package/src/tool-output/policy.ts +410 -0
- package/src/tool.ts +1 -0
- package/src/tools/executor.ts +258 -0
- package/src/tools/install-manifest.ts +40 -0
- package/src/tools/llm-api.ts +77 -0
- package/src/tools/registry.ts +206 -0
- package/src/tools/tool-artifact.ts +182 -0
- package/src/tools/tool-source.ts +53 -0
- package/src/utils/errors.ts +334 -0
- package/src/utils/strip-visualization.ts +50 -0
- package/src/workflow/decomposer.ts +139 -0
- package/src/workflow/execution.ts +523 -0
- package/src/workflow/orchestrator-session.ts +161 -0
- package/src/workflow/synthesizer.ts +130 -0
- package/src/workspace/contracts.ts +135 -0
- 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,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
|
+
}
|