@capagents/forgeqe 1.3.2 → 2.0.0

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.
Files changed (115) hide show
  1. package/HOW_TO_USE.md +373 -9
  2. package/README.md +55 -4
  3. package/docs/ARCHITECTURE.md +62 -1
  4. package/docs/CONTAINERS.md +227 -0
  5. package/docs/COST_ARCHITECTURE.md +32 -0
  6. package/docs/DESKTOP-CI.md +87 -0
  7. package/docs/DESKTOP.md +387 -0
  8. package/docs/MOBILE.md +228 -0
  9. package/docs/feature-test-prompts.md +25 -0
  10. package/docs/security-data-flow.html +440 -3
  11. package/docs/user-guide.html +797 -415
  12. package/env.example.yaml +67 -0
  13. package/forgeqe.example.yaml +56 -1
  14. package/llms.example.json +7 -0
  15. package/package.json +4 -1
  16. package/prompts/brain.md +31 -0
  17. package/prompts/find-tests.md +1 -0
  18. package/py/forgeqe_desktop/__init__.py +1 -0
  19. package/py/forgeqe_desktop/__main__.py +3 -0
  20. package/py/forgeqe_desktop/apps.py +80 -0
  21. package/py/forgeqe_desktop/checks.py +168 -0
  22. package/py/forgeqe_desktop/cli.py +90 -0
  23. package/py/forgeqe_desktop/handhooks.py +293 -0
  24. package/py/forgeqe_desktop/handrecord.py +239 -0
  25. package/py/forgeqe_desktop/heal.py +107 -0
  26. package/py/forgeqe_desktop/models.py +208 -0
  27. package/py/forgeqe_desktop/pywinauto_driver.py +217 -0
  28. package/py/forgeqe_desktop/record.py +150 -0
  29. package/py/forgeqe_desktop/replay.py +345 -0
  30. package/py/forgeqe_desktop/service.py +356 -0
  31. package/py/forgeqe_desktop/session.py +60 -0
  32. package/py/forgeqe_desktop/ufo_bridge.py +86 -0
  33. package/py/forgeqe_desktop/ufo_runner.py +305 -0
  34. package/py/forgeqe_desktop/visual.py +112 -0
  35. package/py/forgeqe_desktop/workflow.py +338 -0
  36. package/py/forgeqe_engine/llm.py +54 -4
  37. package/py/forgeqe_engine/model_params.py +169 -0
  38. package/py/forgeqe_mobile/__init__.py +1 -0
  39. package/py/forgeqe_mobile/__main__.py +3 -0
  40. package/py/forgeqe_mobile/appium_driver.py +342 -0
  41. package/py/forgeqe_mobile/apps.py +84 -0
  42. package/py/forgeqe_mobile/cli.py +70 -0
  43. package/py/forgeqe_mobile/explore.py +203 -0
  44. package/py/forgeqe_mobile/hyperexecute.py +261 -0
  45. package/py/forgeqe_mobile/maestro.py +321 -0
  46. package/py/forgeqe_mobile/service.py +332 -0
  47. package/skills/testcontainers/SKILL.md +42 -0
  48. package/src/agents.ts +11 -2
  49. package/src/api/plan.ts +2 -0
  50. package/src/api/write.ts +2 -0
  51. package/src/auth/copilot.ts +130 -0
  52. package/src/auth/signin.ts +54 -1
  53. package/src/brain/index.ts +26 -4
  54. package/src/capabilities/guide.ts +2 -0
  55. package/src/capabilities/registry.ts +110 -2
  56. package/src/capabilities/status.ts +15 -0
  57. package/src/cases/rca.ts +2 -0
  58. package/src/childEnv.ts +4 -1
  59. package/src/cli/args.ts +3 -2
  60. package/src/cli/help.ts +24 -3
  61. package/src/cli/index.ts +47 -3
  62. package/src/code/find.ts +26 -0
  63. package/src/code/loop.ts +4 -1
  64. package/src/code/prompt.ts +2 -1
  65. package/src/code/prreview.ts +2 -0
  66. package/src/code/review.ts +6 -2
  67. package/src/code/runtime.ts +34 -3
  68. package/src/commands.ts +6 -1
  69. package/src/config.ts +159 -3
  70. package/src/containers/command.ts +68 -0
  71. package/src/containers/compose.ts +173 -0
  72. package/src/containers/config.ts +283 -0
  73. package/src/containers/mock.ts +156 -0
  74. package/src/containers/run.ts +362 -0
  75. package/src/containers/tests.ts +80 -0
  76. package/src/context.ts +33 -6
  77. package/src/copilot.ts +155 -0
  78. package/src/desktop/bridge.ts +229 -0
  79. package/src/desktop/command.ts +350 -0
  80. package/src/desktop/edit.ts +217 -0
  81. package/src/desktop/junit.ts +35 -0
  82. package/src/desktop/report.ts +155 -0
  83. package/src/desktop/run.ts +252 -0
  84. package/src/desktop/setup.ts +134 -0
  85. package/src/engine/bridge.ts +12 -5
  86. package/src/engine/setup.ts +3 -3
  87. package/src/environments.ts +566 -0
  88. package/src/evals/domain.ts +1 -0
  89. package/src/explore/charters.ts +2 -0
  90. package/src/guardrails.ts +145 -0
  91. package/src/heal/loop.ts +4 -1
  92. package/src/heal/prompt.ts +2 -1
  93. package/src/init.ts +5 -2
  94. package/src/llm.ts +23 -2
  95. package/src/mcp/attachtools.ts +16 -4
  96. package/src/mcp/captools.ts +27 -0
  97. package/src/mcp/containertools.ts +212 -0
  98. package/src/mcp/desktoptools.ts +334 -0
  99. package/src/mcp/mobiletools.ts +132 -0
  100. package/src/mcp/reporttools.ts +17 -2
  101. package/src/mcp/repotests.ts +22 -3
  102. package/src/mcp/routing.ts +11 -5
  103. package/src/mcp/scope.ts +210 -0
  104. package/src/mcp/server.ts +80 -5
  105. package/src/mcp/tools.ts +38 -1
  106. package/src/mobile/command.ts +245 -0
  107. package/src/mobile/run.ts +226 -0
  108. package/src/pack/pipeline.ts +44 -2
  109. package/src/refine.ts +3 -0
  110. package/src/report/html.ts +3 -0
  111. package/src/report/template.html +48 -48
  112. package/src/run.ts +7 -3
  113. package/src/tokenomics/ledger.ts +3 -1
  114. package/src/tui/App.tsx +23 -0
  115. package/src/usage.ts +1 -1
package/src/run.ts CHANGED
@@ -4,6 +4,7 @@ import { createRequire } from "node:module"
4
4
  import path from "node:path"
5
5
 
6
6
  import { assertGoalUrlsAllowed, hostAllowed, normalizeHttpUrl } from "./guard.ts"
7
+ import { ghAuthToken } from "./cases/github.ts"
7
8
  import { missingKey } from "./llm.ts"
8
9
  import type { AgentEvent, ReplayMode, RunResult } from "./types.ts"
9
10
  import { browserConfigFor, type AppConfig, type BrowserProvider } from "./config.ts"
@@ -21,7 +22,9 @@ import { historyFile, recordQuality } from "./quality/history.ts"
21
22
  import { compareVisual, engineVisual } from "./quality/visual.ts"
22
23
  import { enginePrompts, prompt } from "./prompts.ts"
23
24
  import { knowledgeBlock, readKnowledge, relevantKnowledge } from "./knowledge.ts"
25
+ import { environmentBrief } from "./environments.ts"
24
26
  import { registryMode } from "./registry/index.ts"
27
+ import { guardrailsNotes } from "./guardrails.ts"
25
28
 
26
29
  export type ExecuteOptions = {
27
30
  config: AppConfig
@@ -211,7 +214,7 @@ export async function executeRun(opts: ExecuteOptions): Promise<{ result: RunRes
211
214
  return withReport(out)
212
215
  }
213
216
 
214
- const missing = missingKey(opts.config.llm)
217
+ const missing = missingKey(opts.config.llm, "engine", ghAuthToken)
215
218
  if (missing) throw new Error(missing)
216
219
  // A remote grid's keys are checked here, before the engine starts, rather than failing inside it.
217
220
  if (browser.remote) {
@@ -220,9 +223,10 @@ export async function executeRun(opts: ExecuteOptions): Promise<{ result: RunRes
220
223
  }
221
224
  const log = (text: string) => opts.onEvent?.({ type: "log", level: "info", text })
222
225
  const python = await ensureEngine(log)
223
- // The browser agent's added instructions: forgeqe.yaml's, prompts/browser-extra.md, and the product knowledge that matches the goal.
226
+ // The browser agent's added instructions: forgeqe.yaml's, prompts/browser-extra.md, the product knowledge that matches the goal, and the guardrails.
224
227
  const knowledge = opts.config.knowledge && opts.config.knowledge.browserMaxChars > 0 ? knowledgeBlock(relevantKnowledge(readKnowledge(opts.config.knowledge.dir), goal, opts.config.knowledge.browserMaxChars, 2)) : ""
225
- const extend = [opts.config.prompts.guiderails, opts.config.prompts.stepInstruction, prompt("browser-extra"), knowledge]
228
+ // The team's guardrails come last, so nothing added after them reads as overriding them.
229
+ const extend = [opts.config.prompts.stepInstruction, prompt("browser-extra"), knowledge, environmentBrief(opts.config, "browser"), guardrailsNotes(opts.config) || ""]
226
230
  .map((s) => s.trim())
227
231
  .filter(Boolean)
228
232
  .join("\n\n")
@@ -18,7 +18,7 @@ import { costOf, recordUsage, type Price, type Role } from "../usage.ts"
18
18
  export type Trigger = "tui" | "cli" | "mcp" | "ci" | "monitor" | "schedule"
19
19
 
20
20
  /** What a call was for, from the tools it asked for: no tools is the model thinking or answering. */
21
- export type Purpose = "reasoning" | "planning" | "search" | "file read" | "edit" | "shell" | "browser" | "integration" | "delegation" | "unattributed"
21
+ export type Purpose = "reasoning" | "planning" | "search" | "file read" | "edit" | "shell" | "browser" | "desktop" | "mobile" | "integration" | "delegation" | "unattributed"
22
22
 
23
23
  /** What ForgeQE was doing when the call was made. Set with withUsageScope; inner scopes add to outer ones. */
24
24
  export type UsageScope = {
@@ -130,6 +130,8 @@ export function purposeOf(tools: string[] | undefined): Purpose {
130
130
  const DEFAULT_JOB: Record<Role, string> = {
131
131
  "ForgeQE's agent": "chat",
132
132
  "browser agent": "browser test",
133
+ "desktop agent": "desktop test",
134
+ "mobile agent": "mobile test",
133
135
  Scribe: "write tests",
134
136
  reviewer: "review",
135
137
  judge: "eval",
package/src/tui/App.tsx CHANGED
@@ -15,6 +15,7 @@ import { FAILURE_ACTION, FAILURE_LABEL } from "../cases/failure.ts"
15
15
  import { authStatus, openInBrowser, signIn, signOut, type AuthIO } from "../auth/signin.ts"
16
16
  import { HELP, parseCommand } from "../commands.ts"
17
17
  import { loadConfig, selectProfile, type AppConfig, type BrowserProvider } from "../config.ts"
18
+ import { appsText, exportEnvironment, withEnvironment } from "../environments.ts"
18
19
  import { TOOLS, type AskQuestion, type ToolEvent } from "../mcp/tools.ts"
19
20
  import type { executeRun } from "../run.ts"
20
21
  import { bundleLogs, logEvent } from "../sessionLog.ts"
@@ -1443,6 +1444,28 @@ export function App({ options }: { options: TuiOptions }) {
1443
1444
  }
1444
1445
  return
1445
1446
  }
1447
+ if (cmd.type === "env") {
1448
+ try {
1449
+ const cfg = configRef.current
1450
+ if (!cfg) throw new Error("Config is not loaded.")
1451
+ if (!cmd.name) {
1452
+ push("sys", appsText(cfg))
1453
+ return
1454
+ }
1455
+ configRef.current = withEnvironment(cfg, cmd.name)
1456
+ exportEnvironment(configRef.current)
1457
+ // Reloads in this session (/init, a profile switch) keep the environment.
1458
+ process.env.FORGEQE_ENV = cmd.name
1459
+ push("sys", `environment ${cmd.name}`)
1460
+ if (brainRef.current) {
1461
+ closeBrain()
1462
+ push("sys", "The agent switches to this environment on your next message and keeps the conversation.")
1463
+ }
1464
+ } catch (err) {
1465
+ push("err", (err as Error).message)
1466
+ }
1467
+ return
1468
+ }
1446
1469
  if (cmd.type === "init") {
1447
1470
  // forgeqe init in this folder, then the session reads what it wrote: the start screen updates at once.
1448
1471
  try {
package/src/usage.ts CHANGED
@@ -98,7 +98,7 @@ export function usageLine(usage: Usage, cost?: Cost | null): string {
98
98
  // The meter: every model session reports here, by role
99
99
  // ---------------------------------------------------------------------------------------------
100
100
 
101
- export type Role = "ForgeQE's agent" | "browser agent" | "Scribe" | "reviewer" | "judge" | "refiner" | "root-cause analyst"
101
+ export type Role = "ForgeQE's agent" | "browser agent" | "desktop agent" | "mobile agent" | "Scribe" | "reviewer" | "judge" | "refiner" | "root-cause analyst"
102
102
 
103
103
  type Entry = { usage: Usage; cost?: Cost; priced: boolean }
104
104