pi-ui-extend 1.0.40 → 1.0.41

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 (110) hide show
  1. package/dist/app/commands/command-registry.js +2 -2
  2. package/dist/app/commands/command-session-actions.d.ts +0 -1
  3. package/dist/app/commands/command-session-actions.js +22 -13
  4. package/dist/app/icons.d.ts +14 -0
  5. package/dist/app/icons.js +33 -0
  6. package/dist/app/rendering/conversation-tool-renderer.js +2 -2
  7. package/dist/app/rendering/dcp-stats.d.ts +6 -1
  8. package/dist/app/rendering/dcp-stats.js +214 -46
  9. package/dist/app/rendering/editor-panels.js +8 -5
  10. package/dist/app/session/lazy-session-manager.js +12 -1
  11. package/dist/app/session/tabs-controller.d.ts +2 -5
  12. package/dist/app/session/tabs-controller.js +12 -21
  13. package/dist/app/subagents/subagents-model.d.ts +14 -1
  14. package/dist/app/subagents/subagents-model.js +34 -15
  15. package/dist/app/types.d.ts +2 -0
  16. package/dist/bundled-extensions/session-title/config.js +1 -1
  17. package/dist/markdown-format.js +27 -9
  18. package/dist/schemas/pi-tools-suite-schema.d.ts +29 -16
  19. package/dist/schemas/pi-tools-suite-schema.js +46 -31
  20. package/external/pi-tools-suite/README.md +188 -55
  21. package/external/pi-tools-suite/docs/browser-qa-subagent.md +31 -21
  22. package/external/pi-tools-suite/docs/context-gateway-p00-adr.md +216 -0
  23. package/external/pi-tools-suite/docs/context-gateway-p01n-gate-review.md +122 -0
  24. package/external/pi-tools-suite/docs/context-gateway-p01n-measurement.md +133 -0
  25. package/external/pi-tools-suite/docs/context-gateway-p01r-ra-evidence.md +111 -0
  26. package/external/pi-tools-suite/docs/context-gateway-p01r-rb-evidence.md +100 -0
  27. package/external/pi-tools-suite/docs/context-gateway-p01r-rc-evidence.md +69 -0
  28. package/external/pi-tools-suite/docs/context-gateway-p01r-rd-evidence.md +100 -0
  29. package/external/pi-tools-suite/docs/context-gateway-p01r-re-evidence.md +74 -0
  30. package/external/pi-tools-suite/docs/context-gateway-p01r-rf-evidence.md +153 -0
  31. package/external/pi-tools-suite/docs/context-gateway-p01r-rg-evidence.md +235 -0
  32. package/external/pi-tools-suite/docs/subagent-model-pools.md +109 -0
  33. package/external/pi-tools-suite/package.json +3 -0
  34. package/external/pi-tools-suite/src/async-subagents/{private-skills → agents}/browser-qa/scripts/browser-qa-runner.mjs +82 -1
  35. package/external/pi-tools-suite/src/async-subagents/{private-skills/browser-qa/SKILL.md → agents/browser-qa.md} +261 -12
  36. package/external/pi-tools-suite/src/async-subagents/agents/implement.md +20 -0
  37. package/external/pi-tools-suite/src/async-subagents/agents/oracle.md +16 -0
  38. package/external/pi-tools-suite/src/async-subagents/agents/research.md +18 -0
  39. package/external/pi-tools-suite/src/async-subagents/agents/verify.md +18 -0
  40. package/external/pi-tools-suite/src/async-subagents/async-subagents.sample.jsonc +27 -243
  41. package/external/pi-tools-suite/src/async-subagents/commands.ts +6 -2
  42. package/external/pi-tools-suite/src/async-subagents/core/agent-catalog.ts +41 -0
  43. package/external/pi-tools-suite/src/async-subagents/core/agent-strategy.ts +13 -93
  44. package/external/pi-tools-suite/src/async-subagents/core/agents-dir.ts +494 -0
  45. package/external/pi-tools-suite/src/async-subagents/core/browser-qa.ts +9 -0
  46. package/external/pi-tools-suite/src/async-subagents/core/config.ts +200 -143
  47. package/external/pi-tools-suite/src/async-subagents/core/model-fallback.ts +1 -1
  48. package/external/pi-tools-suite/src/async-subagents/core/model-selection.ts +54 -0
  49. package/external/pi-tools-suite/src/async-subagents/core/prompt.ts +7 -6
  50. package/external/pi-tools-suite/src/async-subagents/core/routing.ts +52 -45
  51. package/external/pi-tools-suite/src/async-subagents/core/spawn.ts +12 -4
  52. package/external/pi-tools-suite/src/async-subagents/index.ts +11 -1
  53. package/external/pi-tools-suite/src/async-subagents/lib.ts +6 -2
  54. package/external/pi-tools-suite/src/async-subagents/tools/spawn.ts +46 -18
  55. package/external/pi-tools-suite/src/async-subagents/tools/subagents.ts +3 -2
  56. package/external/pi-tools-suite/src/async-subagents/types.ts +2 -0
  57. package/external/pi-tools-suite/src/config.ts +1 -1
  58. package/external/pi-tools-suite/src/context-gateway/accounting.ts +151 -0
  59. package/external/pi-tools-suite/src/context-gateway/config.ts +111 -0
  60. package/external/pi-tools-suite/src/context-gateway/index.ts +160 -0
  61. package/external/pi-tools-suite/src/context-gateway/metadata-normalization.ts +88 -0
  62. package/external/pi-tools-suite/src/context-gateway/storeless-capabilities.ts +89 -0
  63. package/external/pi-tools-suite/src/context-gateway/telemetry.ts +429 -0
  64. package/external/pi-tools-suite/src/context-gateway/test-output-parser.ts +326 -0
  65. package/external/pi-tools-suite/src/context-gateway/types.ts +152 -0
  66. package/external/pi-tools-suite/src/dcp/auto-compress-budget.ts +106 -0
  67. package/external/pi-tools-suite/src/dcp/auto-compress.ts +810 -106
  68. package/external/pi-tools-suite/src/dcp/commands.ts +64 -139
  69. package/external/pi-tools-suite/src/dcp/compress-tool.ts +369 -35
  70. package/external/pi-tools-suite/src/dcp/compression-blocks.ts +510 -64
  71. package/external/pi-tools-suite/src/dcp/compression-preview.ts +113 -0
  72. package/external/pi-tools-suite/src/dcp/compression-progress.ts +70 -0
  73. package/external/pi-tools-suite/src/dcp/config.ts +36 -61
  74. package/external/pi-tools-suite/src/dcp/conversation-index.ts +421 -0
  75. package/external/pi-tools-suite/src/dcp/debug-log.ts +7 -5
  76. package/external/pi-tools-suite/src/dcp/index.ts +617 -203
  77. package/external/pi-tools-suite/src/dcp/journal.ts +566 -0
  78. package/external/pi-tools-suite/src/dcp/progress-controller.ts +244 -0
  79. package/external/pi-tools-suite/src/dcp/prompts.ts +10 -7
  80. package/external/pi-tools-suite/src/dcp/provider-tool-results.ts +189 -0
  81. package/external/pi-tools-suite/src/dcp/pruner-candidates.ts +298 -78
  82. package/external/pi-tools-suite/src/dcp/pruner-compression-blocks.ts +173 -281
  83. package/external/pi-tools-suite/src/dcp/pruner-emergency.ts +2 -4
  84. package/external/pi-tools-suite/src/dcp/pruner-message-ids.ts +17 -5
  85. package/external/pi-tools-suite/src/dcp/pruner-metadata.ts +11 -1
  86. package/external/pi-tools-suite/src/dcp/pruner-nudge.ts +30 -82
  87. package/external/pi-tools-suite/src/dcp/pruner-tools.ts +22 -133
  88. package/external/pi-tools-suite/src/dcp/pruner.ts +18 -33
  89. package/external/pi-tools-suite/src/dcp/recovery.ts +129 -0
  90. package/external/pi-tools-suite/src/dcp/shadow-plan.ts +127 -0
  91. package/external/pi-tools-suite/src/dcp/state-transaction.ts +102 -0
  92. package/external/pi-tools-suite/src/dcp/state.ts +158 -580
  93. package/external/pi-tools-suite/src/dcp/ui.ts +1 -0
  94. package/external/pi-tools-suite/src/default-pi-tools-suite-config.ts +32 -214
  95. package/external/pi-tools-suite/src/index.ts +9 -0
  96. package/external/pi-tools-suite/src/model-tools/index.ts +76 -42
  97. package/external/pi-tools-suite/src/repo-discovery/index.ts +84 -18
  98. package/external/pi-tools-suite/src/repo-discovery/native-compact.ts +458 -0
  99. package/external/pi-tools-suite/src/session-recovery/index.ts +189 -43
  100. package/external/pi-tools-suite/src/tool-descriptions.ts +39 -35
  101. package/external/pi-tools-suite/src/truncation-metadata-normalizer/index.ts +17 -0
  102. package/package.json +3 -2
  103. package/schemas/pi-tools-suite.json +159 -78
  104. package/external/pi-tools-suite/src/async-subagents/private-skills/browser-qa/references/auth-scaffold-spec.md +0 -78
  105. package/external/pi-tools-suite/src/async-subagents/private-skills/browser-qa/references/qa-design.md +0 -223
  106. package/external/pi-tools-suite/src/dcp/state-persistence.ts +0 -195
  107. /package/external/pi-tools-suite/src/async-subagents/{private-skills/browser-qa/references → agents/browser-qa/examples}/qa-auth.example.jsonc +0 -0
  108. /package/external/pi-tools-suite/src/async-subagents/{private-skills/browser-qa/references → agents/browser-qa/examples}/qa-flow.example.jsonc +0 -0
  109. /package/external/pi-tools-suite/src/async-subagents/{private-skills → agents}/browser-qa/vendor/fflate.LICENSE +0 -0
  110. /package/external/pi-tools-suite/src/async-subagents/{private-skills → agents}/browser-qa/vendor/fflate.mjs +0 -0
@@ -145,10 +145,6 @@
145
145
  "enabled": {
146
146
  "type": "boolean",
147
147
  "description": "Enable manual DCP mode."
148
- },
149
- "automaticStrategies": {
150
- "type": "boolean",
151
- "description": "Run dedup/purge even in manual mode."
152
148
  }
153
149
  },
154
150
  "description": "Manual mode configuration."
@@ -358,79 +354,77 @@
358
354
  }
359
355
  },
360
356
  "description": "Message-mode compression configuration."
361
- }
362
- },
363
- "description": "Compression trigger and behavior configuration."
364
- },
365
- "strategies": {
366
- "type": "object",
367
- "properties": {
368
- "deduplication": {
369
- "type": "object",
370
- "properties": {
371
- "enabled": {
372
- "type": "boolean",
373
- "description": "Enable duplicate content deduplication."
374
- },
375
- "protectedTools": {
376
- "type": "array",
377
- "items": {
378
- "type": "string"
379
- },
380
- "description": "Tool outputs protected from dedup."
381
- }
382
- },
383
- "description": "Deduplication strategy configuration."
384
357
  },
385
- "purgeErrors": {
358
+ "autoCompress": {
386
359
  "type": "object",
387
360
  "properties": {
388
361
  "enabled": {
389
362
  "type": "boolean",
390
- "description": "Enable error input purging."
363
+ "description": "Allow bounded automatic summary creation after pressure/opportunity gates."
391
364
  },
392
- "turns": {
365
+ "patience": {
393
366
  "type": "number",
394
- "description": "Prune error inputs after N user turns.",
395
- "minimum": 1
367
+ "description": "Completed actionable opportunities allowed before automatic compression.",
368
+ "minimum": 0
396
369
  },
397
- "protectedTools": {
370
+ "summarizerModel": {
398
371
  "type": "array",
399
372
  "items": {
400
373
  "type": "string"
401
374
  },
402
- "description": "Tool outputs protected from error purge."
375
+ "description": "Ordered summarizer model refs; empty uses the deterministic extractive summary."
376
+ },
377
+ "timeoutMs": {
378
+ "type": "number",
379
+ "description": "Per-summarizer deadline in milliseconds.",
380
+ "minimum": 1
403
381
  }
404
382
  },
405
- "description": "Error purging strategy configuration."
406
- },
407
- "autoToolPruning": {
383
+ "description": "Bounded automatic compression fallback."
384
+ }
385
+ },
386
+ "description": "Compression trigger and behavior configuration."
387
+ },
388
+ "strategies": {
389
+ "type": "object",
390
+ "properties": {
391
+ "emergencyCurrentTurnPruning": {
408
392
  "type": "object",
409
393
  "properties": {
410
394
  "enabled": {
411
395
  "type": "boolean",
412
- "description": "Enable automatic tool output pruning."
396
+ "description": "Enable bounded same-turn emergency planning and last-resort output pruning."
397
+ },
398
+ "hardContextPercent": {
399
+ "type": "number",
400
+ "description": "Context fraction that activates hard emergency pressure.",
401
+ "minimum": 0,
402
+ "maximum": 1
403
+ },
404
+ "targetContextPercent": {
405
+ "type": "number",
406
+ "description": "Context fraction the emergency path attempts to recover toward.",
407
+ "minimum": 0,
408
+ "maximum": 1
413
409
  },
414
- "maxOutputTokens": {
410
+ "patience": {
415
411
  "type": "number",
416
- "description": "Maximum output tokens before truncation.",
412
+ "description": "Completed emergency opportunities allowed before last-resort pruning.",
417
413
  "minimum": 0
418
414
  },
419
- "keepRecentTurns": {
415
+ "keepRecentToolPairs": {
420
416
  "type": "number",
421
- "description": "Recent turns to keep.",
417
+ "description": "Newest complete tool-call/result pairs never selected by emergency cleanup.",
422
418
  "minimum": 0
423
419
  },
424
- "readLikeTools": {
425
- "type": "array",
426
- "items": {
427
- "type": "string"
428
- },
429
- "description": "Tools treated as read-like (aggressively pruned)."
420
+ "minOutputTokens": {
421
+ "type": "number",
422
+ "description": "Minimum tool-result size eligible for emergency pruning.",
423
+ "minimum": 0
430
424
  },
431
- "readLikeTurns": {
425
+ "maxSuggestions": {
432
426
  "type": "number",
433
- "description": "Turns threshold for read-like tool pruning.",
427
+ "description": "Maximum emergency message candidates shown in a reminder.",
434
428
  "minimum": 0
435
429
  },
436
430
  "protectedTools": {
@@ -438,13 +432,13 @@
438
432
  "items": {
439
433
  "type": "string"
440
434
  },
441
- "description": "Tool outputs protected from auto-pruning."
435
+ "description": "Additional tools protected from emergency pruning and manual sweep."
442
436
  }
443
437
  },
444
- "description": "Auto tool pruning strategy configuration."
438
+ "description": "Single bounded emergency cleanup strategy."
445
439
  }
446
440
  },
447
- "description": "DCP pruning strategies."
441
+ "description": "Bounded DCP emergency strategy."
448
442
  },
449
443
  "protectedFilePatterns": {
450
444
  "type": "array",
@@ -452,23 +446,6 @@
452
446
  "type": "string"
453
447
  },
454
448
  "description": "File path glob patterns whose content is protected from pruning."
455
- },
456
- "pruneNotification": {
457
- "anyOf": [
458
- {
459
- "type": "string",
460
- "const": "off"
461
- },
462
- {
463
- "type": "string",
464
- "const": "minimal"
465
- },
466
- {
467
- "type": "string",
468
- "const": "detailed"
469
- }
470
- ],
471
- "description": "Notification level when pruning occurs."
472
449
  }
473
450
  },
474
451
  "description": "DCP (Dynamic Context Pruning) configuration."
@@ -478,14 +455,14 @@
478
455
  "properties": {
479
456
  "defaultType": {
480
457
  "type": "string",
481
- "description": "Default sub-agent type when not specified."
458
+ "description": "Preferred role for genuinely ambiguous router tasks and legacy resolver default. Not used as a spawn fallback for missing/invalid routes."
482
459
  },
483
460
  "routing": {
484
461
  "type": "object",
485
462
  "properties": {
486
463
  "enabled": {
487
464
  "type": "boolean",
488
- "description": "Enable LLM-based automatic role routing."
465
+ "description": "Enable fallback LLM selection for omitted subagentType values. Explicit valid types bypass the router; when disabled, every spawn task must specify a valid type."
489
466
  },
490
467
  "model": {
491
468
  "type": "string",
@@ -524,7 +501,7 @@
524
501
  "description": "Show routing debug warnings."
525
502
  }
526
503
  },
527
- "description": "LLM-based role routing configuration."
504
+ "description": "Fallback role routing for omitted types. Unknown explicit types or failed/incomplete routing reject the entire batch before any agents launch."
528
505
  },
529
506
  "vision": {
530
507
  "type": "object",
@@ -549,16 +526,27 @@
549
526
  "type": "string",
550
527
  "description": "Preset description."
551
528
  },
529
+ "models": {
530
+ "type": "array",
531
+ "items": {
532
+ "type": "string",
533
+ "pattern": "^[^\\s/*]+/[^\\s*]+$"
534
+ },
535
+ "uniqueItems": true,
536
+ "description": "Available model pool. Filter the agent's ordered models by membership; pool order is ignored. Empty intersection rejects spawn. Oracle also respects this pool."
537
+ },
552
538
  "model": {
553
539
  "type": "string",
554
- "description": "Default model for this preset."
540
+ "description": "Legacy default model. Ignored when models is set.",
541
+ "deprecated": true
555
542
  },
556
543
  "fallbackModels": {
557
544
  "type": "array",
558
545
  "items": {
559
546
  "type": "string"
560
547
  },
561
- "description": "Global fallback models for this preset."
548
+ "description": "Legacy fallback list. Ignored when models is set.",
549
+ "deprecated": true
562
550
  },
563
551
  "thinking": {
564
552
  "type": "string",
@@ -616,7 +604,7 @@
616
604
  "description": "Per-type overrides."
617
605
  }
618
606
  },
619
- "description": "Named spawn preset."
607
+ "description": "Named available-model pool. Legacy model/types overrides remain supported; prefer models for new presets."
620
608
  }
621
609
  },
622
610
  "description": "Named spawn presets."
@@ -631,16 +619,31 @@
631
619
  "type": "string",
632
620
  "description": "Role description for routing."
633
621
  },
622
+ "icon": {
623
+ "type": "string",
624
+ "description": "Agent icon name rendered by UIs (pix TUI icon themes and Pix Desktop lucide icons): agent, search, code, flask, globe, sparkles, brain, wrench, terminal, bug, book, eye, zap, rocket. Unknown names render as the neutral agent icon."
625
+ },
626
+ "models": {
627
+ "type": "array",
628
+ "items": {
629
+ "type": "string",
630
+ "pattern": "^[^\\s/*]+/[^\\s*]+$"
631
+ },
632
+ "uniqueItems": true,
633
+ "description": "Ordered model candidates. First usable member of the preset pool runs; only remaining compatible members can be used on quota failure. Replaces legacy model/fallbackModels/modelByParent selection."
634
+ },
634
635
  "model": {
635
636
  "type": "string",
636
- "description": "Model for this sub-agent type."
637
+ "description": "Legacy primary model; use models for new profiles.",
638
+ "deprecated": true
637
639
  },
638
640
  "fallbackModels": {
639
641
  "type": "array",
640
642
  "items": {
641
643
  "type": "string"
642
644
  },
643
- "description": "Ordered model fallbacks."
645
+ "description": "Legacy candidates after model; use models for a complete ranked list.",
646
+ "deprecated": true
644
647
  },
645
648
  "thinking": {
646
649
  "type": "string",
@@ -890,6 +893,84 @@
890
893
  },
891
894
  "description": "Settings for the opt-in credential-firewall module."
892
895
  },
896
+ "contextGateway": {
897
+ "type": "object",
898
+ "properties": {
899
+ "mode": {
900
+ "anyOf": [
901
+ {
902
+ "type": "string",
903
+ "const": "off"
904
+ },
905
+ {
906
+ "type": "string",
907
+ "const": "observe"
908
+ },
909
+ {
910
+ "type": "string",
911
+ "const": "enforce"
912
+ }
913
+ ],
914
+ "description": "Context Gateway mode. P01 implements off/observe; enforce is parsed but explicitly refused until later integration gates."
915
+ },
916
+ "budgets": {
917
+ "type": "object",
918
+ "properties": {
919
+ "maxInlineBytes": {
920
+ "type": "integer",
921
+ "minimum": 1,
922
+ "maximum": 67108864,
923
+ "description": "Initial inline byte budget used by Context Gateway policy experiments. Default 8192."
924
+ },
925
+ "maxResultBytes": {
926
+ "type": "integer",
927
+ "minimum": 1,
928
+ "maximum": 67108864,
929
+ "description": "Experimental delivered-result byte budget. P01 observe measures the available tool-result content boundary; later shaping must account for the final provider-visible serialization. Default 8192."
930
+ },
931
+ "maxExactReadBytes": {
932
+ "type": "integer",
933
+ "minimum": 1,
934
+ "maximum": 67108864,
935
+ "description": "Future exact-read byte budget. Default 32768."
936
+ },
937
+ "maxSearchBytes": {
938
+ "type": "integer",
939
+ "minimum": 1,
940
+ "maximum": 67108864,
941
+ "description": "Future search-delivery byte budget. Default 8192."
942
+ },
943
+ "maxSearchMatches": {
944
+ "type": "integer",
945
+ "minimum": 1,
946
+ "maximum": 1000,
947
+ "description": "Future search match budget. Default 12."
948
+ }
949
+ },
950
+ "description": "Context Gateway delivery budgets. In P01 only maxResultBytes is used for passive observe accounting."
951
+ }
952
+ },
953
+ "description": "Context Gateway configuration. Default mode is off."
954
+ },
955
+ "repoDiscovery": {
956
+ "type": "object",
957
+ "properties": {
958
+ "profile": {
959
+ "anyOf": [
960
+ {
961
+ "type": "string",
962
+ "const": "baseline"
963
+ },
964
+ {
965
+ "type": "string",
966
+ "const": "native-compact"
967
+ }
968
+ ],
969
+ "description": "repo_* runtime profile. baseline preserves historical argv/defaults; native-compact enables bounded native flags, cursors, validation, and an explicit per-call full override."
970
+ }
971
+ },
972
+ "description": "Repository discovery runtime policy. Default profile is baseline."
973
+ },
893
974
  "lsp": {
894
975
  "type": "object",
895
976
  "properties": {
@@ -1,78 +0,0 @@
1
- # Spec: Browser QA form-auth scaffold
2
-
3
- ## Type
4
-
5
- Change
6
-
7
- ## Goal
8
-
9
- Let the browser-QA sub-agent prepare a target-specific form-auth configuration
10
- without reading, writing, receiving, or printing credential values. The user
11
- should only need to replace clearly marked secret placeholders.
12
-
13
- ## Scope
14
-
15
- - Add a trusted runner command that inspects a public login form.
16
- - Generate `.pi/qa_auth.jsonc` with discovered field and submit selectors.
17
- - Keep generated values as non-secret placeholders until the user edits them.
18
- - Preserve the rule that agents never read or edit `.pi/qa_auth.jsonc`.
19
-
20
- ## Non-goals
21
-
22
- - Collecting credentials interactively through the agent.
23
- - Guessing cookie, token, storage-state, MFA, or federated-login credentials.
24
- - Modifying an existing non-empty auth configuration.
25
-
26
- ## Behavior
27
-
28
- - `auth scaffold` requires a project-local browser-QA sub-agent workspace, an
29
- HTTPS login URL (or loopback HTTP for local development), and a safe profile
30
- id.
31
- - The trusted runner opens the unauthenticated login page with fail-closed
32
- origin routing and discovers the most likely login form, fillable fields,
33
- submit control, and form container.
34
- - It writes a private `0600` `.pi/qa_auth.jsonc` containing only generated
35
- selectors and `__PI_QA_SECRET_n__` placeholders. It may replace the runner's
36
- empty generated template, but never a non-empty profiles object.
37
- - By default, successful login means that the discovered form becomes hidden.
38
- A form-less page requires an explicit success URL pattern or selector/state.
39
- - A run cannot use a profile while generated placeholders remain. The runner
40
- returns `QA_AUTH_UPDATE_REQUIRED` without exposing selectors or values.
41
-
42
- ## Invariants
43
-
44
- - No credential value is accepted through command arguments or environment
45
- variables.
46
- - Scaffold status output contains only path, profile id, counts, and action.
47
- - The command never prints inspected DOM text, input values, URLs, or selectors.
48
- - Agents continue to treat `.pi/qa_auth.jsonc` as opaque and user-owned.
49
-
50
- ## Edge cases
51
-
52
- - Reject pages without a discoverable fillable field or submit control, and
53
- form-less pages without an explicit success condition.
54
- - Reject remote plain-HTTP login pages.
55
- - Reject cross-origin base URLs and malformed success options.
56
- - Reject symlinked or permissive auth paths.
57
- - Refuse to overwrite invalid or non-empty auth configuration.
58
-
59
- ## Related files
60
-
61
- - `scripts/browser-qa-runner.mjs`
62
- - `../SKILL.md`
63
- - `../../../../../test/async-subagents/browser-qa-runner.test.ts`
64
-
65
- ## Verification
66
-
67
- - Unit tests for discovery output, placeholder blocking, private permissions,
68
- empty-template replacement, and non-empty-config refusal.
69
- - Existing browser-QA runner tests and repository checks continue to pass.
70
-
71
- ## Evidence
72
-
73
- - Confirmed by code: current runner owns Playwright, origin isolation, private
74
- auth-file creation, validation, and redacted status output.
75
- - Confirmed by tests: current runner rejects permissive/symlinked auth files and
76
- creates its empty template with private permissions.
77
- - Confirmed by docs: the private skill forbids agents from reading or editing
78
- `.pi/qa_auth.jsonc`.
@@ -1,223 +0,0 @@
1
- # Designing deterministic browser QA flows
2
-
3
- Use this reference with the bundled declarative runner. It intentionally does
4
- not describe a separate browser CLI or executable Playwright scripts.
5
-
6
- ## Build the proof before the steps
7
-
8
- Write down three things first:
9
-
10
- 1. **Setup:** the page and state needed to expose the behavior.
11
- 2. **Action:** the smallest user interaction that exercises it.
12
- 3. **Oracle:** the observable state that proves success or reproduces failure.
13
-
14
- Good oracles are product-visible and specific: an exact URL, a stable status
15
- message, a field value, item count, enabled/disabled state, or checked state.
16
- Avoid treating “the click did not throw” or “the screenshot looks plausible” as
17
- proof.
18
-
19
- When verifying a fix, prefer a focused regression flow over a broad tour of the
20
- application. If multiple independent states matter, assert each one explicitly.
21
-
22
- For explicit exploratory/manual QA, keep exploration bounded rather than turning
23
- it into an open-ended crawl. Run at most three minimal rounds. Each round should
24
- start from one concrete hypothesis, produce a deterministic observation plus a
25
- meaningful screenshot, and use that evidence to decide whether another round is
26
- justified. Verification tasks remain one browser run per profile.
27
-
28
- ## Choose resilient locators
29
-
30
- Prefer locators that match how users and accessibility APIs identify controls:
31
-
32
- 1. `testId` when the product exposes a stable test contract.
33
- 2. `role` with accessible `name` for buttons, links, headings, dialogs, and
34
- similar semantic elements.
35
- 3. `label` for form controls.
36
- 4. `placeholder` or visible `text` when they are stable product copy.
37
- 5. `css` only when no semantic contract exists.
38
-
39
- Use `exact: true` when duplicate or substring matches are possible. Avoid CSS
40
- that encodes DOM depth, generated classes, styling details, or element order.
41
- If a locator is ambiguous, inspect nearby source or rendered copy and choose a
42
- more specific product contract rather than adding arbitrary delays.
43
-
44
- ## Wait for state, not time
45
-
46
- Runner interactions inherit Playwright auto-waiting. Usually an action followed
47
- by an assertion is enough. Use `waitFor` only when the next operation depends on
48
- a distinct attached/detached/visible/hidden transition.
49
-
50
- After navigation and visible interactions, the runner also waits for DOM
51
- readiness, tracks requests causally started by that action through a bounded
52
- readiness window, waits for common visible
53
- `aria-busy`/progress/loading/spinner/skeleton markers, and keeps a 500 ms stable
54
- interval. EventSource/WebSocket traffic is ignored, and a long poll is not
55
- allowed to pin the entire flow timeout. A visible busy indicator may still hold
56
- readiness until `timeoutMs`. This is a safe baseline, not an application-specific
57
- oracle: explicitly wait for a custom loader to become hidden and assert the
58
- loaded content when the application uses different readiness semantics.
59
-
60
- `waitForTimeout` is bounded to five seconds and should be exceptional—for a
61
- known animation, debounce, or externally scheduled transition with no
62
- observable intermediate state. Sleeping longer hides races instead of proving
63
- behavior. If a normal operation legitimately needs more time, adjust the flow's
64
- `timeoutMs` rather than inserting repeated sleeps.
65
-
66
- All assertion actions retry until that timeout. This makes an action followed
67
- directly by `assertText`, `assertVisible`, `assertURL`, or another assertion
68
- safe for asynchronously rendered outcomes. `assertText` requires the locator to
69
- be visible and matches rendered `innerText`; use `assertTextContent` only when
70
- hidden/raw DOM text is deliberately part of the oracle. Use `assertAttribute` for
71
- observable state such as `aria-expanded`, `aria-invalid`, or `data-state`
72
- instead of reading DOM state through executable JavaScript.
73
-
74
- ## Responsive and scrolling scenarios
75
-
76
- Set the flow's top-level `viewport` whenever the bug depends on a breakpoint or
77
- available height. Assert `viewportWidth` or `viewportHeight` with
78
- `assertDOMMetric` when the dimensions themselves are part of the proof; the
79
- runner also includes the applied viewport in its result.
80
-
81
- Use `wheel` to reproduce real pointer-wheel input. Add a locator when the wheel
82
- must target a nested scrolling container—the runner hovers it before sending
83
- the input. Because browser scrolling may be scheduled after the wheel event,
84
- wait only for a short known settling interval when a direct metric assertion is
85
- otherwise racy.
86
-
87
- Use safe `evaluate` `scrollTo`/`scrollBy` operations for deterministic setup or
88
- to distinguish input handling from layout behavior. Use the `metrics` operation
89
- to retain a named page/element snapshot in result `observations`, and use
90
- `assertDOMMetric` for pass/fail. Raw JavaScript expressions are intentionally
91
- excluded: flows remain declarative and cannot inspect authentication storage or
92
- execute arbitrary same-origin requests.
93
-
94
- ## Deterministic browser environment
95
-
96
- Set top-level `environment` when locale, timezone, color scheme, or motion
97
- preferences can change the behavior. The runner otherwise uses stable defaults
98
- (`en-US`, `UTC`, `light`, `reduce`) instead of inheriting host settings. Prefer
99
- asserting product-visible copy or state derived from those settings; do not use
100
- screenshot pixels as the only oracle.
101
-
102
- ## Causal network and dialog expectations
103
-
104
- Put `expectResponse` or `expectDialog` on the interaction that causes the event.
105
- The runner arms both listeners before the interaction, avoiding the race in a
106
- separate “click, then wait” sequence. Response expectations deliberately match
107
- only an exact allowlisted-origin pathname, HTTP method, and status. This proves
108
- that a matching request started and received a response within the action
109
- window without retaining its URL query, headers, or body.
110
-
111
- Dialog expectations match a fixed dialog type and exact/included message, then
112
- accept or dismiss it declaratively. A mismatch is dismissed before the step
113
- fails so the page cannot freeze. Actual event metadata is never included in
114
- failure diagnostics. Do not place secrets in expected messages or response
115
- paths even though the runner keeps diagnostics generic.
116
-
117
- ## Drag, upload, and download scenarios
118
-
119
- Use `dragTo` for native DOM drag/drop and assert the resulting product state.
120
- Optional source/drop positions are relative bounded coordinates. Canvas-only,
121
- OS-native, or custom synthetic-event drag protocols remain unsupported; do not
122
- work around that with executable JavaScript.
123
-
124
- Uploads are memory-only base64 payloads declared in the flow. This intentionally
125
- prevents a flow from selecting arbitrary project files, credential config, or
126
- directories. Keep fixtures minimal and non-secret. An empty file list clears a
127
- file input.
128
-
129
- Use the atomic `download` action rather than clicking a download link directly.
130
- Always match the suggested filename and choose a tight `maxBytes`. Retain a
131
- download only when its contents are needed as evidence; otherwise the runner
132
- deletes it after validation. Retained downloads use generated private names,
133
- not server-provided paths, and are scanned for configured authentication before
134
- publication. `maxBytes` bounds the runner's private evidence copy and triggers
135
- cancellation while it grows, but it is not a network-bandwidth guarantee: the
136
- browser can receive temporary bytes before cancellation.
137
-
138
- ## Same-origin frames and popups
139
-
140
- Use a scoped `target` for iframe or named popup interactions. The runner checks
141
- the live frame origin before every scoped step and checks a popup after it loads;
142
- both must remain in `allowedOrigins`. This supports embedded application UI and
143
- same-origin auxiliary windows without opening a route around the network guard.
144
- Cross-origin login, payment, and third-party widgets remain intentionally out of
145
- scope. Each popup adds a separate private video artifact, so open only the
146
- windows needed for the proof.
147
-
148
- ## Authentication transitions
149
-
150
- Add `authRejectedIf` directly after initial navigation and after transitions
151
- that can redirect to login or display an expired-session marker. This converts
152
- stale credentials into an explicit update request instead of misreporting a
153
- product regression.
154
-
155
- Do not encode credentials, tokens, storage values, or login form secrets in the
156
- flow. The trusted runner applies the selected profile internally. For form auth,
157
- video starts on the login page and includes field filling and submission; password
158
- inputs remain masked, but visible identifiers can appear, so treat the video as
159
- sensitive private evidence. Tracing starts only after login succeeds and is
160
- sanitized before retention.
161
-
162
- ## Evidence strategy
163
-
164
- The runner always attempts a final or failure screenshot, records video from the
165
- first page, and creates a sanitized post-auth trace. Add named `screenshot`
166
- steps only at states that materially help explain the result—for example before
167
- and after a destructive interaction, or when a transient success message is
168
- the oracle.
169
-
170
- Use evidence by purpose:
171
-
172
- - **Screenshot:** quick review of one meaningful visual state.
173
- - **Video:** chronological confirmation of the complete user flow.
174
- - **Trace:** action/DOM timing diagnosis for a failed or flaky interaction.
175
-
176
- Videos automatically show a transient cursor and yellow pulse for clicks and
177
- double-clicks. After a native `dragTo` gesture completes, its resolved
178
- source-to-target route is replayed over 450 ms with a large orange cursor and
179
- progressively drawn high-contrast trail, followed by a green drop marker. These
180
- annotations are
181
- runner-owned, pointer-transparent, and accessibility-hidden; they cover the main
182
- page, same-origin frames, declared popups, and form-auth submission. Their
183
- bounded animations finish within the normal post-action stable interval, so
184
- they explain the chronology without becoming screenshot or assertion oracles.
185
-
186
- Assertions determine pass/fail; evidence explains it. Preserve and link every
187
- artifact group returned on both passed and failed runs.
188
-
189
- For visual QA, do not stop at artifact generation. Open at least one meaningful
190
- PNG with the model's image-capable `read` path and inspect layout, clipping,
191
- overlap, state styling, and other visual defects relevant to the scenario. If
192
- the active model cannot read images, explicitly report visual inspection as
193
- unavailable rather than treating deterministic assertions as a visual pass.
194
-
195
- ## Diagnose failures without weakening the test
196
-
197
- Classify the first failing step:
198
-
199
- - wrong target/setup or service unavailable;
200
- - authentication rejected or expired;
201
- - locator no longer matches the product contract;
202
- - expected state never appeared;
203
- - actual product behavior contradicts the expectation.
204
-
205
- Fix the flow only when its setup or locator is wrong. Do not replace a precise
206
- assertion with a vague one, increase timeouts reflexively, or remove the failing
207
- step to manufacture a pass. Keep the failure artifacts and state the expected
208
- versus observed behavior.
209
-
210
- ## Cleanup and isolation
211
-
212
- Each runner invocation owns one isolated context and evidence directory and
213
- closes its browser resources in a `finally` path. Do not create parallel shared
214
- or default sessions outside the runner. Test multiple auth profiles with
215
- separate invocations so cookies, storage, traces, and evidence cannot mix.
216
-
217
- Keep the declarative flow and every generated screenshot, video, trace, and
218
- result manifest inside `$PI_SUBAGENT_AGENT_DIR/browser-qa/`. The launcher owns
219
- that path and the runner validates it before opening a browser. Do not override
220
- the environment path or copy evidence into shared `.pi/qa-runs`/`.pi/qa-flows`
221
- directories: the agent-local workspace is intentionally removed by the normal
222
- sub-agent shutdown and cleanup lifecycle. Authentication config remains a
223
- separate persistent input under project `.pi/`.