@quireco/cli 0.0.9 → 0.0.11
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/README.md +20 -13
- package/dist/{cli-C2HyQykL.mjs → cli-CIBEsC75.mjs} +697 -114
- package/dist/index.mjs +1 -1
- package/dist/quire.mjs +1 -1
- package/package.json +2 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { n as __require, r as __toESM, t as __commonJSMin } from "./chunk-e9Ob2GDo.mjs";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import { createMain, defineCommand, parseArgs, renderUsage, runCommand, showUsage } from "citty";
|
|
4
|
-
import { isCancel, log, note, spinner, text } from "@clack/prompts";
|
|
4
|
+
import { isCancel, log, multiselect, note, spinner, text } from "@clack/prompts";
|
|
5
5
|
import { loginGitHubCopilot, loginOpenAICodex } from "@earendil-works/pi-ai/oauth";
|
|
6
6
|
import { AuthStorage, DefaultResourceLoader, ModelRegistry, SessionManager, SettingsManager, createAgentSession, defineTool, getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
7
7
|
import { exec, execFile, execFileSync, spawn } from "node:child_process";
|
|
@@ -722,7 +722,7 @@ function createQuireCreditsStream(options) {
|
|
|
722
722
|
const output = createEmptyAssistantMessage(model);
|
|
723
723
|
try {
|
|
724
724
|
const accessToken = streamOptions?.apiKey;
|
|
725
|
-
if (!accessToken) throw new CliError$1("Not logged in. Run `quire login` before `quire
|
|
725
|
+
if (!accessToken) throw new CliError$1("Not logged in. Run `quire login` before `quire run` so Quire Credits can be checked.", ExitCode.AuthFailure);
|
|
726
726
|
stream.push({
|
|
727
727
|
type: "start",
|
|
728
728
|
partial: output
|
|
@@ -753,7 +753,7 @@ function createQuireCreditsStream(options) {
|
|
|
753
753
|
}
|
|
754
754
|
async function readValidatedQuireCredentials(options) {
|
|
755
755
|
const credentials = await resolveAuthCredentials({ store: options.store });
|
|
756
|
-
if (!credentials) throw new CliError$1("Not logged in. Run `quire login` or set QUIRE_API_TOKEN before `quire
|
|
756
|
+
if (!credentials) throw new CliError$1("Not logged in. Run `quire login` or set QUIRE_API_TOKEN before `quire run` so Quire Credits can be checked.", ExitCode.AuthFailure);
|
|
757
757
|
if (credentials.tokenType === "QuireApiKey") return credentials;
|
|
758
758
|
const sessionLookup = await fetchCurrentSession({
|
|
759
759
|
apiBaseUrl: credentials.apiBaseUrl,
|
|
@@ -1410,6 +1410,9 @@ function runStatusPath(runPath) {
|
|
|
1410
1410
|
function runProgressPath(runPath) {
|
|
1411
1411
|
return join(resolve(runPath), "progress.jsonl");
|
|
1412
1412
|
}
|
|
1413
|
+
function runPlanPath(runPath) {
|
|
1414
|
+
return join(resolve(runPath), "plan.md");
|
|
1415
|
+
}
|
|
1413
1416
|
function runHandoffPath(runPath) {
|
|
1414
1417
|
return join(resolve(runPath), "handoff.md");
|
|
1415
1418
|
}
|
|
@@ -1418,6 +1421,7 @@ function runDirPaths(root) {
|
|
|
1418
1421
|
return {
|
|
1419
1422
|
status: runStatusPath(root),
|
|
1420
1423
|
progress: runProgressPath(root),
|
|
1424
|
+
plan: runPlanPath(root),
|
|
1421
1425
|
handoff: runHandoffPath(root),
|
|
1422
1426
|
evidence,
|
|
1423
1427
|
screenshots: join(evidence, "screenshots"),
|
|
@@ -1466,6 +1470,7 @@ function appendRunProgressEvent(runDir, event) {
|
|
|
1466
1470
|
seq
|
|
1467
1471
|
};
|
|
1468
1472
|
appendLineAtomically(runDir.paths.progress, `${stableJson(progressEvent)}\n`);
|
|
1473
|
+
if (progressEvent.kind === "plan" && progressEvent.plan !== void 0) writeRunPlan(runDir, progressEvent);
|
|
1469
1474
|
return progressEvent;
|
|
1470
1475
|
});
|
|
1471
1476
|
}
|
|
@@ -1499,6 +1504,45 @@ function patchRunStatusJson(runDir, patch) {
|
|
|
1499
1504
|
function runProgressEventId(seq) {
|
|
1500
1505
|
return `progress-${seq.toString().padStart(6, "0")}`;
|
|
1501
1506
|
}
|
|
1507
|
+
function writeRunPlan(runDir, event) {
|
|
1508
|
+
const plan = event.plan;
|
|
1509
|
+
if (plan === void 0) return;
|
|
1510
|
+
const lines = [
|
|
1511
|
+
"# Evidence plan",
|
|
1512
|
+
"",
|
|
1513
|
+
`Run: ${runDir.runId}`,
|
|
1514
|
+
`Plan: ${plan.id}`,
|
|
1515
|
+
`Recorded: ${event.ts}`,
|
|
1516
|
+
...plan.supersedes === void 0 ? [] : [`Supersedes: ${plan.supersedes}`],
|
|
1517
|
+
...plan.reason === void 0 ? [] : [`Reason: ${plan.reason}`],
|
|
1518
|
+
"",
|
|
1519
|
+
event.summary === void 0 ? void 0 : `## Summary\n\n${event.summary}\n`,
|
|
1520
|
+
"## Steps",
|
|
1521
|
+
"",
|
|
1522
|
+
...plan.steps.flatMap((step, index) => [
|
|
1523
|
+
`${index + 1}. **${step.title}** (${step.purpose})`,
|
|
1524
|
+
...step.expectedOutcome === void 0 ? [] : [` - Expected: ${step.expectedOutcome}`],
|
|
1525
|
+
...step.assertionIds === void 0 || step.assertionIds.length === 0 ? [] : [` - Assertions: ${step.assertionIds.join(", ")}`]
|
|
1526
|
+
]),
|
|
1527
|
+
""
|
|
1528
|
+
].filter((line) => line !== void 0);
|
|
1529
|
+
writeFileAtomically(runDir.paths.plan, `${lines.join("\n").trimEnd()}\n`, 384);
|
|
1530
|
+
patchRunStatusJson(runDir, {
|
|
1531
|
+
files: {
|
|
1532
|
+
status: "status.json",
|
|
1533
|
+
progress: "progress.jsonl",
|
|
1534
|
+
plan: "plan.md",
|
|
1535
|
+
handoff: "handoff.md",
|
|
1536
|
+
evidence: "evidence"
|
|
1537
|
+
},
|
|
1538
|
+
plan: {
|
|
1539
|
+
path: "plan.md",
|
|
1540
|
+
progressEventId: event.id,
|
|
1541
|
+
planId: plan.id,
|
|
1542
|
+
updatedAt: event.ts
|
|
1543
|
+
}
|
|
1544
|
+
});
|
|
1545
|
+
}
|
|
1502
1546
|
function readRepoSha(repoPath) {
|
|
1503
1547
|
try {
|
|
1504
1548
|
return execFileSync("git", [
|
|
@@ -1555,6 +1599,21 @@ function appendLineAtomically(filePath, line) {
|
|
|
1555
1599
|
closeSync(file);
|
|
1556
1600
|
}
|
|
1557
1601
|
}
|
|
1602
|
+
function writeFileAtomically(filePath, contents, mode) {
|
|
1603
|
+
mkdirSync(dirname(filePath), {
|
|
1604
|
+
recursive: true,
|
|
1605
|
+
mode: 448
|
|
1606
|
+
});
|
|
1607
|
+
const tempPath = join(dirname(filePath), `.${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}.${basename(filePath)}.tmp`);
|
|
1608
|
+
const file = openSync(tempPath, constants$1.O_CREAT | constants$1.O_EXCL | constants$1.O_WRONLY, mode);
|
|
1609
|
+
try {
|
|
1610
|
+
writeFileSync(file, contents, "utf8");
|
|
1611
|
+
fsyncSync(file);
|
|
1612
|
+
} finally {
|
|
1613
|
+
closeSync(file);
|
|
1614
|
+
}
|
|
1615
|
+
renameSync(tempPath, filePath);
|
|
1616
|
+
}
|
|
1558
1617
|
function nextJsonlSequence(filePath) {
|
|
1559
1618
|
try {
|
|
1560
1619
|
const file = openSync(filePath, constants$1.O_RDONLY);
|
|
@@ -1618,6 +1677,7 @@ function createRunStatus(runDir, input) {
|
|
|
1618
1677
|
files: {
|
|
1619
1678
|
status: "status.json",
|
|
1620
1679
|
progress: "progress.jsonl",
|
|
1680
|
+
plan: "plan.md",
|
|
1621
1681
|
handoff: "handoff.md",
|
|
1622
1682
|
evidence: "evidence"
|
|
1623
1683
|
},
|
|
@@ -1625,6 +1685,7 @@ function createRunStatus(runDir, input) {
|
|
|
1625
1685
|
status: input.status,
|
|
1626
1686
|
createdAt: timestamp,
|
|
1627
1687
|
updatedAt: timestamp,
|
|
1688
|
+
lastActivityAt: timestamp,
|
|
1628
1689
|
...input.status === "running" ? { startedAt: timestamp } : {},
|
|
1629
1690
|
...input.pid === void 0 ? {} : { pid: input.pid },
|
|
1630
1691
|
...input.error === void 0 ? {} : { error: input.error }
|
|
@@ -1648,6 +1709,22 @@ function updateRunStatus(runPath, patch) {
|
|
|
1648
1709
|
writeStatus(runStatusPath(runPath), status);
|
|
1649
1710
|
return status;
|
|
1650
1711
|
}
|
|
1712
|
+
function touchRunActivity(runPath, options = {}) {
|
|
1713
|
+
const existing = readRunStatus(runPath);
|
|
1714
|
+
if (existing === void 0) throw new CliError$1(`Run status not found: ${runPath}`, ExitCode.InvalidInput);
|
|
1715
|
+
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
1716
|
+
const minIntervalMs = options.minIntervalMs ?? 5e3;
|
|
1717
|
+
const previous = existing.lastActivityAt ?? existing.updatedAt;
|
|
1718
|
+
const previousTime = Date.parse(previous);
|
|
1719
|
+
if (Number.isFinite(previousTime) && now.getTime() - previousTime < minIntervalMs) return existing;
|
|
1720
|
+
const timestamp = now.toISOString();
|
|
1721
|
+
const status = {
|
|
1722
|
+
...existing,
|
|
1723
|
+
lastActivityAt: timestamp
|
|
1724
|
+
};
|
|
1725
|
+
writeStatus(runStatusPath(runPath), status);
|
|
1726
|
+
return status;
|
|
1727
|
+
}
|
|
1651
1728
|
function readRunStatus(runPath) {
|
|
1652
1729
|
const filePath = runStatusPath(runPath);
|
|
1653
1730
|
if (!existsSync(filePath)) return;
|
|
@@ -1765,6 +1842,7 @@ function parseRunStatus(value) {
|
|
|
1765
1842
|
status: value.status,
|
|
1766
1843
|
createdAt: value.createdAt,
|
|
1767
1844
|
updatedAt: value.updatedAt,
|
|
1845
|
+
...typeof value.lastActivityAt === "string" ? { lastActivityAt: value.lastActivityAt } : {},
|
|
1768
1846
|
...typeof value.pid === "number" ? { pid: value.pid } : {},
|
|
1769
1847
|
...typeof value.startedAt === "string" ? { startedAt: value.startedAt } : {},
|
|
1770
1848
|
...typeof value.completedAt === "string" ? { completedAt: value.completedAt } : {},
|
|
@@ -1772,6 +1850,7 @@ function parseRunStatus(value) {
|
|
|
1772
1850
|
...value.agent === void 0 ? {} : { agent: value.agent },
|
|
1773
1851
|
...value.request === void 0 ? {} : { request: value.request },
|
|
1774
1852
|
...value.metadata === void 0 ? {} : { metadata: value.metadata },
|
|
1853
|
+
...value.plan === void 0 ? {} : { plan: value.plan },
|
|
1775
1854
|
...value.harnessSession === void 0 ? {} : { harnessSession: value.harnessSession },
|
|
1776
1855
|
...value.finalHandoff === void 0 ? {} : { finalHandoff: value.finalHandoff },
|
|
1777
1856
|
...value.finalResult === void 0 ? {} : { finalResult: value.finalResult },
|
|
@@ -1783,12 +1862,14 @@ function parseRunStatusFiles(value) {
|
|
|
1783
1862
|
if (isRecord$14(value) && typeof value.status === "string" && typeof value.progress === "string" && typeof value.handoff === "string" && typeof value.evidence === "string") return {
|
|
1784
1863
|
status: value.status,
|
|
1785
1864
|
progress: value.progress,
|
|
1865
|
+
...typeof value.plan === "string" ? { plan: value.plan } : {},
|
|
1786
1866
|
handoff: value.handoff,
|
|
1787
1867
|
evidence: value.evidence
|
|
1788
1868
|
};
|
|
1789
1869
|
return {
|
|
1790
1870
|
status: "status.json",
|
|
1791
1871
|
progress: "progress.jsonl",
|
|
1872
|
+
plan: "plan.md",
|
|
1792
1873
|
handoff: "handoff.md",
|
|
1793
1874
|
evidence: "evidence"
|
|
1794
1875
|
};
|
|
@@ -2288,7 +2369,7 @@ const BrowserToolInputSchema = Type.Object({
|
|
|
2288
2369
|
argv: Type.Array(Type.String(), { minItems: 1 }),
|
|
2289
2370
|
cwd: Type.Optional(Type.String({ minLength: 1 }))
|
|
2290
2371
|
}, { additionalProperties: false });
|
|
2291
|
-
function makeBrowserTool({ runDir, sessionId, cwd: defaultCwd, headed = false, agentBrowserExecutableResolver = resolveAgentBrowserExecutable }) {
|
|
2372
|
+
function makeBrowserTool({ runDir, sessionId, cwd: defaultCwd, headed = false, onProgressEvent, agentBrowserExecutableResolver = resolveAgentBrowserExecutable }) {
|
|
2292
2373
|
const captureState = { videoStarted: false };
|
|
2293
2374
|
return createAgentCliTool({
|
|
2294
2375
|
name: "agent-browser",
|
|
@@ -2302,6 +2383,7 @@ function makeBrowserTool({ runDir, sessionId, cwd: defaultCwd, headed = false, a
|
|
|
2302
2383
|
defaultCwd,
|
|
2303
2384
|
headed,
|
|
2304
2385
|
captureState,
|
|
2386
|
+
onProgressEvent,
|
|
2305
2387
|
agentBrowserExecutableResolver,
|
|
2306
2388
|
input,
|
|
2307
2389
|
signal
|
|
@@ -2376,7 +2458,7 @@ async function runBestEffortBrowserCommand({ executable, args, cwd, timeoutMs })
|
|
|
2376
2458
|
function parseBrowserToolInput(input) {
|
|
2377
2459
|
return parseAgentCliToolInput(BrowserToolInputSchema, input, "agent-browser");
|
|
2378
2460
|
}
|
|
2379
|
-
async function runBrowserCommand({ runDir, sessionId, defaultCwd, headed, captureState, agentBrowserExecutableResolver, input, signal }) {
|
|
2461
|
+
async function runBrowserCommand({ runDir, sessionId, defaultCwd, headed, captureState, onProgressEvent, agentBrowserExecutableResolver, input, signal }) {
|
|
2380
2462
|
const [executable, ...args] = input.argv;
|
|
2381
2463
|
if (executable !== "agent-browser") throw new Error(`Refusing to spawn non-agent-browser executable: ${executable}`);
|
|
2382
2464
|
const cwd = input.cwd ?? defaultCwd ?? process.cwd();
|
|
@@ -2394,6 +2476,7 @@ async function runBrowserCommand({ runDir, sessionId, defaultCwd, headed, captur
|
|
|
2394
2476
|
cwd,
|
|
2395
2477
|
headed,
|
|
2396
2478
|
captureState,
|
|
2479
|
+
onProgressEvent,
|
|
2397
2480
|
agentBrowserExecutableResolver,
|
|
2398
2481
|
signal
|
|
2399
2482
|
});
|
|
@@ -2463,11 +2546,12 @@ function spawnAgentBrowser({ args, sessionId, cwd, agentBrowserExecutableResolve
|
|
|
2463
2546
|
});
|
|
2464
2547
|
});
|
|
2465
2548
|
}
|
|
2466
|
-
async function runPlannedBrowserCommands({ commands, runDir, sessionId, cwd, headed, captureState, agentBrowserExecutableResolver, signal }) {
|
|
2549
|
+
async function runPlannedBrowserCommands({ commands, runDir, sessionId, cwd, headed, captureState, onProgressEvent, agentBrowserExecutableResolver, signal }) {
|
|
2467
2550
|
const stdout = [];
|
|
2468
2551
|
const stderr = [];
|
|
2469
2552
|
for (const command of commands) {
|
|
2470
2553
|
const args = applyBrowserLaunchArgs(applyRunDefaults(applyHeadedMode(command.args, headed), runDir));
|
|
2554
|
+
const commandLabel = browserCommandLabel(args);
|
|
2471
2555
|
const result = await spawnAgentBrowser({
|
|
2472
2556
|
args,
|
|
2473
2557
|
sessionId,
|
|
@@ -2496,12 +2580,22 @@ async function runPlannedBrowserCommands({ commands, runDir, sessionId, cwd, hea
|
|
|
2496
2580
|
signal
|
|
2497
2581
|
}));
|
|
2498
2582
|
stderr.push(result.stderr);
|
|
2499
|
-
if (!result.ok)
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2583
|
+
if (!result.ok) {
|
|
2584
|
+
emitBrowserProgress(runDir, onProgressEvent, {
|
|
2585
|
+
kind: "action",
|
|
2586
|
+
status: "failed",
|
|
2587
|
+
title: `Browser: ${commandLabel}`,
|
|
2588
|
+
summary: oneLine(result.stderr || result.stdout || "agent-browser command failed"),
|
|
2589
|
+
importance: "normal",
|
|
2590
|
+
source: "runtime"
|
|
2591
|
+
});
|
|
2592
|
+
return {
|
|
2593
|
+
ok: false,
|
|
2594
|
+
exitCode: result.exitCode,
|
|
2595
|
+
stdout: stdout.join(""),
|
|
2596
|
+
stderr: stderr.join("")
|
|
2597
|
+
};
|
|
2598
|
+
}
|
|
2505
2599
|
}
|
|
2506
2600
|
return {
|
|
2507
2601
|
ok: true,
|
|
@@ -2510,6 +2604,24 @@ async function runPlannedBrowserCommands({ commands, runDir, sessionId, cwd, hea
|
|
|
2510
2604
|
stderr: stderr.join("")
|
|
2511
2605
|
};
|
|
2512
2606
|
}
|
|
2607
|
+
function emitBrowserProgress(runDir, onProgressEvent, event) {
|
|
2608
|
+
const logged = appendRunProgressEvent(runDir, event);
|
|
2609
|
+
onProgressEvent?.(logged);
|
|
2610
|
+
}
|
|
2611
|
+
function browserCommandLabel(args) {
|
|
2612
|
+
const command = args[0] ?? "command";
|
|
2613
|
+
if (command === "open") return `open ${redactBrowserArg(args[1] ?? "URL")}`;
|
|
2614
|
+
if (command === "batch") return "batch";
|
|
2615
|
+
return command;
|
|
2616
|
+
}
|
|
2617
|
+
function redactBrowserArg(value) {
|
|
2618
|
+
try {
|
|
2619
|
+
const url = new URL(value);
|
|
2620
|
+
return `${url.origin}${url.pathname}`;
|
|
2621
|
+
} catch {
|
|
2622
|
+
return value.length > 80 ? `${value.slice(0, 77)}…` : value;
|
|
2623
|
+
}
|
|
2624
|
+
}
|
|
2513
2625
|
function augmentBrowserArtifactOutput({ runDir, sessionId, cwd, args, result, agentBrowserExecutableResolver, signal }) {
|
|
2514
2626
|
if (!result.ok) return Promise.resolve(result.stdout);
|
|
2515
2627
|
const output = [result.stdout];
|
|
@@ -2605,27 +2717,35 @@ function hasOption$1(args, option) {
|
|
|
2605
2717
|
return args.some((arg) => arg === option || arg.startsWith(`${option}=`));
|
|
2606
2718
|
}
|
|
2607
2719
|
function shouldStartRecordingForCommand(args) {
|
|
2608
|
-
|
|
2720
|
+
const command = args[0];
|
|
2721
|
+
return command !== void 0 && RECORDING_START_COMMANDS.has(command);
|
|
2609
2722
|
}
|
|
2610
2723
|
function shouldCaptureScreenshotAfterCommand$1(args) {
|
|
2611
2724
|
const command = args[0];
|
|
2612
|
-
return command !== void 0 &&
|
|
2613
|
-
"close",
|
|
2614
|
-
"console",
|
|
2615
|
-
"cookies",
|
|
2616
|
-
"diff",
|
|
2617
|
-
"errors",
|
|
2618
|
-
"network",
|
|
2619
|
-
"pdf",
|
|
2620
|
-
"profiler",
|
|
2621
|
-
"record",
|
|
2622
|
-
"screenshot",
|
|
2623
|
-
"set",
|
|
2624
|
-
"skills",
|
|
2625
|
-
"storage",
|
|
2626
|
-
"trace"
|
|
2627
|
-
].includes(command);
|
|
2725
|
+
return command !== void 0 && AUTO_SCREENSHOT_COMMANDS.has(command);
|
|
2628
2726
|
}
|
|
2727
|
+
const RECORDING_START_COMMANDS = new Set([
|
|
2728
|
+
"back",
|
|
2729
|
+
"check",
|
|
2730
|
+
"click",
|
|
2731
|
+
"dblclick",
|
|
2732
|
+
"drag",
|
|
2733
|
+
"fill",
|
|
2734
|
+
"forward",
|
|
2735
|
+
"open",
|
|
2736
|
+
"press",
|
|
2737
|
+
"reload",
|
|
2738
|
+
"select",
|
|
2739
|
+
"type",
|
|
2740
|
+
"uncheck",
|
|
2741
|
+
"upload"
|
|
2742
|
+
]);
|
|
2743
|
+
const AUTO_SCREENSHOT_COMMANDS = new Set([
|
|
2744
|
+
"back",
|
|
2745
|
+
"forward",
|
|
2746
|
+
"open",
|
|
2747
|
+
"reload"
|
|
2748
|
+
]);
|
|
2629
2749
|
//#endregion
|
|
2630
2750
|
//#region src/run-artifacts.ts
|
|
2631
2751
|
function relativeRunPath(runDir, path) {
|
|
@@ -2670,14 +2790,19 @@ function publicRunProgressEvent(runDir, event) {
|
|
|
2670
2790
|
//#region src/pipeline/handoff-materializer.ts
|
|
2671
2791
|
function materializeHandoff({ handoff, runDir, continuation, modelUsage, metadata, harness }) {
|
|
2672
2792
|
const result = toInvestigationResult(handoff, runDir, continuation, modelUsage);
|
|
2673
|
-
const
|
|
2793
|
+
const materializedHandoff = materializeHandoffMarkdown(handoff, result.evidence);
|
|
2794
|
+
const materializedResult = {
|
|
2795
|
+
...result,
|
|
2796
|
+
handoffMarkdown: materializedHandoff.handoffMarkdown
|
|
2797
|
+
};
|
|
2798
|
+
const nextMetadata = updateMetadataWithHandoff(runDir, metadata, materializedHandoff, materializedResult, harness);
|
|
2674
2799
|
writeRunFinalHandoff(runDir, {
|
|
2675
|
-
handoff,
|
|
2676
|
-
result
|
|
2800
|
+
handoff: materializedHandoff,
|
|
2801
|
+
result: materializedResult
|
|
2677
2802
|
});
|
|
2678
|
-
writeHandoffArtifact(runDir,
|
|
2803
|
+
writeHandoffArtifact(runDir, materializedHandoff);
|
|
2679
2804
|
return {
|
|
2680
|
-
result,
|
|
2805
|
+
result: materializedResult,
|
|
2681
2806
|
metadata: nextMetadata
|
|
2682
2807
|
};
|
|
2683
2808
|
}
|
|
@@ -2714,13 +2839,20 @@ function toInvestigationResult(handoff, runDir, continuation, modelUsage) {
|
|
|
2714
2839
|
}
|
|
2715
2840
|
function appendAutoCapturedEvidence(evidence, runDir) {
|
|
2716
2841
|
const seenPaths = new Set(evidence.flatMap((item) => item.path === void 0 ? [] : [item.path]));
|
|
2717
|
-
const autoEvidence = [...readAutoCapturedFiles(runDir.paths.screenshots, runDir, "screenshot"), ...readAutoCapturedFiles(runDir.paths.video, runDir, "recording")].filter((item) => {
|
|
2842
|
+
const autoEvidence = [...selectAutoScreenshotEvidence(evidence, readAutoCapturedFiles(runDir.paths.screenshots, runDir, "screenshot")), ...readAutoCapturedFiles(runDir.paths.video, runDir, "recording")].filter((item) => {
|
|
2718
2843
|
if (item.path === void 0 || seenPaths.has(item.path)) return false;
|
|
2719
2844
|
seenPaths.add(item.path);
|
|
2720
2845
|
return true;
|
|
2721
2846
|
});
|
|
2722
2847
|
return [...evidence, ...autoEvidence];
|
|
2723
2848
|
}
|
|
2849
|
+
function selectAutoScreenshotEvidence(handoffEvidence, autoScreenshots) {
|
|
2850
|
+
if (handoffEvidence.some((item) => item.kind === "screenshot")) return [];
|
|
2851
|
+
if (autoScreenshots.length <= 2) return autoScreenshots;
|
|
2852
|
+
const first = autoScreenshots[0];
|
|
2853
|
+
const last = autoScreenshots[autoScreenshots.length - 1];
|
|
2854
|
+
return first === void 0 || last === void 0 || first.path === last.path ? autoScreenshots.slice(0, 1) : [first, last];
|
|
2855
|
+
}
|
|
2724
2856
|
function readAutoCapturedFiles(directory, runDir, kind) {
|
|
2725
2857
|
if (!existsSync(directory)) return [];
|
|
2726
2858
|
try {
|
|
@@ -2806,6 +2938,86 @@ function confidenceFor(handoff, evidence) {
|
|
|
2806
2938
|
if (handoff.status !== "reproduced" || evidence.length === 0) return "low";
|
|
2807
2939
|
return evidence.length >= 2 ? "high" : "medium";
|
|
2808
2940
|
}
|
|
2941
|
+
const EVIDENCE_SECTION_START = "<!-- quire:evidence-start -->";
|
|
2942
|
+
const EVIDENCE_SECTION_END = "<!-- quire:evidence-end -->";
|
|
2943
|
+
function materializeHandoffMarkdown(handoff, evidence) {
|
|
2944
|
+
const evidenceSection = renderEvidenceSection(evidence);
|
|
2945
|
+
const handoffMarkdown = withGeneratedEvidenceSection(handoff.handoffMarkdown, evidenceSection);
|
|
2946
|
+
return {
|
|
2947
|
+
...handoff,
|
|
2948
|
+
handoffMarkdown,
|
|
2949
|
+
evidenceRefs: evidence.map((item) => ({
|
|
2950
|
+
kind: item.kind,
|
|
2951
|
+
note: item.note,
|
|
2952
|
+
...item.path === void 0 ? {} : { path: item.path },
|
|
2953
|
+
...item.url === void 0 ? {} : { url: item.url }
|
|
2954
|
+
}))
|
|
2955
|
+
};
|
|
2956
|
+
}
|
|
2957
|
+
function withGeneratedEvidenceSection(markdown, evidenceSection) {
|
|
2958
|
+
const withoutExisting = markdown.trimEnd().replace(new RegExp(`\\n*${escapeRegExp$1(EVIDENCE_SECTION_START)}[\\s\\S]*?${escapeRegExp$1(EVIDENCE_SECTION_END)}\\s*$`), "");
|
|
2959
|
+
return evidenceSection.length === 0 ? withoutExisting : `${withoutExisting}\n\n${evidenceSection}`;
|
|
2960
|
+
}
|
|
2961
|
+
function renderEvidenceSection(evidence) {
|
|
2962
|
+
const publicEvidence = evidence.filter((item) => item.path !== void 0 || item.url !== void 0);
|
|
2963
|
+
if (publicEvidence.length === 0) return "";
|
|
2964
|
+
return [
|
|
2965
|
+
EVIDENCE_SECTION_START,
|
|
2966
|
+
"## Evidence artifacts",
|
|
2967
|
+
"",
|
|
2968
|
+
...publicEvidence.flatMap(renderEvidenceItem),
|
|
2969
|
+
EVIDENCE_SECTION_END
|
|
2970
|
+
].join("\n");
|
|
2971
|
+
}
|
|
2972
|
+
function renderEvidenceItem(item) {
|
|
2973
|
+
const label = `${evidenceDisplayKind(item)} — ${item.note}`;
|
|
2974
|
+
const target = evidenceTarget(item);
|
|
2975
|
+
if (target === void 0) return [];
|
|
2976
|
+
if (isImageEvidence(item)) return [
|
|
2977
|
+
`- ${label}`,
|
|
2978
|
+
` `,
|
|
2979
|
+
""
|
|
2980
|
+
];
|
|
2981
|
+
if (isVideoEvidence(item)) return [
|
|
2982
|
+
`- ${label}`,
|
|
2983
|
+
` <video controls src="${escapeHtmlAttribute(target)}"></video>`,
|
|
2984
|
+
` [Open recording](${target})`,
|
|
2985
|
+
""
|
|
2986
|
+
];
|
|
2987
|
+
return [
|
|
2988
|
+
`- ${label}`,
|
|
2989
|
+
` [Open artifact](${target})`,
|
|
2990
|
+
""
|
|
2991
|
+
];
|
|
2992
|
+
}
|
|
2993
|
+
function evidenceDisplayKind(item) {
|
|
2994
|
+
const kind = item.kind.replaceAll("_", " ").trim();
|
|
2995
|
+
return kind.length === 0 ? "Evidence" : `${kind[0]?.toUpperCase() ?? "E"}${kind.slice(1)}`;
|
|
2996
|
+
}
|
|
2997
|
+
function evidenceTarget(item) {
|
|
2998
|
+
if (item.url !== void 0) return item.url;
|
|
2999
|
+
if (item.path === void 0) return;
|
|
3000
|
+
return `./${item.path.replace(/^\.\//, "")}`;
|
|
3001
|
+
}
|
|
3002
|
+
function isImageEvidence(item) {
|
|
3003
|
+
const path = item.path?.toLowerCase() ?? "";
|
|
3004
|
+
const kind = item.kind.toLowerCase();
|
|
3005
|
+
return kind.includes("screenshot") || kind.includes("image") || path.endsWith(".png") || path.endsWith(".jpg") || path.endsWith(".jpeg") || path.endsWith(".webp") || path.endsWith(".gif");
|
|
3006
|
+
}
|
|
3007
|
+
function isVideoEvidence(item) {
|
|
3008
|
+
const path = item.path?.toLowerCase() ?? "";
|
|
3009
|
+
const kind = item.kind.toLowerCase();
|
|
3010
|
+
return kind.includes("recording") || kind.includes("video") || path.endsWith(".webm") || path.endsWith(".mp4");
|
|
3011
|
+
}
|
|
3012
|
+
function escapeMarkdownAlt(value) {
|
|
3013
|
+
return value.replace(/[[\]\\]/g, " ").replace(/\s+/g, " ").trim();
|
|
3014
|
+
}
|
|
3015
|
+
function escapeHtmlAttribute(value) {
|
|
3016
|
+
return value.replaceAll("&", "&").replaceAll("\"", """).replaceAll("<", "<").replaceAll(">", ">");
|
|
3017
|
+
}
|
|
3018
|
+
function escapeRegExp$1(value) {
|
|
3019
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3020
|
+
}
|
|
2809
3021
|
function updateMetadataWithHandoff(runDir, metadata, handoff, result, harness) {
|
|
2810
3022
|
const sessionState = readRunSessionState(runDir);
|
|
2811
3023
|
return writeRunMetadata(runDir, {
|
|
@@ -41110,11 +41322,11 @@ function errorMessage$2(error) {
|
|
|
41110
41322
|
}
|
|
41111
41323
|
//#endregion
|
|
41112
41324
|
//#region ../../packages/agent-mobile/src/wait/android.ts
|
|
41113
|
-
const DEFAULT_TIMEOUT_MS$
|
|
41325
|
+
const DEFAULT_TIMEOUT_MS$2 = 1e4;
|
|
41114
41326
|
const POLL_INTERVAL_MS$1 = 400;
|
|
41115
41327
|
async function waitAndroid(request) {
|
|
41116
41328
|
const waitFor = request.waitFor ?? { kind: "settle" };
|
|
41117
|
-
const timeoutMs = waitFor.timeoutMs ?? DEFAULT_TIMEOUT_MS$
|
|
41329
|
+
const timeoutMs = waitFor.timeoutMs ?? DEFAULT_TIMEOUT_MS$2;
|
|
41118
41330
|
if (waitFor.kind === "settle") {
|
|
41119
41331
|
await waitForSettle$1(request, waitFor, timeoutMs);
|
|
41120
41332
|
return;
|
|
@@ -41192,11 +41404,11 @@ function sleep$1(ms) {
|
|
|
41192
41404
|
}
|
|
41193
41405
|
//#endregion
|
|
41194
41406
|
//#region ../../packages/agent-mobile/src/wait/ios.ts
|
|
41195
|
-
const DEFAULT_TIMEOUT_MS = 1e4;
|
|
41407
|
+
const DEFAULT_TIMEOUT_MS$1 = 1e4;
|
|
41196
41408
|
const POLL_INTERVAL_MS = 400;
|
|
41197
41409
|
async function waitIos(request) {
|
|
41198
41410
|
const waitFor = request.waitFor ?? { kind: "settle" };
|
|
41199
|
-
const timeoutMs = waitFor.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
41411
|
+
const timeoutMs = waitFor.timeoutMs ?? DEFAULT_TIMEOUT_MS$1;
|
|
41200
41412
|
if (waitFor.kind === "settle") {
|
|
41201
41413
|
await waitForSettle(request, waitFor, timeoutMs);
|
|
41202
41414
|
return;
|
|
@@ -44646,48 +44858,293 @@ function shouldCaptureScreenshotAfterCommand(command) {
|
|
|
44646
44858
|
return command !== void 0 && !["install", "screenshot"].includes(command);
|
|
44647
44859
|
}
|
|
44648
44860
|
//#endregion
|
|
44861
|
+
//#region src/tools/shell-check.ts
|
|
44862
|
+
const DEFAULT_TIMEOUT_MS = 12e4;
|
|
44863
|
+
const MAX_OUTPUT_CHARS = 6e4;
|
|
44864
|
+
const ShellCheckToolInputSchema = Type.Object({
|
|
44865
|
+
command: Type.String({ minLength: 1 }),
|
|
44866
|
+
cwd: Type.Optional(Type.String({ minLength: 1 })),
|
|
44867
|
+
timeoutMs: Type.Optional(Type.Number({
|
|
44868
|
+
minimum: 1e3,
|
|
44869
|
+
maximum: 6e5
|
|
44870
|
+
})),
|
|
44871
|
+
reason: Type.String({ minLength: 1 })
|
|
44872
|
+
}, { additionalProperties: false });
|
|
44873
|
+
function makeShellCheckTool({ runDir, cwd: defaultCwd, env = process.env }) {
|
|
44874
|
+
return createAgentCliTool({
|
|
44875
|
+
name: "run_shell_check",
|
|
44876
|
+
description: "Run a project shell command for evidence collection, local app startup checks, tests, typechecks, builds, lint/check commands, or source inspection. Clearly dangerous, destructive, deploy, install, database mutation, and external write commands are blocked.",
|
|
44877
|
+
parameters: ShellCheckToolInputSchema,
|
|
44878
|
+
parseInput: parseShellCheckToolInput,
|
|
44879
|
+
runCommand(input, signal) {
|
|
44880
|
+
return runShellCheckCommand({
|
|
44881
|
+
runDir,
|
|
44882
|
+
defaultCwd,
|
|
44883
|
+
env,
|
|
44884
|
+
input,
|
|
44885
|
+
signal
|
|
44886
|
+
});
|
|
44887
|
+
}
|
|
44888
|
+
});
|
|
44889
|
+
}
|
|
44890
|
+
function parseShellCheckToolInput(input) {
|
|
44891
|
+
return parseAgentCliToolInput(ShellCheckToolInputSchema, input, "run_shell_check");
|
|
44892
|
+
}
|
|
44893
|
+
async function runShellCheckCommand({ runDir, defaultCwd, env, input, signal }) {
|
|
44894
|
+
const command = input.command.trim();
|
|
44895
|
+
const policy = classifyShellCheckCommand(command);
|
|
44896
|
+
if (!policy.allowed) return {
|
|
44897
|
+
ok: false,
|
|
44898
|
+
exitCode: null,
|
|
44899
|
+
stdout: "",
|
|
44900
|
+
stderr: `Blocked by Quire shell safety policy: ${policy.reason}`,
|
|
44901
|
+
blocked: true,
|
|
44902
|
+
reason: policy.reason
|
|
44903
|
+
};
|
|
44904
|
+
const cwd = input.cwd ?? defaultCwd ?? process.cwd();
|
|
44905
|
+
const timeoutMs = input.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
44906
|
+
const result = await spawnShell(command, {
|
|
44907
|
+
cwd,
|
|
44908
|
+
env,
|
|
44909
|
+
timeoutMs,
|
|
44910
|
+
signal
|
|
44911
|
+
});
|
|
44912
|
+
const artifactPath = writeShellArtifact(runDir, {
|
|
44913
|
+
command,
|
|
44914
|
+
cwd,
|
|
44915
|
+
reason: input.reason,
|
|
44916
|
+
timeoutMs,
|
|
44917
|
+
...result
|
|
44918
|
+
});
|
|
44919
|
+
const output = {
|
|
44920
|
+
exitCode: result.exitCode,
|
|
44921
|
+
stdout: truncateOutput(redactSecrets(result.stdout)),
|
|
44922
|
+
stderr: `${truncateOutput(redactSecrets(result.stderr))}\n[quire:artifact] shell_check saved to ${artifactPath}\n`.trimStart(),
|
|
44923
|
+
artifactPath
|
|
44924
|
+
};
|
|
44925
|
+
return result.exitCode === 0 ? {
|
|
44926
|
+
ok: true,
|
|
44927
|
+
...output,
|
|
44928
|
+
exitCode: 0
|
|
44929
|
+
} : {
|
|
44930
|
+
ok: false,
|
|
44931
|
+
...output
|
|
44932
|
+
};
|
|
44933
|
+
}
|
|
44934
|
+
function classifyShellCheckCommand(command) {
|
|
44935
|
+
const trimmed = command.trim();
|
|
44936
|
+
if (trimmed.length === 0) return {
|
|
44937
|
+
allowed: false,
|
|
44938
|
+
reason: "command is empty"
|
|
44939
|
+
};
|
|
44940
|
+
if (/[;&|`$<>]/.test(trimmed)) return {
|
|
44941
|
+
allowed: false,
|
|
44942
|
+
reason: "shell operators, pipes, redirects, and command substitution are not allowed"
|
|
44943
|
+
};
|
|
44944
|
+
const words = splitSimpleCommand(trimmed);
|
|
44945
|
+
if (words === void 0 || words.length === 0) return {
|
|
44946
|
+
allowed: false,
|
|
44947
|
+
reason: "command could not be parsed as a simple command"
|
|
44948
|
+
};
|
|
44949
|
+
const dangerous = firstDangerousToken(words.map((word) => word.toLowerCase()));
|
|
44950
|
+
if (dangerous !== void 0) return {
|
|
44951
|
+
allowed: false,
|
|
44952
|
+
reason: `contains unsafe token ${JSON.stringify(dangerous)}`
|
|
44953
|
+
};
|
|
44954
|
+
return { allowed: true };
|
|
44955
|
+
}
|
|
44956
|
+
function splitSimpleCommand(command) {
|
|
44957
|
+
const words = [];
|
|
44958
|
+
const pattern = /"([^"\\]*(?:\\.[^"\\]*)*)"|'([^']*)'|(\S+)/g;
|
|
44959
|
+
let match;
|
|
44960
|
+
let consumed = "";
|
|
44961
|
+
while ((match = pattern.exec(command)) !== null) {
|
|
44962
|
+
consumed += match[0];
|
|
44963
|
+
words.push(match[1] ?? match[2] ?? match[3] ?? "");
|
|
44964
|
+
}
|
|
44965
|
+
if (words.length === 0 || consumed.replace(/\s+/g, "") !== command.replace(/\s+/g, "")) return;
|
|
44966
|
+
return words;
|
|
44967
|
+
}
|
|
44968
|
+
const DANGEROUS_TOKENS = new Set([
|
|
44969
|
+
"rm",
|
|
44970
|
+
"rmdir",
|
|
44971
|
+
"mv",
|
|
44972
|
+
"cp",
|
|
44973
|
+
"sudo",
|
|
44974
|
+
"chmod",
|
|
44975
|
+
"chown",
|
|
44976
|
+
"kill",
|
|
44977
|
+
"pkill",
|
|
44978
|
+
"reset",
|
|
44979
|
+
"restore",
|
|
44980
|
+
"clean",
|
|
44981
|
+
"checkout",
|
|
44982
|
+
"commit",
|
|
44983
|
+
"push",
|
|
44984
|
+
"publish",
|
|
44985
|
+
"deploy",
|
|
44986
|
+
"release",
|
|
44987
|
+
"install",
|
|
44988
|
+
"add",
|
|
44989
|
+
"remove",
|
|
44990
|
+
"unlink",
|
|
44991
|
+
"migrate",
|
|
44992
|
+
"seed",
|
|
44993
|
+
"drop",
|
|
44994
|
+
"truncate",
|
|
44995
|
+
"delete",
|
|
44996
|
+
"insert",
|
|
44997
|
+
"update",
|
|
44998
|
+
"create",
|
|
44999
|
+
"curl",
|
|
45000
|
+
"wget",
|
|
45001
|
+
"ssh",
|
|
45002
|
+
"scp",
|
|
45003
|
+
"rsync"
|
|
45004
|
+
]);
|
|
45005
|
+
function firstDangerousToken(words) {
|
|
45006
|
+
return words.find((word) => DANGEROUS_TOKENS.has(word));
|
|
45007
|
+
}
|
|
45008
|
+
async function spawnShell(command, { cwd, env, timeoutMs, signal }) {
|
|
45009
|
+
return await new Promise((resolve) => {
|
|
45010
|
+
let stdout = "";
|
|
45011
|
+
let stderr = "";
|
|
45012
|
+
let settled = false;
|
|
45013
|
+
const child = spawn("/bin/sh", ["-lc", command], {
|
|
45014
|
+
cwd,
|
|
45015
|
+
env,
|
|
45016
|
+
stdio: [
|
|
45017
|
+
"ignore",
|
|
45018
|
+
"pipe",
|
|
45019
|
+
"pipe"
|
|
45020
|
+
]
|
|
45021
|
+
});
|
|
45022
|
+
const settle = (exitCode, extraStderr = "") => {
|
|
45023
|
+
if (settled) return;
|
|
45024
|
+
settled = true;
|
|
45025
|
+
clearTimeout(timer);
|
|
45026
|
+
signal?.removeEventListener("abort", abort);
|
|
45027
|
+
resolve({
|
|
45028
|
+
exitCode,
|
|
45029
|
+
stdout,
|
|
45030
|
+
stderr: `${stderr}${extraStderr}`
|
|
45031
|
+
});
|
|
45032
|
+
};
|
|
45033
|
+
const abort = () => {
|
|
45034
|
+
child.kill("SIGTERM");
|
|
45035
|
+
settle(null, "\nCommand aborted.\n");
|
|
45036
|
+
};
|
|
45037
|
+
const timer = setTimeout(() => {
|
|
45038
|
+
child.kill("SIGTERM");
|
|
45039
|
+
settle(null, `\nCommand timed out after ${timeoutMs}ms.\n`);
|
|
45040
|
+
}, timeoutMs);
|
|
45041
|
+
signal?.addEventListener("abort", abort);
|
|
45042
|
+
child.stdout.on("data", (chunk) => {
|
|
45043
|
+
stdout += chunk.toString("utf8");
|
|
45044
|
+
});
|
|
45045
|
+
child.stderr.on("data", (chunk) => {
|
|
45046
|
+
stderr += chunk.toString("utf8");
|
|
45047
|
+
});
|
|
45048
|
+
child.on("error", (error) => settle(null, `\n${errorMessage$5(error)}\n`));
|
|
45049
|
+
child.on("close", (code) => settle(code));
|
|
45050
|
+
});
|
|
45051
|
+
}
|
|
45052
|
+
function writeShellArtifact(runDir, result) {
|
|
45053
|
+
mkdirSync(runDir.paths.trace, {
|
|
45054
|
+
recursive: true,
|
|
45055
|
+
mode: 448
|
|
45056
|
+
});
|
|
45057
|
+
const artifactPath = join(runDir.paths.trace, `shell-check-${Date.now().toString(36)}-${safeArtifactName(result.command.split(/\s+/)[0] ?? "command")}.txt`);
|
|
45058
|
+
writeFileSync(artifactPath, [
|
|
45059
|
+
`$ ${result.command}`,
|
|
45060
|
+
`cwd: ${result.cwd}`,
|
|
45061
|
+
`reason: ${result.reason}`,
|
|
45062
|
+
`timeoutMs: ${result.timeoutMs}`,
|
|
45063
|
+
`exitCode: ${result.exitCode ?? "null"}`,
|
|
45064
|
+
"",
|
|
45065
|
+
"--- stdout ---",
|
|
45066
|
+
redactSecrets(result.stdout),
|
|
45067
|
+
"",
|
|
45068
|
+
"--- stderr ---",
|
|
45069
|
+
redactSecrets(result.stderr)
|
|
45070
|
+
].join("\n"), { mode: 384 });
|
|
45071
|
+
return artifactPath;
|
|
45072
|
+
}
|
|
45073
|
+
function truncateOutput(value) {
|
|
45074
|
+
if (value.length <= MAX_OUTPUT_CHARS) return value;
|
|
45075
|
+
return `${value.slice(0, MAX_OUTPUT_CHARS)}\n[quire:truncated] output truncated to ${MAX_OUTPUT_CHARS} characters\n`;
|
|
45076
|
+
}
|
|
45077
|
+
function redactSecrets(value) {
|
|
45078
|
+
return value.replace(/([A-Za-z_][A-Za-z0-9_]*(?:TOKEN|SECRET|PASSWORD|KEY)[A-Za-z0-9_]*=)([^\s]+)/gi, "$1[REDACTED]").replace(/(Bearer\s+)[A-Za-z0-9._~+/=-]+/gi, "$1[REDACTED]");
|
|
45079
|
+
}
|
|
45080
|
+
//#endregion
|
|
45081
|
+
//#region src/pipeline/prompts/local-run-safety-contract.ts
|
|
45082
|
+
const localRunSafetyContractPrompt = `Local Quire runtime contract:
|
|
45083
|
+
|
|
45084
|
+
- Obey the enabled tool list exactly; do not invent unavailable tools or capabilities.
|
|
45085
|
+
- Code inspection tools are read-only. Do not edit files, install dependencies, start app servers, reset repositories, or mutate user data.
|
|
45086
|
+
- Do not expose hidden reasoning, raw prompts, raw completions, provider credentials, API tokens, or secrets.
|
|
45087
|
+
- record_progress writes the user-visible progress.jsonl timeline. write_handoff is the required final action. The durable public product artifacts are progress.jsonl, handoff.md, and referenced evidence. Private harness sessions under .harness-sessions are diagnostic implementation state.`;
|
|
45088
|
+
//#endregion
|
|
45089
|
+
//#region src/pipeline/prompts/system-instructions.ts
|
|
45090
|
+
const systemInstructionsPrompt = `You are Quire, a read-only software run agent for investigation, verification, triage, explanation, and project-specific procedures.
|
|
45091
|
+
|
|
45092
|
+
Interpret the run brief first. Infer the user's intent naturally; do not require the user to choose a mode.
|
|
45093
|
+
|
|
45094
|
+
Resolve the target, procedure, and blockers from the user request, caller context, project runbook, available tools, and loaded skills.
|
|
45095
|
+
|
|
45096
|
+
Do not assume the run is web, mobile, or code-only before reading the brief. Use browser or mobile tools only when they are available and relevant.
|
|
45097
|
+
|
|
45098
|
+
When a tool provides its own skills, workflows, or usage guides, load the relevant guidance before using that tool for substantive work. For example, load the relevant \`agent-browser skills get ...\` content before browser-backed QA, exploration, or interaction.
|
|
45099
|
+
|
|
45100
|
+
Use read, grep, find, and ls for read-only repository inspection when code can clarify routes, screens, feature names, state requirements, verification commands, or likely causes.
|
|
45101
|
+
|
|
45102
|
+
Code inspection can guide exploration, but product behavior claims need direct evidence from the requested surface when the run requires browser or mobile observation.
|
|
45103
|
+
|
|
45104
|
+
Before broad exploration or target interaction, call record_progress with kind plan and a concise grounded plan.
|
|
45105
|
+
|
|
45106
|
+
Call write_handoff exactly once when the run is complete or blocked.`;
|
|
45107
|
+
//#endregion
|
|
44649
45108
|
//#region src/pipeline/run-instructions.ts
|
|
44650
|
-
const DEFAULT_RUN_INSTRUCTIONS_ID = "local-run-2026-06-
|
|
45109
|
+
const DEFAULT_RUN_INSTRUCTIONS_ID = "local-run-2026-06-06";
|
|
44651
45110
|
const DEFAULT_REMOTE_RUN_ADDONS_ID = "local-none";
|
|
44652
|
-
const LOCAL_RUN_SAFETY_CONTRACT =
|
|
44653
|
-
|
|
44654
|
-
"- Obey the enabled tool list exactly; do not invent unavailable tools or capabilities.",
|
|
44655
|
-
"- Code inspection tools are read-only. Do not edit files, install dependencies, start app servers, reset repositories, or mutate user data.",
|
|
44656
|
-
"- Do not expose hidden reasoning, raw prompts, raw completions, provider credentials, API tokens, or secrets.",
|
|
44657
|
-
"- record_progress writes the user-visible progress.jsonl timeline. write_handoff is the required final action. The durable public product artifacts are progress.jsonl, handoff.md, and referenced evidence. Private harness sessions under .harness-sessions are diagnostic implementation state."
|
|
44658
|
-
].join("\n");
|
|
44659
|
-
const SYSTEM_INSTRUCTIONS = [
|
|
44660
|
-
"You are Quire, a read-only software run agent for investigation, verification, triage, explanation, and project-specific procedures.",
|
|
44661
|
-
"Interpret the run brief first. Infer the user's intent naturally; do not require the user to choose a mode.",
|
|
44662
|
-
"Resolve the target, procedure, and blockers from the user request, caller context, project runbook, available tools, and loaded skills.",
|
|
44663
|
-
"Do not assume the run is web, mobile, or code-only before reading the brief. Use browser or mobile tools only when they are available and relevant.",
|
|
44664
|
-
"Use read, grep, find, and ls for read-only repository inspection when code can clarify routes, screens, feature names, state requirements, verification commands, or likely causes.",
|
|
44665
|
-
"Code inspection can guide exploration, but product behavior claims need direct evidence from the requested surface when the run requires browser or mobile observation.",
|
|
44666
|
-
"Before broad exploration or target interaction, call record_progress with kind plan and a concise grounded plan.",
|
|
44667
|
-
"Call write_handoff exactly once when the run is complete or blocked."
|
|
44668
|
-
].join("\n");
|
|
45111
|
+
const LOCAL_RUN_SAFETY_CONTRACT = localRunSafetyContractPrompt.trim();
|
|
45112
|
+
const SYSTEM_INSTRUCTIONS = systemInstructionsPrompt.trim();
|
|
44669
45113
|
const COMMON_GUIDANCE = [
|
|
44670
45114
|
"Start by understanding the user request, inferred intent, caller context, runtime capabilities, and project runbook status.",
|
|
44671
45115
|
"Read .quire/runbook.md when target context, verification procedures, deployments, local URLs, devices, accounts, or safety conventions are needed.",
|
|
44672
45116
|
"If the runbook is missing or insufficient, report the smallest missing context instead of guessing ports, app IDs, accounts, or deployment names.",
|
|
44673
45117
|
"Prefer deterministic setup scripts or skills from the runbook over manually repeating login, seed-data, runner, or browser-state setup.",
|
|
45118
|
+
"Use loaded skills as optional QA lenses, not mandatory modes. Select the lenses relevant to the request, target, and available evidence.",
|
|
45119
|
+
"Verification can target browser apps, CLI tools, APIs, mobile apps, docs, static sites, or code paths. Choose the relevant lenses and tools for the target rather than forcing every run through browser UI checks.",
|
|
45120
|
+
"When claiming accessibility, design, responsive, link/form, workflow, CLI, API, mobile, command, or smoke verification, state the evidence boundary. If the boundary was not exercised, mark the check untested or blocked instead of passing it.",
|
|
44674
45121
|
"For direct product tasks, perform the requested workflow and report what happened rather than reframing it as a bug.",
|
|
44675
45122
|
"For bug reports, reproduce or rule out the reported behavior honestly with concrete evidence.",
|
|
45123
|
+
"When the target is a localhost or dev-server URL, classify framework toolbars, debug panels, dev-only overlays, hot-reload artifacts, and transient local dependency noise as local-dev caveats unless they block the requested check or clearly affect the built/preview/production surface.",
|
|
45124
|
+
"Separate product findings from environment caveats: a broken CTA, inaccessible interaction, or weak visual hierarchy is a product finding; an Astro/Vite/dev toolbar covering a screenshot, a DEV-gated tuning panel, or a local-only dependency fetch warning is an environment caveat unless it reproduces outside dev.",
|
|
45125
|
+
"If local dev caveats obstruct visual evidence or runtime confidence and the request needs production-like assurance, recommend or perform a preview/production-build check when safe and available. Otherwise report the caveat without treating it as a user-facing failure.",
|
|
44676
45126
|
"Stay within the target product, URLs, devices, commands, and systems named by the request, caller context, or runbook."
|
|
44677
45127
|
];
|
|
44678
45128
|
const PLANNING_GUIDANCE = [
|
|
44679
45129
|
"For any non-trivial run, record a plan before taking broad exploratory actions or interacting with browser/mobile targets.",
|
|
45130
|
+
"The latest structured plan is materialized to plan.md in the run directory for humans and caller agents. Make it useful as an evidence plan: name the claims, boundaries, and evidence sources you expect to gather.",
|
|
44680
45131
|
"Ground the plan in the user request, caller context, project runbook, and source/config inspection. Do not invent app paths, ports, setup state, accounts, or device IDs.",
|
|
44681
45132
|
"Match each planned assertion to the scope it claims. A file-level or code-only assertion can be supported by file, command, or test evidence; an end-to-end, integration, sync, API, persistence, auth, billing, storage, deployment, browser, or mobile assertion needs evidence from the relevant boundary that makes the claim true.",
|
|
44682
45133
|
"If a required boundary cannot be inspected or exercised with available tools, narrow the assertion to what was actually verified or mark the broader assertion as untested or blocked.",
|
|
44683
45134
|
"For verification-like requests, name the assertions you will check and later record each assertion as passed, failed, untested, or blocked.",
|
|
45135
|
+
"For verification requests, plan the relevant evidence paths early across available tools: shell checks for builds/tests/typechecks/CLI behavior, browser evidence for web product behavior, mobile evidence for app/device behavior, source/config inspection for implementation boundaries, and MCP/API evidence when configured.",
|
|
45136
|
+
"For post-fix verification, include relevant deterministic project checks from .quire/runbook.md, package scripts, or explicit caller instructions when they are safe and applicable. Use run_shell_check so the command evidence lands in Quire artifacts instead of leaving caller agents to duplicate those checks.",
|
|
45137
|
+
"For web or mobile product verification, capture representative viewport/device evidence and exercise the important interactions, safe links/forms/CTAs, and accessibility basics needed to support the requested claims. Do not stop early while named high-value assertions remain feasible.",
|
|
44684
45138
|
"For triage-like requests, name the severity, impact, ownership, reproducibility, and next-action signals you will inspect.",
|
|
44685
45139
|
"For investigation-like requests, name the hypotheses, surfaces, code paths, logs, or product states you will examine.",
|
|
44686
45140
|
"Record expected behavior before executing the action that tests it; this makes unexpected observations harder to rationalize as success."
|
|
44687
45141
|
];
|
|
44688
45142
|
const TOOL_GUIDANCE = [
|
|
45143
|
+
"Use run_shell_check for non-dangerous project commands that produce useful evidence: local app startup checks, typechecks, builds, tests, lint/check commands, CLI smoke checks, custom project scripts, and read-only git/source inspection. Include the command artifact path in evidenceRefs when it supports an assertion.",
|
|
45144
|
+
"Do not use run_shell_check for installs, deploys, publishing, git mutation, destructive filesystem operations, database writes, seed/migrate commands, or external write requests. If such a command is required, mark it blocked or ask the caller to approve/add a safe runbook procedure.",
|
|
44689
45145
|
"Use agent-browser only when the runtime capabilities say browser automation is available.",
|
|
44690
45146
|
"Before browser exploration, run agent-browser skills get core once when that skill is available so browser guidance matches the installed CLI.",
|
|
45147
|
+
"For verification-like browser runs, especially QA, dogfooding, design review, accessibility, responsive, link, interaction, or form checks, run agent-browser skills get dogfood once before substantive browser actions when that skill is available. Use core plus dogfood; core alone is insufficient for product QA unless the run is only a tiny page-load smoke test.",
|
|
44691
45148
|
"Every agent-browser invocation is recorded as evidence. Do not call non-investigation browser commands such as install, upgrade, doctor, profile, or dashboard during exploration.",
|
|
44692
45149
|
"Use agent-mobile only when runtime capabilities say the mobile tool is registered. Treat device, simulator, app, and provider readiness as unknown until the tool or runbook proves them.",
|
|
44693
45150
|
"Every agent-mobile invocation is recorded as evidence. Do not call non-replayable commands such as help, doctor, serve, or close during exploration.",
|
|
@@ -44722,8 +45179,13 @@ const DEFAULT_HANDOFF_GUIDANCE = [
|
|
|
44722
45179
|
"Use needs_info when the report lacks necessary route, account, data, state, device, runner, or expected-behavior details after reasonable discovery.",
|
|
44723
45180
|
"Use environment_blocked when the endpoint, app install/launch, auth, data, network, runner, or browser/mobile tooling prevents reaching the needed state.",
|
|
44724
45181
|
"Write handoffMarkdown so a human can understand the verdict without reading raw logs and a coding agent can continue from handoff.md without rerunning the whole exploration.",
|
|
45182
|
+
"Include every artifact that supports the final verdict in write_handoff.evidenceRefs. Use artifactPath for [quire:artifact] paths returned by browser, mobile, or run_shell_check tools.",
|
|
45183
|
+
"Do not rely on prose-only evidence mentions. Quire will append a renderable Evidence artifacts section to handoff.md from public evidenceRefs and auto-captured artifacts.",
|
|
45184
|
+
"Do not include .harness-sessions paths, raw model transcripts, secrets, or private diagnostic state in evidenceRefs or handoffMarkdown.",
|
|
45185
|
+
"For markdown evidence links you write manually, prefer run-relative ./evidence/... paths over absolute local paths.",
|
|
44725
45186
|
"Treat progress.jsonl as the canonical structured ledger. handoff.md is the human-readable report view, not the machine-readable source of truth.",
|
|
44726
45187
|
"For verification-like requests or runs with structured assertions, use a Verification Report shape with Target, Request, Run, Summary, Results, Pre-existing Issues / Non-regressions when relevant, and Evidence sections.",
|
|
45188
|
+
"For post-fix or regression verification, make the requested claim verdict easy to find, then summarize product evidence, deterministic command evidence, blockers/untested checks, and unrelated or follow-up findings. Use natural prose and concise tables where helpful; do not force boilerplate if a simpler report is clearer.",
|
|
44727
45189
|
"For verification-like requests, include a decisive first paragraph with pass, fail, untested, blocked, and unresolved counts plus the strongest supported conclusion.",
|
|
44728
45190
|
"For verification-like requests, include a results table with assertion id, check title, verdict, expected, and actual. Include unresolved planned assertions explicitly; unresolved means no assertion record exists, not that the check passed or was untested.",
|
|
44729
45191
|
"Keep pre-existing, not-regression, and out-of-scope findings separate from the requested verification verdict.",
|
|
@@ -45507,6 +45969,11 @@ function resolveRuntimeCapabilities(input) {
|
|
|
45507
45969
|
available: true,
|
|
45508
45970
|
names: [...READ_ONLY_RUN_CODE_TOOL_NAMES]
|
|
45509
45971
|
},
|
|
45972
|
+
shellCheck: {
|
|
45973
|
+
available: true,
|
|
45974
|
+
toolName: "run_shell_check",
|
|
45975
|
+
safety: "dangerous_commands_blocked"
|
|
45976
|
+
},
|
|
45510
45977
|
browser: browserCapability(input.headed),
|
|
45511
45978
|
mobile: mobileCapability(),
|
|
45512
45979
|
mcp: {
|
|
@@ -45545,6 +46012,7 @@ function composeRunBrief(input) {
|
|
|
45545
46012
|
"## Run Artifacts",
|
|
45546
46013
|
`Run directory: ${toJsonPath(input.runDir.root)}`,
|
|
45547
46014
|
`Narrated progress file: ${toJsonPath(input.runDir.paths.progress)}`,
|
|
46015
|
+
`Evidence plan file: ${toJsonPath(input.runDir.paths.plan)}`,
|
|
45548
46016
|
`Final handoff path: ${toJsonPath(input.runDir.paths.handoff)}`,
|
|
45549
46017
|
`Evidence directory: ${toJsonPath(input.runDir.paths.evidence)}`,
|
|
45550
46018
|
`Private diagnostic harness session directory: ${toJsonPath(input.runDir.paths.harnessSessions)}`,
|
|
@@ -45558,6 +46026,7 @@ function composeRunBrief(input) {
|
|
|
45558
46026
|
"## Planning Requirement",
|
|
45559
46027
|
[
|
|
45560
46028
|
"Before broad exploration or browser/mobile interaction, record a concise plan with record_progress kind plan.",
|
|
46029
|
+
"The latest structured plan is materialized to plan.md in the run directory so caller agents can see what evidence Quire intends to gather while the run is active.",
|
|
45561
46030
|
"For verification-like work, state assertions before checking them and later record each as passed, failed, untested, or blocked.",
|
|
45562
46031
|
"For triage-like work, state the severity, impact, ownership, and next-action signals to inspect.",
|
|
45563
46032
|
"For investigation-like work, state the hypotheses, surfaces, or evidence paths to examine."
|
|
@@ -45643,6 +46112,7 @@ function capabilityLines(capabilities) {
|
|
|
45643
46112
|
`Runner: ${capabilities.runnerKind} on ${capabilities.platform}/${capabilities.arch}`,
|
|
45644
46113
|
`Repository cwd: ${toJsonPath(capabilities.cwd)}`,
|
|
45645
46114
|
`Read-only code tools: ${capabilities.tools.readOnlyCode.names.join(", ")}`,
|
|
46115
|
+
`Shell checks: ${capabilities.tools.shellCheck.toolName} available for non-dangerous project commands, local app startup checks, and verification evidence`,
|
|
45646
46116
|
`Browser automation: ${browser.available ? `${browser.toolName}${browser.headed === true ? " (headed)" : ""}` : `unavailable (${browser.unavailableReason ?? "unknown"})`}`,
|
|
45647
46117
|
`Mobile automation: ${mobile.available ? `${mobile.toolName} tool registered; device/app readiness unknown until checked` : `unavailable (${mobile.unavailableReason ?? "unknown"})`}`,
|
|
45648
46118
|
`MCP gateway: ${mcp.available ? `available (${mcp.toolNames.length === 0 ? "no tools listed" : mcp.toolNames.join(", ")})` : "unavailable"}`
|
|
@@ -45660,15 +46130,16 @@ function followupLines(context) {
|
|
|
45660
46130
|
function orderedListOrNone(values) {
|
|
45661
46131
|
return values.length === 0 ? "(none provided)" : values.map((value, index) => `${index + 1}. ${value}`).join("\n");
|
|
45662
46132
|
}
|
|
46133
|
+
//#endregion
|
|
46134
|
+
//#region src/pipeline/quire-agent.ts
|
|
46135
|
+
const RUN_ACTIVITY_HEARTBEAT_INTERVAL_MS = 5e3;
|
|
45663
46136
|
const QUIRE_AGENT_THINKING_LEVEL = INVESTIGATION_THINKING_LEVEL;
|
|
45664
46137
|
async function runQuireAgent({ intent, runBrief, runInstructions, remoteAddons, runtimeCapabilities, runDir, sessionId, headed = false, progress, onProgressEvent, followup, skillPack, mcpGateway, onResolvedModel }) {
|
|
45665
46138
|
const codebasePath = readRunMetadata(runDir)?.repoPath ?? runDir.root;
|
|
45666
46139
|
let emittedHandoff;
|
|
45667
46140
|
let observedToolAttempts = 0;
|
|
45668
|
-
let diagnosisSteered = false;
|
|
45669
46141
|
const observedTargetToolCallIds = /* @__PURE__ */ new Set();
|
|
45670
46142
|
const targetToolCallArgs = /* @__PURE__ */ new Map();
|
|
45671
|
-
const pendingSessionActions = [];
|
|
45672
46143
|
const runtime = await createQuireAgentRuntime({
|
|
45673
46144
|
runDir,
|
|
45674
46145
|
codebasePath,
|
|
@@ -45691,23 +46162,16 @@ async function runQuireAgent({ intent, runBrief, runInstructions, remoteAddons,
|
|
|
45691
46162
|
modelId: runtime.model.id
|
|
45692
46163
|
});
|
|
45693
46164
|
const unsubscribe = session.subscribe((event) => {
|
|
46165
|
+
touchRunActivity(runDir.root, { minIntervalMs: RUN_ACTIVITY_HEARTBEAT_INTERVAL_MS });
|
|
45694
46166
|
logAgentProgress(event);
|
|
45695
46167
|
emittedHandoff ??= extractWriteHandoff(event);
|
|
45696
46168
|
rememberTargetToolCallArgs(event, registeredTargetTools, targetToolCallArgs);
|
|
45697
46169
|
const attemptsObserved = countNewTargetAttempts(event, registeredTargetTools, observedTargetToolCallIds, targetToolCallArgs);
|
|
45698
|
-
if (attemptsObserved > 0)
|
|
45699
|
-
observedToolAttempts += attemptsObserved;
|
|
45700
|
-
if (observedToolAttempts >= 6 && !diagnosisSteered && emittedHandoff === void 0) {
|
|
45701
|
-
diagnosisSteered = true;
|
|
45702
|
-
pendingSessionActions.push(session.steer(buildRunSteeringPrompt(observedToolAttempts, registeredTargetTools)));
|
|
45703
|
-
}
|
|
45704
|
-
}
|
|
46170
|
+
if (attemptsObserved > 0) observedToolAttempts += attemptsObserved;
|
|
45705
46171
|
});
|
|
45706
46172
|
try {
|
|
45707
46173
|
await session.prompt(runBrief, { expandPromptTemplates: false });
|
|
45708
|
-
await Promise.all(pendingSessionActions);
|
|
45709
46174
|
if (emittedHandoff === void 0) await session.prompt(buildMissingRunHandoffPrompt(observedToolAttempts), { expandPromptTemplates: false });
|
|
45710
|
-
await Promise.all(pendingSessionActions);
|
|
45711
46175
|
} finally {
|
|
45712
46176
|
runtime.writeSessionState();
|
|
45713
46177
|
unsubscribe();
|
|
@@ -45733,12 +46197,17 @@ async function createQuireAgentRuntime({ runDir, codebasePath, sessionId, headed
|
|
|
45733
46197
|
runDir,
|
|
45734
46198
|
sessionId,
|
|
45735
46199
|
cwd: codebasePath,
|
|
45736
|
-
headed
|
|
46200
|
+
headed,
|
|
46201
|
+
onProgressEvent
|
|
45737
46202
|
}) : void 0;
|
|
45738
46203
|
const mobileTool = runtimeCapabilities.tools.mobile.available === true ? makeMobileTool({
|
|
45739
46204
|
runDir,
|
|
45740
46205
|
cwd: codebasePath
|
|
45741
46206
|
}) : void 0;
|
|
46207
|
+
const shellCheckTool = makeShellCheckTool({
|
|
46208
|
+
runDir,
|
|
46209
|
+
cwd: codebasePath
|
|
46210
|
+
});
|
|
45742
46211
|
const mcpGatewayTool = mcpGateway === void 0 ? void 0 : makeQuireMcpGatewayTool({
|
|
45743
46212
|
gateway: mcpGateway,
|
|
45744
46213
|
runId: runDir.runId
|
|
@@ -45781,6 +46250,7 @@ async function createQuireAgentRuntime({ runDir, codebasePath, sessionId, headed
|
|
|
45781
46250
|
customTools: [
|
|
45782
46251
|
...browserTool === void 0 ? [] : [browserTool],
|
|
45783
46252
|
...mobileTool === void 0 ? [] : [mobileTool],
|
|
46253
|
+
shellCheckTool,
|
|
45784
46254
|
withPromptGuidelines(createRecordProgressTool({
|
|
45785
46255
|
runDir,
|
|
45786
46256
|
onProgressEvent
|
|
@@ -45792,6 +46262,7 @@ async function createQuireAgentRuntime({ runDir, codebasePath, sessionId, headed
|
|
|
45792
46262
|
...READ_ONLY_RUN_CODE_TOOL_NAMES,
|
|
45793
46263
|
...browserTool === void 0 ? [] : [browserTool.name],
|
|
45794
46264
|
...mobileTool === void 0 ? [] : [mobileTool.name],
|
|
46265
|
+
shellCheckTool.name,
|
|
45795
46266
|
"record_progress",
|
|
45796
46267
|
writeHandoffTool.name,
|
|
45797
46268
|
...mcpGatewayTool === void 0 ? [] : [mcpGatewayTool.name]
|
|
@@ -45834,13 +46305,6 @@ function modelSourceProgressMessage(source, model) {
|
|
|
45834
46305
|
case "quire_credits": return `Using Quire Credits via brokered model source (${model.provider}/${model.id}).`;
|
|
45835
46306
|
}
|
|
45836
46307
|
}
|
|
45837
|
-
function buildRunSteeringPrompt(attempts, targetTools) {
|
|
45838
|
-
return [
|
|
45839
|
-
`${attempts} browser/mobile-backed attempts have been observed through ${targetTools.map((tool) => tool.toolName).join(", ") || "target tools"}.`,
|
|
45840
|
-
"Stop blind retries. Decide whether the run has enough evidence, needs narrower target context, is blocked by runtime/tooling/account/data, or requires one different action.",
|
|
45841
|
-
"If the run is complete or blocked, call write_handoff now. If more evidence is needed, take the next action that directly tests the diagnosis."
|
|
45842
|
-
].join("\n");
|
|
45843
|
-
}
|
|
45844
46308
|
function buildMissingRunHandoffPrompt(targetActions) {
|
|
45845
46309
|
return [
|
|
45846
46310
|
"The previous run ended without calling write_handoff.",
|
|
@@ -45883,10 +46347,13 @@ function isTargetReproductionAttempt(args, target) {
|
|
|
45883
46347
|
if (args === void 0) return true;
|
|
45884
46348
|
if (target === "mobile") {
|
|
45885
46349
|
const command = findMobileCommand(args);
|
|
45886
|
-
return command !== "open" && command !== "install";
|
|
46350
|
+
return command !== "open" && command !== "install" && command !== "screenshot";
|
|
45887
46351
|
}
|
|
45888
|
-
|
|
45889
|
-
if (
|
|
46352
|
+
const command = args[0];
|
|
46353
|
+
if (command === "open") return false;
|
|
46354
|
+
if (command === "set" && args[1] === "viewport") return false;
|
|
46355
|
+
if (command === "skills") return false;
|
|
46356
|
+
if (command === "snapshot" || command === "screenshot" || command === "get" || command === "eval") return false;
|
|
45890
46357
|
return true;
|
|
45891
46358
|
}
|
|
45892
46359
|
function rememberToolCallId(toolCallId, observedToolCallIds) {
|
|
@@ -48279,8 +48746,8 @@ function defaultSleep$1(ms) {
|
|
|
48279
48746
|
const ARTIFACT_MODE = "evidence";
|
|
48280
48747
|
const investigationCommandSchema = {
|
|
48281
48748
|
version: 1,
|
|
48282
|
-
command: "quire
|
|
48283
|
-
description: "Starts a durable Quire
|
|
48749
|
+
command: "quire run",
|
|
48750
|
+
description: "Starts a durable Quire run for a natural-language request or piped brief. Bare `quire \"<prompt>\"` is equivalent to `quire run \"<prompt>\"`.",
|
|
48284
48751
|
defaultMode: "attached",
|
|
48285
48752
|
input: {
|
|
48286
48753
|
pipedStdin: true,
|
|
@@ -48290,7 +48757,7 @@ const investigationCommandSchema = {
|
|
|
48290
48757
|
stdout: {
|
|
48291
48758
|
human: "Without --json, stdout shows a short start summary and attaches to the live run log unless --detach is passed.",
|
|
48292
48759
|
json: "With --json, stdout contains exactly one newline-terminated JSON start handle and does not attach to the live log.",
|
|
48293
|
-
schema: "With --schema, stdout contains this newline-terminated JSON schema and no
|
|
48760
|
+
schema: "With --schema, stdout contains this newline-terminated JSON schema and no run starts."
|
|
48294
48761
|
},
|
|
48295
48762
|
stderr: { progress: "Machine-readable errors are written to stderr. Human attached runs stream live run logs after the start summary." },
|
|
48296
48763
|
arguments: [
|
|
@@ -48299,7 +48766,7 @@ const investigationCommandSchema = {
|
|
|
48299
48766
|
type: "string",
|
|
48300
48767
|
positional: true,
|
|
48301
48768
|
requiredUnless: "piped stdin",
|
|
48302
|
-
description: "Natural-language
|
|
48769
|
+
description: "Natural-language run request or instruction."
|
|
48303
48770
|
},
|
|
48304
48771
|
{
|
|
48305
48772
|
name: "--json",
|
|
@@ -48382,21 +48849,20 @@ const investigationCommandSchema = {
|
|
|
48382
48849
|
safety: ["Raw model thinking, prompts, completions, provider credentials, API tokens, and local absolute repo paths are not synced to the web case.", "Sync failures are non-fatal for local investigations; local artifacts and retry state remain in the run directory."],
|
|
48383
48850
|
examples: [
|
|
48384
48851
|
"quire \"verify checkout before I approve this PR\"",
|
|
48385
|
-
"printf 'why is CI failing?\\nRelevant log: ...' | quire
|
|
48852
|
+
"printf 'why is CI failing?\\nRelevant log: ...' | quire run --json",
|
|
48386
48853
|
"quire status run_9x4mdq7p2h8kc6nv4apz --json",
|
|
48387
48854
|
"quire watch run_9x4mdq7p2h8kc6nv4apz"
|
|
48388
48855
|
]
|
|
48389
48856
|
};
|
|
48390
|
-
const
|
|
48857
|
+
const quireRunCommand = defineCommand({
|
|
48391
48858
|
meta: {
|
|
48392
|
-
name: "
|
|
48393
|
-
|
|
48394
|
-
description: "Investigate a question against the current workspace."
|
|
48859
|
+
name: "run",
|
|
48860
|
+
description: "Run evidence-backed QA, verification, triage, or investigation work."
|
|
48395
48861
|
},
|
|
48396
48862
|
args: {
|
|
48397
48863
|
prompt: {
|
|
48398
48864
|
type: "positional",
|
|
48399
|
-
description: "Natural-language
|
|
48865
|
+
description: "Natural-language run request.",
|
|
48400
48866
|
required: false
|
|
48401
48867
|
},
|
|
48402
48868
|
url: {
|
|
@@ -48419,11 +48885,11 @@ const investigateCommand = defineCommand({
|
|
|
48419
48885
|
},
|
|
48420
48886
|
headed: {
|
|
48421
48887
|
type: "boolean",
|
|
48422
|
-
description: "Show the browser window while the
|
|
48888
|
+
description: "Show the browser window while the Quire agent runs."
|
|
48423
48889
|
},
|
|
48424
48890
|
schema: {
|
|
48425
48891
|
type: "boolean",
|
|
48426
|
-
description: "Emit the machine-readable
|
|
48892
|
+
description: "Emit the machine-readable run invocation schema and exit."
|
|
48427
48893
|
}
|
|
48428
48894
|
},
|
|
48429
48895
|
async run({ args }) {
|
|
@@ -48466,7 +48932,7 @@ async function runInvestigate(options) {
|
|
|
48466
48932
|
});
|
|
48467
48933
|
if (options.json === true) writeJson(stdout, handle);
|
|
48468
48934
|
else {
|
|
48469
|
-
log.success(`Started
|
|
48935
|
+
log.success(`Started run ${color.value(handle.runId)}.`, {
|
|
48470
48936
|
output: stdout,
|
|
48471
48937
|
spacing: 0
|
|
48472
48938
|
});
|
|
@@ -48480,7 +48946,12 @@ async function runInvestigate(options) {
|
|
|
48480
48946
|
spacing: 0,
|
|
48481
48947
|
withGuide: true
|
|
48482
48948
|
});
|
|
48483
|
-
else log.message(
|
|
48949
|
+
else log.message([
|
|
48950
|
+
`${color.label("Run")}: ${color.path(handle.runPath)}`,
|
|
48951
|
+
`${color.label("Status")}: ${color.command(handle.statusCommand)}`,
|
|
48952
|
+
`${color.label("Watch")}: ${color.command(handle.watchCommand)}`,
|
|
48953
|
+
`This run continues in the background. You can safely exit; use ${color.command(handle.statusCommand)} for the current state or ${color.command(handle.watchCommand)} to follow progress logs.`
|
|
48954
|
+
], {
|
|
48484
48955
|
output: stdout,
|
|
48485
48956
|
spacing: 0,
|
|
48486
48957
|
withGuide: true
|
|
@@ -48631,6 +49102,7 @@ function createDoctorContext(overrides = {}) {
|
|
|
48631
49102
|
cwd: resolve(overrides.cwd ?? process.cwd()),
|
|
48632
49103
|
runsRoot: resolve(overrides.runsRoot ?? defaultRunsRoot()),
|
|
48633
49104
|
authStore: overrides.authStore ?? authStore,
|
|
49105
|
+
env: overrides.env ?? process.env,
|
|
48634
49106
|
fetchFn: overrides.fetchFn ?? fetch,
|
|
48635
49107
|
execFn: overrides.execFn ?? defaultExecFn,
|
|
48636
49108
|
probeTimeoutMs: overrides.probeTimeoutMs ?? 2e3
|
|
@@ -48647,25 +49119,30 @@ async function defaultExecFn(file, args, options) {
|
|
|
48647
49119
|
};
|
|
48648
49120
|
}
|
|
48649
49121
|
async function checkAuthPresent(context) {
|
|
48650
|
-
const
|
|
49122
|
+
const source = context.env["QUIRE_API_TOKEN"]?.trim() ? "env_api_token" : "stored_login";
|
|
49123
|
+
const credentials = await resolveAuthCredentials({
|
|
49124
|
+
store: context.authStore,
|
|
49125
|
+
env: context.env
|
|
49126
|
+
});
|
|
48651
49127
|
if (credentials === null) return { result: {
|
|
48652
49128
|
id: "auth.present",
|
|
48653
49129
|
section: "identity",
|
|
48654
49130
|
severity: "fail",
|
|
48655
|
-
message: "No Quire credentials
|
|
48656
|
-
fix: { command: "quire login" }
|
|
49131
|
+
message: "No Quire credentials found. Authenticate with an API token or interactive login.",
|
|
49132
|
+
fix: { command: "Set QUIRE_API_TOKEN or run `quire login`" }
|
|
48657
49133
|
} };
|
|
48658
49134
|
return {
|
|
48659
49135
|
result: {
|
|
48660
49136
|
id: "auth.present",
|
|
48661
49137
|
section: "identity",
|
|
48662
49138
|
severity: "pass",
|
|
48663
|
-
message:
|
|
49139
|
+
message: `Credentials present (${authSourceLabel(source)}; API ${credentials.apiBaseUrl}).`
|
|
48664
49140
|
},
|
|
48665
49141
|
auth: {
|
|
48666
49142
|
apiBaseUrl: credentials.apiBaseUrl,
|
|
48667
49143
|
accessToken: credentials.accessToken,
|
|
48668
49144
|
tokenType: credentials.tokenType,
|
|
49145
|
+
source,
|
|
48669
49146
|
...credentials.user === void 0 ? {} : { user: credentials.user }
|
|
48670
49147
|
}
|
|
48671
49148
|
};
|
|
@@ -48688,8 +49165,8 @@ async function checkAuthValid(context, auth) {
|
|
|
48688
49165
|
id: "auth.valid",
|
|
48689
49166
|
section: "identity",
|
|
48690
49167
|
severity: "fail",
|
|
48691
|
-
message: `Could not validate
|
|
48692
|
-
fix: { command:
|
|
49168
|
+
message: `Could not validate API token against ${auth.apiBaseUrl}: ${toMessage(error)}`,
|
|
49169
|
+
fix: { command: apiTokenFixCommand(auth.apiBaseUrl) }
|
|
48693
49170
|
} };
|
|
48694
49171
|
}
|
|
48695
49172
|
try {
|
|
@@ -48703,8 +49180,8 @@ async function checkAuthValid(context, auth) {
|
|
|
48703
49180
|
id: "auth.valid",
|
|
48704
49181
|
section: "identity",
|
|
48705
49182
|
severity: "fail",
|
|
48706
|
-
message:
|
|
48707
|
-
fix: { command:
|
|
49183
|
+
message: `Stored login was rejected by ${auth.apiBaseUrl}.`,
|
|
49184
|
+
fix: { command: loginFixCommand(auth.apiBaseUrl) }
|
|
48708
49185
|
} };
|
|
48709
49186
|
const user = lookup.data.user;
|
|
48710
49187
|
return {
|
|
@@ -48721,8 +49198,8 @@ async function checkAuthValid(context, auth) {
|
|
|
48721
49198
|
id: "auth.valid",
|
|
48722
49199
|
section: "identity",
|
|
48723
49200
|
severity: "fail",
|
|
48724
|
-
message: `Could not validate
|
|
48725
|
-
fix: { command:
|
|
49201
|
+
message: `Could not validate stored login against ${auth.apiBaseUrl}: ${toMessage(error)}`,
|
|
49202
|
+
fix: { command: loginFixCommand(auth.apiBaseUrl) }
|
|
48726
49203
|
} };
|
|
48727
49204
|
}
|
|
48728
49205
|
}
|
|
@@ -48764,6 +49241,15 @@ function brokerToCheckResult(status) {
|
|
|
48764
49241
|
message: `Model broker available (${status.selectedProviderMode ?? "default"}).`
|
|
48765
49242
|
};
|
|
48766
49243
|
}
|
|
49244
|
+
function authSourceLabel(source) {
|
|
49245
|
+
return source === "env_api_token" ? `${QUIRE_API_TOKEN_ENV} API token` : "stored login";
|
|
49246
|
+
}
|
|
49247
|
+
function apiTokenFixCommand(apiBaseUrl) {
|
|
49248
|
+
return `Set QUIRE_API_TOKEN to an active token for ${apiBaseUrl}; set QUIRE_API_URL if that API URL is wrong; or run \`quire login --api-url ${apiBaseUrl}\``;
|
|
49249
|
+
}
|
|
49250
|
+
function loginFixCommand(apiBaseUrl) {
|
|
49251
|
+
return `Run \`quire login --api-url ${apiBaseUrl}\`, or set QUIRE_API_TOKEN and QUIRE_API_URL for API-token auth`;
|
|
49252
|
+
}
|
|
48767
49253
|
async function checkRunbook(context) {
|
|
48768
49254
|
const runbook = await inspectProjectRunbookStatus(context.cwd);
|
|
48769
49255
|
if (!runbook.exists) return {
|
|
@@ -49176,22 +49662,78 @@ async function runLogout(options) {
|
|
|
49176
49662
|
//#endregion
|
|
49177
49663
|
//#region src/commands/setup.ts
|
|
49178
49664
|
const SETUP_URL = "https://quire.sh/agent-setup";
|
|
49665
|
+
const VERIFICATION_SKILL_URL = "https://quire.sh/agent-verification";
|
|
49179
49666
|
const AGENT_SETUP_PROMPT = `Set up this repository for Quire investigations.
|
|
49180
49667
|
|
|
49181
49668
|
Please load and follow the Quire agent setup instructions from ${SETUP_URL}.
|
|
49182
49669
|
|
|
49183
|
-
Inspect the project,
|
|
49670
|
+
Inspect the project, ask focused questions for important missing runbook facts instead of writing placeholder unknowns, then create a .quire/runbook.md that encodes the best-practices verification loop for this specific project. Capture how Quire should self-verify work in this domain: target/environment choice, local startup, browser/API/CLI/mobile checks, console/runtime signals, core flows, verification commands, design/reference surfaces, auth/data setup, performance/accessibility expectations, and mutation-safety rules. Update AGENTS.md with concise Quire notes, then run Quire doctor using the available Quire command, e.g. \`quire doctor\` or \`npx @quireco/cli doctor\`, and report anything still blocked.`;
|
|
49671
|
+
const QUIRE_SKILL_MARKDOWN = `---
|
|
49672
|
+
name: quire
|
|
49673
|
+
description: Runs Quire CLI evidence-backed QA, verification, triage, and investigation work. Use after implementing UI or workflow changes, when testing local/preview/prod URLs, or when claims need screenshots, command output, observations, and a trustworthy handoff.
|
|
49674
|
+
---
|
|
49675
|
+
|
|
49676
|
+
# Quire verification
|
|
49677
|
+
|
|
49678
|
+
Load and follow the current Quire verification skill from ${VERIFICATION_SKILL_URL}.
|
|
49679
|
+
|
|
49680
|
+
Use Quire when a task needs evidence-backed verification, QA, product dogfooding, bug reproduction, or triage. Prefer \`quire run\` for new work.
|
|
49681
|
+
`;
|
|
49682
|
+
const SKILL_INSTALL_TARGETS = [{
|
|
49683
|
+
id: "agents",
|
|
49684
|
+
label: "Universal",
|
|
49685
|
+
targetPath: ".agents/skills/quire/SKILL.md",
|
|
49686
|
+
hint: "~/.agents/skills/quire/SKILL.md · Pi, OpenCode, Amp, Codex-style agents",
|
|
49687
|
+
aliases: [
|
|
49688
|
+
"agents",
|
|
49689
|
+
"agent-skills",
|
|
49690
|
+
"pi",
|
|
49691
|
+
"opencode",
|
|
49692
|
+
"amp",
|
|
49693
|
+
"codex",
|
|
49694
|
+
"other"
|
|
49695
|
+
]
|
|
49696
|
+
}, {
|
|
49697
|
+
id: "claude",
|
|
49698
|
+
label: "Claude Code",
|
|
49699
|
+
targetPath: ".claude/skills/quire/SKILL.md",
|
|
49700
|
+
hint: "~/.claude/skills/quire/SKILL.md",
|
|
49701
|
+
aliases: ["claude", "claude-code"]
|
|
49702
|
+
}];
|
|
49184
49703
|
const setupCommand = defineCommand({
|
|
49185
49704
|
meta: {
|
|
49186
49705
|
name: "setup",
|
|
49187
49706
|
description: "Prepare this workspace for Quire investigations."
|
|
49188
49707
|
},
|
|
49189
|
-
|
|
49190
|
-
|
|
49708
|
+
args: {
|
|
49709
|
+
skill: {
|
|
49710
|
+
type: "string",
|
|
49711
|
+
description: "Comma-separated local agent skill targets to install: agents, claude, all, none.",
|
|
49712
|
+
valueHint: "targets"
|
|
49713
|
+
},
|
|
49714
|
+
"no-skill": {
|
|
49715
|
+
type: "boolean",
|
|
49716
|
+
description: "Skip installing local Quire agent skills."
|
|
49717
|
+
},
|
|
49718
|
+
"no-interactive": {
|
|
49719
|
+
type: "boolean",
|
|
49720
|
+
description: "Do not prompt; use default setup choices."
|
|
49721
|
+
}
|
|
49722
|
+
},
|
|
49723
|
+
async run({ args }) {
|
|
49724
|
+
await runSetup({
|
|
49725
|
+
skill: args["no-skill"] === true ? false : args.skill,
|
|
49726
|
+
interactive: args["no-interactive"] !== true
|
|
49727
|
+
});
|
|
49191
49728
|
}
|
|
49192
49729
|
});
|
|
49193
49730
|
async function runSetup(options = {}) {
|
|
49194
49731
|
const stdout = options.io?.stdout ?? process.stdout;
|
|
49732
|
+
const selectedTargets = await selectSkillInstallTargets({
|
|
49733
|
+
skill: options.skill,
|
|
49734
|
+
interactive: options.interactive ?? (options.io?.stdout === void 0 && process.stdout.isTTY === true)
|
|
49735
|
+
});
|
|
49736
|
+
const installedSkills = await installQuireSkill(options.homeDir ?? homedir(), selectedTargets);
|
|
49195
49737
|
log.step("Set up Quire for this workspace", {
|
|
49196
49738
|
output: stdout,
|
|
49197
49739
|
spacing: 0
|
|
@@ -49205,6 +49747,51 @@ async function runSetup(options = {}) {
|
|
|
49205
49747
|
writeLine(stdout, color.label("--- copy prompt ---"));
|
|
49206
49748
|
writeLine(stdout, AGENT_SETUP_PROMPT);
|
|
49207
49749
|
writeLine(stdout, color.label("--- end prompt ---"));
|
|
49750
|
+
if (installedSkills.length > 0) {
|
|
49751
|
+
writeLine(stdout);
|
|
49752
|
+
writeLine(stdout, color.label("Installed local Quire skill:"));
|
|
49753
|
+
for (const path of installedSkills) writeLine(stdout, ` ${color.success("✓")} ${path}`);
|
|
49754
|
+
}
|
|
49755
|
+
}
|
|
49756
|
+
async function selectSkillInstallTargets(options) {
|
|
49757
|
+
if (options.skill === false || options.skill === "none") return [];
|
|
49758
|
+
if (options.skill !== void 0) return resolveSkillTargets(options.skill);
|
|
49759
|
+
if (!options.interactive) return [...SKILL_INSTALL_TARGETS];
|
|
49760
|
+
const selected = await multiselect({
|
|
49761
|
+
message: "Which local agent skill directories should Quire install into?",
|
|
49762
|
+
options: SKILL_INSTALL_TARGETS.map((target) => ({
|
|
49763
|
+
label: target.label,
|
|
49764
|
+
value: target.id,
|
|
49765
|
+
hint: target.hint
|
|
49766
|
+
})),
|
|
49767
|
+
initialValues: ["agents", "claude"],
|
|
49768
|
+
required: false
|
|
49769
|
+
});
|
|
49770
|
+
if (isCancel(selected) || selected.length === 0) return [];
|
|
49771
|
+
return resolveSkillTargets(selected.join(","));
|
|
49772
|
+
}
|
|
49773
|
+
function resolveSkillTargets(value) {
|
|
49774
|
+
const parts = value.split(",").map((part) => part.trim().toLowerCase()).filter((part) => part.length > 0);
|
|
49775
|
+
if (parts.includes("all")) return [...SKILL_INSTALL_TARGETS];
|
|
49776
|
+
const selected = /* @__PURE__ */ new Map();
|
|
49777
|
+
for (const part of parts) {
|
|
49778
|
+
const target = SKILL_INSTALL_TARGETS.find((candidate) => candidate.id === part || candidate.aliases.includes(part));
|
|
49779
|
+
if (target !== void 0) selected.set(target.id, target);
|
|
49780
|
+
}
|
|
49781
|
+
return [...selected.values()];
|
|
49782
|
+
}
|
|
49783
|
+
async function installQuireSkill(homeDir, targets) {
|
|
49784
|
+
const installed = [];
|
|
49785
|
+
await Promise.all(targets.map(async (target) => {
|
|
49786
|
+
const path = join(homeDir, target.targetPath);
|
|
49787
|
+
await mkdir(dirname(path), {
|
|
49788
|
+
recursive: true,
|
|
49789
|
+
mode: 448
|
|
49790
|
+
});
|
|
49791
|
+
await writeFile(path, QUIRE_SKILL_MARKDOWN, { mode: 384 });
|
|
49792
|
+
installed.push(path);
|
|
49793
|
+
}));
|
|
49794
|
+
return installed.sort();
|
|
49208
49795
|
}
|
|
49209
49796
|
//#endregion
|
|
49210
49797
|
//#region src/commands/whoami.ts
|
|
@@ -49356,7 +49943,7 @@ function formatWalletBalance(value) {
|
|
|
49356
49943
|
}
|
|
49357
49944
|
//#endregion
|
|
49358
49945
|
//#region package.json
|
|
49359
|
-
var version$1 = "0.0.
|
|
49946
|
+
var version$1 = "0.0.11";
|
|
49360
49947
|
//#endregion
|
|
49361
49948
|
//#region src/cli.ts
|
|
49362
49949
|
const subCommands = {
|
|
@@ -49382,18 +49969,14 @@ const subCommands = {
|
|
|
49382
49969
|
continue: continueCommand,
|
|
49383
49970
|
doctor: doctorCommand,
|
|
49384
49971
|
env: envCommand,
|
|
49385
|
-
|
|
49972
|
+
run: quireRunCommand,
|
|
49386
49973
|
runs: runsCommand,
|
|
49387
49974
|
setup: setupCommand,
|
|
49388
49975
|
status: statusCommand,
|
|
49389
49976
|
sync: syncCommand,
|
|
49390
49977
|
watch: watchCommand
|
|
49391
49978
|
};
|
|
49392
|
-
const subCommandAliases = new Set([
|
|
49393
|
-
"ask",
|
|
49394
|
-
"me",
|
|
49395
|
-
"resume"
|
|
49396
|
-
]);
|
|
49979
|
+
const subCommandAliases = new Set(["me", "resume"]);
|
|
49397
49980
|
const cli = defineCommand({
|
|
49398
49981
|
meta: {
|
|
49399
49982
|
name: "quire",
|
|
@@ -49427,7 +50010,7 @@ function isVersionRequest(rawArgs) {
|
|
|
49427
50010
|
function normalizeRawArgs(rawArgs) {
|
|
49428
50011
|
const first = rawArgs[0];
|
|
49429
50012
|
if (first === void 0 || isTopLevelFlag(first) || isSubCommandName(first)) return rawArgs;
|
|
49430
|
-
return ["
|
|
50013
|
+
return ["run", ...rawArgs];
|
|
49431
50014
|
}
|
|
49432
50015
|
function isTopLevelFlag(arg) {
|
|
49433
50016
|
return arg === "--help" || arg === "-h" || arg === "--version" || arg === "-v";
|