@algolia/wizard 0.92.0 → 0.94.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 +63 -21
  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.92.0",
2387
+ version: "0.94.0",
2388
2388
  description: "Magically implement Algolia functionality in your codebase",
2389
2389
  type: "module",
2390
2390
  engines: {
@@ -5282,13 +5282,37 @@ function isProxyInactivityRejection(err) {
5282
5282
  const cause = RetryError.isInstance(err) ? err.lastError : err;
5283
5283
  return APICallError.isInstance(cause) && cause.statusCode === 401 && cause.responseBody === PROXY_REJECTION_BODY && cause.responseHeaders?.[PROXY_AUTH_REJECTED_HEADER2] === void 0;
5284
5284
  }
5285
+ var INCOMPLETE_CAUSES = {
5286
+ stepLimit: {
5287
+ kind: "missingReportAtStepLimit",
5288
+ reason: "reached its step limit"
5289
+ },
5290
+ truncated: {
5291
+ kind: "missingReportTruncated",
5292
+ reason: "was cut off by the output token limit"
5293
+ },
5294
+ stopped: {
5295
+ kind: "missingReportStopped",
5296
+ reason: "stopped before it finished"
5297
+ }
5298
+ };
5299
+ var INCOMPLETE_CAUSE_ENTRIES = Object.values(INCOMPLETE_CAUSES);
5300
+ function incompleteReasonOf(kind) {
5301
+ return INCOMPLETE_CAUSE_ENTRIES.find((entry) => entry.kind === kind)?.reason;
5302
+ }
5285
5303
  var MissingReportError = class extends Error {
5286
- atStepLimit;
5287
- constructor(atStepLimit) {
5304
+ incompleteCause;
5305
+ constructor(incompleteCause) {
5288
5306
  super(MISSING_REPORT_STATUS_ERROR_MESSAGE);
5289
- this.atStepLimit = atStepLimit;
5307
+ this.incompleteCause = incompleteCause;
5290
5308
  }
5291
5309
  };
5310
+ function incompleteCauseOf(stepCount, maxSteps, finishReason) {
5311
+ if (stepCount >= maxSteps) return "stepLimit";
5312
+ if (finishReason === "length") return "truncated";
5313
+ if (finishReason !== "stop") return "stopped";
5314
+ return null;
5315
+ }
5292
5316
  var AgentReportedFailureError = class extends Error {
5293
5317
  reason;
5294
5318
  constructor(reason) {
@@ -5314,11 +5338,11 @@ function withCachedHistory(messages) {
5314
5338
  ];
5315
5339
  }
5316
5340
  function isMissingReportRetry(kind) {
5317
- return kind === "missingReport" || kind === "missingReportAtStepLimit";
5341
+ return kind === "missingReport" || incompleteReasonOf(kind) !== void 0;
5318
5342
  }
5319
5343
  function retryKind(err) {
5320
5344
  if (err instanceof MissingReportError) {
5321
- return err.atStepLimit ? "missingReportAtStepLimit" : "missingReport";
5345
+ return err.incompleteCause ? INCOMPLETE_CAUSES[err.incompleteCause].kind : "missingReport";
5322
5346
  }
5323
5347
  if (NoOutputGeneratedError.isInstance(err)) return "transientProvider";
5324
5348
  if (APICallError.isInstance(err) && err.isRetryable) {
@@ -5354,6 +5378,7 @@ async function runAgentAttempt(req, attempt, retryReason) {
5354
5378
  const profileOptions = providerOptionsForProfile(profileName);
5355
5379
  const maxSteps = req.maxSteps ?? profile.maxSteps;
5356
5380
  const forceReport = retryReason === "missingReport";
5381
+ const incompleteReason = incompleteReasonOf(retryReason);
5357
5382
  const modelOptions = forceReport ? { thinking: { type: "disabled" } } : profileOptions;
5358
5383
  logger.info(
5359
5384
  {
@@ -5394,8 +5419,8 @@ async function runAgentAttempt(req, attempt, retryReason) {
5394
5419
  "The previous attempt ended without reportStatus. Do not repeat side effects. Use the available read tools to inspect existing work if needed, then call reportStatus."
5395
5420
  ] : [
5396
5421
  "The previous attempt ended without reportStatus. Do not repeat side effects. Call reportStatus now."
5397
- ] : retryReason === "missingReportAtStepLimit" ? [
5398
- "The previous attempt reached its step limit without reportStatus and may be incomplete. Inspect existing work, finish only the remaining work, avoid repeated side effects, and call reportStatus."
5422
+ ] : incompleteReason ? [
5423
+ `The previous attempt ${incompleteReason} without reportStatus and may be incomplete. Inspect existing work, finish only the remaining work, avoid repeated side effects, and call reportStatus.`
5399
5424
  ] : attempt > 0 ? [
5400
5425
  "This run is a retry after a transient AI service error. Files, shell commands, or ingestion may already have been applied in this workspace \u2014 inspect what is already there and continue from it rather than repeating that work."
5401
5426
  ] : []
@@ -5527,8 +5552,9 @@ async function runAgentAttempt(req, attempt, retryReason) {
5527
5552
  },
5528
5553
  "Agent finished without calling reportStatus"
5529
5554
  );
5530
- const incomplete = hitStepLimit || finishReason !== "stop";
5531
- throw new MissingReportError(incomplete);
5555
+ throw new MissingReportError(
5556
+ incompleteCauseOf(stepCount, maxSteps, finishReason)
5557
+ );
5532
5558
  }
5533
5559
  const result = report.output;
5534
5560
  if (result.status !== "success") {
@@ -6381,7 +6407,8 @@ function sourceSpecificInstructions(input) {
6381
6407
  "Read each record from its real local source at the confirmed entity paths.",
6382
6408
  "Never fabricate, hardcode, or use placeholder records.",
6383
6409
  "Add env vars for any local source access (DB URL, API base, file paths) that is not a fixed repo path.",
6384
- "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."
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.",
6411
+ "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."
6385
6412
  ],
6386
6413
  fileUpload: [
6387
6414
  `Records come from the developer's file, already copied into the project at "${input.uploadFilePath}". Read and parse that exact file.`,
@@ -6437,7 +6464,7 @@ function ingestionInstructions(input) {
6437
6464
  ] : [],
6438
6465
  `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.`,
6439
6466
  '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.',
6440
- "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.",
6467
+ "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.",
6441
6468
  "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.",
6442
6469
  "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`.",
6443
6470
  "If external data access fails, do not run authentication commands, change accounts or machine configuration, or start proxies, containers, or services.",
@@ -6675,6 +6702,12 @@ function parseIngestRecordCount(output) {
6675
6702
  const count = Number(match[1]);
6676
6703
  return Number.isFinite(count) ? count : void 0;
6677
6704
  }
6705
+ function indexedNothing(execution) {
6706
+ return parseIngestRecordCount(execution.output ?? "") === 0;
6707
+ }
6708
+ function ranButIndexedNothing(execution) {
6709
+ return execution.approved && execution.exitCode === 0 && !execution.timedOut && indexedNothing(execution);
6710
+ }
6678
6711
  var START_HINT_LINE = /^ALGOLIA_WIZARD_START_HINT=(.*)$/gm;
6679
6712
  function parseStartHint(output) {
6680
6713
  const matches = [...(output ?? "").matchAll(START_HINT_LINE)];
@@ -6696,7 +6729,7 @@ function ingestOutcome(executions, repoRoot, ingestCommand) {
6696
6729
  (e) => parseIngestRecordCount(e.output ?? "") != null
6697
6730
  );
6698
6731
  const succeeded = newestFirst.filter(
6699
- (e) => e.approved && e.exitCode === 0 && !e.timedOut
6732
+ (e) => e.approved && e.exitCode === 0 && !e.timedOut && !indexedNothing(e)
6700
6733
  );
6701
6734
  const run2 = succeeded.find(matchesReportedCommand) ?? succeeded.find((e) => withCount.includes(e));
6702
6735
  const runs = executions.filter(
@@ -6705,7 +6738,9 @@ function ingestOutcome(executions, repoRoot, ingestCommand) {
6705
6738
  return {
6706
6739
  run: run2,
6707
6740
  attempt: ingestCommand ? newestFirst.find(matchesReportedCommand) : void 0,
6708
- recordCount: parseIngestRecordCount(withCount[0]?.output ?? ""),
6741
+ recordCount: parseIngestRecordCount(
6742
+ withCount.find((e) => !indexedNothing(e))?.output ?? ""
6743
+ ),
6709
6744
  attempts: run2 && !matchesReportedCommand(run2) ? runs + 1 : runs
6710
6745
  };
6711
6746
  }
@@ -6785,9 +6820,13 @@ function noRunReason({
6785
6820
  }
6786
6821
  return "no successful run was recorded";
6787
6822
  }
6823
+ 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.";
6788
6824
  function ingestFailure(attempt, missingRunReason = "no successful run was recorded") {
6789
6825
  if (!attempt) return { reason: missingRunReason };
6790
6826
  if (!attempt.approved) return { reason: "you declined to run it" };
6827
+ if (ranButIndexedNothing(attempt)) {
6828
+ return { reason: "it ran but indexed 0 records", detail: EMPTY_INGEST_DETAIL };
6829
+ }
6791
6830
  const detail = errorLine(attempt.output);
6792
6831
  if (attempt.timedOut) {
6793
6832
  const seconds = Math.round((attempt.durationMs ?? 0) / 1e3);
@@ -6828,8 +6867,8 @@ function briefSummary(summary) {
6828
6867
  if (!firstLine) return "no summary given";
6829
6868
  return firstLine.length > ERROR_SUMMARY_MAX_CHARS ? `${firstLine.slice(0, ERROR_SUMMARY_MAX_CHARS - 1)}\u2026` : firstLine;
6830
6869
  }
6831
- function ingestionRetryInstruction(reason) {
6832
- 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.`;
6870
+ function ingestionRetryInstruction(reason, detail) {
6871
+ 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.`;
6833
6872
  }
6834
6873
  function validationRetryInstructions(validation) {
6835
6874
  return [
@@ -7166,19 +7205,22 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES) {
7166
7205
  await runIngestionAgent();
7167
7206
  let outcome = ingestOutcome(executions, repoRoot, ingestCommand);
7168
7207
  let failureAcknowledged = false;
7169
- while (!outcome.run && !outcome.attempt) {
7170
- const reason = await currentNoRunReason();
7208
+ while (!outcome.run && (!outcome.attempt || ranButIndexedNothing(outcome.attempt))) {
7209
+ const { reason, detail } = outcome.attempt ? ingestFailure(outcome.attempt) : { reason: await currentNoRunReason(), detail: void 0 };
7171
7210
  const choice = await ctx.requestUserInput({
7172
7211
  prompt: INGEST_AGENT_RETRY_PROMPT,
7173
7212
  promptType: "multipleChoice",
7174
7213
  options: [INGEST_RETRY.retry, INGEST_RETRY.skip],
7175
- messages: [`\u26A0\uFE0F Records were not indexed \u2014 ${reason}.`]
7214
+ messages: [
7215
+ `\u26A0\uFE0F Records were not indexed \u2014 ${reason}.`,
7216
+ ...detail ? [detail] : []
7217
+ ]
7176
7218
  });
7177
7219
  if (choice !== INGEST_RETRY.retry) {
7178
7220
  failureAcknowledged = true;
7179
7221
  break;
7180
7222
  }
7181
- await runIngestionAgent([ingestionRetryInstruction(reason)]);
7223
+ await runIngestionAgent([ingestionRetryInstruction(reason, detail)]);
7182
7224
  outcome = ingestOutcome(executions, repoRoot, ingestCommand);
7183
7225
  }
7184
7226
  while (!failureAcknowledged && !outcome.run && outcome.attempt?.approved) {
@@ -9833,7 +9875,7 @@ async function resetProjectState() {
9833
9875
  // package.json with { type: 'json' }
9834
9876
  var package_default2 = {
9835
9877
  name: "@algolia/wizard",
9836
- version: "0.92.0",
9878
+ version: "0.94.0",
9837
9879
  description: "Magically implement Algolia functionality in your codebase",
9838
9880
  type: "module",
9839
9881
  engines: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@algolia/wizard",
3
- "version": "0.92.0",
3
+ "version": "0.94.0",
4
4
  "description": "Magically implement Algolia functionality in your codebase",
5
5
  "type": "module",
6
6
  "engines": {