@capagents/forgeqe 1.1.1 → 1.1.2
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 +20 -4
- package/README.md +27 -1
- package/docs/user-guide.html +24 -4
- package/evals/prompts/brain/capability-question.yaml +12 -0
- package/evals/prompts/brain/failed-runs-cause.yaml +12 -0
- package/evals/prompts/brain/release-route.yaml +14 -0
- package/evals/prompts/brain/report-request.yaml +13 -0
- package/evals/prompts/brain/secret-request.yaml +9 -0
- package/evals/prompts/explore-charters/lockout.yaml +18 -0
- package/evals/prompts/explore-charters/transfer-limit.yaml +16 -0
- package/evals/prompts/refine/current-release.yaml +26 -0
- package/evals/prompts/refine/missing-item.yaml +10 -0
- package/evals/prompts/refine/question-stays-question.yaml +13 -0
- package/evals/prompts/refine/report-request.yaml +19 -0
- package/forgeqe.example.yaml +39 -0
- package/package.json +1 -1
- package/prompts/README.md +28 -7
- package/prompts/api-scenarios.md +56 -24
- package/prompts/api-tests.md +67 -51
- package/prompts/brain.md +277 -268
- package/prompts/browser-capture.md +8 -7
- package/prompts/browser-case-report.md +13 -12
- package/prompts/browser-checks.md +17 -18
- package/prompts/browser-efficiency.md +4 -1
- package/prompts/browser-error-recovery.md +5 -4
- package/prompts/browser-extra.md +1 -0
- package/prompts/browser-goal-verification.md +8 -7
- package/prompts/browser-skill-select.md +28 -1
- package/prompts/conventions.md +10 -0
- package/prompts/domain-rule.md +17 -14
- package/prompts/eval-judge.md +36 -13
- package/prompts/explore-charters.md +53 -22
- package/prompts/find-tests.md +45 -30
- package/prompts/heal.md +42 -32
- package/prompts/pr-review.md +44 -28
- package/prompts/refine.md +73 -40
- package/prompts/skill-rule.md +4 -1
- package/prompts/test-agent.md +61 -45
- package/prompts/test-reviewer.md +64 -37
- package/prompts/test-rules.md +59 -56
- package/py/forgeqe_engine/browser.py +111 -1
- package/py/forgeqe_engine/capture.py +8 -1
- package/py/forgeqe_engine/checks.py +20 -8
- package/py/forgeqe_engine/prompts.py +9 -0
- package/py/forgeqe_engine/report.py +15 -8
- package/py/forgeqe_engine/service.py +50 -28
- package/py/forgeqe_engine/skills.py +31 -7
- package/src/auth/signin.ts +30 -1
- package/src/bi.ts +899 -0
- package/src/childEnv.ts +4 -0
- package/src/cli/help.ts +10 -2
- package/src/cli/index.ts +114 -10
- package/src/config.ts +149 -0
- package/src/engine/bridge.ts +32 -2
- package/src/evals/prompts.ts +288 -0
- package/src/mcp/server.ts +8 -0
- package/src/mcp/tools.ts +4 -0
- package/src/prompts.ts +6 -3
- package/src/promptstyle.ts +159 -0
- package/src/run.ts +1 -0
- package/src/ste.ts +8 -2
|
@@ -6,7 +6,7 @@ import asyncio
|
|
|
6
6
|
import logging
|
|
7
7
|
from typing import Any, Dict, List, Tuple
|
|
8
8
|
|
|
9
|
-
from .prompts import text as prompt_text
|
|
9
|
+
from .prompts import CONVENTIONS, text as prompt_text
|
|
10
10
|
|
|
11
11
|
logger = logging.getLogger("forgeqe_engine")
|
|
12
12
|
|
|
@@ -14,11 +14,35 @@ MAX_CHOSEN = 3
|
|
|
14
14
|
# The choice runs before the agent, outside the run's own timeout: a model that hangs here costs at most this long.
|
|
15
15
|
SELECT_TIMEOUT_SECONDS = 60.0
|
|
16
16
|
|
|
17
|
-
SELECT_SYSTEM =
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
SELECT_SYSTEM = """
|
|
18
|
+
## Role
|
|
19
|
+
You select playbooks for a browser automation agent.
|
|
20
|
+
|
|
21
|
+
## Objective
|
|
22
|
+
Give the names of the playbooks that the goal of the agent clearly needs.
|
|
23
|
+
|
|
24
|
+
## Inputs
|
|
25
|
+
- The goal of the agent.
|
|
26
|
+
- The playbooks: each has a name and tells how to work with one type of website or widget.
|
|
27
|
+
|
|
28
|
+
## Rules
|
|
29
|
+
- You MUST select a playbook only when the goal clearly needs it. The goal names its site or widget, or does the work of the playbook.
|
|
30
|
+
- You MUST NOT select a playbook only because its name looks related.
|
|
31
|
+
- You MUST give 3 names or fewer.
|
|
32
|
+
- You MUST give an empty list when no playbook fits.
|
|
33
|
+
|
|
34
|
+
## Output
|
|
35
|
+
The names of the selected playbooks, exactly as the list gives them.
|
|
36
|
+
|
|
37
|
+
## Examples
|
|
38
|
+
### Example 1
|
|
39
|
+
Goal: "Book a flight from Paris to Rome on 12 March." Playbooks: date-picker (calendar widgets), salesforce (Salesforce Lightning pages).
|
|
40
|
+
Answer: date-picker. The goal picks a date. Salesforce is not part of the goal.
|
|
41
|
+
|
|
42
|
+
### Example 2
|
|
43
|
+
Goal: "Open the home page and check the title." Playbooks: date-picker, salesforce.
|
|
44
|
+
Answer: an empty list. No playbook fits.
|
|
45
|
+
""".strip()
|
|
22
46
|
|
|
23
47
|
|
|
24
48
|
def _clean(catalog: List[Dict[str, Any]]) -> List[Dict[str, str]]:
|
|
@@ -49,7 +73,7 @@ async def choose_skills(llm: Any, goal: str, catalog: List[Dict[str, Any]]) -> T
|
|
|
49
73
|
listing = "\n".join(f"- {s['name']}: {s['description']}" for s in skills)
|
|
50
74
|
res = await asyncio.wait_for(
|
|
51
75
|
llm.ainvoke(
|
|
52
|
-
[SystemMessage(content=prompt_text("browser-skill-select", SELECT_SYSTEM)), UserMessage(content=f"Goal:\n{goal}\n\nPlaybooks:\n{listing}")],
|
|
76
|
+
[SystemMessage(content=prompt_text("conventions", CONVENTIONS) + "\n\n" + prompt_text("browser-skill-select", SELECT_SYSTEM)), UserMessage(content=f"Goal:\n{goal}\n\nPlaybooks:\n{listing}")],
|
|
53
77
|
output_format=Choice,
|
|
54
78
|
),
|
|
55
79
|
timeout=SELECT_TIMEOUT_SECONDS,
|
package/src/auth/signin.ts
CHANGED
|
@@ -19,7 +19,7 @@ export type AuthIO = {
|
|
|
19
19
|
signal?: AbortSignal
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
export type SystemId = "azure_devops" | "github" | "jira" | "confluence" | "testrail" | "zephyr" | "qtest" | "xray"
|
|
22
|
+
export type SystemId = "azure_devops" | "github" | "jira" | "confluence" | "testrail" | "zephyr" | "qtest" | "xray" | "browserstack" | "testmu"
|
|
23
23
|
|
|
24
24
|
export const SYSTEMS: { id: SystemId; label: string; aliases: string[] }[] = [
|
|
25
25
|
{ id: "azure_devops", label: "Azure DevOps", aliases: ["ado", "azure", "devops", "azdo", "azure-devops"] },
|
|
@@ -30,6 +30,8 @@ export const SYSTEMS: { id: SystemId; label: string; aliases: string[] }[] = [
|
|
|
30
30
|
{ id: "zephyr", label: "Zephyr Scale", aliases: ["zephyr-scale"] },
|
|
31
31
|
{ id: "qtest", label: "qTest", aliases: [] },
|
|
32
32
|
{ id: "xray", label: "Xray Cloud", aliases: [] },
|
|
33
|
+
{ id: "browserstack", label: "BrowserStack", aliases: ["bstack"] },
|
|
34
|
+
{ id: "testmu", label: "TestMu AI", aliases: ["lambdatest", "lambda-test", "lt"] },
|
|
33
35
|
]
|
|
34
36
|
|
|
35
37
|
/** A system id from what the user typed ("ado", "Azure DevOps", "jira"); null when it names none. */
|
|
@@ -47,6 +49,7 @@ const ENV_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/
|
|
|
47
49
|
/** The environment variable names the config reads for each system (forgeqe.yaml can rename them). */
|
|
48
50
|
export function envNames(cfg?: AppConfig) {
|
|
49
51
|
const s = cfg?.sources
|
|
52
|
+
const b = cfg?.browser
|
|
50
53
|
return {
|
|
51
54
|
ado: { url: "ADO_ORG_URL", token: s?.ado?.tokenEnv || "ADO_PAT" },
|
|
52
55
|
jira: { url: "JIRA_URL", email: s?.jira?.emailEnv || "JIRA_EMAIL", token: s?.jira?.tokenEnv || "JIRA_API_TOKEN" },
|
|
@@ -55,6 +58,8 @@ export function envNames(cfg?: AppConfig) {
|
|
|
55
58
|
zephyr: { url: "ZEPHYR_URL", token: s?.zephyr?.tokenEnv || "ZEPHYR_API_TOKEN" },
|
|
56
59
|
qtest: { url: "QTEST_URL", project: "QTEST_PROJECT_ID", token: s?.qtest?.tokenEnv || "QTEST_TOKEN" },
|
|
57
60
|
xray: { url: "XRAY_URL", id: s?.xray.clientIdEnv || "XRAY_CLIENT_ID", secret: s?.xray.clientSecretEnv || "XRAY_CLIENT_SECRET" },
|
|
61
|
+
browserstack: { user: b?.remote?.provider === "browserstack" ? b.remote.usernameEnv : "BROWSERSTACK_USERNAME", key: b?.remote?.provider === "browserstack" ? b.remote.accessKeyEnv : "BROWSERSTACK_ACCESS_KEY" },
|
|
62
|
+
testmu: { user: b?.remote?.provider === "testmu" ? b.remote.usernameEnv : "LT_USERNAME", key: b?.remote?.provider === "testmu" ? b.remote.accessKeyEnv : "LT_ACCESS_KEY" },
|
|
58
63
|
}
|
|
59
64
|
}
|
|
60
65
|
|
|
@@ -376,6 +381,24 @@ async function signInXray(io: AuthIO, cfg?: AppConfig): Promise<SignInResult> {
|
|
|
376
381
|
return finish(io, "xray", { via: "token", env: {}, secrets: { [names.id]: id, [names.secret]: secret } }, "API key")
|
|
377
382
|
}
|
|
378
383
|
|
|
384
|
+
async function signInBrowserStack(io: AuthIO, cfg?: AppConfig): Promise<SignInResult> {
|
|
385
|
+
const names = envNames(cfg).browserstack
|
|
386
|
+
io.openUrl("https://www.browserstack.com/accounts/settings")
|
|
387
|
+
io.say("Your browser shows BrowserStack account settings. Copy your BrowserStack username and access key.")
|
|
388
|
+
const user = await ask(io, "BrowserStack user name", { initial: process.env[names.user] || gitEmail() })
|
|
389
|
+
const key = await ask(io, "Paste the BrowserStack access key", { secret: true })
|
|
390
|
+
return finish(io, "browserstack", { via: "token", env: { [names.user]: user }, secrets: { [names.key]: key }, account: user }, "access key")
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
async function signInTestMu(io: AuthIO, cfg?: AppConfig): Promise<SignInResult> {
|
|
394
|
+
const names = envNames(cfg).testmu
|
|
395
|
+
io.openUrl("https://accounts.lambdatest.com/security")
|
|
396
|
+
io.say("Your browser shows the TestMu AI / LambdaTest security page. Copy your user name and access key.")
|
|
397
|
+
const user = await ask(io, "TestMu AI user name", { initial: process.env[names.user] || gitEmail() })
|
|
398
|
+
const key = await ask(io, "Paste the TestMu AI access key", { secret: true })
|
|
399
|
+
return finish(io, "testmu", { via: "token", env: { [names.user]: user }, secrets: { [names.key]: key }, account: user }, "access key")
|
|
400
|
+
}
|
|
401
|
+
|
|
379
402
|
/** Save one secret by its environment variable name: an LLM API key, a webhook URL, an SMTP or login password. */
|
|
380
403
|
export async function saveNamedSecret(io: AuthIO, name: string): Promise<SignInResult> {
|
|
381
404
|
if (!ENV_NAME.test(name)) return { ok: false, system: name, message: `"${name}" is not an environment variable name.` }
|
|
@@ -429,6 +452,10 @@ export async function signIn(io: AuthIO, system: string | undefined, cfg?: AppCo
|
|
|
429
452
|
return await signInQtest(io, cfg)
|
|
430
453
|
case "xray":
|
|
431
454
|
return await signInXray(io, cfg)
|
|
455
|
+
case "browserstack":
|
|
456
|
+
return await signInBrowserStack(io, cfg)
|
|
457
|
+
case "testmu":
|
|
458
|
+
return await signInTestMu(io, cfg)
|
|
432
459
|
}
|
|
433
460
|
return { ok: false, system: id, message: "Unknown system." }
|
|
434
461
|
} catch (err) {
|
|
@@ -486,6 +513,8 @@ export function authStatus(cfg?: AppConfig): string[] {
|
|
|
486
513
|
row("zephyr", "Zephyr Scale", source([n.zephyr.token])),
|
|
487
514
|
row("qtest", "qTest", cfg?.sources.qtest || process.env[n.qtest.url] ? source([n.qtest.token]) : null),
|
|
488
515
|
row("xray", "Xray Cloud", source([n.xray.id, n.xray.secret])),
|
|
516
|
+
row("browserstack", "BrowserStack", source([n.browserstack.user, n.browserstack.key])),
|
|
517
|
+
row("testmu", "TestMu AI", source([n.testmu.user, n.testmu.key])),
|
|
489
518
|
]
|
|
490
519
|
const extra = Object.keys(saved).filter((k) => k.startsWith("env:")).map((k) => k.slice(4))
|
|
491
520
|
if (extra.length) lines.push(`${"Other secrets".padEnd(14)}${extra.join(", ")}`)
|