@algolia/wizard 0.93.0 → 0.95.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/main.js +62 -18
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -2384,7 +2384,7 @@ async function ensureApplication(resumeAppId) {
2384
2384
  // package.json
2385
2385
  var package_default = {
2386
2386
  name: "@algolia/wizard",
2387
- version: "0.93.0",
2387
+ version: "0.95.0",
2388
2388
  description: "Magically implement Algolia functionality in your codebase",
2389
2389
  type: "module",
2390
2390
  engines: {
@@ -5315,9 +5315,11 @@ function incompleteCauseOf(stepCount, maxSteps, finishReason) {
5315
5315
  }
5316
5316
  var AgentReportedFailureError = class extends Error {
5317
5317
  reason;
5318
- constructor(reason) {
5318
+ output;
5319
+ constructor(reason, output) {
5319
5320
  super(`Agent reported failure: ${reason ?? "no reason given"}`);
5320
5321
  this.reason = reason;
5322
+ this.output = output;
5321
5323
  }
5322
5324
  };
5323
5325
  function withCachedHistory(messages) {
@@ -5573,7 +5575,7 @@ async function runAgentAttempt(req, attempt, retryReason) {
5573
5575
  },
5574
5576
  "agent reported failure"
5575
5577
  );
5576
- throw new AgentReportedFailureError(result.reason);
5578
+ throw new AgentReportedFailureError(result.reason, result.output);
5577
5579
  }
5578
5580
  return result.output;
5579
5581
  }
@@ -6120,8 +6122,22 @@ Output:
6120
6122
  ${JSON.stringify(s.output, null, 2)}`
6121
6123
  ).join("\n\n");
6122
6124
  }
6125
+ async function runReviewAgent(request) {
6126
+ try {
6127
+ return await runAgent(request);
6128
+ } catch (err) {
6129
+ if (!(err instanceof AgentReportedFailureError)) throw err;
6130
+ const parsed = reviewSchema.safeParse(err.output);
6131
+ if (!parsed.success) throw err;
6132
+ logger.warn(
6133
+ { reason: err.reason },
6134
+ "review agent reported fail, using its summary"
6135
+ );
6136
+ return parsed.data;
6137
+ }
6138
+ }
6123
6139
  var reviewStep = async (ctx, options) => {
6124
- const result = await runAgent({
6140
+ const result = await runReviewAgent({
6125
6141
  operation: "workflow-review",
6126
6142
  instructions: [
6127
6143
  "Summarize what was accomplished in the workflow, leaving out verbose details.",
@@ -6133,6 +6149,7 @@ var reviewStep = async (ctx, options) => {
6133
6149
  options.nextStepsGuidance,
6134
6150
  `Completed steps:
6135
6151
  ${formatCompletedSteps(ctx.completedSteps)}`,
6152
+ "The reportStatus status is for your summary task, not for the workflow. When a prior step failed, report status=success and describe the failure in summaryPoints.",
6136
6153
  "When done, call reportStatus"
6137
6154
  ],
6138
6155
  tools: [],
@@ -6407,7 +6424,8 @@ function sourceSpecificInstructions(input) {
6407
6424
  "Read each record from its real local source at the confirmed entity paths.",
6408
6425
  "Never fabricate, hardcode, or use placeholder records.",
6409
6426
  "Add env vars for any local source access (DB URL, API base, file paths) that is not a fixed repo path.",
6410
- "To read a database, use the database client or ORM that the project already declares in its manifest. Never install a database driver or ORM. If the project declares none, report that limitation instead of adding one."
6427
+ "To read a database, use the database client or ORM that the project already declares in its manifest. Never install a database driver or ORM. If the project declares none, report that limitation instead of adding one.",
6428
+ "Prefer a server-side credential (a service-role, secret, or admin key, or a database password) over a client-side or public key (for example an anon or publishable key, or any key the project exposes to its frontend) when the source has both: public keys are subject to the source's access rules and can return zero records without an error. Use a public key only when the source offers nothing else. Check the connection to the source before you check for its credential, and make that connection check work without the credential, so an unreachable service is never reported as a missing credential. If the credential the script needs is not in the environment, the script must print an error that names the variable and where the developer finds it, then exit with an error. Never install a client library or driver to work around a missing credential."
6411
6429
  ],
6412
6430
  fileUpload: [
6413
6431
  `Records come from the developer's file, already copied into the project at "${input.uploadFilePath}". Read and parse that exact file.`,
@@ -6463,7 +6481,7 @@ function ingestionInstructions(input) {
6463
6481
  ] : [],
6464
6482
  `When the script is finished, decide the exact command and cwd you will use to run it. Make sure that the script path in the command resolves from that cwd to the filePath. Then call reviewScript with that filePath, command, cwd, the exact selected entityName, and usesGeneratedRecords set to ${input.ingestionSource === "generated"}. Wait for the developer to review it because running it writes records to a live index. Do not run it before that call returns. If you change the command or cwd after a failed run, call reviewScript again before runShell.`,
6465
6483
  'Then, without changing directories, run the script yourself via runShell using the exact same command and cwd. Report the exact command you ran as "ingestCommand", with no cwd note or other text, so the developer can re-run it. Its explanation must say that running it writes records to Algolia.',
6466
- "When the script reads from a database or another service, find how the project connects to it locally (env files, README, socket or TCP settings). If that connection needs a local service, such as a database proxy (for example cloud-sql-proxy) or a tunnel, make the script check the connection before it reads. If the check fails, the script must print an error that names the service, then print exactly one last line to stdout in the form `ALGOLIA_WIZARD_START_HINT=<command>`, where <command> is the command from the project docs that starts the service, and exit with an error. Omit that line when the project docs name no start command.",
6484
+ "When the script reads from a database or another service, find how the project connects to it locally (env files, README, the service's own config file for its host and port, socket or TCP settings). Use the host and port that config sets, not a default. If that connection needs a local service, such as a database proxy (for example cloud-sql-proxy), a tunnel, or a service the project starts with its own CLI or scripts (not necessarily Docker Compose), make the script check the connection before it reads. If the check fails, the script must print an error that names the service, then print exactly one last line to stdout in the form `ALGOLIA_WIZARD_START_HINT=<command>`, where <command> is the command from the project docs or scripts (README, package.json scripts, Makefile) that starts the service, and exit with an error. Omit that line when neither names a start command.",
6467
6485
  "Make sure that the start command and the script connect to the same address (the same socket path, or the same host and port). If the project env points at an address that only a deployed environment has, such as a /cloudsql socket, make the script connect to the address of the start command. Set that address in the script before it loads the project database module. Do not edit project env or config files for this.",
6468
6486
  "When the script uses the project database client or ORM, load that client's env file (the project .env the client already reads) so credentials such as DB user, password, and database name are present. Load the wizard Algolia .env first, then the project .env, so Algolia keys win under dotenv versions that ignore `override`. Do not rely on `override: true`.",
6469
6487
  "If external data access fails, do not run authentication commands, change accounts or machine configuration, or start proxies, containers, or services.",
@@ -6701,6 +6719,12 @@ function parseIngestRecordCount(output) {
6701
6719
  const count = Number(match[1]);
6702
6720
  return Number.isFinite(count) ? count : void 0;
6703
6721
  }
6722
+ function indexedNothing(execution) {
6723
+ return parseIngestRecordCount(execution.output ?? "") === 0;
6724
+ }
6725
+ function ranButIndexedNothing(execution) {
6726
+ return execution.approved && execution.exitCode === 0 && !execution.timedOut && indexedNothing(execution);
6727
+ }
6704
6728
  var START_HINT_LINE = /^ALGOLIA_WIZARD_START_HINT=(.*)$/gm;
6705
6729
  function parseStartHint(output) {
6706
6730
  const matches = [...(output ?? "").matchAll(START_HINT_LINE)];
@@ -6722,7 +6746,7 @@ function ingestOutcome(executions, repoRoot, ingestCommand) {
6722
6746
  (e) => parseIngestRecordCount(e.output ?? "") != null
6723
6747
  );
6724
6748
  const succeeded = newestFirst.filter(
6725
- (e) => e.approved && e.exitCode === 0 && !e.timedOut
6749
+ (e) => e.approved && e.exitCode === 0 && !e.timedOut && !indexedNothing(e)
6726
6750
  );
6727
6751
  const run2 = succeeded.find(matchesReportedCommand) ?? succeeded.find((e) => withCount.includes(e));
6728
6752
  const runs = executions.filter(
@@ -6731,7 +6755,9 @@ function ingestOutcome(executions, repoRoot, ingestCommand) {
6731
6755
  return {
6732
6756
  run: run2,
6733
6757
  attempt: ingestCommand ? newestFirst.find(matchesReportedCommand) : void 0,
6734
- recordCount: parseIngestRecordCount(withCount[0]?.output ?? ""),
6758
+ recordCount: parseIngestRecordCount(
6759
+ withCount.find((e) => !indexedNothing(e))?.output ?? ""
6760
+ ),
6735
6761
  attempts: run2 && !matchesReportedCommand(run2) ? runs + 1 : runs
6736
6762
  };
6737
6763
  }
@@ -6811,9 +6837,13 @@ function noRunReason({
6811
6837
  }
6812
6838
  return "no successful run was recorded";
6813
6839
  }
6840
+ var EMPTY_INGEST_DETAIL = "The script read no records. A client-side or public key can return no rows because of access rules, so check that it reads with a server-side credential and that the data source has data.";
6814
6841
  function ingestFailure(attempt, missingRunReason = "no successful run was recorded") {
6815
6842
  if (!attempt) return { reason: missingRunReason };
6816
6843
  if (!attempt.approved) return { reason: "you declined to run it" };
6844
+ if (ranButIndexedNothing(attempt)) {
6845
+ return { reason: "it ran but indexed 0 records", detail: EMPTY_INGEST_DETAIL };
6846
+ }
6817
6847
  const detail = errorLine(attempt.output);
6818
6848
  if (attempt.timedOut) {
6819
6849
  const seconds = Math.round((attempt.durationMs ?? 0) / 1e3);
@@ -6854,8 +6884,8 @@ function briefSummary(summary) {
6854
6884
  if (!firstLine) return "no summary given";
6855
6885
  return firstLine.length > ERROR_SUMMARY_MAX_CHARS ? `${firstLine.slice(0, ERROR_SUMMARY_MAX_CHARS - 1)}\u2026` : firstLine;
6856
6886
  }
6857
- function ingestionRetryInstruction(reason) {
6858
- return `The previous ingestion attempt ended with no successful run: ${reason}. Look in "${INGEST_DIR}/" for a script from that attempt and finish or fix it instead of starting over. Then call reviewScript and run the script as described above.`;
6887
+ function ingestionRetryInstruction(reason, detail) {
6888
+ return `The previous ingestion attempt ended with no successful run: ${reason}.${detail ? ` ${detail}` : ""} Look in "${INGEST_DIR}/" for a script from that attempt and finish or fix it instead of starting over. Then call reviewScript and run the script as described above.`;
6859
6889
  }
6860
6890
  function validationRetryInstructions(validation) {
6861
6891
  return [
@@ -7192,19 +7222,22 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES) {
7192
7222
  await runIngestionAgent();
7193
7223
  let outcome = ingestOutcome(executions, repoRoot, ingestCommand);
7194
7224
  let failureAcknowledged = false;
7195
- while (!outcome.run && !outcome.attempt) {
7196
- const reason = await currentNoRunReason();
7225
+ while (!outcome.run && (!outcome.attempt || ranButIndexedNothing(outcome.attempt))) {
7226
+ const { reason, detail } = outcome.attempt ? ingestFailure(outcome.attempt) : { reason: await currentNoRunReason(), detail: void 0 };
7197
7227
  const choice = await ctx.requestUserInput({
7198
7228
  prompt: INGEST_AGENT_RETRY_PROMPT,
7199
7229
  promptType: "multipleChoice",
7200
7230
  options: [INGEST_RETRY.retry, INGEST_RETRY.skip],
7201
- messages: [`\u26A0\uFE0F Records were not indexed \u2014 ${reason}.`]
7231
+ messages: [
7232
+ `\u26A0\uFE0F Records were not indexed \u2014 ${reason}.`,
7233
+ ...detail ? [detail] : []
7234
+ ]
7202
7235
  });
7203
7236
  if (choice !== INGEST_RETRY.retry) {
7204
7237
  failureAcknowledged = true;
7205
7238
  break;
7206
7239
  }
7207
- await runIngestionAgent([ingestionRetryInstruction(reason)]);
7240
+ await runIngestionAgent([ingestionRetryInstruction(reason, detail)]);
7208
7241
  outcome = ingestOutcome(executions, repoRoot, ingestCommand);
7209
7242
  }
7210
7243
  while (!failureAcknowledged && !outcome.run && outcome.attempt?.approved) {
@@ -7578,14 +7611,25 @@ var defaultWorkflow = {
7578
7611
  "ingestion"
7579
7612
  );
7580
7613
  return reviewStep(ctx, {
7581
- // ingestCommand was already shown verbatim as a notice; an
7582
- // LLM-paraphrased restatement in nextSteps risks being wrong.
7583
- nextStepsGuidance: ingestion2?.ingestScriptRan ? "The wizard already ran the ingestion script and records are in the index. Do NOT tell the user to run it again; instead point them at the target index to confirm the records. Do not restate the ingestion command \u2014 the wizard already showed it to them." : "Tell the user to run the ingestion script; do not restate the exact command \u2014 the wizard already showed it to them above."
7614
+ nextStepsGuidance: ingestionGuidance(ingestion2)
7584
7615
  });
7585
7616
  }
7586
7617
  })
7587
7618
  ]
7588
7619
  };
7620
+ function ingestionGuidance(ingestion2) {
7621
+ if (ingestion2?.ingestScriptRan) {
7622
+ return "The wizard already ran the ingestion script and records are in the index. Do NOT tell the user to run it again; instead point them at the target index to confirm the records. Do not restate the ingestion command \u2014 the wizard already showed it to them.";
7623
+ }
7624
+ const partialCount = ingestion2?.ingestRecordCount ?? 0;
7625
+ if (partialCount > 0) {
7626
+ return `The ingestion script indexed ${partialCount} record(s) and then failed before it finished. Say that plainly. Do not say that ingestion succeeded or that no records were indexed. Tell the user to fix the error shown above and run the ingestion script again. Do not restate the exact command, because the wizard already showed it to them above.`;
7627
+ }
7628
+ if (ingestion2?.ingestCommand) {
7629
+ return "No records were indexed in this run. Say that plainly, and do not say that the wizard created or populated an index. Tell the user to run the ingestion script; do not restate the exact command \u2014 the wizard already showed it to them above.";
7630
+ }
7631
+ return "No ingestion script ran and no records were indexed in this run. Say that plainly. Do not say that the wizard created, selected, or populated an index, and do not tell the user to run an ingestion script, because the wizard has no tested command for one. Tell the user to run the wizard again to create and run the ingestion script.";
7632
+ }
7589
7633
 
7590
7634
  // src/actions/playbookSteps/selectPlaybook.ts
7591
7635
  import { z as z33 } from "zod";
@@ -9859,7 +9903,7 @@ async function resetProjectState() {
9859
9903
  // package.json with { type: 'json' }
9860
9904
  var package_default2 = {
9861
9905
  name: "@algolia/wizard",
9862
- version: "0.93.0",
9906
+ version: "0.95.0",
9863
9907
  description: "Magically implement Algolia functionality in your codebase",
9864
9908
  type: "module",
9865
9909
  engines: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@algolia/wizard",
3
- "version": "0.93.0",
3
+ "version": "0.95.0",
4
4
  "description": "Magically implement Algolia functionality in your codebase",
5
5
  "type": "module",
6
6
  "engines": {