@h-rig/runtime 0.0.6-alpha.22 → 0.0.6-alpha.23
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/bin/rig-agent-dispatch.js +333 -23
- package/dist/src/control-plane/agent-wrapper.js +336 -23
- package/dist/src/control-plane/harness-main.js +142 -17
- package/dist/src/control-plane/hooks/completion-verification.js +142 -17
- package/dist/src/control-plane/native/harness-cli.js +142 -17
- package/dist/src/control-plane/native/pr-automation.js +142 -17
- package/dist/src/control-plane/native/pr-review-gate.js +142 -17
- package/dist/src/control-plane/native/run-ops.js +1 -1
- package/dist/src/control-plane/native/task-ops.js +142 -17
- package/dist/src/control-plane/native/verifier.js +142 -17
- package/dist/src/control-plane/pi-sessiond/bin.js +793 -0
- package/dist/src/control-plane/pi-sessiond/client.js +41 -0
- package/dist/src/control-plane/pi-sessiond/event-hub.js +59 -0
- package/dist/src/control-plane/pi-sessiond/extension-ui-context.js +198 -0
- package/dist/src/control-plane/pi-sessiond/launcher.js +163 -0
- package/dist/src/control-plane/pi-sessiond/server.js +802 -0
- package/dist/src/control-plane/pi-sessiond/session-service.js +540 -0
- package/dist/src/control-plane/pi-sessiond/types.js +1 -0
- package/dist/src/control-plane/runtime/index.js +17 -0
- package/dist/src/control-plane/runtime/isolation/home.js +17 -0
- package/dist/src/control-plane/runtime/isolation/index.js +17 -0
- package/dist/src/control-plane/runtime/isolation/runner.js +17 -0
- package/dist/src/control-plane/runtime/isolation.js +17 -0
- package/dist/src/control-plane/runtime/queue.js +17 -0
- package/package.json +7 -7
|
@@ -1775,6 +1775,57 @@ function flattenPaginatedArray(value) {
|
|
|
1775
1775
|
}
|
|
1776
1776
|
return value;
|
|
1777
1777
|
}
|
|
1778
|
+
function parseConcatenatedJsonValues(value) {
|
|
1779
|
+
const text = value.trim();
|
|
1780
|
+
const docs = [];
|
|
1781
|
+
let start = null;
|
|
1782
|
+
let depth = 0;
|
|
1783
|
+
let inString = false;
|
|
1784
|
+
let escape = false;
|
|
1785
|
+
for (let index = 0;index < text.length; index += 1) {
|
|
1786
|
+
const char = text[index];
|
|
1787
|
+
if (start === null) {
|
|
1788
|
+
if (/\s/.test(char))
|
|
1789
|
+
continue;
|
|
1790
|
+
start = index;
|
|
1791
|
+
}
|
|
1792
|
+
if (inString) {
|
|
1793
|
+
if (escape) {
|
|
1794
|
+
escape = false;
|
|
1795
|
+
} else if (char === "\\") {
|
|
1796
|
+
escape = true;
|
|
1797
|
+
} else if (char === '"') {
|
|
1798
|
+
inString = false;
|
|
1799
|
+
}
|
|
1800
|
+
continue;
|
|
1801
|
+
}
|
|
1802
|
+
if (char === '"') {
|
|
1803
|
+
inString = true;
|
|
1804
|
+
continue;
|
|
1805
|
+
}
|
|
1806
|
+
if (char === "{" || char === "[") {
|
|
1807
|
+
depth += 1;
|
|
1808
|
+
continue;
|
|
1809
|
+
}
|
|
1810
|
+
if (char === "}" || char === "]") {
|
|
1811
|
+
depth -= 1;
|
|
1812
|
+
if (depth < 0)
|
|
1813
|
+
return { value: docs, error: "unexpected JSON close delimiter" };
|
|
1814
|
+
if (depth === 0 && start !== null) {
|
|
1815
|
+
const segment = text.slice(start, index + 1);
|
|
1816
|
+
try {
|
|
1817
|
+
docs.push(JSON.parse(segment));
|
|
1818
|
+
} catch (error) {
|
|
1819
|
+
return { value: docs, error: error instanceof Error ? error.message : String(error) };
|
|
1820
|
+
}
|
|
1821
|
+
start = null;
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
if (inString || depth !== 0 || start !== null)
|
|
1826
|
+
return { value: docs, error: "incomplete JSON stream" };
|
|
1827
|
+
return { value: docs };
|
|
1828
|
+
}
|
|
1778
1829
|
function parseJsonArray(value) {
|
|
1779
1830
|
if (!value?.trim())
|
|
1780
1831
|
return { value: [], error: "empty JSON output" };
|
|
@@ -1783,7 +1834,11 @@ function parseJsonArray(value) {
|
|
|
1783
1834
|
const flattened = flattenPaginatedArray(parsed);
|
|
1784
1835
|
return flattened ? { value: flattened } : { value: [], error: "JSON output was not an array" };
|
|
1785
1836
|
} catch (error) {
|
|
1786
|
-
|
|
1837
|
+
const streamed = parseConcatenatedJsonValues(value);
|
|
1838
|
+
if (streamed.error)
|
|
1839
|
+
return { value: [], error: error instanceof Error ? error.message : String(error) };
|
|
1840
|
+
const flattened = streamed.value.flatMap((entry) => flattenPaginatedArray(entry) ?? []);
|
|
1841
|
+
return flattened.length > 0 || streamed.value.length === 0 ? { value: flattened } : { value: [], error: "JSON output was not an array" };
|
|
1787
1842
|
}
|
|
1788
1843
|
}
|
|
1789
1844
|
function parseGithubPrUrl(prUrl) {
|
|
@@ -1872,6 +1927,24 @@ function isStrictFiveOfFive(score) {
|
|
|
1872
1927
|
function containsConflictingScoreText(input) {
|
|
1873
1928
|
return parseGreptileScores(input).some((score) => !isStrictFiveOfFive(score));
|
|
1874
1929
|
}
|
|
1930
|
+
function extractGreptileCommentBlock(input) {
|
|
1931
|
+
const match = input.match(/<!--\s*greptile_comment\s*-->([\s\S]*?)<!--\s*\/greptile_comment\s*-->/i);
|
|
1932
|
+
return match?.[1]?.trim() ?? null;
|
|
1933
|
+
}
|
|
1934
|
+
function extractGreptileBodyReviewedSha(input) {
|
|
1935
|
+
const block = extractGreptileCommentBlock(input);
|
|
1936
|
+
if (!block)
|
|
1937
|
+
return null;
|
|
1938
|
+
const commitLink = block.match(/\/commit\/([0-9a-f]{40})(?:\b|[^0-9a-f])/i);
|
|
1939
|
+
return commitLink?.[1]?.toLowerCase() ?? null;
|
|
1940
|
+
}
|
|
1941
|
+
function isoAtOrAfter(value, floor) {
|
|
1942
|
+
if (!value || !floor)
|
|
1943
|
+
return false;
|
|
1944
|
+
const valueMs = Date.parse(value);
|
|
1945
|
+
const floorMs = Date.parse(floor);
|
|
1946
|
+
return Number.isFinite(valueMs) && Number.isFinite(floorMs) && valueMs >= floorMs;
|
|
1947
|
+
}
|
|
1875
1948
|
function greptileStatusVerdict(status) {
|
|
1876
1949
|
const normalized = String(status ?? "").trim().toUpperCase().replace(/[\s-]+/g, "_");
|
|
1877
1950
|
if (!normalized)
|
|
@@ -2246,9 +2319,18 @@ function commentAuthorLogin(comment) {
|
|
|
2246
2319
|
}
|
|
2247
2320
|
function collectGreptileSignals(evidence) {
|
|
2248
2321
|
const signals = [];
|
|
2322
|
+
const greptileBodyReviewedSha = extractGreptileBodyReviewedSha(evidence.body);
|
|
2323
|
+
const trustedGreptileBody = Boolean(greptileBodyReviewedSha && isGreptileGithubLogin(evidence.bodyEditorLogin) && isoAtOrAfter(evidence.bodyLastEditedAt, evidence.headCommittedDate));
|
|
2249
2324
|
const contextSources = [
|
|
2250
2325
|
{ source: "pr-title", body: evidence.title ?? "" },
|
|
2251
|
-
{
|
|
2326
|
+
{
|
|
2327
|
+
source: "pr-body",
|
|
2328
|
+
body: evidence.body,
|
|
2329
|
+
trusted: trustedGreptileBody,
|
|
2330
|
+
authorLogin: trustedGreptileBody ? evidence.bodyEditorLogin ?? null : null,
|
|
2331
|
+
reviewedSha: greptileBodyReviewedSha,
|
|
2332
|
+
verdict: trustedGreptileBody ? "completed" : null
|
|
2333
|
+
}
|
|
2252
2334
|
];
|
|
2253
2335
|
for (const context of contextSources) {
|
|
2254
2336
|
if (!context.body.trim())
|
|
@@ -2260,7 +2342,10 @@ function collectGreptileSignals(evidence) {
|
|
|
2260
2342
|
source: context.source,
|
|
2261
2343
|
body: context.body,
|
|
2262
2344
|
currentHeadSha: evidence.currentHeadSha,
|
|
2263
|
-
trusted:
|
|
2345
|
+
trusted: context.trusted === true,
|
|
2346
|
+
authorLogin: context.authorLogin,
|
|
2347
|
+
reviewedSha: context.reviewedSha,
|
|
2348
|
+
verdict: context.verdict,
|
|
2264
2349
|
blocker: contextBlocker,
|
|
2265
2350
|
actionable: contextBlocker
|
|
2266
2351
|
}));
|
|
@@ -2456,7 +2541,7 @@ function deriveGreptileEvidence(input) {
|
|
|
2456
2541
|
const hasGreptileEvidence = trustedSignals.length > 0 || signals.some((signal) => /greptile/i.test(signal.body));
|
|
2457
2542
|
const approved = fresh && completed && !blockers.length && !unresolvedComments.length && currentPendingApiSignals.length === 0 && (approvedByScore || approvedByExplicitMapping);
|
|
2458
2543
|
const mapping = !hasGreptileEvidence ? "missing" : staleSignals.length > 0 && !approvingSignal ? "stale" : approvedByScore ? "score-5-of-5" : approvedByExplicitMapping ? "explicit-approved" : "unproven";
|
|
2459
|
-
const source = approvingSignal?.source === "api" ? "api" : approvingSignal?.source === "github-review" ? "github-review" : approvingSignal?.source === "changed-file-comment" || approvingSignal?.source === "issue-comment" || approvingSignal?.source === "review-thread" ? "github-comment" : greptileReviews.length > 0 && greptileChecks.length > 0 ? "combined" : greptileReviews.length > 0 ? "github-review" : greptileChecks.length > 0 ? "github-check" : signals.some((signal) => signal.source === "pr-body" || signal.source === "pr-title") ? "pr-body" : "missing";
|
|
2544
|
+
const source = approvingSignal?.source === "api" ? "api" : approvingSignal?.source === "github-review" ? "github-review" : approvingSignal?.source === "pr-body" || approvingSignal?.source === "pr-title" ? "pr-body" : approvingSignal?.source === "changed-file-comment" || approvingSignal?.source === "issue-comment" || approvingSignal?.source === "review-thread" ? "github-comment" : greptileReviews.length > 0 && greptileChecks.length > 0 ? "combined" : greptileReviews.length > 0 ? "github-review" : greptileChecks.length > 0 ? "github-check" : signals.some((signal) => signal.source === "pr-body" || signal.source === "pr-title") ? "pr-body" : "missing";
|
|
2460
2545
|
return {
|
|
2461
2546
|
source,
|
|
2462
2547
|
currentHeadSha: input.currentHeadSha,
|
|
@@ -2477,17 +2562,48 @@ function isGreptileCheckDetail(check) {
|
|
|
2477
2562
|
return isGreptileLabel(checkName(check)) || isGreptileGithubLogin(check.app?.slug) || isGreptileGithubLogin(check.app?.owner?.login) || isGreptileLabel(check.app?.name);
|
|
2478
2563
|
}
|
|
2479
2564
|
async function collectGreptileCheckDetails(input) {
|
|
2480
|
-
const checkRunsRead = await
|
|
2565
|
+
const checkRunsRead = await runJsonObject(input.command, [
|
|
2481
2566
|
"api",
|
|
2482
2567
|
`repos/${input.repoName}/commits/${input.headSha}/check-runs`,
|
|
2483
|
-
"
|
|
2484
|
-
"
|
|
2485
|
-
"--jq",
|
|
2486
|
-
"map(.check_runs // []) | add // []"
|
|
2568
|
+
"-F",
|
|
2569
|
+
"per_page=100"
|
|
2487
2570
|
], input.projectRoot);
|
|
2488
|
-
const checkRuns = checkRunsRead.value.map(normalizeStatusCheck).filter((entry) => !!entry).filter(isGreptileCheckDetail);
|
|
2571
|
+
const checkRuns = arrayField(checkRunsRead.value, "check_runs").map(normalizeStatusCheck).filter((entry) => !!entry).filter(isGreptileCheckDetail);
|
|
2489
2572
|
return checkRunsRead.error ? { value: checkRuns, error: checkRunsRead.error } : { value: checkRuns };
|
|
2490
2573
|
}
|
|
2574
|
+
async function collectPullRequestProvenance(input) {
|
|
2575
|
+
const response = await runJsonObject(input.command, [
|
|
2576
|
+
"api",
|
|
2577
|
+
"graphql",
|
|
2578
|
+
"-F",
|
|
2579
|
+
`owner=${input.owner}`,
|
|
2580
|
+
"-F",
|
|
2581
|
+
`name=${input.name}`,
|
|
2582
|
+
"-F",
|
|
2583
|
+
`prNumber=${input.prNumber}`,
|
|
2584
|
+
"-f",
|
|
2585
|
+
"query=query($owner: String!, $name: String!, $prNumber: Int!) { repository(owner:$owner, name:$name) { pullRequest(number:$prNumber) { lastEditedAt editor { login } commits(last: 1) { nodes { commit { oid committedDate } } } } } }"
|
|
2586
|
+
], input.projectRoot);
|
|
2587
|
+
if (response.error)
|
|
2588
|
+
return { value: {}, error: response.error };
|
|
2589
|
+
const data = response.value.data;
|
|
2590
|
+
const repository = data?.repository;
|
|
2591
|
+
const pullRequest = repository?.pullRequest;
|
|
2592
|
+
if (!pullRequest)
|
|
2593
|
+
return { value: {}, error: "GitHub pullRequest provenance response did not include a pullRequest object" };
|
|
2594
|
+
const editor = pullRequest.editor;
|
|
2595
|
+
const commits = pullRequest.commits;
|
|
2596
|
+
const nodes = Array.isArray(commits?.nodes) ? commits.nodes : [];
|
|
2597
|
+
const latestCommitNode = nodes[nodes.length - 1];
|
|
2598
|
+
const latestCommit = latestCommitNode?.commit;
|
|
2599
|
+
return {
|
|
2600
|
+
value: {
|
|
2601
|
+
bodyEditorLogin: typeof editor?.login === "string" ? editor.login : null,
|
|
2602
|
+
bodyLastEditedAt: typeof pullRequest.lastEditedAt === "string" ? pullRequest.lastEditedAt : null,
|
|
2603
|
+
headCommittedDate: typeof latestCommit?.committedDate === "string" ? latestCommit.committedDate : null
|
|
2604
|
+
}
|
|
2605
|
+
};
|
|
2606
|
+
}
|
|
2491
2607
|
async function collectReviewThreads(input) {
|
|
2492
2608
|
const reviewThreads = [];
|
|
2493
2609
|
let afterCursor = null;
|
|
@@ -2565,11 +2681,19 @@ async function collectPrReviewEvidence(input) {
|
|
|
2565
2681
|
const baseRefName = firstString(view, ["baseRefName"]);
|
|
2566
2682
|
const statusCheckRollup = arrayField(view, "statusCheckRollup").map(normalizeStatusCheck).filter((entry) => !!entry);
|
|
2567
2683
|
const reviews = arrayField(view, "reviews").map(normalizeReview).filter((entry) => !!entry);
|
|
2568
|
-
const
|
|
2684
|
+
const provenanceRead = await collectPullRequestProvenance({
|
|
2685
|
+
command: input.command,
|
|
2686
|
+
projectRoot: input.projectRoot,
|
|
2687
|
+
owner: parsed.owner,
|
|
2688
|
+
name: parsed.repo,
|
|
2689
|
+
prNumber: parsed.prNumber
|
|
2690
|
+
});
|
|
2691
|
+
const provenance = provenanceRead.value;
|
|
2692
|
+
const reviewCommentsRead = await runJsonArray(input.command, ["api", `repos/${parsed.repoName}/pulls/${parsed.prNumber}/comments`, "--paginate"], input.projectRoot);
|
|
2569
2693
|
if (reviewCommentsRead.error)
|
|
2570
2694
|
readErrors.push(reviewCommentsRead.error);
|
|
2571
2695
|
const reviewComments = reviewCommentsRead.value.map(normalizeReviewComment).filter((entry) => !!entry);
|
|
2572
|
-
const issueCommentsRead = await runJsonArray(input.command, ["api", `repos/${parsed.repoName}/issues/${parsed.prNumber}/comments`, "--paginate"
|
|
2696
|
+
const issueCommentsRead = await runJsonArray(input.command, ["api", `repos/${parsed.repoName}/issues/${parsed.prNumber}/comments`, "--paginate"], input.projectRoot);
|
|
2573
2697
|
if (issueCommentsRead.error)
|
|
2574
2698
|
readErrors.push(issueCommentsRead.error);
|
|
2575
2699
|
const issueComments = issueCommentsRead.value.map(normalizeIssueComment).filter((entry) => !!entry).filter(relevantIssueComment);
|
|
@@ -2592,12 +2716,7 @@ async function collectPrReviewEvidence(input) {
|
|
|
2592
2716
|
repoName: parsed.repoName,
|
|
2593
2717
|
headSha
|
|
2594
2718
|
});
|
|
2595
|
-
if (checkDetailsRead.error)
|
|
2596
|
-
readErrors.push(checkDetailsRead.error);
|
|
2597
2719
|
greptileCheckDetails = checkDetailsRead.value;
|
|
2598
|
-
if (!checkDetailsRead.error && greptileCheckDetails.length === 0) {
|
|
2599
|
-
readErrors.push("Greptile check details could not be found for the current PR head");
|
|
2600
|
-
}
|
|
2601
2720
|
}
|
|
2602
2721
|
const checksWithGreptileDetails = [...statusCheckRollup, ...greptileCheckDetails];
|
|
2603
2722
|
const shouldCollectConfiguredGreptileApi = input.greptileApi?.enabled !== false;
|
|
@@ -2616,6 +2735,9 @@ async function collectPrReviewEvidence(input) {
|
|
|
2616
2735
|
const evidenceBase = {
|
|
2617
2736
|
title: firstString(view, ["title"]),
|
|
2618
2737
|
body: firstString(view, ["body"]),
|
|
2738
|
+
bodyEditorLogin: provenance.bodyEditorLogin ?? null,
|
|
2739
|
+
bodyLastEditedAt: provenance.bodyLastEditedAt ?? null,
|
|
2740
|
+
headCommittedDate: provenance.headCommittedDate ?? null,
|
|
2619
2741
|
reviews,
|
|
2620
2742
|
changedFileReviewComments: reviewComments,
|
|
2621
2743
|
relevantIssueComments: issueComments,
|
|
@@ -2631,6 +2753,9 @@ async function collectPrReviewEvidence(input) {
|
|
|
2631
2753
|
repoName: parsed.repoName,
|
|
2632
2754
|
title: evidenceBase.title,
|
|
2633
2755
|
body: evidenceBase.body,
|
|
2756
|
+
bodyEditorLogin: evidenceBase.bodyEditorLogin,
|
|
2757
|
+
bodyLastEditedAt: evidenceBase.bodyLastEditedAt,
|
|
2758
|
+
headCommittedDate: evidenceBase.headCommittedDate,
|
|
2634
2759
|
headSha,
|
|
2635
2760
|
headRefName: firstString(view, ["headRefName"]),
|
|
2636
2761
|
baseRefName,
|