lazycodex-ai 5.0.0-beta.62 → 5.0.0-beta.64
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/cli/doctor/checks/browser-provider.d.ts +2 -0
- package/dist/cli/index.js +70 -41
- package/dist/cli-node/index.js +70 -41
- package/package.json +2 -6
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +4 -2
- package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
- package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
- package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
- package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/rules/package.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/agents/plan.toml +2 -2
- package/packages/omo-codex/plugin/components/ultrawork/dist/cli.js +5 -4
- package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/src/directive-content.ts +1 -1
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/directive.md +1 -1
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/CHANGELOG.md +13 -0
- package/packages/omo-codex/plugin/components/ulw-loop/directive.md +5 -4
- package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-codex-validation.d.ts +7 -5
- package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-codex-validation.js +7 -2
- package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint.js +7 -4
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +931 -442
- package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-snapshot.d.ts +6 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-snapshot.js +20 -12
- package/packages/omo-codex/plugin/components/ulw-loop/dist/domain-types.d.ts +8 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/driver-objective-ack.d.ts +4 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/driver-objective-ack.js +18 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/evidence-artifacts.d.ts +1 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/evidence-artifacts.js +37 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/evidence.d.ts +1 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/evidence.js +24 -12
- package/packages/omo-codex/plugin/components/ulw-loop/dist/ledger.d.ts +4 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/ledger.js +34 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/paths.d.ts +1 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/paths.js +6 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-commit.d.ts +19 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-commit.js +107 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-crud.d.ts +6 -2
- package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-crud.js +54 -25
- package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-goal-factory.d.ts +10 -3
- package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-goal-factory.js +38 -31
- package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-io.d.ts +9 -7
- package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-io.js +105 -114
- package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-log.d.ts +15 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-log.js +86 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-missing-recovery.d.ts +5 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-missing-recovery.js +15 -3
- package/packages/omo-codex/plugin/components/ulw-loop/dist/review-blockers.js +11 -8
- package/packages/omo-codex/plugin/components/ulw-loop/dist/sdk/factory.js +28 -12
- package/packages/omo-codex/plugin/components/ulw-loop/dist/sdk/manifest.d.ts +335 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/sdk/manifest.js +109 -10
- package/packages/omo-codex/plugin/components/ulw-loop/dist/sdk/types.d.ts +3 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/spawn-budget-io.d.ts +5 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/spawn-budget-io.js +67 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/spawn-guard.js +8 -62
- package/packages/omo-codex/plugin/components/ulw-loop/dist/state-lock.d.ts +11 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/state-lock.js +120 -88
- package/packages/omo-codex/plugin/components/ulw-loop/dist/status-next-actions.d.ts +3 -2
- package/packages/omo-codex/plugin/components/ulw-loop/dist/status-next-actions.js +18 -10
- package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-batch.js +4 -4
- package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-mutations.d.ts +3 -2
- package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-mutations.js +4 -4
- package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-types.d.ts +9 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/steering.d.ts +3 -2
- package/packages/omo-codex/plugin/components/ulw-loop/dist/steering.js +11 -11
- package/packages/omo-codex/plugin/components/ulw-loop/dist/stop-resume-hook.js +8 -17
- package/packages/omo-codex/plugin/components/ulw-loop/dist/success-criteria-input.d.ts +9 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/success-criteria-input.js +41 -0
- package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
- package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +2 -2
- package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint-codex-validation.ts +15 -3
- package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint.ts +7 -4
- package/packages/omo-codex/plugin/components/ulw-loop/src/codex-goal-snapshot.ts +26 -14
- package/packages/omo-codex/plugin/components/ulw-loop/src/domain-types.ts +8 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/driver-objective-ack.ts +20 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/evidence-artifacts.ts +45 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/evidence.ts +23 -16
- package/packages/omo-codex/plugin/components/ulw-loop/src/ledger.ts +33 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/paths.ts +7 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/plan-commit.ts +124 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/plan-crud.ts +67 -54
- package/packages/omo-codex/plugin/components/ulw-loop/src/plan-goal-factory.ts +63 -31
- package/packages/omo-codex/plugin/components/ulw-loop/src/plan-io.ts +126 -140
- package/packages/omo-codex/plugin/components/ulw-loop/src/plan-log.ts +84 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/plan-missing-recovery.ts +26 -3
- package/packages/omo-codex/plugin/components/ulw-loop/src/review-blockers.ts +11 -7
- package/packages/omo-codex/plugin/components/ulw-loop/src/sdk/factory.ts +47 -12
- package/packages/omo-codex/plugin/components/ulw-loop/src/sdk/manifest.ts +124 -10
- package/packages/omo-codex/plugin/components/ulw-loop/src/sdk/types.ts +3 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/spawn-budget-io.ts +59 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/spawn-guard.ts +8 -59
- package/packages/omo-codex/plugin/components/ulw-loop/src/state-lock.ts +129 -111
- package/packages/omo-codex/plugin/components/ulw-loop/src/status-next-actions.ts +19 -10
- package/packages/omo-codex/plugin/components/ulw-loop/src/steering-batch.ts +4 -4
- package/packages/omo-codex/plugin/components/ulw-loop/src/steering-mutations.ts +5 -4
- package/packages/omo-codex/plugin/components/ulw-loop/src/steering-types.ts +13 -1
- package/packages/omo-codex/plugin/components/ulw-loop/src/steering.ts +12 -10
- package/packages/omo-codex/plugin/components/ulw-loop/src/stop-resume-hook.ts +8 -16
- package/packages/omo-codex/plugin/components/ulw-loop/src/success-criteria-input.ts +56 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/codex-goal-snapshot.test.ts +37 -1
- package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/barrier.ts +13 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/commit-child.ts +65 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/commit-cli-qa.mjs +76 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/commit-crash.ts +23 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/commit-mutations.mjs +58 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/commit-writer.ts +65 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/lease-clock.ts +23 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/plan-commit-log.test.ts +252 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/plan-commit-recovery.test.ts +190 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/plan-io.test.ts +50 -35
- package/packages/omo-codex/plugin/components/ulw-loop/test/plan-log-newest.test.ts +72 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/sdk-completed-plan.test.ts +43 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/sdk-driver-states.test.ts +33 -4
- package/packages/omo-codex/plugin/components/ulw-loop/test/sdk-dx.test.ts +219 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/sdk-session-context.test.ts +21 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/state-lock.test.ts +92 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/status-next-actions-surface.test.ts +133 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/steering-batch.test.ts +12 -12
- package/packages/omo-codex/plugin/components/ulw-loop/test/steering.test.ts +4 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/package-lock.json +12 -12
- package/packages/omo-codex/plugin/package.json +1 -1
- package/packages/omo-codex/plugin/scripts/sync-skills.d.mts +4 -0
- package/packages/omo-codex/plugin/scripts/sync-skills.mjs +15 -7
- package/packages/omo-codex/plugin/skills/ast-grep/SKILL.md +12 -0
- package/packages/omo-codex/plugin/skills/debugging/references/tools/playwright-cli.md +79 -161
- package/packages/omo-codex/plugin/skills/frontend/SKILL.md +1 -1
- package/packages/omo-codex/plugin/skills/frontend/references/design/aside.md +1 -1
- package/packages/omo-codex/plugin/skills/frontend/references/design/clone-from-url.md +1 -1
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/lighthouse-audit.py +2 -2
- package/packages/omo-codex/plugin/skills/review-work/SKILL.md +1 -1
- package/packages/omo-codex/plugin/skills/ultimate-browsing/ATTRIBUTION.md +1 -82
- package/packages/omo-codex/plugin/skills/ultimate-browsing/SKILL.md +17 -29
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/AGENTS.md +2 -1
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/templates/package.json +2 -2
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/templates/playwright_mobile_chrome.js +5 -2
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/templates/playwright_real_chrome.js +8 -6
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_playwright_stealth.py +86 -0
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_playwright_templates.py +4 -7
- package/packages/omo-codex/plugin/skills/ultimate-browsing/references/chrome-stealth.md +86 -100
- package/packages/omo-codex/plugin/skills/ultimate-browsing/references/insane-search/README.md +9 -3
- package/packages/omo-codex/plugin/skills/ultimate-browsing/references/insane-search/playwright.md +16 -13
- package/packages/omo-codex/plugin/skills/ultimate-browsing/scripts/extract_cookies.py +3 -3
- package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +5 -4
- package/packages/omo-codex/plugin/skills/ulw-execute/SKILL.md +1 -1
- package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +2 -2
- package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +3 -3
- package/packages/omo-codex/plugin/skills/visual-qa/AGENTS.md +1 -1
- package/packages/omo-codex/plugin/skills/visual-qa/SKILL.md +1 -1
- package/packages/omo-codex/plugin/skills/visual-qa/references/browser-setup.md +75 -0
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +6 -4
- package/packages/prompts-core/prompts/ultrawork/codex.md +5 -4
- package/packages/shared-skills/skills/ast-grep/SKILL.md +12 -0
- package/packages/shared-skills/skills/debugging/references/tools/playwright-cli.md +79 -161
- package/packages/shared-skills/skills/frontend/SKILL.md +1 -1
- package/packages/shared-skills/skills/frontend/references/design/aside.md +1 -1
- package/packages/shared-skills/skills/frontend/references/design/clone-from-url.md +1 -1
- package/packages/shared-skills/skills/frontend/scripts/perfection/lighthouse-audit.py +2 -2
- package/packages/shared-skills/skills/review-work/SKILL.md +1 -1
- package/packages/shared-skills/skills/ultimate-browsing/ATTRIBUTION.md +1 -82
- package/packages/shared-skills/skills/ultimate-browsing/SKILL.md +17 -29
- package/packages/shared-skills/skills/ultimate-browsing/engine/AGENTS.md +2 -1
- package/packages/shared-skills/skills/ultimate-browsing/engine/templates/package.json +2 -2
- package/packages/shared-skills/skills/ultimate-browsing/engine/templates/playwright_mobile_chrome.js +5 -2
- package/packages/shared-skills/skills/ultimate-browsing/engine/templates/playwright_real_chrome.js +8 -6
- package/packages/shared-skills/skills/ultimate-browsing/engine/tests/test_playwright_stealth.py +86 -0
- package/packages/shared-skills/skills/ultimate-browsing/engine/tests/test_playwright_templates.py +4 -7
- package/packages/shared-skills/skills/ultimate-browsing/references/chrome-stealth.md +86 -100
- package/packages/shared-skills/skills/ultimate-browsing/references/insane-search/README.md +9 -3
- package/packages/shared-skills/skills/ultimate-browsing/references/insane-search/playwright.md +16 -13
- package/packages/shared-skills/skills/ultimate-browsing/scripts/extract_cookies.py +3 -3
- package/packages/shared-skills/skills/ulw-execute/SKILL.md +1 -1
- package/packages/shared-skills/skills/ulw-plan/scripts/scaffold-plan.mjs +1 -1
- package/packages/shared-skills/skills/ulw-research/SKILL.md +3 -3
- package/packages/shared-skills/skills/visual-qa/AGENTS.md +1 -1
- package/packages/shared-skills/skills/visual-qa/SKILL.md +1 -1
- package/packages/shared-skills/skills/visual-qa/references/browser-setup.md +75 -0
- package/packages/omo-codex/plugin/skills/visual-qa/references/agent-browser-setup.md +0 -45
- package/packages/shared-skills/skills/visual-qa/references/agent-browser-setup.md +0 -45
package/packages/shared-skills/skills/ultimate-browsing/engine/tests/test_playwright_stealth.py
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import subprocess
|
|
6
|
+
import tempfile
|
|
7
|
+
import unittest
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from test_playwright_templates import (
|
|
11
|
+
TEMPLATE_NAMES,
|
|
12
|
+
TEMPLATES_DIR,
|
|
13
|
+
_install_fake_playwright,
|
|
14
|
+
_write_file,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class PlaywrightTemplateStealth(unittest.TestCase):
|
|
19
|
+
def test_extracts_html_and_closes_wrapped_chrome_when_stealth_is_installed(self) -> None:
|
|
20
|
+
for template_name in TEMPLATE_NAMES:
|
|
21
|
+
with self.subTest(template_name=template_name), tempfile.TemporaryDirectory() as tmp:
|
|
22
|
+
# Given: core has no plugin API; only addExtra supplies a registered wrapper.
|
|
23
|
+
root = Path(tmp)
|
|
24
|
+
modules = root / "node_modules"
|
|
25
|
+
profile = root / "profile"
|
|
26
|
+
receipt = root / "launch.json"
|
|
27
|
+
_install_fake_playwright(modules)
|
|
28
|
+
_write_file(modules / "puppeteer-extra-plugin-stealth/index.js", """
|
|
29
|
+
module.exports = () => ({ name: 'stealth' });
|
|
30
|
+
""")
|
|
31
|
+
_write_file(modules / "playwright-extra/index.js", """
|
|
32
|
+
const assert = require('node:assert/strict');
|
|
33
|
+
const fs = require('node:fs');
|
|
34
|
+
exports.addExtra = (core) => {
|
|
35
|
+
assert.equal(core, require('playwright-core').chromium);
|
|
36
|
+
assert.equal(Object.hasOwn(core, 'use'), false);
|
|
37
|
+
const events = ['wrap'];
|
|
38
|
+
let registered = false;
|
|
39
|
+
return {
|
|
40
|
+
use(plugin) {
|
|
41
|
+
assert.equal(plugin.name, 'stealth');
|
|
42
|
+
registered = true;
|
|
43
|
+
events.push(plugin.name);
|
|
44
|
+
},
|
|
45
|
+
async launchPersistentContext(profileDir, options) {
|
|
46
|
+
assert.equal(registered, true);
|
|
47
|
+
events.push({ profileDir, channel: options.channel, viewport: options.viewport });
|
|
48
|
+
const context = await core.launchPersistentContext(profileDir, options);
|
|
49
|
+
return {
|
|
50
|
+
...context,
|
|
51
|
+
async close() {
|
|
52
|
+
await context.close();
|
|
53
|
+
events.push('close');
|
|
54
|
+
fs.writeFileSync(process.env.STEALTH_RECEIPT, JSON.stringify(events));
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
""")
|
|
61
|
+
script = root / template_name
|
|
62
|
+
script.write_bytes((TEMPLATES_DIR / template_name).read_bytes())
|
|
63
|
+
env = {key: value for key, value in os.environ.items() if key != "NODE_PATH"}
|
|
64
|
+
env["STEALTH_RECEIPT"] = str(receipt)
|
|
65
|
+
|
|
66
|
+
# When: the actual bundled entry point consumes its normal stdin protocol.
|
|
67
|
+
result = subprocess.run(
|
|
68
|
+
["node", str(script)],
|
|
69
|
+
input=json.dumps({"url": "https://example.com/", "profileDir": str(profile), "headless": True}),
|
|
70
|
+
text=True, capture_output=True, timeout=5, env=env, check=False,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
# Then: HTML arrives through the registered wrapper and the context is closed.
|
|
74
|
+
self.assertEqual(result.returncode, 0, result.stderr)
|
|
75
|
+
self.assertEqual(result.stdout, "<html><article>ok</article></html>")
|
|
76
|
+
self.assertEqual(result.stderr, "")
|
|
77
|
+
viewport = {"width": 390, "height": 844} if "mobile" in template_name else {"width": 1366, "height": 900}
|
|
78
|
+
self.assertEqual(json.loads(receipt.read_text(encoding="utf-8")), [
|
|
79
|
+
"wrap", "stealth",
|
|
80
|
+
{"profileDir": str(profile), "channel": "chrome", "viewport": viewport},
|
|
81
|
+
"close",
|
|
82
|
+
])
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
if __name__ == "__main__":
|
|
86
|
+
unittest.main()
|
package/packages/shared-skills/skills/ultimate-browsing/engine/tests/test_playwright_templates.py
CHANGED
|
@@ -21,7 +21,7 @@ def _write_file(path: Path, content: str) -> None:
|
|
|
21
21
|
|
|
22
22
|
def _install_fake_playwright(node_modules: Path) -> None:
|
|
23
23
|
_write_file(
|
|
24
|
-
node_modules / "playwright" / "index.js",
|
|
24
|
+
node_modules / "playwright-core" / "index.js",
|
|
25
25
|
"""
|
|
26
26
|
const page = {
|
|
27
27
|
async goto() {},
|
|
@@ -47,7 +47,6 @@ def _install_fake_playwright(node_modules: Path) -> None:
|
|
|
47
47
|
};
|
|
48
48
|
|
|
49
49
|
exports.chromium = {
|
|
50
|
-
use() {},
|
|
51
50
|
async launchPersistentContext() { return context; },
|
|
52
51
|
};
|
|
53
52
|
exports.devices = {
|
|
@@ -78,9 +77,7 @@ def _install_working_playwright_extra(node_modules: Path) -> None:
|
|
|
78
77
|
_write_file(
|
|
79
78
|
node_modules / "playwright-extra" / "index.js",
|
|
80
79
|
"""
|
|
81
|
-
|
|
82
|
-
exports.chromium = playwright.chromium;
|
|
83
|
-
exports.devices = playwright.devices;
|
|
80
|
+
exports.addExtra = (browserType) => browserType;
|
|
84
81
|
""",
|
|
85
82
|
)
|
|
86
83
|
|
|
@@ -149,7 +146,7 @@ def _run_template(
|
|
|
149
146
|
|
|
150
147
|
@unittest.skipUnless(shutil.which("node"), "node is required for Playwright template tests")
|
|
151
148
|
class PlaywrightTemplateErrorHandling(unittest.TestCase):
|
|
152
|
-
def
|
|
149
|
+
def test_missing_playwright_extra_warns_and_falls_back_to_playwright_core(self) -> None:
|
|
153
150
|
for template_name in TEMPLATE_NAMES:
|
|
154
151
|
with self.subTest(template_name=template_name):
|
|
155
152
|
result = _run_template(
|
|
@@ -166,7 +163,7 @@ class PlaywrightTemplateErrorHandling(unittest.TestCase):
|
|
|
166
163
|
self.assertIn("best-effort optional module playwright-extra failed:", result.stderr)
|
|
167
164
|
self.assertIn("Cannot find module 'playwright-extra'", result.stderr)
|
|
168
165
|
|
|
169
|
-
def
|
|
166
|
+
def test_missing_stealth_plugin_warns_and_falls_back_to_playwright_core(self) -> None:
|
|
170
167
|
for template_name in TEMPLATE_NAMES:
|
|
171
168
|
with self.subTest(template_name=template_name):
|
|
172
169
|
result = _run_template(
|
|
@@ -1,127 +1,113 @@
|
|
|
1
|
-
# Tier 2 — Chrome stealth
|
|
1
|
+
# Tier 2 — Chrome stealth through playwright-core scripts
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
```
|
|
9
|
-
CloakBrowser (stealth Chromium) <- CDP port 9242 -> agent-browser CLI
|
|
10
|
-
- 57 C++ fingerprint patches - AX-tree snapshots, @eN refs
|
|
11
|
-
- Canvas/WebGL consistency - click / fill / type / scroll
|
|
12
|
-
- navigator.webdriver = false at C++ source - screenshot, video record
|
|
13
|
-
- Humanize mode (mouse curves, timing) - cookie / state / session mgmt
|
|
14
|
-
```
|
|
3
|
+
For real-Chrome semantics, stealth, traces, or authenticated sessions, WRITE a
|
|
4
|
+
script and run it from the js-eval kernel. Use installed local Chrome, not a
|
|
5
|
+
managed browser download. Ordinary macOS captures on a Bun >= 1.4 kernel use
|
|
6
|
+
`new Bun.WebView()`; Linux/Windows WebView needs installed Chrome/Chromium/Edge.
|
|
7
|
+
Codex's ordinary browser surface is `browser:control-in-app-browser`.
|
|
15
8
|
|
|
16
9
|
## Install (one-time)
|
|
17
10
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
The venv lives at a fixed path so every session reuses one stealth-Chromium download instead of creating a new venv per working directory, and every command below calls its interpreter by absolute path — no `activate` step to forget.
|
|
21
|
-
|
|
22
|
-
```bash
|
|
23
|
-
# CloakBrowser (MIT wrapper source; separate binary license, pin 0.5.7):
|
|
24
|
-
VENV="$HOME/.agents/cloak-venv" # Windows: %USERPROFILE%\.agents\cloak-venv
|
|
25
|
-
uv venv "$VENV" --python 3.13
|
|
26
|
-
uv pip install --python "$VENV/bin/python" "cloakbrowser==0.5.7"
|
|
27
|
-
"$VENV/bin/python" -c "import cloakbrowser; cloakbrowser.ensure_binary()" # downloads stealth Chromium on first import
|
|
11
|
+
Only the user installs these script dependencies, once in the skill's engine
|
|
12
|
+
directory. Stealth plugins run inside scripts, never inside WebView:
|
|
28
13
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
14
|
+
```sh
|
|
15
|
+
cd "$SKILL_DIR/engine"
|
|
16
|
+
test -f package.json || cp templates/package.json package.json
|
|
17
|
+
bun add playwright-core@1.62.1 playwright-extra@4.3.6 puppeteer-extra-plugin-stealth@2.11.2
|
|
32
18
|
```
|
|
33
19
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
# -> 0.5.7 <chromium-version> True
|
|
39
|
-
```
|
|
20
|
+
Chrome must already be installed. The bundled `templates/playwright_real_chrome.js`
|
|
21
|
+
and `templates/playwright_mobile_chrome.js` resolve these dependencies from their
|
|
22
|
+
parent engine directory. Without optional plugins, templates report the fallback
|
|
23
|
+
and use plain `playwright-core`; do not call that fallback stealth.
|
|
40
24
|
|
|
41
25
|
## Launch + drive
|
|
42
26
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
27
|
+
Close the source browser before cloning its user-data directory for a consistent
|
|
28
|
+
copy. Preserve its `Local State` alongside the selected profile. Use a private,
|
|
29
|
+
task-owned clone, never the live user-data-dir. NEVER clear the live profile's
|
|
30
|
+
cookies, cache, or site data. For unauthenticated QA, use a fresh task-owned profile.
|
|
31
|
+
|
|
32
|
+
Write `capture.cjs` in the engine directory. `clonePath`, URL, and output path are
|
|
33
|
+
arguments owned by this QA run; keep cookie values out of argv and logs:
|
|
34
|
+
|
|
35
|
+
```js
|
|
36
|
+
const { chromium: core } = require('playwright-core');
|
|
37
|
+
const { addExtra } = require('playwright-extra');
|
|
38
|
+
const stealth = require('puppeteer-extra-plugin-stealth');
|
|
39
|
+
const [clonePath, url, pngPath] = process.argv.slice(2);
|
|
40
|
+
const chromium = addExtra(core);
|
|
41
|
+
chromium.use(stealth());
|
|
42
|
+
|
|
43
|
+
async function capture() {
|
|
44
|
+
const context = await chromium.launchPersistentContext(clonePath, {
|
|
45
|
+
channel: 'chrome',
|
|
46
|
+
headless: true,
|
|
47
|
+
args: ['--disable-blink-features=AutomationControlled'],
|
|
48
|
+
ignoreDefaultArgs: ['--enable-automation'],
|
|
49
|
+
viewport: { width: 1280, height: 720 },
|
|
50
|
+
});
|
|
51
|
+
try {
|
|
52
|
+
const page = await context.newPage();
|
|
53
|
+
await page.goto(url, { waitUntil: 'load', timeout: 30000 });
|
|
54
|
+
await page.screenshot({ path: pngPath });
|
|
55
|
+
console.log(JSON.stringify({ pngPath, webdriver: await page.evaluate(() => navigator.webdriver) }));
|
|
56
|
+
} finally {
|
|
57
|
+
await context.close();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
capture().catch((error) => { console.error(error); process.exitCode = 1; });
|
|
67
61
|
```
|
|
68
62
|
|
|
69
|
-
|
|
63
|
+
Run the script from js eval with a bounded process lifetime:
|
|
70
64
|
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
65
|
+
```js
|
|
66
|
+
const { promisify } = await import('node:util')
|
|
67
|
+
const { execFile } = await import('node:child_process')
|
|
68
|
+
const result = await promisify(execFile)('node', [scriptPath, clonePath, url, pngPath], { timeout: 45000 })
|
|
69
|
+
console.log(result.stdout)
|
|
76
70
|
```
|
|
77
71
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
72
|
+
For extraction, run a bundled template the same way, supplying its JSON input on
|
|
73
|
+
stdin (`url`, `profileDir: clonePath`, `waitSelector`, `headless`). The template
|
|
74
|
+
returns HTML on stdout. For traces, use `context.tracing.start()` before navigation
|
|
75
|
+
and `context.tracing.stop({ path: tracePath })` before closing. Use `headless: false`
|
|
76
|
+
only with an available display when headed behavior is the criterion.
|
|
83
77
|
|
|
84
|
-
|
|
78
|
+
## Verify stealth
|
|
85
79
|
|
|
86
|
-
|
|
80
|
+
Inspect the page, screenshot, and requested content. `navigator.webdriver === false`
|
|
81
|
+
checks one browser property, NOT a promise to pass a detector or challenge.
|
|
82
|
+
Persistent challenge pages or missing content are failures, even if the script
|
|
83
|
+
exits zero. Do not keep retrying or silently swap browser tooling.
|
|
87
84
|
|
|
88
85
|
## Cookie login (cross-platform)
|
|
89
86
|
|
|
90
|
-
`scripts/extract_cookies.py`
|
|
87
|
+
`scripts/extract_cookies.py` exports local-browser cookies with owner-only `0600`
|
|
88
|
+
permissions. Keep exports private and never commit them. For a persistent context,
|
|
89
|
+
load the exported cookies with `context.addCookies(...)`, then navigate/reload.
|
|
90
|
+
Browser- or device-bound login tokens may not transfer to a clone.
|
|
91
91
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
| Linux | `~/.config/<app>/` (Chromium), `~/.mozilla/firefox/` (Firefox) | libsecret/SecretService, then PBKDF2 + AES-128-CBC |
|
|
96
|
-
| Windows | `%LOCALAPPDATA%\<app>\User Data\` | DPAPI (`CryptUnprotectData`) + AES-256-GCM (`os_crypt` key) |
|
|
97
|
-
|
|
98
|
-
```bash
|
|
99
|
-
# Extract to a file:
|
|
100
|
-
mkdir -p ~/.local/state/omo-cookies
|
|
101
|
-
python3 ../scripts/extract_cookies.py --browser chrome --domain youtube.com --output ~/.local/state/omo-cookies/youtube.cookies.json
|
|
102
|
-
# Extract and inject into the running CDP session:
|
|
103
|
-
python3 ../scripts/extract_cookies.py --browser chrome --domain youtube.com --inject --cdp 9242
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
Cookie export files are written with owner-only `0600` permissions. Do not place live auth cookies in shared temp directories or commit them to a repo. Cookie injection sends values to CDP over stdin rather than argv, so live cookie values do not appear in process listings. Cookies apply on next navigation — reload after injecting. Google services use fingerprint-bound tokens (SIDTS) that may not transfer across browser profiles. Firefox-family profiles store cookies unencrypted; Chromium-family profiles trigger a one-time OS-keyring prompt on macOS/Linux.
|
|
92
|
+
The helper's `--inject --cdp <port>` targets a task-owned local Chrome CDP endpoint,
|
|
93
|
+
which a script can also use via `chromium.connectOverCDP(...)`. Expose CDP on
|
|
94
|
+
loopback only, never against the live profile. Values travel on stdin, not argv.
|
|
107
95
|
|
|
108
96
|
## Anti-patterns
|
|
109
97
|
|
|
110
|
-
-
|
|
111
|
-
-
|
|
112
|
-
-
|
|
113
|
-
-
|
|
114
|
-
- Do NOT forget to `close` the session when done.
|
|
115
|
-
- Do NOT inject cookies without reloading the page.
|
|
98
|
+
- Plain text extraction does not need a stealth browser: use the extraction lane.
|
|
99
|
+
- Never launch against, clear, or delete the user's live profile.
|
|
100
|
+
- Never claim stealth from successful startup alone; validate rendered content.
|
|
101
|
+
- Never run plugins without explicit user-installed script dependencies.
|
|
116
102
|
|
|
117
103
|
## Troubleshooting
|
|
118
104
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
105
|
+
A missing Chrome executable is a prerequisite failure: report it, do not download
|
|
106
|
+
a replacement. Missing plugins require the one-time setup above; module errors
|
|
107
|
+
inside installed plugins are real failures, not optional-dependency misses.
|
|
108
|
+
A locked profile usually means the source was not cloned or another run owns the
|
|
109
|
+
clone. Use a new task-owned clone instead of killing unrelated browsers.
|
|
110
|
+
|
|
111
|
+
Always close contexts in `finally`, stop the fixture server, and remove only this
|
|
112
|
+
run's clone and temporary scripts. Preserve requested PNG/trace evidence and record
|
|
113
|
+
the cleanup receipt, including failures.
|
|
@@ -223,11 +223,17 @@ result = fetch(
|
|
|
223
223
|
python3 -c "import curl_cffi, bs4, yaml" 2>/dev/null || pip install curl_cffi beautifulsoup4 pyyaml -q
|
|
224
224
|
```
|
|
225
225
|
|
|
226
|
-
|
|
226
|
+
브라우저를 직접 제어할 때는 js eval에서 Bun >= 1.4의 `new Bun.WebView()`를
|
|
227
|
+
사용한다(macOS 기본; Linux/Windows는 설치된 Chrome/Chromium/Edge 필요).
|
|
228
|
+
그 외 또는 Chrome 동작·stealth·trace·인증이 필요하면 `playwright-core` 스크립트를
|
|
229
|
+
작성해 js eval에서 로컬 Chrome(`channel: "chrome"`)으로 실행한다.
|
|
230
|
+
사용자가 엔진 디렉터리에서 한 번 설치할 스크립트 의존성:
|
|
227
231
|
```bash
|
|
228
|
-
|
|
229
|
-
|
|
232
|
+
cd "$SKILL_DIR/engine"
|
|
233
|
+
test -f package.json || cp templates/package.json package.json
|
|
234
|
+
bun add playwright-core@1.62.1 playwright-extra@4.3.6 puppeteer-extra-plugin-stealth@2.11.2
|
|
230
235
|
```
|
|
236
|
+
Chrome은 이미 설치되어 있어야 한다. 인증 프로필은 먼저 복제하고 복제본만 사용한다.
|
|
231
237
|
|
|
232
238
|
## 빠른 참조 — Phase 0 명령어
|
|
233
239
|
|
package/packages/shared-skills/skills/ultimate-browsing/references/insane-search/playwright.md
CHANGED
|
@@ -16,10 +16,10 @@
|
|
|
16
16
|
|
|
17
17
|
### 의존성
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
이 항목은 이미 연결된 MCP를 사용하는 엔진 어댑터의 설명이다.
|
|
20
|
+
새 브라우저 세션은 js eval의 Bun.WebView(Bun >= 1.4, macOS 기본;
|
|
21
|
+
Linux/Windows는 설치된 Chrome/Chromium/Edge 필요), 그 외 또는 Chrome 동작·stealth·trace·인증은
|
|
22
|
+
로컬 Chrome을 제어하는 `playwright-core` 스크립트로 실행한다. MCP를 새로 설치하지 않는다.
|
|
23
23
|
|
|
24
24
|
### 기본 워크플로
|
|
25
25
|
|
|
@@ -56,11 +56,11 @@ claude mcp add playwright -- npx @playwright/mcp@latest
|
|
|
56
56
|
# Node (시스템 설치)
|
|
57
57
|
node -v # v18+ 권장
|
|
58
58
|
|
|
59
|
-
#
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
59
|
+
# 사용자가 엔진 디렉터리에서 한 번 설치하는 스크립트 의존성
|
|
60
|
+
cd "$SKILL_DIR/engine"
|
|
61
|
+
test -f package.json || cp templates/package.json package.json
|
|
62
|
+
bun add playwright-core@1.62.1 playwright-extra@4.3.6 puppeteer-extra-plugin-stealth@2.11.2
|
|
63
|
+
# Chrome은 이미 시스템에 설치되어 있어야 한다. 브라우저 다운로드 명령은 없다.
|
|
64
64
|
```
|
|
65
65
|
|
|
66
66
|
### 호출 (engine 내부)
|
|
@@ -81,7 +81,8 @@ attempt, html = run_playwright_fallback(
|
|
|
81
81
|
### 데스크톱 템플릿 (`playwright_real_chrome.js`)
|
|
82
82
|
|
|
83
83
|
```js
|
|
84
|
-
const {
|
|
84
|
+
const { addExtra } = require('playwright-extra');
|
|
85
|
+
const chromium = addExtra(require('playwright-core').chromium);
|
|
85
86
|
const stealth = require('puppeteer-extra-plugin-stealth')();
|
|
86
87
|
chromium.use(stealth);
|
|
87
88
|
|
|
@@ -95,7 +96,9 @@ const ctx = await chromium.launchPersistentContext(profileDir, {
|
|
|
95
96
|
### 모바일 템플릿 (`playwright_mobile_chrome.js`)
|
|
96
97
|
|
|
97
98
|
```js
|
|
98
|
-
const {
|
|
99
|
+
const { devices } = require('playwright-core');
|
|
100
|
+
const { addExtra } = require('playwright-extra');
|
|
101
|
+
const chromium = addExtra(require('playwright-core').chromium);
|
|
99
102
|
const iPhone = devices['iPhone 13 Pro'];
|
|
100
103
|
|
|
101
104
|
const ctx = await chromium.launchPersistentContext(profileDir, {
|
|
@@ -126,8 +129,8 @@ const ctx = await chromium.launchPersistentContext(profileDir, {
|
|
|
126
129
|
|
|
127
130
|
## 디버깅 팁
|
|
128
131
|
|
|
129
|
-
- `profileDir
|
|
130
|
-
-
|
|
132
|
+
- 템플릿은 js eval에서 스크립트로 실행한다. `profileDir`는 작업 전용 경로나 사용자 프로필의 복제본만 사용한다.
|
|
133
|
+
- 사용자의 실제 프로필을 실행·초기화·삭제하지 않는다. 작업 종료 시 컨텍스트를 닫고 작업 전용 복제본만 정리한다.
|
|
131
134
|
- 실패 시 `result.trace`의 `error` 필드에 Node stderr 200자가 포함됨
|
|
132
135
|
|
|
133
136
|
## 사이트 예시 (독자 이해용, 코드 분기 근거 아님)
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
The OS-keyring lookup is an injected boundary: cookie_paths resolves profile
|
|
5
5
|
paths and cookie_crypto derives keys + decrypts values, both pure and testable
|
|
6
6
|
with synthetic fixtures on any OS. This module wires them to a real browser DB
|
|
7
|
-
and
|
|
7
|
+
and a local Chrome CDP session controlled by playwright-core scripts.
|
|
8
8
|
|
|
9
9
|
Usage:
|
|
10
10
|
python extract_cookies.py --browser chrome --domain youtube.com --output /tmp/cookies.json
|
|
@@ -255,7 +255,7 @@ def inject_cookies(cookies: list[CookieRecord], cdp_port: int) -> None:
|
|
|
255
255
|
if proc.returncode != 0:
|
|
256
256
|
raise RuntimeError((proc.stderr or "CDP cookie injection failed").strip())
|
|
257
257
|
ok = int((proc.stdout or "0").strip() or "0")
|
|
258
|
-
print(f"Injected {ok}/{len(filtered)} cookies into
|
|
258
|
+
print(f"Injected {ok}/{len(filtered)} cookies into local Chrome (playwright-core CDP {cdp_port})")
|
|
259
259
|
|
|
260
260
|
|
|
261
261
|
def main() -> None:
|
|
@@ -263,7 +263,7 @@ def main() -> None:
|
|
|
263
263
|
parser.add_argument("--browser", required=True, choices=sorted(BROWSERS.keys()))
|
|
264
264
|
parser.add_argument("--domain", required=True, action="append", dest="domains")
|
|
265
265
|
parser.add_argument("--output", help="Write cookies JSON to file")
|
|
266
|
-
parser.add_argument("--inject", action="store_true", help="Inject into
|
|
266
|
+
parser.add_argument("--inject", action="store_true", help="Inject into the local Chrome CDP endpoint used by a playwright-core script")
|
|
267
267
|
parser.add_argument("--cdp", type=int, default=9242, help="CDP port (default 9242)")
|
|
268
268
|
args = parser.parse_args()
|
|
269
269
|
|
|
@@ -160,7 +160,7 @@ Each sub-task message must include:
|
|
|
160
160
|
5. One Manual-QA channel, named with the exact tool and exact invocation (the literal `curl`, `send-keys`, `browser:control-in-app-browser` action, `page.click`, payload, selectors, and the binary observable that decides PASS/FAIL), not "verify it works". A LIGHT checkbox needs one real-surface proof of its deliverable, and auxiliary surfaces (CLI stdout, DB state diff, parsed config dump) are first-class when the surface is CLI- or data-shaped:
|
|
161
161
|
- HTTP call: `curl -i` against the live endpoint.
|
|
162
162
|
- Terminal / TUI: drive a real pty; `tmux send-keys` is fine for a boot/behavior smoke, but color/layout/CJK evidence goes through the xterm.js web terminal below, NEVER `tmux capture-pane`.
|
|
163
|
-
- Browser use:
|
|
163
|
+
- Browser use: from js eval, use `new Bun.WebView()` on Bun >= 1.4 (macOS default; Linux/Windows need Chrome/Chromium/Edge). Otherwise, or for Chrome semantics, stealth, trace, or auth, write and run a `playwright-core` script against local Chrome (`channel: "chrome"`; persistent context on a CLONED profile). Codex: `browser:control-in-app-browser` for ordinary page control.
|
|
164
164
|
- Computer use: OS-level GUI automation against the running desktop app when the surface is not a page.
|
|
165
165
|
- TUI visual evidence: when a TUI claim needs visual QA or PR proof, run `bun script/qa/web-terminal-visual-qa.mjs --command "<cmd>" --input "{Enter}" --evidence-dir <dir>` (real pty rendered through xterm.js in Chrome) and attach `terminal.png` plus `metadata.json`.
|
|
166
166
|
6. The adversarial classes that apply to this sub-task (from the 9 ultraqa classes) and how each is probed.
|
|
@@ -254,7 +254,7 @@ Your next move: <fill - e.g. approve, or run a high-accuracy review>. Full execu
|
|
|
254
254
|
## Verification strategy
|
|
255
255
|
> Zero human intervention - all verification is agent-executed.
|
|
256
256
|
- Test decision: <TDD | tests-after | none> + framework
|
|
257
|
-
- Evidence: <attemptDir>/task-<N>-${slug}.<ext> (attemptDir = currentAttemptDir from the ulw-loop status operation - the
|
|
257
|
+
- Evidence: <attemptDir>/task-<N>-${slug}.<ext> (attemptDir = currentAttemptDir from the ulw-loop status operation - on Senpi the eval SDK import, const { agentToolkit } = await import(\`\${env("OMO_AGENT_TOOLKIT_SDK_ROOT")}/sdk.js\`) then agentToolkit.status(), on Codex the omo-agent-toolkit CLI, .omo/evidence/ulw/<session>/<goalId>/a<attempt>; outside ulw-loop use .omo/evidence/)
|
|
258
258
|
|
|
259
259
|
## Execution strategy
|
|
260
260
|
### Parallel execution waves
|
|
@@ -175,7 +175,7 @@ Scaling floor — more angles always justify more workers:
|
|
|
175
175
|
| Multi-faceted | 4 | 6 | 2 | 2 | 14 |
|
|
176
176
|
| Full due diligence | 4 | 6 | 3 | 2 | 15 |
|
|
177
177
|
|
|
178
|
-
The browsing column is BINDING, not advisory: when the brief says `Browsing: yes`, the roster names a browsing-worker angle before the first wave launches, and that worker is spawned in the same turn as the rest of the wave. A run that reaches wave 2 with zero browsing workers on a `Browsing: yes` brief has silently downgraded every source to what plain fetch happened to return.
|
|
178
|
+
The browsing column is BINDING, not advisory: when the brief says `Browsing: yes`, the roster names a browsing-worker angle, armed with the `ultimate-browsing` skill, before the first wave launches, and that worker is spawned in the same turn as the rest of the wave. A run that reaches wave 2 with zero browsing workers on a `Browsing: yes` brief has silently downgraded every source to what plain fetch happened to return.
|
|
179
179
|
|
|
180
180
|
**Disambiguate before you expand.** When the topic names something that could resolve several ways — a product, a person, a codename, a version — the first wave settles WHICH entity before any worker researches its history, benchmarks, or controversies: canonical name, first-party URL or account, whether it exists in the claimed category, and a confidence line. An unresolved entity never becomes a premise in a later wave's spawn message; that is exactly how a run starts inventing facts about something that does not exist.
|
|
181
181
|
|
|
@@ -183,7 +183,7 @@ Role protocols — embed the relevant one in each spawn message; every worker ge
|
|
|
183
183
|
|
|
184
184
|
- **Codebase (explore), 2-4 workers.** Grep with 3+ keyword variations; structural/AST search; LSP definitions and references; file-name globs; `git log --all -S '<keyword>'` and `--grep` for history including deleted code. Cross-validate hits across tools. Report absolute file paths, patterns with `file:line`, and how findings connect.
|
|
185
185
|
- **Web (librarian), 3-6 workers.** At least 10 distinct websearch queries per worker, each with a different operator or angle (see Search craft); fetch the full page for every result that matters — snippets lie. Context7 with 3+ queries per known library. grep.app and `gh search code|repos|issues` for real-world usage. Official docs via sitemap discovery (`<base>/sitemap.xml`), then targeted pages.
|
|
186
|
-
- **Browsing, 1-3 workers on a `Browsing: yes` brief (0 otherwise)
|
|
186
|
+
- **Browsing, 1-3 workers on a `Browsing: yes` brief (0 otherwise), every one loaded with the `ultimate-browsing` skill.** The skill owns the routing — the harness's own browser first, then its extraction engine with archive surrogates, platform-native readers, and Chrome stealth as each source demands; the worker owns the deliverable. This worker RENDERS pages, it does not re-fetch them: its standing deliverable is a full-page screenshot of every top source plus the rendered text plain fetch could not reach; a worker that returns only fetched text has not done its job. JS-rendered, login-gated, WAF-blocked, and screenshot-bearing sources all belong here rather than in the web lane. **Provenance is part of the claim**: when a source came back with `provenance` of `snapshot` (an archive copy), cite it with its `snapshot_timestamp` and never state it as the current live page; content from a `proxy` route is `untrusted` and needs a second independent route before any claim rests on it. When one blocked territory hides many leads, fan out more browsing subagents in parallel for breadth instead of serializing one worker through them.
|
|
187
187
|
- **Repo deep-dive (librarian), 0-2 workers.** Shallow-clone the most relevant repos to `${TMPDIR:-/tmp}`, pin the HEAD SHA, read core modules, follow call chains, return SHA-pinned permalinks.
|
|
188
188
|
|
|
189
189
|
Example spawn (codebase axis; librarian, browsing, and repo-dive follow the same contract with their own protocol):
|
|
@@ -383,7 +383,7 @@ High-yield combinations: official docs (`site:<docs domain>`), GitHub implementa
|
|
|
383
383
|
| Obeying a surrounding "stop exploring" rule mid-research | Authority section — those rules do not bind this mode |
|
|
384
384
|
| Asking a worker to write journal or session files | Workers are read-only; you journal every return |
|
|
385
385
|
| Two workers given the same angle | One unique angle per worker, always |
|
|
386
|
-
| A `Browsing: yes` run whose roster carries no browsing worker | The browsing column is binding — name the angle in the brief and spawn it with the first wave
|
|
386
|
+
| A `Browsing: yes` run whose roster carries no browsing worker, or a browsing worker spawned without `ultimate-browsing` | The browsing column is binding — name the angle in the brief and spawn it, armed with the skill, in the first wave before any lead is chased |
|
|
387
387
|
| Contested claim settled by judgment | Phase 3 — run code, capture output, verdict |
|
|
388
388
|
| Deliverable claims without citations | Every claim cites a source or a verification artifact |
|
|
389
389
|
| Guessing the deliverable format instead of asking | The format gate is unconditional: propose PDF+DOCX plus the domain-fitting alternatives and the template, then wait before wave 1 |
|
|
@@ -11,7 +11,7 @@ The visual-QA skill's executable core: a zero-dependency Node CLI that produces
|
|
|
11
11
|
```
|
|
12
12
|
visual-qa/
|
|
13
13
|
├── SKILL.md # reviewer workflow (dual oracle, evidence gates) — the prose contract
|
|
14
|
-
├── references/
|
|
14
|
+
├── references/browser-setup.md
|
|
15
15
|
└── scripts/
|
|
16
16
|
├── visual-qa.mjs # SHIPPED RUNTIME: bun-build bundle of cli.ts (+ embedded modules)
|
|
17
17
|
├── cli.ts # development source; dispatches image-diff / tui-check
|
|
@@ -49,7 +49,7 @@ Before any reviewer sees an image, verify each capture yourself: the file signat
|
|
|
49
49
|
### Web
|
|
50
50
|
|
|
51
51
|
1. Capture a REFERENCE image: the user's mock/target, generated page snapshot, Figma export, source-site capture, or known-good baseline. Save as PNG. If the user provided overview text or annotations, save them next to the image and treat them as part of the reference packet.
|
|
52
|
-
2. Capture the ACTUAL rendered screenshot at the
|
|
52
|
+
2. Capture the ACTUAL rendered screenshot at the reference viewport. Use two browser tiers from js eval: (1) `new Bun.WebView()` on Bun >= 1.4 (macOS default; Linux/Windows need installed Chrome/Chromium/Edge); (2) otherwise, or for Chrome semantics, stealth, trace, or authenticated profiles, WRITE a `playwright-core` script and run it from the kernel against local Chrome (`chromium.launch({ channel: "chrome" })` / `launchPersistentContext` on a CLONED profile, never the live one). In Codex, prefer `browser:control-in-app-browser` for ordinary captures. Save PNG and return its path; close the browser context. See `$SKILL_DIR/references/browser-setup.md` for fixed-viewport examples and prerequisites.
|
|
53
53
|
3. Run the diff and keep the JSON:
|
|
54
54
|
|
|
55
55
|
```
|