mirror of
https://github.com/n8n-io/n8n.git
synced 2026-10-17 21:49:36 +02:00
prompt nudges
This commit is contained in:
parent
af01bf6062
commit
bc7ef3fb5b
|
|
@ -176,7 +176,8 @@ continue one, pass the exact `sessionId` from an earlier
|
|||
|
||||
When setup is finished and the target Agent is runnable, call
|
||||
`agent_builder_call_agent` once with a representative message to verify that it
|
||||
works as intended. If the test exposes errors, tell the user what failed and
|
||||
works as intended. If the user skipped a required credential or the model,
|
||||
do not call it. The test cannot pass until that setup is done. If the test exposes errors, tell the user what failed and
|
||||
ask whether you should fix the problems before you change the Agent.
|
||||
|
||||
Use `agent_builder_call_agent` to verify the Agent's behavior, not its channel
|
||||
|
|
@ -284,9 +285,11 @@ confirm those selections before you finish the build.
|
|||
discovery lookups the plan names: `agent-context({ type: "integrations" })`
|
||||
for a chat channel, and one `agent-context({ type: "integrations", queries })`
|
||||
call for each callable service.
|
||||
4. In the next response, call `agent_builder_get_node_types` for the node
|
||||
results you will use, and resolve the model in the same response (see
|
||||
Model selection).
|
||||
4. In the next response, call `agent_builder_get_node_types` once for every
|
||||
node and operation the build uses, and resolve the model in the same
|
||||
response (see Model selection). Use the node results from the discovery
|
||||
lookups. Do not search again with `agent_builder_search_nodes` for a service
|
||||
that a lookup already returned, and do not fetch one service per call.
|
||||
5. Write the config once, with everything discovery found: the name, the
|
||||
target-Agent `instructions`, the model, every node tool, and the drafts for
|
||||
setup that the user must finish. Do not write a partial config and patch it
|
||||
|
|
@ -316,7 +319,10 @@ confirm those selections before you finish the build.
|
|||
`agent_builder_call_agent` once with a representative message before your
|
||||
final response. If the test exposes errors, report them and ask whether you
|
||||
should fix them. Do not claim the Agent is ready without this test, or
|
||||
without an explanation of why it could not run.
|
||||
without an explanation of why it could not run. The Agent is not runnable
|
||||
when `agent_builder_finish_setup` returned a required credential or model
|
||||
as skipped or dismissed. Then do not call `agent_builder_call_agent`. Say
|
||||
that the test runs after that setup is complete.
|
||||
11. When the user asks to publish, activate, or make the Agent live or usable,
|
||||
call `agent_builder_publish_agent`. Never tell them to click Publish in the
|
||||
editor. Do not publish without that intent. Use
|
||||
|
|
|
|||
|
|
@ -266,7 +266,7 @@ describe('materializeRuntimeSkillsIntoWorkspace', () => {
|
|||
expect(materialized.skills.map(({ id }) => id)).not.toContain('post-build-flow');
|
||||
});
|
||||
|
||||
it('materializes skills into the workspace before load_skill reads them', async () => {
|
||||
it('materializes skills into the workspace when load_skill first runs', async () => {
|
||||
const source = loadInstanceAiRuntimeSkillSource();
|
||||
const { workspace, writes, executeCommand } = createMockWorkspace();
|
||||
const runtimeSource = createLazyWorkspaceRuntimeSkillSource({
|
||||
|
|
@ -277,6 +277,7 @@ describe('materializeRuntimeSkillsIntoWorkspace', () => {
|
|||
const loadTool = createSkillLoadTool(runtimeSource);
|
||||
|
||||
const result = await loadTool.handler?.({ skillId: 'data-table-manager' }, {});
|
||||
await runtimeSource.whenFilesWritten?.();
|
||||
const root = '/home/daytona/workspace';
|
||||
const skillDir = `${root}/${SANDBOX_RUNTIME_SKILLS_DIR}/data-table-manager`;
|
||||
const skillPath = `${skillDir}/SKILL.md`;
|
||||
|
|
@ -292,6 +293,57 @@ describe('materializeRuntimeSkillsIntoWorkspace', () => {
|
|||
expect(executeCommand).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('returns the skill before the workspace writes finish', async () => {
|
||||
const source = loadInstanceAiRuntimeSkillSource();
|
||||
const { workspace, writes, writeFile } = createMockWorkspace();
|
||||
let releaseWrites: () => void = () => {};
|
||||
const writesReleased = new Promise<void>((resolve) => {
|
||||
releaseWrites = resolve;
|
||||
});
|
||||
writeFile.mockImplementation(async (path: string, content: string | Buffer) => {
|
||||
await writesReleased;
|
||||
writes.set(path, Buffer.isBuffer(content) ? content.toString('utf-8') : content);
|
||||
});
|
||||
const runtimeSource = createLazyWorkspaceRuntimeSkillSource({
|
||||
logger: mockLogger,
|
||||
source,
|
||||
workspace,
|
||||
});
|
||||
|
||||
const result = await createSkillLoadTool(runtimeSource).handler?.(
|
||||
{ skillId: 'data-table-manager' },
|
||||
{},
|
||||
);
|
||||
|
||||
const skillPath = `/home/daytona/workspace/${SANDBOX_RUNTIME_SKILLS_DIR}/data-table-manager/SKILL.md`;
|
||||
expect(skillLoadText(result)).toContain(skillPath);
|
||||
expect(writes.has(skillPath)).toBe(false);
|
||||
|
||||
let filesWritten = false;
|
||||
const pending = runtimeSource.whenFilesWritten?.().then(() => {
|
||||
filesWritten = true;
|
||||
});
|
||||
await Promise.resolve();
|
||||
expect(filesWritten).toBe(false);
|
||||
|
||||
releaseWrites();
|
||||
await pending;
|
||||
expect(writes.get(skillPath)).toContain('workflow_builder_data_tables');
|
||||
});
|
||||
|
||||
it('does not wait for skill files when no skill was loaded', async () => {
|
||||
const { workspace, writeFile } = createMockWorkspace();
|
||||
const runtimeSource = createLazyWorkspaceRuntimeSkillSource({
|
||||
logger: mockLogger,
|
||||
source: loadInstanceAiRuntimeSkillSource(),
|
||||
workspace,
|
||||
});
|
||||
|
||||
await runtimeSource.whenFilesWritten?.();
|
||||
|
||||
expect(writeFile).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('uses prebaked runtime skills when the manifest matches the source hash', async () => {
|
||||
const source = loadInstanceAiRuntimeSkillSource();
|
||||
const { workspace, writes, executeCommand, writeFile } = createMockWorkspace();
|
||||
|
|
@ -309,6 +361,7 @@ describe('materializeRuntimeSkillsIntoWorkspace', () => {
|
|||
});
|
||||
const loadTool = createSkillLoadTool(runtimeSource);
|
||||
const result = await loadTool.handler?.({ skillId: 'data-table-manager' }, {});
|
||||
await runtimeSource.whenFilesWritten?.();
|
||||
|
||||
const skillDir = `${root}/${SANDBOX_RUNTIME_SKILLS_DIR}/data-table-manager`;
|
||||
const skillPath = `${skillDir}/SKILL.md`;
|
||||
|
|
@ -341,6 +394,7 @@ describe('materializeRuntimeSkillsIntoWorkspace', () => {
|
|||
const loadTool = createSkillLoadTool(runtimeSource);
|
||||
|
||||
await loadTool.handler?.({ skillId: 'data-table-manager' }, {});
|
||||
await runtimeSource.whenFilesWritten?.();
|
||||
|
||||
const skillPath = `${root}/${SANDBOX_RUNTIME_SKILLS_DIR}/data-table-manager/SKILL.md`;
|
||||
expect(writeFile).toHaveBeenCalled();
|
||||
|
|
@ -362,6 +416,7 @@ describe('materializeRuntimeSkillsIntoWorkspace', () => {
|
|||
const loadTool = createSkillLoadTool(runtimeSource);
|
||||
|
||||
await loadTool.handler?.({ skillId: 'data-table-manager' }, {});
|
||||
await runtimeSource.whenFilesWritten?.();
|
||||
|
||||
const skillPath = `${root}/${SANDBOX_RUNTIME_SKILLS_DIR}/data-table-manager/SKILL.md`;
|
||||
expect(writeFile).toHaveBeenCalled();
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import {
|
|||
type Workspace,
|
||||
} from '@n8n/agents';
|
||||
import { getWorkspaceRoot } from '@n8n/agents/sandbox';
|
||||
import { getErrorMessage } from '@n8n/utils/errors/get-error-message';
|
||||
import { join as posixJoin, normalize as posixNormalize } from 'node:path/posix';
|
||||
|
||||
import type { Logger } from '../logger';
|
||||
|
|
@ -73,6 +74,16 @@ interface MaterializeRuntimeSkillsOptions {
|
|||
root: string;
|
||||
workspaceRoot?: string;
|
||||
logger: Logger;
|
||||
/** A bundle already built for the same source and root. */
|
||||
bundle?: RuntimeSkillWorkspaceBundle;
|
||||
}
|
||||
|
||||
export interface WorkspaceRuntimeSkillSource extends RuntimeSkillSource {
|
||||
/**
|
||||
* Resolves when the skill files are in the workspace. Skill loads do not
|
||||
* wait for the files, so sandbox commands that read them must wait here.
|
||||
*/
|
||||
whenFilesWritten?: () => Promise<void>;
|
||||
}
|
||||
|
||||
interface LazyWorkspaceRuntimeSkillSourceOptions {
|
||||
|
|
@ -390,6 +401,7 @@ export async function loadPrebakedRuntimeSkillsBundle({
|
|||
root,
|
||||
workspaceRoot = root,
|
||||
logger,
|
||||
bundle,
|
||||
}: MaterializeRuntimeSkillsOptions): Promise<RuntimeSkillWorkspaceBundle | undefined> {
|
||||
if (source.registry.skills.length === 0) return undefined;
|
||||
|
||||
|
|
@ -420,13 +432,14 @@ export async function loadPrebakedRuntimeSkillsBundle({
|
|||
count: bundle.skills.length,
|
||||
}),
|
||||
buildBundle: async () =>
|
||||
await buildRuntimeSkillWorkspaceBundle({
|
||||
bundle ??
|
||||
(await buildRuntimeSkillWorkspaceBundle({
|
||||
source,
|
||||
root,
|
||||
workspaceRoot,
|
||||
skillsRoot,
|
||||
logger,
|
||||
}),
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -596,6 +609,7 @@ export async function materializeRuntimeSkillsIntoWorkspace({
|
|||
workspace,
|
||||
root,
|
||||
logger,
|
||||
bundle,
|
||||
}: MaterializeRuntimeSkillsOptions): Promise<MaterializedRuntimeSkills | undefined> {
|
||||
return await traceSandboxOperation(
|
||||
'sync-skills',
|
||||
|
|
@ -613,13 +627,14 @@ export async function materializeRuntimeSkillsIntoWorkspace({
|
|||
resourceLabel: RUNTIME_SKILL_FILE_LABEL,
|
||||
logger,
|
||||
loadPrebaked: async () =>
|
||||
await loadPrebakedRuntimeSkillsBundle({ source, workspace, root, logger }),
|
||||
await loadPrebakedRuntimeSkillsBundle({ source, workspace, root, logger, bundle }),
|
||||
buildBundle: async () => {
|
||||
const bundle = await buildRuntimeSkillWorkspaceBundle({ source, root, logger });
|
||||
if (!bundle) {
|
||||
const built =
|
||||
bundle ?? (await buildRuntimeSkillWorkspaceBundle({ source, root, logger }));
|
||||
if (!built) {
|
||||
throw new Error('Expected runtime skill bundle after registry validation');
|
||||
}
|
||||
return bundle;
|
||||
return built;
|
||||
},
|
||||
materializedLogMessage: 'Materialized runtime skills into workspace',
|
||||
materializedLogContext: (bundle) => ({
|
||||
|
|
@ -638,17 +653,23 @@ export function createLazyWorkspaceRuntimeSkillSource({
|
|||
source,
|
||||
workspace,
|
||||
logger,
|
||||
}: LazyWorkspaceRuntimeSkillSourceOptions): RuntimeSkillSource {
|
||||
}: LazyWorkspaceRuntimeSkillSourceOptions): WorkspaceRuntimeSkillSource {
|
||||
if (!workspace || source.registry.skills.length === 0) return source;
|
||||
const runtimeWorkspace = workspace;
|
||||
|
||||
let materialized: MaterializedRuntimeSkills | undefined;
|
||||
let materializePromise: Promise<MaterializedRuntimeSkills | undefined> | undefined;
|
||||
interface BuiltSkills {
|
||||
bundle: RuntimeSkillWorkspaceBundle;
|
||||
root: string;
|
||||
}
|
||||
let built: BuiltSkills | undefined;
|
||||
let buildPromise: Promise<BuiltSkills | undefined> | undefined;
|
||||
let filesWritten = false;
|
||||
let writePromise: Promise<void> | undefined;
|
||||
|
||||
const workspaceSource: RuntimeSkillSource = {
|
||||
const workspaceSource: WorkspaceRuntimeSkillSource = {
|
||||
registry: source.registry,
|
||||
prepare: async () => {
|
||||
await ensureMaterialized();
|
||||
await ensureBuilt();
|
||||
},
|
||||
loadSkill: async (skillId: string) => await (await ensureSource()).loadSkill(skillId),
|
||||
...(source.loadFile
|
||||
|
|
@ -659,30 +680,60 @@ export function createLazyWorkspaceRuntimeSkillSource({
|
|||
},
|
||||
}
|
||||
: {}),
|
||||
whenFilesWritten: async () => {
|
||||
if (!built) return;
|
||||
await writeFiles(built).catch(logWriteFailure);
|
||||
},
|
||||
};
|
||||
|
||||
async function ensureMaterialized(): Promise<MaterializedRuntimeSkills | undefined> {
|
||||
if (materialized) return materialized;
|
||||
// Skill text comes from the in-memory bundle, so a load returns before the
|
||||
// sandbox writes finish. The writes continue in the background.
|
||||
async function ensureBuilt(): Promise<RuntimeSkillWorkspaceBundle | undefined> {
|
||||
if (!built) {
|
||||
buildPromise ??= (async () => {
|
||||
const root = await getWorkspaceRoot(runtimeWorkspace);
|
||||
const bundle = await buildRuntimeSkillWorkspaceBundle({ source, root, logger });
|
||||
if (!bundle) return undefined;
|
||||
built = { bundle, root };
|
||||
workspaceSource.registry = bundle.source.registry;
|
||||
return built;
|
||||
})().catch((error: unknown) => {
|
||||
buildPromise = undefined;
|
||||
throw error;
|
||||
});
|
||||
await buildPromise;
|
||||
}
|
||||
if (built) void writeFiles(built).catch(logWriteFailure);
|
||||
return built?.bundle;
|
||||
}
|
||||
|
||||
materializePromise ??= (async () => {
|
||||
const root = await getWorkspaceRoot(runtimeWorkspace);
|
||||
const options = { source, workspace: runtimeWorkspace, root, logger };
|
||||
const result = await materializeRuntimeSkillsIntoWorkspace(options);
|
||||
if (result) {
|
||||
materialized = result;
|
||||
workspaceSource.registry = result.source.registry;
|
||||
}
|
||||
return result;
|
||||
})().catch((error: unknown) => {
|
||||
materializePromise = undefined;
|
||||
throw error;
|
||||
async function writeFiles({ bundle, root }: BuiltSkills): Promise<void> {
|
||||
if (filesWritten) return;
|
||||
writePromise ??= materializeRuntimeSkillsIntoWorkspace({
|
||||
source,
|
||||
workspace: runtimeWorkspace,
|
||||
root,
|
||||
logger,
|
||||
bundle,
|
||||
})
|
||||
.then(() => {
|
||||
filesWritten = true;
|
||||
})
|
||||
.catch((error: unknown) => {
|
||||
writePromise = undefined;
|
||||
throw error;
|
||||
});
|
||||
await writePromise;
|
||||
}
|
||||
|
||||
function logWriteFailure(error: unknown): void {
|
||||
logger.warn('Failed to write runtime skills into the workspace', {
|
||||
error: getErrorMessage(error),
|
||||
});
|
||||
|
||||
return await materializePromise;
|
||||
}
|
||||
|
||||
async function ensureSource(): Promise<RuntimeSkillSource> {
|
||||
return (await ensureMaterialized())?.source ?? source;
|
||||
return (await ensureBuilt())?.source ?? source;
|
||||
}
|
||||
|
||||
return workspaceSource;
|
||||
|
|
|
|||
|
|
@ -67,6 +67,12 @@ export function hasRuntimeSkills(
|
|||
return (source?.registry.skills.length ?? 0) > 0;
|
||||
}
|
||||
|
||||
function hasPendingSkillFiles(
|
||||
source: RuntimeSkillSource,
|
||||
): source is RuntimeSkillSource & { whenFilesWritten: () => Promise<void> } {
|
||||
return 'whenFilesWritten' in source && typeof source.whenFilesWritten === 'function';
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the skill source in the background so it overlaps with the first model
|
||||
* call. load_skill awaits the same promise and retries if this one fails. It runs
|
||||
|
|
@ -83,7 +89,11 @@ export function warmRuntimeSkills(
|
|||
const preparation = tracing
|
||||
? tracing.withActiveSpan(tracing.actorRun, async () => await prepare.call(source))
|
||||
: prepare.call(source);
|
||||
tracing?.keepOpenUntilSettled?.(preparation);
|
||||
tracing?.keepOpenUntilSettled?.(
|
||||
hasPendingSkillFiles(source)
|
||||
? preparation.then(async () => await source.whenFilesWritten())
|
||||
: preparation,
|
||||
);
|
||||
preparation.catch((error: unknown) => {
|
||||
logger?.warn('Failed to warm runtime skills in the background', {
|
||||
error: getErrorMessage(error),
|
||||
|
|
|
|||
|
|
@ -129,6 +129,23 @@ describe('createLazyRuntimeWorkspace', () => {
|
|||
await expect(operation).rejects.toMatchObject({ name: 'AbortError' });
|
||||
});
|
||||
|
||||
it('runs commands only after the beforeUse hook settles', async () => {
|
||||
const { workspace, executeCommand } = createMockWorkspace();
|
||||
const pendingWrites = createDeferredPromise();
|
||||
const lazyWorkspace = createLazyRuntimeWorkspace({
|
||||
ensureWorkspace: async () => await Promise.resolve(workspace),
|
||||
beforeUse: async () => await pendingWrites.promise,
|
||||
});
|
||||
|
||||
const operation = lazyWorkspace.sandbox!.executeCommand!('ls', []);
|
||||
await new Promise((resolve) => setImmediate(resolve));
|
||||
expect(executeCommand).not.toHaveBeenCalled();
|
||||
|
||||
pendingWrites.resolve();
|
||||
await operation;
|
||||
expect(executeCommand).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('retries workspace creation after the first lazy initialization fails', async () => {
|
||||
const { workspace } = createMockWorkspace();
|
||||
const ensureWorkspace = vi
|
||||
|
|
|
|||
|
|
@ -45,6 +45,8 @@ export interface LazyRuntimeWorkspaceOptions {
|
|||
*/
|
||||
sandboxInstructions?: string;
|
||||
filesystemInstructions?: string;
|
||||
/** Awaited before each file or command operation, e.g. to finish pending skill writes. */
|
||||
beforeUse?: () => Promise<void>;
|
||||
}
|
||||
|
||||
type WorkspaceResolvedListener = (workspace: Workspace) => void;
|
||||
|
|
@ -56,8 +58,9 @@ export function createLazyRuntimeWorkspace({
|
|||
name = 'Instance AI runtime workspace',
|
||||
sandboxInstructions,
|
||||
filesystemInstructions,
|
||||
beforeUse,
|
||||
}: LazyRuntimeWorkspaceOptions): Workspace {
|
||||
const resolver = new LazyRuntimeWorkspaceResolver(ensureWorkspace);
|
||||
const resolver = new LazyRuntimeWorkspaceResolver(ensureWorkspace, beforeUse);
|
||||
|
||||
const workspace = new Workspace({
|
||||
id,
|
||||
|
|
@ -80,7 +83,10 @@ class LazyRuntimeWorkspaceResolver {
|
|||
private readonly resolvedListeners = new Set<WorkspaceResolvedListener>();
|
||||
private readonly destroyedListeners = new Set<WorkspaceDestroyedListener>();
|
||||
|
||||
constructor(private readonly ensureWorkspace: RuntimeWorkspaceResolver) {}
|
||||
constructor(
|
||||
private readonly ensureWorkspace: RuntimeWorkspaceResolver,
|
||||
private readonly beforeUse?: () => Promise<void>,
|
||||
) {}
|
||||
|
||||
get current(): Workspace | undefined {
|
||||
return this.resolvedWorkspace;
|
||||
|
|
@ -112,6 +118,7 @@ class LazyRuntimeWorkspaceResolver {
|
|||
throw new Error('Instance AI runtime workspace is unavailable.');
|
||||
}
|
||||
|
||||
await this.beforeUse?.();
|
||||
return workspace;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -65,6 +65,9 @@ conventions. Never add an incomplete tool or use a placeholder URL.
|
|||
follow its MCP Servers section, and stop this node-tool workflow.
|
||||
- If it returns \`kind: "node"\`, use its returned node results and call
|
||||
\`agent_builder_get_node_types\`; do not repeat the same search with \`agent_builder_search_nodes\`.
|
||||
- Fetch every node definition that the build needs in one
|
||||
\`agent_builder_get_node_types\` call, with all nodes and operations together.
|
||||
Do not fetch one service per call.
|
||||
- Call \`agent_builder_search_nodes\` directly only when the user explicitly requests an n8n
|
||||
node, when refining node results, or when a verified MCP server lacks the
|
||||
requested capability.
|
||||
|
|
|
|||
|
|
@ -57,7 +57,9 @@ agent, resolve the credential and any ambiguity immediately instead.
|
|||
|
||||
## Workflow
|
||||
|
||||
1. Discover and inspect the node with \`agent_builder_search_nodes\`, then \`agent_builder_get_node_types\`.
|
||||
1. Inspect the node with \`agent_builder_get_node_types\`. Use the node results
|
||||
from the \`agent-context\` integration lookup and the definitions you already
|
||||
fetched. Call \`agent_builder_search_nodes\` only when no lookup returned the node.
|
||||
2. Identify dynamic selectors from node metadata. Treat these as build-time
|
||||
lookup fields:
|
||||
- \`type: "resourceLocator"\`
|
||||
|
|
|
|||
|
|
@ -1077,9 +1077,13 @@ describe('InstanceAiService — runtime workspace setup', () => {
|
|||
|
||||
expect(createLazyRuntimeWorkspace).toHaveBeenCalledTimes(2);
|
||||
expect(createLazyRuntimeWorkspace).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
1,
|
||||
expect.objectContaining({ id: 'instance-ai-runtime-skill-workspace' }),
|
||||
);
|
||||
expect(createLazyRuntimeWorkspace).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
expect.objectContaining({ beforeUse: expect.any(Function) }),
|
||||
);
|
||||
expect(createLazyWorkspaceRuntimeSkillSource).toHaveBeenCalledTimes(1);
|
||||
expect(loadInstanceAiPromptSkills).toHaveBeenCalledTimes(1);
|
||||
expect(environment.orchestrationContext.runtimeSkills?.registry.skills).toEqual([
|
||||
|
|
|
|||
|
|
@ -2786,6 +2786,18 @@ export class InstanceAiService {
|
|||
return createScopedWorkspace(workspace, root);
|
||||
};
|
||||
|
||||
const runtimeSkillWorkspace = createLazyRuntimeWorkspace({
|
||||
id: 'instance-ai-runtime-skill-workspace',
|
||||
name: 'Instance AI runtime skill workspace',
|
||||
ensureWorkspace: async () =>
|
||||
await scopeWorkspaceForAgent((await getSandboxEntry())?.workspace),
|
||||
});
|
||||
const workspaceSkills = createLazyWorkspaceRuntimeSkillSource({
|
||||
source: allRuntimeSkills,
|
||||
workspace: runtimeSkillWorkspace,
|
||||
logger: this.logger,
|
||||
});
|
||||
runtimeSkills = workspaceSkills;
|
||||
runtimeWorkspace = createLazyRuntimeWorkspace({
|
||||
// Empty + stable across resumes: sandbox/filesystem guidance lives in
|
||||
// the system prompt's `## Sandbox workspace` section. Passing '' here
|
||||
|
|
@ -2796,17 +2808,8 @@ export class InstanceAiService {
|
|||
filesystemInstructions: '',
|
||||
ensureWorkspace: async () =>
|
||||
await scopeWorkspaceForAgent((await getSetupSandboxEntry())?.workspace),
|
||||
});
|
||||
const runtimeSkillWorkspace = createLazyRuntimeWorkspace({
|
||||
id: 'instance-ai-runtime-skill-workspace',
|
||||
name: 'Instance AI runtime skill workspace',
|
||||
ensureWorkspace: async () =>
|
||||
await scopeWorkspaceForAgent((await getSandboxEntry())?.workspace),
|
||||
});
|
||||
runtimeSkills = createLazyWorkspaceRuntimeSkillSource({
|
||||
source: allRuntimeSkills,
|
||||
workspace: runtimeSkillWorkspace,
|
||||
logger: this.logger,
|
||||
// Commands can run skill scripts, so they wait for the skill files.
|
||||
beforeUse: async () => await workspaceSkills.whenFilesWritten?.(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user