querysub 0.683.0 → 0.685.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.
package/package.json
CHANGED
|
@@ -14,6 +14,7 @@ module.hotreload = false;
|
|
|
14
14
|
|
|
15
15
|
const PARALLEL_FILE_SEARCHES = 4;
|
|
16
16
|
const DEFAULT_LIMIT = 100;
|
|
17
|
+
const MAX_LIMIT = 10_000;
|
|
17
18
|
// Compressed input bytes of downloaded files to keep resident.
|
|
18
19
|
const DOWNLOAD_CACHE_MAX_BYTES = 1024 ** 3;
|
|
19
20
|
// Decoded output bytes (estimated via serialized length) of decoded files to keep resident. Sized independently of the download cache because the decoded form is much larger than the compressed download.
|
|
@@ -125,6 +126,9 @@ export async function searchStorageLogsForMCP(config: {
|
|
|
125
126
|
throw new Error(`startTime (${startTime}) must be < endTime (${endTime})`);
|
|
126
127
|
}
|
|
127
128
|
let limit = config.limit ?? DEFAULT_LIMIT;
|
|
129
|
+
if (limit > MAX_LIMIT) {
|
|
130
|
+
throw new Error(`limit ${limit} > ${MAX_LIMIT}. This endpoint returns projected rows over a socket, it is not a bulk export. If you need to aggregate large volumes, list the log files and download + parse them directly.`);
|
|
131
|
+
}
|
|
128
132
|
let direction = config.direction ?? "fromEnd";
|
|
129
133
|
let columns = config.columns && (config.columns.includes("time") ? config.columns : ["time", ...config.columns]);
|
|
130
134
|
let clauses = parseSearchClauses(query);
|
|
@@ -32,6 +32,7 @@ const COMMENT_TEXTAREA_COLLAPSED_HEIGHT = 40;
|
|
|
32
32
|
export const STATE_HUES: Record<TicketState, number> = {
|
|
33
33
|
"investigation": 45,
|
|
34
34
|
"code-change": 210,
|
|
35
|
+
"manual-fix-required": 20,
|
|
35
36
|
"fixed": 120,
|
|
36
37
|
"not-a-bug": 280,
|
|
37
38
|
"confused": 330,
|
|
@@ -331,9 +332,6 @@ class TicketDetail extends qreact.Component<{ ticketId: string }> {
|
|
|
331
332
|
|
|
332
333
|
let sortedComments = [...ticket.comments];
|
|
333
334
|
sort(sortedComments, x => -x.time);
|
|
334
|
-
// Chronological, because patches on the same file build on each other in the order they were proposed.
|
|
335
|
-
let patchComments = ticket.comments.filter(c => c.kind === "patch" && c.patchFiles);
|
|
336
|
-
sort(patchComments, x => x.time);
|
|
337
335
|
return <div className={css.vbox(16).pad2(16).fillBoth.maxWidth("100%").minHeight(0)}>
|
|
338
336
|
<div className={css.hbox(16).alignItems("center")}>
|
|
339
337
|
<ATag values={[ticketIdURL.getOverride("")]}>← All Tickets</ATag>
|
|
@@ -411,21 +409,6 @@ class TicketDetail extends qreact.Component<{ ticketId: string }> {
|
|
|
411
409
|
<div className={css.vbox(12).fillWidth.flexGrow(1).minHeight(0).overflowAuto}>
|
|
412
410
|
<div className={css.hbox(16).alignItems("center")}>
|
|
413
411
|
<h3 className={css.margin(0)}>Comments ({sortedComments.length})</h3>
|
|
414
|
-
{patchComments.length > 0 && (
|
|
415
|
-
<Button
|
|
416
|
-
hue={120}
|
|
417
|
-
onClick={() => {
|
|
418
|
-
let ticketId = this.props.ticketId;
|
|
419
|
-
let commentIds = patchComments.map(c => c.id);
|
|
420
|
-
Querysub.onCommitFinished(async () => {
|
|
421
|
-
await getController().applyPatches.promise(ticketId, commentIds);
|
|
422
|
-
resetTicketData();
|
|
423
|
-
});
|
|
424
|
-
}}
|
|
425
|
-
>
|
|
426
|
-
Apply All Patches ({patchComments.length})
|
|
427
|
-
</Button>
|
|
428
|
-
)}
|
|
429
412
|
</div>
|
|
430
413
|
{sortedComments.map(comment => (
|
|
431
414
|
<TicketCommentItem key={comment.id} ticketId={this.props.ticketId} comment={comment} />
|
|
@@ -661,6 +644,22 @@ class TicketCommentItem extends qreact.Component<{
|
|
|
661
644
|
>
|
|
662
645
|
Delete
|
|
663
646
|
</Button>
|
|
647
|
+
{comment.kind === "patch" && comment.patchFiles && (
|
|
648
|
+
<Button
|
|
649
|
+
hue={120}
|
|
650
|
+
flavor="small"
|
|
651
|
+
onClick={() => {
|
|
652
|
+
let ticketId = this.props.ticketId;
|
|
653
|
+
let commentId = comment.id;
|
|
654
|
+
Querysub.onCommitFinished(async () => {
|
|
655
|
+
await getController().applyPatches.promise(ticketId, [commentId]);
|
|
656
|
+
resetTicketData();
|
|
657
|
+
});
|
|
658
|
+
}}
|
|
659
|
+
>
|
|
660
|
+
Apply Patch
|
|
661
|
+
</Button>
|
|
662
|
+
)}
|
|
664
663
|
{searchInfo && (
|
|
665
664
|
<>
|
|
666
665
|
<span className={css.fontSize(12).whiteSpace("nowrap")}>{searchInfo.rangeText}</span>
|
|
@@ -399,11 +399,11 @@ Query syntax (case-insensitive substring match by default):
|
|
|
399
399
|
},
|
|
400
400
|
{
|
|
401
401
|
name: "setTicketState",
|
|
402
|
-
description: `Set the state of the ticket you are investigating. Use "code-change" once you have proposed patches, "not-a-bug" if the error should simply be ignored, or "confused" if you genuinely cannot figure out what is going on.`,
|
|
402
|
+
description: `Set the state of the ticket you are investigating. Use "code-change" once you have proposed patches, "manual-fix-required" if you know what is wrong but cannot express the fix as a patch, "not-a-bug" if the error should simply be ignored, or "confused" if you genuinely cannot figure out what is going on.`,
|
|
403
403
|
inputSchema: {
|
|
404
404
|
type: "object",
|
|
405
405
|
properties: {
|
|
406
|
-
state: { type: "string", enum: ["code-change", "not-a-bug", "confused"] },
|
|
406
|
+
state: { type: "string", enum: ["code-change", "manual-fix-required", "not-a-bug", "confused"] },
|
|
407
407
|
},
|
|
408
408
|
required: ["state"],
|
|
409
409
|
},
|
|
@@ -504,8 +504,8 @@ async function callTool(toolName: string, args: Record<string, unknown>): Promis
|
|
|
504
504
|
result = await getNodeInfos();
|
|
505
505
|
} else if (toolName === "setTicketState") {
|
|
506
506
|
let state = String(args.state ?? "") as TicketState;
|
|
507
|
-
if (state !== "code-change" && state !== "not-a-bug" && state !== "confused") {
|
|
508
|
-
throw new Error(`setTicketState only allows "code-change", "not-a-bug", or "confused"`);
|
|
507
|
+
if (state !== "code-change" && state !== "manual-fix-required" && state !== "not-a-bug" && state !== "confused") {
|
|
508
|
+
throw new Error(`setTicketState only allows "code-change", "manual-fix-required", "not-a-bug", or "confused"`);
|
|
509
509
|
}
|
|
510
510
|
let service = await ticketService();
|
|
511
511
|
await service.setTicketState(ticketId, state);
|
|
@@ -673,7 +673,7 @@ PHASE 2 — FIX. Only after you have added your diagnosis comment, decide on the
|
|
|
673
673
|
3. Gathering more information: if you could NOT determine the root cause from the available logs, propose patches that ADD logging statements to the relevant code paths so the next investigation has the information it needs.
|
|
674
674
|
Each patch file entry is { file, oldText, newText }: oldText must be copied exactly from the current file contents and should be unique within the file; it is replaced with newText. An empty oldText creates a new file. Relative file paths are resolved against the application repository (${process.cwd()}) first, then against the querysub repository (${QUERYSUB_ROOT}) — whichever contains the file. Absolute paths also work. Keep patches minimal and follow the style of the surrounding code.
|
|
675
675
|
|
|
676
|
-
When you are done, call mcp__autofixer__setTicketState with "code-change" if you proposed patches, or "not-a-bug" if the error should simply be ignored without any code change. If you are confused and just cannot figure out what is going on — the logs and code don't add up, and you can't even propose useful additional logging — add a comment explaining what you tried and what confused you, then call mcp__autofixer__setTicketState with "confused". Do NOT edit files directly — only propose changes through mcp__autofixer__addPatch.
|
|
676
|
+
When you are done, call mcp__autofixer__setTicketState with "code-change" if you proposed patches, or "not-a-bug" if the error should simply be ignored without any code change. If you diagnosed the problem but the fix cannot be expressed as a patch — it needs a design decision, a change outside these repositories, or manual intervention on a machine — add a comment saying exactly what a human has to do, then call mcp__autofixer__setTicketState with "manual-fix-required". If you are confused and just cannot figure out what is going on — the logs and code don't add up, and you can't even propose useful additional logging — add a comment explaining what you tried and what confused you, then call mcp__autofixer__setTicketState with "confused". Do NOT edit files directly — only propose changes through mcp__autofixer__addPatch.
|
|
677
677
|
|
|
678
678
|
==== TICKET ====
|
|
679
679
|
Title: ${ticket.title}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { LogDatum } from "../diskLogger";
|
|
2
2
|
|
|
3
|
-
export type TicketState = "investigation" | "code-change" | "fixed" | "not-a-bug" | "confused" | "timed-out";
|
|
4
|
-
export const TICKET_STATES: TicketState[] = ["investigation", "code-change", "fixed", "not-a-bug", "confused", "timed-out"];
|
|
3
|
+
export type TicketState = "investigation" | "code-change" | "manual-fix-required" | "fixed" | "not-a-bug" | "confused" | "timed-out";
|
|
4
|
+
export const TICKET_STATES: TicketState[] = ["investigation", "code-change", "manual-fix-required", "fixed", "not-a-bug", "confused", "timed-out"];
|
|
5
5
|
|
|
6
6
|
export const TICKET_FINAL_STATES: TicketState[] = ["fixed", "not-a-bug"];
|
|
7
7
|
export function isTicketFinished(state: TicketState): boolean {
|