@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.
- package/dist/event-kinds.d.ts +1 -0
- package/dist/event-kinds.d.ts.map +1 -1
- package/dist/event-kinds.js +1 -0
- package/dist/event-kinds.js.map +1 -1
- package/dist/evidence/evidence-store.d.ts +1 -1
- package/dist/evidence/evidence-store.d.ts.map +1 -1
- package/dist/evidence/evidence-store.js +5 -3
- package/dist/evidence/evidence-store.js.map +1 -1
- package/dist/kernel.schemas.d.ts +43 -0
- package/dist/kernel.schemas.d.ts.map +1 -1
- package/dist/kernel.schemas.js +28 -2
- package/dist/kernel.schemas.js.map +1 -1
- package/dist/pack/manifest.d.ts +6 -0
- package/dist/pack/manifest.d.ts.map +1 -1
- package/dist/pack/manifest.js +11 -2
- package/dist/pack/manifest.js.map +1 -1
- package/dist/pack/pack-loader.d.ts +19 -1
- package/dist/pack/pack-loader.d.ts.map +1 -1
- package/dist/pack/pack-loader.js +19 -7
- package/dist/pack/pack-loader.js.map +1 -1
- package/dist/policy/policy-engine.d.ts +9 -0
- package/dist/policy/policy-engine.d.ts.map +1 -1
- package/dist/policy/policy-engine.js.map +1 -1
- package/dist/runtime/kernel-runtime.d.ts +13 -1
- package/dist/runtime/kernel-runtime.d.ts.map +1 -1
- package/dist/runtime/kernel-runtime.js +390 -13
- package/dist/runtime/kernel-runtime.js.map +1 -1
- package/dist/sandbox/bwrap-invocation.js +1 -1
- package/dist/sandbox/bwrap-invocation.js.map +1 -1
- package/dist/sandbox/profile.d.ts +1 -0
- package/dist/sandbox/profile.d.ts.map +1 -1
- package/dist/sandbox/profile.js +6 -2
- package/dist/sandbox/profile.js.map +1 -1
- package/dist/sandbox/subprocess-dispatcher.d.ts.map +1 -1
- package/dist/sandbox/subprocess-dispatcher.js +4 -1
- package/dist/sandbox/subprocess-dispatcher.js.map +1 -1
- package/dist/shared-constants.d.ts +7 -0
- package/dist/shared-constants.d.ts.map +1 -1
- package/dist/shared-constants.js +7 -0
- package/dist/shared-constants.js.map +1 -1
- package/dist/tool-host/tool-host.d.ts +11 -0
- package/dist/tool-host/tool-host.d.ts.map +1 -1
- package/dist/tool-host/tool-host.js +137 -37
- package/dist/tool-host/tool-host.js.map +1 -1
- 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 =
|
|
494
|
-
|
|
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
|
|
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(
|
|
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:
|
|
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(
|
|
1420
|
+
state_aliases: mergeStateAliases(resolvedLoadedPacks),
|
|
1044
1421
|
now,
|
|
1045
1422
|
run_id_factory: options.runIdFactory,
|
|
1046
1423
|
});
|