@exellix/ai-tasks 7.8.0
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/.env.example +3 -0
- package/.metadata/instructions-builders/create-expected-output-instructions.md +195 -0
- package/.metadata/readme.md +48 -0
- package/.metadata/shared/audit.md +29 -0
- package/.metadata/skills/professional-answer +32 -0
- package/.metadata/skills/professional-answer.compressed.md +11 -0
- package/.metadata/skills/professional-answer.instructions +33 -0
- package/.metadata/skills/professional-answer.instructions.md +33 -0
- package/.metadata/skills/professional-answer.l2.md +28 -0
- package/.metadata/skills/professional-answer.md +32 -0
- package/.metadata/skills/professional-answer.prompt +1 -0
- package/.metadata/skills/professional-answer.prompt.md +1 -0
- package/.metadata/skills/professional-decision.md +26 -0
- package/BREAKING-CHANGES.md +43 -0
- package/CHANGELOG.md +147 -0
- package/README.md +2497 -0
- package/RUNTASK_REQUEST.md +369 -0
- package/dist/activix/activixClient.d.ts +6 -0
- package/dist/activix/activixClient.d.ts.map +1 -0
- package/dist/activix/activixClient.js +131 -0
- package/dist/activix/activixClient.js.map +1 -0
- package/dist/activix/getTaskActivities.d.ts +10 -0
- package/dist/activix/getTaskActivities.d.ts.map +1 -0
- package/dist/activix/getTaskActivities.js +17 -0
- package/dist/activix/getTaskActivities.js.map +1 -0
- package/dist/activix/phaseTracking.d.ts +17 -0
- package/dist/activix/phaseTracking.d.ts.map +1 -0
- package/dist/activix/phaseTracking.js +85 -0
- package/dist/activix/phaseTracking.js.map +1 -0
- package/dist/aiScoping/index.d.ts +6 -0
- package/dist/aiScoping/index.d.ts.map +1 -0
- package/dist/aiScoping/index.js +4 -0
- package/dist/aiScoping/index.js.map +1 -0
- package/dist/aiScoping/runAiScoping.d.ts +36 -0
- package/dist/aiScoping/runAiScoping.d.ts.map +1 -0
- package/dist/aiScoping/runAiScoping.js +100 -0
- package/dist/aiScoping/runAiScoping.js.map +1 -0
- package/dist/aiScoping/runScopingCall.d.ts +23 -0
- package/dist/aiScoping/runScopingCall.d.ts.map +1 -0
- package/dist/aiScoping/runScopingCall.js +50 -0
- package/dist/aiScoping/runScopingCall.js.map +1 -0
- package/dist/aiScoping/validateAiScoping.d.ts +7 -0
- package/dist/aiScoping/validateAiScoping.d.ts.map +1 -0
- package/dist/aiScoping/validateAiScoping.js +33 -0
- package/dist/aiScoping/validateAiScoping.js.map +1 -0
- package/dist/aiSkillsUpstreamExports.d.ts +13 -0
- package/dist/aiSkillsUpstreamExports.d.ts.map +1 -0
- package/dist/aiSkillsUpstreamExports.js +12 -0
- package/dist/aiSkillsUpstreamExports.js.map +1 -0
- package/dist/analysis/analyzeRunTaskRequest.d.ts +29 -0
- package/dist/analysis/analyzeRunTaskRequest.d.ts.map +1 -0
- package/dist/analysis/analyzeRunTaskRequest.js +85 -0
- package/dist/analysis/analyzeRunTaskRequest.js.map +1 -0
- package/dist/analysis/index.d.ts +3 -0
- package/dist/analysis/index.d.ts.map +1 -0
- package/dist/analysis/index.js +2 -0
- package/dist/analysis/index.js.map +1 -0
- package/dist/builders/task-request-builder.d.ts +310 -0
- package/dist/builders/task-request-builder.d.ts.map +1 -0
- package/dist/builders/task-request-builder.js +581 -0
- package/dist/builders/task-request-builder.js.map +1 -0
- package/dist/compile/compileTaskConfiguration.d.ts +15 -0
- package/dist/compile/compileTaskConfiguration.d.ts.map +1 -0
- package/dist/compile/compileTaskConfiguration.js +184 -0
- package/dist/compile/compileTaskConfiguration.js.map +1 -0
- package/dist/compile/index.d.ts +2 -0
- package/dist/compile/index.d.ts.map +1 -0
- package/dist/compile/index.js +2 -0
- package/dist/compile/index.js.map +1 -0
- package/dist/core/task-sdk.d.ts +36 -0
- package/dist/core/task-sdk.d.ts.map +1 -0
- package/dist/core/task-sdk.js +2432 -0
- package/dist/core/task-sdk.js.map +1 -0
- package/dist/errors/smartInputValidationError.d.ts +39 -0
- package/dist/errors/smartInputValidationError.d.ts.map +1 -0
- package/dist/errors/smartInputValidationError.js +97 -0
- package/dist/errors/smartInputValidationError.js.map +1 -0
- package/dist/errors/taskConfigurationCompileError.d.ts +16 -0
- package/dist/errors/taskConfigurationCompileError.d.ts.map +1 -0
- package/dist/errors/taskConfigurationCompileError.js +20 -0
- package/dist/errors/taskConfigurationCompileError.js.map +1 -0
- package/dist/execution-strategies/applyExecutionStrategyOutputs.d.ts +17 -0
- package/dist/execution-strategies/applyExecutionStrategyOutputs.d.ts.map +1 -0
- package/dist/execution-strategies/applyExecutionStrategyOutputs.js +63 -0
- package/dist/execution-strategies/applyExecutionStrategyOutputs.js.map +1 -0
- package/dist/execution-strategies/constants.d.ts +14 -0
- package/dist/execution-strategies/constants.d.ts.map +1 -0
- package/dist/execution-strategies/constants.js +13 -0
- package/dist/execution-strategies/constants.js.map +1 -0
- package/dist/execution-strategies/executionStrategyCatalogMetadata.d.ts +9 -0
- package/dist/execution-strategies/executionStrategyCatalogMetadata.d.ts.map +1 -0
- package/dist/execution-strategies/executionStrategyCatalogMetadata.js +37 -0
- package/dist/execution-strategies/executionStrategyCatalogMetadata.js.map +1 -0
- package/dist/execution-strategies/genericExecutionFuncxEnvelope.d.ts +94 -0
- package/dist/execution-strategies/genericExecutionFuncxEnvelope.d.ts.map +1 -0
- package/dist/execution-strategies/genericExecutionFuncxEnvelope.js +306 -0
- package/dist/execution-strategies/genericExecutionFuncxEnvelope.js.map +1 -0
- package/dist/execution-strategies/resolveExecutionStrategies.d.ts +14 -0
- package/dist/execution-strategies/resolveExecutionStrategies.d.ts.map +1 -0
- package/dist/execution-strategies/resolveExecutionStrategies.js +108 -0
- package/dist/execution-strategies/resolveExecutionStrategies.js.map +1 -0
- package/dist/execution-strategies/runFuncxExecutionStrategy.d.ts +37 -0
- package/dist/execution-strategies/runFuncxExecutionStrategy.d.ts.map +1 -0
- package/dist/execution-strategies/runFuncxExecutionStrategy.js +72 -0
- package/dist/execution-strategies/runFuncxExecutionStrategy.js.map +1 -0
- package/dist/index.d.ts +99 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +106 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/resolveLlmCallForXynthesis.d.ts +52 -0
- package/dist/internal/resolveLlmCallForXynthesis.d.ts.map +1 -0
- package/dist/internal/resolveLlmCallForXynthesis.js +81 -0
- package/dist/internal/resolveLlmCallForXynthesis.js.map +1 -0
- package/dist/internal/resolveRunTaskRuntimeKnobs.d.ts +19 -0
- package/dist/internal/resolveRunTaskRuntimeKnobs.d.ts.map +1 -0
- package/dist/internal/resolveRunTaskRuntimeKnobs.js +52 -0
- package/dist/internal/resolveRunTaskRuntimeKnobs.js.map +1 -0
- package/dist/internal/runPostStepLlmCall.d.ts +52 -0
- package/dist/internal/runPostStepLlmCall.d.ts.map +1 -0
- package/dist/internal/runPostStepLlmCall.js +170 -0
- package/dist/internal/runPostStepLlmCall.js.map +1 -0
- package/dist/localTasks/collectEvidence.d.ts +3 -0
- package/dist/localTasks/collectEvidence.d.ts.map +1 -0
- package/dist/localTasks/collectEvidence.js +364 -0
- package/dist/localTasks/collectEvidence.js.map +1 -0
- package/dist/localTasks/decideWebScope.d.ts +3 -0
- package/dist/localTasks/decideWebScope.d.ts.map +1 -0
- package/dist/localTasks/decideWebScope.js +56 -0
- package/dist/localTasks/decideWebScope.js.map +1 -0
- package/dist/localTasks/index.d.ts +5 -0
- package/dist/localTasks/index.d.ts.map +1 -0
- package/dist/localTasks/index.js +19 -0
- package/dist/localTasks/index.js.map +1 -0
- package/dist/localTasks/narrixAssetPlayground.d.ts +13 -0
- package/dist/localTasks/narrixAssetPlayground.d.ts.map +1 -0
- package/dist/localTasks/narrixAssetPlayground.js +161 -0
- package/dist/localTasks/narrixAssetPlayground.js.map +1 -0
- package/dist/localTasks/narrixSubnetPlayground.d.ts +14 -0
- package/dist/localTasks/narrixSubnetPlayground.d.ts.map +1 -0
- package/dist/localTasks/narrixSubnetPlayground.js +168 -0
- package/dist/localTasks/narrixSubnetPlayground.js.map +1 -0
- package/dist/localTasks/narrixVulnGroupPlayground.d.ts +13 -0
- package/dist/localTasks/narrixVulnGroupPlayground.d.ts.map +1 -0
- package/dist/localTasks/narrixVulnGroupPlayground.js +161 -0
- package/dist/localTasks/narrixVulnGroupPlayground.js.map +1 -0
- package/dist/localTasks/narrixVulnInstancePlayground.d.ts +13 -0
- package/dist/localTasks/narrixVulnInstancePlayground.d.ts.map +1 -0
- package/dist/localTasks/narrixVulnInstancePlayground.js +165 -0
- package/dist/localTasks/narrixVulnInstancePlayground.js.map +1 -0
- package/dist/localTasks/nodeCallExport.d.ts +6 -0
- package/dist/localTasks/nodeCallExport.d.ts.map +1 -0
- package/dist/localTasks/nodeCallExport.js +99 -0
- package/dist/localTasks/nodeCallExport.js.map +1 -0
- package/dist/localTasks/nodeCallExportBatch.d.ts +3 -0
- package/dist/localTasks/nodeCallExportBatch.d.ts.map +1 -0
- package/dist/localTasks/nodeCallExportBatch.js +52 -0
- package/dist/localTasks/nodeCallExportBatch.js.map +1 -0
- package/dist/localTasks/normalizeNarrixResult.d.ts +3 -0
- package/dist/localTasks/normalizeNarrixResult.d.ts.map +1 -0
- package/dist/localTasks/normalizeNarrixResult.js +106 -0
- package/dist/localTasks/normalizeNarrixResult.js.map +1 -0
- package/dist/localTasks/registry.d.ts +4 -0
- package/dist/localTasks/registry.d.ts.map +1 -0
- package/dist/localTasks/registry.js +8 -0
- package/dist/localTasks/registry.js.map +1 -0
- package/dist/localTasks/types.d.ts +28 -0
- package/dist/localTasks/types.d.ts.map +1 -0
- package/dist/localTasks/types.js +2 -0
- package/dist/localTasks/types.js.map +1 -0
- package/dist/localTasks/validateInput.d.ts +3 -0
- package/dist/localTasks/validateInput.d.ts.map +1 -0
- package/dist/localTasks/validateInput.js +66 -0
- package/dist/localTasks/validateInput.js.map +1 -0
- package/dist/methods/convenience-methods.d.ts +45 -0
- package/dist/methods/convenience-methods.d.ts.map +1 -0
- package/dist/methods/convenience-methods.js +39 -0
- package/dist/methods/convenience-methods.js.map +1 -0
- package/dist/narrix/applyNarrixScope.d.ts +10 -0
- package/dist/narrix/applyNarrixScope.d.ts.map +1 -0
- package/dist/narrix/applyNarrixScope.js +69 -0
- package/dist/narrix/applyNarrixScope.js.map +1 -0
- package/dist/narrix/buildNarrixAttachment.d.ts +9 -0
- package/dist/narrix/buildNarrixAttachment.d.ts.map +1 -0
- package/dist/narrix/buildNarrixAttachment.js +29 -0
- package/dist/narrix/buildNarrixAttachment.js.map +1 -0
- package/dist/narrix/buildWebScopeScopeInput.d.ts +39 -0
- package/dist/narrix/buildWebScopeScopeInput.d.ts.map +1 -0
- package/dist/narrix/buildWebScopeScopeInput.js +193 -0
- package/dist/narrix/buildWebScopeScopeInput.js.map +1 -0
- package/dist/narrix/flags.d.ts +4 -0
- package/dist/narrix/flags.d.ts.map +1 -0
- package/dist/narrix/flags.js +4 -0
- package/dist/narrix/flags.js.map +1 -0
- package/dist/narrix/index.d.ts +11 -0
- package/dist/narrix/index.d.ts.map +1 -0
- package/dist/narrix/index.js +14 -0
- package/dist/narrix/index.js.map +1 -0
- package/dist/narrix/narrixClient.d.ts +9 -0
- package/dist/narrix/narrixClient.d.ts.map +1 -0
- package/dist/narrix/narrixClient.js +46 -0
- package/dist/narrix/narrixClient.js.map +1 -0
- package/dist/narrix/narrixContextMarkdown.d.ts +15 -0
- package/dist/narrix/narrixContextMarkdown.d.ts.map +1 -0
- package/dist/narrix/narrixContextMarkdown.js +98 -0
- package/dist/narrix/narrixContextMarkdown.js.map +1 -0
- package/dist/narrix/narrixRunnerModule.d.ts +11 -0
- package/dist/narrix/narrixRunnerModule.d.ts.map +1 -0
- package/dist/narrix/narrixRunnerModule.js +17 -0
- package/dist/narrix/narrixRunnerModule.js.map +1 -0
- package/dist/narrix/runNarrixForChat.d.ts +3 -0
- package/dist/narrix/runNarrixForChat.d.ts.map +1 -0
- package/dist/narrix/runNarrixForChat.js +51 -0
- package/dist/narrix/runNarrixForChat.js.map +1 -0
- package/dist/narrix/runNarrixForDocs.d.ts +3 -0
- package/dist/narrix/runNarrixForDocs.d.ts.map +1 -0
- package/dist/narrix/runNarrixForDocs.js +50 -0
- package/dist/narrix/runNarrixForDocs.js.map +1 -0
- package/dist/narrix/runNarrixForRecord.d.ts +3 -0
- package/dist/narrix/runNarrixForRecord.d.ts.map +1 -0
- package/dist/narrix/runNarrixForRecord.js +47 -0
- package/dist/narrix/runNarrixForRecord.js.map +1 -0
- package/dist/narrix/runNarrixForText.d.ts +3 -0
- package/dist/narrix/runNarrixForText.d.ts.map +1 -0
- package/dist/narrix/runNarrixForText.js +49 -0
- package/dist/narrix/runNarrixForText.js.map +1 -0
- package/dist/narrix/runnerDispatch.d.ts +12 -0
- package/dist/narrix/runnerDispatch.d.ts.map +1 -0
- package/dist/narrix/runnerDispatch.js +19 -0
- package/dist/narrix/runnerDispatch.js.map +1 -0
- package/dist/narrix/seedBundleRouting.d.ts +15 -0
- package/dist/narrix/seedBundleRouting.d.ts.map +1 -0
- package/dist/narrix/seedBundleRouting.js +46 -0
- package/dist/narrix/seedBundleRouting.js.map +1 -0
- package/dist/narrix/task.d.ts +4 -0
- package/dist/narrix/task.d.ts.map +1 -0
- package/dist/narrix/task.js +143 -0
- package/dist/narrix/task.js.map +1 -0
- package/dist/narrix/types.d.ts +104 -0
- package/dist/narrix/types.d.ts.map +1 -0
- package/dist/narrix/types.js +3 -0
- package/dist/narrix/types.js.map +1 -0
- package/dist/narrix/webContextMarkdown.d.ts +54 -0
- package/dist/narrix/webContextMarkdown.d.ts.map +1 -0
- package/dist/narrix/webContextMarkdown.js +206 -0
- package/dist/narrix/webContextMarkdown.js.map +1 -0
- package/dist/narrix/webScoper.d.ts +43 -0
- package/dist/narrix/webScoper.d.ts.map +1 -0
- package/dist/narrix/webScoper.js +144 -0
- package/dist/narrix/webScoper.js.map +1 -0
- package/dist/observability/debugTrace.d.ts +31 -0
- package/dist/observability/debugTrace.d.ts.map +1 -0
- package/dist/observability/debugTrace.js +117 -0
- package/dist/observability/debugTrace.js.map +1 -0
- package/dist/observability/extractAiTasksObservability.d.ts +7 -0
- package/dist/observability/extractAiTasksObservability.d.ts.map +1 -0
- package/dist/observability/extractAiTasksObservability.js +98 -0
- package/dist/observability/extractAiTasksObservability.js.map +1 -0
- package/dist/observability/graphExecutionRunLogContract.d.ts +19 -0
- package/dist/observability/graphExecutionRunLogContract.d.ts.map +1 -0
- package/dist/observability/graphExecutionRunLogContract.js +11 -0
- package/dist/observability/graphExecutionRunLogContract.js.map +1 -0
- package/dist/packaged-tasks-client.d.ts +66 -0
- package/dist/packaged-tasks-client.d.ts.map +1 -0
- package/dist/packaged-tasks-client.js +100 -0
- package/dist/packaged-tasks-client.js.map +1 -0
- package/dist/planWebScopeQuestions/index.d.ts +78 -0
- package/dist/planWebScopeQuestions/index.d.ts.map +1 -0
- package/dist/planWebScopeQuestions/index.js +282 -0
- package/dist/planWebScopeQuestions/index.js.map +1 -0
- package/dist/planWebScopeQuestions/runResearchPlanQuestionsFuncx.d.ts +18 -0
- package/dist/planWebScopeQuestions/runResearchPlanQuestionsFuncx.d.ts.map +1 -0
- package/dist/planWebScopeQuestions/runResearchPlanQuestionsFuncx.js +42 -0
- package/dist/planWebScopeQuestions/runResearchPlanQuestionsFuncx.js.map +1 -0
- package/dist/post-steps/audit/loadAuditTemplates.d.ts +72 -0
- package/dist/post-steps/audit/loadAuditTemplates.d.ts.map +1 -0
- package/dist/post-steps/audit/loadAuditTemplates.js +62 -0
- package/dist/post-steps/audit/loadAuditTemplates.js.map +1 -0
- package/dist/post-steps/audit/parseAuditOutput.d.ts +11 -0
- package/dist/post-steps/audit/parseAuditOutput.d.ts.map +1 -0
- package/dist/post-steps/audit/parseAuditOutput.js +50 -0
- package/dist/post-steps/audit/parseAuditOutput.js.map +1 -0
- package/dist/post-steps/audit/runAudit.d.ts +22 -0
- package/dist/post-steps/audit/runAudit.d.ts.map +1 -0
- package/dist/post-steps/audit/runAudit.js +406 -0
- package/dist/post-steps/audit/runAudit.js.map +1 -0
- package/dist/post-steps/audit/runAuditCall.d.ts +23 -0
- package/dist/post-steps/audit/runAuditCall.d.ts.map +1 -0
- package/dist/post-steps/audit/runAuditCall.js +32 -0
- package/dist/post-steps/audit/runAuditCall.js.map +1 -0
- package/dist/post-steps/polish/loadPolishTemplates.d.ts +35 -0
- package/dist/post-steps/polish/loadPolishTemplates.d.ts.map +1 -0
- package/dist/post-steps/polish/loadPolishTemplates.js +38 -0
- package/dist/post-steps/polish/loadPolishTemplates.js.map +1 -0
- package/dist/post-steps/polish/parsePolishOutput.d.ts +6 -0
- package/dist/post-steps/polish/parsePolishOutput.d.ts.map +1 -0
- package/dist/post-steps/polish/parsePolishOutput.js +47 -0
- package/dist/post-steps/polish/parsePolishOutput.js.map +1 -0
- package/dist/post-steps/polish/runPolish.d.ts +24 -0
- package/dist/post-steps/polish/runPolish.d.ts.map +1 -0
- package/dist/post-steps/polish/runPolish.js +147 -0
- package/dist/post-steps/polish/runPolish.js.map +1 -0
- package/dist/post-steps/polish/runPolishCall.d.ts +22 -0
- package/dist/post-steps/polish/runPolishCall.d.ts.map +1 -0
- package/dist/post-steps/polish/runPolishCall.js +32 -0
- package/dist/post-steps/polish/runPolishCall.js.map +1 -0
- package/dist/post-steps/resolvePostStepConfig.d.ts +58 -0
- package/dist/post-steps/resolvePostStepConfig.d.ts.map +1 -0
- package/dist/post-steps/resolvePostStepConfig.js +105 -0
- package/dist/post-steps/resolvePostStepConfig.js.map +1 -0
- package/dist/rendrixUpstreamExports.d.ts +7 -0
- package/dist/rendrixUpstreamExports.d.ts.map +1 -0
- package/dist/rendrixUpstreamExports.js +6 -0
- package/dist/rendrixUpstreamExports.js.map +1 -0
- package/dist/skillCatalogExports.d.ts +8 -0
- package/dist/skillCatalogExports.d.ts.map +1 -0
- package/dist/skillCatalogExports.js +8 -0
- package/dist/skillCatalogExports.js.map +1 -0
- package/dist/strategies/direct-execution-strategy.d.ts +31 -0
- package/dist/strategies/direct-execution-strategy.d.ts.map +1 -0
- package/dist/strategies/direct-execution-strategy.js +107 -0
- package/dist/strategies/direct-execution-strategy.js.map +1 -0
- package/dist/strategies/execution-strategy.interface.d.ts +31 -0
- package/dist/strategies/execution-strategy.interface.d.ts.map +1 -0
- package/dist/strategies/execution-strategy.interface.js +2 -0
- package/dist/strategies/execution-strategy.interface.js.map +1 -0
- package/dist/strategies/index.d.ts +9 -0
- package/dist/strategies/index.d.ts.map +1 -0
- package/dist/strategies/index.js +8 -0
- package/dist/strategies/index.js.map +1 -0
- package/dist/strategies/strategy-factory.d.ts +45 -0
- package/dist/strategies/strategy-factory.d.ts.map +1 -0
- package/dist/strategies/strategy-factory.js +59 -0
- package/dist/strategies/strategy-factory.js.map +1 -0
- package/dist/synthesis/index.d.ts +9 -0
- package/dist/synthesis/index.d.ts.map +1 -0
- package/dist/synthesis/index.js +8 -0
- package/dist/synthesis/index.js.map +1 -0
- package/dist/synthesis/resolveSourceMaterial.d.ts +35 -0
- package/dist/synthesis/resolveSourceMaterial.d.ts.map +1 -0
- package/dist/synthesis/resolveSourceMaterial.js +152 -0
- package/dist/synthesis/resolveSourceMaterial.js.map +1 -0
- package/dist/synthesis/runStructuredSynthesisRobust.d.ts +42 -0
- package/dist/synthesis/runStructuredSynthesisRobust.d.ts.map +1 -0
- package/dist/synthesis/runStructuredSynthesisRobust.js +303 -0
- package/dist/synthesis/runStructuredSynthesisRobust.js.map +1 -0
- package/dist/task-strategies/buildTaskStrategyCatalogDescriptor.d.ts +19 -0
- package/dist/task-strategies/buildTaskStrategyCatalogDescriptor.d.ts.map +1 -0
- package/dist/task-strategies/buildTaskStrategyCatalogDescriptor.js +242 -0
- package/dist/task-strategies/buildTaskStrategyCatalogDescriptor.js.map +1 -0
- package/dist/task-strategies/canonicalInputExecutionStrategies.d.ts +171 -0
- package/dist/task-strategies/canonicalInputExecutionStrategies.d.ts.map +1 -0
- package/dist/task-strategies/canonicalInputExecutionStrategies.js +117 -0
- package/dist/task-strategies/canonicalInputExecutionStrategies.js.map +1 -0
- package/dist/task-strategies/canonicalNarrixModes.d.ts +31 -0
- package/dist/task-strategies/canonicalNarrixModes.d.ts.map +1 -0
- package/dist/task-strategies/canonicalNarrixModes.js +35 -0
- package/dist/task-strategies/canonicalNarrixModes.js.map +1 -0
- package/dist/task-strategies/canonicalTaskStrategies.d.ts +104 -0
- package/dist/task-strategies/canonicalTaskStrategies.d.ts.map +1 -0
- package/dist/task-strategies/canonicalTaskStrategies.js +77 -0
- package/dist/task-strategies/canonicalTaskStrategies.js.map +1 -0
- package/dist/task-strategies/cataloxCatalogViews.d.ts +55 -0
- package/dist/task-strategies/cataloxCatalogViews.d.ts.map +1 -0
- package/dist/task-strategies/cataloxCatalogViews.js +65 -0
- package/dist/task-strategies/cataloxCatalogViews.js.map +1 -0
- package/dist/task-strategies/constants.d.ts +49 -0
- package/dist/task-strategies/constants.d.ts.map +1 -0
- package/dist/task-strategies/constants.js +49 -0
- package/dist/task-strategies/constants.js.map +1 -0
- package/dist/task-strategies/index.d.ts +22 -0
- package/dist/task-strategies/index.d.ts.map +1 -0
- package/dist/task-strategies/index.js +13 -0
- package/dist/task-strategies/index.js.map +1 -0
- package/dist/task-strategies/listAiTaskStrategies.d.ts +43 -0
- package/dist/task-strategies/listAiTaskStrategies.d.ts.map +1 -0
- package/dist/task-strategies/listAiTaskStrategies.js +74 -0
- package/dist/task-strategies/listAiTaskStrategies.js.map +1 -0
- package/dist/task-strategies/normalize.d.ts +7 -0
- package/dist/task-strategies/normalize.d.ts.map +1 -0
- package/dist/task-strategies/normalize.js +44 -0
- package/dist/task-strategies/normalize.js.map +1 -0
- package/dist/task-strategies/types.d.ts +37 -0
- package/dist/task-strategies/types.d.ts.map +1 -0
- package/dist/task-strategies/types.js +2 -0
- package/dist/task-strategies/types.js.map +1 -0
- package/dist/types/decision-contracts.d.ts +31 -0
- package/dist/types/decision-contracts.d.ts.map +1 -0
- package/dist/types/decision-contracts.js +23 -0
- package/dist/types/decision-contracts.js.map +1 -0
- package/dist/types/evidence-types.d.ts +108 -0
- package/dist/types/evidence-types.d.ts.map +1 -0
- package/dist/types/evidence-types.js +9 -0
- package/dist/types/evidence-types.js.map +1 -0
- package/dist/types/executionType.d.ts +9 -0
- package/dist/types/executionType.d.ts.map +1 -0
- package/dist/types/executionType.js +8 -0
- package/dist/types/executionType.js.map +1 -0
- package/dist/types/index.d.ts +28 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +12 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/llmCall.d.ts +121 -0
- package/dist/types/llmCall.d.ts.map +1 -0
- package/dist/types/llmCall.js +39 -0
- package/dist/types/llmCall.js.map +1 -0
- package/dist/types/task-configuration.d.ts +60 -0
- package/dist/types/task-configuration.d.ts.map +1 -0
- package/dist/types/task-configuration.js +3 -0
- package/dist/types/task-configuration.js.map +1 -0
- package/dist/types/task-types.d.ts +887 -0
- package/dist/types/task-types.d.ts.map +1 -0
- package/dist/types/task-types.js +21 -0
- package/dist/types/task-types.js.map +1 -0
- package/dist/utilities/runUtility.d.ts +3 -0
- package/dist/utilities/runUtility.d.ts.map +1 -0
- package/dist/utilities/runUtility.js +204 -0
- package/dist/utilities/runUtility.js.map +1 -0
- package/dist/utils/assertRequiredRunSkillCorrelation.d.ts +7 -0
- package/dist/utils/assertRequiredRunSkillCorrelation.d.ts.map +1 -0
- package/dist/utils/assertRequiredRunSkillCorrelation.js +17 -0
- package/dist/utils/assertRequiredRunSkillCorrelation.js.map +1 -0
- package/dist/utils/assertValidSmartInputConfig.d.ts +5 -0
- package/dist/utils/assertValidSmartInputConfig.d.ts.map +1 -0
- package/dist/utils/assertValidSmartInputConfig.js +71 -0
- package/dist/utils/assertValidSmartInputConfig.js.map +1 -0
- package/dist/utils/bridgeRunSkillGatewayMemory.d.ts +13 -0
- package/dist/utils/bridgeRunSkillGatewayMemory.d.ts.map +1 -0
- package/dist/utils/bridgeRunSkillGatewayMemory.js +65 -0
- package/dist/utils/bridgeRunSkillGatewayMemory.js.map +1 -0
- package/dist/utils/extractSmartInputRenderResult.d.ts +7 -0
- package/dist/utils/extractSmartInputRenderResult.d.ts.map +1 -0
- package/dist/utils/extractSmartInputRenderResult.js +30 -0
- package/dist/utils/extractSmartInputRenderResult.js.map +1 -0
- package/dist/utils/jsonPaths.d.ts +6 -0
- package/dist/utils/jsonPaths.d.ts.map +1 -0
- package/dist/utils/jsonPaths.js +32 -0
- package/dist/utils/jsonPaths.js.map +1 -0
- package/dist/utils/normalizeSmartInputConfig.d.ts +5 -0
- package/dist/utils/normalizeSmartInputConfig.d.ts.map +1 -0
- package/dist/utils/normalizeSmartInputConfig.js +30 -0
- package/dist/utils/normalizeSmartInputConfig.js.map +1 -0
- package/dist/utils/outputValidation.d.ts +19 -0
- package/dist/utils/outputValidation.d.ts.map +1 -0
- package/dist/utils/outputValidation.js +75 -0
- package/dist/utils/outputValidation.js.map +1 -0
- package/dist/utils/runTaskRequestShape.d.ts +16 -0
- package/dist/utils/runTaskRequestShape.d.ts.map +1 -0
- package/dist/utils/runTaskRequestShape.js +80 -0
- package/dist/utils/runTaskRequestShape.js.map +1 -0
- package/dist/utils/skillTemplateVariables.d.ts +20 -0
- package/dist/utils/skillTemplateVariables.d.ts.map +1 -0
- package/dist/utils/skillTemplateVariables.js +63 -0
- package/dist/utils/skillTemplateVariables.js.map +1 -0
- package/dist/utils/xynthesizedSmartInputPaths.d.ts +16 -0
- package/dist/utils/xynthesizedSmartInputPaths.d.ts.map +1 -0
- package/dist/utils/xynthesizedSmartInputPaths.js +56 -0
- package/dist/utils/xynthesizedSmartInputPaths.js.map +1 -0
- package/dist/utils/xynthesizedWrite.d.ts +10 -0
- package/dist/utils/xynthesizedWrite.d.ts.map +1 -0
- package/dist/utils/xynthesizedWrite.js +61 -0
- package/dist/utils/xynthesizedWrite.js.map +1 -0
- package/dist/validation/analyzeExpectedRunTaskInput.d.ts +41 -0
- package/dist/validation/analyzeExpectedRunTaskInput.d.ts.map +1 -0
- package/dist/validation/analyzeExpectedRunTaskInput.js +133 -0
- package/dist/validation/analyzeExpectedRunTaskInput.js.map +1 -0
- package/dist/validation/collectSmartInputValidationIssues.d.ts +6 -0
- package/dist/validation/collectSmartInputValidationIssues.d.ts.map +1 -0
- package/dist/validation/collectSmartInputValidationIssues.js +38 -0
- package/dist/validation/collectSmartInputValidationIssues.js.map +1 -0
- package/dist/validation/helpers.d.ts +15 -0
- package/dist/validation/helpers.d.ts.map +1 -0
- package/dist/validation/helpers.js +184 -0
- package/dist/validation/helpers.js.map +1 -0
- package/dist/validation/index.d.ts +9 -0
- package/dist/validation/index.d.ts.map +1 -0
- package/dist/validation/index.js +6 -0
- package/dist/validation/index.js.map +1 -0
- package/dist/validation/types.d.ts +51 -0
- package/dist/validation/types.d.ts.map +1 -0
- package/dist/validation/types.js +5 -0
- package/dist/validation/types.js.map +1 -0
- package/dist/validation/validateRunTaskConfig.d.ts +8 -0
- package/dist/validation/validateRunTaskConfig.d.ts.map +1 -0
- package/dist/validation/validateRunTaskConfig.js +158 -0
- package/dist/validation/validateRunTaskConfig.js.map +1 -0
- package/dist/validation/validateRunTaskInvoke.d.ts +30 -0
- package/dist/validation/validateRunTaskInvoke.d.ts.map +1 -0
- package/dist/validation/validateRunTaskInvoke.js +108 -0
- package/dist/validation/validateRunTaskInvoke.js.map +1 -0
- package/documenations/activix-feature-request-identity.md +123 -0
- package/documenations/activix.md +175 -0
- package/documenations/bug-report-xynthesis-and-synthesis-call.md +217 -0
- package/documenations/core-runtime-tokens-and-strategies.md +123 -0
- package/documenations/downstream-environment.md +48 -0
- package/documenations/downstream-test-runtime-teardown-cleanup.md +73 -0
- package/documenations/examples/xynthesis-run-task-request.example.json +170 -0
- package/documenations/feature-request-ai-skills-raw-template-access.md +82 -0
- package/documenations/feature-request-athenix-core-directive.md +145 -0
- package/documenations/feature-request-athenix-token-extraction.md +124 -0
- package/documenations/funcx-catalog-hosting-checklist.md +107 -0
- package/documenations/funcx-scoping-integration-gaps.md +120 -0
- package/documenations/funcx-upstream-github-issues-draft.md +153 -0
- package/documenations/identity-metadata-contract.md +165 -0
- package/documenations/intermediate-steps.md +33 -0
- package/documenations/record-and-template-variables.md +32 -0
- package/documenations/run-task-execution-flow.md +153 -0
- package/documenations/run-task-single-run-checklist.md +109 -0
- package/documenations/schemas/README.md +40 -0
- package/documenations/schemas/openapi-3.1-components.yaml +24 -0
- package/documenations/schemas/v1/output-schema.json +55 -0
- package/documenations/schemas/v1/output-validation-result.json +41 -0
- package/documenations/schemas/v1/run-task-request.json +219 -0
- package/documenations/schemas/v1/synthesized-artifacts.json +133 -0
- package/documenations/synthesis-invocation-notes.md +26 -0
- package/documenations/synthesized-context-guide.md +84 -0
- package/documenations/task-core-and-core-aware-synthesis.md +58 -0
- package/documenations/upstream-feature-requests/ai-skills-llm-observability.md +129 -0
- package/documenations/upstream-feature-requests/xynthesis-llm-observability.md +125 -0
- package/documenations/upstream-feedback-request-shape-clarification.md +101 -0
- package/documenations/web-context-precedence.md +33 -0
- package/documenations/web-scoping-in-ai-tasks.md +503 -0
- package/documenations/xynthesis-activix-telemetry.md +28 -0
- package/documenations/xynthesis-upstream-fixes-checklist.md +71 -0
- package/package.json +92 -0
package/README.md
ADDED
|
@@ -0,0 +1,2497 @@
|
|
|
1
|
+
# @exellix/ai-tasks
|
|
2
|
+
|
|
3
|
+
Private Git/npm package for executing **tasks** using the Woreces execution stack.
|
|
4
|
+
|
|
5
|
+
**Breaking — `executionStrategies` (required):** Every `runTask` request must include **`executionStrategies`**: an array of FuncX MAIN wrappers or **`[]`** for plain gateway MAIN. The old **`executionStrategyKey`** field is removed. See [BREAKING-CHANGES.md](BREAKING-CHANGES.md) and [RUNTASK_REQUEST.md](RUNTASK_REQUEST.md). Supported MAIN execution is exactly: **direct** via **`executionStrategies: []`**, **planner** before MAIN, **optimizer** after MAIN, or planner + optimizer together. Default FuncX function ids (generic envelope via **`run()`**, **`@x12i/funcx` ≥ 3.8.2** recommended): **`execution/plan`**, **`execution/evaluate-result`** (overridable via each row’s `args.functionId` when the alternate implementation uses the **same** envelope). Planner/optimizer responses are normalized with **`getRunJsonResult`** from `@x12i/funcx/functions` (also re-exported from this package as **`unwrapFuncxRunValue`** → **`getRunJsonResult`**).
|
|
6
|
+
|
|
7
|
+
**FuncX catalog / hosting:** Those function ids must exist in your FuncX content resolver for live **`run()`** calls — see [`documenations/funcx-catalog-hosting-checklist.md`](documenations/funcx-catalog-hosting-checklist.md).
|
|
8
|
+
|
|
9
|
+
**Execution pipeline (optional):** You can use `executionPipeline` (array of pre/main/post steps) instead of a single `executionType`. PRE steps include `synthesized-context`; POST steps include `audit` (quality-gate loop) and `polish` (refinement checklist). See [BREAKING-CHANGES.md](BREAKING-CHANGES.md) for migration. When `executionPipeline` is omitted, existing `executionType` behavior is unchanged.
|
|
10
|
+
|
|
11
|
+
This package implements the canonical `runTask()` flow (every request carries **`executionStrategies`** — use **`[]`** for plain MAIN):
|
|
12
|
+
|
|
13
|
+
1) **if** `request.narrix` **is set** (task-level pre-processor) → resolve raw record from executionMemory/jobMemory/input, run NARRIX (to-CNI + engine), build `_narrix` attachment (scoping/discovery/meta), inject into `executionMemory` and `jobMemory`; **if** `narrix.enableWebScope === true`, also run **`@exellix/narrix-web-scoper`** and set **`executionMemory.webContext`** (failures are non-fatal); then continue with the same request so the task sees enriched context
|
|
14
|
+
2) **if a local task handler is registered for `skillKey`** → run it and return (no enrichment, no LLM); **`ctx`** includes optional **`xynthesized`** / **`smartInput`** when the caller supplied them
|
|
15
|
+
3) **if** `executionType === 'narrix-then-direct'` **and** `narrixInput` is provided → resolve narrix input, run Narrix, append output to `taskMemory.narrix`, then run the standard DIRECT path (enrich → context → executor); response includes `metadata.narrix`
|
|
16
|
+
4) inject `bindingDefaultsDb` for Xronox routing (defaulting to `MONGO_LOGS_DB` or `logs-db`)
|
|
17
|
+
5) enrich job/task memory with **task-scoped** scoping (using `skillKey`)
|
|
18
|
+
6) generate **task-scoped** context markdown only when requested (`includeContextInPrompt === true`; default is no context). When NARRIX is in play, context is the "## Scoping and discovery" section from `executionMemory._narrix` (`buildNarrixPreProcessorContextMarkdown`), plus—when web scoping returned a hit—a **## Web sources (primary evidence)** block built from `executionMemory.webContext` (by default **cleaned** text is preferred over raw HTML: `providerContent` / `content` before `providerRawContent` / `rawContent`, then `snippet`; override on the DIRECT path is not exposed—synthesis PRE step can tune via `SynthesisConfig.webEvidence.preferCleanContent`). Summary/findings are labeled as hints only. When NARRIX is not in play, context comes from the context generator plus any `taskMemory.narrix` section.
|
|
19
|
+
7) execute via executor using `executionType` (or the pipeline MAIN step when `executionPipeline` is set)
|
|
20
|
+
8) on not-found, call registry diagnostics
|
|
21
|
+
|
|
22
|
+
**`executionPipeline`:** When `executionPipeline` is a non-empty array, PRE steps (e.g. **`synthesized-context`**) run first, then exactly one MAIN step, then optional POST steps. Memory enrichment and context generation in step 6 apply to the MAIN `direct` run; the synthesizer’s **source material** is controlled separately by **`contextSourcePolicy`** / **`webEvidence`** (see below).
|
|
23
|
+
|
|
24
|
+
### Template core and core-aware synthesis (additive)
|
|
25
|
+
|
|
26
|
+
`runTask` supports template-core-aware structured synthesis in PRE `synthesized-context`:
|
|
27
|
+
|
|
28
|
+
- Template content declares one or more core directives in Athenix `{{core:...}}` format (for example `{{core:analysis}}`); closed list: `question`, `action`, `plan`, `objective`, `decision`, `comparison`, `classification`, `evaluation`, `analysis`, `summary`, `generation`, `extraction`.
|
|
29
|
+
- `RunTaskRequest.taskCore` is removed from structured synthesis semantics; runtime derives cores from template declarations.
|
|
30
|
+
- Synthesis mode selection is additive:
|
|
31
|
+
- preferred: `SynthesisConfig.synthesisMode: "markdown" | "structured"`
|
|
32
|
+
- legacy-compatible: `SynthesisConfig.synthesisOutputFormat` still works.
|
|
33
|
+
- Structured mode uses detected `templateCores` + resolved question, then builds clean MAIN context from validated synthesized payload.
|
|
34
|
+
- Core discovery reads raw templates (instructions/prompt) via **`WorecesSkillsClient.resolveRawTemplate`** (templates loaded from the Catalox **`ai-skills`** catalog in `@woroces/ai-skills` 4.1+) before normal rendering; if no core directives are declared, structured synthesis is rejected as a template-definition error.
|
|
35
|
+
- Raw/enriched materials (`_narrix`, memory bundle, `webContext`) remain in memory; synthesized output is also stored at **`executionMemory.synthesizedContext`** for MAIN traceability unless a PRE step opts out via **`SynthesisConfig.xynthesizedOutput.alsoWriteLegacySynthesizedContext: false`** (see [Xynthesized memory and smart input](#xynthesized-memory-and-smart-input)).
|
|
36
|
+
- Optional **`RunTaskRequest.xynthesized`** holds **job-scoped**, **task-scoped**, and **execution-scoped** synthesized material for graph execution (distinct from raw memories). Optional **`smartInput`** matches **`SmartInputConfig`** from **`@exellix/ai-skills`** / Rendrix (**`paths`** as **`{ title, path, required? }[]`**). Callers may still send legacy **`paths: string[]`**; **`runTask`** validates and **normalizes** each string to **`{ title: path, path }`** before **`runSkill`**. Paths under **`xynthesized.*`** must use scope **`job`**, **`task`**, or **`execution`**; full path resolution for **`{{smartInput}}`** happens in the gateway/Rendrix stack. Optional **`smartInputRenderOptions`** is passed through like other **`RunSkillRequest`** fields.
|
|
37
|
+
- Backward compatibility is preserved by default: existing flows continue unchanged unless structured mode is explicitly selected.
|
|
38
|
+
|
|
39
|
+
Task responses may optionally include **`intermediateSteps`** when a task (or skill) runs multiple logical steps in one call (e.g. to-cni + enrich + triage); see [Intermediate steps (multi-step tasks)](#intermediate-steps-multi-step-tasks).
|
|
40
|
+
|
|
41
|
+
`@exellix/ai-tasks` reuses `@woroces/ai-skills` directly (private packages; naming leakage is explicitly acceptable).
|
|
42
|
+
|
|
43
|
+
### Gateway template rendering (v4)
|
|
44
|
+
|
|
45
|
+
Task execution goes through **`WorecesSkillsClient.runSkill`** (or the skills client’s executor), which uses **`gateway.invoke()`** — not `invokeChat()` — so instruction, prompt, and context templates are built via **`buildMessages`**, nx-content resolution, and **`@x12i/rendrix`** `render`. The gateway rejects a top-level **`input`** field on invoke requests; **`runSkill` / `runAudit`** map caller **`input`** (and related fields) into **`workingMemory.input`** (merged with `variables`, memories, object `context`, `knowledge`) so `.prompt` templates can use **`{{input}}`**.
|
|
46
|
+
|
|
47
|
+
On the **`runTask`** MAIN path, **`variables`** is the **job/graph bucket** forwarded **as-is** (align with **`executionMemory.jobVariables`**). **`executionMemory.taskVariables`** holds node scope. **`input`**, **`xynthesized`**, and **`smartInput`** are separate top-level fields — ai-tasks does **not** fold them into **`variables`**. Templates and Rendrix resolve **`jobVariables.*`**, **`taskVariables.*`**, **`xynthesized.*`**, and **`smartInput`** against the forwarded payload. Optional host helper **`mergeSkillTemplateVariables`** merges maps outside default MAIN.
|
|
48
|
+
|
|
49
|
+
**In this package:**
|
|
50
|
+
|
|
51
|
+
- **`runTask({ ... })`** accepts the same optional fields as **`RunSkillRequest`**: **`templateRenderOptions`**, **`templateTokens`**, **`smartInputRenderOptions`** (with **`smartInput`**). They are passed through unchanged to **`runSkill`** on the DIRECT / pipeline MAIN path.
|
|
52
|
+
- **Default parser options** for all skill runs: set **`templateRendering`** on **`WorecesSkillsClientOptions`** when constructing **`WorecesSkillsClient`** (or configure **`templateRendering`** on a gateway instance you pass as **`options.gateway`**). Per-call overrides use **`templateRenderOptions`** on the request.
|
|
53
|
+
- **Synthesis, audit, polish, and AI scoping** are executed via **`@exellix/xynthesis`** (and therefore inherit xynthesis behavior for retries/repair and diagnostics when enabled).
|
|
54
|
+
|
|
55
|
+
| Mechanism | Where | Effect |
|
|
56
|
+
|-----------|--------|--------|
|
|
57
|
+
| **`WorecesSkillsClientOptions.templateRendering`** | Client constructor | Sets gateway defaults when this package (or your app) constructs **`AIGateway`**. If you inject **`options.gateway`**, set **`templateRendering`** on that instance. |
|
|
58
|
+
| **`RunTaskRequest.templateRenderOptions`** | Per **`runTask`** | Deep-merged on gateway defaults for that invoke only. |
|
|
59
|
+
| **`RunTaskRequest.templateTokens`** | Per **`runTask`** | Passed through as gateway **`templateTokens`** (highest overlay priority during render). |
|
|
60
|
+
|
|
61
|
+
Types **`TemplateRenderOptions`**, **`SmartInputConfig`**, **`SmartInputRenderOptions`**, **`SmartInputRenderResult`**, **`RunTaskSmartInput`**, and **`GatewayTemplateTokens`** are re-exported from **`@exellix/ai-tasks`** (skill/gateway/Rendrix definitions). **`mergeSkillTemplateVariables`** is exported for callers that reproduce the same merge outside **`runTask`**. **`TaskRequestBuilder`** supports **`.withTemplateRenderOptions(...)`** and **`.withTemplateTokens(...)`**, plus **`.withXynthesized`**, **`.withSmartInput`**, **`.withSmartInputPaths`**, **`.withSmartInputRenderOptions(...)`**, **`.withXynthesizedJob`**, **`.withXynthesizedTask`**, **`.withXynthesizedExecution`** (see [Xynthesized memory and smart input](#xynthesized-memory-and-smart-input)).
|
|
62
|
+
|
|
63
|
+
**Errors:** **`TemplateResolutionError`** (or codes like **`TEMPLATE_RESOLUTION_ERROR`** / **`TEMPLATE_VARIABLE_MISSING`**) means a **required** template path was **`undefined`** after the gateway merged memory. Fix **`variables` / memories / input**, use optional fragments (**`{{path \|}}`**) in templates, or see the v4 guide for legacy **`silentMissingMustTokens`**. Template resolution failures are **not** treated as missing registry content (they do not go through **`RegistryManager.diagnose`** the same way as “content not found”).
|
|
64
|
+
|
|
65
|
+
Full protocol (MUST vs optional tokens, **`subPathSearch`**, errors): **[GATEWAY_TEMPLATE_PROTOCOL_V4.md](https://github.com/woroces/ai-skills/blob/main/GATEWAY_TEMPLATE_PROTOCOL_V4.md)** in **`@woroces/ai-skills`**. Nx-content layout: **`@x12i/ai-gateway`** [Content Resolver — Upstream Guide](https://github.com/athenices/ai-gateway/blob/main/CONTENT_RESOLVER_UPSTREAM_GUIDE.md).
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Install
|
|
70
|
+
|
|
71
|
+
**Packaged skills (recommended):** depend only on `@exellix/ai-tasks`; it bundles the execution stack and ships `.metadata` templates.
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
npm install @exellix/ai-tasks
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Custom gateway / advanced injection:** add `@woroces/ai-skills` and construct `WorexClientTasks` with your own client + executor.
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
npm install @exellix/ai-tasks @woroces/ai-skills
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Bundled x12i stack (direct dependencies of this package):** `@x12i/catalox` (catalog reads / publish script), `@x12i/env` (env conventions aligned with the Woreces stack), and `@x12i/logxer` (package log-level helpers such as `resolvePackageLogsLevel` used by the optional Activix client). Older names `logs-gateway` and `nx-config2` are not direct dependencies here.
|
|
84
|
+
|
|
85
|
+
### Narrix (v5+)
|
|
86
|
+
|
|
87
|
+
From **v5.0.0**, the local **`skills/skill.local:narrixRun`** path and **`narrixInput`** for **`narrix-then-direct`** require a unified shape: always set **`medium`** (`record` | `text` | `docs` | `chat`), **`datasetId`**, and the payload fields for that medium. Routing uses **`@exellix/narrix-runner` v2** (`runByQuestion` / `runByNarrative`) and **`@exellix/narrix-catalox`** for catalog hints—there is **no** `narrix-packs-library` or legacy “record job without `medium`” compatibility.
|
|
88
|
+
|
|
89
|
+
**Migration:** see **[BREAKING-CHANGES-NARRIX-V5.md](./BREAKING-CHANGES-NARRIX-V5.md)**. **Live Narrix tests:** `npm run test:narrix:live` (requires `.archive/packages/…` seed; see that doc).
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Catalox catalogs (upstream from this package)
|
|
94
|
+
|
|
95
|
+
`@exellix/ai-tasks` publishes **task-strategy** metadata to Catalox under **`appId: ai-tasks`**, and re-exports **read helpers** so apps can list catalogs, descriptors, and items **without adding `@x12i/catalox` as a direct dependency** (types such as `Catalox`, `CataloxContext`, `AppCatalogBootstrap` are re-exported from the task-strategies surface).
|
|
96
|
+
|
|
97
|
+
### AI Tasks catalogs
|
|
98
|
+
|
|
99
|
+
| Catalog ID | Role |
|
|
100
|
+
|-------------|------|
|
|
101
|
+
| **`ai-task-strategies-pre`** | **Pre–core** strategies: `synthesisInputStrategy` for the `synthesized-context` PRE step. |
|
|
102
|
+
| **`ai-task-strategies-post`** | **Post–core** strategies: e.g. audit `selectionStrategy` (`best` / `synthesis`). |
|
|
103
|
+
| **`ai-task-input-strategies`** | Optional authoring hints: `inputStrategyKey` on `RunTaskRequest` (does not drive Narrix invocation). |
|
|
104
|
+
| **`execution-strategy`** | Preferred catalog for supported MAIN execution metadata: **`direct`**, **`planner`**, **`optimizer`**. |
|
|
105
|
+
| **`ai-task-execution-strategies`** | Compatibility MAIN wrappers metadata catalog: same seeded rows as **`execution-strategy`**. |
|
|
106
|
+
| **`ai-task-main-execution-wrappers`** | Compatibility MAIN FuncX wrappers catalog: same seeded rows as **`execution-strategy`**. |
|
|
107
|
+
| **`ai-task-narrix-modes`** | Narrix invocation: `narrixMode` (`off` \| `preprocessor` \| `handler`). |
|
|
108
|
+
|
|
109
|
+
Runtime **does not require** Catalox to be configured for these keys. Catalogs seed console metadata, and `execution-strategy` rows may be passed to `runTask` as `executionStrategyCatalogItems` for guarded metadata consumption. The runtime still code-validates supported strategy keys, phases, and retry rules; malformed or conflicting catalog rows are ignored. **Consumption order:** optional Narrix → optional web (preprocessor path) → optional Xynthesis **PRE** steps → **MAIN**.
|
|
110
|
+
|
|
111
|
+
### Supported MAIN Execution Strategies
|
|
112
|
+
|
|
113
|
+
The runtime supports three MAIN execution strategy records:
|
|
114
|
+
|
|
115
|
+
| Strategy | Request shape | Runtime behavior |
|
|
116
|
+
|----------|---------------|------------------|
|
|
117
|
+
| **`direct`** | **`executionStrategies: []`** | Plain MAIN. Calls `runSkill` once. `direct` is catalog metadata only and is **not** valid as a row inside `executionStrategies[]`. |
|
|
118
|
+
| **`planner`** | **`{ strategyKey: "planner", phase: "before", priority }`** | Runs FuncX **`execution/plan`** before MAIN and can merge instructions, prompt, variables, and template tokens into the request. |
|
|
119
|
+
| **`optimizer`** | **`{ strategyKey: "optimizer", phase: "after", priority, maxIterations? }`** | Runs FuncX **`execution/evaluate-result`** after MAIN. If not satisfied, retries MAIN with feedback until satisfied or `maxIterations` is reached. |
|
|
120
|
+
|
|
121
|
+
`planner` must use `phase: "before"` and `optimizer` must use `phase: "after"`. `optimizer.maxIterations` controls MAIN attempts; when omitted it falls back to `AI_TASKS_OPTIMIZER_MAX_ITERATIONS` or the package default. Per-invocation `args.functionId` always wins over catalog metadata and code defaults.
|
|
122
|
+
|
|
123
|
+
`execution-strategy` catalog items are structured metadata, not an open behavior plug-in. Safe runtime consumption is currently limited to fields explicitly marked in `safeRuntimeFields`, such as wrapper `defaultFunctionId`, and only when the catalog row matches the already-validated strategy key, phase, request shape, and generic FuncX envelope.
|
|
124
|
+
|
|
125
|
+
Legacy monolith id **`ai-task-strategies`** is no longer written by this repo; use the catalogs above.
|
|
126
|
+
|
|
127
|
+
### Publishing Firestore metadata
|
|
128
|
+
|
|
129
|
+
From a clone of this repo, with Firebase env in `.env` as required by **`createCataloxFromEnv()`** (see **`@exellix/ai-skills`**): **`FIREBASE_PROJECT_ID`** (required), **`GOOGLE_SERVICE_ACCOUNT_BASE64`** (required: service account JSON, base64-encoded), and optional **`FIRESTORE_DATABASE_ID`**:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
npm run publish:task-strategies
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
That provisions **`apps/ai-tasks`**, all six native catalogs, bindings, descriptors, and canonical items.
|
|
136
|
+
|
|
137
|
+
### Reading catalogs and items (from `@exellix/ai-tasks`)
|
|
138
|
+
|
|
139
|
+
Construct a **`Catalox`** instance (for example `createCataloxFromEnv()` from `@exellix/ai-tasks`, which re-exports it from `@woroces/ai-skills`), then use any of:
|
|
140
|
+
|
|
141
|
+
| Export | Purpose |
|
|
142
|
+
|--------|---------|
|
|
143
|
+
| **`defaultAiTasksCataloxContext()`** | Build `CataloxContext` with `appId: "ai-tasks"`. |
|
|
144
|
+
| **`listAiTasksAppCatalogs(catalox, context?)`** | Discovery list: catalog id, label, access, source mode. |
|
|
145
|
+
| **`getAiTasksAppCatalogBootstrap(catalox, context?)`** | All **descriptors** visible to the app (identity, query fields, capabilities). |
|
|
146
|
+
| **`getAiTasksTaskStrategiesCatalogDescriptor(catalox, catalogId, context?)`** | One catalog’s **descriptor** (any of the six catalog ids above). |
|
|
147
|
+
| **`listPreCoreTaskStrategies` / `listPostCoreTaskStrategies`** | **Normalized** rows for pre / post synthesis–audit catalogs. |
|
|
148
|
+
| **`listInputStrategies` / `listExecutionStrategyCatalogItems` / `listExecutionStrategies` / `listMainExecutionWrappers` / `listNarrixModes`** | **Normalized** rows for input, preferred execution metadata, compatibility execution catalogs, MAIN wrappers, and Narrix mode catalogs. |
|
|
149
|
+
| **`getAiTasksTaskStrategiesCatalogSnapshot(catalox, context?, query?)`** | **All seven** task-strategy catalogs in one round-trip (`pre`, `post`, `input`, `executionStrategy`, `execution`, `mainExecutionWrappers`, `narrixModes`). |
|
|
150
|
+
|
|
151
|
+
Raw rows (full `UnifiedCatalogItem`) if you need them:
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
import {
|
|
155
|
+
createCataloxFromEnv,
|
|
156
|
+
defaultAiTasksCataloxContext,
|
|
157
|
+
AI_TASK_PRE_STRATEGIES_CATALOG_ID,
|
|
158
|
+
} from "@exellix/ai-tasks";
|
|
159
|
+
|
|
160
|
+
const catalox = createCataloxFromEnv();
|
|
161
|
+
const ctx = defaultAiTasksCataloxContext();
|
|
162
|
+
const { listOutcome, items } = await catalox.listCatalogItems(
|
|
163
|
+
ctx,
|
|
164
|
+
AI_TASK_PRE_STRATEGIES_CATALOG_ID,
|
|
165
|
+
{ limit: 100 }
|
|
166
|
+
);
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Skill templates (`ai-skills` app)
|
|
170
|
+
|
|
171
|
+
Skill definitions and template bodies for execution live under Catalox **`appId: ai-skills`** (owned by **`@woroces/ai-skills`**). This package re-exports the Catalox skill surface from **`@woroces/ai-skills`** via the same entrypoint (e.g. `listAiSkillsCatalogItems`, `fetchSkillTemplatesFromCatalox`, `createCataloxFromEnv`, `initFirebaseAdminFromEnv`, …) so you can **view and edit skill catalog items** while depending only on **`@exellix/ai-tasks`**.
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## Usage
|
|
176
|
+
|
|
177
|
+
### Packaged skills (default client)
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
import {
|
|
181
|
+
createDefaultWorexClientTasks,
|
|
182
|
+
ExecutionType,
|
|
183
|
+
} from "@exellix/ai-tasks";
|
|
184
|
+
|
|
185
|
+
const client = createDefaultWorexClientTasks();
|
|
186
|
+
// Optional before first runTask if nx-content needs time to connect:
|
|
187
|
+
await client.whenReady({ timeoutMs: 60_000 });
|
|
188
|
+
|
|
189
|
+
const result = await client.runTask({
|
|
190
|
+
skillKey: "tasks/your-skill",
|
|
191
|
+
agentId: "my-agent",
|
|
192
|
+
jobTypeId: "my-job-type",
|
|
193
|
+
taskTypeId: "my-task-type",
|
|
194
|
+
executionStrategies: [],
|
|
195
|
+
executionType: ExecutionType.DIRECT,
|
|
196
|
+
input: { question: "..." },
|
|
197
|
+
jobContext: { graphRunId: "..." },
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
await client.dispose();
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## Utility tasks (non-question tasks)
|
|
206
|
+
|
|
207
|
+
ai-tasks supports **utility tasks**: tasks that run from **structured inputs** and **memory/context** to produce **structured, machine-consumable output**, without requiring a user-facing question.
|
|
208
|
+
|
|
209
|
+
- **Contract**: `RunTaskRequest.input` can be any JSON-compatible object (or string). **`input.question` is optional.**
|
|
210
|
+
- **Correlation (required):** every `runTask` request must include non-empty **`agentId`**, **`jobTypeId`**, and **`taskTypeId`** (same as `@exellix/ai-skills`). See [`RUNTASK_REQUEST.md`](RUNTASK_REQUEST.md) (including **Graph task node → RunTaskRequest** for authoring vs invoke).
|
|
211
|
+
- **When question matters**: Only skills/templates that *explicitly* reference `{{question}}` (or rely on question-driven synthesis) need a question. Utility tasks should not.
|
|
212
|
+
- **Output**: Utility tasks should typically return a structured `parsed` payload (for downstream graph nodes), not only prose in `rawText`.
|
|
213
|
+
|
|
214
|
+
Example (utility task with no question):
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
import { runTask } from "@exellix/ai-tasks";
|
|
218
|
+
|
|
219
|
+
const res = await runTask({
|
|
220
|
+
agentId: "my-agent",
|
|
221
|
+
jobTypeId: "prepare-context-job",
|
|
222
|
+
taskTypeId: "prepare-context-task",
|
|
223
|
+
skillKey: "skills/graph.prepareContext",
|
|
224
|
+
input: {
|
|
225
|
+
record: { /* ... */ },
|
|
226
|
+
policy: { /* ... */ },
|
|
227
|
+
},
|
|
228
|
+
executionMemory: {},
|
|
229
|
+
jobMemory: {},
|
|
230
|
+
graphId: "graph-123",
|
|
231
|
+
nodeId: "prepare-context",
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
console.log(res.parsed);
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Example (utility task as a local deterministic handler):
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
import { runTask } from "@exellix/ai-tasks";
|
|
241
|
+
|
|
242
|
+
const res = await runTask({
|
|
243
|
+
skillKey: "skills/skill.local:validateInput",
|
|
244
|
+
input: {
|
|
245
|
+
recordsPath: { $path: "executionMemory.inputs.records" },
|
|
246
|
+
rules: { requirePaths: ["id"] },
|
|
247
|
+
},
|
|
248
|
+
executionMemory: { inputs: { records: [{ id: "a" }] } },
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
console.log(res.parsed); // { ok: true, meta: ... } (example shape)
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### Structured decision output (`response.parsed`) + optional schema enforcement
|
|
255
|
+
|
|
256
|
+
For decision/utility nodes, prefer returning a stable object payload in `response.parsed` so graph `outputMapping` can reference fields deterministically (no markdown parsing).
|
|
257
|
+
|
|
258
|
+
Example decision task (built-in local handler):
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
import { runTask } from "@exellix/ai-tasks";
|
|
262
|
+
|
|
263
|
+
const res = await runTask({
|
|
264
|
+
skillKey: "skills/graph.decide-web-scope",
|
|
265
|
+
input: { localEvidenceSufficient: true },
|
|
266
|
+
taskKind: "decision",
|
|
267
|
+
outputValidation: {
|
|
268
|
+
schema: {
|
|
269
|
+
type: "object",
|
|
270
|
+
required: ["contractVersion", "shouldUseWeb", "reasonCodes", "missingSignals"],
|
|
271
|
+
properties: {
|
|
272
|
+
contractVersion: { type: "string" },
|
|
273
|
+
shouldUseWeb: { type: "boolean" },
|
|
274
|
+
reasonCodes: { type: "array", items: { type: "string" } },
|
|
275
|
+
missingSignals: { type: "array", items: { type: "string" } },
|
|
276
|
+
},
|
|
277
|
+
additionalProperties: false,
|
|
278
|
+
},
|
|
279
|
+
mode: "fail",
|
|
280
|
+
},
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
// Stable structured artifact for deterministic graph mapping:
|
|
284
|
+
// - res.parsed.shouldUseWeb
|
|
285
|
+
// - res.parsed.reasonCodes
|
|
286
|
+
// - res.parsed.missingSignals
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
`getPackagedAiTasksMetadataDir()` returns the absolute path to the shipped `.metadata` folder (override with `contentRegistryLocalPath` in client options). `forPackagedSkills` is an alias of `createDefaultWorexClientTasks`.
|
|
290
|
+
|
|
291
|
+
### Class-Based API (custom `ai-skills` client)
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
import { WorexClientSkills } from "@exellix/ai-tasks"; // re-export, or import from @woroces/ai-skills
|
|
295
|
+
import { WorexClientTasks, ExecutionType } from "@exellix/ai-tasks";
|
|
296
|
+
|
|
297
|
+
// skills client is the shared execution + helper layer
|
|
298
|
+
const skills = new WorexClientSkills({
|
|
299
|
+
// ... your existing ai-skills config (gateway/provider/router)
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
// Get executor from skills client (no-enrichment execute primitive)
|
|
303
|
+
const executor = skills.getExecutor();
|
|
304
|
+
|
|
305
|
+
// tasks client orchestrates task-level execution
|
|
306
|
+
const tasks = new WorexClientTasks(skills, executor);
|
|
307
|
+
|
|
308
|
+
const res = await tasks.runTask({
|
|
309
|
+
skillKey: "tasks/security-risk-summary",
|
|
310
|
+
// executionType is optional, defaults to ExecutionType.DIRECT
|
|
311
|
+
input: { assetId: "a-123", windowDays: 30 },
|
|
312
|
+
|
|
313
|
+
// optional
|
|
314
|
+
variables: { orgName: "Acme" },
|
|
315
|
+
modelConfig: {
|
|
316
|
+
model: "gpt-5",
|
|
317
|
+
temperature: 0.7,
|
|
318
|
+
maxTokens: 2000
|
|
319
|
+
},
|
|
320
|
+
jobId: "job-1",
|
|
321
|
+
agentId: "agent-1",
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
console.log(res.rawContent);
|
|
325
|
+
console.log(res.parsed); // if your pipeline supports parsed output
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### Function-Based API (Convenience)
|
|
329
|
+
|
|
330
|
+
```typescript
|
|
331
|
+
import { runTask, ExecutionType } from "@exellix/ai-tasks";
|
|
332
|
+
|
|
333
|
+
// Automatically initializes SDK (ERC mode)
|
|
334
|
+
// executionType is optional, defaults to ExecutionType.DIRECT
|
|
335
|
+
const res = await runTask({
|
|
336
|
+
skillKey: "tasks/security-risk-summary",
|
|
337
|
+
agentId: "agent-1",
|
|
338
|
+
jobTypeId: "security-job-type",
|
|
339
|
+
taskTypeId: "security-task-type",
|
|
340
|
+
executionStrategies: [],
|
|
341
|
+
input: { assetId: "a-123" },
|
|
342
|
+
jobId: "job-1",
|
|
343
|
+
});
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
## RunTask Algorithm (Canonical)
|
|
349
|
+
|
|
350
|
+
Given a request, `runTask()` performs:
|
|
351
|
+
|
|
352
|
+
1. **Validate**: Non-empty **`agentId`**, **`jobTypeId`**, **`taskTypeId`**; required **`executionStrategies`** array (use **`[]`** for plain MAIN). When **`smartInput`** is present, validate shape (**`paths`**: array of **non-empty strings** *or* **`{ title, path, required? }`** entries; reject `{}`, non-arrays, invalid elements, unknown **root** keys—only **`paths`** is allowed). Paths under **`xynthesized.*`** must use scope **`job`**, **`task`**, or **`execution`**.
|
|
353
|
+
1b. **Compile `taskConfiguration`** (when **`RunTaskRequest.taskConfiguration`** is set): map **`aiTaskStrategies.pre: "synthesis"`** and/or **`aiTaskProfile.inputSynthesis.enabled`** into **`executionPipeline`** PRE **`synthesized-context`** + **`includeContextInPrompt: true`**. Strip **`taskConfiguration`** before execution. See **`compileTaskConfigurationOnRunTaskRequest`**. **Graph-engine** must forward the node blob and wire runtime input — [`reports/graph-engine-task-pre-synthesis-compile.md`](reports/graph-engine-task-pre-synthesis-compile.md).
|
|
354
|
+
2. **NARRIX pre-processor** (if `request.narrix` is set): Resolve raw record (see [NARRIX task-level pre-processor](#narrix-task-level-pre-processor)), run NARRIX (to-CNI + engine), build `_narrix` attachment, set `executionMemory[attachToField]` and `jobMemory._narrix`. Continue with the updated request.
|
|
355
|
+
3. **Structured Narrix** (`narrixMode: "handler"`): Resolve **`narrixInput`**, run handler, merge into **`taskMemory.narrix`** / **`input`** as implemented; handler **`ctx`** includes **`xynthesized`** and **`smartInput`**.
|
|
356
|
+
4. **Local task dispatch**: If `getLocalTask(skillKey)` returns a handler, call `{ input, ctx }`. **`ctx`** includes **`skillKey`**, **`jobMemory`**, **`taskMemory`**, **`executionMemory`**, **`variables`**, **`xynthesized`**, **`smartInput`**, and graph/correlation ids. Returns **`RunTaskResponse`**; **skips LLM MAIN path below**.
|
|
357
|
+
5. **LLM path** (pipeline or default MAIN): If **`executionPipeline`** is non-empty, run PRE (**`synthesized-context`** only), then MAIN, then optional POST (**`audit`** / **`polish`**). PRE synthesis may write **`request.xynthesized`** and **`response.xynthesizedPatch`** ([details](#execution-pipeline-and-synthesized-context-pre-step)). Otherwise behave as a single MAIN **`direct`** step.
|
|
358
|
+
6. **MAIN execution** (inside pipeline MAIN or default path): Build **`memoryBundle = { jobMemory, taskMemory, executionMemory }`**, **`enrichMemoriesWithScoping(skillKey, 'task', bundle)`**, generate **`context`** when **`includeContextInPrompt`** (or pipeline synthesis overrides context). Optionally run **`aiScoping`** into **`input.aiScoped`**. Build **`enrichedInput`**: memories, **`context`**, **`input`**, top-level **`xynthesized`** and **`smartInput`**, and **`variables` = passthroughJobTemplateVariables(request.variables)` (job bucket only; see [Gateway template rendering](#gateway-template-rendering-v4)).
|
|
359
|
+
7. **Execute MAIN**: Apply **`executionStrategies`** (planner/optimizer FuncX wrappers when non-empty) and call **`runSkill`** / gateway via the configured executor.
|
|
360
|
+
8. **Finalize response**: Identity / task metadata as today; attach **`xynthesizedPatch`** when PRE synthesis produced **`job`**, **`task`**, or **`execution`** writes; when the executor returns **`SmartInputRenderResult`**, lift it to **`response.smartInputRenderResult`** and **`metadata.smartInputRenderResult`** (also accepts shaped **`metadata.smartInput`** from downstream).
|
|
361
|
+
9. **Not-found**: Executor may invoke registry diagnostics when content is missing.
|
|
362
|
+
|
|
363
|
+
### Trace mode (authoritative ordered debug trace)
|
|
364
|
+
|
|
365
|
+
Set `executionMode: "trace"` to opt in to an authoritative, ordered per-step execution trace. When enabled, the response includes:
|
|
366
|
+
|
|
367
|
+
- `debugTrace.tasks: DebugTraceTask[]`
|
|
368
|
+
|
|
369
|
+
Each entry represents a real unit of work that executed (pre steps, main skills/gateway call, post steps) and includes:
|
|
370
|
+
|
|
371
|
+
- `taskType`: `"pre-execution" | "ai-task" | "post-execution"`
|
|
372
|
+
- `details`: short human string
|
|
373
|
+
- `modelUsed`: string for LLM-backed steps; `null` for deterministic steps
|
|
374
|
+
- `metadata.timing`: `{ startedAt, endedAt, durationMs }`
|
|
375
|
+
- `metadata.step`: `{ phase, type, stepId }` (when applicable)
|
|
376
|
+
|
|
377
|
+
When upstream providers expose them (via `@exellix/xynthesis` / `@exellix/ai-skills` trace surfaces), trace tasks may also include usage/routing/cost under stable `metadata` keys.
|
|
378
|
+
|
|
379
|
+
**Graph / smart-input hints (ai-tasks–specific):** Trace entries for MAIN **direct** execution include **`metadata.smartInput: { paths }`** (echo of **`RunTaskRequest.smartInput.paths`** after normalization—**`{ title, path }[]`**, or empty array). PRE **`synthesized-context`** entries may include **`metadata.xynthesized`** (`inputPathsUsed`, `outputDestination`, `outputKey`, `patchKeys`) when **`SynthesisConfig.xynthesizedOutput`** is set—keys only, not full synthesized payloads.
|
|
380
|
+
|
|
381
|
+
---
|
|
382
|
+
|
|
383
|
+
## Xynthesized memory and smart input
|
|
384
|
+
|
|
385
|
+
Graph runtimes need structured execution context that is **not** the same as raw **`jobMemory` / `taskMemory` / `executionMemory`**: material synthesized once at job scope, scoped to a single node, or held in a run-wide **execution** bucket. **`RunTaskRequest`** supports:
|
|
386
|
+
|
|
387
|
+
| Field | Purpose |
|
|
388
|
+
|-------|---------|
|
|
389
|
+
| **`xynthesized?: { job?, task?, execution? }`** | **`job`** — synthesized context reusable across nodes in the same graph run (graph-engine merges patches from each **`runTask`**). **`task`** — scoped to this task/node invocation only. **`execution`** — run-wide execution bucket (distinct from job/task). Values are **`Record<string, unknown>`** buckets keyed by your **`outputKey`** (and any caller-supplied keys). |
|
|
390
|
+
| **`smartInput?: RunTaskSmartInput`** | Declares smart-input paths for Rendrix **`{{smartInput}}`** (via **`@exellix/ai-skills`** / gateway). Use Rendrix-native **`paths: { title, path, required? }[]`**, or legacy **`paths: string[]`** (normalized before **`runSkill`**). Paths under **`xynthesized.*`** must use **`job`**, **`task`**, or **`execution`** (e.g. **`xynthesized.execution.priorAnalysis`**). The **`smartInput`** object allows only the **`paths`** property at the root. Optional **`smartInputRenderOptions`** on **`RunTaskRequest`** controls markdown folding for the macro (same merge rules as **`RunSkillRequest`**). Full dot-path resolution against live memory happens downstream (Rendrix / gateway). |
|
|
391
|
+
|
|
392
|
+
### Validation (`smartInput`)
|
|
393
|
+
|
|
394
|
+
If **`smartInput`** is present, it must be a plain object with **`paths`** (array of non-empty strings or **`{ title, path, required? }`**) and optional **`strict`** (boolean). Invalid: **`smartInput: {}`**, bad **`paths`**, extra root keys, or **`xynthesized.<scope>.*`** with **`<scope>`** not **`job`**, **`task`**, or **`execution`**. Prefer path prefixes **`jobVariables.*`** and **`taskVariables.*`** (legacy **`variables.*`** ≡ job scope when the engine resolves paths). **`{ paths: [] }`** is valid.
|
|
395
|
+
|
|
396
|
+
At **`runTask()`** entry, invalid shape throws **`SmartInputValidationError`** (`error.code`, `error.details`, `error.skillKey`) — the task does not run (no NARRIX, no PRE xynthesis, no MAIN). Omitted **`smartInput`** is allowed.
|
|
397
|
+
|
|
398
|
+
### Pre-flight validation (no LLM invoke)
|
|
399
|
+
|
|
400
|
+
Use these helpers **before** `runTask()` to inspect config and payload without calling xynthesis or the gateway:
|
|
401
|
+
|
|
402
|
+
| Function | Purpose |
|
|
403
|
+
|----------|---------|
|
|
404
|
+
| **`validateRunTaskConfig(request)`** | Static checks: `agentId` / `jobTypeId` / `taskTypeId`, `executionStrategies`, `smartInput` shape, `modelConfig` / `llmCall` numeric fields, `executionPipeline` (one MAIN, synthesis PRE context flag). Returns `{ ok, issues, errors, warnings }`. |
|
|
405
|
+
| **`validateRunTaskInvoke({ request, templateResolver?, ... })`** | Config checks **plus** whether expected paths resolve on the request (`input`, memories, `xynthesized`). Uses Rendrix `renderSmartInput` for required smart-input paths and optional `analyzeTemplateResolution` on raw templates. |
|
|
406
|
+
| **`analyzeExpectedRunTaskInput({ skillKey, smartInput?, instructions?, prompt?, templateResolver? })`** | Returns expected dot-paths from **`smartInput.paths`** and path tokens in skill templates (Rendrix **`listTokens`**). |
|
|
407
|
+
|
|
408
|
+
```typescript
|
|
409
|
+
import {
|
|
410
|
+
validateRunTaskConfig,
|
|
411
|
+
validateRunTaskInvoke,
|
|
412
|
+
analyzeExpectedRunTaskInput,
|
|
413
|
+
SmartInputValidationError,
|
|
414
|
+
isSmartInputValidationError,
|
|
415
|
+
} from "@exellix/ai-tasks";
|
|
416
|
+
|
|
417
|
+
// Config only (fast)
|
|
418
|
+
const configCheck = validateRunTaskConfig(runTaskRequest);
|
|
419
|
+
if (!configCheck.ok) {
|
|
420
|
+
for (const issue of configCheck.errors) {
|
|
421
|
+
console.error(issue.code, issue.path, issue.message);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// Config + payload + templates
|
|
426
|
+
const invokeCheck = await validateRunTaskInvoke({
|
|
427
|
+
request: runTaskRequest,
|
|
428
|
+
templateResolver: {
|
|
429
|
+
async resolveRawTemplate(skillKey, section) {
|
|
430
|
+
const r = await skillsClient.resolveRawTemplate(skillKey, section);
|
|
431
|
+
return r?.found ? r.content : undefined;
|
|
432
|
+
},
|
|
433
|
+
},
|
|
434
|
+
checkTemplateResolution: true,
|
|
435
|
+
});
|
|
436
|
+
if (!invokeCheck.ok) {
|
|
437
|
+
console.error(invokeCheck.errors);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// What paths does this node need?
|
|
441
|
+
const expected = await analyzeExpectedRunTaskInput({
|
|
442
|
+
skillKey: runTaskRequest.skillKey,
|
|
443
|
+
smartInput: runTaskRequest.smartInput,
|
|
444
|
+
prompt: "...",
|
|
445
|
+
});
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
Each issue has **`code`**, **`severity`** (`error` \| `warning`), **`message`**, and optional **`path`** (e.g. `smartInput.paths[0].path`, `llmCall.maxTokensCap`). See [`.docs/flow-io/`](.docs/flow-io/flow-README.md) for flow-level wiring.
|
|
449
|
+
|
|
450
|
+
### Skill request analysis (`@exellix/ai-skills` 5.6+)
|
|
451
|
+
|
|
452
|
+
**`@exellix/ai-tasks`** re-exports the Catalox-backed analysis and template-catalog helpers from **`@exellix/ai-skills`** so apps can depend on a single package:
|
|
453
|
+
|
|
454
|
+
| Export | Purpose |
|
|
455
|
+
|--------|---------|
|
|
456
|
+
| **`analyzeSkillRequest`**, **`buildSkillRequestAnalysisPacket`**, **`formatSkillRequestAnalysisMarkdown`** | Deterministic preflight for a flat **`RunSkillRequest`** (templates, smart input, output contract, gateway packet preview). |
|
|
457
|
+
| **`analyzeRunTaskRequest(catalox, request, options?)`** | Same analysis on **`pickRunSkillRequestFields(request)`**, plus optional merge of **`validateRunTaskConfig`** findings (`includeAiTasksConfigValidation`, default `true`). |
|
|
458
|
+
| **`extractTokenNamesFromStrings`**, **`getSkillTokens`**, **`getSkillContent`**, **`modifySkillContent`**, **`createSubSkill`**, **`deleteSubSkill`**, **`SKILL_TEMPLATE_ROLES_ORDER`** | Template catalog introspection and edits (Catalox). |
|
|
459
|
+
| **`runSkillRequestFromFlat`**, **`resolveAiEngineIdForRunSkill`**, **`SkillExecutionTraceError`**, trace helpers | Flat skill execution and trace utilities. |
|
|
460
|
+
|
|
461
|
+
Rendrix helpers used by validation are also re-exported: **`listTokens`**, **`analyzeTemplateResolution`**, **`renderSmartInput`**, **`SmartInputInvalidError`**, **`TemplateResolutionError`**, and related types.
|
|
462
|
+
|
|
463
|
+
```typescript
|
|
464
|
+
import {
|
|
465
|
+
analyzeRunTaskRequest,
|
|
466
|
+
formatSkillRequestAnalysisMarkdown,
|
|
467
|
+
buildSkillRequestAnalysisPacket,
|
|
468
|
+
} from "@exellix/ai-tasks";
|
|
469
|
+
|
|
470
|
+
const analysis = await analyzeRunTaskRequest(catalox, runTaskRequest, {
|
|
471
|
+
logger,
|
|
472
|
+
includeAiTasksConfigValidation: true,
|
|
473
|
+
});
|
|
474
|
+
console.log(formatSkillRequestAnalysisMarkdown(analysis.report));
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
### Runtime surfaces
|
|
478
|
+
|
|
479
|
+
- **Narrix preprocessor / Narrix handler `ctx`**: Same **`xynthesized`** / **`smartInput`** as on the **`runTask`** request after normalization (pipeline PRE runs later and can mutate **`xynthesized`** before MAIN).
|
|
480
|
+
- **PRE `synthesized-context`**: The bundle passed into template/rendering includes **`xynthesized`** and **`smartInput`** so synthesis prompts can reference them consistently with MAIN.
|
|
481
|
+
- **MAIN executor payload**: Top-level **`xynthesized`**, **`smartInput`** (normalized), and **`smartInputRenderOptions`** (when set) are forwarded on the object passed to **`runSkill`** (alongside memories and **`context`**).
|
|
482
|
+
- **Template variables**: **`variables`** (job bucket) and **`executionMemory.jobVariables`** / **`executionMemory.taskVariables`** are forwarded separately; use **`{{xynthesized…}}`**, **`{{smartInput}}`**, **`jobVariables.*`**, and **`taskVariables.*`** in templates (not a single flattened bag).
|
|
483
|
+
- **Response**: When downstream returns a **`SmartInputRenderResult`**, **`runTask`** **`finalize`** exposes **`response.smartInputRenderResult`** and mirrors **`metadata.smartInputRenderResult`** (also reads **`metadata.smartInput`** when it matches that shape).
|
|
484
|
+
|
|
485
|
+
### PRE synthesis destination (`SynthesisConfig.xynthesizedOutput`)
|
|
486
|
+
|
|
487
|
+
Optional on the **`synthesized-context`** step **`config`**:
|
|
488
|
+
|
|
489
|
+
- **`destination`**: **`"job"`** | **`"task"`** | **`"execution"`** — writes under **`request.xynthesized.job[outputKey]`**, **`.task[outputKey]`**, or **`.execution[outputKey]`** (no redirect to other scopes).
|
|
490
|
+
- **`outputKey`**: string key for the synthesized value.
|
|
491
|
+
- **`mode`**: **`"replace"`** (default) or **`"merge"`** — for merge, if the existing value at **`outputKey`** and the new value are both plain objects, ai-tasks **deep-merges** them; otherwise the new value replaces.
|
|
492
|
+
- **`alsoWriteLegacySynthesizedContext`**: When **`true`** or omitted, keep today’s behavior: persist structured/markdown artifacts on **`executionMemory.synthesizedContext`** (and **`synthesizedInput`** when applicable) for MAIN. When **`false`**, skip that legacy persistence for this PRE step; MAIN still receives synthesized **context markdown** from the PRE step when produced.
|
|
493
|
+
|
|
494
|
+
**Stored value:** Prefer the full synthesis **`artifact`** when present (structured / question-driven / markdown artifact shape). If there is no artifact, ai-tasks stores **`{ contextMarkdown }`** using the PRE step’s markdown string.
|
|
495
|
+
|
|
496
|
+
**Response:** **`RunTaskResponse.xynthesizedPatch`** has the same **`{ job?, task?, execution? }`** shape so the graph-engine can merge into durable graph memory. Omitted when nothing was written.
|
|
497
|
+
|
|
498
|
+
**Execution-scope example (PRE + smart-input):**
|
|
499
|
+
|
|
500
|
+
```typescript
|
|
501
|
+
executionPipeline: [
|
|
502
|
+
{
|
|
503
|
+
phase: "pre",
|
|
504
|
+
type: "synthesized-context",
|
|
505
|
+
config: {
|
|
506
|
+
xynthesizedOutput: { destination: "execution", outputKey: "priorAnalysis" },
|
|
507
|
+
},
|
|
508
|
+
},
|
|
509
|
+
{ phase: "main", type: "direct" },
|
|
510
|
+
],
|
|
511
|
+
smartInput: { paths: ["xynthesized.execution.priorAnalysis"] },
|
|
512
|
+
// response.xynthesizedPatch.execution.priorAnalysis → graph-engine merges into executionMemory.xynthesized.execution
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
### Builder
|
|
516
|
+
|
|
517
|
+
**`TaskRequestBuilder`**: **`withXynthesized`**, **`withSmartInput`**, **`withSmartInputPaths`** (builds native **`{ title, path }`** entries), **`withSmartInputRenderOptions`**, **`withXynthesizedJob`**, **`withXynthesizedTask`**, **`withXynthesizedExecution`**.
|
|
518
|
+
|
|
519
|
+
### What ai-tasks does **not** do
|
|
520
|
+
|
|
521
|
+
Does **not** choose graph mappings, apply **`outputMapping`**, run the full graph, or own long-lived graph state—that belongs to the graph runtime. It **allowlists** **`xynthesized.*`** smart-input scopes (**`job`**, **`task`**, **`execution`**) but does **not** render **`{{smartInput}}`** or resolve paths against live memory (Rendrix / gateway).
|
|
522
|
+
|
|
523
|
+
---
|
|
524
|
+
|
|
525
|
+
## Local Tasks
|
|
526
|
+
|
|
527
|
+
Local tasks are **deterministic handlers** (no LLM) that run inside `runTask()` before any memory enrichment or executor call. They are identified by **skillKey**. If a handler is registered for that `skillKey`, it runs with the task input and context; the return value is wrapped into a standard `RunTaskResponse` (e.g. `parsed`, `metadata.durationMs`, `metadata.localSkillKey`).
|
|
528
|
+
|
|
529
|
+
### Built-in local tasks
|
|
530
|
+
|
|
531
|
+
| Skill key | Purpose |
|
|
532
|
+
|--------|--------|
|
|
533
|
+
| `skills/node.callExport` | Dynamically import a module, resolve an export (default or named), and call it. Supports `$path`-based argument resolution (e.g. `input.payload`, `jobMemory.foo`), optional job/global caching for instances. |
|
|
534
|
+
| `skills/node.callExportBatch` | Same module/export/cache semantics; creates or reuses a cached instance, then calls a **batch** method (e.g. `enrichJson`) with payload/options. Returns `{ ok, value, meta: { processed, errors, durationMs } }`. |
|
|
535
|
+
| `skills/skill.local:validateInput` | Validate records at a `$path` (e.g. `executionMemory.inputs.vulnInstances.records`). Rules: `requirePaths`, `graphTypeEquals`. No mutation; returns `{ ok, meta: { total, valid, invalid } }` or `{ ok: false, errors }`. |
|
|
536
|
+
| `skills/skill.local:normalizeNarrixResult` | Normalize records to a stable “Narrix attachment” shape: ensure `_narrix.meta.entity.entityKey`, `_narrix.scoping` / `_narrix.discovery` and their arrays, `joinCandidates`. Returns `{ ok: true, value: { records } }`. |
|
|
537
|
+
| `skills/skill.local:narrixRun` | Run one input through the Narrix pipeline (ingest → runner) for **record**, **text**, **docs**, or **chat**. Unified input: `medium` (`"record"` \| `"text"` \| `"docs"` \| `"chat"`) + `datasetId` + medium-specific payload. Returns `{ ok: true, entity, signals, stories, passes?, meta }` or `{ ok: false, error, message?, meta? }`. Legacy record shape (no `medium`) still supported. |
|
|
538
|
+
| `skills/graph.collectEvidence` | **Opt-in reference local handler**: collect evidence from structured requests (queries and/or URLs) and return a reusable **EvidenceBundle** for downstream graph nodes. No question required. |
|
|
539
|
+
|
|
540
|
+
---
|
|
541
|
+
|
|
542
|
+
## Generic evidence collection (`skills/graph.collectEvidence`)
|
|
543
|
+
|
|
544
|
+
ai-tasks standardizes a **generic, domain-agnostic** evidence collection contract for staged graph execution. This is **independent of NARRIX** question-driven web scoping: callers provide structured requests; the result is a reusable `EvidenceBundle` suitable for storing in `executionMemory` and reusing downstream.
|
|
545
|
+
|
|
546
|
+
- **Must-have**: a stable input/output contract graphs can depend on cleanly.
|
|
547
|
+
- **Reference handler**: ai-tasks ships an **opt-in local task** (`skills/graph.collectEvidence`) that implements the contract to reduce integration friction. Retrieval is **not** implicit; graphs opt in by selecting this `skillKey`.
|
|
548
|
+
- **Backends**: the reference handler may reuse existing internal search/fetch components, but the **public contract does not expose NARRIX semantics** (`narrix`, `webContext`, web-scope templates, etc.).
|
|
549
|
+
- **Types**: see `EvidenceCollectionInput` / `EvidenceBundle` exported from `@exellix/ai-tasks` (implemented in `src/types/evidence-types.ts`).
|
|
550
|
+
- **Current reference implementation notes**:
|
|
551
|
+
- Supports **query-driven discovery** and **direct URL fetch**.
|
|
552
|
+
- Enforces policy limits like `maxRequests`, `maxSourcesPerRequest`, `maxTotalSources`, domain allow/block lists, and snippet size caps.
|
|
553
|
+
- Returns `value.sources[]` + per-request `value.requests[]`. If `extraction.returnFacts` / `extraction.returnSummary` are set, the reference handler currently returns empty `facts: []` / `summary: ""` (placeholders for future variants).
|
|
554
|
+
|
|
555
|
+
Recommended graph pattern:
|
|
556
|
+
|
|
557
|
+
```typescript
|
|
558
|
+
const evidenceRes = await runTask({
|
|
559
|
+
skillKey: "skills/graph.collectEvidence",
|
|
560
|
+
input: {
|
|
561
|
+
requests: [
|
|
562
|
+
{ kind: "vendor_guidance", query: "ExampleProduct security advisory" },
|
|
563
|
+
{ kind: "exploitation_status", url: "https://example.com/advisory" },
|
|
564
|
+
],
|
|
565
|
+
policy: { maxTotalSources: 10, includeSnippets: true },
|
|
566
|
+
},
|
|
567
|
+
executionMemory,
|
|
568
|
+
jobMemory,
|
|
569
|
+
graphId,
|
|
570
|
+
nodeId,
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
// Store once, reuse later:
|
|
574
|
+
executionMemory.evidence = evidenceRes.parsed; // EvidenceBundle
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
### node.callExport (input contract)
|
|
578
|
+
|
|
579
|
+
- `module`: string (module name or path for dynamic `import()`)
|
|
580
|
+
- `export`: string (default `"default"`) — export name
|
|
581
|
+
- `call`: `{ method: string, args?: any[] }` — `method` can be `"function"` (call export as function), `"create"` (call and optionally cache), or any other string (call as method on cached/created instance)
|
|
582
|
+
- `cache`: optional `{ scope: "global" | "job", key: string }`
|
|
583
|
+
- Args may use `{ $path: "input.x" }` (or `jobMemory.*`, `taskMemory.*`, `executionMemory.*`, `variables.*`); resolved via dot-path before calling.
|
|
584
|
+
|
|
585
|
+
### node.callExportBatch (input contract)
|
|
586
|
+
|
|
587
|
+
- `module`, `export`, `cache`: same as above
|
|
588
|
+
- `call.create`: `{ method, args? }` — used to create/cache the instance
|
|
589
|
+
- `call.batch`: `{ method, args? }` — method and args for the batch call (args can use `$path`)
|
|
590
|
+
- `payload`, `options`: typically passed via `$path` in `call.batch.args` (e.g. `input.payload`, `input.options`)
|
|
591
|
+
|
|
592
|
+
### skills/skill.local:validateInput (input contract)
|
|
593
|
+
|
|
594
|
+
- `recordsPath`: `{ $path: "..." }` — path to records array (e.g. `executionMemory.inputs.vulnInstances.records`)
|
|
595
|
+
- `rules.requirePaths`: string[] — dot paths that must exist on each record
|
|
596
|
+
- `rules.graphTypeEquals`: optional string — require `graphized.meta.graphType` to equal this value
|
|
597
|
+
- `datasetId`: optional (for validation rules)
|
|
598
|
+
|
|
599
|
+
### skills/skill.local:normalizeNarrixResult (input contract)
|
|
600
|
+
|
|
601
|
+
- `recordsPath`: `{ $path: "..." }` — path to records array
|
|
602
|
+
- `attachRoot`: string (default `"_narrix"`) — key under which Narrix attachment lives
|
|
603
|
+
- `defaults.scopingPath`, `defaults.discoveryPath`: optional paths under attachRoot (default `"scoping"`, `"discovery"`)
|
|
604
|
+
|
|
605
|
+
### skills/skill.local:narrixRun (input contract)
|
|
606
|
+
|
|
607
|
+
One handler supports **four media**: record, text, docs, chat. Send a **unified input** with `medium` and `datasetId`, plus the payload for that medium. For full object shapes and use cases, see [NARRIX-FOUR-OBJECTS-AND-USE-CASES.md](./NARRIX-FOUR-OBJECTS-AND-USE-CASES.md).
|
|
608
|
+
|
|
609
|
+
**Unified input (recommended):**
|
|
610
|
+
|
|
611
|
+
- **Record:** `{ medium: "record", datasetId: string, record: Record<string, unknown> }`
|
|
612
|
+
- **Text:** `{ medium: "text", datasetId: string, text: string, meta?: object }`
|
|
613
|
+
- **Docs:** `{ medium: "docs", datasetId: string, document: { pages: Array<{ text, pageId?, pageNumber?, ... }>, docId?, title? } }`
|
|
614
|
+
- **Chat:** `{ medium: "chat", datasetId: string, thread: { messages: Array<{ role, text, ... }>, threadId?, title?, participants? } }`
|
|
615
|
+
|
|
616
|
+
**Success response:** `{ ok: true, entity, signals, stories, passes?, meta }` — `meta` includes `datasetId`, `packId`, `entityKind` (and run metadata).
|
|
617
|
+
**Failure response:** `{ ok: false, error: string, message?: string, meta?: object }` — e.g. `NARRIX_DISABLED`, `NARRIX_INVALID_INPUT`, `RUNNER_PACK_NOT_FOUND`, or ingest/runner error codes.
|
|
618
|
+
|
|
619
|
+
**Feature flag:** Set **`USE_NARRIX_INGEST=1`** to enable the Narrix pipeline (use **`npm run test:with-narrix-ingest`** on Windows instead of prefixing the command). When unset, the handler returns `{ ok: false, error: "NARRIX_DISABLED" }` without calling ingest or runner.
|
|
620
|
+
|
|
621
|
+
**Debug logging:** Set `NARRIX_DEBUG=1` to log datasetId, medium, packId, entityKind, signal codes, and narrativeTypeIds on success (default: quiet).
|
|
622
|
+
|
|
623
|
+
See [NARRIX-STATUS-REPORT.md](./NARRIX-STATUS-REPORT.md) for implementation details and test layout.
|
|
624
|
+
|
|
625
|
+
### Custom local tasks
|
|
626
|
+
|
|
627
|
+
You can register your own handlers so they run when `skillKey` matches the registered key:
|
|
628
|
+
|
|
629
|
+
```typescript
|
|
630
|
+
import {
|
|
631
|
+
registerLocalTask,
|
|
632
|
+
getLocalTask,
|
|
633
|
+
type LocalTaskContext,
|
|
634
|
+
type LocalTaskHandler,
|
|
635
|
+
} from "@exellix/ai-tasks";
|
|
636
|
+
|
|
637
|
+
const myHandler: LocalTaskHandler = async ({ input, ctx }) => {
|
|
638
|
+
// ctx: skillKey, jobMemory, taskMemory, executionMemory, variables, jobId, agentId, graphId, nodeId, ...
|
|
639
|
+
return { ok: true, value: input.someField };
|
|
640
|
+
};
|
|
641
|
+
|
|
642
|
+
registerLocalTask("skills/my.custom.task", myHandler);
|
|
643
|
+
|
|
644
|
+
// Later: runTask({ skillKey: "skills/my.custom.task", input: { someField: 1 } }) will run myHandler and skip enrichment/executor
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
- **`registerLocalTask(skillKey: string, handler: LocalTaskHandler): void`** — register a handler for `skillKey`
|
|
648
|
+
- **`getLocalTask(skillKey: string): LocalTaskHandler | undefined`** — get handler (used internally by `runTask`)
|
|
649
|
+
- **`LocalTaskContext`** — `skillKey`, `jobMemory`, `taskMemory`, `executionMemory`, `variables`, `xynthesized`, `smartInput`, `jobId`, `taskId`, `agentId`, `graphId`, `nodeId`, `prevNodeId`, `coreSkillId`, `masterSkillId`, `masterSkillActivityId`
|
|
650
|
+
- **`LocalTaskHandler`** — `(args: { input: any; ctx: LocalTaskContext }) => Promise<any>`
|
|
651
|
+
|
|
652
|
+
### Execution tracing (metadata)
|
|
653
|
+
|
|
654
|
+
For graph runtimes (e.g. worex-graph), local task responses include consistent metadata so a trace can be stored per node:
|
|
655
|
+
|
|
656
|
+
- **`metadata.durationMs`** — time taken by the handler
|
|
657
|
+
- **`metadata.localSkillKey`** — `skillKey` that was run (e.g. `"skills/node.callExportBatch"`, `"skills/skill.local:validateInput"`)
|
|
658
|
+
|
|
659
|
+
Downstream can store e.g. `executionMemory._trace.nodes[nodeId]` with `taskKey`, `ok`, `durationMs`, `summary` (from `parsed.meta` when present).
|
|
660
|
+
|
|
661
|
+
**Exported API for local tasks:** `registerLocalTask`, `getLocalTask`, `registerBuiltInLocalTasks`, `LocalTaskContext`, `LocalTaskHandler` (see [API Reference](#api-reference) and Types).
|
|
662
|
+
|
|
663
|
+
---
|
|
664
|
+
|
|
665
|
+
## ExecutionType
|
|
666
|
+
|
|
667
|
+
`executionType` is **optional** on every request and defaults to `DIRECT` if not provided.
|
|
668
|
+
|
|
669
|
+
| Way | How you trigger it | What runs |
|
|
670
|
+
|-----|--------------------|-----------|
|
|
671
|
+
| **NARRIX pre-processor** | `request.narrix` set (e.g. from graph node metadata) | Resolve record → run NARRIX → inject `_narrix` into executionMemory/jobMemory; then run local task or DIRECT as below. |
|
|
672
|
+
| **Local task** | `skillKey` = local skill key (e.g. `skills/skill.local:narrixRun`) | Handler only; no enrichment, no LLM. |
|
|
673
|
+
| **Executor only** | Any other `skillKey`, `executionType` omitted or `"direct"` | Enrich → context → executor/LLM. No Narrix. |
|
|
674
|
+
| **Narrix then execute** | Same as executor, but `executionType: "narrix-then-direct"` + `narrixInput` | Narrix → append to `taskMemory.narrix` → then enrich → context → executor/LLM. |
|
|
675
|
+
|
|
676
|
+
Currently supported:
|
|
677
|
+
|
|
678
|
+
- `DIRECT` — single execution call after task-level enrichment/context (default)
|
|
679
|
+
- `NARRIX_THEN_DIRECT` (`"narrix-then-direct"`) — run Narrix first, inject result into task memory and context, then run the same DIRECT path; **requires** `narrixInput` (or resolution from job memory via `narrixInput.$path`). See [Narrix then execute](#narrix-then-execute) below for details.
|
|
680
|
+
|
|
681
|
+
Unsupported types throw an error with actionable context.
|
|
682
|
+
|
|
683
|
+
---
|
|
684
|
+
|
|
685
|
+
## Narrix then execute
|
|
686
|
+
|
|
687
|
+
Narrix processes **four input types** (record, text, docs, chat) and supports several **use cases** (local `skills/skill.local:narrixRun`, narrix-then-direct, normalize, validate). For the four input objects, their payload shapes, and detailed use cases, see **[NARRIX-FOUR-OBJECTS-AND-USE-CASES.md](./NARRIX-FOUR-OBJECTS-AND-USE-CASES.md)**.
|
|
688
|
+
|
|
689
|
+
Use **narrix-then-direct** when you want one `runTask()` call to scope data with Narrix (entity, signals, stories) and then run an LLM skill with that context—without calling `skills/skill.local:narrixRun` and then a second `runTask()` manually.
|
|
690
|
+
|
|
691
|
+
### Request
|
|
692
|
+
|
|
693
|
+
Same as a normal executor call, plus:
|
|
694
|
+
|
|
695
|
+
- **`executionType`**: `"narrix-then-direct"` or use the exported constant `NARRIX_THEN_DIRECT`.
|
|
696
|
+
- **`narrixInput`** (required): Either:
|
|
697
|
+
- Full Narrix input: `{ medium, datasetId, record }` (or `text` / `document` / `thread` per medium). Same shape as the **skills/skill.local:narrixRun (input contract)** section elsewhere in this README.
|
|
698
|
+
- Or `{ $path: "jobMemory.currentRecord" }` (or any `jobMemory.*` dot-path) to resolve from `request.jobMemory`.
|
|
699
|
+
- **`narrixScope`** (optional): Filters which signals and stories from Narrix are kept in task memory. Shape:
|
|
700
|
+
- `includeSignals?: string[]` — keep only signals whose `code` is in this array.
|
|
701
|
+
- `excludeSignals?: string[]` — drop signals whose `code` is in this array (ignored when `includeSignals` is set).
|
|
702
|
+
- `includeStories?: string[]` — keep only stories whose `narrativeTypeId` is in this array.
|
|
703
|
+
- `excludeStories?: string[]` — drop stories whose `narrativeTypeId` is in this array (ignored when `includeStories` is set).
|
|
704
|
+
- When omitted, all signals and stories pass through unfiltered.
|
|
705
|
+
- Example: `narrixScope: { includeSignals: ["OPEN_EGRESS_DETECTED", "PUBLIC_IP_EXPOSED"], includeStories: ["subnet-risk-summary"] }` — the task only sees these specific signals and narratives from the full Narrix output.
|
|
706
|
+
|
|
707
|
+
### Where Narrix output goes
|
|
708
|
+
|
|
709
|
+
- **Task memory:** `taskMemory.narrix` is an **array** of Narrix run outputs (`{ entity, signals, stories, meta }`). The executor and LLM receive this in the enriched memory and in the generated context (e.g. "## Narrix Scoping" section).
|
|
710
|
+
- **Convenience:** The latest run is also set on the skill input as `input.narrixContext` so prompt templates can reference it directly.
|
|
711
|
+
- **Response:** On success, `result.metadata.narrix` contains the last run's `{ entity, signals, stories, meta, durationMs }`.
|
|
712
|
+
|
|
713
|
+
### Minimal example
|
|
714
|
+
|
|
715
|
+
```typescript
|
|
716
|
+
import { runTask, NARRIX_THEN_DIRECT } from "@exellix/ai-tasks";
|
|
717
|
+
|
|
718
|
+
const result = await runTask({
|
|
719
|
+
skillKey: "tasks/security-risk-summary",
|
|
720
|
+
executionType: NARRIX_THEN_DIRECT,
|
|
721
|
+
narrixInput: {
|
|
722
|
+
medium: "record",
|
|
723
|
+
datasetId: "subnetEgress",
|
|
724
|
+
record: { graphized: { id: "subnet-1", cidr: "10.0.0.0/24" } },
|
|
725
|
+
},
|
|
726
|
+
input: { question: "Summarize the security posture of this subnet" },
|
|
727
|
+
jobId: "job-1",
|
|
728
|
+
agentId: "agent-1",
|
|
729
|
+
});
|
|
730
|
+
|
|
731
|
+
// result.parsed → LLM output (from the skill)
|
|
732
|
+
// result.metadata.narrix → { entity, signals, stories, meta, durationMs } from Narrix
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
### Failure behavior
|
|
736
|
+
|
|
737
|
+
If Narrix fails (e.g. `ok: false`, or `NARRIX_DISABLED` when `USE_NARRIX_INGEST` is not set), the function returns early with `parsed: { ok: false, phase: "narrix", error, message }` and `metadata.narrix`; the executor/LLM is **not** called.
|
|
738
|
+
|
|
739
|
+
### Export
|
|
740
|
+
|
|
741
|
+
`NARRIX_THEN_DIRECT` is exported from `@exellix/ai-tasks` for use in `executionType`.
|
|
742
|
+
|
|
743
|
+
---
|
|
744
|
+
|
|
745
|
+
## NARRIX task-level pre-processor
|
|
746
|
+
|
|
747
|
+
When a task request includes a **`narrix`** config (e.g. from graph node **`taskConfiguration.narrix`**), ai-tasks runs the NARRIX enrichment pipeline **before** executing the task and injects the result so the task handler (or LLM) can use it. This supports "narrix serving graph": graph nodes are domain tasks (e.g. assess reachability, assess posture); NARRIX is configuration on the node that enriches context, not a separate pipeline exposed as graph steps.
|
|
748
|
+
|
|
749
|
+
### Request
|
|
750
|
+
|
|
751
|
+
Add optional **`narrix`** to `RunTaskRequest`:
|
|
752
|
+
|
|
753
|
+
- **`narrix.datasetId`** (required): e.g. `"network.egress.subnets"`, `"network.vuln.instances"`.
|
|
754
|
+
- **`narrix.attachToField`** (optional): field name on `executionMemory` for the attachment; default `"_narrix"`.
|
|
755
|
+
- **`narrix.engineConfigPath`**, **`narrix.packsRoot`**, **`narrix.deterministicSort`**, **`narrix.assumptionsPolicy`**: reserved for future use.
|
|
756
|
+
- **`narrix.enableWebScope`** (optional): when **`true`**, after a successful NARRIX run the SDK calls **`@exellix/narrix-web-scoper`** and stores the result on **`executionMemory.webContext`** (always set when enabled, including miss/error shapes). Requires **`TAVILY_API_KEY`**. Default off.
|
|
757
|
+
- **`narrix.webScopeTemplates`** (optional): non-empty array → passed to the scoper as template-driven queries (athenix-parser tokens); replaces default query planning when set.
|
|
758
|
+
- **`narrix.webScopeQuestionTemplate`** (optional): Handlebars template for the search question when `webScopeTemplates` is not set.
|
|
759
|
+
- **`narrix.webScopeObjects`** (optional): merged into template context for `webScopeTemplates` / Handlebars.
|
|
760
|
+
- **`narrix.webScopeQuestions`** (optional): explicit list of web-scope questions when `webScopeTemplates` is not set; uses `scopeQuestionPack` in `@exellix/narrix-web-scoper`. Each entry: `{ id?: string; question: string; source?: "manual" | "ai-driven" }` (see [documenations/web-scoping-in-ai-tasks.md](./documenations/web-scoping-in-ai-tasks.md)).
|
|
761
|
+
- **`narrix.webScoping`** (optional): per-request slice of **`WebScoperConfig.scoping`** from **`@exellix/narrix-web-scoper`** (snippets, caps, raw body, `maxQueries`, `freshnessDays`, etc.). Forwarded into the scoper so behavior matches upstream releases. Omit or use `{}` for package defaults.
|
|
762
|
+
|
|
763
|
+
### Web scoping and upstream packages
|
|
764
|
+
|
|
765
|
+
Web scoping is implemented by **`@exellix/narrix-web-scoper`** (planning + **`WebContext`** mapping) and **`@exellix/search-adapter`** (Tavily normalization into **`SearchSource`** fields). This repo does not map provider JSON; it wires **`ScopeInput`**, optional **`narrix.webScoping`**, and **`runWebScope()`**.
|
|
766
|
+
|
|
767
|
+
When **`narrix.webScoping.includeSourceSnippets`** is **`true`**, each **`WebContext.sources[]`** entry may include provider-layer text as documented in the web-scoper README: first-class **`providerContent`** / **`providerRawContent`**, legacy mirrors **`content`** / **`rawContent`**, **`snippet`**, **`snippetCharCount`**, provenance fields (**`contentOrigin`**, **`retrievalStage`**, **`contentSource`**, etc.). Defaults keep **`includeSourceSnippets`** off so those fields are omitted. Optional caps: **`maxSnippetCharsPerSource`** (Unicode cap on provider excerpts and the text chosen for **`snippet`**; also forwarded as **`snippetMaxChars`** on search requests when positive). **`maxTotalWebContextChars`** applies **only** to **`WebSource.snippet`** across sources (after per-source caps)—it does not shrink stored **`providerContent`** / **`providerRawContent`**. To request raw body text from the provider, set **`narrix.webScoping.snippetIncludeRawContent`** (e.g. **`true`** or **`"markdown"`**); it is forwarded as **`includeRawContent`** on search requests. **`sourceExcerptFrom`** selects which provider field feeds **`snippet`** (**`providerContent`** default, or **`providerRawContent`**, with deprecated aliases **`content`** / **`rawContent`**). Use **`@exellix/search-adapter`** and **`@exellix/narrix-web-scoper`** versions from the same release family as this package’s dependencies.
|
|
768
|
+
|
|
769
|
+
**Downstream LLM context:** On the DIRECT path with **`includeContextInPrompt`**, when Narrix output is in play, ai-tasks appends a markdown section from **`executionMemory.webContext`** (when `available: true`) so the model sees source bodies, not only NARRIX signal codes. The **`synthesized-context`** PRE step uses the same markdown builder for policies that include web (e.g. **`narrix-web`**, **`narrix-web-memory`**, **`memory-web`**, **`auto`** when a web hit exists). Optional env **`WEB_CONTEXT_MARKDOWN_MAX_CHARS`** and optional **`SynthesisConfig.webEvidence`** cap and shape that markdown. Serialized memory for synthesis **never** embeds raw **`webContext`** as JSON; web appears as markdown only when the policy includes web.
|
|
770
|
+
|
|
771
|
+
Technical flow, failure behavior, and consumption patterns: **[documenations/web-scoping-in-ai-tasks.md](./documenations/web-scoping-in-ai-tasks.md)**.
|
|
772
|
+
|
|
773
|
+
### Raw record resolution
|
|
774
|
+
|
|
775
|
+
The pre-processor needs a **raw record** to enrich. Resolution order:
|
|
776
|
+
|
|
777
|
+
1. `executionMemory.input.raw`
|
|
778
|
+
2. `executionMemory.inputs.<first key>.raw`
|
|
779
|
+
3. `jobMemory.record` or `jobMemory.currentRecord`
|
|
780
|
+
4. `input.record` or `input.raw`
|
|
781
|
+
|
|
782
|
+
If no record is found, `runTask` throws with a clear error. **sourceMeta** is taken from `executionMemory.input.sourceMeta` or `jobMemory.sourceMeta` or `{}`.
|
|
783
|
+
|
|
784
|
+
### Where the result goes
|
|
785
|
+
|
|
786
|
+
- **Canonical:** `executionMemory[attachToField]` (default `executionMemory._narrix`) — shape `{ scoping: { stories, signals }, discovery: { stories, signals }, meta }`.
|
|
787
|
+
- **Mirrored:** `jobMemory._narrix` — same attachment so handlers can read from either `ctx.executionMemory._narrix` or `ctx.jobMemory._narrix`.
|
|
788
|
+
- **Web scope:** when **`narrix.enableWebScope === true`**, **`executionMemory.webContext`** is set to the **`WebScoperResult`** from **`@exellix/narrix-web-scoper`** (hit, miss, or structured error). It is independent of **`_narrix`**; NARRIX output remains attached even if web scoping fails.
|
|
789
|
+
|
|
790
|
+
Local task handlers receive the enriched `ctx`. On the DIRECT path, context is added only when `includeContextInPrompt === true`. When NARRIX is in play, that context is the "## Scoping and discovery" section from `executionMemory._narrix` when present, plus a **Web sources (primary evidence)** section when web scoping returned **`webContext.available === true`** (see above). We do not inject a "Narrix Pre-Processor" placeholder. The context generator (xontext) is not given _narrix when NARRIX is in play, so it cannot emit that section.
|
|
791
|
+
|
|
792
|
+
### Per-node enrichment
|
|
793
|
+
|
|
794
|
+
Each `runTask` call with `narrix` set is independent. If a graph runs two nodes with narrix config, each gets its own NARRIX run (no shared state). Orchestrators (e.g. graph-engine) lift `node.taskConfiguration.narrix` to `RunTaskRequest.narrix`.
|
|
795
|
+
|
|
796
|
+
### Example
|
|
797
|
+
|
|
798
|
+
```typescript
|
|
799
|
+
await runTask({
|
|
800
|
+
skillKey: "tasks/assess-reachability",
|
|
801
|
+
narrix: { datasetId: "network.egress.subnets" },
|
|
802
|
+
executionMemory: { input: { raw: subnetRecord } },
|
|
803
|
+
input: { question: "Assess reachability based on scoping." },
|
|
804
|
+
});
|
|
805
|
+
|
|
806
|
+
// Handler or LLM sees ctx.executionMemory._narrix (scoping, discovery, meta)
|
|
807
|
+
```
|
|
808
|
+
|
|
809
|
+
---
|
|
810
|
+
|
|
811
|
+
## Intermediate steps (multi-step tasks)
|
|
812
|
+
|
|
813
|
+
When a task runs multiple logical steps in one invocation (e.g. a combined node that does "to-cni + enrich + triage"), the response can include an optional **`intermediateSteps`** array so consumers can inspect and report the chain without extra round-trips.
|
|
814
|
+
|
|
815
|
+
### Response shape
|
|
816
|
+
|
|
817
|
+
- **`intermediateSteps`** (optional): array of steps in execution order. Each step has:
|
|
818
|
+
- **`step`** (number): 1-based order
|
|
819
|
+
- **`id`** (string): stable identifier (e.g. `"to-cni"`, `"engine-enrich"`, `"triage-q1-q6"`)
|
|
820
|
+
- **`ok`** (boolean): whether the step succeeded
|
|
821
|
+
- **`summary`** (optional): short description (e.g. `"cni built"`, `"enriched"`)
|
|
822
|
+
- **`error`** (optional): error message when `ok` is false
|
|
823
|
+
- **`inputSummary`** (optional): summary input for reporting
|
|
824
|
+
- **`outputExcerpt`** (optional): small excerpt of step output for debugging
|
|
825
|
+
|
|
826
|
+
Skills/tasks can return steps **inside `parsed`** (e.g. `parsed.intermediateSteps`); the SDK lifts them to the top-level **`response.intermediateSteps`** so consumers always read from the same place. Local tasks can return `{ output: {...}, intermediateSteps: [...] }`; the SDK exposes `intermediateSteps` on the response and keeps only the rest in `parsed`. For **narrix-then-direct**, the runtime prepends a step `{ step: 1, id: "narrix", ok: true }` and renumbers any steps from the direct phase.
|
|
827
|
+
|
|
828
|
+
### Example
|
|
829
|
+
|
|
830
|
+
```typescript
|
|
831
|
+
const res = await runTask({ skillKey: "tasks/combined-cni-enrich-triage", input: { ... } });
|
|
832
|
+
|
|
833
|
+
if (res.intermediateSteps) {
|
|
834
|
+
for (const s of res.intermediateSteps) {
|
|
835
|
+
console.log(`Step ${s.step}: ${s.id} ${s.ok ? "ok" : s.error}`);
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
// res.parsed holds the final task output (intermediateSteps are not duplicated there)
|
|
839
|
+
```
|
|
840
|
+
|
|
841
|
+
### Execution pipeline and synthesized-context (PRE step)
|
|
842
|
+
|
|
843
|
+
**Breaking change:** Execution can use an **execution pipeline** instead of a single `executionType`. See [BREAKING-CHANGES.md](BREAKING-CHANGES.md) for migration from `executionType` to `executionPipeline`.
|
|
844
|
+
|
|
845
|
+
- **Pipeline:** `executionPipeline` is an array of steps with phases **pre**, **main**, and **post**. Order: all PRE steps (in order) → exactly one MAIN step → all POST steps (optional; built-in types: `audit`, `polish`). Default when omitted: `[{ phase: "main", type: "direct" }]`. Existing `executionType` (e.g. `DIRECT`, `NARRIX_THEN_DIRECT`) is still supported when `executionPipeline` is not set.
|
|
846
|
+
- **PRE step `synthesized-context`:** A weak model (default `gpt-5-nano`) synthesizes **source material** (Narrix / memory / web per policy below) into a single context string; the MAIN task then runs with that string as its `context`. Requires `includeContextInPrompt: true` (or the PRE step’s `config.autoEnableContext: true`). Add `{ phase: "pre", type: SYNTHESIZED_CONTEXT, config: SynthesisConfig }` before the main step (`SYNTHESIZED_CONTEXT` is exported as the string `"synthesized-context"`).
|
|
847
|
+
- **Two LLM calls (typical):** deterministic **source material** (policy + markdown/JSON assembly) → **synthesis** model → synthesized `context` → **main** task model. Synthesis is not a pure string merge; it is a separate gateway invocation before the main skill.
|
|
848
|
+
- **SynthesisConfig** (on the PRE step’s `config`): `modelConfig`, **`contextSourcePolicy`**, optional **`synthesisInputStrategy`** (`policy` / `execution-memory-only` / `job-memory-only` / `task-memory-only` / `full-memory-bundle`), optional **`webEvidence`**, `customSynthesizingGuidelines`, `fallbackToDirect` (default `false`), `memoryPaths`, `synthesisPromptOverride`, `timeoutMs`, `maxOutputLength`, `autoEnableContext`, optional mode selectors **`synthesisMode`** (preferred) and **`synthesisOutputFormat`** (legacy-compatible), plus optional **`questionPath`**, **`getQuestion`**, **`structuredMaxItemsPerSide`**, **`structuredMaxItemContentChars`**, plus optional **`questionDriven`** and **`questions`**, plus optional **`xynthesizedOutput`** (**`destination`**: **`"job"`** | **`"task"`** | **`"execution"`**, **`outputKey`**, **`mode`**, **`alsoWriteLegacySynthesizedContext`**) to mirror synthesis into **`request.xynthesized`** and return **`response.xynthesizedPatch`** (see [Xynthesized memory and smart input](#xynthesized-memory-and-smart-input)). Full API: [documenations/synthesized-context-guide.md](./documenations/synthesized-context-guide.md).
|
|
849
|
+
- **Question-driven synthesis (opt-in):** Set **`questionDriven: true`** and provide **`questions`** (`[{ id, question, source?: "record"|"web"|"both" }]`). This mode runs one synthesis per question and writes a structured artifact to `executionMemory.synthesizedContext` with `mode: "questionDriven"` and stable access path `executionMemory.synthesizedContext.answers.<id>.synthesis`. Web-only questions degrade gracefully when `executionMemory.webContext` is unavailable (answer is present but marked unknown + reason).
|
|
850
|
+
- **Structured synthesis (opt-in):** Set **`synthesisMode: "structured"`** (or legacy-compatible **`synthesisOutputFormat: "structured"`**) so the synthesis model returns **JSON** matching **`SynthesizedPromptPayload`** (`SynthesisInput`, `SynthesizedContext`, `SynthesizedItem`, etc., exported from **`@exellix/ai-tasks`**). The package validates the shape, then builds main-step **`context`** markdown via **`buildSynthesizedContextMarkdown`** (task cores → question → local → supporting → unknowns → assumptions). Raw local vs web blocks are passed as **`local_raw`** / **`supporting_raw`** in the synthesis prompt (see **`resolveSourceMaterialParts`**). The task **question** is taken from **`getQuestion(request)`**, else **`questionPath`** on **`request.input`** (default path **`question`**), else the rendered downstream user prompt (truncated). In structured mode, template core tokens are required in template content; no detected template cores is treated as a template-definition error. Structured artifacts are persisted at `executionMemory.synthesizedContext`. Templates: **`templates/synthesis/system-structured.md`** and **`user-structured.txt`** (override path still **`SYNTHESIS_TEMPLATES_PATH`**). For tests or custom execution, use **`setContextSynthesizer`** / **`getContextSynthesizer`**; **`runStructuredSynthesisGatewayCall`** mirrors the default gateway path.
|
|
851
|
+
- **Synthesis uses a real LLM call** (`AIGateway.invoke` via xynthesis `executeXynthesisAction`) — not just string merging. Building `source_material` is deterministic; the PRE step then **invokes the synthesis model** (e.g. `SYNTHESIS_MODEL` / `modelConfig`) before the MAIN step runs. See [documenations/synthesis-invocation-notes.md](./documenations/synthesis-invocation-notes.md) for details.
|
|
852
|
+
- **`webEvidence`:** Optional tuning for the **Web sources** markdown block inside synthesis source material: `preferCleanContent` (default `true`), `maxSources` (default `5`), `dedupeByUrl` (default `true`), `maxTotalChars`. Env **`WEB_CONTEXT_MARKDOWN_MAX_CHARS`** still applies when `maxTotalChars` is not set.
|
|
853
|
+
- **`contextSourcePolicy` (what becomes `<source_material>` in the synthesis prompt):**
|
|
854
|
+
|
|
855
|
+
| Policy | Narrix section | Web markdown | Memory JSON |
|
|
856
|
+
|--------|----------------|--------------|-------------|
|
|
857
|
+
| **`narrix-web`** | Yes (required) | If `webContext` hit | No |
|
|
858
|
+
| **`narrix-web-memory`** | If present | If hit | Yes — never embeds raw `webContext` in JSON |
|
|
859
|
+
| **`memory-web`** | **Stripped** from JSON | If hit | Yes |
|
|
860
|
+
| **`memory-only`** | No | No | Yes — never embeds raw `webContext` in JSON |
|
|
861
|
+
| **`auto`** | Resolves at runtime | | |
|
|
862
|
+
|
|
863
|
+
**`auto` resolution:** **`narrix-web-memory`** if Narrix output exists (`executionMemory[attachToField]` or coerced **`taskMemory.narrix`**); else **`memory-web`** if web scoping produced a usable hit; else **`memory-only`**. **Legacy aliases:** `narrix-only` → `narrix-web`, `narrix+memory` → `narrix-web-memory`.
|
|
864
|
+
|
|
865
|
+
**Synthesis output shape (default):** `templates/synthesis/system.md` asks the weak model for three markdown sections in order: **`## Local context (primary)`**, **`## Web evidence (supporting)`**, **`## Supporting sources (optional)`** (short traceability lines only). Override with `synthesisPromptOverride` if needed. With **`synthesisOutputFormat: "structured"`**, output is JSON validated to the generic synthesis contract, then converted to markdown for the main step only.
|
|
866
|
+
|
|
867
|
+
**Migration note:** If you relied on **`auto`** with Narrix but **without** memory in the synthesizer input, set **`contextSourcePolicy: "narrix-web"`** explicitly. See [BREAKING-CHANGES.md](BREAKING-CHANGES.md) (*Synthesis `contextSourcePolicy` and `auto`*).
|
|
868
|
+
- **Templates:** Synthesis prompts live in **templates/synthesis/** — markdown mode: `system.md`, `user.txt`; structured mode: `system-structured.md`, `user-structured.txt`. Optional env **`SYNTHESIS_TEMPLATES_PATH`** for base path.
|
|
869
|
+
- **Caching:** Optional synthesis result caching via **nx-cache** (same project) can be used to reduce cost for batch or repeated runs.
|
|
870
|
+
- **Non-streaming:** Synthesis returns a **single complete response**; there is no streaming support for the synthesis step.
|
|
871
|
+
- **Builder:** `withExecutionPipeline(steps)`, `withSynthesizedContextPreStep(modelOrConfig?)` to add the PRE step and set `includeContextInPrompt: true` (pass a **`SynthesisConfig`** object for `contextSourcePolicy`, `webEvidence`, `memoryPaths`, **`xynthesizedOutput`** with **`destination: "job" | "task" | "execution"`**, etc.). Also **`withXynthesized`**, **`withSmartInput`**, **`withSmartInputPaths`**, **`withSmartInputRenderOptions`**, **`withXynthesizedJob`**, **`withXynthesizedTask`**, **`withXynthesizedExecution`** for request-level fields.
|
|
872
|
+
- **Auditability fields in response metadata:** `synthesisEnabled`, `effectiveExecutionPipeline`, `synthesizedContextPresent`, `mainContextSource`, `detectedTemplateCores`. Final response also includes normalized `executionMemory` and `executionState.executionMemory`.
|
|
873
|
+
|
|
874
|
+
**Minimal example (explicit policy + web tuning):**
|
|
875
|
+
|
|
876
|
+
```typescript
|
|
877
|
+
import { SYNTHESIZED_CONTEXT } from "@exellix/ai-tasks";
|
|
878
|
+
|
|
879
|
+
executionPipeline: [
|
|
880
|
+
{
|
|
881
|
+
phase: "pre",
|
|
882
|
+
type: SYNTHESIZED_CONTEXT,
|
|
883
|
+
config: {
|
|
884
|
+
contextSourcePolicy: "narrix-web-memory",
|
|
885
|
+
webEvidence: { maxSources: 3, preferCleanContent: true },
|
|
886
|
+
modelConfig: { model: "gpt-5-nano" },
|
|
887
|
+
},
|
|
888
|
+
},
|
|
889
|
+
{ phase: "main", type: "direct" },
|
|
890
|
+
],
|
|
891
|
+
includeContextInPrompt: true,
|
|
892
|
+
```
|
|
893
|
+
|
|
894
|
+
### POST Step Strategies (post-execution)
|
|
895
|
+
|
|
896
|
+
The execution pipeline supports **POST** steps that run after the MAIN step and operate on its response. Two built-in strategies: **`audit`** (quality-gate loop with re-runs) and **`polish`** (refinement pass against a prioritized checklist). POST steps run in pipeline order; each step’s output becomes the next step’s input.
|
|
897
|
+
|
|
898
|
+
**Pipeline example:**
|
|
899
|
+
|
|
900
|
+
```typescript
|
|
901
|
+
executionPipeline: [
|
|
902
|
+
{ phase: "pre", type: "synthesized-context", config: { /* ... */ } },
|
|
903
|
+
{ phase: "main", type: "direct" },
|
|
904
|
+
{ phase: "post", type: "audit", config: { gateway: { must: [...], should: [...] }, maxCycles: 3 } },
|
|
905
|
+
{ phase: "post", type: "polish", config: { checklist: [...], maxPasses: 2 } },
|
|
906
|
+
]
|
|
907
|
+
```
|
|
908
|
+
|
|
909
|
+
Metadata for each POST step is under `response.metadata.postSteps.<type>`. All POST steps’ intermediate steps are merged into `response.intermediateSteps` with continuous numbering (e.g. `audit-cycle-1`, `audit-synthesis`, `audit-select`, `polish-pass-1`).
|
|
910
|
+
|
|
911
|
+
#### POST step: `audit`
|
|
912
|
+
|
|
913
|
+
Quality-gate loop: evaluate MAIN output against **must** and **should** checks, get actionable feedback, re-run the MAIN step when criteria aren’t met, then choose the best result (or run an optional synthesis merge of the top two candidates).
|
|
914
|
+
|
|
915
|
+
- **Config:** `AuditConfig`: `gateway.must`, `gateway.should` (each `{ check, weight }`), `threshold` (default 80), `minCycles`, `maxCycles` (default 3), `selectionStrategy` (`"best"` | `"synthesis"`), `auditModelConfig`, `synthesisModelConfig`, `customAuditGuidelines`, `customSynthesisGuidelines`, `fallbackToBest` (default true), `auditTimeoutMs`.
|
|
916
|
+
- **Output:** Audit uses **structured Markdown** (Flex-MD style); the runtime parses it into checks and overall feedback and computes `weightedScore`.
|
|
917
|
+
- **Re-runs:** When the loop continues, the MAIN step is re-invoked with the original context plus a feedback block from `templates/post-steps/audit/feedback-injection.md`.
|
|
918
|
+
- **Metadata:** `response.metadata.postSteps.audit`: `totalCycles`, `candidateScores`, `selectedCycleIndex`, `allMustPassed`, `synthesisUsed`, `auditResults`, `durationMs`.
|
|
919
|
+
- **Caching:** Audit (and synthesis) LLM calls use **nx-cache** when inputs and config are unchanged (no cache on re-runs with feedback).
|
|
920
|
+
- **Failure:** If all cycles are exhausted and no candidate passes all `must` checks: with `fallbackToBest: true` (default) the best-scored candidate is returned and `allMustPassed: false`; with `fallbackToBest: false` an error is thrown.
|
|
921
|
+
|
|
922
|
+
#### POST step: `polish`
|
|
923
|
+
|
|
924
|
+
Single- or multi-pass refinement against a **prioritized checklist**. Does not re-run MAIN; always produces a refined output (or falls back to the last good output on failure).
|
|
925
|
+
|
|
926
|
+
- **Config:** `PolishConfig`: `checklist` (each `{ instruction, priority: "high" | "medium" | "low" }`), `maxPasses` (default 1), `modelConfig`, `customGuidelines`, `includeOriginalContext`, `timeoutMs`.
|
|
927
|
+
- **Output:** Polish LLM returns **JSON**: `{ polishedOutput, changeNotes[] }`.
|
|
928
|
+
- **Metadata:** `response.metadata.postSteps.polish`: `totalPasses`, `changeNotes` (per pass), `durationMs`.
|
|
929
|
+
- **Failure:** Polish never throws; on LLM/parse failure it returns the previous output and records `ok: false` in the step.
|
|
930
|
+
|
|
931
|
+
#### Minimal examples
|
|
932
|
+
|
|
933
|
+
**Audit only:**
|
|
934
|
+
|
|
935
|
+
```typescript
|
|
936
|
+
const result = await runTask({
|
|
937
|
+
skillKey: "tasks/security-risk-summary",
|
|
938
|
+
input: { assetId: "a-123" },
|
|
939
|
+
jobId: "job-1",
|
|
940
|
+
agentId: "agent-1",
|
|
941
|
+
executionPipeline: [
|
|
942
|
+
{ phase: "main", type: "direct" },
|
|
943
|
+
{
|
|
944
|
+
phase: "post",
|
|
945
|
+
type: "audit",
|
|
946
|
+
config: {
|
|
947
|
+
gateway: {
|
|
948
|
+
must: [{ check: "Output includes a severity rating (critical/high/medium/low)", weight: 10 }],
|
|
949
|
+
should: [{ check: "Recommendations are ordered by priority", weight: 5 }],
|
|
950
|
+
},
|
|
951
|
+
threshold: 80,
|
|
952
|
+
maxCycles: 3,
|
|
953
|
+
},
|
|
954
|
+
},
|
|
955
|
+
],
|
|
956
|
+
});
|
|
957
|
+
console.log(result.parsed); // best candidate output
|
|
958
|
+
console.log(result.metadata.postSteps?.audit); // totalCycles, candidateScores, allMustPassed, etc.
|
|
959
|
+
```
|
|
960
|
+
|
|
961
|
+
**Polish only:**
|
|
962
|
+
|
|
963
|
+
```typescript
|
|
964
|
+
const result = await runTask({
|
|
965
|
+
skillKey: "tasks/executive-summary",
|
|
966
|
+
input: { report: "..." },
|
|
967
|
+
executionPipeline: [
|
|
968
|
+
{ phase: "main", type: "direct" },
|
|
969
|
+
{
|
|
970
|
+
phase: "post",
|
|
971
|
+
type: "polish",
|
|
972
|
+
config: {
|
|
973
|
+
checklist: [
|
|
974
|
+
{ instruction: "Remove jargon; use plain language", priority: "high" },
|
|
975
|
+
{ instruction: "Each paragraph ≤ 3 sentences", priority: "medium" },
|
|
976
|
+
],
|
|
977
|
+
maxPasses: 2,
|
|
978
|
+
},
|
|
979
|
+
},
|
|
980
|
+
],
|
|
981
|
+
});
|
|
982
|
+
```
|
|
983
|
+
|
|
984
|
+
#### Chaining audit + polish
|
|
985
|
+
|
|
986
|
+
Common pattern: audit first to ensure quality, then polish to refine the approved candidate. Audit’s chosen output becomes polish’s input.
|
|
987
|
+
|
|
988
|
+
#### Builder
|
|
989
|
+
|
|
990
|
+
- **`withAuditPostStep(config: AuditConfig)`** — Appends an audit POST step; ensures pipeline has a MAIN step.
|
|
991
|
+
- **`withPolishPostStep(config: PolishConfig)`** — Appends a polish POST step; ensures pipeline has a MAIN step.
|
|
992
|
+
|
|
993
|
+
Example with builder (no need to set `executionPipeline` manually):
|
|
994
|
+
|
|
995
|
+
```typescript
|
|
996
|
+
const request = new TaskRequestBuilder()
|
|
997
|
+
.withSkillKey("tasks/security-risk-summary")
|
|
998
|
+
.withInput({ assetId: "a-123" })
|
|
999
|
+
.withJobId("job-1")
|
|
1000
|
+
.withAgentId("agent-1")
|
|
1001
|
+
.withAuditPostStep({
|
|
1002
|
+
gateway: {
|
|
1003
|
+
must: [{ check: "Output includes severity rating", weight: 10 }],
|
|
1004
|
+
should: [{ check: "Recommendations ordered by priority", weight: 5 }],
|
|
1005
|
+
},
|
|
1006
|
+
threshold: 80,
|
|
1007
|
+
maxCycles: 3,
|
|
1008
|
+
})
|
|
1009
|
+
.withPolishPostStep({
|
|
1010
|
+
checklist: [
|
|
1011
|
+
{ instruction: "Use active voice throughout", priority: "high" },
|
|
1012
|
+
{ instruction: "Remove redundant qualifiers", priority: "medium" },
|
|
1013
|
+
],
|
|
1014
|
+
})
|
|
1015
|
+
.build();
|
|
1016
|
+
const result = await runTask(request);
|
|
1017
|
+
```
|
|
1018
|
+
|
|
1019
|
+
#### Template paths
|
|
1020
|
+
|
|
1021
|
+
- Audit: `templates/post-steps/audit/` (`system.md`, `user.txt`, `feedback-injection.md`, `synthesis.md`). Override: `AUDIT_TEMPLATES_PATH` (base path for the audit folder).
|
|
1022
|
+
- Polish: `templates/post-steps/polish/` (`system.md`, `user.txt`). Override: `POLISH_TEMPLATES_PATH`.
|
|
1023
|
+
|
|
1024
|
+
Templates use Handlebars (`{{variable}}`, `{{#each}}`, `{{#if}}`).
|
|
1025
|
+
|
|
1026
|
+
#### Design decisions
|
|
1027
|
+
|
|
1028
|
+
- **Audit re-runs MAIN (not the audit model):** The MAIN step has the full prompt and skill context; re-running it with audit feedback yields better corrections.
|
|
1029
|
+
- **Polish does not re-run MAIN:** Polish is surface-level (tone, format); a dedicated rewrite step is cheaper and sufficient.
|
|
1030
|
+
- **Audit output is Markdown (Flex-MD), not forced JSON:** More robust across models; runtime parses structured sections.
|
|
1031
|
+
- **nx-cache for audit/synthesis:** Reduces cost for repeated or batch runs with identical inputs.
|
|
1032
|
+
- **No global cost guardrails** (e.g. `maxTotalPostCalls`) in this release; control via `maxCycles` and `maxPasses`.
|
|
1033
|
+
- **No streaming** for POST steps; final output is returned after all POST steps complete.
|
|
1034
|
+
|
|
1035
|
+
#### Testing
|
|
1036
|
+
|
|
1037
|
+
POST steps are covered by unit and integration tests in `test/post-steps/`:
|
|
1038
|
+
|
|
1039
|
+
- **resolvePostStepConfig.test.ts** — model and timeout resolution (config, env, fallback).
|
|
1040
|
+
- **parseAuditOutput.test.ts** — parsing audit structured markdown into checks and overall feedback.
|
|
1041
|
+
- **parsePolishOutput.test.ts** — parsing polish JSON (including code-fenced) into `polishedOutput` and `changeNotes`.
|
|
1042
|
+
- **runAuditPostStep.test.ts** — audit step with mocked gateway: passing/failing markdown, fallback, and audit-call failure.
|
|
1043
|
+
- **runPolishPostStep.test.ts** — polish step with mocked gateway: valid JSON, call failure fallback, multiple passes.
|
|
1044
|
+
- **pipeline-post-steps.test.ts** — pipeline integration: `runTask` with `executionPipeline` (main + audit, main + polish) and mocked executor/gateways; asserts `metadata.postSteps` and final output.
|
|
1045
|
+
|
|
1046
|
+
Run post-steps tests: `npm test` (includes `dist-test/test/post-steps/*.js`).
|
|
1047
|
+
|
|
1048
|
+
### Types
|
|
1049
|
+
|
|
1050
|
+
Use **`RunTaskResponseWithSteps<TParsed>`** when you expect steps; it extends `RunSkillResponse` with `intermediateSteps?: IntermediateStep[]`. When POST steps run, `response.metadata.postSteps` is set (`audit?: AuditPostStepMetadata`, `polish?: PolishPostStepMetadata`). Exported POST step types: `AuditConfig`, `AuditCheck`, `AuditResult`, `AuditCheckResult`, `AuditLLMOutput`, `AuditPostStepMetadata`, `PolishConfig`, `PolishChecklistItem`, `PolishLLMOutput`, `PolishPostStepMetadata`, `RunTaskResponsePostStepsMetadata`. See the [Types](#intermediate-step-intermediate-steps-runtaskresponsewithsteps) section and [documenations/intermediate-steps.md](./documenations/intermediate-steps.md) for full details.
|
|
1051
|
+
|
|
1052
|
+
---
|
|
1053
|
+
|
|
1054
|
+
## How max tokens are resolved (automation + caller controls)
|
|
1055
|
+
|
|
1056
|
+
`@exellix/xynthesis` 3.1+ ships `resolveMaxTokens` — a deterministic function that picks the right `maxTokens` for any LLM call from `(inputText, outputExpectation, modelId, callerMaxTokens?, structuredOutput?)`. `@exellix/ai-tasks` invokes it on **every** xynthesis-backed call (PRE synthesis, AI scoping, POST audit, POST polish, structured-repair fallback) so callers don't have to pick a number.
|
|
1057
|
+
|
|
1058
|
+
**The pipeline is:**
|
|
1059
|
+
|
|
1060
|
+
1. **Estimate input tokens** from `inputText` (system + user + extra material) using `xynthesis.estimateTokens`.
|
|
1061
|
+
2. **Resolve the effective `OutputExpectation`** = caller's `llmCall.outputExpectation` → per-action default (`xynthesis.resolveOutputExpectation('synthesis' | 'audit' | 'fix' | 'pick-best' | 'craft-final')`) → stage-local default (e.g. AI scoping uses `{ size: { mode: "absolute", minWords: 5, maxWords: 200 }, density: "concise" }`) → xynthesis package fallback.
|
|
1062
|
+
3. **Compute estimated output tokens** from the expectation (absolute words → tokens; relative → ratio × input tokens), multiplied by `headroomMultiplier(density)` and (if `structuredOutput`) the structured-output factor.
|
|
1063
|
+
4. **Cap at the model's `maxOutputTokens`** from `MODEL_CAPABILITIES` (unknown models fall back to `{ contextWindow: 128_000, maxOutputTokens: 4_096, completionTokenCost: "medium" }`).
|
|
1064
|
+
5. **Honor the caller's `maxTokensCap`** as a HARD CEILING — the auto-sizer can pick a smaller number, but never larger.
|
|
1065
|
+
6. The `resolveMaxTokens` result (`{ maxTokens, reason: "computed" | "caller-override" | "model-ceiling" | "fallback", diagnostics: { … } }`) is forwarded to xynthesis as `tokenResolutionMetadata` and surfaced in trace mode as `LlmCallObservation.request.tokenResolution`.
|
|
1066
|
+
|
|
1067
|
+
**To adjust per-call dynamically** (without losing the automation):
|
|
1068
|
+
|
|
1069
|
+
- Set `llmCall.outputExpectation` to bias the auto-sizer toward smaller / larger / denser output.
|
|
1070
|
+
- Set `llmCall.maxTokensCap` to bound the upper limit (e.g. for cost ceilings).
|
|
1071
|
+
- Leave both unset to get the per-stage / per-action default.
|
|
1072
|
+
|
|
1073
|
+
The legacy `llmCall.maxTokens` field is still accepted as an alias for `maxTokensCap`. The legacy `modelConfig.maxTokens` (forwarded as-is to ai-skills for the MAIN skill call) continues to work.
|
|
1074
|
+
|
|
1075
|
+
> **Note:** the MAIN skill call goes through `@exellix/ai-skills` `runSkill`, which today uses your `modelConfig.maxTokens` directly (no auto-sizer). Setting `RunTaskRequest.llmCall.maxTokensCap` on the main skill is overlaid onto `modelConfig.maxTokens` before the call. ai-skills auto-sizing is tracked as an upstream feature request in [`documenations/upstream-feature-requests/ai-skills-llm-observability.md`](documenations/upstream-feature-requests/ai-skills-llm-observability.md).
|
|
1076
|
+
|
|
1077
|
+
---
|
|
1078
|
+
|
|
1079
|
+
## LLM call configuration (`LlmCallConfig`)
|
|
1080
|
+
|
|
1081
|
+
`LlmCallConfig` is the canonical, per-stage knob set every LLM-orchestrated step accepts. It composes with the legacy `modelConfig` (and per-step `model` / `timeoutMs` / `maxOutputLength`) — when both are set, `llmCall.*` wins.
|
|
1082
|
+
|
|
1083
|
+
```typescript
|
|
1084
|
+
type LlmCallConfig = {
|
|
1085
|
+
model?: string; // provider/model id
|
|
1086
|
+
maxTokensCap?: number; // HARD CEILING for resolveMaxTokens (does NOT disable automation)
|
|
1087
|
+
maxTokens?: number; // alias for maxTokensCap (backward compat)
|
|
1088
|
+
temperature?: number;
|
|
1089
|
+
topP?: number;
|
|
1090
|
+
maxOutputLength?: number; // post-call character truncation
|
|
1091
|
+
timeoutMs?: number;
|
|
1092
|
+
outputExpectation?: OutputExpectation; // drives resolveMaxTokens auto-sizing
|
|
1093
|
+
};
|
|
1094
|
+
```
|
|
1095
|
+
|
|
1096
|
+
**Per-stage placement:**
|
|
1097
|
+
|
|
1098
|
+
| Stage | Where to set `llmCall` |
|
|
1099
|
+
|-------|------------------------|
|
|
1100
|
+
| MAIN skill | `RunTaskRequest.llmCall` (or use the legacy `modelConfig`) |
|
|
1101
|
+
| AI scoping (per-instruction) | `RunTaskRequest.aiScoping[].llmCall` |
|
|
1102
|
+
| AI scoping (request fallback) | `RunTaskRequest.aiScopingOptions.llmCall` |
|
|
1103
|
+
| PRE synthesis | `SynthesisConfig.llmCall` (on the `synthesized-context` PRE step) |
|
|
1104
|
+
| POST audit (evaluator) | `AuditConfig.llmCall` or `AuditConfig.audit.llmCall` |
|
|
1105
|
+
| POST audit (synthesis-merge) | `AuditConfig.audit.synthesis.llmCall` |
|
|
1106
|
+
| POST polish | `PolishConfig.llmCall` |
|
|
1107
|
+
| Utility (xynthesis-finalize) | `RunUtilityRequest.exec.{model, maxTokensCap, temperature, topP, outputExpectation, ...}` |
|
|
1108
|
+
|
|
1109
|
+
**Builder helpers (`TaskRequestBuilder`):**
|
|
1110
|
+
|
|
1111
|
+
- `withLlmCall(llmCall)` — main skill.
|
|
1112
|
+
- `withAiScopingOptions({ llmCall, concurrency, timeoutMsPerItem })` — fallback for every `aiScoping[]` item.
|
|
1113
|
+
- `withSynthesisLlmCall(llmCall)` — PRE `synthesized-context` step.
|
|
1114
|
+
- `withAuditLlmCall(llmCall, role?)` — POST audit step (`role: "audit" | "synthesis"`, omit for top-level).
|
|
1115
|
+
- `withPolishLlmCall(llmCall)` — POST polish step.
|
|
1116
|
+
- `withXynthesized(memory)` / `withSmartInput(config)` / `withSmartInputPaths(paths)` / `withSmartInputRenderOptions(opts)` — graph **`xynthesized`** / **`smartInput`** contract (see [Xynthesized memory and smart input](#xynthesized-memory-and-smart-input)).
|
|
1117
|
+
- `withXynthesizedJob(key, value)` / `withXynthesizedTask(key, value)` / `withXynthesizedExecution(key, value)` — merge one key into **`xynthesized.job`** / **`xynthesized.task`** / **`xynthesized.execution`**.
|
|
1118
|
+
|
|
1119
|
+
**Environment variables (POST steps + AI scoping):** every stage reads from per-stage env vars first, then shared `POST_STEP_*` env vars, then the supplied fallback. Order is: `config.<field>` → step env → shared env → fallback.
|
|
1120
|
+
|
|
1121
|
+
| Env var (per stage) | Effect |
|
|
1122
|
+
|---------------------|--------|
|
|
1123
|
+
| `AUDIT_MODEL`, `POLISH_MODEL`, `SYNTHESIS_MODEL`, `AI_SCOPING_MODEL` | Model override for that stage |
|
|
1124
|
+
| `AUDIT_TIMEOUT_MS`, `POLISH_TIMEOUT_MS`, `SYNTHESIS_TIMEOUT_MS`, `AI_SCOPING_TIMEOUT_MS` | Per-call timeout override |
|
|
1125
|
+
| `AUDIT_MAX_TOKENS_CAP`, `POLISH_MAX_TOKENS_CAP`, `SYNTHESIS_MAX_TOKENS_CAP` | Hard ceiling override |
|
|
1126
|
+
| `AUDIT_TEMPERATURE`, `POLISH_TEMPERATURE`, `SYNTHESIS_TEMPERATURE` | Temperature override |
|
|
1127
|
+
| `AUDIT_TOP_P`, `POLISH_TOP_P`, `SYNTHESIS_TOP_P` | Top-P override |
|
|
1128
|
+
| `AUDIT_MAX_OUTPUT_LENGTH`, `POLISH_MAX_OUTPUT_LENGTH`, `SYNTHESIS_MAX_OUTPUT_LENGTH` | Post-call char truncation override |
|
|
1129
|
+
| `AI_SCOPING_CONCURRENCY` | Max concurrent scoping calls (default 5) |
|
|
1130
|
+
| `AI_SCOPING_TIMEOUT_MS_PER_ITEM` | Per-item timeout in ms (default 30_000) |
|
|
1131
|
+
|
|
1132
|
+
| Shared env var | Effect |
|
|
1133
|
+
|----------------|--------|
|
|
1134
|
+
| `POST_STEP_MODEL` | Default model for any POST step that didn't get a specific value |
|
|
1135
|
+
| `POST_STEP_TIMEOUT_MS` | Default timeout |
|
|
1136
|
+
| `POST_STEP_MAX_TOKENS_CAP` | Default hard ceiling |
|
|
1137
|
+
| `POST_STEP_TEMPERATURE` | Default temperature |
|
|
1138
|
+
| `POST_STEP_TOP_P` | Default Top-P |
|
|
1139
|
+
| `POST_STEP_MAX_OUTPUT_LENGTH` | Default char truncation |
|
|
1140
|
+
|
|
1141
|
+
`outputExpectation` is intentionally NOT read from env vars (too structured).
|
|
1142
|
+
|
|
1143
|
+
---
|
|
1144
|
+
|
|
1145
|
+
## Trace mode observability for LLM calls
|
|
1146
|
+
|
|
1147
|
+
When `RunTaskRequest.executionMode === "trace"` (or `RunUtilityRequest.exec.executionMode === "trace"`), `@exellix/ai-tasks` populates a structured `LlmCallObservation` for **every** LLM call attempted (success or failure) and surfaces them on response metadata:
|
|
1148
|
+
|
|
1149
|
+
| `result.metadata` field | Stage | Notes |
|
|
1150
|
+
|-------------------------|-------|-------|
|
|
1151
|
+
| `synthesizedContextLlmCalls?: LlmCallObservation[]` | PRE synthesis (markdown / question-driven / structured) | one entry per question or branch |
|
|
1152
|
+
| `aiScopingLlmCalls?: LlmCallObservation[]` | AI scoping | one entry per `aiScoping[]` instruction |
|
|
1153
|
+
| `aiScopingFailures?: AiScopingFailureRecord[]` | AI scoping | **always** populated when any item failed (regardless of trace mode) — previously these were silently dropped |
|
|
1154
|
+
| `postSteps.audit.llmCalls?: LlmCallObservation[]` | POST audit (evaluator + synthesis-merge) | per-cycle entries |
|
|
1155
|
+
| `postSteps.polish.llmCalls?: LlmCallObservation[]` | POST polish | per-pass entries |
|
|
1156
|
+
| `intermediateSteps[].observation?: LlmCallObservation` | any failed POST step | attached to the failure step so consumers can read the request/response context inline |
|
|
1157
|
+
|
|
1158
|
+
`LlmCallObservation` is a discriminated union over `source: "xynthesis" | "ai-skills"`:
|
|
1159
|
+
|
|
1160
|
+
```typescript
|
|
1161
|
+
type LlmCallObservation =
|
|
1162
|
+
| {
|
|
1163
|
+
source: "xynthesis"; stage: LlmCallStage; stepId?: string;
|
|
1164
|
+
request: LlmCallRequestSnapshot; // includes resolved tokenResolution from resolveMaxTokens
|
|
1165
|
+
summary?: InvokeAttemptSummary; // from executeXynthesisAction on success / typed errors on failure
|
|
1166
|
+
debugTrace?: XynthesisDebugTrace; // present when executionMode: "trace" was forwarded
|
|
1167
|
+
durationMs: number; ok: boolean; error?: { name: string; message: string };
|
|
1168
|
+
}
|
|
1169
|
+
| {
|
|
1170
|
+
source: "ai-skills"; stage: "main-skill"; stepId?: string;
|
|
1171
|
+
request: LlmCallRequestSnapshot;
|
|
1172
|
+
diagnostics?: RunSkillDiagnostics; // from runSkill in trace mode
|
|
1173
|
+
durationMs: number; ok: boolean; error?: { name: string; message: string };
|
|
1174
|
+
};
|
|
1175
|
+
```
|
|
1176
|
+
|
|
1177
|
+
**`InvokeAttemptSummary`** (xynthesis ≥ 3.1) carries `{ jobId, taskId, modelRequested, modelResolved, maxTokensFromCaller, maxTokensEffective, modelUsedFromProvider, usage, routing, costUsd, traceKind, executionMetadata }`.
|
|
1178
|
+
|
|
1179
|
+
**`RunSkillDiagnostics`** (ai-skills ≥ 5.0) carries `{ trace: { step, timing, routing, usage, costUsd, modelUsed, attempts[], rawProviderPayload?, invokeRequest? }, ... }`.
|
|
1180
|
+
|
|
1181
|
+
### Typed errors (instanceof-friendly)
|
|
1182
|
+
|
|
1183
|
+
Failures from xynthesis call sites are returned as upstream typed errors that already carry rich context — `@exellix/ai-tasks` passes them through unchanged so callers can `instanceof` them:
|
|
1184
|
+
|
|
1185
|
+
| Error class | Carries | Where |
|
|
1186
|
+
|-------------|---------|-------|
|
|
1187
|
+
| `XynthesisInvokeError` | `invokeSummary`, `cause` | xynthesis invoke failure (PRE / POST / scoping / utility) |
|
|
1188
|
+
| `XynthesisResponseParseError` | `stage`, `invokeSummary`, `responseLength`, `responsePreview`, `actionType?`, `cause` | xynthesis parse failure |
|
|
1189
|
+
| `SkillExecutionTraceError` | `RunSkillDiagnostics` (incl. `trace.invokeRequest` echo) | MAIN skill failure in trace mode |
|
|
1190
|
+
| `LlmCallContextError` | `observation: LlmCallObservation`, `cause` | thin wrapper for the few sites where the upstream still throws untyped (raw `setTimeout` timeouts, MAIN skill outside trace mode, AIGateway repair fallback) |
|
|
1191
|
+
|
|
1192
|
+
All of these are re-exported from `@exellix/ai-tasks`:
|
|
1193
|
+
|
|
1194
|
+
```typescript
|
|
1195
|
+
import {
|
|
1196
|
+
XynthesisInvokeError,
|
|
1197
|
+
XynthesisResponseParseError,
|
|
1198
|
+
SkillExecutionTraceError,
|
|
1199
|
+
LlmCallContextError,
|
|
1200
|
+
type LlmCallObservation,
|
|
1201
|
+
type LlmCallConfig,
|
|
1202
|
+
type InvokeAttemptSummary,
|
|
1203
|
+
type RunSkillDiagnostics,
|
|
1204
|
+
resolveMaxTokens,
|
|
1205
|
+
resolveOutputExpectation,
|
|
1206
|
+
ACTION_OUTPUT_DEFAULTS,
|
|
1207
|
+
MODEL_CAPABILITIES,
|
|
1208
|
+
} from "@exellix/ai-tasks";
|
|
1209
|
+
```
|
|
1210
|
+
|
|
1211
|
+
### Worked example
|
|
1212
|
+
|
|
1213
|
+
```typescript
|
|
1214
|
+
import { runTask, type LlmCallConfig } from "@exellix/ai-tasks";
|
|
1215
|
+
|
|
1216
|
+
const llmCall: LlmCallConfig = {
|
|
1217
|
+
model: "gpt-5-mini",
|
|
1218
|
+
maxTokensCap: 4096, // hard ceiling
|
|
1219
|
+
outputExpectation: { size: { kind: "absolute", maxWords: 600 }, density: "default" },
|
|
1220
|
+
temperature: 0.4,
|
|
1221
|
+
};
|
|
1222
|
+
|
|
1223
|
+
const result = await runTask({
|
|
1224
|
+
skillKey: "tasks/security-risk-summary",
|
|
1225
|
+
input: { assetId: "a-123" },
|
|
1226
|
+
executionMode: "trace",
|
|
1227
|
+
llmCall,
|
|
1228
|
+
aiScopingOptions: { llmCall: { model: "gpt-5-nano", maxTokensCap: 512 } },
|
|
1229
|
+
// executionPipeline: [{ phase: "pre", type: "synthesized-context", config: { llmCall } }, { phase: "main", type: "direct" }],
|
|
1230
|
+
});
|
|
1231
|
+
|
|
1232
|
+
const m = (result as any).metadata ?? {};
|
|
1233
|
+
console.log("PRE synth calls:", m.synthesizedContextLlmCalls?.length ?? 0);
|
|
1234
|
+
console.log("AI scoping calls:", m.aiScopingLlmCalls?.length ?? 0);
|
|
1235
|
+
console.log("AI scoping fails:", m.aiScopingFailures?.length ?? 0);
|
|
1236
|
+
console.log("audit calls:", m.postSteps?.audit?.llmCalls?.length ?? 0);
|
|
1237
|
+
console.log("polish calls:", m.postSteps?.polish?.llmCalls?.length ?? 0);
|
|
1238
|
+
```
|
|
1239
|
+
|
|
1240
|
+
---
|
|
1241
|
+
|
|
1242
|
+
## Graph Execution Support
|
|
1243
|
+
|
|
1244
|
+
### Overview
|
|
1245
|
+
|
|
1246
|
+
`@exellix/ai-tasks` supports optional graph execution context fields that enable smarter activity tracking and identity for nodes executing within graph workflows. When executing tasks in a graph (e.g., via `worex-graphs`), you can provide additional context fields to improve activity tracking and debugging.
|
|
1247
|
+
|
|
1248
|
+
The client automatically passes these fields through to `@woroces/ai-skills`, which then passes them to `@x12i/ai-gateway`. The gateway maps them to the activity identity structure according to `@x12i/activix-tracking` v3.5.0+ mapping rules.
|
|
1249
|
+
|
|
1250
|
+
### Optional Fields
|
|
1251
|
+
|
|
1252
|
+
#### `graphId` (optional)
|
|
1253
|
+
|
|
1254
|
+
**Type:** `string`
|
|
1255
|
+
**Description:** Identifier for the graph execution context
|
|
1256
|
+
|
|
1257
|
+
Used when executing tasks within a graph to track which graph contains the task. This enables:
|
|
1258
|
+
- Activity grouping and filtering by graph
|
|
1259
|
+
- Better activity identity for graph-based workflows
|
|
1260
|
+
- Enhanced debugging and traceability
|
|
1261
|
+
|
|
1262
|
+
**Example:**
|
|
1263
|
+
```typescript
|
|
1264
|
+
const result = await tasks.runTask({
|
|
1265
|
+
skillKey: 'tasks/security-risk-summary',
|
|
1266
|
+
executionType: ExecutionType.DIRECT,
|
|
1267
|
+
input: { assetId: 'a-123' },
|
|
1268
|
+
jobId: 'job-123',
|
|
1269
|
+
agentId: 'agent-1',
|
|
1270
|
+
graphId: 'graph-456', // Graph identifier
|
|
1271
|
+
nodeId: 'node-789', // Node identifier
|
|
1272
|
+
});
|
|
1273
|
+
```
|
|
1274
|
+
|
|
1275
|
+
**Use Cases:**
|
|
1276
|
+
- Graph workflow execution tracking
|
|
1277
|
+
- Multi-graph job orchestration
|
|
1278
|
+
- Graph-level activity aggregation
|
|
1279
|
+
|
|
1280
|
+
#### `nodeId` (optional)
|
|
1281
|
+
|
|
1282
|
+
**Type:** `string`
|
|
1283
|
+
**Description:** Identifier for the specific node being executed
|
|
1284
|
+
|
|
1285
|
+
Used when executing tasks within a graph to track which specific node is being executed. When provided along with `graphId`, enables:
|
|
1286
|
+
- Precise activity identity for graph nodes
|
|
1287
|
+
- Node-level activity tracking and debugging
|
|
1288
|
+
- Better correlation between graph structure and executed activities
|
|
1289
|
+
|
|
1290
|
+
**Example:**
|
|
1291
|
+
```typescript
|
|
1292
|
+
const result = await tasks.runTask({
|
|
1293
|
+
skillKey: 'tasks/security-risk-summary',
|
|
1294
|
+
executionType: ExecutionType.DIRECT,
|
|
1295
|
+
input: { assetId: 'a-123' },
|
|
1296
|
+
jobId: 'job-123',
|
|
1297
|
+
agentId: 'agent-1',
|
|
1298
|
+
graphId: 'workflow-data-processing', // Graph identifier
|
|
1299
|
+
nodeId: 'extract-emails-node', // Node identifier
|
|
1300
|
+
});
|
|
1301
|
+
```
|
|
1302
|
+
|
|
1303
|
+
**Use Cases:**
|
|
1304
|
+
- Individual node execution tracking
|
|
1305
|
+
- Node-level activity analysis
|
|
1306
|
+
- Graph node debugging
|
|
1307
|
+
|
|
1308
|
+
#### `prevNodeId` (optional)
|
|
1309
|
+
|
|
1310
|
+
**Type:** `string`
|
|
1311
|
+
**Description:** Identifier for the previous node in the graph execution flow
|
|
1312
|
+
|
|
1313
|
+
Used when executing tasks within a graph to track which node was executed before the current one. This enables:
|
|
1314
|
+
- Graph flow tracking and debugging
|
|
1315
|
+
- Understanding execution order in graph workflows
|
|
1316
|
+
- Better activity correlation between consecutive nodes
|
|
1317
|
+
|
|
1318
|
+
**Example:**
|
|
1319
|
+
```typescript
|
|
1320
|
+
const result = await tasks.runTask({
|
|
1321
|
+
skillKey: 'tasks/security-risk-summary',
|
|
1322
|
+
executionType: ExecutionType.DIRECT,
|
|
1323
|
+
input: { assetId: 'a-123' },
|
|
1324
|
+
jobId: 'job-123',
|
|
1325
|
+
agentId: 'agent-1',
|
|
1326
|
+
graphId: 'workflow-data-processing',
|
|
1327
|
+
nodeId: 'extract-emails-node',
|
|
1328
|
+
prevNodeId: 'validate-input-node', // Previous node identifier
|
|
1329
|
+
});
|
|
1330
|
+
```
|
|
1331
|
+
|
|
1332
|
+
**Use Cases:**
|
|
1333
|
+
- Graph execution flow tracking
|
|
1334
|
+
- Understanding node execution sequence
|
|
1335
|
+
- Debugging graph workflow issues
|
|
1336
|
+
|
|
1337
|
+
#### `coreSkillId` (optional)
|
|
1338
|
+
|
|
1339
|
+
**Type:** `string`
|
|
1340
|
+
**Description:** Alternative node identifier for graph execution
|
|
1341
|
+
|
|
1342
|
+
Used as an alternative to `nodeId` when executing tasks within a graph. This field is automatically mapped to `identity.nodeId` in activity tracking, with priority over `nodeId` but below `skillId` (from the underlying skill request).
|
|
1343
|
+
|
|
1344
|
+
**Example:**
|
|
1345
|
+
```typescript
|
|
1346
|
+
const result = await tasks.runTask({
|
|
1347
|
+
skillKey: 'tasks/security-risk-summary',
|
|
1348
|
+
executionType: ExecutionType.DIRECT,
|
|
1349
|
+
input: { assetId: 'a-123' },
|
|
1350
|
+
jobId: 'job-123',
|
|
1351
|
+
agentId: 'agent-1',
|
|
1352
|
+
graphId: 'workflow-data-processing',
|
|
1353
|
+
coreSkillId: 'q0', // Alternative node identifier
|
|
1354
|
+
});
|
|
1355
|
+
```
|
|
1356
|
+
|
|
1357
|
+
**Use Cases:**
|
|
1358
|
+
- Alternative node identification (common in worex-graphs integration)
|
|
1359
|
+
- Node-level tracking when using coreSkillId naming convention
|
|
1360
|
+
|
|
1361
|
+
### Field Mapping
|
|
1362
|
+
|
|
1363
|
+
The client automatically passes graph/node fields through `@woroces/ai-skills` to the underlying `@x12i/ai-gateway`, which maps them to the activity identity structure. The gateway uses the following priority rules:
|
|
1364
|
+
|
|
1365
|
+
#### Graph ID Mapping
|
|
1366
|
+
|
|
1367
|
+
1. `masterSkillId` (primary) → `identity.graphId`
|
|
1368
|
+
2. `graphId` (fallback) → `identity.graphId`
|
|
1369
|
+
|
|
1370
|
+
#### Node ID Mapping
|
|
1371
|
+
|
|
1372
|
+
1. `skillId` (primary, from underlying skill request) → `identity.nodeId`
|
|
1373
|
+
2. `coreSkillId` (alternative) → `identity.nodeId`
|
|
1374
|
+
3. `nodeId` (fallback) → `identity.nodeId`
|
|
1375
|
+
|
|
1376
|
+
#### Previous Node ID Mapping
|
|
1377
|
+
|
|
1378
|
+
1. `prevNodeId` → `identity.prevNodeId` (automatically mapped for graph flow tracking)
|
|
1379
|
+
|
|
1380
|
+
#### Preserved Fields
|
|
1381
|
+
|
|
1382
|
+
- `masterSkillId` → `identity.masterSkillId` (preserved for backward compatibility)
|
|
1383
|
+
- `masterSkillActivityId` → `identity.masterSkillActivityId` (preserved for skill hierarchies)
|
|
1384
|
+
|
|
1385
|
+
This enables:
|
|
1386
|
+
- **Smarter Identity**: Activity records include graph and node context automatically
|
|
1387
|
+
- **Better Grouping**: Filter and aggregate activities by graph or node
|
|
1388
|
+
- **Enhanced Debugging**: Trace activities back to specific graph nodes
|
|
1389
|
+
- **Improved Analytics**: Analyze graph execution patterns and node performance
|
|
1390
|
+
- **Flexible Integration**: Support for multiple field naming conventions
|
|
1391
|
+
|
|
1392
|
+
### Complete Field Mapping Guide
|
|
1393
|
+
|
|
1394
|
+
| @exellix/ai-tasks Field | @woroces/ai-skills Field | @x12i/ai-gateway Field | Identity Field | Mapping Priority |
|
|
1395
|
+
|-------------------------|--------------------------|----------------------------|----------------|------------------|
|
|
1396
|
+
| `masterSkillId` | `masterSkillId` | `masterSkillId` (AIRequest) | `identity.graphId` | Priority 1 (primary) |
|
|
1397
|
+
| `graphId` | `graphId` | `graphId` | `identity.graphId` | Priority 2 (fallback) |
|
|
1398
|
+
| `skillId` (via skills) | `skillId` (AIRequest) | `skillId` (AIRequest) | `identity.nodeId` | Priority 1 (primary) |
|
|
1399
|
+
| `coreSkillId` | `coreSkillId` | `coreSkillId` | `identity.nodeId` | Priority 2 (alternative) |
|
|
1400
|
+
| `nodeId` | `nodeId` | `nodeId` | `identity.nodeId` | Priority 3 (fallback) |
|
|
1401
|
+
| `skillKey` | `skillKey` | `instructions` | - | Direct mapping |
|
|
1402
|
+
| `jobId` | `jobId` | `jobId` | `identity.jobId` | Direct mapping |
|
|
1403
|
+
| `agentId` | `agentId` | `agentId` | `identity.agentId` | Direct mapping |
|
|
1404
|
+
| `masterSkillActivityId` | `masterSkillActivityId` | `masterSkillActivityId` (AIRequest) | `identity.masterSkillActivityId` | Preserved |
|
|
1405
|
+
|
|
1406
|
+
**Note:** `masterSkillId` is a legacy field that is still supported for backward compatibility. The new `graphId` and `nodeId` fields provide a cleaner API for graph-based workflows.
|
|
1407
|
+
|
|
1408
|
+
### Usage Examples
|
|
1409
|
+
|
|
1410
|
+
#### Basic Graph Node Execution
|
|
1411
|
+
|
|
1412
|
+
```typescript
|
|
1413
|
+
import { WorexClientTasks, ExecutionType } from "@exellix/ai-tasks";
|
|
1414
|
+
|
|
1415
|
+
const tasks = new WorexClientTasks(skills, executor);
|
|
1416
|
+
|
|
1417
|
+
// Execute a task within a graph
|
|
1418
|
+
const result = await tasks.runTask({
|
|
1419
|
+
skillKey: 'tasks/security-risk-summary',
|
|
1420
|
+
executionType: ExecutionType.DIRECT,
|
|
1421
|
+
input: { assetId: 'a-123' },
|
|
1422
|
+
jobId: 'job-123',
|
|
1423
|
+
agentId: 'agent-1',
|
|
1424
|
+
graphId: 'workflow-data-processing',
|
|
1425
|
+
nodeId: 'extract-emails-node',
|
|
1426
|
+
});
|
|
1427
|
+
```
|
|
1428
|
+
|
|
1429
|
+
#### Graph Execution with Legacy Fields
|
|
1430
|
+
|
|
1431
|
+
```typescript
|
|
1432
|
+
// Using legacy masterSkillId (still supported)
|
|
1433
|
+
const result = await tasks.runTask({
|
|
1434
|
+
skillKey: 'tasks/security-risk-summary',
|
|
1435
|
+
executionType: ExecutionType.DIRECT,
|
|
1436
|
+
input: { assetId: 'a-123' },
|
|
1437
|
+
jobId: 'job-123',
|
|
1438
|
+
agentId: 'agent-1',
|
|
1439
|
+
masterSkillId: 'workflow-data-processing', // Maps to identity.graphId
|
|
1440
|
+
coreSkillId: 'extract-emails-node', // Maps to identity.nodeId
|
|
1441
|
+
});
|
|
1442
|
+
```
|
|
1443
|
+
|
|
1444
|
+
#### Graph Execution with coreSkillId
|
|
1445
|
+
|
|
1446
|
+
```typescript
|
|
1447
|
+
// Using coreSkillId (common in worex-graphs)
|
|
1448
|
+
const result = await tasks.runTask({
|
|
1449
|
+
skillKey: 'tasks/security-risk-summary',
|
|
1450
|
+
executionType: ExecutionType.DIRECT,
|
|
1451
|
+
input: { assetId: 'a-123' },
|
|
1452
|
+
jobId: 'job-123',
|
|
1453
|
+
agentId: 'agent-1',
|
|
1454
|
+
graphId: 'workflow-data-processing',
|
|
1455
|
+
coreSkillId: 'q0', // Alternative node identifier
|
|
1456
|
+
});
|
|
1457
|
+
```
|
|
1458
|
+
|
|
1459
|
+
#### Graph Execution Without Node ID
|
|
1460
|
+
|
|
1461
|
+
```typescript
|
|
1462
|
+
// Graph-level execution (no specific node)
|
|
1463
|
+
const result = await tasks.runTask({
|
|
1464
|
+
skillKey: 'tasks/process-data',
|
|
1465
|
+
executionType: ExecutionType.DIRECT,
|
|
1466
|
+
input: { data: '...' },
|
|
1467
|
+
jobId: 'job-123',
|
|
1468
|
+
agentId: 'agent-1',
|
|
1469
|
+
graphId: 'workflow-data-processing',
|
|
1470
|
+
// nodeId not provided - still works
|
|
1471
|
+
});
|
|
1472
|
+
```
|
|
1473
|
+
|
|
1474
|
+
#### Non-Graph Execution (Backward Compatible)
|
|
1475
|
+
|
|
1476
|
+
```typescript
|
|
1477
|
+
// Standard execution (no graph context)
|
|
1478
|
+
const result = await tasks.runTask({
|
|
1479
|
+
skillKey: 'tasks/security-risk-summary',
|
|
1480
|
+
executionType: ExecutionType.DIRECT,
|
|
1481
|
+
input: { assetId: 'a-123' },
|
|
1482
|
+
jobId: 'job-123',
|
|
1483
|
+
agentId: 'agent-1',
|
|
1484
|
+
// graphId and nodeId not provided - works as before
|
|
1485
|
+
});
|
|
1486
|
+
```
|
|
1487
|
+
|
|
1488
|
+
#### Using TaskRequestBuilder with Graph Context
|
|
1489
|
+
|
|
1490
|
+
```typescript
|
|
1491
|
+
import { TaskRequestBuilder } from "@exellix/ai-tasks";
|
|
1492
|
+
|
|
1493
|
+
const builder = new TaskRequestBuilder();
|
|
1494
|
+
const request = builder
|
|
1495
|
+
.withSkillKey('tasks/security-risk-summary')
|
|
1496
|
+
.withInput({ assetId: 'a-123' })
|
|
1497
|
+
.withGraphId('workflow-data-processing')
|
|
1498
|
+
.withNodeId('extract-emails-node')
|
|
1499
|
+
.withJobId('job-123')
|
|
1500
|
+
.withAiSkillsCorrelation('agent-1', 'job-type-id', 'task-type-id')
|
|
1501
|
+
.build();
|
|
1502
|
+
|
|
1503
|
+
const result = await tasks.runTask(request);
|
|
1504
|
+
```
|
|
1505
|
+
|
|
1506
|
+
#### Using TaskRequestBuilder with Memory Management
|
|
1507
|
+
|
|
1508
|
+
```typescript
|
|
1509
|
+
import { TaskRequestBuilder } from "@exellix/ai-tasks";
|
|
1510
|
+
import type { JobHistory, TaskHistory, ExecutionHistory } from "@exellix/ai-tasks";
|
|
1511
|
+
|
|
1512
|
+
const jobMemory: JobHistory = { /* previous task results */ };
|
|
1513
|
+
const taskMemory: TaskHistory = { /* previous skill executions */ };
|
|
1514
|
+
const executionMemory: ExecutionHistory = { /* execution context */ };
|
|
1515
|
+
|
|
1516
|
+
const builder = new TaskRequestBuilder();
|
|
1517
|
+
const request = builder
|
|
1518
|
+
.withSkillKey('tasks/my-task')
|
|
1519
|
+
.withInput({ data: 'process' })
|
|
1520
|
+
.withJobMemory(jobMemory)
|
|
1521
|
+
.withTaskMemory(taskMemory)
|
|
1522
|
+
.withExecutionMemory(executionMemory)
|
|
1523
|
+
.withJobId('job-123')
|
|
1524
|
+
.withAiSkillsCorrelation('agent-1', 'job-type-id', 'task-type-id')
|
|
1525
|
+
.build();
|
|
1526
|
+
|
|
1527
|
+
const result = await tasks.runTask(request);
|
|
1528
|
+
```
|
|
1529
|
+
|
|
1530
|
+
#### Using Convenience Methods with Graph Context
|
|
1531
|
+
|
|
1532
|
+
```typescript
|
|
1533
|
+
import { runTask, ExecutionType } from "@exellix/ai-tasks";
|
|
1534
|
+
|
|
1535
|
+
// Convenience method automatically initializes SDK (ERC mode)
|
|
1536
|
+
const result = await runTask({
|
|
1537
|
+
skillKey: 'tasks/security-risk-summary',
|
|
1538
|
+
executionType: ExecutionType.DIRECT,
|
|
1539
|
+
input: { assetId: 'a-123' },
|
|
1540
|
+
jobId: 'job-123',
|
|
1541
|
+
agentId: 'agent-1',
|
|
1542
|
+
graphId: 'workflow-data-processing',
|
|
1543
|
+
nodeId: 'extract-emails-node',
|
|
1544
|
+
});
|
|
1545
|
+
```
|
|
1546
|
+
|
|
1547
|
+
### Integration with Graph Runtimes
|
|
1548
|
+
|
|
1549
|
+
When integrating with graph execution runtimes (e.g., `worex-graphs`), you can pass graph/node fields directly - the client automatically passes them through to the underlying skills client and gateway.
|
|
1550
|
+
|
|
1551
|
+
#### Automatic Mapping (Recommended)
|
|
1552
|
+
|
|
1553
|
+
The client automatically passes fields from graph runtimes to the gateway. You can pass fields directly without manual mapping. **Production calls** must also include **`jobTypeId`**, **`taskTypeId`**, and **`executionStrategies`** (often `[]`); the snippets below omit them for brevity.
|
|
1554
|
+
|
|
1555
|
+
```typescript
|
|
1556
|
+
// worex-graphs sends fields directly - client handles passing through automatically
|
|
1557
|
+
const requestFromGraph = {
|
|
1558
|
+
skillKey: 'tasks/security-risk-summary',
|
|
1559
|
+
executionType: ExecutionType.DIRECT,
|
|
1560
|
+
jobId: 'job-123',
|
|
1561
|
+
agentId: 'agent-1',
|
|
1562
|
+
// jobTypeId, taskTypeId, executionStrategies: [] — required in real requests
|
|
1563
|
+
masterSkillId: 'graph-question-breakdown', // Automatically mapped to identity.graphId
|
|
1564
|
+
coreSkillId: 'q0', // Automatically mapped to identity.nodeId
|
|
1565
|
+
masterSkillActivityId: 'job-123:q0', // Preserved in identity
|
|
1566
|
+
input: { assetId: 'a-123' },
|
|
1567
|
+
// ... other fields
|
|
1568
|
+
};
|
|
1569
|
+
|
|
1570
|
+
// Pass fields directly - client passes through automatically
|
|
1571
|
+
const result = await tasks.runTask({
|
|
1572
|
+
skillKey: requestFromGraph.skillKey,
|
|
1573
|
+
executionType: ExecutionType.DIRECT,
|
|
1574
|
+
input: requestFromGraph.input,
|
|
1575
|
+
jobId: requestFromGraph.jobId,
|
|
1576
|
+
agentId: requestFromGraph.agentId,
|
|
1577
|
+
masterSkillId: requestFromGraph.masterSkillId, // ✅ Automatically passed to gateway
|
|
1578
|
+
coreSkillId: requestFromGraph.coreSkillId, // ✅ Automatically passed to gateway
|
|
1579
|
+
masterSkillActivityId: requestFromGraph.masterSkillActivityId, // ✅ Automatically passed to gateway
|
|
1580
|
+
// ... other fields
|
|
1581
|
+
});
|
|
1582
|
+
```
|
|
1583
|
+
|
|
1584
|
+
#### Using New Graph Fields (Alternative)
|
|
1585
|
+
|
|
1586
|
+
Alternatively, you can map to the new direct fields (`graphId`/`nodeId`):
|
|
1587
|
+
|
|
1588
|
+
```typescript
|
|
1589
|
+
// Map to new graph fields
|
|
1590
|
+
const result = await tasks.runTask({
|
|
1591
|
+
skillKey: requestFromGraph.skillKey,
|
|
1592
|
+
executionType: ExecutionType.DIRECT,
|
|
1593
|
+
input: requestFromGraph.input,
|
|
1594
|
+
jobId: requestFromGraph.jobId,
|
|
1595
|
+
agentId: requestFromGraph.agentId,
|
|
1596
|
+
graphId: requestFromGraph.masterSkillId, // Direct graph identifier
|
|
1597
|
+
nodeId: requestFromGraph.coreSkillId, // Direct node identifier
|
|
1598
|
+
// ... other fields
|
|
1599
|
+
});
|
|
1600
|
+
```
|
|
1601
|
+
|
|
1602
|
+
#### Xynthesized memory and `smartInput` from graph-engine
|
|
1603
|
+
|
|
1604
|
+
Graph runtimes can pass **`xynthesized`** (**`job`**, **`task`**, and **`execution`** buckets) and **`smartInput`** (**Rendrix paths** or legacy **`paths: string[]`**) on the same `runTask` request. ai-tasks **normalizes** legacy string paths, **forwards** **`smartInput`** / **`smartInputRenderOptions`** to local **`ctx`**, PRE synthesis, MAIN **`runSkill`**, and merged **template `variables`**; it **does not** apply graph output mapping or merge patches into a global store—that is the engine’s job after reading **`response.xynthesizedPatch`** (**`job`**, **`task`**, and/or **`execution`** slices) and any **`response.smartInputRenderResult`** from downstream. See [Xynthesized memory and smart input](#xynthesized-memory-and-smart-input).
|
|
1605
|
+
|
|
1606
|
+
### Activity Record Structure
|
|
1607
|
+
|
|
1608
|
+
When graph context is provided, activity records include the mapped fields in the identity structure:
|
|
1609
|
+
|
|
1610
|
+
```typescript
|
|
1611
|
+
{
|
|
1612
|
+
_id: ObjectId('...'),
|
|
1613
|
+
jobId: 'job-123',
|
|
1614
|
+
agentId: 'agent-1',
|
|
1615
|
+
taskId: 'task-456',
|
|
1616
|
+
taskTypeId: 'abc123...',
|
|
1617
|
+
identity: {
|
|
1618
|
+
jobId: 'job-123',
|
|
1619
|
+
agentId: 'agent-1',
|
|
1620
|
+
taskId: 'task-456',
|
|
1621
|
+
taskTypeId: 'abc123...',
|
|
1622
|
+
graphId: 'workflow-data-processing', // ← Automatically mapped from masterSkillId or graphId
|
|
1623
|
+
nodeId: 'extract-emails-node', // ← Automatically mapped from skillId, coreSkillId, or nodeId
|
|
1624
|
+
masterSkillId: 'workflow-data-processing', // ← Preserved if provided (backward compatibility)
|
|
1625
|
+
masterSkillActivityId: 'job-123:node-456' // ← Preserved if provided (skill hierarchies)
|
|
1626
|
+
},
|
|
1627
|
+
request: { /* ... */ },
|
|
1628
|
+
config: { /* ... */ },
|
|
1629
|
+
// ... other fields
|
|
1630
|
+
}
|
|
1631
|
+
```
|
|
1632
|
+
|
|
1633
|
+
### Benefits
|
|
1634
|
+
|
|
1635
|
+
1. **Enhanced Activity Tracking**: Activities include graph and node context for better traceability
|
|
1636
|
+
2. **Smarter Identity**: Activity identity is enriched with graph/node information
|
|
1637
|
+
3. **Better Debugging**: Easier to correlate activities with graph structure
|
|
1638
|
+
4. **Improved Analytics**: Analyze execution patterns at graph and node levels
|
|
1639
|
+
5. **Flexible Integration**: Optional fields don't break existing integrations
|
|
1640
|
+
6. **Backward Compatible**: Legacy `masterSkillId` field still works
|
|
1641
|
+
|
|
1642
|
+
### Migration Guide
|
|
1643
|
+
|
|
1644
|
+
#### For Existing Integrations
|
|
1645
|
+
|
|
1646
|
+
These fields are **optional**, so existing integrations continue to work without changes:
|
|
1647
|
+
|
|
1648
|
+
```typescript
|
|
1649
|
+
// ✅ Existing code continues to work
|
|
1650
|
+
const result = await tasks.runTask({
|
|
1651
|
+
skillKey: 'tasks/security-risk-summary',
|
|
1652
|
+
executionType: ExecutionType.DIRECT,
|
|
1653
|
+
input: { assetId: 'a-123' },
|
|
1654
|
+
jobId: 'job-123',
|
|
1655
|
+
agentId: 'agent-1',
|
|
1656
|
+
// graphId and nodeId not required
|
|
1657
|
+
});
|
|
1658
|
+
```
|
|
1659
|
+
|
|
1660
|
+
#### Adding Graph Support
|
|
1661
|
+
|
|
1662
|
+
To add graph execution support, simply include the optional fields:
|
|
1663
|
+
|
|
1664
|
+
```typescript
|
|
1665
|
+
// ✅ Add graph context for better tracking
|
|
1666
|
+
const result = await tasks.runTask({
|
|
1667
|
+
skillKey: 'tasks/security-risk-summary',
|
|
1668
|
+
executionType: ExecutionType.DIRECT,
|
|
1669
|
+
input: { assetId: 'a-123' },
|
|
1670
|
+
jobId: 'job-123',
|
|
1671
|
+
agentId: 'agent-1',
|
|
1672
|
+
graphId: 'my-graph-id', // Add graph context
|
|
1673
|
+
nodeId: 'my-node-id', // Add node context (optional)
|
|
1674
|
+
});
|
|
1675
|
+
```
|
|
1676
|
+
|
|
1677
|
+
#### Migrating from Legacy Fields
|
|
1678
|
+
|
|
1679
|
+
If you're using `masterSkillId`, you can optionally migrate to the new `graphId` field for clarity:
|
|
1680
|
+
|
|
1681
|
+
```typescript
|
|
1682
|
+
// Before (still works)
|
|
1683
|
+
const result = await tasks.runTask({
|
|
1684
|
+
skillKey: 'tasks/security-risk-summary',
|
|
1685
|
+
executionType: ExecutionType.DIRECT,
|
|
1686
|
+
input: { assetId: 'a-123' },
|
|
1687
|
+
masterSkillId: 'graph-id',
|
|
1688
|
+
coreSkillId: 'node-id',
|
|
1689
|
+
});
|
|
1690
|
+
|
|
1691
|
+
// After (cleaner API)
|
|
1692
|
+
const result = await tasks.runTask({
|
|
1693
|
+
skillKey: 'tasks/security-risk-summary',
|
|
1694
|
+
executionType: ExecutionType.DIRECT,
|
|
1695
|
+
input: { assetId: 'a-123' },
|
|
1696
|
+
graphId: 'graph-id', // More explicit
|
|
1697
|
+
nodeId: 'node-id', // More explicit
|
|
1698
|
+
});
|
|
1699
|
+
```
|
|
1700
|
+
|
|
1701
|
+
### TypeScript Types
|
|
1702
|
+
|
|
1703
|
+
All graph execution fields are properly typed in the TypeScript definitions:
|
|
1704
|
+
|
|
1705
|
+
```typescript
|
|
1706
|
+
interface RunTaskRequest {
|
|
1707
|
+
skillKey: string;
|
|
1708
|
+
agentId: string; // Required by @exellix/ai-skills
|
|
1709
|
+
jobTypeId: string;
|
|
1710
|
+
taskTypeId: string;
|
|
1711
|
+
input: Record<string, any> | string;
|
|
1712
|
+
executionType: ExecutionType;
|
|
1713
|
+
// ... other fields ...
|
|
1714
|
+
graphId?: string; // Graph execution context
|
|
1715
|
+
nodeId?: string; // Node identifier
|
|
1716
|
+
coreSkillId?: string; // Alternative node identifier
|
|
1717
|
+
masterSkillId?: string; // Legacy graph identifier (still supported)
|
|
1718
|
+
// ... other fields ...
|
|
1719
|
+
}
|
|
1720
|
+
```
|
|
1721
|
+
|
|
1722
|
+
### Related Documentation
|
|
1723
|
+
|
|
1724
|
+
- [Activity Tracking](#) - Main documentation with usage examples
|
|
1725
|
+
- [@woroces/ai-skills Graph Execution Support](https://github.com/woroces/ai-skills#graph-execution-support) - Skills-level documentation
|
|
1726
|
+
- [@x12i/ai-gateway Graph Execution Support](https://github.com/athenices/ai-gateway#graph-execution-support) - Gateway-level documentation
|
|
1727
|
+
- [@x12i/activix-tracking](https://github.com/athenices/ai-activities-tracking) - Activity tracking specification
|
|
1728
|
+
|
|
1729
|
+
### Notes
|
|
1730
|
+
|
|
1731
|
+
- **`agentId` / `jobTypeId` / `taskTypeId` are required** on every `runTask` (see [`RUNTASK_REQUEST.md`](RUNTASK_REQUEST.md)).
|
|
1732
|
+
- Graph/node fields (`graphId`, `nodeId`, …) remain optional for non-graph runs — provide them when executing tasks in graphs.
|
|
1733
|
+
- Fields are automatically passed through to the skills client and gateway, then mapped to activity identity.
|
|
1734
|
+
- Multiple field naming conventions supported:
|
|
1735
|
+
- New fields: `graphId`/`nodeId`/`coreSkillId` (recommended for new code)
|
|
1736
|
+
- Legacy fields: `masterSkillId` (still supported for backward compatibility)
|
|
1737
|
+
- Mapping priority ensures consistent identity structure regardless of field naming.
|
|
1738
|
+
- Best practice: Include graph/node context fields for graph task execution for maximum traceability.
|
|
1739
|
+
|
|
1740
|
+
---
|
|
1741
|
+
|
|
1742
|
+
## API Reference
|
|
1743
|
+
|
|
1744
|
+
### `WorexClientTasks`
|
|
1745
|
+
|
|
1746
|
+
Main class for task execution.
|
|
1747
|
+
|
|
1748
|
+
#### Constructor
|
|
1749
|
+
|
|
1750
|
+
```typescript
|
|
1751
|
+
constructor(
|
|
1752
|
+
skillsClient: WorexClientSkills,
|
|
1753
|
+
executor: {
|
|
1754
|
+
execute<TParsed = any>(
|
|
1755
|
+
input: any,
|
|
1756
|
+
onNotFound: (key: string) => Promise<void>
|
|
1757
|
+
): Promise<RunTaskResponse<TParsed>>;
|
|
1758
|
+
}
|
|
1759
|
+
)
|
|
1760
|
+
```
|
|
1761
|
+
|
|
1762
|
+
#### Methods
|
|
1763
|
+
|
|
1764
|
+
##### `runTask<TParsed>(input: RunTaskRequest): Promise<RunTaskResponse<TParsed>>`
|
|
1765
|
+
|
|
1766
|
+
Executes a task with task-level enrichment and context generation.
|
|
1767
|
+
|
|
1768
|
+
```typescript
|
|
1769
|
+
const result = await tasks.runTask({
|
|
1770
|
+
skillKey: "tasks/security-risk-summary",
|
|
1771
|
+
agentId: "my-agent",
|
|
1772
|
+
jobTypeId: "security-risk-job",
|
|
1773
|
+
taskTypeId: "security-risk-task",
|
|
1774
|
+
// executionType is optional, defaults to ExecutionType.DIRECT
|
|
1775
|
+
input: { assetId: "a-123" },
|
|
1776
|
+
variables: { orgName: "Acme" },
|
|
1777
|
+
jobMemory: previousJobMemory,
|
|
1778
|
+
taskMemory: previousTaskMemory,
|
|
1779
|
+
executionMemory: previousExecutionMemory,
|
|
1780
|
+
jobId: "job-123"
|
|
1781
|
+
});
|
|
1782
|
+
```
|
|
1783
|
+
|
|
1784
|
+
#### Local task registry
|
|
1785
|
+
|
|
1786
|
+
- **`registerLocalTask(skillKey: string, handler: LocalTaskHandler): void`** — Register a local task handler. When `runTask` is called with this `skillKey`, the handler runs instead of the normal enrichment/executor path.
|
|
1787
|
+
- **`getLocalTask(skillKey: string): LocalTaskHandler | undefined`** — Return the registered handler for `skillKey`, or `undefined`.
|
|
1788
|
+
- **`registerBuiltInLocalTasks(): void`** — Register built-in handlers (`skills/node.callExport`, `skills/node.callExportBatch`, `skills/skill.local:validateInput`, `skills/skill.local:normalizeNarrixResult`). The Narrix skill `skills/skill.local:narrixRun` (record/text/docs/chat) is registered via a side-effect import and is available when the package is loaded.
|
|
1789
|
+
- **`LocalTaskContext`** — Type: `skillKey`, `jobMemory`, `taskMemory`, `executionMemory`, `variables`, `xynthesized`, `smartInput`, `jobId`, `taskId`, `agentId`, `graphId`, `nodeId`, `prevNodeId`, `coreSkillId`, `masterSkillId`, `masterSkillActivityId`.
|
|
1790
|
+
- **`LocalTaskHandler`** — Type: `(args: { input: any; ctx: LocalTaskContext }) => Promise<any>`.
|
|
1791
|
+
|
|
1792
|
+
### `TaskRequestBuilder`
|
|
1793
|
+
|
|
1794
|
+
Builder class for constructing task requests with a fluent API.
|
|
1795
|
+
|
|
1796
|
+
#### Methods
|
|
1797
|
+
|
|
1798
|
+
- `withSkillKey(skillKey: string): this` - Set the skill key
|
|
1799
|
+
- `withInput(input: Record<string, any> | string): this` - Set input data
|
|
1800
|
+
- `withExecutionStrategies(invocations: ExecutionStrategyInvocation[]): this` — MAIN FuncX wrappers (**required** on the built request; defaults to **`[]`** in **`build()`** when unset)
|
|
1801
|
+
- `withExecutionPipeline(steps: ExecutionStep[]): this` - Set execution pipeline (pre/main/post steps)
|
|
1802
|
+
- `withSynthesizedContextPreStep(modelOrConfig?: string | SynthesisConfig): this` - Add synthesized-context PRE step and set `includeContextInPrompt: true`. Pass a **`SynthesisConfig`** object to set **`contextSourcePolicy`**, **`webEvidence`**, **`memoryPaths`**, etc.
|
|
1803
|
+
- `withAuditPostStep(config: AuditConfig): this` - Add audit POST step (quality-gate loop)
|
|
1804
|
+
- `withPolishPostStep(config: PolishConfig): this` - Add polish POST step (refinement checklist)
|
|
1805
|
+
- `withVariables(variables: Record<string, any>): this` - Set contextual variables
|
|
1806
|
+
- `withContext(context: Record<string, any> | string): this` - Set context
|
|
1807
|
+
- `withKnowledge(knowledge: Record<string, any>): this` - Set knowledge data
|
|
1808
|
+
- `withJobMemory(jobMemory: JobHistory): this` - Set job memory
|
|
1809
|
+
- `withTaskMemory(taskMemory: TaskHistory): this` - Set task memory
|
|
1810
|
+
- `withExecutionMemory(executionMemory: ExecutionHistory): this` - Set execution memory
|
|
1811
|
+
- `withJobId(jobId: string): this` - Set job ID
|
|
1812
|
+
- `withAgentId(agentId: string): this` - Set agent ID
|
|
1813
|
+
- `withJobTypeId(jobTypeId: string): this` - Set job type id (**required** before `build()`)
|
|
1814
|
+
- `withTaskTypeId(taskTypeId: string): this` - Set task type id (**required** before `build()`)
|
|
1815
|
+
- `withAiSkillsCorrelation(agentId: string, jobTypeId: string, taskTypeId: string): this` - Set all three correlation ids (recommended)
|
|
1816
|
+
- `withGraphId(graphId: string): this` - Set graph identifier
|
|
1817
|
+
- `withNodeId(nodeId: string): this` - Set node identifier
|
|
1818
|
+
- `withPrevNodeId(prevNodeId: string): this` - Set previous node identifier
|
|
1819
|
+
- `withModelConfig(modelConfig: ModelConfig): this` - Set model configuration (model, temperature, maxTokens, etc.)
|
|
1820
|
+
- `withTemplateRenderOptions(templateRenderOptions: TemplateRenderOptions): this` - Per-task gateway / parser template overrides (v4)
|
|
1821
|
+
- `withTemplateTokens(templateTokens: GatewayTemplateTokens): this` - Per-task `templateTokens` overlay
|
|
1822
|
+
- `withTimeout(timeoutMs: number): this` - Set timeout
|
|
1823
|
+
- `withXynthesized(memory: XynthesizedMemory): this` — optional **`xynthesized`** bucket (**`job`** / **`task`** / **`execution`** records)
|
|
1824
|
+
- `withSmartInput(config: RunTaskSmartInput): this` — passthrough **`smartInput`** (Rendrix **`paths`** or legacy **`string[]`**)
|
|
1825
|
+
- `withSmartInputPaths(paths: string[]): this` — builds **`smartInput: { paths: paths.map(p => ({ title: p, path: p })) }`**
|
|
1826
|
+
- `withSmartInputRenderOptions(opts: SmartInputRenderOptions): this` — sets **`smartInputRenderOptions`** on the request (gateway/Rendrix merge)
|
|
1827
|
+
- `withXynthesizedJob(key: string, value: unknown): this` / `withXynthesizedTask(key: string, value: unknown): this` / `withXynthesizedExecution(key: string, value: unknown): this` — merge a single key into **`xynthesized.job`** / **`xynthesized.task`** / **`xynthesized.execution`**
|
|
1828
|
+
- `build(): RunTaskRequest` - Build the final request (**`executionStrategies`** defaults to **`[]`** when unset)
|
|
1829
|
+
|
|
1830
|
+
#### Example
|
|
1831
|
+
|
|
1832
|
+
```typescript
|
|
1833
|
+
import { TaskRequestBuilder } from "@exellix/ai-tasks";
|
|
1834
|
+
|
|
1835
|
+
const request = new TaskRequestBuilder()
|
|
1836
|
+
.withSkillKey("tasks/my-task")
|
|
1837
|
+
.withInput({ data: "process" })
|
|
1838
|
+
.withAiSkillsCorrelation("my-agent", "my-job-type", "my-task-type")
|
|
1839
|
+
.withExecutionStrategies([]) // [] = plain MAIN; omit only if you rely on build() defaulting strategies to []
|
|
1840
|
+
.withModelConfig({
|
|
1841
|
+
model: "gpt-5",
|
|
1842
|
+
temperature: 0.7,
|
|
1843
|
+
maxTokens: 2000
|
|
1844
|
+
})
|
|
1845
|
+
.withJobMemory(jobMemory)
|
|
1846
|
+
.withTaskMemory(taskMemory)
|
|
1847
|
+
.withExecutionMemory(executionMemory)
|
|
1848
|
+
.withJobId("job-123")
|
|
1849
|
+
.build();
|
|
1850
|
+
|
|
1851
|
+
const result = await tasks.runTask(request);
|
|
1852
|
+
```
|
|
1853
|
+
|
|
1854
|
+
---
|
|
1855
|
+
|
|
1856
|
+
## Types
|
|
1857
|
+
|
|
1858
|
+
### `RunTaskRequest`
|
|
1859
|
+
|
|
1860
|
+
```typescript
|
|
1861
|
+
interface RunTaskRequest {
|
|
1862
|
+
skillKey: string; // "tasks/security-risk-summary"
|
|
1863
|
+
agentId: string; // Required — @exellix/ai-skills correlation
|
|
1864
|
+
jobTypeId: string; // Required — catalog / telemetry job classification
|
|
1865
|
+
taskTypeId: string; // Required — catalog / telemetry task classification
|
|
1866
|
+
/** Required: MAIN FuncX wrappers; use [] for plain gateway MAIN (see BREAKING-CHANGES / RUNTASK_REQUEST). */
|
|
1867
|
+
executionStrategies: ExecutionStrategyInvocation[];
|
|
1868
|
+
/** Optional guarded metadata rows from catalogId `execution-strategy`; code validation remains authoritative. */
|
|
1869
|
+
executionStrategyCatalogItems?: TaskStrategyItemData[];
|
|
1870
|
+
input: Record<string, any> | string; // Task payload (canonical user/host data)
|
|
1871
|
+
executionType?: ExecutionType | string; // Optional; DIRECT default when using plain MAIN; narrix-then-direct when configured
|
|
1872
|
+
narrixInput?: NarrixRunInput | { $path: string }; // Required when narrixMode infers handler without preprocessor narrix
|
|
1873
|
+
narrixScope?: NarrixScope; // When set with narrix handler path, filters which signals/stories go to task memory
|
|
1874
|
+
/** Task-level NARRIX pre-processor: datasetId, enableWebScope, webScope*, webScoping (see [NARRIX task-level pre-processor](#narrix-task-level-pre-processor)). */
|
|
1875
|
+
narrix?: NarrixPreProcessorConfig;
|
|
1876
|
+
includeContextInPrompt?: boolean; // Opt-in: when true, task-scoped context is added to the prompt; default is false (no context)
|
|
1877
|
+
|
|
1878
|
+
/** Optional: pre / main / post steps. When set (non-empty), runs PRE (e.g. synthesized-context), one MAIN, then POST. See [Execution pipeline](#execution-pipeline-and-synthesized-context-pre-step). */
|
|
1879
|
+
executionPipeline?: ExecutionStep[];
|
|
1880
|
+
|
|
1881
|
+
/** executionMode: "trace" — ordered debugTrace + richer LLM observability (see [Trace mode](#trace-mode-authoritative-ordered-debug-trace)). */
|
|
1882
|
+
executionMode?: "default" | "trace";
|
|
1883
|
+
|
|
1884
|
+
/** Merged into template variables before explicit variables (MAIN path). See mergeSkillTemplateVariables. */
|
|
1885
|
+
jobContext?: Record<string, unknown>;
|
|
1886
|
+
|
|
1887
|
+
/** Job-, task-, and execution-scoped synthesized buckets for graph execution (distinct from raw memories). */
|
|
1888
|
+
xynthesized?: XynthesizedMemory;
|
|
1889
|
+
|
|
1890
|
+
/** Rendrix smart-input paths or legacy `paths: string[]` (normalized before MAIN); root allows only `paths`. */
|
|
1891
|
+
smartInput?: RunTaskSmartInput;
|
|
1892
|
+
/** Optional Rendrix options for the `{{smartInput}}` macro (merged on gateway like `RunSkillRequest`). */
|
|
1893
|
+
smartInputRenderOptions?: SmartInputRenderOptions;
|
|
1894
|
+
|
|
1895
|
+
// Optional fields
|
|
1896
|
+
variables?: Record<string, any>; // Contextual variables
|
|
1897
|
+
context?: Record<string, any> | string;
|
|
1898
|
+
knowledge?: Record<string, any>;
|
|
1899
|
+
jobMemory?: JobHistory; // History of previous task results
|
|
1900
|
+
taskMemory?: TaskHistory; // History of skills executed
|
|
1901
|
+
executionMemory?: ExecutionHistory; // History of execution context (execution-level memory)
|
|
1902
|
+
modelConfig?: ModelConfig; // Model configuration (model, temperature, maxTokens, etc.)
|
|
1903
|
+
jobId?: string;
|
|
1904
|
+
|
|
1905
|
+
// Graph execution context (optional)
|
|
1906
|
+
graphId?: string; // Graph identifier for activity tracking
|
|
1907
|
+
nodeId?: string; // Node identifier for activity tracking
|
|
1908
|
+
coreSkillId?: string; // Alternative node identifier (maps to identity.nodeId)
|
|
1909
|
+
|
|
1910
|
+
// Legacy fields (still supported for backward compatibility)
|
|
1911
|
+
masterSkillId?: string; // Legacy graph identifier (maps to identity.graphId)
|
|
1912
|
+
masterSkillActivityId?: string; // Preserved for skill hierarchies
|
|
1913
|
+
|
|
1914
|
+
timeoutMs?: number;
|
|
1915
|
+
|
|
1916
|
+
/** Per-invoke template parser options (merged on gateway defaults). See [Gateway template rendering (v4)](#gateway-template-rendering-v4). */
|
|
1917
|
+
templateRenderOptions?: TemplateRenderOptions;
|
|
1918
|
+
/** Highest-priority template token overlay for this invoke. */
|
|
1919
|
+
templateTokens?: GatewayTemplateTokens;
|
|
1920
|
+
}
|
|
1921
|
+
```
|
|
1922
|
+
|
|
1923
|
+
`NarrixPreProcessorConfig` is exported from this package; **`webScoping`** matches **`WebScoperConfig["scoping"]`** in **`@exellix/narrix-web-scoper`** (snippet toggles, caps, `snippetIncludeRawContent`, `sourceExcerptFrom`, query limits, etc.).
|
|
1924
|
+
|
|
1925
|
+
The closed JSON schema for hosts and codegen is **[documenations/schemas/v1/run-task-request.json](./documenations/schemas/v1/run-task-request.json)** (includes **`xynthesized`** and **`smartInput`**). Narrative contract: **[RUNTASK_REQUEST.md](./RUNTASK_REQUEST.md)**. For **smart input**, the **TypeScript** types exported from this package (**`RunTaskSmartInput`**, **`SmartInputConfig`**) are authoritative; the JSON schema / RUNTASK narrative may still emphasize string **`paths`** only until updated to Rendrix **`{ title, path }`** entries.
|
|
1926
|
+
|
|
1927
|
+
`TemplateRenderOptions` and `GatewayTemplateTokens` are re-exported from this package (see [Gateway template rendering (v4)](#gateway-template-rendering-v4)).
|
|
1928
|
+
|
|
1929
|
+
### `RunTaskResponse<TParsed>`
|
|
1930
|
+
|
|
1931
|
+
ai-tasks extends the skills response with optional **`identity`**, **`xynthesizedPatch`** (**`job`**, **`task`**, and/or **`execution`** writes from PRE synthesis when configured), **`intermediateSteps`**, **`debugTrace`** (when **`executionMode: "trace"`**), and observability-related **`metadata`** extensions. Underlying shape:
|
|
1932
|
+
|
|
1933
|
+
```typescript
|
|
1934
|
+
type RunTaskResponse<TParsed = any> = RunSkillResponse<TParsed> & {
|
|
1935
|
+
/** When downstream returns smart-input diagnostics markdown + resolved paths. */
|
|
1936
|
+
smartInputRenderResult?: SmartInputRenderResult;
|
|
1937
|
+
identity?: Record<string, unknown>;
|
|
1938
|
+
xynthesizedPatch?: XynthesizedMemory;
|
|
1939
|
+
intermediateSteps?: IntermediateStep[];
|
|
1940
|
+
debugTrace?: { tasks: DebugTraceTask[] };
|
|
1941
|
+
aiTasksObservability?: AiTasksObservabilityDigest; // usually attached by orchestrators, not by default
|
|
1942
|
+
};
|
|
1943
|
+
|
|
1944
|
+
interface RunSkillResponse<TParsed = any> {
|
|
1945
|
+
skillKey: string;
|
|
1946
|
+
rawText: string;
|
|
1947
|
+
flexMd: {
|
|
1948
|
+
frame: string;
|
|
1949
|
+
payloads: Record<string, string>;
|
|
1950
|
+
};
|
|
1951
|
+
parsed: TParsed;
|
|
1952
|
+
metadata: {
|
|
1953
|
+
instructionVersion?: string;
|
|
1954
|
+
activityId?: string;
|
|
1955
|
+
durationMs?: number; // Present for local tasks (execution tracing)
|
|
1956
|
+
localSkillKey?: string; // Present for local tasks (e.g. "skills/node.callExportBatch")
|
|
1957
|
+
narrix?: { // Present when narrix-then-direct ran and Narrix succeeded (or failed; see parsed.phase)
|
|
1958
|
+
entity: { entityKind: string; entityKey: string };
|
|
1959
|
+
signals: unknown[];
|
|
1960
|
+
stories: unknown[];
|
|
1961
|
+
meta: Record<string, unknown>;
|
|
1962
|
+
durationMs?: number;
|
|
1963
|
+
};
|
|
1964
|
+
};
|
|
1965
|
+
}
|
|
1966
|
+
```
|
|
1967
|
+
|
|
1968
|
+
Task responses **may** include an optional top-level **`intermediateSteps`** (array of `IntermediateStep`) for combined/multi-step tasks; see [Intermediate steps (multi-step tasks)](#intermediate-steps-multi-step-tasks).
|
|
1969
|
+
|
|
1970
|
+
### `IntermediateStep`, `IntermediateSteps`, `RunTaskResponseWithSteps`
|
|
1971
|
+
|
|
1972
|
+
For multi-step tasks, use the extended response type and step shape:
|
|
1973
|
+
|
|
1974
|
+
```typescript
|
|
1975
|
+
import type { IntermediateStep, IntermediateSteps, RunTaskResponseWithSteps } from "@exellix/ai-tasks";
|
|
1976
|
+
|
|
1977
|
+
interface IntermediateStep {
|
|
1978
|
+
step: number; // 1-based order
|
|
1979
|
+
id: string; // e.g. "to-cni", "engine-enrich"
|
|
1980
|
+
ok: boolean;
|
|
1981
|
+
summary?: string;
|
|
1982
|
+
error?: string;
|
|
1983
|
+
inputSummary?: Record<string, unknown> | string;
|
|
1984
|
+
outputExcerpt?: Record<string, unknown>;
|
|
1985
|
+
}
|
|
1986
|
+
|
|
1987
|
+
type IntermediateSteps = IntermediateStep[];
|
|
1988
|
+
|
|
1989
|
+
interface RunTaskResponseWithSteps<TParsed = any> extends RunSkillResponse<TParsed> {
|
|
1990
|
+
intermediateSteps?: IntermediateSteps;
|
|
1991
|
+
}
|
|
1992
|
+
```
|
|
1993
|
+
|
|
1994
|
+
See [documenations/intermediate-steps.md](./documenations/intermediate-steps.md) for full documentation and examples.
|
|
1995
|
+
|
|
1996
|
+
### `ExecutionPhase`, `ExecutionStep`, `SynthesisConfig`, `ContextSourcePolicy`, `WebEvidenceConfig`, structured synthesis types
|
|
1997
|
+
|
|
1998
|
+
Used with **`executionPipeline`** and the **`synthesized-context`** PRE step. Import from **`@exellix/ai-tasks`** (and use **`SYNTHESIZED_CONTEXT`** for the PRE step `type` string):
|
|
1999
|
+
|
|
2000
|
+
```typescript
|
|
2001
|
+
import type {
|
|
2002
|
+
ExecutionPhase,
|
|
2003
|
+
ExecutionStep,
|
|
2004
|
+
ExecutionStrategyInvocation,
|
|
2005
|
+
SynthesisConfig,
|
|
2006
|
+
ContextSourcePolicy,
|
|
2007
|
+
WebEvidenceConfig,
|
|
2008
|
+
SynthesisInput,
|
|
2009
|
+
SynthesizedPromptPayload,
|
|
2010
|
+
SynthesizedContext,
|
|
2011
|
+
SynthesizedItem,
|
|
2012
|
+
ContextSynthesizer,
|
|
2013
|
+
XynthesizedMemory,
|
|
2014
|
+
SmartInputConfig,
|
|
2015
|
+
SmartInputRenderOptions,
|
|
2016
|
+
SmartInputRenderResult,
|
|
2017
|
+
RunTaskSmartInput,
|
|
2018
|
+
} from "@exellix/ai-tasks";
|
|
2019
|
+
import { SYNTHESIZED_CONTEXT } from "@exellix/ai-tasks";
|
|
2020
|
+
|
|
2021
|
+
// ExecutionPhase = "pre" | "main" | "post"
|
|
2022
|
+
// ExecutionStep: { phase, type: SYNTHESIZED_CONTEXT | "direct" | "audit" | "polish", config? }
|
|
2023
|
+
// SynthesisConfig: … synthesisOutputFormat?: "markdown" | "structured", questionPath?, getQuestion?, structuredMaxItemsPerSide?, …
|
|
2024
|
+
// ContextSourcePolicy: "auto" | "narrix-web" | "narrix-web-memory" | "memory-web" | "memory-only" | "narrix-only" | "narrix+memory"
|
|
2025
|
+
// WebEvidenceConfig: preferCleanContent?, maxSources?, dedupeByUrl?, maxTotalChars?
|
|
2026
|
+
```
|
|
2027
|
+
|
|
2028
|
+
Semantics and the full policy table: [Execution pipeline and synthesized-context](#execution-pipeline-and-synthesized-context-pre-step).
|
|
2029
|
+
|
|
2030
|
+
### `ExecutionType`
|
|
2031
|
+
|
|
2032
|
+
```typescript
|
|
2033
|
+
enum ExecutionType {
|
|
2034
|
+
DIRECT = "direct"
|
|
2035
|
+
}
|
|
2036
|
+
```
|
|
2037
|
+
|
|
2038
|
+
The package also exports the constant `NARRIX_THEN_DIRECT = "narrix-then-direct"` for use with `executionType` when running Narrix first, then the executor. See [Narrix then execute](#narrix-then-execute).
|
|
2039
|
+
|
|
2040
|
+
### `NarrixScope`
|
|
2041
|
+
|
|
2042
|
+
Optional filter for which signals and stories from Narrix are written to task memory (used with `executionType: "narrix-then-direct"`):
|
|
2043
|
+
|
|
2044
|
+
```typescript
|
|
2045
|
+
interface NarrixScope {
|
|
2046
|
+
includeSignals?: string[]; // Signal codes to keep (allowlist)
|
|
2047
|
+
excludeSignals?: string[]; // Signal codes to drop (blocklist; ignored when includeSignals is set)
|
|
2048
|
+
includeStories?: string[]; // narrativeTypeIds to keep (allowlist)
|
|
2049
|
+
excludeStories?: string[]; // narrativeTypeIds to drop (blocklist; ignored when includeStories is set)
|
|
2050
|
+
}
|
|
2051
|
+
```
|
|
2052
|
+
|
|
2053
|
+
### `NarrixPreProcessorConfig`
|
|
2054
|
+
|
|
2055
|
+
Exported type for **`RunTaskRequest.narrix`**. Includes **`datasetId`**, optional **`attachToField`**, **`enableWebScope`**, **`webScopeTemplates`** / **`webScopeQuestionTemplate`** / **`webScopeObjects`** / **`webScopeQuestions`**, and **`webScoping`** (the same shape as **`WebScoperConfig["scoping"]`** in **`@exellix/narrix-web-scoper`** — snippets, caps, raw content, query limits, etc.). See [NARRIX task-level pre-processor](#narrix-task-level-pre-processor) and [documenations/web-scoping-in-ai-tasks.md](./documenations/web-scoping-in-ai-tasks.md).
|
|
2056
|
+
|
|
2057
|
+
```typescript
|
|
2058
|
+
import type { NarrixPreProcessorConfig } from "@exellix/ai-tasks";
|
|
2059
|
+
```
|
|
2060
|
+
|
|
2061
|
+
### `ModelConfig`
|
|
2062
|
+
|
|
2063
|
+
Model configuration for per-request model selection and generation parameters:
|
|
2064
|
+
|
|
2065
|
+
```typescript
|
|
2066
|
+
import type { ModelConfig } from "@exellix/ai-tasks";
|
|
2067
|
+
|
|
2068
|
+
interface ModelConfig {
|
|
2069
|
+
model?: string; // Model identifier (e.g., "gpt-5")
|
|
2070
|
+
modelId?: string; // Provider-specific model ID
|
|
2071
|
+
provider?: string; // Provider name (e.g., "openai", "anthropic")
|
|
2072
|
+
temperature?: number; // 0.0 to 2.0 - Controls randomness
|
|
2073
|
+
maxTokens?: number; // Maximum tokens to generate
|
|
2074
|
+
topP?: number; // 0.0 to 1.0 - Nucleus sampling
|
|
2075
|
+
frequencyPenalty?: number; // -2.0 to 2.0
|
|
2076
|
+
presencePenalty?: number; // -2.0 to 2.0
|
|
2077
|
+
stop?: string[]; // Stop sequences
|
|
2078
|
+
[key: string]: any; // Additional provider-specific parameters
|
|
2079
|
+
}
|
|
2080
|
+
```
|
|
2081
|
+
|
|
2082
|
+
**Example:**
|
|
2083
|
+
```typescript
|
|
2084
|
+
const modelConfig: ModelConfig = {
|
|
2085
|
+
model: "gpt-5",
|
|
2086
|
+
temperature: 0.7,
|
|
2087
|
+
maxTokens: 2000,
|
|
2088
|
+
topP: 0.9
|
|
2089
|
+
};
|
|
2090
|
+
|
|
2091
|
+
const result = await runTask({
|
|
2092
|
+
skillKey: "tasks/analysis",
|
|
2093
|
+
executionType: ExecutionType.DIRECT,
|
|
2094
|
+
input: { data: "analyze" },
|
|
2095
|
+
modelConfig
|
|
2096
|
+
});
|
|
2097
|
+
```
|
|
2098
|
+
|
|
2099
|
+
**Note:** `ModelConfig` is re-exported from `@woroces/ai-skills`. If not provided, uses gateway/router defaults from client initialization.
|
|
2100
|
+
|
|
2101
|
+
### `JobHistory`, `TaskHistory`, and `ExecutionHistory`
|
|
2102
|
+
|
|
2103
|
+
History objects for managing execution context across workflows:
|
|
2104
|
+
|
|
2105
|
+
```typescript
|
|
2106
|
+
import type { JobHistory, TaskHistory, ExecutionHistory } from "@exellix/ai-tasks";
|
|
2107
|
+
|
|
2108
|
+
// JobHistory: Contains history of all previous task results in the job
|
|
2109
|
+
const jobMemory: JobHistory = {
|
|
2110
|
+
// Previous task results
|
|
2111
|
+
};
|
|
2112
|
+
|
|
2113
|
+
// TaskHistory: Contains history of skills executed up to this task
|
|
2114
|
+
const taskMemory: TaskHistory = {
|
|
2115
|
+
// Previous skill executions
|
|
2116
|
+
};
|
|
2117
|
+
|
|
2118
|
+
// ExecutionHistory: Contains history of execution context (execution-level memory)
|
|
2119
|
+
const executionMemory: ExecutionHistory = {
|
|
2120
|
+
// Execution state, intermediate results, execution-level variables
|
|
2121
|
+
previousAttempts: 2,
|
|
2122
|
+
lastError: "timeout",
|
|
2123
|
+
intermediateResults: { step1: "completed" }
|
|
2124
|
+
};
|
|
2125
|
+
```
|
|
2126
|
+
|
|
2127
|
+
- `JobHistory` and `TaskHistory` are re-exported from `@x12i/execution-memory-manager` for convenience
|
|
2128
|
+
- `ExecutionHistory` is defined in this package as `Record<string, any>` for execution-level memory
|
|
2129
|
+
|
|
2130
|
+
See [HISTORY-OBJECTS.md](./HISTORY-OBJECTS.md) for detailed documentation on history objects and [HISTORY-OBJECTS-DOWNSTREAM.md](./HISTORY-OBJECTS-DOWNSTREAM.md) for information on how they flow downstream.
|
|
2131
|
+
|
|
2132
|
+
---
|
|
2133
|
+
|
|
2134
|
+
## Content Registry Structure (v2.0.2+)
|
|
2135
|
+
|
|
2136
|
+
To ensure robust resolution with `@x12i/ai-gateway` v7.0.0+, it is recommended to provide both `.instructions` and `.prompt` files for each skill in your `.metadata/skills` directory.
|
|
2137
|
+
|
|
2138
|
+
- **`{skillId}.instructions`**: Contains the system instructions (persona, constraints, output format).
|
|
2139
|
+
- **`{skillId}.prompt`**: Contains the user prompt template (e.g., `{{input}}`). With gateway template **v4**, `{{input}}` is supplied from merged working memory (see [Gateway template rendering (v4)](#gateway-template-rendering-v4)); use **`templateRenderOptions.subPathSearch`** when templates need paths like **`{{execution.summary}}`** backed by nested task input.
|
|
2140
|
+
|
|
2141
|
+
See [SKILL-CONTENT-GUIDE.md](./SKILL-CONTENT-GUIDE.md) for a detailed migration and implementation guide.
|
|
2142
|
+
|
|
2143
|
+
---
|
|
2144
|
+
|
|
2145
|
+
## Environment Variables
|
|
2146
|
+
|
|
2147
|
+
See `.env.example` for complete configuration. Key variables:
|
|
2148
|
+
|
|
2149
|
+
**Used by ai-tasks:**
|
|
2150
|
+
|
|
2151
|
+
- `MONGO_LOGS_DB` - [OPTIONAL] Default database for activity logs (defaults to `logs-db`), passed as `bindingDefaultsDb` to the skills client.
|
|
2152
|
+
|
|
2153
|
+
**POST step (shared):**
|
|
2154
|
+
|
|
2155
|
+
- `POST_STEP_MODEL` - [OPTIONAL] Fallback model for all POST step LLM calls when no step-specific env is set.
|
|
2156
|
+
- `POST_STEP_TIMEOUT_MS` - [OPTIONAL] Fallback timeout in ms for POST step LLM calls (default `30000`).
|
|
2157
|
+
|
|
2158
|
+
**Audit POST step:**
|
|
2159
|
+
|
|
2160
|
+
- `AUDIT_MODEL` - [OPTIONAL] Model for audit evaluator (→ `POST_STEP_MODEL` → gateway default).
|
|
2161
|
+
- `AUDIT_SYNTHESIS_MODEL` - [OPTIONAL] Model for synthesis merge when `selectionStrategy: "synthesis"` (→ `POST_STEP_MODEL` → MAIN step model).
|
|
2162
|
+
- `AUDIT_TIMEOUT_MS` - [OPTIONAL] Timeout per audit LLM call (→ `POST_STEP_TIMEOUT_MS` → `30000`).
|
|
2163
|
+
- `AUDIT_TEMPLATES_PATH` - [OPTIONAL] Base path for audit templates (default `<project>/templates/post-steps/audit`).
|
|
2164
|
+
|
|
2165
|
+
**Polish POST step:**
|
|
2166
|
+
|
|
2167
|
+
- `POLISH_MODEL` - [OPTIONAL] Model for polish LLM (→ `POST_STEP_MODEL` → gateway default).
|
|
2168
|
+
- `POLISH_TIMEOUT_MS` - [OPTIONAL] Timeout per polish call (→ `POST_STEP_TIMEOUT_MS` → `30000`).
|
|
2169
|
+
- `POLISH_TEMPLATES_PATH` - [OPTIONAL] Base path for polish templates (default `<project>/templates/post-steps/polish`).
|
|
2170
|
+
|
|
2171
|
+
**Required for content and providers (validated by ai-skills):**
|
|
2172
|
+
|
|
2173
|
+
- `GITHUB_TOKEN` - [REQUIRED] GitHub token for content registry access
|
|
2174
|
+
- `GITHUB_REPO_URL` - [REQUIRED] Content registry repository URL
|
|
2175
|
+
- `OPENAI_API_KEY` or `GROK_API_KEY` - [REQUIRED] AI provider key (or `OPEN_ROUTER_KEY` / `OPENROUTER_API_KEY` if ai-skills accepts OpenRouter-only)
|
|
2176
|
+
|
|
2177
|
+
**Downstream (used by ai-skills → ai-gateway → ai-provider-router, ai-activities-tracking):** ai-tasks does not read these; they affect LLM and activity tracking when using this package.
|
|
2178
|
+
|
|
2179
|
+
- `OPEN_ROUTER_KEY` or `OPENROUTER_API_KEY` - [OPTIONAL] Use OpenRouter for LLM calls; no provider registration needed when set.
|
|
2180
|
+
- `MONGO_LOGS_COLLECTION` - [OPTIONAL] Activities collection (default in ai-activities-tracking: `cognitive-activities`).
|
|
2181
|
+
- `MONGO_BAD_REQUESTS_COLLECTION` - [OPTIONAL] Bad-requests collection (default: `ai-bad-requests`).
|
|
2182
|
+
|
|
2183
|
+
**Synthesis (synthesized-context PRE step):**
|
|
2184
|
+
|
|
2185
|
+
- `SYNTHESIS_MODEL` - [OPTIONAL] Default model for synthesis (e.g. `gpt-5-nano`).
|
|
2186
|
+
- `SYNTHESIS_TIMEOUT_MS` - [OPTIONAL] Timeout for synthesis call (default 30000).
|
|
2187
|
+
- `SYNTHESIS_MAX_OUTPUT_LENGTH` - [OPTIONAL] Max length of synthesis output (characters).
|
|
2188
|
+
- `SYNTHESIS_TEMPLATES_PATH` - [OPTIONAL] Base path for `templates/synthesis/system.md` and `user.txt` (default: project root).
|
|
2189
|
+
|
|
2190
|
+
**Other:**
|
|
2191
|
+
|
|
2192
|
+
- `MONGODB_URI` - [OPTIONAL] MongoDB for context enrichment
|
|
2193
|
+
- `CONTENT_REGISTRY_LOCAL_PATH` - [OPTIONAL] Local registry path
|
|
2194
|
+
- `USE_NARRIX_INGEST` - [OPTIONAL] Set to `"1"` to enable the Narrix pipeline for `skills/skill.local:narrixRun` (record/text/docs/chat). When unset, the handler returns `NARRIX_DISABLED`.
|
|
2195
|
+
- `NARRIX_DEBUG` - [OPTIONAL] Set to `"1"` to log datasetId, medium, packId, entityKind, signal codes, and narrativeTypeIds when the Narrix handler runs.
|
|
2196
|
+
- `TAVILY_API_KEY` - [OPTIONAL] Required when using **`narrix.enableWebScope`** (web scoping via `@exellix/narrix-web-scoper` + `@exellix/search-adapter` / Tavily). When missing or invalid, `executionMemory.webContext` reflects a miss or error; the NARRIX task still completes.
|
|
2197
|
+
- `WEB_CONTEXT_MARKDOWN_MAX_CHARS` - [OPTIONAL] Positive integer: max Unicode length of the **Web sources** markdown block appended to task context / synthesis source material (tail truncated with a marker). Omit for no downstream cap (upstream **`maxTotalWebContextChars`** / **`maxSnippetCharsPerSource`** still apply in the scoper).
|
|
2198
|
+
|
|
2199
|
+
For a concise breakdown of what ai-tasks uses vs downstream, see [documenations/downstream-environment.md](./documenations/downstream-environment.md).
|
|
2200
|
+
|
|
2201
|
+
---
|
|
2202
|
+
|
|
2203
|
+
## Examples
|
|
2204
|
+
|
|
2205
|
+
### Basic Usage
|
|
2206
|
+
|
|
2207
|
+
```typescript
|
|
2208
|
+
import { WorexClientSkills } from "@woroces/ai-skills";
|
|
2209
|
+
import { WorexClientTasks, ExecutionType } from "@exellix/ai-tasks";
|
|
2210
|
+
|
|
2211
|
+
const skills = new WorexClientSkills();
|
|
2212
|
+
const executor = { /* get from skills client */ };
|
|
2213
|
+
const tasks = new WorexClientTasks(skills, executor);
|
|
2214
|
+
|
|
2215
|
+
const result = await tasks.runTask({
|
|
2216
|
+
skillKey: "tasks/security-risk-summary",
|
|
2217
|
+
executionType: ExecutionType.DIRECT,
|
|
2218
|
+
input: { assetId: "a-123" },
|
|
2219
|
+
variables: { orgName: "MyOrganization" },
|
|
2220
|
+
modelConfig: {
|
|
2221
|
+
model: "gpt-5",
|
|
2222
|
+
temperature: 0.7
|
|
2223
|
+
}
|
|
2224
|
+
});
|
|
2225
|
+
|
|
2226
|
+
console.log(result.flexMd.payloads);
|
|
2227
|
+
```
|
|
2228
|
+
|
|
2229
|
+
### With Memory Management
|
|
2230
|
+
|
|
2231
|
+
```typescript
|
|
2232
|
+
import { WorexClientSkills } from "@woroces/ai-skills";
|
|
2233
|
+
import { WorexClientTasks, ExecutionType } from "@exellix/ai-tasks";
|
|
2234
|
+
import type { JobHistory, TaskHistory, ExecutionHistory } from "@exellix/ai-tasks";
|
|
2235
|
+
|
|
2236
|
+
const skills = new WorexClientSkills();
|
|
2237
|
+
const executor = { /* get from skills client */ };
|
|
2238
|
+
const tasks = new WorexClientTasks(skills, executor);
|
|
2239
|
+
|
|
2240
|
+
const jobMemory: JobHistory = {
|
|
2241
|
+
// Previous task results
|
|
2242
|
+
};
|
|
2243
|
+
|
|
2244
|
+
const taskMemory: TaskHistory = {
|
|
2245
|
+
// Previous skill executions
|
|
2246
|
+
};
|
|
2247
|
+
|
|
2248
|
+
const executionMemory: ExecutionHistory = {
|
|
2249
|
+
previousAttempts: 1,
|
|
2250
|
+
intermediateResults: { analysis: "in-progress" }
|
|
2251
|
+
};
|
|
2252
|
+
|
|
2253
|
+
const result = await tasks.runTask({
|
|
2254
|
+
skillKey: "tasks/professional-decision",
|
|
2255
|
+
executionType: ExecutionType.DIRECT,
|
|
2256
|
+
input: { scenario: "Should we migrate to cloud?" },
|
|
2257
|
+
variables: { orgName: "EnterpriseCorp" },
|
|
2258
|
+
jobMemory,
|
|
2259
|
+
taskMemory,
|
|
2260
|
+
executionMemory,
|
|
2261
|
+
jobId: "job-456"
|
|
2262
|
+
});
|
|
2263
|
+
```
|
|
2264
|
+
|
|
2265
|
+
### With Model Configuration
|
|
2266
|
+
|
|
2267
|
+
```typescript
|
|
2268
|
+
import { WorexClientSkills } from "@woroces/ai-skills";
|
|
2269
|
+
import { WorexClientTasks, ExecutionType } from "@exellix/ai-tasks";
|
|
2270
|
+
|
|
2271
|
+
const skills = new WorexClientSkills();
|
|
2272
|
+
const executor = { /* get from skills client */ };
|
|
2273
|
+
const tasks = new WorexClientTasks(skills, executor);
|
|
2274
|
+
|
|
2275
|
+
// Configure model per-request (@exellix/ai-skills correlation ids required)
|
|
2276
|
+
const result = await tasks.runTask({
|
|
2277
|
+
skillKey: "tasks/analysis",
|
|
2278
|
+
agentId: "agent-1",
|
|
2279
|
+
jobTypeId: "analysis-job",
|
|
2280
|
+
taskTypeId: "analysis-task",
|
|
2281
|
+
executionStrategies: [],
|
|
2282
|
+
executionType: ExecutionType.DIRECT,
|
|
2283
|
+
input: { data: "analyze this" },
|
|
2284
|
+
modelConfig: {
|
|
2285
|
+
model: "gpt-5",
|
|
2286
|
+
temperature: 0.7,
|
|
2287
|
+
maxTokens: 2000,
|
|
2288
|
+
topP: 0.9
|
|
2289
|
+
}
|
|
2290
|
+
});
|
|
2291
|
+
|
|
2292
|
+
// Or using the builder
|
|
2293
|
+
import { TaskRequestBuilder } from "@exellix/ai-tasks";
|
|
2294
|
+
|
|
2295
|
+
const request = new TaskRequestBuilder()
|
|
2296
|
+
.withSkillKey("tasks/analysis")
|
|
2297
|
+
.withInput({ data: "analyze this" })
|
|
2298
|
+
.withAiSkillsCorrelation("agent-1", "analysis-job", "analysis-task")
|
|
2299
|
+
.withExecutionStrategies([])
|
|
2300
|
+
.withModelConfig({
|
|
2301
|
+
model: "gpt-5",
|
|
2302
|
+
temperature: 0.5,
|
|
2303
|
+
maxTokens: 4000
|
|
2304
|
+
})
|
|
2305
|
+
.build();
|
|
2306
|
+
|
|
2307
|
+
const result2 = await tasks.runTask(request);
|
|
2308
|
+
```
|
|
2309
|
+
|
|
2310
|
+
### With Graph Execution Context
|
|
2311
|
+
|
|
2312
|
+
```typescript
|
|
2313
|
+
import { WorexClientSkills } from "@woroces/ai-skills";
|
|
2314
|
+
import { WorexClientTasks, ExecutionType } from "@exellix/ai-tasks";
|
|
2315
|
+
|
|
2316
|
+
const skills = new WorexClientSkills();
|
|
2317
|
+
const executor = { /* get from skills client */ };
|
|
2318
|
+
const tasks = new WorexClientTasks(skills, executor);
|
|
2319
|
+
|
|
2320
|
+
// Execute a task within a graph workflow
|
|
2321
|
+
const result = await tasks.runTask({
|
|
2322
|
+
skillKey: "tasks/security-risk-summary",
|
|
2323
|
+
executionType: ExecutionType.DIRECT,
|
|
2324
|
+
input: { assetId: "a-123" },
|
|
2325
|
+
variables: { orgName: "TechCorp" },
|
|
2326
|
+
modelConfig: {
|
|
2327
|
+
model: "gpt-5",
|
|
2328
|
+
temperature: 0.7
|
|
2329
|
+
},
|
|
2330
|
+
jobId: "job-123",
|
|
2331
|
+
agentId: "agent-1",
|
|
2332
|
+
graphId: "workflow-data-processing", // Graph identifier for activity tracking
|
|
2333
|
+
nodeId: "extract-emails-node" // Node identifier for activity tracking
|
|
2334
|
+
});
|
|
2335
|
+
|
|
2336
|
+
console.log(result.flexMd.payloads);
|
|
2337
|
+
// Graph and node context are automatically passed through to activity tracking
|
|
2338
|
+
```
|
|
2339
|
+
|
|
2340
|
+
---
|
|
2341
|
+
|
|
2342
|
+
## Activix — task activity tracking
|
|
2343
|
+
|
|
2344
|
+
`@xronoces/activix` is used here for multi-phase activity tracking: each `runTask()` invocation writes several phase records to MongoDB (`local`, `narrix`, `pipeline_pre`, `direct`, `audit`, `polish`, `narrix_then_direct`). All phase records share the same `correlationId`, so you can group them back into one logical task run.
|
|
2345
|
+
|
|
2346
|
+
**Best practices checklist:** see [documenations/activix.md](./documenations/activix.md) (includes an operational integration checklist + deeper references under `.docs/`).
|
|
2347
|
+
|
|
2348
|
+
### Enable / disable
|
|
2349
|
+
|
|
2350
|
+
```
|
|
2351
|
+
ACTIVIX_ENABLED=false # set true to activate
|
|
2352
|
+
```
|
|
2353
|
+
|
|
2354
|
+
When `false` (the default), `runTask()` runs exactly as today with zero overhead. All activix calls are non-fatal — a MongoDB outage will never surface to the `runTask()` caller.
|
|
2355
|
+
|
|
2356
|
+
### Configuration
|
|
2357
|
+
|
|
2358
|
+
| Variable | Default | Description |
|
|
2359
|
+
|---|---|---|
|
|
2360
|
+
| `ACTIVIX_ENABLED` | `false` | Master toggle |
|
|
2361
|
+
| `ACTIVIX_DB` | `MONGO_LOGS_DB` | MongoDB database for activity records |
|
|
2362
|
+
| `ACTIVIX_COLLECTION` | `task-activities` | Collection name |
|
|
2363
|
+
| `ACTIVIX_STALE_TTL_MS` | `300000` | TTL (ms) before a stuck `started` record is marked `timeout` |
|
|
2364
|
+
| `ACTIVIX_LOGS_LEVEL` | _(see below)_ | Canonical internal log threshold for injected Activix logger (`@x12i/logxer`). If unset, falls back to `ACTIVIX_LOG_LEVEL`, then default `warn`. Values: `verbose` \| `debug` \| `info` \| `warn` \| `error` \| `off` / `silent` / `none`. |
|
|
2365
|
+
| `ACTIVIX_LOG_LEVEL` | `warn` | Legacy alias when `ACTIVIX_LOGS_LEVEL` is unset (same resolution rules as `@x12i/logxer` `resolvePackageLogsLevel`). |
|
|
2366
|
+
|
|
2367
|
+
### What is recorded
|
|
2368
|
+
|
|
2369
|
+
Each record in `ACTIVIX_COLLECTION` captures:
|
|
2370
|
+
|
|
2371
|
+
- `correlationId`, `phase` — join key for grouping phase records into one `runTask()` invocation
|
|
2372
|
+
- `runContext` (Activix 6+) — correlation envelope: `sessionId` matches `correlationId`, plus task fields that used to be duplicated as a top-level `identity` blob (`taskId`, `skillId`, etc.) now live here only
|
|
2373
|
+
- `outer` (Activix 6+) — required activity I/O tier (`input` / `output` / `metadata`); phase tracking uses minimal placeholders until completion metadata is merged
|
|
2374
|
+
- `skillKey`, `executionType`, `jobId`, `agentId`, `graphId`, `nodeId` — from the request
|
|
2375
|
+
- `hasPipeline`, `hasNarrix`, `hasNarrixInput` — execution path flags
|
|
2376
|
+
- `status` — `started` → `completed` | `failed` | `timeout`
|
|
2377
|
+
- `startTime`, `endTime`, `duration` — auto-managed by activix
|
|
2378
|
+
- `instructionVersion`, `durationMs`, `stepCount` — from the response on success
|
|
2379
|
+
- `error` / `errorMessage`, `errorCode` — on failure
|
|
2380
|
+
|
|
2381
|
+
### Stale record cleanup
|
|
2382
|
+
|
|
2383
|
+
Crashed or hung tasks leave records stuck in `started`. Call `ax.markStaleRecords()` from a periodic job (e.g. every 5 minutes) in the host process to flip them to `timeout`.
|
|
2384
|
+
|
|
2385
|
+
**Full integration plan → [documenations/activix.md](./documenations/activix.md)**
|
|
2386
|
+
|
|
2387
|
+
---
|
|
2388
|
+
|
|
2389
|
+
## Development Notes
|
|
2390
|
+
|
|
2391
|
+
- This is a **private package**; we optimize for reuse and speed.
|
|
2392
|
+
- `@woroces/ai-skills` is treated as the shared helper + execution layer.
|
|
2393
|
+
- Naming leakage is explicitly acceptable (private monorepo + private packages).
|
|
2394
|
+
- Task-level enrichment always uses `level: 'task'` (not `'skill'`).
|
|
2395
|
+
- The executor must be a "no-enrichment execute primitive" to avoid double-enrichment.
|
|
2396
|
+
|
|
2397
|
+
---
|
|
2398
|
+
|
|
2399
|
+
## Troubleshooting
|
|
2400
|
+
|
|
2401
|
+
### "Content not found" Error
|
|
2402
|
+
|
|
2403
|
+
- Verify skill key format: `"tasks/security-risk-summary"` (not `"security-risk-summary"`)
|
|
2404
|
+
- Check content registry contains the task
|
|
2405
|
+
- Verify `GITHUB_TOKEN` and `GITHUB_REPO_URL` are set correctly
|
|
2406
|
+
|
|
2407
|
+
### "Repository not accessible"
|
|
2408
|
+
|
|
2409
|
+
- Verify `GITHUB_TOKEN` has `repo` scope for private repos
|
|
2410
|
+
- Check `GITHUB_REPO_URL` points to an existing repository
|
|
2411
|
+
|
|
2412
|
+
### "Environment validation failed"
|
|
2413
|
+
|
|
2414
|
+
- Ensure at least one AI provider key is set (`OPENAI_API_KEY` or `GROK_API_KEY`)
|
|
2415
|
+
- Verify `GITHUB_TOKEN` and `GITHUB_REPO_URL` are set
|
|
2416
|
+
- Check `.env` file is loaded (if using `dotenv`)
|
|
2417
|
+
|
|
2418
|
+
---
|
|
2419
|
+
|
|
2420
|
+
## Development
|
|
2421
|
+
|
|
2422
|
+
```bash
|
|
2423
|
+
# Install dependencies (requires @exellix scope registry/auth for narrix packages)
|
|
2424
|
+
npm install
|
|
2425
|
+
|
|
2426
|
+
# Build
|
|
2427
|
+
npm run build
|
|
2428
|
+
|
|
2429
|
+
# Run tests (all narrix tests under test/narrix/; requires build first)
|
|
2430
|
+
npm test
|
|
2431
|
+
|
|
2432
|
+
# Run full Narrix integration tests (record, text, docs, chat + 14 record scenarios)
|
|
2433
|
+
npm run test:with-narrix-ingest
|
|
2434
|
+
# Bash/macOS/Linux alternative: USE_NARRIX_INGEST=1 npm test
|
|
2435
|
+
# PowerShell: $env:USE_NARRIX_INGEST='1'; npm test
|
|
2436
|
+
|
|
2437
|
+
# Run real E2E for intermediateSteps (real executor + real skill test-intermediate-steps; requires LLM + gateway env like other E2Es)
|
|
2438
|
+
# Also requires Catalox/Firebase: FIREBASE_PROJECT_ID and GOOGLE_SERVICE_ACCOUNT_BASE64 (tests load repo-root `.env` first; skip gate requires both to be set).
|
|
2439
|
+
npm run test:e2e:intermediateSteps
|
|
2440
|
+
# Bash alternative: RUN_INTERMEDIATE_STEPS_E2E=1 npm test
|
|
2441
|
+
|
|
2442
|
+
# Run real E2E for synthesized-context (requires a provider API key in .env)
|
|
2443
|
+
# Keys recognized by the gate: OPENROUTER_API_KEY, OPENAI_API_KEY, OPENAI_KEY, OPEN_ROUTER_KEY, GROK_API_KEY.
|
|
2444
|
+
# Optional: SYNTHESIS_MODEL, LLM_MODEL_STRONG, LLM_MODEL_NORMAL, LLM_MODEL, POST_STEP_MODEL (else gateway default).
|
|
2445
|
+
# Runs three opt-in tests in test/synthesis/e2e-synthesis-real.test.ts:
|
|
2446
|
+
# (1) default markdown synthesis PRE step + main skill,
|
|
2447
|
+
# (2) runStructuredSynthesisGatewayCall (real gateway JSON → parse → buildSynthesizedContextMarkdown),
|
|
2448
|
+
# (3) synthesisOutputFormat: "structured" PRE + main skill (memory-only source + input.question).
|
|
2449
|
+
# The synthesis model must follow JSON instructions for (2) and (3). Uses ~3 synthesis + ~2 main LLM calls.
|
|
2450
|
+
RUN_SYNTHESIS_E2E=1 npm test
|
|
2451
|
+
# Or: npm run test:e2e:synthesis
|
|
2452
|
+
#
|
|
2453
|
+
# Xynthesis + Mongo / Activix + sidekick hardening: documenations/xynthesis-upstream-fixes-checklist.md
|
|
2454
|
+
|
|
2455
|
+
# Development mode
|
|
2456
|
+
npm run dev
|
|
2457
|
+
```
|
|
2458
|
+
|
|
2459
|
+
**Narrix tests:** When `USE_NARRIX_INGEST` is not set, only the "feature flag off" test runs; the rest are skipped. Use **`npm run test:with-narrix-ingest`** (or set the env var per shell above) to run contract and integration tests; live catalog tests also need `.archive/packages/…` (see **BREAKING-CHANGES-NARRIX-V5.md**). Optional: `NARRIX_DEBUG=1` for verbose handler logging.
|
|
2460
|
+
|
|
2461
|
+
**NARRIX task-level pre-processor tests:** `test/narrix/preProcessorAdapter.integration.test.ts` (adapter produces record from jobMemory/executionMemory/input shapes), `test/narrix/buildNarrixAttachment.test.ts` (attachment from runner result with/without passes), and `test/narrix/preProcessorE2E.test.ts` (throw when `narrix` set but no record; e2e handler receives `ctx.executionMemory._narrix` when ingest is enabled and the narrix seed archive is present).
|
|
2462
|
+
|
|
2463
|
+
---
|
|
2464
|
+
|
|
2465
|
+
## License
|
|
2466
|
+
|
|
2467
|
+
ISC
|
|
2468
|
+
|
|
2469
|
+
## Repository
|
|
2470
|
+
|
|
2471
|
+
[GitHub](https://github.com/exellix/ai-tasks)
|
|
2472
|
+
|
|
2473
|
+
## Related Packages
|
|
2474
|
+
|
|
2475
|
+
- [`@woroces/ai-skills`](https://github.com/woroces/ai-skills) - Full SDK with additional features
|
|
2476
|
+
- [`@x12i/ai-gateway`](https://github.com/athenices/ai-gateway) - AI Gateway for LLM interactions
|
|
2477
|
+
- [`@x12i/execution-memory-manager`](https://github.com/athenices/execution-memory-manager) - Memory management utilities
|
|
2478
|
+
|
|
2479
|
+
## Related Documentation
|
|
2480
|
+
|
|
2481
|
+
- [@woroces/ai-skills — GATEWAY_TEMPLATE_PROTOCOL_V4.md](https://github.com/woroces/ai-skills/blob/main/GATEWAY_TEMPLATE_PROTOCOL_V4.md) — Gateway + parser template protocol (MUST tokens, `subPathSearch`, errors)
|
|
2482
|
+
- [documenations/intermediate-steps.md](./documenations/intermediate-steps.md) - Structured intermediate results for combined/multi-step tasks (`intermediateSteps` response field)
|
|
2483
|
+
- [HISTORY-OBJECTS.md](./HISTORY-OBJECTS.md) - Comprehensive guide to `JobHistory`, `TaskHistory`, and `ExecutionHistory` objects
|
|
2484
|
+
- [HISTORY-OBJECTS-DOWNSTREAM.md](./HISTORY-OBJECTS-DOWNSTREAM.md) - How history objects flow downstream through the execution pipeline
|
|
2485
|
+
- [GRAPH-EXECUTION-SUPPORT.md](./GRAPH-EXECUTION-SUPPORT.md) - Graph execution context and activity tracking
|
|
2486
|
+
- [MODEL-CONFIGURATION.md](./MODEL-CONFIGURATION.md) - Complete guide to model configuration in `runTask()`
|
|
2487
|
+
- [NARRIX-FOUR-OBJECTS-AND-USE-CASES.md](./NARRIX-FOUR-OBJECTS-AND-USE-CASES.md) - The four Narrix input objects (record, text, docs, chat), payload shapes, and use cases (local narrixRun, narrix-then-direct, normalize, validate)
|
|
2488
|
+
- [NARRIX-STATUS-REPORT.md](./NARRIX-STATUS-REPORT.md) - Narrix pipeline status: unified handler (record/text/docs/chat), resolver, runners, and tests
|
|
2489
|
+
- [documenations/web-scoping-in-ai-tasks.md](./documenations/web-scoping-in-ai-tasks.md) - Web scoping in the task SDK
|
|
2490
|
+
- [documenations/run-task-execution-flow.md](./documenations/run-task-execution-flow.md) - Step-by-step current `runTask()` behavior and flow diagram
|
|
2491
|
+
- [documenations/schemas/README.md](./documenations/schemas/README.md) - JSON Schema / OpenAPI 3.1 fragments for `outputValidation` and PRE synthesis `executionMemory` fields
|
|
2492
|
+
- [documenations/core-runtime-tokens-and-strategies.md](./documenations/core-runtime-tokens-and-strategies.md) - Core token handling and strategy behavior overview
|
|
2493
|
+
- [documenations/synthesized-context-guide.md](./documenations/synthesized-context-guide.md) - User-facing guide for `synthesized-context` PRE-step behavior and controls
|
|
2494
|
+
- [documenations/task-core-and-core-aware-synthesis.md](./documenations/task-core-and-core-aware-synthesis.md) - Template core token model and structured synthesis behavior
|
|
2495
|
+
- [documenations/synthesis-invocation-notes.md](./documenations/synthesis-invocation-notes.md) - Notes on synthesis call behavior, cost, and operational implications
|
|
2496
|
+
- [documenations/activix.md](./documenations/activix.md) - Activix integration plan: task activity lifecycle tracking, record schema, env config, execution path mapping, and stale cleanup: `enableWebScope`, `narrix.webScoping` vs `@exellix/narrix-web-scoper` / `@exellix/search-adapter`, `executionMemory.webContext`, source snippets
|
|
2497
|
+
- [documenations/downstream-test-runtime-teardown-cleanup.md](./documenations/downstream-test-runtime-teardown-cleanup.md) - Downstream `ai-skills` lifecycle cleanup note for open handles/process-exit hygiene in tests
|