@exellix/ai-tasks 8.1.10 → 8.1.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/.metadata/log-diagnostics.json +76 -0
  2. package/CHANGELOG.md +4 -0
  3. package/README.md +4 -2
  4. package/dist/core/task-sdk.d.ts +7 -1
  5. package/dist/core/task-sdk.d.ts.map +1 -1
  6. package/dist/core/task-sdk.js +1177 -1165
  7. package/dist/core/task-sdk.js.map +1 -1
  8. package/dist/execution-strategies/resolveExecutionStrategies.d.ts.map +1 -1
  9. package/dist/execution-strategies/resolveExecutionStrategies.js +12 -3
  10. package/dist/execution-strategies/resolveExecutionStrategies.js.map +1 -1
  11. package/dist/index.d.ts +10 -1
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +6 -0
  14. package/dist/index.js.map +1 -1
  15. package/dist/internal/runPostStepLlmCall.d.ts +3 -0
  16. package/dist/internal/runPostStepLlmCall.d.ts.map +1 -1
  17. package/dist/internal/runPostStepLlmCall.js +2 -0
  18. package/dist/internal/runPostStepLlmCall.js.map +1 -1
  19. package/dist/logxer/aiTasksDiagnosticCodes.d.ts +12 -0
  20. package/dist/logxer/aiTasksDiagnosticCodes.d.ts.map +1 -0
  21. package/dist/logxer/aiTasksDiagnosticCodes.js +11 -0
  22. package/dist/logxer/aiTasksDiagnosticCodes.js.map +1 -0
  23. package/dist/logxer/aiTasksLogContext.d.ts +16 -0
  24. package/dist/logxer/aiTasksLogContext.d.ts.map +1 -0
  25. package/dist/logxer/aiTasksLogContext.js +27 -0
  26. package/dist/logxer/aiTasksLogContext.js.map +1 -0
  27. package/dist/logxer/configureAiTasksLogging.d.ts +16 -0
  28. package/dist/logxer/configureAiTasksLogging.d.ts.map +1 -0
  29. package/dist/logxer/configureAiTasksLogging.js +19 -0
  30. package/dist/logxer/configureAiTasksLogging.js.map +1 -0
  31. package/dist/logxer/packageLogxers.d.ts +44 -2
  32. package/dist/logxer/packageLogxers.d.ts.map +1 -1
  33. package/dist/logxer/packageLogxers.js +95 -9
  34. package/dist/logxer/packageLogxers.js.map +1 -1
  35. package/dist/logxer/stackLoggingContext.d.ts +7 -0
  36. package/dist/logxer/stackLoggingContext.d.ts.map +1 -0
  37. package/dist/logxer/stackLoggingContext.js +20 -0
  38. package/dist/logxer/stackLoggingContext.js.map +1 -0
  39. package/dist/narrix/task.d.ts.map +1 -1
  40. package/dist/narrix/task.js +2 -1
  41. package/dist/narrix/task.js.map +1 -1
  42. package/dist/packaged-tasks-client.d.ts +4 -0
  43. package/dist/packaged-tasks-client.d.ts.map +1 -1
  44. package/dist/packaged-tasks-client.js +11 -3
  45. package/dist/packaged-tasks-client.js.map +1 -1
  46. package/dist/synthesis/runStructuredSynthesisRobust.d.ts +2 -0
  47. package/dist/synthesis/runStructuredSynthesisRobust.d.ts.map +1 -1
  48. package/dist/synthesis/runStructuredSynthesisRobust.js +5 -2
  49. package/dist/synthesis/runStructuredSynthesisRobust.js.map +1 -1
  50. package/dist/utilities/runUtility.d.ts.map +1 -1
  51. package/dist/utilities/runUtility.js +5 -1
  52. package/dist/utilities/runUtility.js.map +1 -1
  53. package/dist/utils/runTaskRequestShape.d.ts.map +1 -1
  54. package/dist/utils/runTaskRequestShape.js +40 -11
  55. package/dist/utils/runTaskRequestShape.js.map +1 -1
  56. package/package.json +7 -6
@@ -12,6 +12,10 @@ import { registerBuiltInLocalTasks } from "../localTasks/index.js";
12
12
  import { getByPath } from "../utils/jsonPaths.js";
13
13
  import { passthroughJobTemplateVariables } from "../utils/skillTemplateVariables.js";
14
14
  import { emitRunTaskRequestWarnings } from "../utils/runTaskRequestShape.js";
15
+ import { DebugLogAbstract, fieldEvidence } from "@x12i/logxer";
16
+ import { configureFuncxLogging } from "@x12i/funcx";
17
+ import { AI_TASKS_DIAGNOSTIC_CODES } from "../logxer/aiTasksDiagnosticCodes.js";
18
+ import { patchRunTaskLogContext, runAiTasksWithObservabilityContext, } from "../logxer/aiTasksLogContext.js";
15
19
  import { getAiTasksLogxer } from "../logxer/packageLogxers.js";
16
20
  import { assertRequiredRunSkillCorrelation } from "../utils/assertRequiredRunSkillCorrelation.js";
17
21
  import { compileTaskConfigurationOnRunTaskRequest } from "../compile/compileTaskConfiguration.js";
@@ -417,24 +421,17 @@ function safeResolveSynthesisQuestion(synthesisRequest, config, renderedPrompt)
417
421
  return "";
418
422
  }
419
423
  }
420
- /**
421
- * Task execution client for @exellix/ai-tasks.
422
- *
423
- * This class implements the canonical runTask algorithm:
424
- * 1) Look up local handler by skillKey; if found, run it and return
425
- * 2) Enrich memories at TASK level (using skillKey)
426
- * 3) Generate TASK-level context markdown (using skillKey)
427
- * 4) Execute via executor (no-enrichment primitive)
428
- *
429
- * This is a private package. We intentionally reuse @exellix/ai-skills directly,
430
- * including its types and helpers. Naming leakage is acceptable.
431
- */
432
424
  export class WorexClientTasks {
433
425
  skillsClient;
434
426
  executor;
435
- constructor(skillsClient, executor) {
427
+ stackLogging;
428
+ constructor(skillsClient, executor, options) {
436
429
  this.skillsClient = skillsClient;
437
430
  this.executor = executor;
431
+ this.stackLogging = options?.logging;
432
+ if (this.stackLogging) {
433
+ configureFuncxLogging({ logging: this.stackLogging });
434
+ }
438
435
  }
439
436
  async runTask(input) {
440
437
  const compiled = compileTaskConfigurationOnRunTaskRequest(input);
@@ -451,176 +448,147 @@ export class WorexClientTasks {
451
448
  debugTrace: { tasks: traceCollector.tasks() },
452
449
  };
453
450
  };
454
- // `@exellix/ai-skills` requires stable ids on each gateway request; callers may omit for simple runs.
455
- const taskId = input.taskId?.trim() ? input.taskId : randomUUID();
456
- const jobId = input.jobId?.trim() ? input.jobId : randomUUID();
457
- input.taskId = taskId;
458
- input.jobId = jobId;
459
- // Optional activix tracking (non-fatal).
460
- const ax = await getActivixClient().catch(() => null);
461
- const correlationId = ax ? taskId : undefined;
462
- const coreSkillId = input.coreSkillId;
463
- const upstreamIdentity = isRecord(input.identity) ? input.identity : undefined;
464
- const executingSkillId = coreSkillId ?? input.skillKey;
465
- const effectiveIdentity = {
466
- ...(upstreamIdentity ?? {}),
467
- ...(upstreamIdentity?.taskId ? {} : { taskId }),
468
- ...(upstreamIdentity?.skillId ? {} : { skillId: executingSkillId }),
469
- };
470
- const normalizedSmartInput = input.smartInput !== undefined && input.smartInput !== null
471
- ? normalizeSmartInputConfig(input.smartInput)
472
- : undefined;
473
- let request = withDefaultDecisionValidation({
474
- ...input,
475
- identity: effectiveIdentity,
476
- ...(normalizedSmartInput !== undefined ? { smartInput: normalizedSmartInput } : {}),
477
- });
478
- let xynthesizedPatchAccumulator;
479
- const knobs = resolveRunTaskRuntimeKnobs(request);
480
- const baseTraceMeta = () => ({
481
- jobId,
482
- taskId,
483
- skillKey: input.skillKey,
484
- executionStrategiesSummary: executionStrategiesSummaryFromKnobs(knobs),
485
- narrixMode: knobs.narrixMode,
486
- agentId: input.agentId,
487
- graphId: input.graphId,
488
- nodeId: input.nodeId,
489
- prevNodeId: input.prevNodeId,
490
- masterSkillId: input.masterSkillId,
491
- masterSkillActivityId: input.masterSkillActivityId,
492
- hasPipeline: Array.isArray(input.executionPipeline) && input.executionPipeline.length > 0,
493
- hasNarrix: !!input.narrix,
494
- hasNarrixInput: !!input.narrixInput,
495
- });
496
- const modelUsedFromResult = (result) => {
497
- if (!result || typeof result !== "object")
498
- return undefined;
499
- const r = result;
500
- const candidates = [
501
- r?.metadata?.modelUsed,
502
- r?.metadata?.model,
503
- r?.gatewayResponse?.model,
504
- r?.enhancedResponse?.model,
505
- r?.model,
506
- ];
507
- const found = candidates.find((x) => typeof x === "string" && x.trim().length > 0);
508
- return typeof found === "string" ? found : undefined;
509
- };
510
- const finalize = (result) => {
511
- const downstreamIdentity = extractDownstreamIdentity(result);
512
- const identityToReturn = downstreamIdentity ??
513
- (upstreamIdentity ? effectiveIdentity : undefined);
514
- if (identityToReturn) {
515
- result.identity = identityToReturn;
516
- result.metadata = { ...(result.metadata ?? {}), identity: identityToReturn };
517
- }
518
- const smartInputRenderResult = extractSmartInputRenderResult(result);
519
- if (smartInputRenderResult) {
520
- result.smartInputRenderResult = smartInputRenderResult;
521
- }
522
- result.metadata = {
523
- ...result.metadata,
451
+ return runAiTasksWithObservabilityContext({ logging: this.stackLogging }, async () => {
452
+ // `@exellix/ai-skills` requires stable ids on each gateway request; callers may omit for simple runs.
453
+ const taskId = input.taskId?.trim() ? input.taskId : randomUUID();
454
+ const jobId = input.jobId?.trim() ? input.jobId : randomUUID();
455
+ input.taskId = taskId;
456
+ input.jobId = jobId;
457
+ // Optional activix tracking (non-fatal).
458
+ const ax = await getActivixClient().catch(() => null);
459
+ const correlationId = ax ? taskId : undefined;
460
+ patchRunTaskLogContext({
461
+ jobId,
524
462
  taskId,
525
- ...(coreSkillId ? { skillId: coreSkillId } : {}),
526
- identityPresent: (input.identity !== undefined),
527
- ...(smartInputRenderResult ? { smartInputRenderResult } : {}),
463
+ correlationId,
464
+ graphId: input.graphId,
465
+ nodeId: input.nodeId,
466
+ });
467
+ const coreSkillId = input.coreSkillId;
468
+ const upstreamIdentity = isRecord(input.identity) ? input.identity : undefined;
469
+ const executingSkillId = coreSkillId ?? input.skillKey;
470
+ const effectiveIdentity = {
471
+ ...(upstreamIdentity ?? {}),
472
+ ...(upstreamIdentity?.taskId ? {} : { taskId }),
473
+ ...(upstreamIdentity?.skillId ? {} : { skillId: executingSkillId }),
528
474
  };
529
- const patchJob = xynthesizedPatchAccumulator?.job && Object.keys(xynthesizedPatchAccumulator.job).length > 0;
530
- const patchTask = xynthesizedPatchAccumulator?.task && Object.keys(xynthesizedPatchAccumulator.task).length > 0;
531
- const patchExecution = xynthesizedPatchAccumulator?.execution &&
532
- Object.keys(xynthesizedPatchAccumulator.execution).length > 0;
533
- if (patchJob || patchTask || patchExecution) {
534
- result.xynthesizedPatch = xynthesizedPatchAccumulator;
535
- }
536
- return result;
537
- };
538
- const hasPipeline = Array.isArray(input.executionPipeline) && input.executionPipeline.length > 0;
539
- const hasNarrix = !!input.narrix;
540
- const hasNarrixInput = !!input.narrixInput;
541
- // Activix 6+: correlation + task identity belong in `runContext`, not a parallel top-level `identity` field.
542
- const runContext = {
543
- ...effectiveIdentity,
544
- sessionId: taskId,
545
- };
546
- const baseMeta = {
547
- skillKey: input.skillKey,
548
- executionStrategiesSummary: executionStrategiesSummaryFromKnobs(knobs),
549
- narrixMode: knobs.narrixMode,
550
- jobId: input.jobId,
551
- agentId: input.agentId,
552
- graphId: input.graphId,
553
- nodeId: input.nodeId,
554
- prevNodeId: input.prevNodeId,
555
- masterSkillId: input.masterSkillId,
556
- masterSkillActivityId: input.masterSkillActivityId,
557
- runContext,
558
- hasPipeline,
559
- hasNarrix,
560
- hasNarrixInput,
561
- };
562
- const runDirect = async (req, options) => {
563
- const traceTask = {
564
- taskType: "ai-task",
565
- details: "direct execution (skills/gateway)",
566
- metadata: {
567
- ...baseTraceMeta(),
568
- phase: "direct",
569
- overrideContextProvided: options?.overrideContext !== undefined,
570
- captureContextProvided: options?.captureContext !== undefined,
571
- synthesisContextAuthoritative: options?.synthesisContextAuthoritative === true,
572
- ...(traceCollector
573
- ? {
574
- smartInput: {
575
- paths: req.smartInput?.paths ?? [],
576
- },
577
- }
578
- : {}),
579
- },
475
+ const normalizedSmartInput = input.smartInput !== undefined && input.smartInput !== null
476
+ ? normalizeSmartInputConfig(input.smartInput)
477
+ : undefined;
478
+ let request = withDefaultDecisionValidation({
479
+ ...input,
480
+ identity: effectiveIdentity,
481
+ ...(normalizedSmartInput !== undefined ? { smartInput: normalizedSmartInput } : {}),
482
+ });
483
+ let xynthesizedPatchAccumulator;
484
+ const knobs = resolveRunTaskRuntimeKnobs(request);
485
+ const baseTraceMeta = () => ({
486
+ jobId,
487
+ taskId,
488
+ skillKey: input.skillKey,
489
+ executionStrategiesSummary: executionStrategiesSummaryFromKnobs(knobs),
490
+ narrixMode: knobs.narrixMode,
491
+ agentId: input.agentId,
492
+ graphId: input.graphId,
493
+ nodeId: input.nodeId,
494
+ prevNodeId: input.prevNodeId,
495
+ masterSkillId: input.masterSkillId,
496
+ masterSkillActivityId: input.masterSkillActivityId,
497
+ hasPipeline: Array.isArray(input.executionPipeline) && input.executionPipeline.length > 0,
498
+ hasNarrix: !!input.narrix,
499
+ hasNarrixInput: !!input.narrixInput,
500
+ });
501
+ const modelUsedFromResult = (result) => {
502
+ if (!result || typeof result !== "object")
503
+ return undefined;
504
+ const r = result;
505
+ const candidates = [
506
+ r?.metadata?.modelUsed,
507
+ r?.metadata?.model,
508
+ r?.gatewayResponse?.model,
509
+ r?.enhancedResponse?.model,
510
+ r?.model,
511
+ ];
512
+ const found = candidates.find((x) => typeof x === "string" && x.trim().length > 0);
513
+ return typeof found === "string" ? found : undefined;
580
514
  };
581
- return traceWrap(traceCollector, traceTask, async () => withPhaseRecord({
582
- ax,
583
- correlationId,
584
- phase: "direct",
585
- meta: {
586
- ...baseMeta,
587
- outer: activixOuterTier({
588
- kind: "runTask.request",
589
- skillKey: req.skillKey,
590
- executionStrategiesSummary: executionStrategiesSummaryFromKnobs(knobs),
591
- jobId: req.jobId,
592
- agentId: req.agentId,
593
- graphId: req.graphId,
594
- nodeId: req.nodeId,
595
- masterSkillId: req.masterSkillId,
596
- masterSkillActivityId: req.masterSkillActivityId,
597
- includeContextInPrompt: req.includeContextInPrompt === true,
598
- hasPipeline: Array.isArray(req.executionPipeline) && req.executionPipeline.length > 0,
599
- hasNarrix: !!req.narrix,
600
- hasNarrixInput: !!req.narrixInput,
601
- input: summarizeForOuter(req.input, 4_000),
602
- }, null, { phase: "direct" }),
603
- // pipeline MAIN vs DIRECT is still tracked as the same phase label ("direct")
604
- hasPipeline: !!req.executionPipeline,
605
- hasNarrix: !!req.narrix,
606
- hasNarrixInput: !!req.narrixInput,
607
- overrideContextProvided: options?.overrideContext !== undefined,
608
- captureContextProvided: options?.captureContext !== undefined,
609
- synthesisContextAuthoritative: options?.synthesisContextAuthoritative === true,
610
- },
611
- fn: async () => this._executeDirect(req, {
612
- ...options,
613
- validationCorrelationId: taskId,
614
- traceCollector,
615
- }),
616
- onSuccessUpdates: (result) => {
617
- const steps = result?.intermediateSteps;
618
- return {
619
- instructionVersion: result?.metadata?.instructionVersion,
620
- activityId: result?.metadata?.activityId,
621
- durationMs: result?.metadata?.durationMs,
622
- hasIntermediateSteps: Array.isArray(steps) && steps.length > 0,
623
- stepCount: Array.isArray(steps) ? steps.length : undefined,
515
+ const finalize = (result) => {
516
+ const downstreamIdentity = extractDownstreamIdentity(result);
517
+ const identityToReturn = downstreamIdentity ??
518
+ (upstreamIdentity ? effectiveIdentity : undefined);
519
+ if (identityToReturn) {
520
+ result.identity = identityToReturn;
521
+ result.metadata = { ...(result.metadata ?? {}), identity: identityToReturn };
522
+ }
523
+ const smartInputRenderResult = extractSmartInputRenderResult(result);
524
+ if (smartInputRenderResult) {
525
+ result.smartInputRenderResult = smartInputRenderResult;
526
+ }
527
+ result.metadata = {
528
+ ...result.metadata,
529
+ taskId,
530
+ ...(coreSkillId ? { skillId: coreSkillId } : {}),
531
+ identityPresent: (input.identity !== undefined),
532
+ ...(smartInputRenderResult ? { smartInputRenderResult } : {}),
533
+ };
534
+ const patchJob = xynthesizedPatchAccumulator?.job && Object.keys(xynthesizedPatchAccumulator.job).length > 0;
535
+ const patchTask = xynthesizedPatchAccumulator?.task && Object.keys(xynthesizedPatchAccumulator.task).length > 0;
536
+ const patchExecution = xynthesizedPatchAccumulator?.execution &&
537
+ Object.keys(xynthesizedPatchAccumulator.execution).length > 0;
538
+ if (patchJob || patchTask || patchExecution) {
539
+ result.xynthesizedPatch = xynthesizedPatchAccumulator;
540
+ }
541
+ return result;
542
+ };
543
+ const hasPipeline = Array.isArray(input.executionPipeline) && input.executionPipeline.length > 0;
544
+ const hasNarrix = !!input.narrix;
545
+ const hasNarrixInput = !!input.narrixInput;
546
+ // Activix 6+: correlation + task identity belong in `runContext`, not a parallel top-level `identity` field.
547
+ const runContext = {
548
+ ...effectiveIdentity,
549
+ sessionId: taskId,
550
+ };
551
+ const baseMeta = {
552
+ skillKey: input.skillKey,
553
+ executionStrategiesSummary: executionStrategiesSummaryFromKnobs(knobs),
554
+ narrixMode: knobs.narrixMode,
555
+ jobId: input.jobId,
556
+ agentId: input.agentId,
557
+ graphId: input.graphId,
558
+ nodeId: input.nodeId,
559
+ prevNodeId: input.prevNodeId,
560
+ masterSkillId: input.masterSkillId,
561
+ masterSkillActivityId: input.masterSkillActivityId,
562
+ runContext,
563
+ hasPipeline,
564
+ hasNarrix,
565
+ hasNarrixInput,
566
+ };
567
+ const runDirect = async (req, options) => {
568
+ const traceTask = {
569
+ taskType: "ai-task",
570
+ details: "direct execution (skills/gateway)",
571
+ metadata: {
572
+ ...baseTraceMeta(),
573
+ phase: "direct",
574
+ overrideContextProvided: options?.overrideContext !== undefined,
575
+ captureContextProvided: options?.captureContext !== undefined,
576
+ synthesisContextAuthoritative: options?.synthesisContextAuthoritative === true,
577
+ ...(traceCollector
578
+ ? {
579
+ smartInput: {
580
+ paths: req.smartInput?.paths ?? [],
581
+ },
582
+ }
583
+ : {}),
584
+ },
585
+ };
586
+ return traceWrap(traceCollector, traceTask, async () => withPhaseRecord({
587
+ ax,
588
+ correlationId,
589
+ phase: "direct",
590
+ meta: {
591
+ ...baseMeta,
624
592
  outer: activixOuterTier({
625
593
  kind: "runTask.request",
626
594
  skillKey: req.skillKey,
@@ -636,116 +604,75 @@ export class WorexClientTasks {
636
604
  hasNarrix: !!req.narrix,
637
605
  hasNarrixInput: !!req.narrixInput,
638
606
  input: summarizeForOuter(req.input, 4_000),
639
- }, {
640
- kind: "runTask.response",
641
- skillKey: result?.skillKey,
642
- // Keep this intentionally small; rawText / flexMd payloads can be huge.
643
- metadata: summarizeForOuter(result?.metadata, 4_000),
644
- parsed: summarizeForOuter(result?.parsed, 8_000),
645
- }, { phase: "direct" }),
646
- };
647
- },
648
- }), (result) => {
649
- const modelUsed = modelUsedFromResult(result);
650
- const r = result;
651
- return {
652
- modelUsed: modelUsed ?? null,
653
- metadata: {
654
- activityId: r?.metadata?.activityId,
655
- instructionVersion: r?.metadata?.instructionVersion,
656
- durationMs: r?.metadata?.durationMs,
657
- outputValidation: r?.metadata?.outputValidation,
607
+ }, null, { phase: "direct" }),
608
+ // pipeline MAIN vs DIRECT is still tracked as the same phase label ("direct")
609
+ hasPipeline: !!req.executionPipeline,
610
+ hasNarrix: !!req.narrix,
611
+ hasNarrixInput: !!req.narrixInput,
612
+ overrideContextProvided: options?.overrideContext !== undefined,
613
+ captureContextProvided: options?.captureContext !== undefined,
614
+ synthesisContextAuthoritative: options?.synthesisContextAuthoritative === true,
658
615
  },
659
- };
660
- });
661
- };
662
- const runMainWithExecutionStrategies = async (req, options) => {
663
- const strat = knobs.resolvedExecutionStrategies;
664
- let working = { ...req };
665
- const prefixSteps = [];
666
- for (let pi = 0; pi < strat.orderedBefore.length; pi++) {
667
- const spec = strat.orderedBefore[pi];
668
- if (spec.strategyKey !== "planner")
669
- continue;
670
- const payload = buildExecutionStrategyRequestPayload({
671
- skillKey: working.skillKey,
672
- input: working.input,
673
- variables: working.variables,
674
- jobContext: working.jobContext,
675
- jobMemory: working.jobMemory,
676
- taskMemory: working.taskMemory,
677
- executionMemory: working.executionMemory,
678
- jobId: working.jobId,
679
- taskId: working.taskId,
680
- agentId: working.agentId,
681
- graphId: working.graphId,
682
- nodeId: working.nodeId,
683
- prevNodeId: working.prevNodeId,
684
- coreSkillId: working.coreSkillId,
685
- includeContextInPrompt: working.includeContextInPrompt,
686
- });
687
- const catalogDefaultFunctionId = resolveSafeExecutionStrategyCatalogDefaultFunctionId(req.executionStrategyCatalogItems, spec);
688
- const plannerOut = await runPlannerFuncx({
689
- args: spec.args,
690
- requestPayload: payload,
691
- defaultFunctionId: catalogDefaultFunctionId,
692
- });
693
- working = applyPlannerOutputToRequest(working, plannerOut, pi);
694
- prefixSteps.push({
695
- step: prefixSteps.length + 1,
696
- id: `executionStrategy.planner.${pi}`,
697
- ok: true,
698
- summary: "FuncX planner (execution/plan)",
699
- outputExcerpt: {
700
- hasInstructions: typeof plannerOut.instructions === "string",
701
- hasPrompt: typeof plannerOut.prompt === "string",
702
- variableKeys: plannerOut.variables ? Object.keys(plannerOut.variables).slice(0, 20) : [],
703
- functionIdSource: typeof spec.args?.functionId === "string"
704
- ? "invocation"
705
- : catalogDefaultFunctionId
706
- ? "catalog"
707
- : "code-default",
616
+ fn: async () => this._executeDirect(req, {
617
+ ...options,
618
+ validationCorrelationId: taskId,
619
+ traceCollector,
620
+ }),
621
+ onSuccessUpdates: (result) => {
622
+ const steps = result?.intermediateSteps;
623
+ return {
624
+ instructionVersion: result?.metadata?.instructionVersion,
625
+ activityId: result?.metadata?.activityId,
626
+ durationMs: result?.metadata?.durationMs,
627
+ hasIntermediateSteps: Array.isArray(steps) && steps.length > 0,
628
+ stepCount: Array.isArray(steps) ? steps.length : undefined,
629
+ outer: activixOuterTier({
630
+ kind: "runTask.request",
631
+ skillKey: req.skillKey,
632
+ executionStrategiesSummary: executionStrategiesSummaryFromKnobs(knobs),
633
+ jobId: req.jobId,
634
+ agentId: req.agentId,
635
+ graphId: req.graphId,
636
+ nodeId: req.nodeId,
637
+ masterSkillId: req.masterSkillId,
638
+ masterSkillActivityId: req.masterSkillActivityId,
639
+ includeContextInPrompt: req.includeContextInPrompt === true,
640
+ hasPipeline: Array.isArray(req.executionPipeline) && req.executionPipeline.length > 0,
641
+ hasNarrix: !!req.narrix,
642
+ hasNarrixInput: !!req.narrixInput,
643
+ input: summarizeForOuter(req.input, 4_000),
644
+ }, {
645
+ kind: "runTask.response",
646
+ skillKey: result?.skillKey,
647
+ // Keep this intentionally small; rawText / flexMd payloads can be huge.
648
+ metadata: summarizeForOuter(result?.metadata, 4_000),
649
+ parsed: summarizeForOuter(result?.parsed, 8_000),
650
+ }, { phase: "direct" }),
651
+ };
708
652
  },
709
- });
710
- if (traceCollector) {
711
- traceCollector.push({
712
- taskType: "pre-execution",
713
- details: "execution strategy planner (FuncX)",
653
+ }), (result) => {
654
+ const modelUsed = modelUsedFromResult(result);
655
+ const r = result;
656
+ return {
657
+ modelUsed: modelUsed ?? null,
714
658
  metadata: {
715
- ...baseTraceMeta(),
716
- phase: "execution_strategy_planner",
717
- plannerIndex: pi,
659
+ activityId: r?.metadata?.activityId,
660
+ instructionVersion: r?.metadata?.instructionVersion,
661
+ durationMs: r?.metadata?.durationMs,
662
+ outputValidation: r?.metadata?.outputValidation,
718
663
  },
719
- modelUsed: null,
720
- });
721
- }
722
- }
723
- const mergePrefixSteps = (result) => {
724
- if (prefixSteps.length === 0)
725
- return result;
726
- const existing = (result.intermediateSteps ?? []);
727
- const shifted = existing.map((s, i) => ({ ...s, step: prefixSteps.length + i + 1 }));
728
- result.intermediateSteps = [...prefixSteps, ...shifted];
729
- return result;
664
+ };
665
+ });
730
666
  };
731
- const primary = strat.primaryOptimizer;
732
- const maxIter = strat.effectivePrimaryOptimizerMaxIterations;
733
- if (!primary) {
734
- return mergePrefixSteps(await runDirect(working, options));
735
- }
736
- let last;
737
- for (let iter = 0; iter < maxIter; iter++) {
738
- last = await runDirect(working, options);
739
- const mainSummary = {
740
- rawText: typeof last.rawText === "string" ? String(last.rawText).slice(0, 12_000) : undefined,
741
- parsed: last.parsed,
742
- skillKey: last.skillKey,
743
- metadata: last.metadata ? summarizeForOuter(last.metadata, 6_000) : undefined,
744
- };
745
- const evalOut = await runOptimizerFuncx({
746
- args: primary.args,
747
- defaultFunctionId: resolveSafeExecutionStrategyCatalogDefaultFunctionId(req.executionStrategyCatalogItems, primary),
748
- requestPayload: buildExecutionStrategyRequestPayload({
667
+ const runMainWithExecutionStrategies = async (req, options) => {
668
+ const strat = knobs.resolvedExecutionStrategies;
669
+ let working = { ...req };
670
+ const prefixSteps = [];
671
+ for (let pi = 0; pi < strat.orderedBefore.length; pi++) {
672
+ const spec = strat.orderedBefore[pi];
673
+ if (spec.strategyKey !== "planner")
674
+ continue;
675
+ const payload = buildExecutionStrategyRequestPayload({
749
676
  skillKey: working.skillKey,
750
677
  input: working.input,
751
678
  variables: working.variables,
@@ -761,751 +688,864 @@ export class WorexClientTasks {
761
688
  prevNodeId: working.prevNodeId,
762
689
  coreSkillId: working.coreSkillId,
763
690
  includeContextInPrompt: working.includeContextInPrompt,
764
- }),
765
- mainResultSummary: mainSummary,
766
- iterationIndex: iter,
767
- });
768
- if (traceCollector) {
769
- traceCollector.push({
770
- taskType: "ai-task",
771
- details: "execution strategy optimizer (FuncX)",
772
- metadata: {
773
- ...baseTraceMeta(),
774
- phase: "execution_strategy_optimizer",
775
- iterationIndex: iter,
776
- satisfied: evalOut.satisfied === true,
691
+ });
692
+ const catalogDefaultFunctionId = resolveSafeExecutionStrategyCatalogDefaultFunctionId(req.executionStrategyCatalogItems, spec);
693
+ const plannerOut = await runPlannerFuncx({
694
+ args: spec.args,
695
+ requestPayload: payload,
696
+ defaultFunctionId: catalogDefaultFunctionId,
697
+ });
698
+ working = applyPlannerOutputToRequest(working, plannerOut, pi);
699
+ prefixSteps.push({
700
+ step: prefixSteps.length + 1,
701
+ id: `executionStrategy.planner.${pi}`,
702
+ ok: true,
703
+ summary: "FuncX planner (execution/plan)",
704
+ outputExcerpt: {
705
+ hasInstructions: typeof plannerOut.instructions === "string",
706
+ hasPrompt: typeof plannerOut.prompt === "string",
707
+ variableKeys: plannerOut.variables ? Object.keys(plannerOut.variables).slice(0, 20) : [],
708
+ functionIdSource: typeof spec.args?.functionId === "string"
709
+ ? "invocation"
710
+ : catalogDefaultFunctionId
711
+ ? "catalog"
712
+ : "code-default",
777
713
  },
778
- modelUsed: modelUsedFromResult(last) ?? null,
779
714
  });
715
+ if (traceCollector) {
716
+ traceCollector.push({
717
+ taskType: "pre-execution",
718
+ details: "execution strategy planner (FuncX)",
719
+ metadata: {
720
+ ...baseTraceMeta(),
721
+ phase: "execution_strategy_planner",
722
+ plannerIndex: pi,
723
+ },
724
+ modelUsed: null,
725
+ });
726
+ }
780
727
  }
781
- if (evalOut.satisfied) {
782
- return mergePrefixSteps(last);
728
+ const mergePrefixSteps = (result) => {
729
+ if (prefixSteps.length === 0)
730
+ return result;
731
+ const existing = (result.intermediateSteps ?? []);
732
+ const shifted = existing.map((s, i) => ({ ...s, step: prefixSteps.length + i + 1 }));
733
+ result.intermediateSteps = [...prefixSteps, ...shifted];
734
+ return result;
735
+ };
736
+ const primary = strat.primaryOptimizer;
737
+ const maxIter = strat.effectivePrimaryOptimizerMaxIterations;
738
+ if (!primary) {
739
+ return mergePrefixSteps(await runDirect(working, options));
783
740
  }
784
- if (iter === maxIter - 1) {
785
- return mergePrefixSteps(last);
741
+ let last;
742
+ for (let iter = 0; iter < maxIter; iter++) {
743
+ last = await runDirect(working, options);
744
+ const mainSummary = {
745
+ rawText: typeof last.rawText === "string" ? String(last.rawText).slice(0, 12_000) : undefined,
746
+ parsed: last.parsed,
747
+ skillKey: last.skillKey,
748
+ metadata: last.metadata ? summarizeForOuter(last.metadata, 6_000) : undefined,
749
+ };
750
+ const evalOut = await runOptimizerFuncx({
751
+ args: primary.args,
752
+ defaultFunctionId: resolveSafeExecutionStrategyCatalogDefaultFunctionId(req.executionStrategyCatalogItems, primary),
753
+ requestPayload: buildExecutionStrategyRequestPayload({
754
+ skillKey: working.skillKey,
755
+ input: working.input,
756
+ variables: working.variables,
757
+ jobContext: working.jobContext,
758
+ jobMemory: working.jobMemory,
759
+ taskMemory: working.taskMemory,
760
+ executionMemory: working.executionMemory,
761
+ jobId: working.jobId,
762
+ taskId: working.taskId,
763
+ agentId: working.agentId,
764
+ graphId: working.graphId,
765
+ nodeId: working.nodeId,
766
+ prevNodeId: working.prevNodeId,
767
+ coreSkillId: working.coreSkillId,
768
+ includeContextInPrompt: working.includeContextInPrompt,
769
+ }),
770
+ mainResultSummary: mainSummary,
771
+ iterationIndex: iter,
772
+ });
773
+ if (traceCollector) {
774
+ traceCollector.push({
775
+ taskType: "ai-task",
776
+ details: "execution strategy optimizer (FuncX)",
777
+ metadata: {
778
+ ...baseTraceMeta(),
779
+ phase: "execution_strategy_optimizer",
780
+ iterationIndex: iter,
781
+ satisfied: evalOut.satisfied === true,
782
+ },
783
+ modelUsed: modelUsedFromResult(last) ?? null,
784
+ });
785
+ }
786
+ if (evalOut.satisfied) {
787
+ return mergePrefixSteps(last);
788
+ }
789
+ if (iter === maxIter - 1) {
790
+ return mergePrefixSteps(last);
791
+ }
792
+ working = applyOptimizerFeedbackToRequest(working, evalOut, iter + 1);
786
793
  }
787
- working = applyOptimizerFeedbackToRequest(working, evalOut, iter + 1);
788
- }
789
- return mergePrefixSteps(last);
790
- };
791
- // NARRIX task-level pre-processor + structured handler: `narrixMode` (+ inference) via `resolveRunTaskRuntimeKnobs`.
792
- if (knobs.narrixMode === "preprocessor" && request.narrix) {
793
- const narrixCfg = request.narrix;
794
- await traceWrap(traceCollector, {
795
- taskType: "pre-execution",
796
- details: "narrix preprocessor",
797
- metadata: {
798
- ...baseTraceMeta(),
799
- phase: "narrix",
800
- datasetId: narrixCfg.datasetId,
801
- attachToField: narrixCfg.attachToField ?? "_narrix",
802
- enableWebScope: narrixCfg.enableWebScope === true,
803
- },
804
- }, async () => withPhaseRecord({
805
- ax,
806
- correlationId,
807
- phase: "narrix",
808
- meta: {
809
- ...baseMeta,
810
- narrixAttachToField: narrixCfg.attachToField ?? "_narrix",
811
- outer: activixOuterTier({
812
- kind: "narrix.preProcessor.request",
794
+ return mergePrefixSteps(last);
795
+ };
796
+ // NARRIX task-level pre-processor + structured handler: `narrixMode` (+ inference) via `resolveRunTaskRuntimeKnobs`.
797
+ if (knobs.narrixMode === "preprocessor" && request.narrix) {
798
+ const narrixCfg = request.narrix;
799
+ await traceWrap(traceCollector, {
800
+ taskType: "pre-execution",
801
+ details: "narrix preprocessor",
802
+ metadata: {
803
+ ...baseTraceMeta(),
804
+ phase: "narrix",
813
805
  datasetId: narrixCfg.datasetId,
814
806
  attachToField: narrixCfg.attachToField ?? "_narrix",
815
807
  enableWebScope: narrixCfg.enableWebScope === true,
816
- webScoping: narrixCfg.webScoping ? summarizeForOuter(narrixCfg.webScoping, 2_000) : undefined,
817
- requestInput: summarizeForOuter(request.input, 4_000),
818
- }, null, { phase: "narrix" }),
819
- },
820
- fn: async () => {
821
- const adaptResult = adaptMemorixToNarrixInput({
822
- datasetId: narrixCfg.datasetId,
823
- mediumHint: "record",
824
- memorix: {
808
+ },
809
+ }, async () => withPhaseRecord({
810
+ ax,
811
+ correlationId,
812
+ phase: "narrix",
813
+ meta: {
814
+ ...baseMeta,
815
+ narrixAttachToField: narrixCfg.attachToField ?? "_narrix",
816
+ outer: activixOuterTier({
817
+ kind: "narrix.preProcessor.request",
818
+ datasetId: narrixCfg.datasetId,
819
+ attachToField: narrixCfg.attachToField ?? "_narrix",
820
+ enableWebScope: narrixCfg.enableWebScope === true,
821
+ webScoping: narrixCfg.webScoping ? summarizeForOuter(narrixCfg.webScoping, 2_000) : undefined,
822
+ requestInput: summarizeForOuter(request.input, 4_000),
823
+ }, null, { phase: "narrix" }),
824
+ },
825
+ fn: async () => {
826
+ const adaptResult = adaptMemorixToNarrixInput({
827
+ datasetId: narrixCfg.datasetId,
828
+ mediumHint: "record",
829
+ memorix: {
830
+ jobMemory: request.jobMemory,
831
+ executionMemory: request.executionMemory,
832
+ input: request.input,
833
+ },
834
+ }, {
835
+ record: {
836
+ minKeys: 1,
837
+ preferPaths: [
838
+ "executionMemory.input.raw",
839
+ "executionMemory.inputs.*.raw",
840
+ "jobMemory.record",
841
+ "jobMemory.currentRecord",
842
+ "executionMemory.input.record",
843
+ "executionMemory.record",
844
+ "taskMemory.currentRecord",
845
+ "*.currentRecord",
846
+ "*.record",
847
+ ],
848
+ },
849
+ });
850
+ if (!adaptResult.ok) {
851
+ const attempted = adaptResult.attempted?.length ? ` Attempted: ${adaptResult.attempted.join(", ")}.` : "";
852
+ const summary = adaptResult.foundSummary ? ` Seen: ${adaptResult.foundSummary}` : "";
853
+ throw new Error(`NARRIX pre-processor: no usable input. ${adaptResult.message}.${attempted}${summary}`);
854
+ }
855
+ const ctx = {
856
+ skillKey: request.skillKey,
825
857
  jobMemory: request.jobMemory,
858
+ taskMemory: request.taskMemory,
826
859
  executionMemory: request.executionMemory,
827
- input: request.input,
828
- },
829
- }, {
830
- record: {
831
- minKeys: 1,
832
- preferPaths: [
833
- "executionMemory.input.raw",
834
- "executionMemory.inputs.*.raw",
835
- "jobMemory.record",
836
- "jobMemory.currentRecord",
837
- "executionMemory.input.record",
838
- "executionMemory.record",
839
- "taskMemory.currentRecord",
840
- "*.currentRecord",
841
- "*.record",
842
- ],
843
- },
844
- });
845
- if (!adaptResult.ok) {
846
- const attempted = adaptResult.attempted?.length ? ` Attempted: ${adaptResult.attempted.join(", ")}.` : "";
847
- const summary = adaptResult.foundSummary ? ` Seen: ${adaptResult.foundSummary}` : "";
848
- throw new Error(`NARRIX pre-processor: no usable input. ${adaptResult.message}.${attempted}${summary}`);
860
+ variables: input.variables,
861
+ xynthesized: request.xynthesized,
862
+ smartInput: request.smartInput,
863
+ jobId: input.jobId,
864
+ taskId: request.taskId,
865
+ agentId: input.agentId,
866
+ graphId: input.graphId,
867
+ nodeId: input.nodeId,
868
+ prevNodeId: input.prevNodeId,
869
+ coreSkillId: input.coreSkillId,
870
+ masterSkillId: request.masterSkillId,
871
+ masterSkillActivityId: request.masterSkillActivityId,
872
+ };
873
+ const narrixResult = await narrixRunHandler({ input: adaptResult.narrixInput, ctx });
874
+ if (!narrixResult.ok) {
875
+ const fail = narrixResult;
876
+ throw new Error(`NARRIX pre-processor failed: ${fail.error}${fail.message ? ` - ${fail.message}` : ""}`);
877
+ }
878
+ const attachment = buildNarrixAttachment(narrixResult);
879
+ const attachToField = narrixCfg.attachToField ?? "_narrix";
880
+ let webContextEntry = undefined;
881
+ let webContextAvailable;
882
+ let webContextReason;
883
+ const externalWebMdPreNarrix = typeof request.executionMemory?.webContextMarkdown === "string" &&
884
+ request.executionMemory.webContextMarkdown.trim().length > 0;
885
+ const skipWebScopeForExternalMd = narrixCfg.skipWebScopeWhenExternalWebMarkdownPresent === true && externalWebMdPreNarrix;
886
+ if (narrixCfg.enableWebScope === true && !skipWebScopeForExternalMd) {
887
+ const successResult = narrixResult;
888
+ const entity = adaptResult.narrixInput.medium === "record"
889
+ ? adaptResult.narrixInput.record
890
+ : undefined;
891
+ const scopeFields = resolveWebScopeQuestionAndTemplates({
892
+ narrix: narrixCfg,
893
+ requestInput: request.input,
894
+ successResult,
895
+ entity,
896
+ });
897
+ const webScopeOpts = hasNonEmptyWebScoping(narrixCfg.webScoping)
898
+ ? { scoping: narrixCfg.webScoping }
899
+ : undefined;
900
+ const activixPatch = webScopeActivixCorrelationPatch({
901
+ jobId: request.jobId,
902
+ taskId: request.taskId,
903
+ sessionId: request.taskId,
904
+ });
905
+ const packQs = scopeFields.packQuestions;
906
+ if (Array.isArray(packQs) && packQs.length > 0) {
907
+ webContextEntry = await runWebScopeQuestionPack({
908
+ datasetId: narrixCfg.datasetId,
909
+ subjectId: successResult.entity.entityKey,
910
+ entityKind: successResult.entity.entityKind,
911
+ entity,
912
+ cni: successResult.cni,
913
+ ...activixPatch,
914
+ questions: packQs,
915
+ }, webScopeOpts);
916
+ }
917
+ else {
918
+ const { packQuestions: _pq, ...genericScope } = scopeFields;
919
+ void _pq;
920
+ webContextEntry = await runWebScope({
921
+ datasetId: narrixCfg.datasetId,
922
+ subjectId: successResult.entity.entityKey,
923
+ entityKind: successResult.entity.entityKind,
924
+ entity,
925
+ cni: successResult.cni,
926
+ ...activixPatch,
927
+ ...genericScope,
928
+ }, webScopeOpts);
929
+ }
930
+ if (webContextEntry && typeof webContextEntry === "object") {
931
+ const wc = webContextEntry;
932
+ webContextAvailable = typeof wc.available === "boolean" ? wc.available : undefined;
933
+ webContextReason = typeof wc.reason === "string" ? wc.reason : undefined;
934
+ }
935
+ if (webContextEntry &&
936
+ typeof webContextEntry === "object" &&
937
+ webContextEntry.available === false) {
938
+ const miss = webContextEntry;
939
+ if (process.env.NARRIX_DEBUG === "1") {
940
+ getAiTasksLogxer().warnCode(AI_TASKS_DIAGNOSTIC_CODES.NARRIX_WEB_SCOPE_MISS, {
941
+ source: "@exellix/ai-tasks",
942
+ debugKind: DebugLogAbstract.ANOMALY,
943
+ diagnostics: {
944
+ summary: "Narrix web scope miss",
945
+ actual: miss.error ?? miss.reason ?? "web context unavailable",
946
+ },
947
+ evidence: [
948
+ fieldEvidence("webContext.reason", miss.reason ?? null),
949
+ fieldEvidence("webContext.error", miss.error ?? null),
950
+ ],
951
+ });
952
+ }
953
+ if (traceCollector) {
954
+ traceCollector.push({
955
+ taskType: "pre-execution",
956
+ details: "narrix web scope miss",
957
+ metadata: {
958
+ ...baseTraceMeta(),
959
+ phase: "narrix",
960
+ webContext: { available: false, reason: miss.reason, error: miss.error },
961
+ },
962
+ modelUsed: null,
963
+ });
964
+ }
965
+ }
966
+ }
967
+ request = {
968
+ ...request,
969
+ executionMemory: {
970
+ ...(request.executionMemory || {}),
971
+ [attachToField]: attachment,
972
+ ...(webContextEntry !== undefined ? { webContext: webContextEntry } : {}),
973
+ },
974
+ jobMemory: request.jobMemory ? { ...request.jobMemory, _narrix: attachment } : { _narrix: attachment },
975
+ };
976
+ const scopingSignalsCount = Array.isArray(attachment?.scoping?.signals) ? attachment.scoping.signals.length : undefined;
977
+ const scopingStoriesCount = Array.isArray(attachment?.scoping?.stories) ? attachment.scoping.stories.length : undefined;
978
+ const discoverySignalsCount = Array.isArray(attachment?.discovery?.signals) ? attachment.discovery.signals.length : undefined;
979
+ const discoveryStoriesCount = Array.isArray(attachment?.discovery?.stories) ? attachment.discovery.stories.length : undefined;
980
+ const entityKey = narrixResult?.entity?.entityKey;
981
+ const entityKind = narrixResult?.entity?.entityKind;
982
+ return {
983
+ datasetId: narrixCfg.datasetId,
984
+ attachToField,
985
+ enableWebScope: narrixCfg.enableWebScope === true,
986
+ webContextAvailable,
987
+ webContextReason,
988
+ attachmentMeta: isRecord(attachment?.meta) ? attachment.meta : undefined,
989
+ scopingSignalsCount,
990
+ scopingStoriesCount,
991
+ discoverySignalsCount,
992
+ discoveryStoriesCount,
993
+ entityKey: typeof entityKey === "string" ? entityKey : undefined,
994
+ entityKind: typeof entityKind === "string" ? entityKind : undefined,
995
+ medium: typeof adaptResult.narrixInput?.medium === "string" ? adaptResult.narrixInput.medium : undefined,
996
+ };
997
+ },
998
+ onSuccessUpdates: (r) => ({
999
+ outer: activixOuterTier({
1000
+ kind: "narrix.preProcessor.request",
1001
+ datasetId: narrixCfg.datasetId,
1002
+ attachToField: narrixCfg.attachToField ?? "_narrix",
1003
+ enableWebScope: narrixCfg.enableWebScope === true,
1004
+ webScoping: narrixCfg.webScoping ? summarizeForOuter(narrixCfg.webScoping, 2_000) : undefined,
1005
+ requestInput: summarizeForOuter(request.input, 4_000),
1006
+ }, {
1007
+ kind: "narrix.preProcessor.result",
1008
+ datasetId: r.datasetId,
1009
+ attachToField: r.attachToField,
1010
+ enableWebScope: r.enableWebScope,
1011
+ entity: r.entityKey ? { entityKey: r.entityKey, entityKind: r.entityKind } : undefined,
1012
+ counts: {
1013
+ scopingSignals: r.scopingSignalsCount,
1014
+ scopingStories: r.scopingStoriesCount,
1015
+ discoverySignals: r.discoverySignalsCount,
1016
+ discoveryStories: r.discoveryStoriesCount,
1017
+ },
1018
+ webContext: r.enableWebScope
1019
+ ? { available: r.webContextAvailable, reason: r.webContextReason }
1020
+ : undefined,
1021
+ attachmentMeta: r.attachmentMeta,
1022
+ }, { phase: "narrix" }),
1023
+ }),
1024
+ }));
1025
+ }
1026
+ // Structured Narrix (`narrixInput`): same enrichment as legacy `narrix-then-direct`, but runs before local tasks / pipeline PRE.
1027
+ if (knobs.narrixMode === "handler") {
1028
+ if (!request.narrixInput) {
1029
+ throw new Error(`narrixMode "handler" requires narrixInput. ` +
1030
+ `Provide { medium, datasetId, ... } or { $path: "jobMemory.currentRecord" } on the request.`);
1031
+ }
1032
+ const resolvedNarrixInput = resolveNarrixInput(request.narrixInput, request.jobMemory);
1033
+ if (!resolvedNarrixInput) {
1034
+ throw new Error(`narrixMode "handler": narrixInput could not be resolved. ` +
1035
+ `Ensure it has medium and datasetId, or that $path points to a valid object in jobMemory.`);
1036
+ }
1037
+ let narrixThenDirectPhaseRecordId = null;
1038
+ if (ax && correlationId) {
1039
+ try {
1040
+ narrixThenDirectPhaseRecordId = (await ax.startRecord({
1041
+ correlationId,
1042
+ phase: "narrix_then_direct",
1043
+ ...baseMeta,
1044
+ narrixDatasetId: resolvedNarrixInput.datasetId,
1045
+ narrixMedium: resolvedNarrixInput.medium,
1046
+ outer: activixOuterTier({
1047
+ kind: "narrixThenDirect.request",
1048
+ skillKey: request.skillKey,
1049
+ jobId: request.jobId,
1050
+ agentId: request.agentId,
1051
+ narrixInput: summarizeForOuter(resolvedNarrixInput, 6_000),
1052
+ }, null, { phase: "narrix_then_direct" }),
1053
+ })).recordId;
849
1054
  }
850
- const ctx = {
1055
+ catch {
1056
+ narrixThenDirectPhaseRecordId = null;
1057
+ }
1058
+ }
1059
+ try {
1060
+ const narrixStarted = Date.now();
1061
+ const narrixHandlerCtx = {
851
1062
  skillKey: request.skillKey,
852
1063
  jobMemory: request.jobMemory,
853
1064
  taskMemory: request.taskMemory,
854
1065
  executionMemory: request.executionMemory,
855
- variables: input.variables,
1066
+ variables: request.variables,
856
1067
  xynthesized: request.xynthesized,
857
1068
  smartInput: request.smartInput,
858
- jobId: input.jobId,
1069
+ jobId: request.jobId,
859
1070
  taskId: request.taskId,
860
- agentId: input.agentId,
861
- graphId: input.graphId,
862
- nodeId: input.nodeId,
863
- prevNodeId: input.prevNodeId,
864
- coreSkillId: input.coreSkillId,
1071
+ agentId: request.agentId,
1072
+ graphId: request.graphId,
1073
+ nodeId: request.nodeId,
1074
+ prevNodeId: request.prevNodeId,
1075
+ coreSkillId: request.coreSkillId,
865
1076
  masterSkillId: request.masterSkillId,
866
1077
  masterSkillActivityId: request.masterSkillActivityId,
867
1078
  };
868
- const narrixResult = await narrixRunHandler({ input: adaptResult.narrixInput, ctx });
1079
+ const narrixResult = await traceWrap(traceCollector, {
1080
+ taskType: "pre-execution",
1081
+ details: "narrix structured handler (narrixInput)",
1082
+ metadata: {
1083
+ ...baseTraceMeta(),
1084
+ phase: "narrix_then_direct",
1085
+ datasetId: resolvedNarrixInput.datasetId,
1086
+ medium: resolvedNarrixInput.medium,
1087
+ },
1088
+ }, async () => narrixRunHandler({ input: resolvedNarrixInput, ctx: narrixHandlerCtx }), (r) => ({
1089
+ metadata: { ok: r?.ok === true },
1090
+ modelUsed: null,
1091
+ }));
1092
+ const narrixDurationMs = Date.now() - narrixStarted;
869
1093
  if (!narrixResult.ok) {
870
1094
  const fail = narrixResult;
871
- throw new Error(`NARRIX pre-processor failed: ${fail.error}${fail.message ? ` - ${fail.message}` : ""}`);
872
- }
873
- const attachment = buildNarrixAttachment(narrixResult);
874
- const attachToField = narrixCfg.attachToField ?? "_narrix";
875
- let webContextEntry = undefined;
876
- let webContextAvailable;
877
- let webContextReason;
878
- const externalWebMdPreNarrix = typeof request.executionMemory?.webContextMarkdown === "string" &&
879
- request.executionMemory.webContextMarkdown.trim().length > 0;
880
- const skipWebScopeForExternalMd = narrixCfg.skipWebScopeWhenExternalWebMarkdownPresent === true && externalWebMdPreNarrix;
881
- if (narrixCfg.enableWebScope === true && !skipWebScopeForExternalMd) {
882
- const successResult = narrixResult;
883
- const entity = adaptResult.narrixInput.medium === "record"
884
- ? adaptResult.narrixInput.record
885
- : undefined;
886
- const scopeFields = resolveWebScopeQuestionAndTemplates({
887
- narrix: narrixCfg,
888
- requestInput: request.input,
889
- successResult,
890
- entity,
891
- });
892
- const webScopeOpts = hasNonEmptyWebScoping(narrixCfg.webScoping)
893
- ? { scoping: narrixCfg.webScoping }
894
- : undefined;
895
- const activixPatch = webScopeActivixCorrelationPatch({
896
- jobId: request.jobId,
897
- taskId: request.taskId,
898
- sessionId: request.taskId,
899
- });
900
- const packQs = scopeFields.packQuestions;
901
- if (Array.isArray(packQs) && packQs.length > 0) {
902
- webContextEntry = await runWebScopeQuestionPack({
903
- datasetId: narrixCfg.datasetId,
904
- subjectId: successResult.entity.entityKey,
905
- entityKind: successResult.entity.entityKind,
906
- entity,
907
- cni: successResult.cni,
908
- ...activixPatch,
909
- questions: packQs,
910
- }, webScopeOpts);
911
- }
912
- else {
913
- const { packQuestions: _pq, ...genericScope } = scopeFields;
914
- void _pq;
915
- webContextEntry = await runWebScope({
916
- datasetId: narrixCfg.datasetId,
917
- subjectId: successResult.entity.entityKey,
918
- entityKind: successResult.entity.entityKind,
919
- entity,
920
- cni: successResult.cni,
921
- ...activixPatch,
922
- ...genericScope,
923
- }, webScopeOpts);
924
- }
925
- if (webContextEntry && typeof webContextEntry === "object") {
926
- const wc = webContextEntry;
927
- webContextAvailable = typeof wc.available === "boolean" ? wc.available : undefined;
928
- webContextReason = typeof wc.reason === "string" ? wc.reason : undefined;
929
- }
930
- if (webContextEntry &&
931
- typeof webContextEntry === "object" &&
932
- webContextEntry.available === false) {
933
- const miss = webContextEntry;
934
- if (process.env.NARRIX_DEBUG === "1") {
935
- getAiTasksLogxer().warn("Narrix web scope miss", {
936
- source: "@exellix/ai-tasks",
937
- kind: "NARRIX_WEB_SCOPE",
938
- reason: miss.reason,
939
- error: miss.error ?? null,
940
- });
941
- }
942
- if (traceCollector) {
943
- traceCollector.push({
944
- taskType: "pre-execution",
945
- details: "narrix web scope miss",
946
- metadata: {
947
- ...baseTraceMeta(),
948
- phase: "narrix",
949
- webContext: { available: false, reason: miss.reason, error: miss.error },
950
- },
951
- modelUsed: null,
952
- });
953
- }
1095
+ if (ax && correlationId && narrixThenDirectPhaseRecordId) {
1096
+ await ax
1097
+ .failRecord(narrixThenDirectPhaseRecordId, new Error(`Narrix handler failed: ${fail.error}${fail.message ? ` - ${fail.message}` : ""}`), {
1098
+ errorMessage: fail.message ? `${fail.error} - ${fail.message}` : fail.error,
1099
+ errorCode: fail.code,
1100
+ narrixDurationMs,
1101
+ })
1102
+ .catch(() => undefined);
954
1103
  }
1104
+ return withTrace({
1105
+ skillKey: request.skillKey,
1106
+ rawText: JSON.stringify(narrixResult, null, 2),
1107
+ flexMd: { frame: "narrixThenDirect.v1", payloads: { narrix: JSON.stringify(narrixResult) } },
1108
+ parsed: {
1109
+ ok: false,
1110
+ error: narrixResult.error,
1111
+ phase: "narrix",
1112
+ message: narrixResult.message,
1113
+ },
1114
+ metadata: {
1115
+ instructionVersion: "narrix-then-direct",
1116
+ narrix: { ...narrixResult, durationMs: narrixDurationMs },
1117
+ narrixMode: knobs.narrixMode,
1118
+ inputStrategyKey: knobs.inputStrategyKey,
1119
+ executionStrategiesSummary: executionStrategiesSummaryFromKnobs(knobs),
1120
+ },
1121
+ });
955
1122
  }
1123
+ const filteredEntry = applyNarrixScope(narrixResult, request.narrixScope);
1124
+ const existingNarrix = Array.isArray(request.taskMemory?.narrix)
1125
+ ? request.taskMemory.narrix
1126
+ : [];
1127
+ const updatedTaskMemory = {
1128
+ ...(request.taskMemory ?? {}),
1129
+ narrix: [...existingNarrix, filteredEntry],
1130
+ };
1131
+ const skillInput = typeof request.input === "object" && request.input !== null
1132
+ ? { ...request.input, narrixContext: filteredEntry }
1133
+ : { narrixContext: filteredEntry };
956
1134
  request = {
957
1135
  ...request,
958
- executionMemory: {
959
- ...(request.executionMemory || {}),
960
- [attachToField]: attachment,
961
- ...(webContextEntry !== undefined ? { webContext: webContextEntry } : {}),
962
- },
963
- jobMemory: request.jobMemory ? { ...request.jobMemory, _narrix: attachment } : { _narrix: attachment },
964
- };
965
- const scopingSignalsCount = Array.isArray(attachment?.scoping?.signals) ? attachment.scoping.signals.length : undefined;
966
- const scopingStoriesCount = Array.isArray(attachment?.scoping?.stories) ? attachment.scoping.stories.length : undefined;
967
- const discoverySignalsCount = Array.isArray(attachment?.discovery?.signals) ? attachment.discovery.signals.length : undefined;
968
- const discoveryStoriesCount = Array.isArray(attachment?.discovery?.stories) ? attachment.discovery.stories.length : undefined;
969
- const entityKey = narrixResult?.entity?.entityKey;
970
- const entityKind = narrixResult?.entity?.entityKind;
971
- return {
972
- datasetId: narrixCfg.datasetId,
973
- attachToField,
974
- enableWebScope: narrixCfg.enableWebScope === true,
975
- webContextAvailable,
976
- webContextReason,
977
- attachmentMeta: isRecord(attachment?.meta) ? attachment.meta : undefined,
978
- scopingSignalsCount,
979
- scopingStoriesCount,
980
- discoverySignalsCount,
981
- discoveryStoriesCount,
982
- entityKey: typeof entityKey === "string" ? entityKey : undefined,
983
- entityKind: typeof entityKind === "string" ? entityKind : undefined,
984
- medium: typeof adaptResult.narrixInput?.medium === "string" ? adaptResult.narrixInput.medium : undefined,
1136
+ taskMemory: updatedTaskMemory,
1137
+ input: skillInput,
985
1138
  };
986
- },
987
- onSuccessUpdates: (r) => ({
988
- outer: activixOuterTier({
989
- kind: "narrix.preProcessor.request",
990
- datasetId: narrixCfg.datasetId,
991
- attachToField: narrixCfg.attachToField ?? "_narrix",
992
- enableWebScope: narrixCfg.enableWebScope === true,
993
- webScoping: narrixCfg.webScoping ? summarizeForOuter(narrixCfg.webScoping, 2_000) : undefined,
994
- requestInput: summarizeForOuter(request.input, 4_000),
995
- }, {
996
- kind: "narrix.preProcessor.result",
997
- datasetId: r.datasetId,
998
- attachToField: r.attachToField,
999
- enableWebScope: r.enableWebScope,
1000
- entity: r.entityKey ? { entityKey: r.entityKey, entityKind: r.entityKind } : undefined,
1001
- counts: {
1002
- scopingSignals: r.scopingSignalsCount,
1003
- scopingStories: r.scopingStoriesCount,
1004
- discoverySignals: r.discoverySignalsCount,
1005
- discoveryStories: r.discoveryStoriesCount,
1006
- },
1007
- webContext: r.enableWebScope
1008
- ? { available: r.webContextAvailable, reason: r.webContextReason }
1009
- : undefined,
1010
- attachmentMeta: r.attachmentMeta,
1011
- }, { phase: "narrix" }),
1012
- }),
1013
- }));
1014
- }
1015
- // Structured Narrix (`narrixInput`): same enrichment as legacy `narrix-then-direct`, but runs before local tasks / pipeline PRE.
1016
- if (knobs.narrixMode === "handler") {
1017
- if (!request.narrixInput) {
1018
- throw new Error(`narrixMode "handler" requires narrixInput. ` +
1019
- `Provide { medium, datasetId, ... } or { $path: "jobMemory.currentRecord" } on the request.`);
1020
- }
1021
- const resolvedNarrixInput = resolveNarrixInput(request.narrixInput, request.jobMemory);
1022
- if (!resolvedNarrixInput) {
1023
- throw new Error(`narrixMode "handler": narrixInput could not be resolved. ` +
1024
- `Ensure it has medium and datasetId, or that $path points to a valid object in jobMemory.`);
1025
- }
1026
- let narrixThenDirectPhaseRecordId = null;
1027
- if (ax && correlationId) {
1028
- try {
1029
- narrixThenDirectPhaseRecordId = (await ax.startRecord({
1030
- correlationId,
1031
- phase: "narrix_then_direct",
1032
- ...baseMeta,
1033
- narrixDatasetId: resolvedNarrixInput.datasetId,
1034
- narrixMedium: resolvedNarrixInput.medium,
1035
- outer: activixOuterTier({
1036
- kind: "narrixThenDirect.request",
1037
- skillKey: request.skillKey,
1038
- jobId: request.jobId,
1039
- agentId: request.agentId,
1040
- narrixInput: summarizeForOuter(resolvedNarrixInput, 6_000),
1041
- }, null, { phase: "narrix_then_direct" }),
1042
- })).recordId;
1139
+ if (ax && correlationId && narrixThenDirectPhaseRecordId) {
1140
+ await ax
1141
+ .completeRecord(narrixThenDirectPhaseRecordId, {
1142
+ narrixDurationMs,
1143
+ outer: activixOuterTier({
1144
+ kind: "narrixThenDirect.request",
1145
+ skillKey: request.skillKey,
1146
+ jobId: request.jobId,
1147
+ agentId: request.agentId,
1148
+ narrixInput: summarizeForOuter(resolvedNarrixInput, 6_000),
1149
+ }, {
1150
+ kind: "narrixThenDirect.result",
1151
+ ok: true,
1152
+ durationMs: narrixDurationMs,
1153
+ }, { phase: "narrix_then_direct" }),
1154
+ })
1155
+ .catch(() => undefined);
1156
+ }
1043
1157
  }
1044
- catch {
1045
- narrixThenDirectPhaseRecordId = null;
1158
+ catch (error) {
1159
+ if (ax && correlationId && narrixThenDirectPhaseRecordId) {
1160
+ await ax
1161
+ .failRecord(narrixThenDirectPhaseRecordId, error instanceof Error ? error : new Error(String(error)), {
1162
+ errorMessage: error instanceof Error ? error.message : String(error),
1163
+ errorCode: error?.code,
1164
+ outer: activixOuterTier({
1165
+ kind: "narrixThenDirect.request",
1166
+ skillKey: request.skillKey,
1167
+ jobId: request.jobId,
1168
+ agentId: request.agentId,
1169
+ narrixInput: summarizeForOuter(resolvedNarrixInput, 6_000),
1170
+ }, {
1171
+ kind: "narrixThenDirect.error",
1172
+ message: error instanceof Error ? error.message : String(error),
1173
+ }, { phase: "narrix_then_direct" }),
1174
+ })
1175
+ .catch(() => undefined);
1176
+ }
1177
+ throw error;
1046
1178
  }
1047
1179
  }
1048
- try {
1049
- const narrixStarted = Date.now();
1050
- const narrixHandlerCtx = {
1051
- skillKey: request.skillKey,
1052
- jobMemory: request.jobMemory,
1053
- taskMemory: request.taskMemory,
1054
- executionMemory: request.executionMemory,
1055
- variables: request.variables,
1056
- xynthesized: request.xynthesized,
1057
- smartInput: request.smartInput,
1058
- jobId: request.jobId,
1059
- taskId: request.taskId,
1060
- agentId: request.agentId,
1061
- graphId: request.graphId,
1062
- nodeId: request.nodeId,
1063
- prevNodeId: request.prevNodeId,
1064
- coreSkillId: request.coreSkillId,
1065
- masterSkillId: request.masterSkillId,
1066
- masterSkillActivityId: request.masterSkillActivityId,
1067
- };
1068
- const narrixResult = await traceWrap(traceCollector, {
1180
+ // Local task dispatch: if a handler is registered, run it and return (no enrichment)
1181
+ const handler = getLocalTask(request.skillKey);
1182
+ if (handler) {
1183
+ const result = await traceWrap(traceCollector, {
1069
1184
  taskType: "pre-execution",
1070
- details: "narrix structured handler (narrixInput)",
1185
+ details: "local task handler",
1071
1186
  metadata: {
1072
1187
  ...baseTraceMeta(),
1073
- phase: "narrix_then_direct",
1074
- datasetId: resolvedNarrixInput.datasetId,
1075
- medium: resolvedNarrixInput.medium,
1188
+ phase: "local",
1189
+ localSkillKey: request.skillKey,
1076
1190
  },
1077
- }, async () => narrixRunHandler({ input: resolvedNarrixInput, ctx: narrixHandlerCtx }), (r) => ({
1078
- metadata: { ok: r?.ok === true },
1079
1191
  modelUsed: null,
1080
- }));
1081
- const narrixDurationMs = Date.now() - narrixStarted;
1082
- if (!narrixResult.ok) {
1083
- const fail = narrixResult;
1084
- if (ax && correlationId && narrixThenDirectPhaseRecordId) {
1085
- await ax
1086
- .failRecord(narrixThenDirectPhaseRecordId, new Error(`Narrix handler failed: ${fail.error}${fail.message ? ` - ${fail.message}` : ""}`), {
1087
- errorMessage: fail.message ? `${fail.error} - ${fail.message}` : fail.error,
1088
- errorCode: fail.code,
1089
- narrixDurationMs,
1090
- })
1091
- .catch(() => undefined);
1092
- }
1093
- return withTrace({
1094
- skillKey: request.skillKey,
1095
- rawText: JSON.stringify(narrixResult, null, 2),
1096
- flexMd: { frame: "narrixThenDirect.v1", payloads: { narrix: JSON.stringify(narrixResult) } },
1097
- parsed: {
1098
- ok: false,
1099
- error: narrixResult.error,
1100
- phase: "narrix",
1101
- message: narrixResult.message,
1102
- },
1103
- metadata: {
1104
- instructionVersion: "narrix-then-direct",
1105
- narrix: { ...narrixResult, durationMs: narrixDurationMs },
1106
- narrixMode: knobs.narrixMode,
1107
- inputStrategyKey: knobs.inputStrategyKey,
1108
- executionStrategiesSummary: executionStrategiesSummaryFromKnobs(knobs),
1109
- },
1110
- });
1111
- }
1112
- const filteredEntry = applyNarrixScope(narrixResult, request.narrixScope);
1113
- const existingNarrix = Array.isArray(request.taskMemory?.narrix)
1114
- ? request.taskMemory.narrix
1115
- : [];
1116
- const updatedTaskMemory = {
1117
- ...(request.taskMemory ?? {}),
1118
- narrix: [...existingNarrix, filteredEntry],
1119
- };
1120
- const skillInput = typeof request.input === "object" && request.input !== null
1121
- ? { ...request.input, narrixContext: filteredEntry }
1122
- : { narrixContext: filteredEntry };
1123
- request = {
1124
- ...request,
1125
- taskMemory: updatedTaskMemory,
1126
- input: skillInput,
1127
- };
1128
- if (ax && correlationId && narrixThenDirectPhaseRecordId) {
1129
- await ax
1130
- .completeRecord(narrixThenDirectPhaseRecordId, {
1131
- narrixDurationMs,
1192
+ }, async () => withPhaseRecord({
1193
+ ax,
1194
+ correlationId,
1195
+ phase: "local",
1196
+ meta: {
1197
+ ...baseMeta,
1198
+ localSkillKey: request.skillKey,
1132
1199
  outer: activixOuterTier({
1133
- kind: "narrixThenDirect.request",
1200
+ kind: "localTask.request",
1134
1201
  skillKey: request.skillKey,
1135
1202
  jobId: request.jobId,
1136
1203
  agentId: request.agentId,
1137
- narrixInput: summarizeForOuter(resolvedNarrixInput, 6_000),
1138
- }, {
1139
- kind: "narrixThenDirect.result",
1140
- ok: true,
1141
- durationMs: narrixDurationMs,
1142
- }, { phase: "narrix_then_direct" }),
1143
- })
1144
- .catch(() => undefined);
1145
- }
1146
- }
1147
- catch (error) {
1148
- if (ax && correlationId && narrixThenDirectPhaseRecordId) {
1149
- await ax
1150
- .failRecord(narrixThenDirectPhaseRecordId, error instanceof Error ? error : new Error(String(error)), {
1151
- errorMessage: error instanceof Error ? error.message : String(error),
1152
- errorCode: error?.code,
1153
- outer: activixOuterTier({
1154
- kind: "narrixThenDirect.request",
1204
+ input: summarizeForOuter(request.input, 4_000),
1205
+ }, null, { phase: "local" }),
1206
+ },
1207
+ fn: async () => {
1208
+ const started = Date.now();
1209
+ const ctx = {
1155
1210
  skillKey: request.skillKey,
1156
- jobId: request.jobId,
1157
- agentId: request.agentId,
1158
- narrixInput: summarizeForOuter(resolvedNarrixInput, 6_000),
1159
- }, {
1160
- kind: "narrixThenDirect.error",
1161
- message: error instanceof Error ? error.message : String(error),
1162
- }, { phase: "narrix_then_direct" }),
1163
- })
1164
- .catch(() => undefined);
1165
- }
1166
- throw error;
1167
- }
1168
- }
1169
- // Local task dispatch: if a handler is registered, run it and return (no enrichment)
1170
- const handler = getLocalTask(request.skillKey);
1171
- if (handler) {
1172
- const result = await traceWrap(traceCollector, {
1173
- taskType: "pre-execution",
1174
- details: "local task handler",
1175
- metadata: {
1176
- ...baseTraceMeta(),
1177
- phase: "local",
1178
- localSkillKey: request.skillKey,
1179
- },
1180
- modelUsed: null,
1181
- }, async () => withPhaseRecord({
1182
- ax,
1183
- correlationId,
1184
- phase: "local",
1185
- meta: {
1186
- ...baseMeta,
1187
- localSkillKey: request.skillKey,
1188
- outer: activixOuterTier({
1189
- kind: "localTask.request",
1190
- skillKey: request.skillKey,
1191
- jobId: request.jobId,
1192
- agentId: request.agentId,
1193
- input: summarizeForOuter(request.input, 4_000),
1194
- }, null, { phase: "local" }),
1195
- },
1196
- fn: async () => {
1197
- const started = Date.now();
1198
- const ctx = {
1199
- skillKey: request.skillKey,
1200
- jobMemory: request.jobMemory,
1201
- taskMemory: request.taskMemory,
1202
- executionMemory: request.executionMemory,
1203
- variables: input.variables,
1204
- xynthesized: request.xynthesized,
1205
- smartInput: request.smartInput,
1206
- jobId: input.jobId,
1207
- taskId: input.taskId,
1208
- agentId: input.agentId,
1209
- graphId: input.graphId,
1210
- nodeId: input.nodeId,
1211
- prevNodeId: input.prevNodeId,
1212
- coreSkillId: input.coreSkillId,
1213
- masterSkillId: request.masterSkillId,
1214
- masterSkillActivityId: request.masterSkillActivityId,
1215
- };
1216
- const value = await handler({ input: request.input, ctx });
1217
- const durationMs = Date.now() - started;
1218
- let parsed = value;
1219
- let intermediateSteps;
1220
- if (value !== null &&
1221
- typeof value === "object" &&
1222
- !Array.isArray(value) &&
1223
- "intermediateSteps" in value) {
1224
- const steps = value.intermediateSteps;
1225
- if (Array.isArray(steps) &&
1226
- steps.length > 0 &&
1227
- steps.every((s) => typeof s === "object" && s !== null && "step" in s && "id" in s && "ok" in s)) {
1228
- intermediateSteps = steps;
1229
- const { intermediateSteps: _removed, ...rest } = value;
1230
- parsed = (Object.keys(rest).length > 0 ? rest : undefined);
1231
- }
1232
- }
1233
- const response = {
1234
- skillKey: request.skillKey,
1235
- rawText: JSON.stringify(value, null, 2),
1236
- flexMd: {
1237
- frame: "localTask.v1",
1238
- payloads: { value: JSON.stringify(value, null, 2) },
1239
- },
1240
- parsed,
1241
- metadata: {
1242
- instructionVersion: "local",
1243
- activityId: undefined,
1244
- durationMs,
1245
- localSkillKey: request.skillKey,
1246
- },
1247
- };
1248
- if (intermediateSteps)
1249
- response.intermediateSteps = intermediateSteps;
1250
- if (isStrictDecisionTask(request)) {
1251
- const parsedMissing = response.parsed === undefined || response.parsed === null;
1252
- if (parsedMissing) {
1253
- throw new Error(`Decision task "${request.skillKey}" did not return response.parsed`);
1211
+ jobMemory: request.jobMemory,
1212
+ taskMemory: request.taskMemory,
1213
+ executionMemory: request.executionMemory,
1214
+ variables: input.variables,
1215
+ xynthesized: request.xynthesized,
1216
+ smartInput: request.smartInput,
1217
+ jobId: input.jobId,
1218
+ taskId: input.taskId,
1219
+ agentId: input.agentId,
1220
+ graphId: input.graphId,
1221
+ nodeId: input.nodeId,
1222
+ prevNodeId: input.prevNodeId,
1223
+ coreSkillId: input.coreSkillId,
1224
+ masterSkillId: request.masterSkillId,
1225
+ masterSkillActivityId: request.masterSkillActivityId,
1226
+ };
1227
+ const value = await handler({ input: request.input, ctx });
1228
+ const durationMs = Date.now() - started;
1229
+ let parsed = value;
1230
+ let intermediateSteps;
1231
+ if (value !== null &&
1232
+ typeof value === "object" &&
1233
+ !Array.isArray(value) &&
1234
+ "intermediateSteps" in value) {
1235
+ const steps = value.intermediateSteps;
1236
+ if (Array.isArray(steps) &&
1237
+ steps.length > 0 &&
1238
+ steps.every((s) => typeof s === "object" && s !== null && "step" in s && "id" in s && "ok" in s)) {
1239
+ intermediateSteps = steps;
1240
+ const { intermediateSteps: _removed, ...rest } = value;
1241
+ parsed = (Object.keys(rest).length > 0 ? rest : undefined);
1242
+ }
1254
1243
  }
1255
- }
1256
- const outputValidation = request.outputValidation;
1257
- if (outputValidation?.schema) {
1258
- const validateWhenMissing = outputValidation.validateWhenMissing !== false;
1259
- if (validateWhenMissing || response.parsed !== undefined) {
1260
- const vr = validateParsedOutput(response.parsed, outputValidation.schema);
1261
- vr.correlationId = taskId;
1262
- response.metadata = { ...response.metadata, outputValidation: vr };
1263
- if (!vr.ok && (outputValidation.mode ?? "fail") === "fail") {
1264
- throw new Error(`outputValidation failed for skillKey ${request.skillKey}: ` +
1265
- vr.errors.map((e) => `${e.path} ${e.message}`).join("; "));
1244
+ const response = {
1245
+ skillKey: request.skillKey,
1246
+ rawText: JSON.stringify(value, null, 2),
1247
+ flexMd: {
1248
+ frame: "localTask.v1",
1249
+ payloads: { value: JSON.stringify(value, null, 2) },
1250
+ },
1251
+ parsed,
1252
+ metadata: {
1253
+ instructionVersion: "local",
1254
+ activityId: undefined,
1255
+ durationMs,
1256
+ localSkillKey: request.skillKey,
1257
+ },
1258
+ };
1259
+ if (intermediateSteps)
1260
+ response.intermediateSteps = intermediateSteps;
1261
+ if (isStrictDecisionTask(request)) {
1262
+ const parsedMissing = response.parsed === undefined || response.parsed === null;
1263
+ if (parsedMissing) {
1264
+ throw new Error(`Decision task "${request.skillKey}" did not return response.parsed`);
1266
1265
  }
1267
- if (!vr.ok && (outputValidation.mode ?? "fail") === "warn") {
1266
+ }
1267
+ const outputValidation = request.outputValidation;
1268
+ if (outputValidation?.schema) {
1269
+ const validateWhenMissing = outputValidation.validateWhenMissing !== false;
1270
+ if (validateWhenMissing || response.parsed !== undefined) {
1271
+ const vr = validateParsedOutput(response.parsed, outputValidation.schema);
1272
+ vr.correlationId = taskId;
1273
+ response.metadata = { ...response.metadata, outputValidation: vr };
1274
+ if (!vr.ok && (outputValidation.mode ?? "fail") === "fail") {
1275
+ throw new Error(`outputValidation failed for skillKey ${request.skillKey}: ` +
1276
+ vr.errors.map((e) => `${e.path} ${e.message}`).join("; "));
1277
+ }
1278
+ if (!vr.ok && (outputValidation.mode ?? "fail") === "warn") {
1279
+ }
1268
1280
  }
1269
1281
  }
1270
- }
1271
- return response;
1272
- },
1273
- onSuccessUpdates: (result) => {
1274
- const steps = result?.intermediateSteps;
1275
- return {
1276
- localSkillKey: result?.metadata?.localSkillKey,
1277
- durationMs: result?.metadata?.durationMs,
1278
- hasIntermediateSteps: Array.isArray(steps) && steps.length > 0,
1279
- stepCount: Array.isArray(steps) ? steps.length : undefined,
1280
- outer: activixOuterTier({
1281
- kind: "localTask.request",
1282
- skillKey: request.skillKey,
1283
- jobId: request.jobId,
1284
- agentId: request.agentId,
1285
- input: summarizeForOuter(request.input, 4_000),
1286
- }, {
1287
- kind: "localTask.response",
1288
- skillKey: result?.skillKey,
1289
- metadata: summarizeForOuter(result?.metadata, 4_000),
1290
- parsed: summarizeForOuter(result?.parsed, 8_000),
1291
- }, { phase: "local" }),
1292
- };
1293
- },
1294
- }), (r) => ({
1295
- metadata: {
1296
- durationMs: r?.metadata?.durationMs,
1297
- },
1298
- modelUsed: null,
1299
- }));
1300
- return withTrace(finalize(result));
1301
- }
1302
- // Execution pipeline: when request.executionPipeline is set, run PRE steps then main then POST
1303
- const pipeline = request.executionPipeline;
1304
- if (pipeline && Array.isArray(pipeline) && pipeline.length > 0) {
1305
- const mainSteps = pipeline.filter((s) => s.phase === "main");
1306
- if (mainSteps.length !== 1) {
1307
- throw new Error(`executionPipeline must have exactly one step with phase "main"; got ${mainSteps.length}. skillKey: ${request.skillKey}`);
1282
+ return response;
1283
+ },
1284
+ onSuccessUpdates: (result) => {
1285
+ const steps = result?.intermediateSteps;
1286
+ return {
1287
+ localSkillKey: result?.metadata?.localSkillKey,
1288
+ durationMs: result?.metadata?.durationMs,
1289
+ hasIntermediateSteps: Array.isArray(steps) && steps.length > 0,
1290
+ stepCount: Array.isArray(steps) ? steps.length : undefined,
1291
+ outer: activixOuterTier({
1292
+ kind: "localTask.request",
1293
+ skillKey: request.skillKey,
1294
+ jobId: request.jobId,
1295
+ agentId: request.agentId,
1296
+ input: summarizeForOuter(request.input, 4_000),
1297
+ }, {
1298
+ kind: "localTask.response",
1299
+ skillKey: result?.skillKey,
1300
+ metadata: summarizeForOuter(result?.metadata, 4_000),
1301
+ parsed: summarizeForOuter(result?.parsed, 8_000),
1302
+ }, { phase: "local" }),
1303
+ };
1304
+ },
1305
+ }), (r) => ({
1306
+ metadata: {
1307
+ durationMs: r?.metadata?.durationMs,
1308
+ },
1309
+ modelUsed: null,
1310
+ }));
1311
+ return withTrace(finalize(result));
1308
1312
  }
1309
- const preSteps = pipeline.filter((s) => s.phase === "pre");
1310
- let overrideContext;
1311
- let synthesizedArtifact;
1312
- let detectedTemplateCores = [];
1313
- // Aggregated trace-mode-only LlmCallObservations from PRE synthesis (markdown/structured/questionDriven).
1314
- // Lifted into `result.metadata.synthesizedContextLlmCalls` once the pipeline finishes.
1315
- const aggregatedSynthLlmCalls = [];
1316
- for (const step of preSteps) {
1317
- if (step.type !== SYNTHESIZED_CONTEXT) {
1318
- throw new Error(`executionPipeline: unsupported PRE step type "${String(step.type)}". Only "${SYNTHESIZED_CONTEXT}" (Xynthesis synthesized-context) is supported.`);
1313
+ // Execution pipeline: when request.executionPipeline is set, run PRE steps then main then POST
1314
+ const pipeline = request.executionPipeline;
1315
+ if (pipeline && Array.isArray(pipeline) && pipeline.length > 0) {
1316
+ const mainSteps = pipeline.filter((s) => s.phase === "main");
1317
+ if (mainSteps.length !== 1) {
1318
+ throw new Error(`executionPipeline must have exactly one step with phase "main"; got ${mainSteps.length}. skillKey: ${request.skillKey}`);
1319
1319
  }
1320
- {
1321
- const config = (step.config ?? {});
1322
- if (request.includeContextInPrompt !== true && config.autoEnableContext !== true) {
1323
- throw new Error("synthesized-context PRE step requires includeContextInPrompt: true or synthesisConfig.autoEnableContext: true");
1320
+ const preSteps = pipeline.filter((s) => s.phase === "pre");
1321
+ let overrideContext;
1322
+ let synthesizedArtifact;
1323
+ let detectedTemplateCores = [];
1324
+ // Aggregated trace-mode-only LlmCallObservations from PRE synthesis (markdown/structured/questionDriven).
1325
+ // Lifted into `result.metadata.synthesizedContextLlmCalls` once the pipeline finishes.
1326
+ const aggregatedSynthLlmCalls = [];
1327
+ for (const step of preSteps) {
1328
+ if (step.type !== SYNTHESIZED_CONTEXT) {
1329
+ throw new Error(`executionPipeline: unsupported PRE step type "${String(step.type)}". Only "${SYNTHESIZED_CONTEXT}" (Xynthesis synthesized-context) is supported.`);
1324
1330
  }
1325
- const reqWithContext = { ...request, includeContextInPrompt: true };
1326
- const cachedSynth = request.executionMemory?.synthesizedContext;
1327
- const preStepResult = config.reuseCachedSynthesizedContext === true &&
1328
- cachedSynth &&
1329
- reuseableSynthesizedArtifact(cachedSynth)
1330
- ? {
1331
- contextMarkdown: contextMarkdownFromArtifact(cachedSynth),
1332
- artifact: cachedSynth,
1333
- synthesizedInput: request.executionMemory?.synthesizedInput,
1331
+ {
1332
+ const config = (step.config ?? {});
1333
+ if (request.includeContextInPrompt !== true && config.autoEnableContext !== true) {
1334
+ throw new Error("synthesized-context PRE step requires includeContextInPrompt: true or synthesisConfig.autoEnableContext: true");
1334
1335
  }
1335
- : await traceWrap(traceCollector, {
1336
- taskType: "pre-execution",
1337
- details: "synthesized-context pre step",
1338
- metadata: {
1339
- ...baseTraceMeta(),
1340
- phase: "pipeline_pre",
1341
- preStepType: "synthesized-context",
1342
- synthesisMode: resolveSynthesisMode(config),
1343
- reusedCache: false,
1344
- ...(config.xynthesizedOutput && traceCollector
1345
- ? {
1346
- xynthesized: {
1347
- inputPathsUsed: [...(config.memoryPaths ?? [])],
1348
- outputDestination: config.xynthesizedOutput.destination,
1349
- outputKey: config.xynthesizedOutput.outputKey,
1350
- patchKeys: [config.xynthesizedOutput.outputKey],
1351
- },
1352
- smartInput: {
1353
- paths: reqWithContext.smartInput?.paths ?? [],
1354
- },
1355
- }
1356
- : traceCollector
1336
+ const reqWithContext = { ...request, includeContextInPrompt: true };
1337
+ const cachedSynth = request.executionMemory?.synthesizedContext;
1338
+ const preStepResult = config.reuseCachedSynthesizedContext === true &&
1339
+ cachedSynth &&
1340
+ reuseableSynthesizedArtifact(cachedSynth)
1341
+ ? {
1342
+ contextMarkdown: contextMarkdownFromArtifact(cachedSynth),
1343
+ artifact: cachedSynth,
1344
+ synthesizedInput: request.executionMemory?.synthesizedInput,
1345
+ }
1346
+ : await traceWrap(traceCollector, {
1347
+ taskType: "pre-execution",
1348
+ details: "synthesized-context pre step",
1349
+ metadata: {
1350
+ ...baseTraceMeta(),
1351
+ phase: "pipeline_pre",
1352
+ preStepType: "synthesized-context",
1353
+ synthesisMode: resolveSynthesisMode(config),
1354
+ reusedCache: false,
1355
+ ...(config.xynthesizedOutput && traceCollector
1357
1356
  ? {
1357
+ xynthesized: {
1358
+ inputPathsUsed: [...(config.memoryPaths ?? [])],
1359
+ outputDestination: config.xynthesizedOutput.destination,
1360
+ outputKey: config.xynthesizedOutput.outputKey,
1361
+ patchKeys: [config.xynthesizedOutput.outputKey],
1362
+ },
1358
1363
  smartInput: {
1359
1364
  paths: reqWithContext.smartInput?.paths ?? [],
1360
1365
  },
1361
1366
  }
1367
+ : traceCollector
1368
+ ? {
1369
+ smartInput: {
1370
+ paths: reqWithContext.smartInput?.paths ?? [],
1371
+ },
1372
+ }
1373
+ : {}),
1374
+ },
1375
+ }, async () => withPhaseRecord({
1376
+ ax,
1377
+ correlationId,
1378
+ phase: "pipeline_pre",
1379
+ meta: {
1380
+ ...baseMeta,
1381
+ preStepType: "synthesized-context",
1382
+ outer: activixOuterTier({
1383
+ kind: "pipelinePre.synthesizedContext.request",
1384
+ skillKey: reqWithContext.skillKey,
1385
+ jobId: reqWithContext.jobId,
1386
+ agentId: reqWithContext.agentId,
1387
+ contextSourcePolicy: config?.contextSourcePolicy,
1388
+ synthesisMode: resolveSynthesisMode(config),
1389
+ input: summarizeForOuter(reqWithContext.input, 4_000),
1390
+ }, null, { phase: "pipeline_pre" }),
1391
+ },
1392
+ fn: async () => this._runSynthesizedContextPreStep(reqWithContext, config, traceCollector),
1393
+ onSuccessUpdates: (r) => ({
1394
+ outer: activixOuterTier({
1395
+ kind: "pipelinePre.synthesizedContext.request",
1396
+ skillKey: reqWithContext.skillKey,
1397
+ jobId: reqWithContext.jobId,
1398
+ agentId: reqWithContext.agentId,
1399
+ contextSourcePolicy: config?.contextSourcePolicy,
1400
+ synthesisMode: resolveSynthesisMode(config),
1401
+ input: summarizeForOuter(reqWithContext.input, 4_000),
1402
+ }, {
1403
+ kind: "pipelinePre.synthesizedContext.result",
1404
+ contextMarkdown: summarizeForOuter(r.contextMarkdown, 8_000),
1405
+ artifact: r.artifact
1406
+ ? {
1407
+ mode: r.artifact.mode,
1408
+ templateCores: r.artifact?.templateCores ?? [],
1409
+ question: typeof r.artifact?.question === "string"
1410
+ ? summarizeForOuter(r.artifact.question, 2_000)
1411
+ : undefined,
1412
+ }
1413
+ : undefined,
1414
+ }, { phase: "pipeline_pre" }),
1415
+ }),
1416
+ }), (r) => ({
1417
+ metadata: {
1418
+ synthesizedContextChars: typeof r.contextMarkdown === "string" ? r.contextMarkdown.length : 0,
1419
+ ...(config.xynthesizedOutput && traceCollector
1420
+ ? {
1421
+ xynthesized: {
1422
+ inputPathsUsed: [...(config.memoryPaths ?? [])],
1423
+ outputDestination: config.xynthesizedOutput.destination,
1424
+ outputKey: config.xynthesizedOutput.outputKey,
1425
+ patchKeys: [config.xynthesizedOutput.outputKey],
1426
+ },
1427
+ }
1362
1428
  : {}),
1363
- },
1364
- }, async () => withPhaseRecord({
1365
- ax,
1366
- correlationId,
1367
- phase: "pipeline_pre",
1368
- meta: {
1369
- ...baseMeta,
1370
- preStepType: "synthesized-context",
1371
- outer: activixOuterTier({
1372
- kind: "pipelinePre.synthesizedContext.request",
1373
- skillKey: reqWithContext.skillKey,
1374
- jobId: reqWithContext.jobId,
1375
- agentId: reqWithContext.agentId,
1376
- contextSourcePolicy: config?.contextSourcePolicy,
1377
- synthesisMode: resolveSynthesisMode(config),
1378
- input: summarizeForOuter(reqWithContext.input, 4_000),
1379
- }, null, { phase: "pipeline_pre" }),
1380
- },
1381
- fn: async () => this._runSynthesizedContextPreStep(reqWithContext, config, traceCollector),
1382
- onSuccessUpdates: (r) => ({
1383
- outer: activixOuterTier({
1384
- kind: "pipelinePre.synthesizedContext.request",
1385
- skillKey: reqWithContext.skillKey,
1386
- jobId: reqWithContext.jobId,
1387
- agentId: reqWithContext.agentId,
1388
- contextSourcePolicy: config?.contextSourcePolicy,
1429
+ },
1430
+ }));
1431
+ overrideContext = preStepResult.contextMarkdown;
1432
+ synthesizedArtifact = preStepResult.artifact;
1433
+ if (traceCollector && preStepResult.synthesizedContextLlmCalls) {
1434
+ aggregatedSynthLlmCalls.push(...preStepResult.synthesizedContextLlmCalls);
1435
+ }
1436
+ if (synthesizedArtifact) {
1437
+ detectedTemplateCores = synthesizedArtifact?.templateCores ?? [];
1438
+ }
1439
+ const xynthOut = config.xynthesizedOutput;
1440
+ const writeLegacyToExecutionMemory = !xynthOut || xynthOut.alsoWriteLegacySynthesizedContext !== false;
1441
+ if (synthesizedArtifact && writeLegacyToExecutionMemory) {
1442
+ request = {
1443
+ ...request,
1444
+ executionMemory: {
1445
+ ...(request.executionMemory ?? {}),
1446
+ synthesizedContext: synthesizedArtifact,
1447
+ ...(preStepResult.synthesizedInput ? { synthesizedInput: preStepResult.synthesizedInput } : {}),
1448
+ },
1449
+ };
1450
+ }
1451
+ if (xynthOut) {
1452
+ const synthesizedResult = synthesizedResultForXynthesizedPatch(synthesizedArtifact, preStepResult.contextMarkdown);
1453
+ const { nextXynthesized, patchSlice } = applyXynthesizedOutputWrite(request.xynthesized, xynthOut, synthesizedResult);
1454
+ request = { ...request, xynthesized: nextXynthesized };
1455
+ xynthesizedPatchAccumulator = mergeXynthesizedPatchSlices(xynthesizedPatchAccumulator, patchSlice);
1456
+ }
1457
+ if (traceCollector &&
1458
+ config.reuseCachedSynthesizedContext === true &&
1459
+ cachedSynth &&
1460
+ reuseableSynthesizedArtifact(cachedSynth)) {
1461
+ traceCollector.push({
1462
+ taskType: "pre-execution",
1463
+ details: "synthesized-context pre step (reused cached artifact)",
1464
+ metadata: {
1465
+ ...baseTraceMeta(),
1466
+ phase: "pipeline_pre",
1467
+ preStepType: "synthesized-context",
1389
1468
  synthesisMode: resolveSynthesisMode(config),
1390
- input: summarizeForOuter(reqWithContext.input, 4_000),
1391
- }, {
1392
- kind: "pipelinePre.synthesizedContext.result",
1393
- contextMarkdown: summarizeForOuter(r.contextMarkdown, 8_000),
1394
- artifact: r.artifact
1469
+ reusedCache: true,
1470
+ ...(config.xynthesizedOutput
1395
1471
  ? {
1396
- mode: r.artifact.mode,
1397
- templateCores: r.artifact?.templateCores ?? [],
1398
- question: typeof r.artifact?.question === "string"
1399
- ? summarizeForOuter(r.artifact.question, 2_000)
1400
- : undefined,
1472
+ xynthesized: {
1473
+ inputPathsUsed: [...(config.memoryPaths ?? [])],
1474
+ outputDestination: config.xynthesizedOutput.destination,
1475
+ outputKey: config.xynthesizedOutput.outputKey,
1476
+ patchKeys: [config.xynthesizedOutput.outputKey],
1477
+ },
1478
+ smartInput: {
1479
+ paths: request.smartInput?.paths ?? [],
1480
+ },
1401
1481
  }
1402
- : undefined,
1403
- }, { phase: "pipeline_pre" }),
1404
- }),
1405
- }), (r) => ({
1406
- metadata: {
1407
- synthesizedContextChars: typeof r.contextMarkdown === "string" ? r.contextMarkdown.length : 0,
1408
- ...(config.xynthesizedOutput && traceCollector
1409
- ? {
1410
- xynthesized: {
1411
- inputPathsUsed: [...(config.memoryPaths ?? [])],
1412
- outputDestination: config.xynthesizedOutput.destination,
1413
- outputKey: config.xynthesizedOutput.outputKey,
1414
- patchKeys: [config.xynthesizedOutput.outputKey],
1415
- },
1416
- }
1417
- : {}),
1418
- },
1419
- }));
1420
- overrideContext = preStepResult.contextMarkdown;
1421
- synthesizedArtifact = preStepResult.artifact;
1422
- if (traceCollector && preStepResult.synthesizedContextLlmCalls) {
1423
- aggregatedSynthLlmCalls.push(...preStepResult.synthesizedContextLlmCalls);
1482
+ : {
1483
+ smartInput: {
1484
+ paths: request.smartInput?.paths ?? [],
1485
+ },
1486
+ }),
1487
+ },
1488
+ modelUsed: null,
1489
+ });
1490
+ }
1491
+ break;
1424
1492
  }
1425
- if (synthesizedArtifact) {
1426
- detectedTemplateCores = synthesizedArtifact?.templateCores ?? [];
1493
+ }
1494
+ const synthesizedFromExecution = request.executionMemory?.synthesizedContext &&
1495
+ typeof request.executionMemory.synthesizedContext === "object";
1496
+ const mainContextSource = synthesizedFromExecution ? "synthesizedContext" : "unsynthesized";
1497
+ if (mainContextSource === "synthesizedContext") {
1498
+ const artifact = request.executionMemory?.synthesizedContext;
1499
+ if (artifact && typeof artifact === "object" && typeof artifact.contextMarkdown === "string") {
1500
+ overrideContext = artifact.contextMarkdown;
1427
1501
  }
1428
- const xynthOut = config.xynthesizedOutput;
1429
- const writeLegacyToExecutionMemory = !xynthOut || xynthOut.alsoWriteLegacySynthesizedContext !== false;
1430
- if (synthesizedArtifact && writeLegacyToExecutionMemory) {
1431
- request = {
1432
- ...request,
1433
- executionMemory: {
1434
- ...(request.executionMemory ?? {}),
1435
- synthesizedContext: synthesizedArtifact,
1436
- ...(preStepResult.synthesizedInput ? { synthesizedInput: preStepResult.synthesizedInput } : {}),
1502
+ }
1503
+ const postSteps = pipeline.filter((s) => s.phase === "post");
1504
+ const observability = {
1505
+ synthesisEnabled: preSteps.some((s) => s.type === SYNTHESIZED_CONTEXT),
1506
+ effectiveExecutionPipeline: pipeline,
1507
+ synthesizedContextPresent: synthesizedFromExecution,
1508
+ mainContextSource,
1509
+ detectedTemplateCores,
1510
+ synthesisMode: synthesizedArtifact?.mode === "markdown" || synthesizedArtifact?.mode === "structured"
1511
+ ? synthesizedArtifact.mode
1512
+ : undefined,
1513
+ };
1514
+ const directOpts = {
1515
+ overrideContext,
1516
+ synthesisContextAuthoritative: mainContextSource === "synthesizedContext",
1517
+ };
1518
+ if (postSteps.length === 0) {
1519
+ const result = finalize(await runMainWithExecutionStrategies(request, directOpts));
1520
+ if (overrideContext !== undefined) {
1521
+ const synthesisStep = {
1522
+ step: 1,
1523
+ id: "synthesis",
1524
+ ok: true,
1525
+ summary: "context synthesized",
1526
+ outputExcerpt: {
1527
+ mainContextSource,
1528
+ synthesizedContextPresent: observability.synthesizedContextPresent,
1529
+ detectedTemplateCores: observability.detectedTemplateCores ?? [],
1437
1530
  },
1438
1531
  };
1532
+ const existingSteps = result.intermediateSteps ?? [];
1533
+ result.intermediateSteps = [
1534
+ synthesisStep,
1535
+ ...existingSteps.map((s, i) => ({ ...s, step: i + 2 })),
1536
+ ];
1439
1537
  }
1440
- if (xynthOut) {
1441
- const synthesizedResult = synthesizedResultForXynthesizedPatch(synthesizedArtifact, preStepResult.contextMarkdown);
1442
- const { nextXynthesized, patchSlice } = applyXynthesizedOutputWrite(request.xynthesized, xynthOut, synthesizedResult);
1443
- request = { ...request, xynthesized: nextXynthesized };
1444
- xynthesizedPatchAccumulator = mergeXynthesizedPatchSlices(xynthesizedPatchAccumulator, patchSlice);
1445
- }
1446
- if (traceCollector &&
1447
- config.reuseCachedSynthesizedContext === true &&
1448
- cachedSynth &&
1449
- reuseableSynthesizedArtifact(cachedSynth)) {
1450
- traceCollector.push({
1451
- taskType: "pre-execution",
1452
- details: "synthesized-context pre step (reused cached artifact)",
1453
- metadata: {
1454
- ...baseTraceMeta(),
1455
- phase: "pipeline_pre",
1456
- preStepType: "synthesized-context",
1457
- synthesisMode: resolveSynthesisMode(config),
1458
- reusedCache: true,
1459
- ...(config.xynthesizedOutput
1460
- ? {
1461
- xynthesized: {
1462
- inputPathsUsed: [...(config.memoryPaths ?? [])],
1463
- outputDestination: config.xynthesizedOutput.destination,
1464
- outputKey: config.xynthesizedOutput.outputKey,
1465
- patchKeys: [config.xynthesizedOutput.outputKey],
1466
- },
1467
- smartInput: {
1468
- paths: request.smartInput?.paths ?? [],
1469
- },
1470
- }
1471
- : {
1472
- smartInput: {
1473
- paths: request.smartInput?.paths ?? [],
1474
- },
1475
- }),
1476
- },
1477
- modelUsed: null,
1478
- });
1538
+ attachExecutionStateAndObservability(result, request, observability);
1539
+ if (traceCollector && aggregatedSynthLlmCalls.length > 0) {
1540
+ result.metadata = {
1541
+ ...(result.metadata ?? {}),
1542
+ synthesizedContextLlmCalls: aggregatedSynthLlmCalls,
1543
+ };
1479
1544
  }
1480
- break;
1481
- }
1482
- }
1483
- const synthesizedFromExecution = request.executionMemory?.synthesizedContext &&
1484
- typeof request.executionMemory.synthesizedContext === "object";
1485
- const mainContextSource = synthesizedFromExecution ? "synthesizedContext" : "unsynthesized";
1486
- if (mainContextSource === "synthesizedContext") {
1487
- const artifact = request.executionMemory?.synthesizedContext;
1488
- if (artifact && typeof artifact === "object" && typeof artifact.contextMarkdown === "string") {
1489
- overrideContext = artifact.contextMarkdown;
1545
+ return withTrace(result);
1490
1546
  }
1491
- }
1492
- const postSteps = pipeline.filter((s) => s.phase === "post");
1493
- const observability = {
1494
- synthesisEnabled: preSteps.some((s) => s.type === SYNTHESIZED_CONTEXT),
1495
- effectiveExecutionPipeline: pipeline,
1496
- synthesizedContextPresent: synthesizedFromExecution,
1497
- mainContextSource,
1498
- detectedTemplateCores,
1499
- synthesisMode: synthesizedArtifact?.mode === "markdown" || synthesizedArtifact?.mode === "structured"
1500
- ? synthesizedArtifact.mode
1501
- : undefined,
1502
- };
1503
- const directOpts = {
1504
- overrideContext,
1505
- synthesisContextAuthoritative: mainContextSource === "synthesizedContext",
1506
- };
1507
- if (postSteps.length === 0) {
1508
- const result = finalize(await runMainWithExecutionStrategies(request, directOpts));
1547
+ const capture = { contextMarkdown: "" };
1548
+ let result = finalize(await runMainWithExecutionStrategies(request, { ...directOpts, captureContext: capture }));
1509
1549
  if (overrideContext !== undefined) {
1510
1550
  const synthesisStep = {
1511
1551
  step: 1,
@@ -1524,213 +1564,185 @@ export class WorexClientTasks {
1524
1564
  ...existingSteps.map((s, i) => ({ ...s, step: i + 2 })),
1525
1565
  ];
1526
1566
  }
1527
- attachExecutionStateAndObservability(result, request, observability);
1528
- if (traceCollector && aggregatedSynthLlmCalls.length > 0) {
1529
- result.metadata = {
1530
- ...(result.metadata ?? {}),
1531
- synthesizedContextLlmCalls: aggregatedSynthLlmCalls,
1532
- };
1533
- }
1534
- return withTrace(result);
1535
- }
1536
- const capture = { contextMarkdown: "" };
1537
- let result = finalize(await runMainWithExecutionStrategies(request, { ...directOpts, captureContext: capture }));
1538
- if (overrideContext !== undefined) {
1539
- const synthesisStep = {
1540
- step: 1,
1541
- id: "synthesis",
1542
- ok: true,
1543
- summary: "context synthesized",
1544
- outputExcerpt: {
1545
- mainContextSource,
1546
- synthesizedContextPresent: observability.synthesizedContextPresent,
1547
- detectedTemplateCores: observability.detectedTemplateCores ?? [],
1548
- },
1549
- };
1550
- const existingSteps = result.intermediateSteps ?? [];
1551
- result.intermediateSteps = [
1552
- synthesisStep,
1553
- ...existingSteps.map((s, i) => ({ ...s, step: i + 2 })),
1554
- ];
1555
- }
1556
- let currentOutput = typeof result.rawText === "string"
1557
- ? result.rawText
1558
- : JSON.stringify(result.parsed ?? result.rawText ?? "", null, 2);
1559
- const postStepsMeta = {};
1560
- let nextStepNumber = (result.intermediateSteps?.length ?? 0) + 1;
1561
- const reRunMain = (overrideContextWithFeedback) => runDirect(request, { overrideContext: overrideContextWithFeedback });
1562
- for (const step of postSteps) {
1563
- if (step.type === "audit") {
1564
- const auditResult = await traceWrap(traceCollector, {
1565
- taskType: "post-execution",
1566
- details: "audit post step",
1567
- metadata: {
1568
- ...baseTraceMeta(),
1567
+ let currentOutput = typeof result.rawText === "string"
1568
+ ? result.rawText
1569
+ : JSON.stringify(result.parsed ?? result.rawText ?? "", null, 2);
1570
+ const postStepsMeta = {};
1571
+ let nextStepNumber = (result.intermediateSteps?.length ?? 0) + 1;
1572
+ const reRunMain = (overrideContextWithFeedback) => runDirect(request, { overrideContext: overrideContextWithFeedback });
1573
+ for (const step of postSteps) {
1574
+ if (step.type === "audit") {
1575
+ const auditResult = await traceWrap(traceCollector, {
1576
+ taskType: "post-execution",
1577
+ details: "audit post step",
1578
+ metadata: {
1579
+ ...baseTraceMeta(),
1580
+ phase: "audit",
1581
+ postStepType: "audit",
1582
+ },
1583
+ }, async () => withPhaseRecord({
1584
+ ax,
1585
+ correlationId,
1569
1586
  phase: "audit",
1570
- postStepType: "audit",
1571
- },
1572
- }, async () => withPhaseRecord({
1573
- ax,
1574
- correlationId,
1575
- phase: "audit",
1576
- meta: {
1577
- ...baseMeta,
1578
- postStepType: "audit",
1579
- outer: activixOuterTier({
1580
- kind: "postStep.audit.request",
1581
- skillKey: request.skillKey,
1582
- jobId: request.jobId,
1583
- agentId: request.agentId,
1584
- input: summarizeForOuter({
1585
- currentOutput,
1586
- hasContextMarkdown: !!capture.contextMarkdown,
1587
- }, 8_000),
1588
- }, null, { phase: "audit" }),
1589
- },
1590
- fn: async () => runAuditPostStep({
1591
- request,
1592
- mainResult: result,
1593
- contextMarkdown: capture.contextMarkdown,
1594
- config: (step.config ?? {}),
1595
- reRunMain,
1596
- traceCollector,
1597
- }),
1598
- onSuccessUpdates: (auditResult) => ({
1599
- totalCycles: auditResult.metadata?.totalCycles,
1600
- allMustPassed: auditResult.metadata?.allMustPassed,
1601
- synthesisUsed: auditResult.metadata?.synthesisUsed,
1602
- durationMs: auditResult.metadata?.durationMs,
1603
- outer: activixOuterTier({
1604
- kind: "postStep.audit.request",
1605
- skillKey: request.skillKey,
1606
- jobId: request.jobId,
1607
- agentId: request.agentId,
1608
- input: summarizeForOuter({
1609
- currentOutput,
1610
- hasContextMarkdown: !!capture.contextMarkdown,
1611
- }, 8_000),
1612
- }, {
1613
- kind: "postStep.audit.result",
1614
- outputText: summarizeForOuter(auditResult.outputText, 8_000),
1615
- metadata: summarizeForOuter(auditResult.metadata, 6_000),
1616
- }, { phase: "audit" }),
1617
- }),
1618
- }), (r) => ({
1619
- metadata: {
1620
- durationMs: r?.metadata?.durationMs,
1621
- totalCycles: r?.metadata?.totalCycles,
1622
- allMustPassed: r?.metadata?.allMustPassed,
1623
- },
1624
- modelUsed: r?.metadata?.modelUsed ?? null,
1625
- }));
1626
- currentOutput = auditResult.outputText;
1627
- postStepsMeta.audit = auditResult.metadata;
1628
- const stepsWithNumbers = auditResult.intermediateSteps.map((s) => ({
1629
- ...s,
1630
- step: nextStepNumber++,
1631
- }));
1632
- result.intermediateSteps = [
1633
- ...(result.intermediateSteps ?? []),
1634
- ...stepsWithNumbers,
1635
- ];
1636
- }
1637
- else if (step.type === "polish") {
1638
- const polishResult = await traceWrap(traceCollector, {
1639
- taskType: "post-execution",
1640
- details: "polish post step",
1641
- metadata: {
1642
- ...baseTraceMeta(),
1587
+ meta: {
1588
+ ...baseMeta,
1589
+ postStepType: "audit",
1590
+ outer: activixOuterTier({
1591
+ kind: "postStep.audit.request",
1592
+ skillKey: request.skillKey,
1593
+ jobId: request.jobId,
1594
+ agentId: request.agentId,
1595
+ input: summarizeForOuter({
1596
+ currentOutput,
1597
+ hasContextMarkdown: !!capture.contextMarkdown,
1598
+ }, 8_000),
1599
+ }, null, { phase: "audit" }),
1600
+ },
1601
+ fn: async () => runAuditPostStep({
1602
+ request,
1603
+ mainResult: result,
1604
+ contextMarkdown: capture.contextMarkdown,
1605
+ config: (step.config ?? {}),
1606
+ reRunMain,
1607
+ traceCollector,
1608
+ }),
1609
+ onSuccessUpdates: (auditResult) => ({
1610
+ totalCycles: auditResult.metadata?.totalCycles,
1611
+ allMustPassed: auditResult.metadata?.allMustPassed,
1612
+ synthesisUsed: auditResult.metadata?.synthesisUsed,
1613
+ durationMs: auditResult.metadata?.durationMs,
1614
+ outer: activixOuterTier({
1615
+ kind: "postStep.audit.request",
1616
+ skillKey: request.skillKey,
1617
+ jobId: request.jobId,
1618
+ agentId: request.agentId,
1619
+ input: summarizeForOuter({
1620
+ currentOutput,
1621
+ hasContextMarkdown: !!capture.contextMarkdown,
1622
+ }, 8_000),
1623
+ }, {
1624
+ kind: "postStep.audit.result",
1625
+ outputText: summarizeForOuter(auditResult.outputText, 8_000),
1626
+ metadata: summarizeForOuter(auditResult.metadata, 6_000),
1627
+ }, { phase: "audit" }),
1628
+ }),
1629
+ }), (r) => ({
1630
+ metadata: {
1631
+ durationMs: r?.metadata?.durationMs,
1632
+ totalCycles: r?.metadata?.totalCycles,
1633
+ allMustPassed: r?.metadata?.allMustPassed,
1634
+ },
1635
+ modelUsed: r?.metadata?.modelUsed ?? null,
1636
+ }));
1637
+ currentOutput = auditResult.outputText;
1638
+ postStepsMeta.audit = auditResult.metadata;
1639
+ const stepsWithNumbers = auditResult.intermediateSteps.map((s) => ({
1640
+ ...s,
1641
+ step: nextStepNumber++,
1642
+ }));
1643
+ result.intermediateSteps = [
1644
+ ...(result.intermediateSteps ?? []),
1645
+ ...stepsWithNumbers,
1646
+ ];
1647
+ }
1648
+ else if (step.type === "polish") {
1649
+ const polishResult = await traceWrap(traceCollector, {
1650
+ taskType: "post-execution",
1651
+ details: "polish post step",
1652
+ metadata: {
1653
+ ...baseTraceMeta(),
1654
+ phase: "polish",
1655
+ postStepType: "polish",
1656
+ },
1657
+ }, async () => withPhaseRecord({
1658
+ ax,
1659
+ correlationId,
1643
1660
  phase: "polish",
1644
- postStepType: "polish",
1645
- },
1646
- }, async () => withPhaseRecord({
1647
- ax,
1648
- correlationId,
1649
- phase: "polish",
1650
- meta: {
1651
- ...baseMeta,
1652
- postStepType: "polish",
1653
- outer: activixOuterTier({
1654
- kind: "postStep.polish.request",
1655
- skillKey: request.skillKey,
1661
+ meta: {
1662
+ ...baseMeta,
1663
+ postStepType: "polish",
1664
+ outer: activixOuterTier({
1665
+ kind: "postStep.polish.request",
1666
+ skillKey: request.skillKey,
1667
+ jobId: request.jobId,
1668
+ agentId: request.agentId,
1669
+ input: summarizeForOuter({ currentOutput }, 8_000),
1670
+ }, null, { phase: "polish" }),
1671
+ },
1672
+ fn: async () => runPolishPostStep({
1673
+ currentOutput,
1674
+ config: (step.config ?? {}),
1675
+ originalInput: step.config?.includeOriginalContext ? request.input : undefined,
1676
+ promptContext: step.config?.includeOriginalContext ? capture.contextMarkdown : undefined,
1677
+ traceCollector,
1656
1678
  jobId: request.jobId,
1657
- agentId: request.agentId,
1658
- input: summarizeForOuter({ currentOutput }, 8_000),
1659
- }, null, { phase: "polish" }),
1660
- },
1661
- fn: async () => runPolishPostStep({
1662
- currentOutput,
1663
- config: (step.config ?? {}),
1664
- originalInput: step.config?.includeOriginalContext ? request.input : undefined,
1665
- promptContext: step.config?.includeOriginalContext ? capture.contextMarkdown : undefined,
1666
- traceCollector,
1667
- jobId: request.jobId,
1668
- taskId: request.taskId,
1669
- skillKey: request.skillKey,
1670
- agentId: request.agentId,
1671
- graphId: request.graphId,
1672
- nodeId: request.nodeId,
1673
- prevNodeId: request.prevNodeId,
1674
- coreSkillId: request.coreSkillId,
1675
- masterSkillId: request.masterSkillId,
1676
- identity: request.identity,
1677
- }),
1678
- onSuccessUpdates: (polishResult) => ({
1679
- totalPasses: polishResult.metadata?.totalPasses,
1680
- durationMs: polishResult.metadata?.durationMs,
1681
- outer: activixOuterTier({
1682
- kind: "postStep.polish.request",
1679
+ taskId: request.taskId,
1683
1680
  skillKey: request.skillKey,
1684
- jobId: request.jobId,
1685
1681
  agentId: request.agentId,
1686
- input: summarizeForOuter({ currentOutput }, 8_000),
1687
- }, {
1688
- kind: "postStep.polish.result",
1689
- outputText: summarizeForOuter(polishResult.outputText, 8_000),
1690
- metadata: summarizeForOuter(polishResult.metadata, 6_000),
1691
- }, { phase: "polish" }),
1692
- }),
1693
- }), (r) => ({
1694
- metadata: {
1695
- durationMs: r?.metadata?.durationMs,
1696
- totalPasses: r?.metadata?.totalPasses,
1697
- },
1698
- modelUsed: r?.metadata?.modelUsed ?? null,
1699
- }));
1700
- currentOutput = polishResult.outputText;
1701
- postStepsMeta.polish = polishResult.metadata;
1702
- const stepsWithNumbers = polishResult.intermediateSteps.map((s) => ({
1703
- ...s,
1704
- step: nextStepNumber++,
1705
- }));
1706
- result.intermediateSteps = [
1707
- ...(result.intermediateSteps ?? []),
1708
- ...stepsWithNumbers,
1709
- ];
1682
+ graphId: request.graphId,
1683
+ nodeId: request.nodeId,
1684
+ prevNodeId: request.prevNodeId,
1685
+ coreSkillId: request.coreSkillId,
1686
+ masterSkillId: request.masterSkillId,
1687
+ identity: request.identity,
1688
+ }),
1689
+ onSuccessUpdates: (polishResult) => ({
1690
+ totalPasses: polishResult.metadata?.totalPasses,
1691
+ durationMs: polishResult.metadata?.durationMs,
1692
+ outer: activixOuterTier({
1693
+ kind: "postStep.polish.request",
1694
+ skillKey: request.skillKey,
1695
+ jobId: request.jobId,
1696
+ agentId: request.agentId,
1697
+ input: summarizeForOuter({ currentOutput }, 8_000),
1698
+ }, {
1699
+ kind: "postStep.polish.result",
1700
+ outputText: summarizeForOuter(polishResult.outputText, 8_000),
1701
+ metadata: summarizeForOuter(polishResult.metadata, 6_000),
1702
+ }, { phase: "polish" }),
1703
+ }),
1704
+ }), (r) => ({
1705
+ metadata: {
1706
+ durationMs: r?.metadata?.durationMs,
1707
+ totalPasses: r?.metadata?.totalPasses,
1708
+ },
1709
+ modelUsed: r?.metadata?.modelUsed ?? null,
1710
+ }));
1711
+ currentOutput = polishResult.outputText;
1712
+ postStepsMeta.polish = polishResult.metadata;
1713
+ const stepsWithNumbers = polishResult.intermediateSteps.map((s) => ({
1714
+ ...s,
1715
+ step: nextStepNumber++,
1716
+ }));
1717
+ result.intermediateSteps = [
1718
+ ...(result.intermediateSteps ?? []),
1719
+ ...stepsWithNumbers,
1720
+ ];
1721
+ }
1710
1722
  }
1723
+ result.rawText = currentOutput;
1724
+ result.parsed = currentOutput;
1725
+ result.metadata = {
1726
+ ...(result.metadata ?? {}),
1727
+ postSteps: postStepsMeta,
1728
+ ...(traceCollector && aggregatedSynthLlmCalls.length > 0
1729
+ ? { synthesizedContextLlmCalls: aggregatedSynthLlmCalls }
1730
+ : {}),
1731
+ };
1732
+ attachExecutionStateAndObservability(result, request, observability, knobs);
1733
+ return withTrace(result);
1711
1734
  }
1712
- result.rawText = currentOutput;
1713
- result.parsed = currentOutput;
1714
- result.metadata = {
1715
- ...(result.metadata ?? {}),
1716
- postSteps: postStepsMeta,
1717
- ...(traceCollector && aggregatedSynthLlmCalls.length > 0
1718
- ? { synthesizedContextLlmCalls: aggregatedSynthLlmCalls }
1719
- : {}),
1720
- };
1721
- attachExecutionStateAndObservability(result, request, observability, knobs);
1722
- return withTrace(result);
1723
- }
1724
- // MAIN path (direct / gateway skill). Narrix handler runs earlier when `narrixMode === "handler"`.
1725
- {
1726
- const result = finalize(await runMainWithExecutionStrategies(request));
1727
- attachExecutionStateAndObservability(result, request, {
1728
- synthesisEnabled: false,
1729
- synthesizedContextPresent: !!request.executionMemory?.synthesizedContext,
1730
- mainContextSource: "unsynthesized",
1731
- }, knobs);
1732
- return withTrace(result);
1733
- }
1735
+ // MAIN path (direct / gateway skill). Narrix handler runs earlier when `narrixMode === "handler"`.
1736
+ {
1737
+ const result = finalize(await runMainWithExecutionStrategies(request));
1738
+ attachExecutionStateAndObservability(result, request, {
1739
+ synthesisEnabled: false,
1740
+ synthesizedContextPresent: !!request.executionMemory?.synthesizedContext,
1741
+ mainContextSource: "unsynthesized",
1742
+ }, knobs);
1743
+ return withTrace(result);
1744
+ }
1745
+ });
1734
1746
  }
1735
1747
  /**
1736
1748
  * Run the synthesized-context PRE step: enrich memory, resolve source material, render templates, call synthesis model, return synthesized context.