@rallycry/conveyor-agent 7.0.5 → 7.0.6

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.
@@ -9,12 +9,16 @@ import {
9
9
  // src/connection/agent-connection.ts
10
10
  import { io } from "socket.io-client";
11
11
  var EVENT_BATCH_MS = 500;
12
- var AgentConnection = class {
12
+ var AgentConnection = class _AgentConnection {
13
13
  socket = null;
14
14
  config;
15
15
  eventBuffer = [];
16
16
  flushTimer = null;
17
17
  lastEmittedStatus = null;
18
+ // Dedup: suppress near-identical messages within a short window
19
+ recentMessages = [];
20
+ static DEDUP_WINDOW_MS = 3e4;
21
+ static DEDUP_SIMILARITY_THRESHOLD = 0.7;
18
22
  // Early-buffering: events that arrive before callbacks are registered
19
23
  earlyMessages = [];
20
24
  earlyStop = false;
@@ -237,6 +241,7 @@ var AgentConnection = class {
237
241
  // ── Convenience methods (thin wrappers around call / emit) ─────────
238
242
  emitStatus(status) {
239
243
  this.lastEmittedStatus = status;
244
+ this.flushEvents();
240
245
  void this.call("reportAgentStatus", {
241
246
  sessionId: this.config.sessionId,
242
247
  status
@@ -245,12 +250,42 @@ var AgentConnection = class {
245
250
  }
246
251
  postChatMessage(content) {
247
252
  if (!this.socket) return;
253
+ if (this.isDuplicateMessage(content)) {
254
+ process.stderr.write(`[dedup] Suppressed near-duplicate message
255
+ `);
256
+ return;
257
+ }
248
258
  void this.call("postAgentMessage", {
249
259
  sessionId: this.config.sessionId,
250
260
  content
251
261
  }).catch(() => {
252
262
  });
253
263
  }
264
+ isDuplicateMessage(content) {
265
+ const now = Date.now();
266
+ this.recentMessages = this.recentMessages.filter(
267
+ (m) => now - m.timestamp < _AgentConnection.DEDUP_WINDOW_MS
268
+ );
269
+ const words = new Set(
270
+ content.toLowerCase().split(/\s+/).filter((w) => w.length >= 3)
271
+ );
272
+ if (words.size === 0) return false;
273
+ for (const recent of this.recentMessages) {
274
+ let intersection = 0;
275
+ for (const w of words) {
276
+ if (recent.words.has(w)) intersection++;
277
+ }
278
+ const union = (/* @__PURE__ */ new Set([...words, ...recent.words])).size;
279
+ if (union > 0 && intersection / union > _AgentConnection.DEDUP_SIMILARITY_THRESHOLD) {
280
+ return true;
281
+ }
282
+ }
283
+ this.recentMessages.push({ words, timestamp: now });
284
+ if (this.recentMessages.length > 3) {
285
+ this.recentMessages.shift();
286
+ }
287
+ return false;
288
+ }
254
289
  sendHeartbeat() {
255
290
  if (!this.socket) return;
256
291
  const statusMap = {
@@ -696,10 +731,12 @@ function tryPush(cwd, branch) {
696
731
  }
697
732
  function isAuthError(cwd) {
698
733
  try {
699
- execSync("git push --dry-run 2>&1", { cwd, stdio: ["ignore", "pipe", "pipe"] });
734
+ execSync("git push --dry-run", { cwd, stdio: ["ignore", "pipe", "pipe"] });
700
735
  return false;
701
736
  } catch (err) {
702
- const msg = err instanceof Error ? err.stderr?.toString() ?? err.message : "";
737
+ const stderr = err.stderr?.toString() ?? "";
738
+ const stdout = err.stdout?.toString() ?? "";
739
+ const msg = stderr || stdout || (err instanceof Error ? err.message : "");
703
740
  return /authentication|authorization|403|401|token/i.test(msg);
704
741
  }
705
742
  }
@@ -2890,6 +2927,45 @@ function buildPmTools(connection, options) {
2890
2927
  // src/tools/discovery-tools.ts
2891
2928
  import { z as z3 } from "zod";
2892
2929
  var SP_DESCRIPTION2 = "Story point value (1=Common, 2=Magic, 3=Rare, 5=Unique)";
2930
+ function buildSearchIconsTool(connection) {
2931
+ return defineTool(
2932
+ "search_icons",
2933
+ "Search for icons by keyword. Searches both the Conveyor database (existing icons) and FontAwesome library (~2200 icons). Returns matching icons with IDs and preview info. Use this to find an icon before generating one.",
2934
+ {
2935
+ query: z3.string().describe("Search query for icon name or keyword (e.g. 'bug', 'gear', 'star')")
2936
+ },
2937
+ async ({ query }) => {
2938
+ try {
2939
+ const [dbIcons, faIcons] = await Promise.all([
2940
+ connection.call("listIcons", { sessionId: connection.sessionId }),
2941
+ connection.call("searchFaIcons", { sessionId: connection.sessionId, query })
2942
+ ]);
2943
+ const q = query.toLowerCase();
2944
+ const matchingDbIcons = dbIcons.filter((icon) => icon.name.toLowerCase().includes(q));
2945
+ const dbFaIds = new Set(matchingDbIcons.map((i) => i.name));
2946
+ const uniqueFaIcons = faIcons.filter((fa) => !dbFaIds.has(fa.fontAwesomeId));
2947
+ const results = {
2948
+ existingIcons: matchingDbIcons.map((i) => ({
2949
+ id: i.id,
2950
+ name: i.name,
2951
+ source: "database"
2952
+ })),
2953
+ fontAwesomeIcons: uniqueFaIcons.map((fa) => ({
2954
+ fontAwesomeId: fa.fontAwesomeId,
2955
+ label: fa.label,
2956
+ styles: fa.styles,
2957
+ source: "fontawesome"
2958
+ }))
2959
+ };
2960
+ return textResult(JSON.stringify(results, null, 2));
2961
+ } catch (error) {
2962
+ const message = error instanceof Error ? error.message : "Unknown error";
2963
+ return textResult(`Failed to search icons: ${message}`);
2964
+ }
2965
+ },
2966
+ { annotations: { readOnlyHint: true } }
2967
+ );
2968
+ }
2893
2969
  function buildIconTools(connection) {
2894
2970
  return [
2895
2971
  defineTool(
@@ -2912,10 +2988,10 @@ function buildIconTools(connection) {
2912
2988
  ),
2913
2989
  defineTool(
2914
2990
  "generate_task_icon",
2915
- "Generate a new SVG icon using AI and assign it to this task. Only use if no existing icon from list_icons is a good fit. Provide a concise visual description.",
2991
+ "Find a FontAwesome icon matching the description and assign it to this task. Falls back to a placeholder if no match is found. Provide a concise keyword or description.",
2916
2992
  {
2917
2993
  prompt: z3.string().describe(
2918
- "Description of the icon to generate (e.g. 'minimal flat gear and wrench icon')"
2994
+ "Keyword or description to search FontAwesome icons (e.g. 'bug', 'gear', 'rocket')"
2919
2995
  ),
2920
2996
  aspectRatio: z3.enum(["auto", "portrait", "landscape", "square"]).optional().describe("Icon aspect ratio, defaults to square")
2921
2997
  },
@@ -2970,6 +3046,7 @@ function buildDiscoveryTools(connection) {
2970
3046
  }
2971
3047
  }
2972
3048
  ),
3049
+ buildSearchIconsTool(connection),
2973
3050
  ...buildIconTools(connection)
2974
3051
  ];
2975
3052
  }
@@ -4880,6 +4957,7 @@ async function handleExitPlanMode(host, input) {
4880
4957
  host.connection.postChatMessage(
4881
4958
  "Plan complete. The task stays in Planning \u2014 identify it or switch to Build mode when ready."
4882
4959
  );
4960
+ host.requestStop();
4883
4961
  return { behavior: "allow", updatedInput: input };
4884
4962
  }
4885
4963
  await host.connection.triggerIdentification();
@@ -5392,6 +5470,10 @@ var QueryBridge = class {
5392
5470
  this.costTracker = new CostTracker();
5393
5471
  this.planSync = new PlanSync(workspaceDir, connection);
5394
5472
  }
5473
+ connection;
5474
+ mode;
5475
+ runnerConfig;
5476
+ callbacks;
5395
5477
  harness;
5396
5478
  costTracker;
5397
5479
  planSync;
@@ -5485,6 +5567,7 @@ var QueryBridge = class {
5485
5567
  return bridge._abortController;
5486
5568
  },
5487
5569
  isStopped: () => bridge._stopped,
5570
+ requestStop: () => bridge.stop(),
5488
5571
  createInputStream: (prompt) => bridge.createInputStream(prompt),
5489
5572
  snapshotPlanFiles: () => bridge.planSync.snapshotPlanFiles(),
5490
5573
  syncPlanFile: () => bridge.planSync.syncPlanFile(),
@@ -6170,6 +6253,8 @@ var CommitWatcher = class {
6170
6253
  this.config = config;
6171
6254
  this.callbacks = callbacks;
6172
6255
  }
6256
+ config;
6257
+ callbacks;
6173
6258
  interval = null;
6174
6259
  lastKnownRemoteSha = null;
6175
6260
  branch = null;
@@ -7342,4 +7427,4 @@ export {
7342
7427
  loadForwardPorts,
7343
7428
  loadConveyorConfig
7344
7429
  };
7345
- //# sourceMappingURL=chunk-FCRGYU2M.js.map
7430
+ //# sourceMappingURL=chunk-3H2FXJFD.js.map