@pushpalsdev/cli 1.0.29 → 1.0.30

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.
@@ -3953,8 +3953,45 @@ function formatSessionEventLine(event) {
3953
3953
  }
3954
3954
  return null;
3955
3955
  }
3956
+ function buildSessionEventReplayFingerprint(event) {
3957
+ const type = String(event.type ?? "").trim().toLowerCase();
3958
+ if (type !== "status")
3959
+ return null;
3960
+ const payload = event.payload ?? {};
3961
+ const source = String(event.from ?? payload.agentId ?? "status").trim().toLowerCase();
3962
+ const state = String(payload.state ?? "").trim().toLowerCase();
3963
+ const detail = String(payload.detail ?? "").trim().toLowerCase();
3964
+ const message = String(payload.message ?? "").trim().toLowerCase();
3965
+ return {
3966
+ source,
3967
+ fingerprint: `${type}:${source}:${state}:${detail}:${message}`
3968
+ };
3969
+ }
3970
+ function createSessionEventReplayFilter() {
3971
+ const seenEventIds = new Set;
3972
+ const lastStatusFingerprintBySource = new Map;
3973
+ return {
3974
+ shouldRender(event) {
3975
+ const eventId = String(event.id ?? "").trim();
3976
+ if (eventId) {
3977
+ if (seenEventIds.has(eventId))
3978
+ return false;
3979
+ seenEventIds.add(eventId);
3980
+ }
3981
+ const replayStatus = buildSessionEventReplayFingerprint(event);
3982
+ if (!replayStatus)
3983
+ return true;
3984
+ const previous = lastStatusFingerprintBySource.get(replayStatus.source);
3985
+ if (previous === replayStatus.fingerprint)
3986
+ return false;
3987
+ lastStatusFingerprintBySource.set(replayStatus.source, replayStatus.fingerprint);
3988
+ return true;
3989
+ }
3990
+ };
3991
+ }
3956
3992
  async function runSessionStream(serverUrl, sessionId, client, print, signal) {
3957
3993
  let cursor = 0;
3994
+ const replayFilter = createSessionEventReplayFilter();
3958
3995
  while (!signal.aborted) {
3959
3996
  try {
3960
3997
  const response = await fetchWithTimeout(`${serverUrl}/sessions/${encodeURIComponent(sessionId)}/events${buildClientTransportQuery(cursor, client)}`, {}, 15000);
@@ -4004,6 +4041,8 @@ async function runSessionStream(serverUrl, sessionId, client, print, signal) {
4004
4041
  cursor = Math.max(cursor, blockCursor, serverCursor);
4005
4042
  if (!parsed.envelope)
4006
4043
  continue;
4044
+ if (!replayFilter.shouldRender(parsed.envelope))
4045
+ continue;
4007
4046
  const line = formatSessionEventLine(parsed.envelope);
4008
4047
  if (line)
4009
4048
  print(line);
@@ -4506,6 +4545,7 @@ export {
4506
4545
  ensureWorkerpalDockerImageReady,
4507
4546
  ensureRuntimeBinaries,
4508
4547
  downloadRuntimeAssetsFromSourceTag,
4548
+ createSessionEventReplayFilter,
4509
4549
  copyTrackedRepoPath,
4510
4550
  cleanupLingeringWorkerpalWarmContainers,
4511
4551
  cleanupLingeringPushPalsGitWorktrees,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pushpalsdev/cli",
3
- "version": "1.0.29",
3
+ "version": "1.0.30",
4
4
  "description": "PushPals terminal CLI for LocalBuddy -> RemoteBuddy orchestration",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -130,14 +130,51 @@ export function redactSensitiveText(value: string): string {
130
130
  }
131
131
 
132
132
  export function buildCriticRevisionIssues(
133
- critic: { score: number; mustFix: string[] } | null | undefined,
133
+ critic:
134
+ | {
135
+ score: number;
136
+ findings?: string[];
137
+ mustFix?: string[];
138
+ revisionGuidance?: string;
139
+ }
140
+ | null
141
+ | undefined,
134
142
  qualityCriticMinScore: number,
135
143
  ): string[] {
136
144
  if (!critic) return [];
137
145
  if (critic.score >= qualityCriticMinScore) return [];
138
- return [
146
+ const issues = [
139
147
  `Critic score ${critic.score.toFixed(1)} is below required threshold ${qualityCriticMinScore}.`,
140
148
  ];
149
+ const mustFix = Array.isArray(critic.mustFix) ? critic.mustFix : [];
150
+ const findings = Array.isArray(critic.findings) ? critic.findings : [];
151
+ const revisionGuidance = String(critic.revisionGuidance ?? "").trim();
152
+ const actionableItems = (mustFix.length > 0 ? mustFix : findings)
153
+ .map((entry) => toSingleLine(entry, 180))
154
+ .filter(Boolean)
155
+ .slice(0, 3);
156
+ for (const item of actionableItems) {
157
+ issues.push(mustFix.length > 0 ? `Critic must-fix: ${item}` : `Critic finding: ${item}`);
158
+ }
159
+ if (revisionGuidance) {
160
+ issues.push(`Critic revision guidance: ${toSingleLine(revisionGuidance, 220)}`);
161
+ }
162
+ return issues;
163
+ }
164
+
165
+ export function buildQualityGateRevisionIssues(
166
+ qualityIssues: string[],
167
+ critic: CriticReview | null,
168
+ qualityCriticMinScore: number,
169
+ ): string[] {
170
+ const normalizedQualityIssues = qualityIssues
171
+ .map((entry) => String(entry ?? "").trim())
172
+ .filter(Boolean);
173
+ if (!critic || critic.score >= qualityCriticMinScore) {
174
+ return [...normalizedQualityIssues];
175
+ }
176
+ const merged = [...normalizedQualityIssues, ...buildCriticRevisionIssues(critic, qualityCriticMinScore)];
177
+ return [...new Set(merged)];
141
178
  }
142
179
 
143
180
  export function resolveReviewFixCompletionBranch(
@@ -3006,9 +3043,10 @@ export async function executeJob(
3006
3043
  : executor === "openai_codex"
3007
3044
  ? await runCodexCriticReview(repo, attemptParams, quality, runtimeConfig, onLog)
3008
3045
  : await runTaskCriticReview(repo, attemptParams, quality, runtimeConfig, onLog);
3046
+ const deterministicRequiresRevision = !quality.ok;
3009
3047
  const criticRequiresRevision = Boolean(critic && critic.score < qualityCriticMinScore);
3010
3048
 
3011
- if (!criticRequiresRevision) {
3049
+ if (!deterministicRequiresRevision && !criticRequiresRevision) {
3012
3050
  if (critic) {
3013
3051
  onLog?.(
3014
3052
  "stdout",
@@ -3018,10 +3056,11 @@ export async function executeJob(
3018
3056
  return result;
3019
3057
  }
3020
3058
 
3021
- const issues: string[] = [];
3022
- if (criticRequiresRevision && critic) {
3023
- issues.push(...buildCriticRevisionIssues(critic, qualityCriticMinScore));
3024
- }
3059
+ const issues = buildQualityGateRevisionIssues(
3060
+ quality.issues,
3061
+ critic,
3062
+ qualityCriticMinScore,
3063
+ );
3025
3064
  const issueSummary = issues.map((entry) => toSingleLine(entry, 180)).join(" | ");
3026
3065
  if (revisionAttempt >= qualityMaxAutoRevisions) {
3027
3066
  if (qualitySoftPassOnExhausted) {