@quantiya/codevibe-core 2.0.87 → 2.0.88

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.
@@ -23604,7 +23604,7 @@ var AppSyncClient = class _AppSyncClient {
23604
23604
  throw logger.warn("[AppSyncClient] explicit review-scope reset rejected by old backend schema; failing closed", {
23605
23605
  gateId: input.gateId,
23606
23606
  taskId: input.taskId
23607
- }), err;
23607
+ }), err.reviewScopeResetUnsupported = !0, err;
23608
23608
  logger.warn("[AppSyncClient] submitImplementorOutput additive fields rejected by backend schema; retrying legacy input", {
23609
23609
  gateId: input.gateId,
23610
23610
  taskId: input.taskId,
@@ -23621,7 +23621,10 @@ var AppSyncClient = class _AppSyncClient {
23621
23621
  };
23622
23622
  response = await this.graphqlRequest(mutations.submitImplementorOutput, retryVariables);
23623
23623
  }
23624
- return this.#parseAwsJson(response.data?.submitImplementorOutput, "submitImplementorOutput");
23624
+ let parsed = this.#parseOrchestrationResult(response.data?.submitImplementorOutput, "submitImplementorOutput");
23625
+ if (typeof parsed.gateId != "string" || parsed.gateId.length === 0 || typeof parsed.workflowState != "string" || parsed.workflowState.length === 0)
23626
+ throw new Error("submitImplementorOutput: result missing string `gateId` / `workflowState`");
23627
+ return { gateId: parsed.gateId, workflowState: parsed.workflowState };
23625
23628
  }
23626
23629
  /**
23627
23630
  * §3.C.22 — submit one reviewer seat's verdict. The full `ReviewerVerdict`
@@ -23679,7 +23682,7 @@ var AppSyncClient = class _AppSyncClient {
23679
23682
  throw new Error("getReviewerPrompt: gateId is required");
23680
23683
  if (!Number.isInteger(seatId) || seatId < 0)
23681
23684
  throw new Error("getReviewerPrompt: seatId must be a non-negative integer");
23682
- let response = await this.graphqlRequest(queries.getReviewerPrompt, { gateId, seatId }), parsed = this.#parseAwsJson(response.data?.getReviewerPrompt, "getReviewerPrompt");
23685
+ let response = await this.graphqlRequest(queries.getReviewerPrompt, { gateId, seatId }), parsed = this.#parseOrchestrationResult(response.data?.getReviewerPrompt, "getReviewerPrompt");
23683
23686
  if (!parsed.encPrompt || typeof parsed.encPrompt.ciphertextB64 != "string")
23684
23687
  throw new Error("getReviewerPrompt: response missing encPrompt.ciphertextB64");
23685
23688
  let prompt = cryptoService.decryptContent(parsed.encPrompt.ciphertextB64, sessionKeyBase64);
@@ -23751,7 +23754,7 @@ var AppSyncClient = class _AppSyncClient {
23751
23754
  * key.
23752
23755
  */
23753
23756
  async getClassBSigningPublicKey() {
23754
- let response = await this.graphqlRequest(queries.getClassBSigningPublicKey, {}), parsed = this.#parseAwsJson(
23757
+ let response = await this.graphqlRequest(queries.getClassBSigningPublicKey, {}), parsed = this.#parseOrchestrationResult(
23755
23758
  response.data?.getClassBSigningPublicKey,
23756
23759
  "getClassBSigningPublicKey"
23757
23760
  );
@@ -23767,7 +23770,7 @@ var AppSyncClient = class _AppSyncClient {
23767
23770
  async getInReviewAssignments(taskId) {
23768
23771
  if (typeof taskId != "string" || taskId.length === 0)
23769
23772
  throw new Error("getInReviewAssignments: taskId is required");
23770
- let response = await this.graphqlRequest(queries.getInReviewAssignments, { taskId }), parsed = this.#parseAwsJson(
23773
+ let response = await this.graphqlRequest(queries.getInReviewAssignments, { taskId }), parsed = this.#parseOrchestrationResult(
23771
23774
  response.data?.getInReviewAssignments,
23772
23775
  "getInReviewAssignments"
23773
23776
  );
@@ -23785,7 +23788,7 @@ var AppSyncClient = class _AppSyncClient {
23785
23788
  async getTaskGroupTracks(taskGroupId) {
23786
23789
  if (typeof taskGroupId != "string" || taskGroupId.length === 0)
23787
23790
  throw new Error("getTaskGroupTracks: taskGroupId is required");
23788
- let response = await this.graphqlRequest(queries.getTaskGroupTracks, { taskGroupId }), parsed = this.#parseAwsJson(
23791
+ let response = await this.graphqlRequest(queries.getTaskGroupTracks, { taskGroupId }), parsed = this.#parseOrchestrationResult(
23789
23792
  response.data?.getTaskGroupTracks,
23790
23793
  "getTaskGroupTracks"
23791
23794
  );
@@ -23804,7 +23807,7 @@ var AppSyncClient = class _AppSyncClient {
23804
23807
  async getInFlightTeamTracks(sessionId) {
23805
23808
  if (typeof sessionId != "string" || sessionId.length === 0)
23806
23809
  throw new Error("getInFlightTeamTracks: sessionId is required");
23807
- let response = await this.graphqlRequest(queries.getInFlightTeamTracks, { sessionId }), parsed = this.#parseAwsJson(
23810
+ let response = await this.graphqlRequest(queries.getInFlightTeamTracks, { sessionId }), parsed = this.#parseOrchestrationResult(
23808
23811
  response.data?.getInFlightTeamTracks,
23809
23812
  "getInFlightTeamTracks"
23810
23813
  );
@@ -23825,7 +23828,7 @@ var AppSyncClient = class _AppSyncClient {
23825
23828
  async getTaskGroupStatus(taskGroupId) {
23826
23829
  if (typeof taskGroupId != "string" || taskGroupId.length === 0)
23827
23830
  throw new Error("getTaskGroupStatus: taskGroupId is required");
23828
- let response = await this.graphqlRequest(queries.getTaskGroupStatus, { taskGroupId }), parsed = this.#parseAwsJson(
23831
+ let response = await this.graphqlRequest(queries.getTaskGroupStatus, { taskGroupId }), parsed = this.#parseOrchestrationResult(
23829
23832
  response.data?.getTaskGroupStatus,
23830
23833
  "getTaskGroupStatus"
23831
23834
  );
@@ -23989,7 +23992,11 @@ var AppSyncClient = class _AppSyncClient {
23989
23992
  }
23990
23993
  /**
23991
23994
  * P46b (ID-16) — parse an orchestration Lambda AWSJSON result and surface the
23992
- * handler's `{errorType, errorMessage}` envelope as a thrown error.
23995
+ * handler's `{errorType, errorMessage}` envelope as a thrown error. SB-67: every read that went through a raw
23996
+ * `#parseAwsJson` now goes through here — read raw, the envelope was data (a hang on submit, an empty list on the
23997
+ * recovery reads). Reads parsed by hand or by `coerceAwsJsonObject` (createTaskGroup, submitMergeGateVerdict,
23998
+ * submitTrackVerificationOutcome, claimGroupDecision, getTaskReviewSummary) check a required field and throw an
23999
+ * untyped error on an envelope.
23993
24000
  */
23994
24001
  #parseOrchestrationResult(raw, op) {
23995
24002
  let parsed = this.#parseAwsJson(raw, op);
@@ -67655,6 +67662,9 @@ function classifyImplementorRoundFailure(err) {
67655
67662
  return AUTHORITY_REFUSAL_LABEL[err.refusal.category] ?? "the implementor was refused authority";
67656
67663
  if (err instanceof StaleShadowRootError)
67657
67664
  return `the recorded workspace-copy folder ${err.stalePath} is missing and its contents could not be accounted for (they may have been moved). Run \`codevibe doctor\` to review and clear it`;
67665
+ let engine = err;
67666
+ if (engine && engine.name === "AppSyncGraphQLError" && typeof engine.errorType == "string")
67667
+ return engine.errorType === "ClientError" && String(engine.responseMessage ?? "").startsWith("payload too large") ? "the change is too large to submit for review" : "the review service rejected the submission";
67658
67668
  let msg = err instanceof Error ? err.message.toLowerCase() : "";
67659
67669
  return msg.includes("enoent") || msg.includes("spawn") || msg.includes("command not found") || msg.includes("not installed") ? "the coding agent could not be started" : "an unexpected error occurred";
67660
67670
  }
@@ -68777,6 +68787,11 @@ var QuorumLoop = class _QuorumLoop {
68777
68787
  this.inFlightImplementorGates = /* @__PURE__ */ new Set();
68778
68788
  /** Gates whose implementor output has been submitted. */
68779
68789
  this.submittedOutputGates = /* @__PURE__ */ new Set();
68790
+ /**
68791
+ * SB-67 (Stage 1 R1-F1) — gates whose round failed after its submission started. The engine can dispatch reviewers
68792
+ * before it reports an error; a late dispatch for one of these gates belongs to a discarded task and is dropped.
68793
+ */
68794
+ this.abandonedSubmitGates = /* @__PURE__ */ new Set();
68780
68795
  /**
68781
68796
  * PHASE-CP-10-MIN (#585) #C10M-16 / Codex-H1 — the per-task ACTIVE
68782
68797
  * implementor context. `runImplementorRound` populates it at spawn (wiring
@@ -71382,7 +71397,13 @@ ${section}`);
71382
71397
  reviewScopeReset: args.reviewScopeReset
71383
71398
  });
71384
71399
  if (args.reviewScopeReset && this.reviewScopeResetTasks.delete(args.taskId), this.teamRunIsHalted(args.teamAuthority)) return;
71385
- if (this.submittedOutputGates.add(gateId), result && (result.workflowState === "halted" || result.workflowState === "idle")) {
71400
+ if (this.submittedOutputGates.add(gateId), result?.replayedAfterEngineError === !0 && result.workflowState === "halted" && !isEngineEmptyArtifact(files))
71401
+ logger.info("[QuorumLoop] submission confirmed by the engine replay of an already-resolved gate", {
71402
+ gateId,
71403
+ taskId: args.taskId,
71404
+ workflowState: result.workflowState
71405
+ });
71406
+ else if (result && (result.workflowState === "halted" || result.workflowState === "idle")) {
71386
71407
  let crashedEmpty = files.length === 0 && exit.exitCode !== 0, reason = files.length === 0 ? crashedEmpty ? "The coding agent exited with an error before making any changes \u2014 nothing to review. Re-run the task; if this repeats, check the desktop log." : "The implementor produced no changes \u2014 nothing to review." : "The task was halted before review (no reviewer quorum ran).";
71387
71408
  (crashedEmpty ? logger.warn.bind(logger) : logger.info.bind(logger))("[QuorumLoop] execute-first born-halt surfaced synchronously (#EF-1)", {
71388
71409
  gateId,
@@ -71398,7 +71419,11 @@ ${section}`);
71398
71419
  }
71399
71420
  } catch (err) {
71400
71421
  if (this.teamRunIsHalted(args.teamAuthority)) return;
71401
- if (classifyImplementorQuotaFailure(void 0, err.message ?? "")) {
71422
+ if (
71423
+ // SB-67 (Stage 2 R1-F2): only an implementor failure can be a provider quota; once the submission started,
71424
+ // an error is the engine's (its own `HTTP 429/503` text must not read as the coding agent's usage limit).
71425
+ !submissionStarted && classifyImplementorQuotaFailure(void 0, err.message ?? "")
71426
+ ) {
71402
71427
  logger.info("[QuorumLoop] implementor round threw a quota error \u2014 continuation fork (defensive)", {
71403
71428
  gateId,
71404
71429
  taskId: args.taskId
@@ -71416,8 +71441,8 @@ ${section}`);
71416
71441
  });
71417
71442
  let reason = classifyImplementorRoundFailure(err);
71418
71443
  this.surfaceHalt(`The task could not proceed \u2014 ${reason}.`);
71419
- let reviseFeedbackId = args.reviseFeedbackId, preserveResetForRetry = submissionStarted && args.reviewScopeReset === !0 && reviseFeedbackId !== void 0 && this.reviewScopeResetTasks.has(args.taskId);
71420
- this.emitProgress(
71444
+ let reviseFeedbackId = args.reviseFeedbackId, preserveResetForRetry = submissionStarted && args.reviewScopeReset === !0 && reviseFeedbackId !== void 0 && this.reviewScopeResetTasks.has(args.taskId) && err?.reviewScopeResetUnsupported === !0;
71445
+ submissionStarted && !preserveResetForRetry && this.abandonedSubmitGates.add(gateId), this.emitProgress(
71421
71446
  {
71422
71447
  phase: "round_failed",
71423
71448
  round: args.roundNumber,
@@ -73158,10 +73183,10 @@ ${section}`);
73158
73183
  * error propagates (the caller surfaces it fail-closed).
73159
73184
  */
73160
73185
  async submitImplementorOutputWithRetry(args) {
73161
- let lastErr = null;
73186
+ let lastErr = null, replayedAfterEngineError = !1;
73162
73187
  for (let attempt = 0; attempt < SUBMIT_OUTPUT_MAX_ATTEMPTS; attempt++)
73163
73188
  try {
73164
- return await this.deps.appsyncClient.submitImplementorOutput(
73189
+ let result = await this.deps.appsyncClient.submitImplementorOutput(
73165
73190
  {
73166
73191
  taskId: args.taskId,
73167
73192
  gateId: args.gateId,
@@ -73173,8 +73198,9 @@ ${section}`);
73173
73198
  },
73174
73199
  args.sessionKey
73175
73200
  );
73201
+ return replayedAfterEngineError ? { ...result, replayedAfterEngineError } : result;
73176
73202
  } catch (err) {
73177
- if (lastErr = err, !isNotFoundError(lastErr) || attempt === SUBMIT_OUTPUT_MAX_ATTEMPTS - 1)
73203
+ if (lastErr = err, isPossiblyAfterWriteSubmitError(lastErr) && (replayedAfterEngineError = !0), isDefinitiveSubmitRejection(lastErr) || !(isNotFoundError(lastErr) || isPossiblyAfterWriteSubmitError(lastErr)) || attempt === SUBMIT_OUTPUT_MAX_ATTEMPTS - 1)
73178
73204
  throw lastErr;
73179
73205
  await this.sleep(SUBMIT_OUTPUT_BASE_DELAY_MS * Math.pow(2, attempt));
73180
73206
  }
@@ -73182,6 +73208,13 @@ ${section}`);
73182
73208
  }
73183
73209
  // ─── §3.C.21 — REVIEWER_DISPATCH → spawn one seat ───────────────────────
73184
73210
  async handleReviewerDispatch(payload) {
73211
+ if (this.abandonedSubmitGates.has(payload.gateId)) {
73212
+ logger.info("[QuorumLoop] ReviewerDispatch for an abandoned submission \u2014 dropped", {
73213
+ gateId: payload.gateId,
73214
+ seatId: payload.seatId
73215
+ });
73216
+ return;
73217
+ }
73185
73218
  let dispatchTaskId = this.taskByGateId.get(payload.gateId), originEpoch = this.originEpochFor(dispatchTaskId), seat = numericSeatId(payload.seatId);
73186
73219
  if (seat === null) {
73187
73220
  logger.warn("[QuorumLoop] ReviewerDispatch with non-numeric seat \u2014 dropped", {
@@ -73219,6 +73252,10 @@ ${section}`);
73219
73252
  */
73220
73253
  async spawnOneSeat(args) {
73221
73254
  let key = seatKey(args.gateId, args.seatId);
73255
+ if (this.abandonedSubmitGates.has(args.gateId)) {
73256
+ logger.info("[QuorumLoop] seat spawn dropped (abandoned submission)", { key });
73257
+ return;
73258
+ }
73222
73259
  if (this.runningSeats.has(key) || this.submittedSeats.has(key)) {
73223
73260
  logger.info("[QuorumLoop] seat spawn no-op (already running/submitted)", { key });
73224
73261
  return;
@@ -75937,6 +75974,27 @@ function diffCapturedProgress(round, files) {
75937
75974
  deleted
75938
75975
  };
75939
75976
  }
75977
+ var SUBMIT_DEFINITIVE_ERROR_TYPES = /* @__PURE__ */ new Set([
75978
+ "ClientError",
75979
+ "SessionError",
75980
+ "Unauthorized",
75981
+ "CryptoError",
75982
+ "BudgetExhausted",
75983
+ "NotImplemented"
75984
+ ]), RUST_WHITESPACE_ONLY = /^[\t\n\v\f\r \u0085\u00A0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]*$/;
75985
+ function isEngineEmptyArtifact(files) {
75986
+ return files.every(
75987
+ (f) => RUST_WHITESPACE_ONLY.test(f.content) && f.change_kind.trim().toLowerCase() !== "deleted"
75988
+ );
75989
+ }
75990
+ function isDefinitiveSubmitRejection(err) {
75991
+ let e = err;
75992
+ return e.reviewScopeResetUnsupported === !0 || e.name === "AppSyncGraphQLError" && typeof e.errorType == "string" && SUBMIT_DEFINITIVE_ERROR_TYPES.has(e.errorType);
75993
+ }
75994
+ function isPossiblyAfterWriteSubmitError(err) {
75995
+ let e = err;
75996
+ return e.name === "AppSyncGraphQLError" && typeof e.errorType == "string" && !isDefinitiveSubmitRejection(err);
75997
+ }
75940
75998
  function isNotFoundError(err) {
75941
75999
  let m = err.message.toLowerCase();
75942
76000
  return m.includes("notfound") || m.includes("not found") || m.includes("not yet written") || m.includes("not present") || m.includes("gatenotfound");
@@ -57,6 +57,8 @@ export declare function failureReasonTemplate(agentKind: ReviewerAgentKind, reas
57
57
  * "blocked by the local authority allowlist", etc.).
58
58
  * - A spawn/start failure (ENOENT / "spawn" / "not found" on the agent binary)
59
59
  * → a generic "the agent could not be started" label.
60
+ * - The engine's typed rejection of a submission (SB-67) → "the change is too large to submit for review"
61
+ * for PayloadTooLarge, else "the review service rejected the submission".
60
62
  * - Anything else → a generic "an unexpected error occurred" label.
61
63
  *
62
64
  * Exported for the sanitization test (proves no raw message leaks).
@@ -1027,6 +1029,11 @@ export declare class QuorumLoop {
1027
1029
  private readonly inFlightImplementorGates;
1028
1030
  /** Gates whose implementor output has been submitted. */
1029
1031
  private readonly submittedOutputGates;
1032
+ /**
1033
+ * SB-67 (Stage 1 R1-F1) — gates whose round failed after its submission started. The engine can dispatch reviewers
1034
+ * before it reports an error; a late dispatch for one of these gates belongs to a discarded task and is dropped.
1035
+ */
1036
+ private readonly abandonedSubmitGates;
1030
1037
  /**
1031
1038
  * PHASE-CP-10-MIN (#585) #C10M-16 / Codex-H1 — the per-task ACTIVE
1032
1039
  * implementor context. `runImplementorRound` populates it at spawn (wiring
@@ -3087,4 +3094,8 @@ export declare class QuorumLoop {
3087
3094
  _replaceShadowForRound0UnlockedForTests(taskId: string): Promise<void>;
3088
3095
  _ensureShadowUnlockedForTests(taskId: string): Promise<WorkspaceShadow>;
3089
3096
  }
3097
+ export declare function isEngineEmptyArtifact(files: readonly {
3098
+ change_kind: string;
3099
+ content: string;
3100
+ }[]): boolean;
3090
3101
  export { reviewerAgentToLeAgent, renderAgenticResolveBrief };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quantiya/codevibe-core",
3
- "version": "2.0.87",
3
+ "version": "2.0.88",
4
4
  "description": "Core library for CodeVibe plugins - shared keychain, crypto, AppSync, and auth functionality",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",