@lumenflow/kernel 3.17.7 → 3.18.1

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.
Files changed (45) hide show
  1. package/dist/event-kinds.d.ts +1 -0
  2. package/dist/event-kinds.d.ts.map +1 -1
  3. package/dist/event-kinds.js +1 -0
  4. package/dist/event-kinds.js.map +1 -1
  5. package/dist/evidence/evidence-store.d.ts +1 -1
  6. package/dist/evidence/evidence-store.d.ts.map +1 -1
  7. package/dist/evidence/evidence-store.js +5 -3
  8. package/dist/evidence/evidence-store.js.map +1 -1
  9. package/dist/kernel.schemas.d.ts +43 -0
  10. package/dist/kernel.schemas.d.ts.map +1 -1
  11. package/dist/kernel.schemas.js +28 -2
  12. package/dist/kernel.schemas.js.map +1 -1
  13. package/dist/pack/manifest.d.ts +6 -0
  14. package/dist/pack/manifest.d.ts.map +1 -1
  15. package/dist/pack/manifest.js +11 -2
  16. package/dist/pack/manifest.js.map +1 -1
  17. package/dist/pack/pack-loader.d.ts +19 -1
  18. package/dist/pack/pack-loader.d.ts.map +1 -1
  19. package/dist/pack/pack-loader.js +19 -7
  20. package/dist/pack/pack-loader.js.map +1 -1
  21. package/dist/policy/policy-engine.d.ts +9 -0
  22. package/dist/policy/policy-engine.d.ts.map +1 -1
  23. package/dist/policy/policy-engine.js.map +1 -1
  24. package/dist/runtime/kernel-runtime.d.ts +13 -1
  25. package/dist/runtime/kernel-runtime.d.ts.map +1 -1
  26. package/dist/runtime/kernel-runtime.js +390 -13
  27. package/dist/runtime/kernel-runtime.js.map +1 -1
  28. package/dist/sandbox/bwrap-invocation.js +1 -1
  29. package/dist/sandbox/bwrap-invocation.js.map +1 -1
  30. package/dist/sandbox/profile.d.ts +1 -0
  31. package/dist/sandbox/profile.d.ts.map +1 -1
  32. package/dist/sandbox/profile.js +6 -2
  33. package/dist/sandbox/profile.js.map +1 -1
  34. package/dist/sandbox/subprocess-dispatcher.d.ts.map +1 -1
  35. package/dist/sandbox/subprocess-dispatcher.js +4 -1
  36. package/dist/sandbox/subprocess-dispatcher.js.map +1 -1
  37. package/dist/shared-constants.d.ts +7 -0
  38. package/dist/shared-constants.d.ts.map +1 -1
  39. package/dist/shared-constants.js +7 -0
  40. package/dist/shared-constants.js.map +1 -1
  41. package/dist/tool-host/tool-host.d.ts +11 -0
  42. package/dist/tool-host/tool-host.d.ts.map +1 -1
  43. package/dist/tool-host/tool-host.js +137 -37
  44. package/dist/tool-host/tool-host.js.map +1 -1
  45. package/package.json +1 -1
@@ -3,7 +3,7 @@
3
3
  import { randomBytes } from 'node:crypto';
4
4
  import { access, mkdir, open, readFile, readdir, rm } from 'node:fs/promises';
5
5
  import path from 'node:path';
6
- import { fileURLToPath } from 'node:url';
6
+ import { fileURLToPath, pathToFileURL } from 'node:url';
7
7
  import YAML from 'yaml';
8
8
  import { z } from 'zod';
9
9
  import { canonical_json } from '../canonical-json.js';
@@ -11,8 +11,8 @@ import { KERNEL_EVENT_KINDS, TOOL_TRACE_KINDS, isRunLifecycleEventKind, } from '
11
11
  import { EXECUTION_METADATA_KEYS, KERNEL_POLICY_IDS, KERNEL_RUNTIME_EVENTS_DIR_NAME, KERNEL_RUNTIME_EVENTS_FILE_NAME, KERNEL_RUNTIME_EVENTS_LOCK_FILE_NAME, KERNEL_RUNTIME_EVIDENCE_DIR_NAME, KERNEL_RUNTIME_ROOT_DIR_NAME, KERNEL_RUNTIME_TASKS_DIR_NAME, LUMENFLOW_DIR_NAME, LUMENFLOW_SCOPE_NAME, PACKAGES_DIR_NAME, PACK_MANIFEST_FILE_NAME, PACKS_DIR_NAME, UTF8_ENCODING, WORKSPACE_CONFIG_HASH_CONTEXT_KEYS, WORKSPACE_FILE_NAME, } from '../shared-constants.js';
12
12
  import { EventStore, projectTaskState, } from '../event-store/index.js';
13
13
  import { EvidenceStore } from '../evidence/evidence-store.js';
14
- import { ExecutionContextSchema, RUN_STATUSES, RunSchema, TOOL_ERROR_CODES, TOOL_HANDLER_KINDS, TaskSpecSchema, WorkspaceSpecSchema, validateWorkspaceRootKeys, } from '../kernel.schemas.js';
15
- import { PackLoader, resolvePackToolEntryPath } from '../pack/index.js';
14
+ import { ENVIRONMENT_VARIABLE_NAME_PATTERN, ExecutionContextSchema, RUN_STATUSES, RunSchema, ToolScopeSchema, TOOL_ERROR_CODES, TOOL_HANDLER_KINDS, TaskSpecSchema, WorkspaceSpecSchema, validateWorkspaceRootKeys, } from '../kernel.schemas.js';
15
+ import { PackLoader, resolvePackModuleEntry, resolvePackToolEntryPath, } from '../pack/index.js';
16
16
  import { POLICY_TRIGGERS, PolicyEngine, } from '../policy/index.js';
17
17
  import { SandboxSubprocessDispatcher, } from '../sandbox/index.js';
18
18
  import { assertTransition } from '../state-machine/index.js';
@@ -37,9 +37,14 @@ const CLI_PACKS_ROOT_CANDIDATE = path.resolve(KERNEL_RUNTIME_MODULE_DIR, ...CLI_
37
37
  const DEFAULT_PACK_TOOL_INPUT_SCHEMA = z.record(z.string(), z.unknown());
38
38
  const DEFAULT_PACK_TOOL_OUTPUT_SCHEMA = z.record(z.string(), z.unknown());
39
39
  const JSON_SCHEMA_MAX_DEPTH = 12;
40
+ const PACK_CAPABILITY_FACTORY_DEFAULT_EXPORT = 'default';
41
+ const PACK_CAPABILITY_FACTORY_NAMED_EXPORT = 'capabilityFactory';
42
+ const PACK_POLICY_FACTORY_DEFAULT_EXPORT = 'default';
43
+ const PACK_POLICY_FACTORY_NAMED_EXPORT = 'policyFactory';
40
44
  const RUNTIME_LOAD_STAGE_ERROR_PREFIX = 'Runtime load stage failed for pack';
41
45
  const RUNTIME_REGISTRATION_STAGE_ERROR_PREFIX = 'Runtime registration stage failed for tool';
42
46
  const WORKSPACE_UPDATED_INIT_SUMMARY = 'Workspace config hash initialized during runtime startup.';
47
+ const PACK_CONFIG_ENV_REFERENCE_SUFFIX = '_env';
43
48
  const SPEC_TAMPERED_ERROR_CODE = 'SPEC_TAMPERED';
44
49
  const SPEC_TAMPERED_WORKSPACE_MESSAGE = 'Workspace configuration hash mismatch detected; execution blocked.';
45
50
  const SPEC_TAMPERED_WORKSPACE_MISSING_MESSAGE = 'Workspace configuration file is missing; execution blocked.';
@@ -343,6 +348,344 @@ function parsePackToolJsonSchema(schemaValue, toolName, schemaField) {
343
348
  });
344
349
  }
345
350
  }
351
+ function isWithinDirectory(root, candidatePath) {
352
+ const relative = path.relative(root, candidatePath);
353
+ return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
354
+ }
355
+ function formatZodIssuePath(pathSegments) {
356
+ return pathSegments.length === 0
357
+ ? '<root>'
358
+ : pathSegments.map((segment) => String(segment)).join('.');
359
+ }
360
+ function formatZodIssues(issues) {
361
+ return issues.map((issue) => `${formatZodIssuePath(issue.path)}: ${issue.message}`).join('; ');
362
+ }
363
+ function parsePackConfigJsonSchema(schemaValue, packId, configKey) {
364
+ const context = `config_schema for pack "${packId}" (workspace root "${configKey}")`;
365
+ try {
366
+ return buildZodSchemaFromJsonSchema(schemaValue, context, 0);
367
+ }
368
+ catch (error) {
369
+ const message = error instanceof Error ? error.message : 'unknown schema parsing error';
370
+ throw new Error(`Invalid ${context}: ${message}`, {
371
+ cause: error,
372
+ });
373
+ }
374
+ }
375
+ async function resolvePackConfigSchema(loadedPack) {
376
+ const configSchemaEntry = loadedPack.manifest.config_schema;
377
+ if (!configSchemaEntry) {
378
+ return undefined;
379
+ }
380
+ const packRoot = path.resolve(loadedPack.packRoot);
381
+ const configSchemaPath = path.resolve(packRoot, configSchemaEntry);
382
+ if (!isWithinDirectory(packRoot, configSchemaPath)) {
383
+ throw new Error(`Pack "${loadedPack.manifest.id}" config_schema "${configSchemaEntry}" resolves outside pack root.`);
384
+ }
385
+ let schemaRaw;
386
+ try {
387
+ schemaRaw = await readFile(configSchemaPath, UTF8_ENCODING);
388
+ }
389
+ catch (error) {
390
+ throw new Error(`Unable to read config_schema "${configSchemaEntry}" for pack "${loadedPack.manifest.id}".`, { cause: error });
391
+ }
392
+ try {
393
+ return JSON.parse(schemaRaw);
394
+ }
395
+ catch (error) {
396
+ throw new Error(`Invalid config_schema "${configSchemaEntry}" for pack "${loadedPack.manifest.id}": expected valid JSON.`, { cause: error });
397
+ }
398
+ }
399
+ async function attachResolvedPackConfig(loadedPack, rawWorkspaceData) {
400
+ const configKey = loadedPack.manifest.config_key;
401
+ if (!configKey || !(configKey in rawWorkspaceData)) {
402
+ return loadedPack;
403
+ }
404
+ const rawPackConfig = rawWorkspaceData[configKey];
405
+ if (!loadedPack.manifest.config_schema) {
406
+ return {
407
+ ...loadedPack,
408
+ resolvedConfig: rawPackConfig,
409
+ };
410
+ }
411
+ const configSchemaValue = await resolvePackConfigSchema(loadedPack);
412
+ const configSchema = parsePackConfigJsonSchema(configSchemaValue, loadedPack.manifest.id, configKey);
413
+ const validation = configSchema.safeParse(rawPackConfig);
414
+ if (!validation.success) {
415
+ throw new Error(`Invalid workspace config for "${configKey}" in pack "${loadedPack.manifest.id}": ${formatZodIssues(validation.error.issues)}`, { cause: validation.error });
416
+ }
417
+ return {
418
+ ...loadedPack,
419
+ resolvedConfig: validation.data,
420
+ };
421
+ }
422
+ function isRecord(value) {
423
+ return !!value && typeof value === 'object' && !Array.isArray(value);
424
+ }
425
+ function formatPackConfigPath(pathSegments) {
426
+ return pathSegments.join('.');
427
+ }
428
+ function collectPackConfigEnvReferences(value, pathSegments) {
429
+ if (Array.isArray(value)) {
430
+ return value.flatMap((entry, index) => collectPackConfigEnvReferences(entry, [...pathSegments, String(index)]));
431
+ }
432
+ if (!isRecord(value)) {
433
+ return [];
434
+ }
435
+ const references = [];
436
+ for (const [key, childValue] of Object.entries(value)) {
437
+ const childPath = [...pathSegments, key];
438
+ if (key.endsWith(PACK_CONFIG_ENV_REFERENCE_SUFFIX) && typeof childValue === 'string') {
439
+ references.push({
440
+ envName: childValue,
441
+ path: formatPackConfigPath(childPath),
442
+ });
443
+ }
444
+ references.push(...collectPackConfigEnvReferences(childValue, childPath));
445
+ }
446
+ return references;
447
+ }
448
+ function validatePackConfigEnvReferences(loadedPack) {
449
+ if (!loadedPack.manifest.config_key || loadedPack.resolvedConfig === undefined) {
450
+ return;
451
+ }
452
+ const declaredEnvNames = collectDeclaredEnvNames(loadedPack);
453
+ const envReferences = collectPackConfigEnvReferences(loadedPack.resolvedConfig, [
454
+ loadedPack.manifest.config_key,
455
+ ]);
456
+ for (const reference of envReferences) {
457
+ if (!ENVIRONMENT_VARIABLE_NAME_PATTERN.test(reference.envName)) {
458
+ throw new Error(`Pack "${loadedPack.manifest.id}" workspace config reference "${reference.path}" must use an uppercase environment variable name, got "${reference.envName}".`);
459
+ }
460
+ if (!declaredEnvNames.has(reference.envName)) {
461
+ throw new Error(`Pack "${loadedPack.manifest.id}" workspace config reference "${reference.path}" uses "${reference.envName}" but no manifest tool declares it in required_env.`);
462
+ }
463
+ }
464
+ }
465
+ function collectDeclaredEnvNames(loadedPack) {
466
+ const manifestEnvNames = loadedPack.manifest.tools.flatMap((tool) => tool.required_env ?? []);
467
+ const capabilityEnvNames = Object.values(loadedPack.resolvedCapabilityAugmentations ?? {}).flatMap((augmentation) => augmentation.required_env ?? []);
468
+ return new Set([...manifestEnvNames, ...capabilityEnvNames]);
469
+ }
470
+ function isCapabilityFactory(value) {
471
+ return typeof value === 'function';
472
+ }
473
+ function selectPackCapabilityFactory(options) {
474
+ const { loadedModule, exportName, capabilityFactoryEntry, packId } = options;
475
+ if (exportName) {
476
+ if (!isRecord(loadedModule) || !isCapabilityFactory(loadedModule[exportName])) {
477
+ throw new Error(`capability_factory "${capabilityFactoryEntry}" for pack "${packId}" must export function "${exportName}".`);
478
+ }
479
+ return loadedModule[exportName];
480
+ }
481
+ if (isCapabilityFactory(loadedModule)) {
482
+ return loadedModule;
483
+ }
484
+ if (isRecord(loadedModule)) {
485
+ const defaultExport = loadedModule[PACK_CAPABILITY_FACTORY_DEFAULT_EXPORT];
486
+ if (isCapabilityFactory(defaultExport)) {
487
+ return defaultExport;
488
+ }
489
+ const namedExport = loadedModule[PACK_CAPABILITY_FACTORY_NAMED_EXPORT];
490
+ if (isCapabilityFactory(namedExport)) {
491
+ return namedExport;
492
+ }
493
+ }
494
+ throw new Error(`capability_factory "${capabilityFactoryEntry}" for pack "${packId}" must export a function via default export, capabilityFactory, or an explicit #export.`);
495
+ }
496
+ function validateCapabilityAugmentation(augmentationValue, packId, capabilityFactoryEntry, toolName) {
497
+ if (augmentationValue === undefined || augmentationValue === null) {
498
+ return {};
499
+ }
500
+ if (!isRecord(augmentationValue)) {
501
+ throw new Error(`capability_factory "${capabilityFactoryEntry}" for pack "${packId}" returned a non-object augmentation for tool "${toolName}".`);
502
+ }
503
+ const requiredScopesValue = augmentationValue.required_scopes;
504
+ const requiredEnvValue = augmentationValue.required_env;
505
+ const extraKeys = Object.keys(augmentationValue).filter((key) => key !== 'required_scopes' && key !== 'required_env');
506
+ if (extraKeys.length > 0) {
507
+ throw new Error(`capability_factory "${capabilityFactoryEntry}" for pack "${packId}" returned unsupported fields for tool "${toolName}": ${extraKeys.join(', ')}.`);
508
+ }
509
+ const requiredScopes = (() => {
510
+ if (requiredScopesValue === undefined) {
511
+ return undefined;
512
+ }
513
+ if (!Array.isArray(requiredScopesValue)) {
514
+ throw new Error(`capability_factory "${capabilityFactoryEntry}" for pack "${packId}" must return required_scopes as an array for tool "${toolName}".`);
515
+ }
516
+ return requiredScopesValue.map((scopeValue, index) => {
517
+ const parsed = ToolScopeSchema.safeParse(scopeValue);
518
+ if (!parsed.success) {
519
+ throw new Error(`capability_factory "${capabilityFactoryEntry}" for pack "${packId}" returned invalid required_scopes[${index}] for tool "${toolName}": ${formatZodIssues(parsed.error.issues)}`);
520
+ }
521
+ return parsed.data;
522
+ });
523
+ })();
524
+ const requiredEnv = (() => {
525
+ if (requiredEnvValue === undefined) {
526
+ return undefined;
527
+ }
528
+ if (!Array.isArray(requiredEnvValue) ||
529
+ requiredEnvValue.some((envName) => typeof envName !== 'string' || !ENVIRONMENT_VARIABLE_NAME_PATTERN.test(envName))) {
530
+ throw new Error(`capability_factory "${capabilityFactoryEntry}" for pack "${packId}" must return required_env as uppercase environment variable names for tool "${toolName}".`);
531
+ }
532
+ return [...new Set(requiredEnvValue)];
533
+ })();
534
+ return {
535
+ ...(requiredScopes ? { required_scopes: requiredScopes } : {}),
536
+ ...(requiredEnv ? { required_env: requiredEnv } : {}),
537
+ };
538
+ }
539
+ async function resolvePackCapabilityAugmentations(workspaceRoot, loadedPack) {
540
+ const capabilityFactoryEntry = loadedPack.manifest.capability_factory;
541
+ if (!capabilityFactoryEntry) {
542
+ return {};
543
+ }
544
+ const resolvedEntry = resolvePackModuleEntry(loadedPack.packRoot, capabilityFactoryEntry, 'Pack capability_factory entry');
545
+ let loadedModule;
546
+ try {
547
+ loadedModule = await import(pathToFileURL(resolvedEntry.modulePath).href);
548
+ }
549
+ catch (error) {
550
+ throw new Error(`Failed to load capability_factory "${capabilityFactoryEntry}" for pack "${loadedPack.manifest.id}".`, { cause: error });
551
+ }
552
+ const capabilityFactory = selectPackCapabilityFactory({
553
+ loadedModule,
554
+ exportName: resolvedEntry.exportName,
555
+ capabilityFactoryEntry,
556
+ packId: loadedPack.manifest.id,
557
+ });
558
+ const augmentations = {};
559
+ for (const tool of loadedPack.manifest.tools) {
560
+ let augmentationValue;
561
+ try {
562
+ augmentationValue = await capabilityFactory({
563
+ workspaceRoot,
564
+ packId: loadedPack.manifest.id,
565
+ packRoot: loadedPack.packRoot,
566
+ packConfig: loadedPack.resolvedConfig,
567
+ tool,
568
+ });
569
+ }
570
+ catch (error) {
571
+ throw new Error(`capability_factory "${capabilityFactoryEntry}" for pack "${loadedPack.manifest.id}" failed while resolving tool "${tool.name}".`, { cause: error });
572
+ }
573
+ const augmentation = validateCapabilityAugmentation(augmentationValue, loadedPack.manifest.id, capabilityFactoryEntry, tool.name);
574
+ if (augmentation.required_env || augmentation.required_scopes) {
575
+ augmentations[tool.name] = augmentation;
576
+ }
577
+ }
578
+ return augmentations;
579
+ }
580
+ function isPolicyTrigger(value) {
581
+ return (value === POLICY_TRIGGERS.ON_TOOL_REQUEST ||
582
+ value === POLICY_TRIGGERS.ON_CLAIM ||
583
+ value === POLICY_TRIGGERS.ON_COMPLETION ||
584
+ value === POLICY_TRIGGERS.ON_EVIDENCE_ADDED);
585
+ }
586
+ function isPolicyEffect(value) {
587
+ return value === 'allow' || value === 'deny' || value === 'approval_required';
588
+ }
589
+ function isPackPolicyFactory(value) {
590
+ return typeof value === 'function';
591
+ }
592
+ function isPolicyWhenPredicate(value) {
593
+ return typeof value === 'function';
594
+ }
595
+ function selectPackPolicyFactory(options) {
596
+ const { loadedModule, exportName, policyFactoryEntry, packId } = options;
597
+ if (exportName) {
598
+ if (!isRecord(loadedModule) || !isPackPolicyFactory(loadedModule[exportName])) {
599
+ throw new Error(`policy_factory "${policyFactoryEntry}" for pack "${packId}" must export function "${exportName}".`);
600
+ }
601
+ return loadedModule[exportName];
602
+ }
603
+ if (isPackPolicyFactory(loadedModule)) {
604
+ return loadedModule;
605
+ }
606
+ if (isRecord(loadedModule)) {
607
+ const defaultExport = loadedModule[PACK_POLICY_FACTORY_DEFAULT_EXPORT];
608
+ if (isPackPolicyFactory(defaultExport)) {
609
+ return defaultExport;
610
+ }
611
+ const namedExport = loadedModule[PACK_POLICY_FACTORY_NAMED_EXPORT];
612
+ if (isPackPolicyFactory(namedExport)) {
613
+ return namedExport;
614
+ }
615
+ }
616
+ throw new Error(`policy_factory "${policyFactoryEntry}" for pack "${packId}" must export a function via default export, policyFactory, or an explicit #export.`);
617
+ }
618
+ function validatePackPolicyFactoryRules(rulesValue, packId, policyFactoryEntry) {
619
+ if (!Array.isArray(rulesValue)) {
620
+ throw new Error(`policy_factory "${policyFactoryEntry}" for pack "${packId}" must return an array of policy rules.`);
621
+ }
622
+ return rulesValue.map((ruleValue, index) => {
623
+ if (!isRecord(ruleValue)) {
624
+ throw new Error(`policy_factory "${policyFactoryEntry}" for pack "${packId}" returned rule ${index} that is not an object.`);
625
+ }
626
+ const id = ruleValue.id;
627
+ if (typeof id !== 'string' || id.trim().length === 0) {
628
+ throw new Error(`policy_factory "${policyFactoryEntry}" for pack "${packId}" returned rule ${index} with an invalid id.`);
629
+ }
630
+ const trigger = ruleValue.trigger;
631
+ if (!isPolicyTrigger(trigger)) {
632
+ throw new Error(`policy_factory "${policyFactoryEntry}" for pack "${packId}" returned rule "${id}" with an invalid trigger.`);
633
+ }
634
+ const decision = ruleValue.decision;
635
+ if (!isPolicyEffect(decision)) {
636
+ throw new Error(`policy_factory "${policyFactoryEntry}" for pack "${packId}" returned rule "${id}" with an invalid decision.`);
637
+ }
638
+ const reason = ruleValue.reason;
639
+ if (reason !== undefined && (typeof reason !== 'string' || reason.trim().length === 0)) {
640
+ throw new Error(`policy_factory "${policyFactoryEntry}" for pack "${packId}" returned rule "${id}" with an invalid reason.`);
641
+ }
642
+ const when = ruleValue.when;
643
+ if (when !== undefined && !isPolicyWhenPredicate(when)) {
644
+ throw new Error(`policy_factory "${policyFactoryEntry}" for pack "${packId}" returned rule "${id}" with a non-function when predicate.`);
645
+ }
646
+ return {
647
+ id,
648
+ trigger,
649
+ decision,
650
+ ...(reason ? { reason } : {}),
651
+ ...(when ? { when } : {}),
652
+ };
653
+ });
654
+ }
655
+ async function resolvePackPolicyFactoryRules(workspaceRoot, loadedPack) {
656
+ const policyFactoryEntry = loadedPack.manifest.policy_factory;
657
+ if (!policyFactoryEntry) {
658
+ return [];
659
+ }
660
+ const resolvedEntry = resolvePackModuleEntry(loadedPack.packRoot, policyFactoryEntry, 'Pack policy_factory entry');
661
+ let loadedModule;
662
+ try {
663
+ loadedModule = await import(pathToFileURL(resolvedEntry.modulePath).href);
664
+ }
665
+ catch (error) {
666
+ throw new Error(`Failed to load policy_factory "${policyFactoryEntry}" for pack "${loadedPack.manifest.id}".`, { cause: error });
667
+ }
668
+ const policyFactory = selectPackPolicyFactory({
669
+ loadedModule,
670
+ exportName: resolvedEntry.exportName,
671
+ policyFactoryEntry,
672
+ packId: loadedPack.manifest.id,
673
+ });
674
+ let rulesValue;
675
+ try {
676
+ const factoryInput = {
677
+ workspaceRoot,
678
+ packId: loadedPack.manifest.id,
679
+ packRoot: loadedPack.packRoot,
680
+ packConfig: loadedPack.resolvedConfig,
681
+ };
682
+ rulesValue = await policyFactory(factoryInput);
683
+ }
684
+ catch (error) {
685
+ throw new Error(`policy_factory "${policyFactoryEntry}" for pack "${loadedPack.manifest.id}" failed during runtime startup.`, { cause: error });
686
+ }
687
+ return validatePackPolicyFactoryRules(rulesValue, loadedPack.manifest.id, policyFactoryEntry);
688
+ }
346
689
  export async function defaultRuntimeToolCapabilityResolver(input) {
347
690
  const resolvedEntry = resolvePackToolEntryPath(input.loadedPack.packRoot, input.tool.entry);
348
691
  const resolvedInputSchema = input.tool.input_schema
@@ -351,6 +694,7 @@ export async function defaultRuntimeToolCapabilityResolver(input) {
351
694
  const resolvedOutputSchema = input.tool.output_schema
352
695
  ? parsePackToolJsonSchema(input.tool.output_schema, input.tool.name, 'output_schema')
353
696
  : DEFAULT_PACK_TOOL_OUTPUT_SCHEMA;
697
+ const augmentation = input.loadedPack.resolvedCapabilityAugmentations?.[input.tool.name];
354
698
  return {
355
699
  name: input.tool.name,
356
700
  domain: input.loadedPack.manifest.id,
@@ -358,15 +702,34 @@ export async function defaultRuntimeToolCapabilityResolver(input) {
358
702
  input_schema: resolvedInputSchema,
359
703
  output_schema: resolvedOutputSchema,
360
704
  permission: input.tool.permission,
361
- required_scopes: input.tool.required_scopes,
705
+ required_scopes: mergeToolScopes(input.tool.required_scopes, augmentation?.required_scopes),
706
+ required_env: mergeRequiredEnvNames(input.tool.required_env, augmentation?.required_env),
362
707
  handler: {
363
708
  kind: TOOL_HANDLER_KINDS.SUBPROCESS,
364
709
  entry: resolvedEntry,
365
710
  },
366
711
  description: buildPackToolDescription(input.tool.name, input.loadedPack.manifest.id),
367
712
  pack: input.loadedPack.pin.id,
713
+ ...(input.loadedPack.resolvedConfig !== undefined
714
+ ? { pack_config: input.loadedPack.resolvedConfig }
715
+ : {}),
368
716
  };
369
717
  }
718
+ function mergeRequiredEnvNames(manifestRequiredEnv, augmentedRequiredEnv) {
719
+ const merged = [...(manifestRequiredEnv ?? []), ...(augmentedRequiredEnv ?? [])];
720
+ if (merged.length === 0) {
721
+ return undefined;
722
+ }
723
+ return [...new Set(merged)];
724
+ }
725
+ function mergeToolScopes(manifestScopes, augmentedScopes) {
726
+ const merged = [...manifestScopes, ...(augmentedScopes ?? [])];
727
+ const uniqueScopes = new Map();
728
+ for (const scope of merged) {
729
+ uniqueScopes.set(JSON.stringify(scope), scope);
730
+ }
731
+ return [...uniqueScopes.values()];
732
+ }
370
733
  async function fileExists(targetPath) {
371
734
  try {
372
735
  await access(targetPath);
@@ -489,15 +852,17 @@ async function resolveWorkspaceSpec(options) {
489
852
  raw_workspace_data: rawWorkspaceData,
490
853
  };
491
854
  }
492
- function buildDefaultPolicyLayers(loadedPacks) {
493
- const packRules = loadedPacks.flatMap((loadedPack) => {
494
- return loadedPack.manifest.policies.map((policy) => ({
855
+ async function buildDefaultPolicyLayers(workspaceRoot, loadedPacks) {
856
+ const packRules = [];
857
+ for (const loadedPack of loadedPacks) {
858
+ packRules.push(...loadedPack.manifest.policies.map((policy) => ({
495
859
  id: policy.id,
496
860
  trigger: policy.trigger,
497
861
  decision: policy.decision,
498
862
  reason: policy.reason,
499
- }));
500
- });
863
+ })));
864
+ packRules.push(...(await resolvePackPolicyFactoryRules(workspaceRoot, loadedPack)));
865
+ }
501
866
  return [
502
867
  {
503
868
  level: 'workspace',
@@ -533,6 +898,7 @@ function createRuntimePolicyHook(policyEngine) {
533
898
  tool_name: input.capability.name,
534
899
  pack_id: input.capability.pack,
535
900
  tool_arguments: input.tool_arguments,
901
+ execution_metadata: resolveExecutionMetadata(input.context),
536
902
  };
537
903
  const evaluation = await policyEngine.evaluate(context);
538
904
  return [...builtinDecisions, ...toPolicyHookDecisions(evaluation)];
@@ -973,6 +1339,17 @@ export async function initializeKernelRuntime(options) {
973
1339
  const keyList = rootKeyValidation.errors.join('\n - ');
974
1340
  throw new Error(`Workspace root-key validation failed:\n - ${keyList}`);
975
1341
  }
1342
+ const resolvedLoadedPacks = [];
1343
+ for (const loadedPack of loadedPacks) {
1344
+ const resolvedLoadedPack = await attachResolvedPackConfig(loadedPack, resolvedWorkspace.raw_workspace_data);
1345
+ const resolvedCapabilityAugmentations = await resolvePackCapabilityAugmentations(workspaceRoot, resolvedLoadedPack);
1346
+ const augmentedLoadedPack = {
1347
+ ...resolvedLoadedPack,
1348
+ resolvedCapabilityAugmentations,
1349
+ };
1350
+ validatePackConfigEnvReferences(augmentedLoadedPack);
1351
+ resolvedLoadedPacks.push(augmentedLoadedPack);
1352
+ }
976
1353
  const registry = new ToolRegistry();
977
1354
  if (options.includeBuiltinTools !== false) {
978
1355
  registerBuiltinToolCapabilities(registry, {
@@ -980,7 +1357,7 @@ export async function initializeKernelRuntime(options) {
980
1357
  });
981
1358
  }
982
1359
  const toolCapabilityResolver = options.toolCapabilityResolver ?? defaultRuntimeToolCapabilityResolver;
983
- for (const loadedPack of loadedPacks) {
1360
+ for (const loadedPack of resolvedLoadedPacks) {
984
1361
  for (const tool of loadedPack.manifest.tools) {
985
1362
  let capability;
986
1363
  try {
@@ -1001,7 +1378,7 @@ export async function initializeKernelRuntime(options) {
1001
1378
  }
1002
1379
  }
1003
1380
  const policyEngine = new PolicyEngine({
1004
- layers: options.policyLayers ?? buildDefaultPolicyLayers(loadedPacks),
1381
+ layers: options.policyLayers ?? (await buildDefaultPolicyLayers(workspaceRoot, resolvedLoadedPacks)),
1005
1382
  });
1006
1383
  const evidenceStore = new EvidenceStore({ evidenceRoot });
1007
1384
  const eventStoreOptions = {
@@ -1034,13 +1411,13 @@ export async function initializeKernelRuntime(options) {
1034
1411
  workspace_spec: workspaceSpec,
1035
1412
  workspace_file_path: resolvedWorkspace.workspace_file_path,
1036
1413
  workspace_config_hash: resolvedWorkspace.workspace_config_hash,
1037
- loaded_packs: loadedPacks,
1414
+ loaded_packs: resolvedLoadedPacks,
1038
1415
  task_spec_root: taskSpecRoot,
1039
1416
  event_store: eventStore,
1040
1417
  evidence_store: evidenceStore,
1041
1418
  tool_host: toolHost,
1042
1419
  policy_engine: policyEngine,
1043
- state_aliases: mergeStateAliases(loadedPacks),
1420
+ state_aliases: mergeStateAliases(resolvedLoadedPacks),
1044
1421
  now,
1045
1422
  run_id_factory: options.runIdFactory,
1046
1423
  });