deepline 0.1.176 → 0.1.178

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.
@@ -607,17 +607,18 @@ var SDK_RELEASE = {
607
607
  // 0.1.111 ships dataset-native tool list getters and result row datasets.
608
608
  // 0.1.154 removes the short-lived generated enrich StepOptions recompute
609
609
  // fields shipped in 0.1.153.
610
- version: "0.1.176",
610
+ // 0.1.175 ships loud `deepline enrich` empty-waterfall reporting.
611
+ version: "0.1.178",
611
612
  apiContract: "2026-06-dataset-handle-results-hard-cutover",
612
613
  supportPolicy: {
613
- latest: "0.1.176",
614
+ latest: "0.1.178",
614
615
  minimumSupported: "0.1.53",
615
616
  deprecatedBelow: "0.1.53",
616
617
  commandMinimumSupported: [
617
618
  {
618
619
  command: "enrich",
619
- minimumSupported: "0.1.154",
620
- reason: "Older SDK CLI enrich generated stale play source; SDK CLI enrich 0.1.153 generated removed StepOptions fields in play source."
620
+ minimumSupported: "0.1.175",
621
+ reason: "Older SDK CLI enrich generated stale play source; SDK CLI enrich 0.1.153 generated removed StepOptions fields in play source; SDK CLI enrich before 0.1.175 could silently succeed on the empty-waterfall bug when a requested waterfall returned no values for selected rows."
621
622
  },
622
623
  {
623
624
  command: "plays",
@@ -7743,7 +7744,7 @@ import {
7743
7744
  writeFile as writeFile3
7744
7745
  } from "fs/promises";
7745
7746
  import { homedir as homedir7, tmpdir as tmpdir2 } from "os";
7746
- import { dirname as dirname7, join as join7, resolve as resolve9 } from "path";
7747
+ import { basename as basename2, dirname as dirname7, extname, join as join7, resolve as resolve9 } from "path";
7747
7748
 
7748
7749
  // src/cli/commands/play.ts
7749
7750
  import { createHash as createHash2 } from "crypto";
@@ -16955,7 +16956,8 @@ var HEAVY_ENRICH_AUTO_BATCH_PLAYS = /* @__PURE__ */ new Set([
16955
16956
  var HEAVY_ENRICH_AUTO_BATCH_TOOLS = /* @__PURE__ */ new Set([
16956
16957
  "ai_inference",
16957
16958
  "aiinference",
16958
- "deeplineagent"
16959
+ "deeplineagent",
16960
+ "exa_answer"
16959
16961
  ]);
16960
16962
  var ENRICH_TEST_AI_INFERENCE_PAYLOAD_MARKER = "__deeplineTestAiInference";
16961
16963
  var PLAN_SHAPING_OPTION_NAMES = [
@@ -17866,14 +17868,39 @@ function enrichOutputJson(exportResult) {
17866
17868
  ...exportResult.partial ? { rows: exportResult.rows, partial: true } : {}
17867
17869
  };
17868
17870
  }
17871
+ function enrichReportJson(report) {
17872
+ if (!report) {
17873
+ return {};
17874
+ }
17875
+ return {
17876
+ ...report.jobs.length > 0 ? {
17877
+ failure_report: {
17878
+ path: report.path,
17879
+ jobs: report.jobs.length
17880
+ }
17881
+ } : {},
17882
+ ...report.issues.length > 0 ? {
17883
+ enrich_issues: {
17884
+ path: report.path,
17885
+ issues: report.issues.length,
17886
+ items: report.issues
17887
+ }
17888
+ } : {}
17889
+ };
17890
+ }
17869
17891
  async function buildEnrichFailureReport(input2) {
17892
+ const fallbackRowsInfo = input2.exportResult ? null : extractCanonicalRowsInfo(input2.status);
17870
17893
  return maybeEmitEnrichFailureReport({
17871
17894
  config: input2.config,
17872
17895
  rows: input2.rows,
17873
17896
  rowRange: input2.rowRange,
17874
17897
  client: input2.client,
17875
17898
  outputPath: input2.reportOutputPath ?? input2.exportResult?.path ?? input2.outputPath ?? null,
17876
- status: input2.status
17899
+ followUpOutputPath: input2.followUpOutputPath,
17900
+ status: input2.status,
17901
+ rowSetComplete: input2.exportResult ? !input2.exportResult.partial : Boolean(
17902
+ fallbackRowsInfo?.complete && input2.rows.length >= fallbackRowsInfo.totalRows
17903
+ )
17877
17904
  });
17878
17905
  }
17879
17906
  function recordField(value, key) {
@@ -18150,7 +18177,7 @@ function emitPlainBatchRunFailure(input2) {
18150
18177
  `Batch ${input2.chunkIndex + 1}/${input2.chunkCount} failed for rows ${input2.rows.rowStart}:${input2.rows.rowEnd}.
18151
18178
  `
18152
18179
  );
18153
- const runId = extractRunId(input2.status);
18180
+ const runId = extractRunId(input2.status) ?? firstCollectedStringField(input2.status, "runId");
18154
18181
  const error = firstCollectedStringField(input2.status, "error") ?? firstCollectedStringField(input2.status, "message");
18155
18182
  if (runId) {
18156
18183
  process.stderr.write(`Run id: ${runId}
@@ -18366,6 +18393,38 @@ function collectHardFailureAliasSpecs(config) {
18366
18393
  }
18367
18394
  return aliases;
18368
18395
  }
18396
+ function collectWaterfallAliasSpecs(config) {
18397
+ const aliases = [];
18398
+ const seen = /* @__PURE__ */ new Set();
18399
+ for (const command of config.commands) {
18400
+ if (!("with_waterfall" in command)) {
18401
+ continue;
18402
+ }
18403
+ const alias = command.with_waterfall.trim();
18404
+ if (!alias || seen.has(alias)) {
18405
+ continue;
18406
+ }
18407
+ const activeChildren = command.commands.filter(
18408
+ (child) => !("with_waterfall" in child) && !child.disabled
18409
+ );
18410
+ if (activeChildren.length === 0) {
18411
+ continue;
18412
+ }
18413
+ seen.add(alias);
18414
+ const childOperations = activeChildren.map((child) => child.operation?.trim() || child.tool.trim()).filter(Boolean);
18415
+ aliases.push({
18416
+ alias,
18417
+ childAliases: activeChildren.map((child) => child.alias).filter(Boolean),
18418
+ childToolsByAlias: new Map(
18419
+ activeChildren.map(
18420
+ (child) => [child.alias, child.tool.trim()]
18421
+ )
18422
+ ),
18423
+ operation: childOperations.length > 0 ? childOperations.join(", ") : void 0
18424
+ });
18425
+ }
18426
+ return aliases;
18427
+ }
18369
18428
  function hardFailureOperationByAlias(config) {
18370
18429
  const operations = /* @__PURE__ */ new Map();
18371
18430
  if (!config) {
@@ -18378,6 +18437,60 @@ function hardFailureOperationByAlias(config) {
18378
18437
  }
18379
18438
  return operations;
18380
18439
  }
18440
+ function isWaterfallResultMeaningful(value) {
18441
+ if (Array.isArray(value)) {
18442
+ return value.some(isWaterfallResultMeaningful);
18443
+ }
18444
+ if (value && typeof value === "object") {
18445
+ const record = value;
18446
+ const status = typeof record.status === "string" ? record.status.trim().toLowerCase() : "";
18447
+ if (status === "error" || status === "failed") {
18448
+ return false;
18449
+ }
18450
+ if (typeof record.error === "string" && record.error.trim()) {
18451
+ return false;
18452
+ }
18453
+ if (Object.prototype.hasOwnProperty.call(record, "matched_result")) {
18454
+ return isWaterfallResultMeaningful(record.matched_result);
18455
+ }
18456
+ for (const key of [
18457
+ "value",
18458
+ "email",
18459
+ "phone",
18460
+ "phone_number",
18461
+ "mobile_phone",
18462
+ "mobile_phone_number",
18463
+ "url",
18464
+ "domain",
18465
+ "name",
18466
+ "result",
18467
+ "data"
18468
+ ]) {
18469
+ if (isWaterfallResultMeaningful(record[key])) {
18470
+ return true;
18471
+ }
18472
+ }
18473
+ return Object.entries(record).some(
18474
+ ([key, entry]) => !ENRICH_FLATTENED_CONTROL_FIELDS.has(key) && isWaterfallResultMeaningful(entry)
18475
+ );
18476
+ }
18477
+ return isNonEmptyCsvCell(value);
18478
+ }
18479
+ function aliasHasMeaningfulResult(row, alias) {
18480
+ return aliasFailureCellCandidates(row, alias).some(
18481
+ isWaterfallResultMeaningful
18482
+ );
18483
+ }
18484
+ function stableRowSnapshot(value) {
18485
+ if (Array.isArray(value)) {
18486
+ return `[${value.map(stableRowSnapshot).join(",")}]`;
18487
+ }
18488
+ if (value && typeof value === "object") {
18489
+ const record = value;
18490
+ return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${stableRowSnapshot(record[key])}`).join(",")}}`;
18491
+ }
18492
+ return JSON.stringify(value);
18493
+ }
18381
18494
  function cellFailureError(value) {
18382
18495
  const parsed = parseMaybeJsonObject(value);
18383
18496
  if (!isRecord7(parsed)) {
@@ -18523,6 +18636,112 @@ function collectEnrichFailureJobs(input2) {
18523
18636
  });
18524
18637
  return jobs;
18525
18638
  }
18639
+ function collectEmptyWaterfallIssues(input2) {
18640
+ const aliases = collectWaterfallAliasSpecs(input2.config);
18641
+ if (aliases.length === 0 || input2.rows.length === 0 || !input2.rowSetComplete) {
18642
+ return [];
18643
+ }
18644
+ const runId = extractRunId(input2.status) ?? firstCollectedStringField(input2.status, "runId");
18645
+ const followUpCommands = collectEnrichFollowUpCommands({
18646
+ status: input2.status,
18647
+ runId,
18648
+ outputPath: input2.followUpOutputPath ?? input2.outputPath
18649
+ });
18650
+ const issues = [];
18651
+ aliases.forEach((spec) => {
18652
+ if (waterfallExecutionSignal(input2.status, spec) === "all_condition_skipped") {
18653
+ return;
18654
+ }
18655
+ const logicalRows = /* @__PURE__ */ new Map();
18656
+ input2.rows.forEach((row, rowIndex) => {
18657
+ const sourceRowId = sourceRowIndexFromEnrichRow(
18658
+ row,
18659
+ 0,
18660
+ Number.MAX_SAFE_INTEGER
18661
+ );
18662
+ const rowKey = sourceRowId !== null ? `source:${sourceRowId}` : `snapshot:${stableRowSnapshot(row)}`;
18663
+ const existing = logicalRows.get(rowKey);
18664
+ const hasMeaningfulResult = aliasHasMeaningfulResult(row, spec.alias);
18665
+ logicalRows.set(rowKey, {
18666
+ hasMeaningfulResult: (existing?.hasMeaningfulResult ?? false) || hasMeaningfulResult
18667
+ });
18668
+ });
18669
+ const emptyRows = Array.from(logicalRows.values()).filter(
18670
+ (row) => !row.hasMeaningfulResult
18671
+ );
18672
+ if (emptyRows.length !== logicalRows.size) {
18673
+ return;
18674
+ }
18675
+ const selectedRows = logicalRows.size;
18676
+ const message = `Waterfall ${spec.alias} produced 0 values for ${selectedRows} selected row(s).`;
18677
+ issues.push({
18678
+ issue_id: `${spec.alias}-empty-waterfall`,
18679
+ kind: "empty_waterfall",
18680
+ severity: "needs_attention",
18681
+ column: spec.alias,
18682
+ selected_rows: selectedRows,
18683
+ rows_with_value: 0,
18684
+ rows_without_value: selectedRows,
18685
+ message,
18686
+ next_steps: enrichIssueNextSteps({ runId }),
18687
+ follow_up_commands: followUpCommands,
18688
+ ...spec.operation ? { operation: spec.operation } : {},
18689
+ ...runId ? { run_id: runId } : {}
18690
+ });
18691
+ });
18692
+ return issues;
18693
+ }
18694
+ function waterfallExecutionSignal(status, spec) {
18695
+ const childAliases = spec.childAliases ?? [];
18696
+ if (childAliases.length === 0) {
18697
+ return "unknown";
18698
+ }
18699
+ const summaries = collectColumnStats(status);
18700
+ let sawChildStats = false;
18701
+ let everyChildStatOnlyConditionSkipped = true;
18702
+ const childAliasesWithStats = /* @__PURE__ */ new Set();
18703
+ for (const childAlias of childAliases) {
18704
+ for (const stat2 of summaries.map((summary) => summary[childAlias]).filter(isRecord7)) {
18705
+ sawChildStats = true;
18706
+ childAliasesWithStats.add(childAlias);
18707
+ const execution = isRecord7(stat2.execution) ? stat2.execution : null;
18708
+ const executedSummary = parseExecutionSummary(
18709
+ execution?.["completed:executed"]
18710
+ );
18711
+ const skippedConditionSummary = parseExecutionSummary(
18712
+ execution?.["skipped:condition"]
18713
+ );
18714
+ const executedCount = executedSummary?.count ?? 0;
18715
+ const skippedConditionCount = skippedConditionSummary?.count ?? 0;
18716
+ const totalCount = executedSummary?.total ?? skippedConditionSummary?.total;
18717
+ const onlyConditionSkips = totalCount !== void 0 && skippedConditionCount >= totalCount && executedCount <= skippedConditionCount;
18718
+ if (executedCount > 0 && !onlyConditionSkips || numericFailedRowSignal(execution?.failed) > 0 || numericFailedRowSignal(stat2.failed) > 0 || numericFailedRowSignal(stat2.failedRows) > 0) {
18719
+ return "executed";
18720
+ }
18721
+ if (!onlyConditionSkips) {
18722
+ everyChildStatOnlyConditionSkipped = false;
18723
+ }
18724
+ }
18725
+ }
18726
+ if (sawChildStats && childAliasesWithStats.size === childAliases.length && everyChildStatOnlyConditionSkipped) {
18727
+ return "all_condition_skipped";
18728
+ }
18729
+ return "unknown";
18730
+ }
18731
+ function mergeEnrichFailureJobs(...jobGroups) {
18732
+ const merged = [];
18733
+ const seen = /* @__PURE__ */ new Set();
18734
+ for (const jobs of jobGroups) {
18735
+ for (const job of jobs) {
18736
+ if (seen.has(job.job_id)) {
18737
+ continue;
18738
+ }
18739
+ seen.add(job.job_id);
18740
+ merged.push(job);
18741
+ }
18742
+ }
18743
+ return merged;
18744
+ }
18526
18745
  function collectStatusFailureJobs(input2) {
18527
18746
  const aliases = collectHardFailureAliasSpecs(input2.config);
18528
18747
  if (aliases.length === 0) {
@@ -18616,14 +18835,23 @@ function collectCompleteStatusFailureMessages(input2) {
18616
18835
  return complete;
18617
18836
  }
18618
18837
  function parseExecutionCount(value) {
18838
+ return parseExecutionSummary(value)?.count ?? null;
18839
+ }
18840
+ function parseExecutionSummary(value) {
18619
18841
  if (typeof value === "number" && Number.isFinite(value)) {
18620
- return Math.max(0, Math.trunc(value));
18842
+ return { count: Math.max(0, Math.trunc(value)) };
18621
18843
  }
18622
18844
  if (typeof value !== "string") {
18623
18845
  return null;
18624
18846
  }
18625
- const match = value.trim().match(/^(\d+)\s*\//);
18626
- return match?.[1] ? Number.parseInt(match[1], 10) : null;
18847
+ const match = value.trim().match(/^(\d+)(?:\s*\/\s*(\d+))?/);
18848
+ if (!match?.[1]) {
18849
+ return null;
18850
+ }
18851
+ return {
18852
+ count: Number.parseInt(match[1], 10),
18853
+ ...match[2] ? { total: Number.parseInt(match[2], 10) } : {}
18854
+ };
18627
18855
  }
18628
18856
  function executionSummaryText(count, total) {
18629
18857
  const percent = total > 0 ? Math.round(count / total * 100) : 0;
@@ -18749,19 +18977,145 @@ function summarizeFailedJobError(value) {
18749
18977
  const text = String(value ?? "").trim();
18750
18978
  return text.length > 500 ? `${text.slice(0, 497)}...` : text;
18751
18979
  }
18980
+ function shellSingleQuote2(value) {
18981
+ return `'${value.replace(/'/g, `'\\''`)}'`;
18982
+ }
18983
+ function addEnrichFollowUpCommand(commands, seen, command) {
18984
+ const normalized = command.command.trim();
18985
+ if (!normalized || seen.has(normalized)) {
18986
+ return;
18987
+ }
18988
+ seen.add(normalized);
18989
+ commands.push({ ...command, command: normalized });
18990
+ }
18991
+ function datasetSelectorForEnrichCommand(record, fallbackPath) {
18992
+ const candidates = [record.path, record.tableNamespace, fallbackPath];
18993
+ for (const candidate of candidates) {
18994
+ if (typeof candidate === "string" && candidate.trim()) {
18995
+ return candidate.trim();
18996
+ }
18997
+ }
18998
+ return null;
18999
+ }
19000
+ function collectEnrichFollowUpCommands(input2) {
19001
+ const commands = [];
19002
+ const seen = /* @__PURE__ */ new Set();
19003
+ if (input2.runId) {
19004
+ addEnrichFollowUpCommand(commands, seen, {
19005
+ label: "inspect run",
19006
+ command: `deepline runs get ${input2.runId} --full --json`
19007
+ });
19008
+ }
19009
+ collectDatasetFollowUpCommands(input2.status, {
19010
+ runId: input2.runId,
19011
+ outputPath: input2.outputPath,
19012
+ commands,
19013
+ seen,
19014
+ path: "result",
19015
+ depth: 0
19016
+ });
19017
+ if (input2.runId && input2.outputPath && !commands.some((command) => command.label === "export enrich rows")) {
19018
+ addEnrichFollowUpCommand(commands, seen, {
19019
+ label: "export enrich rows",
19020
+ path: GENERATED_ENRICH_ROWS_TABLE_NAMESPACE,
19021
+ command: `deepline runs export ${input2.runId} --dataset ${GENERATED_ENRICH_ROWS_TABLE_NAMESPACE} --out ${shellSingleQuote2(
19022
+ resolve9(input2.outputPath)
19023
+ )}`
19024
+ });
19025
+ }
19026
+ return commands.slice(0, 8);
19027
+ }
19028
+ function sidecarEnrichRowsExportPath(outputPath) {
19029
+ const resolved = resolve9(outputPath);
19030
+ const ext = extname(resolved) || ".csv";
19031
+ const stem = basename2(resolved, ext);
19032
+ return join7(dirname7(resolved), `${stem}.deepline-enrich-rows${ext}`);
19033
+ }
19034
+ function collectDatasetFollowUpCommands(value, state) {
19035
+ if (state.depth > 12 || !value || typeof value !== "object" || state.commands.length >= 8) {
19036
+ return;
19037
+ }
19038
+ if (Array.isArray(value)) {
19039
+ value.slice(0, 50).forEach(
19040
+ (entry, index) => collectDatasetFollowUpCommands(entry, {
19041
+ ...state,
19042
+ path: `${state.path}.${index}`,
19043
+ depth: state.depth + 1
19044
+ })
19045
+ );
19046
+ return;
19047
+ }
19048
+ const record = value;
19049
+ const isDataset = record.kind === "dataset";
19050
+ if (isDataset) {
19051
+ const selector = datasetSelectorForEnrichCommand(record, state.path);
19052
+ const labelPath = typeof record.path === "string" && record.path.trim() ? record.path.trim() : selector ?? state.path;
19053
+ if (typeof record.queryDatasetCommand === "string") {
19054
+ addEnrichFollowUpCommand(state.commands, state.seen, {
19055
+ label: `query ${labelPath}`,
19056
+ path: labelPath,
19057
+ command: record.queryDatasetCommand
19058
+ });
19059
+ }
19060
+ const exportCommand = typeof record.slowExportAsCsvCommand === "string" ? record.slowExportAsCsvCommand : typeof record.fullExportCommand === "string" ? record.fullExportCommand : null;
19061
+ if (exportCommand) {
19062
+ addEnrichFollowUpCommand(state.commands, state.seen, {
19063
+ label: `export ${labelPath}`,
19064
+ path: labelPath,
19065
+ command: exportCommand
19066
+ });
19067
+ } else if (state.runId && state.outputPath && selector) {
19068
+ addEnrichFollowUpCommand(state.commands, state.seen, {
19069
+ label: selector === GENERATED_ENRICH_ROWS_TABLE_NAMESPACE ? "export enrich rows" : `export ${labelPath}`,
19070
+ path: selector,
19071
+ command: `deepline runs export ${state.runId} --dataset ${shellSingleQuote2(
19072
+ selector
19073
+ )} --out ${shellSingleQuote2(resolve9(state.outputPath))}`
19074
+ });
19075
+ }
19076
+ }
19077
+ for (const [key, child] of Object.entries(record)) {
19078
+ if (key === "preview" || key === "access") {
19079
+ continue;
19080
+ }
19081
+ collectDatasetFollowUpCommands(child, {
19082
+ ...state,
19083
+ path: `${state.path}.${key}`,
19084
+ depth: state.depth + 1
19085
+ });
19086
+ if (state.commands.length >= 8) {
19087
+ return;
19088
+ }
19089
+ }
19090
+ }
19091
+ function enrichIssueNextSteps(input2) {
19092
+ const steps = [
19093
+ "Inspect the run output and returned dataset handles.",
19094
+ "If runs get returns queryDatasetCommand or slowExportAsCsvCommand, run that command to fetch durable rows from the customer DB.",
19095
+ "For provider-backed waterfalls, execute the child provider tool on a sample row to confirm the provider returns the expected field."
19096
+ ];
19097
+ if (!input2.runId) {
19098
+ return steps;
19099
+ }
19100
+ return [
19101
+ `Inspect run output: deepline runs get ${input2.runId} --full --json`,
19102
+ ...steps.slice(1)
19103
+ ];
19104
+ }
18752
19105
  function formatFailureReportCommand(reportPath) {
18753
19106
  const quoted = reportPath.replace(/'/g, `'\\''`);
18754
19107
  return `Inspect failed jobs: jq -r '.jobs[] | [.job_id,.row_id,.col_index,.column,.status,.last_error] | @tsv' '${quoted}'`;
18755
19108
  }
18756
19109
  async function persistEnrichFailureReport(input2) {
18757
- if (input2.jobs.length === 0) {
19110
+ if (input2.jobs.length === 0 && input2.issues.length === 0) {
18758
19111
  return null;
18759
19112
  }
18760
19113
  const stateDir = join7(homedir7(), ".local", "deepline", "runtime", "state");
19114
+ const reportPrefix = input2.jobs.length > 0 ? "run-block-failures" : "enrich-issues";
18761
19115
  await mkdir3(stateDir, { recursive: true });
18762
19116
  const reportPath = join7(
18763
19117
  stateDir,
18764
- `run-block-failures-${Math.floor(Date.now() / 1e3)}-${process.pid}.json`
19118
+ `${reportPrefix}-${Math.floor(Date.now() / 1e3)}-${process.pid}.json`
18765
19119
  );
18766
19120
  const report = {
18767
19121
  generated_at: (/* @__PURE__ */ new Date()).toISOString(),
@@ -18776,6 +19130,7 @@ async function persistEnrichFailureReport(input2) {
18776
19130
  },
18777
19131
  jobs: input2.jobs,
18778
19132
  failed_jobs: input2.jobs,
19133
+ enrich_issues: input2.issues,
18779
19134
  canceled_jobs: [],
18780
19135
  pending_jobs: [],
18781
19136
  missing_job_ids: []
@@ -18788,48 +19143,100 @@ async function persistEnrichFailureReport(input2) {
18788
19143
  return reportPath;
18789
19144
  }
18790
19145
  async function maybeEmitEnrichFailureReport(input2) {
19146
+ const fallbackRowsInfo = extractCanonicalRowsInfo(input2.status);
19147
+ const rowSetComplete = input2.rowSetComplete ?? Boolean(
19148
+ fallbackRowsInfo?.complete && input2.rows.length >= fallbackRowsInfo.totalRows
19149
+ );
18791
19150
  const rowJobs = collectEnrichFailureJobs({
18792
19151
  config: input2.config,
18793
19152
  rows: input2.rows,
18794
19153
  rowStart: input2.rowRange.rowStart
18795
19154
  });
18796
- const jobs = rowJobs.length > 0 || input2.status === void 0 ? rowJobs : collectStatusFailureJobs({
19155
+ const enrichIssues = collectEmptyWaterfallIssues({
19156
+ config: input2.config,
19157
+ rows: input2.rows,
19158
+ status: input2.waterfallStatus ?? input2.status,
19159
+ outputPath: input2.outputPath,
19160
+ followUpOutputPath: input2.followUpOutputPath,
19161
+ rowSetComplete
19162
+ });
19163
+ const statusJobs = input2.status === void 0 || rowJobs.length > 0 ? [] : collectStatusFailureJobs({
18797
19164
  config: input2.config,
18798
19165
  status: input2.status,
18799
19166
  rowRange: input2.statusRowRange ?? input2.rowRange
18800
19167
  });
18801
- if (jobs.length === 0) {
19168
+ const jobs = rowJobs.length > 0 || statusJobs.length > 0 || input2.status === void 0 ? mergeEnrichFailureJobs(rowJobs, statusJobs) : [];
19169
+ if (jobs.length === 0 && enrichIssues.length === 0) {
18802
19170
  return null;
18803
19171
  }
18804
19172
  const reportPath = await persistEnrichFailureReport({
18805
19173
  jobs,
19174
+ issues: enrichIssues,
18806
19175
  apiUrl: input2.client.baseUrl,
18807
19176
  outputPath: input2.outputPath,
18808
19177
  rows: input2.rowRange
18809
19178
  });
18810
- process.stderr.write("Execution failed.\n");
18811
- process.stderr.write("Failure preview (up to 5 failed jobs):\n");
18812
- process.stderr.write("job_id row_id col_index column status error\n");
18813
- for (const job of jobs.slice(0, 5)) {
18814
- process.stderr.write(
18815
- `${job.job_id} ${job.row_id} ${job.col_index} ${job.column} ${job.status} ${summarizeFailedJobError(job.last_error)}
19179
+ if (jobs.length > 0) {
19180
+ process.stderr.write("Execution failed.\n");
19181
+ process.stderr.write("Failure preview (up to 5 failed jobs):\n");
19182
+ process.stderr.write("job_id row_id col_index column status error\n");
19183
+ for (const job of jobs.slice(0, 5)) {
19184
+ process.stderr.write(
19185
+ `${job.job_id} ${job.row_id} ${job.col_index} ${job.column} ${job.status} ${summarizeFailedJobError(job.last_error)}
18816
19186
  `
18817
- );
19187
+ );
19188
+ }
19189
+ if (jobs.length > 5) {
19190
+ process.stderr.write(`Showing 5 of ${jobs.length} failed jobs.
19191
+ `);
19192
+ }
18818
19193
  }
18819
- if (jobs.length > 5) {
18820
- process.stderr.write(`Showing 5 of ${jobs.length} failed jobs.
19194
+ if (enrichIssues.length > 0) {
19195
+ process.stderr.write("Enrichment needs attention.\n");
19196
+ process.stderr.write("Issue preview (up to 5 enrichment issues):\n");
19197
+ process.stderr.write(
19198
+ "issue_id column severity selected_rows rows_with_value message\n"
19199
+ );
19200
+ for (const issue of enrichIssues.slice(0, 5)) {
19201
+ process.stderr.write(
19202
+ `${issue.issue_id} ${issue.column} ${issue.severity} ${issue.selected_rows} ${issue.rows_with_value} ${issue.message}
19203
+ `
19204
+ );
19205
+ for (const step of issue.next_steps.slice(0, 2)) {
19206
+ process.stderr.write(`Next: ${step}
19207
+ `);
19208
+ }
19209
+ for (const command of issue.follow_up_commands.slice(0, 4)) {
19210
+ process.stderr.write(`Command: ${command.label}: ${command.command}
18821
19211
  `);
19212
+ }
19213
+ }
19214
+ if (enrichIssues.length > 5) {
19215
+ process.stderr.write(
19216
+ `Showing 5 of ${enrichIssues.length} enrichment issues.
19217
+ `
19218
+ );
19219
+ }
18822
19220
  }
18823
19221
  if (reportPath) {
18824
- process.stderr.write(`Full failure report: ${reportPath}
19222
+ if (jobs.length > 0) {
19223
+ process.stderr.write(`Full failure report: ${reportPath}
18825
19224
  `);
18826
- process.stderr.write(`${formatFailureReportCommand(reportPath)}
19225
+ process.stderr.write(`${formatFailureReportCommand(reportPath)}
18827
19226
  `);
18828
- process.stderr.write("Enrichment failed. See failure report above.\n");
18829
- return { path: reportPath, jobs };
19227
+ } else {
19228
+ process.stderr.write(`Full enrich report: ${reportPath}
19229
+ `);
19230
+ }
19231
+ process.stderr.write(
19232
+ jobs.length > 0 ? "Enrichment failed. See report above.\n" : "Enrichment produced no values for at least one requested waterfall. See issue preview above.\n"
19233
+ );
19234
+ return { path: reportPath, jobs, issues: enrichIssues };
18830
19235
  }
18831
- process.stderr.write("Enrichment failed.\n");
18832
- return { path: "", jobs };
19236
+ process.stderr.write(
19237
+ jobs.length > 0 ? "Enrichment failed.\n" : "Enrichment needs attention.\n"
19238
+ );
19239
+ return { path: "", jobs, issues: enrichIssues };
18833
19240
  }
18834
19241
  function mergePreferredColumns(preferredColumns, rows) {
18835
19242
  const columns = [];
@@ -19495,6 +19902,7 @@ function registerEnrichCommand(program) {
19495
19902
  const inPlaceFinalOutputPath = options.inPlace ? resolve9(inputCsv) : null;
19496
19903
  const inPlaceCommitOutputPath = options.inPlace ? (await lstat(inputCsv)).isSymbolicLink() ? await realpath(inputCsv) : inPlaceFinalOutputPath : null;
19497
19904
  const failureReportOutputPath = options.inPlace ? inPlaceFinalOutputPath : null;
19905
+ const enrichIssueFollowUpOutputPath = options.inPlace && inPlaceFinalOutputPath ? sidecarEnrichRowsExportPath(inPlaceFinalOutputPath) : null;
19498
19906
  let inPlaceCommitted = false;
19499
19907
  const commitInPlaceOutput = async (exportResult) => {
19500
19908
  if (!inPlaceTempOutputPath || !inPlaceCommitOutputPath || !inPlaceFinalOutputPath) {
@@ -19583,6 +19991,7 @@ function registerEnrichCommand(program) {
19583
19991
  }
19584
19992
  let workingMergeSourceCsvPath = sourceCsvPath;
19585
19993
  let lastStatus = null;
19994
+ const batchStatuses = [];
19586
19995
  let finalExportResult = null;
19587
19996
  let totalEnrichedRows = 0;
19588
19997
  const batchFailureRows = [];
@@ -19620,6 +20029,7 @@ function registerEnrichCommand(program) {
19620
20029
  suppressOpen: true
19621
20030
  });
19622
20031
  lastStatus = chunk.status;
20032
+ batchStatuses.push(chunk.status);
19623
20033
  if (chunk.captured.result !== 0) {
19624
20034
  if (chunk.exportResult) {
19625
20035
  finalExportResult = chunk.exportResult;
@@ -19639,7 +20049,10 @@ function registerEnrichCommand(program) {
19639
20049
  statusRowRange: chunkRows,
19640
20050
  client: client2,
19641
20051
  outputPath: failureReportOutputPath ?? finalExportResult?.path ?? outputPath,
19642
- status: chunk.status
20052
+ followUpOutputPath: enrichIssueFollowUpOutputPath,
20053
+ status: chunk.status,
20054
+ waterfallStatus: batchStatuses,
20055
+ ...finalExportResult ? { rowSetComplete: !finalExportResult.partial } : {}
19643
20056
  });
19644
20057
  const committedExportResult2 = finalExportResult ? await commitInPlaceOutput(finalExportResult) : null;
19645
20058
  if (options.json) {
@@ -19661,12 +20074,7 @@ function registerEnrichCommand(program) {
19661
20074
  partial: true
19662
20075
  } : {}
19663
20076
  } : null,
19664
- ...failureReport3 ? {
19665
- failure_report: {
19666
- path: failureReport3.path,
19667
- jobs: failureReport3.jobs.length
19668
- }
19669
- } : {}
20077
+ ...enrichReportJson(failureReport3)
19670
20078
  });
19671
20079
  } else {
19672
20080
  if (committedExportResult2) {
@@ -19705,7 +20113,10 @@ function registerEnrichCommand(program) {
19705
20113
  },
19706
20114
  client: client2,
19707
20115
  outputPath: failureReportOutputPath ?? finalExportResult?.path ?? outputPath,
19708
- status: lastStatus
20116
+ followUpOutputPath: enrichIssueFollowUpOutputPath,
20117
+ status: lastStatus,
20118
+ waterfallStatus: batchStatuses,
20119
+ ...finalExportResult ? { rowSetComplete: !finalExportResult.partial } : {}
19709
20120
  });
19710
20121
  finalExportResult = await commitInPlaceOutput(finalExportResult);
19711
20122
  if (options.json) {
@@ -19737,12 +20148,7 @@ function registerEnrichCommand(program) {
19737
20148
  enrichedRows: totalEnrichedRows,
19738
20149
  path: finalExportResult.path
19739
20150
  } : null,
19740
- ...failureReport2 ? {
19741
- failure_report: {
19742
- path: failureReport2.path,
19743
- jobs: failureReport2.jobs.length
19744
- }
19745
- } : {}
20151
+ ...enrichReportJson(failureReport2)
19746
20152
  });
19747
20153
  }
19748
20154
  if (failureReport2) {
@@ -19773,6 +20179,7 @@ function registerEnrichCommand(program) {
19773
20179
  exportResult: committedExportResult2,
19774
20180
  outputPath,
19775
20181
  reportOutputPath: failureReportOutputPath,
20182
+ followUpOutputPath: enrichIssueFollowUpOutputPath,
19776
20183
  status
19777
20184
  });
19778
20185
  if (options.json) {
@@ -19780,12 +20187,7 @@ function registerEnrichCommand(program) {
19780
20187
  ok: false,
19781
20188
  run: status,
19782
20189
  output: enrichOutputJson(committedExportResult2),
19783
- ...failureReport2 ? {
19784
- failure_report: {
19785
- path: failureReport2.path,
19786
- jobs: failureReport2.jobs.length
19787
- }
19788
- } : {}
20190
+ ...enrichReportJson(failureReport2)
19789
20191
  });
19790
20192
  }
19791
20193
  process.exitCode = failureReport2 ? EXIT_SERVER2 : captured.result;
@@ -19800,6 +20202,7 @@ function registerEnrichCommand(program) {
19800
20202
  exportResult,
19801
20203
  outputPath,
19802
20204
  reportOutputPath: failureReportOutputPath,
20205
+ followUpOutputPath: enrichIssueFollowUpOutputPath,
19803
20206
  status
19804
20207
  });
19805
20208
  const committedExportResult2 = await commitInPlaceOutput(exportResult);
@@ -19814,12 +20217,7 @@ function registerEnrichCommand(program) {
19814
20217
  ok: !failureReport2,
19815
20218
  run,
19816
20219
  output: enrichOutputJson(committedExportResult2),
19817
- ...failureReport2 ? {
19818
- failure_report: {
19819
- path: failureReport2.path,
19820
- jobs: failureReport2.jobs.length
19821
- }
19822
- } : {}
20220
+ ...enrichReportJson(failureReport2)
19823
20221
  });
19824
20222
  if (failureReport2) {
19825
20223
  process.exitCode = EXIT_SERVER2;
@@ -19832,7 +20230,9 @@ function registerEnrichCommand(program) {
19832
20230
  rowRange: rows,
19833
20231
  client: client2,
19834
20232
  outputPath: failureReportOutputPath ?? exportResult?.path ?? null,
19835
- status
20233
+ followUpOutputPath: enrichIssueFollowUpOutputPath,
20234
+ status,
20235
+ ...exportResult ? { rowSetComplete: !exportResult.partial } : {}
19836
20236
  });
19837
20237
  const committedExportResult = await commitInPlaceOutput(exportResult);
19838
20238
  if (committedExportResult) {
@@ -19917,7 +20317,7 @@ import {
19917
20317
  writeFileSync as writeFileSync10
19918
20318
  } from "fs";
19919
20319
  import { homedir as homedir8, platform } from "os";
19920
- import { basename as basename2, dirname as dirname8, join as join8, resolve as resolve10 } from "path";
20320
+ import { basename as basename3, dirname as dirname8, join as join8, resolve as resolve10 } from "path";
19921
20321
  import { gzipSync } from "zlib";
19922
20322
  import { randomUUID as randomUUID2 } from "crypto";
19923
20323
  var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
@@ -19936,7 +20336,7 @@ function homeDir() {
19936
20336
  function detectShellContext() {
19937
20337
  const shellPath = process.env.SHELL?.trim() || process.env.ComSpec?.trim() || process.env.COMSPEC?.trim() || "";
19938
20338
  return {
19939
- shell: shellPath ? basename2(shellPath).replace(/\.exe$/i, "") : "unknown",
20339
+ shell: shellPath ? basename3(shellPath).replace(/\.exe$/i, "") : "unknown",
19940
20340
  shell_path: shellPath || null,
19941
20341
  os: platform(),
19942
20342
  cwd: process.cwd()
@@ -20042,10 +20442,10 @@ function newestSessionFile(agent) {
20042
20442
  return newest;
20043
20443
  }
20044
20444
  function sessionIdFromClaudeFilePath(filePath) {
20045
- return basename2(filePath, ".jsonl");
20445
+ return basename3(filePath, ".jsonl");
20046
20446
  }
20047
20447
  function sessionIdFromCodexFilePath(filePath) {
20048
- const match = basename2(filePath, ".jsonl").match(UUID_IN_TEXT_RE);
20448
+ const match = basename3(filePath, ".jsonl").match(UUID_IN_TEXT_RE);
20049
20449
  return match?.[0] ?? null;
20050
20450
  }
20051
20451
  function readCodexSessionId(filePath) {
@@ -20333,19 +20733,19 @@ async function handleSessionsSend(options) {
20333
20733
  }
20334
20734
  const response2 = await uploadPayload("/api/v2/cli/send-session", {
20335
20735
  file: readFileSync8(filePath).toString("base64"),
20336
- filename: basename2(filePath)
20736
+ filename: basename3(filePath)
20337
20737
  });
20338
20738
  printCommandEnvelope(
20339
20739
  {
20340
20740
  ...response2,
20341
20741
  ok: true,
20342
- filename: basename2(filePath),
20742
+ filename: basename3(filePath),
20343
20743
  render: {
20344
20744
  sections: [
20345
20745
  {
20346
20746
  title: "sessions send",
20347
20747
  lines: [
20348
- `File '${basename2(filePath)}' uploaded to #internal-reports.`
20748
+ `File '${basename3(filePath)}' uploaded to #internal-reports.`
20349
20749
  ]
20350
20750
  }
20351
20751
  ]