@capagents/forgeqe 2.3.5 → 2.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/HOW_TO_USE.md +1 -1
- package/dist/forgeqe.js +613 -613
- package/docs/user-guide.html +1 -1
- package/forgeqe.example.yaml +8 -4
- package/package.json +1 -1
- package/py/forgeqe_desktop/__main__.py +7 -2
- package/py/forgeqe_engine/__main__.py +6 -2
- package/py/forgeqe_engine/stdio.py +38 -0
- package/py/forgeqe_mobile/__main__.py +7 -2
package/docs/user-guide.html
CHANGED
|
@@ -596,7 +596,7 @@ bun src/index.ts init --global --profile azure-gpt4o
|
|
|
596
596
|
</tbody>
|
|
597
597
|
</table></div>
|
|
598
598
|
<p>Copilot runs one SDK session per model request, with none of Copilot's own tools and your request's system prompt in place of Copilot's. The agent's tools are offered to the model as the session's only tools; when it calls one, the gateway ends the turn and hands the call back to ForgeQE's agent runtime, which runs it (with the same permissions as on any other model) and sends the result with the next request. The gateway itself runs nothing.</p>
|
|
599
|
-
<p><strong>With Copilot and a key-based model both in <code>llms.json</code></strong>, Copilot comes first: a sign-in makes it the default, and without a <code>"default"</code> it is the active profile. Before a model is used, ForgeQE checks that it works (its sign-in or key, and one live call, kept for a day once it passes). If Copilot does not work (not signed in, the Copilot SDK missing, the model not on your seat), the other model is used and ForgeQE says why and how to fix Copilot; if a key-based model you made the default does not work, Copilot is tried first. A model that fails its check is never used, and when none works the error lists each one with what to do. <code>--profile</code> turns
|
|
599
|
+
<p><strong>With Copilot and a key-based model both in <code>llms.json</code></strong>, Copilot comes first: a sign-in makes it the default, and without a <code>"default"</code> it is the active profile. Before a model is used, ForgeQE checks that it works (its sign-in or key, and one live call, kept for a day once it passes). If Copilot does not work (not signed in, the Copilot SDK missing, the model not on your seat), the other model is used and ForgeQE says why and how to fix Copilot; if a key-based model you made the default does not work, Copilot is tried first. A model that fails its check is never used, and when none works the error lists each one with what to do. This happens with <code>llm.routing</code> off too: routing picks a model for each kind of work, the fallback only replaces one that does not work. <code>--profile</code> turns it off for one run, and <code>llm.fallback: false</code> in <code>forgeqe.yaml</code> for good: the model you chose is used as it is, and its own error is shown. Each request is billed to the seat (GitHub AI credits or premium requests, by your plan), and GitHub may rate-limit frequent automated requests. If a model is refused, check that your organization's Copilot policies allow it. ForgeQE shows no cost for a <code>github_copilot</code> profile unless you give it a <code>price</code>.</p>
|
|
600
600
|
<p><strong>In pipelines.</strong> <code>forgeqe suite ... --no-code</code> uses only the browser engine. Set <code>COPILOT_GITHUB_TOKEN</code> to a fine-grained token with the Copilot Requests permission, stored as a pipeline secret. GitHub also documents the workflow's own <code>GITHUB_TOKEN</code> with <code>permissions: copilot-requests: write</code> for organization repositories whose Copilot policy allows it (<a href="https://docs.github.com/en/copilot/how-tos/copilot-sdk/auth/server-to-server-tokens" rel="noopener">server tokens</a>). A Copilot seat belongs to one person: prefer an organization-billed token over signing a shared pipeline in to someone's seat.</p>
|
|
601
601
|
<p>GitHub Models, which ForgeQE used for the engine before, was <a href="https://github.blog/changelog/2026-07-30-github-models-is-now-retired/" rel="noopener">retired by GitHub</a> on July 30, 2026. A profile that still has <code>engine_model: "openai/..."</code> or <code>github_org</code> needs updating: drop <code>github_org</code>, and use a Copilot model id. A profile whose endpoint is GitHub Models (<code>models.github.ai</code>) is never called: remove it from <code>llms.json</code>. Copilot needs no <code>GITHUB_TOKEN</code> or <code>GH_TOKEN</code>; ForgeQE does not pass them to Copilot.</p>
|
|
602
602
|
<p><strong>When something goes wrong.</strong></p>
|
package/forgeqe.example.yaml
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
llm:
|
|
2
2
|
models_file: ./llms.json
|
|
3
3
|
# active: azure-gpt4o # profile to use; default: "default" in llms.json (Tab switches in the shell)
|
|
4
|
-
# The model router
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
|
|
4
|
+
# The model router. off (the default): the active profile for everything. auto: each area of work gets the cheapest
|
|
5
|
+
# profile in llms.json that is good enough for it; ForgeQE rates the models once with a few model calls
|
|
6
|
+
# (.forgeqe/model-profile.json), worth it with several models of different price. shadow: decide and log only.
|
|
7
|
+
# forgeqe models --routes shows the picks. --profile always overrides it.
|
|
8
|
+
routing: off
|
|
9
|
+
# A model that does not work (no key, a refused sign-in, a failed check) is replaced by one that does, Copilot
|
|
10
|
+
# first, and ForgeQE says why. false: keep the model and show its own error.
|
|
11
|
+
# fallback: true
|
|
8
12
|
# routes: # optional: the profiles an area may use (one pins it); auto: any of them
|
|
9
13
|
# browser: [azure-gpt4o]
|
|
10
14
|
# coding: auto
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@capagents/forgeqe",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.7",
|
|
4
4
|
"description": "ForgeQE: a browser agent CLI that runs goals and test cases in local Chrome or Chromium, or BrowserStack and TestMu AI/LambdaTest when configured, and turns passing runs into tests in your repo",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"repository": {
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
from forgeqe_engine.
|
|
1
|
+
from forgeqe_engine.stdio import take_stdin
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
# Before the service module binds sys.stdin as a default argument.
|
|
4
|
+
take_stdin()
|
|
5
|
+
|
|
6
|
+
from forgeqe_engine.systemcerts import use_system_certificates # noqa: E402
|
|
7
|
+
|
|
8
|
+
from .cli import main # noqa: E402
|
|
4
9
|
|
|
5
10
|
use_system_certificates()
|
|
6
11
|
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
from forgeqe_engine.
|
|
1
|
+
from forgeqe_engine.stdio import take_stdin
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
take_stdin()
|
|
4
|
+
|
|
5
|
+
from forgeqe_engine.systemcerts import use_system_certificates # noqa: E402
|
|
6
|
+
|
|
7
|
+
from .cli import main # noqa: E402
|
|
4
8
|
|
|
5
9
|
use_system_certificates()
|
|
6
10
|
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""
|
|
2
|
+
The pipe ForgeQE writes to (a request, then "stop"), kept for this process alone.
|
|
3
|
+
|
|
4
|
+
A run reads that pipe on a thread while it starts other programs: Chrome, the browser's installer, Copilot's runtime,
|
|
5
|
+
an app under test. Each of them inherited the pipe as its stdin. On Windows a run's browser then did not start until a
|
|
6
|
+
line arrived on the pipe, so it opened only when the user pressed Stop. The programs a process starts now get an empty
|
|
7
|
+
stdin (the null device), and the process reads its own copy of the pipe.
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import os
|
|
12
|
+
import sys
|
|
13
|
+
|
|
14
|
+
STD_INPUT_HANDLE = -10
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def take_stdin() -> None:
|
|
18
|
+
"""Moves stdin to a private copy (sys.stdin reads it) and makes the null device the stdin children inherit.
|
|
19
|
+
|
|
20
|
+
Call it first in an entry point, before a module binds sys.stdin (a default argument, say)."""
|
|
21
|
+
try:
|
|
22
|
+
fd = sys.stdin.fileno()
|
|
23
|
+
except (AttributeError, OSError, ValueError):
|
|
24
|
+
return # no stdin (pythonw, a closed stdin): nothing for a child to inherit
|
|
25
|
+
encoding = getattr(sys.stdin, "encoding", None) or "utf-8"
|
|
26
|
+
private = os.dup(fd) # not inheritable (PEP 446): no child gets this one
|
|
27
|
+
nul = os.open(os.devnull, os.O_RDONLY)
|
|
28
|
+
try:
|
|
29
|
+
os.dup2(nul, fd)
|
|
30
|
+
finally:
|
|
31
|
+
os.close(nul)
|
|
32
|
+
if os.name == "nt":
|
|
33
|
+
# Windows children take their stdin from the standard handle, which dup2 does not always move.
|
|
34
|
+
import ctypes
|
|
35
|
+
import msvcrt
|
|
36
|
+
|
|
37
|
+
ctypes.windll.kernel32.SetStdHandle(STD_INPUT_HANDLE, msvcrt.get_osfhandle(fd))
|
|
38
|
+
sys.stdin = os.fdopen(private, "r", encoding=encoding, errors="replace")
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
from forgeqe_engine.
|
|
1
|
+
from forgeqe_engine.stdio import take_stdin
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
# Before the service module binds sys.stdin as a default argument.
|
|
4
|
+
take_stdin()
|
|
5
|
+
|
|
6
|
+
from forgeqe_engine.systemcerts import use_system_certificates # noqa: E402
|
|
7
|
+
|
|
8
|
+
from .cli import main # noqa: E402
|
|
4
9
|
|
|
5
10
|
use_system_certificates()
|
|
6
11
|
|