refactor(workflows): use createRole instead of compileWorkflowSpec
Switch build.ts and solve-issue inner roles to @uncaged/nerve-workflow-utils createRole with LlmExtractorConfig. Remove @uncaged/nerve-daemon from workspace dependencies; keep override for linking. Planner uses createCursorAdapter ask mode; dynamic cwd via start.meta.workdir. Made-with: Cursor
This commit is contained in:
parent
70fd064bad
commit
95df8bc3c2
@ -10,7 +10,6 @@
|
|||||||
"@uncaged/nerve-adapter-cursor": "link:../repos/nerve/packages/adapter-cursor",
|
"@uncaged/nerve-adapter-cursor": "link:../repos/nerve/packages/adapter-cursor",
|
||||||
"@uncaged/nerve-adapter-hermes": "link:../repos/nerve/packages/adapter-hermes",
|
"@uncaged/nerve-adapter-hermes": "link:../repos/nerve/packages/adapter-hermes",
|
||||||
"@uncaged/nerve-core": "latest",
|
"@uncaged/nerve-core": "latest",
|
||||||
"@uncaged/nerve-daemon": "latest",
|
|
||||||
"@uncaged/nerve-workflow-utils": "link:../repos/nerve/packages/workflow-utils",
|
"@uncaged/nerve-workflow-utils": "link:../repos/nerve/packages/workflow-utils",
|
||||||
"drizzle-orm": "latest",
|
"drizzle-orm": "latest",
|
||||||
"zod": "^4.3.6"
|
"zod": "^4.3.6"
|
||||||
|
|||||||
12
pnpm-lock.yaml
generated
12
pnpm-lock.yaml
generated
@ -24,9 +24,6 @@ importers:
|
|||||||
'@uncaged/nerve-core':
|
'@uncaged/nerve-core':
|
||||||
specifier: link:../repos/nerve/packages/core
|
specifier: link:../repos/nerve/packages/core
|
||||||
version: link:../repos/nerve/packages/core
|
version: link:../repos/nerve/packages/core
|
||||||
'@uncaged/nerve-daemon':
|
|
||||||
specifier: link:../repos/nerve/packages/daemon
|
|
||||||
version: link:../repos/nerve/packages/daemon
|
|
||||||
'@uncaged/nerve-workflow-utils':
|
'@uncaged/nerve-workflow-utils':
|
||||||
specifier: link:../repos/nerve/packages/workflow-utils
|
specifier: link:../repos/nerve/packages/workflow-utils
|
||||||
version: link:../repos/nerve/packages/workflow-utils
|
version: link:../repos/nerve/packages/workflow-utils
|
||||||
@ -112,9 +109,6 @@ importers:
|
|||||||
'@uncaged/nerve-core':
|
'@uncaged/nerve-core':
|
||||||
specifier: link:../../../repos/nerve/packages/core
|
specifier: link:../../../repos/nerve/packages/core
|
||||||
version: link:../../../repos/nerve/packages/core
|
version: link:../../../repos/nerve/packages/core
|
||||||
'@uncaged/nerve-daemon':
|
|
||||||
specifier: link:../../../repos/nerve/packages/daemon
|
|
||||||
version: link:../../../repos/nerve/packages/daemon
|
|
||||||
'@uncaged/nerve-workflow-utils':
|
'@uncaged/nerve-workflow-utils':
|
||||||
specifier: link:../../../repos/nerve/packages/workflow-utils
|
specifier: link:../../../repos/nerve/packages/workflow-utils
|
||||||
version: link:../../../repos/nerve/packages/workflow-utils
|
version: link:../../../repos/nerve/packages/workflow-utils
|
||||||
@ -143,9 +137,6 @@ importers:
|
|||||||
'@uncaged/nerve-core':
|
'@uncaged/nerve-core':
|
||||||
specifier: link:../../../repos/nerve/packages/core
|
specifier: link:../../../repos/nerve/packages/core
|
||||||
version: link:../../../repos/nerve/packages/core
|
version: link:../../../repos/nerve/packages/core
|
||||||
'@uncaged/nerve-daemon':
|
|
||||||
specifier: link:../../../repos/nerve/packages/daemon
|
|
||||||
version: link:../../../repos/nerve/packages/daemon
|
|
||||||
'@uncaged/nerve-workflow-utils':
|
'@uncaged/nerve-workflow-utils':
|
||||||
specifier: link:../../../repos/nerve/packages/workflow-utils
|
specifier: link:../../../repos/nerve/packages/workflow-utils
|
||||||
version: link:../../../repos/nerve/packages/workflow-utils
|
version: link:../../../repos/nerve/packages/workflow-utils
|
||||||
@ -174,9 +165,6 @@ importers:
|
|||||||
'@uncaged/nerve-core':
|
'@uncaged/nerve-core':
|
||||||
specifier: link:../../../repos/nerve/packages/core
|
specifier: link:../../../repos/nerve/packages/core
|
||||||
version: link:../../../repos/nerve/packages/core
|
version: link:../../../repos/nerve/packages/core
|
||||||
'@uncaged/nerve-daemon':
|
|
||||||
specifier: link:../../../repos/nerve/packages/daemon
|
|
||||||
version: link:../../../repos/nerve/packages/daemon
|
|
||||||
'@uncaged/nerve-workflow-utils':
|
'@uncaged/nerve-workflow-utils':
|
||||||
specifier: link:../../../repos/nerve/packages/workflow-utils
|
specifier: link:../../../repos/nerve/packages/workflow-utils
|
||||||
version: link:../../../repos/nerve/packages/workflow-utils
|
version: link:../../../repos/nerve/packages/workflow-utils
|
||||||
|
|||||||
@ -1,17 +1,8 @@
|
|||||||
import type {
|
import type { StartStep, WorkflowDefinition } from "@uncaged/nerve-core";
|
||||||
Schema,
|
import { createCursorAdapter, cursorAdapter } from "@uncaged/nerve-adapter-cursor";
|
||||||
StartStep,
|
|
||||||
WorkflowContext,
|
|
||||||
WorkflowDefinition,
|
|
||||||
WorkflowMessage,
|
|
||||||
WorkflowSpec,
|
|
||||||
} from "@uncaged/nerve-core";
|
|
||||||
import { END } from "@uncaged/nerve-core";
|
|
||||||
import { compileWorkflowSpec, type CompileWorkflowSpecDeps } from "@uncaged/nerve-daemon";
|
|
||||||
import { createCursorAdapter } from "@uncaged/nerve-adapter-cursor";
|
|
||||||
import { hermesAdapter } from "@uncaged/nerve-adapter-hermes";
|
import { hermesAdapter } from "@uncaged/nerve-adapter-hermes";
|
||||||
import type { LlmProvider } from "@uncaged/nerve-workflow-utils";
|
import type { LlmExtractorConfig } from "@uncaged/nerve-workflow-utils";
|
||||||
import { createLlmExtractFn, zodMeta } from "@uncaged/nerve-workflow-utils";
|
import { createRole } from "@uncaged/nerve-workflow-utils";
|
||||||
|
|
||||||
import { coderPrompt } from "./roles/coder/prompt.js";
|
import { coderPrompt } from "./roles/coder/prompt.js";
|
||||||
import { coderMetaSchema } from "./roles/coder/index.js";
|
import { coderMetaSchema } from "./roles/coder/index.js";
|
||||||
@ -26,84 +17,54 @@ import { moderator } from "./moderator.js";
|
|||||||
import type { SenseMeta } from "./moderator.js";
|
import type { SenseMeta } from "./moderator.js";
|
||||||
|
|
||||||
export type BuildSenseGeneratorDeps = {
|
export type BuildSenseGeneratorDeps = {
|
||||||
provider: LlmProvider;
|
extract: LlmExtractorConfig;
|
||||||
cwd: string;
|
cwd: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const CURSOR_TIMEOUT_MS = 300_000;
|
const CURSOR_TIMEOUT_MS = 300_000;
|
||||||
|
|
||||||
type SenseAgentMeta = Pick<SenseMeta, "planner" | "coder" | "reviewer" | "tester">;
|
|
||||||
|
|
||||||
function defaultAgentCreateContext(
|
|
||||||
workdir: string,
|
|
||||||
): (start: StartStep, messages: WorkflowMessage[]) => WorkflowContext {
|
|
||||||
return (start, messages) => ({
|
|
||||||
start,
|
|
||||||
messages,
|
|
||||||
workdir,
|
|
||||||
signal: new AbortController().signal,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function buildSenseGenerator({
|
export function buildSenseGenerator({
|
||||||
provider,
|
extract,
|
||||||
cwd,
|
cwd,
|
||||||
}: BuildSenseGeneratorDeps): WorkflowDefinition<SenseMeta> {
|
}: BuildSenseGeneratorDeps): WorkflowDefinition<SenseMeta> {
|
||||||
const compileDeps: CompileWorkflowSpecDeps = {
|
const roles = {
|
||||||
extractFn: async <T>(raw: string, schema: Schema<T>, dryRun: boolean) =>
|
planner: createRole(
|
||||||
createLlmExtractFn<T>({ provider, dryRun })(raw, schema),
|
createCursorAdapter({
|
||||||
createContext: defaultAgentCreateContext(cwd),
|
|
||||||
};
|
|
||||||
|
|
||||||
const agentSpec: WorkflowSpec<SenseAgentMeta> = {
|
|
||||||
name: "develop-sense",
|
|
||||||
roles: {
|
|
||||||
planner: {
|
|
||||||
adapter: createCursorAdapter({
|
|
||||||
type: "cursor",
|
type: "cursor",
|
||||||
model: "auto",
|
|
||||||
timeout: CURSOR_TIMEOUT_MS,
|
|
||||||
mode: "ask",
|
mode: "ask",
|
||||||
}),
|
|
||||||
prompt: async (start: StartStep) => plannerPrompt({ threadId: start.meta.threadId }),
|
|
||||||
meta: zodMeta(plannerMetaSchema),
|
|
||||||
},
|
|
||||||
coder: {
|
|
||||||
adapter: createCursorAdapter({
|
|
||||||
type: "cursor",
|
|
||||||
model: "auto",
|
model: "auto",
|
||||||
timeout: CURSOR_TIMEOUT_MS,
|
timeout: CURSOR_TIMEOUT_MS,
|
||||||
}),
|
}),
|
||||||
prompt: async (start: StartStep) => coderPrompt({ threadId: start.meta.threadId }),
|
async (start: StartStep) => plannerPrompt({ threadId: start.meta.threadId }),
|
||||||
meta: zodMeta(coderMetaSchema),
|
plannerMetaSchema,
|
||||||
},
|
extract,
|
||||||
reviewer: {
|
),
|
||||||
adapter: hermesAdapter,
|
coder: createRole(
|
||||||
prompt: async (start: StartStep) =>
|
cursorAdapter,
|
||||||
|
async (start: StartStep) => coderPrompt({ threadId: start.meta.threadId }),
|
||||||
|
coderMetaSchema,
|
||||||
|
extract,
|
||||||
|
),
|
||||||
|
reviewer: createRole(
|
||||||
|
hermesAdapter,
|
||||||
|
async (start: StartStep) =>
|
||||||
reviewerPrompt({ threadId: start.meta.threadId, nerveRoot: cwd }),
|
reviewerPrompt({ threadId: start.meta.threadId, nerveRoot: cwd }),
|
||||||
meta: zodMeta(reviewerMetaSchema),
|
reviewerMetaSchema,
|
||||||
},
|
extract,
|
||||||
tester: {
|
),
|
||||||
adapter: hermesAdapter,
|
tester: createRole(
|
||||||
prompt: async (start: StartStep) =>
|
hermesAdapter,
|
||||||
|
async (start: StartStep) =>
|
||||||
testerPrompt({ threadId: start.meta.threadId, nerveRoot: cwd }),
|
testerPrompt({ threadId: start.meta.threadId, nerveRoot: cwd }),
|
||||||
meta: zodMeta(testerMetaSchema),
|
testerMetaSchema,
|
||||||
},
|
extract,
|
||||||
},
|
),
|
||||||
moderator: () => END,
|
committer: buildCommitterRole({ nerveRoot: cwd }),
|
||||||
};
|
};
|
||||||
|
|
||||||
const compiled = compileWorkflowSpec(agentSpec, compileDeps);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: "develop-sense",
|
name: "develop-sense",
|
||||||
roles: {
|
roles,
|
||||||
planner: compiled.roles.planner,
|
|
||||||
coder: compiled.roles.coder,
|
|
||||||
reviewer: compiled.roles.reviewer,
|
|
||||||
tester: compiled.roles.tester,
|
|
||||||
committer: buildCommitterRole({ nerveRoot: cwd }),
|
|
||||||
},
|
|
||||||
moderator,
|
moderator,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,7 +12,7 @@ if (!apiKey || !baseUrl) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const workflow = buildSenseGenerator({
|
const workflow = buildSenseGenerator({
|
||||||
provider: { apiKey, baseUrl, model },
|
extract: { provider: { apiKey, baseUrl, model } },
|
||||||
cwd: NERVE_ROOT,
|
cwd: NERVE_ROOT,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,6 @@
|
|||||||
"@uncaged/nerve-adapter-cursor": "latest",
|
"@uncaged/nerve-adapter-cursor": "latest",
|
||||||
"@uncaged/nerve-adapter-hermes": "latest",
|
"@uncaged/nerve-adapter-hermes": "latest",
|
||||||
"@uncaged/nerve-core": "latest",
|
"@uncaged/nerve-core": "latest",
|
||||||
"@uncaged/nerve-daemon": "latest",
|
|
||||||
"@uncaged/nerve-workflow-utils": "latest",
|
"@uncaged/nerve-workflow-utils": "latest",
|
||||||
"zod": "^4.3.6"
|
"zod": "^4.3.6"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,17 +1,8 @@
|
|||||||
import type {
|
import type { StartStep, WorkflowDefinition } from "@uncaged/nerve-core";
|
||||||
Schema,
|
import { createCursorAdapter, cursorAdapter } from "@uncaged/nerve-adapter-cursor";
|
||||||
StartStep,
|
|
||||||
WorkflowContext,
|
|
||||||
WorkflowDefinition,
|
|
||||||
WorkflowMessage,
|
|
||||||
WorkflowSpec,
|
|
||||||
} from "@uncaged/nerve-core";
|
|
||||||
import { END } from "@uncaged/nerve-core";
|
|
||||||
import { compileWorkflowSpec, type CompileWorkflowSpecDeps } from "@uncaged/nerve-daemon";
|
|
||||||
import { createCursorAdapter } from "@uncaged/nerve-adapter-cursor";
|
|
||||||
import { hermesAdapter } from "@uncaged/nerve-adapter-hermes";
|
import { hermesAdapter } from "@uncaged/nerve-adapter-hermes";
|
||||||
import type { LlmProvider } from "@uncaged/nerve-workflow-utils";
|
import type { LlmExtractorConfig } from "@uncaged/nerve-workflow-utils";
|
||||||
import { createLlmExtractFn, zodMeta } from "@uncaged/nerve-workflow-utils";
|
import { createRole } from "@uncaged/nerve-workflow-utils";
|
||||||
|
|
||||||
import { coderPrompt } from "./roles/coder/prompt.js";
|
import { coderPrompt } from "./roles/coder/prompt.js";
|
||||||
import { coderMetaSchema } from "./roles/coder/index.js";
|
import { coderMetaSchema } from "./roles/coder/index.js";
|
||||||
@ -26,84 +17,54 @@ import { moderator } from "./moderator.js";
|
|||||||
import type { WorkflowMeta } from "./moderator.js";
|
import type { WorkflowMeta } from "./moderator.js";
|
||||||
|
|
||||||
export type BuildWorkflowGeneratorDeps = {
|
export type BuildWorkflowGeneratorDeps = {
|
||||||
provider: LlmProvider;
|
extract: LlmExtractorConfig;
|
||||||
nerveRoot: string;
|
nerveRoot: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const CURSOR_TIMEOUT_MS = 300_000;
|
const CURSOR_TIMEOUT_MS = 300_000;
|
||||||
|
|
||||||
type WorkflowAgentMeta = Pick<WorkflowMeta, "planner" | "coder" | "reviewer" | "tester">;
|
|
||||||
|
|
||||||
function defaultAgentCreateContext(
|
|
||||||
workdir: string,
|
|
||||||
): (start: StartStep, messages: WorkflowMessage[]) => WorkflowContext {
|
|
||||||
return (start, messages) => ({
|
|
||||||
start,
|
|
||||||
messages,
|
|
||||||
workdir,
|
|
||||||
signal: new AbortController().signal,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function buildWorkflowGenerator({
|
export function buildWorkflowGenerator({
|
||||||
provider,
|
extract,
|
||||||
nerveRoot,
|
nerveRoot,
|
||||||
}: BuildWorkflowGeneratorDeps): WorkflowDefinition<WorkflowMeta> {
|
}: BuildWorkflowGeneratorDeps): WorkflowDefinition<WorkflowMeta> {
|
||||||
const compileDeps: CompileWorkflowSpecDeps = {
|
const roles = {
|
||||||
extractFn: async <T>(raw: string, schema: Schema<T>, dryRun: boolean) =>
|
planner: createRole(
|
||||||
createLlmExtractFn<T>({ provider, dryRun })(raw, schema),
|
createCursorAdapter({
|
||||||
createContext: defaultAgentCreateContext(nerveRoot),
|
|
||||||
};
|
|
||||||
|
|
||||||
const agentSpec: WorkflowSpec<WorkflowAgentMeta> = {
|
|
||||||
name: "develop-workflow",
|
|
||||||
roles: {
|
|
||||||
planner: {
|
|
||||||
adapter: createCursorAdapter({
|
|
||||||
type: "cursor",
|
type: "cursor",
|
||||||
model: "auto",
|
|
||||||
timeout: CURSOR_TIMEOUT_MS,
|
|
||||||
mode: "ask",
|
mode: "ask",
|
||||||
}),
|
|
||||||
prompt: async (start: StartStep) => plannerPrompt({ threadId: start.meta.threadId }),
|
|
||||||
meta: zodMeta(plannerMetaSchema),
|
|
||||||
},
|
|
||||||
coder: {
|
|
||||||
adapter: createCursorAdapter({
|
|
||||||
type: "cursor",
|
|
||||||
model: "auto",
|
model: "auto",
|
||||||
timeout: CURSOR_TIMEOUT_MS,
|
timeout: CURSOR_TIMEOUT_MS,
|
||||||
}),
|
}),
|
||||||
prompt: async (start: StartStep) => coderPrompt({ threadId: start.meta.threadId }),
|
async (start: StartStep) => plannerPrompt({ threadId: start.meta.threadId }),
|
||||||
meta: zodMeta(coderMetaSchema),
|
plannerMetaSchema,
|
||||||
},
|
extract,
|
||||||
reviewer: {
|
),
|
||||||
adapter: hermesAdapter,
|
coder: createRole(
|
||||||
prompt: async (start: StartStep) =>
|
cursorAdapter,
|
||||||
|
async (start: StartStep) => coderPrompt({ threadId: start.meta.threadId }),
|
||||||
|
coderMetaSchema,
|
||||||
|
extract,
|
||||||
|
),
|
||||||
|
reviewer: createRole(
|
||||||
|
hermesAdapter,
|
||||||
|
async (start: StartStep) =>
|
||||||
reviewerPrompt({ threadId: start.meta.threadId, nerveRoot }),
|
reviewerPrompt({ threadId: start.meta.threadId, nerveRoot }),
|
||||||
meta: zodMeta(reviewerMetaSchema),
|
reviewerMetaSchema,
|
||||||
},
|
extract,
|
||||||
tester: {
|
),
|
||||||
adapter: hermesAdapter,
|
tester: createRole(
|
||||||
prompt: async (start: StartStep) =>
|
hermesAdapter,
|
||||||
|
async (start: StartStep) =>
|
||||||
testerPrompt({ threadId: start.meta.threadId, nerveRoot }),
|
testerPrompt({ threadId: start.meta.threadId, nerveRoot }),
|
||||||
meta: zodMeta(testerMetaSchema),
|
testerMetaSchema,
|
||||||
},
|
extract,
|
||||||
},
|
),
|
||||||
moderator: () => END,
|
committer: buildCommitterRole({ nerveRoot }),
|
||||||
};
|
};
|
||||||
|
|
||||||
const compiled = compileWorkflowSpec(agentSpec, compileDeps);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: "develop-workflow",
|
name: "develop-workflow",
|
||||||
roles: {
|
roles,
|
||||||
planner: compiled.roles.planner,
|
|
||||||
coder: compiled.roles.coder,
|
|
||||||
reviewer: compiled.roles.reviewer,
|
|
||||||
tester: compiled.roles.tester,
|
|
||||||
committer: buildCommitterRole({ nerveRoot }),
|
|
||||||
},
|
|
||||||
moderator,
|
moderator,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,7 +13,7 @@ if (!apiKey || !baseUrl) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const workflow = buildWorkflowGenerator({
|
const workflow = buildWorkflowGenerator({
|
||||||
provider: { apiKey, baseUrl, model },
|
extract: { provider: { apiKey, baseUrl, model } },
|
||||||
nerveRoot: NERVE_ROOT,
|
nerveRoot: NERVE_ROOT,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,6 @@
|
|||||||
"@uncaged/nerve-adapter-cursor": "latest",
|
"@uncaged/nerve-adapter-cursor": "latest",
|
||||||
"@uncaged/nerve-adapter-hermes": "latest",
|
"@uncaged/nerve-adapter-hermes": "latest",
|
||||||
"@uncaged/nerve-core": "latest",
|
"@uncaged/nerve-core": "latest",
|
||||||
"@uncaged/nerve-daemon": "latest",
|
|
||||||
"@uncaged/nerve-workflow-utils": "latest",
|
"@uncaged/nerve-workflow-utils": "latest",
|
||||||
"zod": "^4.3.6"
|
"zod": "^4.3.6"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,16 +1,7 @@
|
|||||||
import type {
|
import type { StartStep, WorkflowDefinition } from "@uncaged/nerve-core";
|
||||||
Schema,
|
|
||||||
StartStep,
|
|
||||||
WorkflowContext,
|
|
||||||
WorkflowDefinition,
|
|
||||||
WorkflowMessage,
|
|
||||||
WorkflowSpec,
|
|
||||||
} from "@uncaged/nerve-core";
|
|
||||||
import { END } from "@uncaged/nerve-core";
|
|
||||||
import { compileWorkflowSpec, type CompileWorkflowSpecDeps } from "@uncaged/nerve-daemon";
|
|
||||||
import { hermesAdapter } from "@uncaged/nerve-adapter-hermes";
|
import { hermesAdapter } from "@uncaged/nerve-adapter-hermes";
|
||||||
import type { LlmProvider } from "@uncaged/nerve-workflow-utils";
|
import type { LlmExtractorConfig } from "@uncaged/nerve-workflow-utils";
|
||||||
import { createLlmExtractFn, zodMeta } from "@uncaged/nerve-workflow-utils";
|
import { createRole } from "@uncaged/nerve-workflow-utils";
|
||||||
|
|
||||||
import { moderator } from "./moderator.js";
|
import { moderator } from "./moderator.js";
|
||||||
import type { WorkflowMeta } from "./moderator.js";
|
import type { WorkflowMeta } from "./moderator.js";
|
||||||
@ -28,69 +19,44 @@ import { testPrompt } from "./roles/test/prompt.js";
|
|||||||
|
|
||||||
export type BuildSolveIssueDeps = {
|
export type BuildSolveIssueDeps = {
|
||||||
nerveRoot: string;
|
nerveRoot: string;
|
||||||
provider: LlmProvider;
|
extract: LlmExtractorConfig;
|
||||||
};
|
};
|
||||||
|
|
||||||
type SolveHermesAgentMeta = Pick<WorkflowMeta, "read-issue" | "prepare" | "review" | "test">;
|
export function buildSolveIssue({
|
||||||
|
nerveRoot,
|
||||||
function defaultAgentCreateContext(
|
extract,
|
||||||
workdir: string,
|
}: BuildSolveIssueDeps): WorkflowDefinition<WorkflowMeta> {
|
||||||
): (start: StartStep, messages: WorkflowMessage[]) => WorkflowContext {
|
|
||||||
return (start, messages) => ({
|
|
||||||
start,
|
|
||||||
messages,
|
|
||||||
workdir,
|
|
||||||
signal: new AbortController().signal,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function buildSolveIssue({ nerveRoot, provider }: BuildSolveIssueDeps): WorkflowDefinition<WorkflowMeta> {
|
|
||||||
const compileDeps: CompileWorkflowSpecDeps = {
|
|
||||||
extractFn: async <T>(raw: string, schema: Schema<T>, dryRun: boolean) =>
|
|
||||||
createLlmExtractFn<T>({ provider, dryRun })(raw, schema),
|
|
||||||
createContext: defaultAgentCreateContext(nerveRoot),
|
|
||||||
};
|
|
||||||
|
|
||||||
const agentSpec: WorkflowSpec<SolveHermesAgentMeta> = {
|
|
||||||
name: "solve-issue",
|
|
||||||
roles: {
|
|
||||||
"read-issue": {
|
|
||||||
adapter: hermesAdapter,
|
|
||||||
prompt: async (start: StartStep) => readIssuePrompt({ threadId: start.meta.threadId }),
|
|
||||||
meta: zodMeta(readIssueMetaSchema),
|
|
||||||
},
|
|
||||||
prepare: {
|
|
||||||
adapter: hermesAdapter,
|
|
||||||
prompt: async (start: StartStep) => preparePrompt({ threadId: start.meta.threadId }),
|
|
||||||
meta: zodMeta(prepareMetaSchema),
|
|
||||||
},
|
|
||||||
review: {
|
|
||||||
adapter: hermesAdapter,
|
|
||||||
prompt: async (start: StartStep) =>
|
|
||||||
reviewPrompt({ threadId: start.meta.threadId, nerveRoot }),
|
|
||||||
meta: zodMeta(reviewMetaSchema),
|
|
||||||
},
|
|
||||||
test: {
|
|
||||||
adapter: hermesAdapter,
|
|
||||||
prompt: async (start: StartStep) => testPrompt({ threadId: start.meta.threadId }),
|
|
||||||
meta: zodMeta(testMetaSchema),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
moderator: () => END,
|
|
||||||
};
|
|
||||||
|
|
||||||
const compiled = compileWorkflowSpec(agentSpec, compileDeps);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: "solve-issue",
|
name: "solve-issue",
|
||||||
roles: {
|
roles: {
|
||||||
"read-issue": compiled.roles["read-issue"],
|
"read-issue": createRole(
|
||||||
prepare: compiled.roles.prepare,
|
hermesAdapter,
|
||||||
plan: buildPlanRole({ provider, nerveRoot }),
|
async (start: StartStep) => readIssuePrompt({ threadId: start.meta.threadId }),
|
||||||
implement: buildImplementRole({ provider, nerveRoot }),
|
readIssueMetaSchema,
|
||||||
review: compiled.roles.review,
|
extract,
|
||||||
test: compiled.roles.test,
|
),
|
||||||
publish: buildPublishRole({ provider, nerveRoot }),
|
prepare: createRole(
|
||||||
|
hermesAdapter,
|
||||||
|
async (start: StartStep) => preparePrompt({ threadId: start.meta.threadId }),
|
||||||
|
prepareMetaSchema,
|
||||||
|
extract,
|
||||||
|
),
|
||||||
|
plan: buildPlanRole({ extract, nerveRoot }),
|
||||||
|
implement: buildImplementRole({ extract, nerveRoot }),
|
||||||
|
review: createRole(
|
||||||
|
hermesAdapter,
|
||||||
|
async (start: StartStep) =>
|
||||||
|
reviewPrompt({ threadId: start.meta.threadId, nerveRoot }),
|
||||||
|
reviewMetaSchema,
|
||||||
|
extract,
|
||||||
|
),
|
||||||
|
test: createRole(
|
||||||
|
hermesAdapter,
|
||||||
|
async (start: StartStep) => testPrompt({ threadId: start.meta.threadId }),
|
||||||
|
testMetaSchema,
|
||||||
|
extract,
|
||||||
|
),
|
||||||
|
publish: buildPublishRole({ extract, nerveRoot }),
|
||||||
},
|
},
|
||||||
moderator,
|
moderator,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -11,6 +11,6 @@ if (provider === null) {
|
|||||||
throw new Error("Set DASHSCOPE_API_KEY and DASHSCOPE_BASE_URL (or cfg get equivalents)");
|
throw new Error("Set DASHSCOPE_API_KEY and DASHSCOPE_BASE_URL (or cfg get equivalents)");
|
||||||
}
|
}
|
||||||
|
|
||||||
const workflow = buildSolveIssue({ nerveRoot: NERVE_ROOT, provider });
|
const workflow = buildSolveIssue({ nerveRoot: NERVE_ROOT, extract: { provider } });
|
||||||
|
|
||||||
export default workflow;
|
export default workflow;
|
||||||
|
|||||||
@ -10,7 +10,6 @@
|
|||||||
"@uncaged/nerve-adapter-cursor": "latest",
|
"@uncaged/nerve-adapter-cursor": "latest",
|
||||||
"@uncaged/nerve-adapter-hermes": "latest",
|
"@uncaged/nerve-adapter-hermes": "latest",
|
||||||
"@uncaged/nerve-core": "latest",
|
"@uncaged/nerve-core": "latest",
|
||||||
"@uncaged/nerve-daemon": "latest",
|
|
||||||
"@uncaged/nerve-workflow-utils": "latest",
|
"@uncaged/nerve-workflow-utils": "latest",
|
||||||
"zod": "^4.3.6"
|
"zod": "^4.3.6"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,9 +1,7 @@
|
|||||||
import type { Role, RoleResult, Schema, StartStep, WorkflowMessage } from "@uncaged/nerve-core";
|
import type { Role, RoleResult, StartStep, WorkflowMessage } from "@uncaged/nerve-core";
|
||||||
import { END } from "@uncaged/nerve-core";
|
|
||||||
import { compileWorkflowSpec } from "@uncaged/nerve-daemon";
|
|
||||||
import { createCursorAdapter } from "@uncaged/nerve-adapter-cursor";
|
import { createCursorAdapter } from "@uncaged/nerve-adapter-cursor";
|
||||||
import type { LlmProvider } from "@uncaged/nerve-workflow-utils";
|
import type { LlmExtractorConfig } from "@uncaged/nerve-workflow-utils";
|
||||||
import { createLlmExtractFn, zodMeta } from "@uncaged/nerve-workflow-utils";
|
import { createRole } from "@uncaged/nerve-workflow-utils";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { resolveRepoCwd } from "../../lib/repo-context.js";
|
import { resolveRepoCwd } from "../../lib/repo-context.js";
|
||||||
@ -15,13 +13,16 @@ export const implementMetaSchema = z.object({
|
|||||||
export type ImplementMeta = z.infer<typeof implementMetaSchema>;
|
export type ImplementMeta = z.infer<typeof implementMetaSchema>;
|
||||||
|
|
||||||
export type BuildImplementDeps = {
|
export type BuildImplementDeps = {
|
||||||
provider: LlmProvider;
|
extract: LlmExtractorConfig;
|
||||||
nerveRoot: string;
|
nerveRoot: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const CURSOR_TIMEOUT_MS = 300_000;
|
const CURSOR_TIMEOUT_MS = 300_000;
|
||||||
|
|
||||||
export function buildImplementRole({ provider, nerveRoot }: BuildImplementDeps): Role<ImplementMeta> {
|
export function buildImplementRole({
|
||||||
|
extract,
|
||||||
|
nerveRoot,
|
||||||
|
}: BuildImplementDeps): Role<ImplementMeta> {
|
||||||
return async (start: StartStep, messages: WorkflowMessage[]): Promise<RoleResult<ImplementMeta>> => {
|
return async (start: StartStep, messages: WorkflowMessage[]): Promise<RoleResult<ImplementMeta>> => {
|
||||||
const cwd = resolveRepoCwd(messages);
|
const cwd = resolveRepoCwd(messages);
|
||||||
if (cwd === null) {
|
if (cwd === null) {
|
||||||
@ -31,42 +32,28 @@ export function buildImplementRole({ provider, nerveRoot }: BuildImplementDeps):
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const innerSpec = {
|
const innerRole = createRole(
|
||||||
main: {
|
createCursorAdapter({
|
||||||
adapter: createCursorAdapter({
|
|
||||||
type: "cursor",
|
type: "cursor",
|
||||||
model: "auto",
|
model: "auto",
|
||||||
timeout: CURSOR_TIMEOUT_MS,
|
timeout: CURSOR_TIMEOUT_MS,
|
||||||
}),
|
}),
|
||||||
prompt: async (start: StartStep) =>
|
async (innerStart: StartStep) =>
|
||||||
buildImplementPrompt({
|
buildImplementPrompt({
|
||||||
threadId: start.meta.threadId,
|
threadId: innerStart.meta.threadId,
|
||||||
nerveRoot,
|
nerveRoot,
|
||||||
}),
|
}),
|
||||||
meta: zodMeta(implementMetaSchema),
|
implementMetaSchema,
|
||||||
},
|
extract,
|
||||||
};
|
|
||||||
|
|
||||||
const compiled = compileWorkflowSpec(
|
|
||||||
{
|
|
||||||
name: "_implement-inner",
|
|
||||||
roles: innerSpec,
|
|
||||||
moderator: () => END,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
extractFn: async <T>(raw: string, schema: Schema<T>, dryRun: boolean) =>
|
|
||||||
createLlmExtractFn<T>({ provider, dryRun })(raw, schema),
|
|
||||||
createContext: (s, m) => ({
|
|
||||||
start: s,
|
|
||||||
messages: m,
|
|
||||||
workdir: cwd,
|
|
||||||
signal: new AbortController().signal,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const innerStart = {
|
||||||
|
...start,
|
||||||
|
meta: { ...start.meta, workdir: cwd },
|
||||||
|
} as StartStep;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await compiled.roles.main(start, messages);
|
return await innerRole(innerStart, messages);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const msg = e instanceof Error ? e.message : String(e);
|
const msg = e instanceof Error ? e.message : String(e);
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -1,9 +1,7 @@
|
|||||||
import type { Role, RoleResult, Schema, StartStep, WorkflowMessage } from "@uncaged/nerve-core";
|
import type { Role, RoleResult, StartStep, WorkflowMessage } from "@uncaged/nerve-core";
|
||||||
import { END } from "@uncaged/nerve-core";
|
|
||||||
import { compileWorkflowSpec } from "@uncaged/nerve-daemon";
|
|
||||||
import { createCursorAdapter } from "@uncaged/nerve-adapter-cursor";
|
import { createCursorAdapter } from "@uncaged/nerve-adapter-cursor";
|
||||||
import type { LlmProvider } from "@uncaged/nerve-workflow-utils";
|
import type { LlmExtractorConfig } from "@uncaged/nerve-workflow-utils";
|
||||||
import { createLlmExtractFn, zodMeta } from "@uncaged/nerve-workflow-utils";
|
import { createRole } from "@uncaged/nerve-workflow-utils";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { resolveRepoCwd } from "../../lib/repo-context.js";
|
import { resolveRepoCwd } from "../../lib/repo-context.js";
|
||||||
@ -15,13 +13,13 @@ export const planMetaSchema = z.object({
|
|||||||
export type PlanMeta = z.infer<typeof planMetaSchema>;
|
export type PlanMeta = z.infer<typeof planMetaSchema>;
|
||||||
|
|
||||||
export type BuildPlanDeps = {
|
export type BuildPlanDeps = {
|
||||||
provider: LlmProvider;
|
extract: LlmExtractorConfig;
|
||||||
nerveRoot: string;
|
nerveRoot: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const CURSOR_TIMEOUT_MS = 300_000;
|
const CURSOR_TIMEOUT_MS = 300_000;
|
||||||
|
|
||||||
export function buildPlanRole({ provider, nerveRoot }: BuildPlanDeps): Role<PlanMeta> {
|
export function buildPlanRole({ extract, nerveRoot }: BuildPlanDeps): Role<PlanMeta> {
|
||||||
return async (start: StartStep, messages: WorkflowMessage[]): Promise<RoleResult<PlanMeta>> => {
|
return async (start: StartStep, messages: WorkflowMessage[]): Promise<RoleResult<PlanMeta>> => {
|
||||||
const cwd = resolveRepoCwd(messages);
|
const cwd = resolveRepoCwd(messages);
|
||||||
if (cwd === null) {
|
if (cwd === null) {
|
||||||
@ -31,43 +29,29 @@ export function buildPlanRole({ provider, nerveRoot }: BuildPlanDeps): Role<Plan
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const innerSpec = {
|
const innerRole = createRole(
|
||||||
main: {
|
createCursorAdapter({
|
||||||
adapter: createCursorAdapter({
|
|
||||||
type: "cursor",
|
type: "cursor",
|
||||||
|
mode: "ask",
|
||||||
model: "auto",
|
model: "auto",
|
||||||
timeout: CURSOR_TIMEOUT_MS,
|
timeout: CURSOR_TIMEOUT_MS,
|
||||||
mode: "ask",
|
|
||||||
}),
|
}),
|
||||||
prompt: async (start: StartStep) =>
|
async (innerStart: StartStep) =>
|
||||||
buildPlanPrompt({
|
buildPlanPrompt({
|
||||||
threadId: start.meta.threadId,
|
threadId: innerStart.meta.threadId,
|
||||||
nerveRoot,
|
nerveRoot,
|
||||||
}),
|
}),
|
||||||
meta: zodMeta(planMetaSchema),
|
planMetaSchema,
|
||||||
},
|
extract,
|
||||||
};
|
|
||||||
|
|
||||||
const compiled = compileWorkflowSpec(
|
|
||||||
{
|
|
||||||
name: "_plan-inner",
|
|
||||||
roles: innerSpec,
|
|
||||||
moderator: () => END,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
extractFn: async <T>(raw: string, schema: Schema<T>, dryRun: boolean) =>
|
|
||||||
createLlmExtractFn<T>({ provider, dryRun })(raw, schema),
|
|
||||||
createContext: (s, m) => ({
|
|
||||||
start: s,
|
|
||||||
messages: m,
|
|
||||||
workdir: cwd,
|
|
||||||
signal: new AbortController().signal,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const innerStart = {
|
||||||
|
...start,
|
||||||
|
meta: { ...start.meta, workdir: cwd },
|
||||||
|
} as StartStep;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await compiled.roles.main(start, messages);
|
return await innerRole(innerStart, messages);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const msg = e instanceof Error ? e.message : String(e);
|
const msg = e instanceof Error ? e.message : String(e);
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -1,11 +1,9 @@
|
|||||||
import { mkdirSync, writeFileSync } from "node:fs";
|
import { mkdirSync, writeFileSync } from "node:fs";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import type { Role, RoleResult, Schema, StartStep, WorkflowMessage } from "@uncaged/nerve-core";
|
import type { Role, RoleResult, StartStep, WorkflowMessage } from "@uncaged/nerve-core";
|
||||||
import { END } from "@uncaged/nerve-core";
|
|
||||||
import { compileWorkflowSpec } from "@uncaged/nerve-daemon";
|
|
||||||
import { hermesAdapter } from "@uncaged/nerve-adapter-hermes";
|
import { hermesAdapter } from "@uncaged/nerve-adapter-hermes";
|
||||||
import type { LlmProvider } from "@uncaged/nerve-workflow-utils";
|
import type { LlmExtractorConfig } from "@uncaged/nerve-workflow-utils";
|
||||||
import { createLlmExtractFn, isDryRun, zodMeta } from "@uncaged/nerve-workflow-utils";
|
import { createRole, isDryRun } from "@uncaged/nerve-workflow-utils";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { buildPublishPrompt } from "./prompt.js";
|
import { buildPublishPrompt } from "./prompt.js";
|
||||||
@ -16,42 +14,22 @@ export const publishMetaSchema = z.object({
|
|||||||
export type PublishMeta = z.infer<typeof publishMetaSchema>;
|
export type PublishMeta = z.infer<typeof publishMetaSchema>;
|
||||||
|
|
||||||
export type BuildPublishDeps = {
|
export type BuildPublishDeps = {
|
||||||
provider: LlmProvider;
|
extract: LlmExtractorConfig;
|
||||||
nerveRoot: string;
|
nerveRoot: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
function defaultAgentCreateContext(nerveRoot: string) {
|
|
||||||
return (start: StartStep, messages: WorkflowMessage[]) => ({
|
|
||||||
start,
|
|
||||||
messages,
|
|
||||||
workdir: nerveRoot,
|
|
||||||
signal: new AbortController().signal,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function logPath(nerveRoot: string): string {
|
function logPath(nerveRoot: string): string {
|
||||||
return join(nerveRoot, "logs", `solve-issue-publish-${Date.now()}.log`);
|
return join(nerveRoot, "logs", `solve-issue-publish-${Date.now()}.log`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildPublishRole({ provider, nerveRoot }: BuildPublishDeps): Role<PublishMeta> {
|
export function buildPublishRole({ extract, nerveRoot }: BuildPublishDeps): Role<PublishMeta> {
|
||||||
const runHermes = compileWorkflowSpec(
|
const innerRole = createRole(
|
||||||
{
|
hermesAdapter,
|
||||||
name: "_publish-inner",
|
async (start: StartStep) =>
|
||||||
roles: {
|
buildPublishPrompt({ threadId: start.meta.threadId, nerveRoot }),
|
||||||
main: {
|
publishMetaSchema,
|
||||||
adapter: hermesAdapter,
|
extract,
|
||||||
prompt: async (start: StartStep) => buildPublishPrompt({ threadId: start.meta.threadId, nerveRoot }),
|
);
|
||||||
meta: zodMeta(publishMetaSchema),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
moderator: () => END,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
extractFn: async <T>(raw: string, schema: Schema<T>, dryRun: boolean) =>
|
|
||||||
createLlmExtractFn<T>({ provider, dryRun })(raw, schema),
|
|
||||||
createContext: defaultAgentCreateContext(nerveRoot),
|
|
||||||
},
|
|
||||||
).roles.main;
|
|
||||||
|
|
||||||
return async (start: StartStep, messages: WorkflowMessage[]): Promise<RoleResult<PublishMeta>> => {
|
return async (start: StartStep, messages: WorkflowMessage[]): Promise<RoleResult<PublishMeta>> => {
|
||||||
const file = logPath(nerveRoot);
|
const file = logPath(nerveRoot);
|
||||||
@ -66,8 +44,13 @@ export function buildPublishRole({ provider, nerveRoot }: BuildPublishDeps): Rol
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const innerStart = {
|
||||||
|
...start,
|
||||||
|
meta: { ...start.meta, workdir: nerveRoot },
|
||||||
|
} as StartStep;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await runHermes(start, messages);
|
return await innerRole(innerStart, messages);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const msg = e instanceof Error ? e.message : String(e);
|
const msg = e instanceof Error ? e.message : String(e);
|
||||||
const body = `publish failed: ${msg}\n`;
|
const body = `publish failed: ${msg}\n`;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user