pi-ui-extend 1.0.40 → 1.0.42
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/README.md +10 -2
- package/dist/app/app.d.ts +3 -0
- package/dist/app/app.js +26 -0
- package/dist/app/commands/command-host.d.ts +1 -0
- package/dist/app/commands/command-model-actions.js +6 -1
- package/dist/app/commands/command-registry.js +3 -3
- package/dist/app/commands/command-session-actions.d.ts +0 -1
- package/dist/app/commands/command-session-actions.js +30 -15
- package/dist/app/commands/reload-context-inventory.d.ts +11 -0
- package/dist/app/commands/reload-context-inventory.js +59 -0
- package/dist/app/extensions/extension-actions-controller.d.ts +1 -0
- package/dist/app/extensions/extension-actions-controller.js +6 -0
- package/dist/app/extensions/subagent-catalog-state.d.ts +9 -0
- package/dist/app/extensions/subagent-catalog-state.js +23 -0
- package/dist/app/icons.d.ts +14 -0
- package/dist/app/icons.js +33 -0
- package/dist/app/rendering/conversation-tool-renderer.js +2 -2
- package/dist/app/rendering/dcp-stats.d.ts +6 -1
- package/dist/app/rendering/dcp-stats.js +214 -46
- package/dist/app/rendering/editor-panels.js +17 -8
- package/dist/app/session/lazy-session-manager.js +12 -1
- package/dist/app/session/session-lifecycle-controller.d.ts +1 -0
- package/dist/app/session/session-lifecycle-controller.js +6 -0
- package/dist/app/session/tabs-controller.d.ts +3 -5
- package/dist/app/session/tabs-controller.js +31 -22
- package/dist/app/subagents/subagents-files.js +60 -1
- package/dist/app/subagents/subagents-model.d.ts +15 -1
- package/dist/app/subagents/subagents-model.js +52 -17
- package/dist/app/types.d.ts +7 -0
- package/dist/bundled-extensions/session-title/config.js +1 -1
- package/dist/default-pix-config.js +10 -0
- package/dist/markdown-format.js +27 -9
- package/dist/schemas/index.d.ts +1 -0
- package/dist/schemas/index.js +1 -0
- package/dist/schemas/pi-tools-suite-schema.d.ts +106 -71
- package/dist/schemas/pi-tools-suite-schema.js +68 -88
- package/dist/schemas/pix-schema.d.ts +7 -0
- package/dist/schemas/pix-schema.js +14 -0
- package/dist/schemas/tasks-schema.d.ts +18 -0
- package/dist/schemas/tasks-schema.js +41 -0
- package/docs/concurrency.md +6 -0
- package/docs/desktop-mvp.md +12 -1
- package/docs/desktop-task-manager.md +93 -82
- package/external/pi-tools-suite/README.md +192 -72
- package/external/pi-tools-suite/docs/browser-qa-subagent.md +50 -21
- package/external/pi-tools-suite/docs/context-gateway-p00-adr.md +216 -0
- package/external/pi-tools-suite/docs/context-gateway-p01n-gate-review.md +122 -0
- package/external/pi-tools-suite/docs/context-gateway-p01n-measurement.md +133 -0
- package/external/pi-tools-suite/docs/context-gateway-p01r-ra-evidence.md +111 -0
- package/external/pi-tools-suite/docs/context-gateway-p01r-rb-evidence.md +100 -0
- package/external/pi-tools-suite/docs/context-gateway-p01r-rc-evidence.md +69 -0
- package/external/pi-tools-suite/docs/context-gateway-p01r-rd-evidence.md +100 -0
- package/external/pi-tools-suite/docs/context-gateway-p01r-re-evidence.md +74 -0
- package/external/pi-tools-suite/docs/context-gateway-p01r-rf-evidence.md +153 -0
- package/external/pi-tools-suite/docs/context-gateway-p01r-rg-evidence.md +235 -0
- package/external/pi-tools-suite/docs/evals.md +1 -1
- package/external/pi-tools-suite/docs/session-recovery.md +40 -14
- package/external/pi-tools-suite/docs/subagent-model-pools.md +120 -0
- package/external/pi-tools-suite/package.json +3 -0
- package/external/pi-tools-suite/src/async-subagents/{private-skills → agents}/browser-qa/scripts/browser-qa-runner.mjs +82 -1
- package/external/pi-tools-suite/src/async-subagents/{private-skills/browser-qa/SKILL.md → agents/browser-qa.md} +261 -12
- package/external/pi-tools-suite/src/async-subagents/agents/frontier-review.md +23 -0
- package/external/pi-tools-suite/src/async-subagents/agents/implement.md +20 -0
- package/external/pi-tools-suite/src/async-subagents/agents/oracle.md +16 -0
- package/external/pi-tools-suite/src/async-subagents/agents/presets.jsonc +16 -0
- package/external/pi-tools-suite/src/async-subagents/agents/research.md +20 -0
- package/external/pi-tools-suite/src/async-subagents/agents/verify.md +18 -0
- package/external/pi-tools-suite/src/async-subagents/commands.ts +20 -72
- package/external/pi-tools-suite/src/async-subagents/core/activity.ts +33 -0
- package/external/pi-tools-suite/src/async-subagents/core/agent-catalog.ts +42 -0
- package/external/pi-tools-suite/src/async-subagents/core/agent-strategy.ts +13 -93
- package/external/pi-tools-suite/src/async-subagents/core/agents-dir.ts +499 -0
- package/external/pi-tools-suite/src/async-subagents/core/browser-qa.ts +9 -0
- package/external/pi-tools-suite/src/async-subagents/core/config.ts +284 -417
- package/external/pi-tools-suite/src/async-subagents/core/model-fallback.ts +1 -1
- package/external/pi-tools-suite/src/async-subagents/core/model-selection.ts +54 -0
- package/external/pi-tools-suite/src/async-subagents/core/prompt.ts +7 -6
- package/external/pi-tools-suite/src/async-subagents/core/routing.ts +65 -45
- package/external/pi-tools-suite/src/async-subagents/core/spawn.ts +18 -6
- package/external/pi-tools-suite/src/async-subagents/core/state.ts +40 -0
- package/external/pi-tools-suite/src/async-subagents/core/types.ts +7 -0
- package/external/pi-tools-suite/src/async-subagents/index.ts +77 -3
- package/external/pi-tools-suite/src/async-subagents/lib.ts +12 -9
- package/external/pi-tools-suite/src/async-subagents/tools/spawn.ts +54 -22
- package/external/pi-tools-suite/src/async-subagents/tools/subagents.ts +6 -4
- package/external/pi-tools-suite/src/async-subagents/types.ts +3 -0
- package/external/pi-tools-suite/src/config.ts +42 -2
- package/external/pi-tools-suite/src/context-gateway/accounting.ts +151 -0
- package/external/pi-tools-suite/src/context-gateway/config.ts +120 -0
- package/external/pi-tools-suite/src/context-gateway/enforcement.ts +102 -0
- package/external/pi-tools-suite/src/context-gateway/index.ts +214 -0
- package/external/pi-tools-suite/src/context-gateway/metadata-normalization.ts +88 -0
- package/external/pi-tools-suite/src/context-gateway/storeless-capabilities.ts +89 -0
- package/external/pi-tools-suite/src/context-gateway/telemetry.ts +502 -0
- package/external/pi-tools-suite/src/context-gateway/test-output-parser.ts +326 -0
- package/external/pi-tools-suite/src/context-gateway/types.ts +158 -0
- package/external/pi-tools-suite/src/context-inventory.ts +99 -0
- package/external/pi-tools-suite/src/dcp/auto-compress-budget.ts +106 -0
- package/external/pi-tools-suite/src/dcp/auto-compress.ts +892 -136
- package/external/pi-tools-suite/src/dcp/commands.ts +82 -139
- package/external/pi-tools-suite/src/dcp/compress-tool.ts +533 -74
- package/external/pi-tools-suite/src/dcp/compression-blocks.ts +488 -94
- package/external/pi-tools-suite/src/dcp/compression-preview.ts +122 -0
- package/external/pi-tools-suite/src/dcp/compression-progress.ts +70 -0
- package/external/pi-tools-suite/src/dcp/config.ts +86 -68
- package/external/pi-tools-suite/src/dcp/conversation-index.ts +421 -0
- package/external/pi-tools-suite/src/dcp/debug-log.ts +7 -5
- package/external/pi-tools-suite/src/dcp/index.ts +617 -203
- package/external/pi-tools-suite/src/dcp/journal.ts +566 -0
- package/external/pi-tools-suite/src/dcp/progress-controller.ts +244 -0
- package/external/pi-tools-suite/src/dcp/prompts.ts +83 -189
- package/external/pi-tools-suite/src/dcp/protected-continuity.ts +175 -0
- package/external/pi-tools-suite/src/dcp/provider-tool-results.ts +189 -0
- package/external/pi-tools-suite/src/dcp/pruner-candidates.ts +299 -79
- package/external/pi-tools-suite/src/dcp/pruner-compression-blocks.ts +173 -281
- package/external/pi-tools-suite/src/dcp/pruner-emergency.ts +2 -4
- package/external/pi-tools-suite/src/dcp/pruner-message-ids.ts +23 -15
- package/external/pi-tools-suite/src/dcp/pruner-metadata.ts +11 -1
- package/external/pi-tools-suite/src/dcp/pruner-nudge.ts +30 -82
- package/external/pi-tools-suite/src/dcp/pruner-tools.ts +28 -136
- package/external/pi-tools-suite/src/dcp/pruner.ts +18 -33
- package/external/pi-tools-suite/src/dcp/recovery.ts +129 -0
- package/external/pi-tools-suite/src/dcp/shadow-plan.ts +127 -0
- package/external/pi-tools-suite/src/dcp/state-transaction.ts +102 -0
- package/external/pi-tools-suite/src/dcp/state.ts +158 -580
- package/external/pi-tools-suite/src/dcp/ui.ts +1 -0
- package/external/pi-tools-suite/src/default-pi-tools-suite-config.ts +36 -214
- package/external/pi-tools-suite/src/index.ts +16 -1
- package/external/pi-tools-suite/src/model-tools/index.ts +76 -42
- package/external/pi-tools-suite/src/repo-discovery/index.ts +84 -18
- package/external/pi-tools-suite/src/repo-discovery/native-compact.ts +458 -0
- package/external/pi-tools-suite/src/resource-registry/index.ts +2290 -0
- package/external/pi-tools-suite/src/session-recovery/index.ts +189 -43
- package/external/pi-tools-suite/src/shell-command-policy.ts +219 -0
- package/external/pi-tools-suite/src/tool-descriptions.ts +41 -38
- package/external/pi-tools-suite/src/truncation-metadata-normalizer/index.ts +17 -0
- package/package.json +6 -3
- package/schemas/pi-tools-suite.json +540 -312
- package/schemas/pix.json +24 -0
- package/schemas/tasks.json +132 -0
- package/skills/spec-wiki/SKILL.md +55 -0
- package/skills/spec-wiki/evals/evals.json +132 -0
- package/skills/spec-wiki/evals/files/bootstrap/architecture/rfc-007.md +14 -0
- package/skills/spec-wiki/evals/files/bootstrap/docs/how-to-pay.md +4 -0
- package/skills/spec-wiki/evals/files/bootstrap/docs/overview.md +9 -0
- package/skills/spec-wiki/evals/files/bootstrap/requirements/payment-behavior.md +17 -0
- package/skills/spec-wiki/evals/files/bootstrap/src/payments.ts +12 -0
- package/skills/spec-wiki/evals/files/bootstrap/tests/payments.test.ts +11 -0
- package/skills/spec-wiki/evals/files/focused/src/session.ts +5 -0
- package/skills/spec-wiki/evals/files/focused/tests/session.test.ts +17 -0
- package/skills/spec-wiki/evals/files/maintenance/low-signal/incoming/capture.txt +2 -0
- package/skills/spec-wiki/evals/files/maintenance/low-signal/src/capture.ts +3 -0
- package/skills/spec-wiki/evals/files/maintenance/move/docs/old-job-contract.md +18 -0
- package/skills/spec-wiki/evals/files/maintenance/move/src/job.ts +5 -0
- package/skills/spec-wiki/evals/files/maintenance/new-relation/docs/session-refresh.md +24 -0
- package/skills/spec-wiki/evals/files/maintenance/new-relation/incoming/refresh-worker.ts +7 -0
- package/skills/spec-wiki/evals/files/maintenance/new-relation/src/session.ts +7 -0
- package/skills/spec-wiki/evals/files/maintenance/no-impact/docs/auth-contract.md +17 -0
- package/skills/spec-wiki/evals/files/maintenance/no-impact/incoming/telemetry-format.ts +3 -0
- package/skills/spec-wiki/evals/files/maintenance/no-impact/src/auth.ts +3 -0
- package/skills/spec-wiki/evals/files/query/docs/how-to-run-jobs.md +4 -0
- package/skills/spec-wiki/evals/files/query/docs/spec-overview.md +9 -0
- package/skills/spec-wiki/evals/files/query/features/jobs/cancellation-contract.md +15 -0
- package/skills/spec-wiki/evals/files/query/features/jobs/retry-spec.md +6 -0
- package/skills/spec-wiki/evals/maintenance_regression.py +311 -0
- package/skills/spec-wiki/evals/retrieval-eval.md +37 -0
- package/skills/spec-wiki/evals/retrieval-evals.json +37 -0
- package/skills/spec-wiki/evals/trigger-evals.json +94 -0
- package/skills/spec-wiki/references/bootstrap.md +100 -0
- package/skills/spec-wiki/references/focused-spec.md +117 -0
- package/skills/spec-wiki/references/index-schema.md +330 -0
- package/skills/spec-wiki/references/maintenance.md +134 -0
- package/skills/spec-wiki/references/query.md +74 -0
- package/skills/spec-wiki/scripts/spec_wiki.py +2076 -0
- package/docs/desktop-markdown-media.md +0 -77
- package/external/pi-tools-suite/docs/dcp-emergency-current-turn.md +0 -102
- package/external/pi-tools-suite/src/async-subagents/async-subagents.sample.jsonc +0 -270
- package/external/pi-tools-suite/src/async-subagents/private-skills/browser-qa/references/auth-scaffold-spec.md +0 -78
- package/external/pi-tools-suite/src/async-subagents/private-skills/browser-qa/references/qa-design.md +0 -223
- package/external/pi-tools-suite/src/dcp/state-persistence.ts +0 -195
- package/external/pi-tools-suite/src/skill-installer/index.ts +0 -333
- package/skills/spec-lite/SKILL.md +0 -140
- /package/external/pi-tools-suite/src/async-subagents/{private-skills/browser-qa/references → agents/browser-qa/examples}/qa-auth.example.jsonc +0 -0
- /package/external/pi-tools-suite/src/async-subagents/{private-skills/browser-qa/references → agents/browser-qa/examples}/qa-flow.example.jsonc +0 -0
- /package/external/pi-tools-suite/src/async-subagents/{private-skills → agents}/browser-qa/vendor/fflate.LICENSE +0 -0
- /package/external/pi-tools-suite/src/async-subagents/{private-skills → agents}/browser-qa/vendor/fflate.mjs +0 -0
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# Context Gateway P01-R / R-G storeless release evidence
|
|
2
|
+
|
|
3
|
+
<!-- markdownlint-disable MD013 -->
|
|
4
|
+
|
|
5
|
+
> Scope frozen before the R-G measurement gate on 7 September 2026.
|
|
6
|
+
> Repository HEAD: `daa1b06`; tested source tree is dirty and earlier live run identities must not be inferred from HEAD alone.
|
|
7
|
+
> This document does not authorise Context Gateway enforce mode or P02 durable storage.
|
|
8
|
+
|
|
9
|
+
## Scope fixed before measurement
|
|
10
|
+
|
|
11
|
+
The candidate storeless release is deliberately small:
|
|
12
|
+
|
|
13
|
+
| Surface | Decision for R-G | Runtime effect |
|
|
14
|
+
| --- | --- | --- |
|
|
15
|
+
| Context Gateway observe | Keep available, `off` by default | Aggregate measurement/classification only; no result replacement or archive. |
|
|
16
|
+
| `repo_*` Native Compact | Accept as explicit opt-in profile | Existing bounded native flags/cursors/validation; no store. |
|
|
17
|
+
| `truncation-metadata-normalizer` | Accept as separate explicit opt-in module | Removes only proven duplicate truncation metadata for measured Read/shell/ast_grep shapes. |
|
|
18
|
+
| test/build parser | Keep observe-only | Safe classification/metrics only; no result replacement. |
|
|
19
|
+
| mutation/LSP | Native passthrough | Existing outcome/diagnostics behavior only. |
|
|
20
|
+
| web/document | Native passthrough / limited | No second fetch, no archive. |
|
|
21
|
+
| structured JSON | Native passthrough / limited | No generic parse/rewrite. |
|
|
22
|
+
| subagent result | Native passthrough / producer-managed lifetime | No parent grant or lifetime extension. |
|
|
23
|
+
| images | Native passthrough | No text replacement. |
|
|
24
|
+
| direct browser / MCP | Unsupported | Not in the claimed denominator. |
|
|
25
|
+
|
|
26
|
+
Provider-specific equality claims are limited to the installed OpenAI-completions
|
|
27
|
+
serialization path already exercised by the deterministic harness. UI claims are
|
|
28
|
+
limited to the current TUI result renderers and the existing ACP/Desktop lazy
|
|
29
|
+
persisted-result flow. Native temp-output contents do not get a new UI/ACP reader.
|
|
30
|
+
|
|
31
|
+
## Metrics and release criteria fixed before measurement
|
|
32
|
+
|
|
33
|
+
R-G evaluates the following dimensions independently:
|
|
34
|
+
|
|
35
|
+
- task/fact correctness and execution outcome;
|
|
36
|
+
- initial delivered result bytes and all continuation/recovery calls;
|
|
37
|
+
- JSONL/details bytes for metadata-only cleanup;
|
|
38
|
+
- actual checked provider payload equality/usage rather than converting metadata
|
|
39
|
+
bytes into token estimates;
|
|
40
|
+
- tool-call/refusal/retry counts and elapsed time for model-driven evidence that
|
|
41
|
+
already exists;
|
|
42
|
+
- parser/capability CPU work through explicit scan/output bounds rather than a
|
|
43
|
+
claim based on one timing sample;
|
|
44
|
+
- native temp/current-file/index lifetime limitations and unavailable recovery;
|
|
45
|
+
- unsupported paths and failures remain in the report rather than being removed
|
|
46
|
+
from the denominator.
|
|
47
|
+
|
|
48
|
+
There is no aggregate batch-budget claim. P00 proved stable per-call identity and
|
|
49
|
+
source-order delivery, but not a host API that supplies a durable whole-batch
|
|
50
|
+
budget before execution. Batch cost is therefore the sum of actual per-result
|
|
51
|
+
traffic/calls in measurements.
|
|
52
|
+
|
|
53
|
+
## Measurement results
|
|
54
|
+
|
|
55
|
+
The measurements below were run after the scope and criteria above were written.
|
|
56
|
+
|
|
57
|
+
### Native Compact deterministic paired corpus
|
|
58
|
+
|
|
59
|
+
`PI_CONTEXT_GATEWAY_BENCHMARK_REPORT=1 bun test test/context-gateway/benchmark.test.ts`
|
|
60
|
+
compares the historical baseline behavior with the current Native Compact profile
|
|
61
|
+
over search, structure, AST, explain and dependency scenarios. All eight critical
|
|
62
|
+
facts are recovered in both arms.
|
|
63
|
+
|
|
64
|
+
| Metric | Baseline | Native Compact | Delta |
|
|
65
|
+
| --- | ---: | ---: | ---: |
|
|
66
|
+
| Delivered repo bytes including continuations | 71,025 | 27,519 | **-61.25%** |
|
|
67
|
+
| Tool calls | 6 | 8 | +2 |
|
|
68
|
+
| Continuation calls | 1 | 3 | +2 |
|
|
69
|
+
| Refusals | 0 | 0 | 0 |
|
|
70
|
+
| Full overrides | 0 | 0 | 0 |
|
|
71
|
+
| Critical facts | 8/8 | 8/8 | equal |
|
|
72
|
+
|
|
73
|
+
This is the expected trade-off for narrow native paging: substantially fewer
|
|
74
|
+
delivered bytes, but potentially more calls. It is not evidence of lower total
|
|
75
|
+
model cost by itself.
|
|
76
|
+
|
|
77
|
+
### Historical model-driven paired evidence
|
|
78
|
+
|
|
79
|
+
The retained `zai/glm-5.3` paired report
|
|
80
|
+
`test/evals/artifacts/p01n-2026-09-07T17-30-21-479Z/p01n-paired-report.json`
|
|
81
|
+
contains three Prompt Compact / Native Compact pairs; all **6 arm-runs passed**.
|
|
82
|
+
It predates R-A v2 source/corpus identity, so R-G uses it only as historical
|
|
83
|
+
model-behavior/cost evidence rather than pretending it certifies the current dirty
|
|
84
|
+
tree byte-for-byte.
|
|
85
|
+
|
|
86
|
+
| Aggregate metric | Prompt Compact | Native Compact | Delta |
|
|
87
|
+
| --- | ---: | ---: | ---: |
|
|
88
|
+
| Repo-result bytes | 1,286 | 983 | **-23.6%** |
|
|
89
|
+
| All tool-result bytes | 9,737 | 9,149 | **-6.0%** |
|
|
90
|
+
| Tool calls | 14 | 12 | **-14.3%** |
|
|
91
|
+
| Parent tokens | 179,855 | 207,611 | **+15.4%** |
|
|
92
|
+
| Parent cost | $0.0682 | $0.0923 | **+35.4%** |
|
|
93
|
+
| Elapsed | 88.682s | 96.219s | **+8.5%** |
|
|
94
|
+
| Native refusals / full overrides / refusal retries | 0 / 0 / 0 | 0 / 0 / 0 | equal |
|
|
95
|
+
|
|
96
|
+
The live evidence therefore argues **against** making Native Compact the default:
|
|
97
|
+
repo traffic improved, but total token/cost/latency did not consistently improve.
|
|
98
|
+
It remains a useful explicit opt-in for workloads where repo-result volume is the
|
|
99
|
+
binding constraint.
|
|
100
|
+
|
|
101
|
+
### Metadata normalizer
|
|
102
|
+
|
|
103
|
+
The normalizer changes only `details.truncation.content` on proven Read/shell/
|
|
104
|
+
`ast_grep` SDK shapes. A deterministic 24,007-byte duplicate fixture measured:
|
|
105
|
+
|
|
106
|
+
| Metric | Raw | Normalized | Delta |
|
|
107
|
+
| --- | ---: | ---: | ---: |
|
|
108
|
+
| Serialized tool-result JSON | 48,396 B | 24,376 B | **-24,020 B (-49.6%)** |
|
|
109
|
+
| Ten identical persisted results | — | — | **240,200 B avoided** |
|
|
110
|
+
|
|
111
|
+
The actual SDK fixtures in R-B also remove more than 40 KiB of duplicated metadata
|
|
112
|
+
from large Read/Bash results while preserving visible content and structural
|
|
113
|
+
truncation fields.
|
|
114
|
+
|
|
115
|
+
This is deliberately reported as JSONL/metadata savings only. The installed
|
|
116
|
+
OpenAI-completions serialization contract is byte-equivalent before/after cleanup
|
|
117
|
+
because tool-result `details` are not serialized to that provider payload. R-G
|
|
118
|
+
therefore claims **no token, cache or model-quality saving** from the normalizer.
|
|
119
|
+
|
|
120
|
+
An additional 52,000-byte duplicate sample, matching the approximate size of the
|
|
121
|
+
live observe residual details, measured `104,408 → 52,395 B` for the serialized
|
|
122
|
+
tool-result object and `52,279 → 266 B` for `details`: **52,013 B** removed while
|
|
123
|
+
visible content remained `52,035 B`. Seven reference-machine rounds of 10,000
|
|
124
|
+
normalizer calls on that sample measured approximately `3.5–4.3 µs/call`. These
|
|
125
|
+
timings are diagnostics, not a portable release threshold.
|
|
126
|
+
|
|
127
|
+
### Residual and negative corpus
|
|
128
|
+
|
|
129
|
+
The live observe-only residual run shows the measured large classes remain real:
|
|
130
|
+
Read/Bash/ast_grep each delivered roughly 52 KiB of result content and roughly
|
|
131
|
+
another 52 KiB of details at the checked boundary. The Bash task assertion failed
|
|
132
|
+
because the model made an extra call; the large Bash observation itself remained
|
|
133
|
+
valid. That failure stays visible rather than being removed from the denominator.
|
|
134
|
+
|
|
135
|
+
R-A–R-F deterministic contracts cover the rest of the offline release corpus:
|
|
136
|
+
|
|
137
|
+
- small/exact/control results and current-file Read continuation;
|
|
138
|
+
- Unicode/CRLF/long-line and changed file/index behavior;
|
|
139
|
+
- successful Bash/ast_grep native recovery plus missing/replaced temp output;
|
|
140
|
+
- timeout/abort/nonzero and unavailable structured recovery handles;
|
|
141
|
+
- parser middle failures, PASS→exit-1 conflict, ANSI/CR, mixed/compound commands,
|
|
142
|
+
unknown formats and a bounded 1 Mi-character parser scan;
|
|
143
|
+
- mutation/LSP outcomes, web errors/cancellation, opaque large-integer JSON,
|
|
144
|
+
subagent producer-managed artifacts, image passthrough and unsupported browser/MCP;
|
|
145
|
+
- late/reloaded/session-switched results, parallel calls and UI/history replay.
|
|
146
|
+
|
|
147
|
+
The test/build parser is bounded but remains observe-only. Its prospective compact
|
|
148
|
+
candidate is all-or-passthrough and must fit the configured result budget; no
|
|
149
|
+
runtime result delivery, provider traffic, calls or token usage are changed by it.
|
|
150
|
+
On a 4,550,063-character synthetic input the 1 Mi-character scan bound produced
|
|
151
|
+
`scan-limited / partial`; seven rounds of 100 parses measured about
|
|
152
|
+
`16.2–16.9 ms/call` on the reference machine. This is CPU/telemetry evidence only,
|
|
153
|
+
not a shaping or provider-saving claim.
|
|
154
|
+
|
|
155
|
+
## Live-run decision
|
|
156
|
+
|
|
157
|
+
R-G does **not** request another live model run for the selected release scope:
|
|
158
|
+
|
|
159
|
+
- the metadata normalizer is provider-invisible on the checked serializer and has
|
|
160
|
+
deterministic JSONL/provider equality contracts;
|
|
161
|
+
- the test/build parser does not replace results;
|
|
162
|
+
- mutation/LSP, web/JSON/subagent/image surfaces remain native passthrough;
|
|
163
|
+
- Native Compact already has retained paired model-driven evidence, while all
|
|
164
|
+
post-run changes relevant to this scope are deterministic policy/validation
|
|
165
|
+
hardening rather than a new model-visible adapter.
|
|
166
|
+
|
|
167
|
+
The R-A v2 recovery runner remains available for a future claim that specifically
|
|
168
|
+
depends on exact model-driven native-handle recovery. Such a run still requires
|
|
169
|
+
separate authorization and must create a new v2-identity report. R-G does not
|
|
170
|
+
reinterpret the old v1 recovery reports as v2 passes.
|
|
171
|
+
|
|
172
|
+
## Release decision
|
|
173
|
+
|
|
174
|
+
Accept the verified storeless scope with conservative defaults:
|
|
175
|
+
|
|
176
|
+
1. **Context Gateway stays `off` by default.** Observe remains an explicit passive
|
|
177
|
+
measurement mode; `enforce` remains unavailable.
|
|
178
|
+
2. **Native Compact is accepted only as explicit opt-in.** Default
|
|
179
|
+
`repoDiscovery.profile` remains `baseline` because live total-cost evidence is
|
|
180
|
+
mixed despite lower repo-result traffic.
|
|
181
|
+
3. **`truncation-metadata-normalizer` is accepted as a separate explicit opt-in.**
|
|
182
|
+
It remains disabled by default and claims JSONL/metadata reduction, not token
|
|
183
|
+
savings.
|
|
184
|
+
4. **Test/build parsing stays observe-only.** No production compact-result adapter
|
|
185
|
+
is enabled without a proven recovery/lifetime contract for omitted data.
|
|
186
|
+
5. **Mutation/LSP, web/document, JSON, subagent and visual paths remain their R-E
|
|
187
|
+
passthrough/limited decisions.** Browser/MCP direct adapters remain unsupported.
|
|
188
|
+
6. **P02 durable store remains deferred.** R-G found no task requiring new durable
|
|
189
|
+
snapshot identity strongly enough to justify store/readers/quotas now.
|
|
190
|
+
|
|
191
|
+
Rollback is configuration-only for future calls: disable the normalizer module,
|
|
192
|
+
return `repoDiscovery.profile` to `baseline`, and/or set Context Gateway mode to
|
|
193
|
+
`off`. Rollback does not delete session history, native temp files, user data or
|
|
194
|
+
DCP state and does not require a manual sync operation.
|
|
195
|
+
|
|
196
|
+
## Deterministic R-G gate
|
|
197
|
+
|
|
198
|
+
The final gate reuses the independently proven R-A–R-F contracts rather than one
|
|
199
|
+
monolithic test process where unrelated LSP/AgentSession suites can interfere with
|
|
200
|
+
shared process/temp state. The required groups are:
|
|
201
|
+
|
|
202
|
+
- Context Gateway/storeless contracts, Native Compact, recovery validator/corpus,
|
|
203
|
+
eval harness and coverage registry;
|
|
204
|
+
- ACP lazy history/replay and typecheck;
|
|
205
|
+
- root tab ownership/session UI contracts;
|
|
206
|
+
- suite/root `git diff --check` and source typechecks.
|
|
207
|
+
|
|
208
|
+
No new live model call, manual suite sync, durable store, reader tool or DCP change
|
|
209
|
+
is part of the R-G release gate.
|
|
210
|
+
|
|
211
|
+
The final external-suite P01-R gate is:
|
|
212
|
+
|
|
213
|
+
```text
|
|
214
|
+
bun test \
|
|
215
|
+
test/context-gateway \
|
|
216
|
+
test/repo-native-compact.test.ts \
|
|
217
|
+
test/repo-discovery.test.ts \
|
|
218
|
+
test/config.test.ts \
|
|
219
|
+
test/evals/extension-contracts.test.ts \
|
|
220
|
+
test/evals/harness.test.ts \
|
|
221
|
+
test/evals/recovery-corpus.test.ts \
|
|
222
|
+
test/evals/recovery-run-identity.test.ts \
|
|
223
|
+
test/evals/recovery-report.test.ts \
|
|
224
|
+
test/evals/recovery-validation.test.ts
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Result: **144 pass, 0 fail, 1011 assertions**; suite typecheck and
|
|
228
|
+
`git diff --check` pass. Root SDK pin remains `0.85.1`; generated schemas are
|
|
229
|
+
unchanged when checked through `node --import tsx` (the sandbox blocks the normal
|
|
230
|
+
`tsx` CLI IPC socket), and root `tsc --noEmit` passes.
|
|
231
|
+
|
|
232
|
+
R-F's separate host/UI gates remain green: root TUI/session **98/98**, ACP lazy
|
|
233
|
+
current-result **4/4** plus an additional **6/6** persisted-history/request-boundary
|
|
234
|
+
suite and typecheck, and Desktop transcript/client **28/28** with Desktop check at
|
|
235
|
+
zero errors (two pre-existing accessibility warnings).
|
|
@@ -137,7 +137,7 @@ The current extension registry covers all 19 modules:
|
|
|
137
137
|
| `web-search` | web-search tests | deterministic only |
|
|
138
138
|
| `dcp` | DCP prompt/pruning/state tests | existing prompt evals |
|
|
139
139
|
| `prompt-commands` | eval extension contracts | deterministic only |
|
|
140
|
-
| `
|
|
140
|
+
| `resource-registry` | resource-registry integration + eval extension contracts | deterministic only |
|
|
141
141
|
| `credential-firewall` | firewall tests | deterministic only |
|
|
142
142
|
| `codex-reasoning-fix` | reasoning-fix tests | deterministic only |
|
|
143
143
|
|
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
Status: implemented MVP contract (semantic search is intentionally deferred).
|
|
4
4
|
|
|
5
|
+
## Type
|
|
6
|
+
|
|
7
|
+
As-is
|
|
8
|
+
|
|
9
|
+
## Lifecycle
|
|
10
|
+
|
|
11
|
+
Active implemented contract.
|
|
12
|
+
|
|
5
13
|
## Goal
|
|
6
14
|
|
|
7
15
|
Let an agent recover the task, recent instructions, file activity, and useful raw
|
|
@@ -28,19 +36,29 @@ active model context by compaction remains discoverable.
|
|
|
28
36
|
|
|
29
37
|
A section starts at the first selected entry, a user message, a compaction, or a
|
|
30
38
|
branch summary. Its stable ID is derived from the start entry ID. The overview
|
|
31
|
-
reports bounded
|
|
32
|
-
role/tool/error/file statistics.
|
|
39
|
+
reports bounded section pages with entry ranges, counts, and compact
|
|
40
|
+
role/tool/error/file statistics. When more sections exist it returns an opaque
|
|
41
|
+
`nextCursor`; continuing with that cursor resumes after the last delivered
|
|
42
|
+
section. Labels are previews, not inferred decisions.
|
|
33
43
|
|
|
34
44
|
### Reading and search
|
|
35
45
|
|
|
36
|
-
`session_read_section`
|
|
37
|
-
|
|
46
|
+
`session_read_section` accepts either a section ID produced for the same scope or
|
|
47
|
+
one exact raw `entry_id`. Direct entry reads avoid scanning from the start of a
|
|
48
|
+
section and are useful when overview/search already identified the exact entry.
|
|
49
|
+
Without a continuation cursor, callers must pass exactly one of `section_id` or
|
|
50
|
+
`entry_id`.
|
|
51
|
+
|
|
52
|
+
The reader renders message roles/text, tool calls/arguments, tool results,
|
|
38
53
|
compaction summaries, and branch summaries with per-entry and total output
|
|
39
|
-
limits.
|
|
54
|
+
limits. Long entry bodies and multi-entry pages return an opaque `nextCursor`;
|
|
55
|
+
passing that cursor continues at the exact entry/body offset and must use the
|
|
56
|
+
same scope.
|
|
40
57
|
|
|
41
58
|
`session_search` is case-insensitive by default and searches message text, tool
|
|
42
59
|
arguments/results, custom-message content, and compaction or branch summaries.
|
|
43
|
-
It returns entry and section IDs plus bounded snippets.
|
|
60
|
+
It returns entry and section IDs plus bounded snippets. Search pages use an
|
|
61
|
+
opaque cursor bound to scope, query, and case-sensitivity. Regex and semantic
|
|
44
62
|
search are out of scope for the MVP.
|
|
45
63
|
|
|
46
64
|
### Recovery context
|
|
@@ -60,6 +78,8 @@ tools are not guessed to be mutations.
|
|
|
60
78
|
|
|
61
79
|
- Results use small defaults and hard caps for result count, entry body size,
|
|
62
80
|
and total text size.
|
|
81
|
+
- Overview, read, and search pagination use opaque validated cursors; a cursor
|
|
82
|
+
from another scope/query or a stale section is rejected rather than guessed.
|
|
63
83
|
- Empty or in-memory sessions return a normal explanatory result.
|
|
64
84
|
- Unknown or partially shaped entries are ignored or rendered conservatively.
|
|
65
85
|
- Concurrent sibling tool results might not yet be visible when recovery runs.
|
|
@@ -68,17 +88,23 @@ tools are not guessed to be mutations.
|
|
|
68
88
|
- Parent-session metadata is reported when Pi exposes it; parent files are not
|
|
69
89
|
traversed.
|
|
70
90
|
|
|
91
|
+
## Related files
|
|
92
|
+
|
|
93
|
+
- `external/pi-tools-suite/src/session-recovery/index.ts`
|
|
94
|
+
- `external/pi-tools-suite/src/tool-descriptions.ts`
|
|
95
|
+
- `external/pi-tools-suite/test/session-recovery.test.ts`
|
|
96
|
+
|
|
71
97
|
## Verification
|
|
72
98
|
|
|
73
99
|
Deterministic tests cover active versus all branches, raw pre-compaction search,
|
|
74
|
-
stable section IDs,
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
100
|
+
stable section IDs, overview/search pagination, direct late-entry reads,
|
|
101
|
+
continued long bodies, Unicode case-insensitive search, bounded output, empty
|
|
102
|
+
sessions, DCP-control filtering, current-call exclusion, recent errors, file
|
|
103
|
+
carry-forward details, and conservative handling of unknown entries. Release
|
|
104
|
+
verification runs the suite typecheck/tests/smoke gate and host checks.
|
|
79
105
|
|
|
80
106
|
## Evidence
|
|
81
107
|
|
|
82
|
-
Evidence is recorded by
|
|
83
|
-
`test/session-recovery.test.ts` and the
|
|
84
|
-
|
|
108
|
+
Evidence is recorded by
|
|
109
|
+
`external/pi-tools-suite/test/session-recovery.test.ts` and the implementation
|
|
110
|
+
in `external/pi-tools-suite/src/session-recovery/index.ts`.
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# Sub-agent model pools
|
|
2
|
+
|
|
3
|
+
Sub-agents primarily reduce the cost of bounded work and keep intermediate
|
|
4
|
+
source, searches and logs out of the parent context. The parent owns planning,
|
|
5
|
+
integration, decisions and the final answer. Actual savings depend on worker
|
|
6
|
+
quality, retries and how much work the parent repeats; the configuration is
|
|
7
|
+
not a price oracle.
|
|
8
|
+
|
|
9
|
+
## Six execution modes
|
|
10
|
+
|
|
11
|
+
- `research`: read-only evidence gathering, searches and focused review questions.
|
|
12
|
+
- `implement`: bounded code, documentation, test and frontend changes.
|
|
13
|
+
- `verify`: run checks and interpret logs, without fixing source or tests.
|
|
14
|
+
- `browser-qa`: isolated browser workflow with assertions and visual artifacts.
|
|
15
|
+
- `frontier-review`: independent post-implementation code review on a strong
|
|
16
|
+
model; hidden when the current parent model matches the role's availability
|
|
17
|
+
gate.
|
|
18
|
+
- `oracle`: a deliberate strong second opinion, not automatic worker escalation.
|
|
19
|
+
|
|
20
|
+
Task-specific discipline belongs in the brief or `promptAppend`. A new project
|
|
21
|
+
agent is warranted when it adds a durable contract, capabilities or resources,
|
|
22
|
+
not merely a professional title. `verify` has a behavioral no-edit contract;
|
|
23
|
+
shell access is not a read-only filesystem sandbox.
|
|
24
|
+
|
|
25
|
+
## Agent priority, preset availability
|
|
26
|
+
|
|
27
|
+
Each Markdown profile owns its ordered `models` list. This is one candidate
|
|
28
|
+
chain for initial selection and subsequent quota fallbacks:
|
|
29
|
+
|
|
30
|
+
```yaml
|
|
31
|
+
---
|
|
32
|
+
description: Make bounded implementation changes.
|
|
33
|
+
models:
|
|
34
|
+
- zai/glm-5.3-flash
|
|
35
|
+
- openai-codex/gpt-5.6-terra
|
|
36
|
+
- openai-codex/gpt-5.6-luna
|
|
37
|
+
thinking: medium
|
|
38
|
+
---
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
A preset contains a set of available models, not a per-agent matrix. Project
|
|
42
|
+
pools live in `.pi/agents/presets.jsonc`:
|
|
43
|
+
|
|
44
|
+
```jsonc
|
|
45
|
+
{
|
|
46
|
+
"gpt": {
|
|
47
|
+
"description": "Models available for this project",
|
|
48
|
+
"models": [
|
|
49
|
+
"openai-codex/gpt-5.6-luna",
|
|
50
|
+
"openai-codex/gpt-5.6-terra",
|
|
51
|
+
"openai-codex/gpt-5.6-sol"
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The example agent selects Terra, not Luna: the agent's order wins. Sol is
|
|
58
|
+
available in the pool but absent from this worker's chain, so it cannot become
|
|
59
|
+
an automatic implementation fallback. The oracle and frontier-review can
|
|
60
|
+
declare Sol in their own chains. Model references in `models` must be exact
|
|
61
|
+
`provider/model` values, not wildcards.
|
|
62
|
+
|
|
63
|
+
The resolver intersects the agent chain with the selected pool. Runtime
|
|
64
|
+
selection then skips unregistered, unauthenticated or session-exhausted models.
|
|
65
|
+
Tasks with images and browser QA require confirmed image support. The first
|
|
66
|
+
eligible candidate runs; only the remaining eligible candidates are passed to
|
|
67
|
+
quota fallback. An empty intersection or unavailable chain rejects the batch
|
|
68
|
+
before any children or run state are created. Model selection makes no LLM
|
|
69
|
+
completion request; the optional role router is a separate operation.
|
|
70
|
+
|
|
71
|
+
Oracle prefers another provider when possible, but still respects the pool.
|
|
72
|
+
It never substitutes an ordinary cheap candidate merely to avoid a selection
|
|
73
|
+
error. A single-provider pool cannot promise cross-provider independence.
|
|
74
|
+
|
|
75
|
+
Explicit task `model`, CLI `--model`, and `FORCE_CURRENT_MODEL` remain deliberate
|
|
76
|
+
overrides: they bypass the pool and do not add automatic fallback candidates.
|
|
77
|
+
The parent should not use these to evade the configured budget. The pool is
|
|
78
|
+
a selection policy, not a security boundary against explicit overrides.
|
|
79
|
+
|
|
80
|
+
## Selection and compatibility
|
|
81
|
+
|
|
82
|
+
Use `/subagent-preset <name>`, `AGENTS_PRESET=<name>` or
|
|
83
|
+
`/subagent-preset session <name>`. Clearing the preset uses agent priorities
|
|
84
|
+
without a pool filter. The shipped names remain compatible with saved choices:
|
|
85
|
+
`cheap` is the GLM pool, `gpt` the GPT pool, and `deep` the mixed pool. The last
|
|
86
|
+
name no longer means that ordinary workers should escalate to flagship models.
|
|
87
|
+
Bundled definitions live beside the built-in agents in
|
|
88
|
+
`src/async-subagents/agents/presets.jsonc`; a project file with the same preset
|
|
89
|
+
name overrides that pool.
|
|
90
|
+
|
|
91
|
+
Old role names are not implicit aliases. `quick`, `scan`, `review`, `deep`,
|
|
92
|
+
`docs`, `frontend`, and `tests` work only when explicitly defined as ordinary
|
|
93
|
+
custom/project types. This keeps the effective catalog and accepted names exact.
|
|
94
|
+
|
|
95
|
+
Agent frontmatter can gate whether a role exists for the current parent model:
|
|
96
|
+
`forParentModels` is an optional allow-list and `notForParentModels` is an
|
|
97
|
+
optional deny-list; deny wins when both match. These fields accept model
|
|
98
|
+
patterns such as `zai/*` and affect the parent catalog, explicit role
|
|
99
|
+
validation, and automatic routing. They do not change which model the child
|
|
100
|
+
runs on; `models` / legacy model selectors still own child model selection.
|
|
101
|
+
|
|
102
|
+
Legacy `model` plus `fallbackModels` and `modelByParent` still load when they are
|
|
103
|
+
declared in an agent Markdown file. New profile `models` replaces inherited
|
|
104
|
+
legacy selection fields. Empty `models` means no candidates, not permission to
|
|
105
|
+
inherit the parent model. Model-less project specialists must declare candidates
|
|
106
|
+
or receive an explicit model override.
|
|
107
|
+
|
|
108
|
+
The removed `asyncSubagents` section is no longer part of the public config
|
|
109
|
+
schema and is not read at runtime. Existing legacy files are left untouched but
|
|
110
|
+
have no effect. Migrate role definitions to `.pi/agents/*.md` and custom pools to
|
|
111
|
+
`.pi/agents/presets.jsonc`.
|
|
112
|
+
|
|
113
|
+
## Compact handoff
|
|
114
|
+
|
|
115
|
+
Give workers a scope, acceptance criteria and the evidence needed to start.
|
|
116
|
+
Read compact results first and inspect raw artifacts selectively. One noisy
|
|
117
|
+
sequential investigation can justify a worker; a command whose exit status is
|
|
118
|
+
sufficient usually only needs a saved log, not another LLM. Independent review
|
|
119
|
+
of substantive code changes uses `frontier-review` when it is present in the
|
|
120
|
+
current parent catalog; use `research` for focused evidence/review questions.
|
|
@@ -33,6 +33,9 @@
|
|
|
33
33
|
"test:evals:live": "PI_TOOLS_SUITE_EVALS_LIVE=1 bun test --concurrent --max-concurrency=4 test/evals/live-evals.test.ts",
|
|
34
34
|
"evals": "npm run test:evals:contracts && bun test test/evals/live-evals.test.ts",
|
|
35
35
|
"evals:report": "bun test/evals/run-evals.ts",
|
|
36
|
+
"evals:p01n": "bun test/evals/run-p01n-paired.ts",
|
|
37
|
+
"evals:context-gateway-observe": "bun test/evals/run-context-gateway-observe.ts",
|
|
38
|
+
"evals:context-gateway-recovery": "bun test/evals/run-context-gateway-recovery.ts",
|
|
36
39
|
"bench:locate": "PI_LOCATE_BENCH_ITERATIONS=5 PI_LOCATE_BENCH_FAKE_IDX=0 PI_LOCATE_BENCH_MODEL=zai/glm-5-turbo PI_LOCATE_BENCH_MODES=direct-read-grep,ast-structural,repo-search-hybrid,repo-discovery,subagent-search,unrestricted-suite node test/fixtures/hard-to-find-project/benchmark/run-locate-benchmark.mjs",
|
|
37
40
|
"bench:locate:analyze": "node test/fixtures/hard-to-find-project/benchmark/analyze-locate-benchmark.mjs",
|
|
38
41
|
"test:locate-benchmark-e2e": "PI_LOCATE_BENCH_E2E=1 PI_LOCATE_BENCH_MODEL=zai/glm-5-turbo bun test test/locate-benchmark-e2e.test.ts",
|
|
@@ -2206,12 +2206,60 @@ foreach ($candidatePid in $descendants) {
|
|
|
2206
2206
|
});
|
|
2207
2207
|
return;
|
|
2208
2208
|
}
|
|
2209
|
+
|
|
2210
|
+
// Prefer parent-based enumeration when available. macOS sandboxed hosts can
|
|
2211
|
+
// deny `ps` while still allowing pgrep(1); relying only on `ps` leaves a
|
|
2212
|
+
// detached Playwright/browser process alive after a timeout. Collect the
|
|
2213
|
+
// whole tree before killing anything so descendants cannot be re-parented
|
|
2214
|
+
// out of reach, then terminate deepest-first. A detached child is normally a
|
|
2215
|
+
// process-group leader, so also try its negative PID before the individual
|
|
2216
|
+
// PID; non-group-leaders simply yield ESRCH and fall through safely.
|
|
2217
|
+
const pgrepDescendants = [];
|
|
2218
|
+
const pgrepPendingParents = [process.pid];
|
|
2219
|
+
let pgrepAvailable = true;
|
|
2220
|
+
for (let index = 0; index < pgrepPendingParents.length; index += 1) {
|
|
2221
|
+
const parentPid = pgrepPendingParents[index];
|
|
2222
|
+
const result = spawnSync("pgrep", ["-P", String(parentPid)], {
|
|
2223
|
+
encoding: "utf8",
|
|
2224
|
+
timeout: 1000,
|
|
2225
|
+
maxBuffer: 256 * 1024,
|
|
2226
|
+
});
|
|
2227
|
+
if (result.error?.code === "ENOENT") {
|
|
2228
|
+
pgrepAvailable = false;
|
|
2229
|
+
break;
|
|
2230
|
+
}
|
|
2231
|
+
if (result.status !== 0 && result.status !== 1) {
|
|
2232
|
+
pgrepAvailable = false;
|
|
2233
|
+
break;
|
|
2234
|
+
}
|
|
2235
|
+
for (const line of (result.stdout ?? "").split("\n")) {
|
|
2236
|
+
const pid = Number(line.trim());
|
|
2237
|
+
if (!Number.isInteger(pid) || pid <= 0 || pgrepDescendants.includes(pid)) continue;
|
|
2238
|
+
pgrepDescendants.push(pid);
|
|
2239
|
+
pgrepPendingParents.push(pid);
|
|
2240
|
+
}
|
|
2241
|
+
}
|
|
2242
|
+
if (pgrepAvailable) {
|
|
2243
|
+
for (const pid of [...pgrepDescendants].reverse()) {
|
|
2244
|
+
try { process.kill(-pid, "SIGKILL"); } catch { /* not a group leader or already gone */ }
|
|
2245
|
+
try { process.kill(pid, "SIGKILL"); } catch { /* already gone */ }
|
|
2246
|
+
}
|
|
2247
|
+
return;
|
|
2248
|
+
}
|
|
2249
|
+
|
|
2209
2250
|
const snapshot = spawnSync("ps", ["-axo", "pid=,ppid=,pgid="], {
|
|
2210
2251
|
encoding: "utf8",
|
|
2211
2252
|
timeout: 1000,
|
|
2212
2253
|
maxBuffer: 2 * 1024 * 1024,
|
|
2213
2254
|
});
|
|
2214
|
-
if (snapshot.status !== 0 || !snapshot.stdout)
|
|
2255
|
+
if (snapshot.status !== 0 || !snapshot.stdout) {
|
|
2256
|
+
// Sandboxed macOS hosts can deny `ps` process enumeration while still
|
|
2257
|
+
// allowing parent-scoped `pgrep -P`. Fall back to a bounded recursive
|
|
2258
|
+
// parent walk so a detached browser process is not left behind merely
|
|
2259
|
+
// because the broader process snapshot is unavailable.
|
|
2260
|
+
terminateRunnerDescendantsWithPgrep(process.pid);
|
|
2261
|
+
return;
|
|
2262
|
+
}
|
|
2215
2263
|
const processes = snapshot.stdout
|
|
2216
2264
|
.split("\n")
|
|
2217
2265
|
.map((line) => line.trim().split(/\s+/).map(Number))
|
|
@@ -2241,6 +2289,39 @@ foreach ($candidatePid in $descendants) {
|
|
|
2241
2289
|
}
|
|
2242
2290
|
}
|
|
2243
2291
|
|
|
2292
|
+
function terminateRunnerDescendantsWithPgrep(rootPid) {
|
|
2293
|
+
const descendants = [];
|
|
2294
|
+
const seen = new Set([rootPid]);
|
|
2295
|
+
const pendingParents = [rootPid];
|
|
2296
|
+
for (let index = 0; index < pendingParents.length && descendants.length < 4096; index += 1) {
|
|
2297
|
+
const parentPid = pendingParents[index];
|
|
2298
|
+
const result = spawnSync("pgrep", ["-P", String(parentPid)], {
|
|
2299
|
+
encoding: "utf8",
|
|
2300
|
+
timeout: 500,
|
|
2301
|
+
maxBuffer: 256 * 1024,
|
|
2302
|
+
});
|
|
2303
|
+
// pgrep exits 1 when there are no matching children. Any other failure is
|
|
2304
|
+
// fail-closed for this fallback; callers still terminate the runner itself.
|
|
2305
|
+
if (result.status !== 0 && result.status !== 1) continue;
|
|
2306
|
+
for (const line of (result.stdout ?? "").split("\n")) {
|
|
2307
|
+
const pid = Number.parseInt(line.trim(), 10);
|
|
2308
|
+
if (!Number.isInteger(pid) || pid <= 0 || seen.has(pid)) continue;
|
|
2309
|
+
seen.add(pid);
|
|
2310
|
+
descendants.push(pid);
|
|
2311
|
+
pendingParents.push(pid);
|
|
2312
|
+
}
|
|
2313
|
+
}
|
|
2314
|
+
|
|
2315
|
+
// Kill leaves before their parents so descendants cannot be reparented
|
|
2316
|
+
// before we have sent them a signal. Detached children are killed by PID;
|
|
2317
|
+
// unlike the primary `ps` path this fallback does not need their PGID.
|
|
2318
|
+
for (let index = descendants.length - 1; index >= 0; index -= 1) {
|
|
2319
|
+
try {
|
|
2320
|
+
process.kill(descendants[index], "SIGKILL");
|
|
2321
|
+
} catch { /* already gone */ }
|
|
2322
|
+
}
|
|
2323
|
+
}
|
|
2324
|
+
|
|
2244
2325
|
function sanitizeTraceArchiveInWorker(tracePath, secrets, deadline) {
|
|
2245
2326
|
return new Promise((resolve, reject) => {
|
|
2246
2327
|
const worker = new Worker(new URL(import.meta.url), {
|