@rallycry/conveyor-agent 7.0.5 → 7.0.7
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.
- package/dist/{chunk-FCRGYU2M.js → chunk-7RSDCWEI.js} +111 -8
- package/dist/chunk-7RSDCWEI.js.map +1 -0
- package/dist/cli.js +37 -19
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +16 -0
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-FCRGYU2M.js.map +0 -1
|
@@ -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().replace(/[^\w\s]/g, "").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
|
|
734
|
+
execSync("git push --dry-run", { cwd, stdio: ["ignore", "pipe", "pipe"] });
|
|
700
735
|
return false;
|
|
701
736
|
} catch (err) {
|
|
702
|
-
const
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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(),
|
|
@@ -5560,8 +5643,12 @@ var SessionRunner = class _SessionRunner {
|
|
|
5560
5643
|
return this.stopped;
|
|
5561
5644
|
}
|
|
5562
5645
|
// ── Main lifecycle ─────────────────────────────────────────────────
|
|
5563
|
-
|
|
5564
|
-
|
|
5646
|
+
/**
|
|
5647
|
+
* Establish the API connection, wire callbacks, and join the session room.
|
|
5648
|
+
* Call this before run() when you need to send events (e.g. setup/start
|
|
5649
|
+
* command output) before the main agent lifecycle begins.
|
|
5650
|
+
*/
|
|
5651
|
+
async connect() {
|
|
5565
5652
|
await this.setState("connecting");
|
|
5566
5653
|
await this.connection.connect();
|
|
5567
5654
|
await this.setState("connected");
|
|
@@ -5576,6 +5663,13 @@ var SessionRunner = class _SessionRunner {
|
|
|
5576
5663
|
this.pendingMessages.push({ content: msg.content, userId: msg.userId });
|
|
5577
5664
|
}
|
|
5578
5665
|
}
|
|
5666
|
+
}
|
|
5667
|
+
/**
|
|
5668
|
+
* Run the main agent lifecycle: fetch context, resolve mode, execute, loop.
|
|
5669
|
+
* Requires connect() to have been called first.
|
|
5670
|
+
*/
|
|
5671
|
+
// oxlint-disable-next-line max-lines-per-function, complexity -- lifecycle orchestration is inherently sequential
|
|
5672
|
+
async run() {
|
|
5579
5673
|
await this.setState("fetching_context");
|
|
5580
5674
|
try {
|
|
5581
5675
|
const ctx = await this.connection.call("getTaskContext", {
|
|
@@ -5623,6 +5717,11 @@ var SessionRunner = class _SessionRunner {
|
|
|
5623
5717
|
}
|
|
5624
5718
|
await this.shutdown("finished");
|
|
5625
5719
|
}
|
|
5720
|
+
/** Convenience wrapper: connect() then run(). */
|
|
5721
|
+
async start() {
|
|
5722
|
+
await this.connect();
|
|
5723
|
+
await this.run();
|
|
5724
|
+
}
|
|
5626
5725
|
// ── Core loop ──────────────────────────────────────────────────────
|
|
5627
5726
|
async coreLoop() {
|
|
5628
5727
|
while (!this.stopped) {
|
|
@@ -6170,6 +6269,8 @@ var CommitWatcher = class {
|
|
|
6170
6269
|
this.config = config;
|
|
6171
6270
|
this.callbacks = callbacks;
|
|
6172
6271
|
}
|
|
6272
|
+
config;
|
|
6273
|
+
callbacks;
|
|
6173
6274
|
interval = null;
|
|
6174
6275
|
lastKnownRemoteSha = null;
|
|
6175
6276
|
branch = null;
|
|
@@ -6729,6 +6830,8 @@ function spawnChildAgent(assignment, workDir) {
|
|
|
6729
6830
|
const childEnv = { ...process.env };
|
|
6730
6831
|
delete childEnv.CONVEYOR_PROJECT_TOKEN;
|
|
6731
6832
|
delete childEnv.CONVEYOR_PROJECT_ID;
|
|
6833
|
+
delete childEnv.CONVEYOR_SETUP_COMMAND;
|
|
6834
|
+
delete childEnv.CONVEYOR_START_COMMAND;
|
|
6732
6835
|
const effectiveAgentMode = agentMode ?? (isAuto ? "auto" : "");
|
|
6733
6836
|
const effectiveApiUrl = apiUrl || process.env.CONVEYOR_API_URL || "";
|
|
6734
6837
|
if (!effectiveApiUrl) {
|
|
@@ -7342,4 +7445,4 @@ export {
|
|
|
7342
7445
|
loadForwardPorts,
|
|
7343
7446
|
loadConveyorConfig
|
|
7344
7447
|
};
|
|
7345
|
-
//# sourceMappingURL=chunk-
|
|
7448
|
+
//# sourceMappingURL=chunk-7RSDCWEI.js.map
|