feat(core): RFC-003 Phase 1 — agent config types + nerve.yaml schema
- Add AgentFn, WorkflowContext (workdir + AbortSignal), ExtractFn, ExtractError - Add AgentConfig, ExtractConfig types to NerveConfig - Extend parseNerveConfig: agents (kebab-case keys) + extract sections - Export all new types from @nerve/core - Add config parse tests (7 new tests) - Update all existing test fixtures with agents/extract fields Closes #235 Ref: #234
This commit is contained in:
@@ -200,6 +200,8 @@ function defaultTestConfig(withNoopWorkflow: boolean): NerveConfig {
|
||||
...(withNoopWorkflow ? { noop: { concurrency: 1, overflow: "drop" as const } } : {}),
|
||||
},
|
||||
maxRounds: 10,
|
||||
agents: {},
|
||||
extract: null,
|
||||
api: { port: null, token: null, host: "127.0.0.1" },
|
||||
};
|
||||
}
|
||||
|
||||
@@ -52,6 +52,8 @@ describe("parseNerveConfig", () => {
|
||||
overflow: "queue",
|
||||
maxQueue: 10,
|
||||
});
|
||||
expect(result.value.agents).toEqual({});
|
||||
expect(result.value.extract).toBe(null);
|
||||
expect(result.value.api).toEqual({ port: null, token: null, host: "127.0.0.1" });
|
||||
});
|
||||
|
||||
@@ -220,6 +222,58 @@ senses:
|
||||
expect(result.value.senses.cpu.interval).toBe(5000);
|
||||
expect(result.value.senses.cpu.on).toEqual(["memory"]);
|
||||
});
|
||||
|
||||
it("parses agents and extract sections", () => {
|
||||
const yaml = `
|
||||
senses:
|
||||
cpu:
|
||||
group: system
|
||||
agents:
|
||||
developer:
|
||||
type: cursor
|
||||
model: auto
|
||||
timeout: 300s
|
||||
my-custom-agent:
|
||||
type: hermes
|
||||
model: auto
|
||||
extract:
|
||||
provider: dashscope
|
||||
model: qwen-plus
|
||||
`;
|
||||
const result = parseNerveConfig(yaml);
|
||||
expect(result.ok).toBe(true);
|
||||
if (!result.ok) return;
|
||||
expect(result.value.agents.developer).toEqual({
|
||||
type: "cursor",
|
||||
model: "auto",
|
||||
timeout: 300_000,
|
||||
});
|
||||
expect(result.value.agents["my-custom-agent"]).toEqual({
|
||||
type: "hermes",
|
||||
model: "auto",
|
||||
timeout: null,
|
||||
});
|
||||
expect(result.value.extract).toEqual({ provider: "dashscope", model: "qwen-plus" });
|
||||
});
|
||||
|
||||
it("allows arbitrary kebab-case agent names including multi-segment keys", () => {
|
||||
const yaml = `
|
||||
senses:
|
||||
cpu:
|
||||
group: system
|
||||
agents:
|
||||
a:
|
||||
type: x
|
||||
model: auto
|
||||
bb-cc-dd:
|
||||
type: y
|
||||
model: z
|
||||
`;
|
||||
const result = parseNerveConfig(yaml);
|
||||
expect(result.ok).toBe(true);
|
||||
if (!result.ok) return;
|
||||
expect(Object.keys(result.value.agents).sort()).toEqual(["a", "bb-cc-dd"]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("invalid configs", () => {
|
||||
@@ -449,5 +503,77 @@ workflows:
|
||||
if (result.ok) return;
|
||||
expect(result.error.message).toMatch(/max_queue.*not allowed.*drop/);
|
||||
});
|
||||
|
||||
it("returns error when agent key is not kebab-case", () => {
|
||||
const yaml = `
|
||||
senses:
|
||||
cpu:
|
||||
group: system
|
||||
agents:
|
||||
Developer:
|
||||
type: cursor
|
||||
model: auto
|
||||
`;
|
||||
const result = parseNerveConfig(yaml);
|
||||
expect(result.ok).toBe(false);
|
||||
if (result.ok) return;
|
||||
expect(result.error.message).toMatch(/invalid key "Developer"/);
|
||||
});
|
||||
|
||||
it("returns error when agent key uses underscores", () => {
|
||||
const yaml = `
|
||||
senses:
|
||||
cpu:
|
||||
group: system
|
||||
agents:
|
||||
my_agent:
|
||||
type: cursor
|
||||
model: auto
|
||||
`;
|
||||
const result = parseNerveConfig(yaml);
|
||||
expect(result.ok).toBe(false);
|
||||
if (result.ok) return;
|
||||
expect(result.error.message).toMatch(/invalid key "my_agent"/);
|
||||
});
|
||||
|
||||
it("returns error when agents section is not an object", () => {
|
||||
const yaml = `
|
||||
senses:
|
||||
cpu:
|
||||
group: system
|
||||
agents: []
|
||||
`;
|
||||
const result = parseNerveConfig(yaml);
|
||||
expect(result.ok).toBe(false);
|
||||
if (result.ok) return;
|
||||
expect(result.error.message).toMatch(/agents: must be an object/);
|
||||
});
|
||||
|
||||
it("returns error when extract section is not an object", () => {
|
||||
const yaml = `
|
||||
senses:
|
||||
cpu:
|
||||
group: system
|
||||
extract: "dashscope"
|
||||
`;
|
||||
const result = parseNerveConfig(yaml);
|
||||
expect(result.ok).toBe(false);
|
||||
if (result.ok) return;
|
||||
expect(result.error.message).toMatch(/extract: must be an object/);
|
||||
});
|
||||
|
||||
it("returns error when extract.provider is missing", () => {
|
||||
const yaml = `
|
||||
senses:
|
||||
cpu:
|
||||
group: system
|
||||
extract:
|
||||
model: qwen-plus
|
||||
`;
|
||||
const result = parseNerveConfig(yaml);
|
||||
expect(result.ok).toBe(false);
|
||||
if (result.ok) return;
|
||||
expect(result.error.message).toMatch(/extract\.provider/);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -36,6 +36,21 @@ export type NerveApiConfig = {
|
||||
host: string;
|
||||
};
|
||||
|
||||
/** Agent adapter defaults keyed by arbitrary kebab-case names in `nerve.yaml` (RFC-003). */
|
||||
export type AgentConfig = {
|
||||
/** Adapter id (e.g. `cursor`, `hermes`, `codex`). */
|
||||
type: string;
|
||||
/** Model id or `"auto"` for adapter defaults. */
|
||||
model: string;
|
||||
timeout: number | null;
|
||||
};
|
||||
|
||||
/** Global extract provider for typed meta from agent raw output (RFC-003). */
|
||||
export type ExtractConfig = {
|
||||
provider: string;
|
||||
model: string;
|
||||
};
|
||||
|
||||
/** Parameters for starting a workflow from a Sense compute result (or CLI trigger). */
|
||||
export type WorkflowTrigger = {
|
||||
name: string;
|
||||
@@ -56,4 +71,8 @@ export type NerveConfig = {
|
||||
senses: Record<string, SenseConfig>;
|
||||
workflows: Record<string, WorkflowConfig>;
|
||||
api: NerveApiConfig;
|
||||
/** Named agent adapters; keys must be kebab-case (RFC-003). */
|
||||
agents: Record<string, AgentConfig>;
|
||||
/** Global extract defaults; `null` when the section is omitted. */
|
||||
extract: ExtractConfig | null;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Extract layer types — parses agent raw string output into typed meta (RFC-003).
|
||||
*/
|
||||
|
||||
/** Structured meta validation descriptor for `ExtractFn`; concrete validators are provider-defined. */
|
||||
export type Schema<T> = {
|
||||
readonly witness: T | null;
|
||||
};
|
||||
|
||||
export type ExtractFn<T> = (raw: string, schema: Schema<T>) => Promise<T>;
|
||||
|
||||
export class ExtractError extends Error {
|
||||
readonly raw: string;
|
||||
readonly causeError: Error | null;
|
||||
|
||||
constructor(message: string, detail: { raw: string; causeError: Error | null }) {
|
||||
super(message);
|
||||
this.name = "ExtractError";
|
||||
this.raw = detail.raw;
|
||||
this.causeError = detail.causeError;
|
||||
Object.setPrototypeOf(this, new.target.prototype);
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,8 @@ export type {
|
||||
QueueOverflowConfig,
|
||||
WorkflowConfig,
|
||||
NerveApiConfig,
|
||||
AgentConfig,
|
||||
ExtractConfig,
|
||||
NerveConfig,
|
||||
WorkflowTrigger,
|
||||
ComputeResult,
|
||||
@@ -17,12 +19,16 @@ export type {
|
||||
Role,
|
||||
RoleMeta,
|
||||
StartStep,
|
||||
WorkflowContext,
|
||||
AgentFn,
|
||||
RoleStep,
|
||||
ModeratorContext,
|
||||
Moderator,
|
||||
WorkflowDefinition,
|
||||
} from "./workflow.js";
|
||||
export { START, END, DEFAULT_ENGINE_MAX_ROUNDS } from "./workflow.js";
|
||||
export type { Schema, ExtractFn } from "./extract-layer.js";
|
||||
export { ExtractError } from "./extract-layer.js";
|
||||
export type { Result } from "./result.js";
|
||||
export { ok, err } from "./result.js";
|
||||
export { parseNerveConfig } from "./parse-nerve-config.js";
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { parse } from "yaml";
|
||||
|
||||
import {
|
||||
type AgentConfig,
|
||||
DEFAULT_SENSE_SIGNAL_RETENTION,
|
||||
type ExtractConfig,
|
||||
type NerveApiConfig,
|
||||
type NerveConfig,
|
||||
type SenseConfig,
|
||||
@@ -30,6 +32,11 @@ function isValidGroupName(value: string): boolean {
|
||||
return /^[a-zA-Z0-9_-]+$/.test(value);
|
||||
}
|
||||
|
||||
/** Agent map keys in nerve.yaml — arbitrary kebab-case labels (RFC-003). */
|
||||
function isValidAgentKebabName(name: string): boolean {
|
||||
return /^[a-z0-9]+(-[a-z0-9]+)*$/.test(name);
|
||||
}
|
||||
|
||||
function parseRetentionField(name: string, field: unknown): Result<number> {
|
||||
if (field === undefined || field === null) {
|
||||
return ok(DEFAULT_SENSE_SIGNAL_RETENTION);
|
||||
@@ -281,6 +288,81 @@ function parseWorkflows(obj: Record<string, unknown>): Result<Record<string, Wor
|
||||
return ok(workflows);
|
||||
}
|
||||
|
||||
function validateAgentConfig(agentKey: string, raw: unknown): Result<AgentConfig> {
|
||||
if (!isPlainRecord(raw)) {
|
||||
return err(new Error(`agents.${agentKey}: must be an object`));
|
||||
}
|
||||
|
||||
const obj = raw;
|
||||
|
||||
if (typeof obj.type !== "string" || obj.type.trim() === "") {
|
||||
return err(new Error(`agents.${agentKey}.type: required non-empty string`));
|
||||
}
|
||||
|
||||
if (typeof obj.model !== "string" || obj.model.trim() === "") {
|
||||
return err(new Error(`agents.${agentKey}.model: required non-empty string`));
|
||||
}
|
||||
|
||||
const timeoutResult = parseDurationField(obj.timeout, `agents.${agentKey}.timeout`);
|
||||
if (!timeoutResult.ok) return timeoutResult;
|
||||
|
||||
return ok({
|
||||
type: obj.type,
|
||||
model: obj.model,
|
||||
timeout: timeoutResult.value,
|
||||
});
|
||||
}
|
||||
|
||||
function parseAgents(obj: Record<string, unknown>): Result<Record<string, AgentConfig>> {
|
||||
if (obj.agents === undefined || obj.agents === null) {
|
||||
return ok({});
|
||||
}
|
||||
|
||||
if (!isPlainRecord(obj.agents)) {
|
||||
return err(new Error("agents: must be an object if provided"));
|
||||
}
|
||||
|
||||
const agents: Record<string, AgentConfig> = {};
|
||||
|
||||
for (const [name, agentRaw] of Object.entries(obj.agents)) {
|
||||
if (!isValidAgentKebabName(name)) {
|
||||
return err(
|
||||
new Error(
|
||||
`agents: invalid key "${name}" (expected kebab-case: lowercase letters, digits, single hyphens between segments)`,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
const result = validateAgentConfig(name, agentRaw);
|
||||
if (!result.ok) return result;
|
||||
agents[name] = result.value;
|
||||
}
|
||||
|
||||
return ok(agents);
|
||||
}
|
||||
|
||||
function parseExtract(obj: Record<string, unknown>): Result<ExtractConfig | null> {
|
||||
if (obj.extract === undefined || obj.extract === null) {
|
||||
return ok(null);
|
||||
}
|
||||
|
||||
if (!isPlainRecord(obj.extract)) {
|
||||
return err(new Error("extract: must be an object if provided"));
|
||||
}
|
||||
|
||||
const ext = obj.extract;
|
||||
|
||||
if (typeof ext.provider !== "string" || ext.provider.trim() === "") {
|
||||
return err(new Error("extract.provider: required non-empty string"));
|
||||
}
|
||||
|
||||
if (typeof ext.model !== "string" || ext.model.trim() === "") {
|
||||
return err(new Error("extract.model: required non-empty string"));
|
||||
}
|
||||
|
||||
return ok({ provider: ext.provider, model: ext.model });
|
||||
}
|
||||
|
||||
export function parseNerveConfig(raw: string): Result<NerveConfig> {
|
||||
let parsed: unknown;
|
||||
|
||||
@@ -319,10 +401,18 @@ export function parseNerveConfig(raw: string): Result<NerveConfig> {
|
||||
const apiResult = parseApiConfig(obj);
|
||||
if (!apiResult.ok) return apiResult;
|
||||
|
||||
const agentsResult = parseAgents(obj);
|
||||
if (!agentsResult.ok) return agentsResult;
|
||||
|
||||
const extractResult = parseExtract(obj);
|
||||
if (!extractResult.ok) return extractResult;
|
||||
|
||||
return ok({
|
||||
maxRounds: maxRoundsResult.value,
|
||||
senses,
|
||||
workflows: workflowsResult.value,
|
||||
api: apiResult.value,
|
||||
agents: agentsResult.value,
|
||||
extract: extractResult.value,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -44,6 +44,17 @@ export type StartStep = {
|
||||
timestamp: number;
|
||||
};
|
||||
|
||||
/** Thread context passed to agent adapters (RFC-003): conversation frame, repo root, cancellation. */
|
||||
export type WorkflowContext = {
|
||||
start: StartStep;
|
||||
messages: WorkflowMessage[];
|
||||
workdir: string;
|
||||
signal: AbortSignal;
|
||||
};
|
||||
|
||||
/** Unified agent invocation — raw string output; structured meta uses the extract layer. */
|
||||
export type AgentFn = (prompt: string, context: WorkflowContext) => Promise<string>;
|
||||
|
||||
/** A discriminated union of role steps after each execution, aligned with `StartStep` shape. */
|
||||
export type RoleStep<M extends RoleMeta> = {
|
||||
[K in keyof M & string]: { role: K; meta: M[K]; content: string; timestamp: number };
|
||||
|
||||
@@ -64,6 +64,8 @@ function makeConfig(workflows: Record<string, WorkflowConfig> = {}): NerveConfig
|
||||
senses: {},
|
||||
workflows,
|
||||
maxRounds: 10,
|
||||
agents: {},
|
||||
extract: null,
|
||||
api: { port: null, token: null, host: "127.0.0.1" },
|
||||
};
|
||||
}
|
||||
|
||||
@@ -70,6 +70,8 @@ function makeWfConfig(workflows: Record<string, WorkflowConfig> = {}): NerveConf
|
||||
senses: {},
|
||||
workflows,
|
||||
maxRounds: 10,
|
||||
agents: {},
|
||||
extract: null,
|
||||
api: { port: null, token: null, host: "127.0.0.1" },
|
||||
};
|
||||
}
|
||||
@@ -459,6 +461,8 @@ describe("Kernel — workflow hot reload via file-watcher (Phase 3)", () => {
|
||||
senses: {},
|
||||
workflows: { "my-wf": { concurrency: 1, overflow: "drop" } },
|
||||
maxRounds: 10,
|
||||
agents: {},
|
||||
extract: null,
|
||||
api: { port: null, token: null, host: "127.0.0.1" },
|
||||
};
|
||||
|
||||
@@ -494,6 +498,8 @@ describe("Kernel — workflow hot reload via file-watcher (Phase 3)", () => {
|
||||
senses: {},
|
||||
workflows: { "old-wf": { concurrency: 1, overflow: "drop" } },
|
||||
maxRounds: 10,
|
||||
agents: {},
|
||||
extract: null,
|
||||
api: { port: null, token: null, host: "127.0.0.1" },
|
||||
};
|
||||
|
||||
@@ -515,6 +521,8 @@ describe("Kernel — workflow hot reload via file-watcher (Phase 3)", () => {
|
||||
senses: {},
|
||||
workflows: {},
|
||||
maxRounds: 10,
|
||||
agents: {},
|
||||
extract: null,
|
||||
api: { port: null, token: null, host: "127.0.0.1" },
|
||||
};
|
||||
kernel.reloadConfig(newConfig);
|
||||
@@ -537,6 +545,8 @@ describe("Kernel — workflow hot reload via file-watcher (Phase 3)", () => {
|
||||
senses: {},
|
||||
workflows: { "my-wf": { concurrency: 1, overflow: "drop" } },
|
||||
maxRounds: 10,
|
||||
agents: {},
|
||||
extract: null,
|
||||
api: { port: null, token: null, host: "127.0.0.1" },
|
||||
};
|
||||
|
||||
@@ -553,6 +563,8 @@ describe("Kernel — workflow hot reload via file-watcher (Phase 3)", () => {
|
||||
senses: {},
|
||||
workflows: { "my-wf": { concurrency: 5, overflow: "queue", maxQueue: 50 } },
|
||||
maxRounds: 10,
|
||||
agents: {},
|
||||
extract: null,
|
||||
api: { port: null, token: null, host: "127.0.0.1" },
|
||||
};
|
||||
kernel.reloadConfig(newConfig);
|
||||
|
||||
@@ -37,6 +37,8 @@ function makeConfig(overrides: Partial<NerveConfig> = {}): NerveConfig {
|
||||
},
|
||||
workflows: {},
|
||||
maxRounds: 10,
|
||||
agents: {},
|
||||
extract: null,
|
||||
api: { port: null, token: null, host: "127.0.0.1" },
|
||||
...overrides,
|
||||
};
|
||||
|
||||
@@ -85,6 +85,8 @@ function makeConfig(overrides: Partial<NerveConfig> = {}): NerveConfig {
|
||||
},
|
||||
workflows: {},
|
||||
maxRounds: 10,
|
||||
agents: {},
|
||||
extract: null,
|
||||
api: { port: null, token: null, host: "127.0.0.1" },
|
||||
...overrides,
|
||||
};
|
||||
@@ -244,6 +246,8 @@ describe("kernel — reloadConfig", () => {
|
||||
},
|
||||
workflows: {},
|
||||
maxRounds: 10,
|
||||
agents: {},
|
||||
extract: null,
|
||||
api: { port: null, token: null, host: "127.0.0.1" },
|
||||
});
|
||||
|
||||
@@ -277,6 +281,8 @@ describe("kernel — reloadConfig", () => {
|
||||
},
|
||||
workflows: {},
|
||||
maxRounds: 10,
|
||||
agents: {},
|
||||
extract: null,
|
||||
api: { port: null, token: null, host: "127.0.0.1" },
|
||||
};
|
||||
const kernel = createKernel(config, nerveRoot);
|
||||
@@ -300,6 +306,8 @@ describe("kernel — reloadConfig", () => {
|
||||
},
|
||||
workflows: {},
|
||||
maxRounds: 10,
|
||||
agents: {},
|
||||
extract: null,
|
||||
api: { port: null, token: null, host: "127.0.0.1" },
|
||||
});
|
||||
|
||||
@@ -339,6 +347,8 @@ describe("kernel — reloadConfig", () => {
|
||||
},
|
||||
workflows: {},
|
||||
maxRounds: 10,
|
||||
agents: {},
|
||||
extract: null,
|
||||
api: { port: null, token: null, host: "127.0.0.1" },
|
||||
});
|
||||
|
||||
|
||||
@@ -105,6 +105,8 @@ function makeConfig(overrides: Partial<NerveConfig> = {}): NerveConfig {
|
||||
},
|
||||
workflows: {},
|
||||
maxRounds: 10,
|
||||
agents: {},
|
||||
extract: null,
|
||||
api: { port: null, token: null, host: "127.0.0.1" },
|
||||
...overrides,
|
||||
};
|
||||
|
||||
@@ -117,6 +117,8 @@ function makeConfig(overrides: Partial<NerveConfig> = {}): NerveConfig {
|
||||
},
|
||||
workflows: {},
|
||||
maxRounds: 10,
|
||||
agents: {},
|
||||
extract: null,
|
||||
api: { port: null, token: null, host: "127.0.0.1" },
|
||||
...overrides,
|
||||
};
|
||||
@@ -455,6 +457,8 @@ describe("kernel + workflowManager integration", () => {
|
||||
},
|
||||
workflows: { "new-workflow": { concurrency: 1, overflow: "drop" } },
|
||||
maxRounds: 10,
|
||||
agents: {},
|
||||
extract: null,
|
||||
api: { port: null, token: null, host: "127.0.0.1" },
|
||||
};
|
||||
kernel.reloadConfig(newConfig);
|
||||
@@ -531,6 +535,8 @@ describe("kernel + workflowManager integration", () => {
|
||||
},
|
||||
workflows: {},
|
||||
maxRounds: 10,
|
||||
agents: {},
|
||||
extract: null,
|
||||
api: { port: null, token: null, host: "127.0.0.1" },
|
||||
};
|
||||
kernel.reloadConfig(newConfig);
|
||||
|
||||
@@ -74,6 +74,8 @@ function makeConfig(overrides: Partial<NerveConfig> = {}): NerveConfig {
|
||||
},
|
||||
workflows: {},
|
||||
maxRounds: 10,
|
||||
agents: {},
|
||||
extract: null,
|
||||
api: { port: null, token: null, host: "127.0.0.1" },
|
||||
...overrides,
|
||||
};
|
||||
@@ -285,6 +287,8 @@ describe("kernel — groupForSense mapping", () => {
|
||||
},
|
||||
workflows: {},
|
||||
maxRounds: 10,
|
||||
agents: {},
|
||||
extract: null,
|
||||
api: { port: null, token: null, host: "127.0.0.1" },
|
||||
};
|
||||
const kernel = createKernel(config, nerveRoot);
|
||||
|
||||
@@ -38,6 +38,8 @@ describe("LogStore + SenseScheduler integration", () => {
|
||||
},
|
||||
workflows: {},
|
||||
maxRounds: 10,
|
||||
agents: {},
|
||||
extract: null,
|
||||
api: { port: null, token: null, host: "127.0.0.1" },
|
||||
};
|
||||
const bus = createSignalBus();
|
||||
@@ -74,6 +76,8 @@ describe("LogStore + SenseScheduler integration", () => {
|
||||
},
|
||||
workflows: {},
|
||||
maxRounds: 10,
|
||||
agents: {},
|
||||
extract: null,
|
||||
api: { port: null, token: null, host: "127.0.0.1" },
|
||||
};
|
||||
const bus = createSignalBus();
|
||||
@@ -113,6 +117,8 @@ describe("LogStore + SenseScheduler integration", () => {
|
||||
},
|
||||
workflows: {},
|
||||
maxRounds: 10,
|
||||
agents: {},
|
||||
extract: null,
|
||||
api: { port: null, token: null, host: "127.0.0.1" },
|
||||
};
|
||||
const bus = createSignalBus();
|
||||
|
||||
@@ -34,6 +34,8 @@ function makeConfig(overrides: Partial<NerveConfig> = {}): NerveConfig {
|
||||
},
|
||||
workflows: {},
|
||||
maxRounds: 10,
|
||||
agents: {},
|
||||
extract: null,
|
||||
api: { port: null, token: null, host: "127.0.0.1" },
|
||||
...overrides,
|
||||
};
|
||||
@@ -169,6 +171,8 @@ describe("phase6 — reloadConfig", () => {
|
||||
},
|
||||
workflows: {},
|
||||
maxRounds: 10,
|
||||
agents: {},
|
||||
extract: null,
|
||||
api: { port: null, token: null, host: "127.0.0.1" },
|
||||
};
|
||||
|
||||
@@ -205,6 +209,8 @@ describe("phase6 — reloadConfig", () => {
|
||||
},
|
||||
workflows: {},
|
||||
maxRounds: 10,
|
||||
agents: {},
|
||||
extract: null,
|
||||
api: { port: null, token: null, host: "127.0.0.1" },
|
||||
};
|
||||
kernel = createKernel(config, nerveRoot, {
|
||||
@@ -228,6 +234,8 @@ describe("phase6 — reloadConfig", () => {
|
||||
},
|
||||
workflows: {},
|
||||
maxRounds: 10,
|
||||
agents: {},
|
||||
extract: null,
|
||||
api: { port: null, token: null, host: "127.0.0.1" },
|
||||
};
|
||||
|
||||
@@ -281,6 +289,8 @@ describe("phase6 — error isolation", () => {
|
||||
},
|
||||
workflows: {},
|
||||
maxRounds: 10,
|
||||
agents: {},
|
||||
extract: null,
|
||||
api: { port: null, token: null, host: "127.0.0.1" },
|
||||
};
|
||||
|
||||
@@ -431,6 +441,8 @@ describe("phase6 — getHealth", () => {
|
||||
},
|
||||
workflows: {},
|
||||
maxRounds: 10,
|
||||
agents: {},
|
||||
extract: null,
|
||||
api: { port: null, token: null, host: "127.0.0.1" },
|
||||
};
|
||||
kernel.reloadConfig(newConfig);
|
||||
|
||||
@@ -19,6 +19,8 @@ function makeConfig(overrides: Partial<NerveConfig> = {}): NerveConfig {
|
||||
},
|
||||
workflows: {},
|
||||
maxRounds: 10,
|
||||
agents: {},
|
||||
extract: null,
|
||||
api: { port: null, token: null, host: "127.0.0.1" },
|
||||
...overrides,
|
||||
};
|
||||
|
||||
@@ -41,6 +41,8 @@ function makeConfig(overrides: Partial<NerveConfig> = {}): NerveConfig {
|
||||
},
|
||||
workflows: {},
|
||||
maxRounds: 10,
|
||||
agents: {},
|
||||
extract: null,
|
||||
api: { port: null, token: null, host: "127.0.0.1" },
|
||||
...overrides,
|
||||
};
|
||||
|
||||
@@ -89,6 +89,8 @@ function makeConfig(overrides: Partial<NerveConfig["workflows"]> = {}): NerveCon
|
||||
maxRounds: 10,
|
||||
senses: {},
|
||||
workflows: overrides as NerveConfig["workflows"],
|
||||
agents: {},
|
||||
extract: null,
|
||||
api: { port: null, token: null, host: "127.0.0.1" },
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user