Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
fdf0236
fix(windows): async ACL harden for response-state writes (#612)
Wibias Jul 28, 2026
2bbac19
docs: a dev merge is not done until dev2-go carries it too
lidge-jun Jul 29, 2026
a47aa3f
docs: harden and scope agent guidance (#583)
Wibias Jul 29, 2026
257c626
feat(videos): add Grok video bridge for non-OpenAI models (#582)
tizerluo Jul 29, 2026
20cdc0d
fix: scope Spark cooldowns to its native quota (#599)
akrock Jul 29, 2026
f492f7d
feat(gui): show reset credit expiration time (#613)
ACJF00 Jul 29, 2026
868a9c4
fix(adapters): neutralize Codex CLI 0.145 identity wording (#638)
Wibias Jul 29, 2026
c9bed7c
feat(codex): add account namespace foundation
chrisae9 Jul 26, 2026
f0867e8
feat(codex): account pause controls and bulk exhaustion (#667)
Wibias Jul 29, 2026
f7be351
docs: require needs-go-port for deferred Go ports (#672)
Wibias Jul 29, 2026
dcaede6
fix(cursor): steer Windows bridge shell away from PS 5.1 syntax loops…
Wibias Jul 29, 2026
a4fb284
fix(gui): dedupe dashboard API token prompts on concurrent 401s (#651)
Wibias Jul 29, 2026
14dde56
fix(ci): harden PR target draft conversion and issue-quality Version …
Wibias Jul 29, 2026
9d181fc
fix(catalog): accept Together top-level /models arrays (#639)
Wibias Jul 29, 2026
e6169b0
fix(cli): status/doctor use identity-verified live proxy (#642)
Wibias Jul 29, 2026
f19a73c
fix(catalog): omit bare OpenAI models without openai provider (#643)
Wibias Jul 29, 2026
2421929
merge(dev): resolve #645 conflict with account namespaces
Wibias Jul 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .github/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# GitHub automation instructions

This file applies to `.github/` and inherits the repository-wide rules in `/AGENTS.md`.

## Security boundary

Every workflow, ownership, branch-enforcement, release, or repository-automation
change requires explicit security review under `MAINTAINERS.md`.

## Workflow rules

- Grant the minimum required `permissions`.
- Pin third-party actions to immutable full commit SHAs.
- Preserve the human-readable version comment beside each pinned action.
- Do not run untrusted pull-request code with secrets or write permissions.
- Treat `pull_request_target`, workflow dispatch, reusable workflows, artifacts, caches, and generated command input as trust boundaries.
- Do not broaden triggers, write permissions, token exposure, release eligibility, or publish capability without an explicit task requirement.
- Preserve cross-platform coverage where the workflow currently promises Linux, macOS, and Windows behavior.
- Keep branch-enforcement text synchronized with `AGENTS.md`, `MAINTAINERS.md`, and the public contributing guide.

## Validation

- Inspect the complete workflow diff, including event triggers, permissions, conditions, interpolation, and shell behavior.
- Run the local commands represented by changed workflow steps where possible.
- Run `bun run prepush` for CI, release, dependency, packaging, or cross-platform workflow changes.
- Do not claim the workflow itself passed until GitHub Actions reports success for the exact commit.
2 changes: 2 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,7 @@
/src/server/management-api.ts @lidge-jun @Ingwannu

# Governance and security policy
/AGENTS.md @lidge-jun @Ingwannu
**/AGENTS.md @lidge-jun @Ingwannu
/MAINTAINERS.md @lidge-jun @Ingwannu
/SECURITY.md @lidge-jun @Ingwannu
46 changes: 46 additions & 0 deletions .github/scripts/enforce-pr-target.test.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
"use strict";

const fs = require("node:fs");
const path = require("node:path");
const { describe, it } = require("node:test");
const assert = require("node:assert/strict");

describe("enforce-pr-target workflow", () => {
const workflowPath = path.join(__dirname, "../workflows/enforce-pr-target.yml");
const workflow = fs.readFileSync(workflowPath, "utf8");

it("uses pull_request_target without checking out PR head code", () => {
assert.match(workflow, /pull_request_target:/);
assert.doesNotMatch(
workflow,
/actions\/checkout@/,
"wrong-branch enforcer must not check out untrusted PR code",
);
});

it("grants contents:write so draft GraphQL mutations work with GITHUB_TOKEN", () => {
// convertPullRequestToDraft / markPullRequestReadyForReview fail with
// "Resource not accessible by integration" when contents stays unset/read
// (seen on #626). Assert the real permissions block, not comment text
// that also mentions these scopes.
const permissionsBlock = workflow.match(/^permissions:\n((?:[ \t]+.+\n)+)/m);
assert.ok(permissionsBlock, "workflow must declare a top-level permissions block");
const lines = permissionsBlock[1]
.split("\n")
.map((line) => line.trim())
.filter(Boolean)
.sort();
assert.deepEqual(lines, ["contents: write", "pull-requests: write"]);
});

it("fails the required check on a wrong base even if draft conversion fails", () => {
assert.match(workflow, /core\.setFailed\(/);
assert.match(workflow, /draftConversionFailed/);
assert.match(workflow, /Could not convert pull request to draft/);
});

it("soft-fails ready-for-review restoration the same way", () => {
assert.match(workflow, /readyConversionFailed/);
assert.match(workflow, /Could not mark pull request ready for review/);
});
});
104 changes: 90 additions & 14 deletions .github/scripts/issue-quality.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,29 @@ function unwrapSingleEnclosingFence(text) {
return match[2];
}

function isPlaceholderOnlyValue(raw) {
if (typeof raw !== "string") return false;
/**
* Shared strip/trim/unwrap used by placeholder and unusable-stand-in matchers.
* Returns null when the value is absent after normalisation.
*/
function normalizeRawSectionValue(raw) {
if (typeof raw !== "string") return null;
let value = raw.replace(/<!--[\s\S]*?-->/g, "").trim();
if (!value) return false;
if (!value) return null;

// A lone fenced block whose entire body is a placeholder is still placeholder
// text (e.g. ```text\nN/A\n```), not a real example.
// A lone fenced block whose entire body is a stand-in is still a stand-in
// (e.g. ```text\nN/A\n```), not a real example.
const unwrapped = unwrapSingleEnclosingFence(value);
if (unwrapped !== null) {
value = unwrapped.trim();
if (!value) return false;
if (!value) return null;
}

return value;
}

function isPlaceholderOnlyValue(raw) {
const value = normalizeRawSectionValue(raw);
if (value === null) return false;
return PLACEHOLDER_ONLY_RE.test(value);
}

Expand Down Expand Up @@ -398,6 +408,20 @@ function isPlaceholder(text) {
return isPlaceholderOnlyValue(text);
}

/**
* True when Version is an "I don't know" stand-in rather than an install id.
* Kept separate from PLACEHOLDER_ONLY_RE so legacy N/A / No response soft-pass
* behaviour is unchanged.
*/
const UNUSABLE_VERSION_RE =
/^[\s_*~`]*(?:unknown|unkown|uknown|don'?t\s+know|do\s+not\s+know|idk|dunno|not\s+sure|unsure|\?+|모름|잘\s*모름|모르겠(?:습니다|음)?|不明|わからない|分からない|不知道|不清楚|keine\s+ahnung|wei[sß]{1,2}\s+nicht)[\s_*~`]*[.!?]*$/i;

function isUnusableVersion(raw) {
const value = normalizeRawSectionValue(raw);
if (value === null) return false;
return UNUSABLE_VERSION_RE.test(value);
}

const CJK_RE =
/[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]/gu;

Expand Down Expand Up @@ -433,6 +457,16 @@ function isTooTerseFeatureSection(text) {
return true;
}

/**
* Bug Reproduction needs steps or concrete signals. A title-like phrase with
* no commands, paths, digits, or product keywords is not actionable.
*/
function isTooTerseBugReproduction(text) {
if (isEmpty(text) || isPlaceholder(text)) return false;
if (hasConcreteDetail(text)) return false;
return countWords(text) < 12;
}

/**
* Check if raw section text is a placeholder-only variant without relying on
* clean() first. Used to distinguish intentionally blank optional fields
Expand Down Expand Up @@ -628,6 +662,8 @@ function validateIssue(issue) {
const repro = extractSection(body, "Reproduction");
const version = extractSection(body, "Version");
const os = extractSection(body, "Operating system") ?? extractSection(body, "OS");
// New Bug report template always includes Client or integration.
const isNewBugForm = extractSection(body, "Client or integration") !== null;

if (isEmpty(summary) && isEmpty(repro)) {
// Soft-pass substantial non-English / freeform structured reports once
Expand Down Expand Up @@ -655,17 +691,56 @@ function validateIssue(issue) {
if (isEmpty(repro)) {
reasons.push("Reproduction is empty.");
guidance.push("List the exact steps to reproduce the problem.");
} else if (!softPass && isTooTerseBugReproduction(repro)) {
reasons.push("Reproduction is too vague to act on.");
guidance.push("List exact steps, commands, and the observed failure — not only a short phrase.");
}
Comment on lines +694 to 697

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Dead guard: !softPass can never be false in this else branch.

softPass is only ever set inside the sibling if (isEmpty(summary) && isEmpty(repro)) branch at Line 668, and no kind === "bug" code before Line 694 can set it. Inside this else, softPass is therefore always false, so the extra condition is inert and misleads readers into thinking a soft-passing report can reach the terse-reproduction check. Drop it (the later !softPass guards at Lines 701/716/734 are meaningful because they run outside the branch).

♻️ Proposed simplification
-      } else if (!softPass && isTooTerseBugReproduction(repro)) {
+      } else if (isTooTerseBugReproduction(repro)) {
         reasons.push("Reproduction is too vague to act on.");
         guidance.push("List exact steps, commands, and the observed failure — not only a short phrase.");
       }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} else if (!softPass && isTooTerseBugReproduction(repro)) {
reasons.push("Reproduction is too vague to act on.");
guidance.push("List exact steps, commands, and the observed failure — not only a short phrase.");
}
} else if (isTooTerseBugReproduction(repro)) {
reasons.push("Reproduction is too vague to act on.");
guidance.push("List exact steps, commands, and the observed failure — not only a short phrase.");
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/scripts/issue-quality.cjs around lines 694 - 697, Remove the
redundant !softPass condition from the terse reproduction check in the
bug-report branch, changing the guard around isTooTerseBugReproduction(repro) to
rely only on that predicate. Preserve the existing reasons and guidance
behavior, and leave the later !softPass guards unchanged.

}

// Required environment fields removed after submission.
// Only fire when the headings exist in the body (new form). Legacy bug
// reports never had Version or OS fields, so null means absent, not removed.
// Skip when the raw value is a "No response" placeholder -- the old form had
// both fields as optional, so legacy issues legitimately contain those headings
// with the GitHub placeholder. Only close when the field was actively cleared.
if (!softPass && version !== null && os !== null && isEmpty(version) && isEmpty(os) &&
!isRawPlaceholder(version) && !isRawPlaceholder(os)) {
// Version "Unknown" / "모름" / "idk" is never actionable, on any form.
if (!softPass && version !== null && isUnusableVersion(version)) {
reasons.push("Version is missing or unknown.");
guidance.push("Report the installed `@bitkyc08/opencodex` version (for example `2.7.42`) or a commit SHA from `ocx --version`.");
} else if (
!softPass &&
isNewBugForm &&
version !== null &&
(isEmpty(version) || isRawPlaceholder(version))
) {
// New form requires Version. Legacy N/A / No response soft-pass stays
// only for bodies without Client or integration.
reasons.push("Version is missing.");
guidance.push("Add your OpenCodex version so we can reproduce the environment.");
}

if (!softPass && isNewBugForm && os !== null && isUnusableVersion(os)) {
reasons.push("Operating system is missing or unknown.");
guidance.push("Add your OS name and version (for example Windows 11 24H2).");
} else if (
!softPass &&
isNewBugForm &&
os !== null &&
(isEmpty(os) || isRawPlaceholder(os))
) {
reasons.push("Operating system is missing.");
guidance.push("Add your OS name and version (for example Windows 11 24H2).");
}
Comment on lines +716 to +727

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

isUnusableVersion(os) is a correct-but-misnamed reuse; also the two OS branches emit identical guidance.

Line 716 classifies the Operating-system field with a matcher named for Version. Behaviour is right (Unknown, 모름, ? are unusable for any environment field, and tests/.../issue-quality.test.cjs Line 827 covers it), but the name will invite a future edit to UNUSABLE_VERSION_RE that silently changes OS validation. Consider exporting a neutrally named alias (for example isUnknownStandIn) and using it for both fields, keeping isUnusableVersion as a thin wrapper so the existing test import at .github/scripts/issue-quality.test.cjs Line 19 stays valid.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/scripts/issue-quality.cjs around lines 716 - 727, Add and export a
neutral helper such as isUnknownStandIn for shared unknown-value matching, use
it in the OS validation branch alongside the existing version validation, and
retain isUnusableVersion as a thin compatibility wrapper so current imports and
behavior remain unchanged. Consolidate the two OS branches’ identical reason and
guidance handling without changing validation outcomes.


// Required environment fields removed after submission on bodies that are
// not the new form (no Client or integration). Legacy reports never had
// Version or OS fields, so null means absent, not removed. Skip when the
// raw value is a "No response" placeholder — the old form had both fields
// as optional. Only close when the field was actively cleared.
if (
!softPass &&
!isNewBugForm &&
version !== null &&
os !== null &&
isEmpty(version) &&
isEmpty(os) &&
!isRawPlaceholder(version) &&
!isRawPlaceholder(os)
) {
reasons.push("Version and Operating system are both missing.");
guidance.push("Add your OpenCodex version and OS so we can reproduce the environment.");
}
Expand Down Expand Up @@ -873,6 +948,7 @@ module.exports = {
isPlaceholderOnlyValue,
isPlaceholder,
isRawPlaceholder,
isUnusableVersion,
countWords,
hasConcreteDetail,
labelForKind,
Expand Down
Loading
Loading