@hilbras/omninode 2.0.0-alpha.15 → 2.0.0-alpha.17

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/dist/index.d.ts CHANGED
@@ -509,6 +509,24 @@ interface TaskResult {
509
509
  reportId?: string;
510
510
  /** Structured reports produced by protocol-mode agents (Phase 6 builds on this). */
511
511
  reports?: Report[];
512
+ /** Protocol-mode diagnostics (descriptor, questions, violations) — see docs/PROTOCOL.md. */
513
+ protocol?: {
514
+ protocol: string;
515
+ legacy: boolean;
516
+ descriptor?: {
517
+ protocol: string;
518
+ name: string;
519
+ version?: string;
520
+ capabilities: string[];
521
+ supports: string[];
522
+ };
523
+ questions: Array<{
524
+ questionId: string;
525
+ question: string;
526
+ answer?: string;
527
+ }>;
528
+ violations: string[];
529
+ };
512
530
  /** Truncated verbatim agent output, kept for report extraction and audit. */
513
531
  rawOutput?: string;
514
532
  error?: string;
@@ -1502,6 +1520,10 @@ declare const projectConfigSchema: z.ZodObject<{
1502
1520
  } | undefined;
1503
1521
  }>;
1504
1522
  declare const appConfigSchema: z.ZodObject<{
1523
+ /** Active profile; overlays live under `profiles` (§16). */
1524
+ profile: z.ZodOptional<z.ZodString>;
1525
+ /** Named partial overlays, e.g. development/production/testing. */
1526
+ profiles: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1505
1527
  project: z.ZodObject<{
1506
1528
  name: z.ZodString;
1507
1529
  providers: z.ZodDefault<z.ZodArray<z.ZodObject<{
@@ -1924,6 +1946,8 @@ declare const appConfigSchema: z.ZodObject<{
1924
1946
  required?: boolean | undefined;
1925
1947
  } | undefined;
1926
1948
  };
1949
+ profile?: string | undefined;
1950
+ profiles?: Record<string, unknown> | undefined;
1927
1951
  logging?: {
1928
1952
  level: "info" | "error" | "debug" | "warn";
1929
1953
  format?: "text" | "json" | undefined;
@@ -1997,6 +2021,8 @@ declare const appConfigSchema: z.ZodObject<{
1997
2021
  required?: boolean | undefined;
1998
2022
  } | undefined;
1999
2023
  };
2024
+ profile?: string | undefined;
2025
+ profiles?: Record<string, unknown> | undefined;
2000
2026
  logging?: {
2001
2027
  level: "info" | "error" | "debug" | "warn";
2002
2028
  format?: "text" | "json" | undefined;
@@ -2055,11 +2081,13 @@ interface AppConfig {
2055
2081
  };
2056
2082
  }
2057
2083
  interface LoadConfigOptions {
2058
- /** Explicit path; otherwise omninode.yaml/yml/json is looked up in the given directory. */
2084
+ /** Explicit path (CLI --config); otherwise the project file is discovered. */
2059
2085
  path?: string;
2060
2086
  directory?: string;
2061
- /** Environment used for ${VAR} expansion; defaults to process.env. */
2087
+ /** Environment used for ${VAR} expansion and OMNINODE_* overrides; defaults to process.env. */
2062
2088
  env?: Record<string, string | undefined>;
2089
+ /** Profile override (CLI --profile), above environment and file selection. */
2090
+ profile?: string;
2063
2091
  }
2064
2092
  declare function findConfigFile(directory?: string): string | undefined;
2065
2093
  /**
@@ -2068,7 +2096,58 @@ declare function findConfigFile(directory?: string): string | undefined;
2068
2096
  * authentication.
2069
2097
  */
2070
2098
  declare function expandEnvRefs(text: string, env: Record<string, string | undefined>, source?: string): string;
2099
+ interface LoadConfigResult {
2100
+ config: AppConfig;
2101
+ sources: ConfigSources;
2102
+ }
2103
+ /**
2104
+ * Composes configuration from every source with the documented precedence
2105
+ * (roadmap §16): CLI → environment → project file → user file → profile
2106
+ * overlay → defaults.
2107
+ */
2108
+ declare function loadConfigDetailed(options?: LoadConfigOptions): LoadConfigResult;
2109
+ /** Backwards-compatible wrapper: the composed configuration. */
2071
2110
  declare function loadConfig(options?: LoadConfigOptions): AppConfig;
2111
+ /**
2112
+ * Actionable configuration diagnostics (roadmap §16) — the kind of message a
2113
+ * user can act on, rather than a generic schema failure.
2114
+ */
2115
+ declare function configDiagnostics(config: AppConfig): string[];
2116
+
2117
+ /** Loose input shape: composition happens before strict validation. */
2118
+ type AppConfigInput = Record<string, unknown>;
2119
+ declare const KNOWN_PROFILES: readonly ["default", "development", "production", "testing"];
2120
+ type ProfileName = (typeof KNOWN_PROFILES)[number];
2121
+ interface ConfigSources {
2122
+ /** Project-level configuration file, if any. */
2123
+ projectFile?: string;
2124
+ /** User-level configuration file, if any. */
2125
+ userFile?: string;
2126
+ /** Active profile. */
2127
+ profile: string;
2128
+ /** Environment variables that overrode configuration. */
2129
+ envOverrides: string[];
2130
+ /** Values that came from built-in defaults. */
2131
+ defaults: string[];
2132
+ }
2133
+ /** Default user-level configuration locations, most portable first. */
2134
+ declare function userConfigPath(env?: Record<string, string | undefined>): string;
2135
+ /** Picks the profile: CLI (applied by the loader) > environment > file > default. */
2136
+ declare function resolveProfile(input: {
2137
+ profile?: unknown;
2138
+ }, env: Record<string, string | undefined>): string;
2139
+ /** Deep-merges configuration overlays (objects merge, scalars/arrays replace). */
2140
+ declare function mergeConfig<T>(base: T, overlay: unknown): T;
2141
+ /** Environment overrides applied above every file source. */
2142
+ declare function applyEnvOverrides(input: AppConfigInput, env: Record<string, string | undefined>): {
2143
+ config: AppConfigInput;
2144
+ applied: string[];
2145
+ };
2146
+ /** Built-in defaults, applied last. */
2147
+ declare function applyDefaults(input: AppConfigInput): {
2148
+ config: AppConfigInput;
2149
+ applied: string[];
2150
+ };
2072
2151
 
2073
2152
  /** Template written by `omninode init` (§22, §23). */
2074
2153
  declare function defaultProjectConfigYaml(projectName?: string): string;
@@ -3151,6 +3230,6 @@ declare function migrateProjectStores(directory?: string, options?: {
3151
3230
  checkOnly?: boolean;
3152
3231
  }): Promise<MigrationReport[]>;
3153
3232
 
3154
- declare const OMNINODE_VERSION = "2.0.0-alpha.15";
3233
+ declare const OMNINODE_VERSION = "2.0.0-alpha.17";
3155
3234
 
3156
- export { AgentAdapterRegistry, type AgentConfig, type AgentConfigYaml, type AgentDescriptor, AgentError, type AgentInfo, type AgentInputMode, type AgentIntegrationType, AgentRegistry, type AgentStatus, type AgentTaskInput, type AgentTaskOutput, type AgentToNodeMessage, type AggregateOptions, type AggregatedFinding, type AggregatedRecommendation, type AppConfig, type AppConfigYaml, type ArtifactKind, type ArtifactPayload, type AskedQuestion, type AuditAction, type AuditEvent, type AuditSink, type AuditSink as AuditStore, type ChatFn, type ChatMessage, type ChatRequest, type ChatResponse, type ChatRole, type ChatStreamChunk, type ChatUsage, type CollectionDiagnostics, type CombinedMeta, type CombinedReport, type CombinedReportFilter, type Confidence, ConfigError, type ConflictPosition, ConsoleLogSink, type CreateTaskInput, type CwdValidation, DEFAULT_MAX_OUTPUT_BYTES, type DecodeResult, type DefaultChatOptions, type DiscoveryResult, type DocumentState, type Durability, type EnvPolicy, type EnvelopeMeta, type Evidence, type EvidenceInput, type EvidenceKind, type ExecutionRecord, type ExtractionMeta, FileAuditLog, FilePipelineRunStore, FilePlanStore, FileReportStore, FileTaskStore, type Finding, type FindingOrigin, type FindingSeverity, type FindingSource, type HelloResult, HeuristicPlanner, type HttpRequestOptions, type HttpResponse, type IAgent, type IAgentAdapter, type IChatProvider, type IMemoryProvider, type IPipelineExecutor, type IPlanner, type IProvider, type JsonDocument, JsonFileStore, type JsonFileStoreOptions, type LoadConfigOptions, LocalMemoryProvider, type LogFormat, type LogLevel, type LogSink, Logger, type LoggerOptions, MAX_CONTEXT_CHARS, MAX_MESSAGE_BYTES, MAX_REPORT_BYTES, type MemoryCategory, type MemoryContextQuery, type MemoryEntry, MemoryError, type MemoryProviderConfig, type MemoryProviderMetadata, type MemoryQuery, type MemoryScope, MemoryService, type MemoryServiceOptions, LocalMemoryProvider as MemoryStore, type MemoryTaskContext, type MessageEnvelope, type MigrationReport, type Modality, type ModelCapabilities, type ModelFilter, type ModelInfo, ModelPlanner, ModelRegistry, type ModelStatus, type NodeToAgentMessage, OMNINODE_VERSION, OPENAI_COMPATIBLE_TYPES, type OmniHilbrasConnectionResult, OmniHilbrasProvider, OmniNodeError, type OmniNodeErrorCode, type OmniNodeErrorOptions, OpenAICompatibleProvider, PROTOCOL_V2, type PipelineConfigYaml, type PipelineDefinition, PipelineEngine, type PipelineEngineOptions, PipelineError, type PipelineRun, type PipelineRunAttempt, type PipelineRunFilter, type PipelineRunOptions, type PipelineRunStatus, type PipelineRunStore, type PipelineStep, type PipelineStepCondition, type PipelineStepConfigYaml, type PipelineStepKind, type PipelineStepRun, type PipelineRunStore as PipelineStore, type Plan, type PlanFilter, type PlanJsonResult, type PlanRequest, type PlanStep, type PlanStore, type PlanValidationIssue, type PlanValidationResult, type PlannerConfigYaml, PlannerError, ProcessAgent, ProcessAgentAdapter, type Project, type ProjectConfigYaml, ProjectStores, type ProtocolAgentToNodeType, ProtocolDecoder, type ProtocolEnvelope, ProtocolError, type ProtocolMessageType, type ProtocolNodeToAgentType, ProtocolSession, type ProtocolViolation, type ProtocolViolationCode, type ProviderAuthentication, type ProviderCapability, type ProviderConfig, type ProviderConfigYaml, ProviderError, type ProviderErrorKind, type ProviderHealth, type ProviderStatus, type ProviderType, type QuestionPayload, type QuestionResponder, type RememberaConfig, RememberaMemoryProvider, type Report, type ReportArtifact, type ReportConflict, type ReportFilter, type ReportPayload, ReportService, type ReportStore, type ReportValidationIssue, type ReportValidationResult, type RoleConfigYaml, type RoleDefinition, RoleRegistry, type RunPipelineOptions, SCHEMA_VERSION, STORE_FILES, SUPPORTED_PROTOCOL_VERSIONS, type SecretFinding, type SessionIds, TERMINAL_TASK_STATUSES, type Task, type TaskContext, TaskEngine, type TaskEngineOptions, TaskError, type TaskPayload, type TaskResult, type TaskStatus, type TaskStore, type TaskStoreFilter, type WritableLike, agentConfigSchema, aggregateReports, appConfigSchema, authHeaders, buildAgentRegistry, buildChildEnv, buildHello, buildHelloAck, buildPipelineEngine, buildPlanner, buildPlannerContext, buildRoleRegistry, composeTaskText, configureLogger, createAgent, createDefaultChatFn, createMemoryProvider, createProvider, createTaskEngine, decodeMessage, decodeStream, defaultMetadata, defaultProjectConfigYaml, defaultSearch, describeExit, describeSecretFindings, detectConflicts, encodeMessage, envelope, expandEnvRefs, extractMessage, extractReportFromText, findConfigFile, inspectDocument, isNetworkError, isOmniNodeError, isRetryable, isSupportedProtocol, isTimeoutError, keywords, killTree, kindFromStatus, loadConfig, logger, migrateProjectStores, newMessageId, normalizeEvidence, normalizeFinding, parseHello, parsePlanJson, parseRetryAfter, pipelineConfigSchema, pipelineStepConfigSchema, planJsonSchema, plannerConfigSchema, projectConfigSchema, providerConfigSchema, providerHttpError, providerTransportError, readDocument, registryKey, requestJson, requireCompletion, resolveApiKey, resolveEnvPolicy, roleConfigSchema, sanitizeChunk, scanForInlineSecrets, similar, validateEnvelope, validatePlan, validateReport, validateWorkingDirectory, writeFileAtomic };
3235
+ export { AgentAdapterRegistry, type AgentConfig, type AgentConfigYaml, type AgentDescriptor, AgentError, type AgentInfo, type AgentInputMode, type AgentIntegrationType, AgentRegistry, type AgentStatus, type AgentTaskInput, type AgentTaskOutput, type AgentToNodeMessage, type AggregateOptions, type AggregatedFinding, type AggregatedRecommendation, type AppConfig, type AppConfigInput, type AppConfigYaml, type ArtifactKind, type ArtifactPayload, type AskedQuestion, type AuditAction, type AuditEvent, type AuditSink, type AuditSink as AuditStore, type ChatFn, type ChatMessage, type ChatRequest, type ChatResponse, type ChatRole, type ChatStreamChunk, type ChatUsage, type CollectionDiagnostics, type CombinedMeta, type CombinedReport, type CombinedReportFilter, type Confidence, ConfigError, type ConfigSources, type ConflictPosition, ConsoleLogSink, type CreateTaskInput, type CwdValidation, DEFAULT_MAX_OUTPUT_BYTES, type DecodeResult, type DefaultChatOptions, type DiscoveryResult, type DocumentState, type Durability, type EnvPolicy, type EnvelopeMeta, type Evidence, type EvidenceInput, type EvidenceKind, type ExecutionRecord, type ExtractionMeta, FileAuditLog, FilePipelineRunStore, FilePlanStore, FileReportStore, FileTaskStore, type Finding, type FindingOrigin, type FindingSeverity, type FindingSource, type HelloResult, HeuristicPlanner, type HttpRequestOptions, type HttpResponse, type IAgent, type IAgentAdapter, type IChatProvider, type IMemoryProvider, type IPipelineExecutor, type IPlanner, type IProvider, type JsonDocument, JsonFileStore, type JsonFileStoreOptions, KNOWN_PROFILES, type LoadConfigOptions, type LoadConfigResult, LocalMemoryProvider, type LogFormat, type LogLevel, type LogSink, Logger, type LoggerOptions, MAX_CONTEXT_CHARS, MAX_MESSAGE_BYTES, MAX_REPORT_BYTES, type MemoryCategory, type MemoryContextQuery, type MemoryEntry, MemoryError, type MemoryProviderConfig, type MemoryProviderMetadata, type MemoryQuery, type MemoryScope, MemoryService, type MemoryServiceOptions, LocalMemoryProvider as MemoryStore, type MemoryTaskContext, type MessageEnvelope, type MigrationReport, type Modality, type ModelCapabilities, type ModelFilter, type ModelInfo, ModelPlanner, ModelRegistry, type ModelStatus, type NodeToAgentMessage, OMNINODE_VERSION, OPENAI_COMPATIBLE_TYPES, type OmniHilbrasConnectionResult, OmniHilbrasProvider, OmniNodeError, type OmniNodeErrorCode, type OmniNodeErrorOptions, OpenAICompatibleProvider, PROTOCOL_V2, type PipelineConfigYaml, type PipelineDefinition, PipelineEngine, type PipelineEngineOptions, PipelineError, type PipelineRun, type PipelineRunAttempt, type PipelineRunFilter, type PipelineRunOptions, type PipelineRunStatus, type PipelineRunStore, type PipelineStep, type PipelineStepCondition, type PipelineStepConfigYaml, type PipelineStepKind, type PipelineStepRun, type PipelineRunStore as PipelineStore, type Plan, type PlanFilter, type PlanJsonResult, type PlanRequest, type PlanStep, type PlanStore, type PlanValidationIssue, type PlanValidationResult, type PlannerConfigYaml, PlannerError, ProcessAgent, ProcessAgentAdapter, type ProfileName, type Project, type ProjectConfigYaml, ProjectStores, type ProtocolAgentToNodeType, ProtocolDecoder, type ProtocolEnvelope, ProtocolError, type ProtocolMessageType, type ProtocolNodeToAgentType, ProtocolSession, type ProtocolViolation, type ProtocolViolationCode, type ProviderAuthentication, type ProviderCapability, type ProviderConfig, type ProviderConfigYaml, ProviderError, type ProviderErrorKind, type ProviderHealth, type ProviderStatus, type ProviderType, type QuestionPayload, type QuestionResponder, type RememberaConfig, RememberaMemoryProvider, type Report, type ReportArtifact, type ReportConflict, type ReportFilter, type ReportPayload, ReportService, type ReportStore, type ReportValidationIssue, type ReportValidationResult, type RoleConfigYaml, type RoleDefinition, RoleRegistry, type RunPipelineOptions, SCHEMA_VERSION, STORE_FILES, SUPPORTED_PROTOCOL_VERSIONS, type SecretFinding, type SessionIds, TERMINAL_TASK_STATUSES, type Task, type TaskContext, TaskEngine, type TaskEngineOptions, TaskError, type TaskPayload, type TaskResult, type TaskStatus, type TaskStore, type TaskStoreFilter, type WritableLike, agentConfigSchema, aggregateReports, appConfigSchema, applyDefaults, applyEnvOverrides, authHeaders, buildAgentRegistry, buildChildEnv, buildHello, buildHelloAck, buildPipelineEngine, buildPlanner, buildPlannerContext, buildRoleRegistry, composeTaskText, configDiagnostics, configureLogger, createAgent, createDefaultChatFn, createMemoryProvider, createProvider, createTaskEngine, decodeMessage, decodeStream, defaultMetadata, defaultProjectConfigYaml, defaultSearch, describeExit, describeSecretFindings, detectConflicts, encodeMessage, envelope, expandEnvRefs, extractMessage, extractReportFromText, findConfigFile, inspectDocument, isNetworkError, isOmniNodeError, isRetryable, isSupportedProtocol, isTimeoutError, keywords, killTree, kindFromStatus, loadConfig, loadConfigDetailed, logger, mergeConfig, migrateProjectStores, newMessageId, normalizeEvidence, normalizeFinding, parseHello, parsePlanJson, parseRetryAfter, pipelineConfigSchema, pipelineStepConfigSchema, planJsonSchema, plannerConfigSchema, projectConfigSchema, providerConfigSchema, providerHttpError, providerTransportError, readDocument, registryKey, requestJson, requireCompletion, resolveApiKey, resolveEnvPolicy, resolveProfile, roleConfigSchema, sanitizeChunk, scanForInlineSecrets, similar, userConfigPath, validateEnvelope, validatePlan, validateReport, validateWorkingDirectory, writeFileAtomic };
package/dist/index.js CHANGED
@@ -210,6 +210,10 @@ var projectConfigSchema = z.object({
210
210
  }).strict().optional()
211
211
  }).strict();
212
212
  var appConfigSchema = z.object({
213
+ /** Active profile; overlays live under `profiles` (§16). */
214
+ profile: z.string().min(1).optional(),
215
+ /** Named partial overlays, e.g. development/production/testing. */
216
+ profiles: z.record(z.unknown()).optional(),
213
217
  project: projectConfigSchema,
214
218
  logging: z.object({
215
219
  level: z.enum(["debug", "info", "warn", "error"]),
@@ -250,6 +254,86 @@ ${lines}
250
254
  OmniNode never stores credentials in configuration. Replace each value with an environment-variable reference, e.g. api_key_env_var: MY_API_KEY.`;
251
255
  }
252
256
 
257
+ // src/config/profiles.ts
258
+ import { homedir } from "os";
259
+ import path from "path";
260
+ var KNOWN_PROFILES = ["default", "development", "production", "testing"];
261
+ function userConfigPath(env = process.env) {
262
+ if (env.OMNINODE_USER_CONFIG) return env.OMNINODE_USER_CONFIG;
263
+ const home = env.HOME ?? homedir();
264
+ return path.join(home, ".omninode", "config.yaml");
265
+ }
266
+ function resolveProfile(input, env) {
267
+ if (env.OMNINODE_PROFILE) return env.OMNINODE_PROFILE;
268
+ if (typeof input.profile === "string" && input.profile.length > 0) return input.profile;
269
+ return "default";
270
+ }
271
+ function mergeConfig(base, overlay) {
272
+ if (!isPlainObject(base) || !isPlainObject(overlay)) {
273
+ return overlay === void 0 ? base : overlay;
274
+ }
275
+ const result = { ...base };
276
+ for (const [key, value] of Object.entries(overlay)) {
277
+ const existing = result[key];
278
+ if (Array.isArray(value)) {
279
+ result[key] = value;
280
+ } else if (isPlainObject(value) && isPlainObject(existing)) {
281
+ result[key] = mergeConfig(existing, value);
282
+ } else {
283
+ result[key] = value;
284
+ }
285
+ }
286
+ return result;
287
+ }
288
+ function isPlainObject(value) {
289
+ return value !== null && typeof value === "object" && !Array.isArray(value);
290
+ }
291
+ function asObject(value) {
292
+ return isPlainObject(value) ? value : {};
293
+ }
294
+ function applyEnvOverrides(input, env) {
295
+ const applied = [];
296
+ const config = mergeConfig(input, {});
297
+ if (env.OMNINODE_LOG_LEVEL) {
298
+ config.logging = { ...asObject(config.logging), level: env.OMNINODE_LOG_LEVEL };
299
+ applied.push("OMNINODE_LOG_LEVEL");
300
+ }
301
+ if (env.OMNINODE_LOG_FORMAT) {
302
+ config.logging = { ...asObject(config.logging), format: env.OMNINODE_LOG_FORMAT };
303
+ applied.push("OMNINODE_LOG_FORMAT");
304
+ }
305
+ const memoryProvider = env.OMNINODE_MEMORY_PROVIDER;
306
+ const memoryRequired = env.OMNINODE_MEMORY_REQUIRED;
307
+ if (memoryProvider || memoryRequired) {
308
+ const project = asObject(config.project);
309
+ config.project = {
310
+ ...project,
311
+ memory: {
312
+ ...asObject(project.memory ?? { provider: "local" }),
313
+ ...memoryProvider ? { provider: memoryProvider } : {},
314
+ ...memoryRequired ? { required: memoryRequired !== "false" } : {}
315
+ }
316
+ };
317
+ if (memoryProvider) applied.push("OMNINODE_MEMORY_PROVIDER");
318
+ if (memoryRequired) applied.push("OMNINODE_MEMORY_REQUIRED");
319
+ }
320
+ return { config, applied };
321
+ }
322
+ function applyDefaults(input) {
323
+ const applied = [];
324
+ const config = mergeConfig(input, {});
325
+ const logging = asObject(config.logging);
326
+ if (logging.level === void 0) {
327
+ config.logging = { ...logging, level: "info" };
328
+ applied.push("logging.level");
329
+ }
330
+ if (logging.format === void 0) {
331
+ config.logging = { ...asObject(config.logging), format: "text" };
332
+ applied.push("logging.format");
333
+ }
334
+ return { config, applied };
335
+ }
336
+
253
337
  // src/config/loader.ts
254
338
  import { existsSync, readFileSync } from "fs";
255
339
  import { parse as parseYaml } from "yaml";
@@ -276,38 +360,132 @@ function expandEnvRefs(text, env, source = "config") {
276
360
  }
277
361
  );
278
362
  }
279
- function loadConfig(options = {}) {
363
+ function loadConfigDetailed(options = {}) {
280
364
  const env = options.env ?? process.env;
281
- const path = options.path ?? (options.directory ? findConfigFile(options.directory) : findConfigFile());
282
- if (!path) {
365
+ const explicitPath = options.path;
366
+ let input = {};
367
+ let projectFile;
368
+ let userFile;
369
+ if (!explicitPath) {
370
+ const candidate = userConfigPath(env);
371
+ if (existsSync(candidate)) {
372
+ input = mergeConfig(input, readConfigDocument(candidate, env));
373
+ userFile = candidate;
374
+ }
375
+ }
376
+ const resolvedProject = explicitPath ?? (options.directory ? findConfigFile(options.directory) : findConfigFile());
377
+ if (resolvedProject) {
378
+ input = mergeConfig(input, readConfigDocument(resolvedProject, env));
379
+ projectFile = resolvedProject;
380
+ }
381
+ if (!projectFile && !userFile) {
283
382
  throw new ConfigError(
284
383
  "CONFIG_NOT_FOUND",
285
384
  "No omninode.yaml found. Run `omninode init` to create one, or pass an explicit path."
286
385
  );
287
386
  }
288
- const raw = readFileSync(path, "utf8");
387
+ const profile = options.profile ?? resolveProfile(input, env);
388
+ const overlay = input.profiles?.[profile];
389
+ if (overlay !== void 0) {
390
+ const { profiles: _drop, ...base } = input;
391
+ input = mergeConfig(base, overlay);
392
+ } else if (profile !== "default" && profile !== "") {
393
+ throw new ConfigError(
394
+ "CONFIG_INVALID",
395
+ `Profile "${profile}" is not defined in this configuration (known: ${Object.keys(input.profiles ?? {}).join(", ") || "none"}).`
396
+ );
397
+ }
398
+ const withEnv = applyEnvOverrides(input, env);
399
+ const withDefaults = applyDefaults(withEnv.config);
400
+ const parsed = appConfigSchema.safeParse(withDefaults.config);
401
+ if (!parsed.success) {
402
+ const issues = parsed.error.issues.map((issue) => {
403
+ const location = issue.path.join(".") || "(root)";
404
+ return `${location}: ${issue.message}`;
405
+ });
406
+ throw new ConfigError(
407
+ "CONFIG_INVALID",
408
+ `Invalid configuration:
409
+ ${issues.join("\n ")}`,
410
+ { details: { issues } }
411
+ );
412
+ }
413
+ assertUniqueNames(parsed.data);
414
+ return {
415
+ config: toAppConfig(parsed.data),
416
+ sources: {
417
+ ...projectFile !== void 0 ? { projectFile } : {},
418
+ ...userFile !== void 0 ? { userFile } : {},
419
+ profile,
420
+ envOverrides: withEnv.applied,
421
+ defaults: withDefaults.applied
422
+ }
423
+ };
424
+ }
425
+ function assertUniqueNames(data) {
426
+ const groups = [
427
+ ["provider", data.project.providers.map((p) => p.name)],
428
+ ["agent", data.project.agents.map((a) => a.name)],
429
+ ["role", data.project.roles.map((r) => r.id)],
430
+ ["pipeline", data.project.pipelines.map((p) => p.id)]
431
+ ];
432
+ for (const [kind, names] of groups) {
433
+ const seen = /* @__PURE__ */ new Set();
434
+ for (const name of names) {
435
+ if (seen.has(name)) {
436
+ throw new ConfigError(
437
+ "CONFIG_INVALID",
438
+ `Duplicate ${kind} name "${name}" \u2014 every ${kind} must have a unique name.`
439
+ );
440
+ }
441
+ seen.add(name);
442
+ }
443
+ }
444
+ }
445
+ function readConfigDocument(path2, optionsEnv) {
446
+ const raw = readFileSync(path2, "utf8");
289
447
  const secretFindings = scanForInlineSecrets(raw);
290
448
  if (secretFindings.length > 0) {
291
- throw new ConfigError("CONFIG_INVALID", describeSecretFindings(path, secretFindings));
449
+ throw new ConfigError("CONFIG_INVALID", describeSecretFindings(path2, secretFindings));
292
450
  }
293
- const expanded = expandEnvRefs(raw, env, path);
294
- let data;
451
+ const expanded = expandEnvRefs(raw, optionsEnv, path2);
295
452
  try {
296
- data = parseYaml(expanded);
453
+ return parseYaml(expanded);
297
454
  } catch (error) {
298
- throw new ConfigError("CONFIG_INVALID", `Failed to parse ${path}.`, { cause: error });
455
+ throw new ConfigError("CONFIG_INVALID", `Failed to parse ${path2}.`, { cause: error });
299
456
  }
300
- const parsed = appConfigSchema.safeParse(data);
301
- if (!parsed.success) {
302
- const issues = parsed.error.issues.map((issue) => {
303
- const location = issue.path.length > 0 ? issue.path.join(".") : "(root)";
304
- return `${location}: ${issue.message}`;
305
- });
306
- throw new ConfigError("CONFIG_INVALID", `Invalid configuration in ${path}.`, {
307
- details: { issues }
308
- });
457
+ }
458
+ function loadConfig(options = {}) {
459
+ return loadConfigDetailed(options).config;
460
+ }
461
+ function configDiagnostics(config) {
462
+ const notes = [];
463
+ for (const provider of config.project.providers) {
464
+ if (!provider.apiKeyEnvVar && provider.baseUrl.startsWith("https://")) {
465
+ notes.push(
466
+ `Provider "${provider.name}": API key reference is missing \u2014 add api_key_env_var: <ENV_VAR> unless this endpoint is intentionally keyless.`
467
+ );
468
+ }
469
+ }
470
+ for (const agent of config.project.agents) {
471
+ if (!agent.command && agent.integration !== "custom") {
472
+ notes.push(`Agent "${agent.name}": no command configured \u2014 the process adapter needs one.`);
473
+ }
474
+ if (agent.allowExternalCwd) {
475
+ notes.push(`Agent "${agent.name}": may run outside the project root (allow_external_cwd).`);
476
+ }
477
+ }
478
+ for (const pipeline of config.project.pipelines) {
479
+ const ids = new Set(pipeline.steps.map((s) => s.id));
480
+ for (const step of pipeline.steps) {
481
+ for (const dep of step.dependsOn ?? []) {
482
+ if (!ids.has(dep)) {
483
+ notes.push(`Pipeline "${pipeline.id}": step "${step.id}" depends on unknown step "${dep}".`);
484
+ }
485
+ }
486
+ }
309
487
  }
310
- return toAppConfig(parsed.data);
488
+ return notes;
311
489
  }
312
490
  function toAppConfig(yaml) {
313
491
  return {
@@ -1942,15 +2120,15 @@ async function writeFileAtomic(target, contents, durability = "rename") {
1942
2120
  }
1943
2121
  }
1944
2122
  }
1945
- async function inspectDocument(path) {
2123
+ async function inspectDocument(path2) {
1946
2124
  let raw;
1947
2125
  try {
1948
- await stat(path);
2126
+ await stat(path2);
1949
2127
  } catch {
1950
2128
  return { status: "missing" };
1951
2129
  }
1952
2130
  try {
1953
- raw = await readFile(path, "utf8");
2131
+ raw = await readFile(path2, "utf8");
1954
2132
  } catch (error) {
1955
2133
  return { status: "corrupt", detail: `unreadable: ${error instanceof Error ? error.message : String(error)}` };
1956
2134
  }
@@ -1970,21 +2148,21 @@ async function inspectDocument(path) {
1970
2148
  if (version < SCHEMA_VERSION) return { status: "v1-legacy" };
1971
2149
  return { status: "current", schemaVersion: version, count: doc.items.length };
1972
2150
  }
1973
- async function readDocument(path, onCorrupt, quarantine = true) {
1974
- const state = await inspectDocument(path);
2151
+ async function readDocument(path2, onCorrupt, quarantine = true) {
2152
+ const state = await inspectDocument(path2);
1975
2153
  if (state.status === "missing" || state.status === "empty") return [];
1976
2154
  if (state.status === "future") {
1977
2155
  const error = new Error(
1978
- `${path} was written by a newer OmniNode (schema v${state.schemaVersion} > v${SCHEMA_VERSION})`
2156
+ `${path2} was written by a newer OmniNode (schema v${state.schemaVersion} > v${SCHEMA_VERSION})`
1979
2157
  );
1980
- onCorrupt?.(path, error);
2158
+ onCorrupt?.(path2, error);
1981
2159
  return [];
1982
2160
  }
1983
2161
  let raw;
1984
2162
  try {
1985
- raw = await readFile(path, "utf8");
2163
+ raw = await readFile(path2, "utf8");
1986
2164
  } catch (error) {
1987
- onCorrupt?.(path, error);
2165
+ onCorrupt?.(path2, error);
1988
2166
  return [];
1989
2167
  }
1990
2168
  try {
@@ -1993,16 +2171,16 @@ async function readDocument(path, onCorrupt, quarantine = true) {
1993
2171
  return parsed.items ?? [];
1994
2172
  } catch (error) {
1995
2173
  if (quarantine) {
1996
- const quarantinePath = `${path}.corrupt-${Date.now()}`;
2174
+ const quarantinePath = `${path2}.corrupt-${Date.now()}`;
1997
2175
  try {
1998
- await rename(path, quarantinePath);
2176
+ await rename(path2, quarantinePath);
1999
2177
  logger.child({ component: "persistence" }).warn(
2000
- `Quarantined unreadable store file ${path} \u2192 ${quarantinePath}`
2178
+ `Quarantined unreadable store file ${path2} \u2192 ${quarantinePath}`
2001
2179
  );
2002
2180
  } catch {
2003
2181
  }
2004
2182
  }
2005
- onCorrupt?.(path, error);
2183
+ onCorrupt?.(path2, error);
2006
2184
  return [];
2007
2185
  }
2008
2186
  }
@@ -2246,6 +2424,7 @@ var TaskEngine = class {
2246
2424
  const result = {
2247
2425
  ...output.summary !== void 0 ? { summary: output.summary } : {},
2248
2426
  ...output.reports !== void 0 && output.reports.length > 0 ? { reports: output.reports } : {},
2427
+ ...output.protocol !== void 0 ? { protocol: output.protocol } : {},
2249
2428
  // Keep the verbatim output (bounded) for report extraction and audit.
2250
2429
  ...output.rawOutput !== void 0 && output.rawOutput.length > 0 ? { rawOutput: output.rawOutput.slice(0, 1e4) } : {},
2251
2430
  ...output.error !== void 0 ? { error: output.error } : {},
@@ -4208,8 +4387,8 @@ var STORE_FILES = [
4208
4387
  async function migrateProjectStores(directory = `${process.cwd()}/.omninode`, options = {}) {
4209
4388
  const reports = [];
4210
4389
  for (const file of STORE_FILES) {
4211
- const path = `${directory}/${file}`;
4212
- const state = await inspectDocument(path);
4390
+ const path2 = `${directory}/${file}`;
4391
+ const state = await inspectDocument(path2);
4213
4392
  if (state.status === "missing") {
4214
4393
  reports.push({ file, state: state.status, migrated: false });
4215
4394
  continue;
@@ -4231,9 +4410,9 @@ async function migrateProjectStores(directory = `${process.cwd()}/.omninode`, op
4231
4410
  reports.push({ file, state: state.status, migrated: false, note: state.detail });
4232
4411
  continue;
4233
4412
  }
4234
- const items = await readDocument(path, void 0, false);
4413
+ const items = await readDocument(path2, void 0, false);
4235
4414
  if (!options.checkOnly) {
4236
- await writeFileAtomic(path, `${JSON.stringify({ schemaVersion: SCHEMA_VERSION, items }, null, 2)}
4415
+ await writeFileAtomic(path2, `${JSON.stringify({ schemaVersion: SCHEMA_VERSION, items }, null, 2)}
4237
4416
  `);
4238
4417
  }
4239
4418
  const beforeCount = items.length;
@@ -4250,7 +4429,7 @@ async function migrateProjectStores(directory = `${process.cwd()}/.omninode`, op
4250
4429
  }
4251
4430
 
4252
4431
  // src/version.ts
4253
- var OMNINODE_VERSION = "2.0.0-alpha.15";
4432
+ var OMNINODE_VERSION = "2.0.0-alpha.17";
4254
4433
  export {
4255
4434
  AgentAdapterRegistry,
4256
4435
  AgentError,
@@ -4265,6 +4444,7 @@ export {
4265
4444
  FileTaskStore,
4266
4445
  HeuristicPlanner,
4267
4446
  JsonFileStore,
4447
+ KNOWN_PROFILES,
4268
4448
  LocalMemoryProvider,
4269
4449
  Logger,
4270
4450
  MAX_CONTEXT_CHARS,
@@ -4302,6 +4482,8 @@ export {
4302
4482
  agentConfigSchema,
4303
4483
  aggregateReports,
4304
4484
  appConfigSchema,
4485
+ applyDefaults,
4486
+ applyEnvOverrides,
4305
4487
  authHeaders,
4306
4488
  buildAgentRegistry,
4307
4489
  buildChildEnv,
@@ -4312,6 +4494,7 @@ export {
4312
4494
  buildPlannerContext,
4313
4495
  buildRoleRegistry,
4314
4496
  composeTaskText,
4497
+ configDiagnostics,
4315
4498
  configureLogger,
4316
4499
  createAgent,
4317
4500
  createDefaultChatFn,
@@ -4342,7 +4525,9 @@ export {
4342
4525
  killTree,
4343
4526
  kindFromStatus,
4344
4527
  loadConfig,
4528
+ loadConfigDetailed,
4345
4529
  logger,
4530
+ mergeConfig,
4346
4531
  migrateProjectStores,
4347
4532
  newMessageId,
4348
4533
  normalizeEvidence,
@@ -4364,10 +4549,12 @@ export {
4364
4549
  requireCompletion,
4365
4550
  resolveApiKey,
4366
4551
  resolveEnvPolicy,
4552
+ resolveProfile,
4367
4553
  roleConfigSchema,
4368
4554
  sanitizeChunk,
4369
4555
  scanForInlineSecrets,
4370
4556
  similar,
4557
+ userConfigPath,
4371
4558
  validateEnvelope,
4372
4559
  validatePlan,
4373
4560
  validateReport,